aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-12-20 19:53:05 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-12-20 19:53:05 +0000
commit0b57cec536236d46e3dba9bd041533462f33dbb7 (patch)
tree56229dbdbbf76d18580f72f789003db17246c8d9 /contrib/llvm-project/lldb/source
parent718ef55ec7785aae63f98f8ca05dc07ed399c16d (diff)
downloadsrc-0b57cec536236d46e3dba9bd041533462f33dbb7.tar.gz
src-0b57cec536236d46e3dba9bd041533462f33dbb7.zip
Move all sources from the llvm project into contrib/llvm-project.
This uses the new layout of the upstream repository, which was recently migrated to GitHub, and converted into a "monorepo". That is, most of the earlier separate sub-projects with their own branches and tags were consolidated into one top-level directory, and are now branched and tagged together. Updating the vendor area to match this layout is next.
Notes
Notes: svn path=/head/; revision=355940
Diffstat (limited to 'contrib/llvm-project/lldb/source')
-rw-r--r--contrib/llvm-project/lldb/source/API/SBAddress.cpp331
-rw-r--r--contrib/llvm-project/lldb/source/API/SBAttachInfo.cpp308
-rw-r--r--contrib/llvm-project/lldb/source/API/SBBlock.cpp392
-rw-r--r--contrib/llvm-project/lldb/source/API/SBBreakpoint.cpp1036
-rw-r--r--contrib/llvm-project/lldb/source/API/SBBreakpointLocation.cpp515
-rw-r--r--contrib/llvm-project/lldb/source/API/SBBreakpointName.cpp742
-rw-r--r--contrib/llvm-project/lldb/source/API/SBBreakpointOptionCommon.cpp80
-rw-r--r--contrib/llvm-project/lldb/source/API/SBBreakpointOptionCommon.h36
-rw-r--r--contrib/llvm-project/lldb/source/API/SBBroadcaster.cpp213
-rw-r--r--contrib/llvm-project/lldb/source/API/SBCommandInterpreter.cpp943
-rw-r--r--contrib/llvm-project/lldb/source/API/SBCommandReturnObject.cpp391
-rw-r--r--contrib/llvm-project/lldb/source/API/SBCommunication.cpp215
-rw-r--r--contrib/llvm-project/lldb/source/API/SBCompileUnit.cpp276
-rw-r--r--contrib/llvm-project/lldb/source/API/SBData.cpp724
-rw-r--r--contrib/llvm-project/lldb/source/API/SBDebugger.cpp1730
-rw-r--r--contrib/llvm-project/lldb/source/API/SBDeclaration.cpp207
-rw-r--r--contrib/llvm-project/lldb/source/API/SBError.cpp212
-rw-r--r--contrib/llvm-project/lldb/source/API/SBEvent.cpp242
-rw-r--r--contrib/llvm-project/lldb/source/API/SBExecutionContext.cpp163
-rw-r--r--contrib/llvm-project/lldb/source/API/SBExpressionOptions.cpp336
-rw-r--r--contrib/llvm-project/lldb/source/API/SBFileSpec.cpp223
-rw-r--r--contrib/llvm-project/lldb/source/API/SBFileSpecList.cpp152
-rw-r--r--contrib/llvm-project/lldb/source/API/SBFrame.cpp1371
-rw-r--r--contrib/llvm-project/lldb/source/API/SBFunction.cpp282
-rw-r--r--contrib/llvm-project/lldb/source/API/SBHostOS.cpp194
-rw-r--r--contrib/llvm-project/lldb/source/API/SBInstruction.cpp369
-rw-r--r--contrib/llvm-project/lldb/source/API/SBInstructionList.cpp210
-rw-r--r--contrib/llvm-project/lldb/source/API/SBLanguageRuntime.cpp46
-rw-r--r--contrib/llvm-project/lldb/source/API/SBLaunchInfo.cpp379
-rw-r--r--contrib/llvm-project/lldb/source/API/SBLineEntry.cpp220
-rw-r--r--contrib/llvm-project/lldb/source/API/SBListener.cpp373
-rw-r--r--contrib/llvm-project/lldb/source/API/SBMemoryRegionInfo.cpp166
-rw-r--r--contrib/llvm-project/lldb/source/API/SBMemoryRegionInfoList.cpp166
-rw-r--r--contrib/llvm-project/lldb/source/API/SBModule.cpp768
-rw-r--r--contrib/llvm-project/lldb/source/API/SBModuleSpec.cpp300
-rw-r--r--contrib/llvm-project/lldb/source/API/SBPlatform.cpp708
-rw-r--r--contrib/llvm-project/lldb/source/API/SBProcess.cpp1404
-rw-r--r--contrib/llvm-project/lldb/source/API/SBProcessInfo.cpp210
-rw-r--r--contrib/llvm-project/lldb/source/API/SBQueue.cpp359
-rw-r--r--contrib/llvm-project/lldb/source/API/SBQueueItem.cpp140
-rw-r--r--contrib/llvm-project/lldb/source/API/SBReproducer.cpp153
-rw-r--r--contrib/llvm-project/lldb/source/API/SBReproducerPrivate.h75
-rw-r--r--contrib/llvm-project/lldb/source/API/SBSection.cpp328
-rw-r--r--contrib/llvm-project/lldb/source/API/SBSourceManager.cpp164
-rw-r--r--contrib/llvm-project/lldb/source/API/SBStream.cpp198
-rw-r--r--contrib/llvm-project/lldb/source/API/SBStringList.cpp163
-rw-r--r--contrib/llvm-project/lldb/source/API/SBStructuredData.cpp247
-rw-r--r--contrib/llvm-project/lldb/source/API/SBSymbol.cpp244
-rw-r--r--contrib/llvm-project/lldb/source/API/SBSymbolContext.cpp271
-rw-r--r--contrib/llvm-project/lldb/source/API/SBSymbolContextList.cpp144
-rw-r--r--contrib/llvm-project/lldb/source/API/SBTarget.cpp2646
-rw-r--r--contrib/llvm-project/lldb/source/API/SBThread.cpp1506
-rw-r--r--contrib/llvm-project/lldb/source/API/SBThreadCollection.cpp110
-rw-r--r--contrib/llvm-project/lldb/source/API/SBThreadPlan.cpp439
-rw-r--r--contrib/llvm-project/lldb/source/API/SBTrace.cpp147
-rw-r--r--contrib/llvm-project/lldb/source/API/SBTraceOptions.cpp159
-rw-r--r--contrib/llvm-project/lldb/source/API/SBType.cpp1008
-rw-r--r--contrib/llvm-project/lldb/source/API/SBTypeCategory.cpp737
-rw-r--r--contrib/llvm-project/lldb/source/API/SBTypeEnumMember.cpp235
-rw-r--r--contrib/llvm-project/lldb/source/API/SBTypeFilter.cpp226
-rw-r--r--contrib/llvm-project/lldb/source/API/SBTypeFormat.cpp223
-rw-r--r--contrib/llvm-project/lldb/source/API/SBTypeNameSpecifier.cpp189
-rw-r--r--contrib/llvm-project/lldb/source/API/SBTypeSummary.cpp537
-rw-r--r--contrib/llvm-project/lldb/source/API/SBTypeSynthetic.cpp248
-rw-r--r--contrib/llvm-project/lldb/source/API/SBUnixSignals.cpp206
-rw-r--r--contrib/llvm-project/lldb/source/API/SBValue.cpp1690
-rw-r--r--contrib/llvm-project/lldb/source/API/SBValueList.cpp231
-rw-r--r--contrib/llvm-project/lldb/source/API/SBVariablesOptions.cpp276
-rw-r--r--contrib/llvm-project/lldb/source/API/SBWatchpoint.cpp352
-rw-r--r--contrib/llvm-project/lldb/source/API/SystemInitializerFull.cpp480
-rw-r--r--contrib/llvm-project/lldb/source/API/SystemInitializerFull.h32
-rw-r--r--contrib/llvm-project/lldb/source/API/Utils.h30
-rw-r--r--contrib/llvm-project/lldb/source/API/liblldb-private.exports6
-rw-r--r--contrib/llvm-project/lldb/source/API/liblldb.exports4
-rw-r--r--contrib/llvm-project/lldb/source/API/liblldb.xcode.exports3
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/Breakpoint.cpp1102
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointID.cpp121
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointIDList.cpp348
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointList.cpp191
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocation.cpp660
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationCollection.cpp187
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationList.cpp312
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointName.cpp86
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointOptions.cpp674
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointPrecondition.cpp26
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolver.cpp351
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverAddress.cpp187
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp274
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp173
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverName.cpp435
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverScripted.cpp183
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointSite.cpp206
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointSiteList.cpp200
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/Stoppoint.cpp24
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/StoppointCallbackContext.cpp24
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/StoppointLocation.cpp32
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/Watchpoint.cpp379
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/WatchpointList.cpp252
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/WatchpointOptions.cpp183
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandCompletions.cpp532
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.cpp105
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.h31
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.cpp2628
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.h60
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.cpp748
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.h33
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBugreport.cpp124
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectBugreport.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp1896
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.h30
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.cpp554
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.h81
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp705
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.h87
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.cpp1134
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.h28
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.cpp51
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.h30
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.cpp226
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.h89
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.cpp30
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.h29
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectLog.cpp368
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectLog.h35
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp1810
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp394
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.cpp1822
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.h31
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.cpp83
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.h28
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp1568
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.h27
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.cpp107
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.h32
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.cpp404
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.h32
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.cpp114
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.h28
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.cpp1186
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.h29
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSource.cpp1305
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectSource.h30
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectStats.cpp106
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectStats.h24
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.cpp5050
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.h28
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp2112
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectThread.h25
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectType.cpp3100
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectType.h29
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.cpp35
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.h30
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.cpp1172
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.h33
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.cpp678
-rw-r--r--contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.h31
-rw-r--r--contrib/llvm-project/lldb/source/Commands/Options.td308
-rw-r--r--contrib/llvm-project/lldb/source/Commands/OptionsBase.td160
-rw-r--r--contrib/llvm-project/lldb/source/Core/Address.cpp1006
-rw-r--r--contrib/llvm-project/lldb/source/Core/AddressRange.cpp211
-rw-r--r--contrib/llvm-project/lldb/source/Core/AddressResolver.cpp43
-rw-r--r--contrib/llvm-project/lldb/source/Core/AddressResolverFileLine.cpp85
-rw-r--r--contrib/llvm-project/lldb/source/Core/AddressResolverName.cpp199
-rw-r--r--contrib/llvm-project/lldb/source/Core/Communication.cpp434
-rw-r--r--contrib/llvm-project/lldb/source/Core/Debugger.cpp1773
-rw-r--r--contrib/llvm-project/lldb/source/Core/Disassembler.cpp1453
-rw-r--r--contrib/llvm-project/lldb/source/Core/DumpDataExtractor.cpp831
-rw-r--r--contrib/llvm-project/lldb/source/Core/DumpRegisterValue.cpp78
-rw-r--r--contrib/llvm-project/lldb/source/Core/DynamicLoader.cpp245
-rw-r--r--contrib/llvm-project/lldb/source/Core/EmulateInstruction.cpp585
-rw-r--r--contrib/llvm-project/lldb/source/Core/FileLineResolver.cpp89
-rw-r--r--contrib/llvm-project/lldb/source/Core/FileSpecList.cpp121
-rw-r--r--contrib/llvm-project/lldb/source/Core/FormatEntity.cpp2427
-rw-r--r--contrib/llvm-project/lldb/source/Core/Highlighter.cpp80
-rw-r--r--contrib/llvm-project/lldb/source/Core/IOHandler.cpp4671
-rw-r--r--contrib/llvm-project/lldb/source/Core/Mangled.cpp490
-rw-r--r--contrib/llvm-project/lldb/source/Core/Module.cpp1670
-rw-r--r--contrib/llvm-project/lldb/source/Core/ModuleChild.cpp28
-rw-r--r--contrib/llvm-project/lldb/source/Core/ModuleList.cpp1058
-rw-r--r--contrib/llvm-project/lldb/source/Core/Opcode.cpp140
-rw-r--r--contrib/llvm-project/lldb/source/Core/PluginManager.cpp2625
-rw-r--r--contrib/llvm-project/lldb/source/Core/RichManglingContext.cpp172
-rw-r--r--contrib/llvm-project/lldb/source/Core/SearchFilter.cpp853
-rw-r--r--contrib/llvm-project/lldb/source/Core/Section.cpp628
-rw-r--r--contrib/llvm-project/lldb/source/Core/SourceManager.cpp701
-rw-r--r--contrib/llvm-project/lldb/source/Core/StreamAsynchronousIO.cpp36
-rw-r--r--contrib/llvm-project/lldb/source/Core/StreamFile.cpp49
-rw-r--r--contrib/llvm-project/lldb/source/Core/UserSettingsController.cpp113
-rw-r--r--contrib/llvm-project/lldb/source/Core/Value.cpp708
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObject.cpp3380
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectCast.cpp94
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectChild.cpp228
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectConstResult.cpp296
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectConstResultCast.cpp62
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectConstResultChild.cpp74
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectConstResultImpl.cpp180
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectDynamicValue.cpp389
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectList.cpp110
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectMemory.cpp228
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectRegister.cpp358
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectSyntheticFilter.cpp396
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectVariable.cpp376
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/CXXFunctionPointer.cpp56
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/DataVisualization.cpp210
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/DumpValueObjectOptions.cpp201
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/FormatCache.cpp219
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/FormatClasses.cpp49
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/FormatManager.cpp1043
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/FormattersHelpers.cpp145
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/LanguageCategory.cpp227
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/StringPrinter.cpp651
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/TypeCategory.cpp595
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/TypeCategoryMap.cpp374
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/TypeFormat.cpp205
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/TypeSummary.cpp201
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/TypeSynthetic.cpp217
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/TypeValidator.cpp53
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/ValueObjectPrinter.cpp830
-rw-r--r--contrib/llvm-project/lldb/source/DataFormatters/VectorType.cpp297
-rw-r--r--contrib/llvm-project/lldb/source/Expression/DWARFExpression.cpp3140
-rw-r--r--contrib/llvm-project/lldb/source/Expression/DiagnosticManager.cpp88
-rw-r--r--contrib/llvm-project/lldb/source/Expression/Expression.cpp30
-rw-r--r--contrib/llvm-project/lldb/source/Expression/ExpressionVariable.cpp92
-rw-r--r--contrib/llvm-project/lldb/source/Expression/FunctionCaller.cpp390
-rw-r--r--contrib/llvm-project/lldb/source/Expression/IRExecutionUnit.cpp1277
-rw-r--r--contrib/llvm-project/lldb/source/Expression/IRInterpreter.cpp1698
-rw-r--r--contrib/llvm-project/lldb/source/Expression/IRMemoryMap.cpp842
-rw-r--r--contrib/llvm-project/lldb/source/Expression/LLVMUserExpression.cpp371
-rw-r--r--contrib/llvm-project/lldb/source/Expression/Materializer.cpp1451
-rw-r--r--contrib/llvm-project/lldb/source/Expression/REPL.cpp562
-rw-r--r--contrib/llvm-project/lldb/source/Expression/UserExpression.cpp400
-rw-r--r--contrib/llvm-project/lldb/source/Expression/UtilityFunction.cpp108
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/Editline.cpp1422
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/File.cpp722
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/FileAction.cpp88
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/FileCache.cpp112
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/FileSystem.cpp471
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/GetOptInc.cpp451
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/Host.cpp672
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/HostInfoBase.cpp353
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/HostNativeThreadBase.cpp70
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/HostProcess.cpp47
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/HostThread.cpp46
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/LockFileBase.cpp81
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/MainLoop.cpp407
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/MonitoringProcessLauncher.cpp70
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/NativeProcessProtocol.cpp700
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/NativeRegisterContext.cpp424
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/NativeThreadProtocol.cpp19
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/NativeWatchpointList.cpp30
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/OptionParser.cpp83
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/PipeBase.cpp24
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/ProcessLaunchInfo.cpp350
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/ProcessRunLock.cpp64
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp287
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/Socket.cpp491
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/SocketAddress.cpp328
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/StringConvert.cpp95
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/TCPSocket.cpp310
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/TaskPool.cpp126
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/Terminal.cpp236
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/ThreadLauncher.cpp77
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/UDPSocket.cpp144
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/XML.cpp541
-rw-r--r--contrib/llvm-project/lldb/source/Host/freebsd/Host.cpp245
-rw-r--r--contrib/llvm-project/lldb/source/Host/freebsd/HostInfoFreeBSD.cpp76
-rw-r--r--contrib/llvm-project/lldb/source/Host/netbsd/Host.cpp245
-rw-r--r--contrib/llvm-project/lldb/source/Host/netbsd/HostInfoNetBSD.cpp91
-rw-r--r--contrib/llvm-project/lldb/source/Host/openbsd/Host.cpp219
-rw-r--r--contrib/llvm-project/lldb/source/Host/openbsd/HostInfoOpenBSD.cpp65
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp764
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/DomainSocket.cpp155
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/FileSystem.cpp80
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/HostInfoPosix.cpp140
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/HostProcessPosix.cpp94
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/HostThreadPosix.cpp61
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/LockFilePosix.cpp62
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp319
-rw-r--r--contrib/llvm-project/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp218
-rw-r--r--contrib/llvm-project/lldb/source/Initialization/SystemInitializer.cpp15
-rw-r--r--contrib/llvm-project/lldb/source/Initialization/SystemInitializerCommon.cpp124
-rw-r--r--contrib/llvm-project/lldb/source/Initialization/SystemLifetimeManager.cpp56
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/CommandAlias.cpp247
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/CommandHistory.cpp109
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp3213
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/CommandObject.cpp1110
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/CommandObjectRegexCommand.cpp96
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/CommandObjectScript.cpp79
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/CommandObjectScript.h31
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/CommandOptionValidators.cpp36
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/CommandReturnObject.cpp170
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionArgParser.cpp254
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupArchitecture.cpp59
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupBoolean.cpp56
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupFile.cpp78
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupFormat.cpp268
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupOutputFile.cpp64
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupPlatform.cpp141
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupString.cpp47
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupUInt64.cpp47
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupUUID.cpp53
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp222
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupVariable.cpp145
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionGroupWatchpoint.cpp92
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValue.cpp615
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueArch.cpp78
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueArgs.cpp25
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueArray.cpp316
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueBoolean.cpp91
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueChar.cpp63
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueDictionary.cpp324
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueEnumeration.cpp122
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpec.cpp120
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpecLIst.cpp170
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueFormat.cpp62
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueFormatEntity.cpp122
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueLanguage.cpp77
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValuePathMappings.cpp204
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueProperties.cpp677
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueRegex.cpp67
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueSInt64.cpp76
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueString.cpp148
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueUInt64.cpp75
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/OptionValueUUID.cpp91
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/Options.cpp1425
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/Property.cpp299
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/ScriptInterpreter.cpp96
-rw-r--r--contrib/llvm-project/lldb/source/Interpreter/embedded_interpreter.py134
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp2052
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h93
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp2447
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h101
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp1130
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h100
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.cpp2156
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.h93
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp2417
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h100
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.cpp1353
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h105
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.cpp849
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.h108
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.cpp1069
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.h95
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.cpp1217
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.h108
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.cpp982
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.h104
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.cpp1091
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp749
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h96
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp1086
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.cpp1805
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.h99
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp157
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h40
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.cpp240
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.h51
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.cpp67
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.h41
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp1433
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h85
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp618
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.h138
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.cpp367
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.h246
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp597
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h253
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp773
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h165
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp154
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h59
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp225
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h54
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.cpp108
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.h40
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp516
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.h173
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.cpp185
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.h134
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp26
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h579
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp2237
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h528
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h49
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp2254
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h564
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionHelper.h60
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp1383
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h184
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp497
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h71
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h207
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp215
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.h153
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp168
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.h23
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp716
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h111
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp94
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h84
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp903
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h226
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp161
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h107
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp595
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.h131
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp2270
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h567
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h38
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp14512
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h786
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.cpp356
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.h79
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp1195
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.h192
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp3050
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h221
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp2361
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h182
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp397
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h92
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.cpp325
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.h66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.cpp274
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h67
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp1066
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.h81
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp341
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h68
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp474
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.h80
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp205
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp1071
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h136
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp660
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h177
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp228
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h43
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp681
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h159
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp159
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h33
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp113
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp122
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp444
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp463
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxOptional.cpp84
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp60
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp82
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp221
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp255
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp296
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp426
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h59
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp169
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp98
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h50
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp238
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h37
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp295
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h32
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp1123
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h116
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp95
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp870
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp1049
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h94
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp211
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp205
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp313
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp675
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h39
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp396
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h42
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp1094
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h163
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp56
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h51
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp353
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h90
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp617
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h117
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp541
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h331
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp662
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h51
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp583
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h131
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp443
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h156
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp2736
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h342
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp1151
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h158
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp381
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h83
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp204
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h76
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp436
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h429
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.cpp191
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.h52
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp5055
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h587
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp160
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.h17
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp281
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.h22
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp200
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h48
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp514
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h177
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp411
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h163
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp168
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h101
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp435
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h394
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp3380
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h381
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp258
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h100
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp419
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.h85
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp321
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h71
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.cpp364
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.h69
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp215
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.h63
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp1062
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h111
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp858
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h201
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.cpp68
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.h35
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.cpp153
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.h40
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFUtils.h75
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp642
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.h48
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/LaunchFlavor.h32
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.cpp504
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.h139
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp1541
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.h337
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.cpp281
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.h165
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.cpp702
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.h138
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp632
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.h113
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.cpp44
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.h66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp1081
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h220
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp1437
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h279
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIX.h63
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.cpp259
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.h76
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp264
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.h77
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.cpp262
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.h80
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.cpp274
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.h84
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.cpp613
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.h81
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp798
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h118
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp38
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h43
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp933
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h97
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp205
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h74
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.cpp337
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.h59
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp110
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h46
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp68
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.h168
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp31
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h39
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMDefines.h191
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMUtils.h374
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.cpp96
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.h73
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp758
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h93
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.cpp86
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.h27
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp18
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.h29
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.cpp84
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.h91
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.h41
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp237
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.h38
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/InstructionUtils.h116
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp112
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.h27
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp92
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.h27
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.cpp93
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.h28
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.cpp42
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h40
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.cpp53
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.h27
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwinConstants.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp1757
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h264
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.cpp1043
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.h231
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp969
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h208
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp1078
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h213
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.cpp120
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.h64
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp80
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp117
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp236
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h52
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp143
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.cpp122
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.h65
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp2105
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h253
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp133
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.h33
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp149
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.h36
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp207
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h39
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp74
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h36
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp192
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.h36
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp160
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h56
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp74
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.h40
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp60
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.h35
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp63
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h36
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.cpp139
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.h73
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp123
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.cpp77
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.cpp104
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp212
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp232
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.cpp184
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h84
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp193
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h202
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.cpp211
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h77
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp191
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h79
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp528
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h176
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp216
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.h100
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_mips.h374
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_powerpc.h123
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_s390x.h90
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.h374
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoInterface.h70
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp91
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h61
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp96
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h69
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp63
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h31
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h1619
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h712
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_i386.h305
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips.h305
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips64.h435
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_powerpc.h228
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64.h329
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64le.h474
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_s390x.h124
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64.h466
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp624
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.h51
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp103
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.h106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp505
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.h157
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp247
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h53
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm-register-enums.h199
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm64-register-enums.h264
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h65
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.h360
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64-register-enums.h136
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64le-register-enums.h207
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-s390x-register-enums.h90
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-x86-register-enums.h318
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp915
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h168
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp72
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h53
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp73
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h53
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp91
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.h55
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp111
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h60
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.cpp133
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.h48
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp96
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h55
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp99
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h49
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.cpp38
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h147
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp442
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h176
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp394
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h148
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp1398
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h238
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp4005
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h602
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp142
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h155
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp282
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h82
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp158
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h95
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp1285
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h155
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp3285
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h229
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp550
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h109
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp958
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h132
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp5486
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h459
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp43
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h46
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp343
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h119
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp615
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h115
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp115
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h236
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h36
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp822
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h119
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp533
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h92
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp833
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h82
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp96
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h135
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp110
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h180
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp118
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h45
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp71
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h47
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp1052
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h479
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp169
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h56
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp3259
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h57
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h473
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h45
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp1924
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h118
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp653
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h222
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp175
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h62
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp18
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h63
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h59
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp3885
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h151
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp89
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h65
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp58
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h85
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp149
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h127
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp115
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h35
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp138
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h74
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp483
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h107
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h37
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp146
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h81
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp140
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h62
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp95
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h55
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp197
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h88
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp1246
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h185
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp1038
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h227
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp126
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h61
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp290
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h76
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp87
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h88
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp402
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h77
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp739
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h89
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp23
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h37
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp878
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h306
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp272
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h81
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp583
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h193
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp33
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h35
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp476
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h74
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp78
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h53
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp3762
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h492
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp1440
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h362
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp153
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h79
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.cpp35
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.h29
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.cpp138
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.h50
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp73
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h103
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp457
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h24
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp225
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h94
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp252
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h44
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp1366
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h144
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp137
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h28
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp196
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h162
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp160
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h125
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp877
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h158
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp1590
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h241
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp236
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h82
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp1367
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.h116
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp181
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h47
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp1999
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h255
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp267
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h101
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp162
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h44
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp656
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h154
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp269
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h65
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp1614
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h199
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/ArmUnwindInfo.cpp369
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/Block.cpp517
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/ClangASTContext.cpp10409
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/ClangASTImporter.cpp1205
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/ClangExternalASTSourceCallbacks.cpp99
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/ClangExternalASTSourceCommon.cpp99
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/ClangUtil.cpp57
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/CompactUnwindInfo.cpp1606
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/CompileUnit.cpp413
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/CompilerDecl.cpp53
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/CompilerDeclContext.cpp85
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/CompilerType.cpp1075
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/CxxModuleHandler.cpp283
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/DWARFCallFrameInfo.cpp1020
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/DebugMacros.cpp53
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/DeclVendor.cpp29
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/Declaration.cpp101
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/FuncUnwinders.cpp495
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/Function.cpp614
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/LineEntry.cpp261
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/LineTable.cpp533
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/LocateSymbolFile.cpp390
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/LocateSymbolFileMacOSX.cpp657
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/ObjectFile.cpp744
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/PostfixExpression.cpp227
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/Symbol.cpp570
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/SymbolContext.cpp1336
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/SymbolFile.cpp172
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/SymbolVendor.cpp489
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/Symtab.cpp1129
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/Type.cpp1066
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/TypeList.cpp207
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/TypeMap.cpp250
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/TypeSystem.cpp262
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/UnwindPlan.cpp564
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/UnwindTable.cpp197
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/Variable.cpp768
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/VariableList.cpp176
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/VerifyDecl.cpp15
-rw-r--r--contrib/llvm-project/lldb/source/Target/ABI.cpp212
-rw-r--r--contrib/llvm-project/lldb/source/Target/ExecutionContext.cpp618
-rw-r--r--contrib/llvm-project/lldb/source/Target/InstrumentationRuntime.cpp75
-rw-r--r--contrib/llvm-project/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp36
-rw-r--r--contrib/llvm-project/lldb/source/Target/JITLoader.cpp32
-rw-r--r--contrib/llvm-project/lldb/source/Target/JITLoaderList.cpp55
-rw-r--r--contrib/llvm-project/lldb/source/Target/Language.cpp477
-rw-r--r--contrib/llvm-project/lldb/source/Target/LanguageRuntime.cpp296
-rw-r--r--contrib/llvm-project/lldb/source/Target/Memory.cpp414
-rw-r--r--contrib/llvm-project/lldb/source/Target/MemoryHistory.cpp28
-rw-r--r--contrib/llvm-project/lldb/source/Target/ModuleCache.cpp332
-rw-r--r--contrib/llvm-project/lldb/source/Target/OperatingSystem.cpp54
-rw-r--r--contrib/llvm-project/lldb/source/Target/PathMappingList.cpp317
-rw-r--r--contrib/llvm-project/lldb/source/Target/Platform.cpp1924
-rw-r--r--contrib/llvm-project/lldb/source/Target/Process.cpp6048
-rw-r--r--contrib/llvm-project/lldb/source/Target/Queue.cpp89
-rw-r--r--contrib/llvm-project/lldb/source/Target/QueueItem.cpp106
-rw-r--r--contrib/llvm-project/lldb/source/Target/QueueList.cpp69
-rw-r--r--contrib/llvm-project/lldb/source/Target/RegisterContext.cpp449
-rw-r--r--contrib/llvm-project/lldb/source/Target/RegisterNumber.cpp107
-rw-r--r--contrib/llvm-project/lldb/source/Target/RemoteAwarePlatform.cpp284
-rw-r--r--contrib/llvm-project/lldb/source/Target/SectionLoadHistory.cpp164
-rw-r--r--contrib/llvm-project/lldb/source/Target/SectionLoadList.cpp256
-rw-r--r--contrib/llvm-project/lldb/source/Target/StackFrame.cpp1964
-rw-r--r--contrib/llvm-project/lldb/source/Target/StackFrameList.cpp986
-rw-r--r--contrib/llvm-project/lldb/source/Target/StackFrameRecognizer.cpp192
-rw-r--r--contrib/llvm-project/lldb/source/Target/StackID.cpp97
-rw-r--r--contrib/llvm-project/lldb/source/Target/StopInfo.cpp1205
-rw-r--r--contrib/llvm-project/lldb/source/Target/StructuredDataPlugin.cpp67
-rw-r--r--contrib/llvm-project/lldb/source/Target/SystemRuntime.cpp51
-rw-r--r--contrib/llvm-project/lldb/source/Target/Target.cpp4261
-rw-r--r--contrib/llvm-project/lldb/source/Target/TargetList.cpp620
-rw-r--r--contrib/llvm-project/lldb/source/Target/Thread.cpp2235
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadCollection.cpp65
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadList.cpp755
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlan.cpp274
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanBase.cpp197
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanCallFunction.cpp505
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanCallFunctionUsingABI.cpp68
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanCallOnFunctionExit.cpp97
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanCallUserExpression.cpp119
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanPython.cpp195
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanRunToAddress.cpp204
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanShouldStopHere.cpp163
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanStepInRange.cpp515
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanStepInstruction.cpp253
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp510
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp187
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanStepOverRange.cpp414
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanStepRange.cpp487
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanStepThrough.cpp266
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanStepUntil.cpp339
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadPlanTracer.cpp225
-rw-r--r--contrib/llvm-project/lldb/source/Target/ThreadSpec.cpp157
-rw-r--r--contrib/llvm-project/lldb/source/Target/UnixSignals.cpp324
-rw-r--r--contrib/llvm-project/lldb/source/Target/UnwindAssembly.cpp33
-rw-r--r--contrib/llvm-project/lldb/source/Utility/ARM64_DWARF_Registers.h95
-rw-r--r--contrib/llvm-project/lldb/source/Utility/ARM64_ehframe_Registers.h91
-rw-r--r--contrib/llvm-project/lldb/source/Utility/ARM_DWARF_Registers.h207
-rw-r--r--contrib/llvm-project/lldb/source/Utility/ARM_ehframe_Registers.h36
-rw-r--r--contrib/llvm-project/lldb/source/Utility/ArchSpec.cpp1459
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Args.cpp723
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Baton.cpp12
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Broadcaster.cpp468
-rw-r--r--contrib/llvm-project/lldb/source/Utility/CompletionRequest.cpp92
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Connection.cpp13
-rw-r--r--contrib/llvm-project/lldb/source/Utility/ConstString.cpp329
-rw-r--r--contrib/llvm-project/lldb/source/Utility/DataBufferHeap.cpp70
-rw-r--r--contrib/llvm-project/lldb/source/Utility/DataBufferLLVM.cpp39
-rw-r--r--contrib/llvm-project/lldb/source/Utility/DataEncoder.cpp235
-rw-r--r--contrib/llvm-project/lldb/source/Utility/DataExtractor.cpp1121
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Environment.cpp49
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Event.cpp288
-rw-r--r--contrib/llvm-project/lldb/source/Utility/FileCollector.cpp182
-rw-r--r--contrib/llvm-project/lldb/source/Utility/FileSpec.cpp562
-rw-r--r--contrib/llvm-project/lldb/source/Utility/IOObject.cpp14
-rw-r--r--contrib/llvm-project/lldb/source/Utility/JSON.cpp550
-rw-r--r--contrib/llvm-project/lldb/source/Utility/LLDBAssert.cpp36
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Listener.cpp467
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Log.cpp330
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Logging.cpp74
-rw-r--r--contrib/llvm-project/lldb/source/Utility/NameMatches.cpp34
-rw-r--r--contrib/llvm-project/lldb/source/Utility/PPC64LE_DWARF_Registers.h193
-rw-r--r--contrib/llvm-project/lldb/source/Utility/PPC64LE_ehframe_Registers.h193
-rw-r--r--contrib/llvm-project/lldb/source/Utility/PPC64_DWARF_Registers.h126
-rw-r--r--contrib/llvm-project/lldb/source/Utility/ProcessInfo.cpp310
-rw-r--r--contrib/llvm-project/lldb/source/Utility/RegisterValue.cpp878
-rw-r--r--contrib/llvm-project/lldb/source/Utility/RegularExpression.cpp175
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Reproducer.cpp285
-rw-r--r--contrib/llvm-project/lldb/source/Utility/ReproducerInstrumentation.cpp122
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Scalar.cpp2863
-rw-r--r--contrib/llvm-project/lldb/source/Utility/SelectHelper.cpp254
-rw-r--r--contrib/llvm-project/lldb/source/Utility/SharingPtr.cpp134
-rw-r--r--contrib/llvm-project/lldb/source/Utility/State.cpp110
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Status.cpp296
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Stream.cpp441
-rw-r--r--contrib/llvm-project/lldb/source/Utility/StreamCallback.cpp22
-rw-r--r--contrib/llvm-project/lldb/source/Utility/StreamGDBRemote.cpp45
-rw-r--r--contrib/llvm-project/lldb/source/Utility/StreamString.cpp66
-rw-r--r--contrib/llvm-project/lldb/source/Utility/StringExtractor.cpp398
-rw-r--r--contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp604
-rw-r--r--contrib/llvm-project/lldb/source/Utility/StringLexer.cpp84
-rw-r--r--contrib/llvm-project/lldb/source/Utility/StringList.cpp267
-rw-r--r--contrib/llvm-project/lldb/source/Utility/StructuredData.cpp278
-rw-r--r--contrib/llvm-project/lldb/source/Utility/TildeExpressionResolver.cpp93
-rw-r--r--contrib/llvm-project/lldb/source/Utility/Timer.cpp152
-rw-r--r--contrib/llvm-project/lldb/source/Utility/UUID.cpp123
-rw-r--r--contrib/llvm-project/lldb/source/Utility/UriParser.cpp70
-rw-r--r--contrib/llvm-project/lldb/source/Utility/UserID.cpp20
-rw-r--r--contrib/llvm-project/lldb/source/Utility/UserIDResolver.cpp44
-rw-r--r--contrib/llvm-project/lldb/source/Utility/UuidCompatibility.h17
-rw-r--r--contrib/llvm-project/lldb/source/Utility/VASprintf.cpp55
-rw-r--r--contrib/llvm-project/lldb/source/Utility/VMRange.cpp70
-rw-r--r--contrib/llvm-project/lldb/source/lldb.cpp72
1060 files changed, 439537 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/API/SBAddress.cpp b/contrib/llvm-project/lldb/source/API/SBAddress.cpp
new file mode 100644
index 000000000000..358cb400a76c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBAddress.cpp
@@ -0,0 +1,331 @@
+//===-- SBAddress.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBAddress.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBSection.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBAddress::SBAddress() : m_opaque_up(new Address()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBAddress);
+}
+
+SBAddress::SBAddress(const Address *lldb_object_ptr)
+ : m_opaque_up(new Address()) {
+ if (lldb_object_ptr)
+ m_opaque_up = llvm::make_unique<Address>(*lldb_object_ptr);
+}
+
+SBAddress::SBAddress(const SBAddress &rhs) : m_opaque_up(new Address()) {
+ LLDB_RECORD_CONSTRUCTOR(SBAddress, (const lldb::SBAddress &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+SBAddress::SBAddress(lldb::SBSection section, lldb::addr_t offset)
+ : m_opaque_up(new Address(section.GetSP(), offset)) {
+ LLDB_RECORD_CONSTRUCTOR(SBAddress, (lldb::SBSection, lldb::addr_t), section,
+ offset);
+}
+
+// Create an address by resolving a load address using the supplied target
+SBAddress::SBAddress(lldb::addr_t load_addr, lldb::SBTarget &target)
+ : m_opaque_up(new Address()) {
+ LLDB_RECORD_CONSTRUCTOR(SBAddress, (lldb::addr_t, lldb::SBTarget &),
+ load_addr, target);
+
+ SetLoadAddress(load_addr, target);
+}
+
+SBAddress::~SBAddress() {}
+
+const SBAddress &SBAddress::operator=(const SBAddress &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBAddress &,
+ SBAddress, operator=,(const lldb::SBAddress &), rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool lldb::operator==(const SBAddress &lhs, const SBAddress &rhs) {
+ if (lhs.IsValid() && rhs.IsValid())
+ return lhs.ref() == rhs.ref();
+ return false;
+}
+
+bool SBAddress::operator!=(const SBAddress &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBAddress, operator!=,(const SBAddress &),
+ &rhs);
+
+ return !(*this == rhs);
+}
+
+bool SBAddress::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBAddress, IsValid);
+ return this->operator bool();
+}
+SBAddress::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBAddress, operator bool);
+
+ return m_opaque_up != nullptr && m_opaque_up->IsValid();
+}
+
+void SBAddress::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBAddress, Clear);
+
+ m_opaque_up.reset(new Address());
+}
+
+void SBAddress::SetAddress(lldb::SBSection section, lldb::addr_t offset) {
+ LLDB_RECORD_METHOD(void, SBAddress, SetAddress,
+ (lldb::SBSection, lldb::addr_t), section, offset);
+
+ Address &addr = ref();
+ addr.SetSection(section.GetSP());
+ addr.SetOffset(offset);
+}
+
+void SBAddress::SetAddress(const Address *lldb_object_ptr) {
+ if (lldb_object_ptr)
+ ref() = *lldb_object_ptr;
+ else
+ m_opaque_up.reset(new Address());
+}
+
+lldb::addr_t SBAddress::GetFileAddress() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::addr_t, SBAddress, GetFileAddress);
+
+ if (m_opaque_up->IsValid())
+ return m_opaque_up->GetFileAddress();
+ else
+ return LLDB_INVALID_ADDRESS;
+}
+
+lldb::addr_t SBAddress::GetLoadAddress(const SBTarget &target) const {
+ LLDB_RECORD_METHOD_CONST(lldb::addr_t, SBAddress, GetLoadAddress,
+ (const lldb::SBTarget &), target);
+
+ lldb::addr_t addr = LLDB_INVALID_ADDRESS;
+ TargetSP target_sp(target.GetSP());
+ if (target_sp) {
+ if (m_opaque_up->IsValid()) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ addr = m_opaque_up->GetLoadAddress(target_sp.get());
+ }
+ }
+
+ return addr;
+}
+
+void SBAddress::SetLoadAddress(lldb::addr_t load_addr, lldb::SBTarget &target) {
+ LLDB_RECORD_METHOD(void, SBAddress, SetLoadAddress,
+ (lldb::addr_t, lldb::SBTarget &), load_addr, target);
+
+ // Create the address object if we don't already have one
+ ref();
+ if (target.IsValid())
+ *this = target.ResolveLoadAddress(load_addr);
+ else
+ m_opaque_up->Clear();
+
+ // Check if we weren't were able to resolve a section offset address. If we
+ // weren't it is ok, the load address might be a location on the stack or
+ // heap, so we should just have an address with no section and a valid offset
+ if (!m_opaque_up->IsValid())
+ m_opaque_up->SetOffset(load_addr);
+}
+
+bool SBAddress::OffsetAddress(addr_t offset) {
+ LLDB_RECORD_METHOD(bool, SBAddress, OffsetAddress, (lldb::addr_t), offset);
+
+ if (m_opaque_up->IsValid()) {
+ addr_t addr_offset = m_opaque_up->GetOffset();
+ if (addr_offset != LLDB_INVALID_ADDRESS) {
+ m_opaque_up->SetOffset(addr_offset + offset);
+ return true;
+ }
+ }
+ return false;
+}
+
+lldb::SBSection SBAddress::GetSection() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBSection, SBAddress, GetSection);
+
+ lldb::SBSection sb_section;
+ if (m_opaque_up->IsValid())
+ sb_section.SetSP(m_opaque_up->GetSection());
+ return LLDB_RECORD_RESULT(sb_section);
+}
+
+lldb::addr_t SBAddress::GetOffset() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBAddress, GetOffset);
+
+ if (m_opaque_up->IsValid())
+ return m_opaque_up->GetOffset();
+ return 0;
+}
+
+Address *SBAddress::operator->() { return m_opaque_up.get(); }
+
+const Address *SBAddress::operator->() const { return m_opaque_up.get(); }
+
+Address &SBAddress::ref() {
+ if (m_opaque_up == nullptr)
+ m_opaque_up.reset(new Address());
+ return *m_opaque_up;
+}
+
+const Address &SBAddress::ref() const {
+ // This object should already have checked with "IsValid()" prior to calling
+ // this function. In case you didn't we will assert and die to let you know.
+ assert(m_opaque_up.get());
+ return *m_opaque_up;
+}
+
+Address *SBAddress::get() { return m_opaque_up.get(); }
+
+bool SBAddress::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBAddress, GetDescription, (lldb::SBStream &),
+ description);
+
+ // Call "ref()" on the stream to make sure it creates a backing stream in
+ // case there isn't one already...
+ Stream &strm = description.ref();
+ if (m_opaque_up->IsValid()) {
+ m_opaque_up->Dump(&strm, nullptr, Address::DumpStyleResolvedDescription,
+ Address::DumpStyleModuleWithFileAddress, 4);
+ StreamString sstrm;
+ // m_opaque_up->Dump (&sstrm, NULL,
+ // Address::DumpStyleResolvedDescription, Address::DumpStyleInvalid,
+ // 4);
+ // if (sstrm.GetData())
+ // strm.Printf (" (%s)", sstrm.GetData());
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+SBModule SBAddress::GetModule() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBModule, SBAddress, GetModule);
+
+ SBModule sb_module;
+ if (m_opaque_up->IsValid())
+ sb_module.SetSP(m_opaque_up->GetModule());
+ return LLDB_RECORD_RESULT(sb_module);
+}
+
+SBSymbolContext SBAddress::GetSymbolContext(uint32_t resolve_scope) {
+ LLDB_RECORD_METHOD(lldb::SBSymbolContext, SBAddress, GetSymbolContext,
+ (uint32_t), resolve_scope);
+
+ SBSymbolContext sb_sc;
+ SymbolContextItem scope = static_cast<SymbolContextItem>(resolve_scope);
+ if (m_opaque_up->IsValid())
+ m_opaque_up->CalculateSymbolContext(&sb_sc.ref(), scope);
+ return LLDB_RECORD_RESULT(sb_sc);
+}
+
+SBCompileUnit SBAddress::GetCompileUnit() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBCompileUnit, SBAddress, GetCompileUnit);
+
+ SBCompileUnit sb_comp_unit;
+ if (m_opaque_up->IsValid())
+ sb_comp_unit.reset(m_opaque_up->CalculateSymbolContextCompileUnit());
+ return LLDB_RECORD_RESULT(sb_comp_unit);
+}
+
+SBFunction SBAddress::GetFunction() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFunction, SBAddress, GetFunction);
+
+ SBFunction sb_function;
+ if (m_opaque_up->IsValid())
+ sb_function.reset(m_opaque_up->CalculateSymbolContextFunction());
+ return LLDB_RECORD_RESULT(sb_function);
+}
+
+SBBlock SBAddress::GetBlock() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBBlock, SBAddress, GetBlock);
+
+ SBBlock sb_block;
+ if (m_opaque_up->IsValid())
+ sb_block.SetPtr(m_opaque_up->CalculateSymbolContextBlock());
+ return LLDB_RECORD_RESULT(sb_block);
+}
+
+SBSymbol SBAddress::GetSymbol() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBSymbol, SBAddress, GetSymbol);
+
+ SBSymbol sb_symbol;
+ if (m_opaque_up->IsValid())
+ sb_symbol.reset(m_opaque_up->CalculateSymbolContextSymbol());
+ return LLDB_RECORD_RESULT(sb_symbol);
+}
+
+SBLineEntry SBAddress::GetLineEntry() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBLineEntry, SBAddress, GetLineEntry);
+
+ SBLineEntry sb_line_entry;
+ if (m_opaque_up->IsValid()) {
+ LineEntry line_entry;
+ if (m_opaque_up->CalculateSymbolContextLineEntry(line_entry))
+ sb_line_entry.SetLineEntry(line_entry);
+ }
+ return LLDB_RECORD_RESULT(sb_line_entry);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBAddress>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBAddress, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBAddress, (const lldb::SBAddress &));
+ LLDB_REGISTER_CONSTRUCTOR(SBAddress, (lldb::SBSection, lldb::addr_t));
+ LLDB_REGISTER_CONSTRUCTOR(SBAddress, (lldb::addr_t, lldb::SBTarget &));
+ LLDB_REGISTER_METHOD(const lldb::SBAddress &,
+ SBAddress, operator=,(const lldb::SBAddress &));
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBAddress, operator!=,(const lldb::SBAddress &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBAddress, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBAddress, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBAddress, Clear, ());
+ LLDB_REGISTER_METHOD(void, SBAddress, SetAddress,
+ (lldb::SBSection, lldb::addr_t));
+ LLDB_REGISTER_METHOD_CONST(lldb::addr_t, SBAddress, GetFileAddress, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::addr_t, SBAddress, GetLoadAddress,
+ (const lldb::SBTarget &));
+ LLDB_REGISTER_METHOD(void, SBAddress, SetLoadAddress,
+ (lldb::addr_t, lldb::SBTarget &));
+ LLDB_REGISTER_METHOD(bool, SBAddress, OffsetAddress, (lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::SBSection, SBAddress, GetSection, ());
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBAddress, GetOffset, ());
+ LLDB_REGISTER_METHOD(bool, SBAddress, GetDescription, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(lldb::SBModule, SBAddress, GetModule, ());
+ LLDB_REGISTER_METHOD(lldb::SBSymbolContext, SBAddress, GetSymbolContext,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBCompileUnit, SBAddress, GetCompileUnit, ());
+ LLDB_REGISTER_METHOD(lldb::SBFunction, SBAddress, GetFunction, ());
+ LLDB_REGISTER_METHOD(lldb::SBBlock, SBAddress, GetBlock, ());
+ LLDB_REGISTER_METHOD(lldb::SBSymbol, SBAddress, GetSymbol, ());
+ LLDB_REGISTER_METHOD(lldb::SBLineEntry, SBAddress, GetLineEntry, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBAttachInfo.cpp b/contrib/llvm-project/lldb/source/API/SBAttachInfo.cpp
new file mode 100644
index 000000000000..838385c104ae
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBAttachInfo.cpp
@@ -0,0 +1,308 @@
+//===-- SBAttachInfo.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBAttachInfo.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBListener.h"
+#include "lldb/Target/Process.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBAttachInfo::SBAttachInfo() : m_opaque_sp(new ProcessAttachInfo()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBAttachInfo);
+}
+
+SBAttachInfo::SBAttachInfo(lldb::pid_t pid)
+ : m_opaque_sp(new ProcessAttachInfo()) {
+ LLDB_RECORD_CONSTRUCTOR(SBAttachInfo, (lldb::pid_t), pid);
+
+ m_opaque_sp->SetProcessID(pid);
+}
+
+SBAttachInfo::SBAttachInfo(const char *path, bool wait_for)
+ : m_opaque_sp(new ProcessAttachInfo()) {
+ LLDB_RECORD_CONSTRUCTOR(SBAttachInfo, (const char *, bool), path, wait_for);
+
+ if (path && path[0])
+ m_opaque_sp->GetExecutableFile().SetFile(path, FileSpec::Style::native);
+ m_opaque_sp->SetWaitForLaunch(wait_for);
+}
+
+SBAttachInfo::SBAttachInfo(const char *path, bool wait_for, bool async)
+ : m_opaque_sp(new ProcessAttachInfo()) {
+ LLDB_RECORD_CONSTRUCTOR(SBAttachInfo, (const char *, bool, bool), path,
+ wait_for, async);
+
+ if (path && path[0])
+ m_opaque_sp->GetExecutableFile().SetFile(path, FileSpec::Style::native);
+ m_opaque_sp->SetWaitForLaunch(wait_for);
+ m_opaque_sp->SetAsync(async);
+}
+
+SBAttachInfo::SBAttachInfo(const SBAttachInfo &rhs)
+ : m_opaque_sp(new ProcessAttachInfo()) {
+ LLDB_RECORD_CONSTRUCTOR(SBAttachInfo, (const lldb::SBAttachInfo &), rhs);
+
+ m_opaque_sp = clone(rhs.m_opaque_sp);
+}
+
+SBAttachInfo::~SBAttachInfo() {}
+
+lldb_private::ProcessAttachInfo &SBAttachInfo::ref() { return *m_opaque_sp; }
+
+SBAttachInfo &SBAttachInfo::operator=(const SBAttachInfo &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBAttachInfo &,
+ SBAttachInfo, operator=,(const lldb::SBAttachInfo &), rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = clone(rhs.m_opaque_sp);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+lldb::pid_t SBAttachInfo::GetProcessID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::pid_t, SBAttachInfo, GetProcessID);
+
+ return m_opaque_sp->GetProcessID();
+}
+
+void SBAttachInfo::SetProcessID(lldb::pid_t pid) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetProcessID, (lldb::pid_t), pid);
+
+ m_opaque_sp->SetProcessID(pid);
+}
+
+uint32_t SBAttachInfo::GetResumeCount() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBAttachInfo, GetResumeCount);
+
+ return m_opaque_sp->GetResumeCount();
+}
+
+void SBAttachInfo::SetResumeCount(uint32_t c) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetResumeCount, (uint32_t), c);
+
+ m_opaque_sp->SetResumeCount(c);
+}
+
+const char *SBAttachInfo::GetProcessPluginName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBAttachInfo, GetProcessPluginName);
+
+ return m_opaque_sp->GetProcessPluginName();
+}
+
+void SBAttachInfo::SetProcessPluginName(const char *plugin_name) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetProcessPluginName, (const char *),
+ plugin_name);
+
+ return m_opaque_sp->SetProcessPluginName(plugin_name);
+}
+
+void SBAttachInfo::SetExecutable(const char *path) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetExecutable, (const char *), path);
+
+ if (path && path[0])
+ m_opaque_sp->GetExecutableFile().SetFile(path, FileSpec::Style::native);
+ else
+ m_opaque_sp->GetExecutableFile().Clear();
+}
+
+void SBAttachInfo::SetExecutable(SBFileSpec exe_file) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetExecutable, (lldb::SBFileSpec),
+ exe_file);
+
+ if (exe_file.IsValid())
+ m_opaque_sp->GetExecutableFile() = exe_file.ref();
+ else
+ m_opaque_sp->GetExecutableFile().Clear();
+}
+
+bool SBAttachInfo::GetWaitForLaunch() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBAttachInfo, GetWaitForLaunch);
+
+ return m_opaque_sp->GetWaitForLaunch();
+}
+
+void SBAttachInfo::SetWaitForLaunch(bool b) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetWaitForLaunch, (bool), b);
+
+ m_opaque_sp->SetWaitForLaunch(b);
+}
+
+void SBAttachInfo::SetWaitForLaunch(bool b, bool async) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetWaitForLaunch, (bool, bool), b,
+ async);
+
+ m_opaque_sp->SetWaitForLaunch(b);
+ m_opaque_sp->SetAsync(async);
+}
+
+bool SBAttachInfo::GetIgnoreExisting() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBAttachInfo, GetIgnoreExisting);
+
+ return m_opaque_sp->GetIgnoreExisting();
+}
+
+void SBAttachInfo::SetIgnoreExisting(bool b) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetIgnoreExisting, (bool), b);
+
+ m_opaque_sp->SetIgnoreExisting(b);
+}
+
+uint32_t SBAttachInfo::GetUserID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBAttachInfo, GetUserID);
+
+ return m_opaque_sp->GetUserID();
+}
+
+uint32_t SBAttachInfo::GetGroupID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBAttachInfo, GetGroupID);
+
+ return m_opaque_sp->GetGroupID();
+}
+
+bool SBAttachInfo::UserIDIsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBAttachInfo, UserIDIsValid);
+
+ return m_opaque_sp->UserIDIsValid();
+}
+
+bool SBAttachInfo::GroupIDIsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBAttachInfo, GroupIDIsValid);
+
+ return m_opaque_sp->GroupIDIsValid();
+}
+
+void SBAttachInfo::SetUserID(uint32_t uid) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetUserID, (uint32_t), uid);
+
+ m_opaque_sp->SetUserID(uid);
+}
+
+void SBAttachInfo::SetGroupID(uint32_t gid) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetGroupID, (uint32_t), gid);
+
+ m_opaque_sp->SetGroupID(gid);
+}
+
+uint32_t SBAttachInfo::GetEffectiveUserID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBAttachInfo, GetEffectiveUserID);
+
+ return m_opaque_sp->GetEffectiveUserID();
+}
+
+uint32_t SBAttachInfo::GetEffectiveGroupID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBAttachInfo, GetEffectiveGroupID);
+
+ return m_opaque_sp->GetEffectiveGroupID();
+}
+
+bool SBAttachInfo::EffectiveUserIDIsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBAttachInfo, EffectiveUserIDIsValid);
+
+ return m_opaque_sp->EffectiveUserIDIsValid();
+}
+
+bool SBAttachInfo::EffectiveGroupIDIsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBAttachInfo, EffectiveGroupIDIsValid);
+
+ return m_opaque_sp->EffectiveGroupIDIsValid();
+}
+
+void SBAttachInfo::SetEffectiveUserID(uint32_t uid) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetEffectiveUserID, (uint32_t), uid);
+
+ m_opaque_sp->SetEffectiveUserID(uid);
+}
+
+void SBAttachInfo::SetEffectiveGroupID(uint32_t gid) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetEffectiveGroupID, (uint32_t), gid);
+
+ m_opaque_sp->SetEffectiveGroupID(gid);
+}
+
+lldb::pid_t SBAttachInfo::GetParentProcessID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::pid_t, SBAttachInfo, GetParentProcessID);
+
+ return m_opaque_sp->GetParentProcessID();
+}
+
+void SBAttachInfo::SetParentProcessID(lldb::pid_t pid) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetParentProcessID, (lldb::pid_t),
+ pid);
+
+ m_opaque_sp->SetParentProcessID(pid);
+}
+
+bool SBAttachInfo::ParentProcessIDIsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBAttachInfo, ParentProcessIDIsValid);
+
+ return m_opaque_sp->ParentProcessIDIsValid();
+}
+
+SBListener SBAttachInfo::GetListener() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBListener, SBAttachInfo, GetListener);
+
+ return LLDB_RECORD_RESULT(SBListener(m_opaque_sp->GetListener()));
+}
+
+void SBAttachInfo::SetListener(SBListener &listener) {
+ LLDB_RECORD_METHOD(void, SBAttachInfo, SetListener, (lldb::SBListener &),
+ listener);
+
+ m_opaque_sp->SetListener(listener.GetSP());
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBAttachInfo>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBAttachInfo, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBAttachInfo, (lldb::pid_t));
+ LLDB_REGISTER_CONSTRUCTOR(SBAttachInfo, (const char *, bool));
+ LLDB_REGISTER_CONSTRUCTOR(SBAttachInfo, (const char *, bool, bool));
+ LLDB_REGISTER_CONSTRUCTOR(SBAttachInfo, (const lldb::SBAttachInfo &));
+ LLDB_REGISTER_METHOD(lldb::SBAttachInfo &,
+ SBAttachInfo, operator=,(const lldb::SBAttachInfo &));
+ LLDB_REGISTER_METHOD(lldb::pid_t, SBAttachInfo, GetProcessID, ());
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetProcessID, (lldb::pid_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBAttachInfo, GetResumeCount, ());
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetResumeCount, (uint32_t));
+ LLDB_REGISTER_METHOD(const char *, SBAttachInfo, GetProcessPluginName, ());
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetProcessPluginName,
+ (const char *));
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetExecutable, (const char *));
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetExecutable, (lldb::SBFileSpec));
+ LLDB_REGISTER_METHOD(bool, SBAttachInfo, GetWaitForLaunch, ());
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetWaitForLaunch, (bool));
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetWaitForLaunch, (bool, bool));
+ LLDB_REGISTER_METHOD(bool, SBAttachInfo, GetIgnoreExisting, ());
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetIgnoreExisting, (bool));
+ LLDB_REGISTER_METHOD(uint32_t, SBAttachInfo, GetUserID, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBAttachInfo, GetGroupID, ());
+ LLDB_REGISTER_METHOD(bool, SBAttachInfo, UserIDIsValid, ());
+ LLDB_REGISTER_METHOD(bool, SBAttachInfo, GroupIDIsValid, ());
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetUserID, (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetGroupID, (uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBAttachInfo, GetEffectiveUserID, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBAttachInfo, GetEffectiveGroupID, ());
+ LLDB_REGISTER_METHOD(bool, SBAttachInfo, EffectiveUserIDIsValid, ());
+ LLDB_REGISTER_METHOD(bool, SBAttachInfo, EffectiveGroupIDIsValid, ());
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetEffectiveUserID, (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetEffectiveGroupID, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::pid_t, SBAttachInfo, GetParentProcessID, ());
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetParentProcessID, (lldb::pid_t));
+ LLDB_REGISTER_METHOD(bool, SBAttachInfo, ParentProcessIDIsValid, ());
+ LLDB_REGISTER_METHOD(lldb::SBListener, SBAttachInfo, GetListener, ());
+ LLDB_REGISTER_METHOD(void, SBAttachInfo, SetListener, (lldb::SBListener &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBBlock.cpp b/contrib/llvm-project/lldb/source/API/SBBlock.cpp
new file mode 100644
index 000000000000..f333d1d7b5f3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBBlock.cpp
@@ -0,0 +1,392 @@
+//===-- SBBlock.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBBlock.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBValue.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBBlock::SBBlock() : m_opaque_ptr(nullptr) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBBlock);
+}
+
+SBBlock::SBBlock(lldb_private::Block *lldb_object_ptr)
+ : m_opaque_ptr(lldb_object_ptr) {}
+
+SBBlock::SBBlock(const SBBlock &rhs) : m_opaque_ptr(rhs.m_opaque_ptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBBlock, (const lldb::SBBlock &), rhs);
+}
+
+const SBBlock &SBBlock::operator=(const SBBlock &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBBlock &,
+ SBBlock, operator=,(const lldb::SBBlock &), rhs);
+
+ m_opaque_ptr = rhs.m_opaque_ptr;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBBlock::~SBBlock() { m_opaque_ptr = nullptr; }
+
+bool SBBlock::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBlock, IsValid);
+ return this->operator bool();
+}
+SBBlock::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBlock, operator bool);
+
+ return m_opaque_ptr != nullptr;
+}
+
+bool SBBlock::IsInlined() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBlock, IsInlined);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->GetInlinedFunctionInfo() != nullptr;
+ return false;
+}
+
+const char *SBBlock::GetInlinedName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBBlock, GetInlinedName);
+
+ if (m_opaque_ptr) {
+ const InlineFunctionInfo *inlined_info =
+ m_opaque_ptr->GetInlinedFunctionInfo();
+ if (inlined_info) {
+ Function *function = m_opaque_ptr->CalculateSymbolContextFunction();
+ LanguageType language;
+ if (function)
+ language = function->GetLanguage();
+ else
+ language = lldb::eLanguageTypeUnknown;
+ return inlined_info->GetName(language).AsCString(nullptr);
+ }
+ }
+ return nullptr;
+}
+
+SBFileSpec SBBlock::GetInlinedCallSiteFile() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBFileSpec, SBBlock,
+ GetInlinedCallSiteFile);
+
+ SBFileSpec sb_file;
+ if (m_opaque_ptr) {
+ const InlineFunctionInfo *inlined_info =
+ m_opaque_ptr->GetInlinedFunctionInfo();
+ if (inlined_info)
+ sb_file.SetFileSpec(inlined_info->GetCallSite().GetFile());
+ }
+ return LLDB_RECORD_RESULT(sb_file);
+}
+
+uint32_t SBBlock::GetInlinedCallSiteLine() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBBlock, GetInlinedCallSiteLine);
+
+ if (m_opaque_ptr) {
+ const InlineFunctionInfo *inlined_info =
+ m_opaque_ptr->GetInlinedFunctionInfo();
+ if (inlined_info)
+ return inlined_info->GetCallSite().GetLine();
+ }
+ return 0;
+}
+
+uint32_t SBBlock::GetInlinedCallSiteColumn() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBBlock, GetInlinedCallSiteColumn);
+
+ if (m_opaque_ptr) {
+ const InlineFunctionInfo *inlined_info =
+ m_opaque_ptr->GetInlinedFunctionInfo();
+ if (inlined_info)
+ return inlined_info->GetCallSite().GetColumn();
+ }
+ return 0;
+}
+
+void SBBlock::AppendVariables(bool can_create, bool get_parent_variables,
+ lldb_private::VariableList *var_list) {
+ if (IsValid()) {
+ bool show_inline = true;
+ m_opaque_ptr->AppendVariables(can_create, get_parent_variables, show_inline,
+ [](Variable *) { return true; }, var_list);
+ }
+}
+
+SBBlock SBBlock::GetParent() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBBlock, SBBlock, GetParent);
+
+ SBBlock sb_block;
+ if (m_opaque_ptr)
+ sb_block.m_opaque_ptr = m_opaque_ptr->GetParent();
+ return LLDB_RECORD_RESULT(sb_block);
+}
+
+lldb::SBBlock SBBlock::GetContainingInlinedBlock() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBBlock, SBBlock, GetContainingInlinedBlock);
+
+ SBBlock sb_block;
+ if (m_opaque_ptr)
+ sb_block.m_opaque_ptr = m_opaque_ptr->GetContainingInlinedBlock();
+ return LLDB_RECORD_RESULT(sb_block);
+}
+
+SBBlock SBBlock::GetSibling() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBBlock, SBBlock, GetSibling);
+
+ SBBlock sb_block;
+ if (m_opaque_ptr)
+ sb_block.m_opaque_ptr = m_opaque_ptr->GetSibling();
+ return LLDB_RECORD_RESULT(sb_block);
+}
+
+SBBlock SBBlock::GetFirstChild() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBBlock, SBBlock, GetFirstChild);
+
+ SBBlock sb_block;
+ if (m_opaque_ptr)
+ sb_block.m_opaque_ptr = m_opaque_ptr->GetFirstChild();
+ return LLDB_RECORD_RESULT(sb_block);
+}
+
+lldb_private::Block *SBBlock::GetPtr() { return m_opaque_ptr; }
+
+void SBBlock::SetPtr(lldb_private::Block *block) { m_opaque_ptr = block; }
+
+bool SBBlock::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBBlock, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_ptr) {
+ lldb::user_id_t id = m_opaque_ptr->GetID();
+ strm.Printf("Block: {id: %" PRIu64 "} ", id);
+ if (IsInlined()) {
+ strm.Printf(" (inlined, '%s') ", GetInlinedName());
+ }
+ lldb_private::SymbolContext sc;
+ m_opaque_ptr->CalculateSymbolContext(&sc);
+ if (sc.function) {
+ m_opaque_ptr->DumpAddressRanges(
+ &strm,
+ sc.function->GetAddressRange().GetBaseAddress().GetFileAddress());
+ }
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+uint32_t SBBlock::GetNumRanges() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBBlock, GetNumRanges);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->GetNumRanges();
+ return 0;
+}
+
+lldb::SBAddress SBBlock::GetRangeStartAddress(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBAddress, SBBlock, GetRangeStartAddress, (uint32_t),
+ idx);
+
+ lldb::SBAddress sb_addr;
+ if (m_opaque_ptr) {
+ AddressRange range;
+ if (m_opaque_ptr->GetRangeAtIndex(idx, range)) {
+ sb_addr.ref() = range.GetBaseAddress();
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_addr);
+}
+
+lldb::SBAddress SBBlock::GetRangeEndAddress(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBAddress, SBBlock, GetRangeEndAddress, (uint32_t),
+ idx);
+
+ lldb::SBAddress sb_addr;
+ if (m_opaque_ptr) {
+ AddressRange range;
+ if (m_opaque_ptr->GetRangeAtIndex(idx, range)) {
+ sb_addr.ref() = range.GetBaseAddress();
+ sb_addr.ref().Slide(range.GetByteSize());
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_addr);
+}
+
+uint32_t SBBlock::GetRangeIndexForBlockAddress(lldb::SBAddress block_addr) {
+ LLDB_RECORD_METHOD(uint32_t, SBBlock, GetRangeIndexForBlockAddress,
+ (lldb::SBAddress), block_addr);
+
+ if (m_opaque_ptr && block_addr.IsValid()) {
+ return m_opaque_ptr->GetRangeIndexContainingAddress(block_addr.ref());
+ }
+
+ return UINT32_MAX;
+}
+
+lldb::SBValueList SBBlock::GetVariables(lldb::SBFrame &frame, bool arguments,
+ bool locals, bool statics,
+ lldb::DynamicValueType use_dynamic) {
+ LLDB_RECORD_METHOD(
+ lldb::SBValueList, SBBlock, GetVariables,
+ (lldb::SBFrame &, bool, bool, bool, lldb::DynamicValueType), frame,
+ arguments, locals, statics, use_dynamic);
+
+ Block *block = GetPtr();
+ SBValueList value_list;
+ if (block) {
+ StackFrameSP frame_sp(frame.GetFrameSP());
+ VariableListSP variable_list_sp(block->GetBlockVariableList(true));
+
+ if (variable_list_sp) {
+ const size_t num_variables = variable_list_sp->GetSize();
+ if (num_variables) {
+ for (size_t i = 0; i < num_variables; ++i) {
+ VariableSP variable_sp(variable_list_sp->GetVariableAtIndex(i));
+ if (variable_sp) {
+ bool add_variable = false;
+ switch (variable_sp->GetScope()) {
+ case eValueTypeVariableGlobal:
+ case eValueTypeVariableStatic:
+ case eValueTypeVariableThreadLocal:
+ add_variable = statics;
+ break;
+
+ case eValueTypeVariableArgument:
+ add_variable = arguments;
+ break;
+
+ case eValueTypeVariableLocal:
+ add_variable = locals;
+ break;
+
+ default:
+ break;
+ }
+ if (add_variable) {
+ if (frame_sp) {
+ lldb::ValueObjectSP valobj_sp(
+ frame_sp->GetValueObjectForFrameVariable(variable_sp,
+ eNoDynamicValues));
+ SBValue value_sb;
+ value_sb.SetSP(valobj_sp, use_dynamic);
+ value_list.Append(value_sb);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(value_list);
+}
+
+lldb::SBValueList SBBlock::GetVariables(lldb::SBTarget &target, bool arguments,
+ bool locals, bool statics) {
+ LLDB_RECORD_METHOD(lldb::SBValueList, SBBlock, GetVariables,
+ (lldb::SBTarget &, bool, bool, bool), target, arguments,
+ locals, statics);
+
+ Block *block = GetPtr();
+
+ SBValueList value_list;
+ if (block) {
+ TargetSP target_sp(target.GetSP());
+
+ VariableListSP variable_list_sp(block->GetBlockVariableList(true));
+
+ if (variable_list_sp) {
+ const size_t num_variables = variable_list_sp->GetSize();
+ if (num_variables) {
+ for (size_t i = 0; i < num_variables; ++i) {
+ VariableSP variable_sp(variable_list_sp->GetVariableAtIndex(i));
+ if (variable_sp) {
+ bool add_variable = false;
+ switch (variable_sp->GetScope()) {
+ case eValueTypeVariableGlobal:
+ case eValueTypeVariableStatic:
+ case eValueTypeVariableThreadLocal:
+ add_variable = statics;
+ break;
+
+ case eValueTypeVariableArgument:
+ add_variable = arguments;
+ break;
+
+ case eValueTypeVariableLocal:
+ add_variable = locals;
+ break;
+
+ default:
+ break;
+ }
+ if (add_variable) {
+ if (target_sp)
+ value_list.Append(
+ ValueObjectVariable::Create(target_sp.get(), variable_sp));
+ }
+ }
+ }
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(value_list);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBBlock>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBBlock, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBBlock, (const lldb::SBBlock &));
+ LLDB_REGISTER_METHOD(const lldb::SBBlock &,
+ SBBlock, operator=,(const lldb::SBBlock &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBBlock, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBBlock, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBBlock, IsInlined, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBBlock, GetInlinedName, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBFileSpec, SBBlock,
+ GetInlinedCallSiteFile, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBBlock, GetInlinedCallSiteLine, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBBlock, GetInlinedCallSiteColumn, ());
+ LLDB_REGISTER_METHOD(lldb::SBBlock, SBBlock, GetParent, ());
+ LLDB_REGISTER_METHOD(lldb::SBBlock, SBBlock, GetContainingInlinedBlock, ());
+ LLDB_REGISTER_METHOD(lldb::SBBlock, SBBlock, GetSibling, ());
+ LLDB_REGISTER_METHOD(lldb::SBBlock, SBBlock, GetFirstChild, ());
+ LLDB_REGISTER_METHOD(bool, SBBlock, GetDescription, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(uint32_t, SBBlock, GetNumRanges, ());
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBBlock, GetRangeStartAddress,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBBlock, GetRangeEndAddress,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBBlock, GetRangeIndexForBlockAddress,
+ (lldb::SBAddress));
+ LLDB_REGISTER_METHOD(
+ lldb::SBValueList, SBBlock, GetVariables,
+ (lldb::SBFrame &, bool, bool, bool, lldb::DynamicValueType));
+ LLDB_REGISTER_METHOD(lldb::SBValueList, SBBlock, GetVariables,
+ (lldb::SBTarget &, bool, bool, bool));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBBreakpoint.cpp b/contrib/llvm-project/lldb/source/API/SBBreakpoint.cpp
new file mode 100644
index 000000000000..45eaea6b6181
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBBreakpoint.cpp
@@ -0,0 +1,1036 @@
+//===-- SBBreakpoint.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBBreakpoint.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBBreakpointLocation.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+#include "lldb/API/SBThread.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointIDList.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointResolver.h"
+#include "lldb/Breakpoint/BreakpointResolverScripted.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Stream.h"
+
+#include "SBBreakpointOptionCommon.h"
+
+#include "lldb/lldb-enumerations.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBBreakpoint::SBBreakpoint() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBBreakpoint); }
+
+SBBreakpoint::SBBreakpoint(const SBBreakpoint &rhs)
+ : m_opaque_wp(rhs.m_opaque_wp) {
+ LLDB_RECORD_CONSTRUCTOR(SBBreakpoint, (const lldb::SBBreakpoint &), rhs);
+}
+
+SBBreakpoint::SBBreakpoint(const lldb::BreakpointSP &bp_sp)
+ : m_opaque_wp(bp_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBBreakpoint, (const lldb::BreakpointSP &), bp_sp);
+}
+
+SBBreakpoint::~SBBreakpoint() = default;
+
+const SBBreakpoint &SBBreakpoint::operator=(const SBBreakpoint &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBBreakpoint &,
+ SBBreakpoint, operator=,(const lldb::SBBreakpoint &), rhs);
+
+ m_opaque_wp = rhs.m_opaque_wp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBBreakpoint::operator==(const lldb::SBBreakpoint &rhs) {
+ LLDB_RECORD_METHOD(
+ bool, SBBreakpoint, operator==,(const lldb::SBBreakpoint &), rhs);
+
+ return m_opaque_wp.lock() == rhs.m_opaque_wp.lock();
+}
+
+bool SBBreakpoint::operator!=(const lldb::SBBreakpoint &rhs) {
+ LLDB_RECORD_METHOD(
+ bool, SBBreakpoint, operator!=,(const lldb::SBBreakpoint &), rhs);
+
+ return m_opaque_wp.lock() != rhs.m_opaque_wp.lock();
+}
+
+break_id_t SBBreakpoint::GetID() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::break_id_t, SBBreakpoint, GetID);
+
+ break_id_t break_id = LLDB_INVALID_BREAK_ID;
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp)
+ break_id = bkpt_sp->GetID();
+
+ return break_id;
+}
+
+bool SBBreakpoint::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBreakpoint, IsValid);
+ return this->operator bool();
+}
+SBBreakpoint::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBreakpoint, operator bool);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (!bkpt_sp)
+ return false;
+ else if (bkpt_sp->GetTarget().GetBreakpointByID(bkpt_sp->GetID()))
+ return true;
+ else
+ return false;
+}
+
+void SBBreakpoint::ClearAllBreakpointSites() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBBreakpoint, ClearAllBreakpointSites);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->ClearAllBreakpointSites();
+ }
+}
+
+SBBreakpointLocation SBBreakpoint::FindLocationByAddress(addr_t vm_addr) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpointLocation, SBBreakpoint,
+ FindLocationByAddress, (lldb::addr_t), vm_addr);
+
+ SBBreakpointLocation sb_bp_location;
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ if (vm_addr != LLDB_INVALID_ADDRESS) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ Address address;
+ Target &target = bkpt_sp->GetTarget();
+ if (!target.GetSectionLoadList().ResolveLoadAddress(vm_addr, address)) {
+ address.SetRawAddress(vm_addr);
+ }
+ sb_bp_location.SetLocation(bkpt_sp->FindLocationByAddress(address));
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_bp_location);
+}
+
+break_id_t SBBreakpoint::FindLocationIDByAddress(addr_t vm_addr) {
+ LLDB_RECORD_METHOD(lldb::break_id_t, SBBreakpoint, FindLocationIDByAddress,
+ (lldb::addr_t), vm_addr);
+
+ break_id_t break_id = LLDB_INVALID_BREAK_ID;
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp && vm_addr != LLDB_INVALID_ADDRESS) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ Address address;
+ Target &target = bkpt_sp->GetTarget();
+ if (!target.GetSectionLoadList().ResolveLoadAddress(vm_addr, address)) {
+ address.SetRawAddress(vm_addr);
+ }
+ break_id = bkpt_sp->FindLocationIDByAddress(address);
+ }
+
+ return break_id;
+}
+
+SBBreakpointLocation SBBreakpoint::FindLocationByID(break_id_t bp_loc_id) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpointLocation, SBBreakpoint, FindLocationByID,
+ (lldb::break_id_t), bp_loc_id);
+
+ SBBreakpointLocation sb_bp_location;
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ sb_bp_location.SetLocation(bkpt_sp->FindLocationByID(bp_loc_id));
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp_location);
+}
+
+SBBreakpointLocation SBBreakpoint::GetLocationAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpointLocation, SBBreakpoint,
+ GetLocationAtIndex, (uint32_t), index);
+
+ SBBreakpointLocation sb_bp_location;
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ sb_bp_location.SetLocation(bkpt_sp->GetLocationAtIndex(index));
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp_location);
+}
+
+void SBBreakpoint::SetEnabled(bool enable) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetEnabled, (bool), enable);
+
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->SetEnabled(enable);
+ }
+}
+
+bool SBBreakpoint::IsEnabled() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpoint, IsEnabled);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ return bkpt_sp->IsEnabled();
+ } else
+ return false;
+}
+
+void SBBreakpoint::SetOneShot(bool one_shot) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetOneShot, (bool), one_shot);
+
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->SetOneShot(one_shot);
+ }
+}
+
+bool SBBreakpoint::IsOneShot() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBreakpoint, IsOneShot);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ return bkpt_sp->IsOneShot();
+ } else
+ return false;
+}
+
+bool SBBreakpoint::IsInternal() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpoint, IsInternal);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ return bkpt_sp->IsInternal();
+ } else
+ return false;
+}
+
+void SBBreakpoint::SetIgnoreCount(uint32_t count) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetIgnoreCount, (uint32_t), count);
+
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->SetIgnoreCount(count);
+ }
+}
+
+void SBBreakpoint::SetCondition(const char *condition) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetCondition, (const char *),
+ condition);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->SetCondition(condition);
+ }
+}
+
+const char *SBBreakpoint::GetCondition() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBBreakpoint, GetCondition);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ return bkpt_sp->GetConditionText();
+ }
+ return nullptr;
+}
+
+void SBBreakpoint::SetAutoContinue(bool auto_continue) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetAutoContinue, (bool),
+ auto_continue);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->SetAutoContinue(auto_continue);
+ }
+}
+
+bool SBBreakpoint::GetAutoContinue() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpoint, GetAutoContinue);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ return bkpt_sp->IsAutoContinue();
+ }
+ return false;
+}
+
+uint32_t SBBreakpoint::GetHitCount() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBBreakpoint, GetHitCount);
+
+ uint32_t count = 0;
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ count = bkpt_sp->GetHitCount();
+ }
+
+ return count;
+}
+
+uint32_t SBBreakpoint::GetIgnoreCount() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBBreakpoint, GetIgnoreCount);
+
+ uint32_t count = 0;
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ count = bkpt_sp->GetIgnoreCount();
+ }
+
+ return count;
+}
+
+void SBBreakpoint::SetThreadID(tid_t tid) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetThreadID, (lldb::tid_t), tid);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->SetThreadID(tid);
+ }
+}
+
+tid_t SBBreakpoint::GetThreadID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::tid_t, SBBreakpoint, GetThreadID);
+
+ tid_t tid = LLDB_INVALID_THREAD_ID;
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ tid = bkpt_sp->GetThreadID();
+ }
+
+ return tid;
+}
+
+void SBBreakpoint::SetThreadIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetThreadIndex, (uint32_t), index);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->GetOptions()->GetThreadSpec()->SetIndex(index);
+ }
+}
+
+uint32_t SBBreakpoint::GetThreadIndex() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBBreakpoint, GetThreadIndex);
+
+ uint32_t thread_idx = UINT32_MAX;
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ const ThreadSpec *thread_spec =
+ bkpt_sp->GetOptions()->GetThreadSpecNoCreate();
+ if (thread_spec != nullptr)
+ thread_idx = thread_spec->GetIndex();
+ }
+
+ return thread_idx;
+}
+
+void SBBreakpoint::SetThreadName(const char *thread_name) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetThreadName, (const char *),
+ thread_name);
+
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->GetOptions()->GetThreadSpec()->SetName(thread_name);
+ }
+}
+
+const char *SBBreakpoint::GetThreadName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBBreakpoint, GetThreadName);
+
+ const char *name = nullptr;
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ const ThreadSpec *thread_spec =
+ bkpt_sp->GetOptions()->GetThreadSpecNoCreate();
+ if (thread_spec != nullptr)
+ name = thread_spec->GetName();
+ }
+
+ return name;
+}
+
+void SBBreakpoint::SetQueueName(const char *queue_name) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetQueueName, (const char *),
+ queue_name);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->GetOptions()->GetThreadSpec()->SetQueueName(queue_name);
+ }
+}
+
+const char *SBBreakpoint::GetQueueName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBBreakpoint, GetQueueName);
+
+ const char *name = nullptr;
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ const ThreadSpec *thread_spec =
+ bkpt_sp->GetOptions()->GetThreadSpecNoCreate();
+ if (thread_spec)
+ name = thread_spec->GetQueueName();
+ }
+
+ return name;
+}
+
+size_t SBBreakpoint::GetNumResolvedLocations() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(size_t, SBBreakpoint,
+ GetNumResolvedLocations);
+
+ size_t num_resolved = 0;
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ num_resolved = bkpt_sp->GetNumResolvedLocations();
+ }
+ return num_resolved;
+}
+
+size_t SBBreakpoint::GetNumLocations() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(size_t, SBBreakpoint, GetNumLocations);
+
+ BreakpointSP bkpt_sp = GetSP();
+ size_t num_locs = 0;
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ num_locs = bkpt_sp->GetNumLocations();
+ }
+ return num_locs;
+}
+
+void SBBreakpoint::SetCommandLineCommands(SBStringList &commands) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetCommandLineCommands,
+ (lldb::SBStringList &), commands);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (!bkpt_sp)
+ return;
+ if (commands.GetSize() == 0)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ std::unique_ptr<BreakpointOptions::CommandData> cmd_data_up(
+ new BreakpointOptions::CommandData(*commands, eScriptLanguageNone));
+
+ bkpt_sp->GetOptions()->SetCommandDataCallback(cmd_data_up);
+}
+
+bool SBBreakpoint::GetCommandLineCommands(SBStringList &commands) {
+ LLDB_RECORD_METHOD(bool, SBBreakpoint, GetCommandLineCommands,
+ (lldb::SBStringList &), commands);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (!bkpt_sp)
+ return false;
+ StringList command_list;
+ bool has_commands =
+ bkpt_sp->GetOptions()->GetCommandLineCallbacks(command_list);
+ if (has_commands)
+ commands.AppendList(command_list);
+ return has_commands;
+}
+
+bool SBBreakpoint::GetDescription(SBStream &s) {
+ LLDB_RECORD_METHOD(bool, SBBreakpoint, GetDescription, (lldb::SBStream &), s);
+
+ return GetDescription(s, true);
+}
+
+bool SBBreakpoint::GetDescription(SBStream &s, bool include_locations) {
+ LLDB_RECORD_METHOD(bool, SBBreakpoint, GetDescription,
+ (lldb::SBStream &, bool), s, include_locations);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ s.Printf("SBBreakpoint: id = %i, ", bkpt_sp->GetID());
+ bkpt_sp->GetResolverDescription(s.get());
+ bkpt_sp->GetFilterDescription(s.get());
+ if (include_locations) {
+ const size_t num_locations = bkpt_sp->GetNumLocations();
+ s.Printf(", locations = %" PRIu64, (uint64_t)num_locations);
+ }
+ return true;
+ }
+ s.Printf("No value");
+ return false;
+}
+
+SBError SBBreakpoint::AddLocation(SBAddress &address) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBBreakpoint, AddLocation,
+ (lldb::SBAddress &), address);
+
+ BreakpointSP bkpt_sp = GetSP();
+ SBError error;
+
+ if (!address.IsValid()) {
+ error.SetErrorString("Can't add an invalid address.");
+ return LLDB_RECORD_RESULT(error);
+ }
+
+ if (!bkpt_sp) {
+ error.SetErrorString("No breakpoint to add a location to.");
+ return LLDB_RECORD_RESULT(error);
+ }
+
+ if (!llvm::isa<BreakpointResolverScripted>(bkpt_sp->GetResolver().get())) {
+ error.SetErrorString("Only a scripted resolver can add locations.");
+ return LLDB_RECORD_RESULT(error);
+ }
+
+ if (bkpt_sp->GetSearchFilter()->AddressPasses(address.ref()))
+ bkpt_sp->AddLocation(address.ref());
+ else {
+ StreamString s;
+ address.get()->Dump(&s, &bkpt_sp->GetTarget(),
+ Address::DumpStyleModuleWithFileAddress);
+ error.SetErrorStringWithFormat("Address: %s didn't pass the filter.",
+ s.GetData());
+ }
+ return LLDB_RECORD_RESULT(error);
+}
+
+void SBBreakpoint ::SetCallback(SBBreakpointHitCallback callback, void *baton) {
+ LLDB_RECORD_DUMMY(void, SBBreakpoint, SetCallback,
+ (lldb::SBBreakpointHitCallback, void *), callback, baton);
+
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ BatonSP baton_sp(new SBBreakpointCallbackBaton(callback, baton));
+ bkpt_sp->SetCallback(SBBreakpointCallbackBaton
+ ::PrivateBreakpointHitCallback, baton_sp,
+ false);
+ }
+}
+
+void SBBreakpoint::SetScriptCallbackFunction(
+ const char *callback_function_name) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, SetScriptCallbackFunction,
+ (const char *), callback_function_name);
+
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ BreakpointOptions *bp_options = bkpt_sp->GetOptions();
+ bkpt_sp->GetTarget()
+ .GetDebugger()
+ .GetScriptInterpreter()
+ ->SetBreakpointCommandCallbackFunction(bp_options,
+ callback_function_name);
+ }
+}
+
+SBError SBBreakpoint::SetScriptCallbackBody(const char *callback_body_text) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBBreakpoint, SetScriptCallbackBody,
+ (const char *), callback_body_text);
+
+ BreakpointSP bkpt_sp = GetSP();
+
+ SBError sb_error;
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ BreakpointOptions *bp_options = bkpt_sp->GetOptions();
+ Status error =
+ bkpt_sp->GetTarget()
+ .GetDebugger()
+ .GetScriptInterpreter()
+ ->SetBreakpointCommandCallback(bp_options, callback_body_text);
+ sb_error.SetError(error);
+ } else
+ sb_error.SetErrorString("invalid breakpoint");
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+bool SBBreakpoint::AddName(const char *new_name) {
+ LLDB_RECORD_METHOD(bool, SBBreakpoint, AddName, (const char *), new_name);
+
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ Status error; // Think I'm just going to swallow the error here, it's
+ // probably more annoying to have to provide it.
+ bkpt_sp->GetTarget().AddNameToBreakpoint(bkpt_sp, new_name, error);
+ if (error.Fail())
+ return false;
+ }
+
+ return true;
+}
+
+void SBBreakpoint::RemoveName(const char *name_to_remove) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, RemoveName, (const char *),
+ name_to_remove);
+
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ bkpt_sp->GetTarget().RemoveNameFromBreakpoint(bkpt_sp,
+ ConstString(name_to_remove));
+ }
+}
+
+bool SBBreakpoint::MatchesName(const char *name) {
+ LLDB_RECORD_METHOD(bool, SBBreakpoint, MatchesName, (const char *), name);
+
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ return bkpt_sp->MatchesName(name);
+ }
+
+ return false;
+}
+
+void SBBreakpoint::GetNames(SBStringList &names) {
+ LLDB_RECORD_METHOD(void, SBBreakpoint, GetNames, (lldb::SBStringList &),
+ names);
+
+ BreakpointSP bkpt_sp = GetSP();
+
+ if (bkpt_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ bkpt_sp->GetTarget().GetAPIMutex());
+ std::vector<std::string> names_vec;
+ bkpt_sp->GetNames(names_vec);
+ for (std::string name : names_vec) {
+ names.AppendString(name.c_str());
+ }
+ }
+}
+
+bool SBBreakpoint::EventIsBreakpointEvent(const lldb::SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBBreakpoint, EventIsBreakpointEvent,
+ (const lldb::SBEvent &), event);
+
+ return Breakpoint::BreakpointEventData::GetEventDataFromEvent(event.get()) !=
+ nullptr;
+}
+
+BreakpointEventType
+SBBreakpoint::GetBreakpointEventTypeFromEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(lldb::BreakpointEventType, SBBreakpoint,
+ GetBreakpointEventTypeFromEvent,
+ (const lldb::SBEvent &), event);
+
+ if (event.IsValid())
+ return Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent(
+ event.GetSP());
+ return eBreakpointEventTypeInvalidType;
+}
+
+SBBreakpoint SBBreakpoint::GetBreakpointFromEvent(const lldb::SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBBreakpoint, SBBreakpoint,
+ GetBreakpointFromEvent, (const lldb::SBEvent &),
+ event);
+
+ if (event.IsValid())
+ return LLDB_RECORD_RESULT(
+ SBBreakpoint(Breakpoint::BreakpointEventData::GetBreakpointFromEvent(
+ event.GetSP())));
+ return LLDB_RECORD_RESULT(SBBreakpoint());
+}
+
+SBBreakpointLocation
+SBBreakpoint::GetBreakpointLocationAtIndexFromEvent(const lldb::SBEvent &event,
+ uint32_t loc_idx) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBBreakpointLocation, SBBreakpoint,
+ GetBreakpointLocationAtIndexFromEvent,
+ (const lldb::SBEvent &, uint32_t), event, loc_idx);
+
+ SBBreakpointLocation sb_breakpoint_loc;
+ if (event.IsValid())
+ sb_breakpoint_loc.SetLocation(
+ Breakpoint::BreakpointEventData::GetBreakpointLocationAtIndexFromEvent(
+ event.GetSP(), loc_idx));
+ return LLDB_RECORD_RESULT(sb_breakpoint_loc);
+}
+
+uint32_t
+SBBreakpoint::GetNumBreakpointLocationsFromEvent(const lldb::SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(uint32_t, SBBreakpoint,
+ GetNumBreakpointLocationsFromEvent,
+ (const lldb::SBEvent &), event);
+
+ uint32_t num_locations = 0;
+ if (event.IsValid())
+ num_locations =
+ (Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent(
+ event.GetSP()));
+ return num_locations;
+}
+
+bool SBBreakpoint::IsHardware() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBreakpoint, IsHardware);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp)
+ return bkpt_sp->IsHardware();
+ return false;
+}
+
+BreakpointSP SBBreakpoint::GetSP() const { return m_opaque_wp.lock(); }
+
+// This is simple collection of breakpoint id's and their target.
+class SBBreakpointListImpl {
+public:
+ SBBreakpointListImpl(lldb::TargetSP target_sp) : m_target_wp() {
+ if (target_sp && target_sp->IsValid())
+ m_target_wp = target_sp;
+ }
+
+ ~SBBreakpointListImpl() = default;
+
+ size_t GetSize() { return m_break_ids.size(); }
+
+ BreakpointSP GetBreakpointAtIndex(size_t idx) {
+ if (idx >= m_break_ids.size())
+ return BreakpointSP();
+ TargetSP target_sp = m_target_wp.lock();
+ if (!target_sp)
+ return BreakpointSP();
+ lldb::break_id_t bp_id = m_break_ids[idx];
+ return target_sp->GetBreakpointList().FindBreakpointByID(bp_id);
+ }
+
+ BreakpointSP FindBreakpointByID(lldb::break_id_t desired_id) {
+ TargetSP target_sp = m_target_wp.lock();
+ if (!target_sp)
+ return BreakpointSP();
+
+ for (lldb::break_id_t &break_id : m_break_ids) {
+ if (break_id == desired_id)
+ return target_sp->GetBreakpointList().FindBreakpointByID(break_id);
+ }
+ return BreakpointSP();
+ }
+
+ bool Append(BreakpointSP bkpt) {
+ TargetSP target_sp = m_target_wp.lock();
+ if (!target_sp || !bkpt)
+ return false;
+ if (bkpt->GetTargetSP() != target_sp)
+ return false;
+ m_break_ids.push_back(bkpt->GetID());
+ return true;
+ }
+
+ bool AppendIfUnique(BreakpointSP bkpt) {
+ TargetSP target_sp = m_target_wp.lock();
+ if (!target_sp || !bkpt)
+ return false;
+ if (bkpt->GetTargetSP() != target_sp)
+ return false;
+ lldb::break_id_t bp_id = bkpt->GetID();
+ if (find(m_break_ids.begin(), m_break_ids.end(), bp_id) ==
+ m_break_ids.end())
+ return false;
+
+ m_break_ids.push_back(bkpt->GetID());
+ return true;
+ }
+
+ bool AppendByID(lldb::break_id_t id) {
+ TargetSP target_sp = m_target_wp.lock();
+ if (!target_sp)
+ return false;
+ if (id == LLDB_INVALID_BREAK_ID)
+ return false;
+ m_break_ids.push_back(id);
+ return true;
+ }
+
+ void Clear() { m_break_ids.clear(); }
+
+ void CopyToBreakpointIDList(lldb_private::BreakpointIDList &bp_list) {
+ for (lldb::break_id_t id : m_break_ids) {
+ bp_list.AddBreakpointID(BreakpointID(id));
+ }
+ }
+
+ TargetSP GetTarget() { return m_target_wp.lock(); }
+
+private:
+ std::vector<lldb::break_id_t> m_break_ids;
+ TargetWP m_target_wp;
+};
+
+SBBreakpointList::SBBreakpointList(SBTarget &target)
+ : m_opaque_sp(new SBBreakpointListImpl(target.GetSP())) {
+ LLDB_RECORD_CONSTRUCTOR(SBBreakpointList, (lldb::SBTarget &), target);
+}
+
+SBBreakpointList::~SBBreakpointList() {}
+
+size_t SBBreakpointList::GetSize() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(size_t, SBBreakpointList, GetSize);
+
+ if (!m_opaque_sp)
+ return 0;
+ else
+ return m_opaque_sp->GetSize();
+}
+
+SBBreakpoint SBBreakpointList::GetBreakpointAtIndex(size_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBBreakpointList, GetBreakpointAtIndex,
+ (size_t), idx);
+
+ if (!m_opaque_sp)
+ return LLDB_RECORD_RESULT(SBBreakpoint());
+
+ BreakpointSP bkpt_sp = m_opaque_sp->GetBreakpointAtIndex(idx);
+ return LLDB_RECORD_RESULT(SBBreakpoint(bkpt_sp));
+}
+
+SBBreakpoint SBBreakpointList::FindBreakpointByID(lldb::break_id_t id) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBBreakpointList, FindBreakpointByID,
+ (lldb::break_id_t), id);
+
+ if (!m_opaque_sp)
+ return LLDB_RECORD_RESULT(SBBreakpoint());
+ BreakpointSP bkpt_sp = m_opaque_sp->FindBreakpointByID(id);
+ return LLDB_RECORD_RESULT(SBBreakpoint(bkpt_sp));
+}
+
+void SBBreakpointList::Append(const SBBreakpoint &sb_bkpt) {
+ LLDB_RECORD_METHOD(void, SBBreakpointList, Append,
+ (const lldb::SBBreakpoint &), sb_bkpt);
+
+ if (!sb_bkpt.IsValid())
+ return;
+ if (!m_opaque_sp)
+ return;
+ m_opaque_sp->Append(sb_bkpt.m_opaque_wp.lock());
+}
+
+void SBBreakpointList::AppendByID(lldb::break_id_t id) {
+ LLDB_RECORD_METHOD(void, SBBreakpointList, AppendByID, (lldb::break_id_t),
+ id);
+
+ if (!m_opaque_sp)
+ return;
+ m_opaque_sp->AppendByID(id);
+}
+
+bool SBBreakpointList::AppendIfUnique(const SBBreakpoint &sb_bkpt) {
+ LLDB_RECORD_METHOD(bool, SBBreakpointList, AppendIfUnique,
+ (const lldb::SBBreakpoint &), sb_bkpt);
+
+ if (!sb_bkpt.IsValid())
+ return false;
+ if (!m_opaque_sp)
+ return false;
+ return m_opaque_sp->AppendIfUnique(sb_bkpt.GetSP());
+}
+
+void SBBreakpointList::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBBreakpointList, Clear);
+
+ if (m_opaque_sp)
+ m_opaque_sp->Clear();
+}
+
+void SBBreakpointList::CopyToBreakpointIDList(
+ lldb_private::BreakpointIDList &bp_id_list) {
+ if (m_opaque_sp)
+ m_opaque_sp->CopyToBreakpointIDList(bp_id_list);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBBreakpoint>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBBreakpoint, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBBreakpoint, (const lldb::SBBreakpoint &));
+ LLDB_REGISTER_CONSTRUCTOR(SBBreakpoint, (const lldb::BreakpointSP &));
+ LLDB_REGISTER_METHOD(const lldb::SBBreakpoint &,
+ SBBreakpoint, operator=,(const lldb::SBBreakpoint &));
+ LLDB_REGISTER_METHOD(bool,
+ SBBreakpoint, operator==,(const lldb::SBBreakpoint &));
+ LLDB_REGISTER_METHOD(bool,
+ SBBreakpoint, operator!=,(const lldb::SBBreakpoint &));
+ LLDB_REGISTER_METHOD_CONST(lldb::break_id_t, SBBreakpoint, GetID, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBBreakpoint, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBBreakpoint, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, ClearAllBreakpointSites, ());
+ LLDB_REGISTER_METHOD(lldb::SBBreakpointLocation, SBBreakpoint,
+ FindLocationByAddress, (lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::break_id_t, SBBreakpoint,
+ FindLocationIDByAddress, (lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpointLocation, SBBreakpoint,
+ FindLocationByID, (lldb::break_id_t));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpointLocation, SBBreakpoint,
+ GetLocationAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetEnabled, (bool));
+ LLDB_REGISTER_METHOD(bool, SBBreakpoint, IsEnabled, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetOneShot, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBBreakpoint, IsOneShot, ());
+ LLDB_REGISTER_METHOD(bool, SBBreakpoint, IsInternal, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetIgnoreCount, (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetCondition, (const char *));
+ LLDB_REGISTER_METHOD(const char *, SBBreakpoint, GetCondition, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetAutoContinue, (bool));
+ LLDB_REGISTER_METHOD(bool, SBBreakpoint, GetAutoContinue, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBBreakpoint, GetHitCount, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBBreakpoint, GetIgnoreCount, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetThreadID, (lldb::tid_t));
+ LLDB_REGISTER_METHOD(lldb::tid_t, SBBreakpoint, GetThreadID, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetThreadIndex, (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBBreakpoint, GetThreadIndex, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetThreadName, (const char *));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBBreakpoint, GetThreadName, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetQueueName, (const char *));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBBreakpoint, GetQueueName, ());
+ LLDB_REGISTER_METHOD_CONST(size_t, SBBreakpoint, GetNumResolvedLocations,
+ ());
+ LLDB_REGISTER_METHOD_CONST(size_t, SBBreakpoint, GetNumLocations, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetCommandLineCommands,
+ (lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(bool, SBBreakpoint, GetCommandLineCommands,
+ (lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(bool, SBBreakpoint, GetDescription,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(bool, SBBreakpoint, GetDescription,
+ (lldb::SBStream &, bool));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpoint, AddLocation,
+ (lldb::SBAddress &));
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, SetScriptCallbackFunction,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpoint, SetScriptCallbackBody,
+ (const char *));
+ LLDB_REGISTER_METHOD(bool, SBBreakpoint, AddName, (const char *));
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, RemoveName, (const char *));
+ LLDB_REGISTER_METHOD(bool, SBBreakpoint, MatchesName, (const char *));
+ LLDB_REGISTER_METHOD(void, SBBreakpoint, GetNames, (lldb::SBStringList &));
+ LLDB_REGISTER_STATIC_METHOD(bool, SBBreakpoint, EventIsBreakpointEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::BreakpointEventType, SBBreakpoint,
+ GetBreakpointEventTypeFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBBreakpoint, SBBreakpoint,
+ GetBreakpointFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBBreakpointLocation, SBBreakpoint,
+ GetBreakpointLocationAtIndexFromEvent,
+ (const lldb::SBEvent &, uint32_t));
+ LLDB_REGISTER_STATIC_METHOD(uint32_t, SBBreakpoint,
+ GetNumBreakpointLocationsFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBBreakpoint, IsHardware, ());
+}
+
+template <>
+void RegisterMethods<SBBreakpointList>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBBreakpointList, (lldb::SBTarget &));
+ LLDB_REGISTER_METHOD_CONST(size_t, SBBreakpointList, GetSize, ());
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBBreakpointList,
+ GetBreakpointAtIndex, (size_t));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBBreakpointList,
+ FindBreakpointByID, (lldb::break_id_t));
+ LLDB_REGISTER_METHOD(void, SBBreakpointList, Append,
+ (const lldb::SBBreakpoint &));
+ LLDB_REGISTER_METHOD(void, SBBreakpointList, AppendByID,
+ (lldb::break_id_t));
+ LLDB_REGISTER_METHOD(bool, SBBreakpointList, AppendIfUnique,
+ (const lldb::SBBreakpoint &));
+ LLDB_REGISTER_METHOD(void, SBBreakpointList, Clear, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBBreakpointLocation.cpp b/contrib/llvm-project/lldb/source/API/SBBreakpointLocation.cpp
new file mode 100644
index 000000000000..640545f55ef9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBBreakpointLocation.cpp
@@ -0,0 +1,515 @@
+//===-- SBBreakpointLocation.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBBreakpointLocation.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBDefines.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBBreakpointLocation::SBBreakpointLocation() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBBreakpointLocation);
+}
+
+SBBreakpointLocation::SBBreakpointLocation(
+ const lldb::BreakpointLocationSP &break_loc_sp)
+ : m_opaque_wp(break_loc_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBBreakpointLocation,
+ (const lldb::BreakpointLocationSP &), break_loc_sp);
+}
+
+SBBreakpointLocation::SBBreakpointLocation(const SBBreakpointLocation &rhs)
+ : m_opaque_wp(rhs.m_opaque_wp) {
+ LLDB_RECORD_CONSTRUCTOR(SBBreakpointLocation,
+ (const lldb::SBBreakpointLocation &), rhs);
+}
+
+const SBBreakpointLocation &SBBreakpointLocation::
+operator=(const SBBreakpointLocation &rhs) {
+ LLDB_RECORD_METHOD(
+ const lldb::SBBreakpointLocation &,
+ SBBreakpointLocation, operator=,(const lldb::SBBreakpointLocation &),
+ rhs);
+
+ m_opaque_wp = rhs.m_opaque_wp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBBreakpointLocation::~SBBreakpointLocation() {}
+
+BreakpointLocationSP SBBreakpointLocation::GetSP() const {
+ return m_opaque_wp.lock();
+}
+
+bool SBBreakpointLocation::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBreakpointLocation, IsValid);
+ return this->operator bool();
+}
+SBBreakpointLocation::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBreakpointLocation, operator bool);
+
+ return bool(GetSP());
+}
+
+SBAddress SBBreakpointLocation::GetAddress() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBAddress, SBBreakpointLocation, GetAddress);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ return LLDB_RECORD_RESULT(SBAddress(&loc_sp->GetAddress()));
+ }
+
+ return LLDB_RECORD_RESULT(SBAddress());
+}
+
+addr_t SBBreakpointLocation::GetLoadAddress() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBBreakpointLocation,
+ GetLoadAddress);
+
+ addr_t ret_addr = LLDB_INVALID_ADDRESS;
+ BreakpointLocationSP loc_sp = GetSP();
+
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ ret_addr = loc_sp->GetLoadAddress();
+ }
+
+ return ret_addr;
+}
+
+void SBBreakpointLocation::SetEnabled(bool enabled) {
+ LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetEnabled, (bool), enabled);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ loc_sp->SetEnabled(enabled);
+ }
+}
+
+bool SBBreakpointLocation::IsEnabled() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpointLocation, IsEnabled);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ return loc_sp->IsEnabled();
+ } else
+ return false;
+}
+
+uint32_t SBBreakpointLocation::GetHitCount() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBBreakpointLocation, GetHitCount);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ return loc_sp->GetHitCount();
+ } else
+ return 0;
+}
+
+uint32_t SBBreakpointLocation::GetIgnoreCount() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBBreakpointLocation, GetIgnoreCount);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ return loc_sp->GetIgnoreCount();
+ } else
+ return 0;
+}
+
+void SBBreakpointLocation::SetIgnoreCount(uint32_t n) {
+ LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetIgnoreCount, (uint32_t), n);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ loc_sp->SetIgnoreCount(n);
+ }
+}
+
+void SBBreakpointLocation::SetCondition(const char *condition) {
+ LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetCondition, (const char *),
+ condition);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ loc_sp->SetCondition(condition);
+ }
+}
+
+const char *SBBreakpointLocation::GetCondition() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBBreakpointLocation, GetCondition);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ return loc_sp->GetConditionText();
+ }
+ return nullptr;
+}
+
+void SBBreakpointLocation::SetAutoContinue(bool auto_continue) {
+ LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetAutoContinue, (bool),
+ auto_continue);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ loc_sp->SetAutoContinue(auto_continue);
+ }
+}
+
+bool SBBreakpointLocation::GetAutoContinue() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpointLocation, GetAutoContinue);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ return loc_sp->IsAutoContinue();
+ }
+ return false;
+}
+
+void SBBreakpointLocation::SetScriptCallbackFunction(
+ const char *callback_function_name) {
+ LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction,
+ (const char *), callback_function_name);
+
+ BreakpointLocationSP loc_sp = GetSP();
+
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ BreakpointOptions *bp_options = loc_sp->GetLocationOptions();
+ loc_sp->GetBreakpoint()
+ .GetTarget()
+ .GetDebugger()
+ .GetScriptInterpreter()
+ ->SetBreakpointCommandCallbackFunction(bp_options,
+ callback_function_name);
+ }
+}
+
+SBError
+SBBreakpointLocation::SetScriptCallbackBody(const char *callback_body_text) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBBreakpointLocation, SetScriptCallbackBody,
+ (const char *), callback_body_text);
+
+ BreakpointLocationSP loc_sp = GetSP();
+
+ SBError sb_error;
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ BreakpointOptions *bp_options = loc_sp->GetLocationOptions();
+ Status error =
+ loc_sp->GetBreakpoint()
+ .GetTarget()
+ .GetDebugger()
+ .GetScriptInterpreter()
+ ->SetBreakpointCommandCallback(bp_options, callback_body_text);
+ sb_error.SetError(error);
+ } else
+ sb_error.SetErrorString("invalid breakpoint");
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+void SBBreakpointLocation::SetCommandLineCommands(SBStringList &commands) {
+ LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetCommandLineCommands,
+ (lldb::SBStringList &), commands);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (!loc_sp)
+ return;
+ if (commands.GetSize() == 0)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ std::unique_ptr<BreakpointOptions::CommandData> cmd_data_up(
+ new BreakpointOptions::CommandData(*commands, eScriptLanguageNone));
+
+ loc_sp->GetLocationOptions()->SetCommandDataCallback(cmd_data_up);
+}
+
+bool SBBreakpointLocation::GetCommandLineCommands(SBStringList &commands) {
+ LLDB_RECORD_METHOD(bool, SBBreakpointLocation, GetCommandLineCommands,
+ (lldb::SBStringList &), commands);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (!loc_sp)
+ return false;
+ StringList command_list;
+ bool has_commands =
+ loc_sp->GetLocationOptions()->GetCommandLineCallbacks(command_list);
+ if (has_commands)
+ commands.AppendList(command_list);
+ return has_commands;
+}
+
+void SBBreakpointLocation::SetThreadID(tid_t thread_id) {
+ LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetThreadID, (lldb::tid_t),
+ thread_id);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ loc_sp->SetThreadID(thread_id);
+ }
+}
+
+tid_t SBBreakpointLocation::GetThreadID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::tid_t, SBBreakpointLocation, GetThreadID);
+
+ tid_t tid = LLDB_INVALID_THREAD_ID;
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ return loc_sp->GetThreadID();
+ }
+ return tid;
+}
+
+void SBBreakpointLocation::SetThreadIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetThreadIndex, (uint32_t),
+ index);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ loc_sp->SetThreadIndex(index);
+ }
+}
+
+uint32_t SBBreakpointLocation::GetThreadIndex() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBBreakpointLocation,
+ GetThreadIndex);
+
+ uint32_t thread_idx = UINT32_MAX;
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ return loc_sp->GetThreadIndex();
+ }
+ return thread_idx;
+}
+
+void SBBreakpointLocation::SetThreadName(const char *thread_name) {
+ LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetThreadName, (const char *),
+ thread_name);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ loc_sp->SetThreadName(thread_name);
+ }
+}
+
+const char *SBBreakpointLocation::GetThreadName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBBreakpointLocation,
+ GetThreadName);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ return loc_sp->GetThreadName();
+ }
+ return nullptr;
+}
+
+void SBBreakpointLocation::SetQueueName(const char *queue_name) {
+ LLDB_RECORD_METHOD(void, SBBreakpointLocation, SetQueueName, (const char *),
+ queue_name);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ loc_sp->SetQueueName(queue_name);
+ }
+}
+
+const char *SBBreakpointLocation::GetQueueName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBBreakpointLocation,
+ GetQueueName);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ loc_sp->GetQueueName();
+ }
+ return nullptr;
+}
+
+bool SBBreakpointLocation::IsResolved() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpointLocation, IsResolved);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ return loc_sp->IsResolved();
+ }
+ return false;
+}
+
+void SBBreakpointLocation::SetLocation(
+ const lldb::BreakpointLocationSP &break_loc_sp) {
+ // Uninstall the callbacks?
+ m_opaque_wp = break_loc_sp;
+}
+
+bool SBBreakpointLocation::GetDescription(SBStream &description,
+ DescriptionLevel level) {
+ LLDB_RECORD_METHOD(bool, SBBreakpointLocation, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ level);
+
+ Stream &strm = description.ref();
+ BreakpointLocationSP loc_sp = GetSP();
+
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ loc_sp->GetDescription(&strm, level);
+ strm.EOL();
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+break_id_t SBBreakpointLocation::GetID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::break_id_t, SBBreakpointLocation, GetID);
+
+ BreakpointLocationSP loc_sp = GetSP();
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ return loc_sp->GetID();
+ } else
+ return LLDB_INVALID_BREAK_ID;
+}
+
+SBBreakpoint SBBreakpointLocation::GetBreakpoint() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBBreakpoint, SBBreakpointLocation,
+ GetBreakpoint);
+
+ BreakpointLocationSP loc_sp = GetSP();
+
+ SBBreakpoint sb_bp;
+ if (loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ loc_sp->GetTarget().GetAPIMutex());
+ sb_bp = loc_sp->GetBreakpoint().shared_from_this();
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBBreakpointLocation>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBBreakpointLocation, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBBreakpointLocation,
+ (const lldb::BreakpointLocationSP &));
+ LLDB_REGISTER_CONSTRUCTOR(SBBreakpointLocation,
+ (const lldb::SBBreakpointLocation &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBBreakpointLocation &,
+ SBBreakpointLocation, operator=,(const lldb::SBBreakpointLocation &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBBreakpointLocation, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBBreakpointLocation, operator bool, ());
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBBreakpointLocation, GetAddress, ());
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBBreakpointLocation, GetLoadAddress,
+ ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetEnabled, (bool));
+ LLDB_REGISTER_METHOD(bool, SBBreakpointLocation, IsEnabled, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBBreakpointLocation, GetHitCount, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBBreakpointLocation, GetIgnoreCount, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetIgnoreCount,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetCondition,
+ (const char *));
+ LLDB_REGISTER_METHOD(const char *, SBBreakpointLocation, GetCondition, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetAutoContinue, (bool));
+ LLDB_REGISTER_METHOD(bool, SBBreakpointLocation, GetAutoContinue, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetScriptCallbackFunction,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpointLocation,
+ SetScriptCallbackBody, (const char *));
+ LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetCommandLineCommands,
+ (lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(bool, SBBreakpointLocation, GetCommandLineCommands,
+ (lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetThreadID,
+ (lldb::tid_t));
+ LLDB_REGISTER_METHOD(lldb::tid_t, SBBreakpointLocation, GetThreadID, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetThreadIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBBreakpointLocation, GetThreadIndex,
+ ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetThreadName,
+ (const char *));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBBreakpointLocation,
+ GetThreadName, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointLocation, SetQueueName,
+ (const char *));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBBreakpointLocation, GetQueueName,
+ ());
+ LLDB_REGISTER_METHOD(bool, SBBreakpointLocation, IsResolved, ());
+ LLDB_REGISTER_METHOD(bool, SBBreakpointLocation, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+ LLDB_REGISTER_METHOD(lldb::break_id_t, SBBreakpointLocation, GetID, ());
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBBreakpointLocation,
+ GetBreakpoint, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBBreakpointName.cpp b/contrib/llvm-project/lldb/source/API/SBBreakpointName.cpp
new file mode 100644
index 000000000000..1c794fca8ca5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBBreakpointName.cpp
@@ -0,0 +1,742 @@
+//===-- SBBreakpointName.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBBreakpointName.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+#include "lldb/API/SBTarget.h"
+
+#include "lldb/Breakpoint/BreakpointName.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Stream.h"
+
+#include "SBBreakpointOptionCommon.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace lldb
+{
+class SBBreakpointNameImpl {
+public:
+ SBBreakpointNameImpl(TargetSP target_sp, const char *name) {
+ if (!name || name[0] == '\0')
+ return;
+ m_name.assign(name);
+
+ if (!target_sp)
+ return;
+
+ m_target_wp = target_sp;
+ }
+
+ SBBreakpointNameImpl(SBTarget &sb_target, const char *name);
+ bool operator==(const SBBreakpointNameImpl &rhs);
+ bool operator!=(const SBBreakpointNameImpl &rhs);
+
+ // For now we take a simple approach and only keep the name, and relook up
+ // the location when we need it.
+
+ TargetSP GetTarget() const {
+ return m_target_wp.lock();
+ }
+
+ const char *GetName() const {
+ return m_name.c_str();
+ }
+
+ bool IsValid() const {
+ return !m_name.empty() && m_target_wp.lock();
+ }
+
+ lldb_private::BreakpointName *GetBreakpointName() const;
+
+private:
+ TargetWP m_target_wp;
+ std::string m_name;
+};
+
+SBBreakpointNameImpl::SBBreakpointNameImpl(SBTarget &sb_target,
+ const char *name) {
+ if (!name || name[0] == '\0')
+ return;
+ m_name.assign(name);
+
+ if (!sb_target.IsValid())
+ return;
+
+ TargetSP target_sp = sb_target.GetSP();
+ if (!target_sp)
+ return;
+
+ m_target_wp = target_sp;
+}
+
+bool SBBreakpointNameImpl::operator==(const SBBreakpointNameImpl &rhs) {
+ return m_name == rhs.m_name && m_target_wp.lock() == rhs.m_target_wp.lock();
+}
+
+bool SBBreakpointNameImpl::operator!=(const SBBreakpointNameImpl &rhs) {
+ return m_name != rhs.m_name || m_target_wp.lock() != rhs.m_target_wp.lock();
+}
+
+lldb_private::BreakpointName *SBBreakpointNameImpl::GetBreakpointName() const {
+ if (!IsValid())
+ return nullptr;
+ TargetSP target_sp = GetTarget();
+ if (!target_sp)
+ return nullptr;
+ Status error;
+ return target_sp->FindBreakpointName(ConstString(m_name), true, error);
+}
+
+} // namespace lldb
+
+SBBreakpointName::SBBreakpointName() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBBreakpointName);
+}
+
+SBBreakpointName::SBBreakpointName(SBTarget &sb_target, const char *name) {
+ LLDB_RECORD_CONSTRUCTOR(SBBreakpointName, (lldb::SBTarget &, const char *),
+ sb_target, name);
+
+ m_impl_up.reset(new SBBreakpointNameImpl(sb_target, name));
+ // Call FindBreakpointName here to make sure the name is valid, reset if not:
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ m_impl_up.reset();
+}
+
+SBBreakpointName::SBBreakpointName(SBBreakpoint &sb_bkpt, const char *name) {
+ LLDB_RECORD_CONSTRUCTOR(SBBreakpointName,
+ (lldb::SBBreakpoint &, const char *), sb_bkpt, name);
+
+ if (!sb_bkpt.IsValid()) {
+ m_impl_up.reset();
+ return;
+ }
+ BreakpointSP bkpt_sp = sb_bkpt.GetSP();
+ Target &target = bkpt_sp->GetTarget();
+
+ m_impl_up.reset(new SBBreakpointNameImpl(target.shared_from_this(), name));
+
+ // Call FindBreakpointName here to make sure the name is valid, reset if not:
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name) {
+ m_impl_up.reset();
+ return;
+ }
+
+ // Now copy over the breakpoint's options:
+ target.ConfigureBreakpointName(*bp_name, *bkpt_sp->GetOptions(),
+ BreakpointName::Permissions());
+}
+
+SBBreakpointName::SBBreakpointName(const SBBreakpointName &rhs) {
+ LLDB_RECORD_CONSTRUCTOR(SBBreakpointName, (const lldb::SBBreakpointName &),
+ rhs);
+
+ if (!rhs.m_impl_up)
+ return;
+ else
+ m_impl_up.reset(new SBBreakpointNameImpl(rhs.m_impl_up->GetTarget(),
+ rhs.m_impl_up->GetName()));
+}
+
+SBBreakpointName::~SBBreakpointName() = default;
+
+const SBBreakpointName &SBBreakpointName::
+operator=(const SBBreakpointName &rhs) {
+ LLDB_RECORD_METHOD(
+ const lldb::SBBreakpointName &,
+ SBBreakpointName, operator=,(const lldb::SBBreakpointName &), rhs);
+
+ if (!rhs.m_impl_up) {
+ m_impl_up.reset();
+ return LLDB_RECORD_RESULT(*this);
+ }
+
+ m_impl_up.reset(new SBBreakpointNameImpl(rhs.m_impl_up->GetTarget(),
+ rhs.m_impl_up->GetName()));
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBBreakpointName::operator==(const lldb::SBBreakpointName &rhs) {
+ LLDB_RECORD_METHOD(
+ bool, SBBreakpointName, operator==,(const lldb::SBBreakpointName &), rhs);
+
+ return *m_impl_up == *rhs.m_impl_up;
+}
+
+bool SBBreakpointName::operator!=(const lldb::SBBreakpointName &rhs) {
+ LLDB_RECORD_METHOD(
+ bool, SBBreakpointName, operator!=,(const lldb::SBBreakpointName &), rhs);
+
+ return *m_impl_up != *rhs.m_impl_up;
+}
+
+bool SBBreakpointName::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBreakpointName, IsValid);
+ return this->operator bool();
+}
+SBBreakpointName::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBreakpointName, operator bool);
+
+ if (!m_impl_up)
+ return false;
+ return m_impl_up->IsValid();
+}
+
+const char *SBBreakpointName::GetName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBBreakpointName, GetName);
+
+ if (!m_impl_up)
+ return "<Invalid Breakpoint Name Object>";
+ return m_impl_up->GetName();
+}
+
+void SBBreakpointName::SetEnabled(bool enable) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetEnabled, (bool), enable);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ bp_name->GetOptions().SetEnabled(enable);
+}
+
+void SBBreakpointName::UpdateName(BreakpointName &bp_name) {
+ if (!IsValid())
+ return;
+
+ TargetSP target_sp = m_impl_up->GetTarget();
+ if (!target_sp)
+ return;
+ target_sp->ApplyNameToBreakpoints(bp_name);
+
+}
+
+bool SBBreakpointName::IsEnabled() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpointName, IsEnabled);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ return bp_name->GetOptions().IsEnabled();
+}
+
+void SBBreakpointName::SetOneShot(bool one_shot) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetOneShot, (bool), one_shot);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ bp_name->GetOptions().SetOneShot(one_shot);
+ UpdateName(*bp_name);
+}
+
+bool SBBreakpointName::IsOneShot() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBreakpointName, IsOneShot);
+
+ const BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ return bp_name->GetOptions().IsOneShot();
+}
+
+void SBBreakpointName::SetIgnoreCount(uint32_t count) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetIgnoreCount, (uint32_t), count);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ bp_name->GetOptions().SetIgnoreCount(count);
+ UpdateName(*bp_name);
+}
+
+uint32_t SBBreakpointName::GetIgnoreCount() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBBreakpointName, GetIgnoreCount);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ return bp_name->GetOptions().GetIgnoreCount();
+}
+
+void SBBreakpointName::SetCondition(const char *condition) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetCondition, (const char *),
+ condition);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ bp_name->GetOptions().SetCondition(condition);
+ UpdateName(*bp_name);
+}
+
+const char *SBBreakpointName::GetCondition() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBBreakpointName, GetCondition);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return nullptr;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ return bp_name->GetOptions().GetConditionText();
+}
+
+void SBBreakpointName::SetAutoContinue(bool auto_continue) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetAutoContinue, (bool),
+ auto_continue);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ bp_name->GetOptions().SetAutoContinue(auto_continue);
+ UpdateName(*bp_name);
+}
+
+bool SBBreakpointName::GetAutoContinue() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpointName, GetAutoContinue);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ return bp_name->GetOptions().IsAutoContinue();
+}
+
+void SBBreakpointName::SetThreadID(tid_t tid) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetThreadID, (lldb::tid_t), tid);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ bp_name->GetOptions().SetThreadID(tid);
+ UpdateName(*bp_name);
+}
+
+tid_t SBBreakpointName::GetThreadID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::tid_t, SBBreakpointName, GetThreadID);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return LLDB_INVALID_THREAD_ID;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ return bp_name->GetOptions().GetThreadSpec()->GetTID();
+}
+
+void SBBreakpointName::SetThreadIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetThreadIndex, (uint32_t), index);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ bp_name->GetOptions().GetThreadSpec()->SetIndex(index);
+ UpdateName(*bp_name);
+}
+
+uint32_t SBBreakpointName::GetThreadIndex() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBBreakpointName, GetThreadIndex);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return LLDB_INVALID_THREAD_ID;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ return bp_name->GetOptions().GetThreadSpec()->GetIndex();
+}
+
+void SBBreakpointName::SetThreadName(const char *thread_name) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetThreadName, (const char *),
+ thread_name);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ bp_name->GetOptions().GetThreadSpec()->SetName(thread_name);
+ UpdateName(*bp_name);
+}
+
+const char *SBBreakpointName::GetThreadName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBBreakpointName,
+ GetThreadName);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return nullptr;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ return bp_name->GetOptions().GetThreadSpec()->GetName();
+}
+
+void SBBreakpointName::SetQueueName(const char *queue_name) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetQueueName, (const char *),
+ queue_name);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ bp_name->GetOptions().GetThreadSpec()->SetQueueName(queue_name);
+ UpdateName(*bp_name);
+}
+
+const char *SBBreakpointName::GetQueueName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBBreakpointName,
+ GetQueueName);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return nullptr;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ return bp_name->GetOptions().GetThreadSpec()->GetQueueName();
+}
+
+void SBBreakpointName::SetCommandLineCommands(SBStringList &commands) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetCommandLineCommands,
+ (lldb::SBStringList &), commands);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+ if (commands.GetSize() == 0)
+ return;
+
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+ std::unique_ptr<BreakpointOptions::CommandData> cmd_data_up(
+ new BreakpointOptions::CommandData(*commands, eScriptLanguageNone));
+
+ bp_name->GetOptions().SetCommandDataCallback(cmd_data_up);
+ UpdateName(*bp_name);
+}
+
+bool SBBreakpointName::GetCommandLineCommands(SBStringList &commands) {
+ LLDB_RECORD_METHOD(bool, SBBreakpointName, GetCommandLineCommands,
+ (lldb::SBStringList &), commands);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return false;
+
+ StringList command_list;
+ bool has_commands =
+ bp_name->GetOptions().GetCommandLineCallbacks(command_list);
+ if (has_commands)
+ commands.AppendList(command_list);
+ return has_commands;
+}
+
+const char *SBBreakpointName::GetHelpString() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBBreakpointName,
+ GetHelpString);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return "";
+
+ return bp_name->GetHelp();
+}
+
+void SBBreakpointName::SetHelpString(const char *help_string) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetHelpString, (const char *),
+ help_string);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+ bp_name->SetHelp(help_string);
+}
+
+bool SBBreakpointName::GetDescription(SBStream &s) {
+ LLDB_RECORD_METHOD(bool, SBBreakpointName, GetDescription, (lldb::SBStream &),
+ s);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ {
+ s.Printf("No value");
+ return false;
+ }
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+ bp_name->GetDescription(s.get(), eDescriptionLevelFull);
+ return true;
+}
+
+void SBBreakpointName::SetCallback(SBBreakpointHitCallback callback,
+ void *baton) {
+ LLDB_RECORD_DUMMY(void, SBBreakpointName, SetCallback,
+ (lldb::SBBreakpointHitCallback, void *), callback, baton);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ BatonSP baton_sp(new SBBreakpointCallbackBaton(callback, baton));
+ bp_name->GetOptions().SetCallback(SBBreakpointCallbackBaton
+ ::PrivateBreakpointHitCallback,
+ baton_sp,
+ false);
+ UpdateName(*bp_name);
+}
+
+void SBBreakpointName::SetScriptCallbackFunction(
+ const char *callback_function_name) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetScriptCallbackFunction,
+ (const char *), callback_function_name);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ BreakpointOptions &bp_options = bp_name->GetOptions();
+ m_impl_up->GetTarget()
+ ->GetDebugger()
+ .GetScriptInterpreter()
+ ->SetBreakpointCommandCallbackFunction(&bp_options,
+ callback_function_name);
+ UpdateName(*bp_name);
+}
+
+SBError
+SBBreakpointName::SetScriptCallbackBody(const char *callback_body_text) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBBreakpointName, SetScriptCallbackBody,
+ (const char *), callback_body_text);
+
+ SBError sb_error;
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return LLDB_RECORD_RESULT(sb_error);
+
+ std::lock_guard<std::recursive_mutex> guard(
+ m_impl_up->GetTarget()->GetAPIMutex());
+
+ BreakpointOptions &bp_options = bp_name->GetOptions();
+ Status error =
+ m_impl_up->GetTarget()
+ ->GetDebugger()
+ .GetScriptInterpreter()
+ ->SetBreakpointCommandCallback(&bp_options, callback_body_text);
+ sb_error.SetError(error);
+ if (!sb_error.Fail())
+ UpdateName(*bp_name);
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+bool SBBreakpointName::GetAllowList() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBreakpointName, GetAllowList);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return false;
+ return bp_name->GetPermissions().GetAllowList();
+}
+
+void SBBreakpointName::SetAllowList(bool value) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetAllowList, (bool), value);
+
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+ bp_name->GetPermissions().SetAllowList(value);
+}
+
+bool SBBreakpointName::GetAllowDelete() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpointName, GetAllowDelete);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return false;
+ return bp_name->GetPermissions().GetAllowDelete();
+}
+
+void SBBreakpointName::SetAllowDelete(bool value) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetAllowDelete, (bool), value);
+
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+ bp_name->GetPermissions().SetAllowDelete(value);
+}
+
+bool SBBreakpointName::GetAllowDisable() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBBreakpointName, GetAllowDisable);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return false;
+ return bp_name->GetPermissions().GetAllowDisable();
+}
+
+void SBBreakpointName::SetAllowDisable(bool value) {
+ LLDB_RECORD_METHOD(void, SBBreakpointName, SetAllowDisable, (bool), value);
+
+ BreakpointName *bp_name = GetBreakpointName();
+ if (!bp_name)
+ return;
+ bp_name->GetPermissions().SetAllowDisable(value);
+}
+
+lldb_private::BreakpointName *SBBreakpointName::GetBreakpointName() const
+{
+ if (!IsValid())
+ return nullptr;
+ return m_impl_up->GetBreakpointName();
+}
+
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBBreakpointName>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBBreakpointName, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBBreakpointName,
+ (lldb::SBTarget &, const char *));
+ LLDB_REGISTER_CONSTRUCTOR(SBBreakpointName,
+ (lldb::SBBreakpoint &, const char *));
+ LLDB_REGISTER_CONSTRUCTOR(SBBreakpointName,
+ (const lldb::SBBreakpointName &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBBreakpointName &,
+ SBBreakpointName, operator=,(const lldb::SBBreakpointName &));
+ LLDB_REGISTER_METHOD(
+ bool, SBBreakpointName, operator==,(const lldb::SBBreakpointName &));
+ LLDB_REGISTER_METHOD(
+ bool, SBBreakpointName, operator!=,(const lldb::SBBreakpointName &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBBreakpointName, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBBreakpointName, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBBreakpointName, GetName, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetEnabled, (bool));
+ LLDB_REGISTER_METHOD(bool, SBBreakpointName, IsEnabled, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetOneShot, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBBreakpointName, IsOneShot, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetIgnoreCount, (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBBreakpointName, GetIgnoreCount, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetCondition, (const char *));
+ LLDB_REGISTER_METHOD(const char *, SBBreakpointName, GetCondition, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetAutoContinue, (bool));
+ LLDB_REGISTER_METHOD(bool, SBBreakpointName, GetAutoContinue, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetThreadID, (lldb::tid_t));
+ LLDB_REGISTER_METHOD(lldb::tid_t, SBBreakpointName, GetThreadID, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetThreadIndex, (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBBreakpointName, GetThreadIndex, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetThreadName, (const char *));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBBreakpointName, GetThreadName,
+ ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetQueueName, (const char *));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBBreakpointName, GetQueueName,
+ ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetCommandLineCommands,
+ (lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(bool, SBBreakpointName, GetCommandLineCommands,
+ (lldb::SBStringList &));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBBreakpointName, GetHelpString,
+ ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetHelpString, (const char *));
+ LLDB_REGISTER_METHOD(bool, SBBreakpointName, GetDescription,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetScriptCallbackFunction,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpointName, SetScriptCallbackBody,
+ (const char *));
+ LLDB_REGISTER_METHOD_CONST(bool, SBBreakpointName, GetAllowList, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetAllowList, (bool));
+ LLDB_REGISTER_METHOD(bool, SBBreakpointName, GetAllowDelete, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetAllowDelete, (bool));
+ LLDB_REGISTER_METHOD(bool, SBBreakpointName, GetAllowDisable, ());
+ LLDB_REGISTER_METHOD(void, SBBreakpointName, SetAllowDisable, (bool));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBBreakpointOptionCommon.cpp b/contrib/llvm-project/lldb/source/API/SBBreakpointOptionCommon.cpp
new file mode 100644
index 000000000000..058b3e0398cd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBBreakpointOptionCommon.cpp
@@ -0,0 +1,80 @@
+//===-- SBBreakpointOptionCommon.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBBreakpointName.h"
+#include "lldb/API/SBBreakpointLocation.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+#include "lldb/API/SBThread.h"
+
+#include "lldb/Breakpoint/BreakpointName.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+#include "lldb/lldb-enumerations.h"
+
+#include "SBBreakpointOptionCommon.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBBreakpointCallbackBaton::SBBreakpointCallbackBaton(SBBreakpointHitCallback
+ callback,
+ void *baton)
+ : TypedBaton(llvm::make_unique<CallbackData>()) {
+ getItem()->callback = callback;
+ getItem()->callback_baton = baton;
+ }
+
+ bool SBBreakpointCallbackBaton::PrivateBreakpointHitCallback(void *baton,
+ StoppointCallbackContext *ctx,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id)
+{
+ ExecutionContext exe_ctx(ctx->exe_ctx_ref);
+ BreakpointSP bp_sp(
+ exe_ctx.GetTargetRef().GetBreakpointList().FindBreakpointByID(break_id));
+ if (baton && bp_sp) {
+ CallbackData *data = (CallbackData *)baton;
+ lldb_private::Breakpoint *bp = bp_sp.get();
+ if (bp && data->callback) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ SBProcess sb_process(process->shared_from_this());
+ SBThread sb_thread;
+ SBBreakpointLocation sb_location;
+ assert(bp_sp);
+ sb_location.SetLocation(bp_sp->FindLocationByID(break_loc_id));
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (thread)
+ sb_thread.SetThread(thread->shared_from_this());
+
+ return data->callback(data->callback_baton, sb_process, sb_thread,
+ sb_location);
+ }
+ }
+ }
+ return true; // Return true if we should stop at this breakpoint
+}
+
+SBBreakpointCallbackBaton::~SBBreakpointCallbackBaton() = default;
diff --git a/contrib/llvm-project/lldb/source/API/SBBreakpointOptionCommon.h b/contrib/llvm-project/lldb/source/API/SBBreakpointOptionCommon.h
new file mode 100644
index 000000000000..52049e4e7588
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBBreakpointOptionCommon.h
@@ -0,0 +1,36 @@
+//===-- SBBreakpointOptionCommon.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SBBreakpointOptionCommons_h_
+#define LLDB_SBBreakpointOptionCommons_h_
+
+#include "lldb/API/SBDefines.h"
+#include "lldb/Utility/Baton.h"
+
+namespace lldb
+{
+struct CallbackData {
+ SBBreakpointHitCallback callback;
+ void *callback_baton;
+};
+
+class SBBreakpointCallbackBaton : public lldb_private::TypedBaton<CallbackData> {
+public:
+ SBBreakpointCallbackBaton(SBBreakpointHitCallback callback,
+ void *baton);
+
+ ~SBBreakpointCallbackBaton() override;
+
+ static bool PrivateBreakpointHitCallback(void *baton,
+ lldb_private::StoppointCallbackContext *ctx,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+};
+
+} // namespace lldb
+#endif // LLDB_SBBreakpointOptionCommons_h_
diff --git a/contrib/llvm-project/lldb/source/API/SBBroadcaster.cpp b/contrib/llvm-project/lldb/source/API/SBBroadcaster.cpp
new file mode 100644
index 000000000000..e1efdf7baf61
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBBroadcaster.cpp
@@ -0,0 +1,213 @@
+//===-- SBBroadcaster.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SBReproducerPrivate.h"
+#include "lldb/Utility/Broadcaster.h"
+
+#include "lldb/API/SBBroadcaster.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBListener.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBBroadcaster::SBBroadcaster() : m_opaque_sp(), m_opaque_ptr(nullptr) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBBroadcaster);
+}
+
+SBBroadcaster::SBBroadcaster(const char *name)
+ : m_opaque_sp(new Broadcaster(nullptr, name)), m_opaque_ptr(nullptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBBroadcaster, (const char *), name);
+
+ m_opaque_ptr = m_opaque_sp.get();
+}
+
+SBBroadcaster::SBBroadcaster(lldb_private::Broadcaster *broadcaster, bool owns)
+ : m_opaque_sp(owns ? broadcaster : nullptr), m_opaque_ptr(broadcaster) {}
+
+SBBroadcaster::SBBroadcaster(const SBBroadcaster &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp), m_opaque_ptr(rhs.m_opaque_ptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBBroadcaster, (const lldb::SBBroadcaster &), rhs);
+}
+
+const SBBroadcaster &SBBroadcaster::operator=(const SBBroadcaster &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBBroadcaster &,
+ SBBroadcaster, operator=,(const lldb::SBBroadcaster &),
+ rhs);
+
+ if (this != &rhs) {
+ m_opaque_sp = rhs.m_opaque_sp;
+ m_opaque_ptr = rhs.m_opaque_ptr;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBBroadcaster::~SBBroadcaster() { reset(nullptr, false); }
+
+void SBBroadcaster::BroadcastEventByType(uint32_t event_type, bool unique) {
+ LLDB_RECORD_METHOD(void, SBBroadcaster, BroadcastEventByType,
+ (uint32_t, bool), event_type, unique);
+
+ if (m_opaque_ptr == nullptr)
+ return;
+
+ if (unique)
+ m_opaque_ptr->BroadcastEventIfUnique(event_type);
+ else
+ m_opaque_ptr->BroadcastEvent(event_type);
+}
+
+void SBBroadcaster::BroadcastEvent(const SBEvent &event, bool unique) {
+ LLDB_RECORD_METHOD(void, SBBroadcaster, BroadcastEvent,
+ (const lldb::SBEvent &, bool), event, unique);
+
+ if (m_opaque_ptr == nullptr)
+ return;
+
+ EventSP event_sp = event.GetSP();
+ if (unique)
+ m_opaque_ptr->BroadcastEventIfUnique(event_sp);
+ else
+ m_opaque_ptr->BroadcastEvent(event_sp);
+}
+
+void SBBroadcaster::AddInitialEventsToListener(const SBListener &listener,
+ uint32_t requested_events) {
+ LLDB_RECORD_METHOD(void, SBBroadcaster, AddInitialEventsToListener,
+ (const lldb::SBListener &, uint32_t), listener,
+ requested_events);
+
+ if (m_opaque_ptr)
+ m_opaque_ptr->AddInitialEventsToListener(listener.m_opaque_sp,
+ requested_events);
+}
+
+uint32_t SBBroadcaster::AddListener(const SBListener &listener,
+ uint32_t event_mask) {
+ LLDB_RECORD_METHOD(uint32_t, SBBroadcaster, AddListener,
+ (const lldb::SBListener &, uint32_t), listener,
+ event_mask);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->AddListener(listener.m_opaque_sp, event_mask);
+ return 0;
+}
+
+const char *SBBroadcaster::GetName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBBroadcaster, GetName);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->GetBroadcasterName().GetCString();
+ return nullptr;
+}
+
+bool SBBroadcaster::EventTypeHasListeners(uint32_t event_type) {
+ LLDB_RECORD_METHOD(bool, SBBroadcaster, EventTypeHasListeners, (uint32_t),
+ event_type);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->EventTypeHasListeners(event_type);
+ return false;
+}
+
+bool SBBroadcaster::RemoveListener(const SBListener &listener,
+ uint32_t event_mask) {
+ LLDB_RECORD_METHOD(bool, SBBroadcaster, RemoveListener,
+ (const lldb::SBListener &, uint32_t), listener,
+ event_mask);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->RemoveListener(listener.m_opaque_sp, event_mask);
+ return false;
+}
+
+Broadcaster *SBBroadcaster::get() const { return m_opaque_ptr; }
+
+void SBBroadcaster::reset(Broadcaster *broadcaster, bool owns) {
+ if (owns)
+ m_opaque_sp.reset(broadcaster);
+ else
+ m_opaque_sp.reset();
+ m_opaque_ptr = broadcaster;
+}
+
+bool SBBroadcaster::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBroadcaster, IsValid);
+ return this->operator bool();
+}
+SBBroadcaster::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBBroadcaster, operator bool);
+
+ return m_opaque_ptr != nullptr;
+}
+
+void SBBroadcaster::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBBroadcaster, Clear);
+
+ m_opaque_sp.reset();
+ m_opaque_ptr = nullptr;
+}
+
+bool SBBroadcaster::operator==(const SBBroadcaster &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBBroadcaster, operator==,(const lldb::SBBroadcaster &), rhs);
+
+ return m_opaque_ptr == rhs.m_opaque_ptr;
+}
+
+bool SBBroadcaster::operator!=(const SBBroadcaster &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBBroadcaster, operator!=,(const lldb::SBBroadcaster &), rhs);
+
+ return m_opaque_ptr != rhs.m_opaque_ptr;
+}
+
+bool SBBroadcaster::operator<(const SBBroadcaster &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBBroadcaster, operator<,(const lldb::SBBroadcaster &), rhs);
+
+ return m_opaque_ptr < rhs.m_opaque_ptr;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBBroadcaster>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBBroadcaster, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBBroadcaster, (const char *));
+ LLDB_REGISTER_CONSTRUCTOR(SBBroadcaster, (const lldb::SBBroadcaster &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBBroadcaster &,
+ SBBroadcaster, operator=,(const lldb::SBBroadcaster &));
+ LLDB_REGISTER_METHOD(void, SBBroadcaster, BroadcastEventByType,
+ (uint32_t, bool));
+ LLDB_REGISTER_METHOD(void, SBBroadcaster, BroadcastEvent,
+ (const lldb::SBEvent &, bool));
+ LLDB_REGISTER_METHOD(void, SBBroadcaster, AddInitialEventsToListener,
+ (const lldb::SBListener &, uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBBroadcaster, AddListener,
+ (const lldb::SBListener &, uint32_t));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBBroadcaster, GetName, ());
+ LLDB_REGISTER_METHOD(bool, SBBroadcaster, EventTypeHasListeners,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBBroadcaster, RemoveListener,
+ (const lldb::SBListener &, uint32_t));
+ LLDB_REGISTER_METHOD_CONST(bool, SBBroadcaster, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBBroadcaster, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBBroadcaster, Clear, ());
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBBroadcaster, operator==,(const lldb::SBBroadcaster &));
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBBroadcaster, operator!=,(const lldb::SBBroadcaster &));
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBBroadcaster, operator<,(const lldb::SBBroadcaster &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBCommandInterpreter.cpp b/contrib/llvm-project/lldb/source/API/SBCommandInterpreter.cpp
new file mode 100644
index 000000000000..c07dffce0baa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBCommandInterpreter.cpp
@@ -0,0 +1,943 @@
+//===-- SBCommandInterpreter.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-types.h"
+
+#include "SBReproducerPrivate.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Listener.h"
+
+#include "lldb/API/SBBroadcaster.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBExecutionContext.h"
+#include "lldb/API/SBListener.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+#include "lldb/API/SBTarget.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBCommandInterpreterRunOptions::SBCommandInterpreterRunOptions() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBCommandInterpreterRunOptions);
+
+ m_opaque_up.reset(new CommandInterpreterRunOptions());
+}
+
+SBCommandInterpreterRunOptions::~SBCommandInterpreterRunOptions() = default;
+
+bool SBCommandInterpreterRunOptions::GetStopOnContinue() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreterRunOptions,
+ GetStopOnContinue);
+
+ return m_opaque_up->GetStopOnContinue();
+}
+
+void SBCommandInterpreterRunOptions::SetStopOnContinue(bool stop_on_continue) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreterRunOptions, SetStopOnContinue,
+ (bool), stop_on_continue);
+
+ m_opaque_up->SetStopOnContinue(stop_on_continue);
+}
+
+bool SBCommandInterpreterRunOptions::GetStopOnError() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreterRunOptions,
+ GetStopOnError);
+
+ return m_opaque_up->GetStopOnError();
+}
+
+void SBCommandInterpreterRunOptions::SetStopOnError(bool stop_on_error) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreterRunOptions, SetStopOnError,
+ (bool), stop_on_error);
+
+ m_opaque_up->SetStopOnError(stop_on_error);
+}
+
+bool SBCommandInterpreterRunOptions::GetStopOnCrash() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreterRunOptions,
+ GetStopOnCrash);
+
+ return m_opaque_up->GetStopOnCrash();
+}
+
+void SBCommandInterpreterRunOptions::SetStopOnCrash(bool stop_on_crash) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreterRunOptions, SetStopOnCrash,
+ (bool), stop_on_crash);
+
+ m_opaque_up->SetStopOnCrash(stop_on_crash);
+}
+
+bool SBCommandInterpreterRunOptions::GetEchoCommands() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreterRunOptions,
+ GetEchoCommands);
+
+ return m_opaque_up->GetEchoCommands();
+}
+
+void SBCommandInterpreterRunOptions::SetEchoCommands(bool echo_commands) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreterRunOptions, SetEchoCommands,
+ (bool), echo_commands);
+
+ m_opaque_up->SetEchoCommands(echo_commands);
+}
+
+bool SBCommandInterpreterRunOptions::GetEchoCommentCommands() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreterRunOptions,
+ GetEchoCommentCommands);
+
+ return m_opaque_up->GetEchoCommentCommands();
+}
+
+void SBCommandInterpreterRunOptions::SetEchoCommentCommands(bool echo) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreterRunOptions,
+ SetEchoCommentCommands, (bool), echo);
+
+ m_opaque_up->SetEchoCommentCommands(echo);
+}
+
+bool SBCommandInterpreterRunOptions::GetPrintResults() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreterRunOptions,
+ GetPrintResults);
+
+ return m_opaque_up->GetPrintResults();
+}
+
+void SBCommandInterpreterRunOptions::SetPrintResults(bool print_results) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreterRunOptions, SetPrintResults,
+ (bool), print_results);
+
+ m_opaque_up->SetPrintResults(print_results);
+}
+
+bool SBCommandInterpreterRunOptions::GetAddToHistory() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreterRunOptions,
+ GetAddToHistory);
+
+ return m_opaque_up->GetAddToHistory();
+}
+
+void SBCommandInterpreterRunOptions::SetAddToHistory(bool add_to_history) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreterRunOptions, SetAddToHistory,
+ (bool), add_to_history);
+
+ m_opaque_up->SetAddToHistory(add_to_history);
+}
+
+lldb_private::CommandInterpreterRunOptions *
+SBCommandInterpreterRunOptions::get() const {
+ return m_opaque_up.get();
+}
+
+lldb_private::CommandInterpreterRunOptions &
+SBCommandInterpreterRunOptions::ref() const {
+ return *m_opaque_up;
+}
+
+class CommandPluginInterfaceImplementation : public CommandObjectParsed {
+public:
+ CommandPluginInterfaceImplementation(CommandInterpreter &interpreter,
+ const char *name,
+ lldb::SBCommandPluginInterface *backend,
+ const char *help = nullptr,
+ const char *syntax = nullptr,
+ uint32_t flags = 0)
+ : CommandObjectParsed(interpreter, name, help, syntax, flags),
+ m_backend(backend) {}
+
+ bool IsRemovable() const override { return true; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ SBCommandReturnObject sb_return(&result);
+ SBCommandInterpreter sb_interpreter(&m_interpreter);
+ SBDebugger debugger_sb(m_interpreter.GetDebugger().shared_from_this());
+ bool ret = m_backend->DoExecute(
+ debugger_sb, (char **)command.GetArgumentVector(), sb_return);
+ sb_return.Release();
+ return ret;
+ }
+ std::shared_ptr<lldb::SBCommandPluginInterface> m_backend;
+};
+
+SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter)
+ : m_opaque_ptr(interpreter) {
+ LLDB_RECORD_CONSTRUCTOR(SBCommandInterpreter,
+ (lldb_private::CommandInterpreter *), interpreter);
+
+}
+
+SBCommandInterpreter::SBCommandInterpreter(const SBCommandInterpreter &rhs)
+ : m_opaque_ptr(rhs.m_opaque_ptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBCommandInterpreter,
+ (const lldb::SBCommandInterpreter &), rhs);
+}
+
+SBCommandInterpreter::~SBCommandInterpreter() = default;
+
+const SBCommandInterpreter &SBCommandInterpreter::
+operator=(const SBCommandInterpreter &rhs) {
+ LLDB_RECORD_METHOD(
+ const lldb::SBCommandInterpreter &,
+ SBCommandInterpreter, operator=,(const lldb::SBCommandInterpreter &),
+ rhs);
+
+ m_opaque_ptr = rhs.m_opaque_ptr;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBCommandInterpreter::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreter, IsValid);
+ return this->operator bool();
+}
+SBCommandInterpreter::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreter, operator bool);
+
+ return m_opaque_ptr != nullptr;
+}
+
+bool SBCommandInterpreter::CommandExists(const char *cmd) {
+ LLDB_RECORD_METHOD(bool, SBCommandInterpreter, CommandExists, (const char *),
+ cmd);
+
+ return (((cmd != nullptr) && IsValid()) ? m_opaque_ptr->CommandExists(cmd)
+ : false);
+}
+
+bool SBCommandInterpreter::AliasExists(const char *cmd) {
+ LLDB_RECORD_METHOD(bool, SBCommandInterpreter, AliasExists, (const char *),
+ cmd);
+
+ return (((cmd != nullptr) && IsValid()) ? m_opaque_ptr->AliasExists(cmd)
+ : false);
+}
+
+bool SBCommandInterpreter::IsActive() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommandInterpreter, IsActive);
+
+ return (IsValid() ? m_opaque_ptr->IsActive() : false);
+}
+
+bool SBCommandInterpreter::WasInterrupted() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreter, WasInterrupted);
+
+ return (IsValid() ? m_opaque_ptr->WasInterrupted() : false);
+}
+
+const char *SBCommandInterpreter::GetIOHandlerControlSequence(char ch) {
+ LLDB_RECORD_METHOD(const char *, SBCommandInterpreter,
+ GetIOHandlerControlSequence, (char), ch);
+
+ return (IsValid()
+ ? m_opaque_ptr->GetDebugger()
+ .GetTopIOHandlerControlSequence(ch)
+ .GetCString()
+ : nullptr);
+}
+
+lldb::ReturnStatus
+SBCommandInterpreter::HandleCommand(const char *command_line,
+ SBCommandReturnObject &result,
+ bool add_to_history) {
+ LLDB_RECORD_METHOD(lldb::ReturnStatus, SBCommandInterpreter, HandleCommand,
+ (const char *, lldb::SBCommandReturnObject &, bool),
+ command_line, result, add_to_history);
+
+ SBExecutionContext sb_exe_ctx;
+ return HandleCommand(command_line, sb_exe_ctx, result, add_to_history);
+}
+
+lldb::ReturnStatus SBCommandInterpreter::HandleCommand(
+ const char *command_line, SBExecutionContext &override_context,
+ SBCommandReturnObject &result, bool add_to_history) {
+ LLDB_RECORD_METHOD(lldb::ReturnStatus, SBCommandInterpreter, HandleCommand,
+ (const char *, lldb::SBExecutionContext &,
+ lldb::SBCommandReturnObject &, bool),
+ command_line, override_context, result, add_to_history);
+
+
+ ExecutionContext ctx, *ctx_ptr;
+ if (override_context.get()) {
+ ctx = override_context.get()->Lock(true);
+ ctx_ptr = &ctx;
+ } else
+ ctx_ptr = nullptr;
+
+ result.Clear();
+ if (command_line && IsValid()) {
+ result.ref().SetInteractive(false);
+ m_opaque_ptr->HandleCommand(command_line,
+ add_to_history ? eLazyBoolYes : eLazyBoolNo,
+ result.ref(), ctx_ptr);
+ } else {
+ result->AppendError(
+ "SBCommandInterpreter or the command line is not valid");
+ result->SetStatus(eReturnStatusFailed);
+ }
+
+
+ return result.GetStatus();
+}
+
+void SBCommandInterpreter::HandleCommandsFromFile(
+ lldb::SBFileSpec &file, lldb::SBExecutionContext &override_context,
+ lldb::SBCommandInterpreterRunOptions &options,
+ lldb::SBCommandReturnObject result) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreter, HandleCommandsFromFile,
+ (lldb::SBFileSpec &, lldb::SBExecutionContext &,
+ lldb::SBCommandInterpreterRunOptions &,
+ lldb::SBCommandReturnObject),
+ file, override_context, options, result);
+
+ if (!IsValid()) {
+ result->AppendError("SBCommandInterpreter is not valid.");
+ result->SetStatus(eReturnStatusFailed);
+ return;
+ }
+
+ if (!file.IsValid()) {
+ SBStream s;
+ file.GetDescription(s);
+ result->AppendErrorWithFormat("File is not valid: %s.", s.GetData());
+ result->SetStatus(eReturnStatusFailed);
+ }
+
+ FileSpec tmp_spec = file.ref();
+ ExecutionContext ctx, *ctx_ptr;
+ if (override_context.get()) {
+ ctx = override_context.get()->Lock(true);
+ ctx_ptr = &ctx;
+ } else
+ ctx_ptr = nullptr;
+
+ m_opaque_ptr->HandleCommandsFromFile(tmp_spec, ctx_ptr, options.ref(),
+ result.ref());
+}
+
+int SBCommandInterpreter::HandleCompletion(
+ const char *current_line, const char *cursor, const char *last_char,
+ int match_start_point, int max_return_elements, SBStringList &matches) {
+ LLDB_RECORD_METHOD(int, SBCommandInterpreter, HandleCompletion,
+ (const char *, const char *, const char *, int, int,
+ lldb::SBStringList &),
+ current_line, cursor, last_char, match_start_point,
+ max_return_elements, matches);
+
+ SBStringList dummy_descriptions;
+ return HandleCompletionWithDescriptions(
+ current_line, cursor, last_char, match_start_point, max_return_elements,
+ matches, dummy_descriptions);
+}
+
+int SBCommandInterpreter::HandleCompletionWithDescriptions(
+ const char *current_line, const char *cursor, const char *last_char,
+ int match_start_point, int max_return_elements, SBStringList &matches,
+ SBStringList &descriptions) {
+ LLDB_RECORD_METHOD(int, SBCommandInterpreter,
+ HandleCompletionWithDescriptions,
+ (const char *, const char *, const char *, int, int,
+ lldb::SBStringList &, lldb::SBStringList &),
+ current_line, cursor, last_char, match_start_point,
+ max_return_elements, matches, descriptions);
+
+ int num_completions = 0;
+
+ // Sanity check the arguments that are passed in: cursor & last_char have to
+ // be within the current_line.
+ if (current_line == nullptr || cursor == nullptr || last_char == nullptr)
+ return 0;
+
+ if (cursor < current_line || last_char < current_line)
+ return 0;
+
+ size_t current_line_size = strlen(current_line);
+ if (cursor - current_line > static_cast<ptrdiff_t>(current_line_size) ||
+ last_char - current_line > static_cast<ptrdiff_t>(current_line_size))
+ return 0;
+
+
+ if (IsValid()) {
+ lldb_private::StringList lldb_matches, lldb_descriptions;
+ num_completions = m_opaque_ptr->HandleCompletion(
+ current_line, cursor, last_char, match_start_point, max_return_elements,
+ lldb_matches, lldb_descriptions);
+
+ SBStringList temp_matches_list(&lldb_matches);
+ matches.AppendList(temp_matches_list);
+ SBStringList temp_descriptions_list(&lldb_descriptions);
+ descriptions.AppendList(temp_descriptions_list);
+ }
+
+ return num_completions;
+}
+
+int SBCommandInterpreter::HandleCompletionWithDescriptions(
+ const char *current_line, uint32_t cursor_pos, int match_start_point,
+ int max_return_elements, SBStringList &matches,
+ SBStringList &descriptions) {
+ LLDB_RECORD_METHOD(int, SBCommandInterpreter,
+ HandleCompletionWithDescriptions,
+ (const char *, uint32_t, int, int, lldb::SBStringList &,
+ lldb::SBStringList &),
+ current_line, cursor_pos, match_start_point,
+ max_return_elements, matches, descriptions);
+
+ const char *cursor = current_line + cursor_pos;
+ const char *last_char = current_line + strlen(current_line);
+ return HandleCompletionWithDescriptions(
+ current_line, cursor, last_char, match_start_point, max_return_elements,
+ matches, descriptions);
+}
+
+int SBCommandInterpreter::HandleCompletion(const char *current_line,
+ uint32_t cursor_pos,
+ int match_start_point,
+ int max_return_elements,
+ lldb::SBStringList &matches) {
+ LLDB_RECORD_METHOD(int, SBCommandInterpreter, HandleCompletion,
+ (const char *, uint32_t, int, int, lldb::SBStringList &),
+ current_line, cursor_pos, match_start_point,
+ max_return_elements, matches);
+
+ const char *cursor = current_line + cursor_pos;
+ const char *last_char = current_line + strlen(current_line);
+ return HandleCompletion(current_line, cursor, last_char, match_start_point,
+ max_return_elements, matches);
+}
+
+bool SBCommandInterpreter::HasCommands() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommandInterpreter, HasCommands);
+
+ return (IsValid() ? m_opaque_ptr->HasCommands() : false);
+}
+
+bool SBCommandInterpreter::HasAliases() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommandInterpreter, HasAliases);
+
+ return (IsValid() ? m_opaque_ptr->HasAliases() : false);
+}
+
+bool SBCommandInterpreter::HasAliasOptions() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommandInterpreter, HasAliasOptions);
+
+ return (IsValid() ? m_opaque_ptr->HasAliasOptions() : false);
+}
+
+SBProcess SBCommandInterpreter::GetProcess() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBProcess, SBCommandInterpreter, GetProcess);
+
+ SBProcess sb_process;
+ ProcessSP process_sp;
+ if (IsValid()) {
+ TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ process_sp = target_sp->GetProcessSP();
+ sb_process.SetSP(process_sp);
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+SBDebugger SBCommandInterpreter::GetDebugger() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBDebugger, SBCommandInterpreter,
+ GetDebugger);
+
+ SBDebugger sb_debugger;
+ if (IsValid())
+ sb_debugger.reset(m_opaque_ptr->GetDebugger().shared_from_this());
+
+ return LLDB_RECORD_RESULT(sb_debugger);
+}
+
+bool SBCommandInterpreter::GetPromptOnQuit() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommandInterpreter, GetPromptOnQuit);
+
+ return (IsValid() ? m_opaque_ptr->GetPromptOnQuit() : false);
+}
+
+void SBCommandInterpreter::SetPromptOnQuit(bool b) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreter, SetPromptOnQuit, (bool), b);
+
+ if (IsValid())
+ m_opaque_ptr->SetPromptOnQuit(b);
+}
+
+void SBCommandInterpreter::AllowExitCodeOnQuit(bool allow) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreter, AllowExitCodeOnQuit, (bool),
+ allow);
+
+ if (m_opaque_ptr)
+ m_opaque_ptr->AllowExitCodeOnQuit(allow);
+}
+
+bool SBCommandInterpreter::HasCustomQuitExitCode() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommandInterpreter, HasCustomQuitExitCode);
+
+ bool exited = false;
+ if (m_opaque_ptr)
+ m_opaque_ptr->GetQuitExitCode(exited);
+ return exited;
+}
+
+int SBCommandInterpreter::GetQuitStatus() {
+ LLDB_RECORD_METHOD_NO_ARGS(int, SBCommandInterpreter, GetQuitStatus);
+
+ bool exited = false;
+ return (m_opaque_ptr ? m_opaque_ptr->GetQuitExitCode(exited) : 0);
+}
+
+void SBCommandInterpreter::ResolveCommand(const char *command_line,
+ SBCommandReturnObject &result) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreter, ResolveCommand,
+ (const char *, lldb::SBCommandReturnObject &),
+ command_line, result);
+
+ result.Clear();
+ if (command_line && IsValid()) {
+ m_opaque_ptr->ResolveCommand(command_line, result.ref());
+ } else {
+ result->AppendError(
+ "SBCommandInterpreter or the command line is not valid");
+ result->SetStatus(eReturnStatusFailed);
+ }
+}
+
+CommandInterpreter *SBCommandInterpreter::get() { return m_opaque_ptr; }
+
+CommandInterpreter &SBCommandInterpreter::ref() {
+ assert(m_opaque_ptr);
+ return *m_opaque_ptr;
+}
+
+void SBCommandInterpreter::reset(
+ lldb_private::CommandInterpreter *interpreter) {
+ m_opaque_ptr = interpreter;
+}
+
+void SBCommandInterpreter::SourceInitFileInHomeDirectory(
+ SBCommandReturnObject &result) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreter, SourceInitFileInHomeDirectory,
+ (lldb::SBCommandReturnObject &), result);
+
+ result.Clear();
+ if (IsValid()) {
+ TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget());
+ std::unique_lock<std::recursive_mutex> lock;
+ if (target_sp)
+ lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ m_opaque_ptr->SourceInitFileHome(result.ref());
+ } else {
+ result->AppendError("SBCommandInterpreter is not valid");
+ result->SetStatus(eReturnStatusFailed);
+ }
+}
+
+void SBCommandInterpreter::SourceInitFileInCurrentWorkingDirectory(
+ SBCommandReturnObject &result) {
+ LLDB_RECORD_METHOD(void, SBCommandInterpreter,
+ SourceInitFileInCurrentWorkingDirectory,
+ (lldb::SBCommandReturnObject &), result);
+
+ result.Clear();
+ if (IsValid()) {
+ TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget());
+ std::unique_lock<std::recursive_mutex> lock;
+ if (target_sp)
+ lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ m_opaque_ptr->SourceInitFileCwd(result.ref());
+ } else {
+ result->AppendError("SBCommandInterpreter is not valid");
+ result->SetStatus(eReturnStatusFailed);
+ }
+}
+
+SBBroadcaster SBCommandInterpreter::GetBroadcaster() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBBroadcaster, SBCommandInterpreter,
+ GetBroadcaster);
+
+
+ SBBroadcaster broadcaster(m_opaque_ptr, false);
+
+
+ return LLDB_RECORD_RESULT(broadcaster);
+}
+
+const char *SBCommandInterpreter::GetBroadcasterClass() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(const char *, SBCommandInterpreter,
+ GetBroadcasterClass);
+
+ return CommandInterpreter::GetStaticBroadcasterClass().AsCString();
+}
+
+const char *SBCommandInterpreter::GetArgumentTypeAsCString(
+ const lldb::CommandArgumentType arg_type) {
+ LLDB_RECORD_STATIC_METHOD(const char *, SBCommandInterpreter,
+ GetArgumentTypeAsCString,
+ (const lldb::CommandArgumentType), arg_type);
+
+ return CommandObject::GetArgumentTypeAsCString(arg_type);
+}
+
+const char *SBCommandInterpreter::GetArgumentDescriptionAsCString(
+ const lldb::CommandArgumentType arg_type) {
+ LLDB_RECORD_STATIC_METHOD(const char *, SBCommandInterpreter,
+ GetArgumentDescriptionAsCString,
+ (const lldb::CommandArgumentType), arg_type);
+
+ return CommandObject::GetArgumentDescriptionAsCString(arg_type);
+}
+
+bool SBCommandInterpreter::EventIsCommandInterpreterEvent(
+ const lldb::SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBCommandInterpreter,
+ EventIsCommandInterpreterEvent,
+ (const lldb::SBEvent &), event);
+
+ return event.GetBroadcasterClass() ==
+ SBCommandInterpreter::GetBroadcasterClass();
+}
+
+bool SBCommandInterpreter::SetCommandOverrideCallback(
+ const char *command_name, lldb::CommandOverrideCallback callback,
+ void *baton) {
+ LLDB_RECORD_DUMMY(bool, SBCommandInterpreter, SetCommandOverrideCallback,
+ (const char *, lldb::CommandOverrideCallback, void *),
+ command_name, callback, baton);
+
+ if (command_name && command_name[0] && IsValid()) {
+ llvm::StringRef command_name_str = command_name;
+ CommandObject *cmd_obj =
+ m_opaque_ptr->GetCommandObjectForCommand(command_name_str);
+ if (cmd_obj) {
+ assert(command_name_str.empty());
+ cmd_obj->SetOverrideCallback(callback, baton);
+ return true;
+ }
+ }
+ return false;
+}
+
+lldb::SBCommand SBCommandInterpreter::AddMultiwordCommand(const char *name,
+ const char *help) {
+ LLDB_RECORD_METHOD(lldb::SBCommand, SBCommandInterpreter, AddMultiwordCommand,
+ (const char *, const char *), name, help);
+
+ CommandObjectMultiword *new_command =
+ new CommandObjectMultiword(*m_opaque_ptr, name, help);
+ new_command->SetRemovable(true);
+ lldb::CommandObjectSP new_command_sp(new_command);
+ if (new_command_sp &&
+ m_opaque_ptr->AddUserCommand(name, new_command_sp, true))
+ return LLDB_RECORD_RESULT(lldb::SBCommand(new_command_sp));
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+}
+
+lldb::SBCommand SBCommandInterpreter::AddCommand(
+ const char *name, lldb::SBCommandPluginInterface *impl, const char *help) {
+ LLDB_RECORD_METHOD(
+ lldb::SBCommand, SBCommandInterpreter, AddCommand,
+ (const char *, lldb::SBCommandPluginInterface *, const char *), name,
+ impl, help);
+
+ lldb::CommandObjectSP new_command_sp;
+ new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
+ *m_opaque_ptr, name, impl, help);
+
+ if (new_command_sp &&
+ m_opaque_ptr->AddUserCommand(name, new_command_sp, true))
+ return LLDB_RECORD_RESULT(lldb::SBCommand(new_command_sp));
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+}
+
+lldb::SBCommand
+SBCommandInterpreter::AddCommand(const char *name,
+ lldb::SBCommandPluginInterface *impl,
+ const char *help, const char *syntax) {
+ LLDB_RECORD_METHOD(lldb::SBCommand, SBCommandInterpreter, AddCommand,
+ (const char *, lldb::SBCommandPluginInterface *,
+ const char *, const char *),
+ name, impl, help, syntax);
+
+ lldb::CommandObjectSP new_command_sp;
+ new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
+ *m_opaque_ptr, name, impl, help, syntax);
+
+ if (new_command_sp &&
+ m_opaque_ptr->AddUserCommand(name, new_command_sp, true))
+ return LLDB_RECORD_RESULT(lldb::SBCommand(new_command_sp));
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+}
+
+SBCommand::SBCommand() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBCommand); }
+
+SBCommand::SBCommand(lldb::CommandObjectSP cmd_sp) : m_opaque_sp(cmd_sp) {}
+
+bool SBCommand::IsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommand, IsValid);
+ return this->operator bool();
+}
+SBCommand::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommand, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+const char *SBCommand::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBCommand, GetName);
+
+ return (IsValid() ? ConstString(m_opaque_sp->GetCommandName()).AsCString() : nullptr);
+}
+
+const char *SBCommand::GetHelp() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBCommand, GetHelp);
+
+ return (IsValid() ? ConstString(m_opaque_sp->GetHelp()).AsCString()
+ : nullptr);
+}
+
+const char *SBCommand::GetHelpLong() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBCommand, GetHelpLong);
+
+ return (IsValid() ? ConstString(m_opaque_sp->GetHelpLong()).AsCString()
+ : nullptr);
+}
+
+void SBCommand::SetHelp(const char *help) {
+ LLDB_RECORD_METHOD(void, SBCommand, SetHelp, (const char *), help);
+
+ if (IsValid())
+ m_opaque_sp->SetHelp(help);
+}
+
+void SBCommand::SetHelpLong(const char *help) {
+ LLDB_RECORD_METHOD(void, SBCommand, SetHelpLong, (const char *), help);
+
+ if (IsValid())
+ m_opaque_sp->SetHelpLong(help);
+}
+
+lldb::SBCommand SBCommand::AddMultiwordCommand(const char *name,
+ const char *help) {
+ LLDB_RECORD_METHOD(lldb::SBCommand, SBCommand, AddMultiwordCommand,
+ (const char *, const char *), name, help);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+ if (!m_opaque_sp->IsMultiwordObject())
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+ CommandObjectMultiword *new_command = new CommandObjectMultiword(
+ m_opaque_sp->GetCommandInterpreter(), name, help);
+ new_command->SetRemovable(true);
+ lldb::CommandObjectSP new_command_sp(new_command);
+ if (new_command_sp && m_opaque_sp->LoadSubCommand(name, new_command_sp))
+ return LLDB_RECORD_RESULT(lldb::SBCommand(new_command_sp));
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+}
+
+lldb::SBCommand SBCommand::AddCommand(const char *name,
+ lldb::SBCommandPluginInterface *impl,
+ const char *help) {
+ LLDB_RECORD_METHOD(
+ lldb::SBCommand, SBCommand, AddCommand,
+ (const char *, lldb::SBCommandPluginInterface *, const char *), name,
+ impl, help);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+ if (!m_opaque_sp->IsMultiwordObject())
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+ lldb::CommandObjectSP new_command_sp;
+ new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
+ m_opaque_sp->GetCommandInterpreter(), name, impl, help);
+ if (new_command_sp && m_opaque_sp->LoadSubCommand(name, new_command_sp))
+ return LLDB_RECORD_RESULT(lldb::SBCommand(new_command_sp));
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+}
+
+lldb::SBCommand SBCommand::AddCommand(const char *name,
+ lldb::SBCommandPluginInterface *impl,
+ const char *help, const char *syntax) {
+ LLDB_RECORD_METHOD(lldb::SBCommand, SBCommand, AddCommand,
+ (const char *, lldb::SBCommandPluginInterface *,
+ const char *, const char *),
+ name, impl, help, syntax);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+ if (!m_opaque_sp->IsMultiwordObject())
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+ lldb::CommandObjectSP new_command_sp;
+ new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
+ m_opaque_sp->GetCommandInterpreter(), name, impl, help, syntax);
+ if (new_command_sp && m_opaque_sp->LoadSubCommand(name, new_command_sp))
+ return LLDB_RECORD_RESULT(lldb::SBCommand(new_command_sp));
+ return LLDB_RECORD_RESULT(lldb::SBCommand());
+}
+
+uint32_t SBCommand::GetFlags() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBCommand, GetFlags);
+
+ return (IsValid() ? m_opaque_sp->GetFlags().Get() : 0);
+}
+
+void SBCommand::SetFlags(uint32_t flags) {
+ LLDB_RECORD_METHOD(void, SBCommand, SetFlags, (uint32_t), flags);
+
+ if (IsValid())
+ m_opaque_sp->GetFlags().Set(flags);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBCommandInterpreterRunOptions>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBCommandInterpreterRunOptions, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandInterpreterRunOptions,
+ GetStopOnContinue, ());
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreterRunOptions,
+ SetStopOnContinue, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandInterpreterRunOptions,
+ GetStopOnError, ());
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreterRunOptions, SetStopOnError,
+ (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandInterpreterRunOptions,
+ GetStopOnCrash, ());
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreterRunOptions, SetStopOnCrash,
+ (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandInterpreterRunOptions,
+ GetEchoCommands, ());
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreterRunOptions, SetEchoCommands,
+ (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandInterpreterRunOptions,
+ GetEchoCommentCommands, ());
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreterRunOptions,
+ SetEchoCommentCommands, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandInterpreterRunOptions,
+ GetPrintResults, ());
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreterRunOptions, SetPrintResults,
+ (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandInterpreterRunOptions,
+ GetAddToHistory, ());
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreterRunOptions, SetAddToHistory,
+ (bool));
+ LLDB_REGISTER_CONSTRUCTOR(SBCommandInterpreter,
+ (lldb_private::CommandInterpreter *));
+ LLDB_REGISTER_CONSTRUCTOR(SBCommandInterpreter,
+ (const lldb::SBCommandInterpreter &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBCommandInterpreter &,
+ SBCommandInterpreter, operator=,(const lldb::SBCommandInterpreter &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandInterpreter, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandInterpreter, operator bool, ());
+ LLDB_REGISTER_METHOD(bool, SBCommandInterpreter, CommandExists,
+ (const char *));
+ LLDB_REGISTER_METHOD(bool, SBCommandInterpreter, AliasExists,
+ (const char *));
+ LLDB_REGISTER_METHOD(bool, SBCommandInterpreter, IsActive, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandInterpreter, WasInterrupted, ());
+ LLDB_REGISTER_METHOD(const char *, SBCommandInterpreter,
+ GetIOHandlerControlSequence, (char));
+ LLDB_REGISTER_METHOD(lldb::ReturnStatus, SBCommandInterpreter,
+ HandleCommand,
+ (const char *, lldb::SBCommandReturnObject &, bool));
+ LLDB_REGISTER_METHOD(lldb::ReturnStatus, SBCommandInterpreter,
+ HandleCommand,
+ (const char *, lldb::SBExecutionContext &,
+ lldb::SBCommandReturnObject &, bool));
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreter, HandleCommandsFromFile,
+ (lldb::SBFileSpec &, lldb::SBExecutionContext &,
+ lldb::SBCommandInterpreterRunOptions &,
+ lldb::SBCommandReturnObject));
+ LLDB_REGISTER_METHOD(int, SBCommandInterpreter, HandleCompletion,
+ (const char *, const char *, const char *, int, int,
+ lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(int, SBCommandInterpreter,
+ HandleCompletionWithDescriptions,
+ (const char *, const char *, const char *, int, int,
+ lldb::SBStringList &, lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(int, SBCommandInterpreter,
+ HandleCompletionWithDescriptions,
+ (const char *, uint32_t, int, int,
+ lldb::SBStringList &, lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(
+ int, SBCommandInterpreter, HandleCompletion,
+ (const char *, uint32_t, int, int, lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(bool, SBCommandInterpreter, HasCommands, ());
+ LLDB_REGISTER_METHOD(bool, SBCommandInterpreter, HasAliases, ());
+ LLDB_REGISTER_METHOD(bool, SBCommandInterpreter, HasAliasOptions, ());
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBCommandInterpreter, GetProcess, ());
+ LLDB_REGISTER_METHOD(lldb::SBDebugger, SBCommandInterpreter, GetDebugger,
+ ());
+ LLDB_REGISTER_METHOD(bool, SBCommandInterpreter, GetPromptOnQuit, ());
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreter, SetPromptOnQuit, (bool));
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreter, AllowExitCodeOnQuit,
+ (bool));
+ LLDB_REGISTER_METHOD(bool, SBCommandInterpreter, HasCustomQuitExitCode, ());
+ LLDB_REGISTER_METHOD(int, SBCommandInterpreter, GetQuitStatus, ());
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreter, ResolveCommand,
+ (const char *, lldb::SBCommandReturnObject &));
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreter,
+ SourceInitFileInHomeDirectory,
+ (lldb::SBCommandReturnObject &));
+ LLDB_REGISTER_METHOD(void, SBCommandInterpreter,
+ SourceInitFileInCurrentWorkingDirectory,
+ (lldb::SBCommandReturnObject &));
+ LLDB_REGISTER_METHOD(lldb::SBBroadcaster, SBCommandInterpreter,
+ GetBroadcaster, ());
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBCommandInterpreter,
+ GetBroadcasterClass, ());
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBCommandInterpreter,
+ GetArgumentTypeAsCString,
+ (const lldb::CommandArgumentType));
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBCommandInterpreter,
+ GetArgumentDescriptionAsCString,
+ (const lldb::CommandArgumentType));
+ LLDB_REGISTER_STATIC_METHOD(bool, SBCommandInterpreter,
+ EventIsCommandInterpreterEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommandInterpreter,
+ AddMultiwordCommand, (const char *, const char *));
+ LLDB_REGISTER_METHOD(
+ lldb::SBCommand, SBCommandInterpreter, AddCommand,
+ (const char *, lldb::SBCommandPluginInterface *, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommandInterpreter, AddCommand,
+ (const char *, lldb::SBCommandPluginInterface *,
+ const char *, const char *));
+ LLDB_REGISTER_CONSTRUCTOR(SBCommand, ());
+ LLDB_REGISTER_METHOD(bool, SBCommand, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommand, operator bool, ());
+ LLDB_REGISTER_METHOD(const char *, SBCommand, GetName, ());
+ LLDB_REGISTER_METHOD(const char *, SBCommand, GetHelp, ());
+ LLDB_REGISTER_METHOD(const char *, SBCommand, GetHelpLong, ());
+ LLDB_REGISTER_METHOD(void, SBCommand, SetHelp, (const char *));
+ LLDB_REGISTER_METHOD(void, SBCommand, SetHelpLong, (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommand, AddMultiwordCommand,
+ (const char *, const char *));
+ LLDB_REGISTER_METHOD(
+ lldb::SBCommand, SBCommand, AddCommand,
+ (const char *, lldb::SBCommandPluginInterface *, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommand, AddCommand,
+ (const char *, lldb::SBCommandPluginInterface *,
+ const char *, const char *));
+ LLDB_REGISTER_METHOD(uint32_t, SBCommand, GetFlags, ());
+ LLDB_REGISTER_METHOD(void, SBCommand, SetFlags, (uint32_t));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBCommandReturnObject.cpp b/contrib/llvm-project/lldb/source/API/SBCommandReturnObject.cpp
new file mode 100644
index 000000000000..94e89916f7f6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBCommandReturnObject.cpp
@@ -0,0 +1,391 @@
+//===-- SBCommandReturnObject.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBCommandReturnObject.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBCommandReturnObject::SBCommandReturnObject()
+ : m_opaque_up(new CommandReturnObject()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBCommandReturnObject);
+}
+
+SBCommandReturnObject::SBCommandReturnObject(const SBCommandReturnObject &rhs)
+ : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBCommandReturnObject,
+ (const lldb::SBCommandReturnObject &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+SBCommandReturnObject::SBCommandReturnObject(CommandReturnObject *ptr)
+ : m_opaque_up(ptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBCommandReturnObject,
+ (lldb_private::CommandReturnObject *), ptr);
+}
+
+SBCommandReturnObject::~SBCommandReturnObject() = default;
+
+CommandReturnObject *SBCommandReturnObject::Release() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb_private::CommandReturnObject *,
+ SBCommandReturnObject, Release);
+
+ return LLDB_RECORD_RESULT(m_opaque_up.release());
+}
+
+const SBCommandReturnObject &SBCommandReturnObject::
+operator=(const SBCommandReturnObject &rhs) {
+ LLDB_RECORD_METHOD(
+ const lldb::SBCommandReturnObject &,
+ SBCommandReturnObject, operator=,(const lldb::SBCommandReturnObject &),
+ rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBCommandReturnObject::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandReturnObject, IsValid);
+ return this->operator bool();
+}
+SBCommandReturnObject::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandReturnObject, operator bool);
+
+ return m_opaque_up != nullptr;
+}
+
+const char *SBCommandReturnObject::GetOutput() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBCommandReturnObject, GetOutput);
+
+ if (m_opaque_up) {
+ llvm::StringRef output = m_opaque_up->GetOutputData();
+ ConstString result(output.empty() ? llvm::StringRef("") : output);
+
+ return result.AsCString();
+ }
+
+ return nullptr;
+}
+
+const char *SBCommandReturnObject::GetError() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBCommandReturnObject, GetError);
+
+ if (m_opaque_up) {
+ llvm::StringRef output = m_opaque_up->GetErrorData();
+ ConstString result(output.empty() ? llvm::StringRef("") : output);
+ return result.AsCString();
+ }
+
+ return nullptr;
+}
+
+size_t SBCommandReturnObject::GetOutputSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBCommandReturnObject, GetOutputSize);
+
+ return (m_opaque_up ? m_opaque_up->GetOutputData().size() : 0);
+}
+
+size_t SBCommandReturnObject::GetErrorSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBCommandReturnObject, GetErrorSize);
+
+ return (m_opaque_up ? m_opaque_up->GetErrorData().size() : 0);
+}
+
+size_t SBCommandReturnObject::PutOutput(FILE *fh) {
+ LLDB_RECORD_METHOD(size_t, SBCommandReturnObject, PutOutput, (FILE *), fh);
+
+ if (fh) {
+ size_t num_bytes = GetOutputSize();
+ if (num_bytes)
+ return ::fprintf(fh, "%s", GetOutput());
+ }
+ return 0;
+}
+
+size_t SBCommandReturnObject::PutError(FILE *fh) {
+ LLDB_RECORD_METHOD(size_t, SBCommandReturnObject, PutError, (FILE *), fh);
+
+ if (fh) {
+ size_t num_bytes = GetErrorSize();
+ if (num_bytes)
+ return ::fprintf(fh, "%s", GetError());
+ }
+ return 0;
+}
+
+void SBCommandReturnObject::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBCommandReturnObject, Clear);
+
+ if (m_opaque_up)
+ m_opaque_up->Clear();
+}
+
+lldb::ReturnStatus SBCommandReturnObject::GetStatus() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::ReturnStatus, SBCommandReturnObject,
+ GetStatus);
+
+ return (m_opaque_up ? m_opaque_up->GetStatus() : lldb::eReturnStatusInvalid);
+}
+
+void SBCommandReturnObject::SetStatus(lldb::ReturnStatus status) {
+ LLDB_RECORD_METHOD(void, SBCommandReturnObject, SetStatus,
+ (lldb::ReturnStatus), status);
+
+ if (m_opaque_up)
+ m_opaque_up->SetStatus(status);
+}
+
+bool SBCommandReturnObject::Succeeded() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommandReturnObject, Succeeded);
+
+ return (m_opaque_up ? m_opaque_up->Succeeded() : false);
+}
+
+bool SBCommandReturnObject::HasResult() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommandReturnObject, HasResult);
+
+ return (m_opaque_up ? m_opaque_up->HasResult() : false);
+}
+
+void SBCommandReturnObject::AppendMessage(const char *message) {
+ LLDB_RECORD_METHOD(void, SBCommandReturnObject, AppendMessage, (const char *),
+ message);
+
+ if (m_opaque_up)
+ m_opaque_up->AppendMessage(message);
+}
+
+void SBCommandReturnObject::AppendWarning(const char *message) {
+ LLDB_RECORD_METHOD(void, SBCommandReturnObject, AppendWarning, (const char *),
+ message);
+
+ if (m_opaque_up)
+ m_opaque_up->AppendWarning(message);
+}
+
+CommandReturnObject *SBCommandReturnObject::operator->() const {
+ return m_opaque_up.get();
+}
+
+CommandReturnObject *SBCommandReturnObject::get() const {
+ return m_opaque_up.get();
+}
+
+CommandReturnObject &SBCommandReturnObject::operator*() const {
+ assert(m_opaque_up.get());
+ return *(m_opaque_up.get());
+}
+
+CommandReturnObject &SBCommandReturnObject::ref() const {
+ assert(m_opaque_up.get());
+ return *(m_opaque_up.get());
+}
+
+void SBCommandReturnObject::SetLLDBObjectPtr(CommandReturnObject *ptr) {
+ if (m_opaque_up)
+ m_opaque_up.reset(ptr);
+}
+
+bool SBCommandReturnObject::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBCommandReturnObject, GetDescription,
+ (lldb::SBStream &), description);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_up) {
+ description.Printf("Error: ");
+ lldb::ReturnStatus status = m_opaque_up->GetStatus();
+ if (status == lldb::eReturnStatusStarted)
+ strm.PutCString("Started");
+ else if (status == lldb::eReturnStatusInvalid)
+ strm.PutCString("Invalid");
+ else if (m_opaque_up->Succeeded())
+ strm.PutCString("Success");
+ else
+ strm.PutCString("Fail");
+
+ if (GetOutputSize() > 0)
+ strm.Printf("\nOutput Message:\n%s", GetOutput());
+
+ if (GetErrorSize() > 0)
+ strm.Printf("\nError Message:\n%s", GetError());
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+void SBCommandReturnObject::SetImmediateOutputFile(FILE *fh) {
+ LLDB_RECORD_METHOD(void, SBCommandReturnObject, SetImmediateOutputFile,
+ (FILE *), fh);
+
+ SetImmediateOutputFile(fh, false);
+}
+
+void SBCommandReturnObject::SetImmediateErrorFile(FILE *fh) {
+ LLDB_RECORD_METHOD(void, SBCommandReturnObject, SetImmediateErrorFile,
+ (FILE *), fh);
+
+ SetImmediateErrorFile(fh, false);
+}
+
+void SBCommandReturnObject::SetImmediateOutputFile(FILE *fh,
+ bool transfer_ownership) {
+ LLDB_RECORD_METHOD(void, SBCommandReturnObject, SetImmediateOutputFile,
+ (FILE *, bool), fh, transfer_ownership);
+
+ if (m_opaque_up)
+ m_opaque_up->SetImmediateOutputFile(fh, transfer_ownership);
+}
+
+void SBCommandReturnObject::SetImmediateErrorFile(FILE *fh,
+ bool transfer_ownership) {
+ LLDB_RECORD_METHOD(void, SBCommandReturnObject, SetImmediateErrorFile,
+ (FILE *, bool), fh, transfer_ownership);
+
+ if (m_opaque_up)
+ m_opaque_up->SetImmediateErrorFile(fh, transfer_ownership);
+}
+
+void SBCommandReturnObject::PutCString(const char *string, int len) {
+ LLDB_RECORD_METHOD(void, SBCommandReturnObject, PutCString,
+ (const char *, int), string, len);
+
+ if (m_opaque_up) {
+ if (len == 0 || string == nullptr || *string == 0) {
+ return;
+ } else if (len > 0) {
+ std::string buffer(string, len);
+ m_opaque_up->AppendMessage(buffer.c_str());
+ } else
+ m_opaque_up->AppendMessage(string);
+ }
+}
+
+const char *SBCommandReturnObject::GetOutput(bool only_if_no_immediate) {
+ LLDB_RECORD_METHOD(const char *, SBCommandReturnObject, GetOutput, (bool),
+ only_if_no_immediate);
+
+ if (!m_opaque_up)
+ return nullptr;
+ if (!only_if_no_immediate ||
+ m_opaque_up->GetImmediateOutputStream().get() == nullptr)
+ return GetOutput();
+ return nullptr;
+}
+
+const char *SBCommandReturnObject::GetError(bool only_if_no_immediate) {
+ LLDB_RECORD_METHOD(const char *, SBCommandReturnObject, GetError, (bool),
+ only_if_no_immediate);
+
+ if (!m_opaque_up)
+ return nullptr;
+ if (!only_if_no_immediate ||
+ m_opaque_up->GetImmediateErrorStream().get() == nullptr)
+ return GetError();
+ return nullptr;
+}
+
+size_t SBCommandReturnObject::Printf(const char *format, ...) {
+ if (m_opaque_up) {
+ va_list args;
+ va_start(args, format);
+ size_t result = m_opaque_up->GetOutputStream().PrintfVarArg(format, args);
+ va_end(args);
+ return result;
+ }
+ return 0;
+}
+
+void SBCommandReturnObject::SetError(lldb::SBError &error,
+ const char *fallback_error_cstr) {
+ LLDB_RECORD_METHOD(void, SBCommandReturnObject, SetError,
+ (lldb::SBError &, const char *), error,
+ fallback_error_cstr);
+
+ if (m_opaque_up) {
+ if (error.IsValid())
+ m_opaque_up->SetError(error.ref(), fallback_error_cstr);
+ else if (fallback_error_cstr)
+ m_opaque_up->SetError(Status(), fallback_error_cstr);
+ }
+}
+
+void SBCommandReturnObject::SetError(const char *error_cstr) {
+ LLDB_RECORD_METHOD(void, SBCommandReturnObject, SetError, (const char *),
+ error_cstr);
+
+ if (m_opaque_up && error_cstr)
+ m_opaque_up->SetError(error_cstr);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBCommandReturnObject>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBCommandReturnObject, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBCommandReturnObject,
+ (const lldb::SBCommandReturnObject &));
+ LLDB_REGISTER_CONSTRUCTOR(SBCommandReturnObject,
+ (lldb_private::CommandReturnObject *));
+ LLDB_REGISTER_METHOD(lldb_private::CommandReturnObject *,
+ SBCommandReturnObject, Release, ());
+ LLDB_REGISTER_METHOD(
+ const lldb::SBCommandReturnObject &,
+ SBCommandReturnObject, operator=,(const lldb::SBCommandReturnObject &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandReturnObject, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommandReturnObject, operator bool, ());
+ LLDB_REGISTER_METHOD(const char *, SBCommandReturnObject, GetOutput, ());
+ LLDB_REGISTER_METHOD(const char *, SBCommandReturnObject, GetError, ());
+ LLDB_REGISTER_METHOD(size_t, SBCommandReturnObject, GetOutputSize, ());
+ LLDB_REGISTER_METHOD(size_t, SBCommandReturnObject, GetErrorSize, ());
+ LLDB_REGISTER_METHOD(size_t, SBCommandReturnObject, PutOutput, (FILE *));
+ LLDB_REGISTER_METHOD(size_t, SBCommandReturnObject, PutError, (FILE *));
+ LLDB_REGISTER_METHOD(void, SBCommandReturnObject, Clear, ());
+ LLDB_REGISTER_METHOD(lldb::ReturnStatus, SBCommandReturnObject, GetStatus,
+ ());
+ LLDB_REGISTER_METHOD(void, SBCommandReturnObject, SetStatus,
+ (lldb::ReturnStatus));
+ LLDB_REGISTER_METHOD(bool, SBCommandReturnObject, Succeeded, ());
+ LLDB_REGISTER_METHOD(bool, SBCommandReturnObject, HasResult, ());
+ LLDB_REGISTER_METHOD(void, SBCommandReturnObject, AppendMessage,
+ (const char *));
+ LLDB_REGISTER_METHOD(void, SBCommandReturnObject, AppendWarning,
+ (const char *));
+ LLDB_REGISTER_METHOD(bool, SBCommandReturnObject, GetDescription,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(void, SBCommandReturnObject, SetImmediateOutputFile,
+ (FILE *));
+ LLDB_REGISTER_METHOD(void, SBCommandReturnObject, SetImmediateErrorFile,
+ (FILE *));
+ LLDB_REGISTER_METHOD(void, SBCommandReturnObject, SetImmediateOutputFile,
+ (FILE *, bool));
+ LLDB_REGISTER_METHOD(void, SBCommandReturnObject, SetImmediateErrorFile,
+ (FILE *, bool));
+ LLDB_REGISTER_METHOD(void, SBCommandReturnObject, PutCString,
+ (const char *, int));
+ LLDB_REGISTER_METHOD(const char *, SBCommandReturnObject, GetOutput,
+ (bool));
+ LLDB_REGISTER_METHOD(const char *, SBCommandReturnObject, GetError, (bool));
+ LLDB_REGISTER_METHOD(void, SBCommandReturnObject, SetError,
+ (lldb::SBError &, const char *));
+ LLDB_REGISTER_METHOD(void, SBCommandReturnObject, SetError, (const char *));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBCommunication.cpp b/contrib/llvm-project/lldb/source/API/SBCommunication.cpp
new file mode 100644
index 000000000000..90df70bde72f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBCommunication.cpp
@@ -0,0 +1,215 @@
+//===-- SBCommunication.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBCommunication.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBBroadcaster.h"
+#include "lldb/Core/Communication.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/Host.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBCommunication::SBCommunication() : m_opaque(nullptr), m_opaque_owned(false) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBCommunication);
+}
+
+SBCommunication::SBCommunication(const char *broadcaster_name)
+ : m_opaque(new Communication(broadcaster_name)), m_opaque_owned(true) {
+ LLDB_RECORD_CONSTRUCTOR(SBCommunication, (const char *), broadcaster_name);
+}
+
+SBCommunication::~SBCommunication() {
+ if (m_opaque && m_opaque_owned)
+ delete m_opaque;
+ m_opaque = nullptr;
+ m_opaque_owned = false;
+}
+
+bool SBCommunication::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommunication, IsValid);
+ return this->operator bool();
+}
+SBCommunication::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommunication, operator bool);
+
+ return m_opaque != nullptr;
+}
+
+bool SBCommunication::GetCloseOnEOF() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommunication, GetCloseOnEOF);
+
+ if (m_opaque)
+ return m_opaque->GetCloseOnEOF();
+ return false;
+}
+
+void SBCommunication::SetCloseOnEOF(bool b) {
+ LLDB_RECORD_METHOD(void, SBCommunication, SetCloseOnEOF, (bool), b);
+
+ if (m_opaque)
+ m_opaque->SetCloseOnEOF(b);
+}
+
+ConnectionStatus SBCommunication::Connect(const char *url) {
+ LLDB_RECORD_METHOD(lldb::ConnectionStatus, SBCommunication, Connect,
+ (const char *), url);
+
+ if (m_opaque) {
+ if (!m_opaque->HasConnection())
+ m_opaque->SetConnection(Host::CreateDefaultConnection(url).release());
+ return m_opaque->Connect(url, nullptr);
+ }
+ return eConnectionStatusNoConnection;
+}
+
+ConnectionStatus SBCommunication::AdoptFileDesriptor(int fd, bool owns_fd) {
+ LLDB_RECORD_METHOD(lldb::ConnectionStatus, SBCommunication,
+ AdoptFileDesriptor, (int, bool), fd, owns_fd);
+
+ ConnectionStatus status = eConnectionStatusNoConnection;
+ if (m_opaque) {
+ if (m_opaque->HasConnection()) {
+ if (m_opaque->IsConnected())
+ m_opaque->Disconnect();
+ }
+ m_opaque->SetConnection(new ConnectionFileDescriptor(fd, owns_fd));
+ if (m_opaque->IsConnected())
+ status = eConnectionStatusSuccess;
+ else
+ status = eConnectionStatusLostConnection;
+ }
+ return status;
+}
+
+ConnectionStatus SBCommunication::Disconnect() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::ConnectionStatus, SBCommunication,
+ Disconnect);
+
+ ConnectionStatus status = eConnectionStatusNoConnection;
+ if (m_opaque)
+ status = m_opaque->Disconnect();
+ return status;
+}
+
+bool SBCommunication::IsConnected() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommunication, IsConnected);
+
+ return m_opaque ? m_opaque->IsConnected() : false;
+}
+
+size_t SBCommunication::Read(void *dst, size_t dst_len, uint32_t timeout_usec,
+ ConnectionStatus &status) {
+ LLDB_RECORD_DUMMY(size_t, SBCommunication, Read,
+ (void *, size_t, uint32_t, lldb::ConnectionStatus &), dst,
+ dst_len, timeout_usec, status);
+
+ size_t bytes_read = 0;
+ Timeout<std::micro> timeout = timeout_usec == UINT32_MAX
+ ? Timeout<std::micro>(llvm::None)
+ : std::chrono::microseconds(timeout_usec);
+ if (m_opaque)
+ bytes_read = m_opaque->Read(dst, dst_len, timeout, status, nullptr);
+ else
+ status = eConnectionStatusNoConnection;
+
+ return bytes_read;
+}
+
+size_t SBCommunication::Write(const void *src, size_t src_len,
+ ConnectionStatus &status) {
+ LLDB_RECORD_DUMMY(size_t, SBCommunication, Write,
+ (const void *, size_t, lldb::ConnectionStatus &), src,
+ src_len, status);
+
+ size_t bytes_written = 0;
+ if (m_opaque)
+ bytes_written = m_opaque->Write(src, src_len, status, nullptr);
+ else
+ status = eConnectionStatusNoConnection;
+
+ return bytes_written;
+}
+
+bool SBCommunication::ReadThreadStart() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommunication, ReadThreadStart);
+
+ return m_opaque ? m_opaque->StartReadThread() : false;
+}
+
+bool SBCommunication::ReadThreadStop() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommunication, ReadThreadStop);
+
+ return m_opaque ? m_opaque->StopReadThread() : false;
+}
+
+bool SBCommunication::ReadThreadIsRunning() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBCommunication, ReadThreadIsRunning);
+
+ return m_opaque ? m_opaque->ReadThreadIsRunning() : false;
+}
+
+bool SBCommunication::SetReadThreadBytesReceivedCallback(
+ ReadThreadBytesReceived callback, void *callback_baton) {
+ LLDB_RECORD_DUMMY(bool, SBCommunication, SetReadThreadBytesReceivedCallback,
+ (lldb::SBCommunication::ReadThreadBytesReceived, void *),
+ callback, callback_baton);
+
+ bool result = false;
+ if (m_opaque) {
+ m_opaque->SetReadThreadBytesReceivedCallback(callback, callback_baton);
+ result = true;
+ }
+ return result;
+}
+
+SBBroadcaster SBCommunication::GetBroadcaster() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBBroadcaster, SBCommunication,
+ GetBroadcaster);
+
+ SBBroadcaster broadcaster(m_opaque, false);
+ return LLDB_RECORD_RESULT(broadcaster);
+}
+
+const char *SBCommunication::GetBroadcasterClass() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(const char *, SBCommunication,
+ GetBroadcasterClass);
+
+ return Communication::GetStaticBroadcasterClass().AsCString();
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBCommunication>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBCommunication, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBCommunication, (const char *));
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommunication, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommunication, operator bool, ());
+ LLDB_REGISTER_METHOD(bool, SBCommunication, GetCloseOnEOF, ());
+ LLDB_REGISTER_METHOD(void, SBCommunication, SetCloseOnEOF, (bool));
+ LLDB_REGISTER_METHOD(lldb::ConnectionStatus, SBCommunication, Connect,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::ConnectionStatus, SBCommunication,
+ AdoptFileDesriptor, (int, bool));
+ LLDB_REGISTER_METHOD(lldb::ConnectionStatus, SBCommunication, Disconnect,
+ ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBCommunication, IsConnected, ());
+ LLDB_REGISTER_METHOD(bool, SBCommunication, ReadThreadStart, ());
+ LLDB_REGISTER_METHOD(bool, SBCommunication, ReadThreadStop, ());
+ LLDB_REGISTER_METHOD(bool, SBCommunication, ReadThreadIsRunning, ());
+ LLDB_REGISTER_METHOD(lldb::SBBroadcaster, SBCommunication, GetBroadcaster,
+ ());
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBCommunication,
+ GetBroadcasterClass, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBCompileUnit.cpp b/contrib/llvm-project/lldb/source/API/SBCompileUnit.cpp
new file mode 100644
index 000000000000..c9ca70645d95
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBCompileUnit.cpp
@@ -0,0 +1,276 @@
+//===-- SBCompileUnit.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBCompileUnit.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBLineEntry.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Type.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBCompileUnit::SBCompileUnit() : m_opaque_ptr(nullptr) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBCompileUnit);
+}
+
+SBCompileUnit::SBCompileUnit(lldb_private::CompileUnit *lldb_object_ptr)
+ : m_opaque_ptr(lldb_object_ptr) {}
+
+SBCompileUnit::SBCompileUnit(const SBCompileUnit &rhs)
+ : m_opaque_ptr(rhs.m_opaque_ptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBCompileUnit, (const lldb::SBCompileUnit &), rhs);
+}
+
+const SBCompileUnit &SBCompileUnit::operator=(const SBCompileUnit &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBCompileUnit &,
+ SBCompileUnit, operator=,(const lldb::SBCompileUnit &),
+ rhs);
+
+ m_opaque_ptr = rhs.m_opaque_ptr;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBCompileUnit::~SBCompileUnit() { m_opaque_ptr = nullptr; }
+
+SBFileSpec SBCompileUnit::GetFileSpec() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBFileSpec, SBCompileUnit,
+ GetFileSpec);
+
+ SBFileSpec file_spec;
+ if (m_opaque_ptr)
+ file_spec.SetFileSpec(*m_opaque_ptr);
+ return LLDB_RECORD_RESULT(file_spec);
+}
+
+uint32_t SBCompileUnit::GetNumLineEntries() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBCompileUnit, GetNumLineEntries);
+
+ if (m_opaque_ptr) {
+ LineTable *line_table = m_opaque_ptr->GetLineTable();
+ if (line_table) {
+ return line_table->GetSize();
+ }
+ }
+ return 0;
+}
+
+SBLineEntry SBCompileUnit::GetLineEntryAtIndex(uint32_t idx) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBLineEntry, SBCompileUnit,
+ GetLineEntryAtIndex, (uint32_t), idx);
+
+ SBLineEntry sb_line_entry;
+ if (m_opaque_ptr) {
+ LineTable *line_table = m_opaque_ptr->GetLineTable();
+ if (line_table) {
+ LineEntry line_entry;
+ if (line_table->GetLineEntryAtIndex(idx, line_entry))
+ sb_line_entry.SetLineEntry(line_entry);
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_line_entry);
+}
+
+uint32_t SBCompileUnit::FindLineEntryIndex(uint32_t start_idx, uint32_t line,
+ SBFileSpec *inline_file_spec) const {
+ LLDB_RECORD_METHOD_CONST(uint32_t, SBCompileUnit, FindLineEntryIndex,
+ (uint32_t, uint32_t, lldb::SBFileSpec *), start_idx,
+ line, inline_file_spec);
+
+ const bool exact = true;
+ return FindLineEntryIndex(start_idx, line, inline_file_spec, exact);
+}
+
+uint32_t SBCompileUnit::FindLineEntryIndex(uint32_t start_idx, uint32_t line,
+ SBFileSpec *inline_file_spec,
+ bool exact) const {
+ LLDB_RECORD_METHOD_CONST(uint32_t, SBCompileUnit, FindLineEntryIndex,
+ (uint32_t, uint32_t, lldb::SBFileSpec *, bool),
+ start_idx, line, inline_file_spec, exact);
+
+ uint32_t index = UINT32_MAX;
+ if (m_opaque_ptr) {
+ FileSpec file_spec;
+ if (inline_file_spec && inline_file_spec->IsValid())
+ file_spec = inline_file_spec->ref();
+ else
+ file_spec = *m_opaque_ptr;
+
+ index = m_opaque_ptr->FindLineEntry(
+ start_idx, line, inline_file_spec ? inline_file_spec->get() : nullptr,
+ exact, nullptr);
+ }
+
+ return index;
+}
+
+uint32_t SBCompileUnit::GetNumSupportFiles() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBCompileUnit, GetNumSupportFiles);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->GetSupportFiles().GetSize();
+
+ return 0;
+}
+
+lldb::SBTypeList SBCompileUnit::GetTypes(uint32_t type_mask) {
+ LLDB_RECORD_METHOD(lldb::SBTypeList, SBCompileUnit, GetTypes, (uint32_t),
+ type_mask);
+
+ SBTypeList sb_type_list;
+
+ if (!m_opaque_ptr)
+ return LLDB_RECORD_RESULT(sb_type_list);
+
+ ModuleSP module_sp(m_opaque_ptr->GetModule());
+ if (!module_sp)
+ return LLDB_RECORD_RESULT(sb_type_list);
+
+ SymbolVendor *vendor = module_sp->GetSymbolVendor();
+ if (!vendor)
+ return LLDB_RECORD_RESULT(sb_type_list);
+
+ TypeClass type_class = static_cast<TypeClass>(type_mask);
+ TypeList type_list;
+ vendor->GetTypes(m_opaque_ptr, type_class, type_list);
+ sb_type_list.m_opaque_up->Append(type_list);
+ return LLDB_RECORD_RESULT(sb_type_list);
+}
+
+SBFileSpec SBCompileUnit::GetSupportFileAtIndex(uint32_t idx) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBFileSpec, SBCompileUnit,
+ GetSupportFileAtIndex, (uint32_t), idx);
+
+ SBFileSpec sb_file_spec;
+ if (m_opaque_ptr) {
+ FileSpec spec = m_opaque_ptr->GetSupportFiles().GetFileSpecAtIndex(idx);
+ sb_file_spec.SetFileSpec(spec);
+ }
+
+
+ return LLDB_RECORD_RESULT(sb_file_spec);
+}
+
+uint32_t SBCompileUnit::FindSupportFileIndex(uint32_t start_idx,
+ const SBFileSpec &sb_file,
+ bool full) {
+ LLDB_RECORD_METHOD(uint32_t, SBCompileUnit, FindSupportFileIndex,
+ (uint32_t, const lldb::SBFileSpec &, bool), start_idx,
+ sb_file, full);
+
+ if (m_opaque_ptr) {
+ const FileSpecList &support_files = m_opaque_ptr->GetSupportFiles();
+ return support_files.FindFileIndex(start_idx, sb_file.ref(), full);
+ }
+ return 0;
+}
+
+lldb::LanguageType SBCompileUnit::GetLanguage() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::LanguageType, SBCompileUnit, GetLanguage);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->GetLanguage();
+ return lldb::eLanguageTypeUnknown;
+}
+
+bool SBCompileUnit::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCompileUnit, IsValid);
+ return this->operator bool();
+}
+SBCompileUnit::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCompileUnit, operator bool);
+
+ return m_opaque_ptr != nullptr;
+}
+
+bool SBCompileUnit::operator==(const SBCompileUnit &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBCompileUnit, operator==,(const lldb::SBCompileUnit &), rhs);
+
+ return m_opaque_ptr == rhs.m_opaque_ptr;
+}
+
+bool SBCompileUnit::operator!=(const SBCompileUnit &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBCompileUnit, operator!=,(const lldb::SBCompileUnit &), rhs);
+
+ return m_opaque_ptr != rhs.m_opaque_ptr;
+}
+
+const lldb_private::CompileUnit *SBCompileUnit::operator->() const {
+ return m_opaque_ptr;
+}
+
+const lldb_private::CompileUnit &SBCompileUnit::operator*() const {
+ return *m_opaque_ptr;
+}
+
+lldb_private::CompileUnit *SBCompileUnit::get() { return m_opaque_ptr; }
+
+void SBCompileUnit::reset(lldb_private::CompileUnit *lldb_object_ptr) {
+ m_opaque_ptr = lldb_object_ptr;
+}
+
+bool SBCompileUnit::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBCompileUnit, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_ptr) {
+ m_opaque_ptr->Dump(&strm, false);
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBCompileUnit>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBCompileUnit, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBCompileUnit, (const lldb::SBCompileUnit &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBCompileUnit &,
+ SBCompileUnit, operator=,(const lldb::SBCompileUnit &));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBFileSpec, SBCompileUnit, GetFileSpec,
+ ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBCompileUnit, GetNumLineEntries, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBLineEntry, SBCompileUnit,
+ GetLineEntryAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBCompileUnit, FindLineEntryIndex,
+ (uint32_t, uint32_t, lldb::SBFileSpec *));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBCompileUnit, FindLineEntryIndex,
+ (uint32_t, uint32_t, lldb::SBFileSpec *, bool));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBCompileUnit, GetNumSupportFiles, ());
+ LLDB_REGISTER_METHOD(lldb::SBTypeList, SBCompileUnit, GetTypes, (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBFileSpec, SBCompileUnit,
+ GetSupportFileAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBCompileUnit, FindSupportFileIndex,
+ (uint32_t, const lldb::SBFileSpec &, bool));
+ LLDB_REGISTER_METHOD(lldb::LanguageType, SBCompileUnit, GetLanguage, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBCompileUnit, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBCompileUnit, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBCompileUnit, operator==,(const lldb::SBCompileUnit &));
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBCompileUnit, operator!=,(const lldb::SBCompileUnit &));
+ LLDB_REGISTER_METHOD(bool, SBCompileUnit, GetDescription,
+ (lldb::SBStream &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBData.cpp b/contrib/llvm-project/lldb/source/API/SBData.cpp
new file mode 100644
index 000000000000..528cd8d43ecc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBData.cpp
@@ -0,0 +1,724 @@
+//===-- SBData.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBData.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBStream.h"
+
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Stream.h"
+
+#include <cinttypes>
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBData::SBData() : m_opaque_sp(new DataExtractor()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBData);
+}
+
+SBData::SBData(const lldb::DataExtractorSP &data_sp) : m_opaque_sp(data_sp) {}
+
+SBData::SBData(const SBData &rhs) : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBData, (const lldb::SBData &), rhs);
+}
+
+const SBData &SBData::operator=(const SBData &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBData &,
+ SBData, operator=,(const lldb::SBData &), rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = rhs.m_opaque_sp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBData::~SBData() {}
+
+void SBData::SetOpaque(const lldb::DataExtractorSP &data_sp) {
+ m_opaque_sp = data_sp;
+}
+
+lldb_private::DataExtractor *SBData::get() const { return m_opaque_sp.get(); }
+
+lldb_private::DataExtractor *SBData::operator->() const {
+ return m_opaque_sp.operator->();
+}
+
+lldb::DataExtractorSP &SBData::operator*() { return m_opaque_sp; }
+
+const lldb::DataExtractorSP &SBData::operator*() const { return m_opaque_sp; }
+
+bool SBData::IsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBData, IsValid);
+ return this->operator bool();
+}
+SBData::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBData, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+uint8_t SBData::GetAddressByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint8_t, SBData, GetAddressByteSize);
+
+ uint8_t value = 0;
+ if (m_opaque_sp.get())
+ value = m_opaque_sp->GetAddressByteSize();
+ return value;
+}
+
+void SBData::SetAddressByteSize(uint8_t addr_byte_size) {
+ LLDB_RECORD_METHOD(void, SBData, SetAddressByteSize, (uint8_t),
+ addr_byte_size);
+
+ if (m_opaque_sp.get())
+ m_opaque_sp->SetAddressByteSize(addr_byte_size);
+}
+
+void SBData::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBData, Clear);
+
+ if (m_opaque_sp.get())
+ m_opaque_sp->Clear();
+}
+
+size_t SBData::GetByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBData, GetByteSize);
+
+ size_t value = 0;
+ if (m_opaque_sp.get())
+ value = m_opaque_sp->GetByteSize();
+ return value;
+}
+
+lldb::ByteOrder SBData::GetByteOrder() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::ByteOrder, SBData, GetByteOrder);
+
+ lldb::ByteOrder value = eByteOrderInvalid;
+ if (m_opaque_sp.get())
+ value = m_opaque_sp->GetByteOrder();
+ return value;
+}
+
+void SBData::SetByteOrder(lldb::ByteOrder endian) {
+ LLDB_RECORD_METHOD(void, SBData, SetByteOrder, (lldb::ByteOrder), endian);
+
+ if (m_opaque_sp.get())
+ m_opaque_sp->SetByteOrder(endian);
+}
+
+float SBData::GetFloat(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(float, SBData, GetFloat, (lldb::SBError &, lldb::offset_t),
+ error, offset);
+
+ float value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = m_opaque_sp->GetFloat(&offset);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+double SBData::GetDouble(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(double, SBData, GetDouble,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ double value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = m_opaque_sp->GetDouble(&offset);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+long double SBData::GetLongDouble(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(long double, SBData, GetLongDouble,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ long double value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = m_opaque_sp->GetLongDouble(&offset);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+lldb::addr_t SBData::GetAddress(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(lldb::addr_t, SBData, GetAddress,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ lldb::addr_t value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = m_opaque_sp->GetAddress(&offset);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+uint8_t SBData::GetUnsignedInt8(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(uint8_t, SBData, GetUnsignedInt8,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ uint8_t value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = m_opaque_sp->GetU8(&offset);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+uint16_t SBData::GetUnsignedInt16(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(uint16_t, SBData, GetUnsignedInt16,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ uint16_t value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = m_opaque_sp->GetU16(&offset);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+uint32_t SBData::GetUnsignedInt32(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(uint32_t, SBData, GetUnsignedInt32,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ uint32_t value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = m_opaque_sp->GetU32(&offset);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+uint64_t SBData::GetUnsignedInt64(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(uint64_t, SBData, GetUnsignedInt64,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ uint64_t value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = m_opaque_sp->GetU64(&offset);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+int8_t SBData::GetSignedInt8(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(int8_t, SBData, GetSignedInt8,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ int8_t value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = (int8_t)m_opaque_sp->GetMaxS64(&offset, 1);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+int16_t SBData::GetSignedInt16(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(int16_t, SBData, GetSignedInt16,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ int16_t value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = (int16_t)m_opaque_sp->GetMaxS64(&offset, 2);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+int32_t SBData::GetSignedInt32(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(int32_t, SBData, GetSignedInt32,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ int32_t value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = (int32_t)m_opaque_sp->GetMaxS64(&offset, 4);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+int64_t SBData::GetSignedInt64(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(int64_t, SBData, GetSignedInt64,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ int64_t value = 0;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = (int64_t)m_opaque_sp->GetMaxS64(&offset, 8);
+ if (offset == old_offset)
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+const char *SBData::GetString(lldb::SBError &error, lldb::offset_t offset) {
+ LLDB_RECORD_METHOD(const char *, SBData, GetString,
+ (lldb::SBError &, lldb::offset_t), error, offset);
+
+ const char *value = nullptr;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ value = m_opaque_sp->GetCStr(&offset);
+ if (offset == old_offset || (value == nullptr))
+ error.SetErrorString("unable to read data");
+ }
+ return value;
+}
+
+bool SBData::GetDescription(lldb::SBStream &description,
+ lldb::addr_t base_addr) {
+ LLDB_RECORD_METHOD(bool, SBData, GetDescription,
+ (lldb::SBStream &, lldb::addr_t), description, base_addr);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_sp) {
+ DumpDataExtractor(*m_opaque_sp, &strm, 0, lldb::eFormatBytesWithASCII, 1,
+ m_opaque_sp->GetByteSize(), 16, base_addr, 0, 0);
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+size_t SBData::ReadRawData(lldb::SBError &error, lldb::offset_t offset,
+ void *buf, size_t size) {
+ LLDB_RECORD_DUMMY(size_t, SBData, ReadRawData,
+ (lldb::SBError &, lldb::offset_t, void *, size_t), error,
+ offset, buf, size);
+
+ void *ok = nullptr;
+ if (!m_opaque_sp.get()) {
+ error.SetErrorString("no value to read from");
+ } else {
+ uint32_t old_offset = offset;
+ ok = m_opaque_sp->GetU8(&offset, buf, size);
+ if ((offset == old_offset) || (ok == nullptr))
+ error.SetErrorString("unable to read data");
+ }
+ return ok ? size : 0;
+}
+
+void SBData::SetData(lldb::SBError &error, const void *buf, size_t size,
+ lldb::ByteOrder endian, uint8_t addr_size) {
+ LLDB_RECORD_DUMMY(
+ void, SBData, SetData,
+ (lldb::SBError &, const void *, size_t, lldb::ByteOrder, uint8_t), error,
+ buf, size, endian, addr_size);
+
+ if (!m_opaque_sp.get())
+ m_opaque_sp = std::make_shared<DataExtractor>(buf, size, endian, addr_size);
+ else
+ {
+ m_opaque_sp->SetData(buf, size, endian);
+ m_opaque_sp->SetAddressByteSize(addr_size);
+ }
+}
+
+bool SBData::Append(const SBData &rhs) {
+ LLDB_RECORD_METHOD(bool, SBData, Append, (const lldb::SBData &), rhs);
+
+ bool value = false;
+ if (m_opaque_sp.get() && rhs.m_opaque_sp.get())
+ value = m_opaque_sp.get()->Append(*rhs.m_opaque_sp);
+ return value;
+}
+
+lldb::SBData SBData::CreateDataFromCString(lldb::ByteOrder endian,
+ uint32_t addr_byte_size,
+ const char *data) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBData, SBData, CreateDataFromCString,
+ (lldb::ByteOrder, uint32_t, const char *), endian,
+ addr_byte_size, data);
+
+ if (!data || !data[0])
+ return LLDB_RECORD_RESULT(SBData());
+
+ uint32_t data_len = strlen(data);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(data, data_len));
+ lldb::DataExtractorSP data_sp(
+ new DataExtractor(buffer_sp, endian, addr_byte_size));
+
+ SBData ret(data_sp);
+
+ return LLDB_RECORD_RESULT(ret);
+}
+
+lldb::SBData SBData::CreateDataFromUInt64Array(lldb::ByteOrder endian,
+ uint32_t addr_byte_size,
+ uint64_t *array,
+ size_t array_len) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBData, SBData, CreateDataFromUInt64Array,
+ (lldb::ByteOrder, uint32_t, uint64_t *, size_t),
+ endian, addr_byte_size, array, array_len);
+
+ if (!array || array_len == 0)
+ return LLDB_RECORD_RESULT(SBData());
+
+ size_t data_len = array_len * sizeof(uint64_t);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len));
+ lldb::DataExtractorSP data_sp(
+ new DataExtractor(buffer_sp, endian, addr_byte_size));
+
+ SBData ret(data_sp);
+
+ return LLDB_RECORD_RESULT(ret);
+}
+
+lldb::SBData SBData::CreateDataFromUInt32Array(lldb::ByteOrder endian,
+ uint32_t addr_byte_size,
+ uint32_t *array,
+ size_t array_len) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBData, SBData, CreateDataFromUInt32Array,
+ (lldb::ByteOrder, uint32_t, uint32_t *, size_t),
+ endian, addr_byte_size, array, array_len);
+
+ if (!array || array_len == 0)
+ return LLDB_RECORD_RESULT(SBData());
+
+ size_t data_len = array_len * sizeof(uint32_t);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len));
+ lldb::DataExtractorSP data_sp(
+ new DataExtractor(buffer_sp, endian, addr_byte_size));
+
+ SBData ret(data_sp);
+
+ return LLDB_RECORD_RESULT(ret);
+}
+
+lldb::SBData SBData::CreateDataFromSInt64Array(lldb::ByteOrder endian,
+ uint32_t addr_byte_size,
+ int64_t *array,
+ size_t array_len) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBData, SBData, CreateDataFromSInt64Array,
+ (lldb::ByteOrder, uint32_t, int64_t *, size_t),
+ endian, addr_byte_size, array, array_len);
+
+ if (!array || array_len == 0)
+ return LLDB_RECORD_RESULT(SBData());
+
+ size_t data_len = array_len * sizeof(int64_t);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len));
+ lldb::DataExtractorSP data_sp(
+ new DataExtractor(buffer_sp, endian, addr_byte_size));
+
+ SBData ret(data_sp);
+
+ return LLDB_RECORD_RESULT(ret);
+}
+
+lldb::SBData SBData::CreateDataFromSInt32Array(lldb::ByteOrder endian,
+ uint32_t addr_byte_size,
+ int32_t *array,
+ size_t array_len) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBData, SBData, CreateDataFromSInt32Array,
+ (lldb::ByteOrder, uint32_t, int32_t *, size_t),
+ endian, addr_byte_size, array, array_len);
+
+ if (!array || array_len == 0)
+ return LLDB_RECORD_RESULT(SBData());
+
+ size_t data_len = array_len * sizeof(int32_t);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len));
+ lldb::DataExtractorSP data_sp(
+ new DataExtractor(buffer_sp, endian, addr_byte_size));
+
+ SBData ret(data_sp);
+
+ return LLDB_RECORD_RESULT(ret);
+}
+
+lldb::SBData SBData::CreateDataFromDoubleArray(lldb::ByteOrder endian,
+ uint32_t addr_byte_size,
+ double *array,
+ size_t array_len) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBData, SBData, CreateDataFromDoubleArray,
+ (lldb::ByteOrder, uint32_t, double *, size_t),
+ endian, addr_byte_size, array, array_len);
+
+ if (!array || array_len == 0)
+ return LLDB_RECORD_RESULT(SBData());
+
+ size_t data_len = array_len * sizeof(double);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len));
+ lldb::DataExtractorSP data_sp(
+ new DataExtractor(buffer_sp, endian, addr_byte_size));
+
+ SBData ret(data_sp);
+
+ return LLDB_RECORD_RESULT(ret);
+}
+
+bool SBData::SetDataFromCString(const char *data) {
+ LLDB_RECORD_METHOD(bool, SBData, SetDataFromCString, (const char *), data);
+
+
+ if (!data) {
+ return false;
+ }
+
+ size_t data_len = strlen(data);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(data, data_len));
+
+ if (!m_opaque_sp.get())
+ m_opaque_sp = std::make_shared<DataExtractor>(buffer_sp, GetByteOrder(),
+ GetAddressByteSize());
+ else
+ m_opaque_sp->SetData(buffer_sp);
+
+
+ return true;
+}
+
+bool SBData::SetDataFromUInt64Array(uint64_t *array, size_t array_len) {
+ LLDB_RECORD_METHOD(bool, SBData, SetDataFromUInt64Array, (uint64_t *, size_t),
+ array, array_len);
+
+
+ if (!array || array_len == 0) {
+ return false;
+ }
+
+ size_t data_len = array_len * sizeof(uint64_t);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len));
+
+ if (!m_opaque_sp.get())
+ m_opaque_sp = std::make_shared<DataExtractor>(buffer_sp, GetByteOrder(),
+ GetAddressByteSize());
+ else
+ m_opaque_sp->SetData(buffer_sp);
+
+
+ return true;
+}
+
+bool SBData::SetDataFromUInt32Array(uint32_t *array, size_t array_len) {
+ LLDB_RECORD_METHOD(bool, SBData, SetDataFromUInt32Array, (uint32_t *, size_t),
+ array, array_len);
+
+
+ if (!array || array_len == 0) {
+ return false;
+ }
+
+ size_t data_len = array_len * sizeof(uint32_t);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len));
+
+ if (!m_opaque_sp.get())
+ m_opaque_sp = std::make_shared<DataExtractor>(buffer_sp, GetByteOrder(),
+ GetAddressByteSize());
+ else
+ m_opaque_sp->SetData(buffer_sp);
+
+ return true;
+}
+
+bool SBData::SetDataFromSInt64Array(int64_t *array, size_t array_len) {
+ LLDB_RECORD_METHOD(bool, SBData, SetDataFromSInt64Array, (int64_t *, size_t),
+ array, array_len);
+
+
+ if (!array || array_len == 0) {
+ return false;
+ }
+
+ size_t data_len = array_len * sizeof(int64_t);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len));
+
+ if (!m_opaque_sp.get())
+ m_opaque_sp = std::make_shared<DataExtractor>(buffer_sp, GetByteOrder(),
+ GetAddressByteSize());
+ else
+ m_opaque_sp->SetData(buffer_sp);
+
+ return true;
+}
+
+bool SBData::SetDataFromSInt32Array(int32_t *array, size_t array_len) {
+ LLDB_RECORD_METHOD(bool, SBData, SetDataFromSInt32Array, (int32_t *, size_t),
+ array, array_len);
+
+
+ if (!array || array_len == 0) {
+ return false;
+ }
+
+ size_t data_len = array_len * sizeof(int32_t);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len));
+
+ if (!m_opaque_sp.get())
+ m_opaque_sp = std::make_shared<DataExtractor>(buffer_sp, GetByteOrder(),
+ GetAddressByteSize());
+ else
+ m_opaque_sp->SetData(buffer_sp);
+
+ return true;
+}
+
+bool SBData::SetDataFromDoubleArray(double *array, size_t array_len) {
+ LLDB_RECORD_METHOD(bool, SBData, SetDataFromDoubleArray, (double *, size_t),
+ array, array_len);
+
+
+ if (!array || array_len == 0) {
+ return false;
+ }
+
+ size_t data_len = array_len * sizeof(double);
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(array, data_len));
+
+ if (!m_opaque_sp.get())
+ m_opaque_sp = std::make_shared<DataExtractor>(buffer_sp, GetByteOrder(),
+ GetAddressByteSize());
+ else
+ m_opaque_sp->SetData(buffer_sp);
+
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBData>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBData, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBData, (const lldb::SBData &));
+ LLDB_REGISTER_METHOD(const lldb::SBData &,
+ SBData, operator=,(const lldb::SBData &));
+ LLDB_REGISTER_METHOD(bool, SBData, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBData, operator bool, ());
+ LLDB_REGISTER_METHOD(uint8_t, SBData, GetAddressByteSize, ());
+ LLDB_REGISTER_METHOD(void, SBData, SetAddressByteSize, (uint8_t));
+ LLDB_REGISTER_METHOD(void, SBData, Clear, ());
+ LLDB_REGISTER_METHOD(size_t, SBData, GetByteSize, ());
+ LLDB_REGISTER_METHOD(lldb::ByteOrder, SBData, GetByteOrder, ());
+ LLDB_REGISTER_METHOD(void, SBData, SetByteOrder, (lldb::ByteOrder));
+ LLDB_REGISTER_METHOD(float, SBData, GetFloat,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(double, SBData, GetDouble,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(long double, SBData, GetLongDouble,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBData, GetAddress,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(uint8_t, SBData, GetUnsignedInt8,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(uint16_t, SBData, GetUnsignedInt16,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBData, GetUnsignedInt32,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(uint64_t, SBData, GetUnsignedInt64,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(int8_t, SBData, GetSignedInt8,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(int16_t, SBData, GetSignedInt16,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(int32_t, SBData, GetSignedInt32,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(int64_t, SBData, GetSignedInt64,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(const char *, SBData, GetString,
+ (lldb::SBError &, lldb::offset_t));
+ LLDB_REGISTER_METHOD(bool, SBData, GetDescription,
+ (lldb::SBStream &, lldb::addr_t));
+ LLDB_REGISTER_METHOD(bool, SBData, Append, (const lldb::SBData &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBData, SBData, CreateDataFromCString,
+ (lldb::ByteOrder, uint32_t, const char *));
+ LLDB_REGISTER_STATIC_METHOD(
+ lldb::SBData, SBData, CreateDataFromUInt64Array,
+ (lldb::ByteOrder, uint32_t, uint64_t *, size_t));
+ LLDB_REGISTER_STATIC_METHOD(
+ lldb::SBData, SBData, CreateDataFromUInt32Array,
+ (lldb::ByteOrder, uint32_t, uint32_t *, size_t));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBData, SBData, CreateDataFromSInt64Array,
+ (lldb::ByteOrder, uint32_t, int64_t *, size_t));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBData, SBData, CreateDataFromSInt32Array,
+ (lldb::ByteOrder, uint32_t, int32_t *, size_t));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBData, SBData, CreateDataFromDoubleArray,
+ (lldb::ByteOrder, uint32_t, double *, size_t));
+ LLDB_REGISTER_METHOD(bool, SBData, SetDataFromCString, (const char *));
+ LLDB_REGISTER_METHOD(bool, SBData, SetDataFromUInt64Array,
+ (uint64_t *, size_t));
+ LLDB_REGISTER_METHOD(bool, SBData, SetDataFromUInt32Array,
+ (uint32_t *, size_t));
+ LLDB_REGISTER_METHOD(bool, SBData, SetDataFromSInt64Array,
+ (int64_t *, size_t));
+ LLDB_REGISTER_METHOD(bool, SBData, SetDataFromSInt32Array,
+ (int32_t *, size_t));
+ LLDB_REGISTER_METHOD(bool, SBData, SetDataFromDoubleArray,
+ (double *, size_t));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBDebugger.cpp b/contrib/llvm-project/lldb/source/API/SBDebugger.cpp
new file mode 100644
index 000000000000..634c4a929595
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBDebugger.cpp
@@ -0,0 +1,1730 @@
+//===-- SBDebugger.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SBReproducerPrivate.h"
+#include "SystemInitializerFull.h"
+
+#include "lldb/API/SBDebugger.h"
+
+#include "lldb/lldb-private.h"
+
+#include "lldb/API/SBBroadcaster.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBListener.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBSourceManager.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+#include "lldb/API/SBStructuredData.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/API/SBThread.h"
+#include "lldb/API/SBTypeCategory.h"
+#include "lldb/API/SBTypeFilter.h"
+#include "lldb/API/SBTypeFormat.h"
+#include "lldb/API/SBTypeNameSpecifier.h"
+#include "lldb/API/SBTypeSummary.h"
+#include "lldb/API/SBTypeSynthetic.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Host/XML.h"
+#include "lldb/Initialization/SystemLifetimeManager.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupPlatform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/TargetList.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/State.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/ManagedStatic.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+/// Helper class for replaying commands through the reproducer.
+class CommandLoader {
+public:
+ CommandLoader(std::vector<std::string> files) : m_files(files) {}
+
+ static std::unique_ptr<CommandLoader> Create() {
+ repro::Loader *loader = repro::Reproducer::Instance().GetLoader();
+ if (!loader)
+ return {};
+
+ FileSpec file = loader->GetFile<repro::CommandProvider::Info>();
+ if (!file)
+ return {};
+
+ auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
+ if (auto err = error_or_file.getError())
+ return {};
+
+ std::vector<std::string> files;
+ llvm::yaml::Input yin((*error_or_file)->getBuffer());
+ yin >> files;
+
+ if (auto err = yin.error())
+ return {};
+
+ for (auto &file : files) {
+ FileSpec absolute_path =
+ loader->GetRoot().CopyByAppendingPathComponent(file);
+ file = absolute_path.GetPath();
+ }
+
+ return llvm::make_unique<CommandLoader>(std::move(files));
+ }
+
+ FILE *GetNextFile() {
+ if (m_index >= m_files.size())
+ return nullptr;
+ return FileSystem::Instance().Fopen(m_files[m_index++].c_str(), "r");
+ }
+
+private:
+ std::vector<std::string> m_files;
+ unsigned m_index = 0;
+};
+
+static llvm::sys::DynamicLibrary LoadPlugin(const lldb::DebuggerSP &debugger_sp,
+ const FileSpec &spec,
+ Status &error) {
+ llvm::sys::DynamicLibrary dynlib =
+ llvm::sys::DynamicLibrary::getPermanentLibrary(spec.GetPath().c_str());
+ if (dynlib.isValid()) {
+ typedef bool (*LLDBCommandPluginInit)(lldb::SBDebugger & debugger);
+
+ lldb::SBDebugger debugger_sb(debugger_sp);
+ // This calls the bool lldb::PluginInitialize(lldb::SBDebugger debugger)
+ // function.
+ // TODO: mangle this differently for your system - on OSX, the first
+ // underscore needs to be removed and the second one stays
+ LLDBCommandPluginInit init_func =
+ (LLDBCommandPluginInit)(uintptr_t)dynlib.getAddressOfSymbol(
+ "_ZN4lldb16PluginInitializeENS_10SBDebuggerE");
+ if (init_func) {
+ if (init_func(debugger_sb))
+ return dynlib;
+ else
+ error.SetErrorString("plug-in refused to load "
+ "(lldb::PluginInitialize(lldb::SBDebugger) "
+ "returned false)");
+ } else {
+ error.SetErrorString("plug-in is missing the required initialization: "
+ "lldb::PluginInitialize(lldb::SBDebugger)");
+ }
+ } else {
+ if (FileSystem::Instance().Exists(spec))
+ error.SetErrorString("this file does not represent a loadable dylib");
+ else
+ error.SetErrorString("no such file");
+ }
+ return llvm::sys::DynamicLibrary();
+}
+
+static llvm::ManagedStatic<SystemLifetimeManager> g_debugger_lifetime;
+
+SBError SBInputReader::Initialize(
+ lldb::SBDebugger &sb_debugger,
+ unsigned long (*callback)(void *, lldb::SBInputReader *,
+ lldb::InputReaderAction, char const *,
+ unsigned long),
+ void *a, lldb::InputReaderGranularity b, char const *c, char const *d,
+ bool e) {
+ LLDB_RECORD_DUMMY(
+ lldb::SBError, SBInputReader, Initialize,
+ (lldb::SBDebugger &,
+ unsigned long (*)(void *, lldb::SBInputReader *, lldb::InputReaderAction,
+ const char *, unsigned long),
+ void *, lldb::InputReaderGranularity, const char *, const char *, bool),
+ sb_debugger, callback, a, b, c, d, e);
+
+ return SBError();
+}
+
+void SBInputReader::SetIsDone(bool b) {
+ LLDB_RECORD_METHOD(void, SBInputReader, SetIsDone, (bool), b);
+}
+
+bool SBInputReader::IsActive() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBInputReader, IsActive);
+
+ return false;
+}
+
+SBDebugger::SBDebugger() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBDebugger); }
+
+SBDebugger::SBDebugger(const lldb::DebuggerSP &debugger_sp)
+ : m_opaque_sp(debugger_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBDebugger, (const lldb::DebuggerSP &), debugger_sp);
+}
+
+SBDebugger::SBDebugger(const SBDebugger &rhs) : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBDebugger, (const lldb::SBDebugger &), rhs);
+}
+
+SBDebugger::~SBDebugger() = default;
+
+SBDebugger &SBDebugger::operator=(const SBDebugger &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBDebugger &,
+ SBDebugger, operator=,(const lldb::SBDebugger &), rhs);
+
+ if (this != &rhs) {
+ m_opaque_sp = rhs.m_opaque_sp;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+void SBDebugger::Initialize() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(void, SBDebugger, Initialize);
+ SBError ignored = SBDebugger::InitializeWithErrorHandling();
+}
+
+lldb::SBError SBDebugger::InitializeWithErrorHandling() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(lldb::SBError, SBDebugger,
+ InitializeWithErrorHandling);
+
+
+
+ SBError error;
+ if (auto e = g_debugger_lifetime->Initialize(
+ llvm::make_unique<SystemInitializerFull>(), LoadPlugin)) {
+ error.SetError(Status(std::move(e)));
+ }
+ return LLDB_RECORD_RESULT(error);
+}
+
+void SBDebugger::Terminate() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(void, SBDebugger, Terminate);
+
+ g_debugger_lifetime->Terminate();
+}
+
+void SBDebugger::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBDebugger, Clear);
+
+
+ if (m_opaque_sp)
+ m_opaque_sp->ClearIOHandlers();
+
+ m_opaque_sp.reset();
+}
+
+SBDebugger SBDebugger::Create() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(lldb::SBDebugger, SBDebugger, Create);
+
+ return LLDB_RECORD_RESULT(SBDebugger::Create(false, nullptr, nullptr));
+}
+
+SBDebugger SBDebugger::Create(bool source_init_files) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBDebugger, SBDebugger, Create, (bool),
+ source_init_files);
+
+ return LLDB_RECORD_RESULT(
+ SBDebugger::Create(source_init_files, nullptr, nullptr));
+}
+
+SBDebugger SBDebugger::Create(bool source_init_files,
+ lldb::LogOutputCallback callback, void *baton)
+
+{
+ LLDB_RECORD_DUMMY(lldb::SBDebugger, SBDebugger, Create,
+ (bool, lldb::LogOutputCallback, void *), source_init_files,
+ callback, baton);
+
+ SBDebugger debugger;
+
+ // Currently we have issues if this function is called simultaneously on two
+ // different threads. The issues mainly revolve around the fact that the
+ // lldb_private::FormatManager uses global collections and having two threads
+ // parsing the .lldbinit files can cause mayhem. So to get around this for
+ // now we need to use a mutex to prevent bad things from happening.
+ static std::recursive_mutex g_mutex;
+ std::lock_guard<std::recursive_mutex> guard(g_mutex);
+
+ debugger.reset(Debugger::CreateInstance(callback, baton));
+
+
+ SBCommandInterpreter interp = debugger.GetCommandInterpreter();
+ if (source_init_files) {
+ interp.get()->SkipLLDBInitFiles(false);
+ interp.get()->SkipAppInitFiles(false);
+ SBCommandReturnObject result;
+ interp.SourceInitFileInHomeDirectory(result);
+ } else {
+ interp.get()->SkipLLDBInitFiles(true);
+ interp.get()->SkipAppInitFiles(true);
+ }
+ return debugger;
+}
+
+void SBDebugger::Destroy(SBDebugger &debugger) {
+ LLDB_RECORD_STATIC_METHOD(void, SBDebugger, Destroy, (lldb::SBDebugger &),
+ debugger);
+
+
+ Debugger::Destroy(debugger.m_opaque_sp);
+
+ if (debugger.m_opaque_sp.get() != nullptr)
+ debugger.m_opaque_sp.reset();
+}
+
+void SBDebugger::MemoryPressureDetected() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(void, SBDebugger, MemoryPressureDetected);
+
+ // Since this function can be call asynchronously, we allow it to be non-
+ // mandatory. We have seen deadlocks with this function when called so we
+ // need to safeguard against this until we can determine what is causing the
+ // deadlocks.
+
+ const bool mandatory = false;
+
+ ModuleList::RemoveOrphanSharedModules(mandatory);
+}
+
+bool SBDebugger::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBDebugger, IsValid);
+ return this->operator bool();
+}
+SBDebugger::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBDebugger, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+void SBDebugger::SetAsync(bool b) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SetAsync, (bool), b);
+
+ if (m_opaque_sp)
+ m_opaque_sp->SetAsyncExecution(b);
+}
+
+bool SBDebugger::GetAsync() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBDebugger, GetAsync);
+
+ return (m_opaque_sp ? m_opaque_sp->GetAsyncExecution() : false);
+}
+
+void SBDebugger::SkipLLDBInitFiles(bool b) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SkipLLDBInitFiles, (bool), b);
+
+ if (m_opaque_sp)
+ m_opaque_sp->GetCommandInterpreter().SkipLLDBInitFiles(b);
+}
+
+void SBDebugger::SkipAppInitFiles(bool b) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SkipAppInitFiles, (bool), b);
+
+ if (m_opaque_sp)
+ m_opaque_sp->GetCommandInterpreter().SkipAppInitFiles(b);
+}
+
+// Shouldn't really be settable after initialization as this could cause lots
+// of problems; don't want users trying to switch modes in the middle of a
+// debugging session.
+void SBDebugger::SetInputFileHandle(FILE *fh, bool transfer_ownership) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SetInputFileHandle, (FILE *, bool), fh,
+ transfer_ownership);
+
+ if (!m_opaque_sp)
+ return;
+
+ repro::DataRecorder *recorder = nullptr;
+ if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator())
+ recorder = g->GetOrCreate<repro::CommandProvider>().GetNewDataRecorder();
+
+ static std::unique_ptr<CommandLoader> loader = CommandLoader::Create();
+ if (loader)
+ fh = loader->GetNextFile();
+
+ m_opaque_sp->SetInputFileHandle(fh, transfer_ownership, recorder);
+}
+
+void SBDebugger::SetOutputFileHandle(FILE *fh, bool transfer_ownership) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SetOutputFileHandle, (FILE *, bool), fh,
+ transfer_ownership);
+
+ if (m_opaque_sp)
+ m_opaque_sp->SetOutputFileHandle(fh, transfer_ownership);
+}
+
+void SBDebugger::SetErrorFileHandle(FILE *fh, bool transfer_ownership) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SetErrorFileHandle, (FILE *, bool), fh,
+ transfer_ownership);
+
+
+ if (m_opaque_sp)
+ m_opaque_sp->SetErrorFileHandle(fh, transfer_ownership);
+}
+
+FILE *SBDebugger::GetInputFileHandle() {
+ LLDB_RECORD_METHOD_NO_ARGS(FILE *, SBDebugger, GetInputFileHandle);
+
+ if (m_opaque_sp) {
+ StreamFileSP stream_file_sp(m_opaque_sp->GetInputFile());
+ if (stream_file_sp)
+ return LLDB_RECORD_RESULT(stream_file_sp->GetFile().GetStream());
+ }
+ return nullptr;
+}
+
+FILE *SBDebugger::GetOutputFileHandle() {
+ LLDB_RECORD_METHOD_NO_ARGS(FILE *, SBDebugger, GetOutputFileHandle);
+
+ if (m_opaque_sp) {
+ StreamFileSP stream_file_sp(m_opaque_sp->GetOutputFile());
+ if (stream_file_sp)
+ return LLDB_RECORD_RESULT(stream_file_sp->GetFile().GetStream());
+ }
+ return nullptr;
+}
+
+FILE *SBDebugger::GetErrorFileHandle() {
+ LLDB_RECORD_METHOD_NO_ARGS(FILE *, SBDebugger, GetErrorFileHandle);
+
+ if (m_opaque_sp) {
+ StreamFileSP stream_file_sp(m_opaque_sp->GetErrorFile());
+ if (stream_file_sp)
+ return LLDB_RECORD_RESULT(stream_file_sp->GetFile().GetStream());
+ }
+ return nullptr;
+}
+
+void SBDebugger::SaveInputTerminalState() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBDebugger, SaveInputTerminalState);
+
+ if (m_opaque_sp)
+ m_opaque_sp->SaveInputTerminalState();
+}
+
+void SBDebugger::RestoreInputTerminalState() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBDebugger, RestoreInputTerminalState);
+
+ if (m_opaque_sp)
+ m_opaque_sp->RestoreInputTerminalState();
+}
+SBCommandInterpreter SBDebugger::GetCommandInterpreter() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBCommandInterpreter, SBDebugger,
+ GetCommandInterpreter);
+
+
+ SBCommandInterpreter sb_interpreter;
+ if (m_opaque_sp)
+ sb_interpreter.reset(&m_opaque_sp->GetCommandInterpreter());
+
+
+ return LLDB_RECORD_RESULT(sb_interpreter);
+}
+
+void SBDebugger::HandleCommand(const char *command) {
+ LLDB_RECORD_METHOD(void, SBDebugger, HandleCommand, (const char *), command);
+
+ if (m_opaque_sp) {
+ TargetSP target_sp(m_opaque_sp->GetSelectedTarget());
+ std::unique_lock<std::recursive_mutex> lock;
+ if (target_sp)
+ lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+
+ SBCommandInterpreter sb_interpreter(GetCommandInterpreter());
+ SBCommandReturnObject result;
+
+ sb_interpreter.HandleCommand(command, result, false);
+
+ if (GetErrorFileHandle() != nullptr)
+ result.PutError(GetErrorFileHandle());
+ if (GetOutputFileHandle() != nullptr)
+ result.PutOutput(GetOutputFileHandle());
+
+ if (!m_opaque_sp->GetAsyncExecution()) {
+ SBProcess process(GetCommandInterpreter().GetProcess());
+ ProcessSP process_sp(process.GetSP());
+ if (process_sp) {
+ EventSP event_sp;
+ ListenerSP lldb_listener_sp = m_opaque_sp->GetListener();
+ while (lldb_listener_sp->GetEventForBroadcaster(
+ process_sp.get(), event_sp, std::chrono::seconds(0))) {
+ SBEvent event(event_sp);
+ HandleProcessEvent(process, event, GetOutputFileHandle(),
+ GetErrorFileHandle());
+ }
+ }
+ }
+ }
+}
+
+SBListener SBDebugger::GetListener() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBListener, SBDebugger, GetListener);
+
+
+ SBListener sb_listener;
+ if (m_opaque_sp)
+ sb_listener.reset(m_opaque_sp->GetListener());
+
+
+ return LLDB_RECORD_RESULT(sb_listener);
+}
+
+void SBDebugger::HandleProcessEvent(const SBProcess &process,
+ const SBEvent &event, FILE *out,
+ FILE *err) {
+ LLDB_RECORD_METHOD(
+ void, SBDebugger, HandleProcessEvent,
+ (const lldb::SBProcess &, const lldb::SBEvent &, FILE *, FILE *), process,
+ event, out, err);
+
+ if (!process.IsValid())
+ return;
+
+ TargetSP target_sp(process.GetTarget().GetSP());
+ if (!target_sp)
+ return;
+
+ const uint32_t event_type = event.GetType();
+ char stdio_buffer[1024];
+ size_t len;
+
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+
+ if (event_type &
+ (Process::eBroadcastBitSTDOUT | Process::eBroadcastBitStateChanged)) {
+ // Drain stdout when we stop just in case we have any bytes
+ while ((len = process.GetSTDOUT(stdio_buffer, sizeof(stdio_buffer))) > 0)
+ if (out != nullptr)
+ ::fwrite(stdio_buffer, 1, len, out);
+ }
+
+ if (event_type &
+ (Process::eBroadcastBitSTDERR | Process::eBroadcastBitStateChanged)) {
+ // Drain stderr when we stop just in case we have any bytes
+ while ((len = process.GetSTDERR(stdio_buffer, sizeof(stdio_buffer))) > 0)
+ if (err != nullptr)
+ ::fwrite(stdio_buffer, 1, len, err);
+ }
+
+ if (event_type & Process::eBroadcastBitStateChanged) {
+ StateType event_state = SBProcess::GetStateFromEvent(event);
+
+ if (event_state == eStateInvalid)
+ return;
+
+ bool is_stopped = StateIsStoppedState(event_state);
+ if (!is_stopped)
+ process.ReportEventState(event, out);
+ }
+}
+
+SBSourceManager SBDebugger::GetSourceManager() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBSourceManager, SBDebugger,
+ GetSourceManager);
+
+ SBSourceManager sb_source_manager(*this);
+ return LLDB_RECORD_RESULT(sb_source_manager);
+}
+
+bool SBDebugger::GetDefaultArchitecture(char *arch_name, size_t arch_name_len) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBDebugger, GetDefaultArchitecture,
+ (char *, size_t), "", arch_name_len);
+
+ if (arch_name && arch_name_len) {
+ ArchSpec default_arch = Target::GetDefaultArchitecture();
+
+ if (default_arch.IsValid()) {
+ const std::string &triple_str = default_arch.GetTriple().str();
+ if (!triple_str.empty())
+ ::snprintf(arch_name, arch_name_len, "%s", triple_str.c_str());
+ else
+ ::snprintf(arch_name, arch_name_len, "%s",
+ default_arch.GetArchitectureName());
+ return true;
+ }
+ }
+ if (arch_name && arch_name_len)
+ arch_name[0] = '\0';
+ return false;
+}
+
+bool SBDebugger::SetDefaultArchitecture(const char *arch_name) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBDebugger, SetDefaultArchitecture,
+ (const char *), arch_name);
+
+ if (arch_name) {
+ ArchSpec arch(arch_name);
+ if (arch.IsValid()) {
+ Target::SetDefaultArchitecture(arch);
+ return true;
+ }
+ }
+ return false;
+}
+
+ScriptLanguage
+SBDebugger::GetScriptingLanguage(const char *script_language_name) {
+ LLDB_RECORD_METHOD(lldb::ScriptLanguage, SBDebugger, GetScriptingLanguage,
+ (const char *), script_language_name);
+
+ if (!script_language_name) return eScriptLanguageDefault;
+ return OptionArgParser::ToScriptLanguage(
+ llvm::StringRef(script_language_name), eScriptLanguageDefault, nullptr);
+}
+
+const char *SBDebugger::GetVersionString() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(const char *, SBDebugger, GetVersionString);
+
+ return lldb_private::GetVersion();
+}
+
+const char *SBDebugger::StateAsCString(StateType state) {
+ LLDB_RECORD_STATIC_METHOD(const char *, SBDebugger, StateAsCString,
+ (lldb::StateType), state);
+
+ return lldb_private::StateAsCString(state);
+}
+
+static void AddBoolConfigEntry(StructuredData::Dictionary &dict,
+ llvm::StringRef name, bool value,
+ llvm::StringRef description) {
+ auto entry_up = llvm::make_unique<StructuredData::Dictionary>();
+ entry_up->AddBooleanItem("value", value);
+ entry_up->AddStringItem("description", description);
+ dict.AddItem(name, std::move(entry_up));
+}
+
+static void AddLLVMTargets(StructuredData::Dictionary &dict) {
+ auto array_up = llvm::make_unique<StructuredData::Array>();
+#define LLVM_TARGET(target) \
+ array_up->AddItem(llvm::make_unique<StructuredData::String>(#target));
+#include "llvm/Config/Targets.def"
+ auto entry_up = llvm::make_unique<StructuredData::Dictionary>();
+ entry_up->AddItem("value", std::move(array_up));
+ entry_up->AddStringItem("description", "A list of configured LLVM targets.");
+ dict.AddItem("targets", std::move(entry_up));
+}
+
+SBStructuredData SBDebugger::GetBuildConfiguration() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(lldb::SBStructuredData, SBDebugger,
+ GetBuildConfiguration);
+
+ auto config_up = llvm::make_unique<StructuredData::Dictionary>();
+ AddBoolConfigEntry(
+ *config_up, "xml", XMLDocument::XMLEnabled(),
+ "A boolean value that indicates if XML support is enabled in LLDB");
+ AddLLVMTargets(*config_up);
+
+ SBStructuredData data;
+ data.m_impl_up->SetObjectSP(std::move(config_up));
+ return LLDB_RECORD_RESULT(data);
+}
+
+bool SBDebugger::StateIsRunningState(StateType state) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBDebugger, StateIsRunningState,
+ (lldb::StateType), state);
+
+
+ const bool result = lldb_private::StateIsRunningState(state);
+
+ return result;
+}
+
+bool SBDebugger::StateIsStoppedState(StateType state) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBDebugger, StateIsStoppedState,
+ (lldb::StateType), state);
+
+
+ const bool result = lldb_private::StateIsStoppedState(state, false);
+
+ return result;
+}
+
+lldb::SBTarget SBDebugger::CreateTarget(const char *filename,
+ const char *target_triple,
+ const char *platform_name,
+ bool add_dependent_modules,
+ lldb::SBError &sb_error) {
+ LLDB_RECORD_METHOD(
+ lldb::SBTarget, SBDebugger, CreateTarget,
+ (const char *, const char *, const char *, bool, lldb::SBError &),
+ filename, target_triple, platform_name, add_dependent_modules, sb_error);
+
+ SBTarget sb_target;
+ TargetSP target_sp;
+ if (m_opaque_sp) {
+ sb_error.Clear();
+ OptionGroupPlatform platform_options(false);
+ platform_options.SetPlatformName(platform_name);
+
+ sb_error.ref() = m_opaque_sp->GetTargetList().CreateTarget(
+ *m_opaque_sp, filename, target_triple,
+ add_dependent_modules ? eLoadDependentsYes : eLoadDependentsNo,
+ &platform_options, target_sp);
+
+ if (sb_error.Success())
+ sb_target.SetSP(target_sp);
+ } else {
+ sb_error.SetErrorString("invalid debugger");
+ }
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+ if (log)
+ log->Printf("SBDebugger(%p)::CreateTarget (filename=\"%s\", triple=%s, "
+ "platform_name=%s, add_dependent_modules=%u, error=%s) => "
+ "SBTarget(%p)",
+ static_cast<void *>(m_opaque_sp.get()), filename, target_triple,
+ platform_name, add_dependent_modules, sb_error.GetCString(),
+ static_cast<void *>(target_sp.get()));
+
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+SBTarget
+SBDebugger::CreateTargetWithFileAndTargetTriple(const char *filename,
+ const char *target_triple) {
+ LLDB_RECORD_METHOD(lldb::SBTarget, SBDebugger,
+ CreateTargetWithFileAndTargetTriple,
+ (const char *, const char *), filename, target_triple);
+
+ SBTarget sb_target;
+ TargetSP target_sp;
+ if (m_opaque_sp) {
+ const bool add_dependent_modules = true;
+ Status error(m_opaque_sp->GetTargetList().CreateTarget(
+ *m_opaque_sp, filename, target_triple,
+ add_dependent_modules ? eLoadDependentsYes : eLoadDependentsNo, nullptr,
+ target_sp));
+ sb_target.SetSP(target_sp);
+ }
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+ if (log)
+ log->Printf("SBDebugger(%p)::CreateTargetWithFileAndTargetTriple "
+ "(filename=\"%s\", triple=%s) => SBTarget(%p)",
+ static_cast<void *>(m_opaque_sp.get()), filename, target_triple,
+ static_cast<void *>(target_sp.get()));
+
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+SBTarget SBDebugger::CreateTargetWithFileAndArch(const char *filename,
+ const char *arch_cstr) {
+ LLDB_RECORD_METHOD(lldb::SBTarget, SBDebugger, CreateTargetWithFileAndArch,
+ (const char *, const char *), filename, arch_cstr);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+
+ SBTarget sb_target;
+ TargetSP target_sp;
+ if (m_opaque_sp) {
+ Status error;
+ const bool add_dependent_modules = true;
+
+ error = m_opaque_sp->GetTargetList().CreateTarget(
+ *m_opaque_sp, filename, arch_cstr,
+ add_dependent_modules ? eLoadDependentsYes : eLoadDependentsNo, nullptr,
+ target_sp);
+
+ if (error.Success()) {
+ m_opaque_sp->GetTargetList().SetSelectedTarget(target_sp.get());
+ sb_target.SetSP(target_sp);
+ }
+ }
+
+ if (log)
+ log->Printf("SBDebugger(%p)::CreateTargetWithFileAndArch (filename=\"%s\", "
+ "arch=%s) => SBTarget(%p)",
+ static_cast<void *>(m_opaque_sp.get()), filename, arch_cstr,
+ static_cast<void *>(target_sp.get()));
+
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+SBTarget SBDebugger::CreateTarget(const char *filename) {
+ LLDB_RECORD_METHOD(lldb::SBTarget, SBDebugger, CreateTarget, (const char *),
+ filename);
+
+ SBTarget sb_target;
+ TargetSP target_sp;
+ if (m_opaque_sp) {
+ Status error;
+ const bool add_dependent_modules = true;
+ error = m_opaque_sp->GetTargetList().CreateTarget(
+ *m_opaque_sp, filename, "",
+ add_dependent_modules ? eLoadDependentsYes : eLoadDependentsNo, nullptr,
+ target_sp);
+
+ if (error.Success()) {
+ m_opaque_sp->GetTargetList().SetSelectedTarget(target_sp.get());
+ sb_target.SetSP(target_sp);
+ }
+ }
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+ if (log)
+ log->Printf(
+ "SBDebugger(%p)::CreateTarget (filename=\"%s\") => SBTarget(%p)",
+ static_cast<void *>(m_opaque_sp.get()), filename,
+ static_cast<void *>(target_sp.get()));
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+SBTarget SBDebugger::GetDummyTarget() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBTarget, SBDebugger, GetDummyTarget);
+
+ SBTarget sb_target;
+ if (m_opaque_sp) {
+ sb_target.SetSP(m_opaque_sp->GetDummyTarget()->shared_from_this());
+ }
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+ if (log)
+ log->Printf(
+ "SBDebugger(%p)::GetDummyTarget() => SBTarget(%p)",
+ static_cast<void *>(m_opaque_sp.get()),
+ static_cast<void *>(sb_target.GetSP().get()));
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+bool SBDebugger::DeleteTarget(lldb::SBTarget &target) {
+ LLDB_RECORD_METHOD(bool, SBDebugger, DeleteTarget, (lldb::SBTarget &),
+ target);
+
+ bool result = false;
+ if (m_opaque_sp) {
+ TargetSP target_sp(target.GetSP());
+ if (target_sp) {
+ // No need to lock, the target list is thread safe
+ result = m_opaque_sp->GetTargetList().DeleteTarget(target_sp);
+ target_sp->Destroy();
+ target.Clear();
+ const bool mandatory = true;
+ ModuleList::RemoveOrphanSharedModules(mandatory);
+ }
+ }
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+ if (log)
+ log->Printf("SBDebugger(%p)::DeleteTarget (SBTarget(%p)) => %i",
+ static_cast<void *>(m_opaque_sp.get()),
+ static_cast<void *>(target.m_opaque_sp.get()), result);
+
+ return result;
+}
+
+SBTarget SBDebugger::GetTargetAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBTarget, SBDebugger, GetTargetAtIndex, (uint32_t),
+ idx);
+
+ SBTarget sb_target;
+ if (m_opaque_sp) {
+ // No need to lock, the target list is thread safe
+ sb_target.SetSP(m_opaque_sp->GetTargetList().GetTargetAtIndex(idx));
+ }
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+uint32_t SBDebugger::GetIndexOfTarget(lldb::SBTarget target) {
+ LLDB_RECORD_METHOD(uint32_t, SBDebugger, GetIndexOfTarget, (lldb::SBTarget),
+ target);
+
+ lldb::TargetSP target_sp = target.GetSP();
+ if (!target_sp)
+ return UINT32_MAX;
+
+ if (!m_opaque_sp)
+ return UINT32_MAX;
+
+ return m_opaque_sp->GetTargetList().GetIndexOfTarget(target.GetSP());
+}
+
+SBTarget SBDebugger::FindTargetWithProcessID(lldb::pid_t pid) {
+ LLDB_RECORD_METHOD(lldb::SBTarget, SBDebugger, FindTargetWithProcessID,
+ (lldb::pid_t), pid);
+
+ SBTarget sb_target;
+ if (m_opaque_sp) {
+ // No need to lock, the target list is thread safe
+ sb_target.SetSP(m_opaque_sp->GetTargetList().FindTargetWithProcessID(pid));
+ }
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+SBTarget SBDebugger::FindTargetWithFileAndArch(const char *filename,
+ const char *arch_name) {
+ LLDB_RECORD_METHOD(lldb::SBTarget, SBDebugger, FindTargetWithFileAndArch,
+ (const char *, const char *), filename, arch_name);
+
+ SBTarget sb_target;
+ if (m_opaque_sp && filename && filename[0]) {
+ // No need to lock, the target list is thread safe
+ ArchSpec arch = Platform::GetAugmentedArchSpec(
+ m_opaque_sp->GetPlatformList().GetSelectedPlatform().get(), arch_name);
+ TargetSP target_sp(
+ m_opaque_sp->GetTargetList().FindTargetWithExecutableAndArchitecture(
+ FileSpec(filename), arch_name ? &arch : nullptr));
+ sb_target.SetSP(target_sp);
+ }
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+SBTarget SBDebugger::FindTargetWithLLDBProcess(const ProcessSP &process_sp) {
+ SBTarget sb_target;
+ if (m_opaque_sp) {
+ // No need to lock, the target list is thread safe
+ sb_target.SetSP(
+ m_opaque_sp->GetTargetList().FindTargetWithProcess(process_sp.get()));
+ }
+ return sb_target;
+}
+
+uint32_t SBDebugger::GetNumTargets() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBDebugger, GetNumTargets);
+
+ if (m_opaque_sp) {
+ // No need to lock, the target list is thread safe
+ return m_opaque_sp->GetTargetList().GetNumTargets();
+ }
+ return 0;
+}
+
+SBTarget SBDebugger::GetSelectedTarget() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBTarget, SBDebugger, GetSelectedTarget);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+
+ SBTarget sb_target;
+ TargetSP target_sp;
+ if (m_opaque_sp) {
+ // No need to lock, the target list is thread safe
+ target_sp = m_opaque_sp->GetTargetList().GetSelectedTarget();
+ sb_target.SetSP(target_sp);
+ }
+
+ if (log) {
+ SBStream sstr;
+ sb_target.GetDescription(sstr, eDescriptionLevelBrief);
+ log->Printf("SBDebugger(%p)::GetSelectedTarget () => SBTarget(%p): %s",
+ static_cast<void *>(m_opaque_sp.get()),
+ static_cast<void *>(target_sp.get()), sstr.GetData());
+ }
+
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+void SBDebugger::SetSelectedTarget(SBTarget &sb_target) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SetSelectedTarget, (lldb::SBTarget &),
+ sb_target);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+
+ TargetSP target_sp(sb_target.GetSP());
+ if (m_opaque_sp) {
+ m_opaque_sp->GetTargetList().SetSelectedTarget(target_sp.get());
+ }
+ if (log) {
+ SBStream sstr;
+ sb_target.GetDescription(sstr, eDescriptionLevelBrief);
+ log->Printf("SBDebugger(%p)::SetSelectedTarget () => SBTarget(%p): %s",
+ static_cast<void *>(m_opaque_sp.get()),
+ static_cast<void *>(target_sp.get()), sstr.GetData());
+ }
+}
+
+SBPlatform SBDebugger::GetSelectedPlatform() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBPlatform, SBDebugger, GetSelectedPlatform);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+
+ SBPlatform sb_platform;
+ DebuggerSP debugger_sp(m_opaque_sp);
+ if (debugger_sp) {
+ sb_platform.SetSP(debugger_sp->GetPlatformList().GetSelectedPlatform());
+ }
+ if (log)
+ log->Printf("SBDebugger(%p)::GetSelectedPlatform () => SBPlatform(%p): %s",
+ static_cast<void *>(m_opaque_sp.get()),
+ static_cast<void *>(sb_platform.GetSP().get()),
+ sb_platform.GetName());
+ return LLDB_RECORD_RESULT(sb_platform);
+}
+
+void SBDebugger::SetSelectedPlatform(SBPlatform &sb_platform) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SetSelectedPlatform,
+ (lldb::SBPlatform &), sb_platform);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+
+ DebuggerSP debugger_sp(m_opaque_sp);
+ if (debugger_sp) {
+ debugger_sp->GetPlatformList().SetSelectedPlatform(sb_platform.GetSP());
+ }
+
+ if (log)
+ log->Printf("SBDebugger(%p)::SetSelectedPlatform (SBPlatform(%p) %s)",
+ static_cast<void *>(m_opaque_sp.get()),
+ static_cast<void *>(sb_platform.GetSP().get()),
+ sb_platform.GetName());
+}
+
+uint32_t SBDebugger::GetNumPlatforms() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBDebugger, GetNumPlatforms);
+
+ if (m_opaque_sp) {
+ // No need to lock, the platform list is thread safe
+ return m_opaque_sp->GetPlatformList().GetSize();
+ }
+ return 0;
+}
+
+SBPlatform SBDebugger::GetPlatformAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBPlatform, SBDebugger, GetPlatformAtIndex,
+ (uint32_t), idx);
+
+ SBPlatform sb_platform;
+ if (m_opaque_sp) {
+ // No need to lock, the platform list is thread safe
+ sb_platform.SetSP(m_opaque_sp->GetPlatformList().GetAtIndex(idx));
+ }
+ return LLDB_RECORD_RESULT(sb_platform);
+}
+
+uint32_t SBDebugger::GetNumAvailablePlatforms() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBDebugger, GetNumAvailablePlatforms);
+
+ uint32_t idx = 0;
+ while (true) {
+ if (!PluginManager::GetPlatformPluginNameAtIndex(idx)) {
+ break;
+ }
+ ++idx;
+ }
+ // +1 for the host platform, which should always appear first in the list.
+ return idx + 1;
+}
+
+SBStructuredData SBDebugger::GetAvailablePlatformInfoAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBStructuredData, SBDebugger,
+ GetAvailablePlatformInfoAtIndex, (uint32_t), idx);
+
+ SBStructuredData data;
+ auto platform_dict = llvm::make_unique<StructuredData::Dictionary>();
+ llvm::StringRef name_str("name"), desc_str("description");
+
+ if (idx == 0) {
+ PlatformSP host_platform_sp(Platform::GetHostPlatform());
+ platform_dict->AddStringItem(
+ name_str, host_platform_sp->GetPluginName().GetStringRef());
+ platform_dict->AddStringItem(
+ desc_str, llvm::StringRef(host_platform_sp->GetDescription()));
+ } else if (idx > 0) {
+ const char *plugin_name =
+ PluginManager::GetPlatformPluginNameAtIndex(idx - 1);
+ if (!plugin_name) {
+ return LLDB_RECORD_RESULT(data);
+ }
+ platform_dict->AddStringItem(name_str, llvm::StringRef(plugin_name));
+
+ const char *plugin_desc =
+ PluginManager::GetPlatformPluginDescriptionAtIndex(idx - 1);
+ if (!plugin_desc) {
+ return LLDB_RECORD_RESULT(data);
+ }
+ platform_dict->AddStringItem(desc_str, llvm::StringRef(plugin_desc));
+ }
+
+ data.m_impl_up->SetObjectSP(
+ StructuredData::ObjectSP(platform_dict.release()));
+ return LLDB_RECORD_RESULT(data);
+}
+
+void SBDebugger::DispatchInput(void *baton, const void *data, size_t data_len) {
+ LLDB_RECORD_DUMMY(void, SBDebugger, DispatchInput,
+ (void *, const void *, size_t), baton, data, data_len);
+
+ DispatchInput(data, data_len);
+}
+
+void SBDebugger::DispatchInput(const void *data, size_t data_len) {
+ LLDB_RECORD_DUMMY(void, SBDebugger, DispatchInput, (const void *, size_t),
+ data, data_len);
+
+ // Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
+ //
+ // if (log)
+ // log->Printf ("SBDebugger(%p)::DispatchInput (data=\"%.*s\",
+ // size_t=%" PRIu64 ")",
+ // m_opaque_sp.get(),
+ // (int) data_len,
+ // (const char *) data,
+ // (uint64_t)data_len);
+ //
+ // if (m_opaque_sp)
+ // m_opaque_sp->DispatchInput ((const char *) data, data_len);
+}
+
+void SBDebugger::DispatchInputInterrupt() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBDebugger, DispatchInputInterrupt);
+
+ if (m_opaque_sp)
+ m_opaque_sp->DispatchInputInterrupt();
+}
+
+void SBDebugger::DispatchInputEndOfFile() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBDebugger, DispatchInputEndOfFile);
+
+ if (m_opaque_sp)
+ m_opaque_sp->DispatchInputEndOfFile();
+}
+
+void SBDebugger::PushInputReader(SBInputReader &reader) {
+ LLDB_RECORD_METHOD(void, SBDebugger, PushInputReader, (lldb::SBInputReader &),
+ reader);
+}
+
+void SBDebugger::RunCommandInterpreter(bool auto_handle_events,
+ bool spawn_thread) {
+ LLDB_RECORD_METHOD(void, SBDebugger, RunCommandInterpreter, (bool, bool),
+ auto_handle_events, spawn_thread);
+
+ if (m_opaque_sp) {
+ CommandInterpreterRunOptions options;
+
+ m_opaque_sp->GetCommandInterpreter().RunCommandInterpreter(
+ auto_handle_events, spawn_thread, options);
+ }
+}
+
+void SBDebugger::RunCommandInterpreter(bool auto_handle_events,
+ bool spawn_thread,
+ SBCommandInterpreterRunOptions &options,
+ int &num_errors, bool &quit_requested,
+ bool &stopped_for_crash)
+
+{
+ LLDB_RECORD_METHOD(void, SBDebugger, RunCommandInterpreter,
+ (bool, bool, lldb::SBCommandInterpreterRunOptions &, int &,
+ bool &, bool &),
+ auto_handle_events, spawn_thread, options, num_errors,
+ quit_requested, stopped_for_crash);
+
+ if (m_opaque_sp) {
+ CommandInterpreter &interp = m_opaque_sp->GetCommandInterpreter();
+ interp.RunCommandInterpreter(auto_handle_events, spawn_thread,
+ options.ref());
+ num_errors = interp.GetNumErrors();
+ quit_requested = interp.GetQuitRequested();
+ stopped_for_crash = interp.GetStoppedForCrash();
+ }
+}
+
+SBError SBDebugger::RunREPL(lldb::LanguageType language,
+ const char *repl_options) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBDebugger, RunREPL,
+ (lldb::LanguageType, const char *), language,
+ repl_options);
+
+ SBError error;
+ if (m_opaque_sp)
+ error.ref() = m_opaque_sp->RunREPL(language, repl_options);
+ else
+ error.SetErrorString("invalid debugger");
+ return LLDB_RECORD_RESULT(error);
+}
+
+void SBDebugger::reset(const DebuggerSP &debugger_sp) {
+ m_opaque_sp = debugger_sp;
+}
+
+Debugger *SBDebugger::get() const { return m_opaque_sp.get(); }
+
+Debugger &SBDebugger::ref() const {
+ assert(m_opaque_sp.get());
+ return *m_opaque_sp;
+}
+
+const lldb::DebuggerSP &SBDebugger::get_sp() const { return m_opaque_sp; }
+
+SBDebugger SBDebugger::FindDebuggerWithID(int id) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBDebugger, SBDebugger, FindDebuggerWithID,
+ (int), id);
+
+ // No need to lock, the debugger list is thread safe
+ SBDebugger sb_debugger;
+ DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(id);
+ if (debugger_sp)
+ sb_debugger.reset(debugger_sp);
+ return LLDB_RECORD_RESULT(sb_debugger);
+}
+
+const char *SBDebugger::GetInstanceName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBDebugger, GetInstanceName);
+
+ return (m_opaque_sp ? m_opaque_sp->GetInstanceName().AsCString() : nullptr);
+}
+
+SBError SBDebugger::SetInternalVariable(const char *var_name, const char *value,
+ const char *debugger_instance_name) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBError, SBDebugger, SetInternalVariable,
+ (const char *, const char *, const char *),
+ var_name, value, debugger_instance_name);
+
+ SBError sb_error;
+ DebuggerSP debugger_sp(Debugger::FindDebuggerWithInstanceName(
+ ConstString(debugger_instance_name)));
+ Status error;
+ if (debugger_sp) {
+ ExecutionContext exe_ctx(
+ debugger_sp->GetCommandInterpreter().GetExecutionContext());
+ error = debugger_sp->SetPropertyValue(&exe_ctx, eVarSetOperationAssign,
+ var_name, value);
+ } else {
+ error.SetErrorStringWithFormat("invalid debugger instance name '%s'",
+ debugger_instance_name);
+ }
+ if (error.Fail())
+ sb_error.SetError(error);
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBStringList
+SBDebugger::GetInternalVariableValue(const char *var_name,
+ const char *debugger_instance_name) {
+ LLDB_RECORD_STATIC_METHOD(
+ lldb::SBStringList, SBDebugger, GetInternalVariableValue,
+ (const char *, const char *), var_name, debugger_instance_name);
+
+ SBStringList ret_value;
+ DebuggerSP debugger_sp(Debugger::FindDebuggerWithInstanceName(
+ ConstString(debugger_instance_name)));
+ Status error;
+ if (debugger_sp) {
+ ExecutionContext exe_ctx(
+ debugger_sp->GetCommandInterpreter().GetExecutionContext());
+ lldb::OptionValueSP value_sp(
+ debugger_sp->GetPropertyValue(&exe_ctx, var_name, false, error));
+ if (value_sp) {
+ StreamString value_strm;
+ value_sp->DumpValue(&exe_ctx, value_strm, OptionValue::eDumpOptionValue);
+ const std::string &value_str = value_strm.GetString();
+ if (!value_str.empty()) {
+ StringList string_list;
+ string_list.SplitIntoLines(value_str);
+ return LLDB_RECORD_RESULT(SBStringList(&string_list));
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(SBStringList());
+}
+
+uint32_t SBDebugger::GetTerminalWidth() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBDebugger, GetTerminalWidth);
+
+ return (m_opaque_sp ? m_opaque_sp->GetTerminalWidth() : 0);
+}
+
+void SBDebugger::SetTerminalWidth(uint32_t term_width) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SetTerminalWidth, (uint32_t),
+ term_width);
+
+ if (m_opaque_sp)
+ m_opaque_sp->SetTerminalWidth(term_width);
+}
+
+const char *SBDebugger::GetPrompt() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBDebugger, GetPrompt);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+
+ if (log)
+ log->Printf("SBDebugger(%p)::GetPrompt () => \"%s\"",
+ static_cast<void *>(m_opaque_sp.get()),
+ (m_opaque_sp ? m_opaque_sp->GetPrompt().str().c_str() : ""));
+
+ return (m_opaque_sp ? ConstString(m_opaque_sp->GetPrompt()).GetCString()
+ : nullptr);
+}
+
+void SBDebugger::SetPrompt(const char *prompt) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SetPrompt, (const char *), prompt);
+
+ if (m_opaque_sp)
+ m_opaque_sp->SetPrompt(llvm::StringRef::withNullAsEmpty(prompt));
+}
+
+const char *SBDebugger::GetReproducerPath() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBDebugger, GetReproducerPath);
+
+ return (m_opaque_sp
+ ? ConstString(m_opaque_sp->GetReproducerPath()).GetCString()
+ : nullptr);
+}
+
+ScriptLanguage SBDebugger::GetScriptLanguage() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::ScriptLanguage, SBDebugger,
+ GetScriptLanguage);
+
+ return (m_opaque_sp ? m_opaque_sp->GetScriptLanguage() : eScriptLanguageNone);
+}
+
+void SBDebugger::SetScriptLanguage(ScriptLanguage script_lang) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SetScriptLanguage,
+ (lldb::ScriptLanguage), script_lang);
+
+ if (m_opaque_sp) {
+ m_opaque_sp->SetScriptLanguage(script_lang);
+ }
+}
+
+bool SBDebugger::SetUseExternalEditor(bool value) {
+ LLDB_RECORD_METHOD(bool, SBDebugger, SetUseExternalEditor, (bool), value);
+
+ return (m_opaque_sp ? m_opaque_sp->SetUseExternalEditor(value) : false);
+}
+
+bool SBDebugger::GetUseExternalEditor() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBDebugger, GetUseExternalEditor);
+
+ return (m_opaque_sp ? m_opaque_sp->GetUseExternalEditor() : false);
+}
+
+bool SBDebugger::SetUseColor(bool value) {
+ LLDB_RECORD_METHOD(bool, SBDebugger, SetUseColor, (bool), value);
+
+ return (m_opaque_sp ? m_opaque_sp->SetUseColor(value) : false);
+}
+
+bool SBDebugger::GetUseColor() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBDebugger, GetUseColor);
+
+ return (m_opaque_sp ? m_opaque_sp->GetUseColor() : false);
+}
+
+bool SBDebugger::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBDebugger, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_sp) {
+ const char *name = m_opaque_sp->GetInstanceName().AsCString();
+ user_id_t id = m_opaque_sp->GetID();
+ strm.Printf("Debugger (instance: \"%s\", id: %" PRIu64 ")", name, id);
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+user_id_t SBDebugger::GetID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::user_id_t, SBDebugger, GetID);
+
+ return (m_opaque_sp ? m_opaque_sp->GetID() : LLDB_INVALID_UID);
+}
+
+SBError SBDebugger::SetCurrentPlatform(const char *platform_name_cstr) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBDebugger, SetCurrentPlatform,
+ (const char *), platform_name_cstr);
+
+ SBError sb_error;
+ if (m_opaque_sp) {
+ if (platform_name_cstr && platform_name_cstr[0]) {
+ ConstString platform_name(platform_name_cstr);
+ PlatformSP platform_sp(Platform::Find(platform_name));
+
+ if (platform_sp) {
+ // Already have a platform with this name, just select it
+ m_opaque_sp->GetPlatformList().SetSelectedPlatform(platform_sp);
+ } else {
+ // We don't have a platform by this name yet, create one
+ platform_sp = Platform::Create(platform_name, sb_error.ref());
+ if (platform_sp) {
+ // We created the platform, now append and select it
+ bool make_selected = true;
+ m_opaque_sp->GetPlatformList().Append(platform_sp, make_selected);
+ }
+ }
+ } else {
+ sb_error.ref().SetErrorString("invalid platform name");
+ }
+ } else {
+ sb_error.ref().SetErrorString("invalid debugger");
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+bool SBDebugger::SetCurrentPlatformSDKRoot(const char *sysroot) {
+ LLDB_RECORD_METHOD(bool, SBDebugger, SetCurrentPlatformSDKRoot,
+ (const char *), sysroot);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+ if (m_opaque_sp) {
+ PlatformSP platform_sp(
+ m_opaque_sp->GetPlatformList().GetSelectedPlatform());
+
+ if (platform_sp) {
+ if (log && sysroot)
+ log->Printf("SBDebugger::SetCurrentPlatformSDKRoot (\"%s\")", sysroot);
+ platform_sp->SetSDKRootDirectory(ConstString(sysroot));
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SBDebugger::GetCloseInputOnEOF() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBDebugger, GetCloseInputOnEOF);
+
+ return (m_opaque_sp ? m_opaque_sp->GetCloseInputOnEOF() : false);
+}
+
+void SBDebugger::SetCloseInputOnEOF(bool b) {
+ LLDB_RECORD_METHOD(void, SBDebugger, SetCloseInputOnEOF, (bool), b);
+
+ if (m_opaque_sp)
+ m_opaque_sp->SetCloseInputOnEOF(b);
+}
+
+SBTypeCategory SBDebugger::GetCategory(const char *category_name) {
+ LLDB_RECORD_METHOD(lldb::SBTypeCategory, SBDebugger, GetCategory,
+ (const char *), category_name);
+
+ if (!category_name || *category_name == 0)
+ return LLDB_RECORD_RESULT(SBTypeCategory());
+
+ TypeCategoryImplSP category_sp;
+
+ if (DataVisualization::Categories::GetCategory(ConstString(category_name),
+ category_sp, false)) {
+ return LLDB_RECORD_RESULT(SBTypeCategory(category_sp));
+ } else {
+ return LLDB_RECORD_RESULT(SBTypeCategory());
+ }
+}
+
+SBTypeCategory SBDebugger::GetCategory(lldb::LanguageType lang_type) {
+ LLDB_RECORD_METHOD(lldb::SBTypeCategory, SBDebugger, GetCategory,
+ (lldb::LanguageType), lang_type);
+
+ TypeCategoryImplSP category_sp;
+ if (DataVisualization::Categories::GetCategory(lang_type, category_sp)) {
+ return LLDB_RECORD_RESULT(SBTypeCategory(category_sp));
+ } else {
+ return LLDB_RECORD_RESULT(SBTypeCategory());
+ }
+}
+
+SBTypeCategory SBDebugger::CreateCategory(const char *category_name) {
+ LLDB_RECORD_METHOD(lldb::SBTypeCategory, SBDebugger, CreateCategory,
+ (const char *), category_name);
+
+ if (!category_name || *category_name == 0)
+ return LLDB_RECORD_RESULT(SBTypeCategory());
+
+ TypeCategoryImplSP category_sp;
+
+ if (DataVisualization::Categories::GetCategory(ConstString(category_name),
+ category_sp, true)) {
+ return LLDB_RECORD_RESULT(SBTypeCategory(category_sp));
+ } else {
+ return LLDB_RECORD_RESULT(SBTypeCategory());
+ }
+}
+
+bool SBDebugger::DeleteCategory(const char *category_name) {
+ LLDB_RECORD_METHOD(bool, SBDebugger, DeleteCategory, (const char *),
+ category_name);
+
+ if (!category_name || *category_name == 0)
+ return false;
+
+ return DataVisualization::Categories::Delete(ConstString(category_name));
+}
+
+uint32_t SBDebugger::GetNumCategories() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBDebugger, GetNumCategories);
+
+ return DataVisualization::Categories::GetCount();
+}
+
+SBTypeCategory SBDebugger::GetCategoryAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBTypeCategory, SBDebugger, GetCategoryAtIndex,
+ (uint32_t), index);
+
+ return LLDB_RECORD_RESULT(
+ SBTypeCategory(DataVisualization::Categories::GetCategoryAtIndex(index)));
+}
+
+SBTypeCategory SBDebugger::GetDefaultCategory() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBTypeCategory, SBDebugger,
+ GetDefaultCategory);
+
+ return LLDB_RECORD_RESULT(GetCategory("default"));
+}
+
+SBTypeFormat SBDebugger::GetFormatForType(SBTypeNameSpecifier type_name) {
+ LLDB_RECORD_METHOD(lldb::SBTypeFormat, SBDebugger, GetFormatForType,
+ (lldb::SBTypeNameSpecifier), type_name);
+
+ SBTypeCategory default_category_sb = GetDefaultCategory();
+ if (default_category_sb.GetEnabled())
+ return LLDB_RECORD_RESULT(default_category_sb.GetFormatForType(type_name));
+ return LLDB_RECORD_RESULT(SBTypeFormat());
+}
+
+SBTypeSummary SBDebugger::GetSummaryForType(SBTypeNameSpecifier type_name) {
+ LLDB_RECORD_METHOD(lldb::SBTypeSummary, SBDebugger, GetSummaryForType,
+ (lldb::SBTypeNameSpecifier), type_name);
+
+ if (!type_name.IsValid())
+ return LLDB_RECORD_RESULT(SBTypeSummary());
+ return LLDB_RECORD_RESULT(
+ SBTypeSummary(DataVisualization::GetSummaryForType(type_name.GetSP())));
+}
+
+SBTypeFilter SBDebugger::GetFilterForType(SBTypeNameSpecifier type_name) {
+ LLDB_RECORD_METHOD(lldb::SBTypeFilter, SBDebugger, GetFilterForType,
+ (lldb::SBTypeNameSpecifier), type_name);
+
+ if (!type_name.IsValid())
+ return LLDB_RECORD_RESULT(SBTypeFilter());
+ return LLDB_RECORD_RESULT(
+ SBTypeFilter(DataVisualization::GetFilterForType(type_name.GetSP())));
+}
+
+SBTypeSynthetic SBDebugger::GetSyntheticForType(SBTypeNameSpecifier type_name) {
+ LLDB_RECORD_METHOD(lldb::SBTypeSynthetic, SBDebugger, GetSyntheticForType,
+ (lldb::SBTypeNameSpecifier), type_name);
+
+ if (!type_name.IsValid())
+ return LLDB_RECORD_RESULT(SBTypeSynthetic());
+ return LLDB_RECORD_RESULT(SBTypeSynthetic(
+ DataVisualization::GetSyntheticForType(type_name.GetSP())));
+}
+
+static llvm::ArrayRef<const char *> GetCategoryArray(const char **categories) {
+ if (categories == nullptr)
+ return {};
+ size_t len = 0;
+ while (categories[len] != nullptr)
+ ++len;
+ return llvm::makeArrayRef(categories, len);
+}
+
+bool SBDebugger::EnableLog(const char *channel, const char **categories) {
+ LLDB_RECORD_METHOD(bool, SBDebugger, EnableLog, (const char *, const char **),
+ channel, categories);
+
+ if (m_opaque_sp) {
+ uint32_t log_options =
+ LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
+ std::string error;
+ llvm::raw_string_ostream error_stream(error);
+ return m_opaque_sp->EnableLog(channel, GetCategoryArray(categories), "",
+ log_options, error_stream);
+ } else
+ return false;
+}
+
+void SBDebugger::SetLoggingCallback(lldb::LogOutputCallback log_callback,
+ void *baton) {
+ LLDB_RECORD_DUMMY(void, SBDebugger, SetLoggingCallback,
+ (lldb::LogOutputCallback, void *), log_callback, baton);
+
+ if (m_opaque_sp) {
+ return m_opaque_sp->SetLoggingCallback(log_callback, baton);
+ }
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBInputReader>(Registry &R) {
+ LLDB_REGISTER_METHOD(void, SBInputReader, SetIsDone, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBInputReader, IsActive, ());
+}
+
+static void SetFileHandleRedirect(SBDebugger *, FILE *, bool) {
+ // Do nothing.
+}
+
+static bool GetDefaultArchitectureRedirect(char *arch_name,
+ size_t arch_name_len) {
+ // The function is writing to its argument. Without the redirect it would
+ // write into the replay buffer.
+ char buffer[1024];
+ return SBDebugger::GetDefaultArchitecture(buffer, arch_name_len);
+}
+
+template <>
+void RegisterMethods<SBDebugger>(Registry &R) {
+ // Custom implementation.
+ R.Register(&invoke<void (SBDebugger::*)(
+ FILE *, bool)>::method<&SBDebugger::SetErrorFileHandle>::doit,
+ &SetFileHandleRedirect);
+ R.Register(&invoke<void (SBDebugger::*)(
+ FILE *, bool)>::method<&SBDebugger::SetOutputFileHandle>::doit,
+ &SetFileHandleRedirect);
+ R.Register<bool(char *, size_t)>(static_cast<bool (*)(char *, size_t)>(
+ &SBDebugger::GetDefaultArchitecture),
+ &GetDefaultArchitectureRedirect);
+
+ LLDB_REGISTER_CONSTRUCTOR(SBDebugger, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBDebugger, (const lldb::DebuggerSP &));
+ LLDB_REGISTER_CONSTRUCTOR(SBDebugger, (const lldb::SBDebugger &));
+ LLDB_REGISTER_METHOD(lldb::SBDebugger &,
+ SBDebugger, operator=,(const lldb::SBDebugger &));
+ LLDB_REGISTER_STATIC_METHOD(void, SBDebugger, Initialize, ());
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBError, SBDebugger,
+ InitializeWithErrorHandling, ());
+ LLDB_REGISTER_STATIC_METHOD(void, SBDebugger, Terminate, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, Clear, ());
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBDebugger, SBDebugger, Create, ());
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBDebugger, SBDebugger, Create, (bool));
+ LLDB_REGISTER_STATIC_METHOD(void, SBDebugger, Destroy,
+ (lldb::SBDebugger &));
+ LLDB_REGISTER_STATIC_METHOD(void, SBDebugger, MemoryPressureDetected, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBDebugger, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBDebugger, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, SetAsync, (bool));
+ LLDB_REGISTER_METHOD(bool, SBDebugger, GetAsync, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, SkipLLDBInitFiles, (bool));
+ LLDB_REGISTER_METHOD(void, SBDebugger, SkipAppInitFiles, (bool));
+ LLDB_REGISTER_METHOD(void, SBDebugger, SetInputFileHandle, (FILE *, bool));
+ LLDB_REGISTER_METHOD(FILE *, SBDebugger, GetInputFileHandle, ());
+ LLDB_REGISTER_METHOD(FILE *, SBDebugger, GetOutputFileHandle, ());
+ LLDB_REGISTER_METHOD(FILE *, SBDebugger, GetErrorFileHandle, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, SaveInputTerminalState, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, RestoreInputTerminalState, ());
+ LLDB_REGISTER_METHOD(lldb::SBCommandInterpreter, SBDebugger,
+ GetCommandInterpreter, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, HandleCommand, (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBListener, SBDebugger, GetListener, ());
+ LLDB_REGISTER_METHOD(
+ void, SBDebugger, HandleProcessEvent,
+ (const lldb::SBProcess &, const lldb::SBEvent &, FILE *, FILE *));
+ LLDB_REGISTER_METHOD(lldb::SBSourceManager, SBDebugger, GetSourceManager,
+ ());
+ LLDB_REGISTER_STATIC_METHOD(bool, SBDebugger, SetDefaultArchitecture,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::ScriptLanguage, SBDebugger, GetScriptingLanguage,
+ (const char *));
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBDebugger, GetVersionString, ());
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBDebugger, StateAsCString,
+ (lldb::StateType));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBStructuredData, SBDebugger,
+ GetBuildConfiguration, ());
+ LLDB_REGISTER_STATIC_METHOD(bool, SBDebugger, StateIsRunningState,
+ (lldb::StateType));
+ LLDB_REGISTER_STATIC_METHOD(bool, SBDebugger, StateIsStoppedState,
+ (lldb::StateType));
+ LLDB_REGISTER_METHOD(
+ lldb::SBTarget, SBDebugger, CreateTarget,
+ (const char *, const char *, const char *, bool, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBTarget, SBDebugger,
+ CreateTargetWithFileAndTargetTriple,
+ (const char *, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBTarget, SBDebugger,
+ CreateTargetWithFileAndArch,
+ (const char *, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBTarget, SBDebugger, CreateTarget,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBTarget, SBDebugger, GetDummyTarget, ());
+ LLDB_REGISTER_METHOD(bool, SBDebugger, DeleteTarget, (lldb::SBTarget &));
+ LLDB_REGISTER_METHOD(lldb::SBTarget, SBDebugger, GetTargetAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBDebugger, GetIndexOfTarget,
+ (lldb::SBTarget));
+ LLDB_REGISTER_METHOD(lldb::SBTarget, SBDebugger, FindTargetWithProcessID,
+ (lldb::pid_t));
+ LLDB_REGISTER_METHOD(lldb::SBTarget, SBDebugger, FindTargetWithFileAndArch,
+ (const char *, const char *));
+ LLDB_REGISTER_METHOD(uint32_t, SBDebugger, GetNumTargets, ());
+ LLDB_REGISTER_METHOD(lldb::SBTarget, SBDebugger, GetSelectedTarget, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, SetSelectedTarget,
+ (lldb::SBTarget &));
+ LLDB_REGISTER_METHOD(lldb::SBPlatform, SBDebugger, GetSelectedPlatform, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, SetSelectedPlatform,
+ (lldb::SBPlatform &));
+ LLDB_REGISTER_METHOD(uint32_t, SBDebugger, GetNumPlatforms, ());
+ LLDB_REGISTER_METHOD(lldb::SBPlatform, SBDebugger, GetPlatformAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBDebugger, GetNumAvailablePlatforms, ());
+ LLDB_REGISTER_METHOD(lldb::SBStructuredData, SBDebugger,
+ GetAvailablePlatformInfoAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBDebugger, DispatchInputInterrupt, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, DispatchInputEndOfFile, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, PushInputReader,
+ (lldb::SBInputReader &));
+ LLDB_REGISTER_METHOD(void, SBDebugger, RunCommandInterpreter, (bool, bool));
+ LLDB_REGISTER_METHOD(void, SBDebugger, RunCommandInterpreter,
+ (bool, bool, lldb::SBCommandInterpreterRunOptions &,
+ int &, bool &, bool &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBDebugger, RunREPL,
+ (lldb::LanguageType, const char *));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBDebugger, SBDebugger,
+ FindDebuggerWithID, (int));
+ LLDB_REGISTER_METHOD(const char *, SBDebugger, GetInstanceName, ());
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBError, SBDebugger, SetInternalVariable,
+ (const char *, const char *, const char *));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBStringList, SBDebugger,
+ GetInternalVariableValue,
+ (const char *, const char *));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBDebugger, GetTerminalWidth, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, SetTerminalWidth, (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBDebugger, GetPrompt, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, SetPrompt, (const char *));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBDebugger, GetReproducerPath, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::ScriptLanguage, SBDebugger,
+ GetScriptLanguage, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, SetScriptLanguage,
+ (lldb::ScriptLanguage));
+ LLDB_REGISTER_METHOD(bool, SBDebugger, SetUseExternalEditor, (bool));
+ LLDB_REGISTER_METHOD(bool, SBDebugger, GetUseExternalEditor, ());
+ LLDB_REGISTER_METHOD(bool, SBDebugger, SetUseColor, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBDebugger, GetUseColor, ());
+ LLDB_REGISTER_METHOD(bool, SBDebugger, GetDescription, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(lldb::user_id_t, SBDebugger, GetID, ());
+ LLDB_REGISTER_METHOD(lldb::SBError, SBDebugger, SetCurrentPlatform,
+ (const char *));
+ LLDB_REGISTER_METHOD(bool, SBDebugger, SetCurrentPlatformSDKRoot,
+ (const char *));
+ LLDB_REGISTER_METHOD_CONST(bool, SBDebugger, GetCloseInputOnEOF, ());
+ LLDB_REGISTER_METHOD(void, SBDebugger, SetCloseInputOnEOF, (bool));
+ LLDB_REGISTER_METHOD(lldb::SBTypeCategory, SBDebugger, GetCategory,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBTypeCategory, SBDebugger, GetCategory,
+ (lldb::LanguageType));
+ LLDB_REGISTER_METHOD(lldb::SBTypeCategory, SBDebugger, CreateCategory,
+ (const char *));
+ LLDB_REGISTER_METHOD(bool, SBDebugger, DeleteCategory, (const char *));
+ LLDB_REGISTER_METHOD(uint32_t, SBDebugger, GetNumCategories, ());
+ LLDB_REGISTER_METHOD(lldb::SBTypeCategory, SBDebugger, GetCategoryAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBTypeCategory, SBDebugger, GetDefaultCategory,
+ ());
+ LLDB_REGISTER_METHOD(lldb::SBTypeFormat, SBDebugger, GetFormatForType,
+ (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(lldb::SBTypeSummary, SBDebugger, GetSummaryForType,
+ (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(lldb::SBTypeSynthetic, SBDebugger, GetSyntheticForType,
+ (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(lldb::SBTypeFilter, SBDebugger, GetFilterForType,
+ (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(bool, SBDebugger, EnableLog,
+ (const char *, const char **));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBDeclaration.cpp b/contrib/llvm-project/lldb/source/API/SBDeclaration.cpp
new file mode 100644
index 000000000000..a7790b293981
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBDeclaration.cpp
@@ -0,0 +1,207 @@
+//===-- SBDeclaration.cpp ----------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBDeclaration.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Symbol/Declaration.h"
+#include "lldb/Utility/Stream.h"
+
+#include <limits.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBDeclaration::SBDeclaration() : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBDeclaration);
+}
+
+SBDeclaration::SBDeclaration(const SBDeclaration &rhs) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBDeclaration, (const lldb::SBDeclaration &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+SBDeclaration::SBDeclaration(const lldb_private::Declaration *lldb_object_ptr)
+ : m_opaque_up() {
+ if (lldb_object_ptr)
+ m_opaque_up = llvm::make_unique<Declaration>(*lldb_object_ptr);
+}
+
+const SBDeclaration &SBDeclaration::operator=(const SBDeclaration &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBDeclaration &,
+ SBDeclaration, operator=,(const lldb::SBDeclaration &),
+ rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+void SBDeclaration::SetDeclaration(
+ const lldb_private::Declaration &lldb_object_ref) {
+ ref() = lldb_object_ref;
+}
+
+SBDeclaration::~SBDeclaration() {}
+
+bool SBDeclaration::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBDeclaration, IsValid);
+ return this->operator bool();
+}
+SBDeclaration::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBDeclaration, operator bool);
+
+ return m_opaque_up.get() && m_opaque_up->IsValid();
+}
+
+SBFileSpec SBDeclaration::GetFileSpec() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBFileSpec, SBDeclaration,
+ GetFileSpec);
+
+
+ SBFileSpec sb_file_spec;
+ if (m_opaque_up.get() && m_opaque_up->GetFile())
+ sb_file_spec.SetFileSpec(m_opaque_up->GetFile());
+
+
+ return LLDB_RECORD_RESULT(sb_file_spec);
+}
+
+uint32_t SBDeclaration::GetLine() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBDeclaration, GetLine);
+
+
+ uint32_t line = 0;
+ if (m_opaque_up)
+ line = m_opaque_up->GetLine();
+
+
+ return line;
+}
+
+uint32_t SBDeclaration::GetColumn() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBDeclaration, GetColumn);
+
+ if (m_opaque_up)
+ return m_opaque_up->GetColumn();
+ return 0;
+}
+
+void SBDeclaration::SetFileSpec(lldb::SBFileSpec filespec) {
+ LLDB_RECORD_METHOD(void, SBDeclaration, SetFileSpec, (lldb::SBFileSpec),
+ filespec);
+
+ if (filespec.IsValid())
+ ref().SetFile(filespec.ref());
+ else
+ ref().SetFile(FileSpec());
+}
+void SBDeclaration::SetLine(uint32_t line) {
+ LLDB_RECORD_METHOD(void, SBDeclaration, SetLine, (uint32_t), line);
+
+ ref().SetLine(line);
+}
+
+void SBDeclaration::SetColumn(uint32_t column) {
+ LLDB_RECORD_METHOD(void, SBDeclaration, SetColumn, (uint32_t), column);
+
+ ref().SetColumn(column);
+}
+
+bool SBDeclaration::operator==(const SBDeclaration &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBDeclaration, operator==,(const lldb::SBDeclaration &), rhs);
+
+ lldb_private::Declaration *lhs_ptr = m_opaque_up.get();
+ lldb_private::Declaration *rhs_ptr = rhs.m_opaque_up.get();
+
+ if (lhs_ptr && rhs_ptr)
+ return lldb_private::Declaration::Compare(*lhs_ptr, *rhs_ptr) == 0;
+
+ return lhs_ptr == rhs_ptr;
+}
+
+bool SBDeclaration::operator!=(const SBDeclaration &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBDeclaration, operator!=,(const lldb::SBDeclaration &), rhs);
+
+ lldb_private::Declaration *lhs_ptr = m_opaque_up.get();
+ lldb_private::Declaration *rhs_ptr = rhs.m_opaque_up.get();
+
+ if (lhs_ptr && rhs_ptr)
+ return lldb_private::Declaration::Compare(*lhs_ptr, *rhs_ptr) != 0;
+
+ return lhs_ptr != rhs_ptr;
+}
+
+const lldb_private::Declaration *SBDeclaration::operator->() const {
+ return m_opaque_up.get();
+}
+
+lldb_private::Declaration &SBDeclaration::ref() {
+ if (m_opaque_up == nullptr)
+ m_opaque_up.reset(new lldb_private::Declaration());
+ return *m_opaque_up;
+}
+
+const lldb_private::Declaration &SBDeclaration::ref() const {
+ return *m_opaque_up;
+}
+
+bool SBDeclaration::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBDeclaration, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_up) {
+ char file_path[PATH_MAX * 2];
+ m_opaque_up->GetFile().GetPath(file_path, sizeof(file_path));
+ strm.Printf("%s:%u", file_path, GetLine());
+ if (GetColumn() > 0)
+ strm.Printf(":%u", GetColumn());
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+lldb_private::Declaration *SBDeclaration::get() { return m_opaque_up.get(); }
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBDeclaration>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBDeclaration, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBDeclaration, (const lldb::SBDeclaration &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBDeclaration &,
+ SBDeclaration, operator=,(const lldb::SBDeclaration &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBDeclaration, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBDeclaration, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBFileSpec, SBDeclaration, GetFileSpec,
+ ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBDeclaration, GetLine, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBDeclaration, GetColumn, ());
+ LLDB_REGISTER_METHOD(void, SBDeclaration, SetFileSpec, (lldb::SBFileSpec));
+ LLDB_REGISTER_METHOD(void, SBDeclaration, SetLine, (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBDeclaration, SetColumn, (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBDeclaration, operator==,(const lldb::SBDeclaration &));
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBDeclaration, operator!=,(const lldb::SBDeclaration &));
+ LLDB_REGISTER_METHOD(bool, SBDeclaration, GetDescription,
+ (lldb::SBStream &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBError.cpp b/contrib/llvm-project/lldb/source/API/SBError.cpp
new file mode 100644
index 000000000000..7256e8e55de9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBError.cpp
@@ -0,0 +1,212 @@
+//===-- SBError.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBError.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Utility/Status.h"
+
+#include <stdarg.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBError::SBError() : m_opaque_up() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBError); }
+
+SBError::SBError(const SBError &rhs) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBError, (const lldb::SBError &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+SBError::~SBError() {}
+
+const SBError &SBError::operator=(const SBError &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBError &,
+ SBError, operator=,(const lldb::SBError &), rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+const char *SBError::GetCString() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBError, GetCString);
+
+ if (m_opaque_up)
+ return m_opaque_up->AsCString();
+ return nullptr;
+}
+
+void SBError::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBError, Clear);
+
+ if (m_opaque_up)
+ m_opaque_up->Clear();
+}
+
+bool SBError::Fail() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBError, Fail);
+
+ bool ret_value = false;
+ if (m_opaque_up)
+ ret_value = m_opaque_up->Fail();
+
+
+ return ret_value;
+}
+
+bool SBError::Success() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBError, Success);
+
+ bool ret_value = true;
+ if (m_opaque_up)
+ ret_value = m_opaque_up->Success();
+
+ return ret_value;
+}
+
+uint32_t SBError::GetError() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBError, GetError);
+
+
+ uint32_t err = 0;
+ if (m_opaque_up)
+ err = m_opaque_up->GetError();
+
+
+ return err;
+}
+
+ErrorType SBError::GetType() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::ErrorType, SBError, GetType);
+
+ ErrorType err_type = eErrorTypeInvalid;
+ if (m_opaque_up)
+ err_type = m_opaque_up->GetType();
+
+ return err_type;
+}
+
+void SBError::SetError(uint32_t err, ErrorType type) {
+ LLDB_RECORD_METHOD(void, SBError, SetError, (uint32_t, lldb::ErrorType), err,
+ type);
+
+ CreateIfNeeded();
+ m_opaque_up->SetError(err, type);
+}
+
+void SBError::SetError(const Status &lldb_error) {
+ CreateIfNeeded();
+ *m_opaque_up = lldb_error;
+}
+
+void SBError::SetErrorToErrno() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBError, SetErrorToErrno);
+
+ CreateIfNeeded();
+ m_opaque_up->SetErrorToErrno();
+}
+
+void SBError::SetErrorToGenericError() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBError, SetErrorToGenericError);
+
+ CreateIfNeeded();
+ m_opaque_up->SetErrorToErrno();
+}
+
+void SBError::SetErrorString(const char *err_str) {
+ LLDB_RECORD_METHOD(void, SBError, SetErrorString, (const char *), err_str);
+
+ CreateIfNeeded();
+ m_opaque_up->SetErrorString(err_str);
+}
+
+int SBError::SetErrorStringWithFormat(const char *format, ...) {
+ CreateIfNeeded();
+ va_list args;
+ va_start(args, format);
+ int num_chars = m_opaque_up->SetErrorStringWithVarArg(format, args);
+ va_end(args);
+ return num_chars;
+}
+
+bool SBError::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBError, IsValid);
+ return this->operator bool();
+}
+SBError::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBError, operator bool);
+
+ return m_opaque_up != nullptr;
+}
+
+void SBError::CreateIfNeeded() {
+ if (m_opaque_up == nullptr)
+ m_opaque_up.reset(new Status());
+}
+
+lldb_private::Status *SBError::operator->() { return m_opaque_up.get(); }
+
+lldb_private::Status *SBError::get() { return m_opaque_up.get(); }
+
+lldb_private::Status &SBError::ref() {
+ CreateIfNeeded();
+ return *m_opaque_up;
+}
+
+const lldb_private::Status &SBError::operator*() const {
+ // Be sure to call "IsValid()" before calling this function or it will crash
+ return *m_opaque_up;
+}
+
+bool SBError::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBError, GetDescription, (lldb::SBStream &),
+ description);
+
+ if (m_opaque_up) {
+ if (m_opaque_up->Success())
+ description.Printf("success");
+ else {
+ const char *err_string = GetCString();
+ description.Printf("error: %s",
+ (err_string != nullptr ? err_string : ""));
+ }
+ } else
+ description.Printf("error: <NULL>");
+
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBError>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBError, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBError, (const lldb::SBError &));
+ LLDB_REGISTER_METHOD(const lldb::SBError &,
+ SBError, operator=,(const lldb::SBError &));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBError, GetCString, ());
+ LLDB_REGISTER_METHOD(void, SBError, Clear, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBError, Fail, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBError, Success, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBError, GetError, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::ErrorType, SBError, GetType, ());
+ LLDB_REGISTER_METHOD(void, SBError, SetError, (uint32_t, lldb::ErrorType));
+ LLDB_REGISTER_METHOD(void, SBError, SetErrorToErrno, ());
+ LLDB_REGISTER_METHOD(void, SBError, SetErrorToGenericError, ());
+ LLDB_REGISTER_METHOD(void, SBError, SetErrorString, (const char *));
+ LLDB_REGISTER_METHOD_CONST(bool, SBError, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBError, operator bool, ());
+ LLDB_REGISTER_METHOD(bool, SBError, GetDescription, (lldb::SBStream &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBEvent.cpp b/contrib/llvm-project/lldb/source/API/SBEvent.cpp
new file mode 100644
index 000000000000..75ca2830df9f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBEvent.cpp
@@ -0,0 +1,242 @@
+//===-- SBEvent.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBEvent.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBBroadcaster.h"
+#include "lldb/API/SBStream.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBEvent::SBEvent() : m_event_sp(), m_opaque_ptr(nullptr) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBEvent);
+}
+
+SBEvent::SBEvent(uint32_t event_type, const char *cstr, uint32_t cstr_len)
+ : m_event_sp(new Event(event_type, new EventDataBytes(cstr, cstr_len))),
+ m_opaque_ptr(m_event_sp.get()) {
+ LLDB_RECORD_CONSTRUCTOR(SBEvent, (uint32_t, const char *, uint32_t),
+ event_type, cstr, cstr_len);
+}
+
+SBEvent::SBEvent(EventSP &event_sp)
+ : m_event_sp(event_sp), m_opaque_ptr(event_sp.get()) {
+ LLDB_RECORD_CONSTRUCTOR(SBEvent, (lldb::EventSP &), event_sp);
+}
+
+SBEvent::SBEvent(Event *event_ptr) : m_event_sp(), m_opaque_ptr(event_ptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBEvent, (lldb_private::Event *), event_ptr);
+}
+
+SBEvent::SBEvent(const SBEvent &rhs)
+ : m_event_sp(rhs.m_event_sp), m_opaque_ptr(rhs.m_opaque_ptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBEvent, (const lldb::SBEvent &), rhs);
+}
+
+const SBEvent &SBEvent::operator=(const SBEvent &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBEvent &,
+ SBEvent, operator=,(const lldb::SBEvent &), rhs);
+
+ if (this != &rhs) {
+ m_event_sp = rhs.m_event_sp;
+ m_opaque_ptr = rhs.m_opaque_ptr;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBEvent::~SBEvent() {}
+
+const char *SBEvent::GetDataFlavor() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBEvent, GetDataFlavor);
+
+ Event *lldb_event = get();
+ if (lldb_event) {
+ EventData *event_data = lldb_event->GetData();
+ if (event_data)
+ return lldb_event->GetData()->GetFlavor().AsCString();
+ }
+ return nullptr;
+}
+
+uint32_t SBEvent::GetType() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBEvent, GetType);
+
+
+ const Event *lldb_event = get();
+ uint32_t event_type = 0;
+ if (lldb_event)
+ event_type = lldb_event->GetType();
+
+
+ return event_type;
+}
+
+SBBroadcaster SBEvent::GetBroadcaster() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBBroadcaster, SBEvent,
+ GetBroadcaster);
+
+ SBBroadcaster broadcaster;
+ const Event *lldb_event = get();
+ if (lldb_event)
+ broadcaster.reset(lldb_event->GetBroadcaster(), false);
+ return LLDB_RECORD_RESULT(broadcaster);
+}
+
+const char *SBEvent::GetBroadcasterClass() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBEvent, GetBroadcasterClass);
+
+ const Event *lldb_event = get();
+ if (lldb_event)
+ return lldb_event->GetBroadcaster()->GetBroadcasterClass().AsCString();
+ else
+ return "unknown class";
+}
+
+bool SBEvent::BroadcasterMatchesPtr(const SBBroadcaster *broadcaster) {
+ LLDB_RECORD_METHOD(bool, SBEvent, BroadcasterMatchesPtr,
+ (const lldb::SBBroadcaster *), broadcaster);
+
+ if (broadcaster)
+ return BroadcasterMatchesRef(*broadcaster);
+ return false;
+}
+
+bool SBEvent::BroadcasterMatchesRef(const SBBroadcaster &broadcaster) {
+ LLDB_RECORD_METHOD(bool, SBEvent, BroadcasterMatchesRef,
+ (const lldb::SBBroadcaster &), broadcaster);
+
+ Event *lldb_event = get();
+ bool success = false;
+ if (lldb_event)
+ success = lldb_event->BroadcasterIs(broadcaster.get());
+
+
+ return success;
+}
+
+void SBEvent::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBEvent, Clear);
+
+ Event *lldb_event = get();
+ if (lldb_event)
+ lldb_event->Clear();
+}
+
+EventSP &SBEvent::GetSP() const { return m_event_sp; }
+
+Event *SBEvent::get() const {
+ // There is a dangerous accessor call GetSharedPtr which can be used, so if
+ // we have anything valid in m_event_sp, we must use that since if it gets
+ // used by a function that puts something in there, then it won't update
+ // m_opaque_ptr...
+ if (m_event_sp)
+ m_opaque_ptr = m_event_sp.get();
+
+ return m_opaque_ptr;
+}
+
+void SBEvent::reset(EventSP &event_sp) {
+ m_event_sp = event_sp;
+ m_opaque_ptr = m_event_sp.get();
+}
+
+void SBEvent::reset(Event *event_ptr) {
+ m_opaque_ptr = event_ptr;
+ m_event_sp.reset();
+}
+
+bool SBEvent::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBEvent, IsValid);
+ return this->operator bool();
+}
+SBEvent::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBEvent, operator bool);
+
+ // Do NOT use m_opaque_ptr directly!!! Must use the SBEvent::get() accessor.
+ // See comments in SBEvent::get()....
+ return SBEvent::get() != nullptr;
+}
+
+const char *SBEvent::GetCStringFromEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(const char *, SBEvent, GetCStringFromEvent,
+ (const lldb::SBEvent &), event);
+
+ return reinterpret_cast<const char *>(
+ EventDataBytes::GetBytesFromEvent(event.get()));
+}
+
+bool SBEvent::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBEvent, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ if (get()) {
+ m_opaque_ptr->Dump(&strm);
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+bool SBEvent::GetDescription(SBStream &description) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBEvent, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ if (get()) {
+ m_opaque_ptr->Dump(&strm);
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBEvent>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBEvent, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBEvent, (uint32_t, const char *, uint32_t));
+ LLDB_REGISTER_CONSTRUCTOR(SBEvent, (lldb::EventSP &));
+ LLDB_REGISTER_CONSTRUCTOR(SBEvent, (lldb_private::Event *));
+ LLDB_REGISTER_CONSTRUCTOR(SBEvent, (const lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(const lldb::SBEvent &,
+ SBEvent, operator=,(const lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(const char *, SBEvent, GetDataFlavor, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBEvent, GetType, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBBroadcaster, SBEvent, GetBroadcaster,
+ ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBEvent, GetBroadcasterClass, ());
+ LLDB_REGISTER_METHOD(bool, SBEvent, BroadcasterMatchesPtr,
+ (const lldb::SBBroadcaster *));
+ LLDB_REGISTER_METHOD(bool, SBEvent, BroadcasterMatchesRef,
+ (const lldb::SBBroadcaster &));
+ LLDB_REGISTER_METHOD(void, SBEvent, Clear, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBEvent, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBEvent, operator bool, ());
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBEvent, GetCStringFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(bool, SBEvent, GetDescription, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBEvent, GetDescription,
+ (lldb::SBStream &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBExecutionContext.cpp b/contrib/llvm-project/lldb/source/API/SBExecutionContext.cpp
new file mode 100644
index 000000000000..1224c2abe989
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBExecutionContext.cpp
@@ -0,0 +1,163 @@
+//===-- SBExecutionContext.cpp ------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBExecutionContext.h"
+#include "SBReproducerPrivate.h"
+
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/API/SBThread.h"
+
+#include "lldb/Target/ExecutionContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBExecutionContext::SBExecutionContext() : m_exe_ctx_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBExecutionContext);
+}
+
+SBExecutionContext::SBExecutionContext(const lldb::SBExecutionContext &rhs)
+ : m_exe_ctx_sp(rhs.m_exe_ctx_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBExecutionContext,
+ (const lldb::SBExecutionContext &), rhs);
+}
+
+SBExecutionContext::SBExecutionContext(
+ lldb::ExecutionContextRefSP exe_ctx_ref_sp)
+ : m_exe_ctx_sp(exe_ctx_ref_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBExecutionContext, (lldb::ExecutionContextRefSP),
+ exe_ctx_ref_sp);
+}
+
+SBExecutionContext::SBExecutionContext(const lldb::SBTarget &target)
+ : m_exe_ctx_sp(new ExecutionContextRef()) {
+ LLDB_RECORD_CONSTRUCTOR(SBExecutionContext, (const lldb::SBTarget &), target);
+
+ m_exe_ctx_sp->SetTargetSP(target.GetSP());
+}
+
+SBExecutionContext::SBExecutionContext(const lldb::SBProcess &process)
+ : m_exe_ctx_sp(new ExecutionContextRef()) {
+ LLDB_RECORD_CONSTRUCTOR(SBExecutionContext, (const lldb::SBProcess &),
+ process);
+
+ m_exe_ctx_sp->SetProcessSP(process.GetSP());
+}
+
+SBExecutionContext::SBExecutionContext(lldb::SBThread thread)
+ : m_exe_ctx_sp(new ExecutionContextRef()) {
+ LLDB_RECORD_CONSTRUCTOR(SBExecutionContext, (lldb::SBThread), thread);
+
+ m_exe_ctx_sp->SetThreadPtr(thread.get());
+}
+
+SBExecutionContext::SBExecutionContext(const lldb::SBFrame &frame)
+ : m_exe_ctx_sp(new ExecutionContextRef()) {
+ LLDB_RECORD_CONSTRUCTOR(SBExecutionContext, (const lldb::SBFrame &), frame);
+
+ m_exe_ctx_sp->SetFrameSP(frame.GetFrameSP());
+}
+
+SBExecutionContext::~SBExecutionContext() {}
+
+const SBExecutionContext &SBExecutionContext::
+operator=(const lldb::SBExecutionContext &rhs) {
+ LLDB_RECORD_METHOD(
+ const lldb::SBExecutionContext &,
+ SBExecutionContext, operator=,(const lldb::SBExecutionContext &), rhs);
+
+ m_exe_ctx_sp = rhs.m_exe_ctx_sp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+ExecutionContextRef *SBExecutionContext::get() const {
+ return m_exe_ctx_sp.get();
+}
+
+SBTarget SBExecutionContext::GetTarget() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBTarget, SBExecutionContext,
+ GetTarget);
+
+ SBTarget sb_target;
+ if (m_exe_ctx_sp) {
+ TargetSP target_sp(m_exe_ctx_sp->GetTargetSP());
+ if (target_sp)
+ sb_target.SetSP(target_sp);
+ }
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+SBProcess SBExecutionContext::GetProcess() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBProcess, SBExecutionContext,
+ GetProcess);
+
+ SBProcess sb_process;
+ if (m_exe_ctx_sp) {
+ ProcessSP process_sp(m_exe_ctx_sp->GetProcessSP());
+ if (process_sp)
+ sb_process.SetSP(process_sp);
+ }
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+SBThread SBExecutionContext::GetThread() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBThread, SBExecutionContext,
+ GetThread);
+
+ SBThread sb_thread;
+ if (m_exe_ctx_sp) {
+ ThreadSP thread_sp(m_exe_ctx_sp->GetThreadSP());
+ if (thread_sp)
+ sb_thread.SetThread(thread_sp);
+ }
+ return LLDB_RECORD_RESULT(sb_thread);
+}
+
+SBFrame SBExecutionContext::GetFrame() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBFrame, SBExecutionContext, GetFrame);
+
+ SBFrame sb_frame;
+ if (m_exe_ctx_sp) {
+ StackFrameSP frame_sp(m_exe_ctx_sp->GetFrameSP());
+ if (frame_sp)
+ sb_frame.SetFrameSP(frame_sp);
+ }
+ return LLDB_RECORD_RESULT(sb_frame);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBExecutionContext>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBExecutionContext, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBExecutionContext,
+ (const lldb::SBExecutionContext &));
+ LLDB_REGISTER_CONSTRUCTOR(SBExecutionContext,
+ (lldb::ExecutionContextRefSP));
+ LLDB_REGISTER_CONSTRUCTOR(SBExecutionContext, (const lldb::SBTarget &));
+ LLDB_REGISTER_CONSTRUCTOR(SBExecutionContext, (const lldb::SBProcess &));
+ LLDB_REGISTER_CONSTRUCTOR(SBExecutionContext, (lldb::SBThread));
+ LLDB_REGISTER_CONSTRUCTOR(SBExecutionContext, (const lldb::SBFrame &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBExecutionContext &,
+ SBExecutionContext, operator=,(const lldb::SBExecutionContext &));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBTarget, SBExecutionContext, GetTarget,
+ ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBProcess, SBExecutionContext, GetProcess,
+ ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBThread, SBExecutionContext, GetThread,
+ ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBFrame, SBExecutionContext, GetFrame, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBExpressionOptions.cpp b/contrib/llvm-project/lldb/source/API/SBExpressionOptions.cpp
new file mode 100644
index 000000000000..8c34194abf1e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBExpressionOptions.cpp
@@ -0,0 +1,336 @@
+//===-- SBExpressionOptions.cpp ---------------------------------------------*-
+//C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBExpressionOptions.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBExpressionOptions::SBExpressionOptions()
+ : m_opaque_up(new EvaluateExpressionOptions()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBExpressionOptions);
+}
+
+SBExpressionOptions::SBExpressionOptions(const SBExpressionOptions &rhs)
+ : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBExpressionOptions,
+ (const lldb::SBExpressionOptions &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+const SBExpressionOptions &SBExpressionOptions::
+operator=(const SBExpressionOptions &rhs) {
+ LLDB_RECORD_METHOD(
+ const lldb::SBExpressionOptions &,
+ SBExpressionOptions, operator=,(const lldb::SBExpressionOptions &), rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBExpressionOptions::~SBExpressionOptions() {}
+
+bool SBExpressionOptions::GetCoerceResultToId() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBExpressionOptions,
+ GetCoerceResultToId);
+
+ return m_opaque_up->DoesCoerceToId();
+}
+
+void SBExpressionOptions::SetCoerceResultToId(bool coerce) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetCoerceResultToId, (bool),
+ coerce);
+
+ m_opaque_up->SetCoerceToId(coerce);
+}
+
+bool SBExpressionOptions::GetUnwindOnError() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBExpressionOptions, GetUnwindOnError);
+
+ return m_opaque_up->DoesUnwindOnError();
+}
+
+void SBExpressionOptions::SetUnwindOnError(bool unwind) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetUnwindOnError, (bool),
+ unwind);
+
+ m_opaque_up->SetUnwindOnError(unwind);
+}
+
+bool SBExpressionOptions::GetIgnoreBreakpoints() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBExpressionOptions,
+ GetIgnoreBreakpoints);
+
+ return m_opaque_up->DoesIgnoreBreakpoints();
+}
+
+void SBExpressionOptions::SetIgnoreBreakpoints(bool ignore) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetIgnoreBreakpoints, (bool),
+ ignore);
+
+ m_opaque_up->SetIgnoreBreakpoints(ignore);
+}
+
+lldb::DynamicValueType SBExpressionOptions::GetFetchDynamicValue() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::DynamicValueType, SBExpressionOptions,
+ GetFetchDynamicValue);
+
+ return m_opaque_up->GetUseDynamic();
+}
+
+void SBExpressionOptions::SetFetchDynamicValue(lldb::DynamicValueType dynamic) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetFetchDynamicValue,
+ (lldb::DynamicValueType), dynamic);
+
+ m_opaque_up->SetUseDynamic(dynamic);
+}
+
+uint32_t SBExpressionOptions::GetTimeoutInMicroSeconds() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBExpressionOptions,
+ GetTimeoutInMicroSeconds);
+
+ return m_opaque_up->GetTimeout() ? m_opaque_up->GetTimeout()->count() : 0;
+}
+
+void SBExpressionOptions::SetTimeoutInMicroSeconds(uint32_t timeout) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetTimeoutInMicroSeconds,
+ (uint32_t), timeout);
+
+ m_opaque_up->SetTimeout(timeout == 0 ? Timeout<std::micro>(llvm::None)
+ : std::chrono::microseconds(timeout));
+}
+
+uint32_t SBExpressionOptions::GetOneThreadTimeoutInMicroSeconds() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBExpressionOptions,
+ GetOneThreadTimeoutInMicroSeconds);
+
+ return m_opaque_up->GetOneThreadTimeout()
+ ? m_opaque_up->GetOneThreadTimeout()->count()
+ : 0;
+}
+
+void SBExpressionOptions::SetOneThreadTimeoutInMicroSeconds(uint32_t timeout) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions,
+ SetOneThreadTimeoutInMicroSeconds, (uint32_t), timeout);
+
+ m_opaque_up->SetOneThreadTimeout(timeout == 0
+ ? Timeout<std::micro>(llvm::None)
+ : std::chrono::microseconds(timeout));
+}
+
+bool SBExpressionOptions::GetTryAllThreads() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBExpressionOptions, GetTryAllThreads);
+
+ return m_opaque_up->GetTryAllThreads();
+}
+
+void SBExpressionOptions::SetTryAllThreads(bool run_others) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetTryAllThreads, (bool),
+ run_others);
+
+ m_opaque_up->SetTryAllThreads(run_others);
+}
+
+bool SBExpressionOptions::GetStopOthers() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBExpressionOptions, GetStopOthers);
+
+ return m_opaque_up->GetStopOthers();
+}
+
+void SBExpressionOptions::SetStopOthers(bool run_others) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetStopOthers, (bool),
+ run_others);
+
+ m_opaque_up->SetStopOthers(run_others);
+}
+
+bool SBExpressionOptions::GetTrapExceptions() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBExpressionOptions,
+ GetTrapExceptions);
+
+ return m_opaque_up->GetTrapExceptions();
+}
+
+void SBExpressionOptions::SetTrapExceptions(bool trap_exceptions) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetTrapExceptions, (bool),
+ trap_exceptions);
+
+ m_opaque_up->SetTrapExceptions(trap_exceptions);
+}
+
+void SBExpressionOptions::SetLanguage(lldb::LanguageType language) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetLanguage,
+ (lldb::LanguageType), language);
+
+ m_opaque_up->SetLanguage(language);
+}
+
+void SBExpressionOptions::SetCancelCallback(
+ lldb::ExpressionCancelCallback callback, void *baton) {
+ LLDB_RECORD_DUMMY(void, SBExpressionOptions, SetCancelCallback,
+ (lldb::ExpressionCancelCallback, void *), callback, baton);
+
+ m_opaque_up->SetCancelCallback(callback, baton);
+}
+
+bool SBExpressionOptions::GetGenerateDebugInfo() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBExpressionOptions, GetGenerateDebugInfo);
+
+ return m_opaque_up->GetGenerateDebugInfo();
+}
+
+void SBExpressionOptions::SetGenerateDebugInfo(bool b) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetGenerateDebugInfo, (bool),
+ b);
+
+ return m_opaque_up->SetGenerateDebugInfo(b);
+}
+
+bool SBExpressionOptions::GetSuppressPersistentResult() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBExpressionOptions,
+ GetSuppressPersistentResult);
+
+ return m_opaque_up->GetResultIsInternal();
+}
+
+void SBExpressionOptions::SetSuppressPersistentResult(bool b) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetSuppressPersistentResult,
+ (bool), b);
+
+ return m_opaque_up->SetResultIsInternal(b);
+}
+
+const char *SBExpressionOptions::GetPrefix() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBExpressionOptions,
+ GetPrefix);
+
+ return m_opaque_up->GetPrefix();
+}
+
+void SBExpressionOptions::SetPrefix(const char *prefix) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetPrefix, (const char *),
+ prefix);
+
+ return m_opaque_up->SetPrefix(prefix);
+}
+
+bool SBExpressionOptions::GetAutoApplyFixIts() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBExpressionOptions, GetAutoApplyFixIts);
+
+ return m_opaque_up->GetAutoApplyFixIts();
+}
+
+void SBExpressionOptions::SetAutoApplyFixIts(bool b) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetAutoApplyFixIts, (bool), b);
+
+ return m_opaque_up->SetAutoApplyFixIts(b);
+}
+
+bool SBExpressionOptions::GetTopLevel() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBExpressionOptions, GetTopLevel);
+
+ return m_opaque_up->GetExecutionPolicy() == eExecutionPolicyTopLevel;
+}
+
+void SBExpressionOptions::SetTopLevel(bool b) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetTopLevel, (bool), b);
+
+ m_opaque_up->SetExecutionPolicy(b ? eExecutionPolicyTopLevel
+ : m_opaque_up->default_execution_policy);
+}
+
+bool SBExpressionOptions::GetAllowJIT() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBExpressionOptions, GetAllowJIT);
+
+ return m_opaque_up->GetExecutionPolicy() != eExecutionPolicyNever;
+}
+
+void SBExpressionOptions::SetAllowJIT(bool allow) {
+ LLDB_RECORD_METHOD(void, SBExpressionOptions, SetAllowJIT, (bool), allow);
+
+ m_opaque_up->SetExecutionPolicy(allow ? m_opaque_up->default_execution_policy
+ : eExecutionPolicyNever);
+}
+
+EvaluateExpressionOptions *SBExpressionOptions::get() const {
+ return m_opaque_up.get();
+}
+
+EvaluateExpressionOptions &SBExpressionOptions::ref() const {
+ return *(m_opaque_up.get());
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBExpressionOptions>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBExpressionOptions, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBExpressionOptions,
+ (const lldb::SBExpressionOptions &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBExpressionOptions &,
+ SBExpressionOptions, operator=,(const lldb::SBExpressionOptions &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBExpressionOptions, GetCoerceResultToId,
+ ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetCoerceResultToId,
+ (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBExpressionOptions, GetUnwindOnError, ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetUnwindOnError, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBExpressionOptions, GetIgnoreBreakpoints,
+ ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetIgnoreBreakpoints,
+ (bool));
+ LLDB_REGISTER_METHOD_CONST(lldb::DynamicValueType, SBExpressionOptions,
+ GetFetchDynamicValue, ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetFetchDynamicValue,
+ (lldb::DynamicValueType));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBExpressionOptions,
+ GetTimeoutInMicroSeconds, ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetTimeoutInMicroSeconds,
+ (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBExpressionOptions,
+ GetOneThreadTimeoutInMicroSeconds, ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions,
+ SetOneThreadTimeoutInMicroSeconds, (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(bool, SBExpressionOptions, GetTryAllThreads, ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetTryAllThreads, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBExpressionOptions, GetStopOthers, ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetStopOthers, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBExpressionOptions, GetTrapExceptions,
+ ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetTrapExceptions, (bool));
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetLanguage,
+ (lldb::LanguageType));
+ LLDB_REGISTER_METHOD(bool, SBExpressionOptions, GetGenerateDebugInfo, ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetGenerateDebugInfo,
+ (bool));
+ LLDB_REGISTER_METHOD(bool, SBExpressionOptions, GetSuppressPersistentResult,
+ ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetSuppressPersistentResult,
+ (bool));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBExpressionOptions, GetPrefix,
+ ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetPrefix, (const char *));
+ LLDB_REGISTER_METHOD(bool, SBExpressionOptions, GetAutoApplyFixIts, ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetAutoApplyFixIts, (bool));
+ LLDB_REGISTER_METHOD(bool, SBExpressionOptions, GetTopLevel, ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetTopLevel, (bool));
+ LLDB_REGISTER_METHOD(bool, SBExpressionOptions, GetAllowJIT, ());
+ LLDB_REGISTER_METHOD(void, SBExpressionOptions, SetAllowJIT, (bool));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBFileSpec.cpp b/contrib/llvm-project/lldb/source/API/SBFileSpec.cpp
new file mode 100644
index 000000000000..2f910b9ba294
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBFileSpec.cpp
@@ -0,0 +1,223 @@
+//===-- SBFileSpec.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBFileSpec.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/SmallString.h"
+
+#include <inttypes.h>
+#include <limits.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBFileSpec::SBFileSpec() : m_opaque_up(new lldb_private::FileSpec()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBFileSpec);
+}
+
+SBFileSpec::SBFileSpec(const SBFileSpec &rhs) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBFileSpec, (const lldb::SBFileSpec &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+SBFileSpec::SBFileSpec(const lldb_private::FileSpec &fspec)
+ : m_opaque_up(new lldb_private::FileSpec(fspec)) {}
+
+// Deprecated!!!
+SBFileSpec::SBFileSpec(const char *path) : m_opaque_up(new FileSpec(path)) {
+ LLDB_RECORD_CONSTRUCTOR(SBFileSpec, (const char *), path);
+
+ FileSystem::Instance().Resolve(*m_opaque_up);
+}
+
+SBFileSpec::SBFileSpec(const char *path, bool resolve)
+ : m_opaque_up(new FileSpec(path)) {
+ LLDB_RECORD_CONSTRUCTOR(SBFileSpec, (const char *, bool), path, resolve);
+
+ if (resolve)
+ FileSystem::Instance().Resolve(*m_opaque_up);
+}
+
+SBFileSpec::~SBFileSpec() {}
+
+const SBFileSpec &SBFileSpec::operator=(const SBFileSpec &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBFileSpec &,
+ SBFileSpec, operator=,(const lldb::SBFileSpec &), rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBFileSpec::operator==(const SBFileSpec &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBFileSpec, operator==,(const SBFileSpec &rhs),
+ rhs);
+
+ return ref() == rhs.ref();
+}
+
+bool SBFileSpec::operator!=(const SBFileSpec &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBFileSpec, operator!=,(const SBFileSpec &rhs),
+ rhs);
+
+ return !(*this == rhs);
+}
+
+bool SBFileSpec::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFileSpec, IsValid);
+ return this->operator bool();
+}
+SBFileSpec::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFileSpec, operator bool);
+
+ return m_opaque_up->operator bool();
+}
+
+bool SBFileSpec::Exists() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFileSpec, Exists);
+
+ return FileSystem::Instance().Exists(*m_opaque_up);
+}
+
+bool SBFileSpec::ResolveExecutableLocation() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBFileSpec, ResolveExecutableLocation);
+
+ return FileSystem::Instance().ResolveExecutableLocation(*m_opaque_up);
+}
+
+int SBFileSpec::ResolvePath(const char *src_path, char *dst_path,
+ size_t dst_len) {
+ LLDB_RECORD_STATIC_METHOD(int, SBFileSpec, ResolvePath,
+ (const char *, char *, size_t), src_path, dst_path,
+ dst_len);
+
+ llvm::SmallString<64> result(src_path);
+ FileSystem::Instance().Resolve(result);
+ ::snprintf(dst_path, dst_len, "%s", result.c_str());
+ return std::min(dst_len - 1, result.size());
+}
+
+const char *SBFileSpec::GetFilename() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBFileSpec, GetFilename);
+
+ return m_opaque_up->GetFilename().AsCString();
+}
+
+const char *SBFileSpec::GetDirectory() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBFileSpec, GetDirectory);
+
+ FileSpec directory{*m_opaque_up};
+ directory.GetFilename().Clear();
+ return directory.GetCString();
+}
+
+void SBFileSpec::SetFilename(const char *filename) {
+ LLDB_RECORD_METHOD(void, SBFileSpec, SetFilename, (const char *), filename);
+
+ if (filename && filename[0])
+ m_opaque_up->GetFilename().SetCString(filename);
+ else
+ m_opaque_up->GetFilename().Clear();
+}
+
+void SBFileSpec::SetDirectory(const char *directory) {
+ LLDB_RECORD_METHOD(void, SBFileSpec, SetDirectory, (const char *), directory);
+
+ if (directory && directory[0])
+ m_opaque_up->GetDirectory().SetCString(directory);
+ else
+ m_opaque_up->GetDirectory().Clear();
+}
+
+uint32_t SBFileSpec::GetPath(char *dst_path, size_t dst_len) const {
+ LLDB_RECORD_METHOD_CONST(uint32_t, SBFileSpec, GetPath, (char *, size_t),
+ dst_path, dst_len);
+
+ uint32_t result = m_opaque_up->GetPath(dst_path, dst_len);
+
+ if (result == 0 && dst_path && dst_len > 0)
+ *dst_path = '\0';
+ return result;
+}
+
+const lldb_private::FileSpec *SBFileSpec::operator->() const {
+ return m_opaque_up.get();
+}
+
+const lldb_private::FileSpec *SBFileSpec::get() const {
+ return m_opaque_up.get();
+}
+
+const lldb_private::FileSpec &SBFileSpec::operator*() const {
+ return *m_opaque_up;
+}
+
+const lldb_private::FileSpec &SBFileSpec::ref() const { return *m_opaque_up; }
+
+void SBFileSpec::SetFileSpec(const lldb_private::FileSpec &fs) {
+ *m_opaque_up = fs;
+}
+
+bool SBFileSpec::GetDescription(SBStream &description) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBFileSpec, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+ char path[PATH_MAX];
+ if (m_opaque_up->GetPath(path, sizeof(path)))
+ strm.PutCString(path);
+ return true;
+}
+
+void SBFileSpec::AppendPathComponent(const char *fn) {
+ LLDB_RECORD_METHOD(void, SBFileSpec, AppendPathComponent, (const char *), fn);
+
+ m_opaque_up->AppendPathComponent(fn);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBFileSpec>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBFileSpec, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBFileSpec, (const lldb::SBFileSpec &));
+ LLDB_REGISTER_CONSTRUCTOR(SBFileSpec, (const char *));
+ LLDB_REGISTER_CONSTRUCTOR(SBFileSpec, (const char *, bool));
+ LLDB_REGISTER_METHOD(const lldb::SBFileSpec &,
+ SBFileSpec, operator=,(const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBFileSpec, operator==,(const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBFileSpec, operator!=,(const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBFileSpec, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBFileSpec, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBFileSpec, Exists, ());
+ LLDB_REGISTER_METHOD(bool, SBFileSpec, ResolveExecutableLocation, ());
+ LLDB_REGISTER_STATIC_METHOD(int, SBFileSpec, ResolvePath,
+ (const char *, char *, size_t));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBFileSpec, GetFilename, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBFileSpec, GetDirectory, ());
+ LLDB_REGISTER_METHOD(void, SBFileSpec, SetFilename, (const char *));
+ LLDB_REGISTER_METHOD(void, SBFileSpec, SetDirectory, (const char *));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBFileSpec, GetPath, (char *, size_t));
+ LLDB_REGISTER_METHOD_CONST(bool, SBFileSpec, GetDescription,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(void, SBFileSpec, AppendPathComponent, (const char *));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBFileSpecList.cpp b/contrib/llvm-project/lldb/source/API/SBFileSpecList.cpp
new file mode 100644
index 000000000000..3143964b38cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBFileSpecList.cpp
@@ -0,0 +1,152 @@
+//===-- SBFileSpecList.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBFileSpecList.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Stream.h"
+
+#include <limits.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBFileSpecList::SBFileSpecList() : m_opaque_up(new FileSpecList()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBFileSpecList);
+}
+
+SBFileSpecList::SBFileSpecList(const SBFileSpecList &rhs) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBFileSpecList, (const lldb::SBFileSpecList &), rhs);
+
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+SBFileSpecList::~SBFileSpecList() {}
+
+const SBFileSpecList &SBFileSpecList::operator=(const SBFileSpecList &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBFileSpecList &,
+ SBFileSpecList, operator=,(const lldb::SBFileSpecList &),
+ rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+uint32_t SBFileSpecList::GetSize() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBFileSpecList, GetSize);
+
+ return m_opaque_up->GetSize();
+}
+
+void SBFileSpecList::Append(const SBFileSpec &sb_file) {
+ LLDB_RECORD_METHOD(void, SBFileSpecList, Append, (const lldb::SBFileSpec &),
+ sb_file);
+
+ m_opaque_up->Append(sb_file.ref());
+}
+
+bool SBFileSpecList::AppendIfUnique(const SBFileSpec &sb_file) {
+ LLDB_RECORD_METHOD(bool, SBFileSpecList, AppendIfUnique,
+ (const lldb::SBFileSpec &), sb_file);
+
+ return m_opaque_up->AppendIfUnique(sb_file.ref());
+}
+
+void SBFileSpecList::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBFileSpecList, Clear);
+
+ m_opaque_up->Clear();
+}
+
+uint32_t SBFileSpecList::FindFileIndex(uint32_t idx, const SBFileSpec &sb_file,
+ bool full) {
+ LLDB_RECORD_METHOD(uint32_t, SBFileSpecList, FindFileIndex,
+ (uint32_t, const lldb::SBFileSpec &, bool), idx, sb_file,
+ full);
+
+ return m_opaque_up->FindFileIndex(idx, sb_file.ref(), full);
+}
+
+const SBFileSpec SBFileSpecList::GetFileSpecAtIndex(uint32_t idx) const {
+ LLDB_RECORD_METHOD_CONST(const lldb::SBFileSpec, SBFileSpecList,
+ GetFileSpecAtIndex, (uint32_t), idx);
+
+ SBFileSpec new_spec;
+ new_spec.SetFileSpec(m_opaque_up->GetFileSpecAtIndex(idx));
+ return LLDB_RECORD_RESULT(new_spec);
+}
+
+const lldb_private::FileSpecList *SBFileSpecList::operator->() const {
+ return m_opaque_up.get();
+}
+
+const lldb_private::FileSpecList *SBFileSpecList::get() const {
+ return m_opaque_up.get();
+}
+
+const lldb_private::FileSpecList &SBFileSpecList::operator*() const {
+ return *m_opaque_up;
+}
+
+const lldb_private::FileSpecList &SBFileSpecList::ref() const {
+ return *m_opaque_up;
+}
+
+bool SBFileSpecList::GetDescription(SBStream &description) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBFileSpecList, GetDescription,
+ (lldb::SBStream &), description);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_up) {
+ uint32_t num_files = m_opaque_up->GetSize();
+ strm.Printf("%d files: ", num_files);
+ for (uint32_t i = 0; i < num_files; i++) {
+ char path[PATH_MAX];
+ if (m_opaque_up->GetFileSpecAtIndex(i).GetPath(path, sizeof(path)))
+ strm.Printf("\n %s", path);
+ }
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBFileSpecList>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBFileSpecList, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBFileSpecList, (const lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBFileSpecList &,
+ SBFileSpecList, operator=,(const lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBFileSpecList, GetSize, ());
+ LLDB_REGISTER_METHOD(void, SBFileSpecList, Append,
+ (const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(bool, SBFileSpecList, AppendIfUnique,
+ (const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(void, SBFileSpecList, Clear, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBFileSpecList, FindFileIndex,
+ (uint32_t, const lldb::SBFileSpec &, bool));
+ LLDB_REGISTER_METHOD_CONST(const lldb::SBFileSpec, SBFileSpecList,
+ GetFileSpecAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(bool, SBFileSpecList, GetDescription,
+ (lldb::SBStream &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBFrame.cpp b/contrib/llvm-project/lldb/source/API/SBFrame.cpp
new file mode 100644
index 000000000000..9268f0f9bdbf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBFrame.cpp
@@ -0,0 +1,1371 @@
+//===-- SBFrame.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+#include <set>
+#include <string>
+
+#include "lldb/API/SBFrame.h"
+
+#include "lldb/lldb-types.h"
+
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StackFrameRecognizer.h"
+#include "lldb/Target/StackID.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBExpressionOptions.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBSymbolContext.h"
+#include "lldb/API/SBThread.h"
+#include "lldb/API/SBValue.h"
+#include "lldb/API/SBVariablesOptions.h"
+
+#include "llvm/Support/PrettyStackTrace.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBFrame::SBFrame() : m_opaque_sp(new ExecutionContextRef()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBFrame);
+}
+
+SBFrame::SBFrame(const StackFrameSP &lldb_object_sp)
+ : m_opaque_sp(new ExecutionContextRef(lldb_object_sp)) {
+ LLDB_RECORD_CONSTRUCTOR(SBFrame, (const lldb::StackFrameSP &),
+ lldb_object_sp);
+}
+
+SBFrame::SBFrame(const SBFrame &rhs) : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR(SBFrame, (const lldb::SBFrame &), rhs);
+
+ m_opaque_sp = clone(rhs.m_opaque_sp);
+}
+
+SBFrame::~SBFrame() = default;
+
+const SBFrame &SBFrame::operator=(const SBFrame &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBFrame &,
+ SBFrame, operator=,(const lldb::SBFrame &), rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = clone(rhs.m_opaque_sp);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+StackFrameSP SBFrame::GetFrameSP() const {
+ return (m_opaque_sp ? m_opaque_sp->GetFrameSP() : StackFrameSP());
+}
+
+void SBFrame::SetFrameSP(const StackFrameSP &lldb_object_sp) {
+ return m_opaque_sp->SetFrameSP(lldb_object_sp);
+}
+
+bool SBFrame::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFrame, IsValid);
+ return this->operator bool();
+}
+SBFrame::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFrame, operator bool);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock()))
+ return GetFrameSP().get() != nullptr;
+ }
+
+ // Without a target & process we can't have a valid stack frame.
+ return false;
+}
+
+SBSymbolContext SBFrame::GetSymbolContext(uint32_t resolve_scope) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBSymbolContext, SBFrame, GetSymbolContext,
+ (uint32_t), resolve_scope);
+
+ SBSymbolContext sb_sym_ctx;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ SymbolContextItem scope = static_cast<SymbolContextItem>(resolve_scope);
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame)
+ sb_sym_ctx.SetSymbolContext(&frame->GetSymbolContext(scope));
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_sym_ctx);
+}
+
+SBModule SBFrame::GetModule() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBModule, SBFrame, GetModule);
+
+ SBModule sb_module;
+ ModuleSP module_sp;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ module_sp = frame->GetSymbolContext(eSymbolContextModule).module_sp;
+ sb_module.SetSP(module_sp);
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_module);
+}
+
+SBCompileUnit SBFrame::GetCompileUnit() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBCompileUnit, SBFrame,
+ GetCompileUnit);
+
+ SBCompileUnit sb_comp_unit;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ sb_comp_unit.reset(
+ frame->GetSymbolContext(eSymbolContextCompUnit).comp_unit);
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_comp_unit);
+}
+
+SBFunction SBFrame::GetFunction() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBFunction, SBFrame, GetFunction);
+
+ SBFunction sb_function;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ sb_function.reset(
+ frame->GetSymbolContext(eSymbolContextFunction).function);
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_function);
+}
+
+SBSymbol SBFrame::GetSymbol() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBSymbol, SBFrame, GetSymbol);
+
+ SBSymbol sb_symbol;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ sb_symbol.reset(frame->GetSymbolContext(eSymbolContextSymbol).symbol);
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_symbol);
+}
+
+SBBlock SBFrame::GetBlock() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBBlock, SBFrame, GetBlock);
+
+ SBBlock sb_block;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame)
+ sb_block.SetPtr(frame->GetSymbolContext(eSymbolContextBlock).block);
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_block);
+}
+
+SBBlock SBFrame::GetFrameBlock() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBBlock, SBFrame, GetFrameBlock);
+
+ SBBlock sb_block;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame)
+ sb_block.SetPtr(frame->GetFrameBlock());
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_block);
+}
+
+SBLineEntry SBFrame::GetLineEntry() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBLineEntry, SBFrame, GetLineEntry);
+
+ SBLineEntry sb_line_entry;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ sb_line_entry.SetLineEntry(
+ frame->GetSymbolContext(eSymbolContextLineEntry).line_entry);
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_line_entry);
+}
+
+uint32_t SBFrame::GetFrameID() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBFrame, GetFrameID);
+
+ uint32_t frame_idx = UINT32_MAX;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame)
+ frame_idx = frame->GetFrameIndex();
+
+ return frame_idx;
+}
+
+lldb::addr_t SBFrame::GetCFA() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::addr_t, SBFrame, GetCFA);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame)
+ return frame->GetStackID().GetCallFrameAddress();
+ return LLDB_INVALID_ADDRESS;
+}
+
+addr_t SBFrame::GetPC() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::addr_t, SBFrame, GetPC);
+
+ addr_t addr = LLDB_INVALID_ADDRESS;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ addr = frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
+ target, AddressClass::eCode);
+ }
+ }
+ }
+
+ return addr;
+}
+
+bool SBFrame::SetPC(addr_t new_pc) {
+ LLDB_RECORD_METHOD(bool, SBFrame, SetPC, (lldb::addr_t), new_pc);
+
+ bool ret_val = false;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ ret_val = frame->GetRegisterContext()->SetPC(new_pc);
+ }
+ }
+ }
+
+ return ret_val;
+}
+
+addr_t SBFrame::GetSP() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::addr_t, SBFrame, GetSP);
+
+ addr_t addr = LLDB_INVALID_ADDRESS;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ addr = frame->GetRegisterContext()->GetSP();
+ }
+ }
+ }
+
+ return addr;
+}
+
+addr_t SBFrame::GetFP() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::addr_t, SBFrame, GetFP);
+
+ addr_t addr = LLDB_INVALID_ADDRESS;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame)
+ addr = frame->GetRegisterContext()->GetFP();
+ }
+ }
+
+ return addr;
+}
+
+SBAddress SBFrame::GetPCAddress() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBAddress, SBFrame, GetPCAddress);
+
+ SBAddress sb_addr;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame)
+ sb_addr.SetAddress(&frame->GetFrameCodeAddress());
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_addr);
+}
+
+void SBFrame::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBFrame, Clear);
+
+ m_opaque_sp->Clear();
+}
+
+lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBFrame, GetValueForVariablePath,
+ (const char *), var_path);
+
+ SBValue sb_value;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ Target *target = exe_ctx.GetTargetPtr();
+ if (frame && target) {
+ lldb::DynamicValueType use_dynamic =
+ frame->CalculateTarget()->GetPreferDynamicValue();
+ sb_value = GetValueForVariablePath(var_path, use_dynamic);
+ }
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
+ DynamicValueType use_dynamic) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBFrame, GetValueForVariablePath,
+ (const char *, lldb::DynamicValueType), var_path,
+ use_dynamic);
+
+ SBValue sb_value;
+ if (var_path == nullptr || var_path[0] == '\0') {
+ return LLDB_RECORD_RESULT(sb_value);
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ VariableSP var_sp;
+ Status error;
+ ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath(
+ var_path, eNoDynamicValues,
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
+ var_sp, error));
+ sb_value.SetSP(value_sp, use_dynamic);
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+SBValue SBFrame::FindVariable(const char *name) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBFrame, FindVariable, (const char *),
+ name);
+
+ SBValue value;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ Target *target = exe_ctx.GetTargetPtr();
+ if (frame && target) {
+ lldb::DynamicValueType use_dynamic =
+ frame->CalculateTarget()->GetPreferDynamicValue();
+ value = FindVariable(name, use_dynamic);
+ }
+ return LLDB_RECORD_RESULT(value);
+}
+
+SBValue SBFrame::FindVariable(const char *name,
+ lldb::DynamicValueType use_dynamic) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBFrame, FindVariable,
+ (const char *, lldb::DynamicValueType), name, use_dynamic);
+
+ VariableSP var_sp;
+ SBValue sb_value;
+
+ if (name == nullptr || name[0] == '\0') {
+ return LLDB_RECORD_RESULT(sb_value);
+ }
+
+ ValueObjectSP value_sp;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ value_sp = frame->FindVariable(ConstString(name));
+
+ if (value_sp)
+ sb_value.SetSP(value_sp, use_dynamic);
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+SBValue SBFrame::FindValue(const char *name, ValueType value_type) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBFrame, FindValue,
+ (const char *, lldb::ValueType), name, value_type);
+
+ SBValue value;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ Target *target = exe_ctx.GetTargetPtr();
+ if (frame && target) {
+ lldb::DynamicValueType use_dynamic =
+ frame->CalculateTarget()->GetPreferDynamicValue();
+ value = FindValue(name, value_type, use_dynamic);
+ }
+ return LLDB_RECORD_RESULT(value);
+}
+
+SBValue SBFrame::FindValue(const char *name, ValueType value_type,
+ lldb::DynamicValueType use_dynamic) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBFrame, FindValue,
+ (const char *, lldb::ValueType, lldb::DynamicValueType),
+ name, value_type, use_dynamic);
+
+ SBValue sb_value;
+
+ if (name == nullptr || name[0] == '\0') {
+ return LLDB_RECORD_RESULT(sb_value);
+ }
+
+ ValueObjectSP value_sp;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ VariableList variable_list;
+
+ switch (value_type) {
+ case eValueTypeVariableGlobal: // global variable
+ case eValueTypeVariableStatic: // static variable
+ case eValueTypeVariableArgument: // function argument variables
+ case eValueTypeVariableLocal: // function local variables
+ case eValueTypeVariableThreadLocal: // thread local variables
+ {
+ SymbolContext sc(frame->GetSymbolContext(eSymbolContextBlock));
+
+ const bool can_create = true;
+ const bool get_parent_variables = true;
+ const bool stop_if_block_is_inlined_function = true;
+
+ if (sc.block)
+ sc.block->AppendVariables(
+ can_create, get_parent_variables,
+ stop_if_block_is_inlined_function,
+ [frame](Variable *v) { return v->IsInScope(frame); },
+ &variable_list);
+ if (value_type == eValueTypeVariableGlobal) {
+ const bool get_file_globals = true;
+ VariableList *frame_vars = frame->GetVariableList(get_file_globals);
+ if (frame_vars)
+ frame_vars->AppendVariablesIfUnique(variable_list);
+ }
+ ConstString const_name(name);
+ VariableSP variable_sp(
+ variable_list.FindVariable(const_name, value_type));
+ if (variable_sp) {
+ value_sp = frame->GetValueObjectForFrameVariable(variable_sp,
+ eNoDynamicValues);
+ sb_value.SetSP(value_sp, use_dynamic);
+ }
+ } break;
+
+ case eValueTypeRegister: // stack frame register value
+ {
+ RegisterContextSP reg_ctx(frame->GetRegisterContext());
+ if (reg_ctx) {
+ const uint32_t num_regs = reg_ctx->GetRegisterCount();
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoAtIndex(reg_idx);
+ if (reg_info &&
+ ((reg_info->name && strcasecmp(reg_info->name, name) == 0) ||
+ (reg_info->alt_name &&
+ strcasecmp(reg_info->alt_name, name) == 0))) {
+ value_sp = ValueObjectRegister::Create(frame, reg_ctx, reg_idx);
+ sb_value.SetSP(value_sp);
+ break;
+ }
+ }
+ }
+ } break;
+
+ case eValueTypeRegisterSet: // A collection of stack frame register
+ // values
+ {
+ RegisterContextSP reg_ctx(frame->GetRegisterContext());
+ if (reg_ctx) {
+ const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
+ for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
+ const RegisterSet *reg_set = reg_ctx->GetRegisterSet(set_idx);
+ if (reg_set &&
+ ((reg_set->name && strcasecmp(reg_set->name, name) == 0) ||
+ (reg_set->short_name &&
+ strcasecmp(reg_set->short_name, name) == 0))) {
+ value_sp =
+ ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx);
+ sb_value.SetSP(value_sp);
+ break;
+ }
+ }
+ }
+ } break;
+
+ case eValueTypeConstResult: // constant result variables
+ {
+ ConstString const_name(name);
+ ExpressionVariableSP expr_var_sp(
+ target->GetPersistentVariable(const_name));
+ if (expr_var_sp) {
+ value_sp = expr_var_sp->GetValueObject();
+ sb_value.SetSP(value_sp, use_dynamic);
+ }
+ } break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+bool SBFrame::IsEqual(const SBFrame &that) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBFrame, IsEqual, (const lldb::SBFrame &),
+ that);
+
+ lldb::StackFrameSP this_sp = GetFrameSP();
+ lldb::StackFrameSP that_sp = that.GetFrameSP();
+ return (this_sp && that_sp && this_sp->GetStackID() == that_sp->GetStackID());
+}
+
+bool SBFrame::operator==(const SBFrame &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBFrame, operator==,(const lldb::SBFrame &),
+ rhs);
+
+ return IsEqual(rhs);
+}
+
+bool SBFrame::operator!=(const SBFrame &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBFrame, operator!=,(const lldb::SBFrame &),
+ rhs);
+
+ return !IsEqual(rhs);
+}
+
+SBThread SBFrame::GetThread() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBThread, SBFrame, GetThread);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ ThreadSP thread_sp(exe_ctx.GetThreadSP());
+ SBThread sb_thread(thread_sp);
+
+ return LLDB_RECORD_RESULT(sb_thread);
+}
+
+const char *SBFrame::Disassemble() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBFrame, Disassemble);
+
+ const char *disassembly = nullptr;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ disassembly = frame->Disassemble();
+ }
+ }
+ }
+
+ return disassembly;
+}
+
+SBValueList SBFrame::GetVariables(bool arguments, bool locals, bool statics,
+ bool in_scope_only) {
+ LLDB_RECORD_METHOD(lldb::SBValueList, SBFrame, GetVariables,
+ (bool, bool, bool, bool), arguments, locals, statics,
+ in_scope_only);
+
+ SBValueList value_list;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ Target *target = exe_ctx.GetTargetPtr();
+ if (frame && target) {
+ lldb::DynamicValueType use_dynamic =
+ frame->CalculateTarget()->GetPreferDynamicValue();
+ const bool include_runtime_support_values =
+ target ? target->GetDisplayRuntimeSupportValues() : false;
+
+ SBVariablesOptions options;
+ options.SetIncludeArguments(arguments);
+ options.SetIncludeLocals(locals);
+ options.SetIncludeStatics(statics);
+ options.SetInScopeOnly(in_scope_only);
+ options.SetIncludeRuntimeSupportValues(include_runtime_support_values);
+ options.SetUseDynamic(use_dynamic);
+
+ value_list = GetVariables(options);
+ }
+ return LLDB_RECORD_RESULT(value_list);
+}
+
+lldb::SBValueList SBFrame::GetVariables(bool arguments, bool locals,
+ bool statics, bool in_scope_only,
+ lldb::DynamicValueType use_dynamic) {
+ LLDB_RECORD_METHOD(lldb::SBValueList, SBFrame, GetVariables,
+ (bool, bool, bool, bool, lldb::DynamicValueType),
+ arguments, locals, statics, in_scope_only, use_dynamic);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ Target *target = exe_ctx.GetTargetPtr();
+ const bool include_runtime_support_values =
+ target ? target->GetDisplayRuntimeSupportValues() : false;
+ SBVariablesOptions options;
+ options.SetIncludeArguments(arguments);
+ options.SetIncludeLocals(locals);
+ options.SetIncludeStatics(statics);
+ options.SetInScopeOnly(in_scope_only);
+ options.SetIncludeRuntimeSupportValues(include_runtime_support_values);
+ options.SetUseDynamic(use_dynamic);
+ return LLDB_RECORD_RESULT(GetVariables(options));
+}
+
+SBValueList SBFrame::GetVariables(const lldb::SBVariablesOptions &options) {
+ LLDB_RECORD_METHOD(lldb::SBValueList, SBFrame, GetVariables,
+ (const lldb::SBVariablesOptions &), options);
+
+ SBValueList value_list;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+
+ const bool statics = options.GetIncludeStatics();
+ const bool arguments = options.GetIncludeArguments();
+ const bool recognized_arguments =
+ options.GetIncludeRecognizedArguments(SBTarget(exe_ctx.GetTargetSP()));
+ const bool locals = options.GetIncludeLocals();
+ const bool in_scope_only = options.GetInScopeOnly();
+ const bool include_runtime_support_values =
+ options.GetIncludeRuntimeSupportValues();
+ const lldb::DynamicValueType use_dynamic = options.GetUseDynamic();
+
+
+ std::set<VariableSP> variable_set;
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ size_t i;
+ VariableList *variable_list = nullptr;
+ variable_list = frame->GetVariableList(true);
+ if (variable_list) {
+ const size_t num_variables = variable_list->GetSize();
+ if (num_variables) {
+ for (i = 0; i < num_variables; ++i) {
+ VariableSP variable_sp(variable_list->GetVariableAtIndex(i));
+ if (variable_sp) {
+ bool add_variable = false;
+ switch (variable_sp->GetScope()) {
+ case eValueTypeVariableGlobal:
+ case eValueTypeVariableStatic:
+ case eValueTypeVariableThreadLocal:
+ add_variable = statics;
+ break;
+
+ case eValueTypeVariableArgument:
+ add_variable = arguments;
+ break;
+
+ case eValueTypeVariableLocal:
+ add_variable = locals;
+ break;
+
+ default:
+ break;
+ }
+ if (add_variable) {
+ // Only add variables once so we don't end up with duplicates
+ if (variable_set.find(variable_sp) == variable_set.end())
+ variable_set.insert(variable_sp);
+ else
+ continue;
+
+ if (in_scope_only && !variable_sp->IsInScope(frame))
+ continue;
+
+ ValueObjectSP valobj_sp(frame->GetValueObjectForFrameVariable(
+ variable_sp, eNoDynamicValues));
+
+ if (!include_runtime_support_values && valobj_sp != nullptr &&
+ valobj_sp->IsRuntimeSupportValue())
+ continue;
+
+ SBValue value_sb;
+ value_sb.SetSP(valobj_sp, use_dynamic);
+ value_list.Append(value_sb);
+ }
+ }
+ }
+ }
+ }
+ if (recognized_arguments) {
+ auto recognized_frame = frame->GetRecognizedFrame();
+ if (recognized_frame) {
+ ValueObjectListSP recognized_arg_list =
+ recognized_frame->GetRecognizedArguments();
+ if (recognized_arg_list) {
+ for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
+ SBValue value_sb;
+ value_sb.SetSP(rec_value_sp, use_dynamic);
+ value_list.Append(value_sb);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(value_list);
+}
+
+SBValueList SBFrame::GetRegisters() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBValueList, SBFrame, GetRegisters);
+
+ SBValueList value_list;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ RegisterContextSP reg_ctx(frame->GetRegisterContext());
+ if (reg_ctx) {
+ const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
+ for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
+ value_list.Append(
+ ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
+ }
+ }
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(value_list);
+}
+
+SBValue SBFrame::FindRegister(const char *name) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBFrame, FindRegister, (const char *),
+ name);
+
+ SBValue result;
+ ValueObjectSP value_sp;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ RegisterContextSP reg_ctx(frame->GetRegisterContext());
+ if (reg_ctx) {
+ const uint32_t num_regs = reg_ctx->GetRegisterCount();
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoAtIndex(reg_idx);
+ if (reg_info &&
+ ((reg_info->name && strcasecmp(reg_info->name, name) == 0) ||
+ (reg_info->alt_name &&
+ strcasecmp(reg_info->alt_name, name) == 0))) {
+ value_sp = ValueObjectRegister::Create(frame, reg_ctx, reg_idx);
+ result.SetSP(value_sp);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(result);
+}
+
+bool SBFrame::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBFrame, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ frame->DumpUsingSettingsFormat(&strm);
+ }
+ }
+
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+SBValue SBFrame::EvaluateExpression(const char *expr) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBFrame, EvaluateExpression, (const char *),
+ expr);
+
+ SBValue result;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ Target *target = exe_ctx.GetTargetPtr();
+ if (frame && target) {
+ SBExpressionOptions options;
+ lldb::DynamicValueType fetch_dynamic_value =
+ frame->CalculateTarget()->GetPreferDynamicValue();
+ options.SetFetchDynamicValue(fetch_dynamic_value);
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ if (target->GetLanguage() != eLanguageTypeUnknown)
+ options.SetLanguage(target->GetLanguage());
+ else
+ options.SetLanguage(frame->GetLanguage());
+ return LLDB_RECORD_RESULT(EvaluateExpression(expr, options));
+ }
+ return LLDB_RECORD_RESULT(result);
+}
+
+SBValue
+SBFrame::EvaluateExpression(const char *expr,
+ lldb::DynamicValueType fetch_dynamic_value) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBFrame, EvaluateExpression,
+ (const char *, lldb::DynamicValueType), expr,
+ fetch_dynamic_value);
+
+ SBExpressionOptions options;
+ options.SetFetchDynamicValue(fetch_dynamic_value);
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target && target->GetLanguage() != eLanguageTypeUnknown)
+ options.SetLanguage(target->GetLanguage());
+ else if (frame)
+ options.SetLanguage(frame->GetLanguage());
+ return LLDB_RECORD_RESULT(EvaluateExpression(expr, options));
+}
+
+SBValue SBFrame::EvaluateExpression(const char *expr,
+ lldb::DynamicValueType fetch_dynamic_value,
+ bool unwind_on_error) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBFrame, EvaluateExpression,
+ (const char *, lldb::DynamicValueType, bool), expr,
+ fetch_dynamic_value, unwind_on_error);
+
+ SBExpressionOptions options;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ options.SetFetchDynamicValue(fetch_dynamic_value);
+ options.SetUnwindOnError(unwind_on_error);
+ options.SetIgnoreBreakpoints(true);
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target && target->GetLanguage() != eLanguageTypeUnknown)
+ options.SetLanguage(target->GetLanguage());
+ else if (frame)
+ options.SetLanguage(frame->GetLanguage());
+ return LLDB_RECORD_RESULT(EvaluateExpression(expr, options));
+}
+
+lldb::SBValue SBFrame::EvaluateExpression(const char *expr,
+ const SBExpressionOptions &options) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBFrame, EvaluateExpression,
+ (const char *, const lldb::SBExpressionOptions &), expr,
+ options);
+
+ Log *expr_log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ SBValue expr_result;
+
+ if (expr == nullptr || expr[0] == '\0') {
+ return LLDB_RECORD_RESULT(expr_result);
+ }
+
+ ValueObjectSP expr_value_sp;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ std::unique_ptr<llvm::PrettyStackTraceFormat> stack_trace;
+ if (target->GetDisplayExpressionsInCrashlogs()) {
+ StreamString frame_description;
+ frame->DumpUsingSettingsFormat(&frame_description);
+ stack_trace = llvm::make_unique<llvm::PrettyStackTraceFormat>(
+ "SBFrame::EvaluateExpression (expr = \"%s\", fetch_dynamic_value "
+ "= %u) %s",
+ expr, options.GetFetchDynamicValue(),
+ frame_description.GetData());
+ }
+
+ target->EvaluateExpression(expr, frame, expr_value_sp, options.ref());
+ expr_result.SetSP(expr_value_sp, options.GetFetchDynamicValue());
+ }
+ }
+ }
+
+ if (expr_log)
+ expr_log->Printf("** [SBFrame::EvaluateExpression] Expression result is "
+ "%s, summary %s **",
+ expr_result.GetValue(), expr_result.GetSummary());
+
+ return LLDB_RECORD_RESULT(expr_result);
+}
+
+bool SBFrame::IsInlined() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBFrame, IsInlined);
+
+ return static_cast<const SBFrame *>(this)->IsInlined();
+}
+
+bool SBFrame::IsInlined() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFrame, IsInlined);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+
+ Block *block = frame->GetSymbolContext(eSymbolContextBlock).block;
+ if (block)
+ return block->GetContainingInlinedBlock() != nullptr;
+ }
+ }
+ }
+ return false;
+}
+
+bool SBFrame::IsArtificial() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBFrame, IsArtificial);
+
+ return static_cast<const SBFrame *>(this)->IsArtificial();
+}
+
+bool SBFrame::IsArtificial() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFrame, IsArtificial);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame)
+ return frame->IsArtificial();
+
+ return false;
+}
+
+const char *SBFrame::GetFunctionName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBFrame, GetFunctionName);
+
+ return static_cast<const SBFrame *>(this)->GetFunctionName();
+}
+
+lldb::LanguageType SBFrame::GuessLanguage() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::LanguageType, SBFrame, GuessLanguage);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ return frame->GuessLanguage();
+ }
+ }
+ }
+ return eLanguageTypeUnknown;
+}
+
+const char *SBFrame::GetFunctionName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBFrame, GetFunctionName);
+
+ const char *name = nullptr;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
+ eSymbolContextBlock |
+ eSymbolContextSymbol));
+ if (sc.block) {
+ Block *inlined_block = sc.block->GetContainingInlinedBlock();
+ if (inlined_block) {
+ const InlineFunctionInfo *inlined_info =
+ inlined_block->GetInlinedFunctionInfo();
+ name =
+ inlined_info->GetName(sc.function->GetLanguage()).AsCString();
+ }
+ }
+
+ if (name == nullptr) {
+ if (sc.function)
+ name = sc.function->GetName().GetCString();
+ }
+
+ if (name == nullptr) {
+ if (sc.symbol)
+ name = sc.symbol->GetName().GetCString();
+ }
+ }
+ }
+ }
+ return name;
+}
+
+const char *SBFrame::GetDisplayFunctionName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBFrame, GetDisplayFunctionName);
+
+ const char *name = nullptr;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
+ eSymbolContextBlock |
+ eSymbolContextSymbol));
+ if (sc.block) {
+ Block *inlined_block = sc.block->GetContainingInlinedBlock();
+ if (inlined_block) {
+ const InlineFunctionInfo *inlined_info =
+ inlined_block->GetInlinedFunctionInfo();
+ name = inlined_info->GetDisplayName(sc.function->GetLanguage())
+ .AsCString();
+ }
+ }
+
+ if (name == nullptr) {
+ if (sc.function)
+ name = sc.function->GetDisplayName().GetCString();
+ }
+
+ if (name == nullptr) {
+ if (sc.symbol)
+ name = sc.symbol->GetDisplayName().GetCString();
+ }
+ }
+ }
+ }
+ return name;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBFrame>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBFrame, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBFrame, (const lldb::StackFrameSP &));
+ LLDB_REGISTER_CONSTRUCTOR(SBFrame, (const lldb::SBFrame &));
+ LLDB_REGISTER_METHOD(const lldb::SBFrame &,
+ SBFrame, operator=,(const lldb::SBFrame &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBFrame, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBFrame, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBSymbolContext, SBFrame, GetSymbolContext,
+ (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBModule, SBFrame, GetModule, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBCompileUnit, SBFrame, GetCompileUnit,
+ ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBFunction, SBFrame, GetFunction, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBSymbol, SBFrame, GetSymbol, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBBlock, SBFrame, GetBlock, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBBlock, SBFrame, GetFrameBlock, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBLineEntry, SBFrame, GetLineEntry, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBFrame, GetFrameID, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::addr_t, SBFrame, GetCFA, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::addr_t, SBFrame, GetPC, ());
+ LLDB_REGISTER_METHOD(bool, SBFrame, SetPC, (lldb::addr_t));
+ LLDB_REGISTER_METHOD_CONST(lldb::addr_t, SBFrame, GetSP, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::addr_t, SBFrame, GetFP, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBAddress, SBFrame, GetPCAddress, ());
+ LLDB_REGISTER_METHOD(void, SBFrame, Clear, ());
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBFrame, GetValueForVariablePath,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBFrame, GetValueForVariablePath,
+ (const char *, lldb::DynamicValueType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBFrame, FindVariable, (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBFrame, FindVariable,
+ (const char *, lldb::DynamicValueType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBFrame, FindValue,
+ (const char *, lldb::ValueType));
+ LLDB_REGISTER_METHOD(
+ lldb::SBValue, SBFrame, FindValue,
+ (const char *, lldb::ValueType, lldb::DynamicValueType));
+ LLDB_REGISTER_METHOD_CONST(bool, SBFrame, IsEqual, (const lldb::SBFrame &));
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBFrame, operator==,(const lldb::SBFrame &));
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBFrame, operator!=,(const lldb::SBFrame &));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBThread, SBFrame, GetThread, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBFrame, Disassemble, ());
+ LLDB_REGISTER_METHOD(lldb::SBValueList, SBFrame, GetVariables,
+ (bool, bool, bool, bool));
+ LLDB_REGISTER_METHOD(lldb::SBValueList, SBFrame, GetVariables,
+ (bool, bool, bool, bool, lldb::DynamicValueType));
+ LLDB_REGISTER_METHOD(lldb::SBValueList, SBFrame, GetVariables,
+ (const lldb::SBVariablesOptions &));
+ LLDB_REGISTER_METHOD(lldb::SBValueList, SBFrame, GetRegisters, ());
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBFrame, FindRegister, (const char *));
+ LLDB_REGISTER_METHOD(bool, SBFrame, GetDescription, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBFrame, EvaluateExpression,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBFrame, EvaluateExpression,
+ (const char *, lldb::DynamicValueType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBFrame, EvaluateExpression,
+ (const char *, lldb::DynamicValueType, bool));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBFrame, EvaluateExpression,
+ (const char *, const lldb::SBExpressionOptions &));
+ LLDB_REGISTER_METHOD(bool, SBFrame, IsInlined, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBFrame, IsInlined, ());
+ LLDB_REGISTER_METHOD(bool, SBFrame, IsArtificial, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBFrame, IsArtificial, ());
+ LLDB_REGISTER_METHOD(const char *, SBFrame, GetFunctionName, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::LanguageType, SBFrame, GuessLanguage, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBFrame, GetFunctionName, ());
+ LLDB_REGISTER_METHOD(const char *, SBFrame, GetDisplayFunctionName, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBFunction.cpp b/contrib/llvm-project/lldb/source/API/SBFunction.cpp
new file mode 100644
index 000000000000..1770bede2f42
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBFunction.cpp
@@ -0,0 +1,282 @@
+//===-- SBFunction.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBFunction.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBFunction::SBFunction() : m_opaque_ptr(nullptr) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBFunction);
+}
+
+SBFunction::SBFunction(lldb_private::Function *lldb_object_ptr)
+ : m_opaque_ptr(lldb_object_ptr) {}
+
+SBFunction::SBFunction(const lldb::SBFunction &rhs)
+ : m_opaque_ptr(rhs.m_opaque_ptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBFunction, (const lldb::SBFunction &), rhs);
+}
+
+const SBFunction &SBFunction::operator=(const SBFunction &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBFunction &,
+ SBFunction, operator=,(const lldb::SBFunction &), rhs);
+
+ m_opaque_ptr = rhs.m_opaque_ptr;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBFunction::~SBFunction() { m_opaque_ptr = nullptr; }
+
+bool SBFunction::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFunction, IsValid);
+ return this->operator bool();
+}
+SBFunction::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFunction, operator bool);
+
+ return m_opaque_ptr != nullptr;
+}
+
+const char *SBFunction::GetName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBFunction, GetName);
+
+ const char *cstr = nullptr;
+ if (m_opaque_ptr)
+ cstr = m_opaque_ptr->GetName().AsCString();
+
+ return cstr;
+}
+
+const char *SBFunction::GetDisplayName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBFunction, GetDisplayName);
+
+ const char *cstr = nullptr;
+ if (m_opaque_ptr)
+ cstr = m_opaque_ptr->GetMangled()
+ .GetDisplayDemangledName(m_opaque_ptr->GetLanguage())
+ .AsCString();
+
+ return cstr;
+}
+
+const char *SBFunction::GetMangledName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBFunction, GetMangledName);
+
+ const char *cstr = nullptr;
+ if (m_opaque_ptr)
+ cstr = m_opaque_ptr->GetMangled().GetMangledName().AsCString();
+ return cstr;
+}
+
+bool SBFunction::operator==(const SBFunction &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBFunction, operator==,(const lldb::SBFunction &), rhs);
+
+ return m_opaque_ptr == rhs.m_opaque_ptr;
+}
+
+bool SBFunction::operator!=(const SBFunction &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBFunction, operator!=,(const lldb::SBFunction &), rhs);
+
+ return m_opaque_ptr != rhs.m_opaque_ptr;
+}
+
+bool SBFunction::GetDescription(SBStream &s) {
+ LLDB_RECORD_METHOD(bool, SBFunction, GetDescription, (lldb::SBStream &), s);
+
+ if (m_opaque_ptr) {
+ s.Printf("SBFunction: id = 0x%8.8" PRIx64 ", name = %s",
+ m_opaque_ptr->GetID(), m_opaque_ptr->GetName().AsCString());
+ Type *func_type = m_opaque_ptr->GetType();
+ if (func_type)
+ s.Printf(", type = %s", func_type->GetName().AsCString());
+ return true;
+ }
+ s.Printf("No value");
+ return false;
+}
+
+SBInstructionList SBFunction::GetInstructions(SBTarget target) {
+ LLDB_RECORD_METHOD(lldb::SBInstructionList, SBFunction, GetInstructions,
+ (lldb::SBTarget), target);
+
+ return LLDB_RECORD_RESULT(GetInstructions(target, nullptr));
+}
+
+SBInstructionList SBFunction::GetInstructions(SBTarget target,
+ const char *flavor) {
+ LLDB_RECORD_METHOD(lldb::SBInstructionList, SBFunction, GetInstructions,
+ (lldb::SBTarget, const char *), target, flavor);
+
+ SBInstructionList sb_instructions;
+ if (m_opaque_ptr) {
+ ExecutionContext exe_ctx;
+ TargetSP target_sp(target.GetSP());
+ std::unique_lock<std::recursive_mutex> lock;
+ if (target_sp) {
+ lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ target_sp->CalculateExecutionContext(exe_ctx);
+ exe_ctx.SetProcessSP(target_sp->GetProcessSP());
+ }
+ ModuleSP module_sp(
+ m_opaque_ptr->GetAddressRange().GetBaseAddress().GetModule());
+ if (module_sp) {
+ const bool prefer_file_cache = false;
+ sb_instructions.SetDisassembler(Disassembler::DisassembleRange(
+ module_sp->GetArchitecture(), nullptr, flavor, exe_ctx,
+ m_opaque_ptr->GetAddressRange(), prefer_file_cache));
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_instructions);
+}
+
+lldb_private::Function *SBFunction::get() { return m_opaque_ptr; }
+
+void SBFunction::reset(lldb_private::Function *lldb_object_ptr) {
+ m_opaque_ptr = lldb_object_ptr;
+}
+
+SBAddress SBFunction::GetStartAddress() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBAddress, SBFunction, GetStartAddress);
+
+ SBAddress addr;
+ if (m_opaque_ptr)
+ addr.SetAddress(&m_opaque_ptr->GetAddressRange().GetBaseAddress());
+ return LLDB_RECORD_RESULT(addr);
+}
+
+SBAddress SBFunction::GetEndAddress() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBAddress, SBFunction, GetEndAddress);
+
+ SBAddress addr;
+ if (m_opaque_ptr) {
+ addr_t byte_size = m_opaque_ptr->GetAddressRange().GetByteSize();
+ if (byte_size > 0) {
+ addr.SetAddress(&m_opaque_ptr->GetAddressRange().GetBaseAddress());
+ addr->Slide(byte_size);
+ }
+ }
+ return LLDB_RECORD_RESULT(addr);
+}
+
+const char *SBFunction::GetArgumentName(uint32_t arg_idx) {
+ LLDB_RECORD_METHOD(const char *, SBFunction, GetArgumentName, (uint32_t),
+ arg_idx);
+
+ if (m_opaque_ptr) {
+ Block &block = m_opaque_ptr->GetBlock(true);
+ VariableListSP variable_list_sp = block.GetBlockVariableList(true);
+ if (variable_list_sp) {
+ VariableList arguments;
+ variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument,
+ arguments, true);
+ lldb::VariableSP variable_sp = arguments.GetVariableAtIndex(arg_idx);
+ if (variable_sp)
+ return variable_sp->GetName().GetCString();
+ }
+ }
+ return nullptr;
+}
+
+uint32_t SBFunction::GetPrologueByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBFunction, GetPrologueByteSize);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->GetPrologueByteSize();
+ return 0;
+}
+
+SBType SBFunction::GetType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBFunction, GetType);
+
+ SBType sb_type;
+ if (m_opaque_ptr) {
+ Type *function_type = m_opaque_ptr->GetType();
+ if (function_type)
+ sb_type.ref().SetType(function_type->shared_from_this());
+ }
+ return LLDB_RECORD_RESULT(sb_type);
+}
+
+SBBlock SBFunction::GetBlock() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBBlock, SBFunction, GetBlock);
+
+ SBBlock sb_block;
+ if (m_opaque_ptr)
+ sb_block.SetPtr(&m_opaque_ptr->GetBlock(true));
+ return LLDB_RECORD_RESULT(sb_block);
+}
+
+lldb::LanguageType SBFunction::GetLanguage() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::LanguageType, SBFunction, GetLanguage);
+
+ if (m_opaque_ptr) {
+ if (m_opaque_ptr->GetCompileUnit())
+ return m_opaque_ptr->GetCompileUnit()->GetLanguage();
+ }
+ return lldb::eLanguageTypeUnknown;
+}
+
+bool SBFunction::GetIsOptimized() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBFunction, GetIsOptimized);
+
+ if (m_opaque_ptr) {
+ if (m_opaque_ptr->GetCompileUnit())
+ return m_opaque_ptr->GetCompileUnit()->GetIsOptimized();
+ }
+ return false;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBFunction>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBFunction, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBFunction, (const lldb::SBFunction &));
+ LLDB_REGISTER_METHOD(const lldb::SBFunction &,
+ SBFunction, operator=,(const lldb::SBFunction &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBFunction, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBFunction, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBFunction, GetName, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBFunction, GetDisplayName, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBFunction, GetMangledName, ());
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBFunction, operator==,(const lldb::SBFunction &));
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBFunction, operator!=,(const lldb::SBFunction &));
+ LLDB_REGISTER_METHOD(bool, SBFunction, GetDescription, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBFunction, GetInstructions,
+ (lldb::SBTarget));
+ LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBFunction, GetInstructions,
+ (lldb::SBTarget, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBFunction, GetStartAddress, ());
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBFunction, GetEndAddress, ());
+ LLDB_REGISTER_METHOD(const char *, SBFunction, GetArgumentName, (uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBFunction, GetPrologueByteSize, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBFunction, GetType, ());
+ LLDB_REGISTER_METHOD(lldb::SBBlock, SBFunction, GetBlock, ());
+ LLDB_REGISTER_METHOD(lldb::LanguageType, SBFunction, GetLanguage, ());
+ LLDB_REGISTER_METHOD(bool, SBFunction, GetIsOptimized, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBHostOS.cpp b/contrib/llvm-project/lldb/source/API/SBHostOS.cpp
new file mode 100644
index 000000000000..c3c92e68140d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBHostOS.cpp
@@ -0,0 +1,194 @@
+//===-- SBHostOS.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBHostOS.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/HostNativeThread.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Utility/FileSpec.h"
+
+#include "Plugins/ExpressionParser/Clang/ClangHost.h"
+#ifndef LLDB_DISABLE_PYTHON
+#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h"
+#endif
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Path.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBFileSpec SBHostOS::GetProgramFileSpec() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(lldb::SBFileSpec, SBHostOS,
+ GetProgramFileSpec);
+
+ SBFileSpec sb_filespec;
+ sb_filespec.SetFileSpec(HostInfo::GetProgramFileSpec());
+ return LLDB_RECORD_RESULT(sb_filespec);
+}
+
+SBFileSpec SBHostOS::GetLLDBPythonPath() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(lldb::SBFileSpec, SBHostOS,
+ GetLLDBPythonPath);
+
+ return LLDB_RECORD_RESULT(GetLLDBPath(ePathTypePythonDir));
+}
+
+SBFileSpec SBHostOS::GetLLDBPath(lldb::PathType path_type) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBFileSpec, SBHostOS, GetLLDBPath,
+ (lldb::PathType), path_type);
+
+ FileSpec fspec;
+ switch (path_type) {
+ case ePathTypeLLDBShlibDir:
+ fspec = HostInfo::GetShlibDir();
+ break;
+ case ePathTypeSupportExecutableDir:
+ fspec = HostInfo::GetSupportExeDir();
+ break;
+ case ePathTypeHeaderDir:
+ fspec = HostInfo::GetHeaderDir();
+ break;
+ case ePathTypePythonDir:
+#ifndef LLDB_DISABLE_PYTHON
+ fspec = ScriptInterpreterPython::GetPythonDir();
+#endif
+ break;
+ case ePathTypeLLDBSystemPlugins:
+ fspec = HostInfo::GetSystemPluginDir();
+ break;
+ case ePathTypeLLDBUserPlugins:
+ fspec = HostInfo::GetUserPluginDir();
+ break;
+ case ePathTypeLLDBTempSystemDir:
+ fspec = HostInfo::GetProcessTempDir();
+ break;
+ case ePathTypeGlobalLLDBTempSystemDir:
+ fspec = HostInfo::GetGlobalTempDir();
+ break;
+ case ePathTypeClangDir:
+ fspec = GetClangResourceDir();
+ break;
+ }
+
+ SBFileSpec sb_fspec;
+ sb_fspec.SetFileSpec(fspec);
+ return LLDB_RECORD_RESULT(sb_fspec);
+}
+
+SBFileSpec SBHostOS::GetUserHomeDirectory() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(lldb::SBFileSpec, SBHostOS,
+ GetUserHomeDirectory);
+
+ SBFileSpec sb_fspec;
+
+ llvm::SmallString<64> home_dir_path;
+ llvm::sys::path::home_directory(home_dir_path);
+ FileSpec homedir(home_dir_path.c_str());
+ FileSystem::Instance().Resolve(homedir);
+
+ sb_fspec.SetFileSpec(homedir);
+ return LLDB_RECORD_RESULT(sb_fspec);
+}
+
+lldb::thread_t SBHostOS::ThreadCreate(const char *name,
+ lldb::thread_func_t thread_function,
+ void *thread_arg, SBError *error_ptr) {
+ LLDB_RECORD_DUMMY(lldb::thread_t, SBHostOS, ThreadCreate,
+ (lldb::thread_func_t, void *, SBError *), name,
+ thread_function, thread_arg, error_ptr);
+ llvm::Expected<HostThread> thread =
+ ThreadLauncher::LaunchThread(name, thread_function, thread_arg);
+ if (!thread) {
+ if (error_ptr)
+ error_ptr->SetError(Status(thread.takeError()));
+ else
+ llvm::consumeError(thread.takeError());
+ return LLDB_INVALID_HOST_THREAD;
+ }
+
+ return thread->Release();
+}
+
+void SBHostOS::ThreadCreated(const char *name) {
+ LLDB_RECORD_STATIC_METHOD(void, SBHostOS, ThreadCreated, (const char *),
+ name);
+}
+
+bool SBHostOS::ThreadCancel(lldb::thread_t thread, SBError *error_ptr) {
+ LLDB_RECORD_DUMMY(bool, SBHostOS, ThreadCancel,
+ (lldb::thread_t, lldb::SBError *), thread,
+ error_ptr);
+
+ Status error;
+ HostThread host_thread(thread);
+ error = host_thread.Cancel();
+ if (error_ptr)
+ error_ptr->SetError(error);
+ host_thread.Release();
+ return error.Success();
+}
+
+bool SBHostOS::ThreadDetach(lldb::thread_t thread, SBError *error_ptr) {
+ LLDB_RECORD_DUMMY(bool, SBHostOS, ThreadDetach,
+ (lldb::thread_t, lldb::SBError *), thread,
+ error_ptr);
+
+ Status error;
+#if defined(_WIN32)
+ if (error_ptr)
+ error_ptr->SetErrorString("ThreadDetach is not supported on this platform");
+#else
+ HostThread host_thread(thread);
+ error = host_thread.GetNativeThread().Detach();
+ if (error_ptr)
+ error_ptr->SetError(error);
+ host_thread.Release();
+#endif
+ return error.Success();
+}
+
+bool SBHostOS::ThreadJoin(lldb::thread_t thread, lldb::thread_result_t *result,
+ SBError *error_ptr) {
+ LLDB_RECORD_DUMMY(
+ bool, SBHostOS, ThreadJoin,
+ (lldb::thread_t, lldb::thread_result_t *, lldb::SBError *), thread,
+ result, error_ptr);
+
+ Status error;
+ HostThread host_thread(thread);
+ error = host_thread.Join(result);
+ if (error_ptr)
+ error_ptr->SetError(error);
+ host_thread.Release();
+ return error.Success();
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBHostOS>(Registry &R) {
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBFileSpec, SBHostOS, GetProgramFileSpec,
+ ());
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBFileSpec, SBHostOS, GetLLDBPythonPath,
+ ());
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBFileSpec, SBHostOS, GetLLDBPath,
+ (lldb::PathType));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBFileSpec, SBHostOS,
+ GetUserHomeDirectory, ());
+ LLDB_REGISTER_STATIC_METHOD(void, SBHostOS, ThreadCreated, (const char *));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBInstruction.cpp b/contrib/llvm-project/lldb/source/API/SBInstruction.cpp
new file mode 100644
index 000000000000..fcf66fd25824
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBInstruction.cpp
@@ -0,0 +1,369 @@
+//===-- SBInstruction.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBInstruction.h"
+#include "SBReproducerPrivate.h"
+
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBFrame.h"
+
+#include "lldb/API/SBInstruction.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+
+#include <memory>
+
+// We recently fixed a leak in one of the Instruction subclasses where the
+// instruction will only hold a weak reference to the disassembler to avoid a
+// cycle that was keeping both objects alive (leak) and we need the
+// InstructionImpl class to make sure our public API behaves as users would
+// expect. Calls in our public API allow clients to do things like:
+//
+// 1 lldb::SBInstruction inst;
+// 2 inst = target.ReadInstructions(pc, 1).GetInstructionAtIndex(0)
+// 3 if (inst.DoesBranch())
+// 4 ...
+//
+// There was a temporary lldb::DisassemblerSP object created in the
+// SBInstructionList that was returned by lldb.target.ReadInstructions() that
+// will go away after line 2 but the "inst" object should be able to still
+// answer questions about itself. So we make sure that any SBInstruction
+// objects that are given out have a strong reference to the disassembler and
+// the instruction so that the object can live and successfully respond to all
+// queries.
+class InstructionImpl {
+public:
+ InstructionImpl(const lldb::DisassemblerSP &disasm_sp,
+ const lldb::InstructionSP &inst_sp)
+ : m_disasm_sp(disasm_sp), m_inst_sp(inst_sp) {}
+
+ lldb::InstructionSP GetSP() const { return m_inst_sp; }
+
+ bool IsValid() const { return (bool)m_inst_sp; }
+
+protected:
+ lldb::DisassemblerSP m_disasm_sp; // Can be empty/invalid
+ lldb::InstructionSP m_inst_sp;
+};
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBInstruction::SBInstruction() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBInstruction);
+}
+
+SBInstruction::SBInstruction(const lldb::DisassemblerSP &disasm_sp,
+ const lldb::InstructionSP &inst_sp)
+ : m_opaque_sp(new InstructionImpl(disasm_sp, inst_sp)) {}
+
+SBInstruction::SBInstruction(const SBInstruction &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBInstruction, (const lldb::SBInstruction &), rhs);
+}
+
+const SBInstruction &SBInstruction::operator=(const SBInstruction &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBInstruction &,
+ SBInstruction, operator=,(const lldb::SBInstruction &),
+ rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = rhs.m_opaque_sp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBInstruction::~SBInstruction() {}
+
+bool SBInstruction::IsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, IsValid);
+ return this->operator bool();
+}
+SBInstruction::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBInstruction, operator bool);
+
+ return m_opaque_sp && m_opaque_sp->IsValid();
+}
+
+SBAddress SBInstruction::GetAddress() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBAddress, SBInstruction, GetAddress);
+
+ SBAddress sb_addr;
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp && inst_sp->GetAddress().IsValid())
+ sb_addr.SetAddress(&inst_sp->GetAddress());
+ return LLDB_RECORD_RESULT(sb_addr);
+}
+
+const char *SBInstruction::GetMnemonic(SBTarget target) {
+ LLDB_RECORD_METHOD(const char *, SBInstruction, GetMnemonic, (lldb::SBTarget),
+ target);
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp) {
+ ExecutionContext exe_ctx;
+ TargetSP target_sp(target.GetSP());
+ std::unique_lock<std::recursive_mutex> lock;
+ if (target_sp) {
+ lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+
+ target_sp->CalculateExecutionContext(exe_ctx);
+ exe_ctx.SetProcessSP(target_sp->GetProcessSP());
+ }
+ return inst_sp->GetMnemonic(&exe_ctx);
+ }
+ return nullptr;
+}
+
+const char *SBInstruction::GetOperands(SBTarget target) {
+ LLDB_RECORD_METHOD(const char *, SBInstruction, GetOperands, (lldb::SBTarget),
+ target);
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp) {
+ ExecutionContext exe_ctx;
+ TargetSP target_sp(target.GetSP());
+ std::unique_lock<std::recursive_mutex> lock;
+ if (target_sp) {
+ lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+
+ target_sp->CalculateExecutionContext(exe_ctx);
+ exe_ctx.SetProcessSP(target_sp->GetProcessSP());
+ }
+ return inst_sp->GetOperands(&exe_ctx);
+ }
+ return nullptr;
+}
+
+const char *SBInstruction::GetComment(SBTarget target) {
+ LLDB_RECORD_METHOD(const char *, SBInstruction, GetComment, (lldb::SBTarget),
+ target);
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp) {
+ ExecutionContext exe_ctx;
+ TargetSP target_sp(target.GetSP());
+ std::unique_lock<std::recursive_mutex> lock;
+ if (target_sp) {
+ lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+
+ target_sp->CalculateExecutionContext(exe_ctx);
+ exe_ctx.SetProcessSP(target_sp->GetProcessSP());
+ }
+ return inst_sp->GetComment(&exe_ctx);
+ }
+ return nullptr;
+}
+
+size_t SBInstruction::GetByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBInstruction, GetByteSize);
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp)
+ return inst_sp->GetOpcode().GetByteSize();
+ return 0;
+}
+
+SBData SBInstruction::GetData(SBTarget target) {
+ LLDB_RECORD_METHOD(lldb::SBData, SBInstruction, GetData, (lldb::SBTarget),
+ target);
+
+ lldb::SBData sb_data;
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp) {
+ DataExtractorSP data_extractor_sp(new DataExtractor());
+ if (inst_sp->GetData(*data_extractor_sp)) {
+ sb_data.SetOpaque(data_extractor_sp);
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_data);
+}
+
+bool SBInstruction::DoesBranch() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, DoesBranch);
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp)
+ return inst_sp->DoesBranch();
+ return false;
+}
+
+bool SBInstruction::HasDelaySlot() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, HasDelaySlot);
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp)
+ return inst_sp->HasDelaySlot();
+ return false;
+}
+
+bool SBInstruction::CanSetBreakpoint() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, CanSetBreakpoint);
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp)
+ return inst_sp->CanSetBreakpoint();
+ return false;
+}
+
+lldb::InstructionSP SBInstruction::GetOpaque() {
+ if (m_opaque_sp)
+ return m_opaque_sp->GetSP();
+ else
+ return lldb::InstructionSP();
+}
+
+void SBInstruction::SetOpaque(const lldb::DisassemblerSP &disasm_sp,
+ const lldb::InstructionSP &inst_sp) {
+ m_opaque_sp = std::make_shared<InstructionImpl>(disasm_sp, inst_sp);
+}
+
+bool SBInstruction::GetDescription(lldb::SBStream &s) {
+ LLDB_RECORD_METHOD(bool, SBInstruction, GetDescription, (lldb::SBStream &),
+ s);
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp) {
+ SymbolContext sc;
+ const Address &addr = inst_sp->GetAddress();
+ ModuleSP module_sp(addr.GetModule());
+ if (module_sp)
+ module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything,
+ sc);
+ // Use the "ref()" instead of the "get()" accessor in case the SBStream
+ // didn't have a stream already created, one will get created...
+ FormatEntity::Entry format;
+ FormatEntity::Parse("${addr}: ", format);
+ inst_sp->Dump(&s.ref(), 0, true, false, nullptr, &sc, nullptr, &format, 0);
+ return true;
+ }
+ return false;
+}
+
+void SBInstruction::Print(FILE *out) {
+ LLDB_RECORD_METHOD(void, SBInstruction, Print, (FILE *), out);
+
+ if (out == nullptr)
+ return;
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp) {
+ SymbolContext sc;
+ const Address &addr = inst_sp->GetAddress();
+ ModuleSP module_sp(addr.GetModule());
+ if (module_sp)
+ module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything,
+ sc);
+ StreamFile out_stream(out, false);
+ FormatEntity::Entry format;
+ FormatEntity::Parse("${addr}: ", format);
+ inst_sp->Dump(&out_stream, 0, true, false, nullptr, &sc, nullptr, &format,
+ 0);
+ }
+}
+
+bool SBInstruction::EmulateWithFrame(lldb::SBFrame &frame,
+ uint32_t evaluate_options) {
+ LLDB_RECORD_METHOD(bool, SBInstruction, EmulateWithFrame,
+ (lldb::SBFrame &, uint32_t), frame, evaluate_options);
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp) {
+ lldb::StackFrameSP frame_sp(frame.GetFrameSP());
+
+ if (frame_sp) {
+ lldb_private::ExecutionContext exe_ctx;
+ frame_sp->CalculateExecutionContext(exe_ctx);
+ lldb_private::Target *target = exe_ctx.GetTargetPtr();
+ lldb_private::ArchSpec arch = target->GetArchitecture();
+
+ return inst_sp->Emulate(
+ arch, evaluate_options, (void *)frame_sp.get(),
+ &lldb_private::EmulateInstruction::ReadMemoryFrame,
+ &lldb_private::EmulateInstruction::WriteMemoryFrame,
+ &lldb_private::EmulateInstruction::ReadRegisterFrame,
+ &lldb_private::EmulateInstruction::WriteRegisterFrame);
+ }
+ }
+ return false;
+}
+
+bool SBInstruction::DumpEmulation(const char *triple) {
+ LLDB_RECORD_METHOD(bool, SBInstruction, DumpEmulation, (const char *),
+ triple);
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp && triple) {
+ return inst_sp->DumpEmulation(HostInfo::GetAugmentedArchSpec(triple));
+ }
+ return false;
+}
+
+bool SBInstruction::TestEmulation(lldb::SBStream &output_stream,
+ const char *test_file) {
+ LLDB_RECORD_METHOD(bool, SBInstruction, TestEmulation,
+ (lldb::SBStream &, const char *), output_stream,
+ test_file);
+
+ if (!m_opaque_sp)
+ SetOpaque(lldb::DisassemblerSP(),
+ lldb::InstructionSP(new PseudoInstruction()));
+
+ lldb::InstructionSP inst_sp(GetOpaque());
+ if (inst_sp)
+ return inst_sp->TestEmulation(output_stream.get(), test_file);
+ return false;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBInstruction>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBInstruction, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBInstruction, (const lldb::SBInstruction &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBInstruction &,
+ SBInstruction, operator=,(const lldb::SBInstruction &));
+ LLDB_REGISTER_METHOD(bool, SBInstruction, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBInstruction, operator bool, ());
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBInstruction, GetAddress, ());
+ LLDB_REGISTER_METHOD(const char *, SBInstruction, GetMnemonic,
+ (lldb::SBTarget));
+ LLDB_REGISTER_METHOD(const char *, SBInstruction, GetOperands,
+ (lldb::SBTarget));
+ LLDB_REGISTER_METHOD(const char *, SBInstruction, GetComment,
+ (lldb::SBTarget));
+ LLDB_REGISTER_METHOD(size_t, SBInstruction, GetByteSize, ());
+ LLDB_REGISTER_METHOD(lldb::SBData, SBInstruction, GetData,
+ (lldb::SBTarget));
+ LLDB_REGISTER_METHOD(bool, SBInstruction, DoesBranch, ());
+ LLDB_REGISTER_METHOD(bool, SBInstruction, HasDelaySlot, ());
+ LLDB_REGISTER_METHOD(bool, SBInstruction, CanSetBreakpoint, ());
+ LLDB_REGISTER_METHOD(bool, SBInstruction, GetDescription,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(void, SBInstruction, Print, (FILE *));
+ LLDB_REGISTER_METHOD(bool, SBInstruction, EmulateWithFrame,
+ (lldb::SBFrame &, uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBInstruction, DumpEmulation, (const char *));
+ LLDB_REGISTER_METHOD(bool, SBInstruction, TestEmulation,
+ (lldb::SBStream &, const char *));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBInstructionList.cpp b/contrib/llvm-project/lldb/source/API/SBInstructionList.cpp
new file mode 100644
index 000000000000..cce923bf04a4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBInstructionList.cpp
@@ -0,0 +1,210 @@
+//===-- SBInstructionList.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBInstructionList.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBInstruction.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBInstructionList::SBInstructionList() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBInstructionList);
+}
+
+SBInstructionList::SBInstructionList(const SBInstructionList &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBInstructionList, (const lldb::SBInstructionList &),
+ rhs);
+}
+
+const SBInstructionList &SBInstructionList::
+operator=(const SBInstructionList &rhs) {
+ LLDB_RECORD_METHOD(
+ const lldb::SBInstructionList &,
+ SBInstructionList, operator=,(const lldb::SBInstructionList &), rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = rhs.m_opaque_sp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBInstructionList::~SBInstructionList() {}
+
+bool SBInstructionList::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBInstructionList, IsValid);
+ return this->operator bool();
+}
+SBInstructionList::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBInstructionList, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+size_t SBInstructionList::GetSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBInstructionList, GetSize);
+
+ if (m_opaque_sp)
+ return m_opaque_sp->GetInstructionList().GetSize();
+ return 0;
+}
+
+SBInstruction SBInstructionList::GetInstructionAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBInstruction, SBInstructionList,
+ GetInstructionAtIndex, (uint32_t), idx);
+
+ SBInstruction inst;
+ if (m_opaque_sp && idx < m_opaque_sp->GetInstructionList().GetSize())
+ inst.SetOpaque(
+ m_opaque_sp,
+ m_opaque_sp->GetInstructionList().GetInstructionAtIndex(idx));
+ return LLDB_RECORD_RESULT(inst);
+}
+
+size_t SBInstructionList::GetInstructionsCount(const SBAddress &start,
+ const SBAddress &end,
+ bool canSetBreakpoint) {
+ LLDB_RECORD_METHOD(size_t, SBInstructionList, GetInstructionsCount,
+ (const lldb::SBAddress &, const lldb::SBAddress &, bool),
+ start, end, canSetBreakpoint);
+
+ size_t num_instructions = GetSize();
+ size_t i = 0;
+ SBAddress addr;
+ size_t lower_index = 0;
+ size_t upper_index = 0;
+ size_t instructions_to_skip = 0;
+ for (i = 0; i < num_instructions; ++i) {
+ addr = GetInstructionAtIndex(i).GetAddress();
+ if (start == addr)
+ lower_index = i;
+ if (end == addr)
+ upper_index = i;
+ }
+ if (canSetBreakpoint)
+ for (i = lower_index; i <= upper_index; ++i) {
+ SBInstruction insn = GetInstructionAtIndex(i);
+ if (!insn.CanSetBreakpoint())
+ ++instructions_to_skip;
+ }
+ return upper_index - lower_index - instructions_to_skip;
+}
+
+void SBInstructionList::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBInstructionList, Clear);
+
+ m_opaque_sp.reset();
+}
+
+void SBInstructionList::AppendInstruction(SBInstruction insn) {
+ LLDB_RECORD_METHOD(void, SBInstructionList, AppendInstruction,
+ (lldb::SBInstruction), insn);
+}
+
+void SBInstructionList::SetDisassembler(const lldb::DisassemblerSP &opaque_sp) {
+ m_opaque_sp = opaque_sp;
+}
+
+void SBInstructionList::Print(FILE *out) {
+ LLDB_RECORD_METHOD(void, SBInstructionList, Print, (FILE *), out);
+
+ if (out == nullptr)
+ return;
+}
+
+bool SBInstructionList::GetDescription(lldb::SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBInstructionList, GetDescription,
+ (lldb::SBStream &), description);
+
+ if (m_opaque_sp) {
+ size_t num_instructions = GetSize();
+ if (num_instructions) {
+ // Call the ref() to make sure a stream is created if one deesn't exist
+ // already inside description...
+ Stream &sref = description.ref();
+ const uint32_t max_opcode_byte_size =
+ m_opaque_sp->GetInstructionList().GetMaxOpcocdeByteSize();
+ FormatEntity::Entry format;
+ FormatEntity::Parse("${addr}: ", format);
+ SymbolContext sc;
+ SymbolContext prev_sc;
+ for (size_t i = 0; i < num_instructions; ++i) {
+ Instruction *inst =
+ m_opaque_sp->GetInstructionList().GetInstructionAtIndex(i).get();
+ if (inst == nullptr)
+ break;
+
+ const Address &addr = inst->GetAddress();
+ prev_sc = sc;
+ ModuleSP module_sp(addr.GetModule());
+ if (module_sp) {
+ module_sp->ResolveSymbolContextForAddress(
+ addr, eSymbolContextEverything, sc);
+ }
+
+ inst->Dump(&sref, max_opcode_byte_size, true, false, nullptr, &sc,
+ &prev_sc, &format, 0);
+ sref.EOL();
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SBInstructionList::DumpEmulationForAllInstructions(const char *triple) {
+ LLDB_RECORD_METHOD(bool, SBInstructionList, DumpEmulationForAllInstructions,
+ (const char *), triple);
+
+ if (m_opaque_sp) {
+ size_t len = GetSize();
+ for (size_t i = 0; i < len; ++i) {
+ if (!GetInstructionAtIndex((uint32_t)i).DumpEmulation(triple))
+ return false;
+ }
+ }
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBInstructionList>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBInstructionList, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBInstructionList,
+ (const lldb::SBInstructionList &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBInstructionList &,
+ SBInstructionList, operator=,(const lldb::SBInstructionList &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBInstructionList, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBInstructionList, operator bool, ());
+ LLDB_REGISTER_METHOD(size_t, SBInstructionList, GetSize, ());
+ LLDB_REGISTER_METHOD(lldb::SBInstruction, SBInstructionList,
+ GetInstructionAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(
+ size_t, SBInstructionList, GetInstructionsCount,
+ (const lldb::SBAddress &, const lldb::SBAddress &, bool));
+ LLDB_REGISTER_METHOD(void, SBInstructionList, Clear, ());
+ LLDB_REGISTER_METHOD(void, SBInstructionList, AppendInstruction,
+ (lldb::SBInstruction));
+ LLDB_REGISTER_METHOD(void, SBInstructionList, Print, (FILE *));
+ LLDB_REGISTER_METHOD(bool, SBInstructionList, GetDescription,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(bool, SBInstructionList,
+ DumpEmulationForAllInstructions, (const char *));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBLanguageRuntime.cpp b/contrib/llvm-project/lldb/source/API/SBLanguageRuntime.cpp
new file mode 100644
index 000000000000..04bd08fb739e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBLanguageRuntime.cpp
@@ -0,0 +1,46 @@
+//===-- SBLanguageRuntime.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBLanguageRuntime.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/Target/Language.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+lldb::LanguageType
+SBLanguageRuntime::GetLanguageTypeFromString(const char *string) {
+ LLDB_RECORD_STATIC_METHOD(lldb::LanguageType, SBLanguageRuntime,
+ GetLanguageTypeFromString, (const char *), string);
+
+ return Language::GetLanguageTypeFromString(
+ llvm::StringRef::withNullAsEmpty(string));
+}
+
+const char *
+SBLanguageRuntime::GetNameForLanguageType(lldb::LanguageType language) {
+ LLDB_RECORD_STATIC_METHOD(const char *, SBLanguageRuntime,
+ GetNameForLanguageType, (lldb::LanguageType),
+ language);
+
+ return Language::GetNameForLanguageType(language);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBLanguageRuntime>(Registry &R) {
+ LLDB_REGISTER_STATIC_METHOD(lldb::LanguageType, SBLanguageRuntime,
+ GetLanguageTypeFromString, (const char *));
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBLanguageRuntime,
+ GetNameForLanguageType, (lldb::LanguageType));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBLaunchInfo.cpp b/contrib/llvm-project/lldb/source/API/SBLaunchInfo.cpp
new file mode 100644
index 000000000000..5c5e69704c7c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBLaunchInfo.cpp
@@ -0,0 +1,379 @@
+//===-- SBLaunchInfo.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBLaunchInfo.h"
+#include "SBReproducerPrivate.h"
+
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBListener.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class lldb_private::SBLaunchInfoImpl : public ProcessLaunchInfo {
+public:
+ SBLaunchInfoImpl()
+ : ProcessLaunchInfo(), m_envp(GetEnvironment().getEnvp()) {}
+
+ const char *const *GetEnvp() const { return m_envp; }
+ void RegenerateEnvp() { m_envp = GetEnvironment().getEnvp(); }
+
+ SBLaunchInfoImpl &operator=(const ProcessLaunchInfo &rhs) {
+ ProcessLaunchInfo::operator=(rhs);
+ RegenerateEnvp();
+ return *this;
+ }
+
+private:
+ Environment::Envp m_envp;
+};
+
+SBLaunchInfo::SBLaunchInfo(const char **argv)
+ : m_opaque_sp(new SBLaunchInfoImpl()) {
+ LLDB_RECORD_CONSTRUCTOR(SBLaunchInfo, (const char **), argv);
+
+ m_opaque_sp->GetFlags().Reset(eLaunchFlagDebug | eLaunchFlagDisableASLR);
+ if (argv && argv[0])
+ m_opaque_sp->GetArguments().SetArguments(argv);
+}
+
+SBLaunchInfo::~SBLaunchInfo() {}
+
+const lldb_private::ProcessLaunchInfo &SBLaunchInfo::ref() const {
+ return *m_opaque_sp;
+}
+
+void SBLaunchInfo::set_ref(const ProcessLaunchInfo &info) {
+ *m_opaque_sp = info;
+}
+
+lldb::pid_t SBLaunchInfo::GetProcessID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::pid_t, SBLaunchInfo, GetProcessID);
+
+ return m_opaque_sp->GetProcessID();
+}
+
+uint32_t SBLaunchInfo::GetUserID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBLaunchInfo, GetUserID);
+
+ return m_opaque_sp->GetUserID();
+}
+
+uint32_t SBLaunchInfo::GetGroupID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBLaunchInfo, GetGroupID);
+
+ return m_opaque_sp->GetGroupID();
+}
+
+bool SBLaunchInfo::UserIDIsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBLaunchInfo, UserIDIsValid);
+
+ return m_opaque_sp->UserIDIsValid();
+}
+
+bool SBLaunchInfo::GroupIDIsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBLaunchInfo, GroupIDIsValid);
+
+ return m_opaque_sp->GroupIDIsValid();
+}
+
+void SBLaunchInfo::SetUserID(uint32_t uid) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetUserID, (uint32_t), uid);
+
+ m_opaque_sp->SetUserID(uid);
+}
+
+void SBLaunchInfo::SetGroupID(uint32_t gid) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetGroupID, (uint32_t), gid);
+
+ m_opaque_sp->SetGroupID(gid);
+}
+
+SBFileSpec SBLaunchInfo::GetExecutableFile() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFileSpec, SBLaunchInfo, GetExecutableFile);
+
+ return LLDB_RECORD_RESULT(SBFileSpec(m_opaque_sp->GetExecutableFile()));
+}
+
+void SBLaunchInfo::SetExecutableFile(SBFileSpec exe_file,
+ bool add_as_first_arg) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetExecutableFile,
+ (lldb::SBFileSpec, bool), exe_file, add_as_first_arg);
+
+ m_opaque_sp->SetExecutableFile(exe_file.ref(), add_as_first_arg);
+}
+
+SBListener SBLaunchInfo::GetListener() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBListener, SBLaunchInfo, GetListener);
+
+ return LLDB_RECORD_RESULT(SBListener(m_opaque_sp->GetListener()));
+}
+
+void SBLaunchInfo::SetListener(SBListener &listener) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetListener, (lldb::SBListener &),
+ listener);
+
+ m_opaque_sp->SetListener(listener.GetSP());
+}
+
+uint32_t SBLaunchInfo::GetNumArguments() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBLaunchInfo, GetNumArguments);
+
+ return m_opaque_sp->GetArguments().GetArgumentCount();
+}
+
+const char *SBLaunchInfo::GetArgumentAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(const char *, SBLaunchInfo, GetArgumentAtIndex, (uint32_t),
+ idx);
+
+ return m_opaque_sp->GetArguments().GetArgumentAtIndex(idx);
+}
+
+void SBLaunchInfo::SetArguments(const char **argv, bool append) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetArguments, (const char **, bool),
+ argv, append);
+
+ if (append) {
+ if (argv)
+ m_opaque_sp->GetArguments().AppendArguments(argv);
+ } else {
+ if (argv)
+ m_opaque_sp->GetArguments().SetArguments(argv);
+ else
+ m_opaque_sp->GetArguments().Clear();
+ }
+}
+
+uint32_t SBLaunchInfo::GetNumEnvironmentEntries() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBLaunchInfo, GetNumEnvironmentEntries);
+
+ return m_opaque_sp->GetEnvironment().size();
+}
+
+const char *SBLaunchInfo::GetEnvironmentEntryAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(const char *, SBLaunchInfo, GetEnvironmentEntryAtIndex,
+ (uint32_t), idx);
+
+ if (idx > GetNumEnvironmentEntries())
+ return nullptr;
+ return m_opaque_sp->GetEnvp()[idx];
+}
+
+void SBLaunchInfo::SetEnvironmentEntries(const char **envp, bool append) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetEnvironmentEntries,
+ (const char **, bool), envp, append);
+
+ Environment env(envp);
+ if (append)
+ m_opaque_sp->GetEnvironment().insert(env.begin(), env.end());
+ else
+ m_opaque_sp->GetEnvironment() = env;
+ m_opaque_sp->RegenerateEnvp();
+}
+
+void SBLaunchInfo::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBLaunchInfo, Clear);
+
+ m_opaque_sp->Clear();
+}
+
+const char *SBLaunchInfo::GetWorkingDirectory() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBLaunchInfo,
+ GetWorkingDirectory);
+
+ return m_opaque_sp->GetWorkingDirectory().GetCString();
+}
+
+void SBLaunchInfo::SetWorkingDirectory(const char *working_dir) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetWorkingDirectory, (const char *),
+ working_dir);
+
+ m_opaque_sp->SetWorkingDirectory(FileSpec(working_dir));
+}
+
+uint32_t SBLaunchInfo::GetLaunchFlags() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBLaunchInfo, GetLaunchFlags);
+
+ return m_opaque_sp->GetFlags().Get();
+}
+
+void SBLaunchInfo::SetLaunchFlags(uint32_t flags) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetLaunchFlags, (uint32_t), flags);
+
+ m_opaque_sp->GetFlags().Reset(flags);
+}
+
+const char *SBLaunchInfo::GetProcessPluginName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBLaunchInfo, GetProcessPluginName);
+
+ return m_opaque_sp->GetProcessPluginName();
+}
+
+void SBLaunchInfo::SetProcessPluginName(const char *plugin_name) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetProcessPluginName, (const char *),
+ plugin_name);
+
+ return m_opaque_sp->SetProcessPluginName(plugin_name);
+}
+
+const char *SBLaunchInfo::GetShell() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBLaunchInfo, GetShell);
+
+ // Constify this string so that it is saved in the string pool. Otherwise it
+ // would be freed when this function goes out of scope.
+ ConstString shell(m_opaque_sp->GetShell().GetPath().c_str());
+ return shell.AsCString();
+}
+
+void SBLaunchInfo::SetShell(const char *path) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetShell, (const char *), path);
+
+ m_opaque_sp->SetShell(FileSpec(path));
+}
+
+bool SBLaunchInfo::GetShellExpandArguments() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBLaunchInfo, GetShellExpandArguments);
+
+ return m_opaque_sp->GetShellExpandArguments();
+}
+
+void SBLaunchInfo::SetShellExpandArguments(bool expand) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetShellExpandArguments, (bool),
+ expand);
+
+ m_opaque_sp->SetShellExpandArguments(expand);
+}
+
+uint32_t SBLaunchInfo::GetResumeCount() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBLaunchInfo, GetResumeCount);
+
+ return m_opaque_sp->GetResumeCount();
+}
+
+void SBLaunchInfo::SetResumeCount(uint32_t c) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetResumeCount, (uint32_t), c);
+
+ m_opaque_sp->SetResumeCount(c);
+}
+
+bool SBLaunchInfo::AddCloseFileAction(int fd) {
+ LLDB_RECORD_METHOD(bool, SBLaunchInfo, AddCloseFileAction, (int), fd);
+
+ return m_opaque_sp->AppendCloseFileAction(fd);
+}
+
+bool SBLaunchInfo::AddDuplicateFileAction(int fd, int dup_fd) {
+ LLDB_RECORD_METHOD(bool, SBLaunchInfo, AddDuplicateFileAction, (int, int), fd,
+ dup_fd);
+
+ return m_opaque_sp->AppendDuplicateFileAction(fd, dup_fd);
+}
+
+bool SBLaunchInfo::AddOpenFileAction(int fd, const char *path, bool read,
+ bool write) {
+ LLDB_RECORD_METHOD(bool, SBLaunchInfo, AddOpenFileAction,
+ (int, const char *, bool, bool), fd, path, read, write);
+
+ return m_opaque_sp->AppendOpenFileAction(fd, FileSpec(path), read, write);
+}
+
+bool SBLaunchInfo::AddSuppressFileAction(int fd, bool read, bool write) {
+ LLDB_RECORD_METHOD(bool, SBLaunchInfo, AddSuppressFileAction,
+ (int, bool, bool), fd, read, write);
+
+ return m_opaque_sp->AppendSuppressFileAction(fd, read, write);
+}
+
+void SBLaunchInfo::SetLaunchEventData(const char *data) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetLaunchEventData, (const char *),
+ data);
+
+ m_opaque_sp->SetLaunchEventData(data);
+}
+
+const char *SBLaunchInfo::GetLaunchEventData() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBLaunchInfo,
+ GetLaunchEventData);
+
+ return m_opaque_sp->GetLaunchEventData();
+}
+
+void SBLaunchInfo::SetDetachOnError(bool enable) {
+ LLDB_RECORD_METHOD(void, SBLaunchInfo, SetDetachOnError, (bool), enable);
+
+ m_opaque_sp->SetDetachOnError(enable);
+}
+
+bool SBLaunchInfo::GetDetachOnError() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBLaunchInfo, GetDetachOnError);
+
+ return m_opaque_sp->GetDetachOnError();
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBLaunchInfo>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBLaunchInfo, (const char **));
+ LLDB_REGISTER_METHOD(lldb::pid_t, SBLaunchInfo, GetProcessID, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBLaunchInfo, GetUserID, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBLaunchInfo, GetGroupID, ());
+ LLDB_REGISTER_METHOD(bool, SBLaunchInfo, UserIDIsValid, ());
+ LLDB_REGISTER_METHOD(bool, SBLaunchInfo, GroupIDIsValid, ());
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetUserID, (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetGroupID, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBFileSpec, SBLaunchInfo, GetExecutableFile, ());
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetExecutableFile,
+ (lldb::SBFileSpec, bool));
+ LLDB_REGISTER_METHOD(lldb::SBListener, SBLaunchInfo, GetListener, ());
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetListener, (lldb::SBListener &));
+ LLDB_REGISTER_METHOD(uint32_t, SBLaunchInfo, GetNumArguments, ());
+ LLDB_REGISTER_METHOD(const char *, SBLaunchInfo, GetArgumentAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetArguments,
+ (const char **, bool));
+ LLDB_REGISTER_METHOD(uint32_t, SBLaunchInfo, GetNumEnvironmentEntries, ());
+ LLDB_REGISTER_METHOD(const char *, SBLaunchInfo, GetEnvironmentEntryAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetEnvironmentEntries,
+ (const char **, bool));
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, Clear, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBLaunchInfo, GetWorkingDirectory,
+ ());
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetWorkingDirectory,
+ (const char *));
+ LLDB_REGISTER_METHOD(uint32_t, SBLaunchInfo, GetLaunchFlags, ());
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetLaunchFlags, (uint32_t));
+ LLDB_REGISTER_METHOD(const char *, SBLaunchInfo, GetProcessPluginName, ());
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetProcessPluginName,
+ (const char *));
+ LLDB_REGISTER_METHOD(const char *, SBLaunchInfo, GetShell, ());
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetShell, (const char *));
+ LLDB_REGISTER_METHOD(bool, SBLaunchInfo, GetShellExpandArguments, ());
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetShellExpandArguments, (bool));
+ LLDB_REGISTER_METHOD(uint32_t, SBLaunchInfo, GetResumeCount, ());
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetResumeCount, (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBLaunchInfo, AddCloseFileAction, (int));
+ LLDB_REGISTER_METHOD(bool, SBLaunchInfo, AddDuplicateFileAction,
+ (int, int));
+ LLDB_REGISTER_METHOD(bool, SBLaunchInfo, AddOpenFileAction,
+ (int, const char *, bool, bool));
+ LLDB_REGISTER_METHOD(bool, SBLaunchInfo, AddSuppressFileAction,
+ (int, bool, bool));
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetLaunchEventData,
+ (const char *));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBLaunchInfo, GetLaunchEventData,
+ ());
+ LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetDetachOnError, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBLaunchInfo, GetDetachOnError, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBLineEntry.cpp b/contrib/llvm-project/lldb/source/API/SBLineEntry.cpp
new file mode 100644
index 000000000000..010a6057cd31
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBLineEntry.cpp
@@ -0,0 +1,220 @@
+//===-- SBLineEntry.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBLineEntry.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <limits.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBLineEntry::SBLineEntry() : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBLineEntry);
+}
+
+SBLineEntry::SBLineEntry(const SBLineEntry &rhs) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBLineEntry, (const lldb::SBLineEntry &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+SBLineEntry::SBLineEntry(const lldb_private::LineEntry *lldb_object_ptr)
+ : m_opaque_up() {
+ if (lldb_object_ptr)
+ m_opaque_up = llvm::make_unique<LineEntry>(*lldb_object_ptr);
+}
+
+const SBLineEntry &SBLineEntry::operator=(const SBLineEntry &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBLineEntry &,
+ SBLineEntry, operator=,(const lldb::SBLineEntry &), rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+void SBLineEntry::SetLineEntry(const lldb_private::LineEntry &lldb_object_ref) {
+ m_opaque_up = llvm::make_unique<LineEntry>(lldb_object_ref);
+}
+
+SBLineEntry::~SBLineEntry() {}
+
+SBAddress SBLineEntry::GetStartAddress() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBAddress, SBLineEntry,
+ GetStartAddress);
+
+ SBAddress sb_address;
+ if (m_opaque_up)
+ sb_address.SetAddress(&m_opaque_up->range.GetBaseAddress());
+
+ return LLDB_RECORD_RESULT(sb_address);
+}
+
+SBAddress SBLineEntry::GetEndAddress() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBAddress, SBLineEntry, GetEndAddress);
+
+ SBAddress sb_address;
+ if (m_opaque_up) {
+ sb_address.SetAddress(&m_opaque_up->range.GetBaseAddress());
+ sb_address.OffsetAddress(m_opaque_up->range.GetByteSize());
+ }
+ return LLDB_RECORD_RESULT(sb_address);
+}
+
+bool SBLineEntry::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBLineEntry, IsValid);
+ return this->operator bool();
+}
+SBLineEntry::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBLineEntry, operator bool);
+
+ return m_opaque_up.get() && m_opaque_up->IsValid();
+}
+
+SBFileSpec SBLineEntry::GetFileSpec() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBFileSpec, SBLineEntry, GetFileSpec);
+
+ SBFileSpec sb_file_spec;
+ if (m_opaque_up.get() && m_opaque_up->file)
+ sb_file_spec.SetFileSpec(m_opaque_up->file);
+
+ return LLDB_RECORD_RESULT(sb_file_spec);
+}
+
+uint32_t SBLineEntry::GetLine() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBLineEntry, GetLine);
+
+ uint32_t line = 0;
+ if (m_opaque_up)
+ line = m_opaque_up->line;
+
+ return line;
+}
+
+uint32_t SBLineEntry::GetColumn() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBLineEntry, GetColumn);
+
+ if (m_opaque_up)
+ return m_opaque_up->column;
+ return 0;
+}
+
+void SBLineEntry::SetFileSpec(lldb::SBFileSpec filespec) {
+ LLDB_RECORD_METHOD(void, SBLineEntry, SetFileSpec, (lldb::SBFileSpec),
+ filespec);
+
+ if (filespec.IsValid())
+ ref().file = filespec.ref();
+ else
+ ref().file.Clear();
+}
+void SBLineEntry::SetLine(uint32_t line) {
+ LLDB_RECORD_METHOD(void, SBLineEntry, SetLine, (uint32_t), line);
+
+ ref().line = line;
+}
+
+void SBLineEntry::SetColumn(uint32_t column) {
+ LLDB_RECORD_METHOD(void, SBLineEntry, SetColumn, (uint32_t), column);
+
+ ref().line = column;
+}
+
+bool SBLineEntry::operator==(const SBLineEntry &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBLineEntry, operator==,(const lldb::SBLineEntry &), rhs);
+
+ lldb_private::LineEntry *lhs_ptr = m_opaque_up.get();
+ lldb_private::LineEntry *rhs_ptr = rhs.m_opaque_up.get();
+
+ if (lhs_ptr && rhs_ptr)
+ return lldb_private::LineEntry::Compare(*lhs_ptr, *rhs_ptr) == 0;
+
+ return lhs_ptr == rhs_ptr;
+}
+
+bool SBLineEntry::operator!=(const SBLineEntry &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBLineEntry, operator!=,(const lldb::SBLineEntry &), rhs);
+
+ lldb_private::LineEntry *lhs_ptr = m_opaque_up.get();
+ lldb_private::LineEntry *rhs_ptr = rhs.m_opaque_up.get();
+
+ if (lhs_ptr && rhs_ptr)
+ return lldb_private::LineEntry::Compare(*lhs_ptr, *rhs_ptr) != 0;
+
+ return lhs_ptr != rhs_ptr;
+}
+
+const lldb_private::LineEntry *SBLineEntry::operator->() const {
+ return m_opaque_up.get();
+}
+
+lldb_private::LineEntry &SBLineEntry::ref() {
+ if (m_opaque_up == nullptr)
+ m_opaque_up.reset(new lldb_private::LineEntry());
+ return *m_opaque_up;
+}
+
+const lldb_private::LineEntry &SBLineEntry::ref() const { return *m_opaque_up; }
+
+bool SBLineEntry::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBLineEntry, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_up) {
+ char file_path[PATH_MAX * 2];
+ m_opaque_up->file.GetPath(file_path, sizeof(file_path));
+ strm.Printf("%s:%u", file_path, GetLine());
+ if (GetColumn() > 0)
+ strm.Printf(":%u", GetColumn());
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+lldb_private::LineEntry *SBLineEntry::get() { return m_opaque_up.get(); }
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBLineEntry>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBLineEntry, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBLineEntry, (const lldb::SBLineEntry &));
+ LLDB_REGISTER_METHOD(const lldb::SBLineEntry &,
+ SBLineEntry, operator=,(const lldb::SBLineEntry &));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBAddress, SBLineEntry, GetStartAddress,
+ ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBAddress, SBLineEntry, GetEndAddress, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBLineEntry, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBLineEntry, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBFileSpec, SBLineEntry, GetFileSpec, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBLineEntry, GetLine, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBLineEntry, GetColumn, ());
+ LLDB_REGISTER_METHOD(void, SBLineEntry, SetFileSpec, (lldb::SBFileSpec));
+ LLDB_REGISTER_METHOD(void, SBLineEntry, SetLine, (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBLineEntry, SetColumn, (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBLineEntry, operator==,(const lldb::SBLineEntry &));
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBLineEntry, operator!=,(const lldb::SBLineEntry &));
+ LLDB_REGISTER_METHOD(bool, SBLineEntry, GetDescription, (lldb::SBStream &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBListener.cpp b/contrib/llvm-project/lldb/source/API/SBListener.cpp
new file mode 100644
index 000000000000..4fe90f6f6862
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBListener.cpp
@@ -0,0 +1,373 @@
+//===-- SBListener.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBListener.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBBroadcaster.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Utility/Broadcaster.h"
+#include "lldb/Utility/Listener.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBListener::SBListener() : m_opaque_sp(), m_unused_ptr(nullptr) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBListener);
+}
+
+SBListener::SBListener(const char *name)
+ : m_opaque_sp(Listener::MakeListener(name)), m_unused_ptr(nullptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBListener, (const char *), name);
+}
+
+SBListener::SBListener(const SBListener &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp), m_unused_ptr(nullptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBListener, (const lldb::SBListener &), rhs);
+}
+
+const lldb::SBListener &SBListener::operator=(const lldb::SBListener &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBListener &,
+ SBListener, operator=,(const lldb::SBListener &), rhs);
+
+ if (this != &rhs) {
+ m_opaque_sp = rhs.m_opaque_sp;
+ m_unused_ptr = nullptr;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBListener::SBListener(const lldb::ListenerSP &listener_sp)
+ : m_opaque_sp(listener_sp), m_unused_ptr(nullptr) {}
+
+SBListener::~SBListener() {}
+
+bool SBListener::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBListener, IsValid);
+ return this->operator bool();
+}
+SBListener::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBListener, operator bool);
+
+ return m_opaque_sp != nullptr;
+}
+
+void SBListener::AddEvent(const SBEvent &event) {
+ LLDB_RECORD_METHOD(void, SBListener, AddEvent, (const lldb::SBEvent &),
+ event);
+
+ EventSP &event_sp = event.GetSP();
+ if (event_sp)
+ m_opaque_sp->AddEvent(event_sp);
+}
+
+void SBListener::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBListener, Clear);
+
+ if (m_opaque_sp)
+ m_opaque_sp->Clear();
+}
+
+uint32_t SBListener::StartListeningForEventClass(SBDebugger &debugger,
+ const char *broadcaster_class,
+ uint32_t event_mask) {
+ LLDB_RECORD_METHOD(uint32_t, SBListener, StartListeningForEventClass,
+ (lldb::SBDebugger &, const char *, uint32_t), debugger,
+ broadcaster_class, event_mask);
+
+ if (m_opaque_sp) {
+ Debugger *lldb_debugger = debugger.get();
+ if (!lldb_debugger)
+ return 0;
+ BroadcastEventSpec event_spec(ConstString(broadcaster_class), event_mask);
+ return m_opaque_sp->StartListeningForEventSpec(
+ lldb_debugger->GetBroadcasterManager(), event_spec);
+ } else
+ return 0;
+}
+
+bool SBListener::StopListeningForEventClass(SBDebugger &debugger,
+ const char *broadcaster_class,
+ uint32_t event_mask) {
+ LLDB_RECORD_METHOD(bool, SBListener, StopListeningForEventClass,
+ (lldb::SBDebugger &, const char *, uint32_t), debugger,
+ broadcaster_class, event_mask);
+
+ if (m_opaque_sp) {
+ Debugger *lldb_debugger = debugger.get();
+ if (!lldb_debugger)
+ return false;
+ BroadcastEventSpec event_spec(ConstString(broadcaster_class), event_mask);
+ return m_opaque_sp->StopListeningForEventSpec(
+ lldb_debugger->GetBroadcasterManager(), event_spec);
+ } else
+ return false;
+}
+
+uint32_t SBListener::StartListeningForEvents(const SBBroadcaster &broadcaster,
+ uint32_t event_mask) {
+ LLDB_RECORD_METHOD(uint32_t, SBListener, StartListeningForEvents,
+ (const lldb::SBBroadcaster &, uint32_t), broadcaster,
+ event_mask);
+
+ uint32_t acquired_event_mask = 0;
+ if (m_opaque_sp && broadcaster.IsValid()) {
+ acquired_event_mask =
+ m_opaque_sp->StartListeningForEvents(broadcaster.get(), event_mask);
+ }
+
+ return acquired_event_mask;
+}
+
+bool SBListener::StopListeningForEvents(const SBBroadcaster &broadcaster,
+ uint32_t event_mask) {
+ LLDB_RECORD_METHOD(bool, SBListener, StopListeningForEvents,
+ (const lldb::SBBroadcaster &, uint32_t), broadcaster,
+ event_mask);
+
+ if (m_opaque_sp && broadcaster.IsValid()) {
+ return m_opaque_sp->StopListeningForEvents(broadcaster.get(), event_mask);
+ }
+ return false;
+}
+
+bool SBListener::WaitForEvent(uint32_t timeout_secs, SBEvent &event) {
+ LLDB_RECORD_METHOD(bool, SBListener, WaitForEvent,
+ (uint32_t, lldb::SBEvent &), timeout_secs, event);
+
+ bool success = false;
+
+ if (m_opaque_sp) {
+ Timeout<std::micro> timeout(llvm::None);
+ if (timeout_secs != UINT32_MAX) {
+ assert(timeout_secs != 0); // Take this out after all calls with timeout
+ // set to zero have been removed....
+ timeout = std::chrono::seconds(timeout_secs);
+ }
+ EventSP event_sp;
+ if (m_opaque_sp->GetEvent(event_sp, timeout)) {
+ event.reset(event_sp);
+ success = true;
+ }
+ }
+
+ if (!success)
+ event.reset(nullptr);
+ return success;
+}
+
+bool SBListener::WaitForEventForBroadcaster(uint32_t num_seconds,
+ const SBBroadcaster &broadcaster,
+ SBEvent &event) {
+ LLDB_RECORD_METHOD(bool, SBListener, WaitForEventForBroadcaster,
+ (uint32_t, const lldb::SBBroadcaster &, lldb::SBEvent &),
+ num_seconds, broadcaster, event);
+
+ if (m_opaque_sp && broadcaster.IsValid()) {
+ Timeout<std::micro> timeout(llvm::None);
+ if (num_seconds != UINT32_MAX)
+ timeout = std::chrono::seconds(num_seconds);
+ EventSP event_sp;
+ if (m_opaque_sp->GetEventForBroadcaster(broadcaster.get(), event_sp,
+ timeout)) {
+ event.reset(event_sp);
+ return true;
+ }
+ }
+ event.reset(nullptr);
+ return false;
+}
+
+bool SBListener::WaitForEventForBroadcasterWithType(
+ uint32_t num_seconds, const SBBroadcaster &broadcaster,
+ uint32_t event_type_mask, SBEvent &event) {
+ LLDB_RECORD_METHOD(
+ bool, SBListener, WaitForEventForBroadcasterWithType,
+ (uint32_t, const lldb::SBBroadcaster &, uint32_t, lldb::SBEvent &),
+ num_seconds, broadcaster, event_type_mask, event);
+
+ if (m_opaque_sp && broadcaster.IsValid()) {
+ Timeout<std::micro> timeout(llvm::None);
+ if (num_seconds != UINT32_MAX)
+ timeout = std::chrono::seconds(num_seconds);
+ EventSP event_sp;
+ if (m_opaque_sp->GetEventForBroadcasterWithType(
+ broadcaster.get(), event_type_mask, event_sp, timeout)) {
+ event.reset(event_sp);
+ return true;
+ }
+ }
+ event.reset(nullptr);
+ return false;
+}
+
+bool SBListener::PeekAtNextEvent(SBEvent &event) {
+ LLDB_RECORD_METHOD(bool, SBListener, PeekAtNextEvent, (lldb::SBEvent &),
+ event);
+
+ if (m_opaque_sp) {
+ event.reset(m_opaque_sp->PeekAtNextEvent());
+ return event.IsValid();
+ }
+ event.reset(nullptr);
+ return false;
+}
+
+bool SBListener::PeekAtNextEventForBroadcaster(const SBBroadcaster &broadcaster,
+ SBEvent &event) {
+ LLDB_RECORD_METHOD(bool, SBListener, PeekAtNextEventForBroadcaster,
+ (const lldb::SBBroadcaster &, lldb::SBEvent &),
+ broadcaster, event);
+
+ if (m_opaque_sp && broadcaster.IsValid()) {
+ event.reset(m_opaque_sp->PeekAtNextEventForBroadcaster(broadcaster.get()));
+ return event.IsValid();
+ }
+ event.reset(nullptr);
+ return false;
+}
+
+bool SBListener::PeekAtNextEventForBroadcasterWithType(
+ const SBBroadcaster &broadcaster, uint32_t event_type_mask,
+ SBEvent &event) {
+ LLDB_RECORD_METHOD(bool, SBListener, PeekAtNextEventForBroadcasterWithType,
+ (const lldb::SBBroadcaster &, uint32_t, lldb::SBEvent &),
+ broadcaster, event_type_mask, event);
+
+ if (m_opaque_sp && broadcaster.IsValid()) {
+ event.reset(m_opaque_sp->PeekAtNextEventForBroadcasterWithType(
+ broadcaster.get(), event_type_mask));
+ return event.IsValid();
+ }
+ event.reset(nullptr);
+ return false;
+}
+
+bool SBListener::GetNextEvent(SBEvent &event) {
+ LLDB_RECORD_METHOD(bool, SBListener, GetNextEvent, (lldb::SBEvent &), event);
+
+ if (m_opaque_sp) {
+ EventSP event_sp;
+ if (m_opaque_sp->GetEvent(event_sp, std::chrono::seconds(0))) {
+ event.reset(event_sp);
+ return true;
+ }
+ }
+ event.reset(nullptr);
+ return false;
+}
+
+bool SBListener::GetNextEventForBroadcaster(const SBBroadcaster &broadcaster,
+ SBEvent &event) {
+ LLDB_RECORD_METHOD(bool, SBListener, GetNextEventForBroadcaster,
+ (const lldb::SBBroadcaster &, lldb::SBEvent &),
+ broadcaster, event);
+
+ if (m_opaque_sp && broadcaster.IsValid()) {
+ EventSP event_sp;
+ if (m_opaque_sp->GetEventForBroadcaster(broadcaster.get(), event_sp,
+ std::chrono::seconds(0))) {
+ event.reset(event_sp);
+ return true;
+ }
+ }
+ event.reset(nullptr);
+ return false;
+}
+
+bool SBListener::GetNextEventForBroadcasterWithType(
+ const SBBroadcaster &broadcaster, uint32_t event_type_mask,
+ SBEvent &event) {
+ LLDB_RECORD_METHOD(bool, SBListener, GetNextEventForBroadcasterWithType,
+ (const lldb::SBBroadcaster &, uint32_t, lldb::SBEvent &),
+ broadcaster, event_type_mask, event);
+
+ if (m_opaque_sp && broadcaster.IsValid()) {
+ EventSP event_sp;
+ if (m_opaque_sp->GetEventForBroadcasterWithType(broadcaster.get(),
+ event_type_mask, event_sp,
+ std::chrono::seconds(0))) {
+ event.reset(event_sp);
+ return true;
+ }
+ }
+ event.reset(nullptr);
+ return false;
+}
+
+bool SBListener::HandleBroadcastEvent(const SBEvent &event) {
+ LLDB_RECORD_METHOD(bool, SBListener, HandleBroadcastEvent,
+ (const lldb::SBEvent &), event);
+
+ if (m_opaque_sp)
+ return m_opaque_sp->HandleBroadcastEvent(event.GetSP());
+ return false;
+}
+
+lldb::ListenerSP SBListener::GetSP() { return m_opaque_sp; }
+
+Listener *SBListener::operator->() const { return m_opaque_sp.get(); }
+
+Listener *SBListener::get() const { return m_opaque_sp.get(); }
+
+void SBListener::reset(ListenerSP listener_sp) {
+ m_opaque_sp = listener_sp;
+ m_unused_ptr = nullptr;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBListener>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBListener, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBListener, (const char *));
+ LLDB_REGISTER_CONSTRUCTOR(SBListener, (const lldb::SBListener &));
+ LLDB_REGISTER_METHOD(const lldb::SBListener &,
+ SBListener, operator=,(const lldb::SBListener &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBListener, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBListener, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBListener, AddEvent, (const lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(void, SBListener, Clear, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBListener, StartListeningForEventClass,
+ (lldb::SBDebugger &, const char *, uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBListener, StopListeningForEventClass,
+ (lldb::SBDebugger &, const char *, uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBListener, StartListeningForEvents,
+ (const lldb::SBBroadcaster &, uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBListener, StopListeningForEvents,
+ (const lldb::SBBroadcaster &, uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBListener, WaitForEvent,
+ (uint32_t, lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(
+ bool, SBListener, WaitForEventForBroadcaster,
+ (uint32_t, const lldb::SBBroadcaster &, lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(
+ bool, SBListener, WaitForEventForBroadcasterWithType,
+ (uint32_t, const lldb::SBBroadcaster &, uint32_t, lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(bool, SBListener, PeekAtNextEvent, (lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(bool, SBListener, PeekAtNextEventForBroadcaster,
+ (const lldb::SBBroadcaster &, lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(
+ bool, SBListener, PeekAtNextEventForBroadcasterWithType,
+ (const lldb::SBBroadcaster &, uint32_t, lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(bool, SBListener, GetNextEvent, (lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(bool, SBListener, GetNextEventForBroadcaster,
+ (const lldb::SBBroadcaster &, lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(
+ bool, SBListener, GetNextEventForBroadcasterWithType,
+ (const lldb::SBBroadcaster &, uint32_t, lldb::SBEvent &));
+ LLDB_REGISTER_METHOD(bool, SBListener, HandleBroadcastEvent,
+ (const lldb::SBEvent &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBMemoryRegionInfo.cpp b/contrib/llvm-project/lldb/source/API/SBMemoryRegionInfo.cpp
new file mode 100644
index 000000000000..d25570f51ce5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBMemoryRegionInfo.cpp
@@ -0,0 +1,166 @@
+//===-- SBMemoryRegionInfo.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBMemoryRegionInfo.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBDefines.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBMemoryRegionInfo::SBMemoryRegionInfo() : m_opaque_up(new MemoryRegionInfo()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBMemoryRegionInfo);
+}
+
+SBMemoryRegionInfo::SBMemoryRegionInfo(const MemoryRegionInfo *lldb_object_ptr)
+ : m_opaque_up(new MemoryRegionInfo()) {
+ if (lldb_object_ptr)
+ ref() = *lldb_object_ptr;
+}
+
+SBMemoryRegionInfo::SBMemoryRegionInfo(const SBMemoryRegionInfo &rhs)
+ : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBMemoryRegionInfo,
+ (const lldb::SBMemoryRegionInfo &), rhs);
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+const SBMemoryRegionInfo &SBMemoryRegionInfo::
+operator=(const SBMemoryRegionInfo &rhs) {
+ LLDB_RECORD_METHOD(
+ const lldb::SBMemoryRegionInfo &,
+ SBMemoryRegionInfo, operator=,(const lldb::SBMemoryRegionInfo &), rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBMemoryRegionInfo::~SBMemoryRegionInfo() {}
+
+void SBMemoryRegionInfo::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBMemoryRegionInfo, Clear);
+
+ m_opaque_up->Clear();
+}
+
+bool SBMemoryRegionInfo::operator==(const SBMemoryRegionInfo &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBMemoryRegionInfo, operator==,(const lldb::SBMemoryRegionInfo &),
+ rhs);
+
+ return ref() == rhs.ref();
+}
+
+bool SBMemoryRegionInfo::operator!=(const SBMemoryRegionInfo &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBMemoryRegionInfo, operator!=,(const lldb::SBMemoryRegionInfo &),
+ rhs);
+
+ return ref() != rhs.ref();
+}
+
+MemoryRegionInfo &SBMemoryRegionInfo::ref() { return *m_opaque_up; }
+
+const MemoryRegionInfo &SBMemoryRegionInfo::ref() const { return *m_opaque_up; }
+
+lldb::addr_t SBMemoryRegionInfo::GetRegionBase() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBMemoryRegionInfo, GetRegionBase);
+
+ return m_opaque_up->GetRange().GetRangeBase();
+}
+
+lldb::addr_t SBMemoryRegionInfo::GetRegionEnd() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBMemoryRegionInfo, GetRegionEnd);
+
+ return m_opaque_up->GetRange().GetRangeEnd();
+}
+
+bool SBMemoryRegionInfo::IsReadable() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBMemoryRegionInfo, IsReadable);
+
+ return m_opaque_up->GetReadable() == MemoryRegionInfo::eYes;
+}
+
+bool SBMemoryRegionInfo::IsWritable() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBMemoryRegionInfo, IsWritable);
+
+ return m_opaque_up->GetWritable() == MemoryRegionInfo::eYes;
+}
+
+bool SBMemoryRegionInfo::IsExecutable() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBMemoryRegionInfo, IsExecutable);
+
+ return m_opaque_up->GetExecutable() == MemoryRegionInfo::eYes;
+}
+
+bool SBMemoryRegionInfo::IsMapped() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBMemoryRegionInfo, IsMapped);
+
+ return m_opaque_up->GetMapped() == MemoryRegionInfo::eYes;
+}
+
+const char *SBMemoryRegionInfo::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBMemoryRegionInfo, GetName);
+
+ return m_opaque_up->GetName().AsCString();
+}
+
+bool SBMemoryRegionInfo::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBMemoryRegionInfo, GetDescription,
+ (lldb::SBStream &), description);
+
+ Stream &strm = description.ref();
+ const addr_t load_addr = m_opaque_up->GetRange().base;
+
+ strm.Printf("[0x%16.16" PRIx64 "-0x%16.16" PRIx64 " ", load_addr,
+ load_addr + m_opaque_up->GetRange().size);
+ strm.Printf(m_opaque_up->GetReadable() ? "R" : "-");
+ strm.Printf(m_opaque_up->GetWritable() ? "W" : "-");
+ strm.Printf(m_opaque_up->GetExecutable() ? "X" : "-");
+ strm.Printf("]");
+
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBMemoryRegionInfo>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBMemoryRegionInfo, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBMemoryRegionInfo,
+ (const lldb::SBMemoryRegionInfo &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBMemoryRegionInfo &,
+ SBMemoryRegionInfo, operator=,(const lldb::SBMemoryRegionInfo &));
+ LLDB_REGISTER_METHOD(void, SBMemoryRegionInfo, Clear, ());
+ LLDB_REGISTER_METHOD_CONST(
+ bool,
+ SBMemoryRegionInfo, operator==,(const lldb::SBMemoryRegionInfo &));
+ LLDB_REGISTER_METHOD_CONST(
+ bool,
+ SBMemoryRegionInfo, operator!=,(const lldb::SBMemoryRegionInfo &));
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBMemoryRegionInfo, GetRegionBase, ());
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBMemoryRegionInfo, GetRegionEnd, ());
+ LLDB_REGISTER_METHOD(bool, SBMemoryRegionInfo, IsReadable, ());
+ LLDB_REGISTER_METHOD(bool, SBMemoryRegionInfo, IsWritable, ());
+ LLDB_REGISTER_METHOD(bool, SBMemoryRegionInfo, IsExecutable, ());
+ LLDB_REGISTER_METHOD(bool, SBMemoryRegionInfo, IsMapped, ());
+ LLDB_REGISTER_METHOD(const char *, SBMemoryRegionInfo, GetName, ());
+ LLDB_REGISTER_METHOD(bool, SBMemoryRegionInfo, GetDescription,
+ (lldb::SBStream &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBMemoryRegionInfoList.cpp b/contrib/llvm-project/lldb/source/API/SBMemoryRegionInfoList.cpp
new file mode 100644
index 000000000000..32a3afb84af0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBMemoryRegionInfoList.cpp
@@ -0,0 +1,166 @@
+//===-- SBMemoryRegionInfoList.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBMemoryRegionInfoList.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBMemoryRegionInfo.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+class MemoryRegionInfoListImpl {
+public:
+ MemoryRegionInfoListImpl() : m_regions() {}
+
+ MemoryRegionInfoListImpl(const MemoryRegionInfoListImpl &rhs)
+ : m_regions(rhs.m_regions) {}
+
+ MemoryRegionInfoListImpl &operator=(const MemoryRegionInfoListImpl &rhs) {
+ if (this == &rhs)
+ return *this;
+ m_regions = rhs.m_regions;
+ return *this;
+ }
+
+ size_t GetSize() const { return m_regions.size(); }
+
+ void Reserve(size_t capacity) { return m_regions.reserve(capacity); }
+
+ void Append(const MemoryRegionInfo &sb_region) {
+ m_regions.push_back(sb_region);
+ }
+
+ void Append(const MemoryRegionInfoListImpl &list) {
+ Reserve(GetSize() + list.GetSize());
+
+ for (const auto &val : list.m_regions)
+ Append(val);
+ }
+
+ void Clear() { m_regions.clear(); }
+
+ bool GetMemoryRegionInfoAtIndex(size_t index,
+ MemoryRegionInfo &region_info) {
+ if (index >= GetSize())
+ return false;
+ region_info = m_regions[index];
+ return true;
+ }
+
+ MemoryRegionInfos &Ref() { return m_regions; }
+
+ const MemoryRegionInfos &Ref() const { return m_regions; }
+
+private:
+ MemoryRegionInfos m_regions;
+};
+
+MemoryRegionInfos &SBMemoryRegionInfoList::ref() { return m_opaque_up->Ref(); }
+
+const MemoryRegionInfos &SBMemoryRegionInfoList::ref() const {
+ return m_opaque_up->Ref();
+}
+
+SBMemoryRegionInfoList::SBMemoryRegionInfoList()
+ : m_opaque_up(new MemoryRegionInfoListImpl()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBMemoryRegionInfoList);
+}
+
+SBMemoryRegionInfoList::SBMemoryRegionInfoList(
+ const SBMemoryRegionInfoList &rhs)
+ : m_opaque_up(new MemoryRegionInfoListImpl(*rhs.m_opaque_up)) {
+ LLDB_RECORD_CONSTRUCTOR(SBMemoryRegionInfoList,
+ (const lldb::SBMemoryRegionInfoList &), rhs);
+}
+
+SBMemoryRegionInfoList::~SBMemoryRegionInfoList() {}
+
+const SBMemoryRegionInfoList &SBMemoryRegionInfoList::
+operator=(const SBMemoryRegionInfoList &rhs) {
+ LLDB_RECORD_METHOD(
+ const lldb::SBMemoryRegionInfoList &,
+ SBMemoryRegionInfoList, operator=,(const lldb::SBMemoryRegionInfoList &),
+ rhs);
+
+ if (this != &rhs) {
+ *m_opaque_up = *rhs.m_opaque_up;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+uint32_t SBMemoryRegionInfoList::GetSize() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBMemoryRegionInfoList, GetSize);
+
+ return m_opaque_up->GetSize();
+}
+
+bool SBMemoryRegionInfoList::GetMemoryRegionAtIndex(
+ uint32_t idx, SBMemoryRegionInfo &region_info) {
+ LLDB_RECORD_METHOD(bool, SBMemoryRegionInfoList, GetMemoryRegionAtIndex,
+ (uint32_t, lldb::SBMemoryRegionInfo &), idx, region_info);
+
+ return m_opaque_up->GetMemoryRegionInfoAtIndex(idx, region_info.ref());
+}
+
+void SBMemoryRegionInfoList::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBMemoryRegionInfoList, Clear);
+
+ m_opaque_up->Clear();
+}
+
+void SBMemoryRegionInfoList::Append(SBMemoryRegionInfo &sb_region) {
+ LLDB_RECORD_METHOD(void, SBMemoryRegionInfoList, Append,
+ (lldb::SBMemoryRegionInfo &), sb_region);
+
+ m_opaque_up->Append(sb_region.ref());
+}
+
+void SBMemoryRegionInfoList::Append(SBMemoryRegionInfoList &sb_region_list) {
+ LLDB_RECORD_METHOD(void, SBMemoryRegionInfoList, Append,
+ (lldb::SBMemoryRegionInfoList &), sb_region_list);
+
+ m_opaque_up->Append(*sb_region_list);
+}
+
+const MemoryRegionInfoListImpl *SBMemoryRegionInfoList::operator->() const {
+ return m_opaque_up.get();
+}
+
+const MemoryRegionInfoListImpl &SBMemoryRegionInfoList::operator*() const {
+ assert(m_opaque_up.get());
+ return *m_opaque_up;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBMemoryRegionInfoList>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBMemoryRegionInfoList, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBMemoryRegionInfoList,
+ (const lldb::SBMemoryRegionInfoList &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBMemoryRegionInfoList &,
+ SBMemoryRegionInfoList, operator=,(
+ const lldb::SBMemoryRegionInfoList &));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBMemoryRegionInfoList, GetSize, ());
+ LLDB_REGISTER_METHOD(bool, SBMemoryRegionInfoList, GetMemoryRegionAtIndex,
+ (uint32_t, lldb::SBMemoryRegionInfo &));
+ LLDB_REGISTER_METHOD(void, SBMemoryRegionInfoList, Clear, ());
+ LLDB_REGISTER_METHOD(void, SBMemoryRegionInfoList, Append,
+ (lldb::SBMemoryRegionInfo &));
+ LLDB_REGISTER_METHOD(void, SBMemoryRegionInfoList, Append,
+ (lldb::SBMemoryRegionInfoList &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBModule.cpp b/contrib/llvm-project/lldb/source/API/SBModule.cpp
new file mode 100644
index 000000000000..4bd32bce1c53
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBModule.cpp
@@ -0,0 +1,768 @@
+//===-- SBModule.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBModule.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBModuleSpec.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBSymbolContextList.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/ValueObjectList.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBModule::SBModule() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBModule);
+}
+
+SBModule::SBModule(const lldb::ModuleSP &module_sp) : m_opaque_sp(module_sp) {}
+
+SBModule::SBModule(const SBModuleSpec &module_spec) : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR(SBModule, (const lldb::SBModuleSpec &), module_spec);
+
+ ModuleSP module_sp;
+ Status error = ModuleList::GetSharedModule(
+ *module_spec.m_opaque_up, module_sp, nullptr, nullptr, nullptr);
+ if (module_sp)
+ SetSP(module_sp);
+}
+
+SBModule::SBModule(const SBModule &rhs) : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBModule, (const lldb::SBModule &), rhs);
+}
+
+SBModule::SBModule(lldb::SBProcess &process, lldb::addr_t header_addr)
+ : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR(SBModule, (lldb::SBProcess &, lldb::addr_t), process,
+ header_addr);
+
+ ProcessSP process_sp(process.GetSP());
+ if (process_sp) {
+ m_opaque_sp = process_sp->ReadModuleFromMemory(FileSpec(), header_addr);
+ if (m_opaque_sp) {
+ Target &target = process_sp->GetTarget();
+ bool changed = false;
+ m_opaque_sp->SetLoadAddress(target, 0, true, changed);
+ target.GetImages().Append(m_opaque_sp);
+ }
+ }
+}
+
+const SBModule &SBModule::operator=(const SBModule &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBModule &,
+ SBModule, operator=,(const lldb::SBModule &), rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = rhs.m_opaque_sp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBModule::~SBModule() {}
+
+bool SBModule::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBModule, IsValid);
+ return this->operator bool();
+}
+SBModule::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBModule, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+void SBModule::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBModule, Clear);
+
+ m_opaque_sp.reset();
+}
+
+SBFileSpec SBModule::GetFileSpec() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBFileSpec, SBModule, GetFileSpec);
+
+ SBFileSpec file_spec;
+ ModuleSP module_sp(GetSP());
+ if (module_sp)
+ file_spec.SetFileSpec(module_sp->GetFileSpec());
+
+ return LLDB_RECORD_RESULT(file_spec);
+}
+
+lldb::SBFileSpec SBModule::GetPlatformFileSpec() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBFileSpec, SBModule,
+ GetPlatformFileSpec);
+
+
+ SBFileSpec file_spec;
+ ModuleSP module_sp(GetSP());
+ if (module_sp)
+ file_spec.SetFileSpec(module_sp->GetPlatformFileSpec());
+
+ return LLDB_RECORD_RESULT(file_spec);
+}
+
+bool SBModule::SetPlatformFileSpec(const lldb::SBFileSpec &platform_file) {
+ LLDB_RECORD_METHOD(bool, SBModule, SetPlatformFileSpec,
+ (const lldb::SBFileSpec &), platform_file);
+
+ bool result = false;
+
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ module_sp->SetPlatformFileSpec(*platform_file);
+ result = true;
+ }
+
+ return result;
+}
+
+lldb::SBFileSpec SBModule::GetRemoteInstallFileSpec() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFileSpec, SBModule,
+ GetRemoteInstallFileSpec);
+
+ SBFileSpec sb_file_spec;
+ ModuleSP module_sp(GetSP());
+ if (module_sp)
+ sb_file_spec.SetFileSpec(module_sp->GetRemoteInstallFileSpec());
+ return LLDB_RECORD_RESULT(sb_file_spec);
+}
+
+bool SBModule::SetRemoteInstallFileSpec(lldb::SBFileSpec &file) {
+ LLDB_RECORD_METHOD(bool, SBModule, SetRemoteInstallFileSpec,
+ (lldb::SBFileSpec &), file);
+
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ module_sp->SetRemoteInstallFileSpec(file.ref());
+ return true;
+ }
+ return false;
+}
+
+const uint8_t *SBModule::GetUUIDBytes() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const uint8_t *, SBModule, GetUUIDBytes);
+
+ const uint8_t *uuid_bytes = nullptr;
+ ModuleSP module_sp(GetSP());
+ if (module_sp)
+ uuid_bytes = module_sp->GetUUID().GetBytes().data();
+
+ return uuid_bytes;
+}
+
+const char *SBModule::GetUUIDString() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBModule, GetUUIDString);
+
+ const char *uuid_cstr = nullptr;
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ // We are going to return a "const char *" value through the public API, so
+ // we need to constify it so it gets added permanently the string pool and
+ // then we don't need to worry about the lifetime of the string as it will
+ // never go away once it has been put into the ConstString string pool
+ uuid_cstr = ConstString(module_sp->GetUUID().GetAsString()).GetCString();
+ }
+
+ if (uuid_cstr && uuid_cstr[0]) {
+ return uuid_cstr;
+ }
+
+ return nullptr;
+}
+
+bool SBModule::operator==(const SBModule &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBModule, operator==,(const lldb::SBModule &),
+ rhs);
+
+ if (m_opaque_sp)
+ return m_opaque_sp.get() == rhs.m_opaque_sp.get();
+ return false;
+}
+
+bool SBModule::operator!=(const SBModule &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBModule, operator!=,(const lldb::SBModule &),
+ rhs);
+
+ if (m_opaque_sp)
+ return m_opaque_sp.get() != rhs.m_opaque_sp.get();
+ return false;
+}
+
+ModuleSP SBModule::GetSP() const { return m_opaque_sp; }
+
+void SBModule::SetSP(const ModuleSP &module_sp) { m_opaque_sp = module_sp; }
+
+SBAddress SBModule::ResolveFileAddress(lldb::addr_t vm_addr) {
+ LLDB_RECORD_METHOD(lldb::SBAddress, SBModule, ResolveFileAddress,
+ (lldb::addr_t), vm_addr);
+
+ lldb::SBAddress sb_addr;
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ Address addr;
+ if (module_sp->ResolveFileAddress(vm_addr, addr))
+ sb_addr.ref() = addr;
+ }
+ return LLDB_RECORD_RESULT(sb_addr);
+}
+
+SBSymbolContext
+SBModule::ResolveSymbolContextForAddress(const SBAddress &addr,
+ uint32_t resolve_scope) {
+ LLDB_RECORD_METHOD(lldb::SBSymbolContext, SBModule,
+ ResolveSymbolContextForAddress,
+ (const lldb::SBAddress &, uint32_t), addr, resolve_scope);
+
+ SBSymbolContext sb_sc;
+ ModuleSP module_sp(GetSP());
+ SymbolContextItem scope = static_cast<SymbolContextItem>(resolve_scope);
+ if (module_sp && addr.IsValid())
+ module_sp->ResolveSymbolContextForAddress(addr.ref(), scope, *sb_sc);
+ return LLDB_RECORD_RESULT(sb_sc);
+}
+
+bool SBModule::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBModule, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ module_sp->GetDescription(&strm);
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+uint32_t SBModule::GetNumCompileUnits() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBModule, GetNumCompileUnits);
+
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ return module_sp->GetNumCompileUnits();
+ }
+ return 0;
+}
+
+SBCompileUnit SBModule::GetCompileUnitAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBCompileUnit, SBModule, GetCompileUnitAtIndex,
+ (uint32_t), index);
+
+ SBCompileUnit sb_cu;
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(index);
+ sb_cu.reset(cu_sp.get());
+ }
+ return LLDB_RECORD_RESULT(sb_cu);
+}
+
+SBSymbolContextList SBModule::FindCompileUnits(const SBFileSpec &sb_file_spec) {
+ LLDB_RECORD_METHOD(lldb::SBSymbolContextList, SBModule, FindCompileUnits,
+ (const lldb::SBFileSpec &), sb_file_spec);
+
+ SBSymbolContextList sb_sc_list;
+ const ModuleSP module_sp(GetSP());
+ if (sb_file_spec.IsValid() && module_sp) {
+ const bool append = true;
+ module_sp->FindCompileUnits(*sb_file_spec, append, *sb_sc_list);
+ }
+ return LLDB_RECORD_RESULT(sb_sc_list);
+}
+
+static Symtab *GetUnifiedSymbolTable(const lldb::ModuleSP &module_sp) {
+ if (module_sp) {
+ SymbolVendor *symbols = module_sp->GetSymbolVendor();
+ if (symbols)
+ return symbols->GetSymtab();
+ }
+ return nullptr;
+}
+
+size_t SBModule::GetNumSymbols() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBModule, GetNumSymbols);
+
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ Symtab *symtab = GetUnifiedSymbolTable(module_sp);
+ if (symtab)
+ return symtab->GetNumSymbols();
+ }
+ return 0;
+}
+
+SBSymbol SBModule::GetSymbolAtIndex(size_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBSymbol, SBModule, GetSymbolAtIndex, (size_t), idx);
+
+ SBSymbol sb_symbol;
+ ModuleSP module_sp(GetSP());
+ Symtab *symtab = GetUnifiedSymbolTable(module_sp);
+ if (symtab)
+ sb_symbol.SetSymbol(symtab->SymbolAtIndex(idx));
+ return LLDB_RECORD_RESULT(sb_symbol);
+}
+
+lldb::SBSymbol SBModule::FindSymbol(const char *name,
+ lldb::SymbolType symbol_type) {
+ LLDB_RECORD_METHOD(lldb::SBSymbol, SBModule, FindSymbol,
+ (const char *, lldb::SymbolType), name, symbol_type);
+
+ SBSymbol sb_symbol;
+ if (name && name[0]) {
+ ModuleSP module_sp(GetSP());
+ Symtab *symtab = GetUnifiedSymbolTable(module_sp);
+ if (symtab)
+ sb_symbol.SetSymbol(symtab->FindFirstSymbolWithNameAndType(
+ ConstString(name), symbol_type, Symtab::eDebugAny,
+ Symtab::eVisibilityAny));
+ }
+ return LLDB_RECORD_RESULT(sb_symbol);
+}
+
+lldb::SBSymbolContextList SBModule::FindSymbols(const char *name,
+ lldb::SymbolType symbol_type) {
+ LLDB_RECORD_METHOD(lldb::SBSymbolContextList, SBModule, FindSymbols,
+ (const char *, lldb::SymbolType), name, symbol_type);
+
+ SBSymbolContextList sb_sc_list;
+ if (name && name[0]) {
+ ModuleSP module_sp(GetSP());
+ Symtab *symtab = GetUnifiedSymbolTable(module_sp);
+ if (symtab) {
+ std::vector<uint32_t> matching_symbol_indexes;
+ const size_t num_matches = symtab->FindAllSymbolsWithNameAndType(
+ ConstString(name), symbol_type, matching_symbol_indexes);
+ if (num_matches) {
+ SymbolContext sc;
+ sc.module_sp = module_sp;
+ SymbolContextList &sc_list = *sb_sc_list;
+ for (size_t i = 0; i < num_matches; ++i) {
+ sc.symbol = symtab->SymbolAtIndex(matching_symbol_indexes[i]);
+ if (sc.symbol)
+ sc_list.Append(sc);
+ }
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_sc_list);
+}
+
+size_t SBModule::GetNumSections() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBModule, GetNumSections);
+
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ // Give the symbol vendor a chance to add to the unified section list.
+ module_sp->GetSymbolVendor();
+ SectionList *section_list = module_sp->GetSectionList();
+ if (section_list)
+ return section_list->GetSize();
+ }
+ return 0;
+}
+
+SBSection SBModule::GetSectionAtIndex(size_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBSection, SBModule, GetSectionAtIndex, (size_t),
+ idx);
+
+ SBSection sb_section;
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ // Give the symbol vendor a chance to add to the unified section list.
+ module_sp->GetSymbolVendor();
+ SectionList *section_list = module_sp->GetSectionList();
+
+ if (section_list)
+ sb_section.SetSP(section_list->GetSectionAtIndex(idx));
+ }
+ return LLDB_RECORD_RESULT(sb_section);
+}
+
+lldb::SBSymbolContextList SBModule::FindFunctions(const char *name,
+ uint32_t name_type_mask) {
+ LLDB_RECORD_METHOD(lldb::SBSymbolContextList, SBModule, FindFunctions,
+ (const char *, uint32_t), name, name_type_mask);
+
+ lldb::SBSymbolContextList sb_sc_list;
+ ModuleSP module_sp(GetSP());
+ if (name && module_sp) {
+ const bool append = true;
+ const bool symbols_ok = true;
+ const bool inlines_ok = true;
+ FunctionNameType type = static_cast<FunctionNameType>(name_type_mask);
+ module_sp->FindFunctions(ConstString(name), nullptr, type, symbols_ok,
+ inlines_ok, append, *sb_sc_list);
+ }
+ return LLDB_RECORD_RESULT(sb_sc_list);
+}
+
+SBValueList SBModule::FindGlobalVariables(SBTarget &target, const char *name,
+ uint32_t max_matches) {
+ LLDB_RECORD_METHOD(lldb::SBValueList, SBModule, FindGlobalVariables,
+ (lldb::SBTarget &, const char *, uint32_t), target, name,
+ max_matches);
+
+ SBValueList sb_value_list;
+ ModuleSP module_sp(GetSP());
+ if (name && module_sp) {
+ VariableList variable_list;
+ const uint32_t match_count = module_sp->FindGlobalVariables(
+ ConstString(name), nullptr, max_matches, variable_list);
+
+ if (match_count > 0) {
+ for (uint32_t i = 0; i < match_count; ++i) {
+ lldb::ValueObjectSP valobj_sp;
+ TargetSP target_sp(target.GetSP());
+ valobj_sp = ValueObjectVariable::Create(
+ target_sp.get(), variable_list.GetVariableAtIndex(i));
+ if (valobj_sp)
+ sb_value_list.Append(SBValue(valobj_sp));
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_value_list);
+}
+
+lldb::SBValue SBModule::FindFirstGlobalVariable(lldb::SBTarget &target,
+ const char *name) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBModule, FindFirstGlobalVariable,
+ (lldb::SBTarget &, const char *), target, name);
+
+ SBValueList sb_value_list(FindGlobalVariables(target, name, 1));
+ if (sb_value_list.IsValid() && sb_value_list.GetSize() > 0)
+ return LLDB_RECORD_RESULT(sb_value_list.GetValueAtIndex(0));
+ return LLDB_RECORD_RESULT(SBValue());
+}
+
+lldb::SBType SBModule::FindFirstType(const char *name_cstr) {
+ LLDB_RECORD_METHOD(lldb::SBType, SBModule, FindFirstType, (const char *),
+ name_cstr);
+
+ SBType sb_type;
+ ModuleSP module_sp(GetSP());
+ if (name_cstr && module_sp) {
+ SymbolContext sc;
+ const bool exact_match = false;
+ ConstString name(name_cstr);
+
+ sb_type = SBType(module_sp->FindFirstType(sc, name, exact_match));
+
+ if (!sb_type.IsValid()) {
+ TypeSystem *type_system =
+ module_sp->GetTypeSystemForLanguage(eLanguageTypeC);
+ if (type_system)
+ sb_type = SBType(type_system->GetBuiltinTypeByName(name));
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_type);
+}
+
+lldb::SBType SBModule::GetBasicType(lldb::BasicType type) {
+ LLDB_RECORD_METHOD(lldb::SBType, SBModule, GetBasicType, (lldb::BasicType),
+ type);
+
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ TypeSystem *type_system =
+ module_sp->GetTypeSystemForLanguage(eLanguageTypeC);
+ if (type_system)
+ return LLDB_RECORD_RESULT(SBType(type_system->GetBasicTypeFromAST(type)));
+ }
+ return LLDB_RECORD_RESULT(SBType());
+}
+
+lldb::SBTypeList SBModule::FindTypes(const char *type) {
+ LLDB_RECORD_METHOD(lldb::SBTypeList, SBModule, FindTypes, (const char *),
+ type);
+
+ SBTypeList retval;
+
+ ModuleSP module_sp(GetSP());
+ if (type && module_sp) {
+ TypeList type_list;
+ const bool exact_match = false;
+ ConstString name(type);
+ llvm::DenseSet<SymbolFile *> searched_symbol_files;
+ const uint32_t num_matches = module_sp->FindTypes(
+ name, exact_match, UINT32_MAX, searched_symbol_files, type_list);
+
+ if (num_matches > 0) {
+ for (size_t idx = 0; idx < num_matches; idx++) {
+ TypeSP type_sp(type_list.GetTypeAtIndex(idx));
+ if (type_sp)
+ retval.Append(SBType(type_sp));
+ }
+ } else {
+ TypeSystem *type_system =
+ module_sp->GetTypeSystemForLanguage(eLanguageTypeC);
+ if (type_system) {
+ CompilerType compiler_type = type_system->GetBuiltinTypeByName(name);
+ if (compiler_type)
+ retval.Append(SBType(compiler_type));
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(retval);
+}
+
+lldb::SBType SBModule::GetTypeByID(lldb::user_id_t uid) {
+ LLDB_RECORD_METHOD(lldb::SBType, SBModule, GetTypeByID, (lldb::user_id_t),
+ uid);
+
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ SymbolVendor *vendor = module_sp->GetSymbolVendor();
+ if (vendor) {
+ Type *type_ptr = vendor->ResolveTypeUID(uid);
+ if (type_ptr)
+ return LLDB_RECORD_RESULT(SBType(type_ptr->shared_from_this()));
+ }
+ }
+ return LLDB_RECORD_RESULT(SBType());
+}
+
+lldb::SBTypeList SBModule::GetTypes(uint32_t type_mask) {
+ LLDB_RECORD_METHOD(lldb::SBTypeList, SBModule, GetTypes, (uint32_t),
+ type_mask);
+
+ SBTypeList sb_type_list;
+
+ ModuleSP module_sp(GetSP());
+ if (!module_sp)
+ return LLDB_RECORD_RESULT(sb_type_list);
+ SymbolVendor *vendor = module_sp->GetSymbolVendor();
+ if (!vendor)
+ return LLDB_RECORD_RESULT(sb_type_list);
+
+ TypeClass type_class = static_cast<TypeClass>(type_mask);
+ TypeList type_list;
+ vendor->GetTypes(nullptr, type_class, type_list);
+ sb_type_list.m_opaque_up->Append(type_list);
+ return LLDB_RECORD_RESULT(sb_type_list);
+}
+
+SBSection SBModule::FindSection(const char *sect_name) {
+ LLDB_RECORD_METHOD(lldb::SBSection, SBModule, FindSection, (const char *),
+ sect_name);
+
+ SBSection sb_section;
+
+ ModuleSP module_sp(GetSP());
+ if (sect_name && module_sp) {
+ // Give the symbol vendor a chance to add to the unified section list.
+ module_sp->GetSymbolVendor();
+ SectionList *section_list = module_sp->GetSectionList();
+ if (section_list) {
+ ConstString const_sect_name(sect_name);
+ SectionSP section_sp(section_list->FindSectionByName(const_sect_name));
+ if (section_sp) {
+ sb_section.SetSP(section_sp);
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_section);
+}
+
+lldb::ByteOrder SBModule::GetByteOrder() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::ByteOrder, SBModule, GetByteOrder);
+
+ ModuleSP module_sp(GetSP());
+ if (module_sp)
+ return module_sp->GetArchitecture().GetByteOrder();
+ return eByteOrderInvalid;
+}
+
+const char *SBModule::GetTriple() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBModule, GetTriple);
+
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ std::string triple(module_sp->GetArchitecture().GetTriple().str());
+ // Unique the string so we don't run into ownership issues since the const
+ // strings put the string into the string pool once and the strings never
+ // comes out
+ ConstString const_triple(triple.c_str());
+ return const_triple.GetCString();
+ }
+ return nullptr;
+}
+
+uint32_t SBModule::GetAddressByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBModule, GetAddressByteSize);
+
+ ModuleSP module_sp(GetSP());
+ if (module_sp)
+ return module_sp->GetArchitecture().GetAddressByteSize();
+ return sizeof(void *);
+}
+
+uint32_t SBModule::GetVersion(uint32_t *versions, uint32_t num_versions) {
+ LLDB_RECORD_METHOD(uint32_t, SBModule, GetVersion, (uint32_t *, uint32_t),
+ versions, num_versions);
+
+ llvm::VersionTuple version;
+ if (ModuleSP module_sp = GetSP())
+ version = module_sp->GetVersion();
+ uint32_t result = 0;
+ if (!version.empty())
+ ++result;
+ if (version.getMinor())
+ ++result;
+ if(version.getSubminor())
+ ++result;
+
+ if (!versions)
+ return result;
+
+ if (num_versions > 0)
+ versions[0] = version.empty() ? UINT32_MAX : version.getMajor();
+ if (num_versions > 1)
+ versions[1] = version.getMinor().getValueOr(UINT32_MAX);
+ if (num_versions > 2)
+ versions[2] = version.getSubminor().getValueOr(UINT32_MAX);
+ for (uint32_t i = 3; i < num_versions; ++i)
+ versions[i] = UINT32_MAX;
+ return result;
+}
+
+lldb::SBFileSpec SBModule::GetSymbolFileSpec() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBFileSpec, SBModule,
+ GetSymbolFileSpec);
+
+ lldb::SBFileSpec sb_file_spec;
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ SymbolVendor *symbol_vendor_ptr = module_sp->GetSymbolVendor();
+ if (symbol_vendor_ptr)
+ sb_file_spec.SetFileSpec(symbol_vendor_ptr->GetMainFileSpec());
+ }
+ return LLDB_RECORD_RESULT(sb_file_spec);
+}
+
+lldb::SBAddress SBModule::GetObjectFileHeaderAddress() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBAddress, SBModule,
+ GetObjectFileHeaderAddress);
+
+ lldb::SBAddress sb_addr;
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ ObjectFile *objfile_ptr = module_sp->GetObjectFile();
+ if (objfile_ptr)
+ sb_addr.ref() = objfile_ptr->GetBaseAddress();
+ }
+ return LLDB_RECORD_RESULT(sb_addr);
+}
+
+lldb::SBAddress SBModule::GetObjectFileEntryPointAddress() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBAddress, SBModule,
+ GetObjectFileEntryPointAddress);
+
+ lldb::SBAddress sb_addr;
+ ModuleSP module_sp(GetSP());
+ if (module_sp) {
+ ObjectFile *objfile_ptr = module_sp->GetObjectFile();
+ if (objfile_ptr)
+ sb_addr.ref() = objfile_ptr->GetEntryPointAddress();
+ }
+ return LLDB_RECORD_RESULT(sb_addr);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBModule>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBModule, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBModule, (const lldb::SBModuleSpec &));
+ LLDB_REGISTER_CONSTRUCTOR(SBModule, (const lldb::SBModule &));
+ LLDB_REGISTER_CONSTRUCTOR(SBModule, (lldb::SBProcess &, lldb::addr_t));
+ LLDB_REGISTER_METHOD(const lldb::SBModule &,
+ SBModule, operator=,(const lldb::SBModule &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBModule, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBModule, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBModule, Clear, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBFileSpec, SBModule, GetFileSpec, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBFileSpec, SBModule, GetPlatformFileSpec,
+ ());
+ LLDB_REGISTER_METHOD(bool, SBModule, SetPlatformFileSpec,
+ (const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(lldb::SBFileSpec, SBModule, GetRemoteInstallFileSpec,
+ ());
+ LLDB_REGISTER_METHOD(bool, SBModule, SetRemoteInstallFileSpec,
+ (lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBModule, GetUUIDString, ());
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBModule, operator==,(const lldb::SBModule &));
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBModule, operator!=,(const lldb::SBModule &));
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBModule, ResolveFileAddress,
+ (lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::SBSymbolContext, SBModule,
+ ResolveSymbolContextForAddress,
+ (const lldb::SBAddress &, uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBModule, GetDescription, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(uint32_t, SBModule, GetNumCompileUnits, ());
+ LLDB_REGISTER_METHOD(lldb::SBCompileUnit, SBModule, GetCompileUnitAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBSymbolContextList, SBModule, FindCompileUnits,
+ (const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(size_t, SBModule, GetNumSymbols, ());
+ LLDB_REGISTER_METHOD(lldb::SBSymbol, SBModule, GetSymbolAtIndex, (size_t));
+ LLDB_REGISTER_METHOD(lldb::SBSymbol, SBModule, FindSymbol,
+ (const char *, lldb::SymbolType));
+ LLDB_REGISTER_METHOD(lldb::SBSymbolContextList, SBModule, FindSymbols,
+ (const char *, lldb::SymbolType));
+ LLDB_REGISTER_METHOD(size_t, SBModule, GetNumSections, ());
+ LLDB_REGISTER_METHOD(lldb::SBSection, SBModule, GetSectionAtIndex,
+ (size_t));
+ LLDB_REGISTER_METHOD(lldb::SBSymbolContextList, SBModule, FindFunctions,
+ (const char *, uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBValueList, SBModule, FindGlobalVariables,
+ (lldb::SBTarget &, const char *, uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBModule, FindFirstGlobalVariable,
+ (lldb::SBTarget &, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBType, SBModule, FindFirstType, (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBType, SBModule, GetBasicType,
+ (lldb::BasicType));
+ LLDB_REGISTER_METHOD(lldb::SBTypeList, SBModule, FindTypes, (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBType, SBModule, GetTypeByID,
+ (lldb::user_id_t));
+ LLDB_REGISTER_METHOD(lldb::SBTypeList, SBModule, GetTypes, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBSection, SBModule, FindSection,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::ByteOrder, SBModule, GetByteOrder, ());
+ LLDB_REGISTER_METHOD(const char *, SBModule, GetTriple, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBModule, GetAddressByteSize, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBModule, GetVersion,
+ (uint32_t *, uint32_t));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBFileSpec, SBModule, GetSymbolFileSpec,
+ ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBAddress, SBModule,
+ GetObjectFileHeaderAddress, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBAddress, SBModule,
+ GetObjectFileEntryPointAddress, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBModuleSpec.cpp b/contrib/llvm-project/lldb/source/API/SBModuleSpec.cpp
new file mode 100644
index 000000000000..a5e9ad26fac1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBModuleSpec.cpp
@@ -0,0 +1,300 @@
+//===-- SBModuleSpec.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBModuleSpec.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBModuleSpec::SBModuleSpec() : m_opaque_up(new lldb_private::ModuleSpec()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBModuleSpec);
+}
+
+SBModuleSpec::SBModuleSpec(const SBModuleSpec &rhs) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBModuleSpec, (const lldb::SBModuleSpec &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+const SBModuleSpec &SBModuleSpec::operator=(const SBModuleSpec &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBModuleSpec &,
+ SBModuleSpec, operator=,(const lldb::SBModuleSpec &), rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBModuleSpec::~SBModuleSpec() {}
+
+bool SBModuleSpec::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBModuleSpec, IsValid);
+ return this->operator bool();
+}
+SBModuleSpec::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBModuleSpec, operator bool);
+
+ return m_opaque_up->operator bool();
+}
+
+void SBModuleSpec::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBModuleSpec, Clear);
+
+ m_opaque_up->Clear();
+}
+
+SBFileSpec SBModuleSpec::GetFileSpec() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFileSpec, SBModuleSpec, GetFileSpec);
+
+ SBFileSpec sb_spec(m_opaque_up->GetFileSpec());
+ return LLDB_RECORD_RESULT(sb_spec);
+}
+
+void SBModuleSpec::SetFileSpec(const lldb::SBFileSpec &sb_spec) {
+ LLDB_RECORD_METHOD(void, SBModuleSpec, SetFileSpec,
+ (const lldb::SBFileSpec &), sb_spec);
+
+ m_opaque_up->GetFileSpec() = *sb_spec;
+}
+
+lldb::SBFileSpec SBModuleSpec::GetPlatformFileSpec() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFileSpec, SBModuleSpec,
+ GetPlatformFileSpec);
+
+ return LLDB_RECORD_RESULT(SBFileSpec(m_opaque_up->GetPlatformFileSpec()));
+}
+
+void SBModuleSpec::SetPlatformFileSpec(const lldb::SBFileSpec &sb_spec) {
+ LLDB_RECORD_METHOD(void, SBModuleSpec, SetPlatformFileSpec,
+ (const lldb::SBFileSpec &), sb_spec);
+
+ m_opaque_up->GetPlatformFileSpec() = *sb_spec;
+}
+
+lldb::SBFileSpec SBModuleSpec::GetSymbolFileSpec() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFileSpec, SBModuleSpec, GetSymbolFileSpec);
+
+ return LLDB_RECORD_RESULT(SBFileSpec(m_opaque_up->GetSymbolFileSpec()));
+}
+
+void SBModuleSpec::SetSymbolFileSpec(const lldb::SBFileSpec &sb_spec) {
+ LLDB_RECORD_METHOD(void, SBModuleSpec, SetSymbolFileSpec,
+ (const lldb::SBFileSpec &), sb_spec);
+
+ m_opaque_up->GetSymbolFileSpec() = *sb_spec;
+}
+
+const char *SBModuleSpec::GetObjectName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBModuleSpec, GetObjectName);
+
+ return m_opaque_up->GetObjectName().GetCString();
+}
+
+void SBModuleSpec::SetObjectName(const char *name) {
+ LLDB_RECORD_METHOD(void, SBModuleSpec, SetObjectName, (const char *), name);
+
+ m_opaque_up->GetObjectName().SetCString(name);
+}
+
+const char *SBModuleSpec::GetTriple() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBModuleSpec, GetTriple);
+
+ std::string triple(m_opaque_up->GetArchitecture().GetTriple().str());
+ // Unique the string so we don't run into ownership issues since the const
+ // strings put the string into the string pool once and the strings never
+ // comes out
+ ConstString const_triple(triple.c_str());
+ return const_triple.GetCString();
+}
+
+void SBModuleSpec::SetTriple(const char *triple) {
+ LLDB_RECORD_METHOD(void, SBModuleSpec, SetTriple, (const char *), triple);
+
+ m_opaque_up->GetArchitecture().SetTriple(triple);
+}
+
+const uint8_t *SBModuleSpec::GetUUIDBytes() {
+ return m_opaque_up->GetUUID().GetBytes().data();
+}
+
+size_t SBModuleSpec::GetUUIDLength() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBModuleSpec, GetUUIDLength);
+
+ return m_opaque_up->GetUUID().GetBytes().size();
+}
+
+bool SBModuleSpec::SetUUIDBytes(const uint8_t *uuid, size_t uuid_len) {
+ m_opaque_up->GetUUID() = UUID::fromOptionalData(uuid, uuid_len);
+ return m_opaque_up->GetUUID().IsValid();
+}
+
+bool SBModuleSpec::GetDescription(lldb::SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBModuleSpec, GetDescription, (lldb::SBStream &),
+ description);
+
+ m_opaque_up->Dump(description.ref());
+ return true;
+}
+
+SBModuleSpecList::SBModuleSpecList() : m_opaque_up(new ModuleSpecList()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBModuleSpecList);
+}
+
+SBModuleSpecList::SBModuleSpecList(const SBModuleSpecList &rhs)
+ : m_opaque_up(new ModuleSpecList(*rhs.m_opaque_up)) {
+ LLDB_RECORD_CONSTRUCTOR(SBModuleSpecList, (const lldb::SBModuleSpecList &),
+ rhs);
+}
+
+SBModuleSpecList &SBModuleSpecList::operator=(const SBModuleSpecList &rhs) {
+ LLDB_RECORD_METHOD(
+ lldb::SBModuleSpecList &,
+ SBModuleSpecList, operator=,(const lldb::SBModuleSpecList &), rhs);
+
+ if (this != &rhs)
+ *m_opaque_up = *rhs.m_opaque_up;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBModuleSpecList::~SBModuleSpecList() {}
+
+SBModuleSpecList SBModuleSpecList::GetModuleSpecifications(const char *path) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBModuleSpecList, SBModuleSpecList,
+ GetModuleSpecifications, (const char *), path);
+
+ SBModuleSpecList specs;
+ FileSpec file_spec(path);
+ FileSystem::Instance().Resolve(file_spec);
+ Host::ResolveExecutableInBundle(file_spec);
+ ObjectFile::GetModuleSpecifications(file_spec, 0, 0, *specs.m_opaque_up);
+ return LLDB_RECORD_RESULT(specs);
+}
+
+void SBModuleSpecList::Append(const SBModuleSpec &spec) {
+ LLDB_RECORD_METHOD(void, SBModuleSpecList, Append,
+ (const lldb::SBModuleSpec &), spec);
+
+ m_opaque_up->Append(*spec.m_opaque_up);
+}
+
+void SBModuleSpecList::Append(const SBModuleSpecList &spec_list) {
+ LLDB_RECORD_METHOD(void, SBModuleSpecList, Append,
+ (const lldb::SBModuleSpecList &), spec_list);
+
+ m_opaque_up->Append(*spec_list.m_opaque_up);
+}
+
+size_t SBModuleSpecList::GetSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBModuleSpecList, GetSize);
+
+ return m_opaque_up->GetSize();
+}
+
+SBModuleSpec SBModuleSpecList::GetSpecAtIndex(size_t i) {
+ LLDB_RECORD_METHOD(lldb::SBModuleSpec, SBModuleSpecList, GetSpecAtIndex,
+ (size_t), i);
+
+ SBModuleSpec sb_module_spec;
+ m_opaque_up->GetModuleSpecAtIndex(i, *sb_module_spec.m_opaque_up);
+ return LLDB_RECORD_RESULT(sb_module_spec);
+}
+
+SBModuleSpec
+SBModuleSpecList::FindFirstMatchingSpec(const SBModuleSpec &match_spec) {
+ LLDB_RECORD_METHOD(lldb::SBModuleSpec, SBModuleSpecList,
+ FindFirstMatchingSpec, (const lldb::SBModuleSpec &),
+ match_spec);
+
+ SBModuleSpec sb_module_spec;
+ m_opaque_up->FindMatchingModuleSpec(*match_spec.m_opaque_up,
+ *sb_module_spec.m_opaque_up);
+ return LLDB_RECORD_RESULT(sb_module_spec);
+}
+
+SBModuleSpecList
+SBModuleSpecList::FindMatchingSpecs(const SBModuleSpec &match_spec) {
+ LLDB_RECORD_METHOD(lldb::SBModuleSpecList, SBModuleSpecList,
+ FindMatchingSpecs, (const lldb::SBModuleSpec &),
+ match_spec);
+
+ SBModuleSpecList specs;
+ m_opaque_up->FindMatchingModuleSpecs(*match_spec.m_opaque_up,
+ *specs.m_opaque_up);
+ return LLDB_RECORD_RESULT(specs);
+}
+
+bool SBModuleSpecList::GetDescription(lldb::SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBModuleSpecList, GetDescription, (lldb::SBStream &),
+ description);
+
+ m_opaque_up->Dump(description.ref());
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBModuleSpec>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBModuleSpec, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBModuleSpec, (const lldb::SBModuleSpec &));
+ LLDB_REGISTER_METHOD(const lldb::SBModuleSpec &,
+ SBModuleSpec, operator=,(const lldb::SBModuleSpec &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBModuleSpec, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBModuleSpec, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBModuleSpec, Clear, ());
+ LLDB_REGISTER_METHOD(lldb::SBFileSpec, SBModuleSpec, GetFileSpec, ());
+ LLDB_REGISTER_METHOD(void, SBModuleSpec, SetFileSpec,
+ (const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(lldb::SBFileSpec, SBModuleSpec, GetPlatformFileSpec,
+ ());
+ LLDB_REGISTER_METHOD(void, SBModuleSpec, SetPlatformFileSpec,
+ (const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(lldb::SBFileSpec, SBModuleSpec, GetSymbolFileSpec, ());
+ LLDB_REGISTER_METHOD(void, SBModuleSpec, SetSymbolFileSpec,
+ (const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(const char *, SBModuleSpec, GetObjectName, ());
+ LLDB_REGISTER_METHOD(void, SBModuleSpec, SetObjectName, (const char *));
+ LLDB_REGISTER_METHOD(const char *, SBModuleSpec, GetTriple, ());
+ LLDB_REGISTER_METHOD(void, SBModuleSpec, SetTriple, (const char *));
+ LLDB_REGISTER_METHOD(size_t, SBModuleSpec, GetUUIDLength, ());
+ LLDB_REGISTER_METHOD(bool, SBModuleSpec, GetDescription,
+ (lldb::SBStream &));
+ LLDB_REGISTER_CONSTRUCTOR(SBModuleSpecList, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBModuleSpecList,
+ (const lldb::SBModuleSpecList &));
+ LLDB_REGISTER_METHOD(
+ lldb::SBModuleSpecList &,
+ SBModuleSpecList, operator=,(const lldb::SBModuleSpecList &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBModuleSpecList, SBModuleSpecList,
+ GetModuleSpecifications, (const char *));
+ LLDB_REGISTER_METHOD(void, SBModuleSpecList, Append,
+ (const lldb::SBModuleSpec &));
+ LLDB_REGISTER_METHOD(void, SBModuleSpecList, Append,
+ (const lldb::SBModuleSpecList &));
+ LLDB_REGISTER_METHOD(size_t, SBModuleSpecList, GetSize, ());
+ LLDB_REGISTER_METHOD(lldb::SBModuleSpec, SBModuleSpecList, GetSpecAtIndex,
+ (size_t));
+ LLDB_REGISTER_METHOD(lldb::SBModuleSpec, SBModuleSpecList,
+ FindFirstMatchingSpec, (const lldb::SBModuleSpec &));
+ LLDB_REGISTER_METHOD(lldb::SBModuleSpecList, SBModuleSpecList,
+ FindMatchingSpecs, (const lldb::SBModuleSpec &));
+ LLDB_REGISTER_METHOD(bool, SBModuleSpecList, GetDescription,
+ (lldb::SBStream &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBPlatform.cpp b/contrib/llvm-project/lldb/source/API/SBPlatform.cpp
new file mode 100644
index 000000000000..f3708d8e084f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBPlatform.cpp
@@ -0,0 +1,708 @@
+//===-- SBPlatform.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBPlatform.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBLaunchInfo.h"
+#include "lldb/API/SBUnixSignals.h"
+#include "lldb/Host/File.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/Support/FileSystem.h"
+
+#include <functional>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// PlatformConnectOptions
+struct PlatformConnectOptions {
+ PlatformConnectOptions(const char *url = nullptr)
+ : m_url(), m_rsync_options(), m_rsync_remote_path_prefix(),
+ m_rsync_enabled(false), m_rsync_omit_hostname_from_remote_path(false),
+ m_local_cache_directory() {
+ if (url && url[0])
+ m_url = url;
+ }
+
+ ~PlatformConnectOptions() {}
+
+ std::string m_url;
+ std::string m_rsync_options;
+ std::string m_rsync_remote_path_prefix;
+ bool m_rsync_enabled;
+ bool m_rsync_omit_hostname_from_remote_path;
+ ConstString m_local_cache_directory;
+};
+
+// PlatformShellCommand
+struct PlatformShellCommand {
+ PlatformShellCommand(const char *shell_command = nullptr)
+ : m_command(), m_working_dir(), m_status(0), m_signo(0) {
+ if (shell_command && shell_command[0])
+ m_command = shell_command;
+ }
+
+ ~PlatformShellCommand() {}
+
+ std::string m_command;
+ std::string m_working_dir;
+ std::string m_output;
+ int m_status;
+ int m_signo;
+ Timeout<std::ratio<1>> m_timeout = llvm::None;
+};
+// SBPlatformConnectOptions
+SBPlatformConnectOptions::SBPlatformConnectOptions(const char *url)
+ : m_opaque_ptr(new PlatformConnectOptions(url)) {
+ LLDB_RECORD_CONSTRUCTOR(SBPlatformConnectOptions, (const char *), url);
+}
+
+SBPlatformConnectOptions::SBPlatformConnectOptions(
+ const SBPlatformConnectOptions &rhs)
+ : m_opaque_ptr(new PlatformConnectOptions()) {
+ LLDB_RECORD_CONSTRUCTOR(SBPlatformConnectOptions,
+ (const lldb::SBPlatformConnectOptions &), rhs);
+
+ *m_opaque_ptr = *rhs.m_opaque_ptr;
+}
+
+SBPlatformConnectOptions::~SBPlatformConnectOptions() { delete m_opaque_ptr; }
+
+void SBPlatformConnectOptions::operator=(const SBPlatformConnectOptions &rhs) {
+ LLDB_RECORD_METHOD(
+ void,
+ SBPlatformConnectOptions, operator=,(
+ const lldb::SBPlatformConnectOptions &),
+ rhs);
+
+ *m_opaque_ptr = *rhs.m_opaque_ptr;
+}
+
+const char *SBPlatformConnectOptions::GetURL() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatformConnectOptions, GetURL);
+
+ if (m_opaque_ptr->m_url.empty())
+ return nullptr;
+ return m_opaque_ptr->m_url.c_str();
+}
+
+void SBPlatformConnectOptions::SetURL(const char *url) {
+ LLDB_RECORD_METHOD(void, SBPlatformConnectOptions, SetURL, (const char *),
+ url);
+
+ if (url && url[0])
+ m_opaque_ptr->m_url = url;
+ else
+ m_opaque_ptr->m_url.clear();
+}
+
+bool SBPlatformConnectOptions::GetRsyncEnabled() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBPlatformConnectOptions, GetRsyncEnabled);
+
+ return m_opaque_ptr->m_rsync_enabled;
+}
+
+void SBPlatformConnectOptions::EnableRsync(
+ const char *options, const char *remote_path_prefix,
+ bool omit_hostname_from_remote_path) {
+ LLDB_RECORD_METHOD(void, SBPlatformConnectOptions, EnableRsync,
+ (const char *, const char *, bool), options,
+ remote_path_prefix, omit_hostname_from_remote_path);
+
+ m_opaque_ptr->m_rsync_enabled = true;
+ m_opaque_ptr->m_rsync_omit_hostname_from_remote_path =
+ omit_hostname_from_remote_path;
+ if (remote_path_prefix && remote_path_prefix[0])
+ m_opaque_ptr->m_rsync_remote_path_prefix = remote_path_prefix;
+ else
+ m_opaque_ptr->m_rsync_remote_path_prefix.clear();
+
+ if (options && options[0])
+ m_opaque_ptr->m_rsync_options = options;
+ else
+ m_opaque_ptr->m_rsync_options.clear();
+}
+
+void SBPlatformConnectOptions::DisableRsync() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBPlatformConnectOptions, DisableRsync);
+
+ m_opaque_ptr->m_rsync_enabled = false;
+}
+
+const char *SBPlatformConnectOptions::GetLocalCacheDirectory() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatformConnectOptions,
+ GetLocalCacheDirectory);
+
+ return m_opaque_ptr->m_local_cache_directory.GetCString();
+}
+
+void SBPlatformConnectOptions::SetLocalCacheDirectory(const char *path) {
+ LLDB_RECORD_METHOD(void, SBPlatformConnectOptions, SetLocalCacheDirectory,
+ (const char *), path);
+
+ if (path && path[0])
+ m_opaque_ptr->m_local_cache_directory.SetCString(path);
+ else
+ m_opaque_ptr->m_local_cache_directory = ConstString();
+}
+
+// SBPlatformShellCommand
+SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_command)
+ : m_opaque_ptr(new PlatformShellCommand(shell_command)) {
+ LLDB_RECORD_CONSTRUCTOR(SBPlatformShellCommand, (const char *),
+ shell_command);
+}
+
+SBPlatformShellCommand::SBPlatformShellCommand(
+ const SBPlatformShellCommand &rhs)
+ : m_opaque_ptr(new PlatformShellCommand()) {
+ LLDB_RECORD_CONSTRUCTOR(SBPlatformShellCommand,
+ (const lldb::SBPlatformShellCommand &), rhs);
+
+ *m_opaque_ptr = *rhs.m_opaque_ptr;
+}
+
+SBPlatformShellCommand::~SBPlatformShellCommand() { delete m_opaque_ptr; }
+
+void SBPlatformShellCommand::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBPlatformShellCommand, Clear);
+
+ m_opaque_ptr->m_output = std::string();
+ m_opaque_ptr->m_status = 0;
+ m_opaque_ptr->m_signo = 0;
+}
+
+const char *SBPlatformShellCommand::GetCommand() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatformShellCommand, GetCommand);
+
+ if (m_opaque_ptr->m_command.empty())
+ return nullptr;
+ return m_opaque_ptr->m_command.c_str();
+}
+
+void SBPlatformShellCommand::SetCommand(const char *shell_command) {
+ LLDB_RECORD_METHOD(void, SBPlatformShellCommand, SetCommand, (const char *),
+ shell_command);
+
+ if (shell_command && shell_command[0])
+ m_opaque_ptr->m_command = shell_command;
+ else
+ m_opaque_ptr->m_command.clear();
+}
+
+const char *SBPlatformShellCommand::GetWorkingDirectory() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatformShellCommand,
+ GetWorkingDirectory);
+
+ if (m_opaque_ptr->m_working_dir.empty())
+ return nullptr;
+ return m_opaque_ptr->m_working_dir.c_str();
+}
+
+void SBPlatformShellCommand::SetWorkingDirectory(const char *path) {
+ LLDB_RECORD_METHOD(void, SBPlatformShellCommand, SetWorkingDirectory,
+ (const char *), path);
+
+ if (path && path[0])
+ m_opaque_ptr->m_working_dir = path;
+ else
+ m_opaque_ptr->m_working_dir.clear();
+}
+
+uint32_t SBPlatformShellCommand::GetTimeoutSeconds() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBPlatformShellCommand,
+ GetTimeoutSeconds);
+
+ if (m_opaque_ptr->m_timeout)
+ return m_opaque_ptr->m_timeout->count();
+ return UINT32_MAX;
+}
+
+void SBPlatformShellCommand::SetTimeoutSeconds(uint32_t sec) {
+ LLDB_RECORD_METHOD(void, SBPlatformShellCommand, SetTimeoutSeconds,
+ (uint32_t), sec);
+
+ if (sec == UINT32_MAX)
+ m_opaque_ptr->m_timeout = llvm::None;
+ else
+ m_opaque_ptr->m_timeout = std::chrono::seconds(sec);
+}
+
+int SBPlatformShellCommand::GetSignal() {
+ LLDB_RECORD_METHOD_NO_ARGS(int, SBPlatformShellCommand, GetSignal);
+
+ return m_opaque_ptr->m_signo;
+}
+
+int SBPlatformShellCommand::GetStatus() {
+ LLDB_RECORD_METHOD_NO_ARGS(int, SBPlatformShellCommand, GetStatus);
+
+ return m_opaque_ptr->m_status;
+}
+
+const char *SBPlatformShellCommand::GetOutput() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatformShellCommand, GetOutput);
+
+ if (m_opaque_ptr->m_output.empty())
+ return nullptr;
+ return m_opaque_ptr->m_output.c_str();
+}
+
+// SBPlatform
+SBPlatform::SBPlatform() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBPlatform);
+}
+
+SBPlatform::SBPlatform(const char *platform_name) : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR(SBPlatform, (const char *), platform_name);
+
+ Status error;
+ if (platform_name && platform_name[0])
+ m_opaque_sp = Platform::Create(ConstString(platform_name), error);
+}
+
+SBPlatform::~SBPlatform() {}
+
+bool SBPlatform::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBPlatform, IsValid);
+ return this->operator bool();
+}
+SBPlatform::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBPlatform, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+void SBPlatform::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBPlatform, Clear);
+
+ m_opaque_sp.reset();
+}
+
+const char *SBPlatform::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatform, GetName);
+
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp)
+ return platform_sp->GetName().GetCString();
+ return nullptr;
+}
+
+lldb::PlatformSP SBPlatform::GetSP() const { return m_opaque_sp; }
+
+void SBPlatform::SetSP(const lldb::PlatformSP &platform_sp) {
+ m_opaque_sp = platform_sp;
+}
+
+const char *SBPlatform::GetWorkingDirectory() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatform, GetWorkingDirectory);
+
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp)
+ return platform_sp->GetWorkingDirectory().GetCString();
+ return nullptr;
+}
+
+bool SBPlatform::SetWorkingDirectory(const char *path) {
+ LLDB_RECORD_METHOD(bool, SBPlatform, SetWorkingDirectory, (const char *),
+ path);
+
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp) {
+ if (path)
+ platform_sp->SetWorkingDirectory(FileSpec(path));
+ else
+ platform_sp->SetWorkingDirectory(FileSpec());
+ return true;
+ }
+ return false;
+}
+
+SBError SBPlatform::ConnectRemote(SBPlatformConnectOptions &connect_options) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBPlatform, ConnectRemote,
+ (lldb::SBPlatformConnectOptions &), connect_options);
+
+ SBError sb_error;
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp && connect_options.GetURL()) {
+ Args args;
+ args.AppendArgument(
+ llvm::StringRef::withNullAsEmpty(connect_options.GetURL()));
+ sb_error.ref() = platform_sp->ConnectRemote(args);
+ } else {
+ sb_error.SetErrorString("invalid platform");
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+void SBPlatform::DisconnectRemote() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBPlatform, DisconnectRemote);
+
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp)
+ platform_sp->DisconnectRemote();
+}
+
+bool SBPlatform::IsConnected() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBPlatform, IsConnected);
+
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp)
+ return platform_sp->IsConnected();
+ return false;
+}
+
+const char *SBPlatform::GetTriple() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatform, GetTriple);
+
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp) {
+ ArchSpec arch(platform_sp->GetSystemArchitecture());
+ if (arch.IsValid()) {
+ // Const-ify the string so we don't need to worry about the lifetime of
+ // the string
+ return ConstString(arch.GetTriple().getTriple().c_str()).GetCString();
+ }
+ }
+ return nullptr;
+}
+
+const char *SBPlatform::GetOSBuild() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatform, GetOSBuild);
+
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp) {
+ std::string s;
+ if (platform_sp->GetOSBuildString(s)) {
+ if (!s.empty()) {
+ // Const-ify the string so we don't need to worry about the lifetime of
+ // the string
+ return ConstString(s.c_str()).GetCString();
+ }
+ }
+ }
+ return nullptr;
+}
+
+const char *SBPlatform::GetOSDescription() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatform, GetOSDescription);
+
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp) {
+ std::string s;
+ if (platform_sp->GetOSKernelDescription(s)) {
+ if (!s.empty()) {
+ // Const-ify the string so we don't need to worry about the lifetime of
+ // the string
+ return ConstString(s.c_str()).GetCString();
+ }
+ }
+ }
+ return nullptr;
+}
+
+const char *SBPlatform::GetHostname() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatform, GetHostname);
+
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp)
+ return platform_sp->GetHostname();
+ return nullptr;
+}
+
+uint32_t SBPlatform::GetOSMajorVersion() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBPlatform, GetOSMajorVersion);
+
+ llvm::VersionTuple version;
+ if (PlatformSP platform_sp = GetSP())
+ version = platform_sp->GetOSVersion();
+ return version.empty() ? UINT32_MAX : version.getMajor();
+}
+
+uint32_t SBPlatform::GetOSMinorVersion() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBPlatform, GetOSMinorVersion);
+
+ llvm::VersionTuple version;
+ if (PlatformSP platform_sp = GetSP())
+ version = platform_sp->GetOSVersion();
+ return version.getMinor().getValueOr(UINT32_MAX);
+}
+
+uint32_t SBPlatform::GetOSUpdateVersion() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBPlatform, GetOSUpdateVersion);
+
+ llvm::VersionTuple version;
+ if (PlatformSP platform_sp = GetSP())
+ version = platform_sp->GetOSVersion();
+ return version.getSubminor().getValueOr(UINT32_MAX);
+}
+
+SBError SBPlatform::Get(SBFileSpec &src, SBFileSpec &dst) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBPlatform, Get,
+ (lldb::SBFileSpec &, lldb::SBFileSpec &), src, dst);
+
+ SBError sb_error;
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp) {
+ sb_error.ref() = platform_sp->GetFile(src.ref(), dst.ref());
+ } else {
+ sb_error.SetErrorString("invalid platform");
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBPlatform::Put(SBFileSpec &src, SBFileSpec &dst) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBPlatform, Put,
+ (lldb::SBFileSpec &, lldb::SBFileSpec &), src, dst);
+ return LLDB_RECORD_RESULT(
+ ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
+ if (src.Exists()) {
+ uint32_t permissions =
+ FileSystem::Instance().GetPermissions(src.ref());
+ if (permissions == 0) {
+ if (FileSystem::Instance().IsDirectory(src.ref()))
+ permissions = eFilePermissionsDirectoryDefault;
+ else
+ permissions = eFilePermissionsFileDefault;
+ }
+
+ return platform_sp->PutFile(src.ref(), dst.ref(), permissions);
+ }
+
+ Status error;
+ error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
+ src.ref().GetPath().c_str());
+ return error;
+ }));
+}
+
+SBError SBPlatform::Install(SBFileSpec &src, SBFileSpec &dst) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBPlatform, Install,
+ (lldb::SBFileSpec &, lldb::SBFileSpec &), src, dst);
+ return LLDB_RECORD_RESULT(
+ ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
+ if (src.Exists())
+ return platform_sp->Install(src.ref(), dst.ref());
+
+ Status error;
+ error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
+ src.ref().GetPath().c_str());
+ return error;
+ }));
+}
+
+SBError SBPlatform::Run(SBPlatformShellCommand &shell_command) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBPlatform, Run,
+ (lldb::SBPlatformShellCommand &), shell_command);
+ return LLDB_RECORD_RESULT(ExecuteConnected([&](const lldb::PlatformSP
+ &platform_sp) {
+ const char *command = shell_command.GetCommand();
+ if (!command)
+ return Status("invalid shell command (empty)");
+
+ const char *working_dir = shell_command.GetWorkingDirectory();
+ if (working_dir == nullptr) {
+ working_dir = platform_sp->GetWorkingDirectory().GetCString();
+ if (working_dir)
+ shell_command.SetWorkingDirectory(working_dir);
+ }
+ return platform_sp->RunShellCommand(command, FileSpec(working_dir),
+ &shell_command.m_opaque_ptr->m_status,
+ &shell_command.m_opaque_ptr->m_signo,
+ &shell_command.m_opaque_ptr->m_output,
+ shell_command.m_opaque_ptr->m_timeout);
+ }));
+}
+
+SBError SBPlatform::Launch(SBLaunchInfo &launch_info) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBPlatform, Launch, (lldb::SBLaunchInfo &),
+ launch_info);
+ return LLDB_RECORD_RESULT(
+ ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
+ ProcessLaunchInfo info = launch_info.ref();
+ Status error = platform_sp->LaunchProcess(info);
+ launch_info.set_ref(info);
+ return error;
+ }));
+}
+
+SBError SBPlatform::Kill(const lldb::pid_t pid) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBPlatform, Kill, (const lldb::pid_t), pid);
+ return LLDB_RECORD_RESULT(
+ ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
+ return platform_sp->KillProcess(pid);
+ }));
+}
+
+SBError SBPlatform::ExecuteConnected(
+ const std::function<Status(const lldb::PlatformSP &)> &func) {
+ SBError sb_error;
+ const auto platform_sp(GetSP());
+ if (platform_sp) {
+ if (platform_sp->IsConnected())
+ sb_error.ref() = func(platform_sp);
+ else
+ sb_error.SetErrorString("not connected");
+ } else
+ sb_error.SetErrorString("invalid platform");
+
+ return sb_error;
+}
+
+SBError SBPlatform::MakeDirectory(const char *path, uint32_t file_permissions) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBPlatform, MakeDirectory,
+ (const char *, uint32_t), path, file_permissions);
+
+ SBError sb_error;
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp) {
+ sb_error.ref() =
+ platform_sp->MakeDirectory(FileSpec(path), file_permissions);
+ } else {
+ sb_error.SetErrorString("invalid platform");
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+uint32_t SBPlatform::GetFilePermissions(const char *path) {
+ LLDB_RECORD_METHOD(uint32_t, SBPlatform, GetFilePermissions, (const char *),
+ path);
+
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp) {
+ uint32_t file_permissions = 0;
+ platform_sp->GetFilePermissions(FileSpec(path), file_permissions);
+ return file_permissions;
+ }
+ return 0;
+}
+
+SBError SBPlatform::SetFilePermissions(const char *path,
+ uint32_t file_permissions) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBPlatform, SetFilePermissions,
+ (const char *, uint32_t), path, file_permissions);
+
+ SBError sb_error;
+ PlatformSP platform_sp(GetSP());
+ if (platform_sp) {
+ sb_error.ref() =
+ platform_sp->SetFilePermissions(FileSpec(path), file_permissions);
+ } else {
+ sb_error.SetErrorString("invalid platform");
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBUnixSignals SBPlatform::GetUnixSignals() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBUnixSignals, SBPlatform,
+ GetUnixSignals);
+
+ if (auto platform_sp = GetSP())
+ return LLDB_RECORD_RESULT(SBUnixSignals{platform_sp});
+
+ return LLDB_RECORD_RESULT(SBUnixSignals());
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBPlatformConnectOptions>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBPlatformConnectOptions, (const char *));
+ LLDB_REGISTER_CONSTRUCTOR(SBPlatformConnectOptions,
+ (const lldb::SBPlatformConnectOptions &));
+ LLDB_REGISTER_METHOD(
+ void,
+ SBPlatformConnectOptions, operator=,(
+ const lldb::SBPlatformConnectOptions &));
+ LLDB_REGISTER_METHOD(const char *, SBPlatformConnectOptions, GetURL, ());
+ LLDB_REGISTER_METHOD(void, SBPlatformConnectOptions, SetURL,
+ (const char *));
+ LLDB_REGISTER_METHOD(bool, SBPlatformConnectOptions, GetRsyncEnabled, ());
+ LLDB_REGISTER_METHOD(void, SBPlatformConnectOptions, EnableRsync,
+ (const char *, const char *, bool));
+ LLDB_REGISTER_METHOD(void, SBPlatformConnectOptions, DisableRsync, ());
+ LLDB_REGISTER_METHOD(const char *, SBPlatformConnectOptions,
+ GetLocalCacheDirectory, ());
+ LLDB_REGISTER_METHOD(void, SBPlatformConnectOptions, SetLocalCacheDirectory,
+ (const char *));
+}
+
+template <>
+void RegisterMethods<SBPlatformShellCommand>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBPlatformShellCommand, (const char *));
+ LLDB_REGISTER_CONSTRUCTOR(SBPlatformShellCommand,
+ (const lldb::SBPlatformShellCommand &));
+ LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, Clear, ());
+ LLDB_REGISTER_METHOD(const char *, SBPlatformShellCommand, GetCommand, ());
+ LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, SetCommand,
+ (const char *));
+ LLDB_REGISTER_METHOD(const char *, SBPlatformShellCommand,
+ GetWorkingDirectory, ());
+ LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, SetWorkingDirectory,
+ (const char *));
+ LLDB_REGISTER_METHOD(uint32_t, SBPlatformShellCommand, GetTimeoutSeconds,
+ ());
+ LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, SetTimeoutSeconds,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(int, SBPlatformShellCommand, GetSignal, ());
+ LLDB_REGISTER_METHOD(int, SBPlatformShellCommand, GetStatus, ());
+ LLDB_REGISTER_METHOD(const char *, SBPlatformShellCommand, GetOutput, ());
+}
+
+template <>
+void RegisterMethods<SBPlatform>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBPlatform, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBPlatform, (const char *));
+ LLDB_REGISTER_METHOD_CONST(bool, SBPlatform, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBPlatform, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBPlatform, Clear, ());
+ LLDB_REGISTER_METHOD(const char *, SBPlatform, GetName, ());
+ LLDB_REGISTER_METHOD(const char *, SBPlatform, GetWorkingDirectory, ());
+ LLDB_REGISTER_METHOD(bool, SBPlatform, SetWorkingDirectory, (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBPlatform, ConnectRemote,
+ (lldb::SBPlatformConnectOptions &));
+ LLDB_REGISTER_METHOD(void, SBPlatform, DisconnectRemote, ());
+ LLDB_REGISTER_METHOD(bool, SBPlatform, IsConnected, ());
+ LLDB_REGISTER_METHOD(const char *, SBPlatform, GetTriple, ());
+ LLDB_REGISTER_METHOD(const char *, SBPlatform, GetOSBuild, ());
+ LLDB_REGISTER_METHOD(const char *, SBPlatform, GetOSDescription, ());
+ LLDB_REGISTER_METHOD(const char *, SBPlatform, GetHostname, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBPlatform, GetOSMajorVersion, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBPlatform, GetOSMinorVersion, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBPlatform, GetOSUpdateVersion, ());
+ LLDB_REGISTER_METHOD(lldb::SBError, SBPlatform, Get,
+ (lldb::SBFileSpec &, lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBPlatform, Put,
+ (lldb::SBFileSpec &, lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBPlatform, Install,
+ (lldb::SBFileSpec &, lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBPlatform, Run,
+ (lldb::SBPlatformShellCommand &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBPlatform, Launch,
+ (lldb::SBLaunchInfo &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBPlatform, Kill, (const lldb::pid_t));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBPlatform, MakeDirectory,
+ (const char *, uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBPlatform, GetFilePermissions,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBPlatform, SetFilePermissions,
+ (const char *, uint32_t));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBUnixSignals, SBPlatform, GetUnixSignals,
+ ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBProcess.cpp b/contrib/llvm-project/lldb/source/API/SBProcess.cpp
new file mode 100644
index 000000000000..4226ff77ecdc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBProcess.cpp
@@ -0,0 +1,1404 @@
+//===-- SBProcess.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBProcess.h"
+#include "SBReproducerPrivate.h"
+
+#include <inttypes.h>
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Stream.h"
+
+
+#include "lldb/API/SBBroadcaster.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBMemoryRegionInfo.h"
+#include "lldb/API/SBMemoryRegionInfoList.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+#include "lldb/API/SBStructuredData.h"
+#include "lldb/API/SBThread.h"
+#include "lldb/API/SBThreadCollection.h"
+#include "lldb/API/SBTrace.h"
+#include "lldb/API/SBTraceOptions.h"
+#include "lldb/API/SBUnixSignals.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBProcess::SBProcess() : m_opaque_wp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBProcess);
+}
+
+// SBProcess constructor
+
+SBProcess::SBProcess(const SBProcess &rhs) : m_opaque_wp(rhs.m_opaque_wp) {
+ LLDB_RECORD_CONSTRUCTOR(SBProcess, (const lldb::SBProcess &), rhs);
+}
+
+SBProcess::SBProcess(const lldb::ProcessSP &process_sp)
+ : m_opaque_wp(process_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBProcess, (const lldb::ProcessSP &), process_sp);
+}
+
+const SBProcess &SBProcess::operator=(const SBProcess &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBProcess &,
+ SBProcess, operator=,(const lldb::SBProcess &), rhs);
+
+ if (this != &rhs)
+ m_opaque_wp = rhs.m_opaque_wp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+// Destructor
+SBProcess::~SBProcess() {}
+
+const char *SBProcess::GetBroadcasterClassName() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(const char *, SBProcess,
+ GetBroadcasterClassName);
+
+ return Process::GetStaticBroadcasterClass().AsCString();
+}
+
+const char *SBProcess::GetPluginName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBProcess, GetPluginName);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ return process_sp->GetPluginName().GetCString();
+ }
+ return "<Unknown>";
+}
+
+const char *SBProcess::GetShortPluginName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBProcess, GetShortPluginName);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ return process_sp->GetPluginName().GetCString();
+ }
+ return "<Unknown>";
+}
+
+lldb::ProcessSP SBProcess::GetSP() const { return m_opaque_wp.lock(); }
+
+void SBProcess::SetSP(const ProcessSP &process_sp) { m_opaque_wp = process_sp; }
+
+void SBProcess::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBProcess, Clear);
+
+ m_opaque_wp.reset();
+}
+
+bool SBProcess::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBProcess, IsValid);
+ return this->operator bool();
+}
+SBProcess::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBProcess, operator bool);
+
+ ProcessSP process_sp(m_opaque_wp.lock());
+ return ((bool)process_sp && process_sp->IsValid());
+}
+
+bool SBProcess::RemoteLaunch(char const **argv, char const **envp,
+ const char *stdin_path, const char *stdout_path,
+ const char *stderr_path,
+ const char *working_directory,
+ uint32_t launch_flags, bool stop_at_entry,
+ lldb::SBError &error) {
+ LLDB_RECORD_METHOD(bool, SBProcess, RemoteLaunch,
+ (const char **, const char **, const char *, const char *,
+ const char *, const char *, uint32_t, bool,
+ lldb::SBError &),
+ argv, envp, stdin_path, stdout_path, stderr_path,
+ working_directory, launch_flags, stop_at_entry, error);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ if (process_sp->GetState() == eStateConnected) {
+ if (stop_at_entry)
+ launch_flags |= eLaunchFlagStopAtEntry;
+ ProcessLaunchInfo launch_info(FileSpec(stdin_path), FileSpec(stdout_path),
+ FileSpec(stderr_path),
+ FileSpec(working_directory), launch_flags);
+ Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer();
+ if (exe_module)
+ launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true);
+ if (argv)
+ launch_info.GetArguments().AppendArguments(argv);
+ if (envp)
+ launch_info.GetEnvironment() = Environment(envp);
+ error.SetError(process_sp->Launch(launch_info));
+ } else {
+ error.SetErrorString("must be in eStateConnected to call RemoteLaunch");
+ }
+ } else {
+ error.SetErrorString("unable to attach pid");
+ }
+
+ return error.Success();
+}
+
+bool SBProcess::RemoteAttachToProcessWithID(lldb::pid_t pid,
+ lldb::SBError &error) {
+ LLDB_RECORD_METHOD(bool, SBProcess, RemoteAttachToProcessWithID,
+ (lldb::pid_t, lldb::SBError &), pid, error);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ if (process_sp->GetState() == eStateConnected) {
+ ProcessAttachInfo attach_info;
+ attach_info.SetProcessID(pid);
+ error.SetError(process_sp->Attach(attach_info));
+ } else {
+ error.SetErrorString(
+ "must be in eStateConnected to call RemoteAttachToProcessWithID");
+ }
+ } else {
+ error.SetErrorString("unable to attach pid");
+ }
+
+ return error.Success();
+}
+
+uint32_t SBProcess::GetNumThreads() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBProcess, GetNumThreads);
+
+ uint32_t num_threads = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+
+ const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock());
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ num_threads = process_sp->GetThreadList().GetSize(can_update);
+ }
+
+ return num_threads;
+}
+
+SBThread SBProcess::GetSelectedThread() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBThread, SBProcess,
+ GetSelectedThread);
+
+ SBThread sb_thread;
+ ThreadSP thread_sp;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ thread_sp = process_sp->GetThreadList().GetSelectedThread();
+ sb_thread.SetThread(thread_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_thread);
+}
+
+SBThread SBProcess::CreateOSPluginThread(lldb::tid_t tid,
+ lldb::addr_t context) {
+ LLDB_RECORD_METHOD(lldb::SBThread, SBProcess, CreateOSPluginThread,
+ (lldb::tid_t, lldb::addr_t), tid, context);
+
+ SBThread sb_thread;
+ ThreadSP thread_sp;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ thread_sp = process_sp->CreateOSPluginThread(tid, context);
+ sb_thread.SetThread(thread_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_thread);
+}
+
+SBTarget SBProcess::GetTarget() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBTarget, SBProcess, GetTarget);
+
+ SBTarget sb_target;
+ TargetSP target_sp;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ target_sp = process_sp->GetTarget().shared_from_this();
+ sb_target.SetSP(target_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+size_t SBProcess::PutSTDIN(const char *src, size_t src_len) {
+ LLDB_RECORD_METHOD(size_t, SBProcess, PutSTDIN, (const char *, size_t), src,
+ src_len);
+
+ size_t ret_val = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Status error;
+ ret_val = process_sp->PutSTDIN(src, src_len, error);
+ }
+
+ return ret_val;
+}
+
+size_t SBProcess::GetSTDOUT(char *dst, size_t dst_len) const {
+ LLDB_RECORD_METHOD_CONST(size_t, SBProcess, GetSTDOUT, (char *, size_t), dst,
+ dst_len);
+
+ size_t bytes_read = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Status error;
+ bytes_read = process_sp->GetSTDOUT(dst, dst_len, error);
+ }
+
+ return bytes_read;
+}
+
+size_t SBProcess::GetSTDERR(char *dst, size_t dst_len) const {
+ LLDB_RECORD_METHOD_CONST(size_t, SBProcess, GetSTDERR, (char *, size_t), dst,
+ dst_len);
+
+ size_t bytes_read = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Status error;
+ bytes_read = process_sp->GetSTDERR(dst, dst_len, error);
+ }
+
+ return bytes_read;
+}
+
+size_t SBProcess::GetAsyncProfileData(char *dst, size_t dst_len) const {
+ LLDB_RECORD_METHOD_CONST(size_t, SBProcess, GetAsyncProfileData,
+ (char *, size_t), dst, dst_len);
+
+ size_t bytes_read = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Status error;
+ bytes_read = process_sp->GetAsyncProfileData(dst, dst_len, error);
+ }
+
+ return bytes_read;
+}
+
+lldb::SBTrace SBProcess::StartTrace(SBTraceOptions &options,
+ lldb::SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBTrace, SBProcess, StartTrace,
+ (lldb::SBTraceOptions &, lldb::SBError &), options, error);
+
+ ProcessSP process_sp(GetSP());
+ error.Clear();
+ SBTrace trace_instance;
+ trace_instance.SetSP(process_sp);
+ lldb::user_id_t uid = LLDB_INVALID_UID;
+
+ if (!process_sp) {
+ error.SetErrorString("invalid process");
+ } else {
+ uid = process_sp->StartTrace(*(options.m_traceoptions_sp), error.ref());
+ trace_instance.SetTraceUID(uid);
+ }
+ return LLDB_RECORD_RESULT(trace_instance);
+}
+
+void SBProcess::ReportEventState(const SBEvent &event, FILE *out) const {
+ LLDB_RECORD_METHOD_CONST(void, SBProcess, ReportEventState,
+ (const lldb::SBEvent &, FILE *), event, out);
+
+ if (out == nullptr)
+ return;
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ const StateType event_state = SBProcess::GetStateFromEvent(event);
+ char message[1024];
+ int message_len = ::snprintf(
+ message, sizeof(message), "Process %" PRIu64 " %s\n",
+ process_sp->GetID(), SBDebugger::StateAsCString(event_state));
+
+ if (message_len > 0)
+ ::fwrite(message, 1, message_len, out);
+ }
+}
+
+void SBProcess::AppendEventStateReport(const SBEvent &event,
+ SBCommandReturnObject &result) {
+ LLDB_RECORD_METHOD(void, SBProcess, AppendEventStateReport,
+ (const lldb::SBEvent &, lldb::SBCommandReturnObject &),
+ event, result);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ const StateType event_state = SBProcess::GetStateFromEvent(event);
+ char message[1024];
+ ::snprintf(message, sizeof(message), "Process %" PRIu64 " %s\n",
+ process_sp->GetID(), SBDebugger::StateAsCString(event_state));
+
+ result.AppendMessage(message);
+ }
+}
+
+bool SBProcess::SetSelectedThread(const SBThread &thread) {
+ LLDB_RECORD_METHOD(bool, SBProcess, SetSelectedThread,
+ (const lldb::SBThread &), thread);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ return process_sp->GetThreadList().SetSelectedThreadByID(
+ thread.GetThreadID());
+ }
+ return false;
+}
+
+bool SBProcess::SetSelectedThreadByID(lldb::tid_t tid) {
+ LLDB_RECORD_METHOD(bool, SBProcess, SetSelectedThreadByID, (lldb::tid_t),
+ tid);
+
+
+ bool ret_val = false;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ ret_val = process_sp->GetThreadList().SetSelectedThreadByID(tid);
+ }
+
+ return ret_val;
+}
+
+bool SBProcess::SetSelectedThreadByIndexID(uint32_t index_id) {
+ LLDB_RECORD_METHOD(bool, SBProcess, SetSelectedThreadByIndexID, (uint32_t),
+ index_id);
+
+ bool ret_val = false;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ ret_val = process_sp->GetThreadList().SetSelectedThreadByIndexID(index_id);
+ }
+
+
+ return ret_val;
+}
+
+SBThread SBProcess::GetThreadAtIndex(size_t index) {
+ LLDB_RECORD_METHOD(lldb::SBThread, SBProcess, GetThreadAtIndex, (size_t),
+ index);
+
+ SBThread sb_thread;
+ ThreadSP thread_sp;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock());
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ thread_sp = process_sp->GetThreadList().GetThreadAtIndex(index, can_update);
+ sb_thread.SetThread(thread_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_thread);
+}
+
+uint32_t SBProcess::GetNumQueues() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBProcess, GetNumQueues);
+
+ uint32_t num_queues = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ num_queues = process_sp->GetQueueList().GetSize();
+ }
+ }
+
+ return num_queues;
+}
+
+SBQueue SBProcess::GetQueueAtIndex(size_t index) {
+ LLDB_RECORD_METHOD(lldb::SBQueue, SBProcess, GetQueueAtIndex, (size_t),
+ index);
+
+ SBQueue sb_queue;
+ QueueSP queue_sp;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ queue_sp = process_sp->GetQueueList().GetQueueAtIndex(index);
+ sb_queue.SetQueue(queue_sp);
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_queue);
+}
+
+uint32_t SBProcess::GetStopID(bool include_expression_stops) {
+ LLDB_RECORD_METHOD(uint32_t, SBProcess, GetStopID, (bool),
+ include_expression_stops);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ if (include_expression_stops)
+ return process_sp->GetStopID();
+ else
+ return process_sp->GetLastNaturalStopID();
+ }
+ return 0;
+}
+
+SBEvent SBProcess::GetStopEventForStopID(uint32_t stop_id) {
+ LLDB_RECORD_METHOD(lldb::SBEvent, SBProcess, GetStopEventForStopID,
+ (uint32_t), stop_id);
+
+ SBEvent sb_event;
+ EventSP event_sp;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ event_sp = process_sp->GetStopEventForStopID(stop_id);
+ sb_event.reset(event_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_event);
+}
+
+StateType SBProcess::GetState() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::StateType, SBProcess, GetState);
+
+ StateType ret_val = eStateInvalid;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ ret_val = process_sp->GetState();
+ }
+
+ return ret_val;
+}
+
+int SBProcess::GetExitStatus() {
+ LLDB_RECORD_METHOD_NO_ARGS(int, SBProcess, GetExitStatus);
+
+ int exit_status = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ exit_status = process_sp->GetExitStatus();
+ }
+
+ return exit_status;
+}
+
+const char *SBProcess::GetExitDescription() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBProcess, GetExitDescription);
+
+ const char *exit_desc = nullptr;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ exit_desc = process_sp->GetExitDescription();
+ }
+ return exit_desc;
+}
+
+lldb::pid_t SBProcess::GetProcessID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::pid_t, SBProcess, GetProcessID);
+
+ lldb::pid_t ret_val = LLDB_INVALID_PROCESS_ID;
+ ProcessSP process_sp(GetSP());
+ if (process_sp)
+ ret_val = process_sp->GetID();
+
+ return ret_val;
+}
+
+uint32_t SBProcess::GetUniqueID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBProcess, GetUniqueID);
+
+ uint32_t ret_val = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp)
+ ret_val = process_sp->GetUniqueID();
+ return ret_val;
+}
+
+ByteOrder SBProcess::GetByteOrder() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::ByteOrder, SBProcess, GetByteOrder);
+
+ ByteOrder byteOrder = eByteOrderInvalid;
+ ProcessSP process_sp(GetSP());
+ if (process_sp)
+ byteOrder = process_sp->GetTarget().GetArchitecture().GetByteOrder();
+
+
+ return byteOrder;
+}
+
+uint32_t SBProcess::GetAddressByteSize() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBProcess, GetAddressByteSize);
+
+ uint32_t size = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp)
+ size = process_sp->GetTarget().GetArchitecture().GetAddressByteSize();
+
+
+ return size;
+}
+
+SBError SBProcess::Continue() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBProcess, Continue);
+
+ SBError sb_error;
+ ProcessSP process_sp(GetSP());
+
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+
+ if (process_sp->GetTarget().GetDebugger().GetAsyncExecution())
+ sb_error.ref() = process_sp->Resume();
+ else
+ sb_error.ref() = process_sp->ResumeSynchronous(nullptr);
+ } else
+ sb_error.SetErrorString("SBProcess is invalid");
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBProcess::Destroy() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBProcess, Destroy);
+
+ SBError sb_error;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ sb_error.SetError(process_sp->Destroy(false));
+ } else
+ sb_error.SetErrorString("SBProcess is invalid");
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBProcess::Stop() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBProcess, Stop);
+
+ SBError sb_error;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ sb_error.SetError(process_sp->Halt());
+ } else
+ sb_error.SetErrorString("SBProcess is invalid");
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBProcess::Kill() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBProcess, Kill);
+
+ SBError sb_error;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ sb_error.SetError(process_sp->Destroy(true));
+ } else
+ sb_error.SetErrorString("SBProcess is invalid");
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBProcess::Detach() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBProcess, Detach);
+
+ // FIXME: This should come from a process default.
+ bool keep_stopped = false;
+ return LLDB_RECORD_RESULT(Detach(keep_stopped));
+}
+
+SBError SBProcess::Detach(bool keep_stopped) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBProcess, Detach, (bool), keep_stopped);
+
+ SBError sb_error;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ sb_error.SetError(process_sp->Detach(keep_stopped));
+ } else
+ sb_error.SetErrorString("SBProcess is invalid");
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBProcess::Signal(int signo) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBProcess, Signal, (int), signo);
+
+ SBError sb_error;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ sb_error.SetError(process_sp->Signal(signo));
+ } else
+ sb_error.SetErrorString("SBProcess is invalid");
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBUnixSignals SBProcess::GetUnixSignals() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBUnixSignals, SBProcess, GetUnixSignals);
+
+ if (auto process_sp = GetSP())
+ return LLDB_RECORD_RESULT(SBUnixSignals{process_sp});
+
+ return LLDB_RECORD_RESULT(SBUnixSignals{});
+}
+
+void SBProcess::SendAsyncInterrupt() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBProcess, SendAsyncInterrupt);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ process_sp->SendAsyncInterrupt();
+ }
+}
+
+SBThread SBProcess::GetThreadByID(tid_t tid) {
+ LLDB_RECORD_METHOD(lldb::SBThread, SBProcess, GetThreadByID, (lldb::tid_t),
+ tid);
+
+ SBThread sb_thread;
+ ThreadSP thread_sp;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock());
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ thread_sp = process_sp->GetThreadList().FindThreadByID(tid, can_update);
+ sb_thread.SetThread(thread_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_thread);
+}
+
+SBThread SBProcess::GetThreadByIndexID(uint32_t index_id) {
+ LLDB_RECORD_METHOD(lldb::SBThread, SBProcess, GetThreadByIndexID, (uint32_t),
+ index_id);
+
+ SBThread sb_thread;
+ ThreadSP thread_sp;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock());
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ thread_sp =
+ process_sp->GetThreadList().FindThreadByIndexID(index_id, can_update);
+ sb_thread.SetThread(thread_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_thread);
+}
+
+StateType SBProcess::GetStateFromEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(lldb::StateType, SBProcess, GetStateFromEvent,
+ (const lldb::SBEvent &), event);
+
+ StateType ret_val = Process::ProcessEventData::GetStateFromEvent(event.get());
+
+ return ret_val;
+}
+
+bool SBProcess::GetRestartedFromEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBProcess, GetRestartedFromEvent,
+ (const lldb::SBEvent &), event);
+
+ bool ret_val = Process::ProcessEventData::GetRestartedFromEvent(event.get());
+
+ return ret_val;
+}
+
+size_t SBProcess::GetNumRestartedReasonsFromEvent(const lldb::SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(size_t, SBProcess, GetNumRestartedReasonsFromEvent,
+ (const lldb::SBEvent &), event);
+
+ return Process::ProcessEventData::GetNumRestartedReasons(event.get());
+}
+
+const char *
+SBProcess::GetRestartedReasonAtIndexFromEvent(const lldb::SBEvent &event,
+ size_t idx) {
+ LLDB_RECORD_STATIC_METHOD(const char *, SBProcess,
+ GetRestartedReasonAtIndexFromEvent,
+ (const lldb::SBEvent &, size_t), event, idx);
+
+ return Process::ProcessEventData::GetRestartedReasonAtIndex(event.get(), idx);
+}
+
+SBProcess SBProcess::GetProcessFromEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBProcess, SBProcess, GetProcessFromEvent,
+ (const lldb::SBEvent &), event);
+
+ ProcessSP process_sp =
+ Process::ProcessEventData::GetProcessFromEvent(event.get());
+ if (!process_sp) {
+ // StructuredData events also know the process they come from. Try that.
+ process_sp = EventDataStructuredData::GetProcessFromEvent(event.get());
+ }
+
+ return LLDB_RECORD_RESULT(SBProcess(process_sp));
+}
+
+bool SBProcess::GetInterruptedFromEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBProcess, GetInterruptedFromEvent,
+ (const lldb::SBEvent &), event);
+
+ return Process::ProcessEventData::GetInterruptedFromEvent(event.get());
+}
+
+lldb::SBStructuredData
+SBProcess::GetStructuredDataFromEvent(const lldb::SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBStructuredData, SBProcess,
+ GetStructuredDataFromEvent, (const lldb::SBEvent &),
+ event);
+
+ return LLDB_RECORD_RESULT(SBStructuredData(event.GetSP()));
+}
+
+bool SBProcess::EventIsProcessEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBProcess, EventIsProcessEvent,
+ (const lldb::SBEvent &), event);
+
+ return (event.GetBroadcasterClass() == SBProcess::GetBroadcasterClass()) &&
+ !EventIsStructuredDataEvent(event);
+}
+
+bool SBProcess::EventIsStructuredDataEvent(const lldb::SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBProcess, EventIsStructuredDataEvent,
+ (const lldb::SBEvent &), event);
+
+ EventSP event_sp = event.GetSP();
+ EventData *event_data = event_sp ? event_sp->GetData() : nullptr;
+ return event_data && (event_data->GetFlavor() ==
+ EventDataStructuredData::GetFlavorString());
+}
+
+SBBroadcaster SBProcess::GetBroadcaster() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBBroadcaster, SBProcess,
+ GetBroadcaster);
+
+
+ ProcessSP process_sp(GetSP());
+
+ SBBroadcaster broadcaster(process_sp.get(), false);
+
+
+ return LLDB_RECORD_RESULT(broadcaster);
+}
+
+const char *SBProcess::GetBroadcasterClass() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(const char *, SBProcess,
+ GetBroadcasterClass);
+
+ return Process::GetStaticBroadcasterClass().AsCString();
+}
+
+size_t SBProcess::ReadMemory(addr_t addr, void *dst, size_t dst_len,
+ SBError &sb_error) {
+ LLDB_RECORD_DUMMY(size_t, SBProcess, ReadMemory,
+ (lldb::addr_t, void *, size_t, lldb::SBError &), addr, dst,
+ dst_len, sb_error);
+
+ size_t bytes_read = 0;
+
+ ProcessSP process_sp(GetSP());
+
+
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ bytes_read = process_sp->ReadMemory(addr, dst, dst_len, sb_error.ref());
+ } else {
+ sb_error.SetErrorString("process is running");
+ }
+ } else {
+ sb_error.SetErrorString("SBProcess is invalid");
+ }
+
+ return bytes_read;
+}
+
+size_t SBProcess::ReadCStringFromMemory(addr_t addr, void *buf, size_t size,
+ lldb::SBError &sb_error) {
+ LLDB_RECORD_DUMMY(size_t, SBProcess, ReadCStringFromMemory,
+ (lldb::addr_t, void *, size_t, lldb::SBError &), addr, buf,
+ size, sb_error);
+
+ size_t bytes_read = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ bytes_read = process_sp->ReadCStringFromMemory(addr, (char *)buf, size,
+ sb_error.ref());
+ } else {
+ sb_error.SetErrorString("process is running");
+ }
+ } else {
+ sb_error.SetErrorString("SBProcess is invalid");
+ }
+ return bytes_read;
+}
+
+uint64_t SBProcess::ReadUnsignedFromMemory(addr_t addr, uint32_t byte_size,
+ lldb::SBError &sb_error) {
+ LLDB_RECORD_METHOD(uint64_t, SBProcess, ReadUnsignedFromMemory,
+ (lldb::addr_t, uint32_t, lldb::SBError &), addr, byte_size,
+ sb_error);
+
+ uint64_t value = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ value = process_sp->ReadUnsignedIntegerFromMemory(addr, byte_size, 0,
+ sb_error.ref());
+ } else {
+ sb_error.SetErrorString("process is running");
+ }
+ } else {
+ sb_error.SetErrorString("SBProcess is invalid");
+ }
+ return value;
+}
+
+lldb::addr_t SBProcess::ReadPointerFromMemory(addr_t addr,
+ lldb::SBError &sb_error) {
+ LLDB_RECORD_METHOD(lldb::addr_t, SBProcess, ReadPointerFromMemory,
+ (lldb::addr_t, lldb::SBError &), addr, sb_error);
+
+ lldb::addr_t ptr = LLDB_INVALID_ADDRESS;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ ptr = process_sp->ReadPointerFromMemory(addr, sb_error.ref());
+ } else {
+ sb_error.SetErrorString("process is running");
+ }
+ } else {
+ sb_error.SetErrorString("SBProcess is invalid");
+ }
+ return ptr;
+}
+
+size_t SBProcess::WriteMemory(addr_t addr, const void *src, size_t src_len,
+ SBError &sb_error) {
+ LLDB_RECORD_DUMMY(size_t, SBProcess, WriteMemory,
+ (lldb::addr_t, const void *, size_t, lldb::SBError &), addr,
+ src, src_len, sb_error);
+
+ size_t bytes_written = 0;
+
+ ProcessSP process_sp(GetSP());
+
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ bytes_written =
+ process_sp->WriteMemory(addr, src, src_len, sb_error.ref());
+ } else {
+ sb_error.SetErrorString("process is running");
+ }
+ }
+
+ return bytes_written;
+}
+
+bool SBProcess::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBProcess, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ char path[PATH_MAX];
+ GetTarget().GetExecutable().GetPath(path, sizeof(path));
+ Module *exe_module = process_sp->GetTarget().GetExecutableModulePointer();
+ const char *exe_name = nullptr;
+ if (exe_module)
+ exe_name = exe_module->GetFileSpec().GetFilename().AsCString();
+
+ strm.Printf("SBProcess: pid = %" PRIu64 ", state = %s, threads = %d%s%s",
+ process_sp->GetID(), lldb_private::StateAsCString(GetState()),
+ GetNumThreads(), exe_name ? ", executable = " : "",
+ exe_name ? exe_name : "");
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+uint32_t
+SBProcess::GetNumSupportedHardwareWatchpoints(lldb::SBError &sb_error) const {
+ LLDB_RECORD_METHOD_CONST(uint32_t, SBProcess,
+ GetNumSupportedHardwareWatchpoints,
+ (lldb::SBError &), sb_error);
+
+ uint32_t num = 0;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ sb_error.SetError(process_sp->GetWatchpointSupportInfo(num));
+ } else {
+ sb_error.SetErrorString("SBProcess is invalid");
+ }
+ return num;
+}
+
+uint32_t SBProcess::LoadImage(lldb::SBFileSpec &sb_remote_image_spec,
+ lldb::SBError &sb_error) {
+ LLDB_RECORD_METHOD(uint32_t, SBProcess, LoadImage,
+ (lldb::SBFileSpec &, lldb::SBError &),
+ sb_remote_image_spec, sb_error);
+
+ return LoadImage(SBFileSpec(), sb_remote_image_spec, sb_error);
+}
+
+uint32_t SBProcess::LoadImage(const lldb::SBFileSpec &sb_local_image_spec,
+ const lldb::SBFileSpec &sb_remote_image_spec,
+ lldb::SBError &sb_error) {
+ LLDB_RECORD_METHOD(
+ uint32_t, SBProcess, LoadImage,
+ (const lldb::SBFileSpec &, const lldb::SBFileSpec &, lldb::SBError &),
+ sb_local_image_spec, sb_remote_image_spec, sb_error);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ PlatformSP platform_sp = process_sp->GetTarget().GetPlatform();
+ return platform_sp->LoadImage(process_sp.get(), *sb_local_image_spec,
+ *sb_remote_image_spec, sb_error.ref());
+ } else {
+ sb_error.SetErrorString("process is running");
+ }
+ } else {
+ sb_error.SetErrorString("process is invalid");
+ }
+ return LLDB_INVALID_IMAGE_TOKEN;
+}
+
+uint32_t SBProcess::LoadImageUsingPaths(const lldb::SBFileSpec &image_spec,
+ SBStringList &paths,
+ lldb::SBFileSpec &loaded_path,
+ lldb::SBError &error) {
+ LLDB_RECORD_METHOD(uint32_t, SBProcess, LoadImageUsingPaths,
+ (const lldb::SBFileSpec &, lldb::SBStringList &,
+ lldb::SBFileSpec &, lldb::SBError &),
+ image_spec, paths, loaded_path, error);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ PlatformSP platform_sp = process_sp->GetTarget().GetPlatform();
+ size_t num_paths = paths.GetSize();
+ std::vector<std::string> paths_vec;
+ paths_vec.reserve(num_paths);
+ for (size_t i = 0; i < num_paths; i++)
+ paths_vec.push_back(paths.GetStringAtIndex(i));
+ FileSpec loaded_spec;
+
+ uint32_t token = platform_sp->LoadImageUsingPaths(
+ process_sp.get(), *image_spec, paths_vec, error.ref(), &loaded_spec);
+ if (token != LLDB_INVALID_IMAGE_TOKEN)
+ loaded_path = loaded_spec;
+ return token;
+ } else {
+ error.SetErrorString("process is running");
+ }
+ } else {
+ error.SetErrorString("process is invalid");
+ }
+
+ return LLDB_INVALID_IMAGE_TOKEN;
+}
+
+lldb::SBError SBProcess::UnloadImage(uint32_t image_token) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBProcess, UnloadImage, (uint32_t),
+ image_token);
+
+ lldb::SBError sb_error;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ PlatformSP platform_sp = process_sp->GetTarget().GetPlatform();
+ sb_error.SetError(
+ platform_sp->UnloadImage(process_sp.get(), image_token));
+ } else {
+ sb_error.SetErrorString("process is running");
+ }
+ } else
+ sb_error.SetErrorString("invalid process");
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+lldb::SBError SBProcess::SendEventData(const char *event_data) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBProcess, SendEventData, (const char *),
+ event_data);
+
+ lldb::SBError sb_error;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ sb_error.SetError(process_sp->SendEventData(event_data));
+ } else {
+ sb_error.SetErrorString("process is running");
+ }
+ } else
+ sb_error.SetErrorString("invalid process");
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+uint32_t SBProcess::GetNumExtendedBacktraceTypes() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBProcess, GetNumExtendedBacktraceTypes);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp && process_sp->GetSystemRuntime()) {
+ SystemRuntime *runtime = process_sp->GetSystemRuntime();
+ return runtime->GetExtendedBacktraceTypes().size();
+ }
+ return 0;
+}
+
+const char *SBProcess::GetExtendedBacktraceTypeAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(const char *, SBProcess, GetExtendedBacktraceTypeAtIndex,
+ (uint32_t), idx);
+
+ ProcessSP process_sp(GetSP());
+ if (process_sp && process_sp->GetSystemRuntime()) {
+ SystemRuntime *runtime = process_sp->GetSystemRuntime();
+ const std::vector<ConstString> &names =
+ runtime->GetExtendedBacktraceTypes();
+ if (idx < names.size()) {
+ return names[idx].AsCString();
+ }
+ }
+ return nullptr;
+}
+
+SBThreadCollection SBProcess::GetHistoryThreads(addr_t addr) {
+ LLDB_RECORD_METHOD(lldb::SBThreadCollection, SBProcess, GetHistoryThreads,
+ (lldb::addr_t), addr);
+
+ ProcessSP process_sp(GetSP());
+ SBThreadCollection threads;
+ if (process_sp) {
+ threads = SBThreadCollection(process_sp->GetHistoryThreads(addr));
+ }
+ return LLDB_RECORD_RESULT(threads);
+}
+
+bool SBProcess::IsInstrumentationRuntimePresent(
+ InstrumentationRuntimeType type) {
+ LLDB_RECORD_METHOD(bool, SBProcess, IsInstrumentationRuntimePresent,
+ (lldb::InstrumentationRuntimeType), type);
+
+ ProcessSP process_sp(GetSP());
+ if (!process_sp)
+ return false;
+
+ InstrumentationRuntimeSP runtime_sp =
+ process_sp->GetInstrumentationRuntime(type);
+
+ if (!runtime_sp.get())
+ return false;
+
+ return runtime_sp->IsActive();
+}
+
+lldb::SBError SBProcess::SaveCore(const char *file_name) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBProcess, SaveCore, (const char *),
+ file_name);
+
+ lldb::SBError error;
+ ProcessSP process_sp(GetSP());
+ if (!process_sp) {
+ error.SetErrorString("SBProcess is invalid");
+ return LLDB_RECORD_RESULT(error);
+ }
+
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+
+ if (process_sp->GetState() != eStateStopped) {
+ error.SetErrorString("the process is not stopped");
+ return LLDB_RECORD_RESULT(error);
+ }
+
+ FileSpec core_file(file_name);
+ error.ref() = PluginManager::SaveCore(process_sp, core_file);
+ return LLDB_RECORD_RESULT(error);
+}
+
+lldb::SBError
+SBProcess::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ SBMemoryRegionInfo &sb_region_info) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBProcess, GetMemoryRegionInfo,
+ (lldb::addr_t, lldb::SBMemoryRegionInfo &), load_addr,
+ sb_region_info);
+
+ lldb::SBError sb_error;
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+
+ sb_error.ref() =
+ process_sp->GetMemoryRegionInfo(load_addr, sb_region_info.ref());
+ } else {
+ sb_error.SetErrorString("process is running");
+ }
+ } else {
+ sb_error.SetErrorString("SBProcess is invalid");
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+lldb::SBMemoryRegionInfoList SBProcess::GetMemoryRegions() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBMemoryRegionInfoList, SBProcess,
+ GetMemoryRegions);
+
+ lldb::SBMemoryRegionInfoList sb_region_list;
+
+ ProcessSP process_sp(GetSP());
+ Process::StopLocker stop_locker;
+ if (process_sp && stop_locker.TryLock(&process_sp->GetRunLock())) {
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+
+ process_sp->GetMemoryRegions(sb_region_list.ref());
+ }
+
+ return LLDB_RECORD_RESULT(sb_region_list);
+}
+
+lldb::SBProcessInfo SBProcess::GetProcessInfo() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBProcessInfo, SBProcess, GetProcessInfo);
+
+ lldb::SBProcessInfo sb_proc_info;
+ ProcessSP process_sp(GetSP());
+ ProcessInstanceInfo proc_info;
+ if (process_sp && process_sp->GetProcessInfo(proc_info)) {
+ sb_proc_info.SetProcessInfo(proc_info);
+ }
+ return LLDB_RECORD_RESULT(sb_proc_info);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBProcess>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBProcess, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBProcess, (const lldb::SBProcess &));
+ LLDB_REGISTER_CONSTRUCTOR(SBProcess, (const lldb::ProcessSP &));
+ LLDB_REGISTER_METHOD(const lldb::SBProcess &,
+ SBProcess, operator=,(const lldb::SBProcess &));
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBProcess,
+ GetBroadcasterClassName, ());
+ LLDB_REGISTER_METHOD(const char *, SBProcess, GetPluginName, ());
+ LLDB_REGISTER_METHOD(const char *, SBProcess, GetShortPluginName, ());
+ LLDB_REGISTER_METHOD(void, SBProcess, Clear, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBProcess, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBProcess, operator bool, ());
+ LLDB_REGISTER_METHOD(bool, SBProcess, RemoteLaunch,
+ (const char **, const char **, const char *,
+ const char *, const char *, const char *, uint32_t,
+ bool, lldb::SBError &));
+ LLDB_REGISTER_METHOD(bool, SBProcess, RemoteAttachToProcessWithID,
+ (lldb::pid_t, lldb::SBError &));
+ LLDB_REGISTER_METHOD(uint32_t, SBProcess, GetNumThreads, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBThread, SBProcess, GetSelectedThread,
+ ());
+ LLDB_REGISTER_METHOD(lldb::SBThread, SBProcess, CreateOSPluginThread,
+ (lldb::tid_t, lldb::addr_t));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBTarget, SBProcess, GetTarget, ());
+ LLDB_REGISTER_METHOD(size_t, SBProcess, PutSTDIN, (const char *, size_t));
+ LLDB_REGISTER_METHOD_CONST(size_t, SBProcess, GetSTDOUT, (char *, size_t));
+ LLDB_REGISTER_METHOD_CONST(size_t, SBProcess, GetSTDERR, (char *, size_t));
+ LLDB_REGISTER_METHOD_CONST(size_t, SBProcess, GetAsyncProfileData,
+ (char *, size_t));
+ LLDB_REGISTER_METHOD(lldb::SBTrace, SBProcess, StartTrace,
+ (lldb::SBTraceOptions &, lldb::SBError &));
+ LLDB_REGISTER_METHOD_CONST(void, SBProcess, ReportEventState,
+ (const lldb::SBEvent &, FILE *));
+ LLDB_REGISTER_METHOD(
+ void, SBProcess, AppendEventStateReport,
+ (const lldb::SBEvent &, lldb::SBCommandReturnObject &));
+ LLDB_REGISTER_METHOD(bool, SBProcess, SetSelectedThread,
+ (const lldb::SBThread &));
+ LLDB_REGISTER_METHOD(bool, SBProcess, SetSelectedThreadByID, (lldb::tid_t));
+ LLDB_REGISTER_METHOD(bool, SBProcess, SetSelectedThreadByIndexID,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBThread, SBProcess, GetThreadAtIndex, (size_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBProcess, GetNumQueues, ());
+ LLDB_REGISTER_METHOD(lldb::SBQueue, SBProcess, GetQueueAtIndex, (size_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBProcess, GetStopID, (bool));
+ LLDB_REGISTER_METHOD(lldb::SBEvent, SBProcess, GetStopEventForStopID,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::StateType, SBProcess, GetState, ());
+ LLDB_REGISTER_METHOD(int, SBProcess, GetExitStatus, ());
+ LLDB_REGISTER_METHOD(const char *, SBProcess, GetExitDescription, ());
+ LLDB_REGISTER_METHOD(lldb::pid_t, SBProcess, GetProcessID, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBProcess, GetUniqueID, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::ByteOrder, SBProcess, GetByteOrder, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBProcess, GetAddressByteSize, ());
+ LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, Continue, ());
+ LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, Destroy, ());
+ LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, Stop, ());
+ LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, Kill, ());
+ LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, Detach, ());
+ LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, Detach, (bool));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, Signal, (int));
+ LLDB_REGISTER_METHOD(lldb::SBUnixSignals, SBProcess, GetUnixSignals, ());
+ LLDB_REGISTER_METHOD(void, SBProcess, SendAsyncInterrupt, ());
+ LLDB_REGISTER_METHOD(lldb::SBThread, SBProcess, GetThreadByID,
+ (lldb::tid_t));
+ LLDB_REGISTER_METHOD(lldb::SBThread, SBProcess, GetThreadByIndexID,
+ (uint32_t));
+ LLDB_REGISTER_STATIC_METHOD(lldb::StateType, SBProcess, GetStateFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(bool, SBProcess, GetRestartedFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(size_t, SBProcess,
+ GetNumRestartedReasonsFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBProcess,
+ GetRestartedReasonAtIndexFromEvent,
+ (const lldb::SBEvent &, size_t));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBProcess, SBProcess, GetProcessFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(bool, SBProcess, GetInterruptedFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBStructuredData, SBProcess,
+ GetStructuredDataFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(bool, SBProcess, EventIsProcessEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(bool, SBProcess, EventIsStructuredDataEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBBroadcaster, SBProcess, GetBroadcaster,
+ ());
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBProcess, GetBroadcasterClass,
+ ());
+ LLDB_REGISTER_METHOD(uint64_t, SBProcess, ReadUnsignedFromMemory,
+ (lldb::addr_t, uint32_t, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBProcess, ReadPointerFromMemory,
+ (lldb::addr_t, lldb::SBError &));
+ LLDB_REGISTER_METHOD(bool, SBProcess, GetDescription, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBProcess,
+ GetNumSupportedHardwareWatchpoints,
+ (lldb::SBError &));
+ LLDB_REGISTER_METHOD(uint32_t, SBProcess, LoadImage,
+ (lldb::SBFileSpec &, lldb::SBError &));
+ LLDB_REGISTER_METHOD(
+ uint32_t, SBProcess, LoadImage,
+ (const lldb::SBFileSpec &, const lldb::SBFileSpec &, lldb::SBError &));
+ LLDB_REGISTER_METHOD(uint32_t, SBProcess, LoadImageUsingPaths,
+ (const lldb::SBFileSpec &, lldb::SBStringList &,
+ lldb::SBFileSpec &, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, UnloadImage, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, SendEventData,
+ (const char *));
+ LLDB_REGISTER_METHOD(uint32_t, SBProcess, GetNumExtendedBacktraceTypes, ());
+ LLDB_REGISTER_METHOD(const char *, SBProcess,
+ GetExtendedBacktraceTypeAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBThreadCollection, SBProcess, GetHistoryThreads,
+ (lldb::addr_t));
+ LLDB_REGISTER_METHOD(bool, SBProcess, IsInstrumentationRuntimePresent,
+ (lldb::InstrumentationRuntimeType));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, SaveCore, (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, GetMemoryRegionInfo,
+ (lldb::addr_t, lldb::SBMemoryRegionInfo &));
+ LLDB_REGISTER_METHOD(lldb::SBMemoryRegionInfoList, SBProcess,
+ GetMemoryRegions, ());
+ LLDB_REGISTER_METHOD(lldb::SBProcessInfo, SBProcess, GetProcessInfo, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBProcessInfo.cpp b/contrib/llvm-project/lldb/source/API/SBProcessInfo.cpp
new file mode 100644
index 000000000000..be242ec5872d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBProcessInfo.cpp
@@ -0,0 +1,210 @@
+//===-- SBProcessInfo.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBProcessInfo.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/Utility/ProcessInfo.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBProcessInfo::SBProcessInfo() : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBProcessInfo);
+}
+
+SBProcessInfo::SBProcessInfo(const SBProcessInfo &rhs) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBProcessInfo, (const lldb::SBProcessInfo &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+SBProcessInfo::~SBProcessInfo() {}
+
+SBProcessInfo &SBProcessInfo::operator=(const SBProcessInfo &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBProcessInfo &,
+ SBProcessInfo, operator=,(const lldb::SBProcessInfo &),
+ rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+ProcessInstanceInfo &SBProcessInfo::ref() {
+ if (m_opaque_up == nullptr) {
+ m_opaque_up.reset(new ProcessInstanceInfo());
+ }
+ return *m_opaque_up;
+}
+
+void SBProcessInfo::SetProcessInfo(const ProcessInstanceInfo &proc_info_ref) {
+ ref() = proc_info_ref;
+}
+
+bool SBProcessInfo::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBProcessInfo, IsValid);
+ return this->operator bool();
+}
+SBProcessInfo::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBProcessInfo, operator bool);
+
+ return m_opaque_up != nullptr;
+}
+
+const char *SBProcessInfo::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBProcessInfo, GetName);
+
+ const char *name = nullptr;
+ if (m_opaque_up) {
+ name = m_opaque_up->GetName();
+ }
+ return name;
+}
+
+SBFileSpec SBProcessInfo::GetExecutableFile() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFileSpec, SBProcessInfo,
+ GetExecutableFile);
+
+ SBFileSpec file_spec;
+ if (m_opaque_up) {
+ file_spec.SetFileSpec(m_opaque_up->GetExecutableFile());
+ }
+ return LLDB_RECORD_RESULT(file_spec);
+}
+
+lldb::pid_t SBProcessInfo::GetProcessID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::pid_t, SBProcessInfo, GetProcessID);
+
+ lldb::pid_t proc_id = LLDB_INVALID_PROCESS_ID;
+ if (m_opaque_up) {
+ proc_id = m_opaque_up->GetProcessID();
+ }
+ return proc_id;
+}
+
+uint32_t SBProcessInfo::GetUserID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBProcessInfo, GetUserID);
+
+ uint32_t user_id = UINT32_MAX;
+ if (m_opaque_up) {
+ user_id = m_opaque_up->GetUserID();
+ }
+ return user_id;
+}
+
+uint32_t SBProcessInfo::GetGroupID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBProcessInfo, GetGroupID);
+
+ uint32_t group_id = UINT32_MAX;
+ if (m_opaque_up) {
+ group_id = m_opaque_up->GetGroupID();
+ }
+ return group_id;
+}
+
+bool SBProcessInfo::UserIDIsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBProcessInfo, UserIDIsValid);
+
+ bool is_valid = false;
+ if (m_opaque_up) {
+ is_valid = m_opaque_up->UserIDIsValid();
+ }
+ return is_valid;
+}
+
+bool SBProcessInfo::GroupIDIsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBProcessInfo, GroupIDIsValid);
+
+ bool is_valid = false;
+ if (m_opaque_up) {
+ is_valid = m_opaque_up->GroupIDIsValid();
+ }
+ return is_valid;
+}
+
+uint32_t SBProcessInfo::GetEffectiveUserID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBProcessInfo, GetEffectiveUserID);
+
+ uint32_t user_id = UINT32_MAX;
+ if (m_opaque_up) {
+ user_id = m_opaque_up->GetEffectiveUserID();
+ }
+ return user_id;
+}
+
+uint32_t SBProcessInfo::GetEffectiveGroupID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBProcessInfo, GetEffectiveGroupID);
+
+ uint32_t group_id = UINT32_MAX;
+ if (m_opaque_up) {
+ group_id = m_opaque_up->GetEffectiveGroupID();
+ }
+ return group_id;
+}
+
+bool SBProcessInfo::EffectiveUserIDIsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBProcessInfo, EffectiveUserIDIsValid);
+
+ bool is_valid = false;
+ if (m_opaque_up) {
+ is_valid = m_opaque_up->EffectiveUserIDIsValid();
+ }
+ return is_valid;
+}
+
+bool SBProcessInfo::EffectiveGroupIDIsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBProcessInfo, EffectiveGroupIDIsValid);
+
+ bool is_valid = false;
+ if (m_opaque_up) {
+ is_valid = m_opaque_up->EffectiveGroupIDIsValid();
+ }
+ return is_valid;
+}
+
+lldb::pid_t SBProcessInfo::GetParentProcessID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::pid_t, SBProcessInfo, GetParentProcessID);
+
+ lldb::pid_t proc_id = LLDB_INVALID_PROCESS_ID;
+ if (m_opaque_up) {
+ proc_id = m_opaque_up->GetParentProcessID();
+ }
+ return proc_id;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBProcessInfo>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBProcessInfo, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBProcessInfo, (const lldb::SBProcessInfo &));
+ LLDB_REGISTER_METHOD(
+ lldb::SBProcessInfo &,
+ SBProcessInfo, operator=,(const lldb::SBProcessInfo &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBProcessInfo, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBProcessInfo, operator bool, ());
+ LLDB_REGISTER_METHOD(const char *, SBProcessInfo, GetName, ());
+ LLDB_REGISTER_METHOD(lldb::SBFileSpec, SBProcessInfo, GetExecutableFile,
+ ());
+ LLDB_REGISTER_METHOD(lldb::pid_t, SBProcessInfo, GetProcessID, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBProcessInfo, GetUserID, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBProcessInfo, GetGroupID, ());
+ LLDB_REGISTER_METHOD(bool, SBProcessInfo, UserIDIsValid, ());
+ LLDB_REGISTER_METHOD(bool, SBProcessInfo, GroupIDIsValid, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBProcessInfo, GetEffectiveUserID, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBProcessInfo, GetEffectiveGroupID, ());
+ LLDB_REGISTER_METHOD(bool, SBProcessInfo, EffectiveUserIDIsValid, ());
+ LLDB_REGISTER_METHOD(bool, SBProcessInfo, EffectiveGroupIDIsValid, ());
+ LLDB_REGISTER_METHOD(lldb::pid_t, SBProcessInfo, GetParentProcessID, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBQueue.cpp b/contrib/llvm-project/lldb/source/API/SBQueue.cpp
new file mode 100644
index 000000000000..7d1581c42f60
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBQueue.cpp
@@ -0,0 +1,359 @@
+//===-- SBQueue.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <inttypes.h>
+
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBQueue.h"
+
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBQueueItem.h"
+#include "lldb/API/SBThread.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Queue.h"
+#include "lldb/Target/QueueItem.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace lldb_private {
+
+class QueueImpl {
+public:
+ QueueImpl()
+ : m_queue_wp(), m_threads(), m_thread_list_fetched(false),
+ m_pending_items(), m_pending_items_fetched(false) {}
+
+ QueueImpl(const lldb::QueueSP &queue_sp)
+ : m_queue_wp(), m_threads(), m_thread_list_fetched(false),
+ m_pending_items(), m_pending_items_fetched(false) {
+ m_queue_wp = queue_sp;
+ }
+
+ QueueImpl(const QueueImpl &rhs) {
+ if (&rhs == this)
+ return;
+ m_queue_wp = rhs.m_queue_wp;
+ m_threads = rhs.m_threads;
+ m_thread_list_fetched = rhs.m_thread_list_fetched;
+ m_pending_items = rhs.m_pending_items;
+ m_pending_items_fetched = rhs.m_pending_items_fetched;
+ }
+
+ ~QueueImpl() {}
+
+ bool IsValid() { return m_queue_wp.lock() != nullptr; }
+
+ void Clear() {
+ m_queue_wp.reset();
+ m_thread_list_fetched = false;
+ m_threads.clear();
+ m_pending_items_fetched = false;
+ m_pending_items.clear();
+ }
+
+ void SetQueue(const lldb::QueueSP &queue_sp) {
+ Clear();
+ m_queue_wp = queue_sp;
+ }
+
+ lldb::queue_id_t GetQueueID() const {
+ lldb::queue_id_t result = LLDB_INVALID_QUEUE_ID;
+ lldb::QueueSP queue_sp = m_queue_wp.lock();
+ if (queue_sp) {
+ result = queue_sp->GetID();
+ }
+ return result;
+ }
+
+ uint32_t GetIndexID() const {
+ uint32_t result = LLDB_INVALID_INDEX32;
+ lldb::QueueSP queue_sp = m_queue_wp.lock();
+ if (queue_sp) {
+ result = queue_sp->GetIndexID();
+ }
+ return result;
+ }
+
+ const char *GetName() const {
+ const char *name = nullptr;
+ lldb::QueueSP queue_sp = m_queue_wp.lock();
+ if (queue_sp.get()) {
+ name = queue_sp->GetName();
+ }
+ return name;
+ }
+
+ void FetchThreads() {
+ if (!m_thread_list_fetched) {
+ lldb::QueueSP queue_sp = m_queue_wp.lock();
+ if (queue_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&queue_sp->GetProcess()->GetRunLock())) {
+ const std::vector<ThreadSP> thread_list(queue_sp->GetThreads());
+ m_thread_list_fetched = true;
+ const uint32_t num_threads = thread_list.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ ThreadSP thread_sp = thread_list[idx];
+ if (thread_sp && thread_sp->IsValid()) {
+ m_threads.push_back(thread_sp);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ void FetchItems() {
+ if (!m_pending_items_fetched) {
+ QueueSP queue_sp = m_queue_wp.lock();
+ if (queue_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&queue_sp->GetProcess()->GetRunLock())) {
+ const std::vector<QueueItemSP> queue_items(
+ queue_sp->GetPendingItems());
+ m_pending_items_fetched = true;
+ const uint32_t num_pending_items = queue_items.size();
+ for (uint32_t idx = 0; idx < num_pending_items; ++idx) {
+ QueueItemSP item = queue_items[idx];
+ if (item && item->IsValid()) {
+ m_pending_items.push_back(item);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ uint32_t GetNumThreads() {
+ uint32_t result = 0;
+
+ FetchThreads();
+ if (m_thread_list_fetched) {
+ result = m_threads.size();
+ }
+ return result;
+ }
+
+ lldb::SBThread GetThreadAtIndex(uint32_t idx) {
+ FetchThreads();
+
+ SBThread sb_thread;
+ QueueSP queue_sp = m_queue_wp.lock();
+ if (queue_sp && idx < m_threads.size()) {
+ ProcessSP process_sp = queue_sp->GetProcess();
+ if (process_sp) {
+ ThreadSP thread_sp = m_threads[idx].lock();
+ if (thread_sp) {
+ sb_thread.SetThread(thread_sp);
+ }
+ }
+ }
+ return sb_thread;
+ }
+
+ uint32_t GetNumPendingItems() {
+ uint32_t result = 0;
+
+ QueueSP queue_sp = m_queue_wp.lock();
+ if (!m_pending_items_fetched && queue_sp) {
+ result = queue_sp->GetNumPendingWorkItems();
+ } else {
+ result = m_pending_items.size();
+ }
+ return result;
+ }
+
+ lldb::SBQueueItem GetPendingItemAtIndex(uint32_t idx) {
+ SBQueueItem result;
+ FetchItems();
+ if (m_pending_items_fetched && idx < m_pending_items.size()) {
+ result.SetQueueItem(m_pending_items[idx]);
+ }
+ return result;
+ }
+
+ uint32_t GetNumRunningItems() {
+ uint32_t result = 0;
+ QueueSP queue_sp = m_queue_wp.lock();
+ if (queue_sp)
+ result = queue_sp->GetNumRunningWorkItems();
+ return result;
+ }
+
+ lldb::SBProcess GetProcess() {
+ SBProcess result;
+ QueueSP queue_sp = m_queue_wp.lock();
+ if (queue_sp) {
+ result.SetSP(queue_sp->GetProcess());
+ }
+ return result;
+ }
+
+ lldb::QueueKind GetKind() {
+ lldb::QueueKind kind = eQueueKindUnknown;
+ QueueSP queue_sp = m_queue_wp.lock();
+ if (queue_sp)
+ kind = queue_sp->GetKind();
+
+ return kind;
+ }
+
+private:
+ lldb::QueueWP m_queue_wp;
+ std::vector<lldb::ThreadWP>
+ m_threads; // threads currently executing this queue's items
+ bool
+ m_thread_list_fetched; // have we tried to fetch the threads list already?
+ std::vector<lldb::QueueItemSP> m_pending_items; // items currently enqueued
+ bool m_pending_items_fetched; // have we tried to fetch the item list already?
+};
+}
+
+SBQueue::SBQueue() : m_opaque_sp(new QueueImpl()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBQueue);
+}
+
+SBQueue::SBQueue(const QueueSP &queue_sp)
+ : m_opaque_sp(new QueueImpl(queue_sp)) {
+ LLDB_RECORD_CONSTRUCTOR(SBQueue, (const lldb::QueueSP &), queue_sp);
+}
+
+SBQueue::SBQueue(const SBQueue &rhs) {
+ LLDB_RECORD_CONSTRUCTOR(SBQueue, (const lldb::SBQueue &), rhs);
+
+ if (&rhs == this)
+ return;
+
+ m_opaque_sp = rhs.m_opaque_sp;
+}
+
+const lldb::SBQueue &SBQueue::operator=(const lldb::SBQueue &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBQueue &,
+ SBQueue, operator=,(const lldb::SBQueue &), rhs);
+
+ m_opaque_sp = rhs.m_opaque_sp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBQueue::~SBQueue() {}
+
+bool SBQueue::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBQueue, IsValid);
+ return this->operator bool();
+}
+SBQueue::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBQueue, operator bool);
+
+ return m_opaque_sp->IsValid();
+}
+
+void SBQueue::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBQueue, Clear);
+
+ m_opaque_sp->Clear();
+}
+
+void SBQueue::SetQueue(const QueueSP &queue_sp) {
+ m_opaque_sp->SetQueue(queue_sp);
+}
+
+lldb::queue_id_t SBQueue::GetQueueID() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::queue_id_t, SBQueue, GetQueueID);
+
+ return m_opaque_sp->GetQueueID();
+}
+
+uint32_t SBQueue::GetIndexID() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBQueue, GetIndexID);
+
+ uint32_t index_id = m_opaque_sp->GetIndexID();
+ return index_id;
+}
+
+const char *SBQueue::GetName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBQueue, GetName);
+
+ return m_opaque_sp->GetName();
+}
+
+uint32_t SBQueue::GetNumThreads() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBQueue, GetNumThreads);
+
+ return m_opaque_sp->GetNumThreads();
+}
+
+SBThread SBQueue::GetThreadAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBThread, SBQueue, GetThreadAtIndex, (uint32_t),
+ idx);
+
+ SBThread th = m_opaque_sp->GetThreadAtIndex(idx);
+ return LLDB_RECORD_RESULT(th);
+}
+
+uint32_t SBQueue::GetNumPendingItems() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBQueue, GetNumPendingItems);
+
+ return m_opaque_sp->GetNumPendingItems();
+}
+
+SBQueueItem SBQueue::GetPendingItemAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBQueueItem, SBQueue, GetPendingItemAtIndex,
+ (uint32_t), idx);
+
+ return LLDB_RECORD_RESULT(m_opaque_sp->GetPendingItemAtIndex(idx));
+}
+
+uint32_t SBQueue::GetNumRunningItems() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBQueue, GetNumRunningItems);
+
+ return m_opaque_sp->GetNumRunningItems();
+}
+
+SBProcess SBQueue::GetProcess() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBProcess, SBQueue, GetProcess);
+
+ return LLDB_RECORD_RESULT(m_opaque_sp->GetProcess());
+}
+
+lldb::QueueKind SBQueue::GetKind() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::QueueKind, SBQueue, GetKind);
+
+ return m_opaque_sp->GetKind();
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBQueue>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBQueue, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBQueue, (const lldb::QueueSP &));
+ LLDB_REGISTER_CONSTRUCTOR(SBQueue, (const lldb::SBQueue &));
+ LLDB_REGISTER_METHOD(const lldb::SBQueue &,
+ SBQueue, operator=,(const lldb::SBQueue &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBQueue, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBQueue, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBQueue, Clear, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::queue_id_t, SBQueue, GetQueueID, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBQueue, GetIndexID, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBQueue, GetName, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBQueue, GetNumThreads, ());
+ LLDB_REGISTER_METHOD(lldb::SBThread, SBQueue, GetThreadAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBQueue, GetNumPendingItems, ());
+ LLDB_REGISTER_METHOD(lldb::SBQueueItem, SBQueue, GetPendingItemAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBQueue, GetNumRunningItems, ());
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBQueue, GetProcess, ());
+ LLDB_REGISTER_METHOD(lldb::QueueKind, SBQueue, GetKind, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBQueueItem.cpp b/contrib/llvm-project/lldb/source/API/SBQueueItem.cpp
new file mode 100644
index 000000000000..5f2cbd1bdbfb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBQueueItem.cpp
@@ -0,0 +1,140 @@
+//===-- SBQueueItem.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-forward.h"
+
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBQueueItem.h"
+#include "lldb/API/SBThread.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/QueueItem.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Constructors
+SBQueueItem::SBQueueItem() : m_queue_item_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBQueueItem);
+}
+
+SBQueueItem::SBQueueItem(const QueueItemSP &queue_item_sp)
+ : m_queue_item_sp(queue_item_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBQueueItem, (const lldb::QueueItemSP &),
+ queue_item_sp);
+}
+
+// Destructor
+SBQueueItem::~SBQueueItem() { m_queue_item_sp.reset(); }
+
+bool SBQueueItem::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBQueueItem, IsValid);
+ return this->operator bool();
+}
+SBQueueItem::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBQueueItem, operator bool);
+
+ return m_queue_item_sp.get() != nullptr;
+}
+
+void SBQueueItem::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBQueueItem, Clear);
+
+ m_queue_item_sp.reset();
+}
+
+void SBQueueItem::SetQueueItem(const QueueItemSP &queue_item_sp) {
+ LLDB_RECORD_METHOD(void, SBQueueItem, SetQueueItem,
+ (const lldb::QueueItemSP &), queue_item_sp);
+
+ m_queue_item_sp = queue_item_sp;
+}
+
+lldb::QueueItemKind SBQueueItem::GetKind() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::QueueItemKind, SBQueueItem, GetKind);
+
+ QueueItemKind result = eQueueItemKindUnknown;
+ if (m_queue_item_sp) {
+ result = m_queue_item_sp->GetKind();
+ }
+ return result;
+}
+
+void SBQueueItem::SetKind(lldb::QueueItemKind kind) {
+ LLDB_RECORD_METHOD(void, SBQueueItem, SetKind, (lldb::QueueItemKind), kind);
+
+ if (m_queue_item_sp) {
+ m_queue_item_sp->SetKind(kind);
+ }
+}
+
+SBAddress SBQueueItem::GetAddress() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBAddress, SBQueueItem, GetAddress);
+
+ SBAddress result;
+ if (m_queue_item_sp) {
+ result.SetAddress(&m_queue_item_sp->GetAddress());
+ }
+ return LLDB_RECORD_RESULT(result);
+}
+
+void SBQueueItem::SetAddress(SBAddress addr) {
+ LLDB_RECORD_METHOD(void, SBQueueItem, SetAddress, (lldb::SBAddress), addr);
+
+ if (m_queue_item_sp) {
+ m_queue_item_sp->SetAddress(addr.ref());
+ }
+}
+
+SBThread SBQueueItem::GetExtendedBacktraceThread(const char *type) {
+ LLDB_RECORD_METHOD(lldb::SBThread, SBQueueItem, GetExtendedBacktraceThread,
+ (const char *), type);
+
+ SBThread result;
+ if (m_queue_item_sp) {
+ ProcessSP process_sp = m_queue_item_sp->GetProcessSP();
+ Process::StopLocker stop_locker;
+ if (process_sp && stop_locker.TryLock(&process_sp->GetRunLock())) {
+ ThreadSP thread_sp;
+ ConstString type_const(type);
+ thread_sp = m_queue_item_sp->GetExtendedBacktraceThread(type_const);
+ if (thread_sp) {
+ // Save this in the Process' ExtendedThreadList so a strong pointer
+ // retains the object
+ process_sp->GetExtendedThreadList().AddThread(thread_sp);
+ result.SetThread(thread_sp);
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(result);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBQueueItem>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBQueueItem, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBQueueItem, (const lldb::QueueItemSP &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBQueueItem, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBQueueItem, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBQueueItem, Clear, ());
+ LLDB_REGISTER_METHOD(void, SBQueueItem, SetQueueItem,
+ (const lldb::QueueItemSP &));
+ LLDB_REGISTER_METHOD_CONST(lldb::QueueItemKind, SBQueueItem, GetKind, ());
+ LLDB_REGISTER_METHOD(void, SBQueueItem, SetKind, (lldb::QueueItemKind));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBAddress, SBQueueItem, GetAddress, ());
+ LLDB_REGISTER_METHOD(void, SBQueueItem, SetAddress, (lldb::SBAddress));
+ LLDB_REGISTER_METHOD(lldb::SBThread, SBQueueItem,
+ GetExtendedBacktraceThread, (const char *));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBReproducer.cpp b/contrib/llvm-project/lldb/source/API/SBReproducer.cpp
new file mode 100644
index 000000000000..439ee5a70460
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBReproducer.cpp
@@ -0,0 +1,153 @@
+//===-- SBReproducer.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SBReproducerPrivate.h"
+
+#include "SBReproducerPrivate.h"
+#include "lldb/API/LLDB.h"
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBAttachInfo.h"
+#include "lldb/API/SBBlock.h"
+#include "lldb/API/SBBreakpoint.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBData.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBDeclaration.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBHostOS.h"
+#include "lldb/API/SBReproducer.h"
+
+#include "lldb/Host/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::repro;
+
+SBRegistry::SBRegistry() {
+ Registry& R = *this;
+
+ RegisterMethods<SBAddress>(R);
+ RegisterMethods<SBAttachInfo>(R);
+ RegisterMethods<SBBlock>(R);
+ RegisterMethods<SBBreakpoint>(R);
+ RegisterMethods<SBBreakpointList>(R);
+ RegisterMethods<SBBreakpointLocation>(R);
+ RegisterMethods<SBBreakpointName>(R);
+ RegisterMethods<SBBroadcaster>(R);
+ RegisterMethods<SBCommandInterpreterRunOptions>(R);
+ RegisterMethods<SBCommandReturnObject>(R);
+ RegisterMethods<SBCommunication>(R);
+ RegisterMethods<SBCompileUnit>(R);
+ RegisterMethods<SBData>(R);
+ RegisterMethods<SBInputReader>(R);
+ RegisterMethods<SBDebugger>(R);
+ RegisterMethods<SBDeclaration>(R);
+ RegisterMethods<SBError>(R);
+ RegisterMethods<SBEvent>(R);
+ RegisterMethods<SBExecutionContext>(R);
+ RegisterMethods<SBExpressionOptions>(R);
+ RegisterMethods<SBFileSpec>(R);
+ RegisterMethods<SBFileSpecList>(R);
+ RegisterMethods<SBFrame>(R);
+ RegisterMethods<SBFunction>(R);
+ RegisterMethods<SBHostOS>(R);
+ RegisterMethods<SBInstruction>(R);
+ RegisterMethods<SBInstructionList>(R);
+ RegisterMethods<SBLanguageRuntime>(R);
+ RegisterMethods<SBLaunchInfo>(R);
+ RegisterMethods<SBLineEntry>(R);
+ RegisterMethods<SBListener>(R);
+ RegisterMethods<SBMemoryRegionInfo>(R);
+ RegisterMethods<SBMemoryRegionInfoList>(R);
+ RegisterMethods<SBModule>(R);
+ RegisterMethods<SBModuleSpec>(R);
+ RegisterMethods<SBPlatformConnectOptions>(R);
+ RegisterMethods<SBPlatformShellCommand>(R);
+ RegisterMethods<SBPlatform>(R);
+ RegisterMethods<SBProcess>(R);
+ RegisterMethods<SBProcessInfo>(R);
+ RegisterMethods<SBQueue>(R);
+ RegisterMethods<SBQueueItem>(R);
+ RegisterMethods<SBSection>(R);
+ RegisterMethods<SBSourceManager>(R);
+ RegisterMethods<SBStream>(R);
+ RegisterMethods<SBStringList>(R);
+ RegisterMethods<SBStructuredData>(R);
+ RegisterMethods<SBSymbol>(R);
+ RegisterMethods<SBSymbolContext>(R);
+ RegisterMethods<SBSymbolContextList>(R);
+ RegisterMethods<SBTarget>(R);
+ RegisterMethods<SBThread>(R);
+ RegisterMethods<SBThreadCollection>(R);
+ RegisterMethods<SBThreadPlan>(R);
+ RegisterMethods<SBTrace>(R);
+ RegisterMethods<SBTraceOptions>(R);
+ RegisterMethods<SBType>(R);
+ RegisterMethods<SBTypeCategory>(R);
+ RegisterMethods<SBTypeEnumMember>(R);
+ RegisterMethods<SBTypeFilter>(R);
+ RegisterMethods<SBTypeFormat>(R);
+ RegisterMethods<SBTypeNameSpecifier>(R);
+ RegisterMethods<SBTypeSummaryOptions>(R);
+ RegisterMethods<SBTypeSummary>(R);
+ RegisterMethods<SBTypeSynthetic>(R);
+ RegisterMethods<SBUnixSignals>(R);
+ RegisterMethods<SBValue>(R);
+ RegisterMethods<SBValueList>(R);
+ RegisterMethods<SBVariablesOptions>(R);
+ RegisterMethods<SBWatchpoint>(R);
+}
+
+const char *SBReproducer::Capture() {
+ static std::string error;
+ if (auto e = Reproducer::Initialize(ReproducerMode::Capture, llvm::None)) {
+ error = llvm::toString(std::move(e));
+ return error.c_str();
+ }
+ return nullptr;
+}
+
+const char *SBReproducer::Capture(const char *path) {
+ static std::string error;
+ if (auto e =
+ Reproducer::Initialize(ReproducerMode::Capture, FileSpec(path))) {
+ error = llvm::toString(std::move(e));
+ return error.c_str();
+ }
+ return nullptr;
+}
+
+const char *SBReproducer::Replay(const char *path) {
+ static std::string error;
+ if (auto e = Reproducer::Initialize(ReproducerMode::Replay, FileSpec(path))) {
+ error = llvm::toString(std::move(e));
+ return error.c_str();
+ }
+
+ repro::Loader *loader = repro::Reproducer::Instance().GetLoader();
+ if (!loader) {
+ error = "unable to get replay loader.";
+ return error.c_str();
+ }
+
+ FileSpec file = loader->GetFile<SBProvider::Info>();
+ if (!file) {
+ error = "unable to get replay data from reproducer.";
+ return error.c_str();
+ }
+
+ SBRegistry registry;
+ registry.Replay(file);
+
+ return nullptr;
+}
+
+char lldb_private::repro::SBProvider::ID = 0;
+const char *SBProvider::Info::name = "sbapi";
+const char *SBProvider::Info::file = "sbapi.bin";
diff --git a/contrib/llvm-project/lldb/source/API/SBReproducerPrivate.h b/contrib/llvm-project/lldb/source/API/SBReproducerPrivate.h
new file mode 100644
index 000000000000..84b6ce967c0b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBReproducerPrivate.h
@@ -0,0 +1,75 @@
+//===-- SBReproducerPrivate.h -----------------------------------*- C++ -*-===//
+//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_API_SBREPRODUCER_PRIVATE_H
+#define LLDB_API_SBREPRODUCER_PRIVATE_H
+
+#include "lldb/API/SBReproducer.h"
+
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Utility/ReproducerInstrumentation.h"
+
+#include "llvm/ADT/DenseMap.h"
+
+#define LLDB_GET_INSTRUMENTATION_DATA() \
+ lldb_private::repro::GetInstrumentationData()
+
+namespace lldb_private {
+namespace repro {
+
+class SBRegistry : public Registry {
+public:
+ SBRegistry();
+};
+
+class SBProvider : public Provider<SBProvider> {
+public:
+ struct Info {
+ static const char *name;
+ static const char *file;
+ };
+
+ SBProvider(const FileSpec &directory)
+ : Provider(directory),
+ m_stream(directory.CopyByAppendingPathComponent("sbapi.bin").GetPath(),
+ m_ec, llvm::sys::fs::OpenFlags::F_None),
+ m_serializer(m_stream) {}
+
+ Serializer &GetSerializer() { return m_serializer; }
+ Registry &GetRegistry() { return m_registry; }
+
+ static char ID;
+
+private:
+ std::error_code m_ec;
+ llvm::raw_fd_ostream m_stream;
+ Serializer m_serializer;
+ SBRegistry m_registry;
+};
+
+inline InstrumentationData GetInstrumentationData() {
+ if (!lldb_private::repro::Reproducer::Initialized())
+ return {};
+
+ if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {
+ auto &p = g->GetOrCreate<SBProvider>();
+ return {p.GetSerializer(), p.GetRegistry()};
+ }
+
+ return {};
+}
+
+template <typename T> void RegisterMethods(Registry &R);
+
+} // namespace repro
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/API/SBSection.cpp b/contrib/llvm-project/lldb/source/API/SBSection.cpp
new file mode 100644
index 000000000000..14e1e14f59aa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBSection.cpp
@@ -0,0 +1,328 @@
+//===-- SBSection.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBSection.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBSection::SBSection() : m_opaque_wp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBSection);
+}
+
+SBSection::SBSection(const SBSection &rhs) : m_opaque_wp(rhs.m_opaque_wp) {
+ LLDB_RECORD_CONSTRUCTOR(SBSection, (const lldb::SBSection &), rhs);
+}
+
+SBSection::SBSection(const lldb::SectionSP &section_sp)
+ : m_opaque_wp() // Don't init with section_sp otherwise this will throw if
+ // section_sp doesn't contain a valid Section *
+{
+ if (section_sp)
+ m_opaque_wp = section_sp;
+}
+
+const SBSection &SBSection::operator=(const SBSection &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBSection &,
+ SBSection, operator=,(const lldb::SBSection &), rhs);
+
+ m_opaque_wp = rhs.m_opaque_wp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBSection::~SBSection() {}
+
+bool SBSection::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBSection, IsValid);
+ return this->operator bool();
+}
+SBSection::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBSection, operator bool);
+
+ SectionSP section_sp(GetSP());
+ return section_sp && section_sp->GetModule().get() != nullptr;
+}
+
+const char *SBSection::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBSection, GetName);
+
+ SectionSP section_sp(GetSP());
+ if (section_sp)
+ return section_sp->GetName().GetCString();
+ return nullptr;
+}
+
+lldb::SBSection SBSection::GetParent() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBSection, SBSection, GetParent);
+
+ lldb::SBSection sb_section;
+ SectionSP section_sp(GetSP());
+ if (section_sp) {
+ SectionSP parent_section_sp(section_sp->GetParent());
+ if (parent_section_sp)
+ sb_section.SetSP(parent_section_sp);
+ }
+ return LLDB_RECORD_RESULT(sb_section);
+}
+
+lldb::SBSection SBSection::FindSubSection(const char *sect_name) {
+ LLDB_RECORD_METHOD(lldb::SBSection, SBSection, FindSubSection, (const char *),
+ sect_name);
+
+ lldb::SBSection sb_section;
+ if (sect_name) {
+ SectionSP section_sp(GetSP());
+ if (section_sp) {
+ ConstString const_sect_name(sect_name);
+ sb_section.SetSP(
+ section_sp->GetChildren().FindSectionByName(const_sect_name));
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_section);
+}
+
+size_t SBSection::GetNumSubSections() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBSection, GetNumSubSections);
+
+ SectionSP section_sp(GetSP());
+ if (section_sp)
+ return section_sp->GetChildren().GetSize();
+ return 0;
+}
+
+lldb::SBSection SBSection::GetSubSectionAtIndex(size_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBSection, SBSection, GetSubSectionAtIndex, (size_t),
+ idx);
+
+ lldb::SBSection sb_section;
+ SectionSP section_sp(GetSP());
+ if (section_sp)
+ sb_section.SetSP(section_sp->GetChildren().GetSectionAtIndex(idx));
+ return LLDB_RECORD_RESULT(sb_section);
+}
+
+lldb::SectionSP SBSection::GetSP() const { return m_opaque_wp.lock(); }
+
+void SBSection::SetSP(const lldb::SectionSP &section_sp) {
+ m_opaque_wp = section_sp;
+}
+
+lldb::addr_t SBSection::GetFileAddress() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBSection, GetFileAddress);
+
+ lldb::addr_t file_addr = LLDB_INVALID_ADDRESS;
+ SectionSP section_sp(GetSP());
+ if (section_sp)
+ return section_sp->GetFileAddress();
+ return file_addr;
+}
+
+lldb::addr_t SBSection::GetLoadAddress(lldb::SBTarget &sb_target) {
+ LLDB_RECORD_METHOD(lldb::addr_t, SBSection, GetLoadAddress,
+ (lldb::SBTarget &), sb_target);
+
+ TargetSP target_sp(sb_target.GetSP());
+ if (target_sp) {
+ SectionSP section_sp(GetSP());
+ if (section_sp)
+ return section_sp->GetLoadBaseAddress(target_sp.get());
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+lldb::addr_t SBSection::GetByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBSection, GetByteSize);
+
+ SectionSP section_sp(GetSP());
+ if (section_sp)
+ return section_sp->GetByteSize();
+ return 0;
+}
+
+uint64_t SBSection::GetFileOffset() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint64_t, SBSection, GetFileOffset);
+
+ SectionSP section_sp(GetSP());
+ if (section_sp) {
+ ModuleSP module_sp(section_sp->GetModule());
+ if (module_sp) {
+ ObjectFile *objfile = module_sp->GetObjectFile();
+ if (objfile)
+ return objfile->GetFileOffset() + section_sp->GetFileOffset();
+ }
+ }
+ return UINT64_MAX;
+}
+
+uint64_t SBSection::GetFileByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint64_t, SBSection, GetFileByteSize);
+
+ SectionSP section_sp(GetSP());
+ if (section_sp)
+ return section_sp->GetFileSize();
+ return 0;
+}
+
+SBData SBSection::GetSectionData() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBData, SBSection, GetSectionData);
+
+ return LLDB_RECORD_RESULT(GetSectionData(0, UINT64_MAX));
+}
+
+SBData SBSection::GetSectionData(uint64_t offset, uint64_t size) {
+ LLDB_RECORD_METHOD(lldb::SBData, SBSection, GetSectionData,
+ (uint64_t, uint64_t), offset, size);
+
+ SBData sb_data;
+ SectionSP section_sp(GetSP());
+ if (section_sp) {
+ const uint64_t sect_file_size = section_sp->GetFileSize();
+ if (sect_file_size > 0) {
+ ModuleSP module_sp(section_sp->GetModule());
+ if (module_sp) {
+ ObjectFile *objfile = module_sp->GetObjectFile();
+ if (objfile) {
+ const uint64_t sect_file_offset =
+ objfile->GetFileOffset() + section_sp->GetFileOffset();
+ const uint64_t file_offset = sect_file_offset + offset;
+ uint64_t file_size = size;
+ if (file_size == UINT64_MAX) {
+ file_size = section_sp->GetByteSize();
+ if (file_size > offset)
+ file_size -= offset;
+ else
+ file_size = 0;
+ }
+ auto data_buffer_sp = FileSystem::Instance().CreateDataBuffer(
+ objfile->GetFileSpec().GetPath(), file_size, file_offset);
+ if (data_buffer_sp && data_buffer_sp->GetByteSize() > 0) {
+ DataExtractorSP data_extractor_sp(
+ new DataExtractor(data_buffer_sp, objfile->GetByteOrder(),
+ objfile->GetAddressByteSize()));
+
+ sb_data.SetOpaque(data_extractor_sp);
+ }
+ }
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_data);
+}
+
+SectionType SBSection::GetSectionType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SectionType, SBSection, GetSectionType);
+
+ SectionSP section_sp(GetSP());
+ if (section_sp.get())
+ return section_sp->GetType();
+ return eSectionTypeInvalid;
+}
+
+uint32_t SBSection::GetPermissions() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBSection, GetPermissions);
+
+ SectionSP section_sp(GetSP());
+ if (section_sp)
+ return section_sp->GetPermissions();
+ return 0;
+}
+
+uint32_t SBSection::GetTargetByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBSection, GetTargetByteSize);
+
+ SectionSP section_sp(GetSP());
+ if (section_sp.get())
+ return section_sp->GetTargetByteSize();
+ return 0;
+}
+
+bool SBSection::operator==(const SBSection &rhs) {
+ LLDB_RECORD_METHOD(bool, SBSection, operator==,(const lldb::SBSection &),
+ rhs);
+
+ SectionSP lhs_section_sp(GetSP());
+ SectionSP rhs_section_sp(rhs.GetSP());
+ if (lhs_section_sp && rhs_section_sp)
+ return lhs_section_sp == rhs_section_sp;
+ return false;
+}
+
+bool SBSection::operator!=(const SBSection &rhs) {
+ LLDB_RECORD_METHOD(bool, SBSection, operator!=,(const lldb::SBSection &),
+ rhs);
+
+ SectionSP lhs_section_sp(GetSP());
+ SectionSP rhs_section_sp(rhs.GetSP());
+ return lhs_section_sp != rhs_section_sp;
+}
+
+bool SBSection::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBSection, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ SectionSP section_sp(GetSP());
+ if (section_sp) {
+ const addr_t file_addr = section_sp->GetFileAddress();
+ strm.Printf("[0x%16.16" PRIx64 "-0x%16.16" PRIx64 ") ", file_addr,
+ file_addr + section_sp->GetByteSize());
+ section_sp->DumpName(&strm);
+ } else {
+ strm.PutCString("No value");
+ }
+
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBSection>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBSection, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBSection, (const lldb::SBSection &));
+ LLDB_REGISTER_METHOD(const lldb::SBSection &,
+ SBSection, operator=,(const lldb::SBSection &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBSection, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBSection, operator bool, ());
+ LLDB_REGISTER_METHOD(const char *, SBSection, GetName, ());
+ LLDB_REGISTER_METHOD(lldb::SBSection, SBSection, GetParent, ());
+ LLDB_REGISTER_METHOD(lldb::SBSection, SBSection, FindSubSection,
+ (const char *));
+ LLDB_REGISTER_METHOD(size_t, SBSection, GetNumSubSections, ());
+ LLDB_REGISTER_METHOD(lldb::SBSection, SBSection, GetSubSectionAtIndex,
+ (size_t));
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBSection, GetFileAddress, ());
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBSection, GetLoadAddress,
+ (lldb::SBTarget &));
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBSection, GetByteSize, ());
+ LLDB_REGISTER_METHOD(uint64_t, SBSection, GetFileOffset, ());
+ LLDB_REGISTER_METHOD(uint64_t, SBSection, GetFileByteSize, ());
+ LLDB_REGISTER_METHOD(lldb::SBData, SBSection, GetSectionData, ());
+ LLDB_REGISTER_METHOD(lldb::SBData, SBSection, GetSectionData,
+ (uint64_t, uint64_t));
+ LLDB_REGISTER_METHOD(lldb::SectionType, SBSection, GetSectionType, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBSection, GetPermissions, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBSection, GetTargetByteSize, ());
+ LLDB_REGISTER_METHOD(bool, SBSection, operator==,(const lldb::SBSection &));
+ LLDB_REGISTER_METHOD(bool, SBSection, operator!=,(const lldb::SBSection &));
+ LLDB_REGISTER_METHOD(bool, SBSection, GetDescription, (lldb::SBStream &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBSourceManager.cpp b/contrib/llvm-project/lldb/source/API/SBSourceManager.cpp
new file mode 100644
index 000000000000..9c4ce3c7f4e3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBSourceManager.cpp
@@ -0,0 +1,164 @@
+//===-- SBSourceManager.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBSourceManager.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBTarget.h"
+
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Utility/Stream.h"
+
+#include "lldb/Target/Target.h"
+
+namespace lldb_private {
+class SourceManagerImpl {
+public:
+ SourceManagerImpl(const lldb::DebuggerSP &debugger_sp)
+ : m_debugger_wp(debugger_sp), m_target_wp() {}
+
+ SourceManagerImpl(const lldb::TargetSP &target_sp)
+ : m_debugger_wp(), m_target_wp(target_sp) {}
+
+ SourceManagerImpl(const SourceManagerImpl &rhs) {
+ if (&rhs == this)
+ return;
+ m_debugger_wp = rhs.m_debugger_wp;
+ m_target_wp = rhs.m_target_wp;
+ }
+
+ size_t DisplaySourceLinesWithLineNumbers(const lldb_private::FileSpec &file,
+ uint32_t line, uint32_t column,
+ uint32_t context_before,
+ uint32_t context_after,
+ const char *current_line_cstr,
+ lldb_private::Stream *s) {
+ if (!file)
+ return 0;
+
+ lldb::TargetSP target_sp(m_target_wp.lock());
+ if (target_sp) {
+ return target_sp->GetSourceManager().DisplaySourceLinesWithLineNumbers(
+ file, line, column, context_before, context_after, current_line_cstr,
+ s);
+ } else {
+ lldb::DebuggerSP debugger_sp(m_debugger_wp.lock());
+ if (debugger_sp) {
+ return debugger_sp->GetSourceManager()
+ .DisplaySourceLinesWithLineNumbers(file, line, column,
+ context_before, context_after,
+ current_line_cstr, s);
+ }
+ }
+ return 0;
+ }
+
+private:
+ lldb::DebuggerWP m_debugger_wp;
+ lldb::TargetWP m_target_wp;
+};
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBSourceManager::SBSourceManager(const SBDebugger &debugger) {
+ LLDB_RECORD_CONSTRUCTOR(SBSourceManager, (const lldb::SBDebugger &),
+ debugger);
+
+ m_opaque_up.reset(new SourceManagerImpl(debugger.get_sp()));
+}
+
+SBSourceManager::SBSourceManager(const SBTarget &target) {
+ LLDB_RECORD_CONSTRUCTOR(SBSourceManager, (const lldb::SBTarget &), target);
+
+ m_opaque_up.reset(new SourceManagerImpl(target.GetSP()));
+}
+
+SBSourceManager::SBSourceManager(const SBSourceManager &rhs) {
+ LLDB_RECORD_CONSTRUCTOR(SBSourceManager, (const lldb::SBSourceManager &),
+ rhs);
+
+ if (&rhs == this)
+ return;
+
+ m_opaque_up.reset(new SourceManagerImpl(*(rhs.m_opaque_up.get())));
+}
+
+const lldb::SBSourceManager &SBSourceManager::
+operator=(const lldb::SBSourceManager &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBSourceManager &,
+ SBSourceManager, operator=,(const lldb::SBSourceManager &),
+ rhs);
+
+ m_opaque_up.reset(new SourceManagerImpl(*(rhs.m_opaque_up.get())));
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBSourceManager::~SBSourceManager() {}
+
+size_t SBSourceManager::DisplaySourceLinesWithLineNumbers(
+ const SBFileSpec &file, uint32_t line, uint32_t context_before,
+ uint32_t context_after, const char *current_line_cstr, SBStream &s) {
+ LLDB_RECORD_METHOD(size_t, SBSourceManager, DisplaySourceLinesWithLineNumbers,
+ (const lldb::SBFileSpec &, uint32_t, uint32_t, uint32_t,
+ const char *, lldb::SBStream &),
+ file, line, context_before, context_after,
+ current_line_cstr, s);
+
+ const uint32_t column = 0;
+ return DisplaySourceLinesWithLineNumbersAndColumn(
+ file.ref(), line, column, context_before, context_after,
+ current_line_cstr, s);
+}
+
+size_t SBSourceManager::DisplaySourceLinesWithLineNumbersAndColumn(
+ const SBFileSpec &file, uint32_t line, uint32_t column,
+ uint32_t context_before, uint32_t context_after,
+ const char *current_line_cstr, SBStream &s) {
+ LLDB_RECORD_METHOD(
+ size_t, SBSourceManager, DisplaySourceLinesWithLineNumbersAndColumn,
+ (const lldb::SBFileSpec &, uint32_t, uint32_t, uint32_t, uint32_t,
+ const char *, lldb::SBStream &),
+ file, line, column, context_before, context_after, current_line_cstr, s);
+
+ if (m_opaque_up == nullptr)
+ return 0;
+
+ return m_opaque_up->DisplaySourceLinesWithLineNumbers(
+ file.ref(), line, column, context_before, context_after,
+ current_line_cstr, s.get());
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBSourceManager>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBSourceManager, (const lldb::SBDebugger &));
+ LLDB_REGISTER_CONSTRUCTOR(SBSourceManager, (const lldb::SBTarget &));
+ LLDB_REGISTER_CONSTRUCTOR(SBSourceManager, (const lldb::SBSourceManager &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBSourceManager &,
+ SBSourceManager, operator=,(const lldb::SBSourceManager &));
+ LLDB_REGISTER_METHOD(size_t, SBSourceManager,
+ DisplaySourceLinesWithLineNumbers,
+ (const lldb::SBFileSpec &, uint32_t, uint32_t,
+ uint32_t, const char *, lldb::SBStream &));
+ LLDB_REGISTER_METHOD(size_t, SBSourceManager,
+ DisplaySourceLinesWithLineNumbersAndColumn,
+ (const lldb::SBFileSpec &, uint32_t, uint32_t,
+ uint32_t, uint32_t, const char *, lldb::SBStream &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBStream.cpp b/contrib/llvm-project/lldb/source/API/SBStream.cpp
new file mode 100644
index 000000000000..ae652338e1ea
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBStream.cpp
@@ -0,0 +1,198 @@
+//===-- SBStream.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBStream.h"
+
+#include "SBReproducerPrivate.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBStream::SBStream() : m_opaque_up(new StreamString()), m_is_file(false) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBStream);
+}
+
+SBStream::SBStream(SBStream &&rhs)
+ : m_opaque_up(std::move(rhs.m_opaque_up)), m_is_file(rhs.m_is_file) {}
+
+SBStream::~SBStream() {}
+
+bool SBStream::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBStream, IsValid);
+ return this->operator bool();
+}
+SBStream::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBStream, operator bool);
+
+ return (m_opaque_up != nullptr);
+}
+
+// If this stream is not redirected to a file, it will maintain a local cache
+// for the stream data which can be accessed using this accessor.
+const char *SBStream::GetData() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBStream, GetData);
+
+ if (m_is_file || m_opaque_up == nullptr)
+ return nullptr;
+
+ return static_cast<StreamString *>(m_opaque_up.get())->GetData();
+}
+
+// If this stream is not redirected to a file, it will maintain a local cache
+// for the stream output whose length can be accessed using this accessor.
+size_t SBStream::GetSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBStream, GetSize);
+
+ if (m_is_file || m_opaque_up == nullptr)
+ return 0;
+
+ return static_cast<StreamString *>(m_opaque_up.get())->GetSize();
+}
+
+void SBStream::Printf(const char *format, ...) {
+ if (!format)
+ return;
+ va_list args;
+ va_start(args, format);
+ ref().PrintfVarArg(format, args);
+ va_end(args);
+}
+
+void SBStream::RedirectToFile(const char *path, bool append) {
+ LLDB_RECORD_METHOD(void, SBStream, RedirectToFile, (const char *, bool), path,
+ append);
+
+ if (path == nullptr)
+ return;
+
+ std::string local_data;
+ if (m_opaque_up) {
+ // See if we have any locally backed data. If so, copy it so we can then
+ // redirect it to the file so we don't lose the data
+ if (!m_is_file)
+ local_data = static_cast<StreamString *>(m_opaque_up.get())->GetString();
+ }
+ StreamFile *stream_file = new StreamFile;
+ uint32_t open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate;
+ if (append)
+ open_options |= File::eOpenOptionAppend;
+ else
+ open_options |= File::eOpenOptionTruncate;
+
+ FileSystem::Instance().Open(stream_file->GetFile(), FileSpec(path),
+ open_options);
+ m_opaque_up.reset(stream_file);
+
+ if (m_opaque_up) {
+ m_is_file = true;
+
+ // If we had any data locally in our StreamString, then pass that along to
+ // the to new file we are redirecting to.
+ if (!local_data.empty())
+ m_opaque_up->Write(&local_data[0], local_data.size());
+ } else
+ m_is_file = false;
+}
+
+void SBStream::RedirectToFileHandle(FILE *fh, bool transfer_fh_ownership) {
+ LLDB_RECORD_METHOD(void, SBStream, RedirectToFileHandle, (FILE *, bool), fh,
+ transfer_fh_ownership);
+
+ if (fh == nullptr)
+ return;
+
+ std::string local_data;
+ if (m_opaque_up) {
+ // See if we have any locally backed data. If so, copy it so we can then
+ // redirect it to the file so we don't lose the data
+ if (!m_is_file)
+ local_data = static_cast<StreamString *>(m_opaque_up.get())->GetString();
+ }
+ m_opaque_up.reset(new StreamFile(fh, transfer_fh_ownership));
+
+ if (m_opaque_up) {
+ m_is_file = true;
+
+ // If we had any data locally in our StreamString, then pass that along to
+ // the to new file we are redirecting to.
+ if (!local_data.empty())
+ m_opaque_up->Write(&local_data[0], local_data.size());
+ } else
+ m_is_file = false;
+}
+
+void SBStream::RedirectToFileDescriptor(int fd, bool transfer_fh_ownership) {
+ LLDB_RECORD_METHOD(void, SBStream, RedirectToFileDescriptor, (int, bool), fd,
+ transfer_fh_ownership);
+
+ std::string local_data;
+ if (m_opaque_up) {
+ // See if we have any locally backed data. If so, copy it so we can then
+ // redirect it to the file so we don't lose the data
+ if (!m_is_file)
+ local_data = static_cast<StreamString *>(m_opaque_up.get())->GetString();
+ }
+
+ m_opaque_up.reset(new StreamFile(::fdopen(fd, "w"), transfer_fh_ownership));
+ if (m_opaque_up) {
+ m_is_file = true;
+
+ // If we had any data locally in our StreamString, then pass that along to
+ // the to new file we are redirecting to.
+ if (!local_data.empty())
+ m_opaque_up->Write(&local_data[0], local_data.size());
+ } else
+ m_is_file = false;
+}
+
+lldb_private::Stream *SBStream::operator->() { return m_opaque_up.get(); }
+
+lldb_private::Stream *SBStream::get() { return m_opaque_up.get(); }
+
+lldb_private::Stream &SBStream::ref() {
+ if (m_opaque_up == nullptr)
+ m_opaque_up.reset(new StreamString());
+ return *m_opaque_up;
+}
+
+void SBStream::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBStream, Clear);
+
+ if (m_opaque_up) {
+ // See if we have any locally backed data. If so, copy it so we can then
+ // redirect it to the file so we don't lose the data
+ if (m_is_file)
+ m_opaque_up.reset();
+ else
+ static_cast<StreamString *>(m_opaque_up.get())->Clear();
+ }
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBStream>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBStream, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBStream, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBStream, operator bool, ());
+ LLDB_REGISTER_METHOD(const char *, SBStream, GetData, ());
+ LLDB_REGISTER_METHOD(size_t, SBStream, GetSize, ());
+ LLDB_REGISTER_METHOD(void, SBStream, RedirectToFile, (const char *, bool));
+ LLDB_REGISTER_METHOD(void, SBStream, RedirectToFileHandle, (FILE *, bool));
+ LLDB_REGISTER_METHOD(void, SBStream, RedirectToFileDescriptor, (int, bool));
+ LLDB_REGISTER_METHOD(void, SBStream, Clear, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBStringList.cpp b/contrib/llvm-project/lldb/source/API/SBStringList.cpp
new file mode 100644
index 000000000000..2f8bd55855a1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBStringList.cpp
@@ -0,0 +1,163 @@
+//===-- SBStringList.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBStringList.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/Utility/StringList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBStringList::SBStringList() : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBStringList);
+}
+
+SBStringList::SBStringList(const lldb_private::StringList *lldb_strings_ptr)
+ : m_opaque_up() {
+ if (lldb_strings_ptr)
+ m_opaque_up = llvm::make_unique<StringList>(*lldb_strings_ptr);
+}
+
+SBStringList::SBStringList(const SBStringList &rhs) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBStringList, (const lldb::SBStringList &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+const SBStringList &SBStringList::operator=(const SBStringList &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBStringList &,
+ SBStringList, operator=,(const lldb::SBStringList &), rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBStringList::~SBStringList() {}
+
+const lldb_private::StringList *SBStringList::operator->() const {
+ return m_opaque_up.get();
+}
+
+const lldb_private::StringList &SBStringList::operator*() const {
+ return *m_opaque_up;
+}
+
+bool SBStringList::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBStringList, IsValid);
+ return this->operator bool();
+}
+SBStringList::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBStringList, operator bool);
+
+ return (m_opaque_up != nullptr);
+}
+
+void SBStringList::AppendString(const char *str) {
+ LLDB_RECORD_METHOD(void, SBStringList, AppendString, (const char *), str);
+
+ if (str != nullptr) {
+ if (IsValid())
+ m_opaque_up->AppendString(str);
+ else
+ m_opaque_up.reset(new lldb_private::StringList(str));
+ }
+}
+
+void SBStringList::AppendList(const char **strv, int strc) {
+ LLDB_RECORD_METHOD(void, SBStringList, AppendList, (const char **, int), strv,
+ strc);
+
+ if ((strv != nullptr) && (strc > 0)) {
+ if (IsValid())
+ m_opaque_up->AppendList(strv, strc);
+ else
+ m_opaque_up.reset(new lldb_private::StringList(strv, strc));
+ }
+}
+
+void SBStringList::AppendList(const SBStringList &strings) {
+ LLDB_RECORD_METHOD(void, SBStringList, AppendList,
+ (const lldb::SBStringList &), strings);
+
+ if (strings.IsValid()) {
+ if (!IsValid())
+ m_opaque_up.reset(new lldb_private::StringList());
+ m_opaque_up->AppendList(*(strings.m_opaque_up));
+ }
+}
+
+void SBStringList::AppendList(const StringList &strings) {
+ if (!IsValid())
+ m_opaque_up.reset(new lldb_private::StringList());
+ m_opaque_up->AppendList(strings);
+}
+
+uint32_t SBStringList::GetSize() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBStringList, GetSize);
+
+ if (IsValid()) {
+ return m_opaque_up->GetSize();
+ }
+ return 0;
+}
+
+const char *SBStringList::GetStringAtIndex(size_t idx) {
+ LLDB_RECORD_METHOD(const char *, SBStringList, GetStringAtIndex, (size_t),
+ idx);
+
+ if (IsValid()) {
+ return m_opaque_up->GetStringAtIndex(idx);
+ }
+ return nullptr;
+}
+
+const char *SBStringList::GetStringAtIndex(size_t idx) const {
+ LLDB_RECORD_METHOD_CONST(const char *, SBStringList, GetStringAtIndex,
+ (size_t), idx);
+
+ if (IsValid()) {
+ return m_opaque_up->GetStringAtIndex(idx);
+ }
+ return nullptr;
+}
+
+void SBStringList::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBStringList, Clear);
+
+ if (IsValid()) {
+ m_opaque_up->Clear();
+ }
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBStringList>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBStringList, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBStringList, (const lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(const lldb::SBStringList &,
+ SBStringList, operator=,(const lldb::SBStringList &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBStringList, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBStringList, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBStringList, AppendString, (const char *));
+ LLDB_REGISTER_METHOD(void, SBStringList, AppendList, (const char **, int));
+ LLDB_REGISTER_METHOD(void, SBStringList, AppendList,
+ (const lldb::SBStringList &));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBStringList, GetSize, ());
+ LLDB_REGISTER_METHOD(const char *, SBStringList, GetStringAtIndex,
+ (size_t));
+ LLDB_REGISTER_METHOD_CONST(const char *, SBStringList, GetStringAtIndex,
+ (size_t));
+ LLDB_REGISTER_METHOD(void, SBStringList, Clear, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBStructuredData.cpp b/contrib/llvm-project/lldb/source/API/SBStructuredData.cpp
new file mode 100644
index 000000000000..6b973e82c858
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBStructuredData.cpp
@@ -0,0 +1,247 @@
+//===-- SBStructuredData.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBStructuredData.h"
+#include "SBReproducerPrivate.h"
+
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Target/StructuredDataPlugin.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StructuredData.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark--
+#pragma mark SBStructuredData
+
+SBStructuredData::SBStructuredData() : m_impl_up(new StructuredDataImpl()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBStructuredData);
+}
+
+SBStructuredData::SBStructuredData(const lldb::SBStructuredData &rhs)
+ : m_impl_up(new StructuredDataImpl(*rhs.m_impl_up.get())) {
+ LLDB_RECORD_CONSTRUCTOR(SBStructuredData, (const lldb::SBStructuredData &),
+ rhs);
+}
+
+SBStructuredData::SBStructuredData(const lldb::EventSP &event_sp)
+ : m_impl_up(new StructuredDataImpl(event_sp)) {
+ LLDB_RECORD_CONSTRUCTOR(SBStructuredData, (const lldb::EventSP &), event_sp);
+}
+
+SBStructuredData::SBStructuredData(lldb_private::StructuredDataImpl *impl)
+ : m_impl_up(impl) {
+ LLDB_RECORD_CONSTRUCTOR(SBStructuredData,
+ (lldb_private::StructuredDataImpl *), impl);
+}
+
+SBStructuredData::~SBStructuredData() {}
+
+SBStructuredData &SBStructuredData::
+operator=(const lldb::SBStructuredData &rhs) {
+ LLDB_RECORD_METHOD(
+ lldb::SBStructuredData &,
+ SBStructuredData, operator=,(const lldb::SBStructuredData &), rhs);
+
+ *m_impl_up = *rhs.m_impl_up;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+lldb::SBError SBStructuredData::SetFromJSON(lldb::SBStream &stream) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBStructuredData, SetFromJSON,
+ (lldb::SBStream &), stream);
+
+ lldb::SBError error;
+ std::string json_str(stream.GetData());
+
+ StructuredData::ObjectSP json_obj = StructuredData::ParseJSON(json_str);
+ m_impl_up->SetObjectSP(json_obj);
+
+ if (!json_obj || json_obj->GetType() != eStructuredDataTypeDictionary)
+ error.SetErrorString("Invalid Syntax");
+ return LLDB_RECORD_RESULT(error);
+}
+
+bool SBStructuredData::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBStructuredData, IsValid);
+ return this->operator bool();
+}
+SBStructuredData::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBStructuredData, operator bool);
+
+ return m_impl_up->IsValid();
+}
+
+void SBStructuredData::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBStructuredData, Clear);
+
+ m_impl_up->Clear();
+}
+
+SBError SBStructuredData::GetAsJSON(lldb::SBStream &stream) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBError, SBStructuredData, GetAsJSON,
+ (lldb::SBStream &), stream);
+
+ SBError error;
+ error.SetError(m_impl_up->GetAsJSON(stream.ref()));
+ return LLDB_RECORD_RESULT(error);
+}
+
+lldb::SBError SBStructuredData::GetDescription(lldb::SBStream &stream) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBError, SBStructuredData, GetDescription,
+ (lldb::SBStream &), stream);
+
+ Status error = m_impl_up->GetDescription(stream.ref());
+ SBError sb_error;
+ sb_error.SetError(error);
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+StructuredDataType SBStructuredData::GetType() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::StructuredDataType, SBStructuredData,
+ GetType);
+
+ return (m_impl_up ? m_impl_up->GetType() : eStructuredDataTypeInvalid);
+}
+
+size_t SBStructuredData::GetSize() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(size_t, SBStructuredData, GetSize);
+
+ return (m_impl_up ? m_impl_up->GetSize() : 0);
+}
+
+bool SBStructuredData::GetKeys(lldb::SBStringList &keys) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBStructuredData, GetKeys,
+ (lldb::SBStringList &), keys);
+
+ if (!m_impl_up)
+ return false;
+
+ if (GetType() != eStructuredDataTypeDictionary)
+ return false;
+
+ StructuredData::ObjectSP obj_sp = m_impl_up->GetObjectSP();
+ if (!obj_sp)
+ return false;
+
+ StructuredData::Dictionary *dict = obj_sp->GetAsDictionary();
+ // We claimed we were a dictionary, so this can't be null.
+ assert(dict);
+ // The return kind of GetKeys is an Array:
+ StructuredData::ObjectSP array_sp = dict->GetKeys();
+ StructuredData::Array *key_arr = array_sp->GetAsArray();
+ assert(key_arr);
+
+ key_arr->ForEach([&keys] (StructuredData::Object *object) -> bool {
+ llvm::StringRef key = object->GetStringValue("");
+ keys.AppendString(key.str().c_str());
+ return true;
+ });
+ return true;
+}
+
+lldb::SBStructuredData SBStructuredData::GetValueForKey(const char *key) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBStructuredData, SBStructuredData,
+ GetValueForKey, (const char *), key);
+
+ if (!m_impl_up)
+ return LLDB_RECORD_RESULT(SBStructuredData());
+
+ SBStructuredData result;
+ result.m_impl_up->SetObjectSP(m_impl_up->GetValueForKey(key));
+ return LLDB_RECORD_RESULT(result);
+}
+
+lldb::SBStructuredData SBStructuredData::GetItemAtIndex(size_t idx) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBStructuredData, SBStructuredData,
+ GetItemAtIndex, (size_t), idx);
+
+ if (!m_impl_up)
+ return LLDB_RECORD_RESULT(SBStructuredData());
+
+ SBStructuredData result;
+ result.m_impl_up->SetObjectSP(m_impl_up->GetItemAtIndex(idx));
+ return LLDB_RECORD_RESULT(result);
+}
+
+uint64_t SBStructuredData::GetIntegerValue(uint64_t fail_value) const {
+ LLDB_RECORD_METHOD_CONST(uint64_t, SBStructuredData, GetIntegerValue,
+ (uint64_t), fail_value);
+
+ return (m_impl_up ? m_impl_up->GetIntegerValue(fail_value) : fail_value);
+}
+
+double SBStructuredData::GetFloatValue(double fail_value) const {
+ LLDB_RECORD_METHOD_CONST(double, SBStructuredData, GetFloatValue, (double),
+ fail_value);
+
+ return (m_impl_up ? m_impl_up->GetFloatValue(fail_value) : fail_value);
+}
+
+bool SBStructuredData::GetBooleanValue(bool fail_value) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBStructuredData, GetBooleanValue, (bool),
+ fail_value);
+
+ return (m_impl_up ? m_impl_up->GetBooleanValue(fail_value) : fail_value);
+}
+
+size_t SBStructuredData::GetStringValue(char *dst, size_t dst_len) const {
+ LLDB_RECORD_METHOD_CONST(size_t, SBStructuredData, GetStringValue,
+ (char *, size_t), dst, dst_len);
+
+ return (m_impl_up ? m_impl_up->GetStringValue(dst, dst_len) : 0);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBStructuredData>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBStructuredData, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBStructuredData,
+ (const lldb::SBStructuredData &));
+ LLDB_REGISTER_CONSTRUCTOR(SBStructuredData, (const lldb::EventSP &));
+ LLDB_REGISTER_CONSTRUCTOR(SBStructuredData,
+ (lldb_private::StructuredDataImpl *));
+ LLDB_REGISTER_METHOD(
+ lldb::SBStructuredData &,
+ SBStructuredData, operator=,(const lldb::SBStructuredData &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBStructuredData, SetFromJSON,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBStructuredData, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBStructuredData, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBStructuredData, Clear, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBError, SBStructuredData, GetAsJSON,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBError, SBStructuredData, GetDescription,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD_CONST(lldb::StructuredDataType, SBStructuredData,
+ GetType, ());
+ LLDB_REGISTER_METHOD_CONST(size_t, SBStructuredData, GetSize, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBStructuredData, GetKeys,
+ (lldb::SBStringList &));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBStructuredData, SBStructuredData,
+ GetValueForKey, (const char *));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBStructuredData, SBStructuredData,
+ GetItemAtIndex, (size_t));
+ LLDB_REGISTER_METHOD_CONST(uint64_t, SBStructuredData, GetIntegerValue,
+ (uint64_t));
+ LLDB_REGISTER_METHOD_CONST(double, SBStructuredData, GetFloatValue,
+ (double));
+ LLDB_REGISTER_METHOD_CONST(bool, SBStructuredData, GetBooleanValue, (bool));
+ LLDB_REGISTER_METHOD_CONST(size_t, SBStructuredData, GetStringValue,
+ (char *, size_t));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBSymbol.cpp b/contrib/llvm-project/lldb/source/API/SBSymbol.cpp
new file mode 100644
index 000000000000..6cc90e0ee368
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBSymbol.cpp
@@ -0,0 +1,244 @@
+//===-- SBSymbol.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBSymbol.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBSymbol::SBSymbol() : m_opaque_ptr(nullptr) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBSymbol);
+}
+
+SBSymbol::SBSymbol(lldb_private::Symbol *lldb_object_ptr)
+ : m_opaque_ptr(lldb_object_ptr) {}
+
+SBSymbol::SBSymbol(const lldb::SBSymbol &rhs) : m_opaque_ptr(rhs.m_opaque_ptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBSymbol, (const lldb::SBSymbol &), rhs);
+}
+
+const SBSymbol &SBSymbol::operator=(const SBSymbol &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBSymbol &,
+ SBSymbol, operator=,(const lldb::SBSymbol &), rhs);
+
+ m_opaque_ptr = rhs.m_opaque_ptr;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBSymbol::~SBSymbol() { m_opaque_ptr = nullptr; }
+
+void SBSymbol::SetSymbol(lldb_private::Symbol *lldb_object_ptr) {
+ m_opaque_ptr = lldb_object_ptr;
+}
+
+bool SBSymbol::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBSymbol, IsValid);
+ return this->operator bool();
+}
+SBSymbol::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBSymbol, operator bool);
+
+ return m_opaque_ptr != nullptr;
+}
+
+const char *SBSymbol::GetName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBSymbol, GetName);
+
+ const char *name = nullptr;
+ if (m_opaque_ptr)
+ name = m_opaque_ptr->GetName().AsCString();
+
+ return name;
+}
+
+const char *SBSymbol::GetDisplayName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBSymbol, GetDisplayName);
+
+ const char *name = nullptr;
+ if (m_opaque_ptr)
+ name = m_opaque_ptr->GetMangled()
+ .GetDisplayDemangledName(m_opaque_ptr->GetLanguage())
+ .AsCString();
+
+ return name;
+}
+
+const char *SBSymbol::GetMangledName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBSymbol, GetMangledName);
+
+ const char *name = nullptr;
+ if (m_opaque_ptr)
+ name = m_opaque_ptr->GetMangled().GetMangledName().AsCString();
+ return name;
+}
+
+bool SBSymbol::operator==(const SBSymbol &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBSymbol, operator==,(const lldb::SBSymbol &),
+ rhs);
+
+ return m_opaque_ptr == rhs.m_opaque_ptr;
+}
+
+bool SBSymbol::operator!=(const SBSymbol &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBSymbol, operator!=,(const lldb::SBSymbol &),
+ rhs);
+
+ return m_opaque_ptr != rhs.m_opaque_ptr;
+}
+
+bool SBSymbol::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBSymbol, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_ptr) {
+ m_opaque_ptr->GetDescription(&strm, lldb::eDescriptionLevelFull, nullptr);
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+SBInstructionList SBSymbol::GetInstructions(SBTarget target) {
+ LLDB_RECORD_METHOD(lldb::SBInstructionList, SBSymbol, GetInstructions,
+ (lldb::SBTarget), target);
+
+ return LLDB_RECORD_RESULT(GetInstructions(target, nullptr));
+}
+
+SBInstructionList SBSymbol::GetInstructions(SBTarget target,
+ const char *flavor_string) {
+ LLDB_RECORD_METHOD(lldb::SBInstructionList, SBSymbol, GetInstructions,
+ (lldb::SBTarget, const char *), target, flavor_string);
+
+ SBInstructionList sb_instructions;
+ if (m_opaque_ptr) {
+ ExecutionContext exe_ctx;
+ TargetSP target_sp(target.GetSP());
+ std::unique_lock<std::recursive_mutex> lock;
+ if (target_sp) {
+ lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+
+ target_sp->CalculateExecutionContext(exe_ctx);
+ }
+ if (m_opaque_ptr->ValueIsAddress()) {
+ const Address &symbol_addr = m_opaque_ptr->GetAddressRef();
+ ModuleSP module_sp = symbol_addr.GetModule();
+ if (module_sp) {
+ AddressRange symbol_range(symbol_addr, m_opaque_ptr->GetByteSize());
+ const bool prefer_file_cache = false;
+ sb_instructions.SetDisassembler(Disassembler::DisassembleRange(
+ module_sp->GetArchitecture(), nullptr, flavor_string, exe_ctx,
+ symbol_range, prefer_file_cache));
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_instructions);
+}
+
+lldb_private::Symbol *SBSymbol::get() { return m_opaque_ptr; }
+
+void SBSymbol::reset(lldb_private::Symbol *symbol) { m_opaque_ptr = symbol; }
+
+SBAddress SBSymbol::GetStartAddress() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBAddress, SBSymbol, GetStartAddress);
+
+ SBAddress addr;
+ if (m_opaque_ptr && m_opaque_ptr->ValueIsAddress()) {
+ addr.SetAddress(&m_opaque_ptr->GetAddressRef());
+ }
+ return LLDB_RECORD_RESULT(addr);
+}
+
+SBAddress SBSymbol::GetEndAddress() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBAddress, SBSymbol, GetEndAddress);
+
+ SBAddress addr;
+ if (m_opaque_ptr && m_opaque_ptr->ValueIsAddress()) {
+ lldb::addr_t range_size = m_opaque_ptr->GetByteSize();
+ if (range_size > 0) {
+ addr.SetAddress(&m_opaque_ptr->GetAddressRef());
+ addr->Slide(m_opaque_ptr->GetByteSize());
+ }
+ }
+ return LLDB_RECORD_RESULT(addr);
+}
+
+uint32_t SBSymbol::GetPrologueByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBSymbol, GetPrologueByteSize);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->GetPrologueByteSize();
+ return 0;
+}
+
+SymbolType SBSymbol::GetType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SymbolType, SBSymbol, GetType);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->GetType();
+ return eSymbolTypeInvalid;
+}
+
+bool SBSymbol::IsExternal() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBSymbol, IsExternal);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->IsExternal();
+ return false;
+}
+
+bool SBSymbol::IsSynthetic() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBSymbol, IsSynthetic);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->IsSynthetic();
+ return false;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBSymbol>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBSymbol, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBSymbol, (const lldb::SBSymbol &));
+ LLDB_REGISTER_METHOD(const lldb::SBSymbol &,
+ SBSymbol, operator=,(const lldb::SBSymbol &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBSymbol, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBSymbol, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBSymbol, GetName, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBSymbol, GetDisplayName, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBSymbol, GetMangledName, ());
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBSymbol, operator==,(const lldb::SBSymbol &));
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBSymbol, operator!=,(const lldb::SBSymbol &));
+ LLDB_REGISTER_METHOD(bool, SBSymbol, GetDescription, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBSymbol, GetInstructions,
+ (lldb::SBTarget));
+ LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBSymbol, GetInstructions,
+ (lldb::SBTarget, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBSymbol, GetStartAddress, ());
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBSymbol, GetEndAddress, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBSymbol, GetPrologueByteSize, ());
+ LLDB_REGISTER_METHOD(lldb::SymbolType, SBSymbol, GetType, ());
+ LLDB_REGISTER_METHOD(bool, SBSymbol, IsExternal, ());
+ LLDB_REGISTER_METHOD(bool, SBSymbol, IsSynthetic, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBSymbolContext.cpp b/contrib/llvm-project/lldb/source/API/SBSymbolContext.cpp
new file mode 100644
index 000000000000..365f0ccc2fbf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBSymbolContext.cpp
@@ -0,0 +1,271 @@
+//===-- SBSymbolContext.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBSymbolContext.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBSymbolContext::SBSymbolContext() : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBSymbolContext);
+}
+
+SBSymbolContext::SBSymbolContext(const SymbolContext *sc_ptr) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBSymbolContext,
+ (const lldb_private::SymbolContext *), sc_ptr);
+
+ if (sc_ptr)
+ m_opaque_up = llvm::make_unique<SymbolContext>(*sc_ptr);
+}
+
+SBSymbolContext::SBSymbolContext(const SBSymbolContext &rhs) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBSymbolContext, (const lldb::SBSymbolContext &),
+ rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+SBSymbolContext::~SBSymbolContext() {}
+
+const SBSymbolContext &SBSymbolContext::operator=(const SBSymbolContext &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBSymbolContext &,
+ SBSymbolContext, operator=,(const lldb::SBSymbolContext &),
+ rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+void SBSymbolContext::SetSymbolContext(const SymbolContext *sc_ptr) {
+ if (sc_ptr)
+ m_opaque_up = llvm::make_unique<SymbolContext>(*sc_ptr);
+ else
+ m_opaque_up->Clear(true);
+}
+
+bool SBSymbolContext::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBSymbolContext, IsValid);
+ return this->operator bool();
+}
+SBSymbolContext::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBSymbolContext, operator bool);
+
+ return m_opaque_up != nullptr;
+}
+
+SBModule SBSymbolContext::GetModule() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBModule, SBSymbolContext, GetModule);
+
+ SBModule sb_module;
+ ModuleSP module_sp;
+ if (m_opaque_up) {
+ module_sp = m_opaque_up->module_sp;
+ sb_module.SetSP(module_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_module);
+}
+
+SBCompileUnit SBSymbolContext::GetCompileUnit() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBCompileUnit, SBSymbolContext,
+ GetCompileUnit);
+
+ return LLDB_RECORD_RESULT(
+ SBCompileUnit(m_opaque_up ? m_opaque_up->comp_unit : nullptr));
+}
+
+SBFunction SBSymbolContext::GetFunction() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFunction, SBSymbolContext, GetFunction);
+
+ Function *function = nullptr;
+
+ if (m_opaque_up)
+ function = m_opaque_up->function;
+
+ SBFunction sb_function(function);
+
+ return LLDB_RECORD_RESULT(sb_function);
+}
+
+SBBlock SBSymbolContext::GetBlock() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBBlock, SBSymbolContext, GetBlock);
+
+ return LLDB_RECORD_RESULT(
+ SBBlock(m_opaque_up ? m_opaque_up->block : nullptr));
+}
+
+SBLineEntry SBSymbolContext::GetLineEntry() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBLineEntry, SBSymbolContext, GetLineEntry);
+
+ SBLineEntry sb_line_entry;
+ if (m_opaque_up)
+ sb_line_entry.SetLineEntry(m_opaque_up->line_entry);
+
+ return LLDB_RECORD_RESULT(sb_line_entry);
+}
+
+SBSymbol SBSymbolContext::GetSymbol() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBSymbol, SBSymbolContext, GetSymbol);
+
+ Symbol *symbol = nullptr;
+
+ if (m_opaque_up)
+ symbol = m_opaque_up->symbol;
+
+ SBSymbol sb_symbol(symbol);
+
+ return LLDB_RECORD_RESULT(sb_symbol);
+}
+
+void SBSymbolContext::SetModule(lldb::SBModule module) {
+ LLDB_RECORD_METHOD(void, SBSymbolContext, SetModule, (lldb::SBModule),
+ module);
+
+ ref().module_sp = module.GetSP();
+}
+
+void SBSymbolContext::SetCompileUnit(lldb::SBCompileUnit compile_unit) {
+ LLDB_RECORD_METHOD(void, SBSymbolContext, SetCompileUnit,
+ (lldb::SBCompileUnit), compile_unit);
+
+ ref().comp_unit = compile_unit.get();
+}
+
+void SBSymbolContext::SetFunction(lldb::SBFunction function) {
+ LLDB_RECORD_METHOD(void, SBSymbolContext, SetFunction, (lldb::SBFunction),
+ function);
+
+ ref().function = function.get();
+}
+
+void SBSymbolContext::SetBlock(lldb::SBBlock block) {
+ LLDB_RECORD_METHOD(void, SBSymbolContext, SetBlock, (lldb::SBBlock), block);
+
+ ref().block = block.GetPtr();
+}
+
+void SBSymbolContext::SetLineEntry(lldb::SBLineEntry line_entry) {
+ LLDB_RECORD_METHOD(void, SBSymbolContext, SetLineEntry, (lldb::SBLineEntry),
+ line_entry);
+
+ if (line_entry.IsValid())
+ ref().line_entry = line_entry.ref();
+ else
+ ref().line_entry.Clear();
+}
+
+void SBSymbolContext::SetSymbol(lldb::SBSymbol symbol) {
+ LLDB_RECORD_METHOD(void, SBSymbolContext, SetSymbol, (lldb::SBSymbol),
+ symbol);
+
+ ref().symbol = symbol.get();
+}
+
+lldb_private::SymbolContext *SBSymbolContext::operator->() const {
+ return m_opaque_up.get();
+}
+
+const lldb_private::SymbolContext &SBSymbolContext::operator*() const {
+ assert(m_opaque_up.get());
+ return *m_opaque_up;
+}
+
+lldb_private::SymbolContext &SBSymbolContext::operator*() {
+ if (m_opaque_up == nullptr)
+ m_opaque_up.reset(new SymbolContext);
+ return *m_opaque_up;
+}
+
+lldb_private::SymbolContext &SBSymbolContext::ref() {
+ if (m_opaque_up == nullptr)
+ m_opaque_up.reset(new SymbolContext);
+ return *m_opaque_up;
+}
+
+lldb_private::SymbolContext *SBSymbolContext::get() const {
+ return m_opaque_up.get();
+}
+
+bool SBSymbolContext::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBSymbolContext, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_up) {
+ m_opaque_up->GetDescription(&strm, lldb::eDescriptionLevelFull, nullptr);
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+SBSymbolContext
+SBSymbolContext::GetParentOfInlinedScope(const SBAddress &curr_frame_pc,
+ SBAddress &parent_frame_addr) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBSymbolContext, SBSymbolContext,
+ GetParentOfInlinedScope,
+ (const lldb::SBAddress &, lldb::SBAddress &),
+ curr_frame_pc, parent_frame_addr);
+
+ SBSymbolContext sb_sc;
+ if (m_opaque_up.get() && curr_frame_pc.IsValid()) {
+ if (m_opaque_up->GetParentOfInlinedScope(curr_frame_pc.ref(), sb_sc.ref(),
+ parent_frame_addr.ref()))
+ return LLDB_RECORD_RESULT(sb_sc);
+ }
+ return LLDB_RECORD_RESULT(SBSymbolContext());
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBSymbolContext>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBSymbolContext, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBSymbolContext,
+ (const lldb_private::SymbolContext *));
+ LLDB_REGISTER_CONSTRUCTOR(SBSymbolContext, (const lldb::SBSymbolContext &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBSymbolContext &,
+ SBSymbolContext, operator=,(const lldb::SBSymbolContext &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBSymbolContext, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBSymbolContext, operator bool, ());
+ LLDB_REGISTER_METHOD(lldb::SBModule, SBSymbolContext, GetModule, ());
+ LLDB_REGISTER_METHOD(lldb::SBCompileUnit, SBSymbolContext, GetCompileUnit,
+ ());
+ LLDB_REGISTER_METHOD(lldb::SBFunction, SBSymbolContext, GetFunction, ());
+ LLDB_REGISTER_METHOD(lldb::SBBlock, SBSymbolContext, GetBlock, ());
+ LLDB_REGISTER_METHOD(lldb::SBLineEntry, SBSymbolContext, GetLineEntry, ());
+ LLDB_REGISTER_METHOD(lldb::SBSymbol, SBSymbolContext, GetSymbol, ());
+ LLDB_REGISTER_METHOD(void, SBSymbolContext, SetModule, (lldb::SBModule));
+ LLDB_REGISTER_METHOD(void, SBSymbolContext, SetCompileUnit,
+ (lldb::SBCompileUnit));
+ LLDB_REGISTER_METHOD(void, SBSymbolContext, SetFunction,
+ (lldb::SBFunction));
+ LLDB_REGISTER_METHOD(void, SBSymbolContext, SetBlock, (lldb::SBBlock));
+ LLDB_REGISTER_METHOD(void, SBSymbolContext, SetLineEntry,
+ (lldb::SBLineEntry));
+ LLDB_REGISTER_METHOD(void, SBSymbolContext, SetSymbol, (lldb::SBSymbol));
+ LLDB_REGISTER_METHOD(bool, SBSymbolContext, GetDescription,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBSymbolContext, SBSymbolContext,
+ GetParentOfInlinedScope,
+ (const lldb::SBAddress &, lldb::SBAddress &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBSymbolContextList.cpp b/contrib/llvm-project/lldb/source/API/SBSymbolContextList.cpp
new file mode 100644
index 000000000000..915d04a0282a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBSymbolContextList.cpp
@@ -0,0 +1,144 @@
+//===-- SBSymbolContextList.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBSymbolContextList.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/Symbol/SymbolContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBSymbolContextList::SBSymbolContextList()
+ : m_opaque_up(new SymbolContextList()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBSymbolContextList);
+}
+
+SBSymbolContextList::SBSymbolContextList(const SBSymbolContextList &rhs)
+ : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBSymbolContextList,
+ (const lldb::SBSymbolContextList &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+SBSymbolContextList::~SBSymbolContextList() {}
+
+const SBSymbolContextList &SBSymbolContextList::
+operator=(const SBSymbolContextList &rhs) {
+ LLDB_RECORD_METHOD(
+ const lldb::SBSymbolContextList &,
+ SBSymbolContextList, operator=,(const lldb::SBSymbolContextList &), rhs);
+
+ if (this != &rhs)
+ m_opaque_up = clone(rhs.m_opaque_up);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+uint32_t SBSymbolContextList::GetSize() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBSymbolContextList, GetSize);
+
+ if (m_opaque_up)
+ return m_opaque_up->GetSize();
+ return 0;
+}
+
+SBSymbolContext SBSymbolContextList::GetContextAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBSymbolContext, SBSymbolContextList,
+ GetContextAtIndex, (uint32_t), idx);
+
+ SBSymbolContext sb_sc;
+ if (m_opaque_up) {
+ SymbolContext sc;
+ if (m_opaque_up->GetContextAtIndex(idx, sc)) {
+ sb_sc.SetSymbolContext(&sc);
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_sc);
+}
+
+void SBSymbolContextList::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBSymbolContextList, Clear);
+
+ if (m_opaque_up)
+ m_opaque_up->Clear();
+}
+
+void SBSymbolContextList::Append(SBSymbolContext &sc) {
+ LLDB_RECORD_METHOD(void, SBSymbolContextList, Append,
+ (lldb::SBSymbolContext &), sc);
+
+ if (sc.IsValid() && m_opaque_up.get())
+ m_opaque_up->Append(*sc);
+}
+
+void SBSymbolContextList::Append(SBSymbolContextList &sc_list) {
+ LLDB_RECORD_METHOD(void, SBSymbolContextList, Append,
+ (lldb::SBSymbolContextList &), sc_list);
+
+ if (sc_list.IsValid() && m_opaque_up.get())
+ m_opaque_up->Append(*sc_list);
+}
+
+bool SBSymbolContextList::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBSymbolContextList, IsValid);
+ return this->operator bool();
+}
+SBSymbolContextList::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBSymbolContextList, operator bool);
+
+ return m_opaque_up != nullptr;
+}
+
+lldb_private::SymbolContextList *SBSymbolContextList::operator->() const {
+ return m_opaque_up.get();
+}
+
+lldb_private::SymbolContextList &SBSymbolContextList::operator*() const {
+ assert(m_opaque_up.get());
+ return *m_opaque_up;
+}
+
+bool SBSymbolContextList::GetDescription(lldb::SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBSymbolContextList, GetDescription,
+ (lldb::SBStream &), description);
+
+ Stream &strm = description.ref();
+ if (m_opaque_up)
+ m_opaque_up->GetDescription(&strm, lldb::eDescriptionLevelFull, nullptr);
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBSymbolContextList>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBSymbolContextList, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBSymbolContextList,
+ (const lldb::SBSymbolContextList &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBSymbolContextList &,
+ SBSymbolContextList, operator=,(const lldb::SBSymbolContextList &));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBSymbolContextList, GetSize, ());
+ LLDB_REGISTER_METHOD(lldb::SBSymbolContext, SBSymbolContextList,
+ GetContextAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBSymbolContextList, Clear, ());
+ LLDB_REGISTER_METHOD(void, SBSymbolContextList, Append,
+ (lldb::SBSymbolContext &));
+ LLDB_REGISTER_METHOD(void, SBSymbolContextList, Append,
+ (lldb::SBSymbolContextList &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBSymbolContextList, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBSymbolContextList, operator bool, ());
+ LLDB_REGISTER_METHOD(bool, SBSymbolContextList, GetDescription,
+ (lldb::SBStream &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBTarget.cpp b/contrib/llvm-project/lldb/source/API/SBTarget.cpp
new file mode 100644
index 000000000000..5e87eb6273b3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBTarget.cpp
@@ -0,0 +1,2646 @@
+//===-- SBTarget.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBTarget.h"
+#include "SBReproducerPrivate.h"
+
+#include "lldb/lldb-public.h"
+
+#include "lldb/API/SBBreakpoint.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBExpressionOptions.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBListener.h"
+#include "lldb/API/SBModule.h"
+#include "lldb/API/SBModuleSpec.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBSourceManager.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+#include "lldb/API/SBStructuredData.h"
+#include "lldb/API/SBSymbolContextList.h"
+#include "lldb/Breakpoint/BreakpointID.h"
+#include "lldb/Breakpoint/BreakpointIDList.h"
+#include "lldb/Breakpoint/BreakpointList.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressResolver.h"
+#include "lldb/Core/AddressResolverName.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/STLUtils.h"
+#include "lldb/Core/SearchFilter.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectList.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/DeclVendor.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetList.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/RegularExpression.h"
+
+#include "Commands/CommandObjectBreakpoint.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Regex.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define DEFAULT_DISASM_BYTE_SIZE 32
+
+namespace {
+
+Status AttachToProcess(ProcessAttachInfo &attach_info, Target &target) {
+ std::lock_guard<std::recursive_mutex> guard(target.GetAPIMutex());
+
+ auto process_sp = target.GetProcessSP();
+ if (process_sp) {
+ const auto state = process_sp->GetState();
+ if (process_sp->IsAlive() && state == eStateConnected) {
+ // If we are already connected, then we have already specified the
+ // listener, so if a valid listener is supplied, we need to error out to
+ // let the client know.
+ if (attach_info.GetListener())
+ return Status("process is connected and already has a listener, pass "
+ "empty listener");
+ }
+ }
+
+ return target.Attach(attach_info, nullptr);
+}
+
+} // namespace
+
+// SBTarget constructor
+SBTarget::SBTarget() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTarget);
+}
+
+SBTarget::SBTarget(const SBTarget &rhs) : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBTarget, (const lldb::SBTarget &), rhs);
+}
+
+SBTarget::SBTarget(const TargetSP &target_sp) : m_opaque_sp(target_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBTarget, (const lldb::TargetSP &), target_sp);
+}
+
+const SBTarget &SBTarget::operator=(const SBTarget &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBTarget &,
+ SBTarget, operator=,(const lldb::SBTarget &), rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = rhs.m_opaque_sp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+// Destructor
+SBTarget::~SBTarget() {}
+
+bool SBTarget::EventIsTargetEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBTarget, EventIsTargetEvent,
+ (const lldb::SBEvent &), event);
+
+ return Target::TargetEventData::GetEventDataFromEvent(event.get()) != nullptr;
+}
+
+SBTarget SBTarget::GetTargetFromEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBTarget, SBTarget, GetTargetFromEvent,
+ (const lldb::SBEvent &), event);
+
+ return LLDB_RECORD_RESULT(
+ Target::TargetEventData::GetTargetFromEvent(event.get()));
+}
+
+uint32_t SBTarget::GetNumModulesFromEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(uint32_t, SBTarget, GetNumModulesFromEvent,
+ (const lldb::SBEvent &), event);
+
+ const ModuleList module_list =
+ Target::TargetEventData::GetModuleListFromEvent(event.get());
+ return module_list.GetSize();
+}
+
+SBModule SBTarget::GetModuleAtIndexFromEvent(const uint32_t idx,
+ const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBModule, SBTarget, GetModuleAtIndexFromEvent,
+ (const uint32_t, const lldb::SBEvent &), idx,
+ event);
+
+ const ModuleList module_list =
+ Target::TargetEventData::GetModuleListFromEvent(event.get());
+ return LLDB_RECORD_RESULT(SBModule(module_list.GetModuleAtIndex(idx)));
+}
+
+const char *SBTarget::GetBroadcasterClassName() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(const char *, SBTarget,
+ GetBroadcasterClassName);
+
+ return Target::GetStaticBroadcasterClass().AsCString();
+}
+
+bool SBTarget::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTarget, IsValid);
+ return this->operator bool();
+}
+SBTarget::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTarget, operator bool);
+
+ return m_opaque_sp.get() != nullptr && m_opaque_sp->IsValid();
+}
+
+SBProcess SBTarget::GetProcess() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBProcess, SBTarget, GetProcess);
+
+ SBProcess sb_process;
+ ProcessSP process_sp;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ process_sp = target_sp->GetProcessSP();
+ sb_process.SetSP(process_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+SBPlatform SBTarget::GetPlatform() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBPlatform, SBTarget, GetPlatform);
+
+ TargetSP target_sp(GetSP());
+ if (!target_sp)
+ return LLDB_RECORD_RESULT(SBPlatform());
+
+ SBPlatform platform;
+ platform.m_opaque_sp = target_sp->GetPlatform();
+
+ return LLDB_RECORD_RESULT(platform);
+}
+
+SBDebugger SBTarget::GetDebugger() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBDebugger, SBTarget, GetDebugger);
+
+ SBDebugger debugger;
+ TargetSP target_sp(GetSP());
+ if (target_sp)
+ debugger.reset(target_sp->GetDebugger().shared_from_this());
+ return LLDB_RECORD_RESULT(debugger);
+}
+
+SBStructuredData SBTarget::GetStatistics() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBStructuredData, SBTarget, GetStatistics);
+
+ SBStructuredData data;
+ TargetSP target_sp(GetSP());
+ if (!target_sp)
+ return LLDB_RECORD_RESULT(data);
+
+ auto stats_up = llvm::make_unique<StructuredData::Dictionary>();
+ int i = 0;
+ for (auto &Entry : target_sp->GetStatistics()) {
+ std::string Desc = lldb_private::GetStatDescription(
+ static_cast<lldb_private::StatisticKind>(i));
+ stats_up->AddIntegerItem(Desc, Entry);
+ i += 1;
+ }
+
+ data.m_impl_up->SetObjectSP(std::move(stats_up));
+ return LLDB_RECORD_RESULT(data);
+}
+
+void SBTarget::SetCollectingStats(bool v) {
+ LLDB_RECORD_METHOD(void, SBTarget, SetCollectingStats, (bool), v);
+
+ TargetSP target_sp(GetSP());
+ if (!target_sp)
+ return;
+ return target_sp->SetCollectingStats(v);
+}
+
+bool SBTarget::GetCollectingStats() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, GetCollectingStats);
+
+ TargetSP target_sp(GetSP());
+ if (!target_sp)
+ return false;
+ return target_sp->GetCollectingStats();
+}
+
+SBProcess SBTarget::LoadCore(const char *core_file) {
+ LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, LoadCore, (const char *),
+ core_file);
+
+ lldb::SBError error; // Ignored
+ return LLDB_RECORD_RESULT(LoadCore(core_file, error));
+}
+
+SBProcess SBTarget::LoadCore(const char *core_file, lldb::SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, LoadCore,
+ (const char *, lldb::SBError &), core_file, error);
+
+ SBProcess sb_process;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ FileSpec filespec(core_file);
+ FileSystem::Instance().Resolve(filespec);
+ ProcessSP process_sp(target_sp->CreateProcess(
+ target_sp->GetDebugger().GetListener(), "", &filespec));
+ if (process_sp) {
+ error.SetError(process_sp->LoadCore());
+ if (error.Success())
+ sb_process.SetSP(process_sp);
+ } else {
+ error.SetErrorString("Failed to create the process");
+ }
+ } else {
+ error.SetErrorString("SBTarget is invalid");
+ }
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+SBProcess SBTarget::LaunchSimple(char const **argv, char const **envp,
+ const char *working_directory) {
+ LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, LaunchSimple,
+ (const char **, const char **, const char *), argv, envp,
+ working_directory);
+
+ char *stdin_path = nullptr;
+ char *stdout_path = nullptr;
+ char *stderr_path = nullptr;
+ uint32_t launch_flags = 0;
+ bool stop_at_entry = false;
+ SBError error;
+ SBListener listener = GetDebugger().GetListener();
+ return LLDB_RECORD_RESULT(Launch(listener, argv, envp, stdin_path,
+ stdout_path, stderr_path, working_directory,
+ launch_flags, stop_at_entry, error));
+}
+
+SBError SBTarget::Install() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBTarget, Install);
+
+ SBError sb_error;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ sb_error.ref() = target_sp->Install(nullptr);
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBProcess SBTarget::Launch(SBListener &listener, char const **argv,
+ char const **envp, const char *stdin_path,
+ const char *stdout_path, const char *stderr_path,
+ const char *working_directory,
+ uint32_t launch_flags, // See LaunchFlags
+ bool stop_at_entry, lldb::SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, Launch,
+ (lldb::SBListener &, const char **, const char **,
+ const char *, const char *, const char *, const char *,
+ uint32_t, bool, lldb::SBError &),
+ listener, argv, envp, stdin_path, stdout_path, stderr_path,
+ working_directory, launch_flags, stop_at_entry, error);
+
+ SBProcess sb_process;
+ ProcessSP process_sp;
+ TargetSP target_sp(GetSP());
+
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+
+ if (stop_at_entry)
+ launch_flags |= eLaunchFlagStopAtEntry;
+
+ if (getenv("LLDB_LAUNCH_FLAG_DISABLE_ASLR"))
+ launch_flags |= eLaunchFlagDisableASLR;
+
+ StateType state = eStateInvalid;
+ process_sp = target_sp->GetProcessSP();
+ if (process_sp) {
+ state = process_sp->GetState();
+
+ if (process_sp->IsAlive() && state != eStateConnected) {
+ if (state == eStateAttaching)
+ error.SetErrorString("process attach is in progress");
+ else
+ error.SetErrorString("a process is already being debugged");
+ return LLDB_RECORD_RESULT(sb_process);
+ }
+ }
+
+ if (state == eStateConnected) {
+ // If we are already connected, then we have already specified the
+ // listener, so if a valid listener is supplied, we need to error out to
+ // let the client know.
+ if (listener.IsValid()) {
+ error.SetErrorString("process is connected and already has a listener, "
+ "pass empty listener");
+ return LLDB_RECORD_RESULT(sb_process);
+ }
+ }
+
+ if (getenv("LLDB_LAUNCH_FLAG_DISABLE_STDIO"))
+ launch_flags |= eLaunchFlagDisableSTDIO;
+
+ ProcessLaunchInfo launch_info(FileSpec(stdin_path), FileSpec(stdout_path),
+ FileSpec(stderr_path),
+ FileSpec(working_directory), launch_flags);
+
+ Module *exe_module = target_sp->GetExecutableModulePointer();
+ if (exe_module)
+ launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true);
+ if (argv)
+ launch_info.GetArguments().AppendArguments(argv);
+ if (envp)
+ launch_info.GetEnvironment() = Environment(envp);
+
+ if (listener.IsValid())
+ launch_info.SetListener(listener.GetSP());
+
+ error.SetError(target_sp->Launch(launch_info, nullptr));
+
+ sb_process.SetSP(target_sp->GetProcessSP());
+ } else {
+ error.SetErrorString("SBTarget is invalid");
+ }
+
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+SBProcess SBTarget::Launch(SBLaunchInfo &sb_launch_info, SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, Launch,
+ (lldb::SBLaunchInfo &, lldb::SBError &), sb_launch_info,
+ error);
+
+
+ SBProcess sb_process;
+ TargetSP target_sp(GetSP());
+
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ StateType state = eStateInvalid;
+ {
+ ProcessSP process_sp = target_sp->GetProcessSP();
+ if (process_sp) {
+ state = process_sp->GetState();
+
+ if (process_sp->IsAlive() && state != eStateConnected) {
+ if (state == eStateAttaching)
+ error.SetErrorString("process attach is in progress");
+ else
+ error.SetErrorString("a process is already being debugged");
+ return LLDB_RECORD_RESULT(sb_process);
+ }
+ }
+ }
+
+ lldb_private::ProcessLaunchInfo launch_info = sb_launch_info.ref();
+
+ if (!launch_info.GetExecutableFile()) {
+ Module *exe_module = target_sp->GetExecutableModulePointer();
+ if (exe_module)
+ launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true);
+ }
+
+ const ArchSpec &arch_spec = target_sp->GetArchitecture();
+ if (arch_spec.IsValid())
+ launch_info.GetArchitecture() = arch_spec;
+
+ error.SetError(target_sp->Launch(launch_info, nullptr));
+ sb_launch_info.set_ref(launch_info);
+ sb_process.SetSP(target_sp->GetProcessSP());
+ } else {
+ error.SetErrorString("SBTarget is invalid");
+ }
+
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+lldb::SBProcess SBTarget::Attach(SBAttachInfo &sb_attach_info, SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, Attach,
+ (lldb::SBAttachInfo &, lldb::SBError &), sb_attach_info,
+ error);
+
+ SBProcess sb_process;
+ TargetSP target_sp(GetSP());
+
+ if (target_sp) {
+ ProcessAttachInfo &attach_info = sb_attach_info.ref();
+ if (attach_info.ProcessIDIsValid() && !attach_info.UserIDIsValid()) {
+ PlatformSP platform_sp = target_sp->GetPlatform();
+ // See if we can pre-verify if a process exists or not
+ if (platform_sp && platform_sp->IsConnected()) {
+ lldb::pid_t attach_pid = attach_info.GetProcessID();
+ ProcessInstanceInfo instance_info;
+ if (platform_sp->GetProcessInfo(attach_pid, instance_info)) {
+ attach_info.SetUserID(instance_info.GetEffectiveUserID());
+ } else {
+ error.ref().SetErrorStringWithFormat(
+ "no process found with process ID %" PRIu64, attach_pid);
+ return LLDB_RECORD_RESULT(sb_process);
+ }
+ }
+ }
+ error.SetError(AttachToProcess(attach_info, *target_sp));
+ if (error.Success())
+ sb_process.SetSP(target_sp->GetProcessSP());
+ } else {
+ error.SetErrorString("SBTarget is invalid");
+ }
+
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+lldb::SBProcess SBTarget::AttachToProcessWithID(
+ SBListener &listener,
+ lldb::pid_t pid, // The process ID to attach to
+ SBError &error // An error explaining what went wrong if attach fails
+) {
+ LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, AttachToProcessWithID,
+ (lldb::SBListener &, lldb::pid_t, lldb::SBError &),
+ listener, pid, error);
+
+ SBProcess sb_process;
+ TargetSP target_sp(GetSP());
+
+ if (target_sp) {
+ ProcessAttachInfo attach_info;
+ attach_info.SetProcessID(pid);
+ if (listener.IsValid())
+ attach_info.SetListener(listener.GetSP());
+
+ ProcessInstanceInfo instance_info;
+ if (target_sp->GetPlatform()->GetProcessInfo(pid, instance_info))
+ attach_info.SetUserID(instance_info.GetEffectiveUserID());
+
+ error.SetError(AttachToProcess(attach_info, *target_sp));
+ if (error.Success())
+ sb_process.SetSP(target_sp->GetProcessSP());
+ } else
+ error.SetErrorString("SBTarget is invalid");
+
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+lldb::SBProcess SBTarget::AttachToProcessWithName(
+ SBListener &listener,
+ const char *name, // basename of process to attach to
+ bool wait_for, // if true wait for a new instance of "name" to be launched
+ SBError &error // An error explaining what went wrong if attach fails
+) {
+ LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, AttachToProcessWithName,
+ (lldb::SBListener &, const char *, bool, lldb::SBError &),
+ listener, name, wait_for, error);
+
+ SBProcess sb_process;
+ TargetSP target_sp(GetSP());
+
+ if (name && target_sp) {
+ ProcessAttachInfo attach_info;
+ attach_info.GetExecutableFile().SetFile(name, FileSpec::Style::native);
+ attach_info.SetWaitForLaunch(wait_for);
+ if (listener.IsValid())
+ attach_info.SetListener(listener.GetSP());
+
+ error.SetError(AttachToProcess(attach_info, *target_sp));
+ if (error.Success())
+ sb_process.SetSP(target_sp->GetProcessSP());
+ } else
+ error.SetErrorString("SBTarget is invalid");
+
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+lldb::SBProcess SBTarget::ConnectRemote(SBListener &listener, const char *url,
+ const char *plugin_name,
+ SBError &error) {
+ LLDB_RECORD_METHOD(
+ lldb::SBProcess, SBTarget, ConnectRemote,
+ (lldb::SBListener &, const char *, const char *, lldb::SBError &),
+ listener, url, plugin_name, error);
+
+ SBProcess sb_process;
+ ProcessSP process_sp;
+ TargetSP target_sp(GetSP());
+
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ if (listener.IsValid())
+ process_sp =
+ target_sp->CreateProcess(listener.m_opaque_sp, plugin_name, nullptr);
+ else
+ process_sp = target_sp->CreateProcess(
+ target_sp->GetDebugger().GetListener(), plugin_name, nullptr);
+
+ if (process_sp) {
+ sb_process.SetSP(process_sp);
+ error.SetError(process_sp->ConnectRemote(nullptr, url));
+ } else {
+ error.SetErrorString("unable to create lldb_private::Process");
+ }
+ } else {
+ error.SetErrorString("SBTarget is invalid");
+ }
+
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+SBFileSpec SBTarget::GetExecutable() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFileSpec, SBTarget, GetExecutable);
+
+ SBFileSpec exe_file_spec;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ Module *exe_module = target_sp->GetExecutableModulePointer();
+ if (exe_module)
+ exe_file_spec.SetFileSpec(exe_module->GetFileSpec());
+ }
+
+ return LLDB_RECORD_RESULT(exe_file_spec);
+}
+
+bool SBTarget::operator==(const SBTarget &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBTarget, operator==,(const lldb::SBTarget &),
+ rhs);
+
+ return m_opaque_sp.get() == rhs.m_opaque_sp.get();
+}
+
+bool SBTarget::operator!=(const SBTarget &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBTarget, operator!=,(const lldb::SBTarget &),
+ rhs);
+
+ return m_opaque_sp.get() != rhs.m_opaque_sp.get();
+}
+
+lldb::TargetSP SBTarget::GetSP() const { return m_opaque_sp; }
+
+void SBTarget::SetSP(const lldb::TargetSP &target_sp) {
+ m_opaque_sp = target_sp;
+}
+
+lldb::SBAddress SBTarget::ResolveLoadAddress(lldb::addr_t vm_addr) {
+ LLDB_RECORD_METHOD(lldb::SBAddress, SBTarget, ResolveLoadAddress,
+ (lldb::addr_t), vm_addr);
+
+ lldb::SBAddress sb_addr;
+ Address &addr = sb_addr.ref();
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ if (target_sp->ResolveLoadAddress(vm_addr, addr))
+ return LLDB_RECORD_RESULT(sb_addr);
+ }
+
+ // We have a load address that isn't in a section, just return an address
+ // with the offset filled in (the address) and the section set to NULL
+ addr.SetRawAddress(vm_addr);
+ return LLDB_RECORD_RESULT(sb_addr);
+}
+
+lldb::SBAddress SBTarget::ResolveFileAddress(lldb::addr_t file_addr) {
+ LLDB_RECORD_METHOD(lldb::SBAddress, SBTarget, ResolveFileAddress,
+ (lldb::addr_t), file_addr);
+
+ lldb::SBAddress sb_addr;
+ Address &addr = sb_addr.ref();
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ if (target_sp->ResolveFileAddress(file_addr, addr))
+ return LLDB_RECORD_RESULT(sb_addr);
+ }
+
+ addr.SetRawAddress(file_addr);
+ return LLDB_RECORD_RESULT(sb_addr);
+}
+
+lldb::SBAddress SBTarget::ResolvePastLoadAddress(uint32_t stop_id,
+ lldb::addr_t vm_addr) {
+ LLDB_RECORD_METHOD(lldb::SBAddress, SBTarget, ResolvePastLoadAddress,
+ (uint32_t, lldb::addr_t), stop_id, vm_addr);
+
+ lldb::SBAddress sb_addr;
+ Address &addr = sb_addr.ref();
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ if (target_sp->ResolveLoadAddress(vm_addr, addr))
+ return LLDB_RECORD_RESULT(sb_addr);
+ }
+
+ // We have a load address that isn't in a section, just return an address
+ // with the offset filled in (the address) and the section set to NULL
+ addr.SetRawAddress(vm_addr);
+ return LLDB_RECORD_RESULT(sb_addr);
+}
+
+SBSymbolContext
+SBTarget::ResolveSymbolContextForAddress(const SBAddress &addr,
+ uint32_t resolve_scope) {
+ LLDB_RECORD_METHOD(lldb::SBSymbolContext, SBTarget,
+ ResolveSymbolContextForAddress,
+ (const lldb::SBAddress &, uint32_t), addr, resolve_scope);
+
+ SBSymbolContext sc;
+ SymbolContextItem scope = static_cast<SymbolContextItem>(resolve_scope);
+ if (addr.IsValid()) {
+ TargetSP target_sp(GetSP());
+ if (target_sp)
+ target_sp->GetImages().ResolveSymbolContextForAddress(addr.ref(), scope,
+ sc.ref());
+ }
+ return LLDB_RECORD_RESULT(sc);
+}
+
+size_t SBTarget::ReadMemory(const SBAddress addr, void *buf, size_t size,
+ lldb::SBError &error) {
+ LLDB_RECORD_DUMMY(size_t, SBTarget, ReadMemory,
+ (const lldb::SBAddress, void *, size_t, lldb::SBError &),
+ addr, buf, size, error);
+
+ SBError sb_error;
+ size_t bytes_read = 0;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ bytes_read =
+ target_sp->ReadMemory(addr.ref(), false, buf, size, sb_error.ref());
+ } else {
+ sb_error.SetErrorString("invalid target");
+ }
+
+ return bytes_read;
+}
+
+SBBreakpoint SBTarget::BreakpointCreateByLocation(const char *file,
+ uint32_t line) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation,
+ (const char *, uint32_t), file, line);
+
+ return LLDB_RECORD_RESULT(
+ SBBreakpoint(BreakpointCreateByLocation(SBFileSpec(file, false), line)));
+}
+
+SBBreakpoint
+SBTarget::BreakpointCreateByLocation(const SBFileSpec &sb_file_spec,
+ uint32_t line) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation,
+ (const lldb::SBFileSpec &, uint32_t), sb_file_spec, line);
+
+ return LLDB_RECORD_RESULT(BreakpointCreateByLocation(sb_file_spec, line, 0));
+}
+
+SBBreakpoint
+SBTarget::BreakpointCreateByLocation(const SBFileSpec &sb_file_spec,
+ uint32_t line, lldb::addr_t offset) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation,
+ (const lldb::SBFileSpec &, uint32_t, lldb::addr_t),
+ sb_file_spec, line, offset);
+
+ SBFileSpecList empty_list;
+ return LLDB_RECORD_RESULT(
+ BreakpointCreateByLocation(sb_file_spec, line, offset, empty_list));
+}
+
+SBBreakpoint
+SBTarget::BreakpointCreateByLocation(const SBFileSpec &sb_file_spec,
+ uint32_t line, lldb::addr_t offset,
+ SBFileSpecList &sb_module_list) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation,
+ (const lldb::SBFileSpec &, uint32_t, lldb::addr_t,
+ lldb::SBFileSpecList &),
+ sb_file_spec, line, offset, sb_module_list);
+
+ return LLDB_RECORD_RESULT(BreakpointCreateByLocation(sb_file_spec, line, 0,
+ offset, sb_module_list));
+}
+
+SBBreakpoint SBTarget::BreakpointCreateByLocation(
+ const SBFileSpec &sb_file_spec, uint32_t line, uint32_t column,
+ lldb::addr_t offset, SBFileSpecList &sb_module_list) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation,
+ (const lldb::SBFileSpec &, uint32_t, uint32_t,
+ lldb::addr_t, lldb::SBFileSpecList &),
+ sb_file_spec, line, column, offset, sb_module_list);
+
+ SBBreakpoint sb_bp;
+ TargetSP target_sp(GetSP());
+ if (target_sp && line != 0) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+
+ const LazyBool check_inlines = eLazyBoolCalculate;
+ const LazyBool skip_prologue = eLazyBoolCalculate;
+ const bool internal = false;
+ const bool hardware = false;
+ const LazyBool move_to_nearest_code = eLazyBoolCalculate;
+ const FileSpecList *module_list = nullptr;
+ if (sb_module_list.GetSize() > 0) {
+ module_list = sb_module_list.get();
+ }
+ sb_bp = target_sp->CreateBreakpoint(
+ module_list, *sb_file_spec, line, column, offset, check_inlines,
+ skip_prologue, internal, hardware, move_to_nearest_code);
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp);
+}
+
+SBBreakpoint SBTarget::BreakpointCreateByName(const char *symbol_name,
+ const char *module_name) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName,
+ (const char *, const char *), symbol_name, module_name);
+
+ SBBreakpoint sb_bp;
+ TargetSP target_sp(GetSP());
+ if (target_sp.get()) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+
+ const bool internal = false;
+ const bool hardware = false;
+ const LazyBool skip_prologue = eLazyBoolCalculate;
+ const lldb::addr_t offset = 0;
+ if (module_name && module_name[0]) {
+ FileSpecList module_spec_list;
+ module_spec_list.Append(FileSpec(module_name));
+ sb_bp = target_sp->CreateBreakpoint(
+ &module_spec_list, nullptr, symbol_name, eFunctionNameTypeAuto,
+ eLanguageTypeUnknown, offset, skip_prologue, internal, hardware);
+ } else {
+ sb_bp = target_sp->CreateBreakpoint(
+ nullptr, nullptr, symbol_name, eFunctionNameTypeAuto,
+ eLanguageTypeUnknown, offset, skip_prologue, internal, hardware);
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp);
+}
+
+lldb::SBBreakpoint
+SBTarget::BreakpointCreateByName(const char *symbol_name,
+ const SBFileSpecList &module_list,
+ const SBFileSpecList &comp_unit_list) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName,
+ (const char *, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &),
+ symbol_name, module_list, comp_unit_list);
+
+ lldb::FunctionNameType name_type_mask = eFunctionNameTypeAuto;
+ return LLDB_RECORD_RESULT(
+ BreakpointCreateByName(symbol_name, name_type_mask, eLanguageTypeUnknown,
+ module_list, comp_unit_list));
+}
+
+lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
+ const char *symbol_name, uint32_t name_type_mask,
+ const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName,
+ (const char *, uint32_t, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &),
+ symbol_name, name_type_mask, module_list, comp_unit_list);
+
+ return LLDB_RECORD_RESULT(
+ BreakpointCreateByName(symbol_name, name_type_mask, eLanguageTypeUnknown,
+ module_list, comp_unit_list));
+}
+
+lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
+ const char *symbol_name, uint32_t name_type_mask,
+ LanguageType symbol_language, const SBFileSpecList &module_list,
+ const SBFileSpecList &comp_unit_list) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName,
+ (const char *, uint32_t, lldb::LanguageType,
+ const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &),
+ symbol_name, name_type_mask, symbol_language, module_list,
+ comp_unit_list);
+
+ SBBreakpoint sb_bp;
+ TargetSP target_sp(GetSP());
+ if (target_sp && symbol_name && symbol_name[0]) {
+ const bool internal = false;
+ const bool hardware = false;
+ const LazyBool skip_prologue = eLazyBoolCalculate;
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ FunctionNameType mask = static_cast<FunctionNameType>(name_type_mask);
+ sb_bp = target_sp->CreateBreakpoint(module_list.get(), comp_unit_list.get(),
+ symbol_name, mask, symbol_language, 0,
+ skip_prologue, internal, hardware);
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp);
+}
+
+lldb::SBBreakpoint SBTarget::BreakpointCreateByNames(
+ const char *symbol_names[], uint32_t num_names, uint32_t name_type_mask,
+ const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) {
+ LLDB_RECORD_METHOD(
+ lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames,
+ (const char **, uint32_t, uint32_t, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &),
+ symbol_names, num_names, name_type_mask, module_list, comp_unit_list);
+
+ return LLDB_RECORD_RESULT(BreakpointCreateByNames(
+ symbol_names, num_names, name_type_mask, eLanguageTypeUnknown,
+ module_list, comp_unit_list));
+}
+
+lldb::SBBreakpoint SBTarget::BreakpointCreateByNames(
+ const char *symbol_names[], uint32_t num_names, uint32_t name_type_mask,
+ LanguageType symbol_language, const SBFileSpecList &module_list,
+ const SBFileSpecList &comp_unit_list) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames,
+ (const char **, uint32_t, uint32_t, lldb::LanguageType,
+ const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &),
+ symbol_names, num_names, name_type_mask, symbol_language,
+ module_list, comp_unit_list);
+
+ return LLDB_RECORD_RESULT(BreakpointCreateByNames(
+ symbol_names, num_names, name_type_mask, eLanguageTypeUnknown, 0,
+ module_list, comp_unit_list));
+}
+
+lldb::SBBreakpoint SBTarget::BreakpointCreateByNames(
+ const char *symbol_names[], uint32_t num_names, uint32_t name_type_mask,
+ LanguageType symbol_language, lldb::addr_t offset,
+ const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames,
+ (const char **, uint32_t, uint32_t, lldb::LanguageType,
+ lldb::addr_t, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &),
+ symbol_names, num_names, name_type_mask, symbol_language,
+ offset, module_list, comp_unit_list);
+
+ SBBreakpoint sb_bp;
+ TargetSP target_sp(GetSP());
+ if (target_sp && num_names > 0) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ const bool internal = false;
+ const bool hardware = false;
+ FunctionNameType mask = static_cast<FunctionNameType>(name_type_mask);
+ const LazyBool skip_prologue = eLazyBoolCalculate;
+ sb_bp = target_sp->CreateBreakpoint(
+ module_list.get(), comp_unit_list.get(), symbol_names, num_names, mask,
+ symbol_language, offset, skip_prologue, internal, hardware);
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp);
+}
+
+SBBreakpoint SBTarget::BreakpointCreateByRegex(const char *symbol_name_regex,
+ const char *module_name) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex,
+ (const char *, const char *), symbol_name_regex,
+ module_name);
+
+ SBFileSpecList module_spec_list;
+ SBFileSpecList comp_unit_list;
+ if (module_name && module_name[0]) {
+ module_spec_list.Append(FileSpec(module_name));
+ }
+ return LLDB_RECORD_RESULT(
+ BreakpointCreateByRegex(symbol_name_regex, eLanguageTypeUnknown,
+ module_spec_list, comp_unit_list));
+}
+
+lldb::SBBreakpoint
+SBTarget::BreakpointCreateByRegex(const char *symbol_name_regex,
+ const SBFileSpecList &module_list,
+ const SBFileSpecList &comp_unit_list) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex,
+ (const char *, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &),
+ symbol_name_regex, module_list, comp_unit_list);
+
+ return LLDB_RECORD_RESULT(BreakpointCreateByRegex(
+ symbol_name_regex, eLanguageTypeUnknown, module_list, comp_unit_list));
+}
+
+lldb::SBBreakpoint SBTarget::BreakpointCreateByRegex(
+ const char *symbol_name_regex, LanguageType symbol_language,
+ const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) {
+ LLDB_RECORD_METHOD(
+ lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex,
+ (const char *, lldb::LanguageType, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &),
+ symbol_name_regex, symbol_language, module_list, comp_unit_list);
+
+
+ SBBreakpoint sb_bp;
+ TargetSP target_sp(GetSP());
+ if (target_sp && symbol_name_regex && symbol_name_regex[0]) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ RegularExpression regexp((llvm::StringRef(symbol_name_regex)));
+ const bool internal = false;
+ const bool hardware = false;
+ const LazyBool skip_prologue = eLazyBoolCalculate;
+
+ sb_bp = target_sp->CreateFuncRegexBreakpoint(
+ module_list.get(), comp_unit_list.get(), regexp, symbol_language,
+ skip_prologue, internal, hardware);
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp);
+}
+
+SBBreakpoint SBTarget::BreakpointCreateByAddress(addr_t address) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByAddress,
+ (lldb::addr_t), address);
+
+ SBBreakpoint sb_bp;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ const bool hardware = false;
+ sb_bp = target_sp->CreateBreakpoint(address, false, hardware);
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp);
+}
+
+SBBreakpoint SBTarget::BreakpointCreateBySBAddress(SBAddress &sb_address) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateBySBAddress,
+ (lldb::SBAddress &), sb_address);
+
+ SBBreakpoint sb_bp;
+ TargetSP target_sp(GetSP());
+ if (!sb_address.IsValid()) {
+ return LLDB_RECORD_RESULT(sb_bp);
+ }
+
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ const bool hardware = false;
+ sb_bp = target_sp->CreateBreakpoint(sb_address.ref(), false, hardware);
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp);
+}
+
+lldb::SBBreakpoint
+SBTarget::BreakpointCreateBySourceRegex(const char *source_regex,
+ const lldb::SBFileSpec &source_file,
+ const char *module_name) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateBySourceRegex,
+ (const char *, const lldb::SBFileSpec &, const char *),
+ source_regex, source_file, module_name);
+
+ SBFileSpecList module_spec_list;
+
+ if (module_name && module_name[0]) {
+ module_spec_list.Append(FileSpec(module_name));
+ }
+
+ SBFileSpecList source_file_list;
+ if (source_file.IsValid()) {
+ source_file_list.Append(source_file);
+ }
+
+ return LLDB_RECORD_RESULT(BreakpointCreateBySourceRegex(
+ source_regex, module_spec_list, source_file_list));
+}
+
+lldb::SBBreakpoint SBTarget::BreakpointCreateBySourceRegex(
+ const char *source_regex, const SBFileSpecList &module_list,
+ const lldb::SBFileSpecList &source_file_list) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateBySourceRegex,
+ (const char *, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &),
+ source_regex, module_list, source_file_list);
+
+ return LLDB_RECORD_RESULT(BreakpointCreateBySourceRegex(
+ source_regex, module_list, source_file_list, SBStringList()));
+}
+
+lldb::SBBreakpoint SBTarget::BreakpointCreateBySourceRegex(
+ const char *source_regex, const SBFileSpecList &module_list,
+ const lldb::SBFileSpecList &source_file_list,
+ const SBStringList &func_names) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateBySourceRegex,
+ (const char *, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &, const lldb::SBStringList &),
+ source_regex, module_list, source_file_list, func_names);
+
+ SBBreakpoint sb_bp;
+ TargetSP target_sp(GetSP());
+ if (target_sp && source_regex && source_regex[0]) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ const bool hardware = false;
+ const LazyBool move_to_nearest_code = eLazyBoolCalculate;
+ RegularExpression regexp((llvm::StringRef(source_regex)));
+ std::unordered_set<std::string> func_names_set;
+ for (size_t i = 0; i < func_names.GetSize(); i++) {
+ func_names_set.insert(func_names.GetStringAtIndex(i));
+ }
+
+ sb_bp = target_sp->CreateSourceRegexBreakpoint(
+ module_list.get(), source_file_list.get(), func_names_set, regexp,
+ false, hardware, move_to_nearest_code);
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp);
+}
+
+lldb::SBBreakpoint
+SBTarget::BreakpointCreateForException(lldb::LanguageType language,
+ bool catch_bp, bool throw_bp) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateForException,
+ (lldb::LanguageType, bool, bool), language, catch_bp,
+ throw_bp);
+
+ SBBreakpoint sb_bp;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ const bool hardware = false;
+ sb_bp = target_sp->CreateExceptionBreakpoint(language, catch_bp, throw_bp,
+ hardware);
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp);
+}
+
+lldb::SBBreakpoint SBTarget::BreakpointCreateFromScript(
+ const char *class_name, SBStructuredData &extra_args,
+ const SBFileSpecList &module_list, const SBFileSpecList &file_list,
+ bool request_hardware) {
+ LLDB_RECORD_METHOD(
+ lldb::SBBreakpoint, SBTarget, BreakpointCreateFromScript,
+ (const char *, lldb::SBStructuredData &, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &, bool),
+ class_name, extra_args, module_list, file_list, request_hardware);
+
+ SBBreakpoint sb_bp;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ Status error;
+
+ StructuredData::ObjectSP obj_sp = extra_args.m_impl_up->GetObjectSP();
+ sb_bp =
+ target_sp->CreateScriptedBreakpoint(class_name,
+ module_list.get(),
+ file_list.get(),
+ false, /* internal */
+ request_hardware,
+ obj_sp,
+ &error);
+ }
+
+ return LLDB_RECORD_RESULT(sb_bp);
+}
+
+uint32_t SBTarget::GetNumBreakpoints() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBTarget, GetNumBreakpoints);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ // The breakpoint list is thread safe, no need to lock
+ return target_sp->GetBreakpointList().GetSize();
+ }
+ return 0;
+}
+
+SBBreakpoint SBTarget::GetBreakpointAtIndex(uint32_t idx) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBBreakpoint, SBTarget, GetBreakpointAtIndex,
+ (uint32_t), idx);
+
+ SBBreakpoint sb_breakpoint;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ // The breakpoint list is thread safe, no need to lock
+ sb_breakpoint = target_sp->GetBreakpointList().GetBreakpointAtIndex(idx);
+ }
+ return LLDB_RECORD_RESULT(sb_breakpoint);
+}
+
+bool SBTarget::BreakpointDelete(break_id_t bp_id) {
+ LLDB_RECORD_METHOD(bool, SBTarget, BreakpointDelete, (lldb::break_id_t),
+ bp_id);
+
+ bool result = false;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ result = target_sp->RemoveBreakpointByID(bp_id);
+ }
+
+ return result;
+}
+
+SBBreakpoint SBTarget::FindBreakpointByID(break_id_t bp_id) {
+ LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, FindBreakpointByID,
+ (lldb::break_id_t), bp_id);
+
+ SBBreakpoint sb_breakpoint;
+ TargetSP target_sp(GetSP());
+ if (target_sp && bp_id != LLDB_INVALID_BREAK_ID) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ sb_breakpoint = target_sp->GetBreakpointByID(bp_id);
+ }
+
+ return LLDB_RECORD_RESULT(sb_breakpoint);
+}
+
+bool SBTarget::FindBreakpointsByName(const char *name,
+ SBBreakpointList &bkpts) {
+ LLDB_RECORD_METHOD(bool, SBTarget, FindBreakpointsByName,
+ (const char *, lldb::SBBreakpointList &), name, bkpts);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ BreakpointList bkpt_list(false);
+ bool is_valid =
+ target_sp->GetBreakpointList().FindBreakpointsByName(name, bkpt_list);
+ if (!is_valid)
+ return false;
+ for (BreakpointSP bkpt_sp : bkpt_list.Breakpoints()) {
+ bkpts.AppendByID(bkpt_sp->GetID());
+ }
+ }
+ return true;
+}
+
+void SBTarget::GetBreakpointNames(SBStringList &names) {
+ LLDB_RECORD_METHOD(void, SBTarget, GetBreakpointNames, (lldb::SBStringList &),
+ names);
+
+ names.Clear();
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+
+ std::vector<std::string> name_vec;
+ target_sp->GetBreakpointNames(name_vec);
+ for (auto name : name_vec)
+ names.AppendString(name.c_str());
+ }
+}
+
+void SBTarget::DeleteBreakpointName(const char *name) {
+ LLDB_RECORD_METHOD(void, SBTarget, DeleteBreakpointName, (const char *),
+ name);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ target_sp->DeleteBreakpointName(ConstString(name));
+ }
+}
+
+bool SBTarget::EnableAllBreakpoints() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, EnableAllBreakpoints);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ target_sp->EnableAllowedBreakpoints();
+ return true;
+ }
+ return false;
+}
+
+bool SBTarget::DisableAllBreakpoints() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, DisableAllBreakpoints);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ target_sp->DisableAllowedBreakpoints();
+ return true;
+ }
+ return false;
+}
+
+bool SBTarget::DeleteAllBreakpoints() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, DeleteAllBreakpoints);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ target_sp->RemoveAllowedBreakpoints();
+ return true;
+ }
+ return false;
+}
+
+lldb::SBError SBTarget::BreakpointsCreateFromFile(SBFileSpec &source_file,
+ SBBreakpointList &new_bps) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBTarget, BreakpointsCreateFromFile,
+ (lldb::SBFileSpec &, lldb::SBBreakpointList &),
+ source_file, new_bps);
+
+ SBStringList empty_name_list;
+ return LLDB_RECORD_RESULT(
+ BreakpointsCreateFromFile(source_file, empty_name_list, new_bps));
+}
+
+lldb::SBError SBTarget::BreakpointsCreateFromFile(SBFileSpec &source_file,
+ SBStringList &matching_names,
+ SBBreakpointList &new_bps) {
+ LLDB_RECORD_METHOD(
+ lldb::SBError, SBTarget, BreakpointsCreateFromFile,
+ (lldb::SBFileSpec &, lldb::SBStringList &, lldb::SBBreakpointList &),
+ source_file, matching_names, new_bps);
+
+ SBError sberr;
+ TargetSP target_sp(GetSP());
+ if (!target_sp) {
+ sberr.SetErrorString(
+ "BreakpointCreateFromFile called with invalid target.");
+ return LLDB_RECORD_RESULT(sberr);
+ }
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+
+ BreakpointIDList bp_ids;
+
+ std::vector<std::string> name_vector;
+ size_t num_names = matching_names.GetSize();
+ for (size_t i = 0; i < num_names; i++)
+ name_vector.push_back(matching_names.GetStringAtIndex(i));
+
+ sberr.ref() = target_sp->CreateBreakpointsFromFile(source_file.ref(),
+ name_vector, bp_ids);
+ if (sberr.Fail())
+ return LLDB_RECORD_RESULT(sberr);
+
+ size_t num_bkpts = bp_ids.GetSize();
+ for (size_t i = 0; i < num_bkpts; i++) {
+ BreakpointID bp_id = bp_ids.GetBreakpointIDAtIndex(i);
+ new_bps.AppendByID(bp_id.GetBreakpointID());
+ }
+ return LLDB_RECORD_RESULT(sberr);
+}
+
+lldb::SBError SBTarget::BreakpointsWriteToFile(SBFileSpec &dest_file) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBTarget, BreakpointsWriteToFile,
+ (lldb::SBFileSpec &), dest_file);
+
+ SBError sberr;
+ TargetSP target_sp(GetSP());
+ if (!target_sp) {
+ sberr.SetErrorString("BreakpointWriteToFile called with invalid target.");
+ return LLDB_RECORD_RESULT(sberr);
+ }
+ SBBreakpointList bkpt_list(*this);
+ return LLDB_RECORD_RESULT(BreakpointsWriteToFile(dest_file, bkpt_list));
+}
+
+lldb::SBError SBTarget::BreakpointsWriteToFile(SBFileSpec &dest_file,
+ SBBreakpointList &bkpt_list,
+ bool append) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBTarget, BreakpointsWriteToFile,
+ (lldb::SBFileSpec &, lldb::SBBreakpointList &, bool),
+ dest_file, bkpt_list, append);
+
+ SBError sberr;
+ TargetSP target_sp(GetSP());
+ if (!target_sp) {
+ sberr.SetErrorString("BreakpointWriteToFile called with invalid target.");
+ return LLDB_RECORD_RESULT(sberr);
+ }
+
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ BreakpointIDList bp_id_list;
+ bkpt_list.CopyToBreakpointIDList(bp_id_list);
+ sberr.ref() = target_sp->SerializeBreakpointsToFile(dest_file.ref(),
+ bp_id_list, append);
+ return LLDB_RECORD_RESULT(sberr);
+}
+
+uint32_t SBTarget::GetNumWatchpoints() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBTarget, GetNumWatchpoints);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ // The watchpoint list is thread safe, no need to lock
+ return target_sp->GetWatchpointList().GetSize();
+ }
+ return 0;
+}
+
+SBWatchpoint SBTarget::GetWatchpointAtIndex(uint32_t idx) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBWatchpoint, SBTarget, GetWatchpointAtIndex,
+ (uint32_t), idx);
+
+ SBWatchpoint sb_watchpoint;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ // The watchpoint list is thread safe, no need to lock
+ sb_watchpoint.SetSP(target_sp->GetWatchpointList().GetByIndex(idx));
+ }
+ return LLDB_RECORD_RESULT(sb_watchpoint);
+}
+
+bool SBTarget::DeleteWatchpoint(watch_id_t wp_id) {
+ LLDB_RECORD_METHOD(bool, SBTarget, DeleteWatchpoint, (lldb::watch_id_t),
+ wp_id);
+
+
+ bool result = false;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ std::unique_lock<std::recursive_mutex> lock;
+ target_sp->GetWatchpointList().GetListMutex(lock);
+ result = target_sp->RemoveWatchpointByID(wp_id);
+ }
+
+ return result;
+}
+
+SBWatchpoint SBTarget::FindWatchpointByID(lldb::watch_id_t wp_id) {
+ LLDB_RECORD_METHOD(lldb::SBWatchpoint, SBTarget, FindWatchpointByID,
+ (lldb::watch_id_t), wp_id);
+
+
+ SBWatchpoint sb_watchpoint;
+ lldb::WatchpointSP watchpoint_sp;
+ TargetSP target_sp(GetSP());
+ if (target_sp && wp_id != LLDB_INVALID_WATCH_ID) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ std::unique_lock<std::recursive_mutex> lock;
+ target_sp->GetWatchpointList().GetListMutex(lock);
+ watchpoint_sp = target_sp->GetWatchpointList().FindByID(wp_id);
+ sb_watchpoint.SetSP(watchpoint_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_watchpoint);
+}
+
+lldb::SBWatchpoint SBTarget::WatchAddress(lldb::addr_t addr, size_t size,
+ bool read, bool write,
+ SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBWatchpoint, SBTarget, WatchAddress,
+ (lldb::addr_t, size_t, bool, bool, lldb::SBError &), addr,
+ size, read, write, error);
+
+ SBWatchpoint sb_watchpoint;
+ lldb::WatchpointSP watchpoint_sp;
+ TargetSP target_sp(GetSP());
+ if (target_sp && (read || write) && addr != LLDB_INVALID_ADDRESS &&
+ size > 0) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ uint32_t watch_type = 0;
+ if (read)
+ watch_type |= LLDB_WATCH_TYPE_READ;
+ if (write)
+ watch_type |= LLDB_WATCH_TYPE_WRITE;
+ if (watch_type == 0) {
+ error.SetErrorString(
+ "Can't create a watchpoint that is neither read nor write.");
+ return LLDB_RECORD_RESULT(sb_watchpoint);
+ }
+
+ // Target::CreateWatchpoint() is thread safe.
+ Status cw_error;
+ // This API doesn't take in a type, so we can't figure out what it is.
+ CompilerType *type = nullptr;
+ watchpoint_sp =
+ target_sp->CreateWatchpoint(addr, size, type, watch_type, cw_error);
+ error.SetError(cw_error);
+ sb_watchpoint.SetSP(watchpoint_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_watchpoint);
+}
+
+bool SBTarget::EnableAllWatchpoints() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, EnableAllWatchpoints);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ std::unique_lock<std::recursive_mutex> lock;
+ target_sp->GetWatchpointList().GetListMutex(lock);
+ target_sp->EnableAllWatchpoints();
+ return true;
+ }
+ return false;
+}
+
+bool SBTarget::DisableAllWatchpoints() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, DisableAllWatchpoints);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ std::unique_lock<std::recursive_mutex> lock;
+ target_sp->GetWatchpointList().GetListMutex(lock);
+ target_sp->DisableAllWatchpoints();
+ return true;
+ }
+ return false;
+}
+
+SBValue SBTarget::CreateValueFromAddress(const char *name, SBAddress addr,
+ SBType type) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, CreateValueFromAddress,
+ (const char *, lldb::SBAddress, lldb::SBType), name, addr,
+ type);
+
+ SBValue sb_value;
+ lldb::ValueObjectSP new_value_sp;
+ if (IsValid() && name && *name && addr.IsValid() && type.IsValid()) {
+ lldb::addr_t load_addr(addr.GetLoadAddress(*this));
+ ExecutionContext exe_ctx(
+ ExecutionContextRef(ExecutionContext(m_opaque_sp.get(), false)));
+ CompilerType ast_type(type.GetSP()->GetCompilerType(true));
+ new_value_sp = ValueObject::CreateValueObjectFromAddress(name, load_addr,
+ exe_ctx, ast_type);
+ }
+ sb_value.SetSP(new_value_sp);
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+lldb::SBValue SBTarget::CreateValueFromData(const char *name, lldb::SBData data,
+ lldb::SBType type) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, CreateValueFromData,
+ (const char *, lldb::SBData, lldb::SBType), name, data,
+ type);
+
+ SBValue sb_value;
+ lldb::ValueObjectSP new_value_sp;
+ if (IsValid() && name && *name && data.IsValid() && type.IsValid()) {
+ DataExtractorSP extractor(*data);
+ ExecutionContext exe_ctx(
+ ExecutionContextRef(ExecutionContext(m_opaque_sp.get(), false)));
+ CompilerType ast_type(type.GetSP()->GetCompilerType(true));
+ new_value_sp = ValueObject::CreateValueObjectFromData(name, *extractor,
+ exe_ctx, ast_type);
+ }
+ sb_value.SetSP(new_value_sp);
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+lldb::SBValue SBTarget::CreateValueFromExpression(const char *name,
+ const char *expr) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, CreateValueFromExpression,
+ (const char *, const char *), name, expr);
+
+ SBValue sb_value;
+ lldb::ValueObjectSP new_value_sp;
+ if (IsValid() && name && *name && expr && *expr) {
+ ExecutionContext exe_ctx(
+ ExecutionContextRef(ExecutionContext(m_opaque_sp.get(), false)));
+ new_value_sp =
+ ValueObject::CreateValueObjectFromExpression(name, expr, exe_ctx);
+ }
+ sb_value.SetSP(new_value_sp);
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+bool SBTarget::DeleteAllWatchpoints() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, DeleteAllWatchpoints);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ std::unique_lock<std::recursive_mutex> lock;
+ target_sp->GetWatchpointList().GetListMutex(lock);
+ target_sp->RemoveAllWatchpoints();
+ return true;
+ }
+ return false;
+}
+
+void SBTarget::AppendImageSearchPath(const char *from, const char *to,
+ lldb::SBError &error) {
+ LLDB_RECORD_METHOD(void, SBTarget, AppendImageSearchPath,
+ (const char *, const char *, lldb::SBError &), from, to,
+ error);
+
+ TargetSP target_sp(GetSP());
+ if (!target_sp)
+ return error.SetErrorString("invalid target");
+
+ const ConstString csFrom(from), csTo(to);
+ if (!csFrom)
+ return error.SetErrorString("<from> path can't be empty");
+ if (!csTo)
+ return error.SetErrorString("<to> path can't be empty");
+
+ target_sp->GetImageSearchPathList().Append(csFrom, csTo, true);
+}
+
+lldb::SBModule SBTarget::AddModule(const char *path, const char *triple,
+ const char *uuid_cstr) {
+ LLDB_RECORD_METHOD(lldb::SBModule, SBTarget, AddModule,
+ (const char *, const char *, const char *), path, triple,
+ uuid_cstr);
+
+ return LLDB_RECORD_RESULT(AddModule(path, triple, uuid_cstr, nullptr));
+}
+
+lldb::SBModule SBTarget::AddModule(const char *path, const char *triple,
+ const char *uuid_cstr, const char *symfile) {
+ LLDB_RECORD_METHOD(lldb::SBModule, SBTarget, AddModule,
+ (const char *, const char *, const char *, const char *),
+ path, triple, uuid_cstr, symfile);
+
+ lldb::SBModule sb_module;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ ModuleSpec module_spec;
+ if (path)
+ module_spec.GetFileSpec().SetFile(path, FileSpec::Style::native);
+
+ if (uuid_cstr)
+ module_spec.GetUUID().SetFromStringRef(uuid_cstr);
+
+ if (triple)
+ module_spec.GetArchitecture() = Platform::GetAugmentedArchSpec(
+ target_sp->GetPlatform().get(), triple);
+ else
+ module_spec.GetArchitecture() = target_sp->GetArchitecture();
+
+ if (symfile)
+ module_spec.GetSymbolFileSpec().SetFile(symfile, FileSpec::Style::native);
+
+ sb_module.SetSP(target_sp->GetOrCreateModule(module_spec, true /* notify */));
+ }
+ return LLDB_RECORD_RESULT(sb_module);
+}
+
+lldb::SBModule SBTarget::AddModule(const SBModuleSpec &module_spec) {
+ LLDB_RECORD_METHOD(lldb::SBModule, SBTarget, AddModule,
+ (const lldb::SBModuleSpec &), module_spec);
+
+ lldb::SBModule sb_module;
+ TargetSP target_sp(GetSP());
+ if (target_sp)
+ sb_module.SetSP(target_sp->GetOrCreateModule(*module_spec.m_opaque_up,
+ true /* notify */));
+ return LLDB_RECORD_RESULT(sb_module);
+}
+
+bool SBTarget::AddModule(lldb::SBModule &module) {
+ LLDB_RECORD_METHOD(bool, SBTarget, AddModule, (lldb::SBModule &), module);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ target_sp->GetImages().AppendIfNeeded(module.GetSP());
+ return true;
+ }
+ return false;
+}
+
+uint32_t SBTarget::GetNumModules() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBTarget, GetNumModules);
+
+ uint32_t num = 0;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ // The module list is thread safe, no need to lock
+ num = target_sp->GetImages().GetSize();
+ }
+
+ return num;
+}
+
+void SBTarget::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBTarget, Clear);
+
+ m_opaque_sp.reset();
+}
+
+SBModule SBTarget::FindModule(const SBFileSpec &sb_file_spec) {
+ LLDB_RECORD_METHOD(lldb::SBModule, SBTarget, FindModule,
+ (const lldb::SBFileSpec &), sb_file_spec);
+
+ SBModule sb_module;
+ TargetSP target_sp(GetSP());
+ if (target_sp && sb_file_spec.IsValid()) {
+ ModuleSpec module_spec(*sb_file_spec);
+ // The module list is thread safe, no need to lock
+ sb_module.SetSP(target_sp->GetImages().FindFirstModule(module_spec));
+ }
+ return LLDB_RECORD_RESULT(sb_module);
+}
+
+SBSymbolContextList SBTarget::FindCompileUnits(const SBFileSpec &sb_file_spec) {
+ LLDB_RECORD_METHOD(lldb::SBSymbolContextList, SBTarget, FindCompileUnits,
+ (const lldb::SBFileSpec &), sb_file_spec);
+
+ SBSymbolContextList sb_sc_list;
+ const TargetSP target_sp(GetSP());
+ if (target_sp && sb_file_spec.IsValid()) {
+ const bool append = true;
+ target_sp->GetImages().FindCompileUnits(*sb_file_spec,
+ append, *sb_sc_list);
+ }
+ return LLDB_RECORD_RESULT(sb_sc_list);
+}
+
+lldb::ByteOrder SBTarget::GetByteOrder() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::ByteOrder, SBTarget, GetByteOrder);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp)
+ return target_sp->GetArchitecture().GetByteOrder();
+ return eByteOrderInvalid;
+}
+
+const char *SBTarget::GetTriple() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTarget, GetTriple);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::string triple(target_sp->GetArchitecture().GetTriple().str());
+ // Unique the string so we don't run into ownership issues since the const
+ // strings put the string into the string pool once and the strings never
+ // comes out
+ ConstString const_triple(triple.c_str());
+ return const_triple.GetCString();
+ }
+ return nullptr;
+}
+
+uint32_t SBTarget::GetDataByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTarget, GetDataByteSize);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ return target_sp->GetArchitecture().GetDataByteSize();
+ }
+ return 0;
+}
+
+uint32_t SBTarget::GetCodeByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTarget, GetCodeByteSize);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ return target_sp->GetArchitecture().GetCodeByteSize();
+ }
+ return 0;
+}
+
+uint32_t SBTarget::GetAddressByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTarget, GetAddressByteSize);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp)
+ return target_sp->GetArchitecture().GetAddressByteSize();
+ return sizeof(void *);
+}
+
+SBModule SBTarget::GetModuleAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBModule, SBTarget, GetModuleAtIndex, (uint32_t),
+ idx);
+
+ SBModule sb_module;
+ ModuleSP module_sp;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ // The module list is thread safe, no need to lock
+ module_sp = target_sp->GetImages().GetModuleAtIndex(idx);
+ sb_module.SetSP(module_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_module);
+}
+
+bool SBTarget::RemoveModule(lldb::SBModule module) {
+ LLDB_RECORD_METHOD(bool, SBTarget, RemoveModule, (lldb::SBModule), module);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp)
+ return target_sp->GetImages().Remove(module.GetSP());
+ return false;
+}
+
+SBBroadcaster SBTarget::GetBroadcaster() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBBroadcaster, SBTarget,
+ GetBroadcaster);
+
+
+ TargetSP target_sp(GetSP());
+ SBBroadcaster broadcaster(target_sp.get(), false);
+
+
+ return LLDB_RECORD_RESULT(broadcaster);
+}
+
+bool SBTarget::GetDescription(SBStream &description,
+ lldb::DescriptionLevel description_level) {
+ LLDB_RECORD_METHOD(bool, SBTarget, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ description_level);
+
+ Stream &strm = description.ref();
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ target_sp->Dump(&strm, description_level);
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+lldb::SBSymbolContextList SBTarget::FindFunctions(const char *name,
+ uint32_t name_type_mask) {
+ LLDB_RECORD_METHOD(lldb::SBSymbolContextList, SBTarget, FindFunctions,
+ (const char *, uint32_t), name, name_type_mask);
+
+ lldb::SBSymbolContextList sb_sc_list;
+ if (!name | !name[0])
+ return LLDB_RECORD_RESULT(sb_sc_list);
+
+ TargetSP target_sp(GetSP());
+ if (!target_sp)
+ return LLDB_RECORD_RESULT(sb_sc_list);
+
+ const bool symbols_ok = true;
+ const bool inlines_ok = true;
+ const bool append = true;
+ FunctionNameType mask = static_cast<FunctionNameType>(name_type_mask);
+ target_sp->GetImages().FindFunctions(ConstString(name), mask, symbols_ok,
+ inlines_ok, append, *sb_sc_list);
+ return LLDB_RECORD_RESULT(sb_sc_list);
+}
+
+lldb::SBSymbolContextList SBTarget::FindGlobalFunctions(const char *name,
+ uint32_t max_matches,
+ MatchType matchtype) {
+ LLDB_RECORD_METHOD(lldb::SBSymbolContextList, SBTarget, FindGlobalFunctions,
+ (const char *, uint32_t, lldb::MatchType), name,
+ max_matches, matchtype);
+
+ lldb::SBSymbolContextList sb_sc_list;
+ if (name && name[0]) {
+ llvm::StringRef name_ref(name);
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ std::string regexstr;
+ switch (matchtype) {
+ case eMatchTypeRegex:
+ target_sp->GetImages().FindFunctions(RegularExpression(name_ref), true,
+ true, true, *sb_sc_list);
+ break;
+ case eMatchTypeStartsWith:
+ regexstr = llvm::Regex::escape(name) + ".*";
+ target_sp->GetImages().FindFunctions(RegularExpression(regexstr), true,
+ true, true, *sb_sc_list);
+ break;
+ default:
+ target_sp->GetImages().FindFunctions(ConstString(name),
+ eFunctionNameTypeAny, true, true,
+ true, *sb_sc_list);
+ break;
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_sc_list);
+}
+
+lldb::SBType SBTarget::FindFirstType(const char *typename_cstr) {
+ LLDB_RECORD_METHOD(lldb::SBType, SBTarget, FindFirstType, (const char *),
+ typename_cstr);
+
+ TargetSP target_sp(GetSP());
+ if (typename_cstr && typename_cstr[0] && target_sp) {
+ ConstString const_typename(typename_cstr);
+ SymbolContext sc;
+ const bool exact_match = false;
+
+ const ModuleList &module_list = target_sp->GetImages();
+ size_t count = module_list.GetSize();
+ for (size_t idx = 0; idx < count; idx++) {
+ ModuleSP module_sp(module_list.GetModuleAtIndex(idx));
+ if (module_sp) {
+ TypeSP type_sp(
+ module_sp->FindFirstType(sc, const_typename, exact_match));
+ if (type_sp)
+ return LLDB_RECORD_RESULT(SBType(type_sp));
+ }
+ }
+
+ // Didn't find the type in the symbols; Try the loaded language runtimes
+ if (auto process_sp = target_sp->GetProcessSP()) {
+ for (auto *runtime : process_sp->GetLanguageRuntimes()) {
+ if (auto vendor = runtime->GetDeclVendor()) {
+ auto types = vendor->FindTypes(const_typename, /*max_matches*/ 1);
+ if (!types.empty())
+ return LLDB_RECORD_RESULT(SBType(types.front()));
+ }
+ }
+ }
+
+ // No matches, search for basic typename matches
+ ClangASTContext *clang_ast = target_sp->GetScratchClangASTContext();
+ if (clang_ast)
+ return LLDB_RECORD_RESULT(SBType(ClangASTContext::GetBasicType(
+ clang_ast->getASTContext(), const_typename)));
+ }
+ return LLDB_RECORD_RESULT(SBType());
+}
+
+SBType SBTarget::GetBasicType(lldb::BasicType type) {
+ LLDB_RECORD_METHOD(lldb::SBType, SBTarget, GetBasicType, (lldb::BasicType),
+ type);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ ClangASTContext *clang_ast = target_sp->GetScratchClangASTContext();
+ if (clang_ast)
+ return LLDB_RECORD_RESULT(SBType(
+ ClangASTContext::GetBasicType(clang_ast->getASTContext(), type)));
+ }
+ return LLDB_RECORD_RESULT(SBType());
+}
+
+lldb::SBTypeList SBTarget::FindTypes(const char *typename_cstr) {
+ LLDB_RECORD_METHOD(lldb::SBTypeList, SBTarget, FindTypes, (const char *),
+ typename_cstr);
+
+ SBTypeList sb_type_list;
+ TargetSP target_sp(GetSP());
+ if (typename_cstr && typename_cstr[0] && target_sp) {
+ ModuleList &images = target_sp->GetImages();
+ ConstString const_typename(typename_cstr);
+ bool exact_match = false;
+ TypeList type_list;
+ llvm::DenseSet<SymbolFile *> searched_symbol_files;
+ uint32_t num_matches =
+ images.FindTypes(nullptr, const_typename, exact_match, UINT32_MAX,
+ searched_symbol_files, type_list);
+
+ if (num_matches > 0) {
+ for (size_t idx = 0; idx < num_matches; idx++) {
+ TypeSP type_sp(type_list.GetTypeAtIndex(idx));
+ if (type_sp)
+ sb_type_list.Append(SBType(type_sp));
+ }
+ }
+
+ // Try the loaded language runtimes
+ if (auto process_sp = target_sp->GetProcessSP()) {
+ for (auto *runtime : process_sp->GetLanguageRuntimes()) {
+ if (auto *vendor = runtime->GetDeclVendor()) {
+ auto types =
+ vendor->FindTypes(const_typename, /*max_matches*/ UINT32_MAX);
+ for (auto type : types)
+ sb_type_list.Append(SBType(type));
+ }
+ }
+ }
+
+ if (sb_type_list.GetSize() == 0) {
+ // No matches, search for basic typename matches
+ ClangASTContext *clang_ast = target_sp->GetScratchClangASTContext();
+ if (clang_ast)
+ sb_type_list.Append(SBType(ClangASTContext::GetBasicType(
+ clang_ast->getASTContext(), const_typename)));
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_type_list);
+}
+
+SBValueList SBTarget::FindGlobalVariables(const char *name,
+ uint32_t max_matches) {
+ LLDB_RECORD_METHOD(lldb::SBValueList, SBTarget, FindGlobalVariables,
+ (const char *, uint32_t), name, max_matches);
+
+ SBValueList sb_value_list;
+
+ TargetSP target_sp(GetSP());
+ if (name && target_sp) {
+ VariableList variable_list;
+ const uint32_t match_count = target_sp->GetImages().FindGlobalVariables(
+ ConstString(name), max_matches, variable_list);
+
+ if (match_count > 0) {
+ ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get();
+ if (exe_scope == nullptr)
+ exe_scope = target_sp.get();
+ for (uint32_t i = 0; i < match_count; ++i) {
+ lldb::ValueObjectSP valobj_sp(ValueObjectVariable::Create(
+ exe_scope, variable_list.GetVariableAtIndex(i)));
+ if (valobj_sp)
+ sb_value_list.Append(SBValue(valobj_sp));
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_value_list);
+}
+
+SBValueList SBTarget::FindGlobalVariables(const char *name,
+ uint32_t max_matches,
+ MatchType matchtype) {
+ LLDB_RECORD_METHOD(lldb::SBValueList, SBTarget, FindGlobalVariables,
+ (const char *, uint32_t, lldb::MatchType), name,
+ max_matches, matchtype);
+
+ SBValueList sb_value_list;
+
+ TargetSP target_sp(GetSP());
+ if (name && target_sp) {
+ llvm::StringRef name_ref(name);
+ VariableList variable_list;
+
+ std::string regexstr;
+ uint32_t match_count;
+ switch (matchtype) {
+ case eMatchTypeNormal:
+ match_count = target_sp->GetImages().FindGlobalVariables(
+ ConstString(name), max_matches, variable_list);
+ break;
+ case eMatchTypeRegex:
+ match_count = target_sp->GetImages().FindGlobalVariables(
+ RegularExpression(name_ref), max_matches, variable_list);
+ break;
+ case eMatchTypeStartsWith:
+ regexstr = llvm::Regex::escape(name) + ".*";
+ match_count = target_sp->GetImages().FindGlobalVariables(
+ RegularExpression(regexstr), max_matches, variable_list);
+ break;
+ }
+
+ if (match_count > 0) {
+ ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get();
+ if (exe_scope == nullptr)
+ exe_scope = target_sp.get();
+ for (uint32_t i = 0; i < match_count; ++i) {
+ lldb::ValueObjectSP valobj_sp(ValueObjectVariable::Create(
+ exe_scope, variable_list.GetVariableAtIndex(i)));
+ if (valobj_sp)
+ sb_value_list.Append(SBValue(valobj_sp));
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_value_list);
+}
+
+lldb::SBValue SBTarget::FindFirstGlobalVariable(const char *name) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, FindFirstGlobalVariable,
+ (const char *), name);
+
+ SBValueList sb_value_list(FindGlobalVariables(name, 1));
+ if (sb_value_list.IsValid() && sb_value_list.GetSize() > 0)
+ return LLDB_RECORD_RESULT(sb_value_list.GetValueAtIndex(0));
+ return LLDB_RECORD_RESULT(SBValue());
+}
+
+SBSourceManager SBTarget::GetSourceManager() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBSourceManager, SBTarget, GetSourceManager);
+
+ SBSourceManager source_manager(*this);
+ return LLDB_RECORD_RESULT(source_manager);
+}
+
+lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress base_addr,
+ uint32_t count) {
+ LLDB_RECORD_METHOD(lldb::SBInstructionList, SBTarget, ReadInstructions,
+ (lldb::SBAddress, uint32_t), base_addr, count);
+
+ return LLDB_RECORD_RESULT(ReadInstructions(base_addr, count, nullptr));
+}
+
+lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress base_addr,
+ uint32_t count,
+ const char *flavor_string) {
+ LLDB_RECORD_METHOD(lldb::SBInstructionList, SBTarget, ReadInstructions,
+ (lldb::SBAddress, uint32_t, const char *), base_addr,
+ count, flavor_string);
+
+ SBInstructionList sb_instructions;
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ Address *addr_ptr = base_addr.get();
+
+ if (addr_ptr) {
+ DataBufferHeap data(
+ target_sp->GetArchitecture().GetMaximumOpcodeByteSize() * count, 0);
+ bool prefer_file_cache = false;
+ lldb_private::Status error;
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ const size_t bytes_read =
+ target_sp->ReadMemory(*addr_ptr, prefer_file_cache, data.GetBytes(),
+ data.GetByteSize(), error, &load_addr);
+ const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS;
+ sb_instructions.SetDisassembler(Disassembler::DisassembleBytes(
+ target_sp->GetArchitecture(), nullptr, flavor_string, *addr_ptr,
+ data.GetBytes(), bytes_read, count, data_from_file));
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_instructions);
+}
+
+lldb::SBInstructionList SBTarget::GetInstructions(lldb::SBAddress base_addr,
+ const void *buf,
+ size_t size) {
+ LLDB_RECORD_DUMMY(lldb::SBInstructionList, SBTarget, GetInstructions,
+ (lldb::SBAddress, const void *, size_t), base_addr, buf,
+ size);
+
+ return GetInstructionsWithFlavor(base_addr, nullptr, buf, size);
+}
+
+lldb::SBInstructionList
+SBTarget::GetInstructionsWithFlavor(lldb::SBAddress base_addr,
+ const char *flavor_string, const void *buf,
+ size_t size) {
+ LLDB_RECORD_DUMMY(lldb::SBInstructionList, SBTarget,
+ GetInstructionsWithFlavor,
+ (lldb::SBAddress, const char *, const void *, size_t),
+ base_addr, flavor_string, buf, size);
+
+ SBInstructionList sb_instructions;
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ Address addr;
+
+ if (base_addr.get())
+ addr = *base_addr.get();
+
+ const bool data_from_file = true;
+
+ sb_instructions.SetDisassembler(Disassembler::DisassembleBytes(
+ target_sp->GetArchitecture(), nullptr, flavor_string, addr, buf, size,
+ UINT32_MAX, data_from_file));
+ }
+
+ return sb_instructions;
+}
+
+lldb::SBInstructionList SBTarget::GetInstructions(lldb::addr_t base_addr,
+ const void *buf,
+ size_t size) {
+ LLDB_RECORD_DUMMY(lldb::SBInstructionList, SBTarget, GetInstructions,
+ (lldb::addr_t, const void *, size_t), base_addr, buf, size);
+
+ return GetInstructionsWithFlavor(ResolveLoadAddress(base_addr), nullptr, buf,
+ size);
+}
+
+lldb::SBInstructionList
+SBTarget::GetInstructionsWithFlavor(lldb::addr_t base_addr,
+ const char *flavor_string, const void *buf,
+ size_t size) {
+ LLDB_RECORD_DUMMY(lldb::SBInstructionList, SBTarget,
+ GetInstructionsWithFlavor,
+ (lldb::addr_t, const char *, const void *, size_t),
+ base_addr, flavor_string, buf, size);
+
+ return GetInstructionsWithFlavor(ResolveLoadAddress(base_addr), flavor_string,
+ buf, size);
+}
+
+SBError SBTarget::SetSectionLoadAddress(lldb::SBSection section,
+ lldb::addr_t section_base_addr) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBTarget, SetSectionLoadAddress,
+ (lldb::SBSection, lldb::addr_t), section,
+ section_base_addr);
+
+ SBError sb_error;
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ if (!section.IsValid()) {
+ sb_error.SetErrorStringWithFormat("invalid section");
+ } else {
+ SectionSP section_sp(section.GetSP());
+ if (section_sp) {
+ if (section_sp->IsThreadSpecific()) {
+ sb_error.SetErrorString(
+ "thread specific sections are not yet supported");
+ } else {
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (target_sp->SetSectionLoadAddress(section_sp, section_base_addr)) {
+ ModuleSP module_sp(section_sp->GetModule());
+ if (module_sp) {
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ target_sp->ModulesDidLoad(module_list);
+ }
+ // Flush info in the process (stack frames, etc)
+ if (process_sp)
+ process_sp->Flush();
+ }
+ }
+ }
+ }
+ } else {
+ sb_error.SetErrorString("invalid target");
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBTarget::ClearSectionLoadAddress(lldb::SBSection section) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBTarget, ClearSectionLoadAddress,
+ (lldb::SBSection), section);
+
+ SBError sb_error;
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ if (!section.IsValid()) {
+ sb_error.SetErrorStringWithFormat("invalid section");
+ } else {
+ SectionSP section_sp(section.GetSP());
+ if (section_sp) {
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (target_sp->SetSectionUnloaded(section_sp)) {
+ ModuleSP module_sp(section_sp->GetModule());
+ if (module_sp) {
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ target_sp->ModulesDidUnload(module_list, false);
+ }
+ // Flush info in the process (stack frames, etc)
+ if (process_sp)
+ process_sp->Flush();
+ }
+ } else {
+ sb_error.SetErrorStringWithFormat("invalid section");
+ }
+ }
+ } else {
+ sb_error.SetErrorStringWithFormat("invalid target");
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBTarget::SetModuleLoadAddress(lldb::SBModule module,
+ int64_t slide_offset) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBTarget, SetModuleLoadAddress,
+ (lldb::SBModule, int64_t), module, slide_offset);
+
+ SBError sb_error;
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ ModuleSP module_sp(module.GetSP());
+ if (module_sp) {
+ bool changed = false;
+ if (module_sp->SetLoadAddress(*target_sp, slide_offset, true, changed)) {
+ // The load was successful, make sure that at least some sections
+ // changed before we notify that our module was loaded.
+ if (changed) {
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ target_sp->ModulesDidLoad(module_list);
+ // Flush info in the process (stack frames, etc)
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp)
+ process_sp->Flush();
+ }
+ }
+ } else {
+ sb_error.SetErrorStringWithFormat("invalid module");
+ }
+
+ } else {
+ sb_error.SetErrorStringWithFormat("invalid target");
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBTarget::ClearModuleLoadAddress(lldb::SBModule module) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBTarget, ClearModuleLoadAddress,
+ (lldb::SBModule), module);
+
+ SBError sb_error;
+
+ char path[PATH_MAX];
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ ModuleSP module_sp(module.GetSP());
+ if (module_sp) {
+ ObjectFile *objfile = module_sp->GetObjectFile();
+ if (objfile) {
+ SectionList *section_list = objfile->GetSectionList();
+ if (section_list) {
+ ProcessSP process_sp(target_sp->GetProcessSP());
+
+ bool changed = false;
+ const size_t num_sections = section_list->GetSize();
+ for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
+ SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
+ if (section_sp)
+ changed |= target_sp->SetSectionUnloaded(section_sp);
+ }
+ if (changed) {
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ target_sp->ModulesDidUnload(module_list, false);
+ // Flush info in the process (stack frames, etc)
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp)
+ process_sp->Flush();
+ }
+ } else {
+ module_sp->GetFileSpec().GetPath(path, sizeof(path));
+ sb_error.SetErrorStringWithFormat("no sections in object file '%s'",
+ path);
+ }
+ } else {
+ module_sp->GetFileSpec().GetPath(path, sizeof(path));
+ sb_error.SetErrorStringWithFormat("no object file for module '%s'",
+ path);
+ }
+ } else {
+ sb_error.SetErrorStringWithFormat("invalid module");
+ }
+ } else {
+ sb_error.SetErrorStringWithFormat("invalid target");
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+lldb::SBSymbolContextList SBTarget::FindSymbols(const char *name,
+ lldb::SymbolType symbol_type) {
+ LLDB_RECORD_METHOD(lldb::SBSymbolContextList, SBTarget, FindSymbols,
+ (const char *, lldb::SymbolType), name, symbol_type);
+
+ SBSymbolContextList sb_sc_list;
+ if (name && name[0]) {
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ bool append = true;
+ target_sp->GetImages().FindSymbolsWithNameAndType(
+ ConstString(name), symbol_type, *sb_sc_list, append);
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_sc_list);
+}
+
+lldb::SBValue SBTarget::EvaluateExpression(const char *expr) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, EvaluateExpression,
+ (const char *), expr);
+
+ TargetSP target_sp(GetSP());
+ if (!target_sp)
+ return LLDB_RECORD_RESULT(SBValue());
+
+ SBExpressionOptions options;
+ lldb::DynamicValueType fetch_dynamic_value =
+ target_sp->GetPreferDynamicValue();
+ options.SetFetchDynamicValue(fetch_dynamic_value);
+ options.SetUnwindOnError(true);
+ return LLDB_RECORD_RESULT(EvaluateExpression(expr, options));
+}
+
+lldb::SBValue SBTarget::EvaluateExpression(const char *expr,
+ const SBExpressionOptions &options) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, EvaluateExpression,
+ (const char *, const lldb::SBExpressionOptions &), expr,
+ options);
+
+ Log *expr_log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+ SBValue expr_result;
+ ValueObjectSP expr_value_sp;
+ TargetSP target_sp(GetSP());
+ StackFrame *frame = nullptr;
+ if (target_sp) {
+ if (expr == nullptr || expr[0] == '\0') {
+ return LLDB_RECORD_RESULT(expr_result);
+ }
+
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ ExecutionContext exe_ctx(m_opaque_sp.get());
+
+
+ frame = exe_ctx.GetFramePtr();
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (target) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ StreamString frame_description;
+ if (frame)
+ frame->DumpUsingSettingsFormat(&frame_description);
+ llvm::PrettyStackTraceFormat stack_trace(
+ "SBTarget::EvaluateExpression (expr = \"%s\", fetch_dynamic_value = "
+ "%u) %s",
+ expr, options.GetFetchDynamicValue(),
+ frame_description.GetString().str().c_str());
+#endif
+ target->EvaluateExpression(expr, frame, expr_value_sp, options.ref());
+
+ expr_result.SetSP(expr_value_sp, options.GetFetchDynamicValue());
+ }
+ }
+ if (expr_log)
+ expr_log->Printf("** [SBTarget::EvaluateExpression] Expression result is "
+ "%s, summary %s **",
+ expr_result.GetValue(), expr_result.GetSummary());
+ return LLDB_RECORD_RESULT(expr_result);
+}
+
+lldb::addr_t SBTarget::GetStackRedZoneSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBTarget, GetStackRedZoneSize);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp) {
+ ABISP abi_sp;
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp)
+ abi_sp = process_sp->GetABI();
+ else
+ abi_sp = ABI::FindPlugin(ProcessSP(), target_sp->GetArchitecture());
+ if (abi_sp)
+ return abi_sp->GetRedZoneSize();
+ }
+ return 0;
+}
+
+lldb::SBLaunchInfo SBTarget::GetLaunchInfo() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBLaunchInfo, SBTarget, GetLaunchInfo);
+
+ lldb::SBLaunchInfo launch_info(nullptr);
+ TargetSP target_sp(GetSP());
+ if (target_sp)
+ launch_info.set_ref(m_opaque_sp->GetProcessLaunchInfo());
+ return LLDB_RECORD_RESULT(launch_info);
+}
+
+void SBTarget::SetLaunchInfo(const lldb::SBLaunchInfo &launch_info) {
+ LLDB_RECORD_METHOD(void, SBTarget, SetLaunchInfo,
+ (const lldb::SBLaunchInfo &), launch_info);
+
+ TargetSP target_sp(GetSP());
+ if (target_sp)
+ m_opaque_sp->SetProcessLaunchInfo(launch_info.ref());
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBTarget>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBTarget, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTarget, (const lldb::SBTarget &));
+ LLDB_REGISTER_CONSTRUCTOR(SBTarget, (const lldb::TargetSP &));
+ LLDB_REGISTER_METHOD(const lldb::SBTarget &,
+ SBTarget, operator=,(const lldb::SBTarget &));
+ LLDB_REGISTER_STATIC_METHOD(bool, SBTarget, EventIsTargetEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBTarget, SBTarget, GetTargetFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(uint32_t, SBTarget, GetNumModulesFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBModule, SBTarget,
+ GetModuleAtIndexFromEvent,
+ (const uint32_t, const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBTarget, GetBroadcasterClassName,
+ ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTarget, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTarget, operator bool, ());
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, GetProcess, ());
+ LLDB_REGISTER_METHOD(lldb::SBPlatform, SBTarget, GetPlatform, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBDebugger, SBTarget, GetDebugger, ());
+ LLDB_REGISTER_METHOD(lldb::SBStructuredData, SBTarget, GetStatistics, ());
+ LLDB_REGISTER_METHOD(void, SBTarget, SetCollectingStats, (bool));
+ LLDB_REGISTER_METHOD(bool, SBTarget, GetCollectingStats, ());
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, LoadCore, (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, LoadCore,
+ (const char *, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, LaunchSimple,
+ (const char **, const char **, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, Install, ());
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, Launch,
+ (lldb::SBListener &, const char **, const char **,
+ const char *, const char *, const char *,
+ const char *, uint32_t, bool, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, Launch,
+ (lldb::SBLaunchInfo &, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, Attach,
+ (lldb::SBAttachInfo &, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, AttachToProcessWithID,
+ (lldb::SBListener &, lldb::pid_t, lldb::SBError &));
+ LLDB_REGISTER_METHOD(
+ lldb::SBProcess, SBTarget, AttachToProcessWithName,
+ (lldb::SBListener &, const char *, bool, lldb::SBError &));
+ LLDB_REGISTER_METHOD(
+ lldb::SBProcess, SBTarget, ConnectRemote,
+ (lldb::SBListener &, const char *, const char *, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBFileSpec, SBTarget, GetExecutable, ());
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBTarget, operator==,(const lldb::SBTarget &));
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBTarget, operator!=,(const lldb::SBTarget &));
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBTarget, ResolveLoadAddress,
+ (lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBTarget, ResolveFileAddress,
+ (lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBTarget, ResolvePastLoadAddress,
+ (uint32_t, lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::SBSymbolContext, SBTarget,
+ ResolveSymbolContextForAddress,
+ (const lldb::SBAddress &, uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateByLocation, (const char *, uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateByLocation,
+ (const lldb::SBFileSpec &, uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateByLocation,
+ (const lldb::SBFileSpec &, uint32_t, lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateByLocation,
+ (const lldb::SBFileSpec &, uint32_t, lldb::addr_t,
+ lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateByLocation,
+ (const lldb::SBFileSpec &, uint32_t, uint32_t,
+ lldb::addr_t, lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName,
+ (const char *, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName,
+ (const char *, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName,
+ (const char *, uint32_t, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName,
+ (const char *, uint32_t, lldb::LanguageType,
+ const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames,
+ (const char **, uint32_t, uint32_t,
+ const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames,
+ (const char **, uint32_t, uint32_t, lldb::LanguageType,
+ const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames,
+ (const char **, uint32_t, uint32_t, lldb::LanguageType,
+ lldb::addr_t, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex,
+ (const char *, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex,
+ (const char *, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex,
+ (const char *, lldb::LanguageType,
+ const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateByAddress, (lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateBySBAddress, (lldb::SBAddress &));
+ LLDB_REGISTER_METHOD(
+ lldb::SBBreakpoint, SBTarget, BreakpointCreateBySourceRegex,
+ (const char *, const lldb::SBFileSpec &, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateBySourceRegex,
+ (const char *, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &));
+ LLDB_REGISTER_METHOD(
+ lldb::SBBreakpoint, SBTarget, BreakpointCreateBySourceRegex,
+ (const char *, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &, const lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget,
+ BreakpointCreateForException,
+ (lldb::LanguageType, bool, bool));
+ LLDB_REGISTER_METHOD(
+ lldb::SBBreakpoint, SBTarget, BreakpointCreateFromScript,
+ (const char *, lldb::SBStructuredData &, const lldb::SBFileSpecList &,
+ const lldb::SBFileSpecList &, bool));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBTarget, GetNumBreakpoints, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBBreakpoint, SBTarget,
+ GetBreakpointAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBTarget, BreakpointDelete, (lldb::break_id_t));
+ LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, FindBreakpointByID,
+ (lldb::break_id_t));
+ LLDB_REGISTER_METHOD(bool, SBTarget, FindBreakpointsByName,
+ (const char *, lldb::SBBreakpointList &));
+ LLDB_REGISTER_METHOD(void, SBTarget, GetBreakpointNames,
+ (lldb::SBStringList &));
+ LLDB_REGISTER_METHOD(void, SBTarget, DeleteBreakpointName, (const char *));
+ LLDB_REGISTER_METHOD(bool, SBTarget, EnableAllBreakpoints, ());
+ LLDB_REGISTER_METHOD(bool, SBTarget, DisableAllBreakpoints, ());
+ LLDB_REGISTER_METHOD(bool, SBTarget, DeleteAllBreakpoints, ());
+ LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, BreakpointsCreateFromFile,
+ (lldb::SBFileSpec &, lldb::SBBreakpointList &));
+ LLDB_REGISTER_METHOD(
+ lldb::SBError, SBTarget, BreakpointsCreateFromFile,
+ (lldb::SBFileSpec &, lldb::SBStringList &, lldb::SBBreakpointList &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, BreakpointsWriteToFile,
+ (lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, BreakpointsWriteToFile,
+ (lldb::SBFileSpec &, lldb::SBBreakpointList &, bool));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBTarget, GetNumWatchpoints, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBWatchpoint, SBTarget,
+ GetWatchpointAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBTarget, DeleteWatchpoint, (lldb::watch_id_t));
+ LLDB_REGISTER_METHOD(lldb::SBWatchpoint, SBTarget, FindWatchpointByID,
+ (lldb::watch_id_t));
+ LLDB_REGISTER_METHOD(lldb::SBWatchpoint, SBTarget, WatchAddress,
+ (lldb::addr_t, size_t, bool, bool, lldb::SBError &));
+ LLDB_REGISTER_METHOD(bool, SBTarget, EnableAllWatchpoints, ());
+ LLDB_REGISTER_METHOD(bool, SBTarget, DisableAllWatchpoints, ());
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, CreateValueFromAddress,
+ (const char *, lldb::SBAddress, lldb::SBType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, CreateValueFromData,
+ (const char *, lldb::SBData, lldb::SBType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, CreateValueFromExpression,
+ (const char *, const char *));
+ LLDB_REGISTER_METHOD(bool, SBTarget, DeleteAllWatchpoints, ());
+ LLDB_REGISTER_METHOD(void, SBTarget, AppendImageSearchPath,
+ (const char *, const char *, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBModule, SBTarget, AddModule,
+ (const char *, const char *, const char *));
+ LLDB_REGISTER_METHOD(
+ lldb::SBModule, SBTarget, AddModule,
+ (const char *, const char *, const char *, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBModule, SBTarget, AddModule,
+ (const lldb::SBModuleSpec &));
+ LLDB_REGISTER_METHOD(bool, SBTarget, AddModule, (lldb::SBModule &));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBTarget, GetNumModules, ());
+ LLDB_REGISTER_METHOD(void, SBTarget, Clear, ());
+ LLDB_REGISTER_METHOD(lldb::SBModule, SBTarget, FindModule,
+ (const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(lldb::SBSymbolContextList, SBTarget, FindCompileUnits,
+ (const lldb::SBFileSpec &));
+ LLDB_REGISTER_METHOD(lldb::ByteOrder, SBTarget, GetByteOrder, ());
+ LLDB_REGISTER_METHOD(const char *, SBTarget, GetTriple, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTarget, GetDataByteSize, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTarget, GetCodeByteSize, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTarget, GetAddressByteSize, ());
+ LLDB_REGISTER_METHOD(lldb::SBModule, SBTarget, GetModuleAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBTarget, RemoveModule, (lldb::SBModule));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBBroadcaster, SBTarget, GetBroadcaster,
+ ());
+ LLDB_REGISTER_METHOD(bool, SBTarget, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+ LLDB_REGISTER_METHOD(lldb::SBSymbolContextList, SBTarget, FindFunctions,
+ (const char *, uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBSymbolContextList, SBTarget,
+ FindGlobalFunctions,
+ (const char *, uint32_t, lldb::MatchType));
+ LLDB_REGISTER_METHOD(lldb::SBType, SBTarget, FindFirstType, (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBType, SBTarget, GetBasicType,
+ (lldb::BasicType));
+ LLDB_REGISTER_METHOD(lldb::SBTypeList, SBTarget, FindTypes, (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBValueList, SBTarget, FindGlobalVariables,
+ (const char *, uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBValueList, SBTarget, FindGlobalVariables,
+ (const char *, uint32_t, lldb::MatchType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, FindFirstGlobalVariable,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBSourceManager, SBTarget, GetSourceManager, ());
+ LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBTarget, ReadInstructions,
+ (lldb::SBAddress, uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBTarget, ReadInstructions,
+ (lldb::SBAddress, uint32_t, const char *));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, SetSectionLoadAddress,
+ (lldb::SBSection, lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, ClearSectionLoadAddress,
+ (lldb::SBSection));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, SetModuleLoadAddress,
+ (lldb::SBModule, int64_t));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, ClearModuleLoadAddress,
+ (lldb::SBModule));
+ LLDB_REGISTER_METHOD(lldb::SBSymbolContextList, SBTarget, FindSymbols,
+ (const char *, lldb::SymbolType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, EvaluateExpression,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, EvaluateExpression,
+ (const char *, const lldb::SBExpressionOptions &));
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBTarget, GetStackRedZoneSize, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::SBLaunchInfo, SBTarget, GetLaunchInfo, ());
+ LLDB_REGISTER_METHOD(void, SBTarget, SetLaunchInfo,
+ (const lldb::SBLaunchInfo &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBThread.cpp b/contrib/llvm-project/lldb/source/API/SBThread.cpp
new file mode 100644
index 000000000000..85e9a6b47955
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBThread.cpp
@@ -0,0 +1,1506 @@
+//===-- SBThread.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBThread.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBSymbolContext.h"
+#include "lldb/API/SBThreadCollection.h"
+#include "lldb/API/SBThreadPlan.h"
+#include "lldb/API/SBValue.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Queue.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanStepInRange.h"
+#include "lldb/Target/ThreadPlanStepInstruction.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Target/ThreadPlanStepRange.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-enumerations.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *SBThread::GetBroadcasterClassName() {
+ LLDB_RECORD_STATIC_METHOD_NO_ARGS(const char *, SBThread,
+ GetBroadcasterClassName);
+
+ return Thread::GetStaticBroadcasterClass().AsCString();
+}
+
+// Constructors
+SBThread::SBThread() : m_opaque_sp(new ExecutionContextRef()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBThread);
+}
+
+SBThread::SBThread(const ThreadSP &lldb_object_sp)
+ : m_opaque_sp(new ExecutionContextRef(lldb_object_sp)) {
+ LLDB_RECORD_CONSTRUCTOR(SBThread, (const lldb::ThreadSP &), lldb_object_sp);
+}
+
+SBThread::SBThread(const SBThread &rhs) : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR(SBThread, (const lldb::SBThread &), rhs);
+
+ m_opaque_sp = clone(rhs.m_opaque_sp);
+}
+
+// Assignment operator
+
+const lldb::SBThread &SBThread::operator=(const SBThread &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBThread &,
+ SBThread, operator=,(const lldb::SBThread &), rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = clone(rhs.m_opaque_sp);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+// Destructor
+SBThread::~SBThread() {}
+
+lldb::SBQueue SBThread::GetQueue() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBQueue, SBThread, GetQueue);
+
+ SBQueue sb_queue;
+ QueueSP queue_sp;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ queue_sp = exe_ctx.GetThreadPtr()->GetQueue();
+ if (queue_sp) {
+ sb_queue.SetQueue(queue_sp);
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_queue);
+}
+
+bool SBThread::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBThread, IsValid);
+ return this->operator bool();
+}
+SBThread::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBThread, operator bool);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock()))
+ return m_opaque_sp->GetThreadSP().get() != nullptr;
+ }
+ // Without a valid target & process, this thread can't be valid.
+ return false;
+}
+
+void SBThread::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBThread, Clear);
+
+ m_opaque_sp->Clear();
+}
+
+StopReason SBThread::GetStopReason() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::StopReason, SBThread, GetStopReason);
+
+ StopReason reason = eStopReasonInvalid;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ return exe_ctx.GetThreadPtr()->GetStopReason();
+ }
+ }
+
+ return reason;
+}
+
+size_t SBThread::GetStopReasonDataCount() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBThread, GetStopReasonDataCount);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo();
+ if (stop_info_sp) {
+ StopReason reason = stop_info_sp->GetStopReason();
+ switch (reason) {
+ case eStopReasonInvalid:
+ case eStopReasonNone:
+ case eStopReasonTrace:
+ case eStopReasonExec:
+ case eStopReasonPlanComplete:
+ case eStopReasonThreadExiting:
+ case eStopReasonInstrumentation:
+ // There is no data for these stop reasons.
+ return 0;
+
+ case eStopReasonBreakpoint: {
+ break_id_t site_id = stop_info_sp->GetValue();
+ lldb::BreakpointSiteSP bp_site_sp(
+ exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID(
+ site_id));
+ if (bp_site_sp)
+ return bp_site_sp->GetNumberOfOwners() * 2;
+ else
+ return 0; // Breakpoint must have cleared itself...
+ } break;
+
+ case eStopReasonWatchpoint:
+ return 1;
+
+ case eStopReasonSignal:
+ return 1;
+
+ case eStopReasonException:
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(uint64_t, SBThread, GetStopReasonDataAtIndex, (uint32_t),
+ idx);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ Thread *thread = exe_ctx.GetThreadPtr();
+ StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp) {
+ StopReason reason = stop_info_sp->GetStopReason();
+ switch (reason) {
+ case eStopReasonInvalid:
+ case eStopReasonNone:
+ case eStopReasonTrace:
+ case eStopReasonExec:
+ case eStopReasonPlanComplete:
+ case eStopReasonThreadExiting:
+ case eStopReasonInstrumentation:
+ // There is no data for these stop reasons.
+ return 0;
+
+ case eStopReasonBreakpoint: {
+ break_id_t site_id = stop_info_sp->GetValue();
+ lldb::BreakpointSiteSP bp_site_sp(
+ exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID(
+ site_id));
+ if (bp_site_sp) {
+ uint32_t bp_index = idx / 2;
+ BreakpointLocationSP bp_loc_sp(
+ bp_site_sp->GetOwnerAtIndex(bp_index));
+ if (bp_loc_sp) {
+ if (idx & 1) {
+ // Odd idx, return the breakpoint location ID
+ return bp_loc_sp->GetID();
+ } else {
+ // Even idx, return the breakpoint ID
+ return bp_loc_sp->GetBreakpoint().GetID();
+ }
+ }
+ }
+ return LLDB_INVALID_BREAK_ID;
+ } break;
+
+ case eStopReasonWatchpoint:
+ return stop_info_sp->GetValue();
+
+ case eStopReasonSignal:
+ return stop_info_sp->GetValue();
+
+ case eStopReasonException:
+ return stop_info_sp->GetValue();
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+bool SBThread::GetStopReasonExtendedInfoAsJSON(lldb::SBStream &stream) {
+ LLDB_RECORD_METHOD(bool, SBThread, GetStopReasonExtendedInfoAsJSON,
+ (lldb::SBStream &), stream);
+
+ Stream &strm = stream.ref();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (!exe_ctx.HasThreadScope())
+ return false;
+
+ StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo();
+ StructuredData::ObjectSP info = stop_info->GetExtendedInfo();
+ if (!info)
+ return false;
+
+ info->Dump(strm);
+
+ return true;
+}
+
+SBThreadCollection
+SBThread::GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type) {
+ LLDB_RECORD_METHOD(lldb::SBThreadCollection, SBThread,
+ GetStopReasonExtendedBacktraces,
+ (lldb::InstrumentationRuntimeType), type);
+
+ ThreadCollectionSP threads;
+ threads = std::make_shared<ThreadCollection>();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (!exe_ctx.HasThreadScope())
+ return LLDB_RECORD_RESULT(threads);
+
+ ProcessSP process_sp = exe_ctx.GetProcessSP();
+
+ StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo();
+ StructuredData::ObjectSP info = stop_info->GetExtendedInfo();
+ if (!info)
+ return LLDB_RECORD_RESULT(threads);
+
+ return LLDB_RECORD_RESULT(process_sp->GetInstrumentationRuntime(type)
+ ->GetBacktracesFromExtendedStopInfo(info));
+}
+
+size_t SBThread::GetStopDescription(char *dst, size_t dst_len) {
+ LLDB_RECORD_METHOD(size_t, SBThread, GetStopDescription, (char *, size_t),
+ dst, dst_len);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+
+ StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo();
+ if (stop_info_sp) {
+ const char *stop_desc = stop_info_sp->GetDescription();
+ if (stop_desc) {
+ if (dst)
+ return ::snprintf(dst, dst_len, "%s", stop_desc);
+ else {
+ // NULL dst passed in, return the length needed to contain the
+ // description
+ return ::strlen(stop_desc) + 1; // Include the NULL byte for size
+ }
+ } else {
+ size_t stop_desc_len = 0;
+ switch (stop_info_sp->GetStopReason()) {
+ case eStopReasonTrace:
+ case eStopReasonPlanComplete: {
+ static char trace_desc[] = "step";
+ stop_desc = trace_desc;
+ stop_desc_len =
+ sizeof(trace_desc); // Include the NULL byte for size
+ } break;
+
+ case eStopReasonBreakpoint: {
+ static char bp_desc[] = "breakpoint hit";
+ stop_desc = bp_desc;
+ stop_desc_len = sizeof(bp_desc); // Include the NULL byte for size
+ } break;
+
+ case eStopReasonWatchpoint: {
+ static char wp_desc[] = "watchpoint hit";
+ stop_desc = wp_desc;
+ stop_desc_len = sizeof(wp_desc); // Include the NULL byte for size
+ } break;
+
+ case eStopReasonSignal: {
+ stop_desc =
+ exe_ctx.GetProcessPtr()->GetUnixSignals()->GetSignalAsCString(
+ stop_info_sp->GetValue());
+ if (stop_desc == nullptr || stop_desc[0] == '\0') {
+ static char signal_desc[] = "signal";
+ stop_desc = signal_desc;
+ stop_desc_len =
+ sizeof(signal_desc); // Include the NULL byte for size
+ }
+ } break;
+
+ case eStopReasonException: {
+ char exc_desc[] = "exception";
+ stop_desc = exc_desc;
+ stop_desc_len = sizeof(exc_desc); // Include the NULL byte for size
+ } break;
+
+ case eStopReasonExec: {
+ char exc_desc[] = "exec";
+ stop_desc = exc_desc;
+ stop_desc_len = sizeof(exc_desc); // Include the NULL byte for size
+ } break;
+
+ case eStopReasonThreadExiting: {
+ char limbo_desc[] = "thread exiting";
+ stop_desc = limbo_desc;
+ stop_desc_len = sizeof(limbo_desc);
+ } break;
+ default:
+ break;
+ }
+
+ if (stop_desc && stop_desc[0]) {
+ if (dst)
+ return ::snprintf(dst, dst_len, "%s", stop_desc) +
+ 1; // Include the NULL byte
+
+ if (stop_desc_len == 0)
+ stop_desc_len = ::strlen(stop_desc) + 1; // Include the NULL byte
+
+ return stop_desc_len;
+ }
+ }
+ }
+ }
+ }
+ if (dst)
+ *dst = 0;
+ return 0;
+}
+
+SBValue SBThread::GetStopReturnValue() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBValue, SBThread, GetStopReturnValue);
+
+ ValueObjectSP return_valobj_sp;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo();
+ if (stop_info_sp) {
+ return_valobj_sp = StopInfo::GetReturnValueObject(stop_info_sp);
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(SBValue(return_valobj_sp));
+}
+
+void SBThread::SetThread(const ThreadSP &lldb_object_sp) {
+ m_opaque_sp->SetThreadSP(lldb_object_sp);
+}
+
+lldb::tid_t SBThread::GetThreadID() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::tid_t, SBThread, GetThreadID);
+
+ ThreadSP thread_sp(m_opaque_sp->GetThreadSP());
+ if (thread_sp)
+ return thread_sp->GetID();
+ return LLDB_INVALID_THREAD_ID;
+}
+
+uint32_t SBThread::GetIndexID() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBThread, GetIndexID);
+
+ ThreadSP thread_sp(m_opaque_sp->GetThreadSP());
+ if (thread_sp)
+ return thread_sp->GetIndexID();
+ return LLDB_INVALID_INDEX32;
+}
+
+const char *SBThread::GetName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBThread, GetName);
+
+ const char *name = nullptr;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ name = exe_ctx.GetThreadPtr()->GetName();
+ }
+ }
+
+ return name;
+}
+
+const char *SBThread::GetQueueName() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBThread, GetQueueName);
+
+ const char *name = nullptr;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ name = exe_ctx.GetThreadPtr()->GetQueueName();
+ }
+ }
+
+ return name;
+}
+
+lldb::queue_id_t SBThread::GetQueueID() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::queue_id_t, SBThread, GetQueueID);
+
+ queue_id_t id = LLDB_INVALID_QUEUE_ID;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ id = exe_ctx.GetThreadPtr()->GetQueueID();
+ }
+ }
+
+ return id;
+}
+
+bool SBThread::GetInfoItemByPathAsString(const char *path, SBStream &strm) {
+ LLDB_RECORD_METHOD(bool, SBThread, GetInfoItemByPathAsString,
+ (const char *, lldb::SBStream &), path, strm);
+
+ bool success = false;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ Thread *thread = exe_ctx.GetThreadPtr();
+ StructuredData::ObjectSP info_root_sp = thread->GetExtendedInfo();
+ if (info_root_sp) {
+ StructuredData::ObjectSP node =
+ info_root_sp->GetObjectForDotSeparatedPath(path);
+ if (node) {
+ if (node->GetType() == eStructuredDataTypeString) {
+ strm.Printf("%s", node->GetAsString()->GetValue().str().c_str());
+ success = true;
+ }
+ if (node->GetType() == eStructuredDataTypeInteger) {
+ strm.Printf("0x%" PRIx64, node->GetAsInteger()->GetValue());
+ success = true;
+ }
+ if (node->GetType() == eStructuredDataTypeFloat) {
+ strm.Printf("0x%f", node->GetAsFloat()->GetValue());
+ success = true;
+ }
+ if (node->GetType() == eStructuredDataTypeBoolean) {
+ if (node->GetAsBoolean()->GetValue())
+ strm.Printf("true");
+ else
+ strm.Printf("false");
+ success = true;
+ }
+ if (node->GetType() == eStructuredDataTypeNull) {
+ strm.Printf("null");
+ success = true;
+ }
+ }
+ }
+ }
+ }
+
+ return success;
+}
+
+SBError SBThread::ResumeNewPlan(ExecutionContext &exe_ctx,
+ ThreadPlan *new_plan) {
+ SBError sb_error;
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (!process) {
+ sb_error.SetErrorString("No process in SBThread::ResumeNewPlan");
+ return sb_error;
+ }
+
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (!thread) {
+ sb_error.SetErrorString("No thread in SBThread::ResumeNewPlan");
+ return sb_error;
+ }
+
+ // User level plans should be Master Plans so they can be interrupted, other
+ // plans executed, and then a "continue" will resume the plan.
+ if (new_plan != nullptr) {
+ new_plan->SetIsMasterPlan(true);
+ new_plan->SetOkayToDiscard(false);
+ }
+
+ // Why do we need to set the current thread by ID here???
+ process->GetThreadList().SetSelectedThreadByID(thread->GetID());
+
+ if (process->GetTarget().GetDebugger().GetAsyncExecution())
+ sb_error.ref() = process->Resume();
+ else
+ sb_error.ref() = process->ResumeSynchronous(nullptr);
+
+ return sb_error;
+}
+
+void SBThread::StepOver(lldb::RunMode stop_other_threads) {
+ LLDB_RECORD_METHOD(void, SBThread, StepOver, (lldb::RunMode),
+ stop_other_threads);
+
+ SBError error; // Ignored
+ StepOver(stop_other_threads, error);
+}
+
+void SBThread::StepOver(lldb::RunMode stop_other_threads, SBError &error) {
+ LLDB_RECORD_METHOD(void, SBThread, StepOver, (lldb::RunMode, lldb::SBError &),
+ stop_other_threads, error);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (!exe_ctx.HasThreadScope()) {
+ error.SetErrorString("this SBThread object is invalid");
+ return;
+ }
+
+ Thread *thread = exe_ctx.GetThreadPtr();
+ bool abort_other_plans = false;
+ StackFrameSP frame_sp(thread->GetStackFrameAtIndex(0));
+
+ Status new_plan_status;
+ ThreadPlanSP new_plan_sp;
+ if (frame_sp) {
+ if (frame_sp->HasDebugInformation()) {
+ const LazyBool avoid_no_debug = eLazyBoolCalculate;
+ SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything));
+ new_plan_sp = thread->QueueThreadPlanForStepOverRange(
+ abort_other_plans, sc.line_entry, sc, stop_other_threads,
+ new_plan_status, avoid_no_debug);
+ } else {
+ new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
+ true, abort_other_plans, stop_other_threads, new_plan_status);
+ }
+ }
+ error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+}
+
+void SBThread::StepInto(lldb::RunMode stop_other_threads) {
+ LLDB_RECORD_METHOD(void, SBThread, StepInto, (lldb::RunMode),
+ stop_other_threads);
+
+ StepInto(nullptr, stop_other_threads);
+}
+
+void SBThread::StepInto(const char *target_name,
+ lldb::RunMode stop_other_threads) {
+ LLDB_RECORD_METHOD(void, SBThread, StepInto, (const char *, lldb::RunMode),
+ target_name, stop_other_threads);
+
+ SBError error; // Ignored
+ StepInto(target_name, LLDB_INVALID_LINE_NUMBER, error, stop_other_threads);
+}
+
+void SBThread::StepInto(const char *target_name, uint32_t end_line,
+ SBError &error, lldb::RunMode stop_other_threads) {
+ LLDB_RECORD_METHOD(void, SBThread, StepInto,
+ (const char *, uint32_t, lldb::SBError &, lldb::RunMode),
+ target_name, end_line, error, stop_other_threads);
+
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (!exe_ctx.HasThreadScope()) {
+ error.SetErrorString("this SBThread object is invalid");
+ return;
+ }
+
+ bool abort_other_plans = false;
+
+ Thread *thread = exe_ctx.GetThreadPtr();
+ StackFrameSP frame_sp(thread->GetStackFrameAtIndex(0));
+ ThreadPlanSP new_plan_sp;
+ Status new_plan_status;
+
+ if (frame_sp && frame_sp->HasDebugInformation()) {
+ SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything));
+ AddressRange range;
+ if (end_line == LLDB_INVALID_LINE_NUMBER)
+ range = sc.line_entry.range;
+ else {
+ if (!sc.GetAddressRangeFromHereToEndLine(end_line, range, error.ref()))
+ return;
+ }
+
+ const LazyBool step_out_avoids_code_without_debug_info =
+ eLazyBoolCalculate;
+ const LazyBool step_in_avoids_code_without_debug_info =
+ eLazyBoolCalculate;
+ new_plan_sp = thread->QueueThreadPlanForStepInRange(
+ abort_other_plans, range, sc, target_name, stop_other_threads,
+ new_plan_status, step_in_avoids_code_without_debug_info,
+ step_out_avoids_code_without_debug_info);
+ } else {
+ new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
+ false, abort_other_plans, stop_other_threads, new_plan_status);
+ }
+
+ if (new_plan_status.Success())
+ error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ else
+ error.SetErrorString(new_plan_status.AsCString());
+}
+
+void SBThread::StepOut() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBThread, StepOut);
+
+ SBError error; // Ignored
+ StepOut(error);
+}
+
+void SBThread::StepOut(SBError &error) {
+ LLDB_RECORD_METHOD(void, SBThread, StepOut, (lldb::SBError &), error);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (!exe_ctx.HasThreadScope()) {
+ error.SetErrorString("this SBThread object is invalid");
+ return;
+ }
+
+ bool abort_other_plans = false;
+ bool stop_other_threads = false;
+
+ Thread *thread = exe_ctx.GetThreadPtr();
+
+ const LazyBool avoid_no_debug = eLazyBoolCalculate;
+ Status new_plan_status;
+ ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepOut(
+ abort_other_plans, nullptr, false, stop_other_threads, eVoteYes,
+ eVoteNoOpinion, 0, new_plan_status, avoid_no_debug));
+
+ if (new_plan_status.Success())
+ error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ else
+ error.SetErrorString(new_plan_status.AsCString());
+}
+
+void SBThread::StepOutOfFrame(SBFrame &sb_frame) {
+ LLDB_RECORD_METHOD(void, SBThread, StepOutOfFrame, (lldb::SBFrame &),
+ sb_frame);
+
+ SBError error; // Ignored
+ StepOutOfFrame(sb_frame, error);
+}
+
+void SBThread::StepOutOfFrame(SBFrame &sb_frame, SBError &error) {
+ LLDB_RECORD_METHOD(void, SBThread, StepOutOfFrame,
+ (lldb::SBFrame &, lldb::SBError &), sb_frame, error);
+
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (!sb_frame.IsValid()) {
+ error.SetErrorString("passed invalid SBFrame object");
+ return;
+ }
+
+ StackFrameSP frame_sp(sb_frame.GetFrameSP());
+
+ if (!exe_ctx.HasThreadScope()) {
+ error.SetErrorString("this SBThread object is invalid");
+ return;
+ }
+
+ bool abort_other_plans = false;
+ bool stop_other_threads = false;
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (sb_frame.GetThread().GetThreadID() != thread->GetID()) {
+ error.SetErrorString("passed a frame from another thread");
+ return;
+ }
+
+ Status new_plan_status;
+ ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepOut(
+ abort_other_plans, nullptr, false, stop_other_threads, eVoteYes,
+ eVoteNoOpinion, frame_sp->GetFrameIndex(), new_plan_status));
+
+ if (new_plan_status.Success())
+ error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ else
+ error.SetErrorString(new_plan_status.AsCString());
+}
+
+void SBThread::StepInstruction(bool step_over) {
+ LLDB_RECORD_METHOD(void, SBThread, StepInstruction, (bool), step_over);
+
+ SBError error; // Ignored
+ StepInstruction(step_over, error);
+}
+
+void SBThread::StepInstruction(bool step_over, SBError &error) {
+ LLDB_RECORD_METHOD(void, SBThread, StepInstruction, (bool, lldb::SBError &),
+ step_over, error);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (!exe_ctx.HasThreadScope()) {
+ error.SetErrorString("this SBThread object is invalid");
+ return;
+ }
+
+ Thread *thread = exe_ctx.GetThreadPtr();
+ Status new_plan_status;
+ ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepSingleInstruction(
+ step_over, true, true, new_plan_status));
+
+ if (new_plan_status.Success())
+ error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ else
+ error.SetErrorString(new_plan_status.AsCString());
+}
+
+void SBThread::RunToAddress(lldb::addr_t addr) {
+ LLDB_RECORD_METHOD(void, SBThread, RunToAddress, (lldb::addr_t), addr);
+
+ SBError error; // Ignored
+ RunToAddress(addr, error);
+}
+
+void SBThread::RunToAddress(lldb::addr_t addr, SBError &error) {
+ LLDB_RECORD_METHOD(void, SBThread, RunToAddress,
+ (lldb::addr_t, lldb::SBError &), addr, error);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (!exe_ctx.HasThreadScope()) {
+ error.SetErrorString("this SBThread object is invalid");
+ return;
+ }
+
+ bool abort_other_plans = false;
+ bool stop_other_threads = true;
+
+ Address target_addr(addr);
+
+ Thread *thread = exe_ctx.GetThreadPtr();
+
+ Status new_plan_status;
+ ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForRunToAddress(
+ abort_other_plans, target_addr, stop_other_threads, new_plan_status));
+
+ if (new_plan_status.Success())
+ error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ else
+ error.SetErrorString(new_plan_status.AsCString());
+}
+
+SBError SBThread::StepOverUntil(lldb::SBFrame &sb_frame,
+ lldb::SBFileSpec &sb_file_spec, uint32_t line) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBThread, StepOverUntil,
+ (lldb::SBFrame &, lldb::SBFileSpec &, uint32_t), sb_frame,
+ sb_file_spec, line);
+
+ SBError sb_error;
+ char path[PATH_MAX];
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrameSP frame_sp(sb_frame.GetFrameSP());
+
+ if (exe_ctx.HasThreadScope()) {
+ Target *target = exe_ctx.GetTargetPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+
+ if (line == 0) {
+ sb_error.SetErrorString("invalid line argument");
+ return LLDB_RECORD_RESULT(sb_error);
+ }
+
+ if (!frame_sp) {
+ frame_sp = thread->GetSelectedFrame();
+ if (!frame_sp)
+ frame_sp = thread->GetStackFrameAtIndex(0);
+ }
+
+ SymbolContext frame_sc;
+ if (!frame_sp) {
+ sb_error.SetErrorString("no valid frames in thread to step");
+ return LLDB_RECORD_RESULT(sb_error);
+ }
+
+ // If we have a frame, get its line
+ frame_sc = frame_sp->GetSymbolContext(
+ eSymbolContextCompUnit | eSymbolContextFunction |
+ eSymbolContextLineEntry | eSymbolContextSymbol);
+
+ if (frame_sc.comp_unit == nullptr) {
+ sb_error.SetErrorStringWithFormat(
+ "frame %u doesn't have debug information", frame_sp->GetFrameIndex());
+ return LLDB_RECORD_RESULT(sb_error);
+ }
+
+ FileSpec step_file_spec;
+ if (sb_file_spec.IsValid()) {
+ // The file spec passed in was valid, so use it
+ step_file_spec = sb_file_spec.ref();
+ } else {
+ if (frame_sc.line_entry.IsValid())
+ step_file_spec = frame_sc.line_entry.file;
+ else {
+ sb_error.SetErrorString("invalid file argument or no file for frame");
+ return LLDB_RECORD_RESULT(sb_error);
+ }
+ }
+
+ // Grab the current function, then we will make sure the "until" address is
+ // within the function. We discard addresses that are out of the current
+ // function, and then if there are no addresses remaining, give an
+ // appropriate error message.
+
+ bool all_in_function = true;
+ AddressRange fun_range = frame_sc.function->GetAddressRange();
+
+ std::vector<addr_t> step_over_until_addrs;
+ const bool abort_other_plans = false;
+ const bool stop_other_threads = false;
+ const bool check_inlines = true;
+ const bool exact = false;
+
+ SymbolContextList sc_list;
+ const uint32_t num_matches = frame_sc.comp_unit->ResolveSymbolContext(
+ step_file_spec, line, check_inlines, exact, eSymbolContextLineEntry,
+ sc_list);
+ if (num_matches > 0) {
+ SymbolContext sc;
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ if (sc_list.GetContextAtIndex(i, sc)) {
+ addr_t step_addr =
+ sc.line_entry.range.GetBaseAddress().GetLoadAddress(target);
+ if (step_addr != LLDB_INVALID_ADDRESS) {
+ if (fun_range.ContainsLoadAddress(step_addr, target))
+ step_over_until_addrs.push_back(step_addr);
+ else
+ all_in_function = false;
+ }
+ }
+ }
+ }
+
+ if (step_over_until_addrs.empty()) {
+ if (all_in_function) {
+ step_file_spec.GetPath(path, sizeof(path));
+ sb_error.SetErrorStringWithFormat("No line entries for %s:%u", path,
+ line);
+ } else
+ sb_error.SetErrorString("step until target not in current function");
+ } else {
+ Status new_plan_status;
+ ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepUntil(
+ abort_other_plans, &step_over_until_addrs[0],
+ step_over_until_addrs.size(), stop_other_threads,
+ frame_sp->GetFrameIndex(), new_plan_status));
+
+ if (new_plan_status.Success())
+ sb_error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ else
+ sb_error.SetErrorString(new_plan_status.AsCString());
+ }
+ } else {
+ sb_error.SetErrorString("this SBThread object is invalid");
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBThread, StepUsingScriptedThreadPlan,
+ (const char *), script_class_name);
+
+ return LLDB_RECORD_RESULT(
+ StepUsingScriptedThreadPlan(script_class_name, true));
+}
+
+SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name,
+ bool resume_immediately) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBThread, StepUsingScriptedThreadPlan,
+ (const char *, bool), script_class_name,
+ resume_immediately);
+
+ SBError error;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (!exe_ctx.HasThreadScope()) {
+ error.SetErrorString("this SBThread object is invalid");
+ return LLDB_RECORD_RESULT(error);
+ }
+
+ Thread *thread = exe_ctx.GetThreadPtr();
+ Status new_plan_status;
+ ThreadPlanSP new_plan_sp = thread->QueueThreadPlanForStepScripted(
+ false, script_class_name, false, new_plan_status);
+
+ if (new_plan_status.Fail()) {
+ error.SetErrorString(new_plan_status.AsCString());
+ return LLDB_RECORD_RESULT(error);
+ }
+
+ if (!resume_immediately)
+ return LLDB_RECORD_RESULT(error);
+
+ if (new_plan_status.Success())
+ error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ else
+ error.SetErrorString(new_plan_status.AsCString());
+
+ return LLDB_RECORD_RESULT(error);
+}
+
+SBError SBThread::JumpToLine(lldb::SBFileSpec &file_spec, uint32_t line) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBThread, JumpToLine,
+ (lldb::SBFileSpec &, uint32_t), file_spec, line);
+
+ SBError sb_error;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (!exe_ctx.HasThreadScope()) {
+ sb_error.SetErrorString("this SBThread object is invalid");
+ return LLDB_RECORD_RESULT(sb_error);
+ }
+
+ Thread *thread = exe_ctx.GetThreadPtr();
+
+ Status err = thread->JumpToLine(file_spec.get(), line, true);
+ sb_error.SetError(err);
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBThread::ReturnFromFrame(SBFrame &frame, SBValue &return_value) {
+ LLDB_RECORD_METHOD(lldb::SBError, SBThread, ReturnFromFrame,
+ (lldb::SBFrame &, lldb::SBValue &), frame, return_value);
+
+ SBError sb_error;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Thread *thread = exe_ctx.GetThreadPtr();
+ sb_error.SetError(
+ thread->ReturnFromFrame(frame.GetFrameSP(), return_value.GetSP()));
+ }
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+SBError SBThread::UnwindInnermostExpression() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBThread,
+ UnwindInnermostExpression);
+
+ SBError sb_error;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Thread *thread = exe_ctx.GetThreadPtr();
+ sb_error.SetError(thread->UnwindInnermostExpression());
+ if (sb_error.Success())
+ thread->SetSelectedFrameByIndex(0, false);
+ }
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+bool SBThread::Suspend() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBThread, Suspend);
+
+ SBError error; // Ignored
+ return Suspend(error);
+}
+
+bool SBThread::Suspend(SBError &error) {
+ LLDB_RECORD_METHOD(bool, SBThread, Suspend, (lldb::SBError &), error);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ bool result = false;
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ exe_ctx.GetThreadPtr()->SetResumeState(eStateSuspended);
+ result = true;
+ } else {
+ error.SetErrorString("process is running");
+ }
+ } else
+ error.SetErrorString("this SBThread object is invalid");
+ return result;
+}
+
+bool SBThread::Resume() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBThread, Resume);
+
+ SBError error; // Ignored
+ return Resume(error);
+}
+
+bool SBThread::Resume(SBError &error) {
+ LLDB_RECORD_METHOD(bool, SBThread, Resume, (lldb::SBError &), error);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ bool result = false;
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ const bool override_suspend = true;
+ exe_ctx.GetThreadPtr()->SetResumeState(eStateRunning, override_suspend);
+ result = true;
+ } else {
+ error.SetErrorString("process is running");
+ }
+ } else
+ error.SetErrorString("this SBThread object is invalid");
+ return result;
+}
+
+bool SBThread::IsSuspended() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBThread, IsSuspended);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope())
+ return exe_ctx.GetThreadPtr()->GetResumeState() == eStateSuspended;
+ return false;
+}
+
+bool SBThread::IsStopped() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBThread, IsStopped);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope())
+ return StateIsStoppedState(exe_ctx.GetThreadPtr()->GetState(), true);
+ return false;
+}
+
+SBProcess SBThread::GetProcess() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBProcess, SBThread, GetProcess);
+
+ SBProcess sb_process;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ // Have to go up to the target so we can get a shared pointer to our
+ // process...
+ sb_process.SetSP(exe_ctx.GetProcessSP());
+ }
+
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+uint32_t SBThread::GetNumFrames() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBThread, GetNumFrames);
+
+ uint32_t num_frames = 0;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ num_frames = exe_ctx.GetThreadPtr()->GetStackFrameCount();
+ }
+ }
+
+ return num_frames;
+}
+
+SBFrame SBThread::GetFrameAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBFrame, SBThread, GetFrameAtIndex, (uint32_t), idx);
+
+ SBFrame sb_frame;
+ StackFrameSP frame_sp;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ frame_sp = exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(idx);
+ sb_frame.SetFrameSP(frame_sp);
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_frame);
+}
+
+lldb::SBFrame SBThread::GetSelectedFrame() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFrame, SBThread, GetSelectedFrame);
+
+ SBFrame sb_frame;
+ StackFrameSP frame_sp;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ frame_sp = exe_ctx.GetThreadPtr()->GetSelectedFrame();
+ sb_frame.SetFrameSP(frame_sp);
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_frame);
+}
+
+lldb::SBFrame SBThread::SetSelectedFrame(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBFrame, SBThread, SetSelectedFrame, (uint32_t),
+ idx);
+
+ SBFrame sb_frame;
+ StackFrameSP frame_sp;
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ Thread *thread = exe_ctx.GetThreadPtr();
+ frame_sp = thread->GetStackFrameAtIndex(idx);
+ if (frame_sp) {
+ thread->SetSelectedFrame(frame_sp.get());
+ sb_frame.SetFrameSP(frame_sp);
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_frame);
+}
+
+bool SBThread::EventIsThreadEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBThread, EventIsThreadEvent,
+ (const lldb::SBEvent &), event);
+
+ return Thread::ThreadEventData::GetEventDataFromEvent(event.get()) != nullptr;
+}
+
+SBFrame SBThread::GetStackFrameFromEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBFrame, SBThread, GetStackFrameFromEvent,
+ (const lldb::SBEvent &), event);
+
+ return LLDB_RECORD_RESULT(
+ Thread::ThreadEventData::GetStackFrameFromEvent(event.get()));
+}
+
+SBThread SBThread::GetThreadFromEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBThread, SBThread, GetThreadFromEvent,
+ (const lldb::SBEvent &), event);
+
+ return LLDB_RECORD_RESULT(
+ Thread::ThreadEventData::GetThreadFromEvent(event.get()));
+}
+
+bool SBThread::operator==(const SBThread &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBThread, operator==,(const lldb::SBThread &),
+ rhs);
+
+ return m_opaque_sp->GetThreadSP().get() ==
+ rhs.m_opaque_sp->GetThreadSP().get();
+}
+
+bool SBThread::operator!=(const SBThread &rhs) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBThread, operator!=,(const lldb::SBThread &),
+ rhs);
+
+ return m_opaque_sp->GetThreadSP().get() !=
+ rhs.m_opaque_sp->GetThreadSP().get();
+}
+
+bool SBThread::GetStatus(SBStream &status) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBThread, GetStatus, (lldb::SBStream &),
+ status);
+
+ Stream &strm = status.ref();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ exe_ctx.GetThreadPtr()->GetStatus(strm, 0, 1, 1, true);
+ } else
+ strm.PutCString("No status");
+
+ return true;
+}
+
+bool SBThread::GetDescription(SBStream &description) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBThread, GetDescription, (lldb::SBStream &),
+ description);
+
+ return GetDescription(description, false);
+}
+
+bool SBThread::GetDescription(SBStream &description, bool stop_format) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBThread, GetDescription,
+ (lldb::SBStream &, bool), description, stop_format);
+
+ Stream &strm = description.ref();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (exe_ctx.HasThreadScope()) {
+ exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat(strm,
+ LLDB_INVALID_THREAD_ID,
+ stop_format);
+ // strm.Printf("SBThread: tid = 0x%4.4" PRIx64,
+ // exe_ctx.GetThreadPtr()->GetID());
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+SBThread SBThread::GetExtendedBacktraceThread(const char *type) {
+ LLDB_RECORD_METHOD(lldb::SBThread, SBThread, GetExtendedBacktraceThread,
+ (const char *), type);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ SBThread sb_origin_thread;
+
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
+ if (exe_ctx.HasThreadScope()) {
+ ThreadSP real_thread(exe_ctx.GetThreadSP());
+ if (real_thread) {
+ ConstString type_const(type);
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ SystemRuntime *runtime = process->GetSystemRuntime();
+ if (runtime) {
+ ThreadSP new_thread_sp(
+ runtime->GetExtendedBacktraceThread(real_thread, type_const));
+ if (new_thread_sp) {
+ // Save this in the Process' ExtendedThreadList so a strong
+ // pointer retains the object.
+ process->GetExtendedThreadList().AddThread(new_thread_sp);
+ sb_origin_thread.SetThread(new_thread_sp);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_origin_thread);
+}
+
+uint32_t SBThread::GetExtendedBacktraceOriginatingIndexID() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBThread,
+ GetExtendedBacktraceOriginatingIndexID);
+
+ ThreadSP thread_sp(m_opaque_sp->GetThreadSP());
+ if (thread_sp)
+ return thread_sp->GetExtendedBacktraceOriginatingIndexID();
+ return LLDB_INVALID_INDEX32;
+}
+
+SBValue SBThread::GetCurrentException() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBValue, SBThread, GetCurrentException);
+
+ ThreadSP thread_sp(m_opaque_sp->GetThreadSP());
+ if (!thread_sp)
+ return LLDB_RECORD_RESULT(SBValue());
+
+ return LLDB_RECORD_RESULT(SBValue(thread_sp->GetCurrentException()));
+}
+
+SBThread SBThread::GetCurrentExceptionBacktrace() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBThread, SBThread,
+ GetCurrentExceptionBacktrace);
+
+ ThreadSP thread_sp(m_opaque_sp->GetThreadSP());
+ if (!thread_sp)
+ return LLDB_RECORD_RESULT(SBThread());
+
+ return LLDB_RECORD_RESULT(
+ SBThread(thread_sp->GetCurrentExceptionBacktrace()));
+}
+
+bool SBThread::SafeToCallFunctions() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBThread, SafeToCallFunctions);
+
+ ThreadSP thread_sp(m_opaque_sp->GetThreadSP());
+ if (thread_sp)
+ return thread_sp->SafeToCallFunctions();
+ return true;
+}
+
+lldb_private::Thread *SBThread::operator->() {
+ return get();
+}
+
+lldb_private::Thread *SBThread::get() {
+ return m_opaque_sp->GetThreadSP().get();
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBThread>(Registry &R) {
+ LLDB_REGISTER_STATIC_METHOD(const char *, SBThread, GetBroadcasterClassName,
+ ());
+ LLDB_REGISTER_CONSTRUCTOR(SBThread, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBThread, (const lldb::ThreadSP &));
+ LLDB_REGISTER_CONSTRUCTOR(SBThread, (const lldb::SBThread &));
+ LLDB_REGISTER_METHOD(const lldb::SBThread &,
+ SBThread, operator=,(const lldb::SBThread &));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBQueue, SBThread, GetQueue, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBThread, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBThread, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBThread, Clear, ());
+ LLDB_REGISTER_METHOD(lldb::StopReason, SBThread, GetStopReason, ());
+ LLDB_REGISTER_METHOD(size_t, SBThread, GetStopReasonDataCount, ());
+ LLDB_REGISTER_METHOD(uint64_t, SBThread, GetStopReasonDataAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBThread, GetStopReasonExtendedInfoAsJSON,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(lldb::SBThreadCollection, SBThread,
+ GetStopReasonExtendedBacktraces,
+ (lldb::InstrumentationRuntimeType));
+ LLDB_REGISTER_METHOD(size_t, SBThread, GetStopDescription,
+ (char *, size_t));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBThread, GetStopReturnValue, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::tid_t, SBThread, GetThreadID, ());
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBThread, GetIndexID, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBThread, GetName, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBThread, GetQueueName, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::queue_id_t, SBThread, GetQueueID, ());
+ LLDB_REGISTER_METHOD(bool, SBThread, GetInfoItemByPathAsString,
+ (const char *, lldb::SBStream &));
+ LLDB_REGISTER_METHOD(void, SBThread, StepOver, (lldb::RunMode));
+ LLDB_REGISTER_METHOD(void, SBThread, StepOver,
+ (lldb::RunMode, lldb::SBError &));
+ LLDB_REGISTER_METHOD(void, SBThread, StepInto, (lldb::RunMode));
+ LLDB_REGISTER_METHOD(void, SBThread, StepInto,
+ (const char *, lldb::RunMode));
+ LLDB_REGISTER_METHOD(
+ void, SBThread, StepInto,
+ (const char *, uint32_t, lldb::SBError &, lldb::RunMode));
+ LLDB_REGISTER_METHOD(void, SBThread, StepOut, ());
+ LLDB_REGISTER_METHOD(void, SBThread, StepOut, (lldb::SBError &));
+ LLDB_REGISTER_METHOD(void, SBThread, StepOutOfFrame, (lldb::SBFrame &));
+ LLDB_REGISTER_METHOD(void, SBThread, StepOutOfFrame,
+ (lldb::SBFrame &, lldb::SBError &));
+ LLDB_REGISTER_METHOD(void, SBThread, StepInstruction, (bool));
+ LLDB_REGISTER_METHOD(void, SBThread, StepInstruction,
+ (bool, lldb::SBError &));
+ LLDB_REGISTER_METHOD(void, SBThread, RunToAddress, (lldb::addr_t));
+ LLDB_REGISTER_METHOD(void, SBThread, RunToAddress,
+ (lldb::addr_t, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBThread, StepOverUntil,
+ (lldb::SBFrame &, lldb::SBFileSpec &, uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBThread, StepUsingScriptedThreadPlan,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBThread, StepUsingScriptedThreadPlan,
+ (const char *, bool));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBThread, JumpToLine,
+ (lldb::SBFileSpec &, uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBThread, ReturnFromFrame,
+ (lldb::SBFrame &, lldb::SBValue &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBThread, UnwindInnermostExpression,
+ ());
+ LLDB_REGISTER_METHOD(bool, SBThread, Suspend, ());
+ LLDB_REGISTER_METHOD(bool, SBThread, Suspend, (lldb::SBError &));
+ LLDB_REGISTER_METHOD(bool, SBThread, Resume, ());
+ LLDB_REGISTER_METHOD(bool, SBThread, Resume, (lldb::SBError &));
+ LLDB_REGISTER_METHOD(bool, SBThread, IsSuspended, ());
+ LLDB_REGISTER_METHOD(bool, SBThread, IsStopped, ());
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBThread, GetProcess, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBThread, GetNumFrames, ());
+ LLDB_REGISTER_METHOD(lldb::SBFrame, SBThread, GetFrameAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBFrame, SBThread, GetSelectedFrame, ());
+ LLDB_REGISTER_METHOD(lldb::SBFrame, SBThread, SetSelectedFrame, (uint32_t));
+ LLDB_REGISTER_STATIC_METHOD(bool, SBThread, EventIsThreadEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBFrame, SBThread, GetStackFrameFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBThread, SBThread, GetThreadFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBThread, operator==,(const lldb::SBThread &));
+ LLDB_REGISTER_METHOD_CONST(bool,
+ SBThread, operator!=,(const lldb::SBThread &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBThread, GetStatus, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBThread, GetDescription,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBThread, GetDescription,
+ (lldb::SBStream &, bool));
+ LLDB_REGISTER_METHOD(lldb::SBThread, SBThread, GetExtendedBacktraceThread,
+ (const char *));
+ LLDB_REGISTER_METHOD(uint32_t, SBThread,
+ GetExtendedBacktraceOriginatingIndexID, ());
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBThread, GetCurrentException, ());
+ LLDB_REGISTER_METHOD(lldb::SBThread, SBThread, GetCurrentExceptionBacktrace,
+ ());
+ LLDB_REGISTER_METHOD(bool, SBThread, SafeToCallFunctions, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBThreadCollection.cpp b/contrib/llvm-project/lldb/source/API/SBThreadCollection.cpp
new file mode 100644
index 000000000000..3c1cf9865062
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBThreadCollection.cpp
@@ -0,0 +1,110 @@
+//===-- SBThreadCollection.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBThreadCollection.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBThread.h"
+#include "lldb/Target/ThreadList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBThreadCollection::SBThreadCollection() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBThreadCollection);
+}
+
+SBThreadCollection::SBThreadCollection(const SBThreadCollection &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBThreadCollection,
+ (const lldb::SBThreadCollection &), rhs);
+}
+
+const SBThreadCollection &SBThreadCollection::
+operator=(const SBThreadCollection &rhs) {
+ LLDB_RECORD_METHOD(
+ const lldb::SBThreadCollection &,
+ SBThreadCollection, operator=,(const lldb::SBThreadCollection &), rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = rhs.m_opaque_sp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBThreadCollection::SBThreadCollection(const ThreadCollectionSP &threads)
+ : m_opaque_sp(threads) {}
+
+SBThreadCollection::~SBThreadCollection() {}
+
+void SBThreadCollection::SetOpaque(const lldb::ThreadCollectionSP &threads) {
+ m_opaque_sp = threads;
+}
+
+lldb_private::ThreadCollection *SBThreadCollection::get() const {
+ return m_opaque_sp.get();
+}
+
+lldb_private::ThreadCollection *SBThreadCollection::operator->() const {
+ return m_opaque_sp.operator->();
+}
+
+lldb::ThreadCollectionSP &SBThreadCollection::operator*() {
+ return m_opaque_sp;
+}
+
+const lldb::ThreadCollectionSP &SBThreadCollection::operator*() const {
+ return m_opaque_sp;
+}
+
+bool SBThreadCollection::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBThreadCollection, IsValid);
+ return this->operator bool();
+}
+SBThreadCollection::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBThreadCollection, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+size_t SBThreadCollection::GetSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBThreadCollection, GetSize);
+
+ if (m_opaque_sp)
+ return m_opaque_sp->GetSize();
+ return 0;
+}
+
+SBThread SBThreadCollection::GetThreadAtIndex(size_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBThread, SBThreadCollection, GetThreadAtIndex,
+ (size_t), idx);
+
+ SBThread thread;
+ if (m_opaque_sp && idx < m_opaque_sp->GetSize())
+ thread = m_opaque_sp->GetThreadAtIndex(idx);
+ return LLDB_RECORD_RESULT(thread);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBThreadCollection>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBThreadCollection, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBThreadCollection,
+ (const lldb::SBThreadCollection &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBThreadCollection &,
+ SBThreadCollection, operator=,(const lldb::SBThreadCollection &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBThreadCollection, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBThreadCollection, operator bool, ());
+ LLDB_REGISTER_METHOD(size_t, SBThreadCollection, GetSize, ());
+ LLDB_REGISTER_METHOD(lldb::SBThread, SBThreadCollection, GetThreadAtIndex,
+ (size_t));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBThreadPlan.cpp b/contrib/llvm-project/lldb/source/API/SBThreadPlan.cpp
new file mode 100644
index 000000000000..8f6802fe9cef
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBThreadPlan.cpp
@@ -0,0 +1,439 @@
+//===-- SBThreadPlan.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBThread.h"
+
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBSymbolContext.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Queue.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanPython.h"
+#include "lldb/Target/ThreadPlanStepInRange.h"
+#include "lldb/Target/ThreadPlanStepInstruction.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Target/ThreadPlanStepRange.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StructuredData.h"
+
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBThreadPlan.h"
+#include "lldb/API/SBValue.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Constructors
+SBThreadPlan::SBThreadPlan() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBThreadPlan); }
+
+SBThreadPlan::SBThreadPlan(const ThreadPlanSP &lldb_object_sp)
+ : m_opaque_sp(lldb_object_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBThreadPlan, (const lldb::ThreadPlanSP &),
+ lldb_object_sp);
+}
+
+SBThreadPlan::SBThreadPlan(const SBThreadPlan &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBThreadPlan, (const lldb::SBThreadPlan &), rhs);
+}
+
+SBThreadPlan::SBThreadPlan(lldb::SBThread &sb_thread, const char *class_name) {
+ LLDB_RECORD_CONSTRUCTOR(SBThreadPlan, (lldb::SBThread &, const char *),
+ sb_thread, class_name);
+
+ Thread *thread = sb_thread.get();
+ if (thread)
+ m_opaque_sp = std::make_shared<ThreadPlanPython>(*thread, class_name);
+}
+
+// Assignment operator
+
+const lldb::SBThreadPlan &SBThreadPlan::operator=(const SBThreadPlan &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBThreadPlan &,
+ SBThreadPlan, operator=,(const lldb::SBThreadPlan &), rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = rhs.m_opaque_sp;
+ return LLDB_RECORD_RESULT(*this);
+}
+// Destructor
+SBThreadPlan::~SBThreadPlan() {}
+
+lldb_private::ThreadPlan *SBThreadPlan::get() { return m_opaque_sp.get(); }
+
+bool SBThreadPlan::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBThreadPlan, IsValid);
+ return this->operator bool();
+}
+SBThreadPlan::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBThreadPlan, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+void SBThreadPlan::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBThreadPlan, Clear);
+
+ m_opaque_sp.reset();
+}
+
+lldb::StopReason SBThreadPlan::GetStopReason() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::StopReason, SBThreadPlan, GetStopReason);
+
+ return eStopReasonNone;
+}
+
+size_t SBThreadPlan::GetStopReasonDataCount() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBThreadPlan, GetStopReasonDataCount);
+
+ return 0;
+}
+
+uint64_t SBThreadPlan::GetStopReasonDataAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(uint64_t, SBThreadPlan, GetStopReasonDataAtIndex,
+ (uint32_t), idx);
+
+ return 0;
+}
+
+SBThread SBThreadPlan::GetThread() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBThread, SBThreadPlan, GetThread);
+
+ if (m_opaque_sp) {
+ return LLDB_RECORD_RESULT(
+ SBThread(m_opaque_sp->GetThread().shared_from_this()));
+ } else
+ return LLDB_RECORD_RESULT(SBThread());
+}
+
+bool SBThreadPlan::GetDescription(lldb::SBStream &description) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBThreadPlan, GetDescription,
+ (lldb::SBStream &), description);
+
+ if (m_opaque_sp) {
+ m_opaque_sp->GetDescription(description.get(), eDescriptionLevelFull);
+ } else {
+ description.Printf("Empty SBThreadPlan");
+ }
+ return true;
+}
+
+void SBThreadPlan::SetThreadPlan(const ThreadPlanSP &lldb_object_sp) {
+ m_opaque_sp = lldb_object_sp;
+}
+
+void SBThreadPlan::SetPlanComplete(bool success) {
+ LLDB_RECORD_METHOD(void, SBThreadPlan, SetPlanComplete, (bool), success);
+
+ if (m_opaque_sp)
+ m_opaque_sp->SetPlanComplete(success);
+}
+
+bool SBThreadPlan::IsPlanComplete() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBThreadPlan, IsPlanComplete);
+
+ if (m_opaque_sp)
+ return m_opaque_sp->IsPlanComplete();
+ else
+ return true;
+}
+
+bool SBThreadPlan::IsPlanStale() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBThreadPlan, IsPlanStale);
+
+ if (m_opaque_sp)
+ return m_opaque_sp->IsPlanStale();
+ else
+ return true;
+}
+
+bool SBThreadPlan::IsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBThreadPlan, IsValid);
+
+ if (m_opaque_sp)
+ return m_opaque_sp->ValidatePlan(nullptr);
+ else
+ return false;
+}
+
+// This section allows an SBThreadPlan to push another of the common types of
+// plans...
+//
+// FIXME, you should only be able to queue thread plans from inside the methods
+// of a Scripted Thread Plan. Need a way to enforce that.
+
+SBThreadPlan
+SBThreadPlan::QueueThreadPlanForStepOverRange(SBAddress &sb_start_address,
+ lldb::addr_t size) {
+ LLDB_RECORD_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepOverRange,
+ (lldb::SBAddress &, lldb::addr_t), sb_start_address, size);
+
+ SBError error;
+ return LLDB_RECORD_RESULT(
+ QueueThreadPlanForStepOverRange(sb_start_address, size, error));
+}
+
+SBThreadPlan SBThreadPlan::QueueThreadPlanForStepOverRange(
+ SBAddress &sb_start_address, lldb::addr_t size, SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepOverRange,
+ (lldb::SBAddress &, lldb::addr_t, lldb::SBError &),
+ sb_start_address, size, error);
+
+ if (m_opaque_sp) {
+ Address *start_address = sb_start_address.get();
+ if (!start_address) {
+ return LLDB_RECORD_RESULT(SBThreadPlan());
+ }
+
+ AddressRange range(*start_address, size);
+ SymbolContext sc;
+ start_address->CalculateSymbolContext(&sc);
+ Status plan_status;
+
+ SBThreadPlan plan =
+ SBThreadPlan(m_opaque_sp->GetThread().QueueThreadPlanForStepOverRange(
+ false, range, sc, eAllThreads, plan_status));
+
+ if (plan_status.Fail())
+ error.SetErrorString(plan_status.AsCString());
+
+ return LLDB_RECORD_RESULT(plan);
+ } else {
+ return LLDB_RECORD_RESULT(SBThreadPlan());
+ }
+}
+
+SBThreadPlan
+SBThreadPlan::QueueThreadPlanForStepInRange(SBAddress &sb_start_address,
+ lldb::addr_t size) {
+ LLDB_RECORD_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepInRange,
+ (lldb::SBAddress &, lldb::addr_t), sb_start_address, size);
+
+ SBError error;
+ return LLDB_RECORD_RESULT(
+ QueueThreadPlanForStepInRange(sb_start_address, size, error));
+}
+
+SBThreadPlan
+SBThreadPlan::QueueThreadPlanForStepInRange(SBAddress &sb_start_address,
+ lldb::addr_t size, SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepInRange,
+ (lldb::SBAddress &, lldb::addr_t, lldb::SBError &),
+ sb_start_address, size, error);
+
+ if (m_opaque_sp) {
+ Address *start_address = sb_start_address.get();
+ if (!start_address) {
+ return LLDB_RECORD_RESULT(SBThreadPlan());
+ }
+
+ AddressRange range(*start_address, size);
+ SymbolContext sc;
+ start_address->CalculateSymbolContext(&sc);
+
+ Status plan_status;
+ SBThreadPlan plan =
+ SBThreadPlan(m_opaque_sp->GetThread().QueueThreadPlanForStepInRange(
+ false, range, sc, nullptr, eAllThreads, plan_status));
+
+ if (plan_status.Fail())
+ error.SetErrorString(plan_status.AsCString());
+
+ return LLDB_RECORD_RESULT(plan);
+ } else {
+ return LLDB_RECORD_RESULT(SBThreadPlan());
+ }
+}
+
+SBThreadPlan
+SBThreadPlan::QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to,
+ bool first_insn) {
+ LLDB_RECORD_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepOut, (uint32_t, bool),
+ frame_idx_to_step_to, first_insn);
+
+ SBError error;
+ return LLDB_RECORD_RESULT(
+ QueueThreadPlanForStepOut(frame_idx_to_step_to, first_insn, error));
+}
+
+SBThreadPlan
+SBThreadPlan::QueueThreadPlanForStepOut(uint32_t frame_idx_to_step_to,
+ bool first_insn, SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepOut,
+ (uint32_t, bool, lldb::SBError &), frame_idx_to_step_to,
+ first_insn, error);
+
+ if (m_opaque_sp) {
+ SymbolContext sc;
+ sc = m_opaque_sp->GetThread().GetStackFrameAtIndex(0)->GetSymbolContext(
+ lldb::eSymbolContextEverything);
+
+ Status plan_status;
+ SBThreadPlan plan =
+ SBThreadPlan(m_opaque_sp->GetThread().QueueThreadPlanForStepOut(
+ false, &sc, first_insn, false, eVoteYes, eVoteNoOpinion,
+ frame_idx_to_step_to, plan_status));
+
+ if (plan_status.Fail())
+ error.SetErrorString(plan_status.AsCString());
+
+ return LLDB_RECORD_RESULT(plan);
+ } else {
+ return LLDB_RECORD_RESULT(SBThreadPlan());
+ }
+}
+
+SBThreadPlan
+SBThreadPlan::QueueThreadPlanForRunToAddress(SBAddress sb_address) {
+ LLDB_RECORD_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForRunToAddress, (lldb::SBAddress),
+ sb_address);
+
+ SBError error;
+ return LLDB_RECORD_RESULT(QueueThreadPlanForRunToAddress(sb_address, error));
+}
+
+SBThreadPlan SBThreadPlan::QueueThreadPlanForRunToAddress(SBAddress sb_address,
+ SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForRunToAddress,
+ (lldb::SBAddress, lldb::SBError &), sb_address, error);
+
+ if (m_opaque_sp) {
+ Address *address = sb_address.get();
+ if (!address)
+ return LLDB_RECORD_RESULT(SBThreadPlan());
+
+ Status plan_status;
+ SBThreadPlan plan =
+ SBThreadPlan(m_opaque_sp->GetThread().QueueThreadPlanForRunToAddress(
+ false, *address, false, plan_status));
+
+ if (plan_status.Fail())
+ error.SetErrorString(plan_status.AsCString());
+
+ return LLDB_RECORD_RESULT(plan);
+ } else {
+ return LLDB_RECORD_RESULT(SBThreadPlan());
+ }
+}
+
+SBThreadPlan
+SBThreadPlan::QueueThreadPlanForStepScripted(const char *script_class_name) {
+ LLDB_RECORD_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepScripted, (const char *),
+ script_class_name);
+
+ SBError error;
+ return LLDB_RECORD_RESULT(
+ QueueThreadPlanForStepScripted(script_class_name, error));
+}
+
+SBThreadPlan
+SBThreadPlan::QueueThreadPlanForStepScripted(const char *script_class_name,
+ SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepScripted,
+ (const char *, lldb::SBError &), script_class_name, error);
+
+ if (m_opaque_sp) {
+ Status plan_status;
+ SBThreadPlan plan =
+ SBThreadPlan(m_opaque_sp->GetThread().QueueThreadPlanForStepScripted(
+ false, script_class_name, false, plan_status));
+
+ if (plan_status.Fail())
+ error.SetErrorString(plan_status.AsCString());
+
+ return LLDB_RECORD_RESULT(plan);
+ } else {
+ return LLDB_RECORD_RESULT(SBThreadPlan());
+ }
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBThreadPlan>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBThreadPlan, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBThreadPlan, (const lldb::ThreadPlanSP &));
+ LLDB_REGISTER_CONSTRUCTOR(SBThreadPlan, (const lldb::SBThreadPlan &));
+ LLDB_REGISTER_CONSTRUCTOR(SBThreadPlan, (lldb::SBThread &, const char *));
+ LLDB_REGISTER_METHOD(const lldb::SBThreadPlan &,
+ SBThreadPlan, operator=,(const lldb::SBThreadPlan &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBThreadPlan, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBThreadPlan, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBThreadPlan, Clear, ());
+ LLDB_REGISTER_METHOD(lldb::StopReason, SBThreadPlan, GetStopReason, ());
+ LLDB_REGISTER_METHOD(size_t, SBThreadPlan, GetStopReasonDataCount, ());
+ LLDB_REGISTER_METHOD(uint64_t, SBThreadPlan, GetStopReasonDataAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBThread, SBThreadPlan, GetThread, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBThreadPlan, GetDescription,
+ (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(void, SBThreadPlan, SetPlanComplete, (bool));
+ LLDB_REGISTER_METHOD(bool, SBThreadPlan, IsPlanComplete, ());
+ LLDB_REGISTER_METHOD(bool, SBThreadPlan, IsPlanStale, ());
+ LLDB_REGISTER_METHOD(bool, SBThreadPlan, IsValid, ());
+ LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepOverRange,
+ (lldb::SBAddress &, lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepOverRange,
+ (lldb::SBAddress &, lldb::addr_t, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepInRange,
+ (lldb::SBAddress &, lldb::addr_t));
+ LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepInRange,
+ (lldb::SBAddress &, lldb::addr_t, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepOut, (uint32_t, bool));
+ LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepOut,
+ (uint32_t, bool, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForRunToAddress, (lldb::SBAddress));
+ LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForRunToAddress,
+ (lldb::SBAddress, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepScripted, (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBThreadPlan, SBThreadPlan,
+ QueueThreadPlanForStepScripted,
+ (const char *, lldb::SBError &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBTrace.cpp b/contrib/llvm-project/lldb/source/API/SBTrace.cpp
new file mode 100644
index 000000000000..9b871e6781d9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBTrace.cpp
@@ -0,0 +1,147 @@
+//===-- SBTrace.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SBReproducerPrivate.h"
+#include "lldb/Target/Process.h"
+
+#include "lldb/API/SBTrace.h"
+#include "lldb/API/SBTraceOptions.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+class TraceImpl {
+public:
+ lldb::user_id_t uid;
+};
+
+lldb::ProcessSP SBTrace::GetSP() const { return m_opaque_wp.lock(); }
+
+size_t SBTrace::GetTraceData(SBError &error, void *buf, size_t size,
+ size_t offset, lldb::tid_t thread_id) {
+ LLDB_RECORD_DUMMY(size_t, SBTrace, GetTraceData,
+ (lldb::SBError &, void *, size_t, size_t, lldb::tid_t),
+ error, buf, size, offset, thread_id);
+
+ ProcessSP process_sp(GetSP());
+ llvm::MutableArrayRef<uint8_t> buffer(static_cast<uint8_t *>(buf), size);
+ error.Clear();
+
+ if (!process_sp) {
+ error.SetErrorString("invalid process");
+ } else {
+ error.SetError(
+ process_sp->GetData(GetTraceUID(), thread_id, buffer, offset));
+ }
+ return buffer.size();
+}
+
+size_t SBTrace::GetMetaData(SBError &error, void *buf, size_t size,
+ size_t offset, lldb::tid_t thread_id) {
+ LLDB_RECORD_DUMMY(size_t, SBTrace, GetMetaData,
+ (lldb::SBError &, void *, size_t, size_t, lldb::tid_t),
+ error, buf, size, offset, thread_id);
+
+ ProcessSP process_sp(GetSP());
+ llvm::MutableArrayRef<uint8_t> buffer(static_cast<uint8_t *>(buf), size);
+ error.Clear();
+
+ if (!process_sp) {
+ error.SetErrorString("invalid process");
+ } else {
+ error.SetError(
+ process_sp->GetMetaData(GetTraceUID(), thread_id, buffer, offset));
+ }
+ return buffer.size();
+}
+
+void SBTrace::StopTrace(SBError &error, lldb::tid_t thread_id) {
+ LLDB_RECORD_METHOD(void, SBTrace, StopTrace, (lldb::SBError &, lldb::tid_t),
+ error, thread_id);
+
+ ProcessSP process_sp(GetSP());
+ error.Clear();
+
+ if (!process_sp) {
+ error.SetErrorString("invalid process");
+ return;
+ }
+ error.SetError(process_sp->StopTrace(GetTraceUID(), thread_id));
+}
+
+void SBTrace::GetTraceConfig(SBTraceOptions &options, SBError &error) {
+ LLDB_RECORD_METHOD(void, SBTrace, GetTraceConfig,
+ (lldb::SBTraceOptions &, lldb::SBError &), options, error);
+
+ ProcessSP process_sp(GetSP());
+ error.Clear();
+
+ if (!process_sp) {
+ error.SetErrorString("invalid process");
+ } else {
+ error.SetError(process_sp->GetTraceConfig(GetTraceUID(),
+ *(options.m_traceoptions_sp)));
+ }
+}
+
+lldb::user_id_t SBTrace::GetTraceUID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::user_id_t, SBTrace, GetTraceUID);
+
+ if (m_trace_impl_sp)
+ return m_trace_impl_sp->uid;
+ return LLDB_INVALID_UID;
+}
+
+void SBTrace::SetTraceUID(lldb::user_id_t uid) {
+ if (m_trace_impl_sp)
+ m_trace_impl_sp->uid = uid;
+}
+
+SBTrace::SBTrace() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTrace);
+
+ m_trace_impl_sp = std::make_shared<TraceImpl>();
+ if (m_trace_impl_sp)
+ m_trace_impl_sp->uid = LLDB_INVALID_UID;
+}
+
+void SBTrace::SetSP(const ProcessSP &process_sp) { m_opaque_wp = process_sp; }
+
+bool SBTrace::IsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTrace, IsValid);
+ return this->operator bool();
+}
+SBTrace::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTrace, operator bool);
+
+ if (!m_trace_impl_sp)
+ return false;
+ if (!GetSP())
+ return false;
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBTrace>(Registry &R) {
+ LLDB_REGISTER_METHOD(void, SBTrace, StopTrace,
+ (lldb::SBError &, lldb::tid_t));
+ LLDB_REGISTER_METHOD(void, SBTrace, GetTraceConfig,
+ (lldb::SBTraceOptions &, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::user_id_t, SBTrace, GetTraceUID, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTrace, ());
+ LLDB_REGISTER_METHOD(bool, SBTrace, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTrace, operator bool, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBTraceOptions.cpp b/contrib/llvm-project/lldb/source/API/SBTraceOptions.cpp
new file mode 100644
index 000000000000..a24cdd59af0b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBTraceOptions.cpp
@@ -0,0 +1,159 @@
+//===-- SBTraceOptions.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBTraceOptions.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBStructuredData.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/TraceOptions.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBTraceOptions::SBTraceOptions() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTraceOptions);
+
+ m_traceoptions_sp = std::make_shared<TraceOptions>();
+}
+
+lldb::TraceType SBTraceOptions::getType() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::TraceType, SBTraceOptions, getType);
+
+ if (m_traceoptions_sp)
+ return m_traceoptions_sp->getType();
+ return lldb::TraceType::eTraceTypeNone;
+}
+
+uint64_t SBTraceOptions::getTraceBufferSize() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint64_t, SBTraceOptions,
+ getTraceBufferSize);
+
+ if (m_traceoptions_sp)
+ return m_traceoptions_sp->getTraceBufferSize();
+ return 0;
+}
+
+lldb::SBStructuredData SBTraceOptions::getTraceParams(lldb::SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBStructuredData, SBTraceOptions, getTraceParams,
+ (lldb::SBError &), error);
+
+ error.Clear();
+ const lldb_private::StructuredData::DictionarySP dict_obj =
+ m_traceoptions_sp->getTraceParams();
+ lldb::SBStructuredData structData;
+ if (dict_obj && structData.m_impl_up)
+ structData.m_impl_up->SetObjectSP(dict_obj->shared_from_this());
+ else
+ error.SetErrorString("Empty trace params");
+ return LLDB_RECORD_RESULT(structData);
+}
+
+uint64_t SBTraceOptions::getMetaDataBufferSize() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint64_t, SBTraceOptions,
+ getMetaDataBufferSize);
+
+ if (m_traceoptions_sp)
+ return m_traceoptions_sp->getTraceBufferSize();
+ return 0;
+}
+
+void SBTraceOptions::setTraceParams(lldb::SBStructuredData &params) {
+ LLDB_RECORD_METHOD(void, SBTraceOptions, setTraceParams,
+ (lldb::SBStructuredData &), params);
+
+ if (m_traceoptions_sp && params.m_impl_up) {
+ StructuredData::ObjectSP obj_sp = params.m_impl_up->GetObjectSP();
+ if (obj_sp && obj_sp->GetAsDictionary() != nullptr)
+ m_traceoptions_sp->setTraceParams(
+ std::static_pointer_cast<StructuredData::Dictionary>(obj_sp));
+ }
+ return;
+}
+
+void SBTraceOptions::setType(lldb::TraceType type) {
+ LLDB_RECORD_METHOD(void, SBTraceOptions, setType, (lldb::TraceType), type);
+
+ if (m_traceoptions_sp)
+ m_traceoptions_sp->setType(type);
+}
+
+void SBTraceOptions::setTraceBufferSize(uint64_t size) {
+ LLDB_RECORD_METHOD(void, SBTraceOptions, setTraceBufferSize, (uint64_t),
+ size);
+
+ if (m_traceoptions_sp)
+ m_traceoptions_sp->setTraceBufferSize(size);
+}
+
+void SBTraceOptions::setMetaDataBufferSize(uint64_t size) {
+ LLDB_RECORD_METHOD(void, SBTraceOptions, setMetaDataBufferSize, (uint64_t),
+ size);
+
+ if (m_traceoptions_sp)
+ m_traceoptions_sp->setMetaDataBufferSize(size);
+}
+
+bool SBTraceOptions::IsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTraceOptions, IsValid);
+ return this->operator bool();
+}
+SBTraceOptions::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTraceOptions, operator bool);
+
+ if (m_traceoptions_sp)
+ return true;
+ return false;
+}
+
+void SBTraceOptions::setThreadID(lldb::tid_t thread_id) {
+ LLDB_RECORD_METHOD(void, SBTraceOptions, setThreadID, (lldb::tid_t),
+ thread_id);
+
+ if (m_traceoptions_sp)
+ m_traceoptions_sp->setThreadID(thread_id);
+}
+
+lldb::tid_t SBTraceOptions::getThreadID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::tid_t, SBTraceOptions, getThreadID);
+
+ if (m_traceoptions_sp)
+ return m_traceoptions_sp->getThreadID();
+ return LLDB_INVALID_THREAD_ID;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBTraceOptions>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBTraceOptions, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::TraceType, SBTraceOptions, getType, ());
+ LLDB_REGISTER_METHOD_CONST(uint64_t, SBTraceOptions, getTraceBufferSize,
+ ());
+ LLDB_REGISTER_METHOD(lldb::SBStructuredData, SBTraceOptions, getTraceParams,
+ (lldb::SBError &));
+ LLDB_REGISTER_METHOD_CONST(uint64_t, SBTraceOptions, getMetaDataBufferSize,
+ ());
+ LLDB_REGISTER_METHOD(void, SBTraceOptions, setTraceParams,
+ (lldb::SBStructuredData &));
+ LLDB_REGISTER_METHOD(void, SBTraceOptions, setType, (lldb::TraceType));
+ LLDB_REGISTER_METHOD(void, SBTraceOptions, setTraceBufferSize, (uint64_t));
+ LLDB_REGISTER_METHOD(void, SBTraceOptions, setMetaDataBufferSize,
+ (uint64_t));
+ LLDB_REGISTER_METHOD(bool, SBTraceOptions, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTraceOptions, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBTraceOptions, setThreadID, (lldb::tid_t));
+ LLDB_REGISTER_METHOD(lldb::tid_t, SBTraceOptions, getThreadID, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBType.cpp b/contrib/llvm-project/lldb/source/API/SBType.cpp
new file mode 100644
index 000000000000..5402128b3fae
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBType.cpp
@@ -0,0 +1,1008 @@
+//===-- SBType.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBType.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBDefines.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBTypeEnumMember.h"
+#include "lldb/Core/Mangled.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/APSInt.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBType::SBType() : m_opaque_sp() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBType); }
+
+SBType::SBType(const CompilerType &type)
+ : m_opaque_sp(new TypeImpl(
+ CompilerType(type.GetTypeSystem(), type.GetOpaqueQualType()))) {}
+
+SBType::SBType(const lldb::TypeSP &type_sp)
+ : m_opaque_sp(new TypeImpl(type_sp)) {}
+
+SBType::SBType(const lldb::TypeImplSP &type_impl_sp)
+ : m_opaque_sp(type_impl_sp) {}
+
+SBType::SBType(const SBType &rhs) : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR(SBType, (const lldb::SBType &), rhs);
+
+ if (this != &rhs) {
+ m_opaque_sp = rhs.m_opaque_sp;
+ }
+}
+
+// SBType::SBType (TypeImpl* impl) :
+// m_opaque_up(impl)
+//{}
+//
+bool SBType::operator==(SBType &rhs) {
+ LLDB_RECORD_METHOD(bool, SBType, operator==,(lldb::SBType &), rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+
+ if (!rhs.IsValid())
+ return false;
+
+ return *m_opaque_sp.get() == *rhs.m_opaque_sp.get();
+}
+
+bool SBType::operator!=(SBType &rhs) {
+ LLDB_RECORD_METHOD(bool, SBType, operator!=,(lldb::SBType &), rhs);
+
+ if (!IsValid())
+ return rhs.IsValid();
+
+ if (!rhs.IsValid())
+ return true;
+
+ return *m_opaque_sp.get() != *rhs.m_opaque_sp.get();
+}
+
+lldb::TypeImplSP SBType::GetSP() { return m_opaque_sp; }
+
+void SBType::SetSP(const lldb::TypeImplSP &type_impl_sp) {
+ m_opaque_sp = type_impl_sp;
+}
+
+SBType &SBType::operator=(const SBType &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBType &, SBType, operator=,(const lldb::SBType &),
+ rhs);
+
+ if (this != &rhs) {
+ m_opaque_sp = rhs.m_opaque_sp;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBType::~SBType() {}
+
+TypeImpl &SBType::ref() {
+ if (m_opaque_sp.get() == nullptr)
+ m_opaque_sp = std::make_shared<TypeImpl>();
+ return *m_opaque_sp;
+}
+
+const TypeImpl &SBType::ref() const {
+ // "const SBAddress &addr" should already have checked "addr.IsValid()" prior
+ // to calling this function. In case you didn't we will assert and die to let
+ // you know.
+ assert(m_opaque_sp.get());
+ return *m_opaque_sp;
+}
+
+bool SBType::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBType, IsValid);
+ return this->operator bool();
+}
+SBType::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBType, operator bool);
+
+ if (m_opaque_sp.get() == nullptr)
+ return false;
+
+ return m_opaque_sp->IsValid();
+}
+
+uint64_t SBType::GetByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint64_t, SBType, GetByteSize);
+
+ if (IsValid())
+ if (llvm::Optional<uint64_t> size =
+ m_opaque_sp->GetCompilerType(false).GetByteSize(nullptr))
+ return *size;
+ return 0;
+}
+
+bool SBType::IsPointerType() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBType, IsPointerType);
+
+ if (!IsValid())
+ return false;
+ return m_opaque_sp->GetCompilerType(true).IsPointerType();
+}
+
+bool SBType::IsArrayType() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBType, IsArrayType);
+
+ if (!IsValid())
+ return false;
+ return m_opaque_sp->GetCompilerType(true).IsArrayType(nullptr, nullptr,
+ nullptr);
+}
+
+bool SBType::IsVectorType() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBType, IsVectorType);
+
+ if (!IsValid())
+ return false;
+ return m_opaque_sp->GetCompilerType(true).IsVectorType(nullptr, nullptr);
+}
+
+bool SBType::IsReferenceType() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBType, IsReferenceType);
+
+ if (!IsValid())
+ return false;
+ return m_opaque_sp->GetCompilerType(true).IsReferenceType();
+}
+
+SBType SBType::GetPointerType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBType, GetPointerType);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBType());
+
+ return LLDB_RECORD_RESULT(
+ SBType(TypeImplSP(new TypeImpl(m_opaque_sp->GetPointerType()))));
+}
+
+SBType SBType::GetPointeeType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBType, GetPointeeType);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBType());
+ return LLDB_RECORD_RESULT(
+ SBType(TypeImplSP(new TypeImpl(m_opaque_sp->GetPointeeType()))));
+}
+
+SBType SBType::GetReferenceType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBType, GetReferenceType);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBType());
+ return LLDB_RECORD_RESULT(
+ SBType(TypeImplSP(new TypeImpl(m_opaque_sp->GetReferenceType()))));
+}
+
+SBType SBType::GetTypedefedType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBType, GetTypedefedType);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBType());
+ return LLDB_RECORD_RESULT(
+ SBType(TypeImplSP(new TypeImpl(m_opaque_sp->GetTypedefedType()))));
+}
+
+SBType SBType::GetDereferencedType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBType, GetDereferencedType);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBType());
+ return LLDB_RECORD_RESULT(
+ SBType(TypeImplSP(new TypeImpl(m_opaque_sp->GetDereferencedType()))));
+}
+
+SBType SBType::GetArrayElementType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBType, GetArrayElementType);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBType());
+ return LLDB_RECORD_RESULT(SBType(TypeImplSP(
+ new TypeImpl(m_opaque_sp->GetCompilerType(true).GetArrayElementType()))));
+}
+
+SBType SBType::GetArrayType(uint64_t size) {
+ LLDB_RECORD_METHOD(lldb::SBType, SBType, GetArrayType, (uint64_t), size);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBType());
+ return LLDB_RECORD_RESULT(SBType(TypeImplSP(
+ new TypeImpl(m_opaque_sp->GetCompilerType(true).GetArrayType(size)))));
+}
+
+SBType SBType::GetVectorElementType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBType, GetVectorElementType);
+
+ SBType type_sb;
+ if (IsValid()) {
+ CompilerType vector_element_type;
+ if (m_opaque_sp->GetCompilerType(true).IsVectorType(&vector_element_type,
+ nullptr))
+ type_sb.SetSP(TypeImplSP(new TypeImpl(vector_element_type)));
+ }
+ return LLDB_RECORD_RESULT(type_sb);
+}
+
+bool SBType::IsFunctionType() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBType, IsFunctionType);
+
+ if (!IsValid())
+ return false;
+ return m_opaque_sp->GetCompilerType(true).IsFunctionType();
+}
+
+bool SBType::IsPolymorphicClass() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBType, IsPolymorphicClass);
+
+ if (!IsValid())
+ return false;
+ return m_opaque_sp->GetCompilerType(true).IsPolymorphicClass();
+}
+
+bool SBType::IsTypedefType() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBType, IsTypedefType);
+
+ if (!IsValid())
+ return false;
+ return m_opaque_sp->GetCompilerType(true).IsTypedefType();
+}
+
+bool SBType::IsAnonymousType() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBType, IsAnonymousType);
+
+ if (!IsValid())
+ return false;
+ return m_opaque_sp->GetCompilerType(true).IsAnonymousType();
+}
+
+lldb::SBType SBType::GetFunctionReturnType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBType, GetFunctionReturnType);
+
+ if (IsValid()) {
+ CompilerType return_type(
+ m_opaque_sp->GetCompilerType(true).GetFunctionReturnType());
+ if (return_type.IsValid())
+ return LLDB_RECORD_RESULT(SBType(return_type));
+ }
+ return LLDB_RECORD_RESULT(lldb::SBType());
+}
+
+lldb::SBTypeList SBType::GetFunctionArgumentTypes() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBTypeList, SBType,
+ GetFunctionArgumentTypes);
+
+ SBTypeList sb_type_list;
+ if (IsValid()) {
+ CompilerType func_type(m_opaque_sp->GetCompilerType(true));
+ size_t count = func_type.GetNumberOfFunctionArguments();
+ for (size_t i = 0; i < count; i++) {
+ sb_type_list.Append(SBType(func_type.GetFunctionArgumentAtIndex(i)));
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_type_list);
+}
+
+uint32_t SBType::GetNumberOfMemberFunctions() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBType, GetNumberOfMemberFunctions);
+
+ if (IsValid()) {
+ return m_opaque_sp->GetCompilerType(true).GetNumMemberFunctions();
+ }
+ return 0;
+}
+
+lldb::SBTypeMemberFunction SBType::GetMemberFunctionAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBTypeMemberFunction, SBType,
+ GetMemberFunctionAtIndex, (uint32_t), idx);
+
+ SBTypeMemberFunction sb_func_type;
+ if (IsValid())
+ sb_func_type.reset(new TypeMemberFunctionImpl(
+ m_opaque_sp->GetCompilerType(true).GetMemberFunctionAtIndex(idx)));
+ return LLDB_RECORD_RESULT(sb_func_type);
+}
+
+lldb::SBType SBType::GetUnqualifiedType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBType, GetUnqualifiedType);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBType());
+ return LLDB_RECORD_RESULT(
+ SBType(TypeImplSP(new TypeImpl(m_opaque_sp->GetUnqualifiedType()))));
+}
+
+lldb::SBType SBType::GetCanonicalType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBType, GetCanonicalType);
+
+ if (IsValid())
+ return LLDB_RECORD_RESULT(
+ SBType(TypeImplSP(new TypeImpl(m_opaque_sp->GetCanonicalType()))));
+ return LLDB_RECORD_RESULT(SBType());
+}
+
+lldb::BasicType SBType::GetBasicType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::BasicType, SBType, GetBasicType);
+
+ if (IsValid())
+ return m_opaque_sp->GetCompilerType(false).GetBasicTypeEnumeration();
+ return eBasicTypeInvalid;
+}
+
+SBType SBType::GetBasicType(lldb::BasicType basic_type) {
+ LLDB_RECORD_METHOD(lldb::SBType, SBType, GetBasicType, (lldb::BasicType),
+ basic_type);
+
+ if (IsValid() && m_opaque_sp->IsValid())
+ return LLDB_RECORD_RESULT(SBType(
+ m_opaque_sp->GetTypeSystem(false)->GetBasicTypeFromAST(basic_type)));
+ return LLDB_RECORD_RESULT(SBType());
+}
+
+uint32_t SBType::GetNumberOfDirectBaseClasses() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBType, GetNumberOfDirectBaseClasses);
+
+ if (IsValid())
+ return m_opaque_sp->GetCompilerType(true).GetNumDirectBaseClasses();
+ return 0;
+}
+
+uint32_t SBType::GetNumberOfVirtualBaseClasses() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBType, GetNumberOfVirtualBaseClasses);
+
+ if (IsValid())
+ return m_opaque_sp->GetCompilerType(true).GetNumVirtualBaseClasses();
+ return 0;
+}
+
+uint32_t SBType::GetNumberOfFields() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBType, GetNumberOfFields);
+
+ if (IsValid())
+ return m_opaque_sp->GetCompilerType(true).GetNumFields();
+ return 0;
+}
+
+bool SBType::GetDescription(SBStream &description,
+ lldb::DescriptionLevel description_level) {
+ LLDB_RECORD_METHOD(bool, SBType, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ description_level);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_sp) {
+ m_opaque_sp->GetDescription(strm, description_level);
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+SBTypeMember SBType::GetDirectBaseClassAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBTypeMember, SBType, GetDirectBaseClassAtIndex,
+ (uint32_t), idx);
+
+ SBTypeMember sb_type_member;
+ if (IsValid()) {
+ uint32_t bit_offset = 0;
+ CompilerType base_class_type =
+ m_opaque_sp->GetCompilerType(true).GetDirectBaseClassAtIndex(
+ idx, &bit_offset);
+ if (base_class_type.IsValid())
+ sb_type_member.reset(new TypeMemberImpl(
+ TypeImplSP(new TypeImpl(base_class_type)), bit_offset));
+ }
+ return LLDB_RECORD_RESULT(sb_type_member);
+}
+
+SBTypeMember SBType::GetVirtualBaseClassAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBTypeMember, SBType, GetVirtualBaseClassAtIndex,
+ (uint32_t), idx);
+
+ SBTypeMember sb_type_member;
+ if (IsValid()) {
+ uint32_t bit_offset = 0;
+ CompilerType base_class_type =
+ m_opaque_sp->GetCompilerType(true).GetVirtualBaseClassAtIndex(
+ idx, &bit_offset);
+ if (base_class_type.IsValid())
+ sb_type_member.reset(new TypeMemberImpl(
+ TypeImplSP(new TypeImpl(base_class_type)), bit_offset));
+ }
+ return LLDB_RECORD_RESULT(sb_type_member);
+}
+
+SBTypeEnumMemberList SBType::GetEnumMembers() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBTypeEnumMemberList, SBType,
+ GetEnumMembers);
+
+ SBTypeEnumMemberList sb_enum_member_list;
+ if (IsValid()) {
+ CompilerType this_type(m_opaque_sp->GetCompilerType(true));
+ if (this_type.IsValid()) {
+ this_type.ForEachEnumerator([&sb_enum_member_list](
+ const CompilerType &integer_type,
+ ConstString name,
+ const llvm::APSInt &value) -> bool {
+ SBTypeEnumMember enum_member(
+ lldb::TypeEnumMemberImplSP(new TypeEnumMemberImpl(
+ lldb::TypeImplSP(new TypeImpl(integer_type)), name, value)));
+ sb_enum_member_list.Append(enum_member);
+ return true; // Keep iterating
+ });
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_enum_member_list);
+}
+
+SBTypeMember SBType::GetFieldAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBTypeMember, SBType, GetFieldAtIndex, (uint32_t),
+ idx);
+
+ SBTypeMember sb_type_member;
+ if (IsValid()) {
+ CompilerType this_type(m_opaque_sp->GetCompilerType(false));
+ if (this_type.IsValid()) {
+ uint64_t bit_offset = 0;
+ uint32_t bitfield_bit_size = 0;
+ bool is_bitfield = false;
+ std::string name_sstr;
+ CompilerType field_type(this_type.GetFieldAtIndex(
+ idx, name_sstr, &bit_offset, &bitfield_bit_size, &is_bitfield));
+ if (field_type.IsValid()) {
+ ConstString name;
+ if (!name_sstr.empty())
+ name.SetCString(name_sstr.c_str());
+ sb_type_member.reset(
+ new TypeMemberImpl(TypeImplSP(new TypeImpl(field_type)), bit_offset,
+ name, bitfield_bit_size, is_bitfield));
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_type_member);
+}
+
+bool SBType::IsTypeComplete() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBType, IsTypeComplete);
+
+ if (!IsValid())
+ return false;
+ return m_opaque_sp->GetCompilerType(false).IsCompleteType();
+}
+
+uint32_t SBType::GetTypeFlags() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBType, GetTypeFlags);
+
+ if (!IsValid())
+ return 0;
+ return m_opaque_sp->GetCompilerType(true).GetTypeInfo();
+}
+
+const char *SBType::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBType, GetName);
+
+ if (!IsValid())
+ return "";
+ return m_opaque_sp->GetName().GetCString();
+}
+
+const char *SBType::GetDisplayTypeName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBType, GetDisplayTypeName);
+
+ if (!IsValid())
+ return "";
+ return m_opaque_sp->GetDisplayTypeName().GetCString();
+}
+
+lldb::TypeClass SBType::GetTypeClass() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::TypeClass, SBType, GetTypeClass);
+
+ if (IsValid())
+ return m_opaque_sp->GetCompilerType(true).GetTypeClass();
+ return lldb::eTypeClassInvalid;
+}
+
+uint32_t SBType::GetNumberOfTemplateArguments() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBType, GetNumberOfTemplateArguments);
+
+ if (IsValid())
+ return m_opaque_sp->GetCompilerType(false).GetNumTemplateArguments();
+ return 0;
+}
+
+lldb::SBType SBType::GetTemplateArgumentType(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBType, SBType, GetTemplateArgumentType, (uint32_t),
+ idx);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBType());
+
+ CompilerType type;
+ switch(GetTemplateArgumentKind(idx)) {
+ case eTemplateArgumentKindType:
+ type = m_opaque_sp->GetCompilerType(false).GetTypeTemplateArgument(idx);
+ break;
+ case eTemplateArgumentKindIntegral:
+ type = m_opaque_sp->GetCompilerType(false)
+ .GetIntegralTemplateArgument(idx)
+ ->type;
+ break;
+ default:
+ break;
+ }
+ if (type.IsValid())
+ return LLDB_RECORD_RESULT(SBType(type));
+ return LLDB_RECORD_RESULT(SBType());
+}
+
+lldb::TemplateArgumentKind SBType::GetTemplateArgumentKind(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::TemplateArgumentKind, SBType,
+ GetTemplateArgumentKind, (uint32_t), idx);
+
+ if (IsValid())
+ return m_opaque_sp->GetCompilerType(false).GetTemplateArgumentKind(idx);
+ return eTemplateArgumentKindNull;
+}
+
+SBTypeList::SBTypeList() : m_opaque_up(new TypeListImpl()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeList);
+}
+
+SBTypeList::SBTypeList(const SBTypeList &rhs)
+ : m_opaque_up(new TypeListImpl()) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeList, (const lldb::SBTypeList &), rhs);
+
+ for (uint32_t i = 0, rhs_size = const_cast<SBTypeList &>(rhs).GetSize();
+ i < rhs_size; i++)
+ Append(const_cast<SBTypeList &>(rhs).GetTypeAtIndex(i));
+}
+
+bool SBTypeList::IsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTypeList, IsValid);
+ return this->operator bool();
+}
+SBTypeList::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeList, operator bool);
+
+ return (m_opaque_up != nullptr);
+}
+
+SBTypeList &SBTypeList::operator=(const SBTypeList &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBTypeList &,
+ SBTypeList, operator=,(const lldb::SBTypeList &), rhs);
+
+ if (this != &rhs) {
+ m_opaque_up.reset(new TypeListImpl());
+ for (uint32_t i = 0, rhs_size = const_cast<SBTypeList &>(rhs).GetSize();
+ i < rhs_size; i++)
+ Append(const_cast<SBTypeList &>(rhs).GetTypeAtIndex(i));
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+void SBTypeList::Append(SBType type) {
+ LLDB_RECORD_METHOD(void, SBTypeList, Append, (lldb::SBType), type);
+
+ if (type.IsValid())
+ m_opaque_up->Append(type.m_opaque_sp);
+}
+
+SBType SBTypeList::GetTypeAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBType, SBTypeList, GetTypeAtIndex, (uint32_t),
+ index);
+
+ if (m_opaque_up)
+ return LLDB_RECORD_RESULT(SBType(m_opaque_up->GetTypeAtIndex(index)));
+ return LLDB_RECORD_RESULT(SBType());
+}
+
+uint32_t SBTypeList::GetSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeList, GetSize);
+
+ return m_opaque_up->GetSize();
+}
+
+SBTypeList::~SBTypeList() {}
+
+SBTypeMember::SBTypeMember() : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeMember);
+}
+
+SBTypeMember::~SBTypeMember() {}
+
+SBTypeMember::SBTypeMember(const SBTypeMember &rhs) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeMember, (const lldb::SBTypeMember &), rhs);
+
+ if (this != &rhs) {
+ if (rhs.IsValid())
+ m_opaque_up.reset(new TypeMemberImpl(rhs.ref()));
+ }
+}
+
+lldb::SBTypeMember &SBTypeMember::operator=(const lldb::SBTypeMember &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBTypeMember &,
+ SBTypeMember, operator=,(const lldb::SBTypeMember &), rhs);
+
+ if (this != &rhs) {
+ if (rhs.IsValid())
+ m_opaque_up.reset(new TypeMemberImpl(rhs.ref()));
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBTypeMember::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeMember, IsValid);
+ return this->operator bool();
+}
+SBTypeMember::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeMember, operator bool);
+
+ return m_opaque_up.get();
+}
+
+const char *SBTypeMember::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTypeMember, GetName);
+
+ if (m_opaque_up)
+ return m_opaque_up->GetName().GetCString();
+ return nullptr;
+}
+
+SBType SBTypeMember::GetType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBTypeMember, GetType);
+
+ SBType sb_type;
+ if (m_opaque_up) {
+ sb_type.SetSP(m_opaque_up->GetTypeImpl());
+ }
+ return LLDB_RECORD_RESULT(sb_type);
+}
+
+uint64_t SBTypeMember::GetOffsetInBytes() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint64_t, SBTypeMember, GetOffsetInBytes);
+
+ if (m_opaque_up)
+ return m_opaque_up->GetBitOffset() / 8u;
+ return 0;
+}
+
+uint64_t SBTypeMember::GetOffsetInBits() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint64_t, SBTypeMember, GetOffsetInBits);
+
+ if (m_opaque_up)
+ return m_opaque_up->GetBitOffset();
+ return 0;
+}
+
+bool SBTypeMember::IsBitfield() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTypeMember, IsBitfield);
+
+ if (m_opaque_up)
+ return m_opaque_up->GetIsBitfield();
+ return false;
+}
+
+uint32_t SBTypeMember::GetBitfieldSizeInBits() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeMember, GetBitfieldSizeInBits);
+
+ if (m_opaque_up)
+ return m_opaque_up->GetBitfieldBitSize();
+ return 0;
+}
+
+bool SBTypeMember::GetDescription(lldb::SBStream &description,
+ lldb::DescriptionLevel description_level) {
+ LLDB_RECORD_METHOD(bool, SBTypeMember, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ description_level);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_up) {
+ const uint32_t bit_offset = m_opaque_up->GetBitOffset();
+ const uint32_t byte_offset = bit_offset / 8u;
+ const uint32_t byte_bit_offset = bit_offset % 8u;
+ const char *name = m_opaque_up->GetName().GetCString();
+ if (byte_bit_offset)
+ strm.Printf("+%u + %u bits: (", byte_offset, byte_bit_offset);
+ else
+ strm.Printf("+%u: (", byte_offset);
+
+ TypeImplSP type_impl_sp(m_opaque_up->GetTypeImpl());
+ if (type_impl_sp)
+ type_impl_sp->GetDescription(strm, description_level);
+
+ strm.Printf(") %s", name);
+ if (m_opaque_up->GetIsBitfield()) {
+ const uint32_t bitfield_bit_size = m_opaque_up->GetBitfieldBitSize();
+ strm.Printf(" : %u", bitfield_bit_size);
+ }
+ } else {
+ strm.PutCString("No value");
+ }
+ return true;
+}
+
+void SBTypeMember::reset(TypeMemberImpl *type_member_impl) {
+ m_opaque_up.reset(type_member_impl);
+}
+
+TypeMemberImpl &SBTypeMember::ref() {
+ if (m_opaque_up == nullptr)
+ m_opaque_up.reset(new TypeMemberImpl());
+ return *m_opaque_up;
+}
+
+const TypeMemberImpl &SBTypeMember::ref() const { return *m_opaque_up; }
+
+SBTypeMemberFunction::SBTypeMemberFunction() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeMemberFunction);
+}
+
+SBTypeMemberFunction::~SBTypeMemberFunction() {}
+
+SBTypeMemberFunction::SBTypeMemberFunction(const SBTypeMemberFunction &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeMemberFunction,
+ (const lldb::SBTypeMemberFunction &), rhs);
+}
+
+lldb::SBTypeMemberFunction &SBTypeMemberFunction::
+operator=(const lldb::SBTypeMemberFunction &rhs) {
+ LLDB_RECORD_METHOD(
+ lldb::SBTypeMemberFunction &,
+ SBTypeMemberFunction, operator=,(const lldb::SBTypeMemberFunction &),
+ rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = rhs.m_opaque_sp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBTypeMemberFunction::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeMemberFunction, IsValid);
+ return this->operator bool();
+}
+SBTypeMemberFunction::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeMemberFunction, operator bool);
+
+ return m_opaque_sp.get();
+}
+
+const char *SBTypeMemberFunction::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTypeMemberFunction, GetName);
+
+ if (m_opaque_sp)
+ return m_opaque_sp->GetName().GetCString();
+ return nullptr;
+}
+
+const char *SBTypeMemberFunction::GetDemangledName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTypeMemberFunction,
+ GetDemangledName);
+
+ if (m_opaque_sp) {
+ ConstString mangled_str = m_opaque_sp->GetMangledName();
+ if (mangled_str) {
+ Mangled mangled(mangled_str, true);
+ return mangled.GetDemangledName(mangled.GuessLanguage()).GetCString();
+ }
+ }
+ return nullptr;
+}
+
+const char *SBTypeMemberFunction::GetMangledName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTypeMemberFunction,
+ GetMangledName);
+
+ if (m_opaque_sp)
+ return m_opaque_sp->GetMangledName().GetCString();
+ return nullptr;
+}
+
+SBType SBTypeMemberFunction::GetType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBTypeMemberFunction, GetType);
+
+ SBType sb_type;
+ if (m_opaque_sp) {
+ sb_type.SetSP(lldb::TypeImplSP(new TypeImpl(m_opaque_sp->GetType())));
+ }
+ return LLDB_RECORD_RESULT(sb_type);
+}
+
+lldb::SBType SBTypeMemberFunction::GetReturnType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBTypeMemberFunction, GetReturnType);
+
+ SBType sb_type;
+ if (m_opaque_sp) {
+ sb_type.SetSP(lldb::TypeImplSP(new TypeImpl(m_opaque_sp->GetReturnType())));
+ }
+ return LLDB_RECORD_RESULT(sb_type);
+}
+
+uint32_t SBTypeMemberFunction::GetNumberOfArguments() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeMemberFunction,
+ GetNumberOfArguments);
+
+ if (m_opaque_sp)
+ return m_opaque_sp->GetNumArguments();
+ return 0;
+}
+
+lldb::SBType SBTypeMemberFunction::GetArgumentTypeAtIndex(uint32_t i) {
+ LLDB_RECORD_METHOD(lldb::SBType, SBTypeMemberFunction, GetArgumentTypeAtIndex,
+ (uint32_t), i);
+
+ SBType sb_type;
+ if (m_opaque_sp) {
+ sb_type.SetSP(
+ lldb::TypeImplSP(new TypeImpl(m_opaque_sp->GetArgumentAtIndex(i))));
+ }
+ return LLDB_RECORD_RESULT(sb_type);
+}
+
+lldb::MemberFunctionKind SBTypeMemberFunction::GetKind() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::MemberFunctionKind, SBTypeMemberFunction,
+ GetKind);
+
+ if (m_opaque_sp)
+ return m_opaque_sp->GetKind();
+ return lldb::eMemberFunctionKindUnknown;
+}
+
+bool SBTypeMemberFunction::GetDescription(
+ lldb::SBStream &description, lldb::DescriptionLevel description_level) {
+ LLDB_RECORD_METHOD(bool, SBTypeMemberFunction, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ description_level);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_sp)
+ return m_opaque_sp->GetDescription(strm);
+
+ return false;
+}
+
+void SBTypeMemberFunction::reset(TypeMemberFunctionImpl *type_member_impl) {
+ m_opaque_sp.reset(type_member_impl);
+}
+
+TypeMemberFunctionImpl &SBTypeMemberFunction::ref() {
+ if (!m_opaque_sp)
+ m_opaque_sp = std::make_shared<TypeMemberFunctionImpl>();
+ return *m_opaque_sp.get();
+}
+
+const TypeMemberFunctionImpl &SBTypeMemberFunction::ref() const {
+ return *m_opaque_sp.get();
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBType>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBType, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBType, (const lldb::SBType &));
+ LLDB_REGISTER_METHOD(bool, SBType, operator==,(lldb::SBType &));
+ LLDB_REGISTER_METHOD(bool, SBType, operator!=,(lldb::SBType &));
+ LLDB_REGISTER_METHOD(lldb::SBType &,
+ SBType, operator=,(const lldb::SBType &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBType, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBType, operator bool, ());
+ LLDB_REGISTER_METHOD(uint64_t, SBType, GetByteSize, ());
+ LLDB_REGISTER_METHOD(bool, SBType, IsPointerType, ());
+ LLDB_REGISTER_METHOD(bool, SBType, IsArrayType, ());
+ LLDB_REGISTER_METHOD(bool, SBType, IsVectorType, ());
+ LLDB_REGISTER_METHOD(bool, SBType, IsReferenceType, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetPointerType, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetPointeeType, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetReferenceType, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetTypedefedType, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetDereferencedType, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetArrayElementType, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetArrayType, (uint64_t));
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetVectorElementType, ());
+ LLDB_REGISTER_METHOD(bool, SBType, IsFunctionType, ());
+ LLDB_REGISTER_METHOD(bool, SBType, IsPolymorphicClass, ());
+ LLDB_REGISTER_METHOD(bool, SBType, IsTypedefType, ());
+ LLDB_REGISTER_METHOD(bool, SBType, IsAnonymousType, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetFunctionReturnType, ());
+ LLDB_REGISTER_METHOD(lldb::SBTypeList, SBType, GetFunctionArgumentTypes,
+ ());
+ LLDB_REGISTER_METHOD(uint32_t, SBType, GetNumberOfMemberFunctions, ());
+ LLDB_REGISTER_METHOD(lldb::SBTypeMemberFunction, SBType,
+ GetMemberFunctionAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetUnqualifiedType, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetCanonicalType, ());
+ LLDB_REGISTER_METHOD(lldb::BasicType, SBType, GetBasicType, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetBasicType, (lldb::BasicType));
+ LLDB_REGISTER_METHOD(uint32_t, SBType, GetNumberOfDirectBaseClasses, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBType, GetNumberOfVirtualBaseClasses, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBType, GetNumberOfFields, ());
+ LLDB_REGISTER_METHOD(bool, SBType, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+ LLDB_REGISTER_METHOD(lldb::SBTypeMember, SBType, GetDirectBaseClassAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBTypeMember, SBType, GetVirtualBaseClassAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBTypeEnumMemberList, SBType, GetEnumMembers,
+ ());
+ LLDB_REGISTER_METHOD(lldb::SBTypeMember, SBType, GetFieldAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBType, IsTypeComplete, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBType, GetTypeFlags, ());
+ LLDB_REGISTER_METHOD(const char *, SBType, GetName, ());
+ LLDB_REGISTER_METHOD(const char *, SBType, GetDisplayTypeName, ());
+ LLDB_REGISTER_METHOD(lldb::TypeClass, SBType, GetTypeClass, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBType, GetNumberOfTemplateArguments, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBType, GetTemplateArgumentType,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::TemplateArgumentKind, SBType,
+ GetTemplateArgumentKind, (uint32_t));
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeList, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeList, (const lldb::SBTypeList &));
+ LLDB_REGISTER_METHOD(bool, SBTypeList, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeList, operator bool, ());
+ LLDB_REGISTER_METHOD(lldb::SBTypeList &,
+ SBTypeList, operator=,(const lldb::SBTypeList &));
+ LLDB_REGISTER_METHOD(void, SBTypeList, Append, (lldb::SBType));
+ LLDB_REGISTER_METHOD(lldb::SBType, SBTypeList, GetTypeAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeList, GetSize, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeMember, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeMember, (const lldb::SBTypeMember &));
+ LLDB_REGISTER_METHOD(lldb::SBTypeMember &,
+ SBTypeMember, operator=,(const lldb::SBTypeMember &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeMember, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeMember, operator bool, ());
+ LLDB_REGISTER_METHOD(const char *, SBTypeMember, GetName, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBTypeMember, GetType, ());
+ LLDB_REGISTER_METHOD(uint64_t, SBTypeMember, GetOffsetInBytes, ());
+ LLDB_REGISTER_METHOD(uint64_t, SBTypeMember, GetOffsetInBits, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeMember, IsBitfield, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeMember, GetBitfieldSizeInBits, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeMember, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeMemberFunction, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeMemberFunction,
+ (const lldb::SBTypeMemberFunction &));
+ LLDB_REGISTER_METHOD(
+ lldb::SBTypeMemberFunction &,
+ SBTypeMemberFunction, operator=,(const lldb::SBTypeMemberFunction &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeMemberFunction, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeMemberFunction, operator bool, ());
+ LLDB_REGISTER_METHOD(const char *, SBTypeMemberFunction, GetName, ());
+ LLDB_REGISTER_METHOD(const char *, SBTypeMemberFunction, GetDemangledName,
+ ());
+ LLDB_REGISTER_METHOD(const char *, SBTypeMemberFunction, GetMangledName,
+ ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBTypeMemberFunction, GetType, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBTypeMemberFunction, GetReturnType, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeMemberFunction, GetNumberOfArguments,
+ ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBTypeMemberFunction,
+ GetArgumentTypeAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::MemberFunctionKind, SBTypeMemberFunction,
+ GetKind, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeMemberFunction, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBTypeCategory.cpp b/contrib/llvm-project/lldb/source/API/SBTypeCategory.cpp
new file mode 100644
index 000000000000..43d5a3ab140f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBTypeCategory.cpp
@@ -0,0 +1,737 @@
+//===-- SBTypeCategory.cpp ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBTypeCategory.h"
+#include "SBReproducerPrivate.h"
+
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBTypeFilter.h"
+#include "lldb/API/SBTypeFormat.h"
+#include "lldb/API/SBTypeNameSpecifier.h"
+#include "lldb/API/SBTypeSummary.h"
+#include "lldb/API/SBTypeSynthetic.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+typedef std::pair<lldb::TypeCategoryImplSP, user_id_t> ImplType;
+
+SBTypeCategory::SBTypeCategory() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeCategory);
+}
+
+SBTypeCategory::SBTypeCategory(const char *name) : m_opaque_sp() {
+ DataVisualization::Categories::GetCategory(ConstString(name), m_opaque_sp);
+}
+
+SBTypeCategory::SBTypeCategory(const lldb::SBTypeCategory &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeCategory, (const lldb::SBTypeCategory &), rhs);
+}
+
+SBTypeCategory::~SBTypeCategory() {}
+
+bool SBTypeCategory::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeCategory, IsValid);
+ return this->operator bool();
+}
+SBTypeCategory::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeCategory, operator bool);
+
+ return (m_opaque_sp.get() != nullptr);
+}
+
+bool SBTypeCategory::GetEnabled() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTypeCategory, GetEnabled);
+
+ if (!IsValid())
+ return false;
+ return m_opaque_sp->IsEnabled();
+}
+
+void SBTypeCategory::SetEnabled(bool enabled) {
+ LLDB_RECORD_METHOD(void, SBTypeCategory, SetEnabled, (bool), enabled);
+
+ if (!IsValid())
+ return;
+ if (enabled)
+ DataVisualization::Categories::Enable(m_opaque_sp);
+ else
+ DataVisualization::Categories::Disable(m_opaque_sp);
+}
+
+const char *SBTypeCategory::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTypeCategory, GetName);
+
+ if (!IsValid())
+ return nullptr;
+ return m_opaque_sp->GetName();
+}
+
+lldb::LanguageType SBTypeCategory::GetLanguageAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::LanguageType, SBTypeCategory, GetLanguageAtIndex,
+ (uint32_t), idx);
+
+ if (IsValid())
+ return m_opaque_sp->GetLanguageAtIndex(idx);
+ return lldb::eLanguageTypeUnknown;
+}
+
+uint32_t SBTypeCategory::GetNumLanguages() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeCategory, GetNumLanguages);
+
+ if (IsValid())
+ return m_opaque_sp->GetNumLanguages();
+ return 0;
+}
+
+void SBTypeCategory::AddLanguage(lldb::LanguageType language) {
+ LLDB_RECORD_METHOD(void, SBTypeCategory, AddLanguage, (lldb::LanguageType),
+ language);
+
+ if (IsValid())
+ m_opaque_sp->AddLanguage(language);
+}
+
+uint32_t SBTypeCategory::GetNumFormats() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeCategory, GetNumFormats);
+
+ if (!IsValid())
+ return 0;
+
+ return m_opaque_sp->GetTypeFormatsContainer()->GetCount() +
+ m_opaque_sp->GetRegexTypeFormatsContainer()->GetCount();
+}
+
+uint32_t SBTypeCategory::GetNumSummaries() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeCategory, GetNumSummaries);
+
+ if (!IsValid())
+ return 0;
+ return m_opaque_sp->GetTypeSummariesContainer()->GetCount() +
+ m_opaque_sp->GetRegexTypeSummariesContainer()->GetCount();
+}
+
+uint32_t SBTypeCategory::GetNumFilters() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeCategory, GetNumFilters);
+
+ if (!IsValid())
+ return 0;
+ return m_opaque_sp->GetTypeFiltersContainer()->GetCount() +
+ m_opaque_sp->GetRegexTypeFiltersContainer()->GetCount();
+}
+
+uint32_t SBTypeCategory::GetNumSynthetics() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeCategory, GetNumSynthetics);
+
+ if (!IsValid())
+ return 0;
+ return m_opaque_sp->GetTypeSyntheticsContainer()->GetCount() +
+ m_opaque_sp->GetRegexTypeSyntheticsContainer()->GetCount();
+}
+
+lldb::SBTypeNameSpecifier
+SBTypeCategory::GetTypeNameSpecifierForFilterAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBTypeNameSpecifier, SBTypeCategory,
+ GetTypeNameSpecifierForFilterAtIndex, (uint32_t), index);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeNameSpecifier());
+ return LLDB_RECORD_RESULT(SBTypeNameSpecifier(
+ m_opaque_sp->GetTypeNameSpecifierForFilterAtIndex(index)));
+}
+
+lldb::SBTypeNameSpecifier
+SBTypeCategory::GetTypeNameSpecifierForFormatAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBTypeNameSpecifier, SBTypeCategory,
+ GetTypeNameSpecifierForFormatAtIndex, (uint32_t), index);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeNameSpecifier());
+ return LLDB_RECORD_RESULT(SBTypeNameSpecifier(
+ m_opaque_sp->GetTypeNameSpecifierForFormatAtIndex(index)));
+}
+
+lldb::SBTypeNameSpecifier
+SBTypeCategory::GetTypeNameSpecifierForSummaryAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBTypeNameSpecifier, SBTypeCategory,
+ GetTypeNameSpecifierForSummaryAtIndex, (uint32_t), index);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeNameSpecifier());
+ return LLDB_RECORD_RESULT(SBTypeNameSpecifier(
+ m_opaque_sp->GetTypeNameSpecifierForSummaryAtIndex(index)));
+}
+
+lldb::SBTypeNameSpecifier
+SBTypeCategory::GetTypeNameSpecifierForSyntheticAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBTypeNameSpecifier, SBTypeCategory,
+ GetTypeNameSpecifierForSyntheticAtIndex, (uint32_t),
+ index);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeNameSpecifier());
+ return LLDB_RECORD_RESULT(SBTypeNameSpecifier(
+ m_opaque_sp->GetTypeNameSpecifierForSyntheticAtIndex(index)));
+}
+
+SBTypeFilter SBTypeCategory::GetFilterForType(SBTypeNameSpecifier spec) {
+ LLDB_RECORD_METHOD(lldb::SBTypeFilter, SBTypeCategory, GetFilterForType,
+ (lldb::SBTypeNameSpecifier), spec);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeFilter());
+
+ if (!spec.IsValid())
+ return LLDB_RECORD_RESULT(SBTypeFilter());
+
+ lldb::TypeFilterImplSP children_sp;
+
+ if (spec.IsRegex())
+ m_opaque_sp->GetRegexTypeFiltersContainer()->GetExact(
+ ConstString(spec.GetName()), children_sp);
+ else
+ m_opaque_sp->GetTypeFiltersContainer()->GetExact(
+ ConstString(spec.GetName()), children_sp);
+
+ if (!children_sp)
+ return LLDB_RECORD_RESULT(lldb::SBTypeFilter());
+
+ TypeFilterImplSP filter_sp =
+ std::static_pointer_cast<TypeFilterImpl>(children_sp);
+
+ return LLDB_RECORD_RESULT(lldb::SBTypeFilter(filter_sp));
+}
+SBTypeFormat SBTypeCategory::GetFormatForType(SBTypeNameSpecifier spec) {
+ LLDB_RECORD_METHOD(lldb::SBTypeFormat, SBTypeCategory, GetFormatForType,
+ (lldb::SBTypeNameSpecifier), spec);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeFormat());
+
+ if (!spec.IsValid())
+ return LLDB_RECORD_RESULT(SBTypeFormat());
+
+ lldb::TypeFormatImplSP format_sp;
+
+ if (spec.IsRegex())
+ m_opaque_sp->GetRegexTypeFormatsContainer()->GetExact(
+ ConstString(spec.GetName()), format_sp);
+ else
+ m_opaque_sp->GetTypeFormatsContainer()->GetExact(
+ ConstString(spec.GetName()), format_sp);
+
+ if (!format_sp)
+ return LLDB_RECORD_RESULT(lldb::SBTypeFormat());
+
+ return LLDB_RECORD_RESULT(lldb::SBTypeFormat(format_sp));
+}
+
+SBTypeSummary SBTypeCategory::GetSummaryForType(SBTypeNameSpecifier spec) {
+ LLDB_RECORD_METHOD(lldb::SBTypeSummary, SBTypeCategory, GetSummaryForType,
+ (lldb::SBTypeNameSpecifier), spec);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeSummary());
+
+ if (!spec.IsValid())
+ return LLDB_RECORD_RESULT(SBTypeSummary());
+
+ lldb::TypeSummaryImplSP summary_sp;
+
+ if (spec.IsRegex())
+ m_opaque_sp->GetRegexTypeSummariesContainer()->GetExact(
+ ConstString(spec.GetName()), summary_sp);
+ else
+ m_opaque_sp->GetTypeSummariesContainer()->GetExact(
+ ConstString(spec.GetName()), summary_sp);
+
+ if (!summary_sp)
+ return LLDB_RECORD_RESULT(lldb::SBTypeSummary());
+
+ return LLDB_RECORD_RESULT(lldb::SBTypeSummary(summary_sp));
+}
+
+SBTypeSynthetic SBTypeCategory::GetSyntheticForType(SBTypeNameSpecifier spec) {
+ LLDB_RECORD_METHOD(lldb::SBTypeSynthetic, SBTypeCategory, GetSyntheticForType,
+ (lldb::SBTypeNameSpecifier), spec);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeSynthetic());
+
+ if (!spec.IsValid())
+ return LLDB_RECORD_RESULT(SBTypeSynthetic());
+
+ lldb::SyntheticChildrenSP children_sp;
+
+ if (spec.IsRegex())
+ m_opaque_sp->GetRegexTypeSyntheticsContainer()->GetExact(
+ ConstString(spec.GetName()), children_sp);
+ else
+ m_opaque_sp->GetTypeSyntheticsContainer()->GetExact(
+ ConstString(spec.GetName()), children_sp);
+
+ if (!children_sp)
+ return LLDB_RECORD_RESULT(lldb::SBTypeSynthetic());
+
+ ScriptedSyntheticChildrenSP synth_sp =
+ std::static_pointer_cast<ScriptedSyntheticChildren>(children_sp);
+
+ return LLDB_RECORD_RESULT(lldb::SBTypeSynthetic(synth_sp));
+}
+
+SBTypeFilter SBTypeCategory::GetFilterAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBTypeFilter, SBTypeCategory, GetFilterAtIndex,
+ (uint32_t), index);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeFilter());
+ lldb::SyntheticChildrenSP children_sp =
+ m_opaque_sp->GetSyntheticAtIndex((index));
+
+ if (!children_sp.get())
+ return LLDB_RECORD_RESULT(lldb::SBTypeFilter());
+
+ TypeFilterImplSP filter_sp =
+ std::static_pointer_cast<TypeFilterImpl>(children_sp);
+
+ return LLDB_RECORD_RESULT(lldb::SBTypeFilter(filter_sp));
+}
+
+SBTypeFormat SBTypeCategory::GetFormatAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBTypeFormat, SBTypeCategory, GetFormatAtIndex,
+ (uint32_t), index);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeFormat());
+ return LLDB_RECORD_RESULT(
+ SBTypeFormat(m_opaque_sp->GetFormatAtIndex((index))));
+}
+
+SBTypeSummary SBTypeCategory::GetSummaryAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBTypeSummary, SBTypeCategory, GetSummaryAtIndex,
+ (uint32_t), index);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeSummary());
+ return LLDB_RECORD_RESULT(
+ SBTypeSummary(m_opaque_sp->GetSummaryAtIndex((index))));
+}
+
+SBTypeSynthetic SBTypeCategory::GetSyntheticAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBTypeSynthetic, SBTypeCategory, GetSyntheticAtIndex,
+ (uint32_t), index);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBTypeSynthetic());
+ lldb::SyntheticChildrenSP children_sp =
+ m_opaque_sp->GetSyntheticAtIndex((index));
+
+ if (!children_sp.get())
+ return LLDB_RECORD_RESULT(lldb::SBTypeSynthetic());
+
+ ScriptedSyntheticChildrenSP synth_sp =
+ std::static_pointer_cast<ScriptedSyntheticChildren>(children_sp);
+
+ return LLDB_RECORD_RESULT(lldb::SBTypeSynthetic(synth_sp));
+}
+
+bool SBTypeCategory::AddTypeFormat(SBTypeNameSpecifier type_name,
+ SBTypeFormat format) {
+ LLDB_RECORD_METHOD(bool, SBTypeCategory, AddTypeFormat,
+ (lldb::SBTypeNameSpecifier, lldb::SBTypeFormat), type_name,
+ format);
+
+ if (!IsValid())
+ return false;
+
+ if (!type_name.IsValid())
+ return false;
+
+ if (!format.IsValid())
+ return false;
+
+ if (type_name.IsRegex())
+ m_opaque_sp->GetRegexTypeFormatsContainer()->Add(
+ lldb::RegularExpressionSP(new RegularExpression(
+ llvm::StringRef::withNullAsEmpty(type_name.GetName()))),
+ format.GetSP());
+ else
+ m_opaque_sp->GetTypeFormatsContainer()->Add(
+ ConstString(type_name.GetName()), format.GetSP());
+
+ return true;
+}
+
+bool SBTypeCategory::DeleteTypeFormat(SBTypeNameSpecifier type_name) {
+ LLDB_RECORD_METHOD(bool, SBTypeCategory, DeleteTypeFormat,
+ (lldb::SBTypeNameSpecifier), type_name);
+
+ if (!IsValid())
+ return false;
+
+ if (!type_name.IsValid())
+ return false;
+
+ if (type_name.IsRegex())
+ return m_opaque_sp->GetRegexTypeFormatsContainer()->Delete(
+ ConstString(type_name.GetName()));
+ else
+ return m_opaque_sp->GetTypeFormatsContainer()->Delete(
+ ConstString(type_name.GetName()));
+}
+
+bool SBTypeCategory::AddTypeSummary(SBTypeNameSpecifier type_name,
+ SBTypeSummary summary) {
+ LLDB_RECORD_METHOD(bool, SBTypeCategory, AddTypeSummary,
+ (lldb::SBTypeNameSpecifier, lldb::SBTypeSummary),
+ type_name, summary);
+
+ if (!IsValid())
+ return false;
+
+ if (!type_name.IsValid())
+ return false;
+
+ if (!summary.IsValid())
+ return false;
+
+ // FIXME: we need to iterate over all the Debugger objects and have each of
+ // them contain a copy of the function
+ // since we currently have formatters live in a global space, while Python
+ // code lives in a specific Debugger-related environment this should
+ // eventually be fixed by deciding a final location in the LLDB object space
+ // for formatters
+ if (summary.IsFunctionCode()) {
+ const void *name_token =
+ (const void *)ConstString(type_name.GetName()).GetCString();
+ const char *script = summary.GetData();
+ StringList input;
+ input.SplitIntoLines(script, strlen(script));
+ uint32_t num_debuggers = lldb_private::Debugger::GetNumDebuggers();
+ bool need_set = true;
+ for (uint32_t j = 0; j < num_debuggers; j++) {
+ DebuggerSP debugger_sp = lldb_private::Debugger::GetDebuggerAtIndex(j);
+ if (debugger_sp) {
+ ScriptInterpreter *interpreter_ptr =
+ debugger_sp->GetScriptInterpreter();
+ if (interpreter_ptr) {
+ std::string output;
+ if (interpreter_ptr->GenerateTypeScriptFunction(input, output,
+ name_token) &&
+ !output.empty()) {
+ if (need_set) {
+ need_set = false;
+ summary.SetFunctionName(output.c_str());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (type_name.IsRegex())
+ m_opaque_sp->GetRegexTypeSummariesContainer()->Add(
+ lldb::RegularExpressionSP(new RegularExpression(
+ llvm::StringRef::withNullAsEmpty(type_name.GetName()))),
+ summary.GetSP());
+ else
+ m_opaque_sp->GetTypeSummariesContainer()->Add(
+ ConstString(type_name.GetName()), summary.GetSP());
+
+ return true;
+}
+
+bool SBTypeCategory::DeleteTypeSummary(SBTypeNameSpecifier type_name) {
+ LLDB_RECORD_METHOD(bool, SBTypeCategory, DeleteTypeSummary,
+ (lldb::SBTypeNameSpecifier), type_name);
+
+ if (!IsValid())
+ return false;
+
+ if (!type_name.IsValid())
+ return false;
+
+ if (type_name.IsRegex())
+ return m_opaque_sp->GetRegexTypeSummariesContainer()->Delete(
+ ConstString(type_name.GetName()));
+ else
+ return m_opaque_sp->GetTypeSummariesContainer()->Delete(
+ ConstString(type_name.GetName()));
+}
+
+bool SBTypeCategory::AddTypeFilter(SBTypeNameSpecifier type_name,
+ SBTypeFilter filter) {
+ LLDB_RECORD_METHOD(bool, SBTypeCategory, AddTypeFilter,
+ (lldb::SBTypeNameSpecifier, lldb::SBTypeFilter), type_name,
+ filter);
+
+ if (!IsValid())
+ return false;
+
+ if (!type_name.IsValid())
+ return false;
+
+ if (!filter.IsValid())
+ return false;
+
+ if (type_name.IsRegex())
+ m_opaque_sp->GetRegexTypeFiltersContainer()->Add(
+ lldb::RegularExpressionSP(new RegularExpression(
+ llvm::StringRef::withNullAsEmpty(type_name.GetName()))),
+ filter.GetSP());
+ else
+ m_opaque_sp->GetTypeFiltersContainer()->Add(
+ ConstString(type_name.GetName()), filter.GetSP());
+
+ return true;
+}
+
+bool SBTypeCategory::DeleteTypeFilter(SBTypeNameSpecifier type_name) {
+ LLDB_RECORD_METHOD(bool, SBTypeCategory, DeleteTypeFilter,
+ (lldb::SBTypeNameSpecifier), type_name);
+
+ if (!IsValid())
+ return false;
+
+ if (!type_name.IsValid())
+ return false;
+
+ if (type_name.IsRegex())
+ return m_opaque_sp->GetRegexTypeFiltersContainer()->Delete(
+ ConstString(type_name.GetName()));
+ else
+ return m_opaque_sp->GetTypeFiltersContainer()->Delete(
+ ConstString(type_name.GetName()));
+}
+
+bool SBTypeCategory::AddTypeSynthetic(SBTypeNameSpecifier type_name,
+ SBTypeSynthetic synth) {
+ LLDB_RECORD_METHOD(bool, SBTypeCategory, AddTypeSynthetic,
+ (lldb::SBTypeNameSpecifier, lldb::SBTypeSynthetic),
+ type_name, synth);
+
+ if (!IsValid())
+ return false;
+
+ if (!type_name.IsValid())
+ return false;
+
+ if (!synth.IsValid())
+ return false;
+
+ // FIXME: we need to iterate over all the Debugger objects and have each of
+ // them contain a copy of the function
+ // since we currently have formatters live in a global space, while Python
+ // code lives in a specific Debugger-related environment this should
+ // eventually be fixed by deciding a final location in the LLDB object space
+ // for formatters
+ if (synth.IsClassCode()) {
+ const void *name_token =
+ (const void *)ConstString(type_name.GetName()).GetCString();
+ const char *script = synth.GetData();
+ StringList input;
+ input.SplitIntoLines(script, strlen(script));
+ uint32_t num_debuggers = lldb_private::Debugger::GetNumDebuggers();
+ bool need_set = true;
+ for (uint32_t j = 0; j < num_debuggers; j++) {
+ DebuggerSP debugger_sp = lldb_private::Debugger::GetDebuggerAtIndex(j);
+ if (debugger_sp) {
+ ScriptInterpreter *interpreter_ptr =
+ debugger_sp->GetScriptInterpreter();
+ if (interpreter_ptr) {
+ std::string output;
+ if (interpreter_ptr->GenerateTypeSynthClass(input, output,
+ name_token) &&
+ !output.empty()) {
+ if (need_set) {
+ need_set = false;
+ synth.SetClassName(output.c_str());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (type_name.IsRegex())
+ m_opaque_sp->GetRegexTypeSyntheticsContainer()->Add(
+ lldb::RegularExpressionSP(new RegularExpression(
+ llvm::StringRef::withNullAsEmpty(type_name.GetName()))),
+ synth.GetSP());
+ else
+ m_opaque_sp->GetTypeSyntheticsContainer()->Add(
+ ConstString(type_name.GetName()), synth.GetSP());
+
+ return true;
+}
+
+bool SBTypeCategory::DeleteTypeSynthetic(SBTypeNameSpecifier type_name) {
+ LLDB_RECORD_METHOD(bool, SBTypeCategory, DeleteTypeSynthetic,
+ (lldb::SBTypeNameSpecifier), type_name);
+
+ if (!IsValid())
+ return false;
+
+ if (!type_name.IsValid())
+ return false;
+
+ if (type_name.IsRegex())
+ return m_opaque_sp->GetRegexTypeSyntheticsContainer()->Delete(
+ ConstString(type_name.GetName()));
+ else
+ return m_opaque_sp->GetTypeSyntheticsContainer()->Delete(
+ ConstString(type_name.GetName()));
+}
+
+bool SBTypeCategory::GetDescription(lldb::SBStream &description,
+ lldb::DescriptionLevel description_level) {
+ LLDB_RECORD_METHOD(bool, SBTypeCategory, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ description_level);
+
+ if (!IsValid())
+ return false;
+ description.Printf("Category name: %s\n", GetName());
+ return true;
+}
+
+lldb::SBTypeCategory &SBTypeCategory::
+operator=(const lldb::SBTypeCategory &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBTypeCategory &,
+ SBTypeCategory, operator=,(const lldb::SBTypeCategory &),
+ rhs);
+
+ if (this != &rhs) {
+ m_opaque_sp = rhs.m_opaque_sp;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBTypeCategory::operator==(lldb::SBTypeCategory &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeCategory, operator==,(lldb::SBTypeCategory &),
+ rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+
+ return m_opaque_sp.get() == rhs.m_opaque_sp.get();
+}
+
+bool SBTypeCategory::operator!=(lldb::SBTypeCategory &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeCategory, operator!=,(lldb::SBTypeCategory &),
+ rhs);
+
+ if (!IsValid())
+ return rhs.IsValid();
+
+ return m_opaque_sp.get() != rhs.m_opaque_sp.get();
+}
+
+lldb::TypeCategoryImplSP SBTypeCategory::GetSP() {
+ if (!IsValid())
+ return lldb::TypeCategoryImplSP();
+ return m_opaque_sp;
+}
+
+void SBTypeCategory::SetSP(
+ const lldb::TypeCategoryImplSP &typecategory_impl_sp) {
+ m_opaque_sp = typecategory_impl_sp;
+}
+
+SBTypeCategory::SBTypeCategory(
+ const lldb::TypeCategoryImplSP &typecategory_impl_sp)
+ : m_opaque_sp(typecategory_impl_sp) {}
+
+bool SBTypeCategory::IsDefaultCategory() {
+ if (!IsValid())
+ return false;
+
+ return (strcmp(m_opaque_sp->GetName(), "default") == 0);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBTypeCategory>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeCategory, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeCategory, (const lldb::SBTypeCategory &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeCategory, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeCategory, operator bool, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeCategory, GetEnabled, ());
+ LLDB_REGISTER_METHOD(void, SBTypeCategory, SetEnabled, (bool));
+ LLDB_REGISTER_METHOD(const char *, SBTypeCategory, GetName, ());
+ LLDB_REGISTER_METHOD(lldb::LanguageType, SBTypeCategory, GetLanguageAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeCategory, GetNumLanguages, ());
+ LLDB_REGISTER_METHOD(void, SBTypeCategory, AddLanguage,
+ (lldb::LanguageType));
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeCategory, GetNumFormats, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeCategory, GetNumSummaries, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeCategory, GetNumFilters, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeCategory, GetNumSynthetics, ());
+ LLDB_REGISTER_METHOD(lldb::SBTypeNameSpecifier, SBTypeCategory,
+ GetTypeNameSpecifierForSyntheticAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBTypeSummary, SBTypeCategory, GetSummaryForType,
+ (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(lldb::SBTypeSynthetic, SBTypeCategory,
+ GetSyntheticForType, (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(lldb::SBTypeFilter, SBTypeCategory, GetFilterAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBTypeSummary, SBTypeCategory, GetSummaryAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBTypeSynthetic, SBTypeCategory,
+ GetSyntheticAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBTypeCategory, AddTypeSummary,
+ (lldb::SBTypeNameSpecifier, lldb::SBTypeSummary));
+ LLDB_REGISTER_METHOD(bool, SBTypeCategory, AddTypeSynthetic,
+ (lldb::SBTypeNameSpecifier, lldb::SBTypeSynthetic));
+ LLDB_REGISTER_METHOD(bool, SBTypeCategory, DeleteTypeSynthetic,
+ (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(lldb::SBTypeNameSpecifier, SBTypeCategory,
+ GetTypeNameSpecifierForFilterAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBTypeNameSpecifier, SBTypeCategory,
+ GetTypeNameSpecifierForFormatAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBTypeNameSpecifier, SBTypeCategory,
+ GetTypeNameSpecifierForSummaryAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBTypeFilter, SBTypeCategory, GetFilterForType,
+ (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(lldb::SBTypeFormat, SBTypeCategory, GetFormatForType,
+ (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(lldb::SBTypeFormat, SBTypeCategory, GetFormatAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBTypeCategory, AddTypeFormat,
+ (lldb::SBTypeNameSpecifier, lldb::SBTypeFormat));
+ LLDB_REGISTER_METHOD(bool, SBTypeCategory, DeleteTypeFormat,
+ (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(bool, SBTypeCategory, DeleteTypeSummary,
+ (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(bool, SBTypeCategory, AddTypeFilter,
+ (lldb::SBTypeNameSpecifier, lldb::SBTypeFilter));
+ LLDB_REGISTER_METHOD(bool, SBTypeCategory, DeleteTypeFilter,
+ (lldb::SBTypeNameSpecifier));
+ LLDB_REGISTER_METHOD(bool, SBTypeCategory, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+ LLDB_REGISTER_METHOD(
+ lldb::SBTypeCategory &,
+ SBTypeCategory, operator=,(const lldb::SBTypeCategory &));
+ LLDB_REGISTER_METHOD(bool,
+ SBTypeCategory, operator==,(lldb::SBTypeCategory &));
+ LLDB_REGISTER_METHOD(bool,
+ SBTypeCategory, operator!=,(lldb::SBTypeCategory &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBTypeEnumMember.cpp b/contrib/llvm-project/lldb/source/API/SBTypeEnumMember.cpp
new file mode 100644
index 000000000000..bd0755a140c3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBTypeEnumMember.cpp
@@ -0,0 +1,235 @@
+//===-- SBTypeEnumMember.cpp ---------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBTypeEnumMember.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBDefines.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBType.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Utility/Stream.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBTypeEnumMember::SBTypeEnumMember() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeEnumMember);
+}
+
+SBTypeEnumMember::~SBTypeEnumMember() {}
+
+SBTypeEnumMember::SBTypeEnumMember(
+ const lldb::TypeEnumMemberImplSP &enum_member_sp)
+ : m_opaque_sp(enum_member_sp) {}
+
+SBTypeEnumMember::SBTypeEnumMember(const SBTypeEnumMember &rhs)
+ : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeEnumMember, (const lldb::SBTypeEnumMember &),
+ rhs);
+
+ m_opaque_sp = clone(rhs.m_opaque_sp);
+}
+
+SBTypeEnumMember &SBTypeEnumMember::operator=(const SBTypeEnumMember &rhs) {
+ LLDB_RECORD_METHOD(
+ SBTypeEnumMember &,
+ SBTypeEnumMember, operator=,(const lldb::SBTypeEnumMember &), rhs);
+
+ if (this != &rhs)
+ m_opaque_sp = clone(rhs.m_opaque_sp);
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBTypeEnumMember::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeEnumMember, IsValid);
+ return this->operator bool();
+}
+SBTypeEnumMember::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeEnumMember, operator bool);
+
+ return m_opaque_sp.get();
+}
+
+const char *SBTypeEnumMember::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTypeEnumMember, GetName);
+
+ if (m_opaque_sp.get())
+ return m_opaque_sp->GetName().GetCString();
+ return nullptr;
+}
+
+int64_t SBTypeEnumMember::GetValueAsSigned() {
+ LLDB_RECORD_METHOD_NO_ARGS(int64_t, SBTypeEnumMember, GetValueAsSigned);
+
+ if (m_opaque_sp.get())
+ return m_opaque_sp->GetValueAsSigned();
+ return 0;
+}
+
+uint64_t SBTypeEnumMember::GetValueAsUnsigned() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint64_t, SBTypeEnumMember, GetValueAsUnsigned);
+
+ if (m_opaque_sp.get())
+ return m_opaque_sp->GetValueAsUnsigned();
+ return 0;
+}
+
+SBType SBTypeEnumMember::GetType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBTypeEnumMember, GetType);
+
+ SBType sb_type;
+ if (m_opaque_sp.get()) {
+ sb_type.SetSP(m_opaque_sp->GetIntegerType());
+ }
+ return LLDB_RECORD_RESULT(sb_type);
+}
+
+void SBTypeEnumMember::reset(TypeEnumMemberImpl *type_member_impl) {
+ m_opaque_sp.reset(type_member_impl);
+}
+
+TypeEnumMemberImpl &SBTypeEnumMember::ref() {
+ if (m_opaque_sp.get() == nullptr)
+ m_opaque_sp = std::make_shared<TypeEnumMemberImpl>();
+ return *m_opaque_sp.get();
+}
+
+const TypeEnumMemberImpl &SBTypeEnumMember::ref() const {
+ return *m_opaque_sp.get();
+}
+
+SBTypeEnumMemberList::SBTypeEnumMemberList()
+ : m_opaque_up(new TypeEnumMemberListImpl()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeEnumMemberList);
+}
+
+SBTypeEnumMemberList::SBTypeEnumMemberList(const SBTypeEnumMemberList &rhs)
+ : m_opaque_up(new TypeEnumMemberListImpl()) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeEnumMemberList,
+ (const lldb::SBTypeEnumMemberList &), rhs);
+
+ for (uint32_t i = 0,
+ rhs_size = const_cast<SBTypeEnumMemberList &>(rhs).GetSize();
+ i < rhs_size; i++)
+ Append(const_cast<SBTypeEnumMemberList &>(rhs).GetTypeEnumMemberAtIndex(i));
+}
+
+bool SBTypeEnumMemberList::IsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTypeEnumMemberList, IsValid);
+ return this->operator bool();
+}
+SBTypeEnumMemberList::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeEnumMemberList, operator bool);
+
+ return (m_opaque_up != nullptr);
+}
+
+SBTypeEnumMemberList &SBTypeEnumMemberList::
+operator=(const SBTypeEnumMemberList &rhs) {
+ LLDB_RECORD_METHOD(
+ lldb::SBTypeEnumMemberList &,
+ SBTypeEnumMemberList, operator=,(const lldb::SBTypeEnumMemberList &),
+ rhs);
+
+ if (this != &rhs) {
+ m_opaque_up.reset(new TypeEnumMemberListImpl());
+ for (uint32_t i = 0,
+ rhs_size = const_cast<SBTypeEnumMemberList &>(rhs).GetSize();
+ i < rhs_size; i++)
+ Append(
+ const_cast<SBTypeEnumMemberList &>(rhs).GetTypeEnumMemberAtIndex(i));
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+void SBTypeEnumMemberList::Append(SBTypeEnumMember enum_member) {
+ LLDB_RECORD_METHOD(void, SBTypeEnumMemberList, Append,
+ (lldb::SBTypeEnumMember), enum_member);
+
+ if (enum_member.IsValid())
+ m_opaque_up->Append(enum_member.m_opaque_sp);
+}
+
+SBTypeEnumMember
+SBTypeEnumMemberList::GetTypeEnumMemberAtIndex(uint32_t index) {
+ LLDB_RECORD_METHOD(lldb::SBTypeEnumMember, SBTypeEnumMemberList,
+ GetTypeEnumMemberAtIndex, (uint32_t), index);
+
+ if (m_opaque_up)
+ return LLDB_RECORD_RESULT(
+ SBTypeEnumMember(m_opaque_up->GetTypeEnumMemberAtIndex(index)));
+ return LLDB_RECORD_RESULT(SBTypeEnumMember());
+}
+
+uint32_t SBTypeEnumMemberList::GetSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeEnumMemberList, GetSize);
+
+ return m_opaque_up->GetSize();
+}
+
+SBTypeEnumMemberList::~SBTypeEnumMemberList() {}
+
+bool SBTypeEnumMember::GetDescription(
+ lldb::SBStream &description, lldb::DescriptionLevel description_level) {
+ LLDB_RECORD_METHOD(bool, SBTypeEnumMember, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ description_level);
+
+ Stream &strm = description.ref();
+
+ if (m_opaque_sp.get()) {
+ if (m_opaque_sp->GetIntegerType()->GetDescription(strm,
+ description_level)) {
+ strm.Printf(" %s", m_opaque_sp->GetName().GetCString());
+ }
+ } else {
+ strm.PutCString("No value");
+ }
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBTypeEnumMember>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeEnumMember, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeEnumMember,
+ (const lldb::SBTypeEnumMember &));
+ LLDB_REGISTER_METHOD(
+ lldb::SBTypeEnumMember &,
+ SBTypeEnumMember, operator=,(const lldb::SBTypeEnumMember &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeEnumMember, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeEnumMember, operator bool, ());
+ LLDB_REGISTER_METHOD(const char *, SBTypeEnumMember, GetName, ());
+ LLDB_REGISTER_METHOD(int64_t, SBTypeEnumMember, GetValueAsSigned, ());
+ LLDB_REGISTER_METHOD(uint64_t, SBTypeEnumMember, GetValueAsUnsigned, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBTypeEnumMember, GetType, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeEnumMemberList, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeEnumMemberList,
+ (const lldb::SBTypeEnumMemberList &));
+ LLDB_REGISTER_METHOD(bool, SBTypeEnumMemberList, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeEnumMemberList, operator bool, ());
+ LLDB_REGISTER_METHOD(
+ lldb::SBTypeEnumMemberList &,
+ SBTypeEnumMemberList, operator=,(const lldb::SBTypeEnumMemberList &));
+ LLDB_REGISTER_METHOD(void, SBTypeEnumMemberList, Append,
+ (lldb::SBTypeEnumMember));
+ LLDB_REGISTER_METHOD(lldb::SBTypeEnumMember, SBTypeEnumMemberList,
+ GetTypeEnumMemberAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeEnumMemberList, GetSize, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeEnumMember, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBTypeFilter.cpp b/contrib/llvm-project/lldb/source/API/SBTypeFilter.cpp
new file mode 100644
index 000000000000..d40301b4c153
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBTypeFilter.cpp
@@ -0,0 +1,226 @@
+//===-- SBTypeFilter.cpp ------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBTypeFilter.h"
+#include "SBReproducerPrivate.h"
+
+#include "lldb/API/SBStream.h"
+
+#include "lldb/DataFormatters/DataVisualization.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBTypeFilter::SBTypeFilter() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeFilter);
+}
+
+SBTypeFilter::SBTypeFilter(uint32_t options)
+ : m_opaque_sp(TypeFilterImplSP(new TypeFilterImpl(options))) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeFilter, (uint32_t), options);
+}
+
+SBTypeFilter::SBTypeFilter(const lldb::SBTypeFilter &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeFilter, (const lldb::SBTypeFilter &), rhs);
+}
+
+SBTypeFilter::~SBTypeFilter() {}
+
+bool SBTypeFilter::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeFilter, IsValid);
+ return this->operator bool();
+}
+SBTypeFilter::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeFilter, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+uint32_t SBTypeFilter::GetOptions() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeFilter, GetOptions);
+
+ if (IsValid())
+ return m_opaque_sp->GetOptions();
+ return 0;
+}
+
+void SBTypeFilter::SetOptions(uint32_t value) {
+ LLDB_RECORD_METHOD(void, SBTypeFilter, SetOptions, (uint32_t), value);
+
+ if (CopyOnWrite_Impl())
+ m_opaque_sp->SetOptions(value);
+}
+
+bool SBTypeFilter::GetDescription(lldb::SBStream &description,
+ lldb::DescriptionLevel description_level) {
+ LLDB_RECORD_METHOD(bool, SBTypeFilter, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ description_level);
+
+ if (!IsValid())
+ return false;
+ else {
+ description.Printf("%s\n", m_opaque_sp->GetDescription().c_str());
+ return true;
+ }
+}
+
+void SBTypeFilter::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBTypeFilter, Clear);
+
+ if (CopyOnWrite_Impl())
+ m_opaque_sp->Clear();
+}
+
+uint32_t SBTypeFilter::GetNumberOfExpressionPaths() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeFilter,
+ GetNumberOfExpressionPaths);
+
+ if (IsValid())
+ return m_opaque_sp->GetCount();
+ return 0;
+}
+
+const char *SBTypeFilter::GetExpressionPathAtIndex(uint32_t i) {
+ LLDB_RECORD_METHOD(const char *, SBTypeFilter, GetExpressionPathAtIndex,
+ (uint32_t), i);
+
+ if (IsValid()) {
+ const char *item = m_opaque_sp->GetExpressionPathAtIndex(i);
+ if (item && *item == '.')
+ item++;
+ return item;
+ }
+ return nullptr;
+}
+
+bool SBTypeFilter::ReplaceExpressionPathAtIndex(uint32_t i, const char *item) {
+ LLDB_RECORD_METHOD(bool, SBTypeFilter, ReplaceExpressionPathAtIndex,
+ (uint32_t, const char *), i, item);
+
+ if (CopyOnWrite_Impl())
+ return m_opaque_sp->SetExpressionPathAtIndex(i, item);
+ else
+ return false;
+}
+
+void SBTypeFilter::AppendExpressionPath(const char *item) {
+ LLDB_RECORD_METHOD(void, SBTypeFilter, AppendExpressionPath, (const char *),
+ item);
+
+ if (CopyOnWrite_Impl())
+ m_opaque_sp->AddExpressionPath(item);
+}
+
+lldb::SBTypeFilter &SBTypeFilter::operator=(const lldb::SBTypeFilter &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBTypeFilter &,
+ SBTypeFilter, operator=,(const lldb::SBTypeFilter &), rhs);
+
+ if (this != &rhs) {
+ m_opaque_sp = rhs.m_opaque_sp;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBTypeFilter::operator==(lldb::SBTypeFilter &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeFilter, operator==,(lldb::SBTypeFilter &),
+ rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+
+ return m_opaque_sp == rhs.m_opaque_sp;
+}
+
+bool SBTypeFilter::IsEqualTo(lldb::SBTypeFilter &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeFilter, IsEqualTo, (lldb::SBTypeFilter &),
+ rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+
+ if (GetNumberOfExpressionPaths() != rhs.GetNumberOfExpressionPaths())
+ return false;
+
+ for (uint32_t j = 0; j < GetNumberOfExpressionPaths(); j++)
+ if (strcmp(GetExpressionPathAtIndex(j), rhs.GetExpressionPathAtIndex(j)) !=
+ 0)
+ return false;
+
+ return GetOptions() == rhs.GetOptions();
+}
+
+bool SBTypeFilter::operator!=(lldb::SBTypeFilter &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeFilter, operator!=,(lldb::SBTypeFilter &),
+ rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+
+ return m_opaque_sp != rhs.m_opaque_sp;
+}
+
+lldb::TypeFilterImplSP SBTypeFilter::GetSP() { return m_opaque_sp; }
+
+void SBTypeFilter::SetSP(const lldb::TypeFilterImplSP &typefilter_impl_sp) {
+ m_opaque_sp = typefilter_impl_sp;
+}
+
+SBTypeFilter::SBTypeFilter(const lldb::TypeFilterImplSP &typefilter_impl_sp)
+ : m_opaque_sp(typefilter_impl_sp) {}
+
+bool SBTypeFilter::CopyOnWrite_Impl() {
+ if (!IsValid())
+ return false;
+ if (m_opaque_sp.unique())
+ return true;
+
+ TypeFilterImplSP new_sp(new TypeFilterImpl(GetOptions()));
+
+ for (uint32_t j = 0; j < GetNumberOfExpressionPaths(); j++)
+ new_sp->AddExpressionPath(GetExpressionPathAtIndex(j));
+
+ SetSP(new_sp);
+
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBTypeFilter>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeFilter, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeFilter, (uint32_t));
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeFilter, (const lldb::SBTypeFilter &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeFilter, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeFilter, operator bool, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeFilter, GetOptions, ());
+ LLDB_REGISTER_METHOD(void, SBTypeFilter, SetOptions, (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBTypeFilter, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+ LLDB_REGISTER_METHOD(void, SBTypeFilter, Clear, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeFilter, GetNumberOfExpressionPaths,
+ ());
+ LLDB_REGISTER_METHOD(const char *, SBTypeFilter, GetExpressionPathAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBTypeFilter, ReplaceExpressionPathAtIndex,
+ (uint32_t, const char *));
+ LLDB_REGISTER_METHOD(void, SBTypeFilter, AppendExpressionPath,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBTypeFilter &,
+ SBTypeFilter, operator=,(const lldb::SBTypeFilter &));
+ LLDB_REGISTER_METHOD(bool, SBTypeFilter, operator==,(lldb::SBTypeFilter &));
+ LLDB_REGISTER_METHOD(bool, SBTypeFilter, IsEqualTo, (lldb::SBTypeFilter &));
+ LLDB_REGISTER_METHOD(bool, SBTypeFilter, operator!=,(lldb::SBTypeFilter &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBTypeFormat.cpp b/contrib/llvm-project/lldb/source/API/SBTypeFormat.cpp
new file mode 100644
index 000000000000..6024631e7054
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBTypeFormat.cpp
@@ -0,0 +1,223 @@
+//===-- SBTypeFormat.cpp ------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBTypeFormat.h"
+#include "SBReproducerPrivate.h"
+
+#include "lldb/API/SBStream.h"
+
+#include "lldb/DataFormatters/DataVisualization.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBTypeFormat::SBTypeFormat() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeFormat);
+}
+
+SBTypeFormat::SBTypeFormat(lldb::Format format, uint32_t options)
+ : m_opaque_sp(
+ TypeFormatImplSP(new TypeFormatImpl_Format(format, options))) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeFormat, (lldb::Format, uint32_t), format,
+ options);
+}
+
+SBTypeFormat::SBTypeFormat(const char *type, uint32_t options)
+ : m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl_EnumType(
+ ConstString(type ? type : ""), options))) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeFormat, (const char *, uint32_t), type,
+ options);
+}
+
+SBTypeFormat::SBTypeFormat(const lldb::SBTypeFormat &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeFormat, (const lldb::SBTypeFormat &), rhs);
+}
+
+SBTypeFormat::~SBTypeFormat() {}
+
+bool SBTypeFormat::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeFormat, IsValid);
+ return this->operator bool();
+}
+SBTypeFormat::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeFormat, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+lldb::Format SBTypeFormat::GetFormat() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::Format, SBTypeFormat, GetFormat);
+
+ if (IsValid() && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat)
+ return ((TypeFormatImpl_Format *)m_opaque_sp.get())->GetFormat();
+ return lldb::eFormatInvalid;
+}
+
+const char *SBTypeFormat::GetTypeName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTypeFormat, GetTypeName);
+
+ if (IsValid() && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeEnum)
+ return ((TypeFormatImpl_EnumType *)m_opaque_sp.get())
+ ->GetTypeName()
+ .AsCString("");
+ return "";
+}
+
+uint32_t SBTypeFormat::GetOptions() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeFormat, GetOptions);
+
+ if (IsValid())
+ return m_opaque_sp->GetOptions();
+ return 0;
+}
+
+void SBTypeFormat::SetFormat(lldb::Format fmt) {
+ LLDB_RECORD_METHOD(void, SBTypeFormat, SetFormat, (lldb::Format), fmt);
+
+ if (CopyOnWrite_Impl(Type::eTypeFormat))
+ ((TypeFormatImpl_Format *)m_opaque_sp.get())->SetFormat(fmt);
+}
+
+void SBTypeFormat::SetTypeName(const char *type) {
+ LLDB_RECORD_METHOD(void, SBTypeFormat, SetTypeName, (const char *), type);
+
+ if (CopyOnWrite_Impl(Type::eTypeEnum))
+ ((TypeFormatImpl_EnumType *)m_opaque_sp.get())
+ ->SetTypeName(ConstString(type ? type : ""));
+}
+
+void SBTypeFormat::SetOptions(uint32_t value) {
+ LLDB_RECORD_METHOD(void, SBTypeFormat, SetOptions, (uint32_t), value);
+
+ if (CopyOnWrite_Impl(Type::eTypeKeepSame))
+ m_opaque_sp->SetOptions(value);
+}
+
+bool SBTypeFormat::GetDescription(lldb::SBStream &description,
+ lldb::DescriptionLevel description_level) {
+ LLDB_RECORD_METHOD(bool, SBTypeFormat, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ description_level);
+
+ if (!IsValid())
+ return false;
+ else {
+ description.Printf("%s\n", m_opaque_sp->GetDescription().c_str());
+ return true;
+ }
+}
+
+lldb::SBTypeFormat &SBTypeFormat::operator=(const lldb::SBTypeFormat &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBTypeFormat &,
+ SBTypeFormat, operator=,(const lldb::SBTypeFormat &), rhs);
+
+ if (this != &rhs) {
+ m_opaque_sp = rhs.m_opaque_sp;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBTypeFormat::operator==(lldb::SBTypeFormat &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeFormat, operator==,(lldb::SBTypeFormat &),
+ rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+ return m_opaque_sp == rhs.m_opaque_sp;
+}
+
+bool SBTypeFormat::IsEqualTo(lldb::SBTypeFormat &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeFormat, IsEqualTo, (lldb::SBTypeFormat &),
+ rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+
+ if (GetFormat() == rhs.GetFormat())
+ return GetOptions() == rhs.GetOptions();
+ else
+ return false;
+}
+
+bool SBTypeFormat::operator!=(lldb::SBTypeFormat &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeFormat, operator!=,(lldb::SBTypeFormat &),
+ rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+ return m_opaque_sp != rhs.m_opaque_sp;
+}
+
+lldb::TypeFormatImplSP SBTypeFormat::GetSP() { return m_opaque_sp; }
+
+void SBTypeFormat::SetSP(const lldb::TypeFormatImplSP &typeformat_impl_sp) {
+ m_opaque_sp = typeformat_impl_sp;
+}
+
+SBTypeFormat::SBTypeFormat(const lldb::TypeFormatImplSP &typeformat_impl_sp)
+ : m_opaque_sp(typeformat_impl_sp) {}
+
+bool SBTypeFormat::CopyOnWrite_Impl(Type type) {
+ if (!IsValid())
+ return false;
+
+ if (m_opaque_sp.unique() &&
+ ((type == Type::eTypeKeepSame) ||
+ (type == Type::eTypeFormat &&
+ m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat) ||
+ (type == Type::eTypeEnum &&
+ m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeEnum)))
+ return true;
+
+ if (type == Type::eTypeKeepSame) {
+ if (m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat)
+ type = Type::eTypeFormat;
+ else
+ type = Type::eTypeEnum;
+ }
+
+ if (type == Type::eTypeFormat)
+ SetSP(
+ TypeFormatImplSP(new TypeFormatImpl_Format(GetFormat(), GetOptions())));
+ else
+ SetSP(TypeFormatImplSP(
+ new TypeFormatImpl_EnumType(ConstString(GetTypeName()), GetOptions())));
+
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBTypeFormat>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeFormat, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeFormat, (lldb::Format, uint32_t));
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeFormat, (const char *, uint32_t));
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeFormat, (const lldb::SBTypeFormat &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeFormat, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeFormat, operator bool, ());
+ LLDB_REGISTER_METHOD(lldb::Format, SBTypeFormat, GetFormat, ());
+ LLDB_REGISTER_METHOD(const char *, SBTypeFormat, GetTypeName, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeFormat, GetOptions, ());
+ LLDB_REGISTER_METHOD(void, SBTypeFormat, SetFormat, (lldb::Format));
+ LLDB_REGISTER_METHOD(void, SBTypeFormat, SetTypeName, (const char *));
+ LLDB_REGISTER_METHOD(void, SBTypeFormat, SetOptions, (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBTypeFormat, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+ LLDB_REGISTER_METHOD(lldb::SBTypeFormat &,
+ SBTypeFormat, operator=,(const lldb::SBTypeFormat &));
+ LLDB_REGISTER_METHOD(bool, SBTypeFormat, operator==,(lldb::SBTypeFormat &));
+ LLDB_REGISTER_METHOD(bool, SBTypeFormat, IsEqualTo, (lldb::SBTypeFormat &));
+ LLDB_REGISTER_METHOD(bool, SBTypeFormat, operator!=,(lldb::SBTypeFormat &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBTypeNameSpecifier.cpp b/contrib/llvm-project/lldb/source/API/SBTypeNameSpecifier.cpp
new file mode 100644
index 000000000000..895f69775659
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBTypeNameSpecifier.cpp
@@ -0,0 +1,189 @@
+//===-- SBTypeNameSpecifier.cpp ------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBTypeNameSpecifier.h"
+#include "SBReproducerPrivate.h"
+
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBType.h"
+
+#include "lldb/DataFormatters/DataVisualization.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBTypeNameSpecifier::SBTypeNameSpecifier() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeNameSpecifier);
+}
+
+SBTypeNameSpecifier::SBTypeNameSpecifier(const char *name, bool is_regex)
+ : m_opaque_sp(new TypeNameSpecifierImpl(name, is_regex)) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeNameSpecifier, (const char *, bool), name,
+ is_regex);
+
+ if (name == nullptr || (*name) == 0)
+ m_opaque_sp.reset();
+}
+
+SBTypeNameSpecifier::SBTypeNameSpecifier(SBType type) : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeNameSpecifier, (lldb::SBType), type);
+
+ if (type.IsValid())
+ m_opaque_sp = TypeNameSpecifierImplSP(
+ new TypeNameSpecifierImpl(type.m_opaque_sp->GetCompilerType(true)));
+}
+
+SBTypeNameSpecifier::SBTypeNameSpecifier(const lldb::SBTypeNameSpecifier &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeNameSpecifier,
+ (const lldb::SBTypeNameSpecifier &), rhs);
+}
+
+SBTypeNameSpecifier::~SBTypeNameSpecifier() {}
+
+bool SBTypeNameSpecifier::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeNameSpecifier, IsValid);
+ return this->operator bool();
+}
+SBTypeNameSpecifier::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeNameSpecifier, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+const char *SBTypeNameSpecifier::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTypeNameSpecifier, GetName);
+
+ if (!IsValid())
+ return nullptr;
+
+ return m_opaque_sp->GetName();
+}
+
+SBType SBTypeNameSpecifier::GetType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBTypeNameSpecifier, GetType);
+
+ if (!IsValid())
+ return LLDB_RECORD_RESULT(SBType());
+ lldb_private::CompilerType c_type = m_opaque_sp->GetCompilerType();
+ if (c_type.IsValid())
+ return LLDB_RECORD_RESULT(SBType(c_type));
+ return LLDB_RECORD_RESULT(SBType());
+}
+
+bool SBTypeNameSpecifier::IsRegex() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTypeNameSpecifier, IsRegex);
+
+ if (!IsValid())
+ return false;
+
+ return m_opaque_sp->IsRegex();
+}
+
+bool SBTypeNameSpecifier::GetDescription(
+ lldb::SBStream &description, lldb::DescriptionLevel description_level) {
+ LLDB_RECORD_METHOD(bool, SBTypeNameSpecifier, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ description_level);
+
+ if (!IsValid())
+ return false;
+ description.Printf("SBTypeNameSpecifier(%s,%s)", GetName(),
+ IsRegex() ? "regex" : "plain");
+ return true;
+}
+
+lldb::SBTypeNameSpecifier &SBTypeNameSpecifier::
+operator=(const lldb::SBTypeNameSpecifier &rhs) {
+ LLDB_RECORD_METHOD(
+ lldb::SBTypeNameSpecifier &,
+ SBTypeNameSpecifier, operator=,(const lldb::SBTypeNameSpecifier &), rhs);
+
+ if (this != &rhs) {
+ m_opaque_sp = rhs.m_opaque_sp;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBTypeNameSpecifier::operator==(lldb::SBTypeNameSpecifier &rhs) {
+ LLDB_RECORD_METHOD(
+ bool, SBTypeNameSpecifier, operator==,(lldb::SBTypeNameSpecifier &), rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+ return m_opaque_sp == rhs.m_opaque_sp;
+}
+
+bool SBTypeNameSpecifier::IsEqualTo(lldb::SBTypeNameSpecifier &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeNameSpecifier, IsEqualTo,
+ (lldb::SBTypeNameSpecifier &), rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+
+ if (IsRegex() != rhs.IsRegex())
+ return false;
+ if (GetName() == nullptr || rhs.GetName() == nullptr)
+ return false;
+
+ return (strcmp(GetName(), rhs.GetName()) == 0);
+}
+
+bool SBTypeNameSpecifier::operator!=(lldb::SBTypeNameSpecifier &rhs) {
+ LLDB_RECORD_METHOD(
+ bool, SBTypeNameSpecifier, operator!=,(lldb::SBTypeNameSpecifier &), rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+ return m_opaque_sp != rhs.m_opaque_sp;
+}
+
+lldb::TypeNameSpecifierImplSP SBTypeNameSpecifier::GetSP() {
+ return m_opaque_sp;
+}
+
+void SBTypeNameSpecifier::SetSP(
+ const lldb::TypeNameSpecifierImplSP &type_namespec_sp) {
+ m_opaque_sp = type_namespec_sp;
+}
+
+SBTypeNameSpecifier::SBTypeNameSpecifier(
+ const lldb::TypeNameSpecifierImplSP &type_namespec_sp)
+ : m_opaque_sp(type_namespec_sp) {}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBTypeNameSpecifier>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeNameSpecifier, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeNameSpecifier, (const char *, bool));
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeNameSpecifier, (lldb::SBType));
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeNameSpecifier,
+ (const lldb::SBTypeNameSpecifier &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeNameSpecifier, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeNameSpecifier, operator bool, ());
+ LLDB_REGISTER_METHOD(const char *, SBTypeNameSpecifier, GetName, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBTypeNameSpecifier, GetType, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeNameSpecifier, IsRegex, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeNameSpecifier, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+ LLDB_REGISTER_METHOD(
+ lldb::SBTypeNameSpecifier &,
+ SBTypeNameSpecifier, operator=,(const lldb::SBTypeNameSpecifier &));
+ LLDB_REGISTER_METHOD(
+ bool, SBTypeNameSpecifier, operator==,(lldb::SBTypeNameSpecifier &));
+ LLDB_REGISTER_METHOD(bool, SBTypeNameSpecifier, IsEqualTo,
+ (lldb::SBTypeNameSpecifier &));
+ LLDB_REGISTER_METHOD(
+ bool, SBTypeNameSpecifier, operator!=,(lldb::SBTypeNameSpecifier &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBTypeSummary.cpp b/contrib/llvm-project/lldb/source/API/SBTypeSummary.cpp
new file mode 100644
index 000000000000..8ffb23435757
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBTypeSummary.cpp
@@ -0,0 +1,537 @@
+//===-- SBTypeSummary.cpp -----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBTypeSummary.h"
+#include "SBReproducerPrivate.h"
+#include "Utils.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBValue.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+
+#include "llvm/Support/Casting.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBTypeSummaryOptions::SBTypeSummaryOptions() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeSummaryOptions);
+
+ m_opaque_up.reset(new TypeSummaryOptions());
+}
+
+SBTypeSummaryOptions::SBTypeSummaryOptions(
+ const lldb::SBTypeSummaryOptions &rhs) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeSummaryOptions,
+ (const lldb::SBTypeSummaryOptions &), rhs);
+
+ m_opaque_up = clone(rhs.m_opaque_up);
+}
+
+SBTypeSummaryOptions::~SBTypeSummaryOptions() {}
+
+bool SBTypeSummaryOptions::IsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTypeSummaryOptions, IsValid);
+ return this->operator bool();
+}
+SBTypeSummaryOptions::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeSummaryOptions, operator bool);
+
+ return m_opaque_up.get();
+}
+
+lldb::LanguageType SBTypeSummaryOptions::GetLanguage() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::LanguageType, SBTypeSummaryOptions,
+ GetLanguage);
+
+ if (IsValid())
+ return m_opaque_up->GetLanguage();
+ return lldb::eLanguageTypeUnknown;
+}
+
+lldb::TypeSummaryCapping SBTypeSummaryOptions::GetCapping() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::TypeSummaryCapping, SBTypeSummaryOptions,
+ GetCapping);
+
+ if (IsValid())
+ return m_opaque_up->GetCapping();
+ return eTypeSummaryCapped;
+}
+
+void SBTypeSummaryOptions::SetLanguage(lldb::LanguageType l) {
+ LLDB_RECORD_METHOD(void, SBTypeSummaryOptions, SetLanguage,
+ (lldb::LanguageType), l);
+
+ if (IsValid())
+ m_opaque_up->SetLanguage(l);
+}
+
+void SBTypeSummaryOptions::SetCapping(lldb::TypeSummaryCapping c) {
+ LLDB_RECORD_METHOD(void, SBTypeSummaryOptions, SetCapping,
+ (lldb::TypeSummaryCapping), c);
+
+ if (IsValid())
+ m_opaque_up->SetCapping(c);
+}
+
+lldb_private::TypeSummaryOptions *SBTypeSummaryOptions::operator->() {
+ return m_opaque_up.get();
+}
+
+const lldb_private::TypeSummaryOptions *SBTypeSummaryOptions::
+operator->() const {
+ return m_opaque_up.get();
+}
+
+lldb_private::TypeSummaryOptions *SBTypeSummaryOptions::get() {
+ return m_opaque_up.get();
+}
+
+lldb_private::TypeSummaryOptions &SBTypeSummaryOptions::ref() {
+ return *m_opaque_up;
+}
+
+const lldb_private::TypeSummaryOptions &SBTypeSummaryOptions::ref() const {
+ return *m_opaque_up;
+}
+
+SBTypeSummaryOptions::SBTypeSummaryOptions(
+ const lldb_private::TypeSummaryOptions *lldb_object_ptr) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeSummaryOptions,
+ (const lldb_private::TypeSummaryOptions *),
+ lldb_object_ptr);
+
+ SetOptions(lldb_object_ptr);
+}
+
+void SBTypeSummaryOptions::SetOptions(
+ const lldb_private::TypeSummaryOptions *lldb_object_ptr) {
+ if (lldb_object_ptr)
+ m_opaque_up.reset(new TypeSummaryOptions(*lldb_object_ptr));
+ else
+ m_opaque_up.reset(new TypeSummaryOptions());
+}
+
+SBTypeSummary::SBTypeSummary() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeSummary);
+}
+
+SBTypeSummary SBTypeSummary::CreateWithSummaryString(const char *data,
+ uint32_t options) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBTypeSummary, SBTypeSummary,
+ CreateWithSummaryString, (const char *, uint32_t),
+ data, options);
+
+ if (!data || data[0] == 0)
+ return LLDB_RECORD_RESULT(SBTypeSummary());
+
+ return LLDB_RECORD_RESULT(
+ SBTypeSummary(TypeSummaryImplSP(new StringSummaryFormat(options, data))));
+}
+
+SBTypeSummary SBTypeSummary::CreateWithFunctionName(const char *data,
+ uint32_t options) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBTypeSummary, SBTypeSummary,
+ CreateWithFunctionName, (const char *, uint32_t),
+ data, options);
+
+ if (!data || data[0] == 0)
+ return LLDB_RECORD_RESULT(SBTypeSummary());
+
+ return LLDB_RECORD_RESULT(
+ SBTypeSummary(TypeSummaryImplSP(new ScriptSummaryFormat(options, data))));
+}
+
+SBTypeSummary SBTypeSummary::CreateWithScriptCode(const char *data,
+ uint32_t options) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBTypeSummary, SBTypeSummary,
+ CreateWithScriptCode, (const char *, uint32_t),
+ data, options);
+
+ if (!data || data[0] == 0)
+ return LLDB_RECORD_RESULT(SBTypeSummary());
+
+ return LLDB_RECORD_RESULT(SBTypeSummary(
+ TypeSummaryImplSP(new ScriptSummaryFormat(options, "", data))));
+}
+
+SBTypeSummary SBTypeSummary::CreateWithCallback(FormatCallback cb,
+ uint32_t options,
+ const char *description) {
+ LLDB_RECORD_DUMMY(
+ lldb::SBTypeSummary, SBTypeSummary, CreateWithCallback,
+ (lldb::SBTypeSummary::FormatCallback, uint32_t, const char *), cb,
+ options, description);
+
+ SBTypeSummary retval;
+ if (cb) {
+ retval.SetSP(TypeSummaryImplSP(new CXXFunctionSummaryFormat(
+ options,
+ [cb](ValueObject &valobj, Stream &stm,
+ const TypeSummaryOptions &opt) -> bool {
+ SBStream stream;
+ SBValue sb_value(valobj.GetSP());
+ SBTypeSummaryOptions options(&opt);
+ if (!cb(sb_value, options, stream))
+ return false;
+ stm.Write(stream.GetData(), stream.GetSize());
+ return true;
+ },
+ description ? description : "callback summary formatter")));
+ }
+
+ return retval;
+}
+
+SBTypeSummary::SBTypeSummary(const lldb::SBTypeSummary &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeSummary, (const lldb::SBTypeSummary &), rhs);
+}
+
+SBTypeSummary::~SBTypeSummary() {}
+
+bool SBTypeSummary::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeSummary, IsValid);
+ return this->operator bool();
+}
+SBTypeSummary::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeSummary, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+bool SBTypeSummary::IsFunctionCode() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTypeSummary, IsFunctionCode);
+
+ if (!IsValid())
+ return false;
+ if (ScriptSummaryFormat *script_summary_ptr =
+ llvm::dyn_cast<ScriptSummaryFormat>(m_opaque_sp.get())) {
+ const char *ftext = script_summary_ptr->GetPythonScript();
+ return (ftext && *ftext != 0);
+ }
+ return false;
+}
+
+bool SBTypeSummary::IsFunctionName() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTypeSummary, IsFunctionName);
+
+ if (!IsValid())
+ return false;
+ if (ScriptSummaryFormat *script_summary_ptr =
+ llvm::dyn_cast<ScriptSummaryFormat>(m_opaque_sp.get())) {
+ const char *ftext = script_summary_ptr->GetPythonScript();
+ return (!ftext || *ftext == 0);
+ }
+ return false;
+}
+
+bool SBTypeSummary::IsSummaryString() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTypeSummary, IsSummaryString);
+
+ if (!IsValid())
+ return false;
+
+ return m_opaque_sp->GetKind() == TypeSummaryImpl::Kind::eSummaryString;
+}
+
+const char *SBTypeSummary::GetData() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTypeSummary, GetData);
+
+ if (!IsValid())
+ return nullptr;
+ if (ScriptSummaryFormat *script_summary_ptr =
+ llvm::dyn_cast<ScriptSummaryFormat>(m_opaque_sp.get())) {
+ const char *fname = script_summary_ptr->GetFunctionName();
+ const char *ftext = script_summary_ptr->GetPythonScript();
+ if (ftext && *ftext)
+ return ftext;
+ return fname;
+ } else if (StringSummaryFormat *string_summary_ptr =
+ llvm::dyn_cast<StringSummaryFormat>(m_opaque_sp.get()))
+ return string_summary_ptr->GetSummaryString();
+ return nullptr;
+}
+
+uint32_t SBTypeSummary::GetOptions() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeSummary, GetOptions);
+
+ if (!IsValid())
+ return lldb::eTypeOptionNone;
+ return m_opaque_sp->GetOptions();
+}
+
+void SBTypeSummary::SetOptions(uint32_t value) {
+ LLDB_RECORD_METHOD(void, SBTypeSummary, SetOptions, (uint32_t), value);
+
+ if (!CopyOnWrite_Impl())
+ return;
+ m_opaque_sp->SetOptions(value);
+}
+
+void SBTypeSummary::SetSummaryString(const char *data) {
+ LLDB_RECORD_METHOD(void, SBTypeSummary, SetSummaryString, (const char *),
+ data);
+
+ if (!IsValid())
+ return;
+ if (!llvm::isa<StringSummaryFormat>(m_opaque_sp.get()))
+ ChangeSummaryType(false);
+ if (StringSummaryFormat *string_summary_ptr =
+ llvm::dyn_cast<StringSummaryFormat>(m_opaque_sp.get()))
+ string_summary_ptr->SetSummaryString(data);
+}
+
+void SBTypeSummary::SetFunctionName(const char *data) {
+ LLDB_RECORD_METHOD(void, SBTypeSummary, SetFunctionName, (const char *),
+ data);
+
+ if (!IsValid())
+ return;
+ if (!llvm::isa<ScriptSummaryFormat>(m_opaque_sp.get()))
+ ChangeSummaryType(true);
+ if (ScriptSummaryFormat *script_summary_ptr =
+ llvm::dyn_cast<ScriptSummaryFormat>(m_opaque_sp.get()))
+ script_summary_ptr->SetFunctionName(data);
+}
+
+void SBTypeSummary::SetFunctionCode(const char *data) {
+ LLDB_RECORD_METHOD(void, SBTypeSummary, SetFunctionCode, (const char *),
+ data);
+
+ if (!IsValid())
+ return;
+ if (!llvm::isa<ScriptSummaryFormat>(m_opaque_sp.get()))
+ ChangeSummaryType(true);
+ if (ScriptSummaryFormat *script_summary_ptr =
+ llvm::dyn_cast<ScriptSummaryFormat>(m_opaque_sp.get()))
+ script_summary_ptr->SetPythonScript(data);
+}
+
+bool SBTypeSummary::GetDescription(lldb::SBStream &description,
+ lldb::DescriptionLevel description_level) {
+ LLDB_RECORD_METHOD(bool, SBTypeSummary, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ description_level);
+
+ if (!CopyOnWrite_Impl())
+ return false;
+ else {
+ description.Printf("%s\n", m_opaque_sp->GetDescription().c_str());
+ return true;
+ }
+}
+
+bool SBTypeSummary::DoesPrintValue(lldb::SBValue value) {
+ LLDB_RECORD_METHOD(bool, SBTypeSummary, DoesPrintValue, (lldb::SBValue),
+ value);
+
+ if (!IsValid())
+ return false;
+ lldb::ValueObjectSP value_sp = value.GetSP();
+ return m_opaque_sp->DoesPrintValue(value_sp.get());
+}
+
+lldb::SBTypeSummary &SBTypeSummary::operator=(const lldb::SBTypeSummary &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBTypeSummary &,
+ SBTypeSummary, operator=,(const lldb::SBTypeSummary &),
+ rhs);
+
+ if (this != &rhs) {
+ m_opaque_sp = rhs.m_opaque_sp;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBTypeSummary::operator==(lldb::SBTypeSummary &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeSummary, operator==,(lldb::SBTypeSummary &),
+ rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+ return m_opaque_sp == rhs.m_opaque_sp;
+}
+
+bool SBTypeSummary::IsEqualTo(lldb::SBTypeSummary &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeSummary, IsEqualTo, (lldb::SBTypeSummary &),
+ rhs);
+
+ if (IsValid()) {
+ // valid and invalid are different
+ if (!rhs.IsValid())
+ return false;
+ } else {
+ // invalid and valid are different
+ if (rhs.IsValid())
+ return false;
+ else
+ // both invalid are the same
+ return true;
+ }
+
+ if (m_opaque_sp->GetKind() != rhs.m_opaque_sp->GetKind())
+ return false;
+
+ switch (m_opaque_sp->GetKind()) {
+ case TypeSummaryImpl::Kind::eCallback:
+ return llvm::dyn_cast<CXXFunctionSummaryFormat>(m_opaque_sp.get()) ==
+ llvm::dyn_cast<CXXFunctionSummaryFormat>(rhs.m_opaque_sp.get());
+ case TypeSummaryImpl::Kind::eScript:
+ if (IsFunctionCode() != rhs.IsFunctionCode())
+ return false;
+ if (IsFunctionName() != rhs.IsFunctionName())
+ return false;
+ return GetOptions() == rhs.GetOptions();
+ case TypeSummaryImpl::Kind::eSummaryString:
+ if (IsSummaryString() != rhs.IsSummaryString())
+ return false;
+ return GetOptions() == rhs.GetOptions();
+ case TypeSummaryImpl::Kind::eInternal:
+ return (m_opaque_sp.get() == rhs.m_opaque_sp.get());
+ }
+
+ return false;
+}
+
+bool SBTypeSummary::operator!=(lldb::SBTypeSummary &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeSummary, operator!=,(lldb::SBTypeSummary &),
+ rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+ return m_opaque_sp != rhs.m_opaque_sp;
+}
+
+lldb::TypeSummaryImplSP SBTypeSummary::GetSP() { return m_opaque_sp; }
+
+void SBTypeSummary::SetSP(const lldb::TypeSummaryImplSP &typesummary_impl_sp) {
+ m_opaque_sp = typesummary_impl_sp;
+}
+
+SBTypeSummary::SBTypeSummary(const lldb::TypeSummaryImplSP &typesummary_impl_sp)
+ : m_opaque_sp(typesummary_impl_sp) {}
+
+bool SBTypeSummary::CopyOnWrite_Impl() {
+ if (!IsValid())
+ return false;
+
+ if (m_opaque_sp.unique())
+ return true;
+
+ TypeSummaryImplSP new_sp;
+
+ if (CXXFunctionSummaryFormat *current_summary_ptr =
+ llvm::dyn_cast<CXXFunctionSummaryFormat>(m_opaque_sp.get())) {
+ new_sp = TypeSummaryImplSP(new CXXFunctionSummaryFormat(
+ GetOptions(), current_summary_ptr->m_impl,
+ current_summary_ptr->m_description.c_str()));
+ } else if (ScriptSummaryFormat *current_summary_ptr =
+ llvm::dyn_cast<ScriptSummaryFormat>(m_opaque_sp.get())) {
+ new_sp = TypeSummaryImplSP(new ScriptSummaryFormat(
+ GetOptions(), current_summary_ptr->GetFunctionName(),
+ current_summary_ptr->GetPythonScript()));
+ } else if (StringSummaryFormat *current_summary_ptr =
+ llvm::dyn_cast<StringSummaryFormat>(m_opaque_sp.get())) {
+ new_sp = TypeSummaryImplSP(new StringSummaryFormat(
+ GetOptions(), current_summary_ptr->GetSummaryString()));
+ }
+
+ SetSP(new_sp);
+
+ return nullptr != new_sp.get();
+}
+
+bool SBTypeSummary::ChangeSummaryType(bool want_script) {
+ if (!IsValid())
+ return false;
+
+ TypeSummaryImplSP new_sp;
+
+ if (want_script ==
+ (m_opaque_sp->GetKind() == TypeSummaryImpl::Kind::eScript)) {
+ if (m_opaque_sp->GetKind() ==
+ lldb_private::TypeSummaryImpl::Kind::eCallback &&
+ !want_script)
+ new_sp = TypeSummaryImplSP(new StringSummaryFormat(GetOptions(), ""));
+ else
+ return CopyOnWrite_Impl();
+ }
+
+ if (!new_sp) {
+ if (want_script)
+ new_sp = TypeSummaryImplSP(new ScriptSummaryFormat(GetOptions(), "", ""));
+ else
+ new_sp = TypeSummaryImplSP(new StringSummaryFormat(GetOptions(), ""));
+ }
+
+ SetSP(new_sp);
+
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBTypeSummaryOptions>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeSummaryOptions, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeSummaryOptions,
+ (const lldb::SBTypeSummaryOptions &));
+ LLDB_REGISTER_METHOD(bool, SBTypeSummaryOptions, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeSummaryOptions, operator bool, ());
+ LLDB_REGISTER_METHOD(lldb::LanguageType, SBTypeSummaryOptions, GetLanguage,
+ ());
+ LLDB_REGISTER_METHOD(lldb::TypeSummaryCapping, SBTypeSummaryOptions,
+ GetCapping, ());
+ LLDB_REGISTER_METHOD(void, SBTypeSummaryOptions, SetLanguage,
+ (lldb::LanguageType));
+ LLDB_REGISTER_METHOD(void, SBTypeSummaryOptions, SetCapping,
+ (lldb::TypeSummaryCapping));
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeSummaryOptions,
+ (const lldb_private::TypeSummaryOptions *));
+}
+
+template <>
+void RegisterMethods<SBTypeSummary>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeSummary, ());
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBTypeSummary, SBTypeSummary,
+ CreateWithSummaryString,
+ (const char *, uint32_t));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBTypeSummary, SBTypeSummary,
+ CreateWithFunctionName,
+ (const char *, uint32_t));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBTypeSummary, SBTypeSummary,
+ CreateWithScriptCode, (const char *, uint32_t));
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeSummary, (const lldb::SBTypeSummary &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeSummary, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeSummary, operator bool, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeSummary, IsFunctionCode, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeSummary, IsFunctionName, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeSummary, IsSummaryString, ());
+ LLDB_REGISTER_METHOD(const char *, SBTypeSummary, GetData, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeSummary, GetOptions, ());
+ LLDB_REGISTER_METHOD(void, SBTypeSummary, SetOptions, (uint32_t));
+ LLDB_REGISTER_METHOD(void, SBTypeSummary, SetSummaryString, (const char *));
+ LLDB_REGISTER_METHOD(void, SBTypeSummary, SetFunctionName, (const char *));
+ LLDB_REGISTER_METHOD(void, SBTypeSummary, SetFunctionCode, (const char *));
+ LLDB_REGISTER_METHOD(bool, SBTypeSummary, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+ LLDB_REGISTER_METHOD(bool, SBTypeSummary, DoesPrintValue, (lldb::SBValue));
+ LLDB_REGISTER_METHOD(
+ lldb::SBTypeSummary &,
+ SBTypeSummary, operator=,(const lldb::SBTypeSummary &));
+ LLDB_REGISTER_METHOD(bool,
+ SBTypeSummary, operator==,(lldb::SBTypeSummary &));
+ LLDB_REGISTER_METHOD(bool, SBTypeSummary, IsEqualTo,
+ (lldb::SBTypeSummary &));
+ LLDB_REGISTER_METHOD(bool,
+ SBTypeSummary, operator!=,(lldb::SBTypeSummary &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBTypeSynthetic.cpp b/contrib/llvm-project/lldb/source/API/SBTypeSynthetic.cpp
new file mode 100644
index 000000000000..df6fce1269f0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBTypeSynthetic.cpp
@@ -0,0 +1,248 @@
+//===-- SBTypeSynthetic.cpp -----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBTypeSynthetic.h"
+#include "SBReproducerPrivate.h"
+
+#include "lldb/API/SBStream.h"
+
+#include "lldb/DataFormatters/DataVisualization.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBTypeSynthetic::SBTypeSynthetic() : m_opaque_sp() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTypeSynthetic);
+}
+
+SBTypeSynthetic SBTypeSynthetic::CreateWithClassName(const char *data,
+ uint32_t options) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBTypeSynthetic, SBTypeSynthetic,
+ CreateWithClassName, (const char *, uint32_t), data,
+ options);
+
+ if (!data || data[0] == 0)
+ return LLDB_RECORD_RESULT(SBTypeSynthetic());
+ return LLDB_RECORD_RESULT(SBTypeSynthetic(ScriptedSyntheticChildrenSP(
+ new ScriptedSyntheticChildren(options, data, ""))));
+}
+
+SBTypeSynthetic SBTypeSynthetic::CreateWithScriptCode(const char *data,
+ uint32_t options) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBTypeSynthetic, SBTypeSynthetic,
+ CreateWithScriptCode, (const char *, uint32_t),
+ data, options);
+
+ if (!data || data[0] == 0)
+ return LLDB_RECORD_RESULT(SBTypeSynthetic());
+ return LLDB_RECORD_RESULT(SBTypeSynthetic(ScriptedSyntheticChildrenSP(
+ new ScriptedSyntheticChildren(options, "", data))));
+}
+
+SBTypeSynthetic::SBTypeSynthetic(const lldb::SBTypeSynthetic &rhs)
+ : m_opaque_sp(rhs.m_opaque_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBTypeSynthetic, (const lldb::SBTypeSynthetic &),
+ rhs);
+}
+
+SBTypeSynthetic::~SBTypeSynthetic() {}
+
+bool SBTypeSynthetic::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeSynthetic, IsValid);
+ return this->operator bool();
+}
+SBTypeSynthetic::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTypeSynthetic, operator bool);
+
+ return m_opaque_sp.get() != nullptr;
+}
+
+bool SBTypeSynthetic::IsClassCode() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTypeSynthetic, IsClassCode);
+
+ if (!IsValid())
+ return false;
+ const char *code = m_opaque_sp->GetPythonCode();
+ return (code && *code);
+}
+
+bool SBTypeSynthetic::IsClassName() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBTypeSynthetic, IsClassName);
+
+ if (!IsValid())
+ return false;
+ return !IsClassCode();
+}
+
+const char *SBTypeSynthetic::GetData() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTypeSynthetic, GetData);
+
+ if (!IsValid())
+ return nullptr;
+ if (IsClassCode())
+ return m_opaque_sp->GetPythonCode();
+ else
+ return m_opaque_sp->GetPythonClassName();
+}
+
+void SBTypeSynthetic::SetClassName(const char *data) {
+ LLDB_RECORD_METHOD(void, SBTypeSynthetic, SetClassName, (const char *), data);
+
+ if (IsValid() && data && *data)
+ m_opaque_sp->SetPythonClassName(data);
+}
+
+void SBTypeSynthetic::SetClassCode(const char *data) {
+ LLDB_RECORD_METHOD(void, SBTypeSynthetic, SetClassCode, (const char *), data);
+
+ if (IsValid() && data && *data)
+ m_opaque_sp->SetPythonCode(data);
+}
+
+uint32_t SBTypeSynthetic::GetOptions() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTypeSynthetic, GetOptions);
+
+ if (!IsValid())
+ return lldb::eTypeOptionNone;
+ return m_opaque_sp->GetOptions();
+}
+
+void SBTypeSynthetic::SetOptions(uint32_t value) {
+ LLDB_RECORD_METHOD(void, SBTypeSynthetic, SetOptions, (uint32_t), value);
+
+ if (!CopyOnWrite_Impl())
+ return;
+ m_opaque_sp->SetOptions(value);
+}
+
+bool SBTypeSynthetic::GetDescription(lldb::SBStream &description,
+ lldb::DescriptionLevel description_level) {
+ LLDB_RECORD_METHOD(bool, SBTypeSynthetic, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ description_level);
+
+ if (m_opaque_sp) {
+ description.Printf("%s\n", m_opaque_sp->GetDescription().c_str());
+ return true;
+ }
+ return false;
+}
+
+lldb::SBTypeSynthetic &SBTypeSynthetic::
+operator=(const lldb::SBTypeSynthetic &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBTypeSynthetic &,
+ SBTypeSynthetic, operator=,(const lldb::SBTypeSynthetic &),
+ rhs);
+
+ if (this != &rhs) {
+ m_opaque_sp = rhs.m_opaque_sp;
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+bool SBTypeSynthetic::operator==(lldb::SBTypeSynthetic &rhs) {
+ LLDB_RECORD_METHOD(
+ bool, SBTypeSynthetic, operator==,(lldb::SBTypeSynthetic &), rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+ return m_opaque_sp == rhs.m_opaque_sp;
+}
+
+bool SBTypeSynthetic::IsEqualTo(lldb::SBTypeSynthetic &rhs) {
+ LLDB_RECORD_METHOD(bool, SBTypeSynthetic, IsEqualTo,
+ (lldb::SBTypeSynthetic &), rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+
+ if (m_opaque_sp->IsScripted() != rhs.m_opaque_sp->IsScripted())
+ return false;
+
+ if (IsClassCode() != rhs.IsClassCode())
+ return false;
+
+ if (strcmp(GetData(), rhs.GetData()))
+ return false;
+
+ return GetOptions() == rhs.GetOptions();
+}
+
+bool SBTypeSynthetic::operator!=(lldb::SBTypeSynthetic &rhs) {
+ LLDB_RECORD_METHOD(
+ bool, SBTypeSynthetic, operator!=,(lldb::SBTypeSynthetic &), rhs);
+
+ if (!IsValid())
+ return !rhs.IsValid();
+ return m_opaque_sp != rhs.m_opaque_sp;
+}
+
+lldb::ScriptedSyntheticChildrenSP SBTypeSynthetic::GetSP() {
+ return m_opaque_sp;
+}
+
+void SBTypeSynthetic::SetSP(
+ const lldb::ScriptedSyntheticChildrenSP &TypeSynthetic_impl_sp) {
+ m_opaque_sp = TypeSynthetic_impl_sp;
+}
+
+SBTypeSynthetic::SBTypeSynthetic(
+ const lldb::ScriptedSyntheticChildrenSP &TypeSynthetic_impl_sp)
+ : m_opaque_sp(TypeSynthetic_impl_sp) {}
+
+bool SBTypeSynthetic::CopyOnWrite_Impl() {
+ if (!IsValid())
+ return false;
+ if (m_opaque_sp.unique())
+ return true;
+
+ ScriptedSyntheticChildrenSP new_sp(new ScriptedSyntheticChildren(
+ m_opaque_sp->GetOptions(), m_opaque_sp->GetPythonClassName(),
+ m_opaque_sp->GetPythonCode()));
+
+ SetSP(new_sp);
+
+ return true;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBTypeSynthetic>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeSynthetic, ());
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBTypeSynthetic, SBTypeSynthetic,
+ CreateWithClassName, (const char *, uint32_t));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBTypeSynthetic, SBTypeSynthetic,
+ CreateWithScriptCode, (const char *, uint32_t));
+ LLDB_REGISTER_CONSTRUCTOR(SBTypeSynthetic, (const lldb::SBTypeSynthetic &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeSynthetic, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBTypeSynthetic, operator bool, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeSynthetic, IsClassCode, ());
+ LLDB_REGISTER_METHOD(bool, SBTypeSynthetic, IsClassName, ());
+ LLDB_REGISTER_METHOD(const char *, SBTypeSynthetic, GetData, ());
+ LLDB_REGISTER_METHOD(void, SBTypeSynthetic, SetClassName, (const char *));
+ LLDB_REGISTER_METHOD(void, SBTypeSynthetic, SetClassCode, (const char *));
+ LLDB_REGISTER_METHOD(uint32_t, SBTypeSynthetic, GetOptions, ());
+ LLDB_REGISTER_METHOD(void, SBTypeSynthetic, SetOptions, (uint32_t));
+ LLDB_REGISTER_METHOD(bool, SBTypeSynthetic, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+ LLDB_REGISTER_METHOD(
+ lldb::SBTypeSynthetic &,
+ SBTypeSynthetic, operator=,(const lldb::SBTypeSynthetic &));
+ LLDB_REGISTER_METHOD(bool,
+ SBTypeSynthetic, operator==,(lldb::SBTypeSynthetic &));
+ LLDB_REGISTER_METHOD(bool, SBTypeSynthetic, IsEqualTo,
+ (lldb::SBTypeSynthetic &));
+ LLDB_REGISTER_METHOD(bool,
+ SBTypeSynthetic, operator!=,(lldb::SBTypeSynthetic &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBUnixSignals.cpp b/contrib/llvm-project/lldb/source/API/SBUnixSignals.cpp
new file mode 100644
index 000000000000..277a92d21ae9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBUnixSignals.cpp
@@ -0,0 +1,206 @@
+//===-- SBUnixSignals.cpp -------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SBReproducerPrivate.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/lldb-defines.h"
+
+#include "lldb/API/SBUnixSignals.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBUnixSignals::SBUnixSignals() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBUnixSignals);
+}
+
+SBUnixSignals::SBUnixSignals(const SBUnixSignals &rhs)
+ : m_opaque_wp(rhs.m_opaque_wp) {
+ LLDB_RECORD_CONSTRUCTOR(SBUnixSignals, (const lldb::SBUnixSignals &), rhs);
+}
+
+SBUnixSignals::SBUnixSignals(ProcessSP &process_sp)
+ : m_opaque_wp(process_sp ? process_sp->GetUnixSignals() : nullptr) {}
+
+SBUnixSignals::SBUnixSignals(PlatformSP &platform_sp)
+ : m_opaque_wp(platform_sp ? platform_sp->GetUnixSignals() : nullptr) {}
+
+const SBUnixSignals &SBUnixSignals::operator=(const SBUnixSignals &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBUnixSignals &,
+ SBUnixSignals, operator=,(const lldb::SBUnixSignals &),
+ rhs);
+
+ if (this != &rhs)
+ m_opaque_wp = rhs.m_opaque_wp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBUnixSignals::~SBUnixSignals() {}
+
+UnixSignalsSP SBUnixSignals::GetSP() const { return m_opaque_wp.lock(); }
+
+void SBUnixSignals::SetSP(const UnixSignalsSP &signals_sp) {
+ m_opaque_wp = signals_sp;
+}
+
+void SBUnixSignals::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBUnixSignals, Clear);
+
+ m_opaque_wp.reset();
+}
+
+bool SBUnixSignals::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBUnixSignals, IsValid);
+ return this->operator bool();
+}
+SBUnixSignals::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBUnixSignals, operator bool);
+
+ return static_cast<bool>(GetSP());
+}
+
+const char *SBUnixSignals::GetSignalAsCString(int32_t signo) const {
+ LLDB_RECORD_METHOD_CONST(const char *, SBUnixSignals, GetSignalAsCString,
+ (int32_t), signo);
+
+ if (auto signals_sp = GetSP())
+ return signals_sp->GetSignalAsCString(signo);
+
+ return nullptr;
+}
+
+int32_t SBUnixSignals::GetSignalNumberFromName(const char *name) const {
+ LLDB_RECORD_METHOD_CONST(int32_t, SBUnixSignals, GetSignalNumberFromName,
+ (const char *), name);
+
+ if (auto signals_sp = GetSP())
+ return signals_sp->GetSignalNumberFromName(name);
+
+ return LLDB_INVALID_SIGNAL_NUMBER;
+}
+
+bool SBUnixSignals::GetShouldSuppress(int32_t signo) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBUnixSignals, GetShouldSuppress, (int32_t),
+ signo);
+
+ if (auto signals_sp = GetSP())
+ return signals_sp->GetShouldSuppress(signo);
+
+ return false;
+}
+
+bool SBUnixSignals::SetShouldSuppress(int32_t signo, bool value) {
+ LLDB_RECORD_METHOD(bool, SBUnixSignals, SetShouldSuppress, (int32_t, bool),
+ signo, value);
+
+ auto signals_sp = GetSP();
+
+ if (signals_sp)
+ return signals_sp->SetShouldSuppress(signo, value);
+
+ return false;
+}
+
+bool SBUnixSignals::GetShouldStop(int32_t signo) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBUnixSignals, GetShouldStop, (int32_t),
+ signo);
+
+ if (auto signals_sp = GetSP())
+ return signals_sp->GetShouldStop(signo);
+
+ return false;
+}
+
+bool SBUnixSignals::SetShouldStop(int32_t signo, bool value) {
+ LLDB_RECORD_METHOD(bool, SBUnixSignals, SetShouldStop, (int32_t, bool), signo,
+ value);
+
+ auto signals_sp = GetSP();
+
+ if (signals_sp)
+ return signals_sp->SetShouldStop(signo, value);
+
+ return false;
+}
+
+bool SBUnixSignals::GetShouldNotify(int32_t signo) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBUnixSignals, GetShouldNotify, (int32_t),
+ signo);
+
+ if (auto signals_sp = GetSP())
+ return signals_sp->GetShouldNotify(signo);
+
+ return false;
+}
+
+bool SBUnixSignals::SetShouldNotify(int32_t signo, bool value) {
+ LLDB_RECORD_METHOD(bool, SBUnixSignals, SetShouldNotify, (int32_t, bool),
+ signo, value);
+
+ auto signals_sp = GetSP();
+
+ if (signals_sp)
+ return signals_sp->SetShouldNotify(signo, value);
+
+ return false;
+}
+
+int32_t SBUnixSignals::GetNumSignals() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(int32_t, SBUnixSignals, GetNumSignals);
+
+ if (auto signals_sp = GetSP())
+ return signals_sp->GetNumSignals();
+
+ return -1;
+}
+
+int32_t SBUnixSignals::GetSignalAtIndex(int32_t index) const {
+ LLDB_RECORD_METHOD_CONST(int32_t, SBUnixSignals, GetSignalAtIndex, (int32_t),
+ index);
+
+ if (auto signals_sp = GetSP())
+ return signals_sp->GetSignalAtIndex(index);
+
+ return LLDB_INVALID_SIGNAL_NUMBER;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBUnixSignals>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBUnixSignals, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBUnixSignals, (const lldb::SBUnixSignals &));
+ LLDB_REGISTER_METHOD(
+ const lldb::SBUnixSignals &,
+ SBUnixSignals, operator=,(const lldb::SBUnixSignals &));
+ LLDB_REGISTER_METHOD(void, SBUnixSignals, Clear, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBUnixSignals, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBUnixSignals, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(const char *, SBUnixSignals, GetSignalAsCString,
+ (int32_t));
+ LLDB_REGISTER_METHOD_CONST(int32_t, SBUnixSignals, GetSignalNumberFromName,
+ (const char *));
+ LLDB_REGISTER_METHOD_CONST(bool, SBUnixSignals, GetShouldSuppress,
+ (int32_t));
+ LLDB_REGISTER_METHOD(bool, SBUnixSignals, SetShouldSuppress,
+ (int32_t, bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBUnixSignals, GetShouldStop, (int32_t));
+ LLDB_REGISTER_METHOD(bool, SBUnixSignals, SetShouldStop, (int32_t, bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBUnixSignals, GetShouldNotify, (int32_t));
+ LLDB_REGISTER_METHOD(bool, SBUnixSignals, SetShouldNotify, (int32_t, bool));
+ LLDB_REGISTER_METHOD_CONST(int32_t, SBUnixSignals, GetNumSignals, ());
+ LLDB_REGISTER_METHOD_CONST(int32_t, SBUnixSignals, GetSignalAtIndex,
+ (int32_t));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBValue.cpp b/contrib/llvm-project/lldb/source/API/SBValue.cpp
new file mode 100644
index 000000000000..838300763522
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBValue.cpp
@@ -0,0 +1,1690 @@
+//===-- SBValue.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBValue.h"
+#include "SBReproducerPrivate.h"
+
+#include "lldb/API/SBDeclaration.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBTypeFilter.h"
+#include "lldb/API/SBTypeFormat.h"
+#include "lldb/API/SBTypeSummary.h"
+#include "lldb/API/SBTypeSynthetic.h"
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Declaration.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Stream.h"
+
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBExpressionOptions.h"
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/API/SBThread.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+class ValueImpl {
+public:
+ ValueImpl() {}
+
+ ValueImpl(lldb::ValueObjectSP in_valobj_sp,
+ lldb::DynamicValueType use_dynamic, bool use_synthetic,
+ const char *name = nullptr)
+ : m_valobj_sp(), m_use_dynamic(use_dynamic),
+ m_use_synthetic(use_synthetic), m_name(name) {
+ if (in_valobj_sp) {
+ if ((m_valobj_sp = in_valobj_sp->GetQualifiedRepresentationIfAvailable(
+ lldb::eNoDynamicValues, false))) {
+ if (!m_name.IsEmpty())
+ m_valobj_sp->SetName(m_name);
+ }
+ }
+ }
+
+ ValueImpl(const ValueImpl &rhs)
+ : m_valobj_sp(rhs.m_valobj_sp), m_use_dynamic(rhs.m_use_dynamic),
+ m_use_synthetic(rhs.m_use_synthetic), m_name(rhs.m_name) {}
+
+ ValueImpl &operator=(const ValueImpl &rhs) {
+ if (this != &rhs) {
+ m_valobj_sp = rhs.m_valobj_sp;
+ m_use_dynamic = rhs.m_use_dynamic;
+ m_use_synthetic = rhs.m_use_synthetic;
+ m_name = rhs.m_name;
+ }
+ return *this;
+ }
+
+ bool IsValid() {
+ if (m_valobj_sp.get() == nullptr)
+ return false;
+ else {
+ // FIXME: This check is necessary but not sufficient. We for sure don't
+ // want to touch SBValues whose owning
+ // targets have gone away. This check is a little weak in that it
+ // enforces that restriction when you call IsValid, but since IsValid
+ // doesn't lock the target, you have no guarantee that the SBValue won't
+ // go invalid after you call this... Also, an SBValue could depend on
+ // data from one of the modules in the target, and those could go away
+ // independently of the target, for instance if a module is unloaded.
+ // But right now, neither SBValues nor ValueObjects know which modules
+ // they depend on. So I have no good way to make that check without
+ // tracking that in all the ValueObject subclasses.
+ TargetSP target_sp = m_valobj_sp->GetTargetSP();
+ return target_sp && target_sp->IsValid();
+ }
+ }
+
+ lldb::ValueObjectSP GetRootSP() { return m_valobj_sp; }
+
+ lldb::ValueObjectSP GetSP(Process::StopLocker &stop_locker,
+ std::unique_lock<std::recursive_mutex> &lock,
+ Status &error) {
+ if (!m_valobj_sp) {
+ error.SetErrorString("invalid value object");
+ return m_valobj_sp;
+ }
+
+ lldb::ValueObjectSP value_sp = m_valobj_sp;
+
+ Target *target = value_sp->GetTargetSP().get();
+ if (!target)
+ return ValueObjectSP();
+
+ lock = std::unique_lock<std::recursive_mutex>(target->GetAPIMutex());
+
+ ProcessSP process_sp(value_sp->GetProcessSP());
+ if (process_sp && !stop_locker.TryLock(&process_sp->GetRunLock())) {
+ // We don't allow people to play around with ValueObject if the process
+ // is running. If you want to look at values, pause the process, then
+ // look.
+ error.SetErrorString("process must be stopped.");
+ return ValueObjectSP();
+ }
+
+ if (m_use_dynamic != eNoDynamicValues) {
+ ValueObjectSP dynamic_sp = value_sp->GetDynamicValue(m_use_dynamic);
+ if (dynamic_sp)
+ value_sp = dynamic_sp;
+ }
+
+ if (m_use_synthetic) {
+ ValueObjectSP synthetic_sp = value_sp->GetSyntheticValue(m_use_synthetic);
+ if (synthetic_sp)
+ value_sp = synthetic_sp;
+ }
+
+ if (!value_sp)
+ error.SetErrorString("invalid value object");
+ if (!m_name.IsEmpty())
+ value_sp->SetName(m_name);
+
+ return value_sp;
+ }
+
+ void SetUseDynamic(lldb::DynamicValueType use_dynamic) {
+ m_use_dynamic = use_dynamic;
+ }
+
+ void SetUseSynthetic(bool use_synthetic) { m_use_synthetic = use_synthetic; }
+
+ lldb::DynamicValueType GetUseDynamic() { return m_use_dynamic; }
+
+ bool GetUseSynthetic() { return m_use_synthetic; }
+
+ // All the derived values that we would make from the m_valobj_sp will share
+ // the ExecutionContext with m_valobj_sp, so we don't need to do the
+ // calculations in GetSP to return the Target, Process, Thread or Frame. It
+ // is convenient to provide simple accessors for these, which I do here.
+ TargetSP GetTargetSP() {
+ if (m_valobj_sp)
+ return m_valobj_sp->GetTargetSP();
+ else
+ return TargetSP();
+ }
+
+ ProcessSP GetProcessSP() {
+ if (m_valobj_sp)
+ return m_valobj_sp->GetProcessSP();
+ else
+ return ProcessSP();
+ }
+
+ ThreadSP GetThreadSP() {
+ if (m_valobj_sp)
+ return m_valobj_sp->GetThreadSP();
+ else
+ return ThreadSP();
+ }
+
+ StackFrameSP GetFrameSP() {
+ if (m_valobj_sp)
+ return m_valobj_sp->GetFrameSP();
+ else
+ return StackFrameSP();
+ }
+
+private:
+ lldb::ValueObjectSP m_valobj_sp;
+ lldb::DynamicValueType m_use_dynamic;
+ bool m_use_synthetic;
+ ConstString m_name;
+};
+
+class ValueLocker {
+public:
+ ValueLocker() {}
+
+ ValueObjectSP GetLockedSP(ValueImpl &in_value) {
+ return in_value.GetSP(m_stop_locker, m_lock, m_lock_error);
+ }
+
+ Status &GetError() { return m_lock_error; }
+
+private:
+ Process::StopLocker m_stop_locker;
+ std::unique_lock<std::recursive_mutex> m_lock;
+ Status m_lock_error;
+};
+
+SBValue::SBValue() : m_opaque_sp() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBValue); }
+
+SBValue::SBValue(const lldb::ValueObjectSP &value_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBValue, (const lldb::ValueObjectSP &), value_sp);
+
+ SetSP(value_sp);
+}
+
+SBValue::SBValue(const SBValue &rhs) {
+ LLDB_RECORD_CONSTRUCTOR(SBValue, (const lldb::SBValue &), rhs);
+
+ SetSP(rhs.m_opaque_sp);
+}
+
+SBValue &SBValue::operator=(const SBValue &rhs) {
+ LLDB_RECORD_METHOD(lldb::SBValue &,
+ SBValue, operator=,(const lldb::SBValue &), rhs);
+
+ if (this != &rhs) {
+ SetSP(rhs.m_opaque_sp);
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBValue::~SBValue() {}
+
+bool SBValue::IsValid() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBValue, IsValid);
+ return this->operator bool();
+}
+SBValue::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBValue, operator bool);
+
+ // If this function ever changes to anything that does more than just check
+ // if the opaque shared pointer is non NULL, then we need to update all "if
+ // (m_opaque_sp)" code in this file.
+ return m_opaque_sp.get() != nullptr && m_opaque_sp->IsValid() &&
+ m_opaque_sp->GetRootSP().get() != nullptr;
+}
+
+void SBValue::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBValue, Clear);
+
+ m_opaque_sp.reset();
+}
+
+SBError SBValue::GetError() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBValue, GetError);
+
+ SBError sb_error;
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ sb_error.SetError(value_sp->GetError());
+ else
+ sb_error.SetErrorStringWithFormat("error: %s",
+ locker.GetError().AsCString());
+
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+user_id_t SBValue::GetID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::user_id_t, SBValue, GetID);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ return value_sp->GetID();
+ return LLDB_INVALID_UID;
+}
+
+const char *SBValue::GetName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBValue, GetName);
+
+ const char *name = nullptr;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ name = value_sp->GetName().GetCString();
+
+ return name;
+}
+
+const char *SBValue::GetTypeName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBValue, GetTypeName);
+
+ const char *name = nullptr;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ name = value_sp->GetQualifiedTypeName().GetCString();
+ }
+
+ return name;
+}
+
+const char *SBValue::GetDisplayTypeName() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBValue, GetDisplayTypeName);
+
+ const char *name = nullptr;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ name = value_sp->GetDisplayTypeName().GetCString();
+ }
+
+ return name;
+}
+
+size_t SBValue::GetByteSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBValue, GetByteSize);
+
+ size_t result = 0;
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ result = value_sp->GetByteSize();
+ }
+
+ return result;
+}
+
+bool SBValue::IsInScope() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBValue, IsInScope);
+
+ bool result = false;
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ result = value_sp->IsInScope();
+ }
+
+ return result;
+}
+
+const char *SBValue::GetValue() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBValue, GetValue);
+
+ const char *cstr = nullptr;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ cstr = value_sp->GetValueAsCString();
+ }
+
+ return cstr;
+}
+
+ValueType SBValue::GetValueType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::ValueType, SBValue, GetValueType);
+
+ ValueType result = eValueTypeInvalid;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ result = value_sp->GetValueType();
+
+ return result;
+}
+
+const char *SBValue::GetObjectDescription() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBValue, GetObjectDescription);
+
+ const char *cstr = nullptr;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ cstr = value_sp->GetObjectDescription();
+ }
+
+ return cstr;
+}
+
+const char *SBValue::GetTypeValidatorResult() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBValue, GetTypeValidatorResult);
+
+ const char *cstr = nullptr;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ const auto &validation(value_sp->GetValidationStatus());
+ if (TypeValidatorResult::Failure == validation.first) {
+ if (validation.second.empty())
+ cstr = "unknown error";
+ else
+ cstr = validation.second.c_str();
+ }
+ }
+
+ return cstr;
+}
+
+SBType SBValue::GetType() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBType, SBValue, GetType);
+
+ SBType sb_type;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ TypeImplSP type_sp;
+ if (value_sp) {
+ type_sp = std::make_shared<TypeImpl>(value_sp->GetTypeImpl());
+ sb_type.SetSP(type_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_type);
+}
+
+bool SBValue::GetValueDidChange() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBValue, GetValueDidChange);
+
+ bool result = false;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ if (value_sp->UpdateValueIfNeeded(false))
+ result = value_sp->GetValueDidChange();
+ }
+
+ return result;
+}
+
+const char *SBValue::GetSummary() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBValue, GetSummary);
+
+ const char *cstr = nullptr;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ cstr = value_sp->GetSummaryAsCString();
+ }
+
+ return cstr;
+}
+
+const char *SBValue::GetSummary(lldb::SBStream &stream,
+ lldb::SBTypeSummaryOptions &options) {
+ LLDB_RECORD_METHOD(const char *, SBValue, GetSummary,
+ (lldb::SBStream &, lldb::SBTypeSummaryOptions &), stream,
+ options);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ std::string buffer;
+ if (value_sp->GetSummaryAsCString(buffer, options.ref()) && !buffer.empty())
+ stream.Printf("%s", buffer.c_str());
+ }
+ const char *cstr = stream.GetData();
+ return cstr;
+}
+
+const char *SBValue::GetLocation() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBValue, GetLocation);
+
+ const char *cstr = nullptr;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ cstr = value_sp->GetLocationAsCString();
+ }
+ return cstr;
+}
+
+// Deprecated - use the one that takes an lldb::SBError
+bool SBValue::SetValueFromCString(const char *value_str) {
+ LLDB_RECORD_METHOD(bool, SBValue, SetValueFromCString, (const char *),
+ value_str);
+
+ lldb::SBError dummy;
+ return SetValueFromCString(value_str, dummy);
+}
+
+bool SBValue::SetValueFromCString(const char *value_str, lldb::SBError &error) {
+ LLDB_RECORD_METHOD(bool, SBValue, SetValueFromCString,
+ (const char *, lldb::SBError &), value_str, error);
+
+ bool success = false;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ success = value_sp->SetValueFromCString(value_str, error.ref());
+ } else
+ error.SetErrorStringWithFormat("Could not get value: %s",
+ locker.GetError().AsCString());
+
+ return success;
+}
+
+lldb::SBTypeFormat SBValue::GetTypeFormat() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBTypeFormat, SBValue, GetTypeFormat);
+
+ lldb::SBTypeFormat format;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ if (value_sp->UpdateValueIfNeeded(true)) {
+ lldb::TypeFormatImplSP format_sp = value_sp->GetValueFormat();
+ if (format_sp)
+ format.SetSP(format_sp);
+ }
+ }
+ return LLDB_RECORD_RESULT(format);
+}
+
+lldb::SBTypeSummary SBValue::GetTypeSummary() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBTypeSummary, SBValue, GetTypeSummary);
+
+ lldb::SBTypeSummary summary;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ if (value_sp->UpdateValueIfNeeded(true)) {
+ lldb::TypeSummaryImplSP summary_sp = value_sp->GetSummaryFormat();
+ if (summary_sp)
+ summary.SetSP(summary_sp);
+ }
+ }
+ return LLDB_RECORD_RESULT(summary);
+}
+
+lldb::SBTypeFilter SBValue::GetTypeFilter() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBTypeFilter, SBValue, GetTypeFilter);
+
+ lldb::SBTypeFilter filter;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ if (value_sp->UpdateValueIfNeeded(true)) {
+ lldb::SyntheticChildrenSP synthetic_sp = value_sp->GetSyntheticChildren();
+
+ if (synthetic_sp && !synthetic_sp->IsScripted()) {
+ TypeFilterImplSP filter_sp =
+ std::static_pointer_cast<TypeFilterImpl>(synthetic_sp);
+ filter.SetSP(filter_sp);
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(filter);
+}
+
+lldb::SBTypeSynthetic SBValue::GetTypeSynthetic() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBTypeSynthetic, SBValue, GetTypeSynthetic);
+
+ lldb::SBTypeSynthetic synthetic;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ if (value_sp->UpdateValueIfNeeded(true)) {
+ lldb::SyntheticChildrenSP children_sp = value_sp->GetSyntheticChildren();
+
+ if (children_sp && children_sp->IsScripted()) {
+ ScriptedSyntheticChildrenSP synth_sp =
+ std::static_pointer_cast<ScriptedSyntheticChildren>(children_sp);
+ synthetic.SetSP(synth_sp);
+ }
+ }
+ }
+ return LLDB_RECORD_RESULT(synthetic);
+}
+
+lldb::SBValue SBValue::CreateChildAtOffset(const char *name, uint32_t offset,
+ SBType type) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, CreateChildAtOffset,
+ (const char *, uint32_t, lldb::SBType), name, offset,
+ type);
+
+ lldb::SBValue sb_value;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ lldb::ValueObjectSP new_value_sp;
+ if (value_sp) {
+ TypeImplSP type_sp(type.GetSP());
+ if (type.IsValid()) {
+ sb_value.SetSP(value_sp->GetSyntheticChildAtOffset(
+ offset, type_sp->GetCompilerType(false), true),
+ GetPreferDynamicValue(), GetPreferSyntheticValue(), name);
+ }
+ }
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+lldb::SBValue SBValue::Cast(SBType type) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, Cast, (lldb::SBType), type);
+
+ lldb::SBValue sb_value;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ TypeImplSP type_sp(type.GetSP());
+ if (value_sp && type_sp)
+ sb_value.SetSP(value_sp->Cast(type_sp->GetCompilerType(false)),
+ GetPreferDynamicValue(), GetPreferSyntheticValue());
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+lldb::SBValue SBValue::CreateValueFromExpression(const char *name,
+ const char *expression) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, CreateValueFromExpression,
+ (const char *, const char *), name, expression);
+
+ SBExpressionOptions options;
+ options.ref().SetKeepInMemory(true);
+ return LLDB_RECORD_RESULT(
+ CreateValueFromExpression(name, expression, options));
+}
+
+lldb::SBValue SBValue::CreateValueFromExpression(const char *name,
+ const char *expression,
+ SBExpressionOptions &options) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, CreateValueFromExpression,
+ (const char *, const char *, lldb::SBExpressionOptions &),
+ name, expression, options);
+
+ lldb::SBValue sb_value;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ lldb::ValueObjectSP new_value_sp;
+ if (value_sp) {
+ ExecutionContext exe_ctx(value_sp->GetExecutionContextRef());
+ new_value_sp = ValueObject::CreateValueObjectFromExpression(
+ name, expression, exe_ctx, options.ref());
+ if (new_value_sp)
+ new_value_sp->SetName(ConstString(name));
+ }
+ sb_value.SetSP(new_value_sp);
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+lldb::SBValue SBValue::CreateValueFromAddress(const char *name,
+ lldb::addr_t address,
+ SBType sb_type) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, CreateValueFromAddress,
+ (const char *, lldb::addr_t, lldb::SBType), name, address,
+ sb_type);
+
+ lldb::SBValue sb_value;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ lldb::ValueObjectSP new_value_sp;
+ lldb::TypeImplSP type_impl_sp(sb_type.GetSP());
+ if (value_sp && type_impl_sp) {
+ CompilerType ast_type(type_impl_sp->GetCompilerType(true));
+ ExecutionContext exe_ctx(value_sp->GetExecutionContextRef());
+ new_value_sp = ValueObject::CreateValueObjectFromAddress(name, address,
+ exe_ctx, ast_type);
+ }
+ sb_value.SetSP(new_value_sp);
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+lldb::SBValue SBValue::CreateValueFromData(const char *name, SBData data,
+ SBType sb_type) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, CreateValueFromData,
+ (const char *, lldb::SBData, lldb::SBType), name, data,
+ sb_type);
+
+ lldb::SBValue sb_value;
+ lldb::ValueObjectSP new_value_sp;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ lldb::TypeImplSP type_impl_sp(sb_type.GetSP());
+ if (value_sp && type_impl_sp) {
+ ExecutionContext exe_ctx(value_sp->GetExecutionContextRef());
+ new_value_sp = ValueObject::CreateValueObjectFromData(
+ name, **data, exe_ctx, type_impl_sp->GetCompilerType(true));
+ new_value_sp->SetAddressTypeOfChildren(eAddressTypeLoad);
+ }
+ sb_value.SetSP(new_value_sp);
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+SBValue SBValue::GetChildAtIndex(uint32_t idx) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, GetChildAtIndex, (uint32_t), idx);
+
+ const bool can_create_synthetic = false;
+ lldb::DynamicValueType use_dynamic = eNoDynamicValues;
+ TargetSP target_sp;
+ if (m_opaque_sp)
+ target_sp = m_opaque_sp->GetTargetSP();
+
+ if (target_sp)
+ use_dynamic = target_sp->GetPreferDynamicValue();
+
+ return LLDB_RECORD_RESULT(
+ GetChildAtIndex(idx, use_dynamic, can_create_synthetic));
+}
+
+SBValue SBValue::GetChildAtIndex(uint32_t idx,
+ lldb::DynamicValueType use_dynamic,
+ bool can_create_synthetic) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, GetChildAtIndex,
+ (uint32_t, lldb::DynamicValueType, bool), idx, use_dynamic,
+ can_create_synthetic);
+
+ lldb::ValueObjectSP child_sp;
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ const bool can_create = true;
+ child_sp = value_sp->GetChildAtIndex(idx, can_create);
+ if (can_create_synthetic && !child_sp) {
+ child_sp = value_sp->GetSyntheticArrayMember(idx, can_create);
+ }
+ }
+
+ SBValue sb_value;
+ sb_value.SetSP(child_sp, use_dynamic, GetPreferSyntheticValue());
+
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+uint32_t SBValue::GetIndexOfChildWithName(const char *name) {
+ LLDB_RECORD_METHOD(uint32_t, SBValue, GetIndexOfChildWithName, (const char *),
+ name);
+
+ uint32_t idx = UINT32_MAX;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ idx = value_sp->GetIndexOfChildWithName(ConstString(name));
+ }
+ return idx;
+}
+
+SBValue SBValue::GetChildMemberWithName(const char *name) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, GetChildMemberWithName,
+ (const char *), name);
+
+ lldb::DynamicValueType use_dynamic_value = eNoDynamicValues;
+ TargetSP target_sp;
+ if (m_opaque_sp)
+ target_sp = m_opaque_sp->GetTargetSP();
+
+ if (target_sp)
+ use_dynamic_value = target_sp->GetPreferDynamicValue();
+ return LLDB_RECORD_RESULT(GetChildMemberWithName(name, use_dynamic_value));
+}
+
+SBValue
+SBValue::GetChildMemberWithName(const char *name,
+ lldb::DynamicValueType use_dynamic_value) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, GetChildMemberWithName,
+ (const char *, lldb::DynamicValueType), name,
+ use_dynamic_value);
+
+ lldb::ValueObjectSP child_sp;
+ const ConstString str_name(name);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ child_sp = value_sp->GetChildMemberWithName(str_name, true);
+ }
+
+ SBValue sb_value;
+ sb_value.SetSP(child_sp, use_dynamic_value, GetPreferSyntheticValue());
+
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+lldb::SBValue SBValue::GetDynamicValue(lldb::DynamicValueType use_dynamic) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, GetDynamicValue,
+ (lldb::DynamicValueType), use_dynamic);
+
+ SBValue value_sb;
+ if (IsValid()) {
+ ValueImplSP proxy_sp(new ValueImpl(m_opaque_sp->GetRootSP(), use_dynamic,
+ m_opaque_sp->GetUseSynthetic()));
+ value_sb.SetSP(proxy_sp);
+ }
+ return LLDB_RECORD_RESULT(value_sb);
+}
+
+lldb::SBValue SBValue::GetStaticValue() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBValue, SBValue, GetStaticValue);
+
+ SBValue value_sb;
+ if (IsValid()) {
+ ValueImplSP proxy_sp(new ValueImpl(m_opaque_sp->GetRootSP(),
+ eNoDynamicValues,
+ m_opaque_sp->GetUseSynthetic()));
+ value_sb.SetSP(proxy_sp);
+ }
+ return LLDB_RECORD_RESULT(value_sb);
+}
+
+lldb::SBValue SBValue::GetNonSyntheticValue() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBValue, SBValue, GetNonSyntheticValue);
+
+ SBValue value_sb;
+ if (IsValid()) {
+ ValueImplSP proxy_sp(new ValueImpl(m_opaque_sp->GetRootSP(),
+ m_opaque_sp->GetUseDynamic(), false));
+ value_sb.SetSP(proxy_sp);
+ }
+ return LLDB_RECORD_RESULT(value_sb);
+}
+
+lldb::DynamicValueType SBValue::GetPreferDynamicValue() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::DynamicValueType, SBValue,
+ GetPreferDynamicValue);
+
+ if (!IsValid())
+ return eNoDynamicValues;
+ return m_opaque_sp->GetUseDynamic();
+}
+
+void SBValue::SetPreferDynamicValue(lldb::DynamicValueType use_dynamic) {
+ LLDB_RECORD_METHOD(void, SBValue, SetPreferDynamicValue,
+ (lldb::DynamicValueType), use_dynamic);
+
+ if (IsValid())
+ return m_opaque_sp->SetUseDynamic(use_dynamic);
+}
+
+bool SBValue::GetPreferSyntheticValue() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBValue, GetPreferSyntheticValue);
+
+ if (!IsValid())
+ return false;
+ return m_opaque_sp->GetUseSynthetic();
+}
+
+void SBValue::SetPreferSyntheticValue(bool use_synthetic) {
+ LLDB_RECORD_METHOD(void, SBValue, SetPreferSyntheticValue, (bool),
+ use_synthetic);
+
+ if (IsValid())
+ return m_opaque_sp->SetUseSynthetic(use_synthetic);
+}
+
+bool SBValue::IsDynamic() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBValue, IsDynamic);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ return value_sp->IsDynamic();
+ return false;
+}
+
+bool SBValue::IsSynthetic() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBValue, IsSynthetic);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ return value_sp->IsSynthetic();
+ return false;
+}
+
+bool SBValue::IsSyntheticChildrenGenerated() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBValue, IsSyntheticChildrenGenerated);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ return value_sp->IsSyntheticChildrenGenerated();
+ return false;
+}
+
+void SBValue::SetSyntheticChildrenGenerated(bool is) {
+ LLDB_RECORD_METHOD(void, SBValue, SetSyntheticChildrenGenerated, (bool), is);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ return value_sp->SetSyntheticChildrenGenerated(is);
+}
+
+lldb::SBValue SBValue::GetValueForExpressionPath(const char *expr_path) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, GetValueForExpressionPath,
+ (const char *), expr_path);
+
+ lldb::ValueObjectSP child_sp;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ // using default values for all the fancy options, just do it if you can
+ child_sp = value_sp->GetValueForExpressionPath(expr_path);
+ }
+
+ SBValue sb_value;
+ sb_value.SetSP(child_sp, GetPreferDynamicValue(), GetPreferSyntheticValue());
+
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+int64_t SBValue::GetValueAsSigned(SBError &error, int64_t fail_value) {
+ LLDB_RECORD_METHOD(int64_t, SBValue, GetValueAsSigned,
+ (lldb::SBError &, int64_t), error, fail_value);
+
+ error.Clear();
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ bool success = true;
+ uint64_t ret_val = fail_value;
+ ret_val = value_sp->GetValueAsSigned(fail_value, &success);
+ if (!success)
+ error.SetErrorString("could not resolve value");
+ return ret_val;
+ } else
+ error.SetErrorStringWithFormat("could not get SBValue: %s",
+ locker.GetError().AsCString());
+
+ return fail_value;
+}
+
+uint64_t SBValue::GetValueAsUnsigned(SBError &error, uint64_t fail_value) {
+ LLDB_RECORD_METHOD(uint64_t, SBValue, GetValueAsUnsigned,
+ (lldb::SBError &, uint64_t), error, fail_value);
+
+ error.Clear();
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ bool success = true;
+ uint64_t ret_val = fail_value;
+ ret_val = value_sp->GetValueAsUnsigned(fail_value, &success);
+ if (!success)
+ error.SetErrorString("could not resolve value");
+ return ret_val;
+ } else
+ error.SetErrorStringWithFormat("could not get SBValue: %s",
+ locker.GetError().AsCString());
+
+ return fail_value;
+}
+
+int64_t SBValue::GetValueAsSigned(int64_t fail_value) {
+ LLDB_RECORD_METHOD(int64_t, SBValue, GetValueAsSigned, (int64_t), fail_value);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ return value_sp->GetValueAsSigned(fail_value);
+ }
+ return fail_value;
+}
+
+uint64_t SBValue::GetValueAsUnsigned(uint64_t fail_value) {
+ LLDB_RECORD_METHOD(uint64_t, SBValue, GetValueAsUnsigned, (uint64_t),
+ fail_value);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ return value_sp->GetValueAsUnsigned(fail_value);
+ }
+ return fail_value;
+}
+
+bool SBValue::MightHaveChildren() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBValue, MightHaveChildren);
+
+ bool has_children = false;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ has_children = value_sp->MightHaveChildren();
+
+ return has_children;
+}
+
+bool SBValue::IsRuntimeSupportValue() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBValue, IsRuntimeSupportValue);
+
+ bool is_support = false;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ is_support = value_sp->IsRuntimeSupportValue();
+
+ return is_support;
+}
+
+uint32_t SBValue::GetNumChildren() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBValue, GetNumChildren);
+
+ return GetNumChildren(UINT32_MAX);
+}
+
+uint32_t SBValue::GetNumChildren(uint32_t max) {
+ LLDB_RECORD_METHOD(uint32_t, SBValue, GetNumChildren, (uint32_t), max);
+
+ uint32_t num_children = 0;
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ num_children = value_sp->GetNumChildren(max);
+
+ return num_children;
+}
+
+SBValue SBValue::Dereference() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBValue, SBValue, Dereference);
+
+ SBValue sb_value;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ Status error;
+ sb_value = value_sp->Dereference(error);
+ }
+
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+// Deprecated - please use GetType().IsPointerType() instead.
+bool SBValue::TypeIsPointerType() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBValue, TypeIsPointerType);
+
+ return GetType().IsPointerType();
+}
+
+void *SBValue::GetOpaqueType() {
+ LLDB_RECORD_METHOD_NO_ARGS(void *, SBValue, GetOpaqueType);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ return value_sp->GetCompilerType().GetOpaqueQualType();
+ return nullptr;
+}
+
+lldb::SBTarget SBValue::GetTarget() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBTarget, SBValue, GetTarget);
+
+ SBTarget sb_target;
+ TargetSP target_sp;
+ if (m_opaque_sp) {
+ target_sp = m_opaque_sp->GetTargetSP();
+ sb_target.SetSP(target_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_target);
+}
+
+lldb::SBProcess SBValue::GetProcess() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBProcess, SBValue, GetProcess);
+
+ SBProcess sb_process;
+ ProcessSP process_sp;
+ if (m_opaque_sp) {
+ process_sp = m_opaque_sp->GetProcessSP();
+ sb_process.SetSP(process_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_process);
+}
+
+lldb::SBThread SBValue::GetThread() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBThread, SBValue, GetThread);
+
+ SBThread sb_thread;
+ ThreadSP thread_sp;
+ if (m_opaque_sp) {
+ thread_sp = m_opaque_sp->GetThreadSP();
+ sb_thread.SetThread(thread_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_thread);
+}
+
+lldb::SBFrame SBValue::GetFrame() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFrame, SBValue, GetFrame);
+
+ SBFrame sb_frame;
+ StackFrameSP frame_sp;
+ if (m_opaque_sp) {
+ frame_sp = m_opaque_sp->GetFrameSP();
+ sb_frame.SetFrameSP(frame_sp);
+ }
+
+ return LLDB_RECORD_RESULT(sb_frame);
+}
+
+lldb::ValueObjectSP SBValue::GetSP(ValueLocker &locker) const {
+ if (!m_opaque_sp || !m_opaque_sp->IsValid()) {
+ locker.GetError().SetErrorString("No value");
+ return ValueObjectSP();
+ }
+ return locker.GetLockedSP(*m_opaque_sp.get());
+}
+
+lldb::ValueObjectSP SBValue::GetSP() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::ValueObjectSP, SBValue, GetSP);
+
+ ValueLocker locker;
+ return LLDB_RECORD_RESULT(GetSP(locker));
+}
+
+void SBValue::SetSP(ValueImplSP impl_sp) { m_opaque_sp = impl_sp; }
+
+void SBValue::SetSP(const lldb::ValueObjectSP &sp) {
+ if (sp) {
+ lldb::TargetSP target_sp(sp->GetTargetSP());
+ if (target_sp) {
+ lldb::DynamicValueType use_dynamic = target_sp->GetPreferDynamicValue();
+ bool use_synthetic =
+ target_sp->TargetProperties::GetEnableSyntheticValue();
+ m_opaque_sp = ValueImplSP(new ValueImpl(sp, use_dynamic, use_synthetic));
+ } else
+ m_opaque_sp = ValueImplSP(new ValueImpl(sp, eNoDynamicValues, true));
+ } else
+ m_opaque_sp = ValueImplSP(new ValueImpl(sp, eNoDynamicValues, false));
+}
+
+void SBValue::SetSP(const lldb::ValueObjectSP &sp,
+ lldb::DynamicValueType use_dynamic) {
+ if (sp) {
+ lldb::TargetSP target_sp(sp->GetTargetSP());
+ if (target_sp) {
+ bool use_synthetic =
+ target_sp->TargetProperties::GetEnableSyntheticValue();
+ SetSP(sp, use_dynamic, use_synthetic);
+ } else
+ SetSP(sp, use_dynamic, true);
+ } else
+ SetSP(sp, use_dynamic, false);
+}
+
+void SBValue::SetSP(const lldb::ValueObjectSP &sp, bool use_synthetic) {
+ if (sp) {
+ lldb::TargetSP target_sp(sp->GetTargetSP());
+ if (target_sp) {
+ lldb::DynamicValueType use_dynamic = target_sp->GetPreferDynamicValue();
+ SetSP(sp, use_dynamic, use_synthetic);
+ } else
+ SetSP(sp, eNoDynamicValues, use_synthetic);
+ } else
+ SetSP(sp, eNoDynamicValues, use_synthetic);
+}
+
+void SBValue::SetSP(const lldb::ValueObjectSP &sp,
+ lldb::DynamicValueType use_dynamic, bool use_synthetic) {
+ m_opaque_sp = ValueImplSP(new ValueImpl(sp, use_dynamic, use_synthetic));
+}
+
+void SBValue::SetSP(const lldb::ValueObjectSP &sp,
+ lldb::DynamicValueType use_dynamic, bool use_synthetic,
+ const char *name) {
+ m_opaque_sp =
+ ValueImplSP(new ValueImpl(sp, use_dynamic, use_synthetic, name));
+}
+
+bool SBValue::GetExpressionPath(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBValue, GetExpressionPath, (lldb::SBStream &),
+ description);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ value_sp->GetExpressionPath(description.ref(), false);
+ return true;
+ }
+ return false;
+}
+
+bool SBValue::GetExpressionPath(SBStream &description,
+ bool qualify_cxx_base_classes) {
+ LLDB_RECORD_METHOD(bool, SBValue, GetExpressionPath, (lldb::SBStream &, bool),
+ description, qualify_cxx_base_classes);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ value_sp->GetExpressionPath(description.ref(), qualify_cxx_base_classes);
+ return true;
+ }
+ return false;
+}
+
+lldb::SBValue SBValue::EvaluateExpression(const char *expr) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBValue, SBValue, EvaluateExpression,
+ (const char *), expr);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (!value_sp)
+ return LLDB_RECORD_RESULT(SBValue());
+
+ lldb::TargetSP target_sp = value_sp->GetTargetSP();
+ if (!target_sp)
+ return LLDB_RECORD_RESULT(SBValue());
+
+ lldb::SBExpressionOptions options;
+ options.SetFetchDynamicValue(target_sp->GetPreferDynamicValue());
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+
+ return LLDB_RECORD_RESULT(EvaluateExpression(expr, options, nullptr));
+}
+
+lldb::SBValue
+SBValue::EvaluateExpression(const char *expr,
+ const SBExpressionOptions &options) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBValue, SBValue, EvaluateExpression,
+ (const char *, const lldb::SBExpressionOptions &),
+ expr, options);
+
+ return LLDB_RECORD_RESULT(EvaluateExpression(expr, options, nullptr));
+}
+
+lldb::SBValue SBValue::EvaluateExpression(const char *expr,
+ const SBExpressionOptions &options,
+ const char *name) const {
+ LLDB_RECORD_METHOD_CONST(
+ lldb::SBValue, SBValue, EvaluateExpression,
+ (const char *, const lldb::SBExpressionOptions &, const char *), expr,
+ options, name);
+
+
+ if (!expr || expr[0] == '\0') {
+ return LLDB_RECORD_RESULT(SBValue());
+ }
+
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (!value_sp) {
+ return LLDB_RECORD_RESULT(SBValue());
+ }
+
+ lldb::TargetSP target_sp = value_sp->GetTargetSP();
+ if (!target_sp) {
+ return LLDB_RECORD_RESULT(SBValue());
+ }
+
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ ExecutionContext exe_ctx(target_sp.get());
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (!frame) {
+ return LLDB_RECORD_RESULT(SBValue());
+ }
+
+ ValueObjectSP res_val_sp;
+ target_sp->EvaluateExpression(expr, frame, res_val_sp, options.ref(), nullptr,
+ value_sp.get());
+
+ if (name)
+ res_val_sp->SetName(ConstString(name));
+
+ SBValue result;
+ result.SetSP(res_val_sp, options.GetFetchDynamicValue());
+ return LLDB_RECORD_RESULT(result);
+}
+
+bool SBValue::GetDescription(SBStream &description) {
+ LLDB_RECORD_METHOD(bool, SBValue, GetDescription, (lldb::SBStream &),
+ description);
+
+ Stream &strm = description.ref();
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ value_sp->Dump(strm);
+ else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+lldb::Format SBValue::GetFormat() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::Format, SBValue, GetFormat);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ return value_sp->GetFormat();
+ return eFormatDefault;
+}
+
+void SBValue::SetFormat(lldb::Format format) {
+ LLDB_RECORD_METHOD(void, SBValue, SetFormat, (lldb::Format), format);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp)
+ value_sp->SetFormat(format);
+}
+
+lldb::SBValue SBValue::AddressOf() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBValue, SBValue, AddressOf);
+
+ SBValue sb_value;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ Status error;
+ sb_value.SetSP(value_sp->AddressOf(error), GetPreferDynamicValue(),
+ GetPreferSyntheticValue());
+ }
+
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+lldb::addr_t SBValue::GetLoadAddress() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBValue, GetLoadAddress);
+
+ lldb::addr_t value = LLDB_INVALID_ADDRESS;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ TargetSP target_sp(value_sp->GetTargetSP());
+ if (target_sp) {
+ const bool scalar_is_load_address = true;
+ AddressType addr_type;
+ value = value_sp->GetAddressOf(scalar_is_load_address, &addr_type);
+ if (addr_type == eAddressTypeFile) {
+ ModuleSP module_sp(value_sp->GetModule());
+ if (!module_sp)
+ value = LLDB_INVALID_ADDRESS;
+ else {
+ Address addr;
+ module_sp->ResolveFileAddress(value, addr);
+ value = addr.GetLoadAddress(target_sp.get());
+ }
+ } else if (addr_type == eAddressTypeHost ||
+ addr_type == eAddressTypeInvalid)
+ value = LLDB_INVALID_ADDRESS;
+ }
+ }
+
+ return value;
+}
+
+lldb::SBAddress SBValue::GetAddress() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBAddress, SBValue, GetAddress);
+
+ Address addr;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ TargetSP target_sp(value_sp->GetTargetSP());
+ if (target_sp) {
+ lldb::addr_t value = LLDB_INVALID_ADDRESS;
+ const bool scalar_is_load_address = true;
+ AddressType addr_type;
+ value = value_sp->GetAddressOf(scalar_is_load_address, &addr_type);
+ if (addr_type == eAddressTypeFile) {
+ ModuleSP module_sp(value_sp->GetModule());
+ if (module_sp)
+ module_sp->ResolveFileAddress(value, addr);
+ } else if (addr_type == eAddressTypeLoad) {
+ // no need to check the return value on this.. if it can actually do
+ // the resolve addr will be in the form (section,offset), otherwise it
+ // will simply be returned as (NULL, value)
+ addr.SetLoadAddress(value, target_sp.get());
+ }
+ }
+ }
+
+ return LLDB_RECORD_RESULT(SBAddress(new Address(addr)));
+}
+
+lldb::SBData SBValue::GetPointeeData(uint32_t item_idx, uint32_t item_count) {
+ LLDB_RECORD_METHOD(lldb::SBData, SBValue, GetPointeeData,
+ (uint32_t, uint32_t), item_idx, item_count);
+
+ lldb::SBData sb_data;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ TargetSP target_sp(value_sp->GetTargetSP());
+ if (target_sp) {
+ DataExtractorSP data_sp(new DataExtractor());
+ value_sp->GetPointeeData(*data_sp, item_idx, item_count);
+ if (data_sp->GetByteSize() > 0)
+ *sb_data = data_sp;
+ }
+ }
+
+ return LLDB_RECORD_RESULT(sb_data);
+}
+
+lldb::SBData SBValue::GetData() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBData, SBValue, GetData);
+
+ lldb::SBData sb_data;
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ if (value_sp) {
+ DataExtractorSP data_sp(new DataExtractor());
+ Status error;
+ value_sp->GetData(*data_sp, error);
+ if (error.Success())
+ *sb_data = data_sp;
+ }
+
+ return LLDB_RECORD_RESULT(sb_data);
+}
+
+bool SBValue::SetData(lldb::SBData &data, SBError &error) {
+ LLDB_RECORD_METHOD(bool, SBValue, SetData, (lldb::SBData &, lldb::SBError &),
+ data, error);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ bool ret = true;
+
+ if (value_sp) {
+ DataExtractor *data_extractor = data.get();
+
+ if (!data_extractor) {
+ error.SetErrorString("No data to set");
+ ret = false;
+ } else {
+ Status set_error;
+
+ value_sp->SetData(*data_extractor, set_error);
+
+ if (!set_error.Success()) {
+ error.SetErrorStringWithFormat("Couldn't set data: %s",
+ set_error.AsCString());
+ ret = false;
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "Couldn't set data: could not get SBValue: %s",
+ locker.GetError().AsCString());
+ ret = false;
+ }
+
+ return ret;
+}
+
+lldb::SBDeclaration SBValue::GetDeclaration() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBDeclaration, SBValue, GetDeclaration);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ SBDeclaration decl_sb;
+ if (value_sp) {
+ Declaration decl;
+ if (value_sp->GetDeclaration(decl))
+ decl_sb.SetDeclaration(decl);
+ }
+ return LLDB_RECORD_RESULT(decl_sb);
+}
+
+lldb::SBWatchpoint SBValue::Watch(bool resolve_location, bool read, bool write,
+ SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBWatchpoint, SBValue, Watch,
+ (bool, bool, bool, lldb::SBError &), resolve_location,
+ read, write, error);
+
+ SBWatchpoint sb_watchpoint;
+
+ // If the SBValue is not valid, there's no point in even trying to watch it.
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ TargetSP target_sp(GetTarget().GetSP());
+ if (value_sp && target_sp) {
+ // Read and Write cannot both be false.
+ if (!read && !write)
+ return LLDB_RECORD_RESULT(sb_watchpoint);
+
+ // If the value is not in scope, don't try and watch and invalid value
+ if (!IsInScope())
+ return LLDB_RECORD_RESULT(sb_watchpoint);
+
+ addr_t addr = GetLoadAddress();
+ if (addr == LLDB_INVALID_ADDRESS)
+ return LLDB_RECORD_RESULT(sb_watchpoint);
+ size_t byte_size = GetByteSize();
+ if (byte_size == 0)
+ return LLDB_RECORD_RESULT(sb_watchpoint);
+
+ uint32_t watch_type = 0;
+ if (read)
+ watch_type |= LLDB_WATCH_TYPE_READ;
+ if (write)
+ watch_type |= LLDB_WATCH_TYPE_WRITE;
+
+ Status rc;
+ CompilerType type(value_sp->GetCompilerType());
+ WatchpointSP watchpoint_sp =
+ target_sp->CreateWatchpoint(addr, byte_size, &type, watch_type, rc);
+ error.SetError(rc);
+
+ if (watchpoint_sp) {
+ sb_watchpoint.SetSP(watchpoint_sp);
+ Declaration decl;
+ if (value_sp->GetDeclaration(decl)) {
+ if (decl.GetFile()) {
+ StreamString ss;
+ // True to show fullpath for declaration file.
+ decl.DumpStopContext(&ss, true);
+ watchpoint_sp->SetDeclInfo(ss.GetString());
+ }
+ }
+ }
+ } else if (target_sp) {
+ error.SetErrorStringWithFormat("could not get SBValue: %s",
+ locker.GetError().AsCString());
+ } else {
+ error.SetErrorString("could not set watchpoint, a target is required");
+ }
+
+ return LLDB_RECORD_RESULT(sb_watchpoint);
+}
+
+// FIXME: Remove this method impl (as well as the decl in .h) once it is no
+// longer needed.
+// Backward compatibility fix in the interim.
+lldb::SBWatchpoint SBValue::Watch(bool resolve_location, bool read,
+ bool write) {
+ LLDB_RECORD_METHOD(lldb::SBWatchpoint, SBValue, Watch, (bool, bool, bool),
+ resolve_location, read, write);
+
+ SBError error;
+ return LLDB_RECORD_RESULT(Watch(resolve_location, read, write, error));
+}
+
+lldb::SBWatchpoint SBValue::WatchPointee(bool resolve_location, bool read,
+ bool write, SBError &error) {
+ LLDB_RECORD_METHOD(lldb::SBWatchpoint, SBValue, WatchPointee,
+ (bool, bool, bool, lldb::SBError &), resolve_location,
+ read, write, error);
+
+ SBWatchpoint sb_watchpoint;
+ if (IsInScope() && GetType().IsPointerType())
+ sb_watchpoint = Dereference().Watch(resolve_location, read, write, error);
+ return LLDB_RECORD_RESULT(sb_watchpoint);
+}
+
+lldb::SBValue SBValue::Persist() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBValue, SBValue, Persist);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+ SBValue persisted_sb;
+ if (value_sp) {
+ persisted_sb.SetSP(value_sp->Persist());
+ }
+ return LLDB_RECORD_RESULT(persisted_sb);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBValue>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBValue, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBValue, (const lldb::ValueObjectSP &));
+ LLDB_REGISTER_CONSTRUCTOR(SBValue, (const lldb::SBValue &));
+ LLDB_REGISTER_METHOD(lldb::SBValue &,
+ SBValue, operator=,(const lldb::SBValue &));
+ LLDB_REGISTER_METHOD(bool, SBValue, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBValue, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBValue, Clear, ());
+ LLDB_REGISTER_METHOD(lldb::SBError, SBValue, GetError, ());
+ LLDB_REGISTER_METHOD(lldb::user_id_t, SBValue, GetID, ());
+ LLDB_REGISTER_METHOD(const char *, SBValue, GetName, ());
+ LLDB_REGISTER_METHOD(const char *, SBValue, GetTypeName, ());
+ LLDB_REGISTER_METHOD(const char *, SBValue, GetDisplayTypeName, ());
+ LLDB_REGISTER_METHOD(size_t, SBValue, GetByteSize, ());
+ LLDB_REGISTER_METHOD(bool, SBValue, IsInScope, ());
+ LLDB_REGISTER_METHOD(const char *, SBValue, GetValue, ());
+ LLDB_REGISTER_METHOD(lldb::ValueType, SBValue, GetValueType, ());
+ LLDB_REGISTER_METHOD(const char *, SBValue, GetObjectDescription, ());
+ LLDB_REGISTER_METHOD(const char *, SBValue, GetTypeValidatorResult, ());
+ LLDB_REGISTER_METHOD(lldb::SBType, SBValue, GetType, ());
+ LLDB_REGISTER_METHOD(bool, SBValue, GetValueDidChange, ());
+ LLDB_REGISTER_METHOD(const char *, SBValue, GetSummary, ());
+ LLDB_REGISTER_METHOD(const char *, SBValue, GetSummary,
+ (lldb::SBStream &, lldb::SBTypeSummaryOptions &));
+ LLDB_REGISTER_METHOD(const char *, SBValue, GetLocation, ());
+ LLDB_REGISTER_METHOD(bool, SBValue, SetValueFromCString, (const char *));
+ LLDB_REGISTER_METHOD(bool, SBValue, SetValueFromCString,
+ (const char *, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBTypeFormat, SBValue, GetTypeFormat, ());
+ LLDB_REGISTER_METHOD(lldb::SBTypeSummary, SBValue, GetTypeSummary, ());
+ LLDB_REGISTER_METHOD(lldb::SBTypeFilter, SBValue, GetTypeFilter, ());
+ LLDB_REGISTER_METHOD(lldb::SBTypeSynthetic, SBValue, GetTypeSynthetic, ());
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, CreateChildAtOffset,
+ (const char *, uint32_t, lldb::SBType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, Cast, (lldb::SBType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, CreateValueFromExpression,
+ (const char *, const char *));
+ LLDB_REGISTER_METHOD(
+ lldb::SBValue, SBValue, CreateValueFromExpression,
+ (const char *, const char *, lldb::SBExpressionOptions &));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, CreateValueFromAddress,
+ (const char *, lldb::addr_t, lldb::SBType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, CreateValueFromData,
+ (const char *, lldb::SBData, lldb::SBType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, GetChildAtIndex, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, GetChildAtIndex,
+ (uint32_t, lldb::DynamicValueType, bool));
+ LLDB_REGISTER_METHOD(uint32_t, SBValue, GetIndexOfChildWithName,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, GetChildMemberWithName,
+ (const char *));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, GetChildMemberWithName,
+ (const char *, lldb::DynamicValueType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, GetDynamicValue,
+ (lldb::DynamicValueType));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, GetStaticValue, ());
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, GetNonSyntheticValue, ());
+ LLDB_REGISTER_METHOD(lldb::DynamicValueType, SBValue, GetPreferDynamicValue,
+ ());
+ LLDB_REGISTER_METHOD(void, SBValue, SetPreferDynamicValue,
+ (lldb::DynamicValueType));
+ LLDB_REGISTER_METHOD(bool, SBValue, GetPreferSyntheticValue, ());
+ LLDB_REGISTER_METHOD(void, SBValue, SetPreferSyntheticValue, (bool));
+ LLDB_REGISTER_METHOD(bool, SBValue, IsDynamic, ());
+ LLDB_REGISTER_METHOD(bool, SBValue, IsSynthetic, ());
+ LLDB_REGISTER_METHOD(bool, SBValue, IsSyntheticChildrenGenerated, ());
+ LLDB_REGISTER_METHOD(void, SBValue, SetSyntheticChildrenGenerated, (bool));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, GetValueForExpressionPath,
+ (const char *));
+ LLDB_REGISTER_METHOD(int64_t, SBValue, GetValueAsSigned,
+ (lldb::SBError &, int64_t));
+ LLDB_REGISTER_METHOD(uint64_t, SBValue, GetValueAsUnsigned,
+ (lldb::SBError &, uint64_t));
+ LLDB_REGISTER_METHOD(int64_t, SBValue, GetValueAsSigned, (int64_t));
+ LLDB_REGISTER_METHOD(uint64_t, SBValue, GetValueAsUnsigned, (uint64_t));
+ LLDB_REGISTER_METHOD(bool, SBValue, MightHaveChildren, ());
+ LLDB_REGISTER_METHOD(bool, SBValue, IsRuntimeSupportValue, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBValue, GetNumChildren, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBValue, GetNumChildren, (uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, Dereference, ());
+ LLDB_REGISTER_METHOD(bool, SBValue, TypeIsPointerType, ());
+ LLDB_REGISTER_METHOD(void *, SBValue, GetOpaqueType, ());
+ LLDB_REGISTER_METHOD(lldb::SBTarget, SBValue, GetTarget, ());
+ LLDB_REGISTER_METHOD(lldb::SBProcess, SBValue, GetProcess, ());
+ LLDB_REGISTER_METHOD(lldb::SBThread, SBValue, GetThread, ());
+ LLDB_REGISTER_METHOD(lldb::SBFrame, SBValue, GetFrame, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::ValueObjectSP, SBValue, GetSP, ());
+ LLDB_REGISTER_METHOD(bool, SBValue, GetExpressionPath, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(bool, SBValue, GetExpressionPath,
+ (lldb::SBStream &, bool));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBValue, SBValue, EvaluateExpression,
+ (const char *));
+ LLDB_REGISTER_METHOD_CONST(
+ lldb::SBValue, SBValue, EvaluateExpression,
+ (const char *, const lldb::SBExpressionOptions &));
+ LLDB_REGISTER_METHOD_CONST(
+ lldb::SBValue, SBValue, EvaluateExpression,
+ (const char *, const lldb::SBExpressionOptions &, const char *));
+ LLDB_REGISTER_METHOD(bool, SBValue, GetDescription, (lldb::SBStream &));
+ LLDB_REGISTER_METHOD(lldb::Format, SBValue, GetFormat, ());
+ LLDB_REGISTER_METHOD(void, SBValue, SetFormat, (lldb::Format));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, AddressOf, ());
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBValue, GetLoadAddress, ());
+ LLDB_REGISTER_METHOD(lldb::SBAddress, SBValue, GetAddress, ());
+ LLDB_REGISTER_METHOD(lldb::SBData, SBValue, GetPointeeData,
+ (uint32_t, uint32_t));
+ LLDB_REGISTER_METHOD(lldb::SBData, SBValue, GetData, ());
+ LLDB_REGISTER_METHOD(bool, SBValue, SetData,
+ (lldb::SBData &, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBDeclaration, SBValue, GetDeclaration, ());
+ LLDB_REGISTER_METHOD(lldb::SBWatchpoint, SBValue, Watch,
+ (bool, bool, bool, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBWatchpoint, SBValue, Watch,
+ (bool, bool, bool));
+ LLDB_REGISTER_METHOD(lldb::SBWatchpoint, SBValue, WatchPointee,
+ (bool, bool, bool, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, Persist, ());
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBValueList.cpp b/contrib/llvm-project/lldb/source/API/SBValueList.cpp
new file mode 100644
index 000000000000..7e909df260d7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBValueList.cpp
@@ -0,0 +1,231 @@
+//===-- SBValueList.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBValueList.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBValue.h"
+#include "lldb/Core/ValueObjectList.h"
+
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+class ValueListImpl {
+public:
+ ValueListImpl() : m_values() {}
+
+ ValueListImpl(const ValueListImpl &rhs) : m_values(rhs.m_values) {}
+
+ ValueListImpl &operator=(const ValueListImpl &rhs) {
+ if (this == &rhs)
+ return *this;
+ m_values = rhs.m_values;
+ return *this;
+ }
+
+ uint32_t GetSize() { return m_values.size(); }
+
+ void Append(const lldb::SBValue &sb_value) { m_values.push_back(sb_value); }
+
+ void Append(const ValueListImpl &list) {
+ for (auto val : list.m_values)
+ Append(val);
+ }
+
+ lldb::SBValue GetValueAtIndex(uint32_t index) {
+ if (index >= GetSize())
+ return lldb::SBValue();
+ return m_values[index];
+ }
+
+ lldb::SBValue FindValueByUID(lldb::user_id_t uid) {
+ for (auto val : m_values) {
+ if (val.IsValid() && val.GetID() == uid)
+ return val;
+ }
+ return lldb::SBValue();
+ }
+
+ lldb::SBValue GetFirstValueByName(const char *name) const {
+ if (name) {
+ for (auto val : m_values) {
+ if (val.IsValid() && val.GetName() && strcmp(name, val.GetName()) == 0)
+ return val;
+ }
+ }
+ return lldb::SBValue();
+ }
+
+private:
+ std::vector<lldb::SBValue> m_values;
+};
+
+SBValueList::SBValueList() : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBValueList);
+}
+
+SBValueList::SBValueList(const SBValueList &rhs) : m_opaque_up() {
+ LLDB_RECORD_CONSTRUCTOR(SBValueList, (const lldb::SBValueList &), rhs);
+
+ if (rhs.IsValid())
+ m_opaque_up.reset(new ValueListImpl(*rhs));
+}
+
+SBValueList::SBValueList(const ValueListImpl *lldb_object_ptr) : m_opaque_up() {
+ if (lldb_object_ptr)
+ m_opaque_up.reset(new ValueListImpl(*lldb_object_ptr));
+}
+
+SBValueList::~SBValueList() {}
+
+bool SBValueList::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBValueList, IsValid);
+ return this->operator bool();
+}
+SBValueList::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBValueList, operator bool);
+
+ return (m_opaque_up != nullptr);
+}
+
+void SBValueList::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBValueList, Clear);
+
+ m_opaque_up.reset();
+}
+
+const SBValueList &SBValueList::operator=(const SBValueList &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBValueList &,
+ SBValueList, operator=,(const lldb::SBValueList &), rhs);
+
+ if (this != &rhs) {
+ if (rhs.IsValid())
+ m_opaque_up.reset(new ValueListImpl(*rhs));
+ else
+ m_opaque_up.reset();
+ }
+ return LLDB_RECORD_RESULT(*this);
+}
+
+ValueListImpl *SBValueList::operator->() { return m_opaque_up.get(); }
+
+ValueListImpl &SBValueList::operator*() { return *m_opaque_up; }
+
+const ValueListImpl *SBValueList::operator->() const {
+ return m_opaque_up.get();
+}
+
+const ValueListImpl &SBValueList::operator*() const { return *m_opaque_up; }
+
+void SBValueList::Append(const SBValue &val_obj) {
+ LLDB_RECORD_METHOD(void, SBValueList, Append, (const lldb::SBValue &),
+ val_obj);
+
+ CreateIfNeeded();
+ m_opaque_up->Append(val_obj);
+}
+
+void SBValueList::Append(lldb::ValueObjectSP &val_obj_sp) {
+ if (val_obj_sp) {
+ CreateIfNeeded();
+ m_opaque_up->Append(SBValue(val_obj_sp));
+ }
+}
+
+void SBValueList::Append(const lldb::SBValueList &value_list) {
+ LLDB_RECORD_METHOD(void, SBValueList, Append, (const lldb::SBValueList &),
+ value_list);
+
+ if (value_list.IsValid()) {
+ CreateIfNeeded();
+ m_opaque_up->Append(*value_list);
+ }
+}
+
+SBValue SBValueList::GetValueAtIndex(uint32_t idx) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBValue, SBValueList, GetValueAtIndex,
+ (uint32_t), idx);
+
+
+ SBValue sb_value;
+ if (m_opaque_up)
+ sb_value = m_opaque_up->GetValueAtIndex(idx);
+
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+uint32_t SBValueList::GetSize() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBValueList, GetSize);
+
+ uint32_t size = 0;
+ if (m_opaque_up)
+ size = m_opaque_up->GetSize();
+
+ return size;
+}
+
+void SBValueList::CreateIfNeeded() {
+ if (m_opaque_up == nullptr)
+ m_opaque_up.reset(new ValueListImpl());
+}
+
+SBValue SBValueList::FindValueObjectByUID(lldb::user_id_t uid) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValueList, FindValueObjectByUID,
+ (lldb::user_id_t), uid);
+
+ SBValue sb_value;
+ if (m_opaque_up)
+ sb_value = m_opaque_up->FindValueByUID(uid);
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+SBValue SBValueList::GetFirstValueByName(const char *name) const {
+ LLDB_RECORD_METHOD_CONST(lldb::SBValue, SBValueList, GetFirstValueByName,
+ (const char *), name);
+
+ SBValue sb_value;
+ if (m_opaque_up)
+ sb_value = m_opaque_up->GetFirstValueByName(name);
+ return LLDB_RECORD_RESULT(sb_value);
+}
+
+void *SBValueList::opaque_ptr() { return m_opaque_up.get(); }
+
+ValueListImpl &SBValueList::ref() {
+ CreateIfNeeded();
+ return *m_opaque_up;
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBValueList>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBValueList, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBValueList, (const lldb::SBValueList &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBValueList, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBValueList, operator bool, ());
+ LLDB_REGISTER_METHOD(void, SBValueList, Clear, ());
+ LLDB_REGISTER_METHOD(const lldb::SBValueList &,
+ SBValueList, operator=,(const lldb::SBValueList &));
+ LLDB_REGISTER_METHOD(void, SBValueList, Append, (const lldb::SBValue &));
+ LLDB_REGISTER_METHOD(void, SBValueList, Append,
+ (const lldb::SBValueList &));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBValue, SBValueList, GetValueAtIndex,
+ (uint32_t));
+ LLDB_REGISTER_METHOD_CONST(uint32_t, SBValueList, GetSize, ());
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValueList, FindValueObjectByUID,
+ (lldb::user_id_t));
+ LLDB_REGISTER_METHOD_CONST(lldb::SBValue, SBValueList, GetFirstValueByName,
+ (const char *));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBVariablesOptions.cpp b/contrib/llvm-project/lldb/source/API/SBVariablesOptions.cpp
new file mode 100644
index 000000000000..bf0197cd960b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBVariablesOptions.cpp
@@ -0,0 +1,276 @@
+//===-- SBVariablesOptions.cpp --------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBVariablesOptions.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/Target/Target.h"
+
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class VariablesOptionsImpl {
+public:
+ VariablesOptionsImpl()
+ : m_include_arguments(false), m_include_locals(false),
+ m_include_statics(false), m_in_scope_only(false),
+ m_include_runtime_support_values(false),
+ m_include_recognized_arguments(eLazyBoolCalculate),
+ m_use_dynamic(lldb::eNoDynamicValues) {}
+
+ VariablesOptionsImpl(const VariablesOptionsImpl &) = default;
+
+ ~VariablesOptionsImpl() = default;
+
+ VariablesOptionsImpl &operator=(const VariablesOptionsImpl &) = default;
+
+ bool GetIncludeArguments() const { return m_include_arguments; }
+
+ void SetIncludeArguments(bool b) { m_include_arguments = b; }
+
+ bool GetIncludeRecognizedArguments(const lldb::TargetSP &target_sp) const {
+ if (m_include_recognized_arguments != eLazyBoolCalculate)
+ return m_include_recognized_arguments;
+ return target_sp ? target_sp->GetDisplayRecognizedArguments() : false;
+ }
+
+ void SetIncludeRecognizedArguments(bool b) {
+ m_include_recognized_arguments = b ? eLazyBoolYes : eLazyBoolNo;
+ }
+
+ bool GetIncludeLocals() const { return m_include_locals; }
+
+ void SetIncludeLocals(bool b) { m_include_locals = b; }
+
+ bool GetIncludeStatics() const { return m_include_statics; }
+
+ void SetIncludeStatics(bool b) { m_include_statics = b; }
+
+ bool GetInScopeOnly() const { return m_in_scope_only; }
+
+ void SetInScopeOnly(bool b) { m_in_scope_only = b; }
+
+ bool GetIncludeRuntimeSupportValues() const {
+ return m_include_runtime_support_values;
+ }
+
+ void SetIncludeRuntimeSupportValues(bool b) {
+ m_include_runtime_support_values = b;
+ }
+
+ lldb::DynamicValueType GetUseDynamic() const { return m_use_dynamic; }
+
+ void SetUseDynamic(lldb::DynamicValueType d) { m_use_dynamic = d; }
+
+private:
+ bool m_include_arguments : 1;
+ bool m_include_locals : 1;
+ bool m_include_statics : 1;
+ bool m_in_scope_only : 1;
+ bool m_include_runtime_support_values : 1;
+ LazyBool m_include_recognized_arguments; // can be overridden with a setting
+ lldb::DynamicValueType m_use_dynamic;
+};
+
+SBVariablesOptions::SBVariablesOptions()
+ : m_opaque_up(new VariablesOptionsImpl()) {
+ LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBVariablesOptions);
+}
+
+SBVariablesOptions::SBVariablesOptions(const SBVariablesOptions &options)
+ : m_opaque_up(new VariablesOptionsImpl(options.ref())) {
+ LLDB_RECORD_CONSTRUCTOR(SBVariablesOptions,
+ (const lldb::SBVariablesOptions &), options);
+}
+
+SBVariablesOptions &SBVariablesOptions::
+operator=(const SBVariablesOptions &options) {
+ LLDB_RECORD_METHOD(
+ lldb::SBVariablesOptions &,
+ SBVariablesOptions, operator=,(const lldb::SBVariablesOptions &),
+ options);
+
+ m_opaque_up.reset(new VariablesOptionsImpl(options.ref()));
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBVariablesOptions::~SBVariablesOptions() = default;
+
+bool SBVariablesOptions::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBVariablesOptions, IsValid);
+ return this->operator bool();
+}
+SBVariablesOptions::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBVariablesOptions, operator bool);
+
+ return m_opaque_up != nullptr;
+}
+
+bool SBVariablesOptions::GetIncludeArguments() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBVariablesOptions,
+ GetIncludeArguments);
+
+ return m_opaque_up->GetIncludeArguments();
+}
+
+void SBVariablesOptions::SetIncludeArguments(bool arguments) {
+ LLDB_RECORD_METHOD(void, SBVariablesOptions, SetIncludeArguments, (bool),
+ arguments);
+
+ m_opaque_up->SetIncludeArguments(arguments);
+}
+
+bool SBVariablesOptions::GetIncludeRecognizedArguments(
+ const lldb::SBTarget &target) const {
+ LLDB_RECORD_METHOD_CONST(bool, SBVariablesOptions,
+ GetIncludeRecognizedArguments,
+ (const lldb::SBTarget &), target);
+
+ return m_opaque_up->GetIncludeRecognizedArguments(target.GetSP());
+}
+
+void SBVariablesOptions::SetIncludeRecognizedArguments(bool arguments) {
+ LLDB_RECORD_METHOD(void, SBVariablesOptions, SetIncludeRecognizedArguments,
+ (bool), arguments);
+
+ m_opaque_up->SetIncludeRecognizedArguments(arguments);
+}
+
+bool SBVariablesOptions::GetIncludeLocals() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBVariablesOptions, GetIncludeLocals);
+
+ return m_opaque_up->GetIncludeLocals();
+}
+
+void SBVariablesOptions::SetIncludeLocals(bool locals) {
+ LLDB_RECORD_METHOD(void, SBVariablesOptions, SetIncludeLocals, (bool),
+ locals);
+
+ m_opaque_up->SetIncludeLocals(locals);
+}
+
+bool SBVariablesOptions::GetIncludeStatics() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBVariablesOptions, GetIncludeStatics);
+
+ return m_opaque_up->GetIncludeStatics();
+}
+
+void SBVariablesOptions::SetIncludeStatics(bool statics) {
+ LLDB_RECORD_METHOD(void, SBVariablesOptions, SetIncludeStatics, (bool),
+ statics);
+
+ m_opaque_up->SetIncludeStatics(statics);
+}
+
+bool SBVariablesOptions::GetInScopeOnly() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBVariablesOptions, GetInScopeOnly);
+
+ return m_opaque_up->GetInScopeOnly();
+}
+
+void SBVariablesOptions::SetInScopeOnly(bool in_scope_only) {
+ LLDB_RECORD_METHOD(void, SBVariablesOptions, SetInScopeOnly, (bool),
+ in_scope_only);
+
+ m_opaque_up->SetInScopeOnly(in_scope_only);
+}
+
+bool SBVariablesOptions::GetIncludeRuntimeSupportValues() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBVariablesOptions,
+ GetIncludeRuntimeSupportValues);
+
+ return m_opaque_up->GetIncludeRuntimeSupportValues();
+}
+
+void SBVariablesOptions::SetIncludeRuntimeSupportValues(
+ bool runtime_support_values) {
+ LLDB_RECORD_METHOD(void, SBVariablesOptions, SetIncludeRuntimeSupportValues,
+ (bool), runtime_support_values);
+
+ m_opaque_up->SetIncludeRuntimeSupportValues(runtime_support_values);
+}
+
+lldb::DynamicValueType SBVariablesOptions::GetUseDynamic() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::DynamicValueType, SBVariablesOptions,
+ GetUseDynamic);
+
+ return m_opaque_up->GetUseDynamic();
+}
+
+void SBVariablesOptions::SetUseDynamic(lldb::DynamicValueType dynamic) {
+ LLDB_RECORD_METHOD(void, SBVariablesOptions, SetUseDynamic,
+ (lldb::DynamicValueType), dynamic);
+
+ m_opaque_up->SetUseDynamic(dynamic);
+}
+
+VariablesOptionsImpl *SBVariablesOptions::operator->() {
+ return m_opaque_up.operator->();
+}
+
+const VariablesOptionsImpl *SBVariablesOptions::operator->() const {
+ return m_opaque_up.operator->();
+}
+
+VariablesOptionsImpl *SBVariablesOptions::get() { return m_opaque_up.get(); }
+
+VariablesOptionsImpl &SBVariablesOptions::ref() { return *m_opaque_up; }
+
+const VariablesOptionsImpl &SBVariablesOptions::ref() const {
+ return *m_opaque_up;
+}
+
+SBVariablesOptions::SBVariablesOptions(VariablesOptionsImpl *lldb_object_ptr)
+ : m_opaque_up(std::move(lldb_object_ptr)) {}
+
+void SBVariablesOptions::SetOptions(VariablesOptionsImpl *lldb_object_ptr) {
+ m_opaque_up.reset(std::move(lldb_object_ptr));
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBVariablesOptions>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBVariablesOptions, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBVariablesOptions,
+ (const lldb::SBVariablesOptions &));
+ LLDB_REGISTER_METHOD(
+ lldb::SBVariablesOptions &,
+ SBVariablesOptions, operator=,(const lldb::SBVariablesOptions &));
+ LLDB_REGISTER_METHOD_CONST(bool, SBVariablesOptions, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBVariablesOptions, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBVariablesOptions, GetIncludeArguments,
+ ());
+ LLDB_REGISTER_METHOD(void, SBVariablesOptions, SetIncludeArguments, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBVariablesOptions,
+ GetIncludeRecognizedArguments,
+ (const lldb::SBTarget &));
+ LLDB_REGISTER_METHOD(void, SBVariablesOptions,
+ SetIncludeRecognizedArguments, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBVariablesOptions, GetIncludeLocals, ());
+ LLDB_REGISTER_METHOD(void, SBVariablesOptions, SetIncludeLocals, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBVariablesOptions, GetIncludeStatics, ());
+ LLDB_REGISTER_METHOD(void, SBVariablesOptions, SetIncludeStatics, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBVariablesOptions, GetInScopeOnly, ());
+ LLDB_REGISTER_METHOD(void, SBVariablesOptions, SetInScopeOnly, (bool));
+ LLDB_REGISTER_METHOD_CONST(bool, SBVariablesOptions,
+ GetIncludeRuntimeSupportValues, ());
+ LLDB_REGISTER_METHOD(void, SBVariablesOptions,
+ SetIncludeRuntimeSupportValues, (bool));
+ LLDB_REGISTER_METHOD_CONST(lldb::DynamicValueType, SBVariablesOptions,
+ GetUseDynamic, ());
+ LLDB_REGISTER_METHOD(void, SBVariablesOptions, SetUseDynamic,
+ (lldb::DynamicValueType));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SBWatchpoint.cpp b/contrib/llvm-project/lldb/source/API/SBWatchpoint.cpp
new file mode 100644
index 000000000000..d0a36b71e5c1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SBWatchpoint.cpp
@@ -0,0 +1,352 @@
+//===-- SBWatchpoint.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBWatchpoint.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBDefines.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBStream.h"
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Breakpoint/WatchpointList.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBWatchpoint::SBWatchpoint() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBWatchpoint); }
+
+SBWatchpoint::SBWatchpoint(const lldb::WatchpointSP &wp_sp)
+ : m_opaque_wp(wp_sp) {
+ LLDB_RECORD_CONSTRUCTOR(SBWatchpoint, (const lldb::WatchpointSP &), wp_sp);
+}
+
+SBWatchpoint::SBWatchpoint(const SBWatchpoint &rhs)
+ : m_opaque_wp(rhs.m_opaque_wp) {
+ LLDB_RECORD_CONSTRUCTOR(SBWatchpoint, (const lldb::SBWatchpoint &), rhs);
+}
+
+const SBWatchpoint &SBWatchpoint::operator=(const SBWatchpoint &rhs) {
+ LLDB_RECORD_METHOD(const lldb::SBWatchpoint &,
+ SBWatchpoint, operator=,(const lldb::SBWatchpoint &), rhs);
+
+ m_opaque_wp = rhs.m_opaque_wp;
+ return LLDB_RECORD_RESULT(*this);
+}
+
+SBWatchpoint::~SBWatchpoint() {}
+
+watch_id_t SBWatchpoint::GetID() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::watch_id_t, SBWatchpoint, GetID);
+
+
+ watch_id_t watch_id = LLDB_INVALID_WATCH_ID;
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp)
+ watch_id = watchpoint_sp->GetID();
+
+ return watch_id;
+}
+
+bool SBWatchpoint::IsValid() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBWatchpoint, IsValid);
+ return this->operator bool();
+}
+SBWatchpoint::operator bool() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBWatchpoint, operator bool);
+
+ return bool(m_opaque_wp.lock());
+}
+
+bool SBWatchpoint::operator==(const SBWatchpoint &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBWatchpoint, operator==,(const SBWatchpoint &), rhs);
+
+ return GetSP() == rhs.GetSP();
+}
+
+bool SBWatchpoint::operator!=(const SBWatchpoint &rhs) const {
+ LLDB_RECORD_METHOD_CONST(
+ bool, SBWatchpoint, operator!=,(const SBWatchpoint &), rhs);
+
+ return !(*this == rhs);
+}
+
+SBError SBWatchpoint::GetError() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBWatchpoint, GetError);
+
+ SBError sb_error;
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ sb_error.SetError(watchpoint_sp->GetError());
+ }
+ return LLDB_RECORD_RESULT(sb_error);
+}
+
+int32_t SBWatchpoint::GetHardwareIndex() {
+ LLDB_RECORD_METHOD_NO_ARGS(int32_t, SBWatchpoint, GetHardwareIndex);
+
+ int32_t hw_index = -1;
+
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ watchpoint_sp->GetTarget().GetAPIMutex());
+ hw_index = watchpoint_sp->GetHardwareIndex();
+ }
+
+ return hw_index;
+}
+
+addr_t SBWatchpoint::GetWatchAddress() {
+ LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBWatchpoint, GetWatchAddress);
+
+ addr_t ret_addr = LLDB_INVALID_ADDRESS;
+
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ watchpoint_sp->GetTarget().GetAPIMutex());
+ ret_addr = watchpoint_sp->GetLoadAddress();
+ }
+
+ return ret_addr;
+}
+
+size_t SBWatchpoint::GetWatchSize() {
+ LLDB_RECORD_METHOD_NO_ARGS(size_t, SBWatchpoint, GetWatchSize);
+
+ size_t watch_size = 0;
+
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ watchpoint_sp->GetTarget().GetAPIMutex());
+ watch_size = watchpoint_sp->GetByteSize();
+ }
+
+ return watch_size;
+}
+
+void SBWatchpoint::SetEnabled(bool enabled) {
+ LLDB_RECORD_METHOD(void, SBWatchpoint, SetEnabled, (bool), enabled);
+
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ Target &target = watchpoint_sp->GetTarget();
+ std::lock_guard<std::recursive_mutex> guard(target.GetAPIMutex());
+ ProcessSP process_sp = target.GetProcessSP();
+ const bool notify = true;
+ if (process_sp) {
+ if (enabled)
+ process_sp->EnableWatchpoint(watchpoint_sp.get(), notify);
+ else
+ process_sp->DisableWatchpoint(watchpoint_sp.get(), notify);
+ } else {
+ watchpoint_sp->SetEnabled(enabled, notify);
+ }
+ }
+}
+
+bool SBWatchpoint::IsEnabled() {
+ LLDB_RECORD_METHOD_NO_ARGS(bool, SBWatchpoint, IsEnabled);
+
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ watchpoint_sp->GetTarget().GetAPIMutex());
+ return watchpoint_sp->IsEnabled();
+ } else
+ return false;
+}
+
+uint32_t SBWatchpoint::GetHitCount() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBWatchpoint, GetHitCount);
+
+ uint32_t count = 0;
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ watchpoint_sp->GetTarget().GetAPIMutex());
+ count = watchpoint_sp->GetHitCount();
+ }
+
+ return count;
+}
+
+uint32_t SBWatchpoint::GetIgnoreCount() {
+ LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBWatchpoint, GetIgnoreCount);
+
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ watchpoint_sp->GetTarget().GetAPIMutex());
+ return watchpoint_sp->GetIgnoreCount();
+ } else
+ return 0;
+}
+
+void SBWatchpoint::SetIgnoreCount(uint32_t n) {
+ LLDB_RECORD_METHOD(void, SBWatchpoint, SetIgnoreCount, (uint32_t), n);
+
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ watchpoint_sp->GetTarget().GetAPIMutex());
+ watchpoint_sp->SetIgnoreCount(n);
+ }
+}
+
+const char *SBWatchpoint::GetCondition() {
+ LLDB_RECORD_METHOD_NO_ARGS(const char *, SBWatchpoint, GetCondition);
+
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ watchpoint_sp->GetTarget().GetAPIMutex());
+ return watchpoint_sp->GetConditionText();
+ }
+ return nullptr;
+}
+
+void SBWatchpoint::SetCondition(const char *condition) {
+ LLDB_RECORD_METHOD(void, SBWatchpoint, SetCondition, (const char *),
+ condition);
+
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ watchpoint_sp->GetTarget().GetAPIMutex());
+ watchpoint_sp->SetCondition(condition);
+ }
+}
+
+bool SBWatchpoint::GetDescription(SBStream &description,
+ DescriptionLevel level) {
+ LLDB_RECORD_METHOD(bool, SBWatchpoint, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel), description,
+ level);
+
+ Stream &strm = description.ref();
+
+ lldb::WatchpointSP watchpoint_sp(GetSP());
+ if (watchpoint_sp) {
+ std::lock_guard<std::recursive_mutex> guard(
+ watchpoint_sp->GetTarget().GetAPIMutex());
+ watchpoint_sp->GetDescription(&strm, level);
+ strm.EOL();
+ } else
+ strm.PutCString("No value");
+
+ return true;
+}
+
+void SBWatchpoint::Clear() {
+ LLDB_RECORD_METHOD_NO_ARGS(void, SBWatchpoint, Clear);
+
+ m_opaque_wp.reset();
+}
+
+lldb::WatchpointSP SBWatchpoint::GetSP() const {
+ LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::WatchpointSP, SBWatchpoint, GetSP);
+
+ return LLDB_RECORD_RESULT(m_opaque_wp.lock());
+}
+
+void SBWatchpoint::SetSP(const lldb::WatchpointSP &sp) {
+ LLDB_RECORD_METHOD(void, SBWatchpoint, SetSP, (const lldb::WatchpointSP &),
+ sp);
+
+ m_opaque_wp = sp;
+}
+
+bool SBWatchpoint::EventIsWatchpointEvent(const lldb::SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(bool, SBWatchpoint, EventIsWatchpointEvent,
+ (const lldb::SBEvent &), event);
+
+ return Watchpoint::WatchpointEventData::GetEventDataFromEvent(event.get()) !=
+ nullptr;
+}
+
+WatchpointEventType
+SBWatchpoint::GetWatchpointEventTypeFromEvent(const SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(lldb::WatchpointEventType, SBWatchpoint,
+ GetWatchpointEventTypeFromEvent,
+ (const lldb::SBEvent &), event);
+
+ if (event.IsValid())
+ return Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent(
+ event.GetSP());
+ return eWatchpointEventTypeInvalidType;
+}
+
+SBWatchpoint SBWatchpoint::GetWatchpointFromEvent(const lldb::SBEvent &event) {
+ LLDB_RECORD_STATIC_METHOD(lldb::SBWatchpoint, SBWatchpoint,
+ GetWatchpointFromEvent, (const lldb::SBEvent &),
+ event);
+
+ SBWatchpoint sb_watchpoint;
+ if (event.IsValid())
+ sb_watchpoint =
+ Watchpoint::WatchpointEventData::GetWatchpointFromEvent(event.GetSP());
+ return LLDB_RECORD_RESULT(sb_watchpoint);
+}
+
+namespace lldb_private {
+namespace repro {
+
+template <>
+void RegisterMethods<SBWatchpoint>(Registry &R) {
+ LLDB_REGISTER_CONSTRUCTOR(SBWatchpoint, ());
+ LLDB_REGISTER_CONSTRUCTOR(SBWatchpoint, (const lldb::WatchpointSP &));
+ LLDB_REGISTER_CONSTRUCTOR(SBWatchpoint, (const lldb::SBWatchpoint &));
+ LLDB_REGISTER_METHOD(const lldb::SBWatchpoint &,
+ SBWatchpoint, operator=,(const lldb::SBWatchpoint &));
+ LLDB_REGISTER_METHOD(lldb::watch_id_t, SBWatchpoint, GetID, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBWatchpoint, IsValid, ());
+ LLDB_REGISTER_METHOD_CONST(bool, SBWatchpoint, operator bool, ());
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBWatchpoint, operator==,(const lldb::SBWatchpoint &));
+ LLDB_REGISTER_METHOD_CONST(
+ bool, SBWatchpoint, operator!=,(const lldb::SBWatchpoint &));
+ LLDB_REGISTER_METHOD(lldb::SBError, SBWatchpoint, GetError, ());
+ LLDB_REGISTER_METHOD(int32_t, SBWatchpoint, GetHardwareIndex, ());
+ LLDB_REGISTER_METHOD(lldb::addr_t, SBWatchpoint, GetWatchAddress, ());
+ LLDB_REGISTER_METHOD(size_t, SBWatchpoint, GetWatchSize, ());
+ LLDB_REGISTER_METHOD(void, SBWatchpoint, SetEnabled, (bool));
+ LLDB_REGISTER_METHOD(bool, SBWatchpoint, IsEnabled, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBWatchpoint, GetHitCount, ());
+ LLDB_REGISTER_METHOD(uint32_t, SBWatchpoint, GetIgnoreCount, ());
+ LLDB_REGISTER_METHOD(void, SBWatchpoint, SetIgnoreCount, (uint32_t));
+ LLDB_REGISTER_METHOD(const char *, SBWatchpoint, GetCondition, ());
+ LLDB_REGISTER_METHOD(void, SBWatchpoint, SetCondition, (const char *));
+ LLDB_REGISTER_METHOD(bool, SBWatchpoint, GetDescription,
+ (lldb::SBStream &, lldb::DescriptionLevel));
+ LLDB_REGISTER_METHOD(void, SBWatchpoint, Clear, ());
+ LLDB_REGISTER_METHOD_CONST(lldb::WatchpointSP, SBWatchpoint, GetSP, ());
+ LLDB_REGISTER_METHOD(void, SBWatchpoint, SetSP,
+ (const lldb::WatchpointSP &));
+ LLDB_REGISTER_STATIC_METHOD(bool, SBWatchpoint, EventIsWatchpointEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::WatchpointEventType, SBWatchpoint,
+ GetWatchpointEventTypeFromEvent,
+ (const lldb::SBEvent &));
+ LLDB_REGISTER_STATIC_METHOD(lldb::SBWatchpoint, SBWatchpoint,
+ GetWatchpointFromEvent,
+ (const lldb::SBEvent &));
+}
+
+}
+}
diff --git a/contrib/llvm-project/lldb/source/API/SystemInitializerFull.cpp b/contrib/llvm-project/lldb/source/API/SystemInitializerFull.cpp
new file mode 100644
index 000000000000..e78122efb2dd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SystemInitializerFull.cpp
@@ -0,0 +1,480 @@
+//===-- SystemInitializerFull.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SystemInitializerFull.h"
+
+#include "lldb/API/SBCommandInterpreter.h"
+
+#if !defined(LLDB_DISABLE_PYTHON)
+#include "Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h"
+#endif
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Initialization/SystemInitializerCommon.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Utility/Timer.h"
+
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h"
+#include "Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h"
+#include "Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/ABI/SysV-arm/ABISysV_arm.h"
+#include "Plugins/ABI/SysV-arm64/ABISysV_arm64.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/ABI/SysV-i386/ABISysV_i386.h"
+#include "Plugins/ABI/SysV-mips/ABISysV_mips.h"
+#include "Plugins/ABI/SysV-mips64/ABISysV_mips64.h"
+#include "Plugins/ABI/SysV-ppc/ABISysV_ppc.h"
+#include "Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/ABI/SysV-s390x/ABISysV_s390x.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/Architecture/Arm/ArchitectureArm.h"
+#include "Plugins/Architecture/Mips/ArchitectureMips.h"
+#include "Plugins/Architecture/PPC64/ArchitecturePPC64.h"
+#include "Plugins/Disassembler/llvm/DisassemblerLLVMC.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h"
+#include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h"
+#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/Instruction/ARM/EmulateInstructionARM.h"
+#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h"
+#include "Plugins/Instruction/MIPS/EmulateInstructionMIPS.h"
+#include "Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h"
+#include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h"
+#include "Plugins/InstrumentationRuntime/ASan/ASanRuntime.h"
+#include "Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/InstrumentationRuntime/TSan/TSanRuntime.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h"
+#include "Plugins/JITLoader/GDB/JITLoaderGDB.h"
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+#include "Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h"
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h"
+#include "Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h"
+#include "Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/ObjectContainer/Universal-Mach-O/ObjectContainerUniversalMachO.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h"
+#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h"
+#include "Plugins/OperatingSystem/Python/OperatingSystemPython.h"
+#include "Plugins/Platform/Android/PlatformAndroid.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/Platform/FreeBSD/PlatformFreeBSD.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/Platform/Linux/PlatformLinux.h"
+#include "Plugins/Platform/MacOSX/PlatformMacOSX.h"
+#include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h"
+#include "Plugins/Platform/NetBSD/PlatformNetBSD.h"
+#include "Plugins/Platform/OpenBSD/PlatformOpenBSD.h"
+#include "Plugins/Platform/Windows/PlatformWindows.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h"
+#include "Plugins/Process/elf-core/ProcessElfCore.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/Process/mach-core/ProcessMachCore.h"
+#include "Plugins/Process/minidump/ProcessMinidump.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h"
+#include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/SymbolFile/PDB/SymbolFilePDB.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h"
+#include "Plugins/SymbolVendor/ELF/SymbolVendorELF.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h"
+#include "Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h"
+
+#if defined(__APPLE__)
+#include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h"
+#include "Plugins/Platform/MacOSX/PlatformAppleTVSimulator.h"
+#include "Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.h"
+#include "Plugins/Platform/MacOSX/PlatformDarwinKernel.h"
+#include "Plugins/Platform/MacOSX/PlatformRemoteAppleBridge.h"
+#include "Plugins/Platform/MacOSX/PlatformRemoteAppleTV.h"
+#include "Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.h"
+#include "Plugins/Platform/MacOSX/PlatformiOSSimulator.h"
+#include "Plugins/Process/MacOSX-Kernel/ProcessKDP.h"
+#include "Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h"
+#endif
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h"
+#endif // LLDB_ENABLE_ALL
+
+#if defined(__FreeBSD__)
+#include "Plugins/Process/FreeBSD/ProcessFreeBSD.h"
+#endif
+
+#if defined(_WIN32)
+#include "Plugins/Process/Windows/Common/ProcessWindows.h"
+#include "lldb/Host/windows/windows.h"
+#endif
+
+#include "llvm/Support/TargetSelect.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#include "llvm/ExecutionEngine/MCJIT.h"
+#pragma clang diagnostic pop
+
+#include <string>
+
+using namespace lldb_private;
+
+SystemInitializerFull::SystemInitializerFull() {}
+
+SystemInitializerFull::~SystemInitializerFull() {}
+
+llvm::Error SystemInitializerFull::Initialize() {
+ if (auto e = SystemInitializerCommon::Initialize())
+ return e;
+
+ breakpad::ObjectFileBreakpad::Initialize();
+ ObjectFileELF::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ ObjectFileMachO::Initialize();
+ ObjectFilePECOFF::Initialize();
+#endif // LLDB_ENABLE_ALL
+
+ ObjectContainerBSDArchive::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ ObjectContainerUniversalMachO::Initialize();
+#endif // LLDB_ENABLE_ALL
+
+ ScriptInterpreterNone::Initialize();
+
+#ifndef LLDB_DISABLE_PYTHON
+ OperatingSystemPython::Initialize();
+#endif
+
+#if !defined(LLDB_DISABLE_PYTHON)
+ ScriptInterpreterPython::Initialize();
+#endif
+
+ platform_freebsd::PlatformFreeBSD::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ platform_linux::PlatformLinux::Initialize();
+ platform_netbsd::PlatformNetBSD::Initialize();
+ platform_openbsd::PlatformOpenBSD::Initialize();
+ PlatformWindows::Initialize();
+ platform_android::PlatformAndroid::Initialize();
+ PlatformRemoteiOS::Initialize();
+ PlatformMacOSX::Initialize();
+#endif // LLDB_ENABLE_ALL
+#if defined(__APPLE__)
+ PlatformiOSSimulator::Initialize();
+ PlatformDarwinKernel::Initialize();
+#endif
+
+ // Initialize LLVM and Clang
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllAsmPrinters();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllDisassemblers();
+
+ ClangASTContext::Initialize();
+
+#ifdef LLDB_ENABLE_ALL
+ ABIMacOSX_i386::Initialize();
+ ABIMacOSX_arm::Initialize();
+ ABIMacOSX_arm64::Initialize();
+#endif // LLDB_ENABLE_ALL
+ ABISysV_arm::Initialize();
+ ABISysV_arm64::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ ABISysV_hexagon::Initialize();
+#endif // LLDB_ENABLE_ALL
+ ABISysV_i386::Initialize();
+ ABISysV_x86_64::Initialize();
+ ABISysV_ppc::Initialize();
+ ABISysV_ppc64::Initialize();
+ ABISysV_mips::Initialize();
+ ABISysV_mips64::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ ABISysV_s390x::Initialize();
+ ABIWindows_x86_64::Initialize();
+#endif // LLDB_ENABLE_ALL
+
+ ArchitectureArm::Initialize();
+ ArchitectureMips::Initialize();
+ ArchitecturePPC64::Initialize();
+
+ DisassemblerLLVMC::Initialize();
+
+ JITLoaderGDB::Initialize();
+ ProcessElfCore::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ ProcessMachCore::Initialize();
+ minidump::ProcessMinidump::Initialize();
+#endif // LLDB_ENABLE_ALL
+ MemoryHistoryASan::Initialize();
+ AddressSanitizerRuntime::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ ThreadSanitizerRuntime::Initialize();
+#endif // LLDB_ENABLE_ALL
+ UndefinedBehaviorSanitizerRuntime::Initialize();
+ MainThreadCheckerRuntime::Initialize();
+
+ SymbolVendorELF::Initialize();
+ breakpad::SymbolFileBreakpad::Initialize();
+ SymbolFileDWARF::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ SymbolFilePDB::Initialize();
+#endif // LLDB_ENABLE_ALL
+ SymbolFileSymtab::Initialize();
+ UnwindAssemblyInstEmulation::Initialize();
+ UnwindAssembly_x86::Initialize();
+
+ EmulateInstructionARM::Initialize();
+ EmulateInstructionARM64::Initialize();
+ EmulateInstructionMIPS::Initialize();
+ EmulateInstructionMIPS64::Initialize();
+ EmulateInstructionPPC64::Initialize();
+
+ SymbolFileDWARFDebugMap::Initialize();
+ ItaniumABILanguageRuntime::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ AppleObjCRuntimeV2::Initialize();
+ AppleObjCRuntimeV1::Initialize();
+ SystemRuntimeMacOSX::Initialize();
+ RenderScriptRuntime::Initialize();
+#endif // LLDB_ENABLE_ALL
+
+ CPlusPlusLanguage::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ ObjCLanguage::Initialize();
+ ObjCPlusPlusLanguage::Initialize();
+#endif // LLDB_ENABLE_ALL
+
+#if defined(_WIN32)
+ ProcessWindows::Initialize();
+#endif
+#if defined(__FreeBSD__)
+ ProcessFreeBSD::Initialize();
+#endif
+#if defined(__APPLE__)
+ SymbolVendorMacOSX::Initialize();
+ ProcessKDP::Initialize();
+ PlatformAppleTVSimulator::Initialize();
+ PlatformAppleWatchSimulator::Initialize();
+ PlatformRemoteAppleTV::Initialize();
+ PlatformRemoteAppleWatch::Initialize();
+ PlatformRemoteAppleBridge::Initialize();
+ DynamicLoaderDarwinKernel::Initialize();
+#endif
+
+ // This plugin is valid on any host that talks to a Darwin remote. It
+ // shouldn't be limited to __APPLE__.
+#ifdef LLDB_ENABLE_ALL
+ StructuredDataDarwinLog::Initialize();
+#endif // LLDB_ENABLE_ALL
+
+ // Platform agnostic plugins
+ platform_gdb_server::PlatformRemoteGDBServer::Initialize();
+
+ process_gdb_remote::ProcessGDBRemote::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ DynamicLoaderMacOSXDYLD::Initialize();
+ DynamicLoaderMacOS::Initialize();
+#endif // LLDB_ENABLE_ALL
+ DynamicLoaderPOSIXDYLD::Initialize();
+ DynamicLoaderStatic::Initialize();
+#ifdef LLDB_ENABLE_ALL
+ DynamicLoaderWindowsDYLD::Initialize();
+#endif // LLDB_ENABLE_ALL
+
+ // Scan for any system or user LLDB plug-ins
+ PluginManager::Initialize();
+
+ // The process settings need to know about installed plug-ins, so the
+ // Settings must be initialized
+ // AFTER PluginManager::Initialize is called.
+
+ Debugger::SettingsInitialize();
+
+ return llvm::Error::success();
+}
+
+void SystemInitializerFull::Terminate() {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ Debugger::SettingsTerminate();
+
+ // Terminate and unload and loaded system or user LLDB plug-ins
+ PluginManager::Terminate();
+
+ ClangASTContext::Terminate();
+
+#ifdef LLDB_ENABLE_ALL
+ ArchitectureArm::Terminate();
+ ArchitectureMips::Terminate();
+ ArchitecturePPC64::Terminate();
+
+ ABIMacOSX_i386::Terminate();
+ ABIMacOSX_arm::Terminate();
+ ABIMacOSX_arm64::Terminate();
+#endif // LLDB_ENABLE_ALL
+ ABISysV_arm::Terminate();
+ ABISysV_arm64::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ ABISysV_hexagon::Terminate();
+#endif // LLDB_ENABLE_ALL
+ ABISysV_i386::Terminate();
+ ABISysV_x86_64::Terminate();
+ ABISysV_ppc::Terminate();
+ ABISysV_ppc64::Terminate();
+ ABISysV_mips::Terminate();
+ ABISysV_mips64::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ ABISysV_s390x::Terminate();
+ ABIWindows_x86_64::Terminate();
+#endif // LLDB_ENABLE_ALL
+ DisassemblerLLVMC::Terminate();
+
+ JITLoaderGDB::Terminate();
+ ProcessElfCore::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ ProcessMachCore::Terminate();
+ minidump::ProcessMinidump::Terminate();
+#endif // LLDB_ENABLE_ALL
+ MemoryHistoryASan::Terminate();
+ AddressSanitizerRuntime::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ ThreadSanitizerRuntime::Terminate();
+#endif // LLDB_ENABLE_ALL
+ UndefinedBehaviorSanitizerRuntime::Terminate();
+ MainThreadCheckerRuntime::Terminate();
+ SymbolVendorELF::Terminate();
+ breakpad::SymbolFileBreakpad::Terminate();
+ SymbolFileDWARF::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ SymbolFilePDB::Terminate();
+#endif // LLDB_ENABLE_ALL
+ SymbolFileSymtab::Terminate();
+ UnwindAssembly_x86::Terminate();
+ UnwindAssemblyInstEmulation::Terminate();
+
+ EmulateInstructionARM::Terminate();
+ EmulateInstructionARM64::Terminate();
+ EmulateInstructionMIPS::Terminate();
+ EmulateInstructionMIPS64::Terminate();
+ EmulateInstructionPPC64::Terminate();
+
+ SymbolFileDWARFDebugMap::Terminate();
+ ItaniumABILanguageRuntime::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ AppleObjCRuntimeV2::Terminate();
+ AppleObjCRuntimeV1::Terminate();
+ SystemRuntimeMacOSX::Terminate();
+ RenderScriptRuntime::Terminate();
+#endif // LLDB_ENABLE_ALL
+
+ CPlusPlusLanguage::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ ObjCLanguage::Terminate();
+ ObjCPlusPlusLanguage::Terminate();
+#endif // LLDB_ENABLE_ALL
+
+#if defined(__APPLE__)
+ DynamicLoaderDarwinKernel::Terminate();
+ ProcessKDP::Terminate();
+ SymbolVendorMacOSX::Terminate();
+ PlatformAppleTVSimulator::Terminate();
+ PlatformAppleWatchSimulator::Terminate();
+ PlatformRemoteAppleTV::Terminate();
+ PlatformRemoteAppleWatch::Terminate();
+ PlatformRemoteAppleBridge::Terminate();
+#endif
+
+#if defined(__FreeBSD__)
+ ProcessFreeBSD::Terminate();
+#endif
+ Debugger::SettingsTerminate();
+
+ platform_gdb_server::PlatformRemoteGDBServer::Terminate();
+ process_gdb_remote::ProcessGDBRemote::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ StructuredDataDarwinLog::Terminate();
+
+ DynamicLoaderMacOSXDYLD::Terminate();
+ DynamicLoaderMacOS::Terminate();
+#endif // LLDB_ENABLE_ALL
+ DynamicLoaderPOSIXDYLD::Terminate();
+ DynamicLoaderStatic::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ DynamicLoaderWindowsDYLD::Terminate();
+#endif // LLDB_ENABLE_ALL
+
+#ifndef LLDB_DISABLE_PYTHON
+ OperatingSystemPython::Terminate();
+#endif
+
+ platform_freebsd::PlatformFreeBSD::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ platform_linux::PlatformLinux::Terminate();
+ platform_netbsd::PlatformNetBSD::Terminate();
+ platform_openbsd::PlatformOpenBSD::Terminate();
+ PlatformWindows::Terminate();
+ platform_android::PlatformAndroid::Terminate();
+ PlatformMacOSX::Terminate();
+ PlatformRemoteiOS::Terminate();
+#endif // LLDB_ENABLE_ALL
+#if defined(__APPLE__)
+ PlatformiOSSimulator::Terminate();
+ PlatformDarwinKernel::Terminate();
+#endif
+
+ breakpad::ObjectFileBreakpad::Terminate();
+ ObjectFileELF::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ ObjectFileMachO::Terminate();
+ ObjectFilePECOFF::Terminate();
+#endif // LLDB_ENABLE_ALL
+
+ ObjectContainerBSDArchive::Terminate();
+#ifdef LLDB_ENABLE_ALL
+ ObjectContainerUniversalMachO::Terminate();
+#endif // LLDB_ENABLE_ALL
+
+ // Now shutdown the common parts, in reverse order.
+ SystemInitializerCommon::Terminate();
+}
diff --git a/contrib/llvm-project/lldb/source/API/SystemInitializerFull.h b/contrib/llvm-project/lldb/source/API/SystemInitializerFull.h
new file mode 100644
index 000000000000..cd88bae97858
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/SystemInitializerFull.h
@@ -0,0 +1,32 @@
+//===-- SystemInitializerFull.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_API_SYSTEM_INITIALIZER_FULL_H
+#define LLDB_API_SYSTEM_INITIALIZER_FULL_H
+
+#include "lldb/Initialization/SystemInitializerCommon.h"
+
+namespace lldb_private {
+/// Initializes lldb.
+///
+/// This class is responsible for initializing all of lldb system
+/// services needed to use the full LLDB application. This class is
+/// not intended to be used externally, but is instead used
+/// internally by SBDebugger to initialize the system.
+class SystemInitializerFull : public SystemInitializerCommon {
+public:
+ SystemInitializerFull();
+ ~SystemInitializerFull() override;
+
+ llvm::Error Initialize() override;
+ void Terminate() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_API_SYSTEM_INITIALIZER_FULL_H
diff --git a/contrib/llvm-project/lldb/source/API/Utils.h b/contrib/llvm-project/lldb/source/API/Utils.h
new file mode 100644
index 000000000000..b1975e5421dd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/Utils.h
@@ -0,0 +1,30 @@
+//===-- Utils.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_API_UTILS_H
+#define LLDB_API_UTILS_H
+
+#include "llvm/ADT/STLExtras.h"
+#include <memory>
+
+namespace lldb_private {
+
+template <typename T> std::unique_ptr<T> clone(const std::unique_ptr<T> &src) {
+ if (src)
+ return llvm::make_unique<T>(*src);
+ return nullptr;
+}
+
+template <typename T> std::shared_ptr<T> clone(const std::shared_ptr<T> &src) {
+ if (src)
+ return std::make_shared<T>(*src);
+ return nullptr;
+}
+
+} // namespace lldb_private
+#endif
diff --git a/contrib/llvm-project/lldb/source/API/liblldb-private.exports b/contrib/llvm-project/lldb/source/API/liblldb-private.exports
new file mode 100644
index 000000000000..9b3d86dfc892
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/liblldb-private.exports
@@ -0,0 +1,6 @@
+_ZN4lldb*
+_ZNK4lldb*
+_ZN12lldb_private*
+_ZNK12lldb_private*
+init_lld*
+PyInit__lldb*
diff --git a/contrib/llvm-project/lldb/source/API/liblldb.exports b/contrib/llvm-project/lldb/source/API/liblldb.exports
new file mode 100644
index 000000000000..3ceb562c7ed1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/liblldb.exports
@@ -0,0 +1,4 @@
+_ZN4lldb*
+_ZNK4lldb*
+init_lld*
+PyInit__lldb*
diff --git a/contrib/llvm-project/lldb/source/API/liblldb.xcode.exports b/contrib/llvm-project/lldb/source/API/liblldb.xcode.exports
new file mode 100644
index 000000000000..9c194fa6ff67
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/API/liblldb.xcode.exports
@@ -0,0 +1,3 @@
+__ZN4lldb*
+__ZNK4lldb*
+_init_lld*
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/Breakpoint.cpp b/contrib/llvm-project/lldb/source/Breakpoint/Breakpoint.cpp
new file mode 100644
index 000000000000..3c3841949b91
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/Breakpoint.cpp
@@ -0,0 +1,1102 @@
+//===-- Breakpoint.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Casting.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointLocationCollection.h"
+#include "lldb/Breakpoint/BreakpointPrecondition.h"
+#include "lldb/Breakpoint/BreakpointResolver.h"
+#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/SearchFilter.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+ConstString Breakpoint::GetEventIdentifier() {
+ static ConstString g_identifier("event-identifier.breakpoint.changed");
+ return g_identifier;
+}
+
+const char *Breakpoint::g_option_names[static_cast<uint32_t>(
+ Breakpoint::OptionNames::LastOptionName)]{"Names", "Hardware"};
+
+// Breakpoint constructor
+Breakpoint::Breakpoint(Target &target, SearchFilterSP &filter_sp,
+ BreakpointResolverSP &resolver_sp, bool hardware,
+ bool resolve_indirect_symbols)
+ : m_being_created(true), m_hardware(hardware), m_target(target),
+ m_filter_sp(filter_sp), m_resolver_sp(resolver_sp),
+ m_options_up(new BreakpointOptions(true)), m_locations(*this),
+ m_resolve_indirect_symbols(resolve_indirect_symbols), m_hit_count(0) {
+ m_being_created = false;
+}
+
+Breakpoint::Breakpoint(Target &new_target, Breakpoint &source_bp)
+ : m_being_created(true), m_hardware(source_bp.m_hardware),
+ m_target(new_target), m_name_list(source_bp.m_name_list),
+ m_options_up(new BreakpointOptions(*source_bp.m_options_up)),
+ m_locations(*this),
+ m_resolve_indirect_symbols(source_bp.m_resolve_indirect_symbols),
+ m_hit_count(0) {
+ // Now go through and copy the filter & resolver:
+ m_resolver_sp = source_bp.m_resolver_sp->CopyForBreakpoint(*this);
+ m_filter_sp = source_bp.m_filter_sp->CopyForBreakpoint(*this);
+}
+
+// Destructor
+Breakpoint::~Breakpoint() = default;
+
+// Serialization
+StructuredData::ObjectSP Breakpoint::SerializeToStructuredData() {
+ // Serialize the resolver:
+ StructuredData::DictionarySP breakpoint_dict_sp(
+ new StructuredData::Dictionary());
+ StructuredData::DictionarySP breakpoint_contents_sp(
+ new StructuredData::Dictionary());
+
+ if (!m_name_list.empty()) {
+ StructuredData::ArraySP names_array_sp(new StructuredData::Array());
+ for (auto name : m_name_list) {
+ names_array_sp->AddItem(
+ StructuredData::StringSP(new StructuredData::String(name)));
+ }
+ breakpoint_contents_sp->AddItem(Breakpoint::GetKey(OptionNames::Names),
+ names_array_sp);
+ }
+
+ breakpoint_contents_sp->AddBooleanItem(
+ Breakpoint::GetKey(OptionNames::Hardware), m_hardware);
+
+ StructuredData::ObjectSP resolver_dict_sp(
+ m_resolver_sp->SerializeToStructuredData());
+ if (!resolver_dict_sp)
+ return StructuredData::ObjectSP();
+
+ breakpoint_contents_sp->AddItem(BreakpointResolver::GetSerializationKey(),
+ resolver_dict_sp);
+
+ StructuredData::ObjectSP filter_dict_sp(
+ m_filter_sp->SerializeToStructuredData());
+ if (!filter_dict_sp)
+ return StructuredData::ObjectSP();
+
+ breakpoint_contents_sp->AddItem(SearchFilter::GetSerializationKey(),
+ filter_dict_sp);
+
+ StructuredData::ObjectSP options_dict_sp(
+ m_options_up->SerializeToStructuredData());
+ if (!options_dict_sp)
+ return StructuredData::ObjectSP();
+
+ breakpoint_contents_sp->AddItem(BreakpointOptions::GetSerializationKey(),
+ options_dict_sp);
+
+ breakpoint_dict_sp->AddItem(GetSerializationKey(), breakpoint_contents_sp);
+ return breakpoint_dict_sp;
+}
+
+lldb::BreakpointSP Breakpoint::CreateFromStructuredData(
+ Target &target, StructuredData::ObjectSP &object_data, Status &error) {
+ BreakpointSP result_sp;
+
+ StructuredData::Dictionary *breakpoint_dict = object_data->GetAsDictionary();
+
+ if (!breakpoint_dict || !breakpoint_dict->IsValid()) {
+ error.SetErrorString("Can't deserialize from an invalid data object.");
+ return result_sp;
+ }
+
+ StructuredData::Dictionary *resolver_dict;
+ bool success = breakpoint_dict->GetValueForKeyAsDictionary(
+ BreakpointResolver::GetSerializationKey(), resolver_dict);
+ if (!success) {
+ error.SetErrorStringWithFormat(
+ "Breakpoint data missing toplevel resolver key");
+ return result_sp;
+ }
+
+ Status create_error;
+ BreakpointResolverSP resolver_sp =
+ BreakpointResolver::CreateFromStructuredData(*resolver_dict,
+ create_error);
+ if (create_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Error creating breakpoint resolver from data: %s.",
+ create_error.AsCString());
+ return result_sp;
+ }
+
+ StructuredData::Dictionary *filter_dict;
+ success = breakpoint_dict->GetValueForKeyAsDictionary(
+ SearchFilter::GetSerializationKey(), filter_dict);
+ SearchFilterSP filter_sp;
+ if (!success)
+ filter_sp = std::make_shared<SearchFilterForUnconstrainedSearches>(
+ target.shared_from_this());
+ else {
+ filter_sp = SearchFilter::CreateFromStructuredData(target, *filter_dict,
+ create_error);
+ if (create_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Error creating breakpoint filter from data: %s.",
+ create_error.AsCString());
+ return result_sp;
+ }
+ }
+
+ std::unique_ptr<BreakpointOptions> options_up;
+ StructuredData::Dictionary *options_dict;
+ success = breakpoint_dict->GetValueForKeyAsDictionary(
+ BreakpointOptions::GetSerializationKey(), options_dict);
+ if (success) {
+ options_up = BreakpointOptions::CreateFromStructuredData(
+ target, *options_dict, create_error);
+ if (create_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Error creating breakpoint options from data: %s.",
+ create_error.AsCString());
+ return result_sp;
+ }
+ }
+
+ bool hardware = false;
+ success = breakpoint_dict->GetValueForKeyAsBoolean(
+ Breakpoint::GetKey(OptionNames::Hardware), hardware);
+
+ result_sp =
+ target.CreateBreakpoint(filter_sp, resolver_sp, false, hardware, true);
+
+ if (result_sp && options_up) {
+ result_sp->m_options_up = std::move(options_up);
+ }
+
+ StructuredData::Array *names_array;
+ success = breakpoint_dict->GetValueForKeyAsArray(
+ Breakpoint::GetKey(OptionNames::Names), names_array);
+ if (success && names_array) {
+ size_t num_names = names_array->GetSize();
+ for (size_t i = 0; i < num_names; i++) {
+ llvm::StringRef name;
+ Status error;
+ success = names_array->GetItemAtIndexAsString(i, name);
+ target.AddNameToBreakpoint(result_sp, name.str().c_str(), error);
+ }
+ }
+
+ return result_sp;
+}
+
+bool Breakpoint::SerializedBreakpointMatchesNames(
+ StructuredData::ObjectSP &bkpt_object_sp, std::vector<std::string> &names) {
+ if (!bkpt_object_sp)
+ return false;
+
+ StructuredData::Dictionary *bkpt_dict = bkpt_object_sp->GetAsDictionary();
+ if (!bkpt_dict)
+ return false;
+
+ if (names.empty())
+ return true;
+
+ StructuredData::Array *names_array;
+
+ bool success =
+ bkpt_dict->GetValueForKeyAsArray(GetKey(OptionNames::Names), names_array);
+ // If there are no names, it can't match these names;
+ if (!success)
+ return false;
+
+ size_t num_names = names_array->GetSize();
+ std::vector<std::string>::iterator begin = names.begin();
+ std::vector<std::string>::iterator end = names.end();
+
+ for (size_t i = 0; i < num_names; i++) {
+ llvm::StringRef name;
+ if (names_array->GetItemAtIndexAsString(i, name)) {
+ if (std::find(begin, end, name) != end) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+const lldb::TargetSP Breakpoint::GetTargetSP() {
+ return m_target.shared_from_this();
+}
+
+bool Breakpoint::IsInternal() const { return LLDB_BREAK_ID_IS_INTERNAL(m_bid); }
+
+BreakpointLocationSP Breakpoint::AddLocation(const Address &addr,
+ bool *new_location) {
+ return m_locations.AddLocation(addr, m_resolve_indirect_symbols,
+ new_location);
+}
+
+BreakpointLocationSP Breakpoint::FindLocationByAddress(const Address &addr) {
+ return m_locations.FindByAddress(addr);
+}
+
+break_id_t Breakpoint::FindLocationIDByAddress(const Address &addr) {
+ return m_locations.FindIDByAddress(addr);
+}
+
+BreakpointLocationSP Breakpoint::FindLocationByID(break_id_t bp_loc_id) {
+ return m_locations.FindByID(bp_loc_id);
+}
+
+BreakpointLocationSP Breakpoint::GetLocationAtIndex(size_t index) {
+ return m_locations.GetByIndex(index);
+}
+
+void Breakpoint::RemoveInvalidLocations(const ArchSpec &arch) {
+ m_locations.RemoveInvalidLocations(arch);
+}
+
+// For each of the overall options we need to decide how they propagate to the
+// location options. This will determine the precedence of options on the
+// breakpoint vs. its locations.
+
+// Disable at the breakpoint level should override the location settings. That
+// way you can conveniently turn off a whole breakpoint without messing up the
+// individual settings.
+
+void Breakpoint::SetEnabled(bool enable) {
+ if (enable == m_options_up->IsEnabled())
+ return;
+
+ m_options_up->SetEnabled(enable);
+ if (enable)
+ m_locations.ResolveAllBreakpointSites();
+ else
+ m_locations.ClearAllBreakpointSites();
+
+ SendBreakpointChangedEvent(enable ? eBreakpointEventTypeEnabled
+ : eBreakpointEventTypeDisabled);
+}
+
+bool Breakpoint::IsEnabled() { return m_options_up->IsEnabled(); }
+
+void Breakpoint::SetIgnoreCount(uint32_t n) {
+ if (m_options_up->GetIgnoreCount() == n)
+ return;
+
+ m_options_up->SetIgnoreCount(n);
+ SendBreakpointChangedEvent(eBreakpointEventTypeIgnoreChanged);
+}
+
+void Breakpoint::DecrementIgnoreCount() {
+ uint32_t ignore = m_options_up->GetIgnoreCount();
+ if (ignore != 0)
+ m_options_up->SetIgnoreCount(ignore - 1);
+}
+
+uint32_t Breakpoint::GetIgnoreCount() const {
+ return m_options_up->GetIgnoreCount();
+}
+
+bool Breakpoint::IgnoreCountShouldStop() {
+ uint32_t ignore = GetIgnoreCount();
+ if (ignore != 0) {
+ // When we get here we know the location that caused the stop doesn't have
+ // an ignore count, since by contract we call it first... So we don't have
+ // to find & decrement it, we only have to decrement our own ignore count.
+ DecrementIgnoreCount();
+ return false;
+ } else
+ return true;
+}
+
+uint32_t Breakpoint::GetHitCount() const { return m_hit_count; }
+
+bool Breakpoint::IsOneShot() const { return m_options_up->IsOneShot(); }
+
+void Breakpoint::SetOneShot(bool one_shot) {
+ m_options_up->SetOneShot(one_shot);
+}
+
+bool Breakpoint::IsAutoContinue() const {
+ return m_options_up->IsAutoContinue();
+}
+
+void Breakpoint::SetAutoContinue(bool auto_continue) {
+ m_options_up->SetAutoContinue(auto_continue);
+}
+
+void Breakpoint::SetThreadID(lldb::tid_t thread_id) {
+ if (m_options_up->GetThreadSpec()->GetTID() == thread_id)
+ return;
+
+ m_options_up->GetThreadSpec()->SetTID(thread_id);
+ SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+lldb::tid_t Breakpoint::GetThreadID() const {
+ if (m_options_up->GetThreadSpecNoCreate() == nullptr)
+ return LLDB_INVALID_THREAD_ID;
+ else
+ return m_options_up->GetThreadSpecNoCreate()->GetTID();
+}
+
+void Breakpoint::SetThreadIndex(uint32_t index) {
+ if (m_options_up->GetThreadSpec()->GetIndex() == index)
+ return;
+
+ m_options_up->GetThreadSpec()->SetIndex(index);
+ SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+uint32_t Breakpoint::GetThreadIndex() const {
+ if (m_options_up->GetThreadSpecNoCreate() == nullptr)
+ return 0;
+ else
+ return m_options_up->GetThreadSpecNoCreate()->GetIndex();
+}
+
+void Breakpoint::SetThreadName(const char *thread_name) {
+ if (m_options_up->GetThreadSpec()->GetName() != nullptr &&
+ ::strcmp(m_options_up->GetThreadSpec()->GetName(), thread_name) == 0)
+ return;
+
+ m_options_up->GetThreadSpec()->SetName(thread_name);
+ SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+const char *Breakpoint::GetThreadName() const {
+ if (m_options_up->GetThreadSpecNoCreate() == nullptr)
+ return nullptr;
+ else
+ return m_options_up->GetThreadSpecNoCreate()->GetName();
+}
+
+void Breakpoint::SetQueueName(const char *queue_name) {
+ if (m_options_up->GetThreadSpec()->GetQueueName() != nullptr &&
+ ::strcmp(m_options_up->GetThreadSpec()->GetQueueName(), queue_name) == 0)
+ return;
+
+ m_options_up->GetThreadSpec()->SetQueueName(queue_name);
+ SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+const char *Breakpoint::GetQueueName() const {
+ if (m_options_up->GetThreadSpecNoCreate() == nullptr)
+ return nullptr;
+ else
+ return m_options_up->GetThreadSpecNoCreate()->GetQueueName();
+}
+
+void Breakpoint::SetCondition(const char *condition) {
+ m_options_up->SetCondition(condition);
+ SendBreakpointChangedEvent(eBreakpointEventTypeConditionChanged);
+}
+
+const char *Breakpoint::GetConditionText() const {
+ return m_options_up->GetConditionText();
+}
+
+// This function is used when "baton" doesn't need to be freed
+void Breakpoint::SetCallback(BreakpointHitCallback callback, void *baton,
+ bool is_synchronous) {
+ // The default "Baton" class will keep a copy of "baton" and won't free or
+ // delete it when it goes goes out of scope.
+ m_options_up->SetCallback(callback, std::make_shared<UntypedBaton>(baton),
+ is_synchronous);
+
+ SendBreakpointChangedEvent(eBreakpointEventTypeCommandChanged);
+}
+
+// This function is used when a baton needs to be freed and therefore is
+// contained in a "Baton" subclass.
+void Breakpoint::SetCallback(BreakpointHitCallback callback,
+ const BatonSP &callback_baton_sp,
+ bool is_synchronous) {
+ m_options_up->SetCallback(callback, callback_baton_sp, is_synchronous);
+}
+
+void Breakpoint::ClearCallback() { m_options_up->ClearCallback(); }
+
+bool Breakpoint::InvokeCallback(StoppointCallbackContext *context,
+ break_id_t bp_loc_id) {
+ return m_options_up->InvokeCallback(context, GetID(), bp_loc_id);
+}
+
+BreakpointOptions *Breakpoint::GetOptions() { return m_options_up.get(); }
+
+const BreakpointOptions *Breakpoint::GetOptions() const {
+ return m_options_up.get();
+}
+
+void Breakpoint::ResolveBreakpoint() {
+ if (m_resolver_sp)
+ m_resolver_sp->ResolveBreakpoint(*m_filter_sp);
+}
+
+void Breakpoint::ResolveBreakpointInModules(
+ ModuleList &module_list, BreakpointLocationCollection &new_locations) {
+ m_locations.StartRecordingNewLocations(new_locations);
+
+ m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list);
+
+ m_locations.StopRecordingNewLocations();
+}
+
+void Breakpoint::ResolveBreakpointInModules(ModuleList &module_list,
+ bool send_event) {
+ if (m_resolver_sp) {
+ // If this is not an internal breakpoint, set up to record the new
+ // locations, then dispatch an event with the new locations.
+ if (!IsInternal() && send_event) {
+ BreakpointEventData *new_locations_event = new BreakpointEventData(
+ eBreakpointEventTypeLocationsAdded, shared_from_this());
+
+ ResolveBreakpointInModules(
+ module_list, new_locations_event->GetBreakpointLocationCollection());
+
+ if (new_locations_event->GetBreakpointLocationCollection().GetSize() !=
+ 0) {
+ SendBreakpointChangedEvent(new_locations_event);
+ } else
+ delete new_locations_event;
+ } else {
+ m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list);
+ }
+ }
+}
+
+void Breakpoint::ClearAllBreakpointSites() {
+ m_locations.ClearAllBreakpointSites();
+}
+
+// ModulesChanged: Pass in a list of new modules, and
+
+void Breakpoint::ModulesChanged(ModuleList &module_list, bool load,
+ bool delete_locations) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Breakpoint::ModulesChanged: num_modules: %zu load: %i "
+ "delete_locations: %i\n",
+ module_list.GetSize(), load, delete_locations);
+
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+ if (load) {
+ // The logic for handling new modules is:
+ // 1) If the filter rejects this module, then skip it. 2) Run through the
+ // current location list and if there are any locations
+ // for that module, we mark the module as "seen" and we don't try to
+ // re-resolve
+ // breakpoint locations for that module.
+ // However, we do add breakpoint sites to these locations if needed.
+ // 3) If we don't see this module in our breakpoint location list, call
+ // ResolveInModules.
+
+ ModuleList new_modules; // We'll stuff the "unseen" modules in this list,
+ // and then resolve
+ // them after the locations pass. Have to do it this way because resolving
+ // breakpoints will add new locations potentially.
+
+ for (ModuleSP module_sp : module_list.ModulesNoLocking()) {
+ bool seen = false;
+ if (!m_filter_sp->ModulePasses(module_sp))
+ continue;
+
+ BreakpointLocationCollection locations_with_no_section;
+ for (BreakpointLocationSP break_loc_sp :
+ m_locations.BreakpointLocations()) {
+
+ // If the section for this location was deleted, that means it's Module
+ // has gone away but somebody forgot to tell us. Let's clean it up
+ // here.
+ Address section_addr(break_loc_sp->GetAddress());
+ if (section_addr.SectionWasDeleted()) {
+ locations_with_no_section.Add(break_loc_sp);
+ continue;
+ }
+
+ if (!break_loc_sp->IsEnabled())
+ continue;
+
+ SectionSP section_sp(section_addr.GetSection());
+
+ // If we don't have a Section, that means this location is a raw
+ // address that we haven't resolved to a section yet. So we'll have to
+ // look in all the new modules to resolve this location. Otherwise, if
+ // it was set in this module, re-resolve it here.
+ if (section_sp && section_sp->GetModule() == module_sp) {
+ if (!seen)
+ seen = true;
+
+ if (!break_loc_sp->ResolveBreakpointSite()) {
+ if (log)
+ log->Printf("Warning: could not set breakpoint site for "
+ "breakpoint location %d of breakpoint %d.\n",
+ break_loc_sp->GetID(), GetID());
+ }
+ }
+ }
+
+ size_t num_to_delete = locations_with_no_section.GetSize();
+
+ for (size_t i = 0; i < num_to_delete; i++)
+ m_locations.RemoveLocation(locations_with_no_section.GetByIndex(i));
+
+ if (!seen)
+ new_modules.AppendIfNeeded(module_sp);
+ }
+
+ if (new_modules.GetSize() > 0) {
+ ResolveBreakpointInModules(new_modules);
+ }
+ } else {
+ // Go through the currently set locations and if any have breakpoints in
+ // the module list, then remove their breakpoint sites, and their locations
+ // if asked to.
+
+ BreakpointEventData *removed_locations_event;
+ if (!IsInternal())
+ removed_locations_event = new BreakpointEventData(
+ eBreakpointEventTypeLocationsRemoved, shared_from_this());
+ else
+ removed_locations_event = nullptr;
+
+ size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ ModuleSP module_sp(module_list.GetModuleAtIndexUnlocked(i));
+ if (m_filter_sp->ModulePasses(module_sp)) {
+ size_t loc_idx = 0;
+ size_t num_locations = m_locations.GetSize();
+ BreakpointLocationCollection locations_to_remove;
+ for (loc_idx = 0; loc_idx < num_locations; loc_idx++) {
+ BreakpointLocationSP break_loc_sp(m_locations.GetByIndex(loc_idx));
+ SectionSP section_sp(break_loc_sp->GetAddress().GetSection());
+ if (section_sp && section_sp->GetModule() == module_sp) {
+ // Remove this breakpoint since the shared library is unloaded, but
+ // keep the breakpoint location around so we always get complete
+ // hit count and breakpoint lifetime info
+ break_loc_sp->ClearBreakpointSite();
+ if (removed_locations_event) {
+ removed_locations_event->GetBreakpointLocationCollection().Add(
+ break_loc_sp);
+ }
+ if (delete_locations)
+ locations_to_remove.Add(break_loc_sp);
+ }
+ }
+
+ if (delete_locations) {
+ size_t num_locations_to_remove = locations_to_remove.GetSize();
+ for (loc_idx = 0; loc_idx < num_locations_to_remove; loc_idx++)
+ m_locations.RemoveLocation(locations_to_remove.GetByIndex(loc_idx));
+ }
+ }
+ }
+ SendBreakpointChangedEvent(removed_locations_event);
+ }
+}
+
+namespace {
+static bool SymbolContextsMightBeEquivalent(SymbolContext &old_sc,
+ SymbolContext &new_sc) {
+ bool equivalent_scs = false;
+
+ if (old_sc.module_sp.get() == new_sc.module_sp.get()) {
+ // If these come from the same module, we can directly compare the
+ // pointers:
+ if (old_sc.comp_unit && new_sc.comp_unit &&
+ (old_sc.comp_unit == new_sc.comp_unit)) {
+ if (old_sc.function && new_sc.function &&
+ (old_sc.function == new_sc.function)) {
+ equivalent_scs = true;
+ }
+ } else if (old_sc.symbol && new_sc.symbol &&
+ (old_sc.symbol == new_sc.symbol)) {
+ equivalent_scs = true;
+ }
+ } else {
+ // Otherwise we will compare by name...
+ if (old_sc.comp_unit && new_sc.comp_unit) {
+ if (FileSpec::Equal(*old_sc.comp_unit, *new_sc.comp_unit, true)) {
+ // Now check the functions:
+ if (old_sc.function && new_sc.function &&
+ (old_sc.function->GetName() == new_sc.function->GetName())) {
+ equivalent_scs = true;
+ }
+ }
+ } else if (old_sc.symbol && new_sc.symbol) {
+ if (Mangled::Compare(old_sc.symbol->GetMangled(),
+ new_sc.symbol->GetMangled()) == 0) {
+ equivalent_scs = true;
+ }
+ }
+ }
+ return equivalent_scs;
+}
+} // anonymous namespace
+
+void Breakpoint::ModuleReplaced(ModuleSP old_module_sp,
+ ModuleSP new_module_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Breakpoint::ModulesReplaced for %s\n",
+ old_module_sp->GetSpecificationDescription().c_str());
+ // First find all the locations that are in the old module
+
+ BreakpointLocationCollection old_break_locs;
+ for (BreakpointLocationSP break_loc_sp : m_locations.BreakpointLocations()) {
+ SectionSP section_sp = break_loc_sp->GetAddress().GetSection();
+ if (section_sp && section_sp->GetModule() == old_module_sp) {
+ old_break_locs.Add(break_loc_sp);
+ }
+ }
+
+ size_t num_old_locations = old_break_locs.GetSize();
+
+ if (num_old_locations == 0) {
+ // There were no locations in the old module, so we just need to check if
+ // there were any in the new module.
+ ModuleList temp_list;
+ temp_list.Append(new_module_sp);
+ ResolveBreakpointInModules(temp_list);
+ } else {
+ // First search the new module for locations. Then compare this with the
+ // old list, copy over locations that "look the same" Then delete the old
+ // locations. Finally remember to post the creation event.
+ //
+ // Two locations are the same if they have the same comp unit & function
+ // (by name) and there are the same number of locations in the old function
+ // as in the new one.
+
+ ModuleList temp_list;
+ temp_list.Append(new_module_sp);
+ BreakpointLocationCollection new_break_locs;
+ ResolveBreakpointInModules(temp_list, new_break_locs);
+ BreakpointLocationCollection locations_to_remove;
+ BreakpointLocationCollection locations_to_announce;
+
+ size_t num_new_locations = new_break_locs.GetSize();
+
+ if (num_new_locations > 0) {
+ // Break out the case of one location -> one location since that's the
+ // most common one, and there's no need to build up the structures needed
+ // for the merge in that case.
+ if (num_new_locations == 1 && num_old_locations == 1) {
+ bool equivalent_locations = false;
+ SymbolContext old_sc, new_sc;
+ // The only way the old and new location can be equivalent is if they
+ // have the same amount of information:
+ BreakpointLocationSP old_loc_sp = old_break_locs.GetByIndex(0);
+ BreakpointLocationSP new_loc_sp = new_break_locs.GetByIndex(0);
+
+ if (old_loc_sp->GetAddress().CalculateSymbolContext(&old_sc) ==
+ new_loc_sp->GetAddress().CalculateSymbolContext(&new_sc)) {
+ equivalent_locations =
+ SymbolContextsMightBeEquivalent(old_sc, new_sc);
+ }
+
+ if (equivalent_locations) {
+ m_locations.SwapLocation(old_loc_sp, new_loc_sp);
+ } else {
+ locations_to_remove.Add(old_loc_sp);
+ locations_to_announce.Add(new_loc_sp);
+ }
+ } else {
+ // We don't want to have to keep computing the SymbolContexts for these
+ // addresses over and over, so lets get them up front:
+
+ typedef std::map<lldb::break_id_t, SymbolContext> IDToSCMap;
+ IDToSCMap old_sc_map;
+ for (size_t idx = 0; idx < num_old_locations; idx++) {
+ SymbolContext sc;
+ BreakpointLocationSP bp_loc_sp = old_break_locs.GetByIndex(idx);
+ lldb::break_id_t loc_id = bp_loc_sp->GetID();
+ bp_loc_sp->GetAddress().CalculateSymbolContext(&old_sc_map[loc_id]);
+ }
+
+ std::map<lldb::break_id_t, SymbolContext> new_sc_map;
+ for (size_t idx = 0; idx < num_new_locations; idx++) {
+ SymbolContext sc;
+ BreakpointLocationSP bp_loc_sp = new_break_locs.GetByIndex(idx);
+ lldb::break_id_t loc_id = bp_loc_sp->GetID();
+ bp_loc_sp->GetAddress().CalculateSymbolContext(&new_sc_map[loc_id]);
+ }
+ // Take an element from the old Symbol Contexts
+ while (old_sc_map.size() > 0) {
+ lldb::break_id_t old_id = old_sc_map.begin()->first;
+ SymbolContext &old_sc = old_sc_map.begin()->second;
+
+ // Count the number of entries equivalent to this SC for the old
+ // list:
+ std::vector<lldb::break_id_t> old_id_vec;
+ old_id_vec.push_back(old_id);
+
+ IDToSCMap::iterator tmp_iter;
+ for (tmp_iter = ++old_sc_map.begin(); tmp_iter != old_sc_map.end();
+ tmp_iter++) {
+ if (SymbolContextsMightBeEquivalent(old_sc, tmp_iter->second))
+ old_id_vec.push_back(tmp_iter->first);
+ }
+
+ // Now find all the equivalent locations in the new list.
+ std::vector<lldb::break_id_t> new_id_vec;
+ for (tmp_iter = new_sc_map.begin(); tmp_iter != new_sc_map.end();
+ tmp_iter++) {
+ if (SymbolContextsMightBeEquivalent(old_sc, tmp_iter->second))
+ new_id_vec.push_back(tmp_iter->first);
+ }
+
+ // Alright, if we have the same number of potentially equivalent
+ // locations in the old and new modules, we'll just map them one to
+ // one in ascending ID order (assuming the resolver's order would
+ // match the equivalent ones. Otherwise, we'll dump all the old ones,
+ // and just take the new ones, erasing the elements from both maps as
+ // we go.
+
+ if (old_id_vec.size() == new_id_vec.size()) {
+ llvm::sort(old_id_vec);
+ llvm::sort(new_id_vec);
+ size_t num_elements = old_id_vec.size();
+ for (size_t idx = 0; idx < num_elements; idx++) {
+ BreakpointLocationSP old_loc_sp =
+ old_break_locs.FindByIDPair(GetID(), old_id_vec[idx]);
+ BreakpointLocationSP new_loc_sp =
+ new_break_locs.FindByIDPair(GetID(), new_id_vec[idx]);
+ m_locations.SwapLocation(old_loc_sp, new_loc_sp);
+ old_sc_map.erase(old_id_vec[idx]);
+ new_sc_map.erase(new_id_vec[idx]);
+ }
+ } else {
+ for (lldb::break_id_t old_id : old_id_vec) {
+ locations_to_remove.Add(
+ old_break_locs.FindByIDPair(GetID(), old_id));
+ old_sc_map.erase(old_id);
+ }
+ for (lldb::break_id_t new_id : new_id_vec) {
+ locations_to_announce.Add(
+ new_break_locs.FindByIDPair(GetID(), new_id));
+ new_sc_map.erase(new_id);
+ }
+ }
+ }
+ }
+ }
+
+ // Now remove the remaining old locations, and cons up a removed locations
+ // event. Note, we don't put the new locations that were swapped with an
+ // old location on the locations_to_remove list, so we don't need to worry
+ // about telling the world about removing a location we didn't tell them
+ // about adding.
+
+ BreakpointEventData *locations_event;
+ if (!IsInternal())
+ locations_event = new BreakpointEventData(
+ eBreakpointEventTypeLocationsRemoved, shared_from_this());
+ else
+ locations_event = nullptr;
+
+ for (BreakpointLocationSP loc_sp :
+ locations_to_remove.BreakpointLocations()) {
+ m_locations.RemoveLocation(loc_sp);
+ if (locations_event)
+ locations_event->GetBreakpointLocationCollection().Add(loc_sp);
+ }
+ SendBreakpointChangedEvent(locations_event);
+
+ // And announce the new ones.
+
+ if (!IsInternal()) {
+ locations_event = new BreakpointEventData(
+ eBreakpointEventTypeLocationsAdded, shared_from_this());
+ for (BreakpointLocationSP loc_sp :
+ locations_to_announce.BreakpointLocations())
+ locations_event->GetBreakpointLocationCollection().Add(loc_sp);
+
+ SendBreakpointChangedEvent(locations_event);
+ }
+ m_locations.Compact();
+ }
+}
+
+void Breakpoint::Dump(Stream *) {}
+
+size_t Breakpoint::GetNumResolvedLocations() const {
+ // Return the number of breakpoints that are actually resolved and set down
+ // in the inferior process.
+ return m_locations.GetNumResolvedLocations();
+}
+
+bool Breakpoint::HasResolvedLocations() const {
+ return GetNumResolvedLocations() > 0;
+}
+
+size_t Breakpoint::GetNumLocations() const { return m_locations.GetSize(); }
+
+bool Breakpoint::AddName(llvm::StringRef new_name) {
+ m_name_list.insert(new_name.str().c_str());
+ return true;
+}
+
+void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level,
+ bool show_locations) {
+ assert(s != nullptr);
+
+ if (!m_kind_description.empty()) {
+ if (level == eDescriptionLevelBrief) {
+ s->PutCString(GetBreakpointKind());
+ return;
+ } else
+ s->Printf("Kind: %s\n", GetBreakpointKind());
+ }
+
+ const size_t num_locations = GetNumLocations();
+ const size_t num_resolved_locations = GetNumResolvedLocations();
+
+ // 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
+ // whether there is 1 or more locations.
+ if (level != eDescriptionLevelInitial) {
+ s->Printf("%i: ", GetID());
+ GetResolverDescription(s);
+ GetFilterDescription(s);
+ }
+
+ switch (level) {
+ case lldb::eDescriptionLevelBrief:
+ case lldb::eDescriptionLevelFull:
+ if (num_locations > 0) {
+ s->Printf(", locations = %" PRIu64, (uint64_t)num_locations);
+ if (num_resolved_locations > 0)
+ s->Printf(", resolved = %" PRIu64 ", hit count = %d",
+ (uint64_t)num_resolved_locations, GetHitCount());
+ } else {
+ // Don't print the pending notification for exception resolvers since we
+ // don't generally know how to set them until the target is run.
+ if (m_resolver_sp->getResolverID() !=
+ BreakpointResolver::ExceptionResolver)
+ s->Printf(", locations = 0 (pending)");
+ }
+
+ GetOptions()->GetDescription(s, level);
+
+ if (m_precondition_sp)
+ m_precondition_sp->GetDescription(*s, level);
+
+ if (level == lldb::eDescriptionLevelFull) {
+ if (!m_name_list.empty()) {
+ s->EOL();
+ s->Indent();
+ s->Printf("Names:");
+ s->EOL();
+ s->IndentMore();
+ for (std::string name : m_name_list) {
+ s->Indent();
+ s->Printf("%s\n", name.c_str());
+ }
+ s->IndentLess();
+ }
+ s->IndentLess();
+ s->EOL();
+ }
+ break;
+
+ case lldb::eDescriptionLevelInitial:
+ s->Printf("Breakpoint %i: ", GetID());
+ if (num_locations == 0) {
+ s->Printf("no locations (pending).");
+ } 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);
+ } else {
+ s->Printf("%" PRIu64 " locations.", static_cast<uint64_t>(num_locations));
+ }
+ s->EOL();
+ break;
+
+ case lldb::eDescriptionLevelVerbose:
+ // Verbose mode does a debug dump of the breakpoint
+ Dump(s);
+ s->EOL();
+ // s->Indent();
+ GetOptions()->GetDescription(s, level);
+ break;
+
+ default:
+ break;
+ }
+
+ // 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) {
+ s->IndentMore();
+ for (size_t i = 0; i < num_locations; ++i) {
+ BreakpointLocation *loc = GetLocationAtIndex(i).get();
+ loc->GetDescription(s, level);
+ s->EOL();
+ }
+ s->IndentLess();
+ }
+}
+
+void Breakpoint::GetResolverDescription(Stream *s) {
+ if (m_resolver_sp)
+ m_resolver_sp->GetDescription(s);
+}
+
+bool Breakpoint::GetMatchingFileLine(ConstString filename,
+ uint32_t line_number,
+ BreakpointLocationCollection &loc_coll) {
+ // TODO: To be correct, this method needs to fill the breakpoint location
+ // collection
+ // with the location IDs which match the filename and line_number.
+ //
+
+ if (m_resolver_sp) {
+ BreakpointResolverFileLine *resolverFileLine =
+ dyn_cast<BreakpointResolverFileLine>(m_resolver_sp.get());
+ if (resolverFileLine &&
+ resolverFileLine->m_file_spec.GetFilename() == filename &&
+ resolverFileLine->m_line_number == line_number) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Breakpoint::GetFilterDescription(Stream *s) {
+ m_filter_sp->GetDescription(s);
+}
+
+bool Breakpoint::EvaluatePrecondition(StoppointCallbackContext &context) {
+ if (!m_precondition_sp)
+ return true;
+
+ return m_precondition_sp->EvaluatePrecondition(context);
+}
+
+void Breakpoint::SendBreakpointChangedEvent(
+ lldb::BreakpointEventType eventKind) {
+ if (!m_being_created && !IsInternal() &&
+ GetTarget().EventTypeHasListeners(
+ Target::eBroadcastBitBreakpointChanged)) {
+ BreakpointEventData *data =
+ new Breakpoint::BreakpointEventData(eventKind, shared_from_this());
+
+ GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged, data);
+ }
+}
+
+void Breakpoint::SendBreakpointChangedEvent(BreakpointEventData *data) {
+ if (data == nullptr)
+ return;
+
+ if (!m_being_created && !IsInternal() &&
+ GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged))
+ GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged, data);
+ else
+ delete data;
+}
+
+Breakpoint::BreakpointEventData::BreakpointEventData(
+ BreakpointEventType sub_type, const BreakpointSP &new_breakpoint_sp)
+ : EventData(), m_breakpoint_event(sub_type),
+ m_new_breakpoint_sp(new_breakpoint_sp) {}
+
+Breakpoint::BreakpointEventData::~BreakpointEventData() = default;
+
+ConstString Breakpoint::BreakpointEventData::GetFlavorString() {
+ static ConstString g_flavor("Breakpoint::BreakpointEventData");
+ return g_flavor;
+}
+
+ConstString Breakpoint::BreakpointEventData::GetFlavor() const {
+ return BreakpointEventData::GetFlavorString();
+}
+
+BreakpointSP &Breakpoint::BreakpointEventData::GetBreakpoint() {
+ return m_new_breakpoint_sp;
+}
+
+BreakpointEventType
+Breakpoint::BreakpointEventData::GetBreakpointEventType() const {
+ return m_breakpoint_event;
+}
+
+void Breakpoint::BreakpointEventData::Dump(Stream *s) const {}
+
+const Breakpoint::BreakpointEventData *
+Breakpoint::BreakpointEventData::GetEventDataFromEvent(const Event *event) {
+ if (event) {
+ const EventData *event_data = event->GetData();
+ if (event_data &&
+ event_data->GetFlavor() == BreakpointEventData::GetFlavorString())
+ return static_cast<const BreakpointEventData *>(event->GetData());
+ }
+ return nullptr;
+}
+
+BreakpointEventType
+Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent(
+ const EventSP &event_sp) {
+ const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get());
+
+ if (data == nullptr)
+ return eBreakpointEventTypeInvalidType;
+ else
+ return data->GetBreakpointEventType();
+}
+
+BreakpointSP Breakpoint::BreakpointEventData::GetBreakpointFromEvent(
+ const EventSP &event_sp) {
+ BreakpointSP bp_sp;
+
+ const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get());
+ if (data)
+ bp_sp = data->m_new_breakpoint_sp;
+
+ return bp_sp;
+}
+
+size_t Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent(
+ const EventSP &event_sp) {
+ const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get());
+ if (data)
+ return data->m_locations.GetSize();
+
+ return 0;
+}
+
+lldb::BreakpointLocationSP
+Breakpoint::BreakpointEventData::GetBreakpointLocationAtIndexFromEvent(
+ const lldb::EventSP &event_sp, uint32_t bp_loc_idx) {
+ lldb::BreakpointLocationSP bp_loc_sp;
+
+ const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get());
+ if (data) {
+ bp_loc_sp = data->m_locations.GetByIndex(bp_loc_idx);
+ }
+
+ return bp_loc_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointID.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointID.cpp
new file mode 100644
index 000000000000..dc2e57cb085d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointID.cpp
@@ -0,0 +1,121 @@
+//===-- BreakpointID.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdio.h>
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointID.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointID::BreakpointID(break_id_t bp_id, break_id_t loc_id)
+ : m_break_id(bp_id), m_location_id(loc_id) {}
+
+BreakpointID::~BreakpointID() = default;
+
+static llvm::StringRef g_range_specifiers[] = {"-", "to", "To", "TO"};
+
+// Tells whether or not STR is valid to use between two strings representing
+// breakpoint IDs, to indicate a range of breakpoint IDs. This is broken out
+// into a separate function so that we can easily change or add to the format
+// for specifying ID ranges at a later date.
+
+bool BreakpointID::IsRangeIdentifier(llvm::StringRef str) {
+ for (auto spec : g_range_specifiers) {
+ if (spec == str)
+ return true;
+ }
+
+ return false;
+}
+
+bool BreakpointID::IsValidIDExpression(llvm::StringRef str) {
+ return BreakpointID::ParseCanonicalReference(str).hasValue();
+}
+
+llvm::ArrayRef<llvm::StringRef> BreakpointID::GetRangeSpecifiers() {
+ return llvm::makeArrayRef(g_range_specifiers);
+}
+
+void BreakpointID::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ if (level == eDescriptionLevelVerbose)
+ s->Printf("%p BreakpointID:", static_cast<void *>(this));
+
+ if (m_break_id == LLDB_INVALID_BREAK_ID)
+ s->PutCString("<invalid>");
+ else if (m_location_id == LLDB_INVALID_BREAK_ID)
+ s->Printf("%i", m_break_id);
+ else
+ s->Printf("%i.%i", m_break_id, m_location_id);
+}
+
+void BreakpointID::GetCanonicalReference(Stream *s, break_id_t bp_id,
+ break_id_t loc_id) {
+ if (bp_id == LLDB_INVALID_BREAK_ID)
+ s->PutCString("<invalid>");
+ else if (loc_id == LLDB_INVALID_BREAK_ID)
+ s->Printf("%i", bp_id);
+ else
+ s->Printf("%i.%i", bp_id, loc_id);
+}
+
+llvm::Optional<BreakpointID>
+BreakpointID::ParseCanonicalReference(llvm::StringRef input) {
+ break_id_t bp_id;
+ break_id_t loc_id = LLDB_INVALID_BREAK_ID;
+
+ if (input.empty())
+ return llvm::None;
+
+ // If it doesn't start with an integer, it's not valid.
+ if (input.consumeInteger(0, bp_id))
+ return llvm::None;
+
+ // period is optional, but if it exists, it must be followed by a number.
+ if (input.consume_front(".")) {
+ if (input.consumeInteger(0, loc_id))
+ return llvm::None;
+ }
+
+ // And at the end, the entire string must have been consumed.
+ if (!input.empty())
+ return llvm::None;
+
+ return BreakpointID(bp_id, loc_id);
+}
+
+bool BreakpointID::StringIsBreakpointName(llvm::StringRef str, Status &error) {
+ error.Clear();
+ if (str.empty())
+ {
+ error.SetErrorStringWithFormat("Empty breakpoint names are not allowed");
+ return false;
+ }
+
+ // First character must be a letter or _
+ if (!isalpha(str[0]) && str[0] != '_')
+ {
+ error.SetErrorStringWithFormat("Breakpoint names must start with a "
+ "character or underscore: %s",
+ str.str().c_str());
+ return false;
+ }
+
+ // Cannot contain ., -, or space.
+ if (str.find_first_of(".- ") != llvm::StringRef::npos) {
+ error.SetErrorStringWithFormat("Breakpoint names cannot contain "
+ "'.' or '-': \"%s\"",
+ str.str().c_str());
+ return false;
+ }
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointIDList.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointIDList.cpp
new file mode 100644
index 000000000000..1e695fae6995
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointIDList.cpp
@@ -0,0 +1,348 @@
+//===-- BreakpointIDList.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-enumerations.h"
+#include "lldb/Breakpoint/BreakpointIDList.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Args.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// class BreakpointIDList
+
+BreakpointIDList::BreakpointIDList()
+ : m_invalid_id(LLDB_INVALID_BREAK_ID, LLDB_INVALID_BREAK_ID) {}
+
+BreakpointIDList::~BreakpointIDList() = default;
+
+size_t BreakpointIDList::GetSize() const { return m_breakpoint_ids.size(); }
+
+const BreakpointID &
+BreakpointIDList::GetBreakpointIDAtIndex(size_t index) const {
+ return ((index < m_breakpoint_ids.size()) ? m_breakpoint_ids[index]
+ : m_invalid_id);
+}
+
+bool BreakpointIDList::RemoveBreakpointIDAtIndex(size_t index) {
+ if (index >= m_breakpoint_ids.size())
+ return false;
+
+ m_breakpoint_ids.erase(m_breakpoint_ids.begin() + index);
+ return true;
+}
+
+void BreakpointIDList::Clear() { m_breakpoint_ids.clear(); }
+
+bool BreakpointIDList::AddBreakpointID(BreakpointID bp_id) {
+ m_breakpoint_ids.push_back(bp_id);
+
+ return true; // We don't do any verification in this function, so always
+ // return true.
+}
+
+bool BreakpointIDList::AddBreakpointID(const char *bp_id_str) {
+ auto bp_id = BreakpointID::ParseCanonicalReference(bp_id_str);
+ if (!bp_id.hasValue())
+ return false;
+
+ m_breakpoint_ids.push_back(*bp_id);
+ return true;
+}
+
+bool BreakpointIDList::FindBreakpointID(BreakpointID &bp_id,
+ size_t *position) const {
+ for (size_t i = 0; i < m_breakpoint_ids.size(); ++i) {
+ BreakpointID tmp_id = m_breakpoint_ids[i];
+ if (tmp_id.GetBreakpointID() == bp_id.GetBreakpointID() &&
+ tmp_id.GetLocationID() == bp_id.GetLocationID()) {
+ *position = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool BreakpointIDList::FindBreakpointID(const char *bp_id_str,
+ size_t *position) const {
+ auto bp_id = BreakpointID::ParseCanonicalReference(bp_id_str);
+ if (!bp_id.hasValue())
+ return false;
+
+ return FindBreakpointID(*bp_id, position);
+}
+
+void BreakpointIDList::InsertStringArray(
+ llvm::ArrayRef<const char *> string_array, CommandReturnObject &result) {
+ if(string_array.empty())
+ return;
+
+ for (const char *str : string_array) {
+ auto bp_id = BreakpointID::ParseCanonicalReference(str);
+ if (bp_id.hasValue())
+ m_breakpoint_ids.push_back(*bp_id);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+}
+
+// This function takes OLD_ARGS, which is usually the result of breaking the
+// command string arguments into
+// an array of space-separated strings, and searches through the arguments for
+// any breakpoint ID range specifiers.
+// Any string in the array that is not part of an ID range specifier is copied
+// directly into NEW_ARGS. If any
+// ID range specifiers are found, the range is interpreted and a list of
+// canonical breakpoint IDs corresponding to
+// all the current breakpoints and locations in the range are added to
+// NEW_ARGS. When this function is done,
+// NEW_ARGS should be a copy of OLD_ARGS, with and ID range specifiers replaced
+// by the members of the range.
+
+void BreakpointIDList::FindAndReplaceIDRanges(Args &old_args, Target *target,
+ bool allow_locations,
+ BreakpointName::Permissions
+ ::PermissionKinds purpose,
+ CommandReturnObject &result,
+ Args &new_args) {
+ llvm::StringRef range_from;
+ llvm::StringRef range_to;
+ llvm::StringRef current_arg;
+ std::set<std::string> names_found;
+
+ for (size_t i = 0; i < old_args.size(); ++i) {
+ bool is_range = false;
+
+ current_arg = old_args[i].ref;
+ if (!allow_locations && current_arg.contains('.')) {
+ result.AppendErrorWithFormat(
+ "Breakpoint locations not allowed, saw location: %s.",
+ current_arg.str().c_str());
+ new_args.Clear();
+ return;
+ }
+
+ Status error;
+
+ std::tie(range_from, range_to) =
+ BreakpointIDList::SplitIDRangeExpression(current_arg);
+ if (!range_from.empty() && !range_to.empty()) {
+ is_range = true;
+ } else if (BreakpointID::StringIsBreakpointName(current_arg, error)) {
+ if (!error.Success()) {
+ new_args.Clear();
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return;
+ } else
+ names_found.insert(current_arg);
+ } else if ((i + 2 < old_args.size()) &&
+ BreakpointID::IsRangeIdentifier(old_args[i + 1].ref) &&
+ BreakpointID::IsValidIDExpression(current_arg) &&
+ BreakpointID::IsValidIDExpression(old_args[i + 2].ref)) {
+ range_from = current_arg;
+ range_to = old_args[i + 2].ref;
+ is_range = true;
+ i = i + 2;
+ } else {
+ // See if user has specified id.*
+ llvm::StringRef tmp_str = old_args[i].ref;
+ size_t pos = tmp_str.find('.');
+ if (pos != llvm::StringRef::npos) {
+ llvm::StringRef bp_id_str = tmp_str.substr(0, pos);
+ if (BreakpointID::IsValidIDExpression(bp_id_str) &&
+ tmp_str[pos + 1] == '*' && tmp_str.size() == (pos + 2)) {
+
+ BreakpointSP breakpoint_sp;
+ auto bp_id = BreakpointID::ParseCanonicalReference(bp_id_str);
+ if (bp_id.hasValue())
+ breakpoint_sp = target->GetBreakpointByID(bp_id->GetBreakpointID());
+ if (!breakpoint_sp) {
+ new_args.Clear();
+ result.AppendErrorWithFormat("'%d' is not a valid breakpoint ID.\n",
+ bp_id->GetBreakpointID());
+ result.SetStatus(eReturnStatusFailed);
+ return;
+ }
+ const size_t num_locations = breakpoint_sp->GetNumLocations();
+ for (size_t j = 0; j < num_locations; ++j) {
+ BreakpointLocation *bp_loc =
+ breakpoint_sp->GetLocationAtIndex(j).get();
+ StreamString canonical_id_str;
+ BreakpointID::GetCanonicalReference(
+ &canonical_id_str, bp_id->GetBreakpointID(), bp_loc->GetID());
+ new_args.AppendArgument(canonical_id_str.GetString());
+ }
+ }
+ }
+ }
+
+ if (!is_range) {
+ new_args.AppendArgument(current_arg);
+ continue;
+ }
+
+ auto start_bp = BreakpointID::ParseCanonicalReference(range_from);
+ auto end_bp = BreakpointID::ParseCanonicalReference(range_to);
+
+ if (!start_bp.hasValue() ||
+ !target->GetBreakpointByID(start_bp->GetBreakpointID())) {
+ new_args.Clear();
+ result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n",
+ range_from.str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return;
+ }
+
+ if (!end_bp.hasValue() ||
+ !target->GetBreakpointByID(end_bp->GetBreakpointID())) {
+ new_args.Clear();
+ result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n",
+ range_to.str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return;
+ }
+ break_id_t start_bp_id = start_bp->GetBreakpointID();
+ break_id_t start_loc_id = start_bp->GetLocationID();
+ break_id_t end_bp_id = end_bp->GetBreakpointID();
+ break_id_t end_loc_id = end_bp->GetLocationID();
+ if (((start_loc_id == LLDB_INVALID_BREAK_ID) &&
+ (end_loc_id != LLDB_INVALID_BREAK_ID)) ||
+ ((start_loc_id != LLDB_INVALID_BREAK_ID) &&
+ (end_loc_id == LLDB_INVALID_BREAK_ID))) {
+ new_args.Clear();
+ result.AppendErrorWithFormat("Invalid breakpoint id range: Either "
+ "both ends of range must specify"
+ " a breakpoint location, or neither can "
+ "specify a breakpoint location.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return;
+ }
+
+ // We have valid range starting & ending breakpoint IDs. Go through all
+ // the breakpoints in the target and find all the breakpoints that fit into
+ // this range, and add them to new_args.
+
+ // Next check to see if we have location id's. If so, make sure the
+ // start_bp_id and end_bp_id are for the same breakpoint; otherwise we have
+ // an illegal range: breakpoint id ranges that specify bp locations are NOT
+ // allowed to cross major bp id numbers.
+
+ if ((start_loc_id != LLDB_INVALID_BREAK_ID) ||
+ (end_loc_id != LLDB_INVALID_BREAK_ID)) {
+ if (start_bp_id != end_bp_id) {
+ new_args.Clear();
+ result.AppendErrorWithFormat(
+ "Invalid range: Ranges that specify particular breakpoint "
+ "locations"
+ " must be within the same major breakpoint; you specified two"
+ " different major breakpoints, %d and %d.\n",
+ start_bp_id, end_bp_id);
+ result.SetStatus(eReturnStatusFailed);
+ return;
+ }
+ }
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+ const size_t num_breakpoints = breakpoints.GetSize();
+ for (size_t j = 0; j < num_breakpoints; ++j) {
+ Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex(j).get();
+ break_id_t cur_bp_id = breakpoint->GetID();
+
+ if ((cur_bp_id < start_bp_id) || (cur_bp_id > end_bp_id))
+ continue;
+
+ const size_t num_locations = breakpoint->GetNumLocations();
+
+ if ((cur_bp_id == start_bp_id) &&
+ (start_loc_id != LLDB_INVALID_BREAK_ID)) {
+ for (size_t k = 0; k < num_locations; ++k) {
+ BreakpointLocation *bp_loc = breakpoint->GetLocationAtIndex(k).get();
+ if ((bp_loc->GetID() >= start_loc_id) &&
+ (bp_loc->GetID() <= end_loc_id)) {
+ StreamString canonical_id_str;
+ BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id,
+ bp_loc->GetID());
+ new_args.AppendArgument(canonical_id_str.GetString());
+ }
+ }
+ } else if ((cur_bp_id == end_bp_id) &&
+ (end_loc_id != LLDB_INVALID_BREAK_ID)) {
+ for (size_t k = 0; k < num_locations; ++k) {
+ BreakpointLocation *bp_loc = breakpoint->GetLocationAtIndex(k).get();
+ if (bp_loc->GetID() <= end_loc_id) {
+ StreamString canonical_id_str;
+ BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id,
+ bp_loc->GetID());
+ new_args.AppendArgument(canonical_id_str.GetString());
+ }
+ }
+ } else {
+ StreamString canonical_id_str;
+ BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id,
+ LLDB_INVALID_BREAK_ID);
+ new_args.AppendArgument(canonical_id_str.GetString());
+ }
+ }
+ }
+
+ // Okay, now see if we found any names, and if we did, add them:
+ if (target && !names_found.empty()) {
+ Status error;
+ // Remove any names that aren't visible for this purpose:
+ auto iter = names_found.begin();
+ while (iter != names_found.end()) {
+ BreakpointName *bp_name = target->FindBreakpointName(ConstString(*iter),
+ true,
+ error);
+ if (bp_name && !bp_name->GetPermission(purpose))
+ iter = names_found.erase(iter);
+ else
+ iter++;
+ }
+
+ if (!names_found.empty()) {
+ for (BreakpointSP bkpt_sp : target->GetBreakpointList().Breakpoints()) {
+ for (std::string name : names_found) {
+ if (bkpt_sp->MatchesName(name.c_str())) {
+ StreamString canonical_id_str;
+ BreakpointID::GetCanonicalReference(
+ &canonical_id_str, bkpt_sp->GetID(), LLDB_INVALID_BREAK_ID);
+ new_args.AppendArgument(canonical_id_str.GetString());
+ }
+ }
+ }
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+}
+
+std::pair<llvm::StringRef, llvm::StringRef>
+BreakpointIDList::SplitIDRangeExpression(llvm::StringRef in_string) {
+ for (auto specifier_str : BreakpointID::GetRangeSpecifiers()) {
+ size_t idx = in_string.find(specifier_str);
+ if (idx == llvm::StringRef::npos)
+ continue;
+ llvm::StringRef right1 = in_string.drop_front(idx);
+
+ llvm::StringRef from = in_string.take_front(idx);
+ llvm::StringRef to = right1.drop_front(specifier_str.size());
+
+ if (BreakpointID::IsValidIDExpression(from) &&
+ BreakpointID::IsValidIDExpression(to)) {
+ return std::make_pair(from, to);
+ }
+ }
+
+ return std::pair<llvm::StringRef, llvm::StringRef>();
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointList.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointList.cpp
new file mode 100644
index 000000000000..c80fb917b490
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointList.cpp
@@ -0,0 +1,191 @@
+//===-- BreakpointList.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointList.h"
+
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void NotifyChange(const BreakpointSP &bp, BreakpointEventType event) {
+ Target &target = bp->GetTarget();
+ if (target.EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged))
+ target.BroadcastEvent(Target::eBroadcastBitBreakpointChanged,
+ new Breakpoint::BreakpointEventData(event, bp));
+}
+
+BreakpointList::BreakpointList(bool is_internal)
+ : m_mutex(), m_breakpoints(), m_next_break_id(0),
+ m_is_internal(is_internal) {}
+
+BreakpointList::~BreakpointList() {}
+
+break_id_t BreakpointList::Add(BreakpointSP &bp_sp, bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ // Internal breakpoint IDs are negative, normal ones are positive
+ bp_sp->SetID(m_is_internal ? --m_next_break_id : ++m_next_break_id);
+
+ m_breakpoints.push_back(bp_sp);
+
+ if (notify)
+ NotifyChange(bp_sp, eBreakpointEventTypeAdded);
+
+ return bp_sp->GetID();
+}
+
+bool BreakpointList::Remove(break_id_t break_id, bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ auto it = std::find_if(
+ m_breakpoints.begin(), m_breakpoints.end(),
+ [&](const BreakpointSP &bp) { return bp->GetID() == break_id; });
+
+ if (it == m_breakpoints.end())
+ return false;
+
+ if (notify)
+ NotifyChange(*it, eBreakpointEventTypeRemoved);
+
+ m_breakpoints.erase(it);
+
+ return true;
+}
+
+void BreakpointList::RemoveInvalidLocations(const ArchSpec &arch) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->RemoveInvalidLocations(arch);
+}
+
+void BreakpointList::SetEnabledAll(bool enabled) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->SetEnabled(enabled);
+}
+
+void BreakpointList::SetEnabledAllowed(bool enabled) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ if (bp_sp->AllowDisable())
+ bp_sp->SetEnabled(enabled);
+}
+
+void BreakpointList::RemoveAll(bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ ClearAllBreakpointSites();
+
+ if (notify) {
+ for (const auto &bp_sp : m_breakpoints)
+ NotifyChange(bp_sp, eBreakpointEventTypeRemoved);
+ }
+
+ m_breakpoints.clear();
+}
+
+void BreakpointList::RemoveAllowed(bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ for (const auto &bp_sp : m_breakpoints) {
+ if (bp_sp->AllowDelete())
+ bp_sp->ClearAllBreakpointSites();
+ if (notify)
+ NotifyChange(bp_sp, eBreakpointEventTypeRemoved);
+ }
+
+ m_breakpoints.erase(
+ std::remove_if(m_breakpoints.begin(), m_breakpoints.end(),
+ [&](const BreakpointSP &bp) { return bp->AllowDelete(); }),
+ m_breakpoints.end());
+}
+
+BreakpointList::bp_collection::iterator
+BreakpointList::GetBreakpointIDIterator(break_id_t break_id) {
+ return std::find_if(
+ m_breakpoints.begin(), m_breakpoints.end(),
+ [&](const BreakpointSP &bp) { return bp->GetID() == break_id; });
+}
+
+BreakpointList::bp_collection::const_iterator
+BreakpointList::GetBreakpointIDConstIterator(break_id_t break_id) const {
+ return std::find_if(
+ m_breakpoints.begin(), m_breakpoints.end(),
+ [&](const BreakpointSP &bp) { return bp->GetID() == break_id; });
+}
+
+BreakpointSP BreakpointList::FindBreakpointByID(break_id_t break_id) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ auto it = GetBreakpointIDConstIterator(break_id);
+ if (it != m_breakpoints.end())
+ return *it;
+ return {};
+}
+
+bool BreakpointList::FindBreakpointsByName(const char *name,
+ BreakpointList &matching_bps) {
+ Status error;
+ if (!name)
+ return false;
+
+ if (!BreakpointID::StringIsBreakpointName(llvm::StringRef(name), error))
+ return false;
+
+ for (BreakpointSP bkpt_sp : Breakpoints()) {
+ if (bkpt_sp->MatchesName(name)) {
+ matching_bps.Add(bkpt_sp, false);
+ }
+ }
+
+ return true;
+}
+
+void BreakpointList::Dump(Stream *s) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ s->Printf("%p: ", static_cast<const void *>(this));
+ s->Indent();
+ s->Printf("BreakpointList with %u Breakpoints:\n",
+ (uint32_t)m_breakpoints.size());
+ s->IndentMore();
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->Dump(s);
+ s->IndentLess();
+}
+
+BreakpointSP BreakpointList::GetBreakpointAtIndex(size_t i) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (i < m_breakpoints.size())
+ return m_breakpoints[i];
+ return {};
+}
+
+void BreakpointList::UpdateBreakpoints(ModuleList &module_list, bool added,
+ bool delete_locations) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->ModulesChanged(module_list, added, delete_locations);
+}
+
+void BreakpointList::UpdateBreakpointsWhenModuleIsReplaced(
+ ModuleSP old_module_sp, ModuleSP new_module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->ModuleReplaced(old_module_sp, new_module_sp);
+}
+
+void BreakpointList::ClearAllBreakpointSites() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->ClearAllBreakpointSites();
+}
+
+void BreakpointList::GetListMutex(
+ std::unique_lock<std::recursive_mutex> &lock) {
+ lock = std::unique_lock<std::recursive_mutex>(m_mutex);
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocation.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocation.cpp
new file mode 100644
index 000000000000..b718e2aeea5c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -0,0 +1,660 @@
+//===-- BreakpointLocation.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointID.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner,
+ const Address &addr, lldb::tid_t tid,
+ bool hardware, bool check_for_resolver)
+ : StoppointLocation(loc_id, addr.GetOpcodeLoadAddress(&owner.GetTarget()),
+ hardware),
+ m_being_created(true), m_should_resolve_indirect_functions(false),
+ m_is_reexported(false), m_is_indirect(false), m_address(addr),
+ m_owner(owner), m_options_up(), m_bp_site_sp(), m_condition_mutex() {
+ if (check_for_resolver) {
+ Symbol *symbol = m_address.CalculateSymbolContextSymbol();
+ if (symbol && symbol->IsIndirect()) {
+ SetShouldResolveIndirectFunctions(true);
+ }
+ }
+
+ SetThreadID(tid);
+ m_being_created = false;
+}
+
+BreakpointLocation::~BreakpointLocation() { ClearBreakpointSite(); }
+
+lldb::addr_t BreakpointLocation::GetLoadAddress() const {
+ return m_address.GetOpcodeLoadAddress(&m_owner.GetTarget());
+}
+
+const BreakpointOptions *
+BreakpointLocation::GetOptionsSpecifyingKind(BreakpointOptions::OptionKind kind)
+const {
+ if (m_options_up && m_options_up->IsOptionSet(kind))
+ return m_options_up.get();
+ else
+ return m_owner.GetOptions();
+}
+
+Address &BreakpointLocation::GetAddress() { return m_address; }
+
+Breakpoint &BreakpointLocation::GetBreakpoint() { return m_owner; }
+
+Target &BreakpointLocation::GetTarget() { return m_owner.GetTarget(); }
+
+bool BreakpointLocation::IsEnabled() const {
+ if (!m_owner.IsEnabled())
+ return false;
+ else if (m_options_up != nullptr)
+ return m_options_up->IsEnabled();
+ else
+ return true;
+}
+
+void BreakpointLocation::SetEnabled(bool enabled) {
+ GetLocationOptions()->SetEnabled(enabled);
+ if (enabled) {
+ ResolveBreakpointSite();
+ } else {
+ ClearBreakpointSite();
+ }
+ SendBreakpointLocationChangedEvent(enabled ? eBreakpointEventTypeEnabled
+ : eBreakpointEventTypeDisabled);
+}
+
+bool BreakpointLocation::IsAutoContinue() const {
+ if (m_options_up &&
+ m_options_up->IsOptionSet(BreakpointOptions::eAutoContinue))
+ return m_options_up->IsAutoContinue();
+ else
+ return m_owner.IsAutoContinue();
+}
+
+void BreakpointLocation::SetAutoContinue(bool auto_continue) {
+ GetLocationOptions()->SetAutoContinue(auto_continue);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeAutoContinueChanged);
+}
+
+void BreakpointLocation::SetThreadID(lldb::tid_t thread_id) {
+ if (thread_id != LLDB_INVALID_THREAD_ID)
+ GetLocationOptions()->SetThreadID(thread_id);
+ 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)
+ m_options_up->SetThreadID(thread_id);
+ }
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+lldb::tid_t BreakpointLocation::GetThreadID() {
+ const ThreadSpec *thread_spec =
+ GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ ->GetThreadSpecNoCreate();
+ if (thread_spec)
+ return thread_spec->GetTID();
+ else
+ return LLDB_INVALID_THREAD_ID;
+}
+
+void BreakpointLocation::SetThreadIndex(uint32_t index) {
+ if (index != 0)
+ GetLocationOptions()->GetThreadSpec()->SetIndex(index);
+ 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)
+ m_options_up->GetThreadSpec()->SetIndex(index);
+ }
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+uint32_t BreakpointLocation::GetThreadIndex() const {
+ const ThreadSpec *thread_spec =
+ GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ ->GetThreadSpecNoCreate();
+ if (thread_spec)
+ return thread_spec->GetIndex();
+ else
+ return 0;
+}
+
+void BreakpointLocation::SetThreadName(const char *thread_name) {
+ if (thread_name != nullptr)
+ GetLocationOptions()->GetThreadSpec()->SetName(thread_name);
+ 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)
+ m_options_up->GetThreadSpec()->SetName(thread_name);
+ }
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+const char *BreakpointLocation::GetThreadName() const {
+ const ThreadSpec *thread_spec =
+ GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ ->GetThreadSpecNoCreate();
+ if (thread_spec)
+ return thread_spec->GetName();
+ else
+ return nullptr;
+}
+
+void BreakpointLocation::SetQueueName(const char *queue_name) {
+ if (queue_name != nullptr)
+ GetLocationOptions()->GetThreadSpec()->SetQueueName(queue_name);
+ 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)
+ m_options_up->GetThreadSpec()->SetQueueName(queue_name);
+ }
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+const char *BreakpointLocation::GetQueueName() const {
+ const ThreadSpec *thread_spec =
+ GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ ->GetThreadSpecNoCreate();
+ if (thread_spec)
+ return thread_spec->GetQueueName();
+ else
+ return nullptr;
+}
+
+bool BreakpointLocation::InvokeCallback(StoppointCallbackContext *context) {
+ if (m_options_up != nullptr && m_options_up->HasCallback())
+ return m_options_up->InvokeCallback(context, m_owner.GetID(), GetID());
+ else
+ return m_owner.InvokeCallback(context, GetID());
+}
+
+void BreakpointLocation::SetCallback(BreakpointHitCallback callback,
+ void *baton, bool is_synchronous) {
+ // The default "Baton" class will keep a copy of "baton" and won't free or
+ // delete it when it goes goes out of scope.
+ GetLocationOptions()->SetCallback(
+ callback, std::make_shared<UntypedBaton>(baton), is_synchronous);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeCommandChanged);
+}
+
+void BreakpointLocation::SetCallback(BreakpointHitCallback callback,
+ const BatonSP &baton_sp,
+ bool is_synchronous) {
+ GetLocationOptions()->SetCallback(callback, baton_sp, is_synchronous);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeCommandChanged);
+}
+
+void BreakpointLocation::ClearCallback() {
+ GetLocationOptions()->ClearCallback();
+}
+
+void BreakpointLocation::SetCondition(const char *condition) {
+ GetLocationOptions()->SetCondition(condition);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeConditionChanged);
+}
+
+const char *BreakpointLocation::GetConditionText(size_t *hash) const {
+ return GetOptionsSpecifyingKind(BreakpointOptions::eCondition)
+ ->GetConditionText(hash);
+}
+
+bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
+ Status &error) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS);
+
+ std::lock_guard<std::mutex> guard(m_condition_mutex);
+
+ size_t condition_hash;
+ const char *condition_text = GetConditionText(&condition_hash);
+
+ if (!condition_text) {
+ m_user_expression_sp.reset();
+ return false;
+ }
+
+ error.Clear();
+
+ DiagnosticManager diagnostics;
+
+ if (condition_hash != m_condition_hash || !m_user_expression_sp ||
+ !m_user_expression_sp->MatchesContext(exe_ctx)) {
+ LanguageType language = eLanguageTypeUnknown;
+ // See if we can figure out the language from the frame, otherwise use the
+ // default language:
+ CompileUnit *comp_unit = m_address.CalculateSymbolContextCompileUnit();
+ if (comp_unit)
+ language = comp_unit->GetLanguage();
+
+ m_user_expression_sp.reset(GetTarget().GetUserExpressionForLanguage(
+ condition_text, llvm::StringRef(), language, Expression::eResultTypeAny,
+ EvaluateExpressionOptions(), nullptr, error));
+ if (error.Fail()) {
+ if (log)
+ log->Printf("Error getting condition expression: %s.",
+ error.AsCString());
+ m_user_expression_sp.reset();
+ return true;
+ }
+
+ if (!m_user_expression_sp->Parse(diagnostics, exe_ctx,
+ eExecutionPolicyOnlyWhenNeeded, true,
+ false)) {
+ error.SetErrorStringWithFormat(
+ "Couldn't parse conditional expression:\n%s",
+ diagnostics.GetString().c_str());
+ m_user_expression_sp.reset();
+ return true;
+ }
+
+ m_condition_hash = condition_hash;
+ }
+
+ // We need to make sure the user sees any parse errors in their condition, so
+ // we'll hook the constructor errors up to the debugger's Async I/O.
+
+ ValueObjectSP result_value_sp;
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTryAllThreads(true);
+ options.SetResultIsInternal(
+ true); // Don't generate a user variable for condition expressions.
+
+ Status expr_error;
+
+ diagnostics.Clear();
+
+ ExpressionVariableSP result_variable_sp;
+
+ ExpressionResults result_code = m_user_expression_sp->Execute(
+ diagnostics, exe_ctx, options, m_user_expression_sp, result_variable_sp);
+
+ bool ret;
+
+ if (result_code == eExpressionCompleted) {
+ if (!result_variable_sp) {
+ error.SetErrorString("Expression did not return a result");
+ return false;
+ }
+
+ result_value_sp = result_variable_sp->GetValueObject();
+
+ if (result_value_sp) {
+ ret = result_value_sp->IsLogicalTrue(error);
+ if (log) {
+ if (error.Success()) {
+ log->Printf("Condition successfully evaluated, result is %s.\n",
+ ret ? "true" : "false");
+ } else {
+ error.SetErrorString(
+ "Failed to get an integer result from the expression");
+ ret = false;
+ }
+ }
+ } else {
+ ret = false;
+ error.SetErrorString("Failed to get any result from the expression");
+ }
+ } else {
+ ret = false;
+ error.SetErrorStringWithFormat("Couldn't execute expression:\n%s",
+ diagnostics.GetString().c_str());
+ }
+
+ return ret;
+}
+
+uint32_t BreakpointLocation::GetIgnoreCount() {
+ return GetOptionsSpecifyingKind(BreakpointOptions::eIgnoreCount)
+ ->GetIgnoreCount();
+}
+
+void BreakpointLocation::SetIgnoreCount(uint32_t n) {
+ GetLocationOptions()->SetIgnoreCount(n);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeIgnoreChanged);
+}
+
+void BreakpointLocation::DecrementIgnoreCount() {
+ if (m_options_up != nullptr) {
+ uint32_t loc_ignore = m_options_up->GetIgnoreCount();
+ if (loc_ignore != 0)
+ m_options_up->SetIgnoreCount(loc_ignore - 1);
+ }
+}
+
+bool BreakpointLocation::IgnoreCountShouldStop() {
+ if (m_options_up != nullptr) {
+ uint32_t loc_ignore = m_options_up->GetIgnoreCount();
+ if (loc_ignore != 0) {
+ m_owner.DecrementIgnoreCount();
+ DecrementIgnoreCount(); // Have to decrement our owners' ignore count,
+ // since it won't get a
+ // chance to.
+ return false;
+ }
+ }
+ return true;
+}
+
+BreakpointOptions *BreakpointLocation::GetLocationOptions() {
+ // If we make the copy we don't copy the callbacks because that is
+ // potentially expensive and we don't want to do that for the simple case
+ // where someone is just disabling the location.
+ if (m_options_up == nullptr)
+ m_options_up.reset(new BreakpointOptions(false));
+
+ return m_options_up.get();
+}
+
+bool BreakpointLocation::ValidForThisThread(Thread *thread) {
+ return thread
+ ->MatchesSpec(GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ ->GetThreadSpecNoCreate());
+}
+
+// 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 should_stop = true;
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS);
+
+ // Do this first, if a location is disabled, it shouldn't increment its hit
+ // count.
+ if (!IsEnabled())
+ return false;
+
+ if (!IgnoreCountShouldStop())
+ return false;
+
+ if (!m_owner.IgnoreCountShouldStop())
+ return false;
+
+ // We only run synchronous callbacks in ShouldStop:
+ context->is_synchronous = true;
+ should_stop = InvokeCallback(context);
+
+ if (log) {
+ StreamString s;
+ GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ log->Printf("Hit breakpoint location: %s, %s.\n", s.GetData(),
+ should_stop ? "stopping" : "continuing");
+ }
+
+ return should_stop;
+}
+
+void BreakpointLocation::BumpHitCount() {
+ if (IsEnabled()) {
+ // Step our hit count, and also step the hit count of the owner.
+ IncrementHitCount();
+ m_owner.IncrementHitCount();
+ }
+}
+
+void BreakpointLocation::UndoBumpHitCount() {
+ if (IsEnabled()) {
+ // Step our hit count, and also step the hit count of the owner.
+ DecrementHitCount();
+ m_owner.DecrementHitCount();
+ }
+}
+
+bool BreakpointLocation::IsResolved() const {
+ return m_bp_site_sp.get() != nullptr;
+}
+
+lldb::BreakpointSiteSP BreakpointLocation::GetBreakpointSite() const {
+ return m_bp_site_sp;
+}
+
+bool BreakpointLocation::ResolveBreakpointSite() {
+ if (m_bp_site_sp)
+ return true;
+
+ Process *process = m_owner.GetTarget().GetProcessSP().get();
+ if (process == nullptr)
+ return false;
+
+ lldb::break_id_t new_id =
+ process->CreateBreakpointSite(shared_from_this(), m_owner.IsHardware());
+
+ if (new_id == LLDB_INVALID_BREAK_ID) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS);
+ if (log)
+ log->Warning("Failed to add breakpoint site at 0x%" PRIx64,
+ m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()));
+ }
+
+ return IsResolved();
+}
+
+bool BreakpointLocation::SetBreakpointSite(BreakpointSiteSP &bp_site_sp) {
+ m_bp_site_sp = bp_site_sp;
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeLocationsResolved);
+ return true;
+}
+
+bool BreakpointLocation::ClearBreakpointSite() {
+ if (m_bp_site_sp.get()) {
+ ProcessSP process_sp(m_owner.GetTarget().GetProcessSP());
+ // 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
+ // owners. Otherwise just remove this owner.
+ if (process_sp)
+ process_sp->RemoveOwnerFromBreakpointSite(GetBreakpoint().GetID(),
+ GetID(), m_bp_site_sp);
+ else
+ m_bp_site_sp->RemoveOwner(GetBreakpoint().GetID(), GetID());
+
+ m_bp_site_sp.reset();
+ return true;
+ }
+ return false;
+}
+
+void BreakpointLocation::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ SymbolContext sc;
+
+ // 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.
+ if (level != eDescriptionLevelInitial) {
+ s->Indent();
+ BreakpointID::GetCanonicalReference(s, m_owner.GetID(), GetID());
+ }
+
+ if (level == lldb::eDescriptionLevelBrief)
+ return;
+
+ if (level != eDescriptionLevelInitial)
+ s->PutCString(": ");
+
+ if (level == lldb::eDescriptionLevelVerbose)
+ s->IndentMore();
+
+ if (m_address.IsSectionOffset()) {
+ m_address.CalculateSymbolContext(&sc);
+
+ if (level == lldb::eDescriptionLevelFull ||
+ level == eDescriptionLevelInitial) {
+ if (IsReExported())
+ s->PutCString("re-exported target = ");
+ else
+ s->PutCString("where = ");
+ sc.DumpStopContext(s, m_owner.GetTarget().GetProcessSP().get(), m_address,
+ false, true, false, true, true);
+ } else {
+ if (sc.module_sp) {
+ s->EOL();
+ s->Indent("module = ");
+ sc.module_sp->GetFileSpec().Dump(s);
+ }
+
+ if (sc.comp_unit != nullptr) {
+ s->EOL();
+ s->Indent("compile unit = ");
+ static_cast<FileSpec *>(sc.comp_unit)->GetFilename().Dump(s);
+
+ if (sc.function != nullptr) {
+ s->EOL();
+ s->Indent("function = ");
+ s->PutCString(sc.function->GetName().AsCString("<unknown>"));
+ }
+
+ if (sc.line_entry.line > 0) {
+ s->EOL();
+ s->Indent("location = ");
+ sc.line_entry.DumpStopContext(s, true);
+ }
+
+ } else {
+ // If we don't have a comp unit, see if we have a symbol we can print.
+ if (sc.symbol) {
+ s->EOL();
+ if (IsReExported())
+ s->Indent("re-exported target = ");
+ else
+ s->Indent("symbol = ");
+ s->PutCString(sc.symbol->GetName().AsCString("<unknown>"));
+ }
+ }
+ }
+ }
+
+ if (level == lldb::eDescriptionLevelVerbose) {
+ s->EOL();
+ 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();
+ }
+ s->Printf("indirect target = %s",
+ resolved_symbol->GetName().GetCString());
+ }
+ }
+
+ if (level == lldb::eDescriptionLevelVerbose) {
+ s->EOL();
+ s->Indent();
+ s->Printf("resolved = %s\n", IsResolved() ? "true" : "false");
+
+ s->Indent();
+ s->Printf("hit count = %-4u\n", GetHitCount());
+
+ if (m_options_up) {
+ s->Indent();
+ m_options_up->GetDescription(s, level);
+ s->EOL();
+ }
+ s->IndentLess();
+ } else if (level != eDescriptionLevelInitial) {
+ s->Printf(", %sresolved, hit count = %u ", (IsResolved() ? "" : "un"),
+ GetHitCount());
+ if (m_options_up) {
+ m_options_up->GetDescription(s, level);
+ }
+ }
+}
+
+void BreakpointLocation::Dump(Stream *s) const {
+ if (s == nullptr)
+ return;
+
+ lldb::tid_t tid = GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ ->GetThreadSpecNoCreate()->GetTID();
+ s->Printf("BreakpointLocation %u: tid = %4.4" PRIx64
+ " load addr = 0x%8.8" PRIx64 " state = %s type = %s breakpoint "
+ "hw_index = %i hit_count = %-4u ignore_count = %-4u",
+ GetID(), tid,
+ (uint64_t)m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()),
+ (m_options_up ? m_options_up->IsEnabled() : m_owner.IsEnabled())
+ ? "enabled "
+ : "disabled",
+ IsHardware() ? "hardware" : "software", GetHardwareIndex(),
+ GetHitCount(),
+ GetOptionsSpecifyingKind(BreakpointOptions::eIgnoreCount)
+ ->GetIgnoreCount());
+}
+
+void BreakpointLocation::SendBreakpointLocationChangedEvent(
+ lldb::BreakpointEventType eventKind) {
+ if (!m_being_created && !m_owner.IsInternal() &&
+ m_owner.GetTarget().EventTypeHasListeners(
+ Target::eBroadcastBitBreakpointChanged)) {
+ Breakpoint::BreakpointEventData *data = new Breakpoint::BreakpointEventData(
+ eventKind, m_owner.shared_from_this());
+ data->GetBreakpointLocationCollection().Add(shared_from_this());
+ m_owner.GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged,
+ data);
+ }
+}
+
+void BreakpointLocation::SwapLocation(BreakpointLocationSP swap_from) {
+ m_address = swap_from->m_address;
+ m_should_resolve_indirect_functions =
+ swap_from->m_should_resolve_indirect_functions;
+ m_is_reexported = swap_from->m_is_reexported;
+ m_is_indirect = swap_from->m_is_indirect;
+ m_user_expression_sp.reset();
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationCollection.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationCollection.cpp
new file mode 100644
index 000000000000..76084adbd2aa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationCollection.cpp
@@ -0,0 +1,187 @@
+//===-- BreakpointLocationCollection.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointLocationCollection.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// BreakpointLocationCollection constructor
+BreakpointLocationCollection::BreakpointLocationCollection()
+ : m_break_loc_collection(), m_collection_mutex() {}
+
+// Destructor
+BreakpointLocationCollection::~BreakpointLocationCollection() {}
+
+void BreakpointLocationCollection::Add(const BreakpointLocationSP &bp_loc) {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ BreakpointLocationSP old_bp_loc =
+ FindByIDPair(bp_loc->GetBreakpoint().GetID(), bp_loc->GetID());
+ if (!old_bp_loc.get())
+ m_break_loc_collection.push_back(bp_loc);
+}
+
+bool BreakpointLocationCollection::Remove(lldb::break_id_t bp_id,
+ lldb::break_id_t bp_loc_id) {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ collection::iterator pos = GetIDPairIterator(bp_id, bp_loc_id); // Predicate
+ if (pos != m_break_loc_collection.end()) {
+ m_break_loc_collection.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+class BreakpointIDPairMatches {
+public:
+ BreakpointIDPairMatches(lldb::break_id_t break_id,
+ lldb::break_id_t break_loc_id)
+ : m_break_id(break_id), m_break_loc_id(break_loc_id) {}
+
+ bool operator()(const BreakpointLocationSP &bp_loc) const {
+ return m_break_id == bp_loc->GetBreakpoint().GetID() &&
+ m_break_loc_id == bp_loc->GetID();
+ }
+
+private:
+ const lldb::break_id_t m_break_id;
+ const lldb::break_id_t m_break_loc_id;
+};
+
+BreakpointLocationCollection::collection::iterator
+BreakpointLocationCollection::GetIDPairIterator(lldb::break_id_t break_id,
+ lldb::break_id_t break_loc_id) {
+ return std::find_if(
+ m_break_loc_collection.begin(),
+ m_break_loc_collection.end(), // Search full range
+ BreakpointIDPairMatches(break_id, break_loc_id)); // Predicate
+}
+
+BreakpointLocationCollection::collection::const_iterator
+BreakpointLocationCollection::GetIDPairConstIterator(
+ lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const {
+ return std::find_if(
+ m_break_loc_collection.begin(),
+ m_break_loc_collection.end(), // Search full range
+ BreakpointIDPairMatches(break_id, break_loc_id)); // Predicate
+}
+
+BreakpointLocationSP
+BreakpointLocationCollection::FindByIDPair(lldb::break_id_t break_id,
+ lldb::break_id_t break_loc_id) {
+ BreakpointLocationSP stop_sp;
+ collection::iterator pos = GetIDPairIterator(break_id, break_loc_id);
+ if (pos != m_break_loc_collection.end())
+ stop_sp = *pos;
+
+ return stop_sp;
+}
+
+const BreakpointLocationSP BreakpointLocationCollection::FindByIDPair(
+ lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const {
+ BreakpointLocationSP stop_sp;
+ collection::const_iterator pos =
+ GetIDPairConstIterator(break_id, break_loc_id);
+ if (pos != m_break_loc_collection.end())
+ stop_sp = *pos;
+
+ return stop_sp;
+}
+
+BreakpointLocationSP BreakpointLocationCollection::GetByIndex(size_t i) {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ BreakpointLocationSP stop_sp;
+ if (i < m_break_loc_collection.size())
+ stop_sp = m_break_loc_collection[i];
+
+ return stop_sp;
+}
+
+const BreakpointLocationSP
+BreakpointLocationCollection::GetByIndex(size_t i) const {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ BreakpointLocationSP stop_sp;
+ if (i < m_break_loc_collection.size())
+ stop_sp = m_break_loc_collection[i];
+
+ return stop_sp;
+}
+
+bool BreakpointLocationCollection::ShouldStop(
+ StoppointCallbackContext *context) {
+ bool shouldStop = false;
+ size_t i = 0;
+ size_t prev_size = GetSize();
+ while (i < prev_size) {
+ // ShouldStop can remove the breakpoint from the list
+ if (GetByIndex(i)->ShouldStop(context))
+ shouldStop = true;
+
+ if (prev_size == GetSize())
+ i++;
+ prev_size = GetSize();
+ }
+ return shouldStop;
+}
+
+bool BreakpointLocationCollection::ValidForThisThread(Thread *thread) {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ collection::iterator pos, begin = m_break_loc_collection.begin(),
+ end = m_break_loc_collection.end();
+
+ for (pos = begin; pos != end; ++pos) {
+ if ((*pos)->ValidForThisThread(thread))
+ return true;
+ }
+ return false;
+}
+
+bool BreakpointLocationCollection::IsInternal() const {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ collection::const_iterator pos, begin = m_break_loc_collection.begin(),
+ end = m_break_loc_collection.end();
+
+ bool is_internal = true;
+
+ for (pos = begin; pos != end; ++pos) {
+ if (!(*pos)->GetBreakpoint().IsInternal()) {
+ is_internal = false;
+ break;
+ }
+ }
+ return is_internal;
+}
+
+void BreakpointLocationCollection::GetDescription(
+ Stream *s, lldb::DescriptionLevel level) {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ collection::iterator pos, begin = m_break_loc_collection.begin(),
+ end = m_break_loc_collection.end();
+
+ for (pos = begin; pos != end; ++pos) {
+ if (pos != begin)
+ s->PutChar(' ');
+ (*pos)->GetDescription(s, level);
+ }
+}
+
+BreakpointLocationCollection &BreakpointLocationCollection::operator=(
+ const BreakpointLocationCollection &rhs) {
+ if (this != &rhs) {
+ std::lock(m_collection_mutex, rhs.m_collection_mutex);
+ std::lock_guard<std::mutex> lhs_guard(m_collection_mutex, std::adopt_lock);
+ std::lock_guard<std::mutex> rhs_guard(rhs.m_collection_mutex, std::adopt_lock);
+ m_break_loc_collection = rhs.m_break_loc_collection;
+ }
+ return *this;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationList.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationList.cpp
new file mode 100644
index 000000000000..ee586127ee78
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationList.cpp
@@ -0,0 +1,312 @@
+//===-- BreakpointLocationList.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointLocationList.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointLocationList::BreakpointLocationList(Breakpoint &owner)
+ : m_owner(owner), m_locations(), m_address_to_location(), m_mutex(),
+ m_next_id(0), m_new_location_recorder(nullptr) {}
+
+BreakpointLocationList::~BreakpointLocationList() = default;
+
+BreakpointLocationSP
+BreakpointLocationList::Create(const Address &addr,
+ bool resolve_indirect_symbols) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ // The location ID is just the size of the location list + 1
+ lldb::break_id_t bp_loc_id = ++m_next_id;
+ BreakpointLocationSP bp_loc_sp(
+ new BreakpointLocation(bp_loc_id, m_owner, addr, LLDB_INVALID_THREAD_ID,
+ m_owner.IsHardware(), resolve_indirect_symbols));
+ m_locations.push_back(bp_loc_sp);
+ m_address_to_location[addr] = bp_loc_sp;
+ return bp_loc_sp;
+}
+
+bool BreakpointLocationList::ShouldStop(StoppointCallbackContext *context,
+ lldb::break_id_t break_id) {
+ 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);
+ }
+ // We should stop here since this BreakpointLocation isn't valid anymore or
+ // it doesn't exist.
+ return true;
+}
+
+lldb::break_id_t BreakpointLocationList::FindIDByAddress(const Address &addr) {
+ BreakpointLocationSP bp_loc_sp = FindByAddress(addr);
+ if (bp_loc_sp) {
+ return bp_loc_sp->GetID();
+ }
+ return LLDB_INVALID_BREAK_ID;
+}
+
+static bool Compare(BreakpointLocationSP lhs, lldb::break_id_t val) {
+ return lhs->GetID() < val;
+}
+
+BreakpointLocationSP
+BreakpointLocationList::FindByID(lldb::break_id_t break_id) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::const_iterator end = m_locations.end();
+ collection::const_iterator pos =
+ std::lower_bound(m_locations.begin(), end, break_id, Compare);
+ if (pos != end && (*pos)->GetID() == break_id)
+ return *(pos);
+ else
+ return BreakpointLocationSP();
+}
+
+size_t BreakpointLocationList::FindInModule(
+ Module *module, BreakpointLocationCollection &bp_loc_list) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ const size_t orig_size = bp_loc_list.GetSize();
+ collection::iterator pos, end = m_locations.end();
+
+ for (pos = m_locations.begin(); pos != end; ++pos) {
+ BreakpointLocationSP break_loc = (*pos);
+ SectionSP section_sp(break_loc->GetAddress().GetSection());
+ if (section_sp && section_sp->GetModule().get() == module) {
+ bp_loc_list.Add(break_loc);
+ }
+ }
+ return bp_loc_list.GetSize() - orig_size;
+}
+
+const BreakpointLocationSP
+BreakpointLocationList::FindByAddress(const Address &addr) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ BreakpointLocationSP bp_loc_sp;
+ if (!m_locations.empty()) {
+ Address so_addr;
+
+ if (addr.IsSectionOffset()) {
+ so_addr = addr;
+ } else {
+ // Try and resolve as a load address if possible.
+ m_owner.GetTarget().GetSectionLoadList().ResolveLoadAddress(
+ addr.GetOffset(), so_addr);
+ if (!so_addr.IsValid()) {
+ // The address didn't resolve, so just set to passed in addr.
+ so_addr = addr;
+ }
+ }
+
+ addr_map::const_iterator pos = m_address_to_location.find(so_addr);
+ if (pos != m_address_to_location.end())
+ bp_loc_sp = pos->second;
+ }
+
+ return bp_loc_sp;
+}
+
+void BreakpointLocationList::Dump(Stream *s) const {
+ s->Printf("%p: ", static_cast<const void *>(this));
+ // s->Indent();
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ s->Printf("BreakpointLocationList with %" PRIu64 " BreakpointLocations:\n",
+ (uint64_t)m_locations.size());
+ s->IndentMore();
+ collection::const_iterator pos, end = m_locations.end();
+ for (pos = m_locations.begin(); pos != end; ++pos)
+ (*pos)->Dump(s);
+ s->IndentLess();
+}
+
+BreakpointLocationSP BreakpointLocationList::GetByIndex(size_t i) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ BreakpointLocationSP bp_loc_sp;
+ if (i < m_locations.size())
+ bp_loc_sp = m_locations[i];
+
+ return bp_loc_sp;
+}
+
+const BreakpointLocationSP BreakpointLocationList::GetByIndex(size_t i) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ BreakpointLocationSP bp_loc_sp;
+ if (i < m_locations.size())
+ bp_loc_sp = m_locations[i];
+
+ return bp_loc_sp;
+}
+
+void BreakpointLocationList::ClearAllBreakpointSites() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::iterator pos, end = m_locations.end();
+ for (pos = m_locations.begin(); pos != end; ++pos)
+ (*pos)->ClearBreakpointSite();
+}
+
+void BreakpointLocationList::ResolveAllBreakpointSites() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::iterator pos, end = m_locations.end();
+
+ for (pos = m_locations.begin(); pos != end; ++pos) {
+ if ((*pos)->IsEnabled())
+ (*pos)->ResolveBreakpointSite();
+ }
+}
+
+uint32_t BreakpointLocationList::GetHitCount() const {
+ uint32_t hit_count = 0;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::const_iterator pos, end = m_locations.end();
+ for (pos = m_locations.begin(); pos != end; ++pos)
+ hit_count += (*pos)->GetHitCount();
+ return hit_count;
+}
+
+size_t BreakpointLocationList::GetNumResolvedLocations() const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ size_t resolve_count = 0;
+ collection::const_iterator pos, end = m_locations.end();
+ for (pos = m_locations.begin(); pos != end; ++pos) {
+ if ((*pos)->IsResolved())
+ ++resolve_count;
+ }
+ return resolve_count;
+}
+
+void BreakpointLocationList::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::iterator pos, end = m_locations.end();
+
+ for (pos = m_locations.begin(); pos != end; ++pos) {
+ s->Printf(" ");
+ (*pos)->GetDescription(s, level);
+ }
+}
+
+BreakpointLocationSP BreakpointLocationList::AddLocation(
+ const Address &addr, bool resolve_indirect_symbols, bool *new_location) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ if (new_location)
+ *new_location = false;
+ BreakpointLocationSP bp_loc_sp(FindByAddress(addr));
+ if (!bp_loc_sp) {
+ bp_loc_sp = Create(addr, resolve_indirect_symbols);
+ if (bp_loc_sp) {
+ bp_loc_sp->ResolveBreakpointSite();
+
+ if (new_location)
+ *new_location = true;
+ if (m_new_location_recorder) {
+ m_new_location_recorder->Add(bp_loc_sp);
+ }
+ }
+ }
+ return bp_loc_sp;
+}
+
+void BreakpointLocationList::SwapLocation(
+ BreakpointLocationSP to_location_sp,
+ BreakpointLocationSP from_location_sp) {
+ if (!from_location_sp || !to_location_sp)
+ return;
+
+ m_address_to_location.erase(to_location_sp->GetAddress());
+ to_location_sp->SwapLocation(from_location_sp);
+ RemoveLocation(from_location_sp);
+ m_address_to_location[to_location_sp->GetAddress()] = to_location_sp;
+ to_location_sp->ResolveBreakpointSite();
+}
+
+bool BreakpointLocationList::RemoveLocation(
+ const lldb::BreakpointLocationSP &bp_loc_sp) {
+ if (bp_loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ m_address_to_location.erase(bp_loc_sp->GetAddress());
+
+ size_t num_locations = m_locations.size();
+ for (size_t idx = 0; idx < num_locations; idx++) {
+ if (m_locations[idx].get() == bp_loc_sp.get()) {
+ RemoveLocationByIndex(idx);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void BreakpointLocationList::RemoveLocationByIndex(size_t idx) {
+ assert (idx < m_locations.size());
+ m_address_to_location.erase(m_locations[idx]->GetAddress());
+ m_locations.erase(m_locations.begin() + idx);
+}
+
+void BreakpointLocationList::RemoveInvalidLocations(const ArchSpec &arch) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ size_t idx = 0;
+ // Don't cache m_location.size() as it will change since we might remove
+ // locations from our vector...
+ while (idx < m_locations.size()) {
+ BreakpointLocation *bp_loc = m_locations[idx].get();
+ if (bp_loc->GetAddress().SectionWasDeleted()) {
+ // Section was deleted which means this breakpoint comes from a module
+ // that is no longer valid, so we should remove it.
+ RemoveLocationByIndex(idx);
+ continue;
+ }
+ if (arch.IsValid()) {
+ ModuleSP module_sp(bp_loc->GetAddress().GetModule());
+ if (module_sp) {
+ if (!arch.IsCompatibleMatch(module_sp->GetArchitecture())) {
+ // The breakpoint was in a module whose architecture is no longer
+ // compatible with "arch", so we need to remove it
+ RemoveLocationByIndex(idx);
+ continue;
+ }
+ }
+ }
+ // Only increment the index if we didn't remove the locations at index
+ // "idx"
+ ++idx;
+ }
+}
+
+void BreakpointLocationList::StartRecordingNewLocations(
+ BreakpointLocationCollection &new_locations) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ assert(m_new_location_recorder == nullptr);
+ m_new_location_recorder = &new_locations;
+}
+
+void BreakpointLocationList::StopRecordingNewLocations() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_new_location_recorder = nullptr;
+}
+
+void BreakpointLocationList::Compact() {
+ lldb::break_id_t highest_id = 0;
+
+ for (BreakpointLocationSP loc_sp : m_locations) {
+ lldb::break_id_t cur_id = loc_sp->GetID();
+ if (cur_id > highest_id)
+ highest_id = cur_id;
+ }
+ m_next_id = highest_id;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointName.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointName.cpp
new file mode 100644
index 000000000000..749fa86bca9d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointName.cpp
@@ -0,0 +1,86 @@
+//===-- BreakpointName.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Casting.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointOptions.h"
+#include "lldb/Breakpoint/BreakpointLocationCollection.h"
+#include "lldb/Breakpoint/BreakpointResolver.h"
+#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const Flags::ValueType BreakpointName::Permissions::permissions_mask
+ [BreakpointName::Permissions::PermissionKinds::allPerms + 1] = {
+ (1u << 0),
+ (1u << 1),
+ (1u << 2),
+ (0x5u)
+};
+
+BreakpointName::BreakpointName(ConstString name, const Breakpoint &bkpt,
+ const char *help) :
+ m_name(name), m_options(bkpt.GetOptions())
+{
+ SetHelp(help);
+}
+
+bool BreakpointName::Permissions::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ if (!AnySet())
+ return false;
+ s->IndentMore();
+ s->Indent();
+ if (IsSet(listPerm))
+ s->Printf("list: %s", GetAllowList() ? "allowed" : "disallowed");
+
+ if (IsSet(disablePerm))
+ s->Printf("disable: %s", GetAllowDisable() ? "allowed" : "disallowed");
+
+ if (IsSet(deletePerm))
+ s->Printf("delete: %s", GetAllowDelete() ? "allowed" : "disallowed");
+ s->IndentLess();
+ return true;
+}
+
+bool BreakpointName::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ bool printed_any = false;
+ if (!m_help.empty())
+ s->Printf("Help: %s\n", m_help.c_str());
+
+ if (GetOptions().AnySet())
+ {
+ s->PutCString("Options: \n");
+ s->IndentMore();
+ s->Indent();
+ GetOptions().GetDescription(s, level);
+ printed_any = true;
+ s->IndentLess();
+ }
+ if (GetPermissions().AnySet())
+ {
+ s->PutCString("Permissions: \n");
+ s->IndentMore();
+ s->Indent();
+ GetPermissions().GetDescription(s, level);
+ printed_any = true;
+ s->IndentLess();
+ }
+ return printed_any;
+}
+
+void BreakpointName::ConfigureBreakpoint(lldb::BreakpointSP bp_sp)
+{
+ bp_sp->GetOptions()->CopyOverSetOptions(GetOptions());
+ bp_sp->GetPermissions().MergeInto(GetPermissions());
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointOptions.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointOptions.cpp
new file mode 100644
index 000000000000..f6f279dc382a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointOptions.cpp
@@ -0,0 +1,674 @@
+//===-- BreakpointOptions.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointOptions.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char
+ *BreakpointOptions::CommandData::g_option_names[static_cast<uint32_t>(
+ BreakpointOptions::CommandData::OptionNames::LastOptionName)]{
+ "UserSource", "ScriptSource", "StopOnError"};
+
+StructuredData::ObjectSP
+BreakpointOptions::CommandData::SerializeToStructuredData() {
+ size_t num_strings = user_source.GetSize();
+ if (num_strings == 0 && script_source.empty()) {
+ // We shouldn't serialize commands if there aren't any, return an empty sp
+ // to indicate this.
+ return StructuredData::ObjectSP();
+ }
+
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::StopOnError),
+ stop_on_error);
+
+ StructuredData::ArraySP user_source_sp(new StructuredData::Array());
+ for (size_t i = 0; i < num_strings; i++) {
+ StructuredData::StringSP item_sp(
+ new StructuredData::String(user_source[i]));
+ user_source_sp->AddItem(item_sp);
+ options_dict_sp->AddItem(GetKey(OptionNames::UserSource), user_source_sp);
+ }
+
+ options_dict_sp->AddStringItem(
+ GetKey(OptionNames::Interpreter),
+ ScriptInterpreter::LanguageToString(interpreter));
+ return options_dict_sp;
+}
+
+std::unique_ptr<BreakpointOptions::CommandData>
+BreakpointOptions::CommandData::CreateFromStructuredData(
+ const StructuredData::Dictionary &options_dict, Status &error) {
+ std::unique_ptr<CommandData> data_up(new CommandData());
+ bool found_something = false;
+
+ bool success = options_dict.GetValueForKeyAsBoolean(
+ GetKey(OptionNames::StopOnError), data_up->stop_on_error);
+
+ if (success)
+ found_something = true;
+
+ llvm::StringRef interpreter_str;
+ ScriptLanguage interp_language;
+ success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::Interpreter), interpreter_str);
+
+ if (!success) {
+ error.SetErrorString("Missing command language value.");
+ return data_up;
+ }
+
+ found_something = true;
+ interp_language = ScriptInterpreter::StringToLanguage(interpreter_str);
+ if (interp_language == eScriptLanguageUnknown) {
+ error.SetErrorStringWithFormatv("Unknown breakpoint command language: {0}.",
+ interpreter_str);
+ return data_up;
+ }
+ data_up->interpreter = interp_language;
+
+ StructuredData::Array *user_source;
+ success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource),
+ user_source);
+ if (success) {
+ found_something = true;
+ size_t num_elems = user_source->GetSize();
+ for (size_t i = 0; i < num_elems; i++) {
+ llvm::StringRef elem_string;
+ success = user_source->GetItemAtIndexAsString(i, elem_string);
+ if (success)
+ data_up->user_source.AppendString(elem_string);
+ }
+ }
+
+ if (found_something)
+ return data_up;
+ else
+ return std::unique_ptr<BreakpointOptions::CommandData>();
+}
+
+const char *BreakpointOptions::g_option_names[(
+ size_t)BreakpointOptions::OptionNames::LastOptionName]{
+ "ConditionText", "IgnoreCount",
+ "EnabledState", "OneShotState", "AutoContinue"};
+
+bool BreakpointOptions::NullCallback(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ return true;
+}
+
+// BreakpointOptions constructor
+BreakpointOptions::BreakpointOptions(bool all_flags_set)
+ : m_callback(BreakpointOptions::NullCallback), m_callback_baton_sp(),
+ m_baton_is_command_baton(false), m_callback_is_synchronous(false),
+ m_enabled(true), m_one_shot(false), m_ignore_count(0), m_thread_spec_up(),
+ m_condition_text(), m_condition_text_hash(0), m_auto_continue(false),
+ m_set_flags(0) {
+ if (all_flags_set)
+ m_set_flags.Set(~((Flags::ValueType)0));
+}
+
+BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,
+ int32_t ignore, bool one_shot,
+ bool auto_continue)
+ : m_callback(nullptr), m_baton_is_command_baton(false),
+ m_callback_is_synchronous(false), m_enabled(enabled),
+ m_one_shot(one_shot), m_ignore_count(ignore),
+ m_condition_text_hash(0), m_auto_continue(auto_continue)
+{
+ m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot
+ | eAutoContinue);
+ if (condition && *condition != '\0') {
+ SetCondition(condition);
+ }
+}
+
+// BreakpointOptions copy constructor
+BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs)
+ : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp),
+ m_baton_is_command_baton(rhs.m_baton_is_command_baton),
+ m_callback_is_synchronous(rhs.m_callback_is_synchronous),
+ m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot),
+ m_ignore_count(rhs.m_ignore_count), m_thread_spec_up(),
+ m_auto_continue(rhs.m_auto_continue), m_set_flags(rhs.m_set_flags) {
+ if (rhs.m_thread_spec_up != nullptr)
+ m_thread_spec_up.reset(new ThreadSpec(*rhs.m_thread_spec_up));
+ m_condition_text = rhs.m_condition_text;
+ m_condition_text_hash = rhs.m_condition_text_hash;
+}
+
+// BreakpointOptions assignment operator
+const BreakpointOptions &BreakpointOptions::
+operator=(const BreakpointOptions &rhs) {
+ m_callback = rhs.m_callback;
+ m_callback_baton_sp = rhs.m_callback_baton_sp;
+ m_baton_is_command_baton = rhs.m_baton_is_command_baton;
+ m_callback_is_synchronous = rhs.m_callback_is_synchronous;
+ m_enabled = rhs.m_enabled;
+ m_one_shot = rhs.m_one_shot;
+ m_ignore_count = rhs.m_ignore_count;
+ if (rhs.m_thread_spec_up != nullptr)
+ m_thread_spec_up.reset(new ThreadSpec(*rhs.m_thread_spec_up));
+ m_condition_text = rhs.m_condition_text;
+ m_condition_text_hash = rhs.m_condition_text_hash;
+ m_auto_continue = rhs.m_auto_continue;
+ m_set_flags = rhs.m_set_flags;
+ return *this;
+}
+
+void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming)
+{
+ if (incoming.m_set_flags.Test(eEnabled))
+ {
+ m_enabled = incoming.m_enabled;
+ m_set_flags.Set(eEnabled);
+ }
+ if (incoming.m_set_flags.Test(eOneShot))
+ {
+ m_one_shot = incoming.m_one_shot;
+ m_set_flags.Set(eOneShot);
+ }
+ if (incoming.m_set_flags.Test(eCallback))
+ {
+ m_callback = incoming.m_callback;
+ m_callback_baton_sp = incoming.m_callback_baton_sp;
+ m_callback_is_synchronous = incoming.m_callback_is_synchronous;
+ m_baton_is_command_baton = incoming.m_baton_is_command_baton;
+ m_set_flags.Set(eCallback);
+ }
+ if (incoming.m_set_flags.Test(eIgnoreCount))
+ {
+ m_ignore_count = incoming.m_ignore_count;
+ m_set_flags.Set(eIgnoreCount);
+ }
+ if (incoming.m_set_flags.Test(eCondition))
+ {
+ // If we're copying over an empty condition, mark it as unset.
+ if (incoming.m_condition_text.empty()) {
+ m_condition_text.clear();
+ m_condition_text_hash = 0;
+ m_set_flags.Clear(eCondition);
+ } else {
+ m_condition_text = incoming.m_condition_text;
+ m_condition_text_hash = incoming.m_condition_text_hash;
+ m_set_flags.Set(eCondition);
+ }
+ }
+ if (incoming.m_set_flags.Test(eAutoContinue))
+ {
+ m_auto_continue = incoming.m_auto_continue;
+ m_set_flags.Set(eAutoContinue);
+ }
+ if (incoming.m_set_flags.Test(eThreadSpec) && incoming.m_thread_spec_up) {
+ if (!m_thread_spec_up)
+ m_thread_spec_up.reset(new ThreadSpec(*incoming.m_thread_spec_up));
+ else
+ *m_thread_spec_up = *incoming.m_thread_spec_up;
+ m_set_flags.Set(eThreadSpec);
+ }
+}
+
+// Destructor
+BreakpointOptions::~BreakpointOptions() = default;
+
+std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &options_dict,
+ Status &error) {
+ bool enabled = true;
+ bool one_shot = false;
+ bool auto_continue = false;
+ int32_t ignore_count = 0;
+ llvm::StringRef condition_ref("");
+ Flags set_options;
+
+ const char *key = GetKey(OptionNames::EnabledState);
+ bool success;
+ if (key && options_dict.HasKey(key)) {
+ success = options_dict.GetValueForKeyAsBoolean(key, enabled);
+ if (!success) {
+ error.SetErrorStringWithFormat("%s key is not a boolean.", key);
+ return nullptr;
+ }
+ set_options.Set(eEnabled);
+ }
+
+ key = GetKey(OptionNames::OneShotState);
+ if (key && options_dict.HasKey(key)) {
+ success = options_dict.GetValueForKeyAsBoolean(key, one_shot);
+ if (!success) {
+ error.SetErrorStringWithFormat("%s key is not a boolean.", key);
+ return nullptr;
+ }
+ set_options.Set(eOneShot);
+ }
+
+ key = GetKey(OptionNames::AutoContinue);
+ if (key && options_dict.HasKey(key)) {
+ success = options_dict.GetValueForKeyAsBoolean(key, auto_continue);
+ if (!success) {
+ error.SetErrorStringWithFormat("%s key is not a boolean.", key);
+ return nullptr;
+ }
+ set_options.Set(eAutoContinue);
+ }
+
+ key = GetKey(OptionNames::IgnoreCount);
+ if (key && options_dict.HasKey(key)) {
+ success = options_dict.GetValueForKeyAsInteger(key, ignore_count);
+ if (!success) {
+ error.SetErrorStringWithFormat("%s key is not an integer.", key);
+ return nullptr;
+ }
+ set_options.Set(eIgnoreCount);
+ }
+
+ key = GetKey(OptionNames::ConditionText);
+ if (key && options_dict.HasKey(key)) {
+ success = options_dict.GetValueForKeyAsString(key, condition_ref);
+ if (!success) {
+ error.SetErrorStringWithFormat("%s key is not an string.", key);
+ return nullptr;
+ }
+ set_options.Set(eCondition);
+ }
+
+ std::unique_ptr<CommandData> cmd_data_up;
+ StructuredData::Dictionary *cmds_dict;
+ success = options_dict.GetValueForKeyAsDictionary(
+ CommandData::GetSerializationKey(), cmds_dict);
+ if (success && cmds_dict) {
+ Status cmds_error;
+ cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error);
+ if (cmds_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Failed to deserialize breakpoint command options: %s.",
+ cmds_error.AsCString());
+ return nullptr;
+ }
+ }
+
+ auto bp_options = llvm::make_unique<BreakpointOptions>(
+ condition_ref.str().c_str(), enabled,
+ ignore_count, one_shot, auto_continue);
+ if (cmd_data_up) {
+ if (cmd_data_up->interpreter == eScriptLanguageNone)
+ bp_options->SetCommandDataCallback(cmd_data_up);
+ else {
+ ScriptInterpreter *interp = target.GetDebugger().GetScriptInterpreter();
+ if (!interp) {
+ error.SetErrorStringWithFormat(
+ "Can't set script commands - no script interpreter");
+ return nullptr;
+ }
+ if (interp->GetLanguage() != cmd_data_up->interpreter) {
+ error.SetErrorStringWithFormat(
+ "Current script language doesn't match breakpoint's language: %s",
+ ScriptInterpreter::LanguageToString(cmd_data_up->interpreter)
+ .c_str());
+ return nullptr;
+ }
+ Status script_error;
+ script_error =
+ interp->SetBreakpointCommandCallback(bp_options.get(), cmd_data_up);
+ if (script_error.Fail()) {
+ error.SetErrorStringWithFormat("Error generating script callback: %s.",
+ error.AsCString());
+ return nullptr;
+ }
+ }
+ }
+
+ StructuredData::Dictionary *thread_spec_dict;
+ success = options_dict.GetValueForKeyAsDictionary(
+ ThreadSpec::GetSerializationKey(), thread_spec_dict);
+ if (success) {
+ Status thread_spec_error;
+ std::unique_ptr<ThreadSpec> thread_spec_up =
+ ThreadSpec::CreateFromStructuredData(*thread_spec_dict,
+ thread_spec_error);
+ if (thread_spec_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Failed to deserialize breakpoint thread spec options: %s.",
+ thread_spec_error.AsCString());
+ return nullptr;
+ }
+ bp_options->SetThreadSpec(thread_spec_up);
+ }
+ return bp_options;
+}
+
+StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+ if (m_set_flags.Test(eEnabled))
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState),
+ m_enabled);
+ if (m_set_flags.Test(eOneShot))
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),
+ m_one_shot);
+ if (m_set_flags.Test(eAutoContinue))
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue),
+ m_auto_continue);
+ if (m_set_flags.Test(eIgnoreCount))
+ options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),
+ m_ignore_count);
+ if (m_set_flags.Test(eCondition))
+ options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),
+ m_condition_text);
+
+ if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) {
+ auto cmd_baton =
+ std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
+ StructuredData::ObjectSP commands_sp =
+ cmd_baton->getItem()->SerializeToStructuredData();
+ if (commands_sp) {
+ options_dict_sp->AddItem(
+ BreakpointOptions::CommandData::GetSerializationKey(), commands_sp);
+ }
+ }
+ if (m_set_flags.Test(eThreadSpec) && m_thread_spec_up) {
+ StructuredData::ObjectSP thread_spec_sp =
+ m_thread_spec_up->SerializeToStructuredData();
+ options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp);
+ }
+
+ return options_dict_sp;
+}
+
+// Callbacks
+void BreakpointOptions::SetCallback(BreakpointHitCallback callback,
+ const lldb::BatonSP &callback_baton_sp,
+ bool callback_is_synchronous) {
+ // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but
+ // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will
+ // set m_baton_is_command_baton to false, which is incorrect. One possible
+ // solution is to make the base Baton class provide a method such as:
+ // virtual StringRef getBatonId() const { return ""; }
+ // and have CommandBaton override this to return something unique, and then
+ // check for it here. Another option might be to make Baton using the llvm
+ // casting infrastructure, so that we could write something like:
+ // if (llvm::isa<CommandBaton>(callback_baton_sp))
+ // at relevant callsites instead of storing a boolean.
+ m_callback_is_synchronous = callback_is_synchronous;
+ m_callback = callback;
+ m_callback_baton_sp = callback_baton_sp;
+ m_baton_is_command_baton = false;
+ m_set_flags.Set(eCallback);
+}
+
+void BreakpointOptions::SetCallback(
+ BreakpointHitCallback callback,
+ const BreakpointOptions::CommandBatonSP &callback_baton_sp,
+ bool callback_is_synchronous) {
+ m_callback_is_synchronous = callback_is_synchronous;
+ m_callback = callback;
+ m_callback_baton_sp = callback_baton_sp;
+ m_baton_is_command_baton = true;
+ m_set_flags.Set(eCallback);
+}
+
+void BreakpointOptions::ClearCallback() {
+ m_callback = BreakpointOptions::NullCallback;
+ m_callback_is_synchronous = false;
+ m_callback_baton_sp.reset();
+ m_baton_is_command_baton = false;
+ m_set_flags.Clear(eCallback);
+}
+
+Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); }
+
+const Baton *BreakpointOptions::GetBaton() const {
+ return m_callback_baton_sp.get();
+}
+
+bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ if (m_callback) {
+ if (context->is_synchronous == IsCallbackSynchronous()) {
+ return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()
+ : nullptr,
+ context, break_id, break_loc_id);
+ } else if (IsCallbackSynchronous()) {
+ // If a synchronous callback is called at async time, it should not say
+ // to stop.
+ return false;
+ }
+ }
+ return true;
+}
+
+bool BreakpointOptions::HasCallback() const {
+ return m_callback != BreakpointOptions::NullCallback;
+}
+
+bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {
+ if (!HasCallback())
+ return false;
+ if (!m_baton_is_command_baton)
+ return false;
+
+ auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
+ CommandData *data = cmd_baton->getItem();
+ if (!data)
+ return false;
+ command_list = data->user_source;
+ return true;
+}
+
+void BreakpointOptions::SetCondition(const char *condition) {
+ if (!condition || condition[0] == '\0') {
+ condition = "";
+ m_set_flags.Clear(eCondition);
+ }
+ else
+ m_set_flags.Set(eCondition);
+
+ m_condition_text.assign(condition);
+ std::hash<std::string> hasher;
+ m_condition_text_hash = hasher(m_condition_text);
+}
+
+const char *BreakpointOptions::GetConditionText(size_t *hash) const {
+ if (!m_condition_text.empty()) {
+ if (hash)
+ *hash = m_condition_text_hash;
+
+ return m_condition_text.c_str();
+ } else {
+ return nullptr;
+ }
+}
+
+const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {
+ return m_thread_spec_up.get();
+}
+
+ThreadSpec *BreakpointOptions::GetThreadSpec() {
+ if (m_thread_spec_up == nullptr) {
+ m_set_flags.Set(eThreadSpec);
+ m_thread_spec_up.reset(new ThreadSpec());
+ }
+
+ return m_thread_spec_up.get();
+}
+
+void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) {
+ GetThreadSpec()->SetTID(thread_id);
+ m_set_flags.Set(eThreadSpec);
+}
+
+void BreakpointOptions::SetThreadSpec(
+ std::unique_ptr<ThreadSpec> &thread_spec_up) {
+ m_thread_spec_up = std::move(thread_spec_up);
+ m_set_flags.Set(eThreadSpec);
+}
+
+void BreakpointOptions::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) const {
+ // Figure out if there are any options not at their default value, and only
+ // print anything if there are:
+
+ if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue ||
+ (GetThreadSpecNoCreate() != nullptr &&
+ GetThreadSpecNoCreate()->HasSpecification())) {
+ if (level == lldb::eDescriptionLevelVerbose) {
+ s->EOL();
+ s->IndentMore();
+ s->Indent();
+ s->PutCString("Breakpoint Options:\n");
+ s->IndentMore();
+ s->Indent();
+ } else
+ s->PutCString(" Options: ");
+
+ if (m_ignore_count > 0)
+ s->Printf("ignore: %d ", m_ignore_count);
+ s->Printf("%sabled ", m_enabled ? "en" : "dis");
+
+ if (m_one_shot)
+ s->Printf("one-shot ");
+
+ if (m_auto_continue)
+ s->Printf("auto-continue ");
+
+ if (m_thread_spec_up)
+ m_thread_spec_up->GetDescription(s, level);
+
+ if (level == lldb::eDescriptionLevelFull) {
+ s->IndentLess();
+ s->IndentMore();
+ }
+ }
+
+ if (m_callback_baton_sp.get()) {
+ if (level != eDescriptionLevelBrief) {
+ s->EOL();
+ m_callback_baton_sp->GetDescription(s, level);
+ }
+ }
+ if (!m_condition_text.empty()) {
+ if (level != eDescriptionLevelBrief) {
+ s->EOL();
+ s->Printf("Condition: %s\n", m_condition_text.c_str());
+ }
+ }
+}
+
+void BreakpointOptions::CommandBaton::GetDescription(
+ Stream *s, lldb::DescriptionLevel level) const {
+ const CommandData *data = getItem();
+
+ if (level == eDescriptionLevelBrief) {
+ s->Printf(", commands = %s",
+ (data && data->user_source.GetSize() > 0) ? "yes" : "no");
+ return;
+ }
+
+ s->IndentMore();
+ s->Indent("Breakpoint commands");
+ if (data->interpreter != eScriptLanguageNone)
+ s->Printf(" (%s):\n",
+ ScriptInterpreter::LanguageToString(data->interpreter).c_str());
+ else
+ s->PutCString(":\n");
+
+ s->IndentMore();
+ if (data && data->user_source.GetSize() > 0) {
+ const size_t num_strings = data->user_source.GetSize();
+ for (size_t i = 0; i < num_strings; ++i) {
+ s->Indent(data->user_source.GetStringAtIndex(i));
+ s->EOL();
+ }
+ } else {
+ s->PutCString("No commands.\n");
+ }
+ s->IndentLess();
+ s->IndentLess();
+}
+
+void BreakpointOptions::SetCommandDataCallback(
+ std::unique_ptr<CommandData> &cmd_data) {
+ cmd_data->interpreter = eScriptLanguageNone;
+ auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data));
+ SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp);
+ m_set_flags.Set(eCallback);
+}
+
+bool BreakpointOptions::BreakpointOptionsCallbackFunction(
+ void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ bool ret_value = true;
+ if (baton == nullptr)
+ return true;
+
+ CommandData *data = (CommandData *)baton;
+ StringList &commands = data->user_source;
+
+ if (commands.GetSize() > 0) {
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ CommandReturnObject result;
+ Debugger &debugger = target->GetDebugger();
+ // Rig up the results secondary output stream to the debugger's, so the
+ // output will come out synchronously if the debugger is set up that way.
+
+ StreamSP output_stream(debugger.GetAsyncOutputStream());
+ StreamSP error_stream(debugger.GetAsyncErrorStream());
+ result.SetImmediateOutputStream(output_stream);
+ result.SetImmediateErrorStream(error_stream);
+
+ CommandInterpreterRunOptions options;
+ options.SetStopOnContinue(true);
+ options.SetStopOnError(data->stop_on_error);
+ options.SetEchoCommands(true);
+ options.SetPrintResults(true);
+ options.SetPrintErrors(true);
+ options.SetAddToHistory(false);
+
+ debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx,
+ options, result);
+ result.GetImmediateOutputStream()->Flush();
+ result.GetImmediateErrorStream()->Flush();
+ }
+ }
+ return ret_value;
+}
+
+void BreakpointOptions::Clear()
+{
+ m_set_flags.Clear();
+ m_thread_spec_up.release();
+ m_one_shot = false;
+ m_ignore_count = 0;
+ m_auto_continue = false;
+ m_callback = nullptr;
+ m_callback_baton_sp.reset();
+ m_baton_is_command_baton = false;
+ m_callback_is_synchronous = false;
+ m_enabled = false;
+ m_condition_text.clear();
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointPrecondition.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointPrecondition.cpp
new file mode 100644
index 000000000000..a387c75c8356
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointPrecondition.cpp
@@ -0,0 +1,26 @@
+//===-- BreakpointPrecondition.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointPrecondition.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb_private;
+
+bool BreakpointPrecondition::EvaluatePrecondition(
+ StoppointCallbackContext &context) {
+ return false;
+}
+
+void BreakpointPrecondition::GetDescription(Stream &stream,
+ lldb::DescriptionLevel level) {}
+
+Status BreakpointPrecondition::ConfigurePrecondition(Args &args) {
+ Status error;
+ error.SetErrorString("Base breakpoint precondition has no options.");
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolver.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolver.cpp
new file mode 100644
index 000000000000..b3224aa91753
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolver.cpp
@@ -0,0 +1,351 @@
+//===-- BreakpointResolver.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolver.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+// Have to include the other breakpoint resolver types here so the static
+// create from StructuredData can call them.
+#include "lldb/Breakpoint/BreakpointResolverAddress.h"
+#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
+#include "lldb/Breakpoint/BreakpointResolverFileRegex.h"
+#include "lldb/Breakpoint/BreakpointResolverName.h"
+#include "lldb/Breakpoint/BreakpointResolverScripted.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/SearchFilter.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// BreakpointResolver:
+const char *BreakpointResolver::g_ty_to_name[] = {"FileAndLine", "Address",
+ "SymbolName", "SourceRegex",
+ "Exception", "Unknown"};
+
+const char *BreakpointResolver::g_option_names[static_cast<uint32_t>(
+ BreakpointResolver::OptionNames::LastOptionName)] = {
+ "AddressOffset", "Exact", "FileName", "Inlines", "Language",
+ "LineNumber", "Column", "ModuleName", "NameMask", "Offset",
+ "PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth",
+ "SkipPrologue", "SymbolNames"};
+
+const char *BreakpointResolver::ResolverTyToName(enum ResolverTy type) {
+ if (type > LastKnownResolverType)
+ return g_ty_to_name[UnknownResolver];
+
+ return g_ty_to_name[type];
+}
+
+BreakpointResolver::ResolverTy
+BreakpointResolver::NameToResolverTy(llvm::StringRef name) {
+ for (size_t i = 0; i < LastKnownResolverType; i++) {
+ if (name == g_ty_to_name[i])
+ return (ResolverTy)i;
+ }
+ return UnknownResolver;
+}
+
+BreakpointResolver::BreakpointResolver(Breakpoint *bkpt,
+ const unsigned char resolverTy,
+ lldb::addr_t offset)
+ : m_breakpoint(bkpt), m_offset(offset), SubclassID(resolverTy) {}
+
+BreakpointResolver::~BreakpointResolver() {}
+
+BreakpointResolverSP BreakpointResolver::CreateFromStructuredData(
+ const StructuredData::Dictionary &resolver_dict, Status &error) {
+ BreakpointResolverSP result_sp;
+ if (!resolver_dict.IsValid()) {
+ error.SetErrorString("Can't deserialize from an invalid data object.");
+ return result_sp;
+ }
+
+ llvm::StringRef subclass_name;
+
+ bool success = resolver_dict.GetValueForKeyAsString(
+ GetSerializationSubclassKey(), subclass_name);
+
+ if (!success) {
+ error.SetErrorStringWithFormat(
+ "Resolver data missing subclass resolver key");
+ return result_sp;
+ }
+
+ ResolverTy resolver_type = NameToResolverTy(subclass_name);
+ if (resolver_type == UnknownResolver) {
+ error.SetErrorStringWithFormatv("Unknown resolver type: {0}.",
+ subclass_name);
+ return result_sp;
+ }
+
+ StructuredData::Dictionary *subclass_options = nullptr;
+ success = resolver_dict.GetValueForKeyAsDictionary(
+ GetSerializationSubclassOptionsKey(), subclass_options);
+ if (!success || !subclass_options || !subclass_options->IsValid()) {
+ error.SetErrorString("Resolver data missing subclass options key.");
+ return result_sp;
+ }
+
+ lldb::addr_t offset;
+ success = subclass_options->GetValueForKeyAsInteger(
+ GetKey(OptionNames::Offset), offset);
+ if (!success) {
+ error.SetErrorString("Resolver data missing offset options key.");
+ return result_sp;
+ }
+
+ BreakpointResolver *resolver;
+
+ switch (resolver_type) {
+ case FileLineResolver:
+ resolver = BreakpointResolverFileLine::CreateFromStructuredData(
+ nullptr, *subclass_options, error);
+ break;
+ case AddressResolver:
+ resolver = BreakpointResolverAddress::CreateFromStructuredData(
+ nullptr, *subclass_options, error);
+ break;
+ case NameResolver:
+ resolver = BreakpointResolverName::CreateFromStructuredData(
+ nullptr, *subclass_options, error);
+ break;
+ case FileRegexResolver:
+ resolver = BreakpointResolverFileRegex::CreateFromStructuredData(
+ nullptr, *subclass_options, error);
+ break;
+ case PythonResolver:
+ resolver = BreakpointResolverScripted::CreateFromStructuredData(
+ nullptr, *subclass_options, error);
+ break;
+ case ExceptionResolver:
+ error.SetErrorString("Exception resolvers are hard.");
+ break;
+ default:
+ llvm_unreachable("Should never get an unresolvable resolver type.");
+ }
+
+ if (!error.Success()) {
+ return result_sp;
+ } else {
+ // Add on the global offset option:
+ resolver->SetOffset(offset);
+ return BreakpointResolverSP(resolver);
+ }
+}
+
+StructuredData::DictionarySP BreakpointResolver::WrapOptionsDict(
+ StructuredData::DictionarySP options_dict_sp) {
+ if (!options_dict_sp || !options_dict_sp->IsValid())
+ return StructuredData::DictionarySP();
+
+ StructuredData::DictionarySP type_dict_sp(new StructuredData::Dictionary());
+ type_dict_sp->AddStringItem(GetSerializationSubclassKey(), GetResolverName());
+ type_dict_sp->AddItem(GetSerializationSubclassOptionsKey(), options_dict_sp);
+
+ // Add the m_offset to the dictionary:
+ options_dict_sp->AddIntegerItem(GetKey(OptionNames::Offset), m_offset);
+
+ return type_dict_sp;
+}
+
+void BreakpointResolver::SetBreakpoint(Breakpoint *bkpt) {
+ m_breakpoint = bkpt;
+ NotifyBreakpointSet();
+}
+
+void BreakpointResolver::ResolveBreakpointInModules(SearchFilter &filter,
+ ModuleList &modules) {
+ filter.SearchInModuleList(*this, modules);
+}
+
+void BreakpointResolver::ResolveBreakpoint(SearchFilter &filter) {
+ filter.Search(*this);
+}
+
+namespace {
+struct SourceLoc {
+ uint32_t line = UINT32_MAX;
+ uint32_t column;
+ SourceLoc(uint32_t l, uint32_t c) : line(l), column(c ? c : UINT32_MAX) {}
+ SourceLoc(const SymbolContext &sc)
+ : line(sc.line_entry.line),
+ column(sc.line_entry.column ? sc.line_entry.column : UINT32_MAX) {}
+};
+
+bool operator<(const SourceLoc a, const SourceLoc b) {
+ if (a.line < b.line)
+ return true;
+ if (a.line > b.line)
+ return false;
+ uint32_t a_col = a.column ? a.column : UINT32_MAX;
+ uint32_t b_col = b.column ? b.column : UINT32_MAX;
+ return a_col < b_col;
+}
+} // namespace
+
+void BreakpointResolver::SetSCMatchesByLine(SearchFilter &filter,
+ SymbolContextList &sc_list,
+ bool skip_prologue,
+ llvm::StringRef log_ident,
+ uint32_t line, uint32_t column) {
+ llvm::SmallVector<SymbolContext, 16> all_scs;
+ for (uint32_t i = 0; i < sc_list.GetSize(); ++i)
+ all_scs.push_back(sc_list[i]);
+
+ while (all_scs.size()) {
+ uint32_t closest_line = UINT32_MAX;
+
+ // Move all the elements with a matching file spec to the end.
+ auto &match = all_scs[0];
+ auto worklist_begin = std::partition(
+ all_scs.begin(), all_scs.end(), [&](const SymbolContext &sc) {
+ if (sc.line_entry.file == match.line_entry.file ||
+ sc.line_entry.original_file == match.line_entry.original_file) {
+ // When a match is found, keep track of the smallest line number.
+ closest_line = std::min(closest_line, sc.line_entry.line);
+ return false;
+ }
+ return true;
+ });
+
+ // (worklist_begin, worklist_end) now contains all entries for one filespec.
+ auto worklist_end = all_scs.end();
+
+ if (column) {
+ // If a column was requested, do a more precise match and only
+ // return the first location that comes after or at the
+ // requested location.
+ SourceLoc requested(line, column);
+ // First, filter out all entries left of the requested column.
+ worklist_end = std::remove_if(
+ worklist_begin, worklist_end,
+ [&](const SymbolContext &sc) { return SourceLoc(sc) < requested; });
+ // Sort the remaining entries by (line, column).
+ llvm::sort(worklist_begin, worklist_end,
+ [](const SymbolContext &a, const SymbolContext &b) {
+ return SourceLoc(a) < SourceLoc(b);
+ });
+
+ // Filter out all locations with a source location after the closest match.
+ if (worklist_begin != worklist_end)
+ worklist_end = std::remove_if(
+ worklist_begin, worklist_end, [&](const SymbolContext &sc) {
+ return SourceLoc(*worklist_begin) < SourceLoc(sc);
+ });
+ } else {
+ // Remove all entries with a larger line number.
+ // ResolveSymbolContext will always return a number that is >=
+ // the line number you pass in. So the smaller line number is
+ // always better.
+ worklist_end = std::remove_if(worklist_begin, worklist_end,
+ [&](const SymbolContext &sc) {
+ return closest_line != sc.line_entry.line;
+ });
+ }
+
+ // Sort by file address.
+ llvm::sort(worklist_begin, worklist_end,
+ [](const SymbolContext &a, const SymbolContext &b) {
+ return a.line_entry.range.GetBaseAddress().GetFileAddress() <
+ b.line_entry.range.GetBaseAddress().GetFileAddress();
+ });
+
+ // Go through and see if there are line table entries that are
+ // contiguous, and if so keep only the first of the contiguous range.
+ // We do this by picking the first location in each lexical block.
+ llvm::SmallDenseSet<Block *, 8> blocks_with_breakpoints;
+ for (auto first = worklist_begin; first != worklist_end; ++first) {
+ assert(!blocks_with_breakpoints.count(first->block));
+ blocks_with_breakpoints.insert(first->block);
+ worklist_end =
+ std::remove_if(std::next(first), worklist_end,
+ [&](const SymbolContext &sc) {
+ return blocks_with_breakpoints.count(sc.block);
+ });
+ }
+
+ // Make breakpoints out of the closest line number match.
+ for (auto &sc : llvm::make_range(worklist_begin, worklist_end))
+ AddLocation(filter, sc, skip_prologue, log_ident);
+
+ // Remove all contexts processed by this iteration.
+ all_scs.erase(worklist_begin, all_scs.end());
+ }
+}
+
+void BreakpointResolver::AddLocation(SearchFilter &filter,
+ const SymbolContext &sc,
+ bool skip_prologue,
+ llvm::StringRef log_ident) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ Address line_start = sc.line_entry.range.GetBaseAddress();
+ if (!line_start.IsValid()) {
+ if (log)
+ log->Printf("error: Unable to set breakpoint %s at file address "
+ "0x%" PRIx64 "\n",
+ log_ident.str().c_str(), line_start.GetFileAddress());
+ return;
+ }
+
+ if (!filter.AddressPasses(line_start)) {
+ if (log)
+ log->Printf("Breakpoint %s at file address 0x%" PRIx64
+ " didn't pass the filter.\n",
+ log_ident.str().c_str(), line_start.GetFileAddress());
+ }
+
+ // If the line number is before the prologue end, move it there...
+ bool skipped_prologue = false;
+ if (skip_prologue && sc.function) {
+ Address prologue_addr(sc.function->GetAddressRange().GetBaseAddress());
+ if (prologue_addr.IsValid() && (line_start == prologue_addr)) {
+ const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize();
+ if (prologue_byte_size) {
+ prologue_addr.Slide(prologue_byte_size);
+
+ if (filter.AddressPasses(prologue_addr)) {
+ skipped_prologue = true;
+ line_start = prologue_addr;
+ }
+ }
+ }
+ }
+
+ BreakpointLocationSP bp_loc_sp(AddLocation(line_start));
+ if (log && bp_loc_sp && !m_breakpoint->IsInternal()) {
+ StreamString s;
+ bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ log->Printf("Added location (skipped prologue: %s): %s \n",
+ skipped_prologue ? "yes" : "no", s.GetData());
+ }
+}
+
+BreakpointLocationSP BreakpointResolver::AddLocation(Address loc_addr,
+ bool *new_location) {
+ loc_addr.Slide(m_offset);
+ return m_breakpoint->AddLocation(loc_addr, new_location);
+}
+
+void BreakpointResolver::SetOffset(lldb::addr_t offset) {
+ // There may already be an offset, so we are actually adjusting location
+ // addresses by the difference.
+ // lldb::addr_t slide = offset - m_offset;
+ // FIXME: We should go fix up all the already set locations for the new
+ // slide.
+
+ m_offset = offset;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverAddress.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverAddress.cpp
new file mode 100644
index 000000000000..8a6fd6a2692c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverAddress.cpp
@@ -0,0 +1,187 @@
+//===-- BreakpointResolverAddress.cpp ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolverAddress.h"
+
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// BreakpointResolverAddress:
+BreakpointResolverAddress::BreakpointResolverAddress(
+ Breakpoint *bkpt, const Address &addr, const FileSpec &module_spec)
+ : BreakpointResolver(bkpt, BreakpointResolver::AddressResolver),
+ m_addr(addr), m_resolved_addr(LLDB_INVALID_ADDRESS),
+ m_module_filespec(module_spec) {}
+
+BreakpointResolverAddress::BreakpointResolverAddress(Breakpoint *bkpt,
+ const Address &addr)
+ : BreakpointResolver(bkpt, BreakpointResolver::AddressResolver),
+ m_addr(addr), m_resolved_addr(LLDB_INVALID_ADDRESS), m_module_filespec() {
+}
+
+BreakpointResolverAddress::~BreakpointResolverAddress() {}
+
+BreakpointResolver *BreakpointResolverAddress::CreateFromStructuredData(
+ Breakpoint *bkpt, const StructuredData::Dictionary &options_dict,
+ Status &error) {
+ llvm::StringRef module_name;
+ lldb::addr_t addr_offset;
+ FileSpec module_filespec;
+ bool success;
+
+ success = options_dict.GetValueForKeyAsInteger(
+ GetKey(OptionNames::AddressOffset), addr_offset);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find address offset entry.");
+ return nullptr;
+ }
+ Address address(addr_offset);
+
+ success = options_dict.HasKey(GetKey(OptionNames::ModuleName));
+ if (success) {
+ success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::ModuleName), module_name);
+ if (!success) {
+ error.SetErrorString("BRA::CFSD: Couldn't read module name entry.");
+ return nullptr;
+ }
+ module_filespec.SetFile(module_name, FileSpec::Style::native);
+ }
+ return new BreakpointResolverAddress(bkpt, address, module_filespec);
+}
+
+StructuredData::ObjectSP
+BreakpointResolverAddress::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+ SectionSP section_sp = m_addr.GetSection();
+ if (section_sp) {
+ ModuleSP module_sp = section_sp->GetModule();
+ ConstString module_name;
+ if (module_sp)
+ module_name.SetCString(module_name.GetCString());
+
+ options_dict_sp->AddStringItem(GetKey(OptionNames::ModuleName),
+ module_name.GetCString());
+ options_dict_sp->AddIntegerItem(GetKey(OptionNames::AddressOffset),
+ m_addr.GetOffset());
+ } else {
+ options_dict_sp->AddIntegerItem(GetKey(OptionNames::AddressOffset),
+ m_addr.GetOffset());
+ if (m_module_filespec) {
+ options_dict_sp->AddStringItem(GetKey(OptionNames::ModuleName),
+ m_module_filespec.GetPath());
+ }
+ }
+
+ return WrapOptionsDict(options_dict_sp);
+ return StructuredData::ObjectSP();
+}
+
+void BreakpointResolverAddress::ResolveBreakpoint(SearchFilter &filter) {
+ // If the address is not section relative, then we should not try to re-
+ // resolve it, it is just some random address and we wouldn't know what to do
+ // on reload. But if it is section relative, we need to re-resolve it since
+ // the section it's in may have shifted on re-run.
+ bool re_resolve = false;
+ if (m_addr.GetSection() || m_module_filespec)
+ re_resolve = true;
+ else if (m_breakpoint->GetNumLocations() == 0)
+ re_resolve = true;
+
+ if (re_resolve)
+ BreakpointResolver::ResolveBreakpoint(filter);
+}
+
+void BreakpointResolverAddress::ResolveBreakpointInModules(
+ SearchFilter &filter, ModuleList &modules) {
+ // See comment in ResolveBreakpoint.
+ bool re_resolve = false;
+ if (m_addr.GetSection())
+ re_resolve = true;
+ else if (m_breakpoint->GetNumLocations() == 0)
+ re_resolve = true;
+
+ if (re_resolve)
+ BreakpointResolver::ResolveBreakpointInModules(filter, modules);
+}
+
+Searcher::CallbackReturn
+BreakpointResolverAddress::SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) {
+ assert(m_breakpoint != nullptr);
+
+ if (filter.AddressPasses(m_addr)) {
+ if (m_breakpoint->GetNumLocations() == 0) {
+ // If the address is just an offset, and we're given a module, see if we
+ // can find the appropriate module loaded in the binary, and fix up
+ // m_addr to use that.
+ if (!m_addr.IsSectionOffset() && m_module_filespec) {
+ Target &target = m_breakpoint->GetTarget();
+ ModuleSpec module_spec(m_module_filespec);
+ ModuleSP module_sp = target.GetImages().FindFirstModule(module_spec);
+ if (module_sp) {
+ Address tmp_address;
+ if (module_sp->ResolveFileAddress(m_addr.GetOffset(), tmp_address))
+ m_addr = tmp_address;
+ }
+ }
+
+ m_resolved_addr = m_addr.GetLoadAddress(&m_breakpoint->GetTarget());
+ BreakpointLocationSP bp_loc_sp(AddLocation(m_addr));
+ if (bp_loc_sp && !m_breakpoint->IsInternal()) {
+ StreamString s;
+ bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Added location: %s\n", s.GetData());
+ }
+ } else {
+ BreakpointLocationSP loc_sp = m_breakpoint->GetLocationAtIndex(0);
+ lldb::addr_t cur_load_location =
+ m_addr.GetLoadAddress(&m_breakpoint->GetTarget());
+ if (cur_load_location != m_resolved_addr) {
+ m_resolved_addr = cur_load_location;
+ loc_sp->ClearBreakpointSite();
+ loc_sp->ResolveBreakpointSite();
+ }
+ }
+ }
+ return Searcher::eCallbackReturnStop;
+}
+
+lldb::SearchDepth BreakpointResolverAddress::GetDepth() {
+ return lldb::eSearchDepthTarget;
+}
+
+void BreakpointResolverAddress::GetDescription(Stream *s) {
+ s->PutCString("address = ");
+ m_addr.Dump(s, m_breakpoint->GetTarget().GetProcessSP().get(),
+ Address::DumpStyleModuleWithFileAddress,
+ Address::DumpStyleLoadAddress);
+}
+
+void BreakpointResolverAddress::Dump(Stream *s) const {}
+
+lldb::BreakpointResolverSP
+BreakpointResolverAddress::CopyForBreakpoint(Breakpoint &breakpoint) {
+ lldb::BreakpointResolverSP ret_sp(
+ new BreakpointResolverAddress(&breakpoint, m_addr));
+ return ret_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp
new file mode 100644
index 000000000000..a6095be31647
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp
@@ -0,0 +1,274 @@
+//===-- BreakpointResolverFileLine.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// BreakpointResolverFileLine:
+BreakpointResolverFileLine::BreakpointResolverFileLine(
+ Breakpoint *bkpt, const FileSpec &file_spec, uint32_t line_no,
+ uint32_t column, lldb::addr_t offset, bool check_inlines,
+ bool skip_prologue, bool exact_match)
+ : BreakpointResolver(bkpt, BreakpointResolver::FileLineResolver, offset),
+ m_file_spec(file_spec), m_line_number(line_no), m_column(column),
+ m_inlines(check_inlines), m_skip_prologue(skip_prologue),
+ m_exact_match(exact_match) {}
+
+BreakpointResolverFileLine::~BreakpointResolverFileLine() {}
+
+BreakpointResolver *BreakpointResolverFileLine::CreateFromStructuredData(
+ Breakpoint *bkpt, const StructuredData::Dictionary &options_dict,
+ Status &error) {
+ llvm::StringRef filename;
+ uint32_t line_no;
+ uint32_t column;
+ bool check_inlines;
+ bool skip_prologue;
+ bool exact_match;
+ bool success;
+
+ lldb::addr_t offset = 0;
+
+ success = options_dict.GetValueForKeyAsString(GetKey(OptionNames::FileName),
+ filename);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find filename entry.");
+ return nullptr;
+ }
+
+ success = options_dict.GetValueForKeyAsInteger(
+ GetKey(OptionNames::LineNumber), line_no);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find line number entry.");
+ return nullptr;
+ }
+
+ success =
+ options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Column), column);
+ if (!success) {
+ // Backwards compatibility.
+ column = 0;
+ }
+
+ success = options_dict.GetValueForKeyAsBoolean(GetKey(OptionNames::Inlines),
+ check_inlines);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find check inlines entry.");
+ return nullptr;
+ }
+
+ success = options_dict.GetValueForKeyAsBoolean(
+ GetKey(OptionNames::SkipPrologue), skip_prologue);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find skip prologue entry.");
+ return nullptr;
+ }
+
+ success = options_dict.GetValueForKeyAsBoolean(
+ GetKey(OptionNames::ExactMatch), exact_match);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find exact match entry.");
+ return nullptr;
+ }
+
+ FileSpec file_spec(filename);
+
+ return new BreakpointResolverFileLine(bkpt, file_spec, line_no, column,
+ offset, check_inlines, skip_prologue,
+ exact_match);
+}
+
+StructuredData::ObjectSP
+BreakpointResolverFileLine::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+
+ options_dict_sp->AddStringItem(GetKey(OptionNames::FileName),
+ m_file_spec.GetPath());
+ options_dict_sp->AddIntegerItem(GetKey(OptionNames::LineNumber),
+ m_line_number);
+ options_dict_sp->AddIntegerItem(GetKey(OptionNames::Column),
+ m_column);
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::Inlines), m_inlines);
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue),
+ m_skip_prologue);
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch),
+ m_exact_match);
+
+ return WrapOptionsDict(options_dict_sp);
+}
+
+// Filter the symbol context list to remove contexts where the line number was
+// moved into a new function. We do this conservatively, so if e.g. we cannot
+// resolve the function in the context (which can happen in case of line-table-
+// only debug info), we leave the context as is. The trickiest part here is
+// handling inlined functions -- in this case we need to make sure we look at
+// the declaration line of the inlined function, NOT the function it was
+// inlined into.
+void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list,
+ bool is_relative) {
+ if (m_exact_match)
+ return; // Nothing to do. Contexts are precise.
+
+ llvm::StringRef relative_path;
+ if (is_relative)
+ relative_path = m_file_spec.GetDirectory().GetStringRef();
+
+ Log * log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS);
+ for(uint32_t i = 0; i < sc_list.GetSize(); ++i) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ if (is_relative) {
+ // If the path was relative, make sure any matches match as long as the
+ // relative parts of the path match the path from support files
+ auto sc_dir = sc.line_entry.file.GetDirectory().GetStringRef();
+ if (!sc_dir.endswith(relative_path)) {
+ // We had a relative path specified and the relative directory doesn't
+ // match so remove this one
+ LLDB_LOG(log, "removing not matching relative path {0} since it "
+ "doesn't end with {1}", sc_dir, relative_path);
+ sc_list.RemoveContextAtIndex(i);
+ --i;
+ continue;
+ }
+ }
+
+ if (!sc.block)
+ continue;
+
+ FileSpec file;
+ uint32_t line;
+ const Block *inline_block = sc.block->GetContainingInlinedBlock();
+ if (inline_block) {
+ const Declaration &inline_declaration = inline_block->GetInlinedFunctionInfo()->GetDeclaration();
+ if (!inline_declaration.IsValid())
+ continue;
+ file = inline_declaration.GetFile();
+ line = inline_declaration.GetLine();
+ } else if (sc.function)
+ sc.function->GetStartLineSourceInfo(file, line);
+ else
+ continue;
+
+ if (file != sc.line_entry.file) {
+ LLDB_LOG(log, "unexpected symbol context file {0}", sc.line_entry.file);
+ continue;
+ }
+
+ // Compare the requested line number with the line of the function
+ // declaration. In case of a function declared as:
+ //
+ // int
+ // foo()
+ // {
+ // ...
+ //
+ // the compiler will set the declaration line to the "foo" line, which is
+ // the reason why we have -1 here. This can fail in case of two inline
+ // functions defined back-to-back:
+ //
+ // inline int foo1() { ... }
+ // inline int foo2() { ... }
+ //
+ // but that's the best we can do for now.
+ // One complication, if the line number returned from GetStartLineSourceInfo
+ // is 0, then we can't do this calculation. That can happen if
+ // GetStartLineSourceInfo gets an error, or if the first line number in
+ // the function really is 0 - which happens for some languages.
+ const int decl_line_is_too_late_fudge = 1;
+ if (line && m_line_number < line - decl_line_is_too_late_fudge) {
+ LLDB_LOG(log, "removing symbol context at {0}:{1}", file, line);
+ sc_list.RemoveContextAtIndex(i);
+ --i;
+ }
+ }
+}
+
+Searcher::CallbackReturn
+BreakpointResolverFileLine::SearchCallback(SearchFilter &filter,
+ SymbolContext &context,
+ Address *addr, bool containing) {
+ SymbolContextList sc_list;
+
+ assert(m_breakpoint != nullptr);
+
+ // There is a tricky bit here. You can have two compilation units that
+ // #include the same file, and in one of them the function at m_line_number
+ // is used (and so code and a line entry for it is generated) but in the
+ // other it isn't. If we considered the CU's independently, then in the
+ // second inclusion, we'd move the breakpoint to the next function that
+ // actually generated code in the header file. That would end up being
+ // confusing. So instead, we do the CU iterations by hand here, then scan
+ // through the complete list of matches, and figure out the closest line
+ // number match, and only set breakpoints on that match.
+
+ // Note also that if file_spec only had a file name and not a directory,
+ // there may be many different file spec's in the resultant list. The
+ // closest line match for one will not be right for some totally different
+ // file. So we go through the match list and pull out the sets that have the
+ // same file spec in their line_entry and treat each set separately.
+
+ FileSpec search_file_spec = m_file_spec;
+ const bool is_relative = m_file_spec.IsRelative();
+ if (is_relative)
+ search_file_spec.GetDirectory().Clear();
+
+ const size_t num_comp_units = context.module_sp->GetNumCompileUnits();
+ for (size_t i = 0; i < num_comp_units; i++) {
+ CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i));
+ if (cu_sp) {
+ if (filter.CompUnitPasses(*cu_sp))
+ cu_sp->ResolveSymbolContext(search_file_spec, m_line_number, m_inlines,
+ m_exact_match, eSymbolContextEverything,
+ sc_list);
+ }
+ }
+
+ FilterContexts(sc_list, is_relative);
+
+ StreamString s;
+ s.Printf("for %s:%d ", m_file_spec.GetFilename().AsCString("<Unknown>"),
+ m_line_number);
+
+ SetSCMatchesByLine(filter, sc_list, m_skip_prologue, s.GetString(),
+ m_line_number, m_column);
+
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth BreakpointResolverFileLine::GetDepth() {
+ return lldb::eSearchDepthModule;
+}
+
+void BreakpointResolverFileLine::GetDescription(Stream *s) {
+ s->Printf("file = '%s', line = %u, ", m_file_spec.GetPath().c_str(),
+ m_line_number);
+ if (m_column)
+ s->Printf("column = %u, ", m_column);
+ s->Printf("exact_match = %d", m_exact_match);
+}
+
+void BreakpointResolverFileLine::Dump(Stream *s) const {}
+
+lldb::BreakpointResolverSP
+BreakpointResolverFileLine::CopyForBreakpoint(Breakpoint &breakpoint) {
+ lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine(
+ &breakpoint, m_file_spec, m_line_number, m_column, m_offset, m_inlines,
+ m_skip_prologue, m_exact_match));
+
+ return ret_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp
new file mode 100644
index 000000000000..0b2485245b72
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp
@@ -0,0 +1,173 @@
+//===-- BreakpointResolverFileRegex.cpp -------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolverFileRegex.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// BreakpointResolverFileRegex:
+BreakpointResolverFileRegex::BreakpointResolverFileRegex(
+ Breakpoint *bkpt, RegularExpression &regex,
+ const std::unordered_set<std::string> &func_names, bool exact_match)
+ : BreakpointResolver(bkpt, BreakpointResolver::FileRegexResolver),
+ m_regex(regex), m_exact_match(exact_match), m_function_names(func_names) {
+}
+
+BreakpointResolverFileRegex::~BreakpointResolverFileRegex() {}
+
+BreakpointResolver *BreakpointResolverFileRegex::CreateFromStructuredData(
+ Breakpoint *bkpt, const StructuredData::Dictionary &options_dict,
+ Status &error) {
+ bool success;
+
+ llvm::StringRef regex_string;
+ success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::RegexString), regex_string);
+ if (!success) {
+ error.SetErrorString("BRFR::CFSD: Couldn't find regex entry.");
+ return nullptr;
+ }
+ RegularExpression regex(regex_string);
+
+ bool exact_match;
+ success = options_dict.GetValueForKeyAsBoolean(
+ GetKey(OptionNames::ExactMatch), exact_match);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find exact match entry.");
+ return nullptr;
+ }
+
+ // The names array is optional:
+ std::unordered_set<std::string> names_set;
+ StructuredData::Array *names_array;
+ success = options_dict.GetValueForKeyAsArray(
+ GetKey(OptionNames::SymbolNameArray), names_array);
+ if (success && names_array) {
+ size_t num_names = names_array->GetSize();
+ for (size_t i = 0; i < num_names; i++) {
+ llvm::StringRef name;
+ success = names_array->GetItemAtIndexAsString(i, name);
+ if (!success) {
+ error.SetErrorStringWithFormat(
+ "BRFR::CFSD: Malformed element %zu in the names array.", i);
+ return nullptr;
+ }
+ names_set.insert(name);
+ }
+ }
+
+ return new BreakpointResolverFileRegex(bkpt, regex, names_set, exact_match);
+}
+
+StructuredData::ObjectSP
+BreakpointResolverFileRegex::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+
+ options_dict_sp->AddStringItem(GetKey(OptionNames::RegexString),
+ m_regex.GetText());
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch),
+ m_exact_match);
+ if (!m_function_names.empty()) {
+ StructuredData::ArraySP names_array_sp(new StructuredData::Array());
+ for (std::string name : m_function_names) {
+ StructuredData::StringSP item(new StructuredData::String(name));
+ names_array_sp->AddItem(item);
+ }
+ options_dict_sp->AddItem(GetKey(OptionNames::LineNumber), names_array_sp);
+ }
+
+ return WrapOptionsDict(options_dict_sp);
+}
+
+Searcher::CallbackReturn
+BreakpointResolverFileRegex::SearchCallback(SearchFilter &filter,
+ SymbolContext &context,
+ Address *addr, bool containing) {
+
+ assert(m_breakpoint != nullptr);
+ if (!context.target_sp)
+ return eCallbackReturnContinue;
+
+ CompileUnit *cu = context.comp_unit;
+ FileSpec cu_file_spec = *(static_cast<FileSpec *>(cu));
+ std::vector<uint32_t> line_matches;
+ context.target_sp->GetSourceManager().FindLinesMatchingRegex(
+ cu_file_spec, m_regex, 1, UINT32_MAX, line_matches);
+
+ uint32_t num_matches = line_matches.size();
+ for (uint32_t i = 0; i < num_matches; i++) {
+ SymbolContextList sc_list;
+ const bool search_inlines = false;
+
+ cu->ResolveSymbolContext(cu_file_spec, line_matches[i], search_inlines,
+ m_exact_match, eSymbolContextEverything, sc_list);
+ // Find all the function names:
+ if (!m_function_names.empty()) {
+ std::vector<size_t> sc_to_remove;
+ for (size_t i = 0; i < sc_list.GetSize(); i++) {
+ SymbolContext sc_ctx;
+ sc_list.GetContextAtIndex(i, sc_ctx);
+ std::string name(
+ sc_ctx
+ .GetFunctionName(
+ Mangled::NamePreference::ePreferDemangledWithoutArguments)
+ .AsCString());
+ if (!m_function_names.count(name)) {
+ sc_to_remove.push_back(i);
+ }
+ }
+
+ if (!sc_to_remove.empty()) {
+ std::vector<size_t>::reverse_iterator iter;
+ std::vector<size_t>::reverse_iterator rend = sc_to_remove.rend();
+ for (iter = sc_to_remove.rbegin(); iter != rend; iter++) {
+ sc_list.RemoveContextAtIndex(*iter);
+ }
+ }
+ }
+
+ const bool skip_prologue = true;
+
+ BreakpointResolver::SetSCMatchesByLine(filter, sc_list, skip_prologue,
+ m_regex.GetText());
+ }
+ assert(m_breakpoint != nullptr);
+
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth BreakpointResolverFileRegex::GetDepth() {
+ return lldb::eSearchDepthCompUnit;
+}
+
+void BreakpointResolverFileRegex::GetDescription(Stream *s) {
+ s->Printf("source regex = \"%s\", exact_match = %d",
+ m_regex.GetText().str().c_str(), m_exact_match);
+}
+
+void BreakpointResolverFileRegex::Dump(Stream *s) const {}
+
+lldb::BreakpointResolverSP
+BreakpointResolverFileRegex::CopyForBreakpoint(Breakpoint &breakpoint) {
+ lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileRegex(
+ &breakpoint, m_regex, m_function_names, m_exact_match));
+ return ret_sp;
+}
+
+void BreakpointResolverFileRegex::AddFunctionName(const char *func_name) {
+ m_function_names.insert(func_name);
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverName.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverName.cpp
new file mode 100644
index 000000000000..3ad2e8867f2a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverName.cpp
@@ -0,0 +1,435 @@
+//===-- BreakpointResolverName.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolverName.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Architecture.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointResolverName::BreakpointResolverName(
+ Breakpoint *bkpt, const char *name_cstr, FunctionNameType name_type_mask,
+ LanguageType language, Breakpoint::MatchType type, lldb::addr_t offset,
+ bool skip_prologue)
+ : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset),
+ m_class_name(), m_regex(), m_match_type(type), m_language(language),
+ m_skip_prologue(skip_prologue) {
+ if (m_match_type == Breakpoint::Regexp) {
+ if (!m_regex.Compile(llvm::StringRef::withNullAsEmpty(name_cstr))) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+ if (log)
+ log->Warning("function name regexp: \"%s\" did not compile.",
+ name_cstr);
+ }
+ } else {
+ AddNameLookup(ConstString(name_cstr), name_type_mask);
+ }
+}
+
+BreakpointResolverName::BreakpointResolverName(
+ Breakpoint *bkpt, const char *names[], size_t num_names,
+ FunctionNameType name_type_mask, LanguageType language, lldb::addr_t offset,
+ bool skip_prologue)
+ : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset),
+ m_match_type(Breakpoint::Exact), m_language(language),
+ m_skip_prologue(skip_prologue) {
+ for (size_t i = 0; i < num_names; i++) {
+ AddNameLookup(ConstString(names[i]), name_type_mask);
+ }
+}
+
+BreakpointResolverName::BreakpointResolverName(Breakpoint *bkpt,
+ std::vector<std::string> names,
+ FunctionNameType name_type_mask,
+ LanguageType language,
+ lldb::addr_t offset,
+ bool skip_prologue)
+ : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset),
+ m_match_type(Breakpoint::Exact), m_language(language),
+ m_skip_prologue(skip_prologue) {
+ for (const std::string &name : names) {
+ AddNameLookup(ConstString(name.c_str(), name.size()), name_type_mask);
+ }
+}
+
+BreakpointResolverName::BreakpointResolverName(Breakpoint *bkpt,
+ RegularExpression &func_regex,
+ lldb::LanguageType language,
+ lldb::addr_t offset,
+ bool skip_prologue)
+ : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset),
+ m_class_name(nullptr), m_regex(func_regex),
+ m_match_type(Breakpoint::Regexp), m_language(language),
+ m_skip_prologue(skip_prologue) {}
+
+BreakpointResolverName::~BreakpointResolverName() = default;
+
+BreakpointResolverName::BreakpointResolverName(
+ const BreakpointResolverName &rhs)
+ : BreakpointResolver(rhs.m_breakpoint, BreakpointResolver::NameResolver,
+ rhs.m_offset),
+ m_lookups(rhs.m_lookups), m_class_name(rhs.m_class_name),
+ m_regex(rhs.m_regex), m_match_type(rhs.m_match_type),
+ m_language(rhs.m_language), m_skip_prologue(rhs.m_skip_prologue) {}
+
+BreakpointResolver *BreakpointResolverName::CreateFromStructuredData(
+ Breakpoint *bkpt, const StructuredData::Dictionary &options_dict,
+ Status &error) {
+ LanguageType language = eLanguageTypeUnknown;
+ llvm::StringRef language_name;
+ bool success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::LanguageName), language_name);
+ if (success) {
+ language = Language::GetLanguageTypeFromString(language_name);
+ if (language == eLanguageTypeUnknown) {
+ error.SetErrorStringWithFormatv("BRN::CFSD: Unknown language: {0}.",
+ language_name);
+ return nullptr;
+ }
+ }
+
+ lldb::addr_t offset = 0;
+ success =
+ options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Offset), offset);
+ if (!success) {
+ error.SetErrorStringWithFormat("BRN::CFSD: Missing offset entry.");
+ return nullptr;
+ }
+
+ bool skip_prologue;
+ success = options_dict.GetValueForKeyAsBoolean(
+ GetKey(OptionNames::SkipPrologue), skip_prologue);
+ if (!success) {
+ error.SetErrorStringWithFormat("BRN::CFSD: Missing Skip prologue entry.");
+ return nullptr;
+ }
+
+ llvm::StringRef regex_text;
+ success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::RegexString), regex_text);
+ if (success) {
+ RegularExpression regex(regex_text);
+ return new BreakpointResolverName(bkpt, regex, language, offset,
+ skip_prologue);
+ } else {
+ StructuredData::Array *names_array;
+ success = options_dict.GetValueForKeyAsArray(
+ GetKey(OptionNames::SymbolNameArray), names_array);
+ if (!success) {
+ error.SetErrorStringWithFormat("BRN::CFSD: Missing symbol names entry.");
+ return nullptr;
+ }
+ StructuredData::Array *names_mask_array;
+ success = options_dict.GetValueForKeyAsArray(
+ GetKey(OptionNames::NameMaskArray), names_mask_array);
+ if (!success) {
+ error.SetErrorStringWithFormat(
+ "BRN::CFSD: Missing symbol names mask entry.");
+ return nullptr;
+ }
+
+ size_t num_elem = names_array->GetSize();
+ if (num_elem != names_mask_array->GetSize()) {
+ error.SetErrorString(
+ "BRN::CFSD: names and names mask arrays have different sizes.");
+ return nullptr;
+ }
+
+ if (num_elem == 0) {
+ error.SetErrorString(
+ "BRN::CFSD: no name entry in a breakpoint by name breakpoint.");
+ return nullptr;
+ }
+ std::vector<std::string> names;
+ std::vector<FunctionNameType> name_masks;
+ for (size_t i = 0; i < num_elem; i++) {
+ llvm::StringRef name;
+
+ success = names_array->GetItemAtIndexAsString(i, name);
+ if (!success) {
+ error.SetErrorString("BRN::CFSD: name entry is not a string.");
+ return nullptr;
+ }
+ std::underlying_type<FunctionNameType>::type fnt;
+ success = names_mask_array->GetItemAtIndexAsInteger(i, fnt);
+ if (!success) {
+ error.SetErrorString("BRN::CFSD: name mask entry is not an integer.");
+ return nullptr;
+ }
+ names.push_back(name);
+ name_masks.push_back(static_cast<FunctionNameType>(fnt));
+ }
+
+ BreakpointResolverName *resolver = new BreakpointResolverName(
+ bkpt, names[0].c_str(), name_masks[0], language,
+ Breakpoint::MatchType::Exact, offset, skip_prologue);
+ for (size_t i = 1; i < num_elem; i++) {
+ resolver->AddNameLookup(ConstString(names[i]), name_masks[i]);
+ }
+ return resolver;
+ }
+}
+
+StructuredData::ObjectSP BreakpointResolverName::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+
+ if (m_regex.IsValid()) {
+ options_dict_sp->AddStringItem(GetKey(OptionNames::RegexString),
+ m_regex.GetText());
+ } else {
+ StructuredData::ArraySP names_sp(new StructuredData::Array());
+ StructuredData::ArraySP name_masks_sp(new StructuredData::Array());
+ for (auto lookup : m_lookups) {
+ names_sp->AddItem(StructuredData::StringSP(
+ new StructuredData::String(lookup.GetName().AsCString())));
+ name_masks_sp->AddItem(StructuredData::IntegerSP(
+ new StructuredData::Integer(lookup.GetNameTypeMask())));
+ }
+ options_dict_sp->AddItem(GetKey(OptionNames::SymbolNameArray), names_sp);
+ options_dict_sp->AddItem(GetKey(OptionNames::NameMaskArray), name_masks_sp);
+ }
+ if (m_language != eLanguageTypeUnknown)
+ options_dict_sp->AddStringItem(
+ GetKey(OptionNames::LanguageName),
+ Language::GetNameForLanguageType(m_language));
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue),
+ m_skip_prologue);
+
+ return WrapOptionsDict(options_dict_sp);
+}
+
+void BreakpointResolverName::AddNameLookup(ConstString name,
+ FunctionNameType name_type_mask) {
+
+ Module::LookupInfo lookup(name, name_type_mask, m_language);
+ m_lookups.emplace_back(lookup);
+
+ auto add_variant_funcs = [&](Language *lang) {
+ for (ConstString variant_name : lang->GetMethodNameVariants(name)) {
+ Module::LookupInfo variant_lookup(name, name_type_mask,
+ lang->GetLanguageType());
+ variant_lookup.SetLookupName(variant_name);
+ m_lookups.emplace_back(variant_lookup);
+ }
+ return true;
+ };
+
+ if (Language *lang = Language::FindPlugin(m_language)) {
+ add_variant_funcs(lang);
+ } else {
+ // Most likely m_language is eLanguageTypeUnknown. We check each language for
+ // possible variants or more qualified names and create lookups for those as
+ // well.
+ Language::ForEach(add_variant_funcs);
+ }
+}
+
+// FIXME: Right now we look at the module level, and call the module's
+// "FindFunctions".
+// Greg says he will add function tables, maybe at the CompileUnit level to
+// accelerate function lookup. At that point, we should switch the depth to
+// CompileUnit, and look in these tables.
+
+Searcher::CallbackReturn
+BreakpointResolverName::SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) {
+ SymbolContextList func_list;
+ // SymbolContextList sym_list;
+
+ uint32_t i;
+ bool new_location;
+ Address break_addr;
+ assert(m_breakpoint != nullptr);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+ if (m_class_name) {
+ if (log)
+ log->Warning("Class/method function specification not supported yet.\n");
+ return Searcher::eCallbackReturnStop;
+ }
+ bool filter_by_cu =
+ (filter.GetFilterRequiredItems() & eSymbolContextCompUnit) != 0;
+ bool filter_by_language = (m_language != eLanguageTypeUnknown);
+ const bool include_symbols = !filter_by_cu;
+ const bool include_inlines = true;
+ const bool append = true;
+
+ switch (m_match_type) {
+ case Breakpoint::Exact:
+ if (context.module_sp) {
+ for (const auto &lookup : m_lookups) {
+ const size_t start_func_idx = func_list.GetSize();
+ context.module_sp->FindFunctions(
+ lookup.GetLookupName(), nullptr, lookup.GetNameTypeMask(),
+ include_symbols, include_inlines, append, func_list);
+
+ const size_t end_func_idx = func_list.GetSize();
+
+ if (start_func_idx < end_func_idx)
+ lookup.Prune(func_list, start_func_idx);
+ }
+ }
+ break;
+ case Breakpoint::Regexp:
+ if (context.module_sp) {
+ context.module_sp->FindFunctions(
+ m_regex,
+ !filter_by_cu, // include symbols only if we aren't filtering by CU
+ include_inlines, append, func_list);
+ }
+ break;
+ case Breakpoint::Glob:
+ if (log)
+ log->Warning("glob is not supported yet.");
+ break;
+ }
+
+ // If the filter specifies a Compilation Unit, remove the ones that don't
+ // pass at this point.
+ if (filter_by_cu || filter_by_language) {
+ uint32_t num_functions = func_list.GetSize();
+
+ for (size_t idx = 0; idx < num_functions; idx++) {
+ bool remove_it = false;
+ SymbolContext sc;
+ func_list.GetContextAtIndex(idx, sc);
+ if (filter_by_cu) {
+ if (!sc.comp_unit || !filter.CompUnitPasses(*sc.comp_unit))
+ remove_it = true;
+ }
+
+ if (filter_by_language) {
+ LanguageType sym_language = sc.GetLanguage();
+ if ((Language::GetPrimaryLanguage(sym_language) !=
+ Language::GetPrimaryLanguage(m_language)) &&
+ (sym_language != eLanguageTypeUnknown)) {
+ remove_it = true;
+ }
+ }
+
+ if (remove_it) {
+ func_list.RemoveContextAtIndex(idx);
+ num_functions--;
+ idx--;
+ }
+ }
+ }
+
+ // Remove any duplicates between the function list and the symbol list
+ SymbolContext sc;
+ if (func_list.GetSize()) {
+ for (i = 0; i < func_list.GetSize(); i++) {
+ if (func_list.GetContextAtIndex(i, sc)) {
+ bool is_reexported = false;
+
+ if (sc.block && sc.block->GetInlinedFunctionInfo()) {
+ if (!sc.block->GetStartAddress(break_addr))
+ break_addr.Clear();
+ } else if (sc.function) {
+ break_addr = sc.function->GetAddressRange().GetBaseAddress();
+ if (m_skip_prologue && break_addr.IsValid()) {
+ const uint32_t prologue_byte_size =
+ sc.function->GetPrologueByteSize();
+ if (prologue_byte_size)
+ break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size);
+ }
+ } else if (sc.symbol) {
+ if (sc.symbol->GetType() == eSymbolTypeReExported) {
+ const Symbol *actual_symbol =
+ sc.symbol->ResolveReExportedSymbol(m_breakpoint->GetTarget());
+ if (actual_symbol) {
+ is_reexported = true;
+ break_addr = actual_symbol->GetAddress();
+ }
+ } else {
+ break_addr = sc.symbol->GetAddress();
+ }
+
+ if (m_skip_prologue && break_addr.IsValid()) {
+ const uint32_t prologue_byte_size =
+ sc.symbol->GetPrologueByteSize();
+ if (prologue_byte_size)
+ break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size);
+ else {
+ const Architecture *arch =
+ m_breakpoint->GetTarget().GetArchitecturePlugin();
+ if (arch)
+ arch->AdjustBreakpointAddress(*sc.symbol, break_addr);
+ }
+ }
+ }
+
+ if (break_addr.IsValid()) {
+ if (filter.AddressPasses(break_addr)) {
+ BreakpointLocationSP bp_loc_sp(
+ AddLocation(break_addr, &new_location));
+ bp_loc_sp->SetIsReExported(is_reexported);
+ if (bp_loc_sp && new_location && !m_breakpoint->IsInternal()) {
+ if (log) {
+ StreamString s;
+ bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ log->Printf("Added location: %s\n", s.GetData());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth BreakpointResolverName::GetDepth() {
+ return lldb::eSearchDepthModule;
+}
+
+void BreakpointResolverName::GetDescription(Stream *s) {
+ if (m_match_type == Breakpoint::Regexp)
+ s->Printf("regex = '%s'", m_regex.GetText().str().c_str());
+ else {
+ size_t num_names = m_lookups.size();
+ if (num_names == 1)
+ s->Printf("name = '%s'", m_lookups[0].GetName().GetCString());
+ else {
+ s->Printf("names = {");
+ for (size_t i = 0; i < num_names; i++) {
+ s->Printf("%s'%s'", (i == 0 ? "" : ", "),
+ m_lookups[i].GetName().GetCString());
+ }
+ s->Printf("}");
+ }
+ }
+ if (m_language != eLanguageTypeUnknown) {
+ s->Printf(", language = %s", Language::GetNameForLanguageType(m_language));
+ }
+}
+
+void BreakpointResolverName::Dump(Stream *s) const {}
+
+lldb::BreakpointResolverSP
+BreakpointResolverName::CopyForBreakpoint(Breakpoint &breakpoint) {
+ lldb::BreakpointResolverSP ret_sp(new BreakpointResolverName(*this));
+ ret_sp->SetBreakpoint(&breakpoint);
+ return ret_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverScripted.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverScripted.cpp
new file mode 100644
index 000000000000..8363795a4d7f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverScripted.cpp
@@ -0,0 +1,183 @@
+//===-- BreakpointResolverScripted.cpp ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolverScripted.h"
+
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// BreakpointResolverScripted:
+BreakpointResolverScripted::BreakpointResolverScripted(
+ Breakpoint *bkpt,
+ const llvm::StringRef class_name,
+ lldb::SearchDepth depth,
+ StructuredDataImpl *args_data,
+ ScriptInterpreter &script_interp)
+ : BreakpointResolver(bkpt, BreakpointResolver::PythonResolver),
+ m_class_name(class_name), m_depth(depth), m_args_ptr(args_data) {
+ CreateImplementationIfNeeded();
+}
+
+void BreakpointResolverScripted::CreateImplementationIfNeeded() {
+ if (m_implementation_sp)
+ return;
+
+ if (m_class_name.empty())
+ return;
+
+ if (m_breakpoint) {
+ TargetSP target_sp = m_breakpoint->GetTargetSP();
+ ScriptInterpreter *script_interp = target_sp->GetDebugger()
+ .GetScriptInterpreter();
+ if (!script_interp)
+ return;
+ lldb::BreakpointSP bkpt_sp(m_breakpoint->shared_from_this());
+ m_implementation_sp = script_interp->CreateScriptedBreakpointResolver(
+ m_class_name.c_str(), m_args_ptr, bkpt_sp);
+ }
+}
+
+void BreakpointResolverScripted::NotifyBreakpointSet() {
+ CreateImplementationIfNeeded();
+}
+
+BreakpointResolverScripted::~BreakpointResolverScripted() {}
+
+BreakpointResolver *
+BreakpointResolverScripted::CreateFromStructuredData(
+ Breakpoint *bkpt, const StructuredData::Dictionary &options_dict,
+ Status &error) {
+ llvm::StringRef class_name;
+ bool success;
+
+ if (!bkpt)
+ return nullptr;
+
+ success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::PythonClassName), class_name);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find class name entry.");
+ return nullptr;
+ }
+ lldb::SearchDepth depth;
+ int depth_as_int;
+ success = options_dict.GetValueForKeyAsInteger(
+ GetKey(OptionNames::SearchDepth), depth_as_int);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find class name entry.");
+ return nullptr;
+ }
+ if (depth_as_int >= (int) OptionNames::LastOptionName) {
+ error.SetErrorString("BRFL::CFSD: Invalid value for search depth.");
+ return nullptr;
+ }
+ depth = (lldb::SearchDepth) depth_as_int;
+
+ StructuredDataImpl *args_data_impl = new StructuredDataImpl();
+ StructuredData::Dictionary *args_dict = new StructuredData::Dictionary();
+ success = options_dict.GetValueForKeyAsDictionary(
+ GetKey(OptionNames::ScriptArgs), args_dict);
+ if (success) {
+ // FIXME: The resolver needs a copy of the ARGS dict that it can own,
+ // so I need to make a copy constructor for the Dictionary so I can pass
+ // that to it here. For now the args are empty.
+ //StructuredData::Dictionary *dict_copy = new StructuredData::Dictionary(args_dict);
+
+ }
+ ScriptInterpreter *script_interp = bkpt->GetTarget()
+ .GetDebugger()
+ .GetScriptInterpreter();
+ return new BreakpointResolverScripted(bkpt, class_name, depth, args_data_impl,
+ *script_interp);
+}
+
+StructuredData::ObjectSP
+BreakpointResolverScripted::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+
+ options_dict_sp->AddStringItem(GetKey(OptionNames::PythonClassName),
+ m_class_name);
+ return WrapOptionsDict(options_dict_sp);
+}
+
+ScriptInterpreter *BreakpointResolverScripted::GetScriptInterpreter() {
+ return m_breakpoint->GetTarget().GetDebugger().GetScriptInterpreter();
+}
+
+Searcher::CallbackReturn
+BreakpointResolverScripted::SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) {
+ assert(m_breakpoint != nullptr);
+ bool should_continue = true;
+ if (!m_implementation_sp)
+ return Searcher::eCallbackReturnStop;
+
+ ScriptInterpreter *interp = GetScriptInterpreter();
+ should_continue = interp->ScriptedBreakpointResolverSearchCallback(
+ m_implementation_sp,
+ &context);
+ if (should_continue)
+ return Searcher::eCallbackReturnContinue;
+ else
+ return Searcher::eCallbackReturnStop;
+}
+
+lldb::SearchDepth
+BreakpointResolverScripted::GetDepth() {
+ assert(m_breakpoint != nullptr);
+ lldb::SearchDepth depth = lldb::eSearchDepthModule;
+ if (m_implementation_sp) {
+ ScriptInterpreter *interp = GetScriptInterpreter();
+ depth = interp->ScriptedBreakpointResolverSearchDepth(
+ m_implementation_sp);
+ }
+ return depth;
+}
+
+void BreakpointResolverScripted::GetDescription(Stream *s) {
+ StructuredData::GenericSP generic_sp;
+ std::string short_help;
+
+ if (m_implementation_sp) {
+ ScriptInterpreter *interp = GetScriptInterpreter();
+ interp->GetShortHelpForCommandObject(m_implementation_sp,
+ short_help);
+ }
+ if (!short_help.empty())
+ s->PutCString(short_help.c_str());
+ else
+ s->Printf("python class = %s", m_class_name.c_str());
+}
+
+void BreakpointResolverScripted::Dump(Stream *s) const {}
+
+lldb::BreakpointResolverSP
+BreakpointResolverScripted::CopyForBreakpoint(Breakpoint &breakpoint) {
+ ScriptInterpreter *script_interp = GetScriptInterpreter();
+ // FIXME: Have to make a copy of the arguments from the m_args_ptr and then
+ // pass that to the new resolver.
+ lldb::BreakpointResolverSP ret_sp(
+ new BreakpointResolverScripted(&breakpoint, m_class_name,
+ m_depth, nullptr, *script_interp));
+ return ret_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointSite.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointSite.cpp
new file mode 100644
index 000000000000..a757a01824c7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointSite.cpp
@@ -0,0 +1,206 @@
+//===-- BreakpointSite.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <inttypes.h>
+
+#include "lldb/Breakpoint/BreakpointSite.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointSiteList.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointSite::BreakpointSite(BreakpointSiteList *list,
+ const BreakpointLocationSP &owner,
+ lldb::addr_t addr, bool use_hardware)
+ : StoppointLocation(GetNextID(), addr, 0, use_hardware),
+ m_type(eSoftware), // Process subclasses need to set this correctly using
+ // SetType()
+ m_saved_opcode(), m_trap_opcode(),
+ m_enabled(false), // Need to create it disabled, so the first enable turns
+ // it on.
+ m_owners(), m_owners_mutex() {
+ m_owners.Add(owner);
+}
+
+BreakpointSite::~BreakpointSite() {
+ BreakpointLocationSP bp_loc_sp;
+ const size_t owner_count = m_owners.GetSize();
+ for (size_t i = 0; i < owner_count; i++) {
+ m_owners.GetByIndex(i)->ClearBreakpointSite();
+ }
+}
+
+break_id_t BreakpointSite::GetNextID() {
+ static break_id_t g_next_id = 0;
+ return ++g_next_id;
+}
+
+// RETURNS - true if we should stop at this breakpoint, false if we
+// should continue.
+
+bool BreakpointSite::ShouldStop(StoppointCallbackContext *context) {
+ IncrementHitCount();
+ // ShouldStop can do a lot of work, and might even come come back and hit
+ // this breakpoint site again. So don't hold the m_owners_mutex the whole
+ // while. Instead make a local copy of the collection and call ShouldStop on
+ // the copy.
+ BreakpointLocationCollection owners_copy;
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
+ owners_copy = m_owners;
+ }
+ return owners_copy.ShouldStop(context);
+}
+
+bool BreakpointSite::IsBreakpointAtThisSite(lldb::break_id_t bp_id) {
+ std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
+ const size_t owner_count = m_owners.GetSize();
+ for (size_t i = 0; i < owner_count; i++) {
+ if (m_owners.GetByIndex(i)->GetBreakpoint().GetID() == bp_id)
+ return true;
+ }
+ return false;
+}
+
+void BreakpointSite::Dump(Stream *s) const {
+ if (s == nullptr)
+ return;
+
+ s->Printf("BreakpointSite %u: addr = 0x%8.8" PRIx64
+ " type = %s breakpoint hw_index = %i hit_count = %-4u",
+ GetID(), (uint64_t)m_addr, IsHardware() ? "hardware" : "software",
+ GetHardwareIndex(), GetHitCount());
+}
+
+void BreakpointSite::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
+ if (level != lldb::eDescriptionLevelBrief)
+ s->Printf("breakpoint site: %d at 0x%8.8" PRIx64, GetID(),
+ GetLoadAddress());
+ m_owners.GetDescription(s, level);
+}
+
+bool BreakpointSite::IsInternal() const { return m_owners.IsInternal(); }
+
+uint8_t *BreakpointSite::GetTrapOpcodeBytes() { return &m_trap_opcode[0]; }
+
+const uint8_t *BreakpointSite::GetTrapOpcodeBytes() const {
+ return &m_trap_opcode[0];
+}
+
+size_t BreakpointSite::GetTrapOpcodeMaxByteSize() const {
+ return sizeof(m_trap_opcode);
+}
+
+bool BreakpointSite::SetTrapOpcode(const uint8_t *trap_opcode,
+ uint32_t trap_opcode_size) {
+ if (trap_opcode_size > 0 && trap_opcode_size <= sizeof(m_trap_opcode)) {
+ m_byte_size = trap_opcode_size;
+ ::memcpy(m_trap_opcode, trap_opcode, trap_opcode_size);
+ return true;
+ }
+ m_byte_size = 0;
+ return false;
+}
+
+uint8_t *BreakpointSite::GetSavedOpcodeBytes() { return &m_saved_opcode[0]; }
+
+const uint8_t *BreakpointSite::GetSavedOpcodeBytes() const {
+ return &m_saved_opcode[0];
+}
+
+bool BreakpointSite::IsEnabled() const { return m_enabled; }
+
+void BreakpointSite::SetEnabled(bool enabled) { m_enabled = enabled; }
+
+void BreakpointSite::AddOwner(const BreakpointLocationSP &owner) {
+ std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
+ m_owners.Add(owner);
+}
+
+size_t BreakpointSite::RemoveOwner(lldb::break_id_t break_id,
+ lldb::break_id_t break_loc_id) {
+ std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
+ m_owners.Remove(break_id, break_loc_id);
+ return m_owners.GetSize();
+}
+
+size_t BreakpointSite::GetNumberOfOwners() {
+ std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
+ return m_owners.GetSize();
+}
+
+BreakpointLocationSP BreakpointSite::GetOwnerAtIndex(size_t index) {
+ std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
+ return m_owners.GetByIndex(index);
+}
+
+bool BreakpointSite::ValidForThisThread(Thread *thread) {
+ std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
+ return m_owners.ValidForThisThread(thread);
+}
+
+void BreakpointSite::BumpHitCounts() {
+ std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
+ for (BreakpointLocationSP loc_sp : m_owners.BreakpointLocations()) {
+ loc_sp->BumpHitCount();
+ }
+}
+
+bool BreakpointSite::IntersectsRange(lldb::addr_t addr, size_t size,
+ lldb::addr_t *intersect_addr,
+ size_t *intersect_size,
+ size_t *opcode_offset) const {
+ // We only use software traps for software breakpoints
+ if (!IsHardware()) {
+ if (m_byte_size > 0) {
+ const lldb::addr_t bp_end_addr = m_addr + m_byte_size;
+ const lldb::addr_t end_addr = addr + size;
+ // Is the breakpoint end address before the passed in start address?
+ if (bp_end_addr <= addr)
+ return false;
+ // Is the breakpoint start address after passed in end address?
+ if (end_addr <= m_addr)
+ return false;
+ if (intersect_addr || intersect_size || opcode_offset) {
+ if (m_addr < addr) {
+ if (intersect_addr)
+ *intersect_addr = addr;
+ if (intersect_size)
+ *intersect_size =
+ std::min<lldb::addr_t>(bp_end_addr, end_addr) - addr;
+ if (opcode_offset)
+ *opcode_offset = addr - m_addr;
+ } else {
+ if (intersect_addr)
+ *intersect_addr = m_addr;
+ if (intersect_size)
+ *intersect_size =
+ std::min<lldb::addr_t>(bp_end_addr, end_addr) - m_addr;
+ if (opcode_offset)
+ *opcode_offset = 0;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t
+BreakpointSite::CopyOwnersList(BreakpointLocationCollection &out_collection) {
+ std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
+ for (BreakpointLocationSP loc_sp : m_owners.BreakpointLocations()) {
+ out_collection.Add(loc_sp);
+ }
+ return out_collection.GetSize();
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointSiteList.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointSiteList.cpp
new file mode 100644
index 000000000000..7a986fd83983
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointSiteList.cpp
@@ -0,0 +1,200 @@
+//===-- BreakpointSiteList.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointSiteList.h"
+
+#include "lldb/Utility/Stream.h"
+#include <algorithm>
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointSiteList::BreakpointSiteList() : m_mutex(), m_bp_site_list() {}
+
+BreakpointSiteList::~BreakpointSiteList() {}
+
+// Add breakpoint site to the list. However, if the element already exists in
+// the list, then we don't add it, and return LLDB_INVALID_BREAK_ID.
+
+lldb::break_id_t BreakpointSiteList::Add(const BreakpointSiteSP &bp) {
+ lldb::addr_t bp_site_load_addr = bp->GetLoadAddress();
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::iterator iter = m_bp_site_list.find(bp_site_load_addr);
+
+ if (iter == m_bp_site_list.end()) {
+ m_bp_site_list.insert(iter, collection::value_type(bp_site_load_addr, bp));
+ return bp->GetID();
+ } else {
+ return LLDB_INVALID_BREAK_ID;
+ }
+}
+
+bool BreakpointSiteList::ShouldStop(StoppointCallbackContext *context,
+ lldb::break_id_t site_id) {
+ BreakpointSiteSP site_sp(FindByID(site_id));
+ if (site_sp) {
+ // Let the BreakpointSite 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 BreakpointSite isn't valid anymore or it
+ // doesn't exist.
+ return true;
+}
+lldb::break_id_t BreakpointSiteList::FindIDByAddress(lldb::addr_t addr) {
+ BreakpointSiteSP bp = FindByAddress(addr);
+ if (bp) {
+ // DBLogIf(PD_LOG_BREAKPOINTS, "BreakpointSiteList::%s ( addr = 0x%8.8"
+ // PRIx64 " ) => %u", __FUNCTION__, (uint64_t)addr, bp->GetID());
+ return bp.get()->GetID();
+ }
+ // DBLogIf(PD_LOG_BREAKPOINTS, "BreakpointSiteList::%s ( addr = 0x%8.8"
+ // PRIx64
+ // " ) => NONE", __FUNCTION__, (uint64_t)addr);
+ return LLDB_INVALID_BREAK_ID;
+}
+
+bool BreakpointSiteList::Remove(lldb::break_id_t break_id) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::iterator pos = GetIDIterator(break_id); // Predicate
+ if (pos != m_bp_site_list.end()) {
+ m_bp_site_list.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+bool BreakpointSiteList::RemoveByAddress(lldb::addr_t address) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::iterator pos = m_bp_site_list.find(address);
+ if (pos != m_bp_site_list.end()) {
+ m_bp_site_list.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+class BreakpointSiteIDMatches {
+public:
+ BreakpointSiteIDMatches(lldb::break_id_t break_id) : m_break_id(break_id) {}
+
+ bool operator()(std::pair<lldb::addr_t, BreakpointSiteSP> val_pair) const {
+ return m_break_id == val_pair.second->GetID();
+ }
+
+private:
+ const lldb::break_id_t m_break_id;
+};
+
+BreakpointSiteList::collection::iterator
+BreakpointSiteList::GetIDIterator(lldb::break_id_t break_id) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return std::find_if(m_bp_site_list.begin(),
+ m_bp_site_list.end(), // Search full range
+ BreakpointSiteIDMatches(break_id)); // Predicate
+}
+
+BreakpointSiteList::collection::const_iterator
+BreakpointSiteList::GetIDConstIterator(lldb::break_id_t break_id) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return std::find_if(m_bp_site_list.begin(),
+ m_bp_site_list.end(), // Search full range
+ BreakpointSiteIDMatches(break_id)); // Predicate
+}
+
+BreakpointSiteSP BreakpointSiteList::FindByID(lldb::break_id_t break_id) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ BreakpointSiteSP stop_sp;
+ collection::iterator pos = GetIDIterator(break_id);
+ if (pos != m_bp_site_list.end())
+ stop_sp = pos->second;
+
+ return stop_sp;
+}
+
+const BreakpointSiteSP
+BreakpointSiteList::FindByID(lldb::break_id_t break_id) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ BreakpointSiteSP stop_sp;
+ collection::const_iterator pos = GetIDConstIterator(break_id);
+ if (pos != m_bp_site_list.end())
+ stop_sp = pos->second;
+
+ return stop_sp;
+}
+
+BreakpointSiteSP BreakpointSiteList::FindByAddress(lldb::addr_t addr) {
+ BreakpointSiteSP found_sp;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::iterator iter = m_bp_site_list.find(addr);
+ if (iter != m_bp_site_list.end())
+ found_sp = iter->second;
+ return found_sp;
+}
+
+bool BreakpointSiteList::BreakpointSiteContainsBreakpoint(
+ lldb::break_id_t bp_site_id, lldb::break_id_t bp_id) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::const_iterator pos = GetIDConstIterator(bp_site_id);
+ if (pos != m_bp_site_list.end())
+ return pos->second->IsBreakpointAtThisSite(bp_id);
+
+ return false;
+}
+
+void BreakpointSiteList::Dump(Stream *s) const {
+ s->Printf("%p: ", static_cast<const void *>(this));
+ // s->Indent();
+ s->Printf("BreakpointSiteList with %u BreakpointSites:\n",
+ (uint32_t)m_bp_site_list.size());
+ s->IndentMore();
+ collection::const_iterator pos;
+ collection::const_iterator end = m_bp_site_list.end();
+ for (pos = m_bp_site_list.begin(); pos != end; ++pos)
+ pos->second->Dump(s);
+ s->IndentLess();
+}
+
+void BreakpointSiteList::ForEach(
+ std::function<void(BreakpointSite *)> const &callback) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (auto pair : m_bp_site_list)
+ callback(pair.second.get());
+}
+
+bool BreakpointSiteList::FindInRange(lldb::addr_t lower_bound,
+ lldb::addr_t upper_bound,
+ BreakpointSiteList &bp_site_list) const {
+ if (lower_bound > upper_bound)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::const_iterator lower, upper, pos;
+ lower = m_bp_site_list.lower_bound(lower_bound);
+ if (lower == m_bp_site_list.end() || (*lower).first >= upper_bound)
+ return false;
+
+ // This is one tricky bit. The breakpoint might overlap the bottom end of
+ // the range. So we grab the breakpoint prior to the lower bound, and check
+ // that that + its byte size isn't in our range.
+ if (lower != m_bp_site_list.begin()) {
+ collection::const_iterator prev_pos = lower;
+ prev_pos--;
+ const BreakpointSiteSP &prev_bp = (*prev_pos).second;
+ if (prev_bp->GetLoadAddress() + prev_bp->GetByteSize() > lower_bound)
+ bp_site_list.Add(prev_bp);
+ }
+
+ upper = m_bp_site_list.upper_bound(upper_bound);
+
+ for (pos = lower; pos != upper; pos++) {
+ bp_site_list.Add((*pos).second);
+ }
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/Stoppoint.cpp b/contrib/llvm-project/lldb/source/Breakpoint/Stoppoint.cpp
new file mode 100644
index 000000000000..4cab975fe320
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/Stoppoint.cpp
@@ -0,0 +1,24 @@
+//===-- Stoppoint.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/Stoppoint.h"
+#include "lldb/lldb-private.h"
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Stoppoint constructor
+Stoppoint::Stoppoint() : m_bid(LLDB_INVALID_BREAK_ID) {}
+
+// Destructor
+Stoppoint::~Stoppoint() {}
+
+break_id_t Stoppoint::GetID() const { return m_bid; }
+
+void Stoppoint::SetID(break_id_t bid) { m_bid = bid; }
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/StoppointCallbackContext.cpp b/contrib/llvm-project/lldb/source/Breakpoint/StoppointCallbackContext.cpp
new file mode 100644
index 000000000000..584bf0060a4a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/StoppointCallbackContext.cpp
@@ -0,0 +1,24 @@
+//===-- StoppointCallbackContext.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+
+using namespace lldb_private;
+
+StoppointCallbackContext::StoppointCallbackContext()
+ : event(nullptr), exe_ctx_ref(), is_synchronous(false) {}
+
+StoppointCallbackContext::StoppointCallbackContext(
+ Event *e, const ExecutionContext &exe_ctx, bool synchronously)
+ : event(e), exe_ctx_ref(exe_ctx), is_synchronous(synchronously) {}
+
+void StoppointCallbackContext::Clear() {
+ event = nullptr;
+ exe_ctx_ref.Clear();
+ is_synchronous = false;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/StoppointLocation.cpp b/contrib/llvm-project/lldb/source/Breakpoint/StoppointLocation.cpp
new file mode 100644
index 000000000000..8cc6791fa680
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/StoppointLocation.cpp
@@ -0,0 +1,32 @@
+//===-- StoppointLocation.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/StoppointLocation.h"
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+// StoppointLocation constructor
+StoppointLocation::StoppointLocation(break_id_t bid, addr_t addr, bool hardware)
+ : m_loc_id(bid), m_addr(addr), m_hardware(hardware),
+ m_hardware_index(LLDB_INVALID_INDEX32), m_byte_size(0), m_hit_count(0) {}
+
+StoppointLocation::StoppointLocation(break_id_t bid, addr_t addr,
+ uint32_t byte_size, bool hardware)
+ : m_loc_id(bid), m_addr(addr), m_hardware(hardware),
+ m_hardware_index(LLDB_INVALID_INDEX32), m_byte_size(byte_size),
+ m_hit_count(0) {}
+
+// Destructor
+StoppointLocation::~StoppointLocation() {}
+
+void StoppointLocation::DecrementHitCount() {
+ assert(m_hit_count > 0);
+ --m_hit_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/Watchpoint.cpp b/contrib/llvm-project/lldb/source/Breakpoint/Watchpoint.cpp
new file mode 100644
index 000000000000..e8a926527d24
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/Watchpoint.cpp
@@ -0,0 +1,379 @@
+//===-- Watchpoint.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/Watchpoint.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+Watchpoint::Watchpoint(Target &target, lldb::addr_t addr, uint32_t size,
+ const CompilerType *type, bool hardware)
+ : StoppointLocation(0, addr, size, hardware), m_target(target),
+ m_enabled(false), m_is_hardware(hardware), m_is_watch_variable(false),
+ m_is_ephemeral(false), m_disabled_count(0), m_watch_read(0),
+ m_watch_write(0), m_watch_was_read(0), m_watch_was_written(0),
+ m_ignore_count(0), m_false_alarms(0), m_decl_str(), m_watch_spec_str(),
+ m_type(), m_error(), m_options(), m_being_created(true) {
+ if (type && type->IsValid())
+ m_type = *type;
+ else {
+ // If we don't have a known type, then we force it to unsigned int of the
+ // right size.
+ ClangASTContext *ast_context = target.GetScratchClangASTContext();
+ m_type = ast_context->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint,
+ 8 * size);
+ }
+
+ // Set the initial value of the watched variable:
+ if (m_target.GetProcessSP()) {
+ ExecutionContext exe_ctx;
+ m_target.GetProcessSP()->CalculateExecutionContext(exe_ctx);
+ CaptureWatchedValue(exe_ctx);
+ }
+ m_being_created = false;
+}
+
+Watchpoint::~Watchpoint() = default;
+
+// This function is used when "baton" doesn't need to be freed
+void Watchpoint::SetCallback(WatchpointHitCallback callback, void *baton,
+ bool is_synchronous) {
+ // The default "Baton" class will keep a copy of "baton" and won't free or
+ // delete it when it goes goes out of scope.
+ m_options.SetCallback(callback, std::make_shared<UntypedBaton>(baton),
+ is_synchronous);
+
+ SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);
+}
+
+// This function is used when a baton needs to be freed and therefore is
+// contained in a "Baton" subclass.
+void Watchpoint::SetCallback(WatchpointHitCallback callback,
+ const BatonSP &callback_baton_sp,
+ bool is_synchronous) {
+ m_options.SetCallback(callback, callback_baton_sp, is_synchronous);
+ SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);
+}
+
+void Watchpoint::ClearCallback() {
+ m_options.ClearCallback();
+ SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);
+}
+
+void Watchpoint::SetDeclInfo(const std::string &str) { m_decl_str = str; }
+
+std::string Watchpoint::GetWatchSpec() { return m_watch_spec_str; }
+
+void Watchpoint::SetWatchSpec(const std::string &str) {
+ m_watch_spec_str = str;
+}
+
+// Override default impl of StoppointLocation::IsHardware() since m_is_hardware
+// member field is more accurate.
+bool Watchpoint::IsHardware() const { return m_is_hardware; }
+
+bool Watchpoint::IsWatchVariable() const { return m_is_watch_variable; }
+
+void Watchpoint::SetWatchVariable(bool val) { m_is_watch_variable = val; }
+
+bool Watchpoint::CaptureWatchedValue(const ExecutionContext &exe_ctx) {
+ ConstString watch_name("$__lldb__watch_value");
+ m_old_value_sp = m_new_value_sp;
+ Address watch_address(GetLoadAddress());
+ if (!m_type.IsValid()) {
+ // Don't know how to report new & old values, since we couldn't make a
+ // scalar type for this watchpoint. This works around an assert in
+ // ValueObjectMemory::Create.
+ // FIXME: This should not happen, but if it does in some case we care about,
+ // we can go grab the value raw and print it as unsigned.
+ return false;
+ }
+ m_new_value_sp = ValueObjectMemory::Create(
+ exe_ctx.GetBestExecutionContextScope(), watch_name.GetStringRef(),
+ watch_address, m_type);
+ m_new_value_sp = m_new_value_sp->CreateConstantValue(watch_name);
+ return (m_new_value_sp && m_new_value_sp->GetError().Success());
+}
+
+void Watchpoint::IncrementFalseAlarmsAndReviseHitCount() {
+ ++m_false_alarms;
+ if (m_false_alarms) {
+ if (m_hit_count >= m_false_alarms) {
+ m_hit_count -= m_false_alarms;
+ m_false_alarms = 0;
+ } else {
+ m_false_alarms -= m_hit_count;
+ m_hit_count = 0;
+ }
+ }
+}
+
+// RETURNS - true if we should stop at this breakpoint, false if we
+// should continue.
+
+bool Watchpoint::ShouldStop(StoppointCallbackContext *context) {
+ IncrementHitCount();
+
+ return IsEnabled();
+}
+
+void Watchpoint::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ DumpWithLevel(s, level);
+}
+
+void Watchpoint::Dump(Stream *s) const {
+ DumpWithLevel(s, lldb::eDescriptionLevelBrief);
+}
+
+// If prefix is nullptr, we display the watch id and ignore the prefix
+// altogether.
+void Watchpoint::DumpSnapshots(Stream *s, const char *prefix) const {
+ if (!prefix) {
+ s->Printf("\nWatchpoint %u hit:", GetID());
+ prefix = "";
+ }
+
+ if (m_old_value_sp) {
+ const char *old_value_cstr = m_old_value_sp->GetValueAsCString();
+ if (old_value_cstr && old_value_cstr[0])
+ s->Printf("\n%sold value: %s", prefix, old_value_cstr);
+ else {
+ const char *old_summary_cstr = m_old_value_sp->GetSummaryAsCString();
+ if (old_summary_cstr && old_summary_cstr[0])
+ s->Printf("\n%sold value: %s", prefix, old_summary_cstr);
+ }
+ }
+
+ if (m_new_value_sp) {
+ const char *new_value_cstr = m_new_value_sp->GetValueAsCString();
+ if (new_value_cstr && new_value_cstr[0])
+ s->Printf("\n%snew value: %s", prefix, new_value_cstr);
+ else {
+ const char *new_summary_cstr = m_new_value_sp->GetSummaryAsCString();
+ if (new_summary_cstr && new_summary_cstr[0])
+ s->Printf("\n%snew value: %s", prefix, new_summary_cstr);
+ }
+ }
+}
+
+void Watchpoint::DumpWithLevel(Stream *s,
+ lldb::DescriptionLevel description_level) const {
+ if (s == nullptr)
+ return;
+
+ assert(description_level >= lldb::eDescriptionLevelBrief &&
+ description_level <= lldb::eDescriptionLevelVerbose);
+
+ s->Printf("Watchpoint %u: addr = 0x%8.8" PRIx64
+ " size = %u state = %s type = %s%s",
+ GetID(), GetLoadAddress(), m_byte_size,
+ IsEnabled() ? "enabled" : "disabled", m_watch_read ? "r" : "",
+ m_watch_write ? "w" : "");
+
+ if (description_level >= lldb::eDescriptionLevelFull) {
+ if (!m_decl_str.empty())
+ s->Printf("\n declare @ '%s'", m_decl_str.c_str());
+ if (!m_watch_spec_str.empty())
+ s->Printf("\n watchpoint spec = '%s'", m_watch_spec_str.c_str());
+
+ // Dump the snapshots we have taken.
+ DumpSnapshots(s, " ");
+
+ if (GetConditionText())
+ s->Printf("\n condition = '%s'", GetConditionText());
+ m_options.GetCallbackDescription(s, description_level);
+ }
+
+ if (description_level >= lldb::eDescriptionLevelVerbose) {
+ s->Printf("\n hw_index = %i hit_count = %-4u ignore_count = %-4u",
+ GetHardwareIndex(), GetHitCount(), GetIgnoreCount());
+ }
+}
+
+bool Watchpoint::IsEnabled() const { return m_enabled; }
+
+// Within StopInfo.cpp, we purposely turn on the ephemeral mode right before
+// temporarily disable the watchpoint in order to perform possible watchpoint
+// actions without triggering further watchpoint events. After the temporary
+// disabled watchpoint is enabled, we then turn off the ephemeral mode.
+
+void Watchpoint::TurnOnEphemeralMode() { m_is_ephemeral = true; }
+
+void Watchpoint::TurnOffEphemeralMode() {
+ m_is_ephemeral = false;
+ // Leaving ephemeral mode, reset the m_disabled_count!
+ m_disabled_count = 0;
+}
+
+bool Watchpoint::IsDisabledDuringEphemeralMode() {
+ return m_disabled_count > 1 && m_is_ephemeral;
+}
+
+void Watchpoint::SetEnabled(bool enabled, bool notify) {
+ if (!enabled) {
+ if (!m_is_ephemeral)
+ SetHardwareIndex(LLDB_INVALID_INDEX32);
+ else
+ ++m_disabled_count;
+
+ // Don't clear the snapshots for now.
+ // Within StopInfo.cpp, we purposely do disable/enable watchpoint while
+ // performing watchpoint actions.
+ }
+ bool changed = enabled != m_enabled;
+ m_enabled = enabled;
+ if (notify && !m_is_ephemeral && changed)
+ SendWatchpointChangedEvent(enabled ? eWatchpointEventTypeEnabled
+ : eWatchpointEventTypeDisabled);
+}
+
+void Watchpoint::SetWatchpointType(uint32_t type, bool notify) {
+ int old_watch_read = m_watch_read;
+ int old_watch_write = m_watch_write;
+ m_watch_read = (type & LLDB_WATCH_TYPE_READ) != 0;
+ m_watch_write = (type & LLDB_WATCH_TYPE_WRITE) != 0;
+ if (notify &&
+ (old_watch_read != m_watch_read || old_watch_write != m_watch_write))
+ SendWatchpointChangedEvent(eWatchpointEventTypeTypeChanged);
+}
+
+bool Watchpoint::WatchpointRead() const { return m_watch_read != 0; }
+
+bool Watchpoint::WatchpointWrite() const { return m_watch_write != 0; }
+
+uint32_t Watchpoint::GetIgnoreCount() const { return m_ignore_count; }
+
+void Watchpoint::SetIgnoreCount(uint32_t n) {
+ bool changed = m_ignore_count != n;
+ m_ignore_count = n;
+ if (changed)
+ SendWatchpointChangedEvent(eWatchpointEventTypeIgnoreChanged);
+}
+
+bool Watchpoint::InvokeCallback(StoppointCallbackContext *context) {
+ return m_options.InvokeCallback(context, GetID());
+}
+
+void Watchpoint::SetCondition(const char *condition) {
+ if (condition == nullptr || condition[0] == '\0') {
+ if (m_condition_up)
+ m_condition_up.reset();
+ } else {
+ // Pass nullptr for expr_prefix (no translation-unit level definitions).
+ Status error;
+ m_condition_up.reset(m_target.GetUserExpressionForLanguage(
+ condition, llvm::StringRef(), lldb::eLanguageTypeUnknown,
+ UserExpression::eResultTypeAny, EvaluateExpressionOptions(), nullptr,
+ error));
+ if (error.Fail()) {
+ // FIXME: Log something...
+ m_condition_up.reset();
+ }
+ }
+ SendWatchpointChangedEvent(eWatchpointEventTypeConditionChanged);
+}
+
+const char *Watchpoint::GetConditionText() const {
+ if (m_condition_up)
+ return m_condition_up->GetUserText();
+ else
+ return nullptr;
+}
+
+void Watchpoint::SendWatchpointChangedEvent(
+ lldb::WatchpointEventType eventKind) {
+ if (!m_being_created &&
+ GetTarget().EventTypeHasListeners(
+ Target::eBroadcastBitWatchpointChanged)) {
+ WatchpointEventData *data =
+ new Watchpoint::WatchpointEventData(eventKind, shared_from_this());
+ GetTarget().BroadcastEvent(Target::eBroadcastBitWatchpointChanged, data);
+ }
+}
+
+void Watchpoint::SendWatchpointChangedEvent(WatchpointEventData *data) {
+ if (data == nullptr)
+ return;
+
+ if (!m_being_created &&
+ GetTarget().EventTypeHasListeners(Target::eBroadcastBitWatchpointChanged))
+ GetTarget().BroadcastEvent(Target::eBroadcastBitWatchpointChanged, data);
+ else
+ delete data;
+}
+
+Watchpoint::WatchpointEventData::WatchpointEventData(
+ WatchpointEventType sub_type, const WatchpointSP &new_watchpoint_sp)
+ : EventData(), m_watchpoint_event(sub_type),
+ m_new_watchpoint_sp(new_watchpoint_sp) {}
+
+Watchpoint::WatchpointEventData::~WatchpointEventData() = default;
+
+ConstString Watchpoint::WatchpointEventData::GetFlavorString() {
+ static ConstString g_flavor("Watchpoint::WatchpointEventData");
+ return g_flavor;
+}
+
+ConstString Watchpoint::WatchpointEventData::GetFlavor() const {
+ return WatchpointEventData::GetFlavorString();
+}
+
+WatchpointSP &Watchpoint::WatchpointEventData::GetWatchpoint() {
+ return m_new_watchpoint_sp;
+}
+
+WatchpointEventType
+Watchpoint::WatchpointEventData::GetWatchpointEventType() const {
+ return m_watchpoint_event;
+}
+
+void Watchpoint::WatchpointEventData::Dump(Stream *s) const {}
+
+const Watchpoint::WatchpointEventData *
+Watchpoint::WatchpointEventData::GetEventDataFromEvent(const Event *event) {
+ if (event) {
+ const EventData *event_data = event->GetData();
+ if (event_data &&
+ event_data->GetFlavor() == WatchpointEventData::GetFlavorString())
+ return static_cast<const WatchpointEventData *>(event->GetData());
+ }
+ return nullptr;
+}
+
+WatchpointEventType
+Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent(
+ const EventSP &event_sp) {
+ const WatchpointEventData *data = GetEventDataFromEvent(event_sp.get());
+
+ if (data == nullptr)
+ return eWatchpointEventTypeInvalidType;
+ else
+ return data->GetWatchpointEventType();
+}
+
+WatchpointSP Watchpoint::WatchpointEventData::GetWatchpointFromEvent(
+ const EventSP &event_sp) {
+ WatchpointSP wp_sp;
+
+ const WatchpointEventData *data = GetEventDataFromEvent(event_sp.get());
+ if (data)
+ wp_sp = data->m_new_watchpoint_sp;
+
+ return wp_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/WatchpointList.cpp b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointList.cpp
new file mode 100644
index 000000000000..b1c1e6f253eb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointList.cpp
@@ -0,0 +1,252 @@
+//===-- WatchpointList.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/WatchpointList.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+WatchpointList::WatchpointList()
+ : m_watchpoints(), m_mutex(), m_next_wp_id(0) {}
+
+WatchpointList::~WatchpointList() {}
+
+// Add a watchpoint to the list.
+lldb::watch_id_t WatchpointList::Add(const WatchpointSP &wp_sp, bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ wp_sp->SetID(++m_next_wp_id);
+ m_watchpoints.push_back(wp_sp);
+ if (notify) {
+ if (wp_sp->GetTarget().EventTypeHasListeners(
+ Target::eBroadcastBitWatchpointChanged))
+ wp_sp->GetTarget().BroadcastEvent(Target::eBroadcastBitWatchpointChanged,
+ new Watchpoint::WatchpointEventData(
+ eWatchpointEventTypeAdded, wp_sp));
+ }
+ return wp_sp->GetID();
+}
+
+void WatchpointList::Dump(Stream *s) const {
+ DumpWithLevel(s, lldb::eDescriptionLevelBrief);
+}
+
+void WatchpointList::DumpWithLevel(
+ Stream *s, lldb::DescriptionLevel description_level) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ s->Printf("%p: ", static_cast<const void *>(this));
+ // s->Indent();
+ s->Printf("WatchpointList with %" PRIu64 " Watchpoints:\n",
+ (uint64_t)m_watchpoints.size());
+ s->IndentMore();
+ wp_collection::const_iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos)
+ (*pos)->DumpWithLevel(s, description_level);
+ s->IndentLess();
+}
+
+const WatchpointSP WatchpointList::FindByAddress(lldb::addr_t addr) const {
+ WatchpointSP wp_sp;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_watchpoints.empty()) {
+ wp_collection::const_iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos) {
+ lldb::addr_t wp_addr = (*pos)->GetLoadAddress();
+ uint32_t wp_bytesize = (*pos)->GetByteSize();
+ if ((wp_addr <= addr) && ((wp_addr + wp_bytesize) > addr)) {
+ wp_sp = *pos;
+ break;
+ }
+ }
+ }
+
+ return wp_sp;
+}
+
+const WatchpointSP WatchpointList::FindBySpec(std::string spec) const {
+ WatchpointSP wp_sp;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_watchpoints.empty()) {
+ wp_collection::const_iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos)
+ if ((*pos)->GetWatchSpec() == spec) {
+ wp_sp = *pos;
+ break;
+ }
+ }
+
+ return wp_sp;
+}
+
+class WatchpointIDMatches {
+public:
+ WatchpointIDMatches(lldb::watch_id_t watch_id) : m_watch_id(watch_id) {}
+
+ bool operator()(const WatchpointSP &wp) const {
+ return m_watch_id == wp->GetID();
+ }
+
+private:
+ const lldb::watch_id_t m_watch_id;
+};
+
+WatchpointList::wp_collection::iterator
+WatchpointList::GetIDIterator(lldb::watch_id_t watch_id) {
+ return std::find_if(m_watchpoints.begin(),
+ m_watchpoints.end(), // Search full range
+ WatchpointIDMatches(watch_id)); // Predicate
+}
+
+WatchpointList::wp_collection::const_iterator
+WatchpointList::GetIDConstIterator(lldb::watch_id_t watch_id) const {
+ return std::find_if(m_watchpoints.begin(),
+ m_watchpoints.end(), // Search full range
+ WatchpointIDMatches(watch_id)); // Predicate
+}
+
+WatchpointSP WatchpointList::FindByID(lldb::watch_id_t watch_id) const {
+ WatchpointSP wp_sp;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ wp_collection::const_iterator pos = GetIDConstIterator(watch_id);
+ if (pos != m_watchpoints.end())
+ wp_sp = *pos;
+
+ return wp_sp;
+}
+
+lldb::watch_id_t WatchpointList::FindIDByAddress(lldb::addr_t addr) {
+ WatchpointSP wp_sp = FindByAddress(addr);
+ if (wp_sp) {
+ return wp_sp->GetID();
+ }
+ return LLDB_INVALID_WATCH_ID;
+}
+
+lldb::watch_id_t WatchpointList::FindIDBySpec(std::string spec) {
+ WatchpointSP wp_sp = FindBySpec(spec);
+ if (wp_sp) {
+ return wp_sp->GetID();
+ }
+ return LLDB_INVALID_WATCH_ID;
+}
+
+WatchpointSP WatchpointList::GetByIndex(uint32_t i) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ WatchpointSP wp_sp;
+ if (i < m_watchpoints.size()) {
+ wp_collection::const_iterator pos = m_watchpoints.begin();
+ std::advance(pos, i);
+ wp_sp = *pos;
+ }
+ return wp_sp;
+}
+
+const WatchpointSP WatchpointList::GetByIndex(uint32_t i) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ WatchpointSP wp_sp;
+ if (i < m_watchpoints.size()) {
+ wp_collection::const_iterator pos = m_watchpoints.begin();
+ std::advance(pos, i);
+ wp_sp = *pos;
+ }
+ return wp_sp;
+}
+
+std::vector<lldb::watch_id_t> WatchpointList::GetWatchpointIDs() const {
+ std::vector<lldb::watch_id_t> IDs;
+ wp_collection::const_iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos)
+ IDs.push_back((*pos)->GetID());
+ return IDs;
+}
+
+bool WatchpointList::Remove(lldb::watch_id_t watch_id, bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ wp_collection::iterator pos = GetIDIterator(watch_id);
+ if (pos != m_watchpoints.end()) {
+ WatchpointSP wp_sp = *pos;
+ if (notify) {
+ if (wp_sp->GetTarget().EventTypeHasListeners(
+ Target::eBroadcastBitWatchpointChanged))
+ wp_sp->GetTarget().BroadcastEvent(
+ Target::eBroadcastBitWatchpointChanged,
+ new Watchpoint::WatchpointEventData(eWatchpointEventTypeRemoved,
+ wp_sp));
+ }
+ m_watchpoints.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+uint32_t WatchpointList::GetHitCount() const {
+ uint32_t hit_count = 0;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ wp_collection::const_iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos)
+ hit_count += (*pos)->GetHitCount();
+ return hit_count;
+}
+
+bool WatchpointList::ShouldStop(StoppointCallbackContext *context,
+ lldb::watch_id_t watch_id) {
+
+ WatchpointSP wp_sp = FindByID(watch_id);
+ if (wp_sp) {
+ // Let the Watchpoint 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.
+ return wp_sp->ShouldStop(context);
+ }
+ // We should stop here since this Watchpoint isn't valid anymore or it
+ // doesn't exist.
+ return true;
+}
+
+void WatchpointList::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ wp_collection::iterator pos, end = m_watchpoints.end();
+
+ for (pos = m_watchpoints.begin(); pos != end; ++pos) {
+ s->Printf(" ");
+ (*pos)->Dump(s);
+ }
+}
+
+void WatchpointList::SetEnabledAll(bool enabled) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ wp_collection::iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos)
+ (*pos)->SetEnabled(enabled);
+}
+
+void WatchpointList::RemoveAll(bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (notify) {
+
+ {
+ wp_collection::iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos) {
+ if ((*pos)->GetTarget().EventTypeHasListeners(
+ Target::eBroadcastBitBreakpointChanged)) {
+ (*pos)->GetTarget().BroadcastEvent(
+ Target::eBroadcastBitWatchpointChanged,
+ new Watchpoint::WatchpointEventData(eWatchpointEventTypeRemoved,
+ *pos));
+ }
+ }
+ }
+ }
+ m_watchpoints.clear();
+}
+
+void WatchpointList::GetListMutex(
+ std::unique_lock<std::recursive_mutex> &lock) {
+ lock = std::unique_lock<std::recursive_mutex>(m_mutex);
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/WatchpointOptions.cpp b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointOptions.cpp
new file mode 100644
index 000000000000..7dd130a3072c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointOptions.cpp
@@ -0,0 +1,183 @@
+//===-- WatchpointOptions.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/WatchpointOptions.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool WatchpointOptions::NullCallback(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t watch_id) {
+ return true;
+}
+
+// WatchpointOptions constructor
+WatchpointOptions::WatchpointOptions()
+ : m_callback(WatchpointOptions::NullCallback), m_callback_baton_sp(),
+ m_callback_is_synchronous(false), m_thread_spec_up() {}
+
+// WatchpointOptions copy constructor
+WatchpointOptions::WatchpointOptions(const WatchpointOptions &rhs)
+ : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp),
+ m_callback_is_synchronous(rhs.m_callback_is_synchronous),
+ m_thread_spec_up() {
+ if (rhs.m_thread_spec_up != nullptr)
+ m_thread_spec_up.reset(new ThreadSpec(*rhs.m_thread_spec_up));
+}
+
+// WatchpointOptions assignment operator
+const WatchpointOptions &WatchpointOptions::
+operator=(const WatchpointOptions &rhs) {
+ m_callback = rhs.m_callback;
+ m_callback_baton_sp = rhs.m_callback_baton_sp;
+ m_callback_is_synchronous = rhs.m_callback_is_synchronous;
+ if (rhs.m_thread_spec_up != nullptr)
+ m_thread_spec_up.reset(new ThreadSpec(*rhs.m_thread_spec_up));
+ return *this;
+}
+
+WatchpointOptions *
+WatchpointOptions::CopyOptionsNoCallback(WatchpointOptions &orig) {
+ WatchpointHitCallback orig_callback = orig.m_callback;
+ lldb::BatonSP orig_callback_baton_sp = orig.m_callback_baton_sp;
+ bool orig_is_sync = orig.m_callback_is_synchronous;
+
+ orig.ClearCallback();
+ WatchpointOptions *ret_val = new WatchpointOptions(orig);
+
+ orig.SetCallback(orig_callback, orig_callback_baton_sp, orig_is_sync);
+
+ return ret_val;
+}
+
+// Destructor
+WatchpointOptions::~WatchpointOptions() = default;
+
+// Callbacks
+void WatchpointOptions::SetCallback(WatchpointHitCallback callback,
+ const BatonSP &callback_baton_sp,
+ bool callback_is_synchronous) {
+ m_callback_is_synchronous = callback_is_synchronous;
+ m_callback = callback;
+ m_callback_baton_sp = callback_baton_sp;
+}
+
+void WatchpointOptions::ClearCallback() {
+ m_callback = WatchpointOptions::NullCallback;
+ m_callback_is_synchronous = false;
+ m_callback_baton_sp.reset();
+}
+
+Baton *WatchpointOptions::GetBaton() { return m_callback_baton_sp.get(); }
+
+const Baton *WatchpointOptions::GetBaton() const {
+ return m_callback_baton_sp.get();
+}
+
+bool WatchpointOptions::InvokeCallback(StoppointCallbackContext *context,
+ lldb::user_id_t watch_id) {
+ if (m_callback && context->is_synchronous == IsCallbackSynchronous()) {
+ return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()
+ : nullptr,
+ context, watch_id);
+ } else
+ return true;
+}
+
+bool WatchpointOptions::HasCallback() {
+ return m_callback != WatchpointOptions::NullCallback;
+}
+
+const ThreadSpec *WatchpointOptions::GetThreadSpecNoCreate() const {
+ return m_thread_spec_up.get();
+}
+
+ThreadSpec *WatchpointOptions::GetThreadSpec() {
+ if (m_thread_spec_up == nullptr)
+ m_thread_spec_up.reset(new ThreadSpec());
+
+ return m_thread_spec_up.get();
+}
+
+void WatchpointOptions::SetThreadID(lldb::tid_t thread_id) {
+ GetThreadSpec()->SetTID(thread_id);
+}
+
+void WatchpointOptions::GetCallbackDescription(
+ Stream *s, lldb::DescriptionLevel level) const {
+ if (m_callback_baton_sp.get()) {
+ s->EOL();
+ m_callback_baton_sp->GetDescription(s, level);
+ }
+}
+
+void WatchpointOptions::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) const {
+ // Figure out if there are any options not at their default value, and only
+ // print anything if there are:
+
+ if ((GetThreadSpecNoCreate() != nullptr &&
+ GetThreadSpecNoCreate()->HasSpecification())) {
+ if (level == lldb::eDescriptionLevelVerbose) {
+ s->EOL();
+ s->IndentMore();
+ s->Indent();
+ s->PutCString("Watchpoint Options:\n");
+ s->IndentMore();
+ s->Indent();
+ } else
+ s->PutCString(" Options: ");
+
+ if (m_thread_spec_up)
+ m_thread_spec_up->GetDescription(s, level);
+ else if (level == eDescriptionLevelBrief)
+ s->PutCString("thread spec: no ");
+ if (level == lldb::eDescriptionLevelFull) {
+ s->IndentLess();
+ s->IndentMore();
+ }
+ }
+
+ GetCallbackDescription(s, level);
+}
+
+void WatchpointOptions::CommandBaton::GetDescription(
+ Stream *s, lldb::DescriptionLevel level) const {
+ const CommandData *data = getItem();
+
+ if (level == eDescriptionLevelBrief) {
+ s->Printf(", commands = %s",
+ (data && data->user_source.GetSize() > 0) ? "yes" : "no");
+ return;
+ }
+
+ s->IndentMore();
+ s->Indent("watchpoint commands:\n");
+
+ s->IndentMore();
+ if (data && data->user_source.GetSize() > 0) {
+ const size_t num_strings = data->user_source.GetSize();
+ for (size_t i = 0; i < num_strings; ++i) {
+ s->Indent(data->user_source.GetStringAtIndex(i));
+ s->EOL();
+ }
+ } else {
+ s->PutCString("No commands.\n");
+ }
+ s->IndentLess();
+ s->IndentLess();
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandCompletions.cpp b/contrib/llvm-project/lldb/source/Commands/CommandCompletions.cpp
new file mode 100644
index 000000000000..5d2fb3d67f57
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandCompletions.cpp
@@ -0,0 +1,532 @@
+//===-- CommandCompletions.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <sys/stat.h>
+#if defined(__APPLE__) || defined(__linux__)
+#include <pwd.h>
+#endif
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringSet.h"
+
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/TildeExpressionResolver.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+using namespace lldb_private;
+
+CommandCompletions::CommonCompletionElement
+ CommandCompletions::g_common_completions[] = {
+ {eCustomCompletion, nullptr},
+ {eSourceFileCompletion, CommandCompletions::SourceFiles},
+ {eDiskFileCompletion, CommandCompletions::DiskFiles},
+ {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories},
+ {eSymbolCompletion, CommandCompletions::Symbols},
+ {eModuleCompletion, CommandCompletions::Modules},
+ {eSettingsNameCompletion, CommandCompletions::SettingsNames},
+ {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames},
+ {eArchitectureCompletion, CommandCompletions::ArchitectureNames},
+ {eVariablePathCompletion, CommandCompletions::VariablePath},
+ {eNoCompletion, nullptr} // This one has to be last in the list.
+};
+
+bool CommandCompletions::InvokeCommonCompletionCallbacks(
+ CommandInterpreter &interpreter, uint32_t completion_mask,
+ CompletionRequest &request, SearchFilter *searcher) {
+ bool handled = false;
+
+ if (completion_mask & eCustomCompletion)
+ return false;
+
+ for (int i = 0;; i++) {
+ if (g_common_completions[i].type == eNoCompletion)
+ break;
+ else if ((g_common_completions[i].type & completion_mask) ==
+ g_common_completions[i].type &&
+ g_common_completions[i].callback != nullptr) {
+ handled = true;
+ g_common_completions[i].callback(interpreter, request, searcher);
+ }
+ }
+ return handled;
+}
+
+int CommandCompletions::SourceFiles(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ request.SetWordComplete(true);
+ // Find some way to switch "include support files..."
+ SourceFileCompleter completer(interpreter, false, request);
+
+ if (searcher == nullptr) {
+ lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
+ SearchFilterForUnconstrainedSearches null_searcher(target_sp);
+ completer.DoCompletion(&null_searcher);
+ } else {
+ completer.DoCompletion(searcher);
+ }
+ return request.GetNumberOfMatches();
+}
+
+static int DiskFilesOrDirectories(const llvm::Twine &partial_name,
+ bool only_directories, StringList &matches,
+ TildeExpressionResolver &Resolver) {
+ matches.Clear();
+
+ llvm::SmallString<256> CompletionBuffer;
+ llvm::SmallString<256> Storage;
+ partial_name.toVector(CompletionBuffer);
+
+ if (CompletionBuffer.size() >= PATH_MAX)
+ return matches.GetSize();
+
+ namespace path = llvm::sys::path;
+
+ llvm::StringRef SearchDir;
+ llvm::StringRef PartialItem;
+
+ if (CompletionBuffer.startswith("~")) {
+ llvm::StringRef Buffer(CompletionBuffer);
+ size_t FirstSep =
+ Buffer.find_if([](char c) { return path::is_separator(c); });
+
+ llvm::StringRef Username = Buffer.take_front(FirstSep);
+ llvm::StringRef Remainder;
+ if (FirstSep != llvm::StringRef::npos)
+ Remainder = Buffer.drop_front(FirstSep + 1);
+
+ llvm::SmallString<256> Resolved;
+ if (!Resolver.ResolveExact(Username, Resolved)) {
+ // We couldn't resolve it as a full username. If there were no slashes
+ // then this might be a partial username. We try to resolve it as such
+ // but after that, we're done regardless of any matches.
+ if (FirstSep == llvm::StringRef::npos) {
+ llvm::StringSet<> MatchSet;
+ Resolver.ResolvePartial(Username, MatchSet);
+ for (const auto &S : MatchSet) {
+ Resolved = S.getKey();
+ path::append(Resolved, path::get_separator());
+ matches.AppendString(Resolved);
+ }
+ }
+ return matches.GetSize();
+ }
+
+ // If there was no trailing slash, then we're done as soon as we resolve
+ // the expression to the correct directory. Otherwise we need to continue
+ // looking for matches within that directory.
+ if (FirstSep == llvm::StringRef::npos) {
+ // Make sure it ends with a separator.
+ path::append(CompletionBuffer, path::get_separator());
+ matches.AppendString(CompletionBuffer);
+ return matches.GetSize();
+ }
+
+ // We want to keep the form the user typed, so we special case this to
+ // search in the fully resolved directory, but CompletionBuffer keeps the
+ // unmodified form that the user typed.
+ Storage = Resolved;
+ llvm::StringRef RemainderDir = path::parent_path(Remainder);
+ if (!RemainderDir.empty()) {
+ // Append the remaining path to the resolved directory.
+ Storage.append(path::get_separator());
+ Storage.append(RemainderDir);
+ }
+ SearchDir = Storage;
+ } else {
+ SearchDir = path::parent_path(CompletionBuffer);
+ }
+
+ size_t FullPrefixLen = CompletionBuffer.size();
+
+ PartialItem = path::filename(CompletionBuffer);
+
+ // path::filename() will return "." when the passed path ends with a
+ // directory separator. We have to filter those out, but only when the
+ // "." doesn't come from the completion request itself.
+ if (PartialItem == "." && path::is_separator(CompletionBuffer.back()))
+ PartialItem = llvm::StringRef();
+
+ if (SearchDir.empty()) {
+ llvm::sys::fs::current_path(Storage);
+ SearchDir = Storage;
+ }
+ assert(!PartialItem.contains(path::get_separator()));
+
+ // SearchDir now contains the directory to search in, and Prefix contains the
+ // text we want to match against items in that directory.
+
+ FileSystem &fs = FileSystem::Instance();
+ std::error_code EC;
+ llvm::vfs::directory_iterator Iter = fs.DirBegin(SearchDir, EC);
+ llvm::vfs::directory_iterator End;
+ for (; Iter != End && !EC; Iter.increment(EC)) {
+ auto &Entry = *Iter;
+ llvm::ErrorOr<llvm::vfs::Status> Status = fs.GetStatus(Entry.path());
+
+ if (!Status)
+ continue;
+
+ auto Name = path::filename(Entry.path());
+
+ // Omit ".", ".."
+ if (Name == "." || Name == ".." || !Name.startswith(PartialItem))
+ continue;
+
+ bool is_dir = Status->isDirectory();
+
+ // If it's a symlink, then we treat it as a directory as long as the target
+ // is a directory.
+ if (Status->isSymlink()) {
+ FileSpec symlink_filespec(Entry.path());
+ FileSpec resolved_filespec;
+ auto error = fs.ResolveSymbolicLink(symlink_filespec, resolved_filespec);
+ if (error.Success())
+ is_dir = fs.IsDirectory(symlink_filespec);
+ }
+
+ if (only_directories && !is_dir)
+ continue;
+
+ // Shrink it back down so that it just has the original prefix the user
+ // typed and remove the part of the name which is common to the located
+ // item and what the user typed.
+ CompletionBuffer.resize(FullPrefixLen);
+ Name = Name.drop_front(PartialItem.size());
+ CompletionBuffer.append(Name);
+
+ if (is_dir) {
+ path::append(CompletionBuffer, path::get_separator());
+ }
+
+ matches.AppendString(CompletionBuffer);
+ }
+
+ return matches.GetSize();
+}
+
+static int DiskFilesOrDirectories(CompletionRequest &request,
+ bool only_directories) {
+ request.SetWordComplete(false);
+ StandardTildeExpressionResolver resolver;
+ StringList matches;
+ DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories,
+ matches, resolver);
+ request.AddCompletions(matches);
+ return request.GetNumberOfMatches();
+}
+
+int CommandCompletions::DiskFiles(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ return DiskFilesOrDirectories(request, /*only_dirs*/ false);
+}
+
+int CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name,
+ StringList &matches,
+ TildeExpressionResolver &Resolver) {
+ return DiskFilesOrDirectories(partial_file_name, false, matches, Resolver);
+}
+
+int CommandCompletions::DiskDirectories(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ return DiskFilesOrDirectories(request, /*only_dirs*/ true);
+}
+
+int CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name,
+ StringList &matches,
+ TildeExpressionResolver &Resolver) {
+ return DiskFilesOrDirectories(partial_file_name, true, matches, Resolver);
+}
+
+int CommandCompletions::Modules(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ request.SetWordComplete(true);
+ ModuleCompleter completer(interpreter, request);
+
+ if (searcher == nullptr) {
+ lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
+ SearchFilterForUnconstrainedSearches null_searcher(target_sp);
+ completer.DoCompletion(&null_searcher);
+ } else {
+ completer.DoCompletion(searcher);
+ }
+ return request.GetNumberOfMatches();
+}
+
+int CommandCompletions::Symbols(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ request.SetWordComplete(true);
+ SymbolCompleter completer(interpreter, request);
+
+ if (searcher == nullptr) {
+ lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget();
+ SearchFilterForUnconstrainedSearches null_searcher(target_sp);
+ completer.DoCompletion(&null_searcher);
+ } else {
+ completer.DoCompletion(searcher);
+ }
+ return request.GetNumberOfMatches();
+}
+
+int CommandCompletions::SettingsNames(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ // Cache the full setting name list
+ static StringList g_property_names;
+ if (g_property_names.GetSize() == 0) {
+ // Generate the full setting name list on demand
+ lldb::OptionValuePropertiesSP properties_sp(
+ interpreter.GetDebugger().GetValueProperties());
+ if (properties_sp) {
+ StreamString strm;
+ properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName);
+ const std::string &str = strm.GetString();
+ g_property_names.SplitIntoLines(str.c_str(), str.size());
+ }
+ }
+
+ size_t exact_matches_idx = SIZE_MAX;
+ StringList matches;
+ g_property_names.AutoComplete(request.GetCursorArgumentPrefix(), matches,
+ exact_matches_idx);
+ request.SetWordComplete(exact_matches_idx != SIZE_MAX);
+ request.AddCompletions(matches);
+ return request.GetNumberOfMatches();
+}
+
+int CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ StringList new_matches;
+ std::size_t num_matches = PluginManager::AutoCompletePlatformName(
+ request.GetCursorArgumentPrefix(), new_matches);
+ request.SetWordComplete(num_matches == 1);
+ request.AddCompletions(new_matches);
+ return request.GetNumberOfMatches();
+}
+
+int CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ const uint32_t num_matches = ArchSpec::AutoComplete(request);
+ request.SetWordComplete(num_matches == 1);
+ return num_matches;
+}
+
+int CommandCompletions::VariablePath(CommandInterpreter &interpreter,
+ CompletionRequest &request,
+ SearchFilter *searcher) {
+ return Variable::AutoComplete(interpreter.GetExecutionContext(), request);
+}
+
+CommandCompletions::Completer::Completer(CommandInterpreter &interpreter,
+ CompletionRequest &request)
+ : m_interpreter(interpreter), m_request(request) {}
+
+CommandCompletions::Completer::~Completer() = default;
+
+// SourceFileCompleter
+
+CommandCompletions::SourceFileCompleter::SourceFileCompleter(
+ CommandInterpreter &interpreter, bool include_support_files,
+ CompletionRequest &request)
+ : CommandCompletions::Completer(interpreter, request),
+ m_include_support_files(include_support_files), m_matching_files() {
+ FileSpec partial_spec(m_request.GetCursorArgumentPrefix());
+ m_file_name = partial_spec.GetFilename().GetCString();
+ m_dir_name = partial_spec.GetDirectory().GetCString();
+}
+
+lldb::SearchDepth CommandCompletions::SourceFileCompleter::GetDepth() {
+ return lldb::eSearchDepthCompUnit;
+}
+
+Searcher::CallbackReturn
+CommandCompletions::SourceFileCompleter::SearchCallback(SearchFilter &filter,
+ SymbolContext &context,
+ Address *addr,
+ bool complete) {
+ if (context.comp_unit != nullptr) {
+ if (m_include_support_files) {
+ FileSpecList supporting_files = context.comp_unit->GetSupportFiles();
+ for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) {
+ const FileSpec &sfile_spec =
+ supporting_files.GetFileSpecAtIndex(sfiles);
+ const char *sfile_file_name = sfile_spec.GetFilename().GetCString();
+ const char *sfile_dir_name = sfile_spec.GetFilename().GetCString();
+ bool match = false;
+ if (m_file_name && sfile_file_name &&
+ strstr(sfile_file_name, m_file_name) == sfile_file_name)
+ match = true;
+ if (match && m_dir_name && sfile_dir_name &&
+ strstr(sfile_dir_name, m_dir_name) != sfile_dir_name)
+ match = false;
+
+ if (match) {
+ m_matching_files.AppendIfUnique(sfile_spec);
+ }
+ }
+ } else {
+ const char *cur_file_name = context.comp_unit->GetFilename().GetCString();
+ const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString();
+
+ bool match = false;
+ if (m_file_name && cur_file_name &&
+ strstr(cur_file_name, m_file_name) == cur_file_name)
+ match = true;
+
+ if (match && m_dir_name && cur_dir_name &&
+ strstr(cur_dir_name, m_dir_name) != cur_dir_name)
+ match = false;
+
+ if (match) {
+ m_matching_files.AppendIfUnique(context.comp_unit);
+ }
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+size_t
+CommandCompletions::SourceFileCompleter::DoCompletion(SearchFilter *filter) {
+ filter->Search(*this);
+ // Now convert the filelist to completions:
+ for (size_t i = 0; i < m_matching_files.GetSize(); i++) {
+ m_request.AddCompletion(
+ m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString());
+ }
+ return m_request.GetNumberOfMatches();
+}
+
+// SymbolCompleter
+
+static bool regex_chars(const char comp) {
+ return (comp == '[' || comp == ']' || comp == '(' || comp == ')' ||
+ comp == '{' || comp == '}' || comp == '+' || comp == '.' ||
+ comp == '*' || comp == '|' || comp == '^' || comp == '$' ||
+ comp == '\\' || comp == '?');
+}
+
+CommandCompletions::SymbolCompleter::SymbolCompleter(
+ CommandInterpreter &interpreter, CompletionRequest &request)
+ : CommandCompletions::Completer(interpreter, request) {
+ std::string regex_str;
+ if (!m_request.GetCursorArgumentPrefix().empty()) {
+ regex_str.append("^");
+ regex_str.append(m_request.GetCursorArgumentPrefix());
+ } else {
+ // Match anything since the completion string is empty
+ regex_str.append(".");
+ }
+ std::string::iterator pos =
+ find_if(regex_str.begin() + 1, regex_str.end(), regex_chars);
+ while (pos < regex_str.end()) {
+ pos = regex_str.insert(pos, '\\');
+ pos = find_if(pos + 2, regex_str.end(), regex_chars);
+ }
+ m_regex.Compile(regex_str);
+}
+
+lldb::SearchDepth CommandCompletions::SymbolCompleter::GetDepth() {
+ return lldb::eSearchDepthModule;
+}
+
+Searcher::CallbackReturn CommandCompletions::SymbolCompleter::SearchCallback(
+ SearchFilter &filter, SymbolContext &context, Address *addr,
+ bool complete) {
+ if (context.module_sp) {
+ SymbolContextList sc_list;
+ const bool include_symbols = true;
+ const bool include_inlines = true;
+ const bool append = true;
+ context.module_sp->FindFunctions(m_regex, include_symbols, include_inlines,
+ append, sc_list);
+
+ SymbolContext sc;
+ // Now add the functions & symbols to the list - only add if unique:
+ for (uint32_t i = 0; i < sc_list.GetSize(); i++) {
+ if (sc_list.GetContextAtIndex(i, sc)) {
+ ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled);
+ if (!func_name.IsEmpty())
+ m_match_set.insert(func_name);
+ }
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+size_t CommandCompletions::SymbolCompleter::DoCompletion(SearchFilter *filter) {
+ filter->Search(*this);
+ collection::iterator pos = m_match_set.begin(), end = m_match_set.end();
+ for (pos = m_match_set.begin(); pos != end; pos++)
+ m_request.AddCompletion((*pos).GetCString());
+
+ return m_request.GetNumberOfMatches();
+}
+
+// ModuleCompleter
+CommandCompletions::ModuleCompleter::ModuleCompleter(
+ CommandInterpreter &interpreter, CompletionRequest &request)
+ : CommandCompletions::Completer(interpreter, request) {
+ FileSpec partial_spec(m_request.GetCursorArgumentPrefix());
+ m_file_name = partial_spec.GetFilename().GetCString();
+ m_dir_name = partial_spec.GetDirectory().GetCString();
+}
+
+lldb::SearchDepth CommandCompletions::ModuleCompleter::GetDepth() {
+ return lldb::eSearchDepthModule;
+}
+
+Searcher::CallbackReturn CommandCompletions::ModuleCompleter::SearchCallback(
+ SearchFilter &filter, SymbolContext &context, Address *addr,
+ bool complete) {
+ if (context.module_sp) {
+ const char *cur_file_name =
+ context.module_sp->GetFileSpec().GetFilename().GetCString();
+ const char *cur_dir_name =
+ context.module_sp->GetFileSpec().GetDirectory().GetCString();
+
+ bool match = false;
+ if (m_file_name && cur_file_name &&
+ strstr(cur_file_name, m_file_name) == cur_file_name)
+ match = true;
+
+ if (match && m_dir_name && cur_dir_name &&
+ strstr(cur_dir_name, m_dir_name) != cur_dir_name)
+ match = false;
+
+ if (match) {
+ m_request.AddCompletion(cur_file_name);
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+size_t CommandCompletions::ModuleCompleter::DoCompletion(SearchFilter *filter) {
+ filter->Search(*this);
+ return m_request.GetNumberOfMatches();
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.cpp
new file mode 100644
index 000000000000..957de475569c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.cpp
@@ -0,0 +1,105 @@
+//===-- CommandObjectApropos.cpp ---------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectApropos.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Utility/Args.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectApropos
+
+CommandObjectApropos::CommandObjectApropos(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "apropos",
+ "List debugger commands related to a word or subject.", nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData search_word_arg;
+
+ // Define the first (and only) variant of this arg.
+ search_word_arg.arg_type = eArgTypeSearchWord;
+ search_word_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the argument
+ // entry.
+ arg.push_back(search_word_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+}
+
+CommandObjectApropos::~CommandObjectApropos() = default;
+
+bool CommandObjectApropos::DoExecute(Args &args, CommandReturnObject &result) {
+ const size_t argc = args.GetArgumentCount();
+
+ if (argc == 1) {
+ auto search_word = args[0].ref;
+ if (!search_word.empty()) {
+ // The bulk of the work must be done inside the Command Interpreter,
+ // since the command dictionary is private.
+ StringList commands_found;
+ StringList commands_help;
+
+ m_interpreter.FindCommandsForApropos(search_word, commands_found,
+ commands_help, true, true, true);
+
+ if (commands_found.GetSize() == 0) {
+ result.AppendMessageWithFormat("No commands found pertaining to '%s'. "
+ "Try 'help' to see a complete list of "
+ "debugger commands.\n",
+ args[0].c_str());
+ } else {
+ if (commands_found.GetSize() > 0) {
+ result.AppendMessageWithFormat(
+ "The following commands may relate to '%s':\n", args[0].c_str());
+ size_t max_len = 0;
+
+ for (size_t i = 0; i < commands_found.GetSize(); ++i) {
+ size_t len = strlen(commands_found.GetStringAtIndex(i));
+ if (len > max_len)
+ max_len = len;
+ }
+
+ for (size_t i = 0; i < commands_found.GetSize(); ++i)
+ m_interpreter.OutputFormattedHelpText(
+ result.GetOutputStream(), commands_found.GetStringAtIndex(i),
+ "--", commands_help.GetStringAtIndex(i), max_len);
+ }
+ }
+
+ std::vector<const Property *> properties;
+ const size_t num_properties =
+ GetDebugger().Apropos(search_word, properties);
+ if (num_properties) {
+ const bool dump_qualified_name = true;
+ result.AppendMessageWithFormatv(
+ "\nThe following settings variables may relate to '{0}': \n\n",
+ args[0].ref);
+ for (size_t i = 0; i < num_properties; ++i)
+ properties[i]->DumpDescription(
+ m_interpreter, result.GetOutputStream(), 0, dump_qualified_name);
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("'' is not a valid search word.\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("'apropos' must be called with exactly one argument.\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.h
new file mode 100644
index 000000000000..37d86b17d1a2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectApropos.h
@@ -0,0 +1,31 @@
+//===-- CommandObjectApropos.h -----------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectApropos_h_
+#define liblldb_CommandObjectApropos_h_
+
+#include "lldb/Interpreter/CommandObject.h"
+
+namespace lldb_private {
+
+// CommandObjectApropos
+
+class CommandObjectApropos : public CommandObjectParsed {
+public:
+ CommandObjectApropos(CommandInterpreter &interpreter);
+
+ ~CommandObjectApropos() override;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectApropos_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.cpp
new file mode 100644
index 000000000000..c33f3834cb13
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -0,0 +1,2628 @@
+//===-- CommandObjectBreakpoint.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectBreakpoint.h"
+#include "CommandObjectBreakpointCommand.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointIDList.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <memory>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void AddBreakpointDescription(Stream *s, Breakpoint *bp,
+ lldb::DescriptionLevel level) {
+ s->IndentMore();
+ bp->GetDescription(s, level, true);
+ s->IndentLess();
+ s->EOL();
+}
+
+// Modifiable Breakpoint Options
+#pragma mark Modify::CommandOptions
+static constexpr OptionDefinition g_breakpoint_modify_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "ignore-count", 'i', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount, "Set the number of times this breakpoint is skipped before stopping." },
+ { LLDB_OPT_SET_1, false, "one-shot", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "The breakpoint is deleted the first time it stop causes a stop." },
+ { LLDB_OPT_SET_1, false, "thread-index", 'x', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeThreadIndex, "The breakpoint stops only for the thread whose index matches this argument." },
+ { LLDB_OPT_SET_1, false, "thread-id", 't', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeThreadID, "The breakpoint stops only for the thread whose TID matches this argument." },
+ { LLDB_OPT_SET_1, false, "thread-name", 'T', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeThreadName, "The breakpoint stops only for the thread whose thread name matches this argument." },
+ { LLDB_OPT_SET_1, false, "queue-name", 'q', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeQueueName, "The breakpoint stops only for threads in the queue whose name is given by this argument." },
+ { LLDB_OPT_SET_1, false, "condition", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeExpression, "The breakpoint stops only if this condition expression evaluates to true." },
+ { LLDB_OPT_SET_1, false, "auto-continue",'G', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "The breakpoint will auto-continue after running its commands." },
+ { LLDB_OPT_SET_2, false, "enable", 'e', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Enable the breakpoint." },
+ { LLDB_OPT_SET_3, false, "disable", 'd', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Disable the breakpoint." },
+ { LLDB_OPT_SET_4, false, "command", 'C', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommand, "A command to run when the breakpoint is hit, can be provided more than once, the commands will get run in order left to right." },
+ // clang-format on
+};
+class lldb_private::BreakpointOptionGroup : public OptionGroup
+{
+public:
+ BreakpointOptionGroup() :
+ OptionGroup(),
+ m_bp_opts(false) {}
+
+ ~BreakpointOptionGroup() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_modify_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = g_breakpoint_modify_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'c':
+ // Normally an empty breakpoint condition marks is as unset. But we need
+ // to say it was passed in.
+ m_bp_opts.SetCondition(option_arg.str().c_str());
+ m_bp_opts.m_set_flags.Set(BreakpointOptions::eCondition);
+ break;
+ case 'C':
+ m_commands.push_back(option_arg);
+ break;
+ case 'd':
+ m_bp_opts.SetEnabled(false);
+ break;
+ case 'e':
+ m_bp_opts.SetEnabled(true);
+ break;
+ case 'G': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_bp_opts.SetAutoContinue(value);
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -G option",
+ option_arg.str().c_str());
+ }
+ break;
+ case 'i':
+ {
+ uint32_t ignore_count;
+ if (option_arg.getAsInteger(0, ignore_count))
+ error.SetErrorStringWithFormat("invalid ignore count '%s'",
+ option_arg.str().c_str());
+ else
+ m_bp_opts.SetIgnoreCount(ignore_count);
+ }
+ break;
+ case 'o': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_bp_opts.SetOneShot(value);
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -o option",
+ option_arg.str().c_str());
+ } break;
+ case 't':
+ {
+ lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID;
+ if (option_arg[0] != '\0') {
+ if (option_arg.getAsInteger(0, thread_id))
+ error.SetErrorStringWithFormat("invalid thread id string '%s'",
+ option_arg.str().c_str());
+ }
+ m_bp_opts.SetThreadID(thread_id);
+ }
+ break;
+ case 'T':
+ m_bp_opts.GetThreadSpec()->SetName(option_arg.str().c_str());
+ break;
+ case 'q':
+ m_bp_opts.GetThreadSpec()->SetQueueName(option_arg.str().c_str());
+ break;
+ case 'x':
+ {
+ uint32_t thread_index = UINT32_MAX;
+ if (option_arg[0] != '\n') {
+ if (option_arg.getAsInteger(0, thread_index))
+ error.SetErrorStringWithFormat("invalid thread index string '%s'",
+ option_arg.str().c_str());
+ }
+ m_bp_opts.GetThreadSpec()->SetIndex(thread_index);
+ }
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_bp_opts.Clear();
+ m_commands.clear();
+ }
+
+ Status OptionParsingFinished(ExecutionContext *execution_context) override {
+ if (!m_commands.empty())
+ {
+ if (!m_commands.empty())
+ {
+ auto cmd_data = llvm::make_unique<BreakpointOptions::CommandData>();
+
+ for (std::string &str : m_commands)
+ cmd_data->user_source.AppendString(str);
+
+ cmd_data->stop_on_error = true;
+ m_bp_opts.SetCommandDataCallback(cmd_data);
+ }
+ }
+ return Status();
+ }
+
+ const BreakpointOptions &GetBreakpointOptions()
+ {
+ return m_bp_opts;
+ }
+
+ std::vector<std::string> m_commands;
+ BreakpointOptions m_bp_opts;
+
+};
+static constexpr OptionDefinition g_breakpoint_dummy_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Act on Dummy breakpoints - i.e. breakpoints set before a file is provided, "
+ "which prime new targets." },
+ // clang-format on
+};
+
+class BreakpointDummyOptionGroup : public OptionGroup
+{
+public:
+ BreakpointDummyOptionGroup() :
+ OptionGroup() {}
+
+ ~BreakpointDummyOptionGroup() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_dummy_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = g_breakpoint_modify_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'D':
+ m_use_dummy = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_use_dummy = false;
+ }
+
+ bool m_use_dummy;
+
+};
+
+// If an additional option set beyond LLDB_OPTION_SET_10 is added, make sure to
+// update the numbers passed to LLDB_OPT_SET_FROM_TO(...) appropriately.
+#define LLDB_OPT_NOT_10 (LLDB_OPT_SET_FROM_TO(1, 11) & ~LLDB_OPT_SET_10)
+#define LLDB_OPT_SKIP_PROLOGUE (LLDB_OPT_SET_1 | LLDB_OPT_SET_FROM_TO(3, 8))
+#define LLDB_OPT_FILE (LLDB_OPT_SET_FROM_TO(1, 11) & ~LLDB_OPT_SET_2 & ~LLDB_OPT_SET_10)
+#define LLDB_OPT_OFFSET_APPLIES (LLDB_OPT_SET_FROM_TO(1, 8) & ~LLDB_OPT_SET_2)
+#define LLDB_OPT_MOVE_TO_NEAREST_CODE (LLDB_OPT_SET_1 | LLDB_OPT_SET_9)
+#define LLDB_OPT_EXPR_LANGUAGE (LLDB_OPT_SET_FROM_TO(3, 8))
+
+static constexpr OptionDefinition g_breakpoint_set_options[] = {
+ // clang-format off
+ { LLDB_OPT_NOT_10, false, "shlib", 's', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Set the breakpoint only in this shared library. Can repeat this option "
+ "multiple times to specify multiple shared libraries." },
+ { LLDB_OPT_SET_ALL, false, "hardware", 'H', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Require the breakpoint to use hardware breakpoints." },
+ { LLDB_OPT_FILE, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "Specifies the source file in which to set this breakpoint. Note, by default "
+ "lldb only looks for files that are #included if they use the standard include "
+ "file extensions. To set breakpoints on .c/.cpp/.m/.mm files that are "
+ "#included, set target.inline-breakpoint-strategy to \"always\"." },
+ { LLDB_OPT_SET_1, true, "line", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLineNum, "Specifies the line number on which to set this breakpoint." },
+
+ // Comment out this option for the moment, as we don't actually use it, but
+ // will in the future. This way users won't see it, but the infrastructure is
+ // left in place.
+ // { 0, false, "column", 'C', OptionParser::eRequiredArgument, nullptr, "<column>",
+ // "Set the breakpoint by source location at this particular column."},
+
+ { LLDB_OPT_SET_2, true, "address", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Set the breakpoint at the specified address. If the address maps uniquely to "
+ "a particular binary, then the address will be converted to a \"file\" "
+ "address, so that the breakpoint will track that binary+offset no matter where "
+ "the binary eventually loads. Alternately, if you also specify the module - "
+ "with the -s option - then the address will be treated as a file address in "
+ "that module, and resolved accordingly. Again, this will allow lldb to track "
+ "that offset on subsequent reloads. The module need not have been loaded at "
+ "the time you specify this breakpoint, and will get resolved when the module "
+ "is loaded." },
+ { LLDB_OPT_SET_3, true, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, "Set the breakpoint by function name. Can be repeated multiple times to make "
+ "one breakpoint for multiple names" },
+ { LLDB_OPT_SET_9, false, "source-regexp-function", 'X', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, "When used with '-p' limits the source regex to source contained in the named "
+ "functions. Can be repeated multiple times." },
+ { LLDB_OPT_SET_4, true, "fullname", 'F', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeFullName, "Set the breakpoint by fully qualified function names. For C++ this means "
+ "namespaces and all arguments, and for Objective-C this means a full function "
+ "prototype with class and selector. Can be repeated multiple times to make "
+ "one breakpoint for multiple names." },
+ { LLDB_OPT_SET_5, true, "selector", 'S', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeSelector, "Set the breakpoint by ObjC selector name. Can be repeated multiple times to "
+ "make one breakpoint for multiple Selectors." },
+ { LLDB_OPT_SET_6, true, "method", 'M', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeMethod, "Set the breakpoint by C++ method names. Can be repeated multiple times to "
+ "make one breakpoint for multiple methods." },
+ { LLDB_OPT_SET_7, true, "func-regex", 'r', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeRegularExpression, "Set the breakpoint by function name, evaluating a regular-expression to find "
+ "the function name(s)." },
+ { LLDB_OPT_SET_8, true, "basename", 'b', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, "Set the breakpoint by function basename (C++ namespaces and arguments will be "
+ "ignored). Can be repeated multiple times to make one breakpoint for multiple "
+ "symbols." },
+ { LLDB_OPT_SET_9, true, "source-pattern-regexp", 'p', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeRegularExpression, "Set the breakpoint by specifying a regular expression which is matched "
+ "against the source text in a source file or files specified with the -f "
+ "option. The -f option can be specified more than once. If no source files "
+ "are specified, uses the current \"default source file\". If you want to "
+ "match against all source files, pass the \"--all-files\" option." },
+ { LLDB_OPT_SET_9, false, "all-files", 'A', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "All files are searched for source pattern matches." },
+ { LLDB_OPT_SET_11, true, "python-class", 'P', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePythonClass, "The name of the class that implement a scripted breakpoint." },
+ { LLDB_OPT_SET_11, false, "python-class-key", 'k', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone, "The key for a key/value pair passed to the class that implements a scripted breakpoint. Can be specified more than once." },
+ { LLDB_OPT_SET_11, false, "python-class-value", 'v', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone, "The value for the previous key in the pair passed to the class that implements a scripted breakpoint. Can be specified more than once." },
+ { LLDB_OPT_SET_10, true, "language-exception", 'E', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLanguage, "Set the breakpoint on exceptions thrown by the specified language (without "
+ "options, on throw but not catch.)" },
+ { LLDB_OPT_SET_10, false, "on-throw", 'w', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Set the breakpoint on exception throW." },
+ { LLDB_OPT_SET_10, false, "on-catch", 'h', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Set the breakpoint on exception catcH." },
+
+ // Don't add this option till it actually does something useful...
+ // { LLDB_OPT_SET_10, false, "exception-typename", 'O', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeTypeName,
+ // "The breakpoint will only stop if an exception Object of this type is thrown. Can be repeated multiple times to stop for multiple object types" },
+
+ { LLDB_OPT_EXPR_LANGUAGE, false, "language", 'L', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLanguage, "Specifies the Language to use when interpreting the breakpoint's expression "
+ "(note: currently only implemented for setting breakpoints on identifiers). "
+ "If not set the target.language setting is used." },
+ { LLDB_OPT_SKIP_PROLOGUE, false, "skip-prologue", 'K', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "sKip the prologue if the breakpoint is at the beginning of a function. "
+ "If not set the target.skip-prologue setting is used." },
+ { LLDB_OPT_SET_ALL, false, "breakpoint-name", 'N', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBreakpointName, "Adds this to the list of names for this breakpoint." },
+ { LLDB_OPT_OFFSET_APPLIES, false, "address-slide", 'R', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddress, "Add the specified offset to whatever address(es) the breakpoint resolves to. "
+ "At present this applies the offset directly as given, and doesn't try to align it to instruction boundaries." },
+ { LLDB_OPT_MOVE_TO_NEAREST_CODE, false, "move-to-nearest-code", 'm', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Move breakpoints to nearest code. If not set the target.move-to-nearest-code "
+ "setting is used." },
+ // clang-format on
+};
+
+// CommandObjectBreakpointSet
+
+class CommandObjectBreakpointSet : public CommandObjectParsed {
+public:
+ enum BreakpointSetType {
+ eSetTypeInvalid,
+ eSetTypeFileAndLine,
+ eSetTypeAddress,
+ eSetTypeFunctionName,
+ eSetTypeFunctionRegexp,
+ eSetTypeSourceRegexp,
+ eSetTypeException,
+ eSetTypeScripted,
+ };
+
+ CommandObjectBreakpointSet(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "breakpoint set",
+ "Sets a breakpoint or set of breakpoints in the executable.",
+ "breakpoint set <cmd-options>"),
+ m_bp_opts(), m_options() {
+ // We're picking up all the normal options, commands and disable.
+ m_all_options.Append(&m_bp_opts,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_3 | LLDB_OPT_SET_4,
+ LLDB_OPT_SET_ALL);
+ m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
+ m_all_options.Append(&m_options);
+ m_all_options.Finalize();
+ }
+
+ ~CommandObjectBreakpointSet() override = default;
+
+ Options *GetOptions() override { return &m_all_options; }
+
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions()
+ : OptionGroup(), m_condition(), m_filenames(), m_line_num(0), m_column(0),
+ m_func_names(), m_func_name_type_mask(eFunctionNameTypeNone),
+ m_func_regexp(), m_source_text_regexp(), m_modules(), m_load_addr(),
+ m_catch_bp(false), m_throw_bp(true), m_hardware(false),
+ m_exception_language(eLanguageTypeUnknown),
+ m_language(lldb::eLanguageTypeUnknown),
+ m_skip_prologue(eLazyBoolCalculate),
+ m_all_files(false), m_move_to_nearest_code(eLazyBoolCalculate) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = g_breakpoint_set_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'a': {
+ m_load_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ } break;
+
+ case 'A':
+ m_all_files = true;
+ break;
+
+ case 'b':
+ m_func_names.push_back(option_arg);
+ m_func_name_type_mask |= eFunctionNameTypeBase;
+ break;
+
+ case 'C':
+ if (option_arg.getAsInteger(0, m_column))
+ error.SetErrorStringWithFormat("invalid column number: %s",
+ option_arg.str().c_str());
+ break;
+
+ case 'E': {
+ LanguageType language = Language::GetLanguageTypeFromString(option_arg);
+
+ switch (language) {
+ case eLanguageTypeC89:
+ case eLanguageTypeC:
+ case eLanguageTypeC99:
+ case eLanguageTypeC11:
+ m_exception_language = eLanguageTypeC;
+ break;
+ case eLanguageTypeC_plus_plus:
+ case eLanguageTypeC_plus_plus_03:
+ case eLanguageTypeC_plus_plus_11:
+ case eLanguageTypeC_plus_plus_14:
+ m_exception_language = eLanguageTypeC_plus_plus;
+ break;
+ case eLanguageTypeObjC:
+ m_exception_language = eLanguageTypeObjC;
+ break;
+ case eLanguageTypeObjC_plus_plus:
+ error.SetErrorStringWithFormat(
+ "Set exception breakpoints separately for c++ and objective-c");
+ break;
+ case eLanguageTypeUnknown:
+ error.SetErrorStringWithFormat(
+ "Unknown language type: '%s' for exception breakpoint",
+ option_arg.str().c_str());
+ break;
+ default:
+ error.SetErrorStringWithFormat(
+ "Unsupported language type: '%s' for exception breakpoint",
+ option_arg.str().c_str());
+ }
+ } break;
+
+ case 'f':
+ m_filenames.AppendIfUnique(FileSpec(option_arg));
+ break;
+
+ case 'F':
+ m_func_names.push_back(option_arg);
+ m_func_name_type_mask |= eFunctionNameTypeFull;
+ break;
+
+ case 'h': {
+ bool success;
+ m_catch_bp = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for on-catch option: '%s'",
+ option_arg.str().c_str());
+ } break;
+
+ case 'H':
+ m_hardware = true;
+ break;
+
+ case 'k': {
+ if (m_current_key.empty())
+ m_current_key.assign(option_arg);
+ else
+ error.SetErrorStringWithFormat("Key: %s missing value.",
+ m_current_key.c_str());
+
+ } break;
+ case 'K': {
+ bool success;
+ bool value;
+ value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (value)
+ m_skip_prologue = eLazyBoolYes;
+ else
+ m_skip_prologue = eLazyBoolNo;
+
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for skip prologue option: '%s'",
+ option_arg.str().c_str());
+ } break;
+
+ case 'l':
+ if (option_arg.getAsInteger(0, m_line_num))
+ error.SetErrorStringWithFormat("invalid line number: %s.",
+ option_arg.str().c_str());
+ break;
+
+ case 'L':
+ m_language = Language::GetLanguageTypeFromString(option_arg);
+ if (m_language == eLanguageTypeUnknown)
+ error.SetErrorStringWithFormat(
+ "Unknown language type: '%s' for breakpoint",
+ option_arg.str().c_str());
+ break;
+
+ case 'm': {
+ bool success;
+ bool value;
+ value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (value)
+ m_move_to_nearest_code = eLazyBoolYes;
+ else
+ m_move_to_nearest_code = eLazyBoolNo;
+
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for move-to-nearest-code option: '%s'",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 'M':
+ m_func_names.push_back(option_arg);
+ m_func_name_type_mask |= eFunctionNameTypeMethod;
+ break;
+
+ case 'n':
+ m_func_names.push_back(option_arg);
+ m_func_name_type_mask |= eFunctionNameTypeAuto;
+ break;
+
+ case 'N': {
+ if (BreakpointID::StringIsBreakpointName(option_arg, error))
+ m_breakpoint_names.push_back(option_arg);
+ else
+ error.SetErrorStringWithFormat("Invalid breakpoint name: %s",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 'R': {
+ lldb::addr_t tmp_offset_addr;
+ tmp_offset_addr = OptionArgParser::ToAddress(execution_context,
+ option_arg, 0, &error);
+ if (error.Success())
+ m_offset_addr = tmp_offset_addr;
+ } break;
+
+ case 'O':
+ m_exception_extra_args.AppendArgument("-O");
+ m_exception_extra_args.AppendArgument(option_arg);
+ break;
+
+ case 'p':
+ m_source_text_regexp.assign(option_arg);
+ break;
+
+ case 'P':
+ m_python_class.assign(option_arg);
+ break;
+
+ case 'r':
+ m_func_regexp.assign(option_arg);
+ break;
+
+ case 's':
+ m_modules.AppendIfUnique(FileSpec(option_arg));
+ break;
+
+ case 'S':
+ m_func_names.push_back(option_arg);
+ m_func_name_type_mask |= eFunctionNameTypeSelector;
+ break;
+
+ case 'v': {
+ if (!m_current_key.empty()) {
+ m_extra_args_sp->AddStringItem(m_current_key, option_arg);
+ m_current_key.clear();
+ }
+ else
+ error.SetErrorStringWithFormat("Value \"%s\" missing matching key.",
+ option_arg.str().c_str());
+ } break;
+
+ case 'w': {
+ bool success;
+ m_throw_bp = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for on-throw option: '%s'",
+ option_arg.str().c_str());
+ } break;
+
+ case 'X':
+ m_source_regex_func_names.insert(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filenames.Clear();
+ m_line_num = 0;
+ m_column = 0;
+ m_func_names.clear();
+ m_func_name_type_mask = eFunctionNameTypeNone;
+ m_func_regexp.clear();
+ m_source_text_regexp.clear();
+ m_modules.Clear();
+ m_load_addr = LLDB_INVALID_ADDRESS;
+ m_offset_addr = 0;
+ m_catch_bp = false;
+ m_throw_bp = true;
+ m_hardware = false;
+ m_exception_language = eLanguageTypeUnknown;
+ m_language = lldb::eLanguageTypeUnknown;
+ m_skip_prologue = eLazyBoolCalculate;
+ m_breakpoint_names.clear();
+ m_all_files = false;
+ m_exception_extra_args.Clear();
+ m_move_to_nearest_code = eLazyBoolCalculate;
+ m_source_regex_func_names.clear();
+ m_python_class.clear();
+ m_extra_args_sp = std::make_shared<StructuredData::Dictionary>();
+ m_current_key.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_set_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_condition;
+ FileSpecList m_filenames;
+ uint32_t m_line_num;
+ uint32_t m_column;
+ std::vector<std::string> m_func_names;
+ std::vector<std::string> m_breakpoint_names;
+ lldb::FunctionNameType m_func_name_type_mask;
+ std::string m_func_regexp;
+ std::string m_source_text_regexp;
+ FileSpecList m_modules;
+ lldb::addr_t m_load_addr;
+ lldb::addr_t m_offset_addr;
+ bool m_catch_bp;
+ bool m_throw_bp;
+ bool m_hardware; // Request to use hardware breakpoints
+ lldb::LanguageType m_exception_language;
+ lldb::LanguageType m_language;
+ LazyBool m_skip_prologue;
+ bool m_all_files;
+ Args m_exception_extra_args;
+ LazyBool m_move_to_nearest_code;
+ std::unordered_set<std::string> m_source_regex_func_names;
+ std::string m_python_class;
+ StructuredData::DictionarySP m_extra_args_sp;
+ std::string m_current_key;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget(m_dummy_options.m_use_dummy);
+
+ if (target == nullptr) {
+ result.AppendError("Invalid target. Must set target before setting "
+ "breakpoints (see 'target create' command).");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // The following are the various types of breakpoints that could be set:
+ // 1). -f -l -p [-s -g] (setting breakpoint by source location)
+ // 2). -a [-s -g] (setting breakpoint by address)
+ // 3). -n [-s -g] (setting breakpoint by function name)
+ // 4). -r [-s -g] (setting breakpoint by function name regular
+ // expression)
+ // 5). -p -f (setting a breakpoint by comparing a reg-exp
+ // to source text)
+ // 6). -E [-w -h] (setting a breakpoint for exceptions for a
+ // given language.)
+
+ BreakpointSetType break_type = eSetTypeInvalid;
+
+ if (!m_options.m_python_class.empty())
+ break_type = eSetTypeScripted;
+ else if (m_options.m_line_num != 0)
+ break_type = eSetTypeFileAndLine;
+ else if (m_options.m_load_addr != LLDB_INVALID_ADDRESS)
+ break_type = eSetTypeAddress;
+ else if (!m_options.m_func_names.empty())
+ break_type = eSetTypeFunctionName;
+ else if (!m_options.m_func_regexp.empty())
+ break_type = eSetTypeFunctionRegexp;
+ else if (!m_options.m_source_text_regexp.empty())
+ break_type = eSetTypeSourceRegexp;
+ else if (m_options.m_exception_language != eLanguageTypeUnknown)
+ break_type = eSetTypeException;
+
+ BreakpointSP bp_sp = nullptr;
+ FileSpec module_spec;
+ const bool internal = false;
+
+ // If the user didn't specify skip-prologue, having an offset should turn
+ // that off.
+ if (m_options.m_offset_addr != 0 &&
+ m_options.m_skip_prologue == eLazyBoolCalculate)
+ m_options.m_skip_prologue = eLazyBoolNo;
+
+ switch (break_type) {
+ case eSetTypeFileAndLine: // Breakpoint by source position
+ {
+ FileSpec file;
+ const size_t num_files = m_options.m_filenames.GetSize();
+ if (num_files == 0) {
+ if (!GetDefaultFile(target, file, result)) {
+ result.AppendError("No file supplied and no default file available.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else if (num_files > 1) {
+ result.AppendError("Only one file at a time is allowed for file and "
+ "line breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else
+ file = m_options.m_filenames.GetFileSpecAtIndex(0);
+
+ // Only check for inline functions if
+ LazyBool check_inlines = eLazyBoolCalculate;
+
+ bp_sp = target->CreateBreakpoint(&(m_options.m_modules),
+ file,
+ m_options.m_line_num,
+ m_options.m_column,
+ m_options.m_offset_addr,
+ check_inlines,
+ m_options.m_skip_prologue,
+ internal,
+ m_options.m_hardware,
+ m_options.m_move_to_nearest_code);
+ } break;
+
+ case eSetTypeAddress: // Breakpoint by address
+ {
+ // If a shared library has been specified, make an lldb_private::Address
+ // with the library, and use that. That way the address breakpoint
+ // will track the load location of the library.
+ size_t num_modules_specified = m_options.m_modules.GetSize();
+ if (num_modules_specified == 1) {
+ const FileSpec *file_spec =
+ m_options.m_modules.GetFileSpecPointerAtIndex(0);
+ bp_sp = target->CreateAddressInModuleBreakpoint(m_options.m_load_addr,
+ internal, file_spec,
+ m_options.m_hardware);
+ } else if (num_modules_specified == 0) {
+ bp_sp = target->CreateBreakpoint(m_options.m_load_addr, internal,
+ m_options.m_hardware);
+ } else {
+ result.AppendError("Only one shared library can be specified for "
+ "address breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ break;
+ }
+ case eSetTypeFunctionName: // Breakpoint by function name
+ {
+ FunctionNameType name_type_mask = m_options.m_func_name_type_mask;
+
+ if (name_type_mask == 0)
+ name_type_mask = eFunctionNameTypeAuto;
+
+ bp_sp = target->CreateBreakpoint(&(m_options.m_modules),
+ &(m_options.m_filenames),
+ m_options.m_func_names,
+ name_type_mask,
+ m_options.m_language,
+ m_options.m_offset_addr,
+ m_options.m_skip_prologue,
+ internal,
+ m_options.m_hardware);
+ } break;
+
+ case eSetTypeFunctionRegexp: // Breakpoint by regular expression function
+ // name
+ {
+ RegularExpression regexp(m_options.m_func_regexp);
+ if (!regexp.IsValid()) {
+ char err_str[1024];
+ regexp.GetErrorAsCString(err_str, sizeof(err_str));
+ result.AppendErrorWithFormat(
+ "Function name regular expression could not be compiled: \"%s\"",
+ err_str);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ bp_sp = target->CreateFuncRegexBreakpoint(&(m_options.m_modules),
+ &(m_options.m_filenames),
+ regexp,
+ m_options.m_language,
+ m_options.m_skip_prologue,
+ internal,
+ m_options.m_hardware);
+ }
+ break;
+ case eSetTypeSourceRegexp: // Breakpoint by regexp on source text.
+ {
+ const size_t num_files = m_options.m_filenames.GetSize();
+
+ if (num_files == 0 && !m_options.m_all_files) {
+ FileSpec file;
+ if (!GetDefaultFile(target, file, result)) {
+ result.AppendError(
+ "No files provided and could not find default file.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ m_options.m_filenames.Append(file);
+ }
+ }
+
+ RegularExpression regexp(m_options.m_source_text_regexp);
+ if (!regexp.IsValid()) {
+ char err_str[1024];
+ regexp.GetErrorAsCString(err_str, sizeof(err_str));
+ result.AppendErrorWithFormat(
+ "Source text regular expression could not be compiled: \"%s\"",
+ err_str);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ bp_sp =
+ target->CreateSourceRegexBreakpoint(&(m_options.m_modules),
+ &(m_options.m_filenames),
+ m_options
+ .m_source_regex_func_names,
+ regexp,
+ internal,
+ m_options.m_hardware,
+ m_options.m_move_to_nearest_code);
+ } break;
+ case eSetTypeException: {
+ Status precond_error;
+ bp_sp = target->CreateExceptionBreakpoint(m_options.m_exception_language,
+ m_options.m_catch_bp,
+ m_options.m_throw_bp,
+ internal,
+ &m_options
+ .m_exception_extra_args,
+ &precond_error);
+ if (precond_error.Fail()) {
+ result.AppendErrorWithFormat(
+ "Error setting extra exception arguments: %s",
+ precond_error.AsCString());
+ target->RemoveBreakpointByID(bp_sp->GetID());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } break;
+ case eSetTypeScripted: {
+
+ Status error;
+ bp_sp = target->CreateScriptedBreakpoint(m_options.m_python_class,
+ &(m_options.m_modules),
+ &(m_options.m_filenames),
+ false,
+ m_options.m_hardware,
+ m_options.m_extra_args_sp,
+ &error);
+ if (error.Fail()) {
+ result.AppendErrorWithFormat(
+ "Error setting extra exception arguments: %s",
+ error.AsCString());
+ target->RemoveBreakpointByID(bp_sp->GetID());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } break;
+ default:
+ break;
+ }
+
+ // Now set the various options that were passed in:
+ if (bp_sp) {
+ bp_sp->GetOptions()->CopyOverSetOptions(m_bp_opts.GetBreakpointOptions());
+
+ if (!m_options.m_breakpoint_names.empty()) {
+ Status name_error;
+ for (auto name : m_options.m_breakpoint_names) {
+ target->AddNameToBreakpoint(bp_sp, name.c_str(), name_error);
+ if (name_error.Fail()) {
+ result.AppendErrorWithFormat("Invalid breakpoint name: %s",
+ name.c_str());
+ target->RemoveBreakpointByID(bp_sp->GetID());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ }
+ }
+
+ if (bp_sp) {
+ Stream &output_stream = result.GetOutputStream();
+ const bool show_locations = false;
+ bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
+ show_locations);
+ if (target == GetDebugger().GetDummyTarget())
+ output_stream.Printf("Breakpoint set in dummy target, will get copied "
+ "into future targets.\n");
+ else {
+ // Don't print out this warning for exception breakpoints. They can
+ // get set before the target is set, but we won't know how to actually
+ // set the breakpoint till we run.
+ if (bp_sp->GetNumLocations() == 0 && break_type != eSetTypeException) {
+ output_stream.Printf("WARNING: Unable to resolve breakpoint to any "
+ "actual locations.\n");
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else if (!bp_sp) {
+ result.AppendError("Breakpoint creation failed: No breakpoint created.");
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ bool GetDefaultFile(Target *target, FileSpec &file,
+ CommandReturnObject &result) {
+ uint32_t default_line;
+ // First use the Source Manager's default file. Then use the current stack
+ // frame's file.
+ if (!target->GetSourceManager().GetDefaultFileAndLine(file, default_line)) {
+ StackFrame *cur_frame = m_exe_ctx.GetFramePtr();
+ if (cur_frame == nullptr) {
+ result.AppendError(
+ "No selected frame to use to find the default file.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (!cur_frame->HasDebugInformation()) {
+ result.AppendError("Cannot use the selected frame to find the default "
+ "file, it has no debug info.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ const SymbolContext &sc =
+ cur_frame->GetSymbolContext(eSymbolContextLineEntry);
+ if (sc.line_entry.file) {
+ file = sc.line_entry.file;
+ } else {
+ result.AppendError("Can't find the file for the selected frame to "
+ "use as the default file.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ BreakpointOptionGroup m_bp_opts;
+ BreakpointDummyOptionGroup m_dummy_options;
+ CommandOptions m_options;
+ OptionGroupOptions m_all_options;
+};
+
+// CommandObjectBreakpointModify
+#pragma mark Modify
+
+class CommandObjectBreakpointModify : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointModify(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "breakpoint modify",
+ "Modify the options on a breakpoint or set of "
+ "breakpoints in the executable. "
+ "If no breakpoint is specified, acts on the last "
+ "created breakpoint. "
+ "With the exception of -e, -d and -i, passing an "
+ "empty argument clears the modification.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
+ eArgTypeBreakpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+
+ m_options.Append(&m_bp_opts,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3,
+ LLDB_OPT_SET_ALL);
+ m_options.Append(&m_dummy_opts, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
+ m_options.Finalize();
+ }
+
+ ~CommandObjectBreakpointModify() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget(m_dummy_opts.m_use_dummy);
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ BreakpointIDList valid_bp_ids;
+
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::disablePerm);
+
+ if (result.Succeeded()) {
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *bp =
+ target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ BreakpointLocation *location =
+ bp->FindLocationByID(cur_bp_id.GetLocationID()).get();
+ if (location)
+ location->GetLocationOptions()
+ ->CopyOverSetOptions(m_bp_opts.GetBreakpointOptions());
+ } else {
+ bp->GetOptions()
+ ->CopyOverSetOptions(m_bp_opts.GetBreakpointOptions());
+ }
+ }
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ BreakpointOptionGroup m_bp_opts;
+ BreakpointDummyOptionGroup m_dummy_opts;
+ OptionGroupOptions m_options;
+};
+
+// CommandObjectBreakpointEnable
+#pragma mark Enable
+
+class CommandObjectBreakpointEnable : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointEnable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "enable",
+ "Enable the specified disabled breakpoint(s). If "
+ "no breakpoints are specified, enable all of them.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
+ eArgTypeBreakpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointEnable() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget();
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist to be enabled.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.empty()) {
+ // No breakpoint selected; enable all currently set breakpoints.
+ target->EnableAllowedBreakpoints();
+ result.AppendMessageWithFormat("All breakpoints enabled. (%" PRIu64
+ " breakpoints)\n",
+ (uint64_t)num_breakpoints);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular breakpoint selected; enable that breakpoint.
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::disablePerm);
+
+ if (result.Succeeded()) {
+ int enable_count = 0;
+ int loc_count = 0;
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *breakpoint =
+ target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ BreakpointLocation *location =
+ breakpoint->FindLocationByID(cur_bp_id.GetLocationID()).get();
+ if (location) {
+ location->SetEnabled(true);
+ ++loc_count;
+ }
+ } else {
+ breakpoint->SetEnabled(true);
+ ++enable_count;
+ }
+ }
+ }
+ result.AppendMessageWithFormat("%d breakpoints enabled.\n",
+ enable_count + loc_count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectBreakpointDisable
+#pragma mark Disable
+
+class CommandObjectBreakpointDisable : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "breakpoint disable",
+ "Disable the specified breakpoint(s) without deleting "
+ "them. If none are specified, disable all "
+ "breakpoints.",
+ nullptr) {
+ SetHelpLong(
+ "Disable the specified breakpoint(s) without deleting them. \
+If none are specified, disable all breakpoints."
+ R"(
+
+)"
+ "Note: disabling a breakpoint will cause none of its locations to be hit \
+regardless of whether individual locations are enabled or disabled. After the sequence:"
+ R"(
+
+ (lldb) break disable 1
+ (lldb) break enable 1.1
+
+execution will NOT stop at location 1.1. To achieve that, type:
+
+ (lldb) break disable 1.*
+ (lldb) break enable 1.1
+
+)"
+ "The first command disables all locations for breakpoint 1, \
+the second re-enables the first location.");
+
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
+ eArgTypeBreakpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointDisable() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget();
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist to be disabled.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.empty()) {
+ // No breakpoint selected; disable all currently set breakpoints.
+ target->DisableAllowedBreakpoints();
+ result.AppendMessageWithFormat("All breakpoints disabled. (%" PRIu64
+ " breakpoints)\n",
+ (uint64_t)num_breakpoints);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular breakpoint selected; disable that breakpoint.
+ BreakpointIDList valid_bp_ids;
+
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::disablePerm);
+
+ if (result.Succeeded()) {
+ int disable_count = 0;
+ int loc_count = 0;
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *breakpoint =
+ target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ BreakpointLocation *location =
+ breakpoint->FindLocationByID(cur_bp_id.GetLocationID()).get();
+ if (location) {
+ location->SetEnabled(false);
+ ++loc_count;
+ }
+ } else {
+ breakpoint->SetEnabled(false);
+ ++disable_count;
+ }
+ }
+ }
+ result.AppendMessageWithFormat("%d breakpoints disabled.\n",
+ disable_count + loc_count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectBreakpointList
+
+#pragma mark List::CommandOptions
+static constexpr OptionDefinition g_breakpoint_list_options[] = {
+ // FIXME: We need to add an "internal" command, and then add this sort of
+ // thing to it. But I need to see it for now, and don't want to wait.
+#define LLDB_OPTIONS_breakpoint_list
+#include "CommandOptions.inc"
+};
+
+#pragma mark List
+
+class CommandObjectBreakpointList : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "breakpoint list",
+ "List some or all breakpoints at configurable levels of detail.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData bp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ bp_id_arg.arg_type = eArgTypeBreakpointID;
+ bp_id_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(bp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_level(lldb::eDescriptionLevelBrief), m_use_dummy(false) {
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'b':
+ m_level = lldb::eDescriptionLevelBrief;
+ break;
+ case 'D':
+ m_use_dummy = true;
+ break;
+ case 'f':
+ m_level = lldb::eDescriptionLevelFull;
+ break;
+ case 'v':
+ m_level = lldb::eDescriptionLevelVerbose;
+ break;
+ case 'i':
+ m_internal = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_level = lldb::eDescriptionLevelFull;
+ m_internal = false;
+ m_use_dummy = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ lldb::DescriptionLevel m_level;
+
+ bool m_internal;
+ bool m_use_dummy;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
+
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No current target or breakpoints.");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ const BreakpointList &breakpoints =
+ target->GetBreakpointList(m_options.m_internal);
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList(m_options.m_internal).GetListMutex(lock);
+
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendMessage("No breakpoints currently set.");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ Stream &output_stream = result.GetOutputStream();
+
+ if (command.empty()) {
+ // No breakpoint selected; show info about all currently set breakpoints.
+ result.AppendMessage("Current breakpoints:");
+ for (size_t i = 0; i < num_breakpoints; ++i) {
+ Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex(i).get();
+ if (breakpoint->AllowList())
+ AddBreakpointDescription(&output_stream, breakpoint,
+ m_options.m_level);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular breakpoints selected; show info about that breakpoint.
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ if (result.Succeeded()) {
+ for (size_t i = 0; i < valid_bp_ids.GetSize(); ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+ Breakpoint *breakpoint =
+ target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ AddBreakpointDescription(&output_stream, breakpoint,
+ m_options.m_level);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("Invalid breakpoint ID.");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectBreakpointClear
+#pragma mark Clear::CommandOptions
+
+static constexpr OptionDefinition g_breakpoint_clear_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "Specify the breakpoint by source location in this particular file." },
+ { LLDB_OPT_SET_1, true, "line", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLineNum, "Specify the breakpoint by source location at this particular line." }
+ // clang-format on
+};
+
+#pragma mark Clear
+
+class CommandObjectBreakpointClear : public CommandObjectParsed {
+public:
+ enum BreakpointClearType { eClearTypeInvalid, eClearTypeFileAndLine };
+
+ CommandObjectBreakpointClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "breakpoint clear",
+ "Delete or disable breakpoints matching the "
+ "specified source file and line.",
+ "breakpoint clear <cmd-options>"),
+ m_options() {}
+
+ ~CommandObjectBreakpointClear() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_filename(), m_line_num(0) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_filename.assign(option_arg);
+ break;
+
+ case 'l':
+ option_arg.getAsInteger(0, m_line_num);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filename.clear();
+ m_line_num = 0;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_clear_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_filename;
+ uint32_t m_line_num;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget();
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // The following are the various types of breakpoints that could be
+ // cleared:
+ // 1). -f -l (clearing breakpoint by source location)
+
+ BreakpointClearType break_type = eClearTypeInvalid;
+
+ if (m_options.m_line_num != 0)
+ break_type = eClearTypeFileAndLine;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ BreakpointList &breakpoints = target->GetBreakpointList();
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ // Early return if there's no breakpoint at all.
+ if (num_breakpoints == 0) {
+ result.AppendError("Breakpoint clear: No breakpoint cleared.");
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ }
+
+ // Find matching breakpoints and delete them.
+
+ // First create a copy of all the IDs.
+ std::vector<break_id_t> BreakIDs;
+ for (size_t i = 0; i < num_breakpoints; ++i)
+ BreakIDs.push_back(breakpoints.GetBreakpointAtIndex(i)->GetID());
+
+ int num_cleared = 0;
+ StreamString ss;
+ switch (break_type) {
+ case eClearTypeFileAndLine: // Breakpoint by source position
+ {
+ const ConstString filename(m_options.m_filename.c_str());
+ BreakpointLocationCollection loc_coll;
+
+ for (size_t i = 0; i < num_breakpoints; ++i) {
+ Breakpoint *bp = breakpoints.FindBreakpointByID(BreakIDs[i]).get();
+
+ if (bp->GetMatchingFileLine(filename, m_options.m_line_num, loc_coll)) {
+ // If the collection size is 0, it's a full match and we can just
+ // remove the breakpoint.
+ if (loc_coll.GetSize() == 0) {
+ bp->GetDescription(&ss, lldb::eDescriptionLevelBrief);
+ ss.EOL();
+ target->RemoveBreakpointByID(bp->GetID());
+ ++num_cleared;
+ }
+ }
+ }
+ } break;
+
+ default:
+ break;
+ }
+
+ if (num_cleared > 0) {
+ Stream &output_stream = result.GetOutputStream();
+ output_stream.Printf("%d breakpoints cleared:\n", num_cleared);
+ output_stream << ss.GetString();
+ output_stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("Breakpoint clear: No breakpoint cleared.");
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectBreakpointDelete
+static constexpr OptionDefinition g_breakpoint_delete_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "force", 'f', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Delete all breakpoints without querying for confirmation." },
+ { LLDB_OPT_SET_1, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Delete Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets." },
+ // clang-format on
+};
+
+#pragma mark Delete
+
+class CommandObjectBreakpointDelete : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "breakpoint delete",
+ "Delete the specified breakpoint(s). If no "
+ "breakpoints are specified, delete them all.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
+ eArgTypeBreakpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointDelete() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_use_dummy(false), m_force(false) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_force = true;
+ break;
+
+ case 'D':
+ m_use_dummy = true;
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_use_dummy = false;
+ m_force = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_delete_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_use_dummy;
+ bool m_force;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
+
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist to be deleted.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.empty()) {
+ if (!m_options.m_force &&
+ !m_interpreter.Confirm(
+ "About to delete all breakpoints, do you want to do that?",
+ true)) {
+ result.AppendMessage("Operation cancelled...");
+ } else {
+ target->RemoveAllowedBreakpoints();
+ result.AppendMessageWithFormat(
+ "All breakpoints removed. (%" PRIu64 " breakpoint%s)\n",
+ (uint64_t)num_breakpoints, num_breakpoints > 1 ? "s" : "");
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular breakpoint selected; disable that breakpoint.
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::deletePerm);
+
+ if (result.Succeeded()) {
+ int delete_count = 0;
+ int disable_count = 0;
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *breakpoint =
+ target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ BreakpointLocation *location =
+ breakpoint->FindLocationByID(cur_bp_id.GetLocationID()).get();
+ // It makes no sense to try to delete individual locations, so we
+ // disable them instead.
+ if (location) {
+ location->SetEnabled(false);
+ ++disable_count;
+ }
+ } else {
+ target->RemoveBreakpointByID(cur_bp_id.GetBreakpointID());
+ ++delete_count;
+ }
+ }
+ }
+ result.AppendMessageWithFormat(
+ "%d breakpoints deleted; %d breakpoint locations disabled.\n",
+ delete_count, disable_count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ }
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectBreakpointName
+
+static constexpr OptionDefinition g_breakpoint_name_options[] = {
+ // clang-format off
+ {LLDB_OPT_SET_1, false, "name", 'N', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBreakpointName, "Specifies a breakpoint name to use."},
+ {LLDB_OPT_SET_2, false, "breakpoint-id", 'B', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBreakpointID, "Specify a breakpoint ID to use."},
+ {LLDB_OPT_SET_3, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Operate on Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets."},
+ {LLDB_OPT_SET_4, false, "help-string", 'H', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone, "A help string describing the purpose of this name."},
+ // clang-format on
+};
+class BreakpointNameOptionGroup : public OptionGroup {
+public:
+ BreakpointNameOptionGroup()
+ : OptionGroup(), m_breakpoint(LLDB_INVALID_BREAK_ID), m_use_dummy(false) {
+ }
+
+ ~BreakpointNameOptionGroup() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_name_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = g_breakpoint_name_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'N':
+ if (BreakpointID::StringIsBreakpointName(option_arg, error) &&
+ error.Success())
+ m_name.SetValueFromString(option_arg);
+ break;
+ case 'B':
+ if (m_breakpoint.SetValueFromString(option_arg).Fail())
+ error.SetErrorStringWithFormat(
+ "unrecognized value \"%s\" for breakpoint",
+ option_arg.str().c_str());
+ break;
+ case 'D':
+ if (m_use_dummy.SetValueFromString(option_arg).Fail())
+ error.SetErrorStringWithFormat(
+ "unrecognized value \"%s\" for use-dummy",
+ option_arg.str().c_str());
+ break;
+ case 'H':
+ m_help_string.SetValueFromString(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_name.Clear();
+ m_breakpoint.Clear();
+ m_use_dummy.Clear();
+ m_use_dummy.SetDefaultValue(false);
+ m_help_string.Clear();
+ }
+
+ OptionValueString m_name;
+ OptionValueUInt64 m_breakpoint;
+ OptionValueBoolean m_use_dummy;
+ OptionValueString m_help_string;
+};
+
+static constexpr OptionDefinition g_breakpoint_access_options[] = {
+ // clang-format off
+ {LLDB_OPT_SET_1, false, "allow-list", 'L', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Determines whether the breakpoint will show up in break list if not referred to explicitly."},
+ {LLDB_OPT_SET_2, false, "allow-disable", 'A', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Determines whether the breakpoint can be disabled by name or when all breakpoints are disabled."},
+ {LLDB_OPT_SET_3, false, "allow-delete", 'D', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Determines whether the breakpoint can be deleted by name or when all breakpoints are deleted."},
+ // clang-format on
+};
+
+class BreakpointAccessOptionGroup : public OptionGroup {
+public:
+ BreakpointAccessOptionGroup() : OptionGroup() {}
+
+ ~BreakpointAccessOptionGroup() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_access_options);
+ }
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option
+ = g_breakpoint_access_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'L': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_permissions.SetAllowList(value);
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -L option",
+ option_arg.str().c_str());
+ } break;
+ case 'A': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_permissions.SetAllowDisable(value);
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -L option",
+ option_arg.str().c_str());
+ } break;
+ case 'D': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_permissions.SetAllowDelete(value);
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -L option",
+ option_arg.str().c_str());
+ } break;
+
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ }
+
+ const BreakpointName::Permissions &GetPermissions() const
+ {
+ return m_permissions;
+ }
+ BreakpointName::Permissions m_permissions;
+};
+
+class CommandObjectBreakpointNameConfigure : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointNameConfigure(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "configure", "Configure the options for the breakpoint"
+ " name provided. "
+ "If you provide a breakpoint id, the options will be copied from "
+ "the breakpoint, otherwise only the options specified will be set "
+ "on the name.",
+ "breakpoint name configure <command-options> "
+ "<breakpoint-name-list>"),
+ m_bp_opts(), m_option_group() {
+ // Create the first variant for the first (and only) argument for this
+ // command.
+ CommandArgumentEntry arg1;
+ CommandArgumentData id_arg;
+ id_arg.arg_type = eArgTypeBreakpointName;
+ id_arg.arg_repetition = eArgRepeatOptional;
+ arg1.push_back(id_arg);
+ m_arguments.push_back(arg1);
+
+ m_option_group.Append(&m_bp_opts,
+ LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_access_options,
+ LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_ALL);
+ m_option_group.Append(&m_bp_id,
+ LLDB_OPT_SET_2|LLDB_OPT_SET_4,
+ LLDB_OPT_SET_ALL);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectBreakpointNameConfigure() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ result.AppendError("No names provided.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Target *target =
+ GetSelectedOrDummyTarget(false);
+
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ // Make a pass through first to see that all the names are legal.
+ for (auto &entry : command.entries()) {
+ Status error;
+ if (!BreakpointID::StringIsBreakpointName(entry.ref, error))
+ {
+ result.AppendErrorWithFormat("Invalid breakpoint name: %s - %s",
+ entry.c_str(), error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ // Now configure them, we already pre-checked the names so we don't need to
+ // check the error:
+ BreakpointSP bp_sp;
+ if (m_bp_id.m_breakpoint.OptionWasSet())
+ {
+ lldb::break_id_t bp_id = m_bp_id.m_breakpoint.GetUInt64Value();
+ bp_sp = target->GetBreakpointByID(bp_id);
+ if (!bp_sp)
+ {
+ result.AppendErrorWithFormatv("Could not find specified breakpoint {0}",
+ bp_id);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ Status error;
+ for (auto &entry : command.entries()) {
+ ConstString name(entry.c_str());
+ BreakpointName *bp_name = target->FindBreakpointName(name, true, error);
+ if (!bp_name)
+ continue;
+ if (m_bp_id.m_help_string.OptionWasSet())
+ bp_name->SetHelp(m_bp_id.m_help_string.GetStringValue().str().c_str());
+
+ if (bp_sp)
+ target->ConfigureBreakpointName(*bp_name,
+ *bp_sp->GetOptions(),
+ m_access_options.GetPermissions());
+ else
+ target->ConfigureBreakpointName(*bp_name,
+ m_bp_opts.GetBreakpointOptions(),
+ m_access_options.GetPermissions());
+ }
+ return true;
+ }
+
+private:
+ BreakpointNameOptionGroup m_bp_id; // Only using the id part of this.
+ BreakpointOptionGroup m_bp_opts;
+ BreakpointAccessOptionGroup m_access_options;
+ OptionGroupOptions m_option_group;
+};
+
+class CommandObjectBreakpointNameAdd : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointNameAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "add", "Add a name to the breakpoints provided.",
+ "breakpoint name add <command-options> <breakpoint-id-list>"),
+ m_name_options(), m_option_group() {
+ // Create the first variant for the first (and only) argument for this
+ // command.
+ CommandArgumentEntry arg1;
+ CommandArgumentData id_arg;
+ id_arg.arg_type = eArgTypeBreakpointID;
+ id_arg.arg_repetition = eArgRepeatOptional;
+ arg1.push_back(id_arg);
+ m_arguments.push_back(arg1);
+
+ m_option_group.Append(&m_name_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectBreakpointNameAdd() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (!m_name_options.m_name.OptionWasSet()) {
+ result.SetError("No name option provided.");
+ return false;
+ }
+
+ Target *target =
+ GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue());
+
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+
+ size_t num_breakpoints = breakpoints.GetSize();
+ if (num_breakpoints == 0) {
+ result.SetError("No breakpoints, cannot add names.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Particular breakpoint selected; disable that breakpoint.
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ if (result.Succeeded()) {
+ if (valid_bp_ids.GetSize() == 0) {
+ result.SetError("No breakpoints specified, cannot add names.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ size_t num_valid_ids = valid_bp_ids.GetSize();
+ const char *bp_name = m_name_options.m_name.GetCurrentValue();
+ Status error; // This error reports illegal names, but we've already
+ // checked that, so we don't need to check it again here.
+ for (size_t index = 0; index < num_valid_ids; index++) {
+ lldb::break_id_t bp_id =
+ valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID();
+ BreakpointSP bp_sp = breakpoints.FindBreakpointByID(bp_id);
+ target->AddNameToBreakpoint(bp_sp, bp_name, error);
+ }
+ }
+
+ return true;
+ }
+
+private:
+ BreakpointNameOptionGroup m_name_options;
+ OptionGroupOptions m_option_group;
+};
+
+class CommandObjectBreakpointNameDelete : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointNameDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "delete",
+ "Delete a name from the breakpoints provided.",
+ "breakpoint name delete <command-options> <breakpoint-id-list>"),
+ m_name_options(), m_option_group() {
+ // Create the first variant for the first (and only) argument for this
+ // command.
+ CommandArgumentEntry arg1;
+ CommandArgumentData id_arg;
+ id_arg.arg_type = eArgTypeBreakpointID;
+ id_arg.arg_repetition = eArgRepeatOptional;
+ arg1.push_back(id_arg);
+ m_arguments.push_back(arg1);
+
+ m_option_group.Append(&m_name_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectBreakpointNameDelete() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (!m_name_options.m_name.OptionWasSet()) {
+ result.SetError("No name option provided.");
+ return false;
+ }
+
+ Target *target =
+ GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue());
+
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+
+ size_t num_breakpoints = breakpoints.GetSize();
+ if (num_breakpoints == 0) {
+ result.SetError("No breakpoints, cannot delete names.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Particular breakpoint selected; disable that breakpoint.
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::deletePerm);
+
+ if (result.Succeeded()) {
+ if (valid_bp_ids.GetSize() == 0) {
+ result.SetError("No breakpoints specified, cannot delete names.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ ConstString bp_name(m_name_options.m_name.GetCurrentValue());
+ size_t num_valid_ids = valid_bp_ids.GetSize();
+ for (size_t index = 0; index < num_valid_ids; index++) {
+ lldb::break_id_t bp_id =
+ valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID();
+ BreakpointSP bp_sp = breakpoints.FindBreakpointByID(bp_id);
+ target->RemoveNameFromBreakpoint(bp_sp, bp_name);
+ }
+ }
+
+ return true;
+ }
+
+private:
+ BreakpointNameOptionGroup m_name_options;
+ OptionGroupOptions m_option_group;
+};
+
+class CommandObjectBreakpointNameList : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointNameList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "list",
+ "List either the names for a breakpoint or info "
+ "about a given name. With no arguments, lists all "
+ "names",
+ "breakpoint name list <command-options>"),
+ m_name_options(), m_option_group() {
+ m_option_group.Append(&m_name_options, LLDB_OPT_SET_3, LLDB_OPT_SET_ALL);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectBreakpointNameList() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target =
+ GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue());
+
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+
+ std::vector<std::string> name_list;
+ if (command.empty()) {
+ target->GetBreakpointNames(name_list);
+ } else {
+ for (const Args::ArgEntry &arg : command)
+ {
+ name_list.push_back(arg.c_str());
+ }
+ }
+
+ if (name_list.empty()) {
+ result.AppendMessage("No breakpoint names found.");
+ } else {
+ for (const std::string &name_str : name_list) {
+ const char *name = name_str.c_str();
+ // First print out the options for the name:
+ Status error;
+ BreakpointName *bp_name = target->FindBreakpointName(ConstString(name),
+ false,
+ error);
+ if (bp_name)
+ {
+ StreamString s;
+ result.AppendMessageWithFormat("Name: %s\n", name);
+ if (bp_name->GetDescription(&s, eDescriptionLevelFull))
+ {
+ result.AppendMessage(s.GetString());
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ BreakpointList &breakpoints = target->GetBreakpointList();
+ bool any_set = false;
+ for (BreakpointSP bp_sp : breakpoints.Breakpoints()) {
+ if (bp_sp->MatchesName(name)) {
+ StreamString s;
+ any_set = true;
+ bp_sp->GetDescription(&s, eDescriptionLevelBrief);
+ s.EOL();
+ result.AppendMessage(s.GetString());
+ }
+ }
+ if (!any_set)
+ result.AppendMessage("No breakpoints using this name.");
+ } else {
+ result.AppendMessageWithFormat("Name: %s not found.\n", name);
+ }
+ }
+ }
+ return true;
+ }
+
+private:
+ BreakpointNameOptionGroup m_name_options;
+ OptionGroupOptions m_option_group;
+};
+
+// CommandObjectBreakpointName
+class CommandObjectBreakpointName : public CommandObjectMultiword {
+public:
+ CommandObjectBreakpointName(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "name", "Commands to manage name tags for breakpoints",
+ "breakpoint name <subcommand> [<command-options>]") {
+ CommandObjectSP add_command_object(
+ new CommandObjectBreakpointNameAdd(interpreter));
+ CommandObjectSP delete_command_object(
+ new CommandObjectBreakpointNameDelete(interpreter));
+ CommandObjectSP list_command_object(
+ new CommandObjectBreakpointNameList(interpreter));
+ CommandObjectSP configure_command_object(
+ new CommandObjectBreakpointNameConfigure(interpreter));
+
+ LoadSubCommand("add", add_command_object);
+ LoadSubCommand("delete", delete_command_object);
+ LoadSubCommand("list", list_command_object);
+ LoadSubCommand("configure", configure_command_object);
+ }
+
+ ~CommandObjectBreakpointName() override = default;
+};
+
+// CommandObjectBreakpointRead
+#pragma mark Read::CommandOptions
+static constexpr OptionDefinition g_breakpoint_read_options[] = {
+ // clang-format off
+ {LLDB_OPT_SET_ALL, true, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eDiskFileCompletion, eArgTypeFilename, "The file from which to read the breakpoints." },
+ {LLDB_OPT_SET_ALL, false, "breakpoint-name", 'N', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBreakpointName, "Only read in breakpoints with this name."},
+ // clang-format on
+};
+
+#pragma mark Read
+
+class CommandObjectBreakpointRead : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointRead(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "breakpoint read",
+ "Read and set the breakpoints previously saved to "
+ "a file with \"breakpoint write\". ",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
+ eArgTypeBreakpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointRead() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_filename.assign(option_arg);
+ break;
+ case 'N': {
+ Status name_error;
+ if (!BreakpointID::StringIsBreakpointName(llvm::StringRef(option_arg),
+ name_error)) {
+ error.SetErrorStringWithFormat("Invalid breakpoint name: %s",
+ name_error.AsCString());
+ }
+ m_names.push_back(option_arg);
+ break;
+ }
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filename.clear();
+ m_names.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_read_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_filename;
+ std::vector<std::string> m_names;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget();
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ FileSpec input_spec(m_options.m_filename);
+ FileSystem::Instance().Resolve(input_spec);
+ BreakpointIDList new_bps;
+ Status error = target->CreateBreakpointsFromFile(
+ input_spec, m_options.m_names, new_bps);
+
+ if (!error.Success()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Stream &output_stream = result.GetOutputStream();
+
+ size_t num_breakpoints = new_bps.GetSize();
+ if (num_breakpoints == 0) {
+ result.AppendMessage("No breakpoints added.");
+ } else {
+ // No breakpoint selected; show info about all currently set breakpoints.
+ result.AppendMessage("New breakpoints:");
+ for (size_t i = 0; i < num_breakpoints; ++i) {
+ BreakpointID bp_id = new_bps.GetBreakpointIDAtIndex(i);
+ Breakpoint *bp = target->GetBreakpointList()
+ .FindBreakpointByID(bp_id.GetBreakpointID())
+ .get();
+ if (bp)
+ bp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
+ false);
+ }
+ }
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectBreakpointWrite
+#pragma mark Write::CommandOptions
+static constexpr OptionDefinition g_breakpoint_write_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, true, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eDiskFileCompletion, eArgTypeFilename, "The file into which to write the breakpoints." },
+ { LLDB_OPT_SET_ALL, false, "append",'a', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Append to saved breakpoints file if it exists."},
+ // clang-format on
+};
+
+#pragma mark Write
+class CommandObjectBreakpointWrite : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointWrite(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "breakpoint write",
+ "Write the breakpoints listed to a file that can "
+ "be read in with \"breakpoint read\". "
+ "If given no arguments, writes all breakpoints.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
+ eArgTypeBreakpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointWrite() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_filename.assign(option_arg);
+ break;
+ case 'a':
+ m_append = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filename.clear();
+ m_append = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_write_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_filename;
+ bool m_append = false;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget();
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or breakpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetBreakpointList().GetListMutex(lock);
+
+ BreakpointIDList valid_bp_ids;
+ if (!command.empty()) {
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ if (!result.Succeeded()) {
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ FileSpec file_spec(m_options.m_filename);
+ FileSystem::Instance().Resolve(file_spec);
+ Status error = target->SerializeBreakpointsToFile(file_spec, valid_bp_ids,
+ m_options.m_append);
+ if (!error.Success()) {
+ result.AppendErrorWithFormat("error serializing breakpoints: %s.",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectMultiwordBreakpoint
+#pragma mark MultiwordBreakpoint
+
+CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "breakpoint",
+ "Commands for operating on breakpoints (see 'help b' for shorthand.)",
+ "breakpoint <subcommand> [<command-options>]") {
+ CommandObjectSP list_command_object(
+ new CommandObjectBreakpointList(interpreter));
+ CommandObjectSP enable_command_object(
+ new CommandObjectBreakpointEnable(interpreter));
+ CommandObjectSP disable_command_object(
+ new CommandObjectBreakpointDisable(interpreter));
+ CommandObjectSP clear_command_object(
+ new CommandObjectBreakpointClear(interpreter));
+ CommandObjectSP delete_command_object(
+ new CommandObjectBreakpointDelete(interpreter));
+ CommandObjectSP set_command_object(
+ new CommandObjectBreakpointSet(interpreter));
+ CommandObjectSP command_command_object(
+ new CommandObjectBreakpointCommand(interpreter));
+ CommandObjectSP modify_command_object(
+ new CommandObjectBreakpointModify(interpreter));
+ CommandObjectSP name_command_object(
+ new CommandObjectBreakpointName(interpreter));
+ CommandObjectSP write_command_object(
+ new CommandObjectBreakpointWrite(interpreter));
+ CommandObjectSP read_command_object(
+ new CommandObjectBreakpointRead(interpreter));
+
+ list_command_object->SetCommandName("breakpoint list");
+ enable_command_object->SetCommandName("breakpoint enable");
+ disable_command_object->SetCommandName("breakpoint disable");
+ clear_command_object->SetCommandName("breakpoint clear");
+ delete_command_object->SetCommandName("breakpoint delete");
+ set_command_object->SetCommandName("breakpoint set");
+ command_command_object->SetCommandName("breakpoint command");
+ modify_command_object->SetCommandName("breakpoint modify");
+ name_command_object->SetCommandName("breakpoint name");
+ write_command_object->SetCommandName("breakpoint write");
+ read_command_object->SetCommandName("breakpoint read");
+
+ LoadSubCommand("list", list_command_object);
+ LoadSubCommand("enable", enable_command_object);
+ LoadSubCommand("disable", disable_command_object);
+ LoadSubCommand("clear", clear_command_object);
+ LoadSubCommand("delete", delete_command_object);
+ LoadSubCommand("set", set_command_object);
+ LoadSubCommand("command", command_command_object);
+ LoadSubCommand("modify", modify_command_object);
+ LoadSubCommand("name", name_command_object);
+ LoadSubCommand("write", write_command_object);
+ LoadSubCommand("read", read_command_object);
+}
+
+CommandObjectMultiwordBreakpoint::~CommandObjectMultiwordBreakpoint() = default;
+
+void CommandObjectMultiwordBreakpoint::VerifyIDs(Args &args, Target *target,
+ bool allow_locations,
+ CommandReturnObject &result,
+ BreakpointIDList *valid_ids,
+ BreakpointName::Permissions
+ ::PermissionKinds
+ purpose) {
+ // args can be strings representing 1). integers (for breakpoint ids)
+ // 2). the full breakpoint & location
+ // canonical representation
+ // 3). the word "to" or a hyphen,
+ // representing a range (in which case there
+ // had *better* be an entry both before &
+ // after of one of the first two types.
+ // 4). A breakpoint name
+ // If args is empty, we will use the last created breakpoint (if there is
+ // one.)
+
+ Args temp_args;
+
+ if (args.empty()) {
+ if (target->GetLastCreatedBreakpoint()) {
+ valid_ids->AddBreakpointID(BreakpointID(
+ target->GetLastCreatedBreakpoint()->GetID(), LLDB_INVALID_BREAK_ID));
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError(
+ "No breakpoint specified and no last created breakpoint.");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return;
+ }
+
+ // Create a new Args variable to use; copy any non-breakpoint-id-ranges stuff
+ // directly from the old ARGS to the new TEMP_ARGS. Do not copy breakpoint
+ // id range strings over; instead generate a list of strings for all the
+ // breakpoint ids in the range, and shove all of those breakpoint id strings
+ // into TEMP_ARGS.
+
+ BreakpointIDList::FindAndReplaceIDRanges(args, target, allow_locations,
+ purpose, result, temp_args);
+
+ // NOW, convert the list of breakpoint id strings in TEMP_ARGS into an actual
+ // BreakpointIDList:
+
+ valid_ids->InsertStringArray(temp_args.GetArgumentArrayRef(), result);
+
+ // At this point, all of the breakpoint ids that the user passed in have
+ // been converted to breakpoint IDs and put into valid_ids.
+
+ if (result.Succeeded()) {
+ // Now that we've converted everything from args into a list of breakpoint
+ // ids, go through our tentative list of breakpoint id's and verify that
+ // they correspond to valid/currently set breakpoints.
+
+ const size_t count = valid_ids->GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_ids->GetBreakpointIDAtIndex(i);
+ Breakpoint *breakpoint =
+ target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ if (breakpoint != nullptr) {
+ const size_t num_locations = breakpoint->GetNumLocations();
+ if (static_cast<size_t>(cur_bp_id.GetLocationID()) > num_locations) {
+ StreamString id_str;
+ BreakpointID::GetCanonicalReference(
+ &id_str, cur_bp_id.GetBreakpointID(), cur_bp_id.GetLocationID());
+ i = valid_ids->GetSize() + 1;
+ result.AppendErrorWithFormat(
+ "'%s' is not a currently valid breakpoint/location id.\n",
+ id_str.GetData());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ i = valid_ids->GetSize() + 1;
+ result.AppendErrorWithFormat(
+ "'%d' is not a currently valid breakpoint ID.\n",
+ cur_bp_id.GetBreakpointID());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.h
new file mode 100644
index 000000000000..cba1f3f774ee
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpoint.h
@@ -0,0 +1,60 @@
+//===-- CommandObjectBreakpoint.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectBreakpoint_h_
+#define liblldb_CommandObjectBreakpoint_h_
+
+
+#include <utility>
+#include <vector>
+
+#include "lldb/lldb-private.h"
+#include "lldb/Breakpoint/BreakpointName.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/STLUtils.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/Options.h"
+
+
+namespace lldb_private {
+
+// CommandObjectMultiwordBreakpoint
+
+class CommandObjectMultiwordBreakpoint : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordBreakpoint(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordBreakpoint() override;
+
+ static void VerifyBreakpointOrLocationIDs(Args &args, Target *target,
+ CommandReturnObject &result,
+ BreakpointIDList *valid_ids,
+ BreakpointName::Permissions
+ ::PermissionKinds purpose) {
+ VerifyIDs(args, target, true, result, valid_ids, purpose);
+ }
+
+ static void VerifyBreakpointIDs(Args &args, Target *target,
+ CommandReturnObject &result,
+ BreakpointIDList *valid_ids,
+ BreakpointName::Permissions::PermissionKinds
+ purpose) {
+ VerifyIDs(args, target, false, result, valid_ids, purpose);
+ }
+
+private:
+ static void VerifyIDs(Args &args, Target *target, bool allow_locations,
+ CommandReturnObject &result,
+ BreakpointIDList *valid_ids,
+ BreakpointName::Permissions::PermissionKinds
+ purpose);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectBreakpoint_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
new file mode 100644
index 000000000000..3f9d83cd86a8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
@@ -0,0 +1,748 @@
+//===-- CommandObjectBreakpointCommand.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectBreakpointCommand.h"
+#include "CommandObjectBreakpoint.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointIDList.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/State.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectBreakpointCommandAdd
+
+// FIXME: "script-type" needs to have its contents determined dynamically, so
+// somebody can add a new scripting
+// language to lldb and have it pickable here without having to change this
+// enumeration by hand and rebuild lldb proper.
+
+static constexpr OptionEnumValueElement g_script_option_enumeration[] = {
+ {eScriptLanguageNone, "command",
+ "Commands are in the lldb command interpreter language"},
+ {eScriptLanguagePython, "python", "Commands are in the Python language."},
+ {eSortOrderByName, "default-script",
+ "Commands are in the default scripting language."} };
+
+static constexpr OptionEnumValues ScriptOptionEnum() {
+ return OptionEnumValues(g_script_option_enumeration);
+}
+
+static constexpr OptionDefinition g_breakpoint_add_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "one-liner", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOneLiner, "Specify a one-line breakpoint command inline. Be sure to surround it with quotes." },
+ { LLDB_OPT_SET_ALL, false, "stop-on-error", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Specify whether breakpoint command execution should terminate on error." },
+ { LLDB_OPT_SET_ALL, false, "script-type", 's', OptionParser::eRequiredArgument, nullptr, ScriptOptionEnum(), 0, eArgTypeNone, "Specify the language for the commands - if none is specified, the lldb command interpreter will be used." },
+ { LLDB_OPT_SET_2, false, "python-function", 'F', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePythonFunction, "Give the name of a Python function to run as command for this breakpoint. Be sure to give a module name if appropriate." },
+ { LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Sets Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets." },
+ // clang-format on
+};
+
+class CommandObjectBreakpointCommandAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+public:
+ CommandObjectBreakpointCommandAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "add",
+ "Add LLDB commands to a breakpoint, to be executed "
+ "whenever the breakpoint is hit."
+ " If no breakpoint is specified, adds the "
+ "commands to the last created breakpoint.",
+ nullptr),
+ IOHandlerDelegateMultiline("DONE",
+ IOHandlerDelegate::Completion::LLDBCommand),
+ m_options() {
+ SetHelpLong(
+ R"(
+General information about entering breakpoint commands
+------------------------------------------------------
+
+)"
+ "This command will prompt for commands to be executed when the specified \
+breakpoint is hit. Each command is typed on its own line following the '> ' \
+prompt until 'DONE' is entered."
+ R"(
+
+)"
+ "Syntactic errors may not be detected when initially entered, and many \
+malformed commands can silently fail when executed. If your breakpoint commands \
+do not appear to be executing, double-check the command syntax."
+ R"(
+
+)"
+ "Note: You may enter any debugger command exactly as you would at the debugger \
+prompt. There is no limit to the number of commands supplied, but do NOT enter \
+more than one command per line."
+ R"(
+
+Special information about PYTHON breakpoint commands
+----------------------------------------------------
+
+)"
+ "You may enter either one or more lines of Python, including function \
+definitions or calls to functions that will have been imported by the time \
+the code executes. Single line breakpoint commands will be interpreted 'as is' \
+when the breakpoint is hit. Multiple lines of Python will be wrapped in a \
+generated function, and a call to the function will be attached to the breakpoint."
+ R"(
+
+This auto-generated function is passed in three arguments:
+
+ frame: an lldb.SBFrame object for the frame which hit breakpoint.
+
+ bp_loc: an lldb.SBBreakpointLocation object that represents the breakpoint location that was hit.
+
+ dict: the python session dictionary hit.
+
+)"
+ "When specifying a python function with the --python-function option, you need \
+to supply the function name prepended by the module name:"
+ R"(
+
+ --python-function myutils.breakpoint_callback
+
+The function itself must have the following prototype:
+
+def breakpoint_callback(frame, bp_loc, dict):
+ # Your code goes here
+
+)"
+ "The arguments are the same as the arguments passed to generated functions as \
+described above. Note that the global variable 'lldb.frame' will NOT be updated when \
+this function is called, so be sure to use the 'frame' argument. The 'frame' argument \
+can get you to the thread via frame.GetThread(), the thread can get you to the \
+process via thread.GetProcess(), and the process can get you back to the target \
+via process.GetTarget()."
+ R"(
+
+)"
+ "Important Note: As Python code gets collected into functions, access to global \
+variables requires explicit scoping using the 'global' keyword. Be sure to use correct \
+Python syntax, including indentation, when entering Python breakpoint commands."
+ R"(
+
+Example Python one-line breakpoint command:
+
+(lldb) breakpoint command add -s python 1
+Enter your Python command(s). Type 'DONE' to end.
+> print "Hit this breakpoint!"
+> DONE
+
+As a convenience, this also works for a short Python one-liner:
+
+(lldb) breakpoint command add -s python 1 -o 'import time; print time.asctime()'
+(lldb) run
+Launching '.../a.out' (x86_64)
+(lldb) Fri Sep 10 12:17:45 2010
+Process 21778 Stopped
+* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = breakpoint 1.1, queue = com.apple.main-thread
+ 36
+ 37 int c(int val)
+ 38 {
+ 39 -> return val + 3;
+ 40 }
+ 41
+ 42 int main (int argc, char const *argv[])
+
+Example multiple line Python breakpoint command:
+
+(lldb) breakpoint command add -s p 1
+Enter your Python command(s). Type 'DONE' to end.
+> global bp_count
+> bp_count = bp_count + 1
+> print "Hit this breakpoint " + repr(bp_count) + " times!"
+> DONE
+
+Example multiple line Python breakpoint command, using function definition:
+
+(lldb) breakpoint command add -s python 1
+Enter your Python command(s). Type 'DONE' to end.
+> def breakpoint_output (bp_no):
+> out_string = "Hit breakpoint number " + repr (bp_no)
+> print out_string
+> return True
+> breakpoint_output (1)
+> DONE
+
+)"
+ "In this case, since there is a reference to a global variable, \
+'bp_count', you will also need to make sure 'bp_count' exists and is \
+initialized:"
+ R"(
+
+(lldb) script
+>>> bp_count = 0
+>>> quit()
+
+)"
+ "Your Python code, however organized, can optionally return a value. \
+If the returned value is False, that tells LLDB not to stop at the breakpoint \
+to which the code is associated. Returning anything other than False, or even \
+returning None, or even omitting a return statement entirely, will cause \
+LLDB to stop."
+ R"(
+
+)"
+ "Final Note: A warning that no breakpoint command was generated when there \
+are no syntax errors may indicate that a function was declared but never called.");
+
+ CommandArgumentEntry arg;
+ CommandArgumentData bp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ bp_id_arg.arg_type = eArgTypeBreakpointID;
+ bp_id_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(bp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointCommandAdd() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ if (output_sp && interactive) {
+ output_sp->PutCString(g_reader_instructions);
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) override {
+ io_handler.SetIsDone(true);
+
+ std::vector<BreakpointOptions *> *bp_options_vec =
+ (std::vector<BreakpointOptions *> *)io_handler.GetUserData();
+ for (BreakpointOptions *bp_options : *bp_options_vec) {
+ if (!bp_options)
+ continue;
+
+ auto cmd_data = llvm::make_unique<BreakpointOptions::CommandData>();
+ cmd_data->user_source.SplitIntoLines(line.c_str(), line.size());
+ bp_options->SetCommandDataCallback(cmd_data);
+ }
+ }
+
+ void CollectDataForBreakpointCommandCallback(
+ std::vector<BreakpointOptions *> &bp_options_vec,
+ CommandReturnObject &result) {
+ m_interpreter.GetLLDBCommandsFromIOHandler(
+ "> ", // Prompt
+ *this, // IOHandlerDelegate
+ true, // Run IOHandler in async mode
+ &bp_options_vec); // Baton for the "io_handler" that will be passed back
+ // into our IOHandlerDelegate functions
+ }
+
+ /// Set a one-liner as the callback for the breakpoint.
+ void
+ SetBreakpointCommandCallback(std::vector<BreakpointOptions *> &bp_options_vec,
+ const char *oneliner) {
+ for (auto bp_options : bp_options_vec) {
+ auto cmd_data = llvm::make_unique<BreakpointOptions::CommandData>();
+
+ cmd_data->user_source.AppendString(oneliner);
+ cmd_data->stop_on_error = m_options.m_stop_on_error;
+
+ bp_options->SetCommandDataCallback(cmd_data);
+ }
+ }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_use_commands(false), m_use_script_language(false),
+ m_script_language(eScriptLanguageNone), m_use_one_liner(false),
+ m_one_liner(), m_function_name() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'o':
+ m_use_one_liner = true;
+ m_one_liner = option_arg;
+ break;
+
+ case 's':
+ m_script_language = (lldb::ScriptLanguage)OptionArgParser::ToOptionEnum(
+ option_arg, g_breakpoint_add_options[option_idx].enum_values,
+ eScriptLanguageNone, error);
+
+ if (m_script_language == eScriptLanguagePython ||
+ m_script_language == eScriptLanguageDefault) {
+ m_use_script_language = true;
+ } else {
+ m_use_script_language = false;
+ }
+ break;
+
+ case 'e': {
+ bool success = false;
+ m_stop_on_error =
+ OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid value for stop-on-error: \"%s\"",
+ option_arg.str().c_str());
+ } break;
+
+ case 'F':
+ m_use_one_liner = false;
+ m_use_script_language = true;
+ m_function_name.assign(option_arg);
+ break;
+
+ case 'D':
+ m_use_dummy = true;
+ break;
+
+ default:
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_use_commands = true;
+ m_use_script_language = false;
+ m_script_language = eScriptLanguageNone;
+
+ m_use_one_liner = false;
+ m_stop_on_error = true;
+ m_one_liner.clear();
+ m_function_name.clear();
+ m_use_dummy = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_use_commands;
+ bool m_use_script_language;
+ lldb::ScriptLanguage m_script_language;
+
+ // Instance variables to hold the values for one_liner options.
+ bool m_use_one_liner;
+ std::string m_one_liner;
+ bool m_stop_on_error;
+ std::string m_function_name;
+ bool m_use_dummy;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
+
+ if (target == nullptr) {
+ result.AppendError("There is not a current executable; there are no "
+ "breakpoints to which to add commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist to have commands added");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (!m_options.m_use_script_language &&
+ !m_options.m_function_name.empty()) {
+ result.AppendError("need to enable scripting to have a function run as a "
+ "breakpoint command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ m_bp_options_vec.clear();
+
+ if (result.Succeeded()) {
+ const size_t count = valid_bp_ids.GetSize();
+
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *bp =
+ target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ BreakpointOptions *bp_options = nullptr;
+ if (cur_bp_id.GetLocationID() == LLDB_INVALID_BREAK_ID) {
+ // This breakpoint does not have an associated location.
+ bp_options = bp->GetOptions();
+ } else {
+ BreakpointLocationSP bp_loc_sp(
+ bp->FindLocationByID(cur_bp_id.GetLocationID()));
+ // This breakpoint does have an associated location. Get its
+ // breakpoint options.
+ if (bp_loc_sp)
+ bp_options = bp_loc_sp->GetLocationOptions();
+ }
+ if (bp_options)
+ m_bp_options_vec.push_back(bp_options);
+ }
+ }
+
+ // If we are using script language, get the script interpreter in order
+ // to set or collect command callback. Otherwise, call the methods
+ // associated with this object.
+ if (m_options.m_use_script_language) {
+ ScriptInterpreter *script_interp = GetDebugger().GetScriptInterpreter();
+ // Special handling for one-liner specified inline.
+ if (m_options.m_use_one_liner) {
+ script_interp->SetBreakpointCommandCallback(
+ m_bp_options_vec, m_options.m_one_liner.c_str());
+ } else if (!m_options.m_function_name.empty()) {
+ script_interp->SetBreakpointCommandCallbackFunction(
+ m_bp_options_vec, m_options.m_function_name.c_str());
+ } else {
+ script_interp->CollectDataForBreakpointCommandCallback(
+ m_bp_options_vec, result);
+ }
+ } else {
+ // Special handling for one-liner specified inline.
+ if (m_options.m_use_one_liner)
+ SetBreakpointCommandCallback(m_bp_options_vec,
+ m_options.m_one_liner.c_str());
+ else
+ CollectDataForBreakpointCommandCallback(m_bp_options_vec, result);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+ std::vector<BreakpointOptions *> m_bp_options_vec; // This stores the
+ // breakpoint options that
+ // we are currently
+ // collecting commands for. In the CollectData... calls we need to hand this
+ // off to the IOHandler, which may run asynchronously. So we have to have
+ // some way to keep it alive, and not leak it. Making it an ivar of the
+ // command object, which never goes away achieves this. Note that if we were
+ // able to run the same command concurrently in one interpreter we'd have to
+ // make this "per invocation". But there are many more reasons why it is not
+ // in general safe to do that in lldb at present, so it isn't worthwhile to
+ // come up with a more complex mechanism to address this particular weakness
+ // right now.
+ static const char *g_reader_instructions;
+};
+
+const char *CommandObjectBreakpointCommandAdd::g_reader_instructions =
+ "Enter your debugger command(s). Type 'DONE' to end.\n";
+
+// CommandObjectBreakpointCommandDelete
+
+static constexpr OptionDefinition g_breakpoint_delete_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Delete commands from Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets." },
+ // clang-format on
+};
+
+class CommandObjectBreakpointCommandDelete : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointCommandDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "delete",
+ "Delete the set of commands from a breakpoint.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData bp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ bp_id_arg.arg_type = eArgTypeBreakpointID;
+ bp_id_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(bp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointCommandDelete() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_use_dummy(false) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'D':
+ m_use_dummy = true;
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_use_dummy = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_breakpoint_delete_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_use_dummy;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
+
+ if (target == nullptr) {
+ result.AppendError("There is not a current executable; there are no "
+ "breakpoints from which to delete commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist to have commands deleted");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.empty()) {
+ result.AppendError(
+ "No breakpoint specified from which to delete the commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ if (result.Succeeded()) {
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *bp =
+ target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ BreakpointLocationSP bp_loc_sp(
+ bp->FindLocationByID(cur_bp_id.GetLocationID()));
+ if (bp_loc_sp)
+ bp_loc_sp->ClearCallback();
+ else {
+ result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
+ cur_bp_id.GetBreakpointID(),
+ cur_bp_id.GetLocationID());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ bp->ClearCallback();
+ }
+ }
+ }
+ }
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectBreakpointCommandList
+
+class CommandObjectBreakpointCommandList : public CommandObjectParsed {
+public:
+ CommandObjectBreakpointCommandList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "list", "List the script or set of "
+ "commands to be executed when "
+ "the breakpoint is hit.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData bp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ bp_id_arg.arg_type = eArgTypeBreakpointID;
+ bp_id_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(bp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectBreakpointCommandList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+
+ if (target == nullptr) {
+ result.AppendError("There is not a current executable; there are no "
+ "breakpoints for which to list commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+ size_t num_breakpoints = breakpoints.GetSize();
+
+ if (num_breakpoints == 0) {
+ result.AppendError("No breakpoints exist for which to list commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.empty()) {
+ result.AppendError(
+ "No breakpoint specified for which to list the commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ BreakpointIDList valid_bp_ids;
+ CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
+ command, target, result, &valid_bp_ids,
+ BreakpointName::Permissions::PermissionKinds::listPerm);
+
+ if (result.Succeeded()) {
+ const size_t count = valid_bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
+ if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *bp =
+ target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
+
+ if (bp) {
+ BreakpointLocationSP bp_loc_sp;
+ if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
+ bp_loc_sp = bp->FindLocationByID(cur_bp_id.GetLocationID());
+ if (!bp_loc_sp)
+ {
+ result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
+ cur_bp_id.GetBreakpointID(),
+ cur_bp_id.GetLocationID());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ StreamString id_str;
+ BreakpointID::GetCanonicalReference(&id_str,
+ cur_bp_id.GetBreakpointID(),
+ cur_bp_id.GetLocationID());
+ const Baton *baton = nullptr;
+ if (bp_loc_sp)
+ baton = bp_loc_sp
+ ->GetOptionsSpecifyingKind(BreakpointOptions::eCallback)
+ ->GetBaton();
+ else
+ baton = bp->GetOptions()->GetBaton();
+
+ if (baton) {
+ result.GetOutputStream().Printf("Breakpoint %s:\n",
+ id_str.GetData());
+ result.GetOutputStream().IndentMore();
+ baton->GetDescription(&result.GetOutputStream(),
+ eDescriptionLevelFull);
+ result.GetOutputStream().IndentLess();
+ } else {
+ result.AppendMessageWithFormat(
+ "Breakpoint %s does not have an associated command.\n",
+ id_str.GetData());
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Invalid breakpoint ID: %u.\n",
+ cur_bp_id.GetBreakpointID());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectBreakpointCommand
+
+CommandObjectBreakpointCommand::CommandObjectBreakpointCommand(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "command", "Commands for adding, removing and listing "
+ "LLDB commands executed when a breakpoint is "
+ "hit.",
+ "command <sub-command> [<sub-command-options>] <breakpoint-id>") {
+ CommandObjectSP add_command_object(
+ new CommandObjectBreakpointCommandAdd(interpreter));
+ CommandObjectSP delete_command_object(
+ new CommandObjectBreakpointCommandDelete(interpreter));
+ CommandObjectSP list_command_object(
+ new CommandObjectBreakpointCommandList(interpreter));
+
+ add_command_object->SetCommandName("breakpoint command add");
+ delete_command_object->SetCommandName("breakpoint command delete");
+ list_command_object->SetCommandName("breakpoint command list");
+
+ LoadSubCommand("add", add_command_object);
+ LoadSubCommand("delete", delete_command_object);
+ LoadSubCommand("list", list_command_object);
+}
+
+CommandObjectBreakpointCommand::~CommandObjectBreakpointCommand() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.h
new file mode 100644
index 000000000000..b18e003368be
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBreakpointCommand.h
@@ -0,0 +1,33 @@
+//===-- CommandObjectBreakpointCommand.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectBreakpointCommand_h_
+#define liblldb_CommandObjectBreakpointCommand_h_
+
+
+
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordBreakpoint
+
+class CommandObjectBreakpointCommand : public CommandObjectMultiword {
+public:
+ CommandObjectBreakpointCommand(CommandInterpreter &interpreter);
+
+ ~CommandObjectBreakpointCommand() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectBreakpointCommand_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectBugreport.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectBugreport.cpp
new file mode 100644
index 000000000000..515cc9a113b1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBugreport.cpp
@@ -0,0 +1,124 @@
+//===-- CommandObjectBugreport.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectBugreport.h"
+
+#include <cstdio>
+
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionGroupOutputFile.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// "bugreport unwind"
+
+class CommandObjectBugreportUnwind : public CommandObjectParsed {
+public:
+ CommandObjectBugreportUnwind(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "bugreport unwind",
+ "Create a bugreport for a bug in the stack unwinding code.",
+ nullptr),
+ m_option_group(), m_outfile_options() {
+ m_option_group.Append(&m_outfile_options, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectBugreportUnwind() override {}
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ StringList commands;
+ commands.AppendString("thread backtrace");
+
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ if (thread) {
+ char command_buffer[256];
+
+ uint32_t frame_count = thread->GetStackFrameCount();
+ for (uint32_t i = 0; i < frame_count; ++i) {
+ StackFrameSP frame = thread->GetStackFrameAtIndex(i);
+ lldb::addr_t pc = frame->GetStackID().GetPC();
+
+ snprintf(command_buffer, sizeof(command_buffer),
+ "disassemble --bytes --address 0x%" PRIx64, pc);
+ commands.AppendString(command_buffer);
+
+ snprintf(command_buffer, sizeof(command_buffer),
+ "image show-unwind --address 0x%" PRIx64, pc);
+ commands.AppendString(command_buffer);
+ }
+ }
+
+ const FileSpec &outfile_spec =
+ m_outfile_options.GetFile().GetCurrentValue();
+ if (outfile_spec) {
+
+ uint32_t open_options =
+ File::eOpenOptionWrite | File::eOpenOptionCanCreate |
+ File::eOpenOptionAppend | File::eOpenOptionCloseOnExec;
+
+ const bool append = m_outfile_options.GetAppend().GetCurrentValue();
+ if (!append)
+ open_options |= File::eOpenOptionTruncate;
+
+ StreamFileSP outfile_stream = std::make_shared<StreamFile>();
+ File &file = outfile_stream->GetFile();
+ Status error =
+ FileSystem::Instance().Open(file, outfile_spec, open_options);
+ if (error.Fail()) {
+ auto path = outfile_spec.GetPath();
+ result.AppendErrorWithFormat("Failed to open file '%s' for %s: %s\n",
+ path.c_str(), append ? "append" : "write",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ result.SetImmediateOutputStream(outfile_stream);
+ }
+
+ CommandInterpreterRunOptions options;
+ options.SetStopOnError(false);
+ options.SetEchoCommands(true);
+ options.SetPrintResults(true);
+ options.SetPrintErrors(true);
+ options.SetAddToHistory(false);
+ m_interpreter.HandleCommands(commands, &m_exe_ctx, options, result);
+
+ return result.Succeeded();
+ }
+
+private:
+ OptionGroupOptions m_option_group;
+ OptionGroupOutputFile m_outfile_options;
+};
+
+#pragma mark CommandObjectMultiwordBugreport
+
+// CommandObjectMultiwordBugreport
+
+CommandObjectMultiwordBugreport::CommandObjectMultiwordBugreport(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "bugreport",
+ "Commands for creating domain-specific bug reports.",
+ "bugreport <subcommand> [<subcommand-options>]") {
+
+ LoadSubCommand(
+ "unwind", CommandObjectSP(new CommandObjectBugreportUnwind(interpreter)));
+}
+
+CommandObjectMultiwordBugreport::~CommandObjectMultiwordBugreport() {}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectBugreport.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectBugreport.h
new file mode 100644
index 000000000000..24ce6d237d56
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectBugreport.h
@@ -0,0 +1,27 @@
+//===-- CommandObjectBugreport.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectBugreport_h_
+#define liblldb_CommandObjectBugreport_h_
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordBugreport
+
+class CommandObjectMultiwordBugreport : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordBugreport(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordBugreport() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectBugreport_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp
new file mode 100644
index 000000000000..4092e76be6ac
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp
@@ -0,0 +1,1896 @@
+//===-- CommandObjectCommands.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+
+#include "CommandObjectCommands.h"
+#include "CommandObjectHelp.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandHistory.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObjectRegexCommand.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/StringList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectCommandsSource
+
+static constexpr OptionDefinition g_history_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "How many history commands to print." },
+ { LLDB_OPT_SET_1, false, "start-index", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "Index at which to start printing history commands (or end to mean tail mode)." },
+ { LLDB_OPT_SET_1, false, "end-index", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "Index at which to stop printing history commands." },
+ { LLDB_OPT_SET_2, false, "clear", 'C', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeBoolean, "Clears the current command history." },
+ // clang-format on
+};
+
+class CommandObjectCommandsHistory : public CommandObjectParsed {
+public:
+ CommandObjectCommandsHistory(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command history",
+ "Dump the history of commands in this session.\n"
+ "Commands in the history list can be run again "
+ "using \"!<INDEX>\". \"!-<OFFSET>\" will re-run "
+ "the command that is <OFFSET> commands from the end"
+ " of the list (counting the current command).",
+ nullptr),
+ m_options() {}
+
+ ~CommandObjectCommandsHistory() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_start_idx(0), m_stop_idx(0), m_count(0), m_clear(false) {
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c':
+ error = m_count.SetValueFromString(option_arg, eVarSetOperationAssign);
+ break;
+ case 's':
+ if (option_arg == "end") {
+ m_start_idx.SetCurrentValue(UINT64_MAX);
+ m_start_idx.SetOptionWasSet();
+ } else
+ error = m_start_idx.SetValueFromString(option_arg,
+ eVarSetOperationAssign);
+ break;
+ case 'e':
+ error =
+ m_stop_idx.SetValueFromString(option_arg, eVarSetOperationAssign);
+ break;
+ case 'C':
+ m_clear.SetCurrentValue(true);
+ m_clear.SetOptionWasSet();
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_start_idx.Clear();
+ m_stop_idx.Clear();
+ m_count.Clear();
+ m_clear.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_history_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ OptionValueUInt64 m_start_idx;
+ OptionValueUInt64 m_stop_idx;
+ OptionValueUInt64 m_count;
+ OptionValueBoolean m_clear;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (m_options.m_clear.GetCurrentValue() &&
+ m_options.m_clear.OptionWasSet()) {
+ m_interpreter.GetCommandHistory().Clear();
+ result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
+ } else {
+ if (m_options.m_start_idx.OptionWasSet() &&
+ m_options.m_stop_idx.OptionWasSet() &&
+ m_options.m_count.OptionWasSet()) {
+ result.AppendError("--count, --start-index and --end-index cannot be "
+ "all specified in the same invocation");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ } else {
+ std::pair<bool, uint64_t> start_idx(
+ m_options.m_start_idx.OptionWasSet(),
+ m_options.m_start_idx.GetCurrentValue());
+ std::pair<bool, uint64_t> stop_idx(
+ m_options.m_stop_idx.OptionWasSet(),
+ m_options.m_stop_idx.GetCurrentValue());
+ std::pair<bool, uint64_t> count(m_options.m_count.OptionWasSet(),
+ m_options.m_count.GetCurrentValue());
+
+ const CommandHistory &history(m_interpreter.GetCommandHistory());
+
+ if (start_idx.first && start_idx.second == UINT64_MAX) {
+ if (count.first) {
+ start_idx.second = history.GetSize() - count.second;
+ stop_idx.second = history.GetSize() - 1;
+ } else if (stop_idx.first) {
+ start_idx.second = stop_idx.second;
+ stop_idx.second = history.GetSize() - 1;
+ } else {
+ start_idx.second = 0;
+ stop_idx.second = history.GetSize() - 1;
+ }
+ } else {
+ if (!start_idx.first && !stop_idx.first && !count.first) {
+ start_idx.second = 0;
+ stop_idx.second = history.GetSize() - 1;
+ } else if (start_idx.first) {
+ if (count.first) {
+ stop_idx.second = start_idx.second + count.second - 1;
+ } else if (!stop_idx.first) {
+ stop_idx.second = history.GetSize() - 1;
+ }
+ } else if (stop_idx.first) {
+ if (count.first) {
+ if (stop_idx.second >= count.second)
+ start_idx.second = stop_idx.second - count.second + 1;
+ else
+ start_idx.second = 0;
+ }
+ } else /* if (count.first) */
+ {
+ start_idx.second = 0;
+ stop_idx.second = count.second - 1;
+ }
+ }
+ history.Dump(result.GetOutputStream(), start_idx.second,
+ stop_idx.second);
+ }
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectCommandsSource
+
+static constexpr OptionDefinition g_source_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "stop-on-error", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "If true, stop executing commands on error." },
+ { LLDB_OPT_SET_ALL, false, "stop-on-continue", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "If true, stop executing commands on continue." },
+ { LLDB_OPT_SET_ALL, false, "silent-run", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "If true don't echo commands while executing." },
+ // clang-format on
+};
+
+class CommandObjectCommandsSource : public CommandObjectParsed {
+public:
+ CommandObjectCommandsSource(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "command source",
+ "Read and execute LLDB commands from the file <filename>.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData file_arg;
+
+ // Define the first (and only) variant of this arg.
+ file_arg.arg_type = eArgTypeFilename;
+ file_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(file_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectCommandsSource() override = default;
+
+ const char *GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ return "";
+ }
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_stop_on_error(true), m_silent_run(false),
+ m_stop_on_continue(true) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'e':
+ error = m_stop_on_error.SetValueFromString(option_arg);
+ break;
+
+ case 'c':
+ error = m_stop_on_continue.SetValueFromString(option_arg);
+ break;
+
+ case 's':
+ error = m_silent_run.SetValueFromString(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_stop_on_error.Clear();
+ m_silent_run.Clear();
+ m_stop_on_continue.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_source_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ OptionValueBoolean m_stop_on_error;
+ OptionValueBoolean m_silent_run;
+ OptionValueBoolean m_stop_on_continue;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one executable filename argument.\n",
+ GetCommandName().str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ FileSpec cmd_file(command[0].ref);
+ FileSystem::Instance().Resolve(cmd_file);
+ ExecutionContext *exe_ctx = nullptr; // Just use the default context.
+
+ // If any options were set, then use them
+ if (m_options.m_stop_on_error.OptionWasSet() ||
+ m_options.m_silent_run.OptionWasSet() ||
+ m_options.m_stop_on_continue.OptionWasSet()) {
+ // Use user set settings
+ CommandInterpreterRunOptions options;
+
+ if (m_options.m_stop_on_continue.OptionWasSet())
+ options.SetStopOnContinue(
+ m_options.m_stop_on_continue.GetCurrentValue());
+
+ if (m_options.m_stop_on_error.OptionWasSet())
+ options.SetStopOnError(m_options.m_stop_on_error.GetCurrentValue());
+
+ // Individual silent setting is override for global command echo settings.
+ if (m_options.m_silent_run.GetCurrentValue()) {
+ options.SetSilent(true);
+ } else {
+ options.SetPrintResults(true);
+ options.SetPrintErrors(true);
+ options.SetEchoCommands(m_interpreter.GetEchoCommands());
+ options.SetEchoCommentCommands(m_interpreter.GetEchoCommentCommands());
+ }
+
+ m_interpreter.HandleCommandsFromFile(cmd_file, exe_ctx, options, result);
+ } else {
+ // No options were set, inherit any settings from nested "command source"
+ // commands, or set to sane default settings...
+ CommandInterpreterRunOptions options;
+ m_interpreter.HandleCommandsFromFile(cmd_file, exe_ctx, options, result);
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectCommandsAlias
+// CommandObjectCommandsAlias
+
+static constexpr OptionDefinition g_alias_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "help", 'h', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeHelpText, "Help text for this command" },
+ { LLDB_OPT_SET_ALL, false, "long-help", 'H', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeHelpText, "Long help text for this command" },
+ // clang-format on
+};
+
+static const char *g_python_command_instructions =
+ "Enter your Python command(s). Type 'DONE' to end.\n"
+ "You must define a Python function with this signature:\n"
+ "def my_command_impl(debugger, args, result, internal_dict):\n";
+
+class CommandObjectCommandsAlias : public CommandObjectRaw {
+protected:
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions() : OptionGroup(), m_help(), m_long_help() {}
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_alias_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = GetDefinitions()[option_idx].short_option;
+ std::string option_str(option_value);
+
+ switch (short_option) {
+ case 'h':
+ m_help.SetCurrentValue(option_str);
+ m_help.SetOptionWasSet();
+ break;
+
+ case 'H':
+ m_long_help.SetCurrentValue(option_str);
+ m_long_help.SetOptionWasSet();
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_help.Clear();
+ m_long_help.Clear();
+ }
+
+ OptionValueString m_help;
+ OptionValueString m_long_help;
+ };
+
+ OptionGroupOptions m_option_group;
+ CommandOptions m_command_options;
+
+public:
+ Options *GetOptions() override { return &m_option_group; }
+
+ CommandObjectCommandsAlias(CommandInterpreter &interpreter)
+ : CommandObjectRaw(
+ interpreter, "command alias",
+ "Define a custom command in terms of an existing command."),
+ m_option_group(), m_command_options() {
+ m_option_group.Append(&m_command_options);
+ m_option_group.Finalize();
+
+ SetHelpLong(
+ "'alias' allows the user to create a short-cut or abbreviation for long \
+commands, multi-word commands, and commands that take particular options. \
+Below are some simple examples of how one might use the 'alias' command:"
+ R"(
+
+(lldb) command alias sc script
+
+ Creates the abbreviation 'sc' for the 'script' command.
+
+(lldb) command alias bp breakpoint
+
+)"
+ " Creates the abbreviation 'bp' for the 'breakpoint' command. Since \
+breakpoint commands are two-word commands, the user would still need to \
+enter the second word after 'bp', e.g. 'bp enable' or 'bp delete'."
+ R"(
+
+(lldb) command alias bpl breakpoint list
+
+ Creates the abbreviation 'bpl' for the two-word command 'breakpoint list'.
+
+)"
+ "An alias can include some options for the command, with the values either \
+filled in at the time the alias is created, or specified as positional \
+arguments, to be filled in when the alias is invoked. The following example \
+shows how to create aliases with options:"
+ R"(
+
+(lldb) command alias bfl breakpoint set -f %1 -l %2
+
+)"
+ " Creates the abbreviation 'bfl' (for break-file-line), with the -f and -l \
+options already part of the alias. So if the user wants to set a breakpoint \
+by file and line without explicitly having to use the -f and -l options, the \
+user can now use 'bfl' instead. The '%1' and '%2' are positional placeholders \
+for the actual arguments that will be passed when the alias command is used. \
+The number in the placeholder refers to the position/order the actual value \
+occupies when the alias is used. All the occurrences of '%1' in the alias \
+will be replaced with the first argument, all the occurrences of '%2' in the \
+alias will be replaced with the second argument, and so on. This also allows \
+actual arguments to be used multiple times within an alias (see 'process \
+launch' example below)."
+ R"(
+
+)"
+ "Note: the positional arguments must substitute as whole words in the resultant \
+command, so you can't at present do something like this to append the file extension \
+\".cpp\":"
+ R"(
+
+(lldb) command alias bcppfl breakpoint set -f %1.cpp -l %2
+
+)"
+ "For more complex aliasing, use the \"command regex\" command instead. In the \
+'bfl' case above, the actual file value will be filled in with the first argument \
+following 'bfl' and the actual line number value will be filled in with the second \
+argument. The user would use this alias as follows:"
+ R"(
+
+(lldb) command alias bfl breakpoint set -f %1 -l %2
+(lldb) bfl my-file.c 137
+
+This would be the same as if the user had entered 'breakpoint set -f my-file.c -l 137'.
+
+Another example:
+
+(lldb) command alias pltty process launch -s -o %1 -e %1
+(lldb) pltty /dev/tty0
+
+ Interpreted as 'process launch -s -o /dev/tty0 -e /dev/tty0'
+
+)"
+ "If the user always wanted to pass the same value to a particular option, the \
+alias could be defined with that value directly in the alias as a constant, \
+rather than using a positional placeholder:"
+ R"(
+
+(lldb) command alias bl3 breakpoint set -f %1 -l 3
+
+ Always sets a breakpoint on line 3 of whatever file is indicated.)");
+
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentEntry arg3;
+ CommandArgumentData alias_arg;
+ CommandArgumentData cmd_arg;
+ CommandArgumentData options_arg;
+
+ // Define the first (and only) variant of this arg.
+ alias_arg.arg_type = eArgTypeAliasName;
+ alias_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(alias_arg);
+
+ // Define the first (and only) variant of this arg.
+ cmd_arg.arg_type = eArgTypeCommandName;
+ cmd_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(cmd_arg);
+
+ // Define the first (and only) variant of this arg.
+ options_arg.arg_type = eArgTypeAliasOptions;
+ options_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg3.push_back(options_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ m_arguments.push_back(arg3);
+ }
+
+ ~CommandObjectCommandsAlias() override = default;
+
+protected:
+ bool DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ if (raw_command_line.empty()) {
+ result.AppendError("'command alias' requires at least two arguments");
+ return false;
+ }
+
+ ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext();
+ m_option_group.NotifyOptionParsingStarting(&exe_ctx);
+
+ OptionsWithRaw args_with_suffix(raw_command_line);
+ const char *remainder = args_with_suffix.GetRawPart().c_str();
+
+ if (args_with_suffix.HasArgs())
+ if (!ParseOptionsAndNotify(args_with_suffix.GetArgs(), result,
+ m_option_group, exe_ctx))
+ return false;
+
+ llvm::StringRef raw_command_string(remainder);
+ Args args(raw_command_string);
+
+ if (args.GetArgumentCount() < 2) {
+ result.AppendError("'command alias' requires at least two arguments");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Get the alias command.
+
+ auto alias_command = args[0].ref;
+ if (alias_command.startswith("-")) {
+ result.AppendError("aliases starting with a dash are not supported");
+ if (alias_command == "--help" || alias_command == "--long-help") {
+ result.AppendWarning("if trying to pass options to 'command alias' add "
+ "a -- at the end of the options");
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Strip the new alias name off 'raw_command_string' (leave it on args,
+ // which gets passed to 'Execute', which does the stripping itself.
+ size_t pos = raw_command_string.find(alias_command);
+ if (pos == 0) {
+ raw_command_string = raw_command_string.substr(alias_command.size());
+ pos = raw_command_string.find_first_not_of(' ');
+ if ((pos != std::string::npos) && (pos > 0))
+ raw_command_string = raw_command_string.substr(pos);
+ } else {
+ result.AppendError("Error parsing command string. No alias created.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Verify that the command is alias-able.
+ if (m_interpreter.CommandExists(alias_command)) {
+ result.AppendErrorWithFormat(
+ "'%s' is a permanent debugger command and cannot be redefined.\n",
+ args[0].c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Get CommandObject that is being aliased. The command name is read from
+ // the front of raw_command_string. raw_command_string is returned with the
+ // name of the command object stripped off the front.
+ llvm::StringRef original_raw_command_string = raw_command_string;
+ CommandObject *cmd_obj =
+ m_interpreter.GetCommandObjectForCommand(raw_command_string);
+
+ if (!cmd_obj) {
+ result.AppendErrorWithFormat("invalid command given to 'command alias'. "
+ "'%s' does not begin with a valid command."
+ " No alias created.",
+ original_raw_command_string.str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (!cmd_obj->WantsRawCommandString()) {
+ // Note that args was initialized with the original command, and has not
+ // been updated to this point. Therefore can we pass it to the version of
+ // Execute that does not need/expect raw input in the alias.
+ return HandleAliasingNormalCommand(args, result);
+ } else {
+ return HandleAliasingRawCommand(alias_command, raw_command_string,
+ *cmd_obj, result);
+ }
+ return result.Succeeded();
+ }
+
+ bool HandleAliasingRawCommand(llvm::StringRef alias_command,
+ llvm::StringRef raw_command_string,
+ CommandObject &cmd_obj,
+ CommandReturnObject &result) {
+ // Verify & handle any options/arguments passed to the alias command
+
+ OptionArgVectorSP option_arg_vector_sp =
+ OptionArgVectorSP(new OptionArgVector);
+
+ if (CommandObjectSP cmd_obj_sp =
+ m_interpreter.GetCommandSPExact(cmd_obj.GetCommandName(), false)) {
+ if (m_interpreter.AliasExists(alias_command) ||
+ m_interpreter.UserCommandExists(alias_command)) {
+ result.AppendWarningWithFormat(
+ "Overwriting existing definition for '%s'.\n",
+ alias_command.str().c_str());
+ }
+ if (CommandAlias *alias = m_interpreter.AddAlias(
+ alias_command, cmd_obj_sp, raw_command_string)) {
+ if (m_command_options.m_help.OptionWasSet())
+ alias->SetHelp(m_command_options.m_help.GetCurrentValue());
+ if (m_command_options.m_long_help.OptionWasSet())
+ alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("Unable to create requested alias.\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ } else {
+ result.AppendError("Unable to create requested alias.\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+
+ bool HandleAliasingNormalCommand(Args &args, CommandReturnObject &result) {
+ size_t argc = args.GetArgumentCount();
+
+ if (argc < 2) {
+ result.AppendError("'command alias' requires at least two arguments");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Save these in std::strings since we're going to shift them off.
+ const std::string alias_command(args[0].ref);
+ const std::string actual_command(args[1].ref);
+
+ args.Shift(); // Shift the alias command word off the argument vector.
+ args.Shift(); // Shift the old command word off the argument vector.
+
+ // Verify that the command is alias'able, and get the appropriate command
+ // object.
+
+ if (m_interpreter.CommandExists(alias_command)) {
+ result.AppendErrorWithFormat(
+ "'%s' is a permanent debugger command and cannot be redefined.\n",
+ alias_command.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ CommandObjectSP command_obj_sp(
+ m_interpreter.GetCommandSPExact(actual_command, true));
+ CommandObjectSP subcommand_obj_sp;
+ bool use_subcommand = false;
+ if (!command_obj_sp) {
+ result.AppendErrorWithFormat("'%s' is not an existing command.\n",
+ actual_command.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ CommandObject *cmd_obj = command_obj_sp.get();
+ CommandObject *sub_cmd_obj = nullptr;
+ OptionArgVectorSP option_arg_vector_sp =
+ OptionArgVectorSP(new OptionArgVector);
+
+ while (cmd_obj->IsMultiwordObject() && !args.empty()) {
+ auto sub_command = args[0].ref;
+ assert(!sub_command.empty());
+ subcommand_obj_sp = cmd_obj->GetSubcommandSP(sub_command);
+ if (!subcommand_obj_sp) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid sub-command of '%s'. "
+ "Unable to create alias.\n",
+ args[0].c_str(), actual_command.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ sub_cmd_obj = subcommand_obj_sp.get();
+ use_subcommand = true;
+ args.Shift(); // Shift the sub_command word off the argument vector.
+ cmd_obj = sub_cmd_obj;
+ }
+
+ // Verify & handle any options/arguments passed to the alias command
+
+ std::string args_string;
+
+ if (!args.empty()) {
+ CommandObjectSP tmp_sp =
+ m_interpreter.GetCommandSPExact(cmd_obj->GetCommandName(), false);
+ if (use_subcommand)
+ tmp_sp = m_interpreter.GetCommandSPExact(sub_cmd_obj->GetCommandName(),
+ false);
+
+ args.GetCommandString(args_string);
+ }
+
+ if (m_interpreter.AliasExists(alias_command) ||
+ m_interpreter.UserCommandExists(alias_command)) {
+ result.AppendWarningWithFormat(
+ "Overwriting existing definition for '%s'.\n", alias_command.c_str());
+ }
+
+ if (CommandAlias *alias = m_interpreter.AddAlias(
+ alias_command, use_subcommand ? subcommand_obj_sp : command_obj_sp,
+ args_string)) {
+ if (m_command_options.m_help.OptionWasSet())
+ alias->SetHelp(m_command_options.m_help.GetCurrentValue());
+ if (m_command_options.m_long_help.OptionWasSet())
+ alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("Unable to create requested alias.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectCommandsUnalias
+// CommandObjectCommandsUnalias
+
+class CommandObjectCommandsUnalias : public CommandObjectParsed {
+public:
+ CommandObjectCommandsUnalias(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "command unalias",
+ "Delete one or more custom commands defined by 'command alias'.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData alias_arg;
+
+ // Define the first (and only) variant of this arg.
+ alias_arg.arg_type = eArgTypeAliasName;
+ alias_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(alias_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectCommandsUnalias() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ CommandObject::CommandMap::iterator pos;
+ CommandObject *cmd_obj;
+
+ if (args.empty()) {
+ result.AppendError("must call 'unalias' with a valid alias");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ auto command_name = args[0].ref;
+ cmd_obj = m_interpreter.GetCommandObject(command_name);
+ if (!cmd_obj) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a known command.\nTry 'help' to see a "
+ "current list of commands.\n",
+ args[0].c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_interpreter.CommandExists(command_name)) {
+ if (cmd_obj->IsRemovable()) {
+ result.AppendErrorWithFormat(
+ "'%s' is not an alias, it is a debugger command which can be "
+ "removed using the 'command delete' command.\n",
+ args[0].c_str());
+ } else {
+ result.AppendErrorWithFormat(
+ "'%s' is a permanent debugger command and cannot be removed.\n",
+ args[0].c_str());
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (!m_interpreter.RemoveAlias(command_name)) {
+ if (m_interpreter.AliasExists(command_name))
+ result.AppendErrorWithFormat(
+ "Error occurred while attempting to unalias '%s'.\n",
+ args[0].c_str());
+ else
+ result.AppendErrorWithFormat("'%s' is not an existing alias.\n",
+ args[0].c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectCommandsDelete
+// CommandObjectCommandsDelete
+
+class CommandObjectCommandsDelete : public CommandObjectParsed {
+public:
+ CommandObjectCommandsDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "command delete",
+ "Delete one or more custom commands defined by 'command regex'.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData alias_arg;
+
+ // Define the first (and only) variant of this arg.
+ alias_arg.arg_type = eArgTypeCommandName;
+ alias_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(alias_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectCommandsDelete() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ CommandObject::CommandMap::iterator pos;
+
+ if (args.empty()) {
+ result.AppendErrorWithFormat("must call '%s' with one or more valid user "
+ "defined regular expression command names",
+ GetCommandName().str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ auto command_name = args[0].ref;
+ if (!m_interpreter.CommandExists(command_name)) {
+ StreamString error_msg_stream;
+ const bool generate_upropos = true;
+ const bool generate_type_lookup = false;
+ CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage(
+ &error_msg_stream, command_name, llvm::StringRef(), llvm::StringRef(),
+ generate_upropos, generate_type_lookup);
+ result.AppendError(error_msg_stream.GetString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (!m_interpreter.RemoveCommand(command_name)) {
+ result.AppendErrorWithFormat(
+ "'%s' is a permanent debugger command and cannot be removed.\n",
+ args[0].c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+};
+
+// CommandObjectCommandsAddRegex
+
+static constexpr OptionDefinition g_regex_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "help" , 'h', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone, "The help text to display for this command." },
+ { LLDB_OPT_SET_1, false, "syntax", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone, "A syntax string showing the typical usage syntax." },
+ // clang-format on
+};
+
+#pragma mark CommandObjectCommandsAddRegex
+
+class CommandObjectCommandsAddRegex : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+public:
+ CommandObjectCommandsAddRegex(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "command regex", "Define a custom command in terms of "
+ "existing commands by matching "
+ "regular expressions.",
+ "command regex <cmd-name> [s/<regex>/<subst>/ ...]"),
+ IOHandlerDelegateMultiline("",
+ IOHandlerDelegate::Completion::LLDBCommand),
+ m_options() {
+ SetHelpLong(
+ R"(
+)"
+ "This command allows the user to create powerful regular expression commands \
+with substitutions. The regular expressions and substitutions are specified \
+using the regular expression substitution format of:"
+ R"(
+
+ s/<regex>/<subst>/
+
+)"
+ "<regex> is a regular expression that can use parenthesis to capture regular \
+expression input and substitute the captured matches in the output using %1 \
+for the first match, %2 for the second, and so on."
+ R"(
+
+)"
+ "The regular expressions can all be specified on the command line if more than \
+one argument is provided. If just the command name is provided on the command \
+line, then the regular expressions and substitutions can be entered on separate \
+lines, followed by an empty line to terminate the command definition."
+ R"(
+
+EXAMPLES
+
+)"
+ "The following example will define a regular expression command named 'f' that \
+will call 'finish' if there are no arguments, or 'frame select <frame-idx>' if \
+a number follows 'f':"
+ R"(
+
+ (lldb) command regex f s/^$/finish/ 's/([0-9]+)/frame select %1/')");
+ }
+
+ ~CommandObjectCommandsAddRegex() override = default;
+
+protected:
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ if (output_sp && interactive) {
+ output_sp->PutCString("Enter one or more sed substitution commands in "
+ "the form: 's/<regex>/<subst>/'.\nTerminate the "
+ "substitution list with an empty line.\n");
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override {
+ io_handler.SetIsDone(true);
+ if (m_regex_cmd_up) {
+ StringList lines;
+ if (lines.SplitIntoLines(data)) {
+ const size_t num_lines = lines.GetSize();
+ bool check_only = false;
+ for (size_t i = 0; i < num_lines; ++i) {
+ llvm::StringRef bytes_strref(lines[i]);
+ Status error = AppendRegexSubstitution(bytes_strref, check_only);
+ if (error.Fail()) {
+ if (!GetDebugger().GetCommandInterpreter().GetBatchCommandMode()) {
+ StreamSP out_stream = GetDebugger().GetAsyncOutputStream();
+ out_stream->Printf("error: %s\n", error.AsCString());
+ }
+ }
+ }
+ }
+ if (m_regex_cmd_up->HasRegexEntries()) {
+ CommandObjectSP cmd_sp(m_regex_cmd_up.release());
+ m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true);
+ }
+ }
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ result.AppendError("usage: 'command regex <command-name> "
+ "[s/<regex1>/<subst1>/ s/<regex2>/<subst2>/ ...]'\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Status error;
+ auto name = command[0].ref;
+ m_regex_cmd_up = llvm::make_unique<CommandObjectRegexCommand>(
+ m_interpreter, name, m_options.GetHelp(), m_options.GetSyntax(), 10, 0,
+ true);
+
+ if (argc == 1) {
+ Debugger &debugger = GetDebugger();
+ bool color_prompt = debugger.GetUseColor();
+ const bool multiple_lines = true; // Get multiple lines
+ IOHandlerSP io_handler_sp(new IOHandlerEditline(
+ debugger, IOHandler::Type::Other,
+ "lldb-regex", // Name of input reader for history
+ llvm::StringRef("> "), // Prompt
+ llvm::StringRef(), // Continuation prompt
+ multiple_lines, color_prompt,
+ 0, // Don't show line numbers
+ *this, nullptr));
+
+ if (io_handler_sp) {
+ debugger.PushIOHandler(io_handler_sp);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ } else {
+ for (auto &entry : command.entries().drop_front()) {
+ bool check_only = false;
+ error = AppendRegexSubstitution(entry.ref, check_only);
+ if (error.Fail())
+ break;
+ }
+
+ if (error.Success()) {
+ AddRegexCommandToInterpreter();
+ }
+ }
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+
+ Status AppendRegexSubstitution(const llvm::StringRef &regex_sed,
+ bool check_only) {
+ Status error;
+
+ if (!m_regex_cmd_up) {
+ error.SetErrorStringWithFormat(
+ "invalid regular expression command object for: '%.*s'",
+ (int)regex_sed.size(), regex_sed.data());
+ return error;
+ }
+
+ size_t regex_sed_size = regex_sed.size();
+
+ if (regex_sed_size <= 1) {
+ error.SetErrorStringWithFormat(
+ "regular expression substitution string is too short: '%.*s'",
+ (int)regex_sed.size(), regex_sed.data());
+ return error;
+ }
+
+ if (regex_sed[0] != 's') {
+ error.SetErrorStringWithFormat("regular expression substitution string "
+ "doesn't start with 's': '%.*s'",
+ (int)regex_sed.size(), regex_sed.data());
+ return error;
+ }
+ const size_t first_separator_char_pos = 1;
+ // use the char that follows 's' as the regex separator character so we can
+ // have "s/<regex>/<subst>/" or "s|<regex>|<subst>|"
+ const char separator_char = regex_sed[first_separator_char_pos];
+ const size_t second_separator_char_pos =
+ regex_sed.find(separator_char, first_separator_char_pos + 1);
+
+ if (second_separator_char_pos == std::string::npos) {
+ error.SetErrorStringWithFormat(
+ "missing second '%c' separator char after '%.*s' in '%.*s'",
+ separator_char,
+ (int)(regex_sed.size() - first_separator_char_pos - 1),
+ regex_sed.data() + (first_separator_char_pos + 1),
+ (int)regex_sed.size(), regex_sed.data());
+ return error;
+ }
+
+ const size_t third_separator_char_pos =
+ regex_sed.find(separator_char, second_separator_char_pos + 1);
+
+ if (third_separator_char_pos == std::string::npos) {
+ error.SetErrorStringWithFormat(
+ "missing third '%c' separator char after '%.*s' in '%.*s'",
+ separator_char,
+ (int)(regex_sed.size() - second_separator_char_pos - 1),
+ regex_sed.data() + (second_separator_char_pos + 1),
+ (int)regex_sed.size(), regex_sed.data());
+ return error;
+ }
+
+ if (third_separator_char_pos != regex_sed_size - 1) {
+ // Make sure that everything that follows the last regex separator char
+ if (regex_sed.find_first_not_of("\t\n\v\f\r ",
+ third_separator_char_pos + 1) !=
+ std::string::npos) {
+ error.SetErrorStringWithFormat(
+ "extra data found after the '%.*s' regular expression substitution "
+ "string: '%.*s'",
+ (int)third_separator_char_pos + 1, regex_sed.data(),
+ (int)(regex_sed.size() - third_separator_char_pos - 1),
+ regex_sed.data() + (third_separator_char_pos + 1));
+ return error;
+ }
+ } else if (first_separator_char_pos + 1 == second_separator_char_pos) {
+ error.SetErrorStringWithFormat(
+ "<regex> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'",
+ separator_char, separator_char, separator_char, (int)regex_sed.size(),
+ regex_sed.data());
+ return error;
+ } else if (second_separator_char_pos + 1 == third_separator_char_pos) {
+ error.SetErrorStringWithFormat(
+ "<subst> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'",
+ separator_char, separator_char, separator_char, (int)regex_sed.size(),
+ regex_sed.data());
+ return error;
+ }
+
+ if (!check_only) {
+ std::string regex(regex_sed.substr(first_separator_char_pos + 1,
+ second_separator_char_pos -
+ first_separator_char_pos - 1));
+ std::string subst(regex_sed.substr(second_separator_char_pos + 1,
+ third_separator_char_pos -
+ second_separator_char_pos - 1));
+ m_regex_cmd_up->AddRegexCommand(regex.c_str(), subst.c_str());
+ }
+ return error;
+ }
+
+ void AddRegexCommandToInterpreter() {
+ if (m_regex_cmd_up) {
+ if (m_regex_cmd_up->HasRegexEntries()) {
+ CommandObjectSP cmd_sp(m_regex_cmd_up.release());
+ m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true);
+ }
+ }
+ }
+
+private:
+ std::unique_ptr<CommandObjectRegexCommand> m_regex_cmd_up;
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'h':
+ m_help.assign(option_arg);
+ break;
+ case 's':
+ m_syntax.assign(option_arg);
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_help.clear();
+ m_syntax.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_regex_options);
+ }
+
+ // TODO: Convert these functions to return StringRefs.
+ const char *GetHelp() {
+ return (m_help.empty() ? nullptr : m_help.c_str());
+ }
+
+ const char *GetSyntax() {
+ return (m_syntax.empty() ? nullptr : m_syntax.c_str());
+ }
+
+ protected:
+ // Instance variables to hold the values for command options.
+
+ std::string m_help;
+ std::string m_syntax;
+ };
+
+ Options *GetOptions() override { return &m_options; }
+
+ CommandOptions m_options;
+};
+
+class CommandObjectPythonFunction : public CommandObjectRaw {
+public:
+ CommandObjectPythonFunction(CommandInterpreter &interpreter, std::string name,
+ std::string funct, std::string help,
+ ScriptedCommandSynchronicity synch)
+ : CommandObjectRaw(interpreter, name),
+ m_function_name(funct), m_synchro(synch), m_fetched_help_long(false) {
+ if (!help.empty())
+ SetHelp(help);
+ else {
+ StreamString stream;
+ stream.Printf("For more information run 'help %s'", name.c_str());
+ SetHelp(stream.GetString());
+ }
+ }
+
+ ~CommandObjectPythonFunction() override = default;
+
+ bool IsRemovable() const override { return true; }
+
+ const std::string &GetFunctionName() { return m_function_name; }
+
+ ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
+
+ llvm::StringRef GetHelpLong() override {
+ if (m_fetched_help_long)
+ return CommandObjectRaw::GetHelpLong();
+
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return CommandObjectRaw::GetHelpLong();
+
+ std::string docstring;
+ m_fetched_help_long =
+ scripter->GetDocumentationForItem(m_function_name.c_str(), docstring);
+ if (!docstring.empty())
+ SetHelpLong(docstring);
+ return CommandObjectRaw::GetHelpLong();
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+
+ Status error;
+
+ result.SetStatus(eReturnStatusInvalid);
+
+ if (!scripter ||
+ !scripter->RunScriptBasedCommand(m_function_name.c_str(),
+ raw_command_line, m_synchro, result,
+ error, m_exe_ctx)) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ // Don't change the status if the command already set it...
+ if (result.GetStatus() == eReturnStatusInvalid) {
+ if (result.GetOutputData().empty())
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ std::string m_function_name;
+ ScriptedCommandSynchronicity m_synchro;
+ bool m_fetched_help_long;
+};
+
+class CommandObjectScriptingObject : public CommandObjectRaw {
+public:
+ CommandObjectScriptingObject(CommandInterpreter &interpreter,
+ std::string name,
+ StructuredData::GenericSP cmd_obj_sp,
+ ScriptedCommandSynchronicity synch)
+ : CommandObjectRaw(interpreter, name),
+ m_cmd_obj_sp(cmd_obj_sp), m_synchro(synch), m_fetched_help_short(false),
+ m_fetched_help_long(false) {
+ StreamString stream;
+ stream.Printf("For more information run 'help %s'", name.c_str());
+ SetHelp(stream.GetString());
+ if (ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter())
+ GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp));
+ }
+
+ ~CommandObjectScriptingObject() override = default;
+
+ bool IsRemovable() const override { return true; }
+
+ StructuredData::GenericSP GetImplementingObject() { return m_cmd_obj_sp; }
+
+ ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
+
+ llvm::StringRef GetHelp() override {
+ if (m_fetched_help_short)
+ return CommandObjectRaw::GetHelp();
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return CommandObjectRaw::GetHelp();
+ std::string docstring;
+ m_fetched_help_short =
+ scripter->GetShortHelpForCommandObject(m_cmd_obj_sp, docstring);
+ if (!docstring.empty())
+ SetHelp(docstring);
+
+ return CommandObjectRaw::GetHelp();
+ }
+
+ llvm::StringRef GetHelpLong() override {
+ if (m_fetched_help_long)
+ return CommandObjectRaw::GetHelpLong();
+
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return CommandObjectRaw::GetHelpLong();
+
+ std::string docstring;
+ m_fetched_help_long =
+ scripter->GetLongHelpForCommandObject(m_cmd_obj_sp, docstring);
+ if (!docstring.empty())
+ SetHelpLong(docstring);
+ return CommandObjectRaw::GetHelpLong();
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+
+ Status error;
+
+ result.SetStatus(eReturnStatusInvalid);
+
+ if (!scripter ||
+ !scripter->RunScriptBasedCommand(m_cmd_obj_sp, raw_command_line,
+ m_synchro, result, error, m_exe_ctx)) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ // Don't change the status if the command already set it...
+ if (result.GetStatus() == eReturnStatusInvalid) {
+ if (result.GetOutputData().empty())
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ StructuredData::GenericSP m_cmd_obj_sp;
+ ScriptedCommandSynchronicity m_synchro;
+ bool m_fetched_help_short : 1;
+ bool m_fetched_help_long : 1;
+};
+
+// CommandObjectCommandsScriptImport
+
+static constexpr OptionDefinition g_script_import_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "allow-reload", 'r', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Allow the script to be loaded even if it was already loaded before. This argument exists for backwards compatibility, but reloading is always allowed, whether you specify it or not." },
+ // clang-format on
+};
+
+class CommandObjectCommandsScriptImport : public CommandObjectParsed {
+public:
+ CommandObjectCommandsScriptImport(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command script import",
+ "Import a scripting module in LLDB.", nullptr),
+ m_options() {
+ CommandArgumentEntry arg1;
+ CommandArgumentData cmd_arg;
+
+ // Define the first (and only) variant of this arg.
+ cmd_arg.arg_type = eArgTypeFilename;
+ cmd_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(cmd_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectCommandsScriptImport() override = default;
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'r':
+ m_allow_reload = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_allow_reload = true;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_script_import_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_allow_reload;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) {
+ result.AppendError("only scripting language supported for module "
+ "importing is currently Python");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.empty()) {
+ result.AppendError("command script import needs one or more arguments");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ for (auto &entry : command.entries()) {
+ Status error;
+
+ const bool init_session = true;
+ // FIXME: this is necessary because CommandObject::CheckRequirements()
+ // assumes that commands won't ever be recursively invoked, but it's
+ // actually possible to craft a Python script that does other "command
+ // script imports" in __lldb_init_module the real fix is to have
+ // recursive commands possible with a CommandInvocation object separate
+ // from the CommandObject itself, so that recursive command invocations
+ // won't stomp on each other (wrt to execution contents, options, and
+ // more)
+ m_exe_ctx.Clear();
+ if (GetDebugger().GetScriptInterpreter()->LoadScriptingModule(
+ entry.c_str(), m_options.m_allow_reload, init_session, error)) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendErrorWithFormat("module importing failed: %s",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectCommandsScriptAdd
+static constexpr OptionEnumValueElement g_script_synchro_type[] = {
+ {eScriptedCommandSynchronicitySynchronous, "synchronous",
+ "Run synchronous"},
+ {eScriptedCommandSynchronicityAsynchronous, "asynchronous",
+ "Run asynchronous"},
+ {eScriptedCommandSynchronicityCurrentValue, "current",
+ "Do not alter current setting"} };
+
+static constexpr OptionEnumValues ScriptSynchroType() {
+ return OptionEnumValues(g_script_synchro_type);
+}
+
+static constexpr OptionDefinition g_script_add_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "function", 'f', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePythonFunction, "Name of the Python function to bind to this command name." },
+ { LLDB_OPT_SET_2, false, "class", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePythonClass, "Name of the Python class to bind to this command name." },
+ { LLDB_OPT_SET_1, false, "help" , 'h', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeHelpText, "The help text to display for this command." },
+ { LLDB_OPT_SET_ALL, false, "synchronicity", 's', OptionParser::eRequiredArgument, nullptr, ScriptSynchroType(), 0, eArgTypeScriptedCommandSynchronicity, "Set the synchronicity of this command's executions with regard to LLDB event system." },
+ // clang-format on
+};
+
+class CommandObjectCommandsScriptAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+public:
+ CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command script add",
+ "Add a scripted function as an LLDB command.",
+ nullptr),
+ IOHandlerDelegateMultiline("DONE"), m_options() {
+ CommandArgumentEntry arg1;
+ CommandArgumentData cmd_arg;
+
+ // Define the first (and only) variant of this arg.
+ cmd_arg.arg_type = eArgTypeCommandName;
+ cmd_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(cmd_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectCommandsScriptAdd() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_class_name(), m_funct_name(), m_short_help(),
+ m_synchronicity(eScriptedCommandSynchronicitySynchronous) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ if (!option_arg.empty())
+ m_funct_name = option_arg;
+ break;
+ case 'c':
+ if (!option_arg.empty())
+ m_class_name = option_arg;
+ break;
+ case 'h':
+ if (!option_arg.empty())
+ m_short_help = option_arg;
+ break;
+ case 's':
+ m_synchronicity =
+ (ScriptedCommandSynchronicity)OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
+ if (!error.Success())
+ error.SetErrorStringWithFormat(
+ "unrecognized value for synchronicity '%s'",
+ option_arg.str().c_str());
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_class_name.clear();
+ m_funct_name.clear();
+ m_short_help.clear();
+ m_synchronicity = eScriptedCommandSynchronicitySynchronous;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_script_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_class_name;
+ std::string m_funct_name;
+ std::string m_short_help;
+ ScriptedCommandSynchronicity m_synchronicity;
+ };
+
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ if (output_sp && interactive) {
+ output_sp->PutCString(g_python_command_instructions);
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override {
+ StreamFileSP error_sp = io_handler.GetErrorStreamFile();
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter) {
+
+ StringList lines;
+ lines.SplitIntoLines(data);
+ if (lines.GetSize() > 0) {
+ std::string funct_name_str;
+ if (interpreter->GenerateScriptAliasFunction(lines, funct_name_str)) {
+ if (funct_name_str.empty()) {
+ error_sp->Printf("error: unable to obtain a function name, didn't "
+ "add python command.\n");
+ error_sp->Flush();
+ } else {
+ // everything should be fine now, let's add this alias
+
+ CommandObjectSP command_obj_sp(new CommandObjectPythonFunction(
+ m_interpreter, m_cmd_name, funct_name_str, m_short_help,
+ m_synchronicity));
+
+ if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp,
+ true)) {
+ error_sp->Printf("error: unable to add selected command, didn't "
+ "add python command.\n");
+ error_sp->Flush();
+ }
+ }
+ } else {
+ error_sp->Printf(
+ "error: unable to create function, didn't add python command.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: empty function, didn't add python command.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf(
+ "error: script interpreter missing, didn't add python command.\n");
+ error_sp->Flush();
+ }
+
+ io_handler.SetIsDone(true);
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) {
+ result.AppendError("only scripting language supported for scripted "
+ "commands is currently Python");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() != 1) {
+ result.AppendError("'command script add' requires one argument");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Store the options in case we get multi-line input
+ m_cmd_name = command[0].ref;
+ m_short_help.assign(m_options.m_short_help);
+ m_synchronicity = m_options.m_synchronicity;
+
+ if (m_options.m_class_name.empty()) {
+ if (m_options.m_funct_name.empty()) {
+ m_interpreter.GetPythonCommandsFromIOHandler(
+ " ", // Prompt
+ *this, // IOHandlerDelegate
+ true, // Run IOHandler in async mode
+ nullptr); // Baton for the "io_handler" that will be passed back
+ // into our IOHandlerDelegate functions
+ } else {
+ CommandObjectSP new_cmd(new CommandObjectPythonFunction(
+ m_interpreter, m_cmd_name, m_options.m_funct_name,
+ m_options.m_short_help, m_synchronicity));
+ if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("cannot add command");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ } else {
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (!interpreter) {
+ result.AppendError("cannot find ScriptInterpreter");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ auto cmd_obj_sp = interpreter->CreateScriptCommandObject(
+ m_options.m_class_name.c_str());
+ if (!cmd_obj_sp) {
+ result.AppendError("cannot create helper object");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ CommandObjectSP new_cmd(new CommandObjectScriptingObject(
+ m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity));
+ if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("cannot add command");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+ std::string m_cmd_name;
+ std::string m_short_help;
+ ScriptedCommandSynchronicity m_synchronicity;
+};
+
+// CommandObjectCommandsScriptList
+
+class CommandObjectCommandsScriptList : public CommandObjectParsed {
+public:
+ CommandObjectCommandsScriptList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command script list",
+ "List defined scripted commands.", nullptr) {}
+
+ ~CommandObjectCommandsScriptList() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ m_interpreter.GetHelp(result, CommandInterpreter::eCommandTypesUserDef);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return true;
+ }
+};
+
+// CommandObjectCommandsScriptClear
+
+class CommandObjectCommandsScriptClear : public CommandObjectParsed {
+public:
+ CommandObjectCommandsScriptClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command script clear",
+ "Delete all scripted commands.", nullptr) {}
+
+ ~CommandObjectCommandsScriptClear() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ m_interpreter.RemoveAllUser();
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return true;
+ }
+};
+
+// CommandObjectCommandsScriptDelete
+
+class CommandObjectCommandsScriptDelete : public CommandObjectParsed {
+public:
+ CommandObjectCommandsScriptDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "command script delete",
+ "Delete a scripted command.", nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentData cmd_arg;
+
+ // Define the first (and only) variant of this arg.
+ cmd_arg.arg_type = eArgTypeCommandName;
+ cmd_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(cmd_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectCommandsScriptDelete() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+
+ if (command.GetArgumentCount() != 1) {
+ result.AppendError("'command script delete' requires one argument");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ auto cmd_name = command[0].ref;
+
+ if (cmd_name.empty() || !m_interpreter.HasUserCommands() ||
+ !m_interpreter.UserCommandExists(cmd_name)) {
+ result.AppendErrorWithFormat("command %s not found", command[0].c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ m_interpreter.RemoveUser(cmd_name);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+#pragma mark CommandObjectMultiwordCommandsScript
+
+// CommandObjectMultiwordCommandsScript
+
+class CommandObjectMultiwordCommandsScript : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordCommandsScript(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "command script", "Commands for managing custom "
+ "commands implemented by "
+ "interpreter scripts.",
+ "command script <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("add", CommandObjectSP(
+ new CommandObjectCommandsScriptAdd(interpreter)));
+ LoadSubCommand(
+ "delete",
+ CommandObjectSP(new CommandObjectCommandsScriptDelete(interpreter)));
+ LoadSubCommand(
+ "clear",
+ CommandObjectSP(new CommandObjectCommandsScriptClear(interpreter)));
+ LoadSubCommand("list", CommandObjectSP(new CommandObjectCommandsScriptList(
+ interpreter)));
+ LoadSubCommand(
+ "import",
+ CommandObjectSP(new CommandObjectCommandsScriptImport(interpreter)));
+ }
+
+ ~CommandObjectMultiwordCommandsScript() override = default;
+};
+
+#pragma mark CommandObjectMultiwordCommands
+
+// CommandObjectMultiwordCommands
+
+CommandObjectMultiwordCommands::CommandObjectMultiwordCommands(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "command",
+ "Commands for managing custom LLDB commands.",
+ "command <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("source",
+ CommandObjectSP(new CommandObjectCommandsSource(interpreter)));
+ LoadSubCommand("alias",
+ CommandObjectSP(new CommandObjectCommandsAlias(interpreter)));
+ LoadSubCommand("unalias", CommandObjectSP(
+ new CommandObjectCommandsUnalias(interpreter)));
+ LoadSubCommand("delete",
+ CommandObjectSP(new CommandObjectCommandsDelete(interpreter)));
+ LoadSubCommand(
+ "regex", CommandObjectSP(new CommandObjectCommandsAddRegex(interpreter)));
+ LoadSubCommand("history", CommandObjectSP(
+ new CommandObjectCommandsHistory(interpreter)));
+ LoadSubCommand(
+ "script",
+ CommandObjectSP(new CommandObjectMultiwordCommandsScript(interpreter)));
+}
+
+CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.h
new file mode 100644
index 000000000000..468ee53344f1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.h
@@ -0,0 +1,30 @@
+//===-- CommandObjectCommands.h -----------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectCommands_h_
+#define liblldb_CommandObjectCommands_h_
+
+#include "lldb/Core/STLUtils.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordCommands
+
+class CommandObjectMultiwordCommands : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordCommands(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordCommands() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectCommands_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.cpp
new file mode 100644
index 000000000000..5972555b2499
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.cpp
@@ -0,0 +1,554 @@
+//===-- CommandObjectDisassemble.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectDisassemble.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+
+#define DEFAULT_DISASM_BYTE_SIZE 32
+#define DEFAULT_DISASM_NUM_INS 4
+
+using namespace lldb;
+using namespace lldb_private;
+
+static constexpr OptionDefinition g_disassemble_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "bytes", 'b', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Show opcode bytes when disassembling." },
+ { LLDB_OPT_SET_ALL, false, "context", 'C', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNumLines, "Number of context lines of source to show." },
+ { LLDB_OPT_SET_ALL, false, "mixed", 'm', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Enable mixed source and assembly display." },
+ { LLDB_OPT_SET_ALL, false, "raw", 'r', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Print raw disassembly with no symbol information." },
+ { LLDB_OPT_SET_ALL, false, "plugin", 'P', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePlugin, "Name of the disassembler plugin you want to use." },
+ { LLDB_OPT_SET_ALL, false, "flavor", 'F', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeDisassemblyFlavor, "Name of the disassembly flavor you want to use. "
+ "Currently the only valid options are default, and for Intel "
+ "architectures, att and intel." },
+ { LLDB_OPT_SET_ALL, false, "arch", 'A', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeArchitecture, "Specify the architecture to use from cross disassembly." },
+ { LLDB_OPT_SET_1 |
+ LLDB_OPT_SET_2, true, "start-address", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Address at which to start disassembling." },
+ { LLDB_OPT_SET_1, false, "end-address", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Address at which to end disassembling." },
+ { LLDB_OPT_SET_2 |
+ LLDB_OPT_SET_3 |
+ LLDB_OPT_SET_4 |
+ LLDB_OPT_SET_5, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNumLines, "Number of instructions to display." },
+ { LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, "Disassemble entire contents of the given function name." },
+ { LLDB_OPT_SET_4, false, "frame", 'f', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Disassemble from the start of the current frame's function." },
+ { LLDB_OPT_SET_5, false, "pc", 'p', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Disassemble around the current pc." },
+ { LLDB_OPT_SET_6, false, "line", 'l', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Disassemble the current frame's current source line instructions if there is debug line "
+ "table information, else disassemble around the pc." },
+ { LLDB_OPT_SET_7, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Disassemble function containing this address." },
+ // clang-format on
+};
+
+CommandObjectDisassemble::CommandOptions::CommandOptions()
+ : Options(), num_lines_context(0), num_instructions(0), func_name(),
+ current_function(false), start_addr(), end_addr(), at_pc(false),
+ frame_line(false), plugin_name(), flavor_string(), arch(),
+ some_location_specified(false), symbol_containing_addr() {
+ OptionParsingStarting(nullptr);
+}
+
+CommandObjectDisassemble::CommandOptions::~CommandOptions() = default;
+
+Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'm':
+ show_mixed = true;
+ break;
+
+ case 'C':
+ if (option_arg.getAsInteger(0, num_lines_context))
+ error.SetErrorStringWithFormat("invalid num context lines string: \"%s\"",
+ option_arg.str().c_str());
+ break;
+
+ case 'c':
+ if (option_arg.getAsInteger(0, num_instructions))
+ error.SetErrorStringWithFormat(
+ "invalid num of instructions string: \"%s\"",
+ option_arg.str().c_str());
+ break;
+
+ case 'b':
+ show_bytes = true;
+ break;
+
+ case 's': {
+ start_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ if (start_addr != LLDB_INVALID_ADDRESS)
+ some_location_specified = true;
+ } break;
+ case 'e': {
+ end_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ if (end_addr != LLDB_INVALID_ADDRESS)
+ some_location_specified = true;
+ } break;
+
+ case 'n':
+ func_name.assign(option_arg);
+ some_location_specified = true;
+ break;
+
+ case 'p':
+ at_pc = true;
+ some_location_specified = true;
+ break;
+
+ case 'l':
+ frame_line = true;
+ // Disassemble the current source line kind of implies showing mixed source
+ // code context.
+ show_mixed = true;
+ some_location_specified = true;
+ break;
+
+ case 'P':
+ plugin_name.assign(option_arg);
+ break;
+
+ case 'F': {
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ if (target_sp && (target_sp->GetArchitecture().GetTriple().getArch() ==
+ llvm::Triple::x86 ||
+ target_sp->GetArchitecture().GetTriple().getArch() ==
+ llvm::Triple::x86_64)) {
+ flavor_string.assign(option_arg);
+ } else
+ error.SetErrorStringWithFormat("Disassembler flavors are currently only "
+ "supported for x86 and x86_64 targets.");
+ break;
+ }
+
+ case 'r':
+ raw = true;
+ break;
+
+ case 'f':
+ current_function = true;
+ some_location_specified = true;
+ break;
+
+ case 'A':
+ if (execution_context) {
+ const auto &target_sp = execution_context->GetTargetSP();
+ auto platform_ptr = target_sp ? target_sp->GetPlatform().get() : nullptr;
+ arch = Platform::GetAugmentedArchSpec(platform_ptr, option_arg);
+ }
+ break;
+
+ case 'a': {
+ symbol_containing_addr = OptionArgParser::ToAddress(
+ execution_context, option_arg, LLDB_INVALID_ADDRESS, &error);
+ if (symbol_containing_addr != LLDB_INVALID_ADDRESS) {
+ some_location_specified = true;
+ }
+ } break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+}
+
+void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ show_mixed = false;
+ show_bytes = false;
+ num_lines_context = 0;
+ num_instructions = 0;
+ func_name.clear();
+ current_function = false;
+ at_pc = false;
+ frame_line = false;
+ start_addr = LLDB_INVALID_ADDRESS;
+ end_addr = LLDB_INVALID_ADDRESS;
+ symbol_containing_addr = LLDB_INVALID_ADDRESS;
+ raw = false;
+ plugin_name.clear();
+
+ Target *target =
+ execution_context ? execution_context->GetTargetPtr() : nullptr;
+
+ // This is a hack till we get the ability to specify features based on
+ // architecture. For now GetDisassemblyFlavor is really only valid for x86
+ // (and for the llvm assembler plugin, but I'm papering over that since that
+ // is the only disassembler plugin we have...
+ if (target) {
+ if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 ||
+ target->GetArchitecture().GetTriple().getArch() ==
+ llvm::Triple::x86_64) {
+ flavor_string.assign(target->GetDisassemblyFlavor());
+ } else
+ flavor_string.assign("default");
+
+ } else
+ flavor_string.assign("default");
+
+ arch.Clear();
+ some_location_specified = false;
+}
+
+Status CommandObjectDisassemble::CommandOptions::OptionParsingFinished(
+ ExecutionContext *execution_context) {
+ if (!some_location_specified)
+ current_function = true;
+ return Status();
+}
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectDisassemble::CommandOptions::GetDefinitions() {
+ return llvm::makeArrayRef(g_disassemble_options);
+}
+
+// CommandObjectDisassemble
+
+CommandObjectDisassemble::CommandObjectDisassemble(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "disassemble",
+ "Disassemble specified instructions in the current target. "
+ "Defaults to the current function for the current thread and "
+ "stack frame.",
+ "disassemble [<cmd-options>]"),
+ m_options() {}
+
+CommandObjectDisassemble::~CommandObjectDisassemble() = default;
+
+bool CommandObjectDisassemble::DoExecute(Args &command,
+ CommandReturnObject &result) {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ if (!m_options.arch.IsValid())
+ m_options.arch = target->GetArchitecture();
+
+ if (!m_options.arch.IsValid()) {
+ result.AppendError(
+ "use the --arch option or set the target architecture to disassemble");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *plugin_name = m_options.GetPluginName();
+ const char *flavor_string = m_options.GetFlavorString();
+
+ DisassemblerSP disassembler =
+ Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name);
+
+ if (!disassembler) {
+ if (plugin_name) {
+ result.AppendErrorWithFormat(
+ "Unable to find Disassembler plug-in named '%s' that supports the "
+ "'%s' architecture.\n",
+ plugin_name, m_options.arch.GetArchitectureName());
+ } else
+ result.AppendErrorWithFormat(
+ "Unable to find Disassembler plug-in for the '%s' architecture.\n",
+ m_options.arch.GetArchitectureName());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (flavor_string != nullptr &&
+ !disassembler->FlavorValidForArchSpec(m_options.arch,
+ flavor_string))
+ result.AppendWarningWithFormat(
+ "invalid disassembler flavor \"%s\", using default.\n", flavor_string);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ if (!command.empty()) {
+ result.AppendErrorWithFormat(
+ "\"disassemble\" arguments are specified as options.\n");
+ const int terminal_width =
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth();
+ GetOptions()->GenerateOptionUsage(result.GetErrorStream(), this,
+ terminal_width);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_options.show_mixed && m_options.num_lines_context == 0)
+ m_options.num_lines_context = 2;
+
+ // Always show the PC in the disassembly
+ uint32_t options = Disassembler::eOptionMarkPCAddress;
+
+ // Mark the source line for the current PC only if we are doing mixed source
+ // and assembly
+ if (m_options.show_mixed)
+ options |= Disassembler::eOptionMarkPCSourceLine;
+
+ if (m_options.show_bytes)
+ options |= Disassembler::eOptionShowBytes;
+
+ if (m_options.raw)
+ options |= Disassembler::eOptionRawOuput;
+
+ if (!m_options.func_name.empty()) {
+ ConstString name(m_options.func_name.c_str());
+
+ if (Disassembler::Disassemble(
+ GetDebugger(), m_options.arch, plugin_name, flavor_string,
+ m_exe_ctx, name,
+ nullptr, // Module *
+ m_options.num_instructions, m_options.show_mixed,
+ m_options.show_mixed ? m_options.num_lines_context : 0, options,
+ result.GetOutputStream())) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Unable to find symbol with name '%s'.\n",
+ name.GetCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ std::vector<AddressRange> ranges;
+ AddressRange range;
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ if (m_options.frame_line) {
+ if (frame == nullptr) {
+ result.AppendError("Cannot disassemble around the current line without "
+ "a selected frame.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ LineEntry pc_line_entry(
+ frame->GetSymbolContext(eSymbolContextLineEntry).line_entry);
+ if (pc_line_entry.IsValid()) {
+ range = pc_line_entry.range;
+ } else {
+ m_options.at_pc =
+ true; // No line entry, so just disassemble around the current pc
+ m_options.show_mixed = false;
+ }
+ } else if (m_options.current_function) {
+ if (frame == nullptr) {
+ result.AppendError("Cannot disassemble around the current function "
+ "without a selected frame.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ Symbol *symbol = frame->GetSymbolContext(eSymbolContextSymbol).symbol;
+ if (symbol) {
+ range.GetBaseAddress() = symbol->GetAddress();
+ range.SetByteSize(symbol->GetByteSize());
+ }
+ }
+
+ // Did the "m_options.frame_line" find a valid range already? If so skip
+ // the rest...
+ if (range.GetByteSize() == 0) {
+ if (m_options.at_pc) {
+ if (frame == nullptr) {
+ result.AppendError("Cannot disassemble around the current PC without "
+ "a selected frame.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ range.GetBaseAddress() = frame->GetFrameCodeAddress();
+ if (m_options.num_instructions == 0) {
+ // Disassembling at the PC always disassembles some number of
+ // instructions (not the whole function).
+ m_options.num_instructions = DEFAULT_DISASM_NUM_INS;
+ }
+ ranges.push_back(range);
+ } else {
+ range.GetBaseAddress().SetOffset(m_options.start_addr);
+ if (range.GetBaseAddress().IsValid()) {
+ if (m_options.end_addr != LLDB_INVALID_ADDRESS) {
+ if (m_options.end_addr <= m_options.start_addr) {
+ result.AppendErrorWithFormat(
+ "End address before start address.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ range.SetByteSize(m_options.end_addr - m_options.start_addr);
+ }
+ ranges.push_back(range);
+ } else {
+ if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS &&
+ target) {
+ if (!target->GetSectionLoadList().IsEmpty()) {
+ bool failed = false;
+ Address symbol_containing_address;
+ if (target->GetSectionLoadList().ResolveLoadAddress(
+ m_options.symbol_containing_addr,
+ symbol_containing_address)) {
+ ModuleSP module_sp(symbol_containing_address.GetModule());
+ SymbolContext sc;
+ bool resolve_tail_call_address = true; // PC can be one past the
+ // address range of the
+ // function.
+ module_sp->ResolveSymbolContextForAddress(
+ symbol_containing_address, eSymbolContextEverything, sc,
+ resolve_tail_call_address);
+ if (sc.function || sc.symbol) {
+ sc.GetAddressRange(eSymbolContextFunction |
+ eSymbolContextSymbol,
+ 0, false, range);
+ } else {
+ failed = true;
+ }
+ } else {
+ failed = true;
+ }
+ if (failed) {
+ result.AppendErrorWithFormat(
+ "Could not find function bounds for address 0x%" PRIx64
+ "\n",
+ m_options.symbol_containing_addr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ ranges.push_back(range);
+ } else {
+ for (lldb::ModuleSP module_sp : target->GetImages().Modules()) {
+ lldb::addr_t file_addr = m_options.symbol_containing_addr;
+ Address file_address;
+ if (module_sp->ResolveFileAddress(file_addr, file_address)) {
+ SymbolContext sc;
+ bool resolve_tail_call_address = true; // PC can be one past
+ // the address range of
+ // the function.
+ module_sp->ResolveSymbolContextForAddress(
+ file_address, eSymbolContextEverything, sc,
+ resolve_tail_call_address);
+ if (sc.function || sc.symbol) {
+ sc.GetAddressRange(eSymbolContextFunction |
+ eSymbolContextSymbol,
+ 0, false, range);
+ ranges.push_back(range);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else
+ ranges.push_back(range);
+
+ if (m_options.num_instructions != 0) {
+ if (ranges.empty()) {
+ // The default action is to disassemble the current frame function.
+ if (frame) {
+ SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
+ eSymbolContextSymbol));
+ if (sc.function)
+ range.GetBaseAddress() =
+ sc.function->GetAddressRange().GetBaseAddress();
+ else if (sc.symbol && sc.symbol->ValueIsAddress())
+ range.GetBaseAddress() = sc.symbol->GetAddress();
+ else
+ range.GetBaseAddress() = frame->GetFrameCodeAddress();
+ }
+
+ if (!range.GetBaseAddress().IsValid()) {
+ result.AppendError("invalid frame");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ bool print_sc_header = ranges.size() > 1;
+ for (AddressRange cur_range : ranges) {
+ if (Disassembler::Disassemble(
+ GetDebugger(), m_options.arch, plugin_name, flavor_string,
+ m_exe_ctx, cur_range.GetBaseAddress(),
+ m_options.num_instructions, m_options.show_mixed,
+ m_options.show_mixed ? m_options.num_lines_context : 0, options,
+ result.GetOutputStream())) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ if (m_options.start_addr != LLDB_INVALID_ADDRESS)
+ result.AppendErrorWithFormat(
+ "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
+ m_options.start_addr);
+ else if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS)
+ result.AppendErrorWithFormat(
+ "Failed to disassemble memory in function at 0x%8.8" PRIx64
+ ".\n",
+ m_options.symbol_containing_addr);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ if (print_sc_header)
+ result.AppendMessage("\n");
+ } else {
+ if (ranges.empty()) {
+ // The default action is to disassemble the current frame function.
+ if (frame) {
+ SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
+ eSymbolContextSymbol));
+ if (sc.function)
+ range = sc.function->GetAddressRange();
+ else if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ range.GetBaseAddress() = sc.symbol->GetAddress();
+ range.SetByteSize(sc.symbol->GetByteSize());
+ } else
+ range.GetBaseAddress() = frame->GetFrameCodeAddress();
+ } else {
+ result.AppendError("invalid frame");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ ranges.push_back(range);
+ }
+
+ bool print_sc_header = ranges.size() > 1;
+ for (AddressRange cur_range : ranges) {
+ if (cur_range.GetByteSize() == 0)
+ cur_range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE);
+
+ if (Disassembler::Disassemble(
+ GetDebugger(), m_options.arch, plugin_name, flavor_string,
+ m_exe_ctx, cur_range, m_options.num_instructions,
+ m_options.show_mixed,
+ m_options.show_mixed ? m_options.num_lines_context : 0, options,
+ result.GetOutputStream())) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
+ m_options.start_addr);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ if (print_sc_header)
+ result.AppendMessage("\n");
+ }
+ }
+ }
+
+ return result.Succeeded();
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.h
new file mode 100644
index 000000000000..70193e914c7f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectDisassemble.h
@@ -0,0 +1,81 @@
+//===-- CommandObjectDisassemble.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectDisassemble_h_
+#define liblldb_CommandObjectDisassemble_h_
+
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Utility/ArchSpec.h"
+
+namespace lldb_private {
+
+// CommandObjectDisassemble
+
+class CommandObjectDisassemble : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions();
+
+ ~CommandOptions() override;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
+
+ const char *GetPluginName() {
+ return (plugin_name.empty() ? nullptr : plugin_name.c_str());
+ }
+
+ const char *GetFlavorString() {
+ if (flavor_string.empty() || flavor_string == "default")
+ return nullptr;
+ return flavor_string.c_str();
+ }
+
+ Status OptionParsingFinished(ExecutionContext *execution_context) override;
+
+ bool show_mixed; // Show mixed source/assembly
+ bool show_bytes;
+ uint32_t num_lines_context;
+ uint32_t num_instructions;
+ bool raw;
+ std::string func_name;
+ bool current_function;
+ lldb::addr_t start_addr;
+ lldb::addr_t end_addr;
+ bool at_pc;
+ bool frame_line;
+ std::string plugin_name;
+ std::string flavor_string;
+ ArchSpec arch;
+ bool some_location_specified; // If no location was specified, we'll select
+ // "at_pc". This should be set
+ // in SetOptionValue if anything the selects a location is set.
+ lldb::addr_t symbol_containing_addr;
+ };
+
+ CommandObjectDisassemble(CommandInterpreter &interpreter);
+
+ ~CommandObjectDisassemble() override;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+
+ CommandOptions m_options;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectDisassemble_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp
new file mode 100644
index 000000000000..29e4ab695522
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.cpp
@@ -0,0 +1,705 @@
+//===-- CommandObjectExpression.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+
+#include "CommandObjectExpression.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/DataFormatters/ValueObjectPrinter.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Expression/REPL.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+CommandObjectExpression::CommandOptions::CommandOptions() : OptionGroup() {}
+
+CommandObjectExpression::CommandOptions::~CommandOptions() = default;
+
+static constexpr OptionEnumValueElement g_description_verbosity_type[] = {
+ {eLanguageRuntimeDescriptionDisplayVerbosityCompact, "compact",
+ "Only show the description string"},
+ {eLanguageRuntimeDescriptionDisplayVerbosityFull, "full",
+ "Show the full output, including persistent variable's name and type"} };
+
+static constexpr OptionEnumValues DescriptionVerbosityTypes() {
+ return OptionEnumValues(g_description_verbosity_type);
+}
+
+static constexpr OptionDefinition g_expression_options[] = {
+ // clang-format off
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "all-threads", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Should we run all threads if the execution doesn't complete on one thread."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "ignore-breakpoints", 'i', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Ignore breakpoint hits while running expressions"},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "timeout", 't', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "Timeout value (in microseconds) for running the expression."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "unwind-on-error", 'u', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Clean up program state if the expression causes a crash, or raises a signal. "
+ "Note, unlike gdb hitting a breakpoint is controlled by another option (-i)."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "debug", 'g', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "When specified, debug the JIT code by setting a breakpoint on the first instruction "
+ "and forcing breakpoints to not be ignored (-i0) and no unwinding to happen on error (-u0)."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "language", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLanguage, "Specifies the Language to use when parsing the expression. If not set the target.language "
+ "setting is used." },
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "apply-fixits", 'X', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLanguage, "If true, simple fix-it hints will be automatically applied to the expression." },
+ {LLDB_OPT_SET_1, false, "description-verbosity", 'v', OptionParser::eOptionalArgument, nullptr, DescriptionVerbosityTypes(), 0, eArgTypeDescriptionVerbosity, "How verbose should the output of this expression be, if the object description is asked for."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "top-level", 'p', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Interpret the expression as a complete translation unit, without injecting it into the local "
+ "context. Allows declaration of persistent, top-level entities without a $ prefix."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "allow-jit", 'j', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Controls whether the expression can fall back to being JITted if it's not supported by "
+ "the interpreter (defaults to true)."}
+ // clang-format on
+};
+
+Status CommandObjectExpression::CommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+
+ const int short_option = GetDefinitions()[option_idx].short_option;
+
+ switch (short_option) {
+ case 'l':
+ language = Language::GetLanguageTypeFromString(option_arg);
+ if (language == eLanguageTypeUnknown)
+ error.SetErrorStringWithFormat(
+ "unknown language type: '%s' for expression",
+ option_arg.str().c_str());
+ break;
+
+ case 'a': {
+ bool success;
+ bool result;
+ result = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid all-threads value setting: \"%s\"",
+ option_arg.str().c_str());
+ else
+ try_all_threads = result;
+ } break;
+
+ case 'i': {
+ bool success;
+ bool tmp_value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ ignore_breakpoints = tmp_value;
+ else
+ error.SetErrorStringWithFormat(
+ "could not convert \"%s\" to a boolean value.",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 'j': {
+ bool success;
+ bool tmp_value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ allow_jit = tmp_value;
+ else
+ error.SetErrorStringWithFormat(
+ "could not convert \"%s\" to a boolean value.",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 't':
+ if (option_arg.getAsInteger(0, timeout)) {
+ timeout = 0;
+ error.SetErrorStringWithFormat("invalid timeout setting \"%s\"",
+ option_arg.str().c_str());
+ }
+ break;
+
+ case 'u': {
+ bool success;
+ bool tmp_value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ unwind_on_error = tmp_value;
+ else
+ error.SetErrorStringWithFormat(
+ "could not convert \"%s\" to a boolean value.",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 'v':
+ if (option_arg.empty()) {
+ m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityFull;
+ break;
+ }
+ m_verbosity = (LanguageRuntimeDescriptionDisplayVerbosity)
+ OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values, 0, error);
+ if (!error.Success())
+ error.SetErrorStringWithFormat(
+ "unrecognized value for description-verbosity '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'g':
+ debug = true;
+ unwind_on_error = false;
+ ignore_breakpoints = false;
+ break;
+
+ case 'p':
+ top_level = true;
+ break;
+
+ case 'X': {
+ bool success;
+ bool tmp_value = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ auto_apply_fixits = tmp_value ? eLazyBoolYes : eLazyBoolNo;
+ else
+ error.SetErrorStringWithFormat(
+ "could not convert \"%s\" to a boolean value.",
+ option_arg.str().c_str());
+ break;
+ }
+
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+}
+
+void CommandObjectExpression::CommandOptions::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ auto process_sp =
+ execution_context ? execution_context->GetProcessSP() : ProcessSP();
+ if (process_sp) {
+ ignore_breakpoints = process_sp->GetIgnoreBreakpointsInExpressions();
+ unwind_on_error = process_sp->GetUnwindOnErrorInExpressions();
+ } else {
+ ignore_breakpoints = true;
+ unwind_on_error = true;
+ }
+
+ show_summary = true;
+ try_all_threads = true;
+ timeout = 0;
+ debug = false;
+ language = eLanguageTypeUnknown;
+ m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityCompact;
+ auto_apply_fixits = eLazyBoolCalculate;
+ top_level = false;
+ allow_jit = true;
+}
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectExpression::CommandOptions::GetDefinitions() {
+ return llvm::makeArrayRef(g_expression_options);
+}
+
+CommandObjectExpression::CommandObjectExpression(
+ CommandInterpreter &interpreter)
+ : CommandObjectRaw(
+ interpreter, "expression", "Evaluate an expression on the current "
+ "thread. Displays any returned value "
+ "with LLDB's default formatting.",
+ "", eCommandProcessMustBePaused | eCommandTryTargetAPILock),
+ IOHandlerDelegate(IOHandlerDelegate::Completion::Expression),
+ m_option_group(), m_format_options(eFormatDefault),
+ m_repl_option(LLDB_OPT_SET_1, false, "repl", 'r', "Drop into REPL", false,
+ true),
+ m_command_options(), m_expr_line_count(0), m_expr_lines() {
+ SetHelpLong(
+ R"(
+Single and multi-line expressions:
+
+)"
+ " The expression provided on the command line must be a complete expression \
+with no newlines. To evaluate a multi-line expression, \
+hit a return after an empty expression, and lldb will enter the multi-line expression editor. \
+Hit return on an empty line to end the multi-line expression."
+
+ R"(
+
+Timeouts:
+
+)"
+ " If the expression can be evaluated statically (without running code) then it will be. \
+Otherwise, by default the expression will run on the current thread with a short timeout: \
+currently .25 seconds. If it doesn't return in that time, the evaluation will be interrupted \
+and resumed with all threads running. You can use the -a option to disable retrying on all \
+threads. You can use the -t option to set a shorter timeout."
+ R"(
+
+User defined variables:
+
+)"
+ " You can define your own variables for convenience or to be used in subsequent expressions. \
+You define them the same way you would define variables in C. If the first character of \
+your user defined variable is a $, then the variable's value will be available in future \
+expressions, otherwise it will just be available in the current expression."
+ R"(
+
+Continuing evaluation after a breakpoint:
+
+)"
+ " If the \"-i false\" option is used, and execution is interrupted by a breakpoint hit, once \
+you are done with your investigation, you can either remove the expression execution frames \
+from the stack with \"thread return -x\" or if you are still interested in the expression result \
+you can issue the \"continue\" command and the expression evaluation will complete and the \
+expression result will be available using the \"thread.completed-expression\" key in the thread \
+format."
+
+ R"(
+
+Examples:
+
+ expr my_struct->a = my_array[3]
+ expr -f bin -- (index * 8) + 5
+ expr unsigned int $foo = 5
+ expr char c[] = \"foo\"; c[0])");
+
+ CommandArgumentEntry arg;
+ CommandArgumentData expression_arg;
+
+ // Define the first (and only) variant of this arg.
+ expression_arg.arg_type = eArgTypeExpression;
+ expression_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the argument
+ // entry.
+ arg.push_back(expression_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ // Add the "--format" and "--gdb-format"
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_FORMAT |
+ OptionGroupFormat::OPTION_GROUP_GDB_FMT,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_command_options);
+ m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
+ m_option_group.Append(&m_repl_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3);
+ m_option_group.Finalize();
+}
+
+CommandObjectExpression::~CommandObjectExpression() = default;
+
+Options *CommandObjectExpression::GetOptions() { return &m_option_group; }
+
+int CommandObjectExpression::HandleCompletion(CompletionRequest &request) {
+ EvaluateExpressionOptions options;
+ options.SetCoerceToId(m_varobj_options.use_objc);
+ options.SetLanguage(m_command_options.language);
+ options.SetExecutionPolicy(lldb_private::eExecutionPolicyNever);
+ options.SetAutoApplyFixIts(false);
+ options.SetGenerateDebugInfo(false);
+
+ // We need a valid execution context with a frame pointer for this
+ // completion, so if we don't have one we should try to make a valid
+ // execution context.
+ if (m_interpreter.GetExecutionContext().GetFramePtr() == nullptr)
+ m_interpreter.UpdateExecutionContext(nullptr);
+
+ // This didn't work, so let's get out before we start doing things that
+ // expect a valid frame pointer.
+ if (m_interpreter.GetExecutionContext().GetFramePtr() == nullptr)
+ return 0;
+
+ ExecutionContext exe_ctx(m_interpreter.GetExecutionContext());
+
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target)
+ target = GetDummyTarget();
+
+ if (!target)
+ return 0;
+
+ unsigned cursor_pos = request.GetRawCursorPos();
+ llvm::StringRef code = request.GetRawLine();
+
+ const std::size_t original_code_size = code.size();
+
+ // Remove the first token which is 'expr' or some alias/abbreviation of that.
+ code = llvm::getToken(code).second.ltrim();
+ OptionsWithRaw args(code);
+ code = args.GetRawPart();
+
+ // The position where the expression starts in the command line.
+ assert(original_code_size >= code.size());
+ std::size_t raw_start = original_code_size - code.size();
+
+ // Check if the cursor is actually in the expression string, and if not, we
+ // exit.
+ // FIXME: We should complete the options here.
+ if (cursor_pos < raw_start)
+ return 0;
+
+ // Make the cursor_pos again relative to the start of the code string.
+ assert(cursor_pos >= raw_start);
+ cursor_pos -= raw_start;
+
+ auto language = exe_ctx.GetFrameRef().GetLanguage();
+
+ Status error;
+ lldb::UserExpressionSP expr(target->GetUserExpressionForLanguage(
+ code, llvm::StringRef(), language, UserExpression::eResultTypeAny,
+ options, nullptr, error));
+ if (error.Fail())
+ return 0;
+
+ expr->Complete(exe_ctx, request, cursor_pos);
+ return request.GetNumberOfMatches();
+}
+
+static lldb_private::Status
+CanBeUsedForElementCountPrinting(ValueObject &valobj) {
+ CompilerType type(valobj.GetCompilerType());
+ CompilerType pointee;
+ if (!type.IsPointerType(&pointee))
+ return Status("as it does not refer to a pointer");
+ if (pointee.IsVoidType())
+ return Status("as it refers to a pointer to void");
+ return Status();
+}
+
+bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr,
+ Stream *output_stream,
+ Stream *error_stream,
+ CommandReturnObject *result) {
+ // Don't use m_exe_ctx as this might be called asynchronously after the
+ // command object DoExecute has finished when doing multi-line expression
+ // that use an input reader...
+ ExecutionContext exe_ctx(m_interpreter.GetExecutionContext());
+
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target)
+ target = GetDummyTarget();
+
+ if (target) {
+ lldb::ValueObjectSP result_valobj_sp;
+ bool keep_in_memory = true;
+ StackFrame *frame = exe_ctx.GetFramePtr();
+
+ EvaluateExpressionOptions options;
+ options.SetCoerceToId(m_varobj_options.use_objc);
+ options.SetUnwindOnError(m_command_options.unwind_on_error);
+ options.SetIgnoreBreakpoints(m_command_options.ignore_breakpoints);
+ options.SetKeepInMemory(keep_in_memory);
+ options.SetUseDynamic(m_varobj_options.use_dynamic);
+ options.SetTryAllThreads(m_command_options.try_all_threads);
+ options.SetDebug(m_command_options.debug);
+ options.SetLanguage(m_command_options.language);
+ options.SetExecutionPolicy(
+ m_command_options.allow_jit
+ ? EvaluateExpressionOptions::default_execution_policy
+ : lldb_private::eExecutionPolicyNever);
+
+ bool auto_apply_fixits;
+ if (m_command_options.auto_apply_fixits == eLazyBoolCalculate)
+ auto_apply_fixits = target->GetEnableAutoApplyFixIts();
+ else
+ auto_apply_fixits = m_command_options.auto_apply_fixits == eLazyBoolYes;
+
+ options.SetAutoApplyFixIts(auto_apply_fixits);
+
+ if (m_command_options.top_level)
+ options.SetExecutionPolicy(eExecutionPolicyTopLevel);
+
+ // If there is any chance we are going to stop and want to see what went
+ // wrong with our expression, we should generate debug info
+ if (!m_command_options.ignore_breakpoints ||
+ !m_command_options.unwind_on_error)
+ options.SetGenerateDebugInfo(true);
+
+ if (m_command_options.timeout > 0)
+ options.SetTimeout(std::chrono::microseconds(m_command_options.timeout));
+ else
+ options.SetTimeout(llvm::None);
+
+ ExpressionResults success = target->EvaluateExpression(
+ expr, frame, result_valobj_sp, options, &m_fixed_expression);
+
+ // We only tell you about the FixIt if we applied it. The compiler errors
+ // will suggest the FixIt if it parsed.
+ if (error_stream && !m_fixed_expression.empty() &&
+ target->GetEnableNotifyAboutFixIts()) {
+ if (success == eExpressionCompleted)
+ error_stream->Printf(
+ " Fix-it applied, fixed expression was: \n %s\n",
+ m_fixed_expression.c_str());
+ }
+
+ if (result_valobj_sp) {
+ Format format = m_format_options.GetFormat();
+
+ if (result_valobj_sp->GetError().Success()) {
+ if (format != eFormatVoid) {
+ if (format != eFormatDefault)
+ result_valobj_sp->SetFormat(format);
+
+ if (m_varobj_options.elem_count > 0) {
+ Status error(CanBeUsedForElementCountPrinting(*result_valobj_sp));
+ if (error.Fail()) {
+ result->AppendErrorWithFormat(
+ "expression cannot be used with --element-count %s\n",
+ error.AsCString(""));
+ result->SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
+ m_command_options.m_verbosity, format));
+ options.SetVariableFormatDisplayLanguage(
+ result_valobj_sp->GetPreferredDisplayLanguage());
+
+ result_valobj_sp->Dump(*output_stream, options);
+
+ if (result)
+ result->SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ } else {
+ if (result_valobj_sp->GetError().GetError() ==
+ UserExpression::kNoResult) {
+ if (format != eFormatVoid && GetDebugger().GetNotifyVoid()) {
+ error_stream->PutCString("(void)\n");
+ }
+
+ if (result)
+ result->SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ const char *error_cstr = result_valobj_sp->GetError().AsCString();
+ if (error_cstr && error_cstr[0]) {
+ const size_t error_cstr_len = strlen(error_cstr);
+ const bool ends_with_newline =
+ error_cstr[error_cstr_len - 1] == '\n';
+ if (strstr(error_cstr, "error:") != error_cstr)
+ error_stream->PutCString("error: ");
+ error_stream->Write(error_cstr, error_cstr_len);
+ if (!ends_with_newline)
+ error_stream->EOL();
+ } else {
+ error_stream->PutCString("error: unknown error\n");
+ }
+
+ if (result)
+ result->SetStatus(eReturnStatusFailed);
+ }
+ }
+ }
+ } else {
+ error_stream->Printf("error: invalid execution context for expression\n");
+ return false;
+ }
+
+ return true;
+}
+
+void CommandObjectExpression::IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) {
+ io_handler.SetIsDone(true);
+ // StreamSP output_stream =
+ // io_handler.GetDebugger().GetAsyncOutputStream();
+ // StreamSP error_stream = io_handler.GetDebugger().GetAsyncErrorStream();
+ StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ StreamFileSP error_sp(io_handler.GetErrorStreamFile());
+
+ EvaluateExpression(line.c_str(), output_sp.get(), error_sp.get());
+ if (output_sp)
+ output_sp->Flush();
+ if (error_sp)
+ error_sp->Flush();
+}
+
+bool CommandObjectExpression::IOHandlerIsInputComplete(IOHandler &io_handler,
+ StringList &lines) {
+ // An empty lines is used to indicate the end of input
+ const size_t num_lines = lines.GetSize();
+ if (num_lines > 0 && lines[num_lines - 1].empty()) {
+ // Remove the last empty line from "lines" so it doesn't appear in our
+ // resulting input and return true to indicate we are done getting lines
+ lines.PopBack();
+ return true;
+ }
+ return false;
+}
+
+void CommandObjectExpression::GetMultilineExpression() {
+ m_expr_lines.clear();
+ m_expr_line_count = 0;
+
+ Debugger &debugger = GetCommandInterpreter().GetDebugger();
+ bool color_prompt = debugger.GetUseColor();
+ const bool multiple_lines = true; // Get multiple lines
+ IOHandlerSP io_handler_sp(
+ new IOHandlerEditline(debugger, IOHandler::Type::Expression,
+ "lldb-expr", // Name of input reader for history
+ llvm::StringRef(), // No prompt
+ llvm::StringRef(), // Continuation prompt
+ multiple_lines, color_prompt,
+ 1, // Show line numbers starting at 1
+ *this, nullptr));
+
+ StreamFileSP output_sp(io_handler_sp->GetOutputStreamFile());
+ if (output_sp) {
+ output_sp->PutCString(
+ "Enter expressions, then terminate with an empty line to evaluate:\n");
+ output_sp->Flush();
+ }
+ debugger.PushIOHandler(io_handler_sp);
+}
+
+static EvaluateExpressionOptions
+GetExprOptions(ExecutionContext &ctx,
+ CommandObjectExpression::CommandOptions command_options) {
+ command_options.OptionParsingStarting(&ctx);
+
+ // Default certain settings for REPL regardless of the global settings.
+ command_options.unwind_on_error = false;
+ command_options.ignore_breakpoints = false;
+ command_options.debug = false;
+
+ EvaluateExpressionOptions expr_options;
+ expr_options.SetUnwindOnError(command_options.unwind_on_error);
+ expr_options.SetIgnoreBreakpoints(command_options.ignore_breakpoints);
+ expr_options.SetTryAllThreads(command_options.try_all_threads);
+
+ if (command_options.timeout > 0)
+ expr_options.SetTimeout(std::chrono::microseconds(command_options.timeout));
+ else
+ expr_options.SetTimeout(llvm::None);
+
+ return expr_options;
+}
+
+bool CommandObjectExpression::DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) {
+ m_fixed_expression.clear();
+ auto exe_ctx = GetCommandInterpreter().GetExecutionContext();
+ m_option_group.NotifyOptionParsingStarting(&exe_ctx);
+
+ if (command.empty()) {
+ GetMultilineExpression();
+ return result.Succeeded();
+ }
+
+ OptionsWithRaw args(command);
+ llvm::StringRef expr = args.GetRawPart();
+
+ if (args.HasArgs()) {
+ if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group, exe_ctx))
+ return false;
+
+ if (m_repl_option.GetOptionValue().GetCurrentValue()) {
+ Target *target = m_interpreter.GetExecutionContext().GetTargetPtr();
+ if (target) {
+ // Drop into REPL
+ m_expr_lines.clear();
+ m_expr_line_count = 0;
+
+ Debugger &debugger = target->GetDebugger();
+
+ // Check if the LLDB command interpreter is sitting on top of a REPL
+ // that launched it...
+ if (debugger.CheckTopIOHandlerTypes(IOHandler::Type::CommandInterpreter,
+ IOHandler::Type::REPL)) {
+ // the LLDB command interpreter is sitting on top of a REPL that
+ // launched it, so just say the command interpreter is done and
+ // fall back to the existing REPL
+ m_interpreter.GetIOHandler(false)->SetIsDone(true);
+ } else {
+ // We are launching the REPL on top of the current LLDB command
+ // interpreter, so just push one
+ bool initialize = false;
+ Status repl_error;
+ REPLSP repl_sp(target->GetREPL(repl_error, m_command_options.language,
+ nullptr, false));
+
+ if (!repl_sp) {
+ initialize = true;
+ repl_sp = target->GetREPL(repl_error, m_command_options.language,
+ nullptr, true);
+ if (!repl_error.Success()) {
+ result.SetError(repl_error);
+ return result.Succeeded();
+ }
+ }
+
+ if (repl_sp) {
+ if (initialize) {
+ repl_sp->SetEvaluateOptions(
+ GetExprOptions(exe_ctx, m_command_options));
+ repl_sp->SetFormatOptions(m_format_options);
+ repl_sp->SetValueObjectDisplayOptions(m_varobj_options);
+ }
+
+ IOHandlerSP io_handler_sp(repl_sp->GetIOHandler());
+
+ io_handler_sp->SetIsDone(false);
+
+ debugger.PushIOHandler(io_handler_sp);
+ } else {
+ repl_error.SetErrorStringWithFormat(
+ "Couldn't create a REPL for %s",
+ Language::GetNameForLanguageType(m_command_options.language));
+ result.SetError(repl_error);
+ return result.Succeeded();
+ }
+ }
+ }
+ }
+ // No expression following options
+ else if (expr.empty()) {
+ GetMultilineExpression();
+ return result.Succeeded();
+ }
+ }
+
+ Target *target = GetSelectedOrDummyTarget();
+ if (EvaluateExpression(expr, &(result.GetOutputStream()),
+ &(result.GetErrorStream()), &result)) {
+
+ if (!m_fixed_expression.empty() && target->GetEnableNotifyAboutFixIts()) {
+ CommandHistory &history = m_interpreter.GetCommandHistory();
+ // FIXME: Can we figure out what the user actually typed (e.g. some alias
+ // for expr???)
+ // If we can it would be nice to show that.
+ std::string fixed_command("expression ");
+ if (args.HasArgs()) {
+ // Add in any options that might have been in the original command:
+ fixed_command.append(args.GetArgStringWithDelimiter());
+ fixed_command.append(m_fixed_expression);
+ } else
+ fixed_command.append(m_fixed_expression);
+ history.AppendString(fixed_command);
+ }
+ // Increment statistics to record this expression evaluation success.
+ target->IncrementStats(StatisticKind::ExpressionSuccessful);
+ return true;
+ }
+
+ // Increment statistics to record this expression evaluation failure.
+ target->IncrementStats(StatisticKind::ExpressionFailure);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.h
new file mode 100644
index 000000000000..89c8e1dbeceb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectExpression.h
@@ -0,0 +1,87 @@
+//===-- CommandObjectExpression.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectExpression_h_
+#define liblldb_CommandObjectExpression_h_
+
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/lldb-private-enumerations.h"
+namespace lldb_private {
+
+class CommandObjectExpression : public CommandObjectRaw,
+ public IOHandlerDelegate {
+public:
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions();
+
+ ~CommandOptions() override;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override;
+
+ bool top_level;
+ bool unwind_on_error;
+ bool ignore_breakpoints;
+ bool allow_jit;
+ bool show_types;
+ bool show_summary;
+ bool debug;
+ uint32_t timeout;
+ bool try_all_threads;
+ lldb::LanguageType language;
+ LanguageRuntimeDescriptionDisplayVerbosity m_verbosity;
+ LazyBool auto_apply_fixits;
+ };
+
+ CommandObjectExpression(CommandInterpreter &interpreter);
+
+ ~CommandObjectExpression() override;
+
+ Options *GetOptions() override;
+
+ int HandleCompletion(CompletionRequest &request) override;
+
+protected:
+ // IOHandler::Delegate functions
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) override;
+
+ bool IOHandlerIsInputComplete(IOHandler &io_handler,
+ StringList &lines) override;
+
+ bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override;
+
+ bool EvaluateExpression(llvm::StringRef expr, Stream *output_stream,
+ Stream *error_stream,
+ CommandReturnObject *result = nullptr);
+
+ void GetMultilineExpression();
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFormat m_format_options;
+ OptionGroupValueObjectDisplay m_varobj_options;
+ OptionGroupBoolean m_repl_option;
+ CommandOptions m_command_options;
+ uint32_t m_expr_line_count;
+ std::string m_expr_lines; // Multi-line expression support
+ std::string m_fixed_expression; // Holds the current expression's fixed text.
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectExpression_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.cpp
new file mode 100644
index 000000000000..ab6a07952f19
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.cpp
@@ -0,0 +1,1134 @@
+//===-- CommandObjectFrame.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "CommandObjectFrame.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/DataFormatters/ValueObjectPrinter.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
+#include "lldb/Interpreter/OptionGroupVariable.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StackFrameRecognizer.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+#include <memory>
+#include <string>
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark CommandObjectFrameDiagnose
+
+// CommandObjectFrameInfo
+
+// CommandObjectFrameDiagnose
+
+static constexpr OptionDefinition g_frame_diag_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "register", 'r', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeRegisterName, "A register to diagnose." },
+ { LLDB_OPT_SET_1, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddress, "An address to diagnose." },
+ { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOffset, "An optional offset. Requires --register." }
+ // clang-format on
+};
+
+class CommandObjectFrameDiagnose : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'r':
+ reg = ConstString(option_arg);
+ break;
+
+ case 'a': {
+ address.emplace();
+ if (option_arg.getAsInteger(0, *address)) {
+ address.reset();
+ error.SetErrorStringWithFormat("invalid address argument '%s'",
+ option_arg.str().c_str());
+ }
+ } break;
+
+ case 'o': {
+ offset.emplace();
+ if (option_arg.getAsInteger(0, *offset)) {
+ offset.reset();
+ error.SetErrorStringWithFormat("invalid offset argument '%s'",
+ option_arg.str().c_str());
+ }
+ } break;
+
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ address.reset();
+ reg.reset();
+ offset.reset();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_frame_diag_options);
+ }
+
+ // Options.
+ llvm::Optional<lldb::addr_t> address;
+ llvm::Optional<ConstString> reg;
+ llvm::Optional<int64_t> offset;
+ };
+
+ CommandObjectFrameDiagnose(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame diagnose",
+ "Try to determine what path path the current stop "
+ "location used to get to a register or address",
+ nullptr,
+ eCommandRequiresThread | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeFrameIndex;
+ index_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectFrameDiagnose() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ StackFrameSP frame_sp = thread->GetSelectedFrame();
+
+ ValueObjectSP valobj_sp;
+
+ if (m_options.address.hasValue()) {
+ if (m_options.reg.hasValue() || m_options.offset.hasValue()) {
+ result.AppendError(
+ "`frame diagnose --address` is incompatible with other arguments.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ valobj_sp = frame_sp->GuessValueForAddress(m_options.address.getValue());
+ } else if (m_options.reg.hasValue()) {
+ valobj_sp = frame_sp->GuessValueForRegisterAndOffset(
+ m_options.reg.getValue(), m_options.offset.getValueOr(0));
+ } else {
+ StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (!stop_info_sp) {
+ result.AppendError("No arguments provided, and no stop info.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ valobj_sp = StopInfo::GetCrashingDereference(stop_info_sp);
+ }
+
+ if (!valobj_sp) {
+ result.AppendError("No diagnosis available.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+
+ DumpValueObjectOptions::DeclPrintingHelper helper = [&valobj_sp](
+ ConstString type, ConstString var, const DumpValueObjectOptions &opts,
+ Stream &stream) -> bool {
+ const ValueObject::GetExpressionPathFormat format = ValueObject::
+ GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers;
+ const bool qualify_cxx_base_classes = false;
+ valobj_sp->GetExpressionPath(stream, qualify_cxx_base_classes, format);
+ stream.PutCString(" =");
+ return true;
+ };
+
+ DumpValueObjectOptions options;
+ options.SetDeclPrintingHelper(helper);
+ ValueObjectPrinter printer(valobj_sp.get(), &result.GetOutputStream(),
+ options);
+ printer.PrintValueObject();
+
+ return true;
+ }
+
+protected:
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectFrameInfo
+
+// CommandObjectFrameInfo
+
+class CommandObjectFrameInfo : public CommandObjectParsed {
+public:
+ CommandObjectFrameInfo(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "frame info", "List information about the current "
+ "stack frame in the current thread.",
+ "frame info",
+ eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
+
+ ~CommandObjectFrameInfo() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ m_exe_ctx.GetFrameRef().DumpUsingSettingsFormat(&result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectFrameSelect
+
+// CommandObjectFrameSelect
+
+static OptionDefinition g_frame_select_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "relative", 'r', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOffset, "A relative frame index offset from the current frame index." },
+ // clang-format on
+};
+
+class CommandObjectFrameSelect : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'r':
+ if (option_arg.getAsInteger(0, relative_frame_offset)) {
+ relative_frame_offset = INT32_MIN;
+ error.SetErrorStringWithFormat("invalid frame offset argument '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ relative_frame_offset = INT32_MIN;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_frame_select_options);
+ }
+
+ int32_t relative_frame_offset;
+ };
+
+ CommandObjectFrameSelect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "frame select", "Select the current stack frame by "
+ "index from within the current thread "
+ "(see 'thread backtrace'.)",
+ nullptr,
+ eCommandRequiresThread | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeFrameIndex;
+ index_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectFrameSelect() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // No need to check "thread" for validity as eCommandRequiresThread ensures
+ // it is valid
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+
+ uint32_t frame_idx = UINT32_MAX;
+ if (m_options.relative_frame_offset != INT32_MIN) {
+ // The one and only argument is a signed relative frame index
+ frame_idx = thread->GetSelectedFrameIndex();
+ if (frame_idx == UINT32_MAX)
+ frame_idx = 0;
+
+ if (m_options.relative_frame_offset < 0) {
+ if (static_cast<int32_t>(frame_idx) >= -m_options.relative_frame_offset)
+ frame_idx += m_options.relative_frame_offset;
+ else {
+ if (frame_idx == 0) {
+ // If you are already at the bottom of the stack, then just warn
+ // and don't reset the frame.
+ result.AppendError("Already at the bottom of the stack.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else
+ frame_idx = 0;
+ }
+ } else if (m_options.relative_frame_offset > 0) {
+ // I don't want "up 20" where "20" takes you past the top of the stack
+ // to produce
+ // an error, but rather to just go to the top. So I have to count the
+ // stack here...
+ const uint32_t num_frames = thread->GetStackFrameCount();
+ if (static_cast<int32_t>(num_frames - frame_idx) >
+ m_options.relative_frame_offset)
+ frame_idx += m_options.relative_frame_offset;
+ else {
+ if (frame_idx == num_frames - 1) {
+ // If we are already at the top of the stack, just warn and don't
+ // reset the frame.
+ result.AppendError("Already at the top of the stack.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else
+ frame_idx = num_frames - 1;
+ }
+ }
+ } else {
+ if (command.GetArgumentCount() > 1) {
+ result.AppendErrorWithFormat(
+ "too many arguments; expected frame-index, saw '%s'.\n",
+ command[0].c_str());
+ m_options.GenerateOptionUsage(
+ result.GetErrorStream(), this,
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 1) {
+ if (command[0].ref.getAsInteger(0, frame_idx)) {
+ result.AppendErrorWithFormat("invalid frame index argument '%s'.",
+ command[0].c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else if (command.GetArgumentCount() == 0) {
+ frame_idx = thread->GetSelectedFrameIndex();
+ if (frame_idx == UINT32_MAX) {
+ frame_idx = 0;
+ }
+ }
+ }
+
+ bool success = thread->SetSelectedFrameByIndexNoisily(
+ frame_idx, result.GetOutputStream());
+ if (success) {
+ m_exe_ctx.SetFrameSP(thread->GetSelectedFrame());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Frame index (%u) out of range.\n",
+ frame_idx);
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+
+protected:
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectFrameVariable
+// List images with associated information
+class CommandObjectFrameVariable : public CommandObjectParsed {
+public:
+ CommandObjectFrameVariable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "frame variable",
+ "Show variables for the current stack frame. Defaults to all "
+ "arguments and local variables in scope. Names of argument, "
+ "local, file static and file global variables can be specified. "
+ "Children of aggregate variables can be specified such as "
+ "'var->child.x'. The -> and [] operators in 'frame variable' do "
+ "not invoke operator overloads if they exist, but directly access "
+ "the specified element. If you want to trigger operator overloads "
+ "use the expression command to print the variable instead."
+ "\nIt is worth noting that except for overloaded "
+ "operators, when printing local variables 'expr local_var' and "
+ "'frame var local_var' produce the same "
+ "results. However, 'frame variable' is more efficient, since it "
+ "uses debug information and memory reads directly, rather than "
+ "parsing and evaluating an expression, which may even involve "
+ "JITing and running code in the target program.",
+ nullptr, eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused | eCommandRequiresProcess),
+ m_option_group(),
+ m_option_variable(
+ true), // Include the frame specific options by passing "true"
+ m_option_format(eFormatDefault),
+ m_varobj_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData var_name_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeVarName;
+ var_name_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(var_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_option_format,
+ OptionGroupFormat::OPTION_GROUP_FORMAT |
+ OptionGroupFormat::OPTION_GROUP_GDB_FMT,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectFrameVariable() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Arguments are the standard source file completer.
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eVariablePathCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ llvm::StringRef GetScopeString(VariableSP var_sp) {
+ if (!var_sp)
+ return llvm::StringRef::withNullAsEmpty(nullptr);
+
+ switch (var_sp->GetScope()) {
+ case eValueTypeVariableGlobal:
+ return "GLOBAL: ";
+ case eValueTypeVariableStatic:
+ return "STATIC: ";
+ case eValueTypeVariableArgument:
+ return "ARG: ";
+ case eValueTypeVariableLocal:
+ return "LOCAL: ";
+ case eValueTypeVariableThreadLocal:
+ return "THREAD: ";
+ default:
+ break;
+ }
+
+ return llvm::StringRef::withNullAsEmpty(nullptr);
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // No need to check "frame" for validity as eCommandRequiresFrame ensures
+ // it is valid
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+
+ Stream &s = result.GetOutputStream();
+
+ // Be careful about the stack frame, if any summary formatter runs code, it
+ // might clear the StackFrameList for the thread. So hold onto a shared
+ // pointer to the frame so it stays alive.
+
+ VariableList *variable_list =
+ frame->GetVariableList(m_option_variable.show_globals);
+
+ VariableSP var_sp;
+ ValueObjectSP valobj_sp;
+
+ TypeSummaryImplSP summary_format_sp;
+ if (!m_option_variable.summary.IsCurrentValueEmpty())
+ DataVisualization::NamedSummaryFormats::GetSummaryFormat(
+ ConstString(m_option_variable.summary.GetCurrentValue()),
+ summary_format_sp);
+ else if (!m_option_variable.summary_string.IsCurrentValueEmpty())
+ summary_format_sp = std::make_shared<StringSummaryFormat>(
+ TypeSummaryImpl::Flags(),
+ m_option_variable.summary_string.GetCurrentValue());
+
+ DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
+ eLanguageRuntimeDescriptionDisplayVerbosityFull, eFormatDefault,
+ summary_format_sp));
+
+ const SymbolContext &sym_ctx =
+ frame->GetSymbolContext(eSymbolContextFunction);
+ if (sym_ctx.function && sym_ctx.function->IsTopLevelFunction())
+ m_option_variable.show_globals = true;
+
+ if (variable_list) {
+ const Format format = m_option_format.GetFormat();
+ options.SetFormat(format);
+
+ if (!command.empty()) {
+ VariableList regex_var_list;
+
+ // If we have any args to the variable command, we will make variable
+ // objects from them...
+ for (auto &entry : command) {
+ if (m_option_variable.use_regex) {
+ const size_t regex_start_index = regex_var_list.GetSize();
+ llvm::StringRef name_str = entry.ref;
+ RegularExpression regex(name_str);
+ if (regex.Compile(name_str)) {
+ size_t num_matches = 0;
+ const size_t num_new_regex_vars =
+ variable_list->AppendVariablesIfUnique(regex, regex_var_list,
+ num_matches);
+ if (num_new_regex_vars > 0) {
+ for (size_t regex_idx = regex_start_index,
+ end_index = regex_var_list.GetSize();
+ regex_idx < end_index; ++regex_idx) {
+ var_sp = regex_var_list.GetVariableAtIndex(regex_idx);
+ if (var_sp) {
+ valobj_sp = frame->GetValueObjectForFrameVariable(
+ var_sp, m_varobj_options.use_dynamic);
+ if (valobj_sp) {
+ std::string scope_string;
+ if (m_option_variable.show_scope)
+ scope_string = GetScopeString(var_sp).str();
+
+ if (!scope_string.empty())
+ s.PutCString(scope_string);
+
+ if (m_option_variable.show_decl &&
+ var_sp->GetDeclaration().GetFile()) {
+ bool show_fullpaths = false;
+ bool show_module = true;
+ if (var_sp->DumpDeclaration(&s, show_fullpaths,
+ show_module))
+ s.PutCString(": ");
+ }
+ valobj_sp->Dump(result.GetOutputStream(), options);
+ }
+ }
+ }
+ } else if (num_matches == 0) {
+ result.GetErrorStream().Printf("error: no variables matched "
+ "the regular expression '%s'.\n",
+ entry.c_str());
+ }
+ } else {
+ char regex_error[1024];
+ if (regex.GetErrorAsCString(regex_error, sizeof(regex_error)))
+ result.GetErrorStream().Printf("error: %s\n", regex_error);
+ else
+ result.GetErrorStream().Printf(
+ "error: unknown regex error when compiling '%s'\n",
+ entry.c_str());
+ }
+ } else // No regex, either exact variable names or variable
+ // expressions.
+ {
+ Status error;
+ uint32_t expr_path_options =
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
+ StackFrame::eExpressionPathOptionsInspectAnonymousUnions;
+ lldb::VariableSP var_sp;
+ valobj_sp = frame->GetValueForVariableExpressionPath(
+ entry.ref, m_varobj_options.use_dynamic, expr_path_options,
+ var_sp, error);
+ if (valobj_sp) {
+ std::string scope_string;
+ if (m_option_variable.show_scope)
+ scope_string = GetScopeString(var_sp).str();
+
+ if (!scope_string.empty())
+ s.PutCString(scope_string);
+ if (m_option_variable.show_decl && var_sp &&
+ var_sp->GetDeclaration().GetFile()) {
+ var_sp->GetDeclaration().DumpStopContext(&s, false);
+ s.PutCString(": ");
+ }
+
+ options.SetFormat(format);
+ options.SetVariableFormatDisplayLanguage(
+ valobj_sp->GetPreferredDisplayLanguage());
+
+ Stream &output_stream = result.GetOutputStream();
+ options.SetRootValueObjectName(
+ valobj_sp->GetParent() ? entry.c_str() : nullptr);
+ valobj_sp->Dump(output_stream, options);
+ } else {
+ const char *error_cstr = error.AsCString(nullptr);
+ if (error_cstr)
+ result.GetErrorStream().Printf("error: %s\n", error_cstr);
+ else
+ result.GetErrorStream().Printf("error: unable to find any "
+ "variable expression path that "
+ "matches '%s'.\n",
+ entry.c_str());
+ }
+ }
+ }
+ } else // No command arg specified. Use variable_list, instead.
+ {
+ const size_t num_variables = variable_list->GetSize();
+ if (num_variables > 0) {
+ for (size_t i = 0; i < num_variables; i++) {
+ var_sp = variable_list->GetVariableAtIndex(i);
+ switch (var_sp->GetScope()) {
+ case eValueTypeVariableGlobal:
+ if (!m_option_variable.show_globals)
+ continue;
+ break;
+ case eValueTypeVariableStatic:
+ if (!m_option_variable.show_globals)
+ continue;
+ break;
+ case eValueTypeVariableArgument:
+ if (!m_option_variable.show_args)
+ continue;
+ break;
+ case eValueTypeVariableLocal:
+ if (!m_option_variable.show_locals)
+ continue;
+ break;
+ default:
+ continue;
+ break;
+ }
+ std::string scope_string;
+ if (m_option_variable.show_scope)
+ scope_string = GetScopeString(var_sp).str();
+
+ // Use the variable object code to make sure we are using the same
+ // APIs as the public API will be using...
+ valobj_sp = frame->GetValueObjectForFrameVariable(
+ var_sp, m_varobj_options.use_dynamic);
+ if (valobj_sp) {
+ // When dumping all variables, don't print any variables that are
+ // not in scope to avoid extra unneeded output
+ if (valobj_sp->IsInScope()) {
+ if (!valobj_sp->GetTargetSP()
+ ->GetDisplayRuntimeSupportValues() &&
+ valobj_sp->IsRuntimeSupportValue())
+ continue;
+
+ if (!scope_string.empty())
+ s.PutCString(scope_string);
+
+ if (m_option_variable.show_decl &&
+ var_sp->GetDeclaration().GetFile()) {
+ var_sp->GetDeclaration().DumpStopContext(&s, false);
+ s.PutCString(": ");
+ }
+
+ options.SetFormat(format);
+ options.SetVariableFormatDisplayLanguage(
+ valobj_sp->GetPreferredDisplayLanguage());
+ options.SetRootValueObjectName(
+ var_sp ? var_sp->GetName().AsCString() : nullptr);
+ valobj_sp->Dump(result.GetOutputStream(), options);
+ }
+ }
+ }
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+
+ if (m_option_variable.show_recognized_args) {
+ auto recognized_frame = frame->GetRecognizedFrame();
+ if (recognized_frame) {
+ ValueObjectListSP recognized_arg_list =
+ recognized_frame->GetRecognizedArguments();
+ if (recognized_arg_list) {
+ for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
+ options.SetFormat(m_option_format.GetFormat());
+ options.SetVariableFormatDisplayLanguage(
+ rec_value_sp->GetPreferredDisplayLanguage());
+ options.SetRootValueObjectName(rec_value_sp->GetName().AsCString());
+ rec_value_sp->Dump(result.GetOutputStream(), options);
+ }
+ }
+ }
+ }
+
+ if (m_interpreter.TruncationWarningNecessary()) {
+ result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(),
+ m_cmd_name.c_str());
+ m_interpreter.TruncationWarningGiven();
+ }
+
+ // Increment statistics.
+ bool res = result.Succeeded();
+ Target *target = GetSelectedOrDummyTarget();
+ if (res)
+ target->IncrementStats(StatisticKind::FrameVarSuccess);
+ else
+ target->IncrementStats(StatisticKind::FrameVarFailure);
+ return res;
+ }
+
+protected:
+ OptionGroupOptions m_option_group;
+ OptionGroupVariable m_option_variable;
+ OptionGroupFormat m_option_format;
+ OptionGroupValueObjectDisplay m_varobj_options;
+};
+
+#pragma mark CommandObjectFrameRecognizer
+
+static OptionDefinition g_frame_recognizer_add_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "shlib", 's', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Name of the module or shared library that this recognizer applies to." },
+ { LLDB_OPT_SET_ALL, false, "function", 'n', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeName, "Name of the function that this recognizer applies to." },
+ { LLDB_OPT_SET_2, false, "python-class", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePythonClass, "Give the name of a Python class to use for this frame recognizer." },
+ { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Function name and module name are actually regular expressions." }
+ // clang-format on
+};
+
+class CommandObjectFrameRecognizerAdd : public CommandObjectParsed {
+private:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'l':
+ m_class_name = std::string(option_arg);
+ break;
+ case 's':
+ m_module = std::string(option_arg);
+ break;
+ case 'n':
+ m_function = std::string(option_arg);
+ break;
+ case 'x':
+ m_regex = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_module = "";
+ m_function = "";
+ m_class_name = "";
+ m_regex = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_frame_recognizer_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ std::string m_class_name;
+ std::string m_module;
+ std::string m_function;
+ bool m_regex;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+
+public:
+ CommandObjectFrameRecognizerAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame recognizer add",
+ "Add a new frame recognizer.", nullptr),
+ m_options() {
+ SetHelpLong(R"(
+Frame recognizers allow for retrieving information about special frames based on
+ABI, arguments or other special properties of that frame, even without source
+code or debug info. Currently, one use case is to extract function arguments
+that would otherwise be unaccesible, or augment existing arguments.
+
+Adding a custom frame recognizer is possible by implementing a Python class
+and using the 'frame recognizer add' command. The Python class should have a
+'get_recognized_arguments' method and it will receive an argument of type
+lldb.SBFrame representing the current frame that we are trying to recognize.
+The method should return a (possibly empty) list of lldb.SBValue objects that
+represent the recognized arguments.
+
+An example of a recognizer that retrieves the file descriptor values from libc
+functions 'read', 'write' and 'close' follows:
+
+ class LibcFdRecognizer(object):
+ def get_recognized_arguments(self, frame):
+ if frame.name in ["read", "write", "close"]:
+ fd = frame.EvaluateExpression("$arg1").unsigned
+ value = lldb.target.CreateValueFromExpression("fd", "(int)%d" % fd)
+ return [value]
+ return []
+
+The file containing this implementation can be imported via 'command script
+import' and then we can register this recognizer with 'frame recognizer add'.
+It's important to restrict the recognizer to the libc library (which is
+libsystem_kernel.dylib on macOS) to avoid matching functions with the same name
+in other modules:
+
+(lldb) command script import .../fd_recognizer.py
+(lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib
+
+When the program is stopped at the beginning of the 'read' function in libc, we
+can view the recognizer arguments in 'frame variable':
+
+(lldb) b read
+(lldb) r
+Process 1234 stopped
+* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
+ frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
+(lldb) frame variable
+(int) fd = 3
+
+ )");
+ }
+ ~CommandObjectFrameRecognizerAdd() override = default;
+};
+
+bool CommandObjectFrameRecognizerAdd::DoExecute(Args &command,
+ CommandReturnObject &result) {
+#ifndef LLDB_DISABLE_PYTHON
+ if (m_options.m_class_name.empty()) {
+ result.AppendErrorWithFormat(
+ "%s needs a Python class name (-l argument).\n", m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_options.m_module.empty()) {
+ result.AppendErrorWithFormat("%s needs a module name (-s argument).\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_options.m_function.empty()) {
+ result.AppendErrorWithFormat("%s needs a function name (-n argument).\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+
+ if (interpreter &&
+ !interpreter->CheckObjectExists(m_options.m_class_name.c_str())) {
+ result.AppendWarning(
+ "The provided class does not exist - please define it "
+ "before attempting to use this frame recognizer");
+ }
+
+ StackFrameRecognizerSP recognizer_sp =
+ StackFrameRecognizerSP(new ScriptedStackFrameRecognizer(
+ interpreter, m_options.m_class_name.c_str()));
+ if (m_options.m_regex) {
+ auto module =
+ RegularExpressionSP(new RegularExpression(m_options.m_module));
+ auto func =
+ RegularExpressionSP(new RegularExpression(m_options.m_function));
+ StackFrameRecognizerManager::AddRecognizer(recognizer_sp, module, func);
+ } else {
+ auto module = ConstString(m_options.m_module);
+ auto func = ConstString(m_options.m_function);
+ StackFrameRecognizerManager::AddRecognizer(recognizer_sp, module, func);
+ }
+#endif
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+}
+
+class CommandObjectFrameRecognizerClear : public CommandObjectParsed {
+public:
+ CommandObjectFrameRecognizerClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame recognizer clear",
+ "Delete all frame recognizers.", nullptr) {}
+
+ ~CommandObjectFrameRecognizerClear() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ StackFrameRecognizerManager::RemoveAllRecognizers();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectFrameRecognizerDelete : public CommandObjectParsed {
+ public:
+ CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame recognizer delete",
+ "Delete an existing frame recognizer.", nullptr) {}
+
+ ~CommandObjectFrameRecognizerDelete() override = default;
+
+ protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() == 0) {
+ if (!m_interpreter.Confirm(
+ "About to delete all frame recognizers, do you want to do that?",
+ true)) {
+ result.AppendMessage("Operation cancelled...");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ StackFrameRecognizerManager::RemoveAllRecognizers();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+
+ if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat("'%s' takes zero or one arguments.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ uint32_t recognizer_id =
+ StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0);
+
+ StackFrameRecognizerManager::RemoveRecognizerWithID(recognizer_id);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectFrameRecognizerList : public CommandObjectParsed {
+ public:
+ CommandObjectFrameRecognizerList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "frame recognizer list",
+ "Show a list of active frame recognizers.",
+ nullptr) {}
+
+ ~CommandObjectFrameRecognizerList() override = default;
+
+ protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ bool any_printed = false;
+ StackFrameRecognizerManager::ForEach(
+ [&result, &any_printed](uint32_t recognizer_id, std::string name,
+ std::string function, std::string symbol,
+ bool regexp) {
+ if (name == "") name = "(internal)";
+ result.GetOutputStream().Printf(
+ "%d: %s, module %s, function %s%s\n", recognizer_id, name.c_str(),
+ function.c_str(), symbol.c_str(), regexp ? " (regexp)" : "");
+ any_printed = true;
+ });
+
+ if (any_printed)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.GetOutputStream().PutCString("no matching results found.\n");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectFrameRecognizerInfo : public CommandObjectParsed {
+ public:
+ CommandObjectFrameRecognizerInfo(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "frame recognizer info",
+ "Show which frame recognizer is applied a stack frame (if any).",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeFrameIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectFrameRecognizerInfo() override = default;
+
+ protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ if (thread == nullptr) {
+ result.AppendError("no thread");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ uint32_t frame_index =
+ StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0);
+ StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index);
+ if (!frame_sp) {
+ result.AppendErrorWithFormat("no frame with index %u", frame_index);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ auto recognizer =
+ StackFrameRecognizerManager::GetRecognizerForFrame(frame_sp);
+
+ Stream &output_stream = result.GetOutputStream();
+ output_stream.Printf("frame %d ", frame_index);
+ if (recognizer) {
+ output_stream << "is recognized by ";
+ output_stream << recognizer->GetName();
+ } else {
+ output_stream << "not recognized by any recognizer";
+ }
+ output_stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectFrameRecognizer : public CommandObjectMultiword {
+ public:
+ CommandObjectFrameRecognizer(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "frame recognizer",
+ "Commands for editing and viewing frame recognizers.",
+ "frame recognizer [<sub-command-options>] ") {
+ LoadSubCommand(
+ "add",
+ CommandObjectSP(new CommandObjectFrameRecognizerAdd(interpreter)));
+ LoadSubCommand(
+ "clear",
+ CommandObjectSP(new CommandObjectFrameRecognizerClear(interpreter)));
+ LoadSubCommand(
+ "delete",
+ CommandObjectSP(new CommandObjectFrameRecognizerDelete(interpreter)));
+ LoadSubCommand(
+ "list",
+ CommandObjectSP(new CommandObjectFrameRecognizerList(interpreter)));
+ LoadSubCommand(
+ "info",
+ CommandObjectSP(new CommandObjectFrameRecognizerInfo(interpreter)));
+ }
+
+ ~CommandObjectFrameRecognizer() override = default;
+};
+
+#pragma mark CommandObjectMultiwordFrame
+
+// CommandObjectMultiwordFrame
+
+CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "frame", "Commands for selecting and "
+ "examing the current "
+ "thread's stack frames.",
+ "frame <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("diagnose",
+ CommandObjectSP(new CommandObjectFrameDiagnose(interpreter)));
+ LoadSubCommand("info",
+ CommandObjectSP(new CommandObjectFrameInfo(interpreter)));
+ LoadSubCommand("select",
+ CommandObjectSP(new CommandObjectFrameSelect(interpreter)));
+ LoadSubCommand("variable",
+ CommandObjectSP(new CommandObjectFrameVariable(interpreter)));
+#ifndef LLDB_DISABLE_PYTHON
+ LoadSubCommand(
+ "recognizer",
+ CommandObjectSP(new CommandObjectFrameRecognizer(interpreter)));
+#endif
+}
+
+CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.h
new file mode 100644
index 000000000000..46a59f71733b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectFrame.h
@@ -0,0 +1,28 @@
+//===-- CommandObjectFrame.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectFrame_h_
+#define liblldb_CommandObjectFrame_h_
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/Options.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordFrame
+
+class CommandObjectMultiwordFrame : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordFrame(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordFrame() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectFrame_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.cpp
new file mode 100644
index 000000000000..21ed510d1264
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.cpp
@@ -0,0 +1,51 @@
+//===-- CommandObjectGUI.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectGUI.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectGUI
+
+CommandObjectGUI::CommandObjectGUI(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "gui",
+ "Switch into the curses based GUI mode.", "gui") {}
+
+CommandObjectGUI::~CommandObjectGUI() {}
+
+bool CommandObjectGUI::DoExecute(Args &args, CommandReturnObject &result) {
+#ifndef LLDB_DISABLE_CURSES
+ if (args.GetArgumentCount() == 0) {
+ Debugger &debugger = GetDebugger();
+
+ lldb::StreamFileSP input_sp = debugger.GetInputFile();
+ if (input_sp && input_sp->GetFile().GetIsRealTerminal() &&
+ input_sp->GetFile().GetIsInteractive()) {
+ IOHandlerSP io_handler_sp(new IOHandlerCursesGUI(debugger));
+ if (io_handler_sp)
+ debugger.PushIOHandler(io_handler_sp);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("the gui command requires an interactive terminal.");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("the gui command takes no arguments.");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return true;
+#else
+ result.AppendError("lldb was not build with gui support");
+ return false;
+#endif
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.h
new file mode 100644
index 000000000000..a19aad18ec35
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectGUI.h
@@ -0,0 +1,30 @@
+//===-- CommandObjectGUI.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectGUI_h_
+#define liblldb_CommandObjectGUI_h_
+
+#include "lldb/Interpreter/CommandObject.h"
+
+namespace lldb_private {
+
+// CommandObjectGUI
+
+class CommandObjectGUI : public CommandObjectParsed {
+public:
+ CommandObjectGUI(CommandInterpreter &interpreter);
+
+ ~CommandObjectGUI() override;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectGUI_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.cpp
new file mode 100644
index 000000000000..ab557919d0a0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.cpp
@@ -0,0 +1,226 @@
+//===-- CommandObjectHelp.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectHelp.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/Options.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectHelp
+
+void CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage(
+ Stream *s, llvm::StringRef command, llvm::StringRef prefix,
+ llvm::StringRef subcommand, bool include_upropos,
+ bool include_type_lookup) {
+ if (!s || command.empty())
+ return;
+
+ std::string command_str = command.str();
+ std::string prefix_str = prefix.str();
+ std::string subcommand_str = subcommand.str();
+ const std::string &lookup_str = !subcommand_str.empty() ? subcommand_str : command_str;
+ s->Printf("'%s' is not a known command.\n", command_str.c_str());
+ s->Printf("Try '%shelp' to see a current list of commands.\n",
+ prefix.str().c_str());
+ if (include_upropos) {
+ s->Printf("Try '%sapropos %s' for a list of related commands.\n",
+ prefix_str.c_str(), lookup_str.c_str());
+ }
+ if (include_type_lookup) {
+ s->Printf("Try '%stype lookup %s' for information on types, methods, "
+ "functions, modules, etc.",
+ prefix_str.c_str(), lookup_str.c_str());
+ }
+}
+
+CommandObjectHelp::CommandObjectHelp(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "help", "Show a list of all debugger "
+ "commands, or give details "
+ "about a specific command.",
+ "help [<cmd-name>]"),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData command_arg;
+
+ // Define the first (and only) variant of this arg.
+ command_arg.arg_type = eArgTypeCommandName;
+ command_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the argument
+ // entry.
+ arg.push_back(command_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+}
+
+CommandObjectHelp::~CommandObjectHelp() = default;
+
+static constexpr OptionDefinition g_help_options[] = {
+#define LLDB_OPTIONS_help
+#include "CommandOptions.inc"
+};
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectHelp::CommandOptions::GetDefinitions() {
+ return llvm::makeArrayRef(g_help_options);
+}
+
+bool CommandObjectHelp::DoExecute(Args &command, CommandReturnObject &result) {
+ CommandObject::CommandMap::iterator pos;
+ CommandObject *cmd_obj;
+ const size_t argc = command.GetArgumentCount();
+
+ // 'help' doesn't take any arguments, other than command names. If argc is
+ // 0, we show the user all commands (aliases and user commands if asked for).
+ // Otherwise every argument must be the name of a command or a sub-command.
+ if (argc == 0) {
+ uint32_t cmd_types = CommandInterpreter::eCommandTypesBuiltin;
+ if (m_options.m_show_aliases)
+ cmd_types |= CommandInterpreter::eCommandTypesAliases;
+ if (m_options.m_show_user_defined)
+ cmd_types |= CommandInterpreter::eCommandTypesUserDef;
+ if (m_options.m_show_hidden)
+ cmd_types |= CommandInterpreter::eCommandTypesHidden;
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ m_interpreter.GetHelp(result, cmd_types); // General help
+ } else {
+ // Get command object for the first command argument. Only search built-in
+ // command dictionary.
+ StringList matches;
+ auto command_name = command[0].ref;
+ cmd_obj = m_interpreter.GetCommandObject(command_name, &matches);
+
+ if (cmd_obj != nullptr) {
+ StringList matches;
+ bool all_okay = true;
+ CommandObject *sub_cmd_obj = cmd_obj;
+ // Loop down through sub_command dictionaries until we find the command
+ // object that corresponds to the help command entered.
+ std::string sub_command;
+ for (auto &entry : command.entries().drop_front()) {
+ sub_command = entry.ref;
+ matches.Clear();
+ if (sub_cmd_obj->IsAlias())
+ sub_cmd_obj =
+ ((CommandAlias *)sub_cmd_obj)->GetUnderlyingCommand().get();
+ if (!sub_cmd_obj->IsMultiwordObject()) {
+ all_okay = false;
+ break;
+ } else {
+ CommandObject *found_cmd;
+ found_cmd =
+ sub_cmd_obj->GetSubcommandObject(sub_command.c_str(), &matches);
+ if (found_cmd == nullptr || matches.GetSize() > 1) {
+ all_okay = false;
+ break;
+ } else
+ sub_cmd_obj = found_cmd;
+ }
+ }
+
+ if (!all_okay || (sub_cmd_obj == nullptr)) {
+ std::string cmd_string;
+ command.GetCommandString(cmd_string);
+ if (matches.GetSize() >= 2) {
+ StreamString s;
+ s.Printf("ambiguous command %s", cmd_string.c_str());
+ size_t num_matches = matches.GetSize();
+ for (size_t match_idx = 0; match_idx < num_matches; match_idx++) {
+ s.Printf("\n\t%s", matches.GetStringAtIndex(match_idx));
+ }
+ s.Printf("\n");
+ result.AppendError(s.GetString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (!sub_cmd_obj) {
+ StreamString error_msg_stream;
+ GenerateAdditionalHelpAvenuesMessage(
+ &error_msg_stream, cmd_string.c_str(),
+ m_interpreter.GetCommandPrefix(), sub_command.c_str());
+ result.AppendError(error_msg_stream.GetString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ GenerateAdditionalHelpAvenuesMessage(
+ &result.GetOutputStream(), cmd_string.c_str(),
+ m_interpreter.GetCommandPrefix(), sub_command.c_str());
+ result.GetOutputStream().Printf(
+ "\nThe closest match is '%s'. Help on it follows.\n\n",
+ sub_cmd_obj->GetCommandName().str().c_str());
+ }
+ }
+
+ sub_cmd_obj->GenerateHelpText(result);
+ std::string alias_full_name;
+ // Don't use AliasExists here, that only checks exact name matches. If
+ // the user typed a shorter unique alias name, we should still tell them
+ // it was an alias.
+ if (m_interpreter.GetAliasFullName(command_name, alias_full_name)) {
+ StreamString sstr;
+ m_interpreter.GetAlias(alias_full_name)->GetAliasExpansion(sstr);
+ result.GetOutputStream().Printf("\n'%s' is an abbreviation for %s\n",
+ command[0].c_str(), sstr.GetData());
+ }
+ } else if (matches.GetSize() > 0) {
+ Stream &output_strm = result.GetOutputStream();
+ output_strm.Printf("Help requested with ambiguous command name, possible "
+ "completions:\n");
+ const size_t match_count = matches.GetSize();
+ for (size_t i = 0; i < match_count; i++) {
+ output_strm.Printf("\t%s\n", matches.GetStringAtIndex(i));
+ }
+ } else {
+ // Maybe the user is asking for help about a command argument rather than
+ // a command.
+ const CommandArgumentType arg_type =
+ CommandObject::LookupArgumentName(command_name);
+ if (arg_type != eArgTypeLastArg) {
+ Stream &output_strm = result.GetOutputStream();
+ CommandObject::GetArgumentHelp(output_strm, arg_type, m_interpreter);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ StreamString error_msg_stream;
+ GenerateAdditionalHelpAvenuesMessage(&error_msg_stream, command_name,
+ m_interpreter.GetCommandPrefix(),
+ "");
+ result.AppendError(error_msg_stream.GetString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ }
+
+ return result.Succeeded();
+}
+
+int CommandObjectHelp::HandleCompletion(CompletionRequest &request) {
+ // Return the completions of the commands in the help system:
+ if (request.GetCursorIndex() == 0) {
+ return m_interpreter.HandleCompletionMatches(request);
+ } else {
+ CommandObject *cmd_obj =
+ m_interpreter.GetCommandObject(request.GetParsedLine()[0].ref);
+
+ // The command that they are getting help on might be ambiguous, in which
+ // case we should complete that, otherwise complete with the command the
+ // user is getting help on...
+
+ if (cmd_obj) {
+ request.GetParsedLine().Shift();
+ request.SetCursorIndex(request.GetCursorIndex() - 1);
+ return cmd_obj->HandleCompletion(request);
+ } else {
+ return m_interpreter.HandleCompletionMatches(request);
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.h
new file mode 100644
index 000000000000..a641b19a46d0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectHelp.h
@@ -0,0 +1,89 @@
+//===-- CommandObjectHelp.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectHelp_h_
+#define liblldb_CommandObjectHelp_h_
+
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/Options.h"
+
+namespace lldb_private {
+
+// CommandObjectHelp
+
+class CommandObjectHelp : public CommandObjectParsed {
+public:
+ CommandObjectHelp(CommandInterpreter &interpreter);
+
+ ~CommandObjectHelp() override;
+
+ int HandleCompletion(CompletionRequest &request) override;
+
+ static void GenerateAdditionalHelpAvenuesMessage(
+ Stream *s, llvm::StringRef command, llvm::StringRef prefix,
+ llvm::StringRef subcommand, bool include_upropos = true,
+ bool include_type_lookup = true);
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override {}
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a':
+ m_show_aliases = false;
+ break;
+ case 'u':
+ m_show_user_defined = false;
+ break;
+ case 'h':
+ m_show_hidden = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_show_aliases = true;
+ m_show_user_defined = true;
+ m_show_hidden = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
+
+ // Instance variables to hold the values for command options.
+
+ bool m_show_aliases;
+ bool m_show_user_defined;
+ bool m_show_hidden;
+ };
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+
+private:
+ CommandOptions m_options;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectHelp_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.cpp
new file mode 100644
index 000000000000..47c9e2a52023
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.cpp
@@ -0,0 +1,30 @@
+//===-- CommandObjectLanguage.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectLanguage.h"
+
+#include "lldb/Host/Host.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+
+#include "lldb/Target/Language.h"
+#include "lldb/Target/LanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+CommandObjectLanguage::CommandObjectLanguage(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "language", "Commands specific to a source language.",
+ "language <language-name> <subcommand> [<subcommand-options>]") {
+ // Let the LanguageRuntime populates this command with subcommands
+ LanguageRuntime::InitializeCommands(this);
+}
+
+CommandObjectLanguage::~CommandObjectLanguage() {}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.h
new file mode 100644
index 000000000000..b86457c99af3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectLanguage.h
@@ -0,0 +1,29 @@
+//===-- CommandObjectLanguage.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectLanguage_h_
+#define liblldb_CommandObjectLanguage_h_
+
+
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+class CommandObjectLanguage : public CommandObjectMultiword {
+public:
+ CommandObjectLanguage(CommandInterpreter &interpreter);
+
+ ~CommandObjectLanguage() override;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result);
+};
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectLanguage_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.cpp
new file mode 100644
index 000000000000..2ad61de1a3e9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.cpp
@@ -0,0 +1,368 @@
+//===-- CommandObjectLog.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectLog.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static constexpr OptionDefinition g_log_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeFilename, "Set the destination file to log to." },
+ { LLDB_OPT_SET_1, false, "threadsafe", 't', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Enable thread safe logging to avoid interweaved log lines." },
+ { LLDB_OPT_SET_1, false, "verbose", 'v', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Enable verbose logging." },
+ { LLDB_OPT_SET_1, false, "sequence", 's', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Prepend all log lines with an increasing integer sequence id." },
+ { LLDB_OPT_SET_1, false, "timestamp", 'T', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Prepend all log lines with a timestamp." },
+ { LLDB_OPT_SET_1, false, "pid-tid", 'p', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Prepend all log lines with the process and thread ID that generates the log line." },
+ { LLDB_OPT_SET_1, false, "thread-name",'n', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Prepend all log lines with the thread name for the thread that generates the log line." },
+ { LLDB_OPT_SET_1, false, "stack", 'S', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Append a stack backtrace to each log line." },
+ { LLDB_OPT_SET_1, false, "append", 'a', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Append to the log file instead of overwriting." },
+ { LLDB_OPT_SET_1, false, "file-function",'F',OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Prepend the names of files and function that generate the logs." },
+ // clang-format on
+};
+
+class CommandObjectLogEnable : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogEnable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log enable",
+ "Enable logging for a single log channel.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData channel_arg;
+ CommandArgumentData category_arg;
+
+ // Define the first (and only) variant of this arg.
+ channel_arg.arg_type = eArgTypeLogChannel;
+ channel_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(channel_arg);
+
+ category_arg.arg_type = eArgTypeLogCategory;
+ category_arg.arg_repetition = eArgRepeatPlus;
+
+ arg2.push_back(category_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectLogEnable() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), log_file(), log_options(0) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ log_file.SetFile(option_arg, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(log_file);
+ break;
+ case 't':
+ log_options |= LLDB_LOG_OPTION_THREADSAFE;
+ break;
+ case 'v':
+ log_options |= LLDB_LOG_OPTION_VERBOSE;
+ break;
+ case 's':
+ log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE;
+ break;
+ case 'T':
+ log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP;
+ break;
+ case 'p':
+ log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD;
+ break;
+ case 'n':
+ log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
+ break;
+ case 'S':
+ log_options |= LLDB_LOG_OPTION_BACKTRACE;
+ break;
+ case 'a':
+ log_options |= LLDB_LOG_OPTION_APPEND;
+ break;
+ case 'F':
+ log_options |= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ log_file.Clear();
+ log_options = 0;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_log_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ FileSpec log_file;
+ uint32_t log_options;
+ };
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() < 2) {
+ result.AppendErrorWithFormat(
+ "%s takes a log channel and one or more log types.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ // Store into a std::string since we're about to shift the channel off.
+ const std::string channel = args[0].ref;
+ args.Shift(); // Shift off the channel
+ char log_file[PATH_MAX];
+ if (m_options.log_file)
+ m_options.log_file.GetPath(log_file, sizeof(log_file));
+ else
+ log_file[0] = '\0';
+
+ std::string error;
+ llvm::raw_string_ostream error_stream(error);
+ bool success =
+ GetDebugger().EnableLog(channel, args.GetArgumentArrayRef(), log_file,
+ m_options.log_options, error_stream);
+ result.GetErrorStream() << error_stream.str();
+
+ if (success)
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+class CommandObjectLogDisable : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log disable",
+ "Disable one or more log channel categories.",
+ nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData channel_arg;
+ CommandArgumentData category_arg;
+
+ // Define the first (and only) variant of this arg.
+ channel_arg.arg_type = eArgTypeLogChannel;
+ channel_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(channel_arg);
+
+ category_arg.arg_type = eArgTypeLogCategory;
+ category_arg.arg_repetition = eArgRepeatPlus;
+
+ arg2.push_back(category_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectLogDisable() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.empty()) {
+ result.AppendErrorWithFormat(
+ "%s takes a log channel and one or more log types.\n",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ const std::string channel = args[0].ref;
+ args.Shift(); // Shift off the channel
+ if (channel == "all") {
+ Log::DisableAllLogChannels();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ std::string error;
+ llvm::raw_string_ostream error_stream(error);
+ if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(),
+ error_stream))
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ result.GetErrorStream() << error_stream.str();
+ }
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectLogList : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log list",
+ "List the log categories for one or more log "
+ "channels. If none specified, lists them all.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData channel_arg;
+
+ // Define the first (and only) variant of this arg.
+ channel_arg.arg_type = eArgTypeLogChannel;
+ channel_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(channel_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectLogList() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ std::string output;
+ llvm::raw_string_ostream output_stream(output);
+ if (args.empty()) {
+ Log::ListAllLogChannels(output_stream);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ bool success = true;
+ for (const auto &entry : args.entries())
+ success =
+ success && Log::ListChannelCategories(entry.ref, output_stream);
+ if (success)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ result.GetOutputStream() << output_stream.str();
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectLogTimer : public CommandObjectParsed {
+public:
+ // Constructors and Destructors
+ CommandObjectLogTimer(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "log timers",
+ "Enable, disable, dump, and reset LLDB internal "
+ "performance timers.",
+ "log timers < enable <depth> | disable | dump | "
+ "increment <bool> | reset >") {}
+
+ ~CommandObjectLogTimer() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusFailed);
+
+ if (args.GetArgumentCount() == 1) {
+ auto sub_command = args[0].ref;
+
+ if (sub_command.equals_lower("enable")) {
+ Timer::SetDisplayDepth(UINT32_MAX);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else if (sub_command.equals_lower("disable")) {
+ Timer::DumpCategoryTimes(&result.GetOutputStream());
+ Timer::SetDisplayDepth(0);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else if (sub_command.equals_lower("dump")) {
+ Timer::DumpCategoryTimes(&result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else if (sub_command.equals_lower("reset")) {
+ Timer::ResetCategoryTimes();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ } else if (args.GetArgumentCount() == 2) {
+ auto sub_command = args[0].ref;
+ auto param = args[1].ref;
+
+ if (sub_command.equals_lower("enable")) {
+ uint32_t depth;
+ if (param.consumeInteger(0, depth)) {
+ result.AppendError(
+ "Could not convert enable depth to an unsigned integer.");
+ } else {
+ Timer::SetDisplayDepth(depth);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ } else if (sub_command.equals_lower("increment")) {
+ bool success;
+ bool increment = OptionArgParser::ToBoolean(param, false, &success);
+ if (success) {
+ Timer::SetQuiet(!increment);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else
+ result.AppendError("Could not convert increment value to boolean.");
+ }
+ }
+
+ if (!result.Succeeded()) {
+ result.AppendError("Missing subcommand");
+ result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
+ }
+ return result.Succeeded();
+ }
+};
+
+CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "log",
+ "Commands controlling LLDB internal logging.",
+ "log <subcommand> [<command-options>]") {
+ LoadSubCommand("enable",
+ CommandObjectSP(new CommandObjectLogEnable(interpreter)));
+ LoadSubCommand("disable",
+ CommandObjectSP(new CommandObjectLogDisable(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectLogList(interpreter)));
+ LoadSubCommand("timers",
+ CommandObjectSP(new CommandObjectLogTimer(interpreter)));
+}
+
+CommandObjectLog::~CommandObjectLog() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.h
new file mode 100644
index 000000000000..b2da900e21ed
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.h
@@ -0,0 +1,35 @@
+//===-- CommandObjectLog.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectLog_h_
+#define liblldb_CommandObjectLog_h_
+
+#include <map>
+#include <string>
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectLog
+
+class CommandObjectLog : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectLog(CommandInterpreter &interpreter);
+
+ ~CommandObjectLog() override;
+
+private:
+ // For CommandObjectLog only
+ DISALLOW_COPY_AND_ASSIGN(CommandObjectLog);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectLog_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp
new file mode 100644
index 000000000000..1afcac71318d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp
@@ -0,0 +1,1810 @@
+//===-- CommandObjectMemory.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectMemory.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/DataFormatters/ValueObjectPrinter.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupOutputFile.h"
+#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
+#include "lldb/Interpreter/OptionValueLanguage.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/MemoryHistory.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataBufferLLVM.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "lldb/lldb-private.h"
+
+#include <cinttypes>
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static constexpr OptionDefinition g_read_memory_options[] = {
+ // clang-format off
+ {LLDB_OPT_SET_1, false, "num-per-line", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNumberPerLine, "The number of items per line to display." },
+ {LLDB_OPT_SET_2, false, "binary", 'b', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "If true, memory will be saved as binary. If false, the memory is saved save as an ASCII dump that "
+ "uses the format, size, count and number per line settings." },
+ {LLDB_OPT_SET_3 |
+ LLDB_OPT_SET_4, true , "type", 't', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeName, "The name of a type to view memory as." },
+ {LLDB_OPT_SET_4, false, "language", 'x', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLanguage, "The language of the type to view memory as."},
+ {LLDB_OPT_SET_3, false, "offset", 'E', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount, "How many elements of the specified type to skip before starting to display data." },
+ {LLDB_OPT_SET_1 |
+ LLDB_OPT_SET_2 |
+ LLDB_OPT_SET_3, false, "force", 'r', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Necessary if reading over target.max-memory-read-size bytes." },
+ // clang-format on
+};
+
+class OptionGroupReadMemory : public OptionGroup {
+public:
+ OptionGroupReadMemory()
+ : m_num_per_line(1, 1), m_output_as_binary(false), m_view_as_type(),
+ m_offset(0, 0), m_language_for_type(eLanguageTypeUnknown) {}
+
+ ~OptionGroupReadMemory() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_read_memory_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = g_read_memory_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'l':
+ error = m_num_per_line.SetValueFromString(option_value);
+ if (m_num_per_line.GetCurrentValue() == 0)
+ error.SetErrorStringWithFormat(
+ "invalid value for --num-per-line option '%s'",
+ option_value.str().c_str());
+ break;
+
+ case 'b':
+ m_output_as_binary = true;
+ break;
+
+ case 't':
+ error = m_view_as_type.SetValueFromString(option_value);
+ break;
+
+ case 'r':
+ m_force = true;
+ break;
+
+ case 'x':
+ error = m_language_for_type.SetValueFromString(option_value);
+ break;
+
+ case 'E':
+ error = m_offset.SetValueFromString(option_value);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_num_per_line.Clear();
+ m_output_as_binary = false;
+ m_view_as_type.Clear();
+ m_force = false;
+ m_offset.Clear();
+ m_language_for_type.Clear();
+ }
+
+ Status FinalizeSettings(Target *target, OptionGroupFormat &format_options) {
+ Status error;
+ OptionValueUInt64 &byte_size_value = format_options.GetByteSizeValue();
+ OptionValueUInt64 &count_value = format_options.GetCountValue();
+ const bool byte_size_option_set = byte_size_value.OptionWasSet();
+ const bool num_per_line_option_set = m_num_per_line.OptionWasSet();
+ const bool count_option_set = format_options.GetCountValue().OptionWasSet();
+
+ switch (format_options.GetFormat()) {
+ default:
+ break;
+
+ case eFormatBoolean:
+ if (!byte_size_option_set)
+ byte_size_value = 1;
+ if (!num_per_line_option_set)
+ m_num_per_line = 1;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatCString:
+ break;
+
+ case eFormatInstruction:
+ if (count_option_set)
+ byte_size_value = target->GetArchitecture().GetMaximumOpcodeByteSize();
+ m_num_per_line = 1;
+ break;
+
+ case eFormatAddressInfo:
+ if (!byte_size_option_set)
+ byte_size_value = target->GetArchitecture().GetAddressByteSize();
+ m_num_per_line = 1;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatPointer:
+ byte_size_value = target->GetArchitecture().GetAddressByteSize();
+ if (!num_per_line_option_set)
+ m_num_per_line = 4;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatBinary:
+ case eFormatFloat:
+ case eFormatOctal:
+ case eFormatDecimal:
+ case eFormatEnum:
+ case eFormatUnicode16:
+ case eFormatUnicode32:
+ case eFormatUnsigned:
+ case eFormatHexFloat:
+ if (!byte_size_option_set)
+ byte_size_value = 4;
+ if (!num_per_line_option_set)
+ m_num_per_line = 1;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatBytes:
+ case eFormatBytesWithASCII:
+ if (byte_size_option_set) {
+ if (byte_size_value > 1)
+ error.SetErrorStringWithFormat(
+ "display format (bytes/bytes with ASCII) conflicts with the "
+ "specified byte size %" PRIu64 "\n"
+ "\tconsider using a different display format or don't specify "
+ "the byte size.",
+ byte_size_value.GetCurrentValue());
+ } else
+ byte_size_value = 1;
+ if (!num_per_line_option_set)
+ m_num_per_line = 16;
+ if (!count_option_set)
+ format_options.GetCountValue() = 32;
+ break;
+
+ case eFormatCharArray:
+ case eFormatChar:
+ case eFormatCharPrintable:
+ if (!byte_size_option_set)
+ byte_size_value = 1;
+ if (!num_per_line_option_set)
+ m_num_per_line = 32;
+ if (!count_option_set)
+ format_options.GetCountValue() = 64;
+ break;
+
+ case eFormatComplex:
+ if (!byte_size_option_set)
+ byte_size_value = 8;
+ if (!num_per_line_option_set)
+ m_num_per_line = 1;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatComplexInteger:
+ if (!byte_size_option_set)
+ byte_size_value = 8;
+ if (!num_per_line_option_set)
+ m_num_per_line = 1;
+ if (!count_option_set)
+ format_options.GetCountValue() = 8;
+ break;
+
+ case eFormatHex:
+ if (!byte_size_option_set)
+ byte_size_value = 4;
+ if (!num_per_line_option_set) {
+ switch (byte_size_value) {
+ case 1:
+ case 2:
+ m_num_per_line = 8;
+ break;
+ case 4:
+ m_num_per_line = 4;
+ break;
+ case 8:
+ m_num_per_line = 2;
+ break;
+ default:
+ m_num_per_line = 1;
+ break;
+ }
+ }
+ if (!count_option_set)
+ count_value = 8;
+ break;
+
+ case eFormatVectorOfChar:
+ case eFormatVectorOfSInt8:
+ case eFormatVectorOfUInt8:
+ case eFormatVectorOfSInt16:
+ case eFormatVectorOfUInt16:
+ case eFormatVectorOfSInt32:
+ case eFormatVectorOfUInt32:
+ case eFormatVectorOfSInt64:
+ case eFormatVectorOfUInt64:
+ case eFormatVectorOfFloat16:
+ case eFormatVectorOfFloat32:
+ case eFormatVectorOfFloat64:
+ case eFormatVectorOfUInt128:
+ if (!byte_size_option_set)
+ byte_size_value = 128;
+ if (!num_per_line_option_set)
+ m_num_per_line = 1;
+ if (!count_option_set)
+ count_value = 4;
+ break;
+ }
+ return error;
+ }
+
+ bool AnyOptionWasSet() const {
+ return m_num_per_line.OptionWasSet() || m_output_as_binary ||
+ m_view_as_type.OptionWasSet() || m_offset.OptionWasSet() ||
+ m_language_for_type.OptionWasSet();
+ }
+
+ OptionValueUInt64 m_num_per_line;
+ bool m_output_as_binary;
+ OptionValueString m_view_as_type;
+ bool m_force;
+ OptionValueUInt64 m_offset;
+ OptionValueLanguage m_language_for_type;
+};
+
+// Read memory from the inferior process
+class CommandObjectMemoryRead : public CommandObjectParsed {
+public:
+ CommandObjectMemoryRead(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "memory read",
+ "Read from the memory of the current target process.", nullptr,
+ eCommandRequiresTarget | eCommandProcessMustBePaused),
+ m_option_group(), m_format_options(eFormatBytesWithASCII, 1, 8),
+ m_memory_options(), m_outfile_options(), m_varobj_options(),
+ m_next_addr(LLDB_INVALID_ADDRESS), m_prev_byte_size(0),
+ m_prev_format_options(eFormatBytesWithASCII, 1, 8),
+ m_prev_memory_options(), m_prev_outfile_options(),
+ m_prev_varobj_options() {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData start_addr_arg;
+ CommandArgumentData end_addr_arg;
+
+ // Define the first (and only) variant of this arg.
+ start_addr_arg.arg_type = eArgTypeAddressOrExpression;
+ start_addr_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(start_addr_arg);
+
+ // Define the first (and only) variant of this arg.
+ end_addr_arg.arg_type = eArgTypeAddressOrExpression;
+ end_addr_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(end_addr_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+
+ // Add the "--format" and "--count" options to group 1 and 3
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_FORMAT |
+ OptionGroupFormat::OPTION_GROUP_COUNT,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3);
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_GDB_FMT,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_3);
+ // Add the "--size" option to group 1 and 2
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_SIZE,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
+ m_option_group.Append(&m_memory_options);
+ m_option_group.Append(&m_outfile_options, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3);
+ m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectMemoryRead() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ const char *GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ return m_cmd_name.c_str();
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // No need to check "target" for validity as eCommandRequiresTarget ensures
+ // it is valid
+ Target *target = m_exe_ctx.GetTargetPtr();
+
+ const size_t argc = command.GetArgumentCount();
+
+ if ((argc == 0 && m_next_addr == LLDB_INVALID_ADDRESS) || argc > 2) {
+ result.AppendErrorWithFormat("%s takes a start address expression with "
+ "an optional end address expression.\n",
+ m_cmd_name.c_str());
+ result.AppendRawWarning("Expressions should be quoted if they contain "
+ "spaces or other special characters.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ CompilerType compiler_type;
+ Status error;
+
+ const char *view_as_type_cstr =
+ m_memory_options.m_view_as_type.GetCurrentValue();
+ if (view_as_type_cstr && view_as_type_cstr[0]) {
+ // We are viewing memory as a type
+
+ const bool exact_match = false;
+ TypeList type_list;
+ uint32_t reference_count = 0;
+ uint32_t pointer_count = 0;
+ size_t idx;
+
+#define ALL_KEYWORDS \
+ KEYWORD("const") \
+ KEYWORD("volatile") \
+ KEYWORD("restrict") \
+ KEYWORD("struct") \
+ KEYWORD("class") \
+ KEYWORD("union")
+
+#define KEYWORD(s) s,
+ static const char *g_keywords[] = {ALL_KEYWORDS};
+#undef KEYWORD
+
+#define KEYWORD(s) (sizeof(s) - 1),
+ static const int g_keyword_lengths[] = {ALL_KEYWORDS};
+#undef KEYWORD
+
+#undef ALL_KEYWORDS
+
+ static size_t g_num_keywords = sizeof(g_keywords) / sizeof(const char *);
+ std::string type_str(view_as_type_cstr);
+
+ // Remove all instances of g_keywords that are followed by spaces
+ for (size_t i = 0; i < g_num_keywords; ++i) {
+ const char *keyword = g_keywords[i];
+ int keyword_len = g_keyword_lengths[i];
+
+ idx = 0;
+ while ((idx = type_str.find(keyword, idx)) != std::string::npos) {
+ if (type_str[idx + keyword_len] == ' ' ||
+ type_str[idx + keyword_len] == '\t') {
+ type_str.erase(idx, keyword_len + 1);
+ idx = 0;
+ } else {
+ idx += keyword_len;
+ }
+ }
+ }
+ bool done = type_str.empty();
+ //
+ idx = type_str.find_first_not_of(" \t");
+ if (idx > 0 && idx != std::string::npos)
+ type_str.erase(0, idx);
+ while (!done) {
+ // Strip trailing spaces
+ if (type_str.empty())
+ done = true;
+ else {
+ switch (type_str[type_str.size() - 1]) {
+ case '*':
+ ++pointer_count;
+ LLVM_FALLTHROUGH;
+ case ' ':
+ case '\t':
+ type_str.erase(type_str.size() - 1);
+ break;
+
+ case '&':
+ if (reference_count == 0) {
+ reference_count = 1;
+ type_str.erase(type_str.size() - 1);
+ } else {
+ result.AppendErrorWithFormat("invalid type string: '%s'\n",
+ view_as_type_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ break;
+
+ default:
+ done = true;
+ break;
+ }
+ }
+ }
+
+ llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
+ ConstString lookup_type_name(type_str.c_str());
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ ModuleSP search_first;
+ if (frame) {
+ search_first = frame->GetSymbolContext(eSymbolContextModule).module_sp;
+ }
+ target->GetImages().FindTypes(search_first.get(), lookup_type_name,
+ exact_match, 1, searched_symbol_files,
+ type_list);
+
+ if (type_list.GetSize() == 0 && lookup_type_name.GetCString()) {
+ LanguageType language_for_type =
+ m_memory_options.m_language_for_type.GetCurrentValue();
+ std::set<LanguageType> languages_to_check;
+ if (language_for_type != eLanguageTypeUnknown) {
+ languages_to_check.insert(language_for_type);
+ } else {
+ languages_to_check = Language::GetSupportedLanguages();
+ }
+
+ std::set<CompilerType> user_defined_types;
+ for (auto lang : languages_to_check) {
+ if (auto *persistent_vars =
+ target->GetPersistentExpressionStateForLanguage(lang)) {
+ if (llvm::Optional<CompilerType> type =
+ persistent_vars->GetCompilerTypeFromPersistentDecl(
+ lookup_type_name)) {
+ user_defined_types.emplace(*type);
+ }
+ }
+ }
+
+ if (user_defined_types.size() > 1) {
+ result.AppendErrorWithFormat(
+ "Mutiple types found matching raw type '%s', please disambiguate "
+ "by specifying the language with -x",
+ lookup_type_name.GetCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (user_defined_types.size() == 1) {
+ compiler_type = *user_defined_types.begin();
+ }
+ }
+
+ if (!compiler_type.IsValid()) {
+ if (type_list.GetSize() == 0) {
+ result.AppendErrorWithFormat("unable to find any types that match "
+ "the raw type '%s' for full type '%s'\n",
+ lookup_type_name.GetCString(),
+ view_as_type_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ TypeSP type_sp(type_list.GetTypeAtIndex(0));
+ compiler_type = type_sp->GetFullCompilerType();
+ }
+ }
+
+ while (pointer_count > 0) {
+ CompilerType pointer_type = compiler_type.GetPointerType();
+ if (pointer_type.IsValid())
+ compiler_type = pointer_type;
+ else {
+ result.AppendError("unable make a pointer type\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ --pointer_count;
+ }
+
+ llvm::Optional<uint64_t> size = compiler_type.GetByteSize(nullptr);
+ if (!size) {
+ result.AppendErrorWithFormat(
+ "unable to get the byte size of the type '%s'\n",
+ view_as_type_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ m_format_options.GetByteSizeValue() = *size;
+
+ if (!m_format_options.GetCountValue().OptionWasSet())
+ m_format_options.GetCountValue() = 1;
+ } else {
+ error = m_memory_options.FinalizeSettings(target, m_format_options);
+ }
+
+ // Look for invalid combinations of settings
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ lldb::addr_t addr;
+ size_t total_byte_size = 0;
+ if (argc == 0) {
+ // Use the last address and byte size and all options as they were if no
+ // options have been set
+ addr = m_next_addr;
+ total_byte_size = m_prev_byte_size;
+ compiler_type = m_prev_compiler_type;
+ if (!m_format_options.AnyOptionWasSet() &&
+ !m_memory_options.AnyOptionWasSet() &&
+ !m_outfile_options.AnyOptionWasSet() &&
+ !m_varobj_options.AnyOptionWasSet()) {
+ m_format_options = m_prev_format_options;
+ m_memory_options = m_prev_memory_options;
+ m_outfile_options = m_prev_outfile_options;
+ m_varobj_options = m_prev_varobj_options;
+ }
+ }
+
+ size_t item_count = m_format_options.GetCountValue().GetCurrentValue();
+
+ // TODO For non-8-bit byte addressable architectures this needs to be
+ // revisited to fully support all lldb's range of formatting options.
+ // Furthermore code memory reads (for those architectures) will not be
+ // correctly formatted even w/o formatting options.
+ size_t item_byte_size =
+ target->GetArchitecture().GetDataByteSize() > 1
+ ? target->GetArchitecture().GetDataByteSize()
+ : m_format_options.GetByteSizeValue().GetCurrentValue();
+
+ const size_t num_per_line =
+ m_memory_options.m_num_per_line.GetCurrentValue();
+
+ if (total_byte_size == 0) {
+ total_byte_size = item_count * item_byte_size;
+ if (total_byte_size == 0)
+ total_byte_size = 32;
+ }
+
+ if (argc > 0)
+ addr = OptionArgParser::ToAddress(&m_exe_ctx, command[0].ref,
+ LLDB_INVALID_ADDRESS, &error);
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ result.AppendError("invalid start address expression.");
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (argc == 2) {
+ lldb::addr_t end_addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[1].ref, LLDB_INVALID_ADDRESS, nullptr);
+ if (end_addr == LLDB_INVALID_ADDRESS) {
+ result.AppendError("invalid end address expression.");
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (end_addr <= addr) {
+ result.AppendErrorWithFormat(
+ "end address (0x%" PRIx64
+ ") must be greater that the start address (0x%" PRIx64 ").\n",
+ end_addr, addr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (m_format_options.GetCountValue().OptionWasSet()) {
+ result.AppendErrorWithFormat(
+ "specify either the end address (0x%" PRIx64
+ ") or the count (--count %" PRIu64 "), not both.\n",
+ end_addr, (uint64_t)item_count);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ total_byte_size = end_addr - addr;
+ item_count = total_byte_size / item_byte_size;
+ }
+
+ uint32_t max_unforced_size = target->GetMaximumMemReadSize();
+
+ if (total_byte_size > max_unforced_size && !m_memory_options.m_force) {
+ result.AppendErrorWithFormat(
+ "Normally, \'memory read\' will not read over %" PRIu32
+ " bytes of data.\n",
+ max_unforced_size);
+ result.AppendErrorWithFormat(
+ "Please use --force to override this restriction just once.\n");
+ result.AppendErrorWithFormat("or set target.max-memory-read-size if you "
+ "will often need a larger limit.\n");
+ return false;
+ }
+
+ DataBufferSP data_sp;
+ size_t bytes_read = 0;
+ if (compiler_type.GetOpaqueQualType()) {
+ // Make sure we don't display our type as ASCII bytes like the default
+ // memory read
+ if (!m_format_options.GetFormatValue().OptionWasSet())
+ m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault);
+
+ llvm::Optional<uint64_t> size = compiler_type.GetByteSize(nullptr);
+ if (!size) {
+ result.AppendError("can't get size of type");
+ return false;
+ }
+ bytes_read = *size * m_format_options.GetCountValue().GetCurrentValue();
+
+ if (argc > 0)
+ addr = addr + (*size * m_memory_options.m_offset.GetCurrentValue());
+ } else if (m_format_options.GetFormatValue().GetCurrentValue() !=
+ eFormatCString) {
+ data_sp = std::make_shared<DataBufferHeap>(total_byte_size, '\0');
+ if (data_sp->GetBytes() == nullptr) {
+ result.AppendErrorWithFormat(
+ "can't allocate 0x%" PRIx32
+ " bytes for the memory read buffer, specify a smaller size to read",
+ (uint32_t)total_byte_size);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Address address(addr, nullptr);
+ bytes_read = target->ReadMemory(address, false, data_sp->GetBytes(),
+ data_sp->GetByteSize(), error);
+ if (bytes_read == 0) {
+ const char *error_cstr = error.AsCString();
+ if (error_cstr && error_cstr[0]) {
+ result.AppendError(error_cstr);
+ } else {
+ result.AppendErrorWithFormat(
+ "failed to read memory from 0x%" PRIx64 ".\n", addr);
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (bytes_read < total_byte_size)
+ result.AppendWarningWithFormat(
+ "Not all bytes (%" PRIu64 "/%" PRIu64
+ ") were able to be read from 0x%" PRIx64 ".\n",
+ (uint64_t)bytes_read, (uint64_t)total_byte_size, addr);
+ } else {
+ // we treat c-strings as a special case because they do not have a fixed
+ // size
+ if (m_format_options.GetByteSizeValue().OptionWasSet() &&
+ !m_format_options.HasGDBFormat())
+ item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue();
+ else
+ item_byte_size = target->GetMaximumSizeOfStringSummary();
+ if (!m_format_options.GetCountValue().OptionWasSet())
+ item_count = 1;
+ data_sp = std::make_shared<DataBufferHeap>(
+ (item_byte_size + 1) * item_count,
+ '\0'); // account for NULLs as necessary
+ if (data_sp->GetBytes() == nullptr) {
+ result.AppendErrorWithFormat(
+ "can't allocate 0x%" PRIx64
+ " bytes for the memory read buffer, specify a smaller size to read",
+ (uint64_t)((item_byte_size + 1) * item_count));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ uint8_t *data_ptr = data_sp->GetBytes();
+ auto data_addr = addr;
+ auto count = item_count;
+ item_count = 0;
+ bool break_on_no_NULL = false;
+ while (item_count < count) {
+ std::string buffer;
+ buffer.resize(item_byte_size + 1, 0);
+ Status error;
+ size_t read = target->ReadCStringFromMemory(data_addr, &buffer[0],
+ item_byte_size + 1, error);
+ if (error.Fail()) {
+ result.AppendErrorWithFormat(
+ "failed to read memory from 0x%" PRIx64 ".\n", addr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (item_byte_size == read) {
+ result.AppendWarningWithFormat(
+ "unable to find a NULL terminated string at 0x%" PRIx64
+ ".Consider increasing the maximum read length.\n",
+ data_addr);
+ --read;
+ break_on_no_NULL = true;
+ } else
+ ++read; // account for final NULL byte
+
+ memcpy(data_ptr, &buffer[0], read);
+ data_ptr += read;
+ data_addr += read;
+ bytes_read += read;
+ item_count++; // if we break early we know we only read item_count
+ // strings
+
+ if (break_on_no_NULL)
+ break;
+ }
+ data_sp =
+ std::make_shared<DataBufferHeap>(data_sp->GetBytes(), bytes_read + 1);
+ }
+
+ m_next_addr = addr + bytes_read;
+ m_prev_byte_size = bytes_read;
+ m_prev_format_options = m_format_options;
+ m_prev_memory_options = m_memory_options;
+ m_prev_outfile_options = m_outfile_options;
+ m_prev_varobj_options = m_varobj_options;
+ m_prev_compiler_type = compiler_type;
+
+ StreamFile outfile_stream;
+ Stream *output_stream = nullptr;
+ const FileSpec &outfile_spec =
+ m_outfile_options.GetFile().GetCurrentValue();
+
+ std::string path = outfile_spec.GetPath();
+ if (outfile_spec) {
+
+ uint32_t open_options =
+ File::eOpenOptionWrite | File::eOpenOptionCanCreate;
+ const bool append = m_outfile_options.GetAppend().GetCurrentValue();
+ if (append)
+ open_options |= File::eOpenOptionAppend;
+
+ Status error = FileSystem::Instance().Open(outfile_stream.GetFile(),
+ outfile_spec, open_options);
+ if (error.Success()) {
+ if (m_memory_options.m_output_as_binary) {
+ const size_t bytes_written =
+ outfile_stream.Write(data_sp->GetBytes(), bytes_read);
+ if (bytes_written > 0) {
+ result.GetOutputStream().Printf(
+ "%zi bytes %s to '%s'\n", bytes_written,
+ append ? "appended" : "written", path.c_str());
+ return true;
+ } else {
+ result.AppendErrorWithFormat("Failed to write %" PRIu64
+ " bytes to '%s'.\n",
+ (uint64_t)bytes_read, path.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ // We are going to write ASCII to the file just point the
+ // output_stream to our outfile_stream...
+ output_stream = &outfile_stream;
+ }
+ } else {
+ result.AppendErrorWithFormat("Failed to open file '%s' for %s.\n",
+ path.c_str(), append ? "append" : "write");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ output_stream = &result.GetOutputStream();
+ }
+
+ ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
+ if (compiler_type.GetOpaqueQualType()) {
+ for (uint32_t i = 0; i < item_count; ++i) {
+ addr_t item_addr = addr + (i * item_byte_size);
+ Address address(item_addr);
+ StreamString name_strm;
+ name_strm.Printf("0x%" PRIx64, item_addr);
+ ValueObjectSP valobj_sp(ValueObjectMemory::Create(
+ exe_scope, name_strm.GetString(), address, compiler_type));
+ if (valobj_sp) {
+ Format format = m_format_options.GetFormat();
+ if (format != eFormatDefault)
+ valobj_sp->SetFormat(format);
+
+ DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
+ eLanguageRuntimeDescriptionDisplayVerbosityFull, format));
+
+ valobj_sp->Dump(*output_stream, options);
+ } else {
+ result.AppendErrorWithFormat(
+ "failed to create a value object for: (%s) %s\n",
+ view_as_type_cstr, name_strm.GetData());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ DataExtractor data(data_sp, target->GetArchitecture().GetByteOrder(),
+ target->GetArchitecture().GetAddressByteSize(),
+ target->GetArchitecture().GetDataByteSize());
+
+ Format format = m_format_options.GetFormat();
+ if (((format == eFormatChar) || (format == eFormatCharPrintable)) &&
+ (item_byte_size != 1)) {
+ // if a count was not passed, or it is 1
+ if (!m_format_options.GetCountValue().OptionWasSet() || item_count == 1) {
+ // this turns requests such as
+ // memory read -fc -s10 -c1 *charPtrPtr
+ // which make no sense (what is a char of size 10?) into a request for
+ // fetching 10 chars of size 1 from the same memory location
+ format = eFormatCharArray;
+ item_count = item_byte_size;
+ item_byte_size = 1;
+ } else {
+ // here we passed a count, and it was not 1 so we have a byte_size and
+ // a count we could well multiply those, but instead let's just fail
+ result.AppendErrorWithFormat(
+ "reading memory as characters of size %" PRIu64 " is not supported",
+ (uint64_t)item_byte_size);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ assert(output_stream);
+ size_t bytes_dumped = DumpDataExtractor(
+ data, output_stream, 0, format, item_byte_size, item_count,
+ num_per_line / target->GetArchitecture().GetDataByteSize(), addr, 0, 0,
+ exe_scope);
+ m_next_addr = addr + bytes_dumped;
+ output_stream->EOL();
+ return true;
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFormat m_format_options;
+ OptionGroupReadMemory m_memory_options;
+ OptionGroupOutputFile m_outfile_options;
+ OptionGroupValueObjectDisplay m_varobj_options;
+ lldb::addr_t m_next_addr;
+ lldb::addr_t m_prev_byte_size;
+ OptionGroupFormat m_prev_format_options;
+ OptionGroupReadMemory m_prev_memory_options;
+ OptionGroupOutputFile m_prev_outfile_options;
+ OptionGroupValueObjectDisplay m_prev_varobj_options;
+ CompilerType m_prev_compiler_type;
+};
+
+static constexpr OptionDefinition g_memory_find_option_table[] = {
+ // clang-format off
+ {LLDB_OPT_SET_1, true, "expression", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeExpression, "Evaluate an expression to obtain a byte pattern."},
+ {LLDB_OPT_SET_2, true, "string", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeName, "Use text to find a byte pattern."},
+ {LLDB_OPT_SET_ALL, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount, "How many times to perform the search."},
+ {LLDB_OPT_SET_ALL, false, "dump-offset", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOffset, "When dumping memory for a match, an offset from the match location to start dumping from."},
+ // clang-format on
+};
+
+// Find the specified data in memory
+class CommandObjectMemoryFind : public CommandObjectParsed {
+public:
+ class OptionGroupFindMemory : public OptionGroup {
+ public:
+ OptionGroupFindMemory() : OptionGroup(), m_count(1), m_offset(0) {}
+
+ ~OptionGroupFindMemory() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_memory_find_option_table);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option =
+ g_memory_find_option_table[option_idx].short_option;
+
+ switch (short_option) {
+ case 'e':
+ m_expr.SetValueFromString(option_value);
+ break;
+
+ case 's':
+ m_string.SetValueFromString(option_value);
+ break;
+
+ case 'c':
+ if (m_count.SetValueFromString(option_value).Fail())
+ error.SetErrorString("unrecognized value for count");
+ break;
+
+ case 'o':
+ if (m_offset.SetValueFromString(option_value).Fail())
+ error.SetErrorString("unrecognized value for dump-offset");
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_expr.Clear();
+ m_string.Clear();
+ m_count.Clear();
+ }
+
+ OptionValueString m_expr;
+ OptionValueString m_string;
+ OptionValueUInt64 m_count;
+ OptionValueUInt64 m_offset;
+ };
+
+ CommandObjectMemoryFind(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "memory find",
+ "Find a value in the memory of the current target process.",
+ nullptr, eCommandRequiresProcess | eCommandProcessMustBeLaunched),
+ m_option_group(), m_memory_options() {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData addr_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ addr_arg.arg_type = eArgTypeAddressOrExpression;
+ addr_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(addr_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeAddressOrExpression;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+
+ m_option_group.Append(&m_memory_options);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectMemoryFind() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ class ProcessMemoryIterator {
+ public:
+ ProcessMemoryIterator(ProcessSP process_sp, lldb::addr_t base)
+ : m_process_sp(process_sp), m_base_addr(base), m_is_valid(true) {
+ lldbassert(process_sp.get() != nullptr);
+ }
+
+ bool IsValid() { return m_is_valid; }
+
+ uint8_t operator[](lldb::addr_t offset) {
+ if (!IsValid())
+ return 0;
+
+ uint8_t retval = 0;
+ Status error;
+ if (0 ==
+ m_process_sp->ReadMemory(m_base_addr + offset, &retval, 1, error)) {
+ m_is_valid = false;
+ return 0;
+ }
+
+ return retval;
+ }
+
+ private:
+ ProcessSP m_process_sp;
+ lldb::addr_t m_base_addr;
+ bool m_is_valid;
+ };
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // No need to check "process" for validity as eCommandRequiresProcess
+ // ensures it is valid
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc != 2) {
+ result.AppendError("two addresses needed for memory find");
+ return false;
+ }
+
+ Status error;
+ lldb::addr_t low_addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[0].ref, LLDB_INVALID_ADDRESS, &error);
+ if (low_addr == LLDB_INVALID_ADDRESS || error.Fail()) {
+ result.AppendError("invalid low address");
+ return false;
+ }
+ lldb::addr_t high_addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[1].ref, LLDB_INVALID_ADDRESS, &error);
+ if (high_addr == LLDB_INVALID_ADDRESS || error.Fail()) {
+ result.AppendError("invalid high address");
+ return false;
+ }
+
+ if (high_addr <= low_addr) {
+ result.AppendError(
+ "starting address must be smaller than ending address");
+ return false;
+ }
+
+ lldb::addr_t found_location = LLDB_INVALID_ADDRESS;
+
+ DataBufferHeap buffer;
+
+ if (m_memory_options.m_string.OptionWasSet())
+ buffer.CopyData(m_memory_options.m_string.GetStringValue());
+ else if (m_memory_options.m_expr.OptionWasSet()) {
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ ValueObjectSP result_sp;
+ if ((eExpressionCompleted ==
+ process->GetTarget().EvaluateExpression(
+ m_memory_options.m_expr.GetStringValue(), frame, result_sp)) &&
+ result_sp) {
+ uint64_t value = result_sp->GetValueAsUnsigned(0);
+ llvm::Optional<uint64_t> size =
+ result_sp->GetCompilerType().GetByteSize(nullptr);
+ if (!size)
+ return false;
+ switch (*size) {
+ case 1: {
+ uint8_t byte = (uint8_t)value;
+ buffer.CopyData(&byte, 1);
+ } break;
+ case 2: {
+ uint16_t word = (uint16_t)value;
+ buffer.CopyData(&word, 2);
+ } break;
+ case 4: {
+ uint32_t lword = (uint32_t)value;
+ buffer.CopyData(&lword, 4);
+ } break;
+ case 8: {
+ buffer.CopyData(&value, 8);
+ } break;
+ case 3:
+ case 5:
+ case 6:
+ case 7:
+ result.AppendError("unknown type. pass a string instead");
+ return false;
+ default:
+ result.AppendError(
+ "result size larger than 8 bytes. pass a string instead");
+ return false;
+ }
+ } else {
+ result.AppendError(
+ "expression evaluation failed. pass a string instead");
+ return false;
+ }
+ } else {
+ result.AppendError(
+ "please pass either a block of text, or an expression to evaluate.");
+ return false;
+ }
+
+ size_t count = m_memory_options.m_count.GetCurrentValue();
+ found_location = low_addr;
+ bool ever_found = false;
+ while (count) {
+ found_location = FastSearch(found_location, high_addr, buffer.GetBytes(),
+ buffer.GetByteSize());
+ if (found_location == LLDB_INVALID_ADDRESS) {
+ if (!ever_found) {
+ result.AppendMessage("data not found within the range.\n");
+ result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
+ } else
+ result.AppendMessage("no more matches within the range.\n");
+ break;
+ }
+ result.AppendMessageWithFormat("data found at location: 0x%" PRIx64 "\n",
+ found_location);
+
+ DataBufferHeap dumpbuffer(32, 0);
+ process->ReadMemory(
+ found_location + m_memory_options.m_offset.GetCurrentValue(),
+ dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(), error);
+ if (!error.Fail()) {
+ DataExtractor data(dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(),
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+ DumpDataExtractor(
+ data, &result.GetOutputStream(), 0, lldb::eFormatBytesWithASCII, 1,
+ dumpbuffer.GetByteSize(), 16,
+ found_location + m_memory_options.m_offset.GetCurrentValue(), 0, 0);
+ result.GetOutputStream().EOL();
+ }
+
+ --count;
+ found_location++;
+ ever_found = true;
+ }
+
+ result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ lldb::addr_t FastSearch(lldb::addr_t low, lldb::addr_t high, uint8_t *buffer,
+ size_t buffer_size) {
+ const size_t region_size = high - low;
+
+ if (region_size < buffer_size)
+ return LLDB_INVALID_ADDRESS;
+
+ std::vector<size_t> bad_char_heuristic(256, buffer_size);
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+ ProcessMemoryIterator iterator(process_sp, low);
+
+ for (size_t idx = 0; idx < buffer_size - 1; idx++) {
+ decltype(bad_char_heuristic)::size_type bcu_idx = buffer[idx];
+ bad_char_heuristic[bcu_idx] = buffer_size - idx - 1;
+ }
+ for (size_t s = 0; s <= (region_size - buffer_size);) {
+ int64_t j = buffer_size - 1;
+ while (j >= 0 && buffer[j] == iterator[s + j])
+ j--;
+ if (j < 0)
+ return low + s;
+ else
+ s += bad_char_heuristic[iterator[s + buffer_size - 1]];
+ }
+
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFindMemory m_memory_options;
+};
+
+static constexpr OptionDefinition g_memory_write_option_table[] = {
+ // clang-format off
+ {LLDB_OPT_SET_1, true, "infile", 'i', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeFilename, "Write memory using the contents of a file."},
+ {LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOffset, "Start writing bytes from an offset within the input file."},
+ // clang-format on
+};
+
+// Write memory to the inferior process
+class CommandObjectMemoryWrite : public CommandObjectParsed {
+public:
+ class OptionGroupWriteMemory : public OptionGroup {
+ public:
+ OptionGroupWriteMemory() : OptionGroup() {}
+
+ ~OptionGroupWriteMemory() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_memory_write_option_table);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option =
+ g_memory_write_option_table[option_idx].short_option;
+
+ switch (short_option) {
+ case 'i':
+ m_infile.SetFile(option_value, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(m_infile);
+ if (!FileSystem::Instance().Exists(m_infile)) {
+ m_infile.Clear();
+ error.SetErrorStringWithFormat("input file does not exist: '%s'",
+ option_value.str().c_str());
+ }
+ break;
+
+ case 'o': {
+ if (option_value.getAsInteger(0, m_infile_offset)) {
+ m_infile_offset = 0;
+ error.SetErrorStringWithFormat("invalid offset string '%s'",
+ option_value.str().c_str());
+ }
+ } break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_infile.Clear();
+ m_infile_offset = 0;
+ }
+
+ FileSpec m_infile;
+ off_t m_infile_offset;
+ };
+
+ CommandObjectMemoryWrite(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "memory write",
+ "Write to the memory of the current target process.", nullptr,
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched),
+ m_option_group(), m_format_options(eFormatBytes, 1, UINT64_MAX),
+ m_memory_options() {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData addr_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ addr_arg.arg_type = eArgTypeAddress;
+ addr_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(addr_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_FORMAT,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_SIZE,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
+ m_option_group.Append(&m_memory_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectMemoryWrite() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ bool UIntValueIsValidForSize(uint64_t uval64, size_t total_byte_size) {
+ if (total_byte_size > 8)
+ return false;
+
+ if (total_byte_size == 8)
+ return true;
+
+ const uint64_t max = ((uint64_t)1 << (uint64_t)(total_byte_size * 8)) - 1;
+ return uval64 <= max;
+ }
+
+ bool SIntValueIsValidForSize(int64_t sval64, size_t total_byte_size) {
+ if (total_byte_size > 8)
+ return false;
+
+ if (total_byte_size == 8)
+ return true;
+
+ const int64_t max = ((int64_t)1 << (uint64_t)(total_byte_size * 8 - 1)) - 1;
+ const int64_t min = ~(max);
+ return min <= sval64 && sval64 <= max;
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // No need to check "process" for validity as eCommandRequiresProcess
+ // ensures it is valid
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ const size_t argc = command.GetArgumentCount();
+
+ if (m_memory_options.m_infile) {
+ if (argc < 1) {
+ result.AppendErrorWithFormat(
+ "%s takes a destination address when writing file contents.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else if (argc < 2) {
+ result.AppendErrorWithFormat(
+ "%s takes a destination address and at least one value.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ StreamString buffer(
+ Stream::eBinary,
+ process->GetTarget().GetArchitecture().GetAddressByteSize(),
+ process->GetTarget().GetArchitecture().GetByteOrder());
+
+ OptionValueUInt64 &byte_size_value = m_format_options.GetByteSizeValue();
+ size_t item_byte_size = byte_size_value.GetCurrentValue();
+
+ Status error;
+ lldb::addr_t addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[0].ref, LLDB_INVALID_ADDRESS, &error);
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ result.AppendError("invalid address expression\n");
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_memory_options.m_infile) {
+ size_t length = SIZE_MAX;
+ if (item_byte_size > 1)
+ length = item_byte_size;
+ auto data_sp = FileSystem::Instance().CreateDataBuffer(
+ m_memory_options.m_infile.GetPath(), length,
+ m_memory_options.m_infile_offset);
+ if (data_sp) {
+ length = data_sp->GetByteSize();
+ if (length > 0) {
+ Status error;
+ size_t bytes_written =
+ process->WriteMemory(addr, data_sp->GetBytes(), length, error);
+
+ if (bytes_written == length) {
+ // All bytes written
+ result.GetOutputStream().Printf(
+ "%" PRIu64 " bytes were written to 0x%" PRIx64 "\n",
+ (uint64_t)bytes_written, addr);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else if (bytes_written > 0) {
+ // Some byte written
+ result.GetOutputStream().Printf(
+ "%" PRIu64 " bytes of %" PRIu64
+ " requested were written to 0x%" PRIx64 "\n",
+ (uint64_t)bytes_written, (uint64_t)length, addr);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Memory write to 0x%" PRIx64
+ " failed: %s.\n",
+ addr, error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ } else {
+ result.AppendErrorWithFormat("Unable to read contents of file.\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ } else if (item_byte_size == 0) {
+ if (m_format_options.GetFormat() == eFormatPointer)
+ item_byte_size = buffer.GetAddressByteSize();
+ else
+ item_byte_size = 1;
+ }
+
+ command.Shift(); // shift off the address argument
+ uint64_t uval64;
+ int64_t sval64;
+ bool success = false;
+ for (auto &entry : command) {
+ switch (m_format_options.GetFormat()) {
+ case kNumFormats:
+ case eFormatFloat: // TODO: add support for floats soon
+ case eFormatCharPrintable:
+ case eFormatBytesWithASCII:
+ case eFormatComplex:
+ case eFormatEnum:
+ case eFormatUnicode16:
+ case eFormatUnicode32:
+ case eFormatVectorOfChar:
+ case eFormatVectorOfSInt8:
+ case eFormatVectorOfUInt8:
+ case eFormatVectorOfSInt16:
+ case eFormatVectorOfUInt16:
+ case eFormatVectorOfSInt32:
+ case eFormatVectorOfUInt32:
+ case eFormatVectorOfSInt64:
+ case eFormatVectorOfUInt64:
+ case eFormatVectorOfFloat16:
+ case eFormatVectorOfFloat32:
+ case eFormatVectorOfFloat64:
+ case eFormatVectorOfUInt128:
+ case eFormatOSType:
+ case eFormatComplexInteger:
+ case eFormatAddressInfo:
+ case eFormatHexFloat:
+ case eFormatInstruction:
+ case eFormatVoid:
+ result.AppendError("unsupported format for writing memory");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+
+ case eFormatDefault:
+ case eFormatBytes:
+ case eFormatHex:
+ case eFormatHexUppercase:
+ case eFormatPointer:
+ {
+ // Decode hex bytes
+ // Be careful, getAsInteger with a radix of 16 rejects "0xab" so we
+ // have to special case that:
+ bool success = false;
+ if (entry.ref.startswith("0x"))
+ success = !entry.ref.getAsInteger(0, uval64);
+ if (!success)
+ success = !entry.ref.getAsInteger(16, uval64);
+ if (!success) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid hex string value.\n", entry.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) {
+ result.AppendErrorWithFormat("Value 0x%" PRIx64
+ " is too large to fit in a %" PRIu64
+ " byte unsigned integer value.\n",
+ uval64, (uint64_t)item_byte_size);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ buffer.PutMaxHex64(uval64, item_byte_size);
+ break;
+ }
+ case eFormatBoolean:
+ uval64 = OptionArgParser::ToBoolean(entry.ref, false, &success);
+ if (!success) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid boolean string value.\n", entry.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ buffer.PutMaxHex64(uval64, item_byte_size);
+ break;
+
+ case eFormatBinary:
+ if (entry.ref.getAsInteger(2, uval64)) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid binary string value.\n", entry.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) {
+ result.AppendErrorWithFormat("Value 0x%" PRIx64
+ " is too large to fit in a %" PRIu64
+ " byte unsigned integer value.\n",
+ uval64, (uint64_t)item_byte_size);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ buffer.PutMaxHex64(uval64, item_byte_size);
+ break;
+
+ case eFormatCharArray:
+ case eFormatChar:
+ case eFormatCString: {
+ if (entry.ref.empty())
+ break;
+
+ size_t len = entry.ref.size();
+ // Include the NULL for C strings...
+ if (m_format_options.GetFormat() == eFormatCString)
+ ++len;
+ Status error;
+ if (process->WriteMemory(addr, entry.c_str(), len, error) == len) {
+ addr += len;
+ } else {
+ result.AppendErrorWithFormat("Memory write to 0x%" PRIx64
+ " failed: %s.\n",
+ addr, error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ break;
+ }
+ case eFormatDecimal:
+ if (entry.ref.getAsInteger(0, sval64)) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid signed decimal value.\n", entry.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (!SIntValueIsValidForSize(sval64, item_byte_size)) {
+ result.AppendErrorWithFormat(
+ "Value %" PRIi64 " is too large or small to fit in a %" PRIu64
+ " byte signed integer value.\n",
+ sval64, (uint64_t)item_byte_size);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ buffer.PutMaxHex64(sval64, item_byte_size);
+ break;
+
+ case eFormatUnsigned:
+
+ if (!entry.ref.getAsInteger(0, uval64)) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid unsigned decimal string value.\n",
+ entry.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) {
+ result.AppendErrorWithFormat("Value %" PRIu64
+ " is too large to fit in a %" PRIu64
+ " byte unsigned integer value.\n",
+ uval64, (uint64_t)item_byte_size);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ buffer.PutMaxHex64(uval64, item_byte_size);
+ break;
+
+ case eFormatOctal:
+ if (entry.ref.getAsInteger(8, uval64)) {
+ result.AppendErrorWithFormat(
+ "'%s' is not a valid octal string value.\n", entry.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) {
+ result.AppendErrorWithFormat("Value %" PRIo64
+ " is too large to fit in a %" PRIu64
+ " byte unsigned integer value.\n",
+ uval64, (uint64_t)item_byte_size);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ buffer.PutMaxHex64(uval64, item_byte_size);
+ break;
+ }
+ }
+
+ if (!buffer.GetString().empty()) {
+ Status error;
+ if (process->WriteMemory(addr, buffer.GetString().data(),
+ buffer.GetString().size(),
+ error) == buffer.GetString().size())
+ return true;
+ else {
+ result.AppendErrorWithFormat("Memory write to 0x%" PRIx64
+ " failed: %s.\n",
+ addr, error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFormat m_format_options;
+ OptionGroupWriteMemory m_memory_options;
+};
+
+// Get malloc/free history of a memory address.
+class CommandObjectMemoryHistory : public CommandObjectParsed {
+public:
+ CommandObjectMemoryHistory(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "memory history", "Print recorded stack traces for "
+ "allocation/deallocation events "
+ "associated with an address.",
+ nullptr,
+ eCommandRequiresTarget | eCommandRequiresProcess |
+ eCommandProcessMustBePaused | eCommandProcessMustBeLaunched) {
+ CommandArgumentEntry arg1;
+ CommandArgumentData addr_arg;
+
+ // Define the first (and only) variant of this arg.
+ addr_arg.arg_type = eArgTypeAddress;
+ addr_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(addr_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectMemoryHistory() override = default;
+
+ const char *GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ return m_cmd_name.c_str();
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc == 0 || argc > 1) {
+ result.AppendErrorWithFormat("%s takes an address expression",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Status error;
+ lldb::addr_t addr = OptionArgParser::ToAddress(
+ &m_exe_ctx, command[0].ref, LLDB_INVALID_ADDRESS, &error);
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ result.AppendError("invalid address expression");
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Stream *output_stream = &result.GetOutputStream();
+
+ const ProcessSP &process_sp = m_exe_ctx.GetProcessSP();
+ const MemoryHistorySP &memory_history =
+ MemoryHistory::FindPlugin(process_sp);
+
+ if (!memory_history) {
+ result.AppendError("no available memory history provider");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ HistoryThreads thread_list = memory_history->GetHistoryThreads(addr);
+
+ const bool stop_format = false;
+ for (auto thread : thread_list) {
+ thread->GetStatus(*output_stream, 0, UINT32_MAX, 0, stop_format);
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return true;
+ }
+};
+
+// CommandObjectMemoryRegion
+#pragma mark CommandObjectMemoryRegion
+
+class CommandObjectMemoryRegion : public CommandObjectParsed {
+public:
+ CommandObjectMemoryRegion(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "memory region",
+ "Get information on the memory region containing "
+ "an address in the current target process.",
+ "memory region ADDR",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched),
+ m_prev_end_addr(LLDB_INVALID_ADDRESS) {}
+
+ ~CommandObjectMemoryRegion() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+ if (process_sp) {
+ Status error;
+ lldb::addr_t load_addr = m_prev_end_addr;
+ m_prev_end_addr = LLDB_INVALID_ADDRESS;
+
+ const size_t argc = command.GetArgumentCount();
+ if (argc > 1 || (argc == 0 && load_addr == LLDB_INVALID_ADDRESS)) {
+ result.AppendErrorWithFormat("'%s' takes one argument:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ if (command.GetArgumentCount() == 1) {
+ auto load_addr_str = command[0].ref;
+ load_addr = OptionArgParser::ToAddress(&m_exe_ctx, load_addr_str,
+ LLDB_INVALID_ADDRESS, &error);
+ if (error.Fail() || load_addr == LLDB_INVALID_ADDRESS) {
+ result.AppendErrorWithFormat(
+ "invalid address argument \"%s\": %s\n", command[0].c_str(),
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+
+ lldb_private::MemoryRegionInfo range_info;
+ error = process_sp->GetMemoryRegionInfo(load_addr, range_info);
+ if (error.Success()) {
+ lldb_private::Address addr;
+ ConstString name = range_info.GetName();
+ ConstString section_name;
+ if (process_sp->GetTarget().ResolveLoadAddress(load_addr, addr)) {
+ SectionSP section_sp(addr.GetSection());
+ if (section_sp) {
+ // Got the top most section, not the deepest section
+ while (section_sp->GetParent())
+ section_sp = section_sp->GetParent();
+ section_name = section_sp->GetName();
+ }
+ }
+ result.AppendMessageWithFormat(
+ "[0x%16.16" PRIx64 "-0x%16.16" PRIx64 ") %c%c%c%s%s%s%s\n",
+ range_info.GetRange().GetRangeBase(),
+ range_info.GetRange().GetRangeEnd(),
+ range_info.GetReadable() ? 'r' : '-',
+ range_info.GetWritable() ? 'w' : '-',
+ range_info.GetExecutable() ? 'x' : '-',
+ name ? " " : "", name.AsCString(""),
+ section_name ? " " : "", section_name.AsCString(""));
+ m_prev_end_addr = range_info.GetRange().GetRangeEnd();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.SetStatus(eReturnStatusFailed);
+ result.AppendErrorWithFormat("%s\n", error.AsCString());
+ }
+ }
+ } else {
+ m_prev_end_addr = LLDB_INVALID_ADDRESS;
+ result.AppendError("invalid process");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ const char *GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ // If we repeat this command, repeat it without any arguments so we can
+ // show the next memory range
+ return m_cmd_name.c_str();
+ }
+
+ lldb::addr_t m_prev_end_addr;
+};
+
+// CommandObjectMemory
+
+CommandObjectMemory::CommandObjectMemory(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "memory",
+ "Commands for operating on memory in the current target process.",
+ "memory <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("find",
+ CommandObjectSP(new CommandObjectMemoryFind(interpreter)));
+ LoadSubCommand("read",
+ CommandObjectSP(new CommandObjectMemoryRead(interpreter)));
+ LoadSubCommand("write",
+ CommandObjectSP(new CommandObjectMemoryWrite(interpreter)));
+ LoadSubCommand("history",
+ CommandObjectSP(new CommandObjectMemoryHistory(interpreter)));
+ LoadSubCommand("region",
+ CommandObjectSP(new CommandObjectMemoryRegion(interpreter)));
+}
+
+CommandObjectMemory::~CommandObjectMemory() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.h
new file mode 100644
index 000000000000..f94cdf3287aa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectMemory.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectMemory_h_
+#define liblldb_CommandObjectMemory_h_
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectMemory : public CommandObjectMultiword {
+public:
+ CommandObjectMemory(CommandInterpreter &interpreter);
+
+ ~CommandObjectMemory() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectMemory_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp
new file mode 100644
index 000000000000..4011cceb8a26
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp
@@ -0,0 +1,394 @@
+//===-- CommandObjectMultiword.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/Options.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectMultiword
+
+CommandObjectMultiword::CommandObjectMultiword(CommandInterpreter &interpreter,
+ const char *name,
+ const char *help,
+ const char *syntax,
+ uint32_t flags)
+ : CommandObject(interpreter, name, help, syntax, flags),
+ m_can_be_removed(false) {}
+
+CommandObjectMultiword::~CommandObjectMultiword() = default;
+
+CommandObjectSP CommandObjectMultiword::GetSubcommandSP(llvm::StringRef sub_cmd,
+ StringList *matches) {
+ CommandObjectSP return_cmd_sp;
+ CommandObject::CommandMap::iterator pos;
+
+ if (!m_subcommand_dict.empty()) {
+ pos = m_subcommand_dict.find(sub_cmd);
+ if (pos != m_subcommand_dict.end()) {
+ // An exact match; append the sub_cmd to the 'matches' string list.
+ if (matches)
+ matches->AppendString(sub_cmd);
+ return_cmd_sp = pos->second;
+ } else {
+ StringList local_matches;
+ if (matches == nullptr)
+ matches = &local_matches;
+ int num_matches =
+ AddNamesMatchingPartialString(m_subcommand_dict, sub_cmd, *matches);
+
+ if (num_matches == 1) {
+ // Cleaner, but slightly less efficient would be to call back into this
+ // function, since I now know I have an exact match...
+
+ sub_cmd = matches->GetStringAtIndex(0);
+ pos = m_subcommand_dict.find(sub_cmd);
+ if (pos != m_subcommand_dict.end())
+ return_cmd_sp = pos->second;
+ }
+ }
+ }
+ return return_cmd_sp;
+}
+
+CommandObject *
+CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd,
+ StringList *matches) {
+ return GetSubcommandSP(sub_cmd, matches).get();
+}
+
+bool CommandObjectMultiword::LoadSubCommand(llvm::StringRef name,
+ const CommandObjectSP &cmd_obj) {
+ if (cmd_obj)
+ assert((&GetCommandInterpreter() == &cmd_obj->GetCommandInterpreter()) &&
+ "tried to add a CommandObject from a different interpreter");
+
+ CommandMap::iterator pos;
+ bool success = true;
+
+ pos = m_subcommand_dict.find(name);
+ if (pos == m_subcommand_dict.end()) {
+ m_subcommand_dict[name] = cmd_obj;
+ } else
+ success = false;
+
+ return success;
+}
+
+bool CommandObjectMultiword::Execute(const char *args_string,
+ CommandReturnObject &result) {
+ Args args(args_string);
+ const size_t argc = args.GetArgumentCount();
+ if (argc == 0) {
+ this->CommandObject::GenerateHelpText(result);
+ return result.Succeeded();
+ }
+
+ auto sub_command = args[0].ref;
+ if (sub_command.empty())
+ return result.Succeeded();
+
+ if (sub_command.equals_lower("help")) {
+ this->CommandObject::GenerateHelpText(result);
+ return result.Succeeded();
+ }
+
+ if (m_subcommand_dict.empty()) {
+ result.AppendErrorWithFormat("'%s' does not have any subcommands.\n",
+ GetCommandName().str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ StringList matches;
+ CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches);
+ if (sub_cmd_obj != nullptr) {
+ // Now call CommandObject::Execute to process options in `rest_of_line`.
+ // From there the command-specific version of Execute will be called, with
+ // the processed arguments.
+
+ args.Shift();
+ sub_cmd_obj->Execute(args_string, result);
+ return result.Succeeded();
+ }
+
+ std::string error_msg;
+ const size_t num_subcmd_matches = matches.GetSize();
+ if (num_subcmd_matches > 0)
+ error_msg.assign("ambiguous command ");
+ else
+ error_msg.assign("invalid command ");
+
+ error_msg.append("'");
+ error_msg.append(GetCommandName());
+ error_msg.append(" ");
+ error_msg.append(sub_command);
+ error_msg.append("'.");
+
+ if (num_subcmd_matches > 0) {
+ error_msg.append(" Possible completions:");
+ for (size_t i = 0; i < matches.GetSize(); i++) {
+ error_msg.append("\n\t");
+ error_msg.append(matches.GetStringAtIndex(i));
+ }
+ }
+ error_msg.append("\n");
+ result.AppendRawError(error_msg.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+}
+
+void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) {
+ // First time through here, generate the help text for the object and push it
+ // to the return result object as well
+
+ CommandObject::GenerateHelpText(output_stream);
+ output_stream.PutCString("\nThe following subcommands are supported:\n\n");
+
+ CommandMap::iterator pos;
+ uint32_t max_len = FindLongestCommandWord(m_subcommand_dict);
+
+ if (max_len)
+ max_len += 4; // Indent the output by 4 spaces.
+
+ for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {
+ std::string indented_command(" ");
+ indented_command.append(pos->first);
+ if (pos->second->WantsRawCommandString()) {
+ std::string help_text(pos->second->GetHelp());
+ help_text.append(" Expects 'raw' input (see 'help raw-input'.)");
+ m_interpreter.OutputFormattedHelpText(output_stream,
+ indented_command.c_str(), "--",
+ help_text.c_str(), max_len);
+ } else
+ m_interpreter.OutputFormattedHelpText(output_stream,
+ indented_command.c_str(), "--",
+ pos->second->GetHelp(), max_len);
+ }
+
+ output_stream.PutCString("\nFor more help on any particular subcommand, type "
+ "'help <command> <subcommand>'.\n");
+}
+
+int CommandObjectMultiword::HandleCompletion(CompletionRequest &request) {
+ // Any of the command matches will provide a complete word, otherwise the
+ // individual completers will override this.
+ request.SetWordComplete(true);
+
+ auto arg0 = request.GetParsedLine()[0].ref;
+ if (request.GetCursorIndex() == 0) {
+ StringList new_matches, descriptions;
+ AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches,
+ &descriptions);
+ request.AddCompletions(new_matches, descriptions);
+
+ if (new_matches.GetSize() == 1 &&
+ new_matches.GetStringAtIndex(0) != nullptr &&
+ (arg0 == new_matches.GetStringAtIndex(0))) {
+ StringList temp_matches;
+ CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches);
+ if (cmd_obj != nullptr) {
+ if (request.GetParsedLine().GetArgumentCount() == 1) {
+ request.SetWordComplete(true);
+ } else {
+ request.GetParsedLine().Shift();
+ request.SetCursorCharPosition(0);
+ request.GetParsedLine().AppendArgument(llvm::StringRef());
+ return cmd_obj->HandleCompletion(request);
+ }
+ }
+ }
+ return new_matches.GetSize();
+ } else {
+ StringList new_matches;
+ CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches);
+ if (sub_command_object == nullptr) {
+ request.AddCompletions(new_matches);
+ return request.GetNumberOfMatches();
+ } else {
+ // Remove the one match that we got from calling GetSubcommandObject.
+ new_matches.DeleteStringAtIndex(0);
+ request.AddCompletions(new_matches);
+ request.GetParsedLine().Shift();
+ request.SetCursorIndex(request.GetCursorIndex() - 1);
+ return sub_command_object->HandleCompletion(request);
+ }
+ }
+}
+
+const char *CommandObjectMultiword::GetRepeatCommand(Args &current_command_args,
+ uint32_t index) {
+ index++;
+ if (current_command_args.GetArgumentCount() <= index)
+ return nullptr;
+ CommandObject *sub_command_object =
+ GetSubcommandObject(current_command_args[index].ref);
+ if (sub_command_object == nullptr)
+ return nullptr;
+ return sub_command_object->GetRepeatCommand(current_command_args, index);
+}
+
+void CommandObjectMultiword::AproposAllSubCommands(llvm::StringRef prefix,
+ llvm::StringRef search_word,
+ StringList &commands_found,
+ StringList &commands_help) {
+ CommandObject::CommandMap::const_iterator pos;
+
+ for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {
+ const char *command_name = pos->first.c_str();
+ CommandObject *sub_cmd_obj = pos->second.get();
+ StreamString complete_command_name;
+
+ complete_command_name << prefix << " " << command_name;
+
+ if (sub_cmd_obj->HelpTextContainsWord(search_word)) {
+ commands_found.AppendString(complete_command_name.GetString());
+ commands_help.AppendString(sub_cmd_obj->GetHelp());
+ }
+
+ if (sub_cmd_obj->IsMultiwordObject())
+ sub_cmd_obj->AproposAllSubCommands(complete_command_name.GetString(),
+ search_word, commands_found,
+ commands_help);
+ }
+}
+
+CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter,
+ const char *name, const char *help,
+ const char *syntax, uint32_t flags)
+ : CommandObject(interpreter, name, help, syntax, flags) {}
+
+CommandObjectProxy::~CommandObjectProxy() = default;
+
+llvm::StringRef CommandObjectProxy::GetHelpLong() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetHelpLong();
+ return llvm::StringRef();
+}
+
+bool CommandObjectProxy::IsRemovable() const {
+ const CommandObject *proxy_command =
+ const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->IsRemovable();
+ return false;
+}
+
+bool CommandObjectProxy::IsMultiwordObject() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->IsMultiwordObject();
+ return false;
+}
+
+CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetAsMultiwordCommand();
+ return nullptr;
+}
+
+void CommandObjectProxy::GenerateHelpText(Stream &result) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GenerateHelpText(result);
+}
+
+lldb::CommandObjectSP
+CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd,
+ StringList *matches) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetSubcommandSP(sub_cmd, matches);
+ return lldb::CommandObjectSP();
+}
+
+CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd,
+ StringList *matches) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetSubcommandObject(sub_cmd, matches);
+ return nullptr;
+}
+
+void CommandObjectProxy::AproposAllSubCommands(llvm::StringRef prefix,
+ llvm::StringRef search_word,
+ StringList &commands_found,
+ StringList &commands_help) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->AproposAllSubCommands(prefix, search_word,
+ commands_found, commands_help);
+}
+
+bool CommandObjectProxy::LoadSubCommand(
+ llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->LoadSubCommand(cmd_name, command_sp);
+ return false;
+}
+
+bool CommandObjectProxy::WantsRawCommandString() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->WantsRawCommandString();
+ return false;
+}
+
+bool CommandObjectProxy::WantsCompletion() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->WantsCompletion();
+ return false;
+}
+
+Options *CommandObjectProxy::GetOptions() {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetOptions();
+ return nullptr;
+}
+
+int CommandObjectProxy::HandleCompletion(CompletionRequest &request) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->HandleCompletion(request);
+ return 0;
+}
+
+int CommandObjectProxy::HandleArgumentCompletion(
+ CompletionRequest &request, OptionElementVector &opt_element_vector) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->HandleArgumentCompletion(request, opt_element_vector);
+ return 0;
+}
+
+const char *CommandObjectProxy::GetRepeatCommand(Args &current_command_args,
+ uint32_t index) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->GetRepeatCommand(current_command_args, index);
+ return nullptr;
+}
+
+bool CommandObjectProxy::Execute(const char *args_string,
+ CommandReturnObject &result) {
+ CommandObject *proxy_command = GetProxyCommandObject();
+ if (proxy_command)
+ return proxy_command->Execute(args_string, result);
+ result.AppendError("command is not implemented");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.cpp
new file mode 100644
index 000000000000..53549cdeee32
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.cpp
@@ -0,0 +1,1822 @@
+//===-- CommandObjectPlatform.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <mutex>
+#include "CommandObjectPlatform.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandOptionValidators.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionGroupFile.h"
+#include "lldb/Interpreter/OptionGroupPlatform.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/DataExtractor.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static mode_t ParsePermissionString(const char *) = delete;
+
+static mode_t ParsePermissionString(llvm::StringRef permissions) {
+ if (permissions.size() != 9)
+ return (mode_t)(-1);
+ bool user_r, user_w, user_x, group_r, group_w, group_x, world_r, world_w,
+ world_x;
+
+ user_r = (permissions[0] == 'r');
+ user_w = (permissions[1] == 'w');
+ user_x = (permissions[2] == 'x');
+
+ group_r = (permissions[3] == 'r');
+ group_w = (permissions[4] == 'w');
+ group_x = (permissions[5] == 'x');
+
+ world_r = (permissions[6] == 'r');
+ world_w = (permissions[7] == 'w');
+ world_x = (permissions[8] == 'x');
+
+ mode_t user, group, world;
+ user = (user_r ? 4 : 0) | (user_w ? 2 : 0) | (user_x ? 1 : 0);
+ group = (group_r ? 4 : 0) | (group_w ? 2 : 0) | (group_x ? 1 : 0);
+ world = (world_r ? 4 : 0) | (world_w ? 2 : 0) | (world_x ? 1 : 0);
+
+ return user | group | world;
+}
+
+static constexpr OptionDefinition g_permissions_options[] = {
+ // clang-format off
+ {LLDB_OPT_SET_ALL, false, "permissions-value", 'v', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePermissionsNumber, "Give out the numeric value for permissions (e.g. 757)"},
+ {LLDB_OPT_SET_ALL, false, "permissions-string", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePermissionsString, "Give out the string value for permissions (e.g. rwxr-xr--)."},
+ {LLDB_OPT_SET_ALL, false, "user-read", 'r', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Allow user to read."},
+ {LLDB_OPT_SET_ALL, false, "user-write", 'w', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Allow user to write."},
+ {LLDB_OPT_SET_ALL, false, "user-exec", 'x', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Allow user to execute."},
+ {LLDB_OPT_SET_ALL, false, "group-read", 'R', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Allow group to read."},
+ {LLDB_OPT_SET_ALL, false, "group-write", 'W', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Allow group to write."},
+ {LLDB_OPT_SET_ALL, false, "group-exec", 'X', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Allow group to execute."},
+ {LLDB_OPT_SET_ALL, false, "world-read", 'd', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Allow world to read."},
+ {LLDB_OPT_SET_ALL, false, "world-write", 't', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Allow world to write."},
+ {LLDB_OPT_SET_ALL, false, "world-exec", 'e', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Allow world to execute."},
+ // clang-format on
+};
+
+class OptionPermissions : public OptionGroup {
+public:
+ OptionPermissions() {}
+
+ ~OptionPermissions() override = default;
+
+ lldb_private::Status
+ SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ char short_option = (char)GetDefinitions()[option_idx].short_option;
+ switch (short_option) {
+ case 'v': {
+ if (option_arg.getAsInteger(8, m_permissions)) {
+ m_permissions = 0777;
+ error.SetErrorStringWithFormat("invalid value for permissions: %s",
+ option_arg.str().c_str());
+ }
+
+ } break;
+ case 's': {
+ mode_t perms = ParsePermissionString(option_arg);
+ if (perms == (mode_t)-1)
+ error.SetErrorStringWithFormat("invalid value for permissions: %s",
+ option_arg.str().c_str());
+ else
+ m_permissions = perms;
+ } break;
+ case 'r':
+ m_permissions |= lldb::eFilePermissionsUserRead;
+ break;
+ case 'w':
+ m_permissions |= lldb::eFilePermissionsUserWrite;
+ break;
+ case 'x':
+ m_permissions |= lldb::eFilePermissionsUserExecute;
+ break;
+ case 'R':
+ m_permissions |= lldb::eFilePermissionsGroupRead;
+ break;
+ case 'W':
+ m_permissions |= lldb::eFilePermissionsGroupWrite;
+ break;
+ case 'X':
+ m_permissions |= lldb::eFilePermissionsGroupExecute;
+ break;
+ case 'd':
+ m_permissions |= lldb::eFilePermissionsWorldRead;
+ break;
+ case 't':
+ m_permissions |= lldb::eFilePermissionsWorldWrite;
+ break;
+ case 'e':
+ m_permissions |= lldb::eFilePermissionsWorldExecute;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_permissions = 0;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_permissions_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ uint32_t m_permissions;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(OptionPermissions);
+};
+
+// "platform select <platform-name>"
+class CommandObjectPlatformSelect : public CommandObjectParsed {
+public:
+ CommandObjectPlatformSelect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform select",
+ "Create a platform if needed and select it as the "
+ "current platform.",
+ "platform select <platform-name>", 0),
+ m_option_group(),
+ m_platform_options(
+ false) // Don't include the "--platform" option by passing false
+ {
+ m_option_group.Append(&m_platform_options, LLDB_OPT_SET_ALL, 1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectPlatformSelect() override = default;
+
+ int HandleCompletion(CompletionRequest &request) override {
+ CommandCompletions::PlatformPluginNames(GetCommandInterpreter(), request,
+ nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() == 1) {
+ const char *platform_name = args.GetArgumentAtIndex(0);
+ if (platform_name && platform_name[0]) {
+ const bool select = true;
+ m_platform_options.SetPlatformName(platform_name);
+ Status error;
+ ArchSpec platform_arch;
+ PlatformSP platform_sp(m_platform_options.CreatePlatformWithOptions(
+ m_interpreter, ArchSpec(), select, error, platform_arch));
+ if (platform_sp) {
+ GetDebugger().GetPlatformList().SetSelectedPlatform(platform_sp);
+
+ platform_sp->GetStatus(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("invalid platform name");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError(
+ "platform create takes a platform name as an argument\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupPlatform m_platform_options;
+};
+
+// "platform list"
+class CommandObjectPlatformList : public CommandObjectParsed {
+public:
+ CommandObjectPlatformList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform list",
+ "List all platforms that are available.", nullptr,
+ 0) {}
+
+ ~CommandObjectPlatformList() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Stream &ostrm = result.GetOutputStream();
+ ostrm.Printf("Available platforms:\n");
+
+ PlatformSP host_platform_sp(Platform::GetHostPlatform());
+ ostrm.Printf("%s: %s\n", host_platform_sp->GetPluginName().GetCString(),
+ host_platform_sp->GetDescription());
+
+ uint32_t idx;
+ for (idx = 0; true; ++idx) {
+ const char *plugin_name =
+ PluginManager::GetPlatformPluginNameAtIndex(idx);
+ if (plugin_name == nullptr)
+ break;
+ const char *plugin_desc =
+ PluginManager::GetPlatformPluginDescriptionAtIndex(idx);
+ if (plugin_desc == nullptr)
+ break;
+ ostrm.Printf("%s: %s\n", plugin_name, plugin_desc);
+ }
+
+ if (idx == 0) {
+ result.AppendError("no platforms are available\n");
+ result.SetStatus(eReturnStatusFailed);
+ } else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// "platform status"
+class CommandObjectPlatformStatus : public CommandObjectParsed {
+public:
+ CommandObjectPlatformStatus(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform status",
+ "Display status for the current platform.", nullptr,
+ 0) {}
+
+ ~CommandObjectPlatformStatus() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Stream &ostrm = result.GetOutputStream();
+
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ PlatformSP platform_sp;
+ if (target) {
+ platform_sp = target->GetPlatform();
+ }
+ if (!platform_sp) {
+ platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform();
+ }
+ if (platform_sp) {
+ platform_sp->GetStatus(ostrm);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("no platform is currently selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform connect <connect-url>"
+class CommandObjectPlatformConnect : public CommandObjectParsed {
+public:
+ CommandObjectPlatformConnect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "platform connect",
+ "Select the current platform by providing a connection URL.",
+ "platform connect <connect-url>", 0) {}
+
+ ~CommandObjectPlatformConnect() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Stream &ostrm = result.GetOutputStream();
+
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ Status error(platform_sp->ConnectRemote(args));
+ if (error.Success()) {
+ platform_sp->GetStatus(ostrm);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ platform_sp->ConnectToWaitingProcesses(GetDebugger(), error);
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendErrorWithFormat("%s\n", error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("no platform is currently selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ OptionGroupOptions *m_platform_options = nullptr;
+ if (platform_sp) {
+ m_platform_options = platform_sp->GetConnectionOptions(m_interpreter);
+ if (m_platform_options != nullptr && !m_platform_options->m_did_finalize)
+ m_platform_options->Finalize();
+ }
+ return m_platform_options;
+ }
+};
+
+// "platform disconnect"
+class CommandObjectPlatformDisconnect : public CommandObjectParsed {
+public:
+ CommandObjectPlatformDisconnect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform disconnect",
+ "Disconnect from the current platform.",
+ "platform disconnect", 0) {}
+
+ ~CommandObjectPlatformDisconnect() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ if (args.GetArgumentCount() == 0) {
+ Status error;
+
+ if (platform_sp->IsConnected()) {
+ // Cache the instance name if there is one since we are about to
+ // disconnect and the name might go with it.
+ const char *hostname_cstr = platform_sp->GetHostname();
+ std::string hostname;
+ if (hostname_cstr)
+ hostname.assign(hostname_cstr);
+
+ error = platform_sp->DisconnectRemote();
+ if (error.Success()) {
+ Stream &ostrm = result.GetOutputStream();
+ if (hostname.empty())
+ ostrm.Printf("Disconnected from \"%s\"\n",
+ platform_sp->GetPluginName().GetCString());
+ else
+ ostrm.Printf("Disconnected from \"%s\"\n", hostname.c_str());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("%s", error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ // Not connected...
+ result.AppendErrorWithFormat(
+ "not connected to '%s'",
+ platform_sp->GetPluginName().GetCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ // Bad args
+ result.AppendError(
+ "\"platform disconnect\" doesn't take any arguments");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("no platform is currently selected");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform settings"
+class CommandObjectPlatformSettings : public CommandObjectParsed {
+public:
+ CommandObjectPlatformSettings(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform settings",
+ "Set settings for the current target's platform, "
+ "or for a platform by name.",
+ "platform settings", 0),
+ m_options(),
+ m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w', 0,
+ eArgTypePath,
+ "The working directory for the platform.") {
+ m_options.Append(&m_option_working_dir, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ }
+
+ ~CommandObjectPlatformSettings() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ if (m_option_working_dir.GetOptionValue().OptionWasSet())
+ platform_sp->SetWorkingDirectory(
+ m_option_working_dir.GetOptionValue().GetCurrentValue());
+ } else {
+ result.AppendError("no platform is currently selected");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override {
+ if (!m_options.DidFinalize())
+ m_options.Finalize();
+ return &m_options;
+ }
+
+protected:
+ OptionGroupOptions m_options;
+ OptionGroupFile m_option_working_dir;
+};
+
+// "platform mkdir"
+class CommandObjectPlatformMkDir : public CommandObjectParsed {
+public:
+ CommandObjectPlatformMkDir(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform mkdir",
+ "Make a new directory on the remote end.", nullptr,
+ 0),
+ m_options() {}
+
+ ~CommandObjectPlatformMkDir() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string cmd_line;
+ args.GetCommandString(cmd_line);
+ uint32_t mode;
+ const OptionPermissions *options_permissions =
+ (const OptionPermissions *)m_options.GetGroupWithOption('r');
+ if (options_permissions)
+ mode = options_permissions->m_permissions;
+ else
+ mode = lldb::eFilePermissionsUserRWX | lldb::eFilePermissionsGroupRWX |
+ lldb::eFilePermissionsWorldRX;
+ Status error = platform_sp->MakeDirectory(FileSpec(cmd_line), mode);
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override {
+ if (!m_options.DidFinalize()) {
+ m_options.Append(new OptionPermissions());
+ m_options.Finalize();
+ }
+ return &m_options;
+ }
+
+ OptionGroupOptions m_options;
+};
+
+// "platform fopen"
+class CommandObjectPlatformFOpen : public CommandObjectParsed {
+public:
+ CommandObjectPlatformFOpen(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform file open",
+ "Open a file on the remote end.", nullptr, 0),
+ m_options() {}
+
+ ~CommandObjectPlatformFOpen() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ Status error;
+ std::string cmd_line;
+ args.GetCommandString(cmd_line);
+ mode_t perms;
+ const OptionPermissions *options_permissions =
+ (const OptionPermissions *)m_options.GetGroupWithOption('r');
+ if (options_permissions)
+ perms = options_permissions->m_permissions;
+ else
+ perms = lldb::eFilePermissionsUserRW | lldb::eFilePermissionsGroupRW |
+ lldb::eFilePermissionsWorldRead;
+ lldb::user_id_t fd = platform_sp->OpenFile(
+ FileSpec(cmd_line),
+ File::eOpenOptionRead | File::eOpenOptionWrite |
+ File::eOpenOptionAppend | File::eOpenOptionCanCreate,
+ perms, error);
+ if (error.Success()) {
+ result.AppendMessageWithFormat("File Descriptor = %" PRIu64 "\n", fd);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override {
+ if (!m_options.DidFinalize()) {
+ m_options.Append(new OptionPermissions());
+ m_options.Finalize();
+ }
+ return &m_options;
+ }
+
+ OptionGroupOptions m_options;
+};
+
+// "platform fclose"
+class CommandObjectPlatformFClose : public CommandObjectParsed {
+public:
+ CommandObjectPlatformFClose(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform file close",
+ "Close a file on the remote end.", nullptr, 0) {}
+
+ ~CommandObjectPlatformFClose() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string cmd_line;
+ args.GetCommandString(cmd_line);
+ const lldb::user_id_t fd =
+ StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX);
+ Status error;
+ bool success = platform_sp->CloseFile(fd, error);
+ if (success) {
+ result.AppendMessageWithFormat("file %" PRIu64 " closed.\n", fd);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform fread"
+
+static constexpr OptionDefinition g_platform_fread_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeIndex, "Offset into the file at which to start reading." },
+ { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount, "Number of bytes to read from the file." },
+ // clang-format on
+};
+
+class CommandObjectPlatformFRead : public CommandObjectParsed {
+public:
+ CommandObjectPlatformFRead(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform file read",
+ "Read data from a file on the remote end.", nullptr,
+ 0),
+ m_options() {}
+
+ ~CommandObjectPlatformFRead() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string cmd_line;
+ args.GetCommandString(cmd_line);
+ const lldb::user_id_t fd =
+ StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX);
+ std::string buffer(m_options.m_count, 0);
+ Status error;
+ uint32_t retcode = platform_sp->ReadFile(
+ fd, m_options.m_offset, &buffer[0], m_options.m_count, error);
+ result.AppendMessageWithFormat("Return = %d\n", retcode);
+ result.AppendMessageWithFormat("Data = \"%s\"\n", buffer.c_str());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("no platform currently selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ char short_option = (char)m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'o':
+ if (option_arg.getAsInteger(0, m_offset))
+ error.SetErrorStringWithFormat("invalid offset: '%s'",
+ option_arg.str().c_str());
+ break;
+ case 'c':
+ if (option_arg.getAsInteger(0, m_count))
+ error.SetErrorStringWithFormat("invalid offset: '%s'",
+ option_arg.str().c_str());
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_offset = 0;
+ m_count = 1;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_platform_fread_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ uint32_t m_offset;
+ uint32_t m_count;
+ };
+
+ CommandOptions m_options;
+};
+
+// "platform fwrite"
+
+static constexpr OptionDefinition g_platform_fwrite_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeIndex, "Offset into the file at which to start reading." },
+ { LLDB_OPT_SET_1, false, "data", 'd', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeValue, "Text to write to the file." },
+ // clang-format on
+};
+
+class CommandObjectPlatformFWrite : public CommandObjectParsed {
+public:
+ CommandObjectPlatformFWrite(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform file write",
+ "Write data to a file on the remote end.", nullptr,
+ 0),
+ m_options() {}
+
+ ~CommandObjectPlatformFWrite() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string cmd_line;
+ args.GetCommandString(cmd_line);
+ Status error;
+ const lldb::user_id_t fd =
+ StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX);
+ uint32_t retcode =
+ platform_sp->WriteFile(fd, m_options.m_offset, &m_options.m_data[0],
+ m_options.m_data.size(), error);
+ result.AppendMessageWithFormat("Return = %d\n", retcode);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("no platform currently selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ char short_option = (char)m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'o':
+ if (option_arg.getAsInteger(0, m_offset))
+ error.SetErrorStringWithFormat("invalid offset: '%s'",
+ option_arg.str().c_str());
+ break;
+ case 'd':
+ m_data.assign(option_arg);
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_offset = 0;
+ m_data.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_platform_fwrite_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ uint32_t m_offset;
+ std::string m_data;
+ };
+
+ CommandOptions m_options;
+};
+
+class CommandObjectPlatformFile : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectPlatformFile(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "platform file",
+ "Commands to access files on the current platform.",
+ "platform file [open|close|read|write] ...") {
+ LoadSubCommand(
+ "open", CommandObjectSP(new CommandObjectPlatformFOpen(interpreter)));
+ LoadSubCommand(
+ "close", CommandObjectSP(new CommandObjectPlatformFClose(interpreter)));
+ LoadSubCommand(
+ "read", CommandObjectSP(new CommandObjectPlatformFRead(interpreter)));
+ LoadSubCommand(
+ "write", CommandObjectSP(new CommandObjectPlatformFWrite(interpreter)));
+ }
+
+ ~CommandObjectPlatformFile() override = default;
+
+private:
+ // For CommandObjectPlatform only
+ DISALLOW_COPY_AND_ASSIGN(CommandObjectPlatformFile);
+};
+
+// "platform get-file remote-file-path host-file-path"
+class CommandObjectPlatformGetFile : public CommandObjectParsed {
+public:
+ CommandObjectPlatformGetFile(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "platform get-file",
+ "Transfer a file from the remote end to the local host.",
+ "platform get-file <remote-file-spec> <local-file-spec>", 0) {
+ SetHelpLong(
+ R"(Examples:
+
+(lldb) platform get-file /the/remote/file/path /the/local/file/path
+
+ Transfer a file from the remote end with file path /the/remote/file/path to the local host.)");
+
+ CommandArgumentEntry arg1, arg2;
+ CommandArgumentData file_arg_remote, file_arg_host;
+
+ // Define the first (and only) variant of this arg.
+ file_arg_remote.arg_type = eArgTypeFilename;
+ file_arg_remote.arg_repetition = eArgRepeatPlain;
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(file_arg_remote);
+
+ // Define the second (and only) variant of this arg.
+ file_arg_host.arg_type = eArgTypeFilename;
+ file_arg_host.arg_repetition = eArgRepeatPlain;
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(file_arg_host);
+
+ // Push the data for the first and the second arguments into the
+ // m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectPlatformGetFile() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ // If the number of arguments is incorrect, issue an error message.
+ if (args.GetArgumentCount() != 2) {
+ result.GetErrorStream().Printf("error: required arguments missing; "
+ "specify both the source and destination "
+ "file paths\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ const char *remote_file_path = args.GetArgumentAtIndex(0);
+ const char *local_file_path = args.GetArgumentAtIndex(1);
+ Status error = platform_sp->GetFile(FileSpec(remote_file_path),
+ FileSpec(local_file_path));
+ if (error.Success()) {
+ result.AppendMessageWithFormat(
+ "successfully get-file from %s (remote) to %s (host)\n",
+ remote_file_path, local_file_path);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendMessageWithFormat("get-file failed: %s\n",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform get-size remote-file-path"
+class CommandObjectPlatformGetSize : public CommandObjectParsed {
+public:
+ CommandObjectPlatformGetSize(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform get-size",
+ "Get the file size from the remote end.",
+ "platform get-size <remote-file-spec>", 0) {
+ SetHelpLong(
+ R"(Examples:
+
+(lldb) platform get-size /the/remote/file/path
+
+ Get the file size from the remote end with path /the/remote/file/path.)");
+
+ CommandArgumentEntry arg1;
+ CommandArgumentData file_arg_remote;
+
+ // Define the first (and only) variant of this arg.
+ file_arg_remote.arg_type = eArgTypeFilename;
+ file_arg_remote.arg_repetition = eArgRepeatPlain;
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(file_arg_remote);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectPlatformGetSize() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ // If the number of arguments is incorrect, issue an error message.
+ if (args.GetArgumentCount() != 1) {
+ result.GetErrorStream().Printf("error: required argument missing; "
+ "specify the source file path as the only "
+ "argument\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ std::string remote_file_path(args.GetArgumentAtIndex(0));
+ user_id_t size = platform_sp->GetFileSize(FileSpec(remote_file_path));
+ if (size != UINT64_MAX) {
+ result.AppendMessageWithFormat("File size of %s (remote): %" PRIu64
+ "\n",
+ remote_file_path.c_str(), size);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendMessageWithFormat(
+ "Error getting file size of %s (remote)\n",
+ remote_file_path.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform put-file"
+class CommandObjectPlatformPutFile : public CommandObjectParsed {
+public:
+ CommandObjectPlatformPutFile(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "platform put-file",
+ "Transfer a file from this system to the remote end.", nullptr, 0) {
+ }
+
+ ~CommandObjectPlatformPutFile() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ const char *src = args.GetArgumentAtIndex(0);
+ const char *dst = args.GetArgumentAtIndex(1);
+
+ FileSpec src_fs(src);
+ FileSystem::Instance().Resolve(src_fs);
+ FileSpec dst_fs(dst ? dst : src_fs.GetFilename().GetCString());
+
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ Status error(platform_sp->PutFile(src_fs, dst_fs));
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("no platform currently selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+// "platform process launch"
+class CommandObjectPlatformProcessLaunch : public CommandObjectParsed {
+public:
+ CommandObjectPlatformProcessLaunch(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform process launch",
+ "Launch a new process on a remote platform.",
+ "platform process launch program",
+ eCommandRequiresTarget | eCommandTryTargetAPILock),
+ m_options() {}
+
+ ~CommandObjectPlatformProcessLaunch() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ PlatformSP platform_sp;
+ if (target) {
+ platform_sp = target->GetPlatform();
+ }
+ if (!platform_sp) {
+ platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform();
+ }
+
+ if (platform_sp) {
+ Status error;
+ const size_t argc = args.GetArgumentCount();
+ Target *target = m_exe_ctx.GetTargetPtr();
+ Module *exe_module = target->GetExecutableModulePointer();
+ if (exe_module) {
+ m_options.launch_info.GetExecutableFile() = exe_module->GetFileSpec();
+ llvm::SmallString<128> exe_path;
+ m_options.launch_info.GetExecutableFile().GetPath(exe_path);
+ if (!exe_path.empty())
+ m_options.launch_info.GetArguments().AppendArgument(exe_path);
+ m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture();
+ }
+
+ if (argc > 0) {
+ if (m_options.launch_info.GetExecutableFile()) {
+ // We already have an executable file, so we will use this and all
+ // arguments to this function are extra arguments
+ m_options.launch_info.GetArguments().AppendArguments(args);
+ } else {
+ // We don't have any file yet, so the first argument is our
+ // executable, and the rest are program arguments
+ const bool first_arg_is_executable = true;
+ m_options.launch_info.SetArguments(args, first_arg_is_executable);
+ }
+ }
+
+ if (m_options.launch_info.GetExecutableFile()) {
+ Debugger &debugger = GetDebugger();
+
+ if (argc == 0)
+ target->GetRunArguments(m_options.launch_info.GetArguments());
+
+ ProcessSP process_sp(platform_sp->DebugProcess(
+ m_options.launch_info, debugger, target, error));
+ if (process_sp && process_sp->IsAlive()) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ if (error.Success())
+ result.AppendError("process launch failed");
+ else
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ result.AppendError("'platform process launch' uses the current target "
+ "file and arguments, or the executable and its "
+ "arguments can be specified in this command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ result.AppendError("no platform is selected\n");
+ }
+ return result.Succeeded();
+ }
+
+protected:
+ ProcessLaunchCommandOptions m_options;
+};
+
+// "platform process list"
+
+static OptionDefinition g_platform_process_list_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "pid", 'p', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePid, "List the process info for a specific process ID." },
+ { LLDB_OPT_SET_2, true, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeProcessName, "Find processes with executable basenames that match a string." },
+ { LLDB_OPT_SET_3, true, "ends-with", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeProcessName, "Find processes with executable basenames that end with a string." },
+ { LLDB_OPT_SET_4, true, "starts-with", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeProcessName, "Find processes with executable basenames that start with a string." },
+ { LLDB_OPT_SET_5, true, "contains", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeProcessName, "Find processes with executable basenames that contain a string." },
+ { LLDB_OPT_SET_6, true, "regex", 'r', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeRegularExpression, "Find processes with executable basenames that match a regular expression." },
+ { LLDB_OPT_SET_FROM_TO(2, 6), false, "parent", 'P', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePid, "Find processes that have a matching parent process ID." },
+ { LLDB_OPT_SET_FROM_TO(2, 6), false, "uid", 'u', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "Find processes that have a matching user ID." },
+ { LLDB_OPT_SET_FROM_TO(2, 6), false, "euid", 'U', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "Find processes that have a matching effective user ID." },
+ { LLDB_OPT_SET_FROM_TO(2, 6), false, "gid", 'g', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "Find processes that have a matching group ID." },
+ { LLDB_OPT_SET_FROM_TO(2, 6), false, "egid", 'G', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "Find processes that have a matching effective group ID." },
+ { LLDB_OPT_SET_FROM_TO(2, 6), false, "arch", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeArchitecture, "Find processes that have a matching architecture." },
+ { LLDB_OPT_SET_FROM_TO(1, 6), false, "show-args", 'A', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Show process arguments instead of the process executable basename." },
+ { LLDB_OPT_SET_FROM_TO(1, 6), false, "verbose", 'v', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Enable verbose output." },
+ // clang-format on
+};
+
+class CommandObjectPlatformProcessList : public CommandObjectParsed {
+public:
+ CommandObjectPlatformProcessList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform process list",
+ "List processes on a remote platform by name, pid, "
+ "or many other matching attributes.",
+ "platform process list", 0),
+ m_options() {}
+
+ ~CommandObjectPlatformProcessList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ PlatformSP platform_sp;
+ if (target) {
+ platform_sp = target->GetPlatform();
+ }
+ if (!platform_sp) {
+ platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform();
+ }
+
+ if (platform_sp) {
+ Status error;
+ if (args.GetArgumentCount() == 0) {
+ if (platform_sp) {
+ Stream &ostrm = result.GetOutputStream();
+
+ lldb::pid_t pid =
+ m_options.match_info.GetProcessInfo().GetProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ ProcessInstanceInfo proc_info;
+ if (platform_sp->GetProcessInfo(pid, proc_info)) {
+ ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args,
+ m_options.verbose);
+ proc_info.DumpAsTableRow(ostrm, platform_sp->GetUserIDResolver(),
+ m_options.show_args, m_options.verbose);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "no process found with pid = %" PRIu64 "\n", pid);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ ProcessInstanceInfoList proc_infos;
+ const uint32_t matches =
+ platform_sp->FindProcesses(m_options.match_info, proc_infos);
+ const char *match_desc = nullptr;
+ const char *match_name =
+ m_options.match_info.GetProcessInfo().GetName();
+ if (match_name && match_name[0]) {
+ switch (m_options.match_info.GetNameMatchType()) {
+ case NameMatch::Ignore:
+ break;
+ case NameMatch::Equals:
+ match_desc = "matched";
+ break;
+ case NameMatch::Contains:
+ match_desc = "contained";
+ break;
+ case NameMatch::StartsWith:
+ match_desc = "started with";
+ break;
+ case NameMatch::EndsWith:
+ match_desc = "ended with";
+ break;
+ case NameMatch::RegularExpression:
+ match_desc = "matched the regular expression";
+ break;
+ }
+ }
+
+ if (matches == 0) {
+ if (match_desc)
+ result.AppendErrorWithFormat(
+ "no processes were found that %s \"%s\" on the \"%s\" "
+ "platform\n",
+ match_desc, match_name,
+ platform_sp->GetPluginName().GetCString());
+ else
+ result.AppendErrorWithFormat(
+ "no processes were found on the \"%s\" platform\n",
+ platform_sp->GetPluginName().GetCString());
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ result.AppendMessageWithFormat(
+ "%u matching process%s found on \"%s\"", matches,
+ matches > 1 ? "es were" : " was",
+ platform_sp->GetName().GetCString());
+ if (match_desc)
+ result.AppendMessageWithFormat(" whose name %s \"%s\"",
+ match_desc, match_name);
+ result.AppendMessageWithFormat("\n");
+ ProcessInstanceInfo::DumpTableHeader(ostrm, m_options.show_args,
+ m_options.verbose);
+ for (uint32_t i = 0; i < matches; ++i) {
+ proc_infos.GetProcessInfoAtIndex(i).DumpAsTableRow(
+ ostrm, platform_sp->GetUserIDResolver(),
+ m_options.show_args, m_options.verbose);
+ }
+ }
+ }
+ }
+ } else {
+ result.AppendError("invalid args: process list takes only options\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("no platform is selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), match_info(), show_args(false), verbose(false) {
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ PosixPlatformCommandOptionValidator *posix_validator =
+ new PosixPlatformCommandOptionValidator();
+ for (auto &Option : g_platform_process_list_options) {
+ switch (Option.short_option) {
+ case 'u':
+ case 'U':
+ case 'g':
+ case 'G':
+ Option.validator = posix_validator;
+ break;
+ default:
+ break;
+ }
+ }
+ });
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ bool success = false;
+
+ uint32_t id = LLDB_INVALID_PROCESS_ID;
+ success = !option_arg.getAsInteger(0, id);
+ switch (short_option) {
+ case 'p': {
+ match_info.GetProcessInfo().SetProcessID(id);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid process ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+ }
+ case 'P':
+ match_info.GetProcessInfo().SetParentProcessID(id);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid parent process ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'u':
+ match_info.GetProcessInfo().SetUserID(success ? id : UINT32_MAX);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid user ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'U':
+ match_info.GetProcessInfo().SetEffectiveUserID(success ? id
+ : UINT32_MAX);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid effective user ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'g':
+ match_info.GetProcessInfo().SetGroupID(success ? id : UINT32_MAX);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid group ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'G':
+ match_info.GetProcessInfo().SetEffectiveGroupID(success ? id
+ : UINT32_MAX);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid effective group ID string: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'a': {
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ DebuggerSP debugger_sp =
+ target_sp ? target_sp->GetDebugger().shared_from_this()
+ : DebuggerSP();
+ PlatformSP platform_sp =
+ debugger_sp ? debugger_sp->GetPlatformList().GetSelectedPlatform()
+ : PlatformSP();
+ match_info.GetProcessInfo().GetArchitecture() =
+ Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg);
+ } break;
+
+ case 'n':
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ option_arg, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::Equals);
+ break;
+
+ case 'e':
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ option_arg, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::EndsWith);
+ break;
+
+ case 's':
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ option_arg, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::StartsWith);
+ break;
+
+ case 'c':
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ option_arg, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::Contains);
+ break;
+
+ case 'r':
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ option_arg, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::RegularExpression);
+ break;
+
+ case 'A':
+ show_args = true;
+ break;
+
+ case 'v':
+ verbose = true;
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ match_info.Clear();
+ show_args = false;
+ verbose = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_platform_process_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ ProcessInstanceInfoMatch match_info;
+ bool show_args;
+ bool verbose;
+ };
+
+ CommandOptions m_options;
+};
+
+// "platform process info"
+class CommandObjectPlatformProcessInfo : public CommandObjectParsed {
+public:
+ CommandObjectPlatformProcessInfo(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "platform process info",
+ "Get detailed information for one or more process by process ID.",
+ "platform process info <pid> [<pid> <pid> ...]", 0) {
+ CommandArgumentEntry arg;
+ CommandArgumentData pid_args;
+
+ // Define the first (and only) variant of this arg.
+ pid_args.arg_type = eArgTypePid;
+ pid_args.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(pid_args);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectPlatformProcessInfo() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ PlatformSP platform_sp;
+ if (target) {
+ platform_sp = target->GetPlatform();
+ }
+ if (!platform_sp) {
+ platform_sp = GetDebugger().GetPlatformList().GetSelectedPlatform();
+ }
+
+ if (platform_sp) {
+ const size_t argc = args.GetArgumentCount();
+ if (argc > 0) {
+ Status error;
+
+ if (platform_sp->IsConnected()) {
+ Stream &ostrm = result.GetOutputStream();
+ for (auto &entry : args.entries()) {
+ lldb::pid_t pid;
+ if (entry.ref.getAsInteger(0, pid)) {
+ result.AppendErrorWithFormat("invalid process ID argument '%s'",
+ entry.ref.str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ break;
+ } else {
+ ProcessInstanceInfo proc_info;
+ if (platform_sp->GetProcessInfo(pid, proc_info)) {
+ ostrm.Printf("Process information for process %" PRIu64 ":\n",
+ pid);
+ proc_info.Dump(ostrm, platform_sp->GetUserIDResolver());
+ } else {
+ ostrm.Printf("error: no process information is available for "
+ "process %" PRIu64 "\n",
+ pid);
+ }
+ ostrm.EOL();
+ }
+ }
+ } else {
+ // Not connected...
+ result.AppendErrorWithFormat(
+ "not connected to '%s'",
+ platform_sp->GetPluginName().GetCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ // No args
+ result.AppendError("one or more process id(s) must be specified");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("no platform is currently selected");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+static constexpr OptionDefinition g_platform_process_attach_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "plugin", 'P', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePlugin, "Name of the process plugin you want to use." },
+ { LLDB_OPT_SET_1, false, "pid", 'p', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePid, "The process ID of an existing process to attach to." },
+ { LLDB_OPT_SET_2, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeProcessName, "The name of the process to attach to." },
+ { LLDB_OPT_SET_2, false, "waitfor", 'w', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Wait for the process with <process-name> to launch." },
+ // clang-format on
+};
+
+class CommandObjectPlatformProcessAttach : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ char short_option = (char)m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'p': {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ if (option_arg.getAsInteger(0, pid)) {
+ error.SetErrorStringWithFormat("invalid process ID '%s'",
+ option_arg.str().c_str());
+ } else {
+ attach_info.SetProcessID(pid);
+ }
+ } break;
+
+ case 'P':
+ attach_info.SetProcessPluginName(option_arg);
+ break;
+
+ case 'n':
+ attach_info.GetExecutableFile().SetFile(option_arg,
+ FileSpec::Style::native);
+ break;
+
+ case 'w':
+ attach_info.SetWaitForLaunch(true);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ attach_info.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_platform_process_attach_options);
+ }
+
+ bool HandleOptionArgumentCompletion(
+ CompletionRequest &request, OptionElementVector &opt_element_vector,
+ int opt_element_index, CommandInterpreter &interpreter) override {
+ int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos;
+ int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index;
+
+ // We are only completing the name option for now...
+
+ if (GetDefinitions()[opt_defs_index].short_option == 'n') {
+ // Are we in the name?
+
+ // Look to see if there is a -P argument provided, and if so use that
+ // plugin, otherwise use the default plugin.
+
+ const char *partial_name = nullptr;
+ partial_name = request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos);
+
+ PlatformSP platform_sp(interpreter.GetPlatform(true));
+ if (platform_sp) {
+ ProcessInstanceInfoList process_infos;
+ ProcessInstanceInfoMatch match_info;
+ if (partial_name) {
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ partial_name, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::StartsWith);
+ }
+ platform_sp->FindProcesses(match_info, process_infos);
+ const uint32_t num_matches = process_infos.GetSize();
+ if (num_matches > 0) {
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ request.AddCompletion(llvm::StringRef(
+ process_infos.GetProcessNameAtIndex(i),
+ process_infos.GetProcessNameLengthAtIndex(i)));
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Options table: Required for subclasses of Options.
+
+ static OptionDefinition g_option_table[];
+
+ // Instance variables to hold the values for command options.
+
+ ProcessAttachInfo attach_info;
+ };
+
+ CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "platform process attach",
+ "Attach to a process.",
+ "platform process attach <cmd-options>"),
+ m_options() {}
+
+ ~CommandObjectPlatformProcessAttach() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (platform_sp) {
+ Status err;
+ ProcessSP remote_process_sp = platform_sp->Attach(
+ m_options.attach_info, GetDebugger(), nullptr, err);
+ if (err.Fail()) {
+ result.AppendError(err.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ } else if (!remote_process_sp) {
+ result.AppendError("could not attach: unknown reason");
+ result.SetStatus(eReturnStatusFailed);
+ } else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("no platform is currently selected");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ CommandOptions m_options;
+};
+
+class CommandObjectPlatformProcess : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectPlatformProcess(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "platform process",
+ "Commands to query, launch and attach to "
+ "processes on the current platform.",
+ "platform process [attach|launch|list] ...") {
+ LoadSubCommand(
+ "attach",
+ CommandObjectSP(new CommandObjectPlatformProcessAttach(interpreter)));
+ LoadSubCommand(
+ "launch",
+ CommandObjectSP(new CommandObjectPlatformProcessLaunch(interpreter)));
+ LoadSubCommand("info", CommandObjectSP(new CommandObjectPlatformProcessInfo(
+ interpreter)));
+ LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformProcessList(
+ interpreter)));
+ }
+
+ ~CommandObjectPlatformProcess() override = default;
+
+private:
+ // For CommandObjectPlatform only
+ DISALLOW_COPY_AND_ASSIGN(CommandObjectPlatformProcess);
+};
+
+// "platform shell"
+static constexpr OptionDefinition g_platform_shell_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "timeout", 't', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeValue, "Seconds to wait for the remote host to finish running the command." },
+ // clang-format on
+};
+
+class CommandObjectPlatformShell : public CommandObjectRaw {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_platform_shell_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const char short_option = (char)GetDefinitions()[option_idx].short_option;
+
+ switch (short_option) {
+ case 't':
+ uint32_t timeout_sec;
+ if (option_arg.getAsInteger(10, timeout_sec))
+ error.SetErrorStringWithFormat(
+ "could not convert \"%s\" to a numeric value.",
+ option_arg.str().c_str());
+ else
+ timeout = std::chrono::seconds(timeout_sec);
+ break;
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {}
+
+ Timeout<std::micro> timeout = std::chrono::seconds(10);
+ };
+
+ CommandObjectPlatformShell(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "platform shell",
+ "Run a shell command on the current platform.",
+ "platform shell <shell-command>", 0),
+ m_options() {}
+
+ ~CommandObjectPlatformShell() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext();
+ m_options.NotifyOptionParsingStarting(&exe_ctx);
+
+
+ // Print out an usage syntax on an empty command line.
+ if (raw_command_line.empty()) {
+ result.GetOutputStream().Printf("%s\n", this->GetSyntax().str().c_str());
+ return true;
+ }
+
+ OptionsWithRaw args(raw_command_line);
+ const char *expr = args.GetRawPart().c_str();
+
+ if (args.HasArgs())
+ if (!ParseOptions(args.GetArgs(), result))
+ return false;
+
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ Status error;
+ if (platform_sp) {
+ FileSpec working_dir{};
+ std::string output;
+ int status = -1;
+ int signo = -1;
+ error = (platform_sp->RunShellCommand(expr, working_dir, &status, &signo,
+ &output, m_options.timeout));
+ if (!output.empty())
+ result.GetOutputStream().PutCString(output);
+ if (status > 0) {
+ if (signo > 0) {
+ const char *signo_cstr = Host::GetSignalAsCString(signo);
+ if (signo_cstr)
+ result.GetOutputStream().Printf(
+ "error: command returned with status %i and signal %s\n",
+ status, signo_cstr);
+ else
+ result.GetOutputStream().Printf(
+ "error: command returned with status %i and signal %i\n",
+ status, signo);
+ } else
+ result.GetOutputStream().Printf(
+ "error: command returned with status %i\n", status);
+ }
+ } else {
+ result.GetOutputStream().Printf(
+ "error: cannot run remote shell commands without a platform\n");
+ error.SetErrorString(
+ "error: cannot run remote shell commands without a platform");
+ }
+
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// "platform install" - install a target to a remote end
+class CommandObjectPlatformInstall : public CommandObjectParsed {
+public:
+ CommandObjectPlatformInstall(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "platform target-install",
+ "Install a target (bundle or executable file) to the remote end.",
+ "platform target-install <local-thing> <remote-sandbox>", 0) {}
+
+ ~CommandObjectPlatformInstall() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() != 2) {
+ result.AppendError("platform target-install takes two arguments");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ // TODO: move the bulk of this code over to the platform itself
+ FileSpec src(args.GetArgumentAtIndex(0));
+ FileSystem::Instance().Resolve(src);
+ FileSpec dst(args.GetArgumentAtIndex(1));
+ if (!FileSystem::Instance().Exists(src)) {
+ result.AppendError("source location does not exist or is not accessible");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+ if (!platform_sp) {
+ result.AppendError("no platform currently selected");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Status error = platform_sp->Install(src, dst);
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendErrorWithFormat("install failed: %s", error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "platform", "Commands to manage and create platforms.",
+ "platform [connect|disconnect|info|list|status|select] ...") {
+ LoadSubCommand("select",
+ CommandObjectSP(new CommandObjectPlatformSelect(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectPlatformList(interpreter)));
+ LoadSubCommand("status",
+ CommandObjectSP(new CommandObjectPlatformStatus(interpreter)));
+ LoadSubCommand("connect", CommandObjectSP(
+ new CommandObjectPlatformConnect(interpreter)));
+ LoadSubCommand(
+ "disconnect",
+ CommandObjectSP(new CommandObjectPlatformDisconnect(interpreter)));
+ LoadSubCommand("settings", CommandObjectSP(new CommandObjectPlatformSettings(
+ interpreter)));
+ LoadSubCommand("mkdir",
+ CommandObjectSP(new CommandObjectPlatformMkDir(interpreter)));
+ LoadSubCommand("file",
+ CommandObjectSP(new CommandObjectPlatformFile(interpreter)));
+ LoadSubCommand("get-file", CommandObjectSP(new CommandObjectPlatformGetFile(
+ interpreter)));
+ LoadSubCommand("get-size", CommandObjectSP(new CommandObjectPlatformGetSize(
+ interpreter)));
+ LoadSubCommand("put-file", CommandObjectSP(new CommandObjectPlatformPutFile(
+ interpreter)));
+ LoadSubCommand("process", CommandObjectSP(
+ new CommandObjectPlatformProcess(interpreter)));
+ LoadSubCommand("shell",
+ CommandObjectSP(new CommandObjectPlatformShell(interpreter)));
+ LoadSubCommand(
+ "target-install",
+ CommandObjectSP(new CommandObjectPlatformInstall(interpreter)));
+}
+
+CommandObjectPlatform::~CommandObjectPlatform() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.h
new file mode 100644
index 000000000000..c94d2ea2cc4d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlatform.h
@@ -0,0 +1,31 @@
+//===-- CommandObjectPlatform.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectPlatform_h_
+#define liblldb_CommandObjectPlatform_h_
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/Options.h"
+
+namespace lldb_private {
+
+// CommandObjectPlatform
+
+class CommandObjectPlatform : public CommandObjectMultiword {
+public:
+ CommandObjectPlatform(CommandInterpreter &interpreter);
+
+ ~CommandObjectPlatform() override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(CommandObjectPlatform);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectPlatform_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.cpp
new file mode 100644
index 000000000000..89e01ba52027
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.cpp
@@ -0,0 +1,83 @@
+//===-- CommandObjectPlugin.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectPlugin.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class CommandObjectPluginLoad : public CommandObjectParsed {
+public:
+ CommandObjectPluginLoad(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "plugin load",
+ "Import a dylib that implements an LLDB plugin.",
+ nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentData cmd_arg;
+
+ // Define the first (and only) variant of this arg.
+ cmd_arg.arg_type = eArgTypeFilename;
+ cmd_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(cmd_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectPluginLoad() override = default;
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ size_t argc = command.GetArgumentCount();
+
+ if (argc != 1) {
+ result.AppendError("'plugin load' requires one argument");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Status error;
+
+ FileSpec dylib_fspec(command[0].ref);
+ FileSystem::Instance().Resolve(dylib_fspec);
+
+ if (GetDebugger().LoadPlugin(dylib_fspec, error))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+CommandObjectPlugin::CommandObjectPlugin(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "plugin",
+ "Commands for managing LLDB plugins.",
+ "plugin <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("load",
+ CommandObjectSP(new CommandObjectPluginLoad(interpreter)));
+}
+
+CommandObjectPlugin::~CommandObjectPlugin() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.h
new file mode 100644
index 000000000000..0aabb1399407
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.h
@@ -0,0 +1,28 @@
+//===-- CommandObjectPlugin.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectPlugin_h_
+#define liblldb_CommandObjectPlugin_h_
+
+
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+class CommandObjectPlugin : public CommandObjectMultiword {
+public:
+ CommandObjectPlugin(CommandInterpreter &interpreter);
+
+ ~CommandObjectPlugin() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectPlugin_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp
new file mode 100644
index 000000000000..b20a2d533332
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.cpp
@@ -0,0 +1,1568 @@
+//===-- CommandObjectProcess.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectProcess.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointSite.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class CommandObjectProcessLaunchOrAttach : public CommandObjectParsed {
+public:
+ CommandObjectProcessLaunchOrAttach(CommandInterpreter &interpreter,
+ const char *name, const char *help,
+ const char *syntax, uint32_t flags,
+ const char *new_process_action)
+ : CommandObjectParsed(interpreter, name, help, syntax, flags),
+ m_new_process_action(new_process_action) {}
+
+ ~CommandObjectProcessLaunchOrAttach() override = default;
+
+protected:
+ bool StopProcessIfNecessary(Process *process, StateType &state,
+ CommandReturnObject &result) {
+ state = eStateInvalid;
+ if (process) {
+ state = process->GetState();
+
+ if (process->IsAlive() && state != eStateConnected) {
+ char message[1024];
+ if (process->GetState() == eStateAttaching)
+ ::snprintf(message, sizeof(message),
+ "There is a pending attach, abort it and %s?",
+ m_new_process_action.c_str());
+ else if (process->GetShouldDetach())
+ ::snprintf(message, sizeof(message),
+ "There is a running process, detach from it and %s?",
+ m_new_process_action.c_str());
+ else
+ ::snprintf(message, sizeof(message),
+ "There is a running process, kill it and %s?",
+ m_new_process_action.c_str());
+
+ if (!m_interpreter.Confirm(message, true)) {
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ if (process->GetShouldDetach()) {
+ bool keep_stopped = false;
+ Status detach_error(process->Detach(keep_stopped));
+ if (detach_error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ process = nullptr;
+ } else {
+ result.AppendErrorWithFormat(
+ "Failed to detach from process: %s\n",
+ detach_error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ Status destroy_error(process->Destroy(false));
+ if (destroy_error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ process = nullptr;
+ } else {
+ result.AppendErrorWithFormat("Failed to kill process: %s\n",
+ destroy_error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ }
+ }
+ }
+ return result.Succeeded();
+ }
+
+ std::string m_new_process_action;
+};
+
+// CommandObjectProcessLaunch
+#pragma mark CommandObjectProcessLaunch
+class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach {
+public:
+ CommandObjectProcessLaunch(CommandInterpreter &interpreter)
+ : CommandObjectProcessLaunchOrAttach(
+ interpreter, "process launch",
+ "Launch the executable in the debugger.", nullptr,
+ eCommandRequiresTarget, "restart"),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData run_args_arg;
+
+ // Define the first (and only) variant of this arg.
+ run_args_arg.arg_type = eArgTypeRunArgs;
+ run_args_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(run_args_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectProcessLaunch() override = default;
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+ const char *GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ // No repeat for "process launch"...
+ return "";
+ }
+
+protected:
+ bool DoExecute(Args &launch_args, CommandReturnObject &result) override {
+ Debugger &debugger = GetDebugger();
+ Target *target = debugger.GetSelectedTarget().get();
+ // If our listener is nullptr, users aren't allows to launch
+ ModuleSP exe_module_sp = target->GetExecutableModule();
+
+ if (exe_module_sp == nullptr) {
+ result.AppendError("no file in target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ StateType state = eStateInvalid;
+
+ if (!StopProcessIfNecessary(m_exe_ctx.GetProcessPtr(), state, result))
+ return false;
+
+ llvm::StringRef target_settings_argv0 = target->GetArg0();
+
+ // Determine whether we will disable ASLR or leave it in the default state
+ // (i.e. enabled if the platform supports it). First check if the process
+ // launch options explicitly turn on/off
+ // disabling ASLR. If so, use that setting;
+ // otherwise, use the 'settings target.disable-aslr' setting.
+ bool disable_aslr = false;
+ if (m_options.disable_aslr != eLazyBoolCalculate) {
+ // The user specified an explicit setting on the process launch line.
+ // Use it.
+ disable_aslr = (m_options.disable_aslr == eLazyBoolYes);
+ } else {
+ // The user did not explicitly specify whether to disable ASLR. Fall
+ // back to the target.disable-aslr setting.
+ disable_aslr = target->GetDisableASLR();
+ }
+
+ if (disable_aslr)
+ m_options.launch_info.GetFlags().Set(eLaunchFlagDisableASLR);
+ else
+ m_options.launch_info.GetFlags().Clear(eLaunchFlagDisableASLR);
+
+ if (target->GetDetachOnError())
+ m_options.launch_info.GetFlags().Set(eLaunchFlagDetachOnError);
+
+ if (target->GetDisableSTDIO())
+ m_options.launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
+
+ // Merge the launch info environment with the target environment.
+ Environment target_env = target->GetEnvironment();
+ m_options.launch_info.GetEnvironment().insert(target_env.begin(),
+ target_env.end());
+
+ if (!target_settings_argv0.empty()) {
+ m_options.launch_info.GetArguments().AppendArgument(
+ target_settings_argv0);
+ m_options.launch_info.SetExecutableFile(
+ exe_module_sp->GetPlatformFileSpec(), false);
+ } else {
+ m_options.launch_info.SetExecutableFile(
+ exe_module_sp->GetPlatformFileSpec(), true);
+ }
+
+ if (launch_args.GetArgumentCount() == 0) {
+ m_options.launch_info.GetArguments().AppendArguments(
+ target->GetProcessLaunchInfo().GetArguments());
+ } else {
+ m_options.launch_info.GetArguments().AppendArguments(launch_args);
+ // Save the arguments for subsequent runs in the current target.
+ target->SetRunArguments(launch_args);
+ }
+
+ StreamString stream;
+ Status error = target->Launch(m_options.launch_info, &stream);
+
+ if (error.Success()) {
+ ProcessSP process_sp(target->GetProcessSP());
+ if (process_sp) {
+ // There is a race condition where this thread will return up the call
+ // stack to the main command handler and show an (lldb) prompt before
+ // HandlePrivateEvent (from PrivateStateThread) has a chance to call
+ // PushProcessIOHandler().
+ process_sp->SyncIOHandler(0, std::chrono::seconds(2));
+
+ llvm::StringRef data = stream.GetString();
+ if (!data.empty())
+ result.AppendMessage(data);
+ const char *archname =
+ exe_module_sp->GetArchitecture().GetArchitectureName();
+ result.AppendMessageWithFormat(
+ "Process %" PRIu64 " launched: '%s' (%s)\n", process_sp->GetID(),
+ exe_module_sp->GetFileSpec().GetPath().c_str(), archname);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ result.SetDidChangeProcessState(true);
+ } else {
+ result.AppendError(
+ "no error returned from Target::Launch, and target has no process");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+protected:
+ ProcessLaunchCommandOptions m_options;
+};
+
+static constexpr OptionDefinition g_process_attach_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "continue", 'c', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Immediately continue the process once attached." },
+ { LLDB_OPT_SET_ALL, false, "plugin", 'P', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePlugin, "Name of the process plugin you want to use." },
+ { LLDB_OPT_SET_1, false, "pid", 'p', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePid, "The process ID of an existing process to attach to." },
+ { LLDB_OPT_SET_2, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeProcessName, "The name of the process to attach to." },
+ { LLDB_OPT_SET_2, false, "include-existing", 'i', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Include existing processes when doing attach -w." },
+ { LLDB_OPT_SET_2, false, "waitfor", 'w', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Wait for the process with <process-name> to launch." },
+ // clang-format on
+};
+
+#pragma mark CommandObjectProcessAttach
+class CommandObjectProcessAttach : public CommandObjectProcessLaunchOrAttach {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'c':
+ attach_info.SetContinueOnceAttached(true);
+ break;
+
+ case 'p': {
+ lldb::pid_t pid;
+ if (option_arg.getAsInteger(0, pid)) {
+ error.SetErrorStringWithFormat("invalid process ID '%s'",
+ option_arg.str().c_str());
+ } else {
+ attach_info.SetProcessID(pid);
+ }
+ } break;
+
+ case 'P':
+ attach_info.SetProcessPluginName(option_arg);
+ break;
+
+ case 'n':
+ attach_info.GetExecutableFile().SetFile(option_arg,
+ FileSpec::Style::native);
+ break;
+
+ case 'w':
+ attach_info.SetWaitForLaunch(true);
+ break;
+
+ case 'i':
+ attach_info.SetIgnoreExisting(false);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ attach_info.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_attach_options);
+ }
+
+ bool HandleOptionArgumentCompletion(
+ CompletionRequest &request, OptionElementVector &opt_element_vector,
+ int opt_element_index, CommandInterpreter &interpreter) override {
+ int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos;
+ int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index;
+
+ // We are only completing the name option for now...
+
+ if (GetDefinitions()[opt_defs_index].short_option == 'n') {
+ // Are we in the name?
+
+ // Look to see if there is a -P argument provided, and if so use that
+ // plugin, otherwise use the default plugin.
+
+ const char *partial_name = nullptr;
+ partial_name = request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos);
+
+ PlatformSP platform_sp(interpreter.GetPlatform(true));
+ if (platform_sp) {
+ ProcessInstanceInfoList process_infos;
+ ProcessInstanceInfoMatch match_info;
+ if (partial_name) {
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ partial_name, FileSpec::Style::native);
+ match_info.SetNameMatchType(NameMatch::StartsWith);
+ }
+ platform_sp->FindProcesses(match_info, process_infos);
+ const size_t num_matches = process_infos.GetSize();
+ if (num_matches > 0) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ request.AddCompletion(llvm::StringRef(
+ process_infos.GetProcessNameAtIndex(i),
+ process_infos.GetProcessNameLengthAtIndex(i)));
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Instance variables to hold the values for command options.
+
+ ProcessAttachInfo attach_info;
+ };
+
+ CommandObjectProcessAttach(CommandInterpreter &interpreter)
+ : CommandObjectProcessLaunchOrAttach(
+ interpreter, "process attach", "Attach to a process.",
+ "process attach <cmd-options>", 0, "attach"),
+ m_options() {}
+
+ ~CommandObjectProcessAttach() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ PlatformSP platform_sp(
+ GetDebugger().GetPlatformList().GetSelectedPlatform());
+
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ // N.B. The attach should be synchronous. It doesn't help much to get the
+ // prompt back between initiating the attach and the target actually
+ // stopping. So even if the interpreter is set to be asynchronous, we wait
+ // for the stop ourselves here.
+
+ StateType state = eStateInvalid;
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ if (!StopProcessIfNecessary(process, state, result))
+ return false;
+
+ if (target == nullptr) {
+ // If there isn't a current target create one.
+ TargetSP new_target_sp;
+ Status error;
+
+ error = GetDebugger().GetTargetList().CreateTarget(
+ GetDebugger(), "", "", eLoadDependentsNo,
+ nullptr, // No platform options
+ new_target_sp);
+ target = new_target_sp.get();
+ if (target == nullptr || error.Fail()) {
+ result.AppendError(error.AsCString("Error creating target"));
+ return false;
+ }
+ GetDebugger().GetTargetList().SetSelectedTarget(target);
+ }
+
+ // Record the old executable module, we want to issue a warning if the
+ // process of attaching changed the current executable (like somebody said
+ // "file foo" then attached to a PID whose executable was bar.)
+
+ ModuleSP old_exec_module_sp = target->GetExecutableModule();
+ ArchSpec old_arch_spec = target->GetArchitecture();
+
+ if (command.GetArgumentCount()) {
+ result.AppendErrorWithFormat("Invalid arguments for '%s'.\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ m_interpreter.UpdateExecutionContext(nullptr);
+ StreamString stream;
+ const auto error = target->Attach(m_options.attach_info, &stream);
+ if (error.Success()) {
+ ProcessSP process_sp(target->GetProcessSP());
+ if (process_sp) {
+ result.AppendMessage(stream.GetString());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ result.SetDidChangeProcessState(true);
+ result.SetAbnormalStopWasExpected(true);
+ } else {
+ result.AppendError(
+ "no error returned from Target::Attach, and target has no process");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendErrorWithFormat("attach failed: %s\n", error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ if (!result.Succeeded())
+ return false;
+
+ // Okay, we're done. Last step is to warn if the executable module has
+ // changed:
+ char new_path[PATH_MAX];
+ ModuleSP new_exec_module_sp(target->GetExecutableModule());
+ if (!old_exec_module_sp) {
+ // We might not have a module if we attached to a raw pid...
+ if (new_exec_module_sp) {
+ new_exec_module_sp->GetFileSpec().GetPath(new_path, PATH_MAX);
+ result.AppendMessageWithFormat("Executable module set to \"%s\".\n",
+ new_path);
+ }
+ } else if (old_exec_module_sp->GetFileSpec() !=
+ new_exec_module_sp->GetFileSpec()) {
+ char old_path[PATH_MAX];
+
+ old_exec_module_sp->GetFileSpec().GetPath(old_path, PATH_MAX);
+ new_exec_module_sp->GetFileSpec().GetPath(new_path, PATH_MAX);
+
+ result.AppendWarningWithFormat(
+ "Executable module changed from \"%s\" to \"%s\".\n", old_path,
+ new_path);
+ }
+
+ if (!old_arch_spec.IsValid()) {
+ result.AppendMessageWithFormat(
+ "Architecture set to: %s.\n",
+ target->GetArchitecture().GetTriple().getTriple().c_str());
+ } else if (!old_arch_spec.IsExactMatch(target->GetArchitecture())) {
+ result.AppendWarningWithFormat(
+ "Architecture changed from %s to %s.\n",
+ old_arch_spec.GetTriple().getTriple().c_str(),
+ target->GetArchitecture().GetTriple().getTriple().c_str());
+ }
+
+ // This supports the use-case scenario of immediately continuing the
+ // process once attached.
+ if (m_options.attach_info.GetContinueOnceAttached())
+ m_interpreter.HandleCommand("process continue", eLazyBoolNo, result);
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessContinue
+
+static constexpr OptionDefinition g_process_continue_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "ignore-count",'i', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "Ignore <N> crossings of the breakpoint (if it exists) for the currently selected thread." }
+ // clang-format on
+};
+
+#pragma mark CommandObjectProcessContinue
+
+class CommandObjectProcessContinue : public CommandObjectParsed {
+public:
+ CommandObjectProcessContinue(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process continue",
+ "Continue execution of all threads in the current process.",
+ "process continue",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+ m_options() {}
+
+ ~CommandObjectProcessContinue() override = default;
+
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'i':
+ if (option_arg.getAsInteger(0, m_ignore))
+ error.SetErrorStringWithFormat(
+ "invalid value for ignore option: \"%s\", should be a number.",
+ option_arg.str().c_str());
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_ignore = 0;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_continue_options);
+ }
+
+ uint32_t m_ignore;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ bool synchronous_execution = m_interpreter.GetSynchronous();
+ StateType state = process->GetState();
+ if (state == eStateStopped) {
+ if (command.GetArgumentCount() != 0) {
+ result.AppendErrorWithFormat(
+ "The '%s' command does not take any arguments.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_options.m_ignore > 0) {
+ ThreadSP sel_thread_sp(GetDefaultThread()->shared_from_this());
+ if (sel_thread_sp) {
+ StopInfoSP stop_info_sp = sel_thread_sp->GetStopInfo();
+ if (stop_info_sp &&
+ stop_info_sp->GetStopReason() == eStopReasonBreakpoint) {
+ lldb::break_id_t bp_site_id =
+ (lldb::break_id_t)stop_info_sp->GetValue();
+ BreakpointSiteSP bp_site_sp(
+ process->GetBreakpointSiteList().FindByID(bp_site_id));
+ if (bp_site_sp) {
+ const size_t num_owners = bp_site_sp->GetNumberOfOwners();
+ for (size_t i = 0; i < num_owners; i++) {
+ Breakpoint &bp_ref =
+ bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint();
+ if (!bp_ref.IsInternal()) {
+ bp_ref.SetIgnoreCount(m_options.m_ignore);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ { // Scope for thread list mutex:
+ std::lock_guard<std::recursive_mutex> guard(
+ process->GetThreadList().GetMutex());
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+
+ // Set the actions that the threads should each take when resuming
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ const bool override_suspend = false;
+ process->GetThreadList().GetThreadAtIndex(idx)->SetResumeState(
+ eStateRunning, override_suspend);
+ }
+ }
+
+ const uint32_t iohandler_id = process->GetIOHandlerID();
+
+ StreamString stream;
+ Status error;
+ if (synchronous_execution)
+ error = process->ResumeSynchronous(&stream);
+ else
+ error = process->Resume();
+
+ if (error.Success()) {
+ // There is a race condition where this thread will return up the call
+ // stack to the main command handler and show an (lldb) prompt before
+ // HandlePrivateEvent (from PrivateStateThread) has a chance to call
+ // PushProcessIOHandler().
+ process->SyncIOHandler(iohandler_id, std::chrono::seconds(2));
+
+ result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n",
+ process->GetID());
+ if (synchronous_execution) {
+ // If any state changed events had anything to say, add that to the
+ // result
+ result.AppendMessage(stream.GetString());
+
+ result.SetDidChangeProcessState(true);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.SetStatus(eReturnStatusSuccessContinuingNoResult);
+ }
+ } else {
+ result.AppendErrorWithFormat("Failed to resume process: %s.\n",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "Process cannot be continued from its current state (%s).\n",
+ StateAsCString(state));
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessDetach
+static constexpr OptionDefinition g_process_detach_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "keep-stopped", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Whether or not the process should be kept stopped on detach (if possible)." },
+ // clang-format on
+};
+
+#pragma mark CommandObjectProcessDetach
+
+class CommandObjectProcessDetach : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 's':
+ bool tmp_result;
+ bool success;
+ tmp_result = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid boolean option: \"%s\"",
+ option_arg.str().c_str());
+ else {
+ if (tmp_result)
+ m_keep_stopped = eLazyBoolYes;
+ else
+ m_keep_stopped = eLazyBoolNo;
+ }
+ break;
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_keep_stopped = eLazyBoolCalculate;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_detach_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ LazyBool m_keep_stopped;
+ };
+
+ CommandObjectProcessDetach(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process detach",
+ "Detach from the current target process.",
+ "process detach",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched),
+ m_options() {}
+
+ ~CommandObjectProcessDetach() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ // FIXME: This will be a Command Option:
+ bool keep_stopped;
+ if (m_options.m_keep_stopped == eLazyBoolCalculate) {
+ // Check the process default:
+ keep_stopped = process->GetDetachKeepsStopped();
+ } else if (m_options.m_keep_stopped == eLazyBoolYes)
+ keep_stopped = true;
+ else
+ keep_stopped = false;
+
+ Status error(process->Detach(keep_stopped));
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Detach failed: %s\n", error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessConnect
+
+static constexpr OptionDefinition g_process_connect_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "plugin", 'p', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePlugin, "Name of the process plugin you want to use." },
+ // clang-format on
+};
+
+#pragma mark CommandObjectProcessConnect
+
+class CommandObjectProcessConnect : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'p':
+ plugin_name.assign(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ plugin_name.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_connect_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string plugin_name;
+ };
+
+ CommandObjectProcessConnect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process connect",
+ "Connect to a remote debug service.",
+ "process connect <remote-url>", 0),
+ m_options() {}
+
+ ~CommandObjectProcessConnect() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one argument:\nUsage: %s\n", m_cmd_name.c_str(),
+ m_cmd_syntax.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive()) {
+ result.AppendErrorWithFormat(
+ "Process %" PRIu64
+ " is currently being debugged, kill the process before connecting.\n",
+ process->GetID());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *plugin_name = nullptr;
+ if (!m_options.plugin_name.empty())
+ plugin_name = m_options.plugin_name.c_str();
+
+ Status error;
+ Debugger &debugger = GetDebugger();
+ PlatformSP platform_sp = m_interpreter.GetPlatform(true);
+ ProcessSP process_sp = platform_sp->ConnectProcess(
+ command.GetArgumentAtIndex(0), plugin_name, debugger,
+ debugger.GetSelectedTarget().get(), error);
+ if (error.Fail() || process_sp == nullptr) {
+ result.AppendError(error.AsCString("Error connecting to the process"));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessPlugin
+#pragma mark CommandObjectProcessPlugin
+
+class CommandObjectProcessPlugin : public CommandObjectProxy {
+public:
+ CommandObjectProcessPlugin(CommandInterpreter &interpreter)
+ : CommandObjectProxy(
+ interpreter, "process plugin",
+ "Send a custom command to the current target process plug-in.",
+ "process plugin <args>", 0) {}
+
+ ~CommandObjectProcessPlugin() override = default;
+
+ CommandObject *GetProxyCommandObject() override {
+ Process *process = m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process)
+ return process->GetPluginCommandObject();
+ return nullptr;
+ }
+};
+
+// CommandObjectProcessLoad
+
+static constexpr OptionDefinition g_process_load_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "install", 'i', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypePath, "Install the shared library to the target. If specified without an argument then the library will installed in the current working directory." },
+ // clang-format on
+};
+
+#pragma mark CommandObjectProcessLoad
+
+class CommandObjectProcessLoad : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'i':
+ do_install = true;
+ if (!option_arg.empty())
+ install_path.SetFile(option_arg, FileSpec::Style::native);
+ break;
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ do_install = false;
+ install_path.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_load_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool do_install;
+ FileSpec install_path;
+ };
+
+ CommandObjectProcessLoad(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process load",
+ "Load a shared library into the current process.",
+ "process load <filename> [<filename> ...]",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options() {}
+
+ ~CommandObjectProcessLoad() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ for (auto &entry : command.entries()) {
+ Status error;
+ PlatformSP platform = process->GetTarget().GetPlatform();
+ llvm::StringRef image_path = entry.ref;
+ uint32_t image_token = LLDB_INVALID_IMAGE_TOKEN;
+
+ if (!m_options.do_install) {
+ FileSpec image_spec(image_path);
+ platform->ResolveRemotePath(image_spec, image_spec);
+ image_token =
+ platform->LoadImage(process, FileSpec(), image_spec, error);
+ } else if (m_options.install_path) {
+ FileSpec image_spec(image_path);
+ FileSystem::Instance().Resolve(image_spec);
+ platform->ResolveRemotePath(m_options.install_path,
+ m_options.install_path);
+ image_token = platform->LoadImage(process, image_spec,
+ m_options.install_path, error);
+ } else {
+ FileSpec image_spec(image_path);
+ FileSystem::Instance().Resolve(image_spec);
+ image_token =
+ platform->LoadImage(process, image_spec, FileSpec(), error);
+ }
+
+ if (image_token != LLDB_INVALID_IMAGE_TOKEN) {
+ result.AppendMessageWithFormat(
+ "Loading \"%s\"...ok\nImage %u loaded.\n", image_path.str().c_str(),
+ image_token);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("failed to load '%s': %s",
+ image_path.str().c_str(),
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectProcessUnload
+#pragma mark CommandObjectProcessUnload
+
+class CommandObjectProcessUnload : public CommandObjectParsed {
+public:
+ CommandObjectProcessUnload(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process unload",
+ "Unload a shared library from the current process using the index "
+ "returned by a previous call to \"process load\".",
+ "process unload <index>",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
+
+ ~CommandObjectProcessUnload() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ for (auto &entry : command.entries()) {
+ uint32_t image_token;
+ if (entry.ref.getAsInteger(0, image_token)) {
+ result.AppendErrorWithFormat("invalid image index argument '%s'",
+ entry.ref.str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ break;
+ } else {
+ Status error(process->GetTarget().GetPlatform()->UnloadImage(
+ process, image_token));
+ if (error.Success()) {
+ result.AppendMessageWithFormat(
+ "Unloading shared library with index %u...ok\n", image_token);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("failed to unload image: %s",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ break;
+ }
+ }
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectProcessSignal
+#pragma mark CommandObjectProcessSignal
+
+class CommandObjectProcessSignal : public CommandObjectParsed {
+public:
+ CommandObjectProcessSignal(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process signal",
+ "Send a UNIX signal to the current target process.",
+ nullptr, eCommandRequiresProcess |
+ eCommandTryTargetAPILock) {
+ CommandArgumentEntry arg;
+ CommandArgumentData signal_arg;
+
+ // Define the first (and only) variant of this arg.
+ signal_arg.arg_type = eArgTypeUnixSignal;
+ signal_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(signal_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectProcessSignal() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ if (command.GetArgumentCount() == 1) {
+ int signo = LLDB_INVALID_SIGNAL_NUMBER;
+
+ const char *signal_name = command.GetArgumentAtIndex(0);
+ if (::isxdigit(signal_name[0]))
+ signo =
+ StringConvert::ToSInt32(signal_name, LLDB_INVALID_SIGNAL_NUMBER, 0);
+ else
+ signo = process->GetUnixSignals()->GetSignalNumberFromName(signal_name);
+
+ if (signo == LLDB_INVALID_SIGNAL_NUMBER) {
+ result.AppendErrorWithFormat("Invalid signal argument '%s'.\n",
+ command.GetArgumentAtIndex(0));
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ Status error(process->Signal(signo));
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Failed to send signal %i: %s\n", signo,
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one signal number argument:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectProcessInterrupt
+#pragma mark CommandObjectProcessInterrupt
+
+class CommandObjectProcessInterrupt : public CommandObjectParsed {
+public:
+ CommandObjectProcessInterrupt(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process interrupt",
+ "Interrupt the current target process.",
+ "process interrupt",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectProcessInterrupt() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process to halt");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ bool clear_thread_plans = true;
+ Status error(process->Halt(clear_thread_plans));
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Failed to halt process: %s\n",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectProcessKill
+#pragma mark CommandObjectProcessKill
+
+class CommandObjectProcessKill : public CommandObjectParsed {
+public:
+ CommandObjectProcessKill(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process kill",
+ "Terminate the current target process.",
+ "process kill",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectProcessKill() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process to kill");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ Status error(process->Destroy(true));
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Failed to kill process: %s\n",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectProcessSaveCore
+#pragma mark CommandObjectProcessSaveCore
+
+class CommandObjectProcessSaveCore : public CommandObjectParsed {
+public:
+ CommandObjectProcessSaveCore(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process save-core",
+ "Save the current process as a core file using an "
+ "appropriate file type.",
+ "process save-core FILE",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectProcessSaveCore() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+ if (process_sp) {
+ if (command.GetArgumentCount() == 1) {
+ FileSpec output_file(command.GetArgumentAtIndex(0));
+ Status error = PluginManager::SaveCore(process_sp, output_file);
+ if (error.Success()) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Failed to save core file for process: %s\n", error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendErrorWithFormat("'%s' takes one arguments:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("invalid process");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectProcessStatus
+#pragma mark CommandObjectProcessStatus
+
+class CommandObjectProcessStatus : public CommandObjectParsed {
+public:
+ CommandObjectProcessStatus(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process status",
+ "Show status and stop location for the current target process.",
+ "process status",
+ eCommandRequiresProcess | eCommandTryTargetAPILock) {}
+
+ ~CommandObjectProcessStatus() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Stream &strm = result.GetOutputStream();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ // No need to check "process" for validity as eCommandRequiresProcess
+ // ensures it is valid
+ Process *process = m_exe_ctx.GetProcessPtr();
+ const bool only_threads_with_stop_reason = true;
+ const uint32_t start_frame = 0;
+ const uint32_t num_frames = 1;
+ const uint32_t num_frames_with_source = 1;
+ const bool stop_format = true;
+ process->GetStatus(strm);
+ process->GetThreadStatus(strm, only_threads_with_stop_reason, start_frame,
+ num_frames, num_frames_with_source, stop_format);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectProcessHandle
+
+static constexpr OptionDefinition g_process_handle_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "stop", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Whether or not the process should be stopped if the signal is received." },
+ { LLDB_OPT_SET_1, false, "notify", 'n', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Whether or not the debugger should notify the user if the signal is received." },
+ { LLDB_OPT_SET_1, false, "pass", 'p', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Whether or not the signal should be passed to the process." }
+ // clang-format on
+};
+
+#pragma mark CommandObjectProcessHandle
+
+class CommandObjectProcessHandle : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 's':
+ stop = option_arg;
+ break;
+ case 'n':
+ notify = option_arg;
+ break;
+ case 'p':
+ pass = option_arg;
+ break;
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ stop.clear();
+ notify.clear();
+ pass.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_process_handle_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string stop;
+ std::string notify;
+ std::string pass;
+ };
+
+ CommandObjectProcessHandle(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process handle",
+ "Manage LLDB handling of OS signals for the "
+ "current target process. Defaults to showing "
+ "current policy.",
+ nullptr),
+ m_options() {
+ SetHelpLong("\nIf no signals are specified, update them all. If no update "
+ "option is specified, list the current values.");
+ CommandArgumentEntry arg;
+ CommandArgumentData signal_arg;
+
+ signal_arg.arg_type = eArgTypeUnixSignal;
+ signal_arg.arg_repetition = eArgRepeatStar;
+
+ arg.push_back(signal_arg);
+
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectProcessHandle() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool VerifyCommandOptionValue(const std::string &option, int &real_value) {
+ bool okay = true;
+ bool success = false;
+ bool tmp_value = OptionArgParser::ToBoolean(option, false, &success);
+
+ if (success && tmp_value)
+ real_value = 1;
+ else if (success && !tmp_value)
+ real_value = 0;
+ else {
+ // If the value isn't 'true' or 'false', it had better be 0 or 1.
+ real_value = StringConvert::ToUInt32(option.c_str(), 3);
+ if (real_value != 0 && real_value != 1)
+ okay = false;
+ }
+
+ return okay;
+ }
+
+ void PrintSignalHeader(Stream &str) {
+ str.Printf("NAME PASS STOP NOTIFY\n");
+ str.Printf("=========== ===== ===== ======\n");
+ }
+
+ void PrintSignal(Stream &str, int32_t signo, const char *sig_name,
+ const UnixSignalsSP &signals_sp) {
+ bool stop;
+ bool suppress;
+ bool notify;
+
+ str.Printf("%-11s ", sig_name);
+ if (signals_sp->GetSignalInfo(signo, suppress, stop, notify)) {
+ bool pass = !suppress;
+ str.Printf("%s %s %s", (pass ? "true " : "false"),
+ (stop ? "true " : "false"), (notify ? "true " : "false"));
+ }
+ str.Printf("\n");
+ }
+
+ void PrintSignalInformation(Stream &str, Args &signal_args,
+ int num_valid_signals,
+ const UnixSignalsSP &signals_sp) {
+ PrintSignalHeader(str);
+
+ if (num_valid_signals > 0) {
+ size_t num_args = signal_args.GetArgumentCount();
+ for (size_t i = 0; i < num_args; ++i) {
+ int32_t signo = signals_sp->GetSignalNumberFromName(
+ signal_args.GetArgumentAtIndex(i));
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ PrintSignal(str, signo, signal_args.GetArgumentAtIndex(i),
+ signals_sp);
+ }
+ } else // Print info for ALL signals
+ {
+ int32_t signo = signals_sp->GetFirstSignalNumber();
+ while (signo != LLDB_INVALID_SIGNAL_NUMBER) {
+ PrintSignal(str, signo, signals_sp->GetSignalAsCString(signo),
+ signals_sp);
+ signo = signals_sp->GetNextSignalNumber(signo);
+ }
+ }
+ }
+
+protected:
+ bool DoExecute(Args &signal_args, CommandReturnObject &result) override {
+ TargetSP target_sp = GetDebugger().GetSelectedTarget();
+
+ if (!target_sp) {
+ result.AppendError("No current target;"
+ " cannot handle signals until you have a valid target "
+ "and process.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ProcessSP process_sp = target_sp->GetProcessSP();
+
+ if (!process_sp) {
+ result.AppendError("No current process; cannot handle signals until you "
+ "have a valid process.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ int stop_action = -1; // -1 means leave the current setting alone
+ int pass_action = -1; // -1 means leave the current setting alone
+ int notify_action = -1; // -1 means leave the current setting alone
+
+ if (!m_options.stop.empty() &&
+ !VerifyCommandOptionValue(m_options.stop, stop_action)) {
+ result.AppendError("Invalid argument for command option --stop; must be "
+ "true or false.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (!m_options.notify.empty() &&
+ !VerifyCommandOptionValue(m_options.notify, notify_action)) {
+ result.AppendError("Invalid argument for command option --notify; must "
+ "be true or false.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (!m_options.pass.empty() &&
+ !VerifyCommandOptionValue(m_options.pass, pass_action)) {
+ result.AppendError("Invalid argument for command option --pass; must be "
+ "true or false.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ size_t num_args = signal_args.GetArgumentCount();
+ UnixSignalsSP signals_sp = process_sp->GetUnixSignals();
+ int num_signals_set = 0;
+
+ if (num_args > 0) {
+ for (const auto &arg : signal_args) {
+ int32_t signo = signals_sp->GetSignalNumberFromName(arg.c_str());
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER) {
+ // Casting the actions as bools here should be okay, because
+ // VerifyCommandOptionValue guarantees the value is either 0 or 1.
+ if (stop_action != -1)
+ signals_sp->SetShouldStop(signo, stop_action);
+ if (pass_action != -1) {
+ bool suppress = !pass_action;
+ signals_sp->SetShouldSuppress(signo, suppress);
+ }
+ if (notify_action != -1)
+ signals_sp->SetShouldNotify(signo, notify_action);
+ ++num_signals_set;
+ } else {
+ result.AppendErrorWithFormat("Invalid signal name '%s'\n",
+ arg.c_str());
+ }
+ }
+ } else {
+ // No signal specified, if any command options were specified, update ALL
+ // signals.
+ if ((notify_action != -1) || (stop_action != -1) || (pass_action != -1)) {
+ if (m_interpreter.Confirm(
+ "Do you really want to update all the signals?", false)) {
+ int32_t signo = signals_sp->GetFirstSignalNumber();
+ while (signo != LLDB_INVALID_SIGNAL_NUMBER) {
+ if (notify_action != -1)
+ signals_sp->SetShouldNotify(signo, notify_action);
+ if (stop_action != -1)
+ signals_sp->SetShouldStop(signo, stop_action);
+ if (pass_action != -1) {
+ bool suppress = !pass_action;
+ signals_sp->SetShouldSuppress(signo, suppress);
+ }
+ signo = signals_sp->GetNextSignalNumber(signo);
+ }
+ }
+ }
+ }
+
+ PrintSignalInformation(result.GetOutputStream(), signal_args,
+ num_signals_set, signals_sp);
+
+ if (num_signals_set > 0)
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectMultiwordProcess
+
+CommandObjectMultiwordProcess::CommandObjectMultiwordProcess(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "process",
+ "Commands for interacting with processes on the current platform.",
+ "process <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("attach",
+ CommandObjectSP(new CommandObjectProcessAttach(interpreter)));
+ LoadSubCommand("launch",
+ CommandObjectSP(new CommandObjectProcessLaunch(interpreter)));
+ LoadSubCommand("continue", CommandObjectSP(new CommandObjectProcessContinue(
+ interpreter)));
+ LoadSubCommand("connect",
+ CommandObjectSP(new CommandObjectProcessConnect(interpreter)));
+ LoadSubCommand("detach",
+ CommandObjectSP(new CommandObjectProcessDetach(interpreter)));
+ LoadSubCommand("load",
+ CommandObjectSP(new CommandObjectProcessLoad(interpreter)));
+ LoadSubCommand("unload",
+ CommandObjectSP(new CommandObjectProcessUnload(interpreter)));
+ LoadSubCommand("signal",
+ CommandObjectSP(new CommandObjectProcessSignal(interpreter)));
+ LoadSubCommand("handle",
+ CommandObjectSP(new CommandObjectProcessHandle(interpreter)));
+ LoadSubCommand("status",
+ CommandObjectSP(new CommandObjectProcessStatus(interpreter)));
+ LoadSubCommand("interrupt", CommandObjectSP(new CommandObjectProcessInterrupt(
+ interpreter)));
+ LoadSubCommand("kill",
+ CommandObjectSP(new CommandObjectProcessKill(interpreter)));
+ LoadSubCommand("plugin",
+ CommandObjectSP(new CommandObjectProcessPlugin(interpreter)));
+ LoadSubCommand("save-core", CommandObjectSP(new CommandObjectProcessSaveCore(
+ interpreter)));
+}
+
+CommandObjectMultiwordProcess::~CommandObjectMultiwordProcess() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.h
new file mode 100644
index 000000000000..3b1ff26dbb05
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectProcess.h
@@ -0,0 +1,27 @@
+//===-- CommandObjectProcess.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectProcess_h_
+#define liblldb_CommandObjectProcess_h_
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordProcess
+
+class CommandObjectMultiwordProcess : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordProcess(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordProcess() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectProcess_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.cpp
new file mode 100644
index 000000000000..70ee336f8a1b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.cpp
@@ -0,0 +1,107 @@
+//===-- CommandObjectQuit.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectQuit.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectQuit
+
+CommandObjectQuit::CommandObjectQuit(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "quit", "Quit the LLDB debugger.",
+ "quit [exit-code]") {}
+
+CommandObjectQuit::~CommandObjectQuit() {}
+
+// returns true if there is at least one alive process is_a_detach will be true
+// if all alive processes will be detached when you quit and false if at least
+// one process will be killed instead
+bool CommandObjectQuit::ShouldAskForConfirmation(bool &is_a_detach) {
+ if (!m_interpreter.GetPromptOnQuit())
+ return false;
+ bool should_prompt = false;
+ is_a_detach = true;
+ for (uint32_t debugger_idx = 0; debugger_idx < Debugger::GetNumDebuggers();
+ debugger_idx++) {
+ DebuggerSP debugger_sp(Debugger::GetDebuggerAtIndex(debugger_idx));
+ if (!debugger_sp)
+ continue;
+ const TargetList &target_list(debugger_sp->GetTargetList());
+ for (uint32_t target_idx = 0;
+ target_idx < static_cast<uint32_t>(target_list.GetNumTargets());
+ target_idx++) {
+ TargetSP target_sp(target_list.GetTargetAtIndex(target_idx));
+ if (!target_sp)
+ continue;
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp && process_sp->IsValid() && process_sp->IsAlive() &&
+ process_sp->WarnBeforeDetach()) {
+ should_prompt = true;
+ if (!process_sp->GetShouldDetach()) {
+ // if we need to kill at least one process, just say so and return
+ is_a_detach = false;
+ return should_prompt;
+ }
+ }
+ }
+ }
+ return should_prompt;
+}
+
+bool CommandObjectQuit::DoExecute(Args &command, CommandReturnObject &result) {
+ bool is_a_detach = true;
+ if (ShouldAskForConfirmation(is_a_detach)) {
+ StreamString message;
+ message.Printf("Quitting LLDB will %s one or more processes. Do you really "
+ "want to proceed",
+ (is_a_detach ? "detach from" : "kill"));
+ if (!m_interpreter.Confirm(message.GetString(), true)) {
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ if (command.GetArgumentCount() > 1) {
+ result.AppendError("Too many arguments for 'quit'. Only an optional exit "
+ "code is allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // We parse the exit code argument if there is one.
+ if (command.GetArgumentCount() == 1) {
+ llvm::StringRef arg = command.GetArgumentAtIndex(0);
+ int exit_code;
+ if (arg.getAsInteger(/*autodetect radix*/ 0, exit_code)) {
+ lldb_private::StreamString s;
+ std::string arg_str = arg.str();
+ s.Printf("Couldn't parse '%s' as integer for exit code.", arg_str.data());
+ result.AppendError(s.GetString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ if (!m_interpreter.SetQuitExitCode(exit_code)) {
+ result.AppendError("The current driver doesn't allow custom exit codes"
+ " for the quit command.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ const uint32_t event_type =
+ CommandInterpreter::eBroadcastBitQuitCommandReceived;
+ m_interpreter.BroadcastEvent(event_type);
+ result.SetStatus(eReturnStatusQuit);
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.h
new file mode 100644
index 000000000000..458ef2456fca
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectQuit.h
@@ -0,0 +1,32 @@
+//===-- CommandObjectQuit.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectQuit_h_
+#define liblldb_CommandObjectQuit_h_
+
+#include "lldb/Interpreter/CommandObject.h"
+
+namespace lldb_private {
+
+// CommandObjectQuit
+
+class CommandObjectQuit : public CommandObjectParsed {
+public:
+ CommandObjectQuit(CommandInterpreter &interpreter);
+
+ ~CommandObjectQuit() override;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override;
+
+ bool ShouldAskForConfirmation(bool &is_a_detach);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectQuit_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.cpp
new file mode 100644
index 000000000000..34482a8b1e4f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.cpp
@@ -0,0 +1,404 @@
+//===-- CommandObjectRegister.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectRegister.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionValueArray.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Errno.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// "register read"
+
+static constexpr OptionDefinition g_register_read_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "alternate", 'A', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Display register names using the alternate register name if there is one." },
+ { LLDB_OPT_SET_1, false, "set", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeIndex, "Specify which register sets to dump by index." },
+ { LLDB_OPT_SET_2, false, "all", 'a', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Show all register sets." },
+ // clang-format on
+};
+
+class CommandObjectRegisterRead : public CommandObjectParsed {
+public:
+ CommandObjectRegisterRead(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "register read",
+ "Dump the contents of one or more register values from the current "
+ "frame. If no register is specified, dumps them all.",
+ nullptr,
+ eCommandRequiresFrame | eCommandRequiresRegContext |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+ m_option_group(), m_format_options(eFormatDefault),
+ m_command_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData register_arg;
+
+ // Define the first (and only) variant of this arg.
+ register_arg.arg_type = eArgTypeRegisterName;
+ register_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(register_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ // Add the "--format"
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_FORMAT |
+ OptionGroupFormat::OPTION_GROUP_GDB_FMT,
+ LLDB_OPT_SET_ALL);
+ m_option_group.Append(&m_command_options);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectRegisterRead() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ bool DumpRegister(const ExecutionContext &exe_ctx, Stream &strm,
+ RegisterContext *reg_ctx, const RegisterInfo *reg_info) {
+ if (reg_info) {
+ RegisterValue reg_value;
+
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ strm.Indent();
+
+ bool prefix_with_altname = (bool)m_command_options.alternate_name;
+ bool prefix_with_name = !prefix_with_altname;
+ DumpRegisterValue(reg_value, &strm, reg_info, prefix_with_name,
+ prefix_with_altname, m_format_options.GetFormat(), 8);
+ if ((reg_info->encoding == eEncodingUint) ||
+ (reg_info->encoding == eEncodingSint)) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && reg_info->byte_size == process->GetAddressByteSize()) {
+ addr_t reg_addr = reg_value.GetAsUInt64(LLDB_INVALID_ADDRESS);
+ if (reg_addr != LLDB_INVALID_ADDRESS) {
+ Address so_reg_addr;
+ if (exe_ctx.GetTargetRef()
+ .GetSectionLoadList()
+ .ResolveLoadAddress(reg_addr, so_reg_addr)) {
+ strm.PutCString(" ");
+ so_reg_addr.Dump(&strm, exe_ctx.GetBestExecutionContextScope(),
+ Address::DumpStyleResolvedDescription);
+ }
+ }
+ }
+ }
+ strm.EOL();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool DumpRegisterSet(const ExecutionContext &exe_ctx, Stream &strm,
+ RegisterContext *reg_ctx, size_t set_idx,
+ bool primitive_only = false) {
+ uint32_t unavailable_count = 0;
+ uint32_t available_count = 0;
+
+ if (!reg_ctx)
+ return false; // thread has no registers (i.e. core files are corrupt,
+ // incomplete crash logs...)
+
+ const RegisterSet *const reg_set = reg_ctx->GetRegisterSet(set_idx);
+ if (reg_set) {
+ strm.Printf("%s:\n", (reg_set->name ? reg_set->name : "unknown"));
+ strm.IndentMore();
+ const size_t num_registers = reg_set->num_registers;
+ for (size_t reg_idx = 0; reg_idx < num_registers; ++reg_idx) {
+ const uint32_t reg = reg_set->registers[reg_idx];
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg);
+ // Skip the dumping of derived register if primitive_only is true.
+ if (primitive_only && reg_info && reg_info->value_regs)
+ continue;
+
+ if (DumpRegister(exe_ctx, strm, reg_ctx, reg_info))
+ ++available_count;
+ else
+ ++unavailable_count;
+ }
+ strm.IndentLess();
+ if (unavailable_count) {
+ strm.Indent();
+ strm.Printf("%u registers were unavailable.\n", unavailable_count);
+ }
+ strm.EOL();
+ }
+ return available_count > 0;
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Stream &strm = result.GetOutputStream();
+ RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext();
+
+ const RegisterInfo *reg_info = nullptr;
+ if (command.GetArgumentCount() == 0) {
+ size_t set_idx;
+
+ size_t num_register_sets = 1;
+ const size_t set_array_size = m_command_options.set_indexes.GetSize();
+ if (set_array_size > 0) {
+ for (size_t i = 0; i < set_array_size; ++i) {
+ set_idx = m_command_options.set_indexes[i]->GetUInt64Value(UINT32_MAX,
+ nullptr);
+ if (set_idx < reg_ctx->GetRegisterSetCount()) {
+ if (!DumpRegisterSet(m_exe_ctx, strm, reg_ctx, set_idx)) {
+ if (errno)
+ result.AppendErrorWithFormatv("register read failed: {0}\n",
+ llvm::sys::StrError());
+ else
+ result.AppendError("unknown error while reading registers.\n");
+ result.SetStatus(eReturnStatusFailed);
+ break;
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "invalid register set index: %" PRIu64 "\n", (uint64_t)set_idx);
+ result.SetStatus(eReturnStatusFailed);
+ break;
+ }
+ }
+ } else {
+ if (m_command_options.dump_all_sets)
+ num_register_sets = reg_ctx->GetRegisterSetCount();
+
+ for (set_idx = 0; set_idx < num_register_sets; ++set_idx) {
+ // When dump_all_sets option is set, dump primitive as well as
+ // derived registers.
+ DumpRegisterSet(m_exe_ctx, strm, reg_ctx, set_idx,
+ !m_command_options.dump_all_sets.GetCurrentValue());
+ }
+ }
+ } else {
+ if (m_command_options.dump_all_sets) {
+ result.AppendError("the --all option can't be used when registers "
+ "names are supplied as arguments\n");
+ result.SetStatus(eReturnStatusFailed);
+ } else if (m_command_options.set_indexes.GetSize() > 0) {
+ result.AppendError("the --set <set> option can't be used when "
+ "registers names are supplied as arguments\n");
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ for (auto &entry : command) {
+ // in most LLDB commands we accept $rbx as the name for register RBX
+ // - and here we would reject it and non-existant. we should be more
+ // consistent towards the user and allow them to say reg read $rbx -
+ // internally, however, we should be strict and not allow ourselves
+ // to call our registers $rbx in our own API
+ auto arg_str = entry.ref;
+ arg_str.consume_front("$");
+
+ reg_info = reg_ctx->GetRegisterInfoByName(arg_str);
+
+ if (reg_info) {
+ if (!DumpRegister(m_exe_ctx, strm, reg_ctx, reg_info))
+ strm.Printf("%-12s = error: unavailable\n", reg_info->name);
+ } else {
+ result.AppendErrorWithFormat("Invalid register name '%s'.\n",
+ arg_str.str().c_str());
+ }
+ }
+ }
+ }
+ return result.Succeeded();
+ }
+
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions()
+ : OptionGroup(),
+ set_indexes(OptionValue::ConvertTypeToMask(OptionValue::eTypeUInt64)),
+ dump_all_sets(false, false), // Initial and default values are false
+ alternate_name(false, false) {}
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_register_read_options);
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ set_indexes.Clear();
+ dump_all_sets.Clear();
+ alternate_name.Clear();
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = GetDefinitions()[option_idx].short_option;
+ switch (short_option) {
+ case 's': {
+ OptionValueSP value_sp(OptionValueUInt64::Create(option_value, error));
+ if (value_sp)
+ set_indexes.AppendValue(value_sp);
+ } break;
+
+ case 'a':
+ // When we don't use OptionValue::SetValueFromCString(const char *) to
+ // set an option value, it won't be marked as being set in the options
+ // so we make a call to let users know the value was set via option
+ dump_all_sets.SetCurrentValue(true);
+ dump_all_sets.SetOptionWasSet();
+ break;
+
+ case 'A':
+ // When we don't use OptionValue::SetValueFromCString(const char *) to
+ // set an option value, it won't be marked as being set in the options
+ // so we make a call to let users know the value was set via option
+ alternate_name.SetCurrentValue(true);
+ dump_all_sets.SetOptionWasSet();
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ // Instance variables to hold the values for command options.
+ OptionValueArray set_indexes;
+ OptionValueBoolean dump_all_sets;
+ OptionValueBoolean alternate_name;
+ };
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFormat m_format_options;
+ CommandOptions m_command_options;
+};
+
+// "register write"
+class CommandObjectRegisterWrite : public CommandObjectParsed {
+public:
+ CommandObjectRegisterWrite(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "register write",
+ "Modify a single register value.", nullptr,
+ eCommandRequiresFrame | eCommandRequiresRegContext |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData register_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ register_arg.arg_type = eArgTypeRegisterName;
+ register_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(register_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectRegisterWrite() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ DataExtractor reg_data;
+ RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext();
+
+ if (command.GetArgumentCount() != 2) {
+ result.AppendError(
+ "register write takes exactly 2 arguments: <reg-name> <value>");
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ auto reg_name = command[0].ref;
+ auto value_str = command[1].ref;
+
+ // in most LLDB commands we accept $rbx as the name for register RBX -
+ // and here we would reject it and non-existant. we should be more
+ // consistent towards the user and allow them to say reg write $rbx -
+ // internally, however, we should be strict and not allow ourselves to
+ // call our registers $rbx in our own API
+ reg_name.consume_front("$");
+
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
+
+ if (reg_info) {
+ RegisterValue reg_value;
+
+ Status error(reg_value.SetValueFromString(reg_info, value_str));
+ if (error.Success()) {
+ if (reg_ctx->WriteRegister(reg_info, reg_value)) {
+ // Toss all frames and anything else in the thread after a register
+ // has been written.
+ m_exe_ctx.GetThreadRef().Flush();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+ }
+ if (error.AsCString()) {
+ result.AppendErrorWithFormat(
+ "Failed to write register '%s' with value '%s': %s\n",
+ reg_name.str().c_str(), value_str.str().c_str(),
+ error.AsCString());
+ } else {
+ result.AppendErrorWithFormat(
+ "Failed to write register '%s' with value '%s'",
+ reg_name.str().c_str(), value_str.str().c_str());
+ }
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ result.AppendErrorWithFormat("Register not found for '%s'.\n",
+ reg_name.str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectRegister constructor
+CommandObjectRegister::CommandObjectRegister(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "register",
+ "Commands to access registers for the current "
+ "thread and stack frame.",
+ "register [read|write] ...") {
+ LoadSubCommand("read",
+ CommandObjectSP(new CommandObjectRegisterRead(interpreter)));
+ LoadSubCommand("write",
+ CommandObjectSP(new CommandObjectRegisterWrite(interpreter)));
+}
+
+CommandObjectRegister::~CommandObjectRegister() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.h
new file mode 100644
index 000000000000..6fc47cf386a3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectRegister.h
@@ -0,0 +1,32 @@
+//===-- CommandObjectRegister.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectRegister_h_
+#define liblldb_CommandObjectRegister_h_
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectRegister
+
+class CommandObjectRegister : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectRegister(CommandInterpreter &interpreter);
+
+ ~CommandObjectRegister() override;
+
+private:
+ // For CommandObjectRegister only
+ DISALLOW_COPY_AND_ASSIGN(CommandObjectRegister);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectRegister_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.cpp
new file mode 100644
index 000000000000..4b0e9e36d202
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.cpp
@@ -0,0 +1,114 @@
+//===-- CommandObjectReproducer.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectReproducer.h"
+
+#include "lldb/Utility/Reproducer.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class CommandObjectReproducerGenerate : public CommandObjectParsed {
+public:
+ CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "reproducer generate",
+ "Generate reproducer on disk. When the debugger is in capture "
+ "mode, this command will output the reproducer to a directory on "
+ "disk. In replay mode this command in a no-op.",
+ nullptr) {}
+
+ ~CommandObjectReproducerGenerate() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (!command.empty()) {
+ result.AppendErrorWithFormat("'%s' takes no arguments",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ auto &r = repro::Reproducer::Instance();
+ if (auto generator = r.GetGenerator()) {
+ generator->Keep();
+ } else if (r.GetLoader()) {
+ // Make this operation a NOP in replay mode.
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ } else {
+ result.AppendErrorWithFormat("Unable to get the reproducer generator");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ result.GetOutputStream()
+ << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
+ result.GetOutputStream()
+ << "Please have a look at the directory to assess if you're willing to "
+ "share the contained information.\n";
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectReproducerStatus : public CommandObjectParsed {
+public:
+ CommandObjectReproducerStatus(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "reproducer status",
+ "Show the current reproducer status. In capture mode the debugger "
+ "is collecting all the information it needs to create a "
+ "reproducer. In replay mode the reproducer is replaying a "
+ "reproducer. When the reproducers are off, no data is collected "
+ "and no reproducer can be generated.",
+ nullptr) {}
+
+ ~CommandObjectReproducerStatus() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (!command.empty()) {
+ result.AppendErrorWithFormat("'%s' takes no arguments",
+ m_cmd_name.c_str());
+ return false;
+ }
+
+ auto &r = repro::Reproducer::Instance();
+ if (r.GetGenerator()) {
+ result.GetOutputStream() << "Reproducer is in capture mode.\n";
+ } else if (r.GetLoader()) {
+ result.GetOutputStream() << "Reproducer is in replay mode.\n";
+ } else {
+ result.GetOutputStream() << "Reproducer is off.\n";
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+CommandObjectReproducer::CommandObjectReproducer(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "reproducer",
+ "Commands to inspect and manipulate the reproducer functionality.",
+ "log <subcommand> [<command-options>]") {
+ LoadSubCommand(
+ "generate",
+ CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
+ LoadSubCommand("status", CommandObjectSP(
+ new CommandObjectReproducerStatus(interpreter)));
+}
+
+CommandObjectReproducer::~CommandObjectReproducer() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.h
new file mode 100644
index 000000000000..ad377241f814
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectReproducer.h
@@ -0,0 +1,28 @@
+//===-- CommandObjectReproducer.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectReproducer_h_
+#define liblldb_CommandObjectReproducer_h_
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/Options.h"
+
+namespace lldb_private {
+
+// CommandObjectReproducer
+
+class CommandObjectReproducer : public CommandObjectMultiword {
+public:
+ CommandObjectReproducer(CommandInterpreter &interpreter);
+
+ ~CommandObjectReproducer() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectReproducer_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.cpp
new file mode 100644
index 000000000000..55a0002c5997
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.cpp
@@ -0,0 +1,1186 @@
+//===-- CommandObjectSettings.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectSettings.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectSettingsSet
+
+static constexpr OptionDefinition g_settings_set_options[] = {
+#define LLDB_OPTIONS_settings_set
+#include "CommandOptions.inc"
+};
+
+class CommandObjectSettingsSet : public CommandObjectRaw {
+public:
+ CommandObjectSettingsSet(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings set",
+ "Set the value of the specified debugger setting."),
+ m_options() {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+
+ SetHelpLong(
+ "\nWhen setting a dictionary or array variable, you can set multiple entries \
+at once by giving the values to the set command. For example:"
+ R"(
+
+(lldb) settings set target.run-args value1 value2 value3
+(lldb) settings set target.env-vars MYPATH=~/.:/usr/bin SOME_ENV_VAR=12345
+
+(lldb) settings show target.run-args
+ [0]: 'value1'
+ [1]: 'value2'
+ [3]: 'value3'
+(lldb) settings show target.env-vars
+ 'MYPATH=~/.:/usr/bin'
+ 'SOME_ENV_VAR=12345'
+
+)"
+ "Warning: The 'set' command re-sets the entire array or dictionary. If you \
+just want to add, remove or update individual values (or add something to \
+the end), use one of the other settings sub-commands: append, replace, \
+insert-before or insert-after.");
+ }
+
+ ~CommandObjectSettingsSet() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_global(false) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_force = true;
+ break;
+ case 'g':
+ m_global = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized options '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_global = false;
+ m_force = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_settings_set_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_global;
+ bool m_force;
+ };
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+
+ const size_t argc = request.GetParsedLine().GetArgumentCount();
+ const char *arg = nullptr;
+ int setting_var_idx;
+ for (setting_var_idx = 0; setting_var_idx < static_cast<int>(argc);
+ ++setting_var_idx) {
+ arg = request.GetParsedLine().GetArgumentAtIndex(setting_var_idx);
+ if (arg && arg[0] != '-')
+ break; // We found our setting variable name index
+ }
+ if (request.GetCursorIndex() == setting_var_idx) {
+ // Attempting to complete setting variable name
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ } else {
+ arg =
+ request.GetParsedLine().GetArgumentAtIndex(request.GetCursorIndex());
+
+ if (arg) {
+ if (arg[0] == '-') {
+ // Complete option name
+ } else {
+ // Complete setting value
+ const char *setting_var_name =
+ request.GetParsedLine().GetArgumentAtIndex(setting_var_idx);
+ Status error;
+ lldb::OptionValueSP value_sp(GetDebugger().GetPropertyValue(
+ &m_exe_ctx, setting_var_name, false, error));
+ if (value_sp) {
+ value_sp->AutoComplete(m_interpreter, request);
+ }
+ }
+ }
+ }
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ Args cmd_args(command);
+
+ // Process possible options.
+ if (!ParseOptions(cmd_args, result))
+ return false;
+
+ const size_t min_argc = m_options.m_force ? 1 : 2;
+ const size_t argc = cmd_args.GetArgumentCount();
+
+ if ((argc < min_argc) && (!m_options.m_global)) {
+ result.AppendError("'settings set' takes more arguments");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError(
+ "'settings set' command requires a valid variable name");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // A missing value corresponds to clearing the setting when "force" is
+ // specified.
+ if (argc == 1 && m_options.m_force) {
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationClear, var_name, llvm::StringRef()));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ return result.Succeeded();
+ }
+
+ // Split the raw command into var_name and value pair.
+ llvm::StringRef raw_str(command);
+ std::string var_value_string = raw_str.split(var_name).second.str();
+ const char *var_value_cstr =
+ Args::StripSpaces(var_value_string, true, false, false);
+
+ Status error;
+ if (m_options.m_global) {
+ error = GetDebugger().SetPropertyValue(nullptr, eVarSetOperationAssign,
+ var_name, var_value_cstr);
+ }
+
+ if (error.Success()) {
+ // FIXME this is the same issue as the one in commands script import
+ // we could be setting target.load-script-from-symbol-file which would
+ // cause Python scripts to be loaded, which could run LLDB commands (e.g.
+ // settings set target.process.python-os-plugin-path) and cause a crash
+ // if we did not clear the command's exe_ctx first
+ ExecutionContext exe_ctx(m_exe_ctx);
+ m_exe_ctx.Clear();
+ error = GetDebugger().SetPropertyValue(&exe_ctx, eVarSetOperationAssign,
+ var_name, var_value_cstr);
+ }
+
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectSettingsShow -- Show current values
+
+class CommandObjectSettingsShow : public CommandObjectParsed {
+public:
+ CommandObjectSettingsShow(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "settings show",
+ "Show matching debugger settings and their current "
+ "values. Defaults to showing all settings.",
+ nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentData var_name_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectSettingsShow() override = default;
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ if (!args.empty()) {
+ for (const auto &arg : args) {
+ Status error(GetDebugger().DumpPropertyValue(
+ &m_exe_ctx, result.GetOutputStream(), arg.ref,
+ OptionValue::eDumpGroupValue));
+ if (error.Success()) {
+ result.GetOutputStream().EOL();
+ } else {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ } else {
+ GetDebugger().DumpAllPropertyValues(&m_exe_ctx, result.GetOutputStream(),
+ OptionValue::eDumpGroupValue);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsWrite -- Write settings to file
+
+static constexpr OptionDefinition g_settings_write_options[] = {
+#define LLDB_OPTIONS_settings_write
+#include "CommandOptions.inc"
+};
+
+class CommandObjectSettingsWrite : public CommandObjectParsed {
+public:
+ CommandObjectSettingsWrite(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "settings export",
+ "Write matching debugger settings and their "
+ "current values to a file that can be read in with "
+ "\"settings read\". Defaults to writing all settings.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry arg1;
+ CommandArgumentData var_name_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ }
+
+ ~CommandObjectSettingsWrite() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_filename.assign(option_arg);
+ break;
+ case 'a':
+ m_append = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filename.clear();
+ m_append = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_settings_write_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ std::string m_filename;
+ bool m_append = false;
+ };
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ FileSpec file_spec(m_options.m_filename);
+ FileSystem::Instance().Resolve(file_spec);
+ std::string path(file_spec.GetPath());
+ uint32_t options = File::OpenOptions::eOpenOptionWrite |
+ File::OpenOptions::eOpenOptionCanCreate;
+ if (m_options.m_append)
+ options |= File::OpenOptions::eOpenOptionAppend;
+ else
+ options |= File::OpenOptions::eOpenOptionTruncate;
+
+ StreamFile out_file(path.c_str(), options,
+ lldb::eFilePermissionsFileDefault);
+
+ if (!out_file.GetFile().IsValid()) {
+ result.AppendErrorWithFormat("%s: unable to write to file", path.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Exporting should not be context sensitive.
+ ExecutionContext clean_ctx;
+
+ if (args.empty()) {
+ GetDebugger().DumpAllPropertyValues(&clean_ctx, out_file,
+ OptionValue::eDumpGroupExport);
+ return result.Succeeded();
+ }
+
+ for (const auto &arg : args) {
+ Status error(GetDebugger().DumpPropertyValue(
+ &clean_ctx, out_file, arg.ref, OptionValue::eDumpGroupExport));
+ if (!error.Success()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectSettingsRead -- Read settings from file
+
+static constexpr OptionDefinition g_settings_read_options[] = {
+#define LLDB_OPTIONS_settings_read
+#include "CommandOptions.inc"
+};
+
+class CommandObjectSettingsRead : public CommandObjectParsed {
+public:
+ CommandObjectSettingsRead(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "settings read",
+ "Read settings previously saved to a file with \"settings write\".",
+ nullptr),
+ m_options() {}
+
+ ~CommandObjectSettingsRead() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_filename.assign(option_arg);
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filename.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_settings_read_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ std::string m_filename;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ FileSpec file(m_options.m_filename);
+ FileSystem::Instance().Resolve(file);
+ ExecutionContext clean_ctx;
+ CommandInterpreterRunOptions options;
+ options.SetAddToHistory(false);
+ options.SetEchoCommands(false);
+ options.SetPrintResults(true);
+ options.SetPrintErrors(true);
+ options.SetStopOnError(false);
+ m_interpreter.HandleCommandsFromFile(file, &clean_ctx, options, result);
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectSettingsList -- List settable variables
+
+class CommandObjectSettingsList : public CommandObjectParsed {
+public:
+ CommandObjectSettingsList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "settings list",
+ "List and describe matching debugger settings. "
+ "Defaults to all listing all settings.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData prefix_name_arg;
+
+ // Define the first variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatOptional;
+
+ // Define the second variant of this arg.
+ prefix_name_arg.arg_type = eArgTypeSettingPrefix;
+ prefix_name_arg.arg_repetition = eArgRepeatOptional;
+
+ arg.push_back(var_name_arg);
+ arg.push_back(prefix_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectSettingsList() override = default;
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ const bool will_modify = false;
+ const size_t argc = args.GetArgumentCount();
+ if (argc > 0) {
+ const bool dump_qualified_name = true;
+
+ // TODO: Convert to StringRef based enumeration. Requires converting
+ // GetPropertyAtPath first.
+ for (size_t i = 0; i < argc; ++i) {
+ const char *property_path = args.GetArgumentAtIndex(i);
+
+ const Property *property =
+ GetDebugger().GetValueProperties()->GetPropertyAtPath(
+ &m_exe_ctx, will_modify, property_path);
+
+ if (property) {
+ property->DumpDescription(m_interpreter, result.GetOutputStream(), 0,
+ dump_qualified_name);
+ } else {
+ result.AppendErrorWithFormat("invalid property path '%s'",
+ property_path);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ } else {
+ GetDebugger().DumpAllDescriptions(m_interpreter,
+ result.GetOutputStream());
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsRemove
+
+class CommandObjectSettingsRemove : public CommandObjectRaw {
+public:
+ CommandObjectSettingsRemove(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings remove",
+ "Remove a value from a setting, specified by array "
+ "index or dictionary key.") {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData index_arg;
+ CommandArgumentData key_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first variant of this arg.
+ index_arg.arg_type = eArgTypeSettingIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // Define the second variant of this arg.
+ key_arg.arg_type = eArgTypeSettingKey;
+ key_arg.arg_repetition = eArgRepeatPlain;
+
+ // Push both variants into this arg
+ arg2.push_back(index_arg);
+ arg2.push_back(key_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectSettingsRemove() override = default;
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ Args cmd_args(command);
+
+ // Process possible options.
+ if (!ParseOptions(cmd_args, result))
+ return false;
+
+ const size_t argc = cmd_args.GetArgumentCount();
+ if (argc == 0) {
+ result.AppendError("'settings set' takes an array or dictionary item, or "
+ "an array followed by one or more indexes, or a "
+ "dictionary followed by one or more key names to "
+ "remove");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError(
+ "'settings set' command requires a valid variable name");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Split the raw command into var_name and value pair.
+ llvm::StringRef raw_str(command);
+ std::string var_value_string = raw_str.split(var_name).second.str();
+ const char *var_value_cstr =
+ Args::StripSpaces(var_value_string, true, true, false);
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationRemove, var_name, var_value_cstr));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsReplace
+
+class CommandObjectSettingsReplace : public CommandObjectRaw {
+public:
+ CommandObjectSettingsReplace(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings replace",
+ "Replace the debugger setting value specified by "
+ "array index or dictionary key.") {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentEntry arg3;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData index_arg;
+ CommandArgumentData key_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first (variant of this arg.
+ index_arg.arg_type = eArgTypeSettingIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // Define the second (variant of this arg.
+ key_arg.arg_type = eArgTypeSettingKey;
+ key_arg.arg_repetition = eArgRepeatPlain;
+
+ // Put both variants into this arg
+ arg2.push_back(index_arg);
+ arg2.push_back(key_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg3.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ m_arguments.push_back(arg3);
+ }
+
+ ~CommandObjectSettingsReplace() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Attempting to complete variable name
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ Args cmd_args(command);
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError("'settings replace' command requires a valid variable "
+ "name; No value supplied");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Split the raw command into var_name, index_value, and value triple.
+ llvm::StringRef raw_str(command);
+ std::string var_value_string = raw_str.split(var_name).second.str();
+ const char *var_value_cstr =
+ Args::StripSpaces(var_value_string, true, true, false);
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationReplace, var_name, var_value_cstr));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsInsertBefore
+
+class CommandObjectSettingsInsertBefore : public CommandObjectRaw {
+public:
+ CommandObjectSettingsInsertBefore(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings insert-before",
+ "Insert one or more values into an debugger array "
+ "setting immediately before the specified element "
+ "index.") {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentEntry arg3;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData index_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first (variant of this arg.
+ index_arg.arg_type = eArgTypeSettingIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(index_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg3.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ m_arguments.push_back(arg3);
+ }
+
+ ~CommandObjectSettingsInsertBefore() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Attempting to complete variable name
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ Args cmd_args(command);
+ const size_t argc = cmd_args.GetArgumentCount();
+
+ if (argc < 3) {
+ result.AppendError("'settings insert-before' takes more arguments");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError("'settings insert-before' command requires a valid "
+ "variable name; No value supplied");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Split the raw command into var_name, index_value, and value triple.
+ llvm::StringRef raw_str(command);
+ std::string var_value_string = raw_str.split(var_name).second.str();
+ const char *var_value_cstr =
+ Args::StripSpaces(var_value_string, true, true, false);
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationInsertBefore, var_name, var_value_cstr));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingInsertAfter
+
+class CommandObjectSettingsInsertAfter : public CommandObjectRaw {
+public:
+ CommandObjectSettingsInsertAfter(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings insert-after",
+ "Insert one or more values into a debugger array "
+ "settings after the specified element index.") {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentEntry arg3;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData index_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first (variant of this arg.
+ index_arg.arg_type = eArgTypeSettingIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(index_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg3.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ m_arguments.push_back(arg3);
+ }
+
+ ~CommandObjectSettingsInsertAfter() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Attempting to complete variable name
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ Args cmd_args(command);
+ const size_t argc = cmd_args.GetArgumentCount();
+
+ if (argc < 3) {
+ result.AppendError("'settings insert-after' takes more arguments");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError("'settings insert-after' command requires a valid "
+ "variable name; No value supplied");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Split the raw command into var_name, index_value, and value triple.
+ llvm::StringRef raw_str(command);
+ std::string var_value_string = raw_str.split(var_name).second.str();
+ const char *var_value_cstr =
+ Args::StripSpaces(var_value_string, true, true, false);
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationInsertAfter, var_name, var_value_cstr));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsAppend
+
+class CommandObjectSettingsAppend : public CommandObjectRaw {
+public:
+ CommandObjectSettingsAppend(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "settings append",
+ "Append one or more values to a debugger array, "
+ "dictionary, or string setting.") {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData var_name_arg;
+ CommandArgumentData value_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg1.push_back(var_name_arg);
+
+ // Define the first (and only) variant of this arg.
+ value_arg.arg_type = eArgTypeValue;
+ value_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg2.push_back(value_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectSettingsAppend() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Attempting to complete variable name
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ Args cmd_args(command);
+ const size_t argc = cmd_args.GetArgumentCount();
+
+ if (argc < 2) {
+ result.AppendError("'settings append' takes more arguments");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *var_name = cmd_args.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError("'settings append' command requires a valid variable "
+ "name; No value supplied");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Do not perform cmd_args.Shift() since StringRef is manipulating the raw
+ // character string later on.
+
+ // Split the raw command into var_name and value pair.
+ llvm::StringRef raw_str(command);
+ std::string var_value_string = raw_str.split(var_name).second.str();
+ const char *var_value_cstr =
+ Args::StripSpaces(var_value_string, true, true, false);
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationAppend, var_name, var_value_cstr));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectSettingsClear
+
+class CommandObjectSettingsClear : public CommandObjectParsed {
+public:
+ CommandObjectSettingsClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "settings clear",
+ "Clear a debugger setting array, dictionary, or string.", nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData var_name_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeSettingVariableName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(var_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectSettingsClear() override = default;
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ // Attempting to complete variable name
+ if (request.GetCursorIndex() < 2)
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion,
+ request, nullptr);
+
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc != 1) {
+ result.AppendError("'settings clear' takes exactly one argument");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *var_name = command.GetArgumentAtIndex(0);
+ if ((var_name == nullptr) || (var_name[0] == '\0')) {
+ result.AppendError("'settings clear' command requires a valid variable "
+ "name; No value supplied");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Status error(GetDebugger().SetPropertyValue(
+ &m_exe_ctx, eVarSetOperationClear, var_name, llvm::StringRef()));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectMultiwordSettings
+
+CommandObjectMultiwordSettings::CommandObjectMultiwordSettings(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "settings",
+ "Commands for managing LLDB settings.",
+ "settings <subcommand> [<command-options>]") {
+ LoadSubCommand("set",
+ CommandObjectSP(new CommandObjectSettingsSet(interpreter)));
+ LoadSubCommand("show",
+ CommandObjectSP(new CommandObjectSettingsShow(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectSettingsList(interpreter)));
+ LoadSubCommand("remove",
+ CommandObjectSP(new CommandObjectSettingsRemove(interpreter)));
+ LoadSubCommand("replace", CommandObjectSP(
+ new CommandObjectSettingsReplace(interpreter)));
+ LoadSubCommand(
+ "insert-before",
+ CommandObjectSP(new CommandObjectSettingsInsertBefore(interpreter)));
+ LoadSubCommand(
+ "insert-after",
+ CommandObjectSP(new CommandObjectSettingsInsertAfter(interpreter)));
+ LoadSubCommand("append",
+ CommandObjectSP(new CommandObjectSettingsAppend(interpreter)));
+ LoadSubCommand("clear",
+ CommandObjectSP(new CommandObjectSettingsClear(interpreter)));
+ LoadSubCommand("write",
+ CommandObjectSP(new CommandObjectSettingsWrite(interpreter)));
+ LoadSubCommand("read",
+ CommandObjectSP(new CommandObjectSettingsRead(interpreter)));
+}
+
+CommandObjectMultiwordSettings::~CommandObjectMultiwordSettings() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.h
new file mode 100644
index 000000000000..730425953ea7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSettings.h
@@ -0,0 +1,29 @@
+//===-- CommandObjectSettings.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectSettings_h_
+#define liblldb_CommandObjectSettings_h_
+
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/Options.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordSettings
+
+class CommandObjectMultiwordSettings : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordSettings(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordSettings() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectSettings_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.cpp
new file mode 100644
index 000000000000..1b515d0f1099
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.cpp
@@ -0,0 +1,1305 @@
+//===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectSource.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FileLineResolver.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/TargetList.h"
+#include "lldb/Utility/FileSpec.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark CommandObjectSourceInfo
+// CommandObjectSourceInfo - debug line entries dumping command
+
+static constexpr OptionDefinition g_source_info_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount, "The number of line entries to display." },
+ { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "shlib", 's', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Look up the source in the given module or shared library (can be specified more than once)." },
+ { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source." },
+ { LLDB_OPT_SET_1, false, "line", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLineNum, "The line number at which to start the displaying lines." },
+ { LLDB_OPT_SET_1, false, "end-line", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLineNum, "The line number at which to stop displaying lines." },
+ { LLDB_OPT_SET_2, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeSymbol, "The name of a function whose source to display." },
+ { LLDB_OPT_SET_3, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Lookup the address and display the source information for the corresponding file and line." },
+ // clang-format on
+};
+
+class CommandObjectSourceInfo : public CommandObjectParsed {
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = GetDefinitions()[option_idx].short_option;
+ switch (short_option) {
+ case 'l':
+ if (option_arg.getAsInteger(0, start_line))
+ error.SetErrorStringWithFormat("invalid line number: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'e':
+ if (option_arg.getAsInteger(0, end_line))
+ error.SetErrorStringWithFormat("invalid line number: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'c':
+ if (option_arg.getAsInteger(0, num_lines))
+ error.SetErrorStringWithFormat("invalid line count: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'f':
+ file_name = option_arg;
+ break;
+
+ case 'n':
+ symbol_name = option_arg;
+ break;
+
+ case 'a': {
+ address = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ } break;
+ case 's':
+ modules.push_back(std::string(option_arg));
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ file_spec.Clear();
+ file_name.clear();
+ symbol_name.clear();
+ address = LLDB_INVALID_ADDRESS;
+ start_line = 0;
+ end_line = 0;
+ num_lines = 0;
+ modules.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_source_info_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ FileSpec file_spec;
+ std::string file_name;
+ std::string symbol_name;
+ lldb::addr_t address;
+ uint32_t start_line;
+ uint32_t end_line;
+ uint32_t num_lines;
+ STLStringArray modules;
+ };
+
+public:
+ CommandObjectSourceInfo(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "source info",
+ "Display source line information for the current target "
+ "process. Defaults to instruction pointer in current stack "
+ "frame.",
+ nullptr, eCommandRequiresTarget),
+ m_options() {}
+
+ ~CommandObjectSourceInfo() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ // Dump the line entries in each symbol context. Return the number of entries
+ // found. If module_list is set, only dump lines contained in one of the
+ // modules. If file_spec is set, only dump lines in the file. If the
+ // start_line option was specified, don't print lines less than start_line.
+ // If the end_line option was specified, don't print lines greater than
+ // end_line. If the num_lines option was specified, dont print more than
+ // num_lines entries.
+ uint32_t DumpLinesInSymbolContexts(Stream &strm,
+ const SymbolContextList &sc_list,
+ const ModuleList &module_list,
+ const FileSpec &file_spec) {
+ uint32_t start_line = m_options.start_line;
+ uint32_t end_line = m_options.end_line;
+ uint32_t num_lines = m_options.num_lines;
+ Target *target = m_exe_ctx.GetTargetPtr();
+
+ uint32_t num_matches = 0;
+ bool has_path = false;
+ if (file_spec) {
+ assert(file_spec.GetFilename().AsCString());
+ has_path = (file_spec.GetDirectory().AsCString() != nullptr);
+ }
+
+ // Dump all the line entries for the file in the list.
+ ConstString last_module_file_name;
+ uint32_t num_scs = sc_list.GetSize();
+ for (uint32_t i = 0; i < num_scs; ++i) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ if (sc.comp_unit) {
+ Module *module = sc.module_sp.get();
+ CompileUnit *cu = sc.comp_unit;
+ const LineEntry &line_entry = sc.line_entry;
+ assert(module && cu);
+
+ // Are we looking for specific modules, files or lines?
+ if (module_list.GetSize() &&
+ module_list.GetIndexForModule(module) == LLDB_INVALID_INDEX32)
+ continue;
+ if (file_spec &&
+ !lldb_private::FileSpec::Equal(file_spec, line_entry.file,
+ has_path))
+ continue;
+ if (start_line > 0 && line_entry.line < start_line)
+ continue;
+ if (end_line > 0 && line_entry.line > end_line)
+ continue;
+ if (num_lines > 0 && num_matches > num_lines)
+ continue;
+
+ // Print a new header if the module changed.
+ ConstString module_file_name =
+ module->GetFileSpec().GetFilename();
+ assert(module_file_name);
+ if (module_file_name != last_module_file_name) {
+ if (num_matches > 0)
+ strm << "\n\n";
+ strm << "Lines found in module `" << module_file_name << "\n";
+ }
+ // Dump the line entry.
+ line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu,
+ target, /*show_address_only=*/false);
+ strm << "\n";
+ last_module_file_name = module_file_name;
+ num_matches++;
+ }
+ }
+ return num_matches;
+ }
+
+ // Dump the requested line entries for the file in the compilation unit.
+ // Return the number of entries found. If module_list is set, only dump lines
+ // contained in one of the modules. If the start_line option was specified,
+ // don't print lines less than start_line. If the end_line option was
+ // specified, don't print lines greater than end_line. If the num_lines
+ // option was specified, dont print more than num_lines entries.
+ uint32_t DumpFileLinesInCompUnit(Stream &strm, Module *module,
+ CompileUnit *cu, const FileSpec &file_spec) {
+ uint32_t start_line = m_options.start_line;
+ uint32_t end_line = m_options.end_line;
+ uint32_t num_lines = m_options.num_lines;
+ Target *target = m_exe_ctx.GetTargetPtr();
+
+ uint32_t num_matches = 0;
+ assert(module);
+ if (cu) {
+ assert(file_spec.GetFilename().AsCString());
+ bool has_path = (file_spec.GetDirectory().AsCString() != nullptr);
+ const FileSpecList &cu_file_list = cu->GetSupportFiles();
+ size_t file_idx = cu_file_list.FindFileIndex(0, file_spec, has_path);
+ if (file_idx != UINT32_MAX) {
+ // Update the file to how it appears in the CU.
+ const FileSpec &cu_file_spec =
+ cu_file_list.GetFileSpecAtIndex(file_idx);
+
+ // Dump all matching lines at or above start_line for the file in the
+ // CU.
+ ConstString file_spec_name = file_spec.GetFilename();
+ ConstString module_file_name =
+ module->GetFileSpec().GetFilename();
+ bool cu_header_printed = false;
+ uint32_t line = start_line;
+ while (true) {
+ LineEntry line_entry;
+
+ // Find the lowest index of a line entry with a line equal to or
+ // higher than 'line'.
+ uint32_t start_idx = 0;
+ start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec,
+ /*exact=*/false, &line_entry);
+ if (start_idx == UINT32_MAX)
+ // No more line entries for our file in this CU.
+ break;
+
+ if (end_line > 0 && line_entry.line > end_line)
+ break;
+
+ // Loop through to find any other entries for this line, dumping
+ // each.
+ line = line_entry.line;
+ do {
+ num_matches++;
+ if (num_lines > 0 && num_matches > num_lines)
+ break;
+ assert(lldb_private::FileSpec::Equal(cu_file_spec, line_entry.file,
+ has_path));
+ if (!cu_header_printed) {
+ if (num_matches > 0)
+ strm << "\n\n";
+ strm << "Lines found for file " << file_spec_name
+ << " in compilation unit " << cu->GetFilename() << " in `"
+ << module_file_name << "\n";
+ cu_header_printed = true;
+ }
+ line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu,
+ target, /*show_address_only=*/false);
+ strm << "\n";
+
+ // Anymore after this one?
+ start_idx++;
+ start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec,
+ /*exact=*/true, &line_entry);
+ } while (start_idx != UINT32_MAX);
+
+ // Try the next higher line, starting over at start_idx 0.
+ line++;
+ }
+ }
+ }
+ return num_matches;
+ }
+
+ // Dump the requested line entries for the file in the module. Return the
+ // number of entries found. If module_list is set, only dump lines contained
+ // in one of the modules. If the start_line option was specified, don't print
+ // lines less than start_line. If the end_line option was specified, don't
+ // print lines greater than end_line. If the num_lines option was specified,
+ // dont print more than num_lines entries.
+ uint32_t DumpFileLinesInModule(Stream &strm, Module *module,
+ const FileSpec &file_spec) {
+ uint32_t num_matches = 0;
+ if (module) {
+ // Look through all the compilation units (CUs) in this module for ones
+ // that contain lines of code from this source file.
+ for (size_t i = 0; i < module->GetNumCompileUnits(); i++) {
+ // Look for a matching source file in this CU.
+ CompUnitSP cu_sp(module->GetCompileUnitAtIndex(i));
+ if (cu_sp) {
+ num_matches +=
+ DumpFileLinesInCompUnit(strm, module, cu_sp.get(), file_spec);
+ }
+ }
+ }
+ return num_matches;
+ }
+
+ // Given an address and a list of modules, append the symbol contexts of all
+ // line entries containing the address found in the modules and return the
+ // count of matches. If none is found, return an error in 'error_strm'.
+ size_t GetSymbolContextsForAddress(const ModuleList &module_list,
+ lldb::addr_t addr,
+ SymbolContextList &sc_list,
+ StreamString &error_strm) {
+ Address so_addr;
+ size_t num_matches = 0;
+ assert(module_list.GetSize() > 0);
+ Target *target = m_exe_ctx.GetTargetPtr();
+ if (target->GetSectionLoadList().IsEmpty()) {
+ // The target isn't loaded yet, we need to lookup the file address in all
+ // modules. Note: the module list option does not apply to addresses.
+ const size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; ++i) {
+ ModuleSP module_sp(module_list.GetModuleAtIndex(i));
+ if (!module_sp)
+ continue;
+ if (module_sp->ResolveFileAddress(addr, so_addr)) {
+ SymbolContext sc;
+ sc.Clear(true);
+ if (module_sp->ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, sc) &
+ eSymbolContextLineEntry) {
+ sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false);
+ ++num_matches;
+ }
+ }
+ }
+ if (num_matches == 0)
+ error_strm.Printf("Source information for file address 0x%" PRIx64
+ " not found in any modules.\n",
+ addr);
+ } else {
+ // The target has some things loaded, resolve this address to a compile
+ // unit + file + line and display
+ if (target->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) {
+ ModuleSP module_sp(so_addr.GetModule());
+ // Check to make sure this module is in our list.
+ if (module_sp &&
+ module_list.GetIndexForModule(module_sp.get()) !=
+ LLDB_INVALID_INDEX32) {
+ SymbolContext sc;
+ sc.Clear(true);
+ if (module_sp->ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, sc) &
+ eSymbolContextLineEntry) {
+ sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false);
+ ++num_matches;
+ } else {
+ StreamString addr_strm;
+ so_addr.Dump(&addr_strm, nullptr,
+ Address::DumpStyleModuleWithFileAddress);
+ error_strm.Printf(
+ "Address 0x%" PRIx64 " resolves to %s, but there is"
+ " no source information available for this address.\n",
+ addr, addr_strm.GetData());
+ }
+ } else {
+ StreamString addr_strm;
+ so_addr.Dump(&addr_strm, nullptr,
+ Address::DumpStyleModuleWithFileAddress);
+ error_strm.Printf("Address 0x%" PRIx64
+ " resolves to %s, but it cannot"
+ " be found in any modules.\n",
+ addr, addr_strm.GetData());
+ }
+ } else
+ error_strm.Printf("Unable to resolve address 0x%" PRIx64 ".\n", addr);
+ }
+ return num_matches;
+ }
+
+ // Dump the line entries found in functions matching the name specified in
+ // the option.
+ bool DumpLinesInFunctions(CommandReturnObject &result) {
+ SymbolContextList sc_list_funcs;
+ ConstString name(m_options.symbol_name.c_str());
+ SymbolContextList sc_list_lines;
+ Target *target = m_exe_ctx.GetTargetPtr();
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+
+ // Note: module_list can't be const& because FindFunctionSymbols isn't
+ // const.
+ ModuleList module_list =
+ (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages();
+ size_t num_matches =
+ module_list.FindFunctions(name, eFunctionNameTypeAuto,
+ /*include_symbols=*/false,
+ /*include_inlines=*/true,
+ /*append=*/true, sc_list_funcs);
+ if (!num_matches) {
+ // If we didn't find any functions with that name, try searching for
+ // symbols that line up exactly with function addresses.
+ SymbolContextList sc_list_symbols;
+ size_t num_symbol_matches = module_list.FindFunctionSymbols(
+ name, eFunctionNameTypeAuto, sc_list_symbols);
+ for (size_t i = 0; i < num_symbol_matches; i++) {
+ SymbolContext sc;
+ sc_list_symbols.GetContextAtIndex(i, sc);
+ if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ const Address &base_address = sc.symbol->GetAddressRef();
+ Function *function = base_address.CalculateSymbolContextFunction();
+ if (function) {
+ sc_list_funcs.Append(SymbolContext(function));
+ num_matches++;
+ }
+ }
+ }
+ }
+ if (num_matches == 0) {
+ result.AppendErrorWithFormat("Could not find function named \'%s\'.\n",
+ m_options.symbol_name.c_str());
+ return false;
+ }
+ for (size_t i = 0; i < num_matches; i++) {
+ SymbolContext sc;
+ sc_list_funcs.GetContextAtIndex(i, sc);
+ bool context_found_for_symbol = false;
+ // Loop through all the ranges in the function.
+ AddressRange range;
+ for (uint32_t r = 0;
+ sc.GetAddressRange(eSymbolContextEverything, r,
+ /*use_inline_block_range=*/true, range);
+ ++r) {
+ // Append the symbol contexts for each address in the range to
+ // sc_list_lines.
+ const Address &base_address = range.GetBaseAddress();
+ const addr_t size = range.GetByteSize();
+ lldb::addr_t start_addr = base_address.GetLoadAddress(target);
+ if (start_addr == LLDB_INVALID_ADDRESS)
+ start_addr = base_address.GetFileAddress();
+ lldb::addr_t end_addr = start_addr + size;
+ for (lldb::addr_t addr = start_addr; addr < end_addr;
+ addr += addr_byte_size) {
+ StreamString error_strm;
+ if (!GetSymbolContextsForAddress(module_list, addr, sc_list_lines,
+ error_strm))
+ result.AppendWarningWithFormat("in symbol '%s': %s",
+ sc.GetFunctionName().AsCString(),
+ error_strm.GetData());
+ else
+ context_found_for_symbol = true;
+ }
+ }
+ if (!context_found_for_symbol)
+ result.AppendWarningWithFormat("Unable to find line information"
+ " for matching symbol '%s'.\n",
+ sc.GetFunctionName().AsCString());
+ }
+ if (sc_list_lines.GetSize() == 0) {
+ result.AppendErrorWithFormat("No line information could be found"
+ " for any symbols matching '%s'.\n",
+ name.AsCString());
+ return false;
+ }
+ FileSpec file_spec;
+ if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list_lines,
+ module_list, file_spec)) {
+ result.AppendErrorWithFormat(
+ "Unable to dump line information for symbol '%s'.\n",
+ name.AsCString());
+ return false;
+ }
+ return true;
+ }
+
+ // Dump the line entries found for the address specified in the option.
+ bool DumpLinesForAddress(CommandReturnObject &result) {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ SymbolContextList sc_list;
+
+ StreamString error_strm;
+ if (!GetSymbolContextsForAddress(target->GetImages(), m_options.address,
+ sc_list, error_strm)) {
+ result.AppendErrorWithFormat("%s.\n", error_strm.GetData());
+ return false;
+ }
+ ModuleList module_list;
+ FileSpec file_spec;
+ if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list,
+ module_list, file_spec)) {
+ result.AppendErrorWithFormat("No modules contain load address 0x%" PRIx64
+ ".\n",
+ m_options.address);
+ return false;
+ }
+ return true;
+ }
+
+ // Dump the line entries found in the file specified in the option.
+ bool DumpLinesForFile(CommandReturnObject &result) {
+ FileSpec file_spec(m_options.file_name);
+ const char *filename = m_options.file_name.c_str();
+ Target *target = m_exe_ctx.GetTargetPtr();
+ const ModuleList &module_list =
+ (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages();
+
+ bool displayed_something = false;
+ const size_t num_modules = module_list.GetSize();
+ for (uint32_t i = 0; i < num_modules; ++i) {
+ // Dump lines for this module.
+ Module *module = module_list.GetModulePointerAtIndex(i);
+ assert(module);
+ if (DumpFileLinesInModule(result.GetOutputStream(), module, file_spec))
+ displayed_something = true;
+ }
+ if (!displayed_something) {
+ result.AppendErrorWithFormat("No source filenames matched '%s'.\n",
+ filename);
+ return false;
+ }
+ return true;
+ }
+
+ // Dump the line entries for the current frame.
+ bool DumpLinesForFrame(CommandReturnObject &result) {
+ StackFrame *cur_frame = m_exe_ctx.GetFramePtr();
+ if (cur_frame == nullptr) {
+ result.AppendError(
+ "No selected frame to use to find the default source.");
+ return false;
+ } else if (!cur_frame->HasDebugInformation()) {
+ result.AppendError("No debug info for the selected frame.");
+ return false;
+ } else {
+ const SymbolContext &sc =
+ cur_frame->GetSymbolContext(eSymbolContextLineEntry);
+ SymbolContextList sc_list;
+ sc_list.Append(sc);
+ ModuleList module_list;
+ FileSpec file_spec;
+ if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list,
+ module_list, file_spec)) {
+ result.AppendError(
+ "No source line info available for the selected frame.");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc != 0) {
+ result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n",
+ GetCommandName().str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Target *target = m_exe_ctx.GetTargetPtr();
+ if (target == nullptr) {
+ target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ // Collect the list of modules to search.
+ m_module_list.Clear();
+ if (!m_options.modules.empty()) {
+ for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) {
+ FileSpec module_file_spec(m_options.modules[i]);
+ if (module_file_spec) {
+ ModuleSpec module_spec(module_file_spec);
+ if (target->GetImages().FindModules(module_spec, m_module_list) == 0)
+ result.AppendWarningWithFormat("No module found for '%s'.\n",
+ m_options.modules[i].c_str());
+ }
+ }
+ if (!m_module_list.GetSize()) {
+ result.AppendError("No modules match the input.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else if (target->GetImages().GetSize() == 0) {
+ result.AppendError("The target has no associated executable images.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Check the arguments to see what lines we should dump.
+ if (!m_options.symbol_name.empty()) {
+ // Print lines for symbol.
+ if (DumpLinesInFunctions(result))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ } else if (m_options.address != LLDB_INVALID_ADDRESS) {
+ // Print lines for an address.
+ if (DumpLinesForAddress(result))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ } else if (!m_options.file_name.empty()) {
+ // Dump lines for a file.
+ if (DumpLinesForFile(result))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ // Dump the line for the current frame.
+ if (DumpLinesForFrame(result))
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+ ModuleList m_module_list;
+};
+
+#pragma mark CommandObjectSourceList
+// CommandObjectSourceList
+
+static constexpr OptionDefinition g_source_list_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount, "The number of source lines to display." },
+ { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "shlib", 's', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Look up the source file in the given shared library." },
+ { LLDB_OPT_SET_ALL, false, "show-breakpoints", 'b', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Show the line table locations from the debug information that indicate valid places to set source level breakpoints." },
+ { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source." },
+ { LLDB_OPT_SET_1, false, "line", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLineNum, "The line number at which to start the display source." },
+ { LLDB_OPT_SET_2, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeSymbol, "The name of a function whose source to display." },
+ { LLDB_OPT_SET_3, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Lookup the address and display the source information for the corresponding file and line." },
+ { LLDB_OPT_SET_4, false, "reverse", 'r', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Reverse the listing to look backwards from the last displayed block of source." },
+ // clang-format on
+};
+
+class CommandObjectSourceList : public CommandObjectParsed {
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = GetDefinitions()[option_idx].short_option;
+ switch (short_option) {
+ case 'l':
+ if (option_arg.getAsInteger(0, start_line))
+ error.SetErrorStringWithFormat("invalid line number: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'c':
+ if (option_arg.getAsInteger(0, num_lines))
+ error.SetErrorStringWithFormat("invalid line count: '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'f':
+ file_name = option_arg;
+ break;
+
+ case 'n':
+ symbol_name = option_arg;
+ break;
+
+ case 'a': {
+ address = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ } break;
+ case 's':
+ modules.push_back(std::string(option_arg));
+ break;
+
+ case 'b':
+ show_bp_locs = true;
+ break;
+ case 'r':
+ reverse = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ file_spec.Clear();
+ file_name.clear();
+ symbol_name.clear();
+ address = LLDB_INVALID_ADDRESS;
+ start_line = 0;
+ num_lines = 0;
+ show_bp_locs = false;
+ reverse = false;
+ modules.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_source_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ FileSpec file_spec;
+ std::string file_name;
+ std::string symbol_name;
+ lldb::addr_t address;
+ uint32_t start_line;
+ uint32_t num_lines;
+ STLStringArray modules;
+ bool show_bp_locs;
+ bool reverse;
+ };
+
+public:
+ CommandObjectSourceList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "source list",
+ "Display source code for the current target "
+ "process as specified by options.",
+ nullptr, eCommandRequiresTarget),
+ m_options() {}
+
+ ~CommandObjectSourceList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ const char *GetRepeatCommand(Args &current_command_args,
+ uint32_t index) override {
+ // This is kind of gross, but the command hasn't been parsed yet so we
+ // can't look at the option values for this invocation... I have to scan
+ // the arguments directly.
+ auto iter =
+ llvm::find_if(current_command_args, [](const Args::ArgEntry &e) {
+ return e.ref == "-r" || e.ref == "--reverse";
+ });
+ if (iter == current_command_args.end())
+ return m_cmd_name.c_str();
+
+ if (m_reverse_name.empty()) {
+ m_reverse_name = m_cmd_name;
+ m_reverse_name.append(" -r");
+ }
+ return m_reverse_name.c_str();
+ }
+
+protected:
+ struct SourceInfo {
+ ConstString function;
+ LineEntry line_entry;
+
+ SourceInfo(ConstString name, const LineEntry &line_entry)
+ : function(name), line_entry(line_entry) {}
+
+ SourceInfo() : function(), line_entry() {}
+
+ bool IsValid() const { return (bool)function && line_entry.IsValid(); }
+
+ bool operator==(const SourceInfo &rhs) const {
+ return function == rhs.function &&
+ line_entry.original_file == rhs.line_entry.original_file &&
+ line_entry.line == rhs.line_entry.line;
+ }
+
+ bool operator!=(const SourceInfo &rhs) const {
+ return function != rhs.function ||
+ line_entry.original_file != rhs.line_entry.original_file ||
+ line_entry.line != rhs.line_entry.line;
+ }
+
+ bool operator<(const SourceInfo &rhs) const {
+ if (function.GetCString() < rhs.function.GetCString())
+ return true;
+ if (line_entry.file.GetDirectory().GetCString() <
+ rhs.line_entry.file.GetDirectory().GetCString())
+ return true;
+ if (line_entry.file.GetFilename().GetCString() <
+ rhs.line_entry.file.GetFilename().GetCString())
+ return true;
+ if (line_entry.line < rhs.line_entry.line)
+ return true;
+ return false;
+ }
+ };
+
+ size_t DisplayFunctionSource(const SymbolContext &sc, SourceInfo &source_info,
+ CommandReturnObject &result) {
+ if (!source_info.IsValid()) {
+ source_info.function = sc.GetFunctionName();
+ source_info.line_entry = sc.GetFunctionStartLineEntry();
+ }
+
+ if (sc.function) {
+ Target *target = m_exe_ctx.GetTargetPtr();
+
+ FileSpec start_file;
+ uint32_t start_line;
+ uint32_t end_line;
+ FileSpec end_file;
+
+ if (sc.block == nullptr) {
+ // Not an inlined function
+ sc.function->GetStartLineSourceInfo(start_file, start_line);
+ if (start_line == 0) {
+ result.AppendErrorWithFormat("Could not find line information for "
+ "start of function: \"%s\".\n",
+ source_info.function.GetCString());
+ result.SetStatus(eReturnStatusFailed);
+ return 0;
+ }
+ sc.function->GetEndLineSourceInfo(end_file, end_line);
+ } else {
+ // We have an inlined function
+ start_file = source_info.line_entry.file;
+ start_line = source_info.line_entry.line;
+ end_line = start_line + m_options.num_lines;
+ }
+
+ // This is a little hacky, but the first line table entry for a function
+ // points to the "{" that starts the function block. It would be nice to
+ // actually get the function declaration in there too. So back up a bit,
+ // but not further than what you're going to display.
+ uint32_t extra_lines;
+ if (m_options.num_lines >= 10)
+ extra_lines = 5;
+ else
+ extra_lines = m_options.num_lines / 2;
+ uint32_t line_no;
+ if (start_line <= extra_lines)
+ line_no = 1;
+ else
+ line_no = start_line - extra_lines;
+
+ // For fun, if the function is shorter than the number of lines we're
+ // supposed to display, only display the function...
+ if (end_line != 0) {
+ if (m_options.num_lines > end_line - line_no)
+ m_options.num_lines = end_line - line_no + extra_lines;
+ }
+
+ m_breakpoint_locations.Clear();
+
+ if (m_options.show_bp_locs) {
+ const bool show_inlines = true;
+ m_breakpoint_locations.Reset(start_file, 0, show_inlines);
+ SearchFilterForUnconstrainedSearches target_search_filter(
+ m_exe_ctx.GetTargetSP());
+ target_search_filter.Search(m_breakpoint_locations);
+ }
+
+ result.AppendMessageWithFormat("File: %s\n",
+ start_file.GetPath().c_str());
+ // We don't care about the column here.
+ const uint32_t column = 0;
+ return target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
+ start_file, line_no, column, 0, m_options.num_lines, "",
+ &result.GetOutputStream(), GetBreakpointLocations());
+ } else {
+ result.AppendErrorWithFormat(
+ "Could not find function info for: \"%s\".\n",
+ m_options.symbol_name.c_str());
+ }
+ return 0;
+ }
+
+ // From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols
+ // functions "take a possibly empty vector of strings which are names of
+ // modules, and run the two search functions on the subset of the full module
+ // list that matches the strings in the input vector". If we wanted to put
+ // these somewhere, there should probably be a module-filter-list that can be
+ // passed to the various ModuleList::Find* calls, which would either be a
+ // vector of string names or a ModuleSpecList.
+ size_t FindMatchingFunctions(Target *target, ConstString name,
+ SymbolContextList &sc_list) {
+ // Displaying the source for a symbol:
+ bool include_inlines = true;
+ bool append = true;
+ bool include_symbols = false;
+ size_t num_matches = 0;
+
+ if (m_options.num_lines == 0)
+ m_options.num_lines = 10;
+
+ const size_t num_modules = m_options.modules.size();
+ if (num_modules > 0) {
+ ModuleList matching_modules;
+ for (size_t i = 0; i < num_modules; ++i) {
+ FileSpec module_file_spec(m_options.modules[i]);
+ if (module_file_spec) {
+ ModuleSpec module_spec(module_file_spec);
+ matching_modules.Clear();
+ target->GetImages().FindModules(module_spec, matching_modules);
+ num_matches += matching_modules.FindFunctions(
+ name, eFunctionNameTypeAuto, include_symbols, include_inlines,
+ append, sc_list);
+ }
+ }
+ } else {
+ num_matches = target->GetImages().FindFunctions(
+ name, eFunctionNameTypeAuto, include_symbols, include_inlines, append,
+ sc_list);
+ }
+ return num_matches;
+ }
+
+ size_t FindMatchingFunctionSymbols(Target *target, ConstString name,
+ SymbolContextList &sc_list) {
+ size_t num_matches = 0;
+ const size_t num_modules = m_options.modules.size();
+ if (num_modules > 0) {
+ ModuleList matching_modules;
+ for (size_t i = 0; i < num_modules; ++i) {
+ FileSpec module_file_spec(m_options.modules[i]);
+ if (module_file_spec) {
+ ModuleSpec module_spec(module_file_spec);
+ matching_modules.Clear();
+ target->GetImages().FindModules(module_spec, matching_modules);
+ num_matches += matching_modules.FindFunctionSymbols(
+ name, eFunctionNameTypeAuto, sc_list);
+ }
+ }
+ } else {
+ num_matches = target->GetImages().FindFunctionSymbols(
+ name, eFunctionNameTypeAuto, sc_list);
+ }
+ return num_matches;
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc != 0) {
+ result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n",
+ GetCommandName().str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Target *target = m_exe_ctx.GetTargetPtr();
+
+ if (!m_options.symbol_name.empty()) {
+ SymbolContextList sc_list;
+ ConstString name(m_options.symbol_name.c_str());
+
+ // Displaying the source for a symbol. Search for function named name.
+ size_t num_matches = FindMatchingFunctions(target, name, sc_list);
+ if (!num_matches) {
+ // If we didn't find any functions with that name, try searching for
+ // symbols that line up exactly with function addresses.
+ SymbolContextList sc_list_symbols;
+ size_t num_symbol_matches =
+ FindMatchingFunctionSymbols(target, name, sc_list_symbols);
+ for (size_t i = 0; i < num_symbol_matches; i++) {
+ SymbolContext sc;
+ sc_list_symbols.GetContextAtIndex(i, sc);
+ if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ const Address &base_address = sc.symbol->GetAddressRef();
+ Function *function = base_address.CalculateSymbolContextFunction();
+ if (function) {
+ sc_list.Append(SymbolContext(function));
+ num_matches++;
+ break;
+ }
+ }
+ }
+ }
+
+ if (num_matches == 0) {
+ result.AppendErrorWithFormat("Could not find function named: \"%s\".\n",
+ m_options.symbol_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (num_matches > 1) {
+ std::set<SourceInfo> source_match_set;
+
+ bool displayed_something = false;
+ for (size_t i = 0; i < num_matches; i++) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ SourceInfo source_info(sc.GetFunctionName(),
+ sc.GetFunctionStartLineEntry());
+
+ if (source_info.IsValid()) {
+ if (source_match_set.find(source_info) == source_match_set.end()) {
+ source_match_set.insert(source_info);
+ if (DisplayFunctionSource(sc, source_info, result))
+ displayed_something = true;
+ }
+ }
+ }
+
+ if (displayed_something)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(0, sc);
+ SourceInfo source_info;
+
+ if (DisplayFunctionSource(sc, source_info, result)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ return result.Succeeded();
+ } else if (m_options.address != LLDB_INVALID_ADDRESS) {
+ Address so_addr;
+ StreamString error_strm;
+ SymbolContextList sc_list;
+
+ if (target->GetSectionLoadList().IsEmpty()) {
+ // The target isn't loaded yet, we need to lookup the file address in
+ // all modules
+ const ModuleList &module_list = target->GetImages();
+ const size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; ++i) {
+ ModuleSP module_sp(module_list.GetModuleAtIndex(i));
+ if (module_sp &&
+ module_sp->ResolveFileAddress(m_options.address, so_addr)) {
+ SymbolContext sc;
+ sc.Clear(true);
+ if (module_sp->ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, sc) &
+ eSymbolContextLineEntry)
+ sc_list.Append(sc);
+ }
+ }
+
+ if (sc_list.GetSize() == 0) {
+ result.AppendErrorWithFormat(
+ "no modules have source information for file address 0x%" PRIx64
+ ".\n",
+ m_options.address);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ // The target has some things loaded, resolve this address to a compile
+ // unit + file + line and display
+ if (target->GetSectionLoadList().ResolveLoadAddress(m_options.address,
+ so_addr)) {
+ ModuleSP module_sp(so_addr.GetModule());
+ if (module_sp) {
+ SymbolContext sc;
+ sc.Clear(true);
+ if (module_sp->ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, sc) &
+ eSymbolContextLineEntry) {
+ sc_list.Append(sc);
+ } else {
+ so_addr.Dump(&error_strm, nullptr,
+ Address::DumpStyleModuleWithFileAddress);
+ result.AppendErrorWithFormat("address resolves to %s, but there "
+ "is no line table information "
+ "available for this address.\n",
+ error_strm.GetData());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ }
+
+ if (sc_list.GetSize() == 0) {
+ result.AppendErrorWithFormat(
+ "no modules contain load address 0x%" PRIx64 ".\n",
+ m_options.address);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ uint32_t num_matches = sc_list.GetSize();
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ if (sc.comp_unit) {
+ if (m_options.show_bp_locs) {
+ m_breakpoint_locations.Clear();
+ const bool show_inlines = true;
+ m_breakpoint_locations.Reset(*sc.comp_unit, 0, show_inlines);
+ SearchFilterForUnconstrainedSearches target_search_filter(
+ target->shared_from_this());
+ target_search_filter.Search(m_breakpoint_locations);
+ }
+
+ bool show_fullpaths = true;
+ bool show_module = true;
+ bool show_inlined_frames = true;
+ const bool show_function_arguments = true;
+ const bool show_function_name = true;
+ sc.DumpStopContext(&result.GetOutputStream(),
+ m_exe_ctx.GetBestExecutionContextScope(),
+ sc.line_entry.range.GetBaseAddress(),
+ show_fullpaths, show_module, show_inlined_frames,
+ show_function_arguments, show_function_name);
+ result.GetOutputStream().EOL();
+
+ if (m_options.num_lines == 0)
+ m_options.num_lines = 10;
+
+ size_t lines_to_back_up =
+ m_options.num_lines >= 10 ? 5 : m_options.num_lines / 2;
+
+ const uint32_t column =
+ (GetDebugger().GetStopShowColumn() != eStopShowColumnNone)
+ ? sc.line_entry.column
+ : 0;
+ target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
+ sc.comp_unit, sc.line_entry.line, column, lines_to_back_up,
+ m_options.num_lines - lines_to_back_up, "->",
+ &result.GetOutputStream(), GetBreakpointLocations());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ }
+ } else if (m_options.file_name.empty()) {
+ // Last valid source manager context, or the current frame if no valid
+ // last context in source manager. One little trick here, if you type the
+ // exact same list command twice in a row, it is more likely because you
+ // typed it once, then typed it again
+ if (m_options.start_line == 0) {
+ if (target->GetSourceManager().DisplayMoreWithLineNumbers(
+ &result.GetOutputStream(), m_options.num_lines,
+ m_options.reverse, GetBreakpointLocations())) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ } else {
+ if (m_options.num_lines == 0)
+ m_options.num_lines = 10;
+
+ if (m_options.show_bp_locs) {
+ SourceManager::FileSP last_file_sp(
+ target->GetSourceManager().GetLastFile());
+ if (last_file_sp) {
+ const bool show_inlines = true;
+ m_breakpoint_locations.Reset(last_file_sp->GetFileSpec(), 0,
+ show_inlines);
+ SearchFilterForUnconstrainedSearches target_search_filter(
+ target->shared_from_this());
+ target_search_filter.Search(m_breakpoint_locations);
+ }
+ } else
+ m_breakpoint_locations.Clear();
+
+ const uint32_t column = 0;
+ if (target->GetSourceManager()
+ .DisplaySourceLinesWithLineNumbersUsingLastFile(
+ m_options.start_line, // Line to display
+ m_options.num_lines, // Lines after line to
+ UINT32_MAX, // Don't mark "line"
+ column,
+ "", // Don't mark "line"
+ &result.GetOutputStream(), GetBreakpointLocations())) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ }
+ } else {
+ const char *filename = m_options.file_name.c_str();
+
+ bool check_inlines = false;
+ SymbolContextList sc_list;
+ size_t num_matches = 0;
+
+ if (!m_options.modules.empty()) {
+ ModuleList matching_modules;
+ for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) {
+ FileSpec module_file_spec(m_options.modules[i]);
+ if (module_file_spec) {
+ ModuleSpec module_spec(module_file_spec);
+ matching_modules.Clear();
+ target->GetImages().FindModules(module_spec, matching_modules);
+ num_matches += matching_modules.ResolveSymbolContextForFilePath(
+ filename, 0, check_inlines,
+ SymbolContextItem(eSymbolContextModule |
+ eSymbolContextCompUnit),
+ sc_list);
+ }
+ }
+ } else {
+ num_matches = target->GetImages().ResolveSymbolContextForFilePath(
+ filename, 0, check_inlines,
+ eSymbolContextModule | eSymbolContextCompUnit, sc_list);
+ }
+
+ if (num_matches == 0) {
+ result.AppendErrorWithFormat("Could not find source file \"%s\".\n",
+ m_options.file_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (num_matches > 1) {
+ bool got_multiple = false;
+ FileSpec *test_cu_spec = nullptr;
+
+ for (unsigned i = 0; i < num_matches; i++) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ if (sc.comp_unit) {
+ if (test_cu_spec) {
+ if (test_cu_spec != static_cast<FileSpec *>(sc.comp_unit))
+ got_multiple = true;
+ break;
+ } else
+ test_cu_spec = sc.comp_unit;
+ }
+ }
+ if (got_multiple) {
+ result.AppendErrorWithFormat(
+ "Multiple source files found matching: \"%s.\"\n",
+ m_options.file_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(0, sc)) {
+ if (sc.comp_unit) {
+ if (m_options.show_bp_locs) {
+ const bool show_inlines = true;
+ m_breakpoint_locations.Reset(*sc.comp_unit, 0, show_inlines);
+ SearchFilterForUnconstrainedSearches target_search_filter(
+ target->shared_from_this());
+ target_search_filter.Search(m_breakpoint_locations);
+ } else
+ m_breakpoint_locations.Clear();
+
+ if (m_options.num_lines == 0)
+ m_options.num_lines = 10;
+ const uint32_t column = 0;
+ target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
+ sc.comp_unit, m_options.start_line, column,
+ 0, m_options.num_lines,
+ "", &result.GetOutputStream(), GetBreakpointLocations());
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n",
+ m_options.file_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ }
+ return result.Succeeded();
+ }
+
+ const SymbolContextList *GetBreakpointLocations() {
+ if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0)
+ return &m_breakpoint_locations.GetFileLineMatches();
+ return nullptr;
+ }
+
+ CommandOptions m_options;
+ FileLineResolver m_breakpoint_locations;
+ std::string m_reverse_name;
+};
+
+#pragma mark CommandObjectMultiwordSource
+// CommandObjectMultiwordSource
+
+CommandObjectMultiwordSource::CommandObjectMultiwordSource(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "source", "Commands for examining "
+ "source code described by "
+ "debug information for the "
+ "current target process.",
+ "source <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("info",
+ CommandObjectSP(new CommandObjectSourceInfo(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectSourceList(interpreter)));
+}
+
+CommandObjectMultiwordSource::~CommandObjectMultiwordSource() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.h
new file mode 100644
index 000000000000..d72122d55dc7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectSource.h
@@ -0,0 +1,30 @@
+//===-- CommandObjectSource.h.h -----------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectSource_h_
+#define liblldb_CommandObjectSource_h_
+
+#include "lldb/Core/STLUtils.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordSource
+
+class CommandObjectMultiwordSource : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordSource(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordSource() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectSource_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.cpp
new file mode 100644
index 000000000000..a73c2a8e0409
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.cpp
@@ -0,0 +1,106 @@
+//===-- CommandObjectStats.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectStats.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class CommandObjectStatsEnable : public CommandObjectParsed {
+public:
+ CommandObjectStatsEnable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "enable",
+ "Enable statistics collection", nullptr,
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectStatsEnable() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget();
+
+ if (target->GetCollectingStats()) {
+ result.AppendError("statistics already enabled");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ target->SetCollectingStats(true);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectStatsDisable : public CommandObjectParsed {
+public:
+ CommandObjectStatsDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "disable",
+ "Disable statistics collection", nullptr,
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectStatsDisable() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget();
+
+ if (!target->GetCollectingStats()) {
+ result.AppendError("need to enable statistics before disabling them");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ target->SetCollectingStats(false);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectStatsDump : public CommandObjectParsed {
+public:
+ CommandObjectStatsDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "dump", "Dump statistics results",
+ nullptr, eCommandProcessMustBePaused) {}
+
+ ~CommandObjectStatsDump() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget();
+
+ uint32_t i = 0;
+ for (auto &stat : target->GetStatistics()) {
+ result.AppendMessageWithFormat(
+ "%s : %u\n",
+ lldb_private::GetStatDescription(static_cast<lldb_private::StatisticKind>(i))
+ .c_str(),
+ stat);
+ i += 1;
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+CommandObjectStats::CommandObjectStats(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "statistics",
+ "Print statistics about a debugging session",
+ "statistics <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("enable",
+ CommandObjectSP(new CommandObjectStatsEnable(interpreter)));
+ LoadSubCommand("disable",
+ CommandObjectSP(new CommandObjectStatsDisable(interpreter)));
+ LoadSubCommand("dump",
+ CommandObjectSP(new CommandObjectStatsDump(interpreter)));
+}
+
+CommandObjectStats::~CommandObjectStats() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.h
new file mode 100644
index 000000000000..27e9a6ff865a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectStats.h
@@ -0,0 +1,24 @@
+//===-- CommandObjectStats.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectStats_h_
+#define liblldb_CommandObjectStats_h_
+
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+class CommandObjectStats : public CommandObjectMultiword {
+public:
+ CommandObjectStats(CommandInterpreter &interpreter);
+
+ ~CommandObjectStats() override;
+};
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectLanguage_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.cpp
new file mode 100644
index 000000000000..e913a28501f2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.cpp
@@ -0,0 +1,5050 @@
+//===-- CommandObjectTarget.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectTarget.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/DataFormatters/ValueObjectPrinter.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupArchitecture.h"
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+#include "lldb/Interpreter/OptionGroupFile.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionGroupPlatform.h"
+#include "lldb/Interpreter/OptionGroupString.h"
+#include "lldb/Interpreter/OptionGroupUInt64.h"
+#include "lldb/Interpreter/OptionGroupUUID.h"
+#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
+#include "lldb/Interpreter/OptionGroupVariable.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/LocateSymbolFile.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Timer.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatAdapters.h"
+
+#include <cerrno>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void DumpTargetInfo(uint32_t target_idx, Target *target,
+ const char *prefix_cstr,
+ bool show_stopped_process_status, Stream &strm) {
+ const ArchSpec &target_arch = target->GetArchitecture();
+
+ Module *exe_module = target->GetExecutableModulePointer();
+ char exe_path[PATH_MAX];
+ bool exe_valid = false;
+ if (exe_module)
+ exe_valid = exe_module->GetFileSpec().GetPath(exe_path, sizeof(exe_path));
+
+ if (!exe_valid)
+ ::strcpy(exe_path, "<none>");
+
+ strm.Printf("%starget #%u: %s", prefix_cstr ? prefix_cstr : "", target_idx,
+ exe_path);
+
+ uint32_t properties = 0;
+ if (target_arch.IsValid()) {
+ strm.Printf("%sarch=", properties++ > 0 ? ", " : " ( ");
+ target_arch.DumpTriple(strm);
+ properties++;
+ }
+ PlatformSP platform_sp(target->GetPlatform());
+ if (platform_sp)
+ strm.Printf("%splatform=%s", properties++ > 0 ? ", " : " ( ",
+ platform_sp->GetName().GetCString());
+
+ ProcessSP process_sp(target->GetProcessSP());
+ bool show_process_status = false;
+ if (process_sp) {
+ lldb::pid_t pid = process_sp->GetID();
+ StateType state = process_sp->GetState();
+ if (show_stopped_process_status)
+ show_process_status = StateIsStoppedState(state, true);
+ const char *state_cstr = StateAsCString(state);
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ strm.Printf("%spid=%" PRIu64, properties++ > 0 ? ", " : " ( ", pid);
+ strm.Printf("%sstate=%s", properties++ > 0 ? ", " : " ( ", state_cstr);
+ }
+ if (properties > 0)
+ strm.PutCString(" )\n");
+ else
+ strm.EOL();
+ if (show_process_status) {
+ const bool only_threads_with_stop_reason = true;
+ const uint32_t start_frame = 0;
+ const uint32_t num_frames = 1;
+ const uint32_t num_frames_with_source = 1;
+ const bool stop_format = false;
+ process_sp->GetStatus(strm);
+ process_sp->GetThreadStatus(strm, only_threads_with_stop_reason,
+ start_frame, num_frames,
+ num_frames_with_source, stop_format);
+ }
+}
+
+static uint32_t DumpTargetList(TargetList &target_list,
+ bool show_stopped_process_status, Stream &strm) {
+ const uint32_t num_targets = target_list.GetNumTargets();
+ if (num_targets) {
+ TargetSP selected_target_sp(target_list.GetSelectedTarget());
+ strm.PutCString("Current targets:\n");
+ for (uint32_t i = 0; i < num_targets; ++i) {
+ TargetSP target_sp(target_list.GetTargetAtIndex(i));
+ if (target_sp) {
+ bool is_selected = target_sp.get() == selected_target_sp.get();
+ DumpTargetInfo(i, target_sp.get(), is_selected ? "* " : " ",
+ show_stopped_process_status, strm);
+ }
+ }
+ }
+ return num_targets;
+}
+
+// Note that the negation in the argument name causes a slightly confusing
+// mapping of the enum values,
+static constexpr OptionEnumValueElement g_dependents_enumaration[] = {
+ {eLoadDependentsDefault, "default",
+ "Only load dependents when the target is an executable."},
+ {eLoadDependentsNo, "true",
+ "Don't load dependents, even if the target is an executable."},
+ {eLoadDependentsYes, "false",
+ "Load dependents, even if the target is not an executable."}};
+
+static constexpr OptionDefinition g_dependents_options[] = {
+ {LLDB_OPT_SET_1, false, "no-dependents", 'd',
+ OptionParser::eOptionalArgument, nullptr,
+ OptionEnumValues(g_dependents_enumaration), 0, eArgTypeValue,
+ "Whether or not to load dependents when creating a target. If the option "
+ "is not specified, the value is implicitly 'default'. If the option is "
+ "specified but without a value, the value is implicitly 'true'."}};
+
+class OptionGroupDependents : public OptionGroup {
+public:
+ OptionGroupDependents() {}
+
+ ~OptionGroupDependents() override {}
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_dependents_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ // For compatibility no value means don't load dependents.
+ if (option_value.empty()) {
+ m_load_dependent_files = eLoadDependentsNo;
+ return error;
+ }
+
+ const char short_option = g_dependents_options[option_idx].short_option;
+ if (short_option == 'd') {
+ LoadDependentFiles tmp_load_dependents;
+ tmp_load_dependents = (LoadDependentFiles)OptionArgParser::ToOptionEnum(
+ option_value, g_dependents_options[option_idx].enum_values, 0, error);
+ if (error.Success())
+ m_load_dependent_files = tmp_load_dependents;
+ } else {
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ }
+
+ return error;
+ }
+
+ Status SetOptionValue(uint32_t, const char *, ExecutionContext *) = delete;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_load_dependent_files = eLoadDependentsDefault;
+ }
+
+ LoadDependentFiles m_load_dependent_files;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(OptionGroupDependents);
+};
+
+#pragma mark CommandObjectTargetCreate
+
+// "target create"
+
+class CommandObjectTargetCreate : public CommandObjectParsed {
+public:
+ CommandObjectTargetCreate(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target create",
+ "Create a target using the argument as the main executable.",
+ nullptr),
+ m_option_group(), m_arch_option(),
+ m_core_file(LLDB_OPT_SET_1, false, "core", 'c', 0, eArgTypeFilename,
+ "Fullpath to a core file to use for this target."),
+ m_platform_path(LLDB_OPT_SET_1, false, "platform-path", 'P', 0,
+ eArgTypePath,
+ "Path to the remote file to use for this target."),
+ m_symbol_file(LLDB_OPT_SET_1, false, "symfile", 's', 0,
+ eArgTypeFilename,
+ "Fullpath to a stand alone debug "
+ "symbols file for when debug symbols "
+ "are not in the executable."),
+ m_remote_file(
+ LLDB_OPT_SET_1, false, "remote-file", 'r', 0, eArgTypeFilename,
+ "Fullpath to the file on the remote host if debugging remotely."),
+ m_add_dependents() {
+ CommandArgumentEntry arg;
+ CommandArgumentData file_arg;
+
+ // Define the first (and only) variant of this arg.
+ file_arg.arg_type = eArgTypeFilename;
+ file_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(file_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ m_option_group.Append(&m_arch_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_core_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_platform_path, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_remote_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_add_dependents, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTargetCreate() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ FileSpec core_file(m_core_file.GetOptionValue().GetCurrentValue());
+ FileSpec remote_file(m_remote_file.GetOptionValue().GetCurrentValue());
+
+ if (core_file) {
+ if (!FileSystem::Instance().Exists(core_file)) {
+ result.AppendErrorWithFormat("core file '%s' doesn't exist",
+ core_file.GetPath().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ if (!FileSystem::Instance().Readable(core_file)) {
+ result.AppendErrorWithFormat("core file '%s' is not readable",
+ core_file.GetPath().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ if (argc == 1 || core_file || remote_file) {
+ FileSpec symfile(m_symbol_file.GetOptionValue().GetCurrentValue());
+ if (symfile) {
+ if (FileSystem::Instance().Exists(symfile)) {
+ if (!FileSystem::Instance().Readable(symfile)) {
+ result.AppendErrorWithFormat("symbol file '%s' is not readable",
+ symfile.GetPath().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ char symfile_path[PATH_MAX];
+ symfile.GetPath(symfile_path, sizeof(symfile_path));
+ result.AppendErrorWithFormat("invalid symbol file path '%s'",
+ symfile_path);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ const char *file_path = command.GetArgumentAtIndex(0);
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "(lldb) target create '%s'", file_path);
+ FileSpec file_spec;
+
+ if (file_path) {
+ file_spec.SetFile(file_path, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(file_spec);
+ }
+
+ bool must_set_platform_path = false;
+
+ Debugger &debugger = GetDebugger();
+
+ TargetSP target_sp;
+ llvm::StringRef arch_cstr = m_arch_option.GetArchitectureName();
+ Status error(debugger.GetTargetList().CreateTarget(
+ debugger, file_path, arch_cstr,
+ m_add_dependents.m_load_dependent_files, nullptr, target_sp));
+
+ if (target_sp) {
+ // Only get the platform after we create the target because we might
+ // have switched platforms depending on what the arguments were to
+ // CreateTarget() we can't rely on the selected platform.
+
+ PlatformSP platform_sp = target_sp->GetPlatform();
+
+ if (remote_file) {
+ if (platform_sp) {
+ // I have a remote file.. two possible cases
+ if (file_spec && FileSystem::Instance().Exists(file_spec)) {
+ // if the remote file does not exist, push it there
+ if (!platform_sp->GetFileExists(remote_file)) {
+ Status err = platform_sp->PutFile(file_spec, remote_file);
+ if (err.Fail()) {
+ result.AppendError(err.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ } else {
+ // there is no local file and we need one
+ // in order to make the remote ---> local transfer we need a
+ // platform
+ // TODO: if the user has passed in a --platform argument, use it
+ // to fetch the right platform
+ if (!platform_sp) {
+ result.AppendError(
+ "unable to perform remote debugging without a platform");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ if (file_path) {
+ // copy the remote file to the local file
+ Status err = platform_sp->GetFile(remote_file, file_spec);
+ if (err.Fail()) {
+ result.AppendError(err.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ // make up a local file
+ result.AppendError("remote --> local transfer without local "
+ "path is not implemented yet");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ } else {
+ result.AppendError("no platform found for target");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ if (symfile || remote_file) {
+ ModuleSP module_sp(target_sp->GetExecutableModule());
+ if (module_sp) {
+ if (symfile)
+ module_sp->SetSymbolFileFileSpec(symfile);
+ if (remote_file) {
+ std::string remote_path = remote_file.GetPath();
+ target_sp->SetArg0(remote_path.c_str());
+ module_sp->SetPlatformFileSpec(remote_file);
+ }
+ }
+ }
+
+ debugger.GetTargetList().SetSelectedTarget(target_sp.get());
+ if (must_set_platform_path) {
+ ModuleSpec main_module_spec(file_spec);
+ ModuleSP module_sp = target_sp->GetOrCreateModule(main_module_spec,
+ true /* notify */);
+ if (module_sp)
+ module_sp->SetPlatformFileSpec(remote_file);
+ }
+ if (core_file) {
+ char core_path[PATH_MAX];
+ core_file.GetPath(core_path, sizeof(core_path));
+ if (FileSystem::Instance().Exists(core_file)) {
+ if (!FileSystem::Instance().Readable(core_file)) {
+ result.AppendMessageWithFormat(
+ "Core file '%s' is not readable.\n", core_path);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ FileSpec core_file_dir;
+ core_file_dir.GetDirectory() = core_file.GetDirectory();
+ target_sp->AppendExecutableSearchPaths(core_file_dir);
+
+ ProcessSP process_sp(target_sp->CreateProcess(
+ GetDebugger().GetListener(), llvm::StringRef(), &core_file));
+
+ if (process_sp) {
+ // Seems weird that we Launch a core file, but that is what we
+ // do!
+ error = process_sp->LoadCore();
+
+ if (error.Fail()) {
+ result.AppendError(
+ error.AsCString("can't find plug-in for core file"));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ result.AppendMessageWithFormat(
+ "Core file '%s' (%s) was loaded.\n", core_path,
+ target_sp->GetArchitecture().GetArchitectureName());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "Unable to find process plug-in for core file '%s'\n",
+ core_path);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendErrorWithFormat("Core file '%s' does not exist\n",
+ core_path);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendMessageWithFormat(
+ "Current executable set to '%s' (%s).\n", file_path,
+ target_sp->GetArchitecture().GetArchitectureName());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ } else {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendErrorWithFormat("'%s' takes exactly one executable path "
+ "argument, or use the --core option.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+private:
+ OptionGroupOptions m_option_group;
+ OptionGroupArchitecture m_arch_option;
+ OptionGroupFile m_core_file;
+ OptionGroupFile m_platform_path;
+ OptionGroupFile m_symbol_file;
+ OptionGroupFile m_remote_file;
+ OptionGroupDependents m_add_dependents;
+};
+
+#pragma mark CommandObjectTargetList
+
+// "target list"
+
+class CommandObjectTargetList : public CommandObjectParsed {
+public:
+ CommandObjectTargetList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target list",
+ "List all current targets in the current debug session.", nullptr) {
+ }
+
+ ~CommandObjectTargetList() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() == 0) {
+ Stream &strm = result.GetOutputStream();
+
+ bool show_stopped_process_status = false;
+ if (DumpTargetList(GetDebugger().GetTargetList(),
+ show_stopped_process_status, strm) == 0) {
+ strm.PutCString("No targets.\n");
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("the 'target list' command takes no arguments\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetSelect
+
+// "target select"
+
+class CommandObjectTargetSelect : public CommandObjectParsed {
+public:
+ CommandObjectTargetSelect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target select",
+ "Select a target as the current target by target index.", nullptr) {
+ }
+
+ ~CommandObjectTargetSelect() override = default;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() == 1) {
+ bool success = false;
+ const char *target_idx_arg = args.GetArgumentAtIndex(0);
+ uint32_t target_idx =
+ StringConvert::ToUInt32(target_idx_arg, UINT32_MAX, 0, &success);
+ if (success) {
+ TargetList &target_list = GetDebugger().GetTargetList();
+ const uint32_t num_targets = target_list.GetNumTargets();
+ if (target_idx < num_targets) {
+ TargetSP target_sp(target_list.GetTargetAtIndex(target_idx));
+ if (target_sp) {
+ Stream &strm = result.GetOutputStream();
+ target_list.SetSelectedTarget(target_sp.get());
+ bool show_stopped_process_status = false;
+ DumpTargetList(target_list, show_stopped_process_status, strm);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("target #%u is NULL in target list\n",
+ target_idx);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ if (num_targets > 0) {
+ result.AppendErrorWithFormat(
+ "index %u is out of range, valid target indexes are 0 - %u\n",
+ target_idx, num_targets - 1);
+ } else {
+ result.AppendErrorWithFormat(
+ "index %u is out of range since there are no active targets\n",
+ target_idx);
+ }
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendErrorWithFormat("invalid index string value '%s'\n",
+ target_idx_arg);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError(
+ "'target select' takes a single argument: a target index\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetSelect
+
+// "target delete"
+
+class CommandObjectTargetDelete : public CommandObjectParsed {
+public:
+ CommandObjectTargetDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target delete",
+ "Delete one or more targets by target index.",
+ nullptr),
+ m_option_group(), m_all_option(LLDB_OPT_SET_1, false, "all", 'a',
+ "Delete all targets.", false, true),
+ m_cleanup_option(
+ LLDB_OPT_SET_1, false, "clean", 'c',
+ "Perform extra cleanup to minimize memory consumption after "
+ "deleting the target. "
+ "By default, LLDB will keep in memory any modules previously "
+ "loaded by the target as well "
+ "as all of its debug info. Specifying --clean will unload all of "
+ "these shared modules and "
+ "cause them to be reparsed again the next time the target is run",
+ false, true) {
+ m_option_group.Append(&m_all_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_cleanup_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTargetDelete() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ const size_t argc = args.GetArgumentCount();
+ std::vector<TargetSP> delete_target_list;
+ TargetList &target_list = GetDebugger().GetTargetList();
+ TargetSP target_sp;
+
+ if (m_all_option.GetOptionValue()) {
+ for (int i = 0; i < target_list.GetNumTargets(); ++i)
+ delete_target_list.push_back(target_list.GetTargetAtIndex(i));
+ } else if (argc > 0) {
+ const uint32_t num_targets = target_list.GetNumTargets();
+ // Bail out if don't have any targets.
+ if (num_targets == 0) {
+ result.AppendError("no targets to delete");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ for (auto &entry : args.entries()) {
+ uint32_t target_idx;
+ if (entry.ref.getAsInteger(0, target_idx)) {
+ result.AppendErrorWithFormat("invalid target index '%s'\n",
+ entry.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ if (target_idx < num_targets) {
+ target_sp = target_list.GetTargetAtIndex(target_idx);
+ if (target_sp) {
+ delete_target_list.push_back(target_sp);
+ continue;
+ }
+ }
+ if (num_targets > 1)
+ result.AppendErrorWithFormat("target index %u is out of range, valid "
+ "target indexes are 0 - %u\n",
+ target_idx, num_targets - 1);
+ else
+ result.AppendErrorWithFormat(
+ "target index %u is out of range, the only valid index is 0\n",
+ target_idx);
+
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ target_sp = target_list.GetSelectedTarget();
+ if (!target_sp) {
+ result.AppendErrorWithFormat("no target is currently selected\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ delete_target_list.push_back(target_sp);
+ }
+
+ const size_t num_targets_to_delete = delete_target_list.size();
+ for (size_t idx = 0; idx < num_targets_to_delete; ++idx) {
+ target_sp = delete_target_list[idx];
+ target_list.DeleteTarget(target_sp);
+ target_sp->Destroy();
+ }
+ // If "--clean" was specified, prune any orphaned shared modules from the
+ // global shared module list
+ if (m_cleanup_option.GetOptionValue()) {
+ const bool mandatory = true;
+ ModuleList::RemoveOrphanSharedModules(mandatory);
+ }
+ result.GetOutputStream().Printf("%u targets deleted.\n",
+ (uint32_t)num_targets_to_delete);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return true;
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupBoolean m_all_option;
+ OptionGroupBoolean m_cleanup_option;
+};
+
+#pragma mark CommandObjectTargetVariable
+
+// "target variable"
+
+class CommandObjectTargetVariable : public CommandObjectParsed {
+ static const uint32_t SHORT_OPTION_FILE = 0x66696c65; // 'file'
+ static const uint32_t SHORT_OPTION_SHLB = 0x73686c62; // 'shlb'
+
+public:
+ CommandObjectTargetVariable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target variable",
+ "Read global variables for the current target, "
+ "before or while running a process.",
+ nullptr, eCommandRequiresTarget),
+ m_option_group(),
+ m_option_variable(false), // Don't include frame options
+ m_option_format(eFormatDefault),
+ m_option_compile_units(LLDB_OPT_SET_1, false, "file", SHORT_OPTION_FILE,
+ 0, eArgTypeFilename,
+ "A basename or fullpath to a file that contains "
+ "global variables. This option can be "
+ "specified multiple times."),
+ m_option_shared_libraries(
+ LLDB_OPT_SET_1, false, "shlib", SHORT_OPTION_SHLB, 0,
+ eArgTypeFilename,
+ "A basename or fullpath to a shared library to use in the search "
+ "for global "
+ "variables. This option can be specified multiple times."),
+ m_varobj_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData var_name_arg;
+
+ // Define the first (and only) variant of this arg.
+ var_name_arg.arg_type = eArgTypeVarName;
+ var_name_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(var_name_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_option_format,
+ OptionGroupFormat::OPTION_GROUP_FORMAT |
+ OptionGroupFormat::OPTION_GROUP_GDB_FMT,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_option_compile_units, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_option_shared_libraries, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTargetVariable() override = default;
+
+ void DumpValueObject(Stream &s, VariableSP &var_sp, ValueObjectSP &valobj_sp,
+ const char *root_name) {
+ DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions());
+
+ if (!valobj_sp->GetTargetSP()->GetDisplayRuntimeSupportValues() &&
+ valobj_sp->IsRuntimeSupportValue())
+ return;
+
+ switch (var_sp->GetScope()) {
+ case eValueTypeVariableGlobal:
+ if (m_option_variable.show_scope)
+ s.PutCString("GLOBAL: ");
+ break;
+
+ case eValueTypeVariableStatic:
+ if (m_option_variable.show_scope)
+ s.PutCString("STATIC: ");
+ break;
+
+ case eValueTypeVariableArgument:
+ if (m_option_variable.show_scope)
+ s.PutCString(" ARG: ");
+ break;
+
+ case eValueTypeVariableLocal:
+ if (m_option_variable.show_scope)
+ s.PutCString(" LOCAL: ");
+ break;
+
+ case eValueTypeVariableThreadLocal:
+ if (m_option_variable.show_scope)
+ s.PutCString("THREAD: ");
+ break;
+
+ default:
+ break;
+ }
+
+ if (m_option_variable.show_decl) {
+ bool show_fullpaths = false;
+ bool show_module = true;
+ if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module))
+ s.PutCString(": ");
+ }
+
+ const Format format = m_option_format.GetFormat();
+ if (format != eFormatDefault)
+ options.SetFormat(format);
+
+ options.SetRootValueObjectName(root_name);
+
+ valobj_sp->Dump(s, options);
+ }
+
+ static size_t GetVariableCallback(void *baton, const char *name,
+ VariableList &variable_list) {
+ Target *target = static_cast<Target *>(baton);
+ if (target) {
+ return target->GetImages().FindGlobalVariables(ConstString(name),
+ UINT32_MAX, variable_list);
+ }
+ return 0;
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ void DumpGlobalVariableList(const ExecutionContext &exe_ctx,
+ const SymbolContext &sc,
+ const VariableList &variable_list, Stream &s) {
+ size_t count = variable_list.GetSize();
+ if (count > 0) {
+ if (sc.module_sp) {
+ if (sc.comp_unit) {
+ s.Printf("Global variables for %s in %s:\n",
+ sc.comp_unit->GetPath().c_str(),
+ sc.module_sp->GetFileSpec().GetPath().c_str());
+ } else {
+ s.Printf("Global variables for %s\n",
+ sc.module_sp->GetFileSpec().GetPath().c_str());
+ }
+ } else if (sc.comp_unit) {
+ s.Printf("Global variables for %s\n", sc.comp_unit->GetPath().c_str());
+ }
+
+ for (uint32_t i = 0; i < count; ++i) {
+ VariableSP var_sp(variable_list.GetVariableAtIndex(i));
+ if (var_sp) {
+ ValueObjectSP valobj_sp(ValueObjectVariable::Create(
+ exe_ctx.GetBestExecutionContextScope(), var_sp));
+
+ if (valobj_sp)
+ DumpValueObject(s, var_sp, valobj_sp,
+ var_sp->GetName().GetCString());
+ }
+ }
+ }
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ const size_t argc = args.GetArgumentCount();
+ Stream &s = result.GetOutputStream();
+
+ if (argc > 0) {
+
+ // TODO: Convert to entry-based iteration. Requires converting
+ // DumpValueObject.
+ for (size_t idx = 0; idx < argc; ++idx) {
+ VariableList variable_list;
+ ValueObjectList valobj_list;
+
+ const char *arg = args.GetArgumentAtIndex(idx);
+ size_t matches = 0;
+ bool use_var_name = false;
+ if (m_option_variable.use_regex) {
+ RegularExpression regex(llvm::StringRef::withNullAsEmpty(arg));
+ if (!regex.IsValid()) {
+ result.GetErrorStream().Printf(
+ "error: invalid regular expression: '%s'\n", arg);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ use_var_name = true;
+ matches = target->GetImages().FindGlobalVariables(regex, UINT32_MAX,
+ variable_list);
+ } else {
+ Status error(Variable::GetValuesForVariableExpressionPath(
+ arg, m_exe_ctx.GetBestExecutionContextScope(),
+ GetVariableCallback, target, variable_list, valobj_list));
+ matches = variable_list.GetSize();
+ }
+
+ if (matches == 0) {
+ result.GetErrorStream().Printf(
+ "error: can't find global variable '%s'\n", arg);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ for (uint32_t global_idx = 0; global_idx < matches; ++global_idx) {
+ VariableSP var_sp(variable_list.GetVariableAtIndex(global_idx));
+ if (var_sp) {
+ ValueObjectSP valobj_sp(
+ valobj_list.GetValueObjectAtIndex(global_idx));
+ if (!valobj_sp)
+ valobj_sp = ValueObjectVariable::Create(
+ m_exe_ctx.GetBestExecutionContextScope(), var_sp);
+
+ if (valobj_sp)
+ DumpValueObject(s, var_sp, valobj_sp,
+ use_var_name ? var_sp->GetName().GetCString()
+ : arg);
+ }
+ }
+ }
+ }
+ } else {
+ const FileSpecList &compile_units =
+ m_option_compile_units.GetOptionValue().GetCurrentValue();
+ const FileSpecList &shlibs =
+ m_option_shared_libraries.GetOptionValue().GetCurrentValue();
+ SymbolContextList sc_list;
+ const size_t num_compile_units = compile_units.GetSize();
+ const size_t num_shlibs = shlibs.GetSize();
+ if (num_compile_units == 0 && num_shlibs == 0) {
+ bool success = false;
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ CompileUnit *comp_unit = nullptr;
+ if (frame) {
+ SymbolContext sc = frame->GetSymbolContext(eSymbolContextCompUnit);
+ if (sc.comp_unit) {
+ const bool can_create = true;
+ VariableListSP comp_unit_varlist_sp(
+ sc.comp_unit->GetVariableList(can_create));
+ if (comp_unit_varlist_sp) {
+ size_t count = comp_unit_varlist_sp->GetSize();
+ if (count > 0) {
+ DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp, s);
+ success = true;
+ }
+ }
+ }
+ }
+ if (!success) {
+ if (frame) {
+ if (comp_unit)
+ result.AppendErrorWithFormat(
+ "no global variables in current compile unit: %s\n",
+ comp_unit->GetPath().c_str());
+ else
+ result.AppendErrorWithFormat(
+ "no debug information for frame %u\n",
+ frame->GetFrameIndex());
+ } else
+ result.AppendError("'target variable' takes one or more global "
+ "variable names as arguments\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ SymbolContextList sc_list;
+ const bool append = true;
+ // We have one or more compile unit or shlib
+ if (num_shlibs > 0) {
+ for (size_t shlib_idx = 0; shlib_idx < num_shlibs; ++shlib_idx) {
+ const FileSpec module_file(shlibs.GetFileSpecAtIndex(shlib_idx));
+ ModuleSpec module_spec(module_file);
+
+ ModuleSP module_sp(
+ target->GetImages().FindFirstModule(module_spec));
+ if (module_sp) {
+ if (num_compile_units > 0) {
+ for (size_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx)
+ module_sp->FindCompileUnits(
+ compile_units.GetFileSpecAtIndex(cu_idx), append,
+ sc_list);
+ } else {
+ SymbolContext sc;
+ sc.module_sp = module_sp;
+ sc_list.Append(sc);
+ }
+ } else {
+ // Didn't find matching shlib/module in target...
+ result.AppendErrorWithFormat(
+ "target doesn't contain the specified shared library: %s\n",
+ module_file.GetPath().c_str());
+ }
+ }
+ } else {
+ // No shared libraries, we just want to find globals for the compile
+ // units files that were specified
+ for (size_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx)
+ target->GetImages().FindCompileUnits(
+ compile_units.GetFileSpecAtIndex(cu_idx), append, sc_list);
+ }
+
+ const uint32_t num_scs = sc_list.GetSize();
+ if (num_scs > 0) {
+ SymbolContext sc;
+ for (uint32_t sc_idx = 0; sc_idx < num_scs; ++sc_idx) {
+ if (sc_list.GetContextAtIndex(sc_idx, sc)) {
+ if (sc.comp_unit) {
+ const bool can_create = true;
+ VariableListSP comp_unit_varlist_sp(
+ sc.comp_unit->GetVariableList(can_create));
+ if (comp_unit_varlist_sp)
+ DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp,
+ s);
+ } else if (sc.module_sp) {
+ // Get all global variables for this module
+ lldb_private::RegularExpression all_globals_regex(
+ llvm::StringRef(
+ ".")); // Any global with at least one character
+ VariableList variable_list;
+ sc.module_sp->FindGlobalVariables(all_globals_regex, UINT32_MAX,
+ variable_list);
+ DumpGlobalVariableList(m_exe_ctx, sc, variable_list, s);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (m_interpreter.TruncationWarningNecessary()) {
+ result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(),
+ m_cmd_name.c_str());
+ m_interpreter.TruncationWarningGiven();
+ }
+
+ return result.Succeeded();
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupVariable m_option_variable;
+ OptionGroupFormat m_option_format;
+ OptionGroupFileList m_option_compile_units;
+ OptionGroupFileList m_option_shared_libraries;
+ OptionGroupValueObjectDisplay m_varobj_options;
+};
+
+#pragma mark CommandObjectTargetModulesSearchPathsAdd
+
+class CommandObjectTargetModulesSearchPathsAdd : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSearchPathsAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules search-paths add",
+ "Add new image search paths substitution pairs to "
+ "the current target.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData old_prefix_arg;
+ CommandArgumentData new_prefix_arg;
+
+ // Define the first variant of this arg pair.
+ old_prefix_arg.arg_type = eArgTypeOldPathPrefix;
+ old_prefix_arg.arg_repetition = eArgRepeatPairPlus;
+
+ // Define the first variant of this arg pair.
+ new_prefix_arg.arg_type = eArgTypeNewPathPrefix;
+ new_prefix_arg.arg_repetition = eArgRepeatPairPlus;
+
+ // There are two required arguments that must always occur together, i.e.
+ // an argument "pair". Because they must always occur together, they are
+ // treated as two variants of one argument rather than two independent
+ // arguments. Push them both into the first argument position for
+ // m_arguments...
+
+ arg.push_back(old_prefix_arg);
+ arg.push_back(new_prefix_arg);
+
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectTargetModulesSearchPathsAdd() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target) {
+ const size_t argc = command.GetArgumentCount();
+ if (argc & 1) {
+ result.AppendError("add requires an even number of arguments\n");
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ for (size_t i = 0; i < argc; i += 2) {
+ const char *from = command.GetArgumentAtIndex(i);
+ const char *to = command.GetArgumentAtIndex(i + 1);
+
+ if (from[0] && to[0]) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ if (log) {
+ log->Printf("target modules search path adding ImageSearchPath "
+ "pair: '%s' -> '%s'",
+ from, to);
+ }
+ bool last_pair = ((argc - i) == 2);
+ target->GetImageSearchPathList().Append(
+ ConstString(from), ConstString(to),
+ last_pair); // Notify if this is the last pair
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ if (from[0])
+ result.AppendError("<path-prefix> can't be empty\n");
+ else
+ result.AppendError("<new-path-prefix> can't be empty\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ }
+ } else {
+ result.AppendError("invalid target\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesSearchPathsClear
+
+class CommandObjectTargetModulesSearchPathsClear : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSearchPathsClear(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules search-paths clear",
+ "Clear all current image search path substitution "
+ "pairs from the current target.",
+ "target modules search-paths clear") {}
+
+ ~CommandObjectTargetModulesSearchPathsClear() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target) {
+ bool notify = true;
+ target->GetImageSearchPathList().Clear(notify);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("invalid target\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesSearchPathsInsert
+
+class CommandObjectTargetModulesSearchPathsInsert : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSearchPathsInsert(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules search-paths insert",
+ "Insert a new image search path substitution pair "
+ "into the current target at the specified index.",
+ nullptr) {
+ CommandArgumentEntry arg1;
+ CommandArgumentEntry arg2;
+ CommandArgumentData index_arg;
+ CommandArgumentData old_prefix_arg;
+ CommandArgumentData new_prefix_arg;
+
+ // Define the first and only variant of this arg.
+ index_arg.arg_type = eArgTypeIndex;
+ index_arg.arg_repetition = eArgRepeatPlain;
+
+ // Put the one and only variant into the first arg for m_arguments:
+ arg1.push_back(index_arg);
+
+ // Define the first variant of this arg pair.
+ old_prefix_arg.arg_type = eArgTypeOldPathPrefix;
+ old_prefix_arg.arg_repetition = eArgRepeatPairPlus;
+
+ // Define the first variant of this arg pair.
+ new_prefix_arg.arg_type = eArgTypeNewPathPrefix;
+ new_prefix_arg.arg_repetition = eArgRepeatPairPlus;
+
+ // There are two required arguments that must always occur together, i.e.
+ // an argument "pair". Because they must always occur together, they are
+ // treated as two variants of one argument rather than two independent
+ // arguments. Push them both into the same argument position for
+ // m_arguments...
+
+ arg2.push_back(old_prefix_arg);
+ arg2.push_back(new_prefix_arg);
+
+ // Add arguments to m_arguments.
+ m_arguments.push_back(arg1);
+ m_arguments.push_back(arg2);
+ }
+
+ ~CommandObjectTargetModulesSearchPathsInsert() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target) {
+ size_t argc = command.GetArgumentCount();
+ // check for at least 3 arguments and an odd number of parameters
+ if (argc >= 3 && argc & 1) {
+ bool success = false;
+
+ uint32_t insert_idx = StringConvert::ToUInt32(
+ command.GetArgumentAtIndex(0), UINT32_MAX, 0, &success);
+
+ if (!success) {
+ result.AppendErrorWithFormat(
+ "<index> parameter is not an integer: '%s'.\n",
+ command.GetArgumentAtIndex(0));
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ }
+
+ // shift off the index
+ command.Shift();
+ argc = command.GetArgumentCount();
+
+ for (uint32_t i = 0; i < argc; i += 2, ++insert_idx) {
+ const char *from = command.GetArgumentAtIndex(i);
+ const char *to = command.GetArgumentAtIndex(i + 1);
+
+ if (from[0] && to[0]) {
+ bool last_pair = ((argc - i) == 2);
+ target->GetImageSearchPathList().Insert(
+ ConstString(from), ConstString(to), insert_idx, last_pair);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ if (from[0])
+ result.AppendError("<path-prefix> can't be empty\n");
+ else
+ result.AppendError("<new-path-prefix> can't be empty\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ } else {
+ result.AppendError("insert requires at least three arguments\n");
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ }
+
+ } else {
+ result.AppendError("invalid target\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesSearchPathsList
+
+class CommandObjectTargetModulesSearchPathsList : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSearchPathsList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules search-paths list",
+ "List all current image search path substitution "
+ "pairs in the current target.",
+ "target modules search-paths list") {}
+
+ ~CommandObjectTargetModulesSearchPathsList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target) {
+ if (command.GetArgumentCount() != 0) {
+ result.AppendError("list takes no arguments\n");
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ }
+
+ target->GetImageSearchPathList().Dump(&result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("invalid target\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesSearchPathsQuery
+
+class CommandObjectTargetModulesSearchPathsQuery : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSearchPathsQuery(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target modules search-paths query",
+ "Transform a path using the first applicable image search path.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData path_arg;
+
+ // Define the first (and only) variant of this arg.
+ path_arg.arg_type = eArgTypeDirectoryName;
+ path_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(path_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectTargetModulesSearchPathsQuery() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target) {
+ if (command.GetArgumentCount() != 1) {
+ result.AppendError("query requires one argument\n");
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ }
+
+ ConstString orig(command.GetArgumentAtIndex(0));
+ ConstString transformed;
+ if (target->GetImageSearchPathList().RemapPath(orig, transformed))
+ result.GetOutputStream().Printf("%s\n", transformed.GetCString());
+ else
+ result.GetOutputStream().Printf("%s\n", orig.GetCString());
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("invalid target\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+// Static Helper functions
+static void DumpModuleArchitecture(Stream &strm, Module *module,
+ bool full_triple, uint32_t width) {
+ if (module) {
+ StreamString arch_strm;
+
+ if (full_triple)
+ module->GetArchitecture().DumpTriple(arch_strm);
+ else
+ arch_strm.PutCString(module->GetArchitecture().GetArchitectureName());
+ std::string arch_str = arch_strm.GetString();
+
+ if (width)
+ strm.Printf("%-*s", width, arch_str.c_str());
+ else
+ strm.PutCString(arch_str);
+ }
+}
+
+static void DumpModuleUUID(Stream &strm, Module *module) {
+ if (module && module->GetUUID().IsValid())
+ module->GetUUID().Dump(&strm);
+ else
+ strm.PutCString(" ");
+}
+
+static uint32_t DumpCompileUnitLineTable(CommandInterpreter &interpreter,
+ Stream &strm, Module *module,
+ const FileSpec &file_spec,
+ lldb::DescriptionLevel desc_level) {
+ uint32_t num_matches = 0;
+ if (module) {
+ SymbolContextList sc_list;
+ num_matches = module->ResolveSymbolContextsForFileSpec(
+ file_spec, 0, false, eSymbolContextCompUnit, sc_list);
+
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(i, sc)) {
+ if (i > 0)
+ strm << "\n\n";
+
+ strm << "Line table for " << *static_cast<FileSpec *>(sc.comp_unit)
+ << " in `" << module->GetFileSpec().GetFilename() << "\n";
+ LineTable *line_table = sc.comp_unit->GetLineTable();
+ if (line_table)
+ line_table->GetDescription(
+ &strm, interpreter.GetExecutionContext().GetTargetPtr(),
+ desc_level);
+ else
+ strm << "No line table";
+ }
+ }
+ }
+ return num_matches;
+}
+
+static void DumpFullpath(Stream &strm, const FileSpec *file_spec_ptr,
+ uint32_t width) {
+ if (file_spec_ptr) {
+ if (width > 0) {
+ std::string fullpath = file_spec_ptr->GetPath();
+ strm.Printf("%-*s", width, fullpath.c_str());
+ return;
+ } else {
+ file_spec_ptr->Dump(&strm);
+ return;
+ }
+ }
+ // Keep the width spacing correct if things go wrong...
+ if (width > 0)
+ strm.Printf("%-*s", width, "");
+}
+
+static void DumpDirectory(Stream &strm, const FileSpec *file_spec_ptr,
+ uint32_t width) {
+ if (file_spec_ptr) {
+ if (width > 0)
+ strm.Printf("%-*s", width, file_spec_ptr->GetDirectory().AsCString(""));
+ else
+ file_spec_ptr->GetDirectory().Dump(&strm);
+ return;
+ }
+ // Keep the width spacing correct if things go wrong...
+ if (width > 0)
+ strm.Printf("%-*s", width, "");
+}
+
+static void DumpBasename(Stream &strm, const FileSpec *file_spec_ptr,
+ uint32_t width) {
+ if (file_spec_ptr) {
+ if (width > 0)
+ strm.Printf("%-*s", width, file_spec_ptr->GetFilename().AsCString(""));
+ else
+ file_spec_ptr->GetFilename().Dump(&strm);
+ return;
+ }
+ // Keep the width spacing correct if things go wrong...
+ if (width > 0)
+ strm.Printf("%-*s", width, "");
+}
+
+static size_t DumpModuleObjfileHeaders(Stream &strm, ModuleList &module_list) {
+ size_t num_dumped = 0;
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+ const size_t num_modules = module_list.GetSize();
+ if (num_modules > 0) {
+ strm.Printf("Dumping headers for %" PRIu64 " module(s).\n",
+ static_cast<uint64_t>(num_modules));
+ strm.IndentMore();
+ for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
+ Module *module = module_list.GetModulePointerAtIndexUnlocked(image_idx);
+ if (module) {
+ if (num_dumped++ > 0) {
+ strm.EOL();
+ strm.EOL();
+ }
+ ObjectFile *objfile = module->GetObjectFile();
+ if (objfile)
+ objfile->Dump(&strm);
+ else {
+ strm.Format("No object file for module: {0:F}\n",
+ module->GetFileSpec());
+ }
+ }
+ }
+ strm.IndentLess();
+ }
+ return num_dumped;
+}
+
+static void DumpModuleSymtab(CommandInterpreter &interpreter, Stream &strm,
+ Module *module, SortOrder sort_order) {
+ if (module) {
+ SymbolVendor *sym_vendor = module->GetSymbolVendor();
+ if (sym_vendor) {
+ Symtab *symtab = sym_vendor->GetSymtab();
+ if (symtab)
+ symtab->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(),
+ sort_order);
+ }
+ }
+}
+
+static void DumpModuleSections(CommandInterpreter &interpreter, Stream &strm,
+ Module *module) {
+ if (module) {
+ SectionList *section_list = module->GetSectionList();
+ if (section_list) {
+ strm.Printf("Sections for '%s' (%s):\n",
+ module->GetSpecificationDescription().c_str(),
+ module->GetArchitecture().GetArchitectureName());
+ strm.IndentMore();
+ section_list->Dump(&strm,
+ interpreter.GetExecutionContext().GetTargetPtr(), true,
+ UINT32_MAX);
+ strm.IndentLess();
+ }
+ }
+}
+
+static bool DumpModuleSymbolVendor(Stream &strm, Module *module) {
+ if (module) {
+ SymbolVendor *symbol_vendor = module->GetSymbolVendor(true);
+ if (symbol_vendor) {
+ symbol_vendor->Dump(&strm);
+ return true;
+ }
+ }
+ return false;
+}
+
+static void DumpAddress(ExecutionContextScope *exe_scope,
+ const Address &so_addr, bool verbose, Stream &strm) {
+ strm.IndentMore();
+ strm.Indent(" Address: ");
+ so_addr.Dump(&strm, exe_scope, Address::DumpStyleModuleWithFileAddress);
+ strm.PutCString(" (");
+ so_addr.Dump(&strm, exe_scope, Address::DumpStyleSectionNameOffset);
+ strm.PutCString(")\n");
+ strm.Indent(" Summary: ");
+ const uint32_t save_indent = strm.GetIndentLevel();
+ strm.SetIndentLevel(save_indent + 13);
+ so_addr.Dump(&strm, exe_scope, Address::DumpStyleResolvedDescription);
+ strm.SetIndentLevel(save_indent);
+ // Print out detailed address information when verbose is enabled
+ if (verbose) {
+ strm.EOL();
+ so_addr.Dump(&strm, exe_scope, Address::DumpStyleDetailedSymbolContext);
+ }
+ strm.IndentLess();
+}
+
+static bool LookupAddressInModule(CommandInterpreter &interpreter, Stream &strm,
+ Module *module, uint32_t resolve_mask,
+ lldb::addr_t raw_addr, lldb::addr_t offset,
+ bool verbose) {
+ if (module) {
+ lldb::addr_t addr = raw_addr - offset;
+ Address so_addr;
+ SymbolContext sc;
+ Target *target = interpreter.GetExecutionContext().GetTargetPtr();
+ if (target && !target->GetSectionLoadList().IsEmpty()) {
+ if (!target->GetSectionLoadList().ResolveLoadAddress(addr, so_addr))
+ return false;
+ else if (so_addr.GetModule().get() != module)
+ return false;
+ } else {
+ if (!module->ResolveFileAddress(addr, so_addr))
+ return false;
+ }
+
+ ExecutionContextScope *exe_scope =
+ interpreter.GetExecutionContext().GetBestExecutionContextScope();
+ DumpAddress(exe_scope, so_addr, verbose, strm);
+ // strm.IndentMore();
+ // strm.Indent (" Address: ");
+ // so_addr.Dump (&strm, exe_scope,
+ // Address::DumpStyleModuleWithFileAddress);
+ // strm.PutCString (" (");
+ // so_addr.Dump (&strm, exe_scope,
+ // Address::DumpStyleSectionNameOffset);
+ // strm.PutCString (")\n");
+ // strm.Indent (" Summary: ");
+ // const uint32_t save_indent = strm.GetIndentLevel ();
+ // strm.SetIndentLevel (save_indent + 13);
+ // so_addr.Dump (&strm, exe_scope,
+ // Address::DumpStyleResolvedDescription);
+ // strm.SetIndentLevel (save_indent);
+ // // Print out detailed address information when verbose is enabled
+ // if (verbose)
+ // {
+ // strm.EOL();
+ // so_addr.Dump (&strm, exe_scope,
+ // Address::DumpStyleDetailedSymbolContext);
+ // }
+ // strm.IndentLess();
+ return true;
+ }
+
+ return false;
+}
+
+static uint32_t LookupSymbolInModule(CommandInterpreter &interpreter,
+ Stream &strm, Module *module,
+ const char *name, bool name_is_regex,
+ bool verbose) {
+ if (module) {
+ SymbolContext sc;
+
+ SymbolVendor *sym_vendor = module->GetSymbolVendor();
+ if (sym_vendor) {
+ Symtab *symtab = sym_vendor->GetSymtab();
+ if (symtab) {
+ std::vector<uint32_t> match_indexes;
+ ConstString symbol_name(name);
+ uint32_t num_matches = 0;
+ if (name_is_regex) {
+ RegularExpression name_regexp(symbol_name.GetStringRef());
+ num_matches = symtab->AppendSymbolIndexesMatchingRegExAndType(
+ name_regexp, eSymbolTypeAny, match_indexes);
+ } else {
+ num_matches =
+ symtab->AppendSymbolIndexesWithName(symbol_name, match_indexes);
+ }
+
+ if (num_matches > 0) {
+ strm.Indent();
+ strm.Printf("%u symbols match %s'%s' in ", num_matches,
+ name_is_regex ? "the regular expression " : "", name);
+ DumpFullpath(strm, &module->GetFileSpec(), 0);
+ strm.PutCString(":\n");
+ strm.IndentMore();
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]);
+ if (symbol && symbol->ValueIsAddress()) {
+ DumpAddress(interpreter.GetExecutionContext()
+ .GetBestExecutionContextScope(),
+ symbol->GetAddressRef(), verbose, strm);
+ }
+ }
+ strm.IndentLess();
+ return num_matches;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static void DumpSymbolContextList(ExecutionContextScope *exe_scope,
+ Stream &strm, SymbolContextList &sc_list,
+ bool verbose) {
+ strm.IndentMore();
+
+ const uint32_t num_matches = sc_list.GetSize();
+
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(i, sc)) {
+ AddressRange range;
+
+ sc.GetAddressRange(eSymbolContextEverything, 0, true, range);
+
+ DumpAddress(exe_scope, range.GetBaseAddress(), verbose, strm);
+ }
+ }
+ strm.IndentLess();
+}
+
+static size_t LookupFunctionInModule(CommandInterpreter &interpreter,
+ Stream &strm, Module *module,
+ const char *name, bool name_is_regex,
+ bool include_inlines, bool include_symbols,
+ bool verbose) {
+ if (module && name && name[0]) {
+ SymbolContextList sc_list;
+ const bool append = true;
+ size_t num_matches = 0;
+ if (name_is_regex) {
+ RegularExpression function_name_regex((llvm::StringRef(name)));
+ num_matches = module->FindFunctions(function_name_regex, include_symbols,
+ include_inlines, append, sc_list);
+ } else {
+ ConstString function_name(name);
+ num_matches = module->FindFunctions(
+ function_name, nullptr, eFunctionNameTypeAuto, include_symbols,
+ include_inlines, append, sc_list);
+ }
+
+ if (num_matches) {
+ strm.Indent();
+ strm.Printf("%" PRIu64 " match%s found in ", (uint64_t)num_matches,
+ num_matches > 1 ? "es" : "");
+ DumpFullpath(strm, &module->GetFileSpec(), 0);
+ strm.PutCString(":\n");
+ DumpSymbolContextList(
+ interpreter.GetExecutionContext().GetBestExecutionContextScope(),
+ strm, sc_list, verbose);
+ }
+ return num_matches;
+ }
+ return 0;
+}
+
+static size_t LookupTypeInModule(CommandInterpreter &interpreter, Stream &strm,
+ Module *module, const char *name_cstr,
+ bool name_is_regex) {
+ if (module && name_cstr && name_cstr[0]) {
+ TypeList type_list;
+ const uint32_t max_num_matches = UINT32_MAX;
+ size_t num_matches = 0;
+ bool name_is_fully_qualified = false;
+
+ ConstString name(name_cstr);
+ llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
+ num_matches =
+ module->FindTypes(name, name_is_fully_qualified, max_num_matches,
+ searched_symbol_files, type_list);
+
+ if (num_matches) {
+ strm.Indent();
+ strm.Printf("%" PRIu64 " match%s found in ", (uint64_t)num_matches,
+ num_matches > 1 ? "es" : "");
+ DumpFullpath(strm, &module->GetFileSpec(), 0);
+ strm.PutCString(":\n");
+ for (TypeSP type_sp : type_list.Types()) {
+ if (type_sp) {
+ // Resolve the clang type so that any forward references to types
+ // that haven't yet been parsed will get parsed.
+ type_sp->GetFullCompilerType();
+ type_sp->GetDescription(&strm, eDescriptionLevelFull, true);
+ // Print all typedef chains
+ TypeSP typedef_type_sp(type_sp);
+ TypeSP typedefed_type_sp(typedef_type_sp->GetTypedefType());
+ while (typedefed_type_sp) {
+ strm.EOL();
+ strm.Printf(" typedef '%s': ",
+ typedef_type_sp->GetName().GetCString());
+ typedefed_type_sp->GetFullCompilerType();
+ typedefed_type_sp->GetDescription(&strm, eDescriptionLevelFull,
+ true);
+ typedef_type_sp = typedefed_type_sp;
+ typedefed_type_sp = typedef_type_sp->GetTypedefType();
+ }
+ }
+ strm.EOL();
+ }
+ }
+ return num_matches;
+ }
+ return 0;
+}
+
+static size_t LookupTypeHere(CommandInterpreter &interpreter, Stream &strm,
+ Module &module, const char *name_cstr,
+ bool name_is_regex) {
+ TypeList type_list;
+ const uint32_t max_num_matches = UINT32_MAX;
+ size_t num_matches = 1;
+ bool name_is_fully_qualified = false;
+
+ ConstString name(name_cstr);
+ llvm::DenseSet<SymbolFile *> searched_symbol_files;
+ num_matches = module.FindTypes(name, name_is_fully_qualified, max_num_matches,
+ searched_symbol_files, type_list);
+
+ if (num_matches) {
+ strm.Indent();
+ strm.PutCString("Best match found in ");
+ DumpFullpath(strm, &module.GetFileSpec(), 0);
+ strm.PutCString(":\n");
+
+ TypeSP type_sp(type_list.GetTypeAtIndex(0));
+ if (type_sp) {
+ // Resolve the clang type so that any forward references to types that
+ // haven't yet been parsed will get parsed.
+ type_sp->GetFullCompilerType();
+ type_sp->GetDescription(&strm, eDescriptionLevelFull, true);
+ // Print all typedef chains
+ TypeSP typedef_type_sp(type_sp);
+ TypeSP typedefed_type_sp(typedef_type_sp->GetTypedefType());
+ while (typedefed_type_sp) {
+ strm.EOL();
+ strm.Printf(" typedef '%s': ",
+ typedef_type_sp->GetName().GetCString());
+ typedefed_type_sp->GetFullCompilerType();
+ typedefed_type_sp->GetDescription(&strm, eDescriptionLevelFull, true);
+ typedef_type_sp = typedefed_type_sp;
+ typedefed_type_sp = typedef_type_sp->GetTypedefType();
+ }
+ }
+ strm.EOL();
+ }
+ return num_matches;
+}
+
+static uint32_t LookupFileAndLineInModule(CommandInterpreter &interpreter,
+ Stream &strm, Module *module,
+ const FileSpec &file_spec,
+ uint32_t line, bool check_inlines,
+ bool verbose) {
+ if (module && file_spec) {
+ SymbolContextList sc_list;
+ const uint32_t num_matches = module->ResolveSymbolContextsForFileSpec(
+ file_spec, line, check_inlines, eSymbolContextEverything, sc_list);
+ if (num_matches > 0) {
+ strm.Indent();
+ strm.Printf("%u match%s found in ", num_matches,
+ num_matches > 1 ? "es" : "");
+ strm << file_spec;
+ if (line > 0)
+ strm.Printf(":%u", line);
+ strm << " in ";
+ DumpFullpath(strm, &module->GetFileSpec(), 0);
+ strm.PutCString(":\n");
+ DumpSymbolContextList(
+ interpreter.GetExecutionContext().GetBestExecutionContextScope(),
+ strm, sc_list, verbose);
+ return num_matches;
+ }
+ }
+ return 0;
+}
+
+static size_t FindModulesByName(Target *target, const char *module_name,
+ ModuleList &module_list,
+ bool check_global_list) {
+ FileSpec module_file_spec(module_name);
+ ModuleSpec module_spec(module_file_spec);
+
+ const size_t initial_size = module_list.GetSize();
+
+ if (check_global_list) {
+ // Check the global list
+ std::lock_guard<std::recursive_mutex> guard(
+ Module::GetAllocationModuleCollectionMutex());
+ const size_t num_modules = Module::GetNumberAllocatedModules();
+ ModuleSP module_sp;
+ for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
+ Module *module = Module::GetAllocatedModuleAtIndex(image_idx);
+
+ if (module) {
+ if (module->MatchesModuleSpec(module_spec)) {
+ module_sp = module->shared_from_this();
+ module_list.AppendIfNeeded(module_sp);
+ }
+ }
+ }
+ } else {
+ if (target) {
+ const size_t num_matches =
+ target->GetImages().FindModules(module_spec, module_list);
+
+ // Not found in our module list for our target, check the main shared
+ // module list in case it is a extra file used somewhere else
+ if (num_matches == 0) {
+ module_spec.GetArchitecture() = target->GetArchitecture();
+ ModuleList::FindSharedModules(module_spec, module_list);
+ }
+ } else {
+ ModuleList::FindSharedModules(module_spec, module_list);
+ }
+ }
+
+ return module_list.GetSize() - initial_size;
+}
+
+#pragma mark CommandObjectTargetModulesModuleAutoComplete
+
+// A base command object class that can auto complete with module file
+// paths
+
+class CommandObjectTargetModulesModuleAutoComplete
+ : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesModuleAutoComplete(CommandInterpreter &interpreter,
+ const char *name,
+ const char *help,
+ const char *syntax)
+ : CommandObjectParsed(interpreter, name, help, syntax) {
+ CommandArgumentEntry arg;
+ CommandArgumentData file_arg;
+
+ // Define the first (and only) variant of this arg.
+ file_arg.arg_type = eArgTypeFilename;
+ file_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(file_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectTargetModulesModuleAutoComplete() override = default;
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eModuleCompletion, request,
+ nullptr);
+ return request.GetNumberOfMatches();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesSourceFileAutoComplete
+
+// A base command object class that can auto complete with module source
+// file paths
+
+class CommandObjectTargetModulesSourceFileAutoComplete
+ : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesSourceFileAutoComplete(
+ CommandInterpreter &interpreter, const char *name, const char *help,
+ const char *syntax, uint32_t flags)
+ : CommandObjectParsed(interpreter, name, help, syntax, flags) {
+ CommandArgumentEntry arg;
+ CommandArgumentData source_file_arg;
+
+ // Define the first (and only) variant of this arg.
+ source_file_arg.arg_type = eArgTypeSourceFile;
+ source_file_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(source_file_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectTargetModulesSourceFileAutoComplete() override = default;
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eSourceFileCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesDumpObjfile
+
+class CommandObjectTargetModulesDumpObjfile
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpObjfile(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump objfile",
+ "Dump the object file headers from one or more target modules.",
+ nullptr) {}
+
+ ~CommandObjectTargetModulesDumpObjfile() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ size_t num_dumped = 0;
+ if (command.GetArgumentCount() == 0) {
+ // Dump all headers for all modules images
+ num_dumped = DumpModuleObjfileHeaders(result.GetOutputStream(),
+ target->GetImages());
+ if (num_dumped == 0) {
+ result.AppendError("the target has no associated executable images");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ // Find the modules that match the basename or full path.
+ ModuleList module_list;
+ const char *arg_cstr;
+ for (int arg_idx = 0;
+ (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
+ ++arg_idx) {
+ size_t num_matched =
+ FindModulesByName(target, arg_cstr, module_list, true);
+ if (num_matched == 0) {
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+ // Dump all the modules we found.
+ num_dumped =
+ DumpModuleObjfileHeaders(result.GetOutputStream(), module_list);
+ }
+
+ if (num_dumped > 0) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("no matching executable images found");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesDumpSymtab
+
+static constexpr OptionEnumValueElement g_sort_option_enumeration[] = {
+ {eSortOrderNone, "none",
+ "No sorting, use the original symbol table order."},
+ {eSortOrderByAddress, "address", "Sort output by symbol address."},
+ {eSortOrderByName, "name", "Sort output by symbol name."} };
+
+static constexpr OptionDefinition g_target_modules_dump_symtab_options[] = {
+#define LLDB_OPTIONS_target_modules_dump_symtab
+#include "CommandOptions.inc"
+};
+
+class CommandObjectTargetModulesDumpSymtab
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpSymtab(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump symtab",
+ "Dump the symbol table from one or more target modules.", nullptr),
+ m_options() {}
+
+ ~CommandObjectTargetModulesDumpSymtab() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_sort_order(eSortOrderNone) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 's':
+ m_sort_order = (SortOrder)OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values,
+ eSortOrderNone, error);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_sort_order = eSortOrderNone;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_modules_dump_symtab_options);
+ }
+
+ SortOrder m_sort_order;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ uint32_t num_dumped = 0;
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ if (command.GetArgumentCount() == 0) {
+ // Dump all sections for all modules images
+ std::lock_guard<std::recursive_mutex> guard(
+ target->GetImages().GetMutex());
+ const size_t num_modules = target->GetImages().GetSize();
+ if (num_modules > 0) {
+ result.GetOutputStream().Printf("Dumping symbol table for %" PRIu64
+ " modules.\n",
+ (uint64_t)num_modules);
+ for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
+ if (num_dumped > 0) {
+ result.GetOutputStream().EOL();
+ result.GetOutputStream().EOL();
+ }
+ if (m_interpreter.WasInterrupted())
+ break;
+ num_dumped++;
+ DumpModuleSymtab(
+ m_interpreter, result.GetOutputStream(),
+ target->GetImages().GetModulePointerAtIndexUnlocked(image_idx),
+ m_options.m_sort_order);
+ }
+ } else {
+ result.AppendError("the target has no associated executable images");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr;
+ for (int arg_idx = 0;
+ (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
+ ++arg_idx) {
+ ModuleList module_list;
+ const size_t num_matches =
+ FindModulesByName(target, arg_cstr, module_list, true);
+ if (num_matches > 0) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ Module *module = module_list.GetModulePointerAtIndex(i);
+ if (module) {
+ if (num_dumped > 0) {
+ result.GetOutputStream().EOL();
+ result.GetOutputStream().EOL();
+ }
+ if (m_interpreter.WasInterrupted())
+ break;
+ num_dumped++;
+ DumpModuleSymtab(m_interpreter, result.GetOutputStream(),
+ module, m_options.m_sort_order);
+ }
+ }
+ } else
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+
+ if (num_dumped > 0)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.AppendError("no matching executable images found");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectTargetModulesDumpSections
+
+// Image section dumping command
+
+class CommandObjectTargetModulesDumpSections
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpSections(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump sections",
+ "Dump the sections from one or more target modules.",
+ //"target modules dump sections [<file1> ...]")
+ nullptr) {}
+
+ ~CommandObjectTargetModulesDumpSections() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ uint32_t num_dumped = 0;
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ if (command.GetArgumentCount() == 0) {
+ // Dump all sections for all modules images
+ const size_t num_modules = target->GetImages().GetSize();
+ if (num_modules > 0) {
+ result.GetOutputStream().Printf("Dumping sections for %" PRIu64
+ " modules.\n",
+ (uint64_t)num_modules);
+ for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ num_dumped++;
+ DumpModuleSections(
+ m_interpreter, result.GetOutputStream(),
+ target->GetImages().GetModulePointerAtIndex(image_idx));
+ }
+ } else {
+ result.AppendError("the target has no associated executable images");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr;
+ for (int arg_idx = 0;
+ (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
+ ++arg_idx) {
+ ModuleList module_list;
+ const size_t num_matches =
+ FindModulesByName(target, arg_cstr, module_list, true);
+ if (num_matches > 0) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ Module *module = module_list.GetModulePointerAtIndex(i);
+ if (module) {
+ num_dumped++;
+ DumpModuleSections(m_interpreter, result.GetOutputStream(),
+ module);
+ }
+ }
+ } else {
+ // Check the global list
+ std::lock_guard<std::recursive_mutex> guard(
+ Module::GetAllocationModuleCollectionMutex());
+
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+ }
+
+ if (num_dumped > 0)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.AppendError("no matching executable images found");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesDumpSections
+
+// Clang AST dumping command
+
+class CommandObjectTargetModulesDumpClangAST
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpClangAST(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump ast",
+ "Dump the clang ast for a given module's symbol file.",
+ //"target modules dump ast [<file1> ...]")
+ nullptr) {}
+
+ ~CommandObjectTargetModulesDumpClangAST() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const size_t num_modules = target->GetImages().GetSize();
+ if (num_modules == 0) {
+ result.AppendError("the target has no associated executable images");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ // Dump all ASTs for all modules images
+ result.GetOutputStream().Printf("Dumping clang ast for %" PRIu64
+ " modules.\n",
+ (uint64_t)num_modules);
+ for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ Module *m = target->GetImages().GetModulePointerAtIndex(image_idx);
+ SymbolFile *sf = m->GetSymbolVendor()->GetSymbolFile();
+ sf->DumpClangAST(result.GetOutputStream());
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ // Dump specified ASTs (by basename or fullpath)
+ for (const Args::ArgEntry &arg : command.entries()) {
+ ModuleList module_list;
+ const size_t num_matches =
+ FindModulesByName(target, arg.c_str(), module_list, true);
+ if (num_matches == 0) {
+ // Check the global list
+ std::lock_guard<std::recursive_mutex> guard(
+ Module::GetAllocationModuleCollectionMutex());
+
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg.c_str());
+ continue;
+ }
+
+ for (size_t i = 0; i < num_matches; ++i) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ Module *m = module_list.GetModulePointerAtIndex(i);
+ SymbolFile *sf = m->GetSymbolVendor()->GetSymbolFile();
+ sf->DumpClangAST(result.GetOutputStream());
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+#pragma mark CommandObjectTargetModulesDumpSymfile
+
+// Image debug symbol dumping command
+
+class CommandObjectTargetModulesDumpSymfile
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesDumpSymfile(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules dump symfile",
+ "Dump the debug symbol file for one or more target modules.",
+ //"target modules dump symfile [<file1> ...]")
+ nullptr) {}
+
+ ~CommandObjectTargetModulesDumpSymfile() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ uint32_t num_dumped = 0;
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ if (command.GetArgumentCount() == 0) {
+ // Dump all sections for all modules images
+ const ModuleList &target_modules = target->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ const size_t num_modules = target_modules.GetSize();
+ if (num_modules > 0) {
+ result.GetOutputStream().Printf("Dumping debug symbols for %" PRIu64
+ " modules.\n",
+ (uint64_t)num_modules);
+ for (uint32_t image_idx = 0; image_idx < num_modules; ++image_idx) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ if (DumpModuleSymbolVendor(
+ result.GetOutputStream(),
+ target_modules.GetModulePointerAtIndexUnlocked(image_idx)))
+ num_dumped++;
+ }
+ } else {
+ result.AppendError("the target has no associated executable images");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr;
+ for (int arg_idx = 0;
+ (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
+ ++arg_idx) {
+ ModuleList module_list;
+ const size_t num_matches =
+ FindModulesByName(target, arg_cstr, module_list, true);
+ if (num_matches > 0) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ Module *module = module_list.GetModulePointerAtIndex(i);
+ if (module) {
+ if (DumpModuleSymbolVendor(result.GetOutputStream(), module))
+ num_dumped++;
+ }
+ }
+ } else
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+
+ if (num_dumped > 0)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.AppendError("no matching executable images found");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetModulesDumpLineTable
+
+// Image debug line table dumping command
+
+class CommandObjectTargetModulesDumpLineTable
+ : public CommandObjectTargetModulesSourceFileAutoComplete {
+public:
+ CommandObjectTargetModulesDumpLineTable(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesSourceFileAutoComplete(
+ interpreter, "target modules dump line-table",
+ "Dump the line table for one or more compilation units.", nullptr,
+ eCommandRequiresTarget) {}
+
+ ~CommandObjectTargetModulesDumpLineTable() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ uint32_t total_num_dumped = 0;
+
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError("file option must be specified.");
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ } else {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr;
+ for (int arg_idx = 0;
+ (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr;
+ ++arg_idx) {
+ FileSpec file_spec(arg_cstr);
+
+ const ModuleList &target_modules = target->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ const size_t num_modules = target_modules.GetSize();
+ if (num_modules > 0) {
+ uint32_t num_dumped = 0;
+ for (uint32_t i = 0; i < num_modules; ++i) {
+ if (m_interpreter.WasInterrupted())
+ break;
+ if (DumpCompileUnitLineTable(
+ m_interpreter, result.GetOutputStream(),
+ target_modules.GetModulePointerAtIndexUnlocked(i),
+ file_spec,
+ m_options.m_verbose ? eDescriptionLevelFull
+ : eDescriptionLevelBrief))
+ num_dumped++;
+ }
+ if (num_dumped == 0)
+ result.AppendWarningWithFormat(
+ "No source filenames matched '%s'.\n", arg_cstr);
+ else
+ total_num_dumped += num_dumped;
+ }
+ }
+ }
+
+ if (total_num_dumped > 0)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.AppendError("no source filenames matched any command arguments");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ assert(option_idx == 0 && "We only have one option.");
+ m_verbose = true;
+
+ return Status();
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_verbose = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ static constexpr OptionDefinition g_options[] = {
+ {LLDB_OPT_SET_ALL,
+ false,
+ "verbose",
+ 'v',
+ OptionParser::eNoArgument,
+ nullptr,
+ {},
+ 0,
+ eArgTypeNone,
+ "Enable verbose dump."},
+ };
+ return llvm::makeArrayRef(g_options);
+ }
+
+ bool m_verbose;
+ };
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectTargetModulesDump
+
+// Dump multi-word command for target modules
+
+class CommandObjectTargetModulesDump : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectTargetModulesDump(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "target modules dump",
+ "Commands for dumping information about one or "
+ "more target modules.",
+ "target modules dump "
+ "[headers|symtab|sections|ast|symfile|line-table] "
+ "[<file1> <file2> ...]") {
+ LoadSubCommand("objfile",
+ CommandObjectSP(
+ new CommandObjectTargetModulesDumpObjfile(interpreter)));
+ LoadSubCommand(
+ "symtab",
+ CommandObjectSP(new CommandObjectTargetModulesDumpSymtab(interpreter)));
+ LoadSubCommand("sections",
+ CommandObjectSP(new CommandObjectTargetModulesDumpSections(
+ interpreter)));
+ LoadSubCommand("symfile",
+ CommandObjectSP(
+ new CommandObjectTargetModulesDumpSymfile(interpreter)));
+ LoadSubCommand(
+ "ast", CommandObjectSP(
+ new CommandObjectTargetModulesDumpClangAST(interpreter)));
+ LoadSubCommand("line-table",
+ CommandObjectSP(new CommandObjectTargetModulesDumpLineTable(
+ interpreter)));
+ }
+
+ ~CommandObjectTargetModulesDump() override = default;
+};
+
+class CommandObjectTargetModulesAdd : public CommandObjectParsed {
+public:
+ CommandObjectTargetModulesAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules add",
+ "Add a new module to the current target's modules.",
+ "target modules add [<module>]"),
+ m_option_group(),
+ m_symbol_file(LLDB_OPT_SET_1, false, "symfile", 's', 0,
+ eArgTypeFilename, "Fullpath to a stand alone debug "
+ "symbols file for when debug symbols "
+ "are not in the executable.") {
+ m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTargetModulesAdd() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+protected:
+ OptionGroupOptions m_option_group;
+ OptionGroupUUID m_uuid_option_group;
+ OptionGroupFile m_symbol_file;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ bool flush = false;
+
+ const size_t argc = args.GetArgumentCount();
+ if (argc == 0) {
+ if (m_uuid_option_group.GetOptionValue().OptionWasSet()) {
+ // We are given a UUID only, go locate the file
+ ModuleSpec module_spec;
+ module_spec.GetUUID() =
+ m_uuid_option_group.GetOptionValue().GetCurrentValue();
+ if (m_symbol_file.GetOptionValue().OptionWasSet())
+ module_spec.GetSymbolFileSpec() =
+ m_symbol_file.GetOptionValue().GetCurrentValue();
+ if (Symbols::DownloadObjectAndSymbolFile(module_spec)) {
+ ModuleSP module_sp(target->GetOrCreateModule(module_spec,
+ true /* notify */));
+ if (module_sp) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ } else {
+ StreamString strm;
+ module_spec.GetUUID().Dump(&strm);
+ if (module_spec.GetFileSpec()) {
+ if (module_spec.GetSymbolFileSpec()) {
+ result.AppendErrorWithFormat(
+ "Unable to create the executable or symbol file with "
+ "UUID %s with path %s and symbol file %s",
+ strm.GetData(),
+ module_spec.GetFileSpec().GetPath().c_str(),
+ module_spec.GetSymbolFileSpec().GetPath().c_str());
+ } else {
+ result.AppendErrorWithFormat(
+ "Unable to create the executable or symbol file with "
+ "UUID %s with path %s",
+ strm.GetData(),
+ module_spec.GetFileSpec().GetPath().c_str());
+ }
+ } else {
+ result.AppendErrorWithFormat("Unable to create the executable "
+ "or symbol file with UUID %s",
+ strm.GetData());
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ StreamString strm;
+ module_spec.GetUUID().Dump(&strm);
+ result.AppendErrorWithFormat(
+ "Unable to locate the executable or symbol file with UUID %s",
+ strm.GetData());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ result.AppendError(
+ "one or more executable image paths must be specified");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ for (auto &entry : args.entries()) {
+ if (entry.ref.empty())
+ continue;
+
+ FileSpec file_spec(entry.ref);
+ if (FileSystem::Instance().Exists(file_spec)) {
+ ModuleSpec module_spec(file_spec);
+ if (m_uuid_option_group.GetOptionValue().OptionWasSet())
+ module_spec.GetUUID() =
+ m_uuid_option_group.GetOptionValue().GetCurrentValue();
+ if (m_symbol_file.GetOptionValue().OptionWasSet())
+ module_spec.GetSymbolFileSpec() =
+ m_symbol_file.GetOptionValue().GetCurrentValue();
+ if (!module_spec.GetArchitecture().IsValid())
+ module_spec.GetArchitecture() = target->GetArchitecture();
+ Status error;
+ ModuleSP module_sp(target->GetOrCreateModule(module_spec,
+ true /* notify */, &error));
+ if (!module_sp) {
+ const char *error_cstr = error.AsCString();
+ if (error_cstr)
+ result.AppendError(error_cstr);
+ else
+ result.AppendErrorWithFormat("unsupported module: %s",
+ entry.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ flush = true;
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ std::string resolved_path = file_spec.GetPath();
+ result.SetStatus(eReturnStatusFailed);
+ if (resolved_path != entry.ref) {
+ result.AppendErrorWithFormat(
+ "invalid module path '%s' with resolved path '%s'\n",
+ entry.ref.str().c_str(), resolved_path.c_str());
+ break;
+ }
+ result.AppendErrorWithFormat("invalid module path '%s'\n",
+ entry.c_str());
+ break;
+ }
+ }
+ }
+
+ if (flush) {
+ ProcessSP process = target->GetProcessSP();
+ if (process)
+ process->Flush();
+ }
+ }
+
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectTargetModulesLoad
+ : public CommandObjectTargetModulesModuleAutoComplete {
+public:
+ CommandObjectTargetModulesLoad(CommandInterpreter &interpreter)
+ : CommandObjectTargetModulesModuleAutoComplete(
+ interpreter, "target modules load", "Set the load addresses for "
+ "one or more sections in a "
+ "target module.",
+ "target modules load [--file <module> --uuid <uuid>] <sect-name> "
+ "<address> [<sect-name> <address> ....]"),
+ m_option_group(),
+ m_file_option(LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypeName,
+ "Fullpath or basename for module to load.", ""),
+ m_load_option(LLDB_OPT_SET_1, false, "load", 'l',
+ "Write file contents to the memory.", false, true),
+ m_pc_option(LLDB_OPT_SET_1, false, "set-pc-to-entry", 'p',
+ "Set PC to the entry point."
+ " Only applicable with '--load' option.",
+ false, true),
+ m_slide_option(LLDB_OPT_SET_1, false, "slide", 's', 0, eArgTypeOffset,
+ "Set the load address for all sections to be the "
+ "virtual address in the file plus the offset.",
+ 0) {
+ m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_load_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_pc_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_slide_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTargetModulesLoad() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ const bool load = m_load_option.GetOptionValue().GetCurrentValue();
+ const bool set_pc = m_pc_option.GetOptionValue().GetCurrentValue();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ const size_t argc = args.GetArgumentCount();
+ ModuleSpec module_spec;
+ bool search_using_module_spec = false;
+
+ // Allow "load" option to work without --file or --uuid option.
+ if (load) {
+ if (!m_file_option.GetOptionValue().OptionWasSet() &&
+ !m_uuid_option_group.GetOptionValue().OptionWasSet()) {
+ ModuleList &module_list = target->GetImages();
+ if (module_list.GetSize() == 1) {
+ search_using_module_spec = true;
+ module_spec.GetFileSpec() =
+ module_list.GetModuleAtIndex(0)->GetFileSpec();
+ }
+ }
+ }
+
+ if (m_file_option.GetOptionValue().OptionWasSet()) {
+ search_using_module_spec = true;
+ const char *arg_cstr = m_file_option.GetOptionValue().GetCurrentValue();
+ const bool use_global_module_list = true;
+ ModuleList module_list;
+ const size_t num_matches = FindModulesByName(
+ target, arg_cstr, module_list, use_global_module_list);
+ if (num_matches == 1) {
+ module_spec.GetFileSpec() =
+ module_list.GetModuleAtIndex(0)->GetFileSpec();
+ } else if (num_matches > 1) {
+ search_using_module_spec = false;
+ result.AppendErrorWithFormat(
+ "more than 1 module matched by name '%s'\n", arg_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ search_using_module_spec = false;
+ result.AppendErrorWithFormat("no object file for module '%s'\n",
+ arg_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+
+ if (m_uuid_option_group.GetOptionValue().OptionWasSet()) {
+ search_using_module_spec = true;
+ module_spec.GetUUID() =
+ m_uuid_option_group.GetOptionValue().GetCurrentValue();
+ }
+
+ if (search_using_module_spec) {
+ ModuleList matching_modules;
+ const size_t num_matches =
+ target->GetImages().FindModules(module_spec, matching_modules);
+
+ char path[PATH_MAX];
+ if (num_matches == 1) {
+ Module *module = matching_modules.GetModulePointerAtIndex(0);
+ if (module) {
+ ObjectFile *objfile = module->GetObjectFile();
+ if (objfile) {
+ SectionList *section_list = module->GetSectionList();
+ if (section_list) {
+ bool changed = false;
+ if (argc == 0) {
+ if (m_slide_option.GetOptionValue().OptionWasSet()) {
+ const addr_t slide =
+ m_slide_option.GetOptionValue().GetCurrentValue();
+ const bool slide_is_offset = true;
+ module->SetLoadAddress(*target, slide, slide_is_offset,
+ changed);
+ } else {
+ result.AppendError("one or more section name + load "
+ "address pair must be specified");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ if (m_slide_option.GetOptionValue().OptionWasSet()) {
+ result.AppendError("The \"--slide <offset>\" option can't "
+ "be used in conjunction with setting "
+ "section load addresses.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ for (size_t i = 0; i < argc; i += 2) {
+ const char *sect_name = args.GetArgumentAtIndex(i);
+ const char *load_addr_cstr = args.GetArgumentAtIndex(i + 1);
+ if (sect_name && load_addr_cstr) {
+ ConstString const_sect_name(sect_name);
+ bool success = false;
+ addr_t load_addr = StringConvert::ToUInt64(
+ load_addr_cstr, LLDB_INVALID_ADDRESS, 0, &success);
+ if (success) {
+ SectionSP section_sp(
+ section_list->FindSectionByName(const_sect_name));
+ if (section_sp) {
+ if (section_sp->IsThreadSpecific()) {
+ result.AppendErrorWithFormat(
+ "thread specific sections are not yet "
+ "supported (section '%s')\n",
+ sect_name);
+ result.SetStatus(eReturnStatusFailed);
+ break;
+ } else {
+ if (target->GetSectionLoadList()
+ .SetSectionLoadAddress(section_sp,
+ load_addr))
+ changed = true;
+ result.AppendMessageWithFormat(
+ "section '%s' loaded at 0x%" PRIx64 "\n",
+ sect_name, load_addr);
+ }
+ } else {
+ result.AppendErrorWithFormat("no section found that "
+ "matches the section "
+ "name '%s'\n",
+ sect_name);
+ result.SetStatus(eReturnStatusFailed);
+ break;
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "invalid load address string '%s'\n",
+ load_addr_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ break;
+ }
+ } else {
+ if (sect_name)
+ result.AppendError("section names must be followed by "
+ "a load address.\n");
+ else
+ result.AppendError("one or more section name + load "
+ "address pair must be specified.\n");
+ result.SetStatus(eReturnStatusFailed);
+ break;
+ }
+ }
+ }
+
+ if (changed) {
+ target->ModulesDidLoad(matching_modules);
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process)
+ process->Flush();
+ }
+ if (load) {
+ ProcessSP process = target->CalculateProcess();
+ Address file_entry = objfile->GetEntryPointAddress();
+ if (!process) {
+ result.AppendError("No process");
+ return false;
+ }
+ if (set_pc && !file_entry.IsValid()) {
+ result.AppendError("No entry address in object file");
+ return false;
+ }
+ std::vector<ObjectFile::LoadableData> loadables(
+ objfile->GetLoadableData(*target));
+ if (loadables.size() == 0) {
+ result.AppendError("No loadable sections");
+ return false;
+ }
+ Status error = process->WriteObjectFile(std::move(loadables));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+ if (set_pc) {
+ ThreadList &thread_list = process->GetThreadList();
+ RegisterContextSP reg_context(
+ thread_list.GetSelectedThread()->GetRegisterContext());
+ addr_t file_entry_addr = file_entry.GetLoadAddress(target);
+ if (!reg_context->SetPC(file_entry_addr)) {
+ result.AppendErrorWithFormat("failed to set PC value to "
+ "0x%" PRIx64 "\n",
+ file_entry_addr);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ }
+ } else {
+ module->GetFileSpec().GetPath(path, sizeof(path));
+ result.AppendErrorWithFormat(
+ "no sections in object file '%s'\n", path);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ module->GetFileSpec().GetPath(path, sizeof(path));
+ result.AppendErrorWithFormat("no object file for module '%s'\n",
+ path);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ FileSpec *module_spec_file = module_spec.GetFileSpecPtr();
+ if (module_spec_file) {
+ module_spec_file->GetPath(path, sizeof(path));
+ result.AppendErrorWithFormat("invalid module '%s'.\n", path);
+ } else
+ result.AppendError("no module spec");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ std::string uuid_str;
+
+ if (module_spec.GetFileSpec())
+ module_spec.GetFileSpec().GetPath(path, sizeof(path));
+ else
+ path[0] = '\0';
+
+ if (module_spec.GetUUIDPtr())
+ uuid_str = module_spec.GetUUID().GetAsString();
+ if (num_matches > 1) {
+ result.AppendErrorWithFormat(
+ "multiple modules match%s%s%s%s:\n", path[0] ? " file=" : "",
+ path, !uuid_str.empty() ? " uuid=" : "", uuid_str.c_str());
+ for (size_t i = 0; i < num_matches; ++i) {
+ if (matching_modules.GetModulePointerAtIndex(i)
+ ->GetFileSpec()
+ .GetPath(path, sizeof(path)))
+ result.AppendMessageWithFormat("%s\n", path);
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "no modules were found that match%s%s%s%s.\n",
+ path[0] ? " file=" : "", path,
+ !uuid_str.empty() ? " uuid=" : "", uuid_str.c_str());
+ }
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError("either the \"--file <module>\" or the \"--uuid "
+ "<uuid>\" option must be specified.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ return result.Succeeded();
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupUUID m_uuid_option_group;
+ OptionGroupString m_file_option;
+ OptionGroupBoolean m_load_option;
+ OptionGroupBoolean m_pc_option;
+ OptionGroupUInt64 m_slide_option;
+};
+
+// List images with associated information
+
+static constexpr OptionDefinition g_target_modules_list_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Display the image at this address." },
+ { LLDB_OPT_SET_1, false, "arch", 'A', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeWidth, "Display the architecture when listing images." },
+ { LLDB_OPT_SET_1, false, "triple", 't', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeWidth, "Display the triple when listing images." },
+ { LLDB_OPT_SET_1, false, "header", 'h', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Display the image base address as a load address if debugging, a file address otherwise." },
+ { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Display the image load address offset from the base file address (the slide amount)." },
+ { LLDB_OPT_SET_1, false, "uuid", 'u', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Display the UUID when listing images." },
+ { LLDB_OPT_SET_1, false, "fullpath", 'f', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeWidth, "Display the fullpath to the image object file." },
+ { LLDB_OPT_SET_1, false, "directory", 'd', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeWidth, "Display the directory with optional width for the image object file." },
+ { LLDB_OPT_SET_1, false, "basename", 'b', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeWidth, "Display the basename with optional width for the image object file." },
+ { LLDB_OPT_SET_1, false, "symfile", 's', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeWidth, "Display the fullpath to the image symbol file with optional width." },
+ { LLDB_OPT_SET_1, false, "symfile-unique", 'S', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeWidth, "Display the symbol file with optional width only if it is different from the executable object file." },
+ { LLDB_OPT_SET_1, false, "mod-time", 'm', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeWidth, "Display the modification time with optional width of the module." },
+ { LLDB_OPT_SET_1, false, "ref-count", 'r', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeWidth, "Display the reference count if the module is still in the shared module cache." },
+ { LLDB_OPT_SET_1, false, "pointer", 'p', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeNone, "Display the module pointer." },
+ { LLDB_OPT_SET_1, false, "global", 'g', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Display the modules from the global module list, not just the current target." }
+ // clang-format on
+};
+
+class CommandObjectTargetModulesList : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_format_array(), m_use_global_module_list(false),
+ m_module_addr(LLDB_INVALID_ADDRESS) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = m_getopt_table[option_idx].val;
+ if (short_option == 'g') {
+ m_use_global_module_list = true;
+ } else if (short_option == 'a') {
+ m_module_addr = OptionArgParser::ToAddress(
+ execution_context, option_arg, LLDB_INVALID_ADDRESS, &error);
+ } else {
+ unsigned long width = 0;
+ option_arg.getAsInteger(0, width);
+ m_format_array.push_back(std::make_pair(short_option, width));
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_format_array.clear();
+ m_use_global_module_list = false;
+ m_module_addr = LLDB_INVALID_ADDRESS;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_modules_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ typedef std::vector<std::pair<char, uint32_t>> FormatWidthCollection;
+ FormatWidthCollection m_format_array;
+ bool m_use_global_module_list;
+ lldb::addr_t m_module_addr;
+ };
+
+ CommandObjectTargetModulesList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target modules list",
+ "List current executable and dependent shared library images.",
+ "target modules list [<cmd-options>]"),
+ m_options() {}
+
+ ~CommandObjectTargetModulesList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ const bool use_global_module_list = m_options.m_use_global_module_list;
+ // Define a local module list here to ensure it lives longer than any
+ // "locker" object which might lock its contents below (through the
+ // "module_list_ptr" variable).
+ ModuleList module_list;
+ if (target == nullptr && !use_global_module_list) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ if (target) {
+ uint32_t addr_byte_size =
+ target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+ }
+ // Dump all sections for all modules images
+ Stream &strm = result.GetOutputStream();
+
+ if (m_options.m_module_addr != LLDB_INVALID_ADDRESS) {
+ if (target) {
+ Address module_address;
+ if (module_address.SetLoadAddress(m_options.m_module_addr, target)) {
+ ModuleSP module_sp(module_address.GetModule());
+ if (module_sp) {
+ PrintModule(target, module_sp.get(), 0, strm);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Couldn't find module matching address: 0x%" PRIx64 ".",
+ m_options.m_module_addr);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "Couldn't find module containing address: 0x%" PRIx64 ".",
+ m_options.m_module_addr);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendError(
+ "Can only look up modules by address with a valid target.");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ size_t num_modules = 0;
+
+ // This locker will be locked on the mutex in module_list_ptr if it is
+ // non-nullptr. Otherwise it will lock the
+ // AllocationModuleCollectionMutex when accessing the global module list
+ // directly.
+ std::unique_lock<std::recursive_mutex> guard(
+ Module::GetAllocationModuleCollectionMutex(), std::defer_lock);
+
+ const ModuleList *module_list_ptr = nullptr;
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ if (use_global_module_list) {
+ guard.lock();
+ num_modules = Module::GetNumberAllocatedModules();
+ } else {
+ module_list_ptr = &target->GetImages();
+ }
+ } else {
+ // TODO: Convert to entry based iteration. Requires converting
+ // FindModulesByName.
+ for (size_t i = 0; i < argc; ++i) {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr = command.GetArgumentAtIndex(i);
+ const size_t num_matches = FindModulesByName(
+ target, arg_cstr, module_list, use_global_module_list);
+ if (num_matches == 0) {
+ if (argc == 1) {
+ result.AppendErrorWithFormat("no modules found that match '%s'",
+ arg_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ }
+
+ module_list_ptr = &module_list;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ if (module_list_ptr != nullptr) {
+ lock =
+ std::unique_lock<std::recursive_mutex>(module_list_ptr->GetMutex());
+
+ num_modules = module_list_ptr->GetSize();
+ }
+
+ if (num_modules > 0) {
+ for (uint32_t image_idx = 0; image_idx < num_modules; ++image_idx) {
+ ModuleSP module_sp;
+ Module *module;
+ if (module_list_ptr) {
+ module_sp = module_list_ptr->GetModuleAtIndexUnlocked(image_idx);
+ module = module_sp.get();
+ } else {
+ module = Module::GetAllocatedModuleAtIndex(image_idx);
+ module_sp = module->shared_from_this();
+ }
+
+ const size_t indent = strm.Printf("[%3u] ", image_idx);
+ PrintModule(target, module, indent, strm);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ if (argc) {
+ if (use_global_module_list)
+ result.AppendError(
+ "the global module list has no matching modules");
+ else
+ result.AppendError("the target has no matching modules");
+ } else {
+ if (use_global_module_list)
+ result.AppendError("the global module list is empty");
+ else
+ result.AppendError(
+ "the target has no associated executable images");
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ return result.Succeeded();
+ }
+
+ void PrintModule(Target *target, Module *module, int indent, Stream &strm) {
+ if (module == nullptr) {
+ strm.PutCString("Null module");
+ return;
+ }
+
+ bool dump_object_name = false;
+ if (m_options.m_format_array.empty()) {
+ m_options.m_format_array.push_back(std::make_pair('u', 0));
+ m_options.m_format_array.push_back(std::make_pair('h', 0));
+ m_options.m_format_array.push_back(std::make_pair('f', 0));
+ m_options.m_format_array.push_back(std::make_pair('S', 0));
+ }
+ const size_t num_entries = m_options.m_format_array.size();
+ bool print_space = false;
+ for (size_t i = 0; i < num_entries; ++i) {
+ if (print_space)
+ strm.PutChar(' ');
+ print_space = true;
+ const char format_char = m_options.m_format_array[i].first;
+ uint32_t width = m_options.m_format_array[i].second;
+ switch (format_char) {
+ case 'A':
+ DumpModuleArchitecture(strm, module, false, width);
+ break;
+
+ case 't':
+ DumpModuleArchitecture(strm, module, true, width);
+ break;
+
+ case 'f':
+ DumpFullpath(strm, &module->GetFileSpec(), width);
+ dump_object_name = true;
+ break;
+
+ case 'd':
+ DumpDirectory(strm, &module->GetFileSpec(), width);
+ break;
+
+ case 'b':
+ DumpBasename(strm, &module->GetFileSpec(), width);
+ dump_object_name = true;
+ break;
+
+ case 'h':
+ case 'o':
+ // Image header address
+ {
+ uint32_t addr_nibble_width =
+ target ? (target->GetArchitecture().GetAddressByteSize() * 2)
+ : 16;
+
+ ObjectFile *objfile = module->GetObjectFile();
+ if (objfile) {
+ Address base_addr(objfile->GetBaseAddress());
+ if (base_addr.IsValid()) {
+ if (target && !target->GetSectionLoadList().IsEmpty()) {
+ lldb::addr_t load_addr =
+ base_addr.GetLoadAddress(target);
+ if (load_addr == LLDB_INVALID_ADDRESS) {
+ base_addr.Dump(&strm, target,
+ Address::DumpStyleModuleWithFileAddress,
+ Address::DumpStyleFileAddress);
+ } else {
+ if (format_char == 'o') {
+ // Show the offset of slide for the image
+ strm.Printf(
+ "0x%*.*" PRIx64, addr_nibble_width, addr_nibble_width,
+ load_addr - base_addr.GetFileAddress());
+ } else {
+ // Show the load address of the image
+ strm.Printf("0x%*.*" PRIx64, addr_nibble_width,
+ addr_nibble_width, load_addr);
+ }
+ }
+ break;
+ }
+ // The address was valid, but the image isn't loaded, output the
+ // address in an appropriate format
+ base_addr.Dump(&strm, target, Address::DumpStyleFileAddress);
+ break;
+ }
+ }
+ strm.Printf("%*s", addr_nibble_width + 2, "");
+ }
+ break;
+
+ case 'r': {
+ size_t ref_count = 0;
+ ModuleSP module_sp(module->shared_from_this());
+ if (module_sp) {
+ // Take one away to make sure we don't count our local "module_sp"
+ ref_count = module_sp.use_count() - 1;
+ }
+ if (width)
+ strm.Printf("{%*" PRIu64 "}", width, (uint64_t)ref_count);
+ else
+ strm.Printf("{%" PRIu64 "}", (uint64_t)ref_count);
+ } break;
+
+ case 's':
+ case 'S': {
+ const SymbolVendor *symbol_vendor = module->GetSymbolVendor();
+ if (symbol_vendor) {
+ const FileSpec symfile_spec = symbol_vendor->GetMainFileSpec();
+ if (format_char == 'S') {
+ // Dump symbol file only if different from module file
+ if (!symfile_spec || symfile_spec == module->GetFileSpec()) {
+ print_space = false;
+ break;
+ }
+ // Add a newline and indent past the index
+ strm.Printf("\n%*s", indent, "");
+ }
+ DumpFullpath(strm, &symfile_spec, width);
+ dump_object_name = true;
+ break;
+ }
+ strm.Printf("%.*s", width, "<NONE>");
+ } break;
+
+ case 'm':
+ strm.Format("{0:%c}", llvm::fmt_align(module->GetModificationTime(),
+ llvm::AlignStyle::Left, width));
+ break;
+
+ case 'p':
+ strm.Printf("%p", static_cast<void *>(module));
+ break;
+
+ case 'u':
+ DumpModuleUUID(strm, module);
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (dump_object_name) {
+ const char *object_name = module->GetObjectName().GetCString();
+ if (object_name)
+ strm.Printf("(%s)", object_name);
+ }
+ strm.EOL();
+ }
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectTargetModulesShowUnwind
+
+// Lookup unwind information in images
+
+static constexpr OptionDefinition g_target_modules_show_unwind_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeFunctionName, "Show unwind instructions for a function or symbol name." },
+ { LLDB_OPT_SET_2, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Show unwind instructions for a function or symbol containing an address" }
+ // clang-format on
+};
+
+class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed {
+public:
+ enum {
+ eLookupTypeInvalid = -1,
+ eLookupTypeAddress = 0,
+ eLookupTypeSymbol,
+ eLookupTypeFunction,
+ eLookupTypeFunctionOrSymbol,
+ kNumLookupTypes
+ };
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_type(eLookupTypeInvalid), m_str(),
+ m_addr(LLDB_INVALID_ADDRESS) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a': {
+ m_str = option_arg;
+ m_type = eLookupTypeAddress;
+ m_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ if (m_addr == LLDB_INVALID_ADDRESS)
+ error.SetErrorStringWithFormat("invalid address string '%s'",
+ option_arg.str().c_str());
+ break;
+ }
+
+ case 'n':
+ m_str = option_arg;
+ m_type = eLookupTypeFunctionOrSymbol;
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option %c.", short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_type = eLookupTypeInvalid;
+ m_str.clear();
+ m_addr = LLDB_INVALID_ADDRESS;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_modules_show_unwind_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ int m_type; // Should be a eLookupTypeXXX enum after parsing options
+ std::string m_str; // Holds name lookup
+ lldb::addr_t m_addr; // Holds the address to lookup
+ };
+
+ CommandObjectTargetModulesShowUnwind(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target modules show-unwind",
+ "Show synthesized unwind instructions for a function.", nullptr,
+ eCommandRequiresTarget | eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+ m_options() {}
+
+ ~CommandObjectTargetModulesShowUnwind() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ Process *process = m_exe_ctx.GetProcessPtr();
+ ABI *abi = nullptr;
+ if (process)
+ abi = process->GetABI().get();
+
+ if (process == nullptr) {
+ result.AppendError(
+ "You must have a process running to use this command.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ThreadList threads(process->GetThreadList());
+ if (threads.GetSize() == 0) {
+ result.AppendError("The process must be paused to use this command.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ThreadSP thread(threads.GetThreadAtIndex(0));
+ if (!thread) {
+ result.AppendError("The process must be paused to use this command.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ SymbolContextList sc_list;
+
+ if (m_options.m_type == eLookupTypeFunctionOrSymbol) {
+ ConstString function_name(m_options.m_str.c_str());
+ target->GetImages().FindFunctions(function_name, eFunctionNameTypeAuto,
+ true, false, true, sc_list);
+ } else if (m_options.m_type == eLookupTypeAddress && target) {
+ Address addr;
+ if (target->GetSectionLoadList().ResolveLoadAddress(m_options.m_addr,
+ addr)) {
+ SymbolContext sc;
+ ModuleSP module_sp(addr.GetModule());
+ module_sp->ResolveSymbolContextForAddress(addr,
+ eSymbolContextEverything, sc);
+ if (sc.function || sc.symbol) {
+ sc_list.Append(sc);
+ }
+ }
+ } else {
+ result.AppendError(
+ "address-expression or function name option must be specified.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ size_t num_matches = sc_list.GetSize();
+ if (num_matches == 0) {
+ result.AppendErrorWithFormat("no unwind data found that matches '%s'.",
+ m_options.m_str.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ for (uint32_t idx = 0; idx < num_matches; idx++) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(idx, sc);
+ if (sc.symbol == nullptr && sc.function == nullptr)
+ continue;
+ if (!sc.module_sp || sc.module_sp->GetObjectFile() == nullptr)
+ continue;
+ AddressRange range;
+ if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
+ false, range))
+ continue;
+ if (!range.GetBaseAddress().IsValid())
+ continue;
+ ConstString funcname(sc.GetFunctionName());
+ if (funcname.IsEmpty())
+ continue;
+ addr_t start_addr = range.GetBaseAddress().GetLoadAddress(target);
+ if (abi)
+ start_addr = abi->FixCodeAddress(start_addr);
+
+ FuncUnwindersSP func_unwinders_sp(
+ sc.module_sp->GetUnwindTable()
+ .GetUncachedFuncUnwindersContainingAddress(start_addr, sc));
+ if (!func_unwinders_sp)
+ continue;
+
+ result.GetOutputStream().Printf(
+ "UNWIND PLANS for %s`%s (start addr 0x%" PRIx64 ")\n\n",
+ sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(),
+ funcname.AsCString(), start_addr);
+
+ UnwindPlanSP non_callsite_unwind_plan =
+ func_unwinders_sp->GetUnwindPlanAtNonCallSite(*target, *thread);
+ if (non_callsite_unwind_plan) {
+ result.GetOutputStream().Printf(
+ "Asynchronous (not restricted to call-sites) UnwindPlan is '%s'\n",
+ non_callsite_unwind_plan->GetSourceName().AsCString());
+ }
+ UnwindPlanSP callsite_unwind_plan =
+ func_unwinders_sp->GetUnwindPlanAtCallSite(*target, *thread);
+ if (callsite_unwind_plan) {
+ result.GetOutputStream().Printf(
+ "Synchronous (restricted to call-sites) UnwindPlan is '%s'\n",
+ callsite_unwind_plan->GetSourceName().AsCString());
+ }
+ UnwindPlanSP fast_unwind_plan =
+ func_unwinders_sp->GetUnwindPlanFastUnwind(*target, *thread);
+ if (fast_unwind_plan) {
+ result.GetOutputStream().Printf(
+ "Fast UnwindPlan is '%s'\n",
+ fast_unwind_plan->GetSourceName().AsCString());
+ }
+
+ result.GetOutputStream().Printf("\n");
+
+ UnwindPlanSP assembly_sp =
+ func_unwinders_sp->GetAssemblyUnwindPlan(*target, *thread);
+ if (assembly_sp) {
+ result.GetOutputStream().Printf(
+ "Assembly language inspection UnwindPlan:\n");
+ assembly_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlanSP ehframe_sp =
+ func_unwinders_sp->GetEHFrameUnwindPlan(*target);
+ if (ehframe_sp) {
+ result.GetOutputStream().Printf("eh_frame UnwindPlan:\n");
+ ehframe_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlanSP ehframe_augmented_sp =
+ func_unwinders_sp->GetEHFrameAugmentedUnwindPlan(*target, *thread);
+ if (ehframe_augmented_sp) {
+ result.GetOutputStream().Printf("eh_frame augmented UnwindPlan:\n");
+ ehframe_augmented_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ if (UnwindPlanSP plan_sp =
+ func_unwinders_sp->GetDebugFrameUnwindPlan(*target)) {
+ result.GetOutputStream().Printf("debug_frame UnwindPlan:\n");
+ plan_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ if (UnwindPlanSP plan_sp =
+ func_unwinders_sp->GetDebugFrameAugmentedUnwindPlan(*target,
+ *thread)) {
+ result.GetOutputStream().Printf("debug_frame augmented UnwindPlan:\n");
+ plan_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlanSP arm_unwind_sp =
+ func_unwinders_sp->GetArmUnwindUnwindPlan(*target);
+ if (arm_unwind_sp) {
+ result.GetOutputStream().Printf("ARM.exidx unwind UnwindPlan:\n");
+ arm_unwind_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ if (UnwindPlanSP symfile_plan_sp =
+ func_unwinders_sp->GetSymbolFileUnwindPlan(*thread)) {
+ result.GetOutputStream().Printf("Symbol file UnwindPlan:\n");
+ symfile_plan_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlanSP compact_unwind_sp =
+ func_unwinders_sp->GetCompactUnwindUnwindPlan(*target);
+ if (compact_unwind_sp) {
+ result.GetOutputStream().Printf("Compact unwind UnwindPlan:\n");
+ compact_unwind_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ if (fast_unwind_plan) {
+ result.GetOutputStream().Printf("Fast UnwindPlan:\n");
+ fast_unwind_plan->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ ABISP abi_sp = process->GetABI();
+ if (abi_sp) {
+ UnwindPlan arch_default(lldb::eRegisterKindGeneric);
+ if (abi_sp->CreateDefaultUnwindPlan(arch_default)) {
+ result.GetOutputStream().Printf("Arch default UnwindPlan:\n");
+ arch_default.Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlan arch_entry(lldb::eRegisterKindGeneric);
+ if (abi_sp->CreateFunctionEntryUnwindPlan(arch_entry)) {
+ result.GetOutputStream().Printf(
+ "Arch default at entry point UnwindPlan:\n");
+ arch_entry.Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+ }
+
+ result.GetOutputStream().Printf("\n");
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// Lookup information in images
+
+static constexpr OptionDefinition g_target_modules_lookup_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_1, true, "address", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Lookup an address in one or more target modules." },
+ { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOffset, "When looking up an address subtract <offset> from any addresses before doing the lookup." },
+ /* FIXME: re-enable regex for types when the LookupTypeInModule actually uses the regex option: | LLDB_OPT_SET_6 */
+ { LLDB_OPT_SET_2 | LLDB_OPT_SET_4 | LLDB_OPT_SET_5, false, "regex", 'r', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "The <name> argument for name lookups are regular expressions." },
+ { LLDB_OPT_SET_2, true, "symbol", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeSymbol, "Lookup a symbol by name in the symbol tables in one or more target modules." },
+ { LLDB_OPT_SET_3, true, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeFilename, "Lookup a file by fullpath or basename in one or more target modules." },
+ { LLDB_OPT_SET_3, false, "line", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLineNum, "Lookup a line number in a file (must be used in conjunction with --file)." },
+ { LLDB_OPT_SET_FROM_TO(3,5), false, "no-inlines", 'i', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Ignore inline entries (must be used in conjunction with --file or --function)." },
+ { LLDB_OPT_SET_4, true, "function", 'F', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeFunctionName, "Lookup a function by name in the debug symbols in one or more target modules." },
+ { LLDB_OPT_SET_5, true, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeFunctionOrSymbol, "Lookup a function or symbol by name in one or more target modules." },
+ { LLDB_OPT_SET_6, true, "type", 't', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeName, "Lookup a type by name in the debug symbols in one or more target modules." },
+ { LLDB_OPT_SET_ALL, false, "verbose", 'v', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Enable verbose lookup information." },
+ { LLDB_OPT_SET_ALL, false, "all", 'A', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Print all matches, not just the best match, if a best match is available." },
+ // clang-format on
+};
+
+class CommandObjectTargetModulesLookup : public CommandObjectParsed {
+public:
+ enum {
+ eLookupTypeInvalid = -1,
+ eLookupTypeAddress = 0,
+ eLookupTypeSymbol,
+ eLookupTypeFileLine, // Line is optional
+ eLookupTypeFunction,
+ eLookupTypeFunctionOrSymbol,
+ eLookupTypeType,
+ kNumLookupTypes
+ };
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a': {
+ m_type = eLookupTypeAddress;
+ m_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ } break;
+
+ case 'o':
+ if (option_arg.getAsInteger(0, m_offset))
+ error.SetErrorStringWithFormat("invalid offset string '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 's':
+ m_str = option_arg;
+ m_type = eLookupTypeSymbol;
+ break;
+
+ case 'f':
+ m_file.SetFile(option_arg, FileSpec::Style::native);
+ m_type = eLookupTypeFileLine;
+ break;
+
+ case 'i':
+ m_include_inlines = false;
+ break;
+
+ case 'l':
+ if (option_arg.getAsInteger(0, m_line_number))
+ error.SetErrorStringWithFormat("invalid line number string '%s'",
+ option_arg.str().c_str());
+ else if (m_line_number == 0)
+ error.SetErrorString("zero is an invalid line number");
+ m_type = eLookupTypeFileLine;
+ break;
+
+ case 'F':
+ m_str = option_arg;
+ m_type = eLookupTypeFunction;
+ break;
+
+ case 'n':
+ m_str = option_arg;
+ m_type = eLookupTypeFunctionOrSymbol;
+ break;
+
+ case 't':
+ m_str = option_arg;
+ m_type = eLookupTypeType;
+ break;
+
+ case 'v':
+ m_verbose = true;
+ break;
+
+ case 'A':
+ m_print_all = true;
+ break;
+
+ case 'r':
+ m_use_regex = true;
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_type = eLookupTypeInvalid;
+ m_str.clear();
+ m_file.Clear();
+ m_addr = LLDB_INVALID_ADDRESS;
+ m_offset = 0;
+ m_line_number = 0;
+ m_use_regex = false;
+ m_include_inlines = true;
+ m_verbose = false;
+ m_print_all = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_modules_lookup_options);
+ }
+
+ int m_type; // Should be a eLookupTypeXXX enum after parsing options
+ std::string m_str; // Holds name lookup
+ FileSpec m_file; // Files for file lookups
+ lldb::addr_t m_addr; // Holds the address to lookup
+ lldb::addr_t
+ m_offset; // Subtract this offset from m_addr before doing lookups.
+ uint32_t m_line_number; // Line number for file+line lookups
+ bool m_use_regex; // Name lookups in m_str are regular expressions.
+ bool m_include_inlines; // Check for inline entries when looking up by
+ // file/line.
+ bool m_verbose; // Enable verbose lookup info
+ bool m_print_all; // Print all matches, even in cases where there's a best
+ // match.
+ };
+
+ CommandObjectTargetModulesLookup(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target modules lookup",
+ "Look up information within executable and "
+ "dependent shared library images.",
+ nullptr, eCommandRequiresTarget),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData file_arg;
+
+ // Define the first (and only) variant of this arg.
+ file_arg.arg_type = eArgTypeFilename;
+ file_arg.arg_repetition = eArgRepeatStar;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(file_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectTargetModulesLookup() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool LookupHere(CommandInterpreter &interpreter, CommandReturnObject &result,
+ bool &syntax_error) {
+ switch (m_options.m_type) {
+ case eLookupTypeAddress:
+ case eLookupTypeFileLine:
+ case eLookupTypeFunction:
+ case eLookupTypeFunctionOrSymbol:
+ case eLookupTypeSymbol:
+ default:
+ return false;
+ case eLookupTypeType:
+ break;
+ }
+
+ StackFrameSP frame = m_exe_ctx.GetFrameSP();
+
+ if (!frame)
+ return false;
+
+ const SymbolContext &sym_ctx(frame->GetSymbolContext(eSymbolContextModule));
+
+ if (!sym_ctx.module_sp)
+ return false;
+
+ switch (m_options.m_type) {
+ default:
+ return false;
+ case eLookupTypeType:
+ if (!m_options.m_str.empty()) {
+ if (LookupTypeHere(m_interpreter, result.GetOutputStream(),
+ *sym_ctx.module_sp, m_options.m_str.c_str(),
+ m_options.m_use_regex)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+ }
+
+ return true;
+ }
+
+ bool LookupInModule(CommandInterpreter &interpreter, Module *module,
+ CommandReturnObject &result, bool &syntax_error) {
+ switch (m_options.m_type) {
+ case eLookupTypeAddress:
+ if (m_options.m_addr != LLDB_INVALID_ADDRESS) {
+ if (LookupAddressInModule(
+ m_interpreter, result.GetOutputStream(), module,
+ eSymbolContextEverything |
+ (m_options.m_verbose
+ ? static_cast<int>(eSymbolContextVariable)
+ : 0),
+ m_options.m_addr, m_options.m_offset, m_options.m_verbose)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+
+ case eLookupTypeSymbol:
+ if (!m_options.m_str.empty()) {
+ if (LookupSymbolInModule(m_interpreter, result.GetOutputStream(),
+ module, m_options.m_str.c_str(),
+ m_options.m_use_regex, m_options.m_verbose)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+
+ case eLookupTypeFileLine:
+ if (m_options.m_file) {
+ if (LookupFileAndLineInModule(
+ m_interpreter, result.GetOutputStream(), module,
+ m_options.m_file, m_options.m_line_number,
+ m_options.m_include_inlines, m_options.m_verbose)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+
+ case eLookupTypeFunctionOrSymbol:
+ case eLookupTypeFunction:
+ if (!m_options.m_str.empty()) {
+ if (LookupFunctionInModule(
+ m_interpreter, result.GetOutputStream(), module,
+ m_options.m_str.c_str(), m_options.m_use_regex,
+ m_options.m_include_inlines,
+ m_options.m_type ==
+ eLookupTypeFunctionOrSymbol, // include symbols
+ m_options.m_verbose)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+
+ case eLookupTypeType:
+ if (!m_options.m_str.empty()) {
+ if (LookupTypeInModule(m_interpreter, result.GetOutputStream(), module,
+ m_options.m_str.c_str(),
+ m_options.m_use_regex)) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ break;
+
+ default:
+ m_options.GenerateOptionUsage(
+ result.GetErrorStream(), this,
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ syntax_error = true;
+ break;
+ }
+
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ bool syntax_error = false;
+ uint32_t i;
+ uint32_t num_successful_lookups = 0;
+ uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize();
+ result.GetOutputStream().SetAddressByteSize(addr_byte_size);
+ result.GetErrorStream().SetAddressByteSize(addr_byte_size);
+ // Dump all sections for all modules images
+
+ if (command.GetArgumentCount() == 0) {
+ ModuleSP current_module;
+
+ // Where it is possible to look in the current symbol context first,
+ // try that. If this search was successful and --all was not passed,
+ // don't print anything else.
+ if (LookupHere(m_interpreter, result, syntax_error)) {
+ result.GetOutputStream().EOL();
+ num_successful_lookups++;
+ if (!m_options.m_print_all) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+ }
+
+ // Dump all sections for all other modules
+
+ const ModuleList &target_modules = target->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ const size_t num_modules = target_modules.GetSize();
+ if (num_modules > 0) {
+ for (i = 0; i < num_modules && !syntax_error; ++i) {
+ Module *module_pointer =
+ target_modules.GetModulePointerAtIndexUnlocked(i);
+
+ if (module_pointer != current_module.get() &&
+ LookupInModule(
+ m_interpreter,
+ target_modules.GetModulePointerAtIndexUnlocked(i), result,
+ syntax_error)) {
+ result.GetOutputStream().EOL();
+ num_successful_lookups++;
+ }
+ }
+ } else {
+ result.AppendError("the target has no associated executable images");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ // Dump specified images (by basename or fullpath)
+ const char *arg_cstr;
+ for (i = 0; (arg_cstr = command.GetArgumentAtIndex(i)) != nullptr &&
+ !syntax_error;
+ ++i) {
+ ModuleList module_list;
+ const size_t num_matches =
+ FindModulesByName(target, arg_cstr, module_list, false);
+ if (num_matches > 0) {
+ for (size_t j = 0; j < num_matches; ++j) {
+ Module *module = module_list.GetModulePointerAtIndex(j);
+ if (module) {
+ if (LookupInModule(m_interpreter, module, result,
+ syntax_error)) {
+ result.GetOutputStream().EOL();
+ num_successful_lookups++;
+ }
+ }
+ }
+ } else
+ result.AppendWarningWithFormat(
+ "Unable to find an image that matches '%s'.\n", arg_cstr);
+ }
+ }
+
+ if (num_successful_lookups > 0)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+#pragma mark CommandObjectMultiwordImageSearchPaths
+
+// CommandObjectMultiwordImageSearchPaths
+
+class CommandObjectTargetModulesImageSearchPaths
+ : public CommandObjectMultiword {
+public:
+ CommandObjectTargetModulesImageSearchPaths(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "target modules search-paths",
+ "Commands for managing module search paths for a target.",
+ "target modules search-paths <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "add", CommandObjectSP(
+ new CommandObjectTargetModulesSearchPathsAdd(interpreter)));
+ LoadSubCommand(
+ "clear", CommandObjectSP(new CommandObjectTargetModulesSearchPathsClear(
+ interpreter)));
+ LoadSubCommand(
+ "insert",
+ CommandObjectSP(
+ new CommandObjectTargetModulesSearchPathsInsert(interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectTargetModulesSearchPathsList(
+ interpreter)));
+ LoadSubCommand(
+ "query", CommandObjectSP(new CommandObjectTargetModulesSearchPathsQuery(
+ interpreter)));
+ }
+
+ ~CommandObjectTargetModulesImageSearchPaths() override = default;
+};
+
+#pragma mark CommandObjectTargetModules
+
+// CommandObjectTargetModules
+
+class CommandObjectTargetModules : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectTargetModules(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "target modules",
+ "Commands for accessing information for one or "
+ "more target modules.",
+ "target modules <sub-command> ...") {
+ LoadSubCommand(
+ "add", CommandObjectSP(new CommandObjectTargetModulesAdd(interpreter)));
+ LoadSubCommand("load", CommandObjectSP(new CommandObjectTargetModulesLoad(
+ interpreter)));
+ LoadSubCommand("dump", CommandObjectSP(new CommandObjectTargetModulesDump(
+ interpreter)));
+ LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetModulesList(
+ interpreter)));
+ LoadSubCommand(
+ "lookup",
+ CommandObjectSP(new CommandObjectTargetModulesLookup(interpreter)));
+ LoadSubCommand(
+ "search-paths",
+ CommandObjectSP(
+ new CommandObjectTargetModulesImageSearchPaths(interpreter)));
+ LoadSubCommand(
+ "show-unwind",
+ CommandObjectSP(new CommandObjectTargetModulesShowUnwind(interpreter)));
+ }
+
+ ~CommandObjectTargetModules() override = default;
+
+private:
+ // For CommandObjectTargetModules only
+ DISALLOW_COPY_AND_ASSIGN(CommandObjectTargetModules);
+};
+
+class CommandObjectTargetSymbolsAdd : public CommandObjectParsed {
+public:
+ CommandObjectTargetSymbolsAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "target symbols add",
+ "Add a debug symbol file to one of the target's current modules by "
+ "specifying a path to a debug symbols file, or using the options "
+ "to specify a module to download symbols for.",
+ "target symbols add <cmd-options> [<symfile>]",
+ eCommandRequiresTarget),
+ m_option_group(),
+ m_file_option(
+ LLDB_OPT_SET_1, false, "shlib", 's',
+ CommandCompletions::eModuleCompletion, eArgTypeShlibName,
+ "Fullpath or basename for module to find debug symbols for."),
+ m_current_frame_option(
+ LLDB_OPT_SET_2, false, "frame", 'F',
+ "Locate the debug symbols the currently selected frame.", false,
+ true)
+
+ {
+ m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_current_frame_option, LLDB_OPT_SET_2,
+ LLDB_OPT_SET_2);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTargetSymbolsAdd() override = default;
+
+ int HandleArgumentCompletion(
+ CompletionRequest &request,
+ OptionElementVector &opt_element_vector) override {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion,
+ request, nullptr);
+ return request.GetNumberOfMatches();
+ }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool AddModuleSymbols(Target *target, ModuleSpec &module_spec, bool &flush,
+ CommandReturnObject &result) {
+ const FileSpec &symbol_fspec = module_spec.GetSymbolFileSpec();
+ if (symbol_fspec) {
+ char symfile_path[PATH_MAX];
+ symbol_fspec.GetPath(symfile_path, sizeof(symfile_path));
+
+ if (!module_spec.GetUUID().IsValid()) {
+ if (!module_spec.GetFileSpec() && !module_spec.GetPlatformFileSpec())
+ module_spec.GetFileSpec().GetFilename() = symbol_fspec.GetFilename();
+ }
+ // We now have a module that represents a symbol file that can be used
+ // for a module that might exist in the current target, so we need to
+ // find that module in the target
+ ModuleList matching_module_list;
+
+ size_t num_matches = 0;
+ // First extract all module specs from the symbol file
+ lldb_private::ModuleSpecList symfile_module_specs;
+ if (ObjectFile::GetModuleSpecifications(module_spec.GetSymbolFileSpec(),
+ 0, 0, symfile_module_specs)) {
+ // Now extract the module spec that matches the target architecture
+ ModuleSpec target_arch_module_spec;
+ ModuleSpec symfile_module_spec;
+ target_arch_module_spec.GetArchitecture() = target->GetArchitecture();
+ if (symfile_module_specs.FindMatchingModuleSpec(target_arch_module_spec,
+ symfile_module_spec)) {
+ // See if it has a UUID?
+ if (symfile_module_spec.GetUUID().IsValid()) {
+ // It has a UUID, look for this UUID in the target modules
+ ModuleSpec symfile_uuid_module_spec;
+ symfile_uuid_module_spec.GetUUID() = symfile_module_spec.GetUUID();
+ num_matches = target->GetImages().FindModules(
+ symfile_uuid_module_spec, matching_module_list);
+ }
+ }
+
+ if (num_matches == 0) {
+ // No matches yet, iterate through the module specs to find a UUID
+ // value that we can match up to an image in our target
+ const size_t num_symfile_module_specs =
+ symfile_module_specs.GetSize();
+ for (size_t i = 0; i < num_symfile_module_specs && num_matches == 0;
+ ++i) {
+ if (symfile_module_specs.GetModuleSpecAtIndex(
+ i, symfile_module_spec)) {
+ if (symfile_module_spec.GetUUID().IsValid()) {
+ // It has a UUID, look for this UUID in the target modules
+ ModuleSpec symfile_uuid_module_spec;
+ symfile_uuid_module_spec.GetUUID() =
+ symfile_module_spec.GetUUID();
+ num_matches = target->GetImages().FindModules(
+ symfile_uuid_module_spec, matching_module_list);
+ }
+ }
+ }
+ }
+ }
+
+ // Just try to match up the file by basename if we have no matches at
+ // this point
+ if (num_matches == 0)
+ num_matches =
+ target->GetImages().FindModules(module_spec, matching_module_list);
+
+ while (num_matches == 0) {
+ ConstString filename_no_extension(
+ module_spec.GetFileSpec().GetFileNameStrippingExtension());
+ // Empty string returned, lets bail
+ if (!filename_no_extension)
+ break;
+
+ // Check if there was no extension to strip and the basename is the
+ // same
+ if (filename_no_extension == module_spec.GetFileSpec().GetFilename())
+ break;
+
+ // Replace basename with one less extension
+ module_spec.GetFileSpec().GetFilename() = filename_no_extension;
+
+ num_matches =
+ target->GetImages().FindModules(module_spec, matching_module_list);
+ }
+
+ if (num_matches > 1) {
+ result.AppendErrorWithFormat("multiple modules match symbol file '%s', "
+ "use the --uuid option to resolve the "
+ "ambiguity.\n",
+ symfile_path);
+ } else if (num_matches == 1) {
+ ModuleSP module_sp(matching_module_list.GetModuleAtIndex(0));
+
+ // The module has not yet created its symbol vendor, we can just give
+ // the existing target module the symfile path to use for when it
+ // decides to create it!
+ module_sp->SetSymbolFileFileSpec(symbol_fspec);
+
+ SymbolVendor *symbol_vendor =
+ module_sp->GetSymbolVendor(true, &result.GetErrorStream());
+ if (symbol_vendor) {
+ SymbolFile *symbol_file = symbol_vendor->GetSymbolFile();
+
+ if (symbol_file) {
+ ObjectFile *object_file = symbol_file->GetObjectFile();
+
+ if (object_file && object_file->GetFileSpec() == symbol_fspec) {
+ // Provide feedback that the symfile has been successfully added.
+ const FileSpec &module_fs = module_sp->GetFileSpec();
+ result.AppendMessageWithFormat(
+ "symbol file '%s' has been added to '%s'\n", symfile_path,
+ module_fs.GetPath().c_str());
+
+ // Let clients know something changed in the module if it is
+ // currently loaded
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ target->SymbolsDidLoad(module_list);
+
+ // Make sure we load any scripting resources that may be embedded
+ // in the debug info files in case the platform supports that.
+ Status error;
+ StreamString feedback_stream;
+ module_sp->LoadScriptingResourceInTarget(target, error,
+ &feedback_stream);
+ if (error.Fail() && error.AsCString())
+ result.AppendWarningWithFormat(
+ "unable to load scripting data for module %s - error "
+ "reported was %s",
+ module_sp->GetFileSpec()
+ .GetFileNameStrippingExtension()
+ .GetCString(),
+ error.AsCString());
+ else if (feedback_stream.GetSize())
+ result.AppendWarningWithFormat("%s", feedback_stream.GetData());
+
+ flush = true;
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ }
+ // Clear the symbol file spec if anything went wrong
+ module_sp->SetSymbolFileFileSpec(FileSpec());
+ }
+
+ namespace fs = llvm::sys::fs;
+ if (module_spec.GetUUID().IsValid()) {
+ StreamString ss_symfile_uuid;
+ module_spec.GetUUID().Dump(&ss_symfile_uuid);
+ result.AppendErrorWithFormat(
+ "symbol file '%s' (%s) does not match any existing module%s\n",
+ symfile_path, ss_symfile_uuid.GetData(),
+ !fs::is_regular_file(symbol_fspec.GetPath())
+ ? "\n please specify the full path to the symbol file"
+ : "");
+ } else {
+ result.AppendErrorWithFormat(
+ "symbol file '%s' does not match any existing module%s\n",
+ symfile_path,
+ !fs::is_regular_file(symbol_fspec.GetPath())
+ ? "\n please specify the full path to the symbol file"
+ : "");
+ }
+ } else {
+ result.AppendError(
+ "one or more executable image paths must be specified");
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ result.SetStatus(eReturnStatusFailed);
+ bool flush = false;
+ ModuleSpec module_spec;
+ const bool uuid_option_set =
+ m_uuid_option_group.GetOptionValue().OptionWasSet();
+ const bool file_option_set = m_file_option.GetOptionValue().OptionWasSet();
+ const bool frame_option_set =
+ m_current_frame_option.GetOptionValue().OptionWasSet();
+ const size_t argc = args.GetArgumentCount();
+
+ if (argc == 0) {
+ if (uuid_option_set || file_option_set || frame_option_set) {
+ bool success = false;
+ bool error_set = false;
+ if (frame_option_set) {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process) {
+ const StateType process_state = process->GetState();
+ if (StateIsStoppedState(process_state, true)) {
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ if (frame) {
+ ModuleSP frame_module_sp(
+ frame->GetSymbolContext(eSymbolContextModule).module_sp);
+ if (frame_module_sp) {
+ if (FileSystem::Instance().Exists(
+ frame_module_sp->GetPlatformFileSpec())) {
+ module_spec.GetArchitecture() =
+ frame_module_sp->GetArchitecture();
+ module_spec.GetFileSpec() =
+ frame_module_sp->GetPlatformFileSpec();
+ }
+ module_spec.GetUUID() = frame_module_sp->GetUUID();
+ success = module_spec.GetUUID().IsValid() ||
+ module_spec.GetFileSpec();
+ } else {
+ result.AppendError("frame has no module");
+ error_set = true;
+ }
+ } else {
+ result.AppendError("invalid current frame");
+ error_set = true;
+ }
+ } else {
+ result.AppendErrorWithFormat("process is not stopped: %s",
+ StateAsCString(process_state));
+ error_set = true;
+ }
+ } else {
+ result.AppendError(
+ "a process must exist in order to use the --frame option");
+ error_set = true;
+ }
+ } else {
+ if (uuid_option_set) {
+ module_spec.GetUUID() =
+ m_uuid_option_group.GetOptionValue().GetCurrentValue();
+ success |= module_spec.GetUUID().IsValid();
+ } else if (file_option_set) {
+ module_spec.GetFileSpec() =
+ m_file_option.GetOptionValue().GetCurrentValue();
+ ModuleSP module_sp(
+ target->GetImages().FindFirstModule(module_spec));
+ if (module_sp) {
+ module_spec.GetFileSpec() = module_sp->GetFileSpec();
+ module_spec.GetPlatformFileSpec() =
+ module_sp->GetPlatformFileSpec();
+ module_spec.GetUUID() = module_sp->GetUUID();
+ module_spec.GetArchitecture() = module_sp->GetArchitecture();
+ } else {
+ module_spec.GetArchitecture() = target->GetArchitecture();
+ }
+ success |= module_spec.GetUUID().IsValid() ||
+ FileSystem::Instance().Exists(module_spec.GetFileSpec());
+ }
+ }
+
+ if (success) {
+ if (Symbols::DownloadObjectAndSymbolFile(module_spec)) {
+ if (module_spec.GetSymbolFileSpec())
+ success = AddModuleSymbols(target, module_spec, flush, result);
+ }
+ }
+
+ if (!success && !error_set) {
+ StreamString error_strm;
+ if (uuid_option_set) {
+ error_strm.PutCString("unable to find debug symbols for UUID ");
+ module_spec.GetUUID().Dump(&error_strm);
+ } else if (file_option_set) {
+ error_strm.PutCString(
+ "unable to find debug symbols for the executable file ");
+ error_strm << module_spec.GetFileSpec();
+ } else if (frame_option_set) {
+ error_strm.PutCString(
+ "unable to find debug symbols for the current frame");
+ }
+ result.AppendError(error_strm.GetString());
+ }
+ } else {
+ result.AppendError("one or more symbol file paths must be specified, "
+ "or options must be specified");
+ }
+ } else {
+ if (uuid_option_set) {
+ result.AppendError("specify either one or more paths to symbol files "
+ "or use the --uuid option without arguments");
+ } else if (frame_option_set) {
+ result.AppendError("specify either one or more paths to symbol files "
+ "or use the --frame option without arguments");
+ } else if (file_option_set && argc > 1) {
+ result.AppendError("specify at most one symbol file path when "
+ "--shlib option is set");
+ } else {
+ PlatformSP platform_sp(target->GetPlatform());
+
+ for (auto &entry : args.entries()) {
+ if (!entry.ref.empty()) {
+ auto &symbol_file_spec = module_spec.GetSymbolFileSpec();
+ symbol_file_spec.SetFile(entry.ref, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(symbol_file_spec);
+ if (file_option_set) {
+ module_spec.GetFileSpec() =
+ m_file_option.GetOptionValue().GetCurrentValue();
+ }
+ if (platform_sp) {
+ FileSpec symfile_spec;
+ if (platform_sp
+ ->ResolveSymbolFile(*target, module_spec, symfile_spec)
+ .Success())
+ module_spec.GetSymbolFileSpec() = symfile_spec;
+ }
+
+ ArchSpec arch;
+ bool symfile_exists =
+ FileSystem::Instance().Exists(module_spec.GetSymbolFileSpec());
+
+ if (symfile_exists) {
+ if (!AddModuleSymbols(target, module_spec, flush, result))
+ break;
+ } else {
+ std::string resolved_symfile_path =
+ module_spec.GetSymbolFileSpec().GetPath();
+ if (resolved_symfile_path != entry.ref) {
+ result.AppendErrorWithFormat(
+ "invalid module path '%s' with resolved path '%s'\n",
+ entry.c_str(), resolved_symfile_path.c_str());
+ break;
+ }
+ result.AppendErrorWithFormat("invalid module path '%s'\n",
+ entry.c_str());
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (flush) {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process)
+ process->Flush();
+ }
+ return result.Succeeded();
+ }
+
+ OptionGroupOptions m_option_group;
+ OptionGroupUUID m_uuid_option_group;
+ OptionGroupFile m_file_option;
+ OptionGroupBoolean m_current_frame_option;
+};
+
+#pragma mark CommandObjectTargetSymbols
+
+// CommandObjectTargetSymbols
+
+class CommandObjectTargetSymbols : public CommandObjectMultiword {
+public:
+ // Constructors and Destructors
+ CommandObjectTargetSymbols(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "target symbols",
+ "Commands for adding and managing debug symbol files.",
+ "target symbols <sub-command> ...") {
+ LoadSubCommand(
+ "add", CommandObjectSP(new CommandObjectTargetSymbolsAdd(interpreter)));
+ }
+
+ ~CommandObjectTargetSymbols() override = default;
+
+private:
+ // For CommandObjectTargetModules only
+ DISALLOW_COPY_AND_ASSIGN(CommandObjectTargetSymbols);
+};
+
+#pragma mark CommandObjectTargetStopHookAdd
+
+// CommandObjectTargetStopHookAdd
+
+static constexpr OptionDefinition g_target_stop_hook_add_options[] = {
+ // clang-format off
+ { LLDB_OPT_SET_ALL, false, "one-liner", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOneLiner, "Add a command for the stop hook. Can be specified more than once, and commands will be run in the order they appear." },
+ { LLDB_OPT_SET_ALL, false, "shlib", 's', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Set the module within which the stop-hook is to be run." },
+ { LLDB_OPT_SET_ALL, false, "thread-index", 'x', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeThreadIndex, "The stop hook is run only for the thread whose index matches this argument." },
+ { LLDB_OPT_SET_ALL, false, "thread-id", 't', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeThreadID, "The stop hook is run only for the thread whose TID matches this argument." },
+ { LLDB_OPT_SET_ALL, false, "thread-name", 'T', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeThreadName, "The stop hook is run only for the thread whose thread name matches this argument." },
+ { LLDB_OPT_SET_ALL, false, "queue-name", 'q', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeQueueName, "The stop hook is run only for threads in the queue whose name is given by this argument." },
+ { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "Specify the source file within which the stop-hook is to be run." },
+ { LLDB_OPT_SET_1, false, "start-line", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLineNum, "Set the start of the line range for which the stop-hook is to be run." },
+ { LLDB_OPT_SET_1, false, "end-line", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLineNum, "Set the end of the line range for which the stop-hook is to be run." },
+ { LLDB_OPT_SET_2, false, "classname", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeClassName, "Specify the class within which the stop-hook is to be run." },
+ { LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, "Set the function name within which the stop hook will be run." },
+ { LLDB_OPT_SET_ALL, false, "auto-continue",'G', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "The breakpoint will auto-continue after running its commands." },
+ // clang-format on
+};
+
+class CommandObjectTargetStopHookAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_line_start(0), m_line_end(UINT_MAX),
+ m_func_name_type_mask(eFunctionNameTypeAuto),
+ m_sym_ctx_specified(false), m_thread_specified(false),
+ m_use_one_liner(false), m_one_liner() {}
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_target_stop_hook_add_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c':
+ m_class_name = option_arg;
+ m_sym_ctx_specified = true;
+ break;
+
+ case 'e':
+ if (option_arg.getAsInteger(0, m_line_end)) {
+ error.SetErrorStringWithFormat("invalid end line number: \"%s\"",
+ option_arg.str().c_str());
+ break;
+ }
+ m_sym_ctx_specified = true;
+ break;
+
+ case 'G': {
+ bool value, success;
+ value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success) {
+ m_auto_continue = value;
+ } else
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' passed for -G option",
+ option_arg.str().c_str());
+ }
+ break;
+ case 'l':
+ if (option_arg.getAsInteger(0, m_line_start)) {
+ error.SetErrorStringWithFormat("invalid start line number: \"%s\"",
+ option_arg.str().c_str());
+ break;
+ }
+ m_sym_ctx_specified = true;
+ break;
+
+ case 'i':
+ m_no_inlines = true;
+ break;
+
+ case 'n':
+ m_function_name = option_arg;
+ m_func_name_type_mask |= eFunctionNameTypeAuto;
+ m_sym_ctx_specified = true;
+ break;
+
+ case 'f':
+ m_file_name = option_arg;
+ m_sym_ctx_specified = true;
+ break;
+
+ case 's':
+ m_module_name = option_arg;
+ m_sym_ctx_specified = true;
+ break;
+
+ case 't':
+ if (option_arg.getAsInteger(0, m_thread_id))
+ error.SetErrorStringWithFormat("invalid thread id string '%s'",
+ option_arg.str().c_str());
+ m_thread_specified = true;
+ break;
+
+ case 'T':
+ m_thread_name = option_arg;
+ m_thread_specified = true;
+ break;
+
+ case 'q':
+ m_queue_name = option_arg;
+ m_thread_specified = true;
+ break;
+
+ case 'x':
+ if (option_arg.getAsInteger(0, m_thread_index))
+ error.SetErrorStringWithFormat("invalid thread index string '%s'",
+ option_arg.str().c_str());
+ m_thread_specified = true;
+ break;
+
+ case 'o':
+ m_use_one_liner = true;
+ m_one_liner.push_back(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option %c.", short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_class_name.clear();
+ m_function_name.clear();
+ m_line_start = 0;
+ m_line_end = UINT_MAX;
+ m_file_name.clear();
+ m_module_name.clear();
+ m_func_name_type_mask = eFunctionNameTypeAuto;
+ m_thread_id = LLDB_INVALID_THREAD_ID;
+ m_thread_index = UINT32_MAX;
+ m_thread_name.clear();
+ m_queue_name.clear();
+
+ m_no_inlines = false;
+ m_sym_ctx_specified = false;
+ m_thread_specified = false;
+
+ m_use_one_liner = false;
+ m_one_liner.clear();
+ m_auto_continue = false;
+ }
+
+ std::string m_class_name;
+ std::string m_function_name;
+ uint32_t m_line_start;
+ uint32_t m_line_end;
+ std::string m_file_name;
+ std::string m_module_name;
+ uint32_t m_func_name_type_mask; // A pick from lldb::FunctionNameType.
+ lldb::tid_t m_thread_id;
+ uint32_t m_thread_index;
+ std::string m_thread_name;
+ std::string m_queue_name;
+ bool m_sym_ctx_specified;
+ bool m_no_inlines;
+ bool m_thread_specified;
+ // Instance variables to hold the values for one_liner options.
+ bool m_use_one_liner;
+ std::vector<std::string> m_one_liner;
+ bool m_auto_continue;
+ };
+
+ CommandObjectTargetStopHookAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target stop-hook add",
+ "Add a hook to be executed when the target stops.",
+ "target stop-hook add"),
+ IOHandlerDelegateMultiline("DONE",
+ IOHandlerDelegate::Completion::LLDBCommand),
+ m_options() {}
+
+ ~CommandObjectTargetStopHookAdd() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ if (output_sp && interactive) {
+ output_sp->PutCString(
+ "Enter your stop hook command(s). Type 'DONE' to end.\n");
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) override {
+ if (m_stop_hook_sp) {
+ if (line.empty()) {
+ StreamFileSP error_sp(io_handler.GetErrorStreamFile());
+ if (error_sp) {
+ error_sp->Printf("error: stop hook #%" PRIu64
+ " aborted, no commands.\n",
+ m_stop_hook_sp->GetID());
+ error_sp->Flush();
+ }
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target)
+ target->RemoveStopHookByID(m_stop_hook_sp->GetID());
+ } else {
+ m_stop_hook_sp->GetCommandPointer()->SplitIntoLines(line);
+ StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ if (output_sp) {
+ output_sp->Printf("Stop hook #%" PRIu64 " added.\n",
+ m_stop_hook_sp->GetID());
+ output_sp->Flush();
+ }
+ }
+ m_stop_hook_sp.reset();
+ }
+ io_handler.SetIsDone(true);
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ m_stop_hook_sp.reset();
+
+ Target *target = GetSelectedOrDummyTarget();
+ if (target) {
+ Target::StopHookSP new_hook_sp = target->CreateStopHook();
+
+ // First step, make the specifier.
+ std::unique_ptr<SymbolContextSpecifier> specifier_up;
+ if (m_options.m_sym_ctx_specified) {
+ specifier_up.reset(
+ new SymbolContextSpecifier(GetDebugger().GetSelectedTarget()));
+
+ if (!m_options.m_module_name.empty()) {
+ specifier_up->AddSpecification(
+ m_options.m_module_name.c_str(),
+ SymbolContextSpecifier::eModuleSpecified);
+ }
+
+ if (!m_options.m_class_name.empty()) {
+ specifier_up->AddSpecification(
+ m_options.m_class_name.c_str(),
+ SymbolContextSpecifier::eClassOrNamespaceSpecified);
+ }
+
+ if (!m_options.m_file_name.empty()) {
+ specifier_up->AddSpecification(
+ m_options.m_file_name.c_str(),
+ SymbolContextSpecifier::eFileSpecified);
+ }
+
+ if (m_options.m_line_start != 0) {
+ specifier_up->AddLineSpecification(
+ m_options.m_line_start,
+ SymbolContextSpecifier::eLineStartSpecified);
+ }
+
+ if (m_options.m_line_end != UINT_MAX) {
+ specifier_up->AddLineSpecification(
+ m_options.m_line_end, SymbolContextSpecifier::eLineEndSpecified);
+ }
+
+ if (!m_options.m_function_name.empty()) {
+ specifier_up->AddSpecification(
+ m_options.m_function_name.c_str(),
+ SymbolContextSpecifier::eFunctionSpecified);
+ }
+ }
+
+ if (specifier_up)
+ new_hook_sp->SetSpecifier(specifier_up.release());
+
+ // Next see if any of the thread options have been entered:
+
+ if (m_options.m_thread_specified) {
+ ThreadSpec *thread_spec = new ThreadSpec();
+
+ if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID) {
+ thread_spec->SetTID(m_options.m_thread_id);
+ }
+
+ if (m_options.m_thread_index != UINT32_MAX)
+ thread_spec->SetIndex(m_options.m_thread_index);
+
+ if (!m_options.m_thread_name.empty())
+ thread_spec->SetName(m_options.m_thread_name.c_str());
+
+ if (!m_options.m_queue_name.empty())
+ thread_spec->SetQueueName(m_options.m_queue_name.c_str());
+
+ new_hook_sp->SetThreadSpecifier(thread_spec);
+ }
+
+ new_hook_sp->SetAutoContinue(m_options.m_auto_continue);
+ if (m_options.m_use_one_liner) {
+ // Use one-liners.
+ for (auto cmd : m_options.m_one_liner)
+ new_hook_sp->GetCommandPointer()->AppendString(
+ cmd.c_str());
+ result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n",
+ new_hook_sp->GetID());
+ } else {
+ m_stop_hook_sp = new_hook_sp;
+ m_interpreter.GetLLDBCommandsFromIOHandler(
+ "> ", // Prompt
+ *this, // IOHandlerDelegate
+ true, // Run IOHandler in async mode
+ nullptr); // Baton for the "io_handler" that will be passed back
+ // into our IOHandlerDelegate functions
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("invalid target\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+ Target::StopHookSP m_stop_hook_sp;
+};
+
+#pragma mark CommandObjectTargetStopHookDelete
+
+// CommandObjectTargetStopHookDelete
+
+class CommandObjectTargetStopHookDelete : public CommandObjectParsed {
+public:
+ CommandObjectTargetStopHookDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target stop-hook delete",
+ "Delete a stop-hook.",
+ "target stop-hook delete [<idx>]") {}
+
+ ~CommandObjectTargetStopHookDelete() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget();
+ if (target) {
+ // FIXME: see if we can use the breakpoint id style parser?
+ size_t num_args = command.GetArgumentCount();
+ if (num_args == 0) {
+ if (!m_interpreter.Confirm("Delete all stop hooks?", true)) {
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ target->RemoveAllStopHooks();
+ }
+ } else {
+ bool success;
+ for (size_t i = 0; i < num_args; i++) {
+ lldb::user_id_t user_id = StringConvert::ToUInt32(
+ command.GetArgumentAtIndex(i), 0, 0, &success);
+ if (!success) {
+ result.AppendErrorWithFormat("invalid stop hook id: \"%s\".\n",
+ command.GetArgumentAtIndex(i));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ success = target->RemoveStopHookByID(user_id);
+ if (!success) {
+ result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n",
+ command.GetArgumentAtIndex(i));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("invalid target\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectTargetStopHookEnableDisable
+
+// CommandObjectTargetStopHookEnableDisable
+
+class CommandObjectTargetStopHookEnableDisable : public CommandObjectParsed {
+public:
+ CommandObjectTargetStopHookEnableDisable(CommandInterpreter &interpreter,
+ bool enable, const char *name,
+ const char *help, const char *syntax)
+ : CommandObjectParsed(interpreter, name, help, syntax), m_enable(enable) {
+ }
+
+ ~CommandObjectTargetStopHookEnableDisable() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget();
+ if (target) {
+ // FIXME: see if we can use the breakpoint id style parser?
+ size_t num_args = command.GetArgumentCount();
+ bool success;
+
+ if (num_args == 0) {
+ target->SetAllStopHooksActiveState(m_enable);
+ } else {
+ for (size_t i = 0; i < num_args; i++) {
+ lldb::user_id_t user_id = StringConvert::ToUInt32(
+ command.GetArgumentAtIndex(i), 0, 0, &success);
+ if (!success) {
+ result.AppendErrorWithFormat("invalid stop hook id: \"%s\".\n",
+ command.GetArgumentAtIndex(i));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ success = target->SetStopHookActiveStateByID(user_id, m_enable);
+ if (!success) {
+ result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n",
+ command.GetArgumentAtIndex(i));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("invalid target\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+private:
+ bool m_enable;
+};
+
+#pragma mark CommandObjectTargetStopHookList
+
+// CommandObjectTargetStopHookList
+
+class CommandObjectTargetStopHookList : public CommandObjectParsed {
+public:
+ CommandObjectTargetStopHookList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "target stop-hook list",
+ "List all stop-hooks.",
+ "target stop-hook list [<type>]") {}
+
+ ~CommandObjectTargetStopHookList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetSelectedOrDummyTarget();
+ if (!target) {
+ result.AppendError("invalid target\n");
+ result.SetStatus(eReturnStatusFailed);
+ return result.Succeeded();
+ }
+
+ size_t num_hooks = target->GetNumStopHooks();
+ if (num_hooks == 0) {
+ result.GetOutputStream().PutCString("No stop hooks.\n");
+ } else {
+ for (size_t i = 0; i < num_hooks; i++) {
+ Target::StopHookSP this_hook = target->GetStopHookAtIndex(i);
+ if (i > 0)
+ result.GetOutputStream().PutCString("\n");
+ this_hook->GetDescription(&(result.GetOutputStream()),
+ eDescriptionLevelFull);
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+#pragma mark CommandObjectMultiwordTargetStopHooks
+
+// CommandObjectMultiwordTargetStopHooks
+
+class CommandObjectMultiwordTargetStopHooks : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordTargetStopHooks(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "target stop-hook",
+ "Commands for operating on debugger target stop-hooks.",
+ "target stop-hook <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("add", CommandObjectSP(
+ new CommandObjectTargetStopHookAdd(interpreter)));
+ LoadSubCommand(
+ "delete",
+ CommandObjectSP(new CommandObjectTargetStopHookDelete(interpreter)));
+ LoadSubCommand("disable",
+ CommandObjectSP(new CommandObjectTargetStopHookEnableDisable(
+ interpreter, false, "target stop-hook disable [<id>]",
+ "Disable a stop-hook.", "target stop-hook disable")));
+ LoadSubCommand("enable",
+ CommandObjectSP(new CommandObjectTargetStopHookEnableDisable(
+ interpreter, true, "target stop-hook enable [<id>]",
+ "Enable a stop-hook.", "target stop-hook enable")));
+ LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetStopHookList(
+ interpreter)));
+ }
+
+ ~CommandObjectMultiwordTargetStopHooks() override = default;
+};
+
+#pragma mark CommandObjectMultiwordTarget
+
+// CommandObjectMultiwordTarget
+
+CommandObjectMultiwordTarget::CommandObjectMultiwordTarget(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "target",
+ "Commands for operating on debugger targets.",
+ "target <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("create",
+ CommandObjectSP(new CommandObjectTargetCreate(interpreter)));
+ LoadSubCommand("delete",
+ CommandObjectSP(new CommandObjectTargetDelete(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectTargetList(interpreter)));
+ LoadSubCommand("select",
+ CommandObjectSP(new CommandObjectTargetSelect(interpreter)));
+ LoadSubCommand(
+ "stop-hook",
+ CommandObjectSP(new CommandObjectMultiwordTargetStopHooks(interpreter)));
+ LoadSubCommand("modules",
+ CommandObjectSP(new CommandObjectTargetModules(interpreter)));
+ LoadSubCommand("symbols",
+ CommandObjectSP(new CommandObjectTargetSymbols(interpreter)));
+ LoadSubCommand("variable",
+ CommandObjectSP(new CommandObjectTargetVariable(interpreter)));
+}
+
+CommandObjectMultiwordTarget::~CommandObjectMultiwordTarget() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.h
new file mode 100644
index 000000000000..86d554c8a310
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectTarget.h
@@ -0,0 +1,28 @@
+//===-- CommandObjectTarget.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectTarget_h_
+#define liblldb_CommandObjectTarget_h_
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/Options.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordTarget
+
+class CommandObjectMultiwordTarget : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordTarget(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordTarget() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectTarget_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp
new file mode 100644
index 000000000000..ed7cf0a1a48d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.cpp
@@ -0,0 +1,2112 @@
+//===-- CommandObjectThread.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectThread.h"
+
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanStepInRange.h"
+#include "lldb/Target/ThreadPlanStepInstruction.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Target/ThreadPlanStepRange.h"
+#include "lldb/Utility/State.h"
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectIterateOverThreads
+
+class CommandObjectIterateOverThreads : public CommandObjectParsed {
+
+ class UniqueStack {
+
+ public:
+ UniqueStack(std::stack<lldb::addr_t> stack_frames, uint32_t thread_index_id)
+ : m_stack_frames(stack_frames) {
+ m_thread_index_ids.push_back(thread_index_id);
+ }
+
+ void AddThread(uint32_t thread_index_id) const {
+ m_thread_index_ids.push_back(thread_index_id);
+ }
+
+ const std::vector<uint32_t> &GetUniqueThreadIndexIDs() const {
+ return m_thread_index_ids;
+ }
+
+ lldb::tid_t GetRepresentativeThread() const {
+ return m_thread_index_ids.front();
+ }
+
+ friend bool inline operator<(const UniqueStack &lhs,
+ const UniqueStack &rhs) {
+ return lhs.m_stack_frames < rhs.m_stack_frames;
+ }
+
+ protected:
+ // Mark the thread index as mutable, as we don't care about it from a const
+ // perspective, we only care about m_stack_frames so we keep our std::set
+ // sorted.
+ mutable std::vector<uint32_t> m_thread_index_ids;
+ std::stack<lldb::addr_t> m_stack_frames;
+ };
+
+public:
+ CommandObjectIterateOverThreads(CommandInterpreter &interpreter,
+ const char *name, const char *help,
+ const char *syntax, uint32_t flags)
+ : CommandObjectParsed(interpreter, name, help, syntax, flags) {}
+
+ ~CommandObjectIterateOverThreads() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ result.SetStatus(m_success_return);
+
+ bool all_threads = false;
+ if (command.GetArgumentCount() == 0) {
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ if (!thread || !HandleOneThread(thread->GetID(), result))
+ return false;
+ return result.Succeeded();
+ } else if (command.GetArgumentCount() == 1) {
+ all_threads = ::strcmp(command.GetArgumentAtIndex(0), "all") == 0;
+ m_unique_stacks = ::strcmp(command.GetArgumentAtIndex(0), "unique") == 0;
+ }
+
+ // Use tids instead of ThreadSPs to prevent deadlocking problems which
+ // result from JIT-ing code while iterating over the (locked) ThreadSP
+ // list.
+ std::vector<lldb::tid_t> tids;
+
+ if (all_threads || m_unique_stacks) {
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ for (ThreadSP thread_sp : process->Threads())
+ tids.push_back(thread_sp->GetID());
+ } else {
+ const size_t num_args = command.GetArgumentCount();
+ Process *process = m_exe_ctx.GetProcessPtr();
+
+ std::lock_guard<std::recursive_mutex> guard(
+ process->GetThreadList().GetMutex());
+
+ for (size_t i = 0; i < num_args; i++) {
+ bool success;
+
+ uint32_t thread_idx = StringConvert::ToUInt32(
+ command.GetArgumentAtIndex(i), 0, 0, &success);
+ if (!success) {
+ result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
+ command.GetArgumentAtIndex(i));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ThreadSP thread =
+ process->GetThreadList().FindThreadByIndexID(thread_idx);
+
+ if (!thread) {
+ result.AppendErrorWithFormat("no thread with index: \"%s\"\n",
+ command.GetArgumentAtIndex(i));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ tids.push_back(thread->GetID());
+ }
+ }
+
+ if (m_unique_stacks) {
+ // Iterate over threads, finding unique stack buckets.
+ std::set<UniqueStack> unique_stacks;
+ for (const lldb::tid_t &tid : tids) {
+ if (!BucketThread(tid, unique_stacks, result)) {
+ return false;
+ }
+ }
+
+ // Write the thread id's and unique call stacks to the output stream
+ Stream &strm = result.GetOutputStream();
+ Process *process = m_exe_ctx.GetProcessPtr();
+ for (const UniqueStack &stack : unique_stacks) {
+ // List the common thread ID's
+ const std::vector<uint32_t> &thread_index_ids =
+ stack.GetUniqueThreadIndexIDs();
+ strm.Format("{0} thread(s) ", thread_index_ids.size());
+ for (const uint32_t &thread_index_id : thread_index_ids) {
+ strm.Format("#{0} ", thread_index_id);
+ }
+ strm.EOL();
+
+ // List the shared call stack for this set of threads
+ uint32_t representative_thread_id = stack.GetRepresentativeThread();
+ ThreadSP thread = process->GetThreadList().FindThreadByIndexID(
+ representative_thread_id);
+ if (!HandleOneThread(thread->GetID(), result)) {
+ return false;
+ }
+ }
+ } else {
+ uint32_t idx = 0;
+ for (const lldb::tid_t &tid : tids) {
+ if (idx != 0 && m_add_return)
+ result.AppendMessage("");
+
+ if (!HandleOneThread(tid, result))
+ return false;
+
+ ++idx;
+ }
+ }
+ return result.Succeeded();
+ }
+
+protected:
+ // Override this to do whatever you need to do for one thread.
+ //
+ // If you return false, the iteration will stop, otherwise it will proceed.
+ // The result is set to m_success_return (defaults to
+ // eReturnStatusSuccessFinishResult) before the iteration, so you only need
+ // to set the return status in HandleOneThread if you want to indicate an
+ // error. If m_add_return is true, a blank line will be inserted between each
+ // of the listings (except the last one.)
+
+ virtual bool HandleOneThread(lldb::tid_t, CommandReturnObject &result) = 0;
+
+ bool BucketThread(lldb::tid_t tid, std::set<UniqueStack> &unique_stacks,
+ CommandReturnObject &result) {
+ // Grab the corresponding thread for the given thread id.
+ Process *process = m_exe_ctx.GetProcessPtr();
+ Thread *thread = process->GetThreadList().FindThreadByID(tid).get();
+ if (thread == nullptr) {
+ result.AppendErrorWithFormatv("Failed to process thread #{0}.\n", tid);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Collect the each frame's address for this call-stack
+ std::stack<lldb::addr_t> stack_frames;
+ const uint32_t frame_count = thread->GetStackFrameCount();
+ for (uint32_t frame_index = 0; frame_index < frame_count; frame_index++) {
+ const lldb::StackFrameSP frame_sp =
+ thread->GetStackFrameAtIndex(frame_index);
+ const lldb::addr_t pc = frame_sp->GetStackID().GetPC();
+ stack_frames.push(pc);
+ }
+
+ uint32_t thread_index_id = thread->GetIndexID();
+ UniqueStack new_unique_stack(stack_frames, thread_index_id);
+
+ // Try to match the threads stack to and existing entry.
+ std::set<UniqueStack>::iterator matching_stack =
+ unique_stacks.find(new_unique_stack);
+ if (matching_stack != unique_stacks.end()) {
+ matching_stack->AddThread(thread_index_id);
+ } else {
+ unique_stacks.insert(new_unique_stack);
+ }
+ return true;
+ }
+
+ ReturnStatus m_success_return = eReturnStatusSuccessFinishResult;
+ bool m_unique_stacks = false;
+ bool m_add_return = true;
+};
+
+// CommandObjectThreadBacktrace
+
+static constexpr OptionDefinition g_thread_backtrace_options[] = {
+#define LLDB_OPTIONS_thread_backtrace
+#include "CommandOptions.inc"
+};
+
+class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c': {
+ int32_t input_count = 0;
+ if (option_arg.getAsInteger(0, m_count)) {
+ m_count = UINT32_MAX;
+ error.SetErrorStringWithFormat(
+ "invalid integer value for option '%c'", short_option);
+ } else if (input_count < 0)
+ m_count = UINT32_MAX;
+ } break;
+ case 's':
+ if (option_arg.getAsInteger(0, m_start))
+ error.SetErrorStringWithFormat(
+ "invalid integer value for option '%c'", short_option);
+ break;
+ case 'e': {
+ bool success;
+ m_extended_backtrace =
+ OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid boolean value for option '%c'", short_option);
+ } break;
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_count = UINT32_MAX;
+ m_start = 0;
+ m_extended_backtrace = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_backtrace_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ uint32_t m_count;
+ uint32_t m_start;
+ bool m_extended_backtrace;
+ };
+
+ CommandObjectThreadBacktrace(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread backtrace",
+ "Show thread call stacks. Defaults to the current thread, thread "
+ "indexes can be specified as arguments.\n"
+ "Use the thread-index \"all\" to see all threads.\n"
+ "Use the thread-index \"unique\" to see threads grouped by unique "
+ "call stacks.\n"
+ "Use 'settings set frame-format' to customize the printing of "
+ "frames in the backtrace and 'settings set thread-format' to "
+ "customize the thread header.",
+ nullptr,
+ eCommandRequiresProcess | eCommandRequiresThread |
+ eCommandTryTargetAPILock | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options() {}
+
+ ~CommandObjectThreadBacktrace() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ void DoExtendedBacktrace(Thread *thread, CommandReturnObject &result) {
+ SystemRuntime *runtime = thread->GetProcess()->GetSystemRuntime();
+ if (runtime) {
+ Stream &strm = result.GetOutputStream();
+ const std::vector<ConstString> &types =
+ runtime->GetExtendedBacktraceTypes();
+ for (auto type : types) {
+ ThreadSP ext_thread_sp = runtime->GetExtendedBacktraceThread(
+ thread->shared_from_this(), type);
+ if (ext_thread_sp && ext_thread_sp->IsValid()) {
+ const uint32_t num_frames_with_source = 0;
+ const bool stop_format = false;
+ if (ext_thread_sp->GetStatus(strm, m_options.m_start,
+ m_options.m_count,
+ num_frames_with_source,
+ stop_format)) {
+ DoExtendedBacktrace(ext_thread_sp.get(), result);
+ }
+ }
+ }
+ }
+ }
+
+ bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+ ThreadSP thread_sp =
+ m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+ if (!thread_sp) {
+ result.AppendErrorWithFormat(
+ "thread disappeared while computing backtraces: 0x%" PRIx64 "\n",
+ tid);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Thread *thread = thread_sp.get();
+
+ Stream &strm = result.GetOutputStream();
+
+ // Only dump stack info if we processing unique stacks.
+ const bool only_stacks = m_unique_stacks;
+
+ // Don't show source context when doing backtraces.
+ const uint32_t num_frames_with_source = 0;
+ const bool stop_format = true;
+ if (!thread->GetStatus(strm, m_options.m_start, m_options.m_count,
+ num_frames_with_source, stop_format, only_stacks)) {
+ result.AppendErrorWithFormat(
+ "error displaying backtrace for thread: \"0x%4.4x\"\n",
+ thread->GetIndexID());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ if (m_options.m_extended_backtrace) {
+ DoExtendedBacktrace(thread, result);
+ }
+
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+enum StepScope { eStepScopeSource, eStepScopeInstruction };
+
+static constexpr OptionEnumValueElement g_tri_running_mode[] = {
+ {eOnlyThisThread, "this-thread", "Run only this thread"},
+ {eAllThreads, "all-threads", "Run all threads"},
+ {eOnlyDuringStepping, "while-stepping",
+ "Run only this thread while stepping"} };
+
+static constexpr OptionEnumValues TriRunningModes() {
+ return OptionEnumValues(g_tri_running_mode);
+}
+
+static constexpr OptionDefinition g_thread_step_scope_options[] = {
+#define LLDB_OPTIONS_thread_step_scope
+#include "CommandOptions.inc"
+};
+
+class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a': {
+ bool success;
+ bool avoid_no_debug =
+ OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid boolean value for option '%c'", short_option);
+ else {
+ m_step_in_avoid_no_debug =
+ avoid_no_debug ? eLazyBoolYes : eLazyBoolNo;
+ }
+ } break;
+
+ case 'A': {
+ bool success;
+ bool avoid_no_debug =
+ OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid boolean value for option '%c'", short_option);
+ else {
+ m_step_out_avoid_no_debug =
+ avoid_no_debug ? eLazyBoolYes : eLazyBoolNo;
+ }
+ } break;
+
+ case 'c':
+ if (option_arg.getAsInteger(0, m_step_count))
+ error.SetErrorStringWithFormat("invalid step count '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'C':
+ m_class_name.clear();
+ m_class_name.assign(option_arg);
+ break;
+
+ case 'm': {
+ auto enum_values = GetDefinitions()[option_idx].enum_values;
+ m_run_mode = (lldb::RunMode)OptionArgParser::ToOptionEnum(
+ option_arg, enum_values, eOnlyDuringStepping, error);
+ } break;
+
+ case 'e':
+ if (option_arg == "block") {
+ m_end_line_is_block_end = true;
+ break;
+ }
+ if (option_arg.getAsInteger(0, m_end_line))
+ error.SetErrorStringWithFormat("invalid end line number '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'r':
+ m_avoid_regexp.clear();
+ m_avoid_regexp.assign(option_arg);
+ break;
+
+ case 't':
+ m_step_in_target.clear();
+ m_step_in_target.assign(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_step_in_avoid_no_debug = eLazyBoolCalculate;
+ m_step_out_avoid_no_debug = eLazyBoolCalculate;
+ m_run_mode = eOnlyDuringStepping;
+
+ // Check if we are in Non-Stop mode
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ if (target_sp && target_sp->GetNonStopModeEnabled())
+ m_run_mode = eOnlyThisThread;
+
+ m_avoid_regexp.clear();
+ m_step_in_target.clear();
+ m_class_name.clear();
+ m_step_count = 1;
+ m_end_line = LLDB_INVALID_LINE_NUMBER;
+ m_end_line_is_block_end = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_step_scope_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ LazyBool m_step_in_avoid_no_debug;
+ LazyBool m_step_out_avoid_no_debug;
+ RunMode m_run_mode;
+ std::string m_avoid_regexp;
+ std::string m_step_in_target;
+ std::string m_class_name;
+ uint32_t m_step_count;
+ uint32_t m_end_line;
+ bool m_end_line_is_block_end;
+ };
+
+ CommandObjectThreadStepWithTypeAndScope(CommandInterpreter &interpreter,
+ const char *name, const char *help,
+ const char *syntax,
+ StepType step_type,
+ StepScope step_scope)
+ : CommandObjectParsed(interpreter, name, help, syntax,
+ eCommandRequiresProcess | eCommandRequiresThread |
+ eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_step_type(step_type), m_step_scope(step_scope), m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData thread_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ thread_id_arg.arg_type = eArgTypeThreadID;
+ thread_id_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(thread_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadStepWithTypeAndScope() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ bool synchronous_execution = m_interpreter.GetSynchronous();
+
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+ Thread *thread = nullptr;
+
+ if (command.GetArgumentCount() == 0) {
+ thread = GetDefaultThread();
+
+ if (thread == nullptr) {
+ result.AppendError("no selected thread in process");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ const char *thread_idx_cstr = command.GetArgumentAtIndex(0);
+ uint32_t step_thread_idx =
+ StringConvert::ToUInt32(thread_idx_cstr, LLDB_INVALID_INDEX32);
+ if (step_thread_idx == LLDB_INVALID_INDEX32) {
+ result.AppendErrorWithFormat("invalid thread index '%s'.\n",
+ thread_idx_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ thread =
+ process->GetThreadList().FindThreadByIndexID(step_thread_idx).get();
+ if (thread == nullptr) {
+ result.AppendErrorWithFormat(
+ "Thread index %u is out of range (valid values are 0 - %u).\n",
+ step_thread_idx, num_threads);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ if (m_step_type == eStepTypeScripted) {
+ if (m_options.m_class_name.empty()) {
+ result.AppendErrorWithFormat("empty class name for scripted step.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (!GetDebugger().GetScriptInterpreter()->CheckObjectExists(
+ m_options.m_class_name.c_str())) {
+ result.AppendErrorWithFormat(
+ "class for scripted step: \"%s\" does not exist.",
+ m_options.m_class_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ if (m_options.m_end_line != LLDB_INVALID_LINE_NUMBER &&
+ m_step_type != eStepTypeInto) {
+ result.AppendErrorWithFormat(
+ "end line option is only valid for step into");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const bool abort_other_plans = false;
+ const lldb::RunMode stop_other_threads = m_options.m_run_mode;
+
+ // This is a bit unfortunate, but not all the commands in this command
+ // object support only while stepping, so I use the bool for them.
+ bool bool_stop_other_threads;
+ if (m_options.m_run_mode == eAllThreads)
+ bool_stop_other_threads = false;
+ else if (m_options.m_run_mode == eOnlyDuringStepping)
+ bool_stop_other_threads =
+ (m_step_type != eStepTypeOut && m_step_type != eStepTypeScripted);
+ else
+ bool_stop_other_threads = true;
+
+ ThreadPlanSP new_plan_sp;
+ Status new_plan_status;
+
+ if (m_step_type == eStepTypeInto) {
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ assert(frame != nullptr);
+
+ if (frame->HasDebugInformation()) {
+ AddressRange range;
+ SymbolContext sc = frame->GetSymbolContext(eSymbolContextEverything);
+ if (m_options.m_end_line != LLDB_INVALID_LINE_NUMBER) {
+ Status error;
+ if (!sc.GetAddressRangeFromHereToEndLine(m_options.m_end_line, range,
+ error)) {
+ result.AppendErrorWithFormat("invalid end-line option: %s.",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else if (m_options.m_end_line_is_block_end) {
+ Status error;
+ Block *block = frame->GetSymbolContext(eSymbolContextBlock).block;
+ if (!block) {
+ result.AppendErrorWithFormat("Could not find the current block.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ AddressRange block_range;
+ Address pc_address = frame->GetFrameCodeAddress();
+ block->GetRangeContainingAddress(pc_address, block_range);
+ if (!block_range.GetBaseAddress().IsValid()) {
+ result.AppendErrorWithFormat(
+ "Could not find the current block address.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ lldb::addr_t pc_offset_in_block =
+ pc_address.GetFileAddress() -
+ block_range.GetBaseAddress().GetFileAddress();
+ lldb::addr_t range_length =
+ block_range.GetByteSize() - pc_offset_in_block;
+ range = AddressRange(pc_address, range_length);
+ } else {
+ range = sc.line_entry.range;
+ }
+
+ new_plan_sp = thread->QueueThreadPlanForStepInRange(
+ abort_other_plans, range,
+ frame->GetSymbolContext(eSymbolContextEverything),
+ m_options.m_step_in_target.c_str(), stop_other_threads,
+ new_plan_status, m_options.m_step_in_avoid_no_debug,
+ m_options.m_step_out_avoid_no_debug);
+
+ if (new_plan_sp && !m_options.m_avoid_regexp.empty()) {
+ ThreadPlanStepInRange *step_in_range_plan =
+ static_cast<ThreadPlanStepInRange *>(new_plan_sp.get());
+ step_in_range_plan->SetAvoidRegexp(m_options.m_avoid_regexp.c_str());
+ }
+ } else
+ new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
+ false, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ } else if (m_step_type == eStepTypeOver) {
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+
+ if (frame->HasDebugInformation())
+ new_plan_sp = thread->QueueThreadPlanForStepOverRange(
+ abort_other_plans,
+ frame->GetSymbolContext(eSymbolContextEverything).line_entry,
+ frame->GetSymbolContext(eSymbolContextEverything),
+ stop_other_threads, new_plan_status,
+ m_options.m_step_out_avoid_no_debug);
+ else
+ new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
+ true, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ } else if (m_step_type == eStepTypeTrace) {
+ new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
+ false, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ } else if (m_step_type == eStepTypeTraceOver) {
+ new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
+ true, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ } else if (m_step_type == eStepTypeOut) {
+ new_plan_sp = thread->QueueThreadPlanForStepOut(
+ abort_other_plans, nullptr, false, bool_stop_other_threads, eVoteYes,
+ eVoteNoOpinion, thread->GetSelectedFrameIndex(), new_plan_status,
+ m_options.m_step_out_avoid_no_debug);
+ } else if (m_step_type == eStepTypeScripted) {
+ new_plan_sp = thread->QueueThreadPlanForStepScripted(
+ abort_other_plans, m_options.m_class_name.c_str(),
+ bool_stop_other_threads, new_plan_status);
+ } else {
+ result.AppendError("step type is not supported");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // If we got a new plan, then set it to be a master plan (User level Plans
+ // should be master plans so that they can be interruptible). Then resume
+ // the process.
+
+ if (new_plan_sp) {
+ new_plan_sp->SetIsMasterPlan(true);
+ new_plan_sp->SetOkayToDiscard(false);
+
+ if (m_options.m_step_count > 1) {
+ if (!new_plan_sp->SetIterationCount(m_options.m_step_count)) {
+ result.AppendWarning(
+ "step operation does not support iteration count.");
+ }
+ }
+
+ process->GetThreadList().SetSelectedThreadByID(thread->GetID());
+
+ const uint32_t iohandler_id = process->GetIOHandlerID();
+
+ StreamString stream;
+ Status error;
+ if (synchronous_execution)
+ error = process->ResumeSynchronous(&stream);
+ else
+ error = process->Resume();
+
+ if (!error.Success()) {
+ result.AppendMessage(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // There is a race condition where this thread will return up the call
+ // stack to the main command handler and show an (lldb) prompt before
+ // HandlePrivateEvent (from PrivateStateThread) has a chance to call
+ // PushProcessIOHandler().
+ process->SyncIOHandler(iohandler_id, std::chrono::seconds(2));
+
+ if (synchronous_execution) {
+ // If any state changed events had anything to say, add that to the
+ // result
+ if (stream.GetSize() > 0)
+ result.AppendMessage(stream.GetString());
+
+ process->GetThreadList().SetSelectedThreadByID(thread->GetID());
+ result.SetDidChangeProcessState(true);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.SetStatus(eReturnStatusSuccessContinuingNoResult);
+ }
+ } else {
+ result.SetError(new_plan_status);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return result.Succeeded();
+ }
+
+protected:
+ StepType m_step_type;
+ StepScope m_step_scope;
+ CommandOptions m_options;
+};
+
+// CommandObjectThreadContinue
+
+class CommandObjectThreadContinue : public CommandObjectParsed {
+public:
+ CommandObjectThreadContinue(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "thread continue",
+ "Continue execution of the current target process. One "
+ "or more threads may be specified, by default all "
+ "threads continue.",
+ nullptr,
+ eCommandRequiresThread | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData thread_idx_arg;
+
+ // Define the first (and only) variant of this arg.
+ thread_idx_arg.arg_type = eArgTypeThreadIndex;
+ thread_idx_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(thread_idx_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadContinue() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ bool synchronous_execution = m_interpreter.GetSynchronous();
+
+ if (!GetDebugger().GetSelectedTarget()) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process exists. Cannot continue");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ StateType state = process->GetState();
+ if ((state == eStateCrashed) || (state == eStateStopped) ||
+ (state == eStateSuspended)) {
+ const size_t argc = command.GetArgumentCount();
+ if (argc > 0) {
+ // These two lines appear at the beginning of both blocks in this
+ // if..else, but that is because we need to release the lock before
+ // calling process->Resume below.
+ std::lock_guard<std::recursive_mutex> guard(
+ process->GetThreadList().GetMutex());
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+ std::vector<Thread *> resume_threads;
+ for (auto &entry : command.entries()) {
+ uint32_t thread_idx;
+ if (entry.ref.getAsInteger(0, thread_idx)) {
+ result.AppendErrorWithFormat(
+ "invalid thread index argument: \"%s\".\n", entry.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ Thread *thread =
+ process->GetThreadList().FindThreadByIndexID(thread_idx).get();
+
+ if (thread) {
+ resume_threads.push_back(thread);
+ } else {
+ result.AppendErrorWithFormat("invalid thread index %u.\n",
+ thread_idx);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ if (resume_threads.empty()) {
+ result.AppendError("no valid thread indexes were specified");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ if (resume_threads.size() == 1)
+ result.AppendMessageWithFormat("Resuming thread: ");
+ else
+ result.AppendMessageWithFormat("Resuming threads: ");
+
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ Thread *thread =
+ process->GetThreadList().GetThreadAtIndex(idx).get();
+ std::vector<Thread *>::iterator this_thread_pos =
+ find(resume_threads.begin(), resume_threads.end(), thread);
+
+ if (this_thread_pos != resume_threads.end()) {
+ resume_threads.erase(this_thread_pos);
+ if (!resume_threads.empty())
+ result.AppendMessageWithFormat("%u, ", thread->GetIndexID());
+ else
+ result.AppendMessageWithFormat("%u ", thread->GetIndexID());
+
+ const bool override_suspend = true;
+ thread->SetResumeState(eStateRunning, override_suspend);
+ } else {
+ thread->SetResumeState(eStateSuspended);
+ }
+ }
+ result.AppendMessageWithFormat("in process %" PRIu64 "\n",
+ process->GetID());
+ }
+ } else {
+ // These two lines appear at the beginning of both blocks in this
+ // if..else, but that is because we need to release the lock before
+ // calling process->Resume below.
+ std::lock_guard<std::recursive_mutex> guard(
+ process->GetThreadList().GetMutex());
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+ Thread *current_thread = GetDefaultThread();
+ if (current_thread == nullptr) {
+ result.AppendError("the process doesn't have a current thread");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ // Set the actions that the threads should each take when resuming
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get();
+ if (thread == current_thread) {
+ result.AppendMessageWithFormat("Resuming thread 0x%4.4" PRIx64
+ " in process %" PRIu64 "\n",
+ thread->GetID(), process->GetID());
+ const bool override_suspend = true;
+ thread->SetResumeState(eStateRunning, override_suspend);
+ } else {
+ thread->SetResumeState(eStateSuspended);
+ }
+ }
+ }
+
+ StreamString stream;
+ Status error;
+ if (synchronous_execution)
+ error = process->ResumeSynchronous(&stream);
+ else
+ error = process->Resume();
+
+ // We should not be holding the thread list lock when we do this.
+ if (error.Success()) {
+ result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n",
+ process->GetID());
+ if (synchronous_execution) {
+ // If any state changed events had anything to say, add that to the
+ // result
+ if (stream.GetSize() > 0)
+ result.AppendMessage(stream.GetString());
+
+ result.SetDidChangeProcessState(true);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.SetStatus(eReturnStatusSuccessContinuingNoResult);
+ }
+ } else {
+ result.AppendErrorWithFormat("Failed to resume process: %s\n",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "Process cannot be continued from its current state (%s).\n",
+ StateAsCString(state));
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectThreadUntil
+
+static constexpr OptionEnumValueElement g_duo_running_mode[] = {
+ {eOnlyThisThread, "this-thread", "Run only this thread"},
+ {eAllThreads, "all-threads", "Run all threads"} };
+
+static constexpr OptionEnumValues DuoRunningModes() {
+ return OptionEnumValues(g_duo_running_mode);
+}
+
+static constexpr OptionDefinition g_thread_until_options[] = {
+#define LLDB_OPTIONS_thread_until
+#include "CommandOptions.inc"
+};
+
+class CommandObjectThreadUntil : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ uint32_t m_thread_idx;
+ uint32_t m_frame_idx;
+
+ CommandOptions()
+ : Options(), m_thread_idx(LLDB_INVALID_THREAD_ID),
+ m_frame_idx(LLDB_INVALID_FRAME_ID) {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a': {
+ lldb::addr_t tmp_addr = OptionArgParser::ToAddress(
+ execution_context, option_arg, LLDB_INVALID_ADDRESS, &error);
+ if (error.Success())
+ m_until_addrs.push_back(tmp_addr);
+ } break;
+ case 't':
+ if (option_arg.getAsInteger(0, m_thread_idx)) {
+ m_thread_idx = LLDB_INVALID_INDEX32;
+ error.SetErrorStringWithFormat("invalid thread index '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+ case 'f':
+ if (option_arg.getAsInteger(0, m_frame_idx)) {
+ m_frame_idx = LLDB_INVALID_FRAME_ID;
+ error.SetErrorStringWithFormat("invalid frame index '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+ case 'm': {
+ auto enum_values = GetDefinitions()[option_idx].enum_values;
+ lldb::RunMode run_mode = (lldb::RunMode)OptionArgParser::ToOptionEnum(
+ option_arg, enum_values, eOnlyDuringStepping, error);
+
+ if (error.Success()) {
+ if (run_mode == eAllThreads)
+ m_stop_others = false;
+ else
+ m_stop_others = true;
+ }
+ } break;
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_thread_idx = LLDB_INVALID_THREAD_ID;
+ m_frame_idx = 0;
+ m_stop_others = false;
+ m_until_addrs.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_until_options);
+ }
+
+ uint32_t m_step_thread_idx;
+ bool m_stop_others;
+ std::vector<lldb::addr_t> m_until_addrs;
+
+ // Instance variables to hold the values for command options.
+ };
+
+ CommandObjectThreadUntil(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "thread until",
+ "Continue until a line number or address is reached by the "
+ "current or specified thread. Stops when returning from "
+ "the current function as a safety measure. "
+ "The target line number(s) are given as arguments, and if more than one"
+ " is provided, stepping will stop when the first one is hit.",
+ nullptr,
+ eCommandRequiresThread | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData line_num_arg;
+
+ // Define the first (and only) variant of this arg.
+ line_num_arg.arg_type = eArgTypeLineNum;
+ line_num_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(line_num_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadUntil() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ bool synchronous_execution = m_interpreter.GetSynchronous();
+
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("invalid target, create a debug target using the "
+ "'target create' command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("need a valid process to step");
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ Thread *thread = nullptr;
+ std::vector<uint32_t> line_numbers;
+
+ if (command.GetArgumentCount() >= 1) {
+ size_t num_args = command.GetArgumentCount();
+ for (size_t i = 0; i < num_args; i++) {
+ uint32_t line_number;
+ line_number = StringConvert::ToUInt32(command.GetArgumentAtIndex(i),
+ UINT32_MAX);
+ if (line_number == UINT32_MAX) {
+ result.AppendErrorWithFormat("invalid line number: '%s'.\n",
+ command.GetArgumentAtIndex(i));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else
+ line_numbers.push_back(line_number);
+ }
+ } else if (m_options.m_until_addrs.empty()) {
+ result.AppendErrorWithFormat("No line number or address provided:\n%s",
+ GetSyntax().str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_options.m_thread_idx == LLDB_INVALID_THREAD_ID) {
+ thread = GetDefaultThread();
+ } else {
+ thread = process->GetThreadList()
+ .FindThreadByIndexID(m_options.m_thread_idx)
+ .get();
+ }
+
+ if (thread == nullptr) {
+ const uint32_t num_threads = process->GetThreadList().GetSize();
+ result.AppendErrorWithFormat(
+ "Thread index %u is out of range (valid values are 0 - %u).\n",
+ m_options.m_thread_idx, num_threads);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const bool abort_other_plans = false;
+
+ StackFrame *frame =
+ thread->GetStackFrameAtIndex(m_options.m_frame_idx).get();
+ if (frame == nullptr) {
+ result.AppendErrorWithFormat(
+ "Frame index %u is out of range for thread %u.\n",
+ m_options.m_frame_idx, m_options.m_thread_idx);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ThreadPlanSP new_plan_sp;
+ Status new_plan_status;
+
+ if (frame->HasDebugInformation()) {
+ // Finally we got here... Translate the given line number to a bunch
+ // of addresses:
+ SymbolContext sc(frame->GetSymbolContext(eSymbolContextCompUnit));
+ LineTable *line_table = nullptr;
+ if (sc.comp_unit)
+ line_table = sc.comp_unit->GetLineTable();
+
+ if (line_table == nullptr) {
+ result.AppendErrorWithFormat("Failed to resolve the line table for "
+ "frame %u of thread index %u.\n",
+ m_options.m_frame_idx,
+ m_options.m_thread_idx);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ LineEntry function_start;
+ uint32_t index_ptr = 0, end_ptr;
+ std::vector<addr_t> address_list;
+
+ // Find the beginning & end index of the
+ AddressRange fun_addr_range = sc.function->GetAddressRange();
+ Address fun_start_addr = fun_addr_range.GetBaseAddress();
+ line_table->FindLineEntryByAddress(fun_start_addr, function_start,
+ &index_ptr);
+
+ Address fun_end_addr(fun_start_addr.GetSection(),
+ fun_start_addr.GetOffset() +
+ fun_addr_range.GetByteSize());
+
+ bool all_in_function = true;
+
+ line_table->FindLineEntryByAddress(fun_end_addr, function_start,
+ &end_ptr);
+
+ for (uint32_t line_number : line_numbers) {
+ uint32_t start_idx_ptr = index_ptr;
+ while (start_idx_ptr <= end_ptr) {
+ LineEntry line_entry;
+ const bool exact = false;
+ start_idx_ptr = sc.comp_unit->FindLineEntry(
+ start_idx_ptr, line_number, sc.comp_unit, exact, &line_entry);
+ if (start_idx_ptr == UINT32_MAX)
+ break;
+
+ addr_t address =
+ line_entry.range.GetBaseAddress().GetLoadAddress(target);
+ if (address != LLDB_INVALID_ADDRESS) {
+ if (fun_addr_range.ContainsLoadAddress(address, target))
+ address_list.push_back(address);
+ else
+ all_in_function = false;
+ }
+ start_idx_ptr++;
+ }
+ }
+
+ for (lldb::addr_t address : m_options.m_until_addrs) {
+ if (fun_addr_range.ContainsLoadAddress(address, target))
+ address_list.push_back(address);
+ else
+ all_in_function = false;
+ }
+
+ if (address_list.empty()) {
+ if (all_in_function)
+ result.AppendErrorWithFormat(
+ "No line entries matching until target.\n");
+ else
+ result.AppendErrorWithFormat(
+ "Until target outside of the current function.\n");
+
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ new_plan_sp = thread->QueueThreadPlanForStepUntil(
+ abort_other_plans, &address_list.front(), address_list.size(),
+ m_options.m_stop_others, m_options.m_frame_idx, new_plan_status);
+ if (new_plan_sp) {
+ // User level plans should be master plans so they can be interrupted
+ // (e.g. by hitting a breakpoint) and other plans executed by the
+ // user (stepping around the breakpoint) and then a "continue" will
+ // resume the original plan.
+ new_plan_sp->SetIsMasterPlan(true);
+ new_plan_sp->SetOkayToDiscard(false);
+ } else {
+ result.SetError(new_plan_status);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ result.AppendErrorWithFormat(
+ "Frame index %u of thread %u has no debug information.\n",
+ m_options.m_frame_idx, m_options.m_thread_idx);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ process->GetThreadList().SetSelectedThreadByID(m_options.m_thread_idx);
+
+ StreamString stream;
+ Status error;
+ if (synchronous_execution)
+ error = process->ResumeSynchronous(&stream);
+ else
+ error = process->Resume();
+
+ if (error.Success()) {
+ result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n",
+ process->GetID());
+ if (synchronous_execution) {
+ // If any state changed events had anything to say, add that to the
+ // result
+ if (stream.GetSize() > 0)
+ result.AppendMessage(stream.GetString());
+
+ result.SetDidChangeProcessState(true);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.SetStatus(eReturnStatusSuccessContinuingNoResult);
+ }
+ } else {
+ result.AppendErrorWithFormat("Failed to resume process: %s.\n",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ return result.Succeeded();
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectThreadSelect
+
+class CommandObjectThreadSelect : public CommandObjectParsed {
+public:
+ CommandObjectThreadSelect(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "thread select",
+ "Change the currently selected thread.", nullptr,
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData thread_idx_arg;
+
+ // Define the first (and only) variant of this arg.
+ thread_idx_arg.arg_type = eArgTypeThreadIndex;
+ thread_idx_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(thread_idx_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadSelect() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process == nullptr) {
+ result.AppendError("no process");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (command.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes exactly one thread index argument:\nUsage: %s\n",
+ m_cmd_name.c_str(), m_cmd_syntax.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ uint32_t index_id =
+ StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0);
+
+ Thread *new_thread =
+ process->GetThreadList().FindThreadByIndexID(index_id).get();
+ if (new_thread == nullptr) {
+ result.AppendErrorWithFormat("invalid thread #%s.\n",
+ command.GetArgumentAtIndex(0));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ process->GetThreadList().SetSelectedThreadByID(new_thread->GetID(), true);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectThreadList
+
+class CommandObjectThreadList : public CommandObjectParsed {
+public:
+ CommandObjectThreadList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "thread list",
+ "Show a summary of each thread in the current target process. "
+ "Use 'settings set thread-format' to customize the individual "
+ "thread listings.",
+ "thread list",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
+
+ ~CommandObjectThreadList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Stream &strm = result.GetOutputStream();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ Process *process = m_exe_ctx.GetProcessPtr();
+ const bool only_threads_with_stop_reason = false;
+ const uint32_t start_frame = 0;
+ const uint32_t num_frames = 0;
+ const uint32_t num_frames_with_source = 0;
+ process->GetStatus(strm);
+ process->GetThreadStatus(strm, only_threads_with_stop_reason, start_frame,
+ num_frames, num_frames_with_source, false);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectThreadInfo
+
+static constexpr OptionDefinition g_thread_info_options[] = {
+#define LLDB_OPTIONS_thread_info
+#include "CommandOptions.inc"
+};
+
+class CommandObjectThreadInfo : public CommandObjectIterateOverThreads {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_json_thread = false;
+ m_json_stopinfo = false;
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ const int short_option = m_getopt_table[option_idx].val;
+ Status error;
+
+ switch (short_option) {
+ case 'j':
+ m_json_thread = true;
+ break;
+
+ case 's':
+ m_json_stopinfo = true;
+ break;
+
+ default:
+ return Status("invalid short option character '%c'", short_option);
+ }
+ return error;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_info_options);
+ }
+
+ bool m_json_thread;
+ bool m_json_stopinfo;
+ };
+
+ CommandObjectThreadInfo(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread info", "Show an extended summary of one or "
+ "more threads. Defaults to the "
+ "current thread.",
+ "thread info",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+ m_options() {
+ m_add_return = false;
+ }
+
+ ~CommandObjectThreadInfo() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+ ThreadSP thread_sp =
+ m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+ if (!thread_sp) {
+ result.AppendErrorWithFormat("thread no longer exists: 0x%" PRIx64 "\n",
+ tid);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Thread *thread = thread_sp.get();
+
+ Stream &strm = result.GetOutputStream();
+ if (!thread->GetDescription(strm, eDescriptionLevelFull,
+ m_options.m_json_thread,
+ m_options.m_json_stopinfo)) {
+ result.AppendErrorWithFormat("error displaying info for thread: \"%d\"\n",
+ thread->GetIndexID());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectThreadException
+
+class CommandObjectThreadException : public CommandObjectIterateOverThreads {
+ public:
+ CommandObjectThreadException(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread exception",
+ "Display the current exception object for a thread. Defaults to "
+ "the current thread.",
+ "thread exception",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
+
+ ~CommandObjectThreadException() override = default;
+
+ bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+ ThreadSP thread_sp =
+ m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+ if (!thread_sp) {
+ result.AppendErrorWithFormat("thread no longer exists: 0x%" PRIx64 "\n",
+ tid);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Stream &strm = result.GetOutputStream();
+ ValueObjectSP exception_object_sp = thread_sp->GetCurrentException();
+ if (exception_object_sp) {
+ exception_object_sp->Dump(strm);
+ }
+
+ ThreadSP exception_thread_sp = thread_sp->GetCurrentExceptionBacktrace();
+ if (exception_thread_sp && exception_thread_sp->IsValid()) {
+ const uint32_t num_frames_with_source = 0;
+ const bool stop_format = false;
+ exception_thread_sp->GetStatus(strm, 0, UINT32_MAX,
+ num_frames_with_source, stop_format);
+ }
+
+ return true;
+ }
+};
+
+// CommandObjectThreadReturn
+
+static constexpr OptionDefinition g_thread_return_options[] = {
+#define LLDB_OPTIONS_thread_return
+#include "CommandOptions.inc"
+};
+
+class CommandObjectThreadReturn : public CommandObjectRaw {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_from_expression(false) {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'x': {
+ bool success;
+ bool tmp_value =
+ OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (success)
+ m_from_expression = tmp_value;
+ else {
+ error.SetErrorStringWithFormat(
+ "invalid boolean value '%s' for 'x' option",
+ option_arg.str().c_str());
+ }
+ } break;
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_from_expression = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_return_options);
+ }
+
+ bool m_from_expression;
+
+ // Instance variables to hold the values for command options.
+ };
+
+ CommandObjectThreadReturn(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "thread return",
+ "Prematurely return from a stack frame, "
+ "short-circuiting execution of newer frames "
+ "and optionally yielding a specified value. Defaults "
+ "to the exiting the current stack "
+ "frame.",
+ "thread return",
+ eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData expression_arg;
+
+ // Define the first (and only) variant of this arg.
+ expression_arg.arg_type = eArgTypeExpression;
+ expression_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(expression_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadReturn() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ // I am going to handle this by hand, because I don't want you to have to
+ // say:
+ // "thread return -- -5".
+ if (command.startswith("-x")) {
+ if (command.size() != 2U)
+ result.AppendWarning("Return values ignored when returning from user "
+ "called expressions");
+
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ Status error;
+ error = thread->UnwindInnermostExpression();
+ if (!error.Success()) {
+ result.AppendErrorWithFormat("Unwinding expression failed - %s.",
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ } else {
+ bool success =
+ thread->SetSelectedFrameByIndexNoisily(0, result.GetOutputStream());
+ if (success) {
+ m_exe_ctx.SetFrameSP(thread->GetSelectedFrame());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Could not select 0th frame after unwinding expression.");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ return result.Succeeded();
+ }
+
+ ValueObjectSP return_valobj_sp;
+
+ StackFrameSP frame_sp = m_exe_ctx.GetFrameSP();
+ uint32_t frame_idx = frame_sp->GetFrameIndex();
+
+ if (frame_sp->IsInlined()) {
+ result.AppendError("Don't know how to return from inlined frames.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (!command.empty()) {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ EvaluateExpressionOptions options;
+
+ options.SetUnwindOnError(true);
+ options.SetUseDynamic(eNoDynamicValues);
+
+ ExpressionResults exe_results = eExpressionSetupError;
+ exe_results = target->EvaluateExpression(command, frame_sp.get(),
+ return_valobj_sp, options);
+ if (exe_results != eExpressionCompleted) {
+ if (return_valobj_sp)
+ result.AppendErrorWithFormat(
+ "Error evaluating result expression: %s",
+ return_valobj_sp->GetError().AsCString());
+ else
+ result.AppendErrorWithFormat(
+ "Unknown error evaluating result expression.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ Status error;
+ ThreadSP thread_sp = m_exe_ctx.GetThreadSP();
+ const bool broadcast = true;
+ error = thread_sp->ReturnFromFrame(frame_sp, return_valobj_sp, broadcast);
+ if (!error.Success()) {
+ result.AppendErrorWithFormat(
+ "Error returning from frame %d of thread %d: %s.", frame_idx,
+ thread_sp->GetIndexID(), error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// CommandObjectThreadJump
+
+static constexpr OptionDefinition g_thread_jump_options[] = {
+#define LLDB_OPTIONS_thread_jump
+#include "CommandOptions.inc"
+};
+
+class CommandObjectThreadJump : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+ ~CommandOptions() override = default;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_filenames.Clear();
+ m_line_num = 0;
+ m_line_offset = 0;
+ m_load_addr = LLDB_INVALID_ADDRESS;
+ m_force = false;
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ const int short_option = m_getopt_table[option_idx].val;
+ Status error;
+
+ switch (short_option) {
+ case 'f':
+ m_filenames.AppendIfUnique(FileSpec(option_arg));
+ if (m_filenames.GetSize() > 1)
+ return Status("only one source file expected.");
+ break;
+ case 'l':
+ if (option_arg.getAsInteger(0, m_line_num))
+ return Status("invalid line number: '%s'.", option_arg.str().c_str());
+ break;
+ case 'b':
+ if (option_arg.getAsInteger(0, m_line_offset))
+ return Status("invalid line offset: '%s'.", option_arg.str().c_str());
+ break;
+ case 'a':
+ m_load_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ break;
+ case 'r':
+ m_force = true;
+ break;
+ default:
+ return Status("invalid short option character '%c'", short_option);
+ }
+ return error;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_jump_options);
+ }
+
+ FileSpecList m_filenames;
+ uint32_t m_line_num;
+ int32_t m_line_offset;
+ lldb::addr_t m_load_addr;
+ bool m_force;
+ };
+
+ CommandObjectThreadJump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "thread jump",
+ "Sets the program counter to a new address.", "thread jump",
+ eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+ m_options() {}
+
+ ~CommandObjectThreadJump() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext();
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ Target *target = m_exe_ctx.GetTargetPtr();
+ const SymbolContext &sym_ctx =
+ frame->GetSymbolContext(eSymbolContextLineEntry);
+
+ if (m_options.m_load_addr != LLDB_INVALID_ADDRESS) {
+ // Use this address directly.
+ Address dest = Address(m_options.m_load_addr);
+
+ lldb::addr_t callAddr = dest.GetCallableLoadAddress(target);
+ if (callAddr == LLDB_INVALID_ADDRESS) {
+ result.AppendErrorWithFormat("Invalid destination address.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (!reg_ctx->SetPC(callAddr)) {
+ result.AppendErrorWithFormat("Error changing PC value for thread %d.",
+ thread->GetIndexID());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ // Pick either the absolute line, or work out a relative one.
+ int32_t line = (int32_t)m_options.m_line_num;
+ if (line == 0)
+ line = sym_ctx.line_entry.line + m_options.m_line_offset;
+
+ // Try the current file, but override if asked.
+ FileSpec file = sym_ctx.line_entry.file;
+ if (m_options.m_filenames.GetSize() == 1)
+ file = m_options.m_filenames.GetFileSpecAtIndex(0);
+
+ if (!file) {
+ result.AppendErrorWithFormat(
+ "No source file available for the current location.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::string warnings;
+ Status err = thread->JumpToLine(file, line, m_options.m_force, &warnings);
+
+ if (err.Fail()) {
+ result.SetError(err);
+ return false;
+ }
+
+ if (!warnings.empty())
+ result.AppendWarning(warnings.c_str());
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+// Next are the subcommands of CommandObjectMultiwordThreadPlan
+
+// CommandObjectThreadPlanList
+
+static constexpr OptionDefinition g_thread_plan_list_options[] = {
+#define LLDB_OPTIONS_thread_plan_list
+#include "CommandOptions.inc"
+};
+
+class CommandObjectThreadPlanList : public CommandObjectIterateOverThreads {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {
+ // Keep default values of all options in one place: OptionParsingStarting
+ // ()
+ OptionParsingStarting(nullptr);
+ }
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'i':
+ m_internal = true;
+ break;
+ case 'v':
+ m_verbose = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_verbose = false;
+ m_internal = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_plan_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_verbose;
+ bool m_internal;
+ };
+
+ CommandObjectThreadPlanList(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread plan list",
+ "Show thread plans for one or more threads. If no threads are "
+ "specified, show the "
+ "current thread. Use the thread-index \"all\" to see all threads.",
+ nullptr,
+ eCommandRequiresProcess | eCommandRequiresThread |
+ eCommandTryTargetAPILock | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options() {}
+
+ ~CommandObjectThreadPlanList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+ ThreadSP thread_sp =
+ m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+ if (!thread_sp) {
+ result.AppendErrorWithFormat("thread no longer exists: 0x%" PRIx64 "\n",
+ tid);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Thread *thread = thread_sp.get();
+
+ Stream &strm = result.GetOutputStream();
+ DescriptionLevel desc_level = eDescriptionLevelFull;
+ if (m_options.m_verbose)
+ desc_level = eDescriptionLevelVerbose;
+
+ thread->DumpThreadPlans(&strm, desc_level, m_options.m_internal, true);
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
+class CommandObjectThreadPlanDiscard : public CommandObjectParsed {
+public:
+ CommandObjectThreadPlanDiscard(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "thread plan discard",
+ "Discards thread plans up to and including the "
+ "specified index (see 'thread plan list'.) "
+ "Only user visible plans can be discarded.",
+ nullptr,
+ eCommandRequiresProcess | eCommandRequiresThread |
+ eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData plan_index_arg;
+
+ // Define the first (and only) variant of this arg.
+ plan_index_arg.arg_type = eArgTypeUnsignedInteger;
+ plan_index_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(plan_index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectThreadPlanDiscard() override = default;
+
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ if (args.GetArgumentCount() != 1) {
+ result.AppendErrorWithFormat("Too many arguments, expected one - the "
+ "thread plan index - but got %zu.",
+ args.GetArgumentCount());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ bool success;
+ uint32_t thread_plan_idx =
+ StringConvert::ToUInt32(args.GetArgumentAtIndex(0), 0, 0, &success);
+ if (!success) {
+ result.AppendErrorWithFormat(
+ "Invalid thread index: \"%s\" - should be unsigned int.",
+ args.GetArgumentAtIndex(0));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (thread_plan_idx == 0) {
+ result.AppendErrorWithFormat(
+ "You wouldn't really want me to discard the base thread plan.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (thread->DiscardUserThreadPlansUpToIndex(thread_plan_idx)) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ } else {
+ result.AppendErrorWithFormat(
+ "Could not find User thread plan with index %s.",
+ args.GetArgumentAtIndex(0));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+};
+
+// CommandObjectMultiwordThreadPlan
+
+class CommandObjectMultiwordThreadPlan : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordThreadPlan(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "plan",
+ "Commands for managing thread plans that control execution.",
+ "thread plan <subcommand> [<subcommand objects]") {
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectThreadPlanList(interpreter)));
+ LoadSubCommand(
+ "discard",
+ CommandObjectSP(new CommandObjectThreadPlanDiscard(interpreter)));
+ }
+
+ ~CommandObjectMultiwordThreadPlan() override = default;
+};
+
+// CommandObjectMultiwordThread
+
+CommandObjectMultiwordThread::CommandObjectMultiwordThread(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "thread", "Commands for operating on "
+ "one or more threads in "
+ "the current process.",
+ "thread <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("backtrace", CommandObjectSP(new CommandObjectThreadBacktrace(
+ interpreter)));
+ LoadSubCommand("continue",
+ CommandObjectSP(new CommandObjectThreadContinue(interpreter)));
+ LoadSubCommand("list",
+ CommandObjectSP(new CommandObjectThreadList(interpreter)));
+ LoadSubCommand("return",
+ CommandObjectSP(new CommandObjectThreadReturn(interpreter)));
+ LoadSubCommand("jump",
+ CommandObjectSP(new CommandObjectThreadJump(interpreter)));
+ LoadSubCommand("select",
+ CommandObjectSP(new CommandObjectThreadSelect(interpreter)));
+ LoadSubCommand("until",
+ CommandObjectSP(new CommandObjectThreadUntil(interpreter)));
+ LoadSubCommand("info",
+ CommandObjectSP(new CommandObjectThreadInfo(interpreter)));
+ LoadSubCommand(
+ "exception",
+ CommandObjectSP(new CommandObjectThreadException(interpreter)));
+ LoadSubCommand("step-in",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-in",
+ "Source level single step, stepping into calls. Defaults "
+ "to current thread unless specified.",
+ nullptr, eStepTypeInto, eStepScopeSource)));
+
+ LoadSubCommand("step-out",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-out",
+ "Finish executing the current stack frame and stop after "
+ "returning. Defaults to current thread unless specified.",
+ nullptr, eStepTypeOut, eStepScopeSource)));
+
+ LoadSubCommand("step-over",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-over",
+ "Source level single step, stepping over calls. Defaults "
+ "to current thread unless specified.",
+ nullptr, eStepTypeOver, eStepScopeSource)));
+
+ LoadSubCommand("step-inst",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-inst",
+ "Instruction level single step, stepping into calls. "
+ "Defaults to current thread unless specified.",
+ nullptr, eStepTypeTrace, eStepScopeInstruction)));
+
+ LoadSubCommand("step-inst-over",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-inst-over",
+ "Instruction level single step, stepping over calls. "
+ "Defaults to current thread unless specified.",
+ nullptr, eStepTypeTraceOver, eStepScopeInstruction)));
+
+ LoadSubCommand(
+ "step-scripted",
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-scripted",
+ "Step as instructed by the script class passed in the -C option.",
+ nullptr, eStepTypeScripted, eStepScopeSource)));
+
+ LoadSubCommand("plan", CommandObjectSP(new CommandObjectMultiwordThreadPlan(
+ interpreter)));
+}
+
+CommandObjectMultiwordThread::~CommandObjectMultiwordThread() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.h
new file mode 100644
index 000000000000..77729ceecd63
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectThread.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectThread.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectThread_h_
+#define liblldb_CommandObjectThread_h_
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectMultiwordThread : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordThread(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordThread() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectThread_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectType.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectType.cpp
new file mode 100644
index 000000000000..98a43f50b1b1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectType.cpp
@@ -0,0 +1,3100 @@
+//===-- CommandObjectType.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectType.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupFormat.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Interpreter/OptionValueLanguage.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StringList.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include <algorithm>
+#include <cctype>
+#include <functional>
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+class ScriptAddOptions {
+public:
+ TypeSummaryImpl::Flags m_flags;
+ StringList m_target_types;
+ bool m_regex;
+ ConstString m_name;
+ std::string m_category;
+
+ ScriptAddOptions(const TypeSummaryImpl::Flags &flags, bool regx,
+ ConstString name, std::string catg)
+ : m_flags(flags), m_regex(regx), m_name(name), m_category(catg) {}
+
+ typedef std::shared_ptr<ScriptAddOptions> SharedPointer;
+};
+
+class SynthAddOptions {
+public:
+ bool m_skip_pointers;
+ bool m_skip_references;
+ bool m_cascade;
+ bool m_regex;
+ StringList m_target_types;
+ std::string m_category;
+
+ SynthAddOptions(bool sptr, bool sref, bool casc, bool regx, std::string catg)
+ : m_skip_pointers(sptr), m_skip_references(sref), m_cascade(casc),
+ m_regex(regx), m_target_types(), m_category(catg) {}
+
+ typedef std::shared_ptr<SynthAddOptions> SharedPointer;
+};
+
+static bool WarnOnPotentialUnquotedUnsignedType(Args &command,
+ CommandReturnObject &result) {
+ if (command.empty())
+ return false;
+
+ for (auto entry : llvm::enumerate(command.entries().drop_back())) {
+ if (entry.value().ref != "unsigned")
+ continue;
+ auto next = command.entries()[entry.index() + 1].ref;
+ if (next == "int" || next == "short" || next == "char" || next == "long") {
+ result.AppendWarningWithFormat(
+ "unsigned %s being treated as two types. if you meant the combined "
+ "type "
+ "name use quotes, as in \"unsigned %s\"\n",
+ next.str().c_str(), next.str().c_str());
+ return true;
+ }
+ }
+ return false;
+}
+
+static constexpr OptionDefinition g_type_summary_add_options[] = {
+#define LLDB_OPTIONS_type_summary_add
+#include "CommandOptions.inc"
+};
+
+class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+private:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions(CommandInterpreter &interpreter) : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_summary_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ TypeSummaryImpl::Flags m_flags;
+ bool m_regex;
+ std::string m_format_string;
+ ConstString m_name;
+ std::string m_python_script;
+ std::string m_python_function;
+ bool m_is_add_script;
+ std::string m_category;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool Execute_ScriptSummary(Args &command, CommandReturnObject &result);
+
+ bool Execute_StringSummary(Args &command, CommandReturnObject &result);
+
+public:
+ enum SummaryFormatType { eRegularSummary, eRegexSummary, eNamedSummary };
+
+ CommandObjectTypeSummaryAdd(CommandInterpreter &interpreter);
+
+ ~CommandObjectTypeSummaryAdd() override = default;
+
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ static const char *g_summary_addreader_instructions =
+ "Enter your Python command(s). Type 'DONE' to end.\n"
+ "def function (valobj,internal_dict):\n"
+ " \"\"\"valobj: an SBValue which you want to provide a summary "
+ "for\n"
+ " internal_dict: an LLDB support object not to be used\"\"\"\n";
+
+ StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ if (output_sp && interactive) {
+ output_sp->PutCString(g_summary_addreader_instructions);
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override {
+ StreamFileSP error_sp = io_handler.GetErrorStreamFile();
+
+#ifndef LLDB_DISABLE_PYTHON
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter) {
+ StringList lines;
+ lines.SplitIntoLines(data);
+ if (lines.GetSize() > 0) {
+ ScriptAddOptions *options_ptr =
+ ((ScriptAddOptions *)io_handler.GetUserData());
+ if (options_ptr) {
+ ScriptAddOptions::SharedPointer options(
+ options_ptr); // this will ensure that we get rid of the pointer
+ // when going out of scope
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter) {
+ std::string funct_name_str;
+ if (interpreter->GenerateTypeScriptFunction(lines,
+ funct_name_str)) {
+ if (funct_name_str.empty()) {
+ error_sp->Printf("unable to obtain a valid function name from "
+ "the script interpreter.\n");
+ error_sp->Flush();
+ } else {
+ // now I have a valid function name, let's add this as script
+ // for every type in the list
+
+ TypeSummaryImplSP script_format;
+ script_format = std::make_shared<ScriptSummaryFormat>(
+ options->m_flags, funct_name_str.c_str(),
+ lines.CopyList(" ").c_str());
+
+ Status error;
+
+ for (size_t i = 0; i < options->m_target_types.GetSize(); i++) {
+ const char *type_name =
+ options->m_target_types.GetStringAtIndex(i);
+ CommandObjectTypeSummaryAdd::AddSummary(
+ ConstString(type_name), script_format,
+ (options->m_regex
+ ? CommandObjectTypeSummaryAdd::eRegexSummary
+ : CommandObjectTypeSummaryAdd::eRegularSummary),
+ options->m_category, &error);
+ if (error.Fail()) {
+ error_sp->Printf("error: %s", error.AsCString());
+ error_sp->Flush();
+ }
+ }
+
+ if (options->m_name) {
+ CommandObjectTypeSummaryAdd::AddSummary(
+ options->m_name, script_format,
+ CommandObjectTypeSummaryAdd::eNamedSummary,
+ options->m_category, &error);
+ if (error.Fail()) {
+ CommandObjectTypeSummaryAdd::AddSummary(
+ options->m_name, script_format,
+ CommandObjectTypeSummaryAdd::eNamedSummary,
+ options->m_category, &error);
+ if (error.Fail()) {
+ error_sp->Printf("error: %s", error.AsCString());
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: %s", error.AsCString());
+ error_sp->Flush();
+ }
+ } else {
+ if (error.AsCString()) {
+ error_sp->Printf("error: %s", error.AsCString());
+ error_sp->Flush();
+ }
+ }
+ }
+ } else {
+ error_sp->Printf("error: unable to generate a function.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: no script interpreter.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: internal synchronization information "
+ "missing or invalid.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: empty function, didn't add python command.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf(
+ "error: script interpreter missing, didn't add python command.\n");
+ error_sp->Flush();
+ }
+#endif // LLDB_DISABLE_PYTHON
+ io_handler.SetIsDone(true);
+ }
+
+ static bool AddSummary(ConstString type_name, lldb::TypeSummaryImplSP entry,
+ SummaryFormatType type, std::string category,
+ Status *error = nullptr);
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+};
+
+static const char *g_synth_addreader_instructions =
+ "Enter your Python command(s). Type 'DONE' to end.\n"
+ "You must define a Python class with these methods:\n"
+ " def __init__(self, valobj, dict):\n"
+ " def num_children(self):\n"
+ " def get_child_at_index(self, index):\n"
+ " def get_child_index(self, name):\n"
+ " def update(self):\n"
+ " '''Optional'''\n"
+ "class synthProvider:\n";
+
+static constexpr OptionDefinition g_type_synth_add_options[] = {
+#define LLDB_OPTIONS_type_synth_add
+#include "CommandOptions.inc"
+};
+
+class CommandObjectTypeSynthAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+private:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ bool success;
+
+ switch (short_option) {
+ case 'C':
+ m_cascade = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid value for cascade: %s",
+ option_arg.str().c_str());
+ break;
+ case 'P':
+ handwrite_python = true;
+ break;
+ case 'l':
+ m_class_name = std::string(option_arg);
+ is_class_based = true;
+ break;
+ case 'p':
+ m_skip_pointers = true;
+ break;
+ case 'r':
+ m_skip_references = true;
+ break;
+ case 'w':
+ m_category = std::string(option_arg);
+ break;
+ case 'x':
+ m_regex = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_cascade = true;
+ m_class_name = "";
+ m_skip_pointers = false;
+ m_skip_references = false;
+ m_category = "default";
+ is_class_based = false;
+ handwrite_python = false;
+ m_regex = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_synth_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_cascade;
+ bool m_skip_references;
+ bool m_skip_pointers;
+ std::string m_class_name;
+ bool m_input_python;
+ std::string m_category;
+ bool is_class_based;
+ bool handwrite_python;
+ bool m_regex;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool Execute_HandwritePython(Args &command, CommandReturnObject &result);
+
+ bool Execute_PythonClass(Args &command, CommandReturnObject &result);
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ WarnOnPotentialUnquotedUnsignedType(command, result);
+
+ if (m_options.handwrite_python)
+ return Execute_HandwritePython(command, result);
+ else if (m_options.is_class_based)
+ return Execute_PythonClass(command, result);
+ else {
+ result.AppendError("must either provide a children list, a Python class "
+ "name, or use -P and type a Python class "
+ "line-by-line");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ if (output_sp && interactive) {
+ output_sp->PutCString(g_synth_addreader_instructions);
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override {
+ StreamFileSP error_sp = io_handler.GetErrorStreamFile();
+
+#ifndef LLDB_DISABLE_PYTHON
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter) {
+ StringList lines;
+ lines.SplitIntoLines(data);
+ if (lines.GetSize() > 0) {
+ SynthAddOptions *options_ptr =
+ ((SynthAddOptions *)io_handler.GetUserData());
+ if (options_ptr) {
+ SynthAddOptions::SharedPointer options(
+ options_ptr); // this will ensure that we get rid of the pointer
+ // when going out of scope
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (interpreter) {
+ std::string class_name_str;
+ if (interpreter->GenerateTypeSynthClass(lines, class_name_str)) {
+ if (class_name_str.empty()) {
+ error_sp->Printf(
+ "error: unable to obtain a proper name for the class.\n");
+ error_sp->Flush();
+ } else {
+ // everything should be fine now, let's add the synth provider
+ // class
+
+ SyntheticChildrenSP synth_provider;
+ synth_provider = std::make_shared<ScriptedSyntheticChildren>(
+ SyntheticChildren::Flags()
+ .SetCascades(options->m_cascade)
+ .SetSkipPointers(options->m_skip_pointers)
+ .SetSkipReferences(options->m_skip_references),
+ class_name_str.c_str());
+
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(
+ ConstString(options->m_category.c_str()), category);
+
+ Status error;
+
+ for (size_t i = 0; i < options->m_target_types.GetSize(); i++) {
+ const char *type_name =
+ options->m_target_types.GetStringAtIndex(i);
+ ConstString const_type_name(type_name);
+ if (const_type_name) {
+ if (!CommandObjectTypeSynthAdd::AddSynth(
+ const_type_name, synth_provider,
+ options->m_regex
+ ? CommandObjectTypeSynthAdd::eRegexSynth
+ : CommandObjectTypeSynthAdd::eRegularSynth,
+ options->m_category, &error)) {
+ error_sp->Printf("error: %s\n", error.AsCString());
+ error_sp->Flush();
+ break;
+ }
+ } else {
+ error_sp->Printf("error: invalid type name.\n");
+ error_sp->Flush();
+ break;
+ }
+ }
+ }
+ } else {
+ error_sp->Printf("error: unable to generate a class.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: no script interpreter.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: internal synchronization data missing.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf("error: empty function, didn't add python command.\n");
+ error_sp->Flush();
+ }
+ } else {
+ error_sp->Printf(
+ "error: script interpreter missing, didn't add python command.\n");
+ error_sp->Flush();
+ }
+
+#endif // LLDB_DISABLE_PYTHON
+ io_handler.SetIsDone(true);
+ }
+
+public:
+ enum SynthFormatType { eRegularSynth, eRegexSynth };
+
+ CommandObjectTypeSynthAdd(CommandInterpreter &interpreter);
+
+ ~CommandObjectTypeSynthAdd() override = default;
+
+ static bool AddSynth(ConstString type_name, lldb::SyntheticChildrenSP entry,
+ SynthFormatType type, std::string category_name,
+ Status *error);
+};
+
+// CommandObjectTypeFormatAdd
+
+static constexpr OptionDefinition g_type_format_add_options[] = {
+#define LLDB_OPTIONS_type_format_add
+#include "CommandOptions.inc"
+};
+
+class CommandObjectTypeFormatAdd : public CommandObjectParsed {
+private:
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions() : OptionGroup() {}
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_format_add_options);
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_cascade = true;
+ m_skip_pointers = false;
+ m_skip_references = false;
+ m_regex = false;
+ m_category.assign("default");
+ m_custom_type_name.clear();
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option =
+ g_type_format_add_options[option_idx].short_option;
+ bool success;
+
+ switch (short_option) {
+ case 'C':
+ m_cascade = OptionArgParser::ToBoolean(option_value, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid value for cascade: %s",
+ option_value.str().c_str());
+ break;
+ case 'p':
+ m_skip_pointers = true;
+ break;
+ case 'w':
+ m_category.assign(option_value);
+ break;
+ case 'r':
+ m_skip_references = true;
+ break;
+ case 'x':
+ m_regex = true;
+ break;
+ case 't':
+ m_custom_type_name.assign(option_value);
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_cascade;
+ bool m_skip_references;
+ bool m_skip_pointers;
+ bool m_regex;
+ std::string m_category;
+ std::string m_custom_type_name;
+ };
+
+ OptionGroupOptions m_option_group;
+ OptionGroupFormat m_format_options;
+ CommandOptions m_command_options;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+public:
+ CommandObjectTypeFormatAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type format add",
+ "Add a new formatting style for a type.", nullptr),
+ m_option_group(), m_format_options(eFormatInvalid),
+ m_command_options() {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+
+ SetHelpLong(
+ R"(
+The following examples of 'type format add' refer to this code snippet for context:
+
+ typedef int Aint;
+ typedef float Afloat;
+ typedef Aint Bint;
+ typedef Afloat Bfloat;
+
+ Aint ix = 5;
+ Bint iy = 5;
+
+ Afloat fx = 3.14;
+ BFloat fy = 3.14;
+
+Adding default formatting:
+
+(lldb) type format add -f hex AInt
+(lldb) frame variable iy
+
+)"
+ " Produces hexadecimal display of iy, because no formatter is available for Bint and \
+the one for Aint is used instead."
+ R"(
+
+To prevent this use the cascade option '-C no' to prevent evaluation of typedef chains:
+
+
+(lldb) type format add -f hex -C no AInt
+
+Similar reasoning applies to this:
+
+(lldb) type format add -f hex -C no float -p
+
+)"
+ " All float values and float references are now formatted as hexadecimal, but not \
+pointers to floats. Nor will it change the default display for Afloat and Bfloat objects.");
+
+ // Add the "--format" to all options groups
+ m_option_group.Append(&m_format_options,
+ OptionGroupFormat::OPTION_GROUP_FORMAT,
+ LLDB_OPT_SET_1);
+ m_option_group.Append(&m_command_options);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTypeFormatAdd() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1) {
+ result.AppendErrorWithFormat("%s takes one or more args.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const Format format = m_format_options.GetFormat();
+ if (format == eFormatInvalid &&
+ m_command_options.m_custom_type_name.empty()) {
+ result.AppendErrorWithFormat("%s needs a valid format.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ TypeFormatImplSP entry;
+
+ if (m_command_options.m_custom_type_name.empty())
+ entry = std::make_shared<TypeFormatImpl_Format>(
+ format, TypeFormatImpl::Flags()
+ .SetCascades(m_command_options.m_cascade)
+ .SetSkipPointers(m_command_options.m_skip_pointers)
+ .SetSkipReferences(m_command_options.m_skip_references));
+ else
+ entry = std::make_shared<TypeFormatImpl_EnumType>(
+ ConstString(m_command_options.m_custom_type_name.c_str()),
+ TypeFormatImpl::Flags()
+ .SetCascades(m_command_options.m_cascade)
+ .SetSkipPointers(m_command_options.m_skip_pointers)
+ .SetSkipReferences(m_command_options.m_skip_references));
+
+ // now I have a valid format, let's add it to every type
+
+ TypeCategoryImplSP category_sp;
+ DataVisualization::Categories::GetCategory(
+ ConstString(m_command_options.m_category), category_sp);
+ if (!category_sp)
+ return false;
+
+ WarnOnPotentialUnquotedUnsignedType(command, result);
+
+ for (auto &arg_entry : command.entries()) {
+ if (arg_entry.ref.empty()) {
+ result.AppendError("empty typenames not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ConstString typeCS(arg_entry.ref);
+ if (m_command_options.m_regex) {
+ RegularExpressionSP typeRX(new RegularExpression());
+ if (!typeRX->Compile(arg_entry.ref)) {
+ result.AppendError(
+ "regex format error (maybe this is not really a regex?)");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ category_sp->GetRegexTypeSummariesContainer()->Delete(typeCS);
+ category_sp->GetRegexTypeFormatsContainer()->Add(typeRX, entry);
+ } else
+ category_sp->GetTypeFormatsContainer()->Add(typeCS, entry);
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+};
+
+static constexpr OptionDefinition g_type_formatter_delete_options[] = {
+#define LLDB_OPTIONS_type_formatter_delete
+#include "CommandOptions.inc"
+};
+
+class CommandObjectTypeFormatterDelete : public CommandObjectParsed {
+protected:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a':
+ m_delete_all = true;
+ break;
+ case 'w':
+ m_category = std::string(option_arg);
+ break;
+ case 'l':
+ m_language = Language::GetLanguageTypeFromString(option_arg);
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_delete_all = false;
+ m_category = "default";
+ m_language = lldb::eLanguageTypeUnknown;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_formatter_delete_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_delete_all;
+ std::string m_category;
+ lldb::LanguageType m_language;
+ };
+
+ CommandOptions m_options;
+ uint32_t m_formatter_kind_mask;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeFormatterDelete(CommandInterpreter &interpreter,
+ uint32_t formatter_kind_mask,
+ const char *name, const char *help)
+ : CommandObjectParsed(interpreter, name, help, nullptr), m_options(),
+ m_formatter_kind_mask(formatter_kind_mask) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlain;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeFormatterDelete() override = default;
+
+protected:
+ virtual bool FormatterSpecificDeletion(ConstString typeCS) { return false; }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc != 1) {
+ result.AppendErrorWithFormat("%s takes 1 arg.\n", m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *typeA = command.GetArgumentAtIndex(0);
+ ConstString typeCS(typeA);
+
+ if (!typeCS) {
+ result.AppendError("empty typenames not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_options.m_delete_all) {
+ DataVisualization::Categories::ForEach(
+ [this, typeCS](const lldb::TypeCategoryImplSP &category_sp) -> bool {
+ category_sp->Delete(typeCS, m_formatter_kind_mask);
+ return true;
+ });
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+
+ bool delete_category = false;
+ bool extra_deletion = false;
+
+ if (m_options.m_language != lldb::eLanguageTypeUnknown) {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(m_options.m_language,
+ category);
+ if (category)
+ delete_category = category->Delete(typeCS, m_formatter_kind_mask);
+ extra_deletion = FormatterSpecificDeletion(typeCS);
+ } else {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(
+ ConstString(m_options.m_category.c_str()), category);
+ if (category)
+ delete_category = category->Delete(typeCS, m_formatter_kind_mask);
+ extra_deletion = FormatterSpecificDeletion(typeCS);
+ }
+
+ if (delete_category || extra_deletion) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ } else {
+ result.AppendErrorWithFormat("no custom formatter for %s.\n", typeA);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+};
+
+static constexpr OptionDefinition g_type_formatter_clear_options[] = {
+#define LLDB_OPTIONS_type_formatter_clear
+#include "CommandOptions.inc"
+};
+
+class CommandObjectTypeFormatterClear : public CommandObjectParsed {
+private:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'a':
+ m_delete_all = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_delete_all = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_formatter_clear_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_delete_all;
+ };
+
+ CommandOptions m_options;
+ uint32_t m_formatter_kind_mask;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeFormatterClear(CommandInterpreter &interpreter,
+ uint32_t formatter_kind_mask,
+ const char *name, const char *help)
+ : CommandObjectParsed(interpreter, name, help, nullptr), m_options(),
+ m_formatter_kind_mask(formatter_kind_mask) {}
+
+ ~CommandObjectTypeFormatterClear() override = default;
+
+protected:
+ virtual void FormatterSpecificDeletion() {}
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (m_options.m_delete_all) {
+ DataVisualization::Categories::ForEach(
+ [this](const TypeCategoryImplSP &category_sp) -> bool {
+ category_sp->Clear(m_formatter_kind_mask);
+ return true;
+ });
+ } else {
+ lldb::TypeCategoryImplSP category;
+ if (command.GetArgumentCount() > 0) {
+ const char *cat_name = command.GetArgumentAtIndex(0);
+ ConstString cat_nameCS(cat_name);
+ DataVisualization::Categories::GetCategory(cat_nameCS, category);
+ } else {
+ DataVisualization::Categories::GetCategory(ConstString(nullptr),
+ category);
+ }
+ category->Clear(m_formatter_kind_mask);
+ }
+
+ FormatterSpecificDeletion();
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeFormatDelete
+
+class CommandObjectTypeFormatDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeFormatDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter,
+ eFormatCategoryItemValue | eFormatCategoryItemRegexValue,
+ "type format delete",
+ "Delete an existing formatting style for a type.") {}
+
+ ~CommandObjectTypeFormatDelete() override = default;
+};
+
+// CommandObjectTypeFormatClear
+
+class CommandObjectTypeFormatClear : public CommandObjectTypeFormatterClear {
+public:
+ CommandObjectTypeFormatClear(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterClear(
+ interpreter,
+ eFormatCategoryItemValue | eFormatCategoryItemRegexValue,
+ "type format clear", "Delete all existing format styles.") {}
+};
+
+
+static constexpr OptionDefinition g_type_formatter_list_options[] = {
+#define LLDB_OPTIONS_type_formatter_list
+#include "CommandOptions.inc"
+};
+
+template <typename FormatterType>
+class CommandObjectTypeFormatterList : public CommandObjectParsed {
+ typedef typename FormatterType::SharedPointer FormatterSharedPointer;
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_category_regex("", ""),
+ m_category_language(lldb::eLanguageTypeUnknown,
+ lldb::eLanguageTypeUnknown) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'w':
+ m_category_regex.SetCurrentValue(option_arg);
+ m_category_regex.SetOptionWasSet();
+ break;
+ case 'l':
+ error = m_category_language.SetValueFromString(option_arg);
+ if (error.Success())
+ m_category_language.SetOptionWasSet();
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_category_regex.Clear();
+ m_category_language.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_formatter_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ OptionValueString m_category_regex;
+ OptionValueLanguage m_category_language;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeFormatterList(CommandInterpreter &interpreter,
+ const char *name, const char *help)
+ : CommandObjectParsed(interpreter, name, help, nullptr), m_options() {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatOptional;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeFormatterList() override = default;
+
+protected:
+ virtual bool FormatterSpecificList(CommandReturnObject &result) {
+ return false;
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ std::unique_ptr<RegularExpression> category_regex;
+ std::unique_ptr<RegularExpression> formatter_regex;
+
+ if (m_options.m_category_regex.OptionWasSet()) {
+ category_regex.reset(new RegularExpression());
+ if (!category_regex->Compile(
+ m_options.m_category_regex.GetCurrentValueAsRef())) {
+ result.AppendErrorWithFormat(
+ "syntax error in category regular expression '%s'",
+ m_options.m_category_regex.GetCurrentValueAsRef().str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ if (argc == 1) {
+ const char *arg = command.GetArgumentAtIndex(0);
+ formatter_regex.reset(new RegularExpression());
+ if (!formatter_regex->Compile(llvm::StringRef::withNullAsEmpty(arg))) {
+ result.AppendErrorWithFormat("syntax error in regular expression '%s'",
+ arg);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ bool any_printed = false;
+
+ auto category_closure = [&result, &formatter_regex, &any_printed](
+ const lldb::TypeCategoryImplSP &category) -> void {
+ result.GetOutputStream().Printf(
+ "-----------------------\nCategory: %s%s\n-----------------------\n",
+ category->GetName(), category->IsEnabled() ? "" : " (disabled)");
+
+ TypeCategoryImpl::ForEachCallbacks<FormatterType> foreach;
+ foreach
+ .SetExact([&result, &formatter_regex, &any_printed](
+ ConstString name,
+ const FormatterSharedPointer &format_sp) -> bool {
+ if (formatter_regex) {
+ bool escape = true;
+ if (name.GetStringRef() == formatter_regex->GetText()) {
+ escape = false;
+ } else if (formatter_regex->Execute(name.GetStringRef())) {
+ escape = false;
+ }
+
+ if (escape)
+ return true;
+ }
+
+ any_printed = true;
+ result.GetOutputStream().Printf("%s: %s\n", name.AsCString(),
+ format_sp->GetDescription().c_str());
+ return true;
+ });
+
+ foreach
+ .SetWithRegex([&result, &formatter_regex, &any_printed](
+ RegularExpressionSP regex_sp,
+ const FormatterSharedPointer &format_sp) -> bool {
+ if (formatter_regex) {
+ bool escape = true;
+ if (regex_sp->GetText() == formatter_regex->GetText()) {
+ escape = false;
+ } else if (formatter_regex->Execute(regex_sp->GetText())) {
+ escape = false;
+ }
+
+ if (escape)
+ return true;
+ }
+
+ any_printed = true;
+ result.GetOutputStream().Printf("%s: %s\n",
+ regex_sp->GetText().str().c_str(),
+ format_sp->GetDescription().c_str());
+ return true;
+ });
+
+ category->ForEach(foreach);
+ };
+
+ if (m_options.m_category_language.OptionWasSet()) {
+ lldb::TypeCategoryImplSP category_sp;
+ DataVisualization::Categories::GetCategory(
+ m_options.m_category_language.GetCurrentValue(), category_sp);
+ if (category_sp)
+ category_closure(category_sp);
+ } else {
+ DataVisualization::Categories::ForEach(
+ [&category_regex, &category_closure](
+ const lldb::TypeCategoryImplSP &category) -> bool {
+ if (category_regex) {
+ bool escape = true;
+ if (category->GetName() == category_regex->GetText()) {
+ escape = false;
+ } else if (category_regex->Execute(
+ llvm::StringRef::withNullAsEmpty(
+ category->GetName()))) {
+ escape = false;
+ }
+
+ if (escape)
+ return true;
+ }
+
+ category_closure(category);
+
+ return true;
+ });
+
+ any_printed = FormatterSpecificList(result) | any_printed;
+ }
+
+ if (any_printed)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else {
+ result.GetOutputStream().PutCString("no matching results found.\n");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeFormatList
+
+class CommandObjectTypeFormatList
+ : public CommandObjectTypeFormatterList<TypeFormatImpl> {
+public:
+ CommandObjectTypeFormatList(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterList(interpreter, "type format list",
+ "Show a list of current formats.") {}
+};
+
+#ifndef LLDB_DISABLE_PYTHON
+
+// CommandObjectTypeSummaryAdd
+
+#endif // LLDB_DISABLE_PYTHON
+
+Status CommandObjectTypeSummaryAdd::CommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ bool success;
+
+ switch (short_option) {
+ case 'C':
+ m_flags.SetCascades(OptionArgParser::ToBoolean(option_arg, true, &success));
+ if (!success)
+ error.SetErrorStringWithFormat("invalid value for cascade: %s",
+ option_arg.str().c_str());
+ break;
+ case 'e':
+ m_flags.SetDontShowChildren(false);
+ break;
+ case 'h':
+ m_flags.SetHideEmptyAggregates(true);
+ break;
+ case 'v':
+ m_flags.SetDontShowValue(true);
+ break;
+ case 'c':
+ m_flags.SetShowMembersOneLiner(true);
+ break;
+ case 's':
+ m_format_string = std::string(option_arg);
+ break;
+ case 'p':
+ m_flags.SetSkipPointers(true);
+ break;
+ case 'r':
+ m_flags.SetSkipReferences(true);
+ break;
+ case 'x':
+ m_regex = true;
+ break;
+ case 'n':
+ m_name.SetString(option_arg);
+ break;
+ case 'o':
+ m_python_script = option_arg;
+ m_is_add_script = true;
+ break;
+ case 'F':
+ m_python_function = option_arg;
+ m_is_add_script = true;
+ break;
+ case 'P':
+ m_is_add_script = true;
+ break;
+ case 'w':
+ m_category = std::string(option_arg);
+ break;
+ case 'O':
+ m_flags.SetHideItemNames(true);
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+
+ return error;
+}
+
+void CommandObjectTypeSummaryAdd::CommandOptions::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_flags.Clear().SetCascades().SetDontShowChildren().SetDontShowValue(false);
+ m_flags.SetShowMembersOneLiner(false)
+ .SetSkipPointers(false)
+ .SetSkipReferences(false)
+ .SetHideItemNames(false);
+
+ m_regex = false;
+ m_name.Clear();
+ m_python_script = "";
+ m_python_function = "";
+ m_format_string = "";
+ m_is_add_script = false;
+ m_category = "default";
+}
+
+#ifndef LLDB_DISABLE_PYTHON
+
+bool CommandObjectTypeSummaryAdd::Execute_ScriptSummary(
+ Args &command, CommandReturnObject &result) {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1 && !m_options.m_name) {
+ result.AppendErrorWithFormat("%s takes one or more args.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ TypeSummaryImplSP script_format;
+
+ if (!m_options.m_python_function
+ .empty()) // we have a Python function ready to use
+ {
+ const char *funct_name = m_options.m_python_function.c_str();
+ if (!funct_name || !funct_name[0]) {
+ result.AppendError("function name empty.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::string code =
+ (" " + m_options.m_python_function + "(valobj,internal_dict)");
+
+ script_format = std::make_shared<ScriptSummaryFormat>(
+ m_options.m_flags, funct_name, code.c_str());
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+
+ if (interpreter && !interpreter->CheckObjectExists(funct_name))
+ result.AppendWarningWithFormat(
+ "The provided function \"%s\" does not exist - "
+ "please define it before attempting to use this summary.\n",
+ funct_name);
+ } else if (!m_options.m_python_script
+ .empty()) // we have a quick 1-line script, just use it
+ {
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+ if (!interpreter) {
+ result.AppendError("script interpreter missing - unable to generate "
+ "function wrapper.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ StringList funct_sl;
+ funct_sl << m_options.m_python_script.c_str();
+ std::string funct_name_str;
+ if (!interpreter->GenerateTypeScriptFunction(funct_sl, funct_name_str)) {
+ result.AppendError("unable to generate function wrapper.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ if (funct_name_str.empty()) {
+ result.AppendError(
+ "script interpreter failed to generate a valid function name.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::string code = " " + m_options.m_python_script;
+
+ script_format = std::make_shared<ScriptSummaryFormat>(
+ m_options.m_flags, funct_name_str.c_str(), code.c_str());
+ } else {
+ // Use an IOHandler to grab Python code from the user
+ ScriptAddOptions *options =
+ new ScriptAddOptions(m_options.m_flags, m_options.m_regex,
+ m_options.m_name, m_options.m_category);
+
+ for (auto &entry : command.entries()) {
+ if (entry.ref.empty()) {
+ result.AppendError("empty typenames not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ options->m_target_types << entry.ref;
+ }
+
+ m_interpreter.GetPythonCommandsFromIOHandler(
+ " ", // Prompt
+ *this, // IOHandlerDelegate
+ true, // Run IOHandler in async mode
+ options); // Baton for the "io_handler" that will be passed back into
+ // our IOHandlerDelegate functions
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+
+ return result.Succeeded();
+ }
+
+ // if I am here, script_format must point to something good, so I can add
+ // that as a script summary to all interested parties
+
+ Status error;
+
+ for (auto &entry : command.entries()) {
+ CommandObjectTypeSummaryAdd::AddSummary(
+ ConstString(entry.ref), script_format,
+ (m_options.m_regex ? eRegexSummary : eRegularSummary),
+ m_options.m_category, &error);
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ if (m_options.m_name) {
+ AddSummary(m_options.m_name, script_format, eNamedSummary,
+ m_options.m_category, &error);
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.AppendError("added to types, but not given a name");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ return result.Succeeded();
+}
+
+#endif // LLDB_DISABLE_PYTHON
+
+bool CommandObjectTypeSummaryAdd::Execute_StringSummary(
+ Args &command, CommandReturnObject &result) {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1 && !m_options.m_name) {
+ result.AppendErrorWithFormat("%s takes one or more args.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (!m_options.m_flags.GetShowMembersOneLiner() &&
+ m_options.m_format_string.empty()) {
+ result.AppendError("empty summary strings not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *format_cstr = (m_options.m_flags.GetShowMembersOneLiner()
+ ? ""
+ : m_options.m_format_string.c_str());
+
+ // ${var%S} is an endless recursion, prevent it
+ if (strcmp(format_cstr, "${var%S}") == 0) {
+ result.AppendError("recursive summary not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::unique_ptr<StringSummaryFormat> string_format(
+ new StringSummaryFormat(m_options.m_flags, format_cstr));
+ if (!string_format) {
+ result.AppendError("summary creation failed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ if (string_format->m_error.Fail()) {
+ result.AppendErrorWithFormat("syntax error: %s",
+ string_format->m_error.AsCString("<unknown>"));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ lldb::TypeSummaryImplSP entry(string_format.release());
+
+ // now I have a valid format, let's add it to every type
+ Status error;
+ for (auto &arg_entry : command.entries()) {
+ if (arg_entry.ref.empty()) {
+ result.AppendError("empty typenames not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ ConstString typeCS(arg_entry.ref);
+
+ AddSummary(typeCS, entry,
+ (m_options.m_regex ? eRegexSummary : eRegularSummary),
+ m_options.m_category, &error);
+
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ if (m_options.m_name) {
+ AddSummary(m_options.m_name, entry, eNamedSummary, m_options.m_category,
+ &error);
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.AppendError("added to types, but not given a name");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+}
+
+CommandObjectTypeSummaryAdd::CommandObjectTypeSummaryAdd(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type summary add",
+ "Add a new summary style for a type.", nullptr),
+ IOHandlerDelegateMultiline("DONE"), m_options(interpreter) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+
+ SetHelpLong(
+ R"(
+The following examples of 'type summary add' refer to this code snippet for context:
+
+ struct JustADemo
+ {
+ int* ptr;
+ float value;
+ JustADemo(int p = 1, float v = 0.1) : ptr(new int(p)), value(v) {}
+ };
+ JustADemo demo_instance(42, 3.14);
+
+ typedef JustADemo NewDemo;
+ NewDemo new_demo_instance(42, 3.14);
+
+(lldb) type summary add --summary-string "the answer is ${*var.ptr}" JustADemo
+
+ Subsequently displaying demo_instance with 'frame variable' or 'expression' will display "the answer is 42"
+
+(lldb) type summary add --summary-string "the answer is ${*var.ptr}, and the question is ${var.value}" JustADemo
+
+ Subsequently displaying demo_instance with 'frame variable' or 'expression' will display "the answer is 42 and the question is 3.14"
+
+)"
+ "Alternatively, you could define formatting for all pointers to integers and \
+rely on that when formatting JustADemo to obtain the same result:"
+ R"(
+
+(lldb) type summary add --summary-string "${var%V} -> ${*var}" "int *"
+(lldb) type summary add --summary-string "the answer is ${var.ptr}, and the question is ${var.value}" JustADemo
+
+)"
+ "Type summaries are automatically applied to derived typedefs, so the examples \
+above apply to both JustADemo and NewDemo. The cascade option can be used to \
+suppress this behavior:"
+ R"(
+
+(lldb) type summary add --summary-string "${var.ptr}, ${var.value},{${var.byte}}" JustADemo -C no
+
+ The summary will now be used for values of JustADemo but not NewDemo.
+
+)"
+ "By default summaries are shown for pointers and references to values of the \
+specified type. To suppress formatting for pointers use the -p option, or apply \
+the corresponding -r option to suppress formatting for references:"
+ R"(
+
+(lldb) type summary add -p -r --summary-string "${var.ptr}, ${var.value},{${var.byte}}" JustADemo
+
+)"
+ "One-line summaries including all fields in a type can be inferred without supplying an \
+explicit summary string by passing the -c option:"
+ R"(
+
+(lldb) type summary add -c JustADemo
+(lldb) frame variable demo_instance
+(ptr=<address>, value=3.14)
+
+)"
+ "Type summaries normally suppress the nested display of individual fields. To \
+supply a summary to supplement the default structure add the -e option:"
+ R"(
+
+(lldb) type summary add -e --summary-string "*ptr = ${*var.ptr}" JustADemo
+
+)"
+ "Now when displaying JustADemo values the int* is displayed, followed by the \
+standard LLDB sequence of children, one per line:"
+ R"(
+
+*ptr = 42 {
+ ptr = <address>
+ value = 3.14
+}
+
+)"
+ "You can also add summaries written in Python. These scripts use lldb public API to \
+gather information from your variables and produce a meaningful summary. To start a \
+multi-line script use the -P option. The function declaration will be displayed along with \
+a comment describing the two arguments. End your script with the word 'DONE' on a line by \
+itself:"
+ R"(
+
+(lldb) type summary add JustADemo -P
+def function (valobj,internal_dict):
+"""valobj: an SBValue which you want to provide a summary for
+internal_dict: an LLDB support object not to be used"""
+ value = valobj.GetChildMemberWithName('value');
+ return 'My value is ' + value.GetValue();
+ DONE
+
+Alternatively, the -o option can be used when providing a simple one-line Python script:
+
+(lldb) type summary add JustADemo -o "value = valobj.GetChildMemberWithName('value'); return 'My value is ' + value.GetValue();")");
+}
+
+bool CommandObjectTypeSummaryAdd::DoExecute(Args &command,
+ CommandReturnObject &result) {
+ WarnOnPotentialUnquotedUnsignedType(command, result);
+
+ if (m_options.m_is_add_script) {
+#ifndef LLDB_DISABLE_PYTHON
+ return Execute_ScriptSummary(command, result);
+#else
+ result.AppendError("python is disabled");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+#endif // LLDB_DISABLE_PYTHON
+ }
+
+ return Execute_StringSummary(command, result);
+}
+
+static bool FixArrayTypeNameWithRegex(ConstString &type_name) {
+ llvm::StringRef type_name_ref(type_name.GetStringRef());
+
+ if (type_name_ref.endswith("[]")) {
+ std::string type_name_str(type_name.GetCString());
+ type_name_str.resize(type_name_str.length() - 2);
+ if (type_name_str.back() != ' ')
+ type_name_str.append(" \\[[0-9]+\\]");
+ else
+ type_name_str.append("\\[[0-9]+\\]");
+ type_name.SetCString(type_name_str.c_str());
+ return true;
+ }
+ return false;
+}
+
+bool CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name,
+ TypeSummaryImplSP entry,
+ SummaryFormatType type,
+ std::string category_name,
+ Status *error) {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()),
+ category);
+
+ if (type == eRegularSummary) {
+ if (FixArrayTypeNameWithRegex(type_name))
+ type = eRegexSummary;
+ }
+
+ if (type == eRegexSummary) {
+ RegularExpressionSP typeRX(new RegularExpression());
+ if (!typeRX->Compile(type_name.GetStringRef())) {
+ if (error)
+ error->SetErrorString(
+ "regex format error (maybe this is not really a regex?)");
+ return false;
+ }
+
+ category->GetRegexTypeSummariesContainer()->Delete(type_name);
+ category->GetRegexTypeSummariesContainer()->Add(typeRX, entry);
+
+ return true;
+ } else if (type == eNamedSummary) {
+ // system named summaries do not exist (yet?)
+ DataVisualization::NamedSummaryFormats::Add(type_name, entry);
+ return true;
+ } else {
+ category->GetTypeSummariesContainer()->Add(type_name, entry);
+ return true;
+ }
+}
+
+// CommandObjectTypeSummaryDelete
+
+class CommandObjectTypeSummaryDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeSummaryDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter,
+ eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary,
+ "type summary delete", "Delete an existing summary for a type.") {}
+
+ ~CommandObjectTypeSummaryDelete() override = default;
+
+protected:
+ bool FormatterSpecificDeletion(ConstString typeCS) override {
+ if (m_options.m_language != lldb::eLanguageTypeUnknown)
+ return false;
+ return DataVisualization::NamedSummaryFormats::Delete(typeCS);
+ }
+};
+
+class CommandObjectTypeSummaryClear : public CommandObjectTypeFormatterClear {
+public:
+ CommandObjectTypeSummaryClear(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterClear(
+ interpreter,
+ eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary,
+ "type summary clear", "Delete all existing summaries.") {}
+
+protected:
+ void FormatterSpecificDeletion() override {
+ DataVisualization::NamedSummaryFormats::Clear();
+ }
+};
+
+// CommandObjectTypeSummaryList
+
+class CommandObjectTypeSummaryList
+ : public CommandObjectTypeFormatterList<TypeSummaryImpl> {
+public:
+ CommandObjectTypeSummaryList(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterList(interpreter, "type summary list",
+ "Show a list of current summaries.") {}
+
+protected:
+ bool FormatterSpecificList(CommandReturnObject &result) override {
+ if (DataVisualization::NamedSummaryFormats::GetCount() > 0) {
+ result.GetOutputStream().Printf("Named summaries:\n");
+ DataVisualization::NamedSummaryFormats::ForEach(
+ [&result](ConstString name,
+ const TypeSummaryImplSP &summary_sp) -> bool {
+ result.GetOutputStream().Printf(
+ "%s: %s\n", name.AsCString(),
+ summary_sp->GetDescription().c_str());
+ return true;
+ });
+ return true;
+ }
+ return false;
+ }
+};
+
+// CommandObjectTypeCategoryDefine
+
+static constexpr OptionDefinition g_type_category_define_options[] = {
+#define LLDB_OPTIONS_type_category_define
+#include "CommandOptions.inc"
+};
+
+class CommandObjectTypeCategoryDefine : public CommandObjectParsed {
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_define_enabled(false, false),
+ m_cate_language(eLanguageTypeUnknown, eLanguageTypeUnknown) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'e':
+ m_define_enabled.SetValueFromString(llvm::StringRef("true"));
+ break;
+ case 'l':
+ error = m_cate_language.SetValueFromString(option_arg);
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_define_enabled.Clear();
+ m_cate_language.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_category_define_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ OptionValueBoolean m_define_enabled;
+ OptionValueLanguage m_cate_language;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeCategoryDefine(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category define",
+ "Define a new category as a source of formatters.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeCategoryDefine() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1) {
+ result.AppendErrorWithFormat("%s takes 1 or more args.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ for (auto &entry : command.entries()) {
+ TypeCategoryImplSP category_sp;
+ if (DataVisualization::Categories::GetCategory(ConstString(entry.ref),
+ category_sp) &&
+ category_sp) {
+ category_sp->AddLanguage(m_options.m_cate_language.GetCurrentValue());
+ if (m_options.m_define_enabled.GetCurrentValue())
+ DataVisualization::Categories::Enable(category_sp,
+ TypeCategoryMap::Default);
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeCategoryEnable
+
+static constexpr OptionDefinition g_type_category_enable_options[] = {
+#define LLDB_OPTIONS_type_category_enable
+#include "CommandOptions.inc"
+};
+
+class CommandObjectTypeCategoryEnable : public CommandObjectParsed {
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'l':
+ if (!option_arg.empty()) {
+ m_language = Language::GetLanguageTypeFromString(option_arg);
+ if (m_language == lldb::eLanguageTypeUnknown)
+ error.SetErrorStringWithFormat("unrecognized language '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_language = lldb::eLanguageTypeUnknown;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_category_enable_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ lldb::LanguageType m_language;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeCategoryEnable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category enable",
+ "Enable a category as a source of formatters.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeCategoryEnable() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1 && m_options.m_language == lldb::eLanguageTypeUnknown) {
+ result.AppendErrorWithFormat("%s takes arguments and/or a language",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (argc == 1 && strcmp(command.GetArgumentAtIndex(0), "*") == 0) {
+ DataVisualization::Categories::EnableStar();
+ } else if (argc > 0) {
+ for (int i = argc - 1; i >= 0; i--) {
+ const char *typeA = command.GetArgumentAtIndex(i);
+ ConstString typeCS(typeA);
+
+ if (!typeCS) {
+ result.AppendError("empty category name not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ DataVisualization::Categories::Enable(typeCS);
+ lldb::TypeCategoryImplSP cate;
+ if (DataVisualization::Categories::GetCategory(typeCS, cate) && cate) {
+ if (cate->GetCount() == 0) {
+ result.AppendWarning("empty category enabled (typo?)");
+ }
+ }
+ }
+ }
+
+ if (m_options.m_language != lldb::eLanguageTypeUnknown)
+ DataVisualization::Categories::Enable(m_options.m_language);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeCategoryDelete
+
+class CommandObjectTypeCategoryDelete : public CommandObjectParsed {
+public:
+ CommandObjectTypeCategoryDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category delete",
+ "Delete a category and all associated formatters.",
+ nullptr) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeCategoryDelete() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1) {
+ result.AppendErrorWithFormat("%s takes 1 or more arg.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ bool success = true;
+
+ // the order is not relevant here
+ for (int i = argc - 1; i >= 0; i--) {
+ const char *typeA = command.GetArgumentAtIndex(i);
+ ConstString typeCS(typeA);
+
+ if (!typeCS) {
+ result.AppendError("empty category name not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ if (!DataVisualization::Categories::Delete(typeCS))
+ success = false; // keep deleting even if we hit an error
+ }
+ if (success) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ } else {
+ result.AppendError("cannot delete one or more categories\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+};
+
+// CommandObjectTypeCategoryDisable
+
+OptionDefinition constexpr g_type_category_disable_options[] = {
+#define LLDB_OPTIONS_type_category_disable
+#include "CommandOptions.inc"
+};
+
+class CommandObjectTypeCategoryDisable : public CommandObjectParsed {
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'l':
+ if (!option_arg.empty()) {
+ m_language = Language::GetLanguageTypeFromString(option_arg);
+ if (m_language == lldb::eLanguageTypeUnknown)
+ error.SetErrorStringWithFormat("unrecognized language '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_language = lldb::eLanguageTypeUnknown;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_category_disable_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ lldb::LanguageType m_language;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+public:
+ CommandObjectTypeCategoryDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category disable",
+ "Disable a category as a source of formatters.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeCategoryDisable() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1 && m_options.m_language == lldb::eLanguageTypeUnknown) {
+ result.AppendErrorWithFormat("%s takes arguments and/or a language",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (argc == 1 && strcmp(command.GetArgumentAtIndex(0), "*") == 0) {
+ DataVisualization::Categories::DisableStar();
+ } else if (argc > 0) {
+ // the order is not relevant here
+ for (int i = argc - 1; i >= 0; i--) {
+ const char *typeA = command.GetArgumentAtIndex(i);
+ ConstString typeCS(typeA);
+
+ if (!typeCS) {
+ result.AppendError("empty category name not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ DataVisualization::Categories::Disable(typeCS);
+ }
+ }
+
+ if (m_options.m_language != lldb::eLanguageTypeUnknown)
+ DataVisualization::Categories::Disable(m_options.m_language);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeCategoryList
+
+class CommandObjectTypeCategoryList : public CommandObjectParsed {
+public:
+ CommandObjectTypeCategoryList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type category list",
+ "Provide a list of all existing categories.",
+ nullptr) {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatOptional;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+ }
+
+ ~CommandObjectTypeCategoryList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ std::unique_ptr<RegularExpression> regex;
+
+ if (argc == 1) {
+ regex.reset(new RegularExpression());
+ const char *arg = command.GetArgumentAtIndex(0);
+ if (!regex->Compile(llvm::StringRef::withNullAsEmpty(arg))) {
+ result.AppendErrorWithFormat(
+ "syntax error in category regular expression '%s'", arg);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else if (argc != 0) {
+ result.AppendErrorWithFormat("%s takes 0 or one arg.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ DataVisualization::Categories::ForEach(
+ [&regex, &result](const lldb::TypeCategoryImplSP &category_sp) -> bool {
+ if (regex) {
+ bool escape = true;
+ if (regex->GetText() == category_sp->GetName()) {
+ escape = false;
+ } else if (regex->Execute(llvm::StringRef::withNullAsEmpty(
+ category_sp->GetName()))) {
+ escape = false;
+ }
+
+ if (escape)
+ return true;
+ }
+
+ result.GetOutputStream().Printf(
+ "Category: %s\n", category_sp->GetDescription().c_str());
+
+ return true;
+ });
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectTypeFilterList
+
+class CommandObjectTypeFilterList
+ : public CommandObjectTypeFormatterList<TypeFilterImpl> {
+public:
+ CommandObjectTypeFilterList(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterList(interpreter, "type filter list",
+ "Show a list of current filters.") {}
+};
+
+#ifndef LLDB_DISABLE_PYTHON
+
+// CommandObjectTypeSynthList
+
+class CommandObjectTypeSynthList
+ : public CommandObjectTypeFormatterList<SyntheticChildren> {
+public:
+ CommandObjectTypeSynthList(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterList(
+ interpreter, "type synthetic list",
+ "Show a list of current synthetic providers.") {}
+};
+
+#endif // LLDB_DISABLE_PYTHON
+
+// CommandObjectTypeFilterDelete
+
+class CommandObjectTypeFilterDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeFilterDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter,
+ eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter,
+ "type filter delete", "Delete an existing filter for a type.") {}
+
+ ~CommandObjectTypeFilterDelete() override = default;
+};
+
+#ifndef LLDB_DISABLE_PYTHON
+
+// CommandObjectTypeSynthDelete
+
+class CommandObjectTypeSynthDelete : public CommandObjectTypeFormatterDelete {
+public:
+ CommandObjectTypeSynthDelete(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterDelete(
+ interpreter,
+ eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth,
+ "type synthetic delete",
+ "Delete an existing synthetic provider for a type.") {}
+
+ ~CommandObjectTypeSynthDelete() override = default;
+};
+
+#endif // LLDB_DISABLE_PYTHON
+
+// CommandObjectTypeFilterClear
+
+class CommandObjectTypeFilterClear : public CommandObjectTypeFormatterClear {
+public:
+ CommandObjectTypeFilterClear(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterClear(
+ interpreter,
+ eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter,
+ "type filter clear", "Delete all existing filter.") {}
+};
+
+#ifndef LLDB_DISABLE_PYTHON
+// CommandObjectTypeSynthClear
+
+class CommandObjectTypeSynthClear : public CommandObjectTypeFormatterClear {
+public:
+ CommandObjectTypeSynthClear(CommandInterpreter &interpreter)
+ : CommandObjectTypeFormatterClear(
+ interpreter,
+ eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth,
+ "type synthetic clear",
+ "Delete all existing synthetic providers.") {}
+};
+
+bool CommandObjectTypeSynthAdd::Execute_HandwritePython(
+ Args &command, CommandReturnObject &result) {
+ SynthAddOptions *options = new SynthAddOptions(
+ m_options.m_skip_pointers, m_options.m_skip_references,
+ m_options.m_cascade, m_options.m_regex, m_options.m_category);
+
+ for (auto &entry : command.entries()) {
+ if (entry.ref.empty()) {
+ result.AppendError("empty typenames not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ options->m_target_types << entry.ref;
+ }
+
+ m_interpreter.GetPythonCommandsFromIOHandler(
+ " ", // Prompt
+ *this, // IOHandlerDelegate
+ true, // Run IOHandler in async mode
+ options); // Baton for the "io_handler" that will be passed back into our
+ // IOHandlerDelegate functions
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+}
+
+bool CommandObjectTypeSynthAdd::Execute_PythonClass(
+ Args &command, CommandReturnObject &result) {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1) {
+ result.AppendErrorWithFormat("%s takes one or more args.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_options.m_class_name.empty() && !m_options.m_input_python) {
+ result.AppendErrorWithFormat("%s needs either a Python class name or -P to "
+ "directly input Python code.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ SyntheticChildrenSP entry;
+
+ ScriptedSyntheticChildren *impl = new ScriptedSyntheticChildren(
+ SyntheticChildren::Flags()
+ .SetCascades(m_options.m_cascade)
+ .SetSkipPointers(m_options.m_skip_pointers)
+ .SetSkipReferences(m_options.m_skip_references),
+ m_options.m_class_name.c_str());
+
+ entry.reset(impl);
+
+ ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
+
+ if (interpreter &&
+ !interpreter->CheckObjectExists(impl->GetPythonClassName()))
+ result.AppendWarning("The provided class does not exist - please define it "
+ "before attempting to use this synthetic provider");
+
+ // now I have a valid provider, let's add it to every type
+
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(
+ ConstString(m_options.m_category.c_str()), category);
+
+ Status error;
+
+ for (auto &arg_entry : command.entries()) {
+ if (arg_entry.ref.empty()) {
+ result.AppendError("empty typenames not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ConstString typeCS(arg_entry.ref);
+ if (!AddSynth(typeCS, entry,
+ m_options.m_regex ? eRegexSynth : eRegularSynth,
+ m_options.m_category, &error)) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+}
+
+CommandObjectTypeSynthAdd::CommandObjectTypeSynthAdd(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type synthetic add",
+ "Add a new synthetic provider for a type.", nullptr),
+ IOHandlerDelegateMultiline("DONE"), m_options() {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+}
+
+bool CommandObjectTypeSynthAdd::AddSynth(ConstString type_name,
+ SyntheticChildrenSP entry,
+ SynthFormatType type,
+ std::string category_name,
+ Status *error) {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()),
+ category);
+
+ if (type == eRegularSynth) {
+ if (FixArrayTypeNameWithRegex(type_name))
+ type = eRegexSynth;
+ }
+
+ if (category->AnyMatches(type_name, eFormatCategoryItemFilter |
+ eFormatCategoryItemRegexFilter,
+ false)) {
+ if (error)
+ error->SetErrorStringWithFormat("cannot add synthetic for type %s when "
+ "filter is defined in same category!",
+ type_name.AsCString());
+ return false;
+ }
+
+ if (type == eRegexSynth) {
+ RegularExpressionSP typeRX(new RegularExpression());
+ if (!typeRX->Compile(type_name.GetStringRef())) {
+ if (error)
+ error->SetErrorString(
+ "regex format error (maybe this is not really a regex?)");
+ return false;
+ }
+
+ category->GetRegexTypeSyntheticsContainer()->Delete(type_name);
+ category->GetRegexTypeSyntheticsContainer()->Add(typeRX, entry);
+
+ return true;
+ } else {
+ category->GetTypeSyntheticsContainer()->Add(type_name, entry);
+ return true;
+ }
+}
+
+#endif // LLDB_DISABLE_PYTHON
+
+static constexpr OptionDefinition g_type_filter_add_options[] = {
+#define LLDB_OPTIONS_type_filter_add
+#include "CommandOptions.inc"
+};
+
+class CommandObjectTypeFilterAdd : public CommandObjectParsed {
+private:
+ class CommandOptions : public Options {
+ typedef std::vector<std::string> option_vector;
+
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ bool success;
+
+ switch (short_option) {
+ case 'C':
+ m_cascade = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid value for cascade: %s",
+ option_arg.str().c_str());
+ break;
+ case 'c':
+ m_expr_paths.push_back(option_arg);
+ has_child_list = true;
+ break;
+ case 'p':
+ m_skip_pointers = true;
+ break;
+ case 'r':
+ m_skip_references = true;
+ break;
+ case 'w':
+ m_category = std::string(option_arg);
+ break;
+ case 'x':
+ m_regex = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_cascade = true;
+ m_skip_pointers = false;
+ m_skip_references = false;
+ m_category = "default";
+ m_expr_paths.clear();
+ has_child_list = false;
+ m_regex = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_filter_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_cascade;
+ bool m_skip_references;
+ bool m_skip_pointers;
+ bool m_input_python;
+ option_vector m_expr_paths;
+ std::string m_category;
+ bool has_child_list;
+ bool m_regex;
+
+ typedef option_vector::iterator ExpressionPathsIterator;
+ };
+
+ CommandOptions m_options;
+
+ Options *GetOptions() override { return &m_options; }
+
+ enum FilterFormatType { eRegularFilter, eRegexFilter };
+
+ bool AddFilter(ConstString type_name, TypeFilterImplSP entry,
+ FilterFormatType type, std::string category_name,
+ Status *error) {
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(
+ ConstString(category_name.c_str()), category);
+
+ if (type == eRegularFilter) {
+ if (FixArrayTypeNameWithRegex(type_name))
+ type = eRegexFilter;
+ }
+
+ if (category->AnyMatches(type_name, eFormatCategoryItemSynth |
+ eFormatCategoryItemRegexSynth,
+ false)) {
+ if (error)
+ error->SetErrorStringWithFormat("cannot add filter for type %s when "
+ "synthetic is defined in same "
+ "category!",
+ type_name.AsCString());
+ return false;
+ }
+
+ if (type == eRegexFilter) {
+ RegularExpressionSP typeRX(new RegularExpression());
+ if (!typeRX->Compile(type_name.GetStringRef())) {
+ if (error)
+ error->SetErrorString(
+ "regex format error (maybe this is not really a regex?)");
+ return false;
+ }
+
+ category->GetRegexTypeFiltersContainer()->Delete(type_name);
+ category->GetRegexTypeFiltersContainer()->Add(typeRX, entry);
+
+ return true;
+ } else {
+ category->GetTypeFiltersContainer()->Add(type_name, entry);
+ return true;
+ }
+ }
+
+public:
+ CommandObjectTypeFilterAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "type filter add",
+ "Add a new filter for a type.", nullptr),
+ m_options() {
+ CommandArgumentEntry type_arg;
+ CommandArgumentData type_style_arg;
+
+ type_style_arg.arg_type = eArgTypeName;
+ type_style_arg.arg_repetition = eArgRepeatPlus;
+
+ type_arg.push_back(type_style_arg);
+
+ m_arguments.push_back(type_arg);
+
+ SetHelpLong(
+ R"(
+The following examples of 'type filter add' refer to this code snippet for context:
+
+ class Foo {
+ int a;
+ int b;
+ int c;
+ int d;
+ int e;
+ int f;
+ int g;
+ int h;
+ int i;
+ }
+ Foo my_foo;
+
+Adding a simple filter:
+
+(lldb) type filter add --child a --child g Foo
+(lldb) frame variable my_foo
+
+)"
+ "Produces output where only a and g are displayed. Other children of my_foo \
+(b, c, d, e, f, h and i) are available by asking for them explicitly:"
+ R"(
+
+(lldb) frame variable my_foo.b my_foo.c my_foo.i
+
+)"
+ "The formatting option --raw on frame variable bypasses the filter, showing \
+all children of my_foo as if no filter was defined:"
+ R"(
+
+(lldb) frame variable my_foo --raw)");
+ }
+
+ ~CommandObjectTypeFilterAdd() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+
+ if (argc < 1) {
+ result.AppendErrorWithFormat("%s takes one or more args.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_options.m_expr_paths.empty()) {
+ result.AppendErrorWithFormat("%s needs one or more children.\n",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ TypeFilterImplSP entry(new TypeFilterImpl(
+ SyntheticChildren::Flags()
+ .SetCascades(m_options.m_cascade)
+ .SetSkipPointers(m_options.m_skip_pointers)
+ .SetSkipReferences(m_options.m_skip_references)));
+
+ // go through the expression paths
+ CommandOptions::ExpressionPathsIterator begin,
+ end = m_options.m_expr_paths.end();
+
+ for (begin = m_options.m_expr_paths.begin(); begin != end; begin++)
+ entry->AddExpressionPath(*begin);
+
+ // now I have a valid provider, let's add it to every type
+
+ lldb::TypeCategoryImplSP category;
+ DataVisualization::Categories::GetCategory(
+ ConstString(m_options.m_category.c_str()), category);
+
+ Status error;
+
+ WarnOnPotentialUnquotedUnsignedType(command, result);
+
+ for (auto &arg_entry : command.entries()) {
+ if (arg_entry.ref.empty()) {
+ result.AppendError("empty typenames not allowed");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ConstString typeCS(arg_entry.ref);
+ if (!AddFilter(typeCS, entry,
+ m_options.m_regex ? eRegexFilter : eRegularFilter,
+ m_options.m_category, &error)) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+};
+
+// "type lookup"
+static constexpr OptionDefinition g_type_lookup_options[] = {
+#define LLDB_OPTIONS_type_lookup
+#include "CommandOptions.inc"
+};
+
+class CommandObjectTypeLookup : public CommandObjectRaw {
+protected:
+ // this function is allowed to do a more aggressive job at guessing languages
+ // than the expression parser is comfortable with - so leave the original
+ // call alone and add one that is specific to type lookup
+ lldb::LanguageType GuessLanguage(StackFrame *frame) {
+ lldb::LanguageType lang_type = lldb::eLanguageTypeUnknown;
+
+ if (!frame)
+ return lang_type;
+
+ lang_type = frame->GuessLanguage();
+ if (lang_type != lldb::eLanguageTypeUnknown)
+ return lang_type;
+
+ Symbol *s = frame->GetSymbolContext(eSymbolContextSymbol).symbol;
+ if (s)
+ lang_type = s->GetMangled().GuessLanguage();
+
+ return lang_type;
+ }
+
+ class CommandOptions : public OptionGroup {
+ public:
+ CommandOptions()
+ : OptionGroup(), m_show_help(false), m_language(eLanguageTypeUnknown) {}
+
+ ~CommandOptions() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_type_lookup_options);
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = g_type_lookup_options[option_idx].short_option;
+
+ switch (short_option) {
+ case 'h':
+ m_show_help = true;
+ break;
+
+ case 'l':
+ m_language = Language::GetLanguageTypeFromString(option_value);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_show_help = false;
+ m_language = eLanguageTypeUnknown;
+ }
+
+ // Options table: Required for subclasses of Options.
+
+ bool m_show_help;
+ lldb::LanguageType m_language;
+ };
+
+ OptionGroupOptions m_option_group;
+ CommandOptions m_command_options;
+
+public:
+ CommandObjectTypeLookup(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "type lookup",
+ "Lookup types and declarations in the current target, "
+ "following language-specific naming conventions.",
+ "type lookup <type-specifier>",
+ eCommandRequiresTarget),
+ m_option_group(), m_command_options() {
+ m_option_group.Append(&m_command_options);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectTypeLookup() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ llvm::StringRef GetHelpLong() override {
+ if (!m_cmd_help_long.empty())
+ return m_cmd_help_long;
+
+ StreamString stream;
+ Language::ForEach([&](Language *lang) {
+ if (const char *help = lang->GetLanguageSpecificTypeLookupHelp())
+ stream.Printf("%s\n", help);
+ return true;
+ });
+
+ m_cmd_help_long = stream.GetString();
+ return m_cmd_help_long;
+ }
+
+ bool DoExecute(llvm::StringRef raw_command_line,
+ CommandReturnObject &result) override {
+ if (raw_command_line.empty()) {
+ result.SetError(
+ "type lookup cannot be invoked without a type name as argument");
+ return false;
+ }
+
+ auto exe_ctx = GetCommandInterpreter().GetExecutionContext();
+ m_option_group.NotifyOptionParsingStarting(&exe_ctx);
+
+ OptionsWithRaw args(raw_command_line);
+ const char *name_of_type = args.GetRawPart().c_str();
+
+ if (args.HasArgs())
+ if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group,
+ exe_ctx))
+ return false;
+
+ ExecutionContextScope *best_scope = exe_ctx.GetBestExecutionContextScope();
+
+ bool any_found = false;
+
+ std::vector<Language *> languages;
+
+ bool is_global_search = false;
+ LanguageType guessed_language = lldb::eLanguageTypeUnknown;
+
+ if ((is_global_search =
+ (m_command_options.m_language == eLanguageTypeUnknown))) {
+ Language::ForEach([&](Language *lang) {
+ languages.push_back(lang);
+ return true;
+ });
+ } else {
+ languages.push_back(Language::FindPlugin(m_command_options.m_language));
+ }
+
+ // This is not the most efficient way to do this, but we support very few
+ // languages so the cost of the sort is going to be dwarfed by the actual
+ // lookup anyway
+ if (StackFrame *frame = m_exe_ctx.GetFramePtr()) {
+ guessed_language = GuessLanguage(frame);
+ if (guessed_language != eLanguageTypeUnknown) {
+ llvm::sort(
+ languages.begin(), languages.end(),
+ [guessed_language](Language *lang1, Language *lang2) -> bool {
+ if (!lang1 || !lang2)
+ return false;
+ LanguageType lt1 = lang1->GetLanguageType();
+ LanguageType lt2 = lang2->GetLanguageType();
+ if (lt1 == guessed_language)
+ return true; // make the selected frame's language come first
+ if (lt2 == guessed_language)
+ return false; // make the selected frame's language come first
+ return (lt1 < lt2); // normal comparison otherwise
+ });
+ }
+ }
+
+ bool is_first_language = true;
+
+ for (Language *language : languages) {
+ if (!language)
+ continue;
+
+ if (auto scavenger = language->GetTypeScavenger()) {
+ Language::TypeScavenger::ResultSet search_results;
+ if (scavenger->Find(best_scope, name_of_type, search_results) > 0) {
+ for (const auto &search_result : search_results) {
+ if (search_result && search_result->IsValid()) {
+ any_found = true;
+ search_result->DumpToStream(result.GetOutputStream(),
+ this->m_command_options.m_show_help);
+ }
+ }
+ }
+ }
+ // this is "type lookup SomeName" and we did find a match, so get out
+ if (any_found && is_global_search)
+ break;
+ else if (is_first_language && is_global_search &&
+ guessed_language != lldb::eLanguageTypeUnknown) {
+ is_first_language = false;
+ result.GetOutputStream().Printf(
+ "no type was found in the current language %s matching '%s'; "
+ "performing a global search across all languages\n",
+ Language::GetNameForLanguageType(guessed_language), name_of_type);
+ }
+ }
+
+ if (!any_found)
+ result.AppendMessageWithFormat("no type was found matching '%s'\n",
+ name_of_type);
+
+ result.SetStatus(any_found ? lldb::eReturnStatusSuccessFinishResult
+ : lldb::eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+};
+
+template <typename FormatterType>
+class CommandObjectFormatterInfo : public CommandObjectRaw {
+public:
+ typedef std::function<typename FormatterType::SharedPointer(ValueObject &)>
+ DiscoveryFunction;
+ CommandObjectFormatterInfo(CommandInterpreter &interpreter,
+ const char *formatter_name,
+ DiscoveryFunction discovery_func)
+ : CommandObjectRaw(interpreter, "", "", "",
+ eCommandRequiresFrame),
+ m_formatter_name(formatter_name ? formatter_name : ""),
+ m_discovery_function(discovery_func) {
+ StreamString name;
+ name.Printf("type %s info", formatter_name);
+ SetCommandName(name.GetString());
+ StreamString help;
+ help.Printf("This command evaluates the provided expression and shows "
+ "which %s is applied to the resulting value (if any).",
+ formatter_name);
+ SetHelp(help.GetString());
+ StreamString syntax;
+ syntax.Printf("type %s info <expr>", formatter_name);
+ SetSyntax(syntax.GetString());
+ }
+
+ ~CommandObjectFormatterInfo() override = default;
+
+protected:
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ TargetSP target_sp = GetDebugger().GetSelectedTarget();
+ Thread *thread = GetDefaultThread();
+ if (!thread) {
+ result.AppendError("no default thread");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ StackFrameSP frame_sp = thread->GetSelectedFrame();
+ ValueObjectSP result_valobj_sp;
+ EvaluateExpressionOptions options;
+ lldb::ExpressionResults expr_result = target_sp->EvaluateExpression(
+ command, frame_sp.get(), result_valobj_sp, options);
+ if (expr_result == eExpressionCompleted && result_valobj_sp) {
+ result_valobj_sp =
+ result_valobj_sp->GetQualifiedRepresentationIfAvailable(
+ target_sp->GetPreferDynamicValue(),
+ target_sp->GetEnableSyntheticValue());
+ typename FormatterType::SharedPointer formatter_sp =
+ m_discovery_function(*result_valobj_sp);
+ if (formatter_sp) {
+ std::string description(formatter_sp->GetDescription());
+ result.GetOutputStream()
+ << m_formatter_name << " applied to ("
+ << result_valobj_sp->GetDisplayTypeName().AsCString("<unknown>")
+ << ") " << command << " is: " << description << "\n";
+ result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
+ } else {
+ result.GetOutputStream()
+ << "no " << m_formatter_name << " applies to ("
+ << result_valobj_sp->GetDisplayTypeName().AsCString("<unknown>")
+ << ") " << command << "\n";
+ result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
+ }
+ return true;
+ } else {
+ result.AppendError("failed to evaluate expression");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ }
+
+private:
+ std::string m_formatter_name;
+ DiscoveryFunction m_discovery_function;
+};
+
+class CommandObjectTypeFormat : public CommandObjectMultiword {
+public:
+ CommandObjectTypeFormat(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "type format",
+ "Commands for customizing value display formats.",
+ "type format [<sub-command-options>] ") {
+ LoadSubCommand(
+ "add", CommandObjectSP(new CommandObjectTypeFormatAdd(interpreter)));
+ LoadSubCommand("clear", CommandObjectSP(
+ new CommandObjectTypeFormatClear(interpreter)));
+ LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeFormatDelete(
+ interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectTypeFormatList(interpreter)));
+ LoadSubCommand(
+ "info", CommandObjectSP(new CommandObjectFormatterInfo<TypeFormatImpl>(
+ interpreter, "format",
+ [](ValueObject &valobj) -> TypeFormatImpl::SharedPointer {
+ return valobj.GetValueFormat();
+ })));
+ }
+
+ ~CommandObjectTypeFormat() override = default;
+};
+
+#ifndef LLDB_DISABLE_PYTHON
+
+class CommandObjectTypeSynth : public CommandObjectMultiword {
+public:
+ CommandObjectTypeSynth(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "type synthetic",
+ "Commands for operating on synthetic type representations.",
+ "type synthetic [<sub-command-options>] ") {
+ LoadSubCommand("add",
+ CommandObjectSP(new CommandObjectTypeSynthAdd(interpreter)));
+ LoadSubCommand(
+ "clear", CommandObjectSP(new CommandObjectTypeSynthClear(interpreter)));
+ LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeSynthDelete(
+ interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectTypeSynthList(interpreter)));
+ LoadSubCommand(
+ "info",
+ CommandObjectSP(new CommandObjectFormatterInfo<SyntheticChildren>(
+ interpreter, "synthetic",
+ [](ValueObject &valobj) -> SyntheticChildren::SharedPointer {
+ return valobj.GetSyntheticChildren();
+ })));
+ }
+
+ ~CommandObjectTypeSynth() override = default;
+};
+
+#endif // LLDB_DISABLE_PYTHON
+
+class CommandObjectTypeFilter : public CommandObjectMultiword {
+public:
+ CommandObjectTypeFilter(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "type filter",
+ "Commands for operating on type filters.",
+ "type synthetic [<sub-command-options>] ") {
+ LoadSubCommand(
+ "add", CommandObjectSP(new CommandObjectTypeFilterAdd(interpreter)));
+ LoadSubCommand("clear", CommandObjectSP(
+ new CommandObjectTypeFilterClear(interpreter)));
+ LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeFilterDelete(
+ interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectTypeFilterList(interpreter)));
+ }
+
+ ~CommandObjectTypeFilter() override = default;
+};
+
+class CommandObjectTypeCategory : public CommandObjectMultiword {
+public:
+ CommandObjectTypeCategory(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "type category",
+ "Commands for operating on type categories.",
+ "type category [<sub-command-options>] ") {
+ LoadSubCommand(
+ "define",
+ CommandObjectSP(new CommandObjectTypeCategoryDefine(interpreter)));
+ LoadSubCommand(
+ "enable",
+ CommandObjectSP(new CommandObjectTypeCategoryEnable(interpreter)));
+ LoadSubCommand(
+ "disable",
+ CommandObjectSP(new CommandObjectTypeCategoryDisable(interpreter)));
+ LoadSubCommand(
+ "delete",
+ CommandObjectSP(new CommandObjectTypeCategoryDelete(interpreter)));
+ LoadSubCommand("list", CommandObjectSP(
+ new CommandObjectTypeCategoryList(interpreter)));
+ }
+
+ ~CommandObjectTypeCategory() override = default;
+};
+
+class CommandObjectTypeSummary : public CommandObjectMultiword {
+public:
+ CommandObjectTypeSummary(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "type summary",
+ "Commands for editing variable summary display options.",
+ "type summary [<sub-command-options>] ") {
+ LoadSubCommand(
+ "add", CommandObjectSP(new CommandObjectTypeSummaryAdd(interpreter)));
+ LoadSubCommand("clear", CommandObjectSP(new CommandObjectTypeSummaryClear(
+ interpreter)));
+ LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeSummaryDelete(
+ interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectTypeSummaryList(interpreter)));
+ LoadSubCommand(
+ "info", CommandObjectSP(new CommandObjectFormatterInfo<TypeSummaryImpl>(
+ interpreter, "summary",
+ [](ValueObject &valobj) -> TypeSummaryImpl::SharedPointer {
+ return valobj.GetSummaryFormat();
+ })));
+ }
+
+ ~CommandObjectTypeSummary() override = default;
+};
+
+// CommandObjectType
+
+CommandObjectType::CommandObjectType(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "type",
+ "Commands for operating on the type system.",
+ "type [<sub-command-options>]") {
+ LoadSubCommand("category",
+ CommandObjectSP(new CommandObjectTypeCategory(interpreter)));
+ LoadSubCommand("filter",
+ CommandObjectSP(new CommandObjectTypeFilter(interpreter)));
+ LoadSubCommand("format",
+ CommandObjectSP(new CommandObjectTypeFormat(interpreter)));
+ LoadSubCommand("summary",
+ CommandObjectSP(new CommandObjectTypeSummary(interpreter)));
+#ifndef LLDB_DISABLE_PYTHON
+ LoadSubCommand("synthetic",
+ CommandObjectSP(new CommandObjectTypeSynth(interpreter)));
+#endif // LLDB_DISABLE_PYTHON
+ LoadSubCommand("lookup",
+ CommandObjectSP(new CommandObjectTypeLookup(interpreter)));
+}
+
+CommandObjectType::~CommandObjectType() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectType.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectType.h
new file mode 100644
index 000000000000..ebb19039e503
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectType.h
@@ -0,0 +1,29 @@
+//===-- CommandObjectType.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectType_h_
+#define liblldb_CommandObjectType_h_
+
+
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+class CommandObjectType : public CommandObjectMultiword {
+public:
+ CommandObjectType(CommandInterpreter &interpreter);
+
+ ~CommandObjectType() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectType_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.cpp
new file mode 100644
index 000000000000..904baf5b7d44
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.cpp
@@ -0,0 +1,35 @@
+//===-- CommandObjectVersion.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectVersion.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectVersion
+
+CommandObjectVersion::CommandObjectVersion(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "version",
+ "Show the LLDB debugger version.", "version") {}
+
+CommandObjectVersion::~CommandObjectVersion() {}
+
+bool CommandObjectVersion::DoExecute(Args &args, CommandReturnObject &result) {
+ if (args.GetArgumentCount() == 0) {
+ result.AppendMessageWithFormat("%s\n", lldb_private::GetVersion());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendError("the version command takes no arguments.");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.h
new file mode 100644
index 000000000000..30f44aeb1658
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectVersion.h
@@ -0,0 +1,30 @@
+//===-- CommandObjectVersion.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectVersion_h_
+#define liblldb_CommandObjectVersion_h_
+
+#include "lldb/Interpreter/CommandObject.h"
+
+namespace lldb_private {
+
+// CommandObjectVersion
+
+class CommandObjectVersion : public CommandObjectParsed {
+public:
+ CommandObjectVersion(CommandInterpreter &interpreter);
+
+ ~CommandObjectVersion() override;
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectVersion_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.cpp
new file mode 100644
index 000000000000..98e758b7ef6a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.cpp
@@ -0,0 +1,1172 @@
+//===-- CommandObjectWatchpoint.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectWatchpoint.h"
+#include "CommandObjectWatchpointCommand.h"
+
+#include <vector>
+
+#include "llvm/ADT/StringRef.h"
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Breakpoint/WatchpointList.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void AddWatchpointDescription(Stream *s, Watchpoint *wp,
+ lldb::DescriptionLevel level) {
+ s->IndentMore();
+ wp->GetDescription(s, level);
+ s->IndentLess();
+ s->EOL();
+}
+
+static bool CheckTargetForWatchpointOperations(Target *target,
+ CommandReturnObject &result) {
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No existing target or watchpoints.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ bool process_is_valid =
+ target->GetProcessSP() && target->GetProcessSP()->IsAlive();
+ if (!process_is_valid) {
+ result.AppendError("Thre's no process or it is not alive.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ // Target passes our checks, return true.
+ return true;
+}
+
+// Equivalent class: {"-", "to", "To", "TO"} of range specifier array.
+static const char *RSA[4] = {"-", "to", "To", "TO"};
+
+// Return the index to RSA if found; otherwise -1 is returned.
+static int32_t WithRSAIndex(llvm::StringRef Arg) {
+
+ uint32_t i;
+ for (i = 0; i < 4; ++i)
+ if (Arg.find(RSA[i]) != llvm::StringRef::npos)
+ return i;
+ return -1;
+}
+
+// Return true if wp_ids is successfully populated with the watch ids. False
+// otherwise.
+bool CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ Target *target, Args &args, std::vector<uint32_t> &wp_ids) {
+ // Pre-condition: args.GetArgumentCount() > 0.
+ if (args.GetArgumentCount() == 0) {
+ if (target == nullptr)
+ return false;
+ WatchpointSP watch_sp = target->GetLastCreatedWatchpoint();
+ if (watch_sp) {
+ wp_ids.push_back(watch_sp->GetID());
+ return true;
+ } else
+ return false;
+ }
+
+ llvm::StringRef Minus("-");
+ std::vector<llvm::StringRef> StrRefArgs;
+ llvm::StringRef first;
+ llvm::StringRef second;
+ size_t i;
+ int32_t idx;
+ // Go through the arguments and make a canonical form of arg list containing
+ // only numbers with possible "-" in between.
+ for (auto &entry : args.entries()) {
+ if ((idx = WithRSAIndex(entry.ref)) == -1) {
+ StrRefArgs.push_back(entry.ref);
+ continue;
+ }
+ // The Arg contains the range specifier, split it, then.
+ std::tie(first, second) = entry.ref.split(RSA[idx]);
+ if (!first.empty())
+ StrRefArgs.push_back(first);
+ StrRefArgs.push_back(Minus);
+ if (!second.empty())
+ StrRefArgs.push_back(second);
+ }
+ // Now process the canonical list and fill in the vector of uint32_t's. If
+ // there is any error, return false and the client should ignore wp_ids.
+ uint32_t beg, end, id;
+ size_t size = StrRefArgs.size();
+ bool in_range = false;
+ for (i = 0; i < size; ++i) {
+ llvm::StringRef Arg = StrRefArgs[i];
+ if (in_range) {
+ // Look for the 'end' of the range. Note StringRef::getAsInteger()
+ // returns true to signify error while parsing.
+ if (Arg.getAsInteger(0, end))
+ return false;
+ // Found a range! Now append the elements.
+ for (id = beg; id <= end; ++id)
+ wp_ids.push_back(id);
+ in_range = false;
+ continue;
+ }
+ if (i < (size - 1) && StrRefArgs[i + 1] == Minus) {
+ if (Arg.getAsInteger(0, beg))
+ return false;
+ // Turn on the in_range flag, we are looking for end of range next.
+ ++i;
+ in_range = true;
+ continue;
+ }
+ // Otherwise, we have a simple ID. Just append it.
+ if (Arg.getAsInteger(0, beg))
+ return false;
+ wp_ids.push_back(beg);
+ }
+
+ // It is an error if after the loop, we're still in_range.
+ return !in_range;
+}
+
+// CommandObjectWatchpointList
+
+// CommandObjectWatchpointList::Options
+#pragma mark List::CommandOptions
+
+static constexpr OptionDefinition g_watchpoint_list_options[] = {
+#define LLDB_OPTIONS_watchpoint_list
+#include "CommandOptions.inc"
+};
+
+#pragma mark List
+
+class CommandObjectWatchpointList : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "watchpoint list",
+ "List all watchpoints at configurable levels of detail.", nullptr),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(),
+ m_level(lldb::eDescriptionLevelBrief) // Watchpoint List defaults to
+ // brief descriptions
+ {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'b':
+ m_level = lldb::eDescriptionLevelBrief;
+ break;
+ case 'f':
+ m_level = lldb::eDescriptionLevelFull;
+ break;
+ case 'v':
+ m_level = lldb::eDescriptionLevelVerbose;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_level = lldb::eDescriptionLevelFull;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_watchpoint_list_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ lldb::DescriptionLevel m_level;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (target == nullptr) {
+ result.AppendError("Invalid target. No current target or watchpoints.");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ if (target->GetProcessSP() && target->GetProcessSP()->IsAlive()) {
+ uint32_t num_supported_hardware_watchpoints;
+ Status error = target->GetProcessSP()->GetWatchpointSupportInfo(
+ num_supported_hardware_watchpoints);
+ if (error.Success())
+ result.AppendMessageWithFormat(
+ "Number of supported hardware watchpoints: %u\n",
+ num_supported_hardware_watchpoints);
+ }
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendMessage("No watchpoints currently set.");
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ Stream &output_stream = result.GetOutputStream();
+
+ if (command.GetArgumentCount() == 0) {
+ // No watchpoint selected; show info about all currently set watchpoints.
+ result.AppendMessage("Current watchpoints:");
+ for (size_t i = 0; i < num_watchpoints; ++i) {
+ Watchpoint *wp = watchpoints.GetByIndex(i).get();
+ AddWatchpointDescription(&output_stream, wp, m_options.m_level);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular watchpoints selected; enable them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ target, command, wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i) {
+ Watchpoint *wp = watchpoints.FindByID(wp_ids[i]).get();
+ if (wp)
+ AddWatchpointDescription(&output_stream, wp, m_options.m_level);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectWatchpointEnable
+#pragma mark Enable
+
+class CommandObjectWatchpointEnable : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointEnable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "enable",
+ "Enable the specified disabled watchpoint(s). If "
+ "no watchpoints are specified, enable all of them.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointEnable() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return false;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to be enabled.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ // No watchpoint selected; enable all currently set watchpoints.
+ target->EnableAllWatchpoints();
+ result.AppendMessageWithFormat("All watchpoints enabled. (%" PRIu64
+ " watchpoints)\n",
+ (uint64_t)num_watchpoints);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular watchpoints selected; enable them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ target, command, wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ int count = 0;
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i)
+ if (target->EnableWatchpointByID(wp_ids[i]))
+ ++count;
+ result.AppendMessageWithFormat("%d watchpoints enabled.\n", count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectWatchpointDisable
+#pragma mark Disable
+
+class CommandObjectWatchpointDisable : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointDisable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "watchpoint disable",
+ "Disable the specified watchpoint(s) without "
+ "removing it/them. If no watchpoints are "
+ "specified, disable them all.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointDisable() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return false;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to be disabled.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ // No watchpoint selected; disable all currently set watchpoints.
+ if (target->DisableAllWatchpoints()) {
+ result.AppendMessageWithFormat("All watchpoints disabled. (%" PRIu64
+ " watchpoints)\n",
+ (uint64_t)num_watchpoints);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("Disable all watchpoints failed\n");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ // Particular watchpoints selected; disable them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ target, command, wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ int count = 0;
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i)
+ if (target->DisableWatchpointByID(wp_ids[i]))
+ ++count;
+ result.AppendMessageWithFormat("%d watchpoints disabled.\n", count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectWatchpointDelete
+#pragma mark Delete
+
+class CommandObjectWatchpointDelete : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "watchpoint delete",
+ "Delete the specified watchpoint(s). If no "
+ "watchpoints are specified, delete them all.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointDelete() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return false;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to be deleted.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ if (!m_interpreter.Confirm(
+ "About to delete all watchpoints, do you want to do that?",
+ true)) {
+ result.AppendMessage("Operation cancelled...");
+ } else {
+ target->RemoveAllWatchpoints();
+ result.AppendMessageWithFormat("All watchpoints removed. (%" PRIu64
+ " watchpoints)\n",
+ (uint64_t)num_watchpoints);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular watchpoints selected; delete them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ target, command, wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ int count = 0;
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i)
+ if (target->RemoveWatchpointByID(wp_ids[i]))
+ ++count;
+ result.AppendMessageWithFormat("%d watchpoints deleted.\n", count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectWatchpointIgnore
+
+#pragma mark Ignore::CommandOptions
+static constexpr OptionDefinition g_watchpoint_ignore_options[] = {
+#define LLDB_OPTIONS_watchpoint_ignore
+#include "CommandOptions.inc"
+};
+
+class CommandObjectWatchpointIgnore : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointIgnore(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "watchpoint ignore",
+ "Set ignore count on the specified watchpoint(s). "
+ "If no watchpoints are specified, set them all.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointIgnore() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_ignore_count(0) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'i':
+ if (option_arg.getAsInteger(0, m_ignore_count))
+ error.SetErrorStringWithFormat("invalid ignore count '%s'",
+ option_arg.str().c_str());
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_ignore_count = 0;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_watchpoint_ignore_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ uint32_t m_ignore_count;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return false;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to be ignored.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ target->IgnoreAllWatchpoints(m_options.m_ignore_count);
+ result.AppendMessageWithFormat("All watchpoints ignored. (%" PRIu64
+ " watchpoints)\n",
+ (uint64_t)num_watchpoints);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular watchpoints selected; ignore them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ target, command, wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ int count = 0;
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i)
+ if (target->IgnoreWatchpointByID(wp_ids[i], m_options.m_ignore_count))
+ ++count;
+ result.AppendMessageWithFormat("%d watchpoints ignored.\n", count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectWatchpointModify
+
+#pragma mark Modify::CommandOptions
+
+static constexpr OptionDefinition g_watchpoint_modify_options[] = {
+#define LLDB_OPTIONS_watchpoint_modify
+#include "CommandOptions.inc"
+};
+
+#pragma mark Modify
+
+class CommandObjectWatchpointModify : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointModify(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "watchpoint modify",
+ "Modify the options on a watchpoint or set of watchpoints in the "
+ "executable. "
+ "If no watchpoint is specified, act on the last created "
+ "watchpoint. "
+ "Passing an empty argument clears the modification.",
+ nullptr),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
+ eArgTypeWatchpointIDRange);
+ // Add the entry for the first argument for this command to the object's
+ // arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointModify() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_condition(), m_condition_passed(false) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c':
+ m_condition = option_arg;
+ m_condition_passed = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_condition.clear();
+ m_condition_passed = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_watchpoint_modify_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ std::string m_condition;
+ bool m_condition_passed;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ if (!CheckTargetForWatchpointOperations(target, result))
+ return false;
+
+ std::unique_lock<std::recursive_mutex> lock;
+ target->GetWatchpointList().GetListMutex(lock);
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to be modified.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ WatchpointSP wp_sp = target->GetLastCreatedWatchpoint();
+ wp_sp->SetCondition(m_options.m_condition.c_str());
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ // Particular watchpoints selected; set condition on them.
+ std::vector<uint32_t> wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
+ target, command, wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ int count = 0;
+ const size_t size = wp_ids.size();
+ for (size_t i = 0; i < size; ++i) {
+ WatchpointSP wp_sp = watchpoints.FindByID(wp_ids[i]);
+ if (wp_sp) {
+ wp_sp->SetCondition(m_options.m_condition.c_str());
+ ++count;
+ }
+ }
+ result.AppendMessageWithFormat("%d watchpoints modified.\n", count);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectWatchpointSetVariable
+#pragma mark SetVariable
+
+class CommandObjectWatchpointSetVariable : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointSetVariable(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "watchpoint set variable",
+ "Set a watchpoint on a variable. "
+ "Use the '-w' option to specify the type of watchpoint and "
+ "the '-s' option to specify the byte size to watch for. "
+ "If no '-w' option is specified, it defaults to write. "
+ "If no '-s' option is specified, it defaults to the variable's "
+ "byte size. "
+ "Note that there are limited hardware resources for watchpoints. "
+ "If watchpoint setting fails, consider disable/delete existing "
+ "ones "
+ "to free up resources.",
+ nullptr,
+ eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+ m_option_group(), m_option_watchpoint() {
+ SetHelpLong(
+ R"(
+Examples:
+
+(lldb) watchpoint set variable -w read_write my_global_var
+
+)"
+ " Watches my_global_var for read/write access, with the region to watch \
+corresponding to the byte size of the data type.");
+
+ CommandArgumentEntry arg;
+ CommandArgumentData var_name_arg;
+
+ // Define the only variant of this arg.
+ var_name_arg.arg_type = eArgTypeVarName;
+ var_name_arg.arg_repetition = eArgRepeatPlain;
+
+ // Push the variant into the argument entry.
+ arg.push_back(var_name_arg);
+
+ // Push the data for the only argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ // Absorb the '-w' and '-s' options into our option group.
+ m_option_group.Append(&m_option_watchpoint, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectWatchpointSetVariable() override = default;
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ static size_t GetVariableCallback(void *baton, const char *name,
+ VariableList &variable_list) {
+ Target *target = static_cast<Target *>(baton);
+ if (target) {
+ return target->GetImages().FindGlobalVariables(ConstString(name),
+ UINT32_MAX, variable_list);
+ }
+ return 0;
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+
+ // If no argument is present, issue an error message. There's no way to
+ // set a watchpoint.
+ if (command.GetArgumentCount() <= 0) {
+ result.GetErrorStream().Printf("error: required argument missing; "
+ "specify your program variable to watch "
+ "for\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // If no '-w' is specified, default to '-w write'.
+ if (!m_option_watchpoint.watch_type_specified) {
+ m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchWrite;
+ }
+
+ // We passed the sanity check for the command. Proceed to set the
+ // watchpoint now.
+ lldb::addr_t addr = 0;
+ size_t size = 0;
+
+ VariableSP var_sp;
+ ValueObjectSP valobj_sp;
+ Stream &output_stream = result.GetOutputStream();
+
+ // A simple watch variable gesture allows only one argument.
+ if (command.GetArgumentCount() != 1) {
+ result.GetErrorStream().Printf(
+ "error: specify exactly one variable to watch for\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Things have checked out ok...
+ Status error;
+ uint32_t expr_path_options =
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess;
+ valobj_sp = frame->GetValueForVariableExpressionPath(
+ command.GetArgumentAtIndex(0), eNoDynamicValues, expr_path_options,
+ var_sp, error);
+
+ if (!valobj_sp) {
+ // Not in the frame; let's check the globals.
+
+ VariableList variable_list;
+ ValueObjectList valobj_list;
+
+ Status error(Variable::GetValuesForVariableExpressionPath(
+ command.GetArgumentAtIndex(0),
+ m_exe_ctx.GetBestExecutionContextScope(), GetVariableCallback, target,
+ variable_list, valobj_list));
+
+ if (valobj_list.GetSize())
+ valobj_sp = valobj_list.GetValueObjectAtIndex(0);
+ }
+
+ CompilerType compiler_type;
+
+ if (valobj_sp) {
+ AddressType addr_type;
+ addr = valobj_sp->GetAddressOf(false, &addr_type);
+ if (addr_type == eAddressTypeLoad) {
+ // We're in business.
+ // Find out the size of this variable.
+ size = m_option_watchpoint.watch_size == 0
+ ? valobj_sp->GetByteSize()
+ : m_option_watchpoint.watch_size;
+ }
+ compiler_type = valobj_sp->GetCompilerType();
+ } else {
+ const char *error_cstr = error.AsCString(nullptr);
+ if (error_cstr)
+ result.GetErrorStream().Printf("error: %s\n", error_cstr);
+ else
+ result.GetErrorStream().Printf("error: unable to find any variable "
+ "expression path that matches '%s'\n",
+ command.GetArgumentAtIndex(0));
+ return false;
+ }
+
+ // Now it's time to create the watchpoint.
+ uint32_t watch_type = m_option_watchpoint.watch_type;
+
+ error.Clear();
+ Watchpoint *wp =
+ target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error)
+ .get();
+ if (wp) {
+ wp->SetWatchSpec(command.GetArgumentAtIndex(0));
+ wp->SetWatchVariable(true);
+ if (var_sp && var_sp->GetDeclaration().GetFile()) {
+ StreamString ss;
+ // True to show fullpath for declaration file.
+ var_sp->GetDeclaration().DumpStopContext(&ss, true);
+ wp->SetDeclInfo(ss.GetString());
+ }
+ output_stream.Printf("Watchpoint created: ");
+ wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
+ output_stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Watchpoint creation failed (addr=0x%" PRIx64 ", size=%" PRIu64
+ ", variable expression='%s').\n",
+ addr, (uint64_t)size, command.GetArgumentAtIndex(0));
+ if (error.AsCString(nullptr))
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ OptionGroupOptions m_option_group;
+ OptionGroupWatchpoint m_option_watchpoint;
+};
+
+// CommandObjectWatchpointSetExpression
+#pragma mark Set
+
+class CommandObjectWatchpointSetExpression : public CommandObjectRaw {
+public:
+ CommandObjectWatchpointSetExpression(CommandInterpreter &interpreter)
+ : CommandObjectRaw(
+ interpreter, "watchpoint set expression",
+ "Set a watchpoint on an address by supplying an expression. "
+ "Use the '-w' option to specify the type of watchpoint and "
+ "the '-s' option to specify the byte size to watch for. "
+ "If no '-w' option is specified, it defaults to write. "
+ "If no '-s' option is specified, it defaults to the target's "
+ "pointer byte size. "
+ "Note that there are limited hardware resources for watchpoints. "
+ "If watchpoint setting fails, consider disable/delete existing "
+ "ones "
+ "to free up resources.",
+ "",
+ eCommandRequiresFrame | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+ m_option_group(), m_option_watchpoint() {
+ SetHelpLong(
+ R"(
+Examples:
+
+(lldb) watchpoint set expression -w write -s 1 -- foo + 32
+
+ Watches write access for the 1-byte region pointed to by the address 'foo + 32')");
+
+ CommandArgumentEntry arg;
+ CommandArgumentData expression_arg;
+
+ // Define the only variant of this arg.
+ expression_arg.arg_type = eArgTypeExpression;
+ expression_arg.arg_repetition = eArgRepeatPlain;
+
+ // Push the only variant into the argument entry.
+ arg.push_back(expression_arg);
+
+ // Push the data for the only argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+
+ // Absorb the '-w' and '-s' options into our option group.
+ m_option_group.Append(&m_option_watchpoint, LLDB_OPT_SET_ALL,
+ LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectWatchpointSetExpression() override = default;
+
+ // Overrides base class's behavior where WantsCompletion =
+ // !WantsRawCommandString.
+ bool WantsCompletion() override { return true; }
+
+ Options *GetOptions() override { return &m_option_group; }
+
+protected:
+ bool DoExecute(llvm::StringRef raw_command,
+ CommandReturnObject &result) override {
+ auto exe_ctx = GetCommandInterpreter().GetExecutionContext();
+ m_option_group.NotifyOptionParsingStarting(
+ &exe_ctx); // This is a raw command, so notify the option group
+
+ Target *target = GetDebugger().GetSelectedTarget().get();
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+
+ OptionsWithRaw args(raw_command);
+
+ llvm::StringRef expr = args.GetRawPart();
+
+ if (args.HasArgs())
+ if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group,
+ exe_ctx))
+ return false;
+
+ // If no argument is present, issue an error message. There's no way to
+ // set a watchpoint.
+ if (raw_command.trim().empty()) {
+ result.GetErrorStream().Printf("error: required argument missing; "
+ "specify an expression to evaulate into "
+ "the address to watch for\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // If no '-w' is specified, default to '-w write'.
+ if (!m_option_watchpoint.watch_type_specified) {
+ m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchWrite;
+ }
+
+ // We passed the sanity check for the command. Proceed to set the
+ // watchpoint now.
+ lldb::addr_t addr = 0;
+ size_t size = 0;
+
+ ValueObjectSP valobj_sp;
+
+ // Use expression evaluation to arrive at the address to watch.
+ EvaluateExpressionOptions options;
+ options.SetCoerceToId(false);
+ options.SetUnwindOnError(true);
+ options.SetKeepInMemory(false);
+ options.SetTryAllThreads(true);
+ options.SetTimeout(llvm::None);
+
+ ExpressionResults expr_result =
+ target->EvaluateExpression(expr, frame, valobj_sp, options);
+ if (expr_result != eExpressionCompleted) {
+ result.GetErrorStream().Printf(
+ "error: expression evaluation of address to watch failed\n");
+ result.GetErrorStream() << "expression evaluated: \n" << expr << "\n";
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Get the address to watch.
+ bool success = false;
+ addr = valobj_sp->GetValueAsUnsigned(0, &success);
+ if (!success) {
+ result.GetErrorStream().Printf(
+ "error: expression did not evaluate to an address\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_option_watchpoint.watch_size != 0)
+ size = m_option_watchpoint.watch_size;
+ else
+ size = target->GetArchitecture().GetAddressByteSize();
+
+ // Now it's time to create the watchpoint.
+ uint32_t watch_type = m_option_watchpoint.watch_type;
+
+ // Fetch the type from the value object, the type of the watched object is
+ // the pointee type
+ /// of the expression, so convert to that if we found a valid type.
+ CompilerType compiler_type(valobj_sp->GetCompilerType());
+
+ Status error;
+ Watchpoint *wp =
+ target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error)
+ .get();
+ if (wp) {
+ Stream &output_stream = result.GetOutputStream();
+ output_stream.Printf("Watchpoint created: ");
+ wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
+ output_stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%" PRIx64
+ ", size=%" PRIu64 ").\n",
+ addr, (uint64_t)size);
+ if (error.AsCString(nullptr))
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ OptionGroupOptions m_option_group;
+ OptionGroupWatchpoint m_option_watchpoint;
+};
+
+// CommandObjectWatchpointSet
+#pragma mark Set
+
+class CommandObjectWatchpointSet : public CommandObjectMultiword {
+public:
+ CommandObjectWatchpointSet(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "watchpoint set", "Commands for setting a watchpoint.",
+ "watchpoint set <subcommand> [<subcommand-options>]") {
+
+ LoadSubCommand(
+ "variable",
+ CommandObjectSP(new CommandObjectWatchpointSetVariable(interpreter)));
+ LoadSubCommand(
+ "expression",
+ CommandObjectSP(new CommandObjectWatchpointSetExpression(interpreter)));
+ }
+
+ ~CommandObjectWatchpointSet() override = default;
+};
+
+// CommandObjectMultiwordWatchpoint
+#pragma mark MultiwordWatchpoint
+
+CommandObjectMultiwordWatchpoint::CommandObjectMultiwordWatchpoint(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "watchpoint",
+ "Commands for operating on watchpoints.",
+ "watchpoint <subcommand> [<command-options>]") {
+ CommandObjectSP list_command_object(
+ new CommandObjectWatchpointList(interpreter));
+ CommandObjectSP enable_command_object(
+ new CommandObjectWatchpointEnable(interpreter));
+ CommandObjectSP disable_command_object(
+ new CommandObjectWatchpointDisable(interpreter));
+ CommandObjectSP delete_command_object(
+ new CommandObjectWatchpointDelete(interpreter));
+ CommandObjectSP ignore_command_object(
+ new CommandObjectWatchpointIgnore(interpreter));
+ CommandObjectSP command_command_object(
+ new CommandObjectWatchpointCommand(interpreter));
+ CommandObjectSP modify_command_object(
+ new CommandObjectWatchpointModify(interpreter));
+ CommandObjectSP set_command_object(
+ new CommandObjectWatchpointSet(interpreter));
+
+ list_command_object->SetCommandName("watchpoint list");
+ enable_command_object->SetCommandName("watchpoint enable");
+ disable_command_object->SetCommandName("watchpoint disable");
+ delete_command_object->SetCommandName("watchpoint delete");
+ ignore_command_object->SetCommandName("watchpoint ignore");
+ command_command_object->SetCommandName("watchpoint command");
+ modify_command_object->SetCommandName("watchpoint modify");
+ set_command_object->SetCommandName("watchpoint set");
+
+ LoadSubCommand("list", list_command_object);
+ LoadSubCommand("enable", enable_command_object);
+ LoadSubCommand("disable", disable_command_object);
+ LoadSubCommand("delete", delete_command_object);
+ LoadSubCommand("ignore", ignore_command_object);
+ LoadSubCommand("command", command_command_object);
+ LoadSubCommand("modify", modify_command_object);
+ LoadSubCommand("set", set_command_object);
+}
+
+CommandObjectMultiwordWatchpoint::~CommandObjectMultiwordWatchpoint() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.h
new file mode 100644
index 000000000000..f21796e6bc8d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpoint.h
@@ -0,0 +1,33 @@
+//===-- CommandObjectWatchpoint.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectWatchpoint_h_
+#define liblldb_CommandObjectWatchpoint_h_
+
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/OptionGroupWatchpoint.h"
+#include "lldb/Interpreter/Options.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordWatchpoint
+
+class CommandObjectMultiwordWatchpoint : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordWatchpoint(CommandInterpreter &interpreter);
+
+ ~CommandObjectMultiwordWatchpoint() override;
+
+ static bool VerifyWatchpointIDs(Target *target, Args &args,
+ std::vector<uint32_t> &wp_ids);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectWatchpoint_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
new file mode 100644
index 000000000000..2be0b5b154e0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
@@ -0,0 +1,678 @@
+//===-- CommandObjectWatchpointCommand.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <vector>
+
+#include "CommandObjectWatchpoint.h"
+#include "CommandObjectWatchpointCommand.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectWatchpointCommandAdd
+
+// FIXME: "script-type" needs to have its contents determined dynamically, so
+// somebody can add a new scripting
+// language to lldb and have it pickable here without having to change this
+// enumeration by hand and rebuild lldb proper.
+
+static constexpr OptionEnumValueElement g_script_option_enumeration[] = {
+ {eScriptLanguageNone, "command",
+ "Commands are in the lldb command interpreter language"},
+ {eScriptLanguagePython, "python", "Commands are in the Python language."},
+ {eSortOrderByName, "default-script",
+ "Commands are in the default scripting language."} };
+
+static constexpr OptionEnumValues ScriptOptionEnum() {
+ return OptionEnumValues(g_script_option_enumeration);
+}
+
+static constexpr OptionDefinition g_watchpoint_command_add_options[] = {
+#define LLDB_OPTIONS_watchpoint_command_add
+#include "CommandOptions.inc"
+};
+
+class CommandObjectWatchpointCommandAdd : public CommandObjectParsed,
+ public IOHandlerDelegateMultiline {
+public:
+ CommandObjectWatchpointCommandAdd(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "add",
+ "Add a set of LLDB commands to a watchpoint, to be "
+ "executed whenever the watchpoint is hit.",
+ nullptr),
+ IOHandlerDelegateMultiline("DONE",
+ IOHandlerDelegate::Completion::LLDBCommand),
+ m_options() {
+ SetHelpLong(
+ R"(
+General information about entering watchpoint commands
+------------------------------------------------------
+
+)"
+ "This command will prompt for commands to be executed when the specified \
+watchpoint is hit. Each command is typed on its own line following the '> ' \
+prompt until 'DONE' is entered."
+ R"(
+
+)"
+ "Syntactic errors may not be detected when initially entered, and many \
+malformed commands can silently fail when executed. If your watchpoint commands \
+do not appear to be executing, double-check the command syntax."
+ R"(
+
+)"
+ "Note: You may enter any debugger command exactly as you would at the debugger \
+prompt. There is no limit to the number of commands supplied, but do NOT enter \
+more than one command per line."
+ R"(
+
+Special information about PYTHON watchpoint commands
+----------------------------------------------------
+
+)"
+ "You may enter either one or more lines of Python, including function \
+definitions or calls to functions that will have been imported by the time \
+the code executes. Single line watchpoint commands will be interpreted 'as is' \
+when the watchpoint is hit. Multiple lines of Python will be wrapped in a \
+generated function, and a call to the function will be attached to the watchpoint."
+ R"(
+
+This auto-generated function is passed in three arguments:
+
+ frame: an lldb.SBFrame object for the frame which hit the watchpoint.
+
+ wp: the watchpoint that was hit.
+
+)"
+ "When specifying a python function with the --python-function option, you need \
+to supply the function name prepended by the module name:"
+ R"(
+
+ --python-function myutils.watchpoint_callback
+
+The function itself must have the following prototype:
+
+def watchpoint_callback(frame, wp):
+ # Your code goes here
+
+)"
+ "The arguments are the same as the arguments passed to generated functions as \
+described above. Note that the global variable 'lldb.frame' will NOT be updated when \
+this function is called, so be sure to use the 'frame' argument. The 'frame' argument \
+can get you to the thread via frame.GetThread(), the thread can get you to the \
+process via thread.GetProcess(), and the process can get you back to the target \
+via process.GetTarget()."
+ R"(
+
+)"
+ "Important Note: As Python code gets collected into functions, access to global \
+variables requires explicit scoping using the 'global' keyword. Be sure to use correct \
+Python syntax, including indentation, when entering Python watchpoint commands."
+ R"(
+
+Example Python one-line watchpoint command:
+
+(lldb) watchpoint command add -s python 1
+Enter your Python command(s). Type 'DONE' to end.
+> print "Hit this watchpoint!"
+> DONE
+
+As a convenience, this also works for a short Python one-liner:
+
+(lldb) watchpoint command add -s python 1 -o 'import time; print time.asctime()'
+(lldb) run
+Launching '.../a.out' (x86_64)
+(lldb) Fri Sep 10 12:17:45 2010
+Process 21778 Stopped
+* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = watchpoint 1.1, queue = com.apple.main-thread
+ 36
+ 37 int c(int val)
+ 38 {
+ 39 -> return val + 3;
+ 40 }
+ 41
+ 42 int main (int argc, char const *argv[])
+
+Example multiple line Python watchpoint command, using function definition:
+
+(lldb) watchpoint command add -s python 1
+Enter your Python command(s). Type 'DONE' to end.
+> def watchpoint_output (wp_no):
+> out_string = "Hit watchpoint number " + repr (wp_no)
+> print out_string
+> return True
+> watchpoint_output (1)
+> DONE
+
+Example multiple line Python watchpoint command, using 'loose' Python:
+
+(lldb) watchpoint command add -s p 1
+Enter your Python command(s). Type 'DONE' to end.
+> global wp_count
+> wp_count = wp_count + 1
+> print "Hit this watchpoint " + repr(wp_count) + " times!"
+> DONE
+
+)"
+ "In this case, since there is a reference to a global variable, \
+'wp_count', you will also need to make sure 'wp_count' exists and is \
+initialized:"
+ R"(
+
+(lldb) script
+>>> wp_count = 0
+>>> quit()
+
+)"
+ "Final Note: A warning that no watchpoint command was generated when there \
+are no syntax errors may indicate that a function was declared but never called.");
+
+ CommandArgumentEntry arg;
+ CommandArgumentData wp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ wp_id_arg.arg_type = eArgTypeWatchpointID;
+ wp_id_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(wp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointCommandAdd() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ if (output_sp && interactive) {
+ output_sp->PutCString(
+ "Enter your debugger command(s). Type 'DONE' to end.\n");
+ output_sp->Flush();
+ }
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) override {
+ io_handler.SetIsDone(true);
+
+ // The WatchpointOptions object is owned by the watchpoint or watchpoint
+ // location
+ WatchpointOptions *wp_options =
+ (WatchpointOptions *)io_handler.GetUserData();
+ if (wp_options) {
+ std::unique_ptr<WatchpointOptions::CommandData> data_up(
+ new WatchpointOptions::CommandData());
+ if (data_up) {
+ data_up->user_source.SplitIntoLines(line);
+ auto baton_sp = std::make_shared<WatchpointOptions::CommandBaton>(
+ std::move(data_up));
+ wp_options->SetCallback(WatchpointOptionsCallbackFunction, baton_sp);
+ }
+ }
+ }
+
+ void CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options,
+ CommandReturnObject &result) {
+ m_interpreter.GetLLDBCommandsFromIOHandler(
+ "> ", // Prompt
+ *this, // IOHandlerDelegate
+ true, // Run IOHandler in async mode
+ wp_options); // Baton for the "io_handler" that will be passed back into
+ // our IOHandlerDelegate functions
+ }
+
+ /// Set a one-liner as the callback for the watchpoint.
+ void SetWatchpointCommandCallback(WatchpointOptions *wp_options,
+ const char *oneliner) {
+ std::unique_ptr<WatchpointOptions::CommandData> data_up(
+ new WatchpointOptions::CommandData());
+
+ // It's necessary to set both user_source and script_source to the
+ // oneliner. The former is used to generate callback description (as in
+ // watchpoint command list) while the latter is used for Python to
+ // interpret during the actual callback.
+ data_up->user_source.AppendString(oneliner);
+ data_up->script_source.assign(oneliner);
+ data_up->stop_on_error = m_options.m_stop_on_error;
+
+ auto baton_sp =
+ std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up));
+ wp_options->SetCallback(WatchpointOptionsCallbackFunction, baton_sp);
+ }
+
+ static bool
+ WatchpointOptionsCallbackFunction(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t watch_id) {
+ bool ret_value = true;
+ if (baton == nullptr)
+ return true;
+
+ WatchpointOptions::CommandData *data =
+ (WatchpointOptions::CommandData *)baton;
+ StringList &commands = data->user_source;
+
+ if (commands.GetSize() > 0) {
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ CommandReturnObject result;
+ Debugger &debugger = target->GetDebugger();
+ // Rig up the results secondary output stream to the debugger's, so the
+ // output will come out synchronously if the debugger is set up that
+ // way.
+
+ StreamSP output_stream(debugger.GetAsyncOutputStream());
+ StreamSP error_stream(debugger.GetAsyncErrorStream());
+ result.SetImmediateOutputStream(output_stream);
+ result.SetImmediateErrorStream(error_stream);
+
+ CommandInterpreterRunOptions options;
+ options.SetStopOnContinue(true);
+ options.SetStopOnError(data->stop_on_error);
+ options.SetEchoCommands(false);
+ options.SetPrintResults(true);
+ options.SetPrintErrors(true);
+ options.SetAddToHistory(false);
+
+ debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx,
+ options, result);
+ result.GetImmediateOutputStream()->Flush();
+ result.GetImmediateErrorStream()->Flush();
+ }
+ }
+ return ret_value;
+ }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(), m_use_commands(false), m_use_script_language(false),
+ m_script_language(eScriptLanguageNone), m_use_one_liner(false),
+ m_one_liner(), m_function_name() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'o':
+ m_use_one_liner = true;
+ m_one_liner = option_arg;
+ break;
+
+ case 's':
+ m_script_language = (lldb::ScriptLanguage)OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values,
+ eScriptLanguageNone, error);
+
+ m_use_script_language = (m_script_language == eScriptLanguagePython ||
+ m_script_language == eScriptLanguageDefault);
+ break;
+
+ case 'e': {
+ bool success = false;
+ m_stop_on_error =
+ OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (!success)
+ error.SetErrorStringWithFormat(
+ "invalid value for stop-on-error: \"%s\"",
+ option_arg.str().c_str());
+ } break;
+
+ case 'F':
+ m_use_one_liner = false;
+ m_use_script_language = true;
+ m_function_name.assign(option_arg);
+ break;
+
+ default:
+ break;
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_use_commands = true;
+ m_use_script_language = false;
+ m_script_language = eScriptLanguageNone;
+
+ m_use_one_liner = false;
+ m_stop_on_error = true;
+ m_one_liner.clear();
+ m_function_name.clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_watchpoint_command_add_options);
+ }
+
+ // Instance variables to hold the values for command options.
+
+ bool m_use_commands;
+ bool m_use_script_language;
+ lldb::ScriptLanguage m_script_language;
+
+ // Instance variables to hold the values for one_liner options.
+ bool m_use_one_liner;
+ std::string m_one_liner;
+ bool m_stop_on_error;
+ std::string m_function_name;
+ };
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+
+ if (target == nullptr) {
+ result.AppendError("There is not a current executable; there are no "
+ "watchpoints to which to add commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to have commands added");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (!m_options.m_use_script_language &&
+ !m_options.m_function_name.empty()) {
+ result.AppendError("need to enable scripting to have a function run as a "
+ "watchpoint command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::vector<uint32_t> valid_wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
+ valid_wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ const size_t count = valid_wp_ids.size();
+ for (size_t i = 0; i < count; ++i) {
+ uint32_t cur_wp_id = valid_wp_ids.at(i);
+ if (cur_wp_id != LLDB_INVALID_WATCH_ID) {
+ Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get();
+ // Sanity check wp first.
+ if (wp == nullptr)
+ continue;
+
+ WatchpointOptions *wp_options = wp->GetOptions();
+ // Skip this watchpoint if wp_options is not good.
+ if (wp_options == nullptr)
+ continue;
+
+ // If we are using script language, get the script interpreter in order
+ // to set or collect command callback. Otherwise, call the methods
+ // associated with this object.
+ if (m_options.m_use_script_language) {
+ // Special handling for one-liner specified inline.
+ if (m_options.m_use_one_liner) {
+ GetDebugger().GetScriptInterpreter()->SetWatchpointCommandCallback(
+ wp_options, m_options.m_one_liner.c_str());
+ }
+ // Special handling for using a Python function by name instead of
+ // extending the watchpoint callback data structures, we just
+ // automatize what the user would do manually: make their watchpoint
+ // command be a function call
+ else if (!m_options.m_function_name.empty()) {
+ std::string oneliner(m_options.m_function_name);
+ oneliner += "(frame, wp, internal_dict)";
+ GetDebugger().GetScriptInterpreter()->SetWatchpointCommandCallback(
+ wp_options, oneliner.c_str());
+ } else {
+ GetDebugger()
+ .GetScriptInterpreter()
+ ->CollectDataForWatchpointCommandCallback(wp_options, result);
+ }
+ } else {
+ // Special handling for one-liner specified inline.
+ if (m_options.m_use_one_liner)
+ SetWatchpointCommandCallback(wp_options,
+ m_options.m_one_liner.c_str());
+ else
+ CollectDataForWatchpointCommandCallback(wp_options, result);
+ }
+ }
+ }
+
+ return result.Succeeded();
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+// CommandObjectWatchpointCommandDelete
+
+class CommandObjectWatchpointCommandDelete : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointCommandDelete(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "delete",
+ "Delete the set of commands from a watchpoint.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData wp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ wp_id_arg.arg_type = eArgTypeWatchpointID;
+ wp_id_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(wp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointCommandDelete() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+
+ if (target == nullptr) {
+ result.AppendError("There is not a current executable; there are no "
+ "watchpoints from which to delete commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist to have commands deleted");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError(
+ "No watchpoint specified from which to delete the commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::vector<uint32_t> valid_wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
+ valid_wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ const size_t count = valid_wp_ids.size();
+ for (size_t i = 0; i < count; ++i) {
+ uint32_t cur_wp_id = valid_wp_ids.at(i);
+ if (cur_wp_id != LLDB_INVALID_WATCH_ID) {
+ Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get();
+ if (wp)
+ wp->ClearCallback();
+ } else {
+ result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", cur_wp_id);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectWatchpointCommandList
+
+class CommandObjectWatchpointCommandList : public CommandObjectParsed {
+public:
+ CommandObjectWatchpointCommandList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "list", "List the script or set of "
+ "commands to be executed when "
+ "the watchpoint is hit.",
+ nullptr) {
+ CommandArgumentEntry arg;
+ CommandArgumentData wp_id_arg;
+
+ // Define the first (and only) variant of this arg.
+ wp_id_arg.arg_type = eArgTypeWatchpointID;
+ wp_id_arg.arg_repetition = eArgRepeatPlain;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(wp_id_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectWatchpointCommandList() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Target *target = GetDebugger().GetSelectedTarget().get();
+
+ if (target == nullptr) {
+ result.AppendError("There is not a current executable; there are no "
+ "watchpoints for which to list commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const WatchpointList &watchpoints = target->GetWatchpointList();
+ size_t num_watchpoints = watchpoints.GetSize();
+
+ if (num_watchpoints == 0) {
+ result.AppendError("No watchpoints exist for which to list commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError(
+ "No watchpoint specified for which to list the commands");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ std::vector<uint32_t> valid_wp_ids;
+ if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
+ valid_wp_ids)) {
+ result.AppendError("Invalid watchpoints specification.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ const size_t count = valid_wp_ids.size();
+ for (size_t i = 0; i < count; ++i) {
+ uint32_t cur_wp_id = valid_wp_ids.at(i);
+ if (cur_wp_id != LLDB_INVALID_WATCH_ID) {
+ Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get();
+
+ if (wp) {
+ const WatchpointOptions *wp_options = wp->GetOptions();
+ if (wp_options) {
+ // Get the callback baton associated with the current watchpoint.
+ const Baton *baton = wp_options->GetBaton();
+ if (baton) {
+ result.GetOutputStream().Printf("Watchpoint %u:\n", cur_wp_id);
+ result.GetOutputStream().IndentMore();
+ baton->GetDescription(&result.GetOutputStream(),
+ eDescriptionLevelFull);
+ result.GetOutputStream().IndentLess();
+ } else {
+ result.AppendMessageWithFormat(
+ "Watchpoint %u does not have an associated command.\n",
+ cur_wp_id);
+ }
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n",
+ cur_wp_id);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ }
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectWatchpointCommand
+
+CommandObjectWatchpointCommand::CommandObjectWatchpointCommand(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "command",
+ "Commands for adding, removing and examining LLDB commands "
+ "executed when the watchpoint is hit (watchpoint 'commands').",
+ "command <sub-command> [<sub-command-options>] <watchpoint-id>") {
+ CommandObjectSP add_command_object(
+ new CommandObjectWatchpointCommandAdd(interpreter));
+ CommandObjectSP delete_command_object(
+ new CommandObjectWatchpointCommandDelete(interpreter));
+ CommandObjectSP list_command_object(
+ new CommandObjectWatchpointCommandList(interpreter));
+
+ add_command_object->SetCommandName("watchpoint command add");
+ delete_command_object->SetCommandName("watchpoint command delete");
+ list_command_object->SetCommandName("watchpoint command list");
+
+ LoadSubCommand("add", add_command_object);
+ LoadSubCommand("delete", delete_command_object);
+ LoadSubCommand("list", list_command_object);
+}
+
+CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand() = default;
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.h b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.h
new file mode 100644
index 000000000000..f2f15ef50b0f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectWatchpointCommand.h
@@ -0,0 +1,31 @@
+//===-- CommandObjectWatchpointCommand.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectWatchpointCommand_h_
+#define liblldb_CommandObjectWatchpointCommand_h_
+
+
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+// CommandObjectMultiwordWatchpoint
+
+class CommandObjectWatchpointCommand : public CommandObjectMultiword {
+public:
+ CommandObjectWatchpointCommand(CommandInterpreter &interpreter);
+
+ ~CommandObjectWatchpointCommand() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectWatchpointCommand_h_
diff --git a/contrib/llvm-project/lldb/source/Commands/Options.td b/contrib/llvm-project/lldb/source/Commands/Options.td
new file mode 100644
index 000000000000..9cfbcd2d4ebf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/Options.td
@@ -0,0 +1,308 @@
+include "OptionsBase.td"
+
+let Command = "target modules dump symtab" in {
+ def tm_sort : Option<"sort", "s">, Group<1>,
+ Desc<"Supply a sort order when dumping the symbol table.">,
+ EnumArg<"SortOrder", "OptionEnumValues(g_sort_option_enumeration)">;
+}
+
+let Command = "help" in {
+ def help_hide_aliases : Option<"hide-aliases", "a">,
+ Desc<"Hide aliases in the command list.">;
+ def help_hide_user : Option<"hide-user-commands", "u">,
+ Desc<"Hide user-defined commands from the list.">;
+ def help_show_hidden : Option<"show-hidden-commands", "h">,
+ Desc<"Include commands prefixed with an underscore.">;
+}
+
+let Command = "settings set" in {
+ def setset_global : Option<"global", "g">, Arg<"Filename">,
+ Completion<"DiskFile">,
+ Desc<"Apply the new value to the global default value.">;
+ def setset_force : Option<"force", "f">,
+ Desc<"Force an empty value to be accepted as the default.">;
+}
+
+let Command = "settings write" in {
+ def setwrite_file : Option<"file", "f">, Required, Arg<"Filename">,
+ Completion<"DiskFile">,
+ Desc<"The file into which to write the settings.">;
+ def setwrite_append : Option<"append", "a">,
+ Desc<"Append to saved settings file if it exists.">;
+}
+
+let Command = "settings read" in {
+ def setread_file : Option<"file", "f">, Required, Arg<"Filename">,
+ Completion<"DiskFile">,
+ Desc<"The file from which to read the settings.">;
+}
+
+let Command = "breakpoint list" in {
+ def blist_internal : Option<"internal", "i">,
+ Desc<"Show debugger internal breakpoints">;
+ def blist_brief : Option<"brief", "b">, Group<1>,
+ Desc<"Give a brief description of the breakpoint (no location info).">;
+ def blist_full : Option<"full", "f">, Group<2>,
+ Desc<"Give a full description of the breakpoint and its locations.">;
+ def blist_verbose : Option<"verbose", "v">, Group<3>,
+ Desc<"Explain everything we know about the breakpoint (for debugging "
+ "debugger bugs).">;
+ def blist_dummy_bp : Option<"dummy-breakpoints", "D">,
+ Desc<"List Dummy breakpoints - i.e. breakpoints set before a file is "
+ "provided, which prime new targets.">;
+}
+
+let Command = "thread backtrace" in {
+ def thread_backtrace_count : Option<"count", "c">, Group<1>, Arg<"Count">,
+ Desc<"How many frames to display (-1 for all)">;
+ def thread_backtrace_start : Option<"start", "s">, Group<1>,
+ Arg<"FrameIndex">, Desc<"Frame in which to start the backtrace">;
+ def thread_backtrace_extended : Option<"extended", "e">, Group<1>,
+ Arg<"Boolean">, Desc<"Show the extended backtrace, if available">;
+}
+
+let Command = "thread step scope" in {
+ def thread_step_scope_step_in_avoids_no_debug :
+ Option<"step-in-avoids-no-debug", "a">, Group<1>, Arg<"Boolean">,
+ Desc<"A boolean value that sets whether stepping into functions will step "
+ "over functions with no debug information.">;
+ def thread_step_scope_step_out_avoids_no_debug :
+ Option<"step-out-avoids-no-debug", "A">, Group<1>, Arg<"Boolean">,
+ Desc<"A boolean value, if true stepping out of functions will continue to"
+ " step out till it hits a function with debug information.">;
+ def thread_step_scope_count : Option<"count", "c">, Group<1>, Arg<"Count">,
+ Desc<"How many times to perform the stepping operation - currently only "
+ "supported for step-inst and next-inst.">;
+ def thread_step_scope_end_linenumber : Option<"end-linenumber", "e">,
+ Group<1>, Arg<"LineNum">, Desc<"The line at which to stop stepping - "
+ "defaults to the next line and only supported for step-in and step-over."
+ " You can also pass the string 'block' to step to the end of the current"
+ " block. This is particularly use in conjunction with --step-target to"
+ " step through a complex calling sequence.">;
+ def thread_step_scope_run_mode : Option<"run-mode", "m">, Group<1>,
+ EnumArg<"RunMode", "TriRunningModes()">, Desc<"Determine how to run other "
+ "threads while stepping the current thread.">;
+ def thread_step_scope_step_over_regexp : Option<"step-over-regexp", "r">,
+ Group<1>, Arg<"RegularExpression">, Desc<"A regular expression that defines"
+ "function names to not to stop at when stepping in.">;
+ def thread_step_scope_step_in_target : Option<"step-in-target", "t">,
+ Group<1>, Arg<"FunctionName">, Desc<"The name of the directly called "
+ "function step in should stop at when stepping into.">;
+ def thread_step_scope_python_class : Option<"python-class", "C">, Group<2>,
+ Arg<"PythonClass">, Desc<"The name of the class that will manage this step "
+ "- only supported for Scripted Step.">;
+}
+
+let Command = "thread until" in {
+ def thread_until_frame : Option<"frame", "f">, Group<1>, Arg<"FrameIndex">,
+ Desc<"Frame index for until operation - defaults to 0">;
+ def thread_until_thread : Option<"thread", "t">, Group<1>, Arg<"ThreadIndex">,
+ Desc<"Thread index for the thread for until operation">;
+ def thread_until_run_mode : Option<"run-mode", "m">, Group<1>,
+ EnumArg<"RunMode", "DuoRunningModes()">, Desc<"Determine how to run other"
+ "threads while stepping this one">;
+ def thread_until_address : Option<"address", "a">, Group<1>,
+ Arg<"AddressOrExpression">, Desc<"Run until we reach the specified address,"
+ "or leave the function - can be specified multiple times.">;
+}
+
+let Command = "thread info" in {
+ def thread_info_json : Option<"json", "j">, Desc<"Display the thread info in"
+ " JSON format.">;
+ def thread_info_stop_info : Option<"stop-info", "s">, Desc<"Display the "
+ "extended stop info in JSON format.">;
+}
+
+let Command = "thread return" in {
+ def thread_return_from_expression : Option<"from-expression", "x">,
+ Desc<"Return from the innermost expression evaluation.">;
+}
+
+let Command = "thread jump" in {
+ def thread_jump_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
+ Completion<"SourceFile">, Desc<"Specifies the source file to jump to.">;
+ def thread_jump_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
+ Required, Desc<"Specifies the line number to jump to.">;
+ def thread_jump_by : Option<"by", "b">, Group<2>, Arg<"Offset">, Required,
+ Desc<"Jumps by a relative line offset from the current line.">;
+ def thread_jump_address : Option<"address", "a">, Group<3>,
+ Arg<"AddressOrExpression">, Required, Desc<"Jumps to a specific address.">;
+ def thread_jump_force : Option<"force", "r">, Groups<[1,2,3]>,
+ Desc<"Allows the PC to leave the current function.">;
+}
+
+let Command = "thread plan list" in {
+ def thread_plan_list_verbose : Option<"verbose", "v">, Group<1>,
+ Desc<"Display more information about the thread plans">;
+ def thread_plan_list_internal : Option<"internal", "i">, Group<1>,
+ Desc<"Display internal as well as user thread plans">;
+}
+
+let Command = "type summary add" in {
+ def type_summary_add_category : Option<"category", "w">, Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_summary_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_summary_add_no_value : Option<"no-value", "v">,
+ Desc<"Don't show the value, just show the summary, for this type.">;
+ def type_summary_add_skip_pointers : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_summary_add_skip_references : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_summary_add_regex : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
+ def type_summary_add_inline_children : Option<"inline-children", "c">,
+ Group<1>, Required,
+ Desc<"If true, inline all child values into summary string.">;
+ def type_summary_add_omit_names : Option<"omit-names", "O">, Group<1>,
+ Desc<"If true, omit value names in the summary display.">;
+ def type_summary_add_summary_string : Option<"summary-string", "s">, Group<2>,
+ Arg<"SummaryString">, Required,
+ Desc<"Summary string used to display text and object contents.">;
+ def type_summary_add_python_script : Option<"python-script", "o">, Group<3>,
+ Arg<"PythonScript">,
+ Desc<"Give a one-liner Python script as part of the command.">;
+ def type_summary_add_python_function : Option<"python-function", "F">,
+ Group<3>, Arg<"PythonFunction">,
+ Desc<"Give the name of a Python function to use for this type.">;
+ def type_summary_add_input_python : Option<"input-python", "P">, Group<3>,
+ Desc<"Input Python code to use for this type manually.">;
+ def type_summary_add_expand : Option<"expand", "e">, Groups<[2,3]>,
+ Desc<"Expand aggregate data types to show children on separate lines.">;
+ def type_summary_add_hide_empty : Option<"hide-empty", "h">, Groups<[2,3]>,
+ Desc<"Do not expand aggregate data types with no children.">;
+ def type_summary_add_name : Option<"name", "n">, Groups<[2,3]>, Arg<"Name">,
+ Desc<"A name for this summary string.">;
+}
+
+let Command = "type synth add" in {
+ def type_synth_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_synth_add_skip_pointers : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_synth_add_skip_references : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_synth_add_category : Option<"category", "w">, Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_synth_add_python_class : Option<"python-class", "l">, Group<2>,
+ Arg<"PythonClass">,
+ Desc<"Use this Python class to produce synthetic children.">;
+ def type_synth_add_input_python : Option<"input-python", "P">, Group<3>,
+ Desc<"Type Python code to generate a class that provides synthetic "
+ "children.">;
+ def type_synth_add_regex : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
+}
+
+let Command = "type format add" in {
+ def type_format_add_category : Option<"category", "w">, Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_format_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_format_add_skip_pointers : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_format_add_skip_references : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_format_add_regex : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
+ def type_format_add_type : Option<"type", "t">, Group<2>, Arg<"Name">,
+ Desc<"Format variables as if they were of this type.">;
+}
+
+let Command = "type formatter delete" in {
+ def type_formatter_delete_all : Option<"all", "a">, Group<1>,
+ Desc<"Delete from every category.">;
+ def type_formatter_delete_category : Option<"category", "w">, Group<2>,
+ Arg<"Name">, Desc<"Delete from given category.">;
+ def type_formatter_delete_language : Option<"language", "l">, Group<3>,
+ Arg<"Language">, Desc<"Delete from given language's category.">;
+}
+
+let Command = "type formatter clear" in {
+ def type_formatter_clear_all : Option<"all", "a">,
+ Desc<"Clear every category.">;
+}
+
+let Command = "type formatter list" in {
+ def type_formatter_list_category_regex : Option<"category-regex", "w">,
+ Group<1>, Arg<"Name">, Desc<"Only show categories matching this filter.">;
+ def type_formatter_list_language : Option<"language", "l">, Group<2>,
+ Arg<"Language">, Desc<"Only show the category for a specific language.">;
+}
+
+let Command = "type category define" in {
+ def type_category_define_enabled : Option<"enabled", "e">,
+ Desc<"If specified, this category will be created enabled.">;
+ def type_category_define_language : Option<"language", "l">, Arg<"Language">,
+ Desc<"Specify the language that this category is supported for.">;
+}
+
+let Command = "type category enable" in {
+ def type_category_enable_language : Option<"language", "l">, Arg<"Language">,
+ Desc<"Enable the category for this language.">;
+}
+
+let Command = "type category disable" in {
+ def type_category_disable_language : Option<"language", "l">, Arg<"Language">,
+ Desc<"Enable the category for this language.">;
+}
+
+let Command = "type filter add" in {
+ def type_filter_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_filter_add_skip_pointers : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_filter_add_skip_references : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_filter_add_category : Option<"category", "w">, Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_filter_add_child : Option<"child", "c">, Arg<"ExpressionPath">,
+ Desc<"Include this expression path in the synthetic view.">;
+ def type_filter_add_regex : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
+}
+
+let Command = "type lookup" in {
+ def type_lookup_show_help : Option<"show-help", "h">,
+ Desc<"Display available help for types">;
+ def type_lookup_language : Option<"language", "l">, Arg<"Language">,
+ Desc<"Which language's types should the search scope be">;
+}
+
+let Command = "watchpoint list" in {
+ def watchpoint_list_brief : Option<"brief", "b">, Group<1>, Desc<"Give a "
+ "brief description of the watchpoint (no location info).">;
+ def watchpoint_list_full : Option<"full", "f">, Group<2>, Desc<"Give a full "
+ "description of the watchpoint and its locations.">;
+ def watchpoint_list_verbose : Option<"verbose", "v">, Group<3>, Desc<"Explain"
+ "everything we know about the watchpoint (for debugging debugger bugs).">;
+}
+
+let Command = "watchpoint ignore" in {
+ def watchpoint_ignore_ignore_count : Option<"ignore-count", "i">,
+ Arg<"Count">, Required, Desc<"Set the number of times this watchpoint is"
+ " skipped before stopping.">;
+}
+
+let Command = "watchpoint modify" in {
+ def watchpoint_modify_condition : Option<"condition", "c">, Arg<"Expression">,
+ Desc<"The watchpoint stops only if this condition expression evaluates "
+ "to true.">;
+}
+
+let Command = "watchpoint command add" in {
+ def watchpoint_command_add_one_liner : Option<"one-liner", "o">, Group<1>,
+ Arg<"OneLiner">, Desc<"Specify a one-line watchpoint command inline. Be "
+ "sure to surround it with quotes.">;
+ def watchpoint_command_add_stop_on_error : Option<"stop-on-error", "e">,
+ Arg<"Boolean">, Desc<"Specify whether watchpoint command execution should "
+ "terminate on error.">;
+ def watchpoint_command_add_script_type : Option<"script-type", "s">,
+ EnumArg<"None", "ScriptOptionEnum()">, Desc<"Specify the language for the"
+ " commands - if none is specified, the lldb command interpreter will be "
+ "used.">;
+ def watchpoint_command_add_python_function : Option<"python-function", "F">,
+ Group<2>, Arg<"PythonFunction">, Desc<"Give the name of a Python function "
+ "to run as command for this watchpoint. Be sure to give a module name if "
+ "appropriate.">;
+}
diff --git a/contrib/llvm-project/lldb/source/Commands/OptionsBase.td b/contrib/llvm-project/lldb/source/Commands/OptionsBase.td
new file mode 100644
index 000000000000..a81563ed28c2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Commands/OptionsBase.td
@@ -0,0 +1,160 @@
+
+// The fields below describe how the fields of `OptionDefinition` struct are
+// initialized by different definitions in the Options.td and this file.
+////////////////////////////////////////////////////////////////////////////////
+// Field: usage_mask
+// Default value: LLDB_OPT_SET_ALL (Option allowed in all groups)
+// Set by:
+// - `Group`: Sets a single group to this option.
+// Example: def foo : Option<"foo", "f">, Group<1>;
+// - `Groups`: Sets a given list of group numbers.
+// Example: def foo : Option<"foo", "f">, Groups<[1,4,6]>;
+// - `GroupRange`: Sets an interval of groups. Start and end are inclusive.
+// Example: def foo : Option<"foo", "f">, GroupRange<1, 4>;
+// Sets group 1, 2, 3, 4 for the option.
+////////////////////////////////////////////////////////////////////////////////
+// Field: required
+// Default value: false (Not required)
+// Set by:
+// - `Required`: Marks the option as required.
+// Example: def foo : Option<"foo", "f">, Required;
+////////////////////////////////////////////////////////////////////////////////
+// Field: long_option
+// Default value: not available (has to be defined in Option)
+// Set by:
+// - `Option` constructor: Already set by constructor.
+// Example: def foo : Option<"long-option", "l">
+// ^
+// long option value
+////////////////////////////////////////////////////////////////////////////////
+// Field: short_option
+// Default value: not available (has to be defined in Option)
+// Set by:
+// - `Option` constructor: Already set by constructor.
+// Example: def foo : Option<"long-option", "l">
+// ^
+// short option
+////////////////////////////////////////////////////////////////////////////////
+// Field: option_has_arg
+// Default value: OptionParser::eNoArgument (No argument allowed)
+// Set by:
+// - `OptionalArg`: Sets the argument type and marks it as optional.
+// - `Arg`: Sets the argument type and marks it as required.
+// - `EnumArg`: Sets the argument type to an enum and marks it as required.
+// See argument_type field for more info.
+////////////////////////////////////////////////////////////////////////////////
+// Field: validator
+// Default value: 0 (No validator for option)
+// Set by: Nothing. This is currently only set after initialization in LLDB.
+////////////////////////////////////////////////////////////////////////////////
+// Field: enum_values
+// Default value: {} (No enum associated with this option)
+// Set by:
+// - `EnumArg`: Sets the argument type and assigns it a enum holding the valid
+// values. The enum needs to be a variable in the including code.
+// Marks the option as required (see option_has_arg).
+// Example: def foo : Option<"foo", "f">,
+// EnumArg<"SortOrder",
+// "OptionEnumValues(g_sort_option_enumeration)">;
+////////////////////////////////////////////////////////////////////////////////
+// Field: completion_type
+// Default value: CommandCompletions::eNoCompletion (no tab completion)
+// Set by:
+// - `Completion`: Gives the option a single completion kind.
+// Example: def foo : Option<"foo", "f">,
+// Completion<"DiskFile">;
+// Sets the completion to eDiskFileCompletion
+//
+// - `Completions`: Sets a given kinds of completions.
+// Example: def foo : Option<"foo", "f">,
+// Completions<["DiskFile", "DiskDirectory"]>;
+// Sets the completion to
+// `eDiskFileCompletion | eDiskDirectoryCompletion`.
+////////////////////////////////////////////////////////////////////////////////
+// Field: argument_type
+// Default value: eArgTypeNone
+// Set by:
+// - `OptionalArg`: Sets the argument type and marks it as optional.
+// Example: def foo : Option<"foo", "f">, OptionalArg<"Pid">;
+// Sets the argument type to eArgTypePid and marks option as
+// optional (see option_has_arg).
+// - `Arg`: Sets the argument type and marks it as required.
+// Example: def foo : Option<"foo", "f">, Arg<"Pid">;
+// Sets the argument type to eArgTypePid and marks option as
+// required (see option_has_arg).
+// - `EnumArg`: Sets the argument type and assigns it a enum holding the valid
+// values. The enum needs to be a variable in the including code.
+// Marks the option as required (see option_has_arg).
+// Example: def foo : Option<"foo", "f">,
+// EnumArg<"SortOrder",
+// "OptionEnumValues(g_sort_option_enumeration)">;
+////////////////////////////////////////////////////////////////////////////////
+// Field: usage_text
+// Default value: ""
+// Set by:
+// - `Desc`: Sets the description for the given option.
+// Example: def foo : Option<"foo", "f">, Desc<"does nothing.">;
+// Sets the description to "does nothing.".
+
+// Base class for all options.
+class Option<string fullname, string shortname> {
+ string FullName = fullname;
+ string ShortName = shortname;
+ // The full associated command/subcommand such as "settings set".
+ string Command;
+}
+
+// Moves the option into a list of option groups.
+class Groups<list<int> groups> {
+ list<int> Groups = groups;
+}
+
+// Moves the option in all option groups in a range.
+// Start and end values are inclusive.
+class GroupRange<int start, int end> {
+ int GroupStart = start;
+ int GroupEnd = end;
+}
+// Moves the option in a single option group.
+class Group<int group> {
+ int GroupStart = group;
+ int GroupEnd = group;
+}
+
+// Sets the description for the option that should be
+// displayed to the user.
+class Desc<string description> {
+ string Description = description;
+}
+
+// Marks the option as required when calling the
+// associated command.
+class Required {
+ bit Required = 1;
+}
+
+// Gives the option an optional argument.
+class OptionalArg<string type> {
+ string ArgType = type;
+ bit OptionalArg = 1;
+}
+
+// Gives the option an required argument.
+class Arg<string type> {
+ string ArgType = type;
+}
+
+// Gives the option an required argument.
+class EnumArg<string type, string enum> {
+ string ArgType = type;
+ string ArgEnum = enum;
+}
+
+// Sets the available completions for the given option.
+class Completions<list<string> completions> {
+ list<string> Completions = completions;
+}
+// Sets a single completion for the given option.
+class Completion<string completion> {
+ list<string> Completions = [completion];
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Address.cpp b/contrib/llvm-project/lldb/source/Core/Address.cpp
new file mode 100644
index 000000000000..0da83eb98edb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Address.cpp
@@ -0,0 +1,1006 @@
+//===-- Address.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Declaration.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Compiler.h"
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include <assert.h>
+#include <inttypes.h>
+#include <string.h>
+
+namespace lldb_private {
+class CompileUnit;
+}
+namespace lldb_private {
+class Function;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+static size_t ReadBytes(ExecutionContextScope *exe_scope,
+ const Address &address, void *dst, size_t dst_len) {
+ if (exe_scope == nullptr)
+ return 0;
+
+ TargetSP target_sp(exe_scope->CalculateTarget());
+ if (target_sp) {
+ Status error;
+ bool prefer_file_cache = false;
+ return target_sp->ReadMemory(address, prefer_file_cache, dst, dst_len,
+ error);
+ }
+ return 0;
+}
+
+static bool GetByteOrderAndAddressSize(ExecutionContextScope *exe_scope,
+ const Address &address,
+ ByteOrder &byte_order,
+ uint32_t &addr_size) {
+ byte_order = eByteOrderInvalid;
+ addr_size = 0;
+ if (exe_scope == nullptr)
+ return false;
+
+ TargetSP target_sp(exe_scope->CalculateTarget());
+ if (target_sp) {
+ byte_order = target_sp->GetArchitecture().GetByteOrder();
+ addr_size = target_sp->GetArchitecture().GetAddressByteSize();
+ }
+
+ if (byte_order == eByteOrderInvalid || addr_size == 0) {
+ ModuleSP module_sp(address.GetModule());
+ if (module_sp) {
+ byte_order = module_sp->GetArchitecture().GetByteOrder();
+ addr_size = module_sp->GetArchitecture().GetAddressByteSize();
+ }
+ }
+ return byte_order != eByteOrderInvalid && addr_size != 0;
+}
+
+static uint64_t ReadUIntMax64(ExecutionContextScope *exe_scope,
+ const Address &address, uint32_t byte_size,
+ bool &success) {
+ uint64_t uval64 = 0;
+ if (exe_scope == nullptr || byte_size > sizeof(uint64_t)) {
+ success = false;
+ return 0;
+ }
+ uint64_t buf = 0;
+
+ success = ReadBytes(exe_scope, address, &buf, byte_size) == byte_size;
+ if (success) {
+ ByteOrder byte_order = eByteOrderInvalid;
+ uint32_t addr_size = 0;
+ if (GetByteOrderAndAddressSize(exe_scope, address, byte_order, addr_size)) {
+ DataExtractor data(&buf, sizeof(buf), byte_order, addr_size);
+ lldb::offset_t offset = 0;
+ uval64 = data.GetU64(&offset);
+ } else
+ success = false;
+ }
+ return uval64;
+}
+
+static bool ReadAddress(ExecutionContextScope *exe_scope,
+ const Address &address, uint32_t pointer_size,
+ Address &deref_so_addr) {
+ if (exe_scope == nullptr)
+ return false;
+
+ bool success = false;
+ addr_t deref_addr = ReadUIntMax64(exe_scope, address, pointer_size, success);
+ if (success) {
+ ExecutionContext exe_ctx;
+ exe_scope->CalculateExecutionContext(exe_ctx);
+ // If we have any sections that are loaded, try and resolve using the
+ // section load list
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target && !target->GetSectionLoadList().IsEmpty()) {
+ if (target->GetSectionLoadList().ResolveLoadAddress(deref_addr,
+ deref_so_addr))
+ return true;
+ } else {
+ // If we were not running, yet able to read an integer, we must have a
+ // module
+ ModuleSP module_sp(address.GetModule());
+
+ assert(module_sp);
+ if (module_sp->ResolveFileAddress(deref_addr, deref_so_addr))
+ return true;
+ }
+
+ // We couldn't make "deref_addr" into a section offset value, but we were
+ // able to read the address, so we return a section offset address with no
+ // section and "deref_addr" as the offset (address).
+ deref_so_addr.SetRawAddress(deref_addr);
+ return true;
+ }
+ return false;
+}
+
+static bool DumpUInt(ExecutionContextScope *exe_scope, const Address &address,
+ uint32_t byte_size, Stream *strm) {
+ if (exe_scope == nullptr || byte_size == 0)
+ return false;
+ std::vector<uint8_t> buf(byte_size, 0);
+
+ if (ReadBytes(exe_scope, address, &buf[0], buf.size()) == buf.size()) {
+ ByteOrder byte_order = eByteOrderInvalid;
+ uint32_t addr_size = 0;
+ if (GetByteOrderAndAddressSize(exe_scope, address, byte_order, addr_size)) {
+ DataExtractor data(&buf.front(), buf.size(), byte_order, addr_size);
+
+ DumpDataExtractor(data, strm,
+ 0, // Start offset in "data"
+ eFormatHex, // Print as characters
+ buf.size(), // Size of item
+ 1, // Items count
+ UINT32_MAX, // num per line
+ LLDB_INVALID_ADDRESS, // base address
+ 0, // bitfield bit size
+ 0); // bitfield bit offset
+
+ return true;
+ }
+ }
+ return false;
+}
+
+static size_t ReadCStringFromMemory(ExecutionContextScope *exe_scope,
+ const Address &address, Stream *strm) {
+ if (exe_scope == nullptr)
+ return 0;
+ const size_t k_buf_len = 256;
+ char buf[k_buf_len + 1];
+ buf[k_buf_len] = '\0'; // NULL terminate
+
+ // Byte order and address size don't matter for C string dumping..
+ DataExtractor data(buf, sizeof(buf), endian::InlHostByteOrder(), 4);
+ size_t total_len = 0;
+ size_t bytes_read;
+ Address curr_address(address);
+ strm->PutChar('"');
+ while ((bytes_read = ReadBytes(exe_scope, curr_address, buf, k_buf_len)) >
+ 0) {
+ size_t len = strlen(buf);
+ if (len == 0)
+ break;
+ if (len > bytes_read)
+ len = bytes_read;
+
+ DumpDataExtractor(data, strm,
+ 0, // Start offset in "data"
+ eFormatChar, // Print as characters
+ 1, // Size of item (1 byte for a char!)
+ len, // How many bytes to print?
+ UINT32_MAX, // num per line
+ LLDB_INVALID_ADDRESS, // base address
+ 0, // bitfield bit size
+
+ 0); // bitfield bit offset
+
+ total_len += bytes_read;
+
+ if (len < k_buf_len)
+ break;
+ curr_address.SetOffset(curr_address.GetOffset() + bytes_read);
+ }
+ strm->PutChar('"');
+ return total_len;
+}
+
+Address::Address(lldb::addr_t abs_addr) : m_section_wp(), m_offset(abs_addr) {}
+
+Address::Address(addr_t address, const SectionList *section_list)
+ : m_section_wp(), m_offset(LLDB_INVALID_ADDRESS) {
+ ResolveAddressUsingFileSections(address, section_list);
+}
+
+const Address &Address::operator=(const Address &rhs) {
+ if (this != &rhs) {
+ m_section_wp = rhs.m_section_wp;
+ m_offset = rhs.m_offset;
+ }
+ return *this;
+}
+
+bool Address::ResolveAddressUsingFileSections(addr_t file_addr,
+ const SectionList *section_list) {
+ if (section_list) {
+ SectionSP section_sp(
+ section_list->FindSectionContainingFileAddress(file_addr));
+ m_section_wp = section_sp;
+ if (section_sp) {
+ assert(section_sp->ContainsFileAddress(file_addr));
+ m_offset = file_addr - section_sp->GetFileAddress();
+ return true; // Successfully transformed addr into a section offset
+ // address
+ }
+ }
+ m_offset = file_addr;
+ return false; // Failed to resolve this address to a section offset value
+}
+
+ModuleSP Address::GetModule() const {
+ lldb::ModuleSP module_sp;
+ SectionSP section_sp(GetSection());
+ if (section_sp)
+ module_sp = section_sp->GetModule();
+ return module_sp;
+}
+
+addr_t Address::GetFileAddress() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ addr_t sect_file_addr = section_sp->GetFileAddress();
+ if (sect_file_addr == LLDB_INVALID_ADDRESS) {
+ // Section isn't resolved, we can't return a valid file address
+ return LLDB_INVALID_ADDRESS;
+ }
+ // We have a valid file range, so we can return the file based address by
+ // adding the file base address to our offset
+ return sect_file_addr + m_offset;
+ } else if (SectionWasDeletedPrivate()) {
+ // Used to have a valid section but it got deleted so the offset doesn't
+ // mean anything without the section
+ return LLDB_INVALID_ADDRESS;
+ }
+ // No section, we just return the offset since it is the value in this case
+ return m_offset;
+}
+
+addr_t Address::GetLoadAddress(Target *target) const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ if (target) {
+ addr_t sect_load_addr = section_sp->GetLoadBaseAddress(target);
+
+ if (sect_load_addr != LLDB_INVALID_ADDRESS) {
+ // We have a valid file range, so we can return the file based address
+ // by adding the file base address to our offset
+ return sect_load_addr + m_offset;
+ }
+ }
+ } else if (SectionWasDeletedPrivate()) {
+ // Used to have a valid section but it got deleted so the offset doesn't
+ // mean anything without the section
+ return LLDB_INVALID_ADDRESS;
+ } else {
+ // We don't have a section so the offset is the load address
+ return m_offset;
+ }
+ // The section isn't resolved or an invalid target was passed in so we can't
+ // return a valid load address.
+ return LLDB_INVALID_ADDRESS;
+}
+
+addr_t Address::GetCallableLoadAddress(Target *target, bool is_indirect) const {
+ addr_t code_addr = LLDB_INVALID_ADDRESS;
+
+ if (is_indirect && target) {
+ ProcessSP processSP = target->GetProcessSP();
+ Status error;
+ if (processSP) {
+ code_addr = processSP->ResolveIndirectFunction(this, error);
+ if (!error.Success())
+ code_addr = LLDB_INVALID_ADDRESS;
+ }
+ } else {
+ code_addr = GetLoadAddress(target);
+ }
+
+ if (code_addr == LLDB_INVALID_ADDRESS)
+ return code_addr;
+
+ if (target)
+ return target->GetCallableLoadAddress(code_addr, GetAddressClass());
+ return code_addr;
+}
+
+bool Address::SetCallableLoadAddress(lldb::addr_t load_addr, Target *target) {
+ if (SetLoadAddress(load_addr, target)) {
+ if (target)
+ m_offset = target->GetCallableLoadAddress(m_offset, GetAddressClass());
+ return true;
+ }
+ return false;
+}
+
+addr_t Address::GetOpcodeLoadAddress(Target *target,
+ AddressClass addr_class) const {
+ addr_t code_addr = GetLoadAddress(target);
+ if (code_addr != LLDB_INVALID_ADDRESS) {
+ if (addr_class == AddressClass::eInvalid)
+ addr_class = GetAddressClass();
+ code_addr = target->GetOpcodeLoadAddress(code_addr, addr_class);
+ }
+ return code_addr;
+}
+
+bool Address::SetOpcodeLoadAddress(lldb::addr_t load_addr, Target *target,
+ AddressClass addr_class,
+ bool allow_section_end) {
+ if (SetLoadAddress(load_addr, target, allow_section_end)) {
+ if (target) {
+ if (addr_class == AddressClass::eInvalid)
+ addr_class = GetAddressClass();
+ m_offset = target->GetOpcodeLoadAddress(m_offset, addr_class);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
+ DumpStyle fallback_style, uint32_t addr_size) const {
+ // If the section was nullptr, only load address is going to work unless we
+ // are trying to deref a pointer
+ SectionSP section_sp(GetSection());
+ if (!section_sp && style != DumpStyleResolvedPointerDescription)
+ style = DumpStyleLoadAddress;
+
+ ExecutionContext exe_ctx(exe_scope);
+ Target *target = exe_ctx.GetTargetPtr();
+ // If addr_byte_size is UINT32_MAX, then determine the correct address byte
+ // size for the process or default to the size of addr_t
+ if (addr_size == UINT32_MAX) {
+ if (target)
+ addr_size = target->GetArchitecture().GetAddressByteSize();
+ else
+ addr_size = sizeof(addr_t);
+ }
+
+ Address so_addr;
+ switch (style) {
+ case DumpStyleInvalid:
+ return false;
+
+ case DumpStyleSectionNameOffset:
+ if (section_sp) {
+ section_sp->DumpName(s);
+ s->Printf(" + %" PRIu64, m_offset);
+ } else {
+ s->Address(m_offset, addr_size);
+ }
+ break;
+
+ case DumpStyleSectionPointerOffset:
+ s->Printf("(Section *)%p + ", static_cast<void *>(section_sp.get()));
+ s->Address(m_offset, addr_size);
+ break;
+
+ case DumpStyleModuleWithFileAddress:
+ if (section_sp) {
+ ModuleSP module_sp = section_sp->GetModule();
+ if (module_sp)
+ s->Printf("%s[", module_sp->GetFileSpec().GetFilename().AsCString(
+ "<Unknown>"));
+ else
+ s->Printf("%s[", "<Unknown>");
+ }
+ LLVM_FALLTHROUGH;
+ case DumpStyleFileAddress: {
+ addr_t file_addr = GetFileAddress();
+ if (file_addr == LLDB_INVALID_ADDRESS) {
+ if (fallback_style != DumpStyleInvalid)
+ return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
+ return false;
+ }
+ s->Address(file_addr, addr_size);
+ if (style == DumpStyleModuleWithFileAddress && section_sp)
+ s->PutChar(']');
+ } break;
+
+ case DumpStyleLoadAddress: {
+ addr_t load_addr = GetLoadAddress(target);
+
+ /*
+ * MIPS:
+ * Display address in compressed form for MIPS16 or microMIPS
+ * if the address belongs to AddressClass::eCodeAlternateISA.
+ */
+ if (target) {
+ const llvm::Triple::ArchType llvm_arch =
+ target->GetArchitecture().GetMachine();
+ if (llvm_arch == llvm::Triple::mips ||
+ llvm_arch == llvm::Triple::mipsel ||
+ llvm_arch == llvm::Triple::mips64 ||
+ llvm_arch == llvm::Triple::mips64el)
+ load_addr = GetCallableLoadAddress(target);
+ }
+
+ if (load_addr == LLDB_INVALID_ADDRESS) {
+ if (fallback_style != DumpStyleInvalid)
+ return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
+ return false;
+ }
+ s->Address(load_addr, addr_size);
+ } break;
+
+ case DumpStyleResolvedDescription:
+ case DumpStyleResolvedDescriptionNoModule:
+ case DumpStyleResolvedDescriptionNoFunctionArguments:
+ case DumpStyleNoFunctionName:
+ if (IsSectionOffset()) {
+ uint32_t pointer_size = 4;
+ ModuleSP module_sp(GetModule());
+ if (target)
+ pointer_size = target->GetArchitecture().GetAddressByteSize();
+ else if (module_sp)
+ pointer_size = module_sp->GetArchitecture().GetAddressByteSize();
+
+ bool showed_info = false;
+ if (section_sp) {
+ SectionType sect_type = section_sp->GetType();
+ switch (sect_type) {
+ case eSectionTypeData:
+ if (module_sp) {
+ SymbolVendor *sym_vendor = module_sp->GetSymbolVendor();
+ if (sym_vendor) {
+ Symtab *symtab = sym_vendor->GetSymtab();
+ if (symtab) {
+ const addr_t file_Addr = GetFileAddress();
+ Symbol *symbol =
+ symtab->FindSymbolContainingFileAddress(file_Addr);
+ if (symbol) {
+ const char *symbol_name = symbol->GetName().AsCString();
+ if (symbol_name) {
+ s->PutCString(symbol_name);
+ addr_t delta =
+ file_Addr - symbol->GetAddressRef().GetFileAddress();
+ if (delta)
+ s->Printf(" + %" PRIu64, delta);
+ showed_info = true;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case eSectionTypeDataCString:
+ // Read the C string from memory and display it
+ showed_info = true;
+ ReadCStringFromMemory(exe_scope, *this, s);
+ break;
+
+ case eSectionTypeDataCStringPointers:
+ if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) {
+#if VERBOSE_OUTPUT
+ s->PutCString("(char *)");
+ so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
+ DumpStyleFileAddress);
+ s->PutCString(": ");
+#endif
+ showed_info = true;
+ ReadCStringFromMemory(exe_scope, so_addr, s);
+ }
+ break;
+
+ case eSectionTypeDataObjCMessageRefs:
+ if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) {
+ if (target && so_addr.IsSectionOffset()) {
+ SymbolContext func_sc;
+ target->GetImages().ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, func_sc);
+ if (func_sc.function != nullptr || func_sc.symbol != nullptr) {
+ showed_info = true;
+#if VERBOSE_OUTPUT
+ s->PutCString("(objc_msgref *) -> { (func*)");
+ so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
+ DumpStyleFileAddress);
+#else
+ s->PutCString("{ ");
+#endif
+ Address cstr_addr(*this);
+ cstr_addr.SetOffset(cstr_addr.GetOffset() + pointer_size);
+ func_sc.DumpStopContext(s, exe_scope, so_addr, true, true,
+ false, true, true);
+ if (ReadAddress(exe_scope, cstr_addr, pointer_size, so_addr)) {
+#if VERBOSE_OUTPUT
+ s->PutCString("), (char *)");
+ so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
+ DumpStyleFileAddress);
+ s->PutCString(" (");
+#else
+ s->PutCString(", ");
+#endif
+ ReadCStringFromMemory(exe_scope, so_addr, s);
+ }
+#if VERBOSE_OUTPUT
+ s->PutCString(") }");
+#else
+ s->PutCString(" }");
+#endif
+ }
+ }
+ }
+ break;
+
+ case eSectionTypeDataObjCCFStrings: {
+ Address cfstring_data_addr(*this);
+ cfstring_data_addr.SetOffset(cfstring_data_addr.GetOffset() +
+ (2 * pointer_size));
+ if (ReadAddress(exe_scope, cfstring_data_addr, pointer_size,
+ so_addr)) {
+#if VERBOSE_OUTPUT
+ s->PutCString("(CFString *) ");
+ cfstring_data_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
+ DumpStyleFileAddress);
+ s->PutCString(" -> @");
+#else
+ s->PutChar('@');
+#endif
+ if (so_addr.Dump(s, exe_scope, DumpStyleResolvedDescription))
+ showed_info = true;
+ }
+ } break;
+
+ case eSectionTypeData4:
+ // Read the 4 byte data and display it
+ showed_info = true;
+ s->PutCString("(uint32_t) ");
+ DumpUInt(exe_scope, *this, 4, s);
+ break;
+
+ case eSectionTypeData8:
+ // Read the 8 byte data and display it
+ showed_info = true;
+ s->PutCString("(uint64_t) ");
+ DumpUInt(exe_scope, *this, 8, s);
+ break;
+
+ case eSectionTypeData16:
+ // Read the 16 byte data and display it
+ showed_info = true;
+ s->PutCString("(uint128_t) ");
+ DumpUInt(exe_scope, *this, 16, s);
+ break;
+
+ case eSectionTypeDataPointers:
+ // Read the pointer data and display it
+ if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) {
+ s->PutCString("(void *)");
+ so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
+ DumpStyleFileAddress);
+
+ showed_info = true;
+ if (so_addr.IsSectionOffset()) {
+ SymbolContext pointer_sc;
+ if (target) {
+ target->GetImages().ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, pointer_sc);
+ if (pointer_sc.function != nullptr ||
+ pointer_sc.symbol != nullptr) {
+ s->PutCString(": ");
+ pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false,
+ false, true, true);
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!showed_info) {
+ if (module_sp) {
+ SymbolContext sc;
+ module_sp->ResolveSymbolContextForAddress(
+ *this, eSymbolContextEverything, sc);
+ if (sc.function || sc.symbol) {
+ bool show_stop_context = true;
+ const bool show_module = (style == DumpStyleResolvedDescription);
+ const bool show_fullpaths = false;
+ const bool show_inlined_frames = true;
+ const bool show_function_arguments =
+ (style != DumpStyleResolvedDescriptionNoFunctionArguments);
+ const bool show_function_name = (style != DumpStyleNoFunctionName);
+ if (sc.function == nullptr && sc.symbol != nullptr) {
+ // If we have just a symbol make sure it is in the right section
+ if (sc.symbol->ValueIsAddress()) {
+ if (sc.symbol->GetAddressRef().GetSection() != GetSection()) {
+ // don't show the module if the symbol is a trampoline symbol
+ show_stop_context = false;
+ }
+ }
+ }
+ if (show_stop_context) {
+ // We have a function or a symbol from the same sections as this
+ // address.
+ sc.DumpStopContext(s, exe_scope, *this, show_fullpaths,
+ show_module, show_inlined_frames,
+ show_function_arguments, show_function_name);
+ } else {
+ // We found a symbol but it was in a different section so it
+ // isn't the symbol we should be showing, just show the section
+ // name + offset
+ Dump(s, exe_scope, DumpStyleSectionNameOffset);
+ }
+ }
+ }
+ }
+ } else {
+ if (fallback_style != DumpStyleInvalid)
+ return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
+ return false;
+ }
+ break;
+
+ case DumpStyleDetailedSymbolContext:
+ if (IsSectionOffset()) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ SymbolContext sc;
+ module_sp->ResolveSymbolContextForAddress(
+ *this, eSymbolContextEverything | eSymbolContextVariable, sc);
+ if (sc.symbol) {
+ // If we have just a symbol make sure it is in the same section as
+ // our address. If it isn't, then we might have just found the last
+ // symbol that came before the address that we are looking up that
+ // has nothing to do with our address lookup.
+ if (sc.symbol->ValueIsAddress() &&
+ sc.symbol->GetAddressRef().GetSection() != GetSection())
+ sc.symbol = nullptr;
+ }
+ sc.GetDescription(s, eDescriptionLevelBrief, target);
+
+ if (sc.block) {
+ bool can_create = true;
+ bool get_parent_variables = true;
+ bool stop_if_block_is_inlined_function = false;
+ VariableList variable_list;
+ sc.block->AppendVariables(can_create, get_parent_variables,
+ stop_if_block_is_inlined_function,
+ [](Variable *) { return true; },
+ &variable_list);
+
+ const size_t num_variables = variable_list.GetSize();
+ for (size_t var_idx = 0; var_idx < num_variables; ++var_idx) {
+ Variable *var = variable_list.GetVariableAtIndex(var_idx).get();
+ if (var && var->LocationIsValidForAddress(*this)) {
+ s->Indent();
+ s->Printf(" Variable: id = {0x%8.8" PRIx64 "}, name = \"%s\"",
+ var->GetID(), var->GetName().GetCString());
+ Type *type = var->GetType();
+ if (type)
+ s->Printf(", type = \"%s\"", type->GetName().GetCString());
+ else
+ s->PutCString(", type = <unknown>");
+ s->PutCString(", location = ");
+ var->DumpLocationForAddress(s, *this);
+ s->PutCString(", decl = ");
+ var->GetDeclaration().DumpStopContext(s, false);
+ s->EOL();
+ }
+ }
+ }
+ }
+ } else {
+ if (fallback_style != DumpStyleInvalid)
+ return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
+ return false;
+ }
+ break;
+
+ case DumpStyleResolvedPointerDescription: {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ addr_t load_addr = GetLoadAddress(target);
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ Status memory_error;
+ addr_t dereferenced_load_addr =
+ process->ReadPointerFromMemory(load_addr, memory_error);
+ if (dereferenced_load_addr != LLDB_INVALID_ADDRESS) {
+ Address dereferenced_addr;
+ if (dereferenced_addr.SetLoadAddress(dereferenced_load_addr,
+ target)) {
+ StreamString strm;
+ if (dereferenced_addr.Dump(&strm, exe_scope,
+ DumpStyleResolvedDescription,
+ DumpStyleInvalid, addr_size)) {
+ s->Address(dereferenced_load_addr, addr_size, " -> ", " ");
+ s->Write(strm.GetString().data(), strm.GetSize());
+ return true;
+ }
+ }
+ }
+ }
+ }
+ if (fallback_style != DumpStyleInvalid)
+ return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
+ return false;
+ } break;
+ }
+
+ return true;
+}
+
+bool Address::SectionWasDeleted() const {
+ if (GetSection())
+ return false;
+ return SectionWasDeletedPrivate();
+}
+
+bool Address::SectionWasDeletedPrivate() const {
+ lldb::SectionWP empty_section_wp;
+
+ // If either call to "std::weak_ptr::owner_before(...) value returns true,
+ // this indicates that m_section_wp once contained (possibly still does) a
+ // reference to a valid shared pointer. This helps us know if we had a valid
+ // reference to a section which is now invalid because the module it was in
+ // was unloaded/deleted, or if the address doesn't have a valid reference to
+ // a section.
+ return empty_section_wp.owner_before(m_section_wp) ||
+ m_section_wp.owner_before(empty_section_wp);
+}
+
+uint32_t
+Address::CalculateSymbolContext(SymbolContext *sc,
+ SymbolContextItem resolve_scope) const {
+ sc->Clear(false);
+ // Absolute addresses don't have enough information to reconstruct even their
+ // target.
+
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ ModuleSP module_sp(section_sp->GetModule());
+ if (module_sp) {
+ sc->module_sp = module_sp;
+ if (sc->module_sp)
+ return sc->module_sp->ResolveSymbolContextForAddress(
+ *this, resolve_scope, *sc);
+ }
+ }
+ return 0;
+}
+
+ModuleSP Address::CalculateSymbolContextModule() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp)
+ return section_sp->GetModule();
+ return ModuleSP();
+}
+
+CompileUnit *Address::CalculateSymbolContextCompileUnit() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ SymbolContext sc;
+ sc.module_sp = section_sp->GetModule();
+ if (sc.module_sp) {
+ sc.module_sp->ResolveSymbolContextForAddress(*this,
+ eSymbolContextCompUnit, sc);
+ return sc.comp_unit;
+ }
+ }
+ return nullptr;
+}
+
+Function *Address::CalculateSymbolContextFunction() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ SymbolContext sc;
+ sc.module_sp = section_sp->GetModule();
+ if (sc.module_sp) {
+ sc.module_sp->ResolveSymbolContextForAddress(*this,
+ eSymbolContextFunction, sc);
+ return sc.function;
+ }
+ }
+ return nullptr;
+}
+
+Block *Address::CalculateSymbolContextBlock() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ SymbolContext sc;
+ sc.module_sp = section_sp->GetModule();
+ if (sc.module_sp) {
+ sc.module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextBlock,
+ sc);
+ return sc.block;
+ }
+ }
+ return nullptr;
+}
+
+Symbol *Address::CalculateSymbolContextSymbol() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ SymbolContext sc;
+ sc.module_sp = section_sp->GetModule();
+ if (sc.module_sp) {
+ sc.module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextSymbol,
+ sc);
+ return sc.symbol;
+ }
+ }
+ return nullptr;
+}
+
+bool Address::CalculateSymbolContextLineEntry(LineEntry &line_entry) const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ SymbolContext sc;
+ sc.module_sp = section_sp->GetModule();
+ if (sc.module_sp) {
+ sc.module_sp->ResolveSymbolContextForAddress(*this,
+ eSymbolContextLineEntry, sc);
+ if (sc.line_entry.IsValid()) {
+ line_entry = sc.line_entry;
+ return true;
+ }
+ }
+ }
+ line_entry.Clear();
+ return false;
+}
+
+int Address::CompareFileAddress(const Address &a, const Address &b) {
+ addr_t a_file_addr = a.GetFileAddress();
+ addr_t b_file_addr = b.GetFileAddress();
+ if (a_file_addr < b_file_addr)
+ return -1;
+ if (a_file_addr > b_file_addr)
+ return +1;
+ return 0;
+}
+
+int Address::CompareLoadAddress(const Address &a, const Address &b,
+ Target *target) {
+ assert(target != nullptr);
+ addr_t a_load_addr = a.GetLoadAddress(target);
+ addr_t b_load_addr = b.GetLoadAddress(target);
+ if (a_load_addr < b_load_addr)
+ return -1;
+ if (a_load_addr > b_load_addr)
+ return +1;
+ return 0;
+}
+
+int Address::CompareModulePointerAndOffset(const Address &a, const Address &b) {
+ ModuleSP a_module_sp(a.GetModule());
+ ModuleSP b_module_sp(b.GetModule());
+ Module *a_module = a_module_sp.get();
+ Module *b_module = b_module_sp.get();
+ if (a_module < b_module)
+ return -1;
+ if (a_module > b_module)
+ return +1;
+ // Modules are the same, just compare the file address since they should be
+ // unique
+ addr_t a_file_addr = a.GetFileAddress();
+ addr_t b_file_addr = b.GetFileAddress();
+ if (a_file_addr < b_file_addr)
+ return -1;
+ if (a_file_addr > b_file_addr)
+ return +1;
+ return 0;
+}
+
+size_t Address::MemorySize() const {
+ // Noting special for the memory size of a single Address object, it is just
+ // the size of itself.
+ return sizeof(Address);
+}
+
+// NOTE: Be careful using this operator. It can correctly compare two
+// addresses from the same Module correctly. It can't compare two addresses
+// from different modules in any meaningful way, but it will compare the module
+// pointers.
+//
+// To sum things up:
+// - works great for addresses within the same module - it works for addresses
+// across multiple modules, but don't expect the
+// address results to make much sense
+//
+// This basically lets Address objects be used in ordered collection classes.
+
+bool lldb_private::operator<(const Address &lhs, const Address &rhs) {
+ ModuleSP lhs_module_sp(lhs.GetModule());
+ ModuleSP rhs_module_sp(rhs.GetModule());
+ Module *lhs_module = lhs_module_sp.get();
+ Module *rhs_module = rhs_module_sp.get();
+ if (lhs_module == rhs_module) {
+ // Addresses are in the same module, just compare the file addresses
+ return lhs.GetFileAddress() < rhs.GetFileAddress();
+ } else {
+ // The addresses are from different modules, just use the module pointer
+ // value to get consistent ordering
+ return lhs_module < rhs_module;
+ }
+}
+
+bool lldb_private::operator>(const Address &lhs, const Address &rhs) {
+ ModuleSP lhs_module_sp(lhs.GetModule());
+ ModuleSP rhs_module_sp(rhs.GetModule());
+ Module *lhs_module = lhs_module_sp.get();
+ Module *rhs_module = rhs_module_sp.get();
+ if (lhs_module == rhs_module) {
+ // Addresses are in the same module, just compare the file addresses
+ return lhs.GetFileAddress() > rhs.GetFileAddress();
+ } else {
+ // The addresses are from different modules, just use the module pointer
+ // value to get consistent ordering
+ return lhs_module > rhs_module;
+ }
+}
+
+// The operator == checks for exact equality only (same section, same offset)
+bool lldb_private::operator==(const Address &a, const Address &rhs) {
+ return a.GetOffset() == rhs.GetOffset() && a.GetSection() == rhs.GetSection();
+}
+
+// The operator != checks for exact inequality only (differing section, or
+// different offset)
+bool lldb_private::operator!=(const Address &a, const Address &rhs) {
+ return a.GetOffset() != rhs.GetOffset() || a.GetSection() != rhs.GetSection();
+}
+
+AddressClass Address::GetAddressClass() const {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ ObjectFile *obj_file = module_sp->GetObjectFile();
+ if (obj_file) {
+ // Give the symbol vendor a chance to add to the unified section list
+ // and to symtab from symbol file
+ if (SymbolVendor *vendor = module_sp->GetSymbolVendor())
+ vendor->GetSymtab();
+ return obj_file->GetAddressClass(GetFileAddress());
+ }
+ }
+ return AddressClass::eUnknown;
+}
+
+bool Address::SetLoadAddress(lldb::addr_t load_addr, Target *target,
+ bool allow_section_end) {
+ if (target && target->GetSectionLoadList().ResolveLoadAddress(
+ load_addr, *this, allow_section_end))
+ return true;
+ m_section_wp.reset();
+ m_offset = load_addr;
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/AddressRange.cpp b/contrib/llvm-project/lldb/source/Core/AddressRange.cpp
new file mode 100644
index 000000000000..71eec3c19607
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/AddressRange.cpp
@@ -0,0 +1,211 @@
+//===-- AddressRange.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-defines.h"
+
+#include "llvm/Support/Compiler.h"
+
+#include <memory>
+
+#include <inttypes.h>
+
+namespace lldb_private {
+class SectionList;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+AddressRange::AddressRange() : m_base_addr(), m_byte_size(0) {}
+
+AddressRange::AddressRange(addr_t file_addr, addr_t byte_size,
+ const SectionList *section_list)
+ : m_base_addr(file_addr, section_list), m_byte_size(byte_size) {}
+
+AddressRange::AddressRange(const lldb::SectionSP &section, addr_t offset,
+ addr_t byte_size)
+ : m_base_addr(section, offset), m_byte_size(byte_size) {}
+
+AddressRange::AddressRange(const Address &so_addr, addr_t byte_size)
+ : m_base_addr(so_addr), m_byte_size(byte_size) {}
+
+AddressRange::~AddressRange() {}
+
+// bool
+// AddressRange::Contains (const Address &addr) const
+//{
+// const addr_t byte_size = GetByteSize();
+// if (byte_size)
+// return addr.GetSection() == m_base_addr.GetSection() &&
+// (addr.GetOffset() - m_base_addr.GetOffset()) < byte_size;
+//}
+//
+// bool
+// AddressRange::Contains (const Address *addr) const
+//{
+// if (addr)
+// return Contains (*addr);
+// return false;
+//}
+
+bool AddressRange::ContainsFileAddress(const Address &addr) const {
+ if (addr.GetSection() == m_base_addr.GetSection())
+ return (addr.GetOffset() - m_base_addr.GetOffset()) < GetByteSize();
+ addr_t file_base_addr = GetBaseAddress().GetFileAddress();
+ if (file_base_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ addr_t file_addr = addr.GetFileAddress();
+ if (file_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (file_base_addr <= file_addr)
+ return (file_addr - file_base_addr) < GetByteSize();
+
+ return false;
+}
+
+bool AddressRange::ContainsFileAddress(addr_t file_addr) const {
+ if (file_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ addr_t file_base_addr = GetBaseAddress().GetFileAddress();
+ if (file_base_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (file_base_addr <= file_addr)
+ return (file_addr - file_base_addr) < GetByteSize();
+
+ return false;
+}
+
+bool AddressRange::ContainsLoadAddress(const Address &addr,
+ Target *target) const {
+ if (addr.GetSection() == m_base_addr.GetSection())
+ return (addr.GetOffset() - m_base_addr.GetOffset()) < GetByteSize();
+ addr_t load_base_addr = GetBaseAddress().GetLoadAddress(target);
+ if (load_base_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ addr_t load_addr = addr.GetLoadAddress(target);
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (load_base_addr <= load_addr)
+ return (load_addr - load_base_addr) < GetByteSize();
+
+ return false;
+}
+
+bool AddressRange::ContainsLoadAddress(addr_t load_addr, Target *target) const {
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ addr_t load_base_addr = GetBaseAddress().GetLoadAddress(target);
+ if (load_base_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (load_base_addr <= load_addr)
+ return (load_addr - load_base_addr) < GetByteSize();
+
+ return false;
+}
+
+bool AddressRange::Extend(const AddressRange &rhs_range) {
+ addr_t lhs_end_addr = GetBaseAddress().GetFileAddress() + GetByteSize();
+ addr_t rhs_base_addr = rhs_range.GetBaseAddress().GetFileAddress();
+
+ if (!ContainsFileAddress(rhs_range.GetBaseAddress()) &&
+ lhs_end_addr != rhs_base_addr)
+ // The ranges don't intersect at all on the right side of this range.
+ return false;
+
+ addr_t rhs_end_addr = rhs_base_addr + rhs_range.GetByteSize();
+ if (lhs_end_addr >= rhs_end_addr)
+ // The rhs range totally overlaps this one, nothing to add.
+ return false;
+
+ m_byte_size += rhs_end_addr - lhs_end_addr;
+ return true;
+}
+
+void AddressRange::Clear() {
+ m_base_addr.Clear();
+ m_byte_size = 0;
+}
+
+bool AddressRange::Dump(Stream *s, Target *target, Address::DumpStyle style,
+ Address::DumpStyle fallback_style) const {
+ addr_t vmaddr = LLDB_INVALID_ADDRESS;
+ int addr_size = sizeof(addr_t);
+ if (target)
+ addr_size = target->GetArchitecture().GetAddressByteSize();
+
+ bool show_module = false;
+ switch (style) {
+ default:
+ break;
+ case Address::DumpStyleSectionNameOffset:
+ case Address::DumpStyleSectionPointerOffset:
+ s->PutChar('[');
+ m_base_addr.Dump(s, target, style, fallback_style);
+ s->PutChar('-');
+ s->Address(m_base_addr.GetOffset() + GetByteSize(), addr_size);
+ s->PutChar(')');
+ return true;
+ break;
+
+ case Address::DumpStyleModuleWithFileAddress:
+ show_module = true;
+ LLVM_FALLTHROUGH;
+ case Address::DumpStyleFileAddress:
+ vmaddr = m_base_addr.GetFileAddress();
+ break;
+
+ case Address::DumpStyleLoadAddress:
+ vmaddr = m_base_addr.GetLoadAddress(target);
+ break;
+ }
+
+ if (vmaddr != LLDB_INVALID_ADDRESS) {
+ if (show_module) {
+ ModuleSP module_sp(GetBaseAddress().GetModule());
+ if (module_sp)
+ s->Printf("%s", module_sp->GetFileSpec().GetFilename().AsCString(
+ "<Unknown>"));
+ }
+ s->AddressRange(vmaddr, vmaddr + GetByteSize(), addr_size);
+ return true;
+ } else if (fallback_style != Address::DumpStyleInvalid) {
+ return Dump(s, target, fallback_style, Address::DumpStyleInvalid);
+ }
+
+ return false;
+}
+
+void AddressRange::DumpDebug(Stream *s) const {
+ s->Printf("%p: AddressRange section = %p, offset = 0x%16.16" PRIx64
+ ", byte_size = 0x%16.16" PRIx64 "\n",
+ static_cast<const void *>(this),
+ static_cast<void *>(m_base_addr.GetSection().get()),
+ m_base_addr.GetOffset(), GetByteSize());
+}
+//
+// bool
+// lldb::operator== (const AddressRange& lhs, const AddressRange& rhs)
+//{
+// if (lhs.GetBaseAddress() == rhs.GetBaseAddress())
+// return lhs.GetByteSize() == rhs.GetByteSize();
+// return false;
+//}
diff --git a/contrib/llvm-project/lldb/source/Core/AddressResolver.cpp b/contrib/llvm-project/lldb/source/Core/AddressResolver.cpp
new file mode 100644
index 000000000000..974d99b62065
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/AddressResolver.cpp
@@ -0,0 +1,43 @@
+//===-- AddressResolver.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/AddressResolver.h"
+
+#include "lldb/Core/SearchFilter.h"
+
+namespace lldb_private {
+class ModuleList;
+}
+
+using namespace lldb_private;
+
+// AddressResolver:
+AddressResolver::AddressResolver() {}
+
+AddressResolver::~AddressResolver() {}
+
+void AddressResolver::ResolveAddressInModules(SearchFilter &filter,
+ ModuleList &modules) {
+ filter.SearchInModuleList(*this, modules);
+}
+
+void AddressResolver::ResolveAddress(SearchFilter &filter) {
+ filter.Search(*this);
+}
+
+std::vector<AddressRange> &AddressResolver::GetAddressRanges() {
+ return m_address_ranges;
+}
+
+size_t AddressResolver::GetNumberOfAddresses() {
+ return m_address_ranges.size();
+}
+
+AddressRange &AddressResolver::GetAddressRangeAtIndex(size_t idx) {
+ return m_address_ranges[idx];
+}
diff --git a/contrib/llvm-project/lldb/source/Core/AddressResolverFileLine.cpp b/contrib/llvm-project/lldb/source/Core/AddressResolverFileLine.cpp
new file mode 100644
index 000000000000..24c0222d6ec2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/AddressResolverFileLine.cpp
@@ -0,0 +1,85 @@
+//===-- AddressResolverFileLine.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/AddressResolverFileLine.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-types.h"
+
+#include <inttypes.h>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// AddressResolverFileLine:
+AddressResolverFileLine::AddressResolverFileLine(const FileSpec &file_spec,
+ uint32_t line_no,
+ bool check_inlines)
+ : AddressResolver(), m_file_spec(file_spec), m_line_number(line_no),
+ m_inlines(check_inlines) {}
+
+AddressResolverFileLine::~AddressResolverFileLine() {}
+
+Searcher::CallbackReturn
+AddressResolverFileLine::SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) {
+ SymbolContextList sc_list;
+ uint32_t sc_list_size;
+ CompileUnit *cu = context.comp_unit;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+ sc_list_size =
+ cu->ResolveSymbolContext(m_file_spec, m_line_number, m_inlines, false,
+ eSymbolContextEverything, sc_list);
+ for (uint32_t i = 0; i < sc_list_size; i++) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(i, sc)) {
+ Address line_start = sc.line_entry.range.GetBaseAddress();
+ addr_t byte_size = sc.line_entry.range.GetByteSize();
+ if (line_start.IsValid()) {
+ AddressRange new_range(line_start, byte_size);
+ m_address_ranges.push_back(new_range);
+ if (log) {
+ StreamString s;
+ // new_bp_loc->GetDescription (&s, lldb::eDescriptionLevelVerbose);
+ // log->Printf ("Added address: %s\n", s.GetData());
+ }
+ } else {
+ if (log)
+ log->Printf(
+ "error: Unable to resolve address at file address 0x%" PRIx64
+ " for %s:%d\n",
+ line_start.GetFileAddress(),
+ m_file_spec.GetFilename().AsCString("<Unknown>"), m_line_number);
+ }
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth AddressResolverFileLine::GetDepth() {
+ return lldb::eSearchDepthCompUnit;
+}
+
+void AddressResolverFileLine::GetDescription(Stream *s) {
+ s->Printf("File and line address - file: \"%s\" line: %u",
+ m_file_spec.GetFilename().AsCString("<Unknown>"), m_line_number);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/AddressResolverName.cpp b/contrib/llvm-project/lldb/source/Core/AddressResolverName.cpp
new file mode 100644
index 000000000000..e861368c0a25
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/AddressResolverName.cpp
@@ -0,0 +1,199 @@
+//===-- AddressResolverName.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/AddressResolverName.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <stdint.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+AddressResolverName::AddressResolverName(const char *func_name,
+ AddressResolver::MatchType type)
+ : AddressResolver(), m_func_name(func_name), m_class_name(nullptr),
+ m_regex(), m_match_type(type) {
+ if (m_match_type == AddressResolver::Regexp) {
+ if (!m_regex.Compile(m_func_name.GetStringRef())) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+ if (log)
+ log->Warning("function name regexp: \"%s\" did not compile.",
+ m_func_name.AsCString());
+ }
+ }
+}
+
+AddressResolverName::AddressResolverName(RegularExpression &func_regex)
+ : AddressResolver(), m_func_name(nullptr), m_class_name(nullptr),
+ m_regex(func_regex), m_match_type(AddressResolver::Regexp) {}
+
+AddressResolverName::AddressResolverName(const char *class_name,
+ const char *method,
+ AddressResolver::MatchType type)
+ : AddressResolver(), m_func_name(method), m_class_name(class_name),
+ m_regex(), m_match_type(type) {}
+
+AddressResolverName::~AddressResolverName() = default;
+
+// FIXME: Right now we look at the module level, and call the module's
+// "FindFunctions".
+// Greg says he will add function tables, maybe at the CompileUnit level to
+// accelerate function lookup. At that point, we should switch the depth to
+// CompileUnit, and look in these tables.
+
+Searcher::CallbackReturn
+AddressResolverName::SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) {
+ SymbolContextList func_list;
+ SymbolContextList sym_list;
+
+ bool skip_prologue = true;
+ uint32_t i;
+ SymbolContext sc;
+ Address func_addr;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+ if (m_class_name) {
+ if (log)
+ log->Warning("Class/method function specification not supported yet.\n");
+ return Searcher::eCallbackReturnStop;
+ }
+
+ const bool include_symbols = false;
+ const bool include_inlines = true;
+ const bool append = false;
+ switch (m_match_type) {
+ case AddressResolver::Exact:
+ if (context.module_sp) {
+ context.module_sp->FindSymbolsWithNameAndType(m_func_name,
+ eSymbolTypeCode, sym_list);
+ context.module_sp->FindFunctions(m_func_name, nullptr,
+ eFunctionNameTypeAuto, include_symbols,
+ include_inlines, append, func_list);
+ }
+ break;
+
+ case AddressResolver::Regexp:
+ if (context.module_sp) {
+ context.module_sp->FindSymbolsMatchingRegExAndType(
+ m_regex, eSymbolTypeCode, sym_list);
+ context.module_sp->FindFunctions(m_regex, include_symbols,
+ include_inlines, append, func_list);
+ }
+ break;
+
+ case AddressResolver::Glob:
+ if (log)
+ log->Warning("glob is not supported yet.");
+ break;
+ }
+
+ // Remove any duplicates between the function list and the symbol list
+ if (func_list.GetSize()) {
+ for (i = 0; i < func_list.GetSize(); i++) {
+ if (!func_list.GetContextAtIndex(i, sc))
+ continue;
+
+ if (sc.function == nullptr)
+ continue;
+ uint32_t j = 0;
+ while (j < sym_list.GetSize()) {
+ SymbolContext symbol_sc;
+ if (sym_list.GetContextAtIndex(j, symbol_sc)) {
+ if (symbol_sc.symbol && symbol_sc.symbol->ValueIsAddress()) {
+ if (sc.function->GetAddressRange().GetBaseAddress() ==
+ symbol_sc.symbol->GetAddressRef()) {
+ sym_list.RemoveContextAtIndex(j);
+ continue; // Don't increment j
+ }
+ }
+ }
+
+ j++;
+ }
+ }
+
+ for (i = 0; i < func_list.GetSize(); i++) {
+ if (func_list.GetContextAtIndex(i, sc)) {
+ if (sc.function) {
+ func_addr = sc.function->GetAddressRange().GetBaseAddress();
+ addr_t byte_size = sc.function->GetAddressRange().GetByteSize();
+ if (skip_prologue) {
+ const uint32_t prologue_byte_size =
+ sc.function->GetPrologueByteSize();
+ if (prologue_byte_size) {
+ func_addr.SetOffset(func_addr.GetOffset() + prologue_byte_size);
+ byte_size -= prologue_byte_size;
+ }
+ }
+
+ if (filter.AddressPasses(func_addr)) {
+ AddressRange new_range(func_addr, byte_size);
+ m_address_ranges.push_back(new_range);
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < sym_list.GetSize(); i++) {
+ if (sym_list.GetContextAtIndex(i, sc)) {
+ if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ func_addr = sc.symbol->GetAddressRef();
+ addr_t byte_size = sc.symbol->GetByteSize();
+
+ if (skip_prologue) {
+ const uint32_t prologue_byte_size = sc.symbol->GetPrologueByteSize();
+ if (prologue_byte_size) {
+ func_addr.SetOffset(func_addr.GetOffset() + prologue_byte_size);
+ byte_size -= prologue_byte_size;
+ }
+ }
+
+ if (filter.AddressPasses(func_addr)) {
+ AddressRange new_range(func_addr, byte_size);
+ m_address_ranges.push_back(new_range);
+ }
+ }
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth AddressResolverName::GetDepth() {
+ return lldb::eSearchDepthModule;
+}
+
+void AddressResolverName::GetDescription(Stream *s) {
+ s->PutCString("Address by function name: ");
+
+ if (m_match_type == AddressResolver::Regexp)
+ s->Printf("'%s' (regular expression)", m_regex.GetText().str().c_str());
+ else
+ s->Printf("'%s'", m_func_name.AsCString());
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Communication.cpp b/contrib/llvm-project/lldb/source/Core/Communication.cpp
new file mode 100644
index 000000000000..a67cb925d648
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Communication.cpp
@@ -0,0 +1,434 @@
+//===-- Communication.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Communication.h"
+
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Utility/Connection.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/Listener.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Compiler.h"
+
+#include <algorithm>
+#include <chrono>
+#include <cstring>
+#include <memory>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+ConstString &Communication::GetStaticBroadcasterClass() {
+ static ConstString class_name("lldb.communication");
+ return class_name;
+}
+
+Communication::Communication(const char *name)
+ : Broadcaster(nullptr, name), m_connection_sp(),
+ m_read_thread_enabled(false), m_read_thread_did_exit(false), m_bytes(),
+ m_bytes_mutex(), m_write_mutex(), m_synchronize_mutex(),
+ m_callback(nullptr), m_callback_baton(nullptr), m_close_on_eof(true)
+
+{
+ lldb_private::LogIfAnyCategoriesSet(
+ LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_COMMUNICATION,
+ "%p Communication::Communication (name = %s)", this, name);
+
+ SetEventName(eBroadcastBitDisconnected, "disconnected");
+ SetEventName(eBroadcastBitReadThreadGotBytes, "got bytes");
+ SetEventName(eBroadcastBitReadThreadDidExit, "read thread did exit");
+ SetEventName(eBroadcastBitReadThreadShouldExit, "read thread should exit");
+ SetEventName(eBroadcastBitPacketAvailable, "packet available");
+ SetEventName(eBroadcastBitNoMorePendingInput, "no more pending input");
+
+ CheckInWithManager();
+}
+
+Communication::~Communication() {
+ lldb_private::LogIfAnyCategoriesSet(
+ LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_COMMUNICATION,
+ "%p Communication::~Communication (name = %s)", this,
+ GetBroadcasterName().AsCString());
+ Clear();
+}
+
+void Communication::Clear() {
+ SetReadThreadBytesReceivedCallback(nullptr, nullptr);
+ Disconnect(nullptr);
+ StopReadThread(nullptr);
+}
+
+ConnectionStatus Communication::Connect(const char *url, Status *error_ptr) {
+ Clear();
+
+ lldb_private::LogIfAnyCategoriesSet(LIBLLDB_LOG_COMMUNICATION,
+ "%p Communication::Connect (url = %s)",
+ this, url);
+
+ lldb::ConnectionSP connection_sp(m_connection_sp);
+ if (connection_sp)
+ return connection_sp->Connect(url, error_ptr);
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid connection.");
+ return eConnectionStatusNoConnection;
+}
+
+ConnectionStatus Communication::Disconnect(Status *error_ptr) {
+ lldb_private::LogIfAnyCategoriesSet(LIBLLDB_LOG_COMMUNICATION,
+ "%p Communication::Disconnect ()", this);
+
+ lldb::ConnectionSP connection_sp(m_connection_sp);
+ if (connection_sp) {
+ ConnectionStatus status = connection_sp->Disconnect(error_ptr);
+ // We currently don't protect connection_sp with any mutex for multi-
+ // threaded environments. So lets not nuke our connection class without
+ // putting some multi-threaded protections in. We also probably don't want
+ // to pay for the overhead it might cause if every time we access the
+ // connection we have to take a lock.
+ //
+ // This unique pointer will cleanup after itself when this object goes
+ // away, so there is no need to currently have it destroy itself
+ // immediately upon disconnect.
+ // connection_sp.reset();
+ return status;
+ }
+ return eConnectionStatusNoConnection;
+}
+
+bool Communication::IsConnected() const {
+ lldb::ConnectionSP connection_sp(m_connection_sp);
+ return (connection_sp ? connection_sp->IsConnected() : false);
+}
+
+bool Communication::HasConnection() const {
+ return m_connection_sp.get() != nullptr;
+}
+
+size_t Communication::Read(void *dst, size_t dst_len,
+ const Timeout<std::micro> &timeout,
+ ConnectionStatus &status, Status *error_ptr) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION);
+ LLDB_LOG(
+ log,
+ "this = {0}, dst = {1}, dst_len = {2}, timeout = {3}, connection = {4}",
+ this, dst, dst_len, timeout, m_connection_sp.get());
+
+ if (m_read_thread_enabled) {
+ // We have a dedicated read thread that is getting data for us
+ size_t cached_bytes = GetCachedBytes(dst, dst_len);
+ if (cached_bytes > 0 || (timeout && timeout->count() == 0)) {
+ status = eConnectionStatusSuccess;
+ return cached_bytes;
+ }
+
+ if (!m_connection_sp) {
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid connection.");
+ status = eConnectionStatusNoConnection;
+ return 0;
+ }
+
+ ListenerSP listener_sp(Listener::MakeListener("Communication::Read"));
+ listener_sp->StartListeningForEvents(
+ this, eBroadcastBitReadThreadGotBytes | eBroadcastBitReadThreadDidExit);
+ EventSP event_sp;
+ while (listener_sp->GetEvent(event_sp, timeout)) {
+ const uint32_t event_type = event_sp->GetType();
+ if (event_type & eBroadcastBitReadThreadGotBytes) {
+ return GetCachedBytes(dst, dst_len);
+ }
+
+ if (event_type & eBroadcastBitReadThreadDidExit) {
+ if (GetCloseOnEOF())
+ Disconnect(nullptr);
+ break;
+ }
+ }
+ return 0;
+ }
+
+ // We aren't using a read thread, just read the data synchronously in this
+ // thread.
+ return ReadFromConnection(dst, dst_len, timeout, status, error_ptr);
+}
+
+size_t Communication::Write(const void *src, size_t src_len,
+ ConnectionStatus &status, Status *error_ptr) {
+ lldb::ConnectionSP connection_sp(m_connection_sp);
+
+ std::lock_guard<std::mutex> guard(m_write_mutex);
+ lldb_private::LogIfAnyCategoriesSet(
+ LIBLLDB_LOG_COMMUNICATION,
+ "%p Communication::Write (src = %p, src_len = %" PRIu64
+ ") connection = %p",
+ this, src, (uint64_t)src_len, connection_sp.get());
+
+ if (connection_sp)
+ return connection_sp->Write(src, src_len, status, error_ptr);
+
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid connection.");
+ status = eConnectionStatusNoConnection;
+ return 0;
+}
+
+bool Communication::StartReadThread(Status *error_ptr) {
+ if (error_ptr)
+ error_ptr->Clear();
+
+ if (m_read_thread.IsJoinable())
+ return true;
+
+ lldb_private::LogIfAnyCategoriesSet(
+ LIBLLDB_LOG_COMMUNICATION, "%p Communication::StartReadThread ()", this);
+
+ char thread_name[1024];
+ snprintf(thread_name, sizeof(thread_name), "<lldb.comm.%s>",
+ GetBroadcasterName().AsCString());
+
+ m_read_thread_enabled = true;
+ m_read_thread_did_exit = false;
+ auto maybe_thread = ThreadLauncher::LaunchThread(
+ thread_name, Communication::ReadThread, this);
+ if (maybe_thread) {
+ m_read_thread = *maybe_thread;
+ } else {
+ if (error_ptr)
+ *error_ptr = Status(maybe_thread.takeError());
+ else {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(maybe_thread.takeError()));
+ }
+ }
+
+ if (!m_read_thread.IsJoinable())
+ m_read_thread_enabled = false;
+
+ return m_read_thread_enabled;
+}
+
+bool Communication::StopReadThread(Status *error_ptr) {
+ if (!m_read_thread.IsJoinable())
+ return true;
+
+ lldb_private::LogIfAnyCategoriesSet(
+ LIBLLDB_LOG_COMMUNICATION, "%p Communication::StopReadThread ()", this);
+
+ m_read_thread_enabled = false;
+
+ BroadcastEvent(eBroadcastBitReadThreadShouldExit, nullptr);
+
+ // error = m_read_thread.Cancel();
+
+ Status error = m_read_thread.Join(nullptr);
+ return error.Success();
+}
+
+bool Communication::JoinReadThread(Status *error_ptr) {
+ if (!m_read_thread.IsJoinable())
+ return true;
+
+ Status error = m_read_thread.Join(nullptr);
+ return error.Success();
+}
+
+size_t Communication::GetCachedBytes(void *dst, size_t dst_len) {
+ std::lock_guard<std::recursive_mutex> guard(m_bytes_mutex);
+ if (!m_bytes.empty()) {
+ // If DST is nullptr and we have a thread, then return the number of bytes
+ // that are available so the caller can call again
+ if (dst == nullptr)
+ return m_bytes.size();
+
+ const size_t len = std::min<size_t>(dst_len, m_bytes.size());
+
+ ::memcpy(dst, m_bytes.c_str(), len);
+ m_bytes.erase(m_bytes.begin(), m_bytes.begin() + len);
+
+ return len;
+ }
+ return 0;
+}
+
+void Communication::AppendBytesToCache(const uint8_t *bytes, size_t len,
+ bool broadcast,
+ ConnectionStatus status) {
+ lldb_private::LogIfAnyCategoriesSet(
+ LIBLLDB_LOG_COMMUNICATION,
+ "%p Communication::AppendBytesToCache (src = %p, src_len = %" PRIu64
+ ", broadcast = %i)",
+ this, bytes, (uint64_t)len, broadcast);
+ if ((bytes == nullptr || len == 0) &&
+ (status != lldb::eConnectionStatusEndOfFile))
+ return;
+ if (m_callback) {
+ // If the user registered a callback, then call it and do not broadcast
+ m_callback(m_callback_baton, bytes, len);
+ } else if (bytes != nullptr && len > 0) {
+ std::lock_guard<std::recursive_mutex> guard(m_bytes_mutex);
+ m_bytes.append((const char *)bytes, len);
+ if (broadcast)
+ BroadcastEventIfUnique(eBroadcastBitReadThreadGotBytes);
+ }
+}
+
+size_t Communication::ReadFromConnection(void *dst, size_t dst_len,
+ const Timeout<std::micro> &timeout,
+ ConnectionStatus &status,
+ Status *error_ptr) {
+ lldb::ConnectionSP connection_sp(m_connection_sp);
+ if (connection_sp)
+ return connection_sp->Read(dst, dst_len, timeout, status, error_ptr);
+
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid connection.");
+ status = eConnectionStatusNoConnection;
+ return 0;
+}
+
+bool Communication::ReadThreadIsRunning() { return m_read_thread_enabled; }
+
+lldb::thread_result_t Communication::ReadThread(lldb::thread_arg_t p) {
+ Communication *comm = (Communication *)p;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION));
+
+ if (log)
+ log->Printf("%p Communication::ReadThread () thread starting...", p);
+
+ uint8_t buf[1024];
+
+ Status error;
+ ConnectionStatus status = eConnectionStatusSuccess;
+ bool done = false;
+ while (!done && comm->m_read_thread_enabled) {
+ size_t bytes_read = comm->ReadFromConnection(
+ buf, sizeof(buf), std::chrono::seconds(5), status, &error);
+ if (bytes_read > 0)
+ comm->AppendBytesToCache(buf, bytes_read, true, status);
+ else if ((bytes_read == 0) && status == eConnectionStatusEndOfFile) {
+ if (comm->GetCloseOnEOF())
+ comm->Disconnect();
+ comm->AppendBytesToCache(buf, bytes_read, true, status);
+ }
+
+ switch (status) {
+ case eConnectionStatusSuccess:
+ break;
+
+ case eConnectionStatusEndOfFile:
+ done = true;
+ break;
+ case eConnectionStatusError: // Check GetError() for details
+ if (error.GetType() == eErrorTypePOSIX && error.GetError() == EIO) {
+ // EIO on a pipe is usually caused by remote shutdown
+ comm->Disconnect();
+ done = true;
+ }
+ if (error.Fail())
+ LLDB_LOG(log, "error: {0}, status = {1}", error,
+ Communication::ConnectionStatusAsCString(status));
+ break;
+ case eConnectionStatusInterrupted: // Synchronization signal from
+ // SynchronizeWithReadThread()
+ // The connection returns eConnectionStatusInterrupted only when there is
+ // no input pending to be read, so we can signal that.
+ comm->BroadcastEvent(eBroadcastBitNoMorePendingInput);
+ break;
+ case eConnectionStatusNoConnection: // No connection
+ case eConnectionStatusLostConnection: // Lost connection while connected to
+ // a valid connection
+ done = true;
+ LLVM_FALLTHROUGH;
+ case eConnectionStatusTimedOut: // Request timed out
+ if (error.Fail())
+ LLDB_LOG(log, "error: {0}, status = {1}", error,
+ Communication::ConnectionStatusAsCString(status));
+ break;
+ }
+ }
+ log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION);
+ if (log)
+ log->Printf("%p Communication::ReadThread () thread exiting...", p);
+
+ comm->m_read_thread_did_exit = true;
+ // Let clients know that this thread is exiting
+ comm->BroadcastEvent(eBroadcastBitNoMorePendingInput);
+ comm->BroadcastEvent(eBroadcastBitReadThreadDidExit);
+ return {};
+}
+
+void Communication::SetReadThreadBytesReceivedCallback(
+ ReadThreadBytesReceived callback, void *callback_baton) {
+ m_callback = callback;
+ m_callback_baton = callback_baton;
+}
+
+void Communication::SynchronizeWithReadThread() {
+ // Only one thread can do the synchronization dance at a time.
+ std::lock_guard<std::mutex> guard(m_synchronize_mutex);
+
+ // First start listening for the synchronization event.
+ ListenerSP listener_sp(
+ Listener::MakeListener("Communication::SyncronizeWithReadThread"));
+ listener_sp->StartListeningForEvents(this, eBroadcastBitNoMorePendingInput);
+
+ // If the thread is not running, there is no point in synchronizing.
+ if (!m_read_thread_enabled || m_read_thread_did_exit)
+ return;
+
+ // Notify the read thread.
+ m_connection_sp->InterruptRead();
+
+ // Wait for the synchronization event.
+ EventSP event_sp;
+ listener_sp->GetEvent(event_sp, llvm::None);
+}
+
+void Communication::SetConnection(Connection *connection) {
+ Disconnect(nullptr);
+ StopReadThread(nullptr);
+ m_connection_sp.reset(connection);
+}
+
+const char *
+Communication::ConnectionStatusAsCString(lldb::ConnectionStatus status) {
+ switch (status) {
+ case eConnectionStatusSuccess:
+ return "success";
+ case eConnectionStatusError:
+ return "error";
+ case eConnectionStatusTimedOut:
+ return "timed out";
+ case eConnectionStatusNoConnection:
+ return "no connection";
+ case eConnectionStatusLostConnection:
+ return "lost connection";
+ case eConnectionStatusEndOfFile:
+ return "end of file";
+ case eConnectionStatusInterrupted:
+ return "interrupted";
+ }
+
+ static char unknown_state_string[64];
+ snprintf(unknown_state_string, sizeof(unknown_state_string),
+ "ConnectionStatus = %i", status);
+ return unknown_state_string;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Debugger.cpp b/contrib/llvm-project/lldb/source/Core/Debugger.cpp
new file mode 100644
index 000000000000..1a69fc582d0c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Debugger.cpp
@@ -0,0 +1,1773 @@
+//===-- Debugger.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Debugger.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Core/FormatEntity.h"
+#include "lldb/Core/Mangled.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamAsynchronousIO.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Expression/REPL.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/Terminal.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/OptionValueSInt64.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StructuredDataPlugin.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Utility/AnsiTerminal.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/Listener.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamCallback.h"
+#include "lldb/Utility/StreamString.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/PosixApi.h"
+#include "lldb/Host/windows/windows.h"
+#endif
+
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <list>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#include <system_error>
+
+namespace lldb_private {
+class Address;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+static lldb::user_id_t g_unique_id = 1;
+static size_t g_debugger_event_thread_stack_bytes = 8 * 1024 * 1024;
+
+#pragma mark Static Functions
+
+typedef std::vector<DebuggerSP> DebuggerList;
+static std::recursive_mutex *g_debugger_list_mutex_ptr =
+ nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain
+static DebuggerList *g_debugger_list_ptr =
+ nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain
+
+static constexpr OptionEnumValueElement g_show_disassembly_enum_values[] = {
+ {Debugger::eStopDisassemblyTypeNever, "never",
+ "Never show disassembly when displaying a stop context."},
+ {Debugger::eStopDisassemblyTypeNoDebugInfo, "no-debuginfo",
+ "Show disassembly when there is no debug information."},
+ {Debugger::eStopDisassemblyTypeNoSource, "no-source",
+ "Show disassembly when there is no source information, or the source file "
+ "is missing when displaying a stop context."},
+ {Debugger::eStopDisassemblyTypeAlways, "always",
+ "Always show disassembly when displaying a stop context."} };
+
+static constexpr OptionEnumValueElement g_language_enumerators[] = {
+ {eScriptLanguageNone, "none", "Disable scripting languages."},
+ {eScriptLanguagePython, "python",
+ "Select python as the default scripting language."},
+ {eScriptLanguageDefault, "default",
+ "Select the lldb default as the default scripting language."} };
+
+#define MODULE_WITH_FUNC \
+ "{ " \
+ "${module.file.basename}{`${function.name-with-args}" \
+ "{${frame.no-debug}${function.pc-offset}}}}"
+
+#define MODULE_WITH_FUNC_NO_ARGS \
+ "{ " \
+ "${module.file.basename}{`${function.name-without-args}" \
+ "{${frame.no-debug}${function.pc-offset}}}}"
+
+#define FILE_AND_LINE \
+ "{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}" \
+ ":${ansi.fg.yellow}${line.number}${ansi.normal}" \
+ "{:${ansi.fg.yellow}${line.column}${ansi.normal}}}"
+
+#define IS_OPTIMIZED "{${function.is-optimized} [opt]}"
+
+#define IS_ARTIFICIAL "{${frame.is-artificial} [artificial]}"
+
+#define DEFAULT_THREAD_FORMAT \
+ "thread #${thread.index}: tid = ${thread.id%tid}" \
+ "{, ${frame.pc}}" MODULE_WITH_FUNC FILE_AND_LINE \
+ "{, name = ${ansi.fg.green}'${thread.name}'${ansi.normal}}" \
+ "{, queue = ${ansi.fg.green}'${thread.queue}'${ansi.normal}}" \
+ "{, activity = " \
+ "${ansi.fg.green}'${thread.info.activity.name}'${ansi.normal}}" \
+ "{, ${thread.info.trace_messages} messages}" \
+ "{, stop reason = ${ansi.fg.red}${thread.stop-reason}${ansi.normal}}" \
+ "{\\nReturn value: ${thread.return-value}}" \
+ "{\\nCompleted expression: ${thread.completed-expression}}" \
+ "\\n"
+
+#define DEFAULT_THREAD_STOP_FORMAT \
+ "thread #${thread.index}{, name = '${thread.name}'}" \
+ "{, queue = ${ansi.fg.green}'${thread.queue}'${ansi.normal}}" \
+ "{, activity = " \
+ "${ansi.fg.green}'${thread.info.activity.name}'${ansi.normal}}" \
+ "{, ${thread.info.trace_messages} messages}" \
+ "{, stop reason = ${ansi.fg.red}${thread.stop-reason}${ansi.normal}}" \
+ "{\\nReturn value: ${thread.return-value}}" \
+ "{\\nCompleted expression: ${thread.completed-expression}}" \
+ "\\n"
+
+#define DEFAULT_FRAME_FORMAT \
+ "frame #${frame.index}: " \
+ "${ansi.fg.yellow}${frame.pc}${ansi.normal}" MODULE_WITH_FUNC FILE_AND_LINE \
+ IS_OPTIMIZED IS_ARTIFICIAL "\\n"
+
+#define DEFAULT_FRAME_FORMAT_NO_ARGS \
+ "frame #${frame.index}: " \
+ "${ansi.fg.yellow}${frame.pc}${ansi.normal}" MODULE_WITH_FUNC_NO_ARGS \
+ FILE_AND_LINE IS_OPTIMIZED IS_ARTIFICIAL "\\n"
+
+// Three parts to this disassembly format specification:
+// 1. If this is a new function/symbol (no previous symbol/function), print
+// dylib`funcname:\n
+// 2. If this is a symbol context change (different from previous
+// symbol/function), print
+// dylib`funcname:\n
+// 3. print
+// address <+offset>:
+#define DEFAULT_DISASSEMBLY_FORMAT \
+ "{${function.initial-function}{${module.file.basename}`}{${function.name-" \
+ "without-args}}:\\n}{${function.changed}\\n{${module.file.basename}`}{${" \
+ "function.name-without-args}}:\\n}{${current-pc-arrow} " \
+ "}${addr-file-or-load}{ " \
+ "<${function.concrete-only-addr-offset-no-padding}>}: "
+
+// gdb's disassembly format can be emulated with ${current-pc-arrow}${addr-
+// file-or-load}{ <${function.name-without-args}${function.concrete-only-addr-
+// offset-no-padding}>}:
+
+// lldb's original format for disassembly would look like this format string -
+// {${function.initial-function}{${module.file.basename}`}{${function.name-
+// without-
+// args}}:\n}{${function.changed}\n{${module.file.basename}`}{${function.name-
+// without-args}}:\n}{${current-pc-arrow} }{${addr-file-or-load}}:
+
+static constexpr OptionEnumValueElement s_stop_show_column_values[] = {
+ {eStopShowColumnAnsiOrCaret, "ansi-or-caret",
+ "Highlight the stop column with ANSI terminal codes when color/ANSI mode "
+ "is enabled; otherwise, fall back to using a text-only caret (^) as if "
+ "\"caret-only\" mode was selected."},
+ {eStopShowColumnAnsi, "ansi", "Highlight the stop column with ANSI "
+ "terminal codes when running LLDB with "
+ "color/ANSI enabled."},
+ {eStopShowColumnCaret, "caret",
+ "Highlight the stop column with a caret character (^) underneath the stop "
+ "column. This method introduces a new line in source listings that "
+ "display thread stop locations."},
+ {eStopShowColumnNone, "none", "Do not highlight the stop column."}};
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"auto-confirm", OptionValue::eTypeBoolean, true, false, nullptr, {},
+ "If true all confirmation prompts will receive their default reply."},
+ {"disassembly-format", OptionValue::eTypeFormatEntity, true, 0,
+ DEFAULT_DISASSEMBLY_FORMAT, {},
+ "The default disassembly format "
+ "string to use when disassembling "
+ "instruction sequences."},
+ {"frame-format", OptionValue::eTypeFormatEntity, true, 0,
+ DEFAULT_FRAME_FORMAT, {},
+ "The default frame format string to use "
+ "when displaying stack frame information "
+ "for threads."},
+ {"notify-void", OptionValue::eTypeBoolean, true, false, nullptr, {},
+ "Notify the user explicitly if an expression returns void (default: "
+ "false)."},
+ {"prompt", OptionValue::eTypeString, true,
+ OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ", {},
+ "The debugger command line prompt displayed for the user."},
+ {"script-lang", OptionValue::eTypeEnum, true, eScriptLanguagePython,
+ nullptr, OptionEnumValues(g_language_enumerators),
+ "The script language to be used for evaluating user-written scripts."},
+ {"stop-disassembly-count", OptionValue::eTypeSInt64, true, 4, nullptr, {},
+ "The number of disassembly lines to show when displaying a "
+ "stopped context."},
+ {"stop-disassembly-display", OptionValue::eTypeEnum, true,
+ Debugger::eStopDisassemblyTypeNoDebugInfo, nullptr,
+ OptionEnumValues(g_show_disassembly_enum_values),
+ "Control when to display disassembly when displaying a stopped context."},
+ {"stop-line-count-after", OptionValue::eTypeSInt64, true, 3, nullptr, {},
+ "The number of sources lines to display that come after the "
+ "current source line when displaying a stopped context."},
+ {"stop-line-count-before", OptionValue::eTypeSInt64, true, 3, nullptr, {},
+ "The number of sources lines to display that come before the "
+ "current source line when displaying a stopped context."},
+ {"highlight-source", OptionValue::eTypeBoolean, true, true, nullptr, {},
+ "If true, LLDB will highlight the displayed source code."},
+ {"stop-show-column", OptionValue::eTypeEnum, false,
+ eStopShowColumnAnsiOrCaret, nullptr, OptionEnumValues(s_stop_show_column_values),
+ "If true, LLDB will use the column information from the debug info to "
+ "mark the current position when displaying a stopped context."},
+ {"stop-show-column-ansi-prefix", OptionValue::eTypeString, true, 0,
+ "${ansi.underline}", {},
+ "When displaying the column marker in a color-enabled (i.e. ANSI) "
+ "terminal, use the ANSI terminal code specified in this format at the "
+ "immediately before the column to be marked."},
+ {"stop-show-column-ansi-suffix", OptionValue::eTypeString, true, 0,
+ "${ansi.normal}", {},
+ "When displaying the column marker in a color-enabled (i.e. ANSI) "
+ "terminal, use the ANSI terminal code specified in this format "
+ "immediately after the column to be marked."},
+ {"term-width", OptionValue::eTypeSInt64, true, 80, nullptr, {},
+ "The maximum number of columns to use for displaying text."},
+ {"thread-format", OptionValue::eTypeFormatEntity, true, 0,
+ DEFAULT_THREAD_FORMAT, {},
+ "The default thread format string to use "
+ "when displaying thread information."},
+ {"thread-stop-format", OptionValue::eTypeFormatEntity, true, 0,
+ DEFAULT_THREAD_STOP_FORMAT, {},
+ "The default thread format "
+ "string to use when displaying thread "
+ "information as part of the stop display."},
+ {"use-external-editor", OptionValue::eTypeBoolean, true, false, nullptr, {},
+ "Whether to use an external editor or not."},
+ {"use-color", OptionValue::eTypeBoolean, true, true, nullptr, {},
+ "Whether to use Ansi color codes or not."},
+ {"auto-one-line-summaries", OptionValue::eTypeBoolean, true, true, nullptr,
+ {},
+ "If true, LLDB will automatically display small structs in "
+ "one-liner format (default: true)."},
+ {"auto-indent", OptionValue::eTypeBoolean, true, true, nullptr, {},
+ "If true, LLDB will auto indent/outdent code. Currently only supported in "
+ "the REPL (default: true)."},
+ {"print-decls", OptionValue::eTypeBoolean, true, true, nullptr, {},
+ "If true, LLDB will print the values of variables declared in an "
+ "expression. Currently only supported in the REPL (default: true)."},
+ {"tab-size", OptionValue::eTypeUInt64, true, 4, nullptr, {},
+ "The tab size to use when indenting code in multi-line input mode "
+ "(default: 4)."},
+ {"escape-non-printables", OptionValue::eTypeBoolean, true, true, nullptr,
+ {},
+ "If true, LLDB will automatically escape non-printable and "
+ "escape characters when formatting strings."},
+ {"frame-format-unique", OptionValue::eTypeFormatEntity, true, 0,
+ DEFAULT_FRAME_FORMAT_NO_ARGS, {},
+ "The default frame format string to use when displaying stack frame"
+ "information for threads from thread backtrace unique."}};
+
+enum {
+ ePropertyAutoConfirm = 0,
+ ePropertyDisassemblyFormat,
+ ePropertyFrameFormat,
+ ePropertyNotiftVoid,
+ ePropertyPrompt,
+ ePropertyScriptLanguage,
+ ePropertyStopDisassemblyCount,
+ ePropertyStopDisassemblyDisplay,
+ ePropertyStopLineCountAfter,
+ ePropertyStopLineCountBefore,
+ ePropertyHighlightSource,
+ ePropertyStopShowColumn,
+ ePropertyStopShowColumnAnsiPrefix,
+ ePropertyStopShowColumnAnsiSuffix,
+ ePropertyTerminalWidth,
+ ePropertyThreadFormat,
+ ePropertyThreadStopFormat,
+ ePropertyUseExternalEditor,
+ ePropertyUseColor,
+ ePropertyAutoOneLineSummaries,
+ ePropertyAutoIndent,
+ ePropertyPrintDecls,
+ ePropertyTabSize,
+ ePropertyEscapeNonPrintables,
+ ePropertyFrameFormatUnique,
+};
+
+LoadPluginCallbackType Debugger::g_load_plugin_callback = nullptr;
+
+Status Debugger::SetPropertyValue(const ExecutionContext *exe_ctx,
+ VarSetOperationType op,
+ llvm::StringRef property_path,
+ llvm::StringRef value) {
+ bool is_load_script = (property_path == "target.load-script-from-symbol-file");
+ bool is_escape_non_printables = (property_path == "escape-non-printables");
+ TargetSP target_sp;
+ LoadScriptFromSymFile load_script_old_value;
+ if (is_load_script && exe_ctx->GetTargetSP()) {
+ target_sp = exe_ctx->GetTargetSP();
+ load_script_old_value =
+ target_sp->TargetProperties::GetLoadScriptFromSymbolFile();
+ }
+ Status error(Properties::SetPropertyValue(exe_ctx, op, property_path, value));
+ if (error.Success()) {
+ // FIXME it would be nice to have "on-change" callbacks for properties
+ if (property_path == g_properties[ePropertyPrompt].name) {
+ llvm::StringRef new_prompt = GetPrompt();
+ std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes(
+ new_prompt, GetUseColor());
+ if (str.length())
+ new_prompt = str;
+ GetCommandInterpreter().UpdatePrompt(new_prompt);
+ auto bytes = llvm::make_unique<EventDataBytes>(new_prompt);
+ auto prompt_change_event_sp = std::make_shared<Event>(
+ CommandInterpreter::eBroadcastBitResetPrompt, bytes.release());
+ GetCommandInterpreter().BroadcastEvent(prompt_change_event_sp);
+ } else if (property_path == g_properties[ePropertyUseColor].name) {
+ // use-color changed. Ping the prompt so it can reset the ansi terminal
+ // codes.
+ SetPrompt(GetPrompt());
+ } else if (is_load_script && target_sp &&
+ load_script_old_value == eLoadScriptFromSymFileWarn) {
+ if (target_sp->TargetProperties::GetLoadScriptFromSymbolFile() ==
+ eLoadScriptFromSymFileTrue) {
+ std::list<Status> errors;
+ StreamString feedback_stream;
+ if (!target_sp->LoadScriptingResources(errors, &feedback_stream)) {
+ StreamFileSP stream_sp(GetErrorFile());
+ if (stream_sp) {
+ for (auto error : errors) {
+ stream_sp->Printf("%s\n", error.AsCString());
+ }
+ if (feedback_stream.GetSize())
+ stream_sp->PutCString(feedback_stream.GetString());
+ }
+ }
+ }
+ } else if (is_escape_non_printables) {
+ DataVisualization::ForceUpdate();
+ }
+ }
+ return error;
+}
+
+bool Debugger::GetAutoConfirm() const {
+ const uint32_t idx = ePropertyAutoConfirm;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+const FormatEntity::Entry *Debugger::GetDisassemblyFormat() const {
+ const uint32_t idx = ePropertyDisassemblyFormat;
+ return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+}
+
+const FormatEntity::Entry *Debugger::GetFrameFormat() const {
+ const uint32_t idx = ePropertyFrameFormat;
+ return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+}
+
+const FormatEntity::Entry *Debugger::GetFrameFormatUnique() const {
+ const uint32_t idx = ePropertyFrameFormatUnique;
+ return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+}
+
+bool Debugger::GetNotifyVoid() const {
+ const uint32_t idx = ePropertyNotiftVoid;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+llvm::StringRef Debugger::GetPrompt() const {
+ const uint32_t idx = ePropertyPrompt;
+ return m_collection_sp->GetPropertyAtIndexAsString(
+ nullptr, idx, g_properties[idx].default_cstr_value);
+}
+
+void Debugger::SetPrompt(llvm::StringRef p) {
+ const uint32_t idx = ePropertyPrompt;
+ m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, p);
+ llvm::StringRef new_prompt = GetPrompt();
+ std::string str =
+ lldb_utility::ansi::FormatAnsiTerminalCodes(new_prompt, GetUseColor());
+ if (str.length())
+ new_prompt = str;
+ GetCommandInterpreter().UpdatePrompt(new_prompt);
+}
+
+llvm::StringRef Debugger::GetReproducerPath() const {
+ auto &r = repro::Reproducer::Instance();
+ return r.GetReproducerPath().GetCString();
+}
+
+const FormatEntity::Entry *Debugger::GetThreadFormat() const {
+ const uint32_t idx = ePropertyThreadFormat;
+ return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+}
+
+const FormatEntity::Entry *Debugger::GetThreadStopFormat() const {
+ const uint32_t idx = ePropertyThreadStopFormat;
+ return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+}
+
+lldb::ScriptLanguage Debugger::GetScriptLanguage() const {
+ const uint32_t idx = ePropertyScriptLanguage;
+ return (lldb::ScriptLanguage)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+bool Debugger::SetScriptLanguage(lldb::ScriptLanguage script_lang) {
+ const uint32_t idx = ePropertyScriptLanguage;
+ return m_collection_sp->SetPropertyAtIndexAsEnumeration(nullptr, idx,
+ script_lang);
+}
+
+uint32_t Debugger::GetTerminalWidth() const {
+ const uint32_t idx = ePropertyTerminalWidth;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+bool Debugger::SetTerminalWidth(uint32_t term_width) {
+ const uint32_t idx = ePropertyTerminalWidth;
+ return m_collection_sp->SetPropertyAtIndexAsSInt64(nullptr, idx, term_width);
+}
+
+bool Debugger::GetUseExternalEditor() const {
+ const uint32_t idx = ePropertyUseExternalEditor;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool Debugger::SetUseExternalEditor(bool b) {
+ const uint32_t idx = ePropertyUseExternalEditor;
+ return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+bool Debugger::GetUseColor() const {
+ const uint32_t idx = ePropertyUseColor;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool Debugger::SetUseColor(bool b) {
+ const uint32_t idx = ePropertyUseColor;
+ bool ret = m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+ SetPrompt(GetPrompt());
+ return ret;
+}
+
+bool Debugger::GetHighlightSource() const {
+ const uint32_t idx = ePropertyHighlightSource;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+StopShowColumn Debugger::GetStopShowColumn() const {
+ const uint32_t idx = ePropertyStopShowColumn;
+ return (lldb::StopShowColumn)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+llvm::StringRef Debugger::GetStopShowColumnAnsiPrefix() const {
+ const uint32_t idx = ePropertyStopShowColumnAnsiPrefix;
+ return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, "");
+}
+
+llvm::StringRef Debugger::GetStopShowColumnAnsiSuffix() const {
+ const uint32_t idx = ePropertyStopShowColumnAnsiSuffix;
+ return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, "");
+}
+
+uint32_t Debugger::GetStopSourceLineCount(bool before) const {
+ const uint32_t idx =
+ before ? ePropertyStopLineCountBefore : ePropertyStopLineCountAfter;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+Debugger::StopDisassemblyType Debugger::GetStopDisassemblyDisplay() const {
+ const uint32_t idx = ePropertyStopDisassemblyDisplay;
+ return (Debugger::StopDisassemblyType)
+ m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+uint32_t Debugger::GetDisassemblyLineCount() const {
+ const uint32_t idx = ePropertyStopDisassemblyCount;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+bool Debugger::GetAutoOneLineSummaries() const {
+ const uint32_t idx = ePropertyAutoOneLineSummaries;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
+}
+
+bool Debugger::GetEscapeNonPrintables() const {
+ const uint32_t idx = ePropertyEscapeNonPrintables;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
+}
+
+bool Debugger::GetAutoIndent() const {
+ const uint32_t idx = ePropertyAutoIndent;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
+}
+
+bool Debugger::SetAutoIndent(bool b) {
+ const uint32_t idx = ePropertyAutoIndent;
+ return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+bool Debugger::GetPrintDecls() const {
+ const uint32_t idx = ePropertyPrintDecls;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
+}
+
+bool Debugger::SetPrintDecls(bool b) {
+ const uint32_t idx = ePropertyPrintDecls;
+ return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+uint32_t Debugger::GetTabSize() const {
+ const uint32_t idx = ePropertyTabSize;
+ return m_collection_sp->GetPropertyAtIndexAsUInt64(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+bool Debugger::SetTabSize(uint32_t tab_size) {
+ const uint32_t idx = ePropertyTabSize;
+ return m_collection_sp->SetPropertyAtIndexAsUInt64(nullptr, idx, tab_size);
+}
+
+#pragma mark Debugger
+
+// const DebuggerPropertiesSP &
+// Debugger::GetSettings() const
+//{
+// return m_properties_sp;
+//}
+//
+
+void Debugger::Initialize(LoadPluginCallbackType load_plugin_callback) {
+ assert(g_debugger_list_ptr == nullptr &&
+ "Debugger::Initialize called more than once!");
+ g_debugger_list_mutex_ptr = new std::recursive_mutex();
+ g_debugger_list_ptr = new DebuggerList();
+ g_load_plugin_callback = load_plugin_callback;
+}
+
+void Debugger::Terminate() {
+ assert(g_debugger_list_ptr &&
+ "Debugger::Terminate called without a matching Debugger::Initialize!");
+
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ // Clear our master list of debugger objects
+ {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ for (const auto &debugger : *g_debugger_list_ptr)
+ debugger->Clear();
+ g_debugger_list_ptr->clear();
+ }
+ }
+}
+
+void Debugger::SettingsInitialize() { Target::SettingsInitialize(); }
+
+void Debugger::SettingsTerminate() { Target::SettingsTerminate(); }
+
+bool Debugger::LoadPlugin(const FileSpec &spec, Status &error) {
+ if (g_load_plugin_callback) {
+ llvm::sys::DynamicLibrary dynlib =
+ g_load_plugin_callback(shared_from_this(), spec, error);
+ if (dynlib.isValid()) {
+ m_loaded_plugins.push_back(dynlib);
+ return true;
+ }
+ } else {
+ // The g_load_plugin_callback is registered in SBDebugger::Initialize() and
+ // if the public API layer isn't available (code is linking against all of
+ // the internal LLDB static libraries), then we can't load plugins
+ error.SetErrorString("Public API layer is not available");
+ }
+ return false;
+}
+
+static FileSystem::EnumerateDirectoryResult
+LoadPluginCallback(void *baton, llvm::sys::fs::file_type ft,
+ llvm::StringRef path) {
+ Status error;
+
+ static ConstString g_dylibext(".dylib");
+ static ConstString g_solibext(".so");
+
+ if (!baton)
+ return FileSystem::eEnumerateDirectoryResultQuit;
+
+ Debugger *debugger = (Debugger *)baton;
+
+ namespace fs = llvm::sys::fs;
+ // If we have a regular file, a symbolic link or unknown file type, try and
+ // process the file. We must handle unknown as sometimes the directory
+ // enumeration might be enumerating a file system that doesn't have correct
+ // file type information.
+ if (ft == fs::file_type::regular_file || ft == fs::file_type::symlink_file ||
+ ft == fs::file_type::type_unknown) {
+ FileSpec plugin_file_spec(path);
+ FileSystem::Instance().Resolve(plugin_file_spec);
+
+ if (plugin_file_spec.GetFileNameExtension() != g_dylibext &&
+ plugin_file_spec.GetFileNameExtension() != g_solibext) {
+ return FileSystem::eEnumerateDirectoryResultNext;
+ }
+
+ Status plugin_load_error;
+ debugger->LoadPlugin(plugin_file_spec, plugin_load_error);
+
+ return FileSystem::eEnumerateDirectoryResultNext;
+ } else if (ft == fs::file_type::directory_file ||
+ ft == fs::file_type::symlink_file ||
+ ft == fs::file_type::type_unknown) {
+ // Try and recurse into anything that a directory or symbolic link. We must
+ // also do this for unknown as sometimes the directory enumeration might be
+ // enumerating a file system that doesn't have correct file type
+ // information.
+ return FileSystem::eEnumerateDirectoryResultEnter;
+ }
+
+ return FileSystem::eEnumerateDirectoryResultNext;
+}
+
+void Debugger::InstanceInitialize() {
+ const bool find_directories = true;
+ const bool find_files = true;
+ const bool find_other = true;
+ char dir_path[PATH_MAX];
+ if (FileSpec dir_spec = HostInfo::GetSystemPluginDir()) {
+ if (FileSystem::Instance().Exists(dir_spec) &&
+ dir_spec.GetPath(dir_path, sizeof(dir_path))) {
+ FileSystem::Instance().EnumerateDirectory(dir_path, find_directories,
+ find_files, find_other,
+ LoadPluginCallback, this);
+ }
+ }
+
+ if (FileSpec dir_spec = HostInfo::GetUserPluginDir()) {
+ if (FileSystem::Instance().Exists(dir_spec) &&
+ dir_spec.GetPath(dir_path, sizeof(dir_path))) {
+ FileSystem::Instance().EnumerateDirectory(dir_path, find_directories,
+ find_files, find_other,
+ LoadPluginCallback, this);
+ }
+ }
+
+ PluginManager::DebuggerInitialize(*this);
+}
+
+DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
+ void *baton) {
+ DebuggerSP debugger_sp(new Debugger(log_callback, baton));
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ g_debugger_list_ptr->push_back(debugger_sp);
+ }
+ debugger_sp->InstanceInitialize();
+ return debugger_sp;
+}
+
+void Debugger::Destroy(DebuggerSP &debugger_sp) {
+ if (!debugger_sp)
+ return;
+
+ debugger_sp->Clear();
+
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
+ for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
+ if ((*pos).get() == debugger_sp.get()) {
+ g_debugger_list_ptr->erase(pos);
+ return;
+ }
+ }
+ }
+}
+
+DebuggerSP
+Debugger::FindDebuggerWithInstanceName(ConstString instance_name) {
+ DebuggerSP debugger_sp;
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
+ for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
+ if ((*pos)->m_instance_name == instance_name) {
+ debugger_sp = *pos;
+ break;
+ }
+ }
+ }
+ return debugger_sp;
+}
+
+TargetSP Debugger::FindTargetWithProcessID(lldb::pid_t pid) {
+ TargetSP target_sp;
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
+ for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
+ target_sp = (*pos)->GetTargetList().FindTargetWithProcessID(pid);
+ if (target_sp)
+ break;
+ }
+ }
+ return target_sp;
+}
+
+TargetSP Debugger::FindTargetWithProcess(Process *process) {
+ TargetSP target_sp;
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
+ for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
+ target_sp = (*pos)->GetTargetList().FindTargetWithProcess(process);
+ if (target_sp)
+ break;
+ }
+ }
+ return target_sp;
+}
+
+Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
+ : UserID(g_unique_id++),
+ Properties(std::make_shared<OptionValueProperties>()),
+ m_input_file_sp(std::make_shared<StreamFile>(stdin, false)),
+ m_output_file_sp(std::make_shared<StreamFile>(stdout, false)),
+ m_error_file_sp(std::make_shared<StreamFile>(stderr, false)),
+ m_input_recorder(nullptr),
+ m_broadcaster_manager_sp(BroadcasterManager::MakeBroadcasterManager()),
+ m_terminal_state(), m_target_list(*this), m_platform_list(),
+ m_listener_sp(Listener::MakeListener("lldb.Debugger")),
+ m_source_manager_up(), m_source_file_cache(),
+ m_command_interpreter_up(
+ llvm::make_unique<CommandInterpreter>(*this, false)),
+ m_script_interpreter_sp(), m_input_reader_stack(), m_instance_name(),
+ m_loaded_plugins(), m_event_handler_thread(), m_io_handler_thread(),
+ m_sync_broadcaster(nullptr, "lldb.debugger.sync"),
+ m_forward_listener_sp(), m_clear_once() {
+ char instance_cstr[256];
+ snprintf(instance_cstr, sizeof(instance_cstr), "debugger_%d", (int)GetID());
+ m_instance_name.SetCString(instance_cstr);
+ if (log_callback)
+ m_log_callback_stream_sp =
+ std::make_shared<StreamCallback>(log_callback, baton);
+ m_command_interpreter_up->Initialize();
+ // Always add our default platform to the platform list
+ PlatformSP default_platform_sp(Platform::GetHostPlatform());
+ assert(default_platform_sp);
+ m_platform_list.Append(default_platform_sp, true);
+
+ m_collection_sp->Initialize(g_properties);
+ m_collection_sp->AppendProperty(
+ ConstString("target"),
+ ConstString("Settings specify to debugging targets."), true,
+ Target::GetGlobalProperties()->GetValueProperties());
+ m_collection_sp->AppendProperty(
+ ConstString("platform"), ConstString("Platform settings."), true,
+ Platform::GetGlobalPlatformProperties()->GetValueProperties());
+ m_collection_sp->AppendProperty(
+ ConstString("symbols"), ConstString("Symbol lookup and cache settings."),
+ true, ModuleList::GetGlobalModuleListProperties().GetValueProperties());
+ if (m_command_interpreter_up) {
+ m_collection_sp->AppendProperty(
+ ConstString("interpreter"),
+ ConstString("Settings specify to the debugger's command interpreter."),
+ true, m_command_interpreter_up->GetValueProperties());
+ }
+ OptionValueSInt64 *term_width =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueSInt64(
+ nullptr, ePropertyTerminalWidth);
+ term_width->SetMinimumValue(10);
+ term_width->SetMaximumValue(1024);
+
+ // Turn off use-color if this is a dumb terminal.
+ const char *term = getenv("TERM");
+ if (term && !strcmp(term, "dumb"))
+ SetUseColor(false);
+ // Turn off use-color if we don't write to a terminal with color support.
+ if (!m_output_file_sp->GetFile().GetIsTerminalWithColors())
+ SetUseColor(false);
+
+#if defined(_WIN32) && defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+ // Enabling use of ANSI color codes because LLDB is using them to highlight
+ // text.
+ llvm::sys::Process::UseANSIEscapeCodes(true);
+#endif
+}
+
+Debugger::~Debugger() { Clear(); }
+
+void Debugger::Clear() {
+ // Make sure we call this function only once. With the C++ global destructor
+ // chain having a list of debuggers and with code that can be running on
+ // other threads, we need to ensure this doesn't happen multiple times.
+ //
+ // The following functions call Debugger::Clear():
+ // Debugger::~Debugger();
+ // static void Debugger::Destroy(lldb::DebuggerSP &debugger_sp);
+ // static void Debugger::Terminate();
+ llvm::call_once(m_clear_once, [this]() {
+ ClearIOHandlers();
+ StopIOHandlerThread();
+ StopEventHandlerThread();
+ m_listener_sp->Clear();
+ int num_targets = m_target_list.GetNumTargets();
+ for (int i = 0; i < num_targets; i++) {
+ TargetSP target_sp(m_target_list.GetTargetAtIndex(i));
+ if (target_sp) {
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp)
+ process_sp->Finalize();
+ target_sp->Destroy();
+ }
+ }
+ m_broadcaster_manager_sp->Clear();
+
+ // Close the input file _before_ we close the input read communications
+ // class as it does NOT own the input file, our m_input_file does.
+ m_terminal_state.Clear();
+ if (m_input_file_sp)
+ m_input_file_sp->GetFile().Close();
+
+ m_command_interpreter_up->Clear();
+ });
+}
+
+bool Debugger::GetCloseInputOnEOF() const {
+ // return m_input_comm.GetCloseOnEOF();
+ return false;
+}
+
+void Debugger::SetCloseInputOnEOF(bool b) {
+ // m_input_comm.SetCloseOnEOF(b);
+}
+
+bool Debugger::GetAsyncExecution() {
+ return !m_command_interpreter_up->GetSynchronous();
+}
+
+void Debugger::SetAsyncExecution(bool async_execution) {
+ m_command_interpreter_up->SetSynchronous(!async_execution);
+}
+
+repro::DataRecorder *Debugger::GetInputRecorder() { return m_input_recorder; }
+
+void Debugger::SetInputFileHandle(FILE *fh, bool tranfer_ownership,
+ repro::DataRecorder *recorder) {
+ m_input_recorder = recorder;
+ if (m_input_file_sp)
+ m_input_file_sp->GetFile().SetStream(fh, tranfer_ownership);
+ else
+ m_input_file_sp = std::make_shared<StreamFile>(fh, tranfer_ownership);
+
+ File &in_file = m_input_file_sp->GetFile();
+ if (!in_file.IsValid())
+ in_file.SetStream(stdin, true);
+
+ // Save away the terminal state if that is relevant, so that we can restore
+ // it in RestoreInputState.
+ SaveInputTerminalState();
+}
+
+void Debugger::SetOutputFileHandle(FILE *fh, bool tranfer_ownership) {
+ if (m_output_file_sp)
+ m_output_file_sp->GetFile().SetStream(fh, tranfer_ownership);
+ else
+ m_output_file_sp = std::make_shared<StreamFile>(fh, tranfer_ownership);
+
+ File &out_file = m_output_file_sp->GetFile();
+ if (!out_file.IsValid())
+ out_file.SetStream(stdout, false);
+
+ // Do not create the ScriptInterpreter just for setting the output file
+ // handle as the constructor will know how to do the right thing on its own.
+ if (ScriptInterpreter *script_interpreter =
+ GetScriptInterpreter(/*can_create=*/false))
+ script_interpreter->ResetOutputFileHandle(fh);
+}
+
+void Debugger::SetErrorFileHandle(FILE *fh, bool tranfer_ownership) {
+ if (m_error_file_sp)
+ m_error_file_sp->GetFile().SetStream(fh, tranfer_ownership);
+ else
+ m_error_file_sp = std::make_shared<StreamFile>(fh, tranfer_ownership);
+
+ File &err_file = m_error_file_sp->GetFile();
+ if (!err_file.IsValid())
+ err_file.SetStream(stderr, false);
+}
+
+void Debugger::SaveInputTerminalState() {
+ if (m_input_file_sp) {
+ File &in_file = m_input_file_sp->GetFile();
+ if (in_file.GetDescriptor() != File::kInvalidDescriptor)
+ m_terminal_state.Save(in_file.GetDescriptor(), true);
+ }
+}
+
+void Debugger::RestoreInputTerminalState() { m_terminal_state.Restore(); }
+
+ExecutionContext Debugger::GetSelectedExecutionContext() {
+ ExecutionContext exe_ctx;
+ TargetSP target_sp(GetSelectedTarget());
+ exe_ctx.SetTargetSP(target_sp);
+
+ if (target_sp) {
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ exe_ctx.SetProcessSP(process_sp);
+ if (process_sp && !process_sp->IsRunning()) {
+ ThreadSP thread_sp(process_sp->GetThreadList().GetSelectedThread());
+ if (thread_sp) {
+ exe_ctx.SetThreadSP(thread_sp);
+ exe_ctx.SetFrameSP(thread_sp->GetSelectedFrame());
+ if (exe_ctx.GetFramePtr() == nullptr)
+ exe_ctx.SetFrameSP(thread_sp->GetStackFrameAtIndex(0));
+ }
+ }
+ }
+ return exe_ctx;
+}
+
+void Debugger::DispatchInputInterrupt() {
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+ IOHandlerSP reader_sp(m_input_reader_stack.Top());
+ if (reader_sp)
+ reader_sp->Interrupt();
+}
+
+void Debugger::DispatchInputEndOfFile() {
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+ IOHandlerSP reader_sp(m_input_reader_stack.Top());
+ if (reader_sp)
+ reader_sp->GotEOF();
+}
+
+void Debugger::ClearIOHandlers() {
+ // The bottom input reader should be the main debugger input reader. We do
+ // not want to close that one here.
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+ while (m_input_reader_stack.GetSize() > 1) {
+ IOHandlerSP reader_sp(m_input_reader_stack.Top());
+ if (reader_sp)
+ PopIOHandler(reader_sp);
+ }
+}
+
+void Debugger::ExecuteIOHandlers() {
+ while (true) {
+ IOHandlerSP reader_sp(m_input_reader_stack.Top());
+ if (!reader_sp)
+ break;
+
+ reader_sp->Run();
+
+ // Remove all input readers that are done from the top of the stack
+ while (true) {
+ IOHandlerSP top_reader_sp = m_input_reader_stack.Top();
+ if (top_reader_sp && top_reader_sp->GetIsDone())
+ PopIOHandler(top_reader_sp);
+ else
+ break;
+ }
+ }
+ ClearIOHandlers();
+}
+
+bool Debugger::IsTopIOHandler(const lldb::IOHandlerSP &reader_sp) {
+ return m_input_reader_stack.IsTop(reader_sp);
+}
+
+bool Debugger::CheckTopIOHandlerTypes(IOHandler::Type top_type,
+ IOHandler::Type second_top_type) {
+ return m_input_reader_stack.CheckTopIOHandlerTypes(top_type, second_top_type);
+}
+
+void Debugger::PrintAsync(const char *s, size_t len, bool is_stdout) {
+ lldb::StreamFileSP stream = is_stdout ? GetOutputFile() : GetErrorFile();
+ m_input_reader_stack.PrintAsync(stream.get(), s, len);
+}
+
+ConstString Debugger::GetTopIOHandlerControlSequence(char ch) {
+ return m_input_reader_stack.GetTopIOHandlerControlSequence(ch);
+}
+
+const char *Debugger::GetIOHandlerCommandPrefix() {
+ return m_input_reader_stack.GetTopIOHandlerCommandPrefix();
+}
+
+const char *Debugger::GetIOHandlerHelpPrologue() {
+ return m_input_reader_stack.GetTopIOHandlerHelpPrologue();
+}
+
+void Debugger::RunIOHandler(const IOHandlerSP &reader_sp) {
+ PushIOHandler(reader_sp);
+
+ IOHandlerSP top_reader_sp = reader_sp;
+ while (top_reader_sp) {
+ top_reader_sp->Run();
+
+ if (top_reader_sp.get() == reader_sp.get()) {
+ if (PopIOHandler(reader_sp))
+ break;
+ }
+
+ while (true) {
+ top_reader_sp = m_input_reader_stack.Top();
+ if (top_reader_sp && top_reader_sp->GetIsDone())
+ PopIOHandler(top_reader_sp);
+ else
+ break;
+ }
+ }
+}
+
+void Debugger::AdoptTopIOHandlerFilesIfInvalid(StreamFileSP &in,
+ StreamFileSP &out,
+ StreamFileSP &err) {
+ // Before an IOHandler runs, it must have in/out/err streams. This function
+ // is called when one ore more of the streams are nullptr. We use the top
+ // input reader's in/out/err streams, or fall back to the debugger file
+ // handles, or we fall back onto stdin/stdout/stderr as a last resort.
+
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+ IOHandlerSP top_reader_sp(m_input_reader_stack.Top());
+ // If no STDIN has been set, then set it appropriately
+ if (!in) {
+ if (top_reader_sp)
+ in = top_reader_sp->GetInputStreamFile();
+ else
+ in = GetInputFile();
+
+ // If there is nothing, use stdin
+ if (!in)
+ in = std::make_shared<StreamFile>(stdin, false);
+ }
+ // If no STDOUT has been set, then set it appropriately
+ if (!out) {
+ if (top_reader_sp)
+ out = top_reader_sp->GetOutputStreamFile();
+ else
+ out = GetOutputFile();
+
+ // If there is nothing, use stdout
+ if (!out)
+ out = std::make_shared<StreamFile>(stdout, false);
+ }
+ // If no STDERR has been set, then set it appropriately
+ if (!err) {
+ if (top_reader_sp)
+ err = top_reader_sp->GetErrorStreamFile();
+ else
+ err = GetErrorFile();
+
+ // If there is nothing, use stderr
+ if (!err)
+ err = std::make_shared<StreamFile>(stdout, false);
+ }
+}
+
+void Debugger::PushIOHandler(const IOHandlerSP &reader_sp,
+ bool cancel_top_handler) {
+ if (!reader_sp)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+
+ // Get the current top input reader...
+ IOHandlerSP top_reader_sp(m_input_reader_stack.Top());
+
+ // Don't push the same IO handler twice...
+ if (reader_sp == top_reader_sp)
+ return;
+
+ // Push our new input reader
+ m_input_reader_stack.Push(reader_sp);
+ reader_sp->Activate();
+
+ // Interrupt the top input reader to it will exit its Run() function and let
+ // this new input reader take over
+ if (top_reader_sp) {
+ top_reader_sp->Deactivate();
+ if (cancel_top_handler)
+ top_reader_sp->Cancel();
+ }
+}
+
+bool Debugger::PopIOHandler(const IOHandlerSP &pop_reader_sp) {
+ if (!pop_reader_sp)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+
+ // The reader on the stop of the stack is done, so let the next read on the
+ // stack refresh its prompt and if there is one...
+ if (m_input_reader_stack.IsEmpty())
+ return false;
+
+ IOHandlerSP reader_sp(m_input_reader_stack.Top());
+
+ if (pop_reader_sp != reader_sp)
+ return false;
+
+ reader_sp->Deactivate();
+ reader_sp->Cancel();
+ m_input_reader_stack.Pop();
+
+ reader_sp = m_input_reader_stack.Top();
+ if (reader_sp)
+ reader_sp->Activate();
+
+ return true;
+}
+
+StreamSP Debugger::GetAsyncOutputStream() {
+ return std::make_shared<StreamAsynchronousIO>(*this, true);
+}
+
+StreamSP Debugger::GetAsyncErrorStream() {
+ return std::make_shared<StreamAsynchronousIO>(*this, false);
+}
+
+size_t Debugger::GetNumDebuggers() {
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ return g_debugger_list_ptr->size();
+ }
+ return 0;
+}
+
+lldb::DebuggerSP Debugger::GetDebuggerAtIndex(size_t index) {
+ DebuggerSP debugger_sp;
+
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ if (index < g_debugger_list_ptr->size())
+ debugger_sp = g_debugger_list_ptr->at(index);
+ }
+
+ return debugger_sp;
+}
+
+DebuggerSP Debugger::FindDebuggerWithID(lldb::user_id_t id) {
+ DebuggerSP debugger_sp;
+
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
+ for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
+ if ((*pos)->GetID() == id) {
+ debugger_sp = *pos;
+ break;
+ }
+ }
+ }
+ return debugger_sp;
+}
+
+bool Debugger::FormatDisassemblerAddress(const FormatEntity::Entry *format,
+ const SymbolContext *sc,
+ const SymbolContext *prev_sc,
+ const ExecutionContext *exe_ctx,
+ const Address *addr, Stream &s) {
+ FormatEntity::Entry format_entry;
+
+ if (format == nullptr) {
+ if (exe_ctx != nullptr && exe_ctx->HasTargetScope())
+ format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ if (format == nullptr) {
+ FormatEntity::Parse("${addr}: ", format_entry);
+ format = &format_entry;
+ }
+ }
+ bool function_changed = false;
+ bool initial_function = false;
+ if (prev_sc && (prev_sc->function || prev_sc->symbol)) {
+ if (sc && (sc->function || sc->symbol)) {
+ if (prev_sc->symbol && sc->symbol) {
+ if (!sc->symbol->Compare(prev_sc->symbol->GetName(),
+ prev_sc->symbol->GetType())) {
+ function_changed = true;
+ }
+ } else if (prev_sc->function && sc->function) {
+ if (prev_sc->function->GetMangled() != sc->function->GetMangled()) {
+ function_changed = true;
+ }
+ }
+ }
+ }
+ // The first context on a list of instructions will have a prev_sc that has
+ // no Function or Symbol -- if SymbolContext had an IsValid() method, it
+ // would return false. But we do get a prev_sc pointer.
+ if ((sc && (sc->function || sc->symbol)) && prev_sc &&
+ (prev_sc->function == nullptr && prev_sc->symbol == nullptr)) {
+ initial_function = true;
+ }
+ return FormatEntity::Format(*format, s, sc, exe_ctx, addr, nullptr,
+ function_changed, initial_function);
+}
+
+void Debugger::SetLoggingCallback(lldb::LogOutputCallback log_callback,
+ void *baton) {
+ // For simplicity's sake, I am not going to deal with how to close down any
+ // open logging streams, I just redirect everything from here on out to the
+ // callback.
+ m_log_callback_stream_sp =
+ std::make_shared<StreamCallback>(log_callback, baton);
+}
+
+bool Debugger::EnableLog(llvm::StringRef channel,
+ llvm::ArrayRef<const char *> categories,
+ llvm::StringRef log_file, uint32_t log_options,
+ llvm::raw_ostream &error_stream) {
+ const bool should_close = true;
+ const bool unbuffered = true;
+
+ std::shared_ptr<llvm::raw_ostream> log_stream_sp;
+ if (m_log_callback_stream_sp) {
+ log_stream_sp = m_log_callback_stream_sp;
+ // For now when using the callback mode you always get thread & timestamp.
+ log_options |=
+ LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
+ } else if (log_file.empty()) {
+ log_stream_sp = std::make_shared<llvm::raw_fd_ostream>(
+ GetOutputFile()->GetFile().GetDescriptor(), !should_close, unbuffered);
+ } else {
+ auto pos = m_log_streams.find(log_file);
+ if (pos != m_log_streams.end())
+ log_stream_sp = pos->second.lock();
+ if (!log_stream_sp) {
+ llvm::sys::fs::OpenFlags flags = llvm::sys::fs::F_Text;
+ if (log_options & LLDB_LOG_OPTION_APPEND)
+ flags |= llvm::sys::fs::F_Append;
+ int FD;
+ if (std::error_code ec = llvm::sys::fs::openFileForWrite(
+ log_file, FD, llvm::sys::fs::CD_CreateAlways, flags)) {
+ error_stream << "Unable to open log file: " << ec.message();
+ return false;
+ }
+ log_stream_sp =
+ std::make_shared<llvm::raw_fd_ostream>(FD, should_close, unbuffered);
+ m_log_streams[log_file] = log_stream_sp;
+ }
+ }
+ assert(log_stream_sp);
+
+ if (log_options == 0)
+ log_options =
+ LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE;
+
+ return Log::EnableLogChannel(log_stream_sp, log_options, channel, categories,
+ error_stream);
+}
+
+ScriptInterpreter *Debugger::GetScriptInterpreter(bool can_create) {
+ std::lock_guard<std::recursive_mutex> locker(m_script_interpreter_mutex);
+
+ if (!m_script_interpreter_sp) {
+ if (!can_create)
+ return nullptr;
+ m_script_interpreter_sp = PluginManager::GetScriptInterpreterForLanguage(
+ GetScriptLanguage(), *this);
+ }
+
+ return m_script_interpreter_sp.get();
+}
+
+SourceManager &Debugger::GetSourceManager() {
+ if (!m_source_manager_up)
+ m_source_manager_up = llvm::make_unique<SourceManager>(shared_from_this());
+ return *m_source_manager_up;
+}
+
+// This function handles events that were broadcast by the process.
+void Debugger::HandleBreakpointEvent(const EventSP &event_sp) {
+ using namespace lldb;
+ const uint32_t event_type =
+ Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent(
+ event_sp);
+
+ // if (event_type & eBreakpointEventTypeAdded
+ // || event_type & eBreakpointEventTypeRemoved
+ // || event_type & eBreakpointEventTypeEnabled
+ // || event_type & eBreakpointEventTypeDisabled
+ // || event_type & eBreakpointEventTypeCommandChanged
+ // || event_type & eBreakpointEventTypeConditionChanged
+ // || event_type & eBreakpointEventTypeIgnoreChanged
+ // || event_type & eBreakpointEventTypeLocationsResolved)
+ // {
+ // // Don't do anything about these events, since the breakpoint
+ // commands already echo these actions.
+ // }
+ //
+ if (event_type & eBreakpointEventTypeLocationsAdded) {
+ uint32_t num_new_locations =
+ Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent(
+ event_sp);
+ if (num_new_locations > 0) {
+ BreakpointSP breakpoint =
+ Breakpoint::BreakpointEventData::GetBreakpointFromEvent(event_sp);
+ StreamSP output_sp(GetAsyncOutputStream());
+ if (output_sp) {
+ output_sp->Printf("%d location%s added to breakpoint %d\n",
+ num_new_locations, num_new_locations == 1 ? "" : "s",
+ breakpoint->GetID());
+ output_sp->Flush();
+ }
+ }
+ }
+ // else if (event_type & eBreakpointEventTypeLocationsRemoved)
+ // {
+ // // These locations just get disabled, not sure it is worth spamming
+ // folks about this on the command line.
+ // }
+ // else if (event_type & eBreakpointEventTypeLocationsResolved)
+ // {
+ // // This might be an interesting thing to note, but I'm going to
+ // leave it quiet for now, it just looked noisy.
+ // }
+}
+
+size_t Debugger::GetProcessSTDOUT(Process *process, Stream *stream) {
+ size_t total_bytes = 0;
+ if (stream == nullptr)
+ stream = GetOutputFile().get();
+
+ if (stream) {
+ // The process has stuff waiting for stdout; get it and write it out to the
+ // appropriate place.
+ if (process == nullptr) {
+ TargetSP target_sp = GetTargetList().GetSelectedTarget();
+ if (target_sp)
+ process = target_sp->GetProcessSP().get();
+ }
+ if (process) {
+ Status error;
+ size_t len;
+ char stdio_buffer[1024];
+ while ((len = process->GetSTDOUT(stdio_buffer, sizeof(stdio_buffer),
+ error)) > 0) {
+ stream->Write(stdio_buffer, len);
+ total_bytes += len;
+ }
+ }
+ stream->Flush();
+ }
+ return total_bytes;
+}
+
+size_t Debugger::GetProcessSTDERR(Process *process, Stream *stream) {
+ size_t total_bytes = 0;
+ if (stream == nullptr)
+ stream = GetOutputFile().get();
+
+ if (stream) {
+ // The process has stuff waiting for stderr; get it and write it out to the
+ // appropriate place.
+ if (process == nullptr) {
+ TargetSP target_sp = GetTargetList().GetSelectedTarget();
+ if (target_sp)
+ process = target_sp->GetProcessSP().get();
+ }
+ if (process) {
+ Status error;
+ size_t len;
+ char stdio_buffer[1024];
+ while ((len = process->GetSTDERR(stdio_buffer, sizeof(stdio_buffer),
+ error)) > 0) {
+ stream->Write(stdio_buffer, len);
+ total_bytes += len;
+ }
+ }
+ stream->Flush();
+ }
+ return total_bytes;
+}
+
+// This function handles events that were broadcast by the process.
+void Debugger::HandleProcessEvent(const EventSP &event_sp) {
+ using namespace lldb;
+ const uint32_t event_type = event_sp->GetType();
+ ProcessSP process_sp =
+ (event_type == Process::eBroadcastBitStructuredData)
+ ? EventDataStructuredData::GetProcessFromEvent(event_sp.get())
+ : Process::ProcessEventData::GetProcessFromEvent(event_sp.get());
+
+ StreamSP output_stream_sp = GetAsyncOutputStream();
+ StreamSP error_stream_sp = GetAsyncErrorStream();
+ const bool gui_enabled = IsForwardingEvents();
+
+ if (!gui_enabled) {
+ bool pop_process_io_handler = false;
+ assert(process_sp);
+
+ bool state_is_stopped = false;
+ const bool got_state_changed =
+ (event_type & Process::eBroadcastBitStateChanged) != 0;
+ const bool got_stdout = (event_type & Process::eBroadcastBitSTDOUT) != 0;
+ const bool got_stderr = (event_type & Process::eBroadcastBitSTDERR) != 0;
+ const bool got_structured_data =
+ (event_type & Process::eBroadcastBitStructuredData) != 0;
+
+ if (got_state_changed) {
+ StateType event_state =
+ Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+ state_is_stopped = StateIsStoppedState(event_state, false);
+ }
+
+ // Display running state changes first before any STDIO
+ if (got_state_changed && !state_is_stopped) {
+ Process::HandleProcessStateChangedEvent(event_sp, output_stream_sp.get(),
+ pop_process_io_handler);
+ }
+
+ // Now display and STDOUT
+ if (got_stdout || got_state_changed) {
+ GetProcessSTDOUT(process_sp.get(), output_stream_sp.get());
+ }
+
+ // Now display and STDERR
+ if (got_stderr || got_state_changed) {
+ GetProcessSTDERR(process_sp.get(), error_stream_sp.get());
+ }
+
+ // Give structured data events an opportunity to display.
+ if (got_structured_data) {
+ StructuredDataPluginSP plugin_sp =
+ EventDataStructuredData::GetPluginFromEvent(event_sp.get());
+ if (plugin_sp) {
+ auto structured_data_sp =
+ EventDataStructuredData::GetObjectFromEvent(event_sp.get());
+ if (output_stream_sp) {
+ StreamString content_stream;
+ Status error =
+ plugin_sp->GetDescription(structured_data_sp, content_stream);
+ if (error.Success()) {
+ if (!content_stream.GetString().empty()) {
+ // Add newline.
+ content_stream.PutChar('\n');
+ content_stream.Flush();
+
+ // Print it.
+ output_stream_sp->PutCString(content_stream.GetString());
+ }
+ } else {
+ error_stream_sp->Printf("Failed to print structured "
+ "data with plugin %s: %s",
+ plugin_sp->GetPluginName().AsCString(),
+ error.AsCString());
+ }
+ }
+ }
+ }
+
+ // Now display any stopped state changes after any STDIO
+ if (got_state_changed && state_is_stopped) {
+ Process::HandleProcessStateChangedEvent(event_sp, output_stream_sp.get(),
+ pop_process_io_handler);
+ }
+
+ output_stream_sp->Flush();
+ error_stream_sp->Flush();
+
+ if (pop_process_io_handler)
+ process_sp->PopProcessIOHandler();
+ }
+}
+
+void Debugger::HandleThreadEvent(const EventSP &event_sp) {
+ // At present the only thread event we handle is the Frame Changed event, and
+ // all we do for that is just reprint the thread status for that thread.
+ using namespace lldb;
+ const uint32_t event_type = event_sp->GetType();
+ const bool stop_format = true;
+ if (event_type == Thread::eBroadcastBitStackChanged ||
+ event_type == Thread::eBroadcastBitThreadSelected) {
+ ThreadSP thread_sp(
+ Thread::ThreadEventData::GetThreadFromEvent(event_sp.get()));
+ if (thread_sp) {
+ thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1, stop_format);
+ }
+ }
+}
+
+bool Debugger::IsForwardingEvents() { return (bool)m_forward_listener_sp; }
+
+void Debugger::EnableForwardEvents(const ListenerSP &listener_sp) {
+ m_forward_listener_sp = listener_sp;
+}
+
+void Debugger::CancelForwardEvents(const ListenerSP &listener_sp) {
+ m_forward_listener_sp.reset();
+}
+
+void Debugger::DefaultEventHandler() {
+ ListenerSP listener_sp(GetListener());
+ ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
+ ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
+ ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
+ BroadcastEventSpec target_event_spec(broadcaster_class_target,
+ Target::eBroadcastBitBreakpointChanged);
+
+ BroadcastEventSpec process_event_spec(
+ broadcaster_class_process,
+ Process::eBroadcastBitStateChanged | Process::eBroadcastBitSTDOUT |
+ Process::eBroadcastBitSTDERR | Process::eBroadcastBitStructuredData);
+
+ BroadcastEventSpec thread_event_spec(broadcaster_class_thread,
+ Thread::eBroadcastBitStackChanged |
+ Thread::eBroadcastBitThreadSelected);
+
+ listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp,
+ target_event_spec);
+ listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp,
+ process_event_spec);
+ listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp,
+ thread_event_spec);
+ listener_sp->StartListeningForEvents(
+ m_command_interpreter_up.get(),
+ CommandInterpreter::eBroadcastBitQuitCommandReceived |
+ CommandInterpreter::eBroadcastBitAsynchronousOutputData |
+ CommandInterpreter::eBroadcastBitAsynchronousErrorData);
+
+ // Let the thread that spawned us know that we have started up and that we
+ // are now listening to all required events so no events get missed
+ m_sync_broadcaster.BroadcastEvent(eBroadcastBitEventThreadIsListening);
+
+ bool done = false;
+ while (!done) {
+ EventSP event_sp;
+ if (listener_sp->GetEvent(event_sp, llvm::None)) {
+ if (event_sp) {
+ Broadcaster *broadcaster = event_sp->GetBroadcaster();
+ if (broadcaster) {
+ uint32_t event_type = event_sp->GetType();
+ ConstString broadcaster_class(broadcaster->GetBroadcasterClass());
+ if (broadcaster_class == broadcaster_class_process) {
+ HandleProcessEvent(event_sp);
+ } else if (broadcaster_class == broadcaster_class_target) {
+ if (Breakpoint::BreakpointEventData::GetEventDataFromEvent(
+ event_sp.get())) {
+ HandleBreakpointEvent(event_sp);
+ }
+ } else if (broadcaster_class == broadcaster_class_thread) {
+ HandleThreadEvent(event_sp);
+ } else if (broadcaster == m_command_interpreter_up.get()) {
+ if (event_type &
+ CommandInterpreter::eBroadcastBitQuitCommandReceived) {
+ done = true;
+ } else if (event_type &
+ CommandInterpreter::eBroadcastBitAsynchronousErrorData) {
+ const char *data = reinterpret_cast<const char *>(
+ EventDataBytes::GetBytesFromEvent(event_sp.get()));
+ if (data && data[0]) {
+ StreamSP error_sp(GetAsyncErrorStream());
+ if (error_sp) {
+ error_sp->PutCString(data);
+ error_sp->Flush();
+ }
+ }
+ } else if (event_type & CommandInterpreter::
+ eBroadcastBitAsynchronousOutputData) {
+ const char *data = reinterpret_cast<const char *>(
+ EventDataBytes::GetBytesFromEvent(event_sp.get()));
+ if (data && data[0]) {
+ StreamSP output_sp(GetAsyncOutputStream());
+ if (output_sp) {
+ output_sp->PutCString(data);
+ output_sp->Flush();
+ }
+ }
+ }
+ }
+ }
+
+ if (m_forward_listener_sp)
+ m_forward_listener_sp->AddEvent(event_sp);
+ }
+ }
+ }
+}
+
+lldb::thread_result_t Debugger::EventHandlerThread(lldb::thread_arg_t arg) {
+ ((Debugger *)arg)->DefaultEventHandler();
+ return {};
+}
+
+bool Debugger::StartEventHandlerThread() {
+ if (!m_event_handler_thread.IsJoinable()) {
+ // We must synchronize with the DefaultEventHandler() thread to ensure it
+ // is up and running and listening to events before we return from this
+ // function. We do this by listening to events for the
+ // eBroadcastBitEventThreadIsListening from the m_sync_broadcaster
+ ConstString full_name("lldb.debugger.event-handler");
+ ListenerSP listener_sp(Listener::MakeListener(full_name.AsCString()));
+ listener_sp->StartListeningForEvents(&m_sync_broadcaster,
+ eBroadcastBitEventThreadIsListening);
+
+ auto thread_name =
+ full_name.GetLength() < llvm::get_max_thread_name_length() ?
+ full_name.AsCString() : "dbg.evt-handler";
+
+ // Use larger 8MB stack for this thread
+ llvm::Expected<HostThread> event_handler_thread =
+ ThreadLauncher::LaunchThread(thread_name, EventHandlerThread, this,
+ g_debugger_event_thread_stack_bytes);
+
+ if (event_handler_thread) {
+ m_event_handler_thread = *event_handler_thread;
+ } else {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(event_handler_thread.takeError()));
+ }
+
+ // Make sure DefaultEventHandler() is running and listening to events
+ // before we return from this function. We are only listening for events of
+ // type eBroadcastBitEventThreadIsListening so we don't need to check the
+ // event, we just need to wait an infinite amount of time for it (nullptr
+ // timeout as the first parameter)
+ lldb::EventSP event_sp;
+ listener_sp->GetEvent(event_sp, llvm::None);
+ }
+ return m_event_handler_thread.IsJoinable();
+}
+
+void Debugger::StopEventHandlerThread() {
+ if (m_event_handler_thread.IsJoinable()) {
+ GetCommandInterpreter().BroadcastEvent(
+ CommandInterpreter::eBroadcastBitQuitCommandReceived);
+ m_event_handler_thread.Join(nullptr);
+ }
+}
+
+lldb::thread_result_t Debugger::IOHandlerThread(lldb::thread_arg_t arg) {
+ Debugger *debugger = (Debugger *)arg;
+ debugger->ExecuteIOHandlers();
+ debugger->StopEventHandlerThread();
+ return {};
+}
+
+bool Debugger::HasIOHandlerThread() { return m_io_handler_thread.IsJoinable(); }
+
+bool Debugger::StartIOHandlerThread() {
+ if (!m_io_handler_thread.IsJoinable()) {
+ llvm::Expected<HostThread> io_handler_thread = ThreadLauncher::LaunchThread(
+ "lldb.debugger.io-handler", IOHandlerThread, this,
+ 8 * 1024 * 1024); // Use larger 8MB stack for this thread
+ if (io_handler_thread) {
+ m_io_handler_thread = *io_handler_thread;
+ } else {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(io_handler_thread.takeError()));
+ }
+ }
+ return m_io_handler_thread.IsJoinable();
+}
+
+void Debugger::StopIOHandlerThread() {
+ if (m_io_handler_thread.IsJoinable()) {
+ if (m_input_file_sp)
+ m_input_file_sp->GetFile().Close();
+ m_io_handler_thread.Join(nullptr);
+ }
+}
+
+void Debugger::JoinIOHandlerThread() {
+ if (HasIOHandlerThread()) {
+ thread_result_t result;
+ m_io_handler_thread.Join(&result);
+ m_io_handler_thread = LLDB_INVALID_HOST_THREAD;
+ }
+}
+
+Target *Debugger::GetDummyTarget() {
+ return m_target_list.GetDummyTarget(*this).get();
+}
+
+Target *Debugger::GetSelectedOrDummyTarget(bool prefer_dummy) {
+ Target *target = nullptr;
+ if (!prefer_dummy) {
+ target = m_target_list.GetSelectedTarget().get();
+ if (target)
+ return target;
+ }
+
+ return GetDummyTarget();
+}
+
+Status Debugger::RunREPL(LanguageType language, const char *repl_options) {
+ Status err;
+ FileSpec repl_executable;
+
+ if (language == eLanguageTypeUnknown) {
+ std::set<LanguageType> repl_languages;
+
+ Language::GetLanguagesSupportingREPLs(repl_languages);
+
+ if (repl_languages.size() == 1) {
+ language = *repl_languages.begin();
+ } else if (repl_languages.empty()) {
+ err.SetErrorStringWithFormat(
+ "LLDB isn't configured with REPL support for any languages.");
+ return err;
+ } else {
+ err.SetErrorStringWithFormat(
+ "Multiple possible REPL languages. Please specify a language.");
+ return err;
+ }
+ }
+
+ Target *const target =
+ nullptr; // passing in an empty target means the REPL must create one
+
+ REPLSP repl_sp(REPL::Create(err, language, this, target, repl_options));
+
+ if (!err.Success()) {
+ return err;
+ }
+
+ if (!repl_sp) {
+ err.SetErrorStringWithFormat("couldn't find a REPL for %s",
+ Language::GetNameForLanguageType(language));
+ return err;
+ }
+
+ repl_sp->SetCompilerOptions(repl_options);
+ repl_sp->RunLoop();
+
+ return err;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Disassembler.cpp b/contrib/llvm-project/lldb/source/Core/Disassembler.cpp
new file mode 100644
index 000000000000..af7cf82d470a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Disassembler.cpp
@@ -0,0 +1,1453 @@
+//===-- Disassembler.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Disassembler.h"
+
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Core/Mangled.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Interpreter/OptionValueArray.h"
+#include "lldb/Interpreter/OptionValueDictionary.h"
+#include "lldb/Interpreter/OptionValueRegex.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+#include "lldb/lldb-private-enumerations.h"
+#include "lldb/lldb-private-interfaces.h"
+#include "lldb/lldb-private-types.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Compiler.h"
+
+#include <cstdint>
+#include <cstring>
+#include <utility>
+
+#include <assert.h>
+
+#define DEFAULT_DISASM_BYTE_SIZE 32
+
+using namespace lldb;
+using namespace lldb_private;
+
+DisassemblerSP Disassembler::FindPlugin(const ArchSpec &arch,
+ const char *flavor,
+ const char *plugin_name) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "Disassembler::FindPlugin (arch = %s, plugin_name = %s)",
+ arch.GetArchitectureName(), plugin_name);
+
+ DisassemblerCreateInstance create_callback = nullptr;
+
+ if (plugin_name) {
+ ConstString const_plugin_name(plugin_name);
+ create_callback = PluginManager::GetDisassemblerCreateCallbackForPluginName(
+ const_plugin_name);
+ if (create_callback) {
+ DisassemblerSP disassembler_sp(create_callback(arch, flavor));
+
+ if (disassembler_sp)
+ return disassembler_sp;
+ }
+ } else {
+ for (uint32_t idx = 0;
+ (create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex(
+ idx)) != nullptr;
+ ++idx) {
+ DisassemblerSP disassembler_sp(create_callback(arch, flavor));
+
+ if (disassembler_sp)
+ return disassembler_sp;
+ }
+ }
+ return DisassemblerSP();
+}
+
+DisassemblerSP Disassembler::FindPluginForTarget(const TargetSP target_sp,
+ const ArchSpec &arch,
+ const char *flavor,
+ const char *plugin_name) {
+ if (target_sp && flavor == nullptr) {
+ // FIXME - we don't have the mechanism in place to do per-architecture
+ // settings. But since we know that for now we only support flavors on x86
+ // & x86_64,
+ if (arch.GetTriple().getArch() == llvm::Triple::x86 ||
+ arch.GetTriple().getArch() == llvm::Triple::x86_64)
+ flavor = target_sp->GetDisassemblyFlavor();
+ }
+ return FindPlugin(arch, flavor, plugin_name);
+}
+
+static void ResolveAddress(const ExecutionContext &exe_ctx, const Address &addr,
+ Address &resolved_addr) {
+ if (!addr.IsSectionOffset()) {
+ // If we weren't passed in a section offset address range, try and resolve
+ // it to something
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ bool is_resolved =
+ target->GetSectionLoadList().IsEmpty() ?
+ target->GetImages().ResolveFileAddress(addr.GetOffset(),
+ resolved_addr) :
+ target->GetSectionLoadList().ResolveLoadAddress(addr.GetOffset(),
+ resolved_addr);
+
+ // We weren't able to resolve the address, just treat it as a raw address
+ if (is_resolved && resolved_addr.IsValid())
+ return;
+ }
+ }
+ resolved_addr = addr;
+}
+
+size_t Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
+ const char *plugin_name, const char *flavor,
+ const ExecutionContext &exe_ctx,
+ SymbolContextList &sc_list,
+ uint32_t num_instructions,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
+ size_t success_count = 0;
+ const size_t count = sc_list.GetSize();
+ SymbolContext sc;
+ AddressRange range;
+ const uint32_t scope =
+ eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol;
+ const bool use_inline_block_range = true;
+ for (size_t i = 0; i < count; ++i) {
+ if (!sc_list.GetContextAtIndex(i, sc))
+ break;
+ for (uint32_t range_idx = 0;
+ sc.GetAddressRange(scope, range_idx, use_inline_block_range, range);
+ ++range_idx) {
+ if (Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, range,
+ num_instructions, mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm)) {
+ ++success_count;
+ strm.EOL();
+ }
+ }
+ }
+ return success_count;
+}
+
+bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
+ const char *plugin_name, const char *flavor,
+ const ExecutionContext &exe_ctx,
+ ConstString name, Module *module,
+ uint32_t num_instructions,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
+ SymbolContextList sc_list;
+ if (name) {
+ const bool include_symbols = true;
+ const bool include_inlines = true;
+ if (module) {
+ module->FindFunctions(name, nullptr, eFunctionNameTypeAuto,
+ include_symbols, include_inlines, true, sc_list);
+ } else if (exe_ctx.GetTargetPtr()) {
+ exe_ctx.GetTargetPtr()->GetImages().FindFunctions(
+ name, eFunctionNameTypeAuto, include_symbols, include_inlines, false,
+ sc_list);
+ }
+ }
+
+ if (sc_list.GetSize()) {
+ return Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, sc_list,
+ num_instructions, mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm);
+ }
+ return false;
+}
+
+lldb::DisassemblerSP Disassembler::DisassembleRange(
+ const ArchSpec &arch, const char *plugin_name, const char *flavor,
+ const ExecutionContext &exe_ctx, const AddressRange &range,
+ bool prefer_file_cache) {
+ lldb::DisassemblerSP disasm_sp;
+ if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid()) {
+ disasm_sp = Disassembler::FindPluginForTarget(exe_ctx.GetTargetSP(), arch,
+ flavor, plugin_name);
+
+ if (disasm_sp) {
+ size_t bytes_disassembled = disasm_sp->ParseInstructions(
+ &exe_ctx, range, nullptr, prefer_file_cache);
+ if (bytes_disassembled == 0)
+ disasm_sp.reset();
+ }
+ }
+ return disasm_sp;
+}
+
+lldb::DisassemblerSP
+Disassembler::DisassembleBytes(const ArchSpec &arch, const char *plugin_name,
+ const char *flavor, const Address &start,
+ const void *src, size_t src_len,
+ uint32_t num_instructions, bool data_from_file) {
+ lldb::DisassemblerSP disasm_sp;
+
+ if (src) {
+ disasm_sp = Disassembler::FindPlugin(arch, flavor, plugin_name);
+
+ if (disasm_sp) {
+ DataExtractor data(src, src_len, arch.GetByteOrder(),
+ arch.GetAddressByteSize());
+
+ (void)disasm_sp->DecodeInstructions(start, data, 0, num_instructions,
+ false, data_from_file);
+ }
+ }
+
+ return disasm_sp;
+}
+
+bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
+ const char *plugin_name, const char *flavor,
+ const ExecutionContext &exe_ctx,
+ const AddressRange &disasm_range,
+ uint32_t num_instructions,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
+ if (disasm_range.GetByteSize()) {
+ lldb::DisassemblerSP disasm_sp(Disassembler::FindPluginForTarget(
+ exe_ctx.GetTargetSP(), arch, flavor, plugin_name));
+
+ if (disasm_sp) {
+ AddressRange range;
+ ResolveAddress(exe_ctx, disasm_range.GetBaseAddress(),
+ range.GetBaseAddress());
+ range.SetByteSize(disasm_range.GetByteSize());
+ const bool prefer_file_cache = false;
+ size_t bytes_disassembled = disasm_sp->ParseInstructions(
+ &exe_ctx, range, &strm, prefer_file_cache);
+ if (bytes_disassembled == 0)
+ return false;
+
+ return PrintInstructions(disasm_sp.get(), debugger, arch, exe_ctx,
+ num_instructions, mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm);
+ }
+ }
+ return false;
+}
+
+bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
+ const char *plugin_name, const char *flavor,
+ const ExecutionContext &exe_ctx,
+ const Address &start_address,
+ uint32_t num_instructions,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
+ if (num_instructions > 0) {
+ lldb::DisassemblerSP disasm_sp(Disassembler::FindPluginForTarget(
+ exe_ctx.GetTargetSP(), arch, flavor, plugin_name));
+ if (disasm_sp) {
+ Address addr;
+ ResolveAddress(exe_ctx, start_address, addr);
+ const bool prefer_file_cache = false;
+ size_t bytes_disassembled = disasm_sp->ParseInstructions(
+ &exe_ctx, addr, num_instructions, prefer_file_cache);
+ if (bytes_disassembled == 0)
+ return false;
+ return PrintInstructions(disasm_sp.get(), debugger, arch, exe_ctx,
+ num_instructions, mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm);
+ }
+ }
+ return false;
+}
+
+Disassembler::SourceLine
+Disassembler::GetFunctionDeclLineEntry(const SymbolContext &sc) {
+ SourceLine decl_line;
+ if (sc.function && sc.line_entry.IsValid()) {
+ LineEntry prologue_end_line = sc.line_entry;
+ FileSpec func_decl_file;
+ uint32_t func_decl_line;
+ sc.function->GetStartLineSourceInfo(func_decl_file, func_decl_line);
+ if (func_decl_file == prologue_end_line.file ||
+ func_decl_file == prologue_end_line.original_file) {
+ decl_line.file = func_decl_file;
+ decl_line.line = func_decl_line;
+ // TODO do we care about column on these entries? If so, we need to
+ // plumb that through GetStartLineSourceInfo.
+ decl_line.column = 0;
+ }
+ }
+ return decl_line;
+}
+
+void Disassembler::AddLineToSourceLineTables(
+ SourceLine &line,
+ std::map<FileSpec, std::set<uint32_t>> &source_lines_seen) {
+ if (line.IsValid()) {
+ auto source_lines_seen_pos = source_lines_seen.find(line.file);
+ if (source_lines_seen_pos == source_lines_seen.end()) {
+ std::set<uint32_t> lines;
+ lines.insert(line.line);
+ source_lines_seen.emplace(line.file, lines);
+ } else {
+ source_lines_seen_pos->second.insert(line.line);
+ }
+ }
+}
+
+bool Disassembler::ElideMixedSourceAndDisassemblyLine(
+ const ExecutionContext &exe_ctx, const SymbolContext &sc,
+ SourceLine &line) {
+
+ // TODO: should we also check target.process.thread.step-avoid-libraries ?
+
+ const RegularExpression *avoid_regex = nullptr;
+
+ // Skip any line #0 entries - they are implementation details
+ if (line.line == 0)
+ return false;
+
+ ThreadSP thread_sp = exe_ctx.GetThreadSP();
+ if (thread_sp) {
+ avoid_regex = thread_sp->GetSymbolsToAvoidRegexp();
+ } else {
+ TargetSP target_sp = exe_ctx.GetTargetSP();
+ if (target_sp) {
+ Status error;
+ OptionValueSP value_sp = target_sp->GetDebugger().GetPropertyValue(
+ &exe_ctx, "target.process.thread.step-avoid-regexp", false, error);
+ if (value_sp && value_sp->GetType() == OptionValue::eTypeRegex) {
+ OptionValueRegex *re = value_sp->GetAsRegex();
+ if (re) {
+ avoid_regex = re->GetCurrentValue();
+ }
+ }
+ }
+ }
+ if (avoid_regex && sc.symbol != nullptr) {
+ const char *function_name =
+ sc.GetFunctionName(Mangled::ePreferDemangledWithoutArguments)
+ .GetCString();
+ if (function_name) {
+ RegularExpression::Match regex_match(1);
+ if (avoid_regex->Execute(function_name, &regex_match)) {
+ // skip this source line
+ return true;
+ }
+ }
+ }
+ // don't skip this source line
+ return false;
+}
+
+bool Disassembler::PrintInstructions(Disassembler *disasm_ptr,
+ Debugger &debugger, const ArchSpec &arch,
+ const ExecutionContext &exe_ctx,
+ uint32_t num_instructions,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
+ // We got some things disassembled...
+ size_t num_instructions_found = disasm_ptr->GetInstructionList().GetSize();
+
+ if (num_instructions > 0 && num_instructions < num_instructions_found)
+ num_instructions_found = num_instructions;
+
+ const uint32_t max_opcode_byte_size =
+ disasm_ptr->GetInstructionList().GetMaxOpcocdeByteSize();
+ SymbolContext sc;
+ SymbolContext prev_sc;
+ AddressRange current_source_line_range;
+ const Address *pc_addr_ptr = nullptr;
+ StackFrame *frame = exe_ctx.GetFramePtr();
+
+ TargetSP target_sp(exe_ctx.GetTargetSP());
+ SourceManager &source_manager =
+ target_sp ? target_sp->GetSourceManager() : debugger.GetSourceManager();
+
+ if (frame) {
+ pc_addr_ptr = &frame->GetFrameCodeAddress();
+ }
+ const uint32_t scope =
+ eSymbolContextLineEntry | eSymbolContextFunction | eSymbolContextSymbol;
+ const bool use_inline_block_range = false;
+
+ const FormatEntity::Entry *disassembly_format = nullptr;
+ FormatEntity::Entry format;
+ if (exe_ctx.HasTargetScope()) {
+ disassembly_format =
+ exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ } else {
+ FormatEntity::Parse("${addr}: ", format);
+ disassembly_format = &format;
+ }
+
+ // First pass: step through the list of instructions, find how long the
+ // initial addresses strings are, insert padding in the second pass so the
+ // opcodes all line up nicely.
+
+ // Also build up the source line mapping if this is mixed source & assembly
+ // mode. Calculate the source line for each assembly instruction (eliding
+ // inlined functions which the user wants to skip).
+
+ std::map<FileSpec, std::set<uint32_t>> source_lines_seen;
+ Symbol *previous_symbol = nullptr;
+
+ size_t address_text_size = 0;
+ for (size_t i = 0; i < num_instructions_found; ++i) {
+ Instruction *inst =
+ disasm_ptr->GetInstructionList().GetInstructionAtIndex(i).get();
+ if (inst) {
+ const Address &addr = inst->GetAddress();
+ ModuleSP module_sp(addr.GetModule());
+ if (module_sp) {
+ const SymbolContextItem resolve_mask = eSymbolContextFunction |
+ eSymbolContextSymbol |
+ eSymbolContextLineEntry;
+ uint32_t resolved_mask =
+ module_sp->ResolveSymbolContextForAddress(addr, resolve_mask, sc);
+ if (resolved_mask) {
+ StreamString strmstr;
+ Debugger::FormatDisassemblerAddress(disassembly_format, &sc, nullptr,
+ &exe_ctx, &addr, strmstr);
+ size_t cur_line = strmstr.GetSizeOfLastLine();
+ if (cur_line > address_text_size)
+ address_text_size = cur_line;
+
+ // Add entries to our "source_lines_seen" map+set which list which
+ // sources lines occur in this disassembly session. We will print
+ // lines of context around a source line, but we don't want to print
+ // a source line that has a line table entry of its own - we'll leave
+ // that source line to be printed when it actually occurs in the
+ // disassembly.
+
+ if (mixed_source_and_assembly && sc.line_entry.IsValid()) {
+ if (sc.symbol != previous_symbol) {
+ SourceLine decl_line = GetFunctionDeclLineEntry(sc);
+ if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, decl_line))
+ AddLineToSourceLineTables(decl_line, source_lines_seen);
+ }
+ if (sc.line_entry.IsValid()) {
+ SourceLine this_line;
+ this_line.file = sc.line_entry.file;
+ this_line.line = sc.line_entry.line;
+ this_line.column = sc.line_entry.column;
+ if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, this_line))
+ AddLineToSourceLineTables(this_line, source_lines_seen);
+ }
+ }
+ }
+ sc.Clear(false);
+ }
+ }
+ }
+
+ previous_symbol = nullptr;
+ SourceLine previous_line;
+ for (size_t i = 0; i < num_instructions_found; ++i) {
+ Instruction *inst =
+ disasm_ptr->GetInstructionList().GetInstructionAtIndex(i).get();
+
+ if (inst) {
+ const Address &addr = inst->GetAddress();
+ const bool inst_is_at_pc = pc_addr_ptr && addr == *pc_addr_ptr;
+ SourceLinesToDisplay source_lines_to_display;
+
+ prev_sc = sc;
+
+ ModuleSP module_sp(addr.GetModule());
+ if (module_sp) {
+ uint32_t resolved_mask = module_sp->ResolveSymbolContextForAddress(
+ addr, eSymbolContextEverything, sc);
+ if (resolved_mask) {
+ if (mixed_source_and_assembly) {
+
+ // If we've started a new function (non-inlined), print all of the
+ // source lines from the function declaration until the first line
+ // table entry - typically the opening curly brace of the function.
+ if (previous_symbol != sc.symbol) {
+ // The default disassembly format puts an extra blank line
+ // between functions - so when we're displaying the source
+ // context for a function, we don't want to add a blank line
+ // after the source context or we'll end up with two of them.
+ if (previous_symbol != nullptr)
+ source_lines_to_display.print_source_context_end_eol = false;
+
+ previous_symbol = sc.symbol;
+ if (sc.function && sc.line_entry.IsValid()) {
+ LineEntry prologue_end_line = sc.line_entry;
+ if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc,
+ prologue_end_line)) {
+ FileSpec func_decl_file;
+ uint32_t func_decl_line;
+ sc.function->GetStartLineSourceInfo(func_decl_file,
+ func_decl_line);
+ if (func_decl_file == prologue_end_line.file ||
+ func_decl_file == prologue_end_line.original_file) {
+ // Add all the lines between the function declaration and
+ // the first non-prologue source line to the list of lines
+ // to print.
+ for (uint32_t lineno = func_decl_line;
+ lineno <= prologue_end_line.line; lineno++) {
+ SourceLine this_line;
+ this_line.file = func_decl_file;
+ this_line.line = lineno;
+ source_lines_to_display.lines.push_back(this_line);
+ }
+ // Mark the last line as the "current" one. Usually this
+ // is the open curly brace.
+ if (source_lines_to_display.lines.size() > 0)
+ source_lines_to_display.current_source_line =
+ source_lines_to_display.lines.size() - 1;
+ }
+ }
+ }
+ sc.GetAddressRange(scope, 0, use_inline_block_range,
+ current_source_line_range);
+ }
+
+ // If we've left a previous source line's address range, print a
+ // new source line
+ if (!current_source_line_range.ContainsFileAddress(addr)) {
+ sc.GetAddressRange(scope, 0, use_inline_block_range,
+ current_source_line_range);
+
+ if (sc != prev_sc && sc.comp_unit && sc.line_entry.IsValid()) {
+ SourceLine this_line;
+ this_line.file = sc.line_entry.file;
+ this_line.line = sc.line_entry.line;
+
+ if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc,
+ this_line)) {
+ // Only print this source line if it is different from the
+ // last source line we printed. There may have been inlined
+ // functions between these lines that we elided, resulting in
+ // the same line being printed twice in a row for a
+ // contiguous block of assembly instructions.
+ if (this_line != previous_line) {
+
+ std::vector<uint32_t> previous_lines;
+ for (uint32_t i = 0;
+ i < num_mixed_context_lines &&
+ (this_line.line - num_mixed_context_lines) > 0;
+ i++) {
+ uint32_t line =
+ this_line.line - num_mixed_context_lines + i;
+ auto pos = source_lines_seen.find(this_line.file);
+ if (pos != source_lines_seen.end()) {
+ if (pos->second.count(line) == 1) {
+ previous_lines.clear();
+ } else {
+ previous_lines.push_back(line);
+ }
+ }
+ }
+ for (size_t i = 0; i < previous_lines.size(); i++) {
+ SourceLine previous_line;
+ previous_line.file = this_line.file;
+ previous_line.line = previous_lines[i];
+ auto pos = source_lines_seen.find(previous_line.file);
+ if (pos != source_lines_seen.end()) {
+ pos->second.insert(previous_line.line);
+ }
+ source_lines_to_display.lines.push_back(previous_line);
+ }
+
+ source_lines_to_display.lines.push_back(this_line);
+ source_lines_to_display.current_source_line =
+ source_lines_to_display.lines.size() - 1;
+
+ for (uint32_t i = 0; i < num_mixed_context_lines; i++) {
+ SourceLine next_line;
+ next_line.file = this_line.file;
+ next_line.line = this_line.line + i + 1;
+ auto pos = source_lines_seen.find(next_line.file);
+ if (pos != source_lines_seen.end()) {
+ if (pos->second.count(next_line.line) == 1)
+ break;
+ pos->second.insert(next_line.line);
+ }
+ source_lines_to_display.lines.push_back(next_line);
+ }
+ }
+ previous_line = this_line;
+ }
+ }
+ }
+ }
+ } else {
+ sc.Clear(true);
+ }
+ }
+
+ if (source_lines_to_display.lines.size() > 0) {
+ strm.EOL();
+ for (size_t idx = 0; idx < source_lines_to_display.lines.size();
+ idx++) {
+ SourceLine ln = source_lines_to_display.lines[idx];
+ const char *line_highlight = "";
+ if (inst_is_at_pc && (options & eOptionMarkPCSourceLine)) {
+ line_highlight = "->";
+ } else if (idx == source_lines_to_display.current_source_line) {
+ line_highlight = "**";
+ }
+ source_manager.DisplaySourceLinesWithLineNumbers(
+ ln.file, ln.line, ln.column, 0, 0, line_highlight, &strm);
+ }
+ if (source_lines_to_display.print_source_context_end_eol)
+ strm.EOL();
+ }
+
+ const bool show_bytes = (options & eOptionShowBytes) != 0;
+ inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx, &sc,
+ &prev_sc, nullptr, address_text_size);
+ strm.EOL();
+ } else {
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
+ const char *plugin_name, const char *flavor,
+ const ExecutionContext &exe_ctx,
+ uint32_t num_instructions,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
+ AddressRange range;
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ SymbolContext sc(
+ frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol));
+ if (sc.function) {
+ range = sc.function->GetAddressRange();
+ } else if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ range.GetBaseAddress() = sc.symbol->GetAddressRef();
+ range.SetByteSize(sc.symbol->GetByteSize());
+ } else {
+ range.GetBaseAddress() = frame->GetFrameCodeAddress();
+ }
+
+ if (range.GetBaseAddress().IsValid() && range.GetByteSize() == 0)
+ range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE);
+ }
+
+ return Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, range,
+ num_instructions, mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm);
+}
+
+Instruction::Instruction(const Address &address, AddressClass addr_class)
+ : m_address(address), m_address_class(addr_class), m_opcode(),
+ m_calculated_strings(false) {}
+
+Instruction::~Instruction() = default;
+
+AddressClass Instruction::GetAddressClass() {
+ if (m_address_class == AddressClass::eInvalid)
+ m_address_class = m_address.GetAddressClass();
+ return m_address_class;
+}
+
+void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
+ bool show_address, bool show_bytes,
+ const ExecutionContext *exe_ctx,
+ const SymbolContext *sym_ctx,
+ const SymbolContext *prev_sym_ctx,
+ const FormatEntity::Entry *disassembly_addr_format,
+ size_t max_address_text_size) {
+ size_t opcode_column_width = 7;
+ const size_t operand_column_width = 25;
+
+ CalculateMnemonicOperandsAndCommentIfNeeded(exe_ctx);
+
+ StreamString ss;
+
+ if (show_address) {
+ Debugger::FormatDisassemblerAddress(disassembly_addr_format, sym_ctx,
+ prev_sym_ctx, exe_ctx, &m_address, ss);
+ ss.FillLastLineToColumn(max_address_text_size, ' ');
+ }
+
+ if (show_bytes) {
+ if (m_opcode.GetType() == Opcode::eTypeBytes) {
+ // x86_64 and i386 are the only ones that use bytes right now so pad out
+ // the byte dump to be able to always show 15 bytes (3 chars each) plus a
+ // space
+ if (max_opcode_byte_size > 0)
+ m_opcode.Dump(&ss, max_opcode_byte_size * 3 + 1);
+ else
+ m_opcode.Dump(&ss, 15 * 3 + 1);
+ } else {
+ // Else, we have ARM or MIPS which can show up to a uint32_t 0x00000000
+ // (10 spaces) plus two for padding...
+ if (max_opcode_byte_size > 0)
+ m_opcode.Dump(&ss, max_opcode_byte_size * 3 + 1);
+ else
+ m_opcode.Dump(&ss, 12);
+ }
+ }
+
+ const size_t opcode_pos = ss.GetSizeOfLastLine();
+
+ // The default opcode size of 7 characters is plenty for most architectures
+ // but some like arm can pull out the occasional vqrshrun.s16. We won't get
+ // consistent column spacing in these cases, unfortunately.
+ if (m_opcode_name.length() >= opcode_column_width) {
+ opcode_column_width = m_opcode_name.length() + 1;
+ }
+
+ ss.PutCString(m_opcode_name);
+ ss.FillLastLineToColumn(opcode_pos + opcode_column_width, ' ');
+ ss.PutCString(m_mnemonics);
+
+ if (!m_comment.empty()) {
+ ss.FillLastLineToColumn(
+ opcode_pos + opcode_column_width + operand_column_width, ' ');
+ ss.PutCString(" ; ");
+ ss.PutCString(m_comment);
+ }
+ s->PutCString(ss.GetString());
+}
+
+bool Instruction::DumpEmulation(const ArchSpec &arch) {
+ std::unique_ptr<EmulateInstruction> insn_emulator_up(
+ EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr));
+ if (insn_emulator_up) {
+ insn_emulator_up->SetInstruction(GetOpcode(), GetAddress(), nullptr);
+ return insn_emulator_up->EvaluateInstruction(0);
+ }
+
+ return false;
+}
+
+bool Instruction::CanSetBreakpoint () {
+ return !HasDelaySlot();
+}
+
+bool Instruction::HasDelaySlot() {
+ // Default is false.
+ return false;
+}
+
+OptionValueSP Instruction::ReadArray(FILE *in_file, Stream *out_stream,
+ OptionValue::Type data_type) {
+ bool done = false;
+ char buffer[1024];
+
+ auto option_value_sp = std::make_shared<OptionValueArray>(1u << data_type);
+
+ int idx = 0;
+ while (!done) {
+ if (!fgets(buffer, 1023, in_file)) {
+ out_stream->Printf(
+ "Instruction::ReadArray: Error reading file (fgets).\n");
+ option_value_sp.reset();
+ return option_value_sp;
+ }
+
+ std::string line(buffer);
+
+ size_t len = line.size();
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ line.resize(len - 1);
+ }
+
+ if ((line.size() == 1) && line[0] == ']') {
+ done = true;
+ line.clear();
+ }
+
+ if (!line.empty()) {
+ std::string value;
+ static RegularExpression g_reg_exp(
+ llvm::StringRef("^[ \t]*([^ \t]+)[ \t]*$"));
+ RegularExpression::Match regex_match(1);
+ bool reg_exp_success = g_reg_exp.Execute(line, &regex_match);
+ if (reg_exp_success)
+ regex_match.GetMatchAtIndex(line.c_str(), 1, value);
+ else
+ value = line;
+
+ OptionValueSP data_value_sp;
+ switch (data_type) {
+ case OptionValue::eTypeUInt64:
+ data_value_sp = std::make_shared<OptionValueUInt64>(0, 0);
+ data_value_sp->SetValueFromString(value);
+ break;
+ // Other types can be added later as needed.
+ default:
+ data_value_sp = std::make_shared<OptionValueString>(value.c_str(), "");
+ break;
+ }
+
+ option_value_sp->GetAsArray()->InsertValue(idx, data_value_sp);
+ ++idx;
+ }
+ }
+
+ return option_value_sp;
+}
+
+OptionValueSP Instruction::ReadDictionary(FILE *in_file, Stream *out_stream) {
+ bool done = false;
+ char buffer[1024];
+
+ auto option_value_sp = std::make_shared<OptionValueDictionary>();
+ static ConstString encoding_key("data_encoding");
+ OptionValue::Type data_type = OptionValue::eTypeInvalid;
+
+ while (!done) {
+ // Read the next line in the file
+ if (!fgets(buffer, 1023, in_file)) {
+ out_stream->Printf(
+ "Instruction::ReadDictionary: Error reading file (fgets).\n");
+ option_value_sp.reset();
+ return option_value_sp;
+ }
+
+ // Check to see if the line contains the end-of-dictionary marker ("}")
+ std::string line(buffer);
+
+ size_t len = line.size();
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ line.resize(len - 1);
+ }
+
+ if ((line.size() == 1) && (line[0] == '}')) {
+ done = true;
+ line.clear();
+ }
+
+ // Try to find a key-value pair in the current line and add it to the
+ // dictionary.
+ if (!line.empty()) {
+ static RegularExpression g_reg_exp(llvm::StringRef(
+ "^[ \t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*=[ \t]*(.*)[ \t]*$"));
+ RegularExpression::Match regex_match(2);
+
+ bool reg_exp_success = g_reg_exp.Execute(line, &regex_match);
+ std::string key;
+ std::string value;
+ if (reg_exp_success) {
+ regex_match.GetMatchAtIndex(line.c_str(), 1, key);
+ regex_match.GetMatchAtIndex(line.c_str(), 2, value);
+ } else {
+ out_stream->Printf("Instruction::ReadDictionary: Failure executing "
+ "regular expression.\n");
+ option_value_sp.reset();
+ return option_value_sp;
+ }
+
+ ConstString const_key(key.c_str());
+ // Check value to see if it's the start of an array or dictionary.
+
+ lldb::OptionValueSP value_sp;
+ assert(value.empty() == false);
+ assert(key.empty() == false);
+
+ if (value[0] == '{') {
+ assert(value.size() == 1);
+ // value is a dictionary
+ value_sp = ReadDictionary(in_file, out_stream);
+ if (!value_sp) {
+ option_value_sp.reset();
+ return option_value_sp;
+ }
+ } else if (value[0] == '[') {
+ assert(value.size() == 1);
+ // value is an array
+ value_sp = ReadArray(in_file, out_stream, data_type);
+ if (!value_sp) {
+ option_value_sp.reset();
+ return option_value_sp;
+ }
+ // We've used the data_type to read an array; re-set the type to
+ // Invalid
+ data_type = OptionValue::eTypeInvalid;
+ } else if ((value[0] == '0') && (value[1] == 'x')) {
+ value_sp = std::make_shared<OptionValueUInt64>(0, 0);
+ value_sp->SetValueFromString(value);
+ } else {
+ size_t len = value.size();
+ if ((value[0] == '"') && (value[len - 1] == '"'))
+ value = value.substr(1, len - 2);
+ value_sp = std::make_shared<OptionValueString>(value.c_str(), "");
+ }
+
+ if (const_key == encoding_key) {
+ // A 'data_encoding=..." is NOT a normal key-value pair; it is meta-data
+ // indicating the
+ // data type of an upcoming array (usually the next bit of data to be
+ // read in).
+ if (strcmp(value.c_str(), "uint32_t") == 0)
+ data_type = OptionValue::eTypeUInt64;
+ } else
+ option_value_sp->GetAsDictionary()->SetValueForKey(const_key, value_sp,
+ false);
+ }
+ }
+
+ return option_value_sp;
+}
+
+bool Instruction::TestEmulation(Stream *out_stream, const char *file_name) {
+ if (!out_stream)
+ return false;
+
+ if (!file_name) {
+ out_stream->Printf("Instruction::TestEmulation: Missing file_name.");
+ return false;
+ }
+ FILE *test_file = FileSystem::Instance().Fopen(file_name, "r");
+ if (!test_file) {
+ out_stream->Printf(
+ "Instruction::TestEmulation: Attempt to open test file failed.");
+ return false;
+ }
+
+ char buffer[256];
+ if (!fgets(buffer, 255, test_file)) {
+ out_stream->Printf(
+ "Instruction::TestEmulation: Error reading first line of test file.\n");
+ fclose(test_file);
+ return false;
+ }
+
+ if (strncmp(buffer, "InstructionEmulationState={", 27) != 0) {
+ out_stream->Printf("Instructin::TestEmulation: Test file does not contain "
+ "emulation state dictionary\n");
+ fclose(test_file);
+ return false;
+ }
+
+ // Read all the test information from the test file into an
+ // OptionValueDictionary.
+
+ OptionValueSP data_dictionary_sp(ReadDictionary(test_file, out_stream));
+ if (!data_dictionary_sp) {
+ out_stream->Printf(
+ "Instruction::TestEmulation: Error reading Dictionary Object.\n");
+ fclose(test_file);
+ return false;
+ }
+
+ fclose(test_file);
+
+ OptionValueDictionary *data_dictionary =
+ data_dictionary_sp->GetAsDictionary();
+ static ConstString description_key("assembly_string");
+ static ConstString triple_key("triple");
+
+ OptionValueSP value_sp = data_dictionary->GetValueForKey(description_key);
+
+ if (!value_sp) {
+ out_stream->Printf("Instruction::TestEmulation: Test file does not "
+ "contain description string.\n");
+ return false;
+ }
+
+ SetDescription(value_sp->GetStringValue());
+
+ value_sp = data_dictionary->GetValueForKey(triple_key);
+ if (!value_sp) {
+ out_stream->Printf(
+ "Instruction::TestEmulation: Test file does not contain triple.\n");
+ return false;
+ }
+
+ ArchSpec arch;
+ arch.SetTriple(llvm::Triple(value_sp->GetStringValue()));
+
+ bool success = false;
+ std::unique_ptr<EmulateInstruction> insn_emulator_up(
+ EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr));
+ if (insn_emulator_up)
+ success =
+ insn_emulator_up->TestEmulation(out_stream, arch, data_dictionary);
+
+ if (success)
+ out_stream->Printf("Emulation test succeeded.");
+ else
+ out_stream->Printf("Emulation test failed.");
+
+ return success;
+}
+
+bool Instruction::Emulate(
+ const ArchSpec &arch, uint32_t evaluate_options, void *baton,
+ EmulateInstruction::ReadMemoryCallback read_mem_callback,
+ EmulateInstruction::WriteMemoryCallback write_mem_callback,
+ EmulateInstruction::ReadRegisterCallback read_reg_callback,
+ EmulateInstruction::WriteRegisterCallback write_reg_callback) {
+ std::unique_ptr<EmulateInstruction> insn_emulator_up(
+ EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr));
+ if (insn_emulator_up) {
+ insn_emulator_up->SetBaton(baton);
+ insn_emulator_up->SetCallbacks(read_mem_callback, write_mem_callback,
+ read_reg_callback, write_reg_callback);
+ insn_emulator_up->SetInstruction(GetOpcode(), GetAddress(), nullptr);
+ return insn_emulator_up->EvaluateInstruction(evaluate_options);
+ }
+
+ return false;
+}
+
+uint32_t Instruction::GetData(DataExtractor &data) {
+ return m_opcode.GetData(data);
+}
+
+InstructionList::InstructionList() : m_instructions() {}
+
+InstructionList::~InstructionList() = default;
+
+size_t InstructionList::GetSize() const { return m_instructions.size(); }
+
+uint32_t InstructionList::GetMaxOpcocdeByteSize() const {
+ uint32_t max_inst_size = 0;
+ collection::const_iterator pos, end;
+ for (pos = m_instructions.begin(), end = m_instructions.end(); pos != end;
+ ++pos) {
+ uint32_t inst_size = (*pos)->GetOpcode().GetByteSize();
+ if (max_inst_size < inst_size)
+ max_inst_size = inst_size;
+ }
+ return max_inst_size;
+}
+
+InstructionSP InstructionList::GetInstructionAtIndex(size_t idx) const {
+ InstructionSP inst_sp;
+ if (idx < m_instructions.size())
+ inst_sp = m_instructions[idx];
+ return inst_sp;
+}
+
+void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes,
+ const ExecutionContext *exe_ctx) {
+ const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize();
+ collection::const_iterator pos, begin, end;
+
+ const FormatEntity::Entry *disassembly_format = nullptr;
+ FormatEntity::Entry format;
+ if (exe_ctx && exe_ctx->HasTargetScope()) {
+ disassembly_format =
+ exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ } else {
+ FormatEntity::Parse("${addr}: ", format);
+ disassembly_format = &format;
+ }
+
+ for (begin = m_instructions.begin(), end = m_instructions.end(), pos = begin;
+ pos != end; ++pos) {
+ if (pos != begin)
+ s->EOL();
+ (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx,
+ nullptr, nullptr, disassembly_format, 0);
+ }
+}
+
+void InstructionList::Clear() { m_instructions.clear(); }
+
+void InstructionList::Append(lldb::InstructionSP &inst_sp) {
+ if (inst_sp)
+ m_instructions.push_back(inst_sp);
+}
+
+uint32_t
+InstructionList::GetIndexOfNextBranchInstruction(uint32_t start,
+ Target &target,
+ bool ignore_calls) const {
+ size_t num_instructions = m_instructions.size();
+
+ uint32_t next_branch = UINT32_MAX;
+ size_t i;
+ for (i = start; i < num_instructions; i++) {
+ if (m_instructions[i]->DoesBranch()) {
+ if (ignore_calls && m_instructions[i]->IsCall())
+ continue;
+ next_branch = i;
+ break;
+ }
+ }
+
+ // Hexagon needs the first instruction of the packet with the branch. Go
+ // backwards until we find an instruction marked end-of-packet, or until we
+ // hit start.
+ if (target.GetArchitecture().GetTriple().getArch() == llvm::Triple::hexagon) {
+ // If we didn't find a branch, find the last packet start.
+ if (next_branch == UINT32_MAX) {
+ i = num_instructions - 1;
+ }
+
+ while (i > start) {
+ --i;
+
+ Status error;
+ uint32_t inst_bytes;
+ bool prefer_file_cache = false; // Read from process if process is running
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ target.ReadMemory(m_instructions[i]->GetAddress(), prefer_file_cache,
+ &inst_bytes, sizeof(inst_bytes), error, &load_addr);
+ // If we have an error reading memory, return start
+ if (!error.Success())
+ return start;
+ // check if this is the last instruction in a packet bits 15:14 will be
+ // 11b or 00b for a duplex
+ if (((inst_bytes & 0xC000) == 0xC000) ||
+ ((inst_bytes & 0xC000) == 0x0000)) {
+ // instruction after this should be the start of next packet
+ next_branch = i + 1;
+ break;
+ }
+ }
+
+ if (next_branch == UINT32_MAX) {
+ // We couldn't find the previous packet, so return start
+ next_branch = start;
+ }
+ }
+ return next_branch;
+}
+
+uint32_t
+InstructionList::GetIndexOfInstructionAtAddress(const Address &address) {
+ size_t num_instructions = m_instructions.size();
+ uint32_t index = UINT32_MAX;
+ for (size_t i = 0; i < num_instructions; i++) {
+ if (m_instructions[i]->GetAddress() == address) {
+ index = i;
+ break;
+ }
+ }
+ return index;
+}
+
+uint32_t
+InstructionList::GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr,
+ Target &target) {
+ Address address;
+ address.SetLoadAddress(load_addr, &target);
+ return GetIndexOfInstructionAtAddress(address);
+}
+
+size_t Disassembler::ParseInstructions(const ExecutionContext *exe_ctx,
+ const AddressRange &range,
+ Stream *error_strm_ptr,
+ bool prefer_file_cache) {
+ if (exe_ctx) {
+ Target *target = exe_ctx->GetTargetPtr();
+ const addr_t byte_size = range.GetByteSize();
+ if (target == nullptr || byte_size == 0 ||
+ !range.GetBaseAddress().IsValid())
+ return 0;
+
+ auto data_sp = std::make_shared<DataBufferHeap>(byte_size, '\0');
+
+ Status error;
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ const size_t bytes_read = target->ReadMemory(
+ range.GetBaseAddress(), prefer_file_cache, data_sp->GetBytes(),
+ data_sp->GetByteSize(), error, &load_addr);
+
+ if (bytes_read > 0) {
+ if (bytes_read != data_sp->GetByteSize())
+ data_sp->SetByteSize(bytes_read);
+ DataExtractor data(data_sp, m_arch.GetByteOrder(),
+ m_arch.GetAddressByteSize());
+ const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS;
+ return DecodeInstructions(range.GetBaseAddress(), data, 0, UINT32_MAX,
+ false, data_from_file);
+ } else if (error_strm_ptr) {
+ const char *error_cstr = error.AsCString();
+ if (error_cstr) {
+ error_strm_ptr->Printf("error: %s\n", error_cstr);
+ }
+ }
+ } else if (error_strm_ptr) {
+ error_strm_ptr->PutCString("error: invalid execution context\n");
+ }
+ return 0;
+}
+
+size_t Disassembler::ParseInstructions(const ExecutionContext *exe_ctx,
+ const Address &start,
+ uint32_t num_instructions,
+ bool prefer_file_cache) {
+ m_instruction_list.Clear();
+
+ if (exe_ctx == nullptr || num_instructions == 0 || !start.IsValid())
+ return 0;
+
+ Target *target = exe_ctx->GetTargetPtr();
+ // Calculate the max buffer size we will need in order to disassemble
+ const addr_t byte_size = num_instructions * m_arch.GetMaximumOpcodeByteSize();
+
+ if (target == nullptr || byte_size == 0)
+ return 0;
+
+ DataBufferHeap *heap_buffer = new DataBufferHeap(byte_size, '\0');
+ DataBufferSP data_sp(heap_buffer);
+
+ Status error;
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ const size_t bytes_read =
+ target->ReadMemory(start, prefer_file_cache, heap_buffer->GetBytes(),
+ byte_size, error, &load_addr);
+
+ const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS;
+
+ if (bytes_read == 0)
+ return 0;
+ DataExtractor data(data_sp, m_arch.GetByteOrder(),
+ m_arch.GetAddressByteSize());
+
+ const bool append_instructions = true;
+ DecodeInstructions(start, data, 0, num_instructions, append_instructions,
+ data_from_file);
+
+ return m_instruction_list.GetSize();
+}
+
+// Disassembler copy constructor
+Disassembler::Disassembler(const ArchSpec &arch, const char *flavor)
+ : m_arch(arch), m_instruction_list(), m_base_addr(LLDB_INVALID_ADDRESS),
+ m_flavor() {
+ if (flavor == nullptr)
+ m_flavor.assign("default");
+ else
+ m_flavor.assign(flavor);
+
+ // If this is an arm variant that can only include thumb (T16, T32)
+ // instructions, force the arch triple to be "thumbv.." instead of "armv..."
+ if (arch.IsAlwaysThumbInstructions()) {
+ std::string thumb_arch_name(arch.GetTriple().getArchName().str());
+ // Replace "arm" with "thumb" so we get all thumb variants correct
+ if (thumb_arch_name.size() > 3) {
+ thumb_arch_name.erase(0, 3);
+ thumb_arch_name.insert(0, "thumb");
+ }
+ m_arch.SetTriple(thumb_arch_name.c_str());
+ }
+}
+
+Disassembler::~Disassembler() = default;
+
+InstructionList &Disassembler::GetInstructionList() {
+ return m_instruction_list;
+}
+
+const InstructionList &Disassembler::GetInstructionList() const {
+ return m_instruction_list;
+}
+
+// Class PseudoInstruction
+
+PseudoInstruction::PseudoInstruction()
+ : Instruction(Address(), AddressClass::eUnknown), m_description() {}
+
+PseudoInstruction::~PseudoInstruction() = default;
+
+bool PseudoInstruction::DoesBranch() {
+ // This is NOT a valid question for a pseudo instruction.
+ return false;
+}
+
+bool PseudoInstruction::HasDelaySlot() {
+ // This is NOT a valid question for a pseudo instruction.
+ return false;
+}
+
+size_t PseudoInstruction::Decode(const lldb_private::Disassembler &disassembler,
+ const lldb_private::DataExtractor &data,
+ lldb::offset_t data_offset) {
+ return m_opcode.GetByteSize();
+}
+
+void PseudoInstruction::SetOpcode(size_t opcode_size, void *opcode_data) {
+ if (!opcode_data)
+ return;
+
+ switch (opcode_size) {
+ case 8: {
+ uint8_t value8 = *((uint8_t *)opcode_data);
+ m_opcode.SetOpcode8(value8, eByteOrderInvalid);
+ break;
+ }
+ case 16: {
+ uint16_t value16 = *((uint16_t *)opcode_data);
+ m_opcode.SetOpcode16(value16, eByteOrderInvalid);
+ break;
+ }
+ case 32: {
+ uint32_t value32 = *((uint32_t *)opcode_data);
+ m_opcode.SetOpcode32(value32, eByteOrderInvalid);
+ break;
+ }
+ case 64: {
+ uint64_t value64 = *((uint64_t *)opcode_data);
+ m_opcode.SetOpcode64(value64, eByteOrderInvalid);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void PseudoInstruction::SetDescription(llvm::StringRef description) {
+ m_description = description;
+}
+
+Instruction::Operand Instruction::Operand::BuildRegister(ConstString &r) {
+ Operand ret;
+ ret.m_type = Type::Register;
+ ret.m_register = r;
+ return ret;
+}
+
+Instruction::Operand Instruction::Operand::BuildImmediate(lldb::addr_t imm,
+ bool neg) {
+ Operand ret;
+ ret.m_type = Type::Immediate;
+ ret.m_immediate = imm;
+ ret.m_negative = neg;
+ return ret;
+}
+
+Instruction::Operand Instruction::Operand::BuildImmediate(int64_t imm) {
+ Operand ret;
+ ret.m_type = Type::Immediate;
+ if (imm < 0) {
+ ret.m_immediate = -imm;
+ ret.m_negative = true;
+ } else {
+ ret.m_immediate = imm;
+ ret.m_negative = false;
+ }
+ return ret;
+}
+
+Instruction::Operand
+Instruction::Operand::BuildDereference(const Operand &ref) {
+ Operand ret;
+ ret.m_type = Type::Dereference;
+ ret.m_children = {ref};
+ return ret;
+}
+
+Instruction::Operand Instruction::Operand::BuildSum(const Operand &lhs,
+ const Operand &rhs) {
+ Operand ret;
+ ret.m_type = Type::Sum;
+ ret.m_children = {lhs, rhs};
+ return ret;
+}
+
+Instruction::Operand Instruction::Operand::BuildProduct(const Operand &lhs,
+ const Operand &rhs) {
+ Operand ret;
+ ret.m_type = Type::Product;
+ ret.m_children = {lhs, rhs};
+ return ret;
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::MatchBinaryOp(
+ std::function<bool(const Instruction::Operand &)> base,
+ std::function<bool(const Instruction::Operand &)> left,
+ std::function<bool(const Instruction::Operand &)> right) {
+ return [base, left, right](const Instruction::Operand &op) -> bool {
+ return (base(op) && op.m_children.size() == 2 &&
+ ((left(op.m_children[0]) && right(op.m_children[1])) ||
+ (left(op.m_children[1]) && right(op.m_children[0]))));
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::MatchUnaryOp(
+ std::function<bool(const Instruction::Operand &)> base,
+ std::function<bool(const Instruction::Operand &)> child) {
+ return [base, child](const Instruction::Operand &op) -> bool {
+ return (base(op) && op.m_children.size() == 1 && child(op.m_children[0]));
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::MatchRegOp(const RegisterInfo &info) {
+ return [&info](const Instruction::Operand &op) {
+ return (op.m_type == Instruction::Operand::Type::Register &&
+ (op.m_register == ConstString(info.name) ||
+ op.m_register == ConstString(info.alt_name)));
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::FetchRegOp(ConstString &reg) {
+ return [&reg](const Instruction::Operand &op) {
+ if (op.m_type != Instruction::Operand::Type::Register) {
+ return false;
+ }
+ reg = op.m_register;
+ return true;
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::MatchImmOp(int64_t imm) {
+ return [imm](const Instruction::Operand &op) {
+ return (op.m_type == Instruction::Operand::Type::Immediate &&
+ ((op.m_negative && op.m_immediate == (uint64_t)-imm) ||
+ (!op.m_negative && op.m_immediate == (uint64_t)imm)));
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::FetchImmOp(int64_t &imm) {
+ return [&imm](const Instruction::Operand &op) {
+ if (op.m_type != Instruction::Operand::Type::Immediate) {
+ return false;
+ }
+ if (op.m_negative) {
+ imm = -((int64_t)op.m_immediate);
+ } else {
+ imm = ((int64_t)op.m_immediate);
+ }
+ return true;
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::MatchOpType(Instruction::Operand::Type type) {
+ return [type](const Instruction::Operand &op) { return op.m_type == type; };
+}
diff --git a/contrib/llvm-project/lldb/source/Core/DumpDataExtractor.cpp b/contrib/llvm-project/lldb/source/Core/DumpDataExtractor.cpp
new file mode 100644
index 000000000000..aa84370e223a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/DumpDataExtractor.cpp
@@ -0,0 +1,831 @@
+//===-- DumpDataExtractor.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DumpDataExtractor.h"
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-forward.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Stream.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/CanonicalType.h"
+
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include <limits>
+#include <memory>
+#include <string>
+
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include <bitset>
+#include <sstream>
+
+using namespace lldb_private;
+using namespace lldb;
+
+#define NON_PRINTABLE_CHAR '.'
+
+static float half2float(uint16_t half) {
+ union {
+ float f;
+ uint32_t u;
+ } u;
+ int32_t v = (int16_t)half;
+
+ if (0 == (v & 0x7c00)) {
+ u.u = v & 0x80007FFFU;
+ return u.f * ldexpf(1, 125);
+ }
+
+ v <<= 13;
+ u.u = v | 0x70000000U;
+ return u.f * ldexpf(1, -112);
+}
+
+static bool GetAPInt(const DataExtractor &data, lldb::offset_t *offset_ptr,
+ lldb::offset_t byte_size, llvm::APInt &result) {
+ llvm::SmallVector<uint64_t, 2> uint64_array;
+ lldb::offset_t bytes_left = byte_size;
+ uint64_t u64;
+ const lldb::ByteOrder byte_order = data.GetByteOrder();
+ if (byte_order == lldb::eByteOrderLittle) {
+ while (bytes_left > 0) {
+ if (bytes_left >= 8) {
+ u64 = data.GetU64(offset_ptr);
+ bytes_left -= 8;
+ } else {
+ u64 = data.GetMaxU64(offset_ptr, (uint32_t)bytes_left);
+ bytes_left = 0;
+ }
+ uint64_array.push_back(u64);
+ }
+ result = llvm::APInt(byte_size * 8, llvm::ArrayRef<uint64_t>(uint64_array));
+ return true;
+ } else if (byte_order == lldb::eByteOrderBig) {
+ lldb::offset_t be_offset = *offset_ptr + byte_size;
+ lldb::offset_t temp_offset;
+ while (bytes_left > 0) {
+ if (bytes_left >= 8) {
+ be_offset -= 8;
+ temp_offset = be_offset;
+ u64 = data.GetU64(&temp_offset);
+ bytes_left -= 8;
+ } else {
+ be_offset -= bytes_left;
+ temp_offset = be_offset;
+ u64 = data.GetMaxU64(&temp_offset, (uint32_t)bytes_left);
+ bytes_left = 0;
+ }
+ uint64_array.push_back(u64);
+ }
+ *offset_ptr += byte_size;
+ result = llvm::APInt(byte_size * 8, llvm::ArrayRef<uint64_t>(uint64_array));
+ return true;
+ }
+ return false;
+}
+
+static lldb::offset_t DumpAPInt(Stream *s, const DataExtractor &data,
+ lldb::offset_t offset, lldb::offset_t byte_size,
+ bool is_signed, unsigned radix) {
+ llvm::APInt apint;
+ if (GetAPInt(data, &offset, byte_size, apint)) {
+ std::string apint_str(apint.toString(radix, is_signed));
+ switch (radix) {
+ case 2:
+ s->Write("0b", 2);
+ break;
+ case 8:
+ s->Write("0", 1);
+ break;
+ case 10:
+ break;
+ }
+ s->Write(apint_str.c_str(), apint_str.size());
+ }
+ return offset;
+}
+
+lldb::offset_t lldb_private::DumpDataExtractor(
+ const DataExtractor &DE, Stream *s, offset_t start_offset,
+ lldb::Format item_format, size_t item_byte_size, size_t item_count,
+ size_t num_per_line, uint64_t base_addr,
+ uint32_t item_bit_size, // If zero, this is not a bitfield value, if
+ // non-zero, the value is a bitfield
+ uint32_t item_bit_offset, // If "item_bit_size" is non-zero, this is the
+ // shift amount to apply to a bitfield
+ ExecutionContextScope *exe_scope) {
+ if (s == nullptr)
+ return start_offset;
+
+ if (item_format == eFormatPointer) {
+ if (item_byte_size != 4 && item_byte_size != 8)
+ item_byte_size = s->GetAddressByteSize();
+ }
+
+ offset_t offset = start_offset;
+
+ if (item_format == eFormatInstruction) {
+ TargetSP target_sp;
+ if (exe_scope)
+ target_sp = exe_scope->CalculateTarget();
+ if (target_sp) {
+ DisassemblerSP disassembler_sp(Disassembler::FindPlugin(
+ target_sp->GetArchitecture(),
+ target_sp->GetDisassemblyFlavor(), nullptr));
+ if (disassembler_sp) {
+ lldb::addr_t addr = base_addr + start_offset;
+ lldb_private::Address so_addr;
+ bool data_from_file = true;
+ if (target_sp->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) {
+ data_from_file = false;
+ } else {
+ if (target_sp->GetSectionLoadList().IsEmpty() ||
+ !target_sp->GetImages().ResolveFileAddress(addr, so_addr))
+ so_addr.SetRawAddress(addr);
+ }
+
+ size_t bytes_consumed = disassembler_sp->DecodeInstructions(
+ so_addr, DE, start_offset, item_count, false, data_from_file);
+
+ if (bytes_consumed) {
+ offset += bytes_consumed;
+ const bool show_address = base_addr != LLDB_INVALID_ADDRESS;
+ const bool show_bytes = true;
+ ExecutionContext exe_ctx;
+ exe_scope->CalculateExecutionContext(exe_ctx);
+ disassembler_sp->GetInstructionList().Dump(s, show_address,
+ show_bytes, &exe_ctx);
+ }
+ }
+ } else
+ s->Printf("invalid target");
+
+ return offset;
+ }
+
+ if ((item_format == eFormatOSType || item_format == eFormatAddressInfo) &&
+ item_byte_size > 8)
+ item_format = eFormatHex;
+
+ lldb::offset_t line_start_offset = start_offset;
+ for (uint32_t count = 0; DE.ValidOffset(offset) && count < item_count;
+ ++count) {
+ if ((count % num_per_line) == 0) {
+ if (count > 0) {
+ if (item_format == eFormatBytesWithASCII &&
+ offset > line_start_offset) {
+ s->Printf("%*s",
+ static_cast<int>(
+ (num_per_line - (offset - line_start_offset)) * 3 + 2),
+ "");
+ DumpDataExtractor(DE, s, line_start_offset, eFormatCharPrintable, 1,
+ offset - line_start_offset, SIZE_MAX,
+ LLDB_INVALID_ADDRESS, 0, 0);
+ }
+ s->EOL();
+ }
+ if (base_addr != LLDB_INVALID_ADDRESS)
+ s->Printf("0x%8.8" PRIx64 ": ",
+ (uint64_t)(base_addr +
+ (offset - start_offset) / DE.getTargetByteSize()));
+
+ line_start_offset = offset;
+ } else if (item_format != eFormatChar &&
+ item_format != eFormatCharPrintable &&
+ item_format != eFormatCharArray && count > 0) {
+ s->PutChar(' ');
+ }
+
+ switch (item_format) {
+ case eFormatBoolean:
+ if (item_byte_size <= 8)
+ s->Printf("%s", DE.GetMaxU64Bitfield(&offset, item_byte_size,
+ item_bit_size, item_bit_offset)
+ ? "true"
+ : "false");
+ else {
+ s->Printf("error: unsupported byte size (%" PRIu64
+ ") for boolean format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+ break;
+
+ case eFormatBinary:
+ if (item_byte_size <= 8) {
+ uint64_t uval64 = DE.GetMaxU64Bitfield(&offset, item_byte_size,
+ item_bit_size, item_bit_offset);
+ // Avoid std::bitset<64>::to_string() since it is missing in earlier
+ // C++ libraries
+ std::string binary_value(64, '0');
+ std::bitset<64> bits(uval64);
+ for (uint32_t i = 0; i < 64; ++i)
+ if (bits[i])
+ binary_value[64 - 1 - i] = '1';
+ if (item_bit_size > 0)
+ s->Printf("0b%s", binary_value.c_str() + 64 - item_bit_size);
+ else if (item_byte_size > 0 && item_byte_size <= 8)
+ s->Printf("0b%s", binary_value.c_str() + 64 - item_byte_size * 8);
+ } else {
+ const bool is_signed = false;
+ const unsigned radix = 2;
+ offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
+ }
+ break;
+
+ case eFormatBytes:
+ case eFormatBytesWithASCII:
+ for (uint32_t i = 0; i < item_byte_size; ++i) {
+ s->Printf("%2.2x", DE.GetU8(&offset));
+ }
+
+ // Put an extra space between the groups of bytes if more than one is
+ // being dumped in a group (item_byte_size is more than 1).
+ if (item_byte_size > 1)
+ s->PutChar(' ');
+ break;
+
+ case eFormatChar:
+ case eFormatCharPrintable:
+ case eFormatCharArray: {
+ // Reject invalid item_byte_size.
+ if (item_byte_size > 8) {
+ s->Printf("error: unsupported byte size (%" PRIu64 ") for char format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+
+ // If we are only printing one character surround it with single quotes
+ if (item_count == 1 && item_format == eFormatChar)
+ s->PutChar('\'');
+
+ const uint64_t ch = DE.GetMaxU64Bitfield(&offset, item_byte_size,
+ item_bit_size, item_bit_offset);
+ if (isprint(ch))
+ s->Printf("%c", (char)ch);
+ else if (item_format != eFormatCharPrintable) {
+ switch (ch) {
+ case '\033':
+ s->Printf("\\e");
+ break;
+ case '\a':
+ s->Printf("\\a");
+ break;
+ case '\b':
+ s->Printf("\\b");
+ break;
+ case '\f':
+ s->Printf("\\f");
+ break;
+ case '\n':
+ s->Printf("\\n");
+ break;
+ case '\r':
+ s->Printf("\\r");
+ break;
+ case '\t':
+ s->Printf("\\t");
+ break;
+ case '\v':
+ s->Printf("\\v");
+ break;
+ case '\0':
+ s->Printf("\\0");
+ break;
+ default:
+ if (item_byte_size == 1)
+ s->Printf("\\x%2.2x", (uint8_t)ch);
+ else
+ s->Printf("%" PRIu64, ch);
+ break;
+ }
+ } else {
+ s->PutChar(NON_PRINTABLE_CHAR);
+ }
+
+ // If we are only printing one character surround it with single quotes
+ if (item_count == 1 && item_format == eFormatChar)
+ s->PutChar('\'');
+ } break;
+
+ case eFormatEnum: // Print enum value as a signed integer when we don't get
+ // the enum type
+ case eFormatDecimal:
+ if (item_byte_size <= 8)
+ s->Printf("%" PRId64,
+ DE.GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset));
+ else {
+ const bool is_signed = true;
+ const unsigned radix = 10;
+ offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
+ }
+ break;
+
+ case eFormatUnsigned:
+ if (item_byte_size <= 8)
+ s->Printf("%" PRIu64,
+ DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset));
+ else {
+ const bool is_signed = false;
+ const unsigned radix = 10;
+ offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
+ }
+ break;
+
+ case eFormatOctal:
+ if (item_byte_size <= 8)
+ s->Printf("0%" PRIo64,
+ DE.GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset));
+ else {
+ const bool is_signed = false;
+ const unsigned radix = 8;
+ offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
+ }
+ break;
+
+ case eFormatOSType: {
+ uint64_t uval64 = DE.GetMaxU64Bitfield(&offset, item_byte_size,
+ item_bit_size, item_bit_offset);
+ s->PutChar('\'');
+ for (uint32_t i = 0; i < item_byte_size; ++i) {
+ uint8_t ch = (uint8_t)(uval64 >> ((item_byte_size - i - 1) * 8));
+ if (isprint(ch))
+ s->Printf("%c", ch);
+ else {
+ switch (ch) {
+ case '\033':
+ s->Printf("\\e");
+ break;
+ case '\a':
+ s->Printf("\\a");
+ break;
+ case '\b':
+ s->Printf("\\b");
+ break;
+ case '\f':
+ s->Printf("\\f");
+ break;
+ case '\n':
+ s->Printf("\\n");
+ break;
+ case '\r':
+ s->Printf("\\r");
+ break;
+ case '\t':
+ s->Printf("\\t");
+ break;
+ case '\v':
+ s->Printf("\\v");
+ break;
+ case '\0':
+ s->Printf("\\0");
+ break;
+ default:
+ s->Printf("\\x%2.2x", ch);
+ break;
+ }
+ }
+ }
+ s->PutChar('\'');
+ } break;
+
+ case eFormatCString: {
+ const char *cstr = DE.GetCStr(&offset);
+
+ if (!cstr) {
+ s->Printf("NULL");
+ offset = LLDB_INVALID_OFFSET;
+ } else {
+ s->PutChar('\"');
+
+ while (const char c = *cstr) {
+ if (isprint(c)) {
+ s->PutChar(c);
+ } else {
+ switch (c) {
+ case '\033':
+ s->Printf("\\e");
+ break;
+ case '\a':
+ s->Printf("\\a");
+ break;
+ case '\b':
+ s->Printf("\\b");
+ break;
+ case '\f':
+ s->Printf("\\f");
+ break;
+ case '\n':
+ s->Printf("\\n");
+ break;
+ case '\r':
+ s->Printf("\\r");
+ break;
+ case '\t':
+ s->Printf("\\t");
+ break;
+ case '\v':
+ s->Printf("\\v");
+ break;
+ default:
+ s->Printf("\\x%2.2x", c);
+ break;
+ }
+ }
+
+ ++cstr;
+ }
+
+ s->PutChar('\"');
+ }
+ } break;
+
+ case eFormatPointer:
+ s->Address(DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset),
+ sizeof(addr_t));
+ break;
+
+ case eFormatComplexInteger: {
+ size_t complex_int_byte_size = item_byte_size / 2;
+
+ if (complex_int_byte_size > 0 && complex_int_byte_size <= 8) {
+ s->Printf("%" PRIu64,
+ DE.GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0));
+ s->Printf(" + %" PRIu64 "i",
+ DE.GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0));
+ } else {
+ s->Printf("error: unsupported byte size (%" PRIu64
+ ") for complex integer format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+ } break;
+
+ case eFormatComplex:
+ if (sizeof(float) * 2 == item_byte_size) {
+ float f32_1 = DE.GetFloat(&offset);
+ float f32_2 = DE.GetFloat(&offset);
+
+ s->Printf("%g + %gi", f32_1, f32_2);
+ break;
+ } else if (sizeof(double) * 2 == item_byte_size) {
+ double d64_1 = DE.GetDouble(&offset);
+ double d64_2 = DE.GetDouble(&offset);
+
+ s->Printf("%lg + %lgi", d64_1, d64_2);
+ break;
+ } else if (sizeof(long double) * 2 == item_byte_size) {
+ long double ld64_1 = DE.GetLongDouble(&offset);
+ long double ld64_2 = DE.GetLongDouble(&offset);
+ s->Printf("%Lg + %Lgi", ld64_1, ld64_2);
+ break;
+ } else {
+ s->Printf("error: unsupported byte size (%" PRIu64
+ ") for complex float format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+ break;
+
+ default:
+ case eFormatDefault:
+ case eFormatHex:
+ case eFormatHexUppercase: {
+ bool wantsuppercase = (item_format == eFormatHexUppercase);
+ switch (item_byte_size) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ s->Printf(wantsuppercase ? "0x%*.*" PRIX64 : "0x%*.*" PRIx64,
+ (int)(2 * item_byte_size), (int)(2 * item_byte_size),
+ DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset));
+ break;
+ default: {
+ assert(item_bit_size == 0 && item_bit_offset == 0);
+ const uint8_t *bytes =
+ (const uint8_t *)DE.GetData(&offset, item_byte_size);
+ if (bytes) {
+ s->PutCString("0x");
+ uint32_t idx;
+ if (DE.GetByteOrder() == eByteOrderBig) {
+ for (idx = 0; idx < item_byte_size; ++idx)
+ s->Printf(wantsuppercase ? "%2.2X" : "%2.2x", bytes[idx]);
+ } else {
+ for (idx = 0; idx < item_byte_size; ++idx)
+ s->Printf(wantsuppercase ? "%2.2X" : "%2.2x",
+ bytes[item_byte_size - 1 - idx]);
+ }
+ }
+ } break;
+ }
+ } break;
+
+ case eFormatFloat: {
+ TargetSP target_sp;
+ bool used_upfloat = false;
+ if (exe_scope)
+ target_sp = exe_scope->CalculateTarget();
+ if (target_sp) {
+ ClangASTContext *clang_ast = target_sp->GetScratchClangASTContext();
+ if (clang_ast) {
+ clang::ASTContext *ast = clang_ast->getASTContext();
+ if (ast) {
+ llvm::SmallVector<char, 256> sv;
+ // Show full precision when printing float values
+ const unsigned format_precision = 0;
+ const unsigned format_max_padding = 100;
+ size_t item_bit_size = item_byte_size * 8;
+
+ if (item_bit_size == ast->getTypeSize(ast->FloatTy)) {
+ llvm::APInt apint(item_bit_size,
+ DE.GetMaxU64(&offset, item_byte_size));
+ llvm::APFloat apfloat(ast->getFloatTypeSemantics(ast->FloatTy),
+ apint);
+ apfloat.toString(sv, format_precision, format_max_padding);
+ } else if (item_bit_size == ast->getTypeSize(ast->DoubleTy)) {
+ llvm::APInt apint;
+ if (GetAPInt(DE, &offset, item_byte_size, apint)) {
+ llvm::APFloat apfloat(ast->getFloatTypeSemantics(ast->DoubleTy),
+ apint);
+ apfloat.toString(sv, format_precision, format_max_padding);
+ }
+ } else if (item_bit_size == ast->getTypeSize(ast->LongDoubleTy)) {
+ const auto &semantics =
+ ast->getFloatTypeSemantics(ast->LongDoubleTy);
+
+ offset_t byte_size = item_byte_size;
+ if (&semantics == &llvm::APFloatBase::x87DoubleExtended())
+ byte_size = (llvm::APFloat::getSizeInBits(semantics) + 7) / 8;
+
+ llvm::APInt apint;
+ if (GetAPInt(DE, &offset, byte_size, apint)) {
+ llvm::APFloat apfloat(semantics, apint);
+ apfloat.toString(sv, format_precision, format_max_padding);
+ }
+ } else if (item_bit_size == ast->getTypeSize(ast->HalfTy)) {
+ llvm::APInt apint(item_bit_size, DE.GetU16(&offset));
+ llvm::APFloat apfloat(ast->getFloatTypeSemantics(ast->HalfTy),
+ apint);
+ apfloat.toString(sv, format_precision, format_max_padding);
+ }
+
+ if (!sv.empty()) {
+ s->Printf("%*.*s", (int)sv.size(), (int)sv.size(), sv.data());
+ used_upfloat = true;
+ }
+ }
+ }
+ }
+
+ if (!used_upfloat) {
+ std::ostringstream ss;
+ if (item_byte_size == sizeof(float) || item_byte_size == 2) {
+ float f;
+ if (item_byte_size == 2) {
+ uint16_t half = DE.GetU16(&offset);
+ f = half2float(half);
+ } else {
+ f = DE.GetFloat(&offset);
+ }
+ ss.precision(std::numeric_limits<float>::digits10);
+ ss << f;
+ } else if (item_byte_size == sizeof(double)) {
+ ss.precision(std::numeric_limits<double>::digits10);
+ ss << DE.GetDouble(&offset);
+ } else if (item_byte_size == sizeof(long double) ||
+ item_byte_size == 10) {
+ ss.precision(std::numeric_limits<long double>::digits10);
+ ss << DE.GetLongDouble(&offset);
+ } else {
+ s->Printf("error: unsupported byte size (%" PRIu64
+ ") for float format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+ ss.flush();
+ s->Printf("%s", ss.str().c_str());
+ }
+ } break;
+
+ case eFormatUnicode16:
+ s->Printf("U+%4.4x", DE.GetU16(&offset));
+ break;
+
+ case eFormatUnicode32:
+ s->Printf("U+0x%8.8x", DE.GetU32(&offset));
+ break;
+
+ case eFormatAddressInfo: {
+ addr_t addr = DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset);
+ s->Printf("0x%*.*" PRIx64, (int)(2 * item_byte_size),
+ (int)(2 * item_byte_size), addr);
+ if (exe_scope) {
+ TargetSP target_sp(exe_scope->CalculateTarget());
+ lldb_private::Address so_addr;
+ if (target_sp) {
+ if (target_sp->GetSectionLoadList().ResolveLoadAddress(addr,
+ so_addr)) {
+ s->PutChar(' ');
+ so_addr.Dump(s, exe_scope, Address::DumpStyleResolvedDescription,
+ Address::DumpStyleModuleWithFileAddress);
+ } else {
+ so_addr.SetOffset(addr);
+ so_addr.Dump(s, exe_scope,
+ Address::DumpStyleResolvedPointerDescription);
+ }
+ }
+ }
+ } break;
+
+ case eFormatHexFloat:
+ if (sizeof(float) == item_byte_size) {
+ char float_cstr[256];
+ llvm::APFloat ap_float(DE.GetFloat(&offset));
+ ap_float.convertToHexString(float_cstr, 0, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ s->Printf("%s", float_cstr);
+ break;
+ } else if (sizeof(double) == item_byte_size) {
+ char float_cstr[256];
+ llvm::APFloat ap_float(DE.GetDouble(&offset));
+ ap_float.convertToHexString(float_cstr, 0, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ s->Printf("%s", float_cstr);
+ break;
+ } else {
+ s->Printf("error: unsupported byte size (%" PRIu64
+ ") for hex float format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+ break;
+
+ // please keep the single-item formats below in sync with
+ // FormatManager::GetSingleItemFormat if you fail to do so, users will
+ // start getting different outputs depending on internal implementation
+ // details they should not care about ||
+ case eFormatVectorOfChar: // ||
+ s->PutChar('{'); // \/
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatCharArray, 1, item_byte_size,
+ item_byte_size, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfSInt8:
+ s->PutChar('{');
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatDecimal, 1, item_byte_size,
+ item_byte_size, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfUInt8:
+ s->PutChar('{');
+ offset = DumpDataExtractor(DE, s, offset, eFormatHex, 1, item_byte_size,
+ item_byte_size, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfSInt16:
+ s->PutChar('{');
+ offset = DumpDataExtractor(
+ DE, s, offset, eFormatDecimal, sizeof(uint16_t),
+ item_byte_size / sizeof(uint16_t), item_byte_size / sizeof(uint16_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfUInt16:
+ s->PutChar('{');
+ offset = DumpDataExtractor(DE, s, offset, eFormatHex, sizeof(uint16_t),
+ item_byte_size / sizeof(uint16_t),
+ item_byte_size / sizeof(uint16_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfSInt32:
+ s->PutChar('{');
+ offset = DumpDataExtractor(
+ DE, s, offset, eFormatDecimal, sizeof(uint32_t),
+ item_byte_size / sizeof(uint32_t), item_byte_size / sizeof(uint32_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfUInt32:
+ s->PutChar('{');
+ offset = DumpDataExtractor(DE, s, offset, eFormatHex, sizeof(uint32_t),
+ item_byte_size / sizeof(uint32_t),
+ item_byte_size / sizeof(uint32_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfSInt64:
+ s->PutChar('{');
+ offset = DumpDataExtractor(
+ DE, s, offset, eFormatDecimal, sizeof(uint64_t),
+ item_byte_size / sizeof(uint64_t), item_byte_size / sizeof(uint64_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfUInt64:
+ s->PutChar('{');
+ offset = DumpDataExtractor(DE, s, offset, eFormatHex, sizeof(uint64_t),
+ item_byte_size / sizeof(uint64_t),
+ item_byte_size / sizeof(uint64_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfFloat16:
+ s->PutChar('{');
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatFloat, 2, item_byte_size / 2,
+ item_byte_size / 2, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfFloat32:
+ s->PutChar('{');
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatFloat, 4, item_byte_size / 4,
+ item_byte_size / 4, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfFloat64:
+ s->PutChar('{');
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatFloat, 8, item_byte_size / 8,
+ item_byte_size / 8, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfUInt128:
+ s->PutChar('{');
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatHex, 16, item_byte_size / 16,
+ item_byte_size / 16, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+ }
+ }
+
+ if (item_format == eFormatBytesWithASCII && offset > line_start_offset) {
+ s->Printf("%*s", static_cast<int>(
+ (num_per_line - (offset - line_start_offset)) * 3 + 2),
+ "");
+ DumpDataExtractor(DE, s, line_start_offset, eFormatCharPrintable, 1,
+ offset - line_start_offset, SIZE_MAX,
+ LLDB_INVALID_ADDRESS, 0, 0);
+ }
+ return offset; // Return the offset at which we ended up
+}
+
+void lldb_private::DumpHexBytes(Stream *s, const void *src, size_t src_len,
+ uint32_t bytes_per_line,
+ lldb::addr_t base_addr) {
+ DataExtractor data(src, src_len, lldb::eByteOrderLittle, 4);
+ DumpDataExtractor(data, s,
+ 0, // Offset into "src"
+ lldb::eFormatBytes, // Dump as hex bytes
+ 1, // Size of each item is 1 for single bytes
+ src_len, // Number of bytes
+ bytes_per_line, // Num bytes per line
+ base_addr, // Base address
+ 0, 0); // Bitfield info
+}
diff --git a/contrib/llvm-project/lldb/source/Core/DumpRegisterValue.cpp b/contrib/llvm-project/lldb/source/Core/DumpRegisterValue.cpp
new file mode 100644
index 000000000000..74b02413f101
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/DumpRegisterValue.cpp
@@ -0,0 +1,78 @@
+//===-- DumpRegisterValue.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-private-types.h"
+
+using namespace lldb;
+
+bool lldb_private::DumpRegisterValue(const RegisterValue &reg_val, Stream *s,
+ const RegisterInfo *reg_info,
+ bool prefix_with_name,
+ bool prefix_with_alt_name, Format format,
+ uint32_t reg_name_right_align_at) {
+ DataExtractor data;
+ if (reg_val.GetData(data)) {
+ bool name_printed = false;
+ // For simplicity, alignment of the register name printing applies only in
+ // the most common case where:
+ //
+ // prefix_with_name^prefix_with_alt_name is true
+ //
+ StreamString format_string;
+ if (reg_name_right_align_at && (prefix_with_name ^ prefix_with_alt_name))
+ format_string.Printf("%%%us", reg_name_right_align_at);
+ else
+ format_string.Printf("%%s");
+ std::string fmt = format_string.GetString();
+ if (prefix_with_name) {
+ if (reg_info->name) {
+ s->Printf(fmt.c_str(), reg_info->name);
+ name_printed = true;
+ } else if (reg_info->alt_name) {
+ s->Printf(fmt.c_str(), reg_info->alt_name);
+ prefix_with_alt_name = false;
+ name_printed = true;
+ }
+ }
+ if (prefix_with_alt_name) {
+ if (name_printed)
+ s->PutChar('/');
+ if (reg_info->alt_name) {
+ s->Printf(fmt.c_str(), reg_info->alt_name);
+ name_printed = true;
+ } else if (!name_printed) {
+ // No alternate name but we were asked to display a name, so show the
+ // main name
+ s->Printf(fmt.c_str(), reg_info->name);
+ name_printed = true;
+ }
+ }
+ if (name_printed)
+ s->PutCString(" = ");
+
+ if (format == eFormatDefault)
+ format = reg_info->format;
+
+ DumpDataExtractor(data, s,
+ 0, // Offset in "data"
+ format, // Format to use when dumping
+ reg_info->byte_size, // item_byte_size
+ 1, // item_count
+ UINT32_MAX, // num_per_line
+ LLDB_INVALID_ADDRESS, // base_addr
+ 0, // item_bit_size
+ 0); // item_bit_offset
+ return true;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/DynamicLoader.cpp b/contrib/llvm-project/lldb/source/Core/DynamicLoader.cpp
new file mode 100644
index 000000000000..57130d6fa57a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/DynamicLoader.cpp
@@ -0,0 +1,245 @@
+//===-- DynamicLoader.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/DynamicLoader.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-private-interfaces.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include <memory>
+
+#include <assert.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+DynamicLoader *DynamicLoader::FindPlugin(Process *process,
+ const char *plugin_name) {
+ DynamicLoaderCreateInstance create_callback = nullptr;
+ if (plugin_name) {
+ ConstString const_plugin_name(plugin_name);
+ create_callback =
+ PluginManager::GetDynamicLoaderCreateCallbackForPluginName(
+ const_plugin_name);
+ if (create_callback) {
+ std::unique_ptr<DynamicLoader> instance_up(
+ create_callback(process, true));
+ if (instance_up)
+ return instance_up.release();
+ }
+ } else {
+ for (uint32_t idx = 0;
+ (create_callback =
+ PluginManager::GetDynamicLoaderCreateCallbackAtIndex(idx)) !=
+ nullptr;
+ ++idx) {
+ std::unique_ptr<DynamicLoader> instance_up(
+ create_callback(process, false));
+ if (instance_up)
+ return instance_up.release();
+ }
+ }
+ return nullptr;
+}
+
+DynamicLoader::DynamicLoader(Process *process) : m_process(process) {}
+
+DynamicLoader::~DynamicLoader() = default;
+
+// Accessosors to the global setting as to whether to stop at image (shared
+// library) loading/unloading.
+
+bool DynamicLoader::GetStopWhenImagesChange() const {
+ return m_process->GetStopOnSharedLibraryEvents();
+}
+
+void DynamicLoader::SetStopWhenImagesChange(bool stop) {
+ m_process->SetStopOnSharedLibraryEvents(stop);
+}
+
+ModuleSP DynamicLoader::GetTargetExecutable() {
+ Target &target = m_process->GetTarget();
+ ModuleSP executable = target.GetExecutableModule();
+
+ if (executable) {
+ if (FileSystem::Instance().Exists(executable->GetFileSpec())) {
+ ModuleSpec module_spec(executable->GetFileSpec(),
+ executable->GetArchitecture());
+ auto module_sp = std::make_shared<Module>(module_spec);
+
+ // Check if the executable has changed and set it to the target
+ // executable if they differ.
+ if (module_sp && module_sp->GetUUID().IsValid() &&
+ executable->GetUUID().IsValid()) {
+ if (module_sp->GetUUID() != executable->GetUUID())
+ executable.reset();
+ } else if (executable->FileHasChanged()) {
+ executable.reset();
+ }
+
+ if (!executable) {
+ executable = target.GetOrCreateModule(module_spec, true /* notify */);
+ if (executable.get() != target.GetExecutableModulePointer()) {
+ // Don't load dependent images since we are in dyld where we will
+ // know and find out about all images that are loaded
+ target.SetExecutableModule(executable, eLoadDependentsNo);
+ }
+ }
+ }
+ }
+ return executable;
+}
+
+void DynamicLoader::UpdateLoadedSections(ModuleSP module, addr_t link_map_addr,
+ addr_t base_addr,
+ bool base_addr_is_offset) {
+ UpdateLoadedSectionsCommon(module, base_addr, base_addr_is_offset);
+}
+
+void DynamicLoader::UpdateLoadedSectionsCommon(ModuleSP module,
+ addr_t base_addr,
+ bool base_addr_is_offset) {
+ bool changed;
+ module->SetLoadAddress(m_process->GetTarget(), base_addr, base_addr_is_offset,
+ changed);
+}
+
+void DynamicLoader::UnloadSections(const ModuleSP module) {
+ UnloadSectionsCommon(module);
+}
+
+void DynamicLoader::UnloadSectionsCommon(const ModuleSP module) {
+ Target &target = m_process->GetTarget();
+ const SectionList *sections = GetSectionListFromModule(module);
+
+ assert(sections && "SectionList missing from unloaded module.");
+
+ const size_t num_sections = sections->GetSize();
+ for (size_t i = 0; i < num_sections; ++i) {
+ SectionSP section_sp(sections->GetSectionAtIndex(i));
+ target.SetSectionUnloaded(section_sp);
+ }
+}
+
+const SectionList *
+DynamicLoader::GetSectionListFromModule(const ModuleSP module) const {
+ SectionList *sections = nullptr;
+ if (module) {
+ ObjectFile *obj_file = module->GetObjectFile();
+ if (obj_file != nullptr) {
+ sections = obj_file->GetSectionList();
+ }
+ }
+ return sections;
+}
+
+ModuleSP DynamicLoader::LoadModuleAtAddress(const FileSpec &file,
+ addr_t link_map_addr,
+ addr_t base_addr,
+ bool base_addr_is_offset) {
+ Target &target = m_process->GetTarget();
+ ModuleList &modules = target.GetImages();
+ ModuleSpec module_spec(file, target.GetArchitecture());
+ ModuleSP module_sp;
+
+ if ((module_sp = modules.FindFirstModule(module_spec))) {
+ UpdateLoadedSections(module_sp, link_map_addr, base_addr,
+ base_addr_is_offset);
+ return module_sp;
+ }
+
+ if ((module_sp = target.GetOrCreateModule(module_spec,
+ true /* notify */))) {
+ UpdateLoadedSections(module_sp, link_map_addr, base_addr,
+ base_addr_is_offset);
+ return module_sp;
+ }
+
+ bool check_alternative_file_name = true;
+ if (base_addr_is_offset) {
+ // Try to fetch the load address of the file from the process as we need
+ // absolute load address to read the file out of the memory instead of a
+ // load bias.
+ bool is_loaded = false;
+ lldb::addr_t load_addr;
+ Status error = m_process->GetFileLoadAddress(file, is_loaded, load_addr);
+ if (error.Success() && is_loaded) {
+ check_alternative_file_name = false;
+ base_addr = load_addr;
+ }
+ }
+
+ // We failed to find the module based on its name. Lets try to check if we
+ // can find a different name based on the memory region info.
+ if (check_alternative_file_name) {
+ MemoryRegionInfo memory_info;
+ Status error = m_process->GetMemoryRegionInfo(base_addr, memory_info);
+ if (error.Success() && memory_info.GetMapped() &&
+ memory_info.GetRange().GetRangeBase() == base_addr &&
+ !(memory_info.GetName().IsEmpty())) {
+ ModuleSpec new_module_spec(FileSpec(memory_info.GetName().AsCString()),
+ target.GetArchitecture());
+
+ if ((module_sp = modules.FindFirstModule(new_module_spec))) {
+ UpdateLoadedSections(module_sp, link_map_addr, base_addr, false);
+ return module_sp;
+ }
+
+ if ((module_sp = target.GetOrCreateModule(new_module_spec,
+ true /* notify */))) {
+ UpdateLoadedSections(module_sp, link_map_addr, base_addr, false);
+ return module_sp;
+ }
+ }
+ }
+
+ if ((module_sp = m_process->ReadModuleFromMemory(file, base_addr))) {
+ UpdateLoadedSections(module_sp, link_map_addr, base_addr, false);
+ target.GetImages().AppendIfNeeded(module_sp);
+ }
+
+ return module_sp;
+}
+
+int64_t DynamicLoader::ReadUnsignedIntWithSizeInBytes(addr_t addr,
+ int size_in_bytes) {
+ Status error;
+ uint64_t value =
+ m_process->ReadUnsignedIntegerFromMemory(addr, size_in_bytes, 0, error);
+ if (error.Fail())
+ return -1;
+ else
+ return (int64_t)value;
+}
+
+addr_t DynamicLoader::ReadPointer(addr_t addr) {
+ Status error;
+ addr_t value = m_process->ReadPointerFromMemory(addr, error);
+ if (error.Fail())
+ return LLDB_INVALID_ADDRESS;
+ else
+ return value;
+}
+
+void DynamicLoader::LoadOperatingSystemPlugin(bool flush)
+{
+ if (m_process)
+ m_process->LoadOperatingSystemPlugin(flush);
+}
+
diff --git a/contrib/llvm-project/lldb/source/Core/EmulateInstruction.cpp b/contrib/llvm-project/lldb/source/Core/EmulateInstruction.cpp
new file mode 100644
index 000000000000..62942fb715b3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/EmulateInstruction.cpp
@@ -0,0 +1,585 @@
+//===-- EmulateInstruction.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/EmulateInstruction.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private-interfaces.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include <cstring>
+#include <memory>
+
+#include <inttypes.h>
+#include <stdio.h>
+
+namespace lldb_private {
+class Target;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+EmulateInstruction *
+EmulateInstruction::FindPlugin(const ArchSpec &arch,
+ InstructionType supported_inst_type,
+ const char *plugin_name) {
+ EmulateInstructionCreateInstance create_callback = nullptr;
+ if (plugin_name) {
+ ConstString const_plugin_name(plugin_name);
+ create_callback =
+ PluginManager::GetEmulateInstructionCreateCallbackForPluginName(
+ const_plugin_name);
+ if (create_callback) {
+ EmulateInstruction *emulate_insn_ptr =
+ create_callback(arch, supported_inst_type);
+ if (emulate_insn_ptr)
+ return emulate_insn_ptr;
+ }
+ } else {
+ for (uint32_t idx = 0;
+ (create_callback =
+ PluginManager::GetEmulateInstructionCreateCallbackAtIndex(idx)) !=
+ nullptr;
+ ++idx) {
+ EmulateInstruction *emulate_insn_ptr =
+ create_callback(arch, supported_inst_type);
+ if (emulate_insn_ptr)
+ return emulate_insn_ptr;
+ }
+ }
+ return nullptr;
+}
+
+EmulateInstruction::EmulateInstruction(const ArchSpec &arch) : m_arch(arch) {}
+
+bool EmulateInstruction::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ if (m_read_reg_callback != nullptr)
+ return m_read_reg_callback(this, m_baton, reg_info, reg_value);
+ return false;
+}
+
+bool EmulateInstruction::ReadRegister(lldb::RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterValue &reg_value) {
+ RegisterInfo reg_info;
+ if (GetRegisterInfo(reg_kind, reg_num, reg_info))
+ return ReadRegister(&reg_info, reg_value);
+ return false;
+}
+
+uint64_t EmulateInstruction::ReadRegisterUnsigned(lldb::RegisterKind reg_kind,
+ uint32_t reg_num,
+ uint64_t fail_value,
+ bool *success_ptr) {
+ RegisterValue reg_value;
+ if (ReadRegister(reg_kind, reg_num, reg_value))
+ return reg_value.GetAsUInt64(fail_value, success_ptr);
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+uint64_t EmulateInstruction::ReadRegisterUnsigned(const RegisterInfo *reg_info,
+ uint64_t fail_value,
+ bool *success_ptr) {
+ RegisterValue reg_value;
+ if (ReadRegister(reg_info, reg_value))
+ return reg_value.GetAsUInt64(fail_value, success_ptr);
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+bool EmulateInstruction::WriteRegister(const Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ if (m_write_reg_callback != nullptr)
+ return m_write_reg_callback(this, m_baton, context, reg_info, reg_value);
+ return false;
+}
+
+bool EmulateInstruction::WriteRegister(const Context &context,
+ lldb::RegisterKind reg_kind,
+ uint32_t reg_num,
+ const RegisterValue &reg_value) {
+ RegisterInfo reg_info;
+ if (GetRegisterInfo(reg_kind, reg_num, reg_info))
+ return WriteRegister(context, &reg_info, reg_value);
+ return false;
+}
+
+bool EmulateInstruction::WriteRegisterUnsigned(const Context &context,
+ lldb::RegisterKind reg_kind,
+ uint32_t reg_num,
+ uint64_t uint_value) {
+ RegisterInfo reg_info;
+ if (GetRegisterInfo(reg_kind, reg_num, reg_info)) {
+ RegisterValue reg_value;
+ if (reg_value.SetUInt(uint_value, reg_info.byte_size))
+ return WriteRegister(context, &reg_info, reg_value);
+ }
+ return false;
+}
+
+bool EmulateInstruction::WriteRegisterUnsigned(const Context &context,
+ const RegisterInfo *reg_info,
+ uint64_t uint_value) {
+ if (reg_info != nullptr) {
+ RegisterValue reg_value;
+ if (reg_value.SetUInt(uint_value, reg_info->byte_size))
+ return WriteRegister(context, reg_info, reg_value);
+ }
+ return false;
+}
+
+size_t EmulateInstruction::ReadMemory(const Context &context, lldb::addr_t addr,
+ void *dst, size_t dst_len) {
+ if (m_read_mem_callback != nullptr)
+ return m_read_mem_callback(this, m_baton, context, addr, dst, dst_len) ==
+ dst_len;
+ return false;
+}
+
+uint64_t EmulateInstruction::ReadMemoryUnsigned(const Context &context,
+ lldb::addr_t addr,
+ size_t byte_size,
+ uint64_t fail_value,
+ bool *success_ptr) {
+ uint64_t uval64 = 0;
+ bool success = false;
+ if (byte_size <= 8) {
+ uint8_t buf[sizeof(uint64_t)];
+ size_t bytes_read =
+ m_read_mem_callback(this, m_baton, context, addr, buf, byte_size);
+ if (bytes_read == byte_size) {
+ lldb::offset_t offset = 0;
+ DataExtractor data(buf, byte_size, GetByteOrder(), GetAddressByteSize());
+ uval64 = data.GetMaxU64(&offset, byte_size);
+ success = true;
+ }
+ }
+
+ if (success_ptr)
+ *success_ptr = success;
+
+ if (!success)
+ uval64 = fail_value;
+ return uval64;
+}
+
+bool EmulateInstruction::WriteMemoryUnsigned(const Context &context,
+ lldb::addr_t addr, uint64_t uval,
+ size_t uval_byte_size) {
+ StreamString strm(Stream::eBinary, GetAddressByteSize(), GetByteOrder());
+ strm.PutMaxHex64(uval, uval_byte_size);
+
+ size_t bytes_written = m_write_mem_callback(
+ this, m_baton, context, addr, strm.GetString().data(), uval_byte_size);
+ return (bytes_written == uval_byte_size);
+}
+
+bool EmulateInstruction::WriteMemory(const Context &context, lldb::addr_t addr,
+ const void *src, size_t src_len) {
+ if (m_write_mem_callback != nullptr)
+ return m_write_mem_callback(this, m_baton, context, addr, src, src_len) ==
+ src_len;
+ return false;
+}
+
+void EmulateInstruction::SetBaton(void *baton) { m_baton = baton; }
+
+void EmulateInstruction::SetCallbacks(
+ ReadMemoryCallback read_mem_callback,
+ WriteMemoryCallback write_mem_callback,
+ ReadRegisterCallback read_reg_callback,
+ WriteRegisterCallback write_reg_callback) {
+ m_read_mem_callback = read_mem_callback;
+ m_write_mem_callback = write_mem_callback;
+ m_read_reg_callback = read_reg_callback;
+ m_write_reg_callback = write_reg_callback;
+}
+
+void EmulateInstruction::SetReadMemCallback(
+ ReadMemoryCallback read_mem_callback) {
+ m_read_mem_callback = read_mem_callback;
+}
+
+void EmulateInstruction::SetWriteMemCallback(
+ WriteMemoryCallback write_mem_callback) {
+ m_write_mem_callback = write_mem_callback;
+}
+
+void EmulateInstruction::SetReadRegCallback(
+ ReadRegisterCallback read_reg_callback) {
+ m_read_reg_callback = read_reg_callback;
+}
+
+void EmulateInstruction::SetWriteRegCallback(
+ WriteRegisterCallback write_reg_callback) {
+ m_write_reg_callback = write_reg_callback;
+}
+
+//
+// Read & Write Memory and Registers callback functions.
+//
+
+size_t EmulateInstruction::ReadMemoryFrame(EmulateInstruction *instruction,
+ void *baton, const Context &context,
+ lldb::addr_t addr, void *dst,
+ size_t dst_len) {
+ if (baton == nullptr || dst == nullptr || dst_len == 0)
+ return 0;
+
+ StackFrame *frame = (StackFrame *)baton;
+
+ ProcessSP process_sp(frame->CalculateProcess());
+ if (process_sp) {
+ Status error;
+ return process_sp->ReadMemory(addr, dst, dst_len, error);
+ }
+ return 0;
+}
+
+size_t EmulateInstruction::WriteMemoryFrame(EmulateInstruction *instruction,
+ void *baton, const Context &context,
+ lldb::addr_t addr, const void *src,
+ size_t src_len) {
+ if (baton == nullptr || src == nullptr || src_len == 0)
+ return 0;
+
+ StackFrame *frame = (StackFrame *)baton;
+
+ ProcessSP process_sp(frame->CalculateProcess());
+ if (process_sp) {
+ Status error;
+ return process_sp->WriteMemory(addr, src, src_len, error);
+ }
+
+ return 0;
+}
+
+bool EmulateInstruction::ReadRegisterFrame(EmulateInstruction *instruction,
+ void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ if (baton == nullptr)
+ return false;
+
+ StackFrame *frame = (StackFrame *)baton;
+ return frame->GetRegisterContext()->ReadRegister(reg_info, reg_value);
+}
+
+bool EmulateInstruction::WriteRegisterFrame(EmulateInstruction *instruction,
+ void *baton, const Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ if (baton == nullptr)
+ return false;
+
+ StackFrame *frame = (StackFrame *)baton;
+ return frame->GetRegisterContext()->WriteRegister(reg_info, reg_value);
+}
+
+size_t EmulateInstruction::ReadMemoryDefault(EmulateInstruction *instruction,
+ void *baton,
+ const Context &context,
+ lldb::addr_t addr, void *dst,
+ size_t length) {
+ StreamFile strm(stdout, false);
+ strm.Printf(" Read from Memory (address = 0x%" PRIx64 ", length = %" PRIu64
+ ", context = ",
+ addr, (uint64_t)length);
+ context.Dump(strm, instruction);
+ strm.EOL();
+ *((uint64_t *)dst) = 0xdeadbeef;
+ return length;
+}
+
+size_t EmulateInstruction::WriteMemoryDefault(EmulateInstruction *instruction,
+ void *baton,
+ const Context &context,
+ lldb::addr_t addr,
+ const void *dst, size_t length) {
+ StreamFile strm(stdout, false);
+ strm.Printf(" Write to Memory (address = 0x%" PRIx64 ", length = %" PRIu64
+ ", context = ",
+ addr, (uint64_t)length);
+ context.Dump(strm, instruction);
+ strm.EOL();
+ return length;
+}
+
+bool EmulateInstruction::ReadRegisterDefault(EmulateInstruction *instruction,
+ void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ StreamFile strm(stdout, false);
+ strm.Printf(" Read Register (%s)\n", reg_info->name);
+ lldb::RegisterKind reg_kind;
+ uint32_t reg_num;
+ if (GetBestRegisterKindAndNumber(reg_info, reg_kind, reg_num))
+ reg_value.SetUInt64((uint64_t)reg_kind << 24 | reg_num);
+ else
+ reg_value.SetUInt64(0);
+
+ return true;
+}
+
+bool EmulateInstruction::WriteRegisterDefault(EmulateInstruction *instruction,
+ void *baton,
+ const Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ StreamFile strm(stdout, false);
+ strm.Printf(" Write to Register (name = %s, value = ", reg_info->name);
+ DumpRegisterValue(reg_value, &strm, reg_info, false, false, eFormatDefault);
+ strm.PutCString(", context = ");
+ context.Dump(strm, instruction);
+ strm.EOL();
+ return true;
+}
+
+void EmulateInstruction::Context::Dump(Stream &strm,
+ EmulateInstruction *instruction) const {
+ switch (type) {
+ case eContextReadOpcode:
+ strm.PutCString("reading opcode");
+ break;
+
+ case eContextImmediate:
+ strm.PutCString("immediate");
+ break;
+
+ case eContextPushRegisterOnStack:
+ strm.PutCString("push register");
+ break;
+
+ case eContextPopRegisterOffStack:
+ strm.PutCString("pop register");
+ break;
+
+ case eContextAdjustStackPointer:
+ strm.PutCString("adjust sp");
+ break;
+
+ case eContextSetFramePointer:
+ strm.PutCString("set frame pointer");
+ break;
+
+ case eContextAdjustBaseRegister:
+ strm.PutCString("adjusting (writing value back to) a base register");
+ break;
+
+ case eContextRegisterPlusOffset:
+ strm.PutCString("register + offset");
+ break;
+
+ case eContextRegisterStore:
+ strm.PutCString("store register");
+ break;
+
+ case eContextRegisterLoad:
+ strm.PutCString("load register");
+ break;
+
+ case eContextRelativeBranchImmediate:
+ strm.PutCString("relative branch immediate");
+ break;
+
+ case eContextAbsoluteBranchRegister:
+ strm.PutCString("absolute branch register");
+ break;
+
+ case eContextSupervisorCall:
+ strm.PutCString("supervisor call");
+ break;
+
+ case eContextTableBranchReadMemory:
+ strm.PutCString("table branch read memory");
+ break;
+
+ case eContextWriteRegisterRandomBits:
+ strm.PutCString("write random bits to a register");
+ break;
+
+ case eContextWriteMemoryRandomBits:
+ strm.PutCString("write random bits to a memory address");
+ break;
+
+ case eContextArithmetic:
+ strm.PutCString("arithmetic");
+ break;
+
+ case eContextReturnFromException:
+ strm.PutCString("return from exception");
+ break;
+
+ default:
+ strm.PutCString("unrecognized context.");
+ break;
+ }
+
+ switch (info_type) {
+ case eInfoTypeRegisterPlusOffset:
+ strm.Printf(" (reg_plus_offset = %s%+" PRId64 ")",
+ info.RegisterPlusOffset.reg.name,
+ info.RegisterPlusOffset.signed_offset);
+ break;
+
+ case eInfoTypeRegisterPlusIndirectOffset:
+ strm.Printf(" (reg_plus_reg = %s + %s)",
+ info.RegisterPlusIndirectOffset.base_reg.name,
+ info.RegisterPlusIndirectOffset.offset_reg.name);
+ break;
+
+ case eInfoTypeRegisterToRegisterPlusOffset:
+ strm.Printf(" (base_and_imm_offset = %s%+" PRId64 ", data_reg = %s)",
+ info.RegisterToRegisterPlusOffset.base_reg.name,
+ info.RegisterToRegisterPlusOffset.offset,
+ info.RegisterToRegisterPlusOffset.data_reg.name);
+ break;
+
+ case eInfoTypeRegisterToRegisterPlusIndirectOffset:
+ strm.Printf(" (base_and_reg_offset = %s + %s, data_reg = %s)",
+ info.RegisterToRegisterPlusIndirectOffset.base_reg.name,
+ info.RegisterToRegisterPlusIndirectOffset.offset_reg.name,
+ info.RegisterToRegisterPlusIndirectOffset.data_reg.name);
+ break;
+
+ case eInfoTypeRegisterRegisterOperands:
+ strm.Printf(" (register to register binary op: %s and %s)",
+ info.RegisterRegisterOperands.operand1.name,
+ info.RegisterRegisterOperands.operand2.name);
+ break;
+
+ case eInfoTypeOffset:
+ strm.Printf(" (signed_offset = %+" PRId64 ")", info.signed_offset);
+ break;
+
+ case eInfoTypeRegister:
+ strm.Printf(" (reg = %s)", info.reg.name);
+ break;
+
+ case eInfoTypeImmediate:
+ strm.Printf(" (unsigned_immediate = %" PRIu64 " (0x%16.16" PRIx64 "))",
+ info.unsigned_immediate, info.unsigned_immediate);
+ break;
+
+ case eInfoTypeImmediateSigned:
+ strm.Printf(" (signed_immediate = %+" PRId64 " (0x%16.16" PRIx64 "))",
+ info.signed_immediate, info.signed_immediate);
+ break;
+
+ case eInfoTypeAddress:
+ strm.Printf(" (address = 0x%" PRIx64 ")", info.address);
+ break;
+
+ case eInfoTypeISAAndImmediate:
+ strm.Printf(" (isa = %u, unsigned_immediate = %u (0x%8.8x))",
+ info.ISAAndImmediate.isa, info.ISAAndImmediate.unsigned_data32,
+ info.ISAAndImmediate.unsigned_data32);
+ break;
+
+ case eInfoTypeISAAndImmediateSigned:
+ strm.Printf(" (isa = %u, signed_immediate = %i (0x%8.8x))",
+ info.ISAAndImmediateSigned.isa,
+ info.ISAAndImmediateSigned.signed_data32,
+ info.ISAAndImmediateSigned.signed_data32);
+ break;
+
+ case eInfoTypeISA:
+ strm.Printf(" (isa = %u)", info.isa);
+ break;
+
+ case eInfoTypeNoArgs:
+ break;
+ }
+}
+
+bool EmulateInstruction::SetInstruction(const Opcode &opcode,
+ const Address &inst_addr,
+ Target *target) {
+ m_opcode = opcode;
+ m_addr = LLDB_INVALID_ADDRESS;
+ if (inst_addr.IsValid()) {
+ if (target != nullptr)
+ m_addr = inst_addr.GetLoadAddress(target);
+ if (m_addr == LLDB_INVALID_ADDRESS)
+ m_addr = inst_addr.GetFileAddress();
+ }
+ return true;
+}
+
+bool EmulateInstruction::GetBestRegisterKindAndNumber(
+ const RegisterInfo *reg_info, lldb::RegisterKind &reg_kind,
+ uint32_t &reg_num) {
+ // Generic and DWARF should be the two most popular register kinds when
+ // emulating instructions since they are the most platform agnostic...
+ reg_num = reg_info->kinds[eRegisterKindGeneric];
+ if (reg_num != LLDB_INVALID_REGNUM) {
+ reg_kind = eRegisterKindGeneric;
+ return true;
+ }
+
+ reg_num = reg_info->kinds[eRegisterKindDWARF];
+ if (reg_num != LLDB_INVALID_REGNUM) {
+ reg_kind = eRegisterKindDWARF;
+ return true;
+ }
+
+ reg_num = reg_info->kinds[eRegisterKindLLDB];
+ if (reg_num != LLDB_INVALID_REGNUM) {
+ reg_kind = eRegisterKindLLDB;
+ return true;
+ }
+
+ reg_num = reg_info->kinds[eRegisterKindEHFrame];
+ if (reg_num != LLDB_INVALID_REGNUM) {
+ reg_kind = eRegisterKindEHFrame;
+ return true;
+ }
+
+ reg_num = reg_info->kinds[eRegisterKindProcessPlugin];
+ if (reg_num != LLDB_INVALID_REGNUM) {
+ reg_kind = eRegisterKindProcessPlugin;
+ return true;
+ }
+ return false;
+}
+
+uint32_t
+EmulateInstruction::GetInternalRegisterNumber(RegisterContext *reg_ctx,
+ const RegisterInfo &reg_info) {
+ lldb::RegisterKind reg_kind;
+ uint32_t reg_num;
+ if (reg_ctx && GetBestRegisterKindAndNumber(&reg_info, reg_kind, reg_num))
+ return reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
+ return LLDB_INVALID_REGNUM;
+}
+
+bool EmulateInstruction::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/FileLineResolver.cpp b/contrib/llvm-project/lldb/source/Core/FileLineResolver.cpp
new file mode 100644
index 000000000000..3cba1c7e8143
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/FileLineResolver.cpp
@@ -0,0 +1,89 @@
+//===-- FileLineResolver.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/FileLineResolver.h"
+
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+#include <string>
+
+namespace lldb_private {
+class Address;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+// FileLineResolver:
+FileLineResolver::FileLineResolver(const FileSpec &file_spec, uint32_t line_no,
+ bool check_inlines)
+ : Searcher(), m_file_spec(file_spec), m_line_number(line_no),
+ m_inlines(check_inlines) {}
+
+FileLineResolver::~FileLineResolver() {}
+
+Searcher::CallbackReturn
+FileLineResolver::SearchCallback(SearchFilter &filter, SymbolContext &context,
+ Address *addr, bool containing) {
+ CompileUnit *cu = context.comp_unit;
+
+ if (m_inlines ||
+ m_file_spec.Compare(*cu, m_file_spec, (bool)m_file_spec.GetDirectory())) {
+ uint32_t start_file_idx = 0;
+ uint32_t file_idx =
+ cu->GetSupportFiles().FindFileIndex(start_file_idx, m_file_spec, false);
+ if (file_idx != UINT32_MAX) {
+ LineTable *line_table = cu->GetLineTable();
+ if (line_table) {
+ if (m_line_number == 0) {
+ // Match all lines in a file...
+ const bool append = true;
+ while (file_idx != UINT32_MAX) {
+ line_table->FineLineEntriesForFileIndex(file_idx, append,
+ m_sc_list);
+ // Get the next file index in case we have multiple file entries
+ // for the same file
+ file_idx = cu->GetSupportFiles().FindFileIndex(file_idx + 1,
+ m_file_spec, false);
+ }
+ } else {
+ // Match a specific line in a file...
+ }
+ }
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth FileLineResolver::GetDepth() {
+ return lldb::eSearchDepthCompUnit;
+}
+
+void FileLineResolver::GetDescription(Stream *s) {
+ s->Printf("File and line resolver for file: \"%s\" line: %u",
+ m_file_spec.GetPath().c_str(), m_line_number);
+}
+
+void FileLineResolver::Clear() {
+ m_file_spec.Clear();
+ m_line_number = UINT32_MAX;
+ m_sc_list.Clear();
+ m_inlines = true;
+}
+
+void FileLineResolver::Reset(const FileSpec &file_spec, uint32_t line,
+ bool check_inlines) {
+ m_file_spec = file_spec;
+ m_line_number = line;
+ m_sc_list.Clear();
+ m_inlines = check_inlines;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/FileSpecList.cpp b/contrib/llvm-project/lldb/source/Core/FileSpecList.cpp
new file mode 100644
index 000000000000..95133faf7502
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/FileSpecList.cpp
@@ -0,0 +1,121 @@
+//===-- FileSpecList.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/FileSpecList.h"
+
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+#include <utility>
+
+#include <stdint.h>
+
+using namespace lldb_private;
+using namespace std;
+
+FileSpecList::FileSpecList() : m_files() {}
+
+FileSpecList::~FileSpecList() = default;
+
+// Append the "file_spec" to the end of the file spec list.
+void FileSpecList::Append(const FileSpec &file_spec) {
+ m_files.push_back(file_spec);
+}
+
+// Only append the "file_spec" if this list doesn't already contain it.
+//
+// Returns true if "file_spec" was added, false if this list already contained
+// a copy of "file_spec".
+bool FileSpecList::AppendIfUnique(const FileSpec &file_spec) {
+ collection::iterator end = m_files.end();
+ if (find(m_files.begin(), end, file_spec) == end) {
+ m_files.push_back(file_spec);
+ return true;
+ }
+ return false;
+}
+
+// Clears the file list.
+void FileSpecList::Clear() { m_files.clear(); }
+
+// Dumps the file list to the supplied stream pointer "s".
+void FileSpecList::Dump(Stream *s, const char *separator_cstr) const {
+ collection::const_iterator pos, end = m_files.end();
+ for (pos = m_files.begin(); pos != end; ++pos) {
+ pos->Dump(s);
+ if (separator_cstr && ((pos + 1) != end))
+ s->PutCString(separator_cstr);
+ }
+}
+
+// Find the index of the file in the file spec list that matches "file_spec"
+// starting "start_idx" entries into the file spec list.
+//
+// Returns the valid index of the file that matches "file_spec" if it is found,
+// else std::numeric_limits<uint32_t>::max() is returned.
+size_t FileSpecList::FindFileIndex(size_t start_idx, const FileSpec &file_spec,
+ bool full) const {
+ const size_t num_files = m_files.size();
+
+ // When looking for files, we will compare only the filename if the FILE_SPEC
+ // argument is empty
+ bool compare_filename_only = file_spec.GetDirectory().IsEmpty();
+
+ for (size_t idx = start_idx; idx < num_files; ++idx) {
+ if (compare_filename_only) {
+ if (ConstString::Equals(
+ m_files[idx].GetFilename(), file_spec.GetFilename(),
+ file_spec.IsCaseSensitive() || m_files[idx].IsCaseSensitive()))
+ return idx;
+ } else {
+ if (FileSpec::Equal(m_files[idx], file_spec, full))
+ return idx;
+ }
+ }
+
+ // We didn't find the file, return an invalid index
+ return UINT32_MAX;
+}
+
+// Returns the FileSpec object at index "idx". If "idx" is out of range, then
+// an empty FileSpec object will be returned.
+const FileSpec &FileSpecList::GetFileSpecAtIndex(size_t idx) const {
+ if (idx < m_files.size())
+ return m_files[idx];
+ static FileSpec g_empty_file_spec;
+ return g_empty_file_spec;
+}
+
+const FileSpec *FileSpecList::GetFileSpecPointerAtIndex(size_t idx) const {
+ if (idx < m_files.size())
+ return &m_files[idx];
+ return nullptr;
+}
+
+// Return the size in bytes that this object takes in memory. This returns the
+// size in bytes of this object's member variables and any FileSpec objects its
+// member variables contain, the result doesn't not include the string values
+// for the directories any filenames as those are in shared string pools.
+size_t FileSpecList::MemorySize() const {
+ size_t mem_size = sizeof(FileSpecList);
+ collection::const_iterator pos, end = m_files.end();
+ for (pos = m_files.begin(); pos != end; ++pos) {
+ mem_size += pos->MemorySize();
+ }
+
+ return mem_size;
+}
+
+// Return the number of files in the file spec list.
+size_t FileSpecList::GetSize() const { return m_files.size(); }
+
+size_t FileSpecList::GetFilesMatchingPartialPath(const char *path,
+ bool dir_okay,
+ FileSpecList &matches) {
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/FormatEntity.cpp b/contrib/llvm-project/lldb/source/Core/FormatEntity.cpp
new file mode 100644
index 000000000000..1ffbed2cd64e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/FormatEntity.cpp
@@ -0,0 +1,2427 @@
+//===-- FormatEntity.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/FormatEntity.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/DataFormatters/FormatClasses.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/AnsiTerminal.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/SharingPtr.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringList.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Compiler.h"
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <memory>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <type_traits>
+#include <utility>
+
+namespace lldb_private {
+class ScriptInterpreter;
+}
+namespace lldb_private {
+struct RegisterInfo;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum FileKind { FileError = 0, Basename, Dirname, Fullpath };
+
+#define ENTRY(n, t) \
+ { n, nullptr, FormatEntity::Entry::Type::t, 0, 0, nullptr, false }
+#define ENTRY_VALUE(n, t, v) \
+ { n, nullptr, FormatEntity::Entry::Type::t, v, 0, nullptr, false }
+#define ENTRY_CHILDREN(n, t, c) \
+ { \
+ n, nullptr, FormatEntity::Entry::Type::t, 0, \
+ static_cast<uint32_t>(llvm::array_lengthof(c)), c, false \
+ }
+#define ENTRY_CHILDREN_KEEP_SEP(n, t, c) \
+ { \
+ n, nullptr, FormatEntity::Entry::Type::t, 0, \
+ static_cast<uint32_t>(llvm::array_lengthof(c)), c, true \
+ }
+#define ENTRY_STRING(n, s) \
+ { n, s, FormatEntity::Entry::Type::EscapeCode, 0, 0, nullptr, false }
+static FormatEntity::Entry::Definition g_string_entry[] = {
+ ENTRY("*", ParentString)};
+
+static FormatEntity::Entry::Definition g_addr_entries[] = {
+ ENTRY("load", AddressLoad),
+ ENTRY("file", AddressFile),
+ ENTRY("load", AddressLoadOrFile),
+};
+
+static FormatEntity::Entry::Definition g_file_child_entries[] = {
+ ENTRY_VALUE("basename", ParentNumber, FileKind::Basename),
+ ENTRY_VALUE("dirname", ParentNumber, FileKind::Dirname),
+ ENTRY_VALUE("fullpath", ParentNumber, FileKind::Fullpath)};
+
+static FormatEntity::Entry::Definition g_frame_child_entries[] = {
+ ENTRY("index", FrameIndex),
+ ENTRY("pc", FrameRegisterPC),
+ ENTRY("fp", FrameRegisterFP),
+ ENTRY("sp", FrameRegisterSP),
+ ENTRY("flags", FrameRegisterFlags),
+ ENTRY("no-debug", FrameNoDebug),
+ ENTRY_CHILDREN("reg", FrameRegisterByName, g_string_entry),
+ ENTRY("is-artificial", FrameIsArtificial),
+};
+
+static FormatEntity::Entry::Definition g_function_child_entries[] = {
+ ENTRY("id", FunctionID),
+ ENTRY("name", FunctionName),
+ ENTRY("name-without-args", FunctionNameNoArgs),
+ ENTRY("name-with-args", FunctionNameWithArgs),
+ ENTRY("addr-offset", FunctionAddrOffset),
+ ENTRY("concrete-only-addr-offset-no-padding", FunctionAddrOffsetConcrete),
+ ENTRY("line-offset", FunctionLineOffset),
+ ENTRY("pc-offset", FunctionPCOffset),
+ ENTRY("initial-function", FunctionInitial),
+ ENTRY("changed", FunctionChanged),
+ ENTRY("is-optimized", FunctionIsOptimized)};
+
+static FormatEntity::Entry::Definition g_line_child_entries[] = {
+ ENTRY_CHILDREN("file", LineEntryFile, g_file_child_entries),
+ ENTRY("number", LineEntryLineNumber),
+ ENTRY("column", LineEntryColumn),
+ ENTRY("start-addr", LineEntryStartAddress),
+ ENTRY("end-addr", LineEntryEndAddress),
+};
+
+static FormatEntity::Entry::Definition g_module_child_entries[] = {
+ ENTRY_CHILDREN("file", ModuleFile, g_file_child_entries),
+};
+
+static FormatEntity::Entry::Definition g_process_child_entries[] = {
+ ENTRY("id", ProcessID),
+ ENTRY_VALUE("name", ProcessFile, FileKind::Basename),
+ ENTRY_CHILDREN("file", ProcessFile, g_file_child_entries),
+};
+
+static FormatEntity::Entry::Definition g_svar_child_entries[] = {
+ ENTRY("*", ParentString)};
+
+static FormatEntity::Entry::Definition g_var_child_entries[] = {
+ ENTRY("*", ParentString)};
+
+static FormatEntity::Entry::Definition g_thread_child_entries[] = {
+ ENTRY("id", ThreadID),
+ ENTRY("protocol_id", ThreadProtocolID),
+ ENTRY("index", ThreadIndexID),
+ ENTRY_CHILDREN("info", ThreadInfo, g_string_entry),
+ ENTRY("queue", ThreadQueue),
+ ENTRY("name", ThreadName),
+ ENTRY("stop-reason", ThreadStopReason),
+ ENTRY("return-value", ThreadReturnValue),
+ ENTRY("completed-expression", ThreadCompletedExpression),
+};
+
+static FormatEntity::Entry::Definition g_target_child_entries[] = {
+ ENTRY("arch", TargetArch),
+};
+
+#define _TO_STR2(_val) #_val
+#define _TO_STR(_val) _TO_STR2(_val)
+
+static FormatEntity::Entry::Definition g_ansi_fg_entries[] = {
+ ENTRY_STRING("black",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END),
+ ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END),
+ ENTRY_STRING("green",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END),
+ ENTRY_STRING("yellow",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END),
+ ENTRY_STRING("blue",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END),
+ ENTRY_STRING("purple",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END),
+ ENTRY_STRING("cyan",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END),
+ ENTRY_STRING("white",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END),
+};
+
+static FormatEntity::Entry::Definition g_ansi_bg_entries[] = {
+ ENTRY_STRING("black",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END),
+ ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END),
+ ENTRY_STRING("green",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END),
+ ENTRY_STRING("yellow",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END),
+ ENTRY_STRING("blue",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END),
+ ENTRY_STRING("purple",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END),
+ ENTRY_STRING("cyan",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END),
+ ENTRY_STRING("white",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END),
+};
+
+static FormatEntity::Entry::Definition g_ansi_entries[] = {
+ ENTRY_CHILDREN("fg", Invalid, g_ansi_fg_entries),
+ ENTRY_CHILDREN("bg", Invalid, g_ansi_bg_entries),
+ ENTRY_STRING("normal",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END),
+ ENTRY_STRING("bold", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END),
+ ENTRY_STRING("faint", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END),
+ ENTRY_STRING("italic",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END),
+ ENTRY_STRING("underline",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END),
+ ENTRY_STRING("slow-blink",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END),
+ ENTRY_STRING("fast-blink",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END),
+ ENTRY_STRING("negative",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END),
+ ENTRY_STRING("conceal",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END),
+ ENTRY_STRING("crossed-out",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END),
+};
+
+static FormatEntity::Entry::Definition g_script_child_entries[] = {
+ ENTRY("frame", ScriptFrame), ENTRY("process", ScriptProcess),
+ ENTRY("target", ScriptTarget), ENTRY("thread", ScriptThread),
+ ENTRY("var", ScriptVariable), ENTRY("svar", ScriptVariableSynthetic),
+ ENTRY("thread", ScriptThread),
+};
+
+static FormatEntity::Entry::Definition g_top_level_entries[] = {
+ ENTRY_CHILDREN("addr", AddressLoadOrFile, g_addr_entries),
+ ENTRY("addr-file-or-load", AddressLoadOrFile),
+ ENTRY_CHILDREN("ansi", Invalid, g_ansi_entries),
+ ENTRY("current-pc-arrow", CurrentPCArrow),
+ ENTRY_CHILDREN("file", File, g_file_child_entries),
+ ENTRY("language", Lang),
+ ENTRY_CHILDREN("frame", Invalid, g_frame_child_entries),
+ ENTRY_CHILDREN("function", Invalid, g_function_child_entries),
+ ENTRY_CHILDREN("line", Invalid, g_line_child_entries),
+ ENTRY_CHILDREN("module", Invalid, g_module_child_entries),
+ ENTRY_CHILDREN("process", Invalid, g_process_child_entries),
+ ENTRY_CHILDREN("script", Invalid, g_script_child_entries),
+ ENTRY_CHILDREN_KEEP_SEP("svar", VariableSynthetic, g_svar_child_entries),
+ ENTRY_CHILDREN("thread", Invalid, g_thread_child_entries),
+ ENTRY_CHILDREN("target", Invalid, g_target_child_entries),
+ ENTRY_CHILDREN_KEEP_SEP("var", Variable, g_var_child_entries),
+};
+
+static FormatEntity::Entry::Definition g_root =
+ ENTRY_CHILDREN("<root>", Root, g_top_level_entries);
+
+FormatEntity::Entry::Entry(llvm::StringRef s)
+ : string(s.data(), s.size()), printf_format(), children(),
+ definition(nullptr), type(Type::String), fmt(lldb::eFormatDefault),
+ number(0), deref(false) {}
+
+FormatEntity::Entry::Entry(char ch)
+ : string(1, ch), printf_format(), children(), definition(nullptr),
+ type(Type::String), fmt(lldb::eFormatDefault), number(0), deref(false) {}
+
+void FormatEntity::Entry::AppendChar(char ch) {
+ if (children.empty() || children.back().type != Entry::Type::String)
+ children.push_back(Entry(ch));
+ else
+ children.back().string.append(1, ch);
+}
+
+void FormatEntity::Entry::AppendText(const llvm::StringRef &s) {
+ if (children.empty() || children.back().type != Entry::Type::String)
+ children.push_back(Entry(s));
+ else
+ children.back().string.append(s.data(), s.size());
+}
+
+void FormatEntity::Entry::AppendText(const char *cstr) {
+ return AppendText(llvm::StringRef(cstr));
+}
+
+Status FormatEntity::Parse(const llvm::StringRef &format_str, Entry &entry) {
+ entry.Clear();
+ entry.type = Entry::Type::Root;
+ llvm::StringRef modifiable_format(format_str);
+ return ParseInternal(modifiable_format, entry, 0);
+}
+
+#define ENUM_TO_CSTR(eee) \
+ case FormatEntity::Entry::Type::eee: \
+ return #eee
+
+const char *FormatEntity::Entry::TypeToCString(Type t) {
+ switch (t) {
+ ENUM_TO_CSTR(Invalid);
+ ENUM_TO_CSTR(ParentNumber);
+ ENUM_TO_CSTR(ParentString);
+ ENUM_TO_CSTR(EscapeCode);
+ ENUM_TO_CSTR(Root);
+ ENUM_TO_CSTR(String);
+ ENUM_TO_CSTR(Scope);
+ ENUM_TO_CSTR(Variable);
+ ENUM_TO_CSTR(VariableSynthetic);
+ ENUM_TO_CSTR(ScriptVariable);
+ ENUM_TO_CSTR(ScriptVariableSynthetic);
+ ENUM_TO_CSTR(AddressLoad);
+ ENUM_TO_CSTR(AddressFile);
+ ENUM_TO_CSTR(AddressLoadOrFile);
+ ENUM_TO_CSTR(ProcessID);
+ ENUM_TO_CSTR(ProcessFile);
+ ENUM_TO_CSTR(ScriptProcess);
+ ENUM_TO_CSTR(ThreadID);
+ ENUM_TO_CSTR(ThreadProtocolID);
+ ENUM_TO_CSTR(ThreadIndexID);
+ ENUM_TO_CSTR(ThreadName);
+ ENUM_TO_CSTR(ThreadQueue);
+ ENUM_TO_CSTR(ThreadStopReason);
+ ENUM_TO_CSTR(ThreadReturnValue);
+ ENUM_TO_CSTR(ThreadCompletedExpression);
+ ENUM_TO_CSTR(ScriptThread);
+ ENUM_TO_CSTR(ThreadInfo);
+ ENUM_TO_CSTR(TargetArch);
+ ENUM_TO_CSTR(ScriptTarget);
+ ENUM_TO_CSTR(ModuleFile);
+ ENUM_TO_CSTR(File);
+ ENUM_TO_CSTR(Lang);
+ ENUM_TO_CSTR(FrameIndex);
+ ENUM_TO_CSTR(FrameNoDebug);
+ ENUM_TO_CSTR(FrameRegisterPC);
+ ENUM_TO_CSTR(FrameRegisterSP);
+ ENUM_TO_CSTR(FrameRegisterFP);
+ ENUM_TO_CSTR(FrameRegisterFlags);
+ ENUM_TO_CSTR(FrameRegisterByName);
+ ENUM_TO_CSTR(FrameIsArtificial);
+ ENUM_TO_CSTR(ScriptFrame);
+ ENUM_TO_CSTR(FunctionID);
+ ENUM_TO_CSTR(FunctionDidChange);
+ ENUM_TO_CSTR(FunctionInitialFunction);
+ ENUM_TO_CSTR(FunctionName);
+ ENUM_TO_CSTR(FunctionNameWithArgs);
+ ENUM_TO_CSTR(FunctionNameNoArgs);
+ ENUM_TO_CSTR(FunctionAddrOffset);
+ ENUM_TO_CSTR(FunctionAddrOffsetConcrete);
+ ENUM_TO_CSTR(FunctionLineOffset);
+ ENUM_TO_CSTR(FunctionPCOffset);
+ ENUM_TO_CSTR(FunctionInitial);
+ ENUM_TO_CSTR(FunctionChanged);
+ ENUM_TO_CSTR(FunctionIsOptimized);
+ ENUM_TO_CSTR(LineEntryFile);
+ ENUM_TO_CSTR(LineEntryLineNumber);
+ ENUM_TO_CSTR(LineEntryColumn);
+ ENUM_TO_CSTR(LineEntryStartAddress);
+ ENUM_TO_CSTR(LineEntryEndAddress);
+ ENUM_TO_CSTR(CurrentPCArrow);
+ }
+ return "???";
+}
+
+#undef ENUM_TO_CSTR
+
+void FormatEntity::Entry::Dump(Stream &s, int depth) const {
+ s.Printf("%*.*s%-20s: ", depth * 2, depth * 2, "", TypeToCString(type));
+ if (fmt != eFormatDefault)
+ s.Printf("lldb-format = %s, ", FormatManager::GetFormatAsCString(fmt));
+ if (!string.empty())
+ s.Printf("string = \"%s\"", string.c_str());
+ if (!printf_format.empty())
+ s.Printf("printf_format = \"%s\"", printf_format.c_str());
+ if (number != 0)
+ s.Printf("number = %" PRIu64 " (0x%" PRIx64 "), ", number, number);
+ if (deref)
+ s.Printf("deref = true, ");
+ s.EOL();
+ for (const auto &child : children) {
+ child.Dump(s, depth + 1);
+ }
+}
+
+template <typename T>
+static bool RunScriptFormatKeyword(Stream &s, const SymbolContext *sc,
+ const ExecutionContext *exe_ctx, T t,
+ const char *script_function_name) {
+ Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
+
+ if (target) {
+ ScriptInterpreter *script_interpreter =
+ target->GetDebugger().GetScriptInterpreter();
+ if (script_interpreter) {
+ Status error;
+ std::string script_output;
+
+ if (script_interpreter->RunScriptFormatKeyword(script_function_name, t,
+ script_output, error) &&
+ error.Success()) {
+ s.Printf("%s", script_output.c_str());
+ return true;
+ } else {
+ s.Printf("<error: %s>", error.AsCString());
+ }
+ }
+ }
+ return false;
+}
+
+static bool DumpAddress(Stream &s, const SymbolContext *sc,
+ const ExecutionContext *exe_ctx, const Address &addr,
+ bool print_file_addr_or_load_addr) {
+ Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
+ addr_t vaddr = LLDB_INVALID_ADDRESS;
+ if (exe_ctx && !target->GetSectionLoadList().IsEmpty())
+ vaddr = addr.GetLoadAddress(target);
+ if (vaddr == LLDB_INVALID_ADDRESS)
+ vaddr = addr.GetFileAddress();
+
+ if (vaddr != LLDB_INVALID_ADDRESS) {
+ int addr_width = 0;
+ if (exe_ctx && target) {
+ addr_width = target->GetArchitecture().GetAddressByteSize() * 2;
+ }
+ if (addr_width == 0)
+ addr_width = 16;
+ if (print_file_addr_or_load_addr) {
+ ExecutionContextScope *exe_scope = nullptr;
+ if (exe_ctx)
+ exe_scope = exe_ctx->GetBestExecutionContextScope();
+ addr.Dump(&s, exe_scope, Address::DumpStyleLoadAddress,
+ Address::DumpStyleModuleWithFileAddress, 0);
+ } else {
+ s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr);
+ }
+ return true;
+ }
+ return false;
+}
+
+static bool DumpAddressOffsetFromFunction(Stream &s, const SymbolContext *sc,
+ const ExecutionContext *exe_ctx,
+ const Address &format_addr,
+ bool concrete_only, bool no_padding,
+ bool print_zero_offsets) {
+ if (format_addr.IsValid()) {
+ Address func_addr;
+
+ if (sc) {
+ if (sc->function) {
+ func_addr = sc->function->GetAddressRange().GetBaseAddress();
+ if (sc->block && !concrete_only) {
+ // Check to make sure we aren't in an inline function. If we are, use
+ // the inline block range that contains "format_addr" since blocks
+ // can be discontiguous.
+ Block *inline_block = sc->block->GetContainingInlinedBlock();
+ AddressRange inline_range;
+ if (inline_block && inline_block->GetRangeContainingAddress(
+ format_addr, inline_range))
+ func_addr = inline_range.GetBaseAddress();
+ }
+ } else if (sc->symbol && sc->symbol->ValueIsAddress())
+ func_addr = sc->symbol->GetAddressRef();
+ }
+
+ if (func_addr.IsValid()) {
+ const char *addr_offset_padding = no_padding ? "" : " ";
+
+ if (func_addr.GetSection() == format_addr.GetSection()) {
+ addr_t func_file_addr = func_addr.GetFileAddress();
+ addr_t addr_file_addr = format_addr.GetFileAddress();
+ if (addr_file_addr > func_file_addr ||
+ (addr_file_addr == func_file_addr && print_zero_offsets)) {
+ s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding,
+ addr_file_addr - func_file_addr);
+ } else if (addr_file_addr < func_file_addr) {
+ s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding,
+ func_file_addr - addr_file_addr);
+ }
+ return true;
+ } else {
+ Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
+ if (target) {
+ addr_t func_load_addr = func_addr.GetLoadAddress(target);
+ addr_t addr_load_addr = format_addr.GetLoadAddress(target);
+ if (addr_load_addr > func_load_addr ||
+ (addr_load_addr == func_load_addr && print_zero_offsets)) {
+ s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding,
+ addr_load_addr - func_load_addr);
+ } else if (addr_load_addr < func_load_addr) {
+ s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding,
+ func_load_addr - addr_load_addr);
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static bool ScanBracketedRange(llvm::StringRef subpath,
+ size_t &close_bracket_index,
+ const char *&var_name_final_if_array_range,
+ int64_t &index_lower, int64_t &index_higher) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ close_bracket_index = llvm::StringRef::npos;
+ const size_t open_bracket_index = subpath.find('[');
+ if (open_bracket_index == llvm::StringRef::npos) {
+ if (log)
+ log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely");
+ return false;
+ }
+
+ close_bracket_index = subpath.find(']', open_bracket_index + 1);
+
+ if (close_bracket_index == llvm::StringRef::npos) {
+ if (log)
+ log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely");
+ return false;
+ } else {
+ var_name_final_if_array_range = subpath.data() + open_bracket_index;
+
+ if (close_bracket_index - open_bracket_index == 1) {
+ if (log)
+ log->Printf(
+ "[ScanBracketedRange] '[]' detected.. going from 0 to end of data");
+ index_lower = 0;
+ } else {
+ const size_t separator_index = subpath.find('-', open_bracket_index + 1);
+
+ if (separator_index == llvm::StringRef::npos) {
+ const char *index_lower_cstr = subpath.data() + open_bracket_index + 1;
+ index_lower = ::strtoul(index_lower_cstr, nullptr, 0);
+ index_higher = index_lower;
+ if (log)
+ log->Printf("[ScanBracketedRange] [%" PRId64
+ "] detected, high index is same",
+ index_lower);
+ } else {
+ const char *index_lower_cstr = subpath.data() + open_bracket_index + 1;
+ const char *index_higher_cstr = subpath.data() + separator_index + 1;
+ index_lower = ::strtoul(index_lower_cstr, nullptr, 0);
+ index_higher = ::strtoul(index_higher_cstr, nullptr, 0);
+ if (log)
+ log->Printf("[ScanBracketedRange] [%" PRId64 "-%" PRId64 "] detected",
+ index_lower, index_higher);
+ }
+ if (index_lower > index_higher && index_higher > 0) {
+ if (log)
+ log->Printf("[ScanBracketedRange] swapping indices");
+ const int64_t temp = index_lower;
+ index_lower = index_higher;
+ index_higher = temp;
+ }
+ }
+ }
+ return true;
+}
+
+static bool DumpFile(Stream &s, const FileSpec &file, FileKind file_kind) {
+ switch (file_kind) {
+ case FileKind::FileError:
+ break;
+
+ case FileKind::Basename:
+ if (file.GetFilename()) {
+ s << file.GetFilename();
+ return true;
+ }
+ break;
+
+ case FileKind::Dirname:
+ if (file.GetDirectory()) {
+ s << file.GetDirectory();
+ return true;
+ }
+ break;
+
+ case FileKind::Fullpath:
+ if (file) {
+ s << file;
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+static bool DumpRegister(Stream &s, StackFrame *frame, RegisterKind reg_kind,
+ uint32_t reg_num, Format format)
+
+{
+ if (frame) {
+ RegisterContext *reg_ctx = frame->GetRegisterContext().get();
+
+ if (reg_ctx) {
+ const uint32_t lldb_reg_num =
+ reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
+ if (lldb_reg_num != LLDB_INVALID_REGNUM) {
+ const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoAtIndex(lldb_reg_num);
+ if (reg_info) {
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ DumpRegisterValue(reg_value, &s, reg_info, false, false, format);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static ValueObjectSP ExpandIndexedExpression(ValueObject *valobj, size_t index,
+ StackFrame *frame,
+ bool deref_pointer) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ const char *ptr_deref_format = "[%d]";
+ std::string ptr_deref_buffer(10, 0);
+ ::sprintf(&ptr_deref_buffer[0], ptr_deref_format, index);
+ if (log)
+ log->Printf("[ExpandIndexedExpression] name to deref: %s",
+ ptr_deref_buffer.c_str());
+ ValueObject::GetValueForExpressionPathOptions options;
+ ValueObject::ExpressionPathEndResultType final_value_type;
+ ValueObject::ExpressionPathScanEndReason reason_to_stop;
+ ValueObject::ExpressionPathAftermath what_next =
+ (deref_pointer ? ValueObject::eExpressionPathAftermathDereference
+ : ValueObject::eExpressionPathAftermathNothing);
+ ValueObjectSP item = valobj->GetValueForExpressionPath(
+ ptr_deref_buffer.c_str(), &reason_to_stop, &final_value_type, options,
+ &what_next);
+ if (!item) {
+ if (log)
+ log->Printf("[ExpandIndexedExpression] ERROR: why stopping = %d,"
+ " final_value_type %d",
+ reason_to_stop, final_value_type);
+ } else {
+ if (log)
+ log->Printf("[ExpandIndexedExpression] ALL RIGHT: why stopping = %d,"
+ " final_value_type %d",
+ reason_to_stop, final_value_type);
+ }
+ return item;
+}
+
+static char ConvertValueObjectStyleToChar(
+ ValueObject::ValueObjectRepresentationStyle style) {
+ switch (style) {
+ case ValueObject::eValueObjectRepresentationStyleLanguageSpecific:
+ return '@';
+ case ValueObject::eValueObjectRepresentationStyleValue:
+ return 'V';
+ case ValueObject::eValueObjectRepresentationStyleLocation:
+ return 'L';
+ case ValueObject::eValueObjectRepresentationStyleSummary:
+ return 'S';
+ case ValueObject::eValueObjectRepresentationStyleChildrenCount:
+ return '#';
+ case ValueObject::eValueObjectRepresentationStyleType:
+ return 'T';
+ case ValueObject::eValueObjectRepresentationStyleName:
+ return 'N';
+ case ValueObject::eValueObjectRepresentationStyleExpressionPath:
+ return '>';
+ }
+ return '\0';
+}
+
+static bool DumpValue(Stream &s, const SymbolContext *sc,
+ const ExecutionContext *exe_ctx,
+ const FormatEntity::Entry &entry, ValueObject *valobj) {
+ if (valobj == nullptr)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ Format custom_format = eFormatInvalid;
+ ValueObject::ValueObjectRepresentationStyle val_obj_display =
+ entry.string.empty()
+ ? ValueObject::eValueObjectRepresentationStyleValue
+ : ValueObject::eValueObjectRepresentationStyleSummary;
+
+ bool do_deref_pointer = entry.deref;
+ bool is_script = false;
+ switch (entry.type) {
+ case FormatEntity::Entry::Type::ScriptVariable:
+ is_script = true;
+ break;
+
+ case FormatEntity::Entry::Type::Variable:
+ custom_format = entry.fmt;
+ val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number;
+ break;
+
+ case FormatEntity::Entry::Type::ScriptVariableSynthetic:
+ is_script = true;
+ LLVM_FALLTHROUGH;
+ case FormatEntity::Entry::Type::VariableSynthetic:
+ custom_format = entry.fmt;
+ val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number;
+ if (!valobj->IsSynthetic()) {
+ valobj = valobj->GetSyntheticValue().get();
+ if (valobj == nullptr)
+ return false;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ if (valobj == nullptr)
+ return false;
+
+ ValueObject::ExpressionPathAftermath what_next =
+ (do_deref_pointer ? ValueObject::eExpressionPathAftermathDereference
+ : ValueObject::eExpressionPathAftermathNothing);
+ ValueObject::GetValueForExpressionPathOptions options;
+ options.DontCheckDotVsArrowSyntax()
+ .DoAllowBitfieldSyntax()
+ .DoAllowFragileIVar()
+ .SetSyntheticChildrenTraversal(
+ ValueObject::GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::Both);
+ ValueObject *target = nullptr;
+ const char *var_name_final_if_array_range = nullptr;
+ size_t close_bracket_index = llvm::StringRef::npos;
+ int64_t index_lower = -1;
+ int64_t index_higher = -1;
+ bool is_array_range = false;
+ bool was_plain_var = false;
+ bool was_var_format = false;
+ bool was_var_indexed = false;
+ ValueObject::ExpressionPathScanEndReason reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonEndOfString;
+ ValueObject::ExpressionPathEndResultType final_value_type =
+ ValueObject::eExpressionPathEndResultTypePlain;
+
+ if (is_script) {
+ return RunScriptFormatKeyword(s, sc, exe_ctx, valobj, entry.string.c_str());
+ }
+
+ llvm::StringRef subpath(entry.string);
+ // simplest case ${var}, just print valobj's value
+ if (entry.string.empty()) {
+ if (entry.printf_format.empty() && entry.fmt == eFormatDefault &&
+ entry.number == ValueObject::eValueObjectRepresentationStyleValue)
+ was_plain_var = true;
+ else
+ was_var_format = true;
+ target = valobj;
+ } else // this is ${var.something} or multiple .something nested
+ {
+ if (entry.string[0] == '[')
+ was_var_indexed = true;
+ ScanBracketedRange(subpath, close_bracket_index,
+ var_name_final_if_array_range, index_lower,
+ index_higher);
+
+ Status error;
+
+ const std::string &expr_path = entry.string;
+
+ if (log)
+ log->Printf("[Debugger::FormatPrompt] symbol to expand: %s",
+ expr_path.c_str());
+
+ target =
+ valobj
+ ->GetValueForExpressionPath(expr_path.c_str(), &reason_to_stop,
+ &final_value_type, options, &what_next)
+ .get();
+
+ if (!target) {
+ if (log)
+ log->Printf("[Debugger::FormatPrompt] ERROR: why stopping = %d,"
+ " final_value_type %d",
+ reason_to_stop, final_value_type);
+ return false;
+ } else {
+ if (log)
+ log->Printf("[Debugger::FormatPrompt] ALL RIGHT: why stopping = %d,"
+ " final_value_type %d",
+ reason_to_stop, final_value_type);
+ target = target
+ ->GetQualifiedRepresentationIfAvailable(
+ target->GetDynamicValueType(), true)
+ .get();
+ }
+ }
+
+ is_array_range =
+ (final_value_type ==
+ ValueObject::eExpressionPathEndResultTypeBoundedRange ||
+ final_value_type ==
+ ValueObject::eExpressionPathEndResultTypeUnboundedRange);
+
+ do_deref_pointer =
+ (what_next == ValueObject::eExpressionPathAftermathDereference);
+
+ if (do_deref_pointer && !is_array_range) {
+ // I have not deref-ed yet, let's do it
+ // this happens when we are not going through
+ // GetValueForVariableExpressionPath to get to the target ValueObject
+ Status error;
+ target = target->Dereference(error).get();
+ if (error.Fail()) {
+ if (log)
+ log->Printf("[Debugger::FormatPrompt] ERROR: %s\n",
+ error.AsCString("unknown"));
+ return false;
+ }
+ do_deref_pointer = false;
+ }
+
+ if (!target) {
+ if (log)
+ log->Printf("[Debugger::FormatPrompt] could not calculate target for "
+ "prompt expression");
+ return false;
+ }
+
+ // we do not want to use the summary for a bitfield of type T:n if we were
+ // originally dealing with just a T - that would get us into an endless
+ // recursion
+ if (target->IsBitfield() && was_var_indexed) {
+ // TODO: check for a (T:n)-specific summary - we should still obey that
+ StreamString bitfield_name;
+ bitfield_name.Printf("%s:%d", target->GetTypeName().AsCString(),
+ target->GetBitfieldBitSize());
+ auto type_sp = std::make_shared<TypeNameSpecifierImpl>(
+ bitfield_name.GetString(), false);
+ if (val_obj_display ==
+ ValueObject::eValueObjectRepresentationStyleSummary &&
+ !DataVisualization::GetSummaryForType(type_sp))
+ val_obj_display = ValueObject::eValueObjectRepresentationStyleValue;
+ }
+
+ // TODO use flags for these
+ const uint32_t type_info_flags =
+ target->GetCompilerType().GetTypeInfo(nullptr);
+ bool is_array = (type_info_flags & eTypeIsArray) != 0;
+ bool is_pointer = (type_info_flags & eTypeIsPointer) != 0;
+ bool is_aggregate = target->GetCompilerType().IsAggregateType();
+
+ if ((is_array || is_pointer) && (!is_array_range) &&
+ val_obj_display ==
+ ValueObject::eValueObjectRepresentationStyleValue) // this should be
+ // wrong, but there
+ // are some
+ // exceptions
+ {
+ StreamString str_temp;
+ if (log)
+ log->Printf(
+ "[Debugger::FormatPrompt] I am into array || pointer && !range");
+
+ if (target->HasSpecialPrintableRepresentation(val_obj_display,
+ custom_format)) {
+ // try to use the special cases
+ bool success = target->DumpPrintableRepresentation(
+ str_temp, val_obj_display, custom_format);
+ if (log)
+ log->Printf("[Debugger::FormatPrompt] special cases did%s match",
+ success ? "" : "n't");
+
+ // should not happen
+ if (success)
+ s << str_temp.GetString();
+ return true;
+ } else {
+ if (was_plain_var) // if ${var}
+ {
+ s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
+ } else if (is_pointer) // if pointer, value is the address stored
+ {
+ target->DumpPrintableRepresentation(
+ s, val_obj_display, custom_format,
+ ValueObject::PrintableRepresentationSpecialCases::eDisable);
+ }
+ return true;
+ }
+ }
+
+ // if directly trying to print ${var}, and this is an aggregate, display a
+ // nice type @ location message
+ if (is_aggregate && was_plain_var) {
+ s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
+ return true;
+ }
+
+ // if directly trying to print ${var%V}, and this is an aggregate, do not let
+ // the user do it
+ if (is_aggregate &&
+ ((was_var_format &&
+ val_obj_display ==
+ ValueObject::eValueObjectRepresentationStyleValue))) {
+ s << "<invalid use of aggregate type>";
+ return true;
+ }
+
+ if (!is_array_range) {
+ if (log)
+ log->Printf("[Debugger::FormatPrompt] dumping ordinary printable output");
+ return target->DumpPrintableRepresentation(s, val_obj_display,
+ custom_format);
+ } else {
+ if (log)
+ log->Printf("[Debugger::FormatPrompt] checking if I can handle as array");
+ if (!is_array && !is_pointer)
+ return false;
+ if (log)
+ log->Printf("[Debugger::FormatPrompt] handle as array");
+ StreamString special_directions_stream;
+ llvm::StringRef special_directions;
+ if (close_bracket_index != llvm::StringRef::npos &&
+ subpath.size() > close_bracket_index) {
+ ConstString additional_data(subpath.drop_front(close_bracket_index + 1));
+ special_directions_stream.Printf("${%svar%s", do_deref_pointer ? "*" : "",
+ additional_data.GetCString());
+
+ if (entry.fmt != eFormatDefault) {
+ const char format_char =
+ FormatManager::GetFormatAsFormatChar(entry.fmt);
+ if (format_char != '\0')
+ special_directions_stream.Printf("%%%c", format_char);
+ else {
+ const char *format_cstr =
+ FormatManager::GetFormatAsCString(entry.fmt);
+ special_directions_stream.Printf("%%%s", format_cstr);
+ }
+ } else if (entry.number != 0) {
+ const char style_char = ConvertValueObjectStyleToChar(
+ (ValueObject::ValueObjectRepresentationStyle)entry.number);
+ if (style_char)
+ special_directions_stream.Printf("%%%c", style_char);
+ }
+ special_directions_stream.PutChar('}');
+ special_directions =
+ llvm::StringRef(special_directions_stream.GetString());
+ }
+
+ // let us display items index_lower thru index_higher of this array
+ s.PutChar('[');
+
+ if (index_higher < 0)
+ index_higher = valobj->GetNumChildren() - 1;
+
+ uint32_t max_num_children =
+ target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
+
+ bool success = true;
+ for (int64_t index = index_lower; index <= index_higher; ++index) {
+ ValueObject *item =
+ ExpandIndexedExpression(target, index, exe_ctx->GetFramePtr(), false)
+ .get();
+
+ if (!item) {
+ if (log)
+ log->Printf("[Debugger::FormatPrompt] ERROR in getting child item at "
+ "index %" PRId64,
+ index);
+ } else {
+ if (log)
+ log->Printf(
+ "[Debugger::FormatPrompt] special_directions for child item: %s",
+ special_directions.data() ? special_directions.data() : "");
+ }
+
+ if (special_directions.empty()) {
+ success &= item->DumpPrintableRepresentation(s, val_obj_display,
+ custom_format);
+ } else {
+ success &= FormatEntity::FormatStringRef(
+ special_directions, s, sc, exe_ctx, nullptr, item, false, false);
+ }
+
+ if (--max_num_children == 0) {
+ s.PutCString(", ...");
+ break;
+ }
+
+ if (index < index_higher)
+ s.PutChar(',');
+ }
+ s.PutChar(']');
+ return success;
+ }
+}
+
+static bool DumpRegister(Stream &s, StackFrame *frame, const char *reg_name,
+ Format format) {
+ if (frame) {
+ RegisterContext *reg_ctx = frame->GetRegisterContext().get();
+
+ if (reg_ctx) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
+ if (reg_info) {
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ DumpRegisterValue(reg_value, &s, reg_info, false, false, format);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static bool FormatThreadExtendedInfoRecurse(
+ const FormatEntity::Entry &entry,
+ const StructuredData::ObjectSP &thread_info_dictionary,
+ const SymbolContext *sc, const ExecutionContext *exe_ctx, Stream &s) {
+ llvm::StringRef path(entry.string);
+
+ StructuredData::ObjectSP value =
+ thread_info_dictionary->GetObjectForDotSeparatedPath(path);
+
+ if (value) {
+ if (value->GetType() == eStructuredDataTypeInteger) {
+ const char *token_format = "0x%4.4" PRIx64;
+ if (!entry.printf_format.empty())
+ token_format = entry.printf_format.c_str();
+ s.Printf(token_format, value->GetAsInteger()->GetValue());
+ return true;
+ } else if (value->GetType() == eStructuredDataTypeFloat) {
+ s.Printf("%f", value->GetAsFloat()->GetValue());
+ return true;
+ } else if (value->GetType() == eStructuredDataTypeString) {
+ s.Format("{0}", value->GetAsString()->GetValue());
+ return true;
+ } else if (value->GetType() == eStructuredDataTypeArray) {
+ if (value->GetAsArray()->GetSize() > 0) {
+ s.Printf("%zu", value->GetAsArray()->GetSize());
+ return true;
+ }
+ } else if (value->GetType() == eStructuredDataTypeDictionary) {
+ s.Printf("%zu",
+ value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static inline bool IsToken(const char *var_name_begin, const char *var) {
+ return (::strncmp(var_name_begin, var, strlen(var)) == 0);
+}
+
+bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s,
+ const SymbolContext *sc,
+ const ExecutionContext *exe_ctx,
+ const Address *addr, ValueObject *valobj,
+ bool function_changed,
+ bool initial_function) {
+ if (!format_str.empty()) {
+ FormatEntity::Entry root;
+ Status error = FormatEntity::Parse(format_str, root);
+ if (error.Success()) {
+ return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj,
+ function_changed, initial_function);
+ }
+ }
+ return false;
+}
+
+bool FormatEntity::FormatCString(const char *format, Stream &s,
+ const SymbolContext *sc,
+ const ExecutionContext *exe_ctx,
+ const Address *addr, ValueObject *valobj,
+ bool function_changed, bool initial_function) {
+ if (format && format[0]) {
+ FormatEntity::Entry root;
+ llvm::StringRef format_str(format);
+ Status error = FormatEntity::Parse(format_str, root);
+ if (error.Success()) {
+ return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj,
+ function_changed, initial_function);
+ }
+ }
+ return false;
+}
+
+bool FormatEntity::Format(const Entry &entry, Stream &s,
+ const SymbolContext *sc,
+ const ExecutionContext *exe_ctx, const Address *addr,
+ ValueObject *valobj, bool function_changed,
+ bool initial_function) {
+ switch (entry.type) {
+ case Entry::Type::Invalid:
+ case Entry::Type::ParentNumber: // Only used for
+ // FormatEntity::Entry::Definition encoding
+ case Entry::Type::ParentString: // Only used for
+ // FormatEntity::Entry::Definition encoding
+ return false;
+ case Entry::Type::EscapeCode:
+ if (exe_ctx) {
+ if (Target *target = exe_ctx->GetTargetPtr()) {
+ Debugger &debugger = target->GetDebugger();
+ if (debugger.GetUseColor()) {
+ s.PutCString(entry.string);
+ }
+ }
+ }
+ // Always return true, so colors being disabled is transparent.
+ return true;
+
+ case Entry::Type::Root:
+ for (const auto &child : entry.children) {
+ if (!Format(child, s, sc, exe_ctx, addr, valobj, function_changed,
+ initial_function)) {
+ return false; // If any item of root fails, then the formatting fails
+ }
+ }
+ return true; // Only return true if all items succeeded
+
+ case Entry::Type::String:
+ s.PutCString(entry.string);
+ return true;
+
+ case Entry::Type::Scope: {
+ StreamString scope_stream;
+ bool success = false;
+ for (const auto &child : entry.children) {
+ success = Format(child, scope_stream, sc, exe_ctx, addr, valobj,
+ function_changed, initial_function);
+ if (!success)
+ break;
+ }
+ // Only if all items in a scope succeed, then do we print the output into
+ // the main stream
+ if (success)
+ s.Write(scope_stream.GetString().data(), scope_stream.GetString().size());
+ }
+ return true; // Scopes always successfully print themselves
+
+ case Entry::Type::Variable:
+ case Entry::Type::VariableSynthetic:
+ case Entry::Type::ScriptVariable:
+ case Entry::Type::ScriptVariableSynthetic:
+ return DumpValue(s, sc, exe_ctx, entry, valobj);
+
+ case Entry::Type::AddressFile:
+ case Entry::Type::AddressLoad:
+ case Entry::Type::AddressLoadOrFile:
+ return (addr != nullptr && addr->IsValid() &&
+ DumpAddress(s, sc, exe_ctx, *addr,
+ entry.type == Entry::Type::AddressLoadOrFile));
+
+ case Entry::Type::ProcessID:
+ if (exe_ctx) {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process) {
+ const char *format = "%" PRIu64;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, process->GetID());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::ProcessFile:
+ if (exe_ctx) {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process) {
+ Module *exe_module = process->GetTarget().GetExecutableModulePointer();
+ if (exe_module) {
+ if (DumpFile(s, exe_module->GetFileSpec(), (FileKind)entry.number))
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ScriptProcess:
+ if (exe_ctx) {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process)
+ return RunScriptFormatKeyword(s, sc, exe_ctx, process,
+ entry.string.c_str());
+ }
+ return false;
+
+ case Entry::Type::ThreadID:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ const char *format = "0x%4.4" PRIx64;
+ if (!entry.printf_format.empty()) {
+ // Watch for the special "tid" format...
+ if (entry.printf_format == "tid") {
+ // TODO(zturner): Rather than hardcoding this to be platform
+ // specific, it should be controlled by a setting and the default
+ // value of the setting can be different depending on the platform.
+ Target &target = thread->GetProcess()->GetTarget();
+ ArchSpec arch(target.GetArchitecture());
+ llvm::Triple::OSType ostype = arch.IsValid()
+ ? arch.GetTriple().getOS()
+ : llvm::Triple::UnknownOS;
+ if ((ostype == llvm::Triple::FreeBSD) ||
+ (ostype == llvm::Triple::Linux) ||
+ (ostype == llvm::Triple::NetBSD)) {
+ format = "%" PRIu64;
+ }
+ } else {
+ format = entry.printf_format.c_str();
+ }
+ }
+ s.Printf(format, thread->GetID());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadProtocolID:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ const char *format = "0x%4.4" PRIx64;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, thread->GetProtocolID());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadIndexID:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ const char *format = "%" PRIu32;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, thread->GetIndexID());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadName:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ const char *cstr = thread->GetName();
+ if (cstr && cstr[0]) {
+ s.PutCString(cstr);
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadQueue:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ const char *cstr = thread->GetQueueName();
+ if (cstr && cstr[0]) {
+ s.PutCString(cstr);
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadStopReason:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp && stop_info_sp->IsValid()) {
+ const char *cstr = stop_info_sp->GetDescription();
+ if (cstr && cstr[0]) {
+ s.PutCString(cstr);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadReturnValue:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp && stop_info_sp->IsValid()) {
+ ValueObjectSP return_valobj_sp =
+ StopInfo::GetReturnValueObject(stop_info_sp);
+ if (return_valobj_sp) {
+ return_valobj_sp->Dump(s);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadCompletedExpression:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp && stop_info_sp->IsValid()) {
+ ExpressionVariableSP expression_var_sp =
+ StopInfo::GetExpressionVariable(stop_info_sp);
+ if (expression_var_sp && expression_var_sp->GetValueObject()) {
+ expression_var_sp->GetValueObject()->Dump(s);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ScriptThread:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread)
+ return RunScriptFormatKeyword(s, sc, exe_ctx, thread,
+ entry.string.c_str());
+ }
+ return false;
+
+ case Entry::Type::ThreadInfo:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ StructuredData::ObjectSP object_sp = thread->GetExtendedInfo();
+ if (object_sp &&
+ object_sp->GetType() == eStructuredDataTypeDictionary) {
+ if (FormatThreadExtendedInfoRecurse(entry, object_sp, sc, exe_ctx, s))
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::TargetArch:
+ if (exe_ctx) {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target) {
+ const ArchSpec &arch = target->GetArchitecture();
+ if (arch.IsValid()) {
+ s.PutCString(arch.GetArchitectureName());
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ScriptTarget:
+ if (exe_ctx) {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target)
+ return RunScriptFormatKeyword(s, sc, exe_ctx, target,
+ entry.string.c_str());
+ }
+ return false;
+
+ case Entry::Type::ModuleFile:
+ if (sc) {
+ Module *module = sc->module_sp.get();
+ if (module) {
+ if (DumpFile(s, module->GetFileSpec(), (FileKind)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::File:
+ if (sc) {
+ CompileUnit *cu = sc->comp_unit;
+ if (cu) {
+ // CompileUnit is a FileSpec
+ if (DumpFile(s, *cu, (FileKind)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::Lang:
+ if (sc) {
+ CompileUnit *cu = sc->comp_unit;
+ if (cu) {
+ const char *lang_name =
+ Language::GetNameForLanguageType(cu->GetLanguage());
+ if (lang_name) {
+ s.PutCString(lang_name);
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameIndex:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ const char *format = "%" PRIu32;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, frame->GetFrameIndex());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameRegisterPC:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ const Address &pc_addr = frame->GetFrameCodeAddress();
+ if (pc_addr.IsValid()) {
+ if (DumpAddress(s, sc, exe_ctx, pc_addr, false))
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameRegisterSP:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP,
+ (lldb::Format)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameRegisterFP:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP,
+ (lldb::Format)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameRegisterFlags:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ if (DumpRegister(s, frame, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, (lldb::Format)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameNoDebug:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ return !frame->HasDebugInformation();
+ }
+ }
+ return true;
+
+ case Entry::Type::FrameRegisterByName:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ if (DumpRegister(s, frame, entry.string.c_str(),
+ (lldb::Format)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameIsArtificial: {
+ if (exe_ctx)
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return frame->IsArtificial();
+ return false;
+ }
+
+ case Entry::Type::ScriptFrame:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame)
+ return RunScriptFormatKeyword(s, sc, exe_ctx, frame,
+ entry.string.c_str());
+ }
+ return false;
+
+ case Entry::Type::FunctionID:
+ if (sc) {
+ if (sc->function) {
+ s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID());
+ return true;
+ } else if (sc->symbol) {
+ s.Printf("symbol[%u]", sc->symbol->GetID());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FunctionDidChange:
+ return function_changed;
+
+ case Entry::Type::FunctionInitialFunction:
+ return initial_function;
+
+ case Entry::Type::FunctionName: {
+ Language *language_plugin = nullptr;
+ bool language_plugin_handled = false;
+ StreamString ss;
+ if (sc->function)
+ language_plugin = Language::FindPlugin(sc->function->GetLanguage());
+ else if (sc->symbol)
+ language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
+ if (language_plugin) {
+ language_plugin_handled = language_plugin->GetFunctionDisplayName(
+ sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
+ }
+ if (language_plugin_handled) {
+ s << ss.GetString();
+ return true;
+ } else {
+ const char *name = nullptr;
+ if (sc->function)
+ name = sc->function->GetName().AsCString(nullptr);
+ else if (sc->symbol)
+ name = sc->symbol->GetName().AsCString(nullptr);
+ if (name) {
+ s.PutCString(name);
+
+ if (sc->block) {
+ Block *inline_block = sc->block->GetContainingInlinedBlock();
+ if (inline_block) {
+ const InlineFunctionInfo *inline_info =
+ sc->block->GetInlinedFunctionInfo();
+ if (inline_info) {
+ s.PutCString(" [inlined] ");
+ inline_info->GetName(sc->function->GetLanguage()).Dump(&s);
+ }
+ }
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::FunctionNameNoArgs: {
+ Language *language_plugin = nullptr;
+ bool language_plugin_handled = false;
+ StreamString ss;
+ if (sc->function)
+ language_plugin = Language::FindPlugin(sc->function->GetLanguage());
+ else if (sc->symbol)
+ language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
+ if (language_plugin) {
+ language_plugin_handled = language_plugin->GetFunctionDisplayName(
+ sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
+ ss);
+ }
+ if (language_plugin_handled) {
+ s << ss.GetString();
+ return true;
+ } else {
+ ConstString name;
+ if (sc->function)
+ name = sc->function->GetNameNoArguments();
+ else if (sc->symbol)
+ name = sc->symbol->GetNameNoArguments();
+ if (name) {
+ s.PutCString(name.GetCString());
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::FunctionNameWithArgs: {
+ Language *language_plugin = nullptr;
+ bool language_plugin_handled = false;
+ StreamString ss;
+ if (sc->function)
+ language_plugin = Language::FindPlugin(sc->function->GetLanguage());
+ else if (sc->symbol)
+ language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
+ if (language_plugin) {
+ language_plugin_handled = language_plugin->GetFunctionDisplayName(
+ sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss);
+ }
+ if (language_plugin_handled) {
+ s << ss.GetString();
+ return true;
+ } else {
+ // Print the function name with arguments in it
+ if (sc->function) {
+ ExecutionContextScope *exe_scope =
+ exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
+ const char *cstr = sc->function->GetName().AsCString(nullptr);
+ if (cstr) {
+ const InlineFunctionInfo *inline_info = nullptr;
+ VariableListSP variable_list_sp;
+ bool get_function_vars = true;
+ if (sc->block) {
+ Block *inline_block = sc->block->GetContainingInlinedBlock();
+
+ if (inline_block) {
+ get_function_vars = false;
+ inline_info = sc->block->GetInlinedFunctionInfo();
+ if (inline_info)
+ variable_list_sp = inline_block->GetBlockVariableList(true);
+ }
+ }
+
+ if (get_function_vars) {
+ variable_list_sp =
+ sc->function->GetBlock(true).GetBlockVariableList(true);
+ }
+
+ if (inline_info) {
+ s.PutCString(cstr);
+ s.PutCString(" [inlined] ");
+ cstr =
+ inline_info->GetName(sc->function->GetLanguage()).GetCString();
+ }
+
+ VariableList args;
+ if (variable_list_sp)
+ variable_list_sp->AppendVariablesWithScope(
+ eValueTypeVariableArgument, args);
+ if (args.GetSize() > 0) {
+ const char *open_paren = strchr(cstr, '(');
+ const char *close_paren = nullptr;
+ const char *generic = strchr(cstr, '<');
+ // if before the arguments list begins there is a template sign
+ // then scan to the end of the generic args before you try to find
+ // the arguments list
+ if (generic && open_paren && generic < open_paren) {
+ int generic_depth = 1;
+ ++generic;
+ for (; *generic && generic_depth > 0; generic++) {
+ if (*generic == '<')
+ generic_depth++;
+ if (*generic == '>')
+ generic_depth--;
+ }
+ if (*generic)
+ open_paren = strchr(generic, '(');
+ else
+ open_paren = nullptr;
+ }
+ if (open_paren) {
+ if (IsToken(open_paren, "(anonymous namespace)")) {
+ open_paren =
+ strchr(open_paren + strlen("(anonymous namespace)"), '(');
+ if (open_paren)
+ close_paren = strchr(open_paren, ')');
+ } else
+ close_paren = strchr(open_paren, ')');
+ }
+
+ if (open_paren)
+ s.Write(cstr, open_paren - cstr + 1);
+ else {
+ s.PutCString(cstr);
+ s.PutChar('(');
+ }
+ const size_t num_args = args.GetSize();
+ for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) {
+ std::string buffer;
+
+ VariableSP var_sp(args.GetVariableAtIndex(arg_idx));
+ ValueObjectSP var_value_sp(
+ ValueObjectVariable::Create(exe_scope, var_sp));
+ StreamString ss;
+ llvm::StringRef var_representation;
+ const char *var_name = var_value_sp->GetName().GetCString();
+ if (var_value_sp->GetCompilerType().IsValid()) {
+ if (var_value_sp && exe_scope->CalculateTarget())
+ var_value_sp =
+ var_value_sp->GetQualifiedRepresentationIfAvailable(
+ exe_scope->CalculateTarget()
+ ->TargetProperties::GetPreferDynamicValue(),
+ exe_scope->CalculateTarget()
+ ->TargetProperties::GetEnableSyntheticValue());
+ if (var_value_sp->GetCompilerType().IsAggregateType() &&
+ DataVisualization::ShouldPrintAsOneLiner(*var_value_sp)) {
+ static StringSummaryFormat format(
+ TypeSummaryImpl::Flags()
+ .SetHideItemNames(false)
+ .SetShowMembersOneLiner(true),
+ "");
+ format.FormatObject(var_value_sp.get(), buffer,
+ TypeSummaryOptions());
+ var_representation = buffer;
+ } else
+ var_value_sp->DumpPrintableRepresentation(
+ ss,
+ ValueObject::ValueObjectRepresentationStyle::
+ eValueObjectRepresentationStyleSummary,
+ eFormatDefault,
+ ValueObject::PrintableRepresentationSpecialCases::eAllow,
+ false);
+ }
+
+ if (!ss.GetString().empty())
+ var_representation = ss.GetString();
+ if (arg_idx > 0)
+ s.PutCString(", ");
+ if (var_value_sp->GetError().Success()) {
+ if (!var_representation.empty())
+ s.Printf("%s=%s", var_name, var_representation.str().c_str());
+ else
+ s.Printf("%s=%s at %s", var_name,
+ var_value_sp->GetTypeName().GetCString(),
+ var_value_sp->GetLocationAsCString());
+ } else
+ s.Printf("%s=<unavailable>", var_name);
+ }
+
+ if (close_paren)
+ s.PutCString(close_paren);
+ else
+ s.PutChar(')');
+
+ } else {
+ s.PutCString(cstr);
+ }
+ return true;
+ }
+ } else if (sc->symbol) {
+ const char *cstr = sc->symbol->GetName().AsCString(nullptr);
+ if (cstr) {
+ s.PutCString(cstr);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::FunctionAddrOffset:
+ if (addr) {
+ if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, false, false,
+ false))
+ return true;
+ }
+ return false;
+
+ case Entry::Type::FunctionAddrOffsetConcrete:
+ if (addr) {
+ if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, true, true,
+ true))
+ return true;
+ }
+ return false;
+
+ case Entry::Type::FunctionLineOffset:
+ return (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
+ sc->line_entry.range.GetBaseAddress(),
+ false, false, false));
+
+ case Entry::Type::FunctionPCOffset:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ if (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
+ frame->GetFrameCodeAddress(), false,
+ false, false))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FunctionChanged:
+ return function_changed;
+
+ case Entry::Type::FunctionIsOptimized: {
+ bool is_optimized = false;
+ if (sc->function && sc->function->GetIsOptimized()) {
+ is_optimized = true;
+ }
+ return is_optimized;
+ }
+
+ case Entry::Type::FunctionInitial:
+ return initial_function;
+
+ case Entry::Type::LineEntryFile:
+ if (sc && sc->line_entry.IsValid()) {
+ Module *module = sc->module_sp.get();
+ if (module) {
+ if (DumpFile(s, sc->line_entry.file, (FileKind)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::LineEntryLineNumber:
+ if (sc && sc->line_entry.IsValid()) {
+ const char *format = "%" PRIu32;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, sc->line_entry.line);
+ return true;
+ }
+ return false;
+
+ case Entry::Type::LineEntryColumn:
+ if (sc && sc->line_entry.IsValid() && sc->line_entry.column) {
+ const char *format = "%" PRIu32;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, sc->line_entry.column);
+ return true;
+ }
+ return false;
+
+ case Entry::Type::LineEntryStartAddress:
+ case Entry::Type::LineEntryEndAddress:
+ if (sc && sc->line_entry.range.GetBaseAddress().IsValid()) {
+ Address addr = sc->line_entry.range.GetBaseAddress();
+
+ if (entry.type == Entry::Type::LineEntryEndAddress)
+ addr.Slide(sc->line_entry.range.GetByteSize());
+ if (DumpAddress(s, sc, exe_ctx, addr, false))
+ return true;
+ }
+ return false;
+
+ case Entry::Type::CurrentPCArrow:
+ if (addr && exe_ctx && exe_ctx->GetFramePtr()) {
+ RegisterContextSP reg_ctx =
+ exe_ctx->GetFramePtr()->GetRegisterContextSP();
+ if (reg_ctx) {
+ addr_t pc_loadaddr = reg_ctx->GetPC();
+ if (pc_loadaddr != LLDB_INVALID_ADDRESS) {
+ Address pc;
+ pc.SetLoadAddress(pc_loadaddr, exe_ctx->GetTargetPtr());
+ if (pc == *addr) {
+ s.Printf("-> ");
+ return true;
+ }
+ }
+ }
+ s.Printf(" ");
+ return true;
+ }
+ return false;
+ }
+ return false;
+}
+
+static bool DumpCommaSeparatedChildEntryNames(
+ Stream &s, const FormatEntity::Entry::Definition *parent) {
+ if (parent->children) {
+ const size_t n = parent->num_children;
+ for (size_t i = 0; i < n; ++i) {
+ if (i > 0)
+ s.PutCString(", ");
+ s.Printf("\"%s\"", parent->children[i].name);
+ }
+ return true;
+ }
+ return false;
+}
+
+static Status ParseEntry(const llvm::StringRef &format_str,
+ const FormatEntity::Entry::Definition *parent,
+ FormatEntity::Entry &entry) {
+ Status error;
+
+ const size_t sep_pos = format_str.find_first_of(".[:");
+ const char sep_char =
+ (sep_pos == llvm::StringRef::npos) ? '\0' : format_str[sep_pos];
+ llvm::StringRef key = format_str.substr(0, sep_pos);
+
+ const size_t n = parent->num_children;
+ for (size_t i = 0; i < n; ++i) {
+ const FormatEntity::Entry::Definition *entry_def = parent->children + i;
+ if (key.equals(entry_def->name) || entry_def->name[0] == '*') {
+ llvm::StringRef value;
+ if (sep_char)
+ value =
+ format_str.substr(sep_pos + (entry_def->keep_separator ? 0 : 1));
+ switch (entry_def->type) {
+ case FormatEntity::Entry::Type::ParentString:
+ entry.string = format_str.str();
+ return error; // Success
+
+ case FormatEntity::Entry::Type::ParentNumber:
+ entry.number = entry_def->data;
+ return error; // Success
+
+ case FormatEntity::Entry::Type::EscapeCode:
+ entry.type = entry_def->type;
+ entry.string = entry_def->string;
+ return error; // Success
+
+ default:
+ entry.type = entry_def->type;
+ break;
+ }
+
+ if (value.empty()) {
+ if (entry_def->type == FormatEntity::Entry::Type::Invalid) {
+ if (entry_def->children) {
+ StreamString error_strm;
+ error_strm.Printf("'%s' can't be specified on its own, you must "
+ "access one of its children: ",
+ entry_def->name);
+ DumpCommaSeparatedChildEntryNames(error_strm, entry_def);
+ error.SetErrorStringWithFormat("%s", error_strm.GetData());
+ } else if (sep_char == ':') {
+ // Any value whose separator is a with a ':' means this value has a
+ // string argument that needs to be stored in the entry (like
+ // "${script.var:}"). In this case the string value is the empty
+ // string which is ok.
+ } else {
+ error.SetErrorStringWithFormat("%s", "invalid entry definitions");
+ }
+ }
+ } else {
+ if (entry_def->children) {
+ error = ParseEntry(value, entry_def, entry);
+ } else if (sep_char == ':') {
+ // Any value whose separator is a with a ':' means this value has a
+ // string argument that needs to be stored in the entry (like
+ // "${script.var:modulename.function}")
+ entry.string = value.str();
+ } else {
+ error.SetErrorStringWithFormat(
+ "'%s' followed by '%s' but it has no children", key.str().c_str(),
+ value.str().c_str());
+ }
+ }
+ return error;
+ }
+ }
+ StreamString error_strm;
+ if (parent->type == FormatEntity::Entry::Type::Root)
+ error_strm.Printf(
+ "invalid top level item '%s'. Valid top level items are: ",
+ key.str().c_str());
+ else
+ error_strm.Printf("invalid member '%s' in '%s'. Valid members are: ",
+ key.str().c_str(), parent->name);
+ DumpCommaSeparatedChildEntryNames(error_strm, parent);
+ error.SetErrorStringWithFormat("%s", error_strm.GetData());
+ return error;
+}
+
+static const FormatEntity::Entry::Definition *
+FindEntry(const llvm::StringRef &format_str,
+ const FormatEntity::Entry::Definition *parent,
+ llvm::StringRef &remainder) {
+ Status error;
+
+ std::pair<llvm::StringRef, llvm::StringRef> p = format_str.split('.');
+ const size_t n = parent->num_children;
+ for (size_t i = 0; i < n; ++i) {
+ const FormatEntity::Entry::Definition *entry_def = parent->children + i;
+ if (p.first.equals(entry_def->name) || entry_def->name[0] == '*') {
+ if (p.second.empty()) {
+ if (format_str.back() == '.')
+ remainder = format_str.drop_front(format_str.size() - 1);
+ else
+ remainder = llvm::StringRef(); // Exact match
+ return entry_def;
+ } else {
+ if (entry_def->children) {
+ return FindEntry(p.second, entry_def, remainder);
+ } else {
+ remainder = p.second;
+ return entry_def;
+ }
+ }
+ }
+ }
+ remainder = format_str;
+ return parent;
+}
+
+Status FormatEntity::ParseInternal(llvm::StringRef &format, Entry &parent_entry,
+ uint32_t depth) {
+ Status error;
+ while (!format.empty() && error.Success()) {
+ const size_t non_special_chars = format.find_first_of("${}\\");
+
+ if (non_special_chars == llvm::StringRef::npos) {
+ // No special characters, just string bytes so add them and we are done
+ parent_entry.AppendText(format);
+ return error;
+ }
+
+ if (non_special_chars > 0) {
+ // We have a special character, so add all characters before these as a
+ // plain string
+ parent_entry.AppendText(format.substr(0, non_special_chars));
+ format = format.drop_front(non_special_chars);
+ }
+
+ switch (format[0]) {
+ case '\0':
+ return error;
+
+ case '{': {
+ format = format.drop_front(); // Skip the '{'
+ Entry scope_entry(Entry::Type::Scope);
+ error = FormatEntity::ParseInternal(format, scope_entry, depth + 1);
+ if (error.Fail())
+ return error;
+ parent_entry.AppendEntry(std::move(scope_entry));
+ } break;
+
+ case '}':
+ if (depth == 0)
+ error.SetErrorString("unmatched '}' character");
+ else
+ format =
+ format
+ .drop_front(); // Skip the '}' as we are at the end of the scope
+ return error;
+
+ case '\\': {
+ format = format.drop_front(); // Skip the '\' character
+ if (format.empty()) {
+ error.SetErrorString(
+ "'\\' character was not followed by another character");
+ return error;
+ }
+
+ const char desens_char = format[0];
+ format = format.drop_front(); // Skip the desensitized char character
+ switch (desens_char) {
+ case 'a':
+ parent_entry.AppendChar('\a');
+ break;
+ case 'b':
+ parent_entry.AppendChar('\b');
+ break;
+ case 'f':
+ parent_entry.AppendChar('\f');
+ break;
+ case 'n':
+ parent_entry.AppendChar('\n');
+ break;
+ case 'r':
+ parent_entry.AppendChar('\r');
+ break;
+ case 't':
+ parent_entry.AppendChar('\t');
+ break;
+ case 'v':
+ parent_entry.AppendChar('\v');
+ break;
+ case '\'':
+ parent_entry.AppendChar('\'');
+ break;
+ case '\\':
+ parent_entry.AppendChar('\\');
+ break;
+ case '0':
+ // 1 to 3 octal chars
+ {
+ // Make a string that can hold onto the initial zero char, up to 3
+ // octal digits, and a terminating NULL.
+ char oct_str[5] = {0, 0, 0, 0, 0};
+
+ int i;
+ for (i = 0; (format[i] >= '0' && format[i] <= '7') && i < 4; ++i)
+ oct_str[i] = format[i];
+
+ // We don't want to consume the last octal character since the main
+ // for loop will do this for us, so we advance p by one less than i
+ // (even if i is zero)
+ format = format.drop_front(i);
+ unsigned long octal_value = ::strtoul(oct_str, nullptr, 8);
+ if (octal_value <= UINT8_MAX) {
+ parent_entry.AppendChar((char)octal_value);
+ } else {
+ error.SetErrorString("octal number is larger than a single byte");
+ return error;
+ }
+ }
+ break;
+
+ case 'x':
+ // hex number in the format
+ if (isxdigit(format[0])) {
+ // Make a string that can hold onto two hex chars plus a
+ // NULL terminator
+ char hex_str[3] = {0, 0, 0};
+ hex_str[0] = format[0];
+
+ format = format.drop_front();
+
+ if (isxdigit(format[0])) {
+ hex_str[1] = format[0];
+ format = format.drop_front();
+ }
+
+ unsigned long hex_value = strtoul(hex_str, nullptr, 16);
+ if (hex_value <= UINT8_MAX) {
+ parent_entry.AppendChar((char)hex_value);
+ } else {
+ error.SetErrorString("hex number is larger than a single byte");
+ return error;
+ }
+ } else {
+ parent_entry.AppendChar(desens_char);
+ }
+ break;
+
+ default:
+ // Just desensitize any other character by just printing what came
+ // after the '\'
+ parent_entry.AppendChar(desens_char);
+ break;
+ }
+ } break;
+
+ case '$':
+ if (format.size() == 1) {
+ // '$' at the end of a format string, just print the '$'
+ parent_entry.AppendText("$");
+ } else {
+ format = format.drop_front(); // Skip the '$'
+
+ if (format[0] == '{') {
+ format = format.drop_front(); // Skip the '{'
+
+ llvm::StringRef variable, variable_format;
+ error = FormatEntity::ExtractVariableInfo(format, variable,
+ variable_format);
+ if (error.Fail())
+ return error;
+ bool verify_is_thread_id = false;
+ Entry entry;
+ if (!variable_format.empty()) {
+ entry.printf_format = variable_format.str();
+
+ // If the format contains a '%' we are going to assume this is a
+ // printf style format. So if you want to format your thread ID
+ // using "0x%llx" you can use: ${thread.id%0x%llx}
+ //
+ // If there is no '%' in the format, then it is assumed to be a
+ // LLDB format name, or one of the extended formats specified in
+ // the switch statement below.
+
+ if (entry.printf_format.find('%') == std::string::npos) {
+ bool clear_printf = false;
+
+ if (FormatManager::GetFormatFromCString(
+ entry.printf_format.c_str(), false, entry.fmt)) {
+ // We have an LLDB format, so clear the printf format
+ clear_printf = true;
+ } else if (entry.printf_format.size() == 1) {
+ switch (entry.printf_format[0]) {
+ case '@': // if this is an @ sign, print ObjC description
+ entry.number = ValueObject::
+ eValueObjectRepresentationStyleLanguageSpecific;
+ clear_printf = true;
+ break;
+ case 'V': // if this is a V, print the value using the default
+ // format
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleValue;
+ clear_printf = true;
+ break;
+ case 'L': // if this is an L, print the location of the value
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleLocation;
+ clear_printf = true;
+ break;
+ case 'S': // if this is an S, print the summary after all
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleSummary;
+ clear_printf = true;
+ break;
+ case '#': // if this is a '#', print the number of children
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleChildrenCount;
+ clear_printf = true;
+ break;
+ case 'T': // if this is a 'T', print the type
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleType;
+ clear_printf = true;
+ break;
+ case 'N': // if this is a 'N', print the name
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleName;
+ clear_printf = true;
+ break;
+ case '>': // if this is a '>', print the expression path
+ entry.number = ValueObject::
+ eValueObjectRepresentationStyleExpressionPath;
+ clear_printf = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("invalid format: '%s'",
+ entry.printf_format.c_str());
+ return error;
+ }
+ } else if (FormatManager::GetFormatFromCString(
+ entry.printf_format.c_str(), true, entry.fmt)) {
+ clear_printf = true;
+ } else if (entry.printf_format == "tid") {
+ verify_is_thread_id = true;
+ } else {
+ error.SetErrorStringWithFormat("invalid format: '%s'",
+ entry.printf_format.c_str());
+ return error;
+ }
+
+ // Our format string turned out to not be a printf style format
+ // so lets clear the string
+ if (clear_printf)
+ entry.printf_format.clear();
+ }
+ }
+
+ // Check for dereferences
+ if (variable[0] == '*') {
+ entry.deref = true;
+ variable = variable.drop_front();
+ }
+
+ error = ParseEntry(variable, &g_root, entry);
+ if (error.Fail())
+ return error;
+
+ if (verify_is_thread_id) {
+ if (entry.type != Entry::Type::ThreadID &&
+ entry.type != Entry::Type::ThreadProtocolID) {
+ error.SetErrorString("the 'tid' format can only be used on "
+ "${thread.id} and ${thread.protocol_id}");
+ }
+ }
+
+ switch (entry.type) {
+ case Entry::Type::Variable:
+ case Entry::Type::VariableSynthetic:
+ if (entry.number == 0) {
+ if (entry.string.empty())
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleValue;
+ else
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleSummary;
+ }
+ break;
+ default:
+ // Make sure someone didn't try to dereference anything but ${var}
+ // or ${svar}
+ if (entry.deref) {
+ error.SetErrorStringWithFormat(
+ "${%s} can't be dereferenced, only ${var} and ${svar} can.",
+ variable.str().c_str());
+ return error;
+ }
+ }
+ parent_entry.AppendEntry(std::move(entry));
+ }
+ }
+ break;
+ }
+ }
+ return error;
+}
+
+Status FormatEntity::ExtractVariableInfo(llvm::StringRef &format_str,
+ llvm::StringRef &variable_name,
+ llvm::StringRef &variable_format) {
+ Status error;
+ variable_name = llvm::StringRef();
+ variable_format = llvm::StringRef();
+
+ const size_t paren_pos = format_str.find('}');
+ if (paren_pos != llvm::StringRef::npos) {
+ const size_t percent_pos = format_str.find('%');
+ if (percent_pos < paren_pos) {
+ if (percent_pos > 0) {
+ if (percent_pos > 1)
+ variable_name = format_str.substr(0, percent_pos);
+ variable_format =
+ format_str.substr(percent_pos + 1, paren_pos - (percent_pos + 1));
+ }
+ } else {
+ variable_name = format_str.substr(0, paren_pos);
+ }
+ // Strip off elements and the formatting and the trailing '}'
+ format_str = format_str.substr(paren_pos + 1);
+ } else {
+ error.SetErrorStringWithFormat(
+ "missing terminating '}' character for '${%s'",
+ format_str.str().c_str());
+ }
+ return error;
+}
+
+bool FormatEntity::FormatFileSpec(const FileSpec &file_spec, Stream &s,
+ llvm::StringRef variable_name,
+ llvm::StringRef variable_format) {
+ if (variable_name.empty() || variable_name.equals(".fullpath")) {
+ file_spec.Dump(&s);
+ return true;
+ } else if (variable_name.equals(".basename")) {
+ s.PutCString(file_spec.GetFilename().AsCString(""));
+ return true;
+ } else if (variable_name.equals(".dirname")) {
+ s.PutCString(file_spec.GetFilename().AsCString(""));
+ return true;
+ }
+ return false;
+}
+
+static std::string MakeMatch(const llvm::StringRef &prefix,
+ const char *suffix) {
+ std::string match(prefix.str());
+ match.append(suffix);
+ return match;
+}
+
+static void AddMatches(const FormatEntity::Entry::Definition *def,
+ const llvm::StringRef &prefix,
+ const llvm::StringRef &match_prefix,
+ StringList &matches) {
+ const size_t n = def->num_children;
+ if (n > 0) {
+ for (size_t i = 0; i < n; ++i) {
+ std::string match = prefix.str();
+ if (match_prefix.empty())
+ matches.AppendString(MakeMatch(prefix, def->children[i].name));
+ else if (strncmp(def->children[i].name, match_prefix.data(),
+ match_prefix.size()) == 0)
+ matches.AppendString(
+ MakeMatch(prefix, def->children[i].name + match_prefix.size()));
+ }
+ }
+}
+
+size_t FormatEntity::AutoComplete(CompletionRequest &request) {
+ llvm::StringRef str = request.GetCursorArgumentPrefix().str();
+
+ request.SetWordComplete(false);
+ str = str.drop_front(request.GetMatchStartPoint());
+
+ const size_t dollar_pos = str.rfind('$');
+ if (dollar_pos == llvm::StringRef::npos)
+ return 0;
+
+ // Hitting TAB after $ at the end of the string add a "{"
+ if (dollar_pos == str.size() - 1) {
+ std::string match = str.str();
+ match.append("{");
+ request.AddCompletion(match);
+ return 1;
+ }
+
+ if (str[dollar_pos + 1] != '{')
+ return 0;
+
+ const size_t close_pos = str.find('}', dollar_pos + 2);
+ if (close_pos != llvm::StringRef::npos)
+ return 0;
+
+ const size_t format_pos = str.find('%', dollar_pos + 2);
+ if (format_pos != llvm::StringRef::npos)
+ return 0;
+
+ llvm::StringRef partial_variable(str.substr(dollar_pos + 2));
+ if (partial_variable.empty()) {
+ // Suggest all top level entites as we are just past "${"
+ StringList new_matches;
+ AddMatches(&g_root, str, llvm::StringRef(), new_matches);
+ request.AddCompletions(new_matches);
+ return request.GetNumberOfMatches();
+ }
+
+ // We have a partially specified variable, find it
+ llvm::StringRef remainder;
+ const FormatEntity::Entry::Definition *entry_def =
+ FindEntry(partial_variable, &g_root, remainder);
+ if (!entry_def)
+ return 0;
+
+ const size_t n = entry_def->num_children;
+
+ if (remainder.empty()) {
+ // Exact match
+ if (n > 0) {
+ // "${thread.info" <TAB>
+ request.AddCompletion(MakeMatch(str, "."));
+ } else {
+ // "${thread.id" <TAB>
+ request.AddCompletion(MakeMatch(str, "}"));
+ request.SetWordComplete(true);
+ }
+ } else if (remainder.equals(".")) {
+ // "${thread." <TAB>
+ StringList new_matches;
+ AddMatches(entry_def, str, llvm::StringRef(), new_matches);
+ request.AddCompletions(new_matches);
+ } else {
+ // We have a partial match
+ // "${thre" <TAB>
+ StringList new_matches;
+ AddMatches(entry_def, str, remainder, new_matches);
+ request.AddCompletions(new_matches);
+ }
+ return request.GetNumberOfMatches();
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Highlighter.cpp b/contrib/llvm-project/lldb/source/Core/Highlighter.cpp
new file mode 100644
index 000000000000..0b0aa969bf65
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Highlighter.cpp
@@ -0,0 +1,80 @@
+//===-- Highlighter.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Highlighter.h"
+
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/AnsiTerminal.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+
+void HighlightStyle::ColorStyle::Apply(Stream &s, llvm::StringRef value) const {
+ s << m_prefix << value << m_suffix;
+}
+
+void HighlightStyle::ColorStyle::Set(llvm::StringRef prefix,
+ llvm::StringRef suffix) {
+ m_prefix = lldb_utility::ansi::FormatAnsiTerminalCodes(prefix);
+ m_suffix = lldb_utility::ansi::FormatAnsiTerminalCodes(suffix);
+}
+
+void DefaultHighlighter::Highlight(const HighlightStyle &options,
+ llvm::StringRef line,
+ llvm::Optional<size_t> cursor_pos,
+ llvm::StringRef previous_lines,
+ Stream &s) const {
+ // If we don't have a valid cursor, then we just print the line as-is.
+ if (!cursor_pos || *cursor_pos >= line.size()) {
+ s << line;
+ return;
+ }
+
+ // If we have a valid cursor, we have to apply the 'selected' style around
+ // the character below the cursor.
+
+ // Split the line around the character which is below the cursor.
+ size_t column = *cursor_pos;
+ // Print the characters before the cursor.
+ s << line.substr(0, column);
+ // Print the selected character with the defined color codes.
+ options.selected.Apply(s, line.substr(column, 1));
+ // Print the rest of the line.
+ s << line.substr(column + 1U);
+}
+
+static HighlightStyle::ColorStyle GetColor(const char *c) {
+ return HighlightStyle::ColorStyle(c, "${ansi.normal}");
+}
+
+HighlightStyle HighlightStyle::MakeVimStyle() {
+ HighlightStyle result;
+ result.comment = GetColor("${ansi.fg.purple}");
+ result.scalar_literal = GetColor("${ansi.fg.red}");
+ result.keyword = GetColor("${ansi.fg.green}");
+ return result;
+}
+
+const Highlighter &
+HighlighterManager::getHighlighterFor(lldb::LanguageType language_type,
+ llvm::StringRef path) const {
+ Language *language = lldb_private::Language::FindPlugin(language_type, path);
+ if (language && language->GetHighlighter())
+ return *language->GetHighlighter();
+ return m_default;
+}
+
+std::string Highlighter::Highlight(const HighlightStyle &options,
+ llvm::StringRef line,
+ llvm::Optional<size_t> cursor_pos,
+ llvm::StringRef previous_lines) const {
+ StreamString s;
+ Highlight(options, line, cursor_pos, previous_lines, s);
+ s.Flush();
+ return s.GetString().str();
+}
diff --git a/contrib/llvm-project/lldb/source/Core/IOHandler.cpp b/contrib/llvm-project/lldb/source/Core/IOHandler.cpp
new file mode 100644
index 000000000000..c3c722019faa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/IOHandler.cpp
@@ -0,0 +1,4671 @@
+//===-- IOHandler.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/IOHandler.h"
+
+#ifndef LLDB_DISABLE_CURSES
+#include <curses.h>
+#include <panel.h>
+#endif
+
+#if defined(__APPLE__)
+#include <deque>
+#endif
+#include <string>
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/File.h"
+#include "lldb/Utility/Predicate.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringList.h"
+#include "lldb/lldb-forward.h"
+
+#ifndef LLDB_DISABLE_LIBEDIT
+#include "lldb/Host/Editline.h"
+#endif
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#ifndef LLDB_DISABLE_CURSES
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/State.h"
+#endif
+
+#include "llvm/ADT/StringRef.h"
+
+#ifdef _MSC_VER
+#include "lldb/Host/windows/windows.h"
+#endif
+
+#include <memory>
+#include <mutex>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <type_traits>
+
+using namespace lldb;
+using namespace lldb_private;
+
+IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
+ : IOHandler(debugger, type,
+ StreamFileSP(), // Adopt STDIN from top input reader
+ StreamFileSP(), // Adopt STDOUT from top input reader
+ StreamFileSP(), // Adopt STDERR from top input reader
+ 0, // Flags
+ nullptr // Shadow file recorder
+ ) {}
+
+IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
+ const lldb::StreamFileSP &input_sp,
+ const lldb::StreamFileSP &output_sp,
+ const lldb::StreamFileSP &error_sp, uint32_t flags,
+ repro::DataRecorder *data_recorder)
+ : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
+ m_error_sp(error_sp), m_data_recorder(data_recorder), m_popped(false),
+ m_flags(flags), m_type(type), m_user_data(nullptr), m_done(false),
+ m_active(false) {
+ // If any files are not specified, then adopt them from the top input reader.
+ if (!m_input_sp || !m_output_sp || !m_error_sp)
+ debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
+ m_error_sp);
+}
+
+IOHandler::~IOHandler() = default;
+
+int IOHandler::GetInputFD() {
+ return (m_input_sp ? m_input_sp->GetFile().GetDescriptor() : -1);
+}
+
+int IOHandler::GetOutputFD() {
+ return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
+}
+
+int IOHandler::GetErrorFD() {
+ return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
+}
+
+FILE *IOHandler::GetInputFILE() {
+ return (m_input_sp ? m_input_sp->GetFile().GetStream() : nullptr);
+}
+
+FILE *IOHandler::GetOutputFILE() {
+ return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
+}
+
+FILE *IOHandler::GetErrorFILE() {
+ return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
+}
+
+StreamFileSP &IOHandler::GetInputStreamFile() { return m_input_sp; }
+
+StreamFileSP &IOHandler::GetOutputStreamFile() { return m_output_sp; }
+
+StreamFileSP &IOHandler::GetErrorStreamFile() { return m_error_sp; }
+
+bool IOHandler::GetIsInteractive() {
+ return GetInputStreamFile()->GetFile().GetIsInteractive();
+}
+
+bool IOHandler::GetIsRealTerminal() {
+ return GetInputStreamFile()->GetFile().GetIsRealTerminal();
+}
+
+void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
+
+void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
+
+void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) {
+ if (stream) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_top)
+ m_top->PrintAsync(stream, s, len);
+ }
+}
+
+IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
+ bool default_response)
+ : IOHandlerEditline(
+ debugger, IOHandler::Type::Confirm,
+ nullptr, // nullptr editline_name means no history loaded/saved
+ llvm::StringRef(), // No prompt
+ llvm::StringRef(), // No continuation prompt
+ false, // Multi-line
+ false, // Don't colorize the prompt (i.e. the confirm message.)
+ 0, *this, nullptr),
+ m_default_response(default_response), m_user_response(default_response) {
+ StreamString prompt_stream;
+ prompt_stream.PutCString(prompt);
+ if (m_default_response)
+ prompt_stream.Printf(": [Y/n] ");
+ else
+ prompt_stream.Printf(": [y/N] ");
+
+ SetPrompt(prompt_stream.GetString());
+}
+
+IOHandlerConfirm::~IOHandlerConfirm() = default;
+
+int IOHandlerConfirm::IOHandlerComplete(
+ IOHandler &io_handler, const char *current_line, const char *cursor,
+ const char *last_char, int skip_first_n_matches, int max_matches,
+ StringList &matches, StringList &descriptions) {
+ if (current_line == cursor) {
+ if (m_default_response) {
+ matches.AppendString("y");
+ } else {
+ matches.AppendString("n");
+ }
+ }
+ return matches.GetSize();
+}
+
+void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) {
+ if (line.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]) {
+ case 'y':
+ case 'Y':
+ m_user_response = true;
+ io_handler.SetIsDone(true);
+ return;
+ case 'n':
+ case 'N':
+ m_user_response = false;
+ io_handler.SetIsDone(true);
+ return;
+ default:
+ break;
+ }
+ }
+
+ if (line == "yes" || line == "YES" || line == "Yes") {
+ m_user_response = true;
+ io_handler.SetIsDone(true);
+ } else if (line == "no" || line == "NO" || line == "No") {
+ m_user_response = false;
+ io_handler.SetIsDone(true);
+ }
+}
+
+int IOHandlerDelegate::IOHandlerComplete(
+ IOHandler &io_handler, const char *current_line, const char *cursor,
+ const char *last_char, int skip_first_n_matches, int max_matches,
+ StringList &matches, StringList &descriptions) {
+ switch (m_completion) {
+ case Completion::None:
+ break;
+
+ case Completion::LLDBCommand:
+ return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(
+ current_line, cursor, last_char, skip_first_n_matches, max_matches,
+ matches, descriptions);
+ case Completion::Expression: {
+ CompletionResult result;
+ CompletionRequest request(current_line, cursor - current_line,
+ skip_first_n_matches, max_matches, result);
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ io_handler.GetDebugger().GetCommandInterpreter(),
+ CommandCompletions::eVariablePathCompletion, request, nullptr);
+ result.GetMatches(matches);
+ result.GetDescriptions(descriptions);
+
+ size_t num_matches = request.GetNumberOfMatches();
+ if (num_matches > 0) {
+ std::string common_prefix;
+ matches.LongestCommonPrefix(common_prefix);
+ const size_t partial_name_len = request.GetCursorArgumentPrefix().size();
+
+ // If we matched a unique single command, add a space... Only do this if
+ // the completer told us this was a complete word, however...
+ if (num_matches == 1 && request.GetWordComplete()) {
+ common_prefix.push_back(' ');
+ }
+ common_prefix.erase(0, partial_name_len);
+ matches.InsertStringAtIndex(0, std::move(common_prefix));
+ }
+ return num_matches;
+ } break;
+ }
+
+ return 0;
+}
+
+IOHandlerEditline::IOHandlerEditline(
+ Debugger &debugger, IOHandler::Type type,
+ const char *editline_name, // Used for saving history files
+ llvm::StringRef prompt, llvm::StringRef continuation_prompt,
+ bool multi_line, bool color_prompts, uint32_t line_number_start,
+ IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder)
+ : IOHandlerEditline(debugger, type,
+ StreamFileSP(), // Inherit input from top input reader
+ StreamFileSP(), // Inherit output from top input reader
+ StreamFileSP(), // Inherit error from top input reader
+ 0, // Flags
+ editline_name, // Used for saving history files
+ prompt, continuation_prompt, multi_line, color_prompts,
+ line_number_start, delegate, data_recorder) {}
+
+IOHandlerEditline::IOHandlerEditline(
+ Debugger &debugger, IOHandler::Type type,
+ const lldb::StreamFileSP &input_sp, const lldb::StreamFileSP &output_sp,
+ const lldb::StreamFileSP &error_sp, uint32_t flags,
+ const char *editline_name, // Used for saving history files
+ llvm::StringRef prompt, llvm::StringRef continuation_prompt,
+ bool multi_line, bool color_prompts, uint32_t line_number_start,
+ IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder)
+ : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags,
+ data_recorder),
+#ifndef LLDB_DISABLE_LIBEDIT
+ m_editline_up(),
+#endif
+ m_delegate(delegate), m_prompt(), m_continuation_prompt(),
+ m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
+ m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line),
+ m_color_prompts(color_prompts), m_interrupt_exits(true),
+ m_editing(false) {
+ SetPrompt(prompt);
+
+#ifndef LLDB_DISABLE_LIBEDIT
+ bool use_editline = false;
+
+ use_editline = m_input_sp->GetFile().GetIsRealTerminal();
+
+ if (use_editline) {
+ m_editline_up.reset(new Editline(editline_name, GetInputFILE(),
+ GetOutputFILE(), GetErrorFILE(),
+ m_color_prompts));
+ m_editline_up->SetIsInputCompleteCallback(IsInputCompleteCallback, this);
+ m_editline_up->SetAutoCompleteCallback(AutoCompleteCallback, this);
+ // See if the delegate supports fixing indentation
+ const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
+ if (indent_chars) {
+ // The delegate does support indentation, hook it up so when any
+ // indentation character is typed, the delegate gets a chance to fix it
+ m_editline_up->SetFixIndentationCallback(FixIndentationCallback, this,
+ indent_chars);
+ }
+ }
+#endif
+ SetBaseLineNumber(m_base_line_number);
+ SetPrompt(prompt);
+ SetContinuationPrompt(continuation_prompt);
+}
+
+IOHandlerEditline::~IOHandlerEditline() {
+#ifndef LLDB_DISABLE_LIBEDIT
+ m_editline_up.reset();
+#endif
+}
+
+void IOHandlerEditline::Activate() {
+ IOHandler::Activate();
+ m_delegate.IOHandlerActivated(*this, GetIsInteractive());
+}
+
+void IOHandlerEditline::Deactivate() {
+ IOHandler::Deactivate();
+ m_delegate.IOHandlerDeactivated(*this);
+}
+
+bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up) {
+ bool b = m_editline_up->GetLine(line, interrupted);
+ if (m_data_recorder)
+ m_data_recorder->Record(line, true);
+ return b;
+ } else {
+#endif
+ line.clear();
+
+ FILE *in = GetInputFILE();
+ if (in) {
+ if (GetIsInteractive()) {
+ const char *prompt = nullptr;
+
+ if (m_multi_line && m_curr_line_idx > 0)
+ prompt = GetContinuationPrompt();
+
+ if (prompt == nullptr)
+ prompt = GetPrompt();
+
+ if (prompt && prompt[0]) {
+ FILE *out = GetOutputFILE();
+ if (out) {
+ ::fprintf(out, "%s", prompt);
+ ::fflush(out);
+ }
+ }
+ }
+ char buffer[256];
+ bool done = false;
+ bool got_line = false;
+ m_editing = true;
+ while (!done) {
+#ifdef _WIN32
+ // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED
+ // according to the docs on MSDN. However, this has evidently been a
+ // known bug since Windows 8. Therefore, we can't detect if a signal
+ // interrupted in the fgets. So pressing ctrl-c causes the repl to end
+ // and the process to exit. A temporary workaround is just to attempt to
+ // fgets twice until this bug is fixed.
+ if (fgets(buffer, sizeof(buffer), in) == nullptr &&
+ fgets(buffer, sizeof(buffer), in) == nullptr) {
+#else
+ if (fgets(buffer, sizeof(buffer), in) == nullptr) {
+#endif
+ const int saved_errno = errno;
+ if (feof(in))
+ done = true;
+ else if (ferror(in)) {
+ if (saved_errno != EINTR)
+ done = true;
+ }
+ } else {
+ got_line = true;
+ size_t buffer_len = strlen(buffer);
+ assert(buffer[buffer_len] == '\0');
+ char last_char = buffer[buffer_len - 1];
+ if (last_char == '\r' || last_char == '\n') {
+ done = true;
+ // Strip trailing newlines
+ while (last_char == '\r' || last_char == '\n') {
+ --buffer_len;
+ if (buffer_len == 0)
+ break;
+ last_char = buffer[buffer_len - 1];
+ }
+ }
+ line.append(buffer, buffer_len);
+ }
+ }
+ m_editing = false;
+ if (m_data_recorder && got_line)
+ m_data_recorder->Record(line, true);
+ // We might have gotten a newline on a line by itself make sure to return
+ // true in this case.
+ return got_line;
+ } else {
+ // No more input file, we are done...
+ SetIsDone(true);
+ }
+ return false;
+#ifndef LLDB_DISABLE_LIBEDIT
+ }
+#endif
+}
+
+#ifndef LLDB_DISABLE_LIBEDIT
+bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
+ StringList &lines,
+ void *baton) {
+ IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
+ return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader,
+ lines);
+}
+
+int IOHandlerEditline::FixIndentationCallback(Editline *editline,
+ const StringList &lines,
+ int cursor_position,
+ void *baton) {
+ IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
+ return editline_reader->m_delegate.IOHandlerFixIndentation(
+ *editline_reader, lines, cursor_position);
+}
+
+int IOHandlerEditline::AutoCompleteCallback(
+ const char *current_line, const char *cursor, const char *last_char,
+ int skip_first_n_matches, int max_matches, StringList &matches,
+ StringList &descriptions, void *baton) {
+ IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
+ if (editline_reader)
+ return editline_reader->m_delegate.IOHandlerComplete(
+ *editline_reader, current_line, cursor, last_char, skip_first_n_matches,
+ max_matches, matches, descriptions);
+ return 0;
+}
+#endif
+
+const char *IOHandlerEditline::GetPrompt() {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up) {
+ return m_editline_up->GetPrompt();
+ } else {
+#endif
+ if (m_prompt.empty())
+ return nullptr;
+#ifndef LLDB_DISABLE_LIBEDIT
+ }
+#endif
+ return m_prompt.c_str();
+}
+
+bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
+ m_prompt = prompt;
+
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
+#endif
+ return true;
+}
+
+const char *IOHandlerEditline::GetContinuationPrompt() {
+ return (m_continuation_prompt.empty() ? nullptr
+ : m_continuation_prompt.c_str());
+}
+
+void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
+ m_continuation_prompt = prompt;
+
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty()
+ ? nullptr
+ : m_continuation_prompt.c_str());
+#endif
+}
+
+void IOHandlerEditline::SetBaseLineNumber(uint32_t line) {
+ m_base_line_number = line;
+}
+
+uint32_t IOHandlerEditline::GetCurrentLineIndex() const {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ return m_editline_up->GetCurrentLine();
+#endif
+ return m_curr_line_idx;
+}
+
+bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
+ m_current_lines_ptr = &lines;
+
+ bool success = false;
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up) {
+ return m_editline_up->GetLines(m_base_line_number, lines, interrupted);
+ } else {
+#endif
+ bool done = false;
+ Status error;
+
+ while (!done) {
+ // Show line numbers if we are asked to
+ std::string line;
+ if (m_base_line_number > 0 && GetIsInteractive()) {
+ FILE *out = GetOutputFILE();
+ if (out)
+ ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(),
+ GetPrompt() == nullptr ? " " : "");
+ }
+
+ m_curr_line_idx = lines.GetSize();
+
+ bool interrupted = false;
+ if (GetLine(line, interrupted) && !interrupted) {
+ lines.AppendString(line);
+ done = m_delegate.IOHandlerIsInputComplete(*this, lines);
+ } else {
+ done = true;
+ }
+ }
+ success = lines.GetSize() > 0;
+#ifndef LLDB_DISABLE_LIBEDIT
+ }
+#endif
+ return success;
+}
+
+// Each IOHandler gets to run until it is done. It should read data from the
+// "in" and place output into "out" and "err and return when done.
+void IOHandlerEditline::Run() {
+ std::string line;
+ while (IsActive()) {
+ bool interrupted = false;
+ if (m_multi_line) {
+ StringList lines;
+ if (GetLines(lines, interrupted)) {
+ if (interrupted) {
+ m_done = m_interrupt_exits;
+ m_delegate.IOHandlerInputInterrupted(*this, line);
+
+ } else {
+ line = lines.CopyList();
+ m_delegate.IOHandlerInputComplete(*this, line);
+ }
+ } else {
+ m_done = true;
+ }
+ } else {
+ if (GetLine(line, interrupted)) {
+ if (interrupted)
+ m_delegate.IOHandlerInputInterrupted(*this, line);
+ else
+ m_delegate.IOHandlerInputComplete(*this, line);
+ } else {
+ m_done = true;
+ }
+ }
+ }
+}
+
+void IOHandlerEditline::Cancel() {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ m_editline_up->Cancel();
+#endif
+}
+
+bool IOHandlerEditline::Interrupt() {
+ // Let the delgate handle it first
+ if (m_delegate.IOHandlerInterrupt(*this))
+ return true;
+
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ return m_editline_up->Interrupt();
+#endif
+ return false;
+}
+
+void IOHandlerEditline::GotEOF() {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ m_editline_up->Interrupt();
+#endif
+}
+
+void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ m_editline_up->PrintAsync(stream, s, len);
+ else
+#endif
+ {
+#ifdef _MSC_VER
+ const char *prompt = GetPrompt();
+ if (prompt) {
+ // Back up over previous prompt using Windows API
+ CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
+ HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
+ COORD coord = screen_buffer_info.dwCursorPosition;
+ coord.X -= strlen(prompt);
+ if (coord.X < 0)
+ coord.X = 0;
+ SetConsoleCursorPosition(console_handle, coord);
+ }
+#endif
+ IOHandler::PrintAsync(stream, s, len);
+#ifdef _MSC_VER
+ if (prompt)
+ IOHandler::PrintAsync(GetOutputStreamFile().get(), prompt,
+ strlen(prompt));
+#endif
+ }
+}
+
+// we may want curses to be disabled for some builds for instance, windows
+#ifndef LLDB_DISABLE_CURSES
+
+#define KEY_RETURN 10
+#define KEY_ESCAPE 27
+
+namespace curses {
+class Menu;
+class MenuDelegate;
+class Window;
+class WindowDelegate;
+typedef std::shared_ptr<Menu> MenuSP;
+typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
+typedef std::shared_ptr<Window> WindowSP;
+typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
+typedef std::vector<MenuSP> Menus;
+typedef std::vector<WindowSP> Windows;
+typedef std::vector<WindowDelegateSP> WindowDelegates;
+
+#if 0
+type summary add -s "x=${var.x}, y=${var.y}" curses::Point
+type summary add -s "w=${var.width}, h=${var.height}" curses::Size
+type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
+#endif
+
+struct Point {
+ int x;
+ int y;
+
+ Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
+
+ void Clear() {
+ x = 0;
+ y = 0;
+ }
+
+ Point &operator+=(const Point &rhs) {
+ x += rhs.x;
+ y += rhs.y;
+ return *this;
+ }
+
+ void Dump() { printf("(x=%i, y=%i)\n", x, y); }
+};
+
+bool operator==(const Point &lhs, const Point &rhs) {
+ return lhs.x == rhs.x && lhs.y == rhs.y;
+}
+
+bool operator!=(const Point &lhs, const Point &rhs) {
+ return lhs.x != rhs.x || lhs.y != rhs.y;
+}
+
+struct Size {
+ int width;
+ int height;
+ Size(int w = 0, int h = 0) : width(w), height(h) {}
+
+ void Clear() {
+ width = 0;
+ height = 0;
+ }
+
+ void Dump() { printf("(w=%i, h=%i)\n", width, height); }
+};
+
+bool operator==(const Size &lhs, const Size &rhs) {
+ return lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+bool operator!=(const Size &lhs, const Size &rhs) {
+ return lhs.width != rhs.width || lhs.height != rhs.height;
+}
+
+struct Rect {
+ Point origin;
+ Size size;
+
+ Rect() : origin(), size() {}
+
+ Rect(const Point &p, const Size &s) : origin(p), size(s) {}
+
+ void Clear() {
+ origin.Clear();
+ size.Clear();
+ }
+
+ void Dump() {
+ printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
+ size.height);
+ }
+
+ void Inset(int w, int h) {
+ if (size.width > w * 2)
+ size.width -= w * 2;
+ origin.x += w;
+
+ if (size.height > h * 2)
+ size.height -= h * 2;
+ origin.y += h;
+ }
+
+ // Return a status bar rectangle which is the last line of this rectangle.
+ // This rectangle will be modified to not include the status bar area.
+ Rect MakeStatusBar() {
+ Rect status_bar;
+ if (size.height > 1) {
+ status_bar.origin.x = origin.x;
+ status_bar.origin.y = size.height;
+ status_bar.size.width = size.width;
+ status_bar.size.height = 1;
+ --size.height;
+ }
+ return status_bar;
+ }
+
+ // Return a menubar rectangle which is the first line of this rectangle. This
+ // rectangle will be modified to not include the menubar area.
+ Rect MakeMenuBar() {
+ Rect menubar;
+ if (size.height > 1) {
+ menubar.origin.x = origin.x;
+ menubar.origin.y = origin.y;
+ menubar.size.width = size.width;
+ menubar.size.height = 1;
+ ++origin.y;
+ --size.height;
+ }
+ return menubar;
+ }
+
+ void HorizontalSplitPercentage(float top_percentage, Rect &top,
+ Rect &bottom) const {
+ float top_height = top_percentage * size.height;
+ HorizontalSplit(top_height, top, bottom);
+ }
+
+ void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
+ top = *this;
+ if (top_height < size.height) {
+ top.size.height = top_height;
+ bottom.origin.x = origin.x;
+ bottom.origin.y = origin.y + top.size.height;
+ bottom.size.width = size.width;
+ bottom.size.height = size.height - top.size.height;
+ } else {
+ bottom.Clear();
+ }
+ }
+
+ void VerticalSplitPercentage(float left_percentage, Rect &left,
+ Rect &right) const {
+ float left_width = left_percentage * size.width;
+ VerticalSplit(left_width, left, right);
+ }
+
+ void VerticalSplit(int left_width, Rect &left, Rect &right) const {
+ left = *this;
+ if (left_width < size.width) {
+ left.size.width = left_width;
+ right.origin.x = origin.x + left.size.width;
+ right.origin.y = origin.y;
+ right.size.width = size.width - left.size.width;
+ right.size.height = size.height;
+ } else {
+ right.Clear();
+ }
+ }
+};
+
+bool operator==(const Rect &lhs, const Rect &rhs) {
+ return lhs.origin == rhs.origin && lhs.size == rhs.size;
+}
+
+bool operator!=(const Rect &lhs, const Rect &rhs) {
+ return lhs.origin != rhs.origin || lhs.size != rhs.size;
+}
+
+enum HandleCharResult {
+ eKeyNotHandled = 0,
+ eKeyHandled = 1,
+ eQuitApplication = 2
+};
+
+enum class MenuActionResult {
+ Handled,
+ NotHandled,
+ Quit // Exit all menus and quit
+};
+
+struct KeyHelp {
+ int ch;
+ const char *description;
+};
+
+class WindowDelegate {
+public:
+ virtual ~WindowDelegate() = default;
+
+ virtual bool WindowDelegateDraw(Window &window, bool force) {
+ return false; // Drawing not handled
+ }
+
+ virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
+ return eKeyNotHandled;
+ }
+
+ virtual const char *WindowDelegateGetHelpText() { return nullptr; }
+
+ virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
+};
+
+class HelpDialogDelegate : public WindowDelegate {
+public:
+ HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
+
+ ~HelpDialogDelegate() override;
+
+ bool WindowDelegateDraw(Window &window, bool force) override;
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
+
+ size_t GetNumLines() const { return m_text.GetSize(); }
+
+ size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
+
+protected:
+ StringList m_text;
+ int m_first_visible_line;
+};
+
+class Window {
+public:
+ Window(const char *name)
+ : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
+ m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
+ m_prev_active_window_idx(UINT32_MAX), m_delete(false),
+ m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
+
+ Window(const char *name, WINDOW *w, bool del = true)
+ : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
+ m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
+ m_prev_active_window_idx(UINT32_MAX), m_delete(del),
+ m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
+ if (w)
+ Reset(w);
+ }
+
+ Window(const char *name, const Rect &bounds)
+ : m_name(name), m_window(nullptr), m_parent(nullptr), m_subwindows(),
+ m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
+ m_prev_active_window_idx(UINT32_MAX), m_delete(true),
+ m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
+ Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
+ bounds.origin.y));
+ }
+
+ virtual ~Window() {
+ RemoveSubWindows();
+ Reset();
+ }
+
+ void Reset(WINDOW *w = nullptr, bool del = true) {
+ if (m_window == w)
+ return;
+
+ if (m_panel) {
+ ::del_panel(m_panel);
+ m_panel = nullptr;
+ }
+ if (m_window && m_delete) {
+ ::delwin(m_window);
+ m_window = nullptr;
+ m_delete = false;
+ }
+ if (w) {
+ m_window = w;
+ m_panel = ::new_panel(m_window);
+ m_delete = del;
+ }
+ }
+
+ void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
+ void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
+ void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
+ ::box(m_window, v_char, h_char);
+ }
+ void Clear() { ::wclear(m_window); }
+ void Erase() { ::werase(m_window); }
+ Rect GetBounds() {
+ return Rect(GetParentOrigin(), GetSize());
+ } // Get the rectangle in our parent window
+ int GetChar() { return ::wgetch(m_window); }
+ int GetCursorX() { return getcurx(m_window); }
+ int GetCursorY() { return getcury(m_window); }
+ Rect GetFrame() {
+ return Rect(Point(), GetSize());
+ } // Get our rectangle in our own coordinate system
+ Point GetParentOrigin() { return Point(GetParentX(), GetParentY()); }
+ Size GetSize() { return Size(GetWidth(), GetHeight()); }
+ int GetParentX() { return getparx(m_window); }
+ int GetParentY() { return getpary(m_window); }
+ int GetMaxX() { return getmaxx(m_window); }
+ int GetMaxY() { return getmaxy(m_window); }
+ int GetWidth() { return GetMaxX(); }
+ int GetHeight() { return GetMaxY(); }
+ void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
+ void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
+ void Resize(int w, int h) { ::wresize(m_window, h, w); }
+ void Resize(const Size &size) {
+ ::wresize(m_window, size.height, size.width);
+ }
+ void PutChar(int ch) { ::waddch(m_window, ch); }
+ void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
+ void Refresh() { ::wrefresh(m_window); }
+ void DeferredRefresh() {
+ // We are using panels, so we don't need to call this...
+ //::wnoutrefresh(m_window);
+ }
+ void SetBackground(int color_pair_idx) {
+ ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
+ }
+ void UnderlineOn() { AttributeOn(A_UNDERLINE); }
+ void UnderlineOff() { AttributeOff(A_UNDERLINE); }
+
+ void PutCStringTruncated(const char *s, int right_pad) {
+ int bytes_left = GetWidth() - GetCursorX();
+ if (bytes_left > right_pad) {
+ bytes_left -= right_pad;
+ ::waddnstr(m_window, s, bytes_left);
+ }
+ }
+
+ void MoveWindow(const Point &origin) {
+ const bool moving_window = origin != GetParentOrigin();
+ if (m_is_subwin && moving_window) {
+ // Can't move subwindows, must delete and re-create
+ Size size = GetSize();
+ Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
+ origin.x),
+ true);
+ } else {
+ ::mvwin(m_window, origin.y, origin.x);
+ }
+ }
+
+ void SetBounds(const Rect &bounds) {
+ const bool moving_window = bounds.origin != GetParentOrigin();
+ if (m_is_subwin && moving_window) {
+ // Can't move subwindows, must delete and re-create
+ Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
+ bounds.origin.y, bounds.origin.x),
+ true);
+ } else {
+ if (moving_window)
+ MoveWindow(bounds.origin);
+ Resize(bounds.size);
+ }
+ }
+
+ void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
+ va_list args;
+ va_start(args, format);
+ vwprintw(m_window, format, args);
+ va_end(args);
+ }
+
+ void Touch() {
+ ::touchwin(m_window);
+ if (m_parent)
+ m_parent->Touch();
+ }
+
+ WindowSP CreateSubWindow(const char *name, const Rect &bounds,
+ bool make_active) {
+ auto get_window = [this, &bounds]() {
+ return m_window
+ ? ::subwin(m_window, bounds.size.height, bounds.size.width,
+ bounds.origin.y, bounds.origin.x)
+ : ::newwin(bounds.size.height, bounds.size.width,
+ bounds.origin.y, bounds.origin.x);
+ };
+ WindowSP subwindow_sp = std::make_shared<Window>(name, get_window(), true);
+ subwindow_sp->m_is_subwin = subwindow_sp.operator bool();
+ subwindow_sp->m_parent = this;
+ if (make_active) {
+ m_prev_active_window_idx = m_curr_active_window_idx;
+ m_curr_active_window_idx = m_subwindows.size();
+ }
+ m_subwindows.push_back(subwindow_sp);
+ ::top_panel(subwindow_sp->m_panel);
+ m_needs_update = true;
+ return subwindow_sp;
+ }
+
+ bool RemoveSubWindow(Window *window) {
+ Windows::iterator pos, end = m_subwindows.end();
+ size_t i = 0;
+ for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
+ if ((*pos).get() == window) {
+ if (m_prev_active_window_idx == i)
+ m_prev_active_window_idx = UINT32_MAX;
+ else if (m_prev_active_window_idx != UINT32_MAX &&
+ m_prev_active_window_idx > i)
+ --m_prev_active_window_idx;
+
+ if (m_curr_active_window_idx == i)
+ m_curr_active_window_idx = UINT32_MAX;
+ else if (m_curr_active_window_idx != UINT32_MAX &&
+ m_curr_active_window_idx > i)
+ --m_curr_active_window_idx;
+ window->Erase();
+ m_subwindows.erase(pos);
+ m_needs_update = true;
+ if (m_parent)
+ m_parent->Touch();
+ else
+ ::touchwin(stdscr);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ WindowSP FindSubWindow(const char *name) {
+ Windows::iterator pos, end = m_subwindows.end();
+ size_t i = 0;
+ for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
+ if ((*pos)->m_name == name)
+ return *pos;
+ }
+ return WindowSP();
+ }
+
+ void RemoveSubWindows() {
+ m_curr_active_window_idx = UINT32_MAX;
+ m_prev_active_window_idx = UINT32_MAX;
+ for (Windows::iterator pos = m_subwindows.begin();
+ pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
+ (*pos)->Erase();
+ }
+ if (m_parent)
+ m_parent->Touch();
+ else
+ ::touchwin(stdscr);
+ }
+
+ WINDOW *get() { return m_window; }
+
+ operator WINDOW *() { return m_window; }
+
+ // Window drawing utilities
+ void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
+ attr_t attr = 0;
+ if (IsActive())
+ attr = A_BOLD | COLOR_PAIR(2);
+ else
+ attr = 0;
+ if (attr)
+ AttributeOn(attr);
+
+ Box();
+ MoveCursor(3, 0);
+
+ if (title && title[0]) {
+ PutChar('<');
+ PutCString(title);
+ PutChar('>');
+ }
+
+ if (bottom_message && bottom_message[0]) {
+ int bottom_message_length = strlen(bottom_message);
+ int x = GetWidth() - 3 - (bottom_message_length + 2);
+
+ if (x > 0) {
+ MoveCursor(x, GetHeight() - 1);
+ PutChar('[');
+ PutCString(bottom_message);
+ PutChar(']');
+ } else {
+ MoveCursor(1, GetHeight() - 1);
+ PutChar('[');
+ PutCStringTruncated(bottom_message, 1);
+ }
+ }
+ if (attr)
+ AttributeOff(attr);
+ }
+
+ virtual void Draw(bool force) {
+ if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
+ return;
+
+ for (auto &subwindow_sp : m_subwindows)
+ subwindow_sp->Draw(force);
+ }
+
+ bool CreateHelpSubwindow() {
+ if (m_delegate_sp) {
+ const char *text = m_delegate_sp->WindowDelegateGetHelpText();
+ KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
+ if ((text && text[0]) || key_help) {
+ std::unique_ptr<HelpDialogDelegate> help_delegate_up(
+ new HelpDialogDelegate(text, key_help));
+ const size_t num_lines = help_delegate_up->GetNumLines();
+ const size_t max_length = help_delegate_up->GetMaxLineLength();
+ Rect bounds = GetBounds();
+ bounds.Inset(1, 1);
+ if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
+ bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
+ bounds.size.width = max_length + 4;
+ } else {
+ if (bounds.size.width > 100) {
+ const int inset_w = bounds.size.width / 4;
+ bounds.origin.x += inset_w;
+ bounds.size.width -= 2 * inset_w;
+ }
+ }
+
+ if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
+ bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
+ bounds.size.height = num_lines + 2;
+ } else {
+ if (bounds.size.height > 100) {
+ const int inset_h = bounds.size.height / 4;
+ bounds.origin.y += inset_h;
+ bounds.size.height -= 2 * inset_h;
+ }
+ }
+ WindowSP help_window_sp;
+ Window *parent_window = GetParent();
+ if (parent_window)
+ help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
+ else
+ help_window_sp = CreateSubWindow("Help", bounds, true);
+ help_window_sp->SetDelegate(
+ WindowDelegateSP(help_delegate_up.release()));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ virtual HandleCharResult HandleChar(int key) {
+ // Always check the active window first
+ HandleCharResult result = eKeyNotHandled;
+ WindowSP active_window_sp = GetActiveWindow();
+ if (active_window_sp) {
+ result = active_window_sp->HandleChar(key);
+ if (result != eKeyNotHandled)
+ return result;
+ }
+
+ if (m_delegate_sp) {
+ result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
+ if (result != eKeyNotHandled)
+ return result;
+ }
+
+ // Then check for any windows that want any keys that weren't handled. This
+ // is typically only for a menubar. Make a copy of the subwindows in case
+ // any HandleChar() functions muck with the subwindows. If we don't do
+ // this, we can crash when iterating over the subwindows.
+ Windows subwindows(m_subwindows);
+ for (auto subwindow_sp : subwindows) {
+ if (!subwindow_sp->m_can_activate) {
+ HandleCharResult result = subwindow_sp->HandleChar(key);
+ if (result != eKeyNotHandled)
+ return result;
+ }
+ }
+
+ return eKeyNotHandled;
+ }
+
+ bool SetActiveWindow(Window *window) {
+ const size_t num_subwindows = m_subwindows.size();
+ for (size_t i = 0; i < num_subwindows; ++i) {
+ if (m_subwindows[i].get() == window) {
+ m_prev_active_window_idx = m_curr_active_window_idx;
+ ::top_panel(window->m_panel);
+ m_curr_active_window_idx = i;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ WindowSP GetActiveWindow() {
+ if (!m_subwindows.empty()) {
+ if (m_curr_active_window_idx >= m_subwindows.size()) {
+ if (m_prev_active_window_idx < m_subwindows.size()) {
+ m_curr_active_window_idx = m_prev_active_window_idx;
+ m_prev_active_window_idx = UINT32_MAX;
+ } else if (IsActive()) {
+ m_prev_active_window_idx = UINT32_MAX;
+ m_curr_active_window_idx = UINT32_MAX;
+
+ // Find first window that wants to be active if this window is active
+ const size_t num_subwindows = m_subwindows.size();
+ for (size_t i = 0; i < num_subwindows; ++i) {
+ if (m_subwindows[i]->GetCanBeActive()) {
+ m_curr_active_window_idx = i;
+ break;
+ }
+ }
+ }
+ }
+
+ if (m_curr_active_window_idx < m_subwindows.size())
+ return m_subwindows[m_curr_active_window_idx];
+ }
+ return WindowSP();
+ }
+
+ bool GetCanBeActive() const { return m_can_activate; }
+
+ void SetCanBeActive(bool b) { m_can_activate = b; }
+
+ const WindowDelegateSP &GetDelegate() const { return m_delegate_sp; }
+
+ void SetDelegate(const WindowDelegateSP &delegate_sp) {
+ m_delegate_sp = delegate_sp;
+ }
+
+ Window *GetParent() const { return m_parent; }
+
+ bool IsActive() const {
+ if (m_parent)
+ return m_parent->GetActiveWindow().get() == this;
+ else
+ return true; // Top level window is always active
+ }
+
+ void SelectNextWindowAsActive() {
+ // Move active focus to next window
+ const size_t num_subwindows = m_subwindows.size();
+ if (m_curr_active_window_idx == UINT32_MAX) {
+ uint32_t idx = 0;
+ for (auto subwindow_sp : m_subwindows) {
+ if (subwindow_sp->GetCanBeActive()) {
+ m_curr_active_window_idx = idx;
+ break;
+ }
+ ++idx;
+ }
+ } else if (m_curr_active_window_idx + 1 < num_subwindows) {
+ bool handled = false;
+ m_prev_active_window_idx = m_curr_active_window_idx;
+ for (size_t idx = m_curr_active_window_idx + 1; idx < num_subwindows;
+ ++idx) {
+ if (m_subwindows[idx]->GetCanBeActive()) {
+ m_curr_active_window_idx = idx;
+ handled = true;
+ break;
+ }
+ }
+ if (!handled) {
+ for (size_t idx = 0; idx <= m_prev_active_window_idx; ++idx) {
+ if (m_subwindows[idx]->GetCanBeActive()) {
+ m_curr_active_window_idx = idx;
+ break;
+ }
+ }
+ }
+ } else {
+ m_prev_active_window_idx = m_curr_active_window_idx;
+ for (size_t idx = 0; idx < num_subwindows; ++idx) {
+ if (m_subwindows[idx]->GetCanBeActive()) {
+ m_curr_active_window_idx = idx;
+ break;
+ }
+ }
+ }
+ }
+
+ const char *GetName() const { return m_name.c_str(); }
+
+protected:
+ std::string m_name;
+ WINDOW *m_window;
+ PANEL *m_panel;
+ Window *m_parent;
+ Windows m_subwindows;
+ WindowDelegateSP m_delegate_sp;
+ uint32_t m_curr_active_window_idx;
+ uint32_t m_prev_active_window_idx;
+ bool m_delete;
+ bool m_needs_update;
+ bool m_can_activate;
+ bool m_is_subwin;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(Window);
+};
+
+class MenuDelegate {
+public:
+ virtual ~MenuDelegate() = default;
+
+ virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
+};
+
+class Menu : public WindowDelegate {
+public:
+ enum class Type { Invalid, Bar, Item, Separator };
+
+ // Menubar or separator constructor
+ Menu(Type type);
+
+ // Menuitem constructor
+ Menu(const char *name, const char *key_name, int key_value,
+ uint64_t identifier);
+
+ ~Menu() override = default;
+
+ const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
+
+ void SetDelegate(const MenuDelegateSP &delegate_sp) {
+ m_delegate_sp = delegate_sp;
+ }
+
+ void RecalculateNameLengths();
+
+ void AddSubmenu(const MenuSP &menu_sp);
+
+ int DrawAndRunMenu(Window &window);
+
+ void DrawMenuTitle(Window &window, bool highlight);
+
+ bool WindowDelegateDraw(Window &window, bool force) override;
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
+
+ MenuActionResult ActionPrivate(Menu &menu) {
+ MenuActionResult result = MenuActionResult::NotHandled;
+ if (m_delegate_sp) {
+ result = m_delegate_sp->MenuDelegateAction(menu);
+ if (result != MenuActionResult::NotHandled)
+ return result;
+ } else if (m_parent) {
+ result = m_parent->ActionPrivate(menu);
+ if (result != MenuActionResult::NotHandled)
+ return result;
+ }
+ return m_canned_result;
+ }
+
+ MenuActionResult Action() {
+ // Call the recursive action so it can try to handle it with the menu
+ // delegate, and if not, try our parent menu
+ return ActionPrivate(*this);
+ }
+
+ void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
+
+ Menus &GetSubmenus() { return m_submenus; }
+
+ const Menus &GetSubmenus() const { return m_submenus; }
+
+ int GetSelectedSubmenuIndex() const { return m_selected; }
+
+ void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
+
+ Type GetType() const { return m_type; }
+
+ int GetStartingColumn() const { return m_start_col; }
+
+ void SetStartingColumn(int col) { m_start_col = col; }
+
+ int GetKeyValue() const { return m_key_value; }
+
+ void SetKeyValue(int key_value) { m_key_value = key_value; }
+
+ std::string &GetName() { return m_name; }
+
+ std::string &GetKeyName() { return m_key_name; }
+
+ int GetDrawWidth() const {
+ return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
+ }
+
+ uint64_t GetIdentifier() const { return m_identifier; }
+
+ void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
+
+protected:
+ std::string m_name;
+ std::string m_key_name;
+ uint64_t m_identifier;
+ Type m_type;
+ int m_key_value;
+ int m_start_col;
+ int m_max_submenu_name_length;
+ int m_max_submenu_key_name_length;
+ int m_selected;
+ Menu *m_parent;
+ Menus m_submenus;
+ WindowSP m_menu_window_sp;
+ MenuActionResult m_canned_result;
+ MenuDelegateSP m_delegate_sp;
+};
+
+// Menubar or separator constructor
+Menu::Menu(Type type)
+ : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
+ m_start_col(0), m_max_submenu_name_length(0),
+ m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
+ m_submenus(), m_canned_result(MenuActionResult::NotHandled),
+ m_delegate_sp() {}
+
+// Menuitem constructor
+Menu::Menu(const char *name, const char *key_name, int key_value,
+ uint64_t identifier)
+ : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
+ m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
+ m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
+ m_submenus(), m_canned_result(MenuActionResult::NotHandled),
+ m_delegate_sp() {
+ if (name && name[0]) {
+ m_name = name;
+ m_type = Type::Item;
+ if (key_name && key_name[0])
+ m_key_name = key_name;
+ } else {
+ m_type = Type::Separator;
+ }
+}
+
+void Menu::RecalculateNameLengths() {
+ m_max_submenu_name_length = 0;
+ m_max_submenu_key_name_length = 0;
+ Menus &submenus = GetSubmenus();
+ const size_t num_submenus = submenus.size();
+ for (size_t i = 0; i < num_submenus; ++i) {
+ Menu *submenu = submenus[i].get();
+ if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
+ m_max_submenu_name_length = submenu->m_name.size();
+ if (static_cast<size_t>(m_max_submenu_key_name_length) <
+ submenu->m_key_name.size())
+ m_max_submenu_key_name_length = submenu->m_key_name.size();
+ }
+}
+
+void Menu::AddSubmenu(const MenuSP &menu_sp) {
+ menu_sp->m_parent = this;
+ if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
+ m_max_submenu_name_length = menu_sp->m_name.size();
+ if (static_cast<size_t>(m_max_submenu_key_name_length) <
+ menu_sp->m_key_name.size())
+ m_max_submenu_key_name_length = menu_sp->m_key_name.size();
+ m_submenus.push_back(menu_sp);
+}
+
+void Menu::DrawMenuTitle(Window &window, bool highlight) {
+ if (m_type == Type::Separator) {
+ window.MoveCursor(0, window.GetCursorY());
+ window.PutChar(ACS_LTEE);
+ int width = window.GetWidth();
+ if (width > 2) {
+ width -= 2;
+ for (int i = 0; i < width; ++i)
+ window.PutChar(ACS_HLINE);
+ }
+ window.PutChar(ACS_RTEE);
+ } else {
+ const int shortcut_key = m_key_value;
+ bool underlined_shortcut = false;
+ const attr_t hilgight_attr = A_REVERSE;
+ if (highlight)
+ window.AttributeOn(hilgight_attr);
+ if (isprint(shortcut_key)) {
+ size_t lower_pos = m_name.find(tolower(shortcut_key));
+ size_t upper_pos = m_name.find(toupper(shortcut_key));
+ const char *name = m_name.c_str();
+ size_t pos = std::min<size_t>(lower_pos, upper_pos);
+ if (pos != std::string::npos) {
+ underlined_shortcut = true;
+ if (pos > 0) {
+ window.PutCString(name, pos);
+ name += pos;
+ }
+ const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
+ window.AttributeOn(shortcut_attr);
+ window.PutChar(name[0]);
+ window.AttributeOff(shortcut_attr);
+ name++;
+ if (name[0])
+ window.PutCString(name);
+ }
+ }
+
+ if (!underlined_shortcut) {
+ window.PutCString(m_name.c_str());
+ }
+
+ if (highlight)
+ window.AttributeOff(hilgight_attr);
+
+ if (m_key_name.empty()) {
+ if (!underlined_shortcut && isprint(m_key_value)) {
+ window.AttributeOn(COLOR_PAIR(3));
+ window.Printf(" (%c)", m_key_value);
+ window.AttributeOff(COLOR_PAIR(3));
+ }
+ } else {
+ window.AttributeOn(COLOR_PAIR(3));
+ window.Printf(" (%s)", m_key_name.c_str());
+ window.AttributeOff(COLOR_PAIR(3));
+ }
+ }
+}
+
+bool Menu::WindowDelegateDraw(Window &window, bool force) {
+ Menus &submenus = GetSubmenus();
+ const size_t num_submenus = submenus.size();
+ const int selected_idx = GetSelectedSubmenuIndex();
+ Menu::Type menu_type = GetType();
+ switch (menu_type) {
+ case Menu::Type::Bar: {
+ window.SetBackground(2);
+ window.MoveCursor(0, 0);
+ for (size_t i = 0; i < num_submenus; ++i) {
+ Menu *menu = submenus[i].get();
+ if (i > 0)
+ window.PutChar(' ');
+ menu->SetStartingColumn(window.GetCursorX());
+ window.PutCString("| ");
+ menu->DrawMenuTitle(window, false);
+ }
+ window.PutCString(" |");
+ window.DeferredRefresh();
+ } break;
+
+ case Menu::Type::Item: {
+ int y = 1;
+ int x = 3;
+ // Draw the menu
+ int cursor_x = 0;
+ int cursor_y = 0;
+ window.Erase();
+ window.SetBackground(2);
+ window.Box();
+ for (size_t i = 0; i < num_submenus; ++i) {
+ const bool is_selected = (i == static_cast<size_t>(selected_idx));
+ window.MoveCursor(x, y + i);
+ if (is_selected) {
+ // Remember where we want the cursor to be
+ cursor_x = x - 1;
+ cursor_y = y + i;
+ }
+ submenus[i]->DrawMenuTitle(window, is_selected);
+ }
+ window.MoveCursor(cursor_x, cursor_y);
+ window.DeferredRefresh();
+ } break;
+
+ default:
+ case Menu::Type::Separator:
+ break;
+ }
+ return true; // Drawing handled...
+}
+
+HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
+ HandleCharResult result = eKeyNotHandled;
+
+ Menus &submenus = GetSubmenus();
+ const size_t num_submenus = submenus.size();
+ const int selected_idx = GetSelectedSubmenuIndex();
+ Menu::Type menu_type = GetType();
+ if (menu_type == Menu::Type::Bar) {
+ MenuSP run_menu_sp;
+ switch (key) {
+ case KEY_DOWN:
+ case KEY_UP:
+ // Show last menu or first menu
+ if (selected_idx < static_cast<int>(num_submenus))
+ run_menu_sp = submenus[selected_idx];
+ else if (!submenus.empty())
+ run_menu_sp = submenus.front();
+ result = eKeyHandled;
+ break;
+
+ case KEY_RIGHT:
+ ++m_selected;
+ if (m_selected >= static_cast<int>(num_submenus))
+ m_selected = 0;
+ if (m_selected < static_cast<int>(num_submenus))
+ run_menu_sp = submenus[m_selected];
+ else if (!submenus.empty())
+ run_menu_sp = submenus.front();
+ result = eKeyHandled;
+ break;
+
+ case KEY_LEFT:
+ --m_selected;
+ if (m_selected < 0)
+ m_selected = num_submenus - 1;
+ if (m_selected < static_cast<int>(num_submenus))
+ run_menu_sp = submenus[m_selected];
+ else if (!submenus.empty())
+ run_menu_sp = submenus.front();
+ result = eKeyHandled;
+ break;
+
+ default:
+ for (size_t i = 0; i < num_submenus; ++i) {
+ if (submenus[i]->GetKeyValue() == key) {
+ SetSelectedSubmenuIndex(i);
+ run_menu_sp = submenus[i];
+ result = eKeyHandled;
+ break;
+ }
+ }
+ break;
+ }
+
+ if (run_menu_sp) {
+ // Run the action on this menu in case we need to populate the menu with
+ // dynamic content and also in case check marks, and any other menu
+ // decorations need to be calculated
+ if (run_menu_sp->Action() == MenuActionResult::Quit)
+ return eQuitApplication;
+
+ Rect menu_bounds;
+ menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
+ menu_bounds.origin.y = 1;
+ menu_bounds.size.width = run_menu_sp->GetDrawWidth();
+ menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
+ if (m_menu_window_sp)
+ window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
+
+ m_menu_window_sp = window.GetParent()->CreateSubWindow(
+ run_menu_sp->GetName().c_str(), menu_bounds, true);
+ m_menu_window_sp->SetDelegate(run_menu_sp);
+ }
+ } else if (menu_type == Menu::Type::Item) {
+ switch (key) {
+ case KEY_DOWN:
+ if (m_submenus.size() > 1) {
+ const int start_select = m_selected;
+ while (++m_selected != start_select) {
+ if (static_cast<size_t>(m_selected) >= num_submenus)
+ m_selected = 0;
+ if (m_submenus[m_selected]->GetType() == Type::Separator)
+ continue;
+ else
+ break;
+ }
+ return eKeyHandled;
+ }
+ break;
+
+ case KEY_UP:
+ if (m_submenus.size() > 1) {
+ const int start_select = m_selected;
+ while (--m_selected != start_select) {
+ if (m_selected < static_cast<int>(0))
+ m_selected = num_submenus - 1;
+ if (m_submenus[m_selected]->GetType() == Type::Separator)
+ continue;
+ else
+ break;
+ }
+ return eKeyHandled;
+ }
+ break;
+
+ case KEY_RETURN:
+ if (static_cast<size_t>(selected_idx) < num_submenus) {
+ if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
+ return eQuitApplication;
+ window.GetParent()->RemoveSubWindow(&window);
+ return eKeyHandled;
+ }
+ break;
+
+ case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
+ // case other chars are entered for escaped sequences
+ window.GetParent()->RemoveSubWindow(&window);
+ return eKeyHandled;
+
+ default:
+ for (size_t i = 0; i < num_submenus; ++i) {
+ Menu *menu = submenus[i].get();
+ if (menu->GetKeyValue() == key) {
+ SetSelectedSubmenuIndex(i);
+ window.GetParent()->RemoveSubWindow(&window);
+ if (menu->Action() == MenuActionResult::Quit)
+ return eQuitApplication;
+ return eKeyHandled;
+ }
+ }
+ break;
+ }
+ } else if (menu_type == Menu::Type::Separator) {
+ }
+ return result;
+}
+
+class Application {
+public:
+ Application(FILE *in, FILE *out)
+ : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {}
+
+ ~Application() {
+ m_window_delegates.clear();
+ m_window_sp.reset();
+ if (m_screen) {
+ ::delscreen(m_screen);
+ m_screen = nullptr;
+ }
+ }
+
+ void Initialize() {
+ ::setlocale(LC_ALL, "");
+ ::setlocale(LC_CTYPE, "");
+ m_screen = ::newterm(nullptr, m_out, m_in);
+ ::start_color();
+ ::curs_set(0);
+ ::noecho();
+ ::keypad(stdscr, TRUE);
+ }
+
+ void Terminate() { ::endwin(); }
+
+ void Run(Debugger &debugger) {
+ bool done = false;
+ int delay_in_tenths_of_a_second = 1;
+
+ // Alas the threading model in curses is a bit lame so we need to resort to
+ // polling every 0.5 seconds. We could poll for stdin ourselves and then
+ // pass the keys down but then we need to translate all of the escape
+ // sequences ourselves. So we resort to polling for input because we need
+ // to receive async process events while in this loop.
+
+ halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths
+ // of seconds seconds when calling
+ // Window::GetChar()
+
+ ListenerSP listener_sp(
+ Listener::MakeListener("lldb.IOHandler.curses.Application"));
+ ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
+ ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
+ ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
+ debugger.EnableForwardEvents(listener_sp);
+
+ bool update = true;
+#if defined(__APPLE__)
+ std::deque<int> escape_chars;
+#endif
+
+ while (!done) {
+ if (update) {
+ m_window_sp->Draw(false);
+ // All windows should be calling Window::DeferredRefresh() instead of
+ // Window::Refresh() so we can do a single update and avoid any screen
+ // blinking
+ update_panels();
+
+ // Cursor hiding isn't working on MacOSX, so hide it in the top left
+ // corner
+ m_window_sp->MoveCursor(0, 0);
+
+ doupdate();
+ update = false;
+ }
+
+#if defined(__APPLE__)
+ // Terminal.app doesn't map its function keys correctly, F1-F4 default
+ // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
+ // possible
+ int ch;
+ if (escape_chars.empty())
+ ch = m_window_sp->GetChar();
+ else {
+ ch = escape_chars.front();
+ escape_chars.pop_front();
+ }
+ if (ch == KEY_ESCAPE) {
+ int ch2 = m_window_sp->GetChar();
+ if (ch2 == 'O') {
+ int ch3 = m_window_sp->GetChar();
+ switch (ch3) {
+ case 'P':
+ ch = KEY_F(1);
+ break;
+ case 'Q':
+ ch = KEY_F(2);
+ break;
+ case 'R':
+ ch = KEY_F(3);
+ break;
+ case 'S':
+ ch = KEY_F(4);
+ break;
+ default:
+ escape_chars.push_back(ch2);
+ if (ch3 != -1)
+ escape_chars.push_back(ch3);
+ break;
+ }
+ } else if (ch2 != -1)
+ escape_chars.push_back(ch2);
+ }
+#else
+ int ch = m_window_sp->GetChar();
+
+#endif
+ if (ch == -1) {
+ if (feof(m_in) || ferror(m_in)) {
+ done = true;
+ } else {
+ // Just a timeout from using halfdelay(), check for events
+ EventSP event_sp;
+ while (listener_sp->PeekAtNextEvent()) {
+ listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
+
+ if (event_sp) {
+ Broadcaster *broadcaster = event_sp->GetBroadcaster();
+ if (broadcaster) {
+ // uint32_t event_type = event_sp->GetType();
+ ConstString broadcaster_class(
+ broadcaster->GetBroadcasterClass());
+ if (broadcaster_class == broadcaster_class_process) {
+ debugger.GetCommandInterpreter().UpdateExecutionContext(
+ nullptr);
+ update = true;
+ continue; // Don't get any key, just update our view
+ }
+ }
+ }
+ }
+ }
+ } else {
+ HandleCharResult key_result = m_window_sp->HandleChar(ch);
+ switch (key_result) {
+ case eKeyHandled:
+ debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr);
+ update = true;
+ break;
+ case eKeyNotHandled:
+ break;
+ case eQuitApplication:
+ done = true;
+ break;
+ }
+ }
+ }
+
+ debugger.CancelForwardEvents(listener_sp);
+ }
+
+ WindowSP &GetMainWindow() {
+ if (!m_window_sp)
+ m_window_sp = std::make_shared<Window>("main", stdscr, false);
+ return m_window_sp;
+ }
+
+ WindowDelegates &GetWindowDelegates() { return m_window_delegates; }
+
+protected:
+ WindowSP m_window_sp;
+ WindowDelegates m_window_delegates;
+ SCREEN *m_screen;
+ FILE *m_in;
+ FILE *m_out;
+};
+
+} // namespace curses
+
+using namespace curses;
+
+struct Row {
+ ValueObjectManager value;
+ Row *parent;
+ // The process stop ID when the children were calculated.
+ uint32_t children_stop_id;
+ int row_idx;
+ int x;
+ int y;
+ bool might_have_children;
+ bool expanded;
+ bool calculated_children;
+ std::vector<Row> children;
+
+ Row(const ValueObjectSP &v, Row *p)
+ : value(v, lldb::eDynamicDontRunTarget, true), parent(p), row_idx(0),
+ x(1), y(1), might_have_children(v ? v->MightHaveChildren() : false),
+ expanded(false), calculated_children(false), children() {}
+
+ size_t GetDepth() const {
+ if (parent)
+ return 1 + parent->GetDepth();
+ return 0;
+ }
+
+ void Expand() {
+ expanded = true;
+ }
+
+ std::vector<Row> &GetChildren() {
+ ProcessSP process_sp = value.GetProcessSP();
+ auto stop_id = process_sp->GetStopID();
+ if (process_sp && stop_id != children_stop_id) {
+ children_stop_id = stop_id;
+ calculated_children = false;
+ }
+ if (!calculated_children) {
+ children.clear();
+ calculated_children = true;
+ ValueObjectSP valobj = value.GetSP();
+ if (valobj) {
+ const size_t num_children = valobj->GetNumChildren();
+ for (size_t i = 0; i < num_children; ++i) {
+ children.push_back(Row(valobj->GetChildAtIndex(i, true), this));
+ }
+ }
+ }
+ return children;
+ }
+
+ void Unexpand() {
+ expanded = false;
+ calculated_children = false;
+ children.clear();
+ }
+
+ void DrawTree(Window &window) {
+ if (parent)
+ parent->DrawTreeForChild(window, this, 0);
+
+ if (might_have_children) {
+ // It we can get UTF8 characters to work we should try to use the
+ // "symbol" UTF8 string below
+ // const char *symbol = "";
+ // if (row.expanded)
+ // symbol = "\xe2\x96\xbd ";
+ // else
+ // symbol = "\xe2\x96\xb7 ";
+ // window.PutCString (symbol);
+
+ // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
+ // or '>' character...
+ // if (expanded)
+ // window.PutChar (ACS_DARROW);
+ // else
+ // window.PutChar (ACS_RARROW);
+ // Since we can't find any good looking right arrow/down arrow symbols,
+ // just use a diamond...
+ window.PutChar(ACS_DIAMOND);
+ window.PutChar(ACS_HLINE);
+ }
+ }
+
+ void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
+ if (parent)
+ parent->DrawTreeForChild(window, this, reverse_depth + 1);
+
+ if (&GetChildren().back() == child) {
+ // Last child
+ if (reverse_depth == 0) {
+ window.PutChar(ACS_LLCORNER);
+ window.PutChar(ACS_HLINE);
+ } else {
+ window.PutChar(' ');
+ window.PutChar(' ');
+ }
+ } else {
+ if (reverse_depth == 0) {
+ window.PutChar(ACS_LTEE);
+ window.PutChar(ACS_HLINE);
+ } else {
+ window.PutChar(ACS_VLINE);
+ window.PutChar(' ');
+ }
+ }
+ }
+};
+
+struct DisplayOptions {
+ bool show_types;
+};
+
+class TreeItem;
+
+class TreeDelegate {
+public:
+ TreeDelegate() = default;
+ virtual ~TreeDelegate() = default;
+
+ virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
+ virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
+ virtual bool TreeDelegateItemSelected(
+ TreeItem &item) = 0; // Return true if we need to update views
+};
+
+typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
+
+class TreeItem {
+public:
+ TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
+ : m_parent(parent), m_delegate(delegate), m_user_data(nullptr),
+ m_identifier(0), m_row_idx(-1), m_children(),
+ m_might_have_children(might_have_children), m_is_expanded(false) {}
+
+ TreeItem &operator=(const TreeItem &rhs) {
+ if (this != &rhs) {
+ m_parent = rhs.m_parent;
+ m_delegate = rhs.m_delegate;
+ m_user_data = rhs.m_user_data;
+ m_identifier = rhs.m_identifier;
+ m_row_idx = rhs.m_row_idx;
+ m_children = rhs.m_children;
+ m_might_have_children = rhs.m_might_have_children;
+ m_is_expanded = rhs.m_is_expanded;
+ }
+ return *this;
+ }
+
+ size_t GetDepth() const {
+ if (m_parent)
+ return 1 + m_parent->GetDepth();
+ return 0;
+ }
+
+ int GetRowIndex() const { return m_row_idx; }
+
+ void ClearChildren() { m_children.clear(); }
+
+ void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
+
+ TreeItem &operator[](size_t i) { return m_children[i]; }
+
+ void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
+
+ size_t GetNumChildren() {
+ m_delegate.TreeDelegateGenerateChildren(*this);
+ return m_children.size();
+ }
+
+ void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
+
+ void CalculateRowIndexes(int &row_idx) {
+ SetRowIndex(row_idx);
+ ++row_idx;
+
+ const bool expanded = IsExpanded();
+
+ // The root item must calculate its children, or we must calculate the
+ // number of children if the item is expanded
+ if (m_parent == nullptr || expanded)
+ GetNumChildren();
+
+ for (auto &item : m_children) {
+ if (expanded)
+ item.CalculateRowIndexes(row_idx);
+ else
+ item.SetRowIndex(-1);
+ }
+ }
+
+ TreeItem *GetParent() { return m_parent; }
+
+ bool IsExpanded() const { return m_is_expanded; }
+
+ void Expand() { m_is_expanded = true; }
+
+ void Unexpand() { m_is_expanded = false; }
+
+ bool Draw(Window &window, const int first_visible_row,
+ const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
+ if (num_rows_left <= 0)
+ return false;
+
+ if (m_row_idx >= first_visible_row) {
+ window.MoveCursor(2, row_idx + 1);
+
+ if (m_parent)
+ m_parent->DrawTreeForChild(window, this, 0);
+
+ if (m_might_have_children) {
+ // It we can get UTF8 characters to work we should try to use the
+ // "symbol" UTF8 string below
+ // const char *symbol = "";
+ // if (row.expanded)
+ // symbol = "\xe2\x96\xbd ";
+ // else
+ // symbol = "\xe2\x96\xb7 ";
+ // window.PutCString (symbol);
+
+ // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
+ // 'v' or '>' character...
+ // if (expanded)
+ // window.PutChar (ACS_DARROW);
+ // else
+ // window.PutChar (ACS_RARROW);
+ // Since we can't find any good looking right arrow/down arrow symbols,
+ // just use a diamond...
+ window.PutChar(ACS_DIAMOND);
+ window.PutChar(ACS_HLINE);
+ }
+ bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
+ window.IsActive();
+
+ if (highlight)
+ window.AttributeOn(A_REVERSE);
+
+ m_delegate.TreeDelegateDrawTreeItem(*this, window);
+
+ if (highlight)
+ window.AttributeOff(A_REVERSE);
+ ++row_idx;
+ --num_rows_left;
+ }
+
+ if (num_rows_left <= 0)
+ return false; // We are done drawing...
+
+ if (IsExpanded()) {
+ for (auto &item : m_children) {
+ // If we displayed all the rows and item.Draw() returns false we are
+ // done drawing and can exit this for loop
+ if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
+ num_rows_left))
+ break;
+ }
+ }
+ return num_rows_left >= 0; // Return true if not done drawing yet
+ }
+
+ void DrawTreeForChild(Window &window, TreeItem *child,
+ uint32_t reverse_depth) {
+ if (m_parent)
+ m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
+
+ if (&m_children.back() == child) {
+ // Last child
+ if (reverse_depth == 0) {
+ window.PutChar(ACS_LLCORNER);
+ window.PutChar(ACS_HLINE);
+ } else {
+ window.PutChar(' ');
+ window.PutChar(' ');
+ }
+ } else {
+ if (reverse_depth == 0) {
+ window.PutChar(ACS_LTEE);
+ window.PutChar(ACS_HLINE);
+ } else {
+ window.PutChar(ACS_VLINE);
+ window.PutChar(' ');
+ }
+ }
+ }
+
+ TreeItem *GetItemForRowIndex(uint32_t row_idx) {
+ if (static_cast<uint32_t>(m_row_idx) == row_idx)
+ return this;
+ if (m_children.empty())
+ return nullptr;
+ if (IsExpanded()) {
+ for (auto &item : m_children) {
+ TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
+ if (selected_item_ptr)
+ return selected_item_ptr;
+ }
+ }
+ return nullptr;
+ }
+
+ void *GetUserData() const { return m_user_data; }
+
+ void SetUserData(void *user_data) { m_user_data = user_data; }
+
+ uint64_t GetIdentifier() const { return m_identifier; }
+
+ void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
+
+ void SetMightHaveChildren(bool b) { m_might_have_children = b; }
+
+protected:
+ TreeItem *m_parent;
+ TreeDelegate &m_delegate;
+ void *m_user_data;
+ uint64_t m_identifier;
+ int m_row_idx; // Zero based visible row index, -1 if not visible or for the
+ // root item
+ std::vector<TreeItem> m_children;
+ bool m_might_have_children;
+ bool m_is_expanded;
+};
+
+class TreeWindowDelegate : public WindowDelegate {
+public:
+ TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
+ : m_debugger(debugger), m_delegate_sp(delegate_sp),
+ m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr),
+ m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0),
+ m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
+
+ int NumVisibleRows() const { return m_max_y - m_min_y; }
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ ExecutionContext exe_ctx(
+ m_debugger.GetCommandInterpreter().GetExecutionContext());
+ Process *process = exe_ctx.GetProcessPtr();
+
+ bool display_content = false;
+ if (process) {
+ StateType state = process->GetState();
+ if (StateIsStoppedState(state, true)) {
+ // We are stopped, so it is ok to
+ display_content = true;
+ } else if (StateIsRunningState(state)) {
+ return true; // Don't do any updating when we are running
+ }
+ }
+
+ m_min_x = 2;
+ m_min_y = 1;
+ m_max_x = window.GetWidth() - 1;
+ m_max_y = window.GetHeight() - 1;
+
+ window.Erase();
+ window.DrawTitleBox(window.GetName());
+
+ if (display_content) {
+ const int num_visible_rows = NumVisibleRows();
+ m_num_rows = 0;
+ m_root.CalculateRowIndexes(m_num_rows);
+
+ // If we unexpanded while having something selected our total number of
+ // rows is less than the num visible rows, then make sure we show all the
+ // rows by setting the first visible row accordingly.
+ if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
+ m_first_visible_row = 0;
+
+ // Make sure the selected row is always visible
+ if (m_selected_row_idx < m_first_visible_row)
+ m_first_visible_row = m_selected_row_idx;
+ else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
+ m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
+
+ int row_idx = 0;
+ int num_rows_left = num_visible_rows;
+ m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
+ num_rows_left);
+ // Get the selected row
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ } else {
+ m_selected_item = nullptr;
+ }
+
+ window.DeferredRefresh();
+
+ return true; // Drawing handled
+ }
+
+ const char *WindowDelegateGetHelpText() override {
+ return "Thread window keyboard shortcuts:";
+ }
+
+ KeyHelp *WindowDelegateGetKeyHelp() override {
+ static curses::KeyHelp g_source_view_key_help[] = {
+ {KEY_UP, "Select previous item"},
+ {KEY_DOWN, "Select next item"},
+ {KEY_RIGHT, "Expand the selected item"},
+ {KEY_LEFT,
+ "Unexpand the selected item or select parent if not expanded"},
+ {KEY_PPAGE, "Page up"},
+ {KEY_NPAGE, "Page down"},
+ {'h', "Show help dialog"},
+ {' ', "Toggle item expansion"},
+ {',', "Page up"},
+ {'.', "Page down"},
+ {'\0', nullptr}};
+ return g_source_view_key_help;
+ }
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
+ switch (c) {
+ case ',':
+ case KEY_PPAGE:
+ // Page up key
+ if (m_first_visible_row > 0) {
+ if (m_first_visible_row > m_max_y)
+ m_first_visible_row -= m_max_y;
+ else
+ m_first_visible_row = 0;
+ m_selected_row_idx = m_first_visible_row;
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ if (m_selected_item)
+ m_selected_item->ItemWasSelected();
+ }
+ return eKeyHandled;
+
+ case '.':
+ case KEY_NPAGE:
+ // Page down key
+ if (m_num_rows > m_max_y) {
+ if (m_first_visible_row + m_max_y < m_num_rows) {
+ m_first_visible_row += m_max_y;
+ m_selected_row_idx = m_first_visible_row;
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ if (m_selected_item)
+ m_selected_item->ItemWasSelected();
+ }
+ }
+ return eKeyHandled;
+
+ case KEY_UP:
+ if (m_selected_row_idx > 0) {
+ --m_selected_row_idx;
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ if (m_selected_item)
+ m_selected_item->ItemWasSelected();
+ }
+ return eKeyHandled;
+
+ case KEY_DOWN:
+ if (m_selected_row_idx + 1 < m_num_rows) {
+ ++m_selected_row_idx;
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ if (m_selected_item)
+ m_selected_item->ItemWasSelected();
+ }
+ return eKeyHandled;
+
+ case KEY_RIGHT:
+ if (m_selected_item) {
+ if (!m_selected_item->IsExpanded())
+ m_selected_item->Expand();
+ }
+ return eKeyHandled;
+
+ case KEY_LEFT:
+ if (m_selected_item) {
+ if (m_selected_item->IsExpanded())
+ m_selected_item->Unexpand();
+ else if (m_selected_item->GetParent()) {
+ m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ if (m_selected_item)
+ m_selected_item->ItemWasSelected();
+ }
+ }
+ return eKeyHandled;
+
+ case ' ':
+ // Toggle expansion state when SPACE is pressed
+ if (m_selected_item) {
+ if (m_selected_item->IsExpanded())
+ m_selected_item->Unexpand();
+ else
+ m_selected_item->Expand();
+ }
+ return eKeyHandled;
+
+ case 'h':
+ window.CreateHelpSubwindow();
+ return eKeyHandled;
+
+ default:
+ break;
+ }
+ return eKeyNotHandled;
+ }
+
+protected:
+ Debugger &m_debugger;
+ TreeDelegateSP m_delegate_sp;
+ TreeItem m_root;
+ TreeItem *m_selected_item;
+ int m_num_rows;
+ int m_selected_row_idx;
+ int m_first_visible_row;
+ int m_min_x;
+ int m_min_y;
+ int m_max_x;
+ int m_max_y;
+};
+
+class FrameTreeDelegate : public TreeDelegate {
+public:
+ FrameTreeDelegate() : TreeDelegate() {
+ FormatEntity::Parse(
+ "frame #${frame.index}: {${function.name}${function.pc-offset}}}",
+ m_format);
+ }
+
+ ~FrameTreeDelegate() override = default;
+
+ void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
+ Thread *thread = (Thread *)item.GetUserData();
+ if (thread) {
+ const uint64_t frame_idx = item.GetIdentifier();
+ StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
+ if (frame_sp) {
+ StreamString strm;
+ const SymbolContext &sc =
+ frame_sp->GetSymbolContext(eSymbolContextEverything);
+ ExecutionContext exe_ctx(frame_sp);
+ if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
+ nullptr, false, false)) {
+ int right_pad = 1;
+ window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
+ }
+ }
+ }
+ }
+
+ void TreeDelegateGenerateChildren(TreeItem &item) override {
+ // No children for frames yet...
+ }
+
+ bool TreeDelegateItemSelected(TreeItem &item) override {
+ Thread *thread = (Thread *)item.GetUserData();
+ if (thread) {
+ thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
+ thread->GetID());
+ const uint64_t frame_idx = item.GetIdentifier();
+ thread->SetSelectedFrameByIndex(frame_idx);
+ return true;
+ }
+ return false;
+ }
+
+protected:
+ FormatEntity::Entry m_format;
+};
+
+class ThreadTreeDelegate : public TreeDelegate {
+public:
+ ThreadTreeDelegate(Debugger &debugger)
+ : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID),
+ m_stop_id(UINT32_MAX) {
+ FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
+ "reason = ${thread.stop-reason}}",
+ m_format);
+ }
+
+ ~ThreadTreeDelegate() override = default;
+
+ ProcessSP GetProcess() {
+ return m_debugger.GetCommandInterpreter()
+ .GetExecutionContext()
+ .GetProcessSP();
+ }
+
+ ThreadSP GetThread(const TreeItem &item) {
+ ProcessSP process_sp = GetProcess();
+ if (process_sp)
+ return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
+ return ThreadSP();
+ }
+
+ void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
+ ThreadSP thread_sp = GetThread(item);
+ if (thread_sp) {
+ StreamString strm;
+ ExecutionContext exe_ctx(thread_sp);
+ if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
+ nullptr, false, false)) {
+ int right_pad = 1;
+ window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
+ }
+ }
+ }
+
+ void TreeDelegateGenerateChildren(TreeItem &item) override {
+ ProcessSP process_sp = GetProcess();
+ if (process_sp && process_sp->IsAlive()) {
+ StateType state = process_sp->GetState();
+ if (StateIsStoppedState(state, true)) {
+ ThreadSP thread_sp = GetThread(item);
+ if (thread_sp) {
+ if (m_stop_id == process_sp->GetStopID() &&
+ thread_sp->GetID() == m_tid)
+ return; // Children are already up to date
+ if (!m_frame_delegate_sp) {
+ // Always expand the thread item the first time we show it
+ m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
+ }
+
+ m_stop_id = process_sp->GetStopID();
+ m_tid = thread_sp->GetID();
+
+ TreeItem t(&item, *m_frame_delegate_sp, false);
+ size_t num_frames = thread_sp->GetStackFrameCount();
+ item.Resize(num_frames, t);
+ for (size_t i = 0; i < num_frames; ++i) {
+ item[i].SetUserData(thread_sp.get());
+ item[i].SetIdentifier(i);
+ }
+ }
+ return;
+ }
+ }
+ item.ClearChildren();
+ }
+
+ bool TreeDelegateItemSelected(TreeItem &item) override {
+ ProcessSP process_sp = GetProcess();
+ if (process_sp && process_sp->IsAlive()) {
+ StateType state = process_sp->GetState();
+ if (StateIsStoppedState(state, true)) {
+ ThreadSP thread_sp = GetThread(item);
+ if (thread_sp) {
+ ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
+ std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
+ ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
+ if (selected_thread_sp->GetID() != thread_sp->GetID()) {
+ thread_list.SetSelectedThreadByID(thread_sp->GetID());
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+protected:
+ Debugger &m_debugger;
+ std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
+ lldb::user_id_t m_tid;
+ uint32_t m_stop_id;
+ FormatEntity::Entry m_format;
+};
+
+class ThreadsTreeDelegate : public TreeDelegate {
+public:
+ ThreadsTreeDelegate(Debugger &debugger)
+ : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger),
+ m_stop_id(UINT32_MAX) {
+ FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
+ m_format);
+ }
+
+ ~ThreadsTreeDelegate() override = default;
+
+ ProcessSP GetProcess() {
+ return m_debugger.GetCommandInterpreter()
+ .GetExecutionContext()
+ .GetProcessSP();
+ }
+
+ void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
+ ProcessSP process_sp = GetProcess();
+ if (process_sp && process_sp->IsAlive()) {
+ StreamString strm;
+ ExecutionContext exe_ctx(process_sp);
+ if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
+ nullptr, false, false)) {
+ int right_pad = 1;
+ window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
+ }
+ }
+ }
+
+ void TreeDelegateGenerateChildren(TreeItem &item) override {
+ ProcessSP process_sp = GetProcess();
+ if (process_sp && process_sp->IsAlive()) {
+ StateType state = process_sp->GetState();
+ if (StateIsStoppedState(state, true)) {
+ const uint32_t stop_id = process_sp->GetStopID();
+ if (m_stop_id == stop_id)
+ return; // Children are already up to date
+
+ m_stop_id = stop_id;
+
+ if (!m_thread_delegate_sp) {
+ // Always expand the thread item the first time we show it
+ // item.Expand();
+ m_thread_delegate_sp =
+ std::make_shared<ThreadTreeDelegate>(m_debugger);
+ }
+
+ TreeItem t(&item, *m_thread_delegate_sp, false);
+ ThreadList &threads = process_sp->GetThreadList();
+ std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
+ size_t num_threads = threads.GetSize();
+ item.Resize(num_threads, t);
+ for (size_t i = 0; i < num_threads; ++i) {
+ item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
+ item[i].SetMightHaveChildren(true);
+ }
+ return;
+ }
+ }
+ item.ClearChildren();
+ }
+
+ bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
+
+protected:
+ std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
+ Debugger &m_debugger;
+ uint32_t m_stop_id;
+ FormatEntity::Entry m_format;
+};
+
+class ValueObjectListDelegate : public WindowDelegate {
+public:
+ ValueObjectListDelegate()
+ : m_rows(), m_selected_row(nullptr),
+ m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0),
+ m_max_x(0), m_max_y(0) {}
+
+ ValueObjectListDelegate(ValueObjectList &valobj_list)
+ : m_rows(), m_selected_row(nullptr),
+ m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0),
+ m_max_x(0), m_max_y(0) {
+ SetValues(valobj_list);
+ }
+
+ ~ValueObjectListDelegate() override = default;
+
+ void SetValues(ValueObjectList &valobj_list) {
+ m_selected_row = nullptr;
+ m_selected_row_idx = 0;
+ m_first_visible_row = 0;
+ m_num_rows = 0;
+ m_rows.clear();
+ for (auto &valobj_sp : valobj_list.GetObjects())
+ m_rows.push_back(Row(valobj_sp, nullptr));
+ }
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ m_num_rows = 0;
+ m_min_x = 2;
+ m_min_y = 1;
+ m_max_x = window.GetWidth() - 1;
+ m_max_y = window.GetHeight() - 1;
+
+ window.Erase();
+ window.DrawTitleBox(window.GetName());
+
+ const int num_visible_rows = NumVisibleRows();
+ const int num_rows = CalculateTotalNumberRows(m_rows);
+
+ // If we unexpanded while having something selected our total number of
+ // rows is less than the num visible rows, then make sure we show all the
+ // rows by setting the first visible row accordingly.
+ if (m_first_visible_row > 0 && num_rows < num_visible_rows)
+ m_first_visible_row = 0;
+
+ // Make sure the selected row is always visible
+ if (m_selected_row_idx < m_first_visible_row)
+ m_first_visible_row = m_selected_row_idx;
+ else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
+ m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
+
+ DisplayRows(window, m_rows, g_options);
+
+ window.DeferredRefresh();
+
+ // Get the selected row
+ m_selected_row = GetRowForRowIndex(m_selected_row_idx);
+ // Keep the cursor on the selected row so the highlight and the cursor are
+ // always on the same line
+ if (m_selected_row)
+ window.MoveCursor(m_selected_row->x, m_selected_row->y);
+
+ return true; // Drawing handled
+ }
+
+ KeyHelp *WindowDelegateGetKeyHelp() override {
+ static curses::KeyHelp g_source_view_key_help[] = {
+ {KEY_UP, "Select previous item"},
+ {KEY_DOWN, "Select next item"},
+ {KEY_RIGHT, "Expand selected item"},
+ {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
+ {KEY_PPAGE, "Page up"},
+ {KEY_NPAGE, "Page down"},
+ {'A', "Format as annotated address"},
+ {'b', "Format as binary"},
+ {'B', "Format as hex bytes with ASCII"},
+ {'c', "Format as character"},
+ {'d', "Format as a signed integer"},
+ {'D', "Format selected value using the default format for the type"},
+ {'f', "Format as float"},
+ {'h', "Show help dialog"},
+ {'i', "Format as instructions"},
+ {'o', "Format as octal"},
+ {'p', "Format as pointer"},
+ {'s', "Format as C string"},
+ {'t', "Toggle showing/hiding type names"},
+ {'u', "Format as an unsigned integer"},
+ {'x', "Format as hex"},
+ {'X', "Format as uppercase hex"},
+ {' ', "Toggle item expansion"},
+ {',', "Page up"},
+ {'.', "Page down"},
+ {'\0', nullptr}};
+ return g_source_view_key_help;
+ }
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
+ switch (c) {
+ case 'x':
+ case 'X':
+ case 'o':
+ case 's':
+ case 'u':
+ case 'd':
+ case 'D':
+ case 'i':
+ case 'A':
+ case 'p':
+ case 'c':
+ case 'b':
+ case 'B':
+ case 'f':
+ // Change the format for the currently selected item
+ if (m_selected_row) {
+ auto valobj_sp = m_selected_row->value.GetSP();
+ if (valobj_sp)
+ valobj_sp->SetFormat(FormatForChar(c));
+ }
+ return eKeyHandled;
+
+ case 't':
+ // Toggle showing type names
+ g_options.show_types = !g_options.show_types;
+ return eKeyHandled;
+
+ case ',':
+ case KEY_PPAGE:
+ // Page up key
+ if (m_first_visible_row > 0) {
+ if (static_cast<int>(m_first_visible_row) > m_max_y)
+ m_first_visible_row -= m_max_y;
+ else
+ m_first_visible_row = 0;
+ m_selected_row_idx = m_first_visible_row;
+ }
+ return eKeyHandled;
+
+ case '.':
+ case KEY_NPAGE:
+ // Page down key
+ if (m_num_rows > static_cast<size_t>(m_max_y)) {
+ if (m_first_visible_row + m_max_y < m_num_rows) {
+ m_first_visible_row += m_max_y;
+ m_selected_row_idx = m_first_visible_row;
+ }
+ }
+ return eKeyHandled;
+
+ case KEY_UP:
+ if (m_selected_row_idx > 0)
+ --m_selected_row_idx;
+ return eKeyHandled;
+
+ case KEY_DOWN:
+ if (m_selected_row_idx + 1 < m_num_rows)
+ ++m_selected_row_idx;
+ return eKeyHandled;
+
+ case KEY_RIGHT:
+ if (m_selected_row) {
+ if (!m_selected_row->expanded)
+ m_selected_row->Expand();
+ }
+ return eKeyHandled;
+
+ case KEY_LEFT:
+ if (m_selected_row) {
+ if (m_selected_row->expanded)
+ m_selected_row->Unexpand();
+ else if (m_selected_row->parent)
+ m_selected_row_idx = m_selected_row->parent->row_idx;
+ }
+ return eKeyHandled;
+
+ case ' ':
+ // Toggle expansion state when SPACE is pressed
+ if (m_selected_row) {
+ if (m_selected_row->expanded)
+ m_selected_row->Unexpand();
+ else
+ m_selected_row->Expand();
+ }
+ return eKeyHandled;
+
+ case 'h':
+ window.CreateHelpSubwindow();
+ return eKeyHandled;
+
+ default:
+ break;
+ }
+ return eKeyNotHandled;
+ }
+
+protected:
+ std::vector<Row> m_rows;
+ Row *m_selected_row;
+ uint32_t m_selected_row_idx;
+ uint32_t m_first_visible_row;
+ uint32_t m_num_rows;
+ int m_min_x;
+ int m_min_y;
+ int m_max_x;
+ int m_max_y;
+
+ static Format FormatForChar(int c) {
+ switch (c) {
+ case 'x':
+ return eFormatHex;
+ case 'X':
+ return eFormatHexUppercase;
+ case 'o':
+ return eFormatOctal;
+ case 's':
+ return eFormatCString;
+ case 'u':
+ return eFormatUnsigned;
+ case 'd':
+ return eFormatDecimal;
+ case 'D':
+ return eFormatDefault;
+ case 'i':
+ return eFormatInstruction;
+ case 'A':
+ return eFormatAddressInfo;
+ case 'p':
+ return eFormatPointer;
+ case 'c':
+ return eFormatChar;
+ case 'b':
+ return eFormatBinary;
+ case 'B':
+ return eFormatBytesWithASCII;
+ case 'f':
+ return eFormatFloat;
+ }
+ return eFormatDefault;
+ }
+
+ bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
+ bool highlight, bool last_child) {
+ ValueObject *valobj = row.value.GetSP().get();
+
+ if (valobj == nullptr)
+ return false;
+
+ const char *type_name =
+ options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
+ const char *name = valobj->GetName().GetCString();
+ const char *value = valobj->GetValueAsCString();
+ const char *summary = valobj->GetSummaryAsCString();
+
+ window.MoveCursor(row.x, row.y);
+
+ row.DrawTree(window);
+
+ if (highlight)
+ window.AttributeOn(A_REVERSE);
+
+ if (type_name && type_name[0])
+ window.Printf("(%s) ", type_name);
+
+ if (name && name[0])
+ window.PutCString(name);
+
+ attr_t changd_attr = 0;
+ if (valobj->GetValueDidChange())
+ changd_attr = COLOR_PAIR(5) | A_BOLD;
+
+ if (value && value[0]) {
+ window.PutCString(" = ");
+ if (changd_attr)
+ window.AttributeOn(changd_attr);
+ window.PutCString(value);
+ if (changd_attr)
+ window.AttributeOff(changd_attr);
+ }
+
+ if (summary && summary[0]) {
+ window.PutChar(' ');
+ if (changd_attr)
+ window.AttributeOn(changd_attr);
+ window.PutCString(summary);
+ if (changd_attr)
+ window.AttributeOff(changd_attr);
+ }
+
+ if (highlight)
+ window.AttributeOff(A_REVERSE);
+
+ return true;
+ }
+
+ void DisplayRows(Window &window, std::vector<Row> &rows,
+ DisplayOptions &options) {
+ // > 0x25B7
+ // \/ 0x25BD
+
+ bool window_is_active = window.IsActive();
+ for (auto &row : rows) {
+ const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
+ // Save the row index in each Row structure
+ row.row_idx = m_num_rows;
+ if ((m_num_rows >= m_first_visible_row) &&
+ ((m_num_rows - m_first_visible_row) <
+ static_cast<size_t>(NumVisibleRows()))) {
+ row.x = m_min_x;
+ row.y = m_num_rows - m_first_visible_row + 1;
+ if (DisplayRowObject(window, row, options,
+ window_is_active &&
+ m_num_rows == m_selected_row_idx,
+ last_child)) {
+ ++m_num_rows;
+ } else {
+ row.x = 0;
+ row.y = 0;
+ }
+ } else {
+ row.x = 0;
+ row.y = 0;
+ ++m_num_rows;
+ }
+
+ auto &children = row.GetChildren();
+ if (row.expanded && !children.empty()) {
+ DisplayRows(window, children, options);
+ }
+ }
+ }
+
+ int CalculateTotalNumberRows(std::vector<Row> &rows) {
+ int row_count = 0;
+ for (auto &row : rows) {
+ ++row_count;
+ if (row.expanded)
+ row_count += CalculateTotalNumberRows(row.GetChildren());
+ }
+ return row_count;
+ }
+
+ static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
+ for (auto &row : rows) {
+ if (row_index == 0)
+ return &row;
+ else {
+ --row_index;
+ auto &children = row.GetChildren();
+ if (row.expanded && !children.empty()) {
+ Row *result = GetRowForRowIndexImpl(children, row_index);
+ if (result)
+ return result;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ Row *GetRowForRowIndex(size_t row_index) {
+ return GetRowForRowIndexImpl(m_rows, row_index);
+ }
+
+ int NumVisibleRows() const { return m_max_y - m_min_y; }
+
+ static DisplayOptions g_options;
+};
+
+class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
+public:
+ FrameVariablesWindowDelegate(Debugger &debugger)
+ : ValueObjectListDelegate(), m_debugger(debugger),
+ m_frame_block(nullptr) {}
+
+ ~FrameVariablesWindowDelegate() override = default;
+
+ const char *WindowDelegateGetHelpText() override {
+ return "Frame variable window keyboard shortcuts:";
+ }
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ ExecutionContext exe_ctx(
+ m_debugger.GetCommandInterpreter().GetExecutionContext());
+ Process *process = exe_ctx.GetProcessPtr();
+ Block *frame_block = nullptr;
+ StackFrame *frame = nullptr;
+
+ if (process) {
+ StateType state = process->GetState();
+ if (StateIsStoppedState(state, true)) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame)
+ frame_block = frame->GetFrameBlock();
+ } else if (StateIsRunningState(state)) {
+ return true; // Don't do any updating when we are running
+ }
+ }
+
+ ValueObjectList local_values;
+ if (frame_block) {
+ // Only update the variables if they have changed
+ if (m_frame_block != frame_block) {
+ m_frame_block = frame_block;
+
+ VariableList *locals = frame->GetVariableList(true);
+ if (locals) {
+ const DynamicValueType use_dynamic = eDynamicDontRunTarget;
+ const size_t num_locals = locals->GetSize();
+ for (size_t i = 0; i < num_locals; ++i) {
+ ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable(
+ locals->GetVariableAtIndex(i), use_dynamic);
+ if (value_sp) {
+ ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
+ if (synthetic_value_sp)
+ local_values.Append(synthetic_value_sp);
+ else
+ local_values.Append(value_sp);
+ }
+ }
+ // Update the values
+ SetValues(local_values);
+ }
+ }
+ } else {
+ m_frame_block = nullptr;
+ // Update the values with an empty list if there is no frame
+ SetValues(local_values);
+ }
+
+ return ValueObjectListDelegate::WindowDelegateDraw(window, force);
+ }
+
+protected:
+ Debugger &m_debugger;
+ Block *m_frame_block;
+};
+
+class RegistersWindowDelegate : public ValueObjectListDelegate {
+public:
+ RegistersWindowDelegate(Debugger &debugger)
+ : ValueObjectListDelegate(), m_debugger(debugger) {}
+
+ ~RegistersWindowDelegate() override = default;
+
+ const char *WindowDelegateGetHelpText() override {
+ return "Register window keyboard shortcuts:";
+ }
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ ExecutionContext exe_ctx(
+ m_debugger.GetCommandInterpreter().GetExecutionContext());
+ StackFrame *frame = exe_ctx.GetFramePtr();
+
+ ValueObjectList value_list;
+ if (frame) {
+ if (frame->GetStackID() != m_stack_id) {
+ m_stack_id = frame->GetStackID();
+ RegisterContextSP reg_ctx(frame->GetRegisterContext());
+ if (reg_ctx) {
+ const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
+ for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
+ value_list.Append(
+ ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
+ }
+ }
+ SetValues(value_list);
+ }
+ } else {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive())
+ return true; // Don't do any updating if we are running
+ else {
+ // Update the values with an empty list if there is no process or the
+ // process isn't alive anymore
+ SetValues(value_list);
+ }
+ }
+ return ValueObjectListDelegate::WindowDelegateDraw(window, force);
+ }
+
+protected:
+ Debugger &m_debugger;
+ StackID m_stack_id;
+};
+
+static const char *CursesKeyToCString(int ch) {
+ static char g_desc[32];
+ if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
+ snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
+ return g_desc;
+ }
+ switch (ch) {
+ case KEY_DOWN:
+ return "down";
+ case KEY_UP:
+ return "up";
+ case KEY_LEFT:
+ return "left";
+ case KEY_RIGHT:
+ return "right";
+ case KEY_HOME:
+ return "home";
+ case KEY_BACKSPACE:
+ return "backspace";
+ case KEY_DL:
+ return "delete-line";
+ case KEY_IL:
+ return "insert-line";
+ case KEY_DC:
+ return "delete-char";
+ case KEY_IC:
+ return "insert-char";
+ case KEY_CLEAR:
+ return "clear";
+ case KEY_EOS:
+ return "clear-to-eos";
+ case KEY_EOL:
+ return "clear-to-eol";
+ case KEY_SF:
+ return "scroll-forward";
+ case KEY_SR:
+ return "scroll-backward";
+ case KEY_NPAGE:
+ return "page-down";
+ case KEY_PPAGE:
+ return "page-up";
+ case KEY_STAB:
+ return "set-tab";
+ case KEY_CTAB:
+ return "clear-tab";
+ case KEY_CATAB:
+ return "clear-all-tabs";
+ case KEY_ENTER:
+ return "enter";
+ case KEY_PRINT:
+ return "print";
+ case KEY_LL:
+ return "lower-left key";
+ case KEY_A1:
+ return "upper left of keypad";
+ case KEY_A3:
+ return "upper right of keypad";
+ case KEY_B2:
+ return "center of keypad";
+ case KEY_C1:
+ return "lower left of keypad";
+ case KEY_C3:
+ return "lower right of keypad";
+ case KEY_BTAB:
+ return "back-tab key";
+ case KEY_BEG:
+ return "begin key";
+ case KEY_CANCEL:
+ return "cancel key";
+ case KEY_CLOSE:
+ return "close key";
+ case KEY_COMMAND:
+ return "command key";
+ case KEY_COPY:
+ return "copy key";
+ case KEY_CREATE:
+ return "create key";
+ case KEY_END:
+ return "end key";
+ case KEY_EXIT:
+ return "exit key";
+ case KEY_FIND:
+ return "find key";
+ case KEY_HELP:
+ return "help key";
+ case KEY_MARK:
+ return "mark key";
+ case KEY_MESSAGE:
+ return "message key";
+ case KEY_MOVE:
+ return "move key";
+ case KEY_NEXT:
+ return "next key";
+ case KEY_OPEN:
+ return "open key";
+ case KEY_OPTIONS:
+ return "options key";
+ case KEY_PREVIOUS:
+ return "previous key";
+ case KEY_REDO:
+ return "redo key";
+ case KEY_REFERENCE:
+ return "reference key";
+ case KEY_REFRESH:
+ return "refresh key";
+ case KEY_REPLACE:
+ return "replace key";
+ case KEY_RESTART:
+ return "restart key";
+ case KEY_RESUME:
+ return "resume key";
+ case KEY_SAVE:
+ return "save key";
+ case KEY_SBEG:
+ return "shifted begin key";
+ case KEY_SCANCEL:
+ return "shifted cancel key";
+ case KEY_SCOMMAND:
+ return "shifted command key";
+ case KEY_SCOPY:
+ return "shifted copy key";
+ case KEY_SCREATE:
+ return "shifted create key";
+ case KEY_SDC:
+ return "shifted delete-character key";
+ case KEY_SDL:
+ return "shifted delete-line key";
+ case KEY_SELECT:
+ return "select key";
+ case KEY_SEND:
+ return "shifted end key";
+ case KEY_SEOL:
+ return "shifted clear-to-end-of-line key";
+ case KEY_SEXIT:
+ return "shifted exit key";
+ case KEY_SFIND:
+ return "shifted find key";
+ case KEY_SHELP:
+ return "shifted help key";
+ case KEY_SHOME:
+ return "shifted home key";
+ case KEY_SIC:
+ return "shifted insert-character key";
+ case KEY_SLEFT:
+ return "shifted left-arrow key";
+ case KEY_SMESSAGE:
+ return "shifted message key";
+ case KEY_SMOVE:
+ return "shifted move key";
+ case KEY_SNEXT:
+ return "shifted next key";
+ case KEY_SOPTIONS:
+ return "shifted options key";
+ case KEY_SPREVIOUS:
+ return "shifted previous key";
+ case KEY_SPRINT:
+ return "shifted print key";
+ case KEY_SREDO:
+ return "shifted redo key";
+ case KEY_SREPLACE:
+ return "shifted replace key";
+ case KEY_SRIGHT:
+ return "shifted right-arrow key";
+ case KEY_SRSUME:
+ return "shifted resume key";
+ case KEY_SSAVE:
+ return "shifted save key";
+ case KEY_SSUSPEND:
+ return "shifted suspend key";
+ case KEY_SUNDO:
+ return "shifted undo key";
+ case KEY_SUSPEND:
+ return "suspend key";
+ case KEY_UNDO:
+ return "undo key";
+ case KEY_MOUSE:
+ return "Mouse event has occurred";
+ case KEY_RESIZE:
+ return "Terminal resize event";
+#ifdef KEY_EVENT
+ case KEY_EVENT:
+ return "We were interrupted by an event";
+#endif
+ case KEY_RETURN:
+ return "return";
+ case ' ':
+ return "space";
+ case '\t':
+ return "tab";
+ case KEY_ESCAPE:
+ return "escape";
+ default:
+ if (isprint(ch))
+ snprintf(g_desc, sizeof(g_desc), "%c", ch);
+ else
+ snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
+ return g_desc;
+ }
+ return nullptr;
+}
+
+HelpDialogDelegate::HelpDialogDelegate(const char *text,
+ KeyHelp *key_help_array)
+ : m_text(), m_first_visible_line(0) {
+ if (text && text[0]) {
+ m_text.SplitIntoLines(text);
+ m_text.AppendString("");
+ }
+ if (key_help_array) {
+ for (KeyHelp *key = key_help_array; key->ch; ++key) {
+ StreamString key_description;
+ key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
+ key->description);
+ m_text.AppendString(key_description.GetString());
+ }
+ }
+}
+
+HelpDialogDelegate::~HelpDialogDelegate() = default;
+
+bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
+ window.Erase();
+ const int window_height = window.GetHeight();
+ int x = 2;
+ int y = 1;
+ const int min_y = y;
+ const int max_y = window_height - 1 - y;
+ const size_t num_visible_lines = max_y - min_y + 1;
+ const size_t num_lines = m_text.GetSize();
+ const char *bottom_message;
+ if (num_lines <= num_visible_lines)
+ bottom_message = "Press any key to exit";
+ else
+ bottom_message = "Use arrows to scroll, any other key to exit";
+ window.DrawTitleBox(window.GetName(), bottom_message);
+ while (y <= max_y) {
+ window.MoveCursor(x, y);
+ window.PutCStringTruncated(
+ m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
+ ++y;
+ }
+ return true;
+}
+
+HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
+ int key) {
+ bool done = false;
+ const size_t num_lines = m_text.GetSize();
+ const size_t num_visible_lines = window.GetHeight() - 2;
+
+ if (num_lines <= num_visible_lines) {
+ done = true;
+ // If we have all lines visible and don't need scrolling, then any key
+ // press will cause us to exit
+ } else {
+ switch (key) {
+ case KEY_UP:
+ if (m_first_visible_line > 0)
+ --m_first_visible_line;
+ break;
+
+ case KEY_DOWN:
+ if (m_first_visible_line + num_visible_lines < num_lines)
+ ++m_first_visible_line;
+ break;
+
+ case KEY_PPAGE:
+ case ',':
+ if (m_first_visible_line > 0) {
+ if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
+ m_first_visible_line -= num_visible_lines;
+ else
+ m_first_visible_line = 0;
+ }
+ break;
+
+ case KEY_NPAGE:
+ case '.':
+ if (m_first_visible_line + num_visible_lines < num_lines) {
+ m_first_visible_line += num_visible_lines;
+ if (static_cast<size_t>(m_first_visible_line) > num_lines)
+ m_first_visible_line = num_lines - num_visible_lines;
+ }
+ break;
+
+ default:
+ done = true;
+ break;
+ }
+ }
+ if (done)
+ window.GetParent()->RemoveSubWindow(&window);
+ return eKeyHandled;
+}
+
+class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
+public:
+ enum {
+ eMenuID_LLDB = 1,
+ eMenuID_LLDBAbout,
+ eMenuID_LLDBExit,
+
+ eMenuID_Target,
+ eMenuID_TargetCreate,
+ eMenuID_TargetDelete,
+
+ eMenuID_Process,
+ eMenuID_ProcessAttach,
+ eMenuID_ProcessDetach,
+ eMenuID_ProcessLaunch,
+ eMenuID_ProcessContinue,
+ eMenuID_ProcessHalt,
+ eMenuID_ProcessKill,
+
+ eMenuID_Thread,
+ eMenuID_ThreadStepIn,
+ eMenuID_ThreadStepOver,
+ eMenuID_ThreadStepOut,
+
+ eMenuID_View,
+ eMenuID_ViewBacktrace,
+ eMenuID_ViewRegisters,
+ eMenuID_ViewSource,
+ eMenuID_ViewVariables,
+
+ eMenuID_Help,
+ eMenuID_HelpGUIHelp
+ };
+
+ ApplicationDelegate(Application &app, Debugger &debugger)
+ : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
+
+ ~ApplicationDelegate() override = default;
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ return false; // Drawing not handled, let standard window drawing happen
+ }
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
+ switch (key) {
+ case '\t':
+ window.SelectNextWindowAsActive();
+ return eKeyHandled;
+
+ case 'h':
+ window.CreateHelpSubwindow();
+ return eKeyHandled;
+
+ case KEY_ESCAPE:
+ return eQuitApplication;
+
+ default:
+ break;
+ }
+ return eKeyNotHandled;
+ }
+
+ const char *WindowDelegateGetHelpText() override {
+ return "Welcome to the LLDB curses GUI.\n\n"
+ "Press the TAB key to change the selected view.\n"
+ "Each view has its own keyboard shortcuts, press 'h' to open a "
+ "dialog to display them.\n\n"
+ "Common key bindings for all views:";
+ }
+
+ KeyHelp *WindowDelegateGetKeyHelp() override {
+ static curses::KeyHelp g_source_view_key_help[] = {
+ {'\t', "Select next view"},
+ {'h', "Show help dialog with view specific key bindings"},
+ {',', "Page up"},
+ {'.', "Page down"},
+ {KEY_UP, "Select previous"},
+ {KEY_DOWN, "Select next"},
+ {KEY_LEFT, "Unexpand or select parent"},
+ {KEY_RIGHT, "Expand"},
+ {KEY_PPAGE, "Page up"},
+ {KEY_NPAGE, "Page down"},
+ {'\0', nullptr}};
+ return g_source_view_key_help;
+ }
+
+ MenuActionResult MenuDelegateAction(Menu &menu) override {
+ switch (menu.GetIdentifier()) {
+ case eMenuID_ThreadStepIn: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive() &&
+ StateIsStoppedState(process->GetState(), true))
+ exe_ctx.GetThreadRef().StepIn(true);
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ThreadStepOut: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive() &&
+ StateIsStoppedState(process->GetState(), true))
+ exe_ctx.GetThreadRef().StepOut();
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ThreadStepOver: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive() &&
+ StateIsStoppedState(process->GetState(), true))
+ exe_ctx.GetThreadRef().StepOver(true);
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ProcessContinue: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive() &&
+ StateIsStoppedState(process->GetState(), true))
+ process->Resume();
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ProcessKill: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive())
+ process->Destroy(false);
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ProcessHalt: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive())
+ process->Halt();
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ProcessDetach: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive())
+ process->Detach(false);
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_Process: {
+ // Populate the menu with all of the threads if the process is stopped
+ // when the Process menu gets selected and is about to display its
+ // submenu.
+ Menus &submenus = menu.GetSubmenus();
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive() &&
+ StateIsStoppedState(process->GetState(), true)) {
+ if (submenus.size() == 7)
+ menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
+ else if (submenus.size() > 8)
+ submenus.erase(submenus.begin() + 8, submenus.end());
+
+ ThreadList &threads = process->GetThreadList();
+ std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
+ size_t num_threads = threads.GetSize();
+ for (size_t i = 0; i < num_threads; ++i) {
+ ThreadSP thread_sp = threads.GetThreadAtIndex(i);
+ char menu_char = '\0';
+ if (i < 9)
+ menu_char = '1' + i;
+ StreamString thread_menu_title;
+ thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
+ const char *thread_name = thread_sp->GetName();
+ if (thread_name && thread_name[0])
+ thread_menu_title.Printf(" %s", thread_name);
+ else {
+ const char *queue_name = thread_sp->GetQueueName();
+ if (queue_name && queue_name[0])
+ thread_menu_title.Printf(" %s", queue_name);
+ }
+ menu.AddSubmenu(
+ MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
+ nullptr, menu_char, thread_sp->GetID())));
+ }
+ } else if (submenus.size() > 7) {
+ // Remove the separator and any other thread submenu items that were
+ // previously added
+ submenus.erase(submenus.begin() + 7, submenus.end());
+ }
+ // Since we are adding and removing items we need to recalculate the name
+ // lengths
+ menu.RecalculateNameLengths();
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ViewVariables: {
+ WindowSP main_window_sp = m_app.GetMainWindow();
+ WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
+ WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
+ WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
+ const Rect source_bounds = source_window_sp->GetBounds();
+
+ if (variables_window_sp) {
+ const Rect variables_bounds = variables_window_sp->GetBounds();
+
+ main_window_sp->RemoveSubWindow(variables_window_sp.get());
+
+ if (registers_window_sp) {
+ // We have a registers window, so give all the area back to the
+ // registers window
+ Rect registers_bounds = variables_bounds;
+ registers_bounds.size.width = source_bounds.size.width;
+ registers_window_sp->SetBounds(registers_bounds);
+ } else {
+ // We have no registers window showing so give the bottom area back
+ // to the source view
+ source_window_sp->Resize(source_bounds.size.width,
+ source_bounds.size.height +
+ variables_bounds.size.height);
+ }
+ } else {
+ Rect new_variables_rect;
+ if (registers_window_sp) {
+ // We have a registers window so split the area of the registers
+ // window into two columns where the left hand side will be the
+ // variables and the right hand side will be the registers
+ const Rect variables_bounds = registers_window_sp->GetBounds();
+ Rect new_registers_rect;
+ variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
+ new_registers_rect);
+ registers_window_sp->SetBounds(new_registers_rect);
+ } else {
+ // No variables window, grab the bottom part of the source window
+ Rect new_source_rect;
+ source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
+ new_variables_rect);
+ source_window_sp->SetBounds(new_source_rect);
+ }
+ WindowSP new_window_sp = main_window_sp->CreateSubWindow(
+ "Variables", new_variables_rect, false);
+ new_window_sp->SetDelegate(
+ WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
+ }
+ touchwin(stdscr);
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ViewRegisters: {
+ WindowSP main_window_sp = m_app.GetMainWindow();
+ WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
+ WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
+ WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
+ const Rect source_bounds = source_window_sp->GetBounds();
+
+ if (registers_window_sp) {
+ if (variables_window_sp) {
+ const Rect variables_bounds = variables_window_sp->GetBounds();
+
+ // We have a variables window, so give all the area back to the
+ // variables window
+ variables_window_sp->Resize(variables_bounds.size.width +
+ registers_window_sp->GetWidth(),
+ variables_bounds.size.height);
+ } else {
+ // We have no variables window showing so give the bottom area back
+ // to the source view
+ source_window_sp->Resize(source_bounds.size.width,
+ source_bounds.size.height +
+ registers_window_sp->GetHeight());
+ }
+ main_window_sp->RemoveSubWindow(registers_window_sp.get());
+ } else {
+ Rect new_regs_rect;
+ if (variables_window_sp) {
+ // We have a variables window, split it into two columns where the
+ // left hand side will be the variables and the right hand side will
+ // be the registers
+ const Rect variables_bounds = variables_window_sp->GetBounds();
+ Rect new_vars_rect;
+ variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
+ new_regs_rect);
+ variables_window_sp->SetBounds(new_vars_rect);
+ } else {
+ // No registers window, grab the bottom part of the source window
+ Rect new_source_rect;
+ source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
+ new_regs_rect);
+ source_window_sp->SetBounds(new_source_rect);
+ }
+ WindowSP new_window_sp =
+ main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
+ new_window_sp->SetDelegate(
+ WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
+ }
+ touchwin(stdscr);
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_HelpGUIHelp:
+ m_app.GetMainWindow()->CreateHelpSubwindow();
+ return MenuActionResult::Handled;
+
+ default:
+ break;
+ }
+
+ return MenuActionResult::NotHandled;
+ }
+
+protected:
+ Application &m_app;
+ Debugger &m_debugger;
+};
+
+class StatusBarWindowDelegate : public WindowDelegate {
+public:
+ StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
+ FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
+ }
+
+ ~StatusBarWindowDelegate() override = default;
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ window.Erase();
+ window.SetBackground(2);
+ window.MoveCursor(0, 0);
+ if (process) {
+ const StateType state = process->GetState();
+ window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
+ StateAsCString(state));
+
+ if (StateIsStoppedState(state, true)) {
+ StreamString strm;
+ if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
+ nullptr, nullptr, false, false)) {
+ window.MoveCursor(40, 0);
+ window.PutCStringTruncated(strm.GetString().str().c_str(), 1);
+ }
+
+ window.MoveCursor(60, 0);
+ if (frame)
+ window.Printf("Frame: %3u PC = 0x%16.16" PRIx64,
+ frame->GetFrameIndex(),
+ frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
+ exe_ctx.GetTargetPtr()));
+ } else if (state == eStateExited) {
+ const char *exit_desc = process->GetExitDescription();
+ const int exit_status = process->GetExitStatus();
+ if (exit_desc && exit_desc[0])
+ window.Printf(" with status = %i (%s)", exit_status, exit_desc);
+ else
+ window.Printf(" with status = %i", exit_status);
+ }
+ }
+ window.DeferredRefresh();
+ return true;
+ }
+
+protected:
+ Debugger &m_debugger;
+ FormatEntity::Entry m_format;
+};
+
+class SourceFileWindowDelegate : public WindowDelegate {
+public:
+ SourceFileWindowDelegate(Debugger &debugger)
+ : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
+ m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
+ m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0),
+ m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0),
+ m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
+
+ ~SourceFileWindowDelegate() override = default;
+
+ void Update(const SymbolContext &sc) { m_sc = sc; }
+
+ uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
+
+ const char *WindowDelegateGetHelpText() override {
+ return "Source/Disassembly window keyboard shortcuts:";
+ }
+
+ KeyHelp *WindowDelegateGetKeyHelp() override {
+ static curses::KeyHelp g_source_view_key_help[] = {
+ {KEY_RETURN, "Run to selected line with one shot breakpoint"},
+ {KEY_UP, "Select previous source line"},
+ {KEY_DOWN, "Select next source line"},
+ {KEY_PPAGE, "Page up"},
+ {KEY_NPAGE, "Page down"},
+ {'b', "Set breakpoint on selected source/disassembly line"},
+ {'c', "Continue process"},
+ {'d', "Detach and resume process"},
+ {'D', "Detach with process suspended"},
+ {'h', "Show help dialog"},
+ {'k', "Kill process"},
+ {'n', "Step over (source line)"},
+ {'N', "Step over (single instruction)"},
+ {'o', "Step out"},
+ {'s', "Step in (source line)"},
+ {'S', "Step in (single instruction)"},
+ {',', "Page up"},
+ {'.', "Page down"},
+ {'\0', nullptr}};
+ return g_source_view_key_help;
+ }
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = nullptr;
+
+ bool update_location = false;
+ if (process) {
+ StateType state = process->GetState();
+ if (StateIsStoppedState(state, true)) {
+ // We are stopped, so it is ok to
+ update_location = true;
+ }
+ }
+
+ m_min_x = 1;
+ m_min_y = 2;
+ m_max_x = window.GetMaxX() - 1;
+ m_max_y = window.GetMaxY() - 1;
+
+ const uint32_t num_visible_lines = NumVisibleLines();
+ StackFrameSP frame_sp;
+ bool set_selected_line_to_pc = false;
+
+ if (update_location) {
+ const bool process_alive = process ? process->IsAlive() : false;
+ bool thread_changed = false;
+ if (process_alive) {
+ thread = exe_ctx.GetThreadPtr();
+ if (thread) {
+ frame_sp = thread->GetSelectedFrame();
+ auto tid = thread->GetID();
+ thread_changed = tid != m_tid;
+ m_tid = tid;
+ } else {
+ if (m_tid != LLDB_INVALID_THREAD_ID) {
+ thread_changed = true;
+ m_tid = LLDB_INVALID_THREAD_ID;
+ }
+ }
+ }
+ const uint32_t stop_id = process ? process->GetStopID() : 0;
+ const bool stop_id_changed = stop_id != m_stop_id;
+ bool frame_changed = false;
+ m_stop_id = stop_id;
+ m_title.Clear();
+ if (frame_sp) {
+ m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
+ if (m_sc.module_sp) {
+ m_title.Printf(
+ "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
+ ConstString func_name = m_sc.GetFunctionName();
+ if (func_name)
+ m_title.Printf("`%s", func_name.GetCString());
+ }
+ const uint32_t frame_idx = frame_sp->GetFrameIndex();
+ frame_changed = frame_idx != m_frame_idx;
+ m_frame_idx = frame_idx;
+ } else {
+ m_sc.Clear(true);
+ frame_changed = m_frame_idx != UINT32_MAX;
+ m_frame_idx = UINT32_MAX;
+ }
+
+ const bool context_changed =
+ thread_changed || frame_changed || stop_id_changed;
+
+ if (process_alive) {
+ if (m_sc.line_entry.IsValid()) {
+ m_pc_line = m_sc.line_entry.line;
+ if (m_pc_line != UINT32_MAX)
+ --m_pc_line; // Convert to zero based line number...
+ // Update the selected line if the stop ID changed...
+ if (context_changed)
+ m_selected_line = m_pc_line;
+
+ if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) {
+ // Same file, nothing to do, we should either have the lines or not
+ // (source file missing)
+ if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
+ if (m_selected_line >= m_first_visible_line + num_visible_lines)
+ m_first_visible_line = m_selected_line - 10;
+ } else {
+ if (m_selected_line > 10)
+ m_first_visible_line = m_selected_line - 10;
+ else
+ m_first_visible_line = 0;
+ }
+ } else {
+ // File changed, set selected line to the line with the PC
+ m_selected_line = m_pc_line;
+ m_file_sp =
+ m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
+ if (m_file_sp) {
+ const size_t num_lines = m_file_sp->GetNumLines();
+ m_line_width = 1;
+ for (size_t n = num_lines; n >= 10; n = n / 10)
+ ++m_line_width;
+
+ if (num_lines < num_visible_lines ||
+ m_selected_line < num_visible_lines)
+ m_first_visible_line = 0;
+ else
+ m_first_visible_line = m_selected_line - 10;
+ }
+ }
+ } else {
+ m_file_sp.reset();
+ }
+
+ if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
+ // Show disassembly
+ bool prefer_file_cache = false;
+ if (m_sc.function) {
+ if (m_disassembly_scope != m_sc.function) {
+ m_disassembly_scope = m_sc.function;
+ m_disassembly_sp = m_sc.function->GetInstructions(
+ exe_ctx, nullptr, prefer_file_cache);
+ if (m_disassembly_sp) {
+ set_selected_line_to_pc = true;
+ m_disassembly_range = m_sc.function->GetAddressRange();
+ } else {
+ m_disassembly_range.Clear();
+ }
+ } else {
+ set_selected_line_to_pc = context_changed;
+ }
+ } else if (m_sc.symbol) {
+ if (m_disassembly_scope != m_sc.symbol) {
+ m_disassembly_scope = m_sc.symbol;
+ m_disassembly_sp = m_sc.symbol->GetInstructions(
+ exe_ctx, nullptr, prefer_file_cache);
+ if (m_disassembly_sp) {
+ set_selected_line_to_pc = true;
+ m_disassembly_range.GetBaseAddress() =
+ m_sc.symbol->GetAddress();
+ m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
+ } else {
+ m_disassembly_range.Clear();
+ }
+ } else {
+ set_selected_line_to_pc = context_changed;
+ }
+ }
+ }
+ } else {
+ m_pc_line = UINT32_MAX;
+ }
+ }
+
+ const int window_width = window.GetWidth();
+ window.Erase();
+ window.DrawTitleBox("Sources");
+ if (!m_title.GetString().empty()) {
+ window.AttributeOn(A_REVERSE);
+ window.MoveCursor(1, 1);
+ window.PutChar(' ');
+ window.PutCStringTruncated(m_title.GetString().str().c_str(), 1);
+ int x = window.GetCursorX();
+ if (x < window_width - 1) {
+ window.Printf("%*s", window_width - x - 1, "");
+ }
+ window.AttributeOff(A_REVERSE);
+ }
+
+ Target *target = exe_ctx.GetTargetPtr();
+ const size_t num_source_lines = GetNumSourceLines();
+ if (num_source_lines > 0) {
+ // Display source
+ BreakpointLines bp_lines;
+ if (target) {
+ BreakpointList &bp_list = target->GetBreakpointList();
+ const size_t num_bps = bp_list.GetSize();
+ for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
+ BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
+ const size_t num_bps_locs = bp_sp->GetNumLocations();
+ for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
+ BreakpointLocationSP bp_loc_sp =
+ bp_sp->GetLocationAtIndex(bp_loc_idx);
+ LineEntry bp_loc_line_entry;
+ if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
+ bp_loc_line_entry)) {
+ if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
+ bp_lines.insert(bp_loc_line_entry.line);
+ }
+ }
+ }
+ }
+ }
+
+ const attr_t selected_highlight_attr = A_REVERSE;
+ const attr_t pc_highlight_attr = COLOR_PAIR(1);
+
+ for (size_t i = 0; i < num_visible_lines; ++i) {
+ const uint32_t curr_line = m_first_visible_line + i;
+ if (curr_line < num_source_lines) {
+ const int line_y = m_min_y + i;
+ window.MoveCursor(1, line_y);
+ const bool is_pc_line = curr_line == m_pc_line;
+ const bool line_is_selected = m_selected_line == curr_line;
+ // Highlight the line as the PC line first, then if the selected line
+ // isn't the same as the PC line, highlight it differently
+ attr_t highlight_attr = 0;
+ attr_t bp_attr = 0;
+ if (is_pc_line)
+ highlight_attr = pc_highlight_attr;
+ else if (line_is_selected)
+ highlight_attr = selected_highlight_attr;
+
+ if (bp_lines.find(curr_line + 1) != bp_lines.end())
+ bp_attr = COLOR_PAIR(2);
+
+ if (bp_attr)
+ window.AttributeOn(bp_attr);
+
+ window.Printf(" %*u ", m_line_width, curr_line + 1);
+
+ if (bp_attr)
+ window.AttributeOff(bp_attr);
+
+ window.PutChar(ACS_VLINE);
+ // Mark the line with the PC with a diamond
+ if (is_pc_line)
+ window.PutChar(ACS_DIAMOND);
+ else
+ window.PutChar(' ');
+
+ if (highlight_attr)
+ window.AttributeOn(highlight_attr);
+ const uint32_t line_len =
+ m_file_sp->GetLineLength(curr_line + 1, false);
+ if (line_len > 0)
+ window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
+
+ if (is_pc_line && frame_sp &&
+ frame_sp->GetConcreteFrameIndex() == 0) {
+ StopInfoSP stop_info_sp;
+ if (thread)
+ stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp) {
+ const char *stop_description = stop_info_sp->GetDescription();
+ if (stop_description && stop_description[0]) {
+ size_t stop_description_len = strlen(stop_description);
+ int desc_x = window_width - stop_description_len - 16;
+ window.Printf("%*s", desc_x - window.GetCursorX(), "");
+ // window.MoveCursor(window_width - stop_description_len - 15,
+ // line_y);
+ window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
+ stop_description);
+ }
+ } else {
+ window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
+ }
+ }
+ if (highlight_attr)
+ window.AttributeOff(highlight_attr);
+ } else {
+ break;
+ }
+ }
+ } else {
+ size_t num_disassembly_lines = GetNumDisassemblyLines();
+ if (num_disassembly_lines > 0) {
+ // Display disassembly
+ BreakpointAddrs bp_file_addrs;
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ BreakpointList &bp_list = target->GetBreakpointList();
+ const size_t num_bps = bp_list.GetSize();
+ for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
+ BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
+ const size_t num_bps_locs = bp_sp->GetNumLocations();
+ for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
+ ++bp_loc_idx) {
+ BreakpointLocationSP bp_loc_sp =
+ bp_sp->GetLocationAtIndex(bp_loc_idx);
+ LineEntry bp_loc_line_entry;
+ const lldb::addr_t file_addr =
+ bp_loc_sp->GetAddress().GetFileAddress();
+ if (file_addr != LLDB_INVALID_ADDRESS) {
+ if (m_disassembly_range.ContainsFileAddress(file_addr))
+ bp_file_addrs.insert(file_addr);
+ }
+ }
+ }
+ }
+
+ const attr_t selected_highlight_attr = A_REVERSE;
+ const attr_t pc_highlight_attr = COLOR_PAIR(1);
+
+ StreamString strm;
+
+ InstructionList &insts = m_disassembly_sp->GetInstructionList();
+ Address pc_address;
+
+ if (frame_sp)
+ pc_address = frame_sp->GetFrameCodeAddress();
+ const uint32_t pc_idx =
+ pc_address.IsValid()
+ ? insts.GetIndexOfInstructionAtAddress(pc_address)
+ : UINT32_MAX;
+ if (set_selected_line_to_pc) {
+ m_selected_line = pc_idx;
+ }
+
+ const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
+ if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
+ m_first_visible_line = 0;
+
+ if (pc_idx < num_disassembly_lines) {
+ if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
+ pc_idx >= m_first_visible_line + num_visible_lines)
+ m_first_visible_line = pc_idx - non_visible_pc_offset;
+ }
+
+ for (size_t i = 0; i < num_visible_lines; ++i) {
+ const uint32_t inst_idx = m_first_visible_line + i;
+ Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
+ if (!inst)
+ break;
+
+ const int line_y = m_min_y + i;
+ window.MoveCursor(1, line_y);
+ const bool is_pc_line = frame_sp && inst_idx == pc_idx;
+ const bool line_is_selected = m_selected_line == inst_idx;
+ // Highlight the line as the PC line first, then if the selected line
+ // isn't the same as the PC line, highlight it differently
+ attr_t highlight_attr = 0;
+ attr_t bp_attr = 0;
+ if (is_pc_line)
+ highlight_attr = pc_highlight_attr;
+ else if (line_is_selected)
+ highlight_attr = selected_highlight_attr;
+
+ if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
+ bp_file_addrs.end())
+ bp_attr = COLOR_PAIR(2);
+
+ if (bp_attr)
+ window.AttributeOn(bp_attr);
+
+ window.Printf(" 0x%16.16llx ",
+ static_cast<unsigned long long>(
+ inst->GetAddress().GetLoadAddress(target)));
+
+ if (bp_attr)
+ window.AttributeOff(bp_attr);
+
+ window.PutChar(ACS_VLINE);
+ // Mark the line with the PC with a diamond
+ if (is_pc_line)
+ window.PutChar(ACS_DIAMOND);
+ else
+ window.PutChar(' ');
+
+ if (highlight_attr)
+ window.AttributeOn(highlight_attr);
+
+ const char *mnemonic = inst->GetMnemonic(&exe_ctx);
+ const char *operands = inst->GetOperands(&exe_ctx);
+ const char *comment = inst->GetComment(&exe_ctx);
+
+ if (mnemonic != nullptr && mnemonic[0] == '\0')
+ mnemonic = nullptr;
+ if (operands != nullptr && operands[0] == '\0')
+ operands = nullptr;
+ if (comment != nullptr && comment[0] == '\0')
+ comment = nullptr;
+
+ strm.Clear();
+
+ if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
+ strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
+ else if (mnemonic != nullptr && operands != nullptr)
+ strm.Printf("%-8s %s", mnemonic, operands);
+ else if (mnemonic != nullptr)
+ strm.Printf("%s", mnemonic);
+
+ int right_pad = 1;
+ window.PutCStringTruncated(strm.GetData(), right_pad);
+
+ if (is_pc_line && frame_sp &&
+ frame_sp->GetConcreteFrameIndex() == 0) {
+ StopInfoSP stop_info_sp;
+ if (thread)
+ stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp) {
+ const char *stop_description = stop_info_sp->GetDescription();
+ if (stop_description && stop_description[0]) {
+ size_t stop_description_len = strlen(stop_description);
+ int desc_x = window_width - stop_description_len - 16;
+ window.Printf("%*s", desc_x - window.GetCursorX(), "");
+ // window.MoveCursor(window_width - stop_description_len - 15,
+ // line_y);
+ window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
+ stop_description);
+ }
+ } else {
+ window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
+ }
+ }
+ if (highlight_attr)
+ window.AttributeOff(highlight_attr);
+ }
+ }
+ }
+ window.DeferredRefresh();
+ return true; // Drawing handled
+ }
+
+ size_t GetNumLines() {
+ size_t num_lines = GetNumSourceLines();
+ if (num_lines == 0)
+ num_lines = GetNumDisassemblyLines();
+ return num_lines;
+ }
+
+ size_t GetNumSourceLines() const {
+ if (m_file_sp)
+ return m_file_sp->GetNumLines();
+ return 0;
+ }
+
+ size_t GetNumDisassemblyLines() const {
+ if (m_disassembly_sp)
+ return m_disassembly_sp->GetInstructionList().GetSize();
+ return 0;
+ }
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
+ const uint32_t num_visible_lines = NumVisibleLines();
+ const size_t num_lines = GetNumLines();
+
+ switch (c) {
+ case ',':
+ case KEY_PPAGE:
+ // Page up key
+ if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
+ m_first_visible_line -= num_visible_lines;
+ else
+ m_first_visible_line = 0;
+ m_selected_line = m_first_visible_line;
+ return eKeyHandled;
+
+ case '.':
+ case KEY_NPAGE:
+ // Page down key
+ {
+ if (m_first_visible_line + num_visible_lines < num_lines)
+ m_first_visible_line += num_visible_lines;
+ else if (num_lines < num_visible_lines)
+ m_first_visible_line = 0;
+ else
+ m_first_visible_line = num_lines - num_visible_lines;
+ m_selected_line = m_first_visible_line;
+ }
+ return eKeyHandled;
+
+ case KEY_UP:
+ if (m_selected_line > 0) {
+ m_selected_line--;
+ if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
+ m_first_visible_line = m_selected_line;
+ }
+ return eKeyHandled;
+
+ case KEY_DOWN:
+ if (m_selected_line + 1 < num_lines) {
+ m_selected_line++;
+ if (m_first_visible_line + num_visible_lines < m_selected_line)
+ m_first_visible_line++;
+ }
+ return eKeyHandled;
+
+ case '\r':
+ case '\n':
+ case KEY_ENTER:
+ // Set a breakpoint and run to the line using a one shot breakpoint
+ if (GetNumSourceLines() > 0) {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
+ BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
+ nullptr, // Don't limit the breakpoint to certain modules
+ m_file_sp->GetFileSpec(), // Source file
+ m_selected_line +
+ 1, // Source line number (m_selected_line is zero based)
+ 0, // Unspecified column.
+ 0, // No offset
+ eLazyBoolCalculate, // Check inlines using global setting
+ eLazyBoolCalculate, // Skip prologue using global setting,
+ false, // internal
+ false, // request_hardware
+ eLazyBoolCalculate); // move_to_nearest_code
+ // Make breakpoint one shot
+ bp_sp->GetOptions()->SetOneShot(true);
+ exe_ctx.GetProcessRef().Resume();
+ }
+ } else if (m_selected_line < GetNumDisassemblyLines()) {
+ const Instruction *inst = m_disassembly_sp->GetInstructionList()
+ .GetInstructionAtIndex(m_selected_line)
+ .get();
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasTargetScope()) {
+ Address addr = inst->GetAddress();
+ BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
+ addr, // lldb_private::Address
+ false, // internal
+ false); // request_hardware
+ // Make breakpoint one shot
+ bp_sp->GetOptions()->SetOneShot(true);
+ exe_ctx.GetProcessRef().Resume();
+ }
+ }
+ return eKeyHandled;
+
+ case 'b': // 'b' == toggle breakpoint on currently selected line
+ if (m_selected_line < GetNumSourceLines()) {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasTargetScope()) {
+ BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
+ nullptr, // Don't limit the breakpoint to certain modules
+ m_file_sp->GetFileSpec(), // Source file
+ m_selected_line +
+ 1, // Source line number (m_selected_line is zero based)
+ 0, // No column specified.
+ 0, // No offset
+ eLazyBoolCalculate, // Check inlines using global setting
+ eLazyBoolCalculate, // Skip prologue using global setting,
+ false, // internal
+ false, // request_hardware
+ eLazyBoolCalculate); // move_to_nearest_code
+ }
+ } else if (m_selected_line < GetNumDisassemblyLines()) {
+ const Instruction *inst = m_disassembly_sp->GetInstructionList()
+ .GetInstructionAtIndex(m_selected_line)
+ .get();
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasTargetScope()) {
+ Address addr = inst->GetAddress();
+ BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
+ addr, // lldb_private::Address
+ false, // internal
+ false); // request_hardware
+ }
+ }
+ return eKeyHandled;
+
+ case 'd': // 'd' == detach and let run
+ case 'D': // 'D' == detach and keep stopped
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope())
+ exe_ctx.GetProcessRef().Detach(c == 'D');
+ }
+ return eKeyHandled;
+
+ case 'k':
+ // 'k' == kill
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope())
+ exe_ctx.GetProcessRef().Destroy(false);
+ }
+ return eKeyHandled;
+
+ case 'c':
+ // 'c' == continue
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope())
+ exe_ctx.GetProcessRef().Resume();
+ }
+ return eKeyHandled;
+
+ case 'o':
+ // 'o' == step out
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope() &&
+ StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
+ exe_ctx.GetThreadRef().StepOut();
+ }
+ }
+ return eKeyHandled;
+
+ case 'n': // 'n' == step over
+ case 'N': // 'N' == step over instruction
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope() &&
+ StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
+ bool source_step = (c == 'n');
+ exe_ctx.GetThreadRef().StepOver(source_step);
+ }
+ }
+ return eKeyHandled;
+
+ case 's': // 's' == step into
+ case 'S': // 'S' == step into instruction
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope() &&
+ StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
+ bool source_step = (c == 's');
+ exe_ctx.GetThreadRef().StepIn(source_step);
+ }
+ }
+ return eKeyHandled;
+
+ case 'h':
+ window.CreateHelpSubwindow();
+ return eKeyHandled;
+
+ default:
+ break;
+ }
+ return eKeyNotHandled;
+ }
+
+protected:
+ typedef std::set<uint32_t> BreakpointLines;
+ typedef std::set<lldb::addr_t> BreakpointAddrs;
+
+ Debugger &m_debugger;
+ SymbolContext m_sc;
+ SourceManager::FileSP m_file_sp;
+ SymbolContextScope *m_disassembly_scope;
+ lldb::DisassemblerSP m_disassembly_sp;
+ AddressRange m_disassembly_range;
+ StreamString m_title;
+ lldb::user_id_t m_tid;
+ int m_line_width;
+ uint32_t m_selected_line; // The selected line
+ uint32_t m_pc_line; // The line with the PC
+ uint32_t m_stop_id;
+ uint32_t m_frame_idx;
+ int m_first_visible_line;
+ int m_min_x;
+ int m_min_y;
+ int m_max_x;
+ int m_max_y;
+};
+
+DisplayOptions ValueObjectListDelegate::g_options = {true};
+
+IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
+ : IOHandler(debugger, IOHandler::Type::Curses) {}
+
+void IOHandlerCursesGUI::Activate() {
+ IOHandler::Activate();
+ if (!m_app_ap) {
+ m_app_ap.reset(new Application(GetInputFILE(), GetOutputFILE()));
+
+ // This is both a window and a menu delegate
+ std::shared_ptr<ApplicationDelegate> app_delegate_sp(
+ new ApplicationDelegate(*m_app_ap, m_debugger));
+
+ MenuDelegateSP app_menu_delegate_sp =
+ std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
+ MenuSP lldb_menu_sp(
+ new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
+ MenuSP exit_menuitem_sp(
+ new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
+ exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
+ lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
+ lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
+ lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
+
+ MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
+ ApplicationDelegate::eMenuID_Target));
+ target_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
+ target_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
+
+ MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
+ ApplicationDelegate::eMenuID_Process));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Detach", nullptr, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
+ process_menu_sp->AddSubmenu(
+ MenuSP(new Menu("Continue", nullptr, 'c',
+ ApplicationDelegate::eMenuID_ProcessContinue)));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
+
+ MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
+ ApplicationDelegate::eMenuID_Thread));
+ thread_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
+ thread_menu_sp->AddSubmenu(
+ MenuSP(new Menu("Step Over", nullptr, 'v',
+ ApplicationDelegate::eMenuID_ThreadStepOver)));
+ thread_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
+
+ MenuSP view_menu_sp(
+ new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
+ view_menu_sp->AddSubmenu(
+ MenuSP(new Menu("Backtrace", nullptr, 'b',
+ ApplicationDelegate::eMenuID_ViewBacktrace)));
+ view_menu_sp->AddSubmenu(
+ MenuSP(new Menu("Registers", nullptr, 'r',
+ ApplicationDelegate::eMenuID_ViewRegisters)));
+ view_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
+ view_menu_sp->AddSubmenu(
+ MenuSP(new Menu("Variables", nullptr, 'v',
+ ApplicationDelegate::eMenuID_ViewVariables)));
+
+ MenuSP help_menu_sp(
+ new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
+ help_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
+
+ m_app_ap->Initialize();
+ WindowSP &main_window_sp = m_app_ap->GetMainWindow();
+
+ MenuSP menubar_sp(new Menu(Menu::Type::Bar));
+ menubar_sp->AddSubmenu(lldb_menu_sp);
+ menubar_sp->AddSubmenu(target_menu_sp);
+ menubar_sp->AddSubmenu(process_menu_sp);
+ menubar_sp->AddSubmenu(thread_menu_sp);
+ menubar_sp->AddSubmenu(view_menu_sp);
+ menubar_sp->AddSubmenu(help_menu_sp);
+ menubar_sp->SetDelegate(app_menu_delegate_sp);
+
+ Rect content_bounds = main_window_sp->GetFrame();
+ Rect menubar_bounds = content_bounds.MakeMenuBar();
+ Rect status_bounds = content_bounds.MakeStatusBar();
+ Rect source_bounds;
+ Rect variables_bounds;
+ Rect threads_bounds;
+ Rect source_variables_bounds;
+ content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
+ threads_bounds);
+ source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
+ variables_bounds);
+
+ WindowSP menubar_window_sp =
+ main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
+ // Let the menubar get keys if the active window doesn't handle the keys
+ // that are typed so it can respond to menubar key presses.
+ menubar_window_sp->SetCanBeActive(
+ false); // Don't let the menubar become the active window
+ menubar_window_sp->SetDelegate(menubar_sp);
+
+ WindowSP source_window_sp(
+ main_window_sp->CreateSubWindow("Source", source_bounds, true));
+ WindowSP variables_window_sp(
+ main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
+ WindowSP threads_window_sp(
+ main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
+ WindowSP status_window_sp(
+ main_window_sp->CreateSubWindow("Status", status_bounds, false));
+ status_window_sp->SetCanBeActive(
+ false); // Don't let the status bar become the active window
+ main_window_sp->SetDelegate(
+ std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
+ source_window_sp->SetDelegate(
+ WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
+ variables_window_sp->SetDelegate(
+ WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
+ TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
+ threads_window_sp->SetDelegate(WindowDelegateSP(
+ new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
+ status_window_sp->SetDelegate(
+ WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
+
+ // Show the main help window once the first time the curses GUI is launched
+ static bool g_showed_help = false;
+ if (!g_showed_help) {
+ g_showed_help = true;
+ main_window_sp->CreateHelpSubwindow();
+ }
+
+ init_pair(1, COLOR_WHITE, COLOR_BLUE);
+ init_pair(2, COLOR_BLACK, COLOR_WHITE);
+ init_pair(3, COLOR_MAGENTA, COLOR_WHITE);
+ init_pair(4, COLOR_MAGENTA, COLOR_BLACK);
+ init_pair(5, COLOR_RED, COLOR_BLACK);
+ }
+}
+
+void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
+
+void IOHandlerCursesGUI::Run() {
+ m_app_ap->Run(m_debugger);
+ SetIsDone(true);
+}
+
+IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
+
+void IOHandlerCursesGUI::Cancel() {}
+
+bool IOHandlerCursesGUI::Interrupt() { return false; }
+
+void IOHandlerCursesGUI::GotEOF() {}
+
+#endif // LLDB_DISABLE_CURSES
diff --git a/contrib/llvm-project/lldb/source/Core/Mangled.cpp b/contrib/llvm-project/lldb/source/Core/Mangled.cpp
new file mode 100644
index 000000000000..c6759cc944ca
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Mangled.cpp
@@ -0,0 +1,490 @@
+//===-- Mangled.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Mangled.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/windows.h"
+
+#include <dbghelp.h>
+#pragma comment(lib, "dbghelp.lib")
+#endif
+
+#include "lldb/Core/RichManglingContext.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Support/Compiler.h"
+
+#include <mutex>
+#include <string>
+#include <utility>
+
+#include <stdlib.h>
+#include <string.h>
+using namespace lldb_private;
+
+#if defined(_MSC_VER)
+static DWORD safeUndecorateName(const char *Mangled, char *Demangled,
+ DWORD DemangledLength) {
+ static std::mutex M;
+ std::lock_guard<std::mutex> Lock(M);
+ return ::UnDecorateSymbolName(
+ Mangled, Demangled, DemangledLength,
+ UNDNAME_NO_ACCESS_SPECIFIERS | // Strip public, private, protected
+ // keywords
+ UNDNAME_NO_ALLOCATION_LANGUAGE | // Strip __thiscall, __stdcall,
+ // etc keywords
+ UNDNAME_NO_THROW_SIGNATURES | // Strip throw() specifications
+ UNDNAME_NO_MEMBER_TYPE | // Strip virtual, static, etc
+ // specifiers
+ UNDNAME_NO_MS_KEYWORDS // Strip all MS extension keywords
+ );
+}
+#endif
+
+static inline Mangled::ManglingScheme cstring_mangling_scheme(const char *s) {
+ if (s) {
+ if (s[0] == '?')
+ return Mangled::eManglingSchemeMSVC;
+ if (s[0] == '_' && s[1] == 'Z')
+ return Mangled::eManglingSchemeItanium;
+ }
+ return Mangled::eManglingSchemeNone;
+}
+
+static inline bool cstring_is_mangled(const char *s) {
+ return cstring_mangling_scheme(s) != Mangled::eManglingSchemeNone;
+}
+
+static ConstString
+get_demangled_name_without_arguments(ConstString mangled,
+ ConstString demangled) {
+ // This pair is <mangled name, demangled name without function arguments>
+ static std::pair<ConstString, ConstString>
+ g_most_recent_mangled_to_name_sans_args;
+
+ // Need to have the mangled & demangled names we're currently examining as
+ // statics so we can return a const ref to them at the end of the func if we
+ // don't have anything better.
+ static ConstString g_last_mangled;
+ static ConstString g_last_demangled;
+
+ if (mangled && g_most_recent_mangled_to_name_sans_args.first == mangled) {
+ return g_most_recent_mangled_to_name_sans_args.second;
+ }
+
+ g_last_demangled = demangled;
+ g_last_mangled = mangled;
+
+ const char *mangled_name_cstr = mangled.GetCString();
+
+ if (demangled && mangled_name_cstr && mangled_name_cstr[0]) {
+ if (mangled_name_cstr[0] == '_' && mangled_name_cstr[1] == 'Z' &&
+ (mangled_name_cstr[2] != 'T' && // avoid virtual table, VTT structure,
+ // typeinfo structure, and typeinfo
+ // mangled_name
+ mangled_name_cstr[2] != 'G' && // avoid guard variables
+ mangled_name_cstr[2] != 'Z')) // named local entities (if we eventually
+ // handle eSymbolTypeData, we will want
+ // this back)
+ {
+ CPlusPlusLanguage::MethodName cxx_method(demangled);
+ if (!cxx_method.GetBasename().empty()) {
+ std::string shortname;
+ if (!cxx_method.GetContext().empty())
+ shortname = cxx_method.GetContext().str() + "::";
+ shortname += cxx_method.GetBasename().str();
+ ConstString result(shortname.c_str());
+ g_most_recent_mangled_to_name_sans_args.first = mangled;
+ g_most_recent_mangled_to_name_sans_args.second = result;
+ return g_most_recent_mangled_to_name_sans_args.second;
+ }
+ }
+ }
+
+ if (demangled)
+ return g_last_demangled;
+ return g_last_mangled;
+}
+
+#pragma mark Mangled
+// Default constructor
+Mangled::Mangled() : m_mangled(), m_demangled() {}
+
+// Constructor with an optional string and a boolean indicating if it is the
+// mangled version.
+Mangled::Mangled(ConstString s, bool mangled)
+ : m_mangled(), m_demangled() {
+ if (s)
+ SetValue(s, mangled);
+}
+
+Mangled::Mangled(llvm::StringRef name, bool is_mangled) {
+ if (!name.empty())
+ SetValue(ConstString(name), is_mangled);
+}
+
+Mangled::Mangled(ConstString s) : m_mangled(), m_demangled() {
+ if (s)
+ SetValue(s);
+}
+
+Mangled::Mangled(llvm::StringRef name) {
+ if (!name.empty())
+ SetValue(ConstString(name));
+}
+
+// Destructor
+Mangled::~Mangled() {}
+
+// Convert to pointer operator. This allows code to check any Mangled objects
+// to see if they contain anything valid using code such as:
+//
+// Mangled mangled(...);
+// if (mangled)
+// { ...
+Mangled::operator void *() const {
+ return (m_mangled) ? const_cast<Mangled *>(this) : nullptr;
+}
+
+// Logical NOT operator. This allows code to check any Mangled objects to see
+// if they are invalid using code such as:
+//
+// Mangled mangled(...);
+// if (!file_spec)
+// { ...
+bool Mangled::operator!() const { return !m_mangled; }
+
+// Clear the mangled and demangled values.
+void Mangled::Clear() {
+ m_mangled.Clear();
+ m_demangled.Clear();
+}
+
+// Compare the string values.
+int Mangled::Compare(const Mangled &a, const Mangled &b) {
+ return ConstString::Compare(
+ a.GetName(lldb::eLanguageTypeUnknown, ePreferMangled),
+ b.GetName(lldb::eLanguageTypeUnknown, ePreferMangled));
+}
+
+// Set the string value in this objects. If "mangled" is true, then the mangled
+// named is set with the new value in "s", else the demangled name is set.
+void Mangled::SetValue(ConstString s, bool mangled) {
+ if (s) {
+ if (mangled) {
+ m_demangled.Clear();
+ m_mangled = s;
+ } else {
+ m_demangled = s;
+ m_mangled.Clear();
+ }
+ } else {
+ m_demangled.Clear();
+ m_mangled.Clear();
+ }
+}
+
+void Mangled::SetValue(ConstString name) {
+ if (name) {
+ if (cstring_is_mangled(name.GetCString())) {
+ m_demangled.Clear();
+ m_mangled = name;
+ } else {
+ m_demangled = name;
+ m_mangled.Clear();
+ }
+ } else {
+ m_demangled.Clear();
+ m_mangled.Clear();
+ }
+}
+
+// Local helpers for different demangling implementations.
+static char *GetMSVCDemangledStr(const char *M) {
+#if defined(_MSC_VER)
+ const size_t demangled_length = 2048;
+ char *demangled_cstr = static_cast<char *>(::malloc(demangled_length));
+ ::ZeroMemory(demangled_cstr, demangled_length);
+ DWORD result = safeUndecorateName(M, demangled_cstr, demangled_length);
+
+ if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) {
+ if (demangled_cstr && demangled_cstr[0])
+ log->Printf("demangled msvc: %s -> \"%s\"", M, demangled_cstr);
+ else
+ log->Printf("demangled msvc: %s -> error: 0x%lu", M, result);
+ }
+
+ if (result != 0) {
+ return demangled_cstr;
+ } else {
+ ::free(demangled_cstr);
+ return nullptr;
+ }
+#else
+ return nullptr;
+#endif
+}
+
+static char *GetItaniumDemangledStr(const char *M) {
+ char *demangled_cstr = nullptr;
+
+ llvm::ItaniumPartialDemangler ipd;
+ bool err = ipd.partialDemangle(M);
+ if (!err) {
+ // Default buffer and size (will realloc in case it's too small).
+ size_t demangled_size = 80;
+ demangled_cstr = static_cast<char *>(std::malloc(demangled_size));
+ demangled_cstr = ipd.finishDemangle(demangled_cstr, &demangled_size);
+
+ assert(demangled_cstr &&
+ "finishDemangle must always succeed if partialDemangle did");
+ assert(demangled_cstr[demangled_size - 1] == '\0' &&
+ "Expected demangled_size to return length including trailing null");
+ }
+
+ if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) {
+ if (demangled_cstr)
+ log->Printf("demangled itanium: %s -> \"%s\"", M, demangled_cstr);
+ else
+ log->Printf("demangled itanium: %s -> error: failed to demangle", M);
+ }
+
+ return demangled_cstr;
+}
+
+// Explicit demangling for scheduled requests during batch processing. This
+// makes use of ItaniumPartialDemangler's rich demangle info
+bool Mangled::DemangleWithRichManglingInfo(
+ RichManglingContext &context, SkipMangledNameFn *skip_mangled_name) {
+ // We need to generate and cache the demangled name.
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "Mangled::DemangleWithRichNameIndexInfo (m_mangled = %s)",
+ m_mangled.GetCString());
+
+ // Others are not meant to arrive here. ObjC names or C's main() for example
+ // have their names stored in m_demangled, while m_mangled is empty.
+ assert(m_mangled);
+
+ // Check whether or not we are interested in this name at all.
+ ManglingScheme scheme = cstring_mangling_scheme(m_mangled.GetCString());
+ if (skip_mangled_name && skip_mangled_name(m_mangled.GetStringRef(), scheme))
+ return false;
+
+ switch (scheme) {
+ case eManglingSchemeNone:
+ // The current mangled_name_filter would allow llvm_unreachable here.
+ return false;
+
+ case eManglingSchemeItanium:
+ // We want the rich mangling info here, so we don't care whether or not
+ // there is a demangled string in the pool already.
+ if (context.FromItaniumName(m_mangled)) {
+ // If we got an info, we have a name. Copy to string pool and connect the
+ // counterparts to accelerate later access in GetDemangledName().
+ context.ParseFullName();
+ m_demangled.SetStringWithMangledCounterpart(context.GetBufferRef(),
+ m_mangled);
+ return true;
+ } else {
+ m_demangled.SetCString("");
+ return false;
+ }
+
+ case eManglingSchemeMSVC: {
+ // We have no rich mangling for MSVC-mangled names yet, so first try to
+ // demangle it if necessary.
+ if (!m_demangled && !m_mangled.GetMangledCounterpart(m_demangled)) {
+ if (char *d = GetMSVCDemangledStr(m_mangled.GetCString())) {
+ // If we got an info, we have a name. Copy to string pool and connect
+ // the counterparts to accelerate later access in GetDemangledName().
+ m_demangled.SetStringWithMangledCounterpart(llvm::StringRef(d),
+ m_mangled);
+ ::free(d);
+ } else {
+ m_demangled.SetCString("");
+ }
+ }
+
+ if (m_demangled.IsEmpty()) {
+ // Cannot demangle it, so don't try parsing.
+ return false;
+ } else {
+ // Demangled successfully, we can try and parse it with
+ // CPlusPlusLanguage::MethodName.
+ return context.FromCxxMethodName(m_demangled);
+ }
+ }
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+// Generate the demangled name on demand using this accessor. Code in this
+// class will need to use this accessor if it wishes to decode the demangled
+// name. The result is cached and will be kept until a new string value is
+// supplied to this object, or until the end of the object's lifetime.
+ConstString
+Mangled::GetDemangledName(lldb::LanguageType language) const {
+ // Check to make sure we have a valid mangled name and that we haven't
+ // already decoded our mangled name.
+ if (m_mangled && m_demangled.IsNull()) {
+ // We need to generate and cache the demangled name.
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "Mangled::GetDemangledName (m_mangled = %s)",
+ m_mangled.GetCString());
+
+ // Don't bother running anything that isn't mangled
+ const char *mangled_name = m_mangled.GetCString();
+ ManglingScheme mangling_scheme{cstring_mangling_scheme(mangled_name)};
+ if (mangling_scheme != eManglingSchemeNone &&
+ !m_mangled.GetMangledCounterpart(m_demangled)) {
+ // We didn't already mangle this name, demangle it and if all goes well
+ // add it to our map.
+ char *demangled_name = nullptr;
+ switch (mangling_scheme) {
+ case eManglingSchemeMSVC:
+ demangled_name = GetMSVCDemangledStr(mangled_name);
+ break;
+ case eManglingSchemeItanium: {
+ demangled_name = GetItaniumDemangledStr(mangled_name);
+ break;
+ }
+ case eManglingSchemeNone:
+ llvm_unreachable("eManglingSchemeNone was handled already");
+ }
+ if (demangled_name) {
+ m_demangled.SetStringWithMangledCounterpart(
+ llvm::StringRef(demangled_name), m_mangled);
+ free(demangled_name);
+ }
+ }
+ if (m_demangled.IsNull()) {
+ // Set the demangled string to the empty string to indicate we tried to
+ // parse it once and failed.
+ m_demangled.SetCString("");
+ }
+ }
+
+ return m_demangled;
+}
+
+ConstString
+Mangled::GetDisplayDemangledName(lldb::LanguageType language) const {
+ return GetDemangledName(language);
+}
+
+bool Mangled::NameMatches(const RegularExpression &regex,
+ lldb::LanguageType language) const {
+ if (m_mangled && regex.Execute(m_mangled.AsCString()))
+ return true;
+
+ ConstString demangled = GetDemangledName(language);
+ return demangled && regex.Execute(demangled.AsCString());
+}
+
+// Get the demangled name if there is one, else return the mangled name.
+ConstString Mangled::GetName(lldb::LanguageType language,
+ Mangled::NamePreference preference) const {
+ if (preference == ePreferMangled && m_mangled)
+ return m_mangled;
+
+ ConstString demangled = GetDemangledName(language);
+
+ if (preference == ePreferDemangledWithoutArguments) {
+ return get_demangled_name_without_arguments(m_mangled, demangled);
+ }
+ if (preference == ePreferDemangled) {
+ // Call the accessor to make sure we get a demangled name in case it hasn't
+ // been demangled yet...
+ if (demangled)
+ return demangled;
+ return m_mangled;
+ }
+ return demangled;
+}
+
+// Dump a Mangled object to stream "s". We don't force our demangled name to be
+// computed currently (we don't use the accessor).
+void Mangled::Dump(Stream *s) const {
+ if (m_mangled) {
+ *s << ", mangled = " << m_mangled;
+ }
+ if (m_demangled) {
+ const char *demangled = m_demangled.AsCString();
+ s->Printf(", demangled = %s", demangled[0] ? demangled : "<error>");
+ }
+}
+
+// Dumps a debug version of this string with extra object and state information
+// to stream "s".
+void Mangled::DumpDebug(Stream *s) const {
+ s->Printf("%*p: Mangled mangled = ", static_cast<int>(sizeof(void *) * 2),
+ static_cast<const void *>(this));
+ m_mangled.DumpDebug(s);
+ s->Printf(", demangled = ");
+ m_demangled.DumpDebug(s);
+}
+
+// Return the size in byte that this object takes in memory. The size includes
+// the size of the objects it owns, and not the strings that it references
+// because they are shared strings.
+size_t Mangled::MemorySize() const {
+ return m_mangled.MemorySize() + m_demangled.MemorySize();
+}
+
+// We "guess" the language because we can't determine a symbol's language from
+// it's name. For example, a Pascal symbol can be mangled using the C++
+// Itanium scheme, and defined in a compilation unit within the same module as
+// other C++ units. In addition, different targets could have different ways
+// of mangling names from a given language, likewise the compilation units
+// within those targets.
+lldb::LanguageType Mangled::GuessLanguage() const {
+ ConstString mangled = GetMangledName();
+ if (mangled) {
+ const char *mangled_name = mangled.GetCString();
+ if (CPlusPlusLanguage::IsCPPMangledName(mangled_name))
+ return lldb::eLanguageTypeC_plus_plus;
+ else if (ObjCLanguage::IsPossibleObjCMethodName(mangled_name))
+ return lldb::eLanguageTypeObjC;
+ } else {
+ // ObjC names aren't really mangled, so they won't necessarily be in the
+ // mangled name slot.
+ ConstString demangled_name = GetDemangledName(lldb::eLanguageTypeUnknown);
+ if (demangled_name
+ && ObjCLanguage::IsPossibleObjCMethodName(demangled_name.GetCString()))
+ return lldb::eLanguageTypeObjC;
+
+ }
+ return lldb::eLanguageTypeUnknown;
+}
+
+// Dump OBJ to the supplied stream S.
+Stream &operator<<(Stream &s, const Mangled &obj) {
+ if (obj.GetMangledName())
+ s << "mangled = '" << obj.GetMangledName() << "'";
+
+ ConstString demangled =
+ obj.GetDemangledName(lldb::eLanguageTypeUnknown);
+ if (demangled)
+ s << ", demangled = '" << demangled << '\'';
+ else
+ s << ", demangled = <error>";
+ return s;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Module.cpp b/contrib/llvm-project/lldb/source/Core/Module.cpp
new file mode 100644
index 000000000000..153d5a740936
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Module.cpp
@@ -0,0 +1,1670 @@
+//===-- Module.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Module.h"
+
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/AddressResolverFileLine.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Mangled.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/SearchFilter.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/PosixApi.h"
+#endif
+
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <assert.h>
+#include <cstdint>
+#include <inttypes.h>
+#include <map>
+#include <stdarg.h>
+#include <string.h>
+#include <type_traits>
+#include <utility>
+
+namespace lldb_private {
+class CompilerDeclContext;
+}
+namespace lldb_private {
+class VariableList;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Shared pointers to modules track module lifetimes in targets and in the
+// global module, but this collection will track all module objects that are
+// still alive
+typedef std::vector<Module *> ModuleCollection;
+
+static ModuleCollection &GetModuleCollection() {
+ // This module collection needs to live past any module, so we could either
+ // make it a shared pointer in each module or just leak is. Since it is only
+ // an empty vector by the time all the modules have gone away, we just leak
+ // it for now. If we decide this is a big problem we can introduce a
+ // Finalize method that will tear everything down in a predictable order.
+
+ static ModuleCollection *g_module_collection = nullptr;
+ if (g_module_collection == nullptr)
+ g_module_collection = new ModuleCollection();
+
+ return *g_module_collection;
+}
+
+std::recursive_mutex &Module::GetAllocationModuleCollectionMutex() {
+ // NOTE: The mutex below must be leaked since the global module list in
+ // the ModuleList class will get torn at some point, and we can't know if it
+ // will tear itself down before the "g_module_collection_mutex" below will.
+ // So we leak a Mutex object below to safeguard against that
+
+ static std::recursive_mutex *g_module_collection_mutex = nullptr;
+ if (g_module_collection_mutex == nullptr)
+ g_module_collection_mutex = new std::recursive_mutex; // NOTE: known leak
+ return *g_module_collection_mutex;
+}
+
+size_t Module::GetNumberAllocatedModules() {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ return GetModuleCollection().size();
+}
+
+Module *Module::GetAllocatedModuleAtIndex(size_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ ModuleCollection &modules = GetModuleCollection();
+ if (idx < modules.size())
+ return modules[idx];
+ return nullptr;
+}
+
+Module::Module(const ModuleSpec &module_spec)
+ : m_object_offset(0), m_file_has_changed(false),
+ m_first_file_changed_log(false) {
+ // Scope for locker below...
+ {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ GetModuleCollection().push_back(this);
+ }
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_OBJECT |
+ LIBLLDB_LOG_MODULES));
+ if (log != nullptr)
+ log->Printf("%p Module::Module((%s) '%s%s%s%s')", static_cast<void *>(this),
+ module_spec.GetArchitecture().GetArchitectureName(),
+ module_spec.GetFileSpec().GetPath().c_str(),
+ module_spec.GetObjectName().IsEmpty() ? "" : "(",
+ module_spec.GetObjectName().IsEmpty()
+ ? ""
+ : module_spec.GetObjectName().AsCString(""),
+ module_spec.GetObjectName().IsEmpty() ? "" : ")");
+
+ // First extract all module specifications from the file using the local file
+ // path. If there are no specifications, then don't fill anything in
+ ModuleSpecList modules_specs;
+ if (ObjectFile::GetModuleSpecifications(module_spec.GetFileSpec(), 0, 0,
+ modules_specs) == 0)
+ return;
+
+ // Now make sure that one of the module specifications matches what we just
+ // extract. We might have a module specification that specifies a file
+ // "/usr/lib/dyld" with UUID XXX, but we might have a local version of
+ // "/usr/lib/dyld" that has
+ // UUID YYY and we don't want those to match. If they don't match, just don't
+ // fill any ivars in so we don't accidentally grab the wrong file later since
+ // they don't match...
+ ModuleSpec matching_module_spec;
+ if (!modules_specs.FindMatchingModuleSpec(module_spec,
+ matching_module_spec)) {
+ if (log) {
+ log->Printf("Found local object file but the specs didn't match");
+ }
+ return;
+ }
+
+ if (module_spec.GetFileSpec())
+ m_mod_time = FileSystem::Instance().GetModificationTime(module_spec.GetFileSpec());
+ else if (matching_module_spec.GetFileSpec())
+ m_mod_time =
+ FileSystem::Instance().GetModificationTime(matching_module_spec.GetFileSpec());
+
+ // Copy the architecture from the actual spec if we got one back, else use
+ // the one that was specified
+ if (matching_module_spec.GetArchitecture().IsValid())
+ m_arch = matching_module_spec.GetArchitecture();
+ else if (module_spec.GetArchitecture().IsValid())
+ m_arch = module_spec.GetArchitecture();
+
+ // Copy the file spec over and use the specified one (if there was one) so we
+ // don't use a path that might have gotten resolved a path in
+ // 'matching_module_spec'
+ if (module_spec.GetFileSpec())
+ m_file = module_spec.GetFileSpec();
+ else if (matching_module_spec.GetFileSpec())
+ m_file = matching_module_spec.GetFileSpec();
+
+ // Copy the platform file spec over
+ if (module_spec.GetPlatformFileSpec())
+ m_platform_file = module_spec.GetPlatformFileSpec();
+ else if (matching_module_spec.GetPlatformFileSpec())
+ m_platform_file = matching_module_spec.GetPlatformFileSpec();
+
+ // Copy the symbol file spec over
+ if (module_spec.GetSymbolFileSpec())
+ m_symfile_spec = module_spec.GetSymbolFileSpec();
+ else if (matching_module_spec.GetSymbolFileSpec())
+ m_symfile_spec = matching_module_spec.GetSymbolFileSpec();
+
+ // Copy the object name over
+ if (matching_module_spec.GetObjectName())
+ m_object_name = matching_module_spec.GetObjectName();
+ else
+ m_object_name = module_spec.GetObjectName();
+
+ // Always trust the object offset (file offset) and object modification time
+ // (for mod time in a BSD static archive) of from the matching module
+ // specification
+ m_object_offset = matching_module_spec.GetObjectOffset();
+ m_object_mod_time = matching_module_spec.GetObjectModificationTime();
+}
+
+Module::Module(const FileSpec &file_spec, const ArchSpec &arch,
+ const ConstString *object_name, lldb::offset_t object_offset,
+ const llvm::sys::TimePoint<> &object_mod_time)
+ : m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)), m_arch(arch),
+ m_file(file_spec), m_object_offset(object_offset),
+ m_object_mod_time(object_mod_time), m_file_has_changed(false),
+ m_first_file_changed_log(false) {
+ // Scope for locker below...
+ {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ GetModuleCollection().push_back(this);
+ }
+
+ if (object_name)
+ m_object_name = *object_name;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_OBJECT |
+ LIBLLDB_LOG_MODULES));
+ if (log != nullptr)
+ log->Printf("%p Module::Module((%s) '%s%s%s%s')", static_cast<void *>(this),
+ m_arch.GetArchitectureName(), m_file.GetPath().c_str(),
+ m_object_name.IsEmpty() ? "" : "(",
+ m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""),
+ m_object_name.IsEmpty() ? "" : ")");
+}
+
+Module::Module()
+ : m_object_offset(0), m_file_has_changed(false),
+ m_first_file_changed_log(false) {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ GetModuleCollection().push_back(this);
+}
+
+Module::~Module() {
+ // Lock our module down while we tear everything down to make sure we don't
+ // get any access to the module while it is being destroyed
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ // Scope for locker below...
+ {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ ModuleCollection &modules = GetModuleCollection();
+ ModuleCollection::iterator end = modules.end();
+ ModuleCollection::iterator pos = std::find(modules.begin(), end, this);
+ assert(pos != end);
+ modules.erase(pos);
+ }
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_OBJECT |
+ LIBLLDB_LOG_MODULES));
+ if (log != nullptr)
+ log->Printf("%p Module::~Module((%s) '%s%s%s%s')",
+ static_cast<void *>(this), m_arch.GetArchitectureName(),
+ m_file.GetPath().c_str(), m_object_name.IsEmpty() ? "" : "(",
+ m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""),
+ m_object_name.IsEmpty() ? "" : ")");
+ // Release any auto pointers before we start tearing down our member
+ // variables since the object file and symbol files might need to make
+ // function calls back into this module object. The ordering is important
+ // here because symbol files can require the module object file. So we tear
+ // down the symbol file first, then the object file.
+ m_sections_up.reset();
+ m_symfile_up.reset();
+ m_objfile_sp.reset();
+}
+
+ObjectFile *Module::GetMemoryObjectFile(const lldb::ProcessSP &process_sp,
+ lldb::addr_t header_addr, Status &error,
+ size_t size_to_read) {
+ if (m_objfile_sp) {
+ error.SetErrorString("object file already exists");
+ } else {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (process_sp) {
+ m_did_load_objfile = true;
+ auto data_up = llvm::make_unique<DataBufferHeap>(size_to_read, 0);
+ Status readmem_error;
+ const size_t bytes_read =
+ process_sp->ReadMemory(header_addr, data_up->GetBytes(),
+ data_up->GetByteSize(), readmem_error);
+ if (bytes_read == size_to_read) {
+ DataBufferSP data_sp(data_up.release());
+ m_objfile_sp = ObjectFile::FindPlugin(shared_from_this(), process_sp,
+ header_addr, data_sp);
+ if (m_objfile_sp) {
+ StreamString s;
+ s.Printf("0x%16.16" PRIx64, header_addr);
+ m_object_name.SetString(s.GetString());
+
+ // Once we get the object file, update our module with the object
+ // file's architecture since it might differ in vendor/os if some
+ // parts were unknown.
+ m_arch = m_objfile_sp->GetArchitecture();
+
+ // Augment the arch with the target's information in case
+ // we are unable to extract the os/environment from memory.
+ m_arch.MergeFrom(process_sp->GetTarget().GetArchitecture());
+ } else {
+ error.SetErrorString("unable to find suitable object file plug-in");
+ }
+ } else {
+ error.SetErrorStringWithFormat("unable to read header from memory: %s",
+ readmem_error.AsCString());
+ }
+ } else {
+ error.SetErrorString("invalid process");
+ }
+ }
+ return m_objfile_sp.get();
+}
+
+const lldb_private::UUID &Module::GetUUID() {
+ if (!m_did_set_uuid.load()) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_did_set_uuid.load()) {
+ ObjectFile *obj_file = GetObjectFile();
+
+ if (obj_file != nullptr) {
+ m_uuid = obj_file->GetUUID();
+ m_did_set_uuid = true;
+ }
+ }
+ }
+ return m_uuid;
+}
+
+void Module::SetUUID(const lldb_private::UUID &uuid) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_did_set_uuid) {
+ m_uuid = uuid;
+ m_did_set_uuid = true;
+ } else {
+ lldbassert(0 && "Attempting to overwrite the existing module UUID");
+ }
+}
+
+TypeSystem *Module::GetTypeSystemForLanguage(LanguageType language) {
+ return m_type_system_map.GetTypeSystemForLanguage(language, this, true);
+}
+
+void Module::ParseAllDebugSymbols() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ size_t num_comp_units = GetNumCompileUnits();
+ if (num_comp_units == 0)
+ return;
+
+ SymbolContext sc;
+ sc.module_sp = shared_from_this();
+ SymbolVendor *symbols = GetSymbolVendor();
+
+ for (size_t cu_idx = 0; cu_idx < num_comp_units; cu_idx++) {
+ sc.comp_unit = symbols->GetCompileUnitAtIndex(cu_idx).get();
+ if (!sc.comp_unit)
+ continue;
+
+ symbols->ParseVariablesForContext(sc);
+
+ symbols->ParseFunctions(*sc.comp_unit);
+
+ sc.comp_unit->ForeachFunction([&sc, &symbols](const FunctionSP &f) {
+ symbols->ParseBlocksRecursive(*f);
+
+ // Parse the variables for this function and all its blocks
+ sc.function = f.get();
+ symbols->ParseVariablesForContext(sc);
+ return false;
+ });
+
+ // Parse all types for this compile unit
+ symbols->ParseTypes(*sc.comp_unit);
+ }
+}
+
+void Module::CalculateSymbolContext(SymbolContext *sc) {
+ sc->module_sp = shared_from_this();
+}
+
+ModuleSP Module::CalculateSymbolContextModule() { return shared_from_this(); }
+
+void Module::DumpSymbolContext(Stream *s) {
+ s->Printf(", Module{%p}", static_cast<void *>(this));
+}
+
+size_t Module::GetNumCompileUnits() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "Module::GetNumCompileUnits (module = %p)",
+ static_cast<void *>(this));
+ SymbolVendor *symbols = GetSymbolVendor();
+ if (symbols)
+ return symbols->GetNumCompileUnits();
+ return 0;
+}
+
+CompUnitSP Module::GetCompileUnitAtIndex(size_t index) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ size_t num_comp_units = GetNumCompileUnits();
+ CompUnitSP cu_sp;
+
+ if (index < num_comp_units) {
+ SymbolVendor *symbols = GetSymbolVendor();
+ if (symbols)
+ cu_sp = symbols->GetCompileUnitAtIndex(index);
+ }
+ return cu_sp;
+}
+
+bool Module::ResolveFileAddress(lldb::addr_t vm_addr, Address &so_addr) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "Module::ResolveFileAddress (vm_addr = 0x%" PRIx64 ")",
+ vm_addr);
+ SectionList *section_list = GetSectionList();
+ if (section_list)
+ return so_addr.ResolveAddressUsingFileSections(vm_addr, section_list);
+ return false;
+}
+
+uint32_t Module::ResolveSymbolContextForAddress(
+ const Address &so_addr, lldb::SymbolContextItem resolve_scope,
+ SymbolContext &sc, bool resolve_tail_call_address) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ uint32_t resolved_flags = 0;
+
+ // Clear the result symbol context in case we don't find anything, but don't
+ // clear the target
+ sc.Clear(false);
+
+ // Get the section from the section/offset address.
+ SectionSP section_sp(so_addr.GetSection());
+
+ // Make sure the section matches this module before we try and match anything
+ if (section_sp && section_sp->GetModule().get() == this) {
+ // If the section offset based address resolved itself, then this is the
+ // right module.
+ sc.module_sp = shared_from_this();
+ resolved_flags |= eSymbolContextModule;
+
+ SymbolVendor *sym_vendor = GetSymbolVendor();
+ if (!sym_vendor)
+ return resolved_flags;
+
+ // Resolve the compile unit, function, block, line table or line entry if
+ // requested.
+ if (resolve_scope & eSymbolContextCompUnit ||
+ resolve_scope & eSymbolContextFunction ||
+ resolve_scope & eSymbolContextBlock ||
+ resolve_scope & eSymbolContextLineEntry ||
+ resolve_scope & eSymbolContextVariable) {
+ resolved_flags |=
+ sym_vendor->ResolveSymbolContext(so_addr, resolve_scope, sc);
+ }
+
+ // Resolve the symbol if requested, but don't re-look it up if we've
+ // already found it.
+ if (resolve_scope & eSymbolContextSymbol &&
+ !(resolved_flags & eSymbolContextSymbol)) {
+ Symtab *symtab = sym_vendor->GetSymtab();
+ if (symtab && so_addr.IsSectionOffset()) {
+ Symbol *matching_symbol = nullptr;
+
+ symtab->ForEachSymbolContainingFileAddress(
+ so_addr.GetFileAddress(),
+ [&matching_symbol](Symbol *symbol) -> bool {
+ if (symbol->GetType() != eSymbolTypeInvalid) {
+ matching_symbol = symbol;
+ return false; // Stop iterating
+ }
+ return true; // Keep iterating
+ });
+ sc.symbol = matching_symbol;
+ if (!sc.symbol && resolve_scope & eSymbolContextFunction &&
+ !(resolved_flags & eSymbolContextFunction)) {
+ bool verify_unique = false; // No need to check again since
+ // ResolveSymbolContext failed to find a
+ // symbol at this address.
+ if (ObjectFile *obj_file = sc.module_sp->GetObjectFile())
+ sc.symbol =
+ obj_file->ResolveSymbolForAddress(so_addr, verify_unique);
+ }
+
+ if (sc.symbol) {
+ if (sc.symbol->IsSynthetic()) {
+ // We have a synthetic symbol so lets check if the object file from
+ // the symbol file in the symbol vendor is different than the
+ // object file for the module, and if so search its symbol table to
+ // see if we can come up with a better symbol. For example dSYM
+ // files on MacOSX have an unstripped symbol table inside of them.
+ ObjectFile *symtab_objfile = symtab->GetObjectFile();
+ if (symtab_objfile && symtab_objfile->IsStripped()) {
+ SymbolFile *symfile = sym_vendor->GetSymbolFile();
+ if (symfile) {
+ ObjectFile *symfile_objfile = symfile->GetObjectFile();
+ if (symfile_objfile != symtab_objfile) {
+ Symtab *symfile_symtab = symfile_objfile->GetSymtab();
+ if (symfile_symtab) {
+ Symbol *symbol =
+ symfile_symtab->FindSymbolContainingFileAddress(
+ so_addr.GetFileAddress());
+ if (symbol && !symbol->IsSynthetic()) {
+ sc.symbol = symbol;
+ }
+ }
+ }
+ }
+ }
+ }
+ resolved_flags |= eSymbolContextSymbol;
+ }
+ }
+ }
+
+ // For function symbols, so_addr may be off by one. This is a convention
+ // consistent with FDE row indices in eh_frame sections, but requires extra
+ // logic here to permit symbol lookup for disassembly and unwind.
+ if (resolve_scope & eSymbolContextSymbol &&
+ !(resolved_flags & eSymbolContextSymbol) && resolve_tail_call_address &&
+ so_addr.IsSectionOffset()) {
+ Address previous_addr = so_addr;
+ previous_addr.Slide(-1);
+
+ bool do_resolve_tail_call_address = false; // prevent recursion
+ const uint32_t flags = ResolveSymbolContextForAddress(
+ previous_addr, resolve_scope, sc, do_resolve_tail_call_address);
+ if (flags & eSymbolContextSymbol) {
+ AddressRange addr_range;
+ if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
+ false, addr_range)) {
+ if (addr_range.GetBaseAddress().GetSection() ==
+ so_addr.GetSection()) {
+ // If the requested address is one past the address range of a
+ // function (i.e. a tail call), or the decremented address is the
+ // start of a function (i.e. some forms of trampoline), indicate
+ // that the symbol has been resolved.
+ if (so_addr.GetOffset() ==
+ addr_range.GetBaseAddress().GetOffset() ||
+ so_addr.GetOffset() ==
+ addr_range.GetBaseAddress().GetOffset() +
+ addr_range.GetByteSize()) {
+ resolved_flags |= flags;
+ }
+ } else {
+ sc.symbol =
+ nullptr; // Don't trust the symbol if the sections didn't match.
+ }
+ }
+ }
+ }
+ }
+ return resolved_flags;
+}
+
+uint32_t Module::ResolveSymbolContextForFilePath(
+ const char *file_path, uint32_t line, bool check_inlines,
+ lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
+ FileSpec file_spec(file_path);
+ return ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines,
+ resolve_scope, sc_list);
+}
+
+uint32_t Module::ResolveSymbolContextsForFileSpec(
+ const FileSpec &file_spec, uint32_t line, bool check_inlines,
+ lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "Module::ResolveSymbolContextForFilePath (%s:%u, "
+ "check_inlines = %s, resolve_scope = 0x%8.8x)",
+ file_spec.GetPath().c_str(), line,
+ check_inlines ? "yes" : "no", resolve_scope);
+
+ const uint32_t initial_count = sc_list.GetSize();
+
+ SymbolVendor *symbols = GetSymbolVendor();
+ if (symbols)
+ symbols->ResolveSymbolContext(file_spec, line, check_inlines, resolve_scope,
+ sc_list);
+
+ return sc_list.GetSize() - initial_count;
+}
+
+size_t Module::FindGlobalVariables(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ size_t max_matches,
+ VariableList &variables) {
+ SymbolVendor *symbols = GetSymbolVendor();
+ if (symbols)
+ return symbols->FindGlobalVariables(name, parent_decl_ctx, max_matches,
+ variables);
+ return 0;
+}
+
+size_t Module::FindGlobalVariables(const RegularExpression &regex,
+ size_t max_matches,
+ VariableList &variables) {
+ SymbolVendor *symbols = GetSymbolVendor();
+ if (symbols)
+ return symbols->FindGlobalVariables(regex, max_matches, variables);
+ return 0;
+}
+
+size_t Module::FindCompileUnits(const FileSpec &path, bool append,
+ SymbolContextList &sc_list) {
+ if (!append)
+ sc_list.Clear();
+
+ const size_t start_size = sc_list.GetSize();
+ const size_t num_compile_units = GetNumCompileUnits();
+ SymbolContext sc;
+ sc.module_sp = shared_from_this();
+ const bool compare_directory = (bool)path.GetDirectory();
+ for (size_t i = 0; i < num_compile_units; ++i) {
+ sc.comp_unit = GetCompileUnitAtIndex(i).get();
+ if (sc.comp_unit) {
+ if (FileSpec::Equal(*sc.comp_unit, path, compare_directory))
+ sc_list.Append(sc);
+ }
+ }
+ return sc_list.GetSize() - start_size;
+}
+
+Module::LookupInfo::LookupInfo(ConstString name,
+ FunctionNameType name_type_mask,
+ LanguageType language)
+ : m_name(name), m_lookup_name(), m_language(language),
+ m_name_type_mask(eFunctionNameTypeNone),
+ m_match_name_after_lookup(false) {
+ const char *name_cstr = name.GetCString();
+ llvm::StringRef basename;
+ llvm::StringRef context;
+
+ if (name_type_mask & eFunctionNameTypeAuto) {
+ if (CPlusPlusLanguage::IsCPPMangledName(name_cstr))
+ m_name_type_mask = eFunctionNameTypeFull;
+ else if ((language == eLanguageTypeUnknown ||
+ Language::LanguageIsObjC(language)) &&
+ ObjCLanguage::IsPossibleObjCMethodName(name_cstr))
+ m_name_type_mask = eFunctionNameTypeFull;
+ else if (Language::LanguageIsC(language)) {
+ m_name_type_mask = eFunctionNameTypeFull;
+ } else {
+ if ((language == eLanguageTypeUnknown ||
+ Language::LanguageIsObjC(language)) &&
+ ObjCLanguage::IsPossibleObjCSelector(name_cstr))
+ m_name_type_mask |= eFunctionNameTypeSelector;
+
+ CPlusPlusLanguage::MethodName cpp_method(name);
+ basename = cpp_method.GetBasename();
+ if (basename.empty()) {
+ if (CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context,
+ basename))
+ m_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase);
+ else
+ m_name_type_mask |= eFunctionNameTypeFull;
+ } else {
+ m_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase);
+ }
+ }
+ } else {
+ m_name_type_mask = name_type_mask;
+ if (name_type_mask & eFunctionNameTypeMethod ||
+ name_type_mask & eFunctionNameTypeBase) {
+ // If they've asked for a CPP method or function name and it can't be
+ // that, we don't even need to search for CPP methods or names.
+ CPlusPlusLanguage::MethodName cpp_method(name);
+ if (cpp_method.IsValid()) {
+ basename = cpp_method.GetBasename();
+
+ if (!cpp_method.GetQualifiers().empty()) {
+ // There is a "const" or other qualifier following the end of the
+ // function parens, this can't be a eFunctionNameTypeBase
+ m_name_type_mask &= ~(eFunctionNameTypeBase);
+ if (m_name_type_mask == eFunctionNameTypeNone)
+ return;
+ }
+ } else {
+ // If the CPP method parser didn't manage to chop this up, try to fill
+ // in the base name if we can. If a::b::c is passed in, we need to just
+ // look up "c", and then we'll filter the result later.
+ CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context,
+ basename);
+ }
+ }
+
+ if (name_type_mask & eFunctionNameTypeSelector) {
+ if (!ObjCLanguage::IsPossibleObjCSelector(name_cstr)) {
+ m_name_type_mask &= ~(eFunctionNameTypeSelector);
+ if (m_name_type_mask == eFunctionNameTypeNone)
+ return;
+ }
+ }
+
+ // Still try and get a basename in case someone specifies a name type mask
+ // of eFunctionNameTypeFull and a name like "A::func"
+ if (basename.empty()) {
+ if (name_type_mask & eFunctionNameTypeFull &&
+ !CPlusPlusLanguage::IsCPPMangledName(name_cstr)) {
+ CPlusPlusLanguage::MethodName cpp_method(name);
+ basename = cpp_method.GetBasename();
+ if (basename.empty())
+ CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context,
+ basename);
+ }
+ }
+ }
+
+ if (!basename.empty()) {
+ // The name supplied was a partial C++ path like "a::count". In this case
+ // we want to do a lookup on the basename "count" and then make sure any
+ // matching results contain "a::count" so that it would match "b::a::count"
+ // and "a::count". This is why we set "match_name_after_lookup" to true
+ m_lookup_name.SetString(basename);
+ m_match_name_after_lookup = true;
+ } else {
+ // The name is already correct, just use the exact name as supplied, and we
+ // won't need to check if any matches contain "name"
+ m_lookup_name = name;
+ m_match_name_after_lookup = false;
+ }
+}
+
+void Module::LookupInfo::Prune(SymbolContextList &sc_list,
+ size_t start_idx) const {
+ if (m_match_name_after_lookup && m_name) {
+ SymbolContext sc;
+ size_t i = start_idx;
+ while (i < sc_list.GetSize()) {
+ if (!sc_list.GetContextAtIndex(i, sc))
+ break;
+ ConstString full_name(sc.GetFunctionName());
+ if (full_name &&
+ ::strstr(full_name.GetCString(), m_name.GetCString()) == nullptr) {
+ sc_list.RemoveContextAtIndex(i);
+ } else {
+ ++i;
+ }
+ }
+ }
+
+ // If we have only full name matches we might have tried to set breakpoint on
+ // "func" and specified eFunctionNameTypeFull, but we might have found
+ // "a::func()", "a::b::func()", "c::func()", "func()" and "func". Only
+ // "func()" and "func" should end up matching.
+ if (m_name_type_mask == eFunctionNameTypeFull) {
+ SymbolContext sc;
+ size_t i = start_idx;
+ while (i < sc_list.GetSize()) {
+ if (!sc_list.GetContextAtIndex(i, sc))
+ break;
+ // Make sure the mangled and demangled names don't match before we try to
+ // pull anything out
+ ConstString mangled_name(sc.GetFunctionName(Mangled::ePreferMangled));
+ ConstString full_name(sc.GetFunctionName());
+ if (mangled_name != m_name && full_name != m_name)
+ {
+ CPlusPlusLanguage::MethodName cpp_method(full_name);
+ if (cpp_method.IsValid()) {
+ if (cpp_method.GetContext().empty()) {
+ if (cpp_method.GetBasename().compare(m_name.GetStringRef()) != 0) {
+ sc_list.RemoveContextAtIndex(i);
+ continue;
+ }
+ } else {
+ std::string qualified_name;
+ llvm::StringRef anon_prefix("(anonymous namespace)");
+ if (cpp_method.GetContext() == anon_prefix)
+ qualified_name = cpp_method.GetBasename().str();
+ else
+ qualified_name = cpp_method.GetScopeQualifiedName();
+ if (qualified_name != m_name.GetCString()) {
+ sc_list.RemoveContextAtIndex(i);
+ continue;
+ }
+ }
+ }
+ }
+ ++i;
+ }
+ }
+}
+
+size_t Module::FindFunctions(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask,
+ bool include_symbols, bool include_inlines,
+ bool append, SymbolContextList &sc_list) {
+ if (!append)
+ sc_list.Clear();
+
+ const size_t old_size = sc_list.GetSize();
+
+ // Find all the functions (not symbols, but debug information functions...
+ SymbolVendor *symbols = GetSymbolVendor();
+
+ if (name_type_mask & eFunctionNameTypeAuto) {
+ LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown);
+
+ if (symbols) {
+ symbols->FindFunctions(lookup_info.GetLookupName(), parent_decl_ctx,
+ lookup_info.GetNameTypeMask(), include_inlines,
+ append, sc_list);
+
+ // Now check our symbol table for symbols that are code symbols if
+ // requested
+ if (include_symbols) {
+ Symtab *symtab = symbols->GetSymtab();
+ if (symtab)
+ symtab->FindFunctionSymbols(lookup_info.GetLookupName(),
+ lookup_info.GetNameTypeMask(), sc_list);
+ }
+ }
+
+ const size_t new_size = sc_list.GetSize();
+
+ if (old_size < new_size)
+ lookup_info.Prune(sc_list, old_size);
+ } else {
+ if (symbols) {
+ symbols->FindFunctions(name, parent_decl_ctx, name_type_mask,
+ include_inlines, append, sc_list);
+
+ // Now check our symbol table for symbols that are code symbols if
+ // requested
+ if (include_symbols) {
+ Symtab *symtab = symbols->GetSymtab();
+ if (symtab)
+ symtab->FindFunctionSymbols(name, name_type_mask, sc_list);
+ }
+ }
+ }
+
+ return sc_list.GetSize() - old_size;
+}
+
+size_t Module::FindFunctions(const RegularExpression &regex,
+ bool include_symbols, bool include_inlines,
+ bool append, SymbolContextList &sc_list) {
+ if (!append)
+ sc_list.Clear();
+
+ const size_t start_size = sc_list.GetSize();
+
+ SymbolVendor *symbols = GetSymbolVendor();
+ if (symbols) {
+ symbols->FindFunctions(regex, include_inlines, append, sc_list);
+
+ // Now check our symbol table for symbols that are code symbols if
+ // requested
+ if (include_symbols) {
+ Symtab *symtab = symbols->GetSymtab();
+ if (symtab) {
+ std::vector<uint32_t> symbol_indexes;
+ symtab->AppendSymbolIndexesMatchingRegExAndType(
+ regex, eSymbolTypeAny, Symtab::eDebugAny, Symtab::eVisibilityAny,
+ symbol_indexes);
+ const size_t num_matches = symbol_indexes.size();
+ if (num_matches) {
+ SymbolContext sc(this);
+ const size_t end_functions_added_index = sc_list.GetSize();
+ size_t num_functions_added_to_sc_list =
+ end_functions_added_index - start_size;
+ if (num_functions_added_to_sc_list == 0) {
+ // No functions were added, just symbols, so we can just append
+ // them
+ for (size_t i = 0; i < num_matches; ++i) {
+ sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]);
+ SymbolType sym_type = sc.symbol->GetType();
+ if (sc.symbol && (sym_type == eSymbolTypeCode ||
+ sym_type == eSymbolTypeResolver))
+ sc_list.Append(sc);
+ }
+ } else {
+ typedef std::map<lldb::addr_t, uint32_t> FileAddrToIndexMap;
+ FileAddrToIndexMap file_addr_to_index;
+ for (size_t i = start_size; i < end_functions_added_index; ++i) {
+ const SymbolContext &sc = sc_list[i];
+ if (sc.block)
+ continue;
+ file_addr_to_index[sc.function->GetAddressRange()
+ .GetBaseAddress()
+ .GetFileAddress()] = i;
+ }
+
+ FileAddrToIndexMap::const_iterator end = file_addr_to_index.end();
+ // Functions were added so we need to merge symbols into any
+ // existing function symbol contexts
+ for (size_t i = start_size; i < num_matches; ++i) {
+ sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]);
+ SymbolType sym_type = sc.symbol->GetType();
+ if (sc.symbol && sc.symbol->ValueIsAddress() &&
+ (sym_type == eSymbolTypeCode ||
+ sym_type == eSymbolTypeResolver)) {
+ FileAddrToIndexMap::const_iterator pos =
+ file_addr_to_index.find(
+ sc.symbol->GetAddressRef().GetFileAddress());
+ if (pos == end)
+ sc_list.Append(sc);
+ else
+ sc_list[pos->second].symbol = sc.symbol;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return sc_list.GetSize() - start_size;
+}
+
+void Module::FindAddressesForLine(const lldb::TargetSP target_sp,
+ const FileSpec &file, uint32_t line,
+ Function *function,
+ std::vector<Address> &output_local,
+ std::vector<Address> &output_extern) {
+ SearchFilterByModule filter(target_sp, m_file);
+ AddressResolverFileLine resolver(file, line, true);
+ resolver.ResolveAddress(filter);
+
+ for (size_t n = 0; n < resolver.GetNumberOfAddresses(); n++) {
+ Address addr = resolver.GetAddressRangeAtIndex(n).GetBaseAddress();
+ Function *f = addr.CalculateSymbolContextFunction();
+ if (f && f == function)
+ output_local.push_back(addr);
+ else
+ output_extern.push_back(addr);
+ }
+}
+
+size_t Module::FindTypes_Impl(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ bool append, size_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ TypeMap &types) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+ SymbolVendor *symbols = GetSymbolVendor();
+ if (symbols)
+ return symbols->FindTypes(name, parent_decl_ctx, append, max_matches,
+ searched_symbol_files, types);
+ return 0;
+}
+
+size_t Module::FindTypesInNamespace(ConstString type_name,
+ const CompilerDeclContext *parent_decl_ctx,
+ size_t max_matches, TypeList &type_list) {
+ const bool append = true;
+ TypeMap types_map;
+ llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
+ size_t num_types =
+ FindTypes_Impl(type_name, parent_decl_ctx, append, max_matches,
+ searched_symbol_files, types_map);
+ if (num_types > 0) {
+ SymbolContext sc;
+ sc.module_sp = shared_from_this();
+ sc.SortTypeList(types_map, type_list);
+ }
+ return num_types;
+}
+
+lldb::TypeSP Module::FindFirstType(const SymbolContext &sc,
+ ConstString name, bool exact_match) {
+ TypeList type_list;
+ llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
+ const size_t num_matches =
+ FindTypes(name, exact_match, 1, searched_symbol_files, type_list);
+ if (num_matches)
+ return type_list.GetTypeAtIndex(0);
+ return TypeSP();
+}
+
+size_t Module::FindTypes(
+ ConstString name, bool exact_match, size_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ TypeList &types) {
+ size_t num_matches = 0;
+ const char *type_name_cstr = name.GetCString();
+ llvm::StringRef type_scope;
+ llvm::StringRef type_basename;
+ const bool append = true;
+ TypeClass type_class = eTypeClassAny;
+ TypeMap typesmap;
+
+ if (Type::GetTypeScopeAndBasename(type_name_cstr, type_scope, type_basename,
+ type_class)) {
+ // Check if "name" starts with "::" which means the qualified type starts
+ // from the root namespace and implies and exact match. The typenames we
+ // get back from clang do not start with "::" so we need to strip this off
+ // in order to get the qualified names to match
+ exact_match = type_scope.consume_front("::");
+
+ ConstString type_basename_const_str(type_basename);
+ if (FindTypes_Impl(type_basename_const_str, nullptr, append, max_matches,
+ searched_symbol_files, typesmap)) {
+ typesmap.RemoveMismatchedTypes(type_scope, type_basename, type_class,
+ exact_match);
+ num_matches = typesmap.GetSize();
+ }
+ } else {
+ // The type is not in a namespace/class scope, just search for it by
+ // basename
+ if (type_class != eTypeClassAny && !type_basename.empty()) {
+ // The "type_name_cstr" will have been modified if we have a valid type
+ // class prefix (like "struct", "class", "union", "typedef" etc).
+ FindTypes_Impl(ConstString(type_basename), nullptr, append, UINT_MAX,
+ searched_symbol_files, typesmap);
+ typesmap.RemoveMismatchedTypes(type_scope, type_basename, type_class,
+ exact_match);
+ num_matches = typesmap.GetSize();
+ } else {
+ num_matches = FindTypes_Impl(name, nullptr, append, UINT_MAX,
+ searched_symbol_files, typesmap);
+ if (exact_match) {
+ std::string name_str(name.AsCString(""));
+ typesmap.RemoveMismatchedTypes(type_scope, name_str, type_class,
+ exact_match);
+ num_matches = typesmap.GetSize();
+ }
+ }
+ }
+ if (num_matches > 0) {
+ SymbolContext sc;
+ sc.module_sp = shared_from_this();
+ sc.SortTypeList(typesmap, types);
+ }
+ return num_matches;
+}
+
+SymbolVendor *Module::GetSymbolVendor(bool can_create,
+ lldb_private::Stream *feedback_strm) {
+ if (!m_did_load_symbol_vendor.load()) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_did_load_symbol_vendor.load() && can_create) {
+ ObjectFile *obj_file = GetObjectFile();
+ if (obj_file != nullptr) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+ m_symfile_up.reset(
+ SymbolVendor::FindPlugin(shared_from_this(), feedback_strm));
+ m_did_load_symbol_vendor = true;
+ }
+ }
+ }
+ return m_symfile_up.get();
+}
+
+void Module::SetFileSpecAndObjectName(const FileSpec &file,
+ ConstString object_name) {
+ // Container objects whose paths do not specify a file directly can call this
+ // function to correct the file and object names.
+ m_file = file;
+ m_mod_time = FileSystem::Instance().GetModificationTime(file);
+ m_object_name = object_name;
+}
+
+const ArchSpec &Module::GetArchitecture() const { return m_arch; }
+
+std::string Module::GetSpecificationDescription() const {
+ std::string spec(GetFileSpec().GetPath());
+ if (m_object_name) {
+ spec += '(';
+ spec += m_object_name.GetCString();
+ spec += ')';
+ }
+ return spec;
+}
+
+void Module::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ if (level >= eDescriptionLevelFull) {
+ if (m_arch.IsValid())
+ s->Printf("(%s) ", m_arch.GetArchitectureName());
+ }
+
+ if (level == eDescriptionLevelBrief) {
+ const char *filename = m_file.GetFilename().GetCString();
+ if (filename)
+ s->PutCString(filename);
+ } else {
+ char path[PATH_MAX];
+ if (m_file.GetPath(path, sizeof(path)))
+ s->PutCString(path);
+ }
+
+ const char *object_name = m_object_name.GetCString();
+ if (object_name)
+ s->Printf("(%s)", object_name);
+}
+
+void Module::ReportError(const char *format, ...) {
+ if (format && format[0]) {
+ StreamString strm;
+ strm.PutCString("error: ");
+ GetDescription(&strm, lldb::eDescriptionLevelBrief);
+ strm.PutChar(' ');
+ va_list args;
+ va_start(args, format);
+ strm.PrintfVarArg(format, args);
+ va_end(args);
+
+ const int format_len = strlen(format);
+ if (format_len > 0) {
+ const char last_char = format[format_len - 1];
+ if (last_char != '\n' && last_char != '\r')
+ strm.EOL();
+ }
+ Host::SystemLog(Host::eSystemLogError, "%s", strm.GetData());
+ }
+}
+
+bool Module::FileHasChanged() const {
+ if (!m_file_has_changed)
+ m_file_has_changed =
+ (FileSystem::Instance().GetModificationTime(m_file) != m_mod_time);
+ return m_file_has_changed;
+}
+
+void Module::ReportErrorIfModifyDetected(const char *format, ...) {
+ if (!m_first_file_changed_log) {
+ if (FileHasChanged()) {
+ m_first_file_changed_log = true;
+ if (format) {
+ StreamString strm;
+ strm.PutCString("error: the object file ");
+ GetDescription(&strm, lldb::eDescriptionLevelFull);
+ strm.PutCString(" has been modified\n");
+
+ va_list args;
+ va_start(args, format);
+ strm.PrintfVarArg(format, args);
+ va_end(args);
+
+ const int format_len = strlen(format);
+ if (format_len > 0) {
+ const char last_char = format[format_len - 1];
+ if (last_char != '\n' && last_char != '\r')
+ strm.EOL();
+ }
+ strm.PutCString("The debug session should be aborted as the original "
+ "debug information has been overwritten.\n");
+ Host::SystemLog(Host::eSystemLogError, "%s", strm.GetData());
+ }
+ }
+ }
+}
+
+void Module::ReportWarning(const char *format, ...) {
+ if (format && format[0]) {
+ StreamString strm;
+ strm.PutCString("warning: ");
+ GetDescription(&strm, lldb::eDescriptionLevelFull);
+ strm.PutChar(' ');
+
+ va_list args;
+ va_start(args, format);
+ strm.PrintfVarArg(format, args);
+ va_end(args);
+
+ const int format_len = strlen(format);
+ if (format_len > 0) {
+ const char last_char = format[format_len - 1];
+ if (last_char != '\n' && last_char != '\r')
+ strm.EOL();
+ }
+ Host::SystemLog(Host::eSystemLogWarning, "%s", strm.GetData());
+ }
+}
+
+void Module::LogMessage(Log *log, const char *format, ...) {
+ if (log != nullptr) {
+ StreamString log_message;
+ GetDescription(&log_message, lldb::eDescriptionLevelFull);
+ log_message.PutCString(": ");
+ va_list args;
+ va_start(args, format);
+ log_message.PrintfVarArg(format, args);
+ va_end(args);
+ log->PutCString(log_message.GetData());
+ }
+}
+
+void Module::LogMessageVerboseBacktrace(Log *log, const char *format, ...) {
+ if (log != nullptr) {
+ StreamString log_message;
+ GetDescription(&log_message, lldb::eDescriptionLevelFull);
+ log_message.PutCString(": ");
+ va_list args;
+ va_start(args, format);
+ log_message.PrintfVarArg(format, args);
+ va_end(args);
+ if (log->GetVerbose()) {
+ std::string back_trace;
+ llvm::raw_string_ostream stream(back_trace);
+ llvm::sys::PrintStackTrace(stream);
+ log_message.PutCString(back_trace);
+ }
+ log->PutCString(log_message.GetData());
+ }
+}
+
+void Module::Dump(Stream *s) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this);
+ s->Indent();
+ s->Printf("Module %s%s%s%s\n", m_file.GetPath().c_str(),
+ m_object_name ? "(" : "",
+ m_object_name ? m_object_name.GetCString() : "",
+ m_object_name ? ")" : "");
+
+ s->IndentMore();
+
+ ObjectFile *objfile = GetObjectFile();
+ if (objfile)
+ objfile->Dump(s);
+
+ SymbolVendor *symbols = GetSymbolVendor();
+ if (symbols)
+ symbols->Dump(s);
+
+ s->IndentLess();
+}
+
+TypeList *Module::GetTypeList() {
+ SymbolVendor *symbols = GetSymbolVendor();
+ if (symbols)
+ return &symbols->GetTypeList();
+ return nullptr;
+}
+
+ConstString Module::GetObjectName() const { return m_object_name; }
+
+ObjectFile *Module::GetObjectFile() {
+ if (!m_did_load_objfile.load()) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_did_load_objfile.load()) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "Module::GetObjectFile () module = %s",
+ GetFileSpec().GetFilename().AsCString(""));
+ DataBufferSP data_sp;
+ lldb::offset_t data_offset = 0;
+ const lldb::offset_t file_size =
+ FileSystem::Instance().GetByteSize(m_file);
+ if (file_size > m_object_offset) {
+ m_did_load_objfile = true;
+ m_objfile_sp = ObjectFile::FindPlugin(
+ shared_from_this(), &m_file, m_object_offset,
+ file_size - m_object_offset, data_sp, data_offset);
+ if (m_objfile_sp) {
+ // Once we get the object file, update our module with the object
+ // file's architecture since it might differ in vendor/os if some
+ // parts were unknown. But since the matching arch might already be
+ // more specific than the generic COFF architecture, only merge in
+ // those values that overwrite unspecified unknown values.
+ m_arch.MergeFrom(m_objfile_sp->GetArchitecture());
+ } else {
+ ReportError("failed to load objfile for %s",
+ GetFileSpec().GetPath().c_str());
+ }
+ }
+ }
+ }
+ return m_objfile_sp.get();
+}
+
+SectionList *Module::GetSectionList() {
+ // Populate m_sections_up with sections from objfile.
+ if (!m_sections_up) {
+ ObjectFile *obj_file = GetObjectFile();
+ if (obj_file != nullptr)
+ obj_file->CreateSections(*GetUnifiedSectionList());
+ }
+ return m_sections_up.get();
+}
+
+void Module::SectionFileAddressesChanged() {
+ ObjectFile *obj_file = GetObjectFile();
+ if (obj_file)
+ obj_file->SectionFileAddressesChanged();
+ SymbolVendor *sym_vendor = GetSymbolVendor();
+ if (sym_vendor != nullptr)
+ sym_vendor->SectionFileAddressesChanged();
+}
+
+UnwindTable &Module::GetUnwindTable() {
+ if (!m_unwind_table)
+ m_unwind_table.emplace(*this);
+ return *m_unwind_table;
+}
+
+SectionList *Module::GetUnifiedSectionList() {
+ if (!m_sections_up)
+ m_sections_up = llvm::make_unique<SectionList>();
+ return m_sections_up.get();
+}
+
+const Symbol *Module::FindFirstSymbolWithNameAndType(ConstString name,
+ SymbolType symbol_type) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat, "Module::FindFirstSymbolWithNameAndType (name = %s, type = %i)",
+ name.AsCString(), symbol_type);
+ SymbolVendor *sym_vendor = GetSymbolVendor();
+ if (sym_vendor) {
+ Symtab *symtab = sym_vendor->GetSymtab();
+ if (symtab)
+ return symtab->FindFirstSymbolWithNameAndType(
+ name, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny);
+ }
+ return nullptr;
+}
+void Module::SymbolIndicesToSymbolContextList(
+ Symtab *symtab, std::vector<uint32_t> &symbol_indexes,
+ SymbolContextList &sc_list) {
+ // No need to protect this call using m_mutex all other method calls are
+ // already thread safe.
+
+ size_t num_indices = symbol_indexes.size();
+ if (num_indices > 0) {
+ SymbolContext sc;
+ CalculateSymbolContext(&sc);
+ for (size_t i = 0; i < num_indices; i++) {
+ sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]);
+ if (sc.symbol)
+ sc_list.Append(sc);
+ }
+ }
+}
+
+size_t Module::FindFunctionSymbols(ConstString name,
+ uint32_t name_type_mask,
+ SymbolContextList &sc_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "Module::FindSymbolsFunctions (name = %s, mask = 0x%8.8x)",
+ name.AsCString(), name_type_mask);
+ SymbolVendor *sym_vendor = GetSymbolVendor();
+ if (sym_vendor) {
+ Symtab *symtab = sym_vendor->GetSymtab();
+ if (symtab)
+ return symtab->FindFunctionSymbols(name, name_type_mask, sc_list);
+ }
+ return 0;
+}
+
+size_t Module::FindSymbolsWithNameAndType(ConstString name,
+ SymbolType symbol_type,
+ SymbolContextList &sc_list) {
+ // No need to protect this call using m_mutex all other method calls are
+ // already thread safe.
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat, "Module::FindSymbolsWithNameAndType (name = %s, type = %i)",
+ name.AsCString(), symbol_type);
+ const size_t initial_size = sc_list.GetSize();
+ SymbolVendor *sym_vendor = GetSymbolVendor();
+ if (sym_vendor) {
+ Symtab *symtab = sym_vendor->GetSymtab();
+ if (symtab) {
+ std::vector<uint32_t> symbol_indexes;
+ symtab->FindAllSymbolsWithNameAndType(name, symbol_type, symbol_indexes);
+ SymbolIndicesToSymbolContextList(symtab, symbol_indexes, sc_list);
+ }
+ }
+ return sc_list.GetSize() - initial_size;
+}
+
+size_t Module::FindSymbolsMatchingRegExAndType(const RegularExpression &regex,
+ SymbolType symbol_type,
+ SymbolContextList &sc_list) {
+ // No need to protect this call using m_mutex all other method calls are
+ // already thread safe.
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat,
+ "Module::FindSymbolsMatchingRegExAndType (regex = %s, type = %i)",
+ regex.GetText().str().c_str(), symbol_type);
+ const size_t initial_size = sc_list.GetSize();
+ SymbolVendor *sym_vendor = GetSymbolVendor();
+ if (sym_vendor) {
+ Symtab *symtab = sym_vendor->GetSymtab();
+ if (symtab) {
+ std::vector<uint32_t> symbol_indexes;
+ symtab->FindAllSymbolsMatchingRexExAndType(
+ regex, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny,
+ symbol_indexes);
+ SymbolIndicesToSymbolContextList(symtab, symbol_indexes, sc_list);
+ }
+ }
+ return sc_list.GetSize() - initial_size;
+}
+
+void Module::PreloadSymbols() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ SymbolVendor * sym_vendor = GetSymbolVendor();
+ if (!sym_vendor) {
+ return;
+ }
+ // Prime the symbol file first, since it adds symbols to the symbol table.
+ if (SymbolFile *symbol_file = sym_vendor->GetSymbolFile()) {
+ symbol_file->PreloadSymbols();
+ }
+ // Now we can prime the symbol table.
+ if (Symtab * symtab = sym_vendor->GetSymtab()) {
+ symtab->PreloadSymbols();
+ }
+}
+
+void Module::SetSymbolFileFileSpec(const FileSpec &file) {
+ if (!FileSystem::Instance().Exists(file))
+ return;
+ if (m_symfile_up) {
+ // Remove any sections in the unified section list that come from the
+ // current symbol vendor.
+ SectionList *section_list = GetSectionList();
+ SymbolFile *symbol_file = m_symfile_up->GetSymbolFile();
+ if (section_list && symbol_file) {
+ ObjectFile *obj_file = symbol_file->GetObjectFile();
+ // Make sure we have an object file and that the symbol vendor's objfile
+ // isn't the same as the module's objfile before we remove any sections
+ // for it...
+ if (obj_file) {
+ // Check to make sure we aren't trying to specify the file we already
+ // have
+ if (obj_file->GetFileSpec() == file) {
+ // We are being told to add the exact same file that we already have
+ // we don't have to do anything.
+ return;
+ }
+
+ // Cleare the current symtab as we are going to replace it with a new
+ // one
+ obj_file->ClearSymtab();
+
+ // Clear the unwind table too, as that may also be affected by the
+ // symbol file information.
+ m_unwind_table.reset();
+
+ // The symbol file might be a directory bundle ("/tmp/a.out.dSYM")
+ // instead of a full path to the symbol file within the bundle
+ // ("/tmp/a.out.dSYM/Contents/Resources/DWARF/a.out"). So we need to
+ // check this
+
+ if (FileSystem::Instance().IsDirectory(file)) {
+ std::string new_path(file.GetPath());
+ std::string old_path(obj_file->GetFileSpec().GetPath());
+ if (old_path.find(new_path) == 0) {
+ // We specified the same bundle as the symbol file that we already
+ // have
+ return;
+ }
+ }
+
+ if (obj_file != m_objfile_sp.get()) {
+ size_t num_sections = section_list->GetNumSections(0);
+ for (size_t idx = num_sections; idx > 0; --idx) {
+ lldb::SectionSP section_sp(
+ section_list->GetSectionAtIndex(idx - 1));
+ if (section_sp->GetObjectFile() == obj_file) {
+ section_list->DeleteSection(idx - 1);
+ }
+ }
+ }
+ }
+ }
+ // Keep all old symbol files around in case there are any lingering type
+ // references in any SBValue objects that might have been handed out.
+ m_old_symfiles.push_back(std::move(m_symfile_up));
+ }
+ m_symfile_spec = file;
+ m_symfile_up.reset();
+ m_did_load_symbol_vendor = false;
+}
+
+bool Module::IsExecutable() {
+ if (GetObjectFile() == nullptr)
+ return false;
+ else
+ return GetObjectFile()->IsExecutable();
+}
+
+bool Module::IsLoadedInTarget(Target *target) {
+ ObjectFile *obj_file = GetObjectFile();
+ if (obj_file) {
+ SectionList *sections = GetSectionList();
+ if (sections != nullptr) {
+ size_t num_sections = sections->GetSize();
+ for (size_t sect_idx = 0; sect_idx < num_sections; sect_idx++) {
+ SectionSP section_sp = sections->GetSectionAtIndex(sect_idx);
+ if (section_sp->GetLoadBaseAddress(target) != LLDB_INVALID_ADDRESS) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool Module::LoadScriptingResourceInTarget(Target *target, Status &error,
+ Stream *feedback_stream) {
+ if (!target) {
+ error.SetErrorString("invalid destination Target");
+ return false;
+ }
+
+ LoadScriptFromSymFile should_load =
+ target->TargetProperties::GetLoadScriptFromSymbolFile();
+
+ if (should_load == eLoadScriptFromSymFileFalse)
+ return false;
+
+ Debugger &debugger = target->GetDebugger();
+ const ScriptLanguage script_language = debugger.GetScriptLanguage();
+ if (script_language != eScriptLanguageNone) {
+
+ PlatformSP platform_sp(target->GetPlatform());
+
+ if (!platform_sp) {
+ error.SetErrorString("invalid Platform");
+ return false;
+ }
+
+ FileSpecList file_specs = platform_sp->LocateExecutableScriptingResources(
+ target, *this, feedback_stream);
+
+ const uint32_t num_specs = file_specs.GetSize();
+ if (num_specs) {
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ if (script_interpreter) {
+ for (uint32_t i = 0; i < num_specs; ++i) {
+ FileSpec scripting_fspec(file_specs.GetFileSpecAtIndex(i));
+ if (scripting_fspec &&
+ FileSystem::Instance().Exists(scripting_fspec)) {
+ if (should_load == eLoadScriptFromSymFileWarn) {
+ if (feedback_stream)
+ feedback_stream->Printf(
+ "warning: '%s' contains a debug script. To run this script "
+ "in "
+ "this debug session:\n\n command script import "
+ "\"%s\"\n\n"
+ "To run all discovered debug scripts in this session:\n\n"
+ " settings set target.load-script-from-symbol-file "
+ "true\n",
+ GetFileSpec().GetFileNameStrippingExtension().GetCString(),
+ scripting_fspec.GetPath().c_str());
+ return false;
+ }
+ StreamString scripting_stream;
+ scripting_fspec.Dump(&scripting_stream);
+ const bool can_reload = true;
+ const bool init_lldb_globals = false;
+ bool did_load = script_interpreter->LoadScriptingModule(
+ scripting_stream.GetData(), can_reload, init_lldb_globals,
+ error);
+ if (!did_load)
+ return false;
+ }
+ }
+ } else {
+ error.SetErrorString("invalid ScriptInterpreter");
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool Module::SetArchitecture(const ArchSpec &new_arch) {
+ if (!m_arch.IsValid()) {
+ m_arch = new_arch;
+ return true;
+ }
+ return m_arch.IsCompatibleMatch(new_arch);
+}
+
+bool Module::SetLoadAddress(Target &target, lldb::addr_t value,
+ bool value_is_offset, bool &changed) {
+ ObjectFile *object_file = GetObjectFile();
+ if (object_file != nullptr) {
+ changed = object_file->SetLoadAddress(target, value, value_is_offset);
+ return true;
+ } else {
+ changed = false;
+ }
+ return false;
+}
+
+bool Module::MatchesModuleSpec(const ModuleSpec &module_ref) {
+ const UUID &uuid = module_ref.GetUUID();
+
+ if (uuid.IsValid()) {
+ // If the UUID matches, then nothing more needs to match...
+ return (uuid == GetUUID());
+ }
+
+ const FileSpec &file_spec = module_ref.GetFileSpec();
+ if (file_spec) {
+ if (!FileSpec::Equal(file_spec, m_file, (bool)file_spec.GetDirectory()) &&
+ !FileSpec::Equal(file_spec, m_platform_file,
+ (bool)file_spec.GetDirectory()))
+ return false;
+ }
+
+ const FileSpec &platform_file_spec = module_ref.GetPlatformFileSpec();
+ if (platform_file_spec) {
+ if (!FileSpec::Equal(platform_file_spec, GetPlatformFileSpec(),
+ (bool)platform_file_spec.GetDirectory()))
+ return false;
+ }
+
+ const ArchSpec &arch = module_ref.GetArchitecture();
+ if (arch.IsValid()) {
+ if (!m_arch.IsCompatibleMatch(arch))
+ return false;
+ }
+
+ ConstString object_name = module_ref.GetObjectName();
+ if (object_name) {
+ if (object_name != GetObjectName())
+ return false;
+ }
+ return true;
+}
+
+bool Module::FindSourceFile(const FileSpec &orig_spec,
+ FileSpec &new_spec) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return m_source_mappings.FindFile(orig_spec, new_spec);
+}
+
+bool Module::RemapSourceFile(llvm::StringRef path,
+ std::string &new_path) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return m_source_mappings.RemapPath(path, new_path);
+}
+
+llvm::VersionTuple Module::GetVersion() {
+ if (ObjectFile *obj_file = GetObjectFile())
+ return obj_file->GetVersion();
+ return llvm::VersionTuple();
+}
+
+bool Module::GetIsDynamicLinkEditor() {
+ ObjectFile *obj_file = GetObjectFile();
+
+ if (obj_file)
+ return obj_file->GetIsDynamicLinkEditor();
+
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ModuleChild.cpp b/contrib/llvm-project/lldb/source/Core/ModuleChild.cpp
new file mode 100644
index 000000000000..2fcb2ffca137
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ModuleChild.cpp
@@ -0,0 +1,28 @@
+//===-- ModuleChild.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ModuleChild.h"
+
+using namespace lldb_private;
+
+ModuleChild::ModuleChild(const lldb::ModuleSP &module_sp)
+ : m_module_wp(module_sp) {}
+
+ModuleChild::~ModuleChild() {}
+
+const ModuleChild &ModuleChild::operator=(const ModuleChild &rhs) {
+ if (this != &rhs)
+ m_module_wp = rhs.m_module_wp;
+ return *this;
+}
+
+lldb::ModuleSP ModuleChild::GetModule() const { return m_module_wp.lock(); }
+
+void ModuleChild::SetModule(const lldb::ModuleSP &module_sp) {
+ m_module_wp = module_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ModuleList.cpp b/contrib/llvm-project/lldb/source/Core/ModuleList.cpp
new file mode 100644
index 000000000000..9d795f9e5586
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ModuleList.cpp
@@ -0,0 +1,1058 @@
+//===-- ModuleList.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/OptionValueFileSpec.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Symbol/LocateSymbolFile.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/UUID.h"
+#include "lldb/lldb-defines.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/PosixApi.h"
+#endif
+
+#include "clang/Driver/Driver.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <utility>
+
+namespace lldb_private {
+class Function;
+}
+namespace lldb_private {
+class RegularExpression;
+}
+namespace lldb_private {
+class Stream;
+}
+namespace lldb_private {
+class SymbolFile;
+}
+namespace lldb_private {
+class Target;
+}
+namespace lldb_private {
+class TypeList;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"enable-external-lookup", OptionValue::eTypeBoolean, true, true, nullptr,
+ {},
+ "Control the use of external tools and repositories to locate symbol "
+ "files. Directories listed in target.debug-file-search-paths and "
+ "directory of the executable are always checked first for separate debug "
+ "info files. Then depending on this setting: "
+ "On macOS, Spotlight would be also used to locate a matching .dSYM "
+ "bundle based on the UUID of the executable. "
+ "On NetBSD, directory /usr/libdata/debug would be also searched. "
+ "On platforms other than NetBSD directory /usr/lib/debug would be "
+ "also searched."
+ },
+ {"clang-modules-cache-path", OptionValue::eTypeFileSpec, true, 0, nullptr,
+ {},
+ "The path to the clang modules cache directory (-fmodules-cache-path)."}};
+
+enum { ePropertyEnableExternalLookup, ePropertyClangModulesCachePath };
+
+} // namespace
+
+ModuleListProperties::ModuleListProperties() {
+ m_collection_sp =
+ std::make_shared<OptionValueProperties>(ConstString("symbols"));
+ m_collection_sp->Initialize(g_properties);
+
+ llvm::SmallString<128> path;
+ clang::driver::Driver::getDefaultModuleCachePath(path);
+ SetClangModulesCachePath(path);
+}
+
+bool ModuleListProperties::GetEnableExternalLookup() const {
+ const uint32_t idx = ePropertyEnableExternalLookup;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool ModuleListProperties::SetEnableExternalLookup(bool new_value) {
+ return m_collection_sp->SetPropertyAtIndexAsBoolean(
+ nullptr, ePropertyEnableExternalLookup, new_value);
+}
+
+FileSpec ModuleListProperties::GetClangModulesCachePath() const {
+ return m_collection_sp
+ ->GetPropertyAtIndexAsOptionValueFileSpec(nullptr, false,
+ ePropertyClangModulesCachePath)
+ ->GetCurrentValue();
+}
+
+bool ModuleListProperties::SetClangModulesCachePath(llvm::StringRef path) {
+ return m_collection_sp->SetPropertyAtIndexAsString(
+ nullptr, ePropertyClangModulesCachePath, path);
+}
+
+ModuleList::ModuleList()
+ : m_modules(), m_modules_mutex(), m_notifier(nullptr) {}
+
+ModuleList::ModuleList(const ModuleList &rhs)
+ : m_modules(), m_modules_mutex(), m_notifier(nullptr) {
+ std::lock_guard<std::recursive_mutex> lhs_guard(m_modules_mutex);
+ std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_modules_mutex);
+ m_modules = rhs.m_modules;
+}
+
+ModuleList::ModuleList(ModuleList::Notifier *notifier)
+ : m_modules(), m_modules_mutex(), m_notifier(notifier) {}
+
+const ModuleList &ModuleList::operator=(const ModuleList &rhs) {
+ if (this != &rhs) {
+ std::lock(m_modules_mutex, rhs.m_modules_mutex);
+ std::lock_guard<std::recursive_mutex> lhs_guard(m_modules_mutex,
+ std::adopt_lock);
+ std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_modules_mutex,
+ std::adopt_lock);
+ m_modules = rhs.m_modules;
+ }
+ return *this;
+}
+
+ModuleList::~ModuleList() = default;
+
+void ModuleList::AppendImpl(const ModuleSP &module_sp, bool use_notifier) {
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ m_modules.push_back(module_sp);
+ if (use_notifier && m_notifier)
+ m_notifier->NotifyModuleAdded(*this, module_sp);
+ }
+}
+
+void ModuleList::Append(const ModuleSP &module_sp, bool notify) {
+ AppendImpl(module_sp, notify);
+}
+
+void ModuleList::ReplaceEquivalent(const ModuleSP &module_sp) {
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+
+ // First remove any equivalent modules. Equivalent modules are modules
+ // whose path, platform path and architecture match.
+ ModuleSpec equivalent_module_spec(module_sp->GetFileSpec(),
+ module_sp->GetArchitecture());
+ equivalent_module_spec.GetPlatformFileSpec() =
+ module_sp->GetPlatformFileSpec();
+
+ size_t idx = 0;
+ while (idx < m_modules.size()) {
+ ModuleSP module_sp(m_modules[idx]);
+ if (module_sp->MatchesModuleSpec(equivalent_module_spec))
+ RemoveImpl(m_modules.begin() + idx);
+ else
+ ++idx;
+ }
+ // Now add the new module to the list
+ Append(module_sp);
+ }
+}
+
+bool ModuleList::AppendIfNeeded(const ModuleSP &module_sp, bool notify) {
+ if (module_sp) {
+ 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_sp.get())
+ return false; // Already in the list
+ }
+ // Only push module_sp on the list if it wasn't already in there.
+ Append(module_sp, notify);
+ return true;
+ }
+ return false;
+}
+
+void ModuleList::Append(const ModuleList &module_list) {
+ for (auto pos : module_list.m_modules)
+ Append(pos);
+}
+
+bool ModuleList::AppendIfNeeded(const ModuleList &module_list) {
+ bool any_in = false;
+ for (auto pos : module_list.m_modules) {
+ if (AppendIfNeeded(pos))
+ any_in = true;
+ }
+ return any_in;
+}
+
+bool ModuleList::RemoveImpl(const ModuleSP &module_sp, bool use_notifier) {
+ if (module_sp) {
+ 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_sp.get()) {
+ m_modules.erase(pos);
+ if (use_notifier && m_notifier)
+ m_notifier->NotifyModuleRemoved(*this, module_sp);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ModuleList::collection::iterator
+ModuleList::RemoveImpl(ModuleList::collection::iterator pos,
+ bool use_notifier) {
+ ModuleSP module_sp(*pos);
+ collection::iterator retval = m_modules.erase(pos);
+ if (use_notifier && m_notifier)
+ m_notifier->NotifyModuleRemoved(*this, module_sp);
+ return retval;
+}
+
+bool ModuleList::Remove(const ModuleSP &module_sp, bool notify) {
+ return RemoveImpl(module_sp, notify);
+}
+
+bool ModuleList::ReplaceModule(const lldb::ModuleSP &old_module_sp,
+ const lldb::ModuleSP &new_module_sp) {
+ if (!RemoveImpl(old_module_sp, false))
+ return false;
+ AppendImpl(new_module_sp, false);
+ if (m_notifier)
+ m_notifier->NotifyModuleUpdated(*this, old_module_sp, new_module_sp);
+ return true;
+}
+
+bool ModuleList::RemoveIfOrphaned(const Module *module_ptr) {
+ if (module_ptr) {
+ 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->unique()) {
+ pos = RemoveImpl(pos);
+ return true;
+ } else
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+size_t ModuleList::RemoveOrphans(bool mandatory) {
+ std::unique_lock<std::recursive_mutex> lock(m_modules_mutex, std::defer_lock);
+
+ if (mandatory) {
+ lock.lock();
+ } else {
+ // Not mandatory, remove orphans if we can get the mutex
+ if (!lock.try_lock())
+ return 0;
+ }
+ collection::iterator pos = m_modules.begin();
+ size_t remove_count = 0;
+ while (pos != m_modules.end()) {
+ if (pos->unique()) {
+ pos = RemoveImpl(pos);
+ ++remove_count;
+ } else {
+ ++pos;
+ }
+ }
+ return remove_count;
+}
+
+size_t ModuleList::Remove(ModuleList &module_list) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ size_t num_removed = 0;
+ collection::iterator pos, end = module_list.m_modules.end();
+ for (pos = module_list.m_modules.begin(); pos != end; ++pos) {
+ if (Remove(*pos, false /* notify */))
+ ++num_removed;
+ }
+ if (m_notifier)
+ m_notifier->NotifyModulesRemoved(module_list);
+ return num_removed;
+}
+
+void ModuleList::Clear() { ClearImpl(); }
+
+void ModuleList::Destroy() { ClearImpl(); }
+
+void ModuleList::ClearImpl(bool use_notifier) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ if (use_notifier && m_notifier)
+ m_notifier->NotifyWillClearList(*this);
+ m_modules.clear();
+}
+
+Module *ModuleList::GetModulePointerAtIndex(size_t idx) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ return GetModulePointerAtIndexUnlocked(idx);
+}
+
+Module *ModuleList::GetModulePointerAtIndexUnlocked(size_t idx) const {
+ if (idx < m_modules.size())
+ return m_modules[idx].get();
+ return nullptr;
+}
+
+ModuleSP ModuleList::GetModuleAtIndex(size_t idx) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ return GetModuleAtIndexUnlocked(idx);
+}
+
+ModuleSP ModuleList::GetModuleAtIndexUnlocked(size_t idx) const {
+ ModuleSP module_sp;
+ if (idx < m_modules.size())
+ module_sp = m_modules[idx];
+ return module_sp;
+}
+
+size_t ModuleList::FindFunctions(ConstString name,
+ FunctionNameType name_type_mask,
+ bool include_symbols, bool include_inlines,
+ bool append,
+ SymbolContextList &sc_list) const {
+ if (!append)
+ sc_list.Clear();
+
+ const size_t old_size = sc_list.GetSize();
+
+ if (name_type_mask & eFunctionNameTypeAuto) {
+ Module::LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown);
+
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindFunctions(lookup_info.GetLookupName(), nullptr,
+ lookup_info.GetNameTypeMask(), include_symbols,
+ include_inlines, true, sc_list);
+ }
+
+ const size_t new_size = sc_list.GetSize();
+
+ if (old_size < new_size)
+ lookup_info.Prune(sc_list, old_size);
+ } else {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindFunctions(name, nullptr, name_type_mask, include_symbols,
+ include_inlines, true, sc_list);
+ }
+ }
+ return sc_list.GetSize() - old_size;
+}
+
+size_t ModuleList::FindFunctionSymbols(ConstString name,
+ lldb::FunctionNameType name_type_mask,
+ SymbolContextList &sc_list) {
+ const size_t old_size = sc_list.GetSize();
+
+ if (name_type_mask & eFunctionNameTypeAuto) {
+ Module::LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown);
+
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindFunctionSymbols(lookup_info.GetLookupName(),
+ lookup_info.GetNameTypeMask(), sc_list);
+ }
+
+ const size_t new_size = sc_list.GetSize();
+
+ if (old_size < new_size)
+ lookup_info.Prune(sc_list, old_size);
+ } else {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindFunctionSymbols(name, name_type_mask, sc_list);
+ }
+ }
+
+ return sc_list.GetSize() - old_size;
+}
+
+size_t ModuleList::FindFunctions(const RegularExpression &name,
+ bool include_symbols, bool include_inlines,
+ bool append, SymbolContextList &sc_list) {
+ const size_t old_size = sc_list.GetSize();
+
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindFunctions(name, include_symbols, include_inlines, append,
+ sc_list);
+ }
+
+ return sc_list.GetSize() - old_size;
+}
+
+size_t ModuleList::FindCompileUnits(const FileSpec &path, bool append,
+ SymbolContextList &sc_list) const {
+ if (!append)
+ sc_list.Clear();
+
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindCompileUnits(path, true, sc_list);
+ }
+
+ return sc_list.GetSize();
+}
+
+size_t ModuleList::FindGlobalVariables(ConstString name,
+ size_t max_matches,
+ VariableList &variable_list) const {
+ size_t initial_size = variable_list.GetSize();
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindGlobalVariables(name, nullptr, max_matches, variable_list);
+ }
+ return variable_list.GetSize() - initial_size;
+}
+
+size_t ModuleList::FindGlobalVariables(const RegularExpression &regex,
+ size_t max_matches,
+ VariableList &variable_list) const {
+ size_t initial_size = variable_list.GetSize();
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindGlobalVariables(regex, max_matches, variable_list);
+ }
+ return variable_list.GetSize() - initial_size;
+}
+
+size_t ModuleList::FindSymbolsWithNameAndType(ConstString name,
+ SymbolType symbol_type,
+ SymbolContextList &sc_list,
+ bool append) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ if (!append)
+ sc_list.Clear();
+ size_t initial_size = sc_list.GetSize();
+
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos)
+ (*pos)->FindSymbolsWithNameAndType(name, symbol_type, sc_list);
+ return sc_list.GetSize() - initial_size;
+}
+
+size_t ModuleList::FindSymbolsMatchingRegExAndType(
+ const RegularExpression &regex, lldb::SymbolType symbol_type,
+ SymbolContextList &sc_list, bool append) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ if (!append)
+ sc_list.Clear();
+ size_t initial_size = sc_list.GetSize();
+
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos)
+ (*pos)->FindSymbolsMatchingRegExAndType(regex, symbol_type, sc_list);
+ return sc_list.GetSize() - initial_size;
+}
+
+size_t ModuleList::FindModules(const ModuleSpec &module_spec,
+ ModuleList &matching_module_list) const {
+ size_t existing_matches = matching_module_list.GetSize();
+
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ ModuleSP module_sp(*pos);
+ if (module_sp->MatchesModuleSpec(module_spec))
+ matching_module_list.Append(module_sp);
+ }
+ return matching_module_list.GetSize() - existing_matches;
+}
+
+ModuleSP ModuleList::FindModule(const Module *module_ptr) const {
+ ModuleSP module_sp;
+
+ // Scope for "locker"
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if ((*pos).get() == module_ptr) {
+ module_sp = (*pos);
+ break;
+ }
+ }
+ }
+ return module_sp;
+}
+
+ModuleSP ModuleList::FindModule(const UUID &uuid) const {
+ ModuleSP module_sp;
+
+ if (uuid.IsValid()) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if ((*pos)->GetUUID() == uuid) {
+ module_sp = (*pos);
+ break;
+ }
+ }
+ }
+ return module_sp;
+}
+
+size_t
+ModuleList::FindTypes(Module *search_first, ConstString name,
+ bool name_is_fully_qualified, size_t max_matches,
+ llvm::DenseSet<SymbolFile *> &searched_symbol_files,
+ TypeList &types) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+
+ size_t total_matches = 0;
+ collection::const_iterator pos, end = m_modules.end();
+ if (search_first) {
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if (search_first == pos->get()) {
+ total_matches +=
+ search_first->FindTypes(name, name_is_fully_qualified, max_matches,
+ searched_symbol_files, types);
+
+ if (total_matches >= max_matches)
+ break;
+ }
+ }
+ }
+
+ if (total_matches < max_matches) {
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ // Search the module if the module is not equal to the one in the symbol
+ // context "sc". If "sc" contains a empty module shared pointer, then the
+ // comparison will always be true (valid_module_ptr != nullptr).
+ if (search_first != pos->get())
+ total_matches +=
+ (*pos)->FindTypes(name, name_is_fully_qualified, max_matches,
+ searched_symbol_files, types);
+
+ if (total_matches >= max_matches)
+ break;
+ }
+ }
+
+ return total_matches;
+}
+
+bool ModuleList::FindSourceFile(const FileSpec &orig_spec,
+ FileSpec &new_spec) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if ((*pos)->FindSourceFile(orig_spec, new_spec))
+ return true;
+ }
+ return false;
+}
+
+void ModuleList::FindAddressesForLine(const lldb::TargetSP target_sp,
+ const FileSpec &file, uint32_t line,
+ Function *function,
+ std::vector<Address> &output_local,
+ std::vector<Address> &output_extern) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindAddressesForLine(target_sp, file, line, function, output_local,
+ output_extern);
+ }
+}
+
+ModuleSP ModuleList::FindFirstModule(const ModuleSpec &module_spec) const {
+ ModuleSP module_sp;
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ ModuleSP module_sp(*pos);
+ if (module_sp->MatchesModuleSpec(module_spec))
+ return module_sp;
+ }
+ return module_sp;
+}
+
+size_t ModuleList::GetSize() const {
+ size_t size = 0;
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ size = m_modules.size();
+ }
+ return size;
+}
+
+void ModuleList::Dump(Stream *s) const {
+ // s.Printf("%.*p: ", (int)sizeof(void*) * 2, this);
+ // s.Indent();
+ // s << "ModuleList\n";
+
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->Dump(s);
+ }
+}
+
+void ModuleList::LogUUIDAndPaths(Log *log, const char *prefix_cstr) {
+ if (log != nullptr) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, begin = m_modules.begin(),
+ end = m_modules.end();
+ for (pos = begin; pos != end; ++pos) {
+ Module *module = pos->get();
+ const FileSpec &module_file_spec = module->GetFileSpec();
+ log->Printf("%s[%u] %s (%s) \"%s\"", prefix_cstr ? prefix_cstr : "",
+ (uint32_t)std::distance(begin, pos),
+ module->GetUUID().GetAsString().c_str(),
+ module->GetArchitecture().GetArchitectureName(),
+ module_file_spec.GetPath().c_str());
+ }
+ }
+}
+
+bool ModuleList::ResolveFileAddress(lldb::addr_t vm_addr,
+ Address &so_addr) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if ((*pos)->ResolveFileAddress(vm_addr, so_addr))
+ return true;
+ }
+
+ return false;
+}
+
+uint32_t
+ModuleList::ResolveSymbolContextForAddress(const Address &so_addr,
+ SymbolContextItem resolve_scope,
+ SymbolContext &sc) const {
+ // The address is already section offset so it has a module
+ uint32_t resolved_flags = 0;
+ ModuleSP module_sp(so_addr.GetModule());
+ if (module_sp) {
+ resolved_flags =
+ module_sp->ResolveSymbolContextForAddress(so_addr, resolve_scope, sc);
+ } else {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ resolved_flags =
+ (*pos)->ResolveSymbolContextForAddress(so_addr, resolve_scope, sc);
+ if (resolved_flags != 0)
+ break;
+ }
+ }
+
+ return resolved_flags;
+}
+
+uint32_t ModuleList::ResolveSymbolContextForFilePath(
+ const char *file_path, uint32_t line, bool check_inlines,
+ SymbolContextItem resolve_scope, SymbolContextList &sc_list) const {
+ FileSpec file_spec(file_path);
+ return ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines,
+ resolve_scope, sc_list);
+}
+
+uint32_t ModuleList::ResolveSymbolContextsForFileSpec(
+ const FileSpec &file_spec, uint32_t line, bool check_inlines,
+ SymbolContextItem resolve_scope, SymbolContextList &sc_list) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines,
+ resolve_scope, sc_list);
+ }
+
+ return sc_list.GetSize();
+}
+
+size_t ModuleList::GetIndexForModule(const Module *module) const {
+ if (module) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos;
+ collection::const_iterator begin = m_modules.begin();
+ collection::const_iterator end = m_modules.end();
+ for (pos = begin; pos != end; ++pos) {
+ if ((*pos).get() == module)
+ return std::distance(begin, pos);
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+namespace {
+struct SharedModuleListInfo {
+ ModuleList module_list;
+ ModuleListProperties module_list_properties;
+};
+}
+static SharedModuleListInfo &GetSharedModuleListInfo()
+{
+ static SharedModuleListInfo *g_shared_module_list_info = nullptr;
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ // NOTE: Intentionally leak the module list so a program doesn't have to
+ // cleanup all modules and object files as it exits. This just wastes time
+ // doing a bunch of cleanup that isn't required.
+ if (g_shared_module_list_info == nullptr)
+ g_shared_module_list_info = new SharedModuleListInfo();
+ });
+ return *g_shared_module_list_info;
+}
+
+static ModuleList &GetSharedModuleList() {
+ return GetSharedModuleListInfo().module_list;
+}
+
+ModuleListProperties &ModuleList::GetGlobalModuleListProperties() {
+ return GetSharedModuleListInfo().module_list_properties;
+}
+
+bool ModuleList::ModuleIsInCache(const Module *module_ptr) {
+ if (module_ptr) {
+ ModuleList &shared_module_list = GetSharedModuleList();
+ return shared_module_list.FindModule(module_ptr).get() != nullptr;
+ }
+ return false;
+}
+
+size_t ModuleList::FindSharedModules(const ModuleSpec &module_spec,
+ ModuleList &matching_module_list) {
+ return GetSharedModuleList().FindModules(module_spec, matching_module_list);
+}
+
+size_t ModuleList::RemoveOrphanSharedModules(bool mandatory) {
+ return GetSharedModuleList().RemoveOrphans(mandatory);
+}
+
+Status ModuleList::GetSharedModule(const ModuleSpec &module_spec,
+ ModuleSP &module_sp,
+ const FileSpecList *module_search_paths_ptr,
+ ModuleSP *old_module_sp_ptr,
+ 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);
+ char path[PATH_MAX];
+
+ Status error;
+
+ module_sp.reset();
+
+ if (did_create_ptr)
+ *did_create_ptr = false;
+ if (old_module_sp_ptr)
+ old_module_sp_ptr->reset();
+
+ const UUID *uuid_ptr = module_spec.GetUUIDPtr();
+ const FileSpec &module_file_spec = module_spec.GetFileSpec();
+ const ArchSpec &arch = module_spec.GetArchitecture();
+
+ // Make sure no one else can try and get or create a module while this
+ // function is actively working on it by doing an extra lock on the global
+ // mutex list.
+ if (!always_create) {
+ ModuleList matching_module_list;
+ const size_t num_matching_modules =
+ shared_module_list.FindModules(module_spec, matching_module_list);
+ if (num_matching_modules > 0) {
+ for (size_t module_idx = 0; module_idx < num_matching_modules;
+ ++module_idx) {
+ module_sp = matching_module_list.GetModuleAtIndex(module_idx);
+
+ // Make sure the file for the module hasn't been modified
+ if (module_sp->FileHasChanged()) {
+ if (old_module_sp_ptr && !*old_module_sp_ptr)
+ *old_module_sp_ptr = module_sp;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES));
+ if (log != nullptr)
+ log->Printf("module changed: %p, removing from global module list",
+ static_cast<void *>(module_sp.get()));
+
+ shared_module_list.Remove(module_sp);
+ module_sp.reset();
+ } else {
+ // The module matches and the module was not modified from when it
+ // was last loaded.
+ return error;
+ }
+ }
+ }
+ }
+
+ if (module_sp)
+ return error;
+
+ module_sp = std::make_shared<Module>(module_spec);
+ // Make sure there are a module and an object file since we can specify a
+ // valid file path with an architecture that might not be in that file. By
+ // getting the object file we can guarantee that the architecture matches
+ if (module_sp->GetObjectFile()) {
+ // If we get in here we got the correct arch, now we just need to verify
+ // the UUID if one was given
+ if (uuid_ptr && *uuid_ptr != module_sp->GetUUID()) {
+ module_sp.reset();
+ } else {
+ if (module_sp->GetObjectFile() &&
+ module_sp->GetObjectFile()->GetType() ==
+ ObjectFile::eTypeStubLibrary) {
+ module_sp.reset();
+ } else {
+ if (did_create_ptr) {
+ *did_create_ptr = true;
+ }
+
+ shared_module_list.ReplaceEquivalent(module_sp);
+ return error;
+ }
+ }
+ } else {
+ module_sp.reset();
+ }
+
+ if (module_search_paths_ptr) {
+ const auto num_directories = module_search_paths_ptr->GetSize();
+ for (size_t idx = 0; idx < num_directories; ++idx) {
+ auto search_path_spec = module_search_paths_ptr->GetFileSpecAtIndex(idx);
+ FileSystem::Instance().Resolve(search_path_spec);
+ namespace fs = llvm::sys::fs;
+ if (!FileSystem::Instance().IsDirectory(search_path_spec))
+ continue;
+ search_path_spec.AppendPathComponent(
+ module_spec.GetFileSpec().GetFilename().AsCString());
+ if (!FileSystem::Instance().Exists(search_path_spec))
+ continue;
+
+ auto resolved_module_spec(module_spec);
+ resolved_module_spec.GetFileSpec() = search_path_spec;
+ module_sp = std::make_shared<Module>(resolved_module_spec);
+ if (module_sp->GetObjectFile()) {
+ // If we get in here we got the correct arch, now we just need to
+ // verify the UUID if one was given
+ if (uuid_ptr && *uuid_ptr != module_sp->GetUUID()) {
+ module_sp.reset();
+ } else {
+ if (module_sp->GetObjectFile()->GetType() ==
+ ObjectFile::eTypeStubLibrary) {
+ module_sp.reset();
+ } else {
+ if (did_create_ptr)
+ *did_create_ptr = true;
+
+ shared_module_list.ReplaceEquivalent(module_sp);
+ return Status();
+ }
+ }
+ } else {
+ module_sp.reset();
+ }
+ }
+ }
+
+ // Either the file didn't exist where at the path, or no path was given, so
+ // we now have to use more extreme measures to try and find the appropriate
+ // module.
+
+ // Fixup the incoming path in case the path points to a valid file, yet the
+ // arch or UUID (if one was passed in) don't match.
+ ModuleSpec located_binary_modulespec =
+ Symbols::LocateExecutableObjectFile(module_spec);
+
+ // Don't look for the file if it appears to be the same one we already
+ // checked for above...
+ if (located_binary_modulespec.GetFileSpec() != module_file_spec) {
+ if (!FileSystem::Instance().Exists(
+ located_binary_modulespec.GetFileSpec())) {
+ located_binary_modulespec.GetFileSpec().GetPath(path, sizeof(path));
+ if (path[0] == '\0')
+ module_file_spec.GetPath(path, sizeof(path));
+ // How can this check ever be true? This branch it is false, and we
+ // haven't modified file_spec.
+ if (FileSystem::Instance().Exists(
+ located_binary_modulespec.GetFileSpec())) {
+ std::string uuid_str;
+ if (uuid_ptr && uuid_ptr->IsValid())
+ uuid_str = uuid_ptr->GetAsString();
+
+ if (arch.IsValid()) {
+ if (!uuid_str.empty())
+ error.SetErrorStringWithFormat(
+ "'%s' does not contain the %s architecture and UUID %s", path,
+ arch.GetArchitectureName(), uuid_str.c_str());
+ else
+ error.SetErrorStringWithFormat(
+ "'%s' does not contain the %s architecture.", path,
+ arch.GetArchitectureName());
+ }
+ } else {
+ error.SetErrorStringWithFormat("'%s' does not exist", path);
+ }
+ if (error.Fail())
+ module_sp.reset();
+ return error;
+ }
+
+ // Make sure no one else can try and get or create a module while this
+ // function is actively working on it by doing an extra lock on the global
+ // mutex list.
+ ModuleSpec platform_module_spec(module_spec);
+ platform_module_spec.GetFileSpec() =
+ located_binary_modulespec.GetFileSpec();
+ platform_module_spec.GetPlatformFileSpec() =
+ located_binary_modulespec.GetFileSpec();
+ platform_module_spec.GetSymbolFileSpec() =
+ located_binary_modulespec.GetSymbolFileSpec();
+ ModuleList matching_module_list;
+ if (shared_module_list.FindModules(platform_module_spec,
+ matching_module_list) > 0) {
+ module_sp = matching_module_list.GetModuleAtIndex(0);
+
+ // If we didn't have a UUID in mind when looking for the object file,
+ // then we should make sure the modification time hasn't changed!
+ if (platform_module_spec.GetUUIDPtr() == nullptr) {
+ auto file_spec_mod_time = FileSystem::Instance().GetModificationTime(
+ located_binary_modulespec.GetFileSpec());
+ if (file_spec_mod_time != llvm::sys::TimePoint<>()) {
+ if (file_spec_mod_time != module_sp->GetModificationTime()) {
+ if (old_module_sp_ptr)
+ *old_module_sp_ptr = module_sp;
+ shared_module_list.Remove(module_sp);
+ module_sp.reset();
+ }
+ }
+ }
+ }
+
+ if (!module_sp) {
+ module_sp = std::make_shared<Module>(platform_module_spec);
+ // Make sure there are a module and an object file since we can specify a
+ // valid file path with an architecture that might not be in that file.
+ // By getting the object file we can guarantee that the architecture
+ // matches
+ if (module_sp && module_sp->GetObjectFile()) {
+ if (module_sp->GetObjectFile()->GetType() ==
+ ObjectFile::eTypeStubLibrary) {
+ module_sp.reset();
+ } else {
+ if (did_create_ptr)
+ *did_create_ptr = true;
+
+ shared_module_list.ReplaceEquivalent(module_sp);
+ }
+ } else {
+ located_binary_modulespec.GetFileSpec().GetPath(path, sizeof(path));
+
+ if (located_binary_modulespec.GetFileSpec()) {
+ if (arch.IsValid())
+ error.SetErrorStringWithFormat(
+ "unable to open %s architecture in '%s'",
+ arch.GetArchitectureName(), path);
+ else
+ error.SetErrorStringWithFormat("unable to open '%s'", path);
+ } else {
+ std::string uuid_str;
+ if (uuid_ptr && uuid_ptr->IsValid())
+ uuid_str = uuid_ptr->GetAsString();
+
+ if (!uuid_str.empty())
+ error.SetErrorStringWithFormat(
+ "cannot locate a module for UUID '%s'", uuid_str.c_str());
+ else
+ error.SetErrorStringWithFormat("cannot locate a module");
+ }
+ }
+ }
+ }
+
+ return error;
+}
+
+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::LoadScriptingResourcesInTarget(Target *target,
+ std::list<Status> &errors,
+ Stream *feedback_stream,
+ bool continue_on_error) {
+ if (!target)
+ return false;
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ for (auto module : m_modules) {
+ Status error;
+ if (module) {
+ if (!module->LoadScriptingResourceInTarget(target, error,
+ feedback_stream)) {
+ if (error.Fail() && error.AsCString()) {
+ error.SetErrorStringWithFormat("unable to load scripting data for "
+ "module %s - error reported was %s",
+ module->GetFileSpec()
+ .GetFileNameStrippingExtension()
+ .GetCString(),
+ error.AsCString());
+ errors.push_back(error);
+
+ if (!continue_on_error)
+ return false;
+ }
+ }
+ }
+ }
+ return errors.empty();
+}
+
+void ModuleList::ForEach(
+ std::function<bool(const ModuleSP &module_sp)> const &callback) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ for (const auto &module : m_modules) {
+ // If the callback returns false, then stop iterating and break out
+ if (!callback(module))
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Opcode.cpp b/contrib/llvm-project/lldb/source/Core/Opcode.cpp
new file mode 100644
index 000000000000..6ca46de40de2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Opcode.cpp
@@ -0,0 +1,140 @@
+//===-- Opcode.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Opcode.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-forward.h"
+
+#include <memory>
+
+#include <inttypes.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+int Opcode::Dump(Stream *s, uint32_t min_byte_width) {
+ const uint32_t previous_bytes = s->GetWrittenBytes();
+ switch (m_type) {
+ case Opcode::eTypeInvalid:
+ s->PutCString("<invalid>");
+ break;
+ case Opcode::eType8:
+ s->Printf("0x%2.2x", m_data.inst8);
+ break;
+ case Opcode::eType16:
+ s->Printf("0x%4.4x", m_data.inst16);
+ break;
+ case Opcode::eType16_2:
+ case Opcode::eType32:
+ s->Printf("0x%8.8x", m_data.inst32);
+ break;
+
+ case Opcode::eType64:
+ s->Printf("0x%16.16" PRIx64, m_data.inst64);
+ break;
+
+ case Opcode::eTypeBytes:
+ for (uint32_t i = 0; i < m_data.inst.length; ++i) {
+ if (i > 0)
+ s->PutChar(' ');
+ s->Printf("%2.2x", m_data.inst.bytes[i]);
+ }
+ break;
+ }
+
+ uint32_t bytes_written_so_far = s->GetWrittenBytes() - previous_bytes;
+ // Add spaces to make sure bytes display comes out even in case opcodes aren't
+ // all the same size.
+ if (bytes_written_so_far < min_byte_width)
+ s->Printf("%*s", min_byte_width - bytes_written_so_far, "");
+ return s->GetWrittenBytes() - previous_bytes;
+}
+
+lldb::ByteOrder Opcode::GetDataByteOrder() const {
+ if (m_byte_order != eByteOrderInvalid) {
+ return m_byte_order;
+ }
+ switch (m_type) {
+ case Opcode::eTypeInvalid:
+ break;
+ case Opcode::eType8:
+ case Opcode::eType16:
+ case Opcode::eType16_2:
+ case Opcode::eType32:
+ case Opcode::eType64:
+ return endian::InlHostByteOrder();
+ case Opcode::eTypeBytes:
+ break;
+ }
+ return eByteOrderInvalid;
+}
+
+uint32_t Opcode::GetData(DataExtractor &data) const {
+ uint32_t byte_size = GetByteSize();
+ uint8_t swap_buf[8];
+ const void *buf = nullptr;
+
+ if (byte_size > 0) {
+ if (!GetEndianSwap()) {
+ if (m_type == Opcode::eType16_2) {
+ // 32 bit thumb instruction, we need to sizzle this a bit
+ swap_buf[0] = m_data.inst.bytes[2];
+ swap_buf[1] = m_data.inst.bytes[3];
+ swap_buf[2] = m_data.inst.bytes[0];
+ swap_buf[3] = m_data.inst.bytes[1];
+ buf = swap_buf;
+ } else {
+ buf = GetOpcodeDataBytes();
+ }
+ } else {
+ switch (m_type) {
+ case Opcode::eTypeInvalid:
+ break;
+ case Opcode::eType8:
+ buf = GetOpcodeDataBytes();
+ break;
+ case Opcode::eType16:
+ *(uint16_t *)swap_buf = llvm::ByteSwap_16(m_data.inst16);
+ buf = swap_buf;
+ break;
+ case Opcode::eType16_2:
+ swap_buf[0] = m_data.inst.bytes[1];
+ swap_buf[1] = m_data.inst.bytes[0];
+ swap_buf[2] = m_data.inst.bytes[3];
+ swap_buf[3] = m_data.inst.bytes[2];
+ buf = swap_buf;
+ break;
+ case Opcode::eType32:
+ *(uint32_t *)swap_buf = llvm::ByteSwap_32(m_data.inst32);
+ buf = swap_buf;
+ break;
+ case Opcode::eType64:
+ *(uint32_t *)swap_buf = llvm::ByteSwap_64(m_data.inst64);
+ buf = swap_buf;
+ break;
+ case Opcode::eTypeBytes:
+ buf = GetOpcodeDataBytes();
+ break;
+ }
+ }
+ }
+ if (buf != nullptr) {
+ DataBufferSP buffer_sp;
+
+ buffer_sp = std::make_shared<DataBufferHeap>(buf, byte_size);
+ data.SetByteOrder(GetDataByteOrder());
+ data.SetData(buffer_sp);
+ return byte_size;
+ }
+ data.Clear();
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/PluginManager.cpp b/contrib/llvm-project/lldb/source/Core/PluginManager.cpp
new file mode 100644
index 000000000000..24cadcd85bf5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/PluginManager.cpp
@@ -0,0 +1,2625 @@
+//===-- PluginManager.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/PluginManager.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StringList.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/PosixApi.h"
+#endif
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <assert.h>
+
+namespace lldb_private {
+class CommandInterpreter;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum PluginAction {
+ ePluginRegisterInstance,
+ ePluginUnregisterInstance,
+ ePluginGetInstanceAtIndex
+};
+
+typedef bool (*PluginInitCallback)();
+typedef void (*PluginTermCallback)();
+
+struct PluginInfo {
+ PluginInfo() : plugin_init_callback(nullptr), plugin_term_callback(nullptr) {}
+
+ llvm::sys::DynamicLibrary library;
+ PluginInitCallback plugin_init_callback;
+ PluginTermCallback plugin_term_callback;
+};
+
+typedef std::map<FileSpec, PluginInfo> PluginTerminateMap;
+
+static std::recursive_mutex &GetPluginMapMutex() {
+ static std::recursive_mutex g_plugin_map_mutex;
+ return g_plugin_map_mutex;
+}
+
+static PluginTerminateMap &GetPluginMap() {
+ static PluginTerminateMap g_plugin_map;
+ return g_plugin_map;
+}
+
+static bool PluginIsLoaded(const FileSpec &plugin_file_spec) {
+ std::lock_guard<std::recursive_mutex> guard(GetPluginMapMutex());
+ PluginTerminateMap &plugin_map = GetPluginMap();
+ return plugin_map.find(plugin_file_spec) != plugin_map.end();
+}
+
+static void SetPluginInfo(const FileSpec &plugin_file_spec,
+ const PluginInfo &plugin_info) {
+ std::lock_guard<std::recursive_mutex> guard(GetPluginMapMutex());
+ PluginTerminateMap &plugin_map = GetPluginMap();
+ assert(plugin_map.find(plugin_file_spec) == plugin_map.end());
+ plugin_map[plugin_file_spec] = plugin_info;
+}
+
+template <typename FPtrTy> static FPtrTy CastToFPtr(void *VPtr) {
+ return reinterpret_cast<FPtrTy>(reinterpret_cast<intptr_t>(VPtr));
+}
+
+static FileSystem::EnumerateDirectoryResult
+LoadPluginCallback(void *baton, llvm::sys::fs::file_type ft,
+ llvm::StringRef path) {
+ // PluginManager *plugin_manager = (PluginManager *)baton;
+ Status error;
+
+ namespace fs = llvm::sys::fs;
+ // If we have a regular file, a symbolic link or unknown file type, try and
+ // process the file. We must handle unknown as sometimes the directory
+ // enumeration might be enumerating a file system that doesn't have correct
+ // file type information.
+ if (ft == fs::file_type::regular_file || ft == fs::file_type::symlink_file ||
+ ft == fs::file_type::type_unknown) {
+ FileSpec plugin_file_spec(path);
+ FileSystem::Instance().Resolve(plugin_file_spec);
+
+ if (PluginIsLoaded(plugin_file_spec))
+ return FileSystem::eEnumerateDirectoryResultNext;
+ else {
+ PluginInfo plugin_info;
+
+ std::string pluginLoadError;
+ plugin_info.library = llvm::sys::DynamicLibrary::getPermanentLibrary(
+ plugin_file_spec.GetPath().c_str(), &pluginLoadError);
+ if (plugin_info.library.isValid()) {
+ bool success = false;
+ plugin_info.plugin_init_callback = CastToFPtr<PluginInitCallback>(
+ plugin_info.library.getAddressOfSymbol("LLDBPluginInitialize"));
+ if (plugin_info.plugin_init_callback) {
+ // Call the plug-in "bool LLDBPluginInitialize(void)" function
+ success = plugin_info.plugin_init_callback();
+ }
+
+ if (success) {
+ // It is ok for the "LLDBPluginTerminate" symbol to be nullptr
+ plugin_info.plugin_term_callback = CastToFPtr<PluginTermCallback>(
+ plugin_info.library.getAddressOfSymbol("LLDBPluginTerminate"));
+ } else {
+ // The initialize function returned FALSE which means the plug-in
+ // might not be compatible, or might be too new or too old, or might
+ // not want to run on this machine. Set it to a default-constructed
+ // instance to invalidate it.
+ plugin_info = PluginInfo();
+ }
+
+ // Regardless of success or failure, cache the plug-in load in our
+ // plug-in info so we don't try to load it again and again.
+ SetPluginInfo(plugin_file_spec, plugin_info);
+
+ return FileSystem::eEnumerateDirectoryResultNext;
+ }
+ }
+ }
+
+ if (ft == fs::file_type::directory_file ||
+ ft == fs::file_type::symlink_file || ft == fs::file_type::type_unknown) {
+ // Try and recurse into anything that a directory or symbolic link. We must
+ // also do this for unknown as sometimes the directory enumeration might be
+ // enumerating a file system that doesn't have correct file type
+ // information.
+ return FileSystem::eEnumerateDirectoryResultEnter;
+ }
+
+ return FileSystem::eEnumerateDirectoryResultNext;
+}
+
+void PluginManager::Initialize() {
+#if 1
+ const bool find_directories = true;
+ const bool find_files = true;
+ const bool find_other = true;
+ char dir_path[PATH_MAX];
+ if (FileSpec dir_spec = HostInfo::GetSystemPluginDir()) {
+ if (FileSystem::Instance().Exists(dir_spec) &&
+ dir_spec.GetPath(dir_path, sizeof(dir_path))) {
+ FileSystem::Instance().EnumerateDirectory(dir_path, find_directories,
+ find_files, find_other,
+ LoadPluginCallback, nullptr);
+ }
+ }
+
+ if (FileSpec dir_spec = HostInfo::GetUserPluginDir()) {
+ if (FileSystem::Instance().Exists(dir_spec) &&
+ dir_spec.GetPath(dir_path, sizeof(dir_path))) {
+ FileSystem::Instance().EnumerateDirectory(dir_path, find_directories,
+ find_files, find_other,
+ LoadPluginCallback, nullptr);
+ }
+ }
+#endif
+}
+
+void PluginManager::Terminate() {
+ std::lock_guard<std::recursive_mutex> guard(GetPluginMapMutex());
+ PluginTerminateMap &plugin_map = GetPluginMap();
+
+ PluginTerminateMap::const_iterator pos, end = plugin_map.end();
+ for (pos = plugin_map.begin(); pos != end; ++pos) {
+ // Call the plug-in "void LLDBPluginTerminate (void)" function if there is
+ // one (if the symbol was not nullptr).
+ if (pos->second.library.isValid()) {
+ if (pos->second.plugin_term_callback)
+ pos->second.plugin_term_callback();
+ }
+ }
+ plugin_map.clear();
+}
+
+#pragma mark ABI
+
+struct ABIInstance {
+ ABIInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ ABICreateInstance create_callback;
+};
+
+typedef std::vector<ABIInstance> ABIInstances;
+
+static std::recursive_mutex &GetABIInstancesMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static ABIInstances &GetABIInstances() {
+ static ABIInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(ConstString name,
+ const char *description,
+ ABICreateInstance create_callback) {
+ if (create_callback) {
+ ABIInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetABIInstancesMutex());
+ GetABIInstances().push_back(instance);
+ return true;
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(ABICreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetABIInstancesMutex());
+ ABIInstances &instances = GetABIInstances();
+
+ ABIInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ABICreateInstance PluginManager::GetABICreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetABIInstancesMutex());
+ ABIInstances &instances = GetABIInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+ABICreateInstance
+PluginManager::GetABICreateCallbackForPluginName(ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetABIInstancesMutex());
+ ABIInstances &instances = GetABIInstances();
+
+ ABIInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark Architecture
+
+struct ArchitectureInstance {
+ ConstString name;
+ std::string description;
+ PluginManager::ArchitectureCreateInstance create_callback;
+};
+
+typedef std::vector<ArchitectureInstance> ArchitectureInstances;
+
+static std::mutex &GetArchitectureMutex() {
+ static std::mutex g_architecture_mutex;
+ return g_architecture_mutex;
+}
+
+static ArchitectureInstances &GetArchitectureInstances() {
+ static ArchitectureInstances g_instances;
+ return g_instances;
+}
+
+void PluginManager::RegisterPlugin(ConstString name,
+ llvm::StringRef description,
+ ArchitectureCreateInstance create_callback) {
+ std::lock_guard<std::mutex> guard(GetArchitectureMutex());
+ GetArchitectureInstances().push_back({name, description, create_callback});
+}
+
+void PluginManager::UnregisterPlugin(
+ ArchitectureCreateInstance create_callback) {
+ std::lock_guard<std::mutex> guard(GetArchitectureMutex());
+ auto &instances = GetArchitectureInstances();
+
+ for (auto pos = instances.begin(), end = instances.end(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return;
+ }
+ }
+ llvm_unreachable("Plugin not found");
+}
+
+std::unique_ptr<Architecture>
+PluginManager::CreateArchitectureInstance(const ArchSpec &arch) {
+ std::lock_guard<std::mutex> guard(GetArchitectureMutex());
+ for (const auto &instances : GetArchitectureInstances()) {
+ if (auto plugin_up = instances.create_callback(arch))
+ return plugin_up;
+ }
+ return nullptr;
+}
+
+#pragma mark Disassembler
+
+struct DisassemblerInstance {
+ DisassemblerInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ DisassemblerCreateInstance create_callback;
+};
+
+typedef std::vector<DisassemblerInstance> DisassemblerInstances;
+
+static std::recursive_mutex &GetDisassemblerMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static DisassemblerInstances &GetDisassemblerInstances() {
+ static DisassemblerInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(ConstString name,
+ const char *description,
+ DisassemblerCreateInstance create_callback) {
+ if (create_callback) {
+ DisassemblerInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetDisassemblerMutex());
+ GetDisassemblerInstances().push_back(instance);
+ return true;
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ DisassemblerCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetDisassemblerMutex());
+ DisassemblerInstances &instances = GetDisassemblerInstances();
+
+ DisassemblerInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+DisassemblerCreateInstance
+PluginManager::GetDisassemblerCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetDisassemblerMutex());
+ DisassemblerInstances &instances = GetDisassemblerInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+DisassemblerCreateInstance
+PluginManager::GetDisassemblerCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetDisassemblerMutex());
+ DisassemblerInstances &instances = GetDisassemblerInstances();
+
+ DisassemblerInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark DynamicLoader
+
+struct DynamicLoaderInstance {
+ DynamicLoaderInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ DynamicLoaderCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<DynamicLoaderInstance> DynamicLoaderInstances;
+
+static std::recursive_mutex &GetDynamicLoaderMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static DynamicLoaderInstances &GetDynamicLoaderInstances() {
+ static DynamicLoaderInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ DynamicLoaderCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ DynamicLoaderInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetDynamicLoaderMutex());
+ GetDynamicLoaderInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ DynamicLoaderCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetDynamicLoaderMutex());
+ DynamicLoaderInstances &instances = GetDynamicLoaderInstances();
+
+ DynamicLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+DynamicLoaderCreateInstance
+PluginManager::GetDynamicLoaderCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetDynamicLoaderMutex());
+ DynamicLoaderInstances &instances = GetDynamicLoaderInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+DynamicLoaderCreateInstance
+PluginManager::GetDynamicLoaderCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetDynamicLoaderMutex());
+ DynamicLoaderInstances &instances = GetDynamicLoaderInstances();
+
+ DynamicLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark JITLoader
+
+struct JITLoaderInstance {
+ JITLoaderInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ JITLoaderCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<JITLoaderInstance> JITLoaderInstances;
+
+static std::recursive_mutex &GetJITLoaderMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static JITLoaderInstances &GetJITLoaderInstances() {
+ static JITLoaderInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ JITLoaderCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ JITLoaderInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetJITLoaderMutex());
+ GetJITLoaderInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(JITLoaderCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetJITLoaderMutex());
+ JITLoaderInstances &instances = GetJITLoaderInstances();
+
+ JITLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+JITLoaderCreateInstance
+PluginManager::GetJITLoaderCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetJITLoaderMutex());
+ JITLoaderInstances &instances = GetJITLoaderInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+JITLoaderCreateInstance PluginManager::GetJITLoaderCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetJITLoaderMutex());
+ JITLoaderInstances &instances = GetJITLoaderInstances();
+
+ JITLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark EmulateInstruction
+
+struct EmulateInstructionInstance {
+ EmulateInstructionInstance()
+ : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ EmulateInstructionCreateInstance create_callback;
+};
+
+typedef std::vector<EmulateInstructionInstance> EmulateInstructionInstances;
+
+static std::recursive_mutex &GetEmulateInstructionMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static EmulateInstructionInstances &GetEmulateInstructionInstances() {
+ static EmulateInstructionInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ EmulateInstructionCreateInstance create_callback) {
+ if (create_callback) {
+ EmulateInstructionInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetEmulateInstructionMutex());
+ GetEmulateInstructionInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ EmulateInstructionCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetEmulateInstructionMutex());
+ EmulateInstructionInstances &instances = GetEmulateInstructionInstances();
+
+ EmulateInstructionInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+EmulateInstructionCreateInstance
+PluginManager::GetEmulateInstructionCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetEmulateInstructionMutex());
+ EmulateInstructionInstances &instances = GetEmulateInstructionInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+EmulateInstructionCreateInstance
+PluginManager::GetEmulateInstructionCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetEmulateInstructionMutex());
+ EmulateInstructionInstances &instances = GetEmulateInstructionInstances();
+
+ EmulateInstructionInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark OperatingSystem
+
+struct OperatingSystemInstance {
+ OperatingSystemInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ OperatingSystemCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<OperatingSystemInstance> OperatingSystemInstances;
+
+static std::recursive_mutex &GetOperatingSystemMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static OperatingSystemInstances &GetOperatingSystemInstances() {
+ static OperatingSystemInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ OperatingSystemCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ OperatingSystemInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetOperatingSystemMutex());
+ GetOperatingSystemInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ OperatingSystemCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetOperatingSystemMutex());
+ OperatingSystemInstances &instances = GetOperatingSystemInstances();
+
+ OperatingSystemInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+OperatingSystemCreateInstance
+PluginManager::GetOperatingSystemCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetOperatingSystemMutex());
+ OperatingSystemInstances &instances = GetOperatingSystemInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+OperatingSystemCreateInstance
+PluginManager::GetOperatingSystemCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetOperatingSystemMutex());
+ OperatingSystemInstances &instances = GetOperatingSystemInstances();
+
+ OperatingSystemInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark Language
+
+struct LanguageInstance {
+ LanguageInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ LanguageCreateInstance create_callback;
+};
+
+typedef std::vector<LanguageInstance> LanguageInstances;
+
+static std::recursive_mutex &GetLanguageMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static LanguageInstances &GetLanguageInstances() {
+ static LanguageInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(ConstString name,
+ const char *description,
+ LanguageCreateInstance create_callback) {
+ if (create_callback) {
+ LanguageInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageMutex());
+ GetLanguageInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(LanguageCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageMutex());
+ LanguageInstances &instances = GetLanguageInstances();
+
+ LanguageInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+LanguageCreateInstance
+PluginManager::GetLanguageCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageMutex());
+ LanguageInstances &instances = GetLanguageInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+LanguageCreateInstance
+PluginManager::GetLanguageCreateCallbackForPluginName(ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageMutex());
+ LanguageInstances &instances = GetLanguageInstances();
+
+ LanguageInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark LanguageRuntime
+
+struct LanguageRuntimeInstance {
+ LanguageRuntimeInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ LanguageRuntimeCreateInstance create_callback;
+ LanguageRuntimeGetCommandObject command_callback;
+ LanguageRuntimeGetExceptionPrecondition precondition_callback;
+};
+
+typedef std::vector<LanguageRuntimeInstance> LanguageRuntimeInstances;
+
+static std::recursive_mutex &GetLanguageRuntimeMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static LanguageRuntimeInstances &GetLanguageRuntimeInstances() {
+ static LanguageRuntimeInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ LanguageRuntimeCreateInstance create_callback,
+ LanguageRuntimeGetCommandObject command_callback,
+ LanguageRuntimeGetExceptionPrecondition precondition_callback) {
+ if (create_callback) {
+ LanguageRuntimeInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.command_callback = command_callback;
+ instance.precondition_callback = precondition_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ GetLanguageRuntimeInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ LanguageRuntimeCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances();
+
+ LanguageRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+LanguageRuntimeCreateInstance
+PluginManager::GetLanguageRuntimeCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+LanguageRuntimeGetCommandObject
+PluginManager::GetLanguageRuntimeGetCommandObjectAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].command_callback;
+ return nullptr;
+}
+
+LanguageRuntimeGetExceptionPrecondition
+PluginManager::GetLanguageRuntimeGetExceptionPreconditionAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].precondition_callback;
+ return nullptr;
+}
+
+LanguageRuntimeCreateInstance
+PluginManager::GetLanguageRuntimeCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances();
+
+ LanguageRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark SystemRuntime
+
+struct SystemRuntimeInstance {
+ SystemRuntimeInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ SystemRuntimeCreateInstance create_callback;
+};
+
+typedef std::vector<SystemRuntimeInstance> SystemRuntimeInstances;
+
+static std::recursive_mutex &GetSystemRuntimeMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static SystemRuntimeInstances &GetSystemRuntimeInstances() {
+ static SystemRuntimeInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ SystemRuntimeCreateInstance create_callback) {
+ if (create_callback) {
+ SystemRuntimeInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetSystemRuntimeMutex());
+ GetSystemRuntimeInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ SystemRuntimeCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetSystemRuntimeMutex());
+ SystemRuntimeInstances &instances = GetSystemRuntimeInstances();
+
+ SystemRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+SystemRuntimeCreateInstance
+PluginManager::GetSystemRuntimeCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetSystemRuntimeMutex());
+ SystemRuntimeInstances &instances = GetSystemRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+SystemRuntimeCreateInstance
+PluginManager::GetSystemRuntimeCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetSystemRuntimeMutex());
+ SystemRuntimeInstances &instances = GetSystemRuntimeInstances();
+
+ SystemRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark ObjectFile
+
+struct ObjectFileInstance {
+ ObjectFileInstance()
+ : name(), description(), create_callback(nullptr),
+ create_memory_callback(nullptr), get_module_specifications(nullptr),
+ save_core(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ ObjectFileCreateInstance create_callback;
+ ObjectFileCreateMemoryInstance create_memory_callback;
+ ObjectFileGetModuleSpecifications get_module_specifications;
+ ObjectFileSaveCore save_core;
+};
+
+typedef std::vector<ObjectFileInstance> ObjectFileInstances;
+
+static std::recursive_mutex &GetObjectFileMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static ObjectFileInstances &GetObjectFileInstances() {
+ static ObjectFileInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ ObjectFileCreateInstance create_callback,
+ ObjectFileCreateMemoryInstance create_memory_callback,
+ ObjectFileGetModuleSpecifications get_module_specifications,
+ ObjectFileSaveCore save_core) {
+ if (create_callback) {
+ ObjectFileInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.create_memory_callback = create_memory_callback;
+ instance.save_core = save_core;
+ instance.get_module_specifications = get_module_specifications;
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ GetObjectFileInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(ObjectFileCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+
+ ObjectFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ObjectFileCreateInstance
+PluginManager::GetObjectFileCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+ObjectFileCreateMemoryInstance
+PluginManager::GetObjectFileCreateMemoryCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+ if (idx < instances.size())
+ return instances[idx].create_memory_callback;
+ return nullptr;
+}
+
+ObjectFileGetModuleSpecifications
+PluginManager::GetObjectFileGetModuleSpecificationsCallbackAtIndex(
+ uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+ if (idx < instances.size())
+ return instances[idx].get_module_specifications;
+ return nullptr;
+}
+
+ObjectFileCreateInstance
+PluginManager::GetObjectFileCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+
+ ObjectFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+ObjectFileCreateMemoryInstance
+PluginManager::GetObjectFileCreateMemoryCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+
+ ObjectFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_memory_callback;
+ }
+ }
+ return nullptr;
+}
+
+Status PluginManager::SaveCore(const lldb::ProcessSP &process_sp,
+ const FileSpec &outfile) {
+ Status error;
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+
+ ObjectFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->save_core && pos->save_core(process_sp, outfile, error))
+ return error;
+ }
+ error.SetErrorString(
+ "no ObjectFile plugins were able to save a core for this process");
+ return error;
+}
+
+#pragma mark ObjectContainer
+
+struct ObjectContainerInstance {
+ ObjectContainerInstance()
+ : name(), description(), create_callback(nullptr),
+ get_module_specifications(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ ObjectContainerCreateInstance create_callback;
+ ObjectFileGetModuleSpecifications get_module_specifications;
+};
+
+typedef std::vector<ObjectContainerInstance> ObjectContainerInstances;
+
+static std::recursive_mutex &GetObjectContainerMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static ObjectContainerInstances &GetObjectContainerInstances() {
+ static ObjectContainerInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ ObjectContainerCreateInstance create_callback,
+ ObjectFileGetModuleSpecifications get_module_specifications) {
+ if (create_callback) {
+ ObjectContainerInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.get_module_specifications = get_module_specifications;
+ std::lock_guard<std::recursive_mutex> guard(GetObjectContainerMutex());
+ GetObjectContainerInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ ObjectContainerCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectContainerMutex());
+ ObjectContainerInstances &instances = GetObjectContainerInstances();
+
+ ObjectContainerInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ObjectContainerCreateInstance
+PluginManager::GetObjectContainerCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectContainerMutex());
+ ObjectContainerInstances &instances = GetObjectContainerInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+ObjectContainerCreateInstance
+PluginManager::GetObjectContainerCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectContainerMutex());
+ ObjectContainerInstances &instances = GetObjectContainerInstances();
+
+ ObjectContainerInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+ObjectFileGetModuleSpecifications
+PluginManager::GetObjectContainerGetModuleSpecificationsCallbackAtIndex(
+ uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectContainerMutex());
+ ObjectContainerInstances &instances = GetObjectContainerInstances();
+ if (idx < instances.size())
+ return instances[idx].get_module_specifications;
+ return nullptr;
+}
+
+#pragma mark Platform
+
+struct PlatformInstance {
+ PlatformInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ PlatformCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<PlatformInstance> PlatformInstances;
+
+static std::recursive_mutex &GetPlatformInstancesMutex() {
+ static std::recursive_mutex g_platform_instances_mutex;
+ return g_platform_instances_mutex;
+}
+
+static PlatformInstances &GetPlatformInstances() {
+ static PlatformInstances g_platform_instances;
+ return g_platform_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ PlatformCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+
+ PlatformInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ GetPlatformInstances().push_back(instance);
+ return true;
+ }
+ return false;
+}
+
+const char *PluginManager::GetPlatformPluginNameAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+ if (idx < instances.size())
+ return instances[idx].name.GetCString();
+ return nullptr;
+}
+
+const char *PluginManager::GetPlatformPluginDescriptionAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+ if (idx < instances.size())
+ return instances[idx].description.c_str();
+ return nullptr;
+}
+
+bool PluginManager::UnregisterPlugin(PlatformCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+
+ PlatformInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+PlatformCreateInstance
+PluginManager::GetPlatformCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+PlatformCreateInstance
+PluginManager::GetPlatformCreateCallbackForPluginName(ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+
+ PlatformInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+size_t PluginManager::AutoCompletePlatformName(llvm::StringRef name,
+ StringList &matches) {
+ if (name.empty())
+ return matches.GetSize();
+
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+ llvm::StringRef name_sref(name);
+
+ PlatformInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ llvm::StringRef plugin_name(pos->name.GetCString());
+ if (plugin_name.startswith(name_sref))
+ matches.AppendString(plugin_name.data());
+ }
+ return matches.GetSize();
+}
+
+#pragma mark Process
+
+struct ProcessInstance {
+ ProcessInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ ProcessCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<ProcessInstance> ProcessInstances;
+
+static std::recursive_mutex &GetProcessMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static ProcessInstances &GetProcessInstances() {
+ static ProcessInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ ProcessCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ ProcessInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ GetProcessInstances().push_back(instance);
+ }
+ return false;
+}
+
+const char *PluginManager::GetProcessPluginNameAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+ if (idx < instances.size())
+ return instances[idx].name.GetCString();
+ return nullptr;
+}
+
+const char *PluginManager::GetProcessPluginDescriptionAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+ if (idx < instances.size())
+ return instances[idx].description.c_str();
+ return nullptr;
+}
+
+bool PluginManager::UnregisterPlugin(ProcessCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+
+ ProcessInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ProcessCreateInstance
+PluginManager::GetProcessCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+ProcessCreateInstance
+PluginManager::GetProcessCreateCallbackForPluginName(ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+
+ ProcessInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark ScriptInterpreter
+
+struct ScriptInterpreterInstance {
+ ScriptInterpreterInstance()
+ : name(), language(lldb::eScriptLanguageNone), description(),
+ create_callback(nullptr) {}
+
+ ConstString name;
+ lldb::ScriptLanguage language;
+ std::string description;
+ ScriptInterpreterCreateInstance create_callback;
+};
+
+typedef std::vector<ScriptInterpreterInstance> ScriptInterpreterInstances;
+
+static std::recursive_mutex &GetScriptInterpreterMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static ScriptInterpreterInstances &GetScriptInterpreterInstances() {
+ static ScriptInterpreterInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ lldb::ScriptLanguage script_language,
+ ScriptInterpreterCreateInstance create_callback) {
+ if (!create_callback)
+ return false;
+ ScriptInterpreterInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.language = script_language;
+ std::lock_guard<std::recursive_mutex> guard(GetScriptInterpreterMutex());
+ GetScriptInterpreterInstances().push_back(instance);
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ ScriptInterpreterCreateInstance create_callback) {
+ if (!create_callback)
+ return false;
+ std::lock_guard<std::recursive_mutex> guard(GetScriptInterpreterMutex());
+ ScriptInterpreterInstances &instances = GetScriptInterpreterInstances();
+
+ ScriptInterpreterInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback != create_callback)
+ continue;
+
+ instances.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+ScriptInterpreterCreateInstance
+PluginManager::GetScriptInterpreterCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetScriptInterpreterMutex());
+ ScriptInterpreterInstances &instances = GetScriptInterpreterInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+lldb::ScriptInterpreterSP
+PluginManager::GetScriptInterpreterForLanguage(lldb::ScriptLanguage script_lang,
+ Debugger &debugger) {
+ std::lock_guard<std::recursive_mutex> guard(GetScriptInterpreterMutex());
+ ScriptInterpreterInstances &instances = GetScriptInterpreterInstances();
+
+ ScriptInterpreterInstances::iterator pos, end = instances.end();
+ ScriptInterpreterCreateInstance none_instance = nullptr;
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->language == lldb::eScriptLanguageNone)
+ none_instance = pos->create_callback;
+
+ if (script_lang == pos->language)
+ return pos->create_callback(debugger);
+ }
+
+ // If we didn't find one, return the ScriptInterpreter for the null language.
+ assert(none_instance != nullptr);
+ return none_instance(debugger);
+}
+
+#pragma mark -
+#pragma mark StructuredDataPlugin
+
+// StructuredDataPlugin
+
+struct StructuredDataPluginInstance {
+ StructuredDataPluginInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr), filter_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ StructuredDataPluginCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+ StructuredDataFilterLaunchInfo filter_callback;
+};
+
+typedef std::vector<StructuredDataPluginInstance> StructuredDataPluginInstances;
+
+static std::recursive_mutex &GetStructuredDataPluginMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static StructuredDataPluginInstances &GetStructuredDataPluginInstances() {
+ static StructuredDataPluginInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ StructuredDataPluginCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback,
+ StructuredDataFilterLaunchInfo filter_callback) {
+ if (create_callback) {
+ StructuredDataPluginInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ instance.filter_callback = filter_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ GetStructuredDataPluginInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ StructuredDataPluginCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ StructuredDataPluginInstances &instances =
+ GetStructuredDataPluginInstances();
+
+ StructuredDataPluginInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+StructuredDataPluginCreateInstance
+PluginManager::GetStructuredDataPluginCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ StructuredDataPluginInstances &instances = GetStructuredDataPluginInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+StructuredDataPluginCreateInstance
+PluginManager::GetStructuredDataPluginCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ StructuredDataPluginInstances &instances =
+ GetStructuredDataPluginInstances();
+
+ StructuredDataPluginInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+StructuredDataFilterLaunchInfo
+PluginManager::GetStructuredDataFilterCallbackAtIndex(
+ uint32_t idx, bool &iteration_complete) {
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ StructuredDataPluginInstances &instances = GetStructuredDataPluginInstances();
+ if (idx < instances.size()) {
+ iteration_complete = false;
+ return instances[idx].filter_callback;
+ } else {
+ iteration_complete = true;
+ }
+ return nullptr;
+}
+
+#pragma mark SymbolFile
+
+struct SymbolFileInstance {
+ SymbolFileInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ SymbolFileCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<SymbolFileInstance> SymbolFileInstances;
+
+static std::recursive_mutex &GetSymbolFileMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static SymbolFileInstances &GetSymbolFileInstances() {
+ static SymbolFileInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ SymbolFileCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ SymbolFileInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolFileMutex());
+ GetSymbolFileInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(SymbolFileCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolFileMutex());
+ SymbolFileInstances &instances = GetSymbolFileInstances();
+
+ SymbolFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+SymbolFileCreateInstance
+PluginManager::GetSymbolFileCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolFileMutex());
+ SymbolFileInstances &instances = GetSymbolFileInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+SymbolFileCreateInstance
+PluginManager::GetSymbolFileCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolFileMutex());
+ SymbolFileInstances &instances = GetSymbolFileInstances();
+
+ SymbolFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark SymbolVendor
+
+struct SymbolVendorInstance {
+ SymbolVendorInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ SymbolVendorCreateInstance create_callback;
+};
+
+typedef std::vector<SymbolVendorInstance> SymbolVendorInstances;
+
+static std::recursive_mutex &GetSymbolVendorMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static SymbolVendorInstances &GetSymbolVendorInstances() {
+ static SymbolVendorInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(ConstString name,
+ const char *description,
+ SymbolVendorCreateInstance create_callback) {
+ if (create_callback) {
+ SymbolVendorInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolVendorMutex());
+ GetSymbolVendorInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ SymbolVendorCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolVendorMutex());
+ SymbolVendorInstances &instances = GetSymbolVendorInstances();
+
+ SymbolVendorInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+SymbolVendorCreateInstance
+PluginManager::GetSymbolVendorCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolVendorMutex());
+ SymbolVendorInstances &instances = GetSymbolVendorInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+SymbolVendorCreateInstance
+PluginManager::GetSymbolVendorCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolVendorMutex());
+ SymbolVendorInstances &instances = GetSymbolVendorInstances();
+
+ SymbolVendorInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark UnwindAssembly
+
+struct UnwindAssemblyInstance {
+ UnwindAssemblyInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ UnwindAssemblyCreateInstance create_callback;
+};
+
+typedef std::vector<UnwindAssemblyInstance> UnwindAssemblyInstances;
+
+static std::recursive_mutex &GetUnwindAssemblyMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static UnwindAssemblyInstances &GetUnwindAssemblyInstances() {
+ static UnwindAssemblyInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ UnwindAssemblyCreateInstance create_callback) {
+ if (create_callback) {
+ UnwindAssemblyInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetUnwindAssemblyMutex());
+ GetUnwindAssemblyInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ UnwindAssemblyCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetUnwindAssemblyMutex());
+ UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances();
+
+ UnwindAssemblyInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+UnwindAssemblyCreateInstance
+PluginManager::GetUnwindAssemblyCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetUnwindAssemblyMutex());
+ UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+UnwindAssemblyCreateInstance
+PluginManager::GetUnwindAssemblyCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetUnwindAssemblyMutex());
+ UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances();
+
+ UnwindAssemblyInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark MemoryHistory
+
+struct MemoryHistoryInstance {
+ MemoryHistoryInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ MemoryHistoryCreateInstance create_callback;
+};
+
+typedef std::vector<MemoryHistoryInstance> MemoryHistoryInstances;
+
+static std::recursive_mutex &GetMemoryHistoryMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static MemoryHistoryInstances &GetMemoryHistoryInstances() {
+ static MemoryHistoryInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ MemoryHistoryCreateInstance create_callback) {
+ if (create_callback) {
+ MemoryHistoryInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetMemoryHistoryMutex());
+ GetMemoryHistoryInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ MemoryHistoryCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetMemoryHistoryMutex());
+ MemoryHistoryInstances &instances = GetMemoryHistoryInstances();
+
+ MemoryHistoryInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+MemoryHistoryCreateInstance
+PluginManager::GetMemoryHistoryCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetMemoryHistoryMutex());
+ MemoryHistoryInstances &instances = GetMemoryHistoryInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+MemoryHistoryCreateInstance
+PluginManager::GetMemoryHistoryCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetMemoryHistoryMutex());
+ MemoryHistoryInstances &instances = GetMemoryHistoryInstances();
+
+ MemoryHistoryInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark InstrumentationRuntime
+
+struct InstrumentationRuntimeInstance {
+ InstrumentationRuntimeInstance()
+ : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ InstrumentationRuntimeCreateInstance create_callback;
+ InstrumentationRuntimeGetType get_type_callback;
+};
+
+typedef std::vector<InstrumentationRuntimeInstance>
+ InstrumentationRuntimeInstances;
+
+static std::recursive_mutex &GetInstrumentationRuntimeMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static InstrumentationRuntimeInstances &GetInstrumentationRuntimeInstances() {
+ static InstrumentationRuntimeInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ InstrumentationRuntimeCreateInstance create_callback,
+ InstrumentationRuntimeGetType get_type_callback) {
+ if (create_callback) {
+ InstrumentationRuntimeInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.get_type_callback = get_type_callback;
+ std::lock_guard<std::recursive_mutex> guard(
+ GetInstrumentationRuntimeMutex());
+ GetInstrumentationRuntimeInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ InstrumentationRuntimeCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetInstrumentationRuntimeMutex());
+ InstrumentationRuntimeInstances &instances =
+ GetInstrumentationRuntimeInstances();
+
+ InstrumentationRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+InstrumentationRuntimeGetType
+PluginManager::GetInstrumentationRuntimeGetTypeCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetInstrumentationRuntimeMutex());
+ InstrumentationRuntimeInstances &instances =
+ GetInstrumentationRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].get_type_callback;
+ return nullptr;
+}
+
+InstrumentationRuntimeCreateInstance
+PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetInstrumentationRuntimeMutex());
+ InstrumentationRuntimeInstances &instances =
+ GetInstrumentationRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+InstrumentationRuntimeCreateInstance
+PluginManager::GetInstrumentationRuntimeCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetInstrumentationRuntimeMutex());
+ InstrumentationRuntimeInstances &instances =
+ GetInstrumentationRuntimeInstances();
+
+ InstrumentationRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark TypeSystem
+
+struct TypeSystemInstance {
+ TypeSystemInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ TypeSystemCreateInstance create_callback;
+ TypeSystemEnumerateSupportedLanguages enumerate_callback;
+};
+
+typedef std::vector<TypeSystemInstance> TypeSystemInstances;
+
+static std::recursive_mutex &GetTypeSystemMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static TypeSystemInstances &GetTypeSystemInstances() {
+ static TypeSystemInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(ConstString name,
+ const char *description,
+ TypeSystemCreateInstance create_callback,
+ TypeSystemEnumerateSupportedLanguages
+ enumerate_supported_languages_callback) {
+ if (create_callback) {
+ TypeSystemInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.enumerate_callback = enumerate_supported_languages_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ GetTypeSystemInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(TypeSystemCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ TypeSystemInstances &instances = GetTypeSystemInstances();
+
+ TypeSystemInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+TypeSystemCreateInstance
+PluginManager::GetTypeSystemCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ TypeSystemInstances &instances = GetTypeSystemInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+TypeSystemCreateInstance
+PluginManager::GetTypeSystemCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ TypeSystemInstances &instances = GetTypeSystemInstances();
+
+ TypeSystemInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+TypeSystemEnumerateSupportedLanguages
+PluginManager::GetTypeSystemEnumerateSupportedLanguagesCallbackAtIndex(
+ uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ TypeSystemInstances &instances = GetTypeSystemInstances();
+ if (idx < instances.size())
+ return instances[idx].enumerate_callback;
+ return nullptr;
+}
+
+TypeSystemEnumerateSupportedLanguages
+PluginManager::GetTypeSystemEnumerateSupportedLanguagesCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ TypeSystemInstances &instances = GetTypeSystemInstances();
+
+ TypeSystemInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->enumerate_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark REPL
+
+struct REPLInstance {
+ REPLInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ REPLCreateInstance create_callback;
+ REPLEnumerateSupportedLanguages enumerate_languages_callback;
+};
+
+typedef std::vector<REPLInstance> REPLInstances;
+
+static std::recursive_mutex &GetREPLMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static REPLInstances &GetREPLInstances() {
+ static REPLInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ REPLCreateInstance create_callback,
+ REPLEnumerateSupportedLanguages enumerate_languages_callback) {
+ if (create_callback) {
+ REPLInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.enumerate_languages_callback = enumerate_languages_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetREPLMutex());
+ GetREPLInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(REPLCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetREPLMutex());
+ REPLInstances &instances = GetREPLInstances();
+
+ REPLInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+REPLCreateInstance PluginManager::GetREPLCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetREPLMutex());
+ REPLInstances &instances = GetREPLInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+REPLCreateInstance
+PluginManager::GetREPLCreateCallbackForPluginName(ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetREPLMutex());
+ REPLInstances &instances = GetREPLInstances();
+
+ REPLInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+REPLEnumerateSupportedLanguages
+PluginManager::GetREPLEnumerateSupportedLanguagesCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetREPLMutex());
+ REPLInstances &instances = GetREPLInstances();
+ if (idx < instances.size())
+ return instances[idx].enumerate_languages_callback;
+ return nullptr;
+}
+
+REPLEnumerateSupportedLanguages
+PluginManager::GetREPLSystemEnumerateSupportedLanguagesCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetREPLMutex());
+ REPLInstances &instances = GetREPLInstances();
+
+ REPLInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->enumerate_languages_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark PluginManager
+
+void PluginManager::DebuggerInitialize(Debugger &debugger) {
+ // Initialize the DynamicLoader plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetDynamicLoaderMutex());
+ DynamicLoaderInstances &instances = GetDynamicLoaderInstances();
+
+ DynamicLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->debugger_init_callback)
+ pos->debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the JITLoader plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetJITLoaderMutex());
+ JITLoaderInstances &instances = GetJITLoaderInstances();
+
+ JITLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->debugger_init_callback)
+ pos->debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the Platform plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+
+ PlatformInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->debugger_init_callback)
+ pos->debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the Process plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+
+ ProcessInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->debugger_init_callback)
+ pos->debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the SymbolFile plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolFileMutex());
+ for (auto &sym_file : GetSymbolFileInstances()) {
+ if (sym_file.debugger_init_callback)
+ sym_file.debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the OperatingSystem plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetOperatingSystemMutex());
+ for (auto &os : GetOperatingSystemInstances()) {
+ if (os.debugger_init_callback)
+ os.debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the StructuredDataPlugin plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ for (auto &plugin : GetStructuredDataPluginInstances()) {
+ if (plugin.debugger_init_callback)
+ plugin.debugger_init_callback(debugger);
+ }
+ }
+}
+
+// This is the preferred new way to register plugin specific settings. e.g.
+// This will put a plugin's settings under e.g.
+// "plugin.<plugin_type_name>.<plugin_type_desc>.SETTINGNAME".
+static lldb::OptionValuePropertiesSP GetDebuggerPropertyForPlugins(
+ Debugger &debugger, ConstString plugin_type_name,
+ ConstString plugin_type_desc, bool can_create) {
+ lldb::OptionValuePropertiesSP parent_properties_sp(
+ debugger.GetValueProperties());
+ if (parent_properties_sp) {
+ static ConstString g_property_name("plugin");
+
+ OptionValuePropertiesSP plugin_properties_sp =
+ parent_properties_sp->GetSubProperty(nullptr, g_property_name);
+ if (!plugin_properties_sp && can_create) {
+ plugin_properties_sp =
+ std::make_shared<OptionValueProperties>(g_property_name);
+ parent_properties_sp->AppendProperty(
+ g_property_name, ConstString("Settings specify to plugins."), true,
+ plugin_properties_sp);
+ }
+
+ if (plugin_properties_sp) {
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp =
+ plugin_properties_sp->GetSubProperty(nullptr, plugin_type_name);
+ if (!plugin_type_properties_sp && can_create) {
+ plugin_type_properties_sp =
+ std::make_shared<OptionValueProperties>(plugin_type_name);
+ plugin_properties_sp->AppendProperty(plugin_type_name, plugin_type_desc,
+ true, plugin_type_properties_sp);
+ }
+ return plugin_type_properties_sp;
+ }
+ }
+ return lldb::OptionValuePropertiesSP();
+}
+
+// This is deprecated way to register plugin specific settings. e.g.
+// "<plugin_type_name>.plugin.<plugin_type_desc>.SETTINGNAME" and Platform
+// generic settings would be under "platform.SETTINGNAME".
+static lldb::OptionValuePropertiesSP GetDebuggerPropertyForPluginsOldStyle(
+ Debugger &debugger, ConstString plugin_type_name,
+ ConstString plugin_type_desc, bool can_create) {
+ static ConstString g_property_name("plugin");
+ lldb::OptionValuePropertiesSP parent_properties_sp(
+ debugger.GetValueProperties());
+ if (parent_properties_sp) {
+ OptionValuePropertiesSP plugin_properties_sp =
+ parent_properties_sp->GetSubProperty(nullptr, plugin_type_name);
+ if (!plugin_properties_sp && can_create) {
+ plugin_properties_sp =
+ std::make_shared<OptionValueProperties>(plugin_type_name);
+ parent_properties_sp->AppendProperty(plugin_type_name, plugin_type_desc,
+ true, plugin_properties_sp);
+ }
+
+ if (plugin_properties_sp) {
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp =
+ plugin_properties_sp->GetSubProperty(nullptr, g_property_name);
+ if (!plugin_type_properties_sp && can_create) {
+ plugin_type_properties_sp =
+ std::make_shared<OptionValueProperties>(g_property_name);
+ plugin_properties_sp->AppendProperty(
+ g_property_name, ConstString("Settings specific to plugins"), true,
+ plugin_type_properties_sp);
+ }
+ return plugin_type_properties_sp;
+ }
+ }
+ return lldb::OptionValuePropertiesSP();
+}
+
+namespace {
+
+typedef lldb::OptionValuePropertiesSP
+GetDebuggerPropertyForPluginsPtr(Debugger &, ConstString ,
+ ConstString , bool can_create);
+
+lldb::OptionValuePropertiesSP
+GetSettingForPlugin(Debugger &debugger, ConstString setting_name,
+ ConstString plugin_type_name,
+ GetDebuggerPropertyForPluginsPtr get_debugger_property =
+ GetDebuggerPropertyForPlugins) {
+ lldb::OptionValuePropertiesSP properties_sp;
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp(get_debugger_property(
+ debugger, plugin_type_name,
+ ConstString(), // not creating to so we don't need the description
+ false));
+ if (plugin_type_properties_sp)
+ properties_sp =
+ plugin_type_properties_sp->GetSubProperty(nullptr, setting_name);
+ return properties_sp;
+}
+
+bool CreateSettingForPlugin(
+ Debugger &debugger, ConstString plugin_type_name,
+ ConstString plugin_type_desc,
+ const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property,
+ GetDebuggerPropertyForPluginsPtr get_debugger_property =
+ GetDebuggerPropertyForPlugins) {
+ if (properties_sp) {
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp(
+ get_debugger_property(debugger, plugin_type_name, plugin_type_desc,
+ true));
+ if (plugin_type_properties_sp) {
+ plugin_type_properties_sp->AppendProperty(properties_sp->GetName(),
+ description, is_global_property,
+ properties_sp);
+ return true;
+ }
+ }
+ return false;
+}
+
+const char *kDynamicLoaderPluginName("dynamic-loader");
+const char *kPlatformPluginName("platform");
+const char *kProcessPluginName("process");
+const char *kSymbolFilePluginName("symbol-file");
+const char *kJITLoaderPluginName("jit-loader");
+const char *kStructuredDataPluginName("structured-data");
+
+} // anonymous namespace
+
+lldb::OptionValuePropertiesSP PluginManager::GetSettingForDynamicLoaderPlugin(
+ Debugger &debugger, ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kDynamicLoaderPluginName));
+}
+
+bool PluginManager::CreateSettingForDynamicLoaderPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(
+ debugger, ConstString(kDynamicLoaderPluginName),
+ ConstString("Settings for dynamic loader plug-ins"), properties_sp,
+ description, is_global_property);
+}
+
+lldb::OptionValuePropertiesSP
+PluginManager::GetSettingForPlatformPlugin(Debugger &debugger,
+ ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kPlatformPluginName),
+ GetDebuggerPropertyForPluginsOldStyle);
+}
+
+bool PluginManager::CreateSettingForPlatformPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(debugger, ConstString(kPlatformPluginName),
+ ConstString("Settings for platform plug-ins"),
+ properties_sp, description, is_global_property,
+ GetDebuggerPropertyForPluginsOldStyle);
+}
+
+lldb::OptionValuePropertiesSP
+PluginManager::GetSettingForProcessPlugin(Debugger &debugger,
+ ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kProcessPluginName));
+}
+
+bool PluginManager::CreateSettingForProcessPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(debugger, ConstString(kProcessPluginName),
+ ConstString("Settings for process plug-ins"),
+ properties_sp, description, is_global_property);
+}
+
+lldb::OptionValuePropertiesSP
+PluginManager::GetSettingForSymbolFilePlugin(Debugger &debugger,
+ ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kSymbolFilePluginName));
+}
+
+bool PluginManager::CreateSettingForSymbolFilePlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(
+ debugger, ConstString(kSymbolFilePluginName),
+ ConstString("Settings for symbol file plug-ins"), properties_sp,
+ description, is_global_property);
+}
+
+lldb::OptionValuePropertiesSP
+PluginManager::GetSettingForJITLoaderPlugin(Debugger &debugger,
+ ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kJITLoaderPluginName));
+}
+
+bool PluginManager::CreateSettingForJITLoaderPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(debugger, ConstString(kJITLoaderPluginName),
+ ConstString("Settings for JIT loader plug-ins"),
+ properties_sp, description, is_global_property);
+}
+
+static const char *kOperatingSystemPluginName("os");
+
+lldb::OptionValuePropertiesSP PluginManager::GetSettingForOperatingSystemPlugin(
+ Debugger &debugger, ConstString setting_name) {
+ lldb::OptionValuePropertiesSP properties_sp;
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp(
+ GetDebuggerPropertyForPlugins(
+ debugger, ConstString(kOperatingSystemPluginName),
+ ConstString(), // not creating to so we don't need the description
+ false));
+ if (plugin_type_properties_sp)
+ properties_sp =
+ plugin_type_properties_sp->GetSubProperty(nullptr, setting_name);
+ return properties_sp;
+}
+
+bool PluginManager::CreateSettingForOperatingSystemPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ if (properties_sp) {
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp(
+ GetDebuggerPropertyForPlugins(
+ debugger, ConstString(kOperatingSystemPluginName),
+ ConstString("Settings for operating system plug-ins"), true));
+ if (plugin_type_properties_sp) {
+ plugin_type_properties_sp->AppendProperty(properties_sp->GetName(),
+ description, is_global_property,
+ properties_sp);
+ return true;
+ }
+ }
+ return false;
+}
+
+lldb::OptionValuePropertiesSP PluginManager::GetSettingForStructuredDataPlugin(
+ Debugger &debugger, ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kStructuredDataPluginName));
+}
+
+bool PluginManager::CreateSettingForStructuredDataPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(
+ debugger, ConstString(kStructuredDataPluginName),
+ ConstString("Settings for structured data plug-ins"), properties_sp,
+ description, is_global_property);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/RichManglingContext.cpp b/contrib/llvm-project/lldb/source/Core/RichManglingContext.cpp
new file mode 100644
index 000000000000..3d1941ebdd26
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/RichManglingContext.cpp
@@ -0,0 +1,172 @@
+//===-- RichManglingContext.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/RichManglingContext.h"
+
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+
+#include "llvm/ADT/StringRef.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// RichManglingContext
+void RichManglingContext::ResetProvider(InfoProvider new_provider) {
+ // If we want to support parsers for other languages some day, we need a
+ // switch here to delete the correct parser type.
+ if (m_cxx_method_parser.hasValue()) {
+ assert(m_provider == PluginCxxLanguage);
+ delete get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser);
+ m_cxx_method_parser.reset();
+ }
+
+ assert(new_provider != None && "Only reset to a valid provider");
+ m_provider = new_provider;
+}
+
+bool RichManglingContext::FromItaniumName(ConstString mangled) {
+ bool err = m_ipd.partialDemangle(mangled.GetCString());
+ if (!err) {
+ ResetProvider(ItaniumPartialDemangler);
+ }
+
+ if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) {
+ if (!err) {
+ ParseFullName();
+ LLDB_LOG(log, "demangled itanium: {0} -> \"{1}\"", mangled, m_ipd_buf);
+ } else {
+ LLDB_LOG(log, "demangled itanium: {0} -> error: failed to demangle",
+ mangled);
+ }
+ }
+
+ return !err; // true == success
+}
+
+bool RichManglingContext::FromCxxMethodName(ConstString demangled) {
+ ResetProvider(PluginCxxLanguage);
+ m_cxx_method_parser = new CPlusPlusLanguage::MethodName(demangled);
+ return true;
+}
+
+bool RichManglingContext::IsCtorOrDtor() const {
+ assert(m_provider != None && "Initialize a provider first");
+ switch (m_provider) {
+ case ItaniumPartialDemangler:
+ return m_ipd.isCtorOrDtor();
+ case PluginCxxLanguage: {
+ // We can only check for destructors here.
+ auto base_name =
+ get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser)->GetBasename();
+ return base_name.startswith("~");
+ }
+ case None:
+ return false;
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+bool RichManglingContext::IsFunction() const {
+ assert(m_provider != None && "Initialize a provider first");
+ switch (m_provider) {
+ case ItaniumPartialDemangler:
+ return m_ipd.isFunction();
+ case PluginCxxLanguage:
+ return get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser)->IsValid();
+ case None:
+ return false;
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+void RichManglingContext::processIPDStrResult(char *ipd_res, size_t res_size) {
+ // Error case: Clear the buffer.
+ if (LLVM_UNLIKELY(ipd_res == nullptr)) {
+ assert(res_size == m_ipd_buf_size &&
+ "Failed IPD queries keep the original size in the N parameter");
+
+ m_ipd_buf[0] = '\0';
+ m_buffer = llvm::StringRef(m_ipd_buf, 0);
+ return;
+ }
+
+ // IPD's res_size includes null terminator.
+ assert(ipd_res[res_size - 1] == '\0' &&
+ "IPD returns null-terminated strings and we rely on that");
+
+ // Update buffer/size on realloc.
+ if (LLVM_UNLIKELY(ipd_res != m_ipd_buf || res_size > m_ipd_buf_size)) {
+ m_ipd_buf = ipd_res; // std::realloc freed or reused the old buffer.
+ m_ipd_buf_size = res_size; // May actually be bigger, but we can't know.
+
+ if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE))
+ LLDB_LOG(log, "ItaniumPartialDemangler Realloc: new buffer size is {0}",
+ m_ipd_buf_size);
+ }
+
+ // 99% case: Just remember the string length.
+ m_buffer = llvm::StringRef(m_ipd_buf, res_size - 1);
+}
+
+void RichManglingContext::ParseFunctionBaseName() {
+ assert(m_provider != None && "Initialize a provider first");
+ switch (m_provider) {
+ case ItaniumPartialDemangler: {
+ auto n = m_ipd_buf_size;
+ auto buf = m_ipd.getFunctionBaseName(m_ipd_buf, &n);
+ processIPDStrResult(buf, n);
+ return;
+ }
+ case PluginCxxLanguage:
+ m_buffer =
+ get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser)->GetBasename();
+ return;
+ case None:
+ return;
+ }
+}
+
+void RichManglingContext::ParseFunctionDeclContextName() {
+ assert(m_provider != None && "Initialize a provider first");
+ switch (m_provider) {
+ case ItaniumPartialDemangler: {
+ auto n = m_ipd_buf_size;
+ auto buf = m_ipd.getFunctionDeclContextName(m_ipd_buf, &n);
+ processIPDStrResult(buf, n);
+ return;
+ }
+ case PluginCxxLanguage:
+ m_buffer =
+ get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser)->GetContext();
+ return;
+ case None:
+ return;
+ }
+}
+
+void RichManglingContext::ParseFullName() {
+ assert(m_provider != None && "Initialize a provider first");
+ switch (m_provider) {
+ case ItaniumPartialDemangler: {
+ auto n = m_ipd_buf_size;
+ auto buf = m_ipd.finishDemangle(m_ipd_buf, &n);
+ processIPDStrResult(buf, n);
+ return;
+ }
+ case PluginCxxLanguage:
+ m_buffer = get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser)
+ ->GetFullName()
+ .GetStringRef();
+ return;
+ case None:
+ return;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Core/SearchFilter.cpp b/contrib/llvm-project/lldb/source/Core/SearchFilter.cpp
new file mode 100644
index 000000000000..531fa078de29
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/SearchFilter.cpp
@@ -0,0 +1,853 @@
+//===-- SearchFilter.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/SearchFilter.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include <inttypes.h>
+#include <string.h>
+
+namespace lldb_private {
+class Address;
+}
+namespace lldb_private {
+class Function;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *SearchFilter::g_ty_to_name[] = {"Unconstrained", "Exception",
+ "Module", "Modules",
+ "ModulesAndCU", "Unknown"};
+
+const char
+ *SearchFilter::g_option_names[SearchFilter::OptionNames::LastOptionName] = {
+ "ModuleList", "CUList"};
+
+const char *SearchFilter::FilterTyToName(enum FilterTy type) {
+ if (type > LastKnownFilterType)
+ return g_ty_to_name[UnknownFilter];
+
+ return g_ty_to_name[type];
+}
+
+SearchFilter::FilterTy SearchFilter::NameToFilterTy(llvm::StringRef name) {
+ for (size_t i = 0; i <= LastKnownFilterType; i++) {
+ if (name == g_ty_to_name[i])
+ return (FilterTy)i;
+ }
+ return UnknownFilter;
+}
+
+Searcher::Searcher() = default;
+
+Searcher::~Searcher() = default;
+
+void Searcher::GetDescription(Stream *s) {}
+
+SearchFilter::SearchFilter(const TargetSP &target_sp, unsigned char filterType)
+ : m_target_sp(target_sp), SubclassID(filterType) {}
+
+SearchFilter::~SearchFilter() = default;
+
+SearchFilterSP SearchFilter::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &filter_dict,
+ Status &error) {
+ SearchFilterSP result_sp;
+ if (!filter_dict.IsValid()) {
+ error.SetErrorString("Can't deserialize from an invalid data object.");
+ return result_sp;
+ }
+
+ llvm::StringRef subclass_name;
+
+ bool success = filter_dict.GetValueForKeyAsString(
+ GetSerializationSubclassKey(), subclass_name);
+ if (!success) {
+ error.SetErrorStringWithFormat("Filter data missing subclass key");
+ return result_sp;
+ }
+
+ FilterTy filter_type = NameToFilterTy(subclass_name);
+ if (filter_type == UnknownFilter) {
+ error.SetErrorStringWithFormatv("Unknown filter type: {0}.", subclass_name);
+ return result_sp;
+ }
+
+ StructuredData::Dictionary *subclass_options = nullptr;
+ success = filter_dict.GetValueForKeyAsDictionary(
+ GetSerializationSubclassOptionsKey(), subclass_options);
+ if (!success || !subclass_options || !subclass_options->IsValid()) {
+ error.SetErrorString("Filter data missing subclass options key.");
+ return result_sp;
+ }
+
+ switch (filter_type) {
+ case Unconstrained:
+ result_sp = SearchFilterForUnconstrainedSearches::CreateFromStructuredData(
+ target, *subclass_options, error);
+ break;
+ case ByModule:
+ result_sp = SearchFilterByModule::CreateFromStructuredData(
+ target, *subclass_options, error);
+ break;
+ case ByModules:
+ result_sp = SearchFilterByModuleList::CreateFromStructuredData(
+ target, *subclass_options, error);
+ break;
+ case ByModulesAndCU:
+ result_sp = SearchFilterByModuleListAndCU::CreateFromStructuredData(
+ target, *subclass_options, error);
+ break;
+ case Exception:
+ error.SetErrorString("Can't serialize exception breakpoints yet.");
+ break;
+ default:
+ llvm_unreachable("Should never get an uresolvable filter type.");
+ }
+
+ return result_sp;
+}
+
+bool SearchFilter::ModulePasses(const FileSpec &spec) { return true; }
+
+bool SearchFilter::ModulePasses(const ModuleSP &module_sp) { return true; }
+
+bool SearchFilter::AddressPasses(Address &address) { return true; }
+
+bool SearchFilter::CompUnitPasses(FileSpec &fileSpec) { return true; }
+
+bool SearchFilter::CompUnitPasses(CompileUnit &compUnit) { return true; }
+
+bool SearchFilter::FunctionPasses(Function &function) {
+ // This is a slightly cheesy job, but since we don't have finer grained
+ // filters yet, just checking that the start address passes is probably
+ // good enough for the base class behavior.
+ Address addr = function.GetAddressRange().GetBaseAddress();
+ return AddressPasses(addr);
+}
+
+
+uint32_t SearchFilter::GetFilterRequiredItems() {
+ return (lldb::SymbolContextItem)0;
+}
+
+void SearchFilter::GetDescription(Stream *s) {}
+
+void SearchFilter::Dump(Stream *s) const {}
+
+lldb::SearchFilterSP SearchFilter::CopyForBreakpoint(Breakpoint &breakpoint) {
+ SearchFilterSP ret_sp = DoCopyForBreakpoint(breakpoint);
+ TargetSP target_sp = breakpoint.GetTargetSP();
+ ret_sp->SetTarget(target_sp);
+ return ret_sp;
+}
+
+// Helper functions for serialization.
+
+StructuredData::DictionarySP
+SearchFilter::WrapOptionsDict(StructuredData::DictionarySP options_dict_sp) {
+ if (!options_dict_sp || !options_dict_sp->IsValid())
+ return StructuredData::DictionarySP();
+
+ auto type_dict_sp = std::make_shared<StructuredData::Dictionary>();
+ type_dict_sp->AddStringItem(GetSerializationSubclassKey(), GetFilterName());
+ type_dict_sp->AddItem(GetSerializationSubclassOptionsKey(), options_dict_sp);
+
+ return type_dict_sp;
+}
+
+void SearchFilter::SerializeFileSpecList(
+ StructuredData::DictionarySP &options_dict_sp, OptionNames name,
+ FileSpecList &file_list) {
+ size_t num_modules = file_list.GetSize();
+
+ // Don't serialize empty lists.
+ if (num_modules == 0)
+ return;
+
+ auto module_array_sp = std::make_shared<StructuredData::Array>();
+ for (size_t i = 0; i < num_modules; i++) {
+ module_array_sp->AddItem(std::make_shared<StructuredData::String>(
+ file_list.GetFileSpecAtIndex(i).GetPath()));
+ }
+ options_dict_sp->AddItem(GetKey(name), module_array_sp);
+}
+
+// UTILITY Functions to help iterate down through the elements of the
+// SymbolContext.
+
+void SearchFilter::Search(Searcher &searcher) {
+ SymbolContext empty_sc;
+
+ if (!m_target_sp)
+ return;
+ empty_sc.target_sp = m_target_sp;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthTarget)
+ searcher.SearchCallback(*this, empty_sc, nullptr, false);
+ else
+ DoModuleIteration(empty_sc, searcher);
+}
+
+void SearchFilter::SearchInModuleList(Searcher &searcher, ModuleList &modules) {
+ SymbolContext empty_sc;
+
+ if (!m_target_sp)
+ return;
+ empty_sc.target_sp = m_target_sp;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthTarget)
+ searcher.SearchCallback(*this, empty_sc, nullptr, false);
+ else {
+ std::lock_guard<std::recursive_mutex> guard(modules.GetMutex());
+ const size_t numModules = modules.GetSize();
+
+ for (size_t i = 0; i < numModules; i++) {
+ ModuleSP module_sp(modules.GetModuleAtIndexUnlocked(i));
+ if (ModulePasses(module_sp)) {
+ if (DoModuleIteration(module_sp, searcher) ==
+ Searcher::eCallbackReturnStop)
+ return;
+ }
+ }
+ }
+}
+
+Searcher::CallbackReturn
+SearchFilter::DoModuleIteration(const lldb::ModuleSP &module_sp,
+ Searcher &searcher) {
+ SymbolContext matchingContext(m_target_sp, module_sp);
+ return DoModuleIteration(matchingContext, searcher);
+}
+
+Searcher::CallbackReturn
+SearchFilter::DoModuleIteration(const SymbolContext &context,
+ Searcher &searcher) {
+ if (searcher.GetDepth() >= lldb::eSearchDepthModule) {
+ if (context.module_sp) {
+ if (searcher.GetDepth() == lldb::eSearchDepthModule) {
+ SymbolContext matchingContext(context.module_sp.get());
+ searcher.SearchCallback(*this, matchingContext, nullptr, false);
+ } else {
+ return DoCUIteration(context.module_sp, context, searcher);
+ }
+ } else {
+ const ModuleList &target_images = m_target_sp->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_images.GetMutex());
+
+ size_t n_modules = target_images.GetSize();
+ for (size_t i = 0; i < n_modules; i++) {
+ // If this is the last level supplied, then call the callback directly,
+ // otherwise descend.
+ ModuleSP module_sp(target_images.GetModuleAtIndexUnlocked(i));
+ if (!ModulePasses(module_sp))
+ continue;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthModule) {
+ SymbolContext matchingContext(m_target_sp, module_sp);
+
+ Searcher::CallbackReturn shouldContinue =
+ searcher.SearchCallback(*this, matchingContext, nullptr, false);
+ if (shouldContinue == Searcher::eCallbackReturnStop ||
+ shouldContinue == Searcher::eCallbackReturnPop)
+ return shouldContinue;
+ } else {
+ Searcher::CallbackReturn shouldContinue =
+ DoCUIteration(module_sp, context, searcher);
+ if (shouldContinue == Searcher::eCallbackReturnStop)
+ return shouldContinue;
+ else if (shouldContinue == Searcher::eCallbackReturnPop)
+ continue;
+ }
+ }
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+Searcher::CallbackReturn
+SearchFilter::DoCUIteration(const ModuleSP &module_sp,
+ const SymbolContext &context, Searcher &searcher) {
+ Searcher::CallbackReturn shouldContinue;
+ if (context.comp_unit == nullptr) {
+ const size_t num_comp_units = module_sp->GetNumCompileUnits();
+ for (size_t i = 0; i < num_comp_units; i++) {
+ CompUnitSP cu_sp(module_sp->GetCompileUnitAtIndex(i));
+ if (cu_sp) {
+ if (!CompUnitPasses(*(cu_sp.get())))
+ continue;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthCompUnit) {
+ SymbolContext matchingContext(m_target_sp, module_sp, cu_sp.get());
+
+ shouldContinue =
+ searcher.SearchCallback(*this, matchingContext, nullptr, false);
+
+ if (shouldContinue == Searcher::eCallbackReturnPop)
+ return Searcher::eCallbackReturnContinue;
+ else if (shouldContinue == Searcher::eCallbackReturnStop)
+ return shouldContinue;
+ } else {
+ // First make sure this compile unit's functions are parsed
+ // since CompUnit::ForeachFunction only iterates over already
+ // parsed functions.
+ SymbolVendor *sym_vendor = module_sp->GetSymbolVendor();
+ if (!sym_vendor)
+ continue;
+ if (!sym_vendor->ParseFunctions(*cu_sp))
+ continue;
+ // If we got any functions, use ForeachFunction to do the iteration.
+ cu_sp->ForeachFunction([&](const FunctionSP &func_sp) {
+ if (!FunctionPasses(*func_sp.get()))
+ return false; // Didn't pass the filter, just keep going.
+ if (searcher.GetDepth() == lldb::eSearchDepthFunction) {
+ SymbolContext matchingContext(m_target_sp, module_sp,
+ cu_sp.get(), func_sp.get());
+ shouldContinue = searcher.SearchCallback(*this,
+ matchingContext,
+ nullptr, false);
+ } else {
+ shouldContinue = DoFunctionIteration(func_sp.get(), context,
+ searcher);
+ }
+ return shouldContinue != Searcher::eCallbackReturnContinue;
+ });
+ }
+ }
+ }
+ } else {
+ if (CompUnitPasses(*context.comp_unit)) {
+ SymbolContext matchingContext(m_target_sp, module_sp, context.comp_unit);
+ return searcher.SearchCallback(*this, matchingContext, nullptr, false);
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+Searcher::CallbackReturn SearchFilter::DoFunctionIteration(
+ Function *function, const SymbolContext &context, Searcher &searcher) {
+ // FIXME: Implement...
+ return Searcher::eCallbackReturnContinue;
+}
+
+// SearchFilterForUnconstrainedSearches:
+// Selects a shared library matching a given file spec, consulting the targets
+// "black list".
+SearchFilterSP SearchFilterForUnconstrainedSearches::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &data_dict,
+ Status &error) {
+ // No options for an unconstrained search.
+ return std::make_shared<SearchFilterForUnconstrainedSearches>(
+ target.shared_from_this());
+}
+
+StructuredData::ObjectSP
+SearchFilterForUnconstrainedSearches::SerializeToStructuredData() {
+ // The options dictionary is an empty dictionary:
+ auto result_sp = std::make_shared<StructuredData::Dictionary>();
+ return WrapOptionsDict(result_sp);
+}
+
+bool SearchFilterForUnconstrainedSearches::ModulePasses(
+ const FileSpec &module_spec) {
+ return !m_target_sp->ModuleIsExcludedForUnconstrainedSearches(module_spec);
+}
+
+bool SearchFilterForUnconstrainedSearches::ModulePasses(
+ const lldb::ModuleSP &module_sp) {
+ if (!module_sp)
+ return true;
+ else if (m_target_sp->ModuleIsExcludedForUnconstrainedSearches(module_sp))
+ return false;
+ else
+ return true;
+}
+
+lldb::SearchFilterSP SearchFilterForUnconstrainedSearches::DoCopyForBreakpoint(
+ Breakpoint &breakpoint) {
+ return std::make_shared<SearchFilterForUnconstrainedSearches>(*this);
+}
+
+// SearchFilterByModule:
+// Selects a shared library matching a given file spec
+
+SearchFilterByModule::SearchFilterByModule(const lldb::TargetSP &target_sp,
+ const FileSpec &module)
+ : SearchFilter(target_sp, FilterTy::ByModule), m_module_spec(module) {}
+
+SearchFilterByModule::~SearchFilterByModule() = default;
+
+bool SearchFilterByModule::ModulePasses(const ModuleSP &module_sp) {
+ return (module_sp &&
+ FileSpec::Equal(module_sp->GetFileSpec(), m_module_spec, false));
+}
+
+bool SearchFilterByModule::ModulePasses(const FileSpec &spec) {
+ // Do a full match only if "spec" has a directory
+ const bool full_match = (bool)spec.GetDirectory();
+ return FileSpec::Equal(spec, m_module_spec, full_match);
+}
+
+bool SearchFilterByModule::AddressPasses(Address &address) {
+ // FIXME: Not yet implemented
+ return true;
+}
+
+bool SearchFilterByModule::CompUnitPasses(FileSpec &fileSpec) { return true; }
+
+bool SearchFilterByModule::CompUnitPasses(CompileUnit &compUnit) {
+ return true;
+}
+
+void SearchFilterByModule::Search(Searcher &searcher) {
+ if (!m_target_sp)
+ return;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthTarget) {
+ SymbolContext empty_sc;
+ empty_sc.target_sp = m_target_sp;
+ searcher.SearchCallback(*this, empty_sc, nullptr, false);
+ }
+
+ // If the module file spec is a full path, then we can just find the one
+ // filespec that passes. Otherwise, we need to go through all modules and
+ // find the ones that match the file name.
+
+ const ModuleList &target_modules = m_target_sp->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+
+ const size_t num_modules = target_modules.GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ Module *module = target_modules.GetModulePointerAtIndexUnlocked(i);
+ const bool full_match = (bool)m_module_spec.GetDirectory();
+ if (FileSpec::Equal(m_module_spec, module->GetFileSpec(), full_match)) {
+ SymbolContext matchingContext(m_target_sp, module->shared_from_this());
+ Searcher::CallbackReturn shouldContinue;
+
+ shouldContinue = DoModuleIteration(matchingContext, searcher);
+ if (shouldContinue == Searcher::eCallbackReturnStop)
+ return;
+ }
+ }
+}
+
+void SearchFilterByModule::GetDescription(Stream *s) {
+ s->PutCString(", module = ");
+ s->PutCString(m_module_spec.GetFilename().AsCString("<Unknown>"));
+}
+
+uint32_t SearchFilterByModule::GetFilterRequiredItems() {
+ return eSymbolContextModule;
+}
+
+void SearchFilterByModule::Dump(Stream *s) const {}
+
+lldb::SearchFilterSP
+SearchFilterByModule::DoCopyForBreakpoint(Breakpoint &breakpoint) {
+ return std::make_shared<SearchFilterByModule>(*this);
+}
+
+SearchFilterSP SearchFilterByModule::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &data_dict,
+ Status &error) {
+ StructuredData::Array *modules_array;
+ bool success = data_dict.GetValueForKeyAsArray(GetKey(OptionNames::ModList),
+ modules_array);
+ if (!success) {
+ error.SetErrorString("SFBM::CFSD: Could not find the module list key.");
+ return nullptr;
+ }
+
+ size_t num_modules = modules_array->GetSize();
+ if (num_modules > 1) {
+ error.SetErrorString(
+ "SFBM::CFSD: Only one modules allowed for SearchFilterByModule.");
+ return nullptr;
+ }
+
+ llvm::StringRef module;
+ success = modules_array->GetItemAtIndexAsString(0, module);
+ if (!success) {
+ error.SetErrorString("SFBM::CFSD: filter module item not a string.");
+ return nullptr;
+ }
+ FileSpec module_spec(module);
+
+ return std::make_shared<SearchFilterByModule>(target.shared_from_this(),
+ module_spec);
+}
+
+StructuredData::ObjectSP SearchFilterByModule::SerializeToStructuredData() {
+ auto options_dict_sp = std::make_shared<StructuredData::Dictionary>();
+ auto module_array_sp = std::make_shared<StructuredData::Array>();
+ module_array_sp->AddItem(
+ std::make_shared<StructuredData::String>(m_module_spec.GetPath()));
+ options_dict_sp->AddItem(GetKey(OptionNames::ModList), module_array_sp);
+ return WrapOptionsDict(options_dict_sp);
+}
+
+// SearchFilterByModuleList:
+// Selects a shared library matching a given file spec
+
+SearchFilterByModuleList::SearchFilterByModuleList(
+ const lldb::TargetSP &target_sp, const FileSpecList &module_list)
+ : SearchFilter(target_sp, FilterTy::ByModules),
+ m_module_spec_list(module_list) {}
+
+SearchFilterByModuleList::SearchFilterByModuleList(
+ const lldb::TargetSP &target_sp, const FileSpecList &module_list,
+ enum FilterTy filter_ty)
+ : SearchFilter(target_sp, filter_ty), m_module_spec_list(module_list) {}
+
+SearchFilterByModuleList &SearchFilterByModuleList::
+operator=(const SearchFilterByModuleList &rhs) {
+ m_target_sp = rhs.m_target_sp;
+ m_module_spec_list = rhs.m_module_spec_list;
+ return *this;
+}
+
+SearchFilterByModuleList::~SearchFilterByModuleList() = default;
+
+bool SearchFilterByModuleList::ModulePasses(const ModuleSP &module_sp) {
+ if (m_module_spec_list.GetSize() == 0)
+ return true;
+
+ return module_sp && m_module_spec_list.FindFileIndex(
+ 0, module_sp->GetFileSpec(), false) != UINT32_MAX;
+}
+
+bool SearchFilterByModuleList::ModulePasses(const FileSpec &spec) {
+ if (m_module_spec_list.GetSize() == 0)
+ return true;
+
+ return m_module_spec_list.FindFileIndex(0, spec, true) != UINT32_MAX;
+}
+
+bool SearchFilterByModuleList::AddressPasses(Address &address) {
+ // FIXME: Not yet implemented
+ return true;
+}
+
+bool SearchFilterByModuleList::CompUnitPasses(FileSpec &fileSpec) {
+ return true;
+}
+
+bool SearchFilterByModuleList::CompUnitPasses(CompileUnit &compUnit) {
+ return true;
+}
+
+void SearchFilterByModuleList::Search(Searcher &searcher) {
+ if (!m_target_sp)
+ return;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthTarget) {
+ SymbolContext empty_sc;
+ empty_sc.target_sp = m_target_sp;
+ searcher.SearchCallback(*this, empty_sc, nullptr, false);
+ }
+
+ // If the module file spec is a full path, then we can just find the one
+ // filespec that passes. Otherwise, we need to go through all modules and
+ // find the ones that match the file name.
+
+ const ModuleList &target_modules = m_target_sp->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+
+ const size_t num_modules = target_modules.GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ Module *module = target_modules.GetModulePointerAtIndexUnlocked(i);
+ if (m_module_spec_list.FindFileIndex(0, module->GetFileSpec(), false) !=
+ UINT32_MAX) {
+ SymbolContext matchingContext(m_target_sp, module->shared_from_this());
+ Searcher::CallbackReturn shouldContinue;
+
+ shouldContinue = DoModuleIteration(matchingContext, searcher);
+ if (shouldContinue == Searcher::eCallbackReturnStop)
+ return;
+ }
+ }
+}
+
+void SearchFilterByModuleList::GetDescription(Stream *s) {
+ size_t num_modules = m_module_spec_list.GetSize();
+ if (num_modules == 1) {
+ s->Printf(", module = ");
+ s->PutCString(
+ m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString(
+ "<Unknown>"));
+ } else {
+ s->Printf(", modules(%" PRIu64 ") = ", (uint64_t)num_modules);
+ for (size_t i = 0; i < num_modules; i++) {
+ s->PutCString(
+ m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString(
+ "<Unknown>"));
+ if (i != num_modules - 1)
+ s->PutCString(", ");
+ }
+ }
+}
+
+uint32_t SearchFilterByModuleList::GetFilterRequiredItems() {
+ return eSymbolContextModule;
+}
+
+void SearchFilterByModuleList::Dump(Stream *s) const {}
+
+lldb::SearchFilterSP
+SearchFilterByModuleList::DoCopyForBreakpoint(Breakpoint &breakpoint) {
+ return std::make_shared<SearchFilterByModuleList>(*this);
+}
+
+SearchFilterSP SearchFilterByModuleList::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &data_dict,
+ Status &error) {
+ StructuredData::Array *modules_array;
+ bool success = data_dict.GetValueForKeyAsArray(GetKey(OptionNames::ModList),
+ modules_array);
+ FileSpecList modules;
+ if (success) {
+ size_t num_modules = modules_array->GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ llvm::StringRef module;
+ success = modules_array->GetItemAtIndexAsString(i, module);
+ if (!success) {
+ error.SetErrorStringWithFormat(
+ "SFBM::CFSD: filter module item %zu not a string.", i);
+ return nullptr;
+ }
+ modules.Append(FileSpec(module));
+ }
+ }
+
+ return std::make_shared<SearchFilterByModuleList>(target.shared_from_this(),
+ modules);
+}
+
+void SearchFilterByModuleList::SerializeUnwrapped(
+ StructuredData::DictionarySP &options_dict_sp) {
+ SerializeFileSpecList(options_dict_sp, OptionNames::ModList,
+ m_module_spec_list);
+}
+
+StructuredData::ObjectSP SearchFilterByModuleList::SerializeToStructuredData() {
+ auto options_dict_sp = std::make_shared<StructuredData::Dictionary>();
+ SerializeUnwrapped(options_dict_sp);
+ return WrapOptionsDict(options_dict_sp);
+}
+
+// SearchFilterByModuleListAndCU:
+// Selects a shared library matching a given file spec
+
+SearchFilterByModuleListAndCU::SearchFilterByModuleListAndCU(
+ const lldb::TargetSP &target_sp, const FileSpecList &module_list,
+ const FileSpecList &cu_list)
+ : SearchFilterByModuleList(target_sp, module_list,
+ FilterTy::ByModulesAndCU),
+ m_cu_spec_list(cu_list) {}
+
+SearchFilterByModuleListAndCU::SearchFilterByModuleListAndCU(
+ const SearchFilterByModuleListAndCU &rhs) = default;
+
+SearchFilterByModuleListAndCU &SearchFilterByModuleListAndCU::
+operator=(const SearchFilterByModuleListAndCU &rhs) {
+ if (&rhs != this) {
+ m_target_sp = rhs.m_target_sp;
+ m_module_spec_list = rhs.m_module_spec_list;
+ m_cu_spec_list = rhs.m_cu_spec_list;
+ }
+ return *this;
+}
+
+SearchFilterByModuleListAndCU::~SearchFilterByModuleListAndCU() = default;
+
+lldb::SearchFilterSP SearchFilterByModuleListAndCU::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &data_dict,
+ Status &error) {
+ StructuredData::Array *modules_array = nullptr;
+ SearchFilterSP result_sp;
+ bool success = data_dict.GetValueForKeyAsArray(GetKey(OptionNames::ModList),
+ modules_array);
+ FileSpecList modules;
+ if (success) {
+ size_t num_modules = modules_array->GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ llvm::StringRef module;
+ success = modules_array->GetItemAtIndexAsString(i, module);
+ if (!success) {
+ error.SetErrorStringWithFormat(
+ "SFBM::CFSD: filter module item %zu not a string.", i);
+ return result_sp;
+ }
+ modules.Append(FileSpec(module));
+ }
+ }
+
+ StructuredData::Array *cus_array = nullptr;
+ success =
+ data_dict.GetValueForKeyAsArray(GetKey(OptionNames::CUList), cus_array);
+ if (!success) {
+ error.SetErrorString("SFBM::CFSD: Could not find the CU list key.");
+ return result_sp;
+ }
+
+ size_t num_cus = cus_array->GetSize();
+ FileSpecList cus;
+ for (size_t i = 0; i < num_cus; i++) {
+ llvm::StringRef cu;
+ success = cus_array->GetItemAtIndexAsString(i, cu);
+ if (!success) {
+ error.SetErrorStringWithFormat(
+ "SFBM::CFSD: filter cu item %zu not a string.", i);
+ return nullptr;
+ }
+ cus.Append(FileSpec(cu));
+ }
+
+ return std::make_shared<SearchFilterByModuleListAndCU>(
+ target.shared_from_this(), modules, cus);
+}
+
+StructuredData::ObjectSP
+SearchFilterByModuleListAndCU::SerializeToStructuredData() {
+ auto options_dict_sp = std::make_shared<StructuredData::Dictionary>();
+ SearchFilterByModuleList::SerializeUnwrapped(options_dict_sp);
+ SerializeFileSpecList(options_dict_sp, OptionNames::CUList, m_cu_spec_list);
+ return WrapOptionsDict(options_dict_sp);
+}
+
+bool SearchFilterByModuleListAndCU::AddressPasses(Address &address) {
+ SymbolContext sym_ctx;
+ address.CalculateSymbolContext(&sym_ctx, eSymbolContextEverything);
+ if (!sym_ctx.comp_unit) {
+ if (m_cu_spec_list.GetSize() != 0)
+ return false; // Has no comp_unit so can't pass the file check.
+ }
+ if (m_cu_spec_list.FindFileIndex(0, sym_ctx.comp_unit, false) == UINT32_MAX)
+ return false; // Fails the file check
+ return SearchFilterByModuleList::ModulePasses(sym_ctx.module_sp);
+}
+
+bool SearchFilterByModuleListAndCU::CompUnitPasses(FileSpec &fileSpec) {
+ return m_cu_spec_list.FindFileIndex(0, fileSpec, false) != UINT32_MAX;
+}
+
+bool SearchFilterByModuleListAndCU::CompUnitPasses(CompileUnit &compUnit) {
+ bool in_cu_list =
+ m_cu_spec_list.FindFileIndex(0, compUnit, false) != UINT32_MAX;
+ if (in_cu_list) {
+ ModuleSP module_sp(compUnit.GetModule());
+ if (module_sp) {
+ bool module_passes = SearchFilterByModuleList::ModulePasses(module_sp);
+ return module_passes;
+ } else
+ return true;
+ } else
+ return false;
+}
+
+void SearchFilterByModuleListAndCU::Search(Searcher &searcher) {
+ if (!m_target_sp)
+ return;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthTarget) {
+ SymbolContext empty_sc;
+ empty_sc.target_sp = m_target_sp;
+ searcher.SearchCallback(*this, empty_sc, nullptr, false);
+ }
+
+ // If the module file spec is a full path, then we can just find the one
+ // filespec that passes. Otherwise, we need to go through all modules and
+ // find the ones that match the file name.
+
+ ModuleList matching_modules;
+ const ModuleList &target_images = m_target_sp->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_images.GetMutex());
+
+ const size_t num_modules = target_images.GetSize();
+ bool no_modules_in_filter = m_module_spec_list.GetSize() == 0;
+ for (size_t i = 0; i < num_modules; i++) {
+ lldb::ModuleSP module_sp = target_images.GetModuleAtIndexUnlocked(i);
+ if (no_modules_in_filter ||
+ m_module_spec_list.FindFileIndex(0, module_sp->GetFileSpec(), false) !=
+ UINT32_MAX) {
+ SymbolContext matchingContext(m_target_sp, module_sp);
+ Searcher::CallbackReturn shouldContinue;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthModule) {
+ shouldContinue = DoModuleIteration(matchingContext, searcher);
+ if (shouldContinue == Searcher::eCallbackReturnStop)
+ return;
+ } else {
+ const size_t num_cu = module_sp->GetNumCompileUnits();
+ for (size_t cu_idx = 0; cu_idx < num_cu; cu_idx++) {
+ CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(cu_idx);
+ matchingContext.comp_unit = cu_sp.get();
+ if (matchingContext.comp_unit) {
+ if (m_cu_spec_list.FindFileIndex(0, *matchingContext.comp_unit,
+ false) != UINT32_MAX) {
+ shouldContinue =
+ DoCUIteration(module_sp, matchingContext, searcher);
+ if (shouldContinue == Searcher::eCallbackReturnStop)
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void SearchFilterByModuleListAndCU::GetDescription(Stream *s) {
+ size_t num_modules = m_module_spec_list.GetSize();
+ if (num_modules == 1) {
+ s->Printf(", module = ");
+ s->PutCString(
+ m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString(
+ "<Unknown>"));
+ } else if (num_modules > 0) {
+ s->Printf(", modules(%" PRIu64 ") = ", static_cast<uint64_t>(num_modules));
+ for (size_t i = 0; i < num_modules; i++) {
+ s->PutCString(
+ m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString(
+ "<Unknown>"));
+ if (i != num_modules - 1)
+ s->PutCString(", ");
+ }
+ }
+}
+
+uint32_t SearchFilterByModuleListAndCU::GetFilterRequiredItems() {
+ return eSymbolContextModule | eSymbolContextCompUnit;
+}
+
+void SearchFilterByModuleListAndCU::Dump(Stream *s) const {}
+
+lldb::SearchFilterSP
+SearchFilterByModuleListAndCU::DoCopyForBreakpoint(Breakpoint &breakpoint) {
+ return std::make_shared<SearchFilterByModuleListAndCU>(*this);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Section.cpp b/contrib/llvm-project/lldb/source/Core/Section.cpp
new file mode 100644
index 000000000000..f30ddd2c18c3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Section.cpp
@@ -0,0 +1,628 @@
+//===-- Section.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Section.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/VMRange.h"
+
+#include <inttypes.h>
+#include <limits>
+#include <utility>
+
+namespace lldb_private {
+class DataExtractor;
+}
+using namespace lldb;
+using namespace lldb_private;
+
+const char *Section::GetTypeAsCString() const {
+ switch (m_type) {
+ case eSectionTypeInvalid:
+ return "invalid";
+ case eSectionTypeCode:
+ return "code";
+ case eSectionTypeContainer:
+ return "container";
+ case eSectionTypeData:
+ return "data";
+ case eSectionTypeDataCString:
+ return "data-cstr";
+ case eSectionTypeDataCStringPointers:
+ return "data-cstr-ptr";
+ case eSectionTypeDataSymbolAddress:
+ return "data-symbol-addr";
+ case eSectionTypeData4:
+ return "data-4-byte";
+ case eSectionTypeData8:
+ return "data-8-byte";
+ case eSectionTypeData16:
+ return "data-16-byte";
+ case eSectionTypeDataPointers:
+ return "data-ptrs";
+ case eSectionTypeDebug:
+ return "debug";
+ case eSectionTypeZeroFill:
+ return "zero-fill";
+ case eSectionTypeDataObjCMessageRefs:
+ return "objc-message-refs";
+ case eSectionTypeDataObjCCFStrings:
+ return "objc-cfstrings";
+ case eSectionTypeDWARFDebugAbbrev:
+ return "dwarf-abbrev";
+ case eSectionTypeDWARFDebugAbbrevDwo:
+ return "dwarf-abbrev-dwo";
+ case eSectionTypeDWARFDebugAddr:
+ return "dwarf-addr";
+ case eSectionTypeDWARFDebugAranges:
+ return "dwarf-aranges";
+ case eSectionTypeDWARFDebugCuIndex:
+ return "dwarf-cu-index";
+ case eSectionTypeDWARFDebugFrame:
+ return "dwarf-frame";
+ case eSectionTypeDWARFDebugInfo:
+ return "dwarf-info";
+ case eSectionTypeDWARFDebugInfoDwo:
+ return "dwarf-info-dwo";
+ case eSectionTypeDWARFDebugLine:
+ return "dwarf-line";
+ case eSectionTypeDWARFDebugLineStr:
+ return "dwarf-line-str";
+ case eSectionTypeDWARFDebugLoc:
+ return "dwarf-loc";
+ case eSectionTypeDWARFDebugLocLists:
+ return "dwarf-loclists";
+ case eSectionTypeDWARFDebugMacInfo:
+ return "dwarf-macinfo";
+ case eSectionTypeDWARFDebugMacro:
+ return "dwarf-macro";
+ case eSectionTypeDWARFDebugPubNames:
+ return "dwarf-pubnames";
+ case eSectionTypeDWARFDebugPubTypes:
+ return "dwarf-pubtypes";
+ case eSectionTypeDWARFDebugRanges:
+ return "dwarf-ranges";
+ case eSectionTypeDWARFDebugRngLists:
+ return "dwarf-rnglists";
+ case eSectionTypeDWARFDebugStr:
+ return "dwarf-str";
+ case eSectionTypeDWARFDebugStrDwo:
+ return "dwarf-str-dwo";
+ case eSectionTypeDWARFDebugStrOffsets:
+ return "dwarf-str-offsets";
+ case eSectionTypeDWARFDebugStrOffsetsDwo:
+ return "dwarf-str-offsets-dwo";
+ case eSectionTypeDWARFDebugTypes:
+ return "dwarf-types";
+ case eSectionTypeDWARFDebugTypesDwo:
+ return "dwarf-types-dwo";
+ case eSectionTypeDWARFDebugNames:
+ return "dwarf-names";
+ case eSectionTypeELFSymbolTable:
+ return "elf-symbol-table";
+ case eSectionTypeELFDynamicSymbols:
+ return "elf-dynamic-symbols";
+ case eSectionTypeELFRelocationEntries:
+ return "elf-relocation-entries";
+ case eSectionTypeELFDynamicLinkInfo:
+ return "elf-dynamic-link-info";
+ case eSectionTypeDWARFAppleNames:
+ return "apple-names";
+ case eSectionTypeDWARFAppleTypes:
+ return "apple-types";
+ case eSectionTypeDWARFAppleNamespaces:
+ return "apple-namespaces";
+ case eSectionTypeDWARFAppleObjC:
+ return "apple-objc";
+ case eSectionTypeEHFrame:
+ return "eh-frame";
+ case eSectionTypeARMexidx:
+ return "ARM.exidx";
+ case eSectionTypeARMextab:
+ return "ARM.extab";
+ case eSectionTypeCompactUnwind:
+ return "compact-unwind";
+ case eSectionTypeGoSymtab:
+ return "go-symtab";
+ case eSectionTypeAbsoluteAddress:
+ return "absolute";
+ case eSectionTypeDWARFGNUDebugAltLink:
+ return "dwarf-gnu-debugaltlink";
+ case eSectionTypeOther:
+ return "regular";
+ }
+ return "unknown";
+}
+
+Section::Section(const ModuleSP &module_sp, ObjectFile *obj_file,
+ user_id_t sect_id, ConstString name,
+ SectionType sect_type, addr_t file_addr, addr_t byte_size,
+ lldb::offset_t file_offset, lldb::offset_t file_size,
+ uint32_t log2align, uint32_t flags,
+ uint32_t target_byte_size /*=1*/)
+ : ModuleChild(module_sp), UserID(sect_id), Flags(flags),
+ m_obj_file(obj_file), m_type(sect_type), m_parent_wp(), m_name(name),
+ m_file_addr(file_addr), m_byte_size(byte_size),
+ m_file_offset(file_offset), m_file_size(file_size),
+ m_log2align(log2align), m_children(), m_fake(false), m_encrypted(false),
+ m_thread_specific(false), m_readable(false), m_writable(false),
+ m_executable(false), m_relocated(false), m_target_byte_size(target_byte_size) {
+ // printf ("Section::Section(%p): module=%p, sect_id = 0x%16.16" PRIx64 ",
+ // addr=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), file [0x%16.16" PRIx64 "
+ // - 0x%16.16" PRIx64 "), flags = 0x%8.8x, name = %s\n",
+ // this, module_sp.get(), sect_id, file_addr, file_addr +
+ // byte_size, file_offset, file_offset + file_size, flags,
+ // name.GetCString());
+}
+
+Section::Section(const lldb::SectionSP &parent_section_sp,
+ const ModuleSP &module_sp, ObjectFile *obj_file,
+ user_id_t sect_id, ConstString name,
+ SectionType sect_type, addr_t file_addr, addr_t byte_size,
+ lldb::offset_t file_offset, lldb::offset_t file_size,
+ uint32_t log2align, uint32_t flags,
+ uint32_t target_byte_size /*=1*/)
+ : ModuleChild(module_sp), UserID(sect_id), Flags(flags),
+ m_obj_file(obj_file), m_type(sect_type), m_parent_wp(), m_name(name),
+ m_file_addr(file_addr), m_byte_size(byte_size),
+ m_file_offset(file_offset), m_file_size(file_size),
+ m_log2align(log2align), m_children(), m_fake(false), m_encrypted(false),
+ m_thread_specific(false), m_readable(false), m_writable(false),
+ m_executable(false), m_relocated(false), m_target_byte_size(target_byte_size) {
+ // printf ("Section::Section(%p): module=%p, sect_id = 0x%16.16" PRIx64 ",
+ // addr=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), file [0x%16.16" PRIx64 "
+ // - 0x%16.16" PRIx64 "), flags = 0x%8.8x, name = %s.%s\n",
+ // this, module_sp.get(), sect_id, file_addr, file_addr +
+ // byte_size, file_offset, file_offset + file_size, flags,
+ // parent_section_sp->GetName().GetCString(), name.GetCString());
+ if (parent_section_sp)
+ m_parent_wp = parent_section_sp;
+}
+
+Section::~Section() {
+ // printf ("Section::~Section(%p)\n", this);
+}
+
+addr_t Section::GetFileAddress() const {
+ SectionSP parent_sp(GetParent());
+ if (parent_sp) {
+ // This section has a parent which means m_file_addr is an offset into the
+ // parent section, so the file address for this section is the file address
+ // of the parent plus the offset
+ return parent_sp->GetFileAddress() + m_file_addr;
+ }
+ // This section has no parent, so m_file_addr is the file base address
+ return m_file_addr;
+}
+
+bool Section::SetFileAddress(lldb::addr_t file_addr) {
+ SectionSP parent_sp(GetParent());
+ if (parent_sp) {
+ if (m_file_addr >= file_addr)
+ return parent_sp->SetFileAddress(m_file_addr - file_addr);
+ return false;
+ } else {
+ // This section has no parent, so m_file_addr is the file base address
+ m_file_addr = file_addr;
+ return true;
+ }
+}
+
+lldb::addr_t Section::GetOffset() const {
+ // This section has a parent which means m_file_addr is an offset.
+ SectionSP parent_sp(GetParent());
+ if (parent_sp)
+ return m_file_addr;
+
+ // This section has no parent, so there is no offset to be had
+ return 0;
+}
+
+addr_t Section::GetLoadBaseAddress(Target *target) const {
+ addr_t load_base_addr = LLDB_INVALID_ADDRESS;
+ SectionSP parent_sp(GetParent());
+ if (parent_sp) {
+ load_base_addr = parent_sp->GetLoadBaseAddress(target);
+ if (load_base_addr != LLDB_INVALID_ADDRESS)
+ load_base_addr += GetOffset();
+ }
+ if (load_base_addr == LLDB_INVALID_ADDRESS) {
+ load_base_addr = target->GetSectionLoadList().GetSectionLoadAddress(
+ const_cast<Section *>(this)->shared_from_this());
+ }
+ return load_base_addr;
+}
+
+bool Section::ResolveContainedAddress(addr_t offset, Address &so_addr,
+ bool allow_section_end) const {
+ const size_t num_children = m_children.GetSize();
+ for (size_t i = 0; i < num_children; i++) {
+ Section *child_section = m_children.GetSectionAtIndex(i).get();
+
+ addr_t child_offset = child_section->GetOffset();
+ if (child_offset <= offset &&
+ offset - child_offset <
+ child_section->GetByteSize() + (allow_section_end ? 1 : 0))
+ return child_section->ResolveContainedAddress(offset - child_offset,
+ so_addr, allow_section_end);
+ }
+ so_addr.SetOffset(offset);
+ so_addr.SetSection(const_cast<Section *>(this)->shared_from_this());
+
+#ifdef LLDB_CONFIGURATION_DEBUG
+ // For debug builds, ensure that there are no orphaned (i.e., moduleless)
+ // sections.
+ assert(GetModule().get());
+#endif
+ return true;
+}
+
+bool Section::ContainsFileAddress(addr_t vm_addr) const {
+ const addr_t file_addr = GetFileAddress();
+ if (file_addr != LLDB_INVALID_ADDRESS) {
+ if (file_addr <= vm_addr) {
+ const addr_t offset = (vm_addr - file_addr) * m_target_byte_size;
+ return offset < GetByteSize();
+ }
+ }
+ return false;
+}
+
+int Section::Compare(const Section &a, const Section &b) {
+ if (&a == &b)
+ return 0;
+
+ const ModuleSP a_module_sp = a.GetModule();
+ const ModuleSP b_module_sp = b.GetModule();
+ if (a_module_sp == b_module_sp) {
+ user_id_t a_sect_uid = a.GetID();
+ user_id_t b_sect_uid = b.GetID();
+ if (a_sect_uid < b_sect_uid)
+ return -1;
+ if (a_sect_uid > b_sect_uid)
+ return 1;
+ return 0;
+ } else {
+ // The modules are different, just compare the module pointers
+ if (a_module_sp.get() < b_module_sp.get())
+ return -1;
+ else
+ return 1; // We already know the modules aren't equal
+ }
+}
+
+void Section::Dump(Stream *s, Target *target, uint32_t depth) const {
+ // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this);
+ s->Indent();
+ s->Printf("0x%8.8" PRIx64 " %-16s ", GetID(), GetTypeAsCString());
+ bool resolved = true;
+ addr_t addr = LLDB_INVALID_ADDRESS;
+
+ if (GetByteSize() == 0)
+ s->Printf("%39s", "");
+ else {
+ if (target)
+ addr = GetLoadBaseAddress(target);
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ if (target)
+ resolved = false;
+ addr = GetFileAddress();
+ }
+
+ VMRange range(addr, addr + m_byte_size);
+ range.Dump(s, 0);
+ }
+
+ s->Printf("%c %c%c%c 0x%8.8" PRIx64 " 0x%8.8" PRIx64 " 0x%8.8x ",
+ resolved ? ' ' : '*', m_readable ? 'r' : '-',
+ m_writable ? 'w' : '-', m_executable ? 'x' : '-', m_file_offset,
+ m_file_size, Get());
+
+ DumpName(s);
+
+ s->EOL();
+
+ if (depth > 0)
+ m_children.Dump(s, target, false, depth - 1);
+}
+
+void Section::DumpName(Stream *s) const {
+ SectionSP parent_sp(GetParent());
+ if (parent_sp) {
+ parent_sp->DumpName(s);
+ s->PutChar('.');
+ } else {
+ // The top most section prints the module basename
+ const char *name = nullptr;
+ ModuleSP module_sp(GetModule());
+
+ if (m_obj_file) {
+ const FileSpec &file_spec = m_obj_file->GetFileSpec();
+ name = file_spec.GetFilename().AsCString();
+ }
+ if ((!name || !name[0]) && module_sp)
+ name = module_sp->GetFileSpec().GetFilename().AsCString();
+ if (name && name[0])
+ s->Printf("%s.", name);
+ }
+ m_name.Dump(s);
+}
+
+bool Section::IsDescendant(const Section *section) {
+ if (this == section)
+ return true;
+ SectionSP parent_sp(GetParent());
+ if (parent_sp)
+ return parent_sp->IsDescendant(section);
+ return false;
+}
+
+bool Section::Slide(addr_t slide_amount, bool slide_children) {
+ if (m_file_addr != LLDB_INVALID_ADDRESS) {
+ if (slide_amount == 0)
+ return true;
+
+ m_file_addr += slide_amount;
+
+ if (slide_children)
+ m_children.Slide(slide_amount, slide_children);
+
+ return true;
+ }
+ return false;
+}
+
+/// Get the permissions as OR'ed bits from lldb::Permissions
+uint32_t Section::GetPermissions() const {
+ uint32_t permissions = 0;
+ if (m_readable)
+ permissions |= ePermissionsReadable;
+ if (m_writable)
+ permissions |= ePermissionsWritable;
+ if (m_executable)
+ permissions |= ePermissionsExecutable;
+ return permissions;
+}
+
+/// Set the permissions using bits OR'ed from lldb::Permissions
+void Section::SetPermissions(uint32_t permissions) {
+ m_readable = (permissions & ePermissionsReadable) != 0;
+ m_writable = (permissions & ePermissionsWritable) != 0;
+ m_executable = (permissions & ePermissionsExecutable) != 0;
+}
+
+lldb::offset_t Section::GetSectionData(void *dst, lldb::offset_t dst_len,
+ lldb::offset_t offset) {
+ if (m_obj_file)
+ return m_obj_file->ReadSectionData(this, offset, dst, dst_len);
+ return 0;
+}
+
+lldb::offset_t Section::GetSectionData(DataExtractor &section_data) {
+ if (m_obj_file)
+ return m_obj_file->ReadSectionData(this, section_data);
+ return 0;
+}
+
+#pragma mark SectionList
+
+SectionList::SectionList() : m_sections() {}
+
+SectionList::~SectionList() {}
+
+SectionList &SectionList::operator=(const SectionList &rhs) {
+ if (this != &rhs)
+ m_sections = rhs.m_sections;
+ return *this;
+}
+
+size_t SectionList::AddSection(const lldb::SectionSP &section_sp) {
+ if (section_sp) {
+ size_t section_index = m_sections.size();
+ m_sections.push_back(section_sp);
+ return section_index;
+ }
+
+ return std::numeric_limits<size_t>::max();
+}
+
+// Warning, this can be slow as it's removing items from a std::vector.
+bool SectionList::DeleteSection(size_t idx) {
+ if (idx < m_sections.size()) {
+ m_sections.erase(m_sections.begin() + idx);
+ return true;
+ }
+ return false;
+}
+
+size_t SectionList::FindSectionIndex(const Section *sect) {
+ iterator sect_iter;
+ iterator begin = m_sections.begin();
+ iterator end = m_sections.end();
+ for (sect_iter = begin; sect_iter != end; ++sect_iter) {
+ if (sect_iter->get() == sect) {
+ // The secton was already in this section list
+ return std::distance(begin, sect_iter);
+ }
+ }
+ return UINT32_MAX;
+}
+
+size_t SectionList::AddUniqueSection(const lldb::SectionSP &sect_sp) {
+ size_t sect_idx = FindSectionIndex(sect_sp.get());
+ if (sect_idx == UINT32_MAX) {
+ sect_idx = AddSection(sect_sp);
+ }
+ return sect_idx;
+}
+
+bool SectionList::ReplaceSection(user_id_t sect_id,
+ const lldb::SectionSP &sect_sp,
+ uint32_t depth) {
+ iterator sect_iter, end = m_sections.end();
+ for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) {
+ if ((*sect_iter)->GetID() == sect_id) {
+ *sect_iter = sect_sp;
+ return true;
+ } else if (depth > 0) {
+ if ((*sect_iter)
+ ->GetChildren()
+ .ReplaceSection(sect_id, sect_sp, depth - 1))
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t SectionList::GetNumSections(uint32_t depth) const {
+ size_t count = m_sections.size();
+ if (depth > 0) {
+ const_iterator sect_iter, end = m_sections.end();
+ for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) {
+ count += (*sect_iter)->GetChildren().GetNumSections(depth - 1);
+ }
+ }
+ return count;
+}
+
+SectionSP SectionList::GetSectionAtIndex(size_t idx) const {
+ SectionSP sect_sp;
+ if (idx < m_sections.size())
+ sect_sp = m_sections[idx];
+ return sect_sp;
+}
+
+SectionSP
+SectionList::FindSectionByName(ConstString section_dstr) const {
+ SectionSP sect_sp;
+ // Check if we have a valid section string
+ if (section_dstr && !m_sections.empty()) {
+ const_iterator sect_iter;
+ const_iterator end = m_sections.end();
+ for (sect_iter = m_sections.begin();
+ sect_iter != end && sect_sp.get() == nullptr; ++sect_iter) {
+ Section *child_section = sect_iter->get();
+ if (child_section) {
+ if (child_section->GetName() == section_dstr) {
+ sect_sp = *sect_iter;
+ } else {
+ sect_sp =
+ child_section->GetChildren().FindSectionByName(section_dstr);
+ }
+ }
+ }
+ }
+ return sect_sp;
+}
+
+SectionSP SectionList::FindSectionByID(user_id_t sect_id) const {
+ SectionSP sect_sp;
+ if (sect_id) {
+ const_iterator sect_iter;
+ const_iterator end = m_sections.end();
+ for (sect_iter = m_sections.begin();
+ sect_iter != end && sect_sp.get() == nullptr; ++sect_iter) {
+ if ((*sect_iter)->GetID() == sect_id) {
+ sect_sp = *sect_iter;
+ break;
+ } else {
+ sect_sp = (*sect_iter)->GetChildren().FindSectionByID(sect_id);
+ }
+ }
+ }
+ return sect_sp;
+}
+
+SectionSP SectionList::FindSectionByType(SectionType sect_type,
+ bool check_children,
+ size_t start_idx) const {
+ SectionSP sect_sp;
+ size_t num_sections = m_sections.size();
+ for (size_t idx = start_idx; idx < num_sections; ++idx) {
+ if (m_sections[idx]->GetType() == sect_type) {
+ sect_sp = m_sections[idx];
+ break;
+ } else if (check_children) {
+ sect_sp = m_sections[idx]->GetChildren().FindSectionByType(
+ sect_type, check_children, 0);
+ if (sect_sp)
+ break;
+ }
+ }
+ return sect_sp;
+}
+
+SectionSP SectionList::FindSectionContainingFileAddress(addr_t vm_addr,
+ uint32_t depth) const {
+ SectionSP sect_sp;
+ const_iterator sect_iter;
+ const_iterator end = m_sections.end();
+ for (sect_iter = m_sections.begin();
+ sect_iter != end && sect_sp.get() == nullptr; ++sect_iter) {
+ Section *sect = sect_iter->get();
+ if (sect->ContainsFileAddress(vm_addr)) {
+ // The file address is in this section. We need to make sure one of our
+ // child sections doesn't contain this address as well as obeying the
+ // depth limit that was passed in.
+ if (depth > 0)
+ sect_sp = sect->GetChildren().FindSectionContainingFileAddress(
+ vm_addr, depth - 1);
+
+ if (sect_sp.get() == nullptr && !sect->IsFake())
+ sect_sp = *sect_iter;
+ }
+ }
+ return sect_sp;
+}
+
+bool SectionList::ContainsSection(user_id_t sect_id) const {
+ return FindSectionByID(sect_id).get() != nullptr;
+}
+
+void SectionList::Dump(Stream *s, Target *target, bool show_header,
+ uint32_t depth) const {
+ bool target_has_loaded_sections =
+ target && !target->GetSectionLoadList().IsEmpty();
+ if (show_header && !m_sections.empty()) {
+ s->Indent();
+ s->Printf("SectID Type %s Address "
+ " Perm File Off. File Size Flags "
+ " Section Name\n",
+ target_has_loaded_sections ? "Load" : "File");
+ s->Indent();
+ s->PutCString("---------- ---------------- "
+ "--------------------------------------- ---- ---------- "
+ "---------- "
+ "---------- ----------------------------\n");
+ }
+
+ const_iterator sect_iter;
+ const_iterator end = m_sections.end();
+ for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) {
+ (*sect_iter)->Dump(s, target_has_loaded_sections ? target : nullptr, depth);
+ }
+
+ if (show_header && !m_sections.empty())
+ s->IndentLess();
+}
+
+size_t SectionList::Slide(addr_t slide_amount, bool slide_children) {
+ size_t count = 0;
+ const_iterator pos, end = m_sections.end();
+ for (pos = m_sections.begin(); pos != end; ++pos) {
+ if ((*pos)->Slide(slide_amount, slide_children))
+ ++count;
+ }
+ return count;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/SourceManager.cpp b/contrib/llvm-project/lldb/source/Core/SourceManager.cpp
new file mode 100644
index 000000000000..87065ab62425
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/SourceManager.cpp
@@ -0,0 +1,701 @@
+//===-- SourceManager.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/SourceManager.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FormatEntity.h"
+#include "lldb/Core/Highlighter.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/PathMappingList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferLLVM.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "llvm/ADT/Twine.h"
+
+#include <memory>
+#include <utility>
+
+#include <assert.h>
+#include <stdio.h>
+
+namespace lldb_private {
+class ExecutionContext;
+}
+namespace lldb_private {
+class ValueObject;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+static inline bool is_newline_char(char ch) { return ch == '\n' || ch == '\r'; }
+
+// SourceManager constructor
+SourceManager::SourceManager(const TargetSP &target_sp)
+ : m_last_file_sp(), m_last_line(0), m_last_count(0), m_default_set(false),
+ m_target_wp(target_sp),
+ m_debugger_wp(target_sp->GetDebugger().shared_from_this()) {}
+
+SourceManager::SourceManager(const DebuggerSP &debugger_sp)
+ : m_last_file_sp(), m_last_line(0), m_last_count(0), m_default_set(false),
+ m_target_wp(), m_debugger_wp(debugger_sp) {}
+
+// Destructor
+SourceManager::~SourceManager() {}
+
+SourceManager::FileSP SourceManager::GetFile(const FileSpec &file_spec) {
+ bool same_as_previous =
+ m_last_file_sp && m_last_file_sp->FileSpecMatches(file_spec);
+
+ DebuggerSP debugger_sp(m_debugger_wp.lock());
+ FileSP file_sp;
+ if (same_as_previous)
+ file_sp = m_last_file_sp;
+ else if (debugger_sp)
+ file_sp = debugger_sp->GetSourceFileCache().FindSourceFile(file_spec);
+
+ TargetSP target_sp(m_target_wp.lock());
+
+ // It the target source path map has been updated, get this file again so we
+ // can successfully remap the source file
+ if (target_sp && file_sp &&
+ file_sp->GetSourceMapModificationID() !=
+ target_sp->GetSourcePathMap().GetModificationID())
+ file_sp.reset();
+
+ // Update the file contents if needed if we found a file
+ if (file_sp)
+ file_sp->UpdateIfNeeded();
+
+ // If file_sp is no good or it points to a non-existent file, reset it.
+ if (!file_sp || !FileSystem::Instance().Exists(file_sp->GetFileSpec())) {
+ if (target_sp)
+ file_sp = std::make_shared<File>(file_spec, target_sp.get());
+ else
+ file_sp = std::make_shared<File>(file_spec, debugger_sp);
+
+ if (debugger_sp)
+ debugger_sp->GetSourceFileCache().AddSourceFile(file_sp);
+ }
+ return file_sp;
+}
+
+static bool should_highlight_source(DebuggerSP debugger_sp) {
+ if (!debugger_sp)
+ return false;
+
+ // We don't use ANSI stop column formatting if the debugger doesn't think it
+ // should be using color.
+ if (!debugger_sp->GetUseColor())
+ return false;
+
+ return debugger_sp->GetHighlightSource();
+}
+
+static bool should_show_stop_column_with_ansi(DebuggerSP debugger_sp) {
+ // We don't use ANSI stop column formatting if we can't lookup values from
+ // the debugger.
+ if (!debugger_sp)
+ return false;
+
+ // We don't use ANSI stop column formatting if the debugger doesn't think it
+ // should be using color.
+ if (!debugger_sp->GetUseColor())
+ return false;
+
+ // We only use ANSI stop column formatting if we're either supposed to show
+ // ANSI where available (which we know we have when we get to this point), or
+ // if we're only supposed to use ANSI.
+ const auto value = debugger_sp->GetStopShowColumn();
+ return ((value == eStopShowColumnAnsiOrCaret) ||
+ (value == eStopShowColumnAnsi));
+}
+
+static bool should_show_stop_column_with_caret(DebuggerSP debugger_sp) {
+ // We don't use text-based stop column formatting if we can't lookup values
+ // from the debugger.
+ if (!debugger_sp)
+ return false;
+
+ // If we're asked to show the first available of ANSI or caret, then we do
+ // show the caret when ANSI is not available.
+ const auto value = debugger_sp->GetStopShowColumn();
+ if ((value == eStopShowColumnAnsiOrCaret) && !debugger_sp->GetUseColor())
+ return true;
+
+ // The only other time we use caret is if we're explicitly asked to show
+ // caret.
+ return value == eStopShowColumnCaret;
+}
+
+size_t SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile(
+ uint32_t start_line, uint32_t count, uint32_t curr_line, uint32_t column,
+ const char *current_line_cstr, Stream *s,
+ const SymbolContextList *bp_locs) {
+ if (count == 0)
+ return 0;
+
+ Stream::ByteDelta delta(*s);
+
+ if (start_line == 0) {
+ if (m_last_line != 0 && m_last_line != UINT32_MAX)
+ start_line = m_last_line + m_last_count;
+ else
+ start_line = 1;
+ }
+
+ if (!m_default_set) {
+ FileSpec tmp_spec;
+ uint32_t tmp_line;
+ GetDefaultFileAndLine(tmp_spec, tmp_line);
+ }
+
+ m_last_line = start_line;
+ m_last_count = count;
+
+ if (m_last_file_sp.get()) {
+ const uint32_t end_line = start_line + count - 1;
+ for (uint32_t line = start_line; line <= end_line; ++line) {
+ if (!m_last_file_sp->LineIsValid(line)) {
+ m_last_line = UINT32_MAX;
+ break;
+ }
+
+ char prefix[32] = "";
+ if (bp_locs) {
+ uint32_t bp_count = bp_locs->NumLineEntriesWithLine(line);
+
+ if (bp_count > 0)
+ ::snprintf(prefix, sizeof(prefix), "[%u] ", bp_count);
+ else
+ ::snprintf(prefix, sizeof(prefix), " ");
+ }
+
+ s->Printf("%s%2.2s %-4u\t", prefix,
+ line == curr_line ? current_line_cstr : "", line);
+
+ // So far we treated column 0 as a special 'no column value', but
+ // DisplaySourceLines starts counting columns from 0 (and no column is
+ // expressed by passing an empty optional).
+ llvm::Optional<size_t> columnToHighlight;
+ if (line == curr_line && column)
+ columnToHighlight = column - 1;
+
+ size_t this_line_size =
+ m_last_file_sp->DisplaySourceLines(line, columnToHighlight, 0, 0, s);
+ if (column != 0 && line == curr_line &&
+ should_show_stop_column_with_caret(m_debugger_wp.lock())) {
+ // Display caret cursor.
+ std::string src_line;
+ m_last_file_sp->GetLine(line, src_line);
+ s->Printf(" \t");
+ // Insert a space for every non-tab character in the source line.
+ for (size_t i = 0; i + 1 < column && i < src_line.length(); ++i)
+ s->PutChar(src_line[i] == '\t' ? '\t' : ' ');
+ // Now add the caret.
+ s->Printf("^\n");
+ }
+ if (this_line_size == 0) {
+ m_last_line = UINT32_MAX;
+ break;
+ }
+ }
+ }
+ return *delta;
+}
+
+size_t SourceManager::DisplaySourceLinesWithLineNumbers(
+ const FileSpec &file_spec, uint32_t line, uint32_t column,
+ uint32_t context_before, uint32_t context_after,
+ const char *current_line_cstr, Stream *s,
+ const SymbolContextList *bp_locs) {
+ FileSP file_sp(GetFile(file_spec));
+
+ uint32_t start_line;
+ uint32_t count = context_before + context_after + 1;
+ if (line > context_before)
+ start_line = line - context_before;
+ else
+ start_line = 1;
+
+ if (m_last_file_sp.get() != file_sp.get()) {
+ if (line == 0)
+ m_last_line = 0;
+ m_last_file_sp = file_sp;
+ }
+ return DisplaySourceLinesWithLineNumbersUsingLastFile(
+ start_line, count, line, column, current_line_cstr, s, bp_locs);
+}
+
+size_t SourceManager::DisplayMoreWithLineNumbers(
+ Stream *s, uint32_t count, bool reverse, const SymbolContextList *bp_locs) {
+ // If we get called before anybody has set a default file and line, then try
+ // to figure it out here.
+ const bool have_default_file_line = m_last_file_sp && m_last_line > 0;
+ if (!m_default_set) {
+ FileSpec tmp_spec;
+ uint32_t tmp_line;
+ GetDefaultFileAndLine(tmp_spec, tmp_line);
+ }
+
+ if (m_last_file_sp) {
+ if (m_last_line == UINT32_MAX)
+ return 0;
+
+ if (reverse && m_last_line == 1)
+ return 0;
+
+ if (count > 0)
+ m_last_count = count;
+ else if (m_last_count == 0)
+ m_last_count = 10;
+
+ if (m_last_line > 0) {
+ if (reverse) {
+ // If this is the first time we've done a reverse, then back up one
+ // more time so we end up showing the chunk before the last one we've
+ // shown:
+ if (m_last_line > m_last_count)
+ m_last_line -= m_last_count;
+ else
+ m_last_line = 1;
+ } else if (have_default_file_line)
+ m_last_line += m_last_count;
+ } else
+ m_last_line = 1;
+
+ const uint32_t column = 0;
+ return DisplaySourceLinesWithLineNumbersUsingLastFile(
+ m_last_line, m_last_count, UINT32_MAX, column, "", s, bp_locs);
+ }
+ return 0;
+}
+
+bool SourceManager::SetDefaultFileAndLine(const FileSpec &file_spec,
+ uint32_t line) {
+ FileSP old_file_sp = m_last_file_sp;
+ m_last_file_sp = GetFile(file_spec);
+
+ m_default_set = true;
+ if (m_last_file_sp) {
+ m_last_line = line;
+ return true;
+ } else {
+ m_last_file_sp = old_file_sp;
+ return false;
+ }
+}
+
+bool SourceManager::GetDefaultFileAndLine(FileSpec &file_spec, uint32_t &line) {
+ if (m_last_file_sp) {
+ file_spec = m_last_file_sp->GetFileSpec();
+ line = m_last_line;
+ return true;
+ } else if (!m_default_set) {
+ TargetSP target_sp(m_target_wp.lock());
+
+ if (target_sp) {
+ // If nobody has set the default file and line then try here. If there's
+ // no executable, then we will try again later when there is one.
+ // Otherwise, if we can't find it we won't look again, somebody will have
+ // to set it (for instance when we stop somewhere...)
+ Module *executable_ptr = target_sp->GetExecutableModulePointer();
+ if (executable_ptr) {
+ SymbolContextList sc_list;
+ ConstString main_name("main");
+ bool symbols_okay = false; // Force it to be a debug symbol.
+ bool inlines_okay = true;
+ bool append = false;
+ size_t num_matches = executable_ptr->FindFunctions(
+ main_name, nullptr, lldb::eFunctionNameTypeBase, inlines_okay,
+ symbols_okay, append, sc_list);
+ for (size_t idx = 0; idx < num_matches; idx++) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(idx, sc);
+ if (sc.function) {
+ lldb_private::LineEntry line_entry;
+ if (sc.function->GetAddressRange()
+ .GetBaseAddress()
+ .CalculateSymbolContextLineEntry(line_entry)) {
+ SetDefaultFileAndLine(line_entry.file, line_entry.line);
+ file_spec = m_last_file_sp->GetFileSpec();
+ line = m_last_line;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void SourceManager::FindLinesMatchingRegex(FileSpec &file_spec,
+ RegularExpression &regex,
+ uint32_t start_line,
+ uint32_t end_line,
+ std::vector<uint32_t> &match_lines) {
+ match_lines.clear();
+ FileSP file_sp = GetFile(file_spec);
+ if (!file_sp)
+ return;
+ return file_sp->FindLinesMatchingRegex(regex, start_line, end_line,
+ match_lines);
+}
+
+SourceManager::File::File(const FileSpec &file_spec,
+ lldb::DebuggerSP debugger_sp)
+ : m_file_spec_orig(file_spec), m_file_spec(file_spec),
+ m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)),
+ m_debugger_wp(debugger_sp) {
+ CommonInitializer(file_spec, nullptr);
+}
+
+SourceManager::File::File(const FileSpec &file_spec, Target *target)
+ : m_file_spec_orig(file_spec), m_file_spec(file_spec),
+ m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)),
+ m_debugger_wp(target ? target->GetDebugger().shared_from_this()
+ : DebuggerSP()) {
+ CommonInitializer(file_spec, target);
+}
+
+void SourceManager::File::CommonInitializer(const FileSpec &file_spec,
+ Target *target) {
+ if (m_mod_time == llvm::sys::TimePoint<>()) {
+ if (target) {
+ m_source_map_mod_id = target->GetSourcePathMap().GetModificationID();
+
+ if (!file_spec.GetDirectory() && file_spec.GetFilename()) {
+ // If this is just a file name, lets see if we can find it in the
+ // target:
+ bool check_inlines = false;
+ SymbolContextList sc_list;
+ size_t num_matches =
+ target->GetImages().ResolveSymbolContextForFilePath(
+ file_spec.GetFilename().AsCString(), 0, check_inlines,
+ SymbolContextItem(eSymbolContextModule |
+ eSymbolContextCompUnit),
+ sc_list);
+ bool got_multiple = false;
+ if (num_matches != 0) {
+ if (num_matches > 1) {
+ SymbolContext sc;
+ FileSpec *test_cu_spec = nullptr;
+
+ for (unsigned i = 0; i < num_matches; i++) {
+ sc_list.GetContextAtIndex(i, sc);
+ if (sc.comp_unit) {
+ if (test_cu_spec) {
+ if (test_cu_spec != static_cast<FileSpec *>(sc.comp_unit))
+ got_multiple = true;
+ break;
+ } else
+ test_cu_spec = sc.comp_unit;
+ }
+ }
+ }
+ if (!got_multiple) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(0, sc);
+ m_file_spec = sc.comp_unit;
+ m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
+ }
+ }
+ }
+ // Try remapping if m_file_spec does not correspond to an existing file.
+ if (!FileSystem::Instance().Exists(m_file_spec)) {
+ FileSpec new_file_spec;
+ // Check target specific source remappings first, then fall back to
+ // modules objects can have individual path remappings that were
+ // detected when the debug info for a module was found. then
+ if (target->GetSourcePathMap().FindFile(m_file_spec, new_file_spec) ||
+ target->GetImages().FindSourceFile(m_file_spec, new_file_spec)) {
+ m_file_spec = new_file_spec;
+ m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
+ }
+ }
+ }
+ }
+
+ if (m_mod_time != llvm::sys::TimePoint<>())
+ m_data_sp = FileSystem::Instance().CreateDataBuffer(m_file_spec);
+}
+
+uint32_t SourceManager::File::GetLineOffset(uint32_t line) {
+ if (line == 0)
+ return UINT32_MAX;
+
+ if (line == 1)
+ return 0;
+
+ if (CalculateLineOffsets(line)) {
+ if (line < m_offsets.size())
+ return m_offsets[line - 1]; // yes we want "line - 1" in the index
+ }
+ return UINT32_MAX;
+}
+
+uint32_t SourceManager::File::GetNumLines() {
+ CalculateLineOffsets();
+ return m_offsets.size();
+}
+
+const char *SourceManager::File::PeekLineData(uint32_t line) {
+ if (!LineIsValid(line))
+ return nullptr;
+
+ size_t line_offset = GetLineOffset(line);
+ if (line_offset < m_data_sp->GetByteSize())
+ return (const char *)m_data_sp->GetBytes() + line_offset;
+ return nullptr;
+}
+
+uint32_t SourceManager::File::GetLineLength(uint32_t line,
+ bool include_newline_chars) {
+ if (!LineIsValid(line))
+ return false;
+
+ size_t start_offset = GetLineOffset(line);
+ size_t end_offset = GetLineOffset(line + 1);
+ if (end_offset == UINT32_MAX)
+ end_offset = m_data_sp->GetByteSize();
+
+ if (end_offset > start_offset) {
+ uint32_t length = end_offset - start_offset;
+ if (!include_newline_chars) {
+ const char *line_start =
+ (const char *)m_data_sp->GetBytes() + start_offset;
+ while (length > 0) {
+ const char last_char = line_start[length - 1];
+ if ((last_char == '\r') || (last_char == '\n'))
+ --length;
+ else
+ break;
+ }
+ }
+ return length;
+ }
+ return 0;
+}
+
+bool SourceManager::File::LineIsValid(uint32_t line) {
+ if (line == 0)
+ return false;
+
+ if (CalculateLineOffsets(line))
+ return line < m_offsets.size();
+ return false;
+}
+
+void SourceManager::File::UpdateIfNeeded() {
+ // TODO: use host API to sign up for file modifications to anything in our
+ // source cache and only update when we determine a file has been updated.
+ // For now we check each time we want to display info for the file.
+ auto curr_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
+
+ if (curr_mod_time != llvm::sys::TimePoint<>() &&
+ m_mod_time != curr_mod_time) {
+ m_mod_time = curr_mod_time;
+ m_data_sp = FileSystem::Instance().CreateDataBuffer(m_file_spec);
+ m_offsets.clear();
+ }
+}
+
+size_t SourceManager::File::DisplaySourceLines(uint32_t line,
+ llvm::Optional<size_t> column,
+ uint32_t context_before,
+ uint32_t context_after,
+ Stream *s) {
+ // Nothing to write if there's no stream.
+ if (!s)
+ return 0;
+
+ // Sanity check m_data_sp before proceeding.
+ if (!m_data_sp)
+ return 0;
+
+ size_t bytes_written = s->GetWrittenBytes();
+
+ auto debugger_sp = m_debugger_wp.lock();
+
+ HighlightStyle style;
+ // Use the default Vim style if source highlighting is enabled.
+ if (should_highlight_source(debugger_sp))
+ style = HighlightStyle::MakeVimStyle();
+
+ // If we should mark the stop column with color codes, then copy the prefix
+ // and suffix to our color style.
+ if (should_show_stop_column_with_ansi(debugger_sp))
+ style.selected.Set(debugger_sp->GetStopShowColumnAnsiPrefix(),
+ debugger_sp->GetStopShowColumnAnsiSuffix());
+
+ HighlighterManager mgr;
+ std::string path = GetFileSpec().GetPath(/*denormalize*/ false);
+ // FIXME: Find a way to get the definitive language this file was written in
+ // and pass it to the highlighter.
+ const auto &h = mgr.getHighlighterFor(lldb::eLanguageTypeUnknown, path);
+
+ const uint32_t start_line =
+ line <= context_before ? 1 : line - context_before;
+ const uint32_t start_line_offset = GetLineOffset(start_line);
+ if (start_line_offset != UINT32_MAX) {
+ const uint32_t end_line = line + context_after;
+ uint32_t end_line_offset = GetLineOffset(end_line + 1);
+ if (end_line_offset == UINT32_MAX)
+ end_line_offset = m_data_sp->GetByteSize();
+
+ assert(start_line_offset <= end_line_offset);
+ if (start_line_offset < end_line_offset) {
+ size_t count = end_line_offset - start_line_offset;
+ const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
+
+ auto ref = llvm::StringRef(reinterpret_cast<const char *>(cstr), count);
+
+ h.Highlight(style, ref, column, "", *s);
+
+ // Ensure we get an end of line character one way or another.
+ if (!is_newline_char(ref.back()))
+ s->EOL();
+ }
+ }
+ return s->GetWrittenBytes() - bytes_written;
+}
+
+void SourceManager::File::FindLinesMatchingRegex(
+ RegularExpression &regex, uint32_t start_line, uint32_t end_line,
+ std::vector<uint32_t> &match_lines) {
+ match_lines.clear();
+
+ if (!LineIsValid(start_line) ||
+ (end_line != UINT32_MAX && !LineIsValid(end_line)))
+ return;
+ if (start_line > end_line)
+ return;
+
+ for (uint32_t line_no = start_line; line_no < end_line; line_no++) {
+ std::string buffer;
+ if (!GetLine(line_no, buffer))
+ break;
+ if (regex.Execute(buffer)) {
+ match_lines.push_back(line_no);
+ }
+ }
+}
+
+bool SourceManager::File::FileSpecMatches(const FileSpec &file_spec) {
+ return FileSpec::Equal(m_file_spec, file_spec, false);
+}
+
+bool lldb_private::operator==(const SourceManager::File &lhs,
+ const SourceManager::File &rhs) {
+ if (lhs.m_file_spec != rhs.m_file_spec)
+ return false;
+ return lhs.m_mod_time == rhs.m_mod_time;
+}
+
+bool SourceManager::File::CalculateLineOffsets(uint32_t line) {
+ line =
+ UINT32_MAX; // TODO: take this line out when we support partial indexing
+ if (line == UINT32_MAX) {
+ // Already done?
+ if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX)
+ return true;
+
+ if (m_offsets.empty()) {
+ if (m_data_sp.get() == nullptr)
+ return false;
+
+ const char *start = (char *)m_data_sp->GetBytes();
+ if (start) {
+ const char *end = start + m_data_sp->GetByteSize();
+
+ // Calculate all line offsets from scratch
+
+ // Push a 1 at index zero to indicate the file has been completely
+ // indexed.
+ m_offsets.push_back(UINT32_MAX);
+ const char *s;
+ for (s = start; s < end; ++s) {
+ char curr_ch = *s;
+ if (is_newline_char(curr_ch)) {
+ if (s + 1 < end) {
+ char next_ch = s[1];
+ if (is_newline_char(next_ch)) {
+ if (curr_ch != next_ch)
+ ++s;
+ }
+ }
+ m_offsets.push_back(s + 1 - start);
+ }
+ }
+ if (!m_offsets.empty()) {
+ if (m_offsets.back() < size_t(end - start))
+ m_offsets.push_back(end - start);
+ }
+ return true;
+ }
+ } else {
+ // Some lines have been populated, start where we last left off
+ assert("Not implemented yet" && false);
+ }
+
+ } else {
+ // Calculate all line offsets up to "line"
+ assert("Not implemented yet" && false);
+ }
+ return false;
+}
+
+bool SourceManager::File::GetLine(uint32_t line_no, std::string &buffer) {
+ if (!LineIsValid(line_no))
+ return false;
+
+ size_t start_offset = GetLineOffset(line_no);
+ size_t end_offset = GetLineOffset(line_no + 1);
+ if (end_offset == UINT32_MAX) {
+ end_offset = m_data_sp->GetByteSize();
+ }
+ buffer.assign((char *)m_data_sp->GetBytes() + start_offset,
+ end_offset - start_offset);
+
+ return true;
+}
+
+void SourceManager::SourceFileCache::AddSourceFile(const FileSP &file_sp) {
+ FileSpec file_spec;
+ FileCache::iterator pos = m_file_cache.find(file_spec);
+ if (pos == m_file_cache.end())
+ m_file_cache[file_spec] = file_sp;
+ else {
+ if (file_sp != pos->second)
+ m_file_cache[file_spec] = file_sp;
+ }
+}
+
+SourceManager::FileSP SourceManager::SourceFileCache::FindSourceFile(
+ const FileSpec &file_spec) const {
+ FileSP file_sp;
+ FileCache::const_iterator pos = m_file_cache.find(file_spec);
+ if (pos != m_file_cache.end())
+ file_sp = pos->second;
+ return file_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/StreamAsynchronousIO.cpp b/contrib/llvm-project/lldb/source/Core/StreamAsynchronousIO.cpp
new file mode 100644
index 000000000000..d283749144e1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/StreamAsynchronousIO.cpp
@@ -0,0 +1,36 @@
+//===-- StreamAsynchronousIO.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/StreamAsynchronousIO.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/lldb-enumerations.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+StreamAsynchronousIO::StreamAsynchronousIO(Debugger &debugger, bool for_stdout)
+ : Stream(0, 4, eByteOrderBig), m_debugger(debugger), m_data(),
+ m_for_stdout(for_stdout) {}
+
+StreamAsynchronousIO::~StreamAsynchronousIO() {
+ // Flush when we destroy to make sure we display the data
+ Flush();
+}
+
+void StreamAsynchronousIO::Flush() {
+ if (!m_data.empty()) {
+ m_debugger.PrintAsync(m_data.data(), m_data.size(), m_for_stdout);
+ m_data = std::string();
+ }
+}
+
+size_t StreamAsynchronousIO::WriteImpl(const void *s, size_t length) {
+ m_data.append((const char *)s, length);
+ return length;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/StreamFile.cpp b/contrib/llvm-project/lldb/source/Core/StreamFile.cpp
new file mode 100644
index 000000000000..ce6e1ea2c18f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/StreamFile.cpp
@@ -0,0 +1,49 @@
+//===-- StreamFile.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/FileSystem.h"
+
+#include <stdio.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// StreamFile constructor
+StreamFile::StreamFile() : Stream(), m_file() {}
+
+StreamFile::StreamFile(uint32_t flags, uint32_t addr_size, ByteOrder byte_order)
+ : Stream(flags, addr_size, byte_order), m_file() {}
+
+StreamFile::StreamFile(int fd, bool transfer_ownership)
+ : Stream(), m_file(fd, transfer_ownership) {}
+
+StreamFile::StreamFile(FILE *fh, bool transfer_ownership)
+ : Stream(), m_file(fh, transfer_ownership) {}
+
+StreamFile::StreamFile(const char *path) : Stream(), m_file() {
+ FileSystem::Instance().Open(m_file, FileSpec(path),
+ File::eOpenOptionWrite |
+ File::eOpenOptionCanCreate |
+ File::eOpenOptionCloseOnExec);
+}
+
+StreamFile::StreamFile(const char *path, uint32_t options, uint32_t permissions)
+ : Stream(), m_file() {
+
+ FileSystem::Instance().Open(m_file, FileSpec(path), options, permissions);
+}
+
+StreamFile::~StreamFile() {}
+
+void StreamFile::Flush() { m_file.Flush(); }
+
+size_t StreamFile::WriteImpl(const void *s, size_t length) {
+ m_file.Write(s, length);
+ return length;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/UserSettingsController.cpp b/contrib/llvm-project/lldb/source/Core/UserSettingsController.cpp
new file mode 100644
index 000000000000..3a656766dcec
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/UserSettingsController.cpp
@@ -0,0 +1,113 @@
+//====-- UserSettingsController.cpp ------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/UserSettingsController.h"
+
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include <memory>
+
+namespace lldb_private {
+class CommandInterpreter;
+}
+namespace lldb_private {
+class ConstString;
+}
+namespace lldb_private {
+class ExecutionContext;
+}
+namespace lldb_private {
+class Property;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+lldb::OptionValueSP
+Properties::GetPropertyValue(const ExecutionContext *exe_ctx,
+ llvm::StringRef path, bool will_modify,
+ Status &error) const {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp)
+ return properties_sp->GetSubValue(exe_ctx, path, will_modify, error);
+ return lldb::OptionValueSP();
+}
+
+Status Properties::SetPropertyValue(const ExecutionContext *exe_ctx,
+ VarSetOperationType op,
+ llvm::StringRef path,
+ llvm::StringRef value) {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp)
+ return properties_sp->SetSubValue(exe_ctx, op, path, value);
+ Status error;
+ error.SetErrorString("no properties");
+ return error;
+}
+
+void Properties::DumpAllPropertyValues(const ExecutionContext *exe_ctx,
+ Stream &strm, uint32_t dump_mask) {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp)
+ return properties_sp->DumpValue(exe_ctx, strm, dump_mask);
+}
+
+void Properties::DumpAllDescriptions(CommandInterpreter &interpreter,
+ Stream &strm) const {
+ strm.PutCString("Top level variables:\n\n");
+
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp)
+ return properties_sp->DumpAllDescriptions(interpreter, strm);
+}
+
+Status Properties::DumpPropertyValue(const ExecutionContext *exe_ctx,
+ Stream &strm,
+ llvm::StringRef property_path,
+ uint32_t dump_mask) {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp) {
+ return properties_sp->DumpPropertyValue(exe_ctx, strm, property_path,
+ dump_mask);
+ }
+ Status error;
+ error.SetErrorString("empty property list");
+ return error;
+}
+
+size_t
+Properties::Apropos(llvm::StringRef keyword,
+ std::vector<const Property *> &matching_properties) const {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp) {
+ properties_sp->Apropos(keyword, matching_properties);
+ }
+ return matching_properties.size();
+}
+
+lldb::OptionValuePropertiesSP
+Properties::GetSubProperty(const ExecutionContext *exe_ctx,
+ ConstString name) {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp)
+ return properties_sp->GetSubProperty(exe_ctx, name);
+ return lldb::OptionValuePropertiesSP();
+}
+
+const char *Properties::GetExperimentalSettingsName() { return "experimental"; }
+
+bool Properties::IsSettingExperimental(llvm::StringRef setting) {
+ if (setting.empty())
+ return false;
+
+ llvm::StringRef experimental = GetExperimentalSettingsName();
+ size_t dot_pos = setting.find_first_of('.');
+ return setting.take_front(dot_pos) == experimental;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Value.cpp b/contrib/llvm-project/lldb/source/Core/Value.cpp
new file mode 100644
index 000000000000..fdb4adb5f431
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Value.cpp
@@ -0,0 +1,708 @@
+//===-- Value.cpp -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Value.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-types.h"
+
+#include <memory>
+#include <string>
+
+#include <inttypes.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+Value::Value()
+ : m_value(), m_vector(), m_compiler_type(), m_context(nullptr),
+ m_value_type(eValueTypeScalar), m_context_type(eContextTypeInvalid),
+ m_data_buffer() {}
+
+Value::Value(const Scalar &scalar)
+ : m_value(scalar), m_vector(), m_compiler_type(), m_context(nullptr),
+ m_value_type(eValueTypeScalar), m_context_type(eContextTypeInvalid),
+ m_data_buffer() {}
+
+Value::Value(const void *bytes, int len)
+ : m_value(), m_vector(), m_compiler_type(), m_context(nullptr),
+ m_value_type(eValueTypeHostAddress), m_context_type(eContextTypeInvalid),
+ m_data_buffer() {
+ SetBytes(bytes, len);
+}
+
+Value::Value(const Value &v)
+ : m_value(v.m_value), m_vector(v.m_vector),
+ m_compiler_type(v.m_compiler_type), m_context(v.m_context),
+ m_value_type(v.m_value_type), m_context_type(v.m_context_type),
+ m_data_buffer() {
+ const uintptr_t rhs_value =
+ (uintptr_t)v.m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ if ((rhs_value != 0) &&
+ (rhs_value == (uintptr_t)v.m_data_buffer.GetBytes())) {
+ m_data_buffer.CopyData(v.m_data_buffer.GetBytes(),
+ v.m_data_buffer.GetByteSize());
+
+ m_value = (uintptr_t)m_data_buffer.GetBytes();
+ }
+}
+
+Value &Value::operator=(const Value &rhs) {
+ if (this != &rhs) {
+ m_value = rhs.m_value;
+ m_vector = rhs.m_vector;
+ m_compiler_type = rhs.m_compiler_type;
+ m_context = rhs.m_context;
+ m_value_type = rhs.m_value_type;
+ m_context_type = rhs.m_context_type;
+ const uintptr_t rhs_value =
+ (uintptr_t)rhs.m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ if ((rhs_value != 0) &&
+ (rhs_value == (uintptr_t)rhs.m_data_buffer.GetBytes())) {
+ m_data_buffer.CopyData(rhs.m_data_buffer.GetBytes(),
+ rhs.m_data_buffer.GetByteSize());
+
+ m_value = (uintptr_t)m_data_buffer.GetBytes();
+ }
+ }
+ return *this;
+}
+
+void Value::SetBytes(const void *bytes, int len) {
+ m_value_type = eValueTypeHostAddress;
+ m_data_buffer.CopyData(bytes, len);
+ m_value = (uintptr_t)m_data_buffer.GetBytes();
+}
+
+void Value::AppendBytes(const void *bytes, int len) {
+ m_value_type = eValueTypeHostAddress;
+ m_data_buffer.AppendData(bytes, len);
+ m_value = (uintptr_t)m_data_buffer.GetBytes();
+}
+
+void Value::Dump(Stream *strm) {
+ m_value.GetValue(strm, true);
+ strm->Printf(", value_type = %s, context = %p, context_type = %s",
+ Value::GetValueTypeAsCString(m_value_type), m_context,
+ Value::GetContextTypeAsCString(m_context_type));
+}
+
+Value::ValueType Value::GetValueType() const { return m_value_type; }
+
+AddressType Value::GetValueAddressType() const {
+ switch (m_value_type) {
+ default:
+ case eValueTypeScalar:
+ break;
+ case eValueTypeLoadAddress:
+ return eAddressTypeLoad;
+ case eValueTypeFileAddress:
+ return eAddressTypeFile;
+ case eValueTypeHostAddress:
+ return eAddressTypeHost;
+ }
+ return eAddressTypeInvalid;
+}
+
+RegisterInfo *Value::GetRegisterInfo() const {
+ if (m_context_type == eContextTypeRegisterInfo)
+ return static_cast<RegisterInfo *>(m_context);
+ return nullptr;
+}
+
+Type *Value::GetType() {
+ if (m_context_type == eContextTypeLLDBType)
+ return static_cast<Type *>(m_context);
+ return nullptr;
+}
+
+size_t Value::AppendDataToHostBuffer(const Value &rhs) {
+ if (this == &rhs)
+ return 0;
+
+ size_t curr_size = m_data_buffer.GetByteSize();
+ Status error;
+ switch (rhs.GetValueType()) {
+ case eValueTypeScalar: {
+ const size_t scalar_size = rhs.m_value.GetByteSize();
+ if (scalar_size > 0) {
+ const size_t new_size = curr_size + scalar_size;
+ if (ResizeData(new_size) == new_size) {
+ rhs.m_value.GetAsMemoryData(m_data_buffer.GetBytes() + curr_size,
+ scalar_size, endian::InlHostByteOrder(),
+ error);
+ return scalar_size;
+ }
+ }
+ } break;
+ case eValueTypeVector: {
+ const size_t vector_size = rhs.m_vector.length;
+ if (vector_size > 0) {
+ const size_t new_size = curr_size + vector_size;
+ if (ResizeData(new_size) == new_size) {
+ ::memcpy(m_data_buffer.GetBytes() + curr_size, rhs.m_vector.bytes,
+ vector_size);
+ return vector_size;
+ }
+ }
+ } break;
+ case eValueTypeFileAddress:
+ case eValueTypeLoadAddress:
+ case eValueTypeHostAddress: {
+ const uint8_t *src = rhs.GetBuffer().GetBytes();
+ const size_t src_len = rhs.GetBuffer().GetByteSize();
+ if (src && src_len > 0) {
+ const size_t new_size = curr_size + src_len;
+ if (ResizeData(new_size) == new_size) {
+ ::memcpy(m_data_buffer.GetBytes() + curr_size, src, src_len);
+ return src_len;
+ }
+ }
+ } break;
+ }
+ return 0;
+}
+
+size_t Value::ResizeData(size_t len) {
+ m_value_type = eValueTypeHostAddress;
+ m_data_buffer.SetByteSize(len);
+ m_value = (uintptr_t)m_data_buffer.GetBytes();
+ return m_data_buffer.GetByteSize();
+}
+
+bool Value::ValueOf(ExecutionContext *exe_ctx) {
+ switch (m_context_type) {
+ case eContextTypeInvalid:
+ case eContextTypeRegisterInfo: // RegisterInfo *
+ case eContextTypeLLDBType: // Type *
+ break;
+
+ case eContextTypeVariable: // Variable *
+ ResolveValue(exe_ctx);
+ return true;
+ }
+ return false;
+}
+
+uint64_t Value::GetValueByteSize(Status *error_ptr, ExecutionContext *exe_ctx) {
+ switch (m_context_type) {
+ case eContextTypeRegisterInfo: // RegisterInfo *
+ if (GetRegisterInfo()) {
+ if (error_ptr)
+ error_ptr->Clear();
+ return GetRegisterInfo()->byte_size;
+ }
+ break;
+
+ case eContextTypeInvalid:
+ case eContextTypeLLDBType: // Type *
+ case eContextTypeVariable: // Variable *
+ {
+ auto *scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
+ if (llvm::Optional<uint64_t> size = GetCompilerType().GetByteSize(scope)) {
+ if (error_ptr)
+ error_ptr->Clear();
+ return *size;
+ }
+ break;
+ }
+ }
+ if (error_ptr && error_ptr->Success())
+ error_ptr->SetErrorString("Unable to determine byte size.");
+ return 0;
+}
+
+const CompilerType &Value::GetCompilerType() {
+ if (!m_compiler_type.IsValid()) {
+ switch (m_context_type) {
+ case eContextTypeInvalid:
+ break;
+
+ case eContextTypeRegisterInfo:
+ break; // TODO: Eventually convert into a compiler type?
+
+ case eContextTypeLLDBType: {
+ Type *lldb_type = GetType();
+ if (lldb_type)
+ m_compiler_type = lldb_type->GetForwardCompilerType();
+ } break;
+
+ case eContextTypeVariable: {
+ Variable *variable = GetVariable();
+ if (variable) {
+ Type *variable_type = variable->GetType();
+ if (variable_type)
+ m_compiler_type = variable_type->GetForwardCompilerType();
+ }
+ } break;
+ }
+ }
+
+ return m_compiler_type;
+}
+
+void Value::SetCompilerType(const CompilerType &compiler_type) {
+ m_compiler_type = compiler_type;
+}
+
+lldb::Format Value::GetValueDefaultFormat() {
+ switch (m_context_type) {
+ case eContextTypeRegisterInfo:
+ if (GetRegisterInfo())
+ return GetRegisterInfo()->format;
+ break;
+
+ case eContextTypeInvalid:
+ case eContextTypeLLDBType:
+ case eContextTypeVariable: {
+ const CompilerType &ast_type = GetCompilerType();
+ if (ast_type.IsValid())
+ return ast_type.GetFormat();
+ } break;
+ }
+
+ // Return a good default in case we can't figure anything out
+ return eFormatHex;
+}
+
+bool Value::GetData(DataExtractor &data) {
+ switch (m_value_type) {
+ default:
+ break;
+
+ case eValueTypeScalar:
+ if (m_value.GetData(data))
+ return true;
+ break;
+
+ case eValueTypeLoadAddress:
+ case eValueTypeFileAddress:
+ case eValueTypeHostAddress:
+ if (m_data_buffer.GetByteSize()) {
+ data.SetData(m_data_buffer.GetBytes(), m_data_buffer.GetByteSize(),
+ data.GetByteOrder());
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data,
+ uint32_t data_offset, Module *module) {
+ data.Clear();
+
+ Status error;
+ lldb::addr_t address = LLDB_INVALID_ADDRESS;
+ AddressType address_type = eAddressTypeFile;
+ Address file_so_addr;
+ const CompilerType &ast_type = GetCompilerType();
+ switch (m_value_type) {
+ case eValueTypeVector:
+ if (ast_type.IsValid())
+ data.SetAddressByteSize(ast_type.GetPointerByteSize());
+ else
+ data.SetAddressByteSize(sizeof(void *));
+ data.SetData(m_vector.bytes, m_vector.length, m_vector.byte_order);
+ break;
+
+ case eValueTypeScalar: {
+ data.SetByteOrder(endian::InlHostByteOrder());
+ if (ast_type.IsValid())
+ data.SetAddressByteSize(ast_type.GetPointerByteSize());
+ else
+ data.SetAddressByteSize(sizeof(void *));
+
+ uint32_t limit_byte_size = UINT32_MAX;
+
+ if (llvm::Optional<uint64_t> size = ast_type.GetByteSize(
+ exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr))
+ limit_byte_size = *size;
+
+ if (limit_byte_size <= m_value.GetByteSize()) {
+ if (m_value.GetData(data, limit_byte_size))
+ return error; // Success;
+ }
+
+ error.SetErrorStringWithFormat("extracting data from value failed");
+ break;
+ }
+ case eValueTypeLoadAddress:
+ if (exe_ctx == nullptr) {
+ error.SetErrorString("can't read load address (no execution context)");
+ } else {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process == nullptr || !process->IsAlive()) {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target) {
+ // Allow expressions to run and evaluate things when the target has
+ // memory sections loaded. This allows you to use "target modules
+ // load" to load your executable and any shared libraries, then
+ // execute commands where you can look at types in data sections.
+ const SectionLoadList &target_sections = target->GetSectionLoadList();
+ if (!target_sections.IsEmpty()) {
+ address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ if (target_sections.ResolveLoadAddress(address, file_so_addr)) {
+ address_type = eAddressTypeLoad;
+ data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ data.SetAddressByteSize(
+ target->GetArchitecture().GetAddressByteSize());
+ } else
+ address = LLDB_INVALID_ADDRESS;
+ }
+ } else {
+ error.SetErrorString("can't read load address (invalid process)");
+ }
+ } else {
+ address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ address_type = eAddressTypeLoad;
+ data.SetByteOrder(
+ process->GetTarget().GetArchitecture().GetByteOrder());
+ data.SetAddressByteSize(
+ process->GetTarget().GetArchitecture().GetAddressByteSize());
+ }
+ }
+ break;
+
+ case eValueTypeFileAddress:
+ if (exe_ctx == nullptr) {
+ error.SetErrorString("can't read file address (no execution context)");
+ } else if (exe_ctx->GetTargetPtr() == nullptr) {
+ error.SetErrorString("can't read file address (invalid target)");
+ } else {
+ address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ if (address == LLDB_INVALID_ADDRESS) {
+ error.SetErrorString("invalid file address");
+ } else {
+ if (module == nullptr) {
+ // The only thing we can currently lock down to a module so that we
+ // can resolve a file address, is a variable.
+ Variable *variable = GetVariable();
+ if (variable) {
+ SymbolContext var_sc;
+ variable->CalculateSymbolContext(&var_sc);
+ module = var_sc.module_sp.get();
+ }
+ }
+
+ if (module) {
+ bool resolved = false;
+ ObjectFile *objfile = module->GetObjectFile();
+ if (objfile) {
+ Address so_addr(address, objfile->GetSectionList());
+ addr_t load_address =
+ so_addr.GetLoadAddress(exe_ctx->GetTargetPtr());
+ bool process_launched_and_stopped =
+ exe_ctx->GetProcessPtr()
+ ? StateIsStoppedState(exe_ctx->GetProcessPtr()->GetState(),
+ true /* must_exist */)
+ : false;
+ // Don't use the load address if the process has exited.
+ if (load_address != LLDB_INVALID_ADDRESS &&
+ process_launched_and_stopped) {
+ resolved = true;
+ address = load_address;
+ address_type = eAddressTypeLoad;
+ data.SetByteOrder(
+ exe_ctx->GetTargetRef().GetArchitecture().GetByteOrder());
+ data.SetAddressByteSize(exe_ctx->GetTargetRef()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ } else {
+ if (so_addr.IsSectionOffset()) {
+ resolved = true;
+ file_so_addr = so_addr;
+ data.SetByteOrder(objfile->GetByteOrder());
+ data.SetAddressByteSize(objfile->GetAddressByteSize());
+ }
+ }
+ }
+ if (!resolved) {
+ Variable *variable = GetVariable();
+
+ if (module) {
+ if (variable)
+ error.SetErrorStringWithFormat(
+ "unable to resolve the module for file address 0x%" PRIx64
+ " for variable '%s' in %s",
+ address, variable->GetName().AsCString(""),
+ module->GetFileSpec().GetPath().c_str());
+ else
+ error.SetErrorStringWithFormat(
+ "unable to resolve the module for file address 0x%" PRIx64
+ " in %s",
+ address, module->GetFileSpec().GetPath().c_str());
+ } else {
+ if (variable)
+ error.SetErrorStringWithFormat(
+ "unable to resolve the module for file address 0x%" PRIx64
+ " for variable '%s'",
+ address, variable->GetName().AsCString(""));
+ else
+ error.SetErrorStringWithFormat(
+ "unable to resolve the module for file address 0x%" PRIx64,
+ address);
+ }
+ }
+ } else {
+ // Can't convert a file address to anything valid without more
+ // context (which Module it came from)
+ error.SetErrorString(
+ "can't read memory from file address without more context");
+ }
+ }
+ }
+ break;
+
+ case eValueTypeHostAddress:
+ address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ address_type = eAddressTypeHost;
+ if (exe_ctx) {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target) {
+ data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
+ break;
+ }
+ }
+ // fallback to host settings
+ data.SetByteOrder(endian::InlHostByteOrder());
+ data.SetAddressByteSize(sizeof(void *));
+ break;
+ }
+
+ // Bail if we encountered any errors
+ if (error.Fail())
+ return error;
+
+ if (address == LLDB_INVALID_ADDRESS) {
+ error.SetErrorStringWithFormat("invalid %s address",
+ address_type == eAddressTypeHost ? "host"
+ : "load");
+ return error;
+ }
+
+ // If we got here, we need to read the value from memory
+ size_t byte_size = GetValueByteSize(&error, exe_ctx);
+
+ // Bail if we encountered any errors getting the byte size
+ if (error.Fail())
+ return error;
+
+ // No memory to read for zero-sized types.
+ if (byte_size == 0)
+ return error;
+
+ // Make sure we have enough room within "data", and if we don't make
+ // something large enough that does
+ if (!data.ValidOffsetForDataOfSize(data_offset, byte_size)) {
+ auto data_sp =
+ std::make_shared<DataBufferHeap>(data_offset + byte_size, '\0');
+ data.SetData(data_sp);
+ }
+
+ uint8_t *dst = const_cast<uint8_t *>(data.PeekData(data_offset, byte_size));
+ if (dst != nullptr) {
+ if (address_type == eAddressTypeHost) {
+ // The address is an address in this process, so just copy it.
+ if (address == 0) {
+ error.SetErrorStringWithFormat(
+ "trying to read from host address of 0.");
+ return error;
+ }
+ memcpy(dst, reinterpret_cast<uint8_t *>(address), byte_size);
+ } else if ((address_type == eAddressTypeLoad) ||
+ (address_type == eAddressTypeFile)) {
+ if (file_so_addr.IsValid()) {
+ // We have a file address that we were able to translate into a section
+ // offset address so we might be able to read this from the object
+ // files if we don't have a live process. Lets always try and read from
+ // the process if we have one though since we want to read the actual
+ // value by setting "prefer_file_cache" to false.
+ const bool prefer_file_cache = false;
+ if (exe_ctx->GetTargetRef().ReadMemory(file_so_addr, prefer_file_cache,
+ dst, byte_size,
+ error) != byte_size) {
+ error.SetErrorStringWithFormat(
+ "read memory from 0x%" PRIx64 " failed", (uint64_t)address);
+ }
+ } else {
+ // The execution context might have a NULL process, but it might have a
+ // valid process in the exe_ctx->target, so use the
+ // ExecutionContext::GetProcess accessor to ensure we get the process
+ // if there is one.
+ Process *process = exe_ctx->GetProcessPtr();
+
+ if (process) {
+ const size_t bytes_read =
+ process->ReadMemory(address, dst, byte_size, error);
+ if (bytes_read != byte_size)
+ error.SetErrorStringWithFormat(
+ "read memory from 0x%" PRIx64 " failed (%u of %u bytes read)",
+ (uint64_t)address, (uint32_t)bytes_read, (uint32_t)byte_size);
+ } else {
+ error.SetErrorStringWithFormat("read memory from 0x%" PRIx64
+ " failed (invalid process)",
+ (uint64_t)address);
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat("unsupported AddressType value (%i)",
+ address_type);
+ }
+ } else {
+ error.SetErrorStringWithFormat("out of memory");
+ }
+
+ return error;
+}
+
+Scalar &Value::ResolveValue(ExecutionContext *exe_ctx) {
+ const CompilerType &compiler_type = GetCompilerType();
+ if (compiler_type.IsValid()) {
+ switch (m_value_type) {
+ case eValueTypeScalar: // raw scalar value
+ break;
+
+ default:
+ case eValueTypeFileAddress:
+ case eValueTypeLoadAddress: // load address value
+ case eValueTypeHostAddress: // host address value (for memory in the process
+ // that is using liblldb)
+ {
+ DataExtractor data;
+ lldb::addr_t addr = m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ Status error(GetValueAsData(exe_ctx, data, 0, nullptr));
+ if (error.Success()) {
+ Scalar scalar;
+ if (compiler_type.GetValueAsScalar(data, 0, data.GetByteSize(),
+ scalar)) {
+ m_value = scalar;
+ m_value_type = eValueTypeScalar;
+ } else {
+ if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) {
+ m_value.Clear();
+ m_value_type = eValueTypeScalar;
+ }
+ }
+ } else {
+ if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) {
+ m_value.Clear();
+ m_value_type = eValueTypeScalar;
+ }
+ }
+ } break;
+ }
+ }
+ return m_value;
+}
+
+Variable *Value::GetVariable() {
+ if (m_context_type == eContextTypeVariable)
+ return static_cast<Variable *>(m_context);
+ return nullptr;
+}
+
+void Value::Clear() {
+ m_value.Clear();
+ m_vector.Clear();
+ m_compiler_type.Clear();
+ m_value_type = eValueTypeScalar;
+ m_context = nullptr;
+ m_context_type = eContextTypeInvalid;
+ m_data_buffer.Clear();
+}
+
+const char *Value::GetValueTypeAsCString(ValueType value_type) {
+ switch (value_type) {
+ case eValueTypeScalar:
+ return "scalar";
+ case eValueTypeVector:
+ return "vector";
+ case eValueTypeFileAddress:
+ return "file address";
+ case eValueTypeLoadAddress:
+ return "load address";
+ case eValueTypeHostAddress:
+ return "host address";
+ };
+ return "???";
+}
+
+const char *Value::GetContextTypeAsCString(ContextType context_type) {
+ switch (context_type) {
+ case eContextTypeInvalid:
+ return "invalid";
+ case eContextTypeRegisterInfo:
+ return "RegisterInfo *";
+ case eContextTypeLLDBType:
+ return "Type *";
+ case eContextTypeVariable:
+ return "Variable *";
+ };
+ return "???";
+}
+
+void Value::ConvertToLoadAddress(Module *module, Target *target) {
+ if (!module || !target || (GetValueType() != eValueTypeFileAddress))
+ return;
+
+ lldb::addr_t file_addr = GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ if (file_addr == LLDB_INVALID_ADDRESS)
+ return;
+
+ Address so_addr;
+ if (!module->ResolveFileAddress(file_addr, so_addr))
+ return;
+ lldb::addr_t load_addr = so_addr.GetLoadAddress(target);
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ return;
+
+ SetValueType(Value::eValueTypeLoadAddress);
+ GetScalar() = load_addr;
+}
+
+ValueList::ValueList(const ValueList &rhs) { m_values = rhs.m_values; }
+
+const ValueList &ValueList::operator=(const ValueList &rhs) {
+ m_values = rhs.m_values;
+ return *this;
+}
+
+void ValueList::PushValue(const Value &value) { m_values.push_back(value); }
+
+size_t ValueList::GetSize() { return m_values.size(); }
+
+Value *ValueList::GetValueAtIndex(size_t idx) {
+ if (idx < GetSize()) {
+ return &(m_values[idx]);
+ } else
+ return nullptr;
+}
+
+void ValueList::Clear() { m_values.clear(); }
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObject.cpp b/contrib/llvm-project/lldb/source/Core/ValueObject.cpp
new file mode 100644
index 000000000000..297365b4ecbd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObject.cpp
@@ -0,0 +1,3380 @@
+//===-- ValueObject.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObject.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObjectCast.h"
+#include "lldb/Core/ValueObjectChild.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectDynamicValue.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectSyntheticFilter.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/DataFormatters/DumpValueObjectOptions.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/DataFormatters/TypeFormat.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeValidator.h"
+#include "lldb/DataFormatters/ValueObjectPrinter.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Declaration.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Flags.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/SharingPtr.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-private-types.h"
+
+#include "llvm/Support/Compiler.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+#include <tuple>
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+namespace lldb_private {
+class ExecutionContextScope;
+}
+namespace lldb_private {
+class SymbolContextScope;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_utility;
+
+static user_id_t g_value_obj_uid = 0;
+
+// ValueObject constructor
+ValueObject::ValueObject(ValueObject &parent)
+ : UserID(++g_value_obj_uid), // Unique identifier for every value object
+ m_parent(&parent), m_root(nullptr),
+ m_update_point(parent.GetUpdatePoint()), m_name(), m_data(), m_value(),
+ m_error(), m_value_str(), m_old_value_str(), m_location_str(),
+ m_summary_str(), m_object_desc_str(), m_validation_result(),
+ m_manager(parent.GetManager()), m_children(), m_synthetic_children(),
+ m_dynamic_value(nullptr), m_synthetic_value(nullptr),
+ m_deref_valobj(nullptr), m_format(eFormatDefault),
+ m_last_format(eFormatDefault), m_last_format_mgr_revision(0),
+ m_type_summary_sp(), m_type_format_sp(), m_synthetic_children_sp(),
+ m_type_validator_sp(), m_user_id_of_forced_summary(),
+ m_address_type_of_ptr_or_ref_children(eAddressTypeInvalid),
+ m_value_checksum(),
+ m_preferred_display_language(lldb::eLanguageTypeUnknown),
+ m_language_flags(0), m_value_is_valid(false), m_value_did_change(false),
+ m_children_count_valid(false), m_old_value_valid(false),
+ m_is_deref_of_parent(false), m_is_array_item_for_pointer(false),
+ m_is_bitfield_for_scalar(false), m_is_child_at_offset(false),
+ m_is_getting_summary(false),
+ m_did_calculate_complete_objc_class_type(false),
+ m_is_synthetic_children_generated(
+ parent.m_is_synthetic_children_generated) {
+ m_manager->ManageObject(this);
+}
+
+// ValueObject constructor
+ValueObject::ValueObject(ExecutionContextScope *exe_scope,
+ AddressType child_ptr_or_ref_addr_type)
+ : UserID(++g_value_obj_uid), // Unique identifier for every value object
+ m_parent(nullptr), m_root(nullptr), m_update_point(exe_scope), m_name(),
+ m_data(), m_value(), m_error(), m_value_str(), m_old_value_str(),
+ m_location_str(), m_summary_str(), m_object_desc_str(),
+ m_validation_result(), m_manager(), m_children(), m_synthetic_children(),
+ m_dynamic_value(nullptr), m_synthetic_value(nullptr),
+ m_deref_valobj(nullptr), m_format(eFormatDefault),
+ m_last_format(eFormatDefault), m_last_format_mgr_revision(0),
+ m_type_summary_sp(), m_type_format_sp(), m_synthetic_children_sp(),
+ m_type_validator_sp(), m_user_id_of_forced_summary(),
+ m_address_type_of_ptr_or_ref_children(child_ptr_or_ref_addr_type),
+ m_value_checksum(),
+ m_preferred_display_language(lldb::eLanguageTypeUnknown),
+ m_language_flags(0), m_value_is_valid(false), m_value_did_change(false),
+ m_children_count_valid(false), m_old_value_valid(false),
+ m_is_deref_of_parent(false), m_is_array_item_for_pointer(false),
+ m_is_bitfield_for_scalar(false), m_is_child_at_offset(false),
+ m_is_getting_summary(false),
+ m_did_calculate_complete_objc_class_type(false),
+ m_is_synthetic_children_generated(false) {
+ m_manager = new ValueObjectManager();
+ m_manager->ManageObject(this);
+}
+
+// Destructor
+ValueObject::~ValueObject() {}
+
+bool ValueObject::UpdateValueIfNeeded(bool update_format) {
+
+ bool did_change_formats = false;
+
+ if (update_format)
+ did_change_formats = UpdateFormatsIfNeeded();
+
+ // If this is a constant value, then our success is predicated on whether we
+ // have an error or not
+ if (GetIsConstant()) {
+ // if you are constant, things might still have changed behind your back
+ // (e.g. you are a frozen object and things have changed deeper than you
+ // cared to freeze-dry yourself) in this case, your value has not changed,
+ // but "computed" entries might have, so you might now have a different
+ // summary, or a different object description. clear these so we will
+ // recompute them
+ if (update_format && !did_change_formats)
+ ClearUserVisibleData(eClearUserVisibleDataItemsSummary |
+ eClearUserVisibleDataItemsDescription);
+ return m_error.Success();
+ }
+
+ bool first_update = IsChecksumEmpty();
+
+ if (NeedsUpdating()) {
+ m_update_point.SetUpdated();
+
+ // Save the old value using swap to avoid a string copy which also will
+ // clear our m_value_str
+ if (m_value_str.empty()) {
+ m_old_value_valid = false;
+ } else {
+ m_old_value_valid = true;
+ m_old_value_str.swap(m_value_str);
+ ClearUserVisibleData(eClearUserVisibleDataItemsValue);
+ }
+
+ ClearUserVisibleData();
+
+ if (IsInScope()) {
+ const bool value_was_valid = GetValueIsValid();
+ SetValueDidChange(false);
+
+ m_error.Clear();
+
+ // Call the pure virtual function to update the value
+
+ bool need_compare_checksums = false;
+ llvm::SmallVector<uint8_t, 16> old_checksum;
+
+ if (!first_update && CanProvideValue()) {
+ need_compare_checksums = true;
+ old_checksum.resize(m_value_checksum.size());
+ std::copy(m_value_checksum.begin(), m_value_checksum.end(),
+ old_checksum.begin());
+ }
+
+ bool success = UpdateValue();
+
+ SetValueIsValid(success);
+
+ if (success) {
+ const uint64_t max_checksum_size = 128;
+ m_data.Checksum(m_value_checksum, max_checksum_size);
+ } else {
+ need_compare_checksums = false;
+ m_value_checksum.clear();
+ }
+
+ assert(!need_compare_checksums ||
+ (!old_checksum.empty() && !m_value_checksum.empty()));
+
+ if (first_update)
+ SetValueDidChange(false);
+ else if (!m_value_did_change && !success) {
+ // The value wasn't gotten successfully, so we mark this as changed if
+ // the value used to be valid and now isn't
+ SetValueDidChange(value_was_valid);
+ } else if (need_compare_checksums) {
+ SetValueDidChange(memcmp(&old_checksum[0], &m_value_checksum[0],
+ m_value_checksum.size()));
+ }
+
+ } else {
+ m_error.SetErrorString("out of scope");
+ }
+ }
+ return m_error.Success();
+}
+
+bool ValueObject::UpdateFormatsIfNeeded() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ if (log)
+ log->Printf("[%s %p] checking for FormatManager revisions. ValueObject "
+ "rev: %d - Global rev: %d",
+ GetName().GetCString(), static_cast<void *>(this),
+ m_last_format_mgr_revision,
+ DataVisualization::GetCurrentRevision());
+
+ bool any_change = false;
+
+ if ((m_last_format_mgr_revision != DataVisualization::GetCurrentRevision())) {
+ m_last_format_mgr_revision = DataVisualization::GetCurrentRevision();
+ any_change = true;
+
+ SetValueFormat(DataVisualization::GetFormat(*this, eNoDynamicValues));
+ SetSummaryFormat(
+ DataVisualization::GetSummaryFormat(*this, GetDynamicValueType()));
+#ifndef LLDB_DISABLE_PYTHON
+ SetSyntheticChildren(
+ DataVisualization::GetSyntheticChildren(*this, GetDynamicValueType()));
+#endif
+ SetValidator(DataVisualization::GetValidator(*this, GetDynamicValueType()));
+ }
+
+ return any_change;
+}
+
+void ValueObject::SetNeedsUpdate() {
+ m_update_point.SetNeedsUpdate();
+ // We have to clear the value string here so ConstResult children will notice
+ // if their values are changed by hand (i.e. with SetValueAsCString).
+ ClearUserVisibleData(eClearUserVisibleDataItemsValue);
+}
+
+void ValueObject::ClearDynamicTypeInformation() {
+ m_children_count_valid = false;
+ m_did_calculate_complete_objc_class_type = false;
+ m_last_format_mgr_revision = 0;
+ m_override_type = CompilerType();
+ SetValueFormat(lldb::TypeFormatImplSP());
+ SetSummaryFormat(lldb::TypeSummaryImplSP());
+ SetSyntheticChildren(lldb::SyntheticChildrenSP());
+}
+
+CompilerType ValueObject::MaybeCalculateCompleteType() {
+ CompilerType compiler_type(GetCompilerTypeImpl());
+
+ if (m_did_calculate_complete_objc_class_type) {
+ if (m_override_type.IsValid())
+ return m_override_type;
+ else
+ return compiler_type;
+ }
+
+ m_did_calculate_complete_objc_class_type = true;
+
+ ProcessSP process_sp(
+ GetUpdatePoint().GetExecutionContextRef().GetProcessSP());
+
+ if (!process_sp)
+ return compiler_type;
+
+ if (auto *runtime =
+ process_sp->GetLanguageRuntime(GetObjectRuntimeLanguage())) {
+ if (llvm::Optional<CompilerType> complete_type =
+ runtime->GetRuntimeType(compiler_type)) {
+ m_override_type = complete_type.getValue();
+ if (m_override_type.IsValid())
+ return m_override_type;
+ }
+ }
+ return compiler_type;
+}
+
+CompilerType ValueObject::GetCompilerType() {
+ return MaybeCalculateCompleteType();
+}
+
+TypeImpl ValueObject::GetTypeImpl() { return TypeImpl(GetCompilerType()); }
+
+DataExtractor &ValueObject::GetDataExtractor() {
+ UpdateValueIfNeeded(false);
+ return m_data;
+}
+
+const Status &ValueObject::GetError() {
+ UpdateValueIfNeeded(false);
+ return m_error;
+}
+
+ConstString ValueObject::GetName() const { return m_name; }
+
+const char *ValueObject::GetLocationAsCString() {
+ return GetLocationAsCStringImpl(m_value, m_data);
+}
+
+const char *ValueObject::GetLocationAsCStringImpl(const Value &value,
+ const DataExtractor &data) {
+ if (UpdateValueIfNeeded(false)) {
+ if (m_location_str.empty()) {
+ StreamString sstr;
+
+ Value::ValueType value_type = value.GetValueType();
+
+ switch (value_type) {
+ case Value::eValueTypeScalar:
+ case Value::eValueTypeVector:
+ if (value.GetContextType() == Value::eContextTypeRegisterInfo) {
+ RegisterInfo *reg_info = value.GetRegisterInfo();
+ if (reg_info) {
+ if (reg_info->name)
+ m_location_str = reg_info->name;
+ else if (reg_info->alt_name)
+ m_location_str = reg_info->alt_name;
+ if (m_location_str.empty())
+ m_location_str = (reg_info->encoding == lldb::eEncodingVector)
+ ? "vector"
+ : "scalar";
+ }
+ }
+ if (m_location_str.empty())
+ m_location_str =
+ (value_type == Value::eValueTypeVector) ? "vector" : "scalar";
+ break;
+
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeHostAddress: {
+ uint32_t addr_nibble_size = data.GetAddressByteSize() * 2;
+ sstr.Printf("0x%*.*llx", addr_nibble_size, addr_nibble_size,
+ value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS));
+ m_location_str = sstr.GetString();
+ } break;
+ }
+ }
+ }
+ return m_location_str.c_str();
+}
+
+Value &ValueObject::GetValue() { return m_value; }
+
+const Value &ValueObject::GetValue() const { return m_value; }
+
+bool ValueObject::ResolveValue(Scalar &scalar) {
+ if (UpdateValueIfNeeded(
+ false)) // make sure that you are up to date before returning anything
+ {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Value tmp_value(m_value);
+ scalar = tmp_value.ResolveValue(&exe_ctx);
+ if (scalar.IsValid()) {
+ const uint32_t bitfield_bit_size = GetBitfieldBitSize();
+ if (bitfield_bit_size)
+ return scalar.ExtractBitfield(bitfield_bit_size,
+ GetBitfieldBitOffset());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ValueObject::IsLogicalTrue(Status &error) {
+ if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) {
+ LazyBool is_logical_true = language->IsLogicalTrue(*this, error);
+ switch (is_logical_true) {
+ case eLazyBoolYes:
+ case eLazyBoolNo:
+ return (is_logical_true == true);
+ case eLazyBoolCalculate:
+ break;
+ }
+ }
+
+ Scalar scalar_value;
+
+ if (!ResolveValue(scalar_value)) {
+ error.SetErrorString("failed to get a scalar result");
+ return false;
+ }
+
+ bool ret;
+ ret = scalar_value.ULongLong(1) != 0;
+ error.Clear();
+ return ret;
+}
+
+bool ValueObject::GetValueIsValid() const { return m_value_is_valid; }
+
+void ValueObject::SetValueIsValid(bool b) { m_value_is_valid = b; }
+
+bool ValueObject::GetValueDidChange() { return m_value_did_change; }
+
+void ValueObject::SetValueDidChange(bool value_changed) {
+ m_value_did_change = value_changed;
+}
+
+ValueObjectSP ValueObject::GetChildAtIndex(size_t idx, bool can_create) {
+ ValueObjectSP child_sp;
+ // We may need to update our value if we are dynamic
+ if (IsPossibleDynamicType())
+ UpdateValueIfNeeded(false);
+ if (idx < GetNumChildren()) {
+ // Check if we have already made the child value object?
+ if (can_create && !m_children.HasChildAtIndex(idx)) {
+ // No we haven't created the child at this index, so lets have our
+ // subclass do it and cache the result for quick future access.
+ m_children.SetChildAtIndex(idx, CreateChildAtIndex(idx, false, 0));
+ }
+
+ ValueObject *child = m_children.GetChildAtIndex(idx);
+ if (child != nullptr)
+ return child->GetSP();
+ }
+ return child_sp;
+}
+
+lldb::ValueObjectSP
+ValueObject::GetChildAtIndexPath(llvm::ArrayRef<size_t> idxs,
+ size_t *index_of_error) {
+ if (idxs.size() == 0)
+ return GetSP();
+ ValueObjectSP root(GetSP());
+ for (size_t idx : idxs) {
+ root = root->GetChildAtIndex(idx, true);
+ if (!root) {
+ if (index_of_error)
+ *index_of_error = idx;
+ return root;
+ }
+ }
+ return root;
+}
+
+lldb::ValueObjectSP ValueObject::GetChildAtIndexPath(
+ llvm::ArrayRef<std::pair<size_t, bool>> idxs, size_t *index_of_error) {
+ if (idxs.size() == 0)
+ return GetSP();
+ ValueObjectSP root(GetSP());
+ for (std::pair<size_t, bool> idx : idxs) {
+ root = root->GetChildAtIndex(idx.first, idx.second);
+ if (!root) {
+ if (index_of_error)
+ *index_of_error = idx.first;
+ return root;
+ }
+ }
+ return root;
+}
+
+lldb::ValueObjectSP
+ValueObject::GetChildAtNamePath(llvm::ArrayRef<ConstString> names,
+ ConstString *name_of_error) {
+ if (names.size() == 0)
+ return GetSP();
+ ValueObjectSP root(GetSP());
+ for (ConstString name : names) {
+ root = root->GetChildMemberWithName(name, true);
+ if (!root) {
+ if (name_of_error)
+ *name_of_error = name;
+ return root;
+ }
+ }
+ return root;
+}
+
+lldb::ValueObjectSP ValueObject::GetChildAtNamePath(
+ llvm::ArrayRef<std::pair<ConstString, bool>> names,
+ ConstString *name_of_error) {
+ if (names.size() == 0)
+ return GetSP();
+ ValueObjectSP root(GetSP());
+ for (std::pair<ConstString, bool> name : names) {
+ root = root->GetChildMemberWithName(name.first, name.second);
+ if (!root) {
+ if (name_of_error)
+ *name_of_error = name.first;
+ return root;
+ }
+ }
+ return root;
+}
+
+size_t ValueObject::GetIndexOfChildWithName(ConstString name) {
+ bool omit_empty_base_classes = true;
+ return GetCompilerType().GetIndexOfChildWithName(name.GetCString(),
+ omit_empty_base_classes);
+}
+
+ValueObjectSP ValueObject::GetChildMemberWithName(ConstString name,
+ bool can_create) {
+ // when getting a child by name, it could be buried inside some base classes
+ // (which really aren't part of the expression path), so we need a vector of
+ // indexes that can get us down to the correct child
+ ValueObjectSP child_sp;
+
+ // We may need to update our value if we are dynamic
+ if (IsPossibleDynamicType())
+ UpdateValueIfNeeded(false);
+
+ std::vector<uint32_t> child_indexes;
+ bool omit_empty_base_classes = true;
+ const size_t num_child_indexes =
+ GetCompilerType().GetIndexOfChildMemberWithName(
+ name.GetCString(), omit_empty_base_classes, child_indexes);
+ if (num_child_indexes > 0) {
+ std::vector<uint32_t>::const_iterator pos = child_indexes.begin();
+ std::vector<uint32_t>::const_iterator end = child_indexes.end();
+
+ child_sp = GetChildAtIndex(*pos, can_create);
+ for (++pos; pos != end; ++pos) {
+ if (child_sp) {
+ ValueObjectSP new_child_sp(child_sp->GetChildAtIndex(*pos, can_create));
+ child_sp = new_child_sp;
+ } else {
+ child_sp.reset();
+ }
+ }
+ }
+ return child_sp;
+}
+
+size_t ValueObject::GetNumChildren(uint32_t max) {
+ UpdateValueIfNeeded();
+
+ if (max < UINT32_MAX) {
+ if (m_children_count_valid) {
+ size_t children_count = m_children.GetChildrenCount();
+ return children_count <= max ? children_count : max;
+ } else
+ return CalculateNumChildren(max);
+ }
+
+ if (!m_children_count_valid) {
+ SetNumChildren(CalculateNumChildren());
+ }
+ return m_children.GetChildrenCount();
+}
+
+bool ValueObject::MightHaveChildren() {
+ bool has_children = false;
+ const uint32_t type_info = GetTypeInfo();
+ if (type_info) {
+ if (type_info & (eTypeHasChildren | eTypeIsPointer | eTypeIsReference))
+ has_children = true;
+ } else {
+ has_children = GetNumChildren() > 0;
+ }
+ return has_children;
+}
+
+// Should only be called by ValueObject::GetNumChildren()
+void ValueObject::SetNumChildren(size_t num_children) {
+ m_children_count_valid = true;
+ m_children.SetChildrenCount(num_children);
+}
+
+void ValueObject::SetName(ConstString name) { m_name = name; }
+
+ValueObject *ValueObject::CreateChildAtIndex(size_t idx,
+ bool synthetic_array_member,
+ int32_t synthetic_index) {
+ ValueObject *valobj = nullptr;
+
+ bool omit_empty_base_classes = true;
+ bool ignore_array_bounds = synthetic_array_member;
+ std::string child_name_str;
+ uint32_t child_byte_size = 0;
+ int32_t child_byte_offset = 0;
+ uint32_t child_bitfield_bit_size = 0;
+ uint32_t child_bitfield_bit_offset = 0;
+ bool child_is_base_class = false;
+ bool child_is_deref_of_parent = false;
+ uint64_t language_flags = 0;
+
+ const bool transparent_pointers = !synthetic_array_member;
+ CompilerType child_compiler_type;
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ child_compiler_type = GetCompilerType().GetChildCompilerTypeAtIndex(
+ &exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, this, language_flags);
+ if (child_compiler_type) {
+ if (synthetic_index)
+ child_byte_offset += child_byte_size * synthetic_index;
+
+ ConstString child_name;
+ if (!child_name_str.empty())
+ child_name.SetCString(child_name_str.c_str());
+
+ valobj = new ValueObjectChild(
+ *this, child_compiler_type, child_name, child_byte_size,
+ child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid,
+ language_flags);
+ }
+
+ return valobj;
+}
+
+bool ValueObject::GetSummaryAsCString(TypeSummaryImpl *summary_ptr,
+ std::string &destination,
+ lldb::LanguageType lang) {
+ return GetSummaryAsCString(summary_ptr, destination,
+ TypeSummaryOptions().SetLanguage(lang));
+}
+
+bool ValueObject::GetSummaryAsCString(TypeSummaryImpl *summary_ptr,
+ std::string &destination,
+ const TypeSummaryOptions &options) {
+ destination.clear();
+
+ // ideally we would like to bail out if passing NULL, but if we do so we end
+ // up not providing the summary for function pointers anymore
+ if (/*summary_ptr == NULL ||*/ m_is_getting_summary)
+ return false;
+
+ m_is_getting_summary = true;
+
+ TypeSummaryOptions actual_options(options);
+
+ if (actual_options.GetLanguage() == lldb::eLanguageTypeUnknown)
+ actual_options.SetLanguage(GetPreferredDisplayLanguage());
+
+ // this is a hot path in code and we prefer to avoid setting this string all
+ // too often also clearing out other information that we might care to see in
+ // a crash log. might be useful in very specific situations though.
+ /*Host::SetCrashDescriptionWithFormat("Trying to fetch a summary for %s %s.
+ Summary provider's description is %s",
+ GetTypeName().GetCString(),
+ GetName().GetCString(),
+ summary_ptr->GetDescription().c_str());*/
+
+ if (UpdateValueIfNeeded(false) && summary_ptr) {
+ if (HasSyntheticValue())
+ m_synthetic_value->UpdateValueIfNeeded(); // the summary might depend on
+ // the synthetic children being
+ // up-to-date (e.g. ${svar%#})
+ summary_ptr->FormatObject(this, destination, actual_options);
+ }
+ m_is_getting_summary = false;
+ return !destination.empty();
+}
+
+const char *ValueObject::GetSummaryAsCString(lldb::LanguageType lang) {
+ if (UpdateValueIfNeeded(true) && m_summary_str.empty()) {
+ TypeSummaryOptions summary_options;
+ summary_options.SetLanguage(lang);
+ GetSummaryAsCString(GetSummaryFormat().get(), m_summary_str,
+ summary_options);
+ }
+ if (m_summary_str.empty())
+ return nullptr;
+ return m_summary_str.c_str();
+}
+
+bool ValueObject::GetSummaryAsCString(std::string &destination,
+ const TypeSummaryOptions &options) {
+ return GetSummaryAsCString(GetSummaryFormat().get(), destination, options);
+}
+
+bool ValueObject::IsCStringContainer(bool check_pointer) {
+ CompilerType pointee_or_element_compiler_type;
+ const Flags type_flags(GetTypeInfo(&pointee_or_element_compiler_type));
+ bool is_char_arr_ptr(type_flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
+ pointee_or_element_compiler_type.IsCharType());
+ if (!is_char_arr_ptr)
+ return false;
+ if (!check_pointer)
+ return true;
+ if (type_flags.Test(eTypeIsArray))
+ return true;
+ addr_t cstr_address = LLDB_INVALID_ADDRESS;
+ AddressType cstr_address_type = eAddressTypeInvalid;
+ cstr_address = GetAddressOf(true, &cstr_address_type);
+ return (cstr_address != LLDB_INVALID_ADDRESS);
+}
+
+size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx,
+ uint32_t item_count) {
+ CompilerType pointee_or_element_compiler_type;
+ const uint32_t type_info = GetTypeInfo(&pointee_or_element_compiler_type);
+ const bool is_pointer_type = type_info & eTypeIsPointer;
+ const bool is_array_type = type_info & eTypeIsArray;
+ if (!(is_pointer_type || is_array_type))
+ return 0;
+
+ if (item_count == 0)
+ return 0;
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ llvm::Optional<uint64_t> item_type_size =
+ pointee_or_element_compiler_type.GetByteSize(
+ exe_ctx.GetBestExecutionContextScope());
+ if (!item_type_size)
+ return 0;
+ const uint64_t bytes = item_count * *item_type_size;
+ const uint64_t offset = item_idx * *item_type_size;
+
+ if (item_idx == 0 && item_count == 1) // simply a deref
+ {
+ if (is_pointer_type) {
+ Status error;
+ ValueObjectSP pointee_sp = Dereference(error);
+ if (error.Fail() || pointee_sp.get() == nullptr)
+ return 0;
+ return pointee_sp->GetData(data, error);
+ } else {
+ ValueObjectSP child_sp = GetChildAtIndex(0, true);
+ if (child_sp.get() == nullptr)
+ return 0;
+ Status error;
+ return child_sp->GetData(data, error);
+ }
+ return true;
+ } else /* (items > 1) */
+ {
+ Status error;
+ lldb_private::DataBufferHeap *heap_buf_ptr = nullptr;
+ lldb::DataBufferSP data_sp(heap_buf_ptr =
+ new lldb_private::DataBufferHeap());
+
+ AddressType addr_type;
+ lldb::addr_t addr = is_pointer_type ? GetPointerValue(&addr_type)
+ : GetAddressOf(true, &addr_type);
+
+ switch (addr_type) {
+ case eAddressTypeFile: {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ addr = addr + offset;
+ Address so_addr;
+ module_sp->ResolveFileAddress(addr, so_addr);
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ heap_buf_ptr->SetByteSize(bytes);
+ size_t bytes_read = target->ReadMemory(
+ so_addr, false, heap_buf_ptr->GetBytes(), bytes, error);
+ if (error.Success()) {
+ data.SetData(data_sp);
+ return bytes_read;
+ }
+ }
+ }
+ } break;
+ case eAddressTypeLoad: {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ heap_buf_ptr->SetByteSize(bytes);
+ size_t bytes_read = process->ReadMemory(
+ addr + offset, heap_buf_ptr->GetBytes(), bytes, error);
+ if (error.Success() || bytes_read > 0) {
+ data.SetData(data_sp);
+ return bytes_read;
+ }
+ }
+ } break;
+ case eAddressTypeHost: {
+ auto max_bytes =
+ GetCompilerType().GetByteSize(exe_ctx.GetBestExecutionContextScope());
+ if (max_bytes && *max_bytes > offset) {
+ size_t bytes_read = std::min<uint64_t>(*max_bytes - offset, bytes);
+ addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ if (addr == 0 || addr == LLDB_INVALID_ADDRESS)
+ break;
+ heap_buf_ptr->CopyData((uint8_t *)(addr + offset), bytes_read);
+ data.SetData(data_sp);
+ return bytes_read;
+ }
+ } break;
+ case eAddressTypeInvalid:
+ break;
+ }
+ }
+ return 0;
+}
+
+uint64_t ValueObject::GetData(DataExtractor &data, Status &error) {
+ UpdateValueIfNeeded(false);
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ error = m_value.GetValueAsData(&exe_ctx, data, 0, GetModule().get());
+ if (error.Fail()) {
+ if (m_data.GetByteSize()) {
+ data = m_data;
+ error.Clear();
+ return data.GetByteSize();
+ } else {
+ return 0;
+ }
+ }
+ data.SetAddressByteSize(m_data.GetAddressByteSize());
+ data.SetByteOrder(m_data.GetByteOrder());
+ return data.GetByteSize();
+}
+
+bool ValueObject::SetData(DataExtractor &data, Status &error) {
+ error.Clear();
+ // Make sure our value is up to date first so that our location and location
+ // type is valid.
+ if (!UpdateValueIfNeeded(false)) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ uint64_t count = 0;
+ const Encoding encoding = GetCompilerType().GetEncoding(count);
+
+ const size_t byte_size = GetByteSize();
+
+ Value::ValueType value_type = m_value.GetValueType();
+
+ switch (value_type) {
+ case Value::eValueTypeScalar: {
+ Status set_error =
+ m_value.GetScalar().SetValueFromData(data, encoding, byte_size);
+
+ if (!set_error.Success()) {
+ error.SetErrorStringWithFormat("unable to set scalar value: %s",
+ set_error.AsCString());
+ return false;
+ }
+ } break;
+ case Value::eValueTypeLoadAddress: {
+ // If it is a load address, then the scalar value is the storage location
+ // of the data, and we have to shove this value down to that load location.
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ addr_t target_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ size_t bytes_written = process->WriteMemory(
+ target_addr, data.GetDataStart(), byte_size, error);
+ if (!error.Success())
+ return false;
+ if (bytes_written != byte_size) {
+ error.SetErrorString("unable to write value to memory");
+ return false;
+ }
+ }
+ } break;
+ case Value::eValueTypeHostAddress: {
+ // If it is a host address, then we stuff the scalar as a DataBuffer into
+ // the Value's data.
+ DataBufferSP buffer_sp(new DataBufferHeap(byte_size, 0));
+ m_data.SetData(buffer_sp, 0);
+ data.CopyByteOrderedData(0, byte_size,
+ const_cast<uint8_t *>(m_data.GetDataStart()),
+ byte_size, m_data.GetByteOrder());
+ m_value.GetScalar() = (uintptr_t)m_data.GetDataStart();
+ } break;
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeVector:
+ break;
+ }
+
+ // If we have reached this point, then we have successfully changed the
+ // value.
+ SetNeedsUpdate();
+ return true;
+}
+
+static bool CopyStringDataToBufferSP(const StreamString &source,
+ lldb::DataBufferSP &destination) {
+ destination = std::make_shared<DataBufferHeap>(source.GetSize() + 1, 0);
+ memcpy(destination->GetBytes(), source.GetString().data(), source.GetSize());
+ return true;
+}
+
+std::pair<size_t, bool>
+ValueObject::ReadPointedString(lldb::DataBufferSP &buffer_sp, Status &error,
+ uint32_t max_length, bool honor_array,
+ Format item_format) {
+ bool was_capped = false;
+ StreamString s;
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target) {
+ s << "<no target to read from>";
+ error.SetErrorString("no target to read from");
+ CopyStringDataToBufferSP(s, buffer_sp);
+ return {0, was_capped};
+ }
+
+ if (max_length == 0)
+ max_length = target->GetMaximumSizeOfStringSummary();
+
+ size_t bytes_read = 0;
+ size_t total_bytes_read = 0;
+
+ CompilerType compiler_type = GetCompilerType();
+ CompilerType elem_or_pointee_compiler_type;
+ const Flags type_flags(GetTypeInfo(&elem_or_pointee_compiler_type));
+ if (type_flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
+ elem_or_pointee_compiler_type.IsCharType()) {
+ addr_t cstr_address = LLDB_INVALID_ADDRESS;
+ AddressType cstr_address_type = eAddressTypeInvalid;
+
+ size_t cstr_len = 0;
+ bool capped_data = false;
+ const bool is_array = type_flags.Test(eTypeIsArray);
+ if (is_array) {
+ // We have an array
+ uint64_t array_size = 0;
+ if (compiler_type.IsArrayType(nullptr, &array_size, nullptr)) {
+ cstr_len = array_size;
+ if (cstr_len > max_length) {
+ capped_data = true;
+ cstr_len = max_length;
+ }
+ }
+ cstr_address = GetAddressOf(true, &cstr_address_type);
+ } else {
+ // We have a pointer
+ cstr_address = GetPointerValue(&cstr_address_type);
+ }
+
+ if (cstr_address == 0 || cstr_address == LLDB_INVALID_ADDRESS) {
+ if (cstr_address_type == eAddressTypeHost && is_array) {
+ const char *cstr = GetDataExtractor().PeekCStr(0);
+ if (cstr == nullptr) {
+ s << "<invalid address>";
+ error.SetErrorString("invalid address");
+ CopyStringDataToBufferSP(s, buffer_sp);
+ return {0, was_capped};
+ }
+ buffer_sp = std::make_shared<DataBufferHeap>(cstr_len, 0);
+ memcpy(buffer_sp->GetBytes(), cstr, cstr_len);
+ return {cstr_len, was_capped};
+ } else {
+ s << "<invalid address>";
+ error.SetErrorString("invalid address");
+ CopyStringDataToBufferSP(s, buffer_sp);
+ return {0, was_capped};
+ }
+ }
+
+ Address cstr_so_addr(cstr_address);
+ DataExtractor data;
+ if (cstr_len > 0 && honor_array) {
+ // I am using GetPointeeData() here to abstract the fact that some
+ // ValueObjects are actually frozen pointers in the host but the pointed-
+ // to data lives in the debuggee, and GetPointeeData() automatically
+ // takes care of this
+ GetPointeeData(data, 0, cstr_len);
+
+ if ((bytes_read = data.GetByteSize()) > 0) {
+ total_bytes_read = bytes_read;
+ for (size_t offset = 0; offset < bytes_read; offset++)
+ s.Printf("%c", *data.PeekData(offset, 1));
+ if (capped_data)
+ was_capped = true;
+ }
+ } else {
+ cstr_len = max_length;
+ const size_t k_max_buf_size = 64;
+
+ size_t offset = 0;
+
+ int cstr_len_displayed = -1;
+ bool capped_cstr = false;
+ // I am using GetPointeeData() here to abstract the fact that some
+ // ValueObjects are actually frozen pointers in the host but the pointed-
+ // to data lives in the debuggee, and GetPointeeData() automatically
+ // takes care of this
+ while ((bytes_read = GetPointeeData(data, offset, k_max_buf_size)) > 0) {
+ total_bytes_read += bytes_read;
+ const char *cstr = data.PeekCStr(0);
+ size_t len = strnlen(cstr, k_max_buf_size);
+ if (cstr_len_displayed < 0)
+ cstr_len_displayed = len;
+
+ if (len == 0)
+ break;
+ cstr_len_displayed += len;
+ if (len > bytes_read)
+ len = bytes_read;
+ if (len > cstr_len)
+ len = cstr_len;
+
+ for (size_t offset = 0; offset < bytes_read; offset++)
+ s.Printf("%c", *data.PeekData(offset, 1));
+
+ if (len < k_max_buf_size)
+ break;
+
+ if (len >= cstr_len) {
+ capped_cstr = true;
+ break;
+ }
+
+ cstr_len -= len;
+ offset += len;
+ }
+
+ if (cstr_len_displayed >= 0) {
+ if (capped_cstr)
+ was_capped = true;
+ }
+ }
+ } else {
+ error.SetErrorString("not a string object");
+ s << "<not a string object>";
+ }
+ CopyStringDataToBufferSP(s, buffer_sp);
+ return {total_bytes_read, was_capped};
+}
+
+std::pair<TypeValidatorResult, std::string> ValueObject::GetValidationStatus() {
+ if (!UpdateValueIfNeeded(true))
+ return {TypeValidatorResult::Success,
+ ""}; // not the validator's job to discuss update problems
+
+ if (m_validation_result.hasValue())
+ return m_validation_result.getValue();
+
+ if (!m_type_validator_sp)
+ return {TypeValidatorResult::Success, ""}; // no validator no failure
+
+ auto outcome = m_type_validator_sp->FormatObject(this);
+
+ return (m_validation_result = {outcome.m_result, outcome.m_message})
+ .getValue();
+}
+
+const char *ValueObject::GetObjectDescription() {
+ if (!UpdateValueIfNeeded(true))
+ return nullptr;
+
+ // Return cached value.
+ if (!m_object_desc_str.empty())
+ return m_object_desc_str.c_str();
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (!process)
+ return nullptr;
+
+ // Returns the object description produced by one language runtime.
+ auto get_object_description = [&](LanguageType language) -> const char * {
+ if (LanguageRuntime *runtime = process->GetLanguageRuntime(language)) {
+ StreamString s;
+ if (runtime->GetObjectDescription(s, *this)) {
+ m_object_desc_str.append(s.GetString());
+ return m_object_desc_str.c_str();
+ }
+ }
+ return nullptr;
+ };
+
+ // Try the native language runtime first.
+ LanguageType native_language = GetObjectRuntimeLanguage();
+ if (const char *desc = get_object_description(native_language))
+ return desc;
+
+ // Try the Objective-C language runtime. This fallback is necessary
+ // for Objective-C++ and mixed Objective-C / C++ programs.
+ if (Language::LanguageIsCFamily(native_language))
+ return get_object_description(eLanguageTypeObjC);
+ return nullptr;
+}
+
+bool ValueObject::GetValueAsCString(const lldb_private::TypeFormatImpl &format,
+ std::string &destination) {
+ if (UpdateValueIfNeeded(false))
+ return format.FormatObject(this, destination);
+ else
+ return false;
+}
+
+bool ValueObject::GetValueAsCString(lldb::Format format,
+ std::string &destination) {
+ return GetValueAsCString(TypeFormatImpl_Format(format), destination);
+}
+
+const char *ValueObject::GetValueAsCString() {
+ if (UpdateValueIfNeeded(true)) {
+ lldb::TypeFormatImplSP format_sp;
+ lldb::Format my_format = GetFormat();
+ if (my_format == lldb::eFormatDefault) {
+ if (m_type_format_sp)
+ format_sp = m_type_format_sp;
+ else {
+ if (m_is_bitfield_for_scalar)
+ my_format = eFormatUnsigned;
+ else {
+ if (m_value.GetContextType() == Value::eContextTypeRegisterInfo) {
+ const RegisterInfo *reg_info = m_value.GetRegisterInfo();
+ if (reg_info)
+ my_format = reg_info->format;
+ } else {
+ my_format = GetValue().GetCompilerType().GetFormat();
+ }
+ }
+ }
+ }
+ if (my_format != m_last_format || m_value_str.empty()) {
+ m_last_format = my_format;
+ if (!format_sp)
+ format_sp = std::make_shared<TypeFormatImpl_Format>(my_format);
+ if (GetValueAsCString(*format_sp.get(), m_value_str)) {
+ if (!m_value_did_change && m_old_value_valid) {
+ // The value was gotten successfully, so we consider the value as
+ // changed if the value string differs
+ SetValueDidChange(m_old_value_str != m_value_str);
+ }
+ }
+ }
+ }
+ if (m_value_str.empty())
+ return nullptr;
+ return m_value_str.c_str();
+}
+
+// if > 8bytes, 0 is returned. this method should mostly be used to read
+// address values out of pointers
+uint64_t ValueObject::GetValueAsUnsigned(uint64_t fail_value, bool *success) {
+ // If our byte size is zero this is an aggregate type that has children
+ if (CanProvideValue()) {
+ Scalar scalar;
+ if (ResolveValue(scalar)) {
+ if (success)
+ *success = true;
+ return scalar.ULongLong(fail_value);
+ }
+ // fallthrough, otherwise...
+ }
+
+ if (success)
+ *success = false;
+ return fail_value;
+}
+
+int64_t ValueObject::GetValueAsSigned(int64_t fail_value, bool *success) {
+ // If our byte size is zero this is an aggregate type that has children
+ if (CanProvideValue()) {
+ Scalar scalar;
+ if (ResolveValue(scalar)) {
+ if (success)
+ *success = true;
+ return scalar.SLongLong(fail_value);
+ }
+ // fallthrough, otherwise...
+ }
+
+ if (success)
+ *success = false;
+ return fail_value;
+}
+
+// if any more "special cases" are added to
+// ValueObject::DumpPrintableRepresentation() please keep this call up to date
+// by returning true for your new special cases. We will eventually move to
+// checking this call result before trying to display special cases
+bool ValueObject::HasSpecialPrintableRepresentation(
+ ValueObjectRepresentationStyle val_obj_display, Format custom_format) {
+ Flags flags(GetTypeInfo());
+ if (flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
+ val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) {
+ if (IsCStringContainer(true) &&
+ (custom_format == eFormatCString || custom_format == eFormatCharArray ||
+ custom_format == eFormatChar || custom_format == eFormatVectorOfChar))
+ return true;
+
+ if (flags.Test(eTypeIsArray)) {
+ if ((custom_format == eFormatBytes) ||
+ (custom_format == eFormatBytesWithASCII))
+ return true;
+
+ if ((custom_format == eFormatVectorOfChar) ||
+ (custom_format == eFormatVectorOfFloat32) ||
+ (custom_format == eFormatVectorOfFloat64) ||
+ (custom_format == eFormatVectorOfSInt16) ||
+ (custom_format == eFormatVectorOfSInt32) ||
+ (custom_format == eFormatVectorOfSInt64) ||
+ (custom_format == eFormatVectorOfSInt8) ||
+ (custom_format == eFormatVectorOfUInt128) ||
+ (custom_format == eFormatVectorOfUInt16) ||
+ (custom_format == eFormatVectorOfUInt32) ||
+ (custom_format == eFormatVectorOfUInt64) ||
+ (custom_format == eFormatVectorOfUInt8))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ValueObject::DumpPrintableRepresentation(
+ Stream &s, ValueObjectRepresentationStyle val_obj_display,
+ Format custom_format, PrintableRepresentationSpecialCases special,
+ bool do_dump_error) {
+
+ Flags flags(GetTypeInfo());
+
+ bool allow_special =
+ (special == ValueObject::PrintableRepresentationSpecialCases::eAllow);
+ const bool only_special = false;
+
+ if (allow_special) {
+ if (flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
+ val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) {
+ // when being asked to get a printable display an array or pointer type
+ // directly, try to "do the right thing"
+
+ if (IsCStringContainer(true) &&
+ (custom_format == eFormatCString ||
+ custom_format == eFormatCharArray || custom_format == eFormatChar ||
+ custom_format ==
+ eFormatVectorOfChar)) // print char[] & char* directly
+ {
+ Status error;
+ lldb::DataBufferSP buffer_sp;
+ std::pair<size_t, bool> read_string = ReadPointedString(
+ buffer_sp, error, 0, (custom_format == eFormatVectorOfChar) ||
+ (custom_format == eFormatCharArray));
+ lldb_private::formatters::StringPrinter::
+ ReadBufferAndDumpToStreamOptions options(*this);
+ options.SetData(DataExtractor(
+ buffer_sp, lldb::eByteOrderInvalid,
+ 8)); // none of this matters for a string - pass some defaults
+ options.SetStream(&s);
+ options.SetPrefixToken(nullptr);
+ options.SetQuote('"');
+ options.SetSourceSize(buffer_sp->GetByteSize());
+ options.SetIsTruncated(read_string.second);
+ formatters::StringPrinter::ReadBufferAndDumpToStream<
+ lldb_private::formatters::StringPrinter::StringElementType::ASCII>(
+ options);
+ return !error.Fail();
+ }
+
+ if (custom_format == eFormatEnum)
+ return false;
+
+ // this only works for arrays, because I have no way to know when the
+ // pointed memory ends, and no special \0 end of data marker
+ if (flags.Test(eTypeIsArray)) {
+ if ((custom_format == eFormatBytes) ||
+ (custom_format == eFormatBytesWithASCII)) {
+ const size_t count = GetNumChildren();
+
+ s << '[';
+ for (size_t low = 0; low < count; low++) {
+
+ if (low)
+ s << ',';
+
+ ValueObjectSP child = GetChildAtIndex(low, true);
+ if (!child.get()) {
+ s << "<invalid child>";
+ continue;
+ }
+ child->DumpPrintableRepresentation(
+ s, ValueObject::eValueObjectRepresentationStyleValue,
+ custom_format);
+ }
+
+ s << ']';
+
+ return true;
+ }
+
+ if ((custom_format == eFormatVectorOfChar) ||
+ (custom_format == eFormatVectorOfFloat32) ||
+ (custom_format == eFormatVectorOfFloat64) ||
+ (custom_format == eFormatVectorOfSInt16) ||
+ (custom_format == eFormatVectorOfSInt32) ||
+ (custom_format == eFormatVectorOfSInt64) ||
+ (custom_format == eFormatVectorOfSInt8) ||
+ (custom_format == eFormatVectorOfUInt128) ||
+ (custom_format == eFormatVectorOfUInt16) ||
+ (custom_format == eFormatVectorOfUInt32) ||
+ (custom_format == eFormatVectorOfUInt64) ||
+ (custom_format == eFormatVectorOfUInt8)) // arrays of bytes, bytes
+ // with ASCII or any vector
+ // format should be printed
+ // directly
+ {
+ const size_t count = GetNumChildren();
+
+ Format format = FormatManager::GetSingleItemFormat(custom_format);
+
+ s << '[';
+ for (size_t low = 0; low < count; low++) {
+
+ if (low)
+ s << ',';
+
+ ValueObjectSP child = GetChildAtIndex(low, true);
+ if (!child.get()) {
+ s << "<invalid child>";
+ continue;
+ }
+ child->DumpPrintableRepresentation(
+ s, ValueObject::eValueObjectRepresentationStyleValue, format);
+ }
+
+ s << ']';
+
+ return true;
+ }
+ }
+
+ if ((custom_format == eFormatBoolean) ||
+ (custom_format == eFormatBinary) || (custom_format == eFormatChar) ||
+ (custom_format == eFormatCharPrintable) ||
+ (custom_format == eFormatComplexFloat) ||
+ (custom_format == eFormatDecimal) || (custom_format == eFormatHex) ||
+ (custom_format == eFormatHexUppercase) ||
+ (custom_format == eFormatFloat) || (custom_format == eFormatOctal) ||
+ (custom_format == eFormatOSType) ||
+ (custom_format == eFormatUnicode16) ||
+ (custom_format == eFormatUnicode32) ||
+ (custom_format == eFormatUnsigned) ||
+ (custom_format == eFormatPointer) ||
+ (custom_format == eFormatComplexInteger) ||
+ (custom_format == eFormatComplex) ||
+ (custom_format == eFormatDefault)) // use the [] operator
+ return false;
+ }
+ }
+
+ if (only_special)
+ return false;
+
+ bool var_success = false;
+
+ {
+ llvm::StringRef str;
+
+ // this is a local stream that we are using to ensure that the data pointed
+ // to by cstr survives long enough for us to copy it to its destination -
+ // it is necessary to have this temporary storage area for cases where our
+ // desired output is not backed by some other longer-term storage
+ StreamString strm;
+
+ if (custom_format != eFormatInvalid)
+ SetFormat(custom_format);
+
+ switch (val_obj_display) {
+ case eValueObjectRepresentationStyleValue:
+ str = GetValueAsCString();
+ break;
+
+ case eValueObjectRepresentationStyleSummary:
+ str = GetSummaryAsCString();
+ break;
+
+ case eValueObjectRepresentationStyleLanguageSpecific:
+ str = GetObjectDescription();
+ break;
+
+ case eValueObjectRepresentationStyleLocation:
+ str = GetLocationAsCString();
+ break;
+
+ case eValueObjectRepresentationStyleChildrenCount:
+ strm.Printf("%" PRIu64 "", (uint64_t)GetNumChildren());
+ str = strm.GetString();
+ break;
+
+ case eValueObjectRepresentationStyleType:
+ str = GetTypeName().GetStringRef();
+ break;
+
+ case eValueObjectRepresentationStyleName:
+ str = GetName().GetStringRef();
+ break;
+
+ case eValueObjectRepresentationStyleExpressionPath:
+ GetExpressionPath(strm, false);
+ str = strm.GetString();
+ break;
+ }
+
+ if (str.empty()) {
+ if (val_obj_display == eValueObjectRepresentationStyleValue)
+ str = GetSummaryAsCString();
+ else if (val_obj_display == eValueObjectRepresentationStyleSummary) {
+ if (!CanProvideValue()) {
+ strm.Printf("%s @ %s", GetTypeName().AsCString(),
+ GetLocationAsCString());
+ str = strm.GetString();
+ } else
+ str = GetValueAsCString();
+ }
+ }
+
+ if (!str.empty())
+ s << str;
+ else {
+ if (m_error.Fail()) {
+ if (do_dump_error)
+ s.Printf("<%s>", m_error.AsCString());
+ else
+ return false;
+ } else if (val_obj_display == eValueObjectRepresentationStyleSummary)
+ s.PutCString("<no summary available>");
+ else if (val_obj_display == eValueObjectRepresentationStyleValue)
+ s.PutCString("<no value available>");
+ else if (val_obj_display ==
+ eValueObjectRepresentationStyleLanguageSpecific)
+ s.PutCString("<not a valid Objective-C object>"); // edit this if we
+ // have other runtimes
+ // that support a
+ // description
+ else
+ s.PutCString("<no printable representation>");
+ }
+
+ // we should only return false here if we could not do *anything* even if
+ // we have an error message as output, that's a success from our callers'
+ // perspective, so return true
+ var_success = true;
+
+ if (custom_format != eFormatInvalid)
+ SetFormat(eFormatDefault);
+ }
+
+ return var_success;
+}
+
+addr_t ValueObject::GetAddressOf(bool scalar_is_load_address,
+ AddressType *address_type) {
+ // Can't take address of a bitfield
+ if (IsBitfield())
+ return LLDB_INVALID_ADDRESS;
+
+ if (!UpdateValueIfNeeded(false))
+ return LLDB_INVALID_ADDRESS;
+
+ switch (m_value.GetValueType()) {
+ case Value::eValueTypeScalar:
+ case Value::eValueTypeVector:
+ if (scalar_is_load_address) {
+ if (address_type)
+ *address_type = eAddressTypeLoad;
+ return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ }
+ break;
+
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeFileAddress: {
+ if (address_type)
+ *address_type = m_value.GetValueAddressType();
+ return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ } break;
+ case Value::eValueTypeHostAddress: {
+ if (address_type)
+ *address_type = m_value.GetValueAddressType();
+ return LLDB_INVALID_ADDRESS;
+ } break;
+ }
+ if (address_type)
+ *address_type = eAddressTypeInvalid;
+ return LLDB_INVALID_ADDRESS;
+}
+
+addr_t ValueObject::GetPointerValue(AddressType *address_type) {
+ addr_t address = LLDB_INVALID_ADDRESS;
+ if (address_type)
+ *address_type = eAddressTypeInvalid;
+
+ if (!UpdateValueIfNeeded(false))
+ return address;
+
+ switch (m_value.GetValueType()) {
+ case Value::eValueTypeScalar:
+ case Value::eValueTypeVector:
+ address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ break;
+
+ case Value::eValueTypeHostAddress:
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeFileAddress: {
+ lldb::offset_t data_offset = 0;
+ address = m_data.GetPointer(&data_offset);
+ } break;
+ }
+
+ if (address_type)
+ *address_type = GetAddressTypeOfChildren();
+
+ return address;
+}
+
+bool ValueObject::SetValueFromCString(const char *value_str, Status &error) {
+ error.Clear();
+ // Make sure our value is up to date first so that our location and location
+ // type is valid.
+ if (!UpdateValueIfNeeded(false)) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ uint64_t count = 0;
+ const Encoding encoding = GetCompilerType().GetEncoding(count);
+
+ const size_t byte_size = GetByteSize();
+
+ Value::ValueType value_type = m_value.GetValueType();
+
+ if (value_type == Value::eValueTypeScalar) {
+ // If the value is already a scalar, then let the scalar change itself:
+ m_value.GetScalar().SetValueFromCString(value_str, encoding, byte_size);
+ } else if (byte_size <= 16) {
+ // If the value fits in a scalar, then make a new scalar and again let the
+ // scalar code do the conversion, then figure out where to put the new
+ // value.
+ Scalar new_scalar;
+ error = new_scalar.SetValueFromCString(value_str, encoding, byte_size);
+ if (error.Success()) {
+ switch (value_type) {
+ case Value::eValueTypeLoadAddress: {
+ // If it is a load address, then the scalar value is the storage
+ // location of the data, and we have to shove this value down to that
+ // load location.
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ addr_t target_addr =
+ m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ size_t bytes_written = process->WriteScalarToMemory(
+ target_addr, new_scalar, byte_size, error);
+ if (!error.Success())
+ return false;
+ if (bytes_written != byte_size) {
+ error.SetErrorString("unable to write value to memory");
+ return false;
+ }
+ }
+ } break;
+ case Value::eValueTypeHostAddress: {
+ // If it is a host address, then we stuff the scalar as a DataBuffer
+ // into the Value's data.
+ DataExtractor new_data;
+ new_data.SetByteOrder(m_data.GetByteOrder());
+
+ DataBufferSP buffer_sp(new DataBufferHeap(byte_size, 0));
+ m_data.SetData(buffer_sp, 0);
+ bool success = new_scalar.GetData(new_data);
+ if (success) {
+ new_data.CopyByteOrderedData(
+ 0, byte_size, const_cast<uint8_t *>(m_data.GetDataStart()),
+ byte_size, m_data.GetByteOrder());
+ }
+ m_value.GetScalar() = (uintptr_t)m_data.GetDataStart();
+
+ } break;
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeScalar:
+ case Value::eValueTypeVector:
+ break;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ // We don't support setting things bigger than a scalar at present.
+ error.SetErrorString("unable to write aggregate data type");
+ return false;
+ }
+
+ // If we have reached this point, then we have successfully changed the
+ // value.
+ SetNeedsUpdate();
+ return true;
+}
+
+bool ValueObject::GetDeclaration(Declaration &decl) {
+ decl.Clear();
+ return false;
+}
+
+ConstString ValueObject::GetTypeName() {
+ return GetCompilerType().GetConstTypeName();
+}
+
+ConstString ValueObject::GetDisplayTypeName() { return GetTypeName(); }
+
+ConstString ValueObject::GetQualifiedTypeName() {
+ return GetCompilerType().GetConstQualifiedTypeName();
+}
+
+LanguageType ValueObject::GetObjectRuntimeLanguage() {
+ return GetCompilerType().GetMinimumLanguage();
+}
+
+void ValueObject::AddSyntheticChild(ConstString key,
+ ValueObject *valobj) {
+ m_synthetic_children[key] = valobj;
+}
+
+ValueObjectSP ValueObject::GetSyntheticChild(ConstString key) const {
+ ValueObjectSP synthetic_child_sp;
+ std::map<ConstString, ValueObject *>::const_iterator pos =
+ m_synthetic_children.find(key);
+ if (pos != m_synthetic_children.end())
+ synthetic_child_sp = pos->second->GetSP();
+ return synthetic_child_sp;
+}
+
+uint32_t
+ValueObject::GetTypeInfo(CompilerType *pointee_or_element_compiler_type) {
+ return GetCompilerType().GetTypeInfo(pointee_or_element_compiler_type);
+}
+
+bool ValueObject::IsPointerType() { return GetCompilerType().IsPointerType(); }
+
+bool ValueObject::IsArrayType() {
+ return GetCompilerType().IsArrayType(nullptr, nullptr, nullptr);
+}
+
+bool ValueObject::IsScalarType() { return GetCompilerType().IsScalarType(); }
+
+bool ValueObject::IsIntegerType(bool &is_signed) {
+ return GetCompilerType().IsIntegerType(is_signed);
+}
+
+bool ValueObject::IsPointerOrReferenceType() {
+ return GetCompilerType().IsPointerOrReferenceType();
+}
+
+bool ValueObject::IsPossibleDynamicType() {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process)
+ return process->IsPossibleDynamicValue(*this);
+ else
+ return GetCompilerType().IsPossibleDynamicType(nullptr, true, true);
+}
+
+bool ValueObject::IsRuntimeSupportValue() {
+ Process *process(GetProcessSP().get());
+ if (!process)
+ return false;
+
+ // We trust the the compiler did the right thing and marked runtime support
+ // values as artificial.
+ if (!GetVariable() || !GetVariable()->IsArtificial())
+ return false;
+
+ LanguageType lang = eLanguageTypeUnknown;
+ if (auto *sym_ctx_scope = GetSymbolContextScope()) {
+ if (auto *func = sym_ctx_scope->CalculateSymbolContextFunction())
+ lang = func->GetLanguage();
+ else if (auto *comp_unit =
+ sym_ctx_scope->CalculateSymbolContextCompileUnit())
+ lang = comp_unit->GetLanguage();
+ }
+
+ if (auto *runtime = process->GetLanguageRuntime(lang))
+ if (runtime->IsWhitelistedRuntimeValue(GetName()))
+ return false;
+
+ return true;
+}
+
+bool ValueObject::IsNilReference() {
+ if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) {
+ return language->IsNilReference(*this);
+ }
+ return false;
+}
+
+bool ValueObject::IsUninitializedReference() {
+ if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) {
+ return language->IsUninitializedReference(*this);
+ }
+ return false;
+}
+
+// This allows you to create an array member using and index that doesn't not
+// fall in the normal bounds of the array. Many times structure can be defined
+// as: struct Collection {
+// uint32_t item_count;
+// Item item_array[0];
+// };
+// The size of the "item_array" is 1, but many times in practice there are more
+// items in "item_array".
+
+ValueObjectSP ValueObject::GetSyntheticArrayMember(size_t index,
+ bool can_create) {
+ ValueObjectSP synthetic_child_sp;
+ if (IsPointerType() || IsArrayType()) {
+ char index_str[64];
+ snprintf(index_str, sizeof(index_str), "[%" PRIu64 "]", (uint64_t)index);
+ ConstString index_const_str(index_str);
+ // Check if we have already created a synthetic array member in this valid
+ // object. If we have we will re-use it.
+ synthetic_child_sp = GetSyntheticChild(index_const_str);
+ if (!synthetic_child_sp) {
+ ValueObject *synthetic_child;
+ // We haven't made a synthetic array member for INDEX yet, so lets make
+ // one and cache it for any future reference.
+ synthetic_child = CreateChildAtIndex(0, true, index);
+
+ // Cache the value if we got one back...
+ if (synthetic_child) {
+ AddSyntheticChild(index_const_str, synthetic_child);
+ synthetic_child_sp = synthetic_child->GetSP();
+ synthetic_child_sp->SetName(ConstString(index_str));
+ synthetic_child_sp->m_is_array_item_for_pointer = true;
+ }
+ }
+ }
+ return synthetic_child_sp;
+}
+
+ValueObjectSP ValueObject::GetSyntheticBitFieldChild(uint32_t from, uint32_t to,
+ bool can_create) {
+ ValueObjectSP synthetic_child_sp;
+ if (IsScalarType()) {
+ char index_str[64];
+ snprintf(index_str, sizeof(index_str), "[%i-%i]", from, to);
+ ConstString index_const_str(index_str);
+ // Check if we have already created a synthetic array member in this valid
+ // object. If we have we will re-use it.
+ synthetic_child_sp = GetSyntheticChild(index_const_str);
+ if (!synthetic_child_sp) {
+ uint32_t bit_field_size = to - from + 1;
+ uint32_t bit_field_offset = from;
+ if (GetDataExtractor().GetByteOrder() == eByteOrderBig)
+ bit_field_offset =
+ GetByteSize() * 8 - bit_field_size - bit_field_offset;
+ // We haven't made a synthetic array member for INDEX yet, so lets make
+ // one and cache it for any future reference.
+ ValueObjectChild *synthetic_child = new ValueObjectChild(
+ *this, GetCompilerType(), index_const_str, GetByteSize(), 0,
+ bit_field_size, bit_field_offset, false, false, eAddressTypeInvalid,
+ 0);
+
+ // Cache the value if we got one back...
+ if (synthetic_child) {
+ AddSyntheticChild(index_const_str, synthetic_child);
+ synthetic_child_sp = synthetic_child->GetSP();
+ synthetic_child_sp->SetName(ConstString(index_str));
+ synthetic_child_sp->m_is_bitfield_for_scalar = true;
+ }
+ }
+ }
+ return synthetic_child_sp;
+}
+
+ValueObjectSP ValueObject::GetSyntheticChildAtOffset(
+ uint32_t offset, const CompilerType &type, bool can_create,
+ ConstString name_const_str) {
+
+ ValueObjectSP synthetic_child_sp;
+
+ if (name_const_str.IsEmpty()) {
+ char name_str[64];
+ snprintf(name_str, sizeof(name_str), "@%i", offset);
+ name_const_str.SetCString(name_str);
+ }
+
+ // Check if we have already created a synthetic array member in this valid
+ // object. If we have we will re-use it.
+ synthetic_child_sp = GetSyntheticChild(name_const_str);
+
+ if (synthetic_child_sp.get())
+ return synthetic_child_sp;
+
+ if (!can_create)
+ return {};
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ llvm::Optional<uint64_t> size =
+ type.GetByteSize(exe_ctx.GetBestExecutionContextScope());
+ if (!size)
+ return {};
+ ValueObjectChild *synthetic_child =
+ new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0,
+ false, false, eAddressTypeInvalid, 0);
+ if (synthetic_child) {
+ AddSyntheticChild(name_const_str, synthetic_child);
+ synthetic_child_sp = synthetic_child->GetSP();
+ synthetic_child_sp->SetName(name_const_str);
+ synthetic_child_sp->m_is_child_at_offset = true;
+ }
+ return synthetic_child_sp;
+}
+
+ValueObjectSP ValueObject::GetSyntheticBase(uint32_t offset,
+ const CompilerType &type,
+ bool can_create,
+ ConstString name_const_str) {
+ ValueObjectSP synthetic_child_sp;
+
+ if (name_const_str.IsEmpty()) {
+ char name_str[128];
+ snprintf(name_str, sizeof(name_str), "base%s@%i",
+ type.GetTypeName().AsCString("<unknown>"), offset);
+ name_const_str.SetCString(name_str);
+ }
+
+ // Check if we have already created a synthetic array member in this valid
+ // object. If we have we will re-use it.
+ synthetic_child_sp = GetSyntheticChild(name_const_str);
+
+ if (synthetic_child_sp.get())
+ return synthetic_child_sp;
+
+ if (!can_create)
+ return {};
+
+ const bool is_base_class = true;
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ llvm::Optional<uint64_t> size =
+ type.GetByteSize(exe_ctx.GetBestExecutionContextScope());
+ if (!size)
+ return {};
+ ValueObjectChild *synthetic_child =
+ new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0,
+ is_base_class, false, eAddressTypeInvalid, 0);
+ if (synthetic_child) {
+ AddSyntheticChild(name_const_str, synthetic_child);
+ synthetic_child_sp = synthetic_child->GetSP();
+ synthetic_child_sp->SetName(name_const_str);
+ }
+ return synthetic_child_sp;
+}
+
+// your expression path needs to have a leading . or -> (unless it somehow
+// "looks like" an array, in which case it has a leading [ symbol). while the [
+// is meaningful and should be shown to the user, . and -> are just parser
+// design, but by no means added information for the user.. strip them off
+static const char *SkipLeadingExpressionPathSeparators(const char *expression) {
+ if (!expression || !expression[0])
+ return expression;
+ if (expression[0] == '.')
+ return expression + 1;
+ if (expression[0] == '-' && expression[1] == '>')
+ return expression + 2;
+ return expression;
+}
+
+ValueObjectSP
+ValueObject::GetSyntheticExpressionPathChild(const char *expression,
+ bool can_create) {
+ ValueObjectSP synthetic_child_sp;
+ ConstString name_const_string(expression);
+ // Check if we have already created a synthetic array member in this valid
+ // object. If we have we will re-use it.
+ synthetic_child_sp = GetSyntheticChild(name_const_string);
+ if (!synthetic_child_sp) {
+ // We haven't made a synthetic array member for expression yet, so lets
+ // make one and cache it for any future reference.
+ synthetic_child_sp = GetValueForExpressionPath(
+ expression, nullptr, nullptr,
+ GetValueForExpressionPathOptions().SetSyntheticChildrenTraversal(
+ GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ None));
+
+ // Cache the value if we got one back...
+ if (synthetic_child_sp.get()) {
+ // FIXME: this causes a "real" child to end up with its name changed to
+ // the contents of expression
+ AddSyntheticChild(name_const_string, synthetic_child_sp.get());
+ synthetic_child_sp->SetName(
+ ConstString(SkipLeadingExpressionPathSeparators(expression)));
+ }
+ }
+ return synthetic_child_sp;
+}
+
+void ValueObject::CalculateSyntheticValue(bool use_synthetic) {
+ if (!use_synthetic)
+ return;
+
+ TargetSP target_sp(GetTargetSP());
+ if (target_sp && !target_sp->GetEnableSyntheticValue()) {
+ m_synthetic_value = nullptr;
+ return;
+ }
+
+ lldb::SyntheticChildrenSP current_synth_sp(m_synthetic_children_sp);
+
+ if (!UpdateFormatsIfNeeded() && m_synthetic_value)
+ return;
+
+ if (m_synthetic_children_sp.get() == nullptr)
+ return;
+
+ if (current_synth_sp == m_synthetic_children_sp && m_synthetic_value)
+ return;
+
+ m_synthetic_value = new ValueObjectSynthetic(*this, m_synthetic_children_sp);
+}
+
+void ValueObject::CalculateDynamicValue(DynamicValueType use_dynamic) {
+ if (use_dynamic == eNoDynamicValues)
+ return;
+
+ if (!m_dynamic_value && !IsDynamic()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsPossibleDynamicValue(*this)) {
+ ClearDynamicTypeInformation();
+ m_dynamic_value = new ValueObjectDynamicValue(*this, use_dynamic);
+ }
+ }
+}
+
+ValueObjectSP ValueObject::GetDynamicValue(DynamicValueType use_dynamic) {
+ if (use_dynamic == eNoDynamicValues)
+ return ValueObjectSP();
+
+ if (!IsDynamic() && m_dynamic_value == nullptr) {
+ CalculateDynamicValue(use_dynamic);
+ }
+ if (m_dynamic_value)
+ return m_dynamic_value->GetSP();
+ else
+ return ValueObjectSP();
+}
+
+ValueObjectSP ValueObject::GetStaticValue() { return GetSP(); }
+
+lldb::ValueObjectSP ValueObject::GetNonSyntheticValue() { return GetSP(); }
+
+ValueObjectSP ValueObject::GetSyntheticValue(bool use_synthetic) {
+ if (!use_synthetic)
+ return ValueObjectSP();
+
+ CalculateSyntheticValue(use_synthetic);
+
+ if (m_synthetic_value)
+ return m_synthetic_value->GetSP();
+ else
+ return ValueObjectSP();
+}
+
+bool ValueObject::HasSyntheticValue() {
+ UpdateFormatsIfNeeded();
+
+ if (m_synthetic_children_sp.get() == nullptr)
+ return false;
+
+ CalculateSyntheticValue(true);
+
+ return m_synthetic_value != nullptr;
+}
+
+bool ValueObject::GetBaseClassPath(Stream &s) {
+ if (IsBaseClass()) {
+ bool parent_had_base_class =
+ GetParent() && GetParent()->GetBaseClassPath(s);
+ CompilerType compiler_type = GetCompilerType();
+ std::string cxx_class_name;
+ bool this_had_base_class =
+ ClangASTContext::GetCXXClassName(compiler_type, cxx_class_name);
+ if (this_had_base_class) {
+ if (parent_had_base_class)
+ s.PutCString("::");
+ s.PutCString(cxx_class_name);
+ }
+ return parent_had_base_class || this_had_base_class;
+ }
+ return false;
+}
+
+ValueObject *ValueObject::GetNonBaseClassParent() {
+ if (GetParent()) {
+ if (GetParent()->IsBaseClass())
+ return GetParent()->GetNonBaseClassParent();
+ else
+ return GetParent();
+ }
+ return nullptr;
+}
+
+bool ValueObject::IsBaseClass(uint32_t &depth) {
+ if (!IsBaseClass()) {
+ depth = 0;
+ return false;
+ }
+ if (GetParent()) {
+ GetParent()->IsBaseClass(depth);
+ depth = depth + 1;
+ return true;
+ }
+ // TODO: a base of no parent? weird..
+ depth = 1;
+ return true;
+}
+
+void ValueObject::GetExpressionPath(Stream &s, bool qualify_cxx_base_classes,
+ GetExpressionPathFormat epformat) {
+ // synthetic children do not actually "exist" as part of the hierarchy, and
+ // sometimes they are consed up in ways that don't make sense from an
+ // underlying language/API standpoint. So, use a special code path here to
+ // return something that can hopefully be used in expression
+ if (m_is_synthetic_children_generated) {
+ UpdateValueIfNeeded();
+
+ if (m_value.GetValueType() == Value::eValueTypeLoadAddress) {
+ if (IsPointerOrReferenceType()) {
+ s.Printf("((%s)0x%" PRIx64 ")", GetTypeName().AsCString("void"),
+ GetValueAsUnsigned(0));
+ return;
+ } else {
+ uint64_t load_addr =
+ m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ s.Printf("(*( (%s *)0x%" PRIx64 "))", GetTypeName().AsCString("void"),
+ load_addr);
+ return;
+ }
+ }
+ }
+
+ if (CanProvideValue()) {
+ s.Printf("((%s)%s)", GetTypeName().AsCString("void"),
+ GetValueAsCString());
+ return;
+ }
+
+ return;
+ }
+
+ const bool is_deref_of_parent = IsDereferenceOfParent();
+
+ if (is_deref_of_parent &&
+ epformat == eGetExpressionPathFormatDereferencePointers) {
+ // this is the original format of GetExpressionPath() producing code like
+ // *(a_ptr).memberName, which is entirely fine, until you put this into
+ // StackFrame::GetValueForVariableExpressionPath() which prefers to see
+ // a_ptr->memberName. the eHonorPointers mode is meant to produce strings
+ // in this latter format
+ s.PutCString("*(");
+ }
+
+ ValueObject *parent = GetParent();
+
+ if (parent)
+ parent->GetExpressionPath(s, qualify_cxx_base_classes, epformat);
+
+ // if we are a deref_of_parent just because we are synthetic array members
+ // made up to allow ptr[%d] syntax to work in variable printing, then add our
+ // name ([%d]) to the expression path
+ if (m_is_array_item_for_pointer &&
+ epformat == eGetExpressionPathFormatHonorPointers)
+ s.PutCString(m_name.AsCString());
+
+ if (!IsBaseClass()) {
+ if (!is_deref_of_parent) {
+ ValueObject *non_base_class_parent = GetNonBaseClassParent();
+ if (non_base_class_parent &&
+ !non_base_class_parent->GetName().IsEmpty()) {
+ CompilerType non_base_class_parent_compiler_type =
+ non_base_class_parent->GetCompilerType();
+ if (non_base_class_parent_compiler_type) {
+ if (parent && parent->IsDereferenceOfParent() &&
+ epformat == eGetExpressionPathFormatHonorPointers) {
+ s.PutCString("->");
+ } else {
+ const uint32_t non_base_class_parent_type_info =
+ non_base_class_parent_compiler_type.GetTypeInfo();
+
+ if (non_base_class_parent_type_info & eTypeIsPointer) {
+ s.PutCString("->");
+ } else if ((non_base_class_parent_type_info & eTypeHasChildren) &&
+ !(non_base_class_parent_type_info & eTypeIsArray)) {
+ s.PutChar('.');
+ }
+ }
+ }
+ }
+
+ const char *name = GetName().GetCString();
+ if (name) {
+ if (qualify_cxx_base_classes) {
+ if (GetBaseClassPath(s))
+ s.PutCString("::");
+ }
+ s.PutCString(name);
+ }
+ }
+ }
+
+ if (is_deref_of_parent &&
+ epformat == eGetExpressionPathFormatDereferencePointers) {
+ s.PutChar(')');
+ }
+}
+
+ValueObjectSP ValueObject::GetValueForExpressionPath(
+ llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop,
+ ExpressionPathEndResultType *final_value_type,
+ const GetValueForExpressionPathOptions &options,
+ ExpressionPathAftermath *final_task_on_target) {
+
+ ExpressionPathScanEndReason dummy_reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnknown;
+ ExpressionPathEndResultType dummy_final_value_type =
+ ValueObject::eExpressionPathEndResultTypeInvalid;
+ ExpressionPathAftermath dummy_final_task_on_target =
+ ValueObject::eExpressionPathAftermathNothing;
+
+ ValueObjectSP ret_val = GetValueForExpressionPath_Impl(
+ expression, reason_to_stop ? reason_to_stop : &dummy_reason_to_stop,
+ final_value_type ? final_value_type : &dummy_final_value_type, options,
+ final_task_on_target ? final_task_on_target
+ : &dummy_final_task_on_target);
+
+ if (!final_task_on_target ||
+ *final_task_on_target == ValueObject::eExpressionPathAftermathNothing)
+ return ret_val;
+
+ if (ret_val.get() &&
+ ((final_value_type ? *final_value_type : dummy_final_value_type) ==
+ eExpressionPathEndResultTypePlain)) // I can only deref and takeaddress
+ // of plain objects
+ {
+ if ((final_task_on_target ? *final_task_on_target
+ : dummy_final_task_on_target) ==
+ ValueObject::eExpressionPathAftermathDereference) {
+ Status error;
+ ValueObjectSP final_value = ret_val->Dereference(error);
+ if (error.Fail() || !final_value.get()) {
+ if (reason_to_stop)
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
+ if (final_value_type)
+ *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ } else {
+ if (final_task_on_target)
+ *final_task_on_target = ValueObject::eExpressionPathAftermathNothing;
+ return final_value;
+ }
+ }
+ if (*final_task_on_target ==
+ ValueObject::eExpressionPathAftermathTakeAddress) {
+ Status error;
+ ValueObjectSP final_value = ret_val->AddressOf(error);
+ if (error.Fail() || !final_value.get()) {
+ if (reason_to_stop)
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonTakingAddressFailed;
+ if (final_value_type)
+ *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ } else {
+ if (final_task_on_target)
+ *final_task_on_target = ValueObject::eExpressionPathAftermathNothing;
+ return final_value;
+ }
+ }
+ }
+ return ret_val; // final_task_on_target will still have its original value, so
+ // you know I did not do it
+}
+
+ValueObjectSP ValueObject::GetValueForExpressionPath_Impl(
+ llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop,
+ ExpressionPathEndResultType *final_result,
+ const GetValueForExpressionPathOptions &options,
+ ExpressionPathAftermath *what_next) {
+ ValueObjectSP root = GetSP();
+
+ if (!root)
+ return nullptr;
+
+ llvm::StringRef remainder = expression;
+
+ while (true) {
+ llvm::StringRef temp_expression = remainder;
+
+ CompilerType root_compiler_type = root->GetCompilerType();
+ CompilerType pointee_compiler_type;
+ Flags pointee_compiler_type_info;
+
+ Flags root_compiler_type_info(
+ root_compiler_type.GetTypeInfo(&pointee_compiler_type));
+ if (pointee_compiler_type)
+ pointee_compiler_type_info.Reset(pointee_compiler_type.GetTypeInfo());
+
+ if (temp_expression.empty()) {
+ *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString;
+ return root;
+ }
+
+ switch (temp_expression.front()) {
+ case '-': {
+ temp_expression = temp_expression.drop_front();
+ if (options.m_check_dot_vs_arrow_syntax &&
+ root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to
+ // use -> on a
+ // non-pointer and I
+ // must catch the error
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonArrowInsteadOfDot;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ }
+ if (root_compiler_type_info.Test(eTypeIsObjC) && // if yo are trying to
+ // extract an ObjC IVar
+ // when this is forbidden
+ root_compiler_type_info.Test(eTypeIsPointer) &&
+ options.m_no_fragile_ivar) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonFragileIVarNotAllowed;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ }
+ if (!temp_expression.startswith(">")) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ }
+ }
+ LLVM_FALLTHROUGH;
+ case '.': // or fallthrough from ->
+ {
+ if (options.m_check_dot_vs_arrow_syntax &&
+ temp_expression.front() == '.' &&
+ root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to
+ // use . on a pointer
+ // and I must catch the
+ // error
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonDotInsteadOfArrow;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ temp_expression = temp_expression.drop_front(); // skip . or >
+
+ size_t next_sep_pos = temp_expression.find_first_of("-.[", 1);
+ ConstString child_name;
+ if (next_sep_pos == llvm::StringRef::npos) // if no other separator just
+ // expand this last layer
+ {
+ child_name.SetString(temp_expression);
+ ValueObjectSP child_valobj_sp =
+ root->GetChildMemberWithName(child_name, true);
+
+ if (child_valobj_sp.get()) // we know we are done, so just return
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonEndOfString;
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ return child_valobj_sp;
+ } else {
+ switch (options.m_synthetic_children_traversal) {
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ None:
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ FromSynthetic:
+ if (root->IsSynthetic()) {
+ child_valobj_sp = root->GetNonSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ ToSynthetic:
+ if (!root->IsSynthetic()) {
+ child_valobj_sp = root->GetSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ Both:
+ if (root->IsSynthetic()) {
+ child_valobj_sp = root->GetNonSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ } else {
+ child_valobj_sp = root->GetSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ }
+ }
+
+ // if we are here and options.m_no_synthetic_children is true,
+ // child_valobj_sp is going to be a NULL SP, so we hit the "else"
+ // branch, and return an error
+ if (child_valobj_sp.get()) // if it worked, just return
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonEndOfString;
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ return child_valobj_sp;
+ } else {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ } else // other layers do expand
+ {
+ llvm::StringRef next_separator = temp_expression.substr(next_sep_pos);
+
+ child_name.SetString(temp_expression.slice(0, next_sep_pos));
+
+ ValueObjectSP child_valobj_sp =
+ root->GetChildMemberWithName(child_name, true);
+ if (child_valobj_sp.get()) // store the new root and move on
+ {
+ root = child_valobj_sp;
+ remainder = next_separator;
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ } else {
+ switch (options.m_synthetic_children_traversal) {
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ None:
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ FromSynthetic:
+ if (root->IsSynthetic()) {
+ child_valobj_sp = root->GetNonSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ ToSynthetic:
+ if (!root->IsSynthetic()) {
+ child_valobj_sp = root->GetSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ Both:
+ if (root->IsSynthetic()) {
+ child_valobj_sp = root->GetNonSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ } else {
+ child_valobj_sp = root->GetSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ }
+ }
+
+ // if we are here and options.m_no_synthetic_children is true,
+ // child_valobj_sp is going to be a NULL SP, so we hit the "else"
+ // branch, and return an error
+ if (child_valobj_sp.get()) // if it worked, move on
+ {
+ root = child_valobj_sp;
+ remainder = next_separator;
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ } else {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ }
+ break;
+ }
+ case '[': {
+ if (!root_compiler_type_info.Test(eTypeIsArray) &&
+ !root_compiler_type_info.Test(eTypeIsPointer) &&
+ !root_compiler_type_info.Test(
+ eTypeIsVector)) // if this is not a T[] nor a T*
+ {
+ if (!root_compiler_type_info.Test(
+ eTypeIsScalar)) // if this is not even a scalar...
+ {
+ if (options.m_synthetic_children_traversal ==
+ GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ None) // ...only chance left is synthetic
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ }
+ } else if (!options.m_allow_bitfields_syntax) // if this is a scalar,
+ // check that we can
+ // expand bitfields
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ }
+ }
+ if (temp_expression[1] ==
+ ']') // if this is an unbounded range it only works for arrays
+ {
+ if (!root_compiler_type_info.Test(eTypeIsArray)) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else // even if something follows, we cannot expand unbounded ranges,
+ // just let the caller do it
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet;
+ *final_result =
+ ValueObject::eExpressionPathEndResultTypeUnboundedRange;
+ return root;
+ }
+ }
+
+ size_t close_bracket_position = temp_expression.find(']', 1);
+ if (close_bracket_position ==
+ llvm::StringRef::npos) // if there is no ], this is a syntax error
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+
+ llvm::StringRef bracket_expr =
+ temp_expression.slice(1, close_bracket_position);
+
+ // If this was an empty expression it would have been caught by the if
+ // above.
+ assert(!bracket_expr.empty());
+
+ if (!bracket_expr.contains('-')) {
+ // if no separator, this is of the form [N]. Note that this cannot be
+ // an unbounded range of the form [], because that case was handled
+ // above with an unconditional return.
+ unsigned long index = 0;
+ if (bracket_expr.getAsInteger(0, index)) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+
+ // from here on we do have a valid index
+ if (root_compiler_type_info.Test(eTypeIsArray)) {
+ ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true);
+ if (!child_valobj_sp)
+ child_valobj_sp = root->GetSyntheticArrayMember(index, true);
+ if (!child_valobj_sp)
+ if (root->HasSyntheticValue() &&
+ root->GetSyntheticValue()->GetNumChildren() > index)
+ child_valobj_sp =
+ root->GetSyntheticValue()->GetChildAtIndex(index, true);
+ if (child_valobj_sp) {
+ root = child_valobj_sp;
+ remainder =
+ temp_expression.substr(close_bracket_position + 1); // skip ]
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ } else {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ } else if (root_compiler_type_info.Test(eTypeIsPointer)) {
+ if (*what_next ==
+ ValueObject::
+ eExpressionPathAftermathDereference && // if this is a
+ // ptr-to-scalar, I
+ // am accessing it
+ // by index and I
+ // would have
+ // deref'ed anyway,
+ // then do it now
+ // and use this as
+ // a bitfield
+ pointee_compiler_type_info.Test(eTypeIsScalar)) {
+ Status error;
+ root = root->Dereference(error);
+ if (error.Fail() || !root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else {
+ *what_next = eExpressionPathAftermathNothing;
+ continue;
+ }
+ } else {
+ if (root->GetCompilerType().GetMinimumLanguage() ==
+ eLanguageTypeObjC &&
+ pointee_compiler_type_info.AllClear(eTypeIsPointer) &&
+ root->HasSyntheticValue() &&
+ (options.m_synthetic_children_traversal ==
+ GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::ToSynthetic ||
+ options.m_synthetic_children_traversal ==
+ GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::Both)) {
+ root = root->GetSyntheticValue()->GetChildAtIndex(index, true);
+ } else
+ root = root->GetSyntheticArrayMember(index, true);
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else {
+ remainder =
+ temp_expression.substr(close_bracket_position + 1); // skip ]
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ }
+ }
+ } else if (root_compiler_type_info.Test(eTypeIsScalar)) {
+ root = root->GetSyntheticBitFieldChild(index, index, true);
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else // we do not know how to expand members of bitfields, so we
+ // just return and let the caller do any further processing
+ {
+ *reason_to_stop = ValueObject::
+ eExpressionPathScanEndReasonBitfieldRangeOperatorMet;
+ *final_result = ValueObject::eExpressionPathEndResultTypeBitfield;
+ return root;
+ }
+ } else if (root_compiler_type_info.Test(eTypeIsVector)) {
+ root = root->GetChildAtIndex(index, true);
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ } else {
+ remainder =
+ temp_expression.substr(close_bracket_position + 1); // skip ]
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ }
+ } else if (options.m_synthetic_children_traversal ==
+ GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::ToSynthetic ||
+ options.m_synthetic_children_traversal ==
+ GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::Both) {
+ if (root->HasSyntheticValue())
+ root = root->GetSyntheticValue();
+ else if (!root->IsSynthetic()) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ // if we are here, then root itself is a synthetic VO.. should be
+ // good to go
+
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ root = root->GetChildAtIndex(index, true);
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else {
+ remainder =
+ temp_expression.substr(close_bracket_position + 1); // skip ]
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ }
+ } else {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ } else {
+ // we have a low and a high index
+ llvm::StringRef sleft, sright;
+ unsigned long low_index, high_index;
+ std::tie(sleft, sright) = bracket_expr.split('-');
+ if (sleft.getAsInteger(0, low_index) ||
+ sright.getAsInteger(0, high_index)) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+
+ if (low_index > high_index) // swap indices if required
+ std::swap(low_index, high_index);
+
+ if (root_compiler_type_info.Test(
+ eTypeIsScalar)) // expansion only works for scalars
+ {
+ root = root->GetSyntheticBitFieldChild(low_index, high_index, true);
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else {
+ *reason_to_stop = ValueObject::
+ eExpressionPathScanEndReasonBitfieldRangeOperatorMet;
+ *final_result = ValueObject::eExpressionPathEndResultTypeBitfield;
+ return root;
+ }
+ } else if (root_compiler_type_info.Test(
+ eTypeIsPointer) && // if this is a ptr-to-scalar, I am
+ // accessing it by index and I would
+ // have deref'ed anyway, then do it
+ // now and use this as a bitfield
+ *what_next ==
+ ValueObject::eExpressionPathAftermathDereference &&
+ pointee_compiler_type_info.Test(eTypeIsScalar)) {
+ Status error;
+ root = root->Dereference(error);
+ if (error.Fail() || !root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else {
+ *what_next = ValueObject::eExpressionPathAftermathNothing;
+ continue;
+ }
+ } else {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet;
+ *final_result = ValueObject::eExpressionPathEndResultTypeBoundedRange;
+ return root;
+ }
+ }
+ break;
+ }
+ default: // some non-separator is in the way
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ }
+ }
+}
+
+void ValueObject::LogValueObject(Log *log) {
+ if (log)
+ return LogValueObject(log, DumpValueObjectOptions(*this));
+}
+
+void ValueObject::LogValueObject(Log *log,
+ const DumpValueObjectOptions &options) {
+ if (log) {
+ StreamString s;
+ Dump(s, options);
+ if (s.GetSize())
+ log->PutCString(s.GetData());
+ }
+}
+
+void ValueObject::Dump(Stream &s) { Dump(s, DumpValueObjectOptions(*this)); }
+
+void ValueObject::Dump(Stream &s, const DumpValueObjectOptions &options) {
+ ValueObjectPrinter printer(this, &s, options);
+ printer.PrintValueObject();
+}
+
+ValueObjectSP ValueObject::CreateConstantValue(ConstString name) {
+ ValueObjectSP valobj_sp;
+
+ if (UpdateValueIfNeeded(false) && m_error.Success()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ DataExtractor data;
+ data.SetByteOrder(m_data.GetByteOrder());
+ data.SetAddressByteSize(m_data.GetAddressByteSize());
+
+ if (IsBitfield()) {
+ Value v(Scalar(GetValueAsUnsigned(UINT64_MAX)));
+ m_error = v.GetValueAsData(&exe_ctx, data, 0, GetModule().get());
+ } else
+ m_error = m_value.GetValueAsData(&exe_ctx, data, 0, GetModule().get());
+
+ valobj_sp = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), GetCompilerType(), name, data,
+ GetAddressOf());
+ }
+
+ if (!valobj_sp) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ valobj_sp = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), m_error);
+ }
+ return valobj_sp;
+}
+
+ValueObjectSP ValueObject::GetQualifiedRepresentationIfAvailable(
+ lldb::DynamicValueType dynValue, bool synthValue) {
+ ValueObjectSP result_sp(GetSP());
+
+ switch (dynValue) {
+ case lldb::eDynamicCanRunTarget:
+ case lldb::eDynamicDontRunTarget: {
+ if (!result_sp->IsDynamic()) {
+ if (result_sp->GetDynamicValue(dynValue))
+ result_sp = result_sp->GetDynamicValue(dynValue);
+ }
+ } break;
+ case lldb::eNoDynamicValues: {
+ if (result_sp->IsDynamic()) {
+ if (result_sp->GetStaticValue())
+ result_sp = result_sp->GetStaticValue();
+ }
+ } break;
+ }
+
+ if (synthValue) {
+ if (!result_sp->IsSynthetic()) {
+ if (result_sp->GetSyntheticValue())
+ result_sp = result_sp->GetSyntheticValue();
+ }
+ } else {
+ if (result_sp->IsSynthetic()) {
+ if (result_sp->GetNonSyntheticValue())
+ result_sp = result_sp->GetNonSyntheticValue();
+ }
+ }
+
+ return result_sp;
+}
+
+ValueObjectSP ValueObject::Dereference(Status &error) {
+ if (m_deref_valobj)
+ return m_deref_valobj->GetSP();
+
+ const bool is_pointer_or_reference_type = IsPointerOrReferenceType();
+ if (is_pointer_or_reference_type) {
+ bool omit_empty_base_classes = true;
+ bool ignore_array_bounds = false;
+
+ std::string child_name_str;
+ uint32_t child_byte_size = 0;
+ int32_t child_byte_offset = 0;
+ uint32_t child_bitfield_bit_size = 0;
+ uint32_t child_bitfield_bit_offset = 0;
+ bool child_is_base_class = false;
+ bool child_is_deref_of_parent = false;
+ const bool transparent_pointers = false;
+ CompilerType compiler_type = GetCompilerType();
+ CompilerType child_compiler_type;
+ uint64_t language_flags;
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ child_compiler_type = compiler_type.GetChildCompilerTypeAtIndex(
+ &exe_ctx, 0, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, this, language_flags);
+ if (child_compiler_type && child_byte_size) {
+ ConstString child_name;
+ if (!child_name_str.empty())
+ child_name.SetCString(child_name_str.c_str());
+
+ m_deref_valobj = new ValueObjectChild(
+ *this, child_compiler_type, child_name, child_byte_size,
+ child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid,
+ language_flags);
+ }
+ } else if (HasSyntheticValue()) {
+ m_deref_valobj =
+ GetSyntheticValue()
+ ->GetChildMemberWithName(ConstString("$$dereference$$"), true)
+ .get();
+ }
+
+ if (m_deref_valobj) {
+ error.Clear();
+ return m_deref_valobj->GetSP();
+ } else {
+ StreamString strm;
+ GetExpressionPath(strm, true);
+
+ if (is_pointer_or_reference_type)
+ error.SetErrorStringWithFormat("dereference failed: (%s) %s",
+ GetTypeName().AsCString("<invalid type>"),
+ strm.GetData());
+ else
+ error.SetErrorStringWithFormat("not a pointer or reference type: (%s) %s",
+ GetTypeName().AsCString("<invalid type>"),
+ strm.GetData());
+ return ValueObjectSP();
+ }
+}
+
+ValueObjectSP ValueObject::AddressOf(Status &error) {
+ if (m_addr_of_valobj_sp)
+ return m_addr_of_valobj_sp;
+
+ AddressType address_type = eAddressTypeInvalid;
+ const bool scalar_is_load_address = false;
+ addr_t addr = GetAddressOf(scalar_is_load_address, &address_type);
+ error.Clear();
+ if (addr != LLDB_INVALID_ADDRESS && address_type != eAddressTypeHost) {
+ switch (address_type) {
+ case eAddressTypeInvalid: {
+ StreamString expr_path_strm;
+ GetExpressionPath(expr_path_strm, true);
+ error.SetErrorStringWithFormat("'%s' is not in memory",
+ expr_path_strm.GetData());
+ } break;
+
+ case eAddressTypeFile:
+ case eAddressTypeLoad: {
+ CompilerType compiler_type = GetCompilerType();
+ if (compiler_type) {
+ std::string name(1, '&');
+ name.append(m_name.AsCString(""));
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ m_addr_of_valobj_sp = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(),
+ compiler_type.GetPointerType(), ConstString(name.c_str()), addr,
+ eAddressTypeInvalid, m_data.GetAddressByteSize());
+ }
+ } break;
+ default:
+ break;
+ }
+ } else {
+ StreamString expr_path_strm;
+ GetExpressionPath(expr_path_strm, true);
+ error.SetErrorStringWithFormat("'%s' doesn't have a valid address",
+ expr_path_strm.GetData());
+ }
+
+ return m_addr_of_valobj_sp;
+}
+
+ValueObjectSP ValueObject::Cast(const CompilerType &compiler_type) {
+ return ValueObjectCast::Create(*this, GetName(), compiler_type);
+}
+
+lldb::ValueObjectSP ValueObject::Clone(ConstString new_name) {
+ return ValueObjectCast::Create(*this, new_name, GetCompilerType());
+}
+
+ValueObjectSP ValueObject::CastPointerType(const char *name,
+ CompilerType &compiler_type) {
+ ValueObjectSP valobj_sp;
+ AddressType address_type;
+ addr_t ptr_value = GetPointerValue(&address_type);
+
+ if (ptr_value != LLDB_INVALID_ADDRESS) {
+ Address ptr_addr(ptr_value);
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ valobj_sp = ValueObjectMemory::Create(
+ exe_ctx.GetBestExecutionContextScope(), name, ptr_addr, compiler_type);
+ }
+ return valobj_sp;
+}
+
+ValueObjectSP ValueObject::CastPointerType(const char *name, TypeSP &type_sp) {
+ ValueObjectSP valobj_sp;
+ AddressType address_type;
+ addr_t ptr_value = GetPointerValue(&address_type);
+
+ if (ptr_value != LLDB_INVALID_ADDRESS) {
+ Address ptr_addr(ptr_value);
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ valobj_sp = ValueObjectMemory::Create(
+ exe_ctx.GetBestExecutionContextScope(), name, ptr_addr, type_sp);
+ }
+ return valobj_sp;
+}
+
+ValueObject::EvaluationPoint::EvaluationPoint()
+ : m_mod_id(), m_exe_ctx_ref(), m_needs_update(true) {}
+
+ValueObject::EvaluationPoint::EvaluationPoint(ExecutionContextScope *exe_scope,
+ bool use_selected)
+ : m_mod_id(), m_exe_ctx_ref(), m_needs_update(true) {
+ ExecutionContext exe_ctx(exe_scope);
+ TargetSP target_sp(exe_ctx.GetTargetSP());
+ if (target_sp) {
+ m_exe_ctx_ref.SetTargetSP(target_sp);
+ ProcessSP process_sp(exe_ctx.GetProcessSP());
+ if (!process_sp)
+ process_sp = target_sp->GetProcessSP();
+
+ if (process_sp) {
+ m_mod_id = process_sp->GetModID();
+ m_exe_ctx_ref.SetProcessSP(process_sp);
+
+ ThreadSP thread_sp(exe_ctx.GetThreadSP());
+
+ if (!thread_sp) {
+ if (use_selected)
+ thread_sp = process_sp->GetThreadList().GetSelectedThread();
+ }
+
+ if (thread_sp) {
+ m_exe_ctx_ref.SetThreadSP(thread_sp);
+
+ StackFrameSP frame_sp(exe_ctx.GetFrameSP());
+ if (!frame_sp) {
+ if (use_selected)
+ frame_sp = thread_sp->GetSelectedFrame();
+ }
+ if (frame_sp)
+ m_exe_ctx_ref.SetFrameSP(frame_sp);
+ }
+ }
+ }
+}
+
+ValueObject::EvaluationPoint::EvaluationPoint(
+ const ValueObject::EvaluationPoint &rhs)
+ : m_mod_id(), m_exe_ctx_ref(rhs.m_exe_ctx_ref), m_needs_update(true) {}
+
+ValueObject::EvaluationPoint::~EvaluationPoint() {}
+
+// This function checks the EvaluationPoint against the current process state.
+// If the current state matches the evaluation point, or the evaluation point
+// is already invalid, then we return false, meaning "no change". If the
+// current state is different, we update our state, and return true meaning
+// "yes, change". If we did see a change, we also set m_needs_update to true,
+// so future calls to NeedsUpdate will return true. exe_scope will be set to
+// the current execution context scope.
+
+bool ValueObject::EvaluationPoint::SyncWithProcessState(
+ bool accept_invalid_exe_ctx) {
+ // Start with the target, if it is NULL, then we're obviously not going to
+ // get any further:
+ const bool thread_and_frame_only_if_stopped = true;
+ ExecutionContext exe_ctx(
+ m_exe_ctx_ref.Lock(thread_and_frame_only_if_stopped));
+
+ if (exe_ctx.GetTargetPtr() == nullptr)
+ return false;
+
+ // If we don't have a process nothing can change.
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process == nullptr)
+ return false;
+
+ // If our stop id is the current stop ID, nothing has changed:
+ ProcessModID current_mod_id = process->GetModID();
+
+ // If the current stop id is 0, either we haven't run yet, or the process
+ // state has been cleared. In either case, we aren't going to be able to sync
+ // with the process state.
+ if (current_mod_id.GetStopID() == 0)
+ return false;
+
+ bool changed = false;
+ const bool was_valid = m_mod_id.IsValid();
+ if (was_valid) {
+ if (m_mod_id == current_mod_id) {
+ // Everything is already up to date in this object, no need to update the
+ // execution context scope.
+ changed = false;
+ } else {
+ m_mod_id = current_mod_id;
+ m_needs_update = true;
+ changed = true;
+ }
+ }
+
+ // Now re-look up the thread and frame in case the underlying objects have
+ // gone away & been recreated. That way we'll be sure to return a valid
+ // exe_scope. If we used to have a thread or a frame but can't find it
+ // anymore, then mark ourselves as invalid.
+
+ if (!accept_invalid_exe_ctx) {
+ if (m_exe_ctx_ref.HasThreadRef()) {
+ ThreadSP thread_sp(m_exe_ctx_ref.GetThreadSP());
+ if (thread_sp) {
+ if (m_exe_ctx_ref.HasFrameRef()) {
+ StackFrameSP frame_sp(m_exe_ctx_ref.GetFrameSP());
+ if (!frame_sp) {
+ // We used to have a frame, but now it is gone
+ SetInvalid();
+ changed = was_valid;
+ }
+ }
+ } else {
+ // We used to have a thread, but now it is gone
+ SetInvalid();
+ changed = was_valid;
+ }
+ }
+ }
+
+ return changed;
+}
+
+void ValueObject::EvaluationPoint::SetUpdated() {
+ ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
+ if (process_sp)
+ m_mod_id = process_sp->GetModID();
+ m_needs_update = false;
+}
+
+void ValueObject::ClearUserVisibleData(uint32_t clear_mask) {
+ if ((clear_mask & eClearUserVisibleDataItemsValue) ==
+ eClearUserVisibleDataItemsValue)
+ m_value_str.clear();
+
+ if ((clear_mask & eClearUserVisibleDataItemsLocation) ==
+ eClearUserVisibleDataItemsLocation)
+ m_location_str.clear();
+
+ if ((clear_mask & eClearUserVisibleDataItemsSummary) ==
+ eClearUserVisibleDataItemsSummary)
+ m_summary_str.clear();
+
+ if ((clear_mask & eClearUserVisibleDataItemsDescription) ==
+ eClearUserVisibleDataItemsDescription)
+ m_object_desc_str.clear();
+
+ if ((clear_mask & eClearUserVisibleDataItemsSyntheticChildren) ==
+ eClearUserVisibleDataItemsSyntheticChildren) {
+ if (m_synthetic_value)
+ m_synthetic_value = nullptr;
+ }
+
+ if ((clear_mask & eClearUserVisibleDataItemsValidator) ==
+ eClearUserVisibleDataItemsValidator)
+ m_validation_result.reset();
+}
+
+SymbolContextScope *ValueObject::GetSymbolContextScope() {
+ if (m_parent) {
+ if (!m_parent->IsPointerOrReferenceType())
+ return m_parent->GetSymbolContextScope();
+ }
+ return nullptr;
+}
+
+lldb::ValueObjectSP
+ValueObject::CreateValueObjectFromExpression(llvm::StringRef name,
+ llvm::StringRef expression,
+ const ExecutionContext &exe_ctx) {
+ return CreateValueObjectFromExpression(name, expression, exe_ctx,
+ EvaluateExpressionOptions());
+}
+
+lldb::ValueObjectSP ValueObject::CreateValueObjectFromExpression(
+ llvm::StringRef name, llvm::StringRef expression,
+ const ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options) {
+ lldb::ValueObjectSP retval_sp;
+ lldb::TargetSP target_sp(exe_ctx.GetTargetSP());
+ if (!target_sp)
+ return retval_sp;
+ if (expression.empty())
+ return retval_sp;
+ target_sp->EvaluateExpression(expression, exe_ctx.GetFrameSP().get(),
+ retval_sp, options);
+ if (retval_sp && !name.empty())
+ retval_sp->SetName(ConstString(name));
+ return retval_sp;
+}
+
+lldb::ValueObjectSP ValueObject::CreateValueObjectFromAddress(
+ llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx,
+ CompilerType type) {
+ if (type) {
+ CompilerType pointer_type(type.GetPointerType());
+ if (pointer_type) {
+ lldb::DataBufferSP buffer(
+ new lldb_private::DataBufferHeap(&address, sizeof(lldb::addr_t)));
+ lldb::ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), pointer_type,
+ ConstString(name), buffer, exe_ctx.GetByteOrder(),
+ exe_ctx.GetAddressByteSize()));
+ if (ptr_result_valobj_sp) {
+ ptr_result_valobj_sp->GetValue().SetValueType(
+ Value::eValueTypeLoadAddress);
+ Status err;
+ ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err);
+ if (ptr_result_valobj_sp && !name.empty())
+ ptr_result_valobj_sp->SetName(ConstString(name));
+ }
+ return ptr_result_valobj_sp;
+ }
+ }
+ return lldb::ValueObjectSP();
+}
+
+lldb::ValueObjectSP ValueObject::CreateValueObjectFromData(
+ llvm::StringRef name, const DataExtractor &data,
+ const ExecutionContext &exe_ctx, CompilerType type) {
+ lldb::ValueObjectSP new_value_sp;
+ new_value_sp = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), type, ConstString(name), data,
+ LLDB_INVALID_ADDRESS);
+ new_value_sp->SetAddressTypeOfChildren(eAddressTypeLoad);
+ if (new_value_sp && !name.empty())
+ new_value_sp->SetName(ConstString(name));
+ return new_value_sp;
+}
+
+ModuleSP ValueObject::GetModule() {
+ ValueObject *root(GetRoot());
+ if (root != this)
+ return root->GetModule();
+ return lldb::ModuleSP();
+}
+
+ValueObject *ValueObject::GetRoot() {
+ if (m_root)
+ return m_root;
+ return (m_root = FollowParentChain([](ValueObject *vo) -> bool {
+ return (vo->m_parent != nullptr);
+ }));
+}
+
+ValueObject *
+ValueObject::FollowParentChain(std::function<bool(ValueObject *)> f) {
+ ValueObject *vo = this;
+ while (vo) {
+ if (!f(vo))
+ break;
+ vo = vo->m_parent;
+ }
+ return vo;
+}
+
+AddressType ValueObject::GetAddressTypeOfChildren() {
+ if (m_address_type_of_ptr_or_ref_children == eAddressTypeInvalid) {
+ ValueObject *root(GetRoot());
+ if (root != this)
+ return root->GetAddressTypeOfChildren();
+ }
+ return m_address_type_of_ptr_or_ref_children;
+}
+
+lldb::DynamicValueType ValueObject::GetDynamicValueType() {
+ ValueObject *with_dv_info = this;
+ while (with_dv_info) {
+ if (with_dv_info->HasDynamicValueTypeInfo())
+ return with_dv_info->GetDynamicValueTypeImpl();
+ with_dv_info = with_dv_info->m_parent;
+ }
+ return lldb::eNoDynamicValues;
+}
+
+lldb::Format ValueObject::GetFormat() const {
+ const ValueObject *with_fmt_info = this;
+ while (with_fmt_info) {
+ if (with_fmt_info->m_format != lldb::eFormatDefault)
+ return with_fmt_info->m_format;
+ with_fmt_info = with_fmt_info->m_parent;
+ }
+ return m_format;
+}
+
+lldb::LanguageType ValueObject::GetPreferredDisplayLanguage() {
+ lldb::LanguageType type = m_preferred_display_language;
+ if (m_preferred_display_language == lldb::eLanguageTypeUnknown) {
+ if (GetRoot()) {
+ if (GetRoot() == this) {
+ if (StackFrameSP frame_sp = GetFrameSP()) {
+ const SymbolContext &sc(
+ frame_sp->GetSymbolContext(eSymbolContextCompUnit));
+ if (CompileUnit *cu = sc.comp_unit)
+ type = cu->GetLanguage();
+ }
+ } else {
+ type = GetRoot()->GetPreferredDisplayLanguage();
+ }
+ }
+ }
+ return (m_preferred_display_language = type); // only compute it once
+}
+
+void ValueObject::SetPreferredDisplayLanguage(lldb::LanguageType lt) {
+ m_preferred_display_language = lt;
+}
+
+void ValueObject::SetPreferredDisplayLanguageIfNeeded(lldb::LanguageType lt) {
+ if (m_preferred_display_language == lldb::eLanguageTypeUnknown)
+ SetPreferredDisplayLanguage(lt);
+}
+
+bool ValueObject::CanProvideValue() {
+ // we need to support invalid types as providers of values because some bare-
+ // board debugging scenarios have no notion of types, but still manage to
+ // have raw numeric values for things like registers. sigh.
+ const CompilerType &type(GetCompilerType());
+ return (!type.IsValid()) || (0 != (type.GetTypeInfo() & eTypeHasValue));
+}
+
+bool ValueObject::IsChecksumEmpty() { return m_value_checksum.empty(); }
+
+ValueObjectSP ValueObject::Persist() {
+ if (!UpdateValueIfNeeded())
+ return nullptr;
+
+ TargetSP target_sp(GetTargetSP());
+ if (!target_sp)
+ return nullptr;
+
+ PersistentExpressionState *persistent_state =
+ target_sp->GetPersistentExpressionStateForLanguage(
+ GetPreferredDisplayLanguage());
+
+ if (!persistent_state)
+ return nullptr;
+
+ auto prefix = persistent_state->GetPersistentVariablePrefix();
+ ConstString name =
+ persistent_state->GetNextPersistentVariableName(*target_sp, prefix);
+
+ ValueObjectSP const_result_sp =
+ ValueObjectConstResult::Create(target_sp.get(), GetValue(), name);
+
+ ExpressionVariableSP clang_var_sp =
+ persistent_state->CreatePersistentVariable(const_result_sp);
+ clang_var_sp->m_live_sp = clang_var_sp->m_frozen_sp;
+ clang_var_sp->m_flags |= ExpressionVariable::EVIsProgramReference;
+
+ return clang_var_sp->GetValueObject();
+}
+
+bool ValueObject::IsSyntheticChildrenGenerated() {
+ return m_is_synthetic_children_generated;
+}
+
+void ValueObject::SetSyntheticChildrenGenerated(bool b) {
+ m_is_synthetic_children_generated = b;
+}
+
+uint64_t ValueObject::GetLanguageFlags() { return m_language_flags; }
+
+void ValueObject::SetLanguageFlags(uint64_t flags) { m_language_flags = flags; }
+
+ValueObjectManager::ValueObjectManager(lldb::ValueObjectSP in_valobj_sp,
+ lldb::DynamicValueType use_dynamic,
+ bool use_synthetic) : m_root_valobj_sp(),
+ m_user_valobj_sp(), m_use_dynamic(use_dynamic), m_stop_id(UINT32_MAX),
+ m_use_synthetic(use_synthetic) {
+ if (!in_valobj_sp)
+ return;
+ // If the user passes in a value object that is dynamic or synthetic, then
+ // water it down to the static type.
+ m_root_valobj_sp = in_valobj_sp->GetQualifiedRepresentationIfAvailable(lldb::eNoDynamicValues, false);
+}
+
+bool ValueObjectManager::IsValid() const {
+ if (!m_root_valobj_sp)
+ return false;
+ lldb::TargetSP target_sp = GetTargetSP();
+ if (target_sp)
+ return target_sp->IsValid();
+ return false;
+}
+
+lldb::ValueObjectSP ValueObjectManager::GetSP() {
+ lldb::ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+
+ const uint32_t current_stop_id = process_sp->GetLastNaturalStopID();
+ if (current_stop_id == m_stop_id)
+ return m_user_valobj_sp;
+
+ m_stop_id = current_stop_id;
+
+ if (!m_root_valobj_sp) {
+ m_user_valobj_sp.reset();
+ return m_root_valobj_sp;
+ }
+
+ m_user_valobj_sp = m_root_valobj_sp;
+
+ if (m_use_dynamic != lldb::eNoDynamicValues) {
+ lldb::ValueObjectSP dynamic_sp = m_user_valobj_sp->GetDynamicValue(m_use_dynamic);
+ if (dynamic_sp)
+ m_user_valobj_sp = dynamic_sp;
+ }
+
+ if (m_use_synthetic) {
+ lldb::ValueObjectSP synthetic_sp = m_user_valobj_sp->GetSyntheticValue(m_use_synthetic);
+ if (synthetic_sp)
+ m_user_valobj_sp = synthetic_sp;
+ }
+
+ return m_user_valobj_sp;
+}
+
+void ValueObjectManager::SetUseDynamic(lldb::DynamicValueType use_dynamic) {
+ if (use_dynamic != m_use_dynamic) {
+ m_use_dynamic = use_dynamic;
+ m_user_valobj_sp.reset();
+ m_stop_id = UINT32_MAX;
+ }
+}
+
+void ValueObjectManager::SetUseSynthetic(bool use_synthetic) {
+ if (m_use_synthetic != use_synthetic) {
+ m_use_synthetic = use_synthetic;
+ m_user_valobj_sp.reset();
+ m_stop_id = UINT32_MAX;
+ }
+}
+
+lldb::TargetSP ValueObjectManager::GetTargetSP() const {
+ if (!m_root_valobj_sp)
+ return m_root_valobj_sp->GetTargetSP();
+ return lldb::TargetSP();
+}
+
+lldb::ProcessSP ValueObjectManager::GetProcessSP() const {
+ if (m_root_valobj_sp)
+ return m_root_valobj_sp->GetProcessSP();
+ return lldb::ProcessSP();
+}
+
+lldb::ThreadSP ValueObjectManager::GetThreadSP() const {
+ if (m_root_valobj_sp)
+ return m_root_valobj_sp->GetThreadSP();
+ return lldb::ThreadSP();
+}
+
+lldb::StackFrameSP ValueObjectManager::GetFrameSP() const {
+ if (m_root_valobj_sp)
+ return m_root_valobj_sp->GetFrameSP();
+ return lldb::StackFrameSP();
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectCast.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectCast.cpp
new file mode 100644
index 000000000000..6ccda8c32915
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectCast.cpp
@@ -0,0 +1,94 @@
+//===-- ValueObjectCast.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectCast.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+namespace lldb_private {
+class ConstString;
+}
+
+using namespace lldb_private;
+
+lldb::ValueObjectSP ValueObjectCast::Create(ValueObject &parent,
+ ConstString name,
+ const CompilerType &cast_type) {
+ ValueObjectCast *cast_valobj_ptr =
+ new ValueObjectCast(parent, name, cast_type);
+ return cast_valobj_ptr->GetSP();
+}
+
+ValueObjectCast::ValueObjectCast(ValueObject &parent, ConstString name,
+ const CompilerType &cast_type)
+ : ValueObject(parent), m_cast_type(cast_type) {
+ SetName(name);
+ // m_value.SetContext (Value::eContextTypeClangType,
+ // cast_type.GetOpaqueQualType());
+ m_value.SetCompilerType(cast_type);
+}
+
+ValueObjectCast::~ValueObjectCast() {}
+
+CompilerType ValueObjectCast::GetCompilerTypeImpl() { return m_cast_type; }
+
+size_t ValueObjectCast::CalculateNumChildren(uint32_t max) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ auto children_count = GetCompilerType().GetNumChildren(
+ true, &exe_ctx);
+ return children_count <= max ? children_count : max;
+}
+
+uint64_t ValueObjectCast::GetByteSize() {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ return m_value.GetValueByteSize(nullptr, &exe_ctx);
+}
+
+lldb::ValueType ValueObjectCast::GetValueType() const {
+ // Let our parent answer global, local, argument, etc...
+ return m_parent->GetValueType();
+}
+
+bool ValueObjectCast::UpdateValue() {
+ SetValueIsValid(false);
+ m_error.Clear();
+
+ if (m_parent->UpdateValueIfNeeded(false)) {
+ Value old_value(m_value);
+ m_update_point.SetUpdated();
+ m_value = m_parent->GetValue();
+ CompilerType compiler_type(GetCompilerType());
+ // m_value.SetContext (Value::eContextTypeClangType, compiler_type);
+ m_value.SetCompilerType(compiler_type);
+ SetAddressTypeOfChildren(m_parent->GetAddressTypeOfChildren());
+ if (!CanProvideValue()) {
+ // this value object represents an aggregate type whose children have
+ // values, but this object does not. So we say we are changed if our
+ // location has changed.
+ SetValueDidChange(m_value.GetValueType() != old_value.GetValueType() ||
+ m_value.GetScalar() != old_value.GetScalar());
+ }
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get());
+ SetValueDidChange(m_parent->GetValueDidChange());
+ return true;
+ }
+
+ // The dynamic value failed to get an error, pass the error along
+ if (m_error.Success() && m_parent->GetError().Fail())
+ m_error = m_parent->GetError();
+ SetValueIsValid(false);
+ return false;
+}
+
+bool ValueObjectCast::IsInScope() { return m_parent->IsInScope(); }
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectChild.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectChild.cpp
new file mode 100644
index 000000000000..01f2e20dd0bc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectChild.cpp
@@ -0,0 +1,228 @@
+//===-- ValueObjectChild.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectChild.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/Flags.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-forward.h"
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <stdio.h>
+#include <string.h>
+
+using namespace lldb_private;
+
+ValueObjectChild::ValueObjectChild(
+ ValueObject &parent, const CompilerType &compiler_type,
+ ConstString name, uint64_t byte_size, int32_t byte_offset,
+ uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset,
+ bool is_base_class, bool is_deref_of_parent,
+ AddressType child_ptr_or_ref_addr_type, uint64_t language_flags)
+ : ValueObject(parent), m_compiler_type(compiler_type),
+ m_byte_size(byte_size), m_byte_offset(byte_offset),
+ m_bitfield_bit_size(bitfield_bit_size),
+ m_bitfield_bit_offset(bitfield_bit_offset),
+ m_is_base_class(is_base_class), m_is_deref_of_parent(is_deref_of_parent),
+ m_can_update_with_invalid_exe_ctx() {
+ m_name = name;
+ SetAddressTypeOfChildren(child_ptr_or_ref_addr_type);
+ SetLanguageFlags(language_flags);
+}
+
+ValueObjectChild::~ValueObjectChild() {}
+
+lldb::ValueType ValueObjectChild::GetValueType() const {
+ return m_parent->GetValueType();
+}
+
+size_t ValueObjectChild::CalculateNumChildren(uint32_t max) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ auto children_count = GetCompilerType().GetNumChildren(true, &exe_ctx);
+ return children_count <= max ? children_count : max;
+}
+
+static void AdjustForBitfieldness(ConstString &name,
+ uint8_t bitfield_bit_size) {
+ if (name && bitfield_bit_size) {
+ const char *compiler_type_name = name.AsCString();
+ if (compiler_type_name) {
+ std::vector<char> bitfield_type_name(strlen(compiler_type_name) + 32, 0);
+ ::snprintf(&bitfield_type_name.front(), bitfield_type_name.size(),
+ "%s:%u", compiler_type_name, bitfield_bit_size);
+ name.SetCString(&bitfield_type_name.front());
+ }
+ }
+}
+
+ConstString ValueObjectChild::GetTypeName() {
+ if (m_type_name.IsEmpty()) {
+ m_type_name = GetCompilerType().GetConstTypeName();
+ AdjustForBitfieldness(m_type_name, m_bitfield_bit_size);
+ }
+ return m_type_name;
+}
+
+ConstString ValueObjectChild::GetQualifiedTypeName() {
+ ConstString qualified_name = GetCompilerType().GetConstTypeName();
+ AdjustForBitfieldness(qualified_name, m_bitfield_bit_size);
+ return qualified_name;
+}
+
+ConstString ValueObjectChild::GetDisplayTypeName() {
+ ConstString display_name = GetCompilerType().GetDisplayTypeName();
+ AdjustForBitfieldness(display_name, m_bitfield_bit_size);
+ return display_name;
+}
+
+LazyBool ValueObjectChild::CanUpdateWithInvalidExecutionContext() {
+ if (m_can_update_with_invalid_exe_ctx.hasValue())
+ return m_can_update_with_invalid_exe_ctx.getValue();
+ if (m_parent) {
+ ValueObject *opinionated_parent =
+ m_parent->FollowParentChain([](ValueObject *valobj) -> bool {
+ return (valobj->CanUpdateWithInvalidExecutionContext() ==
+ eLazyBoolCalculate);
+ });
+ if (opinionated_parent)
+ return (m_can_update_with_invalid_exe_ctx =
+ opinionated_parent->CanUpdateWithInvalidExecutionContext())
+ .getValue();
+ }
+ return (m_can_update_with_invalid_exe_ctx =
+ this->ValueObject::CanUpdateWithInvalidExecutionContext())
+ .getValue();
+}
+
+bool ValueObjectChild::UpdateValue() {
+ m_error.Clear();
+ SetValueIsValid(false);
+ ValueObject *parent = m_parent;
+ if (parent) {
+ if (parent->UpdateValueIfNeeded(false)) {
+ m_value.SetCompilerType(GetCompilerType());
+
+ CompilerType parent_type(parent->GetCompilerType());
+ // Copy the parent scalar value and the scalar value type
+ m_value.GetScalar() = parent->GetValue().GetScalar();
+ Value::ValueType value_type = parent->GetValue().GetValueType();
+ m_value.SetValueType(value_type);
+
+ Flags parent_type_flags(parent_type.GetTypeInfo());
+ const bool is_instance_ptr_base =
+ ((m_is_base_class) &&
+ (parent_type_flags.AnySet(lldb::eTypeInstanceIsPointer)));
+
+ if (parent->GetCompilerType().ShouldTreatScalarValueAsAddress()) {
+ lldb::addr_t addr = parent->GetPointerValue();
+ m_value.GetScalar() = addr;
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ m_error.SetErrorString("parent address is invalid.");
+ } else if (addr == 0) {
+ m_error.SetErrorString("parent is NULL");
+ } else {
+ m_value.GetScalar() += m_byte_offset;
+ AddressType addr_type = parent->GetAddressTypeOfChildren();
+
+ switch (addr_type) {
+ case eAddressTypeFile: {
+ lldb::ProcessSP process_sp(GetProcessSP());
+ if (process_sp && process_sp->IsAlive())
+ m_value.SetValueType(Value::eValueTypeLoadAddress);
+ else
+ m_value.SetValueType(Value::eValueTypeFileAddress);
+ } break;
+ case eAddressTypeLoad:
+ m_value.SetValueType(is_instance_ptr_base
+ ? Value::eValueTypeScalar
+ : Value::eValueTypeLoadAddress);
+ break;
+ case eAddressTypeHost:
+ m_value.SetValueType(Value::eValueTypeHostAddress);
+ break;
+ case eAddressTypeInvalid:
+ // TODO: does this make sense?
+ m_value.SetValueType(Value::eValueTypeScalar);
+ break;
+ }
+ }
+ } else {
+ switch (value_type) {
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeHostAddress: {
+ lldb::addr_t addr =
+ m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ if (addr == LLDB_INVALID_ADDRESS) {
+ m_error.SetErrorString("parent address is invalid.");
+ } else if (addr == 0) {
+ m_error.SetErrorString("parent is NULL");
+ } else {
+ // Set this object's scalar value to the address of its value by
+ // adding its byte offset to the parent address
+ m_value.GetScalar() += GetByteOffset();
+ }
+ } break;
+
+ case Value::eValueTypeScalar:
+ // try to extract the child value from the parent's scalar value
+ {
+ Scalar scalar(m_value.GetScalar());
+ if (m_bitfield_bit_size)
+ scalar.ExtractBitfield(m_bitfield_bit_size,
+ m_bitfield_bit_offset);
+ else
+ scalar.ExtractBitfield(8 * m_byte_size, 8 * m_byte_offset);
+ m_value.GetScalar() = scalar;
+ }
+ break;
+ default:
+ m_error.SetErrorString("parent has invalid value.");
+ break;
+ }
+ }
+
+ if (m_error.Success()) {
+ const bool thread_and_frame_only_if_stopped = true;
+ ExecutionContext exe_ctx(
+ GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped));
+ if (GetCompilerType().GetTypeInfo() & lldb::eTypeHasValue) {
+ Value &value = is_instance_ptr_base ? m_parent->GetValue() : m_value;
+ m_error =
+ value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get());
+ } else {
+ m_error.Clear(); // No value so nothing to read...
+ }
+ }
+
+ } else {
+ m_error.SetErrorStringWithFormat("parent failed to evaluate: %s",
+ parent->GetError().AsCString());
+ }
+ } else {
+ m_error.SetErrorString("ValueObjectChild has a NULL parent ValueObject.");
+ }
+
+ return m_error.Success();
+}
+
+bool ValueObjectChild::IsInScope() {
+ ValueObject *root(GetRoot());
+ if (root)
+ return root->IsInScope();
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectConstResult.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResult.cpp
new file mode 100644
index 000000000000..a1b2cac96874
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResult.cpp
@@ -0,0 +1,296 @@
+//===-- ValueObjectConstResult.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectConstResult.h"
+
+#include "lldb/Core/ValueObjectDynamicValue.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Scalar.h"
+
+namespace lldb_private {
+class Module;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ ByteOrder byte_order,
+ uint32_t addr_byte_size,
+ lldb::addr_t address) {
+ return (new ValueObjectConstResult(exe_scope, byte_order, addr_byte_size,
+ address))
+ ->GetSP();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(ExecutionContextScope *exe_scope,
+ ByteOrder byte_order,
+ uint32_t addr_byte_size,
+ lldb::addr_t address)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0),
+ m_impl(this, address) {
+ SetIsConstant();
+ SetValueIsValid(true);
+ m_data.SetByteOrder(byte_order);
+ m_data.SetAddressByteSize(addr_byte_size);
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+}
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ const CompilerType &compiler_type,
+ ConstString name,
+ const DataExtractor &data,
+ lldb::addr_t address) {
+ return (new ValueObjectConstResult(exe_scope, compiler_type, name, data,
+ address))
+ ->GetSP();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(
+ ExecutionContextScope *exe_scope, const CompilerType &compiler_type,
+ ConstString name, const DataExtractor &data, lldb::addr_t address)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0),
+ m_impl(this, address) {
+ m_data = data;
+
+ if (!m_data.GetSharedDataBuffer()) {
+ DataBufferSP shared_data_buffer(
+ new DataBufferHeap(data.GetDataStart(), data.GetByteSize()));
+ m_data.SetData(shared_data_buffer);
+ }
+
+ m_value.GetScalar() = (uintptr_t)m_data.GetDataStart();
+ m_value.SetValueType(Value::eValueTypeHostAddress);
+ m_value.SetCompilerType(compiler_type);
+ m_name = name;
+ SetIsConstant();
+ SetValueIsValid(true);
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+}
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ const CompilerType &compiler_type,
+ ConstString name,
+ const lldb::DataBufferSP &data_sp,
+ lldb::ByteOrder data_byte_order,
+ uint32_t data_addr_size,
+ lldb::addr_t address) {
+ return (new ValueObjectConstResult(exe_scope, compiler_type, name, data_sp,
+ data_byte_order, data_addr_size, address))
+ ->GetSP();
+}
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ Value &value,
+ ConstString name,
+ Module *module) {
+ return (new ValueObjectConstResult(exe_scope, value, name, module))->GetSP();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(
+ ExecutionContextScope *exe_scope, const CompilerType &compiler_type,
+ ConstString name, const lldb::DataBufferSP &data_sp,
+ lldb::ByteOrder data_byte_order, uint32_t data_addr_size,
+ lldb::addr_t address)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0),
+ m_impl(this, address) {
+ m_data.SetByteOrder(data_byte_order);
+ m_data.SetAddressByteSize(data_addr_size);
+ m_data.SetData(data_sp);
+ m_value.GetScalar() = (uintptr_t)data_sp->GetBytes();
+ m_value.SetValueType(Value::eValueTypeHostAddress);
+ // m_value.SetContext(Value::eContextTypeClangType, compiler_type);
+ m_value.SetCompilerType(compiler_type);
+ m_name = name;
+ SetIsConstant();
+ SetValueIsValid(true);
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+}
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ const CompilerType &compiler_type,
+ ConstString name,
+ lldb::addr_t address,
+ AddressType address_type,
+ uint32_t addr_byte_size) {
+ return (new ValueObjectConstResult(exe_scope, compiler_type, name, address,
+ address_type, addr_byte_size))
+ ->GetSP();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(
+ ExecutionContextScope *exe_scope, const CompilerType &compiler_type,
+ ConstString name, lldb::addr_t address, AddressType address_type,
+ uint32_t addr_byte_size)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0),
+ m_impl(this, address) {
+ m_value.GetScalar() = address;
+ m_data.SetAddressByteSize(addr_byte_size);
+ m_value.GetScalar().GetData(m_data, addr_byte_size);
+ // m_value.SetValueType(Value::eValueTypeHostAddress);
+ switch (address_type) {
+ case eAddressTypeInvalid:
+ m_value.SetValueType(Value::eValueTypeScalar);
+ break;
+ case eAddressTypeFile:
+ m_value.SetValueType(Value::eValueTypeFileAddress);
+ break;
+ case eAddressTypeLoad:
+ m_value.SetValueType(Value::eValueTypeLoadAddress);
+ break;
+ case eAddressTypeHost:
+ m_value.SetValueType(Value::eValueTypeHostAddress);
+ break;
+ }
+ // m_value.SetContext(Value::eContextTypeClangType, compiler_type);
+ m_value.SetCompilerType(compiler_type);
+ m_name = name;
+ SetIsConstant();
+ SetValueIsValid(true);
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+}
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ const Status &error) {
+ return (new ValueObjectConstResult(exe_scope, error))->GetSP();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(ExecutionContextScope *exe_scope,
+ const Status &error)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0), m_impl(this) {
+ m_error = error;
+ SetIsConstant();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(ExecutionContextScope *exe_scope,
+ const Value &value,
+ ConstString name,
+ Module *module)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0), m_impl(this) {
+ m_value = value;
+ m_name = name;
+ ExecutionContext exe_ctx;
+ exe_scope->CalculateExecutionContext(exe_ctx);
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, 0, module);
+}
+
+ValueObjectConstResult::~ValueObjectConstResult() {}
+
+CompilerType ValueObjectConstResult::GetCompilerTypeImpl() {
+ return m_value.GetCompilerType();
+}
+
+lldb::ValueType ValueObjectConstResult::GetValueType() const {
+ return eValueTypeConstResult;
+}
+
+uint64_t ValueObjectConstResult::GetByteSize() {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ if (m_byte_size == 0) {
+ if (auto size =
+ GetCompilerType().GetByteSize(exe_ctx.GetBestExecutionContextScope()))
+ SetByteSize(*size);
+ }
+ return m_byte_size;
+}
+
+void ValueObjectConstResult::SetByteSize(size_t size) { m_byte_size = size; }
+
+size_t ValueObjectConstResult::CalculateNumChildren(uint32_t max) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ auto children_count = GetCompilerType().GetNumChildren(true, &exe_ctx);
+ return children_count <= max ? children_count : max;
+}
+
+ConstString ValueObjectConstResult::GetTypeName() {
+ if (m_type_name.IsEmpty())
+ m_type_name = GetCompilerType().GetConstTypeName();
+ return m_type_name;
+}
+
+ConstString ValueObjectConstResult::GetDisplayTypeName() {
+ return GetCompilerType().GetDisplayTypeName();
+}
+
+bool ValueObjectConstResult::UpdateValue() {
+ // Const value is always valid
+ SetValueIsValid(true);
+ return true;
+}
+
+bool ValueObjectConstResult::IsInScope() {
+ // A const result value is always in scope since it serializes all
+ // information needed to contain the constant value.
+ return true;
+}
+
+lldb::ValueObjectSP ValueObjectConstResult::Dereference(Status &error) {
+ return m_impl.Dereference(error);
+}
+
+lldb::ValueObjectSP ValueObjectConstResult::GetSyntheticChildAtOffset(
+ uint32_t offset, const CompilerType &type, bool can_create,
+ ConstString name_const_str) {
+ return m_impl.GetSyntheticChildAtOffset(offset, type, can_create,
+ name_const_str);
+}
+
+lldb::ValueObjectSP ValueObjectConstResult::AddressOf(Status &error) {
+ return m_impl.AddressOf(error);
+}
+
+lldb::addr_t ValueObjectConstResult::GetAddressOf(bool scalar_is_load_address,
+ AddressType *address_type) {
+ return m_impl.GetAddressOf(scalar_is_load_address, address_type);
+}
+
+ValueObject *ValueObjectConstResult::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ return m_impl.CreateChildAtIndex(idx, synthetic_array_member,
+ synthetic_index);
+}
+
+size_t ValueObjectConstResult::GetPointeeData(DataExtractor &data,
+ uint32_t item_idx,
+ uint32_t item_count) {
+ return m_impl.GetPointeeData(data, item_idx, item_count);
+}
+
+lldb::ValueObjectSP
+ValueObjectConstResult::GetDynamicValue(lldb::DynamicValueType use_dynamic) {
+ // Always recalculate dynamic values for const results as the memory that
+ // they might point to might have changed at any time.
+ if (use_dynamic != eNoDynamicValues) {
+ if (!IsDynamic()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsPossibleDynamicValue(*this))
+ m_dynamic_value = new ValueObjectDynamicValue(*this, use_dynamic);
+ }
+ if (m_dynamic_value)
+ return m_dynamic_value->GetSP();
+ }
+ return ValueObjectSP();
+}
+
+lldb::ValueObjectSP
+ValueObjectConstResult::Cast(const CompilerType &compiler_type) {
+ return m_impl.Cast(compiler_type);
+}
+
+lldb::LanguageType ValueObjectConstResult::GetPreferredDisplayLanguage() {
+ if (m_preferred_display_language != lldb::eLanguageTypeUnknown)
+ return m_preferred_display_language;
+ return GetCompilerTypeImpl().GetMinimumLanguage();
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultCast.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultCast.cpp
new file mode 100644
index 000000000000..b47e699e30f1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultCast.cpp
@@ -0,0 +1,62 @@
+//===-- ValueObjectConstResultCast.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectConstResultCast.h"
+
+namespace lldb_private {
+class DataExtractor;
+}
+namespace lldb_private {
+class Status;
+}
+namespace lldb_private {
+class ValueObject;
+}
+
+using namespace lldb_private;
+
+ValueObjectConstResultCast::ValueObjectConstResultCast(
+ ValueObject &parent, ConstString name, const CompilerType &cast_type,
+ lldb::addr_t live_address)
+ : ValueObjectCast(parent, name, cast_type), m_impl(this, live_address) {
+ m_name = name;
+}
+
+ValueObjectConstResultCast::~ValueObjectConstResultCast() {}
+
+lldb::ValueObjectSP ValueObjectConstResultCast::Dereference(Status &error) {
+ return m_impl.Dereference(error);
+}
+
+lldb::ValueObjectSP ValueObjectConstResultCast::GetSyntheticChildAtOffset(
+ uint32_t offset, const CompilerType &type, bool can_create,
+ ConstString name_const_str) {
+ return m_impl.GetSyntheticChildAtOffset(offset, type, can_create,
+ name_const_str);
+}
+
+lldb::ValueObjectSP ValueObjectConstResultCast::AddressOf(Status &error) {
+ return m_impl.AddressOf(error);
+}
+
+ValueObject *ValueObjectConstResultCast::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ return m_impl.CreateChildAtIndex(idx, synthetic_array_member,
+ synthetic_index);
+}
+
+size_t ValueObjectConstResultCast::GetPointeeData(DataExtractor &data,
+ uint32_t item_idx,
+ uint32_t item_count) {
+ return m_impl.GetPointeeData(data, item_idx, item_count);
+}
+
+lldb::ValueObjectSP
+ValueObjectConstResultCast::Cast(const CompilerType &compiler_type) {
+ return m_impl.Cast(compiler_type);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultChild.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultChild.cpp
new file mode 100644
index 000000000000..4e0b303b69d5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultChild.cpp
@@ -0,0 +1,74 @@
+//===-- ValueObjectConstResultChild.cpp --------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectConstResultChild.h"
+
+#include "lldb/lldb-private-enumerations.h"
+namespace lldb_private {
+class DataExtractor;
+}
+namespace lldb_private {
+class Status;
+}
+namespace lldb_private {
+class ValueObject;
+}
+
+using namespace lldb_private;
+
+ValueObjectConstResultChild::ValueObjectConstResultChild(
+ ValueObject &parent, const CompilerType &compiler_type,
+ ConstString name, uint32_t byte_size, int32_t byte_offset,
+ uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset,
+ bool is_base_class, bool is_deref_of_parent, lldb::addr_t live_address,
+ uint64_t language_flags)
+ : ValueObjectChild(parent, compiler_type, name, byte_size, byte_offset,
+ bitfield_bit_size, bitfield_bit_offset, is_base_class,
+ is_deref_of_parent, eAddressTypeLoad, language_flags),
+ m_impl(this, live_address) {
+ m_name = name;
+}
+
+ValueObjectConstResultChild::~ValueObjectConstResultChild() {}
+
+lldb::ValueObjectSP ValueObjectConstResultChild::Dereference(Status &error) {
+ return m_impl.Dereference(error);
+}
+
+lldb::ValueObjectSP ValueObjectConstResultChild::GetSyntheticChildAtOffset(
+ uint32_t offset, const CompilerType &type, bool can_create,
+ ConstString name_const_str) {
+ return m_impl.GetSyntheticChildAtOffset(offset, type, can_create,
+ name_const_str);
+}
+
+lldb::ValueObjectSP ValueObjectConstResultChild::AddressOf(Status &error) {
+ return m_impl.AddressOf(error);
+}
+
+lldb::addr_t ValueObjectConstResultChild::GetAddressOf(
+ bool scalar_is_load_address, AddressType* address_type) {
+ return m_impl.GetAddressOf(scalar_is_load_address, address_type);
+}
+
+ValueObject *ValueObjectConstResultChild::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ return m_impl.CreateChildAtIndex(idx, synthetic_array_member,
+ synthetic_index);
+}
+
+size_t ValueObjectConstResultChild::GetPointeeData(DataExtractor &data,
+ uint32_t item_idx,
+ uint32_t item_count) {
+ return m_impl.GetPointeeData(data, item_idx, item_count);
+}
+
+lldb::ValueObjectSP
+ValueObjectConstResultChild::Cast(const CompilerType &compiler_type) {
+ return m_impl.Cast(compiler_type);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultImpl.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultImpl.cpp
new file mode 100644
index 000000000000..de51735736b9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultImpl.cpp
@@ -0,0 +1,180 @@
+//===-- ValueObjectConstResultImpl.cpp ---------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectConstResultImpl.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectConstResultCast.h"
+#include "lldb/Core/ValueObjectConstResultChild.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/SharingPtr.h"
+
+#include <string>
+
+namespace lldb_private {
+class DataExtractor;
+}
+namespace lldb_private {
+class Status;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+ValueObjectConstResultImpl::ValueObjectConstResultImpl(
+ ValueObject *valobj, lldb::addr_t live_address)
+ : m_impl_backend(valobj), m_live_address(live_address),
+ m_live_address_type(eAddressTypeLoad), m_load_addr_backend(),
+ m_address_of_backend() {}
+
+lldb::ValueObjectSP ValueObjectConstResultImpl::Dereference(Status &error) {
+ if (m_impl_backend == nullptr)
+ return lldb::ValueObjectSP();
+
+ return m_impl_backend->ValueObject::Dereference(error);
+}
+
+ValueObject *ValueObjectConstResultImpl::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ if (m_impl_backend == nullptr)
+ return nullptr;
+
+ m_impl_backend->UpdateValueIfNeeded(false);
+
+ ValueObjectConstResultChild *valobj = nullptr;
+
+ bool omit_empty_base_classes = true;
+ bool ignore_array_bounds = synthetic_array_member;
+ std::string child_name_str;
+ uint32_t child_byte_size = 0;
+ int32_t child_byte_offset = 0;
+ uint32_t child_bitfield_bit_size = 0;
+ uint32_t child_bitfield_bit_offset = 0;
+ bool child_is_base_class = false;
+ bool child_is_deref_of_parent = false;
+ uint64_t language_flags;
+
+ const bool transparent_pointers = !synthetic_array_member;
+ CompilerType compiler_type = m_impl_backend->GetCompilerType();
+ CompilerType child_compiler_type;
+
+ ExecutionContext exe_ctx(m_impl_backend->GetExecutionContextRef());
+
+ child_compiler_type = compiler_type.GetChildCompilerTypeAtIndex(
+ &exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, m_impl_backend, language_flags);
+
+ // One might think we should check that the size of the children
+ // is always strictly positive, hence we could avoid creating a
+ // ValueObject if that's not the case, but it turns out there
+ // are languages out there which allow zero-size types with
+ // children (e.g. Swift).
+ if (child_compiler_type) {
+ if (synthetic_index)
+ child_byte_offset += child_byte_size * synthetic_index;
+
+ ConstString child_name;
+ if (!child_name_str.empty())
+ child_name.SetCString(child_name_str.c_str());
+
+ valobj = new ValueObjectConstResultChild(
+ *m_impl_backend, child_compiler_type, child_name, child_byte_size,
+ child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, child_is_deref_of_parent,
+ m_live_address == LLDB_INVALID_ADDRESS
+ ? m_live_address
+ : m_live_address + child_byte_offset,
+ language_flags);
+ }
+
+ return valobj;
+}
+
+lldb::ValueObjectSP ValueObjectConstResultImpl::GetSyntheticChildAtOffset(
+ uint32_t offset, const CompilerType &type, bool can_create,
+ ConstString name_const_str) {
+ if (m_impl_backend == nullptr)
+ return lldb::ValueObjectSP();
+
+ return m_impl_backend->ValueObject::GetSyntheticChildAtOffset(
+ offset, type, can_create, name_const_str);
+}
+
+lldb::ValueObjectSP ValueObjectConstResultImpl::AddressOf(Status &error) {
+ if (m_address_of_backend.get() != nullptr)
+ return m_address_of_backend;
+
+ if (m_impl_backend == nullptr)
+ return lldb::ValueObjectSP();
+ if (m_live_address != LLDB_INVALID_ADDRESS) {
+ CompilerType compiler_type(m_impl_backend->GetCompilerType());
+
+ lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(
+ &m_live_address, sizeof(lldb::addr_t)));
+
+ std::string new_name("&");
+ new_name.append(m_impl_backend->GetName().AsCString(""));
+ ExecutionContext exe_ctx(m_impl_backend->GetExecutionContextRef());
+ m_address_of_backend = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), compiler_type.GetPointerType(),
+ ConstString(new_name.c_str()), buffer, endian::InlHostByteOrder(),
+ exe_ctx.GetAddressByteSize());
+
+ m_address_of_backend->GetValue().SetValueType(Value::eValueTypeScalar);
+ m_address_of_backend->GetValue().GetScalar() = m_live_address;
+
+ return m_address_of_backend;
+ } else
+ return m_impl_backend->ValueObject::AddressOf(error);
+}
+
+lldb::ValueObjectSP
+ValueObjectConstResultImpl::Cast(const CompilerType &compiler_type) {
+ if (m_impl_backend == nullptr)
+ return lldb::ValueObjectSP();
+
+ ValueObjectConstResultCast *result_cast =
+ new ValueObjectConstResultCast(*m_impl_backend, m_impl_backend->GetName(),
+ compiler_type, m_live_address);
+ return result_cast->GetSP();
+}
+
+lldb::addr_t
+ValueObjectConstResultImpl::GetAddressOf(bool scalar_is_load_address,
+ AddressType *address_type) {
+
+ if (m_impl_backend == nullptr)
+ return 0;
+
+ if (m_live_address == LLDB_INVALID_ADDRESS) {
+ return m_impl_backend->ValueObject::GetAddressOf(scalar_is_load_address,
+ address_type);
+ }
+
+ if (address_type)
+ *address_type = m_live_address_type;
+
+ return m_live_address;
+}
+
+size_t ValueObjectConstResultImpl::GetPointeeData(DataExtractor &data,
+ uint32_t item_idx,
+ uint32_t item_count) {
+ if (m_impl_backend == nullptr)
+ return 0;
+ return m_impl_backend->ValueObject::GetPointeeData(data, item_idx,
+ item_count);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectDynamicValue.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectDynamicValue.cpp
new file mode 100644
index 000000000000..90b46d1f170d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectDynamicValue.cpp
@@ -0,0 +1,389 @@
+//===-- ValueObjectDynamicValue.cpp ------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectDynamicValue.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-types.h"
+
+#include <string.h>
+namespace lldb_private {
+class Declaration;
+}
+
+using namespace lldb_private;
+
+ValueObjectDynamicValue::ValueObjectDynamicValue(
+ ValueObject &parent, lldb::DynamicValueType use_dynamic)
+ : ValueObject(parent), m_address(), m_dynamic_type_info(),
+ m_use_dynamic(use_dynamic) {
+ SetName(parent.GetName());
+}
+
+ValueObjectDynamicValue::~ValueObjectDynamicValue() {
+ m_owning_valobj_sp.reset();
+}
+
+CompilerType ValueObjectDynamicValue::GetCompilerTypeImpl() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success) {
+ if (m_dynamic_type_info.HasType())
+ return m_value.GetCompilerType();
+ else
+ return m_parent->GetCompilerType();
+ }
+ return m_parent->GetCompilerType();
+}
+
+ConstString ValueObjectDynamicValue::GetTypeName() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success) {
+ if (m_dynamic_type_info.HasName())
+ return m_dynamic_type_info.GetName();
+ }
+ return m_parent->GetTypeName();
+}
+
+TypeImpl ValueObjectDynamicValue::GetTypeImpl() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success && m_type_impl.IsValid()) {
+ return m_type_impl;
+ }
+ return m_parent->GetTypeImpl();
+}
+
+ConstString ValueObjectDynamicValue::GetQualifiedTypeName() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success) {
+ if (m_dynamic_type_info.HasName())
+ return m_dynamic_type_info.GetName();
+ }
+ return m_parent->GetQualifiedTypeName();
+}
+
+ConstString ValueObjectDynamicValue::GetDisplayTypeName() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success) {
+ if (m_dynamic_type_info.HasType())
+ return GetCompilerType().GetDisplayTypeName();
+ if (m_dynamic_type_info.HasName())
+ return m_dynamic_type_info.GetName();
+ }
+ return m_parent->GetDisplayTypeName();
+}
+
+size_t ValueObjectDynamicValue::CalculateNumChildren(uint32_t max) {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success && m_dynamic_type_info.HasType()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ auto children_count = GetCompilerType().GetNumChildren(true, &exe_ctx);
+ return children_count <= max ? children_count : max;
+ } else
+ return m_parent->GetNumChildren(max);
+}
+
+uint64_t ValueObjectDynamicValue::GetByteSize() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success && m_dynamic_type_info.HasType()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ return m_value.GetValueByteSize(nullptr, &exe_ctx);
+ } else
+ return m_parent->GetByteSize();
+}
+
+lldb::ValueType ValueObjectDynamicValue::GetValueType() const {
+ return m_parent->GetValueType();
+}
+
+bool ValueObjectDynamicValue::UpdateValue() {
+ SetValueIsValid(false);
+ m_error.Clear();
+
+ if (!m_parent->UpdateValueIfNeeded(false)) {
+ // The dynamic value failed to get an error, pass the error along
+ if (m_error.Success() && m_parent->GetError().Fail())
+ m_error = m_parent->GetError();
+ return false;
+ }
+
+ // Setting our type_sp to NULL will route everything back through our parent
+ // which is equivalent to not using dynamic values.
+ if (m_use_dynamic == lldb::eNoDynamicValues) {
+ m_dynamic_type_info.Clear();
+ return true;
+ }
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ m_data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
+ }
+
+ // First make sure our Type and/or Address haven't changed:
+ Process *process = exe_ctx.GetProcessPtr();
+ if (!process)
+ return false;
+
+ TypeAndOrName class_type_or_name;
+ Address dynamic_address;
+ bool found_dynamic_type = false;
+ Value::ValueType value_type;
+
+ LanguageRuntime *runtime = nullptr;
+
+ lldb::LanguageType known_type = m_parent->GetObjectRuntimeLanguage();
+ if (known_type != lldb::eLanguageTypeUnknown &&
+ known_type != lldb::eLanguageTypeC) {
+ runtime = process->GetLanguageRuntime(known_type);
+ if (runtime)
+ found_dynamic_type = runtime->GetDynamicTypeAndAddress(
+ *m_parent, m_use_dynamic, class_type_or_name, dynamic_address,
+ value_type);
+ } else {
+ runtime = process->GetLanguageRuntime(lldb::eLanguageTypeC_plus_plus);
+ if (runtime)
+ found_dynamic_type = runtime->GetDynamicTypeAndAddress(
+ *m_parent, m_use_dynamic, class_type_or_name, dynamic_address,
+ value_type);
+
+ if (!found_dynamic_type) {
+ runtime = process->GetLanguageRuntime(lldb::eLanguageTypeObjC);
+ if (runtime)
+ found_dynamic_type = runtime->GetDynamicTypeAndAddress(
+ *m_parent, m_use_dynamic, class_type_or_name, dynamic_address,
+ value_type);
+ }
+ }
+
+ // Getting the dynamic value may have run the program a bit, and so marked us
+ // as needing updating, but we really don't...
+
+ m_update_point.SetUpdated();
+
+ if (runtime && found_dynamic_type) {
+ if (class_type_or_name.HasType()) {
+ m_type_impl =
+ TypeImpl(m_parent->GetCompilerType(),
+ runtime->FixUpDynamicType(class_type_or_name, *m_parent)
+ .GetCompilerType());
+ } else {
+ m_type_impl.Clear();
+ }
+ } else {
+ m_type_impl.Clear();
+ }
+
+ // If we don't have a dynamic type, then make ourselves just a echo of our
+ // parent. Or we could return false, and make ourselves an echo of our
+ // parent?
+ if (!found_dynamic_type) {
+ if (m_dynamic_type_info)
+ SetValueDidChange(true);
+ ClearDynamicTypeInformation();
+ m_dynamic_type_info.Clear();
+ m_value = m_parent->GetValue();
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get());
+ return m_error.Success();
+ }
+
+ Value old_value(m_value);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
+
+ bool has_changed_type = false;
+
+ if (!m_dynamic_type_info) {
+ m_dynamic_type_info = class_type_or_name;
+ has_changed_type = true;
+ } else if (class_type_or_name != m_dynamic_type_info) {
+ // We are another type, we need to tear down our children...
+ m_dynamic_type_info = class_type_or_name;
+ SetValueDidChange(true);
+ has_changed_type = true;
+ }
+
+ if (has_changed_type)
+ ClearDynamicTypeInformation();
+
+ if (!m_address.IsValid() || m_address != dynamic_address) {
+ if (m_address.IsValid())
+ SetValueDidChange(true);
+
+ // We've moved, so we should be fine...
+ m_address = dynamic_address;
+ lldb::TargetSP target_sp(GetTargetSP());
+ lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get());
+ m_value.GetScalar() = load_address;
+ }
+
+ if (runtime)
+ m_dynamic_type_info =
+ runtime->FixUpDynamicType(m_dynamic_type_info, *m_parent);
+
+ // m_value.SetContext (Value::eContextTypeClangType, corrected_type);
+ m_value.SetCompilerType(m_dynamic_type_info.GetCompilerType());
+
+ m_value.SetValueType(value_type);
+
+ if (has_changed_type && log)
+ log->Printf("[%s %p] has a new dynamic type %s", GetName().GetCString(),
+ static_cast<void *>(this), GetTypeName().GetCString());
+
+ if (m_address.IsValid() && m_dynamic_type_info) {
+ // The variable value is in the Scalar value inside the m_value. We can
+ // point our m_data right to it.
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get());
+ if (m_error.Success()) {
+ if (!CanProvideValue()) {
+ // this value object represents an aggregate type whose children have
+ // values, but this object does not. So we say we are changed if our
+ // location has changed.
+ SetValueDidChange(m_value.GetValueType() != old_value.GetValueType() ||
+ m_value.GetScalar() != old_value.GetScalar());
+ }
+
+ SetValueIsValid(true);
+ return true;
+ }
+ }
+
+ // We get here if we've failed above...
+ SetValueIsValid(false);
+ return false;
+}
+
+bool ValueObjectDynamicValue::IsInScope() { return m_parent->IsInScope(); }
+
+bool ValueObjectDynamicValue::SetValueFromCString(const char *value_str,
+ Status &error) {
+ if (!UpdateValueIfNeeded(false)) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ uint64_t my_value = GetValueAsUnsigned(UINT64_MAX);
+ uint64_t parent_value = m_parent->GetValueAsUnsigned(UINT64_MAX);
+
+ if (my_value == UINT64_MAX || parent_value == UINT64_MAX) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ // if we are at an offset from our parent, in order to set ourselves
+ // correctly we would need to change the new value so that it refers to the
+ // correct dynamic type. we choose not to deal with that - if anything more
+ // than a value overwrite is required, you should be using the expression
+ // parser instead of the value editing facility
+ if (my_value != parent_value) {
+ // but NULL'ing out a value should always be allowed
+ if (strcmp(value_str, "0")) {
+ error.SetErrorString(
+ "unable to modify dynamic value, use 'expression' command");
+ return false;
+ }
+ }
+
+ bool ret_val = m_parent->SetValueFromCString(value_str, error);
+ SetNeedsUpdate();
+ return ret_val;
+}
+
+bool ValueObjectDynamicValue::SetData(DataExtractor &data, Status &error) {
+ if (!UpdateValueIfNeeded(false)) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ uint64_t my_value = GetValueAsUnsigned(UINT64_MAX);
+ uint64_t parent_value = m_parent->GetValueAsUnsigned(UINT64_MAX);
+
+ if (my_value == UINT64_MAX || parent_value == UINT64_MAX) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ // if we are at an offset from our parent, in order to set ourselves
+ // correctly we would need to change the new value so that it refers to the
+ // correct dynamic type. we choose not to deal with that - if anything more
+ // than a value overwrite is required, you should be using the expression
+ // parser instead of the value editing facility
+ if (my_value != parent_value) {
+ // but NULL'ing out a value should always be allowed
+ lldb::offset_t offset = 0;
+
+ if (data.GetPointer(&offset) != 0) {
+ error.SetErrorString(
+ "unable to modify dynamic value, use 'expression' command");
+ return false;
+ }
+ }
+
+ bool ret_val = m_parent->SetData(data, error);
+ SetNeedsUpdate();
+ return ret_val;
+}
+
+void ValueObjectDynamicValue::SetPreferredDisplayLanguage(
+ lldb::LanguageType lang) {
+ this->ValueObject::SetPreferredDisplayLanguage(lang);
+ if (m_parent)
+ m_parent->SetPreferredDisplayLanguage(lang);
+}
+
+lldb::LanguageType ValueObjectDynamicValue::GetPreferredDisplayLanguage() {
+ if (m_preferred_display_language == lldb::eLanguageTypeUnknown) {
+ if (m_parent)
+ return m_parent->GetPreferredDisplayLanguage();
+ return lldb::eLanguageTypeUnknown;
+ } else
+ return m_preferred_display_language;
+}
+
+bool ValueObjectDynamicValue::IsSyntheticChildrenGenerated() {
+ if (m_parent)
+ return m_parent->IsSyntheticChildrenGenerated();
+ return false;
+}
+
+void ValueObjectDynamicValue::SetSyntheticChildrenGenerated(bool b) {
+ if (m_parent)
+ m_parent->SetSyntheticChildrenGenerated(b);
+ this->ValueObject::SetSyntheticChildrenGenerated(b);
+}
+
+bool ValueObjectDynamicValue::GetDeclaration(Declaration &decl) {
+ if (m_parent)
+ return m_parent->GetDeclaration(decl);
+
+ return ValueObject::GetDeclaration(decl);
+}
+
+uint64_t ValueObjectDynamicValue::GetLanguageFlags() {
+ if (m_parent)
+ return m_parent->GetLanguageFlags();
+ return this->ValueObject::GetLanguageFlags();
+}
+
+void ValueObjectDynamicValue::SetLanguageFlags(uint64_t flags) {
+ if (m_parent)
+ m_parent->SetLanguageFlags(flags);
+ else
+ this->ValueObject::SetLanguageFlags(flags);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectList.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectList.cpp
new file mode 100644
index 000000000000..358a1b14517b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectList.cpp
@@ -0,0 +1,110 @@
+//===-- ValueObjectList.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectList.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/SharingPtr.h"
+
+#include <utility>
+
+using namespace lldb;
+using namespace lldb_private;
+
+const ValueObjectList &ValueObjectList::operator=(const ValueObjectList &rhs) {
+ if (this != &rhs)
+ m_value_objects = rhs.m_value_objects;
+ return *this;
+}
+
+void ValueObjectList::Append(const ValueObjectSP &val_obj_sp) {
+ m_value_objects.push_back(val_obj_sp);
+}
+
+void ValueObjectList::Append(const ValueObjectList &valobj_list) {
+ std::copy(valobj_list.m_value_objects.begin(), // source begin
+ valobj_list.m_value_objects.end(), // source end
+ back_inserter(m_value_objects)); // destination
+}
+
+size_t ValueObjectList::GetSize() const { return m_value_objects.size(); }
+
+void ValueObjectList::Resize(size_t size) { m_value_objects.resize(size); }
+
+lldb::ValueObjectSP ValueObjectList::GetValueObjectAtIndex(size_t idx) {
+ lldb::ValueObjectSP valobj_sp;
+ if (idx < m_value_objects.size())
+ valobj_sp = m_value_objects[idx];
+ return valobj_sp;
+}
+
+lldb::ValueObjectSP ValueObjectList::RemoveValueObjectAtIndex(size_t idx) {
+ lldb::ValueObjectSP valobj_sp;
+ if (idx < m_value_objects.size()) {
+ valobj_sp = m_value_objects[idx];
+ m_value_objects.erase(m_value_objects.begin() + idx);
+ }
+ return valobj_sp;
+}
+
+void ValueObjectList::SetValueObjectAtIndex(size_t idx,
+ const ValueObjectSP &valobj_sp) {
+ if (idx >= m_value_objects.size())
+ m_value_objects.resize(idx + 1);
+ m_value_objects[idx] = valobj_sp;
+}
+
+ValueObjectSP ValueObjectList::FindValueObjectByValueName(const char *name) {
+ ConstString name_const_str(name);
+ ValueObjectSP val_obj_sp;
+ collection::iterator pos, end = m_value_objects.end();
+ for (pos = m_value_objects.begin(); pos != end; ++pos) {
+ ValueObject *valobj = (*pos).get();
+ if (valobj && valobj->GetName() == name_const_str) {
+ val_obj_sp = *pos;
+ break;
+ }
+ }
+ return val_obj_sp;
+}
+
+ValueObjectSP ValueObjectList::FindValueObjectByUID(lldb::user_id_t uid) {
+ ValueObjectSP valobj_sp;
+ collection::iterator pos, end = m_value_objects.end();
+
+ for (pos = m_value_objects.begin(); pos != end; ++pos) {
+ // Watch out for NULL objects in our list as the list might get resized to
+ // a specific size and lazily filled in
+ ValueObject *valobj = (*pos).get();
+ if (valobj && valobj->GetID() == uid) {
+ valobj_sp = *pos;
+ break;
+ }
+ }
+ return valobj_sp;
+}
+
+ValueObjectSP
+ValueObjectList::FindValueObjectByPointer(ValueObject *find_valobj) {
+ ValueObjectSP valobj_sp;
+ collection::iterator pos, end = m_value_objects.end();
+
+ for (pos = m_value_objects.begin(); pos != end; ++pos) {
+ ValueObject *valobj = (*pos).get();
+ if (valobj && valobj == find_valobj) {
+ valobj_sp = *pos;
+ break;
+ }
+ }
+ return valobj_sp;
+}
+
+void ValueObjectList::Swap(ValueObjectList &value_object_list) {
+ m_value_objects.swap(value_object_list.m_value_objects);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectMemory.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectMemory.cpp
new file mode 100644
index 000000000000..95d4330ee0c6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectMemory.cpp
@@ -0,0 +1,228 @@
+//===-- ValueObjectMemory.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-types.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <assert.h>
+#include <memory>
+
+namespace lldb_private {
+class ExecutionContextScope;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+ValueObjectSP ValueObjectMemory::Create(ExecutionContextScope *exe_scope,
+ llvm::StringRef name,
+ const Address &address,
+ lldb::TypeSP &type_sp) {
+ return (new ValueObjectMemory(exe_scope, name, address, type_sp))->GetSP();
+}
+
+ValueObjectSP ValueObjectMemory::Create(ExecutionContextScope *exe_scope,
+ llvm::StringRef name,
+ const Address &address,
+ const CompilerType &ast_type) {
+ return (new ValueObjectMemory(exe_scope, name, address, ast_type))->GetSP();
+}
+
+ValueObjectMemory::ValueObjectMemory(ExecutionContextScope *exe_scope,
+ llvm::StringRef name,
+ const Address &address,
+ lldb::TypeSP &type_sp)
+ : ValueObject(exe_scope), m_address(address), m_type_sp(type_sp),
+ m_compiler_type() {
+ // Do not attempt to construct one of these objects with no variable!
+ assert(m_type_sp.get() != nullptr);
+ SetName(ConstString(name));
+ m_value.SetContext(Value::eContextTypeLLDBType, m_type_sp.get());
+ TargetSP target_sp(GetTargetSP());
+ lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get());
+ if (load_address != LLDB_INVALID_ADDRESS) {
+ m_value.SetValueType(Value::eValueTypeLoadAddress);
+ m_value.GetScalar() = load_address;
+ } else {
+ lldb::addr_t file_address = m_address.GetFileAddress();
+ if (file_address != LLDB_INVALID_ADDRESS) {
+ m_value.SetValueType(Value::eValueTypeFileAddress);
+ m_value.GetScalar() = file_address;
+ } else {
+ m_value.GetScalar() = m_address.GetOffset();
+ m_value.SetValueType(Value::eValueTypeScalar);
+ }
+ }
+}
+
+ValueObjectMemory::ValueObjectMemory(ExecutionContextScope *exe_scope,
+ llvm::StringRef name,
+ const Address &address,
+ const CompilerType &ast_type)
+ : ValueObject(exe_scope), m_address(address), m_type_sp(),
+ m_compiler_type(ast_type) {
+ // Do not attempt to construct one of these objects with no variable!
+ assert(m_compiler_type.GetTypeSystem());
+ assert(m_compiler_type.GetOpaqueQualType());
+
+ TargetSP target_sp(GetTargetSP());
+
+ SetName(ConstString(name));
+ // m_value.SetContext(Value::eContextTypeClangType,
+ // m_compiler_type.GetOpaqueQualType());
+ m_value.SetCompilerType(m_compiler_type);
+ lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get());
+ if (load_address != LLDB_INVALID_ADDRESS) {
+ m_value.SetValueType(Value::eValueTypeLoadAddress);
+ m_value.GetScalar() = load_address;
+ } else {
+ lldb::addr_t file_address = m_address.GetFileAddress();
+ if (file_address != LLDB_INVALID_ADDRESS) {
+ m_value.SetValueType(Value::eValueTypeFileAddress);
+ m_value.GetScalar() = file_address;
+ } else {
+ m_value.GetScalar() = m_address.GetOffset();
+ m_value.SetValueType(Value::eValueTypeScalar);
+ }
+ }
+}
+
+ValueObjectMemory::~ValueObjectMemory() {}
+
+CompilerType ValueObjectMemory::GetCompilerTypeImpl() {
+ if (m_type_sp)
+ return m_type_sp->GetForwardCompilerType();
+ return m_compiler_type;
+}
+
+ConstString ValueObjectMemory::GetTypeName() {
+ if (m_type_sp)
+ return m_type_sp->GetName();
+ return m_compiler_type.GetConstTypeName();
+}
+
+ConstString ValueObjectMemory::GetDisplayTypeName() {
+ if (m_type_sp)
+ return m_type_sp->GetForwardCompilerType().GetDisplayTypeName();
+ return m_compiler_type.GetDisplayTypeName();
+}
+
+size_t ValueObjectMemory::CalculateNumChildren(uint32_t max) {
+ if (m_type_sp) {
+ auto child_count = m_type_sp->GetNumChildren(true);
+ return child_count <= max ? child_count : max;
+ }
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ const bool omit_empty_base_classes = true;
+ auto child_count =
+ m_compiler_type.GetNumChildren(omit_empty_base_classes, &exe_ctx);
+ return child_count <= max ? child_count : max;
+}
+
+uint64_t ValueObjectMemory::GetByteSize() {
+ if (m_type_sp)
+ return m_type_sp->GetByteSize().getValueOr(0);
+ return m_compiler_type.GetByteSize(nullptr).getValueOr(0);
+}
+
+lldb::ValueType ValueObjectMemory::GetValueType() const {
+ // RETHINK: Should this be inherited from somewhere?
+ return lldb::eValueTypeVariableGlobal;
+}
+
+bool ValueObjectMemory::UpdateValue() {
+ SetValueIsValid(false);
+ m_error.Clear();
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ m_data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
+ }
+
+ Value old_value(m_value);
+ if (m_address.IsValid()) {
+ Value::ValueType value_type = m_value.GetValueType();
+
+ switch (value_type) {
+ default:
+ llvm_unreachable("Unhandled expression result value kind...");
+
+ case Value::eValueTypeScalar:
+ // The variable value is in the Scalar value inside the m_value. We can
+ // point our m_data right to it.
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get());
+ break;
+
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeHostAddress:
+ // The DWARF expression result was an address in the inferior process. If
+ // this variable is an aggregate type, we just need the address as the
+ // main value as all child variable objects will rely upon this location
+ // and add an offset and then read their own values as needed. If this
+ // variable is a simple type, we read all data for it into m_data. Make
+ // sure this type has a value before we try and read it
+
+ // If we have a file address, convert it to a load address if we can.
+ if (value_type == Value::eValueTypeFileAddress &&
+ exe_ctx.GetProcessPtr()) {
+ lldb::addr_t load_addr = m_address.GetLoadAddress(target);
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ m_value.SetValueType(Value::eValueTypeLoadAddress);
+ m_value.GetScalar() = load_addr;
+ }
+ }
+
+ if (!CanProvideValue()) {
+ // this value object represents an aggregate type whose children have
+ // values, but this object does not. So we say we are changed if our
+ // location has changed.
+ SetValueDidChange(value_type != old_value.GetValueType() ||
+ m_value.GetScalar() != old_value.GetScalar());
+ } else {
+ // Copy the Value and set the context to use our Variable so it can
+ // extract read its value into m_data appropriately
+ Value value(m_value);
+ if (m_type_sp)
+ value.SetContext(Value::eContextTypeLLDBType, m_type_sp.get());
+ else {
+ // value.SetContext(Value::eContextTypeClangType,
+ // m_compiler_type.GetOpaqueQualType());
+ value.SetCompilerType(m_compiler_type);
+ }
+
+ m_error = value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get());
+ }
+ break;
+ }
+
+ SetValueIsValid(m_error.Success());
+ }
+ return m_error.Success();
+}
+
+bool ValueObjectMemory::IsInScope() {
+ // FIXME: Maybe try to read the memory address, and if that works, then
+ // we are in scope?
+ return true;
+}
+
+lldb::ModuleSP ValueObjectMemory::GetModule() { return m_address.GetModule(); }
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectRegister.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectRegister.cpp
new file mode 100644
index 000000000000..75a254fbbc21
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectRegister.cpp
@@ -0,0 +1,358 @@
+//===-- ValueObjectRegister.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectRegister.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include <assert.h>
+#include <memory>
+
+namespace lldb_private {
+class ExecutionContextScope;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark ValueObjectRegisterContext
+
+ValueObjectRegisterContext::ValueObjectRegisterContext(
+ ValueObject &parent, RegisterContextSP &reg_ctx)
+ : ValueObject(parent), m_reg_ctx_sp(reg_ctx) {
+ assert(reg_ctx);
+ m_name.SetCString("Registers");
+ SetValueIsValid(true);
+}
+
+ValueObjectRegisterContext::~ValueObjectRegisterContext() {}
+
+CompilerType ValueObjectRegisterContext::GetCompilerTypeImpl() {
+ return CompilerType();
+}
+
+ConstString ValueObjectRegisterContext::GetTypeName() { return ConstString(); }
+
+ConstString ValueObjectRegisterContext::GetDisplayTypeName() {
+ return ConstString();
+}
+
+ConstString ValueObjectRegisterContext::GetQualifiedTypeName() {
+ return ConstString();
+}
+
+size_t ValueObjectRegisterContext::CalculateNumChildren(uint32_t max) {
+ auto reg_set_count = m_reg_ctx_sp->GetRegisterSetCount();
+ return reg_set_count <= max ? reg_set_count : max;
+}
+
+uint64_t ValueObjectRegisterContext::GetByteSize() { return 0; }
+
+bool ValueObjectRegisterContext::UpdateValue() {
+ m_error.Clear();
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame)
+ m_reg_ctx_sp = frame->GetRegisterContext();
+ else
+ m_reg_ctx_sp.reset();
+
+ if (m_reg_ctx_sp.get() == nullptr) {
+ SetValueIsValid(false);
+ m_error.SetErrorToGenericError();
+ } else
+ SetValueIsValid(true);
+
+ return m_error.Success();
+}
+
+ValueObject *ValueObjectRegisterContext::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ ValueObject *new_valobj = nullptr;
+
+ const size_t num_children = GetNumChildren();
+ if (idx < num_children) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ new_valobj = new ValueObjectRegisterSet(
+ exe_ctx.GetBestExecutionContextScope(), m_reg_ctx_sp, idx);
+ }
+
+ return new_valobj;
+}
+
+#pragma mark -
+#pragma mark ValueObjectRegisterSet
+
+ValueObjectSP
+ValueObjectRegisterSet::Create(ExecutionContextScope *exe_scope,
+ lldb::RegisterContextSP &reg_ctx_sp,
+ uint32_t set_idx) {
+ return (new ValueObjectRegisterSet(exe_scope, reg_ctx_sp, set_idx))->GetSP();
+}
+
+ValueObjectRegisterSet::ValueObjectRegisterSet(ExecutionContextScope *exe_scope,
+ lldb::RegisterContextSP &reg_ctx,
+ uint32_t reg_set_idx)
+ : ValueObject(exe_scope), m_reg_ctx_sp(reg_ctx), m_reg_set(nullptr),
+ m_reg_set_idx(reg_set_idx) {
+ assert(reg_ctx);
+ m_reg_set = reg_ctx->GetRegisterSet(m_reg_set_idx);
+ if (m_reg_set) {
+ m_name.SetCString(m_reg_set->name);
+ }
+}
+
+ValueObjectRegisterSet::~ValueObjectRegisterSet() {}
+
+CompilerType ValueObjectRegisterSet::GetCompilerTypeImpl() {
+ return CompilerType();
+}
+
+ConstString ValueObjectRegisterSet::GetTypeName() { return ConstString(); }
+
+ConstString ValueObjectRegisterSet::GetQualifiedTypeName() {
+ return ConstString();
+}
+
+size_t ValueObjectRegisterSet::CalculateNumChildren(uint32_t max) {
+ const RegisterSet *reg_set = m_reg_ctx_sp->GetRegisterSet(m_reg_set_idx);
+ if (reg_set) {
+ auto reg_count = reg_set->num_registers;
+ return reg_count <= max ? reg_count : max;
+ }
+ return 0;
+}
+
+uint64_t ValueObjectRegisterSet::GetByteSize() { return 0; }
+
+bool ValueObjectRegisterSet::UpdateValue() {
+ m_error.Clear();
+ SetValueDidChange(false);
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame == nullptr)
+ m_reg_ctx_sp.reset();
+ else {
+ m_reg_ctx_sp = frame->GetRegisterContext();
+ if (m_reg_ctx_sp) {
+ const RegisterSet *reg_set = m_reg_ctx_sp->GetRegisterSet(m_reg_set_idx);
+ if (reg_set == nullptr)
+ m_reg_ctx_sp.reset();
+ else if (m_reg_set != reg_set) {
+ SetValueDidChange(true);
+ m_name.SetCString(reg_set->name);
+ }
+ }
+ }
+ if (m_reg_ctx_sp) {
+ SetValueIsValid(true);
+ } else {
+ SetValueIsValid(false);
+ m_error.SetErrorToGenericError();
+ m_children.Clear();
+ }
+ return m_error.Success();
+}
+
+ValueObject *ValueObjectRegisterSet::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ ValueObject *valobj = nullptr;
+ if (m_reg_ctx_sp && m_reg_set) {
+ const size_t num_children = GetNumChildren();
+ if (idx < num_children)
+ valobj = new ValueObjectRegister(*this, m_reg_ctx_sp,
+ m_reg_set->registers[idx]);
+ }
+ return valobj;
+}
+
+lldb::ValueObjectSP
+ValueObjectRegisterSet::GetChildMemberWithName(ConstString name,
+ bool can_create) {
+ ValueObject *valobj = nullptr;
+ if (m_reg_ctx_sp && m_reg_set) {
+ const RegisterInfo *reg_info =
+ m_reg_ctx_sp->GetRegisterInfoByName(name.AsCString());
+ if (reg_info != nullptr)
+ valobj = new ValueObjectRegister(*this, m_reg_ctx_sp,
+ reg_info->kinds[eRegisterKindLLDB]);
+ }
+ if (valobj)
+ return valobj->GetSP();
+ else
+ return ValueObjectSP();
+}
+
+size_t
+ValueObjectRegisterSet::GetIndexOfChildWithName(ConstString name) {
+ if (m_reg_ctx_sp && m_reg_set) {
+ const RegisterInfo *reg_info =
+ m_reg_ctx_sp->GetRegisterInfoByName(name.AsCString());
+ if (reg_info != nullptr)
+ return reg_info->kinds[eRegisterKindLLDB];
+ }
+ return UINT32_MAX;
+}
+
+#pragma mark -
+#pragma mark ValueObjectRegister
+
+void ValueObjectRegister::ConstructObject(uint32_t reg_num) {
+ const RegisterInfo *reg_info = m_reg_ctx_sp->GetRegisterInfoAtIndex(reg_num);
+ if (reg_info) {
+ m_reg_info = *reg_info;
+ if (reg_info->name)
+ m_name.SetCString(reg_info->name);
+ else if (reg_info->alt_name)
+ m_name.SetCString(reg_info->alt_name);
+ }
+}
+
+ValueObjectRegister::ValueObjectRegister(ValueObject &parent,
+ lldb::RegisterContextSP &reg_ctx_sp,
+ uint32_t reg_num)
+ : ValueObject(parent), m_reg_ctx_sp(reg_ctx_sp), m_reg_info(),
+ m_reg_value(), m_type_name(), m_compiler_type() {
+ assert(reg_ctx_sp.get());
+ ConstructObject(reg_num);
+}
+
+ValueObjectSP ValueObjectRegister::Create(ExecutionContextScope *exe_scope,
+ lldb::RegisterContextSP &reg_ctx_sp,
+ uint32_t reg_num) {
+ return (new ValueObjectRegister(exe_scope, reg_ctx_sp, reg_num))->GetSP();
+}
+
+ValueObjectRegister::ValueObjectRegister(ExecutionContextScope *exe_scope,
+ lldb::RegisterContextSP &reg_ctx,
+ uint32_t reg_num)
+ : ValueObject(exe_scope), m_reg_ctx_sp(reg_ctx), m_reg_info(),
+ m_reg_value(), m_type_name(), m_compiler_type() {
+ assert(reg_ctx);
+ ConstructObject(reg_num);
+}
+
+ValueObjectRegister::~ValueObjectRegister() {}
+
+CompilerType ValueObjectRegister::GetCompilerTypeImpl() {
+ if (!m_compiler_type.IsValid()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ Module *exe_module = target->GetExecutableModulePointer();
+ if (exe_module) {
+ TypeSystem *type_system =
+ exe_module->GetTypeSystemForLanguage(eLanguageTypeC);
+ if (type_system)
+ m_compiler_type = type_system->GetBuiltinTypeForEncodingAndBitSize(
+ m_reg_info.encoding, m_reg_info.byte_size * 8);
+ }
+ }
+ }
+ return m_compiler_type;
+}
+
+ConstString ValueObjectRegister::GetTypeName() {
+ if (m_type_name.IsEmpty())
+ m_type_name = GetCompilerType().GetConstTypeName();
+ return m_type_name;
+}
+
+size_t ValueObjectRegister::CalculateNumChildren(uint32_t max) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ auto children_count = GetCompilerType().GetNumChildren(true, &exe_ctx);
+ return children_count <= max ? children_count : max;
+}
+
+uint64_t ValueObjectRegister::GetByteSize() { return m_reg_info.byte_size; }
+
+bool ValueObjectRegister::UpdateValue() {
+ m_error.Clear();
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame == nullptr) {
+ m_reg_ctx_sp.reset();
+ m_reg_value.Clear();
+ }
+
+ if (m_reg_ctx_sp) {
+ RegisterValue m_old_reg_value(m_reg_value);
+ if (m_reg_ctx_sp->ReadRegister(&m_reg_info, m_reg_value)) {
+ if (m_reg_value.GetData(m_data)) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process)
+ m_data.SetAddressByteSize(process->GetAddressByteSize());
+ m_value.SetContext(Value::eContextTypeRegisterInfo,
+ (void *)&m_reg_info);
+ m_value.SetValueType(Value::eValueTypeHostAddress);
+ m_value.GetScalar() = (uintptr_t)m_data.GetDataStart();
+ SetValueIsValid(true);
+ SetValueDidChange(!(m_old_reg_value == m_reg_value));
+ return true;
+ }
+ }
+ }
+
+ SetValueIsValid(false);
+ m_error.SetErrorToGenericError();
+ return false;
+}
+
+bool ValueObjectRegister::SetValueFromCString(const char *value_str,
+ Status &error) {
+ // The new value will be in the m_data. Copy that into our register value.
+ error =
+ m_reg_value.SetValueFromString(&m_reg_info, llvm::StringRef(value_str));
+ if (error.Success()) {
+ if (m_reg_ctx_sp->WriteRegister(&m_reg_info, m_reg_value)) {
+ SetNeedsUpdate();
+ return true;
+ } else
+ return false;
+ } else
+ return false;
+}
+
+bool ValueObjectRegister::SetData(DataExtractor &data, Status &error) {
+ error = m_reg_value.SetValueFromData(&m_reg_info, data, 0, false);
+ if (error.Success()) {
+ if (m_reg_ctx_sp->WriteRegister(&m_reg_info, m_reg_value)) {
+ SetNeedsUpdate();
+ return true;
+ } else
+ return false;
+ } else
+ return false;
+}
+
+bool ValueObjectRegister::ResolveValue(Scalar &scalar) {
+ if (UpdateValueIfNeeded(
+ false)) // make sure that you are up to date before returning anything
+ return m_reg_value.GetScalarValue(scalar);
+ return false;
+}
+
+void ValueObjectRegister::GetExpressionPath(Stream &s,
+ bool qualify_cxx_base_classes,
+ GetExpressionPathFormat epformat) {
+ s.Printf("$%s", m_reg_info.name);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectSyntheticFilter.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectSyntheticFilter.cpp
new file mode 100644
index 000000000000..400473235759
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectSyntheticFilter.cpp
@@ -0,0 +1,396 @@
+//===-- ValueObjectSyntheticFilter.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectSyntheticFilter.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/SharingPtr.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+namespace lldb_private {
+class Declaration;
+}
+
+using namespace lldb_private;
+
+class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ DummySyntheticFrontEnd(ValueObject &backend)
+ : SyntheticChildrenFrontEnd(backend) {}
+
+ size_t CalculateNumChildren() override { return m_backend.GetNumChildren(); }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ return m_backend.GetChildAtIndex(idx, true);
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return m_backend.GetIndexOfChildWithName(name);
+ }
+
+ bool MightHaveChildren() override { return true; }
+
+ bool Update() override { return false; }
+};
+
+ValueObjectSynthetic::ValueObjectSynthetic(ValueObject &parent,
+ lldb::SyntheticChildrenSP filter)
+ : ValueObject(parent), m_synth_sp(filter), m_children_byindex(),
+ m_name_toindex(), m_synthetic_children_count(UINT32_MAX),
+ m_synthetic_children_cache(), m_parent_type_name(parent.GetTypeName()),
+ m_might_have_children(eLazyBoolCalculate),
+ m_provides_value(eLazyBoolCalculate) {
+ SetName(parent.GetName());
+ CopyValueData(m_parent);
+ CreateSynthFilter();
+}
+
+ValueObjectSynthetic::~ValueObjectSynthetic() = default;
+
+CompilerType ValueObjectSynthetic::GetCompilerTypeImpl() {
+ return m_parent->GetCompilerType();
+}
+
+ConstString ValueObjectSynthetic::GetTypeName() {
+ return m_parent->GetTypeName();
+}
+
+ConstString ValueObjectSynthetic::GetQualifiedTypeName() {
+ return m_parent->GetQualifiedTypeName();
+}
+
+ConstString ValueObjectSynthetic::GetDisplayTypeName() {
+ if (ConstString synth_name = m_synth_filter_up->GetSyntheticTypeName())
+ return synth_name;
+
+ return m_parent->GetDisplayTypeName();
+}
+
+size_t ValueObjectSynthetic::CalculateNumChildren(uint32_t max) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS);
+
+ UpdateValueIfNeeded();
+ if (m_synthetic_children_count < UINT32_MAX)
+ return m_synthetic_children_count <= max ? m_synthetic_children_count : max;
+
+ if (max < UINT32_MAX) {
+ size_t num_children = m_synth_filter_up->CalculateNumChildren(max);
+ if (log)
+ log->Printf("[ValueObjectSynthetic::CalculateNumChildren] for VO of name "
+ "%s and type %s, the filter returned %zu child values",
+ GetName().AsCString(), GetTypeName().AsCString(),
+ num_children);
+ return num_children;
+ } else {
+ size_t num_children = (m_synthetic_children_count =
+ m_synth_filter_up->CalculateNumChildren(max));
+ if (log)
+ log->Printf("[ValueObjectSynthetic::CalculateNumChildren] for VO of name "
+ "%s and type %s, the filter returned %zu child values",
+ GetName().AsCString(), GetTypeName().AsCString(),
+ num_children);
+ return num_children;
+ }
+}
+
+lldb::ValueObjectSP
+ValueObjectSynthetic::GetDynamicValue(lldb::DynamicValueType valueType) {
+ if (!m_parent)
+ return lldb::ValueObjectSP();
+ if (IsDynamic() && GetDynamicValueType() == valueType)
+ return GetSP();
+ return m_parent->GetDynamicValue(valueType);
+}
+
+bool ValueObjectSynthetic::MightHaveChildren() {
+ if (m_might_have_children == eLazyBoolCalculate)
+ m_might_have_children =
+ (m_synth_filter_up->MightHaveChildren() ? eLazyBoolYes : eLazyBoolNo);
+ return (m_might_have_children != eLazyBoolNo);
+}
+
+uint64_t ValueObjectSynthetic::GetByteSize() { return m_parent->GetByteSize(); }
+
+lldb::ValueType ValueObjectSynthetic::GetValueType() const {
+ return m_parent->GetValueType();
+}
+
+void ValueObjectSynthetic::CreateSynthFilter() {
+ ValueObject *valobj_for_frontend = m_parent;
+ if (m_synth_sp->WantsDereference())
+ {
+ CompilerType type = m_parent->GetCompilerType();
+ if (type.IsValid() && type.IsPointerOrReferenceType())
+ {
+ Status error;
+ lldb::ValueObjectSP deref_sp = m_parent->Dereference(error);
+ if (error.Success())
+ valobj_for_frontend = deref_sp.get();
+ }
+ }
+ m_synth_filter_up = (m_synth_sp->GetFrontEnd(*valobj_for_frontend));
+ if (!m_synth_filter_up)
+ m_synth_filter_up = llvm::make_unique<DummySyntheticFrontEnd>(*m_parent);
+}
+
+bool ValueObjectSynthetic::UpdateValue() {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS);
+
+ SetValueIsValid(false);
+ m_error.Clear();
+
+ if (!m_parent->UpdateValueIfNeeded(false)) {
+ // our parent could not update.. as we are meaningless without a parent,
+ // just stop
+ if (m_parent->GetError().Fail())
+ m_error = m_parent->GetError();
+ return false;
+ }
+
+ // regenerate the synthetic filter if our typename changes
+ // <rdar://problem/12424824>
+ ConstString new_parent_type_name = m_parent->GetTypeName();
+ if (new_parent_type_name != m_parent_type_name) {
+ if (log)
+ log->Printf("[ValueObjectSynthetic::UpdateValue] name=%s, type changed "
+ "from %s to %s, recomputing synthetic filter",
+ GetName().AsCString(), m_parent_type_name.AsCString(),
+ new_parent_type_name.AsCString());
+ m_parent_type_name = new_parent_type_name;
+ CreateSynthFilter();
+ }
+
+ // let our backend do its update
+ if (!m_synth_filter_up->Update()) {
+ if (log)
+ log->Printf("[ValueObjectSynthetic::UpdateValue] name=%s, synthetic "
+ "filter said caches are stale - clearing",
+ GetName().AsCString());
+ // filter said that cached values are stale
+ m_children_byindex.Clear();
+ m_name_toindex.Clear();
+ // usually, an object's value can change but this does not alter its
+ // children count for a synthetic VO that might indeed happen, so we need
+ // to tell the upper echelons that they need to come back to us asking for
+ // children
+ m_children_count_valid = false;
+ m_synthetic_children_cache.Clear();
+ m_synthetic_children_count = UINT32_MAX;
+ m_might_have_children = eLazyBoolCalculate;
+ } else {
+ if (log)
+ log->Printf("[ValueObjectSynthetic::UpdateValue] name=%s, synthetic "
+ "filter said caches are still valid",
+ GetName().AsCString());
+ }
+
+ m_provides_value = eLazyBoolCalculate;
+
+ lldb::ValueObjectSP synth_val(m_synth_filter_up->GetSyntheticValue());
+
+ if (synth_val && synth_val->CanProvideValue()) {
+ if (log)
+ log->Printf("[ValueObjectSynthetic::UpdateValue] name=%s, synthetic "
+ "filter said it can provide a value",
+ GetName().AsCString());
+
+ m_provides_value = eLazyBoolYes;
+ CopyValueData(synth_val.get());
+ } else {
+ if (log)
+ log->Printf("[ValueObjectSynthetic::UpdateValue] name=%s, synthetic "
+ "filter said it will not provide a value",
+ GetName().AsCString());
+
+ m_provides_value = eLazyBoolNo;
+ CopyValueData(m_parent);
+ }
+
+ SetValueIsValid(true);
+ return true;
+}
+
+lldb::ValueObjectSP ValueObjectSynthetic::GetChildAtIndex(size_t idx,
+ bool can_create) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS);
+
+ if (log)
+ log->Printf("[ValueObjectSynthetic::GetChildAtIndex] name=%s, retrieving "
+ "child at index %zu",
+ GetName().AsCString(), idx);
+
+ UpdateValueIfNeeded();
+
+ ValueObject *valobj;
+ if (!m_children_byindex.GetValueForKey(idx, valobj)) {
+ if (can_create && m_synth_filter_up != nullptr) {
+ if (log)
+ log->Printf("[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at "
+ "index %zu not cached and will be created",
+ GetName().AsCString(), idx);
+
+ lldb::ValueObjectSP synth_guy = m_synth_filter_up->GetChildAtIndex(idx);
+
+ if (log)
+ log->Printf(
+ "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at index "
+ "%zu created as %p (is "
+ "synthetic: %s)",
+ GetName().AsCString(), idx, static_cast<void *>(synth_guy.get()),
+ synth_guy.get()
+ ? (synth_guy->IsSyntheticChildrenGenerated() ? "yes" : "no")
+ : "no");
+
+ if (!synth_guy)
+ return synth_guy;
+
+ if (synth_guy->IsSyntheticChildrenGenerated())
+ m_synthetic_children_cache.AppendObject(synth_guy);
+ m_children_byindex.SetValueForKey(idx, synth_guy.get());
+ synth_guy->SetPreferredDisplayLanguageIfNeeded(
+ GetPreferredDisplayLanguage());
+ return synth_guy;
+ } else {
+ if (log)
+ log->Printf("[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at "
+ "index %zu not cached and cannot "
+ "be created (can_create = %s, synth_filter = %p)",
+ GetName().AsCString(), idx, can_create ? "yes" : "no",
+ static_cast<void *>(m_synth_filter_up.get()));
+
+ return lldb::ValueObjectSP();
+ }
+ } else {
+ if (log)
+ log->Printf("[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at "
+ "index %zu cached as %p",
+ GetName().AsCString(), idx, static_cast<void *>(valobj));
+
+ return valobj->GetSP();
+ }
+}
+
+lldb::ValueObjectSP
+ValueObjectSynthetic::GetChildMemberWithName(ConstString name,
+ bool can_create) {
+ UpdateValueIfNeeded();
+
+ uint32_t index = GetIndexOfChildWithName(name);
+
+ if (index == UINT32_MAX)
+ return lldb::ValueObjectSP();
+
+ return GetChildAtIndex(index, can_create);
+}
+
+size_t ValueObjectSynthetic::GetIndexOfChildWithName(ConstString name) {
+ UpdateValueIfNeeded();
+
+ uint32_t found_index = UINT32_MAX;
+ bool did_find = m_name_toindex.GetValueForKey(name.GetCString(), found_index);
+
+ if (!did_find && m_synth_filter_up != nullptr) {
+ uint32_t index = m_synth_filter_up->GetIndexOfChildWithName(name);
+ if (index == UINT32_MAX)
+ return index;
+ m_name_toindex.SetValueForKey(name.GetCString(), index);
+ return index;
+ } else if (!did_find && m_synth_filter_up == nullptr)
+ return UINT32_MAX;
+ else /*if (iter != m_name_toindex.end())*/
+ return found_index;
+}
+
+bool ValueObjectSynthetic::IsInScope() { return m_parent->IsInScope(); }
+
+lldb::ValueObjectSP ValueObjectSynthetic::GetNonSyntheticValue() {
+ return m_parent->GetSP();
+}
+
+void ValueObjectSynthetic::CopyValueData(ValueObject *source) {
+ m_value = (source->UpdateValueIfNeeded(), source->GetValue());
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get());
+}
+
+bool ValueObjectSynthetic::CanProvideValue() {
+ if (!UpdateValueIfNeeded())
+ return false;
+ if (m_provides_value == eLazyBoolYes)
+ return true;
+ return m_parent->CanProvideValue();
+}
+
+bool ValueObjectSynthetic::SetValueFromCString(const char *value_str,
+ Status &error) {
+ return m_parent->SetValueFromCString(value_str, error);
+}
+
+void ValueObjectSynthetic::SetFormat(lldb::Format format) {
+ if (m_parent) {
+ m_parent->ClearUserVisibleData(eClearUserVisibleDataItemsAll);
+ m_parent->SetFormat(format);
+ }
+ this->ValueObject::SetFormat(format);
+ this->ClearUserVisibleData(eClearUserVisibleDataItemsAll);
+}
+
+void ValueObjectSynthetic::SetPreferredDisplayLanguage(
+ lldb::LanguageType lang) {
+ this->ValueObject::SetPreferredDisplayLanguage(lang);
+ if (m_parent)
+ m_parent->SetPreferredDisplayLanguage(lang);
+}
+
+lldb::LanguageType ValueObjectSynthetic::GetPreferredDisplayLanguage() {
+ if (m_preferred_display_language == lldb::eLanguageTypeUnknown) {
+ if (m_parent)
+ return m_parent->GetPreferredDisplayLanguage();
+ return lldb::eLanguageTypeUnknown;
+ } else
+ return m_preferred_display_language;
+}
+
+bool ValueObjectSynthetic::IsSyntheticChildrenGenerated() {
+ if (m_parent)
+ return m_parent->IsSyntheticChildrenGenerated();
+ return false;
+}
+
+void ValueObjectSynthetic::SetSyntheticChildrenGenerated(bool b) {
+ if (m_parent)
+ m_parent->SetSyntheticChildrenGenerated(b);
+ this->ValueObject::SetSyntheticChildrenGenerated(b);
+}
+
+bool ValueObjectSynthetic::GetDeclaration(Declaration &decl) {
+ if (m_parent)
+ return m_parent->GetDeclaration(decl);
+
+ return ValueObject::GetDeclaration(decl);
+}
+
+uint64_t ValueObjectSynthetic::GetLanguageFlags() {
+ if (m_parent)
+ return m_parent->GetLanguageFlags();
+ return this->ValueObject::GetLanguageFlags();
+}
+
+void ValueObjectSynthetic::SetLanguageFlags(uint64_t flags) {
+ if (m_parent)
+ m_parent->SetLanguageFlags(flags);
+ else
+ this->ValueObject::SetLanguageFlags(flags);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectVariable.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectVariable.cpp
new file mode 100644
index 000000000000..5aee82493b28
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectVariable.cpp
@@ -0,0 +1,376 @@
+//===-- ValueObjectVariable.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectVariable.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/Declaration.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolContextScope.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-private-enumerations.h"
+#include "lldb/lldb-types.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include <assert.h>
+#include <memory>
+
+namespace lldb_private {
+class ExecutionContextScope;
+}
+namespace lldb_private {
+class StackFrame;
+}
+namespace lldb_private {
+struct RegisterInfo;
+}
+using namespace lldb_private;
+
+lldb::ValueObjectSP
+ValueObjectVariable::Create(ExecutionContextScope *exe_scope,
+ const lldb::VariableSP &var_sp) {
+ return (new ValueObjectVariable(exe_scope, var_sp))->GetSP();
+}
+
+ValueObjectVariable::ValueObjectVariable(ExecutionContextScope *exe_scope,
+ const lldb::VariableSP &var_sp)
+ : ValueObject(exe_scope), m_variable_sp(var_sp) {
+ // Do not attempt to construct one of these objects with no variable!
+ assert(m_variable_sp.get() != nullptr);
+ m_name = var_sp->GetName();
+}
+
+ValueObjectVariable::~ValueObjectVariable() {}
+
+CompilerType ValueObjectVariable::GetCompilerTypeImpl() {
+ Type *var_type = m_variable_sp->GetType();
+ if (var_type)
+ return var_type->GetForwardCompilerType();
+ return CompilerType();
+}
+
+ConstString ValueObjectVariable::GetTypeName() {
+ Type *var_type = m_variable_sp->GetType();
+ if (var_type)
+ return var_type->GetName();
+ return ConstString();
+}
+
+ConstString ValueObjectVariable::GetDisplayTypeName() {
+ Type *var_type = m_variable_sp->GetType();
+ if (var_type)
+ return var_type->GetForwardCompilerType().GetDisplayTypeName();
+ return ConstString();
+}
+
+ConstString ValueObjectVariable::GetQualifiedTypeName() {
+ Type *var_type = m_variable_sp->GetType();
+ if (var_type)
+ return var_type->GetQualifiedName();
+ return ConstString();
+}
+
+size_t ValueObjectVariable::CalculateNumChildren(uint32_t max) {
+ CompilerType type(GetCompilerType());
+
+ if (!type.IsValid())
+ return 0;
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ const bool omit_empty_base_classes = true;
+ auto child_count = type.GetNumChildren(omit_empty_base_classes, &exe_ctx);
+ return child_count <= max ? child_count : max;
+}
+
+uint64_t ValueObjectVariable::GetByteSize() {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ CompilerType type(GetCompilerType());
+
+ if (!type.IsValid())
+ return 0;
+
+ return type.GetByteSize(exe_ctx.GetBestExecutionContextScope()).getValueOr(0);
+}
+
+lldb::ValueType ValueObjectVariable::GetValueType() const {
+ if (m_variable_sp)
+ return m_variable_sp->GetScope();
+ return lldb::eValueTypeInvalid;
+}
+
+bool ValueObjectVariable::UpdateValue() {
+ SetValueIsValid(false);
+ m_error.Clear();
+
+ Variable *variable = m_variable_sp.get();
+ DWARFExpression &expr = variable->LocationExpression();
+
+ if (variable->GetLocationIsConstantValueData()) {
+ // expr doesn't contain DWARF bytes, it contains the constant variable
+ // value bytes themselves...
+ if (expr.GetExpressionData(m_data))
+ m_value.SetContext(Value::eContextTypeVariable, variable);
+ else
+ m_error.SetErrorString("empty constant data");
+ // constant bytes can't be edited - sorry
+ m_resolved_value.SetContext(Value::eContextTypeInvalid, nullptr);
+ } else {
+ lldb::addr_t loclist_base_load_addr = LLDB_INVALID_ADDRESS;
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ m_data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
+ }
+
+ if (expr.IsLocationList()) {
+ SymbolContext sc;
+ variable->CalculateSymbolContext(&sc);
+ if (sc.function)
+ loclist_base_load_addr =
+ sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(
+ target);
+ }
+ Value old_value(m_value);
+ if (expr.Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, nullptr,
+ nullptr, m_value, &m_error)) {
+ m_resolved_value = m_value;
+ m_value.SetContext(Value::eContextTypeVariable, variable);
+
+ CompilerType compiler_type = GetCompilerType();
+ if (compiler_type.IsValid())
+ m_value.SetCompilerType(compiler_type);
+
+ Value::ValueType value_type = m_value.GetValueType();
+
+ Process *process = exe_ctx.GetProcessPtr();
+ const bool process_is_alive = process && process->IsAlive();
+ const uint32_t type_info = compiler_type.GetTypeInfo();
+ const bool is_pointer_or_ref =
+ (type_info & (lldb::eTypeIsPointer | lldb::eTypeIsReference)) != 0;
+
+ switch (value_type) {
+ case Value::eValueTypeFileAddress:
+ // If this type is a pointer, then its children will be considered load
+ // addresses if the pointer or reference is dereferenced, but only if
+ // the process is alive.
+ //
+ // There could be global variables like in the following code:
+ // struct LinkedListNode { Foo* foo; LinkedListNode* next; };
+ // Foo g_foo1;
+ // Foo g_foo2;
+ // LinkedListNode g_second_node = { &g_foo2, NULL };
+ // LinkedListNode g_first_node = { &g_foo1, &g_second_node };
+ //
+ // When we aren't running, we should be able to look at these variables
+ // using the "target variable" command. Children of the "g_first_node"
+ // always will be of the same address type as the parent. But children
+ // of the "next" member of LinkedListNode will become load addresses if
+ // we have a live process, or remain what a file address if it what a
+ // file address.
+ if (process_is_alive && is_pointer_or_ref)
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+ else
+ SetAddressTypeOfChildren(eAddressTypeFile);
+ break;
+ case Value::eValueTypeHostAddress:
+ // Same as above for load addresses, except children of pointer or refs
+ // are always load addresses. Host addresses are used to store freeze
+ // dried variables. If this type is a struct, the entire struct
+ // contents will be copied into the heap of the
+ // LLDB process, but we do not currently follow any pointers.
+ if (is_pointer_or_ref)
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+ else
+ SetAddressTypeOfChildren(eAddressTypeHost);
+ break;
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeScalar:
+ case Value::eValueTypeVector:
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+ break;
+ }
+
+ switch (value_type) {
+ case Value::eValueTypeVector:
+ // fall through
+ case Value::eValueTypeScalar:
+ // The variable value is in the Scalar value inside the m_value. We can
+ // point our m_data right to it.
+ m_error =
+ m_value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get());
+ break;
+
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeHostAddress:
+ // The DWARF expression result was an address in the inferior process.
+ // If this variable is an aggregate type, we just need the address as
+ // the main value as all child variable objects will rely upon this
+ // location and add an offset and then read their own values as needed.
+ // If this variable is a simple type, we read all data for it into
+ // m_data. Make sure this type has a value before we try and read it
+
+ // If we have a file address, convert it to a load address if we can.
+ if (value_type == Value::eValueTypeFileAddress && process_is_alive)
+ m_value.ConvertToLoadAddress(GetModule().get(), target);
+
+ if (!CanProvideValue()) {
+ // this value object represents an aggregate type whose children have
+ // values, but this object does not. So we say we are changed if our
+ // location has changed.
+ SetValueDidChange(value_type != old_value.GetValueType() ||
+ m_value.GetScalar() != old_value.GetScalar());
+ } else {
+ // Copy the Value and set the context to use our Variable so it can
+ // extract read its value into m_data appropriately
+ Value value(m_value);
+ value.SetContext(Value::eContextTypeVariable, variable);
+ m_error =
+ value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get());
+
+ SetValueDidChange(value_type != old_value.GetValueType() ||
+ m_value.GetScalar() != old_value.GetScalar());
+ }
+ break;
+ }
+
+ SetValueIsValid(m_error.Success());
+ } else {
+ // could not find location, won't allow editing
+ m_resolved_value.SetContext(Value::eContextTypeInvalid, nullptr);
+ }
+ }
+ return m_error.Success();
+}
+
+bool ValueObjectVariable::IsInScope() {
+ const ExecutionContextRef &exe_ctx_ref = GetExecutionContextRef();
+ if (exe_ctx_ref.HasFrameRef()) {
+ ExecutionContext exe_ctx(exe_ctx_ref);
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ return m_variable_sp->IsInScope(frame);
+ } else {
+ // This ValueObject had a frame at one time, but now we can't locate it,
+ // so return false since we probably aren't in scope.
+ return false;
+ }
+ }
+ // We have a variable that wasn't tied to a frame, which means it is a global
+ // and is always in scope.
+ return true;
+}
+
+lldb::ModuleSP ValueObjectVariable::GetModule() {
+ if (m_variable_sp) {
+ SymbolContextScope *sc_scope = m_variable_sp->GetSymbolContextScope();
+ if (sc_scope) {
+ return sc_scope->CalculateSymbolContextModule();
+ }
+ }
+ return lldb::ModuleSP();
+}
+
+SymbolContextScope *ValueObjectVariable::GetSymbolContextScope() {
+ if (m_variable_sp)
+ return m_variable_sp->GetSymbolContextScope();
+ return nullptr;
+}
+
+bool ValueObjectVariable::GetDeclaration(Declaration &decl) {
+ if (m_variable_sp) {
+ decl = m_variable_sp->GetDeclaration();
+ return true;
+ }
+ return false;
+}
+
+const char *ValueObjectVariable::GetLocationAsCString() {
+ if (m_resolved_value.GetContextType() == Value::eContextTypeRegisterInfo)
+ return GetLocationAsCStringImpl(m_resolved_value, m_data);
+ else
+ return ValueObject::GetLocationAsCString();
+}
+
+bool ValueObjectVariable::SetValueFromCString(const char *value_str,
+ Status &error) {
+ if (!UpdateValueIfNeeded()) {
+ error.SetErrorString("unable to update value before writing");
+ return false;
+ }
+
+ if (m_resolved_value.GetContextType() == Value::eContextTypeRegisterInfo) {
+ RegisterInfo *reg_info = m_resolved_value.GetRegisterInfo();
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
+ RegisterValue reg_value;
+ if (!reg_info || !reg_ctx) {
+ error.SetErrorString("unable to retrieve register info");
+ return false;
+ }
+ error = reg_value.SetValueFromString(reg_info, llvm::StringRef(value_str));
+ if (error.Fail())
+ return false;
+ if (reg_ctx->WriteRegister(reg_info, reg_value)) {
+ SetNeedsUpdate();
+ return true;
+ } else {
+ error.SetErrorString("unable to write back to register");
+ return false;
+ }
+ } else
+ return ValueObject::SetValueFromCString(value_str, error);
+}
+
+bool ValueObjectVariable::SetData(DataExtractor &data, Status &error) {
+ if (!UpdateValueIfNeeded()) {
+ error.SetErrorString("unable to update value before writing");
+ return false;
+ }
+
+ if (m_resolved_value.GetContextType() == Value::eContextTypeRegisterInfo) {
+ RegisterInfo *reg_info = m_resolved_value.GetRegisterInfo();
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
+ RegisterValue reg_value;
+ if (!reg_info || !reg_ctx) {
+ error.SetErrorString("unable to retrieve register info");
+ return false;
+ }
+ error = reg_value.SetValueFromData(reg_info, data, 0, true);
+ if (error.Fail())
+ return false;
+ if (reg_ctx->WriteRegister(reg_info, reg_value)) {
+ SetNeedsUpdate();
+ return true;
+ } else {
+ error.SetErrorString("unable to write back to register");
+ return false;
+ }
+ } else
+ return ValueObject::SetData(data, error);
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/CXXFunctionPointer.cpp b/contrib/llvm-project/lldb/source/DataFormatters/CXXFunctionPointer.cpp
new file mode 100644
index 000000000000..0ca000eb0529
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/CXXFunctionPointer.cpp
@@ -0,0 +1,56 @@
+//===-- CXXFunctionPointer.cpp-----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/CXXFunctionPointer.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Stream.h"
+
+#include <string>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+bool lldb_private::formatters::CXXFunctionPointerSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ std::string destination;
+ StreamString sstr;
+ AddressType func_ptr_address_type = eAddressTypeInvalid;
+ addr_t func_ptr_address = valobj.GetPointerValue(&func_ptr_address_type);
+ if (func_ptr_address != 0 && func_ptr_address != LLDB_INVALID_ADDRESS) {
+ switch (func_ptr_address_type) {
+ case eAddressTypeInvalid:
+ case eAddressTypeFile:
+ case eAddressTypeHost:
+ break;
+
+ case eAddressTypeLoad: {
+ ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
+
+ Address so_addr;
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target && !target->GetSectionLoadList().IsEmpty()) {
+ if (target->GetSectionLoadList().ResolveLoadAddress(func_ptr_address,
+ so_addr)) {
+ so_addr.Dump(&sstr, exe_ctx.GetBestExecutionContextScope(),
+ Address::DumpStyleResolvedDescription,
+ Address::DumpStyleSectionNameOffset);
+ }
+ }
+ } break;
+ }
+ }
+ if (sstr.GetSize() > 0) {
+ stream.Printf("(%s)", sstr.GetData());
+ return true;
+ } else
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/DataVisualization.cpp b/contrib/llvm-project/lldb/source/DataFormatters/DataVisualization.cpp
new file mode 100644
index 000000000000..08b3b34447bb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/DataVisualization.cpp
@@ -0,0 +1,210 @@
+//===-- DataVisualization.cpp ---------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/DataVisualization.h"
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+static FormatManager &GetFormatManager() {
+ static FormatManager g_format_manager;
+ return g_format_manager;
+}
+
+void DataVisualization::ForceUpdate() { GetFormatManager().Changed(); }
+
+uint32_t DataVisualization::GetCurrentRevision() {
+ return GetFormatManager().GetCurrentRevision();
+}
+
+bool DataVisualization::ShouldPrintAsOneLiner(ValueObject &valobj) {
+ return GetFormatManager().ShouldPrintAsOneLiner(valobj);
+}
+
+lldb::TypeFormatImplSP
+DataVisualization::GetFormat(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ return GetFormatManager().GetFormat(valobj, use_dynamic);
+}
+
+lldb::TypeFormatImplSP
+DataVisualization::GetFormatForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ return GetFormatManager().GetFormatForType(type_sp);
+}
+
+lldb::TypeSummaryImplSP
+DataVisualization::GetSummaryFormat(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ return GetFormatManager().GetSummaryFormat(valobj, use_dynamic);
+}
+
+lldb::TypeSummaryImplSP
+DataVisualization::GetSummaryForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ return GetFormatManager().GetSummaryForType(type_sp);
+}
+
+lldb::SyntheticChildrenSP
+DataVisualization::GetSyntheticChildren(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ return GetFormatManager().GetSyntheticChildren(valobj, use_dynamic);
+}
+
+lldb::TypeFilterImplSP
+DataVisualization::GetFilterForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ return GetFormatManager().GetFilterForType(type_sp);
+}
+
+lldb::ScriptedSyntheticChildrenSP
+DataVisualization::GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ return GetFormatManager().GetSyntheticForType(type_sp);
+}
+
+lldb::TypeValidatorImplSP
+DataVisualization::GetValidator(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ return GetFormatManager().GetValidator(valobj, use_dynamic);
+}
+
+lldb::TypeValidatorImplSP
+DataVisualization::GetValidatorForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ return GetFormatManager().GetValidatorForType(type_sp);
+}
+
+bool DataVisualization::AnyMatches(
+ ConstString type_name, TypeCategoryImpl::FormatCategoryItems items,
+ bool only_enabled, const char **matching_category,
+ TypeCategoryImpl::FormatCategoryItems *matching_type) {
+ return GetFormatManager().AnyMatches(type_name, items, only_enabled,
+ matching_category, matching_type);
+}
+
+bool DataVisualization::Categories::GetCategory(ConstString category,
+ lldb::TypeCategoryImplSP &entry,
+ bool allow_create) {
+ entry = GetFormatManager().GetCategory(category, allow_create);
+ return (entry.get() != nullptr);
+}
+
+bool DataVisualization::Categories::GetCategory(
+ lldb::LanguageType language, lldb::TypeCategoryImplSP &entry) {
+ if (LanguageCategory *lang_category =
+ GetFormatManager().GetCategoryForLanguage(language))
+ entry = lang_category->GetCategory();
+ return (entry.get() != nullptr);
+}
+
+void DataVisualization::Categories::Add(ConstString category) {
+ GetFormatManager().GetCategory(category);
+}
+
+bool DataVisualization::Categories::Delete(ConstString category) {
+ GetFormatManager().DisableCategory(category);
+ return GetFormatManager().DeleteCategory(category);
+}
+
+void DataVisualization::Categories::Clear() {
+ GetFormatManager().ClearCategories();
+}
+
+void DataVisualization::Categories::Clear(ConstString category) {
+ GetFormatManager().GetCategory(category)->Clear(
+ eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary);
+}
+
+void DataVisualization::Categories::Enable(ConstString category,
+ TypeCategoryMap::Position pos) {
+ if (GetFormatManager().GetCategory(category)->IsEnabled())
+ GetFormatManager().DisableCategory(category);
+ GetFormatManager().EnableCategory(
+ category, pos, std::initializer_list<lldb::LanguageType>());
+}
+
+void DataVisualization::Categories::Enable(lldb::LanguageType lang_type) {
+ if (LanguageCategory *lang_category =
+ GetFormatManager().GetCategoryForLanguage(lang_type))
+ lang_category->Enable();
+}
+
+void DataVisualization::Categories::Disable(ConstString category) {
+ if (GetFormatManager().GetCategory(category)->IsEnabled())
+ GetFormatManager().DisableCategory(category);
+}
+
+void DataVisualization::Categories::Disable(lldb::LanguageType lang_type) {
+ if (LanguageCategory *lang_category =
+ GetFormatManager().GetCategoryForLanguage(lang_type))
+ lang_category->Disable();
+}
+
+void DataVisualization::Categories::Enable(
+ const lldb::TypeCategoryImplSP &category, TypeCategoryMap::Position pos) {
+ if (category.get()) {
+ if (category->IsEnabled())
+ GetFormatManager().DisableCategory(category);
+ GetFormatManager().EnableCategory(category, pos);
+ }
+}
+
+void DataVisualization::Categories::Disable(
+ const lldb::TypeCategoryImplSP &category) {
+ if (category.get() && category->IsEnabled())
+ GetFormatManager().DisableCategory(category);
+}
+
+void DataVisualization::Categories::EnableStar() {
+ GetFormatManager().EnableAllCategories();
+}
+
+void DataVisualization::Categories::DisableStar() {
+ GetFormatManager().DisableAllCategories();
+}
+
+void DataVisualization::Categories::ForEach(
+ TypeCategoryMap::ForEachCallback callback) {
+ GetFormatManager().ForEachCategory(callback);
+}
+
+uint32_t DataVisualization::Categories::GetCount() {
+ return GetFormatManager().GetCategoriesCount();
+}
+
+lldb::TypeCategoryImplSP
+DataVisualization::Categories::GetCategoryAtIndex(size_t index) {
+ return GetFormatManager().GetCategoryAtIndex(index);
+}
+
+bool DataVisualization::NamedSummaryFormats::GetSummaryFormat(
+ ConstString type, lldb::TypeSummaryImplSP &entry) {
+ return GetFormatManager().GetNamedSummaryContainer().Get(type, entry);
+}
+
+void DataVisualization::NamedSummaryFormats::Add(
+ ConstString type, const lldb::TypeSummaryImplSP &entry) {
+ GetFormatManager().GetNamedSummaryContainer().Add(
+ FormatManager::GetValidTypeName(type), entry);
+}
+
+bool DataVisualization::NamedSummaryFormats::Delete(ConstString type) {
+ return GetFormatManager().GetNamedSummaryContainer().Delete(type);
+}
+
+void DataVisualization::NamedSummaryFormats::Clear() {
+ GetFormatManager().GetNamedSummaryContainer().Clear();
+}
+
+void DataVisualization::NamedSummaryFormats::ForEach(
+ std::function<bool(ConstString, const lldb::TypeSummaryImplSP &)>
+ callback) {
+ GetFormatManager().GetNamedSummaryContainer().ForEach(callback);
+}
+
+uint32_t DataVisualization::NamedSummaryFormats::GetCount() {
+ return GetFormatManager().GetNamedSummaryContainer().GetCount();
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/DumpValueObjectOptions.cpp b/contrib/llvm-project/lldb/source/DataFormatters/DumpValueObjectOptions.cpp
new file mode 100644
index 000000000000..84f21696e054
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/DumpValueObjectOptions.cpp
@@ -0,0 +1,201 @@
+//===-- DumpValueObjectOptions.cpp -----------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/DumpValueObjectOptions.h"
+
+#include "lldb/Core/ValueObject.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+DumpValueObjectOptions::DumpValueObjectOptions()
+ : m_summary_sp(), m_root_valobj_name(),
+ m_max_ptr_depth(PointerDepth{PointerDepth::Mode::Default, 0}),
+ m_decl_printing_helper(), m_pointer_as_array(), m_use_synthetic(true),
+ m_scope_already_checked(false), m_flat_output(false), m_ignore_cap(false),
+ m_show_types(false), m_show_location(false), m_use_objc(false),
+ m_hide_root_type(false), m_hide_name(false), m_hide_value(false),
+ m_run_validator(false), m_use_type_display_name(true),
+ m_allow_oneliner_mode(true), m_hide_pointer_value(false),
+ m_reveal_empty_aggregates(true) {}
+
+DumpValueObjectOptions::DumpValueObjectOptions(ValueObject &valobj)
+ : DumpValueObjectOptions() {
+ m_use_dynamic = valobj.GetDynamicValueType();
+ m_use_synthetic = valobj.IsSynthetic();
+ m_varformat_language = valobj.GetPreferredDisplayLanguage();
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetMaximumPointerDepth(PointerDepth depth) {
+ m_max_ptr_depth = depth;
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetMaximumDepth(uint32_t depth) {
+ m_max_depth = depth;
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetDeclPrintingHelper(DeclPrintingHelper helper) {
+ m_decl_printing_helper = helper;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetShowTypes(bool show) {
+ m_show_types = show;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetShowLocation(bool show) {
+ m_show_location = show;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetUseObjectiveC(bool use) {
+ m_use_objc = use;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetShowSummary(bool show) {
+ if (!show)
+ SetOmitSummaryDepth(UINT32_MAX);
+ else
+ SetOmitSummaryDepth(0);
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetUseDynamicType(lldb::DynamicValueType dyn) {
+ m_use_dynamic = dyn;
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetUseSyntheticValue(bool use_synthetic) {
+ m_use_synthetic = use_synthetic;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetScopeChecked(bool check) {
+ m_scope_already_checked = check;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetFlatOutput(bool flat) {
+ m_flat_output = flat;
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetOmitSummaryDepth(uint32_t depth) {
+ m_omit_summary_depth = depth;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetIgnoreCap(bool ignore) {
+ m_ignore_cap = ignore;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetRawDisplay() {
+ SetUseSyntheticValue(false);
+ SetOmitSummaryDepth(UINT32_MAX);
+ SetIgnoreCap(true);
+ SetHideName(false);
+ SetHideValue(false);
+ SetUseTypeDisplayName(false);
+ SetAllowOnelinerMode(false);
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetFormat(lldb::Format format) {
+ m_format = format;
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetSummary(lldb::TypeSummaryImplSP summary) {
+ m_summary_sp = summary;
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetRootValueObjectName(const char *name) {
+ if (name)
+ m_root_valobj_name.assign(name);
+ else
+ m_root_valobj_name.clear();
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetHideRootType(bool hide_root_type) {
+ m_hide_root_type = hide_root_type;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetHideName(bool hide_name) {
+ m_hide_name = hide_name;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetHideValue(bool hide_value) {
+ m_hide_value = hide_value;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetHidePointerValue(bool hide) {
+ m_hide_pointer_value = hide;
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetVariableFormatDisplayLanguage(
+ lldb::LanguageType lang) {
+ m_varformat_language = lang;
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetRunValidator(bool run) {
+ m_run_validator = run;
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetUseTypeDisplayName(bool dis) {
+ m_use_type_display_name = dis;
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetAllowOnelinerMode(bool oneliner) {
+ m_allow_oneliner_mode = oneliner;
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetRevealEmptyAggregates(bool reveal) {
+ m_reveal_empty_aggregates = reveal;
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetElementCount(uint32_t element_count) {
+ m_pointer_as_array = PointerAsArraySettings(element_count);
+ return *this;
+}
+
+DumpValueObjectOptions &DumpValueObjectOptions::SetPointerAsArray(
+ const PointerAsArraySettings &ptr_array) {
+ m_pointer_as_array = ptr_array;
+ return *this;
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/FormatCache.cpp b/contrib/llvm-project/lldb/source/DataFormatters/FormatCache.cpp
new file mode 100644
index 000000000000..7e328cb0dac8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/FormatCache.cpp
@@ -0,0 +1,219 @@
+//===-- FormatCache.cpp ------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+
+
+
+#include "lldb/DataFormatters/FormatCache.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+FormatCache::Entry::Entry()
+ : m_format_cached(false), m_summary_cached(false),
+ m_synthetic_cached(false), m_validator_cached(false), m_format_sp(),
+ m_summary_sp(), m_synthetic_sp(), m_validator_sp() {}
+
+FormatCache::Entry::Entry(lldb::TypeFormatImplSP format_sp)
+ : m_summary_cached(false), m_synthetic_cached(false),
+ m_validator_cached(false), m_summary_sp(), m_synthetic_sp(),
+ m_validator_sp() {
+ SetFormat(format_sp);
+}
+
+FormatCache::Entry::Entry(lldb::TypeSummaryImplSP summary_sp)
+ : m_format_cached(false), m_synthetic_cached(false),
+ m_validator_cached(false), m_format_sp(), m_synthetic_sp(),
+ m_validator_sp() {
+ SetSummary(summary_sp);
+}
+
+FormatCache::Entry::Entry(lldb::SyntheticChildrenSP synthetic_sp)
+ : m_format_cached(false), m_summary_cached(false),
+ m_validator_cached(false), m_format_sp(), m_summary_sp(),
+ m_validator_sp() {
+ SetSynthetic(synthetic_sp);
+}
+
+FormatCache::Entry::Entry(lldb::TypeValidatorImplSP validator_sp)
+ : m_format_cached(false), m_summary_cached(false),
+ m_synthetic_cached(false), m_format_sp(), m_summary_sp(),
+ m_synthetic_sp() {
+ SetValidator(validator_sp);
+}
+
+FormatCache::Entry::Entry(lldb::TypeFormatImplSP format_sp,
+ lldb::TypeSummaryImplSP summary_sp,
+ lldb::SyntheticChildrenSP synthetic_sp,
+ lldb::TypeValidatorImplSP validator_sp) {
+ SetFormat(format_sp);
+ SetSummary(summary_sp);
+ SetSynthetic(synthetic_sp);
+ SetValidator(validator_sp);
+}
+
+bool FormatCache::Entry::IsFormatCached() { return m_format_cached; }
+
+bool FormatCache::Entry::IsSummaryCached() { return m_summary_cached; }
+
+bool FormatCache::Entry::IsSyntheticCached() { return m_synthetic_cached; }
+
+bool FormatCache::Entry::IsValidatorCached() { return m_validator_cached; }
+
+lldb::TypeFormatImplSP FormatCache::Entry::GetFormat() { return m_format_sp; }
+
+lldb::TypeSummaryImplSP FormatCache::Entry::GetSummary() {
+ return m_summary_sp;
+}
+
+lldb::SyntheticChildrenSP FormatCache::Entry::GetSynthetic() {
+ return m_synthetic_sp;
+}
+
+lldb::TypeValidatorImplSP FormatCache::Entry::GetValidator() {
+ return m_validator_sp;
+}
+
+void FormatCache::Entry::SetFormat(lldb::TypeFormatImplSP format_sp) {
+ m_format_cached = true;
+ m_format_sp = format_sp;
+}
+
+void FormatCache::Entry::SetSummary(lldb::TypeSummaryImplSP summary_sp) {
+ m_summary_cached = true;
+ m_summary_sp = summary_sp;
+}
+
+void FormatCache::Entry::SetSynthetic(lldb::SyntheticChildrenSP synthetic_sp) {
+ m_synthetic_cached = true;
+ m_synthetic_sp = synthetic_sp;
+}
+
+void FormatCache::Entry::SetValidator(lldb::TypeValidatorImplSP validator_sp) {
+ m_validator_cached = true;
+ m_validator_sp = validator_sp;
+}
+
+FormatCache::FormatCache()
+ : m_map(), m_mutex()
+#ifdef LLDB_CONFIGURATION_DEBUG
+ ,
+ m_cache_hits(0), m_cache_misses(0)
+#endif
+{
+}
+
+FormatCache::Entry &FormatCache::GetEntry(ConstString type) {
+ auto i = m_map.find(type), e = m_map.end();
+ if (i != e)
+ return i->second;
+ m_map[type] = FormatCache::Entry();
+ return m_map[type];
+}
+
+bool FormatCache::GetFormat(ConstString type,
+ lldb::TypeFormatImplSP &format_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ auto entry = GetEntry(type);
+ if (entry.IsFormatCached()) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ m_cache_hits++;
+#endif
+ format_sp = entry.GetFormat();
+ return true;
+ }
+#ifdef LLDB_CONFIGURATION_DEBUG
+ m_cache_misses++;
+#endif
+ format_sp.reset();
+ return false;
+}
+
+bool FormatCache::GetSummary(ConstString type,
+ lldb::TypeSummaryImplSP &summary_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ auto entry = GetEntry(type);
+ if (entry.IsSummaryCached()) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ m_cache_hits++;
+#endif
+ summary_sp = entry.GetSummary();
+ return true;
+ }
+#ifdef LLDB_CONFIGURATION_DEBUG
+ m_cache_misses++;
+#endif
+ summary_sp.reset();
+ return false;
+}
+
+bool FormatCache::GetSynthetic(ConstString type,
+ lldb::SyntheticChildrenSP &synthetic_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ auto entry = GetEntry(type);
+ if (entry.IsSyntheticCached()) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ m_cache_hits++;
+#endif
+ synthetic_sp = entry.GetSynthetic();
+ return true;
+ }
+#ifdef LLDB_CONFIGURATION_DEBUG
+ m_cache_misses++;
+#endif
+ synthetic_sp.reset();
+ return false;
+}
+
+bool FormatCache::GetValidator(ConstString type,
+ lldb::TypeValidatorImplSP &validator_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ auto entry = GetEntry(type);
+ if (entry.IsValidatorCached()) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ m_cache_hits++;
+#endif
+ validator_sp = entry.GetValidator();
+ return true;
+ }
+#ifdef LLDB_CONFIGURATION_DEBUG
+ m_cache_misses++;
+#endif
+ validator_sp.reset();
+ return false;
+}
+
+void FormatCache::SetFormat(ConstString type,
+ lldb::TypeFormatImplSP &format_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ GetEntry(type).SetFormat(format_sp);
+}
+
+void FormatCache::SetSummary(ConstString type,
+ lldb::TypeSummaryImplSP &summary_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ GetEntry(type).SetSummary(summary_sp);
+}
+
+void FormatCache::SetSynthetic(ConstString type,
+ lldb::SyntheticChildrenSP &synthetic_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ GetEntry(type).SetSynthetic(synthetic_sp);
+}
+
+void FormatCache::SetValidator(ConstString type,
+ lldb::TypeValidatorImplSP &validator_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ GetEntry(type).SetValidator(validator_sp);
+}
+
+void FormatCache::Clear() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_map.clear();
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/FormatClasses.cpp b/contrib/llvm-project/lldb/source/DataFormatters/FormatClasses.cpp
new file mode 100644
index 000000000000..271963b5f8f4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/FormatClasses.cpp
@@ -0,0 +1,49 @@
+//===-- FormatClasses.cpp ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/FormatClasses.h"
+
+#include "lldb/DataFormatters/FormatManager.h"
+
+
+
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+FormattersMatchData::FormattersMatchData(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic)
+ : m_valobj(valobj), m_dynamic_value_type(use_dynamic),
+ m_formatters_match_vector({}, false), m_type_for_cache(),
+ m_candidate_languages() {
+ m_type_for_cache = FormatManager::GetTypeForCache(valobj, use_dynamic);
+ m_candidate_languages = FormatManager::GetCandidateLanguages(valobj);
+}
+
+FormattersMatchVector FormattersMatchData::GetMatchesVector() {
+ if (!m_formatters_match_vector.second) {
+ m_formatters_match_vector.second = true;
+ m_formatters_match_vector.first =
+ FormatManager::GetPossibleMatches(m_valobj, m_dynamic_value_type);
+ }
+ return m_formatters_match_vector.first;
+}
+
+ConstString FormattersMatchData::GetTypeForCache() { return m_type_for_cache; }
+
+CandidateLanguagesVector FormattersMatchData::GetCandidateLanguages() {
+ return m_candidate_languages;
+}
+
+ValueObject &FormattersMatchData::GetValueObject() { return m_valobj; }
+
+lldb::DynamicValueType FormattersMatchData::GetDynamicValueType() {
+ return m_dynamic_value_type;
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/FormatManager.cpp b/contrib/llvm-project/lldb/source/DataFormatters/FormatManager.cpp
new file mode 100644
index 000000000000..dd2808a7cf7c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/FormatManager.cpp
@@ -0,0 +1,1043 @@
+//===-- FormatManager.cpp ----------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/FormatManager.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/LanguageCategory.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+struct FormatInfo {
+ Format format;
+ const char format_char; // One or more format characters that can be used for
+ // this format.
+ const char *format_name; // Long format name that can be used to specify the
+ // current format
+};
+
+static FormatInfo g_format_infos[] = {
+ {eFormatDefault, '\0', "default"},
+ {eFormatBoolean, 'B', "boolean"},
+ {eFormatBinary, 'b', "binary"},
+ {eFormatBytes, 'y', "bytes"},
+ {eFormatBytesWithASCII, 'Y', "bytes with ASCII"},
+ {eFormatChar, 'c', "character"},
+ {eFormatCharPrintable, 'C', "printable character"},
+ {eFormatComplexFloat, 'F', "complex float"},
+ {eFormatCString, 's', "c-string"},
+ {eFormatDecimal, 'd', "decimal"},
+ {eFormatEnum, 'E', "enumeration"},
+ {eFormatHex, 'x', "hex"},
+ {eFormatHexUppercase, 'X', "uppercase hex"},
+ {eFormatFloat, 'f', "float"},
+ {eFormatOctal, 'o', "octal"},
+ {eFormatOSType, 'O', "OSType"},
+ {eFormatUnicode16, 'U', "unicode16"},
+ {eFormatUnicode32, '\0', "unicode32"},
+ {eFormatUnsigned, 'u', "unsigned decimal"},
+ {eFormatPointer, 'p', "pointer"},
+ {eFormatVectorOfChar, '\0', "char[]"},
+ {eFormatVectorOfSInt8, '\0', "int8_t[]"},
+ {eFormatVectorOfUInt8, '\0', "uint8_t[]"},
+ {eFormatVectorOfSInt16, '\0', "int16_t[]"},
+ {eFormatVectorOfUInt16, '\0', "uint16_t[]"},
+ {eFormatVectorOfSInt32, '\0', "int32_t[]"},
+ {eFormatVectorOfUInt32, '\0', "uint32_t[]"},
+ {eFormatVectorOfSInt64, '\0', "int64_t[]"},
+ {eFormatVectorOfUInt64, '\0', "uint64_t[]"},
+ {eFormatVectorOfFloat16, '\0', "float16[]"},
+ {eFormatVectorOfFloat32, '\0', "float32[]"},
+ {eFormatVectorOfFloat64, '\0', "float64[]"},
+ {eFormatVectorOfUInt128, '\0', "uint128_t[]"},
+ {eFormatComplexInteger, 'I', "complex integer"},
+ {eFormatCharArray, 'a', "character array"},
+ {eFormatAddressInfo, 'A', "address"},
+ {eFormatHexFloat, '\0', "hex float"},
+ {eFormatInstruction, 'i', "instruction"},
+ {eFormatVoid, 'v', "void"}};
+
+static uint32_t g_num_format_infos = llvm::array_lengthof(g_format_infos);
+
+static bool GetFormatFromFormatChar(char format_char, Format &format) {
+ for (uint32_t i = 0; i < g_num_format_infos; ++i) {
+ if (g_format_infos[i].format_char == format_char) {
+ format = g_format_infos[i].format;
+ return true;
+ }
+ }
+ format = eFormatInvalid;
+ return false;
+}
+
+static bool GetFormatFromFormatName(const char *format_name,
+ bool partial_match_ok, Format &format) {
+ uint32_t i;
+ for (i = 0; i < g_num_format_infos; ++i) {
+ if (strcasecmp(g_format_infos[i].format_name, format_name) == 0) {
+ format = g_format_infos[i].format;
+ return true;
+ }
+ }
+
+ if (partial_match_ok) {
+ for (i = 0; i < g_num_format_infos; ++i) {
+ if (strcasestr(g_format_infos[i].format_name, format_name) ==
+ g_format_infos[i].format_name) {
+ format = g_format_infos[i].format;
+ return true;
+ }
+ }
+ }
+ format = eFormatInvalid;
+ return false;
+}
+
+void FormatManager::Changed() {
+ ++m_last_revision;
+ m_format_cache.Clear();
+ std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex);
+ for (auto &iter : m_language_categories_map) {
+ if (iter.second)
+ iter.second->GetFormatCache().Clear();
+ }
+}
+
+bool FormatManager::GetFormatFromCString(const char *format_cstr,
+ bool partial_match_ok,
+ lldb::Format &format) {
+ bool success = false;
+ if (format_cstr && format_cstr[0]) {
+ if (format_cstr[1] == '\0') {
+ success = GetFormatFromFormatChar(format_cstr[0], format);
+ if (success)
+ return true;
+ }
+
+ success = GetFormatFromFormatName(format_cstr, partial_match_ok, format);
+ }
+ if (!success)
+ format = eFormatInvalid;
+ return success;
+}
+
+char FormatManager::GetFormatAsFormatChar(lldb::Format format) {
+ for (uint32_t i = 0; i < g_num_format_infos; ++i) {
+ if (g_format_infos[i].format == format)
+ return g_format_infos[i].format_char;
+ }
+ return '\0';
+}
+
+const char *FormatManager::GetFormatAsCString(Format format) {
+ if (format >= eFormatDefault && format < kNumFormats)
+ return g_format_infos[format].format_name;
+ return nullptr;
+}
+
+void FormatManager::EnableAllCategories() {
+ m_categories_map.EnableAllCategories();
+ std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex);
+ for (auto &iter : m_language_categories_map) {
+ if (iter.second)
+ iter.second->Enable();
+ }
+}
+
+void FormatManager::DisableAllCategories() {
+ m_categories_map.DisableAllCategories();
+ std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex);
+ for (auto &iter : m_language_categories_map) {
+ if (iter.second)
+ iter.second->Disable();
+ }
+}
+
+void FormatManager::GetPossibleMatches(
+ ValueObject &valobj, CompilerType compiler_type, uint32_t reason,
+ lldb::DynamicValueType use_dynamic, FormattersMatchVector &entries,
+ bool did_strip_ptr, bool did_strip_ref, bool did_strip_typedef,
+ bool root_level) {
+ compiler_type = compiler_type.GetTypeForFormatters();
+ ConstString type_name(compiler_type.GetConstTypeName());
+ if (valobj.GetBitfieldBitSize() > 0) {
+ StreamString sstring;
+ sstring.Printf("%s:%d", type_name.AsCString(), valobj.GetBitfieldBitSize());
+ ConstString bitfieldname(sstring.GetString());
+ entries.push_back(
+ {bitfieldname, 0, did_strip_ptr, did_strip_ref, did_strip_typedef});
+ reason |= lldb_private::eFormatterChoiceCriterionStrippedBitField;
+ }
+
+ if (!compiler_type.IsMeaninglessWithoutDynamicResolution()) {
+ entries.push_back(
+ {type_name, reason, did_strip_ptr, did_strip_ref, did_strip_typedef});
+
+ ConstString display_type_name(compiler_type.GetDisplayTypeName());
+ if (display_type_name != type_name)
+ entries.push_back({display_type_name, reason, did_strip_ptr,
+ did_strip_ref, did_strip_typedef});
+ }
+
+ for (bool is_rvalue_ref = true, j = true;
+ j && compiler_type.IsReferenceType(nullptr, &is_rvalue_ref); j = false) {
+ CompilerType non_ref_type = compiler_type.GetNonReferenceType();
+ GetPossibleMatches(
+ valobj, non_ref_type,
+ reason |
+ lldb_private::eFormatterChoiceCriterionStrippedPointerReference,
+ use_dynamic, entries, did_strip_ptr, true, did_strip_typedef);
+ if (non_ref_type.IsTypedefType()) {
+ CompilerType deffed_referenced_type = non_ref_type.GetTypedefedType();
+ deffed_referenced_type =
+ is_rvalue_ref ? deffed_referenced_type.GetRValueReferenceType()
+ : deffed_referenced_type.GetLValueReferenceType();
+ GetPossibleMatches(
+ valobj, deffed_referenced_type,
+ reason | lldb_private::eFormatterChoiceCriterionNavigatedTypedefs,
+ use_dynamic, entries, did_strip_ptr, did_strip_ref,
+ true); // this is not exactly the usual meaning of stripping typedefs
+ }
+ }
+
+ if (compiler_type.IsPointerType()) {
+ CompilerType non_ptr_type = compiler_type.GetPointeeType();
+ GetPossibleMatches(
+ valobj, non_ptr_type,
+ reason |
+ lldb_private::eFormatterChoiceCriterionStrippedPointerReference,
+ use_dynamic, entries, true, did_strip_ref, did_strip_typedef);
+ if (non_ptr_type.IsTypedefType()) {
+ CompilerType deffed_pointed_type =
+ non_ptr_type.GetTypedefedType().GetPointerType();
+ GetPossibleMatches(
+ valobj, deffed_pointed_type,
+ reason | lldb_private::eFormatterChoiceCriterionNavigatedTypedefs,
+ use_dynamic, entries, did_strip_ptr, did_strip_ref,
+ true); // this is not exactly the usual meaning of stripping typedefs
+ }
+ }
+
+ for (lldb::LanguageType language_type : GetCandidateLanguages(valobj)) {
+ if (Language *language = Language::FindPlugin(language_type)) {
+ for (ConstString candidate :
+ language->GetPossibleFormattersMatches(valobj, use_dynamic)) {
+ entries.push_back(
+ {candidate,
+ reason | lldb_private::eFormatterChoiceCriterionLanguagePlugin,
+ did_strip_ptr, did_strip_ref, did_strip_typedef});
+ }
+ }
+ }
+
+ // try to strip typedef chains
+ if (compiler_type.IsTypedefType()) {
+ CompilerType deffed_type = compiler_type.GetTypedefedType();
+ GetPossibleMatches(
+ valobj, deffed_type,
+ reason | lldb_private::eFormatterChoiceCriterionNavigatedTypedefs,
+ use_dynamic, entries, did_strip_ptr, did_strip_ref, true);
+ }
+
+ if (root_level) {
+ do {
+ if (!compiler_type.IsValid())
+ break;
+
+ CompilerType unqual_compiler_ast_type =
+ compiler_type.GetFullyUnqualifiedType();
+ if (!unqual_compiler_ast_type.IsValid())
+ break;
+ if (unqual_compiler_ast_type.GetOpaqueQualType() !=
+ compiler_type.GetOpaqueQualType())
+ GetPossibleMatches(valobj, unqual_compiler_ast_type, reason,
+ use_dynamic, entries, did_strip_ptr, did_strip_ref,
+ did_strip_typedef);
+ } while (false);
+
+ // if all else fails, go to static type
+ if (valobj.IsDynamic()) {
+ lldb::ValueObjectSP static_value_sp(valobj.GetStaticValue());
+ if (static_value_sp)
+ GetPossibleMatches(
+ *static_value_sp.get(), static_value_sp->GetCompilerType(),
+ reason | lldb_private::eFormatterChoiceCriterionWentToStaticValue,
+ use_dynamic, entries, did_strip_ptr, did_strip_ref,
+ did_strip_typedef, true);
+ }
+ }
+}
+
+lldb::TypeFormatImplSP
+FormatManager::GetFormatForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ if (!type_sp)
+ return lldb::TypeFormatImplSP();
+ lldb::TypeFormatImplSP format_chosen_sp;
+ uint32_t num_categories = m_categories_map.GetCount();
+ lldb::TypeCategoryImplSP category_sp;
+ uint32_t prio_category = UINT32_MAX;
+ for (uint32_t category_id = 0; category_id < num_categories; category_id++) {
+ category_sp = GetCategoryAtIndex(category_id);
+ if (!category_sp->IsEnabled())
+ continue;
+ lldb::TypeFormatImplSP format_current_sp =
+ category_sp->GetFormatForType(type_sp);
+ if (format_current_sp &&
+ (format_chosen_sp.get() == nullptr ||
+ (prio_category > category_sp->GetEnabledPosition()))) {
+ prio_category = category_sp->GetEnabledPosition();
+ format_chosen_sp = format_current_sp;
+ }
+ }
+ return format_chosen_sp;
+}
+
+lldb::TypeSummaryImplSP
+FormatManager::GetSummaryForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ if (!type_sp)
+ return lldb::TypeSummaryImplSP();
+ lldb::TypeSummaryImplSP summary_chosen_sp;
+ uint32_t num_categories = m_categories_map.GetCount();
+ lldb::TypeCategoryImplSP category_sp;
+ uint32_t prio_category = UINT32_MAX;
+ for (uint32_t category_id = 0; category_id < num_categories; category_id++) {
+ category_sp = GetCategoryAtIndex(category_id);
+ if (!category_sp->IsEnabled())
+ continue;
+ lldb::TypeSummaryImplSP summary_current_sp =
+ category_sp->GetSummaryForType(type_sp);
+ if (summary_current_sp &&
+ (summary_chosen_sp.get() == nullptr ||
+ (prio_category > category_sp->GetEnabledPosition()))) {
+ prio_category = category_sp->GetEnabledPosition();
+ summary_chosen_sp = summary_current_sp;
+ }
+ }
+ return summary_chosen_sp;
+}
+
+lldb::TypeFilterImplSP
+FormatManager::GetFilterForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ if (!type_sp)
+ return lldb::TypeFilterImplSP();
+ lldb::TypeFilterImplSP filter_chosen_sp;
+ uint32_t num_categories = m_categories_map.GetCount();
+ lldb::TypeCategoryImplSP category_sp;
+ uint32_t prio_category = UINT32_MAX;
+ for (uint32_t category_id = 0; category_id < num_categories; category_id++) {
+ category_sp = GetCategoryAtIndex(category_id);
+ if (!category_sp->IsEnabled())
+ continue;
+ lldb::TypeFilterImplSP filter_current_sp(
+ (TypeFilterImpl *)category_sp->GetFilterForType(type_sp).get());
+ if (filter_current_sp &&
+ (filter_chosen_sp.get() == nullptr ||
+ (prio_category > category_sp->GetEnabledPosition()))) {
+ prio_category = category_sp->GetEnabledPosition();
+ filter_chosen_sp = filter_current_sp;
+ }
+ }
+ return filter_chosen_sp;
+}
+
+lldb::ScriptedSyntheticChildrenSP
+FormatManager::GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ if (!type_sp)
+ return lldb::ScriptedSyntheticChildrenSP();
+ lldb::ScriptedSyntheticChildrenSP synth_chosen_sp;
+ uint32_t num_categories = m_categories_map.GetCount();
+ lldb::TypeCategoryImplSP category_sp;
+ uint32_t prio_category = UINT32_MAX;
+ for (uint32_t category_id = 0; category_id < num_categories; category_id++) {
+ category_sp = GetCategoryAtIndex(category_id);
+ if (!category_sp->IsEnabled())
+ continue;
+ lldb::ScriptedSyntheticChildrenSP synth_current_sp(
+ (ScriptedSyntheticChildren *)category_sp->GetSyntheticForType(type_sp)
+ .get());
+ if (synth_current_sp &&
+ (synth_chosen_sp.get() == nullptr ||
+ (prio_category > category_sp->GetEnabledPosition()))) {
+ prio_category = category_sp->GetEnabledPosition();
+ synth_chosen_sp = synth_current_sp;
+ }
+ }
+ return synth_chosen_sp;
+}
+
+lldb::TypeValidatorImplSP
+FormatManager::GetValidatorForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ if (!type_sp)
+ return lldb::TypeValidatorImplSP();
+ lldb::TypeValidatorImplSP validator_chosen_sp;
+ uint32_t num_categories = m_categories_map.GetCount();
+ lldb::TypeCategoryImplSP category_sp;
+ uint32_t prio_category = UINT32_MAX;
+ for (uint32_t category_id = 0; category_id < num_categories; category_id++) {
+ category_sp = GetCategoryAtIndex(category_id);
+ if (!category_sp->IsEnabled())
+ continue;
+ lldb::TypeValidatorImplSP validator_current_sp(
+ category_sp->GetValidatorForType(type_sp).get());
+ if (validator_current_sp &&
+ (validator_chosen_sp.get() == nullptr ||
+ (prio_category > category_sp->GetEnabledPosition()))) {
+ prio_category = category_sp->GetEnabledPosition();
+ validator_chosen_sp = validator_current_sp;
+ }
+ }
+ return validator_chosen_sp;
+}
+
+void FormatManager::ForEachCategory(TypeCategoryMap::ForEachCallback callback) {
+ m_categories_map.ForEach(callback);
+ std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex);
+ for (const auto &entry : m_language_categories_map) {
+ if (auto category_sp = entry.second->GetCategory()) {
+ if (!callback(category_sp))
+ break;
+ }
+ }
+}
+
+lldb::TypeCategoryImplSP
+FormatManager::GetCategory(ConstString category_name, bool can_create) {
+ if (!category_name)
+ return GetCategory(m_default_category_name);
+ lldb::TypeCategoryImplSP category;
+ if (m_categories_map.Get(category_name, category))
+ return category;
+
+ if (!can_create)
+ return lldb::TypeCategoryImplSP();
+
+ m_categories_map.Add(
+ category_name,
+ lldb::TypeCategoryImplSP(new TypeCategoryImpl(this, category_name)));
+ return GetCategory(category_name);
+}
+
+lldb::Format FormatManager::GetSingleItemFormat(lldb::Format vector_format) {
+ switch (vector_format) {
+ case eFormatVectorOfChar:
+ return eFormatCharArray;
+
+ case eFormatVectorOfSInt8:
+ case eFormatVectorOfSInt16:
+ case eFormatVectorOfSInt32:
+ case eFormatVectorOfSInt64:
+ return eFormatDecimal;
+
+ case eFormatVectorOfUInt8:
+ case eFormatVectorOfUInt16:
+ case eFormatVectorOfUInt32:
+ case eFormatVectorOfUInt64:
+ case eFormatVectorOfUInt128:
+ return eFormatHex;
+
+ case eFormatVectorOfFloat16:
+ case eFormatVectorOfFloat32:
+ case eFormatVectorOfFloat64:
+ return eFormatFloat;
+
+ default:
+ return lldb::eFormatInvalid;
+ }
+}
+
+bool FormatManager::ShouldPrintAsOneLiner(ValueObject &valobj) {
+ // if settings say no oneline whatsoever
+ if (valobj.GetTargetSP().get() &&
+ !valobj.GetTargetSP()->GetDebugger().GetAutoOneLineSummaries())
+ return false; // then don't oneline
+
+ // if this object has a summary, then ask the summary
+ if (valobj.GetSummaryFormat().get() != nullptr)
+ return valobj.GetSummaryFormat()->IsOneLiner();
+
+ // no children, no party
+ if (valobj.GetNumChildren() == 0)
+ return false;
+
+ // ask the type if it has any opinion about this eLazyBoolCalculate == no
+ // opinion; other values should be self explanatory
+ CompilerType compiler_type(valobj.GetCompilerType());
+ if (compiler_type.IsValid()) {
+ switch (compiler_type.ShouldPrintAsOneLiner(&valobj)) {
+ case eLazyBoolNo:
+ return false;
+ case eLazyBoolYes:
+ return true;
+ case eLazyBoolCalculate:
+ break;
+ }
+ }
+
+ size_t total_children_name_len = 0;
+
+ for (size_t idx = 0; idx < valobj.GetNumChildren(); idx++) {
+ bool is_synth_val = false;
+ ValueObjectSP child_sp(valobj.GetChildAtIndex(idx, true));
+ // something is wrong here - bail out
+ if (!child_sp)
+ return false;
+
+ // also ask the child's type if it has any opinion
+ CompilerType child_compiler_type(child_sp->GetCompilerType());
+ if (child_compiler_type.IsValid()) {
+ switch (child_compiler_type.ShouldPrintAsOneLiner(child_sp.get())) {
+ case eLazyBoolYes:
+ // an opinion of yes is only binding for the child, so keep going
+ case eLazyBoolCalculate:
+ break;
+ case eLazyBoolNo:
+ // but if the child says no, then it's a veto on the whole thing
+ return false;
+ }
+ }
+
+ // if we decided to define synthetic children for a type, we probably care
+ // enough to show them, but avoid nesting children in children
+ if (child_sp->GetSyntheticChildren().get() != nullptr) {
+ ValueObjectSP synth_sp(child_sp->GetSyntheticValue());
+ // wait.. wat? just get out of here..
+ if (!synth_sp)
+ return false;
+ // but if we only have them to provide a value, keep going
+ if (!synth_sp->MightHaveChildren() &&
+ synth_sp->DoesProvideSyntheticValue())
+ is_synth_val = true;
+ else
+ return false;
+ }
+
+ total_children_name_len += child_sp->GetName().GetLength();
+
+ // 50 itself is a "randomly" chosen number - the idea is that
+ // overly long structs should not get this treatment
+ // FIXME: maybe make this a user-tweakable setting?
+ if (total_children_name_len > 50)
+ return false;
+
+ // if a summary is there..
+ if (child_sp->GetSummaryFormat()) {
+ // and it wants children, then bail out
+ if (child_sp->GetSummaryFormat()->DoesPrintChildren(child_sp.get()))
+ return false;
+ }
+
+ // if this child has children..
+ if (child_sp->GetNumChildren()) {
+ // ...and no summary...
+ // (if it had a summary and the summary wanted children, we would have
+ // bailed out anyway
+ // so this only makes us bail out if this has no summary and we would
+ // then print children)
+ if (!child_sp->GetSummaryFormat() && !is_synth_val) // but again only do
+ // that if not a
+ // synthetic valued
+ // child
+ return false; // then bail out
+ }
+ }
+ return true;
+}
+
+ConstString FormatManager::GetValidTypeName(ConstString type) {
+ return ::GetValidTypeName_Impl(type);
+}
+
+ConstString FormatManager::GetTypeForCache(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ ValueObjectSP valobj_sp = valobj.GetQualifiedRepresentationIfAvailable(
+ use_dynamic, valobj.IsSynthetic());
+ if (valobj_sp && valobj_sp->GetCompilerType().IsValid()) {
+ if (!valobj_sp->GetCompilerType().IsMeaninglessWithoutDynamicResolution())
+ return valobj_sp->GetQualifiedTypeName();
+ }
+ return ConstString();
+}
+
+std::vector<lldb::LanguageType>
+FormatManager::GetCandidateLanguages(ValueObject &valobj) {
+ lldb::LanguageType lang_type = valobj.GetObjectRuntimeLanguage();
+ return GetCandidateLanguages(lang_type);
+}
+
+std::vector<lldb::LanguageType>
+FormatManager::GetCandidateLanguages(lldb::LanguageType lang_type) {
+ switch (lang_type) {
+ case lldb::eLanguageTypeC:
+ case lldb::eLanguageTypeC89:
+ case lldb::eLanguageTypeC99:
+ case lldb::eLanguageTypeC11:
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeC_plus_plus_03:
+ case lldb::eLanguageTypeC_plus_plus_11:
+ case lldb::eLanguageTypeC_plus_plus_14:
+ return {lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeObjC};
+ default:
+ return {lang_type};
+ }
+}
+
+LanguageCategory *
+FormatManager::GetCategoryForLanguage(lldb::LanguageType lang_type) {
+ std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex);
+ auto iter = m_language_categories_map.find(lang_type),
+ end = m_language_categories_map.end();
+ if (iter != end)
+ return iter->second.get();
+ LanguageCategory *lang_category = new LanguageCategory(lang_type);
+ m_language_categories_map[lang_type] =
+ LanguageCategory::UniquePointer(lang_category);
+ return lang_category;
+}
+
+lldb::TypeFormatImplSP
+FormatManager::GetHardcodedFormat(FormattersMatchData &match_data) {
+ TypeFormatImplSP retval_sp;
+
+ for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
+ if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
+ if (lang_category->GetHardcoded(*this, match_data, retval_sp))
+ break;
+ }
+ }
+
+ return retval_sp;
+}
+
+lldb::TypeFormatImplSP
+FormatManager::GetFormat(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ FormattersMatchData match_data(valobj, use_dynamic);
+
+ TypeFormatImplSP retval;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ if (match_data.GetTypeForCache()) {
+ if (log)
+ log->Printf(
+ "\n\n[FormatManager::GetFormat] Looking into cache for type %s",
+ match_data.GetTypeForCache().AsCString("<invalid>"));
+ if (m_format_cache.GetFormat(match_data.GetTypeForCache(), retval)) {
+ if (log) {
+ log->Printf(
+ "[FormatManager::GetFormat] Cache search success. Returning.");
+ LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
+ m_format_cache.GetCacheHits(),
+ m_format_cache.GetCacheMisses());
+ }
+ return retval;
+ }
+ if (log)
+ log->Printf(
+ "[FormatManager::GetFormat] Cache search failed. Going normal route");
+ }
+
+ retval = m_categories_map.GetFormat(match_data);
+ if (!retval) {
+ if (log)
+ log->Printf("[FormatManager::GetFormat] Search failed. Giving language a "
+ "chance.");
+ for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
+ if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
+ if (lang_category->Get(match_data, retval))
+ break;
+ }
+ }
+ if (retval) {
+ if (log)
+ log->Printf(
+ "[FormatManager::GetFormat] Language search success. Returning.");
+ return retval;
+ }
+ }
+ if (!retval) {
+ if (log)
+ log->Printf("[FormatManager::GetFormat] Search failed. Giving hardcoded "
+ "a chance.");
+ retval = GetHardcodedFormat(match_data);
+ }
+
+ if (match_data.GetTypeForCache() && (!retval || !retval->NonCacheable())) {
+ if (log)
+ log->Printf("[FormatManager::GetFormat] Caching %p for type %s",
+ static_cast<void *>(retval.get()),
+ match_data.GetTypeForCache().AsCString("<invalid>"));
+ m_format_cache.SetFormat(match_data.GetTypeForCache(), retval);
+ }
+ LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
+ m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses());
+ return retval;
+}
+
+lldb::TypeSummaryImplSP
+FormatManager::GetHardcodedSummaryFormat(FormattersMatchData &match_data) {
+ TypeSummaryImplSP retval_sp;
+
+ for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
+ if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
+ if (lang_category->GetHardcoded(*this, match_data, retval_sp))
+ break;
+ }
+ }
+
+ return retval_sp;
+}
+
+lldb::TypeSummaryImplSP
+FormatManager::GetSummaryFormat(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ FormattersMatchData match_data(valobj, use_dynamic);
+
+ TypeSummaryImplSP retval;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ if (match_data.GetTypeForCache()) {
+ if (log)
+ log->Printf("\n\n[FormatManager::GetSummaryFormat] Looking into cache "
+ "for type %s",
+ match_data.GetTypeForCache().AsCString("<invalid>"));
+ if (m_format_cache.GetSummary(match_data.GetTypeForCache(), retval)) {
+ if (log) {
+ log->Printf("[FormatManager::GetSummaryFormat] Cache search success. "
+ "Returning.");
+ LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
+ m_format_cache.GetCacheHits(),
+ m_format_cache.GetCacheMisses());
+ }
+ return retval;
+ }
+ if (log)
+ log->Printf("[FormatManager::GetSummaryFormat] Cache search failed. "
+ "Going normal route");
+ }
+
+ retval = m_categories_map.GetSummaryFormat(match_data);
+ if (!retval) {
+ if (log)
+ log->Printf("[FormatManager::GetSummaryFormat] Search failed. Giving "
+ "language a chance.");
+ for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
+ if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
+ if (lang_category->Get(match_data, retval))
+ break;
+ }
+ }
+ if (retval) {
+ if (log)
+ log->Printf("[FormatManager::GetSummaryFormat] Language search "
+ "success. Returning.");
+ return retval;
+ }
+ }
+ if (!retval) {
+ if (log)
+ log->Printf("[FormatManager::GetSummaryFormat] Search failed. Giving "
+ "hardcoded a chance.");
+ retval = GetHardcodedSummaryFormat(match_data);
+ }
+
+ if (match_data.GetTypeForCache() && (!retval || !retval->NonCacheable())) {
+ if (log)
+ log->Printf("[FormatManager::GetSummaryFormat] Caching %p for type %s",
+ static_cast<void *>(retval.get()),
+ match_data.GetTypeForCache().AsCString("<invalid>"));
+ m_format_cache.SetSummary(match_data.GetTypeForCache(), retval);
+ }
+ LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
+ m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses());
+ return retval;
+}
+
+lldb::SyntheticChildrenSP
+FormatManager::GetHardcodedSyntheticChildren(FormattersMatchData &match_data) {
+ SyntheticChildrenSP retval_sp;
+
+ for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
+ if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
+ if (lang_category->GetHardcoded(*this, match_data, retval_sp))
+ break;
+ }
+ }
+
+ return retval_sp;
+}
+
+lldb::SyntheticChildrenSP
+FormatManager::GetSyntheticChildren(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ FormattersMatchData match_data(valobj, use_dynamic);
+
+ SyntheticChildrenSP retval;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ if (match_data.GetTypeForCache()) {
+ if (log)
+ log->Printf("\n\n[FormatManager::GetSyntheticChildren] Looking into "
+ "cache for type %s",
+ match_data.GetTypeForCache().AsCString("<invalid>"));
+ if (m_format_cache.GetSynthetic(match_data.GetTypeForCache(), retval)) {
+ if (log) {
+ log->Printf("[FormatManager::GetSyntheticChildren] Cache search "
+ "success. Returning.");
+ LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
+ m_format_cache.GetCacheHits(),
+ m_format_cache.GetCacheMisses());
+ }
+ return retval;
+ }
+ if (log)
+ log->Printf("[FormatManager::GetSyntheticChildren] Cache search failed. "
+ "Going normal route");
+ }
+
+ retval = m_categories_map.GetSyntheticChildren(match_data);
+ if (!retval) {
+ if (log)
+ log->Printf("[FormatManager::GetSyntheticChildren] Search failed. Giving "
+ "language a chance.");
+ for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
+ if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
+ if (lang_category->Get(match_data, retval))
+ break;
+ }
+ }
+ if (retval) {
+ if (log)
+ log->Printf("[FormatManager::GetSyntheticChildren] Language search "
+ "success. Returning.");
+ return retval;
+ }
+ }
+ if (!retval) {
+ if (log)
+ log->Printf("[FormatManager::GetSyntheticChildren] Search failed. Giving "
+ "hardcoded a chance.");
+ retval = GetHardcodedSyntheticChildren(match_data);
+ }
+
+ if (match_data.GetTypeForCache() && (!retval || !retval->NonCacheable())) {
+ if (log)
+ log->Printf(
+ "[FormatManager::GetSyntheticChildren] Caching %p for type %s",
+ static_cast<void *>(retval.get()),
+ match_data.GetTypeForCache().AsCString("<invalid>"));
+ m_format_cache.SetSynthetic(match_data.GetTypeForCache(), retval);
+ }
+ LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
+ m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses());
+ return retval;
+}
+
+lldb::TypeValidatorImplSP
+FormatManager::GetValidator(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ FormattersMatchData match_data(valobj, use_dynamic);
+
+ TypeValidatorImplSP retval;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ if (match_data.GetTypeForCache()) {
+ if (log)
+ log->Printf(
+ "\n\n[FormatManager::GetValidator] Looking into cache for type %s",
+ match_data.GetTypeForCache().AsCString("<invalid>"));
+ if (m_format_cache.GetValidator(match_data.GetTypeForCache(), retval)) {
+ if (log) {
+ log->Printf(
+ "[FormatManager::GetValidator] Cache search success. Returning.");
+ LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
+ m_format_cache.GetCacheHits(),
+ m_format_cache.GetCacheMisses());
+ }
+ return retval;
+ }
+ if (log)
+ log->Printf("[FormatManager::GetValidator] Cache search failed. Going "
+ "normal route");
+ }
+
+ retval = m_categories_map.GetValidator(match_data);
+ if (!retval) {
+ if (log)
+ log->Printf("[FormatManager::GetValidator] Search failed. Giving "
+ "language a chance.");
+ for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
+ if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
+ if (lang_category->Get(match_data, retval))
+ break;
+ }
+ }
+ if (retval) {
+ if (log)
+ log->Printf("[FormatManager::GetValidator] Language search success. "
+ "Returning.");
+ return retval;
+ }
+ }
+ if (!retval) {
+ if (log)
+ log->Printf("[FormatManager::GetValidator] Search failed. Giving "
+ "hardcoded a chance.");
+ retval = GetHardcodedValidator(match_data);
+ }
+
+ if (match_data.GetTypeForCache() && (!retval || !retval->NonCacheable())) {
+ if (log)
+ log->Printf("[FormatManager::GetValidator] Caching %p for type %s",
+ static_cast<void *>(retval.get()),
+ match_data.GetTypeForCache().AsCString("<invalid>"));
+ m_format_cache.SetValidator(match_data.GetTypeForCache(), retval);
+ }
+ LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
+ m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses());
+ return retval;
+}
+
+lldb::TypeValidatorImplSP
+FormatManager::GetHardcodedValidator(FormattersMatchData &match_data) {
+ TypeValidatorImplSP retval_sp;
+
+ for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
+ if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
+ if (lang_category->GetHardcoded(*this, match_data, retval_sp))
+ break;
+ }
+ }
+
+ return retval_sp;
+}
+
+FormatManager::FormatManager()
+ : m_last_revision(0), m_format_cache(), m_language_categories_mutex(),
+ m_language_categories_map(), m_named_summaries_map(this),
+ m_categories_map(this), m_default_category_name(ConstString("default")),
+ m_system_category_name(ConstString("system")),
+ m_vectortypes_category_name(ConstString("VectorTypes")) {
+ LoadSystemFormatters();
+ LoadVectorFormatters();
+
+ EnableCategory(m_vectortypes_category_name, TypeCategoryMap::Last,
+ lldb::eLanguageTypeObjC_plus_plus);
+ EnableCategory(m_system_category_name, TypeCategoryMap::Last,
+ lldb::eLanguageTypeObjC_plus_plus);
+}
+
+void FormatManager::LoadSystemFormatters() {
+ TypeSummaryImpl::Flags string_flags;
+ string_flags.SetCascades(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(false)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ TypeSummaryImpl::Flags string_array_flags;
+ string_array_flags.SetCascades(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(true)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ lldb::TypeSummaryImplSP string_format(
+ new StringSummaryFormat(string_flags, "${var%s}"));
+
+ lldb::TypeSummaryImplSP string_array_format(
+ new StringSummaryFormat(string_array_flags, "${var%s}"));
+
+ lldb::RegularExpressionSP any_size_char_arr(
+ new RegularExpression(llvm::StringRef("char \\[[0-9]+\\]")));
+ lldb::RegularExpressionSP any_size_wchar_arr(
+ new RegularExpression(llvm::StringRef("wchar_t \\[[0-9]+\\]")));
+
+ TypeCategoryImpl::SharedPointer sys_category_sp =
+ GetCategory(m_system_category_name);
+
+ sys_category_sp->GetTypeSummariesContainer()->Add(ConstString("char *"),
+ string_format);
+ sys_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("unsigned char *"), string_format);
+ sys_category_sp->GetRegexTypeSummariesContainer()->Add(any_size_char_arr,
+ string_array_format);
+
+ lldb::TypeSummaryImplSP ostype_summary(
+ new StringSummaryFormat(TypeSummaryImpl::Flags()
+ .SetCascades(false)
+ .SetSkipPointers(true)
+ .SetSkipReferences(true)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(false)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false),
+ "${var%O}"));
+
+ sys_category_sp->GetTypeSummariesContainer()->Add(ConstString("OSType"),
+ ostype_summary);
+
+ TypeFormatImpl::Flags fourchar_flags;
+ fourchar_flags.SetCascades(true).SetSkipPointers(true).SetSkipReferences(
+ true);
+
+ AddFormat(sys_category_sp, lldb::eFormatOSType, ConstString("FourCharCode"),
+ fourchar_flags);
+}
+
+void FormatManager::LoadVectorFormatters() {
+ TypeCategoryImpl::SharedPointer vectors_category_sp =
+ GetCategory(m_vectortypes_category_name);
+
+ TypeSummaryImpl::Flags vector_flags;
+ vector_flags.SetCascades(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(false)
+ .SetShowMembersOneLiner(true)
+ .SetHideItemNames(true);
+
+ AddStringSummary(vectors_category_sp, "${var.uint128}",
+ ConstString("builtin_type_vec128"), vector_flags);
+
+ AddStringSummary(vectors_category_sp, "", ConstString("float [4]"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("int32_t [4]"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("int16_t [8]"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("vDouble"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("vFloat"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("vSInt8"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("vSInt16"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("vSInt32"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("vUInt16"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("vUInt8"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("vUInt16"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("vUInt32"),
+ vector_flags);
+ AddStringSummary(vectors_category_sp, "", ConstString("vBool32"),
+ vector_flags);
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/FormattersHelpers.cpp b/contrib/llvm-project/lldb/source/DataFormatters/FormattersHelpers.cpp
new file mode 100644
index 000000000000..8f007df03faa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/FormattersHelpers.cpp
@@ -0,0 +1,145 @@
+//===-- FormattersHelpers.cpp -------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+
+
+
+#include "lldb/DataFormatters/FormattersHelpers.h"
+
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegularExpression.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+void lldb_private::formatters::AddFormat(
+ TypeCategoryImpl::SharedPointer category_sp, lldb::Format format,
+ ConstString type_name, TypeFormatImpl::Flags flags, bool regex) {
+ lldb::TypeFormatImplSP format_sp(new TypeFormatImpl_Format(format, flags));
+
+ if (regex)
+ category_sp->GetRegexTypeFormatsContainer()->Add(
+ RegularExpressionSP(new RegularExpression(type_name.GetStringRef())),
+ format_sp);
+ else
+ category_sp->GetTypeFormatsContainer()->Add(type_name, format_sp);
+}
+
+void lldb_private::formatters::AddSummary(
+ TypeCategoryImpl::SharedPointer category_sp, TypeSummaryImplSP summary_sp,
+ ConstString type_name, bool regex) {
+ if (regex)
+ category_sp->GetRegexTypeSummariesContainer()->Add(
+ RegularExpressionSP(new RegularExpression(type_name.GetStringRef())),
+ summary_sp);
+ else
+ category_sp->GetTypeSummariesContainer()->Add(type_name, summary_sp);
+}
+
+void lldb_private::formatters::AddStringSummary(
+ TypeCategoryImpl::SharedPointer category_sp, const char *string,
+ ConstString type_name, TypeSummaryImpl::Flags flags, bool regex) {
+ lldb::TypeSummaryImplSP summary_sp(new StringSummaryFormat(flags, string));
+
+ if (regex)
+ category_sp->GetRegexTypeSummariesContainer()->Add(
+ RegularExpressionSP(new RegularExpression(type_name.GetStringRef())),
+ summary_sp);
+ else
+ category_sp->GetTypeSummariesContainer()->Add(type_name, summary_sp);
+}
+
+void lldb_private::formatters::AddOneLineSummary(
+ TypeCategoryImpl::SharedPointer category_sp, ConstString type_name,
+ TypeSummaryImpl::Flags flags, bool regex) {
+ flags.SetShowMembersOneLiner(true);
+ lldb::TypeSummaryImplSP summary_sp(new StringSummaryFormat(flags, ""));
+
+ if (regex)
+ category_sp->GetRegexTypeSummariesContainer()->Add(
+ RegularExpressionSP(new RegularExpression(type_name.GetStringRef())),
+ summary_sp);
+ else
+ category_sp->GetTypeSummariesContainer()->Add(type_name, summary_sp);
+}
+
+void lldb_private::formatters::AddCXXSummary(
+ TypeCategoryImpl::SharedPointer category_sp,
+ CXXFunctionSummaryFormat::Callback funct, const char *description,
+ ConstString type_name, TypeSummaryImpl::Flags flags, bool regex) {
+ lldb::TypeSummaryImplSP summary_sp(
+ new CXXFunctionSummaryFormat(flags, funct, description));
+ if (regex)
+ category_sp->GetRegexTypeSummariesContainer()->Add(
+ RegularExpressionSP(new RegularExpression(type_name.GetStringRef())),
+ summary_sp);
+ else
+ category_sp->GetTypeSummariesContainer()->Add(type_name, summary_sp);
+}
+
+void lldb_private::formatters::AddCXXSynthetic(
+ TypeCategoryImpl::SharedPointer category_sp,
+ CXXSyntheticChildren::CreateFrontEndCallback generator,
+ const char *description, ConstString type_name,
+ ScriptedSyntheticChildren::Flags flags, bool regex) {
+ lldb::SyntheticChildrenSP synth_sp(
+ new CXXSyntheticChildren(flags, description, generator));
+ if (regex)
+ category_sp->GetRegexTypeSyntheticsContainer()->Add(
+ RegularExpressionSP(new RegularExpression(type_name.GetStringRef())),
+ synth_sp);
+ else
+ category_sp->GetTypeSyntheticsContainer()->Add(type_name, synth_sp);
+}
+
+void lldb_private::formatters::AddFilter(
+ TypeCategoryImpl::SharedPointer category_sp,
+ std::vector<std::string> children, const char *description,
+ ConstString type_name, ScriptedSyntheticChildren::Flags flags, bool regex) {
+ TypeFilterImplSP filter_sp(new TypeFilterImpl(flags));
+ for (auto child : children)
+ filter_sp->AddExpressionPath(child);
+ if (regex)
+ category_sp->GetRegexTypeFiltersContainer()->Add(
+ RegularExpressionSP(new RegularExpression(type_name.GetStringRef())),
+ filter_sp);
+ else
+ category_sp->GetTypeFiltersContainer()->Add(type_name, filter_sp);
+}
+
+size_t lldb_private::formatters::ExtractIndexFromString(const char *item_name) {
+ if (!item_name || !*item_name)
+ return UINT32_MAX;
+ if (*item_name != '[')
+ return UINT32_MAX;
+ item_name++;
+ char *endptr = nullptr;
+ unsigned long int idx = ::strtoul(item_name, &endptr, 0);
+ if (idx == 0 && endptr == item_name)
+ return UINT32_MAX;
+ if (idx == ULONG_MAX)
+ return UINT32_MAX;
+ return idx;
+}
+
+lldb::addr_t
+lldb_private::formatters::GetArrayAddressOrPointerValue(ValueObject &valobj) {
+ lldb::addr_t data_addr = LLDB_INVALID_ADDRESS;
+
+ if (valobj.IsPointerType())
+ data_addr = valobj.GetValueAsUnsigned(0);
+ else if (valobj.IsArrayType())
+ data_addr = valobj.GetAddressOf();
+
+ return data_addr;
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/LanguageCategory.cpp b/contrib/llvm-project/lldb/source/DataFormatters/LanguageCategory.cpp
new file mode 100644
index 000000000000..969b02556548
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/LanguageCategory.cpp
@@ -0,0 +1,227 @@
+//===-- LanguageCategory.cpp ---------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/LanguageCategory.h"
+
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/DataFormatters/TypeCategory.h"
+#include "lldb/DataFormatters/TypeFormat.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/DataFormatters/TypeValidator.h"
+#include "lldb/Target/Language.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+LanguageCategory::LanguageCategory(lldb::LanguageType lang_type)
+ : m_category_sp(), m_hardcoded_formats(), m_hardcoded_summaries(),
+ m_hardcoded_synthetics(), m_hardcoded_validators(), m_format_cache(),
+ m_enabled(false) {
+ if (Language *language_plugin = Language::FindPlugin(lang_type)) {
+ m_category_sp = language_plugin->GetFormatters();
+ m_hardcoded_formats = language_plugin->GetHardcodedFormats();
+ m_hardcoded_summaries = language_plugin->GetHardcodedSummaries();
+ m_hardcoded_synthetics = language_plugin->GetHardcodedSynthetics();
+ m_hardcoded_validators = language_plugin->GetHardcodedValidators();
+ }
+ Enable();
+}
+
+bool LanguageCategory::Get(FormattersMatchData &match_data,
+ lldb::TypeFormatImplSP &format_sp) {
+ if (!m_category_sp)
+ return false;
+
+ if (!IsEnabled())
+ return false;
+
+ if (match_data.GetTypeForCache()) {
+ if (m_format_cache.GetFormat(match_data.GetTypeForCache(), format_sp))
+ return format_sp.get() != nullptr;
+ }
+
+ ValueObject &valobj(match_data.GetValueObject());
+ bool result =
+ m_category_sp->Get(valobj, match_data.GetMatchesVector(), format_sp);
+ if (match_data.GetTypeForCache() &&
+ (!format_sp || !format_sp->NonCacheable())) {
+ m_format_cache.SetFormat(match_data.GetTypeForCache(), format_sp);
+ }
+ return result;
+}
+
+bool LanguageCategory::Get(FormattersMatchData &match_data,
+ lldb::TypeSummaryImplSP &format_sp) {
+ if (!m_category_sp)
+ return false;
+
+ if (!IsEnabled())
+ return false;
+
+ if (match_data.GetTypeForCache()) {
+ if (m_format_cache.GetSummary(match_data.GetTypeForCache(), format_sp))
+ return format_sp.get() != nullptr;
+ }
+
+ ValueObject &valobj(match_data.GetValueObject());
+ bool result =
+ m_category_sp->Get(valobj, match_data.GetMatchesVector(), format_sp);
+ if (match_data.GetTypeForCache() &&
+ (!format_sp || !format_sp->NonCacheable())) {
+ m_format_cache.SetSummary(match_data.GetTypeForCache(), format_sp);
+ }
+ return result;
+}
+
+bool LanguageCategory::Get(FormattersMatchData &match_data,
+ lldb::SyntheticChildrenSP &format_sp) {
+ if (!m_category_sp)
+ return false;
+
+ if (!IsEnabled())
+ return false;
+
+ if (match_data.GetTypeForCache()) {
+ if (m_format_cache.GetSynthetic(match_data.GetTypeForCache(), format_sp))
+ return format_sp.get() != nullptr;
+ }
+
+ ValueObject &valobj(match_data.GetValueObject());
+ bool result =
+ m_category_sp->Get(valobj, match_data.GetMatchesVector(), format_sp);
+ if (match_data.GetTypeForCache() &&
+ (!format_sp || !format_sp->NonCacheable())) {
+ m_format_cache.SetSynthetic(match_data.GetTypeForCache(), format_sp);
+ }
+ return result;
+}
+
+bool LanguageCategory::Get(FormattersMatchData &match_data,
+ lldb::TypeValidatorImplSP &format_sp) {
+ if (!m_category_sp)
+ return false;
+
+ if (!IsEnabled())
+ return false;
+
+ if (match_data.GetTypeForCache()) {
+ if (m_format_cache.GetValidator(match_data.GetTypeForCache(), format_sp))
+ return format_sp.get() != nullptr;
+ }
+
+ ValueObject &valobj(match_data.GetValueObject());
+ bool result =
+ m_category_sp->Get(valobj, match_data.GetMatchesVector(), format_sp);
+ if (match_data.GetTypeForCache() &&
+ (!format_sp || !format_sp->NonCacheable())) {
+ m_format_cache.SetValidator(match_data.GetTypeForCache(), format_sp);
+ }
+ return result;
+}
+
+bool LanguageCategory::GetHardcoded(FormatManager &fmt_mgr,
+ FormattersMatchData &match_data,
+ lldb::TypeFormatImplSP &format_sp) {
+ if (!IsEnabled())
+ return false;
+
+ ValueObject &valobj(match_data.GetValueObject());
+ lldb::DynamicValueType use_dynamic(match_data.GetDynamicValueType());
+
+ for (auto &candidate : m_hardcoded_formats) {
+ if ((format_sp = candidate(valobj, use_dynamic, fmt_mgr)))
+ break;
+ }
+ if (match_data.GetTypeForCache() &&
+ (!format_sp || !format_sp->NonCacheable())) {
+ m_format_cache.SetFormat(match_data.GetTypeForCache(), format_sp);
+ }
+ return format_sp.get() != nullptr;
+}
+
+bool LanguageCategory::GetHardcoded(FormatManager &fmt_mgr,
+ FormattersMatchData &match_data,
+ lldb::TypeSummaryImplSP &format_sp) {
+ if (!IsEnabled())
+ return false;
+
+ ValueObject &valobj(match_data.GetValueObject());
+ lldb::DynamicValueType use_dynamic(match_data.GetDynamicValueType());
+
+ for (auto &candidate : m_hardcoded_summaries) {
+ if ((format_sp = candidate(valobj, use_dynamic, fmt_mgr)))
+ break;
+ }
+ if (match_data.GetTypeForCache() &&
+ (!format_sp || !format_sp->NonCacheable())) {
+ m_format_cache.SetSummary(match_data.GetTypeForCache(), format_sp);
+ }
+ return format_sp.get() != nullptr;
+}
+
+bool LanguageCategory::GetHardcoded(FormatManager &fmt_mgr,
+ FormattersMatchData &match_data,
+ lldb::SyntheticChildrenSP &format_sp) {
+ if (!IsEnabled())
+ return false;
+
+ ValueObject &valobj(match_data.GetValueObject());
+ lldb::DynamicValueType use_dynamic(match_data.GetDynamicValueType());
+
+ for (auto &candidate : m_hardcoded_synthetics) {
+ if ((format_sp = candidate(valobj, use_dynamic, fmt_mgr)))
+ break;
+ }
+ if (match_data.GetTypeForCache() &&
+ (!format_sp || !format_sp->NonCacheable())) {
+ m_format_cache.SetSynthetic(match_data.GetTypeForCache(), format_sp);
+ }
+ return format_sp.get() != nullptr;
+}
+
+bool LanguageCategory::GetHardcoded(FormatManager &fmt_mgr,
+ FormattersMatchData &match_data,
+ lldb::TypeValidatorImplSP &format_sp) {
+ if (!IsEnabled())
+ return false;
+
+ ValueObject &valobj(match_data.GetValueObject());
+ lldb::DynamicValueType use_dynamic(match_data.GetDynamicValueType());
+
+ for (auto &candidate : m_hardcoded_validators) {
+ if ((format_sp = candidate(valobj, use_dynamic, fmt_mgr)))
+ break;
+ }
+ if (match_data.GetTypeForCache() &&
+ (!format_sp || !format_sp->NonCacheable())) {
+ m_format_cache.SetValidator(match_data.GetTypeForCache(), format_sp);
+ }
+ return format_sp.get() != nullptr;
+}
+
+lldb::TypeCategoryImplSP LanguageCategory::GetCategory() const {
+ return m_category_sp;
+}
+
+FormatCache &LanguageCategory::GetFormatCache() { return m_format_cache; }
+
+void LanguageCategory::Enable() {
+ if (m_category_sp)
+ m_category_sp->Enable(true, TypeCategoryMap::Default);
+ m_enabled = true;
+}
+
+void LanguageCategory::Disable() {
+ if (m_category_sp)
+ m_category_sp->Disable();
+ m_enabled = false;
+}
+
+bool LanguageCategory::IsEnabled() { return m_enabled; }
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/StringPrinter.cpp b/contrib/llvm-project/lldb/source/DataFormatters/StringPrinter.cpp
new file mode 100644
index 000000000000..27d649bfc370
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/StringPrinter.cpp
@@ -0,0 +1,651 @@
+//===-- StringPrinter.cpp ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/StringPrinter.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/Support/ConvertUTF.h"
+
+#include <ctype.h>
+#include <locale>
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+// we define this for all values of type but only implement it for those we
+// care about that's good because we get linker errors for any unsupported type
+template <lldb_private::formatters::StringPrinter::StringElementType type>
+static StringPrinter::StringPrinterBufferPointer<>
+GetPrintableImpl(uint8_t *buffer, uint8_t *buffer_end, uint8_t *&next);
+
+// mimic isprint() for Unicode codepoints
+static bool isprint(char32_t codepoint) {
+ if (codepoint <= 0x1F || codepoint == 0x7F) // C0
+ {
+ return false;
+ }
+ if (codepoint >= 0x80 && codepoint <= 0x9F) // C1
+ {
+ return false;
+ }
+ if (codepoint == 0x2028 || codepoint == 0x2029) // line/paragraph separators
+ {
+ return false;
+ }
+ if (codepoint == 0x200E || codepoint == 0x200F ||
+ (codepoint >= 0x202A &&
+ codepoint <= 0x202E)) // bidirectional text control
+ {
+ return false;
+ }
+ if (codepoint >= 0xFFF9 &&
+ codepoint <= 0xFFFF) // interlinears and generally specials
+ {
+ return false;
+ }
+ return true;
+}
+
+template <>
+StringPrinter::StringPrinterBufferPointer<>
+GetPrintableImpl<StringPrinter::StringElementType::ASCII>(uint8_t *buffer,
+ uint8_t *buffer_end,
+ uint8_t *&next) {
+ StringPrinter::StringPrinterBufferPointer<> retval = {nullptr};
+
+ switch (*buffer) {
+ case 0:
+ retval = {"\\0", 2};
+ break;
+ case '\a':
+ retval = {"\\a", 2};
+ break;
+ case '\b':
+ retval = {"\\b", 2};
+ break;
+ case '\f':
+ retval = {"\\f", 2};
+ break;
+ case '\n':
+ retval = {"\\n", 2};
+ break;
+ case '\r':
+ retval = {"\\r", 2};
+ break;
+ case '\t':
+ retval = {"\\t", 2};
+ break;
+ case '\v':
+ retval = {"\\v", 2};
+ break;
+ case '\"':
+ retval = {"\\\"", 2};
+ break;
+ case '\\':
+ retval = {"\\\\", 2};
+ break;
+ default:
+ if (isprint(*buffer))
+ retval = {buffer, 1};
+ else {
+ uint8_t *data = new uint8_t[5];
+ sprintf((char *)data, "\\x%02x", *buffer);
+ retval = {data, 4, [](const uint8_t *c) { delete[] c; }};
+ break;
+ }
+ }
+
+ next = buffer + 1;
+ return retval;
+}
+
+static char32_t ConvertUTF8ToCodePoint(unsigned char c0, unsigned char c1) {
+ return (c0 - 192) * 64 + (c1 - 128);
+}
+static char32_t ConvertUTF8ToCodePoint(unsigned char c0, unsigned char c1,
+ unsigned char c2) {
+ return (c0 - 224) * 4096 + (c1 - 128) * 64 + (c2 - 128);
+}
+static char32_t ConvertUTF8ToCodePoint(unsigned char c0, unsigned char c1,
+ unsigned char c2, unsigned char c3) {
+ return (c0 - 240) * 262144 + (c2 - 128) * 4096 + (c2 - 128) * 64 + (c3 - 128);
+}
+
+template <>
+StringPrinter::StringPrinterBufferPointer<>
+GetPrintableImpl<StringPrinter::StringElementType::UTF8>(uint8_t *buffer,
+ uint8_t *buffer_end,
+ uint8_t *&next) {
+ StringPrinter::StringPrinterBufferPointer<> retval{nullptr};
+
+ unsigned utf8_encoded_len = llvm::getNumBytesForUTF8(*buffer);
+
+ if (1u + std::distance(buffer, buffer_end) < utf8_encoded_len) {
+ // I don't have enough bytes - print whatever I have left
+ retval = {buffer, static_cast<size_t>(1 + buffer_end - buffer)};
+ next = buffer_end + 1;
+ return retval;
+ }
+
+ char32_t codepoint = 0;
+ switch (utf8_encoded_len) {
+ case 1:
+ // this is just an ASCII byte - ask ASCII
+ return GetPrintableImpl<StringPrinter::StringElementType::ASCII>(
+ buffer, buffer_end, next);
+ case 2:
+ codepoint = ConvertUTF8ToCodePoint((unsigned char)*buffer,
+ (unsigned char)*(buffer + 1));
+ break;
+ case 3:
+ codepoint = ConvertUTF8ToCodePoint((unsigned char)*buffer,
+ (unsigned char)*(buffer + 1),
+ (unsigned char)*(buffer + 2));
+ break;
+ case 4:
+ codepoint = ConvertUTF8ToCodePoint(
+ (unsigned char)*buffer, (unsigned char)*(buffer + 1),
+ (unsigned char)*(buffer + 2), (unsigned char)*(buffer + 3));
+ break;
+ default:
+ // this is probably some bogus non-character thing just print it as-is and
+ // hope to sync up again soon
+ retval = {buffer, 1};
+ next = buffer + 1;
+ return retval;
+ }
+
+ if (codepoint) {
+ switch (codepoint) {
+ case 0:
+ retval = {"\\0", 2};
+ break;
+ case '\a':
+ retval = {"\\a", 2};
+ break;
+ case '\b':
+ retval = {"\\b", 2};
+ break;
+ case '\f':
+ retval = {"\\f", 2};
+ break;
+ case '\n':
+ retval = {"\\n", 2};
+ break;
+ case '\r':
+ retval = {"\\r", 2};
+ break;
+ case '\t':
+ retval = {"\\t", 2};
+ break;
+ case '\v':
+ retval = {"\\v", 2};
+ break;
+ case '\"':
+ retval = {"\\\"", 2};
+ break;
+ case '\\':
+ retval = {"\\\\", 2};
+ break;
+ default:
+ if (isprint(codepoint))
+ retval = {buffer, utf8_encoded_len};
+ else {
+ uint8_t *data = new uint8_t[11];
+ sprintf((char *)data, "\\U%08x", (unsigned)codepoint);
+ retval = {data, 10, [](const uint8_t *c) { delete[] c; }};
+ break;
+ }
+ }
+
+ next = buffer + utf8_encoded_len;
+ return retval;
+ }
+
+ // this should not happen - but just in case.. try to resync at some point
+ retval = {buffer, 1};
+ next = buffer + 1;
+ return retval;
+}
+
+// Given a sequence of bytes, this function returns: a sequence of bytes to
+// actually print out + a length the following unscanned position of the buffer
+// is in next
+static StringPrinter::StringPrinterBufferPointer<>
+GetPrintable(StringPrinter::StringElementType type, uint8_t *buffer,
+ uint8_t *buffer_end, uint8_t *&next) {
+ if (!buffer)
+ return {nullptr};
+
+ switch (type) {
+ case StringPrinter::StringElementType::ASCII:
+ return GetPrintableImpl<StringPrinter::StringElementType::ASCII>(
+ buffer, buffer_end, next);
+ case StringPrinter::StringElementType::UTF8:
+ return GetPrintableImpl<StringPrinter::StringElementType::UTF8>(
+ buffer, buffer_end, next);
+ default:
+ return {nullptr};
+ }
+}
+
+StringPrinter::EscapingHelper
+StringPrinter::GetDefaultEscapingHelper(GetPrintableElementType elem_type) {
+ switch (elem_type) {
+ case GetPrintableElementType::UTF8:
+ return [](uint8_t *buffer, uint8_t *buffer_end,
+ uint8_t *&next) -> StringPrinter::StringPrinterBufferPointer<> {
+ return GetPrintable(StringPrinter::StringElementType::UTF8, buffer,
+ buffer_end, next);
+ };
+ case GetPrintableElementType::ASCII:
+ return [](uint8_t *buffer, uint8_t *buffer_end,
+ uint8_t *&next) -> StringPrinter::StringPrinterBufferPointer<> {
+ return GetPrintable(StringPrinter::StringElementType::ASCII, buffer,
+ buffer_end, next);
+ };
+ }
+ llvm_unreachable("bad element type");
+}
+
+// use this call if you already have an LLDB-side buffer for the data
+template <typename SourceDataType>
+static bool DumpUTFBufferToStream(
+ llvm::ConversionResult (*ConvertFunction)(const SourceDataType **,
+ const SourceDataType *,
+ llvm::UTF8 **, llvm::UTF8 *,
+ llvm::ConversionFlags),
+ const StringPrinter::ReadBufferAndDumpToStreamOptions &dump_options) {
+ Stream &stream(*dump_options.GetStream());
+ if (dump_options.GetPrefixToken() != nullptr)
+ stream.Printf("%s", dump_options.GetPrefixToken());
+ if (dump_options.GetQuote() != 0)
+ stream.Printf("%c", dump_options.GetQuote());
+ auto data(dump_options.GetData());
+ auto source_size(dump_options.GetSourceSize());
+ if (data.GetByteSize() && data.GetDataStart() && data.GetDataEnd()) {
+ const int bufferSPSize = data.GetByteSize();
+ if (dump_options.GetSourceSize() == 0) {
+ const int origin_encoding = 8 * sizeof(SourceDataType);
+ source_size = bufferSPSize / (origin_encoding / 4);
+ }
+
+ const SourceDataType *data_ptr =
+ (const SourceDataType *)data.GetDataStart();
+ const SourceDataType *data_end_ptr = data_ptr + source_size;
+
+ const bool zero_is_terminator = dump_options.GetBinaryZeroIsTerminator();
+
+ if (zero_is_terminator) {
+ while (data_ptr < data_end_ptr) {
+ if (!*data_ptr) {
+ data_end_ptr = data_ptr;
+ break;
+ }
+ data_ptr++;
+ }
+
+ data_ptr = (const SourceDataType *)data.GetDataStart();
+ }
+
+ lldb::DataBufferSP utf8_data_buffer_sp;
+ llvm::UTF8 *utf8_data_ptr = nullptr;
+ llvm::UTF8 *utf8_data_end_ptr = nullptr;
+
+ if (ConvertFunction) {
+ utf8_data_buffer_sp =
+ std::make_shared<DataBufferHeap>(4 * bufferSPSize, 0);
+ utf8_data_ptr = (llvm::UTF8 *)utf8_data_buffer_sp->GetBytes();
+ utf8_data_end_ptr = utf8_data_ptr + utf8_data_buffer_sp->GetByteSize();
+ ConvertFunction(&data_ptr, data_end_ptr, &utf8_data_ptr,
+ utf8_data_end_ptr, llvm::lenientConversion);
+ if (!zero_is_terminator)
+ utf8_data_end_ptr = utf8_data_ptr;
+ // needed because the ConvertFunction will change the value of the
+ // data_ptr.
+ utf8_data_ptr =
+ (llvm::UTF8 *)utf8_data_buffer_sp->GetBytes();
+ } else {
+ // just copy the pointers - the cast is necessary to make the compiler
+ // happy but this should only happen if we are reading UTF8 data
+ utf8_data_ptr = const_cast<llvm::UTF8 *>(
+ reinterpret_cast<const llvm::UTF8 *>(data_ptr));
+ utf8_data_end_ptr = const_cast<llvm::UTF8 *>(
+ reinterpret_cast<const llvm::UTF8 *>(data_end_ptr));
+ }
+
+ const bool escape_non_printables = dump_options.GetEscapeNonPrintables();
+ lldb_private::formatters::StringPrinter::EscapingHelper escaping_callback;
+ if (escape_non_printables) {
+ if (Language *language = Language::FindPlugin(dump_options.GetLanguage()))
+ escaping_callback = language->GetStringPrinterEscapingHelper(
+ lldb_private::formatters::StringPrinter::GetPrintableElementType::
+ UTF8);
+ else
+ escaping_callback =
+ lldb_private::formatters::StringPrinter::GetDefaultEscapingHelper(
+ lldb_private::formatters::StringPrinter::
+ GetPrintableElementType::UTF8);
+ }
+
+ // since we tend to accept partial data (and even partially malformed data)
+ // we might end up with no NULL terminator before the end_ptr hence we need
+ // to take a slower route and ensure we stay within boundaries
+ for (; utf8_data_ptr < utf8_data_end_ptr;) {
+ if (zero_is_terminator && !*utf8_data_ptr)
+ break;
+
+ if (escape_non_printables) {
+ uint8_t *next_data = nullptr;
+ auto printable =
+ escaping_callback(utf8_data_ptr, utf8_data_end_ptr, next_data);
+ auto printable_bytes = printable.GetBytes();
+ auto printable_size = printable.GetSize();
+ if (!printable_bytes || !next_data) {
+ // GetPrintable() failed on us - print one byte in a desperate resync
+ // attempt
+ printable_bytes = utf8_data_ptr;
+ printable_size = 1;
+ next_data = utf8_data_ptr + 1;
+ }
+ for (unsigned c = 0; c < printable_size; c++)
+ stream.Printf("%c", *(printable_bytes + c));
+ utf8_data_ptr = (uint8_t *)next_data;
+ } else {
+ stream.Printf("%c", *utf8_data_ptr);
+ utf8_data_ptr++;
+ }
+ }
+ }
+ if (dump_options.GetQuote() != 0)
+ stream.Printf("%c", dump_options.GetQuote());
+ if (dump_options.GetSuffixToken() != nullptr)
+ stream.Printf("%s", dump_options.GetSuffixToken());
+ if (dump_options.GetIsTruncated())
+ stream.Printf("...");
+ return true;
+}
+
+lldb_private::formatters::StringPrinter::ReadStringAndDumpToStreamOptions::
+ ReadStringAndDumpToStreamOptions(ValueObject &valobj)
+ : ReadStringAndDumpToStreamOptions() {
+ SetEscapeNonPrintables(
+ valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables());
+}
+
+lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions::
+ ReadBufferAndDumpToStreamOptions(ValueObject &valobj)
+ : ReadBufferAndDumpToStreamOptions() {
+ SetEscapeNonPrintables(
+ valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables());
+}
+
+lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions::
+ ReadBufferAndDumpToStreamOptions(
+ const ReadStringAndDumpToStreamOptions &options)
+ : ReadBufferAndDumpToStreamOptions() {
+ SetStream(options.GetStream());
+ SetPrefixToken(options.GetPrefixToken());
+ SetSuffixToken(options.GetSuffixToken());
+ SetQuote(options.GetQuote());
+ SetEscapeNonPrintables(options.GetEscapeNonPrintables());
+ SetBinaryZeroIsTerminator(options.GetBinaryZeroIsTerminator());
+ SetLanguage(options.GetLanguage());
+}
+
+namespace lldb_private {
+
+namespace formatters {
+
+template <>
+bool StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::ASCII>(
+ const ReadStringAndDumpToStreamOptions &options) {
+ assert(options.GetStream() && "need a Stream to print the string to");
+ Status my_error;
+
+ ProcessSP process_sp(options.GetProcessSP());
+
+ if (process_sp.get() == nullptr || options.GetLocation() == 0)
+ return false;
+
+ size_t size;
+ const auto max_size = process_sp->GetTarget().GetMaximumSizeOfStringSummary();
+ bool is_truncated = false;
+
+ if (options.GetSourceSize() == 0)
+ size = max_size;
+ else if (!options.GetIgnoreMaxLength()) {
+ size = options.GetSourceSize();
+ if (size > max_size) {
+ size = max_size;
+ is_truncated = true;
+ }
+ } else
+ size = options.GetSourceSize();
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(size, 0));
+
+ process_sp->ReadCStringFromMemory(
+ options.GetLocation(), (char *)buffer_sp->GetBytes(), size, my_error);
+
+ if (my_error.Fail())
+ return false;
+
+ const char *prefix_token = options.GetPrefixToken();
+ char quote = options.GetQuote();
+
+ if (prefix_token != nullptr)
+ options.GetStream()->Printf("%s%c", prefix_token, quote);
+ else if (quote != 0)
+ options.GetStream()->Printf("%c", quote);
+
+ uint8_t *data_end = buffer_sp->GetBytes() + buffer_sp->GetByteSize();
+
+ const bool escape_non_printables = options.GetEscapeNonPrintables();
+ lldb_private::formatters::StringPrinter::EscapingHelper escaping_callback;
+ if (escape_non_printables) {
+ if (Language *language = Language::FindPlugin(options.GetLanguage()))
+ escaping_callback = language->GetStringPrinterEscapingHelper(
+ lldb_private::formatters::StringPrinter::GetPrintableElementType::
+ ASCII);
+ else
+ escaping_callback =
+ lldb_private::formatters::StringPrinter::GetDefaultEscapingHelper(
+ lldb_private::formatters::StringPrinter::GetPrintableElementType::
+ ASCII);
+ }
+
+ // since we tend to accept partial data (and even partially malformed data)
+ // we might end up with no NULL terminator before the end_ptr hence we need
+ // to take a slower route and ensure we stay within boundaries
+ for (uint8_t *data = buffer_sp->GetBytes(); *data && (data < data_end);) {
+ if (escape_non_printables) {
+ uint8_t *next_data = nullptr;
+ auto printable = escaping_callback(data, data_end, next_data);
+ auto printable_bytes = printable.GetBytes();
+ auto printable_size = printable.GetSize();
+ if (!printable_bytes || !next_data) {
+ // GetPrintable() failed on us - print one byte in a desperate resync
+ // attempt
+ printable_bytes = data;
+ printable_size = 1;
+ next_data = data + 1;
+ }
+ for (unsigned c = 0; c < printable_size; c++)
+ options.GetStream()->Printf("%c", *(printable_bytes + c));
+ data = (uint8_t *)next_data;
+ } else {
+ options.GetStream()->Printf("%c", *data);
+ data++;
+ }
+ }
+
+ const char *suffix_token = options.GetSuffixToken();
+
+ if (suffix_token != nullptr)
+ options.GetStream()->Printf("%c%s", quote, suffix_token);
+ else if (quote != 0)
+ options.GetStream()->Printf("%c", quote);
+
+ if (is_truncated)
+ options.GetStream()->Printf("...");
+
+ return true;
+}
+
+template <typename SourceDataType>
+static bool ReadUTFBufferAndDumpToStream(
+ const StringPrinter::ReadStringAndDumpToStreamOptions &options,
+ llvm::ConversionResult (*ConvertFunction)(const SourceDataType **,
+ const SourceDataType *,
+ llvm::UTF8 **, llvm::UTF8 *,
+ llvm::ConversionFlags)) {
+ assert(options.GetStream() && "need a Stream to print the string to");
+
+ if (options.GetLocation() == 0 ||
+ options.GetLocation() == LLDB_INVALID_ADDRESS)
+ return false;
+
+ lldb::ProcessSP process_sp(options.GetProcessSP());
+
+ if (!process_sp)
+ return false;
+
+ const int type_width = sizeof(SourceDataType);
+ const int origin_encoding = 8 * type_width;
+ if (origin_encoding != 8 && origin_encoding != 16 && origin_encoding != 32)
+ return false;
+ // if not UTF8, I need a conversion function to return proper UTF8
+ if (origin_encoding != 8 && !ConvertFunction)
+ return false;
+
+ if (!options.GetStream())
+ return false;
+
+ uint32_t sourceSize = options.GetSourceSize();
+ bool needs_zero_terminator = options.GetNeedsZeroTermination();
+
+ bool is_truncated = false;
+ const auto max_size = process_sp->GetTarget().GetMaximumSizeOfStringSummary();
+
+ if (!sourceSize) {
+ sourceSize = max_size;
+ needs_zero_terminator = true;
+ } else if (!options.GetIgnoreMaxLength()) {
+ if (sourceSize > max_size) {
+ sourceSize = max_size;
+ is_truncated = true;
+ }
+ }
+
+ const int bufferSPSize = sourceSize * type_width;
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize, 0));
+
+ if (!buffer_sp->GetBytes())
+ return false;
+
+ Status error;
+ char *buffer = reinterpret_cast<char *>(buffer_sp->GetBytes());
+
+ if (needs_zero_terminator)
+ process_sp->ReadStringFromMemory(options.GetLocation(), buffer,
+ bufferSPSize, error, type_width);
+ else
+ process_sp->ReadMemoryFromInferior(options.GetLocation(),
+ (char *)buffer_sp->GetBytes(),
+ bufferSPSize, error);
+
+ if (error.Fail()) {
+ options.GetStream()->Printf("unable to read data");
+ return true;
+ }
+
+ DataExtractor data(buffer_sp, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions dump_options(options);
+ dump_options.SetData(data);
+ dump_options.SetSourceSize(sourceSize);
+ dump_options.SetIsTruncated(is_truncated);
+
+ return DumpUTFBufferToStream(ConvertFunction, dump_options);
+}
+
+template <>
+bool StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(
+ const ReadStringAndDumpToStreamOptions &options) {
+ return ReadUTFBufferAndDumpToStream<llvm::UTF8>(options, nullptr);
+}
+
+template <>
+bool StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(
+ const ReadStringAndDumpToStreamOptions &options) {
+ return ReadUTFBufferAndDumpToStream<llvm::UTF16>(options,
+ llvm::ConvertUTF16toUTF8);
+}
+
+template <>
+bool StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(
+ const ReadStringAndDumpToStreamOptions &options) {
+ return ReadUTFBufferAndDumpToStream<llvm::UTF32>(options,
+ llvm::ConvertUTF32toUTF8);
+}
+
+template <>
+bool StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(
+ const ReadBufferAndDumpToStreamOptions &options) {
+ assert(options.GetStream() && "need a Stream to print the string to");
+
+ return DumpUTFBufferToStream<llvm::UTF8>(nullptr, options);
+}
+
+template <>
+bool StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::ASCII>(
+ const ReadBufferAndDumpToStreamOptions &options) {
+ // treat ASCII the same as UTF8
+ // FIXME: can we optimize ASCII some more?
+ return ReadBufferAndDumpToStream<StringElementType::UTF8>(options);
+}
+
+template <>
+bool StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(
+ const ReadBufferAndDumpToStreamOptions &options) {
+ assert(options.GetStream() && "need a Stream to print the string to");
+
+ return DumpUTFBufferToStream(llvm::ConvertUTF16toUTF8, options);
+}
+
+template <>
+bool StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(
+ const ReadBufferAndDumpToStreamOptions &options) {
+ assert(options.GetStream() && "need a Stream to print the string to");
+
+ return DumpUTFBufferToStream(llvm::ConvertUTF32toUTF8, options);
+}
+
+} // namespace formatters
+
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/TypeCategory.cpp b/contrib/llvm-project/lldb/source/DataFormatters/TypeCategory.cpp
new file mode 100644
index 000000000000..fed2dfb3c7c5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/TypeCategory.cpp
@@ -0,0 +1,595 @@
+//===-- TypeCategory.cpp -----------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/TypeCategory.h"
+#include "lldb/Target/Language.h"
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+TypeCategoryImpl::TypeCategoryImpl(
+ IFormatChangeListener *clist, ConstString name,
+ std::initializer_list<lldb::LanguageType> langs)
+ : m_format_cont("format", "regex-format", clist),
+ m_summary_cont("summary", "regex-summary", clist),
+ m_filter_cont("filter", "regex-filter", clist),
+ m_synth_cont("synth", "regex-synth", clist),
+ m_validator_cont("validator", "regex-validator", clist), m_enabled(false),
+ m_change_listener(clist), m_mutex(), m_name(name), m_languages() {
+ for (const lldb::LanguageType lang : langs)
+ AddLanguage(lang);
+}
+
+static bool IsApplicable(lldb::LanguageType category_lang,
+ lldb::LanguageType valobj_lang) {
+ switch (category_lang) {
+ // Unless we know better, allow only exact equality.
+ default:
+ return category_lang == valobj_lang;
+
+ // the C family, we consider it as one
+ case eLanguageTypeC89:
+ case eLanguageTypeC:
+ case eLanguageTypeC99:
+ return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC ||
+ valobj_lang == eLanguageTypeC99;
+
+ // ObjC knows about C and itself
+ case eLanguageTypeObjC:
+ return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC ||
+ valobj_lang == eLanguageTypeC99 || valobj_lang == eLanguageTypeObjC;
+
+ // C++ knows about C and C++
+ case eLanguageTypeC_plus_plus:
+ return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC ||
+ valobj_lang == eLanguageTypeC99 ||
+ valobj_lang == eLanguageTypeC_plus_plus;
+
+ // ObjC++ knows about C,C++,ObjC and ObjC++
+ case eLanguageTypeObjC_plus_plus:
+ return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC ||
+ valobj_lang == eLanguageTypeC99 ||
+ valobj_lang == eLanguageTypeC_plus_plus ||
+ valobj_lang == eLanguageTypeObjC;
+
+ // Categories with unspecified language match everything.
+ case eLanguageTypeUnknown:
+ return true;
+ }
+}
+
+bool TypeCategoryImpl::IsApplicable(ValueObject &valobj) {
+ lldb::LanguageType valobj_lang = valobj.GetObjectRuntimeLanguage();
+ for (size_t idx = 0; idx < GetNumLanguages(); idx++) {
+ const lldb::LanguageType category_lang = GetLanguageAtIndex(idx);
+ if (::IsApplicable(category_lang, valobj_lang))
+ return true;
+ }
+ return false;
+}
+
+size_t TypeCategoryImpl::GetNumLanguages() {
+ if (m_languages.empty())
+ return 1;
+ return m_languages.size();
+}
+
+lldb::LanguageType TypeCategoryImpl::GetLanguageAtIndex(size_t idx) {
+ if (m_languages.empty())
+ return lldb::eLanguageTypeUnknown;
+ return m_languages[idx];
+}
+
+void TypeCategoryImpl::AddLanguage(lldb::LanguageType lang) {
+ m_languages.push_back(lang);
+}
+
+bool TypeCategoryImpl::HasLanguage(lldb::LanguageType lang) {
+ const auto iter = std::find(m_languages.begin(), m_languages.end(), lang),
+ end = m_languages.end();
+ return (iter != end);
+}
+
+bool TypeCategoryImpl::Get(ValueObject &valobj,
+ const FormattersMatchVector &candidates,
+ lldb::TypeFormatImplSP &entry, uint32_t *reason) {
+ if (!IsEnabled() || !IsApplicable(valobj))
+ return false;
+ if (GetTypeFormatsContainer()->Get(candidates, entry, reason))
+ return true;
+ bool regex = GetRegexTypeFormatsContainer()->Get(candidates, entry, reason);
+ if (regex && reason)
+ *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionSummary;
+ return regex;
+}
+
+bool TypeCategoryImpl::Get(ValueObject &valobj,
+ const FormattersMatchVector &candidates,
+ lldb::TypeSummaryImplSP &entry, uint32_t *reason) {
+ if (!IsEnabled() || !IsApplicable(valobj))
+ return false;
+ if (GetTypeSummariesContainer()->Get(candidates, entry, reason))
+ return true;
+ bool regex = GetRegexTypeSummariesContainer()->Get(candidates, entry, reason);
+ if (regex && reason)
+ *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionSummary;
+ return regex;
+}
+
+bool TypeCategoryImpl::Get(ValueObject &valobj,
+ const FormattersMatchVector &candidates,
+ lldb::SyntheticChildrenSP &entry, uint32_t *reason) {
+ if (!IsEnabled() || !IsApplicable(valobj))
+ return false;
+ TypeFilterImpl::SharedPointer filter_sp;
+ uint32_t reason_filter = 0;
+ bool regex_filter = false;
+ // first find both Filter and Synth, and then check which is most recent
+
+ if (!GetTypeFiltersContainer()->Get(candidates, filter_sp, &reason_filter))
+ regex_filter = GetRegexTypeFiltersContainer()->Get(candidates, filter_sp,
+ &reason_filter);
+
+ bool regex_synth = false;
+ uint32_t reason_synth = 0;
+ bool pick_synth = false;
+ ScriptedSyntheticChildren::SharedPointer synth;
+ if (!GetTypeSyntheticsContainer()->Get(candidates, synth, &reason_synth))
+ regex_synth = GetRegexTypeSyntheticsContainer()->Get(candidates, synth,
+ &reason_synth);
+ if (!filter_sp.get() && !synth.get())
+ return false;
+ else if (!filter_sp.get() && synth.get())
+ pick_synth = true;
+
+ else if (filter_sp.get() && !synth.get())
+ pick_synth = false;
+
+ else /*if (filter_sp.get() && synth.get())*/
+ {
+ pick_synth = filter_sp->GetRevision() <= synth->GetRevision();
+ }
+ if (pick_synth) {
+ if (regex_synth && reason)
+ *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionFilter;
+ entry = synth;
+ return true;
+ } else {
+ if (regex_filter && reason)
+ *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionFilter;
+ entry = filter_sp;
+ return true;
+ }
+ return false;
+}
+
+bool TypeCategoryImpl::Get(ValueObject &valobj,
+ const FormattersMatchVector &candidates,
+ lldb::TypeValidatorImplSP &entry, uint32_t *reason) {
+ if (!IsEnabled())
+ return false;
+ if (GetTypeValidatorsContainer()->Get(candidates, entry, reason))
+ return true;
+ bool regex =
+ GetRegexTypeValidatorsContainer()->Get(candidates, entry, reason);
+ if (regex && reason)
+ *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionSummary;
+ return regex;
+}
+
+void TypeCategoryImpl::Clear(FormatCategoryItems items) {
+ if ((items & eFormatCategoryItemValue) == eFormatCategoryItemValue)
+ GetTypeFormatsContainer()->Clear();
+ if ((items & eFormatCategoryItemRegexValue) == eFormatCategoryItemRegexValue)
+ GetRegexTypeFormatsContainer()->Clear();
+
+ if ((items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary)
+ GetTypeSummariesContainer()->Clear();
+ if ((items & eFormatCategoryItemRegexSummary) ==
+ eFormatCategoryItemRegexSummary)
+ GetRegexTypeSummariesContainer()->Clear();
+
+ if ((items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter)
+ GetTypeFiltersContainer()->Clear();
+ if ((items & eFormatCategoryItemRegexFilter) ==
+ eFormatCategoryItemRegexFilter)
+ GetRegexTypeFiltersContainer()->Clear();
+
+ if ((items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth)
+ GetTypeSyntheticsContainer()->Clear();
+ if ((items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth)
+ GetRegexTypeSyntheticsContainer()->Clear();
+
+ if ((items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator)
+ GetTypeValidatorsContainer()->Clear();
+ if ((items & eFormatCategoryItemRegexValidator) ==
+ eFormatCategoryItemRegexValidator)
+ GetRegexTypeValidatorsContainer()->Clear();
+}
+
+bool TypeCategoryImpl::Delete(ConstString name, FormatCategoryItems items) {
+ bool success = false;
+
+ if ((items & eFormatCategoryItemValue) == eFormatCategoryItemValue)
+ success = GetTypeFormatsContainer()->Delete(name) || success;
+ if ((items & eFormatCategoryItemRegexValue) == eFormatCategoryItemRegexValue)
+ success = GetRegexTypeFormatsContainer()->Delete(name) || success;
+
+ if ((items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary)
+ success = GetTypeSummariesContainer()->Delete(name) || success;
+ if ((items & eFormatCategoryItemRegexSummary) ==
+ eFormatCategoryItemRegexSummary)
+ success = GetRegexTypeSummariesContainer()->Delete(name) || success;
+
+ if ((items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter)
+ success = GetTypeFiltersContainer()->Delete(name) || success;
+ if ((items & eFormatCategoryItemRegexFilter) ==
+ eFormatCategoryItemRegexFilter)
+ success = GetRegexTypeFiltersContainer()->Delete(name) || success;
+
+ if ((items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth)
+ success = GetTypeSyntheticsContainer()->Delete(name) || success;
+ if ((items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth)
+ success = GetRegexTypeSyntheticsContainer()->Delete(name) || success;
+
+ if ((items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator)
+ success = GetTypeValidatorsContainer()->Delete(name) || success;
+ if ((items & eFormatCategoryItemRegexValidator) ==
+ eFormatCategoryItemRegexValidator)
+ success = GetRegexTypeValidatorsContainer()->Delete(name) || success;
+
+ return success;
+}
+
+uint32_t TypeCategoryImpl::GetCount(FormatCategoryItems items) {
+ uint32_t count = 0;
+
+ if ((items & eFormatCategoryItemValue) == eFormatCategoryItemValue)
+ count += GetTypeFormatsContainer()->GetCount();
+ if ((items & eFormatCategoryItemRegexValue) == eFormatCategoryItemRegexValue)
+ count += GetRegexTypeFormatsContainer()->GetCount();
+
+ if ((items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary)
+ count += GetTypeSummariesContainer()->GetCount();
+ if ((items & eFormatCategoryItemRegexSummary) ==
+ eFormatCategoryItemRegexSummary)
+ count += GetRegexTypeSummariesContainer()->GetCount();
+
+ if ((items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter)
+ count += GetTypeFiltersContainer()->GetCount();
+ if ((items & eFormatCategoryItemRegexFilter) ==
+ eFormatCategoryItemRegexFilter)
+ count += GetRegexTypeFiltersContainer()->GetCount();
+
+ if ((items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth)
+ count += GetTypeSyntheticsContainer()->GetCount();
+ if ((items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth)
+ count += GetRegexTypeSyntheticsContainer()->GetCount();
+
+ if ((items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator)
+ count += GetTypeValidatorsContainer()->GetCount();
+ if ((items & eFormatCategoryItemRegexValidator) ==
+ eFormatCategoryItemRegexValidator)
+ count += GetRegexTypeValidatorsContainer()->GetCount();
+
+ return count;
+}
+
+bool TypeCategoryImpl::AnyMatches(ConstString type_name,
+ FormatCategoryItems items, bool only_enabled,
+ const char **matching_category,
+ FormatCategoryItems *matching_type) {
+ if (!IsEnabled() && only_enabled)
+ return false;
+
+ lldb::TypeFormatImplSP format_sp;
+ lldb::TypeSummaryImplSP summary_sp;
+ TypeFilterImpl::SharedPointer filter_sp;
+ ScriptedSyntheticChildren::SharedPointer synth_sp;
+ TypeValidatorImpl::SharedPointer validator_sp;
+
+ if ((items & eFormatCategoryItemValue) == eFormatCategoryItemValue) {
+ if (GetTypeFormatsContainer()->Get(type_name, format_sp)) {
+ if (matching_category)
+ *matching_category = m_name.GetCString();
+ if (matching_type)
+ *matching_type = eFormatCategoryItemValue;
+ return true;
+ }
+ }
+ if ((items & eFormatCategoryItemRegexValue) ==
+ eFormatCategoryItemRegexValue) {
+ if (GetRegexTypeFormatsContainer()->Get(type_name, format_sp)) {
+ if (matching_category)
+ *matching_category = m_name.GetCString();
+ if (matching_type)
+ *matching_type = eFormatCategoryItemRegexValue;
+ return true;
+ }
+ }
+
+ if ((items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary) {
+ if (GetTypeSummariesContainer()->Get(type_name, summary_sp)) {
+ if (matching_category)
+ *matching_category = m_name.GetCString();
+ if (matching_type)
+ *matching_type = eFormatCategoryItemSummary;
+ return true;
+ }
+ }
+ if ((items & eFormatCategoryItemRegexSummary) ==
+ eFormatCategoryItemRegexSummary) {
+ if (GetRegexTypeSummariesContainer()->Get(type_name, summary_sp)) {
+ if (matching_category)
+ *matching_category = m_name.GetCString();
+ if (matching_type)
+ *matching_type = eFormatCategoryItemRegexSummary;
+ return true;
+ }
+ }
+
+ if ((items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter) {
+ if (GetTypeFiltersContainer()->Get(type_name, filter_sp)) {
+ if (matching_category)
+ *matching_category = m_name.GetCString();
+ if (matching_type)
+ *matching_type = eFormatCategoryItemFilter;
+ return true;
+ }
+ }
+ if ((items & eFormatCategoryItemRegexFilter) ==
+ eFormatCategoryItemRegexFilter) {
+ if (GetRegexTypeFiltersContainer()->Get(type_name, filter_sp)) {
+ if (matching_category)
+ *matching_category = m_name.GetCString();
+ if (matching_type)
+ *matching_type = eFormatCategoryItemRegexFilter;
+ return true;
+ }
+ }
+
+ if ((items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth) {
+ if (GetTypeSyntheticsContainer()->Get(type_name, synth_sp)) {
+ if (matching_category)
+ *matching_category = m_name.GetCString();
+ if (matching_type)
+ *matching_type = eFormatCategoryItemSynth;
+ return true;
+ }
+ }
+ if ((items & eFormatCategoryItemRegexSynth) ==
+ eFormatCategoryItemRegexSynth) {
+ if (GetRegexTypeSyntheticsContainer()->Get(type_name, synth_sp)) {
+ if (matching_category)
+ *matching_category = m_name.GetCString();
+ if (matching_type)
+ *matching_type = eFormatCategoryItemRegexSynth;
+ return true;
+ }
+ }
+
+ if ((items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator) {
+ if (GetTypeValidatorsContainer()->Get(type_name, validator_sp)) {
+ if (matching_category)
+ *matching_category = m_name.GetCString();
+ if (matching_type)
+ *matching_type = eFormatCategoryItemValidator;
+ return true;
+ }
+ }
+ if ((items & eFormatCategoryItemRegexValidator) ==
+ eFormatCategoryItemRegexValidator) {
+ if (GetRegexTypeValidatorsContainer()->Get(type_name, validator_sp)) {
+ if (matching_category)
+ *matching_category = m_name.GetCString();
+ if (matching_type)
+ *matching_type = eFormatCategoryItemRegexValidator;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+TypeCategoryImpl::FormatContainer::MapValueType
+TypeCategoryImpl::GetFormatForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ FormatContainer::MapValueType retval;
+
+ if (type_sp) {
+ if (type_sp->IsRegex())
+ GetRegexTypeFormatsContainer()->GetExact(ConstString(type_sp->GetName()),
+ retval);
+ else
+ GetTypeFormatsContainer()->GetExact(ConstString(type_sp->GetName()),
+ retval);
+ }
+
+ return retval;
+}
+
+TypeCategoryImpl::SummaryContainer::MapValueType
+TypeCategoryImpl::GetSummaryForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ SummaryContainer::MapValueType retval;
+
+ if (type_sp) {
+ if (type_sp->IsRegex())
+ GetRegexTypeSummariesContainer()->GetExact(
+ ConstString(type_sp->GetName()), retval);
+ else
+ GetTypeSummariesContainer()->GetExact(ConstString(type_sp->GetName()),
+ retval);
+ }
+
+ return retval;
+}
+
+TypeCategoryImpl::FilterContainer::MapValueType
+TypeCategoryImpl::GetFilterForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ FilterContainer::MapValueType retval;
+
+ if (type_sp) {
+ if (type_sp->IsRegex())
+ GetRegexTypeFiltersContainer()->GetExact(ConstString(type_sp->GetName()),
+ retval);
+ else
+ GetTypeFiltersContainer()->GetExact(ConstString(type_sp->GetName()),
+ retval);
+ }
+
+ return retval;
+}
+
+TypeCategoryImpl::SynthContainer::MapValueType
+TypeCategoryImpl::GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ SynthContainer::MapValueType retval;
+
+ if (type_sp) {
+ if (type_sp->IsRegex())
+ GetRegexTypeSyntheticsContainer()->GetExact(
+ ConstString(type_sp->GetName()), retval);
+ else
+ GetTypeSyntheticsContainer()->GetExact(ConstString(type_sp->GetName()),
+ retval);
+ }
+
+ return retval;
+}
+
+TypeCategoryImpl::ValidatorContainer::MapValueType
+TypeCategoryImpl::GetValidatorForType(lldb::TypeNameSpecifierImplSP type_sp) {
+ ValidatorContainer::MapValueType retval;
+
+ if (type_sp) {
+ if (type_sp->IsRegex())
+ GetRegexTypeValidatorsContainer()->GetExact(
+ ConstString(type_sp->GetName()), retval);
+ else
+ GetTypeValidatorsContainer()->GetExact(ConstString(type_sp->GetName()),
+ retval);
+ }
+
+ return retval;
+}
+
+lldb::TypeNameSpecifierImplSP
+TypeCategoryImpl::GetTypeNameSpecifierForSummaryAtIndex(size_t index) {
+ if (index < GetTypeSummariesContainer()->GetCount())
+ return GetTypeSummariesContainer()->GetTypeNameSpecifierAtIndex(index);
+ else
+ return GetRegexTypeSummariesContainer()->GetTypeNameSpecifierAtIndex(
+ index - GetTypeSummariesContainer()->GetCount());
+}
+
+TypeCategoryImpl::FormatContainer::MapValueType
+TypeCategoryImpl::GetFormatAtIndex(size_t index) {
+ if (index < GetTypeFormatsContainer()->GetCount())
+ return GetTypeFormatsContainer()->GetAtIndex(index);
+ else
+ return GetRegexTypeFormatsContainer()->GetAtIndex(
+ index - GetTypeFormatsContainer()->GetCount());
+}
+
+TypeCategoryImpl::SummaryContainer::MapValueType
+TypeCategoryImpl::GetSummaryAtIndex(size_t index) {
+ if (index < GetTypeSummariesContainer()->GetCount())
+ return GetTypeSummariesContainer()->GetAtIndex(index);
+ else
+ return GetRegexTypeSummariesContainer()->GetAtIndex(
+ index - GetTypeSummariesContainer()->GetCount());
+}
+
+TypeCategoryImpl::FilterContainer::MapValueType
+TypeCategoryImpl::GetFilterAtIndex(size_t index) {
+ if (index < GetTypeFiltersContainer()->GetCount())
+ return GetTypeFiltersContainer()->GetAtIndex(index);
+ else
+ return GetRegexTypeFiltersContainer()->GetAtIndex(
+ index - GetTypeFiltersContainer()->GetCount());
+}
+
+lldb::TypeNameSpecifierImplSP
+TypeCategoryImpl::GetTypeNameSpecifierForFormatAtIndex(size_t index) {
+ if (index < GetTypeFormatsContainer()->GetCount())
+ return GetTypeFormatsContainer()->GetTypeNameSpecifierAtIndex(index);
+ else
+ return GetRegexTypeFormatsContainer()->GetTypeNameSpecifierAtIndex(
+ index - GetTypeFormatsContainer()->GetCount());
+}
+
+lldb::TypeNameSpecifierImplSP
+TypeCategoryImpl::GetTypeNameSpecifierForFilterAtIndex(size_t index) {
+ if (index < GetTypeFiltersContainer()->GetCount())
+ return GetTypeFiltersContainer()->GetTypeNameSpecifierAtIndex(index);
+ else
+ return GetRegexTypeFiltersContainer()->GetTypeNameSpecifierAtIndex(
+ index - GetTypeFiltersContainer()->GetCount());
+}
+
+TypeCategoryImpl::SynthContainer::MapValueType
+TypeCategoryImpl::GetSyntheticAtIndex(size_t index) {
+ if (index < GetTypeSyntheticsContainer()->GetCount())
+ return GetTypeSyntheticsContainer()->GetAtIndex(index);
+ else
+ return GetRegexTypeSyntheticsContainer()->GetAtIndex(
+ index - GetTypeSyntheticsContainer()->GetCount());
+}
+
+lldb::TypeNameSpecifierImplSP
+TypeCategoryImpl::GetTypeNameSpecifierForSyntheticAtIndex(size_t index) {
+ if (index < GetTypeSyntheticsContainer()->GetCount())
+ return GetTypeSyntheticsContainer()->GetTypeNameSpecifierAtIndex(index);
+ else
+ return GetRegexTypeSyntheticsContainer()->GetTypeNameSpecifierAtIndex(
+ index - GetTypeSyntheticsContainer()->GetCount());
+}
+
+TypeCategoryImpl::ValidatorContainer::MapValueType
+TypeCategoryImpl::GetValidatorAtIndex(size_t index) {
+ if (index < GetTypeValidatorsContainer()->GetCount())
+ return GetTypeValidatorsContainer()->GetAtIndex(index);
+ else
+ return GetRegexTypeValidatorsContainer()->GetAtIndex(
+ index - GetTypeValidatorsContainer()->GetCount());
+}
+
+lldb::TypeNameSpecifierImplSP
+TypeCategoryImpl::GetTypeNameSpecifierForValidatorAtIndex(size_t index) {
+ if (index < GetTypeValidatorsContainer()->GetCount())
+ return GetTypeValidatorsContainer()->GetTypeNameSpecifierAtIndex(index);
+ else
+ return GetRegexTypeValidatorsContainer()->GetTypeNameSpecifierAtIndex(
+ index - GetTypeValidatorsContainer()->GetCount());
+}
+
+void TypeCategoryImpl::Enable(bool value, uint32_t position) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if ((m_enabled = value))
+ m_enabled_position = position;
+ if (m_change_listener)
+ m_change_listener->Changed();
+}
+
+std::string TypeCategoryImpl::GetDescription() {
+ StreamString stream;
+ stream.Printf("%s (%s", GetName(), (IsEnabled() ? "enabled" : "disabled"));
+ StreamString lang_stream;
+ lang_stream.Printf(", applicable for language(s): ");
+ bool print_lang = false;
+ for (size_t idx = 0; idx < GetNumLanguages(); idx++) {
+ const lldb::LanguageType lang = GetLanguageAtIndex(idx);
+ if (lang != lldb::eLanguageTypeUnknown)
+ print_lang = true;
+ lang_stream.Printf("%s%s", Language::GetNameForLanguageType(lang),
+ idx + 1 < GetNumLanguages() ? ", " : "");
+ }
+ if (print_lang)
+ stream.PutCString(lang_stream.GetString());
+ stream.PutChar(')');
+ return stream.GetString();
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/TypeCategoryMap.cpp b/contrib/llvm-project/lldb/source/DataFormatters/TypeCategoryMap.cpp
new file mode 100644
index 000000000000..69757c9844e1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/TypeCategoryMap.cpp
@@ -0,0 +1,374 @@
+//===-- TypeCategoryMap.cpp ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/TypeCategoryMap.h"
+
+#include "lldb/DataFormatters/FormatClasses.h"
+#include "lldb/Utility/Log.h"
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+TypeCategoryMap::TypeCategoryMap(IFormatChangeListener *lst)
+ : m_map_mutex(), listener(lst), m_map(), m_active_categories() {
+ ConstString default_cs("default");
+ lldb::TypeCategoryImplSP default_sp =
+ lldb::TypeCategoryImplSP(new TypeCategoryImpl(listener, default_cs));
+ Add(default_cs, default_sp);
+ Enable(default_cs, First);
+}
+
+void TypeCategoryMap::Add(KeyType name, const ValueSP &entry) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+ m_map[name] = entry;
+ if (listener)
+ listener->Changed();
+}
+
+bool TypeCategoryMap::Delete(KeyType name) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+ MapIterator iter = m_map.find(name);
+ if (iter == m_map.end())
+ return false;
+ m_map.erase(name);
+ Disable(name);
+ if (listener)
+ listener->Changed();
+ return true;
+}
+
+bool TypeCategoryMap::Enable(KeyType category_name, Position pos) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+ ValueSP category;
+ if (!Get(category_name, category))
+ return false;
+ return Enable(category, pos);
+}
+
+bool TypeCategoryMap::Disable(KeyType category_name) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+ ValueSP category;
+ if (!Get(category_name, category))
+ return false;
+ return Disable(category);
+}
+
+bool TypeCategoryMap::Enable(ValueSP category, Position pos) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+ if (category.get()) {
+ Position pos_w = pos;
+ if (pos == First || m_active_categories.size() == 0)
+ m_active_categories.push_front(category);
+ else if (pos == Last || pos == m_active_categories.size())
+ m_active_categories.push_back(category);
+ else if (pos < m_active_categories.size()) {
+ ActiveCategoriesList::iterator iter = m_active_categories.begin();
+ while (pos_w) {
+ pos_w--, iter++;
+ }
+ m_active_categories.insert(iter, category);
+ } else
+ return false;
+ category->Enable(true, pos);
+ return true;
+ }
+ return false;
+}
+
+bool TypeCategoryMap::Disable(ValueSP category) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+ if (category.get()) {
+ m_active_categories.remove_if(delete_matching_categories(category));
+ category->Disable();
+ return true;
+ }
+ return false;
+}
+
+void TypeCategoryMap::EnableAllCategories() {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+ std::vector<ValueSP> sorted_categories(m_map.size(), ValueSP());
+ MapType::iterator iter = m_map.begin(), end = m_map.end();
+ for (; iter != end; ++iter) {
+ if (iter->second->IsEnabled())
+ continue;
+ auto pos = iter->second->GetLastEnabledPosition();
+ if (pos >= sorted_categories.size()) {
+ auto iter = std::find_if(
+ sorted_categories.begin(), sorted_categories.end(),
+ [](const ValueSP &sp) -> bool { return sp.get() == nullptr; });
+ pos = std::distance(sorted_categories.begin(), iter);
+ }
+ sorted_categories.at(pos) = iter->second;
+ }
+ decltype(sorted_categories)::iterator viter = sorted_categories.begin(),
+ vend = sorted_categories.end();
+ for (; viter != vend; viter++)
+ if (*viter)
+ Enable(*viter, Last);
+}
+
+void TypeCategoryMap::DisableAllCategories() {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+ for (Position p = First; !m_active_categories.empty(); p++) {
+ m_active_categories.front()->SetEnabledPosition(p);
+ Disable(m_active_categories.front());
+ }
+}
+
+void TypeCategoryMap::Clear() {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+ m_map.clear();
+ m_active_categories.clear();
+ if (listener)
+ listener->Changed();
+}
+
+bool TypeCategoryMap::Get(KeyType name, ValueSP &entry) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+ MapIterator iter = m_map.find(name);
+ if (iter == m_map.end())
+ return false;
+ entry = iter->second;
+ return true;
+}
+
+bool TypeCategoryMap::Get(uint32_t pos, ValueSP &entry) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+ MapIterator iter = m_map.begin();
+ MapIterator end = m_map.end();
+ while (pos > 0) {
+ iter++;
+ pos--;
+ if (iter == end)
+ return false;
+ }
+ entry = iter->second;
+ return false;
+}
+
+bool TypeCategoryMap::AnyMatches(
+ ConstString type_name, TypeCategoryImpl::FormatCategoryItems items,
+ bool only_enabled, const char **matching_category,
+ TypeCategoryImpl::FormatCategoryItems *matching_type) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+
+ MapIterator pos, end = m_map.end();
+ for (pos = m_map.begin(); pos != end; pos++) {
+ if (pos->second->AnyMatches(type_name, items, only_enabled,
+ matching_category, matching_type))
+ return true;
+ }
+ return false;
+}
+
+lldb::TypeFormatImplSP
+TypeCategoryMap::GetFormat(FormattersMatchData &match_data) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+
+ uint32_t reason_why;
+ ActiveCategoriesIterator begin, end = m_active_categories.end();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+
+ if (log) {
+ for (auto match : match_data.GetMatchesVector()) {
+ log->Printf(
+ "[CategoryMap::GetFormat] candidate match = %s %s %s %s reason = "
+ "%" PRIu32,
+ match.GetTypeName().GetCString(),
+ match.DidStripPointer() ? "strip-pointers" : "no-strip-pointers",
+ match.DidStripReference() ? "strip-reference" : "no-strip-reference",
+ match.DidStripTypedef() ? "strip-typedef" : "no-strip-typedef",
+ match.GetReason());
+ }
+ }
+
+ for (begin = m_active_categories.begin(); begin != end; begin++) {
+ lldb::TypeCategoryImplSP category_sp = *begin;
+ lldb::TypeFormatImplSP current_format;
+ if (log)
+ log->Printf("[TypeCategoryMap::GetFormat] Trying to use category %s",
+ category_sp->GetName());
+ if (!category_sp->Get(match_data.GetValueObject(),
+ match_data.GetMatchesVector(), current_format,
+ &reason_why))
+ continue;
+ return current_format;
+ }
+ if (log)
+ log->Printf(
+ "[TypeCategoryMap::GetFormat] nothing found - returning empty SP");
+ return lldb::TypeFormatImplSP();
+}
+
+lldb::TypeSummaryImplSP
+TypeCategoryMap::GetSummaryFormat(FormattersMatchData &match_data) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+
+ uint32_t reason_why;
+ ActiveCategoriesIterator begin, end = m_active_categories.end();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+
+ if (log) {
+ for (auto match : match_data.GetMatchesVector()) {
+ log->Printf(
+ "[CategoryMap::GetSummaryFormat] candidate match = %s %s %s %s "
+ "reason = %" PRIu32,
+ match.GetTypeName().GetCString(),
+ match.DidStripPointer() ? "strip-pointers" : "no-strip-pointers",
+ match.DidStripReference() ? "strip-reference" : "no-strip-reference",
+ match.DidStripTypedef() ? "strip-typedef" : "no-strip-typedef",
+ match.GetReason());
+ }
+ }
+
+ for (begin = m_active_categories.begin(); begin != end; begin++) {
+ lldb::TypeCategoryImplSP category_sp = *begin;
+ lldb::TypeSummaryImplSP current_format;
+ if (log)
+ log->Printf("[CategoryMap::GetSummaryFormat] Trying to use category %s",
+ category_sp->GetName());
+ if (!category_sp->Get(match_data.GetValueObject(),
+ match_data.GetMatchesVector(), current_format,
+ &reason_why))
+ continue;
+ return current_format;
+ }
+ if (log)
+ log->Printf(
+ "[CategoryMap::GetSummaryFormat] nothing found - returning empty SP");
+ return lldb::TypeSummaryImplSP();
+}
+
+lldb::SyntheticChildrenSP
+TypeCategoryMap::GetSyntheticChildren(FormattersMatchData &match_data) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+
+ uint32_t reason_why;
+
+ ActiveCategoriesIterator begin, end = m_active_categories.end();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+
+ if (log) {
+ for (auto match : match_data.GetMatchesVector()) {
+ log->Printf(
+ "[CategoryMap::GetSyntheticChildren] candidate match = %s %s %s %s "
+ "reason = %" PRIu32,
+ match.GetTypeName().GetCString(),
+ match.DidStripPointer() ? "strip-pointers" : "no-strip-pointers",
+ match.DidStripReference() ? "strip-reference" : "no-strip-reference",
+ match.DidStripTypedef() ? "strip-typedef" : "no-strip-typedef",
+ match.GetReason());
+ }
+ }
+
+ for (begin = m_active_categories.begin(); begin != end; begin++) {
+ lldb::TypeCategoryImplSP category_sp = *begin;
+ lldb::SyntheticChildrenSP current_format;
+ if (log)
+ log->Printf(
+ "[CategoryMap::GetSyntheticChildren] Trying to use category %s",
+ category_sp->GetName());
+ if (!category_sp->Get(match_data.GetValueObject(),
+ match_data.GetMatchesVector(), current_format,
+ &reason_why))
+ continue;
+ return current_format;
+ }
+ if (log)
+ log->Printf("[CategoryMap::GetSyntheticChildren] nothing found - returning "
+ "empty SP");
+ return lldb::SyntheticChildrenSP();
+}
+
+lldb::TypeValidatorImplSP
+TypeCategoryMap::GetValidator(FormattersMatchData &match_data) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+
+ uint32_t reason_why;
+ ActiveCategoriesIterator begin, end = m_active_categories.end();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+
+ if (log) {
+ for (auto match : match_data.GetMatchesVector()) {
+ log->Printf(
+ "[CategoryMap::GetValidator] candidate match = %s %s %s %s reason = "
+ "%" PRIu32,
+ match.GetTypeName().GetCString(),
+ match.DidStripPointer() ? "strip-pointers" : "no-strip-pointers",
+ match.DidStripReference() ? "strip-reference" : "no-strip-reference",
+ match.DidStripTypedef() ? "strip-typedef" : "no-strip-typedef",
+ match.GetReason());
+ }
+ }
+
+ for (begin = m_active_categories.begin(); begin != end; begin++) {
+ lldb::TypeCategoryImplSP category_sp = *begin;
+ lldb::TypeValidatorImplSP current_format;
+ if (log)
+ log->Printf("[CategoryMap::GetValidator] Trying to use category %s",
+ category_sp->GetName());
+ if (!category_sp->Get(match_data.GetValueObject(),
+ match_data.GetMatchesVector(), current_format,
+ &reason_why))
+ continue;
+ return current_format;
+ }
+ if (log)
+ log->Printf(
+ "[CategoryMap::GetValidator] nothing found - returning empty SP");
+ return lldb::TypeValidatorImplSP();
+}
+
+void TypeCategoryMap::ForEach(ForEachCallback callback) {
+ if (callback) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+
+ // loop through enabled categories in respective order
+ {
+ ActiveCategoriesIterator begin, end = m_active_categories.end();
+ for (begin = m_active_categories.begin(); begin != end; begin++) {
+ lldb::TypeCategoryImplSP category = *begin;
+ if (!callback(category))
+ break;
+ }
+ }
+
+ // loop through disabled categories in just any order
+ {
+ MapIterator pos, end = m_map.end();
+ for (pos = m_map.begin(); pos != end; pos++) {
+ if (pos->second->IsEnabled())
+ continue;
+ if (!callback(pos->second))
+ break;
+ }
+ }
+ }
+}
+
+TypeCategoryImplSP TypeCategoryMap::GetAtIndex(uint32_t index) {
+ std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
+
+ if (index < m_map.size()) {
+ MapIterator pos, end = m_map.end();
+ for (pos = m_map.begin(); pos != end; pos++) {
+ if (index == 0)
+ return pos->second;
+ index--;
+ }
+ }
+
+ return TypeCategoryImplSP();
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/TypeFormat.cpp b/contrib/llvm-project/lldb/source/DataFormatters/TypeFormat.cpp
new file mode 100644
index 000000000000..b526e9a744bc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/TypeFormat.cpp
@@ -0,0 +1,205 @@
+//===-- TypeFormat.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/TypeFormat.h"
+
+
+
+
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-public.h"
+
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+TypeFormatImpl::TypeFormatImpl(const Flags &flags)
+ : m_flags(flags), m_my_revision(0) {}
+
+TypeFormatImpl::~TypeFormatImpl() {}
+
+TypeFormatImpl_Format::TypeFormatImpl_Format(lldb::Format f,
+ const TypeFormatImpl::Flags &flags)
+ : TypeFormatImpl(flags), m_format(f) {}
+
+TypeFormatImpl_Format::~TypeFormatImpl_Format() {}
+
+bool TypeFormatImpl_Format::FormatObject(ValueObject *valobj,
+ std::string &dest) const {
+ if (!valobj)
+ return false;
+ if (valobj->CanProvideValue()) {
+ Value &value(valobj->GetValue());
+ const Value::ContextType context_type = value.GetContextType();
+ ExecutionContext exe_ctx(valobj->GetExecutionContextRef());
+ DataExtractor data;
+
+ if (context_type == Value::eContextTypeRegisterInfo) {
+ const RegisterInfo *reg_info = value.GetRegisterInfo();
+ if (reg_info) {
+ Status error;
+ valobj->GetData(data, error);
+ if (error.Fail())
+ return false;
+
+ StreamString reg_sstr;
+ DumpDataExtractor(data, &reg_sstr, 0, GetFormat(), reg_info->byte_size,
+ 1, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0,
+ exe_ctx.GetBestExecutionContextScope());
+ dest = reg_sstr.GetString();
+ }
+ } else {
+ CompilerType compiler_type = value.GetCompilerType();
+ if (compiler_type) {
+ // put custom bytes to display in the DataExtractor to override the
+ // default value logic
+ if (GetFormat() == eFormatCString) {
+ lldb_private::Flags type_flags(compiler_type.GetTypeInfo(
+ nullptr)); // disambiguate w.r.t. TypeFormatImpl::Flags
+ if (type_flags.Test(eTypeIsPointer) &&
+ !type_flags.Test(eTypeIsObjC)) {
+ // if we are dumping a pointer as a c-string, get the pointee data
+ // as a string
+ TargetSP target_sp(valobj->GetTargetSP());
+ if (target_sp) {
+ size_t max_len = target_sp->GetMaximumSizeOfStringSummary();
+ Status error;
+ DataBufferSP buffer_sp(new DataBufferHeap(max_len + 1, 0));
+ Address address(valobj->GetPointerValue());
+ if (target_sp->ReadCStringFromMemory(
+ address, (char *)buffer_sp->GetBytes(), max_len, error) &&
+ error.Success())
+ data.SetData(buffer_sp);
+ }
+ }
+ } else {
+ Status error;
+ valobj->GetData(data, error);
+ if (error.Fail())
+ return false;
+ }
+
+ ExecutionContextScope *exe_scope =
+ exe_ctx.GetBestExecutionContextScope();
+ llvm::Optional<uint64_t> size = compiler_type.GetByteSize(exe_scope);
+ if (!size)
+ return false;
+ StreamString sstr;
+ compiler_type.DumpTypeValue(
+ &sstr, // The stream to use for display
+ GetFormat(), // Format to display this type with
+ data, // Data to extract from
+ 0, // Byte offset into "m_data"
+ *size, // Byte size of item in "m_data"
+ valobj->GetBitfieldBitSize(), // Bitfield bit size
+ valobj->GetBitfieldBitOffset(), // Bitfield bit offset
+ exe_scope);
+ // Given that we do not want to set the ValueObject's m_error for a
+ // formatting error (or else we wouldn't be able to reformat until a
+ // next update), an empty string is treated as a "false" return from
+ // here, but that's about as severe as we get
+ // CompilerType::DumpTypeValue() should always return something, even
+ // if that something is an error message
+ dest = sstr.GetString();
+ }
+ }
+ return !dest.empty();
+ } else
+ return false;
+}
+
+std::string TypeFormatImpl_Format::GetDescription() {
+ StreamString sstr;
+ sstr.Printf("%s%s%s%s", FormatManager::GetFormatAsCString(GetFormat()),
+ Cascades() ? "" : " (not cascading)",
+ SkipsPointers() ? " (skip pointers)" : "",
+ SkipsReferences() ? " (skip references)" : "");
+ return sstr.GetString();
+}
+
+TypeFormatImpl_EnumType::TypeFormatImpl_EnumType(
+ ConstString type_name, const TypeFormatImpl::Flags &flags)
+ : TypeFormatImpl(flags), m_enum_type(type_name), m_types() {}
+
+TypeFormatImpl_EnumType::~TypeFormatImpl_EnumType() {}
+
+bool TypeFormatImpl_EnumType::FormatObject(ValueObject *valobj,
+ std::string &dest) const {
+ dest.clear();
+ if (!valobj)
+ return false;
+ if (!valobj->CanProvideValue())
+ return false;
+ ProcessSP process_sp;
+ TargetSP target_sp;
+ void *valobj_key = (process_sp = valobj->GetProcessSP()).get();
+ if (!valobj_key)
+ valobj_key = (target_sp = valobj->GetTargetSP()).get();
+ else
+ target_sp = process_sp->GetTarget().shared_from_this();
+ if (!valobj_key)
+ return false;
+ auto iter = m_types.find(valobj_key), end = m_types.end();
+ CompilerType valobj_enum_type;
+ if (iter == end) {
+ // probably a redundant check
+ if (!target_sp)
+ return false;
+ const ModuleList &images(target_sp->GetImages());
+ TypeList types;
+ llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
+ images.FindTypes(nullptr, m_enum_type, false, UINT32_MAX,
+ searched_symbol_files, types);
+ if (types.GetSize() == 0)
+ return false;
+ for (lldb::TypeSP type_sp : types.Types()) {
+ if (!type_sp)
+ continue;
+ if ((type_sp->GetForwardCompilerType().GetTypeInfo() &
+ eTypeIsEnumeration) == eTypeIsEnumeration) {
+ valobj_enum_type = type_sp->GetFullCompilerType();
+ m_types.emplace(valobj_key, valobj_enum_type);
+ break;
+ }
+ }
+ } else
+ valobj_enum_type = iter->second;
+ if (!valobj_enum_type.IsValid())
+ return false;
+ DataExtractor data;
+ Status error;
+ valobj->GetData(data, error);
+ if (error.Fail())
+ return false;
+ ExecutionContext exe_ctx(valobj->GetExecutionContextRef());
+ StreamString sstr;
+ valobj_enum_type.DumpTypeValue(&sstr, lldb::eFormatEnum, data, 0,
+ data.GetByteSize(), 0, 0,
+ exe_ctx.GetBestExecutionContextScope());
+ if (!sstr.GetString().empty())
+ dest = sstr.GetString();
+ return !dest.empty();
+}
+
+std::string TypeFormatImpl_EnumType::GetDescription() {
+ StreamString sstr;
+ sstr.Printf("as type %s%s%s%s", m_enum_type.AsCString("<invalid type>"),
+ Cascades() ? "" : " (not cascading)",
+ SkipsPointers() ? " (skip pointers)" : "",
+ SkipsReferences() ? " (skip references)" : "");
+ return sstr.GetString();
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/TypeSummary.cpp b/contrib/llvm-project/lldb/source/DataFormatters/TypeSummary.cpp
new file mode 100644
index 000000000000..7f6930fdf41f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/TypeSummary.cpp
@@ -0,0 +1,201 @@
+//===-- TypeSummary.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/TypeSummary.h"
+
+
+
+
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-public.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/ValueObjectPrinter.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+TypeSummaryOptions::TypeSummaryOptions()
+ : m_lang(eLanguageTypeUnknown), m_capping(eTypeSummaryCapped) {}
+
+lldb::LanguageType TypeSummaryOptions::GetLanguage() const { return m_lang; }
+
+lldb::TypeSummaryCapping TypeSummaryOptions::GetCapping() const {
+ return m_capping;
+}
+
+TypeSummaryOptions &TypeSummaryOptions::SetLanguage(lldb::LanguageType lang) {
+ m_lang = lang;
+ return *this;
+}
+
+TypeSummaryOptions &
+TypeSummaryOptions::SetCapping(lldb::TypeSummaryCapping cap) {
+ m_capping = cap;
+ return *this;
+}
+
+TypeSummaryImpl::TypeSummaryImpl(Kind kind, const TypeSummaryImpl::Flags &flags)
+ : m_flags(flags), m_kind(kind) {}
+
+StringSummaryFormat::StringSummaryFormat(const TypeSummaryImpl::Flags &flags,
+ const char *format_cstr)
+ : TypeSummaryImpl(Kind::eSummaryString, flags), m_format_str() {
+ SetSummaryString(format_cstr);
+}
+
+void StringSummaryFormat::SetSummaryString(const char *format_cstr) {
+ m_format.Clear();
+ if (format_cstr && format_cstr[0]) {
+ m_format_str = format_cstr;
+ m_error = FormatEntity::Parse(format_cstr, m_format);
+ } else {
+ m_format_str.clear();
+ m_error.Clear();
+ }
+}
+
+bool StringSummaryFormat::FormatObject(ValueObject *valobj, std::string &retval,
+ const TypeSummaryOptions &options) {
+ if (!valobj) {
+ retval.assign("NULL ValueObject");
+ return false;
+ }
+
+ StreamString s;
+ ExecutionContext exe_ctx(valobj->GetExecutionContextRef());
+ SymbolContext sc;
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame)
+ sc = frame->GetSymbolContext(lldb::eSymbolContextEverything);
+
+ if (IsOneLiner()) {
+ ValueObjectPrinter printer(valobj, &s, DumpValueObjectOptions());
+ printer.PrintChildrenOneLiner(HideNames(valobj));
+ retval = s.GetString();
+ return true;
+ } else {
+ if (FormatEntity::Format(m_format, s, &sc, &exe_ctx,
+ &sc.line_entry.range.GetBaseAddress(), valobj,
+ false, false)) {
+ retval.assign(s.GetString());
+ return true;
+ } else {
+ retval.assign("error: summary string parsing error");
+ return false;
+ }
+ }
+}
+
+std::string StringSummaryFormat::GetDescription() {
+ StreamString sstr;
+
+ sstr.Printf("`%s`%s%s%s%s%s%s%s%s%s", m_format_str.c_str(),
+ m_error.Fail() ? " error: " : "",
+ m_error.Fail() ? m_error.AsCString() : "",
+ Cascades() ? "" : " (not cascading)",
+ !DoesPrintChildren(nullptr) ? "" : " (show children)",
+ !DoesPrintValue(nullptr) ? " (hide value)" : "",
+ IsOneLiner() ? " (one-line printout)" : "",
+ SkipsPointers() ? " (skip pointers)" : "",
+ SkipsReferences() ? " (skip references)" : "",
+ HideNames(nullptr) ? " (hide member names)" : "");
+ return sstr.GetString();
+}
+
+CXXFunctionSummaryFormat::CXXFunctionSummaryFormat(
+ const TypeSummaryImpl::Flags &flags, Callback impl, const char *description)
+ : TypeSummaryImpl(Kind::eCallback, flags), m_impl(impl),
+ m_description(description ? description : "") {}
+
+bool CXXFunctionSummaryFormat::FormatObject(ValueObject *valobj,
+ std::string &dest,
+ const TypeSummaryOptions &options) {
+ dest.clear();
+ StreamString stream;
+ if (!m_impl || !m_impl(*valobj, stream, options))
+ return false;
+ dest = stream.GetString();
+ return true;
+}
+
+std::string CXXFunctionSummaryFormat::GetDescription() {
+ StreamString sstr;
+ sstr.Printf("%s%s%s%s%s%s%s %s", Cascades() ? "" : " (not cascading)",
+ !DoesPrintChildren(nullptr) ? "" : " (show children)",
+ !DoesPrintValue(nullptr) ? " (hide value)" : "",
+ IsOneLiner() ? " (one-line printout)" : "",
+ SkipsPointers() ? " (skip pointers)" : "",
+ SkipsReferences() ? " (skip references)" : "",
+ HideNames(nullptr) ? " (hide member names)" : "",
+ m_description.c_str());
+ return sstr.GetString();
+}
+
+ScriptSummaryFormat::ScriptSummaryFormat(const TypeSummaryImpl::Flags &flags,
+ const char *function_name,
+ const char *python_script)
+ : TypeSummaryImpl(Kind::eScript, flags), m_function_name(),
+ m_python_script(), m_script_function_sp() {
+ if (function_name)
+ m_function_name.assign(function_name);
+ if (python_script)
+ m_python_script.assign(python_script);
+}
+
+bool ScriptSummaryFormat::FormatObject(ValueObject *valobj, std::string &retval,
+ const TypeSummaryOptions &options) {
+ if (!valobj)
+ return false;
+
+ TargetSP target_sp(valobj->GetTargetSP());
+
+ if (!target_sp) {
+ retval.assign("error: no target");
+ return false;
+ }
+
+ ScriptInterpreter *script_interpreter =
+ target_sp->GetDebugger().GetScriptInterpreter();
+
+ if (!script_interpreter) {
+ retval.assign("error: no ScriptInterpreter");
+ return false;
+ }
+
+ return script_interpreter->GetScriptedSummary(
+ m_function_name.c_str(), valobj->GetSP(), m_script_function_sp, options,
+ retval);
+}
+
+std::string ScriptSummaryFormat::GetDescription() {
+ StreamString sstr;
+ sstr.Printf("%s%s%s%s%s%s%s\n ", Cascades() ? "" : " (not cascading)",
+ !DoesPrintChildren(nullptr) ? "" : " (show children)",
+ !DoesPrintValue(nullptr) ? " (hide value)" : "",
+ IsOneLiner() ? " (one-line printout)" : "",
+ SkipsPointers() ? " (skip pointers)" : "",
+ SkipsReferences() ? " (skip references)" : "",
+ HideNames(nullptr) ? " (hide member names)" : "");
+ if (m_python_script.empty()) {
+ if (m_function_name.empty()) {
+ sstr.PutCString("no backing script");
+ } else {
+ sstr.PutCString(m_function_name);
+ }
+ } else {
+ sstr.PutCString(m_python_script);
+ }
+ return sstr.GetString();
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/TypeSynthetic.cpp b/contrib/llvm-project/lldb/source/DataFormatters/TypeSynthetic.cpp
new file mode 100644
index 000000000000..23c80fc58d02
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/TypeSynthetic.cpp
@@ -0,0 +1,217 @@
+//===-- TypeSynthetic.cpp ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+
+
+
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-public.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void TypeFilterImpl::AddExpressionPath(const std::string &path) {
+ bool need_add_dot = true;
+ if (path[0] == '.' || (path[0] == '-' && path[1] == '>') || path[0] == '[')
+ need_add_dot = false;
+ // add a '.' symbol to help forgetful users
+ if (!need_add_dot)
+ m_expression_paths.push_back(path);
+ else
+ m_expression_paths.push_back(std::string(".") + path);
+}
+
+bool TypeFilterImpl::SetExpressionPathAtIndex(size_t i,
+ const std::string &path) {
+ if (i >= GetCount())
+ return false;
+ bool need_add_dot = true;
+ if (path[0] == '.' || (path[0] == '-' && path[1] == '>') || path[0] == '[')
+ need_add_dot = false;
+ // add a '.' symbol to help forgetful users
+ if (!need_add_dot)
+ m_expression_paths[i] = path;
+ else
+ m_expression_paths[i] = std::string(".") + path;
+ return true;
+}
+
+size_t
+TypeFilterImpl::FrontEnd::GetIndexOfChildWithName(ConstString name) {
+ const char *name_cstr = name.GetCString();
+ if (name_cstr) {
+ for (size_t i = 0; i < filter->GetCount(); i++) {
+ const char *expr_cstr = filter->GetExpressionPathAtIndex(i);
+ if (expr_cstr) {
+ if (*expr_cstr == '.')
+ expr_cstr++;
+ else if (*expr_cstr == '-' && *(expr_cstr + 1) == '>')
+ expr_cstr += 2;
+ }
+ if (expr_cstr) {
+ if (!::strcmp(name_cstr, expr_cstr))
+ return i;
+ }
+ }
+ }
+ return UINT32_MAX;
+}
+
+std::string TypeFilterImpl::GetDescription() {
+ StreamString sstr;
+ sstr.Printf("%s%s%s {\n", Cascades() ? "" : " (not cascading)",
+ SkipsPointers() ? " (skip pointers)" : "",
+ SkipsReferences() ? " (skip references)" : "");
+
+ for (size_t i = 0; i < GetCount(); i++) {
+ sstr.Printf(" %s\n", GetExpressionPathAtIndex(i));
+ }
+
+ sstr.Printf("}");
+ return sstr.GetString();
+}
+
+std::string CXXSyntheticChildren::GetDescription() {
+ StreamString sstr;
+ sstr.Printf("%s%s%s %s", Cascades() ? "" : " (not cascading)",
+ SkipsPointers() ? " (skip pointers)" : "",
+ SkipsReferences() ? " (skip references)" : "",
+ m_description.c_str());
+
+ return sstr.GetString();
+}
+
+lldb::ValueObjectSP SyntheticChildrenFrontEnd::CreateValueObjectFromExpression(
+ llvm::StringRef name, llvm::StringRef expression,
+ const ExecutionContext &exe_ctx) {
+ ValueObjectSP valobj_sp(
+ ValueObject::CreateValueObjectFromExpression(name, expression, exe_ctx));
+ if (valobj_sp)
+ valobj_sp->SetSyntheticChildrenGenerated(true);
+ return valobj_sp;
+}
+
+lldb::ValueObjectSP SyntheticChildrenFrontEnd::CreateValueObjectFromAddress(
+ llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx,
+ CompilerType type) {
+ ValueObjectSP valobj_sp(
+ ValueObject::CreateValueObjectFromAddress(name, address, exe_ctx, type));
+ if (valobj_sp)
+ valobj_sp->SetSyntheticChildrenGenerated(true);
+ return valobj_sp;
+}
+
+lldb::ValueObjectSP SyntheticChildrenFrontEnd::CreateValueObjectFromData(
+ llvm::StringRef name, const DataExtractor &data,
+ const ExecutionContext &exe_ctx, CompilerType type) {
+ ValueObjectSP valobj_sp(
+ ValueObject::CreateValueObjectFromData(name, data, exe_ctx, type));
+ if (valobj_sp)
+ valobj_sp->SetSyntheticChildrenGenerated(true);
+ return valobj_sp;
+}
+
+ScriptedSyntheticChildren::FrontEnd::FrontEnd(std::string pclass,
+ ValueObject &backend)
+ : SyntheticChildrenFrontEnd(backend), m_python_class(pclass),
+ m_wrapper_sp(), m_interpreter(nullptr) {
+ if (backend == LLDB_INVALID_UID)
+ return;
+
+ TargetSP target_sp = backend.GetTargetSP();
+
+ if (!target_sp)
+ return;
+
+ m_interpreter = target_sp->GetDebugger().GetScriptInterpreter();
+
+ if (m_interpreter != nullptr)
+ m_wrapper_sp = m_interpreter->CreateSyntheticScriptedProvider(
+ m_python_class.c_str(), backend.GetSP());
+}
+
+ScriptedSyntheticChildren::FrontEnd::~FrontEnd() {}
+
+lldb::ValueObjectSP
+ScriptedSyntheticChildren::FrontEnd::GetChildAtIndex(size_t idx) {
+ if (!m_wrapper_sp || !m_interpreter)
+ return lldb::ValueObjectSP();
+
+ return m_interpreter->GetChildAtIndex(m_wrapper_sp, idx);
+}
+
+bool ScriptedSyntheticChildren::FrontEnd::IsValid() {
+ return (m_wrapper_sp && m_wrapper_sp->IsValid() && m_interpreter);
+}
+
+size_t ScriptedSyntheticChildren::FrontEnd::CalculateNumChildren() {
+ if (!m_wrapper_sp || m_interpreter == nullptr)
+ return 0;
+ return m_interpreter->CalculateNumChildren(m_wrapper_sp, UINT32_MAX);
+}
+
+size_t ScriptedSyntheticChildren::FrontEnd::CalculateNumChildren(uint32_t max) {
+ if (!m_wrapper_sp || m_interpreter == nullptr)
+ return 0;
+ return m_interpreter->CalculateNumChildren(m_wrapper_sp, max);
+}
+
+bool ScriptedSyntheticChildren::FrontEnd::Update() {
+ if (!m_wrapper_sp || m_interpreter == nullptr)
+ return false;
+
+ return m_interpreter->UpdateSynthProviderInstance(m_wrapper_sp);
+}
+
+bool ScriptedSyntheticChildren::FrontEnd::MightHaveChildren() {
+ if (!m_wrapper_sp || m_interpreter == nullptr)
+ return false;
+
+ return m_interpreter->MightHaveChildrenSynthProviderInstance(m_wrapper_sp);
+}
+
+size_t ScriptedSyntheticChildren::FrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ if (!m_wrapper_sp || m_interpreter == nullptr)
+ return UINT32_MAX;
+ return m_interpreter->GetIndexOfChildWithName(m_wrapper_sp,
+ name.GetCString());
+}
+
+lldb::ValueObjectSP ScriptedSyntheticChildren::FrontEnd::GetSyntheticValue() {
+ if (!m_wrapper_sp || m_interpreter == nullptr)
+ return nullptr;
+
+ return m_interpreter->GetSyntheticValue(m_wrapper_sp);
+}
+
+ConstString ScriptedSyntheticChildren::FrontEnd::GetSyntheticTypeName() {
+ if (!m_wrapper_sp || m_interpreter == nullptr)
+ return ConstString();
+
+ return m_interpreter->GetSyntheticTypeName(m_wrapper_sp);
+}
+
+std::string ScriptedSyntheticChildren::GetDescription() {
+ StreamString sstr;
+ sstr.Printf("%s%s%s Python class %s", Cascades() ? "" : " (not cascading)",
+ SkipsPointers() ? " (skip pointers)" : "",
+ SkipsReferences() ? " (skip references)" : "",
+ m_python_class.c_str());
+
+ return sstr.GetString();
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/TypeValidator.cpp b/contrib/llvm-project/lldb/source/DataFormatters/TypeValidator.cpp
new file mode 100644
index 000000000000..5ce24cacfb55
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/TypeValidator.cpp
@@ -0,0 +1,53 @@
+//===-- TypeValidator.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+
+
+
+#include "lldb/DataFormatters/TypeValidator.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+TypeValidatorImpl::TypeValidatorImpl(const Flags &flags)
+ : m_flags(flags), m_my_revision(0) {}
+
+TypeValidatorImpl::~TypeValidatorImpl() {}
+
+TypeValidatorImpl::ValidationResult TypeValidatorImpl::Success() {
+ return ValidationResult{TypeValidatorResult::Success, ""};
+}
+
+TypeValidatorImpl::ValidationResult
+TypeValidatorImpl::Failure(std::string message) {
+ return ValidationResult{TypeValidatorResult::Failure, message};
+}
+
+TypeValidatorImpl_CXX::TypeValidatorImpl_CXX(
+ ValidatorFunction f, std::string d, const TypeValidatorImpl::Flags &flags)
+ : TypeValidatorImpl(flags), m_description(d), m_validator_function(f) {}
+
+TypeValidatorImpl_CXX::~TypeValidatorImpl_CXX() {}
+
+TypeValidatorImpl::ValidationResult
+TypeValidatorImpl_CXX::FormatObject(ValueObject *valobj) const {
+ if (!valobj)
+ return Success(); // I guess there's nothing wrong with a null valueobject..
+
+ return m_validator_function(valobj);
+}
+
+std::string TypeValidatorImpl_CXX::GetDescription() {
+ StreamString sstr;
+ sstr.Printf("%s%s%s%s", m_description.c_str(),
+ Cascades() ? "" : " (not cascading)",
+ SkipsPointers() ? " (skip pointers)" : "",
+ SkipsReferences() ? " (skip references)" : "");
+ return sstr.GetString();
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/contrib/llvm-project/lldb/source/DataFormatters/ValueObjectPrinter.cpp
new file mode 100644
index 000000000000..409cffed9b0f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/ValueObjectPrinter.cpp
@@ -0,0 +1,830 @@
+//===-- ValueObjectPrinter.cpp -----------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/ValueObjectPrinter.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ValueObjectPrinter::ValueObjectPrinter(ValueObject *valobj, Stream *s) {
+ if (valobj) {
+ DumpValueObjectOptions options(*valobj);
+ Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
+ } else {
+ DumpValueObjectOptions options;
+ Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
+ }
+}
+
+ValueObjectPrinter::ValueObjectPrinter(ValueObject *valobj, Stream *s,
+ const DumpValueObjectOptions &options) {
+ Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
+}
+
+ValueObjectPrinter::ValueObjectPrinter(
+ ValueObject *valobj, Stream *s, const DumpValueObjectOptions &options,
+ const DumpValueObjectOptions::PointerDepth &ptr_depth, uint32_t curr_depth,
+ InstancePointersSetSP printed_instance_pointers) {
+ Init(valobj, s, options, ptr_depth, curr_depth, printed_instance_pointers);
+}
+
+void ValueObjectPrinter::Init(
+ ValueObject *valobj, Stream *s, const DumpValueObjectOptions &options,
+ const DumpValueObjectOptions::PointerDepth &ptr_depth, uint32_t curr_depth,
+ InstancePointersSetSP printed_instance_pointers) {
+ m_orig_valobj = valobj;
+ m_valobj = nullptr;
+ m_stream = s;
+ m_options = options;
+ m_ptr_depth = ptr_depth;
+ m_curr_depth = curr_depth;
+ assert(m_orig_valobj && "cannot print a NULL ValueObject");
+ assert(m_stream && "cannot print to a NULL Stream");
+ m_should_print = eLazyBoolCalculate;
+ m_is_nil = eLazyBoolCalculate;
+ m_is_uninit = eLazyBoolCalculate;
+ m_is_ptr = eLazyBoolCalculate;
+ m_is_ref = eLazyBoolCalculate;
+ m_is_aggregate = eLazyBoolCalculate;
+ m_is_instance_ptr = eLazyBoolCalculate;
+ m_summary_formatter = {nullptr, false};
+ m_value.assign("");
+ m_summary.assign("");
+ m_error.assign("");
+ m_val_summary_ok = false;
+ m_printed_instance_pointers =
+ printed_instance_pointers
+ ? printed_instance_pointers
+ : InstancePointersSetSP(new InstancePointersSet());
+}
+
+bool ValueObjectPrinter::PrintValueObject() {
+ if (!GetMostSpecializedValue() || m_valobj == nullptr)
+ return false;
+
+ if (ShouldPrintValueObject()) {
+ PrintValidationMarkerIfNeeded();
+
+ PrintLocationIfNeeded();
+ m_stream->Indent();
+
+ PrintDecl();
+ }
+
+ bool value_printed = false;
+ bool summary_printed = false;
+
+ m_val_summary_ok =
+ PrintValueAndSummaryIfNeeded(value_printed, summary_printed);
+
+ if (m_val_summary_ok)
+ PrintChildrenIfNeeded(value_printed, summary_printed);
+ else
+ m_stream->EOL();
+
+ PrintValidationErrorIfNeeded();
+
+ return true;
+}
+
+bool ValueObjectPrinter::GetMostSpecializedValue() {
+ if (m_valobj)
+ return true;
+ bool update_success = m_orig_valobj->UpdateValueIfNeeded(true);
+ if (!update_success) {
+ m_valobj = m_orig_valobj;
+ } else {
+ if (m_orig_valobj->IsDynamic()) {
+ if (m_options.m_use_dynamic == eNoDynamicValues) {
+ ValueObject *static_value = m_orig_valobj->GetStaticValue().get();
+ if (static_value)
+ m_valobj = static_value;
+ else
+ m_valobj = m_orig_valobj;
+ } else
+ m_valobj = m_orig_valobj;
+ } else {
+ if (m_options.m_use_dynamic != eNoDynamicValues) {
+ ValueObject *dynamic_value =
+ m_orig_valobj->GetDynamicValue(m_options.m_use_dynamic).get();
+ if (dynamic_value)
+ m_valobj = dynamic_value;
+ else
+ m_valobj = m_orig_valobj;
+ } else
+ m_valobj = m_orig_valobj;
+ }
+
+ if (m_valobj->IsSynthetic()) {
+ if (!m_options.m_use_synthetic) {
+ ValueObject *non_synthetic = m_valobj->GetNonSyntheticValue().get();
+ if (non_synthetic)
+ m_valobj = non_synthetic;
+ }
+ } else {
+ if (m_options.m_use_synthetic) {
+ ValueObject *synthetic = m_valobj->GetSyntheticValue().get();
+ if (synthetic)
+ m_valobj = synthetic;
+ }
+ }
+ }
+ m_compiler_type = m_valobj->GetCompilerType();
+ m_type_flags = m_compiler_type.GetTypeInfo();
+ return true;
+}
+
+const char *ValueObjectPrinter::GetDescriptionForDisplay() {
+ const char *str = m_valobj->GetObjectDescription();
+ if (!str)
+ str = m_valobj->GetSummaryAsCString();
+ if (!str)
+ str = m_valobj->GetValueAsCString();
+ return str;
+}
+
+const char *ValueObjectPrinter::GetRootNameForDisplay(const char *if_fail) {
+ const char *root_valobj_name = m_options.m_root_valobj_name.empty()
+ ? m_valobj->GetName().AsCString()
+ : m_options.m_root_valobj_name.c_str();
+ return root_valobj_name ? root_valobj_name : if_fail;
+}
+
+bool ValueObjectPrinter::ShouldPrintValueObject() {
+ if (m_should_print == eLazyBoolCalculate)
+ m_should_print =
+ (!m_options.m_flat_output || m_type_flags.Test(eTypeHasValue))
+ ? eLazyBoolYes
+ : eLazyBoolNo;
+ return m_should_print == eLazyBoolYes;
+}
+
+bool ValueObjectPrinter::IsNil() {
+ if (m_is_nil == eLazyBoolCalculate)
+ m_is_nil = m_valobj->IsNilReference() ? eLazyBoolYes : eLazyBoolNo;
+ return m_is_nil == eLazyBoolYes;
+}
+
+bool ValueObjectPrinter::IsUninitialized() {
+ if (m_is_uninit == eLazyBoolCalculate)
+ m_is_uninit =
+ m_valobj->IsUninitializedReference() ? eLazyBoolYes : eLazyBoolNo;
+ return m_is_uninit == eLazyBoolYes;
+}
+
+bool ValueObjectPrinter::IsPtr() {
+ if (m_is_ptr == eLazyBoolCalculate)
+ m_is_ptr = m_type_flags.Test(eTypeIsPointer) ? eLazyBoolYes : eLazyBoolNo;
+ return m_is_ptr == eLazyBoolYes;
+}
+
+bool ValueObjectPrinter::IsRef() {
+ if (m_is_ref == eLazyBoolCalculate)
+ m_is_ref = m_type_flags.Test(eTypeIsReference) ? eLazyBoolYes : eLazyBoolNo;
+ return m_is_ref == eLazyBoolYes;
+}
+
+bool ValueObjectPrinter::IsAggregate() {
+ if (m_is_aggregate == eLazyBoolCalculate)
+ m_is_aggregate =
+ m_type_flags.Test(eTypeHasChildren) ? eLazyBoolYes : eLazyBoolNo;
+ return m_is_aggregate == eLazyBoolYes;
+}
+
+bool ValueObjectPrinter::IsInstancePointer() {
+ // you need to do this check on the value's clang type
+ if (m_is_instance_ptr == eLazyBoolCalculate)
+ m_is_instance_ptr = (m_valobj->GetValue().GetCompilerType().GetTypeInfo() &
+ eTypeInstanceIsPointer) != 0
+ ? eLazyBoolYes
+ : eLazyBoolNo;
+ if ((eLazyBoolYes == m_is_instance_ptr) && m_valobj->IsBaseClass())
+ m_is_instance_ptr = eLazyBoolNo;
+ return m_is_instance_ptr == eLazyBoolYes;
+}
+
+bool ValueObjectPrinter::PrintLocationIfNeeded() {
+ if (m_options.m_show_location) {
+ m_stream->Printf("%s: ", m_valobj->GetLocationAsCString());
+ return true;
+ }
+ return false;
+}
+
+void ValueObjectPrinter::PrintDecl() {
+ bool show_type = true;
+ // if we are at the root-level and been asked to hide the root's type, then
+ // hide it
+ if (m_curr_depth == 0 && m_options.m_hide_root_type)
+ show_type = false;
+ else
+ // otherwise decide according to the usual rules (asked to show types -
+ // always at the root level)
+ show_type = m_options.m_show_types ||
+ (m_curr_depth == 0 && !m_options.m_flat_output);
+
+ StreamString typeName;
+
+ // always show the type at the root level if it is invalid
+ if (show_type) {
+ // Some ValueObjects don't have types (like registers sets). Only print the
+ // type if there is one to print
+ ConstString type_name;
+ if (m_compiler_type.IsValid()) {
+ if (m_options.m_use_type_display_name)
+ type_name = m_valobj->GetDisplayTypeName();
+ else
+ type_name = m_valobj->GetQualifiedTypeName();
+ } else {
+ // only show an invalid type name if the user explicitly triggered
+ // show_type
+ if (m_options.m_show_types)
+ type_name = ConstString("<invalid type>");
+ else
+ type_name.Clear();
+ }
+
+ if (type_name) {
+ std::string type_name_str(type_name.GetCString());
+ if (m_options.m_hide_pointer_value) {
+ for (auto iter = type_name_str.find(" *"); iter != std::string::npos;
+ iter = type_name_str.find(" *")) {
+ type_name_str.erase(iter, 2);
+ }
+ }
+ typeName.Printf("%s", type_name_str.c_str());
+ }
+ }
+
+ StreamString varName;
+
+ if (m_options.m_flat_output) {
+ // If we are showing types, also qualify the C++ base classes
+ const bool qualify_cxx_base_classes = show_type;
+ if (!m_options.m_hide_name) {
+ m_valobj->GetExpressionPath(varName, qualify_cxx_base_classes);
+ }
+ } else if (!m_options.m_hide_name) {
+ const char *name_cstr = GetRootNameForDisplay("");
+ varName.Printf("%s", name_cstr);
+ }
+
+ bool decl_printed = false;
+ if (!m_options.m_decl_printing_helper) {
+ // if the user didn't give us a custom helper, pick one based upon the
+ // language, either the one that this printer is bound to, or the preferred
+ // one for the ValueObject
+ lldb::LanguageType lang_type =
+ (m_options.m_varformat_language == lldb::eLanguageTypeUnknown)
+ ? m_valobj->GetPreferredDisplayLanguage()
+ : m_options.m_varformat_language;
+ if (Language *lang_plugin = Language::FindPlugin(lang_type)) {
+ m_options.m_decl_printing_helper = lang_plugin->GetDeclPrintingHelper();
+ }
+ }
+
+ if (m_options.m_decl_printing_helper) {
+ ConstString type_name_cstr(typeName.GetString());
+ ConstString var_name_cstr(varName.GetString());
+
+ StreamString dest_stream;
+ if (m_options.m_decl_printing_helper(type_name_cstr, var_name_cstr,
+ m_options, dest_stream)) {
+ decl_printed = true;
+ m_stream->PutCString(dest_stream.GetString());
+ }
+ }
+
+ // if the helper failed, or there is none, do a default thing
+ if (!decl_printed) {
+ if (!typeName.Empty())
+ m_stream->Printf("(%s) ", typeName.GetData());
+ if (!varName.Empty())
+ m_stream->Printf("%s =", varName.GetData());
+ else if (!m_options.m_hide_name)
+ m_stream->Printf(" =");
+ }
+}
+
+bool ValueObjectPrinter::CheckScopeIfNeeded() {
+ if (m_options.m_scope_already_checked)
+ return true;
+ return m_valobj->IsInScope();
+}
+
+TypeSummaryImpl *ValueObjectPrinter::GetSummaryFormatter(bool null_if_omitted) {
+ if (!m_summary_formatter.second) {
+ TypeSummaryImpl *entry = m_options.m_summary_sp
+ ? m_options.m_summary_sp.get()
+ : m_valobj->GetSummaryFormat().get();
+
+ if (m_options.m_omit_summary_depth > 0)
+ entry = nullptr;
+ m_summary_formatter.first = entry;
+ m_summary_formatter.second = true;
+ }
+ if (m_options.m_omit_summary_depth > 0 && null_if_omitted)
+ return nullptr;
+ return m_summary_formatter.first;
+}
+
+static bool IsPointerValue(const CompilerType &type) {
+ Flags type_flags(type.GetTypeInfo());
+ if (type_flags.AnySet(eTypeInstanceIsPointer | eTypeIsPointer))
+ return type_flags.AllClear(eTypeIsBuiltIn);
+ return false;
+}
+
+void ValueObjectPrinter::GetValueSummaryError(std::string &value,
+ std::string &summary,
+ std::string &error) {
+ lldb::Format format = m_options.m_format;
+ // if I am printing synthetized elements, apply the format to those elements
+ // only
+ if (m_options.m_pointer_as_array)
+ m_valobj->GetValueAsCString(lldb::eFormatDefault, value);
+ else if (format != eFormatDefault && format != m_valobj->GetFormat())
+ m_valobj->GetValueAsCString(format, value);
+ else {
+ const char *val_cstr = m_valobj->GetValueAsCString();
+ if (val_cstr)
+ value.assign(val_cstr);
+ }
+ const char *err_cstr = m_valobj->GetError().AsCString();
+ if (err_cstr)
+ error.assign(err_cstr);
+
+ if (ShouldPrintValueObject()) {
+ if (IsNil())
+ summary.assign("nil");
+ else if (IsUninitialized())
+ summary.assign("<uninitialized>");
+ else if (m_options.m_omit_summary_depth == 0) {
+ TypeSummaryImpl *entry = GetSummaryFormatter();
+ if (entry)
+ m_valobj->GetSummaryAsCString(entry, summary,
+ m_options.m_varformat_language);
+ else {
+ const char *sum_cstr =
+ m_valobj->GetSummaryAsCString(m_options.m_varformat_language);
+ if (sum_cstr)
+ summary.assign(sum_cstr);
+ }
+ }
+ }
+}
+
+bool ValueObjectPrinter::PrintValueAndSummaryIfNeeded(bool &value_printed,
+ bool &summary_printed) {
+ bool error_printed = false;
+ if (ShouldPrintValueObject()) {
+ if (!CheckScopeIfNeeded())
+ m_error.assign("out of scope");
+ if (m_error.empty()) {
+ GetValueSummaryError(m_value, m_summary, m_error);
+ }
+ if (m_error.size()) {
+ // we need to support scenarios in which it is actually fine for a value
+ // to have no type but - on the other hand - if we get an error *AND*
+ // have no type, we try to get out gracefully, since most often that
+ // combination means "could not resolve a type" and the default failure
+ // mode is quite ugly
+ if (!m_compiler_type.IsValid()) {
+ m_stream->Printf(" <could not resolve type>");
+ return false;
+ }
+
+ error_printed = true;
+ m_stream->Printf(" <%s>\n", m_error.c_str());
+ } else {
+ // Make sure we have a value and make sure the summary didn't specify
+ // that the value should not be printed - and do not print the value if
+ // this thing is nil (but show the value if the user passes a format
+ // explicitly)
+ TypeSummaryImpl *entry = GetSummaryFormatter();
+ if (!IsNil() && !IsUninitialized() && !m_value.empty() &&
+ (entry == nullptr ||
+ (entry->DoesPrintValue(m_valobj) ||
+ m_options.m_format != eFormatDefault) ||
+ m_summary.empty()) &&
+ !m_options.m_hide_value) {
+ if (m_options.m_hide_pointer_value &&
+ IsPointerValue(m_valobj->GetCompilerType())) {
+ } else {
+ m_stream->Printf(" %s", m_value.c_str());
+ value_printed = true;
+ }
+ }
+
+ if (m_summary.size()) {
+ m_stream->Printf(" %s", m_summary.c_str());
+ summary_printed = true;
+ }
+ }
+ }
+ return !error_printed;
+}
+
+bool ValueObjectPrinter::PrintObjectDescriptionIfNeeded(bool value_printed,
+ bool summary_printed) {
+ if (ShouldPrintValueObject()) {
+ // let's avoid the overly verbose no description error for a nil thing
+ if (m_options.m_use_objc && !IsNil() && !IsUninitialized() &&
+ (!m_options.m_pointer_as_array)) {
+ if (!m_options.m_hide_value || !m_options.m_hide_name)
+ m_stream->Printf(" ");
+ const char *object_desc = nullptr;
+ if (value_printed || summary_printed)
+ object_desc = m_valobj->GetObjectDescription();
+ else
+ object_desc = GetDescriptionForDisplay();
+ if (object_desc && *object_desc) {
+ // If the description already ends with a \n don't add another one.
+ size_t object_end = strlen(object_desc) - 1;
+ if (object_desc[object_end] == '\n')
+ m_stream->Printf("%s", object_desc);
+ else
+ m_stream->Printf("%s\n", object_desc);
+ return true;
+ } else if (!value_printed && !summary_printed)
+ return true;
+ else
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DumpValueObjectOptions::PointerDepth::CanAllowExpansion() const {
+ switch (m_mode) {
+ case Mode::Always:
+ case Mode::Default:
+ return m_count > 0;
+ case Mode::Never:
+ return false;
+ }
+ return false;
+}
+
+bool ValueObjectPrinter::ShouldPrintChildren(
+ bool is_failed_description,
+ DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
+ const bool is_ref = IsRef();
+ const bool is_ptr = IsPtr();
+ const bool is_uninit = IsUninitialized();
+
+ if (is_uninit)
+ return false;
+
+ // if the user has specified an element count, always print children as it is
+ // explicit user demand being honored
+ if (m_options.m_pointer_as_array)
+ return true;
+
+ TypeSummaryImpl *entry = GetSummaryFormatter();
+
+ if (m_options.m_use_objc)
+ return false;
+
+ if (is_failed_description || m_curr_depth < m_options.m_max_depth) {
+ // We will show children for all concrete types. We won't show pointer
+ // contents unless a pointer depth has been specified. We won't reference
+ // contents unless the reference is the root object (depth of zero).
+
+ // Use a new temporary pointer depth in case we override the current
+ // pointer depth below...
+
+ if (is_ptr || is_ref) {
+ // We have a pointer or reference whose value is an address. Make sure
+ // that address is not NULL
+ AddressType ptr_address_type;
+ if (m_valobj->GetPointerValue(&ptr_address_type) == 0)
+ return false;
+
+ const bool is_root_level = m_curr_depth == 0;
+
+ if (is_ref && is_root_level) {
+ // If this is the root object (depth is zero) that we are showing and
+ // it is a reference, and no pointer depth has been supplied print out
+ // what it references. Don't do this at deeper depths otherwise we can
+ // end up with infinite recursion...
+ return true;
+ }
+
+ return curr_ptr_depth.CanAllowExpansion();
+ }
+
+ return (!entry || entry->DoesPrintChildren(m_valobj) || m_summary.empty());
+ }
+ return false;
+}
+
+bool ValueObjectPrinter::ShouldExpandEmptyAggregates() {
+ TypeSummaryImpl *entry = GetSummaryFormatter();
+
+ if (!entry)
+ return true;
+
+ return entry->DoesPrintEmptyAggregates();
+}
+
+ValueObject *ValueObjectPrinter::GetValueObjectForChildrenGeneration() {
+ return m_valobj;
+}
+
+void ValueObjectPrinter::PrintChildrenPreamble() {
+ if (m_options.m_flat_output) {
+ if (ShouldPrintValueObject())
+ m_stream->EOL();
+ } else {
+ if (ShouldPrintValueObject())
+ m_stream->PutCString(IsRef() ? ": {\n" : " {\n");
+ m_stream->IndentMore();
+ }
+}
+
+void ValueObjectPrinter::PrintChild(
+ ValueObjectSP child_sp,
+ const DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
+ const uint32_t consumed_depth = (!m_options.m_pointer_as_array) ? 1 : 0;
+ const bool does_consume_ptr_depth =
+ ((IsPtr() && !m_options.m_pointer_as_array) || IsRef());
+
+ DumpValueObjectOptions child_options(m_options);
+ child_options.SetFormat(m_options.m_format)
+ .SetSummary()
+ .SetRootValueObjectName();
+ child_options.SetScopeChecked(true)
+ .SetHideName(m_options.m_hide_name)
+ .SetHideValue(m_options.m_hide_value)
+ .SetOmitSummaryDepth(child_options.m_omit_summary_depth > 1
+ ? child_options.m_omit_summary_depth -
+ consumed_depth
+ : 0)
+ .SetElementCount(0);
+
+ if (child_sp.get()) {
+ ValueObjectPrinter child_printer(
+ child_sp.get(), m_stream, child_options,
+ does_consume_ptr_depth ? --curr_ptr_depth : curr_ptr_depth,
+ m_curr_depth + consumed_depth, m_printed_instance_pointers);
+ child_printer.PrintValueObject();
+ }
+}
+
+uint32_t ValueObjectPrinter::GetMaxNumChildrenToPrint(bool &print_dotdotdot) {
+ ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
+
+ if (m_options.m_pointer_as_array)
+ return m_options.m_pointer_as_array.m_element_count;
+
+ size_t num_children = synth_m_valobj->GetNumChildren();
+ print_dotdotdot = false;
+ if (num_children) {
+ const size_t max_num_children =
+ m_valobj->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
+
+ if (num_children > max_num_children && !m_options.m_ignore_cap) {
+ print_dotdotdot = true;
+ return max_num_children;
+ }
+ }
+ return num_children;
+}
+
+void ValueObjectPrinter::PrintChildrenPostamble(bool print_dotdotdot) {
+ if (!m_options.m_flat_output) {
+ if (print_dotdotdot) {
+ m_valobj->GetTargetSP()
+ ->GetDebugger()
+ .GetCommandInterpreter()
+ .ChildrenTruncated();
+ m_stream->Indent("...\n");
+ }
+ m_stream->IndentLess();
+ m_stream->Indent("}\n");
+ }
+}
+
+bool ValueObjectPrinter::ShouldPrintEmptyBrackets(bool value_printed,
+ bool summary_printed) {
+ ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
+
+ if (!IsAggregate())
+ return false;
+
+ if (!m_options.m_reveal_empty_aggregates) {
+ if (value_printed || summary_printed)
+ return false;
+ }
+
+ if (synth_m_valobj->MightHaveChildren())
+ return true;
+
+ if (m_val_summary_ok)
+ return false;
+
+ return true;
+}
+
+static constexpr size_t PhysicalIndexForLogicalIndex(size_t base, size_t stride,
+ size_t logical) {
+ return base + logical * stride;
+}
+
+ValueObjectSP ValueObjectPrinter::GenerateChild(ValueObject *synth_valobj,
+ size_t idx) {
+ if (m_options.m_pointer_as_array) {
+ // if generating pointer-as-array children, use GetSyntheticArrayMember
+ return synth_valobj->GetSyntheticArrayMember(
+ PhysicalIndexForLogicalIndex(
+ m_options.m_pointer_as_array.m_base_element,
+ m_options.m_pointer_as_array.m_stride, idx),
+ true);
+ } else {
+ // otherwise, do the usual thing
+ return synth_valobj->GetChildAtIndex(idx, true);
+ }
+}
+
+void ValueObjectPrinter::PrintChildren(
+ bool value_printed, bool summary_printed,
+ const DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
+ ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
+
+ bool print_dotdotdot = false;
+ size_t num_children = GetMaxNumChildrenToPrint(print_dotdotdot);
+ if (num_children) {
+ bool any_children_printed = false;
+
+ for (size_t idx = 0; idx < num_children; ++idx) {
+ if (ValueObjectSP child_sp = GenerateChild(synth_m_valobj, idx)) {
+ if (!any_children_printed) {
+ PrintChildrenPreamble();
+ any_children_printed = true;
+ }
+ PrintChild(child_sp, curr_ptr_depth);
+ }
+ }
+
+ if (any_children_printed)
+ PrintChildrenPostamble(print_dotdotdot);
+ else {
+ if (ShouldPrintEmptyBrackets(value_printed, summary_printed)) {
+ if (ShouldPrintValueObject())
+ m_stream->PutCString(" {}\n");
+ else
+ m_stream->EOL();
+ } else
+ m_stream->EOL();
+ }
+ } else if (ShouldPrintEmptyBrackets(value_printed, summary_printed)) {
+ // Aggregate, no children...
+ if (ShouldPrintValueObject()) {
+ // if it has a synthetic value, then don't print {}, the synthetic
+ // children are probably only being used to vend a value
+ if (m_valobj->DoesProvideSyntheticValue() ||
+ !ShouldExpandEmptyAggregates())
+ m_stream->PutCString("\n");
+ else
+ m_stream->PutCString(" {}\n");
+ }
+ } else {
+ if (ShouldPrintValueObject())
+ m_stream->EOL();
+ }
+}
+
+bool ValueObjectPrinter::PrintChildrenOneLiner(bool hide_names) {
+ if (!GetMostSpecializedValue() || m_valobj == nullptr)
+ return false;
+
+ ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
+
+ bool print_dotdotdot = false;
+ size_t num_children = GetMaxNumChildrenToPrint(print_dotdotdot);
+
+ if (num_children) {
+ m_stream->PutChar('(');
+
+ for (uint32_t idx = 0; idx < num_children; ++idx) {
+ lldb::ValueObjectSP child_sp(synth_m_valobj->GetChildAtIndex(idx, true));
+ if (child_sp)
+ child_sp = child_sp->GetQualifiedRepresentationIfAvailable(
+ m_options.m_use_dynamic, m_options.m_use_synthetic);
+ if (child_sp) {
+ if (idx)
+ m_stream->PutCString(", ");
+ if (!hide_names) {
+ const char *name = child_sp.get()->GetName().AsCString();
+ if (name && *name) {
+ m_stream->PutCString(name);
+ m_stream->PutCString(" = ");
+ }
+ }
+ child_sp->DumpPrintableRepresentation(
+ *m_stream, ValueObject::eValueObjectRepresentationStyleSummary,
+ m_options.m_format,
+ ValueObject::PrintableRepresentationSpecialCases::eDisable);
+ }
+ }
+
+ if (print_dotdotdot)
+ m_stream->PutCString(", ...)");
+ else
+ m_stream->PutChar(')');
+ }
+ return true;
+}
+
+void ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed,
+ bool summary_printed) {
+ // this flag controls whether we tried to display a description for this
+ // object and failed if that happens, we want to display the children, if any
+ bool is_failed_description =
+ !PrintObjectDescriptionIfNeeded(value_printed, summary_printed);
+
+ auto curr_ptr_depth = m_ptr_depth;
+ bool print_children =
+ ShouldPrintChildren(is_failed_description, curr_ptr_depth);
+ bool print_oneline =
+ (curr_ptr_depth.CanAllowExpansion() || m_options.m_show_types ||
+ !m_options.m_allow_oneliner_mode || m_options.m_flat_output ||
+ (m_options.m_pointer_as_array) || m_options.m_show_location)
+ ? false
+ : DataVisualization::ShouldPrintAsOneLiner(*m_valobj);
+ bool is_instance_ptr = IsInstancePointer();
+ uint64_t instance_ptr_value = LLDB_INVALID_ADDRESS;
+
+ if (print_children && is_instance_ptr) {
+ instance_ptr_value = m_valobj->GetValueAsUnsigned(0);
+ if (m_printed_instance_pointers->count(instance_ptr_value)) {
+ // we already printed this instance-is-pointer thing, so don't expand it
+ m_stream->PutCString(" {...}\n");
+
+ // we're done here - get out fast
+ return;
+ } else
+ m_printed_instance_pointers->emplace(
+ instance_ptr_value); // remember this guy for future reference
+ }
+
+ if (print_children) {
+ if (print_oneline) {
+ m_stream->PutChar(' ');
+ PrintChildrenOneLiner(false);
+ m_stream->EOL();
+ } else
+ PrintChildren(value_printed, summary_printed, curr_ptr_depth);
+ } else if (m_curr_depth >= m_options.m_max_depth && IsAggregate() &&
+ ShouldPrintValueObject()) {
+ m_stream->PutCString("{...}\n");
+ } else
+ m_stream->EOL();
+}
+
+bool ValueObjectPrinter::ShouldPrintValidation() {
+ return m_options.m_run_validator;
+}
+
+bool ValueObjectPrinter::PrintValidationMarkerIfNeeded() {
+ if (!ShouldPrintValidation())
+ return false;
+
+ m_validation = m_valobj->GetValidationStatus();
+
+ if (TypeValidatorResult::Failure == m_validation.first) {
+ m_stream->Printf("! ");
+ return true;
+ }
+
+ return false;
+}
+
+bool ValueObjectPrinter::PrintValidationErrorIfNeeded() {
+ if (!ShouldPrintValidation())
+ return false;
+
+ if (TypeValidatorResult::Success == m_validation.first)
+ return false;
+
+ if (m_validation.second.empty())
+ m_validation.second.assign("unknown error");
+
+ m_stream->Printf(" ! validation error: %s", m_validation.second.c_str());
+ m_stream->EOL();
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/DataFormatters/VectorType.cpp b/contrib/llvm-project/lldb/source/DataFormatters/VectorType.cpp
new file mode 100644
index 000000000000..18880f72ef2e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/DataFormatters/VectorType.cpp
@@ -0,0 +1,297 @@
+//===-- VectorType.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/DataFormatters/VectorType.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Target.h"
+
+#include "lldb/Utility/LLDBAssert.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+static CompilerType GetCompilerTypeForFormat(lldb::Format format,
+ CompilerType element_type,
+ TypeSystem *type_system) {
+ lldbassert(type_system && "type_system needs to be not NULL");
+
+ switch (format) {
+ case lldb::eFormatAddressInfo:
+ case lldb::eFormatPointer:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(
+ eEncodingUint, 8 * type_system->GetPointerByteSize());
+
+ case lldb::eFormatBoolean:
+ return type_system->GetBasicTypeFromAST(lldb::eBasicTypeBool);
+
+ case lldb::eFormatBytes:
+ case lldb::eFormatBytesWithASCII:
+ case lldb::eFormatChar:
+ case lldb::eFormatCharArray:
+ case lldb::eFormatCharPrintable:
+ return type_system->GetBasicTypeFromAST(lldb::eBasicTypeChar);
+
+ case lldb::eFormatComplex /* lldb::eFormatComplexFloat */:
+ return type_system->GetBasicTypeFromAST(lldb::eBasicTypeFloatComplex);
+
+ case lldb::eFormatCString:
+ return type_system->GetBasicTypeFromAST(lldb::eBasicTypeChar)
+ .GetPointerType();
+
+ case lldb::eFormatFloat:
+ return type_system->GetBasicTypeFromAST(lldb::eBasicTypeFloat);
+
+ case lldb::eFormatHex:
+ case lldb::eFormatHexUppercase:
+ case lldb::eFormatOctal:
+ return type_system->GetBasicTypeFromAST(lldb::eBasicTypeInt);
+
+ case lldb::eFormatHexFloat:
+ return type_system->GetBasicTypeFromAST(lldb::eBasicTypeFloat);
+
+ case lldb::eFormatUnicode16:
+ case lldb::eFormatUnicode32:
+
+ case lldb::eFormatUnsigned:
+ return type_system->GetBasicTypeFromAST(lldb::eBasicTypeUnsignedInt);
+
+ case lldb::eFormatVectorOfChar:
+ return type_system->GetBasicTypeFromAST(lldb::eBasicTypeChar);
+
+ case lldb::eFormatVectorOfFloat32:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingIEEE754,
+ 32);
+
+ case lldb::eFormatVectorOfFloat64:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingIEEE754,
+ 64);
+
+ case lldb::eFormatVectorOfSInt16:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 16);
+
+ case lldb::eFormatVectorOfSInt32:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 32);
+
+ case lldb::eFormatVectorOfSInt64:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 64);
+
+ case lldb::eFormatVectorOfSInt8:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 8);
+
+ case lldb::eFormatVectorOfUInt128:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 128);
+
+ case lldb::eFormatVectorOfUInt16:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 16);
+
+ case lldb::eFormatVectorOfUInt32:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
+
+ case lldb::eFormatVectorOfUInt64:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 64);
+
+ case lldb::eFormatVectorOfUInt8:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8);
+
+ case lldb::eFormatDefault:
+ return element_type;
+
+ case lldb::eFormatBinary:
+ case lldb::eFormatComplexInteger:
+ case lldb::eFormatDecimal:
+ case lldb::eFormatEnum:
+ case lldb::eFormatInstruction:
+ case lldb::eFormatOSType:
+ case lldb::eFormatVoid:
+ default:
+ return type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8);
+ }
+}
+
+static lldb::Format GetItemFormatForFormat(lldb::Format format,
+ CompilerType element_type) {
+ switch (format) {
+ case lldb::eFormatVectorOfChar:
+ return lldb::eFormatChar;
+
+ case lldb::eFormatVectorOfFloat32:
+ case lldb::eFormatVectorOfFloat64:
+ return lldb::eFormatFloat;
+
+ case lldb::eFormatVectorOfSInt16:
+ case lldb::eFormatVectorOfSInt32:
+ case lldb::eFormatVectorOfSInt64:
+ case lldb::eFormatVectorOfSInt8:
+ return lldb::eFormatDecimal;
+
+ case lldb::eFormatVectorOfUInt128:
+ case lldb::eFormatVectorOfUInt16:
+ case lldb::eFormatVectorOfUInt32:
+ case lldb::eFormatVectorOfUInt64:
+ case lldb::eFormatVectorOfUInt8:
+ return lldb::eFormatUnsigned;
+
+ case lldb::eFormatBinary:
+ case lldb::eFormatComplexInteger:
+ case lldb::eFormatDecimal:
+ case lldb::eFormatEnum:
+ case lldb::eFormatInstruction:
+ case lldb::eFormatOSType:
+ case lldb::eFormatVoid:
+ return eFormatHex;
+
+ case lldb::eFormatDefault: {
+ // special case the (default, char) combination to actually display as an
+ // integer value most often, you won't want to see the ASCII characters...
+ // (and if you do, eFormatChar is a keystroke away)
+ bool is_char = element_type.IsCharType();
+ bool is_signed = false;
+ element_type.IsIntegerType(is_signed);
+ return is_char ? (is_signed ? lldb::eFormatDecimal : eFormatHex) : format;
+ } break;
+
+ default:
+ return format;
+ }
+}
+
+static size_t CalculateNumChildren(
+ CompilerType container_type, CompilerType element_type,
+ lldb_private::ExecutionContextScope *exe_scope =
+ nullptr // does not matter here because all we trade in are basic types
+ ) {
+ llvm::Optional<uint64_t> container_size =
+ container_type.GetByteSize(exe_scope);
+ llvm::Optional<uint64_t> element_size = element_type.GetByteSize(exe_scope);
+
+ if (container_size && element_size && *element_size) {
+ if (*container_size % *element_size)
+ return 0;
+ return *container_size / *element_size;
+ }
+ return 0;
+}
+
+namespace lldb_private {
+namespace formatters {
+
+class VectorTypeSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ VectorTypeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_parent_format(eFormatInvalid),
+ m_item_format(eFormatInvalid), m_child_type(), m_num_children(0) {}
+
+ ~VectorTypeSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override { return m_num_children; }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ if (idx >= CalculateNumChildren())
+ return {};
+ llvm::Optional<uint64_t> size = m_child_type.GetByteSize(nullptr);
+ if (!size)
+ return {};
+ auto offset = idx * *size;
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ ValueObjectSP child_sp(m_backend.GetSyntheticChildAtOffset(
+ offset, m_child_type, true, ConstString(idx_name.GetString())));
+ if (!child_sp)
+ return child_sp;
+
+ child_sp->SetFormat(m_item_format);
+
+ return child_sp;
+ }
+
+ bool Update() override {
+ m_parent_format = m_backend.GetFormat();
+ CompilerType parent_type(m_backend.GetCompilerType());
+ CompilerType element_type;
+ parent_type.IsVectorType(&element_type, nullptr);
+ TargetSP target_sp(m_backend.GetTargetSP());
+ m_child_type = ::GetCompilerTypeForFormat(
+ m_parent_format, element_type,
+ target_sp
+ ? target_sp->GetScratchTypeSystemForLanguage(nullptr,
+ lldb::eLanguageTypeC)
+ : nullptr);
+ m_num_children = ::CalculateNumChildren(parent_type, m_child_type);
+ m_item_format = GetItemFormatForFormat(m_parent_format, m_child_type);
+ return false;
+ }
+
+ bool MightHaveChildren() override { return true; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+ }
+
+private:
+ lldb::Format m_parent_format;
+ lldb::Format m_item_format;
+ CompilerType m_child_type;
+ size_t m_num_children;
+};
+
+} // namespace formatters
+} // namespace lldb_private
+
+bool lldb_private::formatters::VectorTypeSummaryProvider(
+ ValueObject &valobj, Stream &s, const TypeSummaryOptions &) {
+ auto synthetic_children =
+ VectorTypeSyntheticFrontEndCreator(nullptr, valobj.GetSP());
+ if (!synthetic_children)
+ return false;
+
+ synthetic_children->Update();
+
+ s.PutChar('(');
+ bool first = true;
+
+ size_t idx = 0, len = synthetic_children->CalculateNumChildren();
+
+ for (; idx < len; idx++) {
+ auto child_sp = synthetic_children->GetChildAtIndex(idx);
+ if (!child_sp)
+ continue;
+ child_sp = child_sp->GetQualifiedRepresentationIfAvailable(
+ lldb::eDynamicDontRunTarget, true);
+
+ const char *child_value = child_sp->GetValueAsCString();
+ if (child_value && *child_value) {
+ if (first) {
+ s.Printf("%s", child_value);
+ first = false;
+ } else {
+ s.Printf(", %s", child_value);
+ }
+ }
+ }
+
+ s.PutChar(')');
+
+ return true;
+}
+
+lldb_private::SyntheticChildrenFrontEnd *
+lldb_private::formatters::VectorTypeSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+ return new VectorTypeSyntheticFrontEnd(valobj_sp);
+}
diff --git a/contrib/llvm-project/lldb/source/Expression/DWARFExpression.cpp b/contrib/llvm-project/lldb/source/Expression/DWARFExpression.cpp
new file mode 100644
index 000000000000..a9d365325d9e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/DWARFExpression.cpp
@@ -0,0 +1,3140 @@
+//===-- DWARFExpression.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Expression/DWARFExpression.h"
+
+#include <inttypes.h>
+
+#include <vector>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Utility/DataEncoder.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/VMRange.h"
+
+#include "lldb/Host/Host.h"
+#include "lldb/Utility/Endian.h"
+
+#include "lldb/Symbol/Function.h"
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StackID.h"
+#include "lldb/Target/Thread.h"
+
+#include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static lldb::addr_t
+ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu,
+ uint32_t index) {
+ uint32_t index_size = dwarf_cu->GetAddressByteSize();
+ dw_offset_t addr_base = dwarf_cu->GetAddrBase();
+ lldb::offset_t offset = addr_base + index * index_size;
+ return dwarf_cu->GetSymbolFileDWARF()
+ .GetDWARFContext()
+ .getOrLoadAddrData()
+ .GetMaxU64(&offset, index_size);
+}
+
+// DWARFExpression constructor
+DWARFExpression::DWARFExpression()
+ : m_module_wp(), m_data(), m_dwarf_cu(nullptr),
+ m_reg_kind(eRegisterKindDWARF), m_loclist_slide(LLDB_INVALID_ADDRESS) {}
+
+DWARFExpression::DWARFExpression(lldb::ModuleSP module_sp,
+ const DataExtractor &data,
+ const DWARFUnit *dwarf_cu,
+ lldb::offset_t data_offset,
+ lldb::offset_t data_length)
+ : m_module_wp(), m_data(data, data_offset, data_length),
+ m_dwarf_cu(dwarf_cu), m_reg_kind(eRegisterKindDWARF),
+ m_loclist_slide(LLDB_INVALID_ADDRESS) {
+ if (module_sp)
+ m_module_wp = module_sp;
+}
+
+// Destructor
+DWARFExpression::~DWARFExpression() {}
+
+bool DWARFExpression::IsValid() const { return m_data.GetByteSize() > 0; }
+
+void DWARFExpression::UpdateValue(uint64_t const_value,
+ lldb::offset_t const_value_byte_size,
+ uint8_t addr_byte_size) {
+ if (!const_value_byte_size)
+ return;
+
+ m_data.SetData(
+ DataBufferSP(new DataBufferHeap(&const_value, const_value_byte_size)));
+ m_data.SetByteOrder(endian::InlHostByteOrder());
+ m_data.SetAddressByteSize(addr_byte_size);
+}
+
+void DWARFExpression::DumpLocation(Stream *s, lldb::offset_t offset,
+ lldb::offset_t length,
+ lldb::DescriptionLevel level,
+ ABI *abi) const {
+ if (!m_data.ValidOffsetForDataOfSize(offset, length))
+ return;
+ const lldb::offset_t start_offset = offset;
+ const lldb::offset_t end_offset = offset + length;
+ while (m_data.ValidOffset(offset) && offset < end_offset) {
+ const lldb::offset_t op_offset = offset;
+ const uint8_t op = m_data.GetU8(&offset);
+
+ switch (level) {
+ default:
+ break;
+
+ case lldb::eDescriptionLevelBrief:
+ if (op_offset > start_offset)
+ s->PutChar(' ');
+ break;
+
+ case lldb::eDescriptionLevelFull:
+ case lldb::eDescriptionLevelVerbose:
+ if (op_offset > start_offset)
+ s->EOL();
+ s->Indent();
+ if (level == lldb::eDescriptionLevelFull)
+ break;
+ // Fall through for verbose and print offset and DW_OP prefix..
+ s->Printf("0x%8.8" PRIx64 ": %s", op_offset,
+ op >= DW_OP_APPLE_uninit ? "DW_OP_APPLE_" : "DW_OP_");
+ break;
+ }
+
+ switch (op) {
+ case DW_OP_addr:
+ *s << "DW_OP_addr(" << m_data.GetAddress(&offset) << ") ";
+ break; // 0x03 1 address
+ case DW_OP_deref:
+ *s << "DW_OP_deref";
+ break; // 0x06
+ case DW_OP_const1u:
+ s->Printf("DW_OP_const1u(0x%2.2x)", m_data.GetU8(&offset));
+ break; // 0x08 1 1-byte constant
+ case DW_OP_const1s:
+ s->Printf("DW_OP_const1s(0x%2.2x)", m_data.GetU8(&offset));
+ break; // 0x09 1 1-byte constant
+ case DW_OP_const2u:
+ s->Printf("DW_OP_const2u(0x%4.4x)", m_data.GetU16(&offset));
+ break; // 0x0a 1 2-byte constant
+ case DW_OP_const2s:
+ s->Printf("DW_OP_const2s(0x%4.4x)", m_data.GetU16(&offset));
+ break; // 0x0b 1 2-byte constant
+ case DW_OP_const4u:
+ s->Printf("DW_OP_const4u(0x%8.8x)", m_data.GetU32(&offset));
+ break; // 0x0c 1 4-byte constant
+ case DW_OP_const4s:
+ s->Printf("DW_OP_const4s(0x%8.8x)", m_data.GetU32(&offset));
+ break; // 0x0d 1 4-byte constant
+ case DW_OP_const8u:
+ s->Printf("DW_OP_const8u(0x%16.16" PRIx64 ")", m_data.GetU64(&offset));
+ break; // 0x0e 1 8-byte constant
+ case DW_OP_const8s:
+ s->Printf("DW_OP_const8s(0x%16.16" PRIx64 ")", m_data.GetU64(&offset));
+ break; // 0x0f 1 8-byte constant
+ case DW_OP_constu:
+ s->Printf("DW_OP_constu(0x%" PRIx64 ")", m_data.GetULEB128(&offset));
+ break; // 0x10 1 ULEB128 constant
+ case DW_OP_consts:
+ s->Printf("DW_OP_consts(0x%" PRId64 ")", m_data.GetSLEB128(&offset));
+ break; // 0x11 1 SLEB128 constant
+ case DW_OP_dup:
+ s->PutCString("DW_OP_dup");
+ break; // 0x12
+ case DW_OP_drop:
+ s->PutCString("DW_OP_drop");
+ break; // 0x13
+ case DW_OP_over:
+ s->PutCString("DW_OP_over");
+ break; // 0x14
+ case DW_OP_pick:
+ s->Printf("DW_OP_pick(0x%2.2x)", m_data.GetU8(&offset));
+ break; // 0x15 1 1-byte stack index
+ case DW_OP_swap:
+ s->PutCString("DW_OP_swap");
+ break; // 0x16
+ case DW_OP_rot:
+ s->PutCString("DW_OP_rot");
+ break; // 0x17
+ case DW_OP_xderef:
+ s->PutCString("DW_OP_xderef");
+ break; // 0x18
+ case DW_OP_abs:
+ s->PutCString("DW_OP_abs");
+ break; // 0x19
+ case DW_OP_and:
+ s->PutCString("DW_OP_and");
+ break; // 0x1a
+ case DW_OP_div:
+ s->PutCString("DW_OP_div");
+ break; // 0x1b
+ case DW_OP_minus:
+ s->PutCString("DW_OP_minus");
+ break; // 0x1c
+ case DW_OP_mod:
+ s->PutCString("DW_OP_mod");
+ break; // 0x1d
+ case DW_OP_mul:
+ s->PutCString("DW_OP_mul");
+ break; // 0x1e
+ case DW_OP_neg:
+ s->PutCString("DW_OP_neg");
+ break; // 0x1f
+ case DW_OP_not:
+ s->PutCString("DW_OP_not");
+ break; // 0x20
+ case DW_OP_or:
+ s->PutCString("DW_OP_or");
+ break; // 0x21
+ case DW_OP_plus:
+ s->PutCString("DW_OP_plus");
+ break; // 0x22
+ case DW_OP_plus_uconst: // 0x23 1 ULEB128 addend
+ s->Printf("DW_OP_plus_uconst(0x%" PRIx64 ")",
+ m_data.GetULEB128(&offset));
+ break;
+
+ case DW_OP_shl:
+ s->PutCString("DW_OP_shl");
+ break; // 0x24
+ case DW_OP_shr:
+ s->PutCString("DW_OP_shr");
+ break; // 0x25
+ case DW_OP_shra:
+ s->PutCString("DW_OP_shra");
+ break; // 0x26
+ case DW_OP_xor:
+ s->PutCString("DW_OP_xor");
+ break; // 0x27
+ case DW_OP_skip:
+ s->Printf("DW_OP_skip(0x%4.4x)", m_data.GetU16(&offset));
+ break; // 0x2f 1 signed 2-byte constant
+ case DW_OP_bra:
+ s->Printf("DW_OP_bra(0x%4.4x)", m_data.GetU16(&offset));
+ break; // 0x28 1 signed 2-byte constant
+ case DW_OP_eq:
+ s->PutCString("DW_OP_eq");
+ break; // 0x29
+ case DW_OP_ge:
+ s->PutCString("DW_OP_ge");
+ break; // 0x2a
+ case DW_OP_gt:
+ s->PutCString("DW_OP_gt");
+ break; // 0x2b
+ case DW_OP_le:
+ s->PutCString("DW_OP_le");
+ break; // 0x2c
+ case DW_OP_lt:
+ s->PutCString("DW_OP_lt");
+ break; // 0x2d
+ case DW_OP_ne:
+ s->PutCString("DW_OP_ne");
+ break; // 0x2e
+
+ case DW_OP_lit0: // 0x30
+ case DW_OP_lit1: // 0x31
+ case DW_OP_lit2: // 0x32
+ case DW_OP_lit3: // 0x33
+ case DW_OP_lit4: // 0x34
+ case DW_OP_lit5: // 0x35
+ case DW_OP_lit6: // 0x36
+ case DW_OP_lit7: // 0x37
+ case DW_OP_lit8: // 0x38
+ case DW_OP_lit9: // 0x39
+ case DW_OP_lit10: // 0x3A
+ case DW_OP_lit11: // 0x3B
+ case DW_OP_lit12: // 0x3C
+ case DW_OP_lit13: // 0x3D
+ case DW_OP_lit14: // 0x3E
+ case DW_OP_lit15: // 0x3F
+ case DW_OP_lit16: // 0x40
+ case DW_OP_lit17: // 0x41
+ case DW_OP_lit18: // 0x42
+ case DW_OP_lit19: // 0x43
+ case DW_OP_lit20: // 0x44
+ case DW_OP_lit21: // 0x45
+ case DW_OP_lit22: // 0x46
+ case DW_OP_lit23: // 0x47
+ case DW_OP_lit24: // 0x48
+ case DW_OP_lit25: // 0x49
+ case DW_OP_lit26: // 0x4A
+ case DW_OP_lit27: // 0x4B
+ case DW_OP_lit28: // 0x4C
+ case DW_OP_lit29: // 0x4D
+ case DW_OP_lit30: // 0x4E
+ case DW_OP_lit31:
+ s->Printf("DW_OP_lit%i", op - DW_OP_lit0);
+ break; // 0x4f
+
+ case DW_OP_reg0: // 0x50
+ case DW_OP_reg1: // 0x51
+ case DW_OP_reg2: // 0x52
+ case DW_OP_reg3: // 0x53
+ case DW_OP_reg4: // 0x54
+ case DW_OP_reg5: // 0x55
+ case DW_OP_reg6: // 0x56
+ case DW_OP_reg7: // 0x57
+ case DW_OP_reg8: // 0x58
+ case DW_OP_reg9: // 0x59
+ case DW_OP_reg10: // 0x5A
+ case DW_OP_reg11: // 0x5B
+ case DW_OP_reg12: // 0x5C
+ case DW_OP_reg13: // 0x5D
+ case DW_OP_reg14: // 0x5E
+ case DW_OP_reg15: // 0x5F
+ case DW_OP_reg16: // 0x60
+ case DW_OP_reg17: // 0x61
+ case DW_OP_reg18: // 0x62
+ case DW_OP_reg19: // 0x63
+ case DW_OP_reg20: // 0x64
+ case DW_OP_reg21: // 0x65
+ case DW_OP_reg22: // 0x66
+ case DW_OP_reg23: // 0x67
+ case DW_OP_reg24: // 0x68
+ case DW_OP_reg25: // 0x69
+ case DW_OP_reg26: // 0x6A
+ case DW_OP_reg27: // 0x6B
+ case DW_OP_reg28: // 0x6C
+ case DW_OP_reg29: // 0x6D
+ case DW_OP_reg30: // 0x6E
+ case DW_OP_reg31: // 0x6F
+ {
+ uint32_t reg_num = op - DW_OP_reg0;
+ if (abi) {
+ RegisterInfo reg_info;
+ if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
+ if (reg_info.name) {
+ s->PutCString(reg_info.name);
+ break;
+ } else if (reg_info.alt_name) {
+ s->PutCString(reg_info.alt_name);
+ break;
+ }
+ }
+ }
+ s->Printf("DW_OP_reg%u", reg_num);
+ break;
+ } break;
+
+ case DW_OP_breg0:
+ case DW_OP_breg1:
+ case DW_OP_breg2:
+ case DW_OP_breg3:
+ case DW_OP_breg4:
+ case DW_OP_breg5:
+ case DW_OP_breg6:
+ case DW_OP_breg7:
+ case DW_OP_breg8:
+ case DW_OP_breg9:
+ case DW_OP_breg10:
+ case DW_OP_breg11:
+ case DW_OP_breg12:
+ case DW_OP_breg13:
+ case DW_OP_breg14:
+ case DW_OP_breg15:
+ case DW_OP_breg16:
+ case DW_OP_breg17:
+ case DW_OP_breg18:
+ case DW_OP_breg19:
+ case DW_OP_breg20:
+ case DW_OP_breg21:
+ case DW_OP_breg22:
+ case DW_OP_breg23:
+ case DW_OP_breg24:
+ case DW_OP_breg25:
+ case DW_OP_breg26:
+ case DW_OP_breg27:
+ case DW_OP_breg28:
+ case DW_OP_breg29:
+ case DW_OP_breg30:
+ case DW_OP_breg31: {
+ uint32_t reg_num = op - DW_OP_breg0;
+ int64_t reg_offset = m_data.GetSLEB128(&offset);
+ if (abi) {
+ RegisterInfo reg_info;
+ if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
+ if (reg_info.name) {
+ s->Printf("[%s%+" PRIi64 "]", reg_info.name, reg_offset);
+ break;
+ } else if (reg_info.alt_name) {
+ s->Printf("[%s%+" PRIi64 "]", reg_info.alt_name, reg_offset);
+ break;
+ }
+ }
+ }
+ s->Printf("DW_OP_breg%i(0x%" PRIx64 ")", reg_num, reg_offset);
+ } break;
+
+ case DW_OP_regx: // 0x90 1 ULEB128 register
+ {
+ uint32_t reg_num = m_data.GetULEB128(&offset);
+ if (abi) {
+ RegisterInfo reg_info;
+ if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
+ if (reg_info.name) {
+ s->PutCString(reg_info.name);
+ break;
+ } else if (reg_info.alt_name) {
+ s->PutCString(reg_info.alt_name);
+ break;
+ }
+ }
+ }
+ s->Printf("DW_OP_regx(%" PRIu32 ")", reg_num);
+ break;
+ } break;
+ case DW_OP_fbreg: // 0x91 1 SLEB128 offset
+ s->Printf("DW_OP_fbreg(%" PRIi64 ")", m_data.GetSLEB128(&offset));
+ break;
+ case DW_OP_bregx: // 0x92 2 ULEB128 register followed by SLEB128 offset
+ {
+ uint32_t reg_num = m_data.GetULEB128(&offset);
+ int64_t reg_offset = m_data.GetSLEB128(&offset);
+ if (abi) {
+ RegisterInfo reg_info;
+ if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
+ if (reg_info.name) {
+ s->Printf("[%s%+" PRIi64 "]", reg_info.name, reg_offset);
+ break;
+ } else if (reg_info.alt_name) {
+ s->Printf("[%s%+" PRIi64 "]", reg_info.alt_name, reg_offset);
+ break;
+ }
+ }
+ }
+ s->Printf("DW_OP_bregx(reg=%" PRIu32 ",offset=%" PRIi64 ")", reg_num,
+ reg_offset);
+ } break;
+ case DW_OP_piece: // 0x93 1 ULEB128 size of piece addressed
+ s->Printf("DW_OP_piece(0x%" PRIx64 ")", m_data.GetULEB128(&offset));
+ break;
+ case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved
+ s->Printf("DW_OP_deref_size(0x%2.2x)", m_data.GetU8(&offset));
+ break;
+ case DW_OP_xderef_size: // 0x95 1 1-byte size of data retrieved
+ s->Printf("DW_OP_xderef_size(0x%2.2x)", m_data.GetU8(&offset));
+ break;
+ case DW_OP_nop:
+ s->PutCString("DW_OP_nop");
+ break; // 0x96
+ case DW_OP_push_object_address:
+ s->PutCString("DW_OP_push_object_address");
+ break; // 0x97 DWARF3
+ case DW_OP_call2: // 0x98 DWARF3 1 2-byte offset of DIE
+ s->Printf("DW_OP_call2(0x%4.4x)", m_data.GetU16(&offset));
+ break;
+ case DW_OP_call4: // 0x99 DWARF3 1 4-byte offset of DIE
+ s->Printf("DW_OP_call4(0x%8.8x)", m_data.GetU32(&offset));
+ break;
+ case DW_OP_call_ref: // 0x9a DWARF3 1 4- or 8-byte offset of DIE
+ s->Printf("DW_OP_call_ref(0x%8.8" PRIx64 ")", m_data.GetAddress(&offset));
+ break;
+ case DW_OP_form_tls_address:
+ s->PutCString("DW_OP_form_tls_address"); // 0x9b
+ break;
+ case DW_OP_GNU_addr_index: // 0xfb
+ s->Printf("DW_OP_GNU_addr_index(0x%" PRIx64 ")",
+ m_data.GetULEB128(&offset));
+ break;
+ case DW_OP_addrx:
+ s->Printf("DW_OP_addrx(0x%" PRIx64 ")",
+ m_data.GetULEB128(&offset));
+ break;
+ case DW_OP_GNU_const_index: // 0xfc
+ s->Printf("DW_OP_GNU_const_index(0x%" PRIx64 ")",
+ m_data.GetULEB128(&offset));
+ break;
+ case DW_OP_GNU_push_tls_address:
+ s->PutCString("DW_OP_GNU_push_tls_address"); // 0xe0
+ break;
+ case DW_OP_APPLE_uninit:
+ s->PutCString("DW_OP_APPLE_uninit"); // 0xF0
+ break;
+ }
+ }
+}
+
+void DWARFExpression::SetLocationListSlide(addr_t slide) {
+ m_loclist_slide = slide;
+}
+
+int DWARFExpression::GetRegisterKind() { return m_reg_kind; }
+
+void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) {
+ m_reg_kind = reg_kind;
+}
+
+bool DWARFExpression::IsLocationList() const {
+ return m_loclist_slide != LLDB_INVALID_ADDRESS;
+}
+
+void DWARFExpression::GetDescription(Stream *s, lldb::DescriptionLevel level,
+ addr_t location_list_base_addr,
+ ABI *abi) const {
+ if (IsLocationList()) {
+ // We have a location list
+ lldb::offset_t offset = 0;
+ uint32_t count = 0;
+ addr_t curr_base_addr = location_list_base_addr;
+ while (m_data.ValidOffset(offset)) {
+ addr_t begin_addr_offset = LLDB_INVALID_ADDRESS;
+ addr_t end_addr_offset = LLDB_INVALID_ADDRESS;
+ if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset,
+ begin_addr_offset, end_addr_offset))
+ break;
+
+ if (begin_addr_offset == 0 && end_addr_offset == 0)
+ break;
+
+ if (begin_addr_offset < end_addr_offset) {
+ if (count > 0)
+ s->PutCString(", ");
+ VMRange addr_range(curr_base_addr + begin_addr_offset,
+ curr_base_addr + end_addr_offset);
+ addr_range.Dump(s, 0, 8);
+ s->PutChar('{');
+ lldb::offset_t location_length = m_data.GetU16(&offset);
+ DumpLocation(s, offset, location_length, level, abi);
+ s->PutChar('}');
+ offset += location_length;
+ } else {
+ if ((m_data.GetAddressByteSize() == 4 &&
+ (begin_addr_offset == UINT32_MAX)) ||
+ (m_data.GetAddressByteSize() == 8 &&
+ (begin_addr_offset == UINT64_MAX))) {
+ curr_base_addr = end_addr_offset + location_list_base_addr;
+ // We have a new base address
+ if (count > 0)
+ s->PutCString(", ");
+ *s << "base_addr = " << end_addr_offset;
+ }
+ }
+
+ count++;
+ }
+ } else {
+ // We have a normal location that contains DW_OP location opcodes
+ DumpLocation(s, 0, m_data.GetByteSize(), level, abi);
+ }
+}
+
+static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
+ lldb::RegisterKind reg_kind,
+ uint32_t reg_num, Status *error_ptr,
+ Value &value) {
+ if (reg_ctx == nullptr) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("No register context in frame.\n");
+ } else {
+ uint32_t native_reg =
+ reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
+ if (native_reg == LLDB_INVALID_REGNUM) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("Unable to convert register "
+ "kind=%u reg_num=%u to a native "
+ "register number.\n",
+ reg_kind, reg_num);
+ } else {
+ const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoAtIndex(native_reg);
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ if (reg_value.GetScalarValue(value.GetScalar())) {
+ value.SetValueType(Value::eValueTypeScalar);
+ value.SetContext(Value::eContextTypeRegisterInfo,
+ const_cast<RegisterInfo *>(reg_info));
+ if (error_ptr)
+ error_ptr->Clear();
+ return true;
+ } else {
+ // If we get this error, then we need to implement a value buffer in
+ // the dwarf expression evaluation function...
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "register %s can't be converted to a scalar value",
+ reg_info->name);
+ }
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("register %s is not available",
+ reg_info->name);
+ }
+ }
+ }
+ return false;
+}
+
+static offset_t GetOpcodeDataSize(const DataExtractor &data,
+ const lldb::offset_t data_offset,
+ const uint8_t op) {
+ lldb::offset_t offset = data_offset;
+ switch (op) {
+ case DW_OP_addr:
+ case DW_OP_call_ref: // 0x9a 1 address sized offset of DIE (DWARF3)
+ return data.GetAddressByteSize();
+
+ // Opcodes with no arguments
+ case DW_OP_deref: // 0x06
+ case DW_OP_dup: // 0x12
+ case DW_OP_drop: // 0x13
+ case DW_OP_over: // 0x14
+ case DW_OP_swap: // 0x16
+ case DW_OP_rot: // 0x17
+ case DW_OP_xderef: // 0x18
+ case DW_OP_abs: // 0x19
+ case DW_OP_and: // 0x1a
+ case DW_OP_div: // 0x1b
+ case DW_OP_minus: // 0x1c
+ case DW_OP_mod: // 0x1d
+ case DW_OP_mul: // 0x1e
+ case DW_OP_neg: // 0x1f
+ case DW_OP_not: // 0x20
+ case DW_OP_or: // 0x21
+ case DW_OP_plus: // 0x22
+ case DW_OP_shl: // 0x24
+ case DW_OP_shr: // 0x25
+ case DW_OP_shra: // 0x26
+ case DW_OP_xor: // 0x27
+ case DW_OP_eq: // 0x29
+ case DW_OP_ge: // 0x2a
+ case DW_OP_gt: // 0x2b
+ case DW_OP_le: // 0x2c
+ case DW_OP_lt: // 0x2d
+ case DW_OP_ne: // 0x2e
+ case DW_OP_lit0: // 0x30
+ case DW_OP_lit1: // 0x31
+ case DW_OP_lit2: // 0x32
+ case DW_OP_lit3: // 0x33
+ case DW_OP_lit4: // 0x34
+ case DW_OP_lit5: // 0x35
+ case DW_OP_lit6: // 0x36
+ case DW_OP_lit7: // 0x37
+ case DW_OP_lit8: // 0x38
+ case DW_OP_lit9: // 0x39
+ case DW_OP_lit10: // 0x3A
+ case DW_OP_lit11: // 0x3B
+ case DW_OP_lit12: // 0x3C
+ case DW_OP_lit13: // 0x3D
+ case DW_OP_lit14: // 0x3E
+ case DW_OP_lit15: // 0x3F
+ case DW_OP_lit16: // 0x40
+ case DW_OP_lit17: // 0x41
+ case DW_OP_lit18: // 0x42
+ case DW_OP_lit19: // 0x43
+ case DW_OP_lit20: // 0x44
+ case DW_OP_lit21: // 0x45
+ case DW_OP_lit22: // 0x46
+ case DW_OP_lit23: // 0x47
+ case DW_OP_lit24: // 0x48
+ case DW_OP_lit25: // 0x49
+ case DW_OP_lit26: // 0x4A
+ case DW_OP_lit27: // 0x4B
+ case DW_OP_lit28: // 0x4C
+ case DW_OP_lit29: // 0x4D
+ case DW_OP_lit30: // 0x4E
+ case DW_OP_lit31: // 0x4f
+ case DW_OP_reg0: // 0x50
+ case DW_OP_reg1: // 0x51
+ case DW_OP_reg2: // 0x52
+ case DW_OP_reg3: // 0x53
+ case DW_OP_reg4: // 0x54
+ case DW_OP_reg5: // 0x55
+ case DW_OP_reg6: // 0x56
+ case DW_OP_reg7: // 0x57
+ case DW_OP_reg8: // 0x58
+ case DW_OP_reg9: // 0x59
+ case DW_OP_reg10: // 0x5A
+ case DW_OP_reg11: // 0x5B
+ case DW_OP_reg12: // 0x5C
+ case DW_OP_reg13: // 0x5D
+ case DW_OP_reg14: // 0x5E
+ case DW_OP_reg15: // 0x5F
+ case DW_OP_reg16: // 0x60
+ case DW_OP_reg17: // 0x61
+ case DW_OP_reg18: // 0x62
+ case DW_OP_reg19: // 0x63
+ case DW_OP_reg20: // 0x64
+ case DW_OP_reg21: // 0x65
+ case DW_OP_reg22: // 0x66
+ case DW_OP_reg23: // 0x67
+ case DW_OP_reg24: // 0x68
+ case DW_OP_reg25: // 0x69
+ case DW_OP_reg26: // 0x6A
+ case DW_OP_reg27: // 0x6B
+ case DW_OP_reg28: // 0x6C
+ case DW_OP_reg29: // 0x6D
+ case DW_OP_reg30: // 0x6E
+ case DW_OP_reg31: // 0x6F
+ case DW_OP_nop: // 0x96
+ case DW_OP_push_object_address: // 0x97 DWARF3
+ case DW_OP_form_tls_address: // 0x9b DWARF3
+ case DW_OP_call_frame_cfa: // 0x9c DWARF3
+ case DW_OP_stack_value: // 0x9f DWARF4
+ case DW_OP_GNU_push_tls_address: // 0xe0 GNU extension
+ return 0;
+
+ // Opcodes with a single 1 byte arguments
+ case DW_OP_const1u: // 0x08 1 1-byte constant
+ case DW_OP_const1s: // 0x09 1 1-byte constant
+ case DW_OP_pick: // 0x15 1 1-byte stack index
+ case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved
+ case DW_OP_xderef_size: // 0x95 1 1-byte size of data retrieved
+ return 1;
+
+ // Opcodes with a single 2 byte arguments
+ case DW_OP_const2u: // 0x0a 1 2-byte constant
+ case DW_OP_const2s: // 0x0b 1 2-byte constant
+ case DW_OP_skip: // 0x2f 1 signed 2-byte constant
+ case DW_OP_bra: // 0x28 1 signed 2-byte constant
+ case DW_OP_call2: // 0x98 1 2-byte offset of DIE (DWARF3)
+ return 2;
+
+ // Opcodes with a single 4 byte arguments
+ case DW_OP_const4u: // 0x0c 1 4-byte constant
+ case DW_OP_const4s: // 0x0d 1 4-byte constant
+ case DW_OP_call4: // 0x99 1 4-byte offset of DIE (DWARF3)
+ return 4;
+
+ // Opcodes with a single 8 byte arguments
+ case DW_OP_const8u: // 0x0e 1 8-byte constant
+ case DW_OP_const8s: // 0x0f 1 8-byte constant
+ return 8;
+
+ // All opcodes that have a single ULEB (signed or unsigned) argument
+ case DW_OP_addrx: // 0xa1 1 ULEB128 index
+ case DW_OP_constu: // 0x10 1 ULEB128 constant
+ case DW_OP_consts: // 0x11 1 SLEB128 constant
+ case DW_OP_plus_uconst: // 0x23 1 ULEB128 addend
+ case DW_OP_breg0: // 0x70 1 ULEB128 register
+ case DW_OP_breg1: // 0x71 1 ULEB128 register
+ case DW_OP_breg2: // 0x72 1 ULEB128 register
+ case DW_OP_breg3: // 0x73 1 ULEB128 register
+ case DW_OP_breg4: // 0x74 1 ULEB128 register
+ case DW_OP_breg5: // 0x75 1 ULEB128 register
+ case DW_OP_breg6: // 0x76 1 ULEB128 register
+ case DW_OP_breg7: // 0x77 1 ULEB128 register
+ case DW_OP_breg8: // 0x78 1 ULEB128 register
+ case DW_OP_breg9: // 0x79 1 ULEB128 register
+ case DW_OP_breg10: // 0x7a 1 ULEB128 register
+ case DW_OP_breg11: // 0x7b 1 ULEB128 register
+ case DW_OP_breg12: // 0x7c 1 ULEB128 register
+ case DW_OP_breg13: // 0x7d 1 ULEB128 register
+ case DW_OP_breg14: // 0x7e 1 ULEB128 register
+ case DW_OP_breg15: // 0x7f 1 ULEB128 register
+ case DW_OP_breg16: // 0x80 1 ULEB128 register
+ case DW_OP_breg17: // 0x81 1 ULEB128 register
+ case DW_OP_breg18: // 0x82 1 ULEB128 register
+ case DW_OP_breg19: // 0x83 1 ULEB128 register
+ case DW_OP_breg20: // 0x84 1 ULEB128 register
+ case DW_OP_breg21: // 0x85 1 ULEB128 register
+ case DW_OP_breg22: // 0x86 1 ULEB128 register
+ case DW_OP_breg23: // 0x87 1 ULEB128 register
+ case DW_OP_breg24: // 0x88 1 ULEB128 register
+ case DW_OP_breg25: // 0x89 1 ULEB128 register
+ case DW_OP_breg26: // 0x8a 1 ULEB128 register
+ case DW_OP_breg27: // 0x8b 1 ULEB128 register
+ case DW_OP_breg28: // 0x8c 1 ULEB128 register
+ case DW_OP_breg29: // 0x8d 1 ULEB128 register
+ case DW_OP_breg30: // 0x8e 1 ULEB128 register
+ case DW_OP_breg31: // 0x8f 1 ULEB128 register
+ case DW_OP_regx: // 0x90 1 ULEB128 register
+ case DW_OP_fbreg: // 0x91 1 SLEB128 offset
+ case DW_OP_piece: // 0x93 1 ULEB128 size of piece addressed
+ case DW_OP_GNU_addr_index: // 0xfb 1 ULEB128 index
+ case DW_OP_GNU_const_index: // 0xfc 1 ULEB128 index
+ data.Skip_LEB128(&offset);
+ return offset - data_offset;
+
+ // All opcodes that have a 2 ULEB (signed or unsigned) arguments
+ case DW_OP_bregx: // 0x92 2 ULEB128 register followed by SLEB128 offset
+ case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3);
+ data.Skip_LEB128(&offset);
+ data.Skip_LEB128(&offset);
+ return offset - data_offset;
+
+ case DW_OP_implicit_value: // 0x9e ULEB128 size followed by block of that size
+ // (DWARF4)
+ {
+ uint64_t block_len = data.Skip_LEB128(&offset);
+ offset += block_len;
+ return offset - data_offset;
+ }
+
+ default:
+ break;
+ }
+ return LLDB_INVALID_OFFSET;
+}
+
+lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(uint32_t op_addr_idx,
+ bool &error) const {
+ error = false;
+ if (IsLocationList())
+ return LLDB_INVALID_ADDRESS;
+ lldb::offset_t offset = 0;
+ uint32_t curr_op_addr_idx = 0;
+ while (m_data.ValidOffset(offset)) {
+ const uint8_t op = m_data.GetU8(&offset);
+
+ if (op == DW_OP_addr) {
+ const lldb::addr_t op_file_addr = m_data.GetAddress(&offset);
+ if (curr_op_addr_idx == op_addr_idx)
+ return op_file_addr;
+ else
+ ++curr_op_addr_idx;
+ } else if (op == DW_OP_GNU_addr_index || op == DW_OP_addrx) {
+ uint64_t index = m_data.GetULEB128(&offset);
+ if (curr_op_addr_idx == op_addr_idx) {
+ if (!m_dwarf_cu) {
+ error = true;
+ break;
+ }
+
+ return ReadAddressFromDebugAddrSection(m_dwarf_cu, index);
+ } else
+ ++curr_op_addr_idx;
+ } else {
+ const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
+ if (op_arg_size == LLDB_INVALID_OFFSET) {
+ error = true;
+ break;
+ }
+ offset += op_arg_size;
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool DWARFExpression::Update_DW_OP_addr(lldb::addr_t file_addr) {
+ if (IsLocationList())
+ return false;
+ lldb::offset_t offset = 0;
+ while (m_data.ValidOffset(offset)) {
+ const uint8_t op = m_data.GetU8(&offset);
+
+ if (op == DW_OP_addr) {
+ const uint32_t addr_byte_size = m_data.GetAddressByteSize();
+ // We have to make a copy of the data as we don't know if this data is
+ // from a read only memory mapped buffer, so we duplicate all of the data
+ // first, then modify it, and if all goes well, we then replace the data
+ // for this expression
+
+ // So first we copy the data into a heap buffer
+ std::unique_ptr<DataBufferHeap> head_data_up(
+ new DataBufferHeap(m_data.GetDataStart(), m_data.GetByteSize()));
+
+ // Make en encoder so we can write the address into the buffer using the
+ // correct byte order (endianness)
+ DataEncoder encoder(head_data_up->GetBytes(), head_data_up->GetByteSize(),
+ m_data.GetByteOrder(), addr_byte_size);
+
+ // Replace the address in the new buffer
+ if (encoder.PutMaxU64(offset, addr_byte_size, file_addr) == UINT32_MAX)
+ return false;
+
+ // All went well, so now we can reset the data using a shared pointer to
+ // the heap data so "m_data" will now correctly manage the heap data.
+ m_data.SetData(DataBufferSP(head_data_up.release()));
+ return true;
+ } else {
+ const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
+ if (op_arg_size == LLDB_INVALID_OFFSET)
+ break;
+ offset += op_arg_size;
+ }
+ }
+ return false;
+}
+
+bool DWARFExpression::ContainsThreadLocalStorage() const {
+ // We are assuming for now that any thread local variable will not have a
+ // location list. This has been true for all thread local variables we have
+ // seen so far produced by any compiler.
+ if (IsLocationList())
+ return false;
+ lldb::offset_t offset = 0;
+ while (m_data.ValidOffset(offset)) {
+ const uint8_t op = m_data.GetU8(&offset);
+
+ if (op == DW_OP_form_tls_address || op == DW_OP_GNU_push_tls_address)
+ return true;
+ const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
+ if (op_arg_size == LLDB_INVALID_OFFSET)
+ return false;
+ else
+ offset += op_arg_size;
+ }
+ return false;
+}
+bool DWARFExpression::LinkThreadLocalStorage(
+ lldb::ModuleSP new_module_sp,
+ std::function<lldb::addr_t(lldb::addr_t file_addr)> const
+ &link_address_callback) {
+ // We are assuming for now that any thread local variable will not have a
+ // location list. This has been true for all thread local variables we have
+ // seen so far produced by any compiler.
+ if (IsLocationList())
+ return false;
+
+ const uint32_t addr_byte_size = m_data.GetAddressByteSize();
+ // We have to make a copy of the data as we don't know if this data is from a
+ // read only memory mapped buffer, so we duplicate all of the data first,
+ // then modify it, and if all goes well, we then replace the data for this
+ // expression
+
+ // So first we copy the data into a heap buffer
+ std::shared_ptr<DataBufferHeap> heap_data_sp(
+ new DataBufferHeap(m_data.GetDataStart(), m_data.GetByteSize()));
+
+ // Make en encoder so we can write the address into the buffer using the
+ // correct byte order (endianness)
+ DataEncoder encoder(heap_data_sp->GetBytes(), heap_data_sp->GetByteSize(),
+ m_data.GetByteOrder(), addr_byte_size);
+
+ lldb::offset_t offset = 0;
+ lldb::offset_t const_offset = 0;
+ lldb::addr_t const_value = 0;
+ size_t const_byte_size = 0;
+ while (m_data.ValidOffset(offset)) {
+ const uint8_t op = m_data.GetU8(&offset);
+
+ bool decoded_data = false;
+ switch (op) {
+ case DW_OP_const4u:
+ // Remember the const offset in case we later have a
+ // DW_OP_form_tls_address or DW_OP_GNU_push_tls_address
+ const_offset = offset;
+ const_value = m_data.GetU32(&offset);
+ decoded_data = true;
+ const_byte_size = 4;
+ break;
+
+ case DW_OP_const8u:
+ // Remember the const offset in case we later have a
+ // DW_OP_form_tls_address or DW_OP_GNU_push_tls_address
+ const_offset = offset;
+ const_value = m_data.GetU64(&offset);
+ decoded_data = true;
+ const_byte_size = 8;
+ break;
+
+ case DW_OP_form_tls_address:
+ case DW_OP_GNU_push_tls_address:
+ // DW_OP_form_tls_address and DW_OP_GNU_push_tls_address must be preceded
+ // by a file address on the stack. We assume that DW_OP_const4u or
+ // DW_OP_const8u is used for these values, and we check that the last
+ // opcode we got before either of these was DW_OP_const4u or
+ // DW_OP_const8u. If so, then we can link the value accodingly. For
+ // Darwin, the value in the DW_OP_const4u or DW_OP_const8u is the file
+ // address of a structure that contains a function pointer, the pthread
+ // key and the offset into the data pointed to by the pthread key. So we
+ // must link this address and also set the module of this expression to
+ // the new_module_sp so we can resolve the file address correctly
+ if (const_byte_size > 0) {
+ lldb::addr_t linked_file_addr = link_address_callback(const_value);
+ if (linked_file_addr == LLDB_INVALID_ADDRESS)
+ return false;
+ // Replace the address in the new buffer
+ if (encoder.PutMaxU64(const_offset, const_byte_size,
+ linked_file_addr) == UINT32_MAX)
+ return false;
+ }
+ break;
+
+ default:
+ const_offset = 0;
+ const_value = 0;
+ const_byte_size = 0;
+ break;
+ }
+
+ if (!decoded_data) {
+ const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
+ if (op_arg_size == LLDB_INVALID_OFFSET)
+ return false;
+ else
+ offset += op_arg_size;
+ }
+ }
+
+ // If we linked the TLS address correctly, update the module so that when the
+ // expression is evaluated it can resolve the file address to a load address
+ // and read the
+ // TLS data
+ m_module_wp = new_module_sp;
+ m_data.SetData(heap_data_sp);
+ return true;
+}
+
+bool DWARFExpression::LocationListContainsAddress(
+ lldb::addr_t loclist_base_addr, lldb::addr_t addr) const {
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (IsLocationList()) {
+ lldb::offset_t offset = 0;
+
+ if (loclist_base_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ while (m_data.ValidOffset(offset)) {
+ // We need to figure out what the value is for the location.
+ addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, lo_pc,
+ hi_pc))
+ break;
+
+ if (lo_pc == 0 && hi_pc == 0)
+ break;
+
+ lo_pc += loclist_base_addr - m_loclist_slide;
+ hi_pc += loclist_base_addr - m_loclist_slide;
+
+ if (lo_pc <= addr && addr < hi_pc)
+ return true;
+
+ offset += m_data.GetU16(&offset);
+ }
+ }
+ return false;
+}
+
+bool DWARFExpression::GetLocation(addr_t base_addr, addr_t pc,
+ lldb::offset_t &offset,
+ lldb::offset_t &length) {
+ offset = 0;
+ if (!IsLocationList()) {
+ length = m_data.GetByteSize();
+ return true;
+ }
+
+ if (base_addr != LLDB_INVALID_ADDRESS && pc != LLDB_INVALID_ADDRESS) {
+ addr_t curr_base_addr = base_addr;
+
+ while (m_data.ValidOffset(offset)) {
+ // We need to figure out what the value is for the location.
+ addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, lo_pc,
+ hi_pc))
+ break;
+
+ if (lo_pc == 0 && hi_pc == 0)
+ break;
+
+ lo_pc += curr_base_addr - m_loclist_slide;
+ hi_pc += curr_base_addr - m_loclist_slide;
+
+ length = m_data.GetU16(&offset);
+
+ if (length > 0 && lo_pc <= pc && pc < hi_pc)
+ return true;
+
+ offset += length;
+ }
+ }
+ offset = LLDB_INVALID_OFFSET;
+ length = 0;
+ return false;
+}
+
+bool DWARFExpression::DumpLocationForAddress(Stream *s,
+ lldb::DescriptionLevel level,
+ addr_t base_addr, addr_t address,
+ ABI *abi) {
+ lldb::offset_t offset = 0;
+ lldb::offset_t length = 0;
+
+ if (GetLocation(base_addr, address, offset, length)) {
+ if (length > 0) {
+ DumpLocation(s, offset, length, level, abi);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DWARFExpression::Evaluate(ExecutionContextScope *exe_scope,
+ lldb::addr_t loclist_base_load_addr,
+ const Value *initial_value_ptr,
+ const Value *object_address_ptr, Value &result,
+ Status *error_ptr) const {
+ ExecutionContext exe_ctx(exe_scope);
+ return Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, initial_value_ptr,
+ object_address_ptr, result, error_ptr);
+}
+
+bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx,
+ RegisterContext *reg_ctx,
+ lldb::addr_t loclist_base_load_addr,
+ const Value *initial_value_ptr,
+ const Value *object_address_ptr, Value &result,
+ Status *error_ptr) const {
+ ModuleSP module_sp = m_module_wp.lock();
+
+ if (IsLocationList()) {
+ lldb::offset_t offset = 0;
+ addr_t pc;
+ StackFrame *frame = nullptr;
+ if (reg_ctx)
+ pc = reg_ctx->GetPC();
+ else {
+ frame = exe_ctx->GetFramePtr();
+ if (!frame)
+ return false;
+ RegisterContextSP reg_ctx_sp = frame->GetRegisterContext();
+ if (!reg_ctx_sp)
+ return false;
+ pc = reg_ctx_sp->GetPC();
+ }
+
+ if (loclist_base_load_addr != LLDB_INVALID_ADDRESS) {
+ if (pc == LLDB_INVALID_ADDRESS) {
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid PC in frame.");
+ return false;
+ }
+
+ addr_t curr_loclist_base_load_addr = loclist_base_load_addr;
+
+ while (m_data.ValidOffset(offset)) {
+ // We need to figure out what the value is for the location.
+ addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset,
+ lo_pc, hi_pc))
+ break;
+
+ if (lo_pc == 0 && hi_pc == 0)
+ break;
+
+ lo_pc += curr_loclist_base_load_addr - m_loclist_slide;
+ hi_pc += curr_loclist_base_load_addr - m_loclist_slide;
+
+ uint16_t length = m_data.GetU16(&offset);
+
+ if (length > 0 && lo_pc <= pc && pc < hi_pc) {
+ return DWARFExpression::Evaluate(
+ exe_ctx, reg_ctx, module_sp, m_data, m_dwarf_cu, offset, length,
+ m_reg_kind, initial_value_ptr, object_address_ptr, result,
+ error_ptr);
+ }
+ offset += length;
+ }
+ }
+ if (error_ptr)
+ error_ptr->SetErrorString("variable not available");
+ return false;
+ }
+
+ // Not a location list, just a single expression.
+ return DWARFExpression::Evaluate(
+ exe_ctx, reg_ctx, module_sp, m_data, m_dwarf_cu, 0, m_data.GetByteSize(),
+ m_reg_kind, initial_value_ptr, object_address_ptr, result, error_ptr);
+}
+
+bool DWARFExpression::Evaluate(
+ ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
+ lldb::ModuleSP module_sp, const DataExtractor &opcodes,
+ const DWARFUnit *dwarf_cu, const lldb::offset_t opcodes_offset,
+ const lldb::offset_t opcodes_length, const lldb::RegisterKind reg_kind,
+ const Value *initial_value_ptr, const Value *object_address_ptr,
+ Value &result, Status *error_ptr) {
+
+ if (opcodes_length == 0) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "no location, value may have been optimized out");
+ return false;
+ }
+ std::vector<Value> stack;
+
+ Process *process = nullptr;
+ StackFrame *frame = nullptr;
+
+ if (exe_ctx) {
+ process = exe_ctx->GetProcessPtr();
+ frame = exe_ctx->GetFramePtr();
+ }
+ if (reg_ctx == nullptr && frame)
+ reg_ctx = frame->GetRegisterContext().get();
+
+ if (initial_value_ptr)
+ stack.push_back(*initial_value_ptr);
+
+ lldb::offset_t offset = opcodes_offset;
+ const lldb::offset_t end_offset = opcodes_offset + opcodes_length;
+ Value tmp;
+ uint32_t reg_num;
+
+ /// Insertion point for evaluating multi-piece expression.
+ uint64_t op_piece_offset = 0;
+ Value pieces; // Used for DW_OP_piece
+
+ // Make sure all of the data is available in opcodes.
+ if (!opcodes.ValidOffsetForDataOfSize(opcodes_offset, opcodes_length)) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "invalid offset and/or length for opcodes buffer.");
+ return false;
+ }
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ while (opcodes.ValidOffset(offset) && offset < end_offset) {
+ const lldb::offset_t op_offset = offset;
+ const uint8_t op = opcodes.GetU8(&offset);
+
+ if (log && log->GetVerbose()) {
+ size_t count = stack.size();
+ log->Printf("Stack before operation has %" PRIu64 " values:",
+ (uint64_t)count);
+ for (size_t i = 0; i < count; ++i) {
+ StreamString new_value;
+ new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
+ stack[i].Dump(&new_value);
+ log->Printf(" %s", new_value.GetData());
+ }
+ log->Printf("0x%8.8" PRIx64 ": %s", op_offset, DW_OP_value_to_name(op));
+ }
+
+ switch (op) {
+ // The DW_OP_addr operation has a single operand that encodes a machine
+ // address and whose size is the size of an address on the target machine.
+ case DW_OP_addr:
+ stack.push_back(Scalar(opcodes.GetAddress(&offset)));
+ stack.back().SetValueType(Value::eValueTypeFileAddress);
+ // Convert the file address to a load address, so subsequent
+ // DWARF operators can operate on it.
+ if (frame)
+ stack.back().ConvertToLoadAddress(module_sp.get(),
+ frame->CalculateTarget().get());
+ break;
+
+ // The DW_OP_addr_sect_offset4 is used for any location expressions in
+ // shared libraries that have a location like:
+ // DW_OP_addr(0x1000)
+ // If this address resides in a shared library, then this virtual address
+ // won't make sense when it is evaluated in the context of a running
+ // process where shared libraries have been slid. To account for this, this
+ // new address type where we can store the section pointer and a 4 byte
+ // offset.
+ // case DW_OP_addr_sect_offset4:
+ // {
+ // result_type = eResultTypeFileAddress;
+ // lldb::Section *sect = (lldb::Section
+ // *)opcodes.GetMaxU64(&offset, sizeof(void *));
+ // lldb::addr_t sect_offset = opcodes.GetU32(&offset);
+ //
+ // Address so_addr (sect, sect_offset);
+ // lldb::addr_t load_addr = so_addr.GetLoadAddress();
+ // if (load_addr != LLDB_INVALID_ADDRESS)
+ // {
+ // // We successfully resolve a file address to a load
+ // // address.
+ // stack.push_back(load_addr);
+ // break;
+ // }
+ // else
+ // {
+ // // We were able
+ // if (error_ptr)
+ // error_ptr->SetErrorStringWithFormat ("Section %s in
+ // %s is not currently loaded.\n",
+ // sect->GetName().AsCString(),
+ // sect->GetModule()->GetFileSpec().GetFilename().AsCString());
+ // return false;
+ // }
+ // }
+ // break;
+
+ // OPCODE: DW_OP_deref
+ // OPERANDS: none
+ // DESCRIPTION: Pops the top stack entry and treats it as an address.
+ // The value retrieved from that address is pushed. The size of the data
+ // retrieved from the dereferenced address is the size of an address on the
+ // target machine.
+ case DW_OP_deref: {
+ if (stack.empty()) {
+ if (error_ptr)
+ error_ptr->SetErrorString("Expression stack empty for DW_OP_deref.");
+ return false;
+ }
+ Value::ValueType value_type = stack.back().GetValueType();
+ switch (value_type) {
+ case Value::eValueTypeHostAddress: {
+ void *src = (void *)stack.back().GetScalar().ULongLong();
+ intptr_t ptr;
+ ::memcpy(&ptr, src, sizeof(void *));
+ stack.back().GetScalar() = ptr;
+ stack.back().ClearContext();
+ } break;
+ case Value::eValueTypeFileAddress: {
+ auto file_addr = stack.back().GetScalar().ULongLong(
+ LLDB_INVALID_ADDRESS);
+ if (!module_sp) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "need module to resolve file address for DW_OP_deref");
+ return false;
+ }
+ Address so_addr;
+ if (!module_sp->ResolveFileAddress(file_addr, so_addr)) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "failed to resolve file address in module");
+ return false;
+ }
+ addr_t load_Addr = so_addr.GetLoadAddress(exe_ctx->GetTargetPtr());
+ if (load_Addr == LLDB_INVALID_ADDRESS) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "failed to resolve load address");
+ return false;
+ }
+ stack.back().GetScalar() = load_Addr;
+ stack.back().SetValueType(Value::eValueTypeLoadAddress);
+ // Fall through to load address code below...
+ } LLVM_FALLTHROUGH;
+ case Value::eValueTypeLoadAddress:
+ if (exe_ctx) {
+ if (process) {
+ lldb::addr_t pointer_addr =
+ stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ Status error;
+ lldb::addr_t pointer_value =
+ process->ReadPointerFromMemory(pointer_addr, error);
+ if (pointer_value != LLDB_INVALID_ADDRESS) {
+ stack.back().GetScalar() = pointer_value;
+ stack.back().ClearContext();
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "Failed to dereference pointer from 0x%" PRIx64
+ " for DW_OP_deref: %s\n",
+ pointer_addr, error.AsCString());
+ return false;
+ }
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "NULL process for DW_OP_deref.\n");
+ return false;
+ }
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "NULL execution context for DW_OP_deref.\n");
+ return false;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ } break;
+
+ // OPCODE: DW_OP_deref_size
+ // OPERANDS: 1
+ // 1 - uint8_t that specifies the size of the data to dereference.
+ // DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top
+ // stack entry and treats it as an address. The value retrieved from that
+ // address is pushed. In the DW_OP_deref_size operation, however, the size
+ // in bytes of the data retrieved from the dereferenced address is
+ // specified by the single operand. This operand is a 1-byte unsigned
+ // integral constant whose value may not be larger than the size of an
+ // address on the target machine. The data retrieved is zero extended to
+ // the size of an address on the target machine before being pushed on the
+ // expression stack.
+ case DW_OP_deref_size: {
+ if (stack.empty()) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack empty for DW_OP_deref_size.");
+ return false;
+ }
+ uint8_t size = opcodes.GetU8(&offset);
+ Value::ValueType value_type = stack.back().GetValueType();
+ switch (value_type) {
+ case Value::eValueTypeHostAddress: {
+ void *src = (void *)stack.back().GetScalar().ULongLong();
+ intptr_t ptr;
+ ::memcpy(&ptr, src, sizeof(void *));
+ // I can't decide whether the size operand should apply to the bytes in
+ // their
+ // lldb-host endianness or the target endianness.. I doubt this'll ever
+ // come up but I'll opt for assuming big endian regardless.
+ switch (size) {
+ case 1:
+ ptr = ptr & 0xff;
+ break;
+ case 2:
+ ptr = ptr & 0xffff;
+ break;
+ case 3:
+ ptr = ptr & 0xffffff;
+ break;
+ case 4:
+ ptr = ptr & 0xffffffff;
+ break;
+ // the casts are added to work around the case where intptr_t is a 32
+ // bit quantity;
+ // presumably we won't hit the 5..7 cases if (void*) is 32-bits in this
+ // program.
+ case 5:
+ ptr = (intptr_t)ptr & 0xffffffffffULL;
+ break;
+ case 6:
+ ptr = (intptr_t)ptr & 0xffffffffffffULL;
+ break;
+ case 7:
+ ptr = (intptr_t)ptr & 0xffffffffffffffULL;
+ break;
+ default:
+ break;
+ }
+ stack.back().GetScalar() = ptr;
+ stack.back().ClearContext();
+ } break;
+ case Value::eValueTypeLoadAddress:
+ if (exe_ctx) {
+ if (process) {
+ lldb::addr_t pointer_addr =
+ stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ uint8_t addr_bytes[sizeof(lldb::addr_t)];
+ Status error;
+ if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) ==
+ size) {
+ DataExtractor addr_data(addr_bytes, sizeof(addr_bytes),
+ process->GetByteOrder(), size);
+ lldb::offset_t addr_data_offset = 0;
+ switch (size) {
+ case 1:
+ stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset);
+ break;
+ case 2:
+ stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset);
+ break;
+ case 4:
+ stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset);
+ break;
+ case 8:
+ stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset);
+ break;
+ default:
+ stack.back().GetScalar() =
+ addr_data.GetPointer(&addr_data_offset);
+ }
+ stack.back().ClearContext();
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "Failed to dereference pointer from 0x%" PRIx64
+ " for DW_OP_deref: %s\n",
+ pointer_addr, error.AsCString());
+ return false;
+ }
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "NULL process for DW_OP_deref.\n");
+ return false;
+ }
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "NULL execution context for DW_OP_deref.\n");
+ return false;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ } break;
+
+ // OPCODE: DW_OP_xderef_size
+ // OPERANDS: 1
+ // 1 - uint8_t that specifies the size of the data to dereference.
+ // DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at
+ // the top of the stack is treated as an address. The second stack entry is
+ // treated as an "address space identifier" for those architectures that
+ // support multiple address spaces. The top two stack elements are popped,
+ // a data item is retrieved through an implementation-defined address
+ // calculation and pushed as the new stack top. In the DW_OP_xderef_size
+ // operation, however, the size in bytes of the data retrieved from the
+ // dereferenced address is specified by the single operand. This operand is
+ // a 1-byte unsigned integral constant whose value may not be larger than
+ // the size of an address on the target machine. The data retrieved is zero
+ // extended to the size of an address on the target machine before being
+ // pushed on the expression stack.
+ case DW_OP_xderef_size:
+ if (error_ptr)
+ error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size.");
+ return false;
+ // OPCODE: DW_OP_xderef
+ // OPERANDS: none
+ // DESCRIPTION: Provides an extended dereference mechanism. The entry at
+ // the top of the stack is treated as an address. The second stack entry is
+ // treated as an "address space identifier" for those architectures that
+ // support multiple address spaces. The top two stack elements are popped,
+ // a data item is retrieved through an implementation-defined address
+ // calculation and pushed as the new stack top. The size of the data
+ // retrieved from the dereferenced address is the size of an address on the
+ // target machine.
+ case DW_OP_xderef:
+ if (error_ptr)
+ error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef.");
+ return false;
+
+ // All DW_OP_constXXX opcodes have a single operand as noted below:
+ //
+ // Opcode Operand 1
+ // DW_OP_const1u 1-byte unsigned integer constant DW_OP_const1s
+ // 1-byte signed integer constant DW_OP_const2u 2-byte unsigned integer
+ // constant DW_OP_const2s 2-byte signed integer constant DW_OP_const4u
+ // 4-byte unsigned integer constant DW_OP_const4s 4-byte signed integer
+ // constant DW_OP_const8u 8-byte unsigned integer constant DW_OP_const8s
+ // 8-byte signed integer constant DW_OP_constu unsigned LEB128 integer
+ // constant DW_OP_consts signed LEB128 integer constant
+ case DW_OP_const1u:
+ stack.push_back(Scalar((uint8_t)opcodes.GetU8(&offset)));
+ break;
+ case DW_OP_const1s:
+ stack.push_back(Scalar((int8_t)opcodes.GetU8(&offset)));
+ break;
+ case DW_OP_const2u:
+ stack.push_back(Scalar((uint16_t)opcodes.GetU16(&offset)));
+ break;
+ case DW_OP_const2s:
+ stack.push_back(Scalar((int16_t)opcodes.GetU16(&offset)));
+ break;
+ case DW_OP_const4u:
+ stack.push_back(Scalar((uint32_t)opcodes.GetU32(&offset)));
+ break;
+ case DW_OP_const4s:
+ stack.push_back(Scalar((int32_t)opcodes.GetU32(&offset)));
+ break;
+ case DW_OP_const8u:
+ stack.push_back(Scalar((uint64_t)opcodes.GetU64(&offset)));
+ break;
+ case DW_OP_const8s:
+ stack.push_back(Scalar((int64_t)opcodes.GetU64(&offset)));
+ break;
+ case DW_OP_constu:
+ stack.push_back(Scalar(opcodes.GetULEB128(&offset)));
+ break;
+ case DW_OP_consts:
+ stack.push_back(Scalar(opcodes.GetSLEB128(&offset)));
+ break;
+
+ // OPCODE: DW_OP_dup
+ // OPERANDS: none
+ // DESCRIPTION: duplicates the value at the top of the stack
+ case DW_OP_dup:
+ if (stack.empty()) {
+ if (error_ptr)
+ error_ptr->SetErrorString("Expression stack empty for DW_OP_dup.");
+ return false;
+ } else
+ stack.push_back(stack.back());
+ break;
+
+ // OPCODE: DW_OP_drop
+ // OPERANDS: none
+ // DESCRIPTION: pops the value at the top of the stack
+ case DW_OP_drop:
+ if (stack.empty()) {
+ if (error_ptr)
+ error_ptr->SetErrorString("Expression stack empty for DW_OP_drop.");
+ return false;
+ } else
+ stack.pop_back();
+ break;
+
+ // OPCODE: DW_OP_over
+ // OPERANDS: none
+ // DESCRIPTION: Duplicates the entry currently second in the stack at
+ // the top of the stack.
+ case DW_OP_over:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_over.");
+ return false;
+ } else
+ stack.push_back(stack[stack.size() - 2]);
+ break;
+
+ // OPCODE: DW_OP_pick
+ // OPERANDS: uint8_t index into the current stack
+ // DESCRIPTION: The stack entry with the specified index (0 through 255,
+ // inclusive) is pushed on the stack
+ case DW_OP_pick: {
+ uint8_t pick_idx = opcodes.GetU8(&offset);
+ if (pick_idx < stack.size())
+ stack.push_back(stack[stack.size() - 1 - pick_idx]);
+ else {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "Index %u out of range for DW_OP_pick.\n", pick_idx);
+ return false;
+ }
+ } break;
+
+ // OPCODE: DW_OP_swap
+ // OPERANDS: none
+ // DESCRIPTION: swaps the top two stack entries. The entry at the top
+ // of the stack becomes the second stack entry, and the second entry
+ // becomes the top of the stack
+ case DW_OP_swap:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_swap.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.back() = stack[stack.size() - 2];
+ stack[stack.size() - 2] = tmp;
+ }
+ break;
+
+ // OPCODE: DW_OP_rot
+ // OPERANDS: none
+ // DESCRIPTION: Rotates the first three stack entries. The entry at
+ // the top of the stack becomes the third stack entry, the second entry
+ // becomes the top of the stack, and the third entry becomes the second
+ // entry.
+ case DW_OP_rot:
+ if (stack.size() < 3) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 3 items for DW_OP_rot.");
+ return false;
+ } else {
+ size_t last_idx = stack.size() - 1;
+ Value old_top = stack[last_idx];
+ stack[last_idx] = stack[last_idx - 1];
+ stack[last_idx - 1] = stack[last_idx - 2];
+ stack[last_idx - 2] = old_top;
+ }
+ break;
+
+ // OPCODE: DW_OP_abs
+ // OPERANDS: none
+ // DESCRIPTION: pops the top stack entry, interprets it as a signed
+ // value and pushes its absolute value. If the absolute value can not be
+ // represented, the result is undefined.
+ case DW_OP_abs:
+ if (stack.empty()) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 1 item for DW_OP_abs.");
+ return false;
+ } else if (!stack.back().ResolveValue(exe_ctx).AbsoluteValue()) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Failed to take the absolute value of the first stack item.");
+ return false;
+ }
+ break;
+
+ // OPCODE: DW_OP_and
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack values, performs a bitwise and
+ // operation on the two, and pushes the result.
+ case DW_OP_and:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_and.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) & tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_div
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack values, divides the former second
+ // entry by the former top of the stack using signed division, and pushes
+ // the result.
+ case DW_OP_div:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_div.");
+ return false;
+ } else {
+ tmp = stack.back();
+ if (tmp.ResolveValue(exe_ctx).IsZero()) {
+ if (error_ptr)
+ error_ptr->SetErrorString("Divide by zero.");
+ return false;
+ } else {
+ stack.pop_back();
+ stack.back() =
+ stack.back().ResolveValue(exe_ctx) / tmp.ResolveValue(exe_ctx);
+ if (!stack.back().ResolveValue(exe_ctx).IsValid()) {
+ if (error_ptr)
+ error_ptr->SetErrorString("Divide failed.");
+ return false;
+ }
+ }
+ }
+ break;
+
+ // OPCODE: DW_OP_minus
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack values, subtracts the former top
+ // of the stack from the former second entry, and pushes the result.
+ case DW_OP_minus:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_minus.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) - tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_mod
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack values and pushes the result of
+ // the calculation: former second stack entry modulo the former top of the
+ // stack.
+ case DW_OP_mod:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_mod.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) % tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_mul
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack entries, multiplies them
+ // together, and pushes the result.
+ case DW_OP_mul:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_mul.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) * tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_neg
+ // OPERANDS: none
+ // DESCRIPTION: pops the top stack entry, and pushes its negation.
+ case DW_OP_neg:
+ if (stack.empty()) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 1 item for DW_OP_neg.");
+ return false;
+ } else {
+ if (!stack.back().ResolveValue(exe_ctx).UnaryNegate()) {
+ if (error_ptr)
+ error_ptr->SetErrorString("Unary negate failed.");
+ return false;
+ }
+ }
+ break;
+
+ // OPCODE: DW_OP_not
+ // OPERANDS: none
+ // DESCRIPTION: pops the top stack entry, and pushes its bitwise
+ // complement
+ case DW_OP_not:
+ if (stack.empty()) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 1 item for DW_OP_not.");
+ return false;
+ } else {
+ if (!stack.back().ResolveValue(exe_ctx).OnesComplement()) {
+ if (error_ptr)
+ error_ptr->SetErrorString("Logical NOT failed.");
+ return false;
+ }
+ }
+ break;
+
+ // OPCODE: DW_OP_or
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack entries, performs a bitwise or
+ // operation on the two, and pushes the result.
+ case DW_OP_or:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_or.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) | tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_plus
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack entries, adds them together, and
+ // pushes the result.
+ case DW_OP_plus:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_plus.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().GetScalar() += tmp.GetScalar();
+ }
+ break;
+
+ // OPCODE: DW_OP_plus_uconst
+ // OPERANDS: none
+ // DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128
+ // constant operand and pushes the result.
+ case DW_OP_plus_uconst:
+ if (stack.empty()) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 1 item for DW_OP_plus_uconst.");
+ return false;
+ } else {
+ const uint64_t uconst_value = opcodes.GetULEB128(&offset);
+ // Implicit conversion from a UINT to a Scalar...
+ stack.back().GetScalar() += uconst_value;
+ if (!stack.back().GetScalar().IsValid()) {
+ if (error_ptr)
+ error_ptr->SetErrorString("DW_OP_plus_uconst failed.");
+ return false;
+ }
+ }
+ break;
+
+ // OPCODE: DW_OP_shl
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack entries, shifts the former
+ // second entry left by the number of bits specified by the former top of
+ // the stack, and pushes the result.
+ case DW_OP_shl:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_shl.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) <<= tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_shr
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack entries, shifts the former second
+ // entry right logically (filling with zero bits) by the number of bits
+ // specified by the former top of the stack, and pushes the result.
+ case DW_OP_shr:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_shr.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ if (!stack.back().ResolveValue(exe_ctx).ShiftRightLogical(
+ tmp.ResolveValue(exe_ctx))) {
+ if (error_ptr)
+ error_ptr->SetErrorString("DW_OP_shr failed.");
+ return false;
+ }
+ }
+ break;
+
+ // OPCODE: DW_OP_shra
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack entries, shifts the former second
+ // entry right arithmetically (divide the magnitude by 2, keep the same
+ // sign for the result) by the number of bits specified by the former top
+ // of the stack, and pushes the result.
+ case DW_OP_shra:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_shra.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) >>= tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_xor
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack entries, performs the bitwise
+ // exclusive-or operation on the two, and pushes the result.
+ case DW_OP_xor:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_xor.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) ^ tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_skip
+ // OPERANDS: int16_t
+ // DESCRIPTION: An unconditional branch. Its single operand is a 2-byte
+ // signed integer constant. The 2-byte constant is the number of bytes of
+ // the DWARF expression to skip forward or backward from the current
+ // operation, beginning after the 2-byte constant.
+ case DW_OP_skip: {
+ int16_t skip_offset = (int16_t)opcodes.GetU16(&offset);
+ lldb::offset_t new_offset = offset + skip_offset;
+ if (new_offset >= opcodes_offset && new_offset < end_offset)
+ offset = new_offset;
+ else {
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip.");
+ return false;
+ }
+ } break;
+
+ // OPCODE: DW_OP_bra
+ // OPERANDS: int16_t
+ // DESCRIPTION: A conditional branch. Its single operand is a 2-byte
+ // signed integer constant. This operation pops the top of stack. If the
+ // value popped is not the constant 0, the 2-byte constant operand is the
+ // number of bytes of the DWARF expression to skip forward or backward from
+ // the current operation, beginning after the 2-byte constant.
+ case DW_OP_bra:
+ if (stack.empty()) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 1 item for DW_OP_bra.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ int16_t bra_offset = (int16_t)opcodes.GetU16(&offset);
+ Scalar zero(0);
+ if (tmp.ResolveValue(exe_ctx) != zero) {
+ lldb::offset_t new_offset = offset + bra_offset;
+ if (new_offset >= opcodes_offset && new_offset < end_offset)
+ offset = new_offset;
+ else {
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra.");
+ return false;
+ }
+ }
+ }
+ break;
+
+ // OPCODE: DW_OP_eq
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack values, compares using the
+ // equals (==) operator.
+ // STACK RESULT: push the constant value 1 onto the stack if the result
+ // of the operation is true or the constant value 0 if the result of the
+ // operation is false.
+ case DW_OP_eq:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_eq.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) == tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_ge
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack values, compares using the
+ // greater than or equal to (>=) operator.
+ // STACK RESULT: push the constant value 1 onto the stack if the result
+ // of the operation is true or the constant value 0 if the result of the
+ // operation is false.
+ case DW_OP_ge:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_ge.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) >= tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_gt
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack values, compares using the
+ // greater than (>) operator.
+ // STACK RESULT: push the constant value 1 onto the stack if the result
+ // of the operation is true or the constant value 0 if the result of the
+ // operation is false.
+ case DW_OP_gt:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_gt.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) > tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_le
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack values, compares using the
+ // less than or equal to (<=) operator.
+ // STACK RESULT: push the constant value 1 onto the stack if the result
+ // of the operation is true or the constant value 0 if the result of the
+ // operation is false.
+ case DW_OP_le:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_le.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) <= tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_lt
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack values, compares using the
+ // less than (<) operator.
+ // STACK RESULT: push the constant value 1 onto the stack if the result
+ // of the operation is true or the constant value 0 if the result of the
+ // operation is false.
+ case DW_OP_lt:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_lt.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) < tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_ne
+ // OPERANDS: none
+ // DESCRIPTION: pops the top two stack values, compares using the
+ // not equal (!=) operator.
+ // STACK RESULT: push the constant value 1 onto the stack if the result
+ // of the operation is true or the constant value 0 if the result of the
+ // operation is false.
+ case DW_OP_ne:
+ if (stack.size() < 2) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 2 items for DW_OP_ne.");
+ return false;
+ } else {
+ tmp = stack.back();
+ stack.pop_back();
+ stack.back().ResolveValue(exe_ctx) =
+ stack.back().ResolveValue(exe_ctx) != tmp.ResolveValue(exe_ctx);
+ }
+ break;
+
+ // OPCODE: DW_OP_litn
+ // OPERANDS: none
+ // DESCRIPTION: encode the unsigned literal values from 0 through 31.
+ // STACK RESULT: push the unsigned literal constant value onto the top
+ // of the stack.
+ case DW_OP_lit0:
+ case DW_OP_lit1:
+ case DW_OP_lit2:
+ case DW_OP_lit3:
+ case DW_OP_lit4:
+ case DW_OP_lit5:
+ case DW_OP_lit6:
+ case DW_OP_lit7:
+ case DW_OP_lit8:
+ case DW_OP_lit9:
+ case DW_OP_lit10:
+ case DW_OP_lit11:
+ case DW_OP_lit12:
+ case DW_OP_lit13:
+ case DW_OP_lit14:
+ case DW_OP_lit15:
+ case DW_OP_lit16:
+ case DW_OP_lit17:
+ case DW_OP_lit18:
+ case DW_OP_lit19:
+ case DW_OP_lit20:
+ case DW_OP_lit21:
+ case DW_OP_lit22:
+ case DW_OP_lit23:
+ case DW_OP_lit24:
+ case DW_OP_lit25:
+ case DW_OP_lit26:
+ case DW_OP_lit27:
+ case DW_OP_lit28:
+ case DW_OP_lit29:
+ case DW_OP_lit30:
+ case DW_OP_lit31:
+ stack.push_back(Scalar((uint64_t)(op - DW_OP_lit0)));
+ break;
+
+ // OPCODE: DW_OP_regN
+ // OPERANDS: none
+ // DESCRIPTION: Push the value in register n on the top of the stack.
+ case DW_OP_reg0:
+ case DW_OP_reg1:
+ case DW_OP_reg2:
+ case DW_OP_reg3:
+ case DW_OP_reg4:
+ case DW_OP_reg5:
+ case DW_OP_reg6:
+ case DW_OP_reg7:
+ case DW_OP_reg8:
+ case DW_OP_reg9:
+ case DW_OP_reg10:
+ case DW_OP_reg11:
+ case DW_OP_reg12:
+ case DW_OP_reg13:
+ case DW_OP_reg14:
+ case DW_OP_reg15:
+ case DW_OP_reg16:
+ case DW_OP_reg17:
+ case DW_OP_reg18:
+ case DW_OP_reg19:
+ case DW_OP_reg20:
+ case DW_OP_reg21:
+ case DW_OP_reg22:
+ case DW_OP_reg23:
+ case DW_OP_reg24:
+ case DW_OP_reg25:
+ case DW_OP_reg26:
+ case DW_OP_reg27:
+ case DW_OP_reg28:
+ case DW_OP_reg29:
+ case DW_OP_reg30:
+ case DW_OP_reg31: {
+ reg_num = op - DW_OP_reg0;
+
+ if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
+ stack.push_back(tmp);
+ else
+ return false;
+ } break;
+ // OPCODE: DW_OP_regx
+ // OPERANDS:
+ // ULEB128 literal operand that encodes the register.
+ // DESCRIPTION: Push the value in register on the top of the stack.
+ case DW_OP_regx: {
+ reg_num = opcodes.GetULEB128(&offset);
+ if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
+ stack.push_back(tmp);
+ else
+ return false;
+ } break;
+
+ // OPCODE: DW_OP_bregN
+ // OPERANDS:
+ // SLEB128 offset from register N
+ // DESCRIPTION: Value is in memory at the address specified by register
+ // N plus an offset.
+ case DW_OP_breg0:
+ case DW_OP_breg1:
+ case DW_OP_breg2:
+ case DW_OP_breg3:
+ case DW_OP_breg4:
+ case DW_OP_breg5:
+ case DW_OP_breg6:
+ case DW_OP_breg7:
+ case DW_OP_breg8:
+ case DW_OP_breg9:
+ case DW_OP_breg10:
+ case DW_OP_breg11:
+ case DW_OP_breg12:
+ case DW_OP_breg13:
+ case DW_OP_breg14:
+ case DW_OP_breg15:
+ case DW_OP_breg16:
+ case DW_OP_breg17:
+ case DW_OP_breg18:
+ case DW_OP_breg19:
+ case DW_OP_breg20:
+ case DW_OP_breg21:
+ case DW_OP_breg22:
+ case DW_OP_breg23:
+ case DW_OP_breg24:
+ case DW_OP_breg25:
+ case DW_OP_breg26:
+ case DW_OP_breg27:
+ case DW_OP_breg28:
+ case DW_OP_breg29:
+ case DW_OP_breg30:
+ case DW_OP_breg31: {
+ reg_num = op - DW_OP_breg0;
+
+ if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr,
+ tmp)) {
+ int64_t breg_offset = opcodes.GetSLEB128(&offset);
+ tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset;
+ tmp.ClearContext();
+ stack.push_back(tmp);
+ stack.back().SetValueType(Value::eValueTypeLoadAddress);
+ } else
+ return false;
+ } break;
+ // OPCODE: DW_OP_bregx
+ // OPERANDS: 2
+ // ULEB128 literal operand that encodes the register.
+ // SLEB128 offset from register N
+ // DESCRIPTION: Value is in memory at the address specified by register
+ // N plus an offset.
+ case DW_OP_bregx: {
+ reg_num = opcodes.GetULEB128(&offset);
+
+ if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr,
+ tmp)) {
+ int64_t breg_offset = opcodes.GetSLEB128(&offset);
+ tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset;
+ tmp.ClearContext();
+ stack.push_back(tmp);
+ stack.back().SetValueType(Value::eValueTypeLoadAddress);
+ } else
+ return false;
+ } break;
+
+ case DW_OP_fbreg:
+ if (exe_ctx) {
+ if (frame) {
+ Scalar value;
+ if (frame->GetFrameBaseValue(value, error_ptr)) {
+ int64_t fbreg_offset = opcodes.GetSLEB128(&offset);
+ value += fbreg_offset;
+ stack.push_back(value);
+ stack.back().SetValueType(Value::eValueTypeLoadAddress);
+ } else
+ return false;
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Invalid stack frame in context for DW_OP_fbreg opcode.");
+ return false;
+ }
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "NULL execution context for DW_OP_fbreg.\n");
+ return false;
+ }
+
+ break;
+
+ // OPCODE: DW_OP_nop
+ // OPERANDS: none
+ // DESCRIPTION: A place holder. It has no effect on the location stack
+ // or any of its values.
+ case DW_OP_nop:
+ break;
+
+ // OPCODE: DW_OP_piece
+ // OPERANDS: 1
+ // ULEB128: byte size of the piece
+ // DESCRIPTION: The operand describes the size in bytes of the piece of
+ // the object referenced by the DWARF expression whose result is at the top
+ // of the stack. If the piece is located in a register, but does not occupy
+ // the entire register, the placement of the piece within that register is
+ // defined by the ABI.
+ //
+ // Many compilers store a single variable in sets of registers, or store a
+ // variable partially in memory and partially in registers. DW_OP_piece
+ // provides a way of describing how large a part of a variable a particular
+ // DWARF expression refers to.
+ case DW_OP_piece: {
+ const uint64_t piece_byte_size = opcodes.GetULEB128(&offset);
+
+ if (piece_byte_size > 0) {
+ Value curr_piece;
+
+ if (stack.empty()) {
+ // In a multi-piece expression, this means that the current piece is
+ // not available. Fill with zeros for now by resizing the data and
+ // appending it
+ curr_piece.ResizeData(piece_byte_size);
+ ::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size);
+ pieces.AppendDataToHostBuffer(curr_piece);
+ } else {
+ Status error;
+ // Extract the current piece into "curr_piece"
+ Value curr_piece_source_value(stack.back());
+ stack.pop_back();
+
+ const Value::ValueType curr_piece_source_value_type =
+ curr_piece_source_value.GetValueType();
+ switch (curr_piece_source_value_type) {
+ case Value::eValueTypeLoadAddress:
+ if (process) {
+ if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) {
+ lldb::addr_t load_addr =
+ curr_piece_source_value.GetScalar().ULongLong(
+ LLDB_INVALID_ADDRESS);
+ if (process->ReadMemory(
+ load_addr, curr_piece.GetBuffer().GetBytes(),
+ piece_byte_size, error) != piece_byte_size) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "failed to read memory DW_OP_piece(%" PRIu64
+ ") from 0x%" PRIx64,
+ piece_byte_size, load_addr);
+ return false;
+ }
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "failed to resize the piece memory buffer for "
+ "DW_OP_piece(%" PRIu64 ")",
+ piece_byte_size);
+ return false;
+ }
+ }
+ break;
+
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeHostAddress:
+ if (error_ptr) {
+ lldb::addr_t addr = curr_piece_source_value.GetScalar().ULongLong(
+ LLDB_INVALID_ADDRESS);
+ error_ptr->SetErrorStringWithFormat(
+ "failed to read memory DW_OP_piece(%" PRIu64
+ ") from %s address 0x%" PRIx64,
+ piece_byte_size, curr_piece_source_value.GetValueType() ==
+ Value::eValueTypeFileAddress
+ ? "file"
+ : "host",
+ addr);
+ }
+ return false;
+
+ case Value::eValueTypeScalar: {
+ uint32_t bit_size = piece_byte_size * 8;
+ uint32_t bit_offset = 0;
+ if (!curr_piece_source_value.GetScalar().ExtractBitfield(
+ bit_size, bit_offset)) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "unable to extract %" PRIu64 " bytes from a %" PRIu64
+ " byte scalar value.",
+ piece_byte_size,
+ (uint64_t)curr_piece_source_value.GetScalar()
+ .GetByteSize());
+ return false;
+ }
+ curr_piece = curr_piece_source_value;
+ } break;
+
+ case Value::eValueTypeVector: {
+ if (curr_piece_source_value.GetVector().length >= piece_byte_size)
+ curr_piece_source_value.GetVector().length = piece_byte_size;
+ else {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "unable to extract %" PRIu64 " bytes from a %" PRIu64
+ " byte vector value.",
+ piece_byte_size,
+ (uint64_t)curr_piece_source_value.GetVector().length);
+ return false;
+ }
+ } break;
+ }
+
+ // Check if this is the first piece?
+ if (op_piece_offset == 0) {
+ // This is the first piece, we should push it back onto the stack
+ // so subsequent pieces will be able to access this piece and add
+ // to it
+ if (pieces.AppendDataToHostBuffer(curr_piece) == 0) {
+ if (error_ptr)
+ error_ptr->SetErrorString("failed to append piece data");
+ return false;
+ }
+ } else {
+ // If this is the second or later piece there should be a value on
+ // the stack
+ if (pieces.GetBuffer().GetByteSize() != op_piece_offset) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "DW_OP_piece for offset %" PRIu64
+ " but top of stack is of size %" PRIu64,
+ op_piece_offset, pieces.GetBuffer().GetByteSize());
+ return false;
+ }
+
+ if (pieces.AppendDataToHostBuffer(curr_piece) == 0) {
+ if (error_ptr)
+ error_ptr->SetErrorString("failed to append piece data");
+ return false;
+ }
+ }
+ op_piece_offset += piece_byte_size;
+ }
+ }
+ } break;
+
+ case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3);
+ if (stack.size() < 1) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "Expression stack needs at least 1 item for DW_OP_bit_piece.");
+ return false;
+ } else {
+ const uint64_t piece_bit_size = opcodes.GetULEB128(&offset);
+ const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset);
+ switch (stack.back().GetValueType()) {
+ case Value::eValueTypeScalar: {
+ if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size,
+ piece_bit_offset)) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "unable to extract %" PRIu64 " bit value with %" PRIu64
+ " bit offset from a %" PRIu64 " bit scalar value.",
+ piece_bit_size, piece_bit_offset,
+ (uint64_t)(stack.back().GetScalar().GetByteSize() * 8));
+ return false;
+ }
+ } break;
+
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeHostAddress:
+ if (error_ptr) {
+ error_ptr->SetErrorStringWithFormat(
+ "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64
+ ", bit_offset = %" PRIu64 ") from an address value.",
+ piece_bit_size, piece_bit_offset);
+ }
+ return false;
+
+ case Value::eValueTypeVector:
+ if (error_ptr) {
+ error_ptr->SetErrorStringWithFormat(
+ "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64
+ ", bit_offset = %" PRIu64 ") from a vector value.",
+ piece_bit_size, piece_bit_offset);
+ }
+ return false;
+ }
+ }
+ break;
+
+ // OPCODE: DW_OP_push_object_address
+ // OPERANDS: none
+ // DESCRIPTION: Pushes the address of the object currently being
+ // evaluated as part of evaluation of a user presented expression. This
+ // object may correspond to an independent variable described by its own
+ // DIE or it may be a component of an array, structure, or class whose
+ // address has been dynamically determined by an earlier step during user
+ // expression evaluation.
+ case DW_OP_push_object_address:
+ if (object_address_ptr)
+ stack.push_back(*object_address_ptr);
+ else {
+ if (error_ptr)
+ error_ptr->SetErrorString("DW_OP_push_object_address used without "
+ "specifying an object address");
+ return false;
+ }
+ break;
+
+ // OPCODE: DW_OP_call2
+ // OPERANDS:
+ // uint16_t compile unit relative offset of a DIE
+ // DESCRIPTION: Performs subroutine calls during evaluation
+ // of a DWARF expression. The operand is the 2-byte unsigned offset of a
+ // debugging information entry in the current compilation unit.
+ //
+ // Operand interpretation is exactly like that for DW_FORM_ref2.
+ //
+ // This operation transfers control of DWARF expression evaluation to the
+ // DW_AT_location attribute of the referenced DIE. If there is no such
+ // attribute, then there is no effect. Execution of the DWARF expression of
+ // a DW_AT_location attribute may add to and/or remove from values on the
+ // stack. Execution returns to the point following the call when the end of
+ // the attribute is reached. Values on the stack at the time of the call
+ // may be used as parameters by the called expression and values left on
+ // the stack by the called expression may be used as return values by prior
+ // agreement between the calling and called expressions.
+ case DW_OP_call2:
+ if (error_ptr)
+ error_ptr->SetErrorString("Unimplemented opcode DW_OP_call2.");
+ return false;
+ // OPCODE: DW_OP_call4
+ // OPERANDS: 1
+ // uint32_t compile unit relative offset of a DIE
+ // DESCRIPTION: Performs a subroutine call during evaluation of a DWARF
+ // expression. For DW_OP_call4, the operand is a 4-byte unsigned offset of
+ // a debugging information entry in the current compilation unit.
+ //
+ // Operand interpretation DW_OP_call4 is exactly like that for
+ // DW_FORM_ref4.
+ //
+ // This operation transfers control of DWARF expression evaluation to the
+ // DW_AT_location attribute of the referenced DIE. If there is no such
+ // attribute, then there is no effect. Execution of the DWARF expression of
+ // a DW_AT_location attribute may add to and/or remove from values on the
+ // stack. Execution returns to the point following the call when the end of
+ // the attribute is reached. Values on the stack at the time of the call
+ // may be used as parameters by the called expression and values left on
+ // the stack by the called expression may be used as return values by prior
+ // agreement between the calling and called expressions.
+ case DW_OP_call4:
+ if (error_ptr)
+ error_ptr->SetErrorString("Unimplemented opcode DW_OP_call4.");
+ return false;
+
+ // OPCODE: DW_OP_stack_value
+ // OPERANDS: None
+ // DESCRIPTION: Specifies that the object does not exist in memory but
+ // rather is a constant value. The value from the top of the stack is the
+ // value to be used. This is the actual object value and not the location.
+ case DW_OP_stack_value:
+ stack.back().SetValueType(Value::eValueTypeScalar);
+ break;
+
+ // OPCODE: DW_OP_call_frame_cfa
+ // OPERANDS: None
+ // DESCRIPTION: Specifies a DWARF expression that pushes the value of
+ // the canonical frame address consistent with the call frame information
+ // located in .debug_frame (or in the FDEs of the eh_frame section).
+ case DW_OP_call_frame_cfa:
+ if (frame) {
+ // Note that we don't have to parse FDEs because this DWARF expression
+ // is commonly evaluated with a valid stack frame.
+ StackID id = frame->GetStackID();
+ addr_t cfa = id.GetCallFrameAddress();
+ if (cfa != LLDB_INVALID_ADDRESS) {
+ stack.push_back(Scalar(cfa));
+ stack.back().SetValueType(Value::eValueTypeLoadAddress);
+ } else if (error_ptr)
+ error_ptr->SetErrorString("Stack frame does not include a canonical "
+ "frame address for DW_OP_call_frame_cfa "
+ "opcode.");
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid stack frame in context for "
+ "DW_OP_call_frame_cfa opcode.");
+ return false;
+ }
+ break;
+
+ // OPCODE: DW_OP_form_tls_address (or the old pre-DWARFv3 vendor extension
+ // opcode, DW_OP_GNU_push_tls_address)
+ // OPERANDS: none
+ // DESCRIPTION: Pops a TLS offset from the stack, converts it to
+ // an address in the current thread's thread-local storage block, and
+ // pushes it on the stack.
+ case DW_OP_form_tls_address:
+ case DW_OP_GNU_push_tls_address: {
+ if (stack.size() < 1) {
+ if (error_ptr) {
+ if (op == DW_OP_form_tls_address)
+ error_ptr->SetErrorString(
+ "DW_OP_form_tls_address needs an argument.");
+ else
+ error_ptr->SetErrorString(
+ "DW_OP_GNU_push_tls_address needs an argument.");
+ }
+ return false;
+ }
+
+ if (!exe_ctx || !module_sp) {
+ if (error_ptr)
+ error_ptr->SetErrorString("No context to evaluate TLS within.");
+ return false;
+ }
+
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (!thread) {
+ if (error_ptr)
+ error_ptr->SetErrorString("No thread to evaluate TLS within.");
+ return false;
+ }
+
+ // Lookup the TLS block address for this thread and module.
+ const addr_t tls_file_addr =
+ stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ const addr_t tls_load_addr =
+ thread->GetThreadLocalData(module_sp, tls_file_addr);
+
+ if (tls_load_addr == LLDB_INVALID_ADDRESS) {
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "No TLS data currently exists for this thread.");
+ return false;
+ }
+
+ stack.back().GetScalar() = tls_load_addr;
+ stack.back().SetValueType(Value::eValueTypeLoadAddress);
+ } break;
+
+ // OPCODE: DW_OP_addrx (DW_OP_GNU_addr_index is the legacy name.)
+ // OPERANDS: 1
+ // ULEB128: index to the .debug_addr section
+ // DESCRIPTION: Pushes an address to the stack from the .debug_addr
+ // section with the base address specified by the DW_AT_addr_base attribute
+ // and the 0 based index is the ULEB128 encoded index.
+ case DW_OP_addrx:
+ case DW_OP_GNU_addr_index: {
+ if (!dwarf_cu) {
+ if (error_ptr)
+ error_ptr->SetErrorString("DW_OP_GNU_addr_index found without a "
+ "compile unit being specified");
+ return false;
+ }
+ uint64_t index = opcodes.GetULEB128(&offset);
+ lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index);
+ stack.push_back(Scalar(value));
+ stack.back().SetValueType(Value::eValueTypeFileAddress);
+ } break;
+
+ // OPCODE: DW_OP_GNU_const_index
+ // OPERANDS: 1
+ // ULEB128: index to the .debug_addr section
+ // DESCRIPTION: Pushes an constant with the size of a machine address to
+ // the stack from the .debug_addr section with the base address specified
+ // by the DW_AT_addr_base attribute and the 0 based index is the ULEB128
+ // encoded index.
+ case DW_OP_GNU_const_index: {
+ if (!dwarf_cu) {
+ if (error_ptr)
+ error_ptr->SetErrorString("DW_OP_GNU_const_index found without a "
+ "compile unit being specified");
+ return false;
+ }
+ uint64_t index = opcodes.GetULEB128(&offset);
+ lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index);
+ stack.push_back(Scalar(value));
+ } break;
+
+ default:
+ if (log)
+ log->Printf("Unhandled opcode %s in DWARFExpression.",
+ DW_OP_value_to_name(op));
+ break;
+ }
+ }
+
+ if (stack.empty()) {
+ // Nothing on the stack, check if we created a piece value from DW_OP_piece
+ // or DW_OP_bit_piece opcodes
+ if (pieces.GetBuffer().GetByteSize()) {
+ result = pieces;
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorString("Stack empty after evaluation.");
+ return false;
+ }
+ } else {
+ if (log && log->GetVerbose()) {
+ size_t count = stack.size();
+ log->Printf("Stack after operation has %" PRIu64 " values:",
+ (uint64_t)count);
+ for (size_t i = 0; i < count; ++i) {
+ StreamString new_value;
+ new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
+ stack[i].Dump(&new_value);
+ log->Printf(" %s", new_value.GetData());
+ }
+ }
+ result = stack.back();
+ }
+ return true; // Return true on success
+}
+
+size_t DWARFExpression::LocationListSize(const DWARFUnit *dwarf_cu,
+ const DataExtractor &debug_loc_data,
+ lldb::offset_t offset) {
+ const lldb::offset_t debug_loc_offset = offset;
+ while (debug_loc_data.ValidOffset(offset)) {
+ lldb::addr_t start_addr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t end_addr = LLDB_INVALID_ADDRESS;
+ if (!AddressRangeForLocationListEntry(dwarf_cu, debug_loc_data, &offset,
+ start_addr, end_addr))
+ break;
+
+ if (start_addr == 0 && end_addr == 0)
+ break;
+
+ uint16_t loc_length = debug_loc_data.GetU16(&offset);
+ offset += loc_length;
+ }
+
+ if (offset > debug_loc_offset)
+ return offset - debug_loc_offset;
+ return 0;
+}
+
+bool DWARFExpression::AddressRangeForLocationListEntry(
+ const DWARFUnit *dwarf_cu, const DataExtractor &debug_loc_data,
+ lldb::offset_t *offset_ptr, lldb::addr_t &low_pc, lldb::addr_t &high_pc) {
+ if (!debug_loc_data.ValidOffset(*offset_ptr))
+ return false;
+
+ DWARFExpression::LocationListFormat format =
+ dwarf_cu->GetSymbolFileDWARF().GetLocationListFormat();
+ switch (format) {
+ case NonLocationList:
+ return false;
+ case RegularLocationList:
+ low_pc = debug_loc_data.GetAddress(offset_ptr);
+ high_pc = debug_loc_data.GetAddress(offset_ptr);
+ return true;
+ case SplitDwarfLocationList:
+ case LocLists:
+ switch (debug_loc_data.GetU8(offset_ptr)) {
+ case DW_LLE_end_of_list:
+ return false;
+ case DW_LLE_startx_endx: {
+ uint64_t index = debug_loc_data.GetULEB128(offset_ptr);
+ low_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index);
+ index = debug_loc_data.GetULEB128(offset_ptr);
+ high_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index);
+ return true;
+ }
+ case DW_LLE_startx_length: {
+ uint64_t index = debug_loc_data.GetULEB128(offset_ptr);
+ low_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index);
+ uint64_t length = (format == LocLists)
+ ? debug_loc_data.GetULEB128(offset_ptr)
+ : debug_loc_data.GetU32(offset_ptr);
+ high_pc = low_pc + length;
+ return true;
+ }
+ case DW_LLE_start_length: {
+ low_pc = debug_loc_data.GetAddress(offset_ptr);
+ high_pc = low_pc + debug_loc_data.GetULEB128(offset_ptr);
+ return true;
+ }
+ case DW_LLE_start_end: {
+ low_pc = debug_loc_data.GetAddress(offset_ptr);
+ high_pc = debug_loc_data.GetAddress(offset_ptr);
+ return true;
+ }
+ default:
+ // Not supported entry type
+ lldbassert(false && "Not supported location list type");
+ return false;
+ }
+ }
+ assert(false && "Not supported location list type");
+ return false;
+}
+
+static bool print_dwarf_exp_op(Stream &s, const DataExtractor &data,
+ lldb::offset_t *offset_ptr, int address_size,
+ int dwarf_ref_size) {
+ uint8_t opcode = data.GetU8(offset_ptr);
+ DRC_class opcode_class;
+ uint64_t uint;
+ int64_t sint;
+
+ int size;
+
+ opcode_class = DW_OP_value_to_class(opcode) & (~DRC_DWARFv3);
+
+ s.Printf("%s ", DW_OP_value_to_name(opcode));
+
+ /* Does this take zero parameters? If so we can shortcut this function. */
+ if (opcode_class == DRC_ZEROOPERANDS)
+ return true;
+
+ if (opcode_class == DRC_TWOOPERANDS && opcode == DW_OP_bregx) {
+ uint = data.GetULEB128(offset_ptr);
+ sint = data.GetSLEB128(offset_ptr);
+ s.Printf("%" PRIu64 " %" PRIi64, uint, sint);
+ return true;
+ }
+ if (opcode_class != DRC_ONEOPERAND) {
+ s.Printf("UNKNOWN OP %u", opcode);
+ return false;
+ }
+
+ switch (opcode) {
+ case DW_OP_addr:
+ size = address_size;
+ break;
+ case DW_OP_const1u:
+ size = 1;
+ break;
+ case DW_OP_const1s:
+ size = -1;
+ break;
+ case DW_OP_const2u:
+ size = 2;
+ break;
+ case DW_OP_const2s:
+ size = -2;
+ break;
+ case DW_OP_const4u:
+ size = 4;
+ break;
+ case DW_OP_const4s:
+ size = -4;
+ break;
+ case DW_OP_const8u:
+ size = 8;
+ break;
+ case DW_OP_const8s:
+ size = -8;
+ break;
+ case DW_OP_constu:
+ size = 128;
+ break;
+ case DW_OP_consts:
+ size = -128;
+ break;
+ case DW_OP_fbreg:
+ size = -128;
+ break;
+ case DW_OP_breg0:
+ case DW_OP_breg1:
+ case DW_OP_breg2:
+ case DW_OP_breg3:
+ case DW_OP_breg4:
+ case DW_OP_breg5:
+ case DW_OP_breg6:
+ case DW_OP_breg7:
+ case DW_OP_breg8:
+ case DW_OP_breg9:
+ case DW_OP_breg10:
+ case DW_OP_breg11:
+ case DW_OP_breg12:
+ case DW_OP_breg13:
+ case DW_OP_breg14:
+ case DW_OP_breg15:
+ case DW_OP_breg16:
+ case DW_OP_breg17:
+ case DW_OP_breg18:
+ case DW_OP_breg19:
+ case DW_OP_breg20:
+ case DW_OP_breg21:
+ case DW_OP_breg22:
+ case DW_OP_breg23:
+ case DW_OP_breg24:
+ case DW_OP_breg25:
+ case DW_OP_breg26:
+ case DW_OP_breg27:
+ case DW_OP_breg28:
+ case DW_OP_breg29:
+ case DW_OP_breg30:
+ case DW_OP_breg31:
+ size = -128;
+ break;
+ case DW_OP_pick:
+ case DW_OP_deref_size:
+ case DW_OP_xderef_size:
+ size = 1;
+ break;
+ case DW_OP_skip:
+ case DW_OP_bra:
+ size = -2;
+ break;
+ case DW_OP_call2:
+ size = 2;
+ break;
+ case DW_OP_call4:
+ size = 4;
+ break;
+ case DW_OP_call_ref:
+ size = dwarf_ref_size;
+ break;
+ case DW_OP_addrx:
+ case DW_OP_piece:
+ case DW_OP_plus_uconst:
+ case DW_OP_regx:
+ case DW_OP_GNU_addr_index:
+ case DW_OP_GNU_const_index:
+ size = 128;
+ break;
+ default:
+ s.Printf("UNKNOWN ONE-OPERAND OPCODE, #%u", opcode);
+ return false;
+ }
+
+ switch (size) {
+ case -1:
+ sint = (int8_t)data.GetU8(offset_ptr);
+ s.Printf("%+" PRIi64, sint);
+ break;
+ case -2:
+ sint = (int16_t)data.GetU16(offset_ptr);
+ s.Printf("%+" PRIi64, sint);
+ break;
+ case -4:
+ sint = (int32_t)data.GetU32(offset_ptr);
+ s.Printf("%+" PRIi64, sint);
+ break;
+ case -8:
+ sint = (int64_t)data.GetU64(offset_ptr);
+ s.Printf("%+" PRIi64, sint);
+ break;
+ case -128:
+ sint = data.GetSLEB128(offset_ptr);
+ s.Printf("%+" PRIi64, sint);
+ break;
+ case 1:
+ uint = data.GetU8(offset_ptr);
+ s.Printf("0x%2.2" PRIx64, uint);
+ break;
+ case 2:
+ uint = data.GetU16(offset_ptr);
+ s.Printf("0x%4.4" PRIx64, uint);
+ break;
+ case 4:
+ uint = data.GetU32(offset_ptr);
+ s.Printf("0x%8.8" PRIx64, uint);
+ break;
+ case 8:
+ uint = data.GetU64(offset_ptr);
+ s.Printf("0x%16.16" PRIx64, uint);
+ break;
+ case 128:
+ uint = data.GetULEB128(offset_ptr);
+ s.Printf("0x%" PRIx64, uint);
+ break;
+ }
+
+ return true;
+}
+
+bool DWARFExpression::PrintDWARFExpression(Stream &s, const DataExtractor &data,
+ int address_size, int dwarf_ref_size,
+ bool location_expression) {
+ int op_count = 0;
+ lldb::offset_t offset = 0;
+ while (data.ValidOffset(offset)) {
+ if (location_expression && op_count > 0)
+ return false;
+ if (op_count > 0)
+ s.PutCString(", ");
+ if (!print_dwarf_exp_op(s, data, &offset, address_size, dwarf_ref_size))
+ return false;
+ op_count++;
+ }
+
+ return true;
+}
+
+void DWARFExpression::PrintDWARFLocationList(
+ Stream &s, const DWARFUnit *cu, const DataExtractor &debug_loc_data,
+ lldb::offset_t offset) {
+ uint64_t start_addr, end_addr;
+ uint32_t addr_size = DWARFUnit::GetAddressByteSize(cu);
+ s.SetAddressByteSize(DWARFUnit::GetAddressByteSize(cu));
+ dw_addr_t base_addr = cu ? cu->GetBaseAddress() : 0;
+ while (debug_loc_data.ValidOffset(offset)) {
+ start_addr = debug_loc_data.GetMaxU64(&offset, addr_size);
+ end_addr = debug_loc_data.GetMaxU64(&offset, addr_size);
+
+ if (start_addr == 0 && end_addr == 0)
+ break;
+
+ s.PutCString("\n ");
+ s.Indent();
+ if (cu)
+ s.AddressRange(start_addr + base_addr, end_addr + base_addr,
+ cu->GetAddressByteSize(), nullptr, ": ");
+ uint32_t loc_length = debug_loc_data.GetU16(&offset);
+
+ DataExtractor locationData(debug_loc_data, offset, loc_length);
+ PrintDWARFExpression(s, locationData, addr_size, 4, false);
+ offset += loc_length;
+ }
+}
+
+bool DWARFExpression::GetOpAndEndOffsets(StackFrame &frame,
+ lldb::offset_t &op_offset,
+ lldb::offset_t &end_offset) {
+ SymbolContext sc = frame.GetSymbolContext(eSymbolContextFunction);
+ if (!sc.function) {
+ return false;
+ }
+
+ addr_t loclist_base_file_addr =
+ sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
+ if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) {
+ return false;
+ }
+
+ addr_t pc_file_addr = frame.GetFrameCodeAddress().GetFileAddress();
+ lldb::offset_t opcodes_offset, opcodes_length;
+ if (!GetLocation(loclist_base_file_addr, pc_file_addr, opcodes_offset,
+ opcodes_length)) {
+ return false;
+ }
+
+ if (opcodes_length == 0) {
+ return false;
+ }
+
+ op_offset = opcodes_offset;
+ end_offset = opcodes_offset + opcodes_length;
+ return true;
+}
+
+bool DWARFExpression::MatchesOperand(StackFrame &frame,
+ const Instruction::Operand &operand) {
+ using namespace OperandMatchers;
+
+ lldb::offset_t op_offset;
+ lldb::offset_t end_offset;
+ if (!GetOpAndEndOffsets(frame, op_offset, end_offset)) {
+ return false;
+ }
+
+ if (!m_data.ValidOffset(op_offset) || op_offset >= end_offset) {
+ return false;
+ }
+
+ RegisterContextSP reg_ctx_sp = frame.GetRegisterContext();
+ if (!reg_ctx_sp) {
+ return false;
+ }
+
+ DataExtractor opcodes = m_data;
+ uint8_t opcode = opcodes.GetU8(&op_offset);
+
+ if (opcode == DW_OP_fbreg) {
+ int64_t offset = opcodes.GetSLEB128(&op_offset);
+
+ DWARFExpression *fb_expr = frame.GetFrameBaseExpression(nullptr);
+ if (!fb_expr) {
+ return false;
+ }
+
+ auto recurse = [&frame, fb_expr](const Instruction::Operand &child) {
+ return fb_expr->MatchesOperand(frame, child);
+ };
+
+ if (!offset &&
+ MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference),
+ recurse)(operand)) {
+ return true;
+ }
+
+ return MatchUnaryOp(
+ MatchOpType(Instruction::Operand::Type::Dereference),
+ MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum),
+ MatchImmOp(offset), recurse))(operand);
+ }
+
+ bool dereference = false;
+ const RegisterInfo *reg = nullptr;
+ int64_t offset = 0;
+
+ if (opcode >= DW_OP_reg0 && opcode <= DW_OP_reg31) {
+ reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_reg0);
+ } else if (opcode >= DW_OP_breg0 && opcode <= DW_OP_breg31) {
+ offset = opcodes.GetSLEB128(&op_offset);
+ reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_breg0);
+ } else if (opcode == DW_OP_regx) {
+ uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset));
+ reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num);
+ } else if (opcode == DW_OP_bregx) {
+ uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset));
+ offset = opcodes.GetSLEB128(&op_offset);
+ reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num);
+ } else {
+ return false;
+ }
+
+ if (!reg) {
+ return false;
+ }
+
+ if (dereference) {
+ if (!offset &&
+ MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference),
+ MatchRegOp(*reg))(operand)) {
+ return true;
+ }
+
+ return MatchUnaryOp(
+ MatchOpType(Instruction::Operand::Type::Dereference),
+ MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum),
+ MatchRegOp(*reg),
+ MatchImmOp(offset)))(operand);
+ } else {
+ return MatchRegOp(*reg)(operand);
+ }
+}
+
diff --git a/contrib/llvm-project/lldb/source/Expression/DiagnosticManager.cpp b/contrib/llvm-project/lldb/source/Expression/DiagnosticManager.cpp
new file mode 100644
index 000000000000..53d85f8a6d54
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/DiagnosticManager.cpp
@@ -0,0 +1,88 @@
+//===-- DiagnosticManager.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Expression/DiagnosticManager.h"
+
+#include "llvm/Support/ErrorHandling.h"
+
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+
+void DiagnosticManager::Dump(Log *log) {
+ if (!log)
+ return;
+
+ std::string str = GetString();
+
+ // GetString() puts a separator after each diagnostic. We want to remove the
+ // last '\n' because log->PutCString will add one for us.
+
+ if (str.size() && str.back() == '\n') {
+ str.pop_back();
+ }
+
+ log->PutCString(str.c_str());
+}
+
+static const char *StringForSeverity(DiagnosticSeverity severity) {
+ switch (severity) {
+ // this should be exhaustive
+ case lldb_private::eDiagnosticSeverityError:
+ return "error: ";
+ case lldb_private::eDiagnosticSeverityWarning:
+ return "warning: ";
+ case lldb_private::eDiagnosticSeverityRemark:
+ return "";
+ }
+ llvm_unreachable("switch needs another case for DiagnosticSeverity enum");
+}
+
+std::string DiagnosticManager::GetString(char separator) {
+ std::string ret;
+
+ for (const Diagnostic *diagnostic : Diagnostics()) {
+ ret.append(StringForSeverity(diagnostic->GetSeverity()));
+ ret.append(diagnostic->GetMessage());
+ ret.push_back(separator);
+ }
+
+ return ret;
+}
+
+size_t DiagnosticManager::Printf(DiagnosticSeverity severity,
+ const char *format, ...) {
+ StreamString ss;
+
+ va_list args;
+ va_start(args, format);
+ size_t result = ss.PrintfVarArg(format, args);
+ va_end(args);
+
+ AddDiagnostic(ss.GetString(), severity, eDiagnosticOriginLLDB);
+
+ return result;
+}
+
+size_t DiagnosticManager::PutString(DiagnosticSeverity severity,
+ llvm::StringRef str) {
+ if (str.empty())
+ return 0;
+ AddDiagnostic(str, severity, eDiagnosticOriginLLDB);
+ return str.size();
+}
+
+void DiagnosticManager::CopyDiagnostics(DiagnosticManager &otherDiagnostics) {
+ for (const DiagnosticList::value_type &other_diagnostic:
+ otherDiagnostics.Diagnostics()) {
+ AddDiagnostic(
+ other_diagnostic->GetMessage(), other_diagnostic->GetSeverity(),
+ other_diagnostic->getKind(), other_diagnostic->GetCompilerID());
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Expression/Expression.cpp b/contrib/llvm-project/lldb/source/Expression/Expression.cpp
new file mode 100644
index 000000000000..8e1ef6958cc7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/Expression.cpp
@@ -0,0 +1,30 @@
+//===-- Expression.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Expression/Expression.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb_private;
+
+Expression::Expression(Target &target, ExpressionKind kind)
+ : m_kind(kind),
+ m_target_wp(target.shared_from_this()),
+ m_jit_start_addr(LLDB_INVALID_ADDRESS),
+ m_jit_end_addr(LLDB_INVALID_ADDRESS) {
+ // Can't make any kind of expression without a target.
+ assert(m_target_wp.lock());
+}
+
+Expression::Expression(ExecutionContextScope &exe_scope, ExpressionKind kind)
+ : m_kind(kind),
+ m_target_wp(exe_scope.CalculateTarget()),
+ m_jit_start_addr(LLDB_INVALID_ADDRESS),
+ m_jit_end_addr(LLDB_INVALID_ADDRESS) {
+ assert(m_target_wp.lock());
+}
diff --git a/contrib/llvm-project/lldb/source/Expression/ExpressionVariable.cpp b/contrib/llvm-project/lldb/source/Expression/ExpressionVariable.cpp
new file mode 100644
index 000000000000..97305dcf5a02
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/ExpressionVariable.cpp
@@ -0,0 +1,92 @@
+//===-- ExpressionVariable.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb_private;
+
+ExpressionVariable::~ExpressionVariable() {}
+
+uint8_t *ExpressionVariable::GetValueBytes() {
+ const size_t byte_size = m_frozen_sp->GetByteSize();
+ if (byte_size > 0) {
+ if (m_frozen_sp->GetDataExtractor().GetByteSize() < byte_size) {
+ m_frozen_sp->GetValue().ResizeData(byte_size);
+ m_frozen_sp->GetValue().GetData(m_frozen_sp->GetDataExtractor());
+ }
+ return const_cast<uint8_t *>(
+ m_frozen_sp->GetDataExtractor().GetDataStart());
+ }
+ return nullptr;
+}
+
+PersistentExpressionState::~PersistentExpressionState() {}
+
+lldb::addr_t PersistentExpressionState::LookupSymbol(ConstString name) {
+ SymbolMap::iterator si = m_symbol_map.find(name.GetCString());
+
+ if (si != m_symbol_map.end())
+ return si->second;
+ else
+ return LLDB_INVALID_ADDRESS;
+}
+
+void PersistentExpressionState::RegisterExecutionUnit(
+ lldb::IRExecutionUnitSP &execution_unit_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ m_execution_units.insert(execution_unit_sp);
+
+ if (log)
+ log->Printf("Registering JITted Functions:\n");
+
+ for (const IRExecutionUnit::JittedFunction &jitted_function :
+ execution_unit_sp->GetJittedFunctions()) {
+ if (jitted_function.m_external &&
+ jitted_function.m_name != execution_unit_sp->GetFunctionName() &&
+ jitted_function.m_remote_addr != LLDB_INVALID_ADDRESS) {
+ m_symbol_map[jitted_function.m_name.GetCString()] =
+ jitted_function.m_remote_addr;
+ if (log)
+ log->Printf(" Function: %s at 0x%" PRIx64 ".",
+ jitted_function.m_name.GetCString(),
+ jitted_function.m_remote_addr);
+ }
+ }
+
+ if (log)
+ log->Printf("Registering JIIted Symbols:\n");
+
+ for (const IRExecutionUnit::JittedGlobalVariable &global_var :
+ execution_unit_sp->GetJittedGlobalVariables()) {
+ if (global_var.m_remote_addr != LLDB_INVALID_ADDRESS) {
+ // Demangle the name before inserting it, so that lookups by the ConstStr
+ // of the demangled name will find the mangled one (needed for looking up
+ // metadata pointers.)
+ Mangled mangler(global_var.m_name);
+ mangler.GetDemangledName(lldb::eLanguageTypeUnknown);
+ m_symbol_map[global_var.m_name.GetCString()] = global_var.m_remote_addr;
+ if (log)
+ log->Printf(" Symbol: %s at 0x%" PRIx64 ".",
+ global_var.m_name.GetCString(), global_var.m_remote_addr);
+ }
+ }
+}
+
+ConstString PersistentExpressionState::GetNextPersistentVariableName(
+ Target &target, llvm::StringRef Prefix) {
+ llvm::SmallString<64> name;
+ {
+ llvm::raw_svector_ostream os(name);
+ os << Prefix << target.GetNextPersistentVariableIndex();
+ }
+ return ConstString(name);
+}
diff --git a/contrib/llvm-project/lldb/source/Expression/FunctionCaller.cpp b/contrib/llvm-project/lldb/source/Expression/FunctionCaller.cpp
new file mode 100644
index 000000000000..618c1a13212c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/FunctionCaller.cpp
@@ -0,0 +1,390 @@
+//===-- FunctionCaller.cpp ---------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectList.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb_private;
+
+// FunctionCaller constructor
+FunctionCaller::FunctionCaller(ExecutionContextScope &exe_scope,
+ const CompilerType &return_type,
+ const Address &functionAddress,
+ const ValueList &arg_value_list,
+ const char *name)
+ : Expression(exe_scope, eKindFunctionCaller), m_execution_unit_sp(),
+ m_parser(), m_jit_module_wp(), m_name(name ? name : "<unknown>"),
+ m_function_ptr(nullptr), m_function_addr(functionAddress),
+ m_function_return_type(return_type),
+ m_wrapper_function_name("__lldb_caller_function"),
+ m_wrapper_struct_name("__lldb_caller_struct"), m_wrapper_args_addrs(),
+ m_struct_valid(false), m_arg_values(arg_value_list), m_compiled(false),
+ m_JITted(false) {
+ m_jit_process_wp = lldb::ProcessWP(exe_scope.CalculateProcess());
+ // Can't make a FunctionCaller without a process.
+ assert(m_jit_process_wp.lock());
+}
+
+// Destructor
+FunctionCaller::~FunctionCaller() {
+ lldb::ProcessSP process_sp(m_jit_process_wp.lock());
+ if (process_sp) {
+ lldb::ModuleSP jit_module_sp(m_jit_module_wp.lock());
+ if (jit_module_sp)
+ process_sp->GetTarget().GetImages().Remove(jit_module_sp);
+ }
+}
+
+bool FunctionCaller::WriteFunctionWrapper(
+ ExecutionContext &exe_ctx, DiagnosticManager &diagnostic_manager) {
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (!process)
+ return false;
+
+ lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock());
+
+ if (process != jit_process_sp.get())
+ return false;
+
+ if (!m_compiled)
+ return false;
+
+ if (m_JITted)
+ return true;
+
+ bool can_interpret = false; // should stay that way
+
+ Status jit_error(m_parser->PrepareForExecution(
+ m_jit_start_addr, m_jit_end_addr, m_execution_unit_sp, exe_ctx,
+ can_interpret, eExecutionPolicyAlways));
+
+ if (!jit_error.Success()) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "Error in PrepareForExecution: %s.",
+ jit_error.AsCString());
+ return false;
+ }
+
+ if (m_parser->GetGenerateDebugInfo()) {
+ lldb::ModuleSP jit_module_sp(m_execution_unit_sp->GetJITModule());
+
+ if (jit_module_sp) {
+ ConstString const_func_name(FunctionName());
+ FileSpec jit_file;
+ jit_file.GetFilename() = const_func_name;
+ jit_module_sp->SetFileSpecAndObjectName(jit_file, ConstString());
+ m_jit_module_wp = jit_module_sp;
+ process->GetTarget().GetImages().Append(jit_module_sp,
+ true /* notify */);
+ }
+ }
+ if (process && m_jit_start_addr)
+ m_jit_process_wp = process->shared_from_this();
+
+ m_JITted = true;
+
+ return true;
+}
+
+bool FunctionCaller::WriteFunctionArguments(
+ ExecutionContext &exe_ctx, lldb::addr_t &args_addr_ref,
+ DiagnosticManager &diagnostic_manager) {
+ return WriteFunctionArguments(exe_ctx, args_addr_ref, m_arg_values,
+ diagnostic_manager);
+}
+
+// FIXME: Assure that the ValueList we were passed in is consistent with the one
+// that defined this function.
+
+bool FunctionCaller::WriteFunctionArguments(
+ ExecutionContext &exe_ctx, lldb::addr_t &args_addr_ref,
+ ValueList &arg_values, DiagnosticManager &diagnostic_manager) {
+ // All the information to reconstruct the struct is provided by the
+ // StructExtractor.
+ if (!m_struct_valid) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "Argument information was not correctly "
+ "parsed, so the function cannot be called.");
+ return false;
+ }
+
+ Status error;
+ lldb::ExpressionResults return_value = lldb::eExpressionSetupError;
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (process == nullptr)
+ return return_value;
+
+ lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock());
+
+ if (process != jit_process_sp.get())
+ return false;
+
+ if (args_addr_ref == LLDB_INVALID_ADDRESS) {
+ args_addr_ref = process->AllocateMemory(
+ m_struct_size, lldb::ePermissionsReadable | lldb::ePermissionsWritable,
+ error);
+ if (args_addr_ref == LLDB_INVALID_ADDRESS)
+ return false;
+ m_wrapper_args_addrs.push_back(args_addr_ref);
+ } else {
+ // Make sure this is an address that we've already handed out.
+ if (find(m_wrapper_args_addrs.begin(), m_wrapper_args_addrs.end(),
+ args_addr_ref) == m_wrapper_args_addrs.end()) {
+ return false;
+ }
+ }
+
+ // TODO: verify fun_addr needs to be a callable address
+ Scalar fun_addr(
+ m_function_addr.GetCallableLoadAddress(exe_ctx.GetTargetPtr()));
+ uint64_t first_offset = m_member_offsets[0];
+ process->WriteScalarToMemory(args_addr_ref + first_offset, fun_addr,
+ process->GetAddressByteSize(), error);
+
+ // FIXME: We will need to extend this for Variadic functions.
+
+ Status value_error;
+
+ size_t num_args = arg_values.GetSize();
+ if (num_args != m_arg_values.GetSize()) {
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityError,
+ "Wrong number of arguments - was: %" PRIu64 " should be: %" PRIu64 "",
+ (uint64_t)num_args, (uint64_t)m_arg_values.GetSize());
+ return false;
+ }
+
+ for (size_t i = 0; i < num_args; i++) {
+ // FIXME: We should sanity check sizes.
+
+ uint64_t offset = m_member_offsets[i + 1]; // Clang sizes are in bytes.
+ Value *arg_value = arg_values.GetValueAtIndex(i);
+
+ // FIXME: For now just do scalars:
+
+ // Special case: if it's a pointer, don't do anything (the ABI supports
+ // passing cstrings)
+
+ if (arg_value->GetValueType() == Value::eValueTypeHostAddress &&
+ arg_value->GetContextType() == Value::eContextTypeInvalid &&
+ arg_value->GetCompilerType().IsPointerType())
+ continue;
+
+ const Scalar &arg_scalar = arg_value->ResolveValue(&exe_ctx);
+
+ if (!process->WriteScalarToMemory(args_addr_ref + offset, arg_scalar,
+ arg_scalar.GetByteSize(), error))
+ return false;
+ }
+
+ return true;
+}
+
+bool FunctionCaller::InsertFunction(ExecutionContext &exe_ctx,
+ lldb::addr_t &args_addr_ref,
+ DiagnosticManager &diagnostic_manager) {
+ if (CompileFunction(exe_ctx.GetThreadSP(), diagnostic_manager) != 0)
+ return false;
+ if (!WriteFunctionWrapper(exe_ctx, diagnostic_manager))
+ return false;
+ if (!WriteFunctionArguments(exe_ctx, args_addr_ref, diagnostic_manager))
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Call Address: 0x%" PRIx64 " Struct Address: 0x%" PRIx64 ".\n",
+ m_jit_start_addr, args_addr_ref);
+
+ return true;
+}
+
+lldb::ThreadPlanSP FunctionCaller::GetThreadPlanToCallFunction(
+ ExecutionContext &exe_ctx, lldb::addr_t args_addr,
+ const EvaluateExpressionOptions &options,
+ DiagnosticManager &diagnostic_manager) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EXPRESSIONS |
+ LIBLLDB_LOG_STEP));
+
+ if (log)
+ log->Printf("-- [FunctionCaller::GetThreadPlanToCallFunction] Creating "
+ "thread plan to call function \"%s\" --",
+ m_name.c_str());
+
+ // FIXME: Use the errors Stream for better error reporting.
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (thread == nullptr) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "Can't call a function without a valid thread.");
+ return nullptr;
+ }
+
+ // Okay, now run the function:
+
+ Address wrapper_address(m_jit_start_addr);
+
+ lldb::addr_t args = {args_addr};
+
+ lldb::ThreadPlanSP new_plan_sp(new ThreadPlanCallFunction(
+ *thread, wrapper_address, CompilerType(), args, options));
+ new_plan_sp->SetIsMasterPlan(true);
+ new_plan_sp->SetOkayToDiscard(false);
+ return new_plan_sp;
+}
+
+bool FunctionCaller::FetchFunctionResults(ExecutionContext &exe_ctx,
+ lldb::addr_t args_addr,
+ Value &ret_value) {
+ // Read the return value - it is the last field in the struct:
+ // FIXME: How does clang tell us there's no return value? We need to handle
+ // that case.
+ // FIXME: Create our ThreadPlanCallFunction with the return CompilerType, and
+ // then use GetReturnValueObject
+ // to fetch the value. That way we can fetch any values we need.
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EXPRESSIONS |
+ LIBLLDB_LOG_STEP));
+
+ if (log)
+ log->Printf("-- [FunctionCaller::FetchFunctionResults] Fetching function "
+ "results for \"%s\"--",
+ m_name.c_str());
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (process == nullptr)
+ return false;
+
+ lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock());
+
+ if (process != jit_process_sp.get())
+ return false;
+
+ Status error;
+ ret_value.GetScalar() = process->ReadUnsignedIntegerFromMemory(
+ args_addr + m_return_offset, m_return_size, 0, error);
+
+ if (error.Fail())
+ return false;
+
+ ret_value.SetCompilerType(m_function_return_type);
+ ret_value.SetValueType(Value::eValueTypeScalar);
+ return true;
+}
+
+void FunctionCaller::DeallocateFunctionResults(ExecutionContext &exe_ctx,
+ lldb::addr_t args_addr) {
+ std::list<lldb::addr_t>::iterator pos;
+ pos = std::find(m_wrapper_args_addrs.begin(), m_wrapper_args_addrs.end(),
+ args_addr);
+ if (pos != m_wrapper_args_addrs.end())
+ m_wrapper_args_addrs.erase(pos);
+
+ exe_ctx.GetProcessRef().DeallocateMemory(args_addr);
+}
+
+lldb::ExpressionResults FunctionCaller::ExecuteFunction(
+ ExecutionContext &exe_ctx, lldb::addr_t *args_addr_ptr,
+ const EvaluateExpressionOptions &options,
+ DiagnosticManager &diagnostic_manager, Value &results) {
+ lldb::ExpressionResults return_value = lldb::eExpressionSetupError;
+
+ // FunctionCaller::ExecuteFunction execution is always just to get the
+ // result. Do make sure we ignore breakpoints, unwind on error, and don't try
+ // to debug it.
+ EvaluateExpressionOptions real_options = options;
+ real_options.SetDebug(false);
+ real_options.SetUnwindOnError(true);
+ real_options.SetIgnoreBreakpoints(true);
+
+ lldb::addr_t args_addr;
+
+ if (args_addr_ptr != nullptr)
+ args_addr = *args_addr_ptr;
+ else
+ args_addr = LLDB_INVALID_ADDRESS;
+
+ if (CompileFunction(exe_ctx.GetThreadSP(), diagnostic_manager) != 0)
+ return lldb::eExpressionSetupError;
+
+ if (args_addr == LLDB_INVALID_ADDRESS) {
+ if (!InsertFunction(exe_ctx, args_addr, diagnostic_manager))
+ return lldb::eExpressionSetupError;
+ }
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EXPRESSIONS |
+ LIBLLDB_LOG_STEP));
+
+ if (log)
+ log->Printf(
+ "== [FunctionCaller::ExecuteFunction] Executing function \"%s\" ==",
+ m_name.c_str());
+
+ lldb::ThreadPlanSP call_plan_sp = GetThreadPlanToCallFunction(
+ exe_ctx, args_addr, real_options, diagnostic_manager);
+ if (!call_plan_sp)
+ return lldb::eExpressionSetupError;
+
+ // We need to make sure we record the fact that we are running an expression
+ // here otherwise this fact will fail to be recorded when fetching an
+ // Objective-C object description
+ if (exe_ctx.GetProcessPtr())
+ exe_ctx.GetProcessPtr()->SetRunningUserExpression(true);
+
+ return_value = exe_ctx.GetProcessRef().RunThreadPlan(
+ exe_ctx, call_plan_sp, real_options, diagnostic_manager);
+
+ if (log) {
+ if (return_value != lldb::eExpressionCompleted) {
+ log->Printf("== [FunctionCaller::ExecuteFunction] Execution of \"%s\" "
+ "completed abnormally ==",
+ m_name.c_str());
+ } else {
+ log->Printf("== [FunctionCaller::ExecuteFunction] Execution of \"%s\" "
+ "completed normally ==",
+ m_name.c_str());
+ }
+ }
+
+ if (exe_ctx.GetProcessPtr())
+ exe_ctx.GetProcessPtr()->SetRunningUserExpression(false);
+
+ if (args_addr_ptr != nullptr)
+ *args_addr_ptr = args_addr;
+
+ if (return_value != lldb::eExpressionCompleted)
+ return return_value;
+
+ FetchFunctionResults(exe_ctx, args_addr, results);
+
+ if (args_addr_ptr == nullptr)
+ DeallocateFunctionResults(exe_ctx, args_addr);
+
+ return lldb::eExpressionCompleted;
+}
diff --git a/contrib/llvm-project/lldb/source/Expression/IRExecutionUnit.cpp b/contrib/llvm-project/lldb/source/Expression/IRExecutionUnit.cpp
new file mode 100644
index 000000000000..25404ad313e1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/IRExecutionUnit.cpp
@@ -0,0 +1,1277 @@
+//===-- IRExecutionUnit.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/ExecutionEngine.h"
+#include "llvm/ExecutionEngine/ObjectCache.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+
+#include "lldb/../../source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "lldb/../../source/Plugins/ObjectFile/JIT/ObjectFileJIT.h"
+
+using namespace lldb_private;
+
+IRExecutionUnit::IRExecutionUnit(std::unique_ptr<llvm::LLVMContext> &context_up,
+ std::unique_ptr<llvm::Module> &module_up,
+ ConstString &name,
+ const lldb::TargetSP &target_sp,
+ const SymbolContext &sym_ctx,
+ std::vector<std::string> &cpu_features)
+ : IRMemoryMap(target_sp), m_context_up(context_up.release()),
+ m_module_up(module_up.release()), m_module(m_module_up.get()),
+ m_cpu_features(cpu_features), m_name(name), m_sym_ctx(sym_ctx),
+ m_did_jit(false), m_function_load_addr(LLDB_INVALID_ADDRESS),
+ m_function_end_load_addr(LLDB_INVALID_ADDRESS),
+ m_reported_allocations(false) {}
+
+lldb::addr_t IRExecutionUnit::WriteNow(const uint8_t *bytes, size_t size,
+ Status &error) {
+ const bool zero_memory = false;
+ lldb::addr_t allocation_process_addr =
+ Malloc(size, 8, lldb::ePermissionsWritable | lldb::ePermissionsReadable,
+ eAllocationPolicyMirror, zero_memory, error);
+
+ if (!error.Success())
+ return LLDB_INVALID_ADDRESS;
+
+ WriteMemory(allocation_process_addr, bytes, size, error);
+
+ if (!error.Success()) {
+ Status err;
+ Free(allocation_process_addr, err);
+
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ if (Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)) {
+ DataBufferHeap my_buffer(size, 0);
+ Status err;
+ ReadMemory(my_buffer.GetBytes(), allocation_process_addr, size, err);
+
+ if (err.Success()) {
+ DataExtractor my_extractor(my_buffer.GetBytes(), my_buffer.GetByteSize(),
+ lldb::eByteOrderBig, 8);
+ my_extractor.PutToLog(log, 0, my_buffer.GetByteSize(),
+ allocation_process_addr, 16,
+ DataExtractor::TypeUInt8);
+ }
+ }
+
+ return allocation_process_addr;
+}
+
+void IRExecutionUnit::FreeNow(lldb::addr_t allocation) {
+ if (allocation == LLDB_INVALID_ADDRESS)
+ return;
+
+ Status err;
+
+ Free(allocation, err);
+}
+
+Status IRExecutionUnit::DisassembleFunction(Stream &stream,
+ lldb::ProcessSP &process_wp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ ExecutionContext exe_ctx(process_wp);
+
+ Status ret;
+
+ ret.Clear();
+
+ lldb::addr_t func_local_addr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t func_remote_addr = LLDB_INVALID_ADDRESS;
+
+ for (JittedFunction &function : m_jitted_functions) {
+ if (function.m_name == m_name) {
+ func_local_addr = function.m_local_addr;
+ func_remote_addr = function.m_remote_addr;
+ }
+ }
+
+ if (func_local_addr == LLDB_INVALID_ADDRESS) {
+ ret.SetErrorToGenericError();
+ ret.SetErrorStringWithFormat("Couldn't find function %s for disassembly",
+ m_name.AsCString());
+ return ret;
+ }
+
+ if (log)
+ log->Printf("Found function, has local address 0x%" PRIx64
+ " and remote address 0x%" PRIx64,
+ (uint64_t)func_local_addr, (uint64_t)func_remote_addr);
+
+ std::pair<lldb::addr_t, lldb::addr_t> func_range;
+
+ func_range = GetRemoteRangeForLocal(func_local_addr);
+
+ if (func_range.first == 0 && func_range.second == 0) {
+ ret.SetErrorToGenericError();
+ ret.SetErrorStringWithFormat("Couldn't find code range for function %s",
+ m_name.AsCString());
+ return ret;
+ }
+
+ if (log)
+ log->Printf("Function's code range is [0x%" PRIx64 "+0x%" PRIx64 "]",
+ func_range.first, func_range.second);
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (!target) {
+ ret.SetErrorToGenericError();
+ ret.SetErrorString("Couldn't find the target");
+ return ret;
+ }
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(func_range.second, 0));
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Status err;
+ process->ReadMemory(func_remote_addr, buffer_sp->GetBytes(),
+ buffer_sp->GetByteSize(), err);
+
+ if (!err.Success()) {
+ ret.SetErrorToGenericError();
+ ret.SetErrorStringWithFormat("Couldn't read from process: %s",
+ err.AsCString("unknown error"));
+ return ret;
+ }
+
+ ArchSpec arch(target->GetArchitecture());
+
+ const char *plugin_name = nullptr;
+ const char *flavor_string = nullptr;
+ lldb::DisassemblerSP disassembler_sp =
+ Disassembler::FindPlugin(arch, flavor_string, plugin_name);
+
+ if (!disassembler_sp) {
+ ret.SetErrorToGenericError();
+ ret.SetErrorStringWithFormat(
+ "Unable to find disassembler plug-in for %s architecture.",
+ arch.GetArchitectureName());
+ return ret;
+ }
+
+ if (!process) {
+ ret.SetErrorToGenericError();
+ ret.SetErrorString("Couldn't find the process");
+ return ret;
+ }
+
+ DataExtractor extractor(buffer_sp, process->GetByteOrder(),
+ target->GetArchitecture().GetAddressByteSize());
+
+ if (log) {
+ log->Printf("Function data has contents:");
+ extractor.PutToLog(log, 0, extractor.GetByteSize(), func_remote_addr, 16,
+ DataExtractor::TypeUInt8);
+ }
+
+ disassembler_sp->DecodeInstructions(Address(func_remote_addr), extractor, 0,
+ UINT32_MAX, false, false);
+
+ InstructionList &instruction_list = disassembler_sp->GetInstructionList();
+ instruction_list.Dump(&stream, true, true, &exe_ctx);
+ return ret;
+}
+
+static void ReportInlineAsmError(const llvm::SMDiagnostic &diagnostic,
+ void *Context, unsigned LocCookie) {
+ Status *err = static_cast<Status *>(Context);
+
+ if (err && err->Success()) {
+ err->SetErrorToGenericError();
+ err->SetErrorStringWithFormat("Inline assembly error: %s",
+ diagnostic.getMessage().str().c_str());
+ }
+}
+
+void IRExecutionUnit::ReportSymbolLookupError(ConstString name) {
+ m_failed_lookups.push_back(name);
+}
+
+void IRExecutionUnit::GetRunnableInfo(Status &error, lldb::addr_t &func_addr,
+ lldb::addr_t &func_end) {
+ lldb::ProcessSP process_sp(GetProcessWP().lock());
+
+ static std::recursive_mutex s_runnable_info_mutex;
+
+ func_addr = LLDB_INVALID_ADDRESS;
+ func_end = LLDB_INVALID_ADDRESS;
+
+ if (!process_sp) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't write the JIT compiled code into the "
+ "process because the process is invalid");
+ return;
+ }
+
+ if (m_did_jit) {
+ func_addr = m_function_load_addr;
+ func_end = m_function_end_load_addr;
+
+ return;
+ };
+
+ std::lock_guard<std::recursive_mutex> guard(s_runnable_info_mutex);
+
+ m_did_jit = true;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ std::string error_string;
+
+ if (log) {
+ std::string s;
+ llvm::raw_string_ostream oss(s);
+
+ m_module->print(oss, nullptr);
+
+ oss.flush();
+
+ log->Printf("Module being sent to JIT: \n%s", s.c_str());
+ }
+
+ m_module_up->getContext().setInlineAsmDiagnosticHandler(ReportInlineAsmError,
+ &error);
+
+ llvm::EngineBuilder builder(std::move(m_module_up));
+ llvm::Triple triple(m_module->getTargetTriple());
+
+ builder.setEngineKind(llvm::EngineKind::JIT)
+ .setErrorStr(&error_string)
+ .setRelocationModel(triple.isOSBinFormatMachO()
+ ? llvm::Reloc::PIC_
+ : llvm::Reloc::Static)
+ .setMCJITMemoryManager(
+ std::unique_ptr<MemoryManager>(new MemoryManager(*this)))
+ .setOptLevel(llvm::CodeGenOpt::Less);
+
+ llvm::StringRef mArch;
+ llvm::StringRef mCPU;
+ llvm::SmallVector<std::string, 0> mAttrs;
+
+ for (std::string &feature : m_cpu_features)
+ mAttrs.push_back(feature);
+
+ llvm::TargetMachine *target_machine =
+ builder.selectTarget(triple, mArch, mCPU, mAttrs);
+
+ m_execution_engine_up.reset(builder.create(target_machine));
+
+ if (!m_execution_engine_up) {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("Couldn't JIT the function: %s",
+ error_string.c_str());
+ return;
+ }
+
+ m_strip_underscore =
+ (m_execution_engine_up->getDataLayout().getGlobalPrefix() == '_');
+
+ class ObjectDumper : public llvm::ObjectCache {
+ public:
+ void notifyObjectCompiled(const llvm::Module *module,
+ llvm::MemoryBufferRef object) override {
+ int fd = 0;
+ llvm::SmallVector<char, 256> result_path;
+ std::string object_name_model =
+ "jit-object-" + module->getModuleIdentifier() + "-%%%.o";
+ (void)llvm::sys::fs::createUniqueFile(object_name_model, fd, result_path);
+ llvm::raw_fd_ostream fds(fd, true);
+ fds.write(object.getBufferStart(), object.getBufferSize());
+ }
+
+ std::unique_ptr<llvm::MemoryBuffer>
+ getObject(const llvm::Module *module) override {
+ // Return nothing - we're just abusing the object-cache mechanism to dump
+ // objects.
+ return nullptr;
+ }
+ };
+
+ if (process_sp->GetTarget().GetEnableSaveObjects()) {
+ m_object_cache_up = llvm::make_unique<ObjectDumper>();
+ m_execution_engine_up->setObjectCache(m_object_cache_up.get());
+ }
+
+ // Make sure we see all sections, including ones that don't have
+ // relocations...
+ m_execution_engine_up->setProcessAllSections(true);
+
+ m_execution_engine_up->DisableLazyCompilation();
+
+ for (llvm::Function &function : *m_module) {
+ if (function.isDeclaration() || function.hasPrivateLinkage())
+ continue;
+
+ const bool external =
+ function.hasExternalLinkage() || function.hasLinkOnceODRLinkage();
+
+ void *fun_ptr = m_execution_engine_up->getPointerToFunction(&function);
+
+ if (!error.Success()) {
+ // We got an error through our callback!
+ return;
+ }
+
+ if (!fun_ptr) {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat(
+ "'%s' was in the JITted module but wasn't lowered",
+ function.getName().str().c_str());
+ return;
+ }
+ m_jitted_functions.push_back(JittedFunction(
+ function.getName().str().c_str(), external, (lldb::addr_t)fun_ptr));
+ }
+
+ CommitAllocations(process_sp);
+ ReportAllocations(*m_execution_engine_up);
+
+ // We have to do this after calling ReportAllocations because for the MCJIT,
+ // getGlobalValueAddress will cause the JIT to perform all relocations. That
+ // can only be done once, and has to happen after we do the remapping from
+ // local -> remote. That means we don't know the local address of the
+ // Variables, but we don't need that for anything, so that's okay.
+
+ std::function<void(llvm::GlobalValue &)> RegisterOneValue = [this](
+ llvm::GlobalValue &val) {
+ if (val.hasExternalLinkage() && !val.isDeclaration()) {
+ uint64_t var_ptr_addr =
+ m_execution_engine_up->getGlobalValueAddress(val.getName().str());
+
+ lldb::addr_t remote_addr = GetRemoteAddressForLocal(var_ptr_addr);
+
+ // This is a really unfortunae API that sometimes returns local addresses
+ // and sometimes returns remote addresses, based on whether the variable
+ // was relocated during ReportAllocations or not.
+
+ if (remote_addr == LLDB_INVALID_ADDRESS) {
+ remote_addr = var_ptr_addr;
+ }
+
+ if (var_ptr_addr != 0)
+ m_jitted_global_variables.push_back(JittedGlobalVariable(
+ val.getName().str().c_str(), LLDB_INVALID_ADDRESS, remote_addr));
+ }
+ };
+
+ for (llvm::GlobalVariable &global_var : m_module->getGlobalList()) {
+ RegisterOneValue(global_var);
+ }
+
+ for (llvm::GlobalAlias &global_alias : m_module->getAliasList()) {
+ RegisterOneValue(global_alias);
+ }
+
+ WriteData(process_sp);
+
+ if (m_failed_lookups.size()) {
+ StreamString ss;
+
+ ss.PutCString("Couldn't lookup symbols:\n");
+
+ bool emitNewLine = false;
+
+ for (ConstString failed_lookup : m_failed_lookups) {
+ if (emitNewLine)
+ ss.PutCString("\n");
+ emitNewLine = true;
+ ss.PutCString(" ");
+ ss.PutCString(Mangled(failed_lookup)
+ .GetDemangledName(lldb::eLanguageTypeObjC_plus_plus)
+ .AsCString());
+ }
+
+ m_failed_lookups.clear();
+
+ error.SetErrorString(ss.GetString());
+
+ return;
+ }
+
+ m_function_load_addr = LLDB_INVALID_ADDRESS;
+ m_function_end_load_addr = LLDB_INVALID_ADDRESS;
+
+ for (JittedFunction &jitted_function : m_jitted_functions) {
+ jitted_function.m_remote_addr =
+ GetRemoteAddressForLocal(jitted_function.m_local_addr);
+
+ if (!m_name.IsEmpty() && jitted_function.m_name == m_name) {
+ AddrRange func_range =
+ GetRemoteRangeForLocal(jitted_function.m_local_addr);
+ m_function_end_load_addr = func_range.first + func_range.second;
+ m_function_load_addr = jitted_function.m_remote_addr;
+ }
+ }
+
+ if (log) {
+ log->Printf("Code can be run in the target.");
+
+ StreamString disassembly_stream;
+
+ Status err = DisassembleFunction(disassembly_stream, process_sp);
+
+ if (!err.Success()) {
+ log->Printf("Couldn't disassemble function : %s",
+ err.AsCString("unknown error"));
+ } else {
+ log->Printf("Function disassembly:\n%s", disassembly_stream.GetData());
+ }
+
+ log->Printf("Sections: ");
+ for (AllocationRecord &record : m_records) {
+ if (record.m_process_address != LLDB_INVALID_ADDRESS) {
+ record.dump(log);
+
+ DataBufferHeap my_buffer(record.m_size, 0);
+ Status err;
+ ReadMemory(my_buffer.GetBytes(), record.m_process_address,
+ record.m_size, err);
+
+ if (err.Success()) {
+ DataExtractor my_extractor(my_buffer.GetBytes(),
+ my_buffer.GetByteSize(),
+ lldb::eByteOrderBig, 8);
+ my_extractor.PutToLog(log, 0, my_buffer.GetByteSize(),
+ record.m_process_address, 16,
+ DataExtractor::TypeUInt8);
+ }
+ } else {
+ record.dump(log);
+
+ DataExtractor my_extractor((const void *)record.m_host_address,
+ record.m_size, lldb::eByteOrderBig, 8);
+ my_extractor.PutToLog(log, 0, record.m_size, record.m_host_address, 16,
+ DataExtractor::TypeUInt8);
+ }
+ }
+ }
+
+ func_addr = m_function_load_addr;
+ func_end = m_function_end_load_addr;
+
+ return;
+}
+
+IRExecutionUnit::~IRExecutionUnit() {
+ m_module_up.reset();
+ m_execution_engine_up.reset();
+ m_context_up.reset();
+}
+
+IRExecutionUnit::MemoryManager::MemoryManager(IRExecutionUnit &parent)
+ : m_default_mm_up(new llvm::SectionMemoryManager()), m_parent(parent) {}
+
+IRExecutionUnit::MemoryManager::~MemoryManager() {}
+
+lldb::SectionType IRExecutionUnit::GetSectionTypeFromSectionName(
+ const llvm::StringRef &name, IRExecutionUnit::AllocationKind alloc_kind) {
+ lldb::SectionType sect_type = lldb::eSectionTypeCode;
+ switch (alloc_kind) {
+ case AllocationKind::Stub:
+ sect_type = lldb::eSectionTypeCode;
+ break;
+ case AllocationKind::Code:
+ sect_type = lldb::eSectionTypeCode;
+ break;
+ case AllocationKind::Data:
+ sect_type = lldb::eSectionTypeData;
+ break;
+ case AllocationKind::Global:
+ sect_type = lldb::eSectionTypeData;
+ break;
+ case AllocationKind::Bytes:
+ sect_type = lldb::eSectionTypeOther;
+ break;
+ }
+
+ if (!name.empty()) {
+ if (name.equals("__text") || name.equals(".text"))
+ sect_type = lldb::eSectionTypeCode;
+ else if (name.equals("__data") || name.equals(".data"))
+ sect_type = lldb::eSectionTypeCode;
+ else if (name.startswith("__debug_") || name.startswith(".debug_")) {
+ const uint32_t name_idx = name[0] == '_' ? 8 : 7;
+ llvm::StringRef dwarf_name(name.substr(name_idx));
+ switch (dwarf_name[0]) {
+ case 'a':
+ if (dwarf_name.equals("abbrev"))
+ sect_type = lldb::eSectionTypeDWARFDebugAbbrev;
+ else if (dwarf_name.equals("aranges"))
+ sect_type = lldb::eSectionTypeDWARFDebugAranges;
+ else if (dwarf_name.equals("addr"))
+ sect_type = lldb::eSectionTypeDWARFDebugAddr;
+ break;
+
+ case 'f':
+ if (dwarf_name.equals("frame"))
+ sect_type = lldb::eSectionTypeDWARFDebugFrame;
+ break;
+
+ case 'i':
+ if (dwarf_name.equals("info"))
+ sect_type = lldb::eSectionTypeDWARFDebugInfo;
+ break;
+
+ case 'l':
+ if (dwarf_name.equals("line"))
+ sect_type = lldb::eSectionTypeDWARFDebugLine;
+ else if (dwarf_name.equals("loc"))
+ sect_type = lldb::eSectionTypeDWARFDebugLoc;
+ else if (dwarf_name.equals("loclists"))
+ sect_type = lldb::eSectionTypeDWARFDebugLocLists;
+ break;
+
+ case 'm':
+ if (dwarf_name.equals("macinfo"))
+ sect_type = lldb::eSectionTypeDWARFDebugMacInfo;
+ break;
+
+ case 'p':
+ if (dwarf_name.equals("pubnames"))
+ sect_type = lldb::eSectionTypeDWARFDebugPubNames;
+ else if (dwarf_name.equals("pubtypes"))
+ sect_type = lldb::eSectionTypeDWARFDebugPubTypes;
+ break;
+
+ case 's':
+ if (dwarf_name.equals("str"))
+ sect_type = lldb::eSectionTypeDWARFDebugStr;
+ else if (dwarf_name.equals("str_offsets"))
+ sect_type = lldb::eSectionTypeDWARFDebugStrOffsets;
+ break;
+
+ case 'r':
+ if (dwarf_name.equals("ranges"))
+ sect_type = lldb::eSectionTypeDWARFDebugRanges;
+ break;
+
+ default:
+ break;
+ }
+ } else if (name.startswith("__apple_") || name.startswith(".apple_"))
+ sect_type = lldb::eSectionTypeInvalid;
+ else if (name.equals("__objc_imageinfo"))
+ sect_type = lldb::eSectionTypeOther;
+ }
+ return sect_type;
+}
+
+uint8_t *IRExecutionUnit::MemoryManager::allocateCodeSection(
+ uintptr_t Size, unsigned Alignment, unsigned SectionID,
+ llvm::StringRef SectionName) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ uint8_t *return_value = m_default_mm_up->allocateCodeSection(
+ Size, Alignment, SectionID, SectionName);
+
+ m_parent.m_records.push_back(AllocationRecord(
+ (uintptr_t)return_value,
+ lldb::ePermissionsReadable | lldb::ePermissionsExecutable,
+ GetSectionTypeFromSectionName(SectionName, AllocationKind::Code), Size,
+ Alignment, SectionID, SectionName.str().c_str()));
+
+ if (log) {
+ log->Printf("IRExecutionUnit::allocateCodeSection(Size=0x%" PRIx64
+ ", Alignment=%u, SectionID=%u) = %p",
+ (uint64_t)Size, Alignment, SectionID, (void *)return_value);
+ }
+
+ if (m_parent.m_reported_allocations) {
+ Status err;
+ lldb::ProcessSP process_sp =
+ m_parent.GetBestExecutionContextScope()->CalculateProcess();
+
+ m_parent.CommitOneAllocation(process_sp, err, m_parent.m_records.back());
+ }
+
+ return return_value;
+}
+
+uint8_t *IRExecutionUnit::MemoryManager::allocateDataSection(
+ uintptr_t Size, unsigned Alignment, unsigned SectionID,
+ llvm::StringRef SectionName, bool IsReadOnly) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ uint8_t *return_value = m_default_mm_up->allocateDataSection(
+ Size, Alignment, SectionID, SectionName, IsReadOnly);
+
+ uint32_t permissions = lldb::ePermissionsReadable;
+ if (!IsReadOnly)
+ permissions |= lldb::ePermissionsWritable;
+ m_parent.m_records.push_back(AllocationRecord(
+ (uintptr_t)return_value, permissions,
+ GetSectionTypeFromSectionName(SectionName, AllocationKind::Data), Size,
+ Alignment, SectionID, SectionName.str().c_str()));
+ if (log) {
+ log->Printf("IRExecutionUnit::allocateDataSection(Size=0x%" PRIx64
+ ", Alignment=%u, SectionID=%u) = %p",
+ (uint64_t)Size, Alignment, SectionID, (void *)return_value);
+ }
+
+ if (m_parent.m_reported_allocations) {
+ Status err;
+ lldb::ProcessSP process_sp =
+ m_parent.GetBestExecutionContextScope()->CalculateProcess();
+
+ m_parent.CommitOneAllocation(process_sp, err, m_parent.m_records.back());
+ }
+
+ return return_value;
+}
+
+static ConstString
+FindBestAlternateMangledName(ConstString demangled,
+ const lldb::LanguageType &lang_type,
+ const SymbolContext &sym_ctx) {
+ CPlusPlusLanguage::MethodName cpp_name(demangled);
+ std::string scope_qualified_name = cpp_name.GetScopeQualifiedName();
+
+ if (!scope_qualified_name.size())
+ return ConstString();
+
+ if (!sym_ctx.module_sp)
+ return ConstString();
+
+ SymbolVendor *sym_vendor = sym_ctx.module_sp->GetSymbolVendor();
+ if (!sym_vendor)
+ return ConstString();
+
+ lldb_private::SymbolFile *sym_file = sym_vendor->GetSymbolFile();
+ if (!sym_file)
+ return ConstString();
+
+ std::vector<ConstString> alternates;
+ sym_file->GetMangledNamesForFunction(scope_qualified_name, alternates);
+
+ std::vector<ConstString> param_and_qual_matches;
+ std::vector<ConstString> param_matches;
+ for (size_t i = 0; i < alternates.size(); i++) {
+ ConstString alternate_mangled_name = alternates[i];
+ Mangled mangled(alternate_mangled_name, true);
+ ConstString demangled = mangled.GetDemangledName(lang_type);
+
+ CPlusPlusLanguage::MethodName alternate_cpp_name(demangled);
+ if (!cpp_name.IsValid())
+ continue;
+
+ if (alternate_cpp_name.GetArguments() == cpp_name.GetArguments()) {
+ if (alternate_cpp_name.GetQualifiers() == cpp_name.GetQualifiers())
+ param_and_qual_matches.push_back(alternate_mangled_name);
+ else
+ param_matches.push_back(alternate_mangled_name);
+ }
+ }
+
+ if (param_and_qual_matches.size())
+ return param_and_qual_matches[0]; // It is assumed that there will be only
+ // one!
+ else if (param_matches.size())
+ return param_matches[0]; // Return one of them as a best match
+ else
+ return ConstString();
+}
+
+struct IRExecutionUnit::SearchSpec {
+ ConstString name;
+ lldb::FunctionNameType mask;
+
+ SearchSpec(ConstString n,
+ lldb::FunctionNameType m = lldb::eFunctionNameTypeFull)
+ : name(n), mask(m) {}
+};
+
+void IRExecutionUnit::CollectCandidateCNames(
+ std::vector<IRExecutionUnit::SearchSpec> &C_specs,
+ ConstString name) {
+ if (m_strip_underscore && name.AsCString()[0] == '_')
+ C_specs.insert(C_specs.begin(), ConstString(&name.AsCString()[1]));
+ C_specs.push_back(SearchSpec(name));
+}
+
+void IRExecutionUnit::CollectCandidateCPlusPlusNames(
+ std::vector<IRExecutionUnit::SearchSpec> &CPP_specs,
+ const std::vector<SearchSpec> &C_specs, const SymbolContext &sc) {
+ for (const SearchSpec &C_spec : C_specs) {
+ ConstString name = C_spec.name;
+
+ if (CPlusPlusLanguage::IsCPPMangledName(name.GetCString())) {
+ Mangled mangled(name, true);
+ ConstString demangled =
+ mangled.GetDemangledName(lldb::eLanguageTypeC_plus_plus);
+
+ if (demangled) {
+ ConstString best_alternate_mangled_name = FindBestAlternateMangledName(
+ demangled, lldb::eLanguageTypeC_plus_plus, sc);
+
+ if (best_alternate_mangled_name) {
+ CPP_specs.push_back(best_alternate_mangled_name);
+ }
+
+ CPP_specs.push_back(SearchSpec(demangled, lldb::eFunctionNameTypeFull));
+ }
+ }
+
+ std::set<ConstString> alternates;
+ CPlusPlusLanguage::FindAlternateFunctionManglings(name, alternates);
+ CPP_specs.insert(CPP_specs.end(), alternates.begin(), alternates.end());
+ }
+}
+
+void IRExecutionUnit::CollectFallbackNames(
+ std::vector<SearchSpec> &fallback_specs,
+ const std::vector<SearchSpec> &C_specs) {
+ // As a last-ditch fallback, try the base name for C++ names. It's terrible,
+ // but the DWARF doesn't always encode "extern C" correctly.
+
+ for (const SearchSpec &C_spec : C_specs) {
+ ConstString name = C_spec.name;
+
+ if (CPlusPlusLanguage::IsCPPMangledName(name.GetCString())) {
+ Mangled mangled_name(name);
+ ConstString demangled_name =
+ mangled_name.GetDemangledName(lldb::eLanguageTypeC_plus_plus);
+ if (!demangled_name.IsEmpty()) {
+ const char *demangled_cstr = demangled_name.AsCString();
+ const char *lparen_loc = strchr(demangled_cstr, '(');
+ if (lparen_loc) {
+ llvm::StringRef base_name(demangled_cstr,
+ lparen_loc - demangled_cstr);
+ fallback_specs.push_back(ConstString(base_name));
+ }
+ }
+ }
+ }
+}
+
+lldb::addr_t IRExecutionUnit::FindInSymbols(
+ const std::vector<IRExecutionUnit::SearchSpec> &specs,
+ const lldb_private::SymbolContext &sc,
+ bool &symbol_was_missing_weak) {
+ symbol_was_missing_weak = false;
+ Target *target = sc.target_sp.get();
+
+ if (!target) {
+ // we shouldn't be doing any symbol lookup at all without a target
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ for (const SearchSpec &spec : specs) {
+ SymbolContextList sc_list;
+
+ lldb::addr_t best_internal_load_address = LLDB_INVALID_ADDRESS;
+
+ std::function<bool(lldb::addr_t &, SymbolContextList &,
+ const lldb_private::SymbolContext &)>
+ get_external_load_address = [&best_internal_load_address, target,
+ &symbol_was_missing_weak](
+ lldb::addr_t &load_address, SymbolContextList &sc_list,
+ const lldb_private::SymbolContext &sc) -> lldb::addr_t {
+ load_address = LLDB_INVALID_ADDRESS;
+
+ if (sc_list.GetSize() == 0)
+ return false;
+
+ // missing_weak_symbol will be true only if we found only weak undefined
+ // references to this symbol.
+ symbol_was_missing_weak = true;
+ for (auto candidate_sc : sc_list.SymbolContexts()) {
+ // Only symbols can be weak undefined:
+ if (!candidate_sc.symbol)
+ symbol_was_missing_weak = false;
+ else if (candidate_sc.symbol->GetType() != lldb::eSymbolTypeUndefined
+ || !candidate_sc.symbol->IsWeak())
+ symbol_was_missing_weak = false;
+
+ const bool is_external =
+ (candidate_sc.function) ||
+ (candidate_sc.symbol && candidate_sc.symbol->IsExternal());
+ if (candidate_sc.symbol) {
+ load_address = candidate_sc.symbol->ResolveCallableAddress(*target);
+
+ if (load_address == LLDB_INVALID_ADDRESS) {
+ if (target->GetProcessSP())
+ load_address =
+ candidate_sc.symbol->GetAddress().GetLoadAddress(target);
+ else
+ load_address = candidate_sc.symbol->GetAddress().GetFileAddress();
+ }
+ }
+
+ if (load_address == LLDB_INVALID_ADDRESS && candidate_sc.function) {
+ if (target->GetProcessSP())
+ load_address = candidate_sc.function->GetAddressRange()
+ .GetBaseAddress()
+ .GetLoadAddress(target);
+ else
+ load_address = candidate_sc.function->GetAddressRange()
+ .GetBaseAddress()
+ .GetFileAddress();
+ }
+
+ if (load_address != LLDB_INVALID_ADDRESS) {
+ if (is_external) {
+ return true;
+ } else if (best_internal_load_address == LLDB_INVALID_ADDRESS) {
+ best_internal_load_address = load_address;
+ load_address = LLDB_INVALID_ADDRESS;
+ }
+ }
+ }
+
+ // You test the address of a weak symbol against NULL to see if it is
+ // present. So we should return 0 for a missing weak symbol.
+ if (symbol_was_missing_weak) {
+ load_address = 0;
+ return true;
+ }
+
+ return false;
+ };
+
+ if (sc.module_sp) {
+ sc.module_sp->FindFunctions(spec.name, nullptr, spec.mask,
+ true, // include_symbols
+ false, // include_inlines
+ true, // append
+ sc_list);
+ }
+
+ lldb::addr_t load_address = LLDB_INVALID_ADDRESS;
+
+ if (get_external_load_address(load_address, sc_list, sc)) {
+ return load_address;
+ } else {
+ sc_list.Clear();
+ }
+
+ if (sc_list.GetSize() == 0 && sc.target_sp) {
+ sc.target_sp->GetImages().FindFunctions(spec.name, spec.mask,
+ true, // include_symbols
+ false, // include_inlines
+ true, // append
+ sc_list);
+ }
+
+ if (get_external_load_address(load_address, sc_list, sc)) {
+ return load_address;
+ } else {
+ sc_list.Clear();
+ }
+
+ if (sc_list.GetSize() == 0 && sc.target_sp) {
+ sc.target_sp->GetImages().FindSymbolsWithNameAndType(
+ spec.name, lldb::eSymbolTypeAny, sc_list);
+ }
+
+ if (get_external_load_address(load_address, sc_list, sc)) {
+ return load_address;
+ }
+ // if there are any searches we try after this, add an sc_list.Clear() in
+ // an "else" clause here
+
+ if (best_internal_load_address != LLDB_INVALID_ADDRESS) {
+ return best_internal_load_address;
+ }
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+lldb::addr_t
+IRExecutionUnit::FindInRuntimes(const std::vector<SearchSpec> &specs,
+ const lldb_private::SymbolContext &sc) {
+ lldb::TargetSP target_sp = sc.target_sp;
+
+ if (!target_sp) {
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ lldb::ProcessSP process_sp = sc.target_sp->GetProcessSP();
+
+ if (!process_sp) {
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ for (const SearchSpec &spec : specs) {
+ for (LanguageRuntime *runtime : process_sp->GetLanguageRuntimes()) {
+ lldb::addr_t symbol_load_addr = runtime->LookupRuntimeSymbol(spec.name);
+
+ if (symbol_load_addr != LLDB_INVALID_ADDRESS)
+ return symbol_load_addr;
+ }
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+lldb::addr_t IRExecutionUnit::FindInUserDefinedSymbols(
+ const std::vector<SearchSpec> &specs,
+ const lldb_private::SymbolContext &sc) {
+ lldb::TargetSP target_sp = sc.target_sp;
+
+ for (const SearchSpec &spec : specs) {
+ lldb::addr_t symbol_load_addr = target_sp->GetPersistentSymbol(spec.name);
+
+ if (symbol_load_addr != LLDB_INVALID_ADDRESS)
+ return symbol_load_addr;
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+lldb::addr_t
+IRExecutionUnit::FindSymbol(lldb_private::ConstString name, bool &missing_weak) {
+ std::vector<SearchSpec> candidate_C_names;
+ std::vector<SearchSpec> candidate_CPlusPlus_names;
+
+ CollectCandidateCNames(candidate_C_names, name);
+
+ lldb::addr_t ret = FindInSymbols(candidate_C_names, m_sym_ctx, missing_weak);
+ if (ret != LLDB_INVALID_ADDRESS)
+ return ret;
+
+ // If we find the symbol in runtimes or user defined symbols it can't be
+ // a missing weak symbol.
+ missing_weak = false;
+ ret = FindInRuntimes(candidate_C_names, m_sym_ctx);
+ if (ret != LLDB_INVALID_ADDRESS)
+ return ret;
+
+ ret = FindInUserDefinedSymbols(candidate_C_names, m_sym_ctx);
+ if (ret != LLDB_INVALID_ADDRESS)
+ return ret;
+
+ CollectCandidateCPlusPlusNames(candidate_CPlusPlus_names, candidate_C_names,
+ m_sym_ctx);
+ ret = FindInSymbols(candidate_CPlusPlus_names, m_sym_ctx, missing_weak);
+ if (ret != LLDB_INVALID_ADDRESS)
+ return ret;
+
+ std::vector<SearchSpec> candidate_fallback_names;
+
+ CollectFallbackNames(candidate_fallback_names, candidate_C_names);
+ ret = FindInSymbols(candidate_fallback_names, m_sym_ctx, missing_weak);
+
+ return ret;
+}
+
+void IRExecutionUnit::GetStaticInitializers(
+ std::vector<lldb::addr_t> &static_initializers) {
+ if (llvm::GlobalVariable *global_ctors =
+ m_module->getNamedGlobal("llvm.global_ctors")) {
+ if (llvm::ConstantArray *ctor_array = llvm::dyn_cast<llvm::ConstantArray>(
+ global_ctors->getInitializer())) {
+ for (llvm::Use &ctor_use : ctor_array->operands()) {
+ if (llvm::ConstantStruct *ctor_struct =
+ llvm::dyn_cast<llvm::ConstantStruct>(ctor_use)) {
+ lldbassert(ctor_struct->getNumOperands() ==
+ 3); // this is standardized
+ if (llvm::Function *ctor_function =
+ llvm::dyn_cast<llvm::Function>(ctor_struct->getOperand(1))) {
+ ConstString ctor_function_name_cs(ctor_function->getName().str());
+
+ for (JittedFunction &jitted_function : m_jitted_functions) {
+ if (ctor_function_name_cs == jitted_function.m_name) {
+ if (jitted_function.m_remote_addr != LLDB_INVALID_ADDRESS) {
+ static_initializers.push_back(jitted_function.m_remote_addr);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+llvm::JITSymbol
+IRExecutionUnit::MemoryManager::findSymbol(const std::string &Name) {
+ bool missing_weak = false;
+ uint64_t addr = GetSymbolAddressAndPresence(Name, missing_weak);
+ // This is a weak symbol:
+ if (missing_weak)
+ return llvm::JITSymbol(addr,
+ llvm::JITSymbolFlags::Exported | llvm::JITSymbolFlags::Weak);
+ else
+ return llvm::JITSymbol(addr, llvm::JITSymbolFlags::Exported);
+}
+
+uint64_t
+IRExecutionUnit::MemoryManager::getSymbolAddress(const std::string &Name) {
+ bool missing_weak = false;
+ return GetSymbolAddressAndPresence(Name, missing_weak);
+}
+
+uint64_t
+IRExecutionUnit::MemoryManager::GetSymbolAddressAndPresence(
+ const std::string &Name, bool &missing_weak) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ ConstString name_cs(Name.c_str());
+
+ lldb::addr_t ret = m_parent.FindSymbol(name_cs, missing_weak);
+
+ if (ret == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf(
+ "IRExecutionUnit::getSymbolAddress(Name=\"%s\") = <not found>",
+ Name.c_str());
+
+ m_parent.ReportSymbolLookupError(name_cs);
+ return 0;
+ } else {
+ if (log)
+ log->Printf("IRExecutionUnit::getSymbolAddress(Name=\"%s\") = %" PRIx64,
+ Name.c_str(), ret);
+ return ret;
+ }
+}
+
+void *IRExecutionUnit::MemoryManager::getPointerToNamedFunction(
+ const std::string &Name, bool AbortOnFailure) {
+ return (void *)getSymbolAddress(Name);
+}
+
+lldb::addr_t
+IRExecutionUnit::GetRemoteAddressForLocal(lldb::addr_t local_address) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ for (AllocationRecord &record : m_records) {
+ if (local_address >= record.m_host_address &&
+ local_address < record.m_host_address + record.m_size) {
+ if (record.m_process_address == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ lldb::addr_t ret =
+ record.m_process_address + (local_address - record.m_host_address);
+
+ if (log) {
+ log->Printf(
+ "IRExecutionUnit::GetRemoteAddressForLocal() found 0x%" PRIx64
+ " in [0x%" PRIx64 "..0x%" PRIx64 "], and returned 0x%" PRIx64
+ " from [0x%" PRIx64 "..0x%" PRIx64 "].",
+ local_address, (uint64_t)record.m_host_address,
+ (uint64_t)record.m_host_address + (uint64_t)record.m_size, ret,
+ record.m_process_address, record.m_process_address + record.m_size);
+ }
+
+ return ret;
+ }
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+IRExecutionUnit::AddrRange
+IRExecutionUnit::GetRemoteRangeForLocal(lldb::addr_t local_address) {
+ for (AllocationRecord &record : m_records) {
+ if (local_address >= record.m_host_address &&
+ local_address < record.m_host_address + record.m_size) {
+ if (record.m_process_address == LLDB_INVALID_ADDRESS)
+ return AddrRange(0, 0);
+
+ return AddrRange(record.m_process_address, record.m_size);
+ }
+ }
+
+ return AddrRange(0, 0);
+}
+
+bool IRExecutionUnit::CommitOneAllocation(lldb::ProcessSP &process_sp,
+ Status &error,
+ AllocationRecord &record) {
+ if (record.m_process_address != LLDB_INVALID_ADDRESS) {
+ return true;
+ }
+
+ switch (record.m_sect_type) {
+ case lldb::eSectionTypeInvalid:
+ case lldb::eSectionTypeDWARFDebugAbbrev:
+ case lldb::eSectionTypeDWARFDebugAddr:
+ case lldb::eSectionTypeDWARFDebugAranges:
+ case lldb::eSectionTypeDWARFDebugCuIndex:
+ case lldb::eSectionTypeDWARFDebugFrame:
+ case lldb::eSectionTypeDWARFDebugInfo:
+ case lldb::eSectionTypeDWARFDebugLine:
+ case lldb::eSectionTypeDWARFDebugLoc:
+ case lldb::eSectionTypeDWARFDebugLocLists:
+ case lldb::eSectionTypeDWARFDebugMacInfo:
+ case lldb::eSectionTypeDWARFDebugPubNames:
+ case lldb::eSectionTypeDWARFDebugPubTypes:
+ case lldb::eSectionTypeDWARFDebugRanges:
+ case lldb::eSectionTypeDWARFDebugStr:
+ case lldb::eSectionTypeDWARFDebugStrOffsets:
+ case lldb::eSectionTypeDWARFAppleNames:
+ case lldb::eSectionTypeDWARFAppleTypes:
+ case lldb::eSectionTypeDWARFAppleNamespaces:
+ case lldb::eSectionTypeDWARFAppleObjC:
+ case lldb::eSectionTypeDWARFGNUDebugAltLink:
+ error.Clear();
+ break;
+ default:
+ const bool zero_memory = false;
+ record.m_process_address =
+ Malloc(record.m_size, record.m_alignment, record.m_permissions,
+ eAllocationPolicyProcessOnly, zero_memory, error);
+ break;
+ }
+
+ return error.Success();
+}
+
+bool IRExecutionUnit::CommitAllocations(lldb::ProcessSP &process_sp) {
+ bool ret = true;
+
+ lldb_private::Status err;
+
+ for (AllocationRecord &record : m_records) {
+ ret = CommitOneAllocation(process_sp, err, record);
+
+ if (!ret) {
+ break;
+ }
+ }
+
+ if (!ret) {
+ for (AllocationRecord &record : m_records) {
+ if (record.m_process_address != LLDB_INVALID_ADDRESS) {
+ Free(record.m_process_address, err);
+ record.m_process_address = LLDB_INVALID_ADDRESS;
+ }
+ }
+ }
+
+ return ret;
+}
+
+void IRExecutionUnit::ReportAllocations(llvm::ExecutionEngine &engine) {
+ m_reported_allocations = true;
+
+ for (AllocationRecord &record : m_records) {
+ if (record.m_process_address == LLDB_INVALID_ADDRESS)
+ continue;
+
+ if (record.m_section_id == eSectionIDInvalid)
+ continue;
+
+ engine.mapSectionAddress((void *)record.m_host_address,
+ record.m_process_address);
+ }
+
+ // Trigger re-application of relocations.
+ engine.finalizeObject();
+}
+
+bool IRExecutionUnit::WriteData(lldb::ProcessSP &process_sp) {
+ bool wrote_something = false;
+ for (AllocationRecord &record : m_records) {
+ if (record.m_process_address != LLDB_INVALID_ADDRESS) {
+ lldb_private::Status err;
+ WriteMemory(record.m_process_address, (uint8_t *)record.m_host_address,
+ record.m_size, err);
+ if (err.Success())
+ wrote_something = true;
+ }
+ }
+ return wrote_something;
+}
+
+void IRExecutionUnit::AllocationRecord::dump(Log *log) {
+ if (!log)
+ return;
+
+ log->Printf("[0x%llx+0x%llx]->0x%llx (alignment %d, section ID %d, name %s)",
+ (unsigned long long)m_host_address, (unsigned long long)m_size,
+ (unsigned long long)m_process_address, (unsigned)m_alignment,
+ (unsigned)m_section_id, m_name.c_str());
+}
+
+lldb::ByteOrder IRExecutionUnit::GetByteOrder() const {
+ ExecutionContext exe_ctx(GetBestExecutionContextScope());
+ return exe_ctx.GetByteOrder();
+}
+
+uint32_t IRExecutionUnit::GetAddressByteSize() const {
+ ExecutionContext exe_ctx(GetBestExecutionContextScope());
+ return exe_ctx.GetAddressByteSize();
+}
+
+void IRExecutionUnit::PopulateSymtab(lldb_private::ObjectFile *obj_file,
+ lldb_private::Symtab &symtab) {
+ // No symbols yet...
+}
+
+void IRExecutionUnit::PopulateSectionList(
+ lldb_private::ObjectFile *obj_file,
+ lldb_private::SectionList &section_list) {
+ for (AllocationRecord &record : m_records) {
+ if (record.m_size > 0) {
+ lldb::SectionSP section_sp(new lldb_private::Section(
+ obj_file->GetModule(), obj_file, record.m_section_id,
+ ConstString(record.m_name), record.m_sect_type,
+ record.m_process_address, record.m_size,
+ record.m_host_address, // file_offset (which is the host address for
+ // the data)
+ record.m_size, // file_size
+ 0,
+ record.m_permissions)); // flags
+ section_list.AddSection(section_sp);
+ }
+ }
+}
+
+ArchSpec IRExecutionUnit::GetArchitecture() {
+ ExecutionContext exe_ctx(GetBestExecutionContextScope());
+ if(Target *target = exe_ctx.GetTargetPtr())
+ return target->GetArchitecture();
+ return ArchSpec();
+}
+
+lldb::ModuleSP IRExecutionUnit::GetJITModule() {
+ ExecutionContext exe_ctx(GetBestExecutionContextScope());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (!target)
+ return nullptr;
+
+ auto Delegate = std::static_pointer_cast<lldb_private::ObjectFileJITDelegate>(
+ shared_from_this());
+
+ lldb::ModuleSP jit_module_sp =
+ lldb_private::Module::CreateModuleFromObjectFile<ObjectFileJIT>(Delegate);
+ if (!jit_module_sp)
+ return nullptr;
+
+ bool changed = false;
+ jit_module_sp->SetLoadAddress(*target, 0, true, changed);
+ return jit_module_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Expression/IRInterpreter.cpp b/contrib/llvm-project/lldb/source/Expression/IRInterpreter.cpp
new file mode 100644
index 000000000000..5a9814d15362
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/IRInterpreter.cpp
@@ -0,0 +1,1698 @@
+//===-- IRInterpreter.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Expression/IRInterpreter.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Expression/IRMemoryMap.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallFunctionUsingABI.h"
+
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Operator.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <map>
+
+using namespace llvm;
+
+static std::string PrintValue(const Value *value, bool truncate = false) {
+ std::string s;
+ raw_string_ostream rso(s);
+ value->print(rso);
+ rso.flush();
+ if (truncate)
+ s.resize(s.length() - 1);
+
+ size_t offset;
+ while ((offset = s.find('\n')) != s.npos)
+ s.erase(offset, 1);
+ while (s[0] == ' ' || s[0] == '\t')
+ s.erase(0, 1);
+
+ return s;
+}
+
+static std::string PrintType(const Type *type, bool truncate = false) {
+ std::string s;
+ raw_string_ostream rso(s);
+ type->print(rso);
+ rso.flush();
+ if (truncate)
+ s.resize(s.length() - 1);
+ return s;
+}
+
+static bool CanIgnoreCall(const CallInst *call) {
+ const llvm::Function *called_function = call->getCalledFunction();
+
+ if (!called_function)
+ return false;
+
+ if (called_function->isIntrinsic()) {
+ switch (called_function->getIntrinsicID()) {
+ default:
+ break;
+ case llvm::Intrinsic::dbg_declare:
+ case llvm::Intrinsic::dbg_value:
+ return true;
+ }
+ }
+
+ return false;
+}
+
+class InterpreterStackFrame {
+public:
+ typedef std::map<const Value *, lldb::addr_t> ValueMap;
+
+ ValueMap m_values;
+ DataLayout &m_target_data;
+ lldb_private::IRExecutionUnit &m_execution_unit;
+ const BasicBlock *m_bb;
+ const BasicBlock *m_prev_bb;
+ BasicBlock::const_iterator m_ii;
+ BasicBlock::const_iterator m_ie;
+
+ lldb::addr_t m_frame_process_address;
+ size_t m_frame_size;
+ lldb::addr_t m_stack_pointer;
+
+ lldb::ByteOrder m_byte_order;
+ size_t m_addr_byte_size;
+
+ InterpreterStackFrame(DataLayout &target_data,
+ lldb_private::IRExecutionUnit &execution_unit,
+ lldb::addr_t stack_frame_bottom,
+ lldb::addr_t stack_frame_top)
+ : m_target_data(target_data), m_execution_unit(execution_unit),
+ m_bb(nullptr), m_prev_bb(nullptr) {
+ m_byte_order = (target_data.isLittleEndian() ? lldb::eByteOrderLittle
+ : lldb::eByteOrderBig);
+ m_addr_byte_size = (target_data.getPointerSize(0));
+
+ m_frame_process_address = stack_frame_bottom;
+ m_frame_size = stack_frame_top - stack_frame_bottom;
+ m_stack_pointer = stack_frame_top;
+ }
+
+ ~InterpreterStackFrame() {}
+
+ void Jump(const BasicBlock *bb) {
+ m_prev_bb = m_bb;
+ m_bb = bb;
+ m_ii = m_bb->begin();
+ m_ie = m_bb->end();
+ }
+
+ std::string SummarizeValue(const Value *value) {
+ lldb_private::StreamString ss;
+
+ ss.Printf("%s", PrintValue(value).c_str());
+
+ ValueMap::iterator i = m_values.find(value);
+
+ if (i != m_values.end()) {
+ lldb::addr_t addr = i->second;
+
+ ss.Printf(" 0x%llx", (unsigned long long)addr);
+ }
+
+ return ss.GetString();
+ }
+
+ bool AssignToMatchType(lldb_private::Scalar &scalar, uint64_t u64value,
+ Type *type) {
+ size_t type_size = m_target_data.getTypeStoreSize(type);
+
+ if (type_size > 8)
+ return false;
+
+ if (type_size != 1)
+ type_size = PowerOf2Ceil(type_size);
+
+ scalar = llvm::APInt(type_size*8, u64value);
+ return true;
+ }
+
+ bool EvaluateValue(lldb_private::Scalar &scalar, const Value *value,
+ Module &module) {
+ const Constant *constant = dyn_cast<Constant>(value);
+
+ if (constant) {
+ APInt value_apint;
+
+ if (!ResolveConstantValue(value_apint, constant))
+ return false;
+
+ return AssignToMatchType(scalar, value_apint.getLimitedValue(),
+ value->getType());
+ } else {
+ lldb::addr_t process_address = ResolveValue(value, module);
+ size_t value_size = m_target_data.getTypeStoreSize(value->getType());
+
+ lldb_private::DataExtractor value_extractor;
+ lldb_private::Status extract_error;
+
+ m_execution_unit.GetMemoryData(value_extractor, process_address,
+ value_size, extract_error);
+
+ if (!extract_error.Success())
+ return false;
+
+ lldb::offset_t offset = 0;
+ if (value_size <= 8) {
+ uint64_t u64value = value_extractor.GetMaxU64(&offset, value_size);
+ return AssignToMatchType(scalar, u64value, value->getType());
+ }
+ }
+
+ return false;
+ }
+
+ bool AssignValue(const Value *value, lldb_private::Scalar &scalar,
+ Module &module) {
+ lldb::addr_t process_address = ResolveValue(value, module);
+
+ if (process_address == LLDB_INVALID_ADDRESS)
+ return false;
+
+ lldb_private::Scalar cast_scalar;
+
+ if (!AssignToMatchType(cast_scalar, scalar.ULongLong(), value->getType()))
+ return false;
+
+ size_t value_byte_size = m_target_data.getTypeStoreSize(value->getType());
+
+ lldb_private::DataBufferHeap buf(value_byte_size, 0);
+
+ lldb_private::Status get_data_error;
+
+ if (!cast_scalar.GetAsMemoryData(buf.GetBytes(), buf.GetByteSize(),
+ m_byte_order, get_data_error))
+ return false;
+
+ lldb_private::Status write_error;
+
+ m_execution_unit.WriteMemory(process_address, buf.GetBytes(),
+ buf.GetByteSize(), write_error);
+
+ return write_error.Success();
+ }
+
+ bool ResolveConstantValue(APInt &value, const Constant *constant) {
+ switch (constant->getValueID()) {
+ default:
+ break;
+ case Value::FunctionVal:
+ if (const Function *constant_func = dyn_cast<Function>(constant)) {
+ lldb_private::ConstString name(constant_func->getName());
+ bool missing_weak = false;
+ lldb::addr_t addr = m_execution_unit.FindSymbol(name, missing_weak);
+ if (addr == LLDB_INVALID_ADDRESS || missing_weak)
+ return false;
+ value = APInt(m_target_data.getPointerSizeInBits(), addr);
+ return true;
+ }
+ break;
+ case Value::ConstantIntVal:
+ if (const ConstantInt *constant_int = dyn_cast<ConstantInt>(constant)) {
+ value = constant_int->getValue();
+ return true;
+ }
+ break;
+ case Value::ConstantFPVal:
+ if (const ConstantFP *constant_fp = dyn_cast<ConstantFP>(constant)) {
+ value = constant_fp->getValueAPF().bitcastToAPInt();
+ return true;
+ }
+ break;
+ case Value::ConstantExprVal:
+ if (const ConstantExpr *constant_expr =
+ dyn_cast<ConstantExpr>(constant)) {
+ switch (constant_expr->getOpcode()) {
+ default:
+ return false;
+ case Instruction::IntToPtr:
+ case Instruction::PtrToInt:
+ case Instruction::BitCast:
+ return ResolveConstantValue(value, constant_expr->getOperand(0));
+ case Instruction::GetElementPtr: {
+ ConstantExpr::const_op_iterator op_cursor = constant_expr->op_begin();
+ ConstantExpr::const_op_iterator op_end = constant_expr->op_end();
+
+ Constant *base = dyn_cast<Constant>(*op_cursor);
+
+ if (!base)
+ return false;
+
+ if (!ResolveConstantValue(value, base))
+ return false;
+
+ op_cursor++;
+
+ if (op_cursor == op_end)
+ return true; // no offset to apply!
+
+ SmallVector<Value *, 8> indices(op_cursor, op_end);
+
+ Type *src_elem_ty =
+ cast<GEPOperator>(constant_expr)->getSourceElementType();
+ uint64_t offset =
+ m_target_data.getIndexedOffsetInType(src_elem_ty, indices);
+
+ const bool is_signed = true;
+ value += APInt(value.getBitWidth(), offset, is_signed);
+
+ return true;
+ }
+ }
+ }
+ break;
+ case Value::ConstantPointerNullVal:
+ if (isa<ConstantPointerNull>(constant)) {
+ value = APInt(m_target_data.getPointerSizeInBits(), 0);
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ bool MakeArgument(const Argument *value, uint64_t address) {
+ lldb::addr_t data_address = Malloc(value->getType());
+
+ if (data_address == LLDB_INVALID_ADDRESS)
+ return false;
+
+ lldb_private::Status write_error;
+
+ m_execution_unit.WritePointerToMemory(data_address, address, write_error);
+
+ if (!write_error.Success()) {
+ lldb_private::Status free_error;
+ m_execution_unit.Free(data_address, free_error);
+ return false;
+ }
+
+ m_values[value] = data_address;
+
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ log->Printf("Made an allocation for argument %s",
+ PrintValue(value).c_str());
+ log->Printf(" Data region : %llx", (unsigned long long)address);
+ log->Printf(" Ref region : %llx", (unsigned long long)data_address);
+ }
+
+ return true;
+ }
+
+ bool ResolveConstant(lldb::addr_t process_address, const Constant *constant) {
+ APInt resolved_value;
+
+ if (!ResolveConstantValue(resolved_value, constant))
+ return false;
+
+ size_t constant_size = m_target_data.getTypeStoreSize(constant->getType());
+ lldb_private::DataBufferHeap buf(constant_size, 0);
+
+ lldb_private::Status get_data_error;
+
+ lldb_private::Scalar resolved_scalar(
+ resolved_value.zextOrTrunc(llvm::NextPowerOf2(constant_size) * 8));
+ if (!resolved_scalar.GetAsMemoryData(buf.GetBytes(), buf.GetByteSize(),
+ m_byte_order, get_data_error))
+ return false;
+
+ lldb_private::Status write_error;
+
+ m_execution_unit.WriteMemory(process_address, buf.GetBytes(),
+ buf.GetByteSize(), write_error);
+
+ return write_error.Success();
+ }
+
+ lldb::addr_t Malloc(size_t size, uint8_t byte_alignment) {
+ lldb::addr_t ret = m_stack_pointer;
+
+ ret -= size;
+ ret -= (ret % byte_alignment);
+
+ if (ret < m_frame_process_address)
+ return LLDB_INVALID_ADDRESS;
+
+ m_stack_pointer = ret;
+ return ret;
+ }
+
+ lldb::addr_t Malloc(llvm::Type *type) {
+ lldb_private::Status alloc_error;
+
+ return Malloc(m_target_data.getTypeAllocSize(type),
+ m_target_data.getPrefTypeAlignment(type));
+ }
+
+ std::string PrintData(lldb::addr_t addr, llvm::Type *type) {
+ size_t length = m_target_data.getTypeStoreSize(type);
+
+ lldb_private::DataBufferHeap buf(length, 0);
+
+ lldb_private::Status read_error;
+
+ m_execution_unit.ReadMemory(buf.GetBytes(), addr, length, read_error);
+
+ if (!read_error.Success())
+ return std::string("<couldn't read data>");
+
+ lldb_private::StreamString ss;
+
+ for (size_t i = 0; i < length; i++) {
+ if ((!(i & 0xf)) && i)
+ ss.Printf("%02hhx - ", buf.GetBytes()[i]);
+ else
+ ss.Printf("%02hhx ", buf.GetBytes()[i]);
+ }
+
+ return ss.GetString();
+ }
+
+ lldb::addr_t ResolveValue(const Value *value, Module &module) {
+ ValueMap::iterator i = m_values.find(value);
+
+ if (i != m_values.end())
+ return i->second;
+
+ // Fall back and allocate space [allocation type Alloca]
+
+ lldb::addr_t data_address = Malloc(value->getType());
+
+ if (const Constant *constant = dyn_cast<Constant>(value)) {
+ if (!ResolveConstant(data_address, constant)) {
+ lldb_private::Status free_error;
+ m_execution_unit.Free(data_address, free_error);
+ return LLDB_INVALID_ADDRESS;
+ }
+ }
+
+ m_values[value] = data_address;
+ return data_address;
+ }
+};
+
+static const char *unsupported_opcode_error =
+ "Interpreter doesn't handle one of the expression's opcodes";
+static const char *unsupported_operand_error =
+ "Interpreter doesn't handle one of the expression's operands";
+// static const char *interpreter_initialization_error = "Interpreter couldn't
+// be initialized";
+static const char *interpreter_internal_error =
+ "Interpreter encountered an internal error";
+static const char *bad_value_error =
+ "Interpreter couldn't resolve a value during execution";
+static const char *memory_allocation_error =
+ "Interpreter couldn't allocate memory";
+static const char *memory_write_error = "Interpreter couldn't write to memory";
+static const char *memory_read_error = "Interpreter couldn't read from memory";
+static const char *infinite_loop_error = "Interpreter ran for too many cycles";
+// static const char *bad_result_error = "Result of expression
+// is in bad memory";
+static const char *too_many_functions_error =
+ "Interpreter doesn't handle modules with multiple function bodies.";
+
+static bool CanResolveConstant(llvm::Constant *constant) {
+ switch (constant->getValueID()) {
+ default:
+ return false;
+ case Value::ConstantIntVal:
+ case Value::ConstantFPVal:
+ case Value::FunctionVal:
+ return true;
+ case Value::ConstantExprVal:
+ if (const ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(constant)) {
+ switch (constant_expr->getOpcode()) {
+ default:
+ return false;
+ case Instruction::IntToPtr:
+ case Instruction::PtrToInt:
+ case Instruction::BitCast:
+ return CanResolveConstant(constant_expr->getOperand(0));
+ case Instruction::GetElementPtr: {
+ ConstantExpr::const_op_iterator op_cursor = constant_expr->op_begin();
+ Constant *base = dyn_cast<Constant>(*op_cursor);
+ if (!base)
+ return false;
+
+ return CanResolveConstant(base);
+ }
+ }
+ } else {
+ return false;
+ }
+ case Value::ConstantPointerNullVal:
+ return true;
+ }
+}
+
+bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
+ lldb_private::Status &error,
+ const bool support_function_calls) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ bool saw_function_with_body = false;
+
+ for (Module::iterator fi = module.begin(), fe = module.end(); fi != fe;
+ ++fi) {
+ if (fi->begin() != fi->end()) {
+ if (saw_function_with_body) {
+ if (log)
+ log->Printf("More than one function in the module has a body");
+ error.SetErrorToGenericError();
+ error.SetErrorString(too_many_functions_error);
+ return false;
+ }
+ saw_function_with_body = true;
+ }
+ }
+
+ for (Function::iterator bbi = function.begin(), bbe = function.end();
+ bbi != bbe; ++bbi) {
+ for (BasicBlock::iterator ii = bbi->begin(), ie = bbi->end(); ii != ie;
+ ++ii) {
+ switch (ii->getOpcode()) {
+ default: {
+ if (log)
+ log->Printf("Unsupported instruction: %s", PrintValue(&*ii).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(unsupported_opcode_error);
+ return false;
+ }
+ case Instruction::Add:
+ case Instruction::Alloca:
+ case Instruction::BitCast:
+ case Instruction::Br:
+ case Instruction::PHI:
+ break;
+ case Instruction::Call: {
+ CallInst *call_inst = dyn_cast<CallInst>(ii);
+
+ if (!call_inst) {
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ if (!CanIgnoreCall(call_inst) && !support_function_calls) {
+ if (log)
+ log->Printf("Unsupported instruction: %s",
+ PrintValue(&*ii).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(unsupported_opcode_error);
+ return false;
+ }
+ } break;
+ case Instruction::GetElementPtr:
+ break;
+ case Instruction::ICmp: {
+ ICmpInst *icmp_inst = dyn_cast<ICmpInst>(ii);
+
+ if (!icmp_inst) {
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ switch (icmp_inst->getPredicate()) {
+ default: {
+ if (log)
+ log->Printf("Unsupported ICmp predicate: %s",
+ PrintValue(&*ii).c_str());
+
+ error.SetErrorToGenericError();
+ error.SetErrorString(unsupported_opcode_error);
+ return false;
+ }
+ case CmpInst::ICMP_EQ:
+ case CmpInst::ICMP_NE:
+ case CmpInst::ICMP_UGT:
+ case CmpInst::ICMP_UGE:
+ case CmpInst::ICMP_ULT:
+ case CmpInst::ICMP_ULE:
+ case CmpInst::ICMP_SGT:
+ case CmpInst::ICMP_SGE:
+ case CmpInst::ICMP_SLT:
+ case CmpInst::ICMP_SLE:
+ break;
+ }
+ } break;
+ case Instruction::And:
+ case Instruction::AShr:
+ case Instruction::IntToPtr:
+ case Instruction::PtrToInt:
+ case Instruction::Load:
+ case Instruction::LShr:
+ case Instruction::Mul:
+ case Instruction::Or:
+ case Instruction::Ret:
+ case Instruction::SDiv:
+ case Instruction::SExt:
+ case Instruction::Shl:
+ case Instruction::SRem:
+ case Instruction::Store:
+ case Instruction::Sub:
+ case Instruction::Trunc:
+ case Instruction::UDiv:
+ case Instruction::URem:
+ case Instruction::Xor:
+ case Instruction::ZExt:
+ break;
+ }
+
+ for (int oi = 0, oe = ii->getNumOperands(); oi != oe; ++oi) {
+ Value *operand = ii->getOperand(oi);
+ Type *operand_type = operand->getType();
+
+ switch (operand_type->getTypeID()) {
+ default:
+ break;
+ case Type::VectorTyID: {
+ if (log)
+ log->Printf("Unsupported operand type: %s",
+ PrintType(operand_type).c_str());
+ error.SetErrorString(unsupported_operand_error);
+ return false;
+ }
+ }
+
+ // The IR interpreter currently doesn't know about
+ // 128-bit integers. As they're not that frequent,
+ // we can just fall back to the JIT rather than
+ // choking.
+ if (operand_type->getPrimitiveSizeInBits() > 64) {
+ if (log)
+ log->Printf("Unsupported operand type: %s",
+ PrintType(operand_type).c_str());
+ error.SetErrorString(unsupported_operand_error);
+ return false;
+ }
+
+ if (Constant *constant = llvm::dyn_cast<Constant>(operand)) {
+ if (!CanResolveConstant(constant)) {
+ if (log)
+ log->Printf("Unsupported constant: %s",
+ PrintValue(constant).c_str());
+ error.SetErrorString(unsupported_operand_error);
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
+ llvm::ArrayRef<lldb::addr_t> args,
+ lldb_private::IRExecutionUnit &execution_unit,
+ lldb_private::Status &error,
+ lldb::addr_t stack_frame_bottom,
+ lldb::addr_t stack_frame_top,
+ lldb_private::ExecutionContext &exe_ctx) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ module.print(oss, nullptr);
+
+ oss.flush();
+
+ log->Printf("Module as passed in to IRInterpreter::Interpret: \n\"%s\"",
+ s.c_str());
+ }
+
+ DataLayout data_layout(&module);
+
+ InterpreterStackFrame frame(data_layout, execution_unit, stack_frame_bottom,
+ stack_frame_top);
+
+ if (frame.m_frame_process_address == LLDB_INVALID_ADDRESS) {
+ error.SetErrorString("Couldn't allocate stack frame");
+ }
+
+ int arg_index = 0;
+
+ for (llvm::Function::arg_iterator ai = function.arg_begin(),
+ ae = function.arg_end();
+ ai != ae; ++ai, ++arg_index) {
+ if (args.size() <= static_cast<size_t>(arg_index)) {
+ error.SetErrorString("Not enough arguments passed in to function");
+ return false;
+ }
+
+ lldb::addr_t ptr = args[arg_index];
+
+ frame.MakeArgument(&*ai, ptr);
+ }
+
+ uint32_t num_insts = 0;
+
+ frame.Jump(&function.front());
+
+ while (frame.m_ii != frame.m_ie && (++num_insts < 4096)) {
+ const Instruction *inst = &*frame.m_ii;
+
+ if (log)
+ log->Printf("Interpreting %s", PrintValue(inst).c_str());
+
+ switch (inst->getOpcode()) {
+ default:
+ break;
+
+ case Instruction::Add:
+ case Instruction::Sub:
+ case Instruction::Mul:
+ case Instruction::SDiv:
+ case Instruction::UDiv:
+ case Instruction::SRem:
+ case Instruction::URem:
+ case Instruction::Shl:
+ case Instruction::LShr:
+ case Instruction::AShr:
+ case Instruction::And:
+ case Instruction::Or:
+ case Instruction::Xor: {
+ const BinaryOperator *bin_op = dyn_cast<BinaryOperator>(inst);
+
+ if (!bin_op) {
+ if (log)
+ log->Printf(
+ "getOpcode() returns %s, but instruction is not a BinaryOperator",
+ inst->getOpcodeName());
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ Value *lhs = inst->getOperand(0);
+ Value *rhs = inst->getOperand(1);
+
+ lldb_private::Scalar L;
+ lldb_private::Scalar R;
+
+ if (!frame.EvaluateValue(L, lhs, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(lhs).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ if (!frame.EvaluateValue(R, rhs, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(rhs).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ lldb_private::Scalar result;
+
+ switch (inst->getOpcode()) {
+ default:
+ break;
+ case Instruction::Add:
+ result = L + R;
+ break;
+ case Instruction::Mul:
+ result = L * R;
+ break;
+ case Instruction::Sub:
+ result = L - R;
+ break;
+ case Instruction::SDiv:
+ L.MakeSigned();
+ R.MakeSigned();
+ result = L / R;
+ break;
+ case Instruction::UDiv:
+ L.MakeUnsigned();
+ R.MakeUnsigned();
+ result = L / R;
+ break;
+ case Instruction::SRem:
+ L.MakeSigned();
+ R.MakeSigned();
+ result = L % R;
+ break;
+ case Instruction::URem:
+ L.MakeUnsigned();
+ R.MakeUnsigned();
+ result = L % R;
+ break;
+ case Instruction::Shl:
+ result = L << R;
+ break;
+ case Instruction::AShr:
+ result = L >> R;
+ break;
+ case Instruction::LShr:
+ result = L;
+ result.ShiftRightLogical(R);
+ break;
+ case Instruction::And:
+ result = L & R;
+ break;
+ case Instruction::Or:
+ result = L | R;
+ break;
+ case Instruction::Xor:
+ result = L ^ R;
+ break;
+ }
+
+ frame.AssignValue(inst, result, module);
+
+ if (log) {
+ log->Printf("Interpreted a %s", inst->getOpcodeName());
+ log->Printf(" L : %s", frame.SummarizeValue(lhs).c_str());
+ log->Printf(" R : %s", frame.SummarizeValue(rhs).c_str());
+ log->Printf(" = : %s", frame.SummarizeValue(inst).c_str());
+ }
+ } break;
+ case Instruction::Alloca: {
+ const AllocaInst *alloca_inst = dyn_cast<AllocaInst>(inst);
+
+ if (!alloca_inst) {
+ if (log)
+ log->Printf("getOpcode() returns Alloca, but instruction is not an "
+ "AllocaInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ if (alloca_inst->isArrayAllocation()) {
+ if (log)
+ log->Printf(
+ "AllocaInsts are not handled if isArrayAllocation() is true");
+ error.SetErrorToGenericError();
+ error.SetErrorString(unsupported_opcode_error);
+ return false;
+ }
+
+ // The semantics of Alloca are:
+ // Create a region R of virtual memory of type T, backed by a data
+ // buffer
+ // Create a region P of virtual memory of type T*, backed by a data
+ // buffer
+ // Write the virtual address of R into P
+
+ Type *T = alloca_inst->getAllocatedType();
+ Type *Tptr = alloca_inst->getType();
+
+ lldb::addr_t R = frame.Malloc(T);
+
+ if (R == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("Couldn't allocate memory for an AllocaInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(memory_allocation_error);
+ return false;
+ }
+
+ lldb::addr_t P = frame.Malloc(Tptr);
+
+ if (P == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("Couldn't allocate the result pointer for an AllocaInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(memory_allocation_error);
+ return false;
+ }
+
+ lldb_private::Status write_error;
+
+ execution_unit.WritePointerToMemory(P, R, write_error);
+
+ if (!write_error.Success()) {
+ if (log)
+ log->Printf("Couldn't write the result pointer for an AllocaInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(memory_write_error);
+ lldb_private::Status free_error;
+ execution_unit.Free(P, free_error);
+ execution_unit.Free(R, free_error);
+ return false;
+ }
+
+ frame.m_values[alloca_inst] = P;
+
+ if (log) {
+ log->Printf("Interpreted an AllocaInst");
+ log->Printf(" R : 0x%" PRIx64, R);
+ log->Printf(" P : 0x%" PRIx64, P);
+ }
+ } break;
+ case Instruction::BitCast:
+ case Instruction::ZExt: {
+ const CastInst *cast_inst = dyn_cast<CastInst>(inst);
+
+ if (!cast_inst) {
+ if (log)
+ log->Printf(
+ "getOpcode() returns %s, but instruction is not a BitCastInst",
+ cast_inst->getOpcodeName());
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ Value *source = cast_inst->getOperand(0);
+
+ lldb_private::Scalar S;
+
+ if (!frame.EvaluateValue(S, source, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(source).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ frame.AssignValue(inst, S, module);
+ } break;
+ case Instruction::SExt: {
+ const CastInst *cast_inst = dyn_cast<CastInst>(inst);
+
+ if (!cast_inst) {
+ if (log)
+ log->Printf(
+ "getOpcode() returns %s, but instruction is not a BitCastInst",
+ cast_inst->getOpcodeName());
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ Value *source = cast_inst->getOperand(0);
+
+ lldb_private::Scalar S;
+
+ if (!frame.EvaluateValue(S, source, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(source).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ S.MakeSigned();
+
+ lldb_private::Scalar S_signextend(S.SLongLong());
+
+ frame.AssignValue(inst, S_signextend, module);
+ } break;
+ case Instruction::Br: {
+ const BranchInst *br_inst = dyn_cast<BranchInst>(inst);
+
+ if (!br_inst) {
+ if (log)
+ log->Printf(
+ "getOpcode() returns Br, but instruction is not a BranchInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ if (br_inst->isConditional()) {
+ Value *condition = br_inst->getCondition();
+
+ lldb_private::Scalar C;
+
+ if (!frame.EvaluateValue(C, condition, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(condition).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ if (!C.IsZero())
+ frame.Jump(br_inst->getSuccessor(0));
+ else
+ frame.Jump(br_inst->getSuccessor(1));
+
+ if (log) {
+ log->Printf("Interpreted a BrInst with a condition");
+ log->Printf(" cond : %s", frame.SummarizeValue(condition).c_str());
+ }
+ } else {
+ frame.Jump(br_inst->getSuccessor(0));
+
+ if (log) {
+ log->Printf("Interpreted a BrInst with no condition");
+ }
+ }
+ }
+ continue;
+ case Instruction::PHI: {
+ const PHINode *phi_inst = dyn_cast<PHINode>(inst);
+
+ if (!phi_inst) {
+ if (log)
+ log->Printf(
+ "getOpcode() returns PHI, but instruction is not a PHINode");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+ if (!frame.m_prev_bb) {
+ if (log)
+ log->Printf("Encountered PHI node without having jumped from another "
+ "basic block");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ Value *value = phi_inst->getIncomingValueForBlock(frame.m_prev_bb);
+ lldb_private::Scalar result;
+ if (!frame.EvaluateValue(result, value, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(value).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+ frame.AssignValue(inst, result, module);
+
+ if (log) {
+ log->Printf("Interpreted a %s", inst->getOpcodeName());
+ log->Printf(" Incoming value : %s",
+ frame.SummarizeValue(value).c_str());
+ }
+ } break;
+ case Instruction::GetElementPtr: {
+ const GetElementPtrInst *gep_inst = dyn_cast<GetElementPtrInst>(inst);
+
+ if (!gep_inst) {
+ if (log)
+ log->Printf("getOpcode() returns GetElementPtr, but instruction is "
+ "not a GetElementPtrInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ const Value *pointer_operand = gep_inst->getPointerOperand();
+ Type *src_elem_ty = gep_inst->getSourceElementType();
+
+ lldb_private::Scalar P;
+
+ if (!frame.EvaluateValue(P, pointer_operand, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s",
+ PrintValue(pointer_operand).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ typedef SmallVector<Value *, 8> IndexVector;
+ typedef IndexVector::iterator IndexIterator;
+
+ SmallVector<Value *, 8> indices(gep_inst->idx_begin(),
+ gep_inst->idx_end());
+
+ SmallVector<Value *, 8> const_indices;
+
+ for (IndexIterator ii = indices.begin(), ie = indices.end(); ii != ie;
+ ++ii) {
+ ConstantInt *constant_index = dyn_cast<ConstantInt>(*ii);
+
+ if (!constant_index) {
+ lldb_private::Scalar I;
+
+ if (!frame.EvaluateValue(I, *ii, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(*ii).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ if (log)
+ log->Printf("Evaluated constant index %s as %llu",
+ PrintValue(*ii).c_str(),
+ I.ULongLong(LLDB_INVALID_ADDRESS));
+
+ constant_index = cast<ConstantInt>(ConstantInt::get(
+ (*ii)->getType(), I.ULongLong(LLDB_INVALID_ADDRESS)));
+ }
+
+ const_indices.push_back(constant_index);
+ }
+
+ uint64_t offset =
+ data_layout.getIndexedOffsetInType(src_elem_ty, const_indices);
+
+ lldb_private::Scalar Poffset = P + offset;
+
+ frame.AssignValue(inst, Poffset, module);
+
+ if (log) {
+ log->Printf("Interpreted a GetElementPtrInst");
+ log->Printf(" P : %s",
+ frame.SummarizeValue(pointer_operand).c_str());
+ log->Printf(" Poffset : %s", frame.SummarizeValue(inst).c_str());
+ }
+ } break;
+ case Instruction::ICmp: {
+ const ICmpInst *icmp_inst = dyn_cast<ICmpInst>(inst);
+
+ if (!icmp_inst) {
+ if (log)
+ log->Printf(
+ "getOpcode() returns ICmp, but instruction is not an ICmpInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ CmpInst::Predicate predicate = icmp_inst->getPredicate();
+
+ Value *lhs = inst->getOperand(0);
+ Value *rhs = inst->getOperand(1);
+
+ lldb_private::Scalar L;
+ lldb_private::Scalar R;
+
+ if (!frame.EvaluateValue(L, lhs, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(lhs).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ if (!frame.EvaluateValue(R, rhs, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(rhs).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ lldb_private::Scalar result;
+
+ switch (predicate) {
+ default:
+ return false;
+ case CmpInst::ICMP_EQ:
+ result = (L == R);
+ break;
+ case CmpInst::ICMP_NE:
+ result = (L != R);
+ break;
+ case CmpInst::ICMP_UGT:
+ L.MakeUnsigned();
+ R.MakeUnsigned();
+ result = (L > R);
+ break;
+ case CmpInst::ICMP_UGE:
+ L.MakeUnsigned();
+ R.MakeUnsigned();
+ result = (L >= R);
+ break;
+ case CmpInst::ICMP_ULT:
+ L.MakeUnsigned();
+ R.MakeUnsigned();
+ result = (L < R);
+ break;
+ case CmpInst::ICMP_ULE:
+ L.MakeUnsigned();
+ R.MakeUnsigned();
+ result = (L <= R);
+ break;
+ case CmpInst::ICMP_SGT:
+ L.MakeSigned();
+ R.MakeSigned();
+ result = (L > R);
+ break;
+ case CmpInst::ICMP_SGE:
+ L.MakeSigned();
+ R.MakeSigned();
+ result = (L >= R);
+ break;
+ case CmpInst::ICMP_SLT:
+ L.MakeSigned();
+ R.MakeSigned();
+ result = (L < R);
+ break;
+ case CmpInst::ICMP_SLE:
+ L.MakeSigned();
+ R.MakeSigned();
+ result = (L <= R);
+ break;
+ }
+
+ frame.AssignValue(inst, result, module);
+
+ if (log) {
+ log->Printf("Interpreted an ICmpInst");
+ log->Printf(" L : %s", frame.SummarizeValue(lhs).c_str());
+ log->Printf(" R : %s", frame.SummarizeValue(rhs).c_str());
+ log->Printf(" = : %s", frame.SummarizeValue(inst).c_str());
+ }
+ } break;
+ case Instruction::IntToPtr: {
+ const IntToPtrInst *int_to_ptr_inst = dyn_cast<IntToPtrInst>(inst);
+
+ if (!int_to_ptr_inst) {
+ if (log)
+ log->Printf("getOpcode() returns IntToPtr, but instruction is not an "
+ "IntToPtrInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ Value *src_operand = int_to_ptr_inst->getOperand(0);
+
+ lldb_private::Scalar I;
+
+ if (!frame.EvaluateValue(I, src_operand, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(src_operand).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ frame.AssignValue(inst, I, module);
+
+ if (log) {
+ log->Printf("Interpreted an IntToPtr");
+ log->Printf(" Src : %s", frame.SummarizeValue(src_operand).c_str());
+ log->Printf(" = : %s", frame.SummarizeValue(inst).c_str());
+ }
+ } break;
+ case Instruction::PtrToInt: {
+ const PtrToIntInst *ptr_to_int_inst = dyn_cast<PtrToIntInst>(inst);
+
+ if (!ptr_to_int_inst) {
+ if (log)
+ log->Printf("getOpcode() returns PtrToInt, but instruction is not an "
+ "PtrToIntInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ Value *src_operand = ptr_to_int_inst->getOperand(0);
+
+ lldb_private::Scalar I;
+
+ if (!frame.EvaluateValue(I, src_operand, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(src_operand).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ frame.AssignValue(inst, I, module);
+
+ if (log) {
+ log->Printf("Interpreted a PtrToInt");
+ log->Printf(" Src : %s", frame.SummarizeValue(src_operand).c_str());
+ log->Printf(" = : %s", frame.SummarizeValue(inst).c_str());
+ }
+ } break;
+ case Instruction::Trunc: {
+ const TruncInst *trunc_inst = dyn_cast<TruncInst>(inst);
+
+ if (!trunc_inst) {
+ if (log)
+ log->Printf(
+ "getOpcode() returns Trunc, but instruction is not a TruncInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ Value *src_operand = trunc_inst->getOperand(0);
+
+ lldb_private::Scalar I;
+
+ if (!frame.EvaluateValue(I, src_operand, module)) {
+ if (log)
+ log->Printf("Couldn't evaluate %s", PrintValue(src_operand).c_str());
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ frame.AssignValue(inst, I, module);
+
+ if (log) {
+ log->Printf("Interpreted a Trunc");
+ log->Printf(" Src : %s", frame.SummarizeValue(src_operand).c_str());
+ log->Printf(" = : %s", frame.SummarizeValue(inst).c_str());
+ }
+ } break;
+ case Instruction::Load: {
+ const LoadInst *load_inst = dyn_cast<LoadInst>(inst);
+
+ if (!load_inst) {
+ if (log)
+ log->Printf(
+ "getOpcode() returns Load, but instruction is not a LoadInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ // The semantics of Load are:
+ // Create a region D that will contain the loaded data
+ // Resolve the region P containing a pointer
+ // Dereference P to get the region R that the data should be loaded from
+ // Transfer a unit of type type(D) from R to D
+
+ const Value *pointer_operand = load_inst->getPointerOperand();
+
+ Type *pointer_ty = pointer_operand->getType();
+ PointerType *pointer_ptr_ty = dyn_cast<PointerType>(pointer_ty);
+ if (!pointer_ptr_ty) {
+ if (log)
+ log->Printf("getPointerOperand()->getType() is not a PointerType");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+ Type *target_ty = pointer_ptr_ty->getElementType();
+
+ lldb::addr_t D = frame.ResolveValue(load_inst, module);
+ lldb::addr_t P = frame.ResolveValue(pointer_operand, module);
+
+ if (D == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("LoadInst's value doesn't resolve to anything");
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ if (P == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("LoadInst's pointer doesn't resolve to anything");
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ lldb::addr_t R;
+ lldb_private::Status read_error;
+ execution_unit.ReadPointerFromMemory(&R, P, read_error);
+
+ if (!read_error.Success()) {
+ if (log)
+ log->Printf("Couldn't read the address to be loaded for a LoadInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(memory_read_error);
+ return false;
+ }
+
+ size_t target_size = data_layout.getTypeStoreSize(target_ty);
+ lldb_private::DataBufferHeap buffer(target_size, 0);
+
+ read_error.Clear();
+ execution_unit.ReadMemory(buffer.GetBytes(), R, buffer.GetByteSize(),
+ read_error);
+ if (!read_error.Success()) {
+ if (log)
+ log->Printf("Couldn't read from a region on behalf of a LoadInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(memory_read_error);
+ return false;
+ }
+
+ lldb_private::Status write_error;
+ execution_unit.WriteMemory(D, buffer.GetBytes(), buffer.GetByteSize(),
+ write_error);
+ if (!write_error.Success()) {
+ if (log)
+ log->Printf("Couldn't write to a region on behalf of a LoadInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(memory_read_error);
+ return false;
+ }
+
+ if (log) {
+ log->Printf("Interpreted a LoadInst");
+ log->Printf(" P : 0x%" PRIx64, P);
+ log->Printf(" R : 0x%" PRIx64, R);
+ log->Printf(" D : 0x%" PRIx64, D);
+ }
+ } break;
+ case Instruction::Ret: {
+ return true;
+ }
+ case Instruction::Store: {
+ const StoreInst *store_inst = dyn_cast<StoreInst>(inst);
+
+ if (!store_inst) {
+ if (log)
+ log->Printf(
+ "getOpcode() returns Store, but instruction is not a StoreInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ // The semantics of Store are:
+ // Resolve the region D containing the data to be stored
+ // Resolve the region P containing a pointer
+ // Dereference P to get the region R that the data should be stored in
+ // Transfer a unit of type type(D) from D to R
+
+ const Value *value_operand = store_inst->getValueOperand();
+ const Value *pointer_operand = store_inst->getPointerOperand();
+
+ Type *pointer_ty = pointer_operand->getType();
+ PointerType *pointer_ptr_ty = dyn_cast<PointerType>(pointer_ty);
+ if (!pointer_ptr_ty)
+ return false;
+ Type *target_ty = pointer_ptr_ty->getElementType();
+
+ lldb::addr_t D = frame.ResolveValue(value_operand, module);
+ lldb::addr_t P = frame.ResolveValue(pointer_operand, module);
+
+ if (D == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("StoreInst's value doesn't resolve to anything");
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ if (P == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("StoreInst's pointer doesn't resolve to anything");
+ error.SetErrorToGenericError();
+ error.SetErrorString(bad_value_error);
+ return false;
+ }
+
+ lldb::addr_t R;
+ lldb_private::Status read_error;
+ execution_unit.ReadPointerFromMemory(&R, P, read_error);
+
+ if (!read_error.Success()) {
+ if (log)
+ log->Printf("Couldn't read the address to be loaded for a LoadInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(memory_read_error);
+ return false;
+ }
+
+ size_t target_size = data_layout.getTypeStoreSize(target_ty);
+ lldb_private::DataBufferHeap buffer(target_size, 0);
+
+ read_error.Clear();
+ execution_unit.ReadMemory(buffer.GetBytes(), D, buffer.GetByteSize(),
+ read_error);
+ if (!read_error.Success()) {
+ if (log)
+ log->Printf("Couldn't read from a region on behalf of a StoreInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(memory_read_error);
+ return false;
+ }
+
+ lldb_private::Status write_error;
+ execution_unit.WriteMemory(R, buffer.GetBytes(), buffer.GetByteSize(),
+ write_error);
+ if (!write_error.Success()) {
+ if (log)
+ log->Printf("Couldn't write to a region on behalf of a StoreInst");
+ error.SetErrorToGenericError();
+ error.SetErrorString(memory_write_error);
+ return false;
+ }
+
+ if (log) {
+ log->Printf("Interpreted a StoreInst");
+ log->Printf(" D : 0x%" PRIx64, D);
+ log->Printf(" P : 0x%" PRIx64, P);
+ log->Printf(" R : 0x%" PRIx64, R);
+ }
+ } break;
+ case Instruction::Call: {
+ const CallInst *call_inst = dyn_cast<CallInst>(inst);
+
+ if (!call_inst) {
+ if (log)
+ log->Printf(
+ "getOpcode() returns %s, but instruction is not a CallInst",
+ inst->getOpcodeName());
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ if (CanIgnoreCall(call_inst))
+ break;
+
+ // Get the return type
+ llvm::Type *returnType = call_inst->getType();
+ if (returnType == nullptr) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("unable to access return type");
+ return false;
+ }
+
+ // Work with void, integer and pointer return types
+ if (!returnType->isVoidTy() && !returnType->isIntegerTy() &&
+ !returnType->isPointerTy()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("return type is not supported");
+ return false;
+ }
+
+ // Check we can actually get a thread
+ if (exe_ctx.GetThreadPtr() == nullptr) {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("unable to acquire thread");
+ return false;
+ }
+
+ // Make sure we have a valid process
+ if (!exe_ctx.GetProcessPtr()) {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("unable to get the process");
+ return false;
+ }
+
+ // Find the address of the callee function
+ lldb_private::Scalar I;
+ const llvm::Value *val = call_inst->getCalledValue();
+
+ if (!frame.EvaluateValue(I, val, module)) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("unable to get address of function");
+ return false;
+ }
+ lldb_private::Address funcAddr(I.ULongLong(LLDB_INVALID_ADDRESS));
+
+ lldb_private::DiagnosticManager diagnostics;
+ lldb_private::EvaluateExpressionOptions options;
+
+ // We generally receive a function pointer which we must dereference
+ llvm::Type *prototype = val->getType();
+ if (!prototype->isPointerTy()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("call need function pointer");
+ return false;
+ }
+
+ // Dereference the function pointer
+ prototype = prototype->getPointerElementType();
+ if (!(prototype->isFunctionTy() || prototype->isFunctionVarArg())) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("call need function pointer");
+ return false;
+ }
+
+ // Find number of arguments
+ const int numArgs = call_inst->getNumArgOperands();
+
+ // We work with a fixed array of 16 arguments which is our upper limit
+ static lldb_private::ABI::CallArgument rawArgs[16];
+ if (numArgs >= 16) {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("function takes too many arguments");
+ return false;
+ }
+
+ // Push all function arguments to the argument list that will be passed
+ // to the call function thread plan
+ for (int i = 0; i < numArgs; i++) {
+ // Get details of this argument
+ llvm::Value *arg_op = call_inst->getArgOperand(i);
+ llvm::Type *arg_ty = arg_op->getType();
+
+ // Ensure that this argument is an supported type
+ if (!arg_ty->isIntegerTy() && !arg_ty->isPointerTy()) {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("argument %d must be integer type", i);
+ return false;
+ }
+
+ // Extract the arguments value
+ lldb_private::Scalar tmp_op = 0;
+ if (!frame.EvaluateValue(tmp_op, arg_op, module)) {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("unable to evaluate argument %d", i);
+ return false;
+ }
+
+ // Check if this is a string literal or constant string pointer
+ if (arg_ty->isPointerTy()) {
+ lldb::addr_t addr = tmp_op.ULongLong();
+ size_t dataSize = 0;
+
+ bool Success = execution_unit.GetAllocSize(addr, dataSize);
+ (void)Success;
+ assert(Success &&
+ "unable to locate host data for transfer to device");
+ // Create the required buffer
+ rawArgs[i].size = dataSize;
+ rawArgs[i].data_up.reset(new uint8_t[dataSize + 1]);
+
+ // Read string from host memory
+ execution_unit.ReadMemory(rawArgs[i].data_up.get(), addr, dataSize,
+ error);
+ assert(!error.Fail() &&
+ "we have failed to read the string from memory");
+
+ // Add null terminator
+ rawArgs[i].data_up[dataSize] = '\0';
+ rawArgs[i].type = lldb_private::ABI::CallArgument::HostPointer;
+ } else /* if ( arg_ty->isPointerTy() ) */
+ {
+ rawArgs[i].type = lldb_private::ABI::CallArgument::TargetValue;
+ // Get argument size in bytes
+ rawArgs[i].size = arg_ty->getIntegerBitWidth() / 8;
+ // Push value into argument list for thread plan
+ rawArgs[i].value = tmp_op.ULongLong();
+ }
+ }
+
+ // Pack the arguments into an llvm::array
+ llvm::ArrayRef<lldb_private::ABI::CallArgument> args(rawArgs, numArgs);
+
+ // Setup a thread plan to call the target function
+ lldb::ThreadPlanSP call_plan_sp(
+ new lldb_private::ThreadPlanCallFunctionUsingABI(
+ exe_ctx.GetThreadRef(), funcAddr, *prototype, *returnType, args,
+ options));
+
+ // Check if the plan is valid
+ lldb_private::StreamString ss;
+ if (!call_plan_sp || !call_plan_sp->ValidatePlan(&ss)) {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat(
+ "unable to make ThreadPlanCallFunctionUsingABI for 0x%llx",
+ I.ULongLong());
+ return false;
+ }
+
+ exe_ctx.GetProcessPtr()->SetRunningUserExpression(true);
+
+ // Execute the actual function call thread plan
+ lldb::ExpressionResults res = exe_ctx.GetProcessRef().RunThreadPlan(
+ exe_ctx, call_plan_sp, options, diagnostics);
+
+ // Check that the thread plan completed successfully
+ if (res != lldb::ExpressionResults::eExpressionCompleted) {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("ThreadPlanCallFunctionUsingABI failed");
+ return false;
+ }
+
+ exe_ctx.GetProcessPtr()->SetRunningUserExpression(false);
+
+ // Void return type
+ if (returnType->isVoidTy()) {
+ // Cant assign to void types, so we leave the frame untouched
+ } else
+ // Integer or pointer return type
+ if (returnType->isIntegerTy() || returnType->isPointerTy()) {
+ // Get the encapsulated return value
+ lldb::ValueObjectSP retVal = call_plan_sp.get()->GetReturnValueObject();
+
+ lldb_private::Scalar returnVal = -1;
+ lldb_private::ValueObject *vobj = retVal.get();
+
+ // Check if the return value is valid
+ if (vobj == nullptr || retVal.empty()) {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("unable to get the return value");
+ return false;
+ }
+
+ // Extract the return value as a integer
+ lldb_private::Value &value = vobj->GetValue();
+ returnVal = value.GetScalar();
+
+ // Push the return value as the result
+ frame.AssignValue(inst, returnVal, module);
+ }
+ } break;
+ }
+
+ ++frame.m_ii;
+ }
+
+ if (num_insts >= 4096) {
+ error.SetErrorToGenericError();
+ error.SetErrorString(infinite_loop_error);
+ return false;
+ }
+
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Expression/IRMemoryMap.cpp b/contrib/llvm-project/lldb/source/Expression/IRMemoryMap.cpp
new file mode 100644
index 000000000000..70e62ac12b0e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/IRMemoryMap.cpp
@@ -0,0 +1,842 @@
+//===-- IRMemoryMap.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Expression/IRMemoryMap.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb_private;
+
+IRMemoryMap::IRMemoryMap(lldb::TargetSP target_sp) : m_target_wp(target_sp) {
+ if (target_sp)
+ m_process_wp = target_sp->GetProcessSP();
+}
+
+IRMemoryMap::~IRMemoryMap() {
+ lldb::ProcessSP process_sp = m_process_wp.lock();
+
+ if (process_sp) {
+ AllocationMap::iterator iter;
+
+ Status err;
+
+ while ((iter = m_allocations.begin()) != m_allocations.end()) {
+ err.Clear();
+ if (iter->second.m_leak)
+ m_allocations.erase(iter);
+ else
+ Free(iter->first, err);
+ }
+ }
+}
+
+lldb::addr_t IRMemoryMap::FindSpace(size_t size) {
+ // The FindSpace algorithm's job is to find a region of memory that the
+ // underlying process is unlikely to be using.
+ //
+ // The memory returned by this function will never be written to. The only
+ // point is that it should not shadow process memory if possible, so that
+ // expressions processing real values from the process do not use the wrong
+ // data.
+ //
+ // If the process can in fact allocate memory (CanJIT() lets us know this)
+ // then this can be accomplished just be allocating memory in the inferior.
+ // Then no guessing is required.
+
+ lldb::TargetSP target_sp = m_target_wp.lock();
+ lldb::ProcessSP process_sp = m_process_wp.lock();
+
+ const bool process_is_alive = process_sp && process_sp->IsAlive();
+
+ lldb::addr_t ret = LLDB_INVALID_ADDRESS;
+ if (size == 0)
+ return ret;
+
+ if (process_is_alive && process_sp->CanJIT()) {
+ Status alloc_error;
+
+ ret = process_sp->AllocateMemory(size, lldb::ePermissionsReadable |
+ lldb::ePermissionsWritable,
+ alloc_error);
+
+ if (!alloc_error.Success())
+ return LLDB_INVALID_ADDRESS;
+ else
+ return ret;
+ }
+
+ // At this point we know that we need to hunt.
+ //
+ // First, go to the end of the existing allocations we've made if there are
+ // any allocations. Otherwise start at the beginning of memory.
+
+ if (m_allocations.empty()) {
+ ret = 0x0;
+ } else {
+ auto back = m_allocations.rbegin();
+ lldb::addr_t addr = back->first;
+ size_t alloc_size = back->second.m_size;
+ ret = llvm::alignTo(addr + alloc_size, 4096);
+ }
+
+ // Now, if it's possible to use the GetMemoryRegionInfo API to detect mapped
+ // regions, walk forward through memory until a region is found that has
+ // adequate space for our allocation.
+ if (process_is_alive) {
+ const uint64_t end_of_memory = process_sp->GetAddressByteSize() == 8
+ ? 0xffffffffffffffffull
+ : 0xffffffffull;
+
+ lldbassert(process_sp->GetAddressByteSize() == 4 ||
+ end_of_memory != 0xffffffffull);
+
+ MemoryRegionInfo region_info;
+ Status err = process_sp->GetMemoryRegionInfo(ret, region_info);
+ if (err.Success()) {
+ while (true) {
+ if (region_info.GetReadable() != MemoryRegionInfo::OptionalBool::eNo ||
+ region_info.GetWritable() != MemoryRegionInfo::OptionalBool::eNo ||
+ region_info.GetExecutable() !=
+ MemoryRegionInfo::OptionalBool::eNo) {
+ if (region_info.GetRange().GetRangeEnd() - 1 >= end_of_memory) {
+ ret = LLDB_INVALID_ADDRESS;
+ break;
+ } else {
+ ret = region_info.GetRange().GetRangeEnd();
+ }
+ } else if (ret + size < region_info.GetRange().GetRangeEnd()) {
+ return ret;
+ } else {
+ // ret stays the same. We just need to walk a bit further.
+ }
+
+ err = process_sp->GetMemoryRegionInfo(
+ region_info.GetRange().GetRangeEnd(), region_info);
+ if (err.Fail()) {
+ lldbassert(0 && "GetMemoryRegionInfo() succeeded, then failed");
+ ret = LLDB_INVALID_ADDRESS;
+ break;
+ }
+ }
+ }
+ }
+
+ // We've tried our algorithm, and it didn't work. Now we have to reset back
+ // to the end of the allocations we've already reported, or use a 'sensible'
+ // default if this is our first allocation.
+
+ if (m_allocations.empty()) {
+ uint32_t address_byte_size = GetAddressByteSize();
+ if (address_byte_size != UINT32_MAX) {
+ switch (address_byte_size) {
+ case 8:
+ ret = 0xffffffff00000000ull;
+ break;
+ case 4:
+ ret = 0xee000000ull;
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ auto back = m_allocations.rbegin();
+ lldb::addr_t addr = back->first;
+ size_t alloc_size = back->second.m_size;
+ ret = llvm::alignTo(addr + alloc_size, 4096);
+ }
+
+ return ret;
+}
+
+IRMemoryMap::AllocationMap::iterator
+IRMemoryMap::FindAllocation(lldb::addr_t addr, size_t size) {
+ if (addr == LLDB_INVALID_ADDRESS)
+ return m_allocations.end();
+
+ AllocationMap::iterator iter = m_allocations.lower_bound(addr);
+
+ if (iter == m_allocations.end() || iter->first > addr) {
+ if (iter == m_allocations.begin())
+ return m_allocations.end();
+ iter--;
+ }
+
+ if (iter->first <= addr && iter->first + iter->second.m_size >= addr + size)
+ return iter;
+
+ return m_allocations.end();
+}
+
+bool IRMemoryMap::IntersectsAllocation(lldb::addr_t addr, size_t size) const {
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ AllocationMap::const_iterator iter = m_allocations.lower_bound(addr);
+
+ // Since we only know that the returned interval begins at a location greater
+ // than or equal to where the given interval begins, it's possible that the
+ // given interval intersects either the returned interval or the previous
+ // interval. Thus, we need to check both. Note that we only need to check
+ // these two intervals. Since all intervals are disjoint it is not possible
+ // that an adjacent interval does not intersect, but a non-adjacent interval
+ // does intersect.
+ if (iter != m_allocations.end()) {
+ if (AllocationsIntersect(addr, size, iter->second.m_process_start,
+ iter->second.m_size))
+ return true;
+ }
+
+ if (iter != m_allocations.begin()) {
+ --iter;
+ if (AllocationsIntersect(addr, size, iter->second.m_process_start,
+ iter->second.m_size))
+ return true;
+ }
+
+ return false;
+}
+
+bool IRMemoryMap::AllocationsIntersect(lldb::addr_t addr1, size_t size1,
+ lldb::addr_t addr2, size_t size2) {
+ // Given two half open intervals [A, B) and [X, Y), the only 6 permutations
+ // that satisfy A<B and X<Y are the following:
+ // A B X Y
+ // A X B Y (intersects)
+ // A X Y B (intersects)
+ // X A B Y (intersects)
+ // X A Y B (intersects)
+ // X Y A B
+ // The first is B <= X, and the last is Y <= A. So the condition is !(B <= X
+ // || Y <= A)), or (X < B && A < Y)
+ return (addr2 < (addr1 + size1)) && (addr1 < (addr2 + size2));
+}
+
+lldb::ByteOrder IRMemoryMap::GetByteOrder() {
+ lldb::ProcessSP process_sp = m_process_wp.lock();
+
+ if (process_sp)
+ return process_sp->GetByteOrder();
+
+ lldb::TargetSP target_sp = m_target_wp.lock();
+
+ if (target_sp)
+ return target_sp->GetArchitecture().GetByteOrder();
+
+ return lldb::eByteOrderInvalid;
+}
+
+uint32_t IRMemoryMap::GetAddressByteSize() {
+ lldb::ProcessSP process_sp = m_process_wp.lock();
+
+ if (process_sp)
+ return process_sp->GetAddressByteSize();
+
+ lldb::TargetSP target_sp = m_target_wp.lock();
+
+ if (target_sp)
+ return target_sp->GetArchitecture().GetAddressByteSize();
+
+ return UINT32_MAX;
+}
+
+ExecutionContextScope *IRMemoryMap::GetBestExecutionContextScope() const {
+ lldb::ProcessSP process_sp = m_process_wp.lock();
+
+ if (process_sp)
+ return process_sp.get();
+
+ lldb::TargetSP target_sp = m_target_wp.lock();
+
+ if (target_sp)
+ return target_sp.get();
+
+ return nullptr;
+}
+
+IRMemoryMap::Allocation::Allocation(lldb::addr_t process_alloc,
+ lldb::addr_t process_start, size_t size,
+ uint32_t permissions, uint8_t alignment,
+ AllocationPolicy policy)
+ : m_process_alloc(process_alloc), m_process_start(process_start),
+ m_size(size), m_policy(policy), m_leak(false), m_permissions(permissions),
+ m_alignment(alignment) {
+ switch (policy) {
+ default:
+ llvm_unreachable("Invalid AllocationPolicy");
+ case eAllocationPolicyHostOnly:
+ case eAllocationPolicyMirror:
+ m_data.SetByteSize(size);
+ break;
+ case eAllocationPolicyProcessOnly:
+ break;
+ }
+}
+
+lldb::addr_t IRMemoryMap::Malloc(size_t size, uint8_t alignment,
+ uint32_t permissions, AllocationPolicy policy,
+ bool zero_memory, Status &error) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+ error.Clear();
+
+ lldb::ProcessSP process_sp;
+ lldb::addr_t allocation_address = LLDB_INVALID_ADDRESS;
+ lldb::addr_t aligned_address = LLDB_INVALID_ADDRESS;
+
+ size_t allocation_size;
+
+ if (size == 0) {
+ // FIXME: Malloc(0) should either return an invalid address or assert, in
+ // order to cut down on unnecessary allocations.
+ allocation_size = alignment;
+ } else {
+ // Round up the requested size to an aligned value.
+ allocation_size = llvm::alignTo(size, alignment);
+
+ // The process page cache does not see the requested alignment. We can't
+ // assume its result will be any more than 1-byte aligned. To work around
+ // this, request `alignment - 1` additional bytes.
+ allocation_size += alignment - 1;
+ }
+
+ switch (policy) {
+ default:
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't malloc: invalid allocation policy");
+ return LLDB_INVALID_ADDRESS;
+ case eAllocationPolicyHostOnly:
+ allocation_address = FindSpace(allocation_size);
+ if (allocation_address == LLDB_INVALID_ADDRESS) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't malloc: address space is full");
+ return LLDB_INVALID_ADDRESS;
+ }
+ break;
+ case eAllocationPolicyMirror:
+ process_sp = m_process_wp.lock();
+ if (log)
+ log->Printf("IRMemoryMap::%s process_sp=0x%" PRIx64
+ ", process_sp->CanJIT()=%s, process_sp->IsAlive()=%s",
+ __FUNCTION__, (lldb::addr_t)process_sp.get(),
+ process_sp && process_sp->CanJIT() ? "true" : "false",
+ process_sp && process_sp->IsAlive() ? "true" : "false");
+ if (process_sp && process_sp->CanJIT() && process_sp->IsAlive()) {
+ if (!zero_memory)
+ allocation_address =
+ process_sp->AllocateMemory(allocation_size, permissions, error);
+ else
+ allocation_address =
+ process_sp->CallocateMemory(allocation_size, permissions, error);
+
+ if (!error.Success())
+ return LLDB_INVALID_ADDRESS;
+ } else {
+ if (log)
+ log->Printf("IRMemoryMap::%s switching to eAllocationPolicyHostOnly "
+ "due to failed condition (see previous expr log message)",
+ __FUNCTION__);
+ policy = eAllocationPolicyHostOnly;
+ allocation_address = FindSpace(allocation_size);
+ if (allocation_address == LLDB_INVALID_ADDRESS) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't malloc: address space is full");
+ return LLDB_INVALID_ADDRESS;
+ }
+ }
+ break;
+ case eAllocationPolicyProcessOnly:
+ process_sp = m_process_wp.lock();
+ if (process_sp) {
+ if (process_sp->CanJIT() && process_sp->IsAlive()) {
+ if (!zero_memory)
+ allocation_address =
+ process_sp->AllocateMemory(allocation_size, permissions, error);
+ else
+ allocation_address =
+ process_sp->CallocateMemory(allocation_size, permissions, error);
+
+ if (!error.Success())
+ return LLDB_INVALID_ADDRESS;
+ } else {
+ error.SetErrorToGenericError();
+ error.SetErrorString(
+ "Couldn't malloc: process doesn't support allocating memory");
+ return LLDB_INVALID_ADDRESS;
+ }
+ } else {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't malloc: process doesn't exist, and this "
+ "memory must be in the process");
+ return LLDB_INVALID_ADDRESS;
+ }
+ break;
+ }
+
+ lldb::addr_t mask = alignment - 1;
+ aligned_address = (allocation_address + mask) & (~mask);
+
+ m_allocations.emplace(
+ std::piecewise_construct, std::forward_as_tuple(aligned_address),
+ std::forward_as_tuple(allocation_address, aligned_address,
+ allocation_size, permissions, alignment, policy));
+
+ if (zero_memory) {
+ Status write_error;
+ std::vector<uint8_t> zero_buf(size, 0);
+ WriteMemory(aligned_address, zero_buf.data(), size, write_error);
+ }
+
+ if (log) {
+ const char *policy_string;
+
+ switch (policy) {
+ default:
+ policy_string = "<invalid policy>";
+ break;
+ case eAllocationPolicyHostOnly:
+ policy_string = "eAllocationPolicyHostOnly";
+ break;
+ case eAllocationPolicyProcessOnly:
+ policy_string = "eAllocationPolicyProcessOnly";
+ break;
+ case eAllocationPolicyMirror:
+ policy_string = "eAllocationPolicyMirror";
+ break;
+ }
+
+ log->Printf("IRMemoryMap::Malloc (%" PRIu64 ", 0x%" PRIx64 ", 0x%" PRIx64
+ ", %s) -> 0x%" PRIx64,
+ (uint64_t)allocation_size, (uint64_t)alignment,
+ (uint64_t)permissions, policy_string, aligned_address);
+ }
+
+ return aligned_address;
+}
+
+void IRMemoryMap::Leak(lldb::addr_t process_address, Status &error) {
+ error.Clear();
+
+ AllocationMap::iterator iter = m_allocations.find(process_address);
+
+ if (iter == m_allocations.end()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't leak: allocation doesn't exist");
+ return;
+ }
+
+ Allocation &allocation = iter->second;
+
+ allocation.m_leak = true;
+}
+
+void IRMemoryMap::Free(lldb::addr_t process_address, Status &error) {
+ error.Clear();
+
+ AllocationMap::iterator iter = m_allocations.find(process_address);
+
+ if (iter == m_allocations.end()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't free: allocation doesn't exist");
+ return;
+ }
+
+ Allocation &allocation = iter->second;
+
+ switch (allocation.m_policy) {
+ default:
+ case eAllocationPolicyHostOnly: {
+ lldb::ProcessSP process_sp = m_process_wp.lock();
+ if (process_sp) {
+ if (process_sp->CanJIT() && process_sp->IsAlive())
+ process_sp->DeallocateMemory(
+ allocation.m_process_alloc); // FindSpace allocated this for real
+ }
+
+ break;
+ }
+ case eAllocationPolicyMirror:
+ case eAllocationPolicyProcessOnly: {
+ lldb::ProcessSP process_sp = m_process_wp.lock();
+ if (process_sp)
+ process_sp->DeallocateMemory(allocation.m_process_alloc);
+ }
+ }
+
+ if (lldb_private::Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)) {
+ log->Printf("IRMemoryMap::Free (0x%" PRIx64 ") freed [0x%" PRIx64
+ "..0x%" PRIx64 ")",
+ (uint64_t)process_address, iter->second.m_process_start,
+ iter->second.m_process_start + iter->second.m_size);
+ }
+
+ m_allocations.erase(iter);
+}
+
+bool IRMemoryMap::GetAllocSize(lldb::addr_t address, size_t &size) {
+ AllocationMap::iterator iter = FindAllocation(address, size);
+ if (iter == m_allocations.end())
+ return false;
+
+ Allocation &al = iter->second;
+
+ if (address > (al.m_process_start + al.m_size)) {
+ size = 0;
+ return false;
+ }
+
+ if (address > al.m_process_start) {
+ int dif = address - al.m_process_start;
+ size = al.m_size - dif;
+ return true;
+ }
+
+ size = al.m_size;
+ return true;
+}
+
+void IRMemoryMap::WriteMemory(lldb::addr_t process_address,
+ const uint8_t *bytes, size_t size,
+ Status &error) {
+ error.Clear();
+
+ AllocationMap::iterator iter = FindAllocation(process_address, size);
+
+ if (iter == m_allocations.end()) {
+ lldb::ProcessSP process_sp = m_process_wp.lock();
+
+ if (process_sp) {
+ process_sp->WriteMemory(process_address, bytes, size, error);
+ return;
+ }
+
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't write: no allocation contains the target "
+ "range and the process doesn't exist");
+ return;
+ }
+
+ Allocation &allocation = iter->second;
+
+ uint64_t offset = process_address - allocation.m_process_start;
+
+ lldb::ProcessSP process_sp;
+
+ switch (allocation.m_policy) {
+ default:
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't write: invalid allocation policy");
+ return;
+ case eAllocationPolicyHostOnly:
+ if (!allocation.m_data.GetByteSize()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't write: data buffer is empty");
+ return;
+ }
+ ::memcpy(allocation.m_data.GetBytes() + offset, bytes, size);
+ break;
+ case eAllocationPolicyMirror:
+ if (!allocation.m_data.GetByteSize()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't write: data buffer is empty");
+ return;
+ }
+ ::memcpy(allocation.m_data.GetBytes() + offset, bytes, size);
+ process_sp = m_process_wp.lock();
+ if (process_sp) {
+ process_sp->WriteMemory(process_address, bytes, size, error);
+ if (!error.Success())
+ return;
+ }
+ break;
+ case eAllocationPolicyProcessOnly:
+ process_sp = m_process_wp.lock();
+ if (process_sp) {
+ process_sp->WriteMemory(process_address, bytes, size, error);
+ if (!error.Success())
+ return;
+ }
+ break;
+ }
+
+ if (lldb_private::Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)) {
+ log->Printf("IRMemoryMap::WriteMemory (0x%" PRIx64 ", 0x%" PRIx64
+ ", 0x%" PRId64 ") went to [0x%" PRIx64 "..0x%" PRIx64 ")",
+ (uint64_t)process_address, (uint64_t)bytes, (uint64_t)size,
+ (uint64_t)allocation.m_process_start,
+ (uint64_t)allocation.m_process_start +
+ (uint64_t)allocation.m_size);
+ }
+}
+
+void IRMemoryMap::WriteScalarToMemory(lldb::addr_t process_address,
+ Scalar &scalar, size_t size,
+ Status &error) {
+ error.Clear();
+
+ if (size == UINT32_MAX)
+ size = scalar.GetByteSize();
+
+ if (size > 0) {
+ uint8_t buf[32];
+ const size_t mem_size =
+ scalar.GetAsMemoryData(buf, size, GetByteOrder(), error);
+ if (mem_size > 0) {
+ return WriteMemory(process_address, buf, mem_size, error);
+ } else {
+ error.SetErrorToGenericError();
+ error.SetErrorString(
+ "Couldn't write scalar: failed to get scalar as memory data");
+ }
+ } else {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't write scalar: its size was zero");
+ }
+ return;
+}
+
+void IRMemoryMap::WritePointerToMemory(lldb::addr_t process_address,
+ lldb::addr_t address, Status &error) {
+ error.Clear();
+
+ Scalar scalar(address);
+
+ WriteScalarToMemory(process_address, scalar, GetAddressByteSize(), error);
+}
+
+void IRMemoryMap::ReadMemory(uint8_t *bytes, lldb::addr_t process_address,
+ size_t size, Status &error) {
+ error.Clear();
+
+ AllocationMap::iterator iter = FindAllocation(process_address, size);
+
+ if (iter == m_allocations.end()) {
+ lldb::ProcessSP process_sp = m_process_wp.lock();
+
+ if (process_sp) {
+ process_sp->ReadMemory(process_address, bytes, size, error);
+ return;
+ }
+
+ lldb::TargetSP target_sp = m_target_wp.lock();
+
+ if (target_sp) {
+ Address absolute_address(process_address);
+ target_sp->ReadMemory(absolute_address, false, bytes, size, error);
+ return;
+ }
+
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't read: no allocation contains the target "
+ "range, and neither the process nor the target exist");
+ return;
+ }
+
+ Allocation &allocation = iter->second;
+
+ uint64_t offset = process_address - allocation.m_process_start;
+
+ if (offset > allocation.m_size) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't read: data is not in the allocation");
+ return;
+ }
+
+ lldb::ProcessSP process_sp;
+
+ switch (allocation.m_policy) {
+ default:
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't read: invalid allocation policy");
+ return;
+ case eAllocationPolicyHostOnly:
+ if (!allocation.m_data.GetByteSize()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't read: data buffer is empty");
+ return;
+ }
+ if (allocation.m_data.GetByteSize() < offset + size) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't read: not enough underlying data");
+ return;
+ }
+
+ ::memcpy(bytes, allocation.m_data.GetBytes() + offset, size);
+ break;
+ case eAllocationPolicyMirror:
+ process_sp = m_process_wp.lock();
+ if (process_sp) {
+ process_sp->ReadMemory(process_address, bytes, size, error);
+ if (!error.Success())
+ return;
+ } else {
+ if (!allocation.m_data.GetByteSize()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't read: data buffer is empty");
+ return;
+ }
+ ::memcpy(bytes, allocation.m_data.GetBytes() + offset, size);
+ }
+ break;
+ case eAllocationPolicyProcessOnly:
+ process_sp = m_process_wp.lock();
+ if (process_sp) {
+ process_sp->ReadMemory(process_address, bytes, size, error);
+ if (!error.Success())
+ return;
+ }
+ break;
+ }
+
+ if (lldb_private::Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)) {
+ log->Printf("IRMemoryMap::ReadMemory (0x%" PRIx64 ", 0x%" PRIx64
+ ", 0x%" PRId64 ") came from [0x%" PRIx64 "..0x%" PRIx64 ")",
+ (uint64_t)process_address, (uint64_t)bytes, (uint64_t)size,
+ (uint64_t)allocation.m_process_start,
+ (uint64_t)allocation.m_process_start +
+ (uint64_t)allocation.m_size);
+ }
+}
+
+void IRMemoryMap::ReadScalarFromMemory(Scalar &scalar,
+ lldb::addr_t process_address,
+ size_t size, Status &error) {
+ error.Clear();
+
+ if (size > 0) {
+ DataBufferHeap buf(size, 0);
+ ReadMemory(buf.GetBytes(), process_address, size, error);
+
+ if (!error.Success())
+ return;
+
+ DataExtractor extractor(buf.GetBytes(), buf.GetByteSize(), GetByteOrder(),
+ GetAddressByteSize());
+
+ lldb::offset_t offset = 0;
+
+ switch (size) {
+ default:
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat(
+ "Couldn't read scalar: unsupported size %" PRIu64, (uint64_t)size);
+ return;
+ case 1:
+ scalar = extractor.GetU8(&offset);
+ break;
+ case 2:
+ scalar = extractor.GetU16(&offset);
+ break;
+ case 4:
+ scalar = extractor.GetU32(&offset);
+ break;
+ case 8:
+ scalar = extractor.GetU64(&offset);
+ break;
+ }
+ } else {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't read scalar: its size was zero");
+ }
+ return;
+}
+
+void IRMemoryMap::ReadPointerFromMemory(lldb::addr_t *address,
+ lldb::addr_t process_address,
+ Status &error) {
+ error.Clear();
+
+ Scalar pointer_scalar;
+ ReadScalarFromMemory(pointer_scalar, process_address, GetAddressByteSize(),
+ error);
+
+ if (!error.Success())
+ return;
+
+ *address = pointer_scalar.ULongLong();
+
+ return;
+}
+
+void IRMemoryMap::GetMemoryData(DataExtractor &extractor,
+ lldb::addr_t process_address, size_t size,
+ Status &error) {
+ error.Clear();
+
+ if (size > 0) {
+ AllocationMap::iterator iter = FindAllocation(process_address, size);
+
+ if (iter == m_allocations.end()) {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat(
+ "Couldn't find an allocation containing [0x%" PRIx64 "..0x%" PRIx64
+ ")",
+ process_address, process_address + size);
+ return;
+ }
+
+ Allocation &allocation = iter->second;
+
+ switch (allocation.m_policy) {
+ default:
+ error.SetErrorToGenericError();
+ error.SetErrorString(
+ "Couldn't get memory data: invalid allocation policy");
+ return;
+ case eAllocationPolicyProcessOnly:
+ error.SetErrorToGenericError();
+ error.SetErrorString(
+ "Couldn't get memory data: memory is only in the target");
+ return;
+ case eAllocationPolicyMirror: {
+ lldb::ProcessSP process_sp = m_process_wp.lock();
+
+ if (!allocation.m_data.GetByteSize()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't get memory data: data buffer is empty");
+ return;
+ }
+ if (process_sp) {
+ process_sp->ReadMemory(allocation.m_process_start,
+ allocation.m_data.GetBytes(),
+ allocation.m_data.GetByteSize(), error);
+ if (!error.Success())
+ return;
+ uint64_t offset = process_address - allocation.m_process_start;
+ extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size,
+ GetByteOrder(), GetAddressByteSize());
+ return;
+ }
+ } break;
+ case eAllocationPolicyHostOnly:
+ if (!allocation.m_data.GetByteSize()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't get memory data: data buffer is empty");
+ return;
+ }
+ uint64_t offset = process_address - allocation.m_process_start;
+ extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size,
+ GetByteOrder(), GetAddressByteSize());
+ return;
+ }
+ } else {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't get memory data: its size was zero");
+ return;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Expression/LLVMUserExpression.cpp b/contrib/llvm-project/lldb/source/Expression/LLVMUserExpression.cpp
new file mode 100644
index 000000000000..5a1b750318c9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/LLVMUserExpression.cpp
@@ -0,0 +1,371 @@
+//===-- LLVMUserExpression.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "lldb/Expression/LLVMUserExpression.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Expression/IRInterpreter.h"
+#include "lldb/Expression/Materializer.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallUserExpression.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+
+LLVMUserExpression::LLVMUserExpression(ExecutionContextScope &exe_scope,
+ llvm::StringRef expr,
+ llvm::StringRef prefix,
+ lldb::LanguageType language,
+ ResultType desired_type,
+ const EvaluateExpressionOptions &options,
+ ExpressionKind kind)
+ : UserExpression(exe_scope, expr, prefix, language, desired_type, options,
+ kind),
+ m_stack_frame_bottom(LLDB_INVALID_ADDRESS),
+ m_stack_frame_top(LLDB_INVALID_ADDRESS), m_allow_cxx(false),
+ m_allow_objc(false), m_transformed_text(), m_execution_unit_sp(),
+ m_materializer_up(), m_jit_module_wp(), m_enforce_valid_object(true),
+ m_in_cplusplus_method(false), m_in_objectivec_method(false),
+ m_in_static_method(false), m_needs_object_ptr(false), m_target(nullptr),
+ m_can_interpret(false), m_materialized_address(LLDB_INVALID_ADDRESS) {}
+
+LLVMUserExpression::~LLVMUserExpression() {
+ if (m_target) {
+ lldb::ModuleSP jit_module_sp(m_jit_module_wp.lock());
+ if (jit_module_sp)
+ m_target->GetImages().Remove(jit_module_sp);
+ }
+}
+
+lldb::ExpressionResults
+LLVMUserExpression::DoExecute(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx,
+ const EvaluateExpressionOptions &options,
+ lldb::UserExpressionSP &shared_ptr_to_me,
+ lldb::ExpressionVariableSP &result) {
+ // The expression log is quite verbose, and if you're just tracking the
+ // execution of the expression, it's quite convenient to have these logs come
+ // out with the STEP log as well.
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EXPRESSIONS |
+ LIBLLDB_LOG_STEP));
+
+ if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) {
+ lldb::addr_t struct_address = LLDB_INVALID_ADDRESS;
+
+ if (!PrepareToExecuteJITExpression(diagnostic_manager, exe_ctx,
+ struct_address)) {
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityError,
+ "errored out in %s, couldn't PrepareToExecuteJITExpression",
+ __FUNCTION__);
+ return lldb::eExpressionSetupError;
+ }
+
+ lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS;
+ lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS;
+
+ if (m_can_interpret) {
+ llvm::Module *module = m_execution_unit_sp->GetModule();
+ llvm::Function *function = m_execution_unit_sp->GetFunction();
+
+ if (!module || !function) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "supposed to interpret, but nothing is there");
+ return lldb::eExpressionSetupError;
+ }
+
+ Status interpreter_error;
+
+ std::vector<lldb::addr_t> args;
+
+ if (!AddArguments(exe_ctx, args, struct_address, diagnostic_manager)) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "errored out in %s, couldn't AddArguments",
+ __FUNCTION__);
+ return lldb::eExpressionSetupError;
+ }
+
+ function_stack_bottom = m_stack_frame_bottom;
+ function_stack_top = m_stack_frame_top;
+
+ IRInterpreter::Interpret(*module, *function, args, *m_execution_unit_sp,
+ interpreter_error, function_stack_bottom,
+ function_stack_top, exe_ctx);
+
+ if (!interpreter_error.Success()) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "supposed to interpret, but failed: %s",
+ interpreter_error.AsCString());
+ return lldb::eExpressionDiscarded;
+ }
+ } else {
+ if (!exe_ctx.HasThreadScope()) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "%s called with no thread selected",
+ __FUNCTION__);
+ return lldb::eExpressionSetupError;
+ }
+
+ Address wrapper_address(m_jit_start_addr);
+
+ std::vector<lldb::addr_t> args;
+
+ if (!AddArguments(exe_ctx, args, struct_address, diagnostic_manager)) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "errored out in %s, couldn't AddArguments",
+ __FUNCTION__);
+ return lldb::eExpressionSetupError;
+ }
+
+ lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression(
+ exe_ctx.GetThreadRef(), wrapper_address, args, options,
+ shared_ptr_to_me));
+
+ StreamString ss;
+ if (!call_plan_sp || !call_plan_sp->ValidatePlan(&ss)) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError, ss.GetString());
+ return lldb::eExpressionSetupError;
+ }
+
+ ThreadPlanCallUserExpression *user_expression_plan =
+ static_cast<ThreadPlanCallUserExpression *>(call_plan_sp.get());
+
+ lldb::addr_t function_stack_pointer =
+ user_expression_plan->GetFunctionStackPointer();
+
+ function_stack_bottom = function_stack_pointer - HostInfo::GetPageSize();
+ function_stack_top = function_stack_pointer;
+
+ if (log)
+ log->Printf(
+ "-- [UserExpression::Execute] Execution of expression begins --");
+
+ if (exe_ctx.GetProcessPtr())
+ exe_ctx.GetProcessPtr()->SetRunningUserExpression(true);
+
+ lldb::ExpressionResults execution_result =
+ exe_ctx.GetProcessRef().RunThreadPlan(exe_ctx, call_plan_sp, options,
+ diagnostic_manager);
+
+ if (exe_ctx.GetProcessPtr())
+ exe_ctx.GetProcessPtr()->SetRunningUserExpression(false);
+
+ if (log)
+ log->Printf("-- [UserExpression::Execute] Execution of expression "
+ "completed --");
+
+ if (execution_result == lldb::eExpressionInterrupted ||
+ execution_result == lldb::eExpressionHitBreakpoint) {
+ const char *error_desc = nullptr;
+
+ if (call_plan_sp) {
+ lldb::StopInfoSP real_stop_info_sp = call_plan_sp->GetRealStopInfo();
+ if (real_stop_info_sp)
+ error_desc = real_stop_info_sp->GetDescription();
+ }
+ if (error_desc)
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "Execution was interrupted, reason: %s.",
+ error_desc);
+ else
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "Execution was interrupted.");
+
+ if ((execution_result == lldb::eExpressionInterrupted &&
+ options.DoesUnwindOnError()) ||
+ (execution_result == lldb::eExpressionHitBreakpoint &&
+ options.DoesIgnoreBreakpoints()))
+ diagnostic_manager.AppendMessageToDiagnostic(
+ "The process has been returned to the state before expression "
+ "evaluation.");
+ else {
+ if (execution_result == lldb::eExpressionHitBreakpoint)
+ user_expression_plan->TransferExpressionOwnership();
+ diagnostic_manager.AppendMessageToDiagnostic(
+ "The process has been left at the point where it was "
+ "interrupted, "
+ "use \"thread return -x\" to return to the state before "
+ "expression evaluation.");
+ }
+
+ return execution_result;
+ } else if (execution_result == lldb::eExpressionStoppedForDebug) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityRemark,
+ "Execution was halted at the first instruction of the expression "
+ "function because \"debug\" was requested.\n"
+ "Use \"thread return -x\" to return to the state before expression "
+ "evaluation.");
+ return execution_result;
+ } else if (execution_result != lldb::eExpressionCompleted) {
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityError,
+ "Couldn't execute function; result was %s",
+ Process::ExecutionResultAsCString(execution_result));
+ return execution_result;
+ }
+ }
+
+ if (FinalizeJITExecution(diagnostic_manager, exe_ctx, result,
+ function_stack_bottom, function_stack_top)) {
+ return lldb::eExpressionCompleted;
+ } else {
+ return lldb::eExpressionResultUnavailable;
+ }
+ } else {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "Expression can't be run, because there is no JIT compiled function");
+ return lldb::eExpressionSetupError;
+ }
+}
+
+bool LLVMUserExpression::FinalizeJITExecution(
+ DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
+ lldb::ExpressionVariableSP &result, lldb::addr_t function_stack_bottom,
+ lldb::addr_t function_stack_top) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("-- [UserExpression::FinalizeJITExecution] Dematerializing "
+ "after execution --");
+
+ if (!m_dematerializer_sp) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "Couldn't apply expression side effects : no "
+ "dematerializer is present");
+ return false;
+ }
+
+ Status dematerialize_error;
+
+ m_dematerializer_sp->Dematerialize(dematerialize_error, function_stack_bottom,
+ function_stack_top);
+
+ if (!dematerialize_error.Success()) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "Couldn't apply expression side effects : %s",
+ dematerialize_error.AsCString("unknown error"));
+ return false;
+ }
+
+ result =
+ GetResultAfterDematerialization(exe_ctx.GetBestExecutionContextScope());
+
+ if (result)
+ result->TransferAddress();
+
+ m_dematerializer_sp.reset();
+
+ return true;
+}
+
+bool LLVMUserExpression::PrepareToExecuteJITExpression(
+ DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
+ lldb::addr_t &struct_address) {
+ lldb::TargetSP target;
+ lldb::ProcessSP process;
+ lldb::StackFrameSP frame;
+
+ if (!LockAndCheckContext(exe_ctx, target, process, frame)) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "The context has changed before we could JIT the expression!");
+ return false;
+ }
+
+ if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) {
+ if (m_materialized_address == LLDB_INVALID_ADDRESS) {
+ Status alloc_error;
+
+ IRMemoryMap::AllocationPolicy policy =
+ m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly
+ : IRMemoryMap::eAllocationPolicyMirror;
+
+ const bool zero_memory = false;
+
+ m_materialized_address = m_execution_unit_sp->Malloc(
+ m_materializer_up->GetStructByteSize(),
+ m_materializer_up->GetStructAlignment(),
+ lldb::ePermissionsReadable | lldb::ePermissionsWritable, policy,
+ zero_memory, alloc_error);
+
+ if (!alloc_error.Success()) {
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityError,
+ "Couldn't allocate space for materialized struct: %s",
+ alloc_error.AsCString());
+ return false;
+ }
+ }
+
+ struct_address = m_materialized_address;
+
+ if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS) {
+ Status alloc_error;
+
+ const size_t stack_frame_size = 512 * 1024;
+
+ const bool zero_memory = false;
+
+ m_stack_frame_bottom = m_execution_unit_sp->Malloc(
+ stack_frame_size, 8,
+ lldb::ePermissionsReadable | lldb::ePermissionsWritable,
+ IRMemoryMap::eAllocationPolicyHostOnly, zero_memory, alloc_error);
+
+ m_stack_frame_top = m_stack_frame_bottom + stack_frame_size;
+
+ if (!alloc_error.Success()) {
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityError,
+ "Couldn't allocate space for the stack frame: %s",
+ alloc_error.AsCString());
+ return false;
+ }
+ }
+
+ Status materialize_error;
+
+ m_dematerializer_sp = m_materializer_up->Materialize(
+ frame, *m_execution_unit_sp, struct_address, materialize_error);
+
+ if (!materialize_error.Success()) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "Couldn't materialize: %s",
+ materialize_error.AsCString());
+ return false;
+ }
+ }
+ return true;
+}
+
+lldb::ModuleSP LLVMUserExpression::GetJITModule() {
+ if (m_execution_unit_sp)
+ return m_execution_unit_sp->GetJITModule();
+ return lldb::ModuleSP();
+}
diff --git a/contrib/llvm-project/lldb/source/Expression/Materializer.cpp b/contrib/llvm-project/lldb/source/Expression/Materializer.cpp
new file mode 100644
index 000000000000..0f871fcbefbc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/Materializer.cpp
@@ -0,0 +1,1451 @@
+//===-- Materializer.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Expression/Materializer.h"
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+uint32_t Materializer::AddStructMember(Entity &entity) {
+ uint32_t size = entity.GetSize();
+ uint32_t alignment = entity.GetAlignment();
+
+ uint32_t ret;
+
+ if (m_current_offset == 0)
+ m_struct_alignment = alignment;
+
+ if (m_current_offset % alignment)
+ m_current_offset += (alignment - (m_current_offset % alignment));
+
+ ret = m_current_offset;
+
+ m_current_offset += size;
+
+ return ret;
+}
+
+void Materializer::Entity::SetSizeAndAlignmentFromType(CompilerType &type) {
+ if (llvm::Optional<uint64_t> size = type.GetByteSize(nullptr))
+ m_size = *size;
+
+ uint32_t bit_alignment = type.GetTypeBitAlign();
+
+ if (bit_alignment % 8) {
+ bit_alignment += 8;
+ bit_alignment &= ~((uint32_t)0x111u);
+ }
+
+ m_alignment = bit_alignment / 8;
+}
+
+class EntityPersistentVariable : public Materializer::Entity {
+public:
+ EntityPersistentVariable(lldb::ExpressionVariableSP &persistent_variable_sp,
+ Materializer::PersistentVariableDelegate *delegate)
+ : Entity(), m_persistent_variable_sp(persistent_variable_sp),
+ m_delegate(delegate) {
+ // Hard-coding to maximum size of a pointer since persistent variables are
+ // materialized by reference
+ m_size = 8;
+ m_alignment = 8;
+ }
+
+ void MakeAllocation(IRMemoryMap &map, Status &err) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ // Allocate a spare memory area to store the persistent variable's
+ // contents.
+
+ Status allocate_error;
+ const bool zero_memory = false;
+
+ lldb::addr_t mem = map.Malloc(
+ m_persistent_variable_sp->GetByteSize(), 8,
+ lldb::ePermissionsReadable | lldb::ePermissionsWritable,
+ IRMemoryMap::eAllocationPolicyMirror, zero_memory, allocate_error);
+
+ if (!allocate_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't allocate a memory area to store %s: %s",
+ m_persistent_variable_sp->GetName().GetCString(),
+ allocate_error.AsCString());
+ return;
+ }
+
+ if (log)
+ log->Printf("Allocated %s (0x%" PRIx64 ") successfully",
+ m_persistent_variable_sp->GetName().GetCString(), mem);
+
+ // Put the location of the spare memory into the live data of the
+ // ValueObject.
+
+ m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create(
+ map.GetBestExecutionContextScope(),
+ m_persistent_variable_sp->GetCompilerType(),
+ m_persistent_variable_sp->GetName(), mem, eAddressTypeLoad,
+ map.GetAddressByteSize());
+
+ // Clear the flag if the variable will never be deallocated.
+
+ if (m_persistent_variable_sp->m_flags &
+ ExpressionVariable::EVKeepInTarget) {
+ Status leak_error;
+ map.Leak(mem, leak_error);
+ m_persistent_variable_sp->m_flags &=
+ ~ExpressionVariable::EVNeedsAllocation;
+ }
+
+ // Write the contents of the variable to the area.
+
+ Status write_error;
+
+ map.WriteMemory(mem, m_persistent_variable_sp->GetValueBytes(),
+ m_persistent_variable_sp->GetByteSize(), write_error);
+
+ if (!write_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't write %s to the target: %s",
+ m_persistent_variable_sp->GetName().AsCString(),
+ write_error.AsCString());
+ return;
+ }
+ }
+
+ void DestroyAllocation(IRMemoryMap &map, Status &err) {
+ Status deallocate_error;
+
+ map.Free((lldb::addr_t)m_persistent_variable_sp->m_live_sp->GetValue()
+ .GetScalar()
+ .ULongLong(),
+ deallocate_error);
+
+ m_persistent_variable_sp->m_live_sp.reset();
+
+ if (!deallocate_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't deallocate memory for %s: %s",
+ m_persistent_variable_sp->GetName().GetCString(),
+ deallocate_error.AsCString());
+ }
+ }
+
+ void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
+ lldb::addr_t process_address, Status &err) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ if (log) {
+ log->Printf("EntityPersistentVariable::Materialize [address = 0x%" PRIx64
+ ", m_name = %s, m_flags = 0x%hx]",
+ (uint64_t)load_addr,
+ m_persistent_variable_sp->GetName().AsCString(),
+ m_persistent_variable_sp->m_flags);
+ }
+
+ if (m_persistent_variable_sp->m_flags &
+ ExpressionVariable::EVNeedsAllocation) {
+ MakeAllocation(map, err);
+ m_persistent_variable_sp->m_flags |=
+ ExpressionVariable::EVIsLLDBAllocated;
+
+ if (!err.Success())
+ return;
+ }
+
+ if ((m_persistent_variable_sp->m_flags &
+ ExpressionVariable::EVIsProgramReference &&
+ m_persistent_variable_sp->m_live_sp) ||
+ m_persistent_variable_sp->m_flags &
+ ExpressionVariable::EVIsLLDBAllocated) {
+ Status write_error;
+
+ map.WriteScalarToMemory(
+ load_addr,
+ m_persistent_variable_sp->m_live_sp->GetValue().GetScalar(),
+ map.GetAddressByteSize(), write_error);
+
+ if (!write_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't write the location of %s to memory: %s",
+ m_persistent_variable_sp->GetName().AsCString(),
+ write_error.AsCString());
+ }
+ } else {
+ err.SetErrorStringWithFormat(
+ "no materialization happened for persistent variable %s",
+ m_persistent_variable_sp->GetName().AsCString());
+ return;
+ }
+ }
+
+ void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
+ lldb::addr_t process_address, lldb::addr_t frame_top,
+ lldb::addr_t frame_bottom, Status &err) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ if (log) {
+ log->Printf(
+ "EntityPersistentVariable::Dematerialize [address = 0x%" PRIx64
+ ", m_name = %s, m_flags = 0x%hx]",
+ (uint64_t)process_address + m_offset,
+ m_persistent_variable_sp->GetName().AsCString(),
+ m_persistent_variable_sp->m_flags);
+ }
+
+ if (m_delegate) {
+ m_delegate->DidDematerialize(m_persistent_variable_sp);
+ }
+
+ if ((m_persistent_variable_sp->m_flags &
+ ExpressionVariable::EVIsLLDBAllocated) ||
+ (m_persistent_variable_sp->m_flags &
+ ExpressionVariable::EVIsProgramReference)) {
+ if (m_persistent_variable_sp->m_flags &
+ ExpressionVariable::EVIsProgramReference &&
+ !m_persistent_variable_sp->m_live_sp) {
+ // If the reference comes from the program, then the
+ // ClangExpressionVariable's live variable data hasn't been set up yet.
+ // Do this now.
+
+ lldb::addr_t location;
+ Status read_error;
+
+ map.ReadPointerFromMemory(&location, load_addr, read_error);
+
+ if (!read_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't read the address of program-allocated variable %s: %s",
+ m_persistent_variable_sp->GetName().GetCString(),
+ read_error.AsCString());
+ return;
+ }
+
+ m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create(
+ map.GetBestExecutionContextScope(),
+ m_persistent_variable_sp.get()->GetCompilerType(),
+ m_persistent_variable_sp->GetName(), location, eAddressTypeLoad,
+ m_persistent_variable_sp->GetByteSize());
+
+ if (frame_top != LLDB_INVALID_ADDRESS &&
+ frame_bottom != LLDB_INVALID_ADDRESS && location >= frame_bottom &&
+ location <= frame_top) {
+ // If the variable is resident in the stack frame created by the
+ // expression, then it cannot be relied upon to stay around. We
+ // treat it as needing reallocation.
+ m_persistent_variable_sp->m_flags |=
+ ExpressionVariable::EVIsLLDBAllocated;
+ m_persistent_variable_sp->m_flags |=
+ ExpressionVariable::EVNeedsAllocation;
+ m_persistent_variable_sp->m_flags |=
+ ExpressionVariable::EVNeedsFreezeDry;
+ m_persistent_variable_sp->m_flags &=
+ ~ExpressionVariable::EVIsProgramReference;
+ }
+ }
+
+ lldb::addr_t mem = m_persistent_variable_sp->m_live_sp->GetValue()
+ .GetScalar()
+ .ULongLong();
+
+ if (!m_persistent_variable_sp->m_live_sp) {
+ err.SetErrorStringWithFormat(
+ "couldn't find the memory area used to store %s",
+ m_persistent_variable_sp->GetName().GetCString());
+ return;
+ }
+
+ if (m_persistent_variable_sp->m_live_sp->GetValue()
+ .GetValueAddressType() != eAddressTypeLoad) {
+ err.SetErrorStringWithFormat(
+ "the address of the memory area for %s is in an incorrect format",
+ m_persistent_variable_sp->GetName().GetCString());
+ return;
+ }
+
+ if (m_persistent_variable_sp->m_flags &
+ ExpressionVariable::EVNeedsFreezeDry ||
+ m_persistent_variable_sp->m_flags &
+ ExpressionVariable::EVKeepInTarget) {
+ if (log)
+ log->Printf(
+ "Dematerializing %s from 0x%" PRIx64 " (size = %llu)",
+ m_persistent_variable_sp->GetName().GetCString(), (uint64_t)mem,
+ (unsigned long long)m_persistent_variable_sp->GetByteSize());
+
+ // Read the contents of the spare memory area
+
+ m_persistent_variable_sp->ValueUpdated();
+
+ Status read_error;
+
+ map.ReadMemory(m_persistent_variable_sp->GetValueBytes(), mem,
+ m_persistent_variable_sp->GetByteSize(), read_error);
+
+ if (!read_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't read the contents of %s from memory: %s",
+ m_persistent_variable_sp->GetName().GetCString(),
+ read_error.AsCString());
+ return;
+ }
+
+ m_persistent_variable_sp->m_flags &=
+ ~ExpressionVariable::EVNeedsFreezeDry;
+ }
+ } else {
+ err.SetErrorStringWithFormat(
+ "no dematerialization happened for persistent variable %s",
+ m_persistent_variable_sp->GetName().AsCString());
+ return;
+ }
+
+ lldb::ProcessSP process_sp =
+ map.GetBestExecutionContextScope()->CalculateProcess();
+ if (!process_sp || !process_sp->CanJIT()) {
+ // Allocations are not persistent so persistent variables cannot stay
+ // materialized.
+
+ m_persistent_variable_sp->m_flags |=
+ ExpressionVariable::EVNeedsAllocation;
+
+ DestroyAllocation(map, err);
+ if (!err.Success())
+ return;
+ } else if (m_persistent_variable_sp->m_flags &
+ ExpressionVariable::EVNeedsAllocation &&
+ !(m_persistent_variable_sp->m_flags &
+ ExpressionVariable::EVKeepInTarget)) {
+ DestroyAllocation(map, err);
+ if (!err.Success())
+ return;
+ }
+ }
+
+ void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address,
+ Log *log) override {
+ StreamString dump_stream;
+
+ Status err;
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ dump_stream.Printf("0x%" PRIx64 ": EntityPersistentVariable (%s)\n",
+ load_addr,
+ m_persistent_variable_sp->GetName().AsCString());
+
+ {
+ dump_stream.Printf("Pointer:\n");
+
+ DataBufferHeap data(m_size, 0);
+
+ map.ReadMemory(data.GetBytes(), load_addr, m_size, err);
+
+ if (!err.Success()) {
+ dump_stream.Printf(" <could not be read>\n");
+ } else {
+ DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
+ load_addr);
+
+ dump_stream.PutChar('\n');
+ }
+ }
+
+ {
+ dump_stream.Printf("Target:\n");
+
+ lldb::addr_t target_address;
+
+ map.ReadPointerFromMemory(&target_address, load_addr, err);
+
+ if (!err.Success()) {
+ dump_stream.Printf(" <could not be read>\n");
+ } else {
+ DataBufferHeap data(m_persistent_variable_sp->GetByteSize(), 0);
+
+ map.ReadMemory(data.GetBytes(), target_address,
+ m_persistent_variable_sp->GetByteSize(), err);
+
+ if (!err.Success()) {
+ dump_stream.Printf(" <could not be read>\n");
+ } else {
+ DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
+ target_address);
+
+ dump_stream.PutChar('\n');
+ }
+ }
+ }
+
+ log->PutString(dump_stream.GetString());
+ }
+
+ void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {}
+
+private:
+ lldb::ExpressionVariableSP m_persistent_variable_sp;
+ Materializer::PersistentVariableDelegate *m_delegate;
+};
+
+uint32_t Materializer::AddPersistentVariable(
+ lldb::ExpressionVariableSP &persistent_variable_sp,
+ PersistentVariableDelegate *delegate, Status &err) {
+ EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
+ iter->reset(new EntityPersistentVariable(persistent_variable_sp, delegate));
+ uint32_t ret = AddStructMember(**iter);
+ (*iter)->SetOffset(ret);
+ return ret;
+}
+
+class EntityVariable : public Materializer::Entity {
+public:
+ EntityVariable(lldb::VariableSP &variable_sp)
+ : Entity(), m_variable_sp(variable_sp), m_is_reference(false),
+ m_temporary_allocation(LLDB_INVALID_ADDRESS),
+ m_temporary_allocation_size(0) {
+ // Hard-coding to maximum size of a pointer since all variables are
+ // materialized by reference
+ m_size = 8;
+ m_alignment = 8;
+ m_is_reference =
+ m_variable_sp->GetType()->GetForwardCompilerType().IsReferenceType();
+ }
+
+ void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
+ lldb::addr_t process_address, Status &err) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+ if (log) {
+ log->Printf("EntityVariable::Materialize [address = 0x%" PRIx64
+ ", m_variable_sp = %s]",
+ (uint64_t)load_addr, m_variable_sp->GetName().AsCString());
+ }
+
+ ExecutionContextScope *scope = frame_sp.get();
+
+ if (!scope)
+ scope = map.GetBestExecutionContextScope();
+
+ lldb::ValueObjectSP valobj_sp =
+ ValueObjectVariable::Create(scope, m_variable_sp);
+
+ if (!valobj_sp) {
+ err.SetErrorStringWithFormat(
+ "couldn't get a value object for variable %s",
+ m_variable_sp->GetName().AsCString());
+ return;
+ }
+
+ Status valobj_error = valobj_sp->GetError();
+
+ if (valobj_error.Fail()) {
+ err.SetErrorStringWithFormat("couldn't get the value of variable %s: %s",
+ m_variable_sp->GetName().AsCString(),
+ valobj_error.AsCString());
+ return;
+ }
+
+ if (m_is_reference) {
+ DataExtractor valobj_extractor;
+ Status extract_error;
+ valobj_sp->GetData(valobj_extractor, extract_error);
+
+ if (!extract_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't read contents of reference variable %s: %s",
+ m_variable_sp->GetName().AsCString(), extract_error.AsCString());
+ return;
+ }
+
+ lldb::offset_t offset = 0;
+ lldb::addr_t reference_addr = valobj_extractor.GetAddress(&offset);
+
+ Status write_error;
+ map.WritePointerToMemory(load_addr, reference_addr, write_error);
+
+ if (!write_error.Success()) {
+ err.SetErrorStringWithFormat("couldn't write the contents of reference "
+ "variable %s to memory: %s",
+ m_variable_sp->GetName().AsCString(),
+ write_error.AsCString());
+ return;
+ }
+ } else {
+ AddressType address_type = eAddressTypeInvalid;
+ const bool scalar_is_load_address = false;
+ lldb::addr_t addr_of_valobj =
+ valobj_sp->GetAddressOf(scalar_is_load_address, &address_type);
+ if (addr_of_valobj != LLDB_INVALID_ADDRESS) {
+ Status write_error;
+ map.WritePointerToMemory(load_addr, addr_of_valobj, write_error);
+
+ if (!write_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't write the address of variable %s to memory: %s",
+ m_variable_sp->GetName().AsCString(), write_error.AsCString());
+ return;
+ }
+ } else {
+ DataExtractor data;
+ Status extract_error;
+ valobj_sp->GetData(data, extract_error);
+ if (!extract_error.Success()) {
+ err.SetErrorStringWithFormat("couldn't get the value of %s: %s",
+ m_variable_sp->GetName().AsCString(),
+ extract_error.AsCString());
+ return;
+ }
+
+ if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
+ err.SetErrorStringWithFormat(
+ "trying to create a temporary region for %s but one exists",
+ m_variable_sp->GetName().AsCString());
+ return;
+ }
+
+ if (data.GetByteSize() < m_variable_sp->GetType()->GetByteSize()) {
+ if (data.GetByteSize() == 0 &&
+ !m_variable_sp->LocationExpression().IsValid()) {
+ err.SetErrorStringWithFormat("the variable '%s' has no location, "
+ "it may have been optimized out",
+ m_variable_sp->GetName().AsCString());
+ } else {
+ err.SetErrorStringWithFormat(
+ "size of variable %s (%" PRIu64
+ ") is larger than the ValueObject's size (%" PRIu64 ")",
+ m_variable_sp->GetName().AsCString(),
+ m_variable_sp->GetType()->GetByteSize().getValueOr(0),
+ data.GetByteSize());
+ }
+ return;
+ }
+
+ size_t bit_align =
+ m_variable_sp->GetType()->GetLayoutCompilerType().GetTypeBitAlign();
+ size_t byte_align = (bit_align + 7) / 8;
+
+ if (!byte_align)
+ byte_align = 1;
+
+ Status alloc_error;
+ const bool zero_memory = false;
+
+ m_temporary_allocation = map.Malloc(
+ data.GetByteSize(), byte_align,
+ lldb::ePermissionsReadable | lldb::ePermissionsWritable,
+ IRMemoryMap::eAllocationPolicyMirror, zero_memory, alloc_error);
+
+ m_temporary_allocation_size = data.GetByteSize();
+
+ m_original_data = std::make_shared<DataBufferHeap>(data.GetDataStart(),
+ data.GetByteSize());
+
+ if (!alloc_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't allocate a temporary region for %s: %s",
+ m_variable_sp->GetName().AsCString(), alloc_error.AsCString());
+ return;
+ }
+
+ Status write_error;
+
+ map.WriteMemory(m_temporary_allocation, data.GetDataStart(),
+ data.GetByteSize(), write_error);
+
+ if (!write_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't write to the temporary region for %s: %s",
+ m_variable_sp->GetName().AsCString(), write_error.AsCString());
+ return;
+ }
+
+ Status pointer_write_error;
+
+ map.WritePointerToMemory(load_addr, m_temporary_allocation,
+ pointer_write_error);
+
+ if (!pointer_write_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't write the address of the temporary region for %s: %s",
+ m_variable_sp->GetName().AsCString(),
+ pointer_write_error.AsCString());
+ }
+ }
+ }
+ }
+
+ void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
+ lldb::addr_t process_address, lldb::addr_t frame_top,
+ lldb::addr_t frame_bottom, Status &err) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+ if (log) {
+ log->Printf("EntityVariable::Dematerialize [address = 0x%" PRIx64
+ ", m_variable_sp = %s]",
+ (uint64_t)load_addr, m_variable_sp->GetName().AsCString());
+ }
+
+ if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
+ ExecutionContextScope *scope = frame_sp.get();
+
+ if (!scope)
+ scope = map.GetBestExecutionContextScope();
+
+ lldb::ValueObjectSP valobj_sp =
+ ValueObjectVariable::Create(scope, m_variable_sp);
+
+ if (!valobj_sp) {
+ err.SetErrorStringWithFormat(
+ "couldn't get a value object for variable %s",
+ m_variable_sp->GetName().AsCString());
+ return;
+ }
+
+ lldb_private::DataExtractor data;
+
+ Status extract_error;
+
+ map.GetMemoryData(data, m_temporary_allocation, valobj_sp->GetByteSize(),
+ extract_error);
+
+ if (!extract_error.Success()) {
+ err.SetErrorStringWithFormat("couldn't get the data for variable %s",
+ m_variable_sp->GetName().AsCString());
+ return;
+ }
+
+ bool actually_write = true;
+
+ if (m_original_data) {
+ if ((data.GetByteSize() == m_original_data->GetByteSize()) &&
+ !memcmp(m_original_data->GetBytes(), data.GetDataStart(),
+ data.GetByteSize())) {
+ actually_write = false;
+ }
+ }
+
+ Status set_error;
+
+ if (actually_write) {
+ valobj_sp->SetData(data, set_error);
+
+ if (!set_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't write the new contents of %s back into the variable",
+ m_variable_sp->GetName().AsCString());
+ return;
+ }
+ }
+
+ Status free_error;
+
+ map.Free(m_temporary_allocation, free_error);
+
+ if (!free_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't free the temporary region for %s: %s",
+ m_variable_sp->GetName().AsCString(), free_error.AsCString());
+ return;
+ }
+
+ m_original_data.reset();
+ m_temporary_allocation = LLDB_INVALID_ADDRESS;
+ m_temporary_allocation_size = 0;
+ }
+ }
+
+ void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address,
+ Log *log) override {
+ StreamString dump_stream;
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+ dump_stream.Printf("0x%" PRIx64 ": EntityVariable\n", load_addr);
+
+ Status err;
+
+ lldb::addr_t ptr = LLDB_INVALID_ADDRESS;
+
+ {
+ dump_stream.Printf("Pointer:\n");
+
+ DataBufferHeap data(m_size, 0);
+
+ map.ReadMemory(data.GetBytes(), load_addr, m_size, err);
+
+ if (!err.Success()) {
+ dump_stream.Printf(" <could not be read>\n");
+ } else {
+ DataExtractor extractor(data.GetBytes(), data.GetByteSize(),
+ map.GetByteOrder(), map.GetAddressByteSize());
+
+ DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
+ load_addr);
+
+ lldb::offset_t offset;
+
+ ptr = extractor.GetPointer(&offset);
+
+ dump_stream.PutChar('\n');
+ }
+ }
+
+ if (m_temporary_allocation == LLDB_INVALID_ADDRESS) {
+ dump_stream.Printf("Points to process memory:\n");
+ } else {
+ dump_stream.Printf("Temporary allocation:\n");
+ }
+
+ if (ptr == LLDB_INVALID_ADDRESS) {
+ dump_stream.Printf(" <could not be be found>\n");
+ } else {
+ DataBufferHeap data(m_temporary_allocation_size, 0);
+
+ map.ReadMemory(data.GetBytes(), m_temporary_allocation,
+ m_temporary_allocation_size, err);
+
+ if (!err.Success()) {
+ dump_stream.Printf(" <could not be read>\n");
+ } else {
+ DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
+ load_addr);
+
+ dump_stream.PutChar('\n');
+ }
+ }
+
+ log->PutString(dump_stream.GetString());
+ }
+
+ void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {
+ if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
+ Status free_error;
+
+ map.Free(m_temporary_allocation, free_error);
+
+ m_temporary_allocation = LLDB_INVALID_ADDRESS;
+ m_temporary_allocation_size = 0;
+ }
+ }
+
+private:
+ lldb::VariableSP m_variable_sp;
+ bool m_is_reference;
+ lldb::addr_t m_temporary_allocation;
+ size_t m_temporary_allocation_size;
+ lldb::DataBufferSP m_original_data;
+};
+
+uint32_t Materializer::AddVariable(lldb::VariableSP &variable_sp, Status &err) {
+ EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
+ iter->reset(new EntityVariable(variable_sp));
+ uint32_t ret = AddStructMember(**iter);
+ (*iter)->SetOffset(ret);
+ return ret;
+}
+
+class EntityResultVariable : public Materializer::Entity {
+public:
+ EntityResultVariable(const CompilerType &type, bool is_program_reference,
+ bool keep_in_memory,
+ Materializer::PersistentVariableDelegate *delegate)
+ : Entity(), m_type(type), m_is_program_reference(is_program_reference),
+ m_keep_in_memory(keep_in_memory),
+ m_temporary_allocation(LLDB_INVALID_ADDRESS),
+ m_temporary_allocation_size(0), m_delegate(delegate) {
+ // Hard-coding to maximum size of a pointer since all results are
+ // materialized by reference
+ m_size = 8;
+ m_alignment = 8;
+ }
+
+ void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
+ lldb::addr_t process_address, Status &err) override {
+ if (!m_is_program_reference) {
+ if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
+ err.SetErrorString("Trying to create a temporary region for the result "
+ "but one exists");
+ return;
+ }
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ ExecutionContextScope *exe_scope = map.GetBestExecutionContextScope();
+
+ llvm::Optional<uint64_t> byte_size = m_type.GetByteSize(exe_scope);
+ if (!byte_size) {
+ err.SetErrorString("can't get size of type");
+ return;
+ }
+ size_t bit_align = m_type.GetTypeBitAlign();
+ size_t byte_align = (bit_align + 7) / 8;
+
+ if (!byte_align)
+ byte_align = 1;
+
+ Status alloc_error;
+ const bool zero_memory = true;
+
+ m_temporary_allocation = map.Malloc(
+ *byte_size, byte_align,
+ lldb::ePermissionsReadable | lldb::ePermissionsWritable,
+ IRMemoryMap::eAllocationPolicyMirror, zero_memory, alloc_error);
+ m_temporary_allocation_size = *byte_size;
+
+ if (!alloc_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't allocate a temporary region for the result: %s",
+ alloc_error.AsCString());
+ return;
+ }
+
+ Status pointer_write_error;
+
+ map.WritePointerToMemory(load_addr, m_temporary_allocation,
+ pointer_write_error);
+
+ if (!pointer_write_error.Success()) {
+ err.SetErrorStringWithFormat("couldn't write the address of the "
+ "temporary region for the result: %s",
+ pointer_write_error.AsCString());
+ }
+ }
+ }
+
+ void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
+ lldb::addr_t process_address, lldb::addr_t frame_top,
+ lldb::addr_t frame_bottom, Status &err) override {
+ err.Clear();
+
+ ExecutionContextScope *exe_scope = map.GetBestExecutionContextScope();
+
+ if (!exe_scope) {
+ err.SetErrorString("Couldn't dematerialize a result variable: invalid "
+ "execution context scope");
+ return;
+ }
+
+ lldb::addr_t address;
+ Status read_error;
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ map.ReadPointerFromMemory(&address, load_addr, read_error);
+
+ if (!read_error.Success()) {
+ err.SetErrorString("Couldn't dematerialize a result variable: couldn't "
+ "read its address");
+ return;
+ }
+
+ lldb::TargetSP target_sp = exe_scope->CalculateTarget();
+
+ if (!target_sp) {
+ err.SetErrorString("Couldn't dematerialize a result variable: no target");
+ return;
+ }
+
+ Status type_system_error;
+ TypeSystem *type_system = target_sp->GetScratchTypeSystemForLanguage(
+ &type_system_error, m_type.GetMinimumLanguage());
+
+ if (!type_system) {
+ err.SetErrorStringWithFormat("Couldn't dematerialize a result variable: "
+ "couldn't get the corresponding type "
+ "system: %s",
+ type_system_error.AsCString());
+ return;
+ }
+
+ PersistentExpressionState *persistent_state =
+ type_system->GetPersistentExpressionState();
+
+ if (!persistent_state) {
+ err.SetErrorString("Couldn't dematerialize a result variable: "
+ "corresponding type system doesn't handle persistent "
+ "variables");
+ return;
+ }
+
+ ConstString name =
+ m_delegate
+ ? m_delegate->GetName()
+ : persistent_state->GetNextPersistentVariableName(
+ *target_sp, persistent_state->GetPersistentVariablePrefix());
+
+ lldb::ExpressionVariableSP ret = persistent_state->CreatePersistentVariable(
+ exe_scope, name, m_type, map.GetByteOrder(), map.GetAddressByteSize());
+
+ if (!ret) {
+ err.SetErrorStringWithFormat("couldn't dematerialize a result variable: "
+ "failed to make persistent variable %s",
+ name.AsCString());
+ return;
+ }
+
+ lldb::ProcessSP process_sp =
+ map.GetBestExecutionContextScope()->CalculateProcess();
+
+ if (m_delegate) {
+ m_delegate->DidDematerialize(ret);
+ }
+
+ bool can_persist =
+ (m_is_program_reference && process_sp && process_sp->CanJIT() &&
+ !(address >= frame_bottom && address < frame_top));
+
+ if (can_persist && m_keep_in_memory) {
+ ret->m_live_sp = ValueObjectConstResult::Create(exe_scope, m_type, name,
+ address, eAddressTypeLoad,
+ map.GetAddressByteSize());
+ }
+
+ ret->ValueUpdated();
+
+ const size_t pvar_byte_size = ret->GetByteSize();
+ uint8_t *pvar_data = ret->GetValueBytes();
+
+ map.ReadMemory(pvar_data, address, pvar_byte_size, read_error);
+
+ if (!read_error.Success()) {
+ err.SetErrorString(
+ "Couldn't dematerialize a result variable: couldn't read its memory");
+ return;
+ }
+
+ if (!can_persist || !m_keep_in_memory) {
+ ret->m_flags |= ExpressionVariable::EVNeedsAllocation;
+
+ if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
+ Status free_error;
+ map.Free(m_temporary_allocation, free_error);
+ }
+ } else {
+ ret->m_flags |= ExpressionVariable::EVIsLLDBAllocated;
+ }
+
+ m_temporary_allocation = LLDB_INVALID_ADDRESS;
+ m_temporary_allocation_size = 0;
+ }
+
+ void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address,
+ Log *log) override {
+ StreamString dump_stream;
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ dump_stream.Printf("0x%" PRIx64 ": EntityResultVariable\n", load_addr);
+
+ Status err;
+
+ lldb::addr_t ptr = LLDB_INVALID_ADDRESS;
+
+ {
+ dump_stream.Printf("Pointer:\n");
+
+ DataBufferHeap data(m_size, 0);
+
+ map.ReadMemory(data.GetBytes(), load_addr, m_size, err);
+
+ if (!err.Success()) {
+ dump_stream.Printf(" <could not be read>\n");
+ } else {
+ DataExtractor extractor(data.GetBytes(), data.GetByteSize(),
+ map.GetByteOrder(), map.GetAddressByteSize());
+
+ DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
+ load_addr);
+
+ lldb::offset_t offset;
+
+ ptr = extractor.GetPointer(&offset);
+
+ dump_stream.PutChar('\n');
+ }
+ }
+
+ if (m_temporary_allocation == LLDB_INVALID_ADDRESS) {
+ dump_stream.Printf("Points to process memory:\n");
+ } else {
+ dump_stream.Printf("Temporary allocation:\n");
+ }
+
+ if (ptr == LLDB_INVALID_ADDRESS) {
+ dump_stream.Printf(" <could not be be found>\n");
+ } else {
+ DataBufferHeap data(m_temporary_allocation_size, 0);
+
+ map.ReadMemory(data.GetBytes(), m_temporary_allocation,
+ m_temporary_allocation_size, err);
+
+ if (!err.Success()) {
+ dump_stream.Printf(" <could not be read>\n");
+ } else {
+ DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
+ load_addr);
+
+ dump_stream.PutChar('\n');
+ }
+ }
+
+ log->PutString(dump_stream.GetString());
+ }
+
+ void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {
+ if (!m_keep_in_memory && m_temporary_allocation != LLDB_INVALID_ADDRESS) {
+ Status free_error;
+
+ map.Free(m_temporary_allocation, free_error);
+ }
+
+ m_temporary_allocation = LLDB_INVALID_ADDRESS;
+ m_temporary_allocation_size = 0;
+ }
+
+private:
+ CompilerType m_type;
+ bool m_is_program_reference;
+ bool m_keep_in_memory;
+
+ lldb::addr_t m_temporary_allocation;
+ size_t m_temporary_allocation_size;
+ Materializer::PersistentVariableDelegate *m_delegate;
+};
+
+uint32_t Materializer::AddResultVariable(const CompilerType &type,
+ bool is_program_reference,
+ bool keep_in_memory,
+ PersistentVariableDelegate *delegate,
+ Status &err) {
+ EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
+ iter->reset(new EntityResultVariable(type, is_program_reference,
+ keep_in_memory, delegate));
+ uint32_t ret = AddStructMember(**iter);
+ (*iter)->SetOffset(ret);
+ return ret;
+}
+
+class EntitySymbol : public Materializer::Entity {
+public:
+ EntitySymbol(const Symbol &symbol) : Entity(), m_symbol(symbol) {
+ // Hard-coding to maximum size of a symbol
+ m_size = 8;
+ m_alignment = 8;
+ }
+
+ void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
+ lldb::addr_t process_address, Status &err) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ if (log) {
+ log->Printf("EntitySymbol::Materialize [address = 0x%" PRIx64
+ ", m_symbol = %s]",
+ (uint64_t)load_addr, m_symbol.GetName().AsCString());
+ }
+
+ const Address sym_address = m_symbol.GetAddress();
+
+ ExecutionContextScope *exe_scope = map.GetBestExecutionContextScope();
+
+ lldb::TargetSP target_sp;
+
+ if (exe_scope)
+ target_sp = map.GetBestExecutionContextScope()->CalculateTarget();
+
+ if (!target_sp) {
+ err.SetErrorStringWithFormat(
+ "couldn't resolve symbol %s because there is no target",
+ m_symbol.GetName().AsCString());
+ return;
+ }
+
+ lldb::addr_t resolved_address = sym_address.GetLoadAddress(target_sp.get());
+
+ if (resolved_address == LLDB_INVALID_ADDRESS)
+ resolved_address = sym_address.GetFileAddress();
+
+ Status pointer_write_error;
+
+ map.WritePointerToMemory(load_addr, resolved_address, pointer_write_error);
+
+ if (!pointer_write_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't write the address of symbol %s: %s",
+ m_symbol.GetName().AsCString(), pointer_write_error.AsCString());
+ return;
+ }
+ }
+
+ void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
+ lldb::addr_t process_address, lldb::addr_t frame_top,
+ lldb::addr_t frame_bottom, Status &err) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ if (log) {
+ log->Printf("EntitySymbol::Dematerialize [address = 0x%" PRIx64
+ ", m_symbol = %s]",
+ (uint64_t)load_addr, m_symbol.GetName().AsCString());
+ }
+
+ // no work needs to be done
+ }
+
+ void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address,
+ Log *log) override {
+ StreamString dump_stream;
+
+ Status err;
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ dump_stream.Printf("0x%" PRIx64 ": EntitySymbol (%s)\n", load_addr,
+ m_symbol.GetName().AsCString());
+
+ {
+ dump_stream.Printf("Pointer:\n");
+
+ DataBufferHeap data(m_size, 0);
+
+ map.ReadMemory(data.GetBytes(), load_addr, m_size, err);
+
+ if (!err.Success()) {
+ dump_stream.Printf(" <could not be read>\n");
+ } else {
+ DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
+ load_addr);
+
+ dump_stream.PutChar('\n');
+ }
+ }
+
+ log->PutString(dump_stream.GetString());
+ }
+
+ void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {}
+
+private:
+ Symbol m_symbol;
+};
+
+uint32_t Materializer::AddSymbol(const Symbol &symbol_sp, Status &err) {
+ EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
+ iter->reset(new EntitySymbol(symbol_sp));
+ uint32_t ret = AddStructMember(**iter);
+ (*iter)->SetOffset(ret);
+ return ret;
+}
+
+class EntityRegister : public Materializer::Entity {
+public:
+ EntityRegister(const RegisterInfo &register_info)
+ : Entity(), m_register_info(register_info) {
+ // Hard-coding alignment conservatively
+ m_size = m_register_info.byte_size;
+ m_alignment = m_register_info.byte_size;
+ }
+
+ void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
+ lldb::addr_t process_address, Status &err) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ if (log) {
+ log->Printf("EntityRegister::Materialize [address = 0x%" PRIx64
+ ", m_register_info = %s]",
+ (uint64_t)load_addr, m_register_info.name);
+ }
+
+ RegisterValue reg_value;
+
+ if (!frame_sp.get()) {
+ err.SetErrorStringWithFormat(
+ "couldn't materialize register %s without a stack frame",
+ m_register_info.name);
+ return;
+ }
+
+ lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext();
+
+ if (!reg_context_sp->ReadRegister(&m_register_info, reg_value)) {
+ err.SetErrorStringWithFormat("couldn't read the value of register %s",
+ m_register_info.name);
+ return;
+ }
+
+ DataExtractor register_data;
+
+ if (!reg_value.GetData(register_data)) {
+ err.SetErrorStringWithFormat("couldn't get the data for register %s",
+ m_register_info.name);
+ return;
+ }
+
+ if (register_data.GetByteSize() != m_register_info.byte_size) {
+ err.SetErrorStringWithFormat(
+ "data for register %s had size %llu but we expected %llu",
+ m_register_info.name, (unsigned long long)register_data.GetByteSize(),
+ (unsigned long long)m_register_info.byte_size);
+ return;
+ }
+
+ m_register_contents = std::make_shared<DataBufferHeap>(
+ register_data.GetDataStart(), register_data.GetByteSize());
+
+ Status write_error;
+
+ map.WriteMemory(load_addr, register_data.GetDataStart(),
+ register_data.GetByteSize(), write_error);
+
+ if (!write_error.Success()) {
+ err.SetErrorStringWithFormat(
+ "couldn't write the contents of register %s: %s",
+ m_register_info.name, write_error.AsCString());
+ return;
+ }
+ }
+
+ void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
+ lldb::addr_t process_address, lldb::addr_t frame_top,
+ lldb::addr_t frame_bottom, Status &err) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ if (log) {
+ log->Printf("EntityRegister::Dematerialize [address = 0x%" PRIx64
+ ", m_register_info = %s]",
+ (uint64_t)load_addr, m_register_info.name);
+ }
+
+ Status extract_error;
+
+ DataExtractor register_data;
+
+ if (!frame_sp.get()) {
+ err.SetErrorStringWithFormat(
+ "couldn't dematerialize register %s without a stack frame",
+ m_register_info.name);
+ return;
+ }
+
+ lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext();
+
+ map.GetMemoryData(register_data, load_addr, m_register_info.byte_size,
+ extract_error);
+
+ if (!extract_error.Success()) {
+ err.SetErrorStringWithFormat("couldn't get the data for register %s: %s",
+ m_register_info.name,
+ extract_error.AsCString());
+ return;
+ }
+
+ if (!memcmp(register_data.GetDataStart(), m_register_contents->GetBytes(),
+ register_data.GetByteSize())) {
+ // No write required, and in particular we avoid errors if the register
+ // wasn't writable
+
+ m_register_contents.reset();
+ return;
+ }
+
+ m_register_contents.reset();
+
+ RegisterValue register_value(
+ const_cast<uint8_t *>(register_data.GetDataStart()),
+ register_data.GetByteSize(), register_data.GetByteOrder());
+
+ if (!reg_context_sp->WriteRegister(&m_register_info, register_value)) {
+ err.SetErrorStringWithFormat("couldn't write the value of register %s",
+ m_register_info.name);
+ return;
+ }
+ }
+
+ void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address,
+ Log *log) override {
+ StreamString dump_stream;
+
+ Status err;
+
+ const lldb::addr_t load_addr = process_address + m_offset;
+
+ dump_stream.Printf("0x%" PRIx64 ": EntityRegister (%s)\n", load_addr,
+ m_register_info.name);
+
+ {
+ dump_stream.Printf("Value:\n");
+
+ DataBufferHeap data(m_size, 0);
+
+ map.ReadMemory(data.GetBytes(), load_addr, m_size, err);
+
+ if (!err.Success()) {
+ dump_stream.Printf(" <could not be read>\n");
+ } else {
+ DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
+ load_addr);
+
+ dump_stream.PutChar('\n');
+ }
+ }
+
+ log->PutString(dump_stream.GetString());
+ }
+
+ void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {}
+
+private:
+ RegisterInfo m_register_info;
+ lldb::DataBufferSP m_register_contents;
+};
+
+uint32_t Materializer::AddRegister(const RegisterInfo &register_info,
+ Status &err) {
+ EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
+ iter->reset(new EntityRegister(register_info));
+ uint32_t ret = AddStructMember(**iter);
+ (*iter)->SetOffset(ret);
+ return ret;
+}
+
+Materializer::Materializer()
+ : m_dematerializer_wp(), m_current_offset(0), m_struct_alignment(8) {}
+
+Materializer::~Materializer() {
+ DematerializerSP dematerializer_sp = m_dematerializer_wp.lock();
+
+ if (dematerializer_sp)
+ dematerializer_sp->Wipe();
+}
+
+Materializer::DematerializerSP
+Materializer::Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
+ lldb::addr_t process_address, Status &error) {
+ ExecutionContextScope *exe_scope = frame_sp.get();
+
+ if (!exe_scope)
+ exe_scope = map.GetBestExecutionContextScope();
+
+ DematerializerSP dematerializer_sp = m_dematerializer_wp.lock();
+
+ if (dematerializer_sp) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't materialize: already materialized");
+ }
+
+ DematerializerSP ret(
+ new Dematerializer(*this, frame_sp, map, process_address));
+
+ if (!exe_scope) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't materialize: target doesn't exist");
+ }
+
+ for (EntityUP &entity_up : m_entities) {
+ entity_up->Materialize(frame_sp, map, process_address, error);
+
+ if (!error.Success())
+ return DematerializerSP();
+ }
+
+ if (Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)) {
+ log->Printf(
+ "Materializer::Materialize (frame_sp = %p, process_address = 0x%" PRIx64
+ ") materialized:",
+ static_cast<void *>(frame_sp.get()), process_address);
+ for (EntityUP &entity_up : m_entities)
+ entity_up->DumpToLog(map, process_address, log);
+ }
+
+ m_dematerializer_wp = ret;
+
+ return ret;
+}
+
+void Materializer::Dematerializer::Dematerialize(Status &error,
+ lldb::addr_t frame_bottom,
+ lldb::addr_t frame_top) {
+ lldb::StackFrameSP frame_sp;
+
+ lldb::ThreadSP thread_sp = m_thread_wp.lock();
+ if (thread_sp)
+ frame_sp = thread_sp->GetFrameWithStackID(m_stack_id);
+
+ ExecutionContextScope *exe_scope = m_map->GetBestExecutionContextScope();
+
+ if (!IsValid()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't dematerialize: invalid dematerializer");
+ }
+
+ if (!exe_scope) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Couldn't dematerialize: target is gone");
+ } else {
+ if (Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)) {
+ log->Printf("Materializer::Dematerialize (frame_sp = %p, process_address "
+ "= 0x%" PRIx64 ") about to dematerialize:",
+ static_cast<void *>(frame_sp.get()), m_process_address);
+ for (EntityUP &entity_up : m_materializer->m_entities)
+ entity_up->DumpToLog(*m_map, m_process_address, log);
+ }
+
+ for (EntityUP &entity_up : m_materializer->m_entities) {
+ entity_up->Dematerialize(frame_sp, *m_map, m_process_address, frame_top,
+ frame_bottom, error);
+
+ if (!error.Success())
+ break;
+ }
+ }
+
+ Wipe();
+}
+
+void Materializer::Dematerializer::Wipe() {
+ if (!IsValid())
+ return;
+
+ for (EntityUP &entity_up : m_materializer->m_entities) {
+ entity_up->Wipe(*m_map, m_process_address);
+ }
+
+ m_materializer = nullptr;
+ m_map = nullptr;
+ m_process_address = LLDB_INVALID_ADDRESS;
+}
+
+Materializer::PersistentVariableDelegate::~PersistentVariableDelegate() =
+ default;
diff --git a/contrib/llvm-project/lldb/source/Expression/REPL.cpp b/contrib/llvm-project/lldb/source/Expression/REPL.cpp
new file mode 100644
index 000000000000..f4ed887729d8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/REPL.cpp
@@ -0,0 +1,562 @@
+//===-- REPL.cpp ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Expression/REPL.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/AnsiTerminal.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+REPL::REPL(LLVMCastKind kind, Target &target) : m_target(target), m_kind(kind) {
+ // Make sure all option values have sane defaults
+ Debugger &debugger = m_target.GetDebugger();
+ auto exe_ctx = debugger.GetCommandInterpreter().GetExecutionContext();
+ m_format_options.OptionParsingStarting(&exe_ctx);
+ m_varobj_options.OptionParsingStarting(&exe_ctx);
+}
+
+REPL::~REPL() = default;
+
+lldb::REPLSP REPL::Create(Status &err, lldb::LanguageType language,
+ Debugger *debugger, Target *target,
+ const char *repl_options) {
+ uint32_t idx = 0;
+ lldb::REPLSP ret;
+
+ while (REPLCreateInstance create_instance =
+ PluginManager::GetREPLCreateCallbackAtIndex(idx++)) {
+ ret = (*create_instance)(err, language, debugger, target, repl_options);
+ if (ret) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+std::string REPL::GetSourcePath() {
+ ConstString file_basename = GetSourceFileBasename();
+ FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
+ if (tmpdir_file_spec) {
+ tmpdir_file_spec.GetFilename().SetCString(file_basename.AsCString());
+ m_repl_source_path = tmpdir_file_spec.GetPath();
+ } else {
+ tmpdir_file_spec = FileSpec("/tmp");
+ tmpdir_file_spec.AppendPathComponent(file_basename.AsCString());
+ }
+
+ return tmpdir_file_spec.GetPath();
+}
+
+lldb::IOHandlerSP REPL::GetIOHandler() {
+ if (!m_io_handler_sp) {
+ Debugger &debugger = m_target.GetDebugger();
+ m_io_handler_sp = std::make_shared<IOHandlerEditline>(
+ debugger, IOHandler::Type::REPL,
+ "lldb-repl", // Name of input reader for history
+ llvm::StringRef("> "), // prompt
+ llvm::StringRef(". "), // Continuation prompt
+ true, // Multi-line
+ true, // The REPL prompt is always colored
+ 1, // Line number
+ *this, nullptr);
+
+ // Don't exit if CTRL+C is pressed
+ static_cast<IOHandlerEditline *>(m_io_handler_sp.get())
+ ->SetInterruptExits(false);
+
+ if (m_io_handler_sp->GetIsInteractive() &&
+ m_io_handler_sp->GetIsRealTerminal()) {
+ m_indent_str.assign(debugger.GetTabSize(), ' ');
+ m_enable_auto_indent = debugger.GetAutoIndent();
+ } else {
+ m_indent_str.clear();
+ m_enable_auto_indent = false;
+ }
+ }
+ return m_io_handler_sp;
+}
+
+void REPL::IOHandlerActivated(IOHandler &io_handler, bool interactive) {
+ lldb::ProcessSP process_sp = m_target.GetProcessSP();
+ if (process_sp && process_sp->IsAlive())
+ return;
+ lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile());
+ error_sp->Printf("REPL requires a running target process.\n");
+ io_handler.SetIsDone(true);
+}
+
+bool REPL::IOHandlerInterrupt(IOHandler &io_handler) { return false; }
+
+void REPL::IOHandlerInputInterrupted(IOHandler &io_handler, std::string &line) {
+}
+
+const char *REPL::IOHandlerGetFixIndentationCharacters() {
+ return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr);
+}
+
+ConstString REPL::IOHandlerGetControlSequence(char ch) {
+ if (ch == 'd')
+ return ConstString(":quit\n");
+ return ConstString();
+}
+
+const char *REPL::IOHandlerGetCommandPrefix() { return ":"; }
+
+const char *REPL::IOHandlerGetHelpPrologue() {
+ return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter. "
+ "Valid statements, expressions, and declarations are immediately "
+ "compiled and executed.\n\n"
+ "The complete set of LLDB debugging commands are also available as "
+ "described below. Commands "
+ "must be prefixed with a colon at the REPL prompt (:quit for "
+ "example.) Typing just a colon "
+ "followed by return will switch to the LLDB prompt.\n\n";
+}
+
+bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) {
+ // Check for meta command
+ const size_t num_lines = lines.GetSize();
+ if (num_lines == 1) {
+ const char *first_line = lines.GetStringAtIndex(0);
+ if (first_line[0] == ':')
+ return true; // Meta command is a single line where that starts with ':'
+ }
+
+ // Check if REPL input is done
+ std::string source_string(lines.CopyList());
+ return SourceIsComplete(source_string);
+}
+
+int REPL::CalculateActualIndentation(const StringList &lines) {
+ std::string last_line = lines[lines.GetSize() - 1];
+
+ int actual_indent = 0;
+ for (char &ch : last_line) {
+ if (ch != ' ')
+ break;
+ ++actual_indent;
+ }
+
+ return actual_indent;
+}
+
+int REPL::IOHandlerFixIndentation(IOHandler &io_handler,
+ const StringList &lines,
+ int cursor_position) {
+ if (!m_enable_auto_indent)
+ return 0;
+
+ if (!lines.GetSize()) {
+ return 0;
+ }
+
+ int tab_size = io_handler.GetDebugger().GetTabSize();
+
+ lldb::offset_t desired_indent =
+ GetDesiredIndentation(lines, cursor_position, tab_size);
+
+ int actual_indent = REPL::CalculateActualIndentation(lines);
+
+ if (desired_indent == LLDB_INVALID_OFFSET)
+ return 0;
+
+ return (int)desired_indent - actual_indent;
+}
+
+void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) {
+ lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile());
+ bool extra_line = false;
+ bool did_quit = false;
+
+ if (code.empty()) {
+ m_code.AppendString("");
+ static_cast<IOHandlerEditline &>(io_handler)
+ .SetBaseLineNumber(m_code.GetSize() + 1);
+ } else {
+ Debugger &debugger = m_target.GetDebugger();
+ CommandInterpreter &ci = debugger.GetCommandInterpreter();
+ extra_line = ci.GetSpaceReplPrompts();
+
+ ExecutionContext exe_ctx(m_target.GetProcessSP()
+ ->GetThreadList()
+ .GetSelectedThread()
+ ->GetSelectedFrame()
+ .get());
+
+ lldb::ProcessSP process_sp(exe_ctx.GetProcessSP());
+
+ if (code[0] == ':') {
+ // Meta command
+ // Strip the ':'
+ code.erase(0, 1);
+ if (Args::StripSpaces(code)) {
+ // "lldb" was followed by arguments, so just execute the command dump
+ // the results
+
+ // Turn off prompt on quit in case the user types ":quit"
+ const bool saved_prompt_on_quit = ci.GetPromptOnQuit();
+ if (saved_prompt_on_quit)
+ ci.SetPromptOnQuit(false);
+
+ // Execute the command
+ CommandReturnObject result;
+ result.SetImmediateOutputStream(output_sp);
+ result.SetImmediateErrorStream(error_sp);
+ ci.HandleCommand(code.c_str(), eLazyBoolNo, result);
+
+ if (saved_prompt_on_quit)
+ ci.SetPromptOnQuit(true);
+
+ if (result.GetStatus() == lldb::eReturnStatusQuit) {
+ did_quit = true;
+ io_handler.SetIsDone(true);
+ if (debugger.CheckTopIOHandlerTypes(
+ IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
+ // We typed "quit" or an alias to quit so we need to check if the
+ // command interpreter is above us and tell it that it is done as
+ // well so we don't drop back into the command interpreter if we
+ // have already quit
+ lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
+ if (io_handler_sp)
+ io_handler_sp->SetIsDone(true);
+ }
+ }
+ } else {
+ // ":" was followed by no arguments, so push the LLDB command prompt
+ if (debugger.CheckTopIOHandlerTypes(
+ IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
+ // If the user wants to get back to the command interpreter and the
+ // command interpreter is what launched the REPL, then just let the
+ // REPL exit and fall back to the command interpreter.
+ io_handler.SetIsDone(true);
+ } else {
+ // The REPL wasn't launched the by the command interpreter, it is the
+ // base IOHandler, so we need to get the command interpreter and
+ lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
+ if (io_handler_sp) {
+ io_handler_sp->SetIsDone(false);
+ debugger.PushIOHandler(ci.GetIOHandler());
+ }
+ }
+ }
+ } else {
+ // Unwind any expression we might have been running in case our REPL
+ // expression crashed and the user was looking around
+ if (m_dedicated_repl_mode) {
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (thread && thread->UnwindInnermostExpression().Success()) {
+ thread->SetSelectedFrameByIndex(0, false);
+ exe_ctx.SetFrameSP(thread->GetSelectedFrame());
+ }
+ }
+
+ const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
+
+ EvaluateExpressionOptions expr_options = m_expr_options;
+ expr_options.SetCoerceToId(m_varobj_options.use_objc);
+ expr_options.SetKeepInMemory(true);
+ expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
+ expr_options.SetGenerateDebugInfo(true);
+ expr_options.SetREPLEnabled(true);
+ expr_options.SetColorizeErrors(colorize_err);
+ expr_options.SetPoundLine(m_repl_source_path.c_str(),
+ m_code.GetSize() + 1);
+
+ expr_options.SetLanguage(GetLanguage());
+
+ PersistentExpressionState *persistent_state =
+ m_target.GetPersistentExpressionStateForLanguage(GetLanguage());
+
+ const size_t var_count_before = persistent_state->GetSize();
+
+ const char *expr_prefix = nullptr;
+ lldb::ValueObjectSP result_valobj_sp;
+ Status error;
+ lldb::ModuleSP jit_module_sp;
+ lldb::ExpressionResults execution_results =
+ UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(),
+ expr_prefix, result_valobj_sp, error,
+ nullptr, // Fixed Expression
+ &jit_module_sp);
+
+ // CommandInterpreter &ci = debugger.GetCommandInterpreter();
+
+ if (process_sp && process_sp->IsAlive()) {
+ bool add_to_code = true;
+ bool handled = false;
+ if (result_valobj_sp) {
+ lldb::Format format = m_format_options.GetFormat();
+
+ if (result_valobj_sp->GetError().Success()) {
+ handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp);
+ } else if (result_valobj_sp->GetError().GetError() ==
+ UserExpression::kNoResult) {
+ if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) {
+ error_sp->PutCString("(void)\n");
+ handled = true;
+ }
+ }
+ }
+
+ if (debugger.GetPrintDecls()) {
+ for (size_t vi = var_count_before, ve = persistent_state->GetSize();
+ vi != ve; ++vi) {
+ lldb::ExpressionVariableSP persistent_var_sp =
+ persistent_state->GetVariableAtIndex(vi);
+ lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject();
+
+ PrintOneVariable(debugger, output_sp, valobj_sp,
+ persistent_var_sp.get());
+ }
+ }
+
+ if (!handled) {
+ bool useColors = error_sp->GetFile().GetIsTerminalWithColors();
+ switch (execution_results) {
+ case lldb::eExpressionSetupError:
+ case lldb::eExpressionParseError:
+ add_to_code = false;
+ LLVM_FALLTHROUGH;
+ case lldb::eExpressionDiscarded:
+ error_sp->Printf("%s\n", error.AsCString());
+ break;
+
+ case lldb::eExpressionCompleted:
+ break;
+ case lldb::eExpressionInterrupted:
+ if (useColors) {
+ error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
+ error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
+ }
+ error_sp->Printf("Execution interrupted. ");
+ if (useColors)
+ error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
+ error_sp->Printf("Enter code to recover and continue.\nEnter LLDB "
+ "commands to investigate (type :help for "
+ "assistance.)\n");
+ break;
+
+ case lldb::eExpressionHitBreakpoint:
+ // Breakpoint was hit, drop into LLDB command interpreter
+ if (useColors) {
+ error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
+ error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
+ }
+ output_sp->Printf("Execution stopped at breakpoint. ");
+ if (useColors)
+ error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
+ output_sp->Printf("Enter LLDB commands to investigate (type help "
+ "for assistance.)\n");
+ {
+ lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
+ if (io_handler_sp) {
+ io_handler_sp->SetIsDone(false);
+ debugger.PushIOHandler(ci.GetIOHandler());
+ }
+ }
+ break;
+
+ case lldb::eExpressionTimedOut:
+ error_sp->Printf("error: timeout\n");
+ if (error.AsCString())
+ error_sp->Printf("error: %s\n", error.AsCString());
+ break;
+ case lldb::eExpressionResultUnavailable:
+ // Shoulnd't happen???
+ error_sp->Printf("error: could not fetch result -- %s\n",
+ error.AsCString());
+ break;
+ case lldb::eExpressionStoppedForDebug:
+ // Shoulnd't happen???
+ error_sp->Printf("error: stopped for debug -- %s\n",
+ error.AsCString());
+ break;
+ }
+ }
+
+ if (add_to_code) {
+ const uint32_t new_default_line = m_code.GetSize() + 1;
+
+ m_code.SplitIntoLines(code);
+
+ // Update our code on disk
+ if (!m_repl_source_path.empty()) {
+ lldb_private::File file;
+ FileSystem::Instance().Open(file, FileSpec(m_repl_source_path),
+ File::eOpenOptionWrite |
+ File::eOpenOptionTruncate |
+ File::eOpenOptionCanCreate,
+ lldb::eFilePermissionsFileDefault);
+ std::string code(m_code.CopyList());
+ code.append(1, '\n');
+ size_t bytes_written = code.size();
+ file.Write(code.c_str(), bytes_written);
+ file.Close();
+
+ // Now set the default file and line to the REPL source file
+ m_target.GetSourceManager().SetDefaultFileAndLine(
+ FileSpec(m_repl_source_path), new_default_line);
+ }
+ static_cast<IOHandlerEditline &>(io_handler)
+ .SetBaseLineNumber(m_code.GetSize() + 1);
+ }
+ if (extra_line) {
+ fprintf(output_sp->GetFile().GetStream(), "\n");
+ }
+ }
+ }
+
+ // Don't complain about the REPL process going away if we are in the
+ // process of quitting.
+ if (!did_quit && (!process_sp || !process_sp->IsAlive())) {
+ error_sp->Printf(
+ "error: REPL process is no longer alive, exiting REPL\n");
+ io_handler.SetIsDone(true);
+ }
+ }
+}
+
+int REPL::IOHandlerComplete(IOHandler &io_handler, const char *current_line,
+ const char *cursor, const char *last_char,
+ int skip_first_n_matches, int max_matches,
+ StringList &matches, StringList &descriptions) {
+ matches.Clear();
+
+ llvm::StringRef line(current_line, cursor - current_line);
+
+ // Complete an LLDB command if the first character is a colon...
+ if (!line.empty() && line[0] == ':') {
+ Debugger &debugger = m_target.GetDebugger();
+
+ // auto complete LLDB commands
+ const char *lldb_current_line = line.substr(1).data();
+ return debugger.GetCommandInterpreter().HandleCompletion(
+ lldb_current_line, cursor, last_char, skip_first_n_matches, max_matches,
+ matches, descriptions);
+ }
+
+ // Strip spaces from the line and see if we had only spaces
+ line = line.ltrim();
+ if (line.empty()) {
+ // Only spaces on this line, so just indent
+ matches.AppendString(m_indent_str);
+ return 1;
+ }
+
+ std::string current_code;
+ current_code.append(m_code.CopyList());
+
+ IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler);
+ const StringList *current_lines = editline.GetCurrentLines();
+ if (current_lines) {
+ const uint32_t current_line_idx = editline.GetCurrentLineIndex();
+
+ if (current_line_idx < current_lines->GetSize()) {
+ for (uint32_t i = 0; i < current_line_idx; ++i) {
+ const char *line_cstr = current_lines->GetStringAtIndex(i);
+ if (line_cstr) {
+ current_code.append("\n");
+ current_code.append(line_cstr);
+ }
+ }
+ }
+ }
+
+ if (cursor > current_line) {
+ current_code.append("\n");
+ current_code.append(current_line, cursor - current_line);
+ }
+
+ return CompleteCode(current_code, matches);
+}
+
+bool QuitCommandOverrideCallback(void *baton, const char **argv) {
+ Target *target = (Target *)baton;
+ lldb::ProcessSP process_sp(target->GetProcessSP());
+ if (process_sp) {
+ process_sp->Destroy(false);
+ process_sp->GetTarget().GetDebugger().ClearIOHandlers();
+ }
+ return false;
+}
+
+Status REPL::RunLoop() {
+ Status error;
+
+ error = DoInitialization();
+ m_repl_source_path = GetSourcePath();
+
+ if (!error.Success())
+ return error;
+
+ Debugger &debugger = m_target.GetDebugger();
+
+ lldb::IOHandlerSP io_handler_sp(GetIOHandler());
+
+ FileSpec save_default_file;
+ uint32_t save_default_line = 0;
+
+ if (!m_repl_source_path.empty()) {
+ // Save the current default file and line
+ m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file,
+ save_default_line);
+ }
+
+ debugger.PushIOHandler(io_handler_sp);
+
+ // Check if we are in dedicated REPL mode where LLDB was start with the "--
+ // repl" option from the command line. Currently we know this by checking if
+ // the debugger already has a IOHandler thread.
+ if (!debugger.HasIOHandlerThread()) {
+ // The debugger doesn't have an existing IOHandler thread, so this must be
+ // dedicated REPL mode...
+ m_dedicated_repl_mode = true;
+ debugger.StartIOHandlerThread();
+ llvm::StringRef command_name_str("quit");
+ CommandObject *cmd_obj =
+ debugger.GetCommandInterpreter().GetCommandObjectForCommand(
+ command_name_str);
+ if (cmd_obj) {
+ assert(command_name_str.empty());
+ cmd_obj->SetOverrideCallback(QuitCommandOverrideCallback, &m_target);
+ }
+ }
+
+ // Wait for the REPL command interpreter to get popped
+ io_handler_sp->WaitForPop();
+
+ if (m_dedicated_repl_mode) {
+ // If we were in dedicated REPL mode we would have started the IOHandler
+ // thread, and we should kill our process
+ lldb::ProcessSP process_sp = m_target.GetProcessSP();
+ if (process_sp && process_sp->IsAlive())
+ process_sp->Destroy(false);
+
+ // Wait for the IO handler thread to exit (TODO: don't do this if the IO
+ // handler thread already exists...)
+ debugger.JoinIOHandlerThread();
+ }
+
+ // Restore the default file and line
+ if (save_default_file && save_default_line != 0)
+ m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file,
+ save_default_line);
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Expression/UserExpression.cpp b/contrib/llvm-project/lldb/source/Expression/UserExpression.cpp
new file mode 100644
index 000000000000..a72e2a07599e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/UserExpression.cpp
@@ -0,0 +1,400 @@
+//===-- UserExpression.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <cstdlib>
+#include <map>
+#include <string>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Expression/IRInterpreter.h"
+#include "lldb/Expression/Materializer.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallUserExpression.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+
+UserExpression::UserExpression(ExecutionContextScope &exe_scope,
+ llvm::StringRef expr, llvm::StringRef prefix,
+ lldb::LanguageType language,
+ ResultType desired_type,
+ const EvaluateExpressionOptions &options,
+ ExpressionKind kind)
+ : Expression(exe_scope, kind), m_expr_text(expr), m_expr_prefix(prefix),
+ m_language(language), m_desired_type(desired_type), m_options(options) {}
+
+UserExpression::~UserExpression() {}
+
+void UserExpression::InstallContext(ExecutionContext &exe_ctx) {
+ m_jit_process_wp = exe_ctx.GetProcessSP();
+
+ lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP();
+
+ if (frame_sp)
+ m_address = frame_sp->GetFrameCodeAddress();
+}
+
+bool UserExpression::LockAndCheckContext(ExecutionContext &exe_ctx,
+ lldb::TargetSP &target_sp,
+ lldb::ProcessSP &process_sp,
+ lldb::StackFrameSP &frame_sp) {
+ lldb::ProcessSP expected_process_sp = m_jit_process_wp.lock();
+ process_sp = exe_ctx.GetProcessSP();
+
+ if (process_sp != expected_process_sp)
+ return false;
+
+ process_sp = exe_ctx.GetProcessSP();
+ target_sp = exe_ctx.GetTargetSP();
+ frame_sp = exe_ctx.GetFrameSP();
+
+ if (m_address.IsValid()) {
+ if (!frame_sp)
+ return false;
+ else
+ return (0 == Address::CompareLoadAddress(m_address,
+ frame_sp->GetFrameCodeAddress(),
+ target_sp.get()));
+ }
+
+ return true;
+}
+
+bool UserExpression::MatchesContext(ExecutionContext &exe_ctx) {
+ lldb::TargetSP target_sp;
+ lldb::ProcessSP process_sp;
+ lldb::StackFrameSP frame_sp;
+
+ return LockAndCheckContext(exe_ctx, target_sp, process_sp, frame_sp);
+}
+
+lldb::addr_t UserExpression::GetObjectPointer(lldb::StackFrameSP frame_sp,
+ ConstString &object_name,
+ Status &err) {
+ err.Clear();
+
+ if (!frame_sp) {
+ err.SetErrorStringWithFormat(
+ "Couldn't load '%s' because the context is incomplete",
+ object_name.AsCString());
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ lldb::VariableSP var_sp;
+ lldb::ValueObjectSP valobj_sp;
+
+ valobj_sp = frame_sp->GetValueForVariableExpressionPath(
+ object_name.AsCString(), lldb::eNoDynamicValues,
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsNoFragileObjcIvar |
+ StackFrame::eExpressionPathOptionsNoSyntheticChildren |
+ StackFrame::eExpressionPathOptionsNoSyntheticArrayRange,
+ var_sp, err);
+
+ if (!err.Success() || !valobj_sp.get())
+ return LLDB_INVALID_ADDRESS;
+
+ lldb::addr_t ret = valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+
+ if (ret == LLDB_INVALID_ADDRESS) {
+ err.SetErrorStringWithFormat(
+ "Couldn't load '%s' because its value couldn't be evaluated",
+ object_name.AsCString());
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ return ret;
+}
+
+lldb::ExpressionResults UserExpression::Evaluate(
+ ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options,
+ llvm::StringRef expr, llvm::StringRef prefix,
+ lldb::ValueObjectSP &result_valobj_sp, Status &error,
+ std::string *fixed_expression, lldb::ModuleSP *jit_module_sp_ptr,
+ ValueObject *ctx_obj) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EXPRESSIONS |
+ LIBLLDB_LOG_STEP));
+
+ if (ctx_obj) {
+ static unsigned const ctx_type_mask =
+ lldb::TypeFlags::eTypeIsClass | lldb::TypeFlags::eTypeIsStructUnion;
+ if (!(ctx_obj->GetTypeInfo() & ctx_type_mask)) {
+ LLDB_LOG(log, "== [UserExpression::Evaluate] Passed a context object of "
+ "an invalid type, can't run expressions.");
+ error.SetErrorString("a context object of an invalid type passed");
+ return lldb::eExpressionSetupError;
+ }
+ }
+
+ lldb_private::ExecutionPolicy execution_policy = options.GetExecutionPolicy();
+ lldb::LanguageType language = options.GetLanguage();
+ const ResultType desired_type = options.DoesCoerceToId()
+ ? UserExpression::eResultTypeId
+ : UserExpression::eResultTypeAny;
+ lldb::ExpressionResults execution_results = lldb::eExpressionSetupError;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (!target) {
+ if (log)
+ log->Printf("== [UserExpression::Evaluate] Passed a NULL target, can't "
+ "run expressions.");
+ error.SetErrorString("expression passed a null target");
+ return lldb::eExpressionSetupError;
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (process == nullptr || process->GetState() != lldb::eStateStopped) {
+ if (execution_policy == eExecutionPolicyAlways) {
+ if (log)
+ log->Printf("== [UserExpression::Evaluate] Expression may not run, but "
+ "is not constant ==");
+
+ error.SetErrorString("expression needed to run but couldn't");
+
+ return execution_results;
+ }
+ }
+
+ if (process == nullptr || !process->CanJIT())
+ execution_policy = eExecutionPolicyNever;
+
+ // We need to set the expression execution thread here, turns out parse can
+ // call functions in the process of looking up symbols, which will escape the
+ // context set by exe_ctx passed to Execute.
+ lldb::ThreadSP thread_sp = exe_ctx.GetThreadSP();
+ ThreadList::ExpressionExecutionThreadPusher execution_thread_pusher(
+ thread_sp);
+
+ llvm::StringRef full_prefix;
+ llvm::StringRef option_prefix(options.GetPrefix());
+ std::string full_prefix_storage;
+ if (!prefix.empty() && !option_prefix.empty()) {
+ full_prefix_storage = prefix;
+ full_prefix_storage.append(option_prefix);
+ full_prefix = full_prefix_storage;
+ } else if (!prefix.empty())
+ full_prefix = prefix;
+ else
+ full_prefix = option_prefix;
+
+ // If the language was not specified in the expression command, set it to the
+ // language in the target's properties if specified, else default to the
+ // langage for the frame.
+ if (language == lldb::eLanguageTypeUnknown) {
+ if (target->GetLanguage() != lldb::eLanguageTypeUnknown)
+ language = target->GetLanguage();
+ else if (StackFrame *frame = exe_ctx.GetFramePtr())
+ language = frame->GetLanguage();
+ }
+
+ lldb::UserExpressionSP user_expression_sp(
+ target->GetUserExpressionForLanguage(expr, full_prefix, language,
+ desired_type, options, ctx_obj,
+ error));
+ if (error.Fail()) {
+ if (log)
+ log->Printf("== [UserExpression::Evaluate] Getting expression: %s ==",
+ error.AsCString());
+ return lldb::eExpressionSetupError;
+ }
+
+ if (log)
+ log->Printf("== [UserExpression::Evaluate] Parsing expression %s ==",
+ expr.str().c_str());
+
+ const bool keep_expression_in_memory = true;
+ const bool generate_debug_info = options.GetGenerateDebugInfo();
+
+ if (options.InvokeCancelCallback(lldb::eExpressionEvaluationParse)) {
+ error.SetErrorString("expression interrupted by callback before parse");
+ result_valobj_sp = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), error);
+ return lldb::eExpressionInterrupted;
+ }
+
+ DiagnosticManager diagnostic_manager;
+
+ bool parse_success =
+ user_expression_sp->Parse(diagnostic_manager, exe_ctx, execution_policy,
+ keep_expression_in_memory, generate_debug_info);
+
+ // Calculate the fixed expression always, since we need it for errors.
+ std::string tmp_fixed_expression;
+ if (fixed_expression == nullptr)
+ fixed_expression = &tmp_fixed_expression;
+
+ const char *fixed_text = user_expression_sp->GetFixedText();
+ if (fixed_text != nullptr)
+ fixed_expression->append(fixed_text);
+
+ // If there is a fixed expression, try to parse it:
+ if (!parse_success) {
+ execution_results = lldb::eExpressionParseError;
+ if (fixed_expression && !fixed_expression->empty() &&
+ options.GetAutoApplyFixIts()) {
+ lldb::UserExpressionSP fixed_expression_sp(
+ target->GetUserExpressionForLanguage(fixed_expression->c_str(),
+ full_prefix, language,
+ desired_type, options, ctx_obj,
+ error));
+ DiagnosticManager fixed_diagnostic_manager;
+ parse_success = fixed_expression_sp->Parse(
+ fixed_diagnostic_manager, exe_ctx, execution_policy,
+ keep_expression_in_memory, generate_debug_info);
+ if (parse_success) {
+ diagnostic_manager.Clear();
+ user_expression_sp = fixed_expression_sp;
+ } else {
+ // If the fixed expression failed to parse, don't tell the user about,
+ // that won't help.
+ fixed_expression->clear();
+ }
+ }
+
+ if (!parse_success) {
+ if (!fixed_expression->empty() && target->GetEnableNotifyAboutFixIts()) {
+ error.SetExpressionErrorWithFormat(
+ execution_results,
+ "expression failed to parse, fixed expression suggested:\n %s",
+ fixed_expression->c_str());
+ } else {
+ if (!diagnostic_manager.Diagnostics().size())
+ error.SetExpressionError(execution_results,
+ "expression failed to parse, unknown error");
+ else
+ error.SetExpressionError(execution_results,
+ diagnostic_manager.GetString().c_str());
+ }
+ }
+ }
+
+ if (parse_success) {
+ // If a pointer to a lldb::ModuleSP was passed in, return the JIT'ed module
+ // if one was created
+ if (jit_module_sp_ptr)
+ *jit_module_sp_ptr = user_expression_sp->GetJITModule();
+
+ lldb::ExpressionVariableSP expr_result;
+
+ if (execution_policy == eExecutionPolicyNever &&
+ !user_expression_sp->CanInterpret()) {
+ if (log)
+ log->Printf("== [UserExpression::Evaluate] Expression may not run, but "
+ "is not constant ==");
+
+ if (!diagnostic_manager.Diagnostics().size())
+ error.SetExpressionError(lldb::eExpressionSetupError,
+ "expression needed to run but couldn't");
+ } else if (execution_policy == eExecutionPolicyTopLevel) {
+ error.SetError(UserExpression::kNoResult, lldb::eErrorTypeGeneric);
+ return lldb::eExpressionCompleted;
+ } else {
+ if (options.InvokeCancelCallback(lldb::eExpressionEvaluationExecution)) {
+ error.SetExpressionError(
+ lldb::eExpressionInterrupted,
+ "expression interrupted by callback before execution");
+ result_valobj_sp = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), error);
+ return lldb::eExpressionInterrupted;
+ }
+
+ diagnostic_manager.Clear();
+
+ if (log)
+ log->Printf("== [UserExpression::Evaluate] Executing expression ==");
+
+ execution_results =
+ user_expression_sp->Execute(diagnostic_manager, exe_ctx, options,
+ user_expression_sp, expr_result);
+
+ if (execution_results != lldb::eExpressionCompleted) {
+ if (log)
+ log->Printf("== [UserExpression::Evaluate] Execution completed "
+ "abnormally ==");
+
+ if (!diagnostic_manager.Diagnostics().size())
+ error.SetExpressionError(
+ execution_results, "expression failed to execute, unknown error");
+ else
+ error.SetExpressionError(execution_results,
+ diagnostic_manager.GetString().c_str());
+ } else {
+ if (expr_result) {
+ result_valobj_sp = expr_result->GetValueObject();
+
+ if (log)
+ log->Printf("== [UserExpression::Evaluate] Execution completed "
+ "normally with result %s ==",
+ result_valobj_sp->GetValueAsCString());
+ } else {
+ if (log)
+ log->Printf("== [UserExpression::Evaluate] Execution completed "
+ "normally with no result ==");
+
+ error.SetError(UserExpression::kNoResult, lldb::eErrorTypeGeneric);
+ }
+ }
+ }
+ }
+
+ if (options.InvokeCancelCallback(lldb::eExpressionEvaluationComplete)) {
+ error.SetExpressionError(
+ lldb::eExpressionInterrupted,
+ "expression interrupted by callback after complete");
+ return lldb::eExpressionInterrupted;
+ }
+
+ if (result_valobj_sp.get() == nullptr) {
+ result_valobj_sp = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), error);
+ }
+
+ return execution_results;
+}
+
+lldb::ExpressionResults
+UserExpression::Execute(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx,
+ const EvaluateExpressionOptions &options,
+ lldb::UserExpressionSP &shared_ptr_to_me,
+ lldb::ExpressionVariableSP &result_var) {
+ lldb::ExpressionResults expr_result = DoExecute(
+ diagnostic_manager, exe_ctx, options, shared_ptr_to_me, result_var);
+ Target *target = exe_ctx.GetTargetPtr();
+ if (options.GetResultIsInternal() && result_var && target) {
+ target->GetPersistentExpressionStateForLanguage(m_language)
+ ->RemovePersistentVariable(result_var);
+ }
+ return expr_result;
+}
diff --git a/contrib/llvm-project/lldb/source/Expression/UtilityFunction.cpp b/contrib/llvm-project/lldb/source/Expression/UtilityFunction.cpp
new file mode 100644
index 000000000000..eeaeca18da86
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Expression/UtilityFunction.cpp
@@ -0,0 +1,108 @@
+//===-- UtilityFunction.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+/// Constructor
+///
+/// \param[in] text
+/// The text of the function. Must be a full translation unit.
+///
+/// \param[in] name
+/// The name of the function, as used in the text.
+UtilityFunction::UtilityFunction(ExecutionContextScope &exe_scope,
+ const char *text, const char *name,
+ ExpressionKind kind)
+ : Expression(exe_scope, kind),
+ m_execution_unit_sp(), m_jit_module_wp(),
+ m_function_text(),
+ m_function_name(name) {}
+
+UtilityFunction::~UtilityFunction() {
+ lldb::ProcessSP process_sp(m_jit_process_wp.lock());
+ if (process_sp) {
+ lldb::ModuleSP jit_module_sp(m_jit_module_wp.lock());
+ if (jit_module_sp)
+ process_sp->GetTarget().GetImages().Remove(jit_module_sp);
+ }
+}
+
+// FIXME: We should check that every time this is called it is called with the
+// same return type & arguments...
+
+FunctionCaller *UtilityFunction::MakeFunctionCaller(
+ const CompilerType &return_type, const ValueList &arg_value_list,
+ lldb::ThreadSP thread_to_use_sp, Status &error) {
+ if (m_caller_up)
+ return m_caller_up.get();
+
+ ProcessSP process_sp = m_jit_process_wp.lock();
+ if (!process_sp) {
+ error.SetErrorString("Can't make a function caller without a process.");
+ return nullptr;
+ }
+
+ Address impl_code_address;
+ impl_code_address.SetOffset(StartAddress());
+ std::string name(m_function_name);
+ name.append("-caller");
+
+ m_caller_up.reset(process_sp->GetTarget().GetFunctionCallerForLanguage(
+ Language(), return_type, impl_code_address, arg_value_list, name.c_str(),
+ error));
+ if (error.Fail()) {
+
+ return nullptr;
+ }
+ if (m_caller_up) {
+ DiagnosticManager diagnostics;
+
+ unsigned num_errors =
+ m_caller_up->CompileFunction(thread_to_use_sp, diagnostics);
+ if (num_errors) {
+ error.SetErrorStringWithFormat(
+ "Error compiling %s caller function: \"%s\".",
+ m_function_name.c_str(), diagnostics.GetString().c_str());
+ m_caller_up.reset();
+ return nullptr;
+ }
+
+ diagnostics.Clear();
+ ExecutionContext exe_ctx(process_sp);
+
+ if (!m_caller_up->WriteFunctionWrapper(exe_ctx, diagnostics)) {
+ error.SetErrorStringWithFormat(
+ "Error inserting caller function for %s: \"%s\".",
+ m_function_name.c_str(), diagnostics.GetString().c_str());
+ m_caller_up.reset();
+ return nullptr;
+ }
+ }
+ return m_caller_up.get();
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/Editline.cpp b/contrib/llvm-project/lldb/source/Host/common/Editline.cpp
new file mode 100644
index 000000000000..d3a70aeaa326
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/Editline.cpp
@@ -0,0 +1,1422 @@
+//===-- Editline.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <iomanip>
+#include <iostream>
+#include <limits.h>
+
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/Editline.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/SelectHelper.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringList.h"
+#include "lldb/Utility/Timeout.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb_private;
+using namespace lldb_private::line_editor;
+
+// Workaround for what looks like an OS X-specific issue, but other platforms
+// may benefit from something similar if issues arise. The libedit library
+// doesn't explicitly initialize the curses termcap library, which it gets away
+// with until TERM is set to VT100 where it stumbles over an implementation
+// assumption that may not exist on other platforms. The setupterm() function
+// would normally require headers that don't work gracefully in this context,
+// so the function declaraction has been hoisted here.
+#if defined(__APPLE__)
+extern "C" {
+int setupterm(char *term, int fildes, int *errret);
+}
+#define USE_SETUPTERM_WORKAROUND
+#endif
+
+// Editline uses careful cursor management to achieve the illusion of editing a
+// multi-line block of text with a single line editor. Preserving this
+// illusion requires fairly careful management of cursor state. Read and
+// understand the relationship between DisplayInput(), MoveCursor(),
+// SetCurrentLine(), and SaveEditedLine() before making changes.
+
+#define ESCAPE "\x1b"
+#define ANSI_FAINT ESCAPE "[2m"
+#define ANSI_UNFAINT ESCAPE "[22m"
+#define ANSI_CLEAR_BELOW ESCAPE "[J"
+#define ANSI_CLEAR_RIGHT ESCAPE "[K"
+#define ANSI_SET_COLUMN_N ESCAPE "[%dG"
+#define ANSI_UP_N_ROWS ESCAPE "[%dA"
+#define ANSI_DOWN_N_ROWS ESCAPE "[%dB"
+
+#if LLDB_EDITLINE_USE_WCHAR
+
+#define EditLineConstString(str) L##str
+#define EditLineStringFormatSpec "%ls"
+
+#else
+
+#define EditLineConstString(str) str
+#define EditLineStringFormatSpec "%s"
+
+// use #defines so wide version functions and structs will resolve to old
+// versions for case of libedit not built with wide char support
+#define history_w history
+#define history_winit history_init
+#define history_wend history_end
+#define HistoryW History
+#define HistEventW HistEvent
+#define LineInfoW LineInfo
+
+#define el_wgets el_gets
+#define el_wgetc el_getc
+#define el_wpush el_push
+#define el_wparse el_parse
+#define el_wset el_set
+#define el_wget el_get
+#define el_wline el_line
+#define el_winsertstr el_insertstr
+#define el_wdeletestr el_deletestr
+
+#endif // #if LLDB_EDITLINE_USE_WCHAR
+
+bool IsOnlySpaces(const EditLineStringType &content) {
+ for (wchar_t ch : content) {
+ if (ch != EditLineCharType(' '))
+ return false;
+ }
+ return true;
+}
+
+EditLineStringType CombineLines(const std::vector<EditLineStringType> &lines) {
+ EditLineStringStreamType combined_stream;
+ for (EditLineStringType line : lines) {
+ combined_stream << line.c_str() << "\n";
+ }
+ return combined_stream.str();
+}
+
+std::vector<EditLineStringType> SplitLines(const EditLineStringType &input) {
+ std::vector<EditLineStringType> result;
+ size_t start = 0;
+ while (start < input.length()) {
+ size_t end = input.find('\n', start);
+ if (end == std::string::npos) {
+ result.insert(result.end(), input.substr(start));
+ break;
+ }
+ result.insert(result.end(), input.substr(start, end - start));
+ start = end + 1;
+ }
+ return result;
+}
+
+EditLineStringType FixIndentation(const EditLineStringType &line,
+ int indent_correction) {
+ if (indent_correction == 0)
+ return line;
+ if (indent_correction < 0)
+ return line.substr(-indent_correction);
+ return EditLineStringType(indent_correction, EditLineCharType(' ')) + line;
+}
+
+int GetIndentation(const EditLineStringType &line) {
+ int space_count = 0;
+ for (EditLineCharType ch : line) {
+ if (ch != EditLineCharType(' '))
+ break;
+ ++space_count;
+ }
+ return space_count;
+}
+
+bool IsInputPending(FILE *file) {
+ // FIXME: This will be broken on Windows if we ever re-enable Editline. You
+ // can't use select
+ // on something that isn't a socket. This will have to be re-written to not
+ // use a FILE*, but instead use some kind of yet-to-be-created abstraction
+ // that select-like functionality on non-socket objects.
+ const int fd = fileno(file);
+ SelectHelper select_helper;
+ select_helper.SetTimeout(std::chrono::microseconds(0));
+ select_helper.FDSetRead(fd);
+ return select_helper.Select().Success();
+}
+
+namespace lldb_private {
+namespace line_editor {
+typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
+
+// EditlineHistory objects are sometimes shared between multiple Editline
+// instances with the same program name.
+
+class EditlineHistory {
+private:
+ // Use static GetHistory() function to get a EditlineHistorySP to one of
+ // these objects
+ EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries)
+ : m_history(nullptr), m_event(), m_prefix(prefix), m_path() {
+ m_history = history_winit();
+ history_w(m_history, &m_event, H_SETSIZE, size);
+ if (unique_entries)
+ history_w(m_history, &m_event, H_SETUNIQUE, 1);
+ }
+
+ const char *GetHistoryFilePath() {
+ // Compute the history path lazily.
+ if (m_path.empty() && m_history && !m_prefix.empty()) {
+ llvm::SmallString<128> lldb_history_file;
+ llvm::sys::path::home_directory(lldb_history_file);
+ llvm::sys::path::append(lldb_history_file, ".lldb");
+
+ // LLDB stores its history in ~/.lldb/. If for some reason this directory
+ // isn't writable or cannot be created, history won't be available.
+ if (!llvm::sys::fs::create_directory(lldb_history_file)) {
+#if LLDB_EDITLINE_USE_WCHAR
+ std::string filename = m_prefix + "-widehistory";
+#else
+ std::string filename = m_prefix + "-history";
+#endif
+ llvm::sys::path::append(lldb_history_file, filename);
+ m_path = lldb_history_file.str();
+ }
+ }
+
+ if (m_path.empty())
+ return nullptr;
+
+ return m_path.c_str();
+ }
+
+public:
+ ~EditlineHistory() {
+ Save();
+
+ if (m_history) {
+ history_wend(m_history);
+ m_history = nullptr;
+ }
+ }
+
+ static EditlineHistorySP GetHistory(const std::string &prefix) {
+ typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
+ static std::recursive_mutex g_mutex;
+ static WeakHistoryMap g_weak_map;
+ std::lock_guard<std::recursive_mutex> guard(g_mutex);
+ WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix);
+ EditlineHistorySP history_sp;
+ if (pos != g_weak_map.end()) {
+ history_sp = pos->second.lock();
+ if (history_sp)
+ return history_sp;
+ g_weak_map.erase(pos);
+ }
+ history_sp.reset(new EditlineHistory(prefix, 800, true));
+ g_weak_map[prefix] = history_sp;
+ return history_sp;
+ }
+
+ bool IsValid() const { return m_history != nullptr; }
+
+ HistoryW *GetHistoryPtr() { return m_history; }
+
+ void Enter(const EditLineCharType *line_cstr) {
+ if (m_history)
+ history_w(m_history, &m_event, H_ENTER, line_cstr);
+ }
+
+ bool Load() {
+ if (m_history) {
+ const char *path = GetHistoryFilePath();
+ if (path) {
+ history_w(m_history, &m_event, H_LOAD, path);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool Save() {
+ if (m_history) {
+ const char *path = GetHistoryFilePath();
+ if (path) {
+ history_w(m_history, &m_event, H_SAVE, path);
+ return true;
+ }
+ }
+ return false;
+ }
+
+protected:
+ HistoryW *m_history; // The history object
+ HistEventW m_event; // The history event needed to contain all history events
+ std::string m_prefix; // The prefix name (usually the editline program name)
+ // to use when loading/saving history
+ std::string m_path; // Path to the history file
+};
+}
+}
+
+// Editline private methods
+
+void Editline::SetBaseLineNumber(int line_number) {
+ std::stringstream line_number_stream;
+ line_number_stream << line_number;
+ m_base_line_number = line_number;
+ m_line_number_digits =
+ std::max(3, (int)line_number_stream.str().length() + 1);
+}
+
+std::string Editline::PromptForIndex(int line_index) {
+ bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0;
+ std::string prompt = m_set_prompt;
+ if (use_line_numbers && prompt.length() == 0) {
+ prompt = ": ";
+ }
+ std::string continuation_prompt = prompt;
+ if (m_set_continuation_prompt.length() > 0) {
+ continuation_prompt = m_set_continuation_prompt;
+
+ // Ensure that both prompts are the same length through space padding
+ while (continuation_prompt.length() < prompt.length()) {
+ continuation_prompt += ' ';
+ }
+ while (prompt.length() < continuation_prompt.length()) {
+ prompt += ' ';
+ }
+ }
+
+ if (use_line_numbers) {
+ StreamString prompt_stream;
+ prompt_stream.Printf(
+ "%*d%s", m_line_number_digits, m_base_line_number + line_index,
+ (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str());
+ return std::move(prompt_stream.GetString());
+ }
+ return (line_index == 0) ? prompt : continuation_prompt;
+}
+
+void Editline::SetCurrentLine(int line_index) {
+ m_current_line_index = line_index;
+ m_current_prompt = PromptForIndex(line_index);
+}
+
+int Editline::GetPromptWidth() { return (int)PromptForIndex(0).length(); }
+
+bool Editline::IsEmacs() {
+ const char *editor;
+ el_get(m_editline, EL_EDITOR, &editor);
+ return editor[0] == 'e';
+}
+
+bool Editline::IsOnlySpaces() {
+ const LineInfoW *info = el_wline(m_editline);
+ for (const EditLineCharType *character = info->buffer;
+ character < info->lastchar; character++) {
+ if (*character != ' ')
+ return false;
+ }
+ return true;
+}
+
+int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) {
+ int line = 0;
+ if (location == CursorLocation::EditingPrompt ||
+ location == CursorLocation::BlockEnd ||
+ location == CursorLocation::EditingCursor) {
+ for (unsigned index = 0; index < m_current_line_index; index++) {
+ line += CountRowsForLine(m_input_lines[index]);
+ }
+ if (location == CursorLocation::EditingCursor) {
+ line += cursor_row;
+ } else if (location == CursorLocation::BlockEnd) {
+ for (unsigned index = m_current_line_index; index < m_input_lines.size();
+ index++) {
+ line += CountRowsForLine(m_input_lines[index]);
+ }
+ --line;
+ }
+ }
+ return line;
+}
+
+void Editline::MoveCursor(CursorLocation from, CursorLocation to) {
+ const LineInfoW *info = el_wline(m_editline);
+ int editline_cursor_position =
+ (int)((info->cursor - info->buffer) + GetPromptWidth());
+ int editline_cursor_row = editline_cursor_position / m_terminal_width;
+
+ // Determine relative starting and ending lines
+ int fromLine = GetLineIndexForLocation(from, editline_cursor_row);
+ int toLine = GetLineIndexForLocation(to, editline_cursor_row);
+ if (toLine != fromLine) {
+ fprintf(m_output_file,
+ (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS,
+ std::abs(toLine - fromLine));
+ }
+
+ // Determine target column
+ int toColumn = 1;
+ if (to == CursorLocation::EditingCursor) {
+ toColumn =
+ editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1;
+ } else if (to == CursorLocation::BlockEnd && !m_input_lines.empty()) {
+ toColumn =
+ ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) %
+ 80) +
+ 1;
+ }
+ fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn);
+}
+
+void Editline::DisplayInput(int firstIndex) {
+ fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1);
+ int line_count = (int)m_input_lines.size();
+ const char *faint = m_color_prompts ? ANSI_FAINT : "";
+ const char *unfaint = m_color_prompts ? ANSI_UNFAINT : "";
+
+ for (int index = firstIndex; index < line_count; index++) {
+ fprintf(m_output_file, "%s"
+ "%s"
+ "%s" EditLineStringFormatSpec " ",
+ faint, PromptForIndex(index).c_str(), unfaint,
+ m_input_lines[index].c_str());
+ if (index < line_count - 1)
+ fprintf(m_output_file, "\n");
+ }
+}
+
+int Editline::CountRowsForLine(const EditLineStringType &content) {
+ auto prompt =
+ PromptForIndex(0); // Prompt width is constant during an edit session
+ int line_length = (int)(content.length() + prompt.length());
+ return (line_length / m_terminal_width) + 1;
+}
+
+void Editline::SaveEditedLine() {
+ const LineInfoW *info = el_wline(m_editline);
+ m_input_lines[m_current_line_index] =
+ EditLineStringType(info->buffer, info->lastchar - info->buffer);
+}
+
+StringList Editline::GetInputAsStringList(int line_count) {
+ StringList lines;
+ for (EditLineStringType line : m_input_lines) {
+ if (line_count == 0)
+ break;
+#if LLDB_EDITLINE_USE_WCHAR
+ lines.AppendString(m_utf8conv.to_bytes(line));
+#else
+ lines.AppendString(line);
+#endif
+ --line_count;
+ }
+ return lines;
+}
+
+unsigned char Editline::RecallHistory(bool earlier) {
+ if (!m_history_sp || !m_history_sp->IsValid())
+ return CC_ERROR;
+
+ HistoryW *pHistory = m_history_sp->GetHistoryPtr();
+ HistEventW history_event;
+ std::vector<EditLineStringType> new_input_lines;
+
+ // Treat moving from the "live" entry differently
+ if (!m_in_history) {
+ if (!earlier)
+ return CC_ERROR; // Can't go newer than the "live" entry
+ if (history_w(pHistory, &history_event, H_FIRST) == -1)
+ return CC_ERROR;
+
+ // Save any edits to the "live" entry in case we return by moving forward
+ // in history (it would be more bash-like to save over any current entry,
+ // but libedit doesn't offer the ability to add entries anywhere except the
+ // end.)
+ SaveEditedLine();
+ m_live_history_lines = m_input_lines;
+ m_in_history = true;
+ } else {
+ if (history_w(pHistory, &history_event, earlier ? H_PREV : H_NEXT) == -1) {
+ // Can't move earlier than the earliest entry
+ if (earlier)
+ return CC_ERROR;
+
+ // ... but moving to newer than the newest yields the "live" entry
+ new_input_lines = m_live_history_lines;
+ m_in_history = false;
+ }
+ }
+
+ // If we're pulling the lines from history, split them apart
+ if (m_in_history)
+ new_input_lines = SplitLines(history_event.str);
+
+ // Erase the current edit session and replace it with a new one
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
+ m_input_lines = new_input_lines;
+ DisplayInput();
+
+ // Prepare to edit the last line when moving to previous entry, or the first
+ // line when moving to next entry
+ SetCurrentLine(m_current_line_index =
+ earlier ? (int)m_input_lines.size() - 1 : 0);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+ return CC_NEWLINE;
+}
+
+int Editline::GetCharacter(EditLineGetCharType *c) {
+ const LineInfoW *info = el_wline(m_editline);
+
+ // Paint a faint version of the desired prompt over the version libedit draws
+ // (will only be requested if colors are supported)
+ if (m_needs_prompt_repaint) {
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+ fprintf(m_output_file, "%s"
+ "%s"
+ "%s",
+ ANSI_FAINT, Prompt(), ANSI_UNFAINT);
+ MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor);
+ m_needs_prompt_repaint = false;
+ }
+
+ if (m_multiline_enabled) {
+ // Detect when the number of rows used for this input line changes due to
+ // an edit
+ int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth());
+ int new_line_rows = (lineLength / m_terminal_width) + 1;
+ if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) {
+ // Respond by repainting the current state from this line on
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+ SaveEditedLine();
+ DisplayInput(m_current_line_index);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
+ }
+ m_current_line_rows = new_line_rows;
+ }
+
+ // Read an actual character
+ while (true) {
+ lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
+ char ch = 0;
+
+ // This mutex is locked by our caller (GetLine). Unlock it while we read a
+ // character (blocking operation), so we do not hold the mutex
+ // indefinitely. This gives a chance for someone to interrupt us. After
+ // Read returns, immediately lock the mutex again and check if we were
+ // interrupted.
+ m_output_mutex.unlock();
+ int read_count =
+ m_input_connection.Read(&ch, 1, llvm::None, status, nullptr);
+ m_output_mutex.lock();
+ if (m_editor_status == EditorStatus::Interrupted) {
+ while (read_count > 0 && status == lldb::eConnectionStatusSuccess)
+ read_count =
+ m_input_connection.Read(&ch, 1, llvm::None, status, nullptr);
+ lldbassert(status == lldb::eConnectionStatusInterrupted);
+ return 0;
+ }
+
+ if (read_count) {
+ if (CompleteCharacter(ch, *c))
+ return 1;
+ } else {
+ switch (status) {
+ case lldb::eConnectionStatusSuccess: // Success
+ break;
+
+ case lldb::eConnectionStatusInterrupted:
+ llvm_unreachable("Interrupts should have been handled above.");
+
+ case lldb::eConnectionStatusError: // Check GetError() for details
+ case lldb::eConnectionStatusTimedOut: // Request timed out
+ case lldb::eConnectionStatusEndOfFile: // End-of-file encountered
+ case lldb::eConnectionStatusNoConnection: // No connection
+ case lldb::eConnectionStatusLostConnection: // Lost connection while
+ // connected to a valid
+ // connection
+ m_editor_status = EditorStatus::EndOfInput;
+ return 0;
+ }
+ }
+ }
+}
+
+const char *Editline::Prompt() {
+ if (m_color_prompts)
+ m_needs_prompt_repaint = true;
+ return m_current_prompt.c_str();
+}
+
+unsigned char Editline::BreakLineCommand(int ch) {
+ // Preserve any content beyond the cursor, truncate and save the current line
+ const LineInfoW *info = el_wline(m_editline);
+ auto current_line =
+ EditLineStringType(info->buffer, info->cursor - info->buffer);
+ auto new_line_fragment =
+ EditLineStringType(info->cursor, info->lastchar - info->cursor);
+ m_input_lines[m_current_line_index] = current_line;
+
+ // Ignore whitespace-only extra fragments when breaking a line
+ if (::IsOnlySpaces(new_line_fragment))
+ new_line_fragment = EditLineConstString("");
+
+ // Establish the new cursor position at the start of a line when inserting a
+ // line break
+ m_revert_cursor_index = 0;
+
+ // Don't perform automatic formatting when pasting
+ if (!IsInputPending(m_input_file)) {
+ // Apply smart indentation
+ if (m_fix_indentation_callback) {
+ StringList lines = GetInputAsStringList(m_current_line_index + 1);
+#if LLDB_EDITLINE_USE_WCHAR
+ lines.AppendString(m_utf8conv.to_bytes(new_line_fragment));
+#else
+ lines.AppendString(new_line_fragment);
+#endif
+
+ int indent_correction = m_fix_indentation_callback(
+ this, lines, 0, m_fix_indentation_callback_baton);
+ new_line_fragment = FixIndentation(new_line_fragment, indent_correction);
+ m_revert_cursor_index = GetIndentation(new_line_fragment);
+ }
+ }
+
+ // Insert the new line and repaint everything from the split line on down
+ m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1,
+ new_line_fragment);
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+ DisplayInput(m_current_line_index);
+
+ // Reposition the cursor to the right line and prepare to edit the new line
+ SetCurrentLine(m_current_line_index + 1);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::EndOrAddLineCommand(int ch) {
+ // Don't perform end of input detection when pasting, always treat this as a
+ // line break
+ if (IsInputPending(m_input_file)) {
+ return BreakLineCommand(ch);
+ }
+
+ // Save any edits to this line
+ SaveEditedLine();
+
+ // If this is the end of the last line, consider whether to add a line
+ // instead
+ const LineInfoW *info = el_wline(m_editline);
+ if (m_current_line_index == m_input_lines.size() - 1 &&
+ info->cursor == info->lastchar) {
+ if (m_is_input_complete_callback) {
+ auto lines = GetInputAsStringList();
+ if (!m_is_input_complete_callback(this, lines,
+ m_is_input_complete_callback_baton)) {
+ return BreakLineCommand(ch);
+ }
+
+ // The completion test is allowed to change the input lines when complete
+ m_input_lines.clear();
+ for (unsigned index = 0; index < lines.GetSize(); index++) {
+#if LLDB_EDITLINE_USE_WCHAR
+ m_input_lines.insert(m_input_lines.end(),
+ m_utf8conv.from_bytes(lines[index]));
+#else
+ m_input_lines.insert(m_input_lines.end(), lines[index]);
+#endif
+ }
+ }
+ }
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
+ fprintf(m_output_file, "\n");
+ m_editor_status = EditorStatus::Complete;
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::DeleteNextCharCommand(int ch) {
+ LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
+
+ // Just delete the next character normally if possible
+ if (info->cursor < info->lastchar) {
+ info->cursor++;
+ el_deletestr(m_editline, 1);
+ return CC_REFRESH;
+ }
+
+ // Fail when at the end of the last line, except when ^D is pressed on the
+ // line is empty, in which case it is treated as EOF
+ if (m_current_line_index == m_input_lines.size() - 1) {
+ if (ch == 4 && info->buffer == info->lastchar) {
+ fprintf(m_output_file, "^D\n");
+ m_editor_status = EditorStatus::EndOfInput;
+ return CC_EOF;
+ }
+ return CC_ERROR;
+ }
+
+ // Prepare to combine this line with the one below
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+
+ // Insert the next line of text at the cursor and restore the cursor position
+ const EditLineCharType *cursor = info->cursor;
+ el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str());
+ info->cursor = cursor;
+ SaveEditedLine();
+
+ // Delete the extra line
+ m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1);
+
+ // Clear and repaint from this line on down
+ DisplayInput(m_current_line_index);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
+ return CC_REFRESH;
+}
+
+unsigned char Editline::DeletePreviousCharCommand(int ch) {
+ LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
+
+ // Just delete the previous character normally when not at the start of a
+ // line
+ if (info->cursor > info->buffer) {
+ el_deletestr(m_editline, 1);
+ return CC_REFRESH;
+ }
+
+ // No prior line and no prior character? Let the user know
+ if (m_current_line_index == 0)
+ return CC_ERROR;
+
+ // No prior character, but prior line? Combine with the line above
+ SaveEditedLine();
+ SetCurrentLine(m_current_line_index - 1);
+ auto priorLine = m_input_lines[m_current_line_index];
+ m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
+ m_input_lines[m_current_line_index] =
+ priorLine + m_input_lines[m_current_line_index];
+
+ // Repaint from the new line down
+ fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
+ CountRowsForLine(priorLine), 1);
+ DisplayInput(m_current_line_index);
+
+ // Put the cursor back where libedit expects it to be before returning to
+ // editing by telling libedit about the newly inserted text
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+ el_winsertstr(m_editline, priorLine.c_str());
+ return CC_REDISPLAY;
+}
+
+unsigned char Editline::PreviousLineCommand(int ch) {
+ SaveEditedLine();
+
+ if (m_current_line_index == 0) {
+ return RecallHistory(true);
+ }
+
+ // Start from a known location
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+
+ // Treat moving up from a blank last line as a deletion of that line
+ if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) {
+ m_input_lines.erase(m_input_lines.begin() + m_current_line_index);
+ fprintf(m_output_file, ANSI_CLEAR_BELOW);
+ }
+
+ SetCurrentLine(m_current_line_index - 1);
+ fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N,
+ CountRowsForLine(m_input_lines[m_current_line_index]), 1);
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::NextLineCommand(int ch) {
+ SaveEditedLine();
+
+ // Handle attempts to move down from the last line
+ if (m_current_line_index == m_input_lines.size() - 1) {
+ // Don't add an extra line if the existing last line is blank, move through
+ // history instead
+ if (IsOnlySpaces()) {
+ return RecallHistory(false);
+ }
+
+ // Determine indentation for the new line
+ int indentation = 0;
+ if (m_fix_indentation_callback) {
+ StringList lines = GetInputAsStringList();
+ lines.AppendString("");
+ indentation = m_fix_indentation_callback(
+ this, lines, 0, m_fix_indentation_callback_baton);
+ }
+ m_input_lines.insert(
+ m_input_lines.end(),
+ EditLineStringType(indentation, EditLineCharType(' ')));
+ }
+
+ // Move down past the current line using newlines to force scrolling if
+ // needed
+ SetCurrentLine(m_current_line_index + 1);
+ const LineInfoW *info = el_wline(m_editline);
+ int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth());
+ int cursor_row = cursor_position / m_terminal_width;
+ for (int line_count = 0; line_count < m_current_line_rows - cursor_row;
+ line_count++) {
+ fprintf(m_output_file, "\n");
+ }
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::PreviousHistoryCommand(int ch) {
+ SaveEditedLine();
+
+ return RecallHistory(true);
+}
+
+unsigned char Editline::NextHistoryCommand(int ch) {
+ SaveEditedLine();
+
+ return RecallHistory(false);
+}
+
+unsigned char Editline::FixIndentationCommand(int ch) {
+ if (!m_fix_indentation_callback)
+ return CC_NORM;
+
+ // Insert the character typed before proceeding
+ EditLineCharType inserted[] = {(EditLineCharType)ch, 0};
+ el_winsertstr(m_editline, inserted);
+ LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
+ int cursor_position = info->cursor - info->buffer;
+
+ // Save the edits and determine the correct indentation level
+ SaveEditedLine();
+ StringList lines = GetInputAsStringList(m_current_line_index + 1);
+ int indent_correction = m_fix_indentation_callback(
+ this, lines, cursor_position, m_fix_indentation_callback_baton);
+
+ // If it is already correct no special work is needed
+ if (indent_correction == 0)
+ return CC_REFRESH;
+
+ // Change the indentation level of the line
+ std::string currentLine = lines.GetStringAtIndex(m_current_line_index);
+ if (indent_correction > 0) {
+ currentLine = currentLine.insert(0, indent_correction, ' ');
+ } else {
+ currentLine = currentLine.erase(0, -indent_correction);
+ }
+#if LLDB_EDITLINE_USE_WCHAR
+ m_input_lines[m_current_line_index] = m_utf8conv.from_bytes(currentLine);
+#else
+ m_input_lines[m_current_line_index] = currentLine;
+#endif
+
+ // Update the display to reflect the change
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
+ DisplayInput(m_current_line_index);
+
+ // Reposition the cursor back on the original line and prepare to restart
+ // editing with a new cursor position
+ SetCurrentLine(m_current_line_index);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+ m_revert_cursor_index = cursor_position + indent_correction;
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::RevertLineCommand(int ch) {
+ el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str());
+ if (m_revert_cursor_index >= 0) {
+ LineInfoW *info = const_cast<LineInfoW *>(el_wline(m_editline));
+ info->cursor = info->buffer + m_revert_cursor_index;
+ if (info->cursor > info->lastchar) {
+ info->cursor = info->lastchar;
+ }
+ m_revert_cursor_index = -1;
+ }
+ return CC_REFRESH;
+}
+
+unsigned char Editline::BufferStartCommand(int ch) {
+ SaveEditedLine();
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
+ SetCurrentLine(0);
+ m_revert_cursor_index = 0;
+ return CC_NEWLINE;
+}
+
+unsigned char Editline::BufferEndCommand(int ch) {
+ SaveEditedLine();
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd);
+ SetCurrentLine((int)m_input_lines.size() - 1);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt);
+ return CC_NEWLINE;
+}
+
+/// Prints completions and their descriptions to the given file. Only the
+/// completions in the interval [start, end) are printed.
+static void PrintCompletion(FILE *output_file, size_t start, size_t end,
+ StringList &completions, StringList &descriptions) {
+ // This is an 'int' because of printf.
+ int max_len = 0;
+
+ for (size_t i = start; i < end; i++) {
+ const char *completion_str = completions.GetStringAtIndex(i);
+ max_len = std::max((int)strlen(completion_str), max_len);
+ }
+
+ for (size_t i = start; i < end; i++) {
+ const char *completion_str = completions.GetStringAtIndex(i);
+ const char *description_str = descriptions.GetStringAtIndex(i);
+
+ if (completion_str)
+ fprintf(output_file, "\n\t%-*s", max_len, completion_str);
+
+ // Print the description if we got one.
+ if (description_str && strlen(description_str))
+ fprintf(output_file, " -- %s", description_str);
+ }
+}
+
+unsigned char Editline::TabCommand(int ch) {
+ if (m_completion_callback == nullptr)
+ return CC_ERROR;
+
+ const LineInfo *line_info = el_line(m_editline);
+ StringList completions, descriptions;
+ int page_size = 40;
+
+ const int num_completions = m_completion_callback(
+ line_info->buffer, line_info->cursor, line_info->lastchar,
+ 0, // Don't skip any matches (start at match zero)
+ -1, // Get all the matches
+ completions, descriptions, m_completion_callback_baton);
+
+ if (num_completions == 0)
+ return CC_ERROR;
+ // if (num_completions == -1)
+ // {
+ // el_insertstr (m_editline, m_completion_key);
+ // return CC_REDISPLAY;
+ // }
+ // else
+ if (num_completions == -2) {
+ // Replace the entire line with the first string...
+ el_deletestr(m_editline, line_info->cursor - line_info->buffer);
+ el_insertstr(m_editline, completions.GetStringAtIndex(0));
+ return CC_REDISPLAY;
+ }
+
+ // If we get a longer match display that first.
+ const char *completion_str = completions.GetStringAtIndex(0);
+ if (completion_str != nullptr && *completion_str != '\0') {
+ el_insertstr(m_editline, completion_str);
+ return CC_REDISPLAY;
+ }
+
+ if (num_completions > 1) {
+ int num_elements = num_completions + 1;
+ fprintf(m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:");
+ if (num_completions < page_size) {
+ PrintCompletion(m_output_file, 1, num_elements, completions,
+ descriptions);
+ fprintf(m_output_file, "\n");
+ } else {
+ int cur_pos = 1;
+ char reply;
+ int got_char;
+ while (cur_pos < num_elements) {
+ int endpoint = cur_pos + page_size;
+ if (endpoint > num_elements)
+ endpoint = num_elements;
+
+ PrintCompletion(m_output_file, cur_pos, endpoint, completions,
+ descriptions);
+ cur_pos = endpoint;
+
+ if (cur_pos >= num_elements) {
+ fprintf(m_output_file, "\n");
+ break;
+ }
+
+ fprintf(m_output_file, "\nMore (Y/n/a): ");
+ reply = 'n';
+ got_char = el_getc(m_editline, &reply);
+ if (got_char == -1 || reply == 'n')
+ break;
+ if (reply == 'a')
+ page_size = num_elements - cur_pos;
+ }
+ }
+ DisplayInput();
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
+ }
+ return CC_REDISPLAY;
+}
+
+void Editline::ConfigureEditor(bool multiline) {
+ if (m_editline && m_multiline_enabled == multiline)
+ return;
+ m_multiline_enabled = multiline;
+
+ if (m_editline) {
+ // Disable edit mode to stop the terminal from flushing all input during
+ // the call to el_end() since we expect to have multiple editline instances
+ // in this program.
+ el_set(m_editline, EL_EDITMODE, 0);
+ el_end(m_editline);
+ }
+
+ m_editline =
+ el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file);
+ TerminalSizeChanged();
+
+ if (m_history_sp && m_history_sp->IsValid()) {
+ if (!m_history_sp->Load()) {
+ fputs("Could not load history file\n.", m_output_file);
+ }
+ el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
+ }
+ el_set(m_editline, EL_CLIENTDATA, this);
+ el_set(m_editline, EL_SIGNAL, 0);
+ el_set(m_editline, EL_EDITOR, "emacs");
+ el_set(m_editline, EL_PROMPT,
+ (EditlinePromptCallbackType)([](EditLine *editline) {
+ return Editline::InstanceFor(editline)->Prompt();
+ }));
+
+ el_wset(m_editline, EL_GETCFN, (EditlineGetCharCallbackType)([](
+ EditLine *editline, EditLineGetCharType *c) {
+ return Editline::InstanceFor(editline)->GetCharacter(c);
+ }));
+
+ // Commands used for multiline support, registered whether or not they're
+ // used
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"),
+ EditLineConstString("Insert a line break"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->BreakLineCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-end-or-add-line"),
+ EditLineConstString("End editing or continue when incomplete"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"),
+ EditLineConstString("Delete next character"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch);
+ }));
+ el_wset(
+ m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"),
+ EditLineConstString("Delete previous character"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"),
+ EditLineConstString("Move to previous line"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->PreviousLineCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"),
+ EditLineConstString("Move to next line"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->NextLineCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-history"),
+ EditLineConstString("Move to previous history"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-history"),
+ EditLineConstString("Move to next history"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->NextHistoryCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"),
+ EditLineConstString("Move to start of buffer"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->BufferStartCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"),
+ EditLineConstString("Move to end of buffer"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->BufferEndCommand(ch);
+ }));
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"),
+ EditLineConstString("Fix line indentation"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->FixIndentationCommand(ch);
+ }));
+
+ // Register the complete callback under two names for compatibility with
+ // older clients using custom .editrc files (largely because libedit has a
+ // bad bug where if you have a bind command that tries to bind to a function
+ // name that doesn't exist, it can corrupt the heap and crash your process
+ // later.)
+ EditlineCommandCallbackType complete_callback = [](EditLine *editline,
+ int ch) {
+ return Editline::InstanceFor(editline)->TabCommand(ch);
+ };
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"),
+ EditLineConstString("Invoke completion"), complete_callback);
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"),
+ EditLineConstString("Invoke completion"), complete_callback);
+
+ // General bindings we don't mind being overridden
+ if (!multiline) {
+ el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev",
+ NULL); // Cycle through backwards search, entering string
+ }
+ el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word",
+ NULL); // Delete previous word, behave like bash in emacs mode
+ el_set(m_editline, EL_BIND, "\t", "lldb-complete",
+ NULL); // Bind TAB to auto complete
+
+ // Allow user-specific customization prior to registering bindings we
+ // absolutely require
+ el_source(m_editline, nullptr);
+
+ // Register an internal binding that external developers shouldn't use
+ el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"),
+ EditLineConstString("Revert line to saved state"),
+ (EditlineCommandCallbackType)([](EditLine *editline, int ch) {
+ return Editline::InstanceFor(editline)->RevertLineCommand(ch);
+ }));
+
+ // Register keys that perform auto-indent correction
+ if (m_fix_indentation_callback && m_fix_indentation_callback_chars) {
+ char bind_key[2] = {0, 0};
+ const char *indent_chars = m_fix_indentation_callback_chars;
+ while (*indent_chars) {
+ bind_key[0] = *indent_chars;
+ el_set(m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL);
+ ++indent_chars;
+ }
+ }
+
+ // Multi-line editor bindings
+ if (multiline) {
+ el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL);
+ el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL);
+ el_set(m_editline, EL_BIND, "^p", "lldb-previous-line", NULL);
+ el_set(m_editline, EL_BIND, "^n", "lldb-next-line", NULL);
+ el_set(m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL);
+ el_set(m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL);
+
+ // Editor-specific bindings
+ if (IsEmacs()) {
+ el_set(m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL);
+ el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history",
+ NULL);
+ el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history",
+ NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history",
+ NULL);
+ el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL);
+ } else {
+ el_set(m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL);
+
+ el_set(m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line",
+ NULL);
+ el_set(m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL);
+ el_set(m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL);
+ el_set(m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char",
+ NULL);
+ el_set(m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char",
+ NULL);
+
+ // Escape is absorbed exiting edit mode, so re-register important
+ // sequences without the prefix
+ el_set(m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL);
+ el_set(m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL);
+ el_set(m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL);
+ }
+ }
+}
+
+// Editline public methods
+
+Editline *Editline::InstanceFor(EditLine *editline) {
+ Editline *editor;
+ el_get(editline, EL_CLIENTDATA, &editor);
+ return editor;
+}
+
+Editline::Editline(const char *editline_name, FILE *input_file,
+ FILE *output_file, FILE *error_file, bool color_prompts)
+ : m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts),
+ m_input_file(input_file), m_output_file(output_file),
+ m_error_file(error_file), m_input_connection(fileno(input_file), false) {
+ // Get a shared history instance
+ m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
+ m_history_sp = EditlineHistory::GetHistory(m_editor_name);
+
+#ifdef USE_SETUPTERM_WORKAROUND
+ if (m_output_file) {
+ const int term_fd = fileno(m_output_file);
+ if (term_fd != -1) {
+ static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr;
+ static std::set<int> *g_init_terminal_fds_ptr = nullptr;
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, [&]() {
+ g_init_terminal_fds_mutex_ptr =
+ new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues
+ g_init_terminal_fds_ptr = new std::set<int>(); // NOTE: Leak to avoid
+ // C++ destructor chain
+ // issues
+ });
+
+ // We must make sure to initialize the terminal a given file descriptor
+ // only once. If we do this multiple times, we start leaking memory.
+ std::lock_guard<std::mutex> guard(*g_init_terminal_fds_mutex_ptr);
+ if (g_init_terminal_fds_ptr->find(term_fd) ==
+ g_init_terminal_fds_ptr->end()) {
+ g_init_terminal_fds_ptr->insert(term_fd);
+ setupterm((char *)0, term_fd, (int *)0);
+ }
+ }
+ }
+#endif
+}
+
+Editline::~Editline() {
+ if (m_editline) {
+ // Disable edit mode to stop the terminal from flushing all input during
+ // the call to el_end() since we expect to have multiple editline instances
+ // in this program.
+ el_set(m_editline, EL_EDITMODE, 0);
+ el_end(m_editline);
+ m_editline = nullptr;
+ }
+
+ // EditlineHistory objects are sometimes shared between multiple Editline
+ // instances with the same program name. So just release our shared pointer
+ // and if we are the last owner, it will save the history to the history save
+ // file automatically.
+ m_history_sp.reset();
+}
+
+void Editline::SetPrompt(const char *prompt) {
+ m_set_prompt = prompt == nullptr ? "" : prompt;
+}
+
+void Editline::SetContinuationPrompt(const char *continuation_prompt) {
+ m_set_continuation_prompt =
+ continuation_prompt == nullptr ? "" : continuation_prompt;
+}
+
+void Editline::TerminalSizeChanged() {
+ if (m_editline != nullptr) {
+ el_resize(m_editline);
+ int columns;
+ // This function is documenting as taking (const char *, void *) for the
+ // vararg part, but in reality in was consuming arguments until the first
+ // null pointer. This was fixed in libedit in April 2019
+ // <http://mail-index.netbsd.org/source-changes/2019/04/26/msg105454.html>,
+ // but we're keeping the workaround until a version with that fix is more
+ // widely available.
+ if (el_get(m_editline, EL_GETTC, "co", &columns, nullptr) == 0) {
+ m_terminal_width = columns;
+ if (m_current_line_rows != -1) {
+ const LineInfoW *info = el_wline(m_editline);
+ int lineLength =
+ (int)((info->lastchar - info->buffer) + GetPromptWidth());
+ m_current_line_rows = (lineLength / columns) + 1;
+ }
+ } else {
+ m_terminal_width = INT_MAX;
+ m_current_line_rows = 1;
+ }
+ }
+}
+
+const char *Editline::GetPrompt() { return m_set_prompt.c_str(); }
+
+uint32_t Editline::GetCurrentLine() { return m_current_line_index; }
+
+bool Editline::Interrupt() {
+ bool result = true;
+ std::lock_guard<std::mutex> guard(m_output_mutex);
+ if (m_editor_status == EditorStatus::Editing) {
+ fprintf(m_output_file, "^C\n");
+ result = m_input_connection.InterruptRead();
+ }
+ m_editor_status = EditorStatus::Interrupted;
+ return result;
+}
+
+bool Editline::Cancel() {
+ bool result = true;
+ std::lock_guard<std::mutex> guard(m_output_mutex);
+ if (m_editor_status == EditorStatus::Editing) {
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
+ fprintf(m_output_file, ANSI_CLEAR_BELOW);
+ result = m_input_connection.InterruptRead();
+ }
+ m_editor_status = EditorStatus::Interrupted;
+ return result;
+}
+
+void Editline::SetAutoCompleteCallback(CompleteCallbackType callback,
+ void *baton) {
+ m_completion_callback = callback;
+ m_completion_callback_baton = baton;
+}
+
+void Editline::SetIsInputCompleteCallback(IsInputCompleteCallbackType callback,
+ void *baton) {
+ m_is_input_complete_callback = callback;
+ m_is_input_complete_callback_baton = baton;
+}
+
+bool Editline::SetFixIndentationCallback(FixIndentationCallbackType callback,
+ void *baton,
+ const char *indent_chars) {
+ m_fix_indentation_callback = callback;
+ m_fix_indentation_callback_baton = baton;
+ m_fix_indentation_callback_chars = indent_chars;
+ return false;
+}
+
+bool Editline::GetLine(std::string &line, bool &interrupted) {
+ ConfigureEditor(false);
+ m_input_lines = std::vector<EditLineStringType>();
+ m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
+
+ std::lock_guard<std::mutex> guard(m_output_mutex);
+
+ lldbassert(m_editor_status != EditorStatus::Editing);
+ if (m_editor_status == EditorStatus::Interrupted) {
+ m_editor_status = EditorStatus::Complete;
+ interrupted = true;
+ return true;
+ }
+
+ SetCurrentLine(0);
+ m_in_history = false;
+ m_editor_status = EditorStatus::Editing;
+ m_revert_cursor_index = -1;
+
+ int count;
+ auto input = el_wgets(m_editline, &count);
+
+ interrupted = m_editor_status == EditorStatus::Interrupted;
+ if (!interrupted) {
+ if (input == nullptr) {
+ fprintf(m_output_file, "\n");
+ m_editor_status = EditorStatus::EndOfInput;
+ } else {
+ m_history_sp->Enter(input);
+#if LLDB_EDITLINE_USE_WCHAR
+ line = m_utf8conv.to_bytes(SplitLines(input)[0]);
+#else
+ line = SplitLines(input)[0];
+#endif
+ m_editor_status = EditorStatus::Complete;
+ }
+ }
+ return m_editor_status != EditorStatus::EndOfInput;
+}
+
+bool Editline::GetLines(int first_line_number, StringList &lines,
+ bool &interrupted) {
+ ConfigureEditor(true);
+
+ // Print the initial input lines, then move the cursor back up to the start
+ // of input
+ SetBaseLineNumber(first_line_number);
+ m_input_lines = std::vector<EditLineStringType>();
+ m_input_lines.insert(m_input_lines.begin(), EditLineConstString(""));
+
+ std::lock_guard<std::mutex> guard(m_output_mutex);
+ // Begin the line editing loop
+ DisplayInput();
+ SetCurrentLine(0);
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart);
+ m_editor_status = EditorStatus::Editing;
+ m_in_history = false;
+
+ m_revert_cursor_index = -1;
+ while (m_editor_status == EditorStatus::Editing) {
+ int count;
+ m_current_line_rows = -1;
+ el_wpush(m_editline, EditLineConstString(
+ "\x1b[^")); // Revert to the existing line content
+ el_wgets(m_editline, &count);
+ }
+
+ interrupted = m_editor_status == EditorStatus::Interrupted;
+ if (!interrupted) {
+ // Save the completed entry in history before returning
+ m_history_sp->Enter(CombineLines(m_input_lines).c_str());
+
+ lines = GetInputAsStringList();
+ }
+ return m_editor_status != EditorStatus::EndOfInput;
+}
+
+void Editline::PrintAsync(Stream *stream, const char *s, size_t len) {
+ std::lock_guard<std::mutex> guard(m_output_mutex);
+ if (m_editor_status == EditorStatus::Editing) {
+ MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart);
+ fprintf(m_output_file, ANSI_CLEAR_BELOW);
+ }
+ stream->Write(s, len);
+ stream->Flush();
+ if (m_editor_status == EditorStatus::Editing) {
+ DisplayInput();
+ MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor);
+ }
+}
+
+bool Editline::CompleteCharacter(char ch, EditLineGetCharType &out) {
+#if !LLDB_EDITLINE_USE_WCHAR
+ if (ch == (char)EOF)
+ return false;
+
+ out = (unsigned char)ch;
+ return true;
+#else
+ std::codecvt_utf8<wchar_t> cvt;
+ llvm::SmallString<4> input;
+ for (;;) {
+ const char *from_next;
+ wchar_t *to_next;
+ std::mbstate_t state = std::mbstate_t();
+ input.push_back(ch);
+ switch (cvt.in(state, input.begin(), input.end(), from_next, &out, &out + 1,
+ to_next)) {
+ case std::codecvt_base::ok:
+ return out != WEOF;
+
+ case std::codecvt_base::error:
+ case std::codecvt_base::noconv:
+ return false;
+
+ case std::codecvt_base::partial:
+ lldb::ConnectionStatus status;
+ size_t read_count = m_input_connection.Read(
+ &ch, 1, std::chrono::seconds(0), status, nullptr);
+ if (read_count == 0)
+ return false;
+ break;
+ }
+ }
+#endif
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/File.cpp b/contrib/llvm-project/lldb/source/Host/common/File.cpp
new file mode 100644
index 000000000000..c8c8d7a0d496
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/File.cpp
@@ -0,0 +1,722 @@
+//===-- File.cpp ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/File.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#ifdef _WIN32
+#include "lldb/Host/windows/windows.h"
+#else
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+#endif
+
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Process.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static const char *GetStreamOpenModeFromOptions(uint32_t options) {
+ if (options & File::eOpenOptionAppend) {
+ if (options & File::eOpenOptionRead) {
+ if (options & File::eOpenOptionCanCreateNewOnly)
+ return "a+x";
+ else
+ return "a+";
+ } else if (options & File::eOpenOptionWrite) {
+ if (options & File::eOpenOptionCanCreateNewOnly)
+ return "ax";
+ else
+ return "a";
+ }
+ } else if (options & File::eOpenOptionRead &&
+ options & File::eOpenOptionWrite) {
+ if (options & File::eOpenOptionCanCreate) {
+ if (options & File::eOpenOptionCanCreateNewOnly)
+ return "w+x";
+ else
+ return "w+";
+ } else
+ return "r+";
+ } else if (options & File::eOpenOptionRead) {
+ return "r";
+ } else if (options & File::eOpenOptionWrite) {
+ return "w";
+ }
+ return nullptr;
+}
+
+int File::kInvalidDescriptor = -1;
+FILE *File::kInvalidStream = nullptr;
+
+File::~File() { Close(); }
+
+int File::GetDescriptor() const {
+ if (DescriptorIsValid())
+ return m_descriptor;
+
+ // Don't open the file descriptor if we don't need to, just get it from the
+ // stream if we have one.
+ if (StreamIsValid()) {
+#if defined(_WIN32)
+ return _fileno(m_stream);
+#else
+ return fileno(m_stream);
+#endif
+ }
+
+ // Invalid descriptor and invalid stream, return invalid descriptor.
+ return kInvalidDescriptor;
+}
+
+IOObject::WaitableHandle File::GetWaitableHandle() { return m_descriptor; }
+
+void File::SetDescriptor(int fd, bool transfer_ownership) {
+ if (IsValid())
+ Close();
+ m_descriptor = fd;
+ m_should_close_fd = transfer_ownership;
+}
+
+FILE *File::GetStream() {
+ if (!StreamIsValid()) {
+ if (DescriptorIsValid()) {
+ const char *mode = GetStreamOpenModeFromOptions(m_options);
+ if (mode) {
+ if (!m_should_close_fd) {
+// We must duplicate the file descriptor if we don't own it because when you
+// call fdopen, the stream will own the fd
+#ifdef _WIN32
+ m_descriptor = ::_dup(GetDescriptor());
+#else
+ m_descriptor = dup(GetDescriptor());
+#endif
+ m_should_close_fd = true;
+ }
+
+ m_stream =
+ llvm::sys::RetryAfterSignal(nullptr, ::fdopen, m_descriptor, mode);
+
+ // If we got a stream, then we own the stream and should no longer own
+ // the descriptor because fclose() will close it for us
+
+ if (m_stream) {
+ m_own_stream = true;
+ m_should_close_fd = false;
+ }
+ }
+ }
+ }
+ return m_stream;
+}
+
+void File::SetStream(FILE *fh, bool transfer_ownership) {
+ if (IsValid())
+ Close();
+ m_stream = fh;
+ m_own_stream = transfer_ownership;
+}
+
+uint32_t File::GetPermissions(Status &error) const {
+ int fd = GetDescriptor();
+ if (fd != kInvalidDescriptor) {
+ struct stat file_stats;
+ if (::fstat(fd, &file_stats) == -1)
+ error.SetErrorToErrno();
+ else {
+ error.Clear();
+ return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+ } else {
+ error.SetErrorString("invalid file descriptor");
+ }
+ return 0;
+}
+
+Status File::Close() {
+ Status error;
+ if (StreamIsValid() && m_own_stream) {
+ if (::fclose(m_stream) == EOF)
+ error.SetErrorToErrno();
+ }
+
+ if (DescriptorIsValid() && m_should_close_fd) {
+ if (::close(m_descriptor) != 0)
+ error.SetErrorToErrno();
+ }
+ m_descriptor = kInvalidDescriptor;
+ m_stream = kInvalidStream;
+ m_options = 0;
+ m_own_stream = false;
+ m_should_close_fd = false;
+ m_is_interactive = eLazyBoolCalculate;
+ m_is_real_terminal = eLazyBoolCalculate;
+ return error;
+}
+
+void File::Clear() {
+ m_stream = nullptr;
+ m_descriptor = kInvalidDescriptor;
+ m_options = 0;
+ m_own_stream = false;
+ m_is_interactive = m_supports_colors = m_is_real_terminal =
+ eLazyBoolCalculate;
+}
+
+Status File::GetFileSpec(FileSpec &file_spec) const {
+ Status error;
+#ifdef F_GETPATH
+ if (IsValid()) {
+ char path[PATH_MAX];
+ if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1)
+ error.SetErrorToErrno();
+ else
+ file_spec.SetFile(path, FileSpec::Style::native);
+ } else {
+ error.SetErrorString("invalid file handle");
+ }
+#elif defined(__linux__)
+ char proc[64];
+ char path[PATH_MAX];
+ if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0)
+ error.SetErrorString("cannot resolve file descriptor");
+ else {
+ ssize_t len;
+ if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)
+ error.SetErrorToErrno();
+ else {
+ path[len] = '\0';
+ file_spec.SetFile(path, FileSpec::Style::native);
+ }
+ }
+#else
+ error.SetErrorString("File::GetFileSpec is not supported on this platform");
+#endif
+
+ if (error.Fail())
+ file_spec.Clear();
+ return error;
+}
+
+off_t File::SeekFromStart(off_t offset, Status *error_ptr) {
+ off_t result = 0;
+ if (DescriptorIsValid()) {
+ result = ::lseek(m_descriptor, offset, SEEK_SET);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ } else if (StreamIsValid()) {
+ result = ::fseek(m_stream, offset, SEEK_SET);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ } else if (error_ptr) {
+ error_ptr->SetErrorString("invalid file handle");
+ }
+ return result;
+}
+
+off_t File::SeekFromCurrent(off_t offset, Status *error_ptr) {
+ off_t result = -1;
+ if (DescriptorIsValid()) {
+ result = ::lseek(m_descriptor, offset, SEEK_CUR);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ } else if (StreamIsValid()) {
+ result = ::fseek(m_stream, offset, SEEK_CUR);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ } else if (error_ptr) {
+ error_ptr->SetErrorString("invalid file handle");
+ }
+ return result;
+}
+
+off_t File::SeekFromEnd(off_t offset, Status *error_ptr) {
+ off_t result = -1;
+ if (DescriptorIsValid()) {
+ result = ::lseek(m_descriptor, offset, SEEK_END);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ } else if (StreamIsValid()) {
+ result = ::fseek(m_stream, offset, SEEK_END);
+
+ if (error_ptr) {
+ if (result == -1)
+ error_ptr->SetErrorToErrno();
+ else
+ error_ptr->Clear();
+ }
+ } else if (error_ptr) {
+ error_ptr->SetErrorString("invalid file handle");
+ }
+ return result;
+}
+
+Status File::Flush() {
+ Status error;
+ if (StreamIsValid()) {
+ if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF)
+ error.SetErrorToErrno();
+ } else if (!DescriptorIsValid()) {
+ error.SetErrorString("invalid file handle");
+ }
+ return error;
+}
+
+Status File::Sync() {
+ Status error;
+ if (DescriptorIsValid()) {
+#ifdef _WIN32
+ int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor));
+ if (err == 0)
+ error.SetErrorToGenericError();
+#else
+ if (llvm::sys::RetryAfterSignal(-1, ::fsync, m_descriptor) == -1)
+ error.SetErrorToErrno();
+#endif
+ } else {
+ error.SetErrorString("invalid file handle");
+ }
+ return error;
+}
+
+#if defined(__APPLE__)
+// Darwin kernels only can read/write <= INT_MAX bytes
+#define MAX_READ_SIZE INT_MAX
+#define MAX_WRITE_SIZE INT_MAX
+#endif
+
+Status File::Read(void *buf, size_t &num_bytes) {
+ Status error;
+
+#if defined(MAX_READ_SIZE)
+ if (num_bytes > MAX_READ_SIZE) {
+ uint8_t *p = (uint8_t *)buf;
+ size_t bytes_left = num_bytes;
+ // Init the num_bytes read to zero
+ num_bytes = 0;
+
+ while (bytes_left > 0) {
+ size_t curr_num_bytes;
+ if (bytes_left > MAX_READ_SIZE)
+ curr_num_bytes = MAX_READ_SIZE;
+ else
+ curr_num_bytes = bytes_left;
+
+ error = Read(p + num_bytes, curr_num_bytes);
+
+ // Update how many bytes were read
+ num_bytes += curr_num_bytes;
+ if (bytes_left < curr_num_bytes)
+ bytes_left = 0;
+ else
+ bytes_left -= curr_num_bytes;
+
+ if (error.Fail())
+ break;
+ }
+ return error;
+ }
+#endif
+
+ ssize_t bytes_read = -1;
+ if (DescriptorIsValid()) {
+ bytes_read = llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes);
+ if (bytes_read == -1) {
+ error.SetErrorToErrno();
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_read;
+ } else if (StreamIsValid()) {
+ bytes_read = ::fread(buf, 1, num_bytes, m_stream);
+
+ if (bytes_read == 0) {
+ if (::feof(m_stream))
+ error.SetErrorString("feof");
+ else if (::ferror(m_stream))
+ error.SetErrorString("ferror");
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_read;
+ } else {
+ num_bytes = 0;
+ error.SetErrorString("invalid file handle");
+ }
+ return error;
+}
+
+Status File::Write(const void *buf, size_t &num_bytes) {
+ Status error;
+
+#if defined(MAX_WRITE_SIZE)
+ if (num_bytes > MAX_WRITE_SIZE) {
+ const uint8_t *p = (const uint8_t *)buf;
+ size_t bytes_left = num_bytes;
+ // Init the num_bytes written to zero
+ num_bytes = 0;
+
+ while (bytes_left > 0) {
+ size_t curr_num_bytes;
+ if (bytes_left > MAX_WRITE_SIZE)
+ curr_num_bytes = MAX_WRITE_SIZE;
+ else
+ curr_num_bytes = bytes_left;
+
+ error = Write(p + num_bytes, curr_num_bytes);
+
+ // Update how many bytes were read
+ num_bytes += curr_num_bytes;
+ if (bytes_left < curr_num_bytes)
+ bytes_left = 0;
+ else
+ bytes_left -= curr_num_bytes;
+
+ if (error.Fail())
+ break;
+ }
+ return error;
+ }
+#endif
+
+ ssize_t bytes_written = -1;
+ if (DescriptorIsValid()) {
+ bytes_written =
+ llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes);
+ if (bytes_written == -1) {
+ error.SetErrorToErrno();
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_written;
+ } else if (StreamIsValid()) {
+ bytes_written = ::fwrite(buf, 1, num_bytes, m_stream);
+
+ if (bytes_written == 0) {
+ if (::feof(m_stream))
+ error.SetErrorString("feof");
+ else if (::ferror(m_stream))
+ error.SetErrorString("ferror");
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_written;
+
+ } else {
+ num_bytes = 0;
+ error.SetErrorString("invalid file handle");
+ }
+
+ return error;
+}
+
+Status File::Read(void *buf, size_t &num_bytes, off_t &offset) {
+ Status error;
+
+#if defined(MAX_READ_SIZE)
+ if (num_bytes > MAX_READ_SIZE) {
+ uint8_t *p = (uint8_t *)buf;
+ size_t bytes_left = num_bytes;
+ // Init the num_bytes read to zero
+ num_bytes = 0;
+
+ while (bytes_left > 0) {
+ size_t curr_num_bytes;
+ if (bytes_left > MAX_READ_SIZE)
+ curr_num_bytes = MAX_READ_SIZE;
+ else
+ curr_num_bytes = bytes_left;
+
+ error = Read(p + num_bytes, curr_num_bytes, offset);
+
+ // Update how many bytes were read
+ num_bytes += curr_num_bytes;
+ if (bytes_left < curr_num_bytes)
+ bytes_left = 0;
+ else
+ bytes_left -= curr_num_bytes;
+
+ if (error.Fail())
+ break;
+ }
+ return error;
+ }
+#endif
+
+#ifndef _WIN32
+ int fd = GetDescriptor();
+ if (fd != kInvalidDescriptor) {
+ ssize_t bytes_read =
+ llvm::sys::RetryAfterSignal(-1, ::pread, fd, buf, num_bytes, offset);
+ if (bytes_read < 0) {
+ num_bytes = 0;
+ error.SetErrorToErrno();
+ } else {
+ offset += bytes_read;
+ num_bytes = bytes_read;
+ }
+ } else {
+ num_bytes = 0;
+ error.SetErrorString("invalid file handle");
+ }
+#else
+ std::lock_guard<std::mutex> guard(offset_access_mutex);
+ long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
+ SeekFromStart(offset);
+ error = Read(buf, num_bytes);
+ if (!error.Fail())
+ SeekFromStart(cur);
+#endif
+ return error;
+}
+
+Status File::Read(size_t &num_bytes, off_t &offset, bool null_terminate,
+ DataBufferSP &data_buffer_sp) {
+ Status error;
+
+ if (num_bytes > 0) {
+ int fd = GetDescriptor();
+ if (fd != kInvalidDescriptor) {
+ struct stat file_stats;
+ if (::fstat(fd, &file_stats) == 0) {
+ if (file_stats.st_size > offset) {
+ const size_t bytes_left = file_stats.st_size - offset;
+ if (num_bytes > bytes_left)
+ num_bytes = bytes_left;
+
+ size_t num_bytes_plus_nul_char = num_bytes + (null_terminate ? 1 : 0);
+ std::unique_ptr<DataBufferHeap> data_heap_up;
+ data_heap_up.reset(new DataBufferHeap());
+ data_heap_up->SetByteSize(num_bytes_plus_nul_char);
+
+ if (data_heap_up) {
+ error = Read(data_heap_up->GetBytes(), num_bytes, offset);
+ if (error.Success()) {
+ // Make sure we read exactly what we asked for and if we got
+ // less, adjust the array
+ if (num_bytes_plus_nul_char < data_heap_up->GetByteSize())
+ data_heap_up->SetByteSize(num_bytes_plus_nul_char);
+ data_buffer_sp.reset(data_heap_up.release());
+ return error;
+ }
+ }
+ } else
+ error.SetErrorString("file is empty");
+ } else
+ error.SetErrorToErrno();
+ } else
+ error.SetErrorString("invalid file handle");
+ } else
+ error.SetErrorString("invalid file handle");
+
+ num_bytes = 0;
+ data_buffer_sp.reset();
+ return error;
+}
+
+Status File::Write(const void *buf, size_t &num_bytes, off_t &offset) {
+ Status error;
+
+#if defined(MAX_WRITE_SIZE)
+ if (num_bytes > MAX_WRITE_SIZE) {
+ const uint8_t *p = (const uint8_t *)buf;
+ size_t bytes_left = num_bytes;
+ // Init the num_bytes written to zero
+ num_bytes = 0;
+
+ while (bytes_left > 0) {
+ size_t curr_num_bytes;
+ if (bytes_left > MAX_WRITE_SIZE)
+ curr_num_bytes = MAX_WRITE_SIZE;
+ else
+ curr_num_bytes = bytes_left;
+
+ error = Write(p + num_bytes, curr_num_bytes, offset);
+
+ // Update how many bytes were read
+ num_bytes += curr_num_bytes;
+ if (bytes_left < curr_num_bytes)
+ bytes_left = 0;
+ else
+ bytes_left -= curr_num_bytes;
+
+ if (error.Fail())
+ break;
+ }
+ return error;
+ }
+#endif
+
+ int fd = GetDescriptor();
+ if (fd != kInvalidDescriptor) {
+#ifndef _WIN32
+ ssize_t bytes_written =
+ llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset);
+ if (bytes_written < 0) {
+ num_bytes = 0;
+ error.SetErrorToErrno();
+ } else {
+ offset += bytes_written;
+ num_bytes = bytes_written;
+ }
+#else
+ std::lock_guard<std::mutex> guard(offset_access_mutex);
+ long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
+ SeekFromStart(offset);
+ error = Write(buf, num_bytes);
+ long after = ::lseek(m_descriptor, 0, SEEK_CUR);
+
+ if (!error.Fail())
+ SeekFromStart(cur);
+
+ offset = after;
+#endif
+ } else {
+ num_bytes = 0;
+ error.SetErrorString("invalid file handle");
+ }
+ return error;
+}
+
+// Print some formatted output to the stream.
+size_t File::Printf(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ size_t result = PrintfVarArg(format, args);
+ va_end(args);
+ return result;
+}
+
+// Print some formatted output to the stream.
+size_t File::PrintfVarArg(const char *format, va_list args) {
+ size_t result = 0;
+ if (DescriptorIsValid()) {
+ char *s = nullptr;
+ result = vasprintf(&s, format, args);
+ if (s != nullptr) {
+ if (result > 0) {
+ size_t s_len = result;
+ Write(s, s_len);
+ result = s_len;
+ }
+ free(s);
+ }
+ } else if (StreamIsValid()) {
+ result = ::vfprintf(m_stream, format, args);
+ }
+ return result;
+}
+
+mode_t File::ConvertOpenOptionsForPOSIXOpen(uint32_t open_options) {
+ mode_t mode = 0;
+ if (open_options & eOpenOptionRead && open_options & eOpenOptionWrite)
+ mode |= O_RDWR;
+ else if (open_options & eOpenOptionWrite)
+ mode |= O_WRONLY;
+
+ if (open_options & eOpenOptionAppend)
+ mode |= O_APPEND;
+
+ if (open_options & eOpenOptionTruncate)
+ mode |= O_TRUNC;
+
+ if (open_options & eOpenOptionNonBlocking)
+ mode |= O_NONBLOCK;
+
+ if (open_options & eOpenOptionCanCreateNewOnly)
+ mode |= O_CREAT | O_EXCL;
+ else if (open_options & eOpenOptionCanCreate)
+ mode |= O_CREAT;
+
+ return mode;
+}
+
+void File::CalculateInteractiveAndTerminal() {
+ const int fd = GetDescriptor();
+ if (fd >= 0) {
+ m_is_interactive = eLazyBoolNo;
+ m_is_real_terminal = eLazyBoolNo;
+#if defined(_WIN32)
+ if (_isatty(fd)) {
+ m_is_interactive = eLazyBoolYes;
+ m_is_real_terminal = eLazyBoolYes;
+#if defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+ m_supports_colors = eLazyBoolYes;
+#endif
+ }
+#else
+ if (isatty(fd)) {
+ m_is_interactive = eLazyBoolYes;
+ struct winsize window_size;
+ if (::ioctl(fd, TIOCGWINSZ, &window_size) == 0) {
+ if (window_size.ws_col > 0) {
+ m_is_real_terminal = eLazyBoolYes;
+ if (llvm::sys::Process::FileDescriptorHasColors(fd))
+ m_supports_colors = eLazyBoolYes;
+ }
+ }
+ }
+#endif
+ }
+}
+
+bool File::GetIsInteractive() {
+ if (m_is_interactive == eLazyBoolCalculate)
+ CalculateInteractiveAndTerminal();
+ return m_is_interactive == eLazyBoolYes;
+}
+
+bool File::GetIsRealTerminal() {
+ if (m_is_real_terminal == eLazyBoolCalculate)
+ CalculateInteractiveAndTerminal();
+ return m_is_real_terminal == eLazyBoolYes;
+}
+
+bool File::GetIsTerminalWithColors() {
+ if (m_supports_colors == eLazyBoolCalculate)
+ CalculateInteractiveAndTerminal();
+ return m_supports_colors == eLazyBoolYes;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/FileAction.cpp b/contrib/llvm-project/lldb/source/Host/common/FileAction.cpp
new file mode 100644
index 000000000000..3268d952bcc9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/FileAction.cpp
@@ -0,0 +1,88 @@
+//===-- FileAction.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <fcntl.h>
+
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+
+// FileAction member functions
+
+FileAction::FileAction()
+ : m_action(eFileActionNone), m_fd(-1), m_arg(-1), m_file_spec() {}
+
+void FileAction::Clear() {
+ m_action = eFileActionNone;
+ m_fd = -1;
+ m_arg = -1;
+ m_file_spec.Clear();
+}
+
+llvm::StringRef FileAction::GetPath() const { return m_file_spec.GetCString(); }
+
+const FileSpec &FileAction::GetFileSpec() const { return m_file_spec; }
+
+bool FileAction::Open(int fd, const FileSpec &file_spec, bool read,
+ bool write) {
+ if ((read || write) && fd >= 0 && file_spec) {
+ m_action = eFileActionOpen;
+ m_fd = fd;
+ if (read && write)
+ m_arg = O_NOCTTY | O_CREAT | O_RDWR;
+ else if (read)
+ m_arg = O_NOCTTY | O_RDONLY;
+ else
+ m_arg = O_NOCTTY | O_CREAT | O_WRONLY;
+ m_file_spec = file_spec;
+ return true;
+ } else {
+ Clear();
+ }
+ return false;
+}
+
+bool FileAction::Close(int fd) {
+ Clear();
+ if (fd >= 0) {
+ m_action = eFileActionClose;
+ m_fd = fd;
+ }
+ return m_fd >= 0;
+}
+
+bool FileAction::Duplicate(int fd, int dup_fd) {
+ Clear();
+ if (fd >= 0 && dup_fd >= 0) {
+ m_action = eFileActionDuplicate;
+ m_fd = fd;
+ m_arg = dup_fd;
+ }
+ return m_fd >= 0;
+}
+
+void FileAction::Dump(Stream &stream) const {
+ stream.PutCString("file action: ");
+ switch (m_action) {
+ case eFileActionClose:
+ stream.Printf("close fd %d", m_fd);
+ break;
+ case eFileActionDuplicate:
+ stream.Printf("duplicate fd %d to %d", m_fd, m_arg);
+ break;
+ case eFileActionNone:
+ stream.PutCString("no action");
+ break;
+ case eFileActionOpen:
+ stream.Printf("open fd %d with '%s', OFLAGS = 0x%x", m_fd,
+ m_file_spec.GetCString(), m_arg);
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/FileCache.cpp b/contrib/llvm-project/lldb/source/Host/common/FileCache.cpp
new file mode 100644
index 000000000000..4bd3efda7fb0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/FileCache.cpp
@@ -0,0 +1,112 @@
+//===-- FileCache.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/FileCache.h"
+
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+FileCache *FileCache::m_instance = nullptr;
+
+FileCache &FileCache::GetInstance() {
+ if (m_instance == nullptr)
+ m_instance = new FileCache();
+
+ return *m_instance;
+}
+
+lldb::user_id_t FileCache::OpenFile(const FileSpec &file_spec, uint32_t flags,
+ uint32_t mode, Status &error) {
+ if (!file_spec) {
+ error.SetErrorString("empty path");
+ return UINT64_MAX;
+ }
+ FileSP file_sp(new File());
+ error = FileSystem::Instance().Open(*file_sp, file_spec, flags, mode);
+ if (!file_sp->IsValid())
+ return UINT64_MAX;
+ lldb::user_id_t fd = file_sp->GetDescriptor();
+ m_cache[fd] = file_sp;
+ return fd;
+}
+
+bool FileCache::CloseFile(lldb::user_id_t fd, Status &error) {
+ if (fd == UINT64_MAX) {
+ error.SetErrorString("invalid file descriptor");
+ return false;
+ }
+ FDToFileMap::iterator pos = m_cache.find(fd);
+ if (pos == m_cache.end()) {
+ error.SetErrorStringWithFormat("invalid host file descriptor %" PRIu64, fd);
+ return false;
+ }
+ FileSP file_sp = pos->second;
+ if (!file_sp) {
+ error.SetErrorString("invalid host backing file");
+ return false;
+ }
+ error = file_sp->Close();
+ m_cache.erase(pos);
+ return error.Success();
+}
+
+uint64_t FileCache::WriteFile(lldb::user_id_t fd, uint64_t offset,
+ const void *src, uint64_t src_len,
+ Status &error) {
+ if (fd == UINT64_MAX) {
+ error.SetErrorString("invalid file descriptor");
+ return UINT64_MAX;
+ }
+ FDToFileMap::iterator pos = m_cache.find(fd);
+ if (pos == m_cache.end()) {
+ error.SetErrorStringWithFormat("invalid host file descriptor %" PRIu64, fd);
+ return false;
+ }
+ FileSP file_sp = pos->second;
+ if (!file_sp) {
+ error.SetErrorString("invalid host backing file");
+ return UINT64_MAX;
+ }
+ if (static_cast<uint64_t>(file_sp->SeekFromStart(offset, &error)) != offset ||
+ error.Fail())
+ return UINT64_MAX;
+ size_t bytes_written = src_len;
+ error = file_sp->Write(src, bytes_written);
+ if (error.Fail())
+ return UINT64_MAX;
+ return bytes_written;
+}
+
+uint64_t FileCache::ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst,
+ uint64_t dst_len, Status &error) {
+ if (fd == UINT64_MAX) {
+ error.SetErrorString("invalid file descriptor");
+ return UINT64_MAX;
+ }
+ FDToFileMap::iterator pos = m_cache.find(fd);
+ if (pos == m_cache.end()) {
+ error.SetErrorStringWithFormat("invalid host file descriptor %" PRIu64, fd);
+ return false;
+ }
+ FileSP file_sp = pos->second;
+ if (!file_sp) {
+ error.SetErrorString("invalid host backing file");
+ return UINT64_MAX;
+ }
+ if (static_cast<uint64_t>(file_sp->SeekFromStart(offset, &error)) != offset ||
+ error.Fail())
+ return UINT64_MAX;
+ size_t bytes_read = dst_len;
+ error = file_sp->Read(dst, bytes_read);
+ if (error.Fail())
+ return UINT64_MAX;
+ return bytes_read;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/FileSystem.cpp b/contrib/llvm-project/lldb/source/Host/common/FileSystem.cpp
new file mode 100644
index 000000000000..d5ac05bd447c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/FileSystem.cpp
@@ -0,0 +1,471 @@
+//===-- FileSystem.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/FileSystem.h"
+
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/TildeExpressionResolver.h"
+
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Threading.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#ifdef _WIN32
+#include "lldb/Host/windows/windows.h"
+#else
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+#include <fstream>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+FileSystem &FileSystem::Instance() { return *InstanceImpl(); }
+
+void FileSystem::Initialize() {
+ lldbassert(!InstanceImpl() && "Already initialized.");
+ InstanceImpl().emplace();
+}
+
+void FileSystem::Initialize(FileCollector &collector) {
+ lldbassert(!InstanceImpl() && "Already initialized.");
+ InstanceImpl().emplace(collector);
+}
+
+llvm::Error FileSystem::Initialize(const FileSpec &mapping) {
+ lldbassert(!InstanceImpl() && "Already initialized.");
+
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
+ llvm::vfs::getRealFileSystem()->getBufferForFile(mapping.GetPath());
+
+ if (!buffer)
+ return llvm::errorCodeToError(buffer.getError());
+
+ InstanceImpl().emplace(llvm::vfs::getVFSFromYAML(std::move(buffer.get()),
+ nullptr, mapping.GetPath()),
+ true);
+
+ return llvm::Error::success();
+}
+
+void FileSystem::Initialize(IntrusiveRefCntPtr<vfs::FileSystem> fs) {
+ lldbassert(!InstanceImpl() && "Already initialized.");
+ InstanceImpl().emplace(fs);
+}
+
+void FileSystem::Terminate() {
+ lldbassert(InstanceImpl() && "Already terminated.");
+ InstanceImpl().reset();
+}
+
+Optional<FileSystem> &FileSystem::InstanceImpl() {
+ static Optional<FileSystem> g_fs;
+ return g_fs;
+}
+
+vfs::directory_iterator FileSystem::DirBegin(const FileSpec &file_spec,
+ std::error_code &ec) {
+ return DirBegin(file_spec.GetPath(), ec);
+}
+
+vfs::directory_iterator FileSystem::DirBegin(const Twine &dir,
+ std::error_code &ec) {
+ return m_fs->dir_begin(dir, ec);
+}
+
+llvm::ErrorOr<vfs::Status>
+FileSystem::GetStatus(const FileSpec &file_spec) const {
+ return GetStatus(file_spec.GetPath());
+}
+
+llvm::ErrorOr<vfs::Status> FileSystem::GetStatus(const Twine &path) const {
+ return m_fs->status(path);
+}
+
+sys::TimePoint<>
+FileSystem::GetModificationTime(const FileSpec &file_spec) const {
+ return GetModificationTime(file_spec.GetPath());
+}
+
+sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {
+ ErrorOr<vfs::Status> status = m_fs->status(path);
+ if (!status)
+ return sys::TimePoint<>();
+ return status->getLastModificationTime();
+}
+
+uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {
+ return GetByteSize(file_spec.GetPath());
+}
+
+uint64_t FileSystem::GetByteSize(const Twine &path) const {
+ ErrorOr<vfs::Status> status = m_fs->status(path);
+ if (!status)
+ return 0;
+ return status->getSize();
+}
+
+uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {
+ return GetPermissions(file_spec.GetPath());
+}
+
+uint32_t FileSystem::GetPermissions(const FileSpec &file_spec,
+ std::error_code &ec) const {
+ return GetPermissions(file_spec.GetPath(), ec);
+}
+
+uint32_t FileSystem::GetPermissions(const Twine &path) const {
+ std::error_code ec;
+ return GetPermissions(path, ec);
+}
+
+uint32_t FileSystem::GetPermissions(const Twine &path,
+ std::error_code &ec) const {
+ ErrorOr<vfs::Status> status = m_fs->status(path);
+ if (!status) {
+ ec = status.getError();
+ return sys::fs::perms::perms_not_known;
+ }
+ return status->getPermissions();
+}
+
+bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }
+
+bool FileSystem::Exists(const FileSpec &file_spec) const {
+ return Exists(file_spec.GetPath());
+}
+
+bool FileSystem::Readable(const Twine &path) const {
+ return GetPermissions(path) & sys::fs::perms::all_read;
+}
+
+bool FileSystem::Readable(const FileSpec &file_spec) const {
+ return Readable(file_spec.GetPath());
+}
+
+bool FileSystem::IsDirectory(const Twine &path) const {
+ ErrorOr<vfs::Status> status = m_fs->status(path);
+ if (!status)
+ return false;
+ return status->isDirectory();
+}
+
+bool FileSystem::IsDirectory(const FileSpec &file_spec) const {
+ return IsDirectory(file_spec.GetPath());
+}
+
+bool FileSystem::IsLocal(const Twine &path) const {
+ bool b = false;
+ m_fs->isLocal(path, b);
+ return b;
+}
+
+bool FileSystem::IsLocal(const FileSpec &file_spec) const {
+ return IsLocal(file_spec.GetPath());
+}
+
+void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
+ bool find_files, bool find_other,
+ EnumerateDirectoryCallbackType callback,
+ void *callback_baton) {
+ std::error_code EC;
+ vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
+ vfs::recursive_directory_iterator End;
+ for (; Iter != End && !EC; Iter.increment(EC)) {
+ const auto &Item = *Iter;
+ ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
+ if (!Status)
+ break;
+ if (!find_files && Status->isRegularFile())
+ continue;
+ if (!find_directories && Status->isDirectory())
+ continue;
+ if (!find_other && Status->isOther())
+ continue;
+
+ auto Result = callback(callback_baton, Status->getType(), Item.path());
+ if (Result == eEnumerateDirectoryResultQuit)
+ return;
+ if (Result == eEnumerateDirectoryResultNext) {
+ // Default behavior is to recurse. Opt out if the callback doesn't want
+ // this behavior.
+ Iter.no_push();
+ }
+ }
+}
+
+std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
+ return m_fs->makeAbsolute(path);
+}
+
+std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
+ SmallString<128> path;
+ file_spec.GetPath(path, false);
+
+ auto EC = MakeAbsolute(path);
+ if (EC)
+ return EC;
+
+ FileSpec new_file_spec(path, file_spec.GetPathStyle());
+ file_spec = new_file_spec;
+ return {};
+}
+
+std::error_code FileSystem::GetRealPath(const Twine &path,
+ SmallVectorImpl<char> &output) const {
+ return m_fs->getRealPath(path, output);
+}
+
+void FileSystem::Resolve(SmallVectorImpl<char> &path) {
+ if (path.empty())
+ return;
+
+ // Resolve tilde in path.
+ SmallString<128> resolved(path.begin(), path.end());
+ StandardTildeExpressionResolver Resolver;
+ Resolver.ResolveFullPath(llvm::StringRef(path.begin(), path.size()),
+ resolved);
+
+ // Try making the path absolute if it exists.
+ SmallString<128> absolute(resolved.begin(), resolved.end());
+ MakeAbsolute(absolute);
+
+ path.clear();
+ if (Exists(absolute)) {
+ path.append(absolute.begin(), absolute.end());
+ } else {
+ path.append(resolved.begin(), resolved.end());
+ }
+}
+
+void FileSystem::Resolve(FileSpec &file_spec) {
+ // Extract path from the FileSpec.
+ SmallString<128> path;
+ file_spec.GetPath(path);
+
+ // Resolve the path.
+ Resolve(path);
+
+ // Update the FileSpec with the resolved path.
+ if (file_spec.GetFilename().IsEmpty())
+ file_spec.GetDirectory().SetString(path);
+ else
+ file_spec.SetPath(path);
+ file_spec.SetIsResolved(true);
+}
+
+std::shared_ptr<DataBufferLLVM>
+FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size,
+ uint64_t offset) {
+ if (m_collector)
+ m_collector->AddFile(path);
+
+ const bool is_volatile = !IsLocal(path);
+ const ErrorOr<std::string> external_path = GetExternalPath(path);
+
+ if (!external_path)
+ return nullptr;
+
+ std::unique_ptr<llvm::WritableMemoryBuffer> buffer;
+ if (size == 0) {
+ auto buffer_or_error =
+ llvm::WritableMemoryBuffer::getFile(*external_path, -1, is_volatile);
+ if (!buffer_or_error)
+ return nullptr;
+ buffer = std::move(*buffer_or_error);
+ } else {
+ auto buffer_or_error = llvm::WritableMemoryBuffer::getFileSlice(
+ *external_path, size, offset, is_volatile);
+ if (!buffer_or_error)
+ return nullptr;
+ buffer = std::move(*buffer_or_error);
+ }
+ return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer)));
+}
+
+std::shared_ptr<DataBufferLLVM>
+FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size,
+ uint64_t offset) {
+ return CreateDataBuffer(file_spec.GetPath(), size, offset);
+}
+
+bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
+ // If the directory is set there's nothing to do.
+ ConstString directory = file_spec.GetDirectory();
+ if (directory)
+ return false;
+
+ // We cannot look for a file if there's no file name.
+ ConstString filename = file_spec.GetFilename();
+ if (!filename)
+ return false;
+
+ // Search for the file on the host.
+ const std::string filename_str(filename.GetCString());
+ llvm::ErrorOr<std::string> error_or_path =
+ llvm::sys::findProgramByName(filename_str);
+ if (!error_or_path)
+ return false;
+
+ // findProgramByName returns "." if it can't find the file.
+ llvm::StringRef path = *error_or_path;
+ llvm::StringRef parent = llvm::sys::path::parent_path(path);
+ if (parent.empty() || parent == ".")
+ return false;
+
+ // Make sure that the result exists.
+ FileSpec result(*error_or_path);
+ if (!Exists(result))
+ return false;
+
+ file_spec = result;
+ return true;
+}
+
+static int OpenWithFS(const FileSystem &fs, const char *path, int flags,
+ int mode) {
+ return const_cast<FileSystem &>(fs).Open(path, flags, mode);
+}
+
+static int GetOpenFlags(uint32_t options) {
+ const bool read = options & File::eOpenOptionRead;
+ const bool write = options & File::eOpenOptionWrite;
+
+ int open_flags = 0;
+ if (write) {
+ if (read)
+ open_flags |= O_RDWR;
+ else
+ open_flags |= O_WRONLY;
+
+ if (options & File::eOpenOptionAppend)
+ open_flags |= O_APPEND;
+
+ if (options & File::eOpenOptionTruncate)
+ open_flags |= O_TRUNC;
+
+ if (options & File::eOpenOptionCanCreate)
+ open_flags |= O_CREAT;
+
+ if (options & File::eOpenOptionCanCreateNewOnly)
+ open_flags |= O_CREAT | O_EXCL;
+ } else if (read) {
+ open_flags |= O_RDONLY;
+
+#ifndef _WIN32
+ if (options & File::eOpenOptionDontFollowSymlinks)
+ open_flags |= O_NOFOLLOW;
+#endif
+ }
+
+#ifndef _WIN32
+ if (options & File::eOpenOptionNonBlocking)
+ open_flags |= O_NONBLOCK;
+ if (options & File::eOpenOptionCloseOnExec)
+ open_flags |= O_CLOEXEC;
+#else
+ open_flags |= O_BINARY;
+#endif
+
+ return open_flags;
+}
+
+static mode_t GetOpenMode(uint32_t permissions) {
+ mode_t mode = 0;
+ if (permissions & lldb::eFilePermissionsUserRead)
+ mode |= S_IRUSR;
+ if (permissions & lldb::eFilePermissionsUserWrite)
+ mode |= S_IWUSR;
+ if (permissions & lldb::eFilePermissionsUserExecute)
+ mode |= S_IXUSR;
+ if (permissions & lldb::eFilePermissionsGroupRead)
+ mode |= S_IRGRP;
+ if (permissions & lldb::eFilePermissionsGroupWrite)
+ mode |= S_IWGRP;
+ if (permissions & lldb::eFilePermissionsGroupExecute)
+ mode |= S_IXGRP;
+ if (permissions & lldb::eFilePermissionsWorldRead)
+ mode |= S_IROTH;
+ if (permissions & lldb::eFilePermissionsWorldWrite)
+ mode |= S_IWOTH;
+ if (permissions & lldb::eFilePermissionsWorldExecute)
+ mode |= S_IXOTH;
+ return mode;
+}
+
+Status FileSystem::Open(File &File, const FileSpec &file_spec, uint32_t options,
+ uint32_t permissions, bool should_close_fd) {
+ if (m_collector)
+ m_collector->AddFile(file_spec);
+
+ if (File.IsValid())
+ File.Close();
+
+ const int open_flags = GetOpenFlags(options);
+ const mode_t open_mode =
+ (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;
+
+ auto path = GetExternalPath(file_spec);
+ if (!path)
+ return Status(path.getError());
+
+ int descriptor = llvm::sys::RetryAfterSignal(
+ -1, OpenWithFS, *this, path->c_str(), open_flags, open_mode);
+
+ Status error;
+ if (!File::DescriptorIsValid(descriptor)) {
+ File.SetDescriptor(descriptor, false);
+ error.SetErrorToErrno();
+ } else {
+ File.SetDescriptor(descriptor, should_close_fd);
+ File.SetOptions(options);
+ }
+ return error;
+}
+
+ErrorOr<std::string> FileSystem::GetExternalPath(const llvm::Twine &path) {
+ if (!m_mapped)
+ return path.str();
+
+ // If VFS mapped we know the underlying FS is a RedirectingFileSystem.
+ ErrorOr<vfs::RedirectingFileSystem::Entry *> E =
+ static_cast<vfs::RedirectingFileSystem &>(*m_fs).lookupPath(path);
+ if (!E) {
+ if (E.getError() == llvm::errc::no_such_file_or_directory) {
+ return path.str();
+ }
+ return E.getError();
+ }
+
+ auto *F = dyn_cast<vfs::RedirectingFileSystem::RedirectingFileEntry>(*E);
+ if (!F)
+ return make_error_code(llvm::errc::not_supported);
+
+ return F->getExternalContentsPath().str();
+}
+
+ErrorOr<std::string> FileSystem::GetExternalPath(const FileSpec &file_spec) {
+ return GetExternalPath(file_spec.GetPath());
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/GetOptInc.cpp b/contrib/llvm-project/lldb/source/Host/common/GetOptInc.cpp
new file mode 100644
index 000000000000..95a68c5d3c76
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/GetOptInc.cpp
@@ -0,0 +1,451 @@
+//===-- GetOptInc.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/common/GetOptInc.h"
+
+#if defined(REPLACE_GETOPT) || defined(REPLACE_GETOPT_LONG) || \
+ defined(REPLACE_GETOPT_LONG_ONLY)
+
+// getopt.cpp
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(REPLACE_GETOPT)
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt = '?'; /* character checked for validity */
+int optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+#endif
+
+#define PRINT_ERROR ((opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
+#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
+#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
+
+/* return values */
+#define BADCH (int)'?'
+#define BADARG ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define EMSG ""
+
+static int getopt_internal(int, char *const *, const char *,
+ const struct option *, int *, int);
+static int parse_long_options(char *const *, const char *,
+ const struct option *, int *, int);
+static int gcd(int, int);
+static void permute_args(int, int, int, char *const *);
+
+static const char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1; /* first option after non options (for permute) */
+
+/*
+* Compute the greatest common divisor of a and b.
+*/
+static int gcd(int a, int b) {
+ int c;
+
+ c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+
+ return (b);
+}
+
+static void pass() {}
+#define warnx(a, ...) pass();
+
+/*
+* Exchange the block from nonopt_start to nonopt_end with the block
+* from nonopt_end to opt_end (keeping the same order of arguments
+* in each block).
+*/
+static void permute_args(int panonopt_start, int panonopt_end, int opt_end,
+ char *const *nargv) {
+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+ char *swap;
+
+ /*
+ * compute lengths of blocks and number and size of cycles
+ */
+ nnonopts = panonopt_end - panonopt_start;
+ nopts = opt_end - panonopt_end;
+ ncycle = gcd(nnonopts, nopts);
+ cyclelen = (opt_end - panonopt_start) / ncycle;
+
+ for (i = 0; i < ncycle; i++) {
+ cstart = panonopt_end + i;
+ pos = cstart;
+ for (j = 0; j < cyclelen; j++) {
+ if (pos >= panonopt_end)
+ pos -= nnonopts;
+ else
+ pos += nopts;
+ swap = nargv[pos];
+ /* LINTED const cast */
+ const_cast<char **>(nargv)[pos] = nargv[cstart];
+ /* LINTED const cast */
+ const_cast<char **>(nargv)[cstart] = swap;
+ }
+ }
+}
+
+/*
+* parse_long_options --
+* Parse long options in argc/argv argument vector.
+* Returns -1 if short_too is set and the option does not match long_options.
+*/
+static int parse_long_options(char *const *nargv, const char *options,
+ const struct option *long_options, int *idx,
+ int short_too) {
+ char *current_argv, *has_equal;
+ size_t current_argv_len;
+ int i, match;
+
+ current_argv = const_cast<char *>(place);
+ match = -1;
+
+ optind++;
+
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ /* argument found (--option=arg) */
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ /* find matching long option */
+ if (strncmp(current_argv, long_options[i].name, current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == current_argv_len) {
+ /* exact match */
+ match = i;
+ break;
+ }
+ /*
+ * If this is a known short option, don't allow
+ * a partial match of a single character.
+ */
+ if (short_too && current_argv_len == 1)
+ continue;
+
+ if (match == -1) /* partial match */
+ match = i;
+ else {
+ /* ambiguous abbreviation */
+ if (PRINT_ERROR)
+ warnx(ambig, (int)current_argv_len, current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ }
+ if (match != -1) { /* option found */
+ if (long_options[match].has_arg == no_argument && has_equal) {
+ if (PRINT_ERROR)
+ warnx(noarg, (int)current_argv_len, current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ return (BADARG);
+ }
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else if (long_options[match].has_arg == required_argument) {
+ /*
+ * optional argument doesn't use next nargv
+ */
+ optarg = nargv[optind++];
+ }
+ }
+ if ((long_options[match].has_arg == required_argument) &&
+ (optarg == NULL)) {
+ /*
+ * Missing argument; leading ':' indicates no error
+ * should be generated.
+ */
+ if (PRINT_ERROR)
+ warnx(recargstring, current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ --optind;
+ return (BADARG);
+ }
+ } else { /* unknown option */
+ if (short_too) {
+ --optind;
+ return (-1);
+ }
+ if (PRINT_ERROR)
+ warnx(illoptstring, current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ if (idx)
+ *idx = match;
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ return (0);
+ } else
+ return (long_options[match].val);
+}
+
+/*
+* getopt_internal --
+* Parse argc/argv argument vector. Called by user level routines.
+*/
+static int getopt_internal(int nargc, char *const *nargv, const char *options,
+ const struct option *long_options, int *idx,
+ int flags) {
+ const char *oli; /* option letter list index */
+ int optchar, short_too;
+ static int posixly_correct = -1;
+
+ if (options == NULL)
+ return (-1);
+
+ /*
+ * XXX Some GNU programs (like cvs) set optind to 0 instead of
+ * XXX using optreset. Work around this braindamage.
+ */
+ if (optind == 0)
+ optind = optreset = 1;
+
+ /*
+ * Disable GNU extensions if POSIXLY_CORRECT is set or options
+ * string begins with a '+'.
+ */
+ if (posixly_correct == -1 || optreset)
+ posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+ if (*options == '-')
+ flags |= FLAG_ALLARGS;
+ else if (posixly_correct || *options == '+')
+ flags &= ~FLAG_PERMUTE;
+ if (*options == '+' || *options == '-')
+ options++;
+
+ optarg = NULL;
+ if (optreset)
+ nonopt_start = nonopt_end = -1;
+start:
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc) { /* end of argument vector */
+ place = EMSG;
+ if (nonopt_end != -1) {
+ /* do permutation, if we have to */
+ permute_args(nonopt_start, nonopt_end, optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ } else if (nonopt_start != -1) {
+ /*
+ * If we skipped non-options, set optind
+ * to the first of them.
+ */
+ optind = nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ if (*(place = nargv[optind]) != '-' ||
+ (place[1] == '\0' && strchr(options, '-') == NULL)) {
+ place = EMSG; /* found non-option */
+ if (flags & FLAG_ALLARGS) {
+ /*
+ * GNU extension:
+ * return non-option as argument to option 1
+ */
+ optarg = nargv[optind++];
+ return (INORDER);
+ }
+ if (!(flags & FLAG_PERMUTE)) {
+ /*
+ * If no permutation wanted, stop parsing
+ * at first non-option.
+ */
+ return (-1);
+ }
+ /* do permutation */
+ if (nonopt_start == -1)
+ nonopt_start = optind;
+ else if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end, optind, nargv);
+ nonopt_start = optind - (nonopt_end - nonopt_start);
+ nonopt_end = -1;
+ }
+ optind++;
+ /* process next argument */
+ goto start;
+ }
+ if (nonopt_start != -1 && nonopt_end == -1)
+ nonopt_end = optind;
+
+ /*
+ * If we have "-" do nothing, if "--" we are done.
+ */
+ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
+ optind++;
+ place = EMSG;
+ /*
+ * We found an option (--), so if we skipped
+ * non-options, we have to permute.
+ */
+ if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end, optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ }
+
+ /*
+ * Check long options if:
+ * 1) we were passed some
+ * 2) the arg is not just "-"
+ * 3) either the arg starts with -- we are getopt_long_only()
+ */
+ if (long_options != NULL && place != nargv[optind] &&
+ (*place == '-' || (flags & FLAG_LONGONLY))) {
+ short_too = 0;
+ if (*place == '-')
+ place++; /* --foo long option */
+ else if (*place != ':' && strchr(options, *place) != NULL)
+ short_too = 1; /* could be short option too */
+
+ optchar = parse_long_options(nargv, options, long_options, idx, short_too);
+ if (optchar != -1) {
+ place = EMSG;
+ return (optchar);
+ }
+ }
+
+ if ((optchar = (int)*place++) == (int)':' ||
+ (optchar == (int)'-' && *place != '\0') ||
+ (oli = strchr(options, optchar)) == NULL) {
+ /*
+ * If the user specified "-" and '-' isn't listed in
+ * options, return -1 (non-option) as per POSIX.
+ * Otherwise, it is an unknown option character (or ':').
+ */
+ if (optchar == (int)'-' && *place == '\0')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (PRINT_ERROR)
+ warnx(illoptchar, optchar);
+ optopt = optchar;
+ return (BADCH);
+ }
+ if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
+ /* -W long-option */
+ if (*place) /* no space */
+ /* NOTHING */;
+ else if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else /* white space */
+ place = nargv[optind];
+ optchar = parse_long_options(nargv, options, long_options, idx, 0);
+ place = EMSG;
+ return (optchar);
+ }
+ if (*++oli != ':') { /* doesn't take argument */
+ if (!*place)
+ ++optind;
+ } else { /* takes (optional) argument */
+ optarg = NULL;
+ if (*place) /* no white space */
+ optarg = const_cast<char *>(place);
+ else if (oli[1] != ':') { /* arg not optional */
+ if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else
+ optarg = nargv[optind];
+ }
+ place = EMSG;
+ ++optind;
+ }
+ /* dump back option letter */
+ return (optchar);
+}
+
+/*
+* getopt --
+* Parse argc/argv argument vector.
+*
+* [eventually this will replace the BSD getopt]
+*/
+#if defined(REPLACE_GETOPT)
+int getopt(int nargc, char *const *nargv, const char *options) {
+
+ /*
+ * We don't pass FLAG_PERMUTE to getopt_internal() since
+ * the BSD getopt(3) (unlike GNU) has never done this.
+ *
+ * Furthermore, since many privileged programs call getopt()
+ * before dropping privileges it makes sense to keep things
+ * as simple (and bug-free) as possible.
+ */
+ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
+}
+#endif
+
+/*
+* getopt_long --
+* Parse argc/argv argument vector.
+*/
+#if defined(REPLACE_GETOPT_LONG)
+int getopt_long(int nargc, char *const *nargv, const char *options,
+ const struct option *long_options, int *idx) {
+ return (
+ getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE));
+}
+#endif
+
+/*
+* getopt_long_only --
+* Parse argc/argv argument vector.
+*/
+#if defined(REPLACE_GETOPT_LONG_ONLY)
+int getopt_long_only(int nargc, char *const *nargv, const char *options,
+ const struct option *long_options, int *idx) {
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE | FLAG_LONGONLY));
+}
+#endif
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Host/common/Host.cpp b/contrib/llvm-project/lldb/source/Host/common/Host.cpp
new file mode 100644
index 000000000000..3ba9ab7f21f3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/Host.cpp
@@ -0,0 +1,672 @@
+//===-- Host.cpp ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// C includes
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <dlfcn.h>
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#include <mach/mach_init.h>
+#include <mach/mach_port.h>
+#endif
+
+#if defined(__linux__) || defined(__FreeBSD__) || \
+ defined(__FreeBSD_kernel__) || defined(__APPLE__) || \
+ defined(__NetBSD__) || defined(__OpenBSD__)
+#if !defined(__ANDROID__)
+#include <spawn.h>
+#endif
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <pthread_np.h>
+#endif
+
+#if defined(__NetBSD__)
+#include <lwp.h>
+#endif
+
+#include <csignal>
+
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/MonitoringProcessLauncher.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Host/ProcessLauncher.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
+#include "lldb/Utility/DataBufferLLVM.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Predicate.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-private-forward.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FileSystem.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/ConnectionGenericFileWindows.h"
+#include "lldb/Host/windows/ProcessLauncherWindows.h"
+#else
+#include "lldb/Host/posix/ProcessLauncherPosixFork.h"
+#endif
+
+#if defined(__APPLE__)
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+#endif
+
+extern "C" {
+int __pthread_chdir(const char *path);
+int __pthread_fchdir(int fildes);
+}
+
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+#if !defined(__APPLE__) && !defined(_WIN32)
+struct MonitorInfo {
+ lldb::pid_t pid; // The process ID to monitor
+ Host::MonitorChildProcessCallback
+ callback; // The callback function to call when "pid" exits or signals
+ bool monitor_signals; // If true, call the callback when "pid" gets signaled.
+};
+
+static thread_result_t MonitorChildProcessThreadFunction(void *arg);
+
+llvm::Expected<HostThread> Host::StartMonitoringChildProcess(
+ const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid,
+ bool monitor_signals) {
+ MonitorInfo *info_ptr = new MonitorInfo();
+
+ info_ptr->pid = pid;
+ info_ptr->callback = callback;
+ info_ptr->monitor_signals = monitor_signals;
+
+ char thread_name[256];
+ ::snprintf(thread_name, sizeof(thread_name),
+ "<lldb.host.wait4(pid=%" PRIu64 ")>", pid);
+ return ThreadLauncher::LaunchThread(
+ thread_name, MonitorChildProcessThreadFunction, info_ptr, 0);
+}
+
+#ifndef __linux__
+// Scoped class that will disable thread canceling when it is constructed, and
+// exception safely restore the previous value it when it goes out of scope.
+class ScopedPThreadCancelDisabler {
+public:
+ ScopedPThreadCancelDisabler() {
+ // Disable the ability for this thread to be cancelled
+ int err = ::pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m_old_state);
+ if (err != 0)
+ m_old_state = -1;
+ }
+
+ ~ScopedPThreadCancelDisabler() {
+ // Restore the ability for this thread to be cancelled to what it
+ // previously was.
+ if (m_old_state != -1)
+ ::pthread_setcancelstate(m_old_state, 0);
+ }
+
+private:
+ int m_old_state; // Save the old cancelability state.
+};
+#endif // __linux__
+
+#ifdef __linux__
+#if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
+static __thread volatile sig_atomic_t g_usr1_called;
+#else
+static thread_local volatile sig_atomic_t g_usr1_called;
+#endif
+
+static void SigUsr1Handler(int) { g_usr1_called = 1; }
+#endif // __linux__
+
+static bool CheckForMonitorCancellation() {
+#ifdef __linux__
+ if (g_usr1_called) {
+ g_usr1_called = 0;
+ return true;
+ }
+#else
+ ::pthread_testcancel();
+#endif
+ return false;
+}
+
+static thread_result_t MonitorChildProcessThreadFunction(void *arg) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ const char *function = __FUNCTION__;
+ if (log)
+ log->Printf("%s (arg = %p) thread starting...", function, arg);
+
+ MonitorInfo *info = (MonitorInfo *)arg;
+
+ const Host::MonitorChildProcessCallback callback = info->callback;
+ const bool monitor_signals = info->monitor_signals;
+
+ assert(info->pid <= UINT32_MAX);
+ const ::pid_t pid = monitor_signals ? -1 * getpgid(info->pid) : info->pid;
+
+ delete info;
+
+ int status = -1;
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
+#define __WALL 0
+#endif
+ const int options = __WALL;
+
+#ifdef __linux__
+ // This signal is only used to interrupt the thread from waitpid
+ struct sigaction sigUsr1Action;
+ memset(&sigUsr1Action, 0, sizeof(sigUsr1Action));
+ sigUsr1Action.sa_handler = SigUsr1Handler;
+ ::sigaction(SIGUSR1, &sigUsr1Action, nullptr);
+#endif // __linux__
+
+ while (1) {
+ log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS);
+ if (log)
+ log->Printf("%s ::waitpid (pid = %" PRIi32 ", &status, options = %i)...",
+ function, pid, options);
+
+ if (CheckForMonitorCancellation())
+ break;
+
+ // Get signals from all children with same process group of pid
+ const ::pid_t wait_pid = ::waitpid(pid, &status, options);
+
+ if (CheckForMonitorCancellation())
+ break;
+
+ if (wait_pid == -1) {
+ if (errno == EINTR)
+ continue;
+ else {
+ LLDB_LOG(log,
+ "arg = {0}, thread exiting because waitpid failed ({1})...",
+ arg, llvm::sys::StrError());
+ break;
+ }
+ } else if (wait_pid > 0) {
+ bool exited = false;
+ int signal = 0;
+ int exit_status = 0;
+ const char *status_cstr = nullptr;
+ if (WIFSTOPPED(status)) {
+ signal = WSTOPSIG(status);
+ status_cstr = "STOPPED";
+ } else if (WIFEXITED(status)) {
+ exit_status = WEXITSTATUS(status);
+ status_cstr = "EXITED";
+ exited = true;
+ } else if (WIFSIGNALED(status)) {
+ signal = WTERMSIG(status);
+ status_cstr = "SIGNALED";
+ if (wait_pid == abs(pid)) {
+ exited = true;
+ exit_status = -1;
+ }
+ } else {
+ status_cstr = "(\?\?\?)";
+ }
+
+ // Scope for pthread_cancel_disabler
+ {
+#ifndef __linux__
+ ScopedPThreadCancelDisabler pthread_cancel_disabler;
+#endif
+
+ log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS);
+ if (log)
+ log->Printf("%s ::waitpid (pid = %" PRIi32
+ ", &status, options = %i) => pid = %" PRIi32
+ ", status = 0x%8.8x (%s), signal = %i, exit_state = %i",
+ function, pid, options, wait_pid, status, status_cstr,
+ signal, exit_status);
+
+ if (exited || (signal != 0 && monitor_signals)) {
+ bool callback_return = false;
+ if (callback)
+ callback_return = callback(wait_pid, exited, signal, exit_status);
+
+ // If our process exited, then this thread should exit
+ if (exited && wait_pid == abs(pid)) {
+ if (log)
+ log->Printf("%s (arg = %p) thread exiting because pid received "
+ "exit signal...",
+ __FUNCTION__, arg);
+ break;
+ }
+ // If the callback returns true, it means this process should exit
+ if (callback_return) {
+ if (log)
+ log->Printf("%s (arg = %p) thread exiting because callback "
+ "returned true...",
+ __FUNCTION__, arg);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS);
+ if (log)
+ log->Printf("%s (arg = %p) thread exiting...", __FUNCTION__, arg);
+
+ return nullptr;
+}
+
+#endif // #if !defined (__APPLE__) && !defined (_WIN32)
+
+#if !defined(__APPLE__)
+
+void Host::SystemLog(SystemLogType type, const char *format, va_list args) {
+ vfprintf(stderr, format, args);
+}
+
+#endif
+
+void Host::SystemLog(SystemLogType type, const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ SystemLog(type, format, args);
+ va_end(args);
+}
+
+lldb::pid_t Host::GetCurrentProcessID() { return ::getpid(); }
+
+#ifndef _WIN32
+
+lldb::thread_t Host::GetCurrentThread() {
+ return lldb::thread_t(pthread_self());
+}
+
+const char *Host::GetSignalAsCString(int signo) {
+ switch (signo) {
+ case SIGHUP:
+ return "SIGHUP"; // 1 hangup
+ case SIGINT:
+ return "SIGINT"; // 2 interrupt
+ case SIGQUIT:
+ return "SIGQUIT"; // 3 quit
+ case SIGILL:
+ return "SIGILL"; // 4 illegal instruction (not reset when caught)
+ case SIGTRAP:
+ return "SIGTRAP"; // 5 trace trap (not reset when caught)
+ case SIGABRT:
+ return "SIGABRT"; // 6 abort()
+#if defined(SIGPOLL)
+#if !defined(SIGIO) || (SIGPOLL != SIGIO)
+ // Under some GNU/Linux, SIGPOLL and SIGIO are the same. Causing the build to
+ // fail with 'multiple define cases with same value'
+ case SIGPOLL:
+ return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported)
+#endif
+#endif
+#if defined(SIGEMT)
+ case SIGEMT:
+ return "SIGEMT"; // 7 EMT instruction
+#endif
+ case SIGFPE:
+ return "SIGFPE"; // 8 floating point exception
+ case SIGKILL:
+ return "SIGKILL"; // 9 kill (cannot be caught or ignored)
+ case SIGBUS:
+ return "SIGBUS"; // 10 bus error
+ case SIGSEGV:
+ return "SIGSEGV"; // 11 segmentation violation
+ case SIGSYS:
+ return "SIGSYS"; // 12 bad argument to system call
+ case SIGPIPE:
+ return "SIGPIPE"; // 13 write on a pipe with no one to read it
+ case SIGALRM:
+ return "SIGALRM"; // 14 alarm clock
+ case SIGTERM:
+ return "SIGTERM"; // 15 software termination signal from kill
+ case SIGURG:
+ return "SIGURG"; // 16 urgent condition on IO channel
+ case SIGSTOP:
+ return "SIGSTOP"; // 17 sendable stop signal not from tty
+ case SIGTSTP:
+ return "SIGTSTP"; // 18 stop signal from tty
+ case SIGCONT:
+ return "SIGCONT"; // 19 continue a stopped process
+ case SIGCHLD:
+ return "SIGCHLD"; // 20 to parent on child stop or exit
+ case SIGTTIN:
+ return "SIGTTIN"; // 21 to readers pgrp upon background tty read
+ case SIGTTOU:
+ return "SIGTTOU"; // 22 like TTIN for output if (tp->t_local&LTOSTOP)
+#if defined(SIGIO)
+ case SIGIO:
+ return "SIGIO"; // 23 input/output possible signal
+#endif
+ case SIGXCPU:
+ return "SIGXCPU"; // 24 exceeded CPU time limit
+ case SIGXFSZ:
+ return "SIGXFSZ"; // 25 exceeded file size limit
+ case SIGVTALRM:
+ return "SIGVTALRM"; // 26 virtual time alarm
+ case SIGPROF:
+ return "SIGPROF"; // 27 profiling time alarm
+#if defined(SIGWINCH)
+ case SIGWINCH:
+ return "SIGWINCH"; // 28 window size changes
+#endif
+#if defined(SIGINFO)
+ case SIGINFO:
+ return "SIGINFO"; // 29 information request
+#endif
+ case SIGUSR1:
+ return "SIGUSR1"; // 30 user defined signal 1
+ case SIGUSR2:
+ return "SIGUSR2"; // 31 user defined signal 2
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+#endif
+
+#if !defined(__APPLE__) // see Host.mm
+
+bool Host::GetBundleDirectory(const FileSpec &file, FileSpec &bundle) {
+ bundle.Clear();
+ return false;
+}
+
+bool Host::ResolveExecutableInBundle(FileSpec &file) { return false; }
+#endif
+
+#ifndef _WIN32
+
+FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) {
+ FileSpec module_filespec;
+#if !defined(__ANDROID__)
+ Dl_info info;
+ if (::dladdr(host_addr, &info)) {
+ if (info.dli_fname) {
+ module_filespec.SetFile(info.dli_fname, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(module_filespec);
+ }
+ }
+#endif
+ return module_filespec;
+}
+
+#endif
+
+#if !defined(__linux__)
+bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) {
+ return false;
+}
+#endif
+
+struct ShellInfo {
+ ShellInfo()
+ : process_reaped(false), pid(LLDB_INVALID_PROCESS_ID), signo(-1),
+ status(-1) {}
+
+ lldb_private::Predicate<bool> process_reaped;
+ lldb::pid_t pid;
+ int signo;
+ int status;
+};
+
+static bool
+MonitorShellCommand(std::shared_ptr<ShellInfo> shell_info, lldb::pid_t pid,
+ bool exited, // True if the process did exit
+ int signo, // Zero for no signal
+ int status) // Exit value of process if signal is zero
+{
+ shell_info->pid = pid;
+ shell_info->signo = signo;
+ shell_info->status = status;
+ // Let the thread running Host::RunShellCommand() know that the process
+ // exited and that ShellInfo has been filled in by broadcasting to it
+ shell_info->process_reaped.SetValue(true, eBroadcastAlways);
+ return true;
+}
+
+Status Host::RunShellCommand(const char *command, const FileSpec &working_dir,
+ int *status_ptr, int *signo_ptr,
+ std::string *command_output_ptr,
+ const Timeout<std::micro> &timeout,
+ bool run_in_default_shell,
+ bool hide_stderr) {
+ return RunShellCommand(Args(command), working_dir, status_ptr, signo_ptr,
+ command_output_ptr, timeout, run_in_default_shell,
+ hide_stderr);
+}
+
+Status Host::RunShellCommand(const Args &args, const FileSpec &working_dir,
+ int *status_ptr, int *signo_ptr,
+ std::string *command_output_ptr,
+ const Timeout<std::micro> &timeout,
+ bool run_in_default_shell,
+ bool hide_stderr) {
+ Status error;
+ ProcessLaunchInfo launch_info;
+ launch_info.SetArchitecture(HostInfo::GetArchitecture());
+ if (run_in_default_shell) {
+ // Run the command in a shell
+ launch_info.SetShell(HostInfo::GetDefaultShell());
+ launch_info.GetArguments().AppendArguments(args);
+ const bool localhost = true;
+ const bool will_debug = false;
+ const bool first_arg_is_full_shell_command = false;
+ launch_info.ConvertArgumentsForLaunchingInShell(
+ error, localhost, will_debug, first_arg_is_full_shell_command, 0);
+ } else {
+ // No shell, just run it
+ const bool first_arg_is_executable = true;
+ launch_info.SetArguments(args, first_arg_is_executable);
+ }
+
+ if (working_dir)
+ launch_info.SetWorkingDirectory(working_dir);
+ llvm::SmallString<64> output_file_path;
+
+ if (command_output_ptr) {
+ // Create a temporary file to get the stdout/stderr and redirect the output
+ // of the command into this file. We will later read this file if all goes
+ // well and fill the data into "command_output_ptr"
+ if (FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir()) {
+ tmpdir_file_spec.AppendPathComponent("lldb-shell-output.%%%%%%");
+ llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(),
+ output_file_path);
+ } else {
+ llvm::sys::fs::createTemporaryFile("lldb-shell-output.%%%%%%", "",
+ output_file_path);
+ }
+ }
+
+ FileSpec output_file_spec(output_file_path.c_str());
+ // Set up file descriptors.
+ launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false);
+ if (output_file_spec)
+ launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_spec, false,
+ true);
+ else
+ launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true);
+
+ if (output_file_spec && !hide_stderr)
+ launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO);
+ else
+ launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true);
+
+ std::shared_ptr<ShellInfo> shell_info_sp(new ShellInfo());
+ const bool monitor_signals = false;
+ launch_info.SetMonitorProcessCallback(
+ std::bind(MonitorShellCommand, shell_info_sp, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3,
+ std::placeholders::_4),
+ monitor_signals);
+
+ error = LaunchProcess(launch_info);
+ const lldb::pid_t pid = launch_info.GetProcessID();
+
+ if (error.Success() && pid == LLDB_INVALID_PROCESS_ID)
+ error.SetErrorString("failed to get process ID");
+
+ if (error.Success()) {
+ if (!shell_info_sp->process_reaped.WaitForValueEqualTo(true, timeout)) {
+ error.SetErrorString("timed out waiting for shell command to complete");
+
+ // Kill the process since it didn't complete within the timeout specified
+ Kill(pid, SIGKILL);
+ // Wait for the monitor callback to get the message
+ shell_info_sp->process_reaped.WaitForValueEqualTo(
+ true, std::chrono::seconds(1));
+ } else {
+ if (status_ptr)
+ *status_ptr = shell_info_sp->status;
+
+ if (signo_ptr)
+ *signo_ptr = shell_info_sp->signo;
+
+ if (command_output_ptr) {
+ command_output_ptr->clear();
+ uint64_t file_size =
+ FileSystem::Instance().GetByteSize(output_file_spec);
+ if (file_size > 0) {
+ if (file_size > command_output_ptr->max_size()) {
+ error.SetErrorStringWithFormat(
+ "shell command output is too large to fit into a std::string");
+ } else {
+ auto Buffer =
+ FileSystem::Instance().CreateDataBuffer(output_file_spec);
+ if (error.Success())
+ command_output_ptr->assign(Buffer->GetChars(),
+ Buffer->GetByteSize());
+ }
+ }
+ }
+ }
+ }
+
+ llvm::sys::fs::remove(output_file_spec.GetPath());
+ return error;
+}
+
+// The functions below implement process launching for non-Apple-based
+// platforms
+#if !defined(__APPLE__)
+Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) {
+ std::unique_ptr<ProcessLauncher> delegate_launcher;
+#if defined(_WIN32)
+ delegate_launcher.reset(new ProcessLauncherWindows());
+#else
+ delegate_launcher.reset(new ProcessLauncherPosixFork());
+#endif
+ MonitoringProcessLauncher launcher(std::move(delegate_launcher));
+
+ Status error;
+ HostProcess process = launcher.LaunchProcess(launch_info, error);
+
+ // TODO(zturner): It would be better if the entire HostProcess were returned
+ // instead of writing it into this structure.
+ launch_info.SetProcessID(process.GetProcessId());
+
+ return error;
+}
+#endif // !defined(__APPLE__)
+
+#ifndef _WIN32
+void Host::Kill(lldb::pid_t pid, int signo) { ::kill(pid, signo); }
+
+#endif
+
+#if !defined(__APPLE__)
+bool Host::OpenFileInExternalEditor(const FileSpec &file_spec,
+ uint32_t line_no) {
+ return false;
+}
+
+#endif
+
+std::unique_ptr<Connection> Host::CreateDefaultConnection(llvm::StringRef url) {
+#if defined(_WIN32)
+ if (url.startswith("file://"))
+ return std::unique_ptr<Connection>(new ConnectionGenericFile());
+#endif
+ return std::unique_ptr<Connection>(new ConnectionFileDescriptor());
+}
+
+#if defined(LLVM_ON_UNIX)
+WaitStatus WaitStatus::Decode(int wstatus) {
+ if (WIFEXITED(wstatus))
+ return {Exit, uint8_t(WEXITSTATUS(wstatus))};
+ else if (WIFSIGNALED(wstatus))
+ return {Signal, uint8_t(WTERMSIG(wstatus))};
+ else if (WIFSTOPPED(wstatus))
+ return {Stop, uint8_t(WSTOPSIG(wstatus))};
+ llvm_unreachable("Unknown wait status");
+}
+#endif
+
+void llvm::format_provider<WaitStatus>::format(const WaitStatus &WS,
+ raw_ostream &OS,
+ StringRef Options) {
+ if (Options == "g") {
+ char type;
+ switch (WS.type) {
+ case WaitStatus::Exit:
+ type = 'W';
+ break;
+ case WaitStatus::Signal:
+ type = 'X';
+ break;
+ case WaitStatus::Stop:
+ type = 'S';
+ break;
+ }
+ OS << formatv("{0}{1:x-2}", type, WS.status);
+ return;
+ }
+
+ assert(Options.empty());
+ const char *desc;
+ switch(WS.type) {
+ case WaitStatus::Exit:
+ desc = "Exited with status";
+ break;
+ case WaitStatus::Signal:
+ desc = "Killed by signal";
+ break;
+ case WaitStatus::Stop:
+ desc = "Stopped by signal";
+ break;
+ }
+ OS << desc << " " << int(WS.status);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/HostInfoBase.cpp b/contrib/llvm-project/lldb/source/Host/common/HostInfoBase.cpp
new file mode 100644
index 000000000000..130f0eb8ac8d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/HostInfoBase.cpp
@@ -0,0 +1,353 @@
+//===-- HostInfoBase.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Config.h"
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/HostInfoBase.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <mutex>
+#include <thread>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+// The HostInfoBaseFields is a work around for windows not supporting static
+// variables correctly in a thread safe way. Really each of the variables in
+// HostInfoBaseFields should live in the functions in which they are used and
+// each one should be static, but the work around is in place to avoid this
+// restriction. Ick.
+
+struct HostInfoBaseFields {
+ ~HostInfoBaseFields() {
+ if (FileSystem::Instance().Exists(m_lldb_process_tmp_dir)) {
+ // Remove the LLDB temporary directory if we have one. Set "recurse" to
+ // true to all files that were created for the LLDB process can be
+ // cleaned up.
+ llvm::sys::fs::remove_directories(m_lldb_process_tmp_dir.GetPath());
+ }
+ }
+
+ std::string m_host_triple;
+
+ ArchSpec m_host_arch_32;
+ ArchSpec m_host_arch_64;
+
+ FileSpec m_lldb_so_dir;
+ FileSpec m_lldb_support_exe_dir;
+ FileSpec m_lldb_headers_dir;
+ FileSpec m_lldb_clang_resource_dir;
+ FileSpec m_lldb_system_plugin_dir;
+ FileSpec m_lldb_user_plugin_dir;
+ FileSpec m_lldb_process_tmp_dir;
+ FileSpec m_lldb_global_tmp_dir;
+};
+
+HostInfoBaseFields *g_fields = nullptr;
+}
+
+void HostInfoBase::Initialize() { g_fields = new HostInfoBaseFields(); }
+
+void HostInfoBase::Terminate() {
+ delete g_fields;
+ g_fields = nullptr;
+}
+
+llvm::StringRef HostInfoBase::GetTargetTriple() {
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ g_fields->m_host_triple =
+ HostInfo::GetArchitecture().GetTriple().getTriple();
+ });
+ return g_fields->m_host_triple;
+}
+
+const ArchSpec &HostInfoBase::GetArchitecture(ArchitectureKind arch_kind) {
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ HostInfo::ComputeHostArchitectureSupport(g_fields->m_host_arch_32,
+ g_fields->m_host_arch_64);
+ });
+
+ // If an explicit 32 or 64-bit architecture was requested, return that.
+ if (arch_kind == eArchKind32)
+ return g_fields->m_host_arch_32;
+ if (arch_kind == eArchKind64)
+ return g_fields->m_host_arch_64;
+
+ // Otherwise prefer the 64-bit architecture if it is valid.
+ return (g_fields->m_host_arch_64.IsValid()) ? g_fields->m_host_arch_64
+ : g_fields->m_host_arch_32;
+}
+
+llvm::Optional<HostInfoBase::ArchitectureKind> HostInfoBase::ParseArchitectureKind(llvm::StringRef kind) {
+ return llvm::StringSwitch<llvm::Optional<ArchitectureKind>>(kind)
+ .Case(LLDB_ARCH_DEFAULT, eArchKindDefault)
+ .Case(LLDB_ARCH_DEFAULT_32BIT, eArchKind32)
+ .Case(LLDB_ARCH_DEFAULT_64BIT, eArchKind64)
+ .Default(llvm::None);
+}
+
+FileSpec HostInfoBase::GetShlibDir() {
+ static llvm::once_flag g_once_flag;
+ static bool success = false;
+ llvm::call_once(g_once_flag, []() {
+ success = HostInfo::ComputeSharedLibraryDirectory(g_fields->m_lldb_so_dir);
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ LLDB_LOG(log, "shlib dir -> `{0}`", g_fields->m_lldb_so_dir);
+ });
+ return success ? g_fields->m_lldb_so_dir : FileSpec();
+}
+
+FileSpec HostInfoBase::GetSupportExeDir() {
+ static llvm::once_flag g_once_flag;
+ static bool success = false;
+ llvm::call_once(g_once_flag, []() {
+ success =
+ HostInfo::ComputeSupportExeDirectory(g_fields->m_lldb_support_exe_dir);
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ LLDB_LOG(log, "support exe dir -> `{0}`", g_fields->m_lldb_support_exe_dir);
+ });
+ return success ? g_fields->m_lldb_support_exe_dir : FileSpec();
+}
+
+FileSpec HostInfoBase::GetHeaderDir() {
+ static llvm::once_flag g_once_flag;
+ static bool success = false;
+ llvm::call_once(g_once_flag, []() {
+ success = HostInfo::ComputeHeaderDirectory(g_fields->m_lldb_headers_dir);
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ LLDB_LOG(log, "header dir -> `{0}`", g_fields->m_lldb_headers_dir);
+ });
+ return success ? g_fields->m_lldb_headers_dir : FileSpec();
+}
+
+FileSpec HostInfoBase::GetSystemPluginDir() {
+ static llvm::once_flag g_once_flag;
+ static bool success = false;
+ llvm::call_once(g_once_flag, []() {
+ success = HostInfo::ComputeSystemPluginsDirectory(
+ g_fields->m_lldb_system_plugin_dir);
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ LLDB_LOG(log, "system plugin dir -> `{0}`",
+ g_fields->m_lldb_system_plugin_dir);
+ });
+ return success ? g_fields->m_lldb_system_plugin_dir : FileSpec();
+}
+
+FileSpec HostInfoBase::GetUserPluginDir() {
+ static llvm::once_flag g_once_flag;
+ static bool success = false;
+ llvm::call_once(g_once_flag, []() {
+ success =
+ HostInfo::ComputeUserPluginsDirectory(g_fields->m_lldb_user_plugin_dir);
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ LLDB_LOG(log, "user plugin dir -> `{0}`", g_fields->m_lldb_user_plugin_dir);
+ });
+ return success ? g_fields->m_lldb_user_plugin_dir : FileSpec();
+}
+
+FileSpec HostInfoBase::GetProcessTempDir() {
+ static llvm::once_flag g_once_flag;
+ static bool success = false;
+ llvm::call_once(g_once_flag, []() {
+ success = HostInfo::ComputeProcessTempFileDirectory(
+ g_fields->m_lldb_process_tmp_dir);
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ LLDB_LOG(log, "process temp dir -> `{0}`",
+ g_fields->m_lldb_process_tmp_dir);
+ });
+ return success ? g_fields->m_lldb_process_tmp_dir : FileSpec();
+}
+
+FileSpec HostInfoBase::GetGlobalTempDir() {
+ static llvm::once_flag g_once_flag;
+ static bool success = false;
+ llvm::call_once(g_once_flag, []() {
+ success = HostInfo::ComputeGlobalTempFileDirectory(
+ g_fields->m_lldb_global_tmp_dir);
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ LLDB_LOG(log, "global temp dir -> `{0}`", g_fields->m_lldb_global_tmp_dir);
+ });
+ return success ? g_fields->m_lldb_global_tmp_dir : FileSpec();
+}
+
+ArchSpec HostInfoBase::GetAugmentedArchSpec(llvm::StringRef triple) {
+ if (triple.empty())
+ return ArchSpec();
+ llvm::Triple normalized_triple(llvm::Triple::normalize(triple));
+ if (!ArchSpec::ContainsOnlyArch(normalized_triple))
+ return ArchSpec(triple);
+
+ if (auto kind = HostInfo::ParseArchitectureKind(triple))
+ return HostInfo::GetArchitecture(*kind);
+
+ llvm::Triple host_triple(llvm::sys::getDefaultTargetTriple());
+
+ if (normalized_triple.getVendorName().empty())
+ normalized_triple.setVendor(host_triple.getVendor());
+ if (normalized_triple.getOSName().empty())
+ normalized_triple.setOS(host_triple.getOS());
+ if (normalized_triple.getEnvironmentName().empty())
+ normalized_triple.setEnvironment(host_triple.getEnvironment());
+ return ArchSpec(normalized_triple);
+}
+
+bool HostInfoBase::ComputePathRelativeToLibrary(FileSpec &file_spec,
+ llvm::StringRef dir) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+
+ FileSpec lldb_file_spec = GetShlibDir();
+ if (!lldb_file_spec)
+ return false;
+
+ std::string raw_path = lldb_file_spec.GetPath();
+ if (log)
+ log->Printf("HostInfo::%s() attempting to "
+ "derive the path %s relative to liblldb install path: %s",
+ __FUNCTION__, dir.data(), raw_path.c_str());
+
+ // Drop bin (windows) or lib
+ llvm::StringRef parent_path = llvm::sys::path::parent_path(raw_path);
+ if (parent_path.empty()) {
+ if (log)
+ log->Printf("HostInfo::%s() failed to find liblldb within the shared "
+ "lib path",
+ __FUNCTION__);
+ return false;
+ }
+
+ raw_path = (parent_path + dir).str();
+ if (log)
+ log->Printf("HostInfo::%s() derived the path as: %s", __FUNCTION__,
+ raw_path.c_str());
+ file_spec.GetDirectory().SetString(raw_path);
+ return (bool)file_spec.GetDirectory();
+}
+
+bool HostInfoBase::ComputeSharedLibraryDirectory(FileSpec &file_spec) {
+ // To get paths related to LLDB we get the path to the executable that
+ // contains this function. On MacOSX this will be "LLDB.framework/.../LLDB".
+ // On other posix systems, we will get .../lib(64|32)?/liblldb.so.
+
+ FileSpec lldb_file_spec(Host::GetModuleFileSpecForHostAddress(
+ reinterpret_cast<void *>(reinterpret_cast<intptr_t>(
+ HostInfoBase::ComputeSharedLibraryDirectory))));
+
+ // This is necessary because when running the testsuite the shlib might be a
+ // symbolic link inside the Python resource dir.
+ FileSystem::Instance().ResolveSymbolicLink(lldb_file_spec, lldb_file_spec);
+
+ // Remove the filename so that this FileSpec only represents the directory.
+ file_spec.GetDirectory() = lldb_file_spec.GetDirectory();
+
+ return (bool)file_spec.GetDirectory();
+}
+
+bool HostInfoBase::ComputeSupportExeDirectory(FileSpec &file_spec) {
+ file_spec = GetShlibDir();
+ return bool(file_spec);
+}
+
+bool HostInfoBase::ComputeProcessTempFileDirectory(FileSpec &file_spec) {
+ FileSpec temp_file_spec;
+ if (!HostInfo::ComputeGlobalTempFileDirectory(temp_file_spec))
+ return false;
+
+ std::string pid_str{llvm::to_string(Host::GetCurrentProcessID())};
+ temp_file_spec.AppendPathComponent(pid_str);
+ if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
+ return false;
+
+ file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
+ return true;
+}
+
+bool HostInfoBase::ComputeTempFileBaseDirectory(FileSpec &file_spec) {
+ llvm::SmallVector<char, 16> tmpdir;
+ llvm::sys::path::system_temp_directory(/*ErasedOnReboot*/ true, tmpdir);
+ file_spec = FileSpec(std::string(tmpdir.data(), tmpdir.size()));
+ FileSystem::Instance().Resolve(file_spec);
+ return true;
+}
+
+bool HostInfoBase::ComputeGlobalTempFileDirectory(FileSpec &file_spec) {
+ file_spec.Clear();
+
+ FileSpec temp_file_spec;
+ if (!HostInfo::ComputeTempFileBaseDirectory(temp_file_spec))
+ return false;
+
+ temp_file_spec.AppendPathComponent("lldb");
+ if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
+ return false;
+
+ file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
+ return true;
+}
+
+bool HostInfoBase::ComputeHeaderDirectory(FileSpec &file_spec) {
+ // TODO(zturner): Figure out how to compute the header directory for all
+ // platforms.
+ return false;
+}
+
+bool HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec) {
+ // TODO(zturner): Figure out how to compute the system plugins directory for
+ // all platforms.
+ return false;
+}
+
+bool HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) {
+ // TODO(zturner): Figure out how to compute the user plugins directory for
+ // all platforms.
+ return false;
+}
+
+void HostInfoBase::ComputeHostArchitectureSupport(ArchSpec &arch_32,
+ ArchSpec &arch_64) {
+ llvm::Triple triple(llvm::sys::getProcessTriple());
+
+ arch_32.Clear();
+ arch_64.Clear();
+
+ switch (triple.getArch()) {
+ default:
+ arch_32.SetTriple(triple);
+ break;
+
+ case llvm::Triple::aarch64:
+ case llvm::Triple::ppc64:
+ case llvm::Triple::ppc64le:
+ case llvm::Triple::x86_64:
+ arch_64.SetTriple(triple);
+ arch_32.SetTriple(triple.get32BitArchVariant());
+ break;
+
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ case llvm::Triple::sparcv9:
+ case llvm::Triple::systemz:
+ arch_64.SetTriple(triple);
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/HostNativeThreadBase.cpp b/contrib/llvm-project/lldb/source/Host/common/HostNativeThreadBase.cpp
new file mode 100644
index 000000000000..a5f876a7232a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/HostNativeThreadBase.cpp
@@ -0,0 +1,70 @@
+//===-- HostNativeThreadBase.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/HostNativeThreadBase.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+HostNativeThreadBase::HostNativeThreadBase()
+ : m_thread(LLDB_INVALID_HOST_THREAD), m_result(0) {}
+
+HostNativeThreadBase::HostNativeThreadBase(thread_t thread)
+ : m_thread(thread), m_result(0) {}
+
+lldb::thread_t HostNativeThreadBase::GetSystemHandle() const {
+ return m_thread;
+}
+
+lldb::thread_result_t HostNativeThreadBase::GetResult() const {
+ return m_result;
+}
+
+bool HostNativeThreadBase::IsJoinable() const {
+ return m_thread != LLDB_INVALID_HOST_THREAD;
+}
+
+void HostNativeThreadBase::Reset() {
+ m_thread = LLDB_INVALID_HOST_THREAD;
+ m_result = 0;
+}
+
+bool HostNativeThreadBase::EqualsThread(lldb::thread_t thread) const {
+ return m_thread == thread;
+}
+
+lldb::thread_t HostNativeThreadBase::Release() {
+ lldb::thread_t result = m_thread;
+ m_thread = LLDB_INVALID_HOST_THREAD;
+ m_result = 0;
+
+ return result;
+}
+
+lldb::thread_result_t
+HostNativeThreadBase::ThreadCreateTrampoline(lldb::thread_arg_t arg) {
+ ThreadLauncher::HostThreadCreateInfo *info =
+ (ThreadLauncher::HostThreadCreateInfo *)arg;
+ llvm::set_thread_name(info->thread_name);
+
+ thread_func_t thread_fptr = info->thread_fptr;
+ thread_arg_t thread_arg = info->thread_arg;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("thread created");
+
+ delete info;
+ return thread_fptr(thread_arg);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/HostProcess.cpp b/contrib/llvm-project/lldb/source/Host/common/HostProcess.cpp
new file mode 100644
index 000000000000..e180687551f8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/HostProcess.cpp
@@ -0,0 +1,47 @@
+//===-- HostProcess.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/HostNativeProcess.h"
+#include "lldb/Host/HostThread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+HostProcess::HostProcess() : m_native_process(new HostNativeProcess) {}
+
+HostProcess::HostProcess(lldb::process_t process)
+ : m_native_process(new HostNativeProcess(process)) {}
+
+HostProcess::~HostProcess() {}
+
+Status HostProcess::Terminate() { return m_native_process->Terminate(); }
+
+Status HostProcess::GetMainModule(FileSpec &file_spec) const {
+ return m_native_process->GetMainModule(file_spec);
+}
+
+lldb::pid_t HostProcess::GetProcessId() const {
+ return m_native_process->GetProcessId();
+}
+
+bool HostProcess::IsRunning() const { return m_native_process->IsRunning(); }
+
+llvm::Expected<HostThread>
+HostProcess::StartMonitoring(const Host::MonitorChildProcessCallback &callback,
+ bool monitor_signals) {
+ return m_native_process->StartMonitoring(callback, monitor_signals);
+}
+
+HostNativeProcessBase &HostProcess::GetNativeProcess() {
+ return *m_native_process;
+}
+
+const HostNativeProcessBase &HostProcess::GetNativeProcess() const {
+ return *m_native_process;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/HostThread.cpp b/contrib/llvm-project/lldb/source/Host/common/HostThread.cpp
new file mode 100644
index 000000000000..89cadce5b206
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/HostThread.cpp
@@ -0,0 +1,46 @@
+//===-- HostThread.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/HostNativeThread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+HostThread::HostThread() : m_native_thread(new HostNativeThread) {}
+
+HostThread::HostThread(lldb::thread_t thread)
+ : m_native_thread(new HostNativeThread(thread)) {}
+
+Status HostThread::Join(lldb::thread_result_t *result) {
+ return m_native_thread->Join(result);
+}
+
+Status HostThread::Cancel() { return m_native_thread->Cancel(); }
+
+void HostThread::Reset() { return m_native_thread->Reset(); }
+
+lldb::thread_t HostThread::Release() { return m_native_thread->Release(); }
+
+bool HostThread::IsJoinable() const { return m_native_thread->IsJoinable(); }
+
+HostNativeThread &HostThread::GetNativeThread() {
+ return static_cast<HostNativeThread &>(*m_native_thread);
+}
+
+const HostNativeThread &HostThread::GetNativeThread() const {
+ return static_cast<const HostNativeThread &>(*m_native_thread);
+}
+
+lldb::thread_result_t HostThread::GetResult() const {
+ return m_native_thread->GetResult();
+}
+
+bool HostThread::EqualsThread(lldb::thread_t thread) const {
+ return m_native_thread->EqualsThread(thread);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/LockFileBase.cpp b/contrib/llvm-project/lldb/source/Host/common/LockFileBase.cpp
new file mode 100644
index 000000000000..744b1eaabb4e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/LockFileBase.cpp
@@ -0,0 +1,81 @@
+//===-- LockFileBase.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/LockFileBase.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+Status AlreadyLocked() { return Status("Already locked"); }
+
+Status NotLocked() { return Status("Not locked"); }
+}
+
+LockFileBase::LockFileBase(int fd)
+ : m_fd(fd), m_locked(false), m_start(0), m_len(0) {}
+
+bool LockFileBase::IsLocked() const { return m_locked; }
+
+Status LockFileBase::WriteLock(const uint64_t start, const uint64_t len) {
+ return DoLock([&](const uint64_t start,
+ const uint64_t len) { return DoWriteLock(start, len); },
+ start, len);
+}
+
+Status LockFileBase::TryWriteLock(const uint64_t start, const uint64_t len) {
+ return DoLock([&](const uint64_t start,
+ const uint64_t len) { return DoTryWriteLock(start, len); },
+ start, len);
+}
+
+Status LockFileBase::ReadLock(const uint64_t start, const uint64_t len) {
+ return DoLock([&](const uint64_t start,
+ const uint64_t len) { return DoReadLock(start, len); },
+ start, len);
+}
+
+Status LockFileBase::TryReadLock(const uint64_t start, const uint64_t len) {
+ return DoLock([&](const uint64_t start,
+ const uint64_t len) { return DoTryReadLock(start, len); },
+ start, len);
+}
+
+Status LockFileBase::Unlock() {
+ if (!IsLocked())
+ return NotLocked();
+
+ const auto error = DoUnlock();
+ if (error.Success()) {
+ m_locked = false;
+ m_start = 0;
+ m_len = 0;
+ }
+ return error;
+}
+
+bool LockFileBase::IsValidFile() const { return m_fd != -1; }
+
+Status LockFileBase::DoLock(const Locker &locker, const uint64_t start,
+ const uint64_t len) {
+ if (!IsValidFile())
+ return Status("File is invalid");
+
+ if (IsLocked())
+ return AlreadyLocked();
+
+ const auto error = locker(start, len);
+ if (error.Success()) {
+ m_locked = true;
+ m_start = start;
+ m_len = len;
+ }
+
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/MainLoop.cpp b/contrib/llvm-project/lldb/source/Host/common/MainLoop.cpp
new file mode 100644
index 000000000000..1ce09a84671c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/MainLoop.cpp
@@ -0,0 +1,407 @@
+//===-- MainLoop.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Config/llvm-config.h"
+
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Utility/Status.h"
+#include <algorithm>
+#include <cassert>
+#include <cerrno>
+#include <csignal>
+#include <time.h>
+#include <vector>
+
+// Multiplexing is implemented using kqueue on systems that support it (BSD
+// variants including OSX). On linux we use ppoll, while android uses pselect
+// (ppoll is present but not implemented properly). On windows we use WSApoll
+// (which does not support signals).
+
+#if HAVE_SYS_EVENT_H
+#include <sys/event.h>
+#elif defined(_WIN32)
+#include <winsock2.h>
+#elif defined(__ANDROID__)
+#include <sys/syscall.h>
+#else
+#include <poll.h>
+#endif
+
+#ifdef _WIN32
+#define POLL WSAPoll
+#else
+#define POLL poll
+#endif
+
+#if SIGNAL_POLLING_UNSUPPORTED
+#ifdef _WIN32
+typedef int sigset_t;
+typedef int siginfo_t;
+#endif
+
+int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout_ts,
+ const sigset_t *) {
+ int timeout =
+ (timeout_ts == nullptr)
+ ? -1
+ : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000);
+ return POLL(fds, nfds, timeout);
+}
+
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+static sig_atomic_t g_signal_flags[NSIG];
+
+#ifndef SIGNAL_POLLING_UNSUPPORTED
+static void SignalHandler(int signo, siginfo_t *info, void *) {
+ assert(signo < NSIG);
+ g_signal_flags[signo] = 1;
+}
+#endif
+
+class MainLoop::RunImpl {
+public:
+ RunImpl(MainLoop &loop);
+ ~RunImpl() = default;
+
+ Status Poll();
+ void ProcessEvents();
+
+private:
+ MainLoop &loop;
+
+#if HAVE_SYS_EVENT_H
+ std::vector<struct kevent> in_events;
+ struct kevent out_events[4];
+ int num_events = -1;
+
+#else
+#ifdef __ANDROID__
+ fd_set read_fd_set;
+#else
+ std::vector<struct pollfd> read_fds;
+#endif
+
+ sigset_t get_sigmask();
+#endif
+};
+
+#if HAVE_SYS_EVENT_H
+MainLoop::RunImpl::RunImpl(MainLoop &loop) : loop(loop) {
+ in_events.reserve(loop.m_read_fds.size());
+}
+
+Status MainLoop::RunImpl::Poll() {
+ in_events.resize(loop.m_read_fds.size());
+ unsigned i = 0;
+ for (auto &fd : loop.m_read_fds)
+ EV_SET(&in_events[i++], fd.first, EVFILT_READ, EV_ADD, 0, 0, 0);
+
+ num_events = kevent(loop.m_kqueue, in_events.data(), in_events.size(),
+ out_events, llvm::array_lengthof(out_events), nullptr);
+
+ if (num_events < 0) {
+ if (errno == EINTR) {
+ // in case of EINTR, let the main loop run one iteration
+ // we need to zero num_events to avoid assertions failing
+ num_events = 0;
+ } else
+ return Status(errno, eErrorTypePOSIX);
+ }
+ return Status();
+}
+
+void MainLoop::RunImpl::ProcessEvents() {
+ assert(num_events >= 0);
+ for (int i = 0; i < num_events; ++i) {
+ if (loop.m_terminate_request)
+ return;
+ switch (out_events[i].filter) {
+ case EVFILT_READ:
+ loop.ProcessReadObject(out_events[i].ident);
+ break;
+ case EVFILT_SIGNAL:
+ loop.ProcessSignal(out_events[i].ident);
+ break;
+ default:
+ llvm_unreachable("Unknown event");
+ }
+ }
+}
+#else
+MainLoop::RunImpl::RunImpl(MainLoop &loop) : loop(loop) {
+#ifndef __ANDROID__
+ read_fds.reserve(loop.m_read_fds.size());
+#endif
+}
+
+sigset_t MainLoop::RunImpl::get_sigmask() {
+ sigset_t sigmask;
+#if defined(_WIN32)
+ sigmask = 0;
+#elif SIGNAL_POLLING_UNSUPPORTED
+ sigemptyset(&sigmask);
+#else
+ int ret = pthread_sigmask(SIG_SETMASK, nullptr, &sigmask);
+ assert(ret == 0);
+ (void) ret;
+
+ for (const auto &sig : loop.m_signals)
+ sigdelset(&sigmask, sig.first);
+#endif
+ return sigmask;
+}
+
+#ifdef __ANDROID__
+Status MainLoop::RunImpl::Poll() {
+ // ppoll(2) is not supported on older all android versions. Also, older
+ // versions android (API <= 19) implemented pselect in a non-atomic way, as a
+ // combination of pthread_sigmask and select. This is not sufficient for us,
+ // as we rely on the atomicity to correctly implement signal polling, so we
+ // call the underlying syscall ourselves.
+
+ FD_ZERO(&read_fd_set);
+ int nfds = 0;
+ for (const auto &fd : loop.m_read_fds) {
+ FD_SET(fd.first, &read_fd_set);
+ nfds = std::max(nfds, fd.first + 1);
+ }
+
+ union {
+ sigset_t set;
+ uint64_t pad;
+ } kernel_sigset;
+ memset(&kernel_sigset, 0, sizeof(kernel_sigset));
+ kernel_sigset.set = get_sigmask();
+
+ struct {
+ void *sigset_ptr;
+ size_t sigset_len;
+ } extra_data = {&kernel_sigset, sizeof(kernel_sigset)};
+ if (syscall(__NR_pselect6, nfds, &read_fd_set, nullptr, nullptr, nullptr,
+ &extra_data) == -1 &&
+ errno != EINTR)
+ return Status(errno, eErrorTypePOSIX);
+
+ return Status();
+}
+#else
+Status MainLoop::RunImpl::Poll() {
+ read_fds.clear();
+
+ sigset_t sigmask = get_sigmask();
+
+ for (const auto &fd : loop.m_read_fds) {
+ struct pollfd pfd;
+ pfd.fd = fd.first;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ read_fds.push_back(pfd);
+ }
+
+ if (ppoll(read_fds.data(), read_fds.size(), nullptr, &sigmask) == -1 &&
+ errno != EINTR)
+ return Status(errno, eErrorTypePOSIX);
+
+ return Status();
+}
+#endif
+
+void MainLoop::RunImpl::ProcessEvents() {
+#ifdef __ANDROID__
+ // Collect first all readable file descriptors into a separate vector and
+ // then iterate over it to invoke callbacks. Iterating directly over
+ // loop.m_read_fds is not possible because the callbacks can modify the
+ // container which could invalidate the iterator.
+ std::vector<IOObject::WaitableHandle> fds;
+ for (const auto &fd : loop.m_read_fds)
+ if (FD_ISSET(fd.first, &read_fd_set))
+ fds.push_back(fd.first);
+
+ for (const auto &handle : fds) {
+#else
+ for (const auto &fd : read_fds) {
+ if ((fd.revents & (POLLIN | POLLHUP)) == 0)
+ continue;
+ IOObject::WaitableHandle handle = fd.fd;
+#endif
+ if (loop.m_terminate_request)
+ return;
+
+ loop.ProcessReadObject(handle);
+ }
+
+ std::vector<int> signals;
+ for (const auto &entry : loop.m_signals)
+ if (g_signal_flags[entry.first] != 0)
+ signals.push_back(entry.first);
+
+ for (const auto &signal : signals) {
+ if (loop.m_terminate_request)
+ return;
+ g_signal_flags[signal] = 0;
+ loop.ProcessSignal(signal);
+ }
+}
+#endif
+
+MainLoop::MainLoop() {
+#if HAVE_SYS_EVENT_H
+ m_kqueue = kqueue();
+ assert(m_kqueue >= 0);
+#endif
+}
+MainLoop::~MainLoop() {
+#if HAVE_SYS_EVENT_H
+ close(m_kqueue);
+#endif
+ assert(m_read_fds.size() == 0);
+ assert(m_signals.size() == 0);
+}
+
+MainLoop::ReadHandleUP MainLoop::RegisterReadObject(const IOObjectSP &object_sp,
+ const Callback &callback,
+ Status &error) {
+#ifdef _WIN32
+ if (object_sp->GetFdType() != IOObject:: eFDTypeSocket) {
+ error.SetErrorString("MainLoop: non-socket types unsupported on Windows");
+ return nullptr;
+ }
+#endif
+ if (!object_sp || !object_sp->IsValid()) {
+ error.SetErrorString("IO object is not valid.");
+ return nullptr;
+ }
+
+ const bool inserted =
+ m_read_fds.insert({object_sp->GetWaitableHandle(), callback}).second;
+ if (!inserted) {
+ error.SetErrorStringWithFormat("File descriptor %d already monitored.",
+ object_sp->GetWaitableHandle());
+ return nullptr;
+ }
+
+ return CreateReadHandle(object_sp);
+}
+
+// We shall block the signal, then install the signal handler. The signal will
+// be unblocked in the Run() function to check for signal delivery.
+MainLoop::SignalHandleUP
+MainLoop::RegisterSignal(int signo, const Callback &callback, Status &error) {
+#ifdef SIGNAL_POLLING_UNSUPPORTED
+ error.SetErrorString("Signal polling is not supported on this platform.");
+ return nullptr;
+#else
+ if (m_signals.find(signo) != m_signals.end()) {
+ error.SetErrorStringWithFormat("Signal %d already monitored.", signo);
+ return nullptr;
+ }
+
+ SignalInfo info;
+ info.callback = callback;
+ struct sigaction new_action;
+ new_action.sa_sigaction = &SignalHandler;
+ new_action.sa_flags = SA_SIGINFO;
+ sigemptyset(&new_action.sa_mask);
+ sigaddset(&new_action.sa_mask, signo);
+ sigset_t old_set;
+
+ g_signal_flags[signo] = 0;
+
+ // Even if using kqueue, the signal handler will still be invoked, so it's
+ // important to replace it with our "benign" handler.
+ int ret = sigaction(signo, &new_action, &info.old_action);
+ assert(ret == 0 && "sigaction failed");
+
+#if HAVE_SYS_EVENT_H
+ struct kevent ev;
+ EV_SET(&ev, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, 0);
+ ret = kevent(m_kqueue, &ev, 1, nullptr, 0, nullptr);
+ assert(ret == 0);
+#endif
+
+ // If we're using kqueue, the signal needs to be unblocked in order to
+ // receive it. If using pselect/ppoll, we need to block it, and later unblock
+ // it as a part of the system call.
+ ret = pthread_sigmask(HAVE_SYS_EVENT_H ? SIG_UNBLOCK : SIG_BLOCK,
+ &new_action.sa_mask, &old_set);
+ assert(ret == 0 && "pthread_sigmask failed");
+ info.was_blocked = sigismember(&old_set, signo);
+ m_signals.insert({signo, info});
+
+ return SignalHandleUP(new SignalHandle(*this, signo));
+#endif
+}
+
+void MainLoop::UnregisterReadObject(IOObject::WaitableHandle handle) {
+ bool erased = m_read_fds.erase(handle);
+ UNUSED_IF_ASSERT_DISABLED(erased);
+ assert(erased);
+}
+
+void MainLoop::UnregisterSignal(int signo) {
+#if SIGNAL_POLLING_UNSUPPORTED
+ Status("Signal polling is not supported on this platform.");
+#else
+ auto it = m_signals.find(signo);
+ assert(it != m_signals.end());
+
+ sigaction(signo, &it->second.old_action, nullptr);
+
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, signo);
+ int ret = pthread_sigmask(it->second.was_blocked ? SIG_BLOCK : SIG_UNBLOCK,
+ &set, nullptr);
+ assert(ret == 0);
+ (void)ret;
+
+#if HAVE_SYS_EVENT_H
+ struct kevent ev;
+ EV_SET(&ev, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, 0);
+ ret = kevent(m_kqueue, &ev, 1, nullptr, 0, nullptr);
+ assert(ret == 0);
+#endif
+
+ m_signals.erase(it);
+#endif
+}
+
+Status MainLoop::Run() {
+ m_terminate_request = false;
+
+ Status error;
+ RunImpl impl(*this);
+
+ // run until termination or until we run out of things to listen to
+ while (!m_terminate_request && (!m_read_fds.empty() || !m_signals.empty())) {
+
+ error = impl.Poll();
+ if (error.Fail())
+ return error;
+
+ impl.ProcessEvents();
+ }
+ return Status();
+}
+
+void MainLoop::ProcessSignal(int signo) {
+ auto it = m_signals.find(signo);
+ if (it != m_signals.end())
+ it->second.callback(*this); // Do the work
+}
+
+void MainLoop::ProcessReadObject(IOObject::WaitableHandle handle) {
+ auto it = m_read_fds.find(handle);
+ if (it != m_read_fds.end())
+ it->second(*this); // Do the work
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/MonitoringProcessLauncher.cpp b/contrib/llvm-project/lldb/source/Host/common/MonitoringProcessLauncher.cpp
new file mode 100644
index 000000000000..55e9f69a089a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/MonitoringProcessLauncher.cpp
@@ -0,0 +1,70 @@
+//===-- MonitoringProcessLauncher.cpp ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/MonitoringProcessLauncher.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/Support/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+MonitoringProcessLauncher::MonitoringProcessLauncher(
+ std::unique_ptr<ProcessLauncher> delegate_launcher)
+ : m_delegate_launcher(std::move(delegate_launcher)) {}
+
+HostProcess
+MonitoringProcessLauncher::LaunchProcess(const ProcessLaunchInfo &launch_info,
+ Status &error) {
+ ProcessLaunchInfo resolved_info(launch_info);
+
+ error.Clear();
+
+ FileSystem &fs = FileSystem::Instance();
+ FileSpec exe_spec(resolved_info.GetExecutableFile());
+
+ if (!fs.Exists(exe_spec))
+ FileSystem::Instance().Resolve(exe_spec);
+
+ if (!fs.Exists(exe_spec))
+ FileSystem::Instance().ResolveExecutableLocation(exe_spec);
+
+ if (!fs.Exists(exe_spec)) {
+ error.SetErrorStringWithFormatv("executable doesn't exist: '{0}'",
+ exe_spec);
+ return HostProcess();
+ }
+
+ resolved_info.SetExecutableFile(exe_spec, false);
+ assert(!resolved_info.GetFlags().Test(eLaunchFlagLaunchInTTY));
+
+ HostProcess process =
+ m_delegate_launcher->LaunchProcess(resolved_info, error);
+
+ if (process.GetProcessId() != LLDB_INVALID_PROCESS_ID) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ assert(launch_info.GetMonitorProcessCallback());
+ llvm::Expected<HostThread> maybe_thread =
+ process.StartMonitoring(launch_info.GetMonitorProcessCallback(),
+ launch_info.GetMonitorSignals());
+ if (!maybe_thread)
+ error.SetErrorStringWithFormatv("failed to launch host thread: {}",
+ llvm::toString(maybe_thread.takeError()));
+ if (log)
+ log->PutCString("started monitoring child process.");
+ } else {
+ // Invalid process ID, something didn't go well
+ if (error.Success())
+ error.SetErrorString("process launch failed for unknown reasons");
+ }
+ return process;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/NativeProcessProtocol.cpp b/contrib/llvm-project/lldb/source/Host/common/NativeProcessProtocol.cpp
new file mode 100644
index 000000000000..90272cb8d0bc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/NativeProcessProtocol.cpp
@@ -0,0 +1,700 @@
+//===-- NativeProcessProtocol.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/common/NativeBreakpointList.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/lldb-enumerations.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// NativeProcessProtocol Members
+
+NativeProcessProtocol::NativeProcessProtocol(lldb::pid_t pid, int terminal_fd,
+ NativeDelegate &delegate)
+ : m_pid(pid), m_terminal_fd(terminal_fd) {
+ bool registered = RegisterNativeDelegate(delegate);
+ assert(registered);
+ (void)registered;
+}
+
+lldb_private::Status NativeProcessProtocol::Interrupt() {
+ Status error;
+#if !defined(SIGSTOP)
+ error.SetErrorString("local host does not support signaling");
+ return error;
+#else
+ return Signal(SIGSTOP);
+#endif
+}
+
+Status NativeProcessProtocol::IgnoreSignals(llvm::ArrayRef<int> signals) {
+ m_signals_to_ignore.clear();
+ m_signals_to_ignore.insert(signals.begin(), signals.end());
+ return Status();
+}
+
+lldb_private::Status
+NativeProcessProtocol::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) {
+ // Default: not implemented.
+ return Status("not implemented");
+}
+
+llvm::Optional<WaitStatus> NativeProcessProtocol::GetExitStatus() {
+ if (m_state == lldb::eStateExited)
+ return m_exit_status;
+
+ return llvm::None;
+}
+
+bool NativeProcessProtocol::SetExitStatus(WaitStatus status,
+ bool bNotifyStateChange) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ LLDB_LOG(log, "status = {0}, notify = {1}", status, bNotifyStateChange);
+
+ // Exit status already set
+ if (m_state == lldb::eStateExited) {
+ if (m_exit_status)
+ LLDB_LOG(log, "exit status already set to {0}", *m_exit_status);
+ else
+ LLDB_LOG(log, "state is exited, but status not set");
+ return false;
+ }
+
+ m_state = lldb::eStateExited;
+ m_exit_status = status;
+
+ if (bNotifyStateChange)
+ SynchronouslyNotifyProcessStateChanged(lldb::eStateExited);
+
+ return true;
+}
+
+NativeThreadProtocol *NativeProcessProtocol::GetThreadAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ if (idx < m_threads.size())
+ return m_threads[idx].get();
+ return nullptr;
+}
+
+NativeThreadProtocol *
+NativeProcessProtocol::GetThreadByIDUnlocked(lldb::tid_t tid) {
+ for (const auto &thread : m_threads) {
+ if (thread->GetID() == tid)
+ return thread.get();
+ }
+ return nullptr;
+}
+
+NativeThreadProtocol *NativeProcessProtocol::GetThreadByID(lldb::tid_t tid) {
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ return GetThreadByIDUnlocked(tid);
+}
+
+bool NativeProcessProtocol::IsAlive() const {
+ return m_state != eStateDetached && m_state != eStateExited &&
+ m_state != eStateInvalid && m_state != eStateUnloaded;
+}
+
+const NativeWatchpointList::WatchpointMap &
+NativeProcessProtocol::GetWatchpointMap() const {
+ return m_watchpoint_list.GetWatchpointMap();
+}
+
+llvm::Optional<std::pair<uint32_t, uint32_t>>
+NativeProcessProtocol::GetHardwareDebugSupportInfo() const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // get any thread
+ NativeThreadProtocol *thread(
+ const_cast<NativeProcessProtocol *>(this)->GetThreadAtIndex(0));
+ if (!thread) {
+ LLDB_LOG(log, "failed to find a thread to grab a NativeRegisterContext!");
+ return llvm::None;
+ }
+
+ NativeRegisterContext &reg_ctx = thread->GetRegisterContext();
+ return std::make_pair(reg_ctx.NumSupportedHardwareBreakpoints(),
+ reg_ctx.NumSupportedHardwareWatchpoints());
+}
+
+Status NativeProcessProtocol::SetWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags,
+ bool hardware) {
+ // This default implementation assumes setting the watchpoint for the process
+ // will require setting the watchpoint for each of the threads. Furthermore,
+ // it will track watchpoints set for the process and will add them to each
+ // thread that is attached to via the (FIXME implement) OnThreadAttached ()
+ // method.
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Update the thread list
+ UpdateThreads();
+
+ // Keep track of the threads we successfully set the watchpoint for. If one
+ // of the thread watchpoint setting operations fails, back off and remove the
+ // watchpoint for all the threads that were successfully set so we get back
+ // to a consistent state.
+ std::vector<NativeThreadProtocol *> watchpoint_established_threads;
+
+ // Tell each thread to set a watchpoint. In the event that hardware
+ // watchpoints are requested but the SetWatchpoint fails, try to set a
+ // software watchpoint as a fallback. It's conceivable that if there are
+ // more threads than hardware watchpoints available, some of the threads will
+ // fail to set hardware watchpoints while software ones may be available.
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ for (const auto &thread : m_threads) {
+ assert(thread && "thread list should not have a NULL thread!");
+
+ Status thread_error =
+ thread->SetWatchpoint(addr, size, watch_flags, hardware);
+ if (thread_error.Fail() && hardware) {
+ // Try software watchpoints since we failed on hardware watchpoint
+ // setting and we may have just run out of hardware watchpoints.
+ thread_error = thread->SetWatchpoint(addr, size, watch_flags, false);
+ if (thread_error.Success())
+ LLDB_LOG(log,
+ "hardware watchpoint requested but software watchpoint set");
+ }
+
+ if (thread_error.Success()) {
+ // Remember that we set this watchpoint successfully in case we need to
+ // clear it later.
+ watchpoint_established_threads.push_back(thread.get());
+ } else {
+ // Unset the watchpoint for each thread we successfully set so that we
+ // get back to a consistent state of "not set" for the watchpoint.
+ for (auto unwatch_thread_sp : watchpoint_established_threads) {
+ Status remove_error = unwatch_thread_sp->RemoveWatchpoint(addr);
+ if (remove_error.Fail())
+ LLDB_LOG(log, "RemoveWatchpoint failed for pid={0}, tid={1}: {2}",
+ GetID(), unwatch_thread_sp->GetID(), remove_error);
+ }
+
+ return thread_error;
+ }
+ }
+ return m_watchpoint_list.Add(addr, size, watch_flags, hardware);
+}
+
+Status NativeProcessProtocol::RemoveWatchpoint(lldb::addr_t addr) {
+ // Update the thread list
+ UpdateThreads();
+
+ Status overall_error;
+
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ for (const auto &thread : m_threads) {
+ assert(thread && "thread list should not have a NULL thread!");
+
+ const Status thread_error = thread->RemoveWatchpoint(addr);
+ if (thread_error.Fail()) {
+ // Keep track of the first thread error if any threads fail. We want to
+ // try to remove the watchpoint from every thread, though, even if one or
+ // more have errors.
+ if (!overall_error.Fail())
+ overall_error = thread_error;
+ }
+ }
+ const Status error = m_watchpoint_list.Remove(addr);
+ return overall_error.Fail() ? overall_error : error;
+}
+
+const HardwareBreakpointMap &
+NativeProcessProtocol::GetHardwareBreakpointMap() const {
+ return m_hw_breakpoints_map;
+}
+
+Status NativeProcessProtocol::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ // This default implementation assumes setting a hardware breakpoint for this
+ // process will require setting same hardware breakpoint for each of its
+ // existing threads. New thread will do the same once created.
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Update the thread list
+ UpdateThreads();
+
+ // Exit here if target does not have required hardware breakpoint capability.
+ auto hw_debug_cap = GetHardwareDebugSupportInfo();
+
+ if (hw_debug_cap == llvm::None || hw_debug_cap->first == 0 ||
+ hw_debug_cap->first <= m_hw_breakpoints_map.size())
+ return Status("Target does not have required no of hardware breakpoints");
+
+ // Vector below stores all thread pointer for which we have we successfully
+ // set this hardware breakpoint. If any of the current process threads fails
+ // to set this hardware breakpoint then roll back and remove this breakpoint
+ // for all the threads that had already set it successfully.
+ std::vector<NativeThreadProtocol *> breakpoint_established_threads;
+
+ // Request to set a hardware breakpoint for each of current process threads.
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ for (const auto &thread : m_threads) {
+ assert(thread && "thread list should not have a NULL thread!");
+
+ Status thread_error = thread->SetHardwareBreakpoint(addr, size);
+ if (thread_error.Success()) {
+ // Remember that we set this breakpoint successfully in case we need to
+ // clear it later.
+ breakpoint_established_threads.push_back(thread.get());
+ } else {
+ // Unset the breakpoint for each thread we successfully set so that we
+ // get back to a consistent state of "not set" for this hardware
+ // breakpoint.
+ for (auto rollback_thread_sp : breakpoint_established_threads) {
+ Status remove_error =
+ rollback_thread_sp->RemoveHardwareBreakpoint(addr);
+ if (remove_error.Fail())
+ LLDB_LOG(log,
+ "RemoveHardwareBreakpoint failed for pid={0}, tid={1}: {2}",
+ GetID(), rollback_thread_sp->GetID(), remove_error);
+ }
+
+ return thread_error;
+ }
+ }
+
+ // Register new hardware breakpoint into hardware breakpoints map of current
+ // process.
+ m_hw_breakpoints_map[addr] = {addr, size};
+
+ return Status();
+}
+
+Status NativeProcessProtocol::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+ // Update the thread list
+ UpdateThreads();
+
+ Status error;
+
+ std::lock_guard<std::recursive_mutex> guard(m_threads_mutex);
+ for (const auto &thread : m_threads) {
+ assert(thread && "thread list should not have a NULL thread!");
+ error = thread->RemoveHardwareBreakpoint(addr);
+ }
+
+ // Also remove from hardware breakpoint map of current process.
+ m_hw_breakpoints_map.erase(addr);
+
+ return error;
+}
+
+bool NativeProcessProtocol::RegisterNativeDelegate(
+ NativeDelegate &native_delegate) {
+ std::lock_guard<std::recursive_mutex> guard(m_delegates_mutex);
+ if (std::find(m_delegates.begin(), m_delegates.end(), &native_delegate) !=
+ m_delegates.end())
+ return false;
+
+ m_delegates.push_back(&native_delegate);
+ native_delegate.InitializeDelegate(this);
+ return true;
+}
+
+bool NativeProcessProtocol::UnregisterNativeDelegate(
+ NativeDelegate &native_delegate) {
+ std::lock_guard<std::recursive_mutex> guard(m_delegates_mutex);
+
+ const auto initial_size = m_delegates.size();
+ m_delegates.erase(
+ remove(m_delegates.begin(), m_delegates.end(), &native_delegate),
+ m_delegates.end());
+
+ // We removed the delegate if the count of delegates shrank after removing
+ // all copies of the given native_delegate from the vector.
+ return m_delegates.size() < initial_size;
+}
+
+void NativeProcessProtocol::SynchronouslyNotifyProcessStateChanged(
+ lldb::StateType state) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ std::lock_guard<std::recursive_mutex> guard(m_delegates_mutex);
+ for (auto native_delegate : m_delegates)
+ native_delegate->ProcessStateChanged(this, state);
+
+ if (log) {
+ if (!m_delegates.empty()) {
+ log->Printf("NativeProcessProtocol::%s: sent state notification [%s] "
+ "from process %" PRIu64,
+ __FUNCTION__, lldb_private::StateAsCString(state), GetID());
+ } else {
+ log->Printf("NativeProcessProtocol::%s: would send state notification "
+ "[%s] from process %" PRIu64 ", but no delegates",
+ __FUNCTION__, lldb_private::StateAsCString(state), GetID());
+ }
+ }
+}
+
+void NativeProcessProtocol::NotifyDidExec() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessProtocol::%s - preparing to call delegates",
+ __FUNCTION__);
+
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_delegates_mutex);
+ for (auto native_delegate : m_delegates)
+ native_delegate->DidExec(this);
+ }
+}
+
+Status NativeProcessProtocol::SetSoftwareBreakpoint(lldb::addr_t addr,
+ uint32_t size_hint) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ LLDB_LOG(log, "addr = {0:x}, size_hint = {1}", addr, size_hint);
+
+ auto it = m_software_breakpoints.find(addr);
+ if (it != m_software_breakpoints.end()) {
+ ++it->second.ref_count;
+ return Status();
+ }
+ auto expected_bkpt = EnableSoftwareBreakpoint(addr, size_hint);
+ if (!expected_bkpt)
+ return Status(expected_bkpt.takeError());
+
+ m_software_breakpoints.emplace(addr, std::move(*expected_bkpt));
+ return Status();
+}
+
+Status NativeProcessProtocol::RemoveSoftwareBreakpoint(lldb::addr_t addr) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ LLDB_LOG(log, "addr = {0:x}", addr);
+ auto it = m_software_breakpoints.find(addr);
+ if (it == m_software_breakpoints.end())
+ return Status("Breakpoint not found.");
+ assert(it->second.ref_count > 0);
+ if (--it->second.ref_count > 0)
+ return Status();
+
+ // This is the last reference. Let's remove the breakpoint.
+ Status error;
+
+ // Clear a software breakpoint instruction
+ llvm::SmallVector<uint8_t, 4> curr_break_op(
+ it->second.breakpoint_opcodes.size(), 0);
+
+ // Read the breakpoint opcode
+ size_t bytes_read = 0;
+ error =
+ ReadMemory(addr, curr_break_op.data(), curr_break_op.size(), bytes_read);
+ if (error.Fail() || bytes_read < curr_break_op.size()) {
+ return Status("addr=0x%" PRIx64
+ ": tried to read %zu bytes but only read %zu",
+ addr, curr_break_op.size(), bytes_read);
+ }
+ const auto &saved = it->second.saved_opcodes;
+ // Make sure the breakpoint opcode exists at this address
+ if (makeArrayRef(curr_break_op) != it->second.breakpoint_opcodes) {
+ if (curr_break_op != it->second.saved_opcodes)
+ return Status("Original breakpoint trap is no longer in memory.");
+ LLDB_LOG(log,
+ "Saved opcodes ({0:@[x]}) have already been restored at {1:x}.",
+ llvm::make_range(saved.begin(), saved.end()), addr);
+ } else {
+ // We found a valid breakpoint opcode at this address, now restore the
+ // saved opcode.
+ size_t bytes_written = 0;
+ error = WriteMemory(addr, saved.data(), saved.size(), bytes_written);
+ if (error.Fail() || bytes_written < saved.size()) {
+ return Status("addr=0x%" PRIx64
+ ": tried to write %zu bytes but only wrote %zu",
+ addr, saved.size(), bytes_written);
+ }
+
+ // Verify that our original opcode made it back to the inferior
+ llvm::SmallVector<uint8_t, 4> verify_opcode(saved.size(), 0);
+ size_t verify_bytes_read = 0;
+ error = ReadMemory(addr, verify_opcode.data(), verify_opcode.size(),
+ verify_bytes_read);
+ if (error.Fail() || verify_bytes_read < verify_opcode.size()) {
+ return Status("addr=0x%" PRIx64
+ ": tried to read %zu verification bytes but only read %zu",
+ addr, verify_opcode.size(), verify_bytes_read);
+ }
+ if (verify_opcode != saved)
+ LLDB_LOG(log, "Restoring bytes at {0:x}: {1:@[x]}", addr,
+ llvm::make_range(saved.begin(), saved.end()));
+ }
+
+ m_software_breakpoints.erase(it);
+ return Status();
+}
+
+llvm::Expected<NativeProcessProtocol::SoftwareBreakpoint>
+NativeProcessProtocol::EnableSoftwareBreakpoint(lldb::addr_t addr,
+ uint32_t size_hint) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+ auto expected_trap = GetSoftwareBreakpointTrapOpcode(size_hint);
+ if (!expected_trap)
+ return expected_trap.takeError();
+
+ llvm::SmallVector<uint8_t, 4> saved_opcode_bytes(expected_trap->size(), 0);
+ // Save the original opcodes by reading them so we can restore later.
+ size_t bytes_read = 0;
+ Status error = ReadMemory(addr, saved_opcode_bytes.data(),
+ saved_opcode_bytes.size(), bytes_read);
+ if (error.Fail())
+ return error.ToError();
+
+ // Ensure we read as many bytes as we expected.
+ if (bytes_read != saved_opcode_bytes.size()) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Failed to read memory while attempting to set breakpoint: attempted "
+ "to read {0} bytes but only read {1}.",
+ saved_opcode_bytes.size(), bytes_read);
+ }
+
+ LLDB_LOG(
+ log, "Overwriting bytes at {0:x}: {1:@[x]}", addr,
+ llvm::make_range(saved_opcode_bytes.begin(), saved_opcode_bytes.end()));
+
+ // Write a software breakpoint in place of the original opcode.
+ size_t bytes_written = 0;
+ error = WriteMemory(addr, expected_trap->data(), expected_trap->size(),
+ bytes_written);
+ if (error.Fail())
+ return error.ToError();
+
+ // Ensure we wrote as many bytes as we expected.
+ if (bytes_written != expected_trap->size()) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Failed write memory while attempting to set "
+ "breakpoint: attempted to write {0} bytes but only wrote {1}",
+ expected_trap->size(), bytes_written);
+ }
+
+ llvm::SmallVector<uint8_t, 4> verify_bp_opcode_bytes(expected_trap->size(),
+ 0);
+ size_t verify_bytes_read = 0;
+ error = ReadMemory(addr, verify_bp_opcode_bytes.data(),
+ verify_bp_opcode_bytes.size(), verify_bytes_read);
+ if (error.Fail())
+ return error.ToError();
+
+ // Ensure we read as many verification bytes as we expected.
+ if (verify_bytes_read != verify_bp_opcode_bytes.size()) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Failed to read memory while "
+ "attempting to verify breakpoint: attempted to read {0} bytes "
+ "but only read {1}",
+ verify_bp_opcode_bytes.size(), verify_bytes_read);
+ }
+
+ if (llvm::makeArrayRef(verify_bp_opcode_bytes.data(), verify_bytes_read) !=
+ *expected_trap) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Verification of software breakpoint "
+ "writing failed - trap opcodes not successfully read back "
+ "after writing when setting breakpoint at {0:x}",
+ addr);
+ }
+
+ LLDB_LOG(log, "addr = {0:x}: SUCCESS", addr);
+ return SoftwareBreakpoint{1, saved_opcode_bytes, *expected_trap};
+}
+
+llvm::Expected<llvm::ArrayRef<uint8_t>>
+NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode(size_t size_hint) {
+ static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xd4};
+ static const uint8_t g_i386_opcode[] = {0xCC};
+ static const uint8_t g_mips64_opcode[] = {0x00, 0x00, 0x00, 0x0d};
+ static const uint8_t g_mips64el_opcode[] = {0x0d, 0x00, 0x00, 0x00};
+ static const uint8_t g_s390x_opcode[] = {0x00, 0x01};
+ static const uint8_t g_ppc64le_opcode[] = {0x08, 0x00, 0xe0, 0x7f}; // trap
+
+ switch (GetArchitecture().GetMachine()) {
+ case llvm::Triple::aarch64:
+ return llvm::makeArrayRef(g_aarch64_opcode);
+
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ return llvm::makeArrayRef(g_i386_opcode);
+
+ case llvm::Triple::mips:
+ case llvm::Triple::mips64:
+ return llvm::makeArrayRef(g_mips64_opcode);
+
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64el:
+ return llvm::makeArrayRef(g_mips64el_opcode);
+
+ case llvm::Triple::systemz:
+ return llvm::makeArrayRef(g_s390x_opcode);
+
+ case llvm::Triple::ppc64le:
+ return llvm::makeArrayRef(g_ppc64le_opcode);
+
+ default:
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "CPU type not supported!");
+ }
+}
+
+size_t NativeProcessProtocol::GetSoftwareBreakpointPCOffset() {
+ switch (GetArchitecture().GetMachine()) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ case llvm::Triple::systemz:
+ // These architectures report increment the PC after breakpoint is hit.
+ return cantFail(GetSoftwareBreakpointTrapOpcode(0)).size();
+
+ case llvm::Triple::arm:
+ case llvm::Triple::aarch64:
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ case llvm::Triple::ppc64le:
+ // On these architectures the PC doesn't get updated for breakpoint hits.
+ return 0;
+
+ default:
+ llvm_unreachable("CPU type not supported!");
+ }
+}
+
+void NativeProcessProtocol::FixupBreakpointPCAsNeeded(
+ NativeThreadProtocol &thread) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS);
+
+ Status error;
+
+ // Find out the size of a breakpoint (might depend on where we are in the
+ // code).
+ NativeRegisterContext &context = thread.GetRegisterContext();
+
+ uint32_t breakpoint_size = GetSoftwareBreakpointPCOffset();
+ LLDB_LOG(log, "breakpoint size: {0}", breakpoint_size);
+ if (breakpoint_size == 0)
+ return;
+
+ // First try probing for a breakpoint at a software breakpoint location: PC -
+ // breakpoint size.
+ const lldb::addr_t initial_pc_addr = context.GetPCfromBreakpointLocation();
+ lldb::addr_t breakpoint_addr = initial_pc_addr;
+ // Do not allow breakpoint probe to wrap around.
+ if (breakpoint_addr >= breakpoint_size)
+ breakpoint_addr -= breakpoint_size;
+
+ if (m_software_breakpoints.count(breakpoint_addr) == 0) {
+ // We didn't find one at a software probe location. Nothing to do.
+ LLDB_LOG(log,
+ "pid {0} no lldb software breakpoint found at current pc with "
+ "adjustment: {1}",
+ GetID(), breakpoint_addr);
+ return;
+ }
+
+ //
+ // We have a software breakpoint and need to adjust the PC.
+ //
+
+ // Change the program counter.
+ LLDB_LOG(log, "pid {0} tid {1}: changing PC from {2:x} to {3:x}", GetID(),
+ thread.GetID(), initial_pc_addr, breakpoint_addr);
+
+ error = context.SetPC(breakpoint_addr);
+ if (error.Fail()) {
+ // This can happen in case the process was killed between the time we read
+ // the PC and when we are updating it. There's nothing better to do than to
+ // swallow the error.
+ LLDB_LOG(log, "pid {0} tid {1}: failed to set PC: {2}", GetID(),
+ thread.GetID(), error);
+ }
+}
+
+Status NativeProcessProtocol::RemoveBreakpoint(lldb::addr_t addr,
+ bool hardware) {
+ if (hardware)
+ return RemoveHardwareBreakpoint(addr);
+ else
+ return RemoveSoftwareBreakpoint(addr);
+}
+
+Status NativeProcessProtocol::ReadMemoryWithoutTrap(lldb::addr_t addr,
+ void *buf, size_t size,
+ size_t &bytes_read) {
+ Status error = ReadMemory(addr, buf, size, bytes_read);
+ if (error.Fail())
+ return error;
+
+ auto data =
+ llvm::makeMutableArrayRef(static_cast<uint8_t *>(buf), bytes_read);
+ for (const auto &pair : m_software_breakpoints) {
+ lldb::addr_t bp_addr = pair.first;
+ auto saved_opcodes = makeArrayRef(pair.second.saved_opcodes);
+
+ if (bp_addr + saved_opcodes.size() < addr || addr + bytes_read <= bp_addr)
+ continue; // Breapoint not in range, ignore
+
+ if (bp_addr < addr) {
+ saved_opcodes = saved_opcodes.drop_front(addr - bp_addr);
+ bp_addr = addr;
+ }
+ auto bp_data = data.drop_front(bp_addr - addr);
+ std::copy_n(saved_opcodes.begin(),
+ std::min(saved_opcodes.size(), bp_data.size()),
+ bp_data.begin());
+ }
+ return Status();
+}
+
+lldb::StateType NativeProcessProtocol::GetState() const {
+ std::lock_guard<std::recursive_mutex> guard(m_state_mutex);
+ return m_state;
+}
+
+void NativeProcessProtocol::SetState(lldb::StateType state,
+ bool notify_delegates) {
+ std::lock_guard<std::recursive_mutex> guard(m_state_mutex);
+
+ if (state == m_state)
+ return;
+
+ m_state = state;
+
+ if (StateIsStoppedState(state, false)) {
+ ++m_stop_id;
+
+ // Give process a chance to do any stop id bump processing, such as
+ // clearing cached data that is invalidated each time the process runs.
+ // Note if/when we support some threads running, we'll end up needing to
+ // manage this per thread and per process.
+ DoStopIDBumped(m_stop_id);
+ }
+
+ // Optionally notify delegates of the state change.
+ if (notify_delegates)
+ SynchronouslyNotifyProcessStateChanged(state);
+}
+
+uint32_t NativeProcessProtocol::GetStopID() const {
+ std::lock_guard<std::recursive_mutex> guard(m_state_mutex);
+ return m_stop_id;
+}
+
+void NativeProcessProtocol::DoStopIDBumped(uint32_t /* newBumpId */) {
+ // Default implementation does nothing.
+}
+
+NativeProcessProtocol::Factory::~Factory() = default;
diff --git a/contrib/llvm-project/lldb/source/Host/common/NativeRegisterContext.cpp b/contrib/llvm-project/lldb/source/Host/common/NativeRegisterContext.cpp
new file mode 100644
index 000000000000..2f30d52aea63
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/NativeRegisterContext.cpp
@@ -0,0 +1,424 @@
+//===-- NativeRegisterContext.cpp -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/common/NativeRegisterContext.h"
+
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+NativeRegisterContext::NativeRegisterContext(NativeThreadProtocol &thread)
+ : m_thread(thread) {}
+
+// Destructor
+NativeRegisterContext::~NativeRegisterContext() {}
+
+// FIXME revisit invalidation, process stop ids, etc. Right now we don't
+// support caching in NativeRegisterContext. We can do this later by utilizing
+// NativeProcessProtocol::GetStopID () and adding a stop id to
+// NativeRegisterContext.
+
+// void
+// NativeRegisterContext::InvalidateIfNeeded (bool force) {
+// ProcessSP process_sp (m_thread.GetProcess());
+// bool invalidate = force;
+// uint32_t process_stop_id = UINT32_MAX;
+
+// if (process_sp)
+// process_stop_id = process_sp->GetStopID();
+// else
+// invalidate = true;
+
+// if (!invalidate)
+// invalidate = process_stop_id != GetStopID();
+
+// if (invalidate)
+// {
+// InvalidateAllRegisters ();
+// SetStopID (process_stop_id);
+// }
+// }
+
+const RegisterInfo *
+NativeRegisterContext::GetRegisterInfoByName(llvm::StringRef reg_name,
+ uint32_t start_idx) {
+ if (reg_name.empty())
+ return nullptr;
+
+ const uint32_t num_registers = GetRegisterCount();
+ for (uint32_t reg = start_idx; reg < num_registers; ++reg) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+
+ if (reg_name.equals_lower(reg_info->name) ||
+ reg_name.equals_lower(reg_info->alt_name))
+ return reg_info;
+ }
+ return nullptr;
+}
+
+const RegisterInfo *NativeRegisterContext::GetRegisterInfo(uint32_t kind,
+ uint32_t num) {
+ const uint32_t reg_num = ConvertRegisterKindToRegisterNumber(kind, num);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return nullptr;
+ return GetRegisterInfoAtIndex(reg_num);
+}
+
+const char *NativeRegisterContext::GetRegisterName(uint32_t reg) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info)
+ return reg_info->name;
+ return nullptr;
+}
+
+const char *NativeRegisterContext::GetRegisterSetNameForRegisterAtIndex(
+ uint32_t reg_index) const {
+ const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index);
+ if (!reg_info)
+ return nullptr;
+
+ for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index) {
+ const RegisterSet *const reg_set = GetRegisterSet(set_index);
+ if (!reg_set)
+ continue;
+
+ for (uint32_t reg_num_index = 0; reg_num_index < reg_set->num_registers;
+ ++reg_num_index) {
+ const uint32_t reg_num = reg_set->registers[reg_num_index];
+ // FIXME double check we're checking the right register kind here.
+ if (reg_info->kinds[RegisterKind::eRegisterKindLLDB] == reg_num) {
+ // The given register is a member of this register set. Return the
+ // register set name.
+ return reg_set->name;
+ }
+ }
+ }
+
+ // Didn't find it.
+ return nullptr;
+}
+
+lldb::addr_t NativeRegisterContext::GetPC(lldb::addr_t fail_value) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+ if (log)
+ log->Printf("NativeRegisterContext::%s using reg index %" PRIu32
+ " (default %" PRIu64 ")",
+ __FUNCTION__, reg, fail_value);
+
+ const uint64_t retval = ReadRegisterAsUnsigned(reg, fail_value);
+
+ if (log)
+ log->Printf("NativeRegisterContext::%s " PRIu32 " retval %" PRIu64,
+ __FUNCTION__, retval);
+
+ return retval;
+}
+
+lldb::addr_t
+NativeRegisterContext::GetPCfromBreakpointLocation(lldb::addr_t fail_value) {
+ return GetPC(fail_value);
+}
+
+Status NativeRegisterContext::SetPC(lldb::addr_t pc) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+ return WriteRegisterFromUnsigned(reg, pc);
+}
+
+lldb::addr_t NativeRegisterContext::GetSP(lldb::addr_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+Status NativeRegisterContext::SetSP(lldb::addr_t sp) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP);
+ return WriteRegisterFromUnsigned(reg, sp);
+}
+
+lldb::addr_t NativeRegisterContext::GetFP(lldb::addr_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FP);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+Status NativeRegisterContext::SetFP(lldb::addr_t fp) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FP);
+ return WriteRegisterFromUnsigned(reg, fp);
+}
+
+lldb::addr_t NativeRegisterContext::GetReturnAddress(lldb::addr_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+lldb::addr_t NativeRegisterContext::GetFlags(lldb::addr_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+lldb::addr_t
+NativeRegisterContext::ReadRegisterAsUnsigned(uint32_t reg,
+ lldb::addr_t fail_value) {
+ if (reg != LLDB_INVALID_REGNUM)
+ return ReadRegisterAsUnsigned(GetRegisterInfoAtIndex(reg), fail_value);
+ return fail_value;
+}
+
+uint64_t
+NativeRegisterContext::ReadRegisterAsUnsigned(const RegisterInfo *reg_info,
+ lldb::addr_t fail_value) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ if (reg_info) {
+ RegisterValue value;
+ Status error = ReadRegister(reg_info, value);
+ if (error.Success()) {
+ if (log)
+ log->Printf("NativeRegisterContext::%s ReadRegister() succeeded, value "
+ "%" PRIu64,
+ __FUNCTION__, value.GetAsUInt64());
+ return value.GetAsUInt64();
+ } else {
+ if (log)
+ log->Printf("NativeRegisterContext::%s ReadRegister() failed, error %s",
+ __FUNCTION__, error.AsCString());
+ }
+ } else {
+ if (log)
+ log->Printf("NativeRegisterContext::%s ReadRegister() null reg_info",
+ __FUNCTION__);
+ }
+ return fail_value;
+}
+
+Status NativeRegisterContext::WriteRegisterFromUnsigned(uint32_t reg,
+ uint64_t uval) {
+ if (reg == LLDB_INVALID_REGNUM)
+ return Status("NativeRegisterContext::%s (): reg is invalid", __FUNCTION__);
+ return WriteRegisterFromUnsigned(GetRegisterInfoAtIndex(reg), uval);
+}
+
+Status
+NativeRegisterContext::WriteRegisterFromUnsigned(const RegisterInfo *reg_info,
+ uint64_t uval) {
+ assert(reg_info);
+ if (!reg_info)
+ return Status("reg_info is nullptr");
+
+ RegisterValue value;
+ if (!value.SetUInt(uval, reg_info->byte_size))
+ return Status("RegisterValue::SetUInt () failed");
+
+ return WriteRegister(reg_info, value);
+}
+
+lldb::tid_t NativeRegisterContext::GetThreadID() const {
+ return m_thread.GetID();
+}
+
+uint32_t NativeRegisterContext::NumSupportedHardwareBreakpoints() { return 0; }
+
+uint32_t NativeRegisterContext::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ return LLDB_INVALID_INDEX32;
+}
+
+Status NativeRegisterContext::ClearAllHardwareBreakpoints() {
+ return Status("not implemented");
+}
+
+bool NativeRegisterContext::ClearHardwareBreakpoint(uint32_t hw_idx) {
+ return false;
+}
+
+Status NativeRegisterContext::GetHardwareBreakHitIndex(uint32_t &bp_index,
+ lldb::addr_t trap_addr) {
+ bp_index = LLDB_INVALID_INDEX32;
+ return Status("not implemented");
+}
+
+uint32_t NativeRegisterContext::NumSupportedHardwareWatchpoints() { return 0; }
+
+uint32_t NativeRegisterContext::SetHardwareWatchpoint(lldb::addr_t addr,
+ size_t size,
+ uint32_t watch_flags) {
+ return LLDB_INVALID_INDEX32;
+}
+
+bool NativeRegisterContext::ClearHardwareWatchpoint(uint32_t hw_index) {
+ return false;
+}
+
+Status NativeRegisterContext::ClearAllHardwareWatchpoints() {
+ return Status("not implemented");
+}
+
+Status NativeRegisterContext::IsWatchpointHit(uint32_t wp_index, bool &is_hit) {
+ is_hit = false;
+ return Status("not implemented");
+}
+
+Status NativeRegisterContext::GetWatchpointHitIndex(uint32_t &wp_index,
+ lldb::addr_t trap_addr) {
+ wp_index = LLDB_INVALID_INDEX32;
+ return Status("not implemented");
+}
+
+Status NativeRegisterContext::IsWatchpointVacant(uint32_t wp_index,
+ bool &is_vacant) {
+ is_vacant = false;
+ return Status("not implemented");
+}
+
+lldb::addr_t NativeRegisterContext::GetWatchpointAddress(uint32_t wp_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+lldb::addr_t NativeRegisterContext::GetWatchpointHitAddress(uint32_t wp_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool NativeRegisterContext::HardwareSingleStep(bool enable) { return false; }
+
+Status NativeRegisterContext::ReadRegisterValueFromMemory(
+ const RegisterInfo *reg_info, lldb::addr_t src_addr, size_t src_len,
+ RegisterValue &reg_value) {
+ Status error;
+ if (reg_info == nullptr) {
+ error.SetErrorString("invalid register info argument.");
+ return error;
+ }
+
+ // Moving from addr into a register
+ //
+ // Case 1: src_len == dst_len
+ //
+ // |AABBCCDD| Address contents
+ // |AABBCCDD| Register contents
+ //
+ // Case 2: src_len > dst_len
+ //
+ // Status! (The register should always be big enough to hold the data)
+ //
+ // Case 3: src_len < dst_len
+ //
+ // |AABB| Address contents
+ // |AABB0000| Register contents [on little-endian hardware]
+ // |0000AABB| Register contents [on big-endian hardware]
+ if (src_len > RegisterValue::kMaxRegisterByteSize) {
+ error.SetErrorString("register too small to receive memory data");
+ return error;
+ }
+
+ const size_t dst_len = reg_info->byte_size;
+
+ if (src_len > dst_len) {
+ error.SetErrorStringWithFormat(
+ "%" PRIu64 " bytes is too big to store in register %s (%" PRIu64
+ " bytes)",
+ static_cast<uint64_t>(src_len), reg_info->name,
+ static_cast<uint64_t>(dst_len));
+ return error;
+ }
+
+ NativeProcessProtocol &process = m_thread.GetProcess();
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Read the memory
+ size_t bytes_read;
+ error = process.ReadMemory(src_addr, src, src_len, bytes_read);
+ if (error.Fail())
+ return error;
+
+ // Make sure the memory read succeeded...
+ if (bytes_read != src_len) {
+ // This might happen if we read _some_ bytes but not all
+ error.SetErrorStringWithFormat("read %" PRIu64 " of %" PRIu64 " bytes",
+ static_cast<uint64_t>(bytes_read),
+ static_cast<uint64_t>(src_len));
+ return error;
+ }
+
+ // We now have a memory buffer that contains the part or all of the register
+ // value. Set the register value using this memory data.
+ // TODO: we might need to add a parameter to this function in case the byte
+ // order of the memory data doesn't match the process. For now we are
+ // assuming they are the same.
+ reg_value.SetFromMemoryData(reg_info, src, src_len, process.GetByteOrder(),
+ error);
+
+ return error;
+}
+
+Status NativeRegisterContext::WriteRegisterValueToMemory(
+ const RegisterInfo *reg_info, lldb::addr_t dst_addr, size_t dst_len,
+ const RegisterValue &reg_value) {
+
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ Status error;
+
+ NativeProcessProtocol &process = m_thread.GetProcess();
+
+ // TODO: we might need to add a parameter to this function in case the byte
+ // order of the memory data doesn't match the process. For now we are
+ // assuming they are the same.
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, dst, dst_len, process.GetByteOrder(), error);
+
+ if (error.Success()) {
+ if (bytes_copied == 0) {
+ error.SetErrorString("byte copy failed.");
+ } else {
+ size_t bytes_written;
+ error = process.WriteMemory(dst_addr, dst, bytes_copied, bytes_written);
+ if (error.Fail())
+ return error;
+
+ if (bytes_written != bytes_copied) {
+ // This might happen if we read _some_ bytes but not all
+ error.SetErrorStringWithFormat("only wrote %" PRIu64 " of %" PRIu64
+ " bytes",
+ static_cast<uint64_t>(bytes_written),
+ static_cast<uint64_t>(bytes_copied));
+ }
+ }
+ }
+
+ return error;
+}
+
+uint32_t
+NativeRegisterContext::ConvertRegisterKindToRegisterNumber(uint32_t kind,
+ uint32_t num) const {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/NativeThreadProtocol.cpp b/contrib/llvm-project/lldb/source/Host/common/NativeThreadProtocol.cpp
new file mode 100644
index 000000000000..e62b1425c891
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/NativeThreadProtocol.cpp
@@ -0,0 +1,19 @@
+//===-- NativeThreadProtocol.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+NativeThreadProtocol::NativeThreadProtocol(NativeProcessProtocol &process,
+ lldb::tid_t tid)
+ : m_process(process), m_tid(tid) {}
diff --git a/contrib/llvm-project/lldb/source/Host/common/NativeWatchpointList.cpp b/contrib/llvm-project/lldb/source/Host/common/NativeWatchpointList.cpp
new file mode 100644
index 000000000000..c3db95fb252e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/NativeWatchpointList.cpp
@@ -0,0 +1,30 @@
+//===-- NativeWatchpointList.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/common/NativeWatchpointList.h"
+
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+Status NativeWatchpointList::Add(addr_t addr, size_t size, uint32_t watch_flags,
+ bool hardware) {
+ m_watchpoints[addr] = {addr, size, watch_flags, hardware};
+ return Status();
+}
+
+Status NativeWatchpointList::Remove(addr_t addr) {
+ m_watchpoints.erase(addr);
+ return Status();
+}
+
+const NativeWatchpointList::WatchpointMap &
+NativeWatchpointList::GetWatchpointMap() const {
+ return m_watchpoints;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/OptionParser.cpp b/contrib/llvm-project/lldb/source/Host/common/OptionParser.cpp
new file mode 100644
index 000000000000..1e76f9b8f9f1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/OptionParser.cpp
@@ -0,0 +1,83 @@
+//===-- source/Host/common/OptionParser.cpp ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/HostGetOpt.h"
+#include "lldb/lldb-private-types.h"
+
+#include <vector>
+
+using namespace lldb_private;
+
+void OptionParser::Prepare(std::unique_lock<std::mutex> &lock) {
+ static std::mutex g_mutex;
+ lock = std::unique_lock<std::mutex>(g_mutex);
+#ifdef __GLIBC__
+ optind = 0;
+#else
+ optreset = 1;
+ optind = 1;
+#endif
+}
+
+void OptionParser::EnableError(bool error) { opterr = error ? 1 : 0; }
+
+int OptionParser::Parse(llvm::MutableArrayRef<char *> argv,
+ llvm::StringRef optstring, const Option *longopts,
+ int *longindex) {
+ std::vector<option> opts;
+ while (longopts->definition != nullptr) {
+ option opt;
+ opt.flag = longopts->flag;
+ opt.val = longopts->val;
+ opt.name = longopts->definition->long_option;
+ opt.has_arg = longopts->definition->option_has_arg;
+ opts.push_back(opt);
+ ++longopts;
+ }
+ opts.push_back(option());
+ std::string opt_cstr = optstring;
+ return getopt_long_only(argv.size() - 1, argv.data(), opt_cstr.c_str(),
+ &opts[0], longindex);
+}
+
+char *OptionParser::GetOptionArgument() { return optarg; }
+
+int OptionParser::GetOptionIndex() { return optind; }
+
+int OptionParser::GetOptionErrorCause() { return optopt; }
+
+std::string OptionParser::GetShortOptionString(struct option *long_options) {
+ std::string s;
+ int i = 0;
+ bool done = false;
+ while (!done) {
+ if (long_options[i].name == nullptr && long_options[i].has_arg == 0 &&
+ long_options[i].flag == nullptr && long_options[i].val == 0) {
+ done = true;
+ } else {
+ if (long_options[i].flag == nullptr && isalpha(long_options[i].val)) {
+ s.append(1, (char)long_options[i].val);
+ switch (long_options[i].has_arg) {
+ default:
+ case no_argument:
+ break;
+
+ case optional_argument:
+ s.append(2, ':');
+ break;
+ case required_argument:
+ s.append(1, ':');
+ break;
+ }
+ }
+ ++i;
+ }
+ }
+ return s;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/PipeBase.cpp b/contrib/llvm-project/lldb/source/Host/common/PipeBase.cpp
new file mode 100644
index 000000000000..2cbadf0c85f6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/PipeBase.cpp
@@ -0,0 +1,24 @@
+//===-- source/Host/common/PipeBase.cpp -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/PipeBase.h"
+
+using namespace lldb_private;
+
+PipeBase::~PipeBase() = default;
+
+Status PipeBase::OpenAsWriter(llvm::StringRef name,
+ bool child_process_inherit) {
+ return OpenAsWriterWithTimeout(name, child_process_inherit,
+ std::chrono::microseconds::zero());
+}
+
+Status PipeBase::Read(void *buf, size_t size, size_t &bytes_read) {
+ return ReadWithTimeout(buf, size, std::chrono::microseconds::zero(),
+ bytes_read);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/ProcessLaunchInfo.cpp b/contrib/llvm-project/lldb/source/Host/common/ProcessLaunchInfo.cpp
new file mode 100644
index 000000000000..266b46763996
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/ProcessLaunchInfo.cpp
@@ -0,0 +1,350 @@
+//===-- ProcessLaunchInfo.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <climits>
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/FileSystem.h"
+
+#if !defined(_WIN32)
+#include <limits.h>
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ProcessLaunchInfo member functions
+
+ProcessLaunchInfo::ProcessLaunchInfo()
+ : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(0),
+ m_file_actions(), m_pty(new PseudoTerminal), m_resume_count(0),
+ m_monitor_callback(nullptr), m_monitor_callback_baton(nullptr),
+ m_monitor_signals(false), m_listener_sp(), m_hijack_listener_sp() {}
+
+ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec,
+ const FileSpec &stdout_file_spec,
+ const FileSpec &stderr_file_spec,
+ const FileSpec &working_directory,
+ uint32_t launch_flags)
+ : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(launch_flags),
+ m_file_actions(), m_pty(new PseudoTerminal), m_resume_count(0),
+ m_monitor_callback(nullptr), m_monitor_callback_baton(nullptr),
+ m_monitor_signals(false), m_listener_sp(), m_hijack_listener_sp() {
+ if (stdin_file_spec) {
+ FileAction file_action;
+ const bool read = true;
+ const bool write = false;
+ if (file_action.Open(STDIN_FILENO, stdin_file_spec, read, write))
+ AppendFileAction(file_action);
+ }
+ if (stdout_file_spec) {
+ FileAction file_action;
+ const bool read = false;
+ const bool write = true;
+ if (file_action.Open(STDOUT_FILENO, stdout_file_spec, read, write))
+ AppendFileAction(file_action);
+ }
+ if (stderr_file_spec) {
+ FileAction file_action;
+ const bool read = false;
+ const bool write = true;
+ if (file_action.Open(STDERR_FILENO, stderr_file_spec, read, write))
+ AppendFileAction(file_action);
+ }
+ if (working_directory)
+ SetWorkingDirectory(working_directory);
+}
+
+bool ProcessLaunchInfo::AppendCloseFileAction(int fd) {
+ FileAction file_action;
+ if (file_action.Close(fd)) {
+ AppendFileAction(file_action);
+ return true;
+ }
+ return false;
+}
+
+bool ProcessLaunchInfo::AppendDuplicateFileAction(int fd, int dup_fd) {
+ FileAction file_action;
+ if (file_action.Duplicate(fd, dup_fd)) {
+ AppendFileAction(file_action);
+ return true;
+ }
+ return false;
+}
+
+bool ProcessLaunchInfo::AppendOpenFileAction(int fd, const FileSpec &file_spec,
+ bool read, bool write) {
+ FileAction file_action;
+ if (file_action.Open(fd, file_spec, read, write)) {
+ AppendFileAction(file_action);
+ return true;
+ }
+ return false;
+}
+
+bool ProcessLaunchInfo::AppendSuppressFileAction(int fd, bool read,
+ bool write) {
+ FileAction file_action;
+ if (file_action.Open(fd, FileSpec(FileSystem::DEV_NULL), read, write)) {
+ AppendFileAction(file_action);
+ return true;
+ }
+ return false;
+}
+
+const FileAction *ProcessLaunchInfo::GetFileActionAtIndex(size_t idx) const {
+ if (idx < m_file_actions.size())
+ return &m_file_actions[idx];
+ return nullptr;
+}
+
+const FileAction *ProcessLaunchInfo::GetFileActionForFD(int fd) const {
+ for (size_t idx = 0, count = m_file_actions.size(); idx < count; ++idx) {
+ if (m_file_actions[idx].GetFD() == fd)
+ return &m_file_actions[idx];
+ }
+ return nullptr;
+}
+
+const FileSpec &ProcessLaunchInfo::GetWorkingDirectory() const {
+ return m_working_dir;
+}
+
+void ProcessLaunchInfo::SetWorkingDirectory(const FileSpec &working_dir) {
+ m_working_dir = working_dir;
+}
+
+const char *ProcessLaunchInfo::GetProcessPluginName() const {
+ return (m_plugin_name.empty() ? nullptr : m_plugin_name.c_str());
+}
+
+void ProcessLaunchInfo::SetProcessPluginName(llvm::StringRef plugin) {
+ m_plugin_name = plugin;
+}
+
+const FileSpec &ProcessLaunchInfo::GetShell() const { return m_shell; }
+
+void ProcessLaunchInfo::SetShell(const FileSpec &shell) {
+ m_shell = shell;
+ if (m_shell) {
+ FileSystem::Instance().ResolveExecutableLocation(m_shell);
+ m_flags.Set(lldb::eLaunchFlagLaunchInShell);
+ } else
+ m_flags.Clear(lldb::eLaunchFlagLaunchInShell);
+}
+
+void ProcessLaunchInfo::SetLaunchInSeparateProcessGroup(bool separate) {
+ if (separate)
+ m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
+ else
+ m_flags.Clear(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
+}
+
+void ProcessLaunchInfo::SetShellExpandArguments(bool expand) {
+ if (expand)
+ m_flags.Set(lldb::eLaunchFlagShellExpandArguments);
+ else
+ m_flags.Clear(lldb::eLaunchFlagShellExpandArguments);
+}
+
+void ProcessLaunchInfo::Clear() {
+ ProcessInfo::Clear();
+ m_working_dir.Clear();
+ m_plugin_name.clear();
+ m_shell.Clear();
+ m_flags.Clear();
+ m_file_actions.clear();
+ m_resume_count = 0;
+ m_listener_sp.reset();
+ m_hijack_listener_sp.reset();
+}
+
+void ProcessLaunchInfo::SetMonitorProcessCallback(
+ const Host::MonitorChildProcessCallback &callback, bool monitor_signals) {
+ m_monitor_callback = callback;
+ m_monitor_signals = monitor_signals;
+}
+
+bool ProcessLaunchInfo::NoOpMonitorCallback(lldb::pid_t pid, bool exited, int signal, int status) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS);
+ LLDB_LOG(log, "pid = {0}, exited = {1}, signal = {2}, status = {3}", pid,
+ exited, signal, status);
+ return true;
+}
+
+bool ProcessLaunchInfo::MonitorProcess() const {
+ if (m_monitor_callback && ProcessIDIsValid()) {
+ llvm::Expected<HostThread> maybe_thread =
+ Host::StartMonitoringChildProcess(m_monitor_callback, GetProcessID(),
+ m_monitor_signals);
+ if (!maybe_thread)
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(maybe_thread.takeError()));
+ return true;
+ }
+ return false;
+}
+
+void ProcessLaunchInfo::SetDetachOnError(bool enable) {
+ if (enable)
+ m_flags.Set(lldb::eLaunchFlagDetachOnError);
+ else
+ m_flags.Clear(lldb::eLaunchFlagDetachOnError);
+}
+
+llvm::Error ProcessLaunchInfo::SetUpPtyRedirection() {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS);
+ LLDB_LOG(log, "Generating a pty to use for stdin/out/err");
+
+ int open_flags = O_RDWR | O_NOCTTY;
+#if !defined(_WIN32)
+ // We really shouldn't be specifying platform specific flags that are
+ // intended for a system call in generic code. But this will have to
+ // do for now.
+ open_flags |= O_CLOEXEC;
+#endif
+ if (!m_pty->OpenFirstAvailableMaster(open_flags, nullptr, 0)) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "PTY::OpenFirstAvailableMaster failed");
+ }
+ const FileSpec slave_file_spec(m_pty->GetSlaveName(nullptr, 0));
+
+ // Only use the slave tty if we don't have anything specified for
+ // input and don't have an action for stdin
+ if (GetFileActionForFD(STDIN_FILENO) == nullptr)
+ AppendOpenFileAction(STDIN_FILENO, slave_file_spec, true, false);
+
+ // Only use the slave tty if we don't have anything specified for
+ // output and don't have an action for stdout
+ if (GetFileActionForFD(STDOUT_FILENO) == nullptr)
+ AppendOpenFileAction(STDOUT_FILENO, slave_file_spec, false, true);
+
+ // Only use the slave tty if we don't have anything specified for
+ // error and don't have an action for stderr
+ if (GetFileActionForFD(STDERR_FILENO) == nullptr)
+ AppendOpenFileAction(STDERR_FILENO, slave_file_spec, false, true);
+ return llvm::Error::success();
+}
+
+bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell(
+ Status &error, bool localhost, bool will_debug,
+ bool first_arg_is_full_shell_command, int32_t num_resumes) {
+ error.Clear();
+
+ if (GetFlags().Test(eLaunchFlagLaunchInShell)) {
+ if (m_shell) {
+ std::string shell_executable = m_shell.GetPath();
+
+ const char **argv = GetArguments().GetConstArgumentVector();
+ if (argv == nullptr || argv[0] == nullptr)
+ return false;
+ Args shell_arguments;
+ std::string safe_arg;
+ shell_arguments.AppendArgument(shell_executable);
+ const llvm::Triple &triple = GetArchitecture().GetTriple();
+ if (triple.getOS() == llvm::Triple::Win32 &&
+ !triple.isWindowsCygwinEnvironment())
+ shell_arguments.AppendArgument(llvm::StringRef("/C"));
+ else
+ shell_arguments.AppendArgument(llvm::StringRef("-c"));
+
+ StreamString shell_command;
+ if (will_debug) {
+ // Add a modified PATH environment variable in case argv[0] is a
+ // relative path.
+ const char *argv0 = argv[0];
+ FileSpec arg_spec(argv0);
+ if (arg_spec.IsRelative()) {
+ // We have a relative path to our executable which may not work if we
+ // just try to run "a.out" (without it being converted to "./a.out")
+ FileSpec working_dir = GetWorkingDirectory();
+ // Be sure to put quotes around PATH's value in case any paths have
+ // spaces...
+ std::string new_path("PATH=\"");
+ const size_t empty_path_len = new_path.size();
+
+ if (working_dir) {
+ new_path += working_dir.GetPath();
+ } else {
+ llvm::SmallString<64> cwd;
+ if (! llvm::sys::fs::current_path(cwd))
+ new_path += cwd;
+ }
+ std::string curr_path;
+ if (HostInfo::GetEnvironmentVar("PATH", curr_path)) {
+ if (new_path.size() > empty_path_len)
+ new_path += ':';
+ new_path += curr_path;
+ }
+ new_path += "\" ";
+ shell_command.PutCString(new_path);
+ }
+
+ if (triple.getOS() != llvm::Triple::Win32 ||
+ triple.isWindowsCygwinEnvironment())
+ shell_command.PutCString("exec");
+
+ // Only Apple supports /usr/bin/arch being able to specify the
+ // architecture
+ if (GetArchitecture().IsValid() && // Valid architecture
+ GetArchitecture().GetTriple().getVendor() ==
+ llvm::Triple::Apple && // Apple only
+ GetArchitecture().GetCore() !=
+ ArchSpec::eCore_x86_64_x86_64h) // Don't do this for x86_64h
+ {
+ shell_command.Printf(" /usr/bin/arch -arch %s",
+ GetArchitecture().GetArchitectureName());
+ // Set the resume count to 2:
+ // 1 - stop in shell
+ // 2 - stop in /usr/bin/arch
+ // 3 - then we will stop in our program
+ SetResumeCount(num_resumes + 1);
+ } else {
+ // Set the resume count to 1:
+ // 1 - stop in shell
+ // 2 - then we will stop in our program
+ SetResumeCount(num_resumes);
+ }
+ }
+
+ if (first_arg_is_full_shell_command) {
+ // There should only be one argument that is the shell command itself
+ // to be used as is
+ if (argv[0] && !argv[1])
+ shell_command.Printf("%s", argv[0]);
+ else
+ return false;
+ } else {
+ for (size_t i = 0; argv[i] != nullptr; ++i) {
+ const char *arg =
+ Args::GetShellSafeArgument(m_shell, argv[i], safe_arg);
+ shell_command.Printf(" %s", arg);
+ }
+ }
+ shell_arguments.AppendArgument(shell_command.GetString());
+ m_executable = m_shell;
+ m_arguments = shell_arguments;
+ return true;
+ } else {
+ error.SetErrorString("invalid shell path");
+ }
+ } else {
+ error.SetErrorString("not launching in shell");
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/ProcessRunLock.cpp b/contrib/llvm-project/lldb/source/Host/common/ProcessRunLock.cpp
new file mode 100644
index 000000000000..a931da718766
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/ProcessRunLock.cpp
@@ -0,0 +1,64 @@
+//===-- ProcessRunLock.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _WIN32
+#include "lldb/Host/ProcessRunLock.h"
+
+namespace lldb_private {
+
+ProcessRunLock::ProcessRunLock() : m_running(false) {
+ int err = ::pthread_rwlock_init(&m_rwlock, nullptr);
+ (void)err;
+}
+
+ProcessRunLock::~ProcessRunLock() {
+ int err = ::pthread_rwlock_destroy(&m_rwlock);
+ (void)err;
+}
+
+bool ProcessRunLock::ReadTryLock() {
+ ::pthread_rwlock_rdlock(&m_rwlock);
+ if (!m_running) {
+ return true;
+ }
+ ::pthread_rwlock_unlock(&m_rwlock);
+ return false;
+}
+
+bool ProcessRunLock::ReadUnlock() {
+ return ::pthread_rwlock_unlock(&m_rwlock) == 0;
+}
+
+bool ProcessRunLock::SetRunning() {
+ ::pthread_rwlock_wrlock(&m_rwlock);
+ m_running = true;
+ ::pthread_rwlock_unlock(&m_rwlock);
+ return true;
+}
+
+bool ProcessRunLock::TrySetRunning() {
+ bool r;
+
+ if (::pthread_rwlock_trywrlock(&m_rwlock) == 0) {
+ r = !m_running;
+ m_running = true;
+ ::pthread_rwlock_unlock(&m_rwlock);
+ return r;
+ }
+ return false;
+}
+
+bool ProcessRunLock::SetStopped() {
+ ::pthread_rwlock_wrlock(&m_rwlock);
+ m_running = false;
+ ::pthread_rwlock_unlock(&m_rwlock);
+ return true;
+}
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp b/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp
new file mode 100644
index 000000000000..85e54f4d3d6a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp
@@ -0,0 +1,287 @@
+//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Host/Config.h"
+
+#include "llvm/Support/Errno.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(TIOCSCTTY)
+#include <sys/ioctl.h>
+#endif
+
+#include "lldb/Host/PosixApi.h"
+
+#if defined(__ANDROID__)
+int posix_openpt(int flags);
+#endif
+
+using namespace lldb_private;
+
+// Write string describing error number
+static void ErrnoToStr(char *error_str, size_t error_len) {
+ std::string strerror = llvm::sys::StrError();
+ ::snprintf(error_str, error_len, "%s", strerror.c_str());
+}
+
+// PseudoTerminal constructor
+PseudoTerminal::PseudoTerminal()
+ : m_master_fd(invalid_fd), m_slave_fd(invalid_fd) {}
+
+// Destructor
+//
+// The destructor will close the master and slave file descriptors if they are
+// valid and ownership has not been released using the
+// ReleaseMasterFileDescriptor() or the ReleaseSaveFileDescriptor() member
+// functions.
+PseudoTerminal::~PseudoTerminal() {
+ CloseMasterFileDescriptor();
+ CloseSlaveFileDescriptor();
+}
+
+// Close the master file descriptor if it is valid.
+void PseudoTerminal::CloseMasterFileDescriptor() {
+ if (m_master_fd >= 0) {
+ ::close(m_master_fd);
+ m_master_fd = invalid_fd;
+ }
+}
+
+// Close the slave file descriptor if it is valid.
+void PseudoTerminal::CloseSlaveFileDescriptor() {
+ if (m_slave_fd >= 0) {
+ ::close(m_slave_fd);
+ m_slave_fd = invalid_fd;
+ }
+}
+
+// Open the first available pseudo terminal with OFLAG as the permissions. The
+// file descriptor is stored in this object and can be accessed with the
+// MasterFileDescriptor() accessor. The ownership of the master file descriptor
+// can be released using the ReleaseMasterFileDescriptor() accessor. If this
+// object has a valid master files descriptor when its destructor is called, it
+// will close the master file descriptor, therefore clients must call
+// ReleaseMasterFileDescriptor() if they wish to use the master file descriptor
+// after this object is out of scope or destroyed.
+//
+// RETURNS:
+// True when successful, false indicating an error occurred.
+bool PseudoTerminal::OpenFirstAvailableMaster(int oflag, char *error_str,
+ size_t error_len) {
+ if (error_str)
+ error_str[0] = '\0';
+
+#if !defined(LLDB_DISABLE_POSIX)
+ // Open the master side of a pseudo terminal
+ m_master_fd = ::posix_openpt(oflag);
+ if (m_master_fd < 0) {
+ if (error_str)
+ ErrnoToStr(error_str, error_len);
+ return false;
+ }
+
+ // Grant access to the slave pseudo terminal
+ if (::grantpt(m_master_fd) < 0) {
+ if (error_str)
+ ErrnoToStr(error_str, error_len);
+ CloseMasterFileDescriptor();
+ return false;
+ }
+
+ // Clear the lock flag on the slave pseudo terminal
+ if (::unlockpt(m_master_fd) < 0) {
+ if (error_str)
+ ErrnoToStr(error_str, error_len);
+ CloseMasterFileDescriptor();
+ return false;
+ }
+
+ return true;
+#else
+ if (error_str)
+ ::snprintf(error_str, error_len, "%s", "pseudo terminal not supported");
+ return false;
+#endif
+}
+
+// Open the slave pseudo terminal for the current master pseudo terminal. A
+// master pseudo terminal should already be valid prior to calling this
+// function (see OpenFirstAvailableMaster()). The file descriptor is stored
+// this object's member variables and can be accessed via the
+// GetSlaveFileDescriptor(), or released using the ReleaseSlaveFileDescriptor()
+// member function.
+//
+// RETURNS:
+// True when successful, false indicating an error occurred.
+bool PseudoTerminal::OpenSlave(int oflag, char *error_str, size_t error_len) {
+ if (error_str)
+ error_str[0] = '\0';
+
+ CloseSlaveFileDescriptor();
+
+ // Open the master side of a pseudo terminal
+ const char *slave_name = GetSlaveName(error_str, error_len);
+
+ if (slave_name == nullptr)
+ return false;
+
+ m_slave_fd = llvm::sys::RetryAfterSignal(-1, ::open, slave_name, oflag);
+
+ if (m_slave_fd < 0) {
+ if (error_str)
+ ErrnoToStr(error_str, error_len);
+ return false;
+ }
+
+ return true;
+}
+
+// Get the name of the slave pseudo terminal. A master pseudo terminal should
+// already be valid prior to calling this function (see
+// OpenFirstAvailableMaster()).
+//
+// RETURNS:
+// NULL if no valid master pseudo terminal or if ptsname() fails.
+// The name of the slave pseudo terminal as a NULL terminated C string
+// that comes from static memory, so a copy of the string should be
+// made as subsequent calls can change this value.
+const char *PseudoTerminal::GetSlaveName(char *error_str,
+ size_t error_len) const {
+ if (error_str)
+ error_str[0] = '\0';
+
+ if (m_master_fd < 0) {
+ if (error_str)
+ ::snprintf(error_str, error_len, "%s",
+ "master file descriptor is invalid");
+ return nullptr;
+ }
+ const char *slave_name = ::ptsname(m_master_fd);
+
+ if (error_str && slave_name == nullptr)
+ ErrnoToStr(error_str, error_len);
+
+ return slave_name;
+}
+
+// Fork a child process and have its stdio routed to a pseudo terminal.
+//
+// In the parent process when a valid pid is returned, the master file
+// descriptor can be used as a read/write access to stdio of the child process.
+//
+// In the child process the stdin/stdout/stderr will already be routed to the
+// slave pseudo terminal and the master file descriptor will be closed as it is
+// no longer needed by the child process.
+//
+// This class will close the file descriptors for the master/slave when the
+// destructor is called, so be sure to call ReleaseMasterFileDescriptor() or
+// ReleaseSlaveFileDescriptor() if any file descriptors are going to be used
+// past the lifespan of this object.
+//
+// RETURNS:
+// in the parent process: the pid of the child, or -1 if fork fails
+// in the child process: zero
+lldb::pid_t PseudoTerminal::Fork(char *error_str, size_t error_len) {
+ if (error_str)
+ error_str[0] = '\0';
+ pid_t pid = LLDB_INVALID_PROCESS_ID;
+#if !defined(LLDB_DISABLE_POSIX)
+ int flags = O_RDWR;
+ flags |= O_CLOEXEC;
+ if (OpenFirstAvailableMaster(flags, error_str, error_len)) {
+ // Successfully opened our master pseudo terminal
+
+ pid = ::fork();
+ if (pid < 0) {
+ // Fork failed
+ if (error_str)
+ ErrnoToStr(error_str, error_len);
+ } else if (pid == 0) {
+ // Child Process
+ ::setsid();
+
+ if (OpenSlave(O_RDWR, error_str, error_len)) {
+ // Successfully opened slave
+
+ // Master FD should have O_CLOEXEC set, but let's close it just in
+ // case...
+ CloseMasterFileDescriptor();
+
+#if defined(TIOCSCTTY)
+ // Acquire the controlling terminal
+ if (::ioctl(m_slave_fd, TIOCSCTTY, (char *)0) < 0) {
+ if (error_str)
+ ErrnoToStr(error_str, error_len);
+ }
+#endif
+ // Duplicate all stdio file descriptors to the slave pseudo terminal
+ if (::dup2(m_slave_fd, STDIN_FILENO) != STDIN_FILENO) {
+ if (error_str && !error_str[0])
+ ErrnoToStr(error_str, error_len);
+ }
+
+ if (::dup2(m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO) {
+ if (error_str && !error_str[0])
+ ErrnoToStr(error_str, error_len);
+ }
+
+ if (::dup2(m_slave_fd, STDERR_FILENO) != STDERR_FILENO) {
+ if (error_str && !error_str[0])
+ ErrnoToStr(error_str, error_len);
+ }
+ }
+ } else {
+ // Parent Process
+ // Do nothing and let the pid get returned!
+ }
+ }
+#endif
+ return pid;
+}
+
+// The master file descriptor accessor. This object retains ownership of the
+// master file descriptor when this accessor is used. Use
+// ReleaseMasterFileDescriptor() if you wish this object to release ownership
+// of the master file descriptor.
+//
+// Returns the master file descriptor, or -1 if the master file descriptor is
+// not currently valid.
+int PseudoTerminal::GetMasterFileDescriptor() const { return m_master_fd; }
+
+// The slave file descriptor accessor.
+//
+// Returns the slave file descriptor, or -1 if the slave file descriptor is not
+// currently valid.
+int PseudoTerminal::GetSlaveFileDescriptor() const { return m_slave_fd; }
+
+// Release ownership of the master pseudo terminal file descriptor without
+// closing it. The destructor for this class will close the master file
+// descriptor if the ownership isn't released using this call and the master
+// file descriptor has been opened.
+int PseudoTerminal::ReleaseMasterFileDescriptor() {
+ // Release ownership of the master pseudo terminal file descriptor without
+ // closing it. (the destructor for this class will close it otherwise!)
+ int fd = m_master_fd;
+ m_master_fd = invalid_fd;
+ return fd;
+}
+
+// Release ownership of the slave pseudo terminal file descriptor without
+// closing it. The destructor for this class will close the slave file
+// descriptor if the ownership isn't released using this call and the slave
+// file descriptor has been opened.
+int PseudoTerminal::ReleaseSlaveFileDescriptor() {
+ // Release ownership of the slave pseudo terminal file descriptor without
+ // closing it (the destructor for this class will close it otherwise!)
+ int fd = m_slave_fd;
+ m_slave_fd = invalid_fd;
+ return fd;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/Socket.cpp b/contrib/llvm-project/lldb/source/Host/common/Socket.cpp
new file mode 100644
index 000000000000..a89f1178e96c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/Socket.cpp
@@ -0,0 +1,491 @@
+//===-- Socket.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Socket.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/SocketAddress.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/common/UDPSocket.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/WindowsError.h"
+
+#ifndef LLDB_DISABLE_POSIX
+#include "lldb/Host/posix/DomainSocket.h"
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#endif
+
+#ifdef __linux__
+#include "lldb/Host/linux/AbstractSocket.h"
+#endif
+
+#ifdef __ANDROID__
+#include <arpa/inet.h>
+#include <asm-generic/errno-base.h>
+#include <errno.h>
+#include <linux/tcp.h>
+#include <fcntl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#endif // __ANDROID__
+
+using namespace lldb;
+using namespace lldb_private;
+
+#if defined(_WIN32)
+typedef const char *set_socket_option_arg_type;
+typedef char *get_socket_option_arg_type;
+const NativeSocket Socket::kInvalidSocketValue = INVALID_SOCKET;
+#else // #if defined(_WIN32)
+typedef const void *set_socket_option_arg_type;
+typedef void *get_socket_option_arg_type;
+const NativeSocket Socket::kInvalidSocketValue = -1;
+#endif // #if defined(_WIN32)
+
+namespace {
+
+bool IsInterrupted() {
+#if defined(_WIN32)
+ return ::WSAGetLastError() == WSAEINTR;
+#else
+ return errno == EINTR;
+#endif
+}
+}
+
+Socket::Socket(SocketProtocol protocol, bool should_close,
+ bool child_processes_inherit)
+ : IOObject(eFDTypeSocket, should_close), m_protocol(protocol),
+ m_socket(kInvalidSocketValue),
+ m_child_processes_inherit(child_processes_inherit) {}
+
+Socket::~Socket() { Close(); }
+
+llvm::Error Socket::Initialize() {
+#if defined(_WIN32)
+ auto wVersion = WINSOCK_VERSION;
+ WSADATA wsaData;
+ int err = ::WSAStartup(wVersion, &wsaData);
+ if (err == 0) {
+ if (wsaData.wVersion < wVersion) {
+ WSACleanup();
+ return llvm::make_error<llvm::StringError>(
+ "WSASock version is not expected.", llvm::inconvertibleErrorCode());
+ }
+ } else {
+ return llvm::errorCodeToError(llvm::mapWindowsError(::WSAGetLastError()));
+ }
+#endif
+
+ return llvm::Error::success();
+}
+
+void Socket::Terminate() {
+#if defined(_WIN32)
+ ::WSACleanup();
+#endif
+}
+
+std::unique_ptr<Socket> Socket::Create(const SocketProtocol protocol,
+ bool child_processes_inherit,
+ Status &error) {
+ error.Clear();
+
+ std::unique_ptr<Socket> socket_up;
+ switch (protocol) {
+ case ProtocolTcp:
+ socket_up =
+ llvm::make_unique<TCPSocket>(true, child_processes_inherit);
+ break;
+ case ProtocolUdp:
+ socket_up =
+ llvm::make_unique<UDPSocket>(true, child_processes_inherit);
+ break;
+ case ProtocolUnixDomain:
+#ifndef LLDB_DISABLE_POSIX
+ socket_up =
+ llvm::make_unique<DomainSocket>(true, child_processes_inherit);
+#else
+ error.SetErrorString(
+ "Unix domain sockets are not supported on this platform.");
+#endif
+ break;
+ case ProtocolUnixAbstract:
+#ifdef __linux__
+ socket_up =
+ llvm::make_unique<AbstractSocket>(child_processes_inherit);
+#else
+ error.SetErrorString(
+ "Abstract domain sockets are not supported on this platform.");
+#endif
+ break;
+ }
+
+ if (error.Fail())
+ socket_up.reset();
+
+ return socket_up;
+}
+
+Status Socket::TcpConnect(llvm::StringRef host_and_port,
+ bool child_processes_inherit, Socket *&socket) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_COMMUNICATION));
+ if (log)
+ log->Printf("Socket::%s (host/port = %s)", __FUNCTION__,
+ host_and_port.str().c_str());
+
+ Status error;
+ std::unique_ptr<Socket> connect_socket(
+ Create(ProtocolTcp, child_processes_inherit, error));
+ if (error.Fail())
+ return error;
+
+ error = connect_socket->Connect(host_and_port);
+ if (error.Success())
+ socket = connect_socket.release();
+
+ return error;
+}
+
+Status Socket::TcpListen(llvm::StringRef host_and_port,
+ bool child_processes_inherit, Socket *&socket,
+ Predicate<uint16_t> *predicate, int backlog) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("Socket::%s (%s)", __FUNCTION__, host_and_port.str().c_str());
+
+ Status error;
+ std::string host_str;
+ std::string port_str;
+ int32_t port = INT32_MIN;
+ if (!DecodeHostAndPort(host_and_port, host_str, port_str, port, &error))
+ return error;
+
+ std::unique_ptr<TCPSocket> listen_socket(
+ new TCPSocket(true, child_processes_inherit));
+ if (error.Fail())
+ return error;
+
+ error = listen_socket->Listen(host_and_port, backlog);
+ if (error.Success()) {
+ // We were asked to listen on port zero which means we must now read the
+ // actual port that was given to us as port zero is a special code for
+ // "find an open port for me".
+ if (port == 0)
+ port = listen_socket->GetLocalPortNumber();
+
+ // Set the port predicate since when doing a listen://<host>:<port> it
+ // often needs to accept the incoming connection which is a blocking system
+ // call. Allowing access to the bound port using a predicate allows us to
+ // wait for the port predicate to be set to a non-zero value from another
+ // thread in an efficient manor.
+ if (predicate)
+ predicate->SetValue(port, eBroadcastAlways);
+ socket = listen_socket.release();
+ }
+
+ return error;
+}
+
+Status Socket::UdpConnect(llvm::StringRef host_and_port,
+ bool child_processes_inherit, Socket *&socket) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("Socket::%s (host/port = %s)", __FUNCTION__,
+ host_and_port.str().c_str());
+
+ return UDPSocket::Connect(host_and_port, child_processes_inherit, socket);
+}
+
+Status Socket::UnixDomainConnect(llvm::StringRef name,
+ bool child_processes_inherit,
+ Socket *&socket) {
+ Status error;
+ std::unique_ptr<Socket> connect_socket(
+ Create(ProtocolUnixDomain, child_processes_inherit, error));
+ if (error.Fail())
+ return error;
+
+ error = connect_socket->Connect(name);
+ if (error.Success())
+ socket = connect_socket.release();
+
+ return error;
+}
+
+Status Socket::UnixDomainAccept(llvm::StringRef name,
+ bool child_processes_inherit, Socket *&socket) {
+ Status error;
+ std::unique_ptr<Socket> listen_socket(
+ Create(ProtocolUnixDomain, child_processes_inherit, error));
+ if (error.Fail())
+ return error;
+
+ error = listen_socket->Listen(name, 5);
+ if (error.Fail())
+ return error;
+
+ error = listen_socket->Accept(socket);
+ return error;
+}
+
+Status Socket::UnixAbstractConnect(llvm::StringRef name,
+ bool child_processes_inherit,
+ Socket *&socket) {
+ Status error;
+ std::unique_ptr<Socket> connect_socket(
+ Create(ProtocolUnixAbstract, child_processes_inherit, error));
+ if (error.Fail())
+ return error;
+
+ error = connect_socket->Connect(name);
+ if (error.Success())
+ socket = connect_socket.release();
+ return error;
+}
+
+Status Socket::UnixAbstractAccept(llvm::StringRef name,
+ bool child_processes_inherit,
+ Socket *&socket) {
+ Status error;
+ std::unique_ptr<Socket> listen_socket(
+ Create(ProtocolUnixAbstract, child_processes_inherit, error));
+ if (error.Fail())
+ return error;
+
+ error = listen_socket->Listen(name, 5);
+ if (error.Fail())
+ return error;
+
+ error = listen_socket->Accept(socket);
+ return error;
+}
+
+bool Socket::DecodeHostAndPort(llvm::StringRef host_and_port,
+ std::string &host_str, std::string &port_str,
+ int32_t &port, Status *error_ptr) {
+ static RegularExpression g_regex(
+ llvm::StringRef("([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)"));
+ RegularExpression::Match regex_match(2);
+ if (g_regex.Execute(host_and_port, &regex_match)) {
+ if (regex_match.GetMatchAtIndex(host_and_port, 1, host_str) &&
+ regex_match.GetMatchAtIndex(host_and_port, 2, port_str)) {
+ // IPv6 addresses are wrapped in [] when specified with ports
+ if (host_str.front() == '[' && host_str.back() == ']')
+ host_str = host_str.substr(1, host_str.size() - 2);
+ bool ok = false;
+ port = StringConvert::ToUInt32(port_str.c_str(), UINT32_MAX, 10, &ok);
+ if (ok && port <= UINT16_MAX) {
+ if (error_ptr)
+ error_ptr->Clear();
+ return true;
+ }
+ // port is too large
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat(
+ "invalid host:port specification: '%s'",
+ host_and_port.str().c_str());
+ return false;
+ }
+ }
+
+ // If this was unsuccessful, then check if it's simply a signed 32-bit
+ // integer, representing a port with an empty host.
+ host_str.clear();
+ port_str.clear();
+ if (to_integer(host_and_port, port, 10) && port < UINT16_MAX) {
+ port_str = host_and_port;
+ if (error_ptr)
+ error_ptr->Clear();
+ return true;
+ }
+
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("invalid host:port specification: '%s'",
+ host_and_port.str().c_str());
+ return false;
+}
+
+IOObject::WaitableHandle Socket::GetWaitableHandle() {
+ // TODO: On Windows, use WSAEventSelect
+ return m_socket;
+}
+
+Status Socket::Read(void *buf, size_t &num_bytes) {
+ Status error;
+ int bytes_received = 0;
+ do {
+ bytes_received = ::recv(m_socket, static_cast<char *>(buf), num_bytes, 0);
+ } while (bytes_received < 0 && IsInterrupted());
+
+ if (bytes_received < 0) {
+ SetLastError(error);
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_received;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_COMMUNICATION));
+ if (log) {
+ log->Printf("%p Socket::Read() (socket = %" PRIu64
+ ", src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64
+ " (error = %s)",
+ static_cast<void *>(this), static_cast<uint64_t>(m_socket), buf,
+ static_cast<uint64_t>(num_bytes),
+ static_cast<int64_t>(bytes_received), error.AsCString());
+ }
+
+ return error;
+}
+
+Status Socket::Write(const void *buf, size_t &num_bytes) {
+ Status error;
+ int bytes_sent = 0;
+ do {
+ bytes_sent = Send(buf, num_bytes);
+ } while (bytes_sent < 0 && IsInterrupted());
+
+ if (bytes_sent < 0) {
+ SetLastError(error);
+ num_bytes = 0;
+ } else
+ num_bytes = bytes_sent;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_COMMUNICATION));
+ if (log) {
+ log->Printf("%p Socket::Write() (socket = %" PRIu64
+ ", src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64
+ " (error = %s)",
+ static_cast<void *>(this), static_cast<uint64_t>(m_socket), buf,
+ static_cast<uint64_t>(num_bytes),
+ static_cast<int64_t>(bytes_sent), error.AsCString());
+ }
+
+ return error;
+}
+
+Status Socket::PreDisconnect() {
+ Status error;
+ return error;
+}
+
+Status Socket::Close() {
+ Status error;
+ if (!IsValid() || !m_should_close_fd)
+ return error;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("%p Socket::Close (fd = %" PRIu64 ")",
+ static_cast<void *>(this), static_cast<uint64_t>(m_socket));
+
+#if defined(_WIN32)
+ bool success = !!closesocket(m_socket);
+#else
+ bool success = !!::close(m_socket);
+#endif
+ // A reference to a FD was passed in, set it to an invalid value
+ m_socket = kInvalidSocketValue;
+ if (!success) {
+ SetLastError(error);
+ }
+
+ return error;
+}
+
+int Socket::GetOption(int level, int option_name, int &option_value) {
+ get_socket_option_arg_type option_value_p =
+ reinterpret_cast<get_socket_option_arg_type>(&option_value);
+ socklen_t option_value_size = sizeof(int);
+ return ::getsockopt(m_socket, level, option_name, option_value_p,
+ &option_value_size);
+}
+
+int Socket::SetOption(int level, int option_name, int option_value) {
+ set_socket_option_arg_type option_value_p =
+ reinterpret_cast<get_socket_option_arg_type>(&option_value);
+ return ::setsockopt(m_socket, level, option_name, option_value_p,
+ sizeof(option_value));
+}
+
+size_t Socket::Send(const void *buf, const size_t num_bytes) {
+ return ::send(m_socket, static_cast<const char *>(buf), num_bytes, 0);
+}
+
+void Socket::SetLastError(Status &error) {
+#if defined(_WIN32)
+ error.SetError(::WSAGetLastError(), lldb::eErrorTypeWin32);
+#else
+ error.SetErrorToErrno();
+#endif
+}
+
+NativeSocket Socket::CreateSocket(const int domain, const int type,
+ const int protocol,
+ bool child_processes_inherit, Status &error) {
+ error.Clear();
+ auto socket_type = type;
+#ifdef SOCK_CLOEXEC
+ if (!child_processes_inherit)
+ socket_type |= SOCK_CLOEXEC;
+#endif
+ auto sock = ::socket(domain, socket_type, protocol);
+ if (sock == kInvalidSocketValue)
+ SetLastError(error);
+
+ return sock;
+}
+
+NativeSocket Socket::AcceptSocket(NativeSocket sockfd, struct sockaddr *addr,
+ socklen_t *addrlen,
+ bool child_processes_inherit, Status &error) {
+ error.Clear();
+#if defined(ANDROID_USE_ACCEPT_WORKAROUND)
+ // Hack:
+ // This enables static linking lldb-server to an API 21 libc, but still
+ // having it run on older devices. It is necessary because API 21 libc's
+ // implementation of accept() uses the accept4 syscall(), which is not
+ // available in older kernels. Using an older libc would fix this issue, but
+ // introduce other ones, as the old libraries were quite buggy.
+ int fd = syscall(__NR_accept, sockfd, addr, addrlen);
+ if (fd >= 0 && !child_processes_inherit) {
+ int flags = ::fcntl(fd, F_GETFD);
+ if (flags != -1 && ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1)
+ return fd;
+ SetLastError(error);
+ close(fd);
+ }
+ return fd;
+#elif defined(SOCK_CLOEXEC) && defined(HAVE_ACCEPT4)
+ int flags = 0;
+ if (!child_processes_inherit) {
+ flags |= SOCK_CLOEXEC;
+ }
+ NativeSocket fd = llvm::sys::RetryAfterSignal(-1, ::accept4,
+ sockfd, addr, addrlen, flags);
+#else
+ NativeSocket fd = llvm::sys::RetryAfterSignal(-1, ::accept,
+ sockfd, addr, addrlen);
+#endif
+ if (fd == kInvalidSocketValue)
+ SetLastError(error);
+ return fd;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/SocketAddress.cpp b/contrib/llvm-project/lldb/source/Host/common/SocketAddress.cpp
new file mode 100644
index 000000000000..882fd24558f7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/SocketAddress.cpp
@@ -0,0 +1,328 @@
+//===-- SocketAddress.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Note: This file is used on Darwin by debugserver, so it needs to remain as
+// self contained as possible, and devoid of references to LLVM unless
+// there is compelling reason.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(_MSC_VER)
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+#endif
+
+#include "lldb/Host/SocketAddress.h"
+#include <stddef.h>
+#include <stdio.h>
+
+#if !defined(_WIN32)
+#include <arpa/inet.h>
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+#include "lldb/Host/PosixApi.h"
+
+// WindowsXP needs an inet_ntop implementation
+#ifdef _WIN32
+
+#ifndef INET6_ADDRSTRLEN // might not be defined in older Windows SDKs
+#define INET6_ADDRSTRLEN 46
+#endif
+
+// TODO: implement shortened form "::" for runs of zeros
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) {
+ if (size == 0) {
+ return nullptr;
+ }
+
+ switch (af) {
+ case AF_INET: {
+ {
+ const char *formatted = inet_ntoa(*static_cast<const in_addr *>(src));
+ if (formatted && strlen(formatted) < static_cast<size_t>(size)) {
+ return ::strcpy(dst, formatted);
+ }
+ }
+ return nullptr;
+ case AF_INET6: {
+ char tmp[INET6_ADDRSTRLEN] = {0};
+ const uint16_t *src16 = static_cast<const uint16_t *>(src);
+ int full_size = ::snprintf(
+ tmp, sizeof(tmp), "%x:%x:%x:%x:%x:%x:%x:%x", ntohs(src16[0]),
+ ntohs(src16[1]), ntohs(src16[2]), ntohs(src16[3]), ntohs(src16[4]),
+ ntohs(src16[5]), ntohs(src16[6]), ntohs(src16[7]));
+ if (full_size < static_cast<int>(size)) {
+ return ::strcpy(dst, tmp);
+ }
+ return nullptr;
+ }
+ }
+ }
+ return nullptr;
+}
+#endif
+
+using namespace lldb_private;
+
+// SocketAddress constructor
+SocketAddress::SocketAddress() { Clear(); }
+
+SocketAddress::SocketAddress(const struct sockaddr &s) { m_socket_addr.sa = s; }
+
+SocketAddress::SocketAddress(const struct sockaddr_in &s) {
+ m_socket_addr.sa_ipv4 = s;
+}
+
+SocketAddress::SocketAddress(const struct sockaddr_in6 &s) {
+ m_socket_addr.sa_ipv6 = s;
+}
+
+SocketAddress::SocketAddress(const struct sockaddr_storage &s) {
+ m_socket_addr.sa_storage = s;
+}
+
+SocketAddress::SocketAddress(const struct addrinfo *addr_info) {
+ *this = addr_info;
+}
+
+// Destructor
+SocketAddress::~SocketAddress() {}
+
+void SocketAddress::Clear() {
+ memset(&m_socket_addr, 0, sizeof(m_socket_addr));
+}
+
+bool SocketAddress::IsValid() const { return GetLength() != 0; }
+
+static socklen_t GetFamilyLength(sa_family_t family) {
+ switch (family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ }
+ assert(0 && "Unsupported address family");
+ return 0;
+}
+
+socklen_t SocketAddress::GetLength() const {
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
+ return m_socket_addr.sa.sa_len;
+#else
+ return GetFamilyLength(GetFamily());
+#endif
+}
+
+socklen_t SocketAddress::GetMaxLength() { return sizeof(sockaddr_t); }
+
+sa_family_t SocketAddress::GetFamily() const {
+ return m_socket_addr.sa.sa_family;
+}
+
+void SocketAddress::SetFamily(sa_family_t family) {
+ m_socket_addr.sa.sa_family = family;
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
+ m_socket_addr.sa.sa_len = GetFamilyLength(family);
+#endif
+}
+
+std::string SocketAddress::GetIPAddress() const {
+ char str[INET6_ADDRSTRLEN] = {0};
+ switch (GetFamily()) {
+ case AF_INET:
+ if (inet_ntop(GetFamily(), &m_socket_addr.sa_ipv4.sin_addr, str,
+ sizeof(str)))
+ return str;
+ break;
+ case AF_INET6:
+ if (inet_ntop(GetFamily(), &m_socket_addr.sa_ipv6.sin6_addr, str,
+ sizeof(str)))
+ return str;
+ break;
+ }
+ return "";
+}
+
+uint16_t SocketAddress::GetPort() const {
+ switch (GetFamily()) {
+ case AF_INET:
+ return ntohs(m_socket_addr.sa_ipv4.sin_port);
+ case AF_INET6:
+ return ntohs(m_socket_addr.sa_ipv6.sin6_port);
+ }
+ return 0;
+}
+
+bool SocketAddress::SetPort(uint16_t port) {
+ switch (GetFamily()) {
+ case AF_INET:
+ m_socket_addr.sa_ipv4.sin_port = htons(port);
+ return true;
+
+ case AF_INET6:
+ m_socket_addr.sa_ipv6.sin6_port = htons(port);
+ return true;
+ }
+ return false;
+}
+
+// SocketAddress assignment operator
+const SocketAddress &SocketAddress::operator=(const SocketAddress &rhs) {
+ if (this != &rhs)
+ m_socket_addr = rhs.m_socket_addr;
+ return *this;
+}
+
+const SocketAddress &SocketAddress::
+operator=(const struct addrinfo *addr_info) {
+ Clear();
+ if (addr_info && addr_info->ai_addr && addr_info->ai_addrlen > 0 &&
+ size_t(addr_info->ai_addrlen) <= sizeof m_socket_addr) {
+ ::memcpy(&m_socket_addr, addr_info->ai_addr, addr_info->ai_addrlen);
+ }
+ return *this;
+}
+
+const SocketAddress &SocketAddress::operator=(const struct sockaddr &s) {
+ m_socket_addr.sa = s;
+ return *this;
+}
+
+const SocketAddress &SocketAddress::operator=(const struct sockaddr_in &s) {
+ m_socket_addr.sa_ipv4 = s;
+ return *this;
+}
+
+const SocketAddress &SocketAddress::operator=(const struct sockaddr_in6 &s) {
+ m_socket_addr.sa_ipv6 = s;
+ return *this;
+}
+
+const SocketAddress &SocketAddress::
+operator=(const struct sockaddr_storage &s) {
+ m_socket_addr.sa_storage = s;
+ return *this;
+}
+
+bool SocketAddress::getaddrinfo(const char *host, const char *service,
+ int ai_family, int ai_socktype, int ai_protocol,
+ int ai_flags) {
+ Clear();
+
+ auto addresses = GetAddressInfo(host, service, ai_family, ai_socktype,
+ ai_protocol, ai_flags);
+ if (!addresses.empty())
+ *this = addresses[0];
+ return IsValid();
+}
+
+std::vector<SocketAddress>
+SocketAddress::GetAddressInfo(const char *hostname, const char *servname,
+ int ai_family, int ai_socktype, int ai_protocol,
+ int ai_flags) {
+ std::vector<SocketAddress> addr_list;
+
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = ai_family;
+ hints.ai_socktype = ai_socktype;
+ hints.ai_protocol = ai_protocol;
+ hints.ai_flags = ai_flags;
+
+ struct addrinfo *service_info_list = nullptr;
+ int err = ::getaddrinfo(hostname, servname, &hints, &service_info_list);
+ if (err == 0 && service_info_list) {
+ for (struct addrinfo *service_ptr = service_info_list;
+ service_ptr != nullptr; service_ptr = service_ptr->ai_next) {
+ addr_list.emplace_back(SocketAddress(service_ptr));
+ }
+ }
+
+ if (service_info_list)
+ ::freeaddrinfo(service_info_list);
+ return addr_list;
+}
+
+bool SocketAddress::SetToLocalhost(sa_family_t family, uint16_t port) {
+ switch (family) {
+ case AF_INET:
+ SetFamily(AF_INET);
+ if (SetPort(port)) {
+ m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ return true;
+ }
+ break;
+
+ case AF_INET6:
+ SetFamily(AF_INET6);
+ if (SetPort(port)) {
+ m_socket_addr.sa_ipv6.sin6_addr = in6addr_loopback;
+ return true;
+ }
+ break;
+ }
+ Clear();
+ return false;
+}
+
+bool SocketAddress::SetToAnyAddress(sa_family_t family, uint16_t port) {
+ switch (family) {
+ case AF_INET:
+ SetFamily(AF_INET);
+ if (SetPort(port)) {
+ m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
+ return true;
+ }
+ break;
+
+ case AF_INET6:
+ SetFamily(AF_INET6);
+ if (SetPort(port)) {
+ m_socket_addr.sa_ipv6.sin6_addr = in6addr_any;
+ return true;
+ }
+ break;
+ }
+ Clear();
+ return false;
+}
+
+bool SocketAddress::IsAnyAddr() const {
+ return (GetFamily() == AF_INET)
+ ? m_socket_addr.sa_ipv4.sin_addr.s_addr == htonl(INADDR_ANY)
+ : 0 == memcmp(&m_socket_addr.sa_ipv6.sin6_addr, &in6addr_any, 16);
+}
+
+bool SocketAddress::IsLocalhost() const {
+ return (GetFamily() == AF_INET)
+ ? m_socket_addr.sa_ipv4.sin_addr.s_addr == htonl(INADDR_LOOPBACK)
+ : 0 == memcmp(&m_socket_addr.sa_ipv6.sin6_addr, &in6addr_loopback,
+ 16);
+}
+
+bool SocketAddress::operator==(const SocketAddress &rhs) const {
+ if (GetFamily() != rhs.GetFamily())
+ return false;
+ if (GetLength() != rhs.GetLength())
+ return false;
+ switch (GetFamily()) {
+ case AF_INET:
+ return m_socket_addr.sa_ipv4.sin_addr.s_addr ==
+ rhs.m_socket_addr.sa_ipv4.sin_addr.s_addr;
+ case AF_INET6:
+ return 0 == memcmp(&m_socket_addr.sa_ipv6.sin6_addr,
+ &rhs.m_socket_addr.sa_ipv6.sin6_addr, 16);
+ }
+ return false;
+}
+
+bool SocketAddress::operator!=(const SocketAddress &rhs) const {
+ return !(*this == rhs);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/StringConvert.cpp b/contrib/llvm-project/lldb/source/Host/common/StringConvert.cpp
new file mode 100644
index 000000000000..8bf04f0a9ca3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/StringConvert.cpp
@@ -0,0 +1,95 @@
+//===-- StringConvert.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdlib.h>
+
+#include "lldb/Host/StringConvert.h"
+
+namespace lldb_private {
+namespace StringConvert {
+
+int32_t ToSInt32(const char *s, int32_t fail_value, int base,
+ bool *success_ptr) {
+ if (s && s[0]) {
+ char *end = nullptr;
+ const long sval = ::strtol(s, &end, base);
+ if (*end == '\0') {
+ if (success_ptr)
+ *success_ptr = ((sval <= INT32_MAX) && (sval >= INT32_MIN));
+ return (int32_t)sval; // All characters were used, return the result
+ }
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+uint32_t ToUInt32(const char *s, uint32_t fail_value, int base,
+ bool *success_ptr) {
+ if (s && s[0]) {
+ char *end = nullptr;
+ const unsigned long uval = ::strtoul(s, &end, base);
+ if (*end == '\0') {
+ if (success_ptr)
+ *success_ptr = (uval <= UINT32_MAX);
+ return (uint32_t)uval; // All characters were used, return the result
+ }
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+int64_t ToSInt64(const char *s, int64_t fail_value, int base,
+ bool *success_ptr) {
+ if (s && s[0]) {
+ char *end = nullptr;
+ int64_t uval = ::strtoll(s, &end, base);
+ if (*end == '\0') {
+ if (success_ptr)
+ *success_ptr = true;
+ return uval; // All characters were used, return the result
+ }
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+uint64_t ToUInt64(const char *s, uint64_t fail_value, int base,
+ bool *success_ptr) {
+ if (s && s[0]) {
+ char *end = nullptr;
+ uint64_t uval = ::strtoull(s, &end, base);
+ if (*end == '\0') {
+ if (success_ptr)
+ *success_ptr = true;
+ return uval; // All characters were used, return the result
+ }
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+double ToDouble(const char *s, double fail_value, bool *success_ptr) {
+ if (s && s[0]) {
+ char *end = nullptr;
+ double val = strtod(s, &end);
+ if (*end == '\0') {
+ if (success_ptr)
+ *success_ptr = true;
+ return val; // All characters were used, return the result
+ }
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+}
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/TCPSocket.cpp b/contrib/llvm-project/lldb/source/Host/common/TCPSocket.cpp
new file mode 100644
index 000000000000..58f99f7832fe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/TCPSocket.cpp
@@ -0,0 +1,310 @@
+//===-- TCPSocket.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(_MSC_VER)
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+#endif
+
+#include "lldb/Host/common/TCPSocket.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/raw_ostream.h"
+
+#ifndef LLDB_DISABLE_POSIX
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#endif
+
+#if defined(_WIN32)
+#include <winsock2.h>
+#endif
+
+#ifdef _WIN32
+#define CLOSE_SOCKET closesocket
+typedef const char *set_socket_option_arg_type;
+#else
+#include <unistd.h>
+#define CLOSE_SOCKET ::close
+typedef const void *set_socket_option_arg_type;
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+const int kType = SOCK_STREAM;
+}
+
+TCPSocket::TCPSocket(bool should_close, bool child_processes_inherit)
+ : Socket(ProtocolTcp, should_close, child_processes_inherit) {}
+
+TCPSocket::TCPSocket(NativeSocket socket, const TCPSocket &listen_socket)
+ : Socket(ProtocolTcp, listen_socket.m_should_close_fd,
+ listen_socket.m_child_processes_inherit) {
+ m_socket = socket;
+}
+
+TCPSocket::TCPSocket(NativeSocket socket, bool should_close,
+ bool child_processes_inherit)
+ : Socket(ProtocolTcp, should_close, child_processes_inherit) {
+ m_socket = socket;
+}
+
+TCPSocket::~TCPSocket() { CloseListenSockets(); }
+
+bool TCPSocket::IsValid() const {
+ return m_socket != kInvalidSocketValue || m_listen_sockets.size() != 0;
+}
+
+// Return the port number that is being used by the socket.
+uint16_t TCPSocket::GetLocalPortNumber() const {
+ if (m_socket != kInvalidSocketValue) {
+ SocketAddress sock_addr;
+ socklen_t sock_addr_len = sock_addr.GetMaxLength();
+ if (::getsockname(m_socket, sock_addr, &sock_addr_len) == 0)
+ return sock_addr.GetPort();
+ } else if (!m_listen_sockets.empty()) {
+ SocketAddress sock_addr;
+ socklen_t sock_addr_len = sock_addr.GetMaxLength();
+ if (::getsockname(m_listen_sockets.begin()->first, sock_addr,
+ &sock_addr_len) == 0)
+ return sock_addr.GetPort();
+ }
+ return 0;
+}
+
+std::string TCPSocket::GetLocalIPAddress() const {
+ // We bound to port zero, so we need to figure out which port we actually
+ // bound to
+ if (m_socket != kInvalidSocketValue) {
+ SocketAddress sock_addr;
+ socklen_t sock_addr_len = sock_addr.GetMaxLength();
+ if (::getsockname(m_socket, sock_addr, &sock_addr_len) == 0)
+ return sock_addr.GetIPAddress();
+ }
+ return "";
+}
+
+uint16_t TCPSocket::GetRemotePortNumber() const {
+ if (m_socket != kInvalidSocketValue) {
+ SocketAddress sock_addr;
+ socklen_t sock_addr_len = sock_addr.GetMaxLength();
+ if (::getpeername(m_socket, sock_addr, &sock_addr_len) == 0)
+ return sock_addr.GetPort();
+ }
+ return 0;
+}
+
+std::string TCPSocket::GetRemoteIPAddress() const {
+ // We bound to port zero, so we need to figure out which port we actually
+ // bound to
+ if (m_socket != kInvalidSocketValue) {
+ SocketAddress sock_addr;
+ socklen_t sock_addr_len = sock_addr.GetMaxLength();
+ if (::getpeername(m_socket, sock_addr, &sock_addr_len) == 0)
+ return sock_addr.GetIPAddress();
+ }
+ return "";
+}
+
+std::string TCPSocket::GetRemoteConnectionURI() const {
+ if (m_socket != kInvalidSocketValue) {
+ return llvm::formatv("connect://[{0}]:{1}", GetRemoteIPAddress(),
+ GetRemotePortNumber());
+ }
+ return "";
+}
+
+Status TCPSocket::CreateSocket(int domain) {
+ Status error;
+ if (IsValid())
+ error = Close();
+ if (error.Fail())
+ return error;
+ m_socket = Socket::CreateSocket(domain, kType, IPPROTO_TCP,
+ m_child_processes_inherit, error);
+ return error;
+}
+
+Status TCPSocket::Connect(llvm::StringRef name) {
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_COMMUNICATION));
+ if (log)
+ log->Printf("TCPSocket::%s (host/port = %s)", __FUNCTION__, name.data());
+
+ Status error;
+ std::string host_str;
+ std::string port_str;
+ int32_t port = INT32_MIN;
+ if (!DecodeHostAndPort(name, host_str, port_str, port, &error))
+ return error;
+
+ auto addresses = lldb_private::SocketAddress::GetAddressInfo(
+ host_str.c_str(), nullptr, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
+ for (auto address : addresses) {
+ error = CreateSocket(address.GetFamily());
+ if (error.Fail())
+ continue;
+
+ address.SetPort(port);
+
+ if (-1 == llvm::sys::RetryAfterSignal(-1, ::connect,
+ GetNativeSocket(), &address.sockaddr(), address.GetLength())) {
+ CLOSE_SOCKET(GetNativeSocket());
+ continue;
+ }
+
+ SetOptionNoDelay();
+
+ error.Clear();
+ return error;
+ }
+
+ error.SetErrorString("Failed to connect port");
+ return error;
+}
+
+Status TCPSocket::Listen(llvm::StringRef name, int backlog) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("TCPSocket::%s (%s)", __FUNCTION__, name.data());
+
+ Status error;
+ std::string host_str;
+ std::string port_str;
+ int32_t port = INT32_MIN;
+ if (!DecodeHostAndPort(name, host_str, port_str, port, &error))
+ return error;
+
+ if (host_str == "*")
+ host_str = "0.0.0.0";
+ auto addresses = lldb_private::SocketAddress::GetAddressInfo(
+ host_str.c_str(), nullptr, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
+ for (auto address : addresses) {
+ int fd = Socket::CreateSocket(address.GetFamily(), kType, IPPROTO_TCP,
+ m_child_processes_inherit, error);
+ if (error.Fail()) {
+ error.Clear();
+ continue;
+ }
+
+ // enable local address reuse
+ int option_value = 1;
+ set_socket_option_arg_type option_value_p =
+ reinterpret_cast<set_socket_option_arg_type>(&option_value);
+ ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, option_value_p,
+ sizeof(option_value));
+
+ SocketAddress listen_address = address;
+ if(!listen_address.IsLocalhost())
+ listen_address.SetToAnyAddress(address.GetFamily(), port);
+ else
+ listen_address.SetPort(port);
+
+ int err =
+ ::bind(fd, &listen_address.sockaddr(), listen_address.GetLength());
+ if (-1 != err)
+ err = ::listen(fd, backlog);
+
+ if (-1 == err) {
+ CLOSE_SOCKET(fd);
+ continue;
+ }
+
+ if (port == 0) {
+ socklen_t sa_len = address.GetLength();
+ if (getsockname(fd, &address.sockaddr(), &sa_len) == 0)
+ port = address.GetPort();
+ }
+ m_listen_sockets[fd] = address;
+ }
+
+ if (m_listen_sockets.size() == 0)
+ error.SetErrorString("Failed to connect port");
+ return error;
+}
+
+void TCPSocket::CloseListenSockets() {
+ for (auto socket : m_listen_sockets)
+ CLOSE_SOCKET(socket.first);
+ m_listen_sockets.clear();
+}
+
+Status TCPSocket::Accept(Socket *&conn_socket) {
+ Status error;
+ if (m_listen_sockets.size() == 0) {
+ error.SetErrorString("No open listening sockets!");
+ return error;
+ }
+
+ int sock = -1;
+ int listen_sock = -1;
+ lldb_private::SocketAddress AcceptAddr;
+ MainLoop accept_loop;
+ std::vector<MainLoopBase::ReadHandleUP> handles;
+ for (auto socket : m_listen_sockets) {
+ auto fd = socket.first;
+ auto inherit = this->m_child_processes_inherit;
+ auto io_sp = IOObjectSP(new TCPSocket(socket.first, false, inherit));
+ handles.emplace_back(accept_loop.RegisterReadObject(
+ io_sp, [fd, inherit, &sock, &AcceptAddr, &error,
+ &listen_sock](MainLoopBase &loop) {
+ socklen_t sa_len = AcceptAddr.GetMaxLength();
+ sock = AcceptSocket(fd, &AcceptAddr.sockaddr(), &sa_len, inherit,
+ error);
+ listen_sock = fd;
+ loop.RequestTermination();
+ }, error));
+ if (error.Fail())
+ return error;
+ }
+
+ bool accept_connection = false;
+ std::unique_ptr<TCPSocket> accepted_socket;
+ // Loop until we are happy with our connection
+ while (!accept_connection) {
+ accept_loop.Run();
+
+ if (error.Fail())
+ return error;
+
+ lldb_private::SocketAddress &AddrIn = m_listen_sockets[listen_sock];
+ if (!AddrIn.IsAnyAddr() && AcceptAddr != AddrIn) {
+ CLOSE_SOCKET(sock);
+ llvm::errs() << llvm::formatv(
+ "error: rejecting incoming connection from {0} (expecting {1})",
+ AcceptAddr.GetIPAddress(), AddrIn.GetIPAddress());
+ continue;
+ }
+ accept_connection = true;
+ accepted_socket.reset(new TCPSocket(sock, *this));
+ }
+
+ if (!accepted_socket)
+ return error;
+
+ // Keep our TCP packets coming without any delays.
+ accepted_socket->SetOptionNoDelay();
+ error.Clear();
+ conn_socket = accepted_socket.release();
+ return error;
+}
+
+int TCPSocket::SetOptionNoDelay() {
+ return SetOption(IPPROTO_TCP, TCP_NODELAY, 1);
+}
+
+int TCPSocket::SetOptionReuseAddress() {
+ return SetOption(SOL_SOCKET, SO_REUSEADDR, 1);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/TaskPool.cpp b/contrib/llvm-project/lldb/source/Host/common/TaskPool.cpp
new file mode 100644
index 000000000000..73f761b5cf63
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/TaskPool.cpp
@@ -0,0 +1,126 @@
+//===--------------------- TaskPool.cpp -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/TaskPool.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Utility/Log.h"
+
+#include <cstdint>
+#include <queue>
+#include <thread>
+
+namespace lldb_private {
+
+namespace {
+class TaskPoolImpl {
+public:
+ static TaskPoolImpl &GetInstance();
+
+ void AddTask(std::function<void()> &&task_fn);
+
+private:
+ TaskPoolImpl();
+
+ static lldb::thread_result_t WorkerPtr(void *pool);
+
+ static void Worker(TaskPoolImpl *pool);
+
+ std::queue<std::function<void()>> m_tasks;
+ std::mutex m_tasks_mutex;
+ uint32_t m_thread_count;
+};
+
+} // end of anonymous namespace
+
+TaskPoolImpl &TaskPoolImpl::GetInstance() {
+ static TaskPoolImpl g_task_pool_impl;
+ return g_task_pool_impl;
+}
+
+void TaskPool::AddTaskImpl(std::function<void()> &&task_fn) {
+ TaskPoolImpl::GetInstance().AddTask(std::move(task_fn));
+}
+
+TaskPoolImpl::TaskPoolImpl() : m_thread_count(0) {}
+
+unsigned GetHardwareConcurrencyHint() {
+ // std::thread::hardware_concurrency may return 0 if the value is not well
+ // defined or not computable.
+ static const unsigned g_hardware_concurrency =
+ std::max(1u, std::thread::hardware_concurrency());
+ return g_hardware_concurrency;
+}
+
+void TaskPoolImpl::AddTask(std::function<void()> &&task_fn) {
+ const size_t min_stack_size = 8 * 1024 * 1024;
+
+ std::unique_lock<std::mutex> lock(m_tasks_mutex);
+ m_tasks.emplace(std::move(task_fn));
+ if (m_thread_count < GetHardwareConcurrencyHint()) {
+ m_thread_count++;
+ // Note that this detach call needs to happen with the m_tasks_mutex held.
+ // This prevents the thread from exiting prematurely and triggering a linux
+ // libc bug (https://sourceware.org/bugzilla/show_bug.cgi?id=19951).
+ llvm::Expected<HostThread> host_thread =
+ lldb_private::ThreadLauncher::LaunchThread(
+ "task-pool.worker", WorkerPtr, this, min_stack_size);
+ if (host_thread) {
+ host_thread->Release();
+ } else {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(host_thread.takeError()));
+ }
+ }
+}
+
+lldb::thread_result_t TaskPoolImpl::WorkerPtr(void *pool) {
+ Worker((TaskPoolImpl *)pool);
+ return {};
+}
+
+void TaskPoolImpl::Worker(TaskPoolImpl *pool) {
+ while (true) {
+ std::unique_lock<std::mutex> lock(pool->m_tasks_mutex);
+ if (pool->m_tasks.empty()) {
+ pool->m_thread_count--;
+ break;
+ }
+
+ std::function<void()> f = std::move(pool->m_tasks.front());
+ pool->m_tasks.pop();
+ lock.unlock();
+
+ f();
+ }
+}
+
+void TaskMapOverInt(size_t begin, size_t end,
+ const llvm::function_ref<void(size_t)> &func) {
+ const size_t num_workers = std::min<size_t>(end, GetHardwareConcurrencyHint());
+ std::atomic<size_t> idx{begin};
+
+ auto wrapper = [&idx, end, &func]() {
+ while (true) {
+ size_t i = idx.fetch_add(1);
+ if (i >= end)
+ break;
+ func(i);
+ }
+ };
+
+ std::vector<std::future<void>> futures;
+ futures.reserve(num_workers);
+ for (size_t i = 0; i < num_workers; i++)
+ futures.push_back(TaskPool::AddTask(wrapper));
+ for (size_t i = 0; i < num_workers; i++)
+ futures[i].wait();
+}
+
+} // namespace lldb_private
+
diff --git a/contrib/llvm-project/lldb/source/Host/common/Terminal.cpp b/contrib/llvm-project/lldb/source/Host/common/Terminal.cpp
new file mode 100644
index 000000000000..4b536b03d852
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/Terminal.cpp
@@ -0,0 +1,236 @@
+//===-- Terminal.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Terminal.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/PosixApi.h"
+#include "llvm/ADT/STLExtras.h"
+
+#include <fcntl.h>
+#include <signal.h>
+
+#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+#include <termios.h>
+#endif
+
+using namespace lldb_private;
+
+bool Terminal::IsATerminal() const { return m_fd >= 0 && ::isatty(m_fd); }
+
+bool Terminal::SetEcho(bool enabled) {
+ if (FileDescriptorIsValid()) {
+#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+ if (IsATerminal()) {
+ struct termios fd_termios;
+ if (::tcgetattr(m_fd, &fd_termios) == 0) {
+ bool set_corectly = false;
+ if (enabled) {
+ if (fd_termios.c_lflag & ECHO)
+ set_corectly = true;
+ else
+ fd_termios.c_lflag |= ECHO;
+ } else {
+ if (fd_termios.c_lflag & ECHO)
+ fd_termios.c_lflag &= ~ECHO;
+ else
+ set_corectly = true;
+ }
+
+ if (set_corectly)
+ return true;
+ return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0;
+ }
+ }
+#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+ }
+ return false;
+}
+
+bool Terminal::SetCanonical(bool enabled) {
+ if (FileDescriptorIsValid()) {
+#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+ if (IsATerminal()) {
+ struct termios fd_termios;
+ if (::tcgetattr(m_fd, &fd_termios) == 0) {
+ bool set_corectly = false;
+ if (enabled) {
+ if (fd_termios.c_lflag & ICANON)
+ set_corectly = true;
+ else
+ fd_termios.c_lflag |= ICANON;
+ } else {
+ if (fd_termios.c_lflag & ICANON)
+ fd_termios.c_lflag &= ~ICANON;
+ else
+ set_corectly = true;
+ }
+
+ if (set_corectly)
+ return true;
+ return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0;
+ }
+ }
+#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+ }
+ return false;
+}
+
+// Default constructor
+TerminalState::TerminalState()
+ : m_tty(), m_tflags(-1),
+#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+ m_termios_up(),
+#endif
+ m_process_group(-1) {
+}
+
+// Destructor
+TerminalState::~TerminalState() {}
+
+void TerminalState::Clear() {
+ m_tty.Clear();
+ m_tflags = -1;
+#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+ m_termios_up.reset();
+#endif
+ m_process_group = -1;
+}
+
+// Save the current state of the TTY for the file descriptor "fd" and if
+// "save_process_group" is true, attempt to save the process group info for the
+// TTY.
+bool TerminalState::Save(int fd, bool save_process_group) {
+ m_tty.SetFileDescriptor(fd);
+ if (m_tty.IsATerminal()) {
+#ifndef LLDB_DISABLE_POSIX
+ m_tflags = ::fcntl(fd, F_GETFL, 0);
+#endif
+#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+ if (m_termios_up == nullptr)
+ m_termios_up.reset(new struct termios);
+ int err = ::tcgetattr(fd, m_termios_up.get());
+ if (err != 0)
+ m_termios_up.reset();
+#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+#ifndef LLDB_DISABLE_POSIX
+ if (save_process_group)
+ m_process_group = ::tcgetpgrp(0);
+ else
+ m_process_group = -1;
+#endif
+ } else {
+ m_tty.Clear();
+ m_tflags = -1;
+#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+ m_termios_up.reset();
+#endif
+ m_process_group = -1;
+ }
+ return IsValid();
+}
+
+// Restore the state of the TTY using the cached values from a previous call to
+// Save().
+bool TerminalState::Restore() const {
+#ifndef LLDB_DISABLE_POSIX
+ if (IsValid()) {
+ const int fd = m_tty.GetFileDescriptor();
+ if (TFlagsIsValid())
+ fcntl(fd, F_SETFL, m_tflags);
+
+#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+ if (TTYStateIsValid())
+ tcsetattr(fd, TCSANOW, m_termios_up.get());
+#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+
+ if (ProcessGroupIsValid()) {
+ // Save the original signal handler.
+ void (*saved_sigttou_callback)(int) = nullptr;
+ saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN);
+ // Set the process group
+ tcsetpgrp(fd, m_process_group);
+ // Restore the original signal handler.
+ signal(SIGTTOU, saved_sigttou_callback);
+ }
+ return true;
+ }
+#endif
+ return false;
+}
+
+// Returns true if this object has valid saved TTY state settings that can be
+// used to restore a previous state.
+bool TerminalState::IsValid() const {
+ return m_tty.FileDescriptorIsValid() &&
+ (TFlagsIsValid() || TTYStateIsValid());
+}
+
+// Returns true if m_tflags is valid
+bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; }
+
+// Returns true if m_ttystate is valid
+bool TerminalState::TTYStateIsValid() const {
+#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
+ return m_termios_up != nullptr;
+#else
+ return false;
+#endif
+}
+
+// Returns true if m_process_group is valid
+bool TerminalState::ProcessGroupIsValid() const {
+ return static_cast<int32_t>(m_process_group) != -1;
+}
+
+// Constructor
+TerminalStateSwitcher::TerminalStateSwitcher() : m_currentState(UINT32_MAX) {}
+
+// Destructor
+TerminalStateSwitcher::~TerminalStateSwitcher() {}
+
+// Returns the number of states that this switcher contains
+uint32_t TerminalStateSwitcher::GetNumberOfStates() const {
+ return llvm::array_lengthof(m_ttystates);
+}
+
+// Restore the state at index "idx".
+//
+// Returns true if the restore was successful, false otherwise.
+bool TerminalStateSwitcher::Restore(uint32_t idx) const {
+ const uint32_t num_states = GetNumberOfStates();
+ if (idx >= num_states)
+ return false;
+
+ // See if we already are in this state?
+ if (m_currentState < num_states && (idx == m_currentState) &&
+ m_ttystates[idx].IsValid())
+ return true;
+
+ // Set the state to match the index passed in and only update the current
+ // state if there are no errors.
+ if (m_ttystates[idx].Restore()) {
+ m_currentState = idx;
+ return true;
+ }
+
+ // We failed to set the state. The tty state was invalid or not initialized.
+ return false;
+}
+
+// Save the state at index "idx" for file descriptor "fd" and save the process
+// group if requested.
+//
+// Returns true if the restore was successful, false otherwise.
+bool TerminalStateSwitcher::Save(uint32_t idx, int fd,
+ bool save_process_group) {
+ const uint32_t num_states = GetNumberOfStates();
+ if (idx < num_states)
+ return m_ttystates[idx].Save(fd, save_process_group);
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/ThreadLauncher.cpp b/contrib/llvm-project/lldb/source/Host/common/ThreadLauncher.cpp
new file mode 100644
index 000000000000..6e3c8b6a13a4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/ThreadLauncher.cpp
@@ -0,0 +1,77 @@
+//===-- ThreadLauncher.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// lldb Includes
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Host/HostNativeThread.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Utility/Log.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/windows.h"
+#endif
+
+#include "llvm/Support/WindowsError.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+llvm::Expected<HostThread> ThreadLauncher::LaunchThread(
+ llvm::StringRef name, lldb::thread_func_t thread_function,
+ lldb::thread_arg_t thread_arg, size_t min_stack_byte_size) {
+ // Host::ThreadCreateTrampoline will delete this pointer for us.
+ HostThreadCreateInfo *info_ptr =
+ new HostThreadCreateInfo(name.data(), thread_function, thread_arg);
+ lldb::thread_t thread;
+#ifdef _WIN32
+ thread = (lldb::thread_t)::_beginthreadex(
+ 0, (unsigned)min_stack_byte_size,
+ HostNativeThread::ThreadCreateTrampoline, info_ptr, 0, NULL);
+ if (thread == LLDB_INVALID_HOST_THREAD)
+ return llvm::errorCodeToError(llvm::mapWindowsError(GetLastError()));
+#else
+
+// ASAN instrumentation adds a lot of bookkeeping overhead on stack frames.
+#if __has_feature(address_sanitizer)
+ const size_t eight_megabytes = 8 * 1024 * 1024;
+ if (min_stack_byte_size < eight_megabytes) {
+ min_stack_byte_size += eight_megabytes;
+ }
+#endif
+
+ pthread_attr_t *thread_attr_ptr = nullptr;
+ pthread_attr_t thread_attr;
+ bool destroy_attr = false;
+ if (min_stack_byte_size > 0) {
+ if (::pthread_attr_init(&thread_attr) == 0) {
+ destroy_attr = true;
+ size_t default_min_stack_byte_size = 0;
+ if (::pthread_attr_getstacksize(&thread_attr,
+ &default_min_stack_byte_size) == 0) {
+ if (default_min_stack_byte_size < min_stack_byte_size) {
+ if (::pthread_attr_setstacksize(&thread_attr, min_stack_byte_size) ==
+ 0)
+ thread_attr_ptr = &thread_attr;
+ }
+ }
+ }
+ }
+ int err =
+ ::pthread_create(&thread, thread_attr_ptr,
+ HostNativeThread::ThreadCreateTrampoline, info_ptr);
+
+ if (destroy_attr)
+ ::pthread_attr_destroy(&thread_attr);
+
+ if (err)
+ return llvm::errorCodeToError(
+ std::error_code(err, std::generic_category()));
+#endif
+
+ return HostThread(thread);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/UDPSocket.cpp b/contrib/llvm-project/lldb/source/Host/common/UDPSocket.cpp
new file mode 100644
index 000000000000..8dbf57d6fe4e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/UDPSocket.cpp
@@ -0,0 +1,144 @@
+//===-- UDPSocket.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/common/UDPSocket.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Utility/Log.h"
+
+#ifndef LLDB_DISABLE_POSIX
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#endif
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+const int kDomain = AF_INET;
+const int kType = SOCK_DGRAM;
+
+static const char *g_not_supported_error = "Not supported";
+}
+
+UDPSocket::UDPSocket(NativeSocket socket) : Socket(ProtocolUdp, true, true) {
+ m_socket = socket;
+}
+
+UDPSocket::UDPSocket(bool should_close, bool child_processes_inherit)
+ : Socket(ProtocolUdp, should_close, child_processes_inherit) {}
+
+size_t UDPSocket::Send(const void *buf, const size_t num_bytes) {
+ return ::sendto(m_socket, static_cast<const char *>(buf), num_bytes, 0,
+ m_sockaddr, m_sockaddr.GetLength());
+}
+
+Status UDPSocket::Connect(llvm::StringRef name) {
+ return Status("%s", g_not_supported_error);
+}
+
+Status UDPSocket::Listen(llvm::StringRef name, int backlog) {
+ return Status("%s", g_not_supported_error);
+}
+
+Status UDPSocket::Accept(Socket *&socket) {
+ return Status("%s", g_not_supported_error);
+}
+
+Status UDPSocket::Connect(llvm::StringRef name, bool child_processes_inherit,
+ Socket *&socket) {
+ std::unique_ptr<UDPSocket> final_socket;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("UDPSocket::%s (host/port = %s)", __FUNCTION__, name.data());
+
+ Status error;
+ std::string host_str;
+ std::string port_str;
+ int32_t port = INT32_MIN;
+ if (!DecodeHostAndPort(name, host_str, port_str, port, &error))
+ return error;
+
+ // At this point we have setup the receive port, now we need to setup the UDP
+ // send socket
+
+ struct addrinfo hints;
+ struct addrinfo *service_info_list = nullptr;
+
+ ::memset(&hints, 0, sizeof(hints));
+ hints.ai_family = kDomain;
+ hints.ai_socktype = kType;
+ int err = ::getaddrinfo(host_str.c_str(), port_str.c_str(), &hints,
+ &service_info_list);
+ if (err != 0) {
+ error.SetErrorStringWithFormat(
+#if defined(_MSC_VER) && defined(UNICODE)
+ "getaddrinfo(%s, %s, &hints, &info) returned error %i (%S)",
+#else
+ "getaddrinfo(%s, %s, &hints, &info) returned error %i (%s)",
+#endif
+ host_str.c_str(), port_str.c_str(), err, gai_strerror(err));
+ return error;
+ }
+
+ for (struct addrinfo *service_info_ptr = service_info_list;
+ service_info_ptr != nullptr;
+ service_info_ptr = service_info_ptr->ai_next) {
+ auto send_fd = CreateSocket(
+ service_info_ptr->ai_family, service_info_ptr->ai_socktype,
+ service_info_ptr->ai_protocol, child_processes_inherit, error);
+ if (error.Success()) {
+ final_socket.reset(new UDPSocket(send_fd));
+ final_socket->m_sockaddr = service_info_ptr;
+ break;
+ } else
+ continue;
+ }
+
+ ::freeaddrinfo(service_info_list);
+
+ if (!final_socket)
+ return error;
+
+ SocketAddress bind_addr;
+
+ // Only bind to the loopback address if we are expecting a connection from
+ // localhost to avoid any firewall issues.
+ const bool bind_addr_success = (host_str == "127.0.0.1" || host_str == "localhost")
+ ? bind_addr.SetToLocalhost(kDomain, port)
+ : bind_addr.SetToAnyAddress(kDomain, port);
+
+ if (!bind_addr_success) {
+ error.SetErrorString("Failed to get hostspec to bind for");
+ return error;
+ }
+
+ bind_addr.SetPort(0); // Let the source port # be determined dynamically
+
+ err = ::bind(final_socket->GetNativeSocket(), bind_addr, bind_addr.GetLength());
+
+ struct sockaddr_in source_info;
+ socklen_t address_len = sizeof (struct sockaddr_in);
+ err = ::getsockname(final_socket->GetNativeSocket(), (struct sockaddr *) &source_info, &address_len);
+
+ socket = final_socket.release();
+ error.Clear();
+ return error;
+}
+
+std::string UDPSocket::GetRemoteConnectionURI() const {
+ if (m_socket != kInvalidSocketValue) {
+ return llvm::formatv("udp://[{0}]:{1}", m_sockaddr.GetIPAddress(),
+ m_sockaddr.GetPort());
+ }
+ return "";
+}
diff --git a/contrib/llvm-project/lldb/source/Host/common/XML.cpp b/contrib/llvm-project/lldb/source/Host/common/XML.cpp
new file mode 100644
index 000000000000..cb23ac17ef53
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/XML.cpp
@@ -0,0 +1,541 @@
+//===-- XML.cpp -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdlib.h> /* atof */
+
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Host/XML.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark-- XMLDocument
+
+XMLDocument::XMLDocument() : m_document(nullptr) {}
+
+XMLDocument::~XMLDocument() { Clear(); }
+
+void XMLDocument::Clear() {
+#if defined(LIBXML2_DEFINED)
+ if (m_document) {
+ xmlDocPtr doc = m_document;
+ m_document = nullptr;
+ xmlFreeDoc(doc);
+ }
+#endif
+}
+
+bool XMLDocument::IsValid() const { return m_document != nullptr; }
+
+void XMLDocument::ErrorCallback(void *ctx, const char *format, ...) {
+ XMLDocument *document = (XMLDocument *)ctx;
+ va_list args;
+ va_start(args, format);
+ document->m_errors.PrintfVarArg(format, args);
+ document->m_errors.EOL();
+ va_end(args);
+}
+
+bool XMLDocument::ParseFile(const char *path) {
+#if defined(LIBXML2_DEFINED)
+ Clear();
+ xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
+ m_document = xmlParseFile(path);
+ xmlSetGenericErrorFunc(nullptr, nullptr);
+#endif
+ return IsValid();
+}
+
+bool XMLDocument::ParseMemory(const char *xml, size_t xml_length,
+ const char *url) {
+#if defined(LIBXML2_DEFINED)
+ Clear();
+ xmlSetGenericErrorFunc((void *)this, XMLDocument::ErrorCallback);
+ m_document = xmlReadMemory(xml, (int)xml_length, url, nullptr, 0);
+ xmlSetGenericErrorFunc(nullptr, nullptr);
+#endif
+ return IsValid();
+}
+
+XMLNode XMLDocument::GetRootElement(const char *required_name) {
+#if defined(LIBXML2_DEFINED)
+ if (IsValid()) {
+ XMLNode root_node(xmlDocGetRootElement(m_document));
+ if (required_name) {
+ llvm::StringRef actual_name = root_node.GetName();
+ if (actual_name == required_name)
+ return root_node;
+ } else {
+ return root_node;
+ }
+ }
+#endif
+ return XMLNode();
+}
+
+llvm::StringRef XMLDocument::GetErrors() const { return m_errors.GetString(); }
+
+bool XMLDocument::XMLEnabled() {
+#if defined(LIBXML2_DEFINED)
+ return true;
+#else
+ return false;
+#endif
+}
+
+#pragma mark-- XMLNode
+
+XMLNode::XMLNode() : m_node(nullptr) {}
+
+XMLNode::XMLNode(XMLNodeImpl node) : m_node(node) {}
+
+XMLNode::~XMLNode() {}
+
+void XMLNode::Clear() { m_node = nullptr; }
+
+XMLNode XMLNode::GetParent() const {
+#if defined(LIBXML2_DEFINED)
+ if (IsValid())
+ return XMLNode(m_node->parent);
+ else
+ return XMLNode();
+#else
+ return XMLNode();
+#endif
+}
+
+XMLNode XMLNode::GetSibling() const {
+#if defined(LIBXML2_DEFINED)
+ if (IsValid())
+ return XMLNode(m_node->next);
+ else
+ return XMLNode();
+#else
+ return XMLNode();
+#endif
+}
+
+XMLNode XMLNode::GetChild() const {
+#if defined(LIBXML2_DEFINED)
+
+ if (IsValid())
+ return XMLNode(m_node->children);
+ else
+ return XMLNode();
+#else
+ return XMLNode();
+#endif
+}
+
+llvm::StringRef XMLNode::GetAttributeValue(const char *name,
+ const char *fail_value) const {
+ const char *attr_value = nullptr;
+#if defined(LIBXML2_DEFINED)
+
+ if (IsValid())
+ attr_value = (const char *)xmlGetProp(m_node, (const xmlChar *)name);
+ else
+ attr_value = fail_value;
+#else
+ attr_value = fail_value;
+#endif
+ if (attr_value)
+ return llvm::StringRef(attr_value);
+ else
+ return llvm::StringRef();
+}
+
+bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value,
+ uint64_t fail_value, int base) const {
+#if defined(LIBXML2_DEFINED)
+ llvm::StringRef str_value = GetAttributeValue(name, "");
+#else
+ llvm::StringRef str_value;
+#endif
+ bool success = false;
+ value = StringConvert::ToUInt64(str_value.data(), fail_value, base, &success);
+ return success;
+}
+
+void XMLNode::ForEachChildNode(NodeCallback const &callback) const {
+#if defined(LIBXML2_DEFINED)
+ if (IsValid())
+ GetChild().ForEachSiblingNode(callback);
+#endif
+}
+
+void XMLNode::ForEachChildElement(NodeCallback const &callback) const {
+#if defined(LIBXML2_DEFINED)
+ XMLNode child = GetChild();
+ if (child)
+ child.ForEachSiblingElement(callback);
+#endif
+}
+
+void XMLNode::ForEachChildElementWithName(const char *name,
+ NodeCallback const &callback) const {
+#if defined(LIBXML2_DEFINED)
+ XMLNode child = GetChild();
+ if (child)
+ child.ForEachSiblingElementWithName(name, callback);
+#endif
+}
+
+void XMLNode::ForEachAttribute(AttributeCallback const &callback) const {
+#if defined(LIBXML2_DEFINED)
+
+ if (IsValid()) {
+ for (xmlAttrPtr attr = m_node->properties; attr != nullptr;
+ attr = attr->next) {
+ // check if name matches
+ if (attr->name) {
+ // check child is a text node
+ xmlNodePtr child = attr->children;
+ if (child->type == XML_TEXT_NODE) {
+ llvm::StringRef attr_value;
+ if (child->content)
+ attr_value = llvm::StringRef((const char *)child->content);
+ if (!callback(llvm::StringRef((const char *)attr->name), attr_value))
+ return;
+ }
+ }
+ }
+ }
+#endif
+}
+
+void XMLNode::ForEachSiblingNode(NodeCallback const &callback) const {
+#if defined(LIBXML2_DEFINED)
+
+ if (IsValid()) {
+ // iterate through all siblings
+ for (xmlNodePtr node = m_node; node; node = node->next) {
+ if (!callback(XMLNode(node)))
+ return;
+ }
+ }
+#endif
+}
+
+void XMLNode::ForEachSiblingElement(NodeCallback const &callback) const {
+#if defined(LIBXML2_DEFINED)
+
+ if (IsValid()) {
+ // iterate through all siblings
+ for (xmlNodePtr node = m_node; node; node = node->next) {
+ // we are looking for element nodes only
+ if (node->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (!callback(XMLNode(node)))
+ return;
+ }
+ }
+#endif
+}
+
+void XMLNode::ForEachSiblingElementWithName(
+ const char *name, NodeCallback const &callback) const {
+#if defined(LIBXML2_DEFINED)
+
+ if (IsValid()) {
+ // iterate through all siblings
+ for (xmlNodePtr node = m_node; node; node = node->next) {
+ // we are looking for element nodes only
+ if (node->type != XML_ELEMENT_NODE)
+ continue;
+
+ // If name is nullptr, we take all nodes of type "t", else just the ones
+ // whose name matches
+ if (name) {
+ if (strcmp((const char *)node->name, name) != 0)
+ continue; // Name mismatch, ignore this one
+ } else {
+ if (node->name)
+ continue; // nullptr name specified and this element has a name,
+ // ignore this one
+ }
+
+ if (!callback(XMLNode(node)))
+ return;
+ }
+ }
+#endif
+}
+
+llvm::StringRef XMLNode::GetName() const {
+#if defined(LIBXML2_DEFINED)
+ if (IsValid()) {
+ if (m_node->name)
+ return llvm::StringRef((const char *)m_node->name);
+ }
+#endif
+ return llvm::StringRef();
+}
+
+bool XMLNode::GetElementText(std::string &text) const {
+ text.clear();
+#if defined(LIBXML2_DEFINED)
+ if (IsValid()) {
+ bool success = false;
+ if (m_node->type == XML_ELEMENT_NODE) {
+ // check child is a text node
+ for (xmlNodePtr node = m_node->children; node != nullptr;
+ node = node->next) {
+ if (node->type == XML_TEXT_NODE) {
+ text.append((const char *)node->content);
+ success = true;
+ }
+ }
+ }
+ return success;
+ }
+#endif
+ return false;
+}
+
+bool XMLNode::GetElementTextAsUnsigned(uint64_t &value, uint64_t fail_value,
+ int base) const {
+ bool success = false;
+#if defined(LIBXML2_DEFINED)
+ if (IsValid()) {
+ std::string text;
+ if (GetElementText(text))
+ value = StringConvert::ToUInt64(text.c_str(), fail_value, base, &success);
+ }
+#endif
+ if (!success)
+ value = fail_value;
+ return success;
+}
+
+bool XMLNode::GetElementTextAsFloat(double &value, double fail_value) const {
+ bool success = false;
+#if defined(LIBXML2_DEFINED)
+ if (IsValid()) {
+ std::string text;
+ if (GetElementText(text)) {
+ value = atof(text.c_str());
+ success = true;
+ }
+ }
+#endif
+ if (!success)
+ value = fail_value;
+ return success;
+}
+
+bool XMLNode::NameIs(const char *name) const {
+#if defined(LIBXML2_DEFINED)
+
+ if (IsValid()) {
+ // In case we are looking for a nullptr name or an exact pointer match
+ if (m_node->name == (const xmlChar *)name)
+ return true;
+ if (m_node->name)
+ return strcmp((const char *)m_node->name, name) == 0;
+ }
+#endif
+ return false;
+}
+
+XMLNode XMLNode::FindFirstChildElementWithName(const char *name) const {
+ XMLNode result_node;
+
+#if defined(LIBXML2_DEFINED)
+ ForEachChildElementWithName(
+ name, [&result_node](const XMLNode &node) -> bool {
+ result_node = node;
+ // Stop iterating, we found the node we wanted
+ return false;
+ });
+#endif
+
+ return result_node;
+}
+
+bool XMLNode::IsValid() const { return m_node != nullptr; }
+
+bool XMLNode::IsElement() const {
+#if defined(LIBXML2_DEFINED)
+ if (IsValid())
+ return m_node->type == XML_ELEMENT_NODE;
+#endif
+ return false;
+}
+
+XMLNode XMLNode::GetElementForPath(const NamePath &path) {
+#if defined(LIBXML2_DEFINED)
+
+ if (IsValid()) {
+ if (path.empty())
+ return *this;
+ else {
+ XMLNode node = FindFirstChildElementWithName(path[0].c_str());
+ const size_t n = path.size();
+ for (size_t i = 1; node && i < n; ++i)
+ node = node.FindFirstChildElementWithName(path[i].c_str());
+ return node;
+ }
+ }
+#endif
+
+ return XMLNode();
+}
+
+#pragma mark-- ApplePropertyList
+
+ApplePropertyList::ApplePropertyList() : m_xml_doc(), m_dict_node() {}
+
+ApplePropertyList::ApplePropertyList(const char *path)
+ : m_xml_doc(), m_dict_node() {
+ ParseFile(path);
+}
+
+ApplePropertyList::~ApplePropertyList() {}
+
+llvm::StringRef ApplePropertyList::GetErrors() const {
+ return m_xml_doc.GetErrors();
+}
+
+bool ApplePropertyList::ParseFile(const char *path) {
+ if (m_xml_doc.ParseFile(path)) {
+ XMLNode plist = m_xml_doc.GetRootElement("plist");
+ if (plist) {
+ plist.ForEachChildElementWithName("dict",
+ [this](const XMLNode &dict) -> bool {
+ this->m_dict_node = dict;
+ return false; // Stop iterating
+ });
+ return (bool)m_dict_node;
+ }
+ }
+ return false;
+}
+
+bool ApplePropertyList::IsValid() const { return (bool)m_dict_node; }
+
+bool ApplePropertyList::GetValueAsString(const char *key,
+ std::string &value) const {
+ XMLNode value_node = GetValueNode(key);
+ if (value_node)
+ return ApplePropertyList::ExtractStringFromValueNode(value_node, value);
+ return false;
+}
+
+XMLNode ApplePropertyList::GetValueNode(const char *key) const {
+ XMLNode value_node;
+#if defined(LIBXML2_DEFINED)
+
+ if (IsValid()) {
+ m_dict_node.ForEachChildElementWithName(
+ "key", [key, &value_node](const XMLNode &key_node) -> bool {
+ std::string key_name;
+ if (key_node.GetElementText(key_name)) {
+ if (key_name == key) {
+ value_node = key_node.GetSibling();
+ while (value_node && !value_node.IsElement())
+ value_node = value_node.GetSibling();
+ return false; // Stop iterating
+ }
+ }
+ return true; // Keep iterating
+ });
+ }
+#endif
+ return value_node;
+}
+
+bool ApplePropertyList::ExtractStringFromValueNode(const XMLNode &node,
+ std::string &value) {
+ value.clear();
+#if defined(LIBXML2_DEFINED)
+ if (node.IsValid()) {
+ llvm::StringRef element_name = node.GetName();
+ if (element_name == "true" || element_name == "false") {
+ // The text value _is_ the element name itself...
+ value = element_name.str();
+ return true;
+ } else if (element_name == "dict" || element_name == "array")
+ return false; // dictionaries and arrays have no text value, so we fail
+ else
+ return node.GetElementText(value);
+ }
+#endif
+ return false;
+}
+
+#if defined(LIBXML2_DEFINED)
+
+namespace {
+
+StructuredData::ObjectSP CreatePlistValue(XMLNode node) {
+ llvm::StringRef element_name = node.GetName();
+ if (element_name == "array") {
+ std::shared_ptr<StructuredData::Array> array_sp(
+ new StructuredData::Array());
+ node.ForEachChildElement([&array_sp](const XMLNode &node) -> bool {
+ array_sp->AddItem(CreatePlistValue(node));
+ return true; // Keep iterating through all child elements of the array
+ });
+ return array_sp;
+ } else if (element_name == "dict") {
+ XMLNode key_node;
+ std::shared_ptr<StructuredData::Dictionary> dict_sp(
+ new StructuredData::Dictionary());
+ node.ForEachChildElement(
+ [&key_node, &dict_sp](const XMLNode &node) -> bool {
+ if (node.NameIs("key")) {
+ // This is a "key" element node
+ key_node = node;
+ } else {
+ // This is a value node
+ if (key_node) {
+ std::string key_name;
+ key_node.GetElementText(key_name);
+ dict_sp->AddItem(key_name, CreatePlistValue(node));
+ key_node.Clear();
+ }
+ }
+ return true; // Keep iterating through all child elements of the
+ // dictionary
+ });
+ return dict_sp;
+ } else if (element_name == "real") {
+ double value = 0.0;
+ node.GetElementTextAsFloat(value);
+ return StructuredData::ObjectSP(new StructuredData::Float(value));
+ } else if (element_name == "integer") {
+ uint64_t value = 0;
+ node.GetElementTextAsUnsigned(value, 0, 0);
+ return StructuredData::ObjectSP(new StructuredData::Integer(value));
+ } else if ((element_name == "string") || (element_name == "data") ||
+ (element_name == "date")) {
+ std::string text;
+ node.GetElementText(text);
+ return StructuredData::ObjectSP(
+ new StructuredData::String(std::move(text)));
+ } else if (element_name == "true") {
+ return StructuredData::ObjectSP(new StructuredData::Boolean(true));
+ } else if (element_name == "false") {
+ return StructuredData::ObjectSP(new StructuredData::Boolean(false));
+ }
+ return StructuredData::ObjectSP(new StructuredData::Null());
+}
+}
+#endif
+
+StructuredData::ObjectSP ApplePropertyList::GetStructuredData() {
+ StructuredData::ObjectSP root_sp;
+#if defined(LIBXML2_DEFINED)
+ if (IsValid()) {
+ return CreatePlistValue(m_dict_node);
+ }
+#endif
+ return root_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/freebsd/Host.cpp b/contrib/llvm-project/lldb/source/Host/freebsd/Host.cpp
new file mode 100644
index 000000000000..99d728d63bc0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/freebsd/Host.cpp
@@ -0,0 +1,245 @@
+//===-- source/Host/freebsd/Host.cpp ------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <sys/types.h>
+
+#include <sys/exec.h>
+#include <sys/proc.h>
+#include <sys/ptrace.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <machine/elf.h>
+
+#include <dlfcn.h>
+#include <execinfo.h>
+#include <stdio.h>
+
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/NameMatches.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Support/Host.h"
+
+extern "C" {
+extern char **environ;
+}
+
+namespace lldb_private {
+class ProcessLaunchInfo;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+static bool
+GetFreeBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
+ ProcessInstanceInfo &process_info) {
+ if (!process_info.ProcessIDIsValid())
+ return false;
+
+ int pid = process_info.GetProcessID();
+
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, pid};
+
+ char arg_data[8192];
+ size_t arg_data_size = sizeof(arg_data);
+ if (::sysctl(mib, 4, arg_data, &arg_data_size, NULL, 0) != 0)
+ return false;
+
+ DataExtractor data(arg_data, arg_data_size, endian::InlHostByteOrder(),
+ sizeof(void *));
+ lldb::offset_t offset = 0;
+ const char *cstr;
+
+ cstr = data.GetCStr(&offset);
+ if (!cstr)
+ return false;
+
+ // Get pathname for pid. If that fails fall back to argv[0].
+ char pathname[MAXPATHLEN];
+ size_t pathname_len = sizeof(pathname);
+ mib[2] = KERN_PROC_PATHNAME;
+ if (::sysctl(mib, 4, pathname, &pathname_len, NULL, 0) == 0)
+ process_info.GetExecutableFile().SetFile(pathname, FileSpec::Style::native);
+ else
+ process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native);
+
+ if (!(match_info_ptr == NULL ||
+ NameMatches(process_info.GetExecutableFile().GetFilename().GetCString(),
+ match_info_ptr->GetNameMatchType(),
+ match_info_ptr->GetProcessInfo().GetName())))
+ return false;
+
+ Args &proc_args = process_info.GetArguments();
+ while (1) {
+ const uint8_t *p = data.PeekData(offset, 1);
+ while ((p != NULL) && (*p == '\0') && offset < arg_data_size) {
+ ++offset;
+ p = data.PeekData(offset, 1);
+ }
+ if (p == NULL || offset >= arg_data_size)
+ break;
+
+ cstr = data.GetCStr(&offset);
+ if (!cstr)
+ break;
+
+ 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);
+ return true;
+ }
+ process_info.GetArchitecture().Clear();
+ return false;
+}
+
+static bool GetFreeBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
+ struct kinfo_proc proc_kinfo;
+ size_t proc_kinfo_size;
+ const int pid = process_info.GetProcessID();
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+
+ if (!process_info.ProcessIDIsValid())
+ goto error;
+
+ proc_kinfo_size = sizeof(struct kinfo_proc);
+
+ if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) != 0)
+ goto error;
+
+ if (proc_kinfo_size == 0)
+ goto error;
+
+ process_info.SetParentProcessID(proc_kinfo.ki_ppid);
+ process_info.SetUserID(proc_kinfo.ki_ruid);
+ process_info.SetGroupID(proc_kinfo.ki_rgid);
+ process_info.SetEffectiveUserID(proc_kinfo.ki_uid);
+ if (proc_kinfo.ki_ngroups > 0)
+ process_info.SetEffectiveGroupID(proc_kinfo.ki_groups[0]);
+ else
+ process_info.SetEffectiveGroupID(UINT32_MAX);
+ return true;
+
+error:
+ process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);
+ process_info.SetUserID(UINT32_MAX);
+ process_info.SetGroupID(UINT32_MAX);
+ process_info.SetEffectiveUserID(UINT32_MAX);
+ process_info.SetEffectiveGroupID(UINT32_MAX);
+ return false;
+}
+
+uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ const ::pid_t our_pid = ::getpid();
+ const ::uid_t our_uid = ::getuid();
+ std::vector<struct kinfo_proc> kinfos;
+ // Special case, if lldb is being run as root we can attach to anything.
+ bool all_users = match_info.GetMatchAllUsers() || (our_uid == 0);
+
+ int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
+
+ size_t pid_data_size = 0;
+ if (::sysctl(mib, 3, NULL, &pid_data_size, NULL, 0) != 0)
+ return 0;
+
+ // Add a few extra in case a few more show up
+ const size_t estimated_pid_count =
+ (pid_data_size / sizeof(struct kinfo_proc)) + 10;
+
+ kinfos.resize(estimated_pid_count);
+ pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);
+
+ if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, NULL, 0) != 0)
+ return 0;
+
+ const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));
+
+ for (size_t i = 0; i < actual_pid_count; i++) {
+ const struct kinfo_proc &kinfo = kinfos[i];
+
+ /* Make sure the user is acceptable */
+ if (!all_users && kinfo.ki_ruid != our_uid)
+ continue;
+
+ if (kinfo.ki_pid == our_pid || // Skip this process
+ kinfo.ki_pid == 0 || // Skip kernel (kernel pid is 0)
+ kinfo.ki_stat == SZOMB || // Zombies are bad
+ kinfo.ki_flag & P_TRACED || // Being debugged?
+ kinfo.ki_flag & P_WEXIT) // Working on exiting
+ continue;
+
+ // Every thread is a process in FreeBSD, but all the threads of a single
+ // process have the same pid. Do not store the process info in the result
+ // list if a process with given identifier is already registered there.
+ bool already_registered = false;
+ for (uint32_t pi = 0;
+ !already_registered && (const int)kinfo.ki_numthreads > 1 &&
+ pi < (const uint32_t)process_infos.GetSize();
+ pi++)
+ already_registered =
+ (process_infos.GetProcessIDAtIndex(pi) == (uint32_t)kinfo.ki_pid);
+
+ if (already_registered)
+ continue;
+
+ ProcessInstanceInfo process_info;
+ process_info.SetProcessID(kinfo.ki_pid);
+ process_info.SetParentProcessID(kinfo.ki_ppid);
+ process_info.SetUserID(kinfo.ki_ruid);
+ process_info.SetGroupID(kinfo.ki_rgid);
+ process_info.SetEffectiveUserID(kinfo.ki_svuid);
+ process_info.SetEffectiveGroupID(kinfo.ki_svgid);
+
+ // Make sure our info matches before we go fetch the name and cpu type
+ if (match_info.Matches(process_info) &&
+ GetFreeBSDProcessArgs(&match_info, process_info)) {
+ GetFreeBSDProcessCPUType(process_info);
+ if (match_info.Matches(process_info))
+ process_infos.Append(process_info);
+ }
+ }
+
+ return process_infos.GetSize();
+}
+
+bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ process_info.SetProcessID(pid);
+
+ if (GetFreeBSDProcessArgs(NULL, process_info)) {
+ // should use libprocstat instead of going right into sysctl?
+ GetFreeBSDProcessCPUType(process_info);
+ GetFreeBSDProcessUserAndGroup(process_info);
+ return true;
+ }
+
+ process_info.Clear();
+ return false;
+}
+
+Environment Host::GetEnvironment() { return Environment(environ); }
+
+Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
+ return Status("unimplemented");
+}
diff --git a/contrib/llvm-project/lldb/source/Host/freebsd/HostInfoFreeBSD.cpp b/contrib/llvm-project/lldb/source/Host/freebsd/HostInfoFreeBSD.cpp
new file mode 100644
index 000000000000..e28cf4aa420f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/freebsd/HostInfoFreeBSD.cpp
@@ -0,0 +1,76 @@
+//===-- HostInfoFreeBSD.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/freebsd/HostInfoFreeBSD.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+using namespace lldb_private;
+
+llvm::VersionTuple HostInfoFreeBSD::GetOSVersion() {
+ struct utsname un;
+
+ ::memset(&un, 0, sizeof(utsname));
+ if (uname(&un) < 0)
+ return llvm::VersionTuple();
+
+ unsigned major, minor;
+ if (2 == sscanf(un.release, "%u.%u", &major, &minor))
+ return llvm::VersionTuple(major, minor);
+ return llvm::VersionTuple();
+}
+
+bool HostInfoFreeBSD::GetOSBuildString(std::string &s) {
+ int mib[2] = {CTL_KERN, KERN_OSREV};
+ char osrev_str[12];
+ uint32_t osrev = 0;
+ size_t osrev_len = sizeof(osrev);
+
+ if (::sysctl(mib, 2, &osrev, &osrev_len, NULL, 0) == 0) {
+ ::snprintf(osrev_str, sizeof(osrev_str), "%-8.8u", osrev);
+ s.assign(osrev_str);
+ return true;
+ }
+
+ s.clear();
+ return false;
+}
+
+bool HostInfoFreeBSD::GetOSKernelDescription(std::string &s) {
+ struct utsname un;
+
+ ::memset(&un, 0, sizeof(utsname));
+ s.clear();
+
+ if (uname(&un) < 0)
+ return false;
+
+ s.assign(un.version);
+
+ return true;
+}
+
+FileSpec HostInfoFreeBSD::GetProgramFileSpec() {
+ static FileSpec g_program_filespec;
+ if (!g_program_filespec) {
+ int exe_path_mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, getpid()};
+ size_t exe_path_size;
+ if (sysctl(exe_path_mib, 4, NULL, &exe_path_size, NULL, 0) == 0) {
+ char *exe_path = new char[exe_path_size];
+ if (sysctl(exe_path_mib, 4, exe_path, &exe_path_size, NULL, 0) == 0)
+ g_program_filespec.SetFile(exe_path, FileSpec::Style::native);
+ delete[] exe_path;
+ }
+ }
+ return g_program_filespec;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/netbsd/Host.cpp b/contrib/llvm-project/lldb/source/Host/netbsd/Host.cpp
new file mode 100644
index 000000000000..08fec099bf49
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/netbsd/Host.cpp
@@ -0,0 +1,245 @@
+//===-- source/Host/netbsd/Host.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <dlfcn.h>
+#include <execinfo.h>
+#include <stdio.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <limits.h>
+
+#include <elf.h>
+#include <kvm.h>
+#include <sys/exec.h>
+#include <sys/ptrace.h>
+
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/NameMatches.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Support/Host.h"
+
+extern "C" {
+extern char **environ;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace lldb_private {
+class ProcessLaunchInfo;
+}
+
+Environment Host::GetEnvironment() { return Environment(environ); }
+
+static bool GetNetBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
+ ProcessInstanceInfo &process_info) {
+ if (!process_info.ProcessIDIsValid())
+ return false;
+
+ int pid = process_info.GetProcessID();
+
+ int mib[4] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV};
+
+ char arg_data[8192];
+ size_t arg_data_size = sizeof(arg_data);
+ if (::sysctl(mib, 4, arg_data, &arg_data_size, NULL, 0) != 0)
+ return false;
+
+ DataExtractor data(arg_data, arg_data_size, endian::InlHostByteOrder(),
+ sizeof(void *));
+ lldb::offset_t offset = 0;
+ const char *cstr;
+
+ cstr = data.GetCStr(&offset);
+ if (!cstr)
+ return false;
+
+ process_info.GetExecutableFile().SetFile(cstr,
+ FileSpec::Style::native);
+
+ if (!(match_info_ptr == NULL ||
+ NameMatches(process_info.GetExecutableFile().GetFilename().GetCString(),
+ match_info_ptr->GetNameMatchType(),
+ match_info_ptr->GetProcessInfo().GetName())))
+ return false;
+
+ Args &proc_args = process_info.GetArguments();
+ while (1) {
+ const uint8_t *p = data.PeekData(offset, 1);
+ while ((p != NULL) && (*p == '\0') && offset < arg_data_size) {
+ ++offset;
+ p = data.PeekData(offset, 1);
+ }
+ if (p == NULL || offset >= arg_data_size)
+ break;
+
+ cstr = data.GetCStr(&offset);
+ if (!cstr)
+ break;
+
+ proc_args.AppendArgument(llvm::StringRef(cstr));
+ }
+
+ return true;
+}
+
+static bool GetNetBSDProcessCPUType(ProcessInstanceInfo &process_info) {
+ if (process_info.ProcessIDIsValid()) {
+ process_info.GetArchitecture() =
+ HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
+ return true;
+ }
+ process_info.GetArchitecture().Clear();
+ return false;
+}
+
+static bool GetNetBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
+ ::kvm_t *kdp;
+ char errbuf[_POSIX2_LINE_MAX]; /* XXX: error string unused */
+
+ struct ::kinfo_proc2 *proc_kinfo;
+ const int pid = process_info.GetProcessID();
+ int nproc;
+
+ if (!process_info.ProcessIDIsValid())
+ goto error;
+
+ if ((kdp = ::kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL)
+ goto error;
+
+ if ((proc_kinfo = ::kvm_getproc2(kdp, KERN_PROC_PID, pid,
+ sizeof(struct ::kinfo_proc2), &nproc)) ==
+ NULL) {
+ ::kvm_close(kdp);
+ goto error;
+ }
+
+ if (nproc < 1) {
+ ::kvm_close(kdp); /* XXX: we don't check for error here */
+ goto error;
+ }
+
+ process_info.SetParentProcessID(proc_kinfo->p_ppid);
+ process_info.SetUserID(proc_kinfo->p_ruid);
+ process_info.SetGroupID(proc_kinfo->p_rgid);
+ process_info.SetEffectiveUserID(proc_kinfo->p_uid);
+ process_info.SetEffectiveGroupID(proc_kinfo->p_gid);
+
+ ::kvm_close(kdp); /* XXX: we don't check for error here */
+
+ return true;
+
+error:
+ process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);
+ process_info.SetUserID(UINT32_MAX);
+ process_info.SetGroupID(UINT32_MAX);
+ process_info.SetEffectiveUserID(UINT32_MAX);
+ process_info.SetEffectiveGroupID(UINT32_MAX);
+ return false;
+}
+
+uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ const ::pid_t our_pid = ::getpid();
+ const ::uid_t our_uid = ::getuid();
+
+ const bool all_users =
+ match_info.GetMatchAllUsers() ||
+ // Special case, if lldb is being run as root we can attach to anything
+ (our_uid == 0);
+
+ kvm_t *kdp;
+ char errbuf[_POSIX2_LINE_MAX]; /* XXX: error string unused */
+ if ((kdp = ::kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL)
+ return 0;
+
+ struct ::kinfo_proc2 *proc_kinfo;
+ int nproc;
+ if ((proc_kinfo = ::kvm_getproc2(kdp, KERN_PROC_ALL, 0,
+ sizeof(struct ::kinfo_proc2), &nproc)) ==
+ NULL) {
+ ::kvm_close(kdp);
+ return 0;
+ }
+
+ for (int i = 0; i < nproc; i++) {
+ if (proc_kinfo[i].p_pid < 1)
+ continue; /* not valid */
+ /* Make sure the user is acceptable */
+ if (!all_users && proc_kinfo[i].p_ruid != our_uid)
+ continue;
+
+ if (proc_kinfo[i].p_pid == our_pid || // Skip this process
+ proc_kinfo[i].p_pid == 0 || // Skip kernel (kernel pid is 0)
+ proc_kinfo[i].p_stat == LSZOMB || // Zombies are bad
+ proc_kinfo[i].p_flag & P_TRACED || // Being debugged?
+ proc_kinfo[i].p_flag & P_WEXIT) // Working on exiting
+ continue;
+
+ // Every thread is a process in NetBSD, but all the threads of a single
+ // process have the same pid. Do not store the process info in the result
+ // list if a process with given identifier is already registered there.
+ if (proc_kinfo[i].p_nlwps > 1) {
+ bool already_registered = false;
+ for (size_t pi = 0; pi < process_infos.GetSize(); pi++) {
+ if (process_infos.GetProcessIDAtIndex(pi) == proc_kinfo[i].p_pid) {
+ already_registered = true;
+ break;
+ }
+ }
+
+ if (already_registered)
+ continue;
+ }
+ ProcessInstanceInfo process_info;
+ process_info.SetProcessID(proc_kinfo[i].p_pid);
+ process_info.SetParentProcessID(proc_kinfo[i].p_ppid);
+ process_info.SetUserID(proc_kinfo[i].p_ruid);
+ process_info.SetGroupID(proc_kinfo[i].p_rgid);
+ process_info.SetEffectiveUserID(proc_kinfo[i].p_uid);
+ process_info.SetEffectiveGroupID(proc_kinfo[i].p_gid);
+ // Make sure our info matches before we go fetch the name and cpu type
+ if (match_info.Matches(process_info) &&
+ GetNetBSDProcessArgs(&match_info, process_info)) {
+ GetNetBSDProcessCPUType(process_info);
+ if (match_info.Matches(process_info))
+ process_infos.Append(process_info);
+ }
+ }
+
+ kvm_close(kdp); /* XXX: we don't check for error here */
+
+ return process_infos.GetSize();
+}
+
+bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ process_info.SetProcessID(pid);
+
+ if (GetNetBSDProcessArgs(NULL, process_info)) {
+ GetNetBSDProcessCPUType(process_info);
+ GetNetBSDProcessUserAndGroup(process_info);
+ return true;
+ }
+
+ process_info.Clear();
+ return false;
+}
+
+Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
+ return Status("unimplemented");
+}
diff --git a/contrib/llvm-project/lldb/source/Host/netbsd/HostInfoNetBSD.cpp b/contrib/llvm-project/lldb/source/Host/netbsd/HostInfoNetBSD.cpp
new file mode 100644
index 000000000000..99d413922327
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/netbsd/HostInfoNetBSD.cpp
@@ -0,0 +1,91 @@
+//===-- HostInfoNetBSD.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/netbsd/HostInfoNetBSD.h"
+
+#include <inttypes.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+using namespace lldb_private;
+
+llvm::VersionTuple HostInfoNetBSD::GetOSVersion() {
+ struct utsname un;
+
+ ::memset(&un, 0, sizeof(un));
+ if (::uname(&un) < 0)
+ return llvm::VersionTuple();
+
+ /* Accept versions like 7.99.21 and 6.1_STABLE */
+ uint32_t major, minor, update;
+ int status = ::sscanf(un.release, "%" PRIu32 ".%" PRIu32 ".%" PRIu32, &major,
+ &minor, &update);
+ switch (status) {
+ case 1:
+ return llvm::VersionTuple(major);
+ case 2:
+ return llvm::VersionTuple(major, minor);
+ case 3:
+ return llvm::VersionTuple(major, minor, update);
+ }
+ return llvm::VersionTuple();
+}
+
+bool HostInfoNetBSD::GetOSBuildString(std::string &s) {
+ int mib[2] = {CTL_KERN, KERN_OSREV};
+ char osrev_str[12];
+ int osrev = 0;
+ size_t osrev_len = sizeof(osrev);
+
+ if (::sysctl(mib, 2, &osrev, &osrev_len, NULL, 0) == 0) {
+ ::snprintf(osrev_str, sizeof(osrev_str), "%-10.10d", osrev);
+ s.assign(osrev_str);
+ return true;
+ }
+
+ s.clear();
+ return false;
+}
+
+bool HostInfoNetBSD::GetOSKernelDescription(std::string &s) {
+ struct utsname un;
+
+ ::memset(&un, 0, sizeof(un));
+ s.clear();
+
+ if (::uname(&un) < 0)
+ return false;
+
+ s.assign(un.version);
+
+ return true;
+}
+
+FileSpec HostInfoNetBSD::GetProgramFileSpec() {
+ static FileSpec g_program_filespec;
+
+ if (!g_program_filespec) {
+ static const int name[] = {
+ CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME,
+ };
+ char path[MAXPATHLEN];
+ size_t len;
+
+ len = sizeof(path);
+ if (sysctl(name, __arraycount(name), path, &len, NULL, 0) != -1) {
+ g_program_filespec.SetFile(path, FileSpec::Style::native);
+ }
+ }
+ return g_program_filespec;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/openbsd/Host.cpp b/contrib/llvm-project/lldb/source/Host/openbsd/Host.cpp
new file mode 100644
index 000000000000..ba6cf057ca17
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/openbsd/Host.cpp
@@ -0,0 +1,219 @@
+//===-- source/Host/openbsd/Host.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <sys/types.h>
+
+#include <sys/signal.h>
+#include <sys/exec.h>
+#include <sys/proc.h>
+#include <sys/ptrace.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <stdio.h>
+
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/NameMatches.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Support/Host.h"
+
+extern "C" {
+extern char **environ;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace lldb_private {
+class ProcessLaunchInfo;
+}
+
+Environment Host::GetEnvironment() {
+ Environment env;
+ char *v;
+ char **var = environ;
+ for (; var != NULL && *var != NULL; ++var) {
+ v = strchr(*var, (int)'-');
+ if (v == NULL)
+ continue;
+ env.insert(v);
+ }
+ return env;
+}
+
+static bool
+GetOpenBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
+ ProcessInstanceInfo &process_info) {
+ if (process_info.ProcessIDIsValid()) {
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS,
+ (int)process_info.GetProcessID()};
+
+ char arg_data[8192];
+ size_t arg_data_size = sizeof(arg_data);
+ if (::sysctl(mib, 4, arg_data, &arg_data_size, NULL, 0) == 0) {
+ DataExtractor data(arg_data, arg_data_size, endian::InlHostByteOrder(),
+ sizeof(void *));
+ lldb::offset_t offset = 0;
+ const char *cstr;
+
+ cstr = data.GetCStr(&offset);
+ if (cstr) {
+ process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native);
+
+ if (!(match_info_ptr == NULL ||
+ NameMatches(
+ process_info.GetExecutableFile().GetFilename().GetCString(),
+ match_info_ptr->GetNameMatchType(),
+ match_info_ptr->GetProcessInfo().GetName())))
+ return false;
+
+ Args &proc_args = process_info.GetArguments();
+ while (1) {
+ const uint8_t *p = data.PeekData(offset, 1);
+ while ((p != NULL) && (*p == '\0') && offset < arg_data_size) {
+ ++offset;
+ p = data.PeekData(offset, 1);
+ }
+ if (p == NULL || offset >= arg_data_size)
+ return true;
+
+ cstr = data.GetCStr(&offset);
+ if (cstr)
+ proc_args.AppendArgument(llvm::StringRef(cstr));
+ else
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static bool GetOpenBSDProcessCPUType(ProcessInstanceInfo &process_info) {
+ if (process_info.ProcessIDIsValid()) {
+ process_info.GetArchitecture() =
+ HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
+ return true;
+ }
+ process_info.GetArchitecture().Clear();
+ return false;
+}
+
+static bool GetOpenBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
+ struct kinfo_proc proc_kinfo;
+ size_t proc_kinfo_size;
+
+ if (process_info.ProcessIDIsValid()) {
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID,
+ (int)process_info.GetProcessID()};
+ proc_kinfo_size = sizeof(struct kinfo_proc);
+
+ if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) {
+ if (proc_kinfo_size > 0) {
+ process_info.SetParentProcessID(proc_kinfo.p_ppid);
+ process_info.SetUserID(proc_kinfo.p_ruid);
+ process_info.SetGroupID(proc_kinfo.p_rgid);
+ process_info.SetEffectiveUserID(proc_kinfo.p_uid);
+ process_info.SetEffectiveGroupID(proc_kinfo.p_gid);
+ return true;
+ }
+ }
+ }
+ process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);
+ process_info.SetUserID(UINT32_MAX);
+ process_info.SetGroupID(UINT32_MAX);
+ process_info.SetEffectiveUserID(UINT32_MAX);
+ process_info.SetEffectiveGroupID(UINT32_MAX);
+ return false;
+}
+
+uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ std::vector<struct kinfo_proc> kinfos;
+
+ int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
+
+ size_t pid_data_size = 0;
+ if (::sysctl(mib, 3, NULL, &pid_data_size, NULL, 0) != 0)
+ return 0;
+
+ // Add a few extra in case a few more show up
+ const size_t estimated_pid_count =
+ (pid_data_size / sizeof(struct kinfo_proc)) + 10;
+
+ kinfos.resize(estimated_pid_count);
+ pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);
+
+ if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, NULL, 0) != 0)
+ return 0;
+
+ const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));
+
+ bool all_users = match_info.GetMatchAllUsers();
+ const ::pid_t our_pid = getpid();
+ const uid_t our_uid = getuid();
+ for (size_t i = 0; i < actual_pid_count; i++) {
+ const struct kinfo_proc &kinfo = kinfos[i];
+ const bool kinfo_user_matches = (all_users || (kinfo.p_ruid == our_uid) ||
+ // Special case, if lldb is being run as
+ // root we can attach to anything.
+ (our_uid == 0));
+
+ if (kinfo_user_matches == false || // Make sure the user is acceptable
+ kinfo.p_pid == our_pid || // Skip this process
+ kinfo.p_pid == 0 || // Skip kernel (kernel pid is zero)
+ kinfo.p_stat == SZOMB || // Zombies are bad, they like brains...
+ kinfo.p_psflags & PS_TRACED || // Being debugged?
+ kinfo.p_flag & P_WEXIT) // Working on exiting
+ continue;
+
+ ProcessInstanceInfo process_info;
+ process_info.SetProcessID(kinfo.p_pid);
+ process_info.SetParentProcessID(kinfo.p_ppid);
+ process_info.SetUserID(kinfo.p_ruid);
+ process_info.SetGroupID(kinfo.p_rgid);
+ process_info.SetEffectiveUserID(kinfo.p_svuid);
+ process_info.SetEffectiveGroupID(kinfo.p_svgid);
+
+ // Make sure our info matches before we go fetch the name and cpu type
+ if (match_info.Matches(process_info) &&
+ GetOpenBSDProcessArgs(&match_info, process_info)) {
+ GetOpenBSDProcessCPUType(process_info);
+ if (match_info.Matches(process_info))
+ process_infos.Append(process_info);
+ }
+ }
+
+ return process_infos.GetSize();
+}
+
+bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ process_info.SetProcessID(pid);
+
+ if (GetOpenBSDProcessArgs(NULL, process_info)) {
+ // should use libprocstat instead of going right into sysctl?
+ GetOpenBSDProcessCPUType(process_info);
+ GetOpenBSDProcessUserAndGroup(process_info);
+ return true;
+ }
+
+ process_info.Clear();
+ return false;
+}
+
+Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
+ return Status("unimplemented");
+}
diff --git a/contrib/llvm-project/lldb/source/Host/openbsd/HostInfoOpenBSD.cpp b/contrib/llvm-project/lldb/source/Host/openbsd/HostInfoOpenBSD.cpp
new file mode 100644
index 000000000000..950f2ebb86b8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/openbsd/HostInfoOpenBSD.cpp
@@ -0,0 +1,65 @@
+//===-- HostInfoOpenBSD.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/openbsd/HostInfoOpenBSD.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+using namespace lldb_private;
+
+llvm::VersionTuple HostInfoOpenBSD::GetOSVersion() {
+ struct utsname un;
+
+ ::memset(&un, 0, sizeof(utsname));
+ if (uname(&un) < 0)
+ return llvm::VersionTuple();
+
+ unsigned major, minor;
+ if (2 == sscanf(un.release, "%u.%u", &major, &minor))
+ return llvm::VersionTuple(major, minor);
+ return llvm::VersionTuple();
+}
+
+bool HostInfoOpenBSD::GetOSBuildString(std::string &s) {
+ int mib[2] = {CTL_KERN, KERN_OSREV};
+ char osrev_str[12];
+ uint32_t osrev = 0;
+ size_t osrev_len = sizeof(osrev);
+
+ if (::sysctl(mib, 2, &osrev, &osrev_len, NULL, 0) == 0) {
+ ::snprintf(osrev_str, sizeof(osrev_str), "%-8.8u", osrev);
+ s.assign(osrev_str);
+ return true;
+ }
+
+ s.clear();
+ return false;
+}
+
+bool HostInfoOpenBSD::GetOSKernelDescription(std::string &s) {
+ struct utsname un;
+
+ ::memset(&un, 0, sizeof(utsname));
+ s.clear();
+
+ if (uname(&un) < 0)
+ return false;
+
+ s.assign(un.version);
+
+ return true;
+}
+
+FileSpec HostInfoOpenBSD::GetProgramFileSpec() {
+ static FileSpec g_program_filespec;
+ return g_program_filespec;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp
new file mode 100644
index 000000000000..067e85972eca
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp
@@ -0,0 +1,764 @@
+//===-- ConnectionFileDescriptorPosix.cpp -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+// Enable this special support for Apple builds where we can have unlimited
+// select bounds. We tried switching to poll() and kqueue and we were panicing
+// the kernel, so we have to stick with select for now.
+#define _DARWIN_UNLIMITED_SELECT
+#endif
+
+#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Host/SocketAddress.h"
+#include "lldb/Utility/SelectHelper.h"
+#include "lldb/Utility/Timeout.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifndef LLDB_DISABLE_POSIX
+#include <termios.h>
+#include <unistd.h>
+#endif
+
+#include <memory>
+#include <sstream>
+
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/ErrorHandling.h"
+#if defined(__APPLE__)
+#include "llvm/ADT/SmallVector.h"
+#endif
+#include "lldb/Host/Host.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *ConnectionFileDescriptor::LISTEN_SCHEME = "listen";
+const char *ConnectionFileDescriptor::ACCEPT_SCHEME = "accept";
+const char *ConnectionFileDescriptor::UNIX_ACCEPT_SCHEME = "unix-accept";
+const char *ConnectionFileDescriptor::CONNECT_SCHEME = "connect";
+const char *ConnectionFileDescriptor::TCP_CONNECT_SCHEME = "tcp-connect";
+const char *ConnectionFileDescriptor::UDP_SCHEME = "udp";
+const char *ConnectionFileDescriptor::UNIX_CONNECT_SCHEME = "unix-connect";
+const char *ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME =
+ "unix-abstract-connect";
+const char *ConnectionFileDescriptor::FD_SCHEME = "fd";
+const char *ConnectionFileDescriptor::FILE_SCHEME = "file";
+
+namespace {
+
+llvm::Optional<llvm::StringRef> GetURLAddress(llvm::StringRef url,
+ llvm::StringRef scheme) {
+ if (!url.consume_front(scheme))
+ return llvm::None;
+ if (!url.consume_front("://"))
+ return llvm::None;
+ return url;
+}
+}
+
+ConnectionFileDescriptor::ConnectionFileDescriptor(bool child_processes_inherit)
+ : Connection(), m_pipe(), m_mutex(), m_shutting_down(false),
+ m_waiting_for_accept(false),
+ m_child_processes_inherit(child_processes_inherit) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION |
+ LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()",
+ static_cast<void *>(this));
+}
+
+ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd)
+ : Connection(), m_pipe(), m_mutex(), m_shutting_down(false),
+ m_waiting_for_accept(false), m_child_processes_inherit(false) {
+ m_write_sp = std::make_shared<File>(fd, owns_fd);
+ m_read_sp = std::make_shared<File>(fd, false);
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION |
+ LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = "
+ "%i, owns_fd = %i)",
+ static_cast<void *>(this), fd, owns_fd);
+ OpenCommandPipe();
+}
+
+ConnectionFileDescriptor::ConnectionFileDescriptor(Socket *socket)
+ : Connection(), m_pipe(), m_mutex(), m_shutting_down(false),
+ m_waiting_for_accept(false), m_child_processes_inherit(false) {
+ InitializeSocket(socket);
+}
+
+ConnectionFileDescriptor::~ConnectionFileDescriptor() {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION |
+ LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()",
+ static_cast<void *>(this));
+ Disconnect(nullptr);
+ CloseCommandPipe();
+}
+
+void ConnectionFileDescriptor::OpenCommandPipe() {
+ CloseCommandPipe();
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ // Make the command file descriptor here:
+ Status result = m_pipe.CreateNew(m_child_processes_inherit);
+ if (!result.Success()) {
+ if (log)
+ log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe () - could not "
+ "make pipe: %s",
+ static_cast<void *>(this), result.AsCString());
+ } else {
+ if (log)
+ log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe() - success "
+ "readfd=%d writefd=%d",
+ static_cast<void *>(this), m_pipe.GetReadFileDescriptor(),
+ m_pipe.GetWriteFileDescriptor());
+ }
+}
+
+void ConnectionFileDescriptor::CloseCommandPipe() {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("%p ConnectionFileDescriptor::CloseCommandPipe()",
+ static_cast<void *>(this));
+
+ m_pipe.Close();
+}
+
+bool ConnectionFileDescriptor::IsConnected() const {
+ return (m_read_sp && m_read_sp->IsValid()) ||
+ (m_write_sp && m_write_sp->IsValid());
+}
+
+ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path,
+ Status *error_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("%p ConnectionFileDescriptor::Connect (url = '%s')",
+ static_cast<void *>(this), path.str().c_str());
+
+ OpenCommandPipe();
+
+ if (!path.empty()) {
+ llvm::Optional<llvm::StringRef> addr;
+ if ((addr = GetURLAddress(path, LISTEN_SCHEME))) {
+ // listen://HOST:PORT
+ return SocketListenAndAccept(*addr, error_ptr);
+ } else if ((addr = GetURLAddress(path, ACCEPT_SCHEME))) {
+ // unix://SOCKNAME
+ return NamedSocketAccept(*addr, error_ptr);
+ } else if ((addr = GetURLAddress(path, UNIX_ACCEPT_SCHEME))) {
+ // unix://SOCKNAME
+ return NamedSocketAccept(*addr, error_ptr);
+ } else if ((addr = GetURLAddress(path, CONNECT_SCHEME))) {
+ return ConnectTCP(*addr, error_ptr);
+ } else if ((addr = GetURLAddress(path, TCP_CONNECT_SCHEME))) {
+ return ConnectTCP(*addr, error_ptr);
+ } else if ((addr = GetURLAddress(path, UDP_SCHEME))) {
+ return ConnectUDP(*addr, error_ptr);
+ } else if ((addr = GetURLAddress(path, UNIX_CONNECT_SCHEME))) {
+ // unix-connect://SOCKNAME
+ return NamedSocketConnect(*addr, error_ptr);
+ } else if ((addr = GetURLAddress(path, UNIX_ABSTRACT_CONNECT_SCHEME))) {
+ // unix-abstract-connect://SOCKNAME
+ return UnixAbstractSocketConnect(*addr, error_ptr);
+ }
+#ifndef LLDB_DISABLE_POSIX
+ else if ((addr = GetURLAddress(path, FD_SCHEME))) {
+ // Just passing a native file descriptor within this current process that
+ // is already opened (possibly from a service or other source).
+ int fd = -1;
+
+ if (!addr->getAsInteger(0, fd)) {
+ // We have what looks to be a valid file descriptor, but we should make
+ // sure it is. We currently are doing this by trying to get the flags
+ // from the file descriptor and making sure it isn't a bad fd.
+ errno = 0;
+ int flags = ::fcntl(fd, F_GETFL, 0);
+ if (flags == -1 || errno == EBADF) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("stale file descriptor: %s",
+ path.str().c_str());
+ m_read_sp.reset();
+ m_write_sp.reset();
+ return eConnectionStatusError;
+ } else {
+ // Don't take ownership of a file descriptor that gets passed to us
+ // since someone else opened the file descriptor and handed it to us.
+ // TODO: Since are using a URL to open connection we should
+ // eventually parse options using the web standard where we have
+ // "fd://123?opt1=value;opt2=value" and we can have an option be
+ // "owns=1" or "owns=0" or something like this to allow us to specify
+ // this. For now, we assume we must assume we don't own it.
+
+ std::unique_ptr<TCPSocket> tcp_socket;
+ tcp_socket.reset(new TCPSocket(fd, false, false));
+ // Try and get a socket option from this file descriptor to see if
+ // this is a socket and set m_is_socket accordingly.
+ int resuse;
+ bool is_socket =
+ !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse);
+ if (is_socket) {
+ m_read_sp = std::move(tcp_socket);
+ m_write_sp = m_read_sp;
+ } else {
+ m_read_sp = std::make_shared<File>(fd, false);
+ m_write_sp = std::make_shared<File>(fd, false);
+ }
+ m_uri = *addr;
+ return eConnectionStatusSuccess;
+ }
+ }
+
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("invalid file descriptor: \"%s\"",
+ path.str().c_str());
+ m_read_sp.reset();
+ m_write_sp.reset();
+ return eConnectionStatusError;
+ } else if ((addr = GetURLAddress(path, FILE_SCHEME))) {
+ std::string addr_str = addr->str();
+ // file:///PATH
+ int fd = llvm::sys::RetryAfterSignal(-1, ::open, addr_str.c_str(), O_RDWR);
+ if (fd == -1) {
+ if (error_ptr)
+ error_ptr->SetErrorToErrno();
+ return eConnectionStatusError;
+ }
+
+ if (::isatty(fd)) {
+ // Set up serial terminal emulation
+ struct termios options;
+ ::tcgetattr(fd, &options);
+
+ // Set port speed to maximum
+ ::cfsetospeed(&options, B115200);
+ ::cfsetispeed(&options, B115200);
+
+ // Raw input, disable echo and signals
+ options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+
+ // Make sure only one character is needed to return from a read
+ options.c_cc[VMIN] = 1;
+ options.c_cc[VTIME] = 0;
+
+ llvm::sys::RetryAfterSignal(-1, ::tcsetattr, fd, TCSANOW, &options);
+ }
+
+ int flags = ::fcntl(fd, F_GETFL, 0);
+ if (flags >= 0) {
+ if ((flags & O_NONBLOCK) == 0) {
+ flags |= O_NONBLOCK;
+ ::fcntl(fd, F_SETFL, flags);
+ }
+ }
+ m_read_sp = std::make_shared<File>(fd, true);
+ m_write_sp = std::make_shared<File>(fd, false);
+ return eConnectionStatusSuccess;
+ }
+#endif
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'",
+ path.str().c_str());
+ return eConnectionStatusError;
+ }
+ if (error_ptr)
+ error_ptr->SetErrorString("invalid connect arguments");
+ return eConnectionStatusError;
+}
+
+bool ConnectionFileDescriptor::InterruptRead() {
+ size_t bytes_written = 0;
+ Status result = m_pipe.Write("i", 1, bytes_written);
+ return result.Success();
+}
+
+ConnectionStatus ConnectionFileDescriptor::Disconnect(Status *error_ptr) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf("%p ConnectionFileDescriptor::Disconnect ()",
+ static_cast<void *>(this));
+
+ ConnectionStatus status = eConnectionStatusSuccess;
+
+ if (!IsConnected()) {
+ if (log)
+ log->Printf(
+ "%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect",
+ static_cast<void *>(this));
+ return eConnectionStatusSuccess;
+ }
+
+ if (m_read_sp && m_read_sp->IsValid() &&
+ m_read_sp->GetFdType() == IOObject::eFDTypeSocket)
+ static_cast<Socket &>(*m_read_sp).PreDisconnect();
+
+ // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is
+ // quite likely because somebody is doing a blocking read on our file
+ // descriptor. If that's the case, then send the "q" char to the command
+ // file channel so the read will wake up and the connection will then know to
+ // shut down.
+
+ m_shutting_down = true;
+
+ std::unique_lock<std::recursive_mutex> locker(m_mutex, std::defer_lock);
+ if (!locker.try_lock()) {
+ if (m_pipe.CanWrite()) {
+ size_t bytes_written = 0;
+ Status result = m_pipe.Write("q", 1, bytes_written);
+ if (log)
+ log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get "
+ "the lock, sent 'q' to %d, error = '%s'.",
+ static_cast<void *>(this), m_pipe.GetWriteFileDescriptor(),
+ result.AsCString());
+ } else if (log) {
+ log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the "
+ "lock, but no command pipe is available.",
+ static_cast<void *>(this));
+ }
+ locker.lock();
+ }
+
+ Status error = m_read_sp->Close();
+ Status error2 = m_write_sp->Close();
+ if (error.Fail() || error2.Fail())
+ status = eConnectionStatusError;
+ if (error_ptr)
+ *error_ptr = error.Fail() ? error : error2;
+
+ // Close any pipes we were using for async interrupts
+ m_pipe.Close();
+
+ m_uri.clear();
+ m_shutting_down = false;
+ return status;
+}
+
+size_t ConnectionFileDescriptor::Read(void *dst, size_t dst_len,
+ const Timeout<std::micro> &timeout,
+ ConnectionStatus &status,
+ Status *error_ptr) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+
+ std::unique_lock<std::recursive_mutex> locker(m_mutex, std::defer_lock);
+ if (!locker.try_lock()) {
+ if (log)
+ log->Printf("%p ConnectionFileDescriptor::Read () failed to get the "
+ "connection lock.",
+ static_cast<void *>(this));
+ if (error_ptr)
+ error_ptr->SetErrorString("failed to get the connection lock for read.");
+
+ status = eConnectionStatusTimedOut;
+ return 0;
+ }
+
+ if (m_shutting_down) {
+ status = eConnectionStatusError;
+ return 0;
+ }
+
+ status = BytesAvailable(timeout, error_ptr);
+ if (status != eConnectionStatusSuccess)
+ return 0;
+
+ Status error;
+ size_t bytes_read = dst_len;
+ error = m_read_sp->Read(dst, bytes_read);
+
+ if (log) {
+ log->Printf("%p ConnectionFileDescriptor::Read() fd = %" PRIu64
+ ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s",
+ static_cast<void *>(this),
+ static_cast<uint64_t>(m_read_sp->GetWaitableHandle()),
+ static_cast<void *>(dst), static_cast<uint64_t>(dst_len),
+ static_cast<uint64_t>(bytes_read), error.AsCString());
+ }
+
+ if (bytes_read == 0) {
+ error.Clear(); // End-of-file. Do not automatically close; pass along for
+ // the end-of-file handlers.
+ status = eConnectionStatusEndOfFile;
+ }
+
+ if (error_ptr)
+ *error_ptr = error;
+
+ if (error.Fail()) {
+ uint32_t error_value = error.GetError();
+ switch (error_value) {
+ case EAGAIN: // The file was marked for non-blocking I/O, and no data were
+ // ready to be read.
+ if (m_read_sp->GetFdType() == IOObject::eFDTypeSocket)
+ status = eConnectionStatusTimedOut;
+ else
+ status = eConnectionStatusSuccess;
+ return 0;
+
+ case EFAULT: // Buf points outside the allocated address space.
+ case EINTR: // A read from a slow device was interrupted before any data
+ // arrived by the delivery of a signal.
+ case EINVAL: // The pointer associated with fildes was negative.
+ case EIO: // An I/O error occurred while reading from the file system.
+ // The process group is orphaned.
+ // The file is a regular file, nbyte is greater than 0, the
+ // starting position is before the end-of-file, and the
+ // starting position is greater than or equal to the offset
+ // maximum established for the open file descriptor
+ // associated with fildes.
+ case EISDIR: // An attempt is made to read a directory.
+ case ENOBUFS: // An attempt to allocate a memory buffer fails.
+ case ENOMEM: // Insufficient memory is available.
+ status = eConnectionStatusError;
+ break; // Break to close....
+
+ case ENOENT: // no such file or directory
+ case EBADF: // fildes is not a valid file or socket descriptor open for
+ // reading.
+ case ENXIO: // An action is requested of a device that does not exist..
+ // A requested action cannot be performed by the device.
+ case ECONNRESET: // The connection is closed by the peer during a read
+ // attempt on a socket.
+ case ENOTCONN: // A read is attempted on an unconnected socket.
+ status = eConnectionStatusLostConnection;
+ break; // Break to close....
+
+ case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a
+ // socket.
+ status = eConnectionStatusTimedOut;
+ return 0;
+
+ default:
+ LLDB_LOG(log, "this = {0}, unexpected error: {1}", this,
+ llvm::sys::StrError(error_value));
+ status = eConnectionStatusError;
+ break; // Break to close....
+ }
+
+ return 0;
+ }
+ return bytes_read;
+}
+
+size_t ConnectionFileDescriptor::Write(const void *src, size_t src_len,
+ ConnectionStatus &status,
+ Status *error_ptr) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf(
+ "%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")",
+ static_cast<void *>(this), static_cast<const void *>(src),
+ static_cast<uint64_t>(src_len));
+
+ if (!IsConnected()) {
+ if (error_ptr)
+ error_ptr->SetErrorString("not connected");
+ status = eConnectionStatusNoConnection;
+ return 0;
+ }
+
+ Status error;
+
+ size_t bytes_sent = src_len;
+ error = m_write_sp->Write(src, bytes_sent);
+
+ if (log) {
+ log->Printf("%p ConnectionFileDescriptor::Write(fd = %" PRIu64
+ ", src = %p, src_len = %" PRIu64 ") => %" PRIu64
+ " (error = %s)",
+ static_cast<void *>(this),
+ static_cast<uint64_t>(m_write_sp->GetWaitableHandle()),
+ static_cast<const void *>(src), static_cast<uint64_t>(src_len),
+ static_cast<uint64_t>(bytes_sent), error.AsCString());
+ }
+
+ if (error_ptr)
+ *error_ptr = error;
+
+ if (error.Fail()) {
+ switch (error.GetError()) {
+ case EAGAIN:
+ case EINTR:
+ status = eConnectionStatusSuccess;
+ return 0;
+
+ case ECONNRESET: // The connection is closed by the peer during a read
+ // attempt on a socket.
+ case ENOTCONN: // A read is attempted on an unconnected socket.
+ status = eConnectionStatusLostConnection;
+ break; // Break to close....
+
+ default:
+ status = eConnectionStatusError;
+ break; // Break to close....
+ }
+
+ return 0;
+ }
+
+ status = eConnectionStatusSuccess;
+ return bytes_sent;
+}
+
+std::string ConnectionFileDescriptor::GetURI() { return m_uri; }
+
+// This ConnectionFileDescriptor::BytesAvailable() uses select() via
+// SelectHelper
+//
+// PROS:
+// - select is consistent across most unix platforms
+// - The Apple specific version allows for unlimited fds in the fd_sets by
+// setting the _DARWIN_UNLIMITED_SELECT define prior to including the
+// required header files.
+// CONS:
+// - on non-Apple platforms, only supports file descriptors up to FD_SETSIZE.
+// This implementation will assert if it runs into that hard limit to let
+// users know that another ConnectionFileDescriptor::BytesAvailable() should
+// be used or a new version of ConnectionFileDescriptor::BytesAvailable()
+// should be written for the system that is running into the limitations.
+
+ConnectionStatus
+ConnectionFileDescriptor::BytesAvailable(const Timeout<std::micro> &timeout,
+ Status *error_ptr) {
+ // Don't need to take the mutex here separately since we are only called from
+ // Read. If we ever get used more generally we will need to lock here as
+ // well.
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_CONNECTION));
+ LLDB_LOG(log, "this = {0}, timeout = {1}", this, timeout);
+
+ // Make a copy of the file descriptors to make sure we don't have another
+ // thread change these values out from under us and cause problems in the
+ // loop below where like in FS_SET()
+ const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle();
+ const int pipe_fd = m_pipe.GetReadFileDescriptor();
+
+ if (handle != IOObject::kInvalidHandleValue) {
+ SelectHelper select_helper;
+ if (timeout)
+ select_helper.SetTimeout(*timeout);
+
+ select_helper.FDSetRead(handle);
+#if defined(_MSC_VER)
+ // select() won't accept pipes on Windows. The entire Windows codepath
+ // needs to be converted over to using WaitForMultipleObjects and event
+ // HANDLEs, but for now at least this will allow ::select() to not return
+ // an error.
+ const bool have_pipe_fd = false;
+#else
+ const bool have_pipe_fd = pipe_fd >= 0;
+#endif
+ if (have_pipe_fd)
+ select_helper.FDSetRead(pipe_fd);
+
+ while (handle == m_read_sp->GetWaitableHandle()) {
+
+ Status error = select_helper.Select();
+
+ if (error_ptr)
+ *error_ptr = error;
+
+ if (error.Fail()) {
+ switch (error.GetError()) {
+ case EBADF: // One of the descriptor sets specified an invalid
+ // descriptor.
+ return eConnectionStatusLostConnection;
+
+ case EINVAL: // The specified time limit is invalid. One of its
+ // components is negative or too large.
+ default: // Other unknown error
+ return eConnectionStatusError;
+
+ case ETIMEDOUT:
+ return eConnectionStatusTimedOut;
+
+ case EAGAIN: // The kernel was (perhaps temporarily) unable to
+ // allocate the requested number of file descriptors, or
+ // we have non-blocking IO
+ case EINTR: // A signal was delivered before the time limit
+ // expired and before any of the selected events occurred.
+ break; // Lets keep reading to until we timeout
+ }
+ } else {
+ if (select_helper.FDIsSetRead(handle))
+ return eConnectionStatusSuccess;
+
+ if (select_helper.FDIsSetRead(pipe_fd)) {
+ // There is an interrupt or exit command in the command pipe Read the
+ // data from that pipe:
+ char c;
+
+ ssize_t bytes_read = llvm::sys::RetryAfterSignal(-1, ::read, pipe_fd, &c, 1);
+ assert(bytes_read == 1);
+ (void)bytes_read;
+ switch (c) {
+ case 'q':
+ if (log)
+ log->Printf("%p ConnectionFileDescriptor::BytesAvailable() "
+ "got data: %c from the command channel.",
+ static_cast<void *>(this), c);
+ return eConnectionStatusEndOfFile;
+ case 'i':
+ // Interrupt the current read
+ return eConnectionStatusInterrupted;
+ }
+ }
+ }
+ }
+ }
+
+ if (error_ptr)
+ error_ptr->SetErrorString("not connected");
+ return eConnectionStatusLostConnection;
+}
+
+ConnectionStatus
+ConnectionFileDescriptor::NamedSocketAccept(llvm::StringRef socket_name,
+ Status *error_ptr) {
+ Socket *socket = nullptr;
+ Status error =
+ Socket::UnixDomainAccept(socket_name, m_child_processes_inherit, socket);
+ if (error_ptr)
+ *error_ptr = error;
+ m_write_sp.reset(socket);
+ m_read_sp = m_write_sp;
+ if (error.Fail()) {
+ return eConnectionStatusError;
+ }
+ m_uri.assign(socket_name);
+ return eConnectionStatusSuccess;
+}
+
+ConnectionStatus
+ConnectionFileDescriptor::NamedSocketConnect(llvm::StringRef socket_name,
+ Status *error_ptr) {
+ Socket *socket = nullptr;
+ Status error =
+ Socket::UnixDomainConnect(socket_name, m_child_processes_inherit, socket);
+ if (error_ptr)
+ *error_ptr = error;
+ m_write_sp.reset(socket);
+ m_read_sp = m_write_sp;
+ if (error.Fail()) {
+ return eConnectionStatusError;
+ }
+ m_uri.assign(socket_name);
+ return eConnectionStatusSuccess;
+}
+
+lldb::ConnectionStatus
+ConnectionFileDescriptor::UnixAbstractSocketConnect(llvm::StringRef socket_name,
+ Status *error_ptr) {
+ Socket *socket = nullptr;
+ Status error = Socket::UnixAbstractConnect(socket_name,
+ m_child_processes_inherit, socket);
+ if (error_ptr)
+ *error_ptr = error;
+ m_write_sp.reset(socket);
+ m_read_sp = m_write_sp;
+ if (error.Fail()) {
+ return eConnectionStatusError;
+ }
+ m_uri.assign(socket_name);
+ return eConnectionStatusSuccess;
+}
+
+ConnectionStatus
+ConnectionFileDescriptor::SocketListenAndAccept(llvm::StringRef s,
+ Status *error_ptr) {
+ m_port_predicate.SetValue(0, eBroadcastNever);
+
+ Socket *socket = nullptr;
+ m_waiting_for_accept = true;
+ Status error = Socket::TcpListen(s, m_child_processes_inherit, socket,
+ &m_port_predicate);
+ if (error_ptr)
+ *error_ptr = error;
+ if (error.Fail())
+ return eConnectionStatusError;
+
+ std::unique_ptr<Socket> listening_socket_up;
+
+ listening_socket_up.reset(socket);
+ socket = nullptr;
+ error = listening_socket_up->Accept(socket);
+ listening_socket_up.reset();
+ if (error_ptr)
+ *error_ptr = error;
+ if (error.Fail())
+ return eConnectionStatusError;
+
+ InitializeSocket(socket);
+ return eConnectionStatusSuccess;
+}
+
+ConnectionStatus ConnectionFileDescriptor::ConnectTCP(llvm::StringRef s,
+ Status *error_ptr) {
+ Socket *socket = nullptr;
+ Status error = Socket::TcpConnect(s, m_child_processes_inherit, socket);
+ if (error_ptr)
+ *error_ptr = error;
+ m_write_sp.reset(socket);
+ m_read_sp = m_write_sp;
+ if (error.Fail()) {
+ return eConnectionStatusError;
+ }
+ m_uri.assign(s);
+ return eConnectionStatusSuccess;
+}
+
+ConnectionStatus ConnectionFileDescriptor::ConnectUDP(llvm::StringRef s,
+ Status *error_ptr) {
+ Socket *socket = nullptr;
+ Status error = Socket::UdpConnect(s, m_child_processes_inherit, socket);
+ if (error_ptr)
+ *error_ptr = error;
+ m_write_sp.reset(socket);
+ m_read_sp = m_write_sp;
+ if (error.Fail()) {
+ return eConnectionStatusError;
+ }
+ m_uri.assign(s);
+ return eConnectionStatusSuccess;
+}
+
+uint16_t
+ConnectionFileDescriptor::GetListeningPort(const Timeout<std::micro> &timeout) {
+ auto Result = m_port_predicate.WaitForValueNotEqualTo(0, timeout);
+ return Result ? *Result : 0;
+}
+
+bool ConnectionFileDescriptor::GetChildProcessesInherit() const {
+ return m_child_processes_inherit;
+}
+
+void ConnectionFileDescriptor::SetChildProcessesInherit(
+ bool child_processes_inherit) {
+ m_child_processes_inherit = child_processes_inherit;
+}
+
+void ConnectionFileDescriptor::InitializeSocket(Socket *socket) {
+ m_write_sp.reset(socket);
+ m_read_sp = m_write_sp;
+ m_uri = socket->GetRemoteConnectionURI();
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/DomainSocket.cpp b/contrib/llvm-project/lldb/source/Host/posix/DomainSocket.cpp
new file mode 100644
index 000000000000..27872f48129c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/DomainSocket.cpp
@@ -0,0 +1,155 @@
+//===-- DomainSocket.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/posix/DomainSocket.h"
+
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FileSystem.h"
+
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+#ifdef __ANDROID__
+// Android does not have SUN_LEN
+#ifndef SUN_LEN
+#define SUN_LEN(ptr) \
+ (offsetof(struct sockaddr_un, sun_path) + strlen((ptr)->sun_path))
+#endif
+#endif // #ifdef __ANDROID__
+
+namespace {
+
+const int kDomain = AF_UNIX;
+const int kType = SOCK_STREAM;
+
+bool SetSockAddr(llvm::StringRef name, const size_t name_offset,
+ sockaddr_un *saddr_un, socklen_t &saddr_un_len) {
+ if (name.size() + name_offset > sizeof(saddr_un->sun_path))
+ return false;
+
+ memset(saddr_un, 0, sizeof(*saddr_un));
+ saddr_un->sun_family = kDomain;
+
+ memcpy(saddr_un->sun_path + name_offset, name.data(), name.size());
+
+ // For domain sockets we can use SUN_LEN in order to calculate size of
+ // sockaddr_un, but for abstract sockets we have to calculate size manually
+ // because of leading null symbol.
+ if (name_offset == 0)
+ saddr_un_len = SUN_LEN(saddr_un);
+ else
+ saddr_un_len =
+ offsetof(struct sockaddr_un, sun_path) + name_offset + name.size();
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
+ saddr_un->sun_len = saddr_un_len;
+#endif
+
+ return true;
+}
+} // namespace
+
+DomainSocket::DomainSocket(bool should_close, bool child_processes_inherit)
+ : Socket(ProtocolUnixDomain, should_close, child_processes_inherit) {}
+
+DomainSocket::DomainSocket(SocketProtocol protocol,
+ bool child_processes_inherit)
+ : Socket(protocol, true, child_processes_inherit) {}
+
+DomainSocket::DomainSocket(NativeSocket socket,
+ const DomainSocket &listen_socket)
+ : Socket(ProtocolUnixDomain, listen_socket.m_should_close_fd,
+ listen_socket.m_child_processes_inherit) {
+ m_socket = socket;
+}
+
+Status DomainSocket::Connect(llvm::StringRef name) {
+ sockaddr_un saddr_un;
+ socklen_t saddr_un_len;
+ if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len))
+ return Status("Failed to set socket address");
+
+ Status error;
+ m_socket = CreateSocket(kDomain, kType, 0, m_child_processes_inherit, error);
+ if (error.Fail())
+ return error;
+ if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(),
+ (struct sockaddr *)&saddr_un, saddr_un_len) < 0)
+ SetLastError(error);
+
+ return error;
+}
+
+Status DomainSocket::Listen(llvm::StringRef name, int backlog) {
+ sockaddr_un saddr_un;
+ socklen_t saddr_un_len;
+ if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len))
+ return Status("Failed to set socket address");
+
+ DeleteSocketFile(name);
+
+ Status error;
+ m_socket = CreateSocket(kDomain, kType, 0, m_child_processes_inherit, error);
+ if (error.Fail())
+ return error;
+ if (::bind(GetNativeSocket(), (struct sockaddr *)&saddr_un, saddr_un_len) ==
+ 0)
+ if (::listen(GetNativeSocket(), backlog) == 0)
+ return error;
+
+ SetLastError(error);
+ return error;
+}
+
+Status DomainSocket::Accept(Socket *&socket) {
+ Status error;
+ auto conn_fd = AcceptSocket(GetNativeSocket(), nullptr, nullptr,
+ m_child_processes_inherit, error);
+ if (error.Success())
+ socket = new DomainSocket(conn_fd, *this);
+
+ return error;
+}
+
+size_t DomainSocket::GetNameOffset() const { return 0; }
+
+void DomainSocket::DeleteSocketFile(llvm::StringRef name) {
+ llvm::sys::fs::remove(name);
+}
+
+std::string DomainSocket::GetSocketName() const {
+ if (m_socket != kInvalidSocketValue) {
+ struct sockaddr_un saddr_un;
+ saddr_un.sun_family = AF_UNIX;
+ socklen_t sock_addr_len = sizeof(struct sockaddr_un);
+ if (::getpeername(m_socket, (struct sockaddr *)&saddr_un, &sock_addr_len) ==
+ 0) {
+ std::string name(saddr_un.sun_path + GetNameOffset(),
+ sock_addr_len -
+ offsetof(struct sockaddr_un, sun_path) -
+ GetNameOffset());
+ if (name.back() == '\0') name.pop_back();
+ return name;
+ }
+ }
+ return "";
+}
+
+std::string DomainSocket::GetRemoteConnectionURI() const {
+ if (m_socket != kInvalidSocketValue) {
+ return llvm::formatv("{0}://{1}",
+ GetNameOffset() == 0 ? "unix-connect"
+ : "unix-abstract-connect",
+ GetSocketName());
+ }
+ return "";
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/FileSystem.cpp b/contrib/llvm-project/lldb/source/Host/posix/FileSystem.cpp
new file mode 100644
index 000000000000..32fae68abb4d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/FileSystem.cpp
@@ -0,0 +1,80 @@
+//===-- FileSystem.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/FileSystem.h"
+
+// C includes
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#if defined(__NetBSD__)
+#include <sys/statvfs.h>
+#endif
+
+// lldb Includes
+#include "lldb/Host/Host.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *FileSystem::DEV_NULL = "/dev/null";
+
+Status FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) {
+ Status error;
+ if (::symlink(dst.GetCString(), src.GetCString()) == -1)
+ error.SetErrorToErrno();
+ return error;
+}
+
+Status FileSystem::Readlink(const FileSpec &src, FileSpec &dst) {
+ Status error;
+ char buf[PATH_MAX];
+ ssize_t count = ::readlink(src.GetCString(), buf, sizeof(buf) - 1);
+ if (count < 0)
+ error.SetErrorToErrno();
+ else {
+ buf[count] = '\0'; // Success
+ dst.SetFile(buf, FileSpec::Style::native);
+ }
+ return error;
+}
+
+Status FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) {
+ char resolved_path[PATH_MAX];
+ if (!src.GetPath(resolved_path, sizeof(resolved_path))) {
+ return Status("Couldn't get the canonical path for %s", src.GetCString());
+ }
+
+ char real_path[PATH_MAX + 1];
+ if (realpath(resolved_path, real_path) == nullptr) {
+ Status err;
+ err.SetErrorToErrno();
+ return err;
+ }
+
+ dst = FileSpec(real_path);
+
+ return Status();
+}
+
+FILE *FileSystem::Fopen(const char *path, const char *mode) {
+ return llvm::sys::RetryAfterSignal(nullptr, ::fopen, path, mode);
+}
+
+int FileSystem::Open(const char *path, int flags, int mode) {
+ return llvm::sys::RetryAfterSignal(-1, ::open, path, flags, mode);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/HostInfoPosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/HostInfoPosix.cpp
new file mode 100644
index 000000000000..f300e22e9e5c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/HostInfoPosix.cpp
@@ -0,0 +1,140 @@
+//===-- HostInfoPosix.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/posix/HostInfoPosix.h"
+#include "lldb/Utility/UserIDResolver.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <grp.h>
+#include <limits.h>
+#include <mutex>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+using namespace lldb_private;
+
+size_t HostInfoPosix::GetPageSize() { return ::getpagesize(); }
+
+bool HostInfoPosix::GetHostname(std::string &s) {
+ char hostname[PATH_MAX];
+ hostname[sizeof(hostname) - 1] = '\0';
+ if (::gethostname(hostname, sizeof(hostname) - 1) == 0) {
+ struct hostent *h = ::gethostbyname(hostname);
+ if (h)
+ s.assign(h->h_name);
+ else
+ s.assign(hostname);
+ return true;
+ }
+ return false;
+}
+
+#ifdef __ANDROID__
+#include <android/api-level.h>
+#endif
+#if defined(__ANDROID_API__) && __ANDROID_API__ < 21
+#define USE_GETPWUID
+#endif
+
+namespace {
+class PosixUserIDResolver : public UserIDResolver {
+protected:
+ llvm::Optional<std::string> DoGetUserName(id_t uid) override;
+ llvm::Optional<std::string> DoGetGroupName(id_t gid) override;
+};
+} // namespace
+
+llvm::Optional<std::string> PosixUserIDResolver::DoGetUserName(id_t uid) {
+#ifdef USE_GETPWUID
+ // getpwuid_r is missing from android-9
+ // UserIDResolver provides some thread safety by making sure noone calls this
+ // function concurrently, but using getpwuid is ultimately not thread-safe as
+ // we don't know who else might be calling it.
+ struct passwd *user_info_ptr = ::getpwuid(uid);
+ if (user_info_ptr)
+ return std::string(user_info_ptr->pw_name);
+#else
+ struct passwd user_info;
+ struct passwd *user_info_ptr = &user_info;
+ char user_buffer[PATH_MAX];
+ size_t user_buffer_size = sizeof(user_buffer);
+ if (::getpwuid_r(uid, &user_info, user_buffer, user_buffer_size,
+ &user_info_ptr) == 0 &&
+ user_info_ptr) {
+ return std::string(user_info_ptr->pw_name);
+ }
+#endif
+ return llvm::None;
+}
+
+llvm::Optional<std::string> PosixUserIDResolver::DoGetGroupName(id_t gid) {
+#ifndef __ANDROID__
+ char group_buffer[PATH_MAX];
+ size_t group_buffer_size = sizeof(group_buffer);
+ struct group group_info;
+ struct group *group_info_ptr = &group_info;
+ // Try the threadsafe version first
+ if (::getgrgid_r(gid, &group_info, group_buffer, group_buffer_size,
+ &group_info_ptr) == 0) {
+ if (group_info_ptr)
+ return std::string(group_info_ptr->gr_name);
+ } else {
+ // The threadsafe version isn't currently working for me on darwin, but the
+ // non-threadsafe version is, so I am calling it below.
+ group_info_ptr = ::getgrgid(gid);
+ if (group_info_ptr)
+ return std::string(group_info_ptr->gr_name);
+ }
+#else
+ assert(false && "getgrgid_r() not supported on Android");
+#endif
+ return llvm::None;
+}
+
+static llvm::ManagedStatic<PosixUserIDResolver> g_user_id_resolver;
+
+UserIDResolver &HostInfoPosix::GetUserIDResolver() {
+ return *g_user_id_resolver;
+}
+
+uint32_t HostInfoPosix::GetUserID() { return getuid(); }
+
+uint32_t HostInfoPosix::GetGroupID() { return getgid(); }
+
+uint32_t HostInfoPosix::GetEffectiveUserID() { return geteuid(); }
+
+uint32_t HostInfoPosix::GetEffectiveGroupID() { return getegid(); }
+
+FileSpec HostInfoPosix::GetDefaultShell() { return FileSpec("/bin/sh"); }
+
+bool HostInfoPosix::ComputeSupportExeDirectory(FileSpec &file_spec) {
+ return ComputePathRelativeToLibrary(file_spec, "/bin");
+}
+
+bool HostInfoPosix::ComputeHeaderDirectory(FileSpec &file_spec) {
+ FileSpec temp_file("/opt/local/include/lldb");
+ file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str());
+ return true;
+}
+
+bool HostInfoPosix::GetEnvironmentVar(const std::string &var_name,
+ std::string &var) {
+ if (const char *pvar = ::getenv(var_name.c_str())) {
+ var = std::string(pvar);
+ return true;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/HostProcessPosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/HostProcessPosix.cpp
new file mode 100644
index 000000000000..cc187d442468
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/HostProcessPosix.cpp
@@ -0,0 +1,94 @@
+//===-- HostProcessPosix.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Host.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/posix/HostProcessPosix.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include <csignal>
+#include <limits.h>
+#include <unistd.h>
+
+using namespace lldb_private;
+
+namespace {
+const int kInvalidPosixProcess = 0;
+}
+
+HostProcessPosix::HostProcessPosix()
+ : HostNativeProcessBase(kInvalidPosixProcess) {}
+
+HostProcessPosix::HostProcessPosix(lldb::process_t process)
+ : HostNativeProcessBase(process) {}
+
+HostProcessPosix::~HostProcessPosix() {}
+
+Status HostProcessPosix::Signal(int signo) const {
+ if (m_process == kInvalidPosixProcess) {
+ Status error;
+ error.SetErrorString("HostProcessPosix refers to an invalid process");
+ return error;
+ }
+
+ return HostProcessPosix::Signal(m_process, signo);
+}
+
+Status HostProcessPosix::Signal(lldb::process_t process, int signo) {
+ Status error;
+
+ if (-1 == ::kill(process, signo))
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status HostProcessPosix::Terminate() { return Signal(SIGKILL); }
+
+Status HostProcessPosix::GetMainModule(FileSpec &file_spec) const {
+ Status error;
+
+ // Use special code here because proc/[pid]/exe is a symbolic link.
+ char link_path[PATH_MAX];
+ if (snprintf(link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", m_process) != 1) {
+ error.SetErrorString("Unable to build /proc/<pid>/exe string");
+ return error;
+ }
+
+ error = FileSystem::Instance().Readlink(FileSpec(link_path), file_spec);
+ if (!error.Success())
+ return error;
+
+ // If the binary has been deleted, the link name has " (deleted)" appended.
+ // Remove if there.
+ if (file_spec.GetFilename().GetStringRef().endswith(" (deleted)")) {
+ const char *filename = file_spec.GetFilename().GetCString();
+ static const size_t deleted_len = strlen(" (deleted)");
+ const size_t len = file_spec.GetFilename().GetLength();
+ file_spec.GetFilename().SetCStringWithLength(filename, len - deleted_len);
+ }
+ return error;
+}
+
+lldb::pid_t HostProcessPosix::GetProcessId() const { return m_process; }
+
+bool HostProcessPosix::IsRunning() const {
+ if (m_process == kInvalidPosixProcess)
+ return false;
+
+ // Send this process the null signal. If it succeeds the process is running.
+ Status error = Signal(0);
+ return error.Success();
+}
+
+llvm::Expected<HostThread> HostProcessPosix::StartMonitoring(
+ const Host::MonitorChildProcessCallback &callback, bool monitor_signals) {
+ return Host::StartMonitoringChildProcess(callback, m_process,
+ monitor_signals);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/HostThreadPosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/HostThreadPosix.cpp
new file mode 100644
index 000000000000..d78bba517f69
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/HostThreadPosix.cpp
@@ -0,0 +1,61 @@
+//===-- HostThreadPosix.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/posix/HostThreadPosix.h"
+#include "lldb/Utility/Status.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+HostThreadPosix::HostThreadPosix() {}
+
+HostThreadPosix::HostThreadPosix(lldb::thread_t thread)
+ : HostNativeThreadBase(thread) {}
+
+HostThreadPosix::~HostThreadPosix() {}
+
+Status HostThreadPosix::Join(lldb::thread_result_t *result) {
+ Status error;
+ if (IsJoinable()) {
+ int err = ::pthread_join(m_thread, result);
+ error.SetError(err, lldb::eErrorTypePOSIX);
+ } else {
+ if (result)
+ *result = nullptr;
+ error.SetError(EINVAL, eErrorTypePOSIX);
+ }
+
+ Reset();
+ return error;
+}
+
+Status HostThreadPosix::Cancel() {
+ Status error;
+ if (IsJoinable()) {
+#ifndef __FreeBSD__
+ llvm_unreachable("someone is calling HostThread::Cancel()");
+#else
+ int err = ::pthread_cancel(m_thread);
+ error.SetError(err, eErrorTypePOSIX);
+#endif
+ }
+ return error;
+}
+
+Status HostThreadPosix::Detach() {
+ Status error;
+ if (IsJoinable()) {
+ int err = ::pthread_detach(m_thread);
+ error.SetError(err, eErrorTypePOSIX);
+ }
+ Reset();
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/LockFilePosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/LockFilePosix.cpp
new file mode 100644
index 000000000000..a6eae95c333b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/LockFilePosix.cpp
@@ -0,0 +1,62 @@
+//===-- LockFilePosix.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/posix/LockFilePosix.h"
+
+#include "llvm/Support/Errno.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+Status fileLock(int fd, int cmd, int lock_type, const uint64_t start,
+ const uint64_t len) {
+ struct flock fl;
+
+ fl.l_type = lock_type;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = start;
+ fl.l_len = len;
+ fl.l_pid = ::getpid();
+
+ Status error;
+ if (llvm::sys::RetryAfterSignal(-1, ::fcntl, fd, cmd, &fl) == -1)
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+} // namespace
+
+LockFilePosix::LockFilePosix(int fd) : LockFileBase(fd) {}
+
+LockFilePosix::~LockFilePosix() { Unlock(); }
+
+Status LockFilePosix::DoWriteLock(const uint64_t start, const uint64_t len) {
+ return fileLock(m_fd, F_SETLKW, F_WRLCK, start, len);
+}
+
+Status LockFilePosix::DoTryWriteLock(const uint64_t start, const uint64_t len) {
+ return fileLock(m_fd, F_SETLK, F_WRLCK, start, len);
+}
+
+Status LockFilePosix::DoReadLock(const uint64_t start, const uint64_t len) {
+ return fileLock(m_fd, F_SETLKW, F_RDLCK, start, len);
+}
+
+Status LockFilePosix::DoTryReadLock(const uint64_t start, const uint64_t len) {
+ return fileLock(m_fd, F_SETLK, F_RDLCK, start, len);
+}
+
+Status LockFilePosix::DoUnlock() {
+ return fileLock(m_fd, F_SETLK, F_UNLCK, m_start, m_len);
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp b/contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp
new file mode 100644
index 000000000000..efdc151e3763
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp
@@ -0,0 +1,319 @@
+//===-- PipePosix.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/posix/PipePosix.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/SelectHelper.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FileSystem.h"
+
+#if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
+#ifndef _GLIBCXX_USE_NANOSLEEP
+#define _GLIBCXX_USE_NANOSLEEP
+#endif
+#endif
+
+#include <functional>
+#include <thread>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+int PipePosix::kInvalidDescriptor = -1;
+
+enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
+
+// pipe2 is supported by a limited set of platforms
+// TODO: Add more platforms that support pipe2.
+#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) || \
+ defined(__NetBSD__)
+#define PIPE2_SUPPORTED 1
+#else
+#define PIPE2_SUPPORTED 0
+#endif
+
+namespace {
+
+constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
+
+#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
+bool SetCloexecFlag(int fd) {
+ int flags = ::fcntl(fd, F_GETFD);
+ if (flags == -1)
+ return false;
+ return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
+}
+#endif
+
+std::chrono::time_point<std::chrono::steady_clock> Now() {
+ return std::chrono::steady_clock::now();
+}
+} // namespace
+
+PipePosix::PipePosix()
+ : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
+
+PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)
+ : m_fds{read, write} {}
+
+PipePosix::PipePosix(PipePosix &&pipe_posix)
+ : PipeBase{std::move(pipe_posix)},
+ m_fds{pipe_posix.ReleaseReadFileDescriptor(),
+ pipe_posix.ReleaseWriteFileDescriptor()} {}
+
+PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) {
+ PipeBase::operator=(std::move(pipe_posix));
+ m_fds[READ] = pipe_posix.ReleaseReadFileDescriptor();
+ m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptor();
+ return *this;
+}
+
+PipePosix::~PipePosix() { Close(); }
+
+Status PipePosix::CreateNew(bool child_processes_inherit) {
+ if (CanRead() || CanWrite())
+ return Status(EINVAL, eErrorTypePOSIX);
+
+ Status error;
+#if PIPE2_SUPPORTED
+ if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
+ return error;
+#else
+ if (::pipe(m_fds) == 0) {
+#ifdef FD_CLOEXEC
+ if (!child_processes_inherit) {
+ if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
+ error.SetErrorToErrno();
+ Close();
+ return error;
+ }
+ }
+#endif
+ return error;
+ }
+#endif
+
+ error.SetErrorToErrno();
+ m_fds[READ] = PipePosix::kInvalidDescriptor;
+ m_fds[WRITE] = PipePosix::kInvalidDescriptor;
+ return error;
+}
+
+Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
+ if (CanRead() || CanWrite())
+ return Status("Pipe is already opened");
+
+ Status error;
+ if (::mkfifo(name.data(), 0660) != 0)
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,
+ bool child_process_inherit,
+ llvm::SmallVectorImpl<char> &name) {
+ llvm::SmallString<128> named_pipe_path;
+ llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str());
+ FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
+ if (!tmpdir_file_spec)
+ tmpdir_file_spec.AppendPathComponent("/tmp");
+ tmpdir_file_spec.AppendPathComponent(pipe_spec);
+
+ // It's possible that another process creates the target path after we've
+ // verified it's available but before we create it, in which case we should
+ // try again.
+ Status error;
+ do {
+ llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(),
+ named_pipe_path);
+ error = CreateNew(named_pipe_path, child_process_inherit);
+ } while (error.GetError() == EEXIST);
+
+ if (error.Success())
+ name = named_pipe_path;
+ return error;
+}
+
+Status PipePosix::OpenAsReader(llvm::StringRef name,
+ bool child_process_inherit) {
+ if (CanRead() || CanWrite())
+ return Status("Pipe is already opened");
+
+ int flags = O_RDONLY | O_NONBLOCK;
+ if (!child_process_inherit)
+ flags |= O_CLOEXEC;
+
+ Status error;
+ int fd = llvm::sys::RetryAfterSignal(-1, ::open, name.data(), flags);
+ if (fd != -1)
+ m_fds[READ] = fd;
+ else
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status
+PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
+ bool child_process_inherit,
+ const std::chrono::microseconds &timeout) {
+ if (CanRead() || CanWrite())
+ return Status("Pipe is already opened");
+
+ int flags = O_WRONLY | O_NONBLOCK;
+ if (!child_process_inherit)
+ flags |= O_CLOEXEC;
+
+ using namespace std::chrono;
+ const auto finish_time = Now() + timeout;
+
+ while (!CanWrite()) {
+ if (timeout != microseconds::zero()) {
+ const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
+ if (dur <= 0)
+ return Status("timeout exceeded - reader hasn't opened so far");
+ }
+
+ errno = 0;
+ int fd = ::open(name.data(), flags);
+ if (fd == -1) {
+ const auto errno_copy = errno;
+ // We may get ENXIO if a reader side of the pipe hasn't opened yet.
+ if (errno_copy != ENXIO && errno_copy != EINTR)
+ return Status(errno_copy, eErrorTypePOSIX);
+
+ std::this_thread::sleep_for(
+ milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
+ } else {
+ m_fds[WRITE] = fd;
+ }
+ }
+
+ return Status();
+}
+
+int PipePosix::GetReadFileDescriptor() const { return m_fds[READ]; }
+
+int PipePosix::GetWriteFileDescriptor() const { return m_fds[WRITE]; }
+
+int PipePosix::ReleaseReadFileDescriptor() {
+ const int fd = m_fds[READ];
+ m_fds[READ] = PipePosix::kInvalidDescriptor;
+ return fd;
+}
+
+int PipePosix::ReleaseWriteFileDescriptor() {
+ const int fd = m_fds[WRITE];
+ m_fds[WRITE] = PipePosix::kInvalidDescriptor;
+ return fd;
+}
+
+void PipePosix::Close() {
+ CloseReadFileDescriptor();
+ CloseWriteFileDescriptor();
+}
+
+Status PipePosix::Delete(llvm::StringRef name) {
+ return llvm::sys::fs::remove(name);
+}
+
+bool PipePosix::CanRead() const {
+ return m_fds[READ] != PipePosix::kInvalidDescriptor;
+}
+
+bool PipePosix::CanWrite() const {
+ return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
+}
+
+void PipePosix::CloseReadFileDescriptor() {
+ if (CanRead()) {
+ close(m_fds[READ]);
+ m_fds[READ] = PipePosix::kInvalidDescriptor;
+ }
+}
+
+void PipePosix::CloseWriteFileDescriptor() {
+ if (CanWrite()) {
+ close(m_fds[WRITE]);
+ m_fds[WRITE] = PipePosix::kInvalidDescriptor;
+ }
+}
+
+Status PipePosix::ReadWithTimeout(void *buf, size_t size,
+ const std::chrono::microseconds &timeout,
+ size_t &bytes_read) {
+ bytes_read = 0;
+ if (!CanRead())
+ return Status(EINVAL, eErrorTypePOSIX);
+
+ const int fd = GetReadFileDescriptor();
+
+ SelectHelper select_helper;
+ select_helper.SetTimeout(timeout);
+ select_helper.FDSetRead(fd);
+
+ Status error;
+ while (error.Success()) {
+ error = select_helper.Select();
+ if (error.Success()) {
+ auto result = ::read(fd, reinterpret_cast<char *>(buf) + bytes_read,
+ size - bytes_read);
+ if (result != -1) {
+ bytes_read += result;
+ if (bytes_read == size || result == 0)
+ break;
+ } else if (errno == EINTR) {
+ continue;
+ } else {
+ error.SetErrorToErrno();
+ break;
+ }
+ }
+ }
+ return error;
+}
+
+Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
+ bytes_written = 0;
+ if (!CanWrite())
+ return Status(EINVAL, eErrorTypePOSIX);
+
+ const int fd = GetWriteFileDescriptor();
+ SelectHelper select_helper;
+ select_helper.SetTimeout(std::chrono::seconds(0));
+ select_helper.FDSetWrite(fd);
+
+ Status error;
+ while (error.Success()) {
+ error = select_helper.Select();
+ if (error.Success()) {
+ auto result =
+ ::write(fd, reinterpret_cast<const char *>(buf) + bytes_written,
+ size - bytes_written);
+ if (result != -1) {
+ bytes_written += result;
+ if (bytes_written == size)
+ break;
+ } else if (errno == EINTR) {
+ continue;
+ } else {
+ error.SetErrorToErrno();
+ }
+ }
+ }
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/contrib/llvm-project/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp
new file mode 100644
index 000000000000..185c7f0fe248
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp
@@ -0,0 +1,218 @@
+//===-- ProcessLauncherPosixFork.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/posix/ProcessLauncherPosixFork.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "llvm/Support/Errno.h"
+
+#include <limits.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <sstream>
+#include <csignal>
+
+#ifdef __ANDROID__
+#include <android/api-level.h>
+#define PT_TRACE_ME PTRACE_TRACEME
+#endif
+
+#if defined(__ANDROID_API__) && __ANDROID_API__ < 15
+#include <linux/personality.h>
+#elif defined(__linux__)
+#include <sys/personality.h>
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void FixupEnvironment(Environment &env) {
+#ifdef __ANDROID__
+ // If there is no PATH variable specified inside the environment then set the
+ // path to /system/bin. It is required because the default path used by
+ // execve() is wrong on android.
+ env.try_emplace("PATH", "/system/bin");
+#endif
+}
+
+static void LLVM_ATTRIBUTE_NORETURN ExitWithError(int error_fd,
+ const char *operation) {
+ int err = errno;
+ llvm::raw_fd_ostream os(error_fd, true);
+ os << operation << " failed: " << llvm::sys::StrError(err);
+ os.flush();
+ _exit(1);
+}
+
+static void DisableASLRIfRequested(int error_fd, const ProcessLaunchInfo &info) {
+#if defined(__linux__)
+ if (info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)) {
+ const unsigned long personality_get_current = 0xffffffff;
+ int value = personality(personality_get_current);
+ if (value == -1)
+ ExitWithError(error_fd, "personality get");
+
+ value = personality(ADDR_NO_RANDOMIZE | value);
+ if (value == -1)
+ ExitWithError(error_fd, "personality set");
+ }
+#endif
+}
+
+static void DupDescriptor(int error_fd, const FileSpec &file_spec, int fd,
+ int flags) {
+ int target_fd = llvm::sys::RetryAfterSignal(-1, ::open,
+ file_spec.GetCString(), flags, 0666);
+
+ if (target_fd == -1)
+ ExitWithError(error_fd, "DupDescriptor-open");
+
+ if (target_fd == fd)
+ return;
+
+ if (::dup2(target_fd, fd) == -1)
+ ExitWithError(error_fd, "DupDescriptor-dup2");
+
+ ::close(target_fd);
+ return;
+}
+
+static void LLVM_ATTRIBUTE_NORETURN ChildFunc(int error_fd,
+ const ProcessLaunchInfo &info) {
+ if (info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)) {
+ if (setpgid(0, 0) != 0)
+ ExitWithError(error_fd, "setpgid");
+ }
+
+ for (size_t i = 0; i < info.GetNumFileActions(); ++i) {
+ const FileAction &action = *info.GetFileActionAtIndex(i);
+ switch (action.GetAction()) {
+ case FileAction::eFileActionClose:
+ if (close(action.GetFD()) != 0)
+ ExitWithError(error_fd, "close");
+ break;
+ case FileAction::eFileActionDuplicate:
+ if (dup2(action.GetFD(), action.GetActionArgument()) == -1)
+ ExitWithError(error_fd, "dup2");
+ break;
+ case FileAction::eFileActionOpen:
+ DupDescriptor(error_fd, action.GetFileSpec(), action.GetFD(),
+ action.GetActionArgument());
+ break;
+ case FileAction::eFileActionNone:
+ break;
+ }
+ }
+
+ const char **argv = info.GetArguments().GetConstArgumentVector();
+
+ // Change working directory
+ if (info.GetWorkingDirectory() &&
+ 0 != ::chdir(info.GetWorkingDirectory().GetCString()))
+ ExitWithError(error_fd, "chdir");
+
+ DisableASLRIfRequested(error_fd, info);
+ Environment env = info.GetEnvironment();
+ FixupEnvironment(env);
+ Environment::Envp envp = env.getEnvp();
+
+ // Clear the signal mask to prevent the child from being affected by any
+ // masking done by the parent.
+ sigset_t set;
+ if (sigemptyset(&set) != 0 ||
+ pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0)
+ ExitWithError(error_fd, "pthread_sigmask");
+
+ if (info.GetFlags().Test(eLaunchFlagDebug)) {
+ // Do not inherit setgid powers.
+ if (setgid(getgid()) != 0)
+ ExitWithError(error_fd, "setgid");
+
+ // HACK:
+ // Close everything besides stdin, stdout, and stderr that has no file
+ // action to avoid leaking. Only do this when debugging, as elsewhere we
+ // actually rely on passing open descriptors to child processes.
+ for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd)
+ if (!info.GetFileActionForFD(fd) && fd != error_fd)
+ close(fd);
+
+ // Start tracing this child that is about to exec.
+ if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1)
+ ExitWithError(error_fd, "ptrace");
+ }
+
+ // Execute. We should never return...
+ execve(argv[0], const_cast<char *const *>(argv), envp);
+
+#if defined(__linux__)
+ if (errno == ETXTBSY) {
+ // On android M and earlier we can get this error because the adb daemon
+ // can hold a write handle on the executable even after it has finished
+ // uploading it. This state lasts only a short time and happens only when
+ // there are many concurrent adb commands being issued, such as when
+ // running the test suite. (The file remains open when someone does an "adb
+ // shell" command in the fork() child before it has had a chance to exec.)
+ // Since this state should clear up quickly, wait a while and then give it
+ // one more go.
+ usleep(50000);
+ execve(argv[0], const_cast<char *const *>(argv), envp);
+ }
+#endif
+
+ // ...unless exec fails. In which case we definitely need to end the child
+ // here.
+ ExitWithError(error_fd, "execve");
+}
+
+HostProcess
+ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
+ Status &error) {
+ char exe_path[PATH_MAX];
+ launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path));
+
+ // A pipe used by the child process to report errors.
+ PipePosix pipe;
+ const bool child_processes_inherit = false;
+ error = pipe.CreateNew(child_processes_inherit);
+ if (error.Fail())
+ return HostProcess();
+
+ ::pid_t pid = ::fork();
+ if (pid == -1) {
+ // Fork failed
+ error.SetErrorStringWithFormatv("Fork failed with error message: {0}",
+ llvm::sys::StrError());
+ return HostProcess(LLDB_INVALID_PROCESS_ID);
+ }
+ if (pid == 0) {
+ // child process
+ pipe.CloseReadFileDescriptor();
+ ChildFunc(pipe.ReleaseWriteFileDescriptor(), launch_info);
+ }
+
+ // parent process
+
+ pipe.CloseWriteFileDescriptor();
+ char buf[1000];
+ int r = read(pipe.GetReadFileDescriptor(), buf, sizeof buf);
+
+ if (r == 0)
+ return HostProcess(pid); // No error. We're done.
+
+ error.SetErrorString(buf);
+
+ llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0);
+
+ return HostProcess();
+}
diff --git a/contrib/llvm-project/lldb/source/Initialization/SystemInitializer.cpp b/contrib/llvm-project/lldb/source/Initialization/SystemInitializer.cpp
new file mode 100644
index 000000000000..1e40c2694464
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Initialization/SystemInitializer.cpp
@@ -0,0 +1,15 @@
+//===-- SystemInitializer.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Initialization/SystemInitializer.h"
+
+using namespace lldb_private;
+
+SystemInitializer::SystemInitializer() {}
+
+SystemInitializer::~SystemInitializer() {}
diff --git a/contrib/llvm-project/lldb/source/Initialization/SystemInitializerCommon.cpp b/contrib/llvm-project/lldb/source/Initialization/SystemInitializerCommon.cpp
new file mode 100644
index 000000000000..8558911c2f4c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Initialization/SystemInitializerCommon.cpp
@@ -0,0 +1,124 @@
+//===-- SystemInitializerCommon.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Initialization/SystemInitializerCommon.h"
+
+#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Utility/Timer.h"
+#include "lldb/lldb-private.h"
+
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#endif
+
+#if defined(_WIN32)
+#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h"
+#include "lldb/Host/windows/windows.h"
+#endif
+
+#include "llvm/Support/TargetSelect.h"
+
+#include <string>
+
+using namespace lldb_private;
+using namespace lldb_private::repro;
+
+SystemInitializerCommon::SystemInitializerCommon() {}
+
+SystemInitializerCommon::~SystemInitializerCommon() {}
+
+llvm::Error SystemInitializerCommon::Initialize() {
+#if defined(_WIN32)
+ const char *disable_crash_dialog_var = getenv("LLDB_DISABLE_CRASH_DIALOG");
+ if (disable_crash_dialog_var &&
+ llvm::StringRef(disable_crash_dialog_var).equals_lower("true")) {
+ // This will prevent Windows from displaying a dialog box requiring user
+ // interaction when
+ // LLDB crashes. This is mostly useful when automating LLDB, for example
+ // via the test
+ // suite, so that a crash in LLDB does not prevent completion of the test
+ // suite.
+ ::SetErrorMode(GetErrorMode() | SEM_FAILCRITICALERRORS |
+ SEM_NOGPFAULTERRORBOX);
+
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ }
+#endif
+
+ // If the reproducer wasn't initialized before, we can safely assume it's
+ // off.
+ if (!Reproducer::Initialized()) {
+ if (auto e = Reproducer::Initialize(ReproducerMode::Off, llvm::None))
+ return e;
+ }
+
+ auto &r = repro::Reproducer::Instance();
+ if (repro::Loader *loader = r.GetLoader()) {
+ FileSpec vfs_mapping = loader->GetFile<FileProvider::Info>();
+ if (vfs_mapping) {
+ if (llvm::Error e = FileSystem::Initialize(vfs_mapping))
+ return e;
+ } else {
+ FileSystem::Initialize();
+ }
+ } else if (repro::Generator *g = r.GetGenerator()) {
+ repro::VersionProvider &vp = g->GetOrCreate<repro::VersionProvider>();
+ vp.SetVersion(lldb_private::GetVersion());
+ repro::FileProvider &fp = g->GetOrCreate<repro::FileProvider>();
+ FileSystem::Initialize(fp.GetFileCollector());
+ } else {
+ FileSystem::Initialize();
+ }
+
+ Log::Initialize();
+ HostInfo::Initialize();
+
+ llvm::Error error = Socket::Initialize();
+ if (error)
+ return error;
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ process_gdb_remote::ProcessGDBRemoteLog::Initialize();
+
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
+ ProcessPOSIXLog::Initialize();
+#endif
+#if defined(_WIN32)
+ ProcessWindowsLog::Initialize();
+#endif
+
+ return llvm::Error::success();
+}
+
+void SystemInitializerCommon::Terminate() {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+#if defined(_WIN32)
+ ProcessWindowsLog::Terminate();
+#endif
+
+ Socket::Terminate();
+ HostInfo::Terminate();
+ Log::DisableAllLogChannels();
+ FileSystem::Terminate();
+ Reproducer::Terminate();
+}
diff --git a/contrib/llvm-project/lldb/source/Initialization/SystemLifetimeManager.cpp b/contrib/llvm-project/lldb/source/Initialization/SystemLifetimeManager.cpp
new file mode 100644
index 000000000000..97f60489f04b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Initialization/SystemLifetimeManager.cpp
@@ -0,0 +1,56 @@
+//===-- SystemLifetimeManager.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Initialization/SystemLifetimeManager.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Initialization/SystemInitializer.h"
+
+#include <utility>
+
+using namespace lldb_private;
+
+SystemLifetimeManager::SystemLifetimeManager()
+ : m_mutex(), m_initialized(false) {}
+
+SystemLifetimeManager::~SystemLifetimeManager() {
+ assert(!m_initialized &&
+ "SystemLifetimeManager destroyed without calling Terminate!");
+}
+
+llvm::Error SystemLifetimeManager::Initialize(
+ std::unique_ptr<SystemInitializer> initializer,
+ LoadPluginCallbackType plugin_callback) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_initialized) {
+ assert(!m_initializer && "Attempting to call "
+ "SystemLifetimeManager::Initialize() when it is "
+ "already initialized");
+ m_initialized = true;
+ m_initializer = std::move(initializer);
+
+ if (auto e = m_initializer->Initialize())
+ return e;
+
+ Debugger::Initialize(plugin_callback);
+ }
+
+ return llvm::Error::success();
+}
+
+void SystemLifetimeManager::Terminate() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ if (m_initialized) {
+ Debugger::Terminate();
+ m_initializer->Terminate();
+
+ m_initializer.reset();
+ m_initialized = false;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandAlias.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandAlias.cpp
new file mode 100644
index 000000000000..8c40574ee50e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/CommandAlias.cpp
@@ -0,0 +1,247 @@
+//===-- CommandAlias.cpp -----------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/CommandAlias.h"
+
+#include "llvm/Support/ErrorHandling.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static bool ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp,
+ llvm::StringRef options_args,
+ OptionArgVectorSP &option_arg_vector_sp) {
+ bool success = true;
+ OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
+
+ if (options_args.size() < 1)
+ return true;
+
+ Args args(options_args);
+ std::string options_string(options_args);
+ // TODO: Find a way to propagate errors in this CommandReturnObject up the
+ // stack.
+ CommandReturnObject result;
+ // Check to see if the command being aliased can take any command options.
+ Options *options = cmd_obj_sp->GetOptions();
+ if (options) {
+ // See if any options were specified as part of the alias; if so, handle
+ // them appropriately.
+ ExecutionContext exe_ctx =
+ cmd_obj_sp->GetCommandInterpreter().GetExecutionContext();
+ options->NotifyOptionParsingStarting(&exe_ctx);
+
+ llvm::Expected<Args> args_or =
+ options->ParseAlias(args, option_arg_vector, options_string);
+ if (!args_or) {
+ result.AppendError(toString(args_or.takeError()));
+ result.AppendError("Unable to create requested alias.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ args = std::move(*args_or);
+ options->VerifyPartialOptions(result);
+ if (!result.Succeeded() &&
+ result.GetStatus() != lldb::eReturnStatusStarted) {
+ result.AppendError("Unable to create requested alias.\n");
+ return false;
+ }
+ }
+
+ if (!options_string.empty()) {
+ if (cmd_obj_sp->WantsRawCommandString())
+ option_arg_vector->emplace_back("<argument>", -1, options_string);
+ else {
+ for (auto &entry : args.entries()) {
+ if (!entry.ref.empty())
+ option_arg_vector->emplace_back("<argument>", -1, entry.ref);
+ }
+ }
+ }
+
+ return success;
+}
+
+CommandAlias::CommandAlias(CommandInterpreter &interpreter,
+ lldb::CommandObjectSP cmd_sp,
+ llvm::StringRef options_args, llvm::StringRef name,
+ llvm::StringRef help, llvm::StringRef syntax,
+ uint32_t flags)
+ : CommandObject(interpreter, name, help, syntax, flags),
+ m_underlying_command_sp(), m_option_string(options_args),
+ m_option_args_sp(new OptionArgVector),
+ m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false),
+ m_did_set_help_long(false) {
+ if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) {
+ m_underlying_command_sp = cmd_sp;
+ for (int i = 0;
+ auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i);
+ i++) {
+ m_arguments.push_back(*cmd_entry);
+ }
+ if (!help.empty()) {
+ StreamString sstr;
+ StreamString translation_and_help;
+ GetAliasExpansion(sstr);
+
+ translation_and_help.Printf(
+ "(%s) %s", sstr.GetData(),
+ GetUnderlyingCommand()->GetHelp().str().c_str());
+ SetHelp(translation_and_help.GetString());
+ }
+ }
+}
+
+bool CommandAlias::WantsRawCommandString() {
+ if (IsValid())
+ return m_underlying_command_sp->WantsRawCommandString();
+ return false;
+}
+
+bool CommandAlias::WantsCompletion() {
+ if (IsValid())
+ return m_underlying_command_sp->WantsCompletion();
+ return false;
+}
+
+int CommandAlias::HandleCompletion(CompletionRequest &request) {
+ if (IsValid())
+ return m_underlying_command_sp->HandleCompletion(request);
+ return -1;
+}
+
+int CommandAlias::HandleArgumentCompletion(
+ CompletionRequest &request, OptionElementVector &opt_element_vector) {
+ if (IsValid())
+ return m_underlying_command_sp->HandleArgumentCompletion(
+ request, opt_element_vector);
+ return -1;
+}
+
+Options *CommandAlias::GetOptions() {
+ if (IsValid())
+ return m_underlying_command_sp->GetOptions();
+ return nullptr;
+}
+
+bool CommandAlias::Execute(const char *args_string,
+ CommandReturnObject &result) {
+ llvm_unreachable("CommandAlias::Execute is not to be called");
+}
+
+void CommandAlias::GetAliasExpansion(StreamString &help_string) const {
+ llvm::StringRef command_name = m_underlying_command_sp->GetCommandName();
+ help_string.Printf("'%*s", (int)command_name.size(), command_name.data());
+
+ if (!m_option_args_sp) {
+ help_string.Printf("'");
+ return;
+ }
+
+ OptionArgVector *options = m_option_args_sp.get();
+ std::string opt;
+ std::string value;
+
+ for (const auto &opt_entry : *options) {
+ std::tie(opt, std::ignore, value) = opt_entry;
+ if (opt == "<argument>") {
+ help_string.Printf(" %s", value.c_str());
+ } else {
+ help_string.Printf(" %s", opt.c_str());
+ if ((value != "<no-argument>") && (value != "<need-argument")) {
+ help_string.Printf(" %s", value.c_str());
+ }
+ }
+ }
+
+ help_string.Printf("'");
+}
+
+bool CommandAlias::IsDashDashCommand() {
+ if (m_is_dashdash_alias != eLazyBoolCalculate)
+ return (m_is_dashdash_alias == eLazyBoolYes);
+ m_is_dashdash_alias = eLazyBoolNo;
+ if (!IsValid())
+ return false;
+
+ std::string opt;
+ std::string value;
+
+ for (const auto &opt_entry : *GetOptionArguments()) {
+ std::tie(opt, std::ignore, value) = opt_entry;
+ if (opt == "<argument>" && !value.empty() &&
+ llvm::StringRef(value).endswith("--")) {
+ m_is_dashdash_alias = eLazyBoolYes;
+ break;
+ }
+ }
+
+ // if this is a nested alias, it may be adding arguments on top of an already
+ // dash-dash alias
+ if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias())
+ m_is_dashdash_alias =
+ (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes
+ : eLazyBoolNo);
+ return (m_is_dashdash_alias == eLazyBoolYes);
+}
+
+bool CommandAlias::IsNestedAlias() {
+ if (GetUnderlyingCommand())
+ return GetUnderlyingCommand()->IsAlias();
+ return false;
+}
+
+std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() {
+ auto underlying = GetUnderlyingCommand();
+ if (!underlying)
+ return {nullptr, nullptr};
+
+ if (underlying->IsAlias()) {
+ auto desugared = ((CommandAlias *)underlying.get())->Desugar();
+ auto options = GetOptionArguments();
+ options->insert(options->begin(), desugared.second->begin(),
+ desugared.second->end());
+ return {desugared.first, options};
+ }
+
+ return {underlying, GetOptionArguments()};
+}
+
+// allow CommandAlias objects to provide their own help, but fallback to the
+// info for the underlying command if no customization has been provided
+void CommandAlias::SetHelp(llvm::StringRef str) {
+ this->CommandObject::SetHelp(str);
+ m_did_set_help = true;
+}
+
+void CommandAlias::SetHelpLong(llvm::StringRef str) {
+ this->CommandObject::SetHelpLong(str);
+ m_did_set_help_long = true;
+}
+
+llvm::StringRef CommandAlias::GetHelp() {
+ if (!m_cmd_help_short.empty() || m_did_set_help)
+ return m_cmd_help_short;
+ if (IsValid())
+ return m_underlying_command_sp->GetHelp();
+ return llvm::StringRef();
+}
+
+llvm::StringRef CommandAlias::GetHelpLong() {
+ if (!m_cmd_help_long.empty() || m_did_set_help_long)
+ return m_cmd_help_long;
+ if (IsValid())
+ return m_underlying_command_sp->GetHelpLong();
+ return llvm::StringRef();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandHistory.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandHistory.cpp
new file mode 100644
index 000000000000..0be61f836e00
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/CommandHistory.cpp
@@ -0,0 +1,109 @@
+//===-- CommandHistory.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <inttypes.h>
+
+#include "lldb/Interpreter/CommandHistory.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+CommandHistory::CommandHistory() : m_mutex(), m_history() {}
+
+CommandHistory::~CommandHistory() {}
+
+size_t CommandHistory::GetSize() const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return m_history.size();
+}
+
+bool CommandHistory::IsEmpty() const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return m_history.empty();
+}
+
+llvm::Optional<llvm::StringRef>
+CommandHistory::FindString(llvm::StringRef input_str) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (input_str.size() < 2)
+ return llvm::None;
+
+ if (input_str[0] != g_repeat_char)
+ return llvm::None;
+
+ if (input_str[1] == g_repeat_char) {
+ if (m_history.empty())
+ return llvm::None;
+ return llvm::StringRef(m_history.back());
+ }
+
+ input_str = input_str.drop_front();
+
+ size_t idx = 0;
+ if (input_str.front() == '-') {
+ if (input_str.drop_front(1).getAsInteger(0, idx))
+ return llvm::None;
+ if (idx >= m_history.size())
+ return llvm::None;
+ idx = m_history.size() - idx;
+ } else {
+ if (input_str.getAsInteger(0, idx))
+ return llvm::None;
+ if (idx >= m_history.size())
+ return llvm::None;
+ }
+
+ return llvm::StringRef(m_history[idx]);
+}
+
+llvm::StringRef CommandHistory::GetStringAtIndex(size_t idx) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (idx < m_history.size())
+ return m_history[idx];
+ return "";
+}
+
+llvm::StringRef CommandHistory::operator[](size_t idx) const {
+ return GetStringAtIndex(idx);
+}
+
+llvm::StringRef CommandHistory::GetRecentmostString() const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_history.empty())
+ return "";
+ return m_history.back();
+}
+
+void CommandHistory::AppendString(llvm::StringRef str, bool reject_if_dupe) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (reject_if_dupe) {
+ if (!m_history.empty()) {
+ if (str == m_history.back())
+ return;
+ }
+ }
+ m_history.push_back(str);
+}
+
+void CommandHistory::Clear() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_history.clear();
+}
+
+void CommandHistory::Dump(Stream &stream, size_t start_idx,
+ size_t stop_idx) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ stop_idx = std::min(stop_idx + 1, m_history.size());
+ for (size_t counter = start_idx; counter < stop_idx; counter++) {
+ const std::string hist_item = m_history[counter];
+ if (!hist_item.empty()) {
+ stream.Indent();
+ stream.Printf("%4" PRIu64 ": %s\n", (uint64_t)counter, hist_item.c_str());
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp
new file mode 100644
index 000000000000..8948037a6307
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -0,0 +1,3213 @@
+//===-- CommandInterpreter.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <memory>
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+#include "CommandObjectScript.h"
+#include "lldb/Interpreter/CommandObjectRegexCommand.h"
+
+#include "Commands/CommandObjectApropos.h"
+#include "Commands/CommandObjectBreakpoint.h"
+#include "Commands/CommandObjectBugreport.h"
+#include "Commands/CommandObjectCommands.h"
+#include "Commands/CommandObjectDisassemble.h"
+#include "Commands/CommandObjectExpression.h"
+#include "Commands/CommandObjectFrame.h"
+#include "Commands/CommandObjectGUI.h"
+#include "Commands/CommandObjectHelp.h"
+#include "Commands/CommandObjectLanguage.h"
+#include "Commands/CommandObjectLog.h"
+#include "Commands/CommandObjectMemory.h"
+#include "Commands/CommandObjectPlatform.h"
+#include "Commands/CommandObjectPlugin.h"
+#include "Commands/CommandObjectProcess.h"
+#include "Commands/CommandObjectQuit.h"
+#include "Commands/CommandObjectRegister.h"
+#include "Commands/CommandObjectReproducer.h"
+#include "Commands/CommandObjectSettings.h"
+#include "Commands/CommandObjectSource.h"
+#include "Commands/CommandObjectStats.h"
+#include "Commands/CommandObjectTarget.h"
+#include "Commands/CommandObjectThread.h"
+#include "Commands/CommandObjectType.h"
+#include "Commands/CommandObjectVersion.h"
+#include "Commands/CommandObjectWatchpoint.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+
+#ifndef LLDB_DISABLE_LIBEDIT
+#include "lldb/Host/Editline.h"
+#endif
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Utility/Args.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/TargetList.h"
+#include "lldb/Target/Thread.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static const char *k_white_space = " \t\v";
+
+static constexpr bool NoGlobalSetting = true;
+static constexpr uintptr_t DefaultValueTrue = true;
+static constexpr uintptr_t DefaultValueFalse = false;
+static constexpr const char *NoCStrDefault = nullptr;
+static constexpr const char *InitFileWarning =
+ "There is a .lldbinit file in the current directory which is not being "
+ "read.\n"
+ "To silence this warning without sourcing in the local .lldbinit,\n"
+ "add the following to the lldbinit file in your home directory:\n"
+ " settings set target.load-cwd-lldbinit false\n"
+ "To allow lldb to source .lldbinit files in the current working "
+ "directory,\n"
+ "set the value of this variable to true. Only do so if you understand "
+ "and\n"
+ "accept the security risk.";
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"expand-regex-aliases", OptionValue::eTypeBoolean, NoGlobalSetting,
+ DefaultValueFalse, NoCStrDefault, {},
+ "If true, regular expression alias commands will show the "
+ "expanded command that will be executed. This can be used to "
+ "debug new regular expression alias commands."},
+ {"prompt-on-quit", OptionValue::eTypeBoolean, NoGlobalSetting,
+ DefaultValueTrue, NoCStrDefault, {},
+ "If true, LLDB will prompt you before quitting if there are any live "
+ "processes being debugged. If false, LLDB will quit without asking in any "
+ "case."},
+ {"stop-command-source-on-error", OptionValue::eTypeBoolean, NoGlobalSetting,
+ DefaultValueTrue, NoCStrDefault, {},
+ "If true, LLDB will stop running a 'command source' "
+ "script upon encountering an error."},
+ {"space-repl-prompts", OptionValue::eTypeBoolean, NoGlobalSetting,
+ DefaultValueFalse, NoCStrDefault, {},
+ "If true, blank lines will be printed between between REPL submissions."},
+ {"echo-commands", OptionValue::eTypeBoolean, NoGlobalSetting,
+ DefaultValueTrue, NoCStrDefault, {},
+ "If true, commands will be echoed before they are evaluated."},
+ {"echo-comment-commands", OptionValue::eTypeBoolean, NoGlobalSetting,
+ DefaultValueTrue, NoCStrDefault, {},
+ "If true, commands will be echoed even if they are pure comment lines."}};
+
+enum {
+ ePropertyExpandRegexAliases = 0,
+ ePropertyPromptOnQuit = 1,
+ ePropertyStopCmdSourceOnError = 2,
+ eSpaceReplPrompts = 3,
+ eEchoCommands = 4,
+ eEchoCommentCommands = 5
+};
+
+ConstString &CommandInterpreter::GetStaticBroadcasterClass() {
+ static ConstString class_name("lldb.commandInterpreter");
+ return class_name;
+}
+
+CommandInterpreter::CommandInterpreter(Debugger &debugger,
+ bool synchronous_execution)
+ : Broadcaster(debugger.GetBroadcasterManager(),
+ CommandInterpreter::GetStaticBroadcasterClass().AsCString()),
+ Properties(OptionValuePropertiesSP(
+ new OptionValueProperties(ConstString("interpreter")))),
+ IOHandlerDelegate(IOHandlerDelegate::Completion::LLDBCommand),
+ m_debugger(debugger), m_synchronous_execution(synchronous_execution),
+ m_skip_lldbinit_files(false), m_skip_app_init_files(false),
+ m_command_io_handler_sp(), m_comment_char('#'),
+ m_batch_command_mode(false), m_truncation_warning(eNoTruncation),
+ m_command_source_depth(0), m_num_errors(0), m_quit_requested(false),
+ m_stopped_for_crash(false) {
+ SetEventName(eBroadcastBitThreadShouldExit, "thread-should-exit");
+ SetEventName(eBroadcastBitResetPrompt, "reset-prompt");
+ SetEventName(eBroadcastBitQuitCommandReceived, "quit");
+ CheckInWithManager();
+ m_collection_sp->Initialize(g_properties);
+}
+
+bool CommandInterpreter::GetExpandRegexAliases() const {
+ const uint32_t idx = ePropertyExpandRegexAliases;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool CommandInterpreter::GetPromptOnQuit() const {
+ const uint32_t idx = ePropertyPromptOnQuit;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void CommandInterpreter::SetPromptOnQuit(bool b) {
+ const uint32_t idx = ePropertyPromptOnQuit;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+bool CommandInterpreter::GetEchoCommands() const {
+ const uint32_t idx = eEchoCommands;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void CommandInterpreter::SetEchoCommands(bool b) {
+ const uint32_t idx = eEchoCommands;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+bool CommandInterpreter::GetEchoCommentCommands() const {
+ const uint32_t idx = eEchoCommentCommands;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void CommandInterpreter::SetEchoCommentCommands(bool b) {
+ const uint32_t idx = eEchoCommentCommands;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+void CommandInterpreter::AllowExitCodeOnQuit(bool allow) {
+ m_allow_exit_code = allow;
+ if (!allow)
+ m_quit_exit_code.reset();
+}
+
+bool CommandInterpreter::SetQuitExitCode(int exit_code) {
+ if (!m_allow_exit_code)
+ return false;
+ m_quit_exit_code = exit_code;
+ return true;
+}
+
+int CommandInterpreter::GetQuitExitCode(bool &exited) const {
+ exited = m_quit_exit_code.hasValue();
+ if (exited)
+ return *m_quit_exit_code;
+ return 0;
+}
+
+void CommandInterpreter::ResolveCommand(const char *command_line,
+ CommandReturnObject &result) {
+ std::string command = command_line;
+ if (ResolveCommandImpl(command, result) != nullptr) {
+ result.AppendMessageWithFormat("%s", command.c_str());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+}
+
+bool CommandInterpreter::GetStopCmdSourceOnError() const {
+ const uint32_t idx = ePropertyStopCmdSourceOnError;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool CommandInterpreter::GetSpaceReplPrompts() const {
+ const uint32_t idx = eSpaceReplPrompts;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void CommandInterpreter::Initialize() {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ CommandReturnObject result;
+
+ LoadCommandDictionary();
+
+ // An alias arguments vector to reuse - reset it before use...
+ OptionArgVectorSP alias_arguments_vector_sp(new OptionArgVector);
+
+ // Set up some initial aliases.
+ CommandObjectSP cmd_obj_sp = GetCommandSPExact("quit", false);
+ if (cmd_obj_sp) {
+ AddAlias("q", cmd_obj_sp);
+ AddAlias("exit", cmd_obj_sp);
+ }
+
+ cmd_obj_sp = GetCommandSPExact("_regexp-attach", false);
+ if (cmd_obj_sp)
+ AddAlias("attach", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+
+ cmd_obj_sp = GetCommandSPExact("process detach", false);
+ if (cmd_obj_sp) {
+ AddAlias("detach", cmd_obj_sp);
+ }
+
+ cmd_obj_sp = GetCommandSPExact("process continue", false);
+ if (cmd_obj_sp) {
+ AddAlias("c", cmd_obj_sp);
+ AddAlias("continue", cmd_obj_sp);
+ }
+
+ cmd_obj_sp = GetCommandSPExact("_regexp-break", false);
+ if (cmd_obj_sp)
+ AddAlias("b", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+
+ cmd_obj_sp = GetCommandSPExact("_regexp-tbreak", false);
+ if (cmd_obj_sp)
+ AddAlias("tbreak", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+
+ cmd_obj_sp = GetCommandSPExact("thread step-inst", false);
+ if (cmd_obj_sp) {
+ AddAlias("stepi", cmd_obj_sp);
+ AddAlias("si", cmd_obj_sp);
+ }
+
+ cmd_obj_sp = GetCommandSPExact("thread step-inst-over", false);
+ if (cmd_obj_sp) {
+ AddAlias("nexti", cmd_obj_sp);
+ AddAlias("ni", cmd_obj_sp);
+ }
+
+ cmd_obj_sp = GetCommandSPExact("thread step-in", false);
+ if (cmd_obj_sp) {
+ AddAlias("s", cmd_obj_sp);
+ AddAlias("step", cmd_obj_sp);
+ CommandAlias *sif_alias = AddAlias(
+ "sif", cmd_obj_sp, "--end-linenumber block --step-in-target %1");
+ if (sif_alias) {
+ sif_alias->SetHelp("Step through the current block, stopping if you step "
+ "directly into a function whose name matches the "
+ "TargetFunctionName.");
+ sif_alias->SetSyntax("sif <TargetFunctionName>");
+ }
+ }
+
+ cmd_obj_sp = GetCommandSPExact("thread step-over", false);
+ if (cmd_obj_sp) {
+ AddAlias("n", cmd_obj_sp);
+ AddAlias("next", cmd_obj_sp);
+ }
+
+ cmd_obj_sp = GetCommandSPExact("thread step-out", false);
+ if (cmd_obj_sp) {
+ AddAlias("finish", cmd_obj_sp);
+ }
+
+ cmd_obj_sp = GetCommandSPExact("frame select", false);
+ if (cmd_obj_sp) {
+ AddAlias("f", cmd_obj_sp);
+ }
+
+ cmd_obj_sp = GetCommandSPExact("thread select", false);
+ if (cmd_obj_sp) {
+ AddAlias("t", cmd_obj_sp);
+ }
+
+ cmd_obj_sp = GetCommandSPExact("_regexp-jump", false);
+ if (cmd_obj_sp) {
+ AddAlias("j", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+ AddAlias("jump", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+ }
+
+ cmd_obj_sp = GetCommandSPExact("_regexp-list", false);
+ if (cmd_obj_sp) {
+ AddAlias("l", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+ AddAlias("list", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+ }
+
+ cmd_obj_sp = GetCommandSPExact("_regexp-env", false);
+ if (cmd_obj_sp)
+ AddAlias("env", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+
+ cmd_obj_sp = GetCommandSPExact("memory read", false);
+ if (cmd_obj_sp)
+ AddAlias("x", cmd_obj_sp);
+
+ cmd_obj_sp = GetCommandSPExact("_regexp-up", false);
+ if (cmd_obj_sp)
+ AddAlias("up", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+
+ cmd_obj_sp = GetCommandSPExact("_regexp-down", false);
+ if (cmd_obj_sp)
+ AddAlias("down", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+
+ cmd_obj_sp = GetCommandSPExact("_regexp-display", false);
+ if (cmd_obj_sp)
+ AddAlias("display", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+
+ cmd_obj_sp = GetCommandSPExact("disassemble", false);
+ if (cmd_obj_sp)
+ AddAlias("dis", cmd_obj_sp);
+
+ cmd_obj_sp = GetCommandSPExact("disassemble", false);
+ if (cmd_obj_sp)
+ AddAlias("di", cmd_obj_sp);
+
+ cmd_obj_sp = GetCommandSPExact("_regexp-undisplay", false);
+ if (cmd_obj_sp)
+ AddAlias("undisplay", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+
+ cmd_obj_sp = GetCommandSPExact("_regexp-bt", false);
+ if (cmd_obj_sp)
+ AddAlias("bt", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
+
+ cmd_obj_sp = GetCommandSPExact("target create", false);
+ if (cmd_obj_sp)
+ AddAlias("file", cmd_obj_sp);
+
+ cmd_obj_sp = GetCommandSPExact("target modules", false);
+ if (cmd_obj_sp)
+ AddAlias("image", cmd_obj_sp);
+
+ alias_arguments_vector_sp = std::make_shared<OptionArgVector>();
+
+ cmd_obj_sp = GetCommandSPExact("expression", false);
+ if (cmd_obj_sp) {
+ AddAlias("p", cmd_obj_sp, "--")->SetHelpLong("");
+ AddAlias("print", cmd_obj_sp, "--")->SetHelpLong("");
+ AddAlias("call", cmd_obj_sp, "--")->SetHelpLong("");
+ if (auto po = AddAlias("po", cmd_obj_sp, "-O --")) {
+ po->SetHelp("Evaluate an expression on the current thread. Displays any "
+ "returned value with formatting "
+ "controlled by the type's author.");
+ po->SetHelpLong("");
+ }
+ AddAlias("parray", cmd_obj_sp, "--element-count %1 --")->SetHelpLong("");
+ AddAlias("poarray", cmd_obj_sp,
+ "--object-description --element-count %1 --")
+ ->SetHelpLong("");
+ }
+
+ cmd_obj_sp = GetCommandSPExact("process kill", false);
+ if (cmd_obj_sp) {
+ AddAlias("kill", cmd_obj_sp);
+ }
+
+ cmd_obj_sp = GetCommandSPExact("process launch", false);
+ if (cmd_obj_sp) {
+ alias_arguments_vector_sp = std::make_shared<OptionArgVector>();
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ AddAlias("r", cmd_obj_sp, "--");
+ AddAlias("run", cmd_obj_sp, "--");
+#else
+#if defined(__APPLE__)
+ std::string shell_option;
+ shell_option.append("--shell-expand-args");
+ shell_option.append(" true");
+ shell_option.append(" --");
+ AddAlias("r", cmd_obj_sp, "--shell-expand-args true --");
+ AddAlias("run", cmd_obj_sp, "--shell-expand-args true --");
+#else
+ StreamString defaultshell;
+ defaultshell.Printf("--shell=%s --",
+ HostInfo::GetDefaultShell().GetPath().c_str());
+ AddAlias("r", cmd_obj_sp, defaultshell.GetString());
+ AddAlias("run", cmd_obj_sp, defaultshell.GetString());
+#endif
+#endif
+ }
+
+ cmd_obj_sp = GetCommandSPExact("target symbols add", false);
+ if (cmd_obj_sp) {
+ AddAlias("add-dsym", cmd_obj_sp);
+ }
+
+ cmd_obj_sp = GetCommandSPExact("breakpoint set", false);
+ if (cmd_obj_sp) {
+ AddAlias("rbreak", cmd_obj_sp, "--func-regex %1");
+ }
+
+ cmd_obj_sp = GetCommandSPExact("frame variable", false);
+ if (cmd_obj_sp) {
+ AddAlias("v", cmd_obj_sp);
+ AddAlias("var", cmd_obj_sp);
+ AddAlias("vo", cmd_obj_sp, "--object-description");
+ }
+
+ cmd_obj_sp = GetCommandSPExact("register", false);
+ if (cmd_obj_sp) {
+ AddAlias("re", cmd_obj_sp);
+ }
+}
+
+void CommandInterpreter::Clear() {
+ m_command_io_handler_sp.reset();
+}
+
+const char *CommandInterpreter::ProcessEmbeddedScriptCommands(const char *arg) {
+ // This function has not yet been implemented.
+
+ // Look for any embedded script command
+ // If found,
+ // get interpreter object from the command dictionary,
+ // call execute_one_command on it,
+ // get the results as a string,
+ // substitute that string for current stuff.
+
+ return arg;
+}
+
+void CommandInterpreter::LoadCommandDictionary() {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ lldb::ScriptLanguage script_language = m_debugger.GetScriptLanguage();
+
+ m_command_dict["apropos"] = CommandObjectSP(new CommandObjectApropos(*this));
+ m_command_dict["breakpoint"] =
+ CommandObjectSP(new CommandObjectMultiwordBreakpoint(*this));
+ m_command_dict["bugreport"] =
+ CommandObjectSP(new CommandObjectMultiwordBugreport(*this));
+ m_command_dict["command"] =
+ CommandObjectSP(new CommandObjectMultiwordCommands(*this));
+ m_command_dict["disassemble"] =
+ CommandObjectSP(new CommandObjectDisassemble(*this));
+ m_command_dict["expression"] =
+ CommandObjectSP(new CommandObjectExpression(*this));
+ m_command_dict["frame"] =
+ CommandObjectSP(new CommandObjectMultiwordFrame(*this));
+ m_command_dict["gui"] = CommandObjectSP(new CommandObjectGUI(*this));
+ m_command_dict["help"] = CommandObjectSP(new CommandObjectHelp(*this));
+ m_command_dict["log"] = CommandObjectSP(new CommandObjectLog(*this));
+ m_command_dict["memory"] = CommandObjectSP(new CommandObjectMemory(*this));
+ m_command_dict["platform"] =
+ CommandObjectSP(new CommandObjectPlatform(*this));
+ m_command_dict["plugin"] = CommandObjectSP(new CommandObjectPlugin(*this));
+ m_command_dict["process"] =
+ CommandObjectSP(new CommandObjectMultiwordProcess(*this));
+ m_command_dict["quit"] = CommandObjectSP(new CommandObjectQuit(*this));
+ m_command_dict["register"] =
+ CommandObjectSP(new CommandObjectRegister(*this));
+ m_command_dict["reproducer"] =
+ CommandObjectSP(new CommandObjectReproducer(*this));
+ m_command_dict["script"] =
+ CommandObjectSP(new CommandObjectScript(*this, script_language));
+ m_command_dict["settings"] =
+ CommandObjectSP(new CommandObjectMultiwordSettings(*this));
+ m_command_dict["source"] =
+ CommandObjectSP(new CommandObjectMultiwordSource(*this));
+ m_command_dict["statistics"] = CommandObjectSP(new CommandObjectStats(*this));
+ m_command_dict["target"] =
+ CommandObjectSP(new CommandObjectMultiwordTarget(*this));
+ m_command_dict["thread"] =
+ CommandObjectSP(new CommandObjectMultiwordThread(*this));
+ m_command_dict["type"] = CommandObjectSP(new CommandObjectType(*this));
+ m_command_dict["version"] = CommandObjectSP(new CommandObjectVersion(*this));
+ m_command_dict["watchpoint"] =
+ CommandObjectSP(new CommandObjectMultiwordWatchpoint(*this));
+ m_command_dict["language"] =
+ CommandObjectSP(new CommandObjectLanguage(*this));
+
+ const char *break_regexes[][2] = {
+ {"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$",
+ "breakpoint set --file '%1' --line %2"},
+ {"^/([^/]+)/$", "breakpoint set --source-pattern-regexp '%1'"},
+ {"^([[:digit:]]+)[[:space:]]*$", "breakpoint set --line %1"},
+ {"^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1"},
+ {"^[\"']?([-+]?\\[.*\\])[\"']?[[:space:]]*$",
+ "breakpoint set --name '%1'"},
+ {"^(-.*)$", "breakpoint set %1"},
+ {"^(.*[^[:space:]])`(.*[^[:space:]])[[:space:]]*$",
+ "breakpoint set --name '%2' --shlib '%1'"},
+ {"^\\&(.*[^[:space:]])[[:space:]]*$",
+ "breakpoint set --name '%1' --skip-prologue=0"},
+ {"^[\"']?(.*[^[:space:]\"'])[\"']?[[:space:]]*$",
+ "breakpoint set --name '%1'"}};
+
+ size_t num_regexes = llvm::array_lengthof(break_regexes);
+
+ std::unique_ptr<CommandObjectRegexCommand> break_regex_cmd_up(
+ new CommandObjectRegexCommand(
+ *this, "_regexp-break",
+ "Set a breakpoint using one of several shorthand formats.",
+ "\n"
+ "_regexp-break <filename>:<linenum>\n"
+ " main.c:12 // Break at line 12 of "
+ "main.c\n\n"
+ "_regexp-break <linenum>\n"
+ " 12 // Break at line 12 of current "
+ "file\n\n"
+ "_regexp-break 0x<address>\n"
+ " 0x1234000 // Break at address "
+ "0x1234000\n\n"
+ "_regexp-break <name>\n"
+ " main // Break in 'main' after the "
+ "prologue\n\n"
+ "_regexp-break &<name>\n"
+ " &main // Break at first instruction "
+ "in 'main'\n\n"
+ "_regexp-break <module>`<name>\n"
+ " libc.so`malloc // Break in 'malloc' from "
+ "'libc.so'\n\n"
+ "_regexp-break /<source-regex>/\n"
+ " /break here/ // Break on source lines in "
+ "current file\n"
+ " // containing text 'break "
+ "here'.\n",
+ 2,
+ CommandCompletions::eSymbolCompletion |
+ CommandCompletions::eSourceFileCompletion,
+ false));
+
+ if (break_regex_cmd_up) {
+ bool success = true;
+ for (size_t i = 0; i < num_regexes; i++) {
+ success = break_regex_cmd_up->AddRegexCommand(break_regexes[i][0],
+ break_regexes[i][1]);
+ if (!success)
+ break;
+ }
+ success =
+ break_regex_cmd_up->AddRegexCommand("^$", "breakpoint list --full");
+
+ if (success) {
+ CommandObjectSP break_regex_cmd_sp(break_regex_cmd_up.release());
+ m_command_dict[break_regex_cmd_sp->GetCommandName()] = break_regex_cmd_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> tbreak_regex_cmd_up(
+ new CommandObjectRegexCommand(
+ *this, "_regexp-tbreak",
+ "Set a one-shot breakpoint using one of several shorthand formats.",
+ "\n"
+ "_regexp-break <filename>:<linenum>\n"
+ " main.c:12 // Break at line 12 of "
+ "main.c\n\n"
+ "_regexp-break <linenum>\n"
+ " 12 // Break at line 12 of current "
+ "file\n\n"
+ "_regexp-break 0x<address>\n"
+ " 0x1234000 // Break at address "
+ "0x1234000\n\n"
+ "_regexp-break <name>\n"
+ " main // Break in 'main' after the "
+ "prologue\n\n"
+ "_regexp-break &<name>\n"
+ " &main // Break at first instruction "
+ "in 'main'\n\n"
+ "_regexp-break <module>`<name>\n"
+ " libc.so`malloc // Break in 'malloc' from "
+ "'libc.so'\n\n"
+ "_regexp-break /<source-regex>/\n"
+ " /break here/ // Break on source lines in "
+ "current file\n"
+ " // containing text 'break "
+ "here'.\n",
+ 2,
+ CommandCompletions::eSymbolCompletion |
+ CommandCompletions::eSourceFileCompletion,
+ false));
+
+ if (tbreak_regex_cmd_up) {
+ bool success = true;
+ for (size_t i = 0; i < num_regexes; i++) {
+ // If you add a resultant command string longer than 1024 characters be
+ // sure to increase the size of this buffer.
+ char buffer[1024];
+ int num_printed =
+ snprintf(buffer, 1024, "%s %s", break_regexes[i][1], "-o 1");
+ lldbassert(num_printed < 1024);
+ UNUSED_IF_ASSERT_DISABLED(num_printed);
+ success =
+ tbreak_regex_cmd_up->AddRegexCommand(break_regexes[i][0], buffer);
+ if (!success)
+ break;
+ }
+ success =
+ tbreak_regex_cmd_up->AddRegexCommand("^$", "breakpoint list --full");
+
+ if (success) {
+ CommandObjectSP tbreak_regex_cmd_sp(tbreak_regex_cmd_up.release());
+ m_command_dict[tbreak_regex_cmd_sp->GetCommandName()] =
+ tbreak_regex_cmd_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> attach_regex_cmd_up(
+ new CommandObjectRegexCommand(
+ *this, "_regexp-attach", "Attach to process by ID or name.",
+ "_regexp-attach <pid> | <process-name>", 2, 0, false));
+ if (attach_regex_cmd_up) {
+ if (attach_regex_cmd_up->AddRegexCommand("^([0-9]+)[[:space:]]*$",
+ "process attach --pid %1") &&
+ attach_regex_cmd_up->AddRegexCommand(
+ "^(-.*|.* -.*)$", "process attach %1") && // Any options that are
+ // specified get passed to
+ // 'process attach'
+ attach_regex_cmd_up->AddRegexCommand("^(.+)$",
+ "process attach --name '%1'") &&
+ attach_regex_cmd_up->AddRegexCommand("^$", "process attach")) {
+ CommandObjectSP attach_regex_cmd_sp(attach_regex_cmd_up.release());
+ m_command_dict[attach_regex_cmd_sp->GetCommandName()] =
+ attach_regex_cmd_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> down_regex_cmd_up(
+ new CommandObjectRegexCommand(*this, "_regexp-down",
+ "Select a newer stack frame. Defaults to "
+ "moving one frame, a numeric argument can "
+ "specify an arbitrary number.",
+ "_regexp-down [<count>]", 2, 0, false));
+ if (down_regex_cmd_up) {
+ if (down_regex_cmd_up->AddRegexCommand("^$", "frame select -r -1") &&
+ down_regex_cmd_up->AddRegexCommand("^([0-9]+)$",
+ "frame select -r -%1")) {
+ CommandObjectSP down_regex_cmd_sp(down_regex_cmd_up.release());
+ m_command_dict[down_regex_cmd_sp->GetCommandName()] = down_regex_cmd_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> up_regex_cmd_up(
+ new CommandObjectRegexCommand(
+ *this, "_regexp-up",
+ "Select an older stack frame. Defaults to moving one "
+ "frame, a numeric argument can specify an arbitrary number.",
+ "_regexp-up [<count>]", 2, 0, false));
+ if (up_regex_cmd_up) {
+ if (up_regex_cmd_up->AddRegexCommand("^$", "frame select -r 1") &&
+ up_regex_cmd_up->AddRegexCommand("^([0-9]+)$", "frame select -r %1")) {
+ CommandObjectSP up_regex_cmd_sp(up_regex_cmd_up.release());
+ m_command_dict[up_regex_cmd_sp->GetCommandName()] = up_regex_cmd_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> display_regex_cmd_up(
+ new CommandObjectRegexCommand(
+ *this, "_regexp-display",
+ "Evaluate an expression at every stop (see 'help target stop-hook'.)",
+ "_regexp-display expression", 2, 0, false));
+ if (display_regex_cmd_up) {
+ if (display_regex_cmd_up->AddRegexCommand(
+ "^(.+)$", "target stop-hook add -o \"expr -- %1\"")) {
+ CommandObjectSP display_regex_cmd_sp(display_regex_cmd_up.release());
+ m_command_dict[display_regex_cmd_sp->GetCommandName()] =
+ display_regex_cmd_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> undisplay_regex_cmd_up(
+ new CommandObjectRegexCommand(*this, "_regexp-undisplay",
+ "Stop displaying expression at every "
+ "stop (specified by stop-hook index.)",
+ "_regexp-undisplay stop-hook-number", 2, 0,
+ false));
+ if (undisplay_regex_cmd_up) {
+ if (undisplay_regex_cmd_up->AddRegexCommand("^([0-9]+)$",
+ "target stop-hook delete %1")) {
+ CommandObjectSP undisplay_regex_cmd_sp(undisplay_regex_cmd_up.release());
+ m_command_dict[undisplay_regex_cmd_sp->GetCommandName()] =
+ undisplay_regex_cmd_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> connect_gdb_remote_cmd_up(
+ new CommandObjectRegexCommand(
+ *this, "gdb-remote",
+ "Connect to a process via remote GDB server. "
+ "If no host is specifed, localhost is assumed.",
+ "gdb-remote [<hostname>:]<portnum>", 2, 0, false));
+ if (connect_gdb_remote_cmd_up) {
+ if (connect_gdb_remote_cmd_up->AddRegexCommand(
+ "^([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)$",
+ "process connect --plugin gdb-remote connect://%1:%2") &&
+ connect_gdb_remote_cmd_up->AddRegexCommand(
+ "^([[:digit:]]+)$",
+ "process connect --plugin gdb-remote connect://localhost:%1")) {
+ CommandObjectSP command_sp(connect_gdb_remote_cmd_up.release());
+ m_command_dict[command_sp->GetCommandName()] = command_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> connect_kdp_remote_cmd_up(
+ new CommandObjectRegexCommand(
+ *this, "kdp-remote",
+ "Connect to a process via remote KDP server. "
+ "If no UDP port is specified, port 41139 is "
+ "assumed.",
+ "kdp-remote <hostname>[:<portnum>]", 2, 0, false));
+ if (connect_kdp_remote_cmd_up) {
+ if (connect_kdp_remote_cmd_up->AddRegexCommand(
+ "^([^:]+:[[:digit:]]+)$",
+ "process connect --plugin kdp-remote udp://%1") &&
+ connect_kdp_remote_cmd_up->AddRegexCommand(
+ "^(.+)$", "process connect --plugin kdp-remote udp://%1:41139")) {
+ CommandObjectSP command_sp(connect_kdp_remote_cmd_up.release());
+ m_command_dict[command_sp->GetCommandName()] = command_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> bt_regex_cmd_up(
+ new CommandObjectRegexCommand(
+ *this, "_regexp-bt",
+ "Show the current thread's call stack. Any numeric argument "
+ "displays at most that many "
+ "frames. The argument 'all' displays all threads. Use 'settings"
+ " set frame-format' to customize the printing of individual frames "
+ "and 'settings set thread-format' to customize the thread header.",
+ "bt [<digit> | all]", 2, 0, false));
+ if (bt_regex_cmd_up) {
+ // accept but don't document "bt -c <number>" -- before bt was a regex
+ // command if you wanted to backtrace three frames you would do "bt -c 3"
+ // but the intention is to have this emulate the gdb "bt" command and so
+ // now "bt 3" is the preferred form, in line with gdb.
+ if (bt_regex_cmd_up->AddRegexCommand("^([[:digit:]]+)[[:space:]]*$",
+ "thread backtrace -c %1") &&
+ bt_regex_cmd_up->AddRegexCommand("^-c ([[:digit:]]+)[[:space:]]*$",
+ "thread backtrace -c %1") &&
+ bt_regex_cmd_up->AddRegexCommand("^all[[:space:]]*$", "thread backtrace all") &&
+ bt_regex_cmd_up->AddRegexCommand("^[[:space:]]*$", "thread backtrace")) {
+ CommandObjectSP command_sp(bt_regex_cmd_up.release());
+ m_command_dict[command_sp->GetCommandName()] = command_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> list_regex_cmd_up(
+ new CommandObjectRegexCommand(
+ *this, "_regexp-list",
+ "List relevant source code using one of several shorthand formats.",
+ "\n"
+ "_regexp-list <file>:<line> // List around specific file/line\n"
+ "_regexp-list <line> // List current file around specified "
+ "line\n"
+ "_regexp-list <function-name> // List specified function\n"
+ "_regexp-list 0x<address> // List around specified address\n"
+ "_regexp-list -[<count>] // List previous <count> lines\n"
+ "_regexp-list // List subsequent lines",
+ 2, CommandCompletions::eSourceFileCompletion, false));
+ if (list_regex_cmd_up) {
+ if (list_regex_cmd_up->AddRegexCommand("^([0-9]+)[[:space:]]*$",
+ "source list --line %1") &&
+ list_regex_cmd_up->AddRegexCommand(
+ "^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]"
+ "]*$",
+ "source list --file '%1' --line %2") &&
+ list_regex_cmd_up->AddRegexCommand(
+ "^\\*?(0x[[:xdigit:]]+)[[:space:]]*$",
+ "source list --address %1") &&
+ list_regex_cmd_up->AddRegexCommand("^-[[:space:]]*$",
+ "source list --reverse") &&
+ list_regex_cmd_up->AddRegexCommand(
+ "^-([[:digit:]]+)[[:space:]]*$",
+ "source list --reverse --count %1") &&
+ list_regex_cmd_up->AddRegexCommand("^(.+)$",
+ "source list --name \"%1\"") &&
+ list_regex_cmd_up->AddRegexCommand("^$", "source list")) {
+ CommandObjectSP list_regex_cmd_sp(list_regex_cmd_up.release());
+ m_command_dict[list_regex_cmd_sp->GetCommandName()] = list_regex_cmd_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> env_regex_cmd_up(
+ new CommandObjectRegexCommand(
+ *this, "_regexp-env",
+ "Shorthand for viewing and setting environment variables.",
+ "\n"
+ "_regexp-env // Show environment\n"
+ "_regexp-env <name>=<value> // Set an environment variable",
+ 2, 0, false));
+ if (env_regex_cmd_up) {
+ if (env_regex_cmd_up->AddRegexCommand("^$",
+ "settings show target.env-vars") &&
+ env_regex_cmd_up->AddRegexCommand("^([A-Za-z_][A-Za-z_0-9]*=.*)$",
+ "settings set target.env-vars %1")) {
+ CommandObjectSP env_regex_cmd_sp(env_regex_cmd_up.release());
+ m_command_dict[env_regex_cmd_sp->GetCommandName()] = env_regex_cmd_sp;
+ }
+ }
+
+ std::unique_ptr<CommandObjectRegexCommand> jump_regex_cmd_up(
+ new CommandObjectRegexCommand(
+ *this, "_regexp-jump", "Set the program counter to a new address.",
+ "\n"
+ "_regexp-jump <line>\n"
+ "_regexp-jump +<line-offset> | -<line-offset>\n"
+ "_regexp-jump <file>:<line>\n"
+ "_regexp-jump *<addr>\n",
+ 2, 0, false));
+ if (jump_regex_cmd_up) {
+ if (jump_regex_cmd_up->AddRegexCommand("^\\*(.*)$",
+ "thread jump --addr %1") &&
+ jump_regex_cmd_up->AddRegexCommand("^([0-9]+)$",
+ "thread jump --line %1") &&
+ jump_regex_cmd_up->AddRegexCommand("^([^:]+):([0-9]+)$",
+ "thread jump --file %1 --line %2") &&
+ jump_regex_cmd_up->AddRegexCommand("^([+\\-][0-9]+)$",
+ "thread jump --by %1")) {
+ CommandObjectSP jump_regex_cmd_sp(jump_regex_cmd_up.release());
+ m_command_dict[jump_regex_cmd_sp->GetCommandName()] = jump_regex_cmd_sp;
+ }
+ }
+}
+
+int CommandInterpreter::GetCommandNamesMatchingPartialString(
+ const char *cmd_str, bool include_aliases, StringList &matches,
+ StringList &descriptions) {
+ AddNamesMatchingPartialString(m_command_dict, cmd_str, matches,
+ &descriptions);
+
+ if (include_aliases) {
+ AddNamesMatchingPartialString(m_alias_dict, cmd_str, matches,
+ &descriptions);
+ }
+
+ return matches.GetSize();
+}
+
+CommandObjectSP
+CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases,
+ bool exact, StringList *matches,
+ StringList *descriptions) const {
+ CommandObjectSP command_sp;
+
+ std::string cmd = cmd_str;
+
+ if (HasCommands()) {
+ auto pos = m_command_dict.find(cmd);
+ if (pos != m_command_dict.end())
+ command_sp = pos->second;
+ }
+
+ if (include_aliases && HasAliases()) {
+ auto alias_pos = m_alias_dict.find(cmd);
+ if (alias_pos != m_alias_dict.end())
+ command_sp = alias_pos->second;
+ }
+
+ if (HasUserCommands()) {
+ auto pos = m_user_dict.find(cmd);
+ if (pos != m_user_dict.end())
+ command_sp = pos->second;
+ }
+
+ if (!exact && !command_sp) {
+ // We will only get into here if we didn't find any exact matches.
+
+ CommandObjectSP user_match_sp, alias_match_sp, real_match_sp;
+
+ StringList local_matches;
+ if (matches == nullptr)
+ matches = &local_matches;
+
+ unsigned int num_cmd_matches = 0;
+ unsigned int num_alias_matches = 0;
+ unsigned int num_user_matches = 0;
+
+ // Look through the command dictionaries one by one, and if we get only one
+ // match from any of them in toto, then return that, otherwise return an
+ // empty CommandObjectSP and the list of matches.
+
+ if (HasCommands()) {
+ num_cmd_matches = AddNamesMatchingPartialString(m_command_dict, cmd_str,
+ *matches, descriptions);
+ }
+
+ if (num_cmd_matches == 1) {
+ cmd.assign(matches->GetStringAtIndex(0));
+ auto pos = m_command_dict.find(cmd);
+ if (pos != m_command_dict.end())
+ real_match_sp = pos->second;
+ }
+
+ if (include_aliases && HasAliases()) {
+ num_alias_matches = AddNamesMatchingPartialString(m_alias_dict, cmd_str,
+ *matches, descriptions);
+ }
+
+ if (num_alias_matches == 1) {
+ cmd.assign(matches->GetStringAtIndex(num_cmd_matches));
+ auto alias_pos = m_alias_dict.find(cmd);
+ if (alias_pos != m_alias_dict.end())
+ alias_match_sp = alias_pos->second;
+ }
+
+ if (HasUserCommands()) {
+ num_user_matches = AddNamesMatchingPartialString(m_user_dict, cmd_str,
+ *matches, descriptions);
+ }
+
+ if (num_user_matches == 1) {
+ cmd.assign(
+ matches->GetStringAtIndex(num_cmd_matches + num_alias_matches));
+
+ auto pos = m_user_dict.find(cmd);
+ if (pos != m_user_dict.end())
+ user_match_sp = pos->second;
+ }
+
+ // If we got exactly one match, return that, otherwise return the match
+ // list.
+
+ if (num_user_matches + num_cmd_matches + num_alias_matches == 1) {
+ if (num_cmd_matches)
+ return real_match_sp;
+ else if (num_alias_matches)
+ return alias_match_sp;
+ else
+ return user_match_sp;
+ }
+ } else if (matches && command_sp) {
+ matches->AppendString(cmd_str);
+ if (descriptions)
+ descriptions->AppendString(command_sp->GetHelp());
+ }
+
+ return command_sp;
+}
+
+bool CommandInterpreter::AddCommand(llvm::StringRef name,
+ const lldb::CommandObjectSP &cmd_sp,
+ bool can_replace) {
+ if (cmd_sp.get())
+ lldbassert((this == &cmd_sp->GetCommandInterpreter()) &&
+ "tried to add a CommandObject from a different interpreter");
+
+ if (name.empty())
+ return false;
+
+ std::string name_sstr(name);
+ auto name_iter = m_command_dict.find(name_sstr);
+ if (name_iter != m_command_dict.end()) {
+ if (!can_replace || !name_iter->second->IsRemovable())
+ return false;
+ name_iter->second = cmd_sp;
+ } else {
+ m_command_dict[name_sstr] = cmd_sp;
+ }
+ return true;
+}
+
+bool CommandInterpreter::AddUserCommand(llvm::StringRef name,
+ const lldb::CommandObjectSP &cmd_sp,
+ bool can_replace) {
+ if (cmd_sp.get())
+ lldbassert((this == &cmd_sp->GetCommandInterpreter()) &&
+ "tried to add a CommandObject from a different interpreter");
+
+ if (!name.empty()) {
+ // do not allow replacement of internal commands
+ if (CommandExists(name)) {
+ if (!can_replace)
+ return false;
+ if (!m_command_dict[name]->IsRemovable())
+ return false;
+ }
+
+ if (UserCommandExists(name)) {
+ if (!can_replace)
+ return false;
+ if (!m_user_dict[name]->IsRemovable())
+ return false;
+ }
+
+ m_user_dict[name] = cmd_sp;
+ return true;
+ }
+ return false;
+}
+
+CommandObjectSP CommandInterpreter::GetCommandSPExact(llvm::StringRef cmd_str,
+ bool include_aliases) const {
+ Args cmd_words(cmd_str); // Break up the command string into words, in case
+ // it's a multi-word command.
+ CommandObjectSP ret_val; // Possibly empty return value.
+
+ if (cmd_str.empty())
+ return ret_val;
+
+ if (cmd_words.GetArgumentCount() == 1)
+ return GetCommandSP(cmd_str, include_aliases, true, nullptr);
+ else {
+ // We have a multi-word command (seemingly), so we need to do more work.
+ // First, get the cmd_obj_sp for the first word in the command.
+ CommandObjectSP cmd_obj_sp = GetCommandSP(llvm::StringRef(cmd_words.GetArgumentAtIndex(0)),
+ include_aliases, true, nullptr);
+ if (cmd_obj_sp.get() != nullptr) {
+ // Loop through the rest of the words in the command (everything passed
+ // in was supposed to be part of a command name), and find the
+ // appropriate sub-command SP for each command word....
+ size_t end = cmd_words.GetArgumentCount();
+ for (size_t j = 1; j < end; ++j) {
+ if (cmd_obj_sp->IsMultiwordObject()) {
+ cmd_obj_sp =
+ cmd_obj_sp->GetSubcommandSP(cmd_words.GetArgumentAtIndex(j));
+ if (cmd_obj_sp.get() == nullptr)
+ // The sub-command name was invalid. Fail and return the empty
+ // 'ret_val'.
+ return ret_val;
+ } else
+ // We have more words in the command name, but we don't have a
+ // multiword object. Fail and return empty 'ret_val'.
+ return ret_val;
+ }
+ // We successfully looped through all the command words and got valid
+ // command objects for them. Assign the last object retrieved to
+ // 'ret_val'.
+ ret_val = cmd_obj_sp;
+ }
+ }
+ return ret_val;
+}
+
+CommandObject *
+CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str,
+ StringList *matches,
+ StringList *descriptions) const {
+ CommandObject *command_obj =
+ GetCommandSP(cmd_str, false, true, matches, descriptions).get();
+
+ // If we didn't find an exact match to the command string in the commands,
+ // look in the aliases.
+
+ if (command_obj)
+ return command_obj;
+
+ command_obj = GetCommandSP(cmd_str, true, true, matches, descriptions).get();
+
+ if (command_obj)
+ return command_obj;
+
+ // If there wasn't an exact match then look for an inexact one in just the
+ // commands
+ command_obj = GetCommandSP(cmd_str, false, false, nullptr).get();
+
+ // Finally, if there wasn't an inexact match among the commands, look for an
+ // inexact match in both the commands and aliases.
+
+ if (command_obj) {
+ if (matches)
+ matches->AppendString(command_obj->GetCommandName());
+ if (descriptions)
+ descriptions->AppendString(command_obj->GetHelp());
+ return command_obj;
+ }
+
+ return GetCommandSP(cmd_str, true, false, matches, descriptions).get();
+}
+
+bool CommandInterpreter::CommandExists(llvm::StringRef cmd) const {
+ return m_command_dict.find(cmd) != m_command_dict.end();
+}
+
+bool CommandInterpreter::GetAliasFullName(llvm::StringRef cmd,
+ std::string &full_name) const {
+ bool exact_match = (m_alias_dict.find(cmd) != m_alias_dict.end());
+ if (exact_match) {
+ full_name.assign(cmd);
+ return exact_match;
+ } else {
+ StringList matches;
+ size_t num_alias_matches;
+ num_alias_matches =
+ AddNamesMatchingPartialString(m_alias_dict, cmd, matches);
+ if (num_alias_matches == 1) {
+ // Make sure this isn't shadowing a command in the regular command space:
+ StringList regular_matches;
+ const bool include_aliases = false;
+ const bool exact = false;
+ CommandObjectSP cmd_obj_sp(
+ GetCommandSP(cmd, include_aliases, exact, &regular_matches));
+ if (cmd_obj_sp || regular_matches.GetSize() > 0)
+ return false;
+ else {
+ full_name.assign(matches.GetStringAtIndex(0));
+ return true;
+ }
+ } else
+ return false;
+ }
+}
+
+bool CommandInterpreter::AliasExists(llvm::StringRef cmd) const {
+ return m_alias_dict.find(cmd) != m_alias_dict.end();
+}
+
+bool CommandInterpreter::UserCommandExists(llvm::StringRef cmd) const {
+ return m_user_dict.find(cmd) != m_user_dict.end();
+}
+
+CommandAlias *
+CommandInterpreter::AddAlias(llvm::StringRef alias_name,
+ lldb::CommandObjectSP &command_obj_sp,
+ llvm::StringRef args_string) {
+ if (command_obj_sp.get())
+ lldbassert((this == &command_obj_sp->GetCommandInterpreter()) &&
+ "tried to add a CommandObject from a different interpreter");
+
+ std::unique_ptr<CommandAlias> command_alias_up(
+ new CommandAlias(*this, command_obj_sp, args_string, alias_name));
+
+ if (command_alias_up && command_alias_up->IsValid()) {
+ m_alias_dict[alias_name] = CommandObjectSP(command_alias_up.get());
+ return command_alias_up.release();
+ }
+
+ return nullptr;
+}
+
+bool CommandInterpreter::RemoveAlias(llvm::StringRef alias_name) {
+ auto pos = m_alias_dict.find(alias_name);
+ if (pos != m_alias_dict.end()) {
+ m_alias_dict.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+bool CommandInterpreter::RemoveCommand(llvm::StringRef cmd) {
+ auto pos = m_command_dict.find(cmd);
+ if (pos != m_command_dict.end()) {
+ if (pos->second->IsRemovable()) {
+ // Only regular expression objects or python commands are removable
+ m_command_dict.erase(pos);
+ return true;
+ }
+ }
+ return false;
+}
+bool CommandInterpreter::RemoveUser(llvm::StringRef alias_name) {
+ CommandObject::CommandMap::iterator pos = m_user_dict.find(alias_name);
+ if (pos != m_user_dict.end()) {
+ m_user_dict.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+void CommandInterpreter::GetHelp(CommandReturnObject &result,
+ uint32_t cmd_types) {
+ llvm::StringRef help_prologue(GetDebugger().GetIOHandlerHelpPrologue());
+ if (!help_prologue.empty()) {
+ OutputFormattedHelpText(result.GetOutputStream(), llvm::StringRef(),
+ help_prologue);
+ }
+
+ CommandObject::CommandMap::const_iterator pos;
+ size_t max_len = FindLongestCommandWord(m_command_dict);
+
+ if ((cmd_types & eCommandTypesBuiltin) == eCommandTypesBuiltin) {
+ result.AppendMessage("Debugger commands:");
+ result.AppendMessage("");
+
+ for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) {
+ if (!(cmd_types & eCommandTypesHidden) &&
+ (pos->first.compare(0, 1, "_") == 0))
+ continue;
+
+ OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--",
+ pos->second->GetHelp(), max_len);
+ }
+ result.AppendMessage("");
+ }
+
+ if (!m_alias_dict.empty() &&
+ ((cmd_types & eCommandTypesAliases) == eCommandTypesAliases)) {
+ result.AppendMessageWithFormat(
+ "Current command abbreviations "
+ "(type '%shelp command alias' for more info):\n",
+ GetCommandPrefix());
+ result.AppendMessage("");
+ max_len = FindLongestCommandWord(m_alias_dict);
+
+ for (auto alias_pos = m_alias_dict.begin(); alias_pos != m_alias_dict.end();
+ ++alias_pos) {
+ OutputFormattedHelpText(result.GetOutputStream(), alias_pos->first, "--",
+ alias_pos->second->GetHelp(), max_len);
+ }
+ result.AppendMessage("");
+ }
+
+ if (!m_user_dict.empty() &&
+ ((cmd_types & eCommandTypesUserDef) == eCommandTypesUserDef)) {
+ result.AppendMessage("Current user-defined commands:");
+ result.AppendMessage("");
+ max_len = FindLongestCommandWord(m_user_dict);
+ for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) {
+ OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--",
+ pos->second->GetHelp(), max_len);
+ }
+ result.AppendMessage("");
+ }
+
+ result.AppendMessageWithFormat(
+ "For more information on any command, type '%shelp <command-name>'.\n",
+ GetCommandPrefix());
+}
+
+CommandObject *CommandInterpreter::GetCommandObjectForCommand(
+ llvm::StringRef &command_string) {
+ // This function finds the final, lowest-level, alias-resolved command object
+ // whose 'Execute' function will eventually be invoked by the given command
+ // line.
+
+ CommandObject *cmd_obj = nullptr;
+ size_t start = command_string.find_first_not_of(k_white_space);
+ size_t end = 0;
+ bool done = false;
+ while (!done) {
+ if (start != std::string::npos) {
+ // Get the next word from command_string.
+ end = command_string.find_first_of(k_white_space, start);
+ if (end == std::string::npos)
+ end = command_string.size();
+ std::string cmd_word = command_string.substr(start, end - start);
+
+ if (cmd_obj == nullptr)
+ // Since cmd_obj is NULL we are on our first time through this loop.
+ // Check to see if cmd_word is a valid command or alias.
+ cmd_obj = GetCommandObject(cmd_word);
+ else if (cmd_obj->IsMultiwordObject()) {
+ // Our current object is a multi-word object; see if the cmd_word is a
+ // valid sub-command for our object.
+ CommandObject *sub_cmd_obj =
+ cmd_obj->GetSubcommandObject(cmd_word.c_str());
+ if (sub_cmd_obj)
+ cmd_obj = sub_cmd_obj;
+ else // cmd_word was not a valid sub-command word, so we are done
+ done = true;
+ } else
+ // We have a cmd_obj and it is not a multi-word object, so we are done.
+ done = true;
+
+ // If we didn't find a valid command object, or our command object is not
+ // a multi-word object, or we are at the end of the command_string, then
+ // we are done. Otherwise, find the start of the next word.
+
+ if (!cmd_obj || !cmd_obj->IsMultiwordObject() ||
+ end >= command_string.size())
+ done = true;
+ else
+ start = command_string.find_first_not_of(k_white_space, end);
+ } else
+ // Unable to find any more words.
+ done = true;
+ }
+
+ command_string = command_string.substr(end);
+ return cmd_obj;
+}
+
+static const char *k_valid_command_chars =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
+static void StripLeadingSpaces(std::string &s) {
+ if (!s.empty()) {
+ size_t pos = s.find_first_not_of(k_white_space);
+ if (pos == std::string::npos)
+ s.clear();
+ else if (pos == 0)
+ return;
+ s.erase(0, pos);
+ }
+}
+
+static size_t FindArgumentTerminator(const std::string &s) {
+ const size_t s_len = s.size();
+ size_t offset = 0;
+ while (offset < s_len) {
+ size_t pos = s.find("--", offset);
+ if (pos == std::string::npos)
+ break;
+ if (pos > 0) {
+ if (isspace(s[pos - 1])) {
+ // Check if the string ends "\s--" (where \s is a space character) or
+ // if we have "\s--\s".
+ if ((pos + 2 >= s_len) || isspace(s[pos + 2])) {
+ return pos;
+ }
+ }
+ }
+ offset = pos + 2;
+ }
+ return std::string::npos;
+}
+
+static bool ExtractCommand(std::string &command_string, std::string &command,
+ std::string &suffix, char &quote_char) {
+ command.clear();
+ suffix.clear();
+ StripLeadingSpaces(command_string);
+
+ bool result = false;
+ quote_char = '\0';
+
+ if (!command_string.empty()) {
+ const char first_char = command_string[0];
+ if (first_char == '\'' || first_char == '"') {
+ quote_char = first_char;
+ const size_t end_quote_pos = command_string.find(quote_char, 1);
+ if (end_quote_pos == std::string::npos) {
+ command.swap(command_string);
+ command_string.erase();
+ } else {
+ command.assign(command_string, 1, end_quote_pos - 1);
+ if (end_quote_pos + 1 < command_string.size())
+ command_string.erase(0, command_string.find_first_not_of(
+ k_white_space, end_quote_pos + 1));
+ else
+ command_string.erase();
+ }
+ } else {
+ const size_t first_space_pos =
+ command_string.find_first_of(k_white_space);
+ if (first_space_pos == std::string::npos) {
+ command.swap(command_string);
+ command_string.erase();
+ } else {
+ command.assign(command_string, 0, first_space_pos);
+ command_string.erase(0, command_string.find_first_not_of(
+ k_white_space, first_space_pos));
+ }
+ }
+ result = true;
+ }
+
+ if (!command.empty()) {
+ // actual commands can't start with '-' or '_'
+ if (command[0] != '-' && command[0] != '_') {
+ size_t pos = command.find_first_not_of(k_valid_command_chars);
+ if (pos > 0 && pos != std::string::npos) {
+ suffix.assign(command.begin() + pos, command.end());
+ command.erase(pos);
+ }
+ }
+ }
+
+ return result;
+}
+
+CommandObject *CommandInterpreter::BuildAliasResult(
+ llvm::StringRef alias_name, std::string &raw_input_string,
+ std::string &alias_result, CommandReturnObject &result) {
+ CommandObject *alias_cmd_obj = nullptr;
+ Args cmd_args(raw_input_string);
+ alias_cmd_obj = GetCommandObject(alias_name);
+ StreamString result_str;
+
+ if (!alias_cmd_obj || !alias_cmd_obj->IsAlias()) {
+ alias_result.clear();
+ return alias_cmd_obj;
+ }
+ std::pair<CommandObjectSP, OptionArgVectorSP> desugared =
+ ((CommandAlias *)alias_cmd_obj)->Desugar();
+ OptionArgVectorSP option_arg_vector_sp = desugared.second;
+ alias_cmd_obj = desugared.first.get();
+ std::string alias_name_str = alias_name;
+ if ((cmd_args.GetArgumentCount() == 0) ||
+ (alias_name_str != cmd_args.GetArgumentAtIndex(0)))
+ cmd_args.Unshift(alias_name_str);
+
+ result_str.Printf("%s", alias_cmd_obj->GetCommandName().str().c_str());
+
+ if (!option_arg_vector_sp.get()) {
+ alias_result = result_str.GetString();
+ return alias_cmd_obj;
+ }
+ OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
+
+ int value_type;
+ std::string option;
+ std::string value;
+ for (const auto &entry : *option_arg_vector) {
+ std::tie(option, value_type, value) = entry;
+ if (option == "<argument>") {
+ result_str.Printf(" %s", value.c_str());
+ continue;
+ }
+
+ result_str.Printf(" %s", option.c_str());
+ if (value_type == OptionParser::eNoArgument)
+ continue;
+
+ if (value_type != OptionParser::eOptionalArgument)
+ result_str.Printf(" ");
+ int index = GetOptionArgumentPosition(value.c_str());
+ if (index == 0)
+ result_str.Printf("%s", value.c_str());
+ else if (static_cast<size_t>(index) >= cmd_args.GetArgumentCount()) {
+
+ result.AppendErrorWithFormat("Not enough arguments provided; you "
+ "need at least %d arguments to use "
+ "this alias.\n",
+ index);
+ result.SetStatus(eReturnStatusFailed);
+ return nullptr;
+ } else {
+ size_t strpos = raw_input_string.find(cmd_args.GetArgumentAtIndex(index));
+ if (strpos != std::string::npos)
+ raw_input_string = raw_input_string.erase(
+ strpos, strlen(cmd_args.GetArgumentAtIndex(index)));
+ result_str.Printf("%s", cmd_args.GetArgumentAtIndex(index));
+ }
+ }
+
+ alias_result = result_str.GetString();
+ return alias_cmd_obj;
+}
+
+Status CommandInterpreter::PreprocessCommand(std::string &command) {
+ // The command preprocessor needs to do things to the command line before any
+ // parsing of arguments or anything else is done. The only current stuff that
+ // gets preprocessed is anything enclosed in backtick ('`') characters is
+ // evaluated as an expression and the result of the expression must be a
+ // scalar that can be substituted into the command. An example would be:
+ // (lldb) memory read `$rsp + 20`
+ Status error; // Status for any expressions that might not evaluate
+ size_t start_backtick;
+ size_t pos = 0;
+ while ((start_backtick = command.find('`', pos)) != std::string::npos) {
+ // Stop if an error was encountered during the previous iteration.
+ if (error.Fail())
+ break;
+
+ if (start_backtick > 0 && command[start_backtick - 1] == '\\') {
+ // The backtick was preceded by a '\' character, remove the slash and
+ // don't treat the backtick as the start of an expression.
+ command.erase(start_backtick - 1, 1);
+ // No need to add one to start_backtick since we just deleted a char.
+ pos = start_backtick;
+ continue;
+ }
+
+ const size_t expr_content_start = start_backtick + 1;
+ const size_t end_backtick = command.find('`', expr_content_start);
+
+ if (end_backtick == std::string::npos) {
+ // Stop if there's no end backtick.
+ break;
+ }
+
+ if (end_backtick == expr_content_start) {
+ // Skip over empty expression. (two backticks in a row)
+ command.erase(start_backtick, 2);
+ continue;
+ }
+
+ std::string expr_str(command, expr_content_start,
+ end_backtick - expr_content_start);
+
+ ExecutionContext exe_ctx(GetExecutionContext());
+ Target *target = exe_ctx.GetTargetPtr();
+
+ // Get a dummy target to allow for calculator mode while processing
+ // backticks. This also helps break the infinite loop caused when target is
+ // null.
+ if (!target)
+ target = m_debugger.GetDummyTarget();
+
+ if (!target)
+ continue;
+
+ ValueObjectSP expr_result_valobj_sp;
+
+ EvaluateExpressionOptions options;
+ options.SetCoerceToId(false);
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetKeepInMemory(false);
+ options.SetTryAllThreads(true);
+ options.SetTimeout(llvm::None);
+
+ ExpressionResults expr_result =
+ target->EvaluateExpression(expr_str.c_str(), exe_ctx.GetFramePtr(),
+ expr_result_valobj_sp, options);
+
+ if (expr_result == eExpressionCompleted) {
+ Scalar scalar;
+ if (expr_result_valobj_sp)
+ expr_result_valobj_sp =
+ expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable(
+ expr_result_valobj_sp->GetDynamicValueType(), true);
+ if (expr_result_valobj_sp->ResolveValue(scalar)) {
+ command.erase(start_backtick, end_backtick - start_backtick + 1);
+ StreamString value_strm;
+ const bool show_type = false;
+ scalar.GetValue(&value_strm, show_type);
+ size_t value_string_size = value_strm.GetSize();
+ if (value_string_size) {
+ command.insert(start_backtick, value_strm.GetString());
+ pos = start_backtick + value_string_size;
+ continue;
+ } else {
+ error.SetErrorStringWithFormat("expression value didn't result "
+ "in a scalar value for the "
+ "expression '%s'",
+ expr_str.c_str());
+ break;
+ }
+ } else {
+ error.SetErrorStringWithFormat("expression value didn't result "
+ "in a scalar value for the "
+ "expression '%s'",
+ expr_str.c_str());
+ break;
+ }
+
+ continue;
+ }
+
+ if (expr_result_valobj_sp)
+ error = expr_result_valobj_sp->GetError();
+
+ if (error.Success()) {
+ switch (expr_result) {
+ case eExpressionSetupError:
+ error.SetErrorStringWithFormat(
+ "expression setup error for the expression '%s'", expr_str.c_str());
+ break;
+ case eExpressionParseError:
+ error.SetErrorStringWithFormat(
+ "expression parse error for the expression '%s'", expr_str.c_str());
+ break;
+ case eExpressionResultUnavailable:
+ error.SetErrorStringWithFormat(
+ "expression error fetching result for the expression '%s'",
+ expr_str.c_str());
+ break;
+ case eExpressionCompleted:
+ break;
+ case eExpressionDiscarded:
+ error.SetErrorStringWithFormat(
+ "expression discarded for the expression '%s'", expr_str.c_str());
+ break;
+ case eExpressionInterrupted:
+ error.SetErrorStringWithFormat(
+ "expression interrupted for the expression '%s'", expr_str.c_str());
+ break;
+ case eExpressionHitBreakpoint:
+ error.SetErrorStringWithFormat(
+ "expression hit breakpoint for the expression '%s'",
+ expr_str.c_str());
+ break;
+ case eExpressionTimedOut:
+ error.SetErrorStringWithFormat(
+ "expression timed out for the expression '%s'", expr_str.c_str());
+ break;
+ case eExpressionStoppedForDebug:
+ error.SetErrorStringWithFormat("expression stop at entry point "
+ "for debugging for the "
+ "expression '%s'",
+ expr_str.c_str());
+ break;
+ }
+ }
+ }
+ return error;
+}
+
+bool CommandInterpreter::HandleCommand(const char *command_line,
+ LazyBool lazy_add_to_history,
+ CommandReturnObject &result,
+ ExecutionContext *override_context,
+ bool repeat_on_empty_command,
+ bool no_context_switching)
+
+{
+
+ std::string command_string(command_line);
+ std::string original_command_string(command_line);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMANDS));
+ llvm::PrettyStackTraceFormat stack_trace("HandleCommand(command = \"%s\")",
+ command_line);
+
+ if (log)
+ log->Printf("Processing command: %s", command_line);
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "Handling command: %s.", command_line);
+
+ if (!no_context_switching)
+ UpdateExecutionContext(override_context);
+
+ if (WasInterrupted()) {
+ result.AppendError("interrupted");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ bool add_to_history;
+ if (lazy_add_to_history == eLazyBoolCalculate)
+ add_to_history = (m_command_source_depth == 0);
+ else
+ add_to_history = (lazy_add_to_history == eLazyBoolYes);
+
+ bool empty_command = false;
+ bool comment_command = false;
+ if (command_string.empty())
+ empty_command = true;
+ else {
+ const char *k_space_characters = "\t\n\v\f\r ";
+
+ size_t non_space = command_string.find_first_not_of(k_space_characters);
+ // Check for empty line or comment line (lines whose first non-space
+ // character is the comment character for this interpreter)
+ if (non_space == std::string::npos)
+ empty_command = true;
+ else if (command_string[non_space] == m_comment_char)
+ comment_command = true;
+ else if (command_string[non_space] == CommandHistory::g_repeat_char) {
+ llvm::StringRef search_str(command_string);
+ search_str = search_str.drop_front(non_space);
+ if (auto hist_str = m_command_history.FindString(search_str)) {
+ add_to_history = false;
+ command_string = *hist_str;
+ original_command_string = *hist_str;
+ } else {
+ result.AppendErrorWithFormat("Could not find entry: %s in history",
+ command_string.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ }
+
+ if (empty_command) {
+ if (repeat_on_empty_command) {
+ if (m_command_history.IsEmpty()) {
+ result.AppendError("empty command");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else {
+ command_line = m_repeat_command.c_str();
+ command_string = command_line;
+ original_command_string = command_line;
+ if (m_repeat_command.empty()) {
+ result.AppendErrorWithFormat("No auto repeat.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ add_to_history = false;
+ } else {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+ } else if (comment_command) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ Status error(PreprocessCommand(command_string));
+
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ // Phase 1.
+
+ // Before we do ANY kind of argument processing, we need to figure out what
+ // the real/final command object is for the specified command. This gets
+ // complicated by the fact that the user could have specified an alias, and,
+ // in translating the alias, there may also be command options and/or even
+ // data (including raw text strings) that need to be found and inserted into
+ // the command line as part of the translation. So this first step is plain
+ // look-up and replacement, resulting in:
+ // 1. the command object whose Execute method will actually be called
+ // 2. a revised command string, with all substitutions and replacements
+ // taken care of
+ // From 1 above, we can determine whether the Execute function wants raw
+ // input or not.
+
+ CommandObject *cmd_obj = ResolveCommandImpl(command_string, result);
+
+ // Although the user may have abbreviated the command, the command_string now
+ // has the command expanded to the full name. For example, if the input was
+ // "br s -n main", command_string is now "breakpoint set -n main".
+ if (log) {
+ llvm::StringRef command_name = cmd_obj ? cmd_obj->GetCommandName() : "<not found>";
+ log->Printf("HandleCommand, cmd_obj : '%s'", command_name.str().c_str());
+ log->Printf("HandleCommand, (revised) command_string: '%s'",
+ command_string.c_str());
+ const bool wants_raw_input =
+ (cmd_obj != nullptr) ? cmd_obj->WantsRawCommandString() : false;
+ log->Printf("HandleCommand, wants_raw_input:'%s'",
+ wants_raw_input ? "True" : "False");
+ }
+
+ // Phase 2.
+ // Take care of things like setting up the history command & calling the
+ // appropriate Execute method on the CommandObject, with the appropriate
+ // arguments.
+
+ if (cmd_obj != nullptr) {
+ if (add_to_history) {
+ Args command_args(command_string);
+ const char *repeat_command = cmd_obj->GetRepeatCommand(command_args, 0);
+ if (repeat_command != nullptr)
+ m_repeat_command.assign(repeat_command);
+ else
+ m_repeat_command.assign(original_command_string);
+
+ m_command_history.AppendString(original_command_string);
+ }
+
+ std::string remainder;
+ const std::size_t actual_cmd_name_len = cmd_obj->GetCommandName().size();
+ if (actual_cmd_name_len < command_string.length())
+ remainder = command_string.substr(actual_cmd_name_len);
+
+ // Remove any initial spaces
+ size_t pos = remainder.find_first_not_of(k_white_space);
+ if (pos != 0 && pos != std::string::npos)
+ remainder.erase(0, pos);
+
+ if (log)
+ log->Printf(
+ "HandleCommand, command line after removing command name(s): '%s'",
+ remainder.c_str());
+
+ cmd_obj->Execute(remainder.c_str(), result);
+ }
+
+ if (log)
+ log->Printf("HandleCommand, command %s",
+ (result.Succeeded() ? "succeeded" : "did not succeed"));
+
+ return result.Succeeded();
+}
+
+int CommandInterpreter::HandleCompletionMatches(CompletionRequest &request) {
+ int num_command_matches = 0;
+ bool look_for_subcommand = false;
+
+ // For any of the command completions a unique match will be a complete word.
+ request.SetWordComplete(true);
+
+ if (request.GetCursorIndex() == -1) {
+ // We got nothing on the command line, so return the list of commands
+ bool include_aliases = true;
+ StringList new_matches, descriptions;
+ num_command_matches = GetCommandNamesMatchingPartialString(
+ "", include_aliases, new_matches, descriptions);
+ request.AddCompletions(new_matches, descriptions);
+ } else if (request.GetCursorIndex() == 0) {
+ // The cursor is in the first argument, so just do a lookup in the
+ // dictionary.
+ StringList new_matches, new_descriptions;
+ CommandObject *cmd_obj =
+ GetCommandObject(request.GetParsedLine().GetArgumentAtIndex(0),
+ &new_matches, &new_descriptions);
+
+ if (num_command_matches == 1 && cmd_obj && cmd_obj->IsMultiwordObject() &&
+ new_matches.GetStringAtIndex(0) != nullptr &&
+ strcmp(request.GetParsedLine().GetArgumentAtIndex(0),
+ new_matches.GetStringAtIndex(0)) == 0) {
+ if (request.GetParsedLine().GetArgumentCount() == 1) {
+ request.SetWordComplete(true);
+ } else {
+ look_for_subcommand = true;
+ num_command_matches = 0;
+ new_matches.DeleteStringAtIndex(0);
+ new_descriptions.DeleteStringAtIndex(0);
+ request.GetParsedLine().AppendArgument(llvm::StringRef());
+ request.SetCursorIndex(request.GetCursorIndex() + 1);
+ request.SetCursorCharPosition(0);
+ }
+ }
+ request.AddCompletions(new_matches, new_descriptions);
+ num_command_matches = request.GetNumberOfMatches();
+ }
+
+ if (request.GetCursorIndex() > 0 || look_for_subcommand) {
+ // We are completing further on into a commands arguments, so find the
+ // command and tell it to complete the command. First see if there is a
+ // matching initial command:
+ CommandObject *command_object =
+ GetCommandObject(request.GetParsedLine().GetArgumentAtIndex(0));
+ if (command_object == nullptr) {
+ return 0;
+ } else {
+ request.GetParsedLine().Shift();
+ request.SetCursorIndex(request.GetCursorIndex() - 1);
+ num_command_matches = command_object->HandleCompletion(request);
+ }
+ }
+
+ return num_command_matches;
+}
+
+int CommandInterpreter::HandleCompletion(
+ const char *current_line, const char *cursor, const char *last_char,
+ int match_start_point, int max_return_elements, StringList &matches,
+ StringList &descriptions) {
+
+ llvm::StringRef command_line(current_line, last_char - current_line);
+ CompletionResult result;
+ CompletionRequest request(command_line, cursor - current_line,
+ match_start_point, max_return_elements, result);
+ // Don't complete comments, and if the line we are completing is just the
+ // history repeat character, substitute the appropriate history line.
+ const char *first_arg = request.GetParsedLine().GetArgumentAtIndex(0);
+ if (first_arg) {
+ if (first_arg[0] == m_comment_char)
+ return 0;
+ else if (first_arg[0] == CommandHistory::g_repeat_char) {
+ if (auto hist_str = m_command_history.FindString(first_arg)) {
+ matches.InsertStringAtIndex(0, *hist_str);
+ descriptions.InsertStringAtIndex(0, "Previous command history event");
+ return -2;
+ } else
+ return 0;
+ }
+ }
+
+ // Only max_return_elements == -1 is supported at present:
+ lldbassert(max_return_elements == -1);
+
+ int num_command_matches = HandleCompletionMatches(request);
+ result.GetMatches(matches);
+ result.GetDescriptions(descriptions);
+
+ if (num_command_matches <= 0)
+ return num_command_matches;
+
+ if (request.GetParsedLine().GetArgumentCount() == 0) {
+ // If we got an empty string, insert nothing.
+ matches.InsertStringAtIndex(0, "");
+ descriptions.InsertStringAtIndex(0, "");
+ } else {
+ // Now figure out if there is a common substring, and if so put that in
+ // element 0, otherwise put an empty string in element 0.
+ std::string command_partial_str = request.GetCursorArgumentPrefix().str();
+
+ std::string common_prefix;
+ matches.LongestCommonPrefix(common_prefix);
+ const size_t partial_name_len = command_partial_str.size();
+ common_prefix.erase(0, partial_name_len);
+
+ // If we matched a unique single command, add a space... Only do this if
+ // the completer told us this was a complete word, however...
+ if (num_command_matches == 1 && request.GetWordComplete()) {
+ char quote_char = request.GetParsedLine()[request.GetCursorIndex()].quote;
+ common_prefix =
+ Args::EscapeLLDBCommandArgument(common_prefix, quote_char);
+ if (quote_char != '\0')
+ common_prefix.push_back(quote_char);
+ common_prefix.push_back(' ');
+ }
+ matches.InsertStringAtIndex(0, common_prefix.c_str());
+ descriptions.InsertStringAtIndex(0, "");
+ }
+ return num_command_matches;
+}
+
+CommandInterpreter::~CommandInterpreter() {}
+
+void CommandInterpreter::UpdatePrompt(llvm::StringRef new_prompt) {
+ EventSP prompt_change_event_sp(
+ new Event(eBroadcastBitResetPrompt, new EventDataBytes(new_prompt)));
+ ;
+ BroadcastEvent(prompt_change_event_sp);
+ if (m_command_io_handler_sp)
+ m_command_io_handler_sp->SetPrompt(new_prompt);
+}
+
+bool CommandInterpreter::Confirm(llvm::StringRef message, bool default_answer) {
+ // Check AutoConfirm first:
+ if (m_debugger.GetAutoConfirm())
+ return default_answer;
+
+ IOHandlerConfirm *confirm =
+ new IOHandlerConfirm(m_debugger, message, default_answer);
+ IOHandlerSP io_handler_sp(confirm);
+ m_debugger.RunIOHandler(io_handler_sp);
+ return confirm->GetResponse();
+}
+
+const CommandAlias *
+CommandInterpreter::GetAlias(llvm::StringRef alias_name) const {
+ OptionArgVectorSP ret_val;
+
+ auto pos = m_alias_dict.find(alias_name);
+ if (pos != m_alias_dict.end())
+ return (CommandAlias *)pos->second.get();
+
+ return nullptr;
+}
+
+bool CommandInterpreter::HasCommands() const { return (!m_command_dict.empty()); }
+
+bool CommandInterpreter::HasAliases() const { return (!m_alias_dict.empty()); }
+
+bool CommandInterpreter::HasUserCommands() const { return (!m_user_dict.empty()); }
+
+bool CommandInterpreter::HasAliasOptions() const { return HasAliases(); }
+
+void CommandInterpreter::BuildAliasCommandArgs(CommandObject *alias_cmd_obj,
+ const char *alias_name,
+ Args &cmd_args,
+ std::string &raw_input_string,
+ CommandReturnObject &result) {
+ OptionArgVectorSP option_arg_vector_sp =
+ GetAlias(alias_name)->GetOptionArguments();
+
+ bool wants_raw_input = alias_cmd_obj->WantsRawCommandString();
+
+ // Make sure that the alias name is the 0th element in cmd_args
+ std::string alias_name_str = alias_name;
+ if (alias_name_str != cmd_args.GetArgumentAtIndex(0))
+ cmd_args.Unshift(alias_name_str);
+
+ Args new_args(alias_cmd_obj->GetCommandName());
+ if (new_args.GetArgumentCount() == 2)
+ new_args.Shift();
+
+ if (option_arg_vector_sp.get()) {
+ if (wants_raw_input) {
+ // We have a command that both has command options and takes raw input.
+ // Make *sure* it has a " -- " in the right place in the
+ // raw_input_string.
+ size_t pos = raw_input_string.find(" -- ");
+ if (pos == std::string::npos) {
+ // None found; assume it goes at the beginning of the raw input string
+ raw_input_string.insert(0, " -- ");
+ }
+ }
+
+ OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
+ const size_t old_size = cmd_args.GetArgumentCount();
+ std::vector<bool> used(old_size + 1, false);
+
+ used[0] = true;
+
+ int value_type;
+ std::string option;
+ std::string value;
+ for (const auto &option_entry : *option_arg_vector) {
+ std::tie(option, value_type, value) = option_entry;
+ if (option == "<argument>") {
+ if (!wants_raw_input || (value != "--")) {
+ // Since we inserted this above, make sure we don't insert it twice
+ new_args.AppendArgument(value);
+ }
+ continue;
+ }
+
+ if (value_type != OptionParser::eOptionalArgument)
+ new_args.AppendArgument(option);
+
+ if (value == "<no-argument>")
+ continue;
+
+ int index = GetOptionArgumentPosition(value.c_str());
+ if (index == 0) {
+ // value was NOT a positional argument; must be a real value
+ if (value_type != OptionParser::eOptionalArgument)
+ new_args.AppendArgument(value);
+ else {
+ char buffer[255];
+ ::snprintf(buffer, sizeof(buffer), "%s%s", option.c_str(),
+ value.c_str());
+ new_args.AppendArgument(llvm::StringRef(buffer));
+ }
+
+ } else if (static_cast<size_t>(index) >= cmd_args.GetArgumentCount()) {
+ result.AppendErrorWithFormat("Not enough arguments provided; you "
+ "need at least %d arguments to use "
+ "this alias.\n",
+ index);
+ result.SetStatus(eReturnStatusFailed);
+ return;
+ } else {
+ // Find and remove cmd_args.GetArgumentAtIndex(i) from raw_input_string
+ size_t strpos =
+ raw_input_string.find(cmd_args.GetArgumentAtIndex(index));
+ if (strpos != std::string::npos) {
+ raw_input_string = raw_input_string.erase(
+ strpos, strlen(cmd_args.GetArgumentAtIndex(index)));
+ }
+
+ if (value_type != OptionParser::eOptionalArgument)
+ new_args.AppendArgument(cmd_args.GetArgumentAtIndex(index));
+ else {
+ char buffer[255];
+ ::snprintf(buffer, sizeof(buffer), "%s%s", option.c_str(),
+ cmd_args.GetArgumentAtIndex(index));
+ new_args.AppendArgument(buffer);
+ }
+ used[index] = true;
+ }
+ }
+
+ for (auto entry : llvm::enumerate(cmd_args.entries())) {
+ if (!used[entry.index()] && !wants_raw_input)
+ new_args.AppendArgument(entry.value().ref);
+ }
+
+ cmd_args.Clear();
+ cmd_args.SetArguments(new_args.GetArgumentCount(),
+ new_args.GetConstArgumentVector());
+ } else {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ // This alias was not created with any options; nothing further needs to be
+ // done, unless it is a command that wants raw input, in which case we need
+ // to clear the rest of the data from cmd_args, since its in the raw input
+ // string.
+ if (wants_raw_input) {
+ cmd_args.Clear();
+ cmd_args.SetArguments(new_args.GetArgumentCount(),
+ new_args.GetConstArgumentVector());
+ }
+ return;
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return;
+}
+
+int CommandInterpreter::GetOptionArgumentPosition(const char *in_string) {
+ int position = 0; // Any string that isn't an argument position, i.e. '%'
+ // followed by an integer, gets a position
+ // of zero.
+
+ const char *cptr = in_string;
+
+ // Does it start with '%'
+ if (cptr[0] == '%') {
+ ++cptr;
+
+ // Is the rest of it entirely digits?
+ if (isdigit(cptr[0])) {
+ const char *start = cptr;
+ while (isdigit(cptr[0]))
+ ++cptr;
+
+ // We've gotten to the end of the digits; are we at the end of the
+ // string?
+ if (cptr[0] == '\0')
+ position = atoi(start);
+ }
+ }
+
+ return position;
+}
+
+static void GetHomeInitFile(llvm::SmallVectorImpl<char> &init_file,
+ llvm::StringRef suffix = {}) {
+ std::string init_file_name = ".lldbinit";
+ if (!suffix.empty()) {
+ init_file_name.append("-");
+ init_file_name.append(suffix.str());
+ }
+
+ llvm::sys::path::home_directory(init_file);
+ llvm::sys::path::append(init_file, init_file_name);
+
+ FileSystem::Instance().Resolve(init_file);
+}
+
+static void GetCwdInitFile(llvm::SmallVectorImpl<char> &init_file) {
+ llvm::StringRef s = ".lldbinit";
+ init_file.assign(s.begin(), s.end());
+ FileSystem::Instance().Resolve(init_file);
+}
+
+static LoadCWDlldbinitFile ShouldLoadCwdInitFile() {
+ lldb::TargetPropertiesSP properties = Target::GetGlobalProperties();
+ if (!properties)
+ return eLoadCWDlldbinitFalse;
+ return properties->GetLoadCWDlldbinitFile();
+}
+
+void CommandInterpreter::SourceInitFile(FileSpec file,
+ CommandReturnObject &result) {
+ assert(!m_skip_lldbinit_files);
+
+ if (!FileSystem::Instance().Exists(file)) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return;
+ }
+
+ // Use HandleCommand to 'source' the given file; this will do the actual
+ // broadcasting of the commands back to any appropriate listener (see
+ // CommandObjectSource::Execute for more details).
+ const bool saved_batch = SetBatchCommandMode(true);
+ ExecutionContext *ctx = nullptr;
+ CommandInterpreterRunOptions options;
+ options.SetSilent(true);
+ options.SetPrintErrors(true);
+ options.SetStopOnError(false);
+ options.SetStopOnContinue(true);
+ HandleCommandsFromFile(file, ctx, options, result);
+ SetBatchCommandMode(saved_batch);
+}
+
+void CommandInterpreter::SourceInitFileCwd(CommandReturnObject &result) {
+ if (m_skip_lldbinit_files) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return;
+ }
+
+ llvm::SmallString<128> init_file;
+ GetCwdInitFile(init_file);
+ if (!FileSystem::Instance().Exists(init_file)) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return;
+ }
+
+ LoadCWDlldbinitFile should_load = ShouldLoadCwdInitFile();
+
+ switch (should_load) {
+ case eLoadCWDlldbinitFalse:
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ break;
+ case eLoadCWDlldbinitTrue:
+ SourceInitFile(FileSpec(init_file.str()), result);
+ break;
+ case eLoadCWDlldbinitWarn: {
+ llvm::SmallString<128> home_init_file;
+ GetHomeInitFile(home_init_file);
+ if (llvm::sys::path::parent_path(init_file) ==
+ llvm::sys::path::parent_path(home_init_file)) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendErrorWithFormat(InitFileWarning);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ }
+}
+
+/// We will first see if there is an application specific ".lldbinit" file
+/// whose name is "~/.lldbinit" followed by a "-" and the name of the program.
+/// If this file doesn't exist, we fall back to just the "~/.lldbinit" file.
+void CommandInterpreter::SourceInitFileHome(CommandReturnObject &result) {
+ if (m_skip_lldbinit_files) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return;
+ }
+
+ llvm::SmallString<128> init_file;
+ GetHomeInitFile(init_file);
+
+ if (!m_skip_app_init_files) {
+ llvm::StringRef program_name =
+ HostInfo::GetProgramFileSpec().GetFilename().GetStringRef();
+ llvm::SmallString<128> program_init_file;
+ GetHomeInitFile(program_init_file, program_name);
+ if (FileSystem::Instance().Exists(program_init_file))
+ init_file = program_init_file;
+ }
+
+ SourceInitFile(FileSpec(init_file.str()), result);
+}
+
+const char *CommandInterpreter::GetCommandPrefix() {
+ const char *prefix = GetDebugger().GetIOHandlerCommandPrefix();
+ return prefix == nullptr ? "" : prefix;
+}
+
+PlatformSP CommandInterpreter::GetPlatform(bool prefer_target_platform) {
+ PlatformSP platform_sp;
+ if (prefer_target_platform) {
+ ExecutionContext exe_ctx(GetExecutionContext());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target)
+ platform_sp = target->GetPlatform();
+ }
+
+ if (!platform_sp)
+ platform_sp = m_debugger.GetPlatformList().GetSelectedPlatform();
+ return platform_sp;
+}
+
+void CommandInterpreter::HandleCommands(const StringList &commands,
+ ExecutionContext *override_context,
+ CommandInterpreterRunOptions &options,
+ CommandReturnObject &result) {
+ size_t num_lines = commands.GetSize();
+
+ // If we are going to continue past a "continue" then we need to run the
+ // commands synchronously. Make sure you reset this value anywhere you return
+ // from the function.
+
+ bool old_async_execution = m_debugger.GetAsyncExecution();
+
+ // If we've been given an execution context, set it at the start, but don't
+ // keep resetting it or we will cause series of commands that change the
+ // context, then do an operation that relies on that context to fail.
+
+ if (override_context != nullptr)
+ UpdateExecutionContext(override_context);
+
+ if (!options.GetStopOnContinue()) {
+ m_debugger.SetAsyncExecution(false);
+ }
+
+ for (size_t idx = 0; idx < num_lines && !WasInterrupted(); idx++) {
+ const char *cmd = commands.GetStringAtIndex(idx);
+ if (cmd[0] == '\0')
+ continue;
+
+ if (options.GetEchoCommands()) {
+ // TODO: Add Stream support.
+ result.AppendMessageWithFormat("%s %s\n",
+ m_debugger.GetPrompt().str().c_str(), cmd);
+ }
+
+ CommandReturnObject tmp_result;
+ // If override_context is not NULL, pass no_context_switching = true for
+ // HandleCommand() since we updated our context already.
+
+ // We might call into a regex or alias command, in which case the
+ // add_to_history will get lost. This m_command_source_depth dingus is the
+ // way we turn off adding to the history in that case, so set it up here.
+ if (!options.GetAddToHistory())
+ m_command_source_depth++;
+ bool success =
+ HandleCommand(cmd, options.m_add_to_history, tmp_result,
+ nullptr, /* override_context */
+ true, /* repeat_on_empty_command */
+ override_context != nullptr /* no_context_switching */);
+ if (!options.GetAddToHistory())
+ m_command_source_depth--;
+
+ if (options.GetPrintResults()) {
+ if (tmp_result.Succeeded())
+ result.AppendMessage(tmp_result.GetOutputData());
+ }
+
+ if (!success || !tmp_result.Succeeded()) {
+ llvm::StringRef error_msg = tmp_result.GetErrorData();
+ if (error_msg.empty())
+ error_msg = "<unknown error>.\n";
+ if (options.GetStopOnError()) {
+ result.AppendErrorWithFormat(
+ "Aborting reading of commands after command #%" PRIu64
+ ": '%s' failed with %s",
+ (uint64_t)idx, cmd, error_msg.str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ m_debugger.SetAsyncExecution(old_async_execution);
+ return;
+ } else if (options.GetPrintResults()) {
+ result.AppendMessageWithFormat(
+ "Command #%" PRIu64 " '%s' failed with %s", (uint64_t)idx + 1, cmd,
+ error_msg.str().c_str());
+ }
+ }
+
+ if (result.GetImmediateOutputStream())
+ result.GetImmediateOutputStream()->Flush();
+
+ if (result.GetImmediateErrorStream())
+ result.GetImmediateErrorStream()->Flush();
+
+ // N.B. Can't depend on DidChangeProcessState, because the state coming
+ // into the command execution could be running (for instance in Breakpoint
+ // Commands. So we check the return value to see if it is has running in
+ // it.
+ if ((tmp_result.GetStatus() == eReturnStatusSuccessContinuingNoResult) ||
+ (tmp_result.GetStatus() == eReturnStatusSuccessContinuingResult)) {
+ if (options.GetStopOnContinue()) {
+ // If we caused the target to proceed, and we're going to stop in that
+ // case, set the status in our real result before returning. This is
+ // an error if the continue was not the last command in the set of
+ // commands to be run.
+ if (idx != num_lines - 1)
+ result.AppendErrorWithFormat(
+ "Aborting reading of commands after command #%" PRIu64
+ ": '%s' continued the target.\n",
+ (uint64_t)idx + 1, cmd);
+ else
+ result.AppendMessageWithFormat("Command #%" PRIu64
+ " '%s' continued the target.\n",
+ (uint64_t)idx + 1, cmd);
+
+ result.SetStatus(tmp_result.GetStatus());
+ m_debugger.SetAsyncExecution(old_async_execution);
+
+ return;
+ }
+ }
+
+ // Also check for "stop on crash here:
+ bool should_stop = false;
+ if (tmp_result.GetDidChangeProcessState() && options.GetStopOnCrash()) {
+ TargetSP target_sp(m_debugger.GetTargetList().GetSelectedTarget());
+ if (target_sp) {
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp) {
+ for (ThreadSP thread_sp : process_sp->GetThreadList().Threads()) {
+ StopReason reason = thread_sp->GetStopReason();
+ if (reason == eStopReasonSignal || reason == eStopReasonException ||
+ reason == eStopReasonInstrumentation) {
+ should_stop = true;
+ break;
+ }
+ }
+ }
+ }
+ if (should_stop) {
+ if (idx != num_lines - 1)
+ result.AppendErrorWithFormat(
+ "Aborting reading of commands after command #%" PRIu64
+ ": '%s' stopped with a signal or exception.\n",
+ (uint64_t)idx + 1, cmd);
+ else
+ result.AppendMessageWithFormat(
+ "Command #%" PRIu64 " '%s' stopped with a signal or exception.\n",
+ (uint64_t)idx + 1, cmd);
+
+ result.SetStatus(tmp_result.GetStatus());
+ m_debugger.SetAsyncExecution(old_async_execution);
+
+ return;
+ }
+ }
+ }
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ m_debugger.SetAsyncExecution(old_async_execution);
+
+ return;
+}
+
+// Make flags that we can pass into the IOHandler so our delegates can do the
+// right thing
+enum {
+ eHandleCommandFlagStopOnContinue = (1u << 0),
+ eHandleCommandFlagStopOnError = (1u << 1),
+ eHandleCommandFlagEchoCommand = (1u << 2),
+ eHandleCommandFlagEchoCommentCommand = (1u << 3),
+ eHandleCommandFlagPrintResult = (1u << 4),
+ eHandleCommandFlagPrintErrors = (1u << 5),
+ eHandleCommandFlagStopOnCrash = (1u << 6)
+};
+
+void CommandInterpreter::HandleCommandsFromFile(
+ FileSpec &cmd_file, ExecutionContext *context,
+ CommandInterpreterRunOptions &options, CommandReturnObject &result) {
+ if (!FileSystem::Instance().Exists(cmd_file)) {
+ result.AppendErrorWithFormat(
+ "Error reading commands from file %s - file not found.\n",
+ cmd_file.GetFilename().AsCString("<Unknown>"));
+ result.SetStatus(eReturnStatusFailed);
+ return;
+ }
+
+ StreamFileSP input_file_sp(new StreamFile());
+ std::string cmd_file_path = cmd_file.GetPath();
+ Status error = FileSystem::Instance().Open(input_file_sp->GetFile(), cmd_file,
+ File::eOpenOptionRead);
+
+ if (error.Fail()) {
+ result.AppendErrorWithFormat(
+ "error: an error occurred read file '%s': %s\n", cmd_file_path.c_str(),
+ error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return;
+ }
+
+ Debugger &debugger = GetDebugger();
+
+ uint32_t flags = 0;
+
+ if (options.m_stop_on_continue == eLazyBoolCalculate) {
+ if (m_command_source_flags.empty()) {
+ // Stop on continue by default
+ flags |= eHandleCommandFlagStopOnContinue;
+ } else if (m_command_source_flags.back() &
+ eHandleCommandFlagStopOnContinue) {
+ flags |= eHandleCommandFlagStopOnContinue;
+ }
+ } else if (options.m_stop_on_continue == eLazyBoolYes) {
+ flags |= eHandleCommandFlagStopOnContinue;
+ }
+
+ if (options.m_stop_on_error == eLazyBoolCalculate) {
+ if (m_command_source_flags.empty()) {
+ if (GetStopCmdSourceOnError())
+ flags |= eHandleCommandFlagStopOnError;
+ } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnError) {
+ flags |= eHandleCommandFlagStopOnError;
+ }
+ } else if (options.m_stop_on_error == eLazyBoolYes) {
+ flags |= eHandleCommandFlagStopOnError;
+ }
+
+ // stop-on-crash can only be set, if it is present in all levels of
+ // pushed flag sets.
+ if (options.GetStopOnCrash()) {
+ if (m_command_source_flags.empty()) {
+ flags |= eHandleCommandFlagStopOnCrash;
+ } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnCrash) {
+ flags |= eHandleCommandFlagStopOnCrash;
+ }
+ }
+
+ if (options.m_echo_commands == eLazyBoolCalculate) {
+ if (m_command_source_flags.empty()) {
+ // Echo command by default
+ flags |= eHandleCommandFlagEchoCommand;
+ } else if (m_command_source_flags.back() & eHandleCommandFlagEchoCommand) {
+ flags |= eHandleCommandFlagEchoCommand;
+ }
+ } else if (options.m_echo_commands == eLazyBoolYes) {
+ flags |= eHandleCommandFlagEchoCommand;
+ }
+
+ // We will only ever ask for this flag, if we echo commands in general.
+ if (options.m_echo_comment_commands == eLazyBoolCalculate) {
+ if (m_command_source_flags.empty()) {
+ // Echo comments by default
+ flags |= eHandleCommandFlagEchoCommentCommand;
+ } else if (m_command_source_flags.back() &
+ eHandleCommandFlagEchoCommentCommand) {
+ flags |= eHandleCommandFlagEchoCommentCommand;
+ }
+ } else if (options.m_echo_comment_commands == eLazyBoolYes) {
+ flags |= eHandleCommandFlagEchoCommentCommand;
+ }
+
+ if (options.m_print_results == eLazyBoolCalculate) {
+ if (m_command_source_flags.empty()) {
+ // Print output by default
+ flags |= eHandleCommandFlagPrintResult;
+ } else if (m_command_source_flags.back() & eHandleCommandFlagPrintResult) {
+ flags |= eHandleCommandFlagPrintResult;
+ }
+ } else if (options.m_print_results == eLazyBoolYes) {
+ flags |= eHandleCommandFlagPrintResult;
+ }
+
+ if (options.m_print_errors == eLazyBoolCalculate) {
+ if (m_command_source_flags.empty()) {
+ // Print output by default
+ flags |= eHandleCommandFlagPrintErrors;
+ } else if (m_command_source_flags.back() & eHandleCommandFlagPrintErrors) {
+ flags |= eHandleCommandFlagPrintErrors;
+ }
+ } else if (options.m_print_errors == eLazyBoolYes) {
+ flags |= eHandleCommandFlagPrintErrors;
+ }
+
+ if (flags & eHandleCommandFlagPrintResult) {
+ debugger.GetOutputFile()->Printf("Executing commands in '%s'.\n",
+ cmd_file_path.c_str());
+ }
+
+ // Used for inheriting the right settings when "command source" might
+ // have nested "command source" commands
+ lldb::StreamFileSP empty_stream_sp;
+ m_command_source_flags.push_back(flags);
+ IOHandlerSP io_handler_sp(new IOHandlerEditline(
+ debugger, IOHandler::Type::CommandInterpreter, input_file_sp,
+ empty_stream_sp, // Pass in an empty stream so we inherit the top
+ // input reader output stream
+ empty_stream_sp, // Pass in an empty stream so we inherit the top
+ // input reader error stream
+ flags,
+ nullptr, // Pass in NULL for "editline_name" so no history is saved,
+ // or written
+ debugger.GetPrompt(), llvm::StringRef(),
+ false, // Not multi-line
+ debugger.GetUseColor(), 0, *this, nullptr));
+ const bool old_async_execution = debugger.GetAsyncExecution();
+
+ // Set synchronous execution if we are not stopping on continue
+ if ((flags & eHandleCommandFlagStopOnContinue) == 0)
+ debugger.SetAsyncExecution(false);
+
+ m_command_source_depth++;
+
+ debugger.RunIOHandler(io_handler_sp);
+ if (!m_command_source_flags.empty())
+ m_command_source_flags.pop_back();
+ m_command_source_depth--;
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ debugger.SetAsyncExecution(old_async_execution);
+}
+
+bool CommandInterpreter::GetSynchronous() { return m_synchronous_execution; }
+
+void CommandInterpreter::SetSynchronous(bool value) {
+ m_synchronous_execution = value;
+}
+
+void CommandInterpreter::OutputFormattedHelpText(Stream &strm,
+ llvm::StringRef prefix,
+ llvm::StringRef help_text) {
+ const uint32_t max_columns = m_debugger.GetTerminalWidth();
+
+ size_t line_width_max = max_columns - prefix.size();
+ if (line_width_max < 16)
+ line_width_max = help_text.size() + prefix.size();
+
+ strm.IndentMore(prefix.size());
+ bool prefixed_yet = false;
+ while (!help_text.empty()) {
+ // Prefix the first line, indent subsequent lines to line up
+ if (!prefixed_yet) {
+ strm << prefix;
+ prefixed_yet = true;
+ } else
+ strm.Indent();
+
+ // Never print more than the maximum on one line.
+ llvm::StringRef this_line = help_text.substr(0, line_width_max);
+
+ // Always break on an explicit newline.
+ std::size_t first_newline = this_line.find_first_of("\n");
+
+ // Don't break on space/tab unless the text is too long to fit on one line.
+ std::size_t last_space = llvm::StringRef::npos;
+ if (this_line.size() != help_text.size())
+ last_space = this_line.find_last_of(" \t");
+
+ // Break at whichever condition triggered first.
+ this_line = this_line.substr(0, std::min(first_newline, last_space));
+ strm.PutCString(this_line);
+ strm.EOL();
+
+ // Remove whitespace / newlines after breaking.
+ help_text = help_text.drop_front(this_line.size()).ltrim();
+ }
+ strm.IndentLess(prefix.size());
+}
+
+void CommandInterpreter::OutputFormattedHelpText(Stream &strm,
+ llvm::StringRef word_text,
+ llvm::StringRef separator,
+ llvm::StringRef help_text,
+ size_t max_word_len) {
+ StreamString prefix_stream;
+ prefix_stream.Printf(" %-*s %*s ", (int)max_word_len, word_text.data(),
+ (int)separator.size(), separator.data());
+ OutputFormattedHelpText(strm, prefix_stream.GetString(), help_text);
+}
+
+void CommandInterpreter::OutputHelpText(Stream &strm, llvm::StringRef word_text,
+ llvm::StringRef separator,
+ llvm::StringRef help_text,
+ uint32_t max_word_len) {
+ int indent_size = max_word_len + separator.size() + 2;
+
+ strm.IndentMore(indent_size);
+
+ StreamString text_strm;
+ text_strm.Printf("%-*s ", (int)max_word_len, word_text.data());
+ text_strm << separator << " " << help_text;
+
+ const uint32_t max_columns = m_debugger.GetTerminalWidth();
+
+ llvm::StringRef text = text_strm.GetString();
+
+ uint32_t chars_left = max_columns;
+
+ auto nextWordLength = [](llvm::StringRef S) {
+ size_t pos = S.find(' ');
+ return pos == llvm::StringRef::npos ? S.size() : pos;
+ };
+
+ while (!text.empty()) {
+ if (text.front() == '\n' ||
+ (text.front() == ' ' && nextWordLength(text.ltrim(' ')) > chars_left)) {
+ strm.EOL();
+ strm.Indent();
+ chars_left = max_columns - indent_size;
+ if (text.front() == '\n')
+ text = text.drop_front();
+ else
+ text = text.ltrim(' ');
+ } else {
+ strm.PutChar(text.front());
+ --chars_left;
+ text = text.drop_front();
+ }
+ }
+
+ strm.EOL();
+ strm.IndentLess(indent_size);
+}
+
+void CommandInterpreter::FindCommandsForApropos(
+ llvm::StringRef search_word, StringList &commands_found,
+ StringList &commands_help, CommandObject::CommandMap &command_map) {
+ CommandObject::CommandMap::const_iterator pos;
+
+ for (pos = command_map.begin(); pos != command_map.end(); ++pos) {
+ llvm::StringRef command_name = pos->first;
+ CommandObject *cmd_obj = pos->second.get();
+
+ const bool search_short_help = true;
+ const bool search_long_help = false;
+ const bool search_syntax = false;
+ const bool search_options = false;
+ if (command_name.contains_lower(search_word) ||
+ cmd_obj->HelpTextContainsWord(search_word, search_short_help,
+ search_long_help, search_syntax,
+ search_options)) {
+ commands_found.AppendString(cmd_obj->GetCommandName());
+ commands_help.AppendString(cmd_obj->GetHelp());
+ }
+
+ if (cmd_obj->IsMultiwordObject()) {
+ CommandObjectMultiword *cmd_multiword = cmd_obj->GetAsMultiwordCommand();
+ FindCommandsForApropos(search_word, commands_found, commands_help,
+ cmd_multiword->GetSubcommandDictionary());
+ }
+ }
+}
+
+void CommandInterpreter::FindCommandsForApropos(llvm::StringRef search_word,
+ StringList &commands_found,
+ StringList &commands_help,
+ bool search_builtin_commands,
+ bool search_user_commands,
+ bool search_alias_commands) {
+ CommandObject::CommandMap::const_iterator pos;
+
+ if (search_builtin_commands)
+ FindCommandsForApropos(search_word, commands_found, commands_help,
+ m_command_dict);
+
+ if (search_user_commands)
+ FindCommandsForApropos(search_word, commands_found, commands_help,
+ m_user_dict);
+
+ if (search_alias_commands)
+ FindCommandsForApropos(search_word, commands_found, commands_help,
+ m_alias_dict);
+}
+
+void CommandInterpreter::UpdateExecutionContext(
+ ExecutionContext *override_context) {
+ if (override_context != nullptr) {
+ m_exe_ctx_ref = *override_context;
+ } else {
+ const bool adopt_selected = true;
+ m_exe_ctx_ref.SetTargetPtr(m_debugger.GetSelectedTarget().get(),
+ adopt_selected);
+ }
+}
+
+size_t CommandInterpreter::GetProcessOutput() {
+ // The process has stuff waiting for stderr; get it and write it out to the
+ // appropriate place.
+ char stdio_buffer[1024];
+ size_t len;
+ size_t total_bytes = 0;
+ Status error;
+ TargetSP target_sp(m_debugger.GetTargetList().GetSelectedTarget());
+ if (target_sp) {
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp) {
+ while ((len = process_sp->GetSTDOUT(stdio_buffer, sizeof(stdio_buffer),
+ error)) > 0) {
+ size_t bytes_written = len;
+ m_debugger.GetOutputFile()->Write(stdio_buffer, bytes_written);
+ total_bytes += len;
+ }
+ while ((len = process_sp->GetSTDERR(stdio_buffer, sizeof(stdio_buffer),
+ error)) > 0) {
+ size_t bytes_written = len;
+ m_debugger.GetErrorFile()->Write(stdio_buffer, bytes_written);
+ total_bytes += len;
+ }
+ }
+ }
+ return total_bytes;
+}
+
+void CommandInterpreter::StartHandlingCommand() {
+ auto idle_state = CommandHandlingState::eIdle;
+ if (m_command_state.compare_exchange_strong(
+ idle_state, CommandHandlingState::eInProgress))
+ lldbassert(m_iohandler_nesting_level == 0);
+ else
+ lldbassert(m_iohandler_nesting_level > 0);
+ ++m_iohandler_nesting_level;
+}
+
+void CommandInterpreter::FinishHandlingCommand() {
+ lldbassert(m_iohandler_nesting_level > 0);
+ if (--m_iohandler_nesting_level == 0) {
+ auto prev_state = m_command_state.exchange(CommandHandlingState::eIdle);
+ lldbassert(prev_state != CommandHandlingState::eIdle);
+ }
+}
+
+bool CommandInterpreter::InterruptCommand() {
+ auto in_progress = CommandHandlingState::eInProgress;
+ return m_command_state.compare_exchange_strong(
+ in_progress, CommandHandlingState::eInterrupted);
+}
+
+bool CommandInterpreter::WasInterrupted() const {
+ bool was_interrupted =
+ (m_command_state == CommandHandlingState::eInterrupted);
+ lldbassert(!was_interrupted || m_iohandler_nesting_level > 0);
+ return was_interrupted;
+}
+
+void CommandInterpreter::PrintCommandOutput(Stream &stream,
+ llvm::StringRef str) {
+ // Split the output into lines and poll for interrupt requests
+ const char *data = str.data();
+ size_t size = str.size();
+ while (size > 0 && !WasInterrupted()) {
+ size_t chunk_size = 0;
+ for (; chunk_size < size; ++chunk_size) {
+ lldbassert(data[chunk_size] != '\0');
+ if (data[chunk_size] == '\n') {
+ ++chunk_size;
+ break;
+ }
+ }
+ chunk_size = stream.Write(data, chunk_size);
+ lldbassert(size >= chunk_size);
+ data += chunk_size;
+ size -= chunk_size;
+ }
+ if (size > 0) {
+ stream.Printf("\n... Interrupted.\n");
+ }
+}
+
+bool CommandInterpreter::EchoCommandNonInteractive(
+ llvm::StringRef line, const Flags &io_handler_flags) const {
+ if (!io_handler_flags.Test(eHandleCommandFlagEchoCommand))
+ return false;
+
+ llvm::StringRef command = line.trim();
+ if (command.empty())
+ return true;
+
+ if (command.front() == m_comment_char)
+ return io_handler_flags.Test(eHandleCommandFlagEchoCommentCommand);
+
+ return true;
+}
+
+void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) {
+ // If we were interrupted, bail out...
+ if (WasInterrupted())
+ return;
+
+ const bool is_interactive = io_handler.GetIsInteractive();
+ if (!is_interactive) {
+ // When we are not interactive, don't execute blank lines. This will happen
+ // sourcing a commands file. We don't want blank lines to repeat the
+ // previous command and cause any errors to occur (like redefining an
+ // alias, get an error and stop parsing the commands file).
+ if (line.empty())
+ return;
+
+ // When using a non-interactive file handle (like when sourcing commands
+ // from a file) we need to echo the command out so we don't just see the
+ // command output and no command...
+ if (EchoCommandNonInteractive(line, io_handler.GetFlags()))
+ io_handler.GetOutputStreamFile()->Printf("%s%s\n", io_handler.GetPrompt(),
+ line.c_str());
+ }
+
+ StartHandlingCommand();
+
+ lldb_private::CommandReturnObject result;
+ HandleCommand(line.c_str(), eLazyBoolCalculate, result);
+
+ // Now emit the command output text from the command we just executed
+ if ((result.Succeeded() &&
+ io_handler.GetFlags().Test(eHandleCommandFlagPrintResult)) ||
+ io_handler.GetFlags().Test(eHandleCommandFlagPrintErrors)) {
+ // Display any STDOUT/STDERR _prior_ to emitting the command result text
+ GetProcessOutput();
+
+ if (!result.GetImmediateOutputStream()) {
+ llvm::StringRef output = result.GetOutputData();
+ PrintCommandOutput(*io_handler.GetOutputStreamFile(), output);
+ }
+
+ // Now emit the command error text from the command we just executed
+ if (!result.GetImmediateErrorStream()) {
+ llvm::StringRef error = result.GetErrorData();
+ PrintCommandOutput(*io_handler.GetErrorStreamFile(), error);
+ }
+ }
+
+ FinishHandlingCommand();
+
+ switch (result.GetStatus()) {
+ case eReturnStatusInvalid:
+ case eReturnStatusSuccessFinishNoResult:
+ case eReturnStatusSuccessFinishResult:
+ case eReturnStatusStarted:
+ break;
+
+ case eReturnStatusSuccessContinuingNoResult:
+ case eReturnStatusSuccessContinuingResult:
+ if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnContinue))
+ io_handler.SetIsDone(true);
+ break;
+
+ case eReturnStatusFailed:
+ m_num_errors++;
+ if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnError))
+ io_handler.SetIsDone(true);
+ break;
+
+ case eReturnStatusQuit:
+ m_quit_requested = true;
+ io_handler.SetIsDone(true);
+ break;
+ }
+
+ // Finally, if we're going to stop on crash, check that here:
+ if (!m_quit_requested && result.GetDidChangeProcessState() &&
+ io_handler.GetFlags().Test(eHandleCommandFlagStopOnCrash)) {
+ bool should_stop = false;
+ TargetSP target_sp(m_debugger.GetTargetList().GetSelectedTarget());
+ if (target_sp) {
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp) {
+ for (ThreadSP thread_sp : process_sp->GetThreadList().Threads()) {
+ StopReason reason = thread_sp->GetStopReason();
+ if ((reason == eStopReasonSignal || reason == eStopReasonException ||
+ reason == eStopReasonInstrumentation) &&
+ !result.GetAbnormalStopWasExpected()) {
+ should_stop = true;
+ break;
+ }
+ }
+ }
+ }
+ if (should_stop) {
+ io_handler.SetIsDone(true);
+ m_stopped_for_crash = true;
+ }
+ }
+}
+
+bool CommandInterpreter::IOHandlerInterrupt(IOHandler &io_handler) {
+ ExecutionContext exe_ctx(GetExecutionContext());
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (InterruptCommand())
+ return true;
+
+ if (process) {
+ StateType state = process->GetState();
+ if (StateIsRunningState(state)) {
+ process->Halt();
+ return true; // Don't do any updating when we are running
+ }
+ }
+
+ ScriptInterpreter *script_interpreter =
+ m_debugger.GetScriptInterpreter(false);
+ if (script_interpreter) {
+ if (script_interpreter->Interrupt())
+ return true;
+ }
+ return false;
+}
+
+void CommandInterpreter::GetLLDBCommandsFromIOHandler(
+ const char *prompt, IOHandlerDelegate &delegate, bool asynchronously,
+ void *baton) {
+ Debugger &debugger = GetDebugger();
+ IOHandlerSP io_handler_sp(
+ new IOHandlerEditline(debugger, IOHandler::Type::CommandList,
+ "lldb", // Name of input reader for history
+ llvm::StringRef::withNullAsEmpty(prompt), // Prompt
+ llvm::StringRef(), // Continuation prompt
+ true, // Get multiple lines
+ debugger.GetUseColor(),
+ 0, // Don't show line numbers
+ delegate, // IOHandlerDelegate
+ nullptr)); // FileShadowCollector
+
+ if (io_handler_sp) {
+ io_handler_sp->SetUserData(baton);
+ if (asynchronously)
+ debugger.PushIOHandler(io_handler_sp);
+ else
+ debugger.RunIOHandler(io_handler_sp);
+ }
+}
+
+void CommandInterpreter::GetPythonCommandsFromIOHandler(
+ const char *prompt, IOHandlerDelegate &delegate, bool asynchronously,
+ void *baton) {
+ Debugger &debugger = GetDebugger();
+ IOHandlerSP io_handler_sp(
+ new IOHandlerEditline(debugger, IOHandler::Type::PythonCode,
+ "lldb-python", // Name of input reader for history
+ llvm::StringRef::withNullAsEmpty(prompt), // Prompt
+ llvm::StringRef(), // Continuation prompt
+ true, // Get multiple lines
+ debugger.GetUseColor(),
+ 0, // Don't show line numbers
+ delegate, // IOHandlerDelegate
+ nullptr)); // FileShadowCollector
+
+ if (io_handler_sp) {
+ io_handler_sp->SetUserData(baton);
+ if (asynchronously)
+ debugger.PushIOHandler(io_handler_sp);
+ else
+ debugger.RunIOHandler(io_handler_sp);
+ }
+}
+
+bool CommandInterpreter::IsActive() {
+ return m_debugger.IsTopIOHandler(m_command_io_handler_sp);
+}
+
+lldb::IOHandlerSP
+CommandInterpreter::GetIOHandler(bool force_create,
+ CommandInterpreterRunOptions *options) {
+ // Always re-create the IOHandlerEditline in case the input changed. The old
+ // instance might have had a non-interactive input and now it does or vice
+ // versa.
+ if (force_create || !m_command_io_handler_sp) {
+ // Always re-create the IOHandlerEditline in case the input changed. The
+ // old instance might have had a non-interactive input and now it does or
+ // vice versa.
+ uint32_t flags = 0;
+
+ if (options) {
+ if (options->m_stop_on_continue == eLazyBoolYes)
+ flags |= eHandleCommandFlagStopOnContinue;
+ if (options->m_stop_on_error == eLazyBoolYes)
+ flags |= eHandleCommandFlagStopOnError;
+ if (options->m_stop_on_crash == eLazyBoolYes)
+ flags |= eHandleCommandFlagStopOnCrash;
+ if (options->m_echo_commands != eLazyBoolNo)
+ flags |= eHandleCommandFlagEchoCommand;
+ if (options->m_echo_comment_commands != eLazyBoolNo)
+ flags |= eHandleCommandFlagEchoCommentCommand;
+ if (options->m_print_results != eLazyBoolNo)
+ flags |= eHandleCommandFlagPrintResult;
+ if (options->m_print_errors != eLazyBoolNo)
+ flags |= eHandleCommandFlagPrintErrors;
+ } else {
+ flags = eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult |
+ eHandleCommandFlagPrintErrors;
+ }
+
+ m_command_io_handler_sp = std::make_shared<IOHandlerEditline>(
+ m_debugger, IOHandler::Type::CommandInterpreter,
+ m_debugger.GetInputFile(), m_debugger.GetOutputFile(),
+ m_debugger.GetErrorFile(), flags, "lldb", m_debugger.GetPrompt(),
+ llvm::StringRef(), // Continuation prompt
+ false, // Don't enable multiple line input, just single line commands
+ m_debugger.GetUseColor(),
+ 0, // Don't show line numbers
+ *this, // IOHandlerDelegate
+ GetDebugger().GetInputRecorder());
+ }
+ return m_command_io_handler_sp;
+}
+
+void CommandInterpreter::RunCommandInterpreter(
+ bool auto_handle_events, bool spawn_thread,
+ CommandInterpreterRunOptions &options) {
+ // Always re-create the command interpreter when we run it in case any file
+ // handles have changed.
+ bool force_create = true;
+ m_debugger.PushIOHandler(GetIOHandler(force_create, &options));
+ m_stopped_for_crash = false;
+
+ if (auto_handle_events)
+ m_debugger.StartEventHandlerThread();
+
+ if (spawn_thread) {
+ m_debugger.StartIOHandlerThread();
+ } else {
+ m_debugger.ExecuteIOHandlers();
+
+ if (auto_handle_events)
+ m_debugger.StopEventHandlerThread();
+ }
+}
+
+CommandObject *
+CommandInterpreter::ResolveCommandImpl(std::string &command_line,
+ CommandReturnObject &result) {
+ std::string scratch_command(command_line); // working copy so we don't modify
+ // command_line unless we succeed
+ CommandObject *cmd_obj = nullptr;
+ StreamString revised_command_line;
+ bool wants_raw_input = false;
+ size_t actual_cmd_name_len = 0;
+ std::string next_word;
+ StringList matches;
+ bool done = false;
+ while (!done) {
+ char quote_char = '\0';
+ std::string suffix;
+ ExtractCommand(scratch_command, next_word, suffix, quote_char);
+ if (cmd_obj == nullptr) {
+ std::string full_name;
+ bool is_alias = GetAliasFullName(next_word, full_name);
+ cmd_obj = GetCommandObject(next_word, &matches);
+ bool is_real_command =
+ (!is_alias) || (cmd_obj != nullptr && !cmd_obj->IsAlias());
+ if (!is_real_command) {
+ matches.Clear();
+ std::string alias_result;
+ cmd_obj =
+ BuildAliasResult(full_name, scratch_command, alias_result, result);
+ revised_command_line.Printf("%s", alias_result.c_str());
+ if (cmd_obj) {
+ wants_raw_input = cmd_obj->WantsRawCommandString();
+ actual_cmd_name_len = cmd_obj->GetCommandName().size();
+ }
+ } else {
+ if (cmd_obj) {
+ llvm::StringRef cmd_name = cmd_obj->GetCommandName();
+ actual_cmd_name_len += cmd_name.size();
+ revised_command_line.Printf("%s", cmd_name.str().c_str());
+ wants_raw_input = cmd_obj->WantsRawCommandString();
+ } else {
+ revised_command_line.Printf("%s", next_word.c_str());
+ }
+ }
+ } else {
+ if (cmd_obj->IsMultiwordObject()) {
+ CommandObject *sub_cmd_obj =
+ cmd_obj->GetSubcommandObject(next_word.c_str());
+ if (sub_cmd_obj) {
+ // The subcommand's name includes the parent command's name, so
+ // restart rather than append to the revised_command_line.
+ llvm::StringRef sub_cmd_name = sub_cmd_obj->GetCommandName();
+ actual_cmd_name_len = sub_cmd_name.size() + 1;
+ revised_command_line.Clear();
+ revised_command_line.Printf("%s", sub_cmd_name.str().c_str());
+ cmd_obj = sub_cmd_obj;
+ wants_raw_input = cmd_obj->WantsRawCommandString();
+ } else {
+ if (quote_char)
+ revised_command_line.Printf(" %c%s%s%c", quote_char,
+ next_word.c_str(), suffix.c_str(),
+ quote_char);
+ else
+ revised_command_line.Printf(" %s%s", next_word.c_str(),
+ suffix.c_str());
+ done = true;
+ }
+ } else {
+ if (quote_char)
+ revised_command_line.Printf(" %c%s%s%c", quote_char,
+ next_word.c_str(), suffix.c_str(),
+ quote_char);
+ else
+ revised_command_line.Printf(" %s%s", next_word.c_str(),
+ suffix.c_str());
+ done = true;
+ }
+ }
+
+ if (cmd_obj == nullptr) {
+ const size_t num_matches = matches.GetSize();
+ if (matches.GetSize() > 1) {
+ StreamString error_msg;
+ error_msg.Printf("Ambiguous command '%s'. Possible matches:\n",
+ next_word.c_str());
+
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i));
+ }
+ result.AppendRawError(error_msg.GetString());
+ } else {
+ // We didn't have only one match, otherwise we wouldn't get here.
+ lldbassert(num_matches == 0);
+ result.AppendErrorWithFormat("'%s' is not a valid command.\n",
+ next_word.c_str());
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return nullptr;
+ }
+
+ if (cmd_obj->IsMultiwordObject()) {
+ if (!suffix.empty()) {
+ result.AppendErrorWithFormat(
+ "command '%s' did not recognize '%s%s%s' as valid (subcommand "
+ "might be invalid).\n",
+ cmd_obj->GetCommandName().str().c_str(),
+ next_word.empty() ? "" : next_word.c_str(),
+ next_word.empty() ? " -- " : " ", suffix.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return nullptr;
+ }
+ } else {
+ // If we found a normal command, we are done
+ done = true;
+ if (!suffix.empty()) {
+ switch (suffix[0]) {
+ case '/':
+ // GDB format suffixes
+ {
+ Options *command_options = cmd_obj->GetOptions();
+ if (command_options &&
+ command_options->SupportsLongOption("gdb-format")) {
+ std::string gdb_format_option("--gdb-format=");
+ gdb_format_option += (suffix.c_str() + 1);
+
+ std::string cmd = revised_command_line.GetString();
+ size_t arg_terminator_idx = FindArgumentTerminator(cmd);
+ if (arg_terminator_idx != std::string::npos) {
+ // Insert the gdb format option before the "--" that terminates
+ // options
+ gdb_format_option.append(1, ' ');
+ cmd.insert(arg_terminator_idx, gdb_format_option);
+ revised_command_line.Clear();
+ revised_command_line.PutCString(cmd);
+ } else
+ revised_command_line.Printf(" %s", gdb_format_option.c_str());
+
+ if (wants_raw_input &&
+ FindArgumentTerminator(cmd) == std::string::npos)
+ revised_command_line.PutCString(" --");
+ } else {
+ result.AppendErrorWithFormat(
+ "the '%s' command doesn't support the --gdb-format option\n",
+ cmd_obj->GetCommandName().str().c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return nullptr;
+ }
+ }
+ break;
+
+ default:
+ result.AppendErrorWithFormat(
+ "unknown command shorthand suffix: '%s'\n", suffix.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return nullptr;
+ }
+ }
+ }
+ if (scratch_command.empty())
+ done = true;
+ }
+
+ if (!scratch_command.empty())
+ revised_command_line.Printf(" %s", scratch_command.c_str());
+
+ if (cmd_obj != nullptr)
+ command_line = revised_command_line.GetString();
+
+ return cmd_obj;
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandObject.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandObject.cpp
new file mode 100644
index 000000000000..8e493c7a326f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/CommandObject.cpp
@@ -0,0 +1,1110 @@
+//===-- CommandObject.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/CommandObject.h"
+
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "lldb/Core/Address.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Utility/ArchSpec.h"
+
+// These are for the Sourcename completers.
+// FIXME: Make a separate file for the completers.
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+
+#include "lldb/Target/Language.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObject
+
+CommandObject::CommandObject(CommandInterpreter &interpreter, llvm::StringRef name,
+ llvm::StringRef help, llvm::StringRef syntax, uint32_t flags)
+ : m_interpreter(interpreter), m_cmd_name(name),
+ m_cmd_help_short(), m_cmd_help_long(), m_cmd_syntax(), m_flags(flags),
+ m_arguments(), m_deprecated_command_override_callback(nullptr),
+ m_command_override_callback(nullptr), m_command_override_baton(nullptr) {
+ m_cmd_help_short = help;
+ m_cmd_syntax = syntax;
+}
+
+CommandObject::~CommandObject() {}
+
+Debugger &CommandObject::GetDebugger() { return m_interpreter.GetDebugger(); }
+
+llvm::StringRef CommandObject::GetHelp() { return m_cmd_help_short; }
+
+llvm::StringRef CommandObject::GetHelpLong() { return m_cmd_help_long; }
+
+llvm::StringRef CommandObject::GetSyntax() {
+ if (!m_cmd_syntax.empty())
+ return m_cmd_syntax;
+
+ StreamString syntax_str;
+ syntax_str.PutCString(GetCommandName());
+
+ if (!IsDashDashCommand() && GetOptions() != nullptr)
+ syntax_str.PutCString(" <cmd-options>");
+
+ if (!m_arguments.empty()) {
+ syntax_str.PutCString(" ");
+
+ if (!IsDashDashCommand() && WantsRawCommandString() && GetOptions() &&
+ GetOptions()->NumCommandOptions())
+ syntax_str.PutCString("-- ");
+ GetFormattedCommandArguments(syntax_str);
+ }
+ m_cmd_syntax = syntax_str.GetString();
+
+ return m_cmd_syntax;
+}
+
+llvm::StringRef CommandObject::GetCommandName() const { return m_cmd_name; }
+
+void CommandObject::SetCommandName(llvm::StringRef name) { m_cmd_name = name; }
+
+void CommandObject::SetHelp(llvm::StringRef str) { m_cmd_help_short = str; }
+
+void CommandObject::SetHelpLong(llvm::StringRef str) { m_cmd_help_long = str; }
+
+void CommandObject::SetSyntax(llvm::StringRef str) { m_cmd_syntax = str; }
+
+Options *CommandObject::GetOptions() {
+ // By default commands don't have options unless this virtual function is
+ // overridden by base classes.
+ return nullptr;
+}
+
+bool CommandObject::ParseOptions(Args &args, CommandReturnObject &result) {
+ // See if the subclass has options?
+ Options *options = GetOptions();
+ if (options != nullptr) {
+ Status error;
+
+ auto exe_ctx = GetCommandInterpreter().GetExecutionContext();
+ options->NotifyOptionParsingStarting(&exe_ctx);
+
+ const bool require_validation = true;
+ llvm::Expected<Args> args_or = options->Parse(
+ args, &exe_ctx, GetCommandInterpreter().GetPlatform(true),
+ require_validation);
+
+ if (args_or) {
+ args = std::move(*args_or);
+ error = options->NotifyOptionParsingFinished(&exe_ctx);
+ } else
+ error = args_or.takeError();
+
+ if (error.Success()) {
+ if (options->VerifyOptions(result))
+ return true;
+ } else {
+ const char *error_cstr = error.AsCString();
+ if (error_cstr) {
+ // We got an error string, lets use that
+ result.AppendError(error_cstr);
+ } else {
+ // No error string, output the usage information into result
+ options->GenerateOptionUsage(
+ result.GetErrorStream(), this,
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ }
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ return true;
+}
+
+bool CommandObject::CheckRequirements(CommandReturnObject &result) {
+ // Nothing should be stored in m_exe_ctx between running commands as
+ // m_exe_ctx has shared pointers to the target, process, thread and frame and
+ // we don't want any CommandObject instances to keep any of these objects
+ // around longer than for a single command. Every command should call
+ // CommandObject::Cleanup() after it has completed.
+ assert(!m_exe_ctx.GetTargetPtr());
+ assert(!m_exe_ctx.GetProcessPtr());
+ assert(!m_exe_ctx.GetThreadPtr());
+ assert(!m_exe_ctx.GetFramePtr());
+
+ // Lock down the interpreter's execution context prior to running the command
+ // so we guarantee the selected target, process, thread and frame can't go
+ // away during the execution
+ m_exe_ctx = m_interpreter.GetExecutionContext();
+
+ const uint32_t flags = GetFlags().Get();
+ if (flags & (eCommandRequiresTarget | eCommandRequiresProcess |
+ eCommandRequiresThread | eCommandRequiresFrame |
+ eCommandTryTargetAPILock)) {
+
+ if ((flags & eCommandRequiresTarget) && !m_exe_ctx.HasTargetScope()) {
+ result.AppendError(GetInvalidTargetDescription());
+ return false;
+ }
+
+ if ((flags & eCommandRequiresProcess) && !m_exe_ctx.HasProcessScope()) {
+ if (!m_exe_ctx.HasTargetScope())
+ result.AppendError(GetInvalidTargetDescription());
+ else
+ result.AppendError(GetInvalidProcessDescription());
+ return false;
+ }
+
+ if ((flags & eCommandRequiresThread) && !m_exe_ctx.HasThreadScope()) {
+ if (!m_exe_ctx.HasTargetScope())
+ result.AppendError(GetInvalidTargetDescription());
+ else if (!m_exe_ctx.HasProcessScope())
+ result.AppendError(GetInvalidProcessDescription());
+ else
+ result.AppendError(GetInvalidThreadDescription());
+ return false;
+ }
+
+ if ((flags & eCommandRequiresFrame) && !m_exe_ctx.HasFrameScope()) {
+ if (!m_exe_ctx.HasTargetScope())
+ result.AppendError(GetInvalidTargetDescription());
+ else if (!m_exe_ctx.HasProcessScope())
+ result.AppendError(GetInvalidProcessDescription());
+ else if (!m_exe_ctx.HasThreadScope())
+ result.AppendError(GetInvalidThreadDescription());
+ else
+ result.AppendError(GetInvalidFrameDescription());
+ return false;
+ }
+
+ if ((flags & eCommandRequiresRegContext) &&
+ (m_exe_ctx.GetRegisterContext() == nullptr)) {
+ result.AppendError(GetInvalidRegContextDescription());
+ return false;
+ }
+
+ if (flags & eCommandTryTargetAPILock) {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ if (target)
+ m_api_locker =
+ std::unique_lock<std::recursive_mutex>(target->GetAPIMutex());
+ }
+ }
+
+ if (GetFlags().AnySet(eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused)) {
+ Process *process = m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process == nullptr) {
+ // A process that is not running is considered paused.
+ if (GetFlags().Test(eCommandProcessMustBeLaunched)) {
+ result.AppendError("Process must exist.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else {
+ StateType state = process->GetState();
+ switch (state) {
+ case eStateInvalid:
+ case eStateSuspended:
+ case eStateCrashed:
+ case eStateStopped:
+ break;
+
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateDetached:
+ case eStateExited:
+ case eStateUnloaded:
+ if (GetFlags().Test(eCommandProcessMustBeLaunched)) {
+ result.AppendError("Process must be launched.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ break;
+
+ case eStateRunning:
+ case eStateStepping:
+ if (GetFlags().Test(eCommandProcessMustBePaused)) {
+ result.AppendError("Process is running. Use 'process interrupt' to "
+ "pause execution.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void CommandObject::Cleanup() {
+ m_exe_ctx.Clear();
+ if (m_api_locker.owns_lock())
+ m_api_locker.unlock();
+}
+
+int CommandObject::HandleCompletion(CompletionRequest &request) {
+ // Default implementation of WantsCompletion() is !WantsRawCommandString().
+ // Subclasses who want raw command string but desire, for example, argument
+ // completion should override WantsCompletion() to return true, instead.
+ if (WantsRawCommandString() && !WantsCompletion()) {
+ // FIXME: Abstract telling the completion to insert the completion
+ // character.
+ return -1;
+ } else {
+ // Can we do anything generic with the options?
+ Options *cur_options = GetOptions();
+ CommandReturnObject result;
+ OptionElementVector opt_element_vector;
+
+ if (cur_options != nullptr) {
+ opt_element_vector = cur_options->ParseForCompletion(
+ request.GetParsedLine(), request.GetCursorIndex());
+
+ bool handled_by_options = cur_options->HandleOptionCompletion(
+ request, opt_element_vector, GetCommandInterpreter());
+ if (handled_by_options)
+ return request.GetNumberOfMatches();
+ }
+
+ // If we got here, the last word is not an option or an option argument.
+ return HandleArgumentCompletion(request, opt_element_vector);
+ }
+}
+
+bool CommandObject::HelpTextContainsWord(llvm::StringRef search_word,
+ bool search_short_help,
+ bool search_long_help,
+ bool search_syntax,
+ bool search_options) {
+ std::string options_usage_help;
+
+ bool found_word = false;
+
+ llvm::StringRef short_help = GetHelp();
+ llvm::StringRef long_help = GetHelpLong();
+ llvm::StringRef syntax_help = GetSyntax();
+
+ if (search_short_help && short_help.contains_lower(search_word))
+ found_word = true;
+ else if (search_long_help && long_help.contains_lower(search_word))
+ found_word = true;
+ else if (search_syntax && syntax_help.contains_lower(search_word))
+ found_word = true;
+
+ if (!found_word && search_options && GetOptions() != nullptr) {
+ StreamString usage_help;
+ GetOptions()->GenerateOptionUsage(
+ usage_help, this,
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ if (!usage_help.Empty()) {
+ llvm::StringRef usage_text = usage_help.GetString();
+ if (usage_text.contains_lower(search_word))
+ found_word = true;
+ }
+ }
+
+ return found_word;
+}
+
+bool CommandObject::ParseOptionsAndNotify(Args &args,
+ CommandReturnObject &result,
+ OptionGroupOptions &group_options,
+ ExecutionContext &exe_ctx) {
+ if (!ParseOptions(args, result))
+ return false;
+
+ Status error(group_options.NotifyOptionParsingFinished(&exe_ctx));
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ return true;
+}
+
+int CommandObject::GetNumArgumentEntries() { return m_arguments.size(); }
+
+CommandObject::CommandArgumentEntry *
+CommandObject::GetArgumentEntryAtIndex(int idx) {
+ if (static_cast<size_t>(idx) < m_arguments.size())
+ return &(m_arguments[idx]);
+
+ return nullptr;
+}
+
+const CommandObject::ArgumentTableEntry *
+CommandObject::FindArgumentDataByType(CommandArgumentType arg_type) {
+ const ArgumentTableEntry *table = CommandObject::GetArgumentTable();
+
+ for (int i = 0; i < eArgTypeLastArg; ++i)
+ if (table[i].arg_type == arg_type)
+ return &(table[i]);
+
+ return nullptr;
+}
+
+void CommandObject::GetArgumentHelp(Stream &str, CommandArgumentType arg_type,
+ CommandInterpreter &interpreter) {
+ const ArgumentTableEntry *table = CommandObject::GetArgumentTable();
+ const ArgumentTableEntry *entry = &(table[arg_type]);
+
+ // The table is *supposed* to be kept in arg_type order, but someone *could*
+ // have messed it up...
+
+ if (entry->arg_type != arg_type)
+ entry = CommandObject::FindArgumentDataByType(arg_type);
+
+ if (!entry)
+ return;
+
+ StreamString name_str;
+ name_str.Printf("<%s>", entry->arg_name);
+
+ if (entry->help_function) {
+ llvm::StringRef help_text = entry->help_function();
+ if (!entry->help_function.self_formatting) {
+ interpreter.OutputFormattedHelpText(str, name_str.GetString(), "--",
+ help_text, name_str.GetSize());
+ } else {
+ interpreter.OutputHelpText(str, name_str.GetString(), "--", help_text,
+ name_str.GetSize());
+ }
+ } else
+ interpreter.OutputFormattedHelpText(str, name_str.GetString(), "--",
+ entry->help_text, name_str.GetSize());
+}
+
+const char *CommandObject::GetArgumentName(CommandArgumentType arg_type) {
+ const ArgumentTableEntry *entry =
+ &(CommandObject::GetArgumentTable()[arg_type]);
+
+ // The table is *supposed* to be kept in arg_type order, but someone *could*
+ // have messed it up...
+
+ if (entry->arg_type != arg_type)
+ entry = CommandObject::FindArgumentDataByType(arg_type);
+
+ if (entry)
+ return entry->arg_name;
+
+ return nullptr;
+}
+
+bool CommandObject::IsPairType(ArgumentRepetitionType arg_repeat_type) {
+ return (arg_repeat_type == eArgRepeatPairPlain) ||
+ (arg_repeat_type == eArgRepeatPairOptional) ||
+ (arg_repeat_type == eArgRepeatPairPlus) ||
+ (arg_repeat_type == eArgRepeatPairStar) ||
+ (arg_repeat_type == eArgRepeatPairRange) ||
+ (arg_repeat_type == eArgRepeatPairRangeOptional);
+}
+
+static CommandObject::CommandArgumentEntry
+OptSetFiltered(uint32_t opt_set_mask,
+ CommandObject::CommandArgumentEntry &cmd_arg_entry) {
+ CommandObject::CommandArgumentEntry ret_val;
+ for (unsigned i = 0; i < cmd_arg_entry.size(); ++i)
+ if (opt_set_mask & cmd_arg_entry[i].arg_opt_set_association)
+ ret_val.push_back(cmd_arg_entry[i]);
+ return ret_val;
+}
+
+// Default parameter value of opt_set_mask is LLDB_OPT_SET_ALL, which means
+// take all the argument data into account. On rare cases where some argument
+// sticks with certain option sets, this function returns the option set
+// filtered args.
+void CommandObject::GetFormattedCommandArguments(Stream &str,
+ uint32_t opt_set_mask) {
+ int num_args = m_arguments.size();
+ for (int i = 0; i < num_args; ++i) {
+ if (i > 0)
+ str.Printf(" ");
+ CommandArgumentEntry arg_entry =
+ opt_set_mask == LLDB_OPT_SET_ALL
+ ? m_arguments[i]
+ : OptSetFiltered(opt_set_mask, m_arguments[i]);
+ int num_alternatives = arg_entry.size();
+
+ if ((num_alternatives == 2) && IsPairType(arg_entry[0].arg_repetition)) {
+ const char *first_name = GetArgumentName(arg_entry[0].arg_type);
+ const char *second_name = GetArgumentName(arg_entry[1].arg_type);
+ switch (arg_entry[0].arg_repetition) {
+ case eArgRepeatPairPlain:
+ str.Printf("<%s> <%s>", first_name, second_name);
+ break;
+ case eArgRepeatPairOptional:
+ str.Printf("[<%s> <%s>]", first_name, second_name);
+ break;
+ case eArgRepeatPairPlus:
+ str.Printf("<%s> <%s> [<%s> <%s> [...]]", first_name, second_name,
+ first_name, second_name);
+ break;
+ case eArgRepeatPairStar:
+ str.Printf("[<%s> <%s> [<%s> <%s> [...]]]", first_name, second_name,
+ first_name, second_name);
+ break;
+ case eArgRepeatPairRange:
+ str.Printf("<%s_1> <%s_1> ... <%s_n> <%s_n>", first_name, second_name,
+ first_name, second_name);
+ break;
+ case eArgRepeatPairRangeOptional:
+ str.Printf("[<%s_1> <%s_1> ... <%s_n> <%s_n>]", first_name, second_name,
+ first_name, second_name);
+ break;
+ // Explicitly test for all the rest of the cases, so if new types get
+ // added we will notice the missing case statement(s).
+ case eArgRepeatPlain:
+ case eArgRepeatOptional:
+ case eArgRepeatPlus:
+ case eArgRepeatStar:
+ case eArgRepeatRange:
+ // These should not be reached, as they should fail the IsPairType test
+ // above.
+ break;
+ }
+ } else {
+ StreamString names;
+ for (int j = 0; j < num_alternatives; ++j) {
+ if (j > 0)
+ names.Printf(" | ");
+ names.Printf("%s", GetArgumentName(arg_entry[j].arg_type));
+ }
+
+ std::string name_str = names.GetString();
+ switch (arg_entry[0].arg_repetition) {
+ case eArgRepeatPlain:
+ str.Printf("<%s>", name_str.c_str());
+ break;
+ case eArgRepeatPlus:
+ str.Printf("<%s> [<%s> [...]]", name_str.c_str(), name_str.c_str());
+ break;
+ case eArgRepeatStar:
+ str.Printf("[<%s> [<%s> [...]]]", name_str.c_str(), name_str.c_str());
+ break;
+ case eArgRepeatOptional:
+ str.Printf("[<%s>]", name_str.c_str());
+ break;
+ case eArgRepeatRange:
+ str.Printf("<%s_1> .. <%s_n>", name_str.c_str(), name_str.c_str());
+ break;
+ // Explicitly test for all the rest of the cases, so if new types get
+ // added we will notice the missing case statement(s).
+ case eArgRepeatPairPlain:
+ case eArgRepeatPairOptional:
+ case eArgRepeatPairPlus:
+ case eArgRepeatPairStar:
+ case eArgRepeatPairRange:
+ case eArgRepeatPairRangeOptional:
+ // These should not be hit, as they should pass the IsPairType test
+ // above, and control should have gone into the other branch of the if
+ // statement.
+ break;
+ }
+ }
+ }
+}
+
+CommandArgumentType
+CommandObject::LookupArgumentName(llvm::StringRef arg_name) {
+ CommandArgumentType return_type = eArgTypeLastArg;
+
+ arg_name = arg_name.ltrim('<').rtrim('>');
+
+ const ArgumentTableEntry *table = GetArgumentTable();
+ for (int i = 0; i < eArgTypeLastArg; ++i)
+ if (arg_name == table[i].arg_name)
+ return_type = g_arguments_data[i].arg_type;
+
+ return return_type;
+}
+
+static llvm::StringRef RegisterNameHelpTextCallback() {
+ return "Register names can be specified using the architecture specific "
+ "names. "
+ "They can also be specified using generic names. Not all generic "
+ "entities have "
+ "registers backing them on all architectures. When they don't the "
+ "generic name "
+ "will return an error.\n"
+ "The generic names defined in lldb are:\n"
+ "\n"
+ "pc - program counter register\n"
+ "ra - return address register\n"
+ "fp - frame pointer register\n"
+ "sp - stack pointer register\n"
+ "flags - the flags register\n"
+ "arg{1-6} - integer argument passing registers.\n";
+}
+
+static llvm::StringRef BreakpointIDHelpTextCallback() {
+ return "Breakpoints are identified using major and minor numbers; the major "
+ "number corresponds to the single entity that was created with a "
+ "'breakpoint "
+ "set' command; the minor numbers correspond to all the locations that "
+ "were "
+ "actually found/set based on the major breakpoint. A full breakpoint "
+ "ID might "
+ "look like 3.14, meaning the 14th location set for the 3rd "
+ "breakpoint. You "
+ "can specify all the locations of a breakpoint by just indicating the "
+ "major "
+ "breakpoint number. A valid breakpoint ID consists either of just the "
+ "major "
+ "number, or the major number followed by a dot and the location "
+ "number (e.g. "
+ "3 or 3.2 could both be valid breakpoint IDs.)";
+}
+
+static llvm::StringRef BreakpointIDRangeHelpTextCallback() {
+ return "A 'breakpoint ID list' is a manner of specifying multiple "
+ "breakpoints. "
+ "This can be done through several mechanisms. The easiest way is to "
+ "just "
+ "enter a space-separated list of breakpoint IDs. To specify all the "
+ "breakpoint locations under a major breakpoint, you can use the major "
+ "breakpoint number followed by '.*', eg. '5.*' means all the "
+ "locations under "
+ "breakpoint 5. You can also indicate a range of breakpoints by using "
+ "<start-bp-id> - <end-bp-id>. The start-bp-id and end-bp-id for a "
+ "range can "
+ "be any valid breakpoint IDs. It is not legal, however, to specify a "
+ "range "
+ "using specific locations that cross major breakpoint numbers. I.e. "
+ "3.2 - 3.7"
+ " is legal; 2 - 5 is legal; but 3.2 - 4.4 is not legal.";
+}
+
+static llvm::StringRef BreakpointNameHelpTextCallback() {
+ return "A name that can be added to a breakpoint when it is created, or "
+ "later "
+ "on with the \"breakpoint name add\" command. "
+ "Breakpoint names can be used to specify breakpoints in all the "
+ "places breakpoint IDs "
+ "and breakpoint ID ranges can be used. As such they provide a "
+ "convenient way to group breakpoints, "
+ "and to operate on breakpoints you create without having to track the "
+ "breakpoint number. "
+ "Note, the attributes you set when using a breakpoint name in a "
+ "breakpoint command don't "
+ "adhere to the name, but instead are set individually on all the "
+ "breakpoints currently tagged with that "
+ "name. Future breakpoints "
+ "tagged with that name will not pick up the attributes previously "
+ "given using that name. "
+ "In order to distinguish breakpoint names from breakpoint IDs and "
+ "ranges, "
+ "names must start with a letter from a-z or A-Z and cannot contain "
+ "spaces, \".\" or \"-\". "
+ "Also, breakpoint names can only be applied to breakpoints, not to "
+ "breakpoint locations.";
+}
+
+static llvm::StringRef GDBFormatHelpTextCallback() {
+ return "A GDB format consists of a repeat count, a format letter and a size "
+ "letter. "
+ "The repeat count is optional and defaults to 1. The format letter is "
+ "optional "
+ "and defaults to the previous format that was used. The size letter "
+ "is optional "
+ "and defaults to the previous size that was used.\n"
+ "\n"
+ "Format letters include:\n"
+ "o - octal\n"
+ "x - hexadecimal\n"
+ "d - decimal\n"
+ "u - unsigned decimal\n"
+ "t - binary\n"
+ "f - float\n"
+ "a - address\n"
+ "i - instruction\n"
+ "c - char\n"
+ "s - string\n"
+ "T - OSType\n"
+ "A - float as hex\n"
+ "\n"
+ "Size letters include:\n"
+ "b - 1 byte (byte)\n"
+ "h - 2 bytes (halfword)\n"
+ "w - 4 bytes (word)\n"
+ "g - 8 bytes (giant)\n"
+ "\n"
+ "Example formats:\n"
+ "32xb - show 32 1 byte hexadecimal integer values\n"
+ "16xh - show 16 2 byte hexadecimal integer values\n"
+ "64 - show 64 2 byte hexadecimal integer values (format and size "
+ "from the last format)\n"
+ "dw - show 1 4 byte decimal integer value\n";
+}
+
+static llvm::StringRef FormatHelpTextCallback() {
+ static std::string help_text;
+
+ if (!help_text.empty())
+ return help_text;
+
+ StreamString sstr;
+ sstr << "One of the format names (or one-character names) that can be used "
+ "to show a variable's value:\n";
+ for (Format f = eFormatDefault; f < kNumFormats; f = Format(f + 1)) {
+ if (f != eFormatDefault)
+ sstr.PutChar('\n');
+
+ char format_char = FormatManager::GetFormatAsFormatChar(f);
+ if (format_char)
+ sstr.Printf("'%c' or ", format_char);
+
+ sstr.Printf("\"%s\"", FormatManager::GetFormatAsCString(f));
+ }
+
+ sstr.Flush();
+
+ help_text = sstr.GetString();
+
+ return help_text;
+}
+
+static llvm::StringRef LanguageTypeHelpTextCallback() {
+ static std::string help_text;
+
+ if (!help_text.empty())
+ return help_text;
+
+ StreamString sstr;
+ sstr << "One of the following languages:\n";
+
+ Language::PrintAllLanguages(sstr, " ", "\n");
+
+ sstr.Flush();
+
+ help_text = sstr.GetString();
+
+ return help_text;
+}
+
+static llvm::StringRef SummaryStringHelpTextCallback() {
+ return "A summary string is a way to extract information from variables in "
+ "order to present them using a summary.\n"
+ "Summary strings contain static text, variables, scopes and control "
+ "sequences:\n"
+ " - Static text can be any sequence of non-special characters, i.e. "
+ "anything but '{', '}', '$', or '\\'.\n"
+ " - Variables are sequences of characters beginning with ${, ending "
+ "with } and that contain symbols in the format described below.\n"
+ " - Scopes are any sequence of text between { and }. Anything "
+ "included in a scope will only appear in the output summary if there "
+ "were no errors.\n"
+ " - Control sequences are the usual C/C++ '\\a', '\\n', ..., plus "
+ "'\\$', '\\{' and '\\}'.\n"
+ "A summary string works by copying static text verbatim, turning "
+ "control sequences into their character counterpart, expanding "
+ "variables and trying to expand scopes.\n"
+ "A variable is expanded by giving it a value other than its textual "
+ "representation, and the way this is done depends on what comes after "
+ "the ${ marker.\n"
+ "The most common sequence if ${var followed by an expression path, "
+ "which is the text one would type to access a member of an aggregate "
+ "types, given a variable of that type"
+ " (e.g. if type T has a member named x, which has a member named y, "
+ "and if t is of type T, the expression path would be .x.y and the way "
+ "to fit that into a summary string would be"
+ " ${var.x.y}). You can also use ${*var followed by an expression path "
+ "and in that case the object referred by the path will be "
+ "dereferenced before being displayed."
+ " If the object is not a pointer, doing so will cause an error. For "
+ "additional details on expression paths, you can type 'help "
+ "expr-path'. \n"
+ "By default, summary strings attempt to display the summary for any "
+ "variable they reference, and if that fails the value. If neither can "
+ "be shown, nothing is displayed."
+ "In a summary string, you can also use an array index [n], or a "
+ "slice-like range [n-m]. This can have two different meanings "
+ "depending on what kind of object the expression"
+ " path refers to:\n"
+ " - if it is a scalar type (any basic type like int, float, ...) the "
+ "expression is a bitfield, i.e. the bits indicated by the indexing "
+ "operator are extracted out of the number"
+ " and displayed as an individual variable\n"
+ " - if it is an array or pointer the array items indicated by the "
+ "indexing operator are shown as the result of the variable. if the "
+ "expression is an array, real array items are"
+ " printed; if it is a pointer, the pointer-as-array syntax is used to "
+ "obtain the values (this means, the latter case can have no range "
+ "checking)\n"
+ "If you are trying to display an array for which the size is known, "
+ "you can also use [] instead of giving an exact range. This has the "
+ "effect of showing items 0 thru size - 1.\n"
+ "Additionally, a variable can contain an (optional) format code, as "
+ "in ${var.x.y%code}, where code can be any of the valid formats "
+ "described in 'help format', or one of the"
+ " special symbols only allowed as part of a variable:\n"
+ " %V: show the value of the object by default\n"
+ " %S: show the summary of the object by default\n"
+ " %@: show the runtime-provided object description (for "
+ "Objective-C, it calls NSPrintForDebugger; for C/C++ it does "
+ "nothing)\n"
+ " %L: show the location of the object (memory address or a "
+ "register name)\n"
+ " %#: show the number of children of the object\n"
+ " %T: show the type of the object\n"
+ "Another variable that you can use in summary strings is ${svar . "
+ "This sequence works exactly like ${var, including the fact that "
+ "${*svar is an allowed sequence, but uses"
+ " the object's synthetic children provider instead of the actual "
+ "objects. For instance, if you are using STL synthetic children "
+ "providers, the following summary string would"
+ " count the number of actual elements stored in an std::list:\n"
+ "type summary add -s \"${svar%#}\" -x \"std::list<\"";
+}
+
+static llvm::StringRef ExprPathHelpTextCallback() {
+ return "An expression path is the sequence of symbols that is used in C/C++ "
+ "to access a member variable of an aggregate object (class).\n"
+ "For instance, given a class:\n"
+ " class foo {\n"
+ " int a;\n"
+ " int b; .\n"
+ " foo* next;\n"
+ " };\n"
+ "the expression to read item b in the item pointed to by next for foo "
+ "aFoo would be aFoo.next->b.\n"
+ "Given that aFoo could just be any object of type foo, the string "
+ "'.next->b' is the expression path, because it can be attached to any "
+ "foo instance to achieve the effect.\n"
+ "Expression paths in LLDB include dot (.) and arrow (->) operators, "
+ "and most commands using expression paths have ways to also accept "
+ "the star (*) operator.\n"
+ "The meaning of these operators is the same as the usual one given to "
+ "them by the C/C++ standards.\n"
+ "LLDB also has support for indexing ([ ]) in expression paths, and "
+ "extends the traditional meaning of the square brackets operator to "
+ "allow bitfield extraction:\n"
+ "for objects of native types (int, float, char, ...) saying '[n-m]' "
+ "as an expression path (where n and m are any positive integers, e.g. "
+ "[3-5]) causes LLDB to extract"
+ " bits n thru m from the value of the variable. If n == m, [n] is "
+ "also allowed as a shortcut syntax. For arrays and pointers, "
+ "expression paths can only contain one index"
+ " and the meaning of the operation is the same as the one defined by "
+ "C/C++ (item extraction). Some commands extend bitfield-like syntax "
+ "for arrays and pointers with the"
+ " meaning of array slicing (taking elements n thru m inside the array "
+ "or pointed-to memory).";
+}
+
+void CommandObject::FormatLongHelpText(Stream &output_strm,
+ llvm::StringRef long_help) {
+ CommandInterpreter &interpreter = GetCommandInterpreter();
+ std::stringstream lineStream(long_help);
+ std::string line;
+ while (std::getline(lineStream, line)) {
+ if (line.empty()) {
+ output_strm << "\n";
+ continue;
+ }
+ size_t result = line.find_first_not_of(" \t");
+ if (result == std::string::npos) {
+ result = 0;
+ }
+ std::string whitespace_prefix = line.substr(0, result);
+ std::string remainder = line.substr(result);
+ interpreter.OutputFormattedHelpText(output_strm, whitespace_prefix.c_str(),
+ remainder.c_str());
+ }
+}
+
+void CommandObject::GenerateHelpText(CommandReturnObject &result) {
+ GenerateHelpText(result.GetOutputStream());
+
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+}
+
+void CommandObject::GenerateHelpText(Stream &output_strm) {
+ CommandInterpreter &interpreter = GetCommandInterpreter();
+ if (WantsRawCommandString()) {
+ std::string help_text(GetHelp());
+ help_text.append(" Expects 'raw' input (see 'help raw-input'.)");
+ interpreter.OutputFormattedHelpText(output_strm, "", "", help_text.c_str(),
+ 1);
+ } else
+ interpreter.OutputFormattedHelpText(output_strm, "", "", GetHelp(), 1);
+ output_strm << "\nSyntax: " << GetSyntax() << "\n";
+ Options *options = GetOptions();
+ if (options != nullptr) {
+ options->GenerateOptionUsage(
+ output_strm, this,
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ }
+ llvm::StringRef long_help = GetHelpLong();
+ if (!long_help.empty()) {
+ FormatLongHelpText(output_strm, long_help);
+ }
+ if (!IsDashDashCommand() && options && options->NumCommandOptions() > 0) {
+ if (WantsRawCommandString() && !WantsCompletion()) {
+ // Emit the message about using ' -- ' between the end of the command
+ // options and the raw input conditionally, i.e., only if the command
+ // object does not want completion.
+ interpreter.OutputFormattedHelpText(
+ output_strm, "", "",
+ "\nImportant Note: Because this command takes 'raw' input, if you "
+ "use any command options"
+ " you must use ' -- ' between the end of the command options and the "
+ "beginning of the raw input.",
+ 1);
+ } else if (GetNumArgumentEntries() > 0) {
+ // Also emit a warning about using "--" in case you are using a command
+ // that takes options and arguments.
+ interpreter.OutputFormattedHelpText(
+ output_strm, "", "",
+ "\nThis command takes options and free-form arguments. If your "
+ "arguments resemble"
+ " option specifiers (i.e., they start with a - or --), you must use "
+ "' -- ' between"
+ " the end of the command options and the beginning of the arguments.",
+ 1);
+ }
+ }
+}
+
+void CommandObject::AddIDsArgumentData(CommandArgumentEntry &arg,
+ CommandArgumentType ID,
+ CommandArgumentType IDRange) {
+ CommandArgumentData id_arg;
+ CommandArgumentData id_range_arg;
+
+ // Create the first variant for the first (and only) argument for this
+ // command.
+ id_arg.arg_type = ID;
+ id_arg.arg_repetition = eArgRepeatOptional;
+
+ // Create the second variant for the first (and only) argument for this
+ // command.
+ id_range_arg.arg_type = IDRange;
+ id_range_arg.arg_repetition = eArgRepeatOptional;
+
+ // The first (and only) argument for this command could be either an id or an
+ // id_range. Push both variants into the entry for the first argument for
+ // this command.
+ arg.push_back(id_arg);
+ arg.push_back(id_range_arg);
+}
+
+const char *CommandObject::GetArgumentTypeAsCString(
+ const lldb::CommandArgumentType arg_type) {
+ assert(arg_type < eArgTypeLastArg &&
+ "Invalid argument type passed to GetArgumentTypeAsCString");
+ return g_arguments_data[arg_type].arg_name;
+}
+
+const char *CommandObject::GetArgumentDescriptionAsCString(
+ const lldb::CommandArgumentType arg_type) {
+ assert(arg_type < eArgTypeLastArg &&
+ "Invalid argument type passed to GetArgumentDescriptionAsCString");
+ return g_arguments_data[arg_type].help_text;
+}
+
+Target *CommandObject::GetDummyTarget() {
+ return m_interpreter.GetDebugger().GetDummyTarget();
+}
+
+Target *CommandObject::GetSelectedOrDummyTarget(bool prefer_dummy) {
+ return m_interpreter.GetDebugger().GetSelectedOrDummyTarget(prefer_dummy);
+}
+
+Thread *CommandObject::GetDefaultThread() {
+ Thread *thread_to_use = m_exe_ctx.GetThreadPtr();
+ if (thread_to_use)
+ return thread_to_use;
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (!process) {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ if (!target) {
+ target = m_interpreter.GetDebugger().GetSelectedTarget().get();
+ }
+ if (target)
+ process = target->GetProcessSP().get();
+ }
+
+ if (process)
+ return process->GetThreadList().GetSelectedThread().get();
+ else
+ return nullptr;
+}
+
+bool CommandObjectParsed::Execute(const char *args_string,
+ CommandReturnObject &result) {
+ bool handled = false;
+ Args cmd_args(args_string);
+ if (HasOverrideCallback()) {
+ Args full_args(GetCommandName());
+ full_args.AppendArguments(cmd_args);
+ handled =
+ InvokeOverrideCallback(full_args.GetConstArgumentVector(), result);
+ }
+ if (!handled) {
+ for (auto entry : llvm::enumerate(cmd_args.entries())) {
+ if (!entry.value().ref.empty() && entry.value().ref.front() == '`') {
+ cmd_args.ReplaceArgumentAtIndex(
+ entry.index(),
+ m_interpreter.ProcessEmbeddedScriptCommands(entry.value().c_str()));
+ }
+ }
+
+ if (CheckRequirements(result)) {
+ if (ParseOptions(cmd_args, result)) {
+ // Call the command-specific version of 'Execute', passing it the
+ // already processed arguments.
+ handled = DoExecute(cmd_args, result);
+ }
+ }
+
+ Cleanup();
+ }
+ return handled;
+}
+
+bool CommandObjectRaw::Execute(const char *args_string,
+ CommandReturnObject &result) {
+ bool handled = false;
+ if (HasOverrideCallback()) {
+ std::string full_command(GetCommandName());
+ full_command += ' ';
+ full_command += args_string;
+ const char *argv[2] = {nullptr, nullptr};
+ argv[0] = full_command.c_str();
+ handled = InvokeOverrideCallback(argv, result);
+ }
+ if (!handled) {
+ if (CheckRequirements(result))
+ handled = DoExecute(args_string, result);
+
+ Cleanup();
+ }
+ return handled;
+}
+
+static llvm::StringRef arch_helper() {
+ static StreamString g_archs_help;
+ if (g_archs_help.Empty()) {
+ StringList archs;
+
+ ArchSpec::ListSupportedArchNames(archs);
+ g_archs_help.Printf("These are the supported architecture names:\n");
+ archs.Join("\n", g_archs_help);
+ }
+ return g_archs_help.GetString();
+}
+
+CommandObject::ArgumentTableEntry CommandObject::g_arguments_data[] = {
+ // clang-format off
+ { eArgTypeAddress, "address", CommandCompletions::eNoCompletion, { nullptr, false }, "A valid address in the target program's execution space." },
+ { eArgTypeAddressOrExpression, "address-expression", CommandCompletions::eNoCompletion, { nullptr, false }, "An expression that resolves to an address." },
+ { eArgTypeAliasName, "alias-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of an abbreviation (alias) for a debugger command." },
+ { eArgTypeAliasOptions, "options-for-aliased-command", CommandCompletions::eNoCompletion, { nullptr, false }, "Command options to be used as part of an alias (abbreviation) definition. (See 'help commands alias' for more information.)" },
+ { eArgTypeArchitecture, "arch", CommandCompletions::eArchitectureCompletion, { arch_helper, true }, "The architecture name, e.g. i386 or x86_64." },
+ { eArgTypeBoolean, "boolean", CommandCompletions::eNoCompletion, { nullptr, false }, "A Boolean value: 'true' or 'false'" },
+ { eArgTypeBreakpointID, "breakpt-id", CommandCompletions::eNoCompletion, { BreakpointIDHelpTextCallback, false }, nullptr },
+ { eArgTypeBreakpointIDRange, "breakpt-id-list", CommandCompletions::eNoCompletion, { BreakpointIDRangeHelpTextCallback, false }, nullptr },
+ { eArgTypeBreakpointName, "breakpoint-name", CommandCompletions::eNoCompletion, { BreakpointNameHelpTextCallback, false }, nullptr },
+ { eArgTypeByteSize, "byte-size", CommandCompletions::eNoCompletion, { nullptr, false }, "Number of bytes to use." },
+ { eArgTypeClassName, "class-name", CommandCompletions::eNoCompletion, { nullptr, false }, "Then name of a class from the debug information in the program." },
+ { eArgTypeCommandName, "cmd-name", CommandCompletions::eNoCompletion, { nullptr, false }, "A debugger command (may be multiple words), without any options or arguments." },
+ { eArgTypeCount, "count", CommandCompletions::eNoCompletion, { nullptr, false }, "An unsigned integer." },
+ { eArgTypeDirectoryName, "directory", CommandCompletions::eDiskDirectoryCompletion, { nullptr, false }, "A directory name." },
+ { eArgTypeDisassemblyFlavor, "disassembly-flavor", CommandCompletions::eNoCompletion, { nullptr, false }, "A disassembly flavor recognized by your disassembly plugin. Currently the only valid options are \"att\" and \"intel\" for Intel targets" },
+ { eArgTypeDescriptionVerbosity, "description-verbosity", CommandCompletions::eNoCompletion, { nullptr, false }, "How verbose the output of 'po' should be." },
+ { eArgTypeEndAddress, "end-address", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." },
+ { eArgTypeExpression, "expr", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." },
+ { eArgTypeExpressionPath, "expr-path", CommandCompletions::eNoCompletion, { ExprPathHelpTextCallback, true }, nullptr },
+ { eArgTypeExprFormat, "expression-format", CommandCompletions::eNoCompletion, { nullptr, false }, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]" },
+ { eArgTypeFilename, "filename", CommandCompletions::eDiskFileCompletion, { nullptr, false }, "The name of a file (can include path)." },
+ { eArgTypeFormat, "format", CommandCompletions::eNoCompletion, { FormatHelpTextCallback, true }, nullptr },
+ { eArgTypeFrameIndex, "frame-index", CommandCompletions::eNoCompletion, { nullptr, false }, "Index into a thread's list of frames." },
+ { eArgTypeFullName, "fullname", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." },
+ { eArgTypeFunctionName, "function-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a function." },
+ { eArgTypeFunctionOrSymbol, "function-or-symbol", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a function or symbol." },
+ { eArgTypeGDBFormat, "gdb-format", CommandCompletions::eNoCompletion, { GDBFormatHelpTextCallback, true }, nullptr },
+ { eArgTypeHelpText, "help-text", CommandCompletions::eNoCompletion, { nullptr, false }, "Text to be used as help for some other entity in LLDB" },
+ { eArgTypeIndex, "index", CommandCompletions::eNoCompletion, { nullptr, false }, "An index into a list." },
+ { eArgTypeLanguage, "source-language", CommandCompletions::eNoCompletion, { LanguageTypeHelpTextCallback, true }, nullptr },
+ { eArgTypeLineNum, "linenum", CommandCompletions::eNoCompletion, { nullptr, false }, "Line number in a source file." },
+ { eArgTypeLogCategory, "log-category", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a category within a log channel, e.g. all (try \"log list\" to see a list of all channels and their categories." },
+ { eArgTypeLogChannel, "log-channel", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a log channel, e.g. process.gdb-remote (try \"log list\" to see a list of all channels and their categories)." },
+ { eArgTypeMethod, "method", CommandCompletions::eNoCompletion, { nullptr, false }, "A C++ method name." },
+ { eArgTypeName, "name", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." },
+ { eArgTypeNewPathPrefix, "new-path-prefix", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." },
+ { eArgTypeNumLines, "num-lines", CommandCompletions::eNoCompletion, { nullptr, false }, "The number of lines to use." },
+ { eArgTypeNumberPerLine, "number-per-line", CommandCompletions::eNoCompletion, { nullptr, false }, "The number of items per line to display." },
+ { eArgTypeOffset, "offset", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." },
+ { eArgTypeOldPathPrefix, "old-path-prefix", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." },
+ { eArgTypeOneLiner, "one-line-command", CommandCompletions::eNoCompletion, { nullptr, false }, "A command that is entered as a single line of text." },
+ { eArgTypePath, "path", CommandCompletions::eDiskFileCompletion, { nullptr, false }, "Path." },
+ { eArgTypePermissionsNumber, "perms-numeric", CommandCompletions::eNoCompletion, { nullptr, false }, "Permissions given as an octal number (e.g. 755)." },
+ { eArgTypePermissionsString, "perms=string", CommandCompletions::eNoCompletion, { nullptr, false }, "Permissions given as a string value (e.g. rw-r-xr--)." },
+ { eArgTypePid, "pid", CommandCompletions::eNoCompletion, { nullptr, false }, "The process ID number." },
+ { eArgTypePlugin, "plugin", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." },
+ { eArgTypeProcessName, "process-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of the process." },
+ { eArgTypePythonClass, "python-class", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a Python class." },
+ { eArgTypePythonFunction, "python-function", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a Python function." },
+ { eArgTypePythonScript, "python-script", CommandCompletions::eNoCompletion, { nullptr, false }, "Source code written in Python." },
+ { eArgTypeQueueName, "queue-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of the thread queue." },
+ { eArgTypeRegisterName, "register-name", CommandCompletions::eNoCompletion, { RegisterNameHelpTextCallback, true }, nullptr },
+ { eArgTypeRegularExpression, "regular-expression", CommandCompletions::eNoCompletion, { nullptr, false }, "A regular expression." },
+ { eArgTypeRunArgs, "run-args", CommandCompletions::eNoCompletion, { nullptr, false }, "Arguments to be passed to the target program when it starts executing." },
+ { eArgTypeRunMode, "run-mode", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." },
+ { eArgTypeScriptedCommandSynchronicity, "script-cmd-synchronicity", CommandCompletions::eNoCompletion, { nullptr, false }, "The synchronicity to use to run scripted commands with regard to LLDB event system." },
+ { eArgTypeScriptLang, "script-language", CommandCompletions::eNoCompletion, { nullptr, false }, "The scripting language to be used for script-based commands. Currently only Python is valid." },
+ { eArgTypeSearchWord, "search-word", CommandCompletions::eNoCompletion, { nullptr, false }, "Any word of interest for search purposes." },
+ { eArgTypeSelector, "selector", CommandCompletions::eNoCompletion, { nullptr, false }, "An Objective-C selector name." },
+ { eArgTypeSettingIndex, "setting-index", CommandCompletions::eNoCompletion, { nullptr, false }, "An index into a settings variable that is an array (try 'settings list' to see all the possible settings variables and their types)." },
+ { eArgTypeSettingKey, "setting-key", CommandCompletions::eNoCompletion, { nullptr, false }, "A key into a settings variables that is a dictionary (try 'settings list' to see all the possible settings variables and their types)." },
+ { eArgTypeSettingPrefix, "setting-prefix", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a settable internal debugger variable up to a dot ('.'), e.g. 'target.process.'" },
+ { eArgTypeSettingVariableName, "setting-variable-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a settable internal debugger variable. Type 'settings list' to see a complete list of such variables." },
+ { eArgTypeShlibName, "shlib-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a shared library." },
+ { eArgTypeSourceFile, "source-file", CommandCompletions::eSourceFileCompletion, { nullptr, false }, "The name of a source file.." },
+ { eArgTypeSortOrder, "sort-order", CommandCompletions::eNoCompletion, { nullptr, false }, "Specify a sort order when dumping lists." },
+ { eArgTypeStartAddress, "start-address", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." },
+ { eArgTypeSummaryString, "summary-string", CommandCompletions::eNoCompletion, { SummaryStringHelpTextCallback, true }, nullptr },
+ { eArgTypeSymbol, "symbol", CommandCompletions::eSymbolCompletion, { nullptr, false }, "Any symbol name (function name, variable, argument, etc.)" },
+ { eArgTypeThreadID, "thread-id", CommandCompletions::eNoCompletion, { nullptr, false }, "Thread ID number." },
+ { eArgTypeThreadIndex, "thread-index", CommandCompletions::eNoCompletion, { nullptr, false }, "Index into the process' list of threads." },
+ { eArgTypeThreadName, "thread-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The thread's name." },
+ { eArgTypeTypeName, "type-name", CommandCompletions::eNoCompletion, { nullptr, false }, "A type name." },
+ { eArgTypeUnsignedInteger, "unsigned-integer", CommandCompletions::eNoCompletion, { nullptr, false }, "An unsigned integer." },
+ { eArgTypeUnixSignal, "unix-signal", CommandCompletions::eNoCompletion, { nullptr, false }, "A valid Unix signal name or number (e.g. SIGKILL, KILL or 9)." },
+ { eArgTypeVarName, "variable-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a variable in your program." },
+ { eArgTypeValue, "value", CommandCompletions::eNoCompletion, { nullptr, false }, "A value could be anything, depending on where and how it is used." },
+ { eArgTypeWidth, "width", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." },
+ { eArgTypeNone, "none", CommandCompletions::eNoCompletion, { nullptr, false }, "No help available for this." },
+ { eArgTypePlatform, "platform-name", CommandCompletions::ePlatformPluginCompletion, { nullptr, false }, "The name of an installed platform plug-in . Type 'platform list' to see a complete list of installed platforms." },
+ { eArgTypeWatchpointID, "watchpt-id", CommandCompletions::eNoCompletion, { nullptr, false }, "Watchpoint IDs are positive integers." },
+ { eArgTypeWatchpointIDRange, "watchpt-id-list", CommandCompletions::eNoCompletion, { nullptr, false }, "For example, '1-3' or '1 to 3'." },
+ { eArgTypeWatchType, "watch-type", CommandCompletions::eNoCompletion, { nullptr, false }, "Specify the type for a watchpoint." },
+ { eArgRawInput, "raw-input", CommandCompletions::eNoCompletion, { nullptr, false }, "Free-form text passed to a command without prior interpretation, allowing spaces without requiring quotes. To pass arguments and free form text put two dashes ' -- ' between the last argument and any raw input." },
+ { eArgTypeCommand, "command", CommandCompletions::eNoCompletion, { nullptr, false }, "An LLDB Command line command." }
+ // clang-format on
+};
+
+const CommandObject::ArgumentTableEntry *CommandObject::GetArgumentTable() {
+ // If this assertion fires, then the table above is out of date with the
+ // CommandArgumentType enumeration
+ static_assert((sizeof(CommandObject::g_arguments_data) /
+ sizeof(CommandObject::ArgumentTableEntry)) == eArgTypeLastArg,
+ "");
+ return CommandObject::g_arguments_data;
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandObjectRegexCommand.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandObjectRegexCommand.cpp
new file mode 100644
index 000000000000..19335b95ca3a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/CommandObjectRegexCommand.cpp
@@ -0,0 +1,96 @@
+//===-- CommandObjectRegexCommand.cpp ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/CommandObjectRegexCommand.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectRegexCommand constructor
+CommandObjectRegexCommand::CommandObjectRegexCommand(
+ CommandInterpreter &interpreter, llvm::StringRef name, llvm::StringRef help,
+ llvm::StringRef syntax, uint32_t max_matches, uint32_t completion_type_mask,
+ bool is_removable)
+ : CommandObjectRaw(interpreter, name, help, syntax),
+ m_max_matches(max_matches), m_completion_type_mask(completion_type_mask),
+ m_entries(), m_is_removable(is_removable) {}
+
+// Destructor
+CommandObjectRegexCommand::~CommandObjectRegexCommand() {}
+
+bool CommandObjectRegexCommand::DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) {
+ EntryCollection::const_iterator pos, end = m_entries.end();
+ for (pos = m_entries.begin(); pos != end; ++pos) {
+ RegularExpression::Match regex_match(m_max_matches);
+
+ if (pos->regex.Execute(command, &regex_match)) {
+ std::string new_command(pos->command);
+ std::string match_str;
+ char percent_var[8];
+ size_t idx, percent_var_idx;
+ for (uint32_t match_idx = 1; match_idx <= m_max_matches; ++match_idx) {
+ if (regex_match.GetMatchAtIndex(command, match_idx, match_str)) {
+ const int percent_var_len =
+ ::snprintf(percent_var, sizeof(percent_var), "%%%u", match_idx);
+ for (idx = 0; (percent_var_idx = new_command.find(
+ percent_var, idx)) != std::string::npos;) {
+ new_command.erase(percent_var_idx, percent_var_len);
+ new_command.insert(percent_var_idx, match_str);
+ idx += percent_var_idx + match_str.size();
+ }
+ }
+ }
+ // Interpret the new command and return this as the result!
+ if (m_interpreter.GetExpandRegexAliases())
+ result.GetOutputStream().Printf("%s\n", new_command.c_str());
+ // Pass in true for "no context switching". The command that called us
+ // should have set up the context appropriately, we shouldn't have to
+ // redo that.
+ return m_interpreter.HandleCommand(
+ new_command.c_str(), eLazyBoolCalculate, result, nullptr, true, true);
+ }
+ }
+ result.SetStatus(eReturnStatusFailed);
+ if (!GetSyntax().empty())
+ result.AppendError(GetSyntax());
+ else
+ result.GetOutputStream() << "Command contents '" << command
+ << "' failed to match any "
+ "regular expression in the '"
+ << m_cmd_name << "' regex ";
+ return false;
+}
+
+bool CommandObjectRegexCommand::AddRegexCommand(const char *re_cstr,
+ const char *command_cstr) {
+ m_entries.resize(m_entries.size() + 1);
+ // Only add the regular expression if it compiles
+ if (m_entries.back().regex.Compile(
+ llvm::StringRef::withNullAsEmpty(re_cstr))) {
+ m_entries.back().command.assign(command_cstr);
+ return true;
+ }
+ // The regex didn't compile...
+ m_entries.pop_back();
+ return false;
+}
+
+int CommandObjectRegexCommand::HandleCompletion(CompletionRequest &request) {
+ if (m_completion_type_mask) {
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ GetCommandInterpreter(), m_completion_type_mask, request, nullptr);
+ return request.GetNumberOfMatches();
+ } else {
+ request.SetWordComplete(false);
+ }
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandObjectScript.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandObjectScript.cpp
new file mode 100644
index 000000000000..edb1f67e7b37
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/CommandObjectScript.cpp
@@ -0,0 +1,79 @@
+//===-- CommandObjectScript.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectScript.h"
+
+
+#include "lldb/Core/Debugger.h"
+
+#include "lldb/DataFormatters/DataVisualization.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Utility/Args.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// CommandObjectScript
+
+CommandObjectScript::CommandObjectScript(CommandInterpreter &interpreter,
+ ScriptLanguage script_lang)
+ : CommandObjectRaw(
+ interpreter, "script",
+ "Invoke the script interpreter with provided code and display any "
+ "results. Start the interactive interpreter if no code is supplied.",
+ "script [<script-code>]") {}
+
+CommandObjectScript::~CommandObjectScript() {}
+
+bool CommandObjectScript::DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) {
+#ifdef LLDB_DISABLE_PYTHON
+ // if we ever support languages other than Python this simple #ifdef won't
+ // work
+ result.AppendError("your copy of LLDB does not support scripting.");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+#else
+ if (m_interpreter.GetDebugger().GetScriptLanguage() ==
+ lldb::eScriptLanguageNone) {
+ result.AppendError(
+ "the script-lang setting is set to none - scripting not available");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ScriptInterpreter *script_interpreter = GetDebugger().GetScriptInterpreter();
+
+ if (script_interpreter == nullptr) {
+ result.AppendError("no script interpreter");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ DataVisualization::ForceUpdate(); // script might change Python code we use
+ // for formatting.. make sure we keep up to
+ // date with it
+
+ if (command.empty()) {
+ script_interpreter->ExecuteInterpreterLoop();
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return result.Succeeded();
+ }
+
+ // We can do better when reporting the status of one-liner script execution.
+ if (script_interpreter->ExecuteOneLine(command, &result))
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+
+ return result.Succeeded();
+#endif
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandObjectScript.h b/contrib/llvm-project/lldb/source/Interpreter/CommandObjectScript.h
new file mode 100644
index 000000000000..4f7a912979bf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/CommandObjectScript.h
@@ -0,0 +1,31 @@
+//===-- CommandObjectScript.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectScript_h_
+#define liblldb_CommandObjectScript_h_
+
+#include "lldb/Interpreter/CommandObject.h"
+
+namespace lldb_private {
+
+// CommandObjectScript
+
+class CommandObjectScript : public CommandObjectRaw {
+public:
+ CommandObjectScript(CommandInterpreter &interpreter,
+ lldb::ScriptLanguage script_lang);
+
+ ~CommandObjectScript() override;
+
+protected:
+ bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectScript_h_
diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandOptionValidators.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandOptionValidators.cpp
new file mode 100644
index 000000000000..c41c1566aab9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/CommandOptionValidators.cpp
@@ -0,0 +1,36 @@
+//===-- CommandOptionValidators.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/CommandOptionValidators.h"
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Target/Platform.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool PosixPlatformCommandOptionValidator::IsValid(
+ Platform &platform, const ExecutionContext &target) const {
+ llvm::Triple::OSType os =
+ platform.GetSystemArchitecture().GetTriple().getOS();
+ switch (os) {
+ // Are there any other platforms that are not POSIX-compatible?
+ case llvm::Triple::Win32:
+ return false;
+ default:
+ return true;
+ }
+}
+
+const char *PosixPlatformCommandOptionValidator::ShortConditionString() const {
+ return "POSIX";
+}
+
+const char *PosixPlatformCommandOptionValidator::LongConditionString() const {
+ return "Option only valid for POSIX-compliant hosts.";
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandReturnObject.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandReturnObject.cpp
new file mode 100644
index 000000000000..3a7a8755d975
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/CommandReturnObject.cpp
@@ -0,0 +1,170 @@
+//===-- CommandReturnObject.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/CommandReturnObject.h"
+
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void DumpStringToStreamWithNewline(Stream &strm, const std::string &s,
+ bool add_newline_if_empty) {
+ bool add_newline = false;
+ if (s.empty()) {
+ add_newline = add_newline_if_empty;
+ } else {
+ // We already checked for empty above, now make sure there is a newline in
+ // the error, and if there isn't one, add one.
+ strm.Write(s.c_str(), s.size());
+
+ const char last_char = *s.rbegin();
+ add_newline = last_char != '\n' && last_char != '\r';
+ }
+ if (add_newline)
+ strm.EOL();
+}
+
+CommandReturnObject::CommandReturnObject()
+ : m_out_stream(), m_err_stream(), m_status(eReturnStatusStarted),
+ m_did_change_process_state(false), m_interactive(true),
+ m_abnormal_stop_was_expected(false) {}
+
+CommandReturnObject::~CommandReturnObject() {}
+
+void CommandReturnObject::AppendErrorWithFormat(const char *format, ...) {
+ if (!format)
+ return;
+ va_list args;
+ va_start(args, format);
+ StreamString sstrm;
+ sstrm.PrintfVarArg(format, args);
+ va_end(args);
+
+ const std::string &s = sstrm.GetString();
+ if (!s.empty()) {
+ Stream &error_strm = GetErrorStream();
+ error_strm.PutCString("error: ");
+ DumpStringToStreamWithNewline(error_strm, s, false);
+ }
+}
+
+void CommandReturnObject::AppendMessageWithFormat(const char *format, ...) {
+ if (!format)
+ return;
+ va_list args;
+ va_start(args, format);
+ StreamString sstrm;
+ sstrm.PrintfVarArg(format, args);
+ va_end(args);
+
+ GetOutputStream() << sstrm.GetString();
+}
+
+void CommandReturnObject::AppendWarningWithFormat(const char *format, ...) {
+ if (!format)
+ return;
+ va_list args;
+ va_start(args, format);
+ StreamString sstrm;
+ sstrm.PrintfVarArg(format, args);
+ va_end(args);
+
+ GetErrorStream() << "warning: " << sstrm.GetString();
+}
+
+void CommandReturnObject::AppendMessage(llvm::StringRef in_string) {
+ if (in_string.empty())
+ return;
+ GetOutputStream() << in_string << "\n";
+}
+
+void CommandReturnObject::AppendWarning(llvm::StringRef in_string) {
+ if (in_string.empty())
+ return;
+ GetErrorStream() << "warning: " << in_string << "\n";
+}
+
+// Similar to AppendWarning, but do not prepend 'warning: ' to message, and
+// don't append "\n" to the end of it.
+
+void CommandReturnObject::AppendRawWarning(llvm::StringRef in_string) {
+ if (in_string.empty())
+ return;
+ GetErrorStream() << in_string;
+}
+
+void CommandReturnObject::AppendError(llvm::StringRef in_string) {
+ if (in_string.empty())
+ return;
+ GetErrorStream() << "error: " << in_string << "\n";
+}
+
+void CommandReturnObject::SetError(const Status &error,
+ const char *fallback_error_cstr) {
+ const char *error_cstr = error.AsCString();
+ if (error_cstr == nullptr)
+ error_cstr = fallback_error_cstr;
+ SetError(error_cstr);
+}
+
+void CommandReturnObject::SetError(llvm::StringRef error_str) {
+ if (error_str.empty())
+ return;
+
+ AppendError(error_str);
+ SetStatus(eReturnStatusFailed);
+}
+
+// Similar to AppendError, but do not prepend 'Status: ' to message, and don't
+// append "\n" to the end of it.
+
+void CommandReturnObject::AppendRawError(llvm::StringRef in_string) {
+ if (in_string.empty())
+ return;
+ GetErrorStream() << in_string;
+}
+
+void CommandReturnObject::SetStatus(ReturnStatus status) { m_status = status; }
+
+ReturnStatus CommandReturnObject::GetStatus() { return m_status; }
+
+bool CommandReturnObject::Succeeded() {
+ return m_status <= eReturnStatusSuccessContinuingResult;
+}
+
+bool CommandReturnObject::HasResult() {
+ return (m_status == eReturnStatusSuccessFinishResult ||
+ m_status == eReturnStatusSuccessContinuingResult);
+}
+
+void CommandReturnObject::Clear() {
+ lldb::StreamSP stream_sp;
+ stream_sp = m_out_stream.GetStreamAtIndex(eStreamStringIndex);
+ if (stream_sp)
+ static_cast<StreamString *>(stream_sp.get())->Clear();
+ stream_sp = m_err_stream.GetStreamAtIndex(eStreamStringIndex);
+ if (stream_sp)
+ static_cast<StreamString *>(stream_sp.get())->Clear();
+ m_status = eReturnStatusStarted;
+ m_did_change_process_state = false;
+ m_interactive = true;
+}
+
+bool CommandReturnObject::GetDidChangeProcessState() {
+ return m_did_change_process_state;
+}
+
+void CommandReturnObject::SetDidChangeProcessState(bool b) {
+ m_did_change_process_state = b;
+}
+
+bool CommandReturnObject::GetInteractive() const { return m_interactive; }
+
+void CommandReturnObject::SetInteractive(bool b) { m_interactive = b; }
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionArgParser.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionArgParser.cpp
new file mode 100644
index 000000000000..efaac0720fd0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionArgParser.cpp
@@ -0,0 +1,254 @@
+//===-- OptionArgParser.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+bool OptionArgParser::ToBoolean(llvm::StringRef ref, bool fail_value,
+ bool *success_ptr) {
+ if (success_ptr)
+ *success_ptr = true;
+ ref = ref.trim();
+ if (ref.equals_lower("false") || ref.equals_lower("off") ||
+ ref.equals_lower("no") || ref.equals_lower("0")) {
+ return false;
+ } else if (ref.equals_lower("true") || ref.equals_lower("on") ||
+ ref.equals_lower("yes") || ref.equals_lower("1")) {
+ return true;
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+char OptionArgParser::ToChar(llvm::StringRef s, char fail_value,
+ bool *success_ptr) {
+ if (success_ptr)
+ *success_ptr = false;
+ if (s.size() != 1)
+ return fail_value;
+
+ if (success_ptr)
+ *success_ptr = true;
+ return s[0];
+}
+
+int64_t OptionArgParser::ToOptionEnum(llvm::StringRef s,
+ const OptionEnumValues &enum_values,
+ int32_t fail_value, Status &error) {
+ error.Clear();
+ if (enum_values.empty()) {
+ error.SetErrorString("invalid enumeration argument");
+ return fail_value;
+ }
+
+ if (s.empty()) {
+ error.SetErrorString("empty enumeration string");
+ return fail_value;
+ }
+
+ for (const auto &enum_value : enum_values) {
+ llvm::StringRef this_enum(enum_value.string_value);
+ if (this_enum.startswith(s))
+ return enum_value.value;
+ }
+
+ StreamString strm;
+ strm.PutCString("invalid enumeration value, valid values are: ");
+ bool is_first = true;
+ for (const auto &enum_value : enum_values) {
+ strm.Printf("%s\"%s\"",
+ is_first ? is_first = false,"" : ", ", enum_value.string_value);
+ }
+ error.SetErrorString(strm.GetString());
+ return fail_value;
+}
+
+Status OptionArgParser::ToFormat(const char *s, lldb::Format &format,
+ size_t *byte_size_ptr) {
+ format = eFormatInvalid;
+ Status error;
+
+ if (s && s[0]) {
+ if (byte_size_ptr) {
+ if (isdigit(s[0])) {
+ char *format_char = nullptr;
+ unsigned long byte_size = ::strtoul(s, &format_char, 0);
+ if (byte_size != ULONG_MAX)
+ *byte_size_ptr = byte_size;
+ s = format_char;
+ } else
+ *byte_size_ptr = 0;
+ }
+
+ const bool partial_match_ok = true;
+ if (!FormatManager::GetFormatFromCString(s, partial_match_ok, format)) {
+ StreamString error_strm;
+ error_strm.Printf(
+ "Invalid format character or name '%s'. Valid values are:\n", s);
+ for (Format f = eFormatDefault; f < kNumFormats; f = Format(f + 1)) {
+ char format_char = FormatManager::GetFormatAsFormatChar(f);
+ if (format_char)
+ error_strm.Printf("'%c' or ", format_char);
+
+ error_strm.Printf("\"%s\"", FormatManager::GetFormatAsCString(f));
+ error_strm.EOL();
+ }
+
+ if (byte_size_ptr)
+ error_strm.PutCString(
+ "An optional byte size can precede the format character.\n");
+ error.SetErrorString(error_strm.GetString());
+ }
+
+ if (error.Fail())
+ return error;
+ } else {
+ error.SetErrorStringWithFormat("%s option string", s ? "empty" : "invalid");
+ }
+ return error;
+}
+
+lldb::ScriptLanguage OptionArgParser::ToScriptLanguage(
+ llvm::StringRef s, lldb::ScriptLanguage fail_value, bool *success_ptr) {
+ if (success_ptr)
+ *success_ptr = true;
+
+ if (s.equals_lower("python"))
+ return eScriptLanguagePython;
+ if (s.equals_lower("default"))
+ return eScriptLanguageDefault;
+ if (s.equals_lower("none"))
+ return eScriptLanguageNone;
+
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+lldb::addr_t OptionArgParser::ToAddress(const ExecutionContext *exe_ctx,
+ llvm::StringRef s,
+ lldb::addr_t fail_value,
+ Status *error_ptr) {
+ bool error_set = false;
+ if (s.empty()) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"",
+ s.str().c_str());
+ return fail_value;
+ }
+
+ llvm::StringRef sref = s;
+
+ lldb::addr_t addr = LLDB_INVALID_ADDRESS;
+ if (!s.getAsInteger(0, addr)) {
+ if (error_ptr)
+ error_ptr->Clear();
+ return addr;
+ }
+
+ // Try base 16 with no prefix...
+ if (!s.getAsInteger(16, addr)) {
+ if (error_ptr)
+ error_ptr->Clear();
+ return addr;
+ }
+
+ Target *target = nullptr;
+ if (!exe_ctx || !(target = exe_ctx->GetTargetPtr())) {
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"",
+ s.str().c_str());
+ return fail_value;
+ }
+
+ lldb::ValueObjectSP valobj_sp;
+ EvaluateExpressionOptions options;
+ options.SetCoerceToId(false);
+ options.SetUnwindOnError(true);
+ options.SetKeepInMemory(false);
+ options.SetTryAllThreads(true);
+
+ ExpressionResults expr_result =
+ target->EvaluateExpression(s, exe_ctx->GetFramePtr(), valobj_sp, options);
+
+ bool success = false;
+ if (expr_result == eExpressionCompleted) {
+ if (valobj_sp)
+ valobj_sp = valobj_sp->GetQualifiedRepresentationIfAvailable(
+ valobj_sp->GetDynamicValueType(), true);
+ // Get the address to watch.
+ if (valobj_sp)
+ addr = valobj_sp->GetValueAsUnsigned(fail_value, &success);
+ if (success) {
+ if (error_ptr)
+ error_ptr->Clear();
+ return addr;
+ } else {
+ if (error_ptr) {
+ error_set = true;
+ error_ptr->SetErrorStringWithFormat(
+ "address expression \"%s\" resulted in a value whose type "
+ "can't be converted to an address: %s",
+ s.str().c_str(), valobj_sp->GetTypeName().GetCString());
+ }
+ }
+
+ } else {
+ // Since the compiler can't handle things like "main + 12" we should try to
+ // do this for now. The compiler doesn't like adding offsets to function
+ // pointer types.
+ static RegularExpression g_symbol_plus_offset_regex(
+ "^(.*)([-\\+])[[:space:]]*(0x[0-9A-Fa-f]+|[0-9]+)[[:space:]]*$");
+ RegularExpression::Match regex_match(3);
+ if (g_symbol_plus_offset_regex.Execute(sref, &regex_match)) {
+ uint64_t offset = 0;
+ bool add = true;
+ std::string name;
+ std::string str;
+ if (regex_match.GetMatchAtIndex(s, 1, name)) {
+ if (regex_match.GetMatchAtIndex(s, 2, str)) {
+ add = str[0] == '+';
+
+ if (regex_match.GetMatchAtIndex(s, 3, str)) {
+ if (!llvm::StringRef(str).getAsInteger(0, offset)) {
+ Status error;
+ addr = ToAddress(exe_ctx, name.c_str(), LLDB_INVALID_ADDRESS,
+ &error);
+ if (addr != LLDB_INVALID_ADDRESS) {
+ if (add)
+ return addr + offset;
+ else
+ return addr - offset;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (error_ptr) {
+ error_set = true;
+ error_ptr->SetErrorStringWithFormat(
+ "address expression \"%s\" evaluation failed", s.str().c_str());
+ }
+ }
+
+ if (error_ptr) {
+ if (!error_set)
+ error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"",
+ s.str().c_str());
+ }
+ return fail_value;
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupArchitecture.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupArchitecture.cpp
new file mode 100644
index 000000000000..2ee1a9c7cf84
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupArchitecture.cpp
@@ -0,0 +1,59 @@
+//===-- OptionGroupArchitecture.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupArchitecture.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Target/Platform.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionGroupArchitecture::OptionGroupArchitecture() : m_arch_str() {}
+
+OptionGroupArchitecture::~OptionGroupArchitecture() {}
+
+static constexpr OptionDefinition g_option_table[] = {
+ {LLDB_OPT_SET_1, false, "arch", 'a', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeArchitecture,
+ "Specify the architecture for the target."},
+};
+
+llvm::ArrayRef<OptionDefinition> OptionGroupArchitecture::GetDefinitions() {
+ return llvm::makeArrayRef(g_option_table);
+}
+
+bool OptionGroupArchitecture::GetArchitecture(Platform *platform,
+ ArchSpec &arch) {
+ arch = Platform::GetAugmentedArchSpec(platform, m_arch_str);
+ return arch.IsValid();
+}
+
+Status
+OptionGroupArchitecture::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = g_option_table[option_idx].short_option;
+
+ switch (short_option) {
+ case 'a':
+ m_arch_str.assign(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+
+ return error;
+}
+
+void OptionGroupArchitecture::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_arch_str.clear();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupBoolean.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupBoolean.cpp
new file mode 100644
index 000000000000..8a6482c8df25
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupBoolean.cpp
@@ -0,0 +1,56 @@
+//===-- OptionGroupBoolean.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+
+#include "lldb/Host/OptionParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionGroupBoolean::OptionGroupBoolean(uint32_t usage_mask, bool required,
+ const char *long_option,
+ int short_option, const char *usage_text,
+ bool default_value,
+ bool no_argument_toggle_default)
+ : m_value(default_value, default_value) {
+ m_option_definition.usage_mask = usage_mask;
+ m_option_definition.required = required;
+ m_option_definition.long_option = long_option;
+ m_option_definition.short_option = short_option;
+ m_option_definition.validator = nullptr;
+ m_option_definition.option_has_arg = no_argument_toggle_default
+ ? OptionParser::eNoArgument
+ : OptionParser::eRequiredArgument;
+ m_option_definition.enum_values = {};
+ m_option_definition.completion_type = 0;
+ m_option_definition.argument_type = eArgTypeBoolean;
+ m_option_definition.usage_text = usage_text;
+}
+
+OptionGroupBoolean::~OptionGroupBoolean() {}
+
+Status OptionGroupBoolean::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_value,
+ ExecutionContext *execution_context) {
+ Status error;
+ if (m_option_definition.option_has_arg == OptionParser::eNoArgument) {
+ // Not argument, toggle the default value and mark the option as having
+ // been set
+ m_value.SetCurrentValue(!m_value.GetDefaultValue());
+ m_value.SetOptionWasSet();
+ } else {
+ error = m_value.SetValueFromString(option_value);
+ }
+ return error;
+}
+
+void OptionGroupBoolean::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_value.Clear();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFile.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFile.cpp
new file mode 100644
index 000000000000..cda75ec205ec
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFile.cpp
@@ -0,0 +1,78 @@
+//===-- OptionGroupFile.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupFile.h"
+
+#include "lldb/Host/OptionParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionGroupFile::OptionGroupFile(uint32_t usage_mask, bool required,
+ const char *long_option, int short_option,
+ uint32_t completion_type,
+ lldb::CommandArgumentType argument_type,
+ const char *usage_text)
+ : m_file() {
+ m_option_definition.usage_mask = usage_mask;
+ m_option_definition.required = required;
+ m_option_definition.long_option = long_option;
+ m_option_definition.short_option = short_option;
+ m_option_definition.validator = nullptr;
+ m_option_definition.option_has_arg = OptionParser::eRequiredArgument;
+ m_option_definition.enum_values = {};
+ m_option_definition.completion_type = completion_type;
+ m_option_definition.argument_type = argument_type;
+ m_option_definition.usage_text = usage_text;
+}
+
+OptionGroupFile::~OptionGroupFile() {}
+
+Status OptionGroupFile::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error(m_file.SetValueFromString(option_arg));
+ return error;
+}
+
+void OptionGroupFile::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_file.Clear();
+}
+
+OptionGroupFileList::OptionGroupFileList(
+ uint32_t usage_mask, bool required, const char *long_option,
+ int short_option, uint32_t completion_type,
+ lldb::CommandArgumentType argument_type, const char *usage_text)
+ : m_file_list() {
+ m_option_definition.usage_mask = usage_mask;
+ m_option_definition.required = required;
+ m_option_definition.long_option = long_option;
+ m_option_definition.short_option = short_option;
+ m_option_definition.validator = nullptr;
+ m_option_definition.option_has_arg = OptionParser::eRequiredArgument;
+ m_option_definition.enum_values = {};
+ m_option_definition.completion_type = completion_type;
+ m_option_definition.argument_type = argument_type;
+ m_option_definition.usage_text = usage_text;
+}
+
+OptionGroupFileList::~OptionGroupFileList() {}
+
+Status
+OptionGroupFileList::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_value,
+ ExecutionContext *execution_context) {
+ Status error(m_file_list.SetValueFromString(option_value));
+ return error;
+}
+
+void OptionGroupFileList::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_file_list.Clear();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFormat.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFormat.cpp
new file mode 100644
index 000000000000..d9acfd663dd1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFormat.cpp
@@ -0,0 +1,268 @@
+//===-- OptionGroupFormat.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupFormat.h"
+
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionGroupFormat::OptionGroupFormat(lldb::Format default_format,
+ uint64_t default_byte_size,
+ uint64_t default_count)
+ : m_format(default_format, default_format),
+ m_byte_size(default_byte_size, default_byte_size),
+ m_count(default_count, default_count), m_prev_gdb_format('x'),
+ m_prev_gdb_size('w') {}
+
+OptionGroupFormat::~OptionGroupFormat() {}
+
+static constexpr OptionDefinition g_option_table[] = {
+ {LLDB_OPT_SET_1, false, "format", 'f', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeFormat,
+ "Specify a format to be used for display."},
+ {LLDB_OPT_SET_2, false, "gdb-format", 'G', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeGDBFormat,
+ "Specify a format using a GDB format specifier string."},
+ {LLDB_OPT_SET_3, false, "size", 's', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeByteSize,
+ "The size in bytes to use when displaying with the selected format."},
+ {LLDB_OPT_SET_4, false, "count", 'c', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeCount,
+ "The number of total items to display."},
+};
+
+llvm::ArrayRef<OptionDefinition> OptionGroupFormat::GetDefinitions() {
+ auto result = llvm::makeArrayRef(g_option_table);
+ if (m_byte_size.GetDefaultValue() < UINT64_MAX) {
+ if (m_count.GetDefaultValue() < UINT64_MAX)
+ return result;
+ else
+ return result.take_front(3);
+ }
+ return result.take_front(2);
+}
+
+Status OptionGroupFormat::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = g_option_table[option_idx].short_option;
+
+ switch (short_option) {
+ case 'f':
+ error = m_format.SetValueFromString(option_arg);
+ break;
+
+ case 'c':
+ if (m_count.GetDefaultValue() == 0) {
+ error.SetErrorString("--count option is disabled");
+ } else {
+ error = m_count.SetValueFromString(option_arg);
+ if (m_count.GetCurrentValue() == 0)
+ error.SetErrorStringWithFormat("invalid --count option value '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+
+ case 's':
+ if (m_byte_size.GetDefaultValue() == 0) {
+ error.SetErrorString("--size option is disabled");
+ } else {
+ error = m_byte_size.SetValueFromString(option_arg);
+ if (m_byte_size.GetCurrentValue() == 0)
+ error.SetErrorStringWithFormat("invalid --size option value '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+
+ case 'G': {
+ uint64_t count = 0;
+ llvm::StringRef gdb_format_str = option_arg;
+ gdb_format_str.consumeInteger(0, count);
+
+ Format format = eFormatDefault;
+ uint32_t byte_size = 0;
+
+ while (!gdb_format_str.empty() &&
+ ParserGDBFormatLetter(execution_context, gdb_format_str[0], format,
+ byte_size)) {
+ gdb_format_str = gdb_format_str.drop_front();
+ }
+
+ // We the first character of the "gdb_format_str" is not the
+ // NULL terminator, we didn't consume the entire string and
+ // something is wrong. Also, if none of the format, size or count was
+ // specified correctly, then abort.
+ if (!gdb_format_str.empty() ||
+ (format == eFormatInvalid && byte_size == 0 && count == 0)) {
+ // Nothing got set correctly
+ error.SetErrorStringWithFormat("invalid gdb format string '%s'",
+ option_arg.str().c_str());
+ return error;
+ }
+
+ // At least one of the format, size or count was set correctly. Anything
+ // that wasn't set correctly should be set to the previous default
+ if (format == eFormatInvalid)
+ ParserGDBFormatLetter(execution_context, m_prev_gdb_format, format,
+ byte_size);
+
+ const bool byte_size_enabled = m_byte_size.GetDefaultValue() < UINT64_MAX;
+ const bool count_enabled = m_count.GetDefaultValue() < UINT64_MAX;
+ if (byte_size_enabled) {
+ // Byte size is enabled
+ if (byte_size == 0)
+ ParserGDBFormatLetter(execution_context, m_prev_gdb_size, format,
+ byte_size);
+ } else {
+ // Byte size is disabled, make sure it wasn't specified but if this is an
+ // address, it's actually necessary to specify one so don't error out
+ if (byte_size > 0 && format != lldb::eFormatAddressInfo) {
+ error.SetErrorString(
+ "this command doesn't support specifying a byte size");
+ return error;
+ }
+ }
+
+ if (count_enabled) {
+ // Count is enabled and was not set, set it to the default for gdb format
+ // statements (which is 1).
+ if (count == 0)
+ count = 1;
+ } else {
+ // Count is disabled, make sure it wasn't specified
+ if (count > 0) {
+ error.SetErrorString("this command doesn't support specifying a count");
+ return error;
+ }
+ }
+
+ m_format.SetCurrentValue(format);
+ m_format.SetOptionWasSet();
+ if (byte_size_enabled) {
+ m_byte_size.SetCurrentValue(byte_size);
+ m_byte_size.SetOptionWasSet();
+ }
+ if (count_enabled) {
+ m_count.SetCurrentValue(count);
+ m_count.SetOptionWasSet();
+ }
+ } break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+
+ return error;
+}
+
+bool OptionGroupFormat::ParserGDBFormatLetter(
+ ExecutionContext *execution_context, char format_letter, Format &format,
+ uint32_t &byte_size) {
+ m_has_gdb_format = true;
+ switch (format_letter) {
+ case 'o':
+ format = eFormatOctal;
+ m_prev_gdb_format = format_letter;
+ return true;
+ case 'x':
+ format = eFormatHex;
+ m_prev_gdb_format = format_letter;
+ return true;
+ case 'd':
+ format = eFormatDecimal;
+ m_prev_gdb_format = format_letter;
+ return true;
+ case 'u':
+ format = eFormatUnsigned;
+ m_prev_gdb_format = format_letter;
+ return true;
+ case 't':
+ format = eFormatBinary;
+ m_prev_gdb_format = format_letter;
+ return true;
+ case 'f':
+ format = eFormatFloat;
+ m_prev_gdb_format = format_letter;
+ return true;
+ case 'a':
+ format = eFormatAddressInfo;
+ {
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ if (target_sp)
+ byte_size = target_sp->GetArchitecture().GetAddressByteSize();
+ m_prev_gdb_format = format_letter;
+ return true;
+ }
+ case 'i':
+ format = eFormatInstruction;
+ m_prev_gdb_format = format_letter;
+ return true;
+ case 'c':
+ format = eFormatChar;
+ m_prev_gdb_format = format_letter;
+ return true;
+ case 's':
+ format = eFormatCString;
+ m_prev_gdb_format = format_letter;
+ return true;
+ case 'T':
+ format = eFormatOSType;
+ m_prev_gdb_format = format_letter;
+ return true;
+ case 'A':
+ format = eFormatHexFloat;
+ m_prev_gdb_format = format_letter;
+ return true;
+
+ case 'b':
+ case 'h':
+ case 'w':
+ case 'g':
+ {
+ // Size isn't used for printing instructions, so if a size is specified,
+ // and the previous format was 'i', then we should reset it to the
+ // default ('x'). Otherwise we'll continue to print as instructions,
+ // which isn't expected.
+ if (format_letter == 'b')
+ byte_size = 1;
+ else if (format_letter == 'h')
+ byte_size = 2;
+ else if (format_letter == 'w')
+ byte_size = 4;
+ else if (format_letter == 'g')
+ byte_size = 8;
+
+ m_prev_gdb_size = format_letter;
+ if (m_prev_gdb_format == 'i')
+ m_prev_gdb_format = 'x';
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+
+ return false;
+}
+
+void OptionGroupFormat::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_format.Clear();
+ m_byte_size.Clear();
+ m_count.Clear();
+ m_has_gdb_format = false;
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupOutputFile.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupOutputFile.cpp
new file mode 100644
index 000000000000..ccb99a8fce4a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupOutputFile.cpp
@@ -0,0 +1,64 @@
+//===-- OptionGroupOutputFile.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupOutputFile.h"
+
+#include "lldb/Host/OptionParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionGroupOutputFile::OptionGroupOutputFile()
+ : m_file(), m_append(false, false) {}
+
+OptionGroupOutputFile::~OptionGroupOutputFile() {}
+
+static const uint32_t SHORT_OPTION_APND = 0x61706e64; // 'apnd'
+
+static constexpr OptionDefinition g_option_table[] = {
+ {LLDB_OPT_SET_1, false, "outfile", 'o', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeFilename,
+ "Specify a path for capturing command output."},
+ {LLDB_OPT_SET_1, false, "append-outfile", SHORT_OPTION_APND,
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
+ "Append to the file specified with '--outfile <path>'."},
+};
+
+llvm::ArrayRef<OptionDefinition> OptionGroupOutputFile::GetDefinitions() {
+ return llvm::makeArrayRef(g_option_table);
+}
+
+Status
+OptionGroupOutputFile::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = g_option_table[option_idx].short_option;
+
+ switch (short_option) {
+ case 'o':
+ error = m_file.SetValueFromString(option_arg);
+ break;
+
+ case SHORT_OPTION_APND:
+ m_append.SetCurrentValue(true);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+
+ return error;
+}
+
+void OptionGroupOutputFile::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_file.Clear();
+ m_append.Clear();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupPlatform.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupPlatform.cpp
new file mode 100644
index 000000000000..6dc2996bb78a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupPlatform.cpp
@@ -0,0 +1,141 @@
+//===-- OptionGroupPlatform.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupPlatform.h"
+
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Target/Platform.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+PlatformSP OptionGroupPlatform::CreatePlatformWithOptions(
+ CommandInterpreter &interpreter, const ArchSpec &arch, bool make_selected,
+ Status &error, ArchSpec &platform_arch) const {
+ PlatformSP platform_sp;
+
+ if (!m_platform_name.empty()) {
+ platform_sp = Platform::Create(ConstString(m_platform_name.c_str()), error);
+ if (platform_sp) {
+ if (platform_arch.IsValid() &&
+ !platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch)) {
+ error.SetErrorStringWithFormat("platform '%s' doesn't support '%s'",
+ platform_sp->GetName().GetCString(),
+ arch.GetTriple().getTriple().c_str());
+ platform_sp.reset();
+ return platform_sp;
+ }
+ }
+ } else if (arch.IsValid()) {
+ platform_sp = Platform::Create(arch, &platform_arch, error);
+ }
+
+ if (platform_sp) {
+ interpreter.GetDebugger().GetPlatformList().Append(platform_sp,
+ make_selected);
+ if (!m_os_version.empty())
+ platform_sp->SetOSVersion(m_os_version);
+
+ if (m_sdk_sysroot)
+ platform_sp->SetSDKRootDirectory(m_sdk_sysroot);
+
+ if (m_sdk_build)
+ platform_sp->SetSDKBuild(m_sdk_build);
+ }
+
+ return platform_sp;
+}
+
+void OptionGroupPlatform::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_platform_name.clear();
+ m_sdk_sysroot.Clear();
+ m_sdk_build.Clear();
+ m_os_version = llvm::VersionTuple();
+}
+
+static constexpr OptionDefinition g_option_table[] = {
+ {LLDB_OPT_SET_ALL, false, "platform", 'p', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypePlatform, "Specify name of the platform to "
+ "use for this target, creating the "
+ "platform if necessary."},
+ {LLDB_OPT_SET_ALL, false, "version", 'v', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Specify the initial SDK version to use prior to connecting."},
+ {LLDB_OPT_SET_ALL, false, "build", 'b', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Specify the initial SDK build number."},
+ {LLDB_OPT_SET_ALL, false, "sysroot", 'S', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeFilename, "Specify the SDK root directory "
+ "that contains a root of all "
+ "remote system files."}};
+
+llvm::ArrayRef<OptionDefinition> OptionGroupPlatform::GetDefinitions() {
+ llvm::ArrayRef<OptionDefinition> result(g_option_table);
+ if (m_include_platform_option)
+ return result;
+ return result.drop_front();
+}
+
+Status
+OptionGroupPlatform::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ if (!m_include_platform_option)
+ ++option_idx;
+
+ const int short_option = g_option_table[option_idx].short_option;
+
+ switch (short_option) {
+ case 'p':
+ m_platform_name.assign(option_arg);
+ break;
+
+ case 'v':
+ if (m_os_version.tryParse(option_arg))
+ error.SetErrorStringWithFormatv("invalid version string '{0}'",
+ option_arg);
+ break;
+
+ case 'b':
+ m_sdk_build.SetString(option_arg);
+ break;
+
+ case 'S':
+ m_sdk_sysroot.SetString(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+ return error;
+}
+
+bool OptionGroupPlatform::PlatformMatches(
+ const lldb::PlatformSP &platform_sp) const {
+ if (platform_sp) {
+ if (!m_platform_name.empty()) {
+ if (platform_sp->GetName() != ConstString(m_platform_name.c_str()))
+ return false;
+ }
+
+ if (m_sdk_build && m_sdk_build != platform_sp->GetSDKBuild())
+ return false;
+
+ if (m_sdk_sysroot && m_sdk_sysroot != platform_sp->GetSDKRootDirectory())
+ return false;
+
+ if (!m_os_version.empty() && m_os_version != platform_sp->GetOSVersion())
+ return false;
+ return true;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupString.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupString.cpp
new file mode 100644
index 000000000000..c01b7065fbd0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupString.cpp
@@ -0,0 +1,47 @@
+//===-- OptionGroupString.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupString.h"
+
+#include "lldb/Host/OptionParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionGroupString::OptionGroupString(uint32_t usage_mask, bool required,
+ const char *long_option, int short_option,
+ uint32_t completion_type,
+ lldb::CommandArgumentType argument_type,
+ const char *usage_text,
+ const char *default_value)
+ : m_value(default_value, default_value) {
+ m_option_definition.usage_mask = usage_mask;
+ m_option_definition.required = required;
+ m_option_definition.long_option = long_option;
+ m_option_definition.short_option = short_option;
+ m_option_definition.validator = nullptr;
+ m_option_definition.option_has_arg = OptionParser::eRequiredArgument;
+ m_option_definition.enum_values = {};
+ m_option_definition.completion_type = completion_type;
+ m_option_definition.argument_type = argument_type;
+ m_option_definition.usage_text = usage_text;
+}
+
+OptionGroupString::~OptionGroupString() {}
+
+Status OptionGroupString::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error(m_value.SetValueFromString(option_arg));
+ return error;
+}
+
+void OptionGroupString::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_value.Clear();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUInt64.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUInt64.cpp
new file mode 100644
index 000000000000..53e5674d0296
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUInt64.cpp
@@ -0,0 +1,47 @@
+//===-- OptionGroupUInt64.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupUInt64.h"
+
+#include "lldb/Host/OptionParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionGroupUInt64::OptionGroupUInt64(uint32_t usage_mask, bool required,
+ const char *long_option, int short_option,
+ uint32_t completion_type,
+ lldb::CommandArgumentType argument_type,
+ const char *usage_text,
+ uint64_t default_value)
+ : m_value(default_value, default_value) {
+ m_option_definition.usage_mask = usage_mask;
+ m_option_definition.required = required;
+ m_option_definition.long_option = long_option;
+ m_option_definition.short_option = short_option;
+ m_option_definition.validator = nullptr;
+ m_option_definition.option_has_arg = OptionParser::eRequiredArgument;
+ m_option_definition.enum_values = {};
+ m_option_definition.completion_type = completion_type;
+ m_option_definition.argument_type = argument_type;
+ m_option_definition.usage_text = usage_text;
+}
+
+OptionGroupUInt64::~OptionGroupUInt64() {}
+
+Status OptionGroupUInt64::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error(m_value.SetValueFromString(option_arg));
+ return error;
+}
+
+void OptionGroupUInt64::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_value.Clear();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUUID.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUUID.cpp
new file mode 100644
index 000000000000..e32673bc52af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUUID.cpp
@@ -0,0 +1,53 @@
+//===-- OptionGroupUUID.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupUUID.h"
+
+#include "lldb/Host/OptionParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionGroupUUID::OptionGroupUUID() : m_uuid() {}
+
+OptionGroupUUID::~OptionGroupUUID() {}
+
+static constexpr OptionDefinition g_option_table[] = {
+ {LLDB_OPT_SET_1, false, "uuid", 'u', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeNone, "A module UUID value."},
+};
+
+llvm::ArrayRef<OptionDefinition> OptionGroupUUID::GetDefinitions() {
+ return llvm::makeArrayRef(g_option_table);
+}
+
+Status OptionGroupUUID::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = g_option_table[option_idx].short_option;
+
+ switch (short_option) {
+ case 'u':
+ error = m_uuid.SetValueFromString(option_arg);
+ if (error.Success())
+ m_uuid.SetOptionWasSet();
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+
+ return error;
+}
+
+void OptionGroupUUID::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_uuid.Clear();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp
new file mode 100644
index 000000000000..4e5463a4de00
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp
@@ -0,0 +1,222 @@
+//===-- OptionGroupValueObjectDisplay.cpp -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
+
+#include "lldb/DataFormatters/ValueObjectPrinter.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Target/Target.h"
+
+#include "llvm/ADT/ArrayRef.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionGroupValueObjectDisplay::OptionGroupValueObjectDisplay() {}
+
+OptionGroupValueObjectDisplay::~OptionGroupValueObjectDisplay() {}
+
+static const OptionDefinition g_option_table[] = {
+ {LLDB_OPT_SET_1, false, "dynamic-type", 'd',
+ OptionParser::eRequiredArgument, nullptr, GetDynamicValueTypes(), 0,
+ eArgTypeNone, "Show the object as its full dynamic type, not its static "
+ "type, if available."},
+ {LLDB_OPT_SET_1, false, "synthetic-type", 'S',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
+ "Show the object obeying its synthetic provider, if available."},
+ {LLDB_OPT_SET_1, false, "depth", 'D', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeCount, "Set the max recurse depth when dumping "
+ "aggregate types (default is infinity)."},
+ {LLDB_OPT_SET_1, false, "flat", 'F', OptionParser::eNoArgument, nullptr,
+ {}, 0, eArgTypeNone, "Display results in a flat format that uses "
+ "expression paths for each variable or member."},
+ {LLDB_OPT_SET_1, false, "location", 'L', OptionParser::eNoArgument, nullptr,
+ {}, 0, eArgTypeNone, "Show variable location information."},
+ {LLDB_OPT_SET_1, false, "object-description", 'O',
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
+ "Print as an Objective-C object."},
+ {LLDB_OPT_SET_1, false, "ptr-depth", 'P', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeCount, "The number of pointers to be traversed "
+ "when dumping values (default is zero)."},
+ {LLDB_OPT_SET_1, false, "show-types", 'T', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Show variable types when dumping values."},
+ {LLDB_OPT_SET_1, false, "no-summary-depth", 'Y',
+ OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeCount,
+ "Set the depth at which omitting summary information stops (default is "
+ "1)."},
+ {LLDB_OPT_SET_1, false, "raw-output", 'R', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone, "Don't use formatting options."},
+ {LLDB_OPT_SET_1, false, "show-all-children", 'A', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Ignore the upper bound on the number of children to show."},
+ {LLDB_OPT_SET_1, false, "validate", 'V', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeBoolean, "Show results of type validators."},
+ {LLDB_OPT_SET_1, false, "element-count", 'Z',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount,
+ "Treat the result of the expression as if its type is an array of this "
+ "many values."}};
+
+llvm::ArrayRef<OptionDefinition>
+OptionGroupValueObjectDisplay::GetDefinitions() {
+ return llvm::makeArrayRef(g_option_table);
+}
+
+Status OptionGroupValueObjectDisplay::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = g_option_table[option_idx].short_option;
+ bool success = false;
+
+ switch (short_option) {
+ case 'd': {
+ int32_t result;
+ result = OptionArgParser::ToOptionEnum(option_arg, GetDynamicValueTypes(),
+ 2, error);
+ if (error.Success())
+ use_dynamic = (lldb::DynamicValueType)result;
+ } break;
+ case 'T':
+ show_types = true;
+ break;
+ case 'L':
+ show_location = true;
+ break;
+ case 'F':
+ flat_output = true;
+ break;
+ case 'O':
+ use_objc = true;
+ break;
+ case 'R':
+ be_raw = true;
+ break;
+ case 'A':
+ ignore_cap = true;
+ break;
+
+ case 'D':
+ if (option_arg.getAsInteger(0, max_depth)) {
+ max_depth = UINT32_MAX;
+ error.SetErrorStringWithFormat("invalid max depth '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+
+ case 'Z':
+ if (option_arg.getAsInteger(0, elem_count)) {
+ elem_count = UINT32_MAX;
+ error.SetErrorStringWithFormat("invalid element count '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+
+ case 'P':
+ if (option_arg.getAsInteger(0, ptr_depth)) {
+ ptr_depth = 0;
+ error.SetErrorStringWithFormat("invalid pointer depth '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+
+ case 'Y':
+ if (option_arg.empty())
+ no_summary_depth = 1;
+ else if (option_arg.getAsInteger(0, no_summary_depth)) {
+ no_summary_depth = 0;
+ error.SetErrorStringWithFormat("invalid pointer depth '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+
+ case 'S':
+ use_synth = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid synthetic-type '%s'",
+ option_arg.str().c_str());
+ break;
+
+ case 'V':
+ run_validator = OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (!success)
+ error.SetErrorStringWithFormat("invalid validate '%s'",
+ option_arg.str().c_str());
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+
+ return error;
+}
+
+void OptionGroupValueObjectDisplay::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ // If these defaults change, be sure to modify AnyOptionWasSet().
+ show_types = false;
+ no_summary_depth = 0;
+ show_location = false;
+ flat_output = false;
+ use_objc = false;
+ max_depth = UINT32_MAX;
+ ptr_depth = 0;
+ elem_count = 0;
+ use_synth = true;
+ be_raw = false;
+ ignore_cap = false;
+ run_validator = false;
+
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ if (target_sp)
+ use_dynamic = target_sp->GetPreferDynamicValue();
+ else {
+ // If we don't have any targets, then dynamic values won't do us much good.
+ use_dynamic = lldb::eNoDynamicValues;
+ }
+}
+
+DumpValueObjectOptions OptionGroupValueObjectDisplay::GetAsDumpOptions(
+ LanguageRuntimeDescriptionDisplayVerbosity lang_descr_verbosity,
+ lldb::Format format, lldb::TypeSummaryImplSP summary_sp) {
+ DumpValueObjectOptions options;
+ options.SetMaximumPointerDepth(
+ {DumpValueObjectOptions::PointerDepth::Mode::Always, ptr_depth});
+ if (use_objc)
+ options.SetShowSummary(false);
+ else
+ options.SetOmitSummaryDepth(no_summary_depth);
+ options.SetMaximumDepth(max_depth)
+ .SetShowTypes(show_types)
+ .SetShowLocation(show_location)
+ .SetUseObjectiveC(use_objc)
+ .SetUseDynamicType(use_dynamic)
+ .SetUseSyntheticValue(use_synth)
+ .SetFlatOutput(flat_output)
+ .SetIgnoreCap(ignore_cap)
+ .SetFormat(format)
+ .SetSummary(summary_sp);
+
+ if (lang_descr_verbosity ==
+ eLanguageRuntimeDescriptionDisplayVerbosityCompact)
+ options.SetHideRootType(use_objc).SetHideName(use_objc).SetHideValue(
+ use_objc);
+
+ if (be_raw)
+ options.SetRawDisplay();
+
+ options.SetRunValidator(run_validator);
+
+ options.SetElementCount(elem_count);
+
+ return options;
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupVariable.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupVariable.cpp
new file mode 100644
index 000000000000..d703c3dedcd9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupVariable.cpp
@@ -0,0 +1,145 @@
+//===-- OptionGroupVariable.cpp -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupVariable.h"
+
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// if you add any options here, remember to update the counters in
+// OptionGroupVariable::GetNumDefinitions()
+static constexpr OptionDefinition g_variable_options[] = {
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-args", 'a',
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
+ "Omit function arguments."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-recognized-args", 't',
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
+ "Omit recognized function arguments."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-locals", 'l',
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
+ "Omit local variables."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "show-globals", 'g',
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
+ "Show the current frame source file global and static variables."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "show-declaration", 'c',
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
+ "Show variable declaration information (source file and line where the "
+ "variable was declared)."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "regex", 'r',
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeRegularExpression,
+ "The <variable-name> argument for name lookups are regular expressions."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "scope", 's',
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
+ "Show variable scope (argument, local, global, static)."},
+ {LLDB_OPT_SET_1, false, "summary", 'y', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeName,
+ "Specify the summary that the variable output should use."},
+ {LLDB_OPT_SET_2, false, "summary-string", 'z',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeName,
+ "Specify a summary string to use to format the variable output."},
+};
+
+static Status ValidateNamedSummary(const char *str, void *) {
+ if (!str || !str[0])
+ return Status("must specify a valid named summary");
+ TypeSummaryImplSP summary_sp;
+ if (!DataVisualization::NamedSummaryFormats::GetSummaryFormat(
+ ConstString(str), summary_sp))
+ return Status("must specify a valid named summary");
+ return Status();
+}
+
+static Status ValidateSummaryString(const char *str, void *) {
+ if (!str || !str[0])
+ return Status("must specify a non-empty summary string");
+ return Status();
+}
+
+OptionGroupVariable::OptionGroupVariable(bool show_frame_options)
+ : OptionGroup(), include_frame_options(show_frame_options),
+ summary(ValidateNamedSummary), summary_string(ValidateSummaryString) {}
+
+OptionGroupVariable::~OptionGroupVariable() {}
+
+Status
+OptionGroupVariable::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ if (!include_frame_options)
+ option_idx += 3;
+ const int short_option = g_variable_options[option_idx].short_option;
+ switch (short_option) {
+ case 'r':
+ use_regex = true;
+ break;
+ case 'a':
+ show_args = false;
+ break;
+ case 'l':
+ show_locals = false;
+ break;
+ case 'g':
+ show_globals = true;
+ break;
+ case 'c':
+ show_decl = true;
+ break;
+ case 's':
+ show_scope = true;
+ break;
+ case 't':
+ show_recognized_args = false;
+ break;
+ case 'y':
+ error = summary.SetCurrentValue(option_arg);
+ break;
+ case 'z':
+ error = summary_string.SetCurrentValue(option_arg);
+ break;
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+}
+
+void OptionGroupVariable::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ show_args = true; // Frame option only
+ show_recognized_args = true; // Frame option only
+ show_locals = true; // Frame option only
+ show_globals = false; // Frame option only
+ show_decl = false;
+ use_regex = false;
+ show_scope = false;
+ summary.Clear();
+ summary_string.Clear();
+}
+
+#define NUM_FRAME_OPTS 3
+
+llvm::ArrayRef<OptionDefinition> OptionGroupVariable::GetDefinitions() {
+ auto result = llvm::makeArrayRef(g_variable_options);
+ // Show the "--no-args", "--no-locals" and "--show-globals" options if we are
+ // showing frame specific options
+ if (include_frame_options)
+ return result;
+
+ // Skip the "--no-args", "--no-locals" and "--show-globals" options if we are
+ // not showing frame specific options (globals only)
+ return result.drop_front(NUM_FRAME_OPTS);
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupWatchpoint.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupWatchpoint.cpp
new file mode 100644
index 000000000000..28e6b817fcc5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupWatchpoint.cpp
@@ -0,0 +1,92 @@
+//===-- OptionGroupWatchpoint.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupWatchpoint.h"
+
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/lldb-enumerations.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static constexpr OptionEnumValueElement g_watch_type[] = {
+ {OptionGroupWatchpoint::eWatchRead, "read", "Watch for read"},
+ {OptionGroupWatchpoint::eWatchWrite, "write", "Watch for write"},
+ {OptionGroupWatchpoint::eWatchReadWrite, "read_write",
+ "Watch for read/write"} };
+
+static constexpr OptionEnumValueElement g_watch_size[] = {
+ {1, "1", "Watch for byte size of 1"},
+ {2, "2", "Watch for byte size of 2"},
+ {4, "4", "Watch for byte size of 4"},
+ {8, "8", "Watch for byte size of 8"} };
+
+static constexpr OptionDefinition g_option_table[] = {
+ {LLDB_OPT_SET_1, false, "watch", 'w', OptionParser::eRequiredArgument,
+ nullptr, OptionEnumValues(g_watch_type), 0, eArgTypeWatchType,
+ "Specify the type of watching to perform."},
+ {LLDB_OPT_SET_1, false, "size", 's', OptionParser::eRequiredArgument,
+ nullptr, OptionEnumValues(g_watch_size), 0, eArgTypeByteSize,
+ "Number of bytes to use to watch a region."}};
+
+bool OptionGroupWatchpoint::IsWatchSizeSupported(uint32_t watch_size) {
+ for (const auto& size : g_watch_size) {
+ if (0 == size.value)
+ break;
+ if (watch_size == size.value)
+ return true;
+ }
+ return false;
+}
+
+OptionGroupWatchpoint::OptionGroupWatchpoint() : OptionGroup() {}
+
+OptionGroupWatchpoint::~OptionGroupWatchpoint() {}
+
+Status
+OptionGroupWatchpoint::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = g_option_table[option_idx].short_option;
+ switch (short_option) {
+ case 'w': {
+ WatchType tmp_watch_type;
+ tmp_watch_type = (WatchType)OptionArgParser::ToOptionEnum(
+ option_arg, g_option_table[option_idx].enum_values, 0, error);
+ if (error.Success()) {
+ watch_type = tmp_watch_type;
+ watch_type_specified = true;
+ }
+ break;
+ }
+ case 's':
+ watch_size = (uint32_t)OptionArgParser::ToOptionEnum(
+ option_arg, g_option_table[option_idx].enum_values, 0, error);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+}
+
+void OptionGroupWatchpoint::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ watch_type_specified = false;
+ watch_type = eWatchInvalid;
+ watch_size = 0;
+}
+
+llvm::ArrayRef<OptionDefinition> OptionGroupWatchpoint::GetDefinitions() {
+ return llvm::makeArrayRef(g_option_table);
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValue.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValue.cpp
new file mode 100644
index 000000000000..00c8642595b7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValue.cpp
@@ -0,0 +1,615 @@
+//===-- OptionValue.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValue.h"
+
+#include "lldb/Interpreter/OptionValues.h"
+#include "lldb/Utility/StringList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Get this value as a uint64_t value if it is encoded as a boolean, uint64_t
+// or int64_t. Other types will cause "fail_value" to be returned
+uint64_t OptionValue::GetUInt64Value(uint64_t fail_value, bool *success_ptr) {
+ if (success_ptr)
+ *success_ptr = true;
+ switch (GetType()) {
+ case OptionValue::eTypeBoolean:
+ return static_cast<OptionValueBoolean *>(this)->GetCurrentValue();
+ case OptionValue::eTypeSInt64:
+ return static_cast<OptionValueSInt64 *>(this)->GetCurrentValue();
+ case OptionValue::eTypeUInt64:
+ return static_cast<OptionValueUInt64 *>(this)->GetCurrentValue();
+ default:
+ break;
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+Status OptionValue::SetSubValue(const ExecutionContext *exe_ctx,
+ VarSetOperationType op, llvm::StringRef name,
+ llvm::StringRef value) {
+ Status error;
+ error.SetErrorStringWithFormat("SetSubValue is not supported");
+ return error;
+}
+
+OptionValueBoolean *OptionValue::GetAsBoolean() {
+ if (GetType() == OptionValue::eTypeBoolean)
+ return static_cast<OptionValueBoolean *>(this);
+ return nullptr;
+}
+
+const OptionValueBoolean *OptionValue::GetAsBoolean() const {
+ if (GetType() == OptionValue::eTypeBoolean)
+ return static_cast<const OptionValueBoolean *>(this);
+ return nullptr;
+}
+
+const OptionValueChar *OptionValue::GetAsChar() const {
+ if (GetType() == OptionValue::eTypeChar)
+ return static_cast<const OptionValueChar *>(this);
+ return nullptr;
+}
+
+OptionValueChar *OptionValue::GetAsChar() {
+ if (GetType() == OptionValue::eTypeChar)
+ return static_cast<OptionValueChar *>(this);
+ return nullptr;
+}
+
+OptionValueFileSpec *OptionValue::GetAsFileSpec() {
+ if (GetType() == OptionValue::eTypeFileSpec)
+ return static_cast<OptionValueFileSpec *>(this);
+ return nullptr;
+}
+
+const OptionValueFileSpec *OptionValue::GetAsFileSpec() const {
+ if (GetType() == OptionValue::eTypeFileSpec)
+ return static_cast<const OptionValueFileSpec *>(this);
+ return nullptr;
+}
+
+OptionValueFileSpecList *OptionValue::GetAsFileSpecList() {
+ if (GetType() == OptionValue::eTypeFileSpecList)
+ return static_cast<OptionValueFileSpecList *>(this);
+ return nullptr;
+}
+
+const OptionValueFileSpecList *OptionValue::GetAsFileSpecList() const {
+ if (GetType() == OptionValue::eTypeFileSpecList)
+ return static_cast<const OptionValueFileSpecList *>(this);
+ return nullptr;
+}
+
+OptionValueArch *OptionValue::GetAsArch() {
+ if (GetType() == OptionValue::eTypeArch)
+ return static_cast<OptionValueArch *>(this);
+ return nullptr;
+}
+
+const OptionValueArch *OptionValue::GetAsArch() const {
+ if (GetType() == OptionValue::eTypeArch)
+ return static_cast<const OptionValueArch *>(this);
+ return nullptr;
+}
+
+OptionValueArray *OptionValue::GetAsArray() {
+ if (GetType() == OptionValue::eTypeArray)
+ return static_cast<OptionValueArray *>(this);
+ return nullptr;
+}
+
+const OptionValueArray *OptionValue::GetAsArray() const {
+ if (GetType() == OptionValue::eTypeArray)
+ return static_cast<const OptionValueArray *>(this);
+ return nullptr;
+}
+
+OptionValueArgs *OptionValue::GetAsArgs() {
+ if (GetType() == OptionValue::eTypeArgs)
+ return static_cast<OptionValueArgs *>(this);
+ return nullptr;
+}
+
+const OptionValueArgs *OptionValue::GetAsArgs() const {
+ if (GetType() == OptionValue::eTypeArgs)
+ return static_cast<const OptionValueArgs *>(this);
+ return nullptr;
+}
+
+OptionValueDictionary *OptionValue::GetAsDictionary() {
+ if (GetType() == OptionValue::eTypeDictionary)
+ return static_cast<OptionValueDictionary *>(this);
+ return nullptr;
+}
+
+const OptionValueDictionary *OptionValue::GetAsDictionary() const {
+ if (GetType() == OptionValue::eTypeDictionary)
+ return static_cast<const OptionValueDictionary *>(this);
+ return nullptr;
+}
+
+OptionValueEnumeration *OptionValue::GetAsEnumeration() {
+ if (GetType() == OptionValue::eTypeEnum)
+ return static_cast<OptionValueEnumeration *>(this);
+ return nullptr;
+}
+
+const OptionValueEnumeration *OptionValue::GetAsEnumeration() const {
+ if (GetType() == OptionValue::eTypeEnum)
+ return static_cast<const OptionValueEnumeration *>(this);
+ return nullptr;
+}
+
+OptionValueFormat *OptionValue::GetAsFormat() {
+ if (GetType() == OptionValue::eTypeFormat)
+ return static_cast<OptionValueFormat *>(this);
+ return nullptr;
+}
+
+const OptionValueFormat *OptionValue::GetAsFormat() const {
+ if (GetType() == OptionValue::eTypeFormat)
+ return static_cast<const OptionValueFormat *>(this);
+ return nullptr;
+}
+
+OptionValueLanguage *OptionValue::GetAsLanguage() {
+ if (GetType() == OptionValue::eTypeLanguage)
+ return static_cast<OptionValueLanguage *>(this);
+ return nullptr;
+}
+
+const OptionValueLanguage *OptionValue::GetAsLanguage() const {
+ if (GetType() == OptionValue::eTypeLanguage)
+ return static_cast<const OptionValueLanguage *>(this);
+ return nullptr;
+}
+
+OptionValueFormatEntity *OptionValue::GetAsFormatEntity() {
+ if (GetType() == OptionValue::eTypeFormatEntity)
+ return static_cast<OptionValueFormatEntity *>(this);
+ return nullptr;
+}
+
+const OptionValueFormatEntity *OptionValue::GetAsFormatEntity() const {
+ if (GetType() == OptionValue::eTypeFormatEntity)
+ return static_cast<const OptionValueFormatEntity *>(this);
+ return nullptr;
+}
+
+OptionValuePathMappings *OptionValue::GetAsPathMappings() {
+ if (GetType() == OptionValue::eTypePathMap)
+ return static_cast<OptionValuePathMappings *>(this);
+ return nullptr;
+}
+
+const OptionValuePathMappings *OptionValue::GetAsPathMappings() const {
+ if (GetType() == OptionValue::eTypePathMap)
+ return static_cast<const OptionValuePathMappings *>(this);
+ return nullptr;
+}
+
+OptionValueProperties *OptionValue::GetAsProperties() {
+ if (GetType() == OptionValue::eTypeProperties)
+ return static_cast<OptionValueProperties *>(this);
+ return nullptr;
+}
+
+const OptionValueProperties *OptionValue::GetAsProperties() const {
+ if (GetType() == OptionValue::eTypeProperties)
+ return static_cast<const OptionValueProperties *>(this);
+ return nullptr;
+}
+
+OptionValueRegex *OptionValue::GetAsRegex() {
+ if (GetType() == OptionValue::eTypeRegex)
+ return static_cast<OptionValueRegex *>(this);
+ return nullptr;
+}
+
+const OptionValueRegex *OptionValue::GetAsRegex() const {
+ if (GetType() == OptionValue::eTypeRegex)
+ return static_cast<const OptionValueRegex *>(this);
+ return nullptr;
+}
+
+OptionValueSInt64 *OptionValue::GetAsSInt64() {
+ if (GetType() == OptionValue::eTypeSInt64)
+ return static_cast<OptionValueSInt64 *>(this);
+ return nullptr;
+}
+
+const OptionValueSInt64 *OptionValue::GetAsSInt64() const {
+ if (GetType() == OptionValue::eTypeSInt64)
+ return static_cast<const OptionValueSInt64 *>(this);
+ return nullptr;
+}
+
+OptionValueString *OptionValue::GetAsString() {
+ if (GetType() == OptionValue::eTypeString)
+ return static_cast<OptionValueString *>(this);
+ return nullptr;
+}
+
+const OptionValueString *OptionValue::GetAsString() const {
+ if (GetType() == OptionValue::eTypeString)
+ return static_cast<const OptionValueString *>(this);
+ return nullptr;
+}
+
+OptionValueUInt64 *OptionValue::GetAsUInt64() {
+ if (GetType() == OptionValue::eTypeUInt64)
+ return static_cast<OptionValueUInt64 *>(this);
+ return nullptr;
+}
+
+const OptionValueUInt64 *OptionValue::GetAsUInt64() const {
+ if (GetType() == OptionValue::eTypeUInt64)
+ return static_cast<const OptionValueUInt64 *>(this);
+ return nullptr;
+}
+
+OptionValueUUID *OptionValue::GetAsUUID() {
+ if (GetType() == OptionValue::eTypeUUID)
+ return static_cast<OptionValueUUID *>(this);
+ return nullptr;
+}
+
+const OptionValueUUID *OptionValue::GetAsUUID() const {
+ if (GetType() == OptionValue::eTypeUUID)
+ return static_cast<const OptionValueUUID *>(this);
+ return nullptr;
+}
+
+bool OptionValue::GetBooleanValue(bool fail_value) const {
+ const OptionValueBoolean *option_value = GetAsBoolean();
+ if (option_value)
+ return option_value->GetCurrentValue();
+ return fail_value;
+}
+
+bool OptionValue::SetBooleanValue(bool new_value) {
+ OptionValueBoolean *option_value = GetAsBoolean();
+ if (option_value) {
+ option_value->SetCurrentValue(new_value);
+ return true;
+ }
+ return false;
+}
+
+char OptionValue::GetCharValue(char fail_value) const {
+ const OptionValueChar *option_value = GetAsChar();
+ if (option_value)
+ return option_value->GetCurrentValue();
+ return fail_value;
+}
+
+char OptionValue::SetCharValue(char new_value) {
+ OptionValueChar *option_value = GetAsChar();
+ if (option_value) {
+ option_value->SetCurrentValue(new_value);
+ return true;
+ }
+ return false;
+}
+
+int64_t OptionValue::GetEnumerationValue(int64_t fail_value) const {
+ const OptionValueEnumeration *option_value = GetAsEnumeration();
+ if (option_value)
+ return option_value->GetCurrentValue();
+ return fail_value;
+}
+
+bool OptionValue::SetEnumerationValue(int64_t value) {
+ OptionValueEnumeration *option_value = GetAsEnumeration();
+ if (option_value) {
+ option_value->SetCurrentValue(value);
+ return true;
+ }
+ return false;
+}
+
+FileSpec OptionValue::GetFileSpecValue() const {
+ const OptionValueFileSpec *option_value = GetAsFileSpec();
+ if (option_value)
+ return option_value->GetCurrentValue();
+ return FileSpec();
+}
+
+bool OptionValue::SetFileSpecValue(const FileSpec &file_spec) {
+ OptionValueFileSpec *option_value = GetAsFileSpec();
+ if (option_value) {
+ option_value->SetCurrentValue(file_spec, false);
+ return true;
+ }
+ return false;
+}
+
+FileSpecList OptionValue::GetFileSpecListValue() const {
+ const OptionValueFileSpecList *option_value = GetAsFileSpecList();
+ if (option_value)
+ return option_value->GetCurrentValue();
+ return FileSpecList();
+}
+
+lldb::Format OptionValue::GetFormatValue(lldb::Format fail_value) const {
+ const OptionValueFormat *option_value = GetAsFormat();
+ if (option_value)
+ return option_value->GetCurrentValue();
+ return fail_value;
+}
+
+bool OptionValue::SetFormatValue(lldb::Format new_value) {
+ OptionValueFormat *option_value = GetAsFormat();
+ if (option_value) {
+ option_value->SetCurrentValue(new_value);
+ return true;
+ }
+ return false;
+}
+
+lldb::LanguageType
+OptionValue::GetLanguageValue(lldb::LanguageType fail_value) const {
+ const OptionValueLanguage *option_value = GetAsLanguage();
+ if (option_value)
+ return option_value->GetCurrentValue();
+ return fail_value;
+}
+
+bool OptionValue::SetLanguageValue(lldb::LanguageType new_language) {
+ OptionValueLanguage *option_value = GetAsLanguage();
+ if (option_value) {
+ option_value->SetCurrentValue(new_language);
+ return true;
+ }
+ return false;
+}
+
+const FormatEntity::Entry *OptionValue::GetFormatEntity() const {
+ const OptionValueFormatEntity *option_value = GetAsFormatEntity();
+ if (option_value)
+ return &option_value->GetCurrentValue();
+ return nullptr;
+}
+
+const RegularExpression *OptionValue::GetRegexValue() const {
+ const OptionValueRegex *option_value = GetAsRegex();
+ if (option_value)
+ return option_value->GetCurrentValue();
+ return nullptr;
+}
+
+int64_t OptionValue::GetSInt64Value(int64_t fail_value) const {
+ const OptionValueSInt64 *option_value = GetAsSInt64();
+ if (option_value)
+ return option_value->GetCurrentValue();
+ return fail_value;
+}
+
+bool OptionValue::SetSInt64Value(int64_t new_value) {
+ OptionValueSInt64 *option_value = GetAsSInt64();
+ if (option_value) {
+ option_value->SetCurrentValue(new_value);
+ return true;
+ }
+ return false;
+}
+
+llvm::StringRef OptionValue::GetStringValue(llvm::StringRef fail_value) const {
+ const OptionValueString *option_value = GetAsString();
+ if (option_value)
+ return option_value->GetCurrentValueAsRef();
+ return fail_value;
+}
+
+bool OptionValue::SetStringValue(llvm::StringRef new_value) {
+ OptionValueString *option_value = GetAsString();
+ if (option_value) {
+ option_value->SetCurrentValue(new_value);
+ return true;
+ }
+ return false;
+}
+
+uint64_t OptionValue::GetUInt64Value(uint64_t fail_value) const {
+ const OptionValueUInt64 *option_value = GetAsUInt64();
+ if (option_value)
+ return option_value->GetCurrentValue();
+ return fail_value;
+}
+
+bool OptionValue::SetUInt64Value(uint64_t new_value) {
+ OptionValueUInt64 *option_value = GetAsUInt64();
+ if (option_value) {
+ option_value->SetCurrentValue(new_value);
+ return true;
+ }
+ return false;
+}
+
+UUID OptionValue::GetUUIDValue() const {
+ const OptionValueUUID *option_value = GetAsUUID();
+ if (option_value)
+ return option_value->GetCurrentValue();
+ return UUID();
+}
+
+bool OptionValue::SetUUIDValue(const UUID &uuid) {
+ OptionValueUUID *option_value = GetAsUUID();
+ if (option_value) {
+ option_value->SetCurrentValue(uuid);
+ return true;
+ }
+ return false;
+}
+
+const char *OptionValue::GetBuiltinTypeAsCString(Type t) {
+ switch (t) {
+ case eTypeInvalid:
+ return "invalid";
+ case eTypeArch:
+ return "arch";
+ case eTypeArgs:
+ return "arguments";
+ case eTypeArray:
+ return "array";
+ case eTypeBoolean:
+ return "boolean";
+ case eTypeChar:
+ return "char";
+ case eTypeDictionary:
+ return "dictionary";
+ case eTypeEnum:
+ return "enum";
+ case eTypeFileSpec:
+ return "file";
+ case eTypeFileSpecList:
+ return "file-list";
+ case eTypeFormat:
+ return "format";
+ case eTypeFormatEntity:
+ return "format-string";
+ case eTypeLanguage:
+ return "language";
+ case eTypePathMap:
+ return "path-map";
+ case eTypeProperties:
+ return "properties";
+ case eTypeRegex:
+ return "regex";
+ case eTypeSInt64:
+ return "int";
+ case eTypeString:
+ return "string";
+ case eTypeUInt64:
+ return "unsigned";
+ case eTypeUUID:
+ return "uuid";
+ }
+ return nullptr;
+}
+
+lldb::OptionValueSP OptionValue::CreateValueFromCStringForTypeMask(
+ const char *value_cstr, uint32_t type_mask, Status &error) {
+ // If only 1 bit is set in the type mask for a dictionary or array then we
+ // know how to decode a value from a cstring
+ lldb::OptionValueSP value_sp;
+ switch (type_mask) {
+ case 1u << eTypeArch:
+ value_sp.reset(new OptionValueArch());
+ break;
+ case 1u << eTypeBoolean:
+ value_sp.reset(new OptionValueBoolean(false));
+ break;
+ case 1u << eTypeChar:
+ value_sp.reset(new OptionValueChar('\0'));
+ break;
+ case 1u << eTypeFileSpec:
+ value_sp.reset(new OptionValueFileSpec());
+ break;
+ case 1u << eTypeFormat:
+ value_sp.reset(new OptionValueFormat(eFormatInvalid));
+ break;
+ case 1u << eTypeFormatEntity:
+ value_sp.reset(new OptionValueFormatEntity(nullptr));
+ break;
+ case 1u << eTypeLanguage:
+ value_sp.reset(new OptionValueLanguage(eLanguageTypeUnknown));
+ break;
+ case 1u << eTypeSInt64:
+ value_sp.reset(new OptionValueSInt64());
+ break;
+ case 1u << eTypeString:
+ value_sp.reset(new OptionValueString());
+ break;
+ case 1u << eTypeUInt64:
+ value_sp.reset(new OptionValueUInt64());
+ break;
+ case 1u << eTypeUUID:
+ value_sp.reset(new OptionValueUUID());
+ break;
+ }
+
+ if (value_sp)
+ error = value_sp->SetValueFromString(
+ llvm::StringRef::withNullAsEmpty(value_cstr), eVarSetOperationAssign);
+ else
+ error.SetErrorString("unsupported type mask");
+ return value_sp;
+}
+
+bool OptionValue::DumpQualifiedName(Stream &strm) const {
+ bool dumped_something = false;
+ lldb::OptionValueSP m_parent_sp(m_parent_wp.lock());
+ if (m_parent_sp) {
+ if (m_parent_sp->DumpQualifiedName(strm))
+ dumped_something = true;
+ }
+ ConstString name(GetName());
+ if (name) {
+ if (dumped_something)
+ strm.PutChar('.');
+ else
+ dumped_something = true;
+ strm << name;
+ }
+ return dumped_something;
+}
+
+size_t OptionValue::AutoComplete(CommandInterpreter &interpreter,
+ CompletionRequest &request) {
+ request.SetWordComplete(false);
+ return request.GetNumberOfMatches();
+}
+
+Status OptionValue::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationReplace:
+ error.SetErrorStringWithFormat(
+ "%s objects do not support the 'replace' operation",
+ GetTypeAsCString());
+ break;
+ case eVarSetOperationInsertBefore:
+ error.SetErrorStringWithFormat(
+ "%s objects do not support the 'insert-before' operation",
+ GetTypeAsCString());
+ break;
+ case eVarSetOperationInsertAfter:
+ error.SetErrorStringWithFormat(
+ "%s objects do not support the 'insert-after' operation",
+ GetTypeAsCString());
+ break;
+ case eVarSetOperationRemove:
+ error.SetErrorStringWithFormat(
+ "%s objects do not support the 'remove' operation", GetTypeAsCString());
+ break;
+ case eVarSetOperationAppend:
+ error.SetErrorStringWithFormat(
+ "%s objects do not support the 'append' operation", GetTypeAsCString());
+ break;
+ case eVarSetOperationClear:
+ error.SetErrorStringWithFormat(
+ "%s objects do not support the 'clear' operation", GetTypeAsCString());
+ break;
+ case eVarSetOperationAssign:
+ error.SetErrorStringWithFormat(
+ "%s objects do not support the 'assign' operation", GetTypeAsCString());
+ break;
+ case eVarSetOperationInvalid:
+ error.SetErrorStringWithFormat("invalid operation performed on a %s object",
+ GetTypeAsCString());
+ break;
+ }
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueArch.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArch.cpp
new file mode 100644
index 000000000000..92dc45d092be
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArch.cpp
@@ -0,0 +1,78 @@
+//===-- OptionValueArch.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueArch.h"
+
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueArch::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
+ uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+
+ if (m_current_value.IsValid()) {
+ const char *arch_name = m_current_value.GetArchitectureName();
+ if (arch_name)
+ strm.PutCString(arch_name);
+ }
+ }
+}
+
+Status OptionValueArch::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign: {
+ std::string value_str = value.trim().str();
+ if (m_current_value.SetTriple(value_str.c_str())) {
+ m_value_was_set = true;
+ NotifyValueChanged();
+ } else
+ error.SetErrorStringWithFormat("unsupported architecture '%s'",
+ value_str.c_str());
+ break;
+ }
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ case eVarSetOperationAppend:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueArch::DeepCopy() const {
+ return OptionValueSP(new OptionValueArch(*this));
+}
+
+size_t OptionValueArch::AutoComplete(CommandInterpreter &interpreter,
+ CompletionRequest &request) {
+ request.SetWordComplete(false);
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ interpreter, CommandCompletions::eArchitectureCompletion, request,
+ nullptr);
+ return request.GetNumberOfMatches();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueArgs.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArgs.cpp
new file mode 100644
index 000000000000..d619dba5678a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArgs.cpp
@@ -0,0 +1,25 @@
+//===-- OptionValueArgs.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueArgs.h"
+
+#include "lldb/Utility/Args.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+size_t OptionValueArgs::GetArgs(Args &args) {
+ args.Clear();
+ for (auto value : m_values) {
+ llvm::StringRef string_value = value->GetStringValue();
+ if (!string_value.empty())
+ args.AppendArgument(string_value);
+ }
+
+ return args.GetArgumentCount();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueArray.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArray.cpp
new file mode 100644
index 000000000000..30902c0f295a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArray.cpp
@@ -0,0 +1,316 @@
+//===-- OptionValueArray.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueArray.h"
+
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
+ uint32_t dump_mask) {
+ const Type array_element_type = ConvertTypeMaskToType(m_type_mask);
+ if (dump_mask & eDumpOptionType) {
+ if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid))
+ strm.Printf("(%s of %ss)", GetTypeAsCString(),
+ GetBuiltinTypeAsCString(array_element_type));
+ else
+ strm.Printf("(%s)", GetTypeAsCString());
+ }
+ if (dump_mask & eDumpOptionValue) {
+ const bool one_line = dump_mask & eDumpOptionCommand;
+ const uint32_t size = m_values.size();
+ if (dump_mask & eDumpOptionType)
+ strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : "");
+ if (!one_line)
+ strm.IndentMore();
+ for (uint32_t i = 0; i < size; ++i) {
+ if (!one_line) {
+ strm.Indent();
+ strm.Printf("[%u]: ", i);
+ }
+ const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
+ switch (array_element_type) {
+ default:
+ case eTypeArray:
+ case eTypeDictionary:
+ case eTypeProperties:
+ case eTypeFileSpecList:
+ case eTypePathMap:
+ m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
+ break;
+
+ case eTypeBoolean:
+ case eTypeChar:
+ case eTypeEnum:
+ case eTypeFileSpec:
+ case eTypeFormat:
+ case eTypeSInt64:
+ case eTypeString:
+ case eTypeUInt64:
+ case eTypeUUID:
+ // No need to show the type for dictionaries of simple items
+ m_values[i]->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) |
+ extra_dump_options);
+ break;
+ }
+
+ if (!one_line) {
+ if (i < (size - 1))
+ strm.EOL();
+ } else {
+ strm << ' ';
+ }
+ }
+ if (!one_line)
+ strm.IndentLess();
+ }
+}
+
+Status OptionValueArray::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Args args(value.str());
+ Status error = SetArgs(args, op);
+ if (error.Success())
+ NotifyValueChanged();
+ return error;
+}
+
+lldb::OptionValueSP
+OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx,
+ llvm::StringRef name, bool will_modify,
+ Status &error) const {
+ if (name.empty() || name.front() != '[') {
+ error.SetErrorStringWithFormat(
+ "invalid value path '%s', %s values only support '[<index>]' subvalues "
+ "where <index> is a positive or negative array index",
+ name.str().c_str(), GetTypeAsCString());
+ return nullptr;
+ }
+
+ name = name.drop_front();
+ llvm::StringRef index, sub_value;
+ std::tie(index, sub_value) = name.split(']');
+ if (index.size() == name.size()) {
+ // Couldn't find a closing bracket
+ return nullptr;
+ }
+
+ const size_t array_count = m_values.size();
+ int32_t idx = 0;
+ if (index.getAsInteger(0, idx))
+ return nullptr;
+
+ uint32_t new_idx = UINT32_MAX;
+ if (idx < 0) {
+ // Access from the end of the array if the index is negative
+ new_idx = array_count - idx;
+ } else {
+ // Just a standard index
+ new_idx = idx;
+ }
+
+ if (new_idx < array_count) {
+ if (m_values[new_idx]) {
+ if (!sub_value.empty())
+ return m_values[new_idx]->GetSubValue(exe_ctx, sub_value,
+ will_modify, error);
+ else
+ return m_values[new_idx];
+ }
+ } else {
+ if (array_count == 0)
+ error.SetErrorStringWithFormat(
+ "index %i is not valid for an empty array", idx);
+ else if (idx > 0)
+ error.SetErrorStringWithFormat(
+ "index %i out of range, valid values are 0 through %" PRIu64,
+ idx, (uint64_t)(array_count - 1));
+ else
+ error.SetErrorStringWithFormat("negative index %i out of range, "
+ "valid values are -1 through "
+ "-%" PRIu64,
+ idx, (uint64_t)array_count);
+ }
+ return OptionValueSP();
+}
+
+size_t OptionValueArray::GetArgs(Args &args) const {
+ args.Clear();
+ const uint32_t size = m_values.size();
+ for (uint32_t i = 0; i < size; ++i) {
+ llvm::StringRef string_value = m_values[i]->GetStringValue();
+ if (!string_value.empty())
+ args.AppendArgument(string_value);
+ }
+
+ return args.GetArgumentCount();
+}
+
+Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) {
+ Status error;
+ const size_t argc = args.GetArgumentCount();
+ switch (op) {
+ case eVarSetOperationInvalid:
+ error.SetErrorString("unsupported operation");
+ break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ if (argc > 1) {
+ uint32_t idx =
+ StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
+ const uint32_t count = GetSize();
+ if (idx > count) {
+ error.SetErrorStringWithFormat(
+ "invalid insert array index %u, index must be 0 through %u", idx,
+ count);
+ } else {
+ if (op == eVarSetOperationInsertAfter)
+ ++idx;
+ for (size_t i = 1; i < argc; ++i, ++idx) {
+ lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
+ args.GetArgumentAtIndex(i), m_type_mask, error));
+ if (value_sp) {
+ if (error.Fail())
+ return error;
+ if (idx >= m_values.size())
+ m_values.push_back(value_sp);
+ else
+ m_values.insert(m_values.begin() + idx, value_sp);
+ } else {
+ error.SetErrorString(
+ "array of complex types must subclass OptionValueArray");
+ return error;
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("insert operation takes an array index followed by "
+ "one or more values");
+ }
+ break;
+
+ case eVarSetOperationRemove:
+ if (argc > 0) {
+ const uint32_t size = m_values.size();
+ std::vector<int> remove_indexes;
+ bool all_indexes_valid = true;
+ size_t i;
+ for (i = 0; i < argc; ++i) {
+ const size_t idx =
+ StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX);
+ if (idx >= size) {
+ all_indexes_valid = false;
+ break;
+ } else
+ remove_indexes.push_back(idx);
+ }
+
+ if (all_indexes_valid) {
+ size_t num_remove_indexes = remove_indexes.size();
+ if (num_remove_indexes) {
+ // Sort and then erase in reverse so indexes are always valid
+ if (num_remove_indexes > 1) {
+ llvm::sort(remove_indexes.begin(), remove_indexes.end());
+ for (std::vector<int>::const_reverse_iterator
+ pos = remove_indexes.rbegin(),
+ end = remove_indexes.rend();
+ pos != end; ++pos) {
+ m_values.erase(m_values.begin() + *pos);
+ }
+ } else {
+ // Only one index
+ m_values.erase(m_values.begin() + remove_indexes.front());
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "invalid array index '%s', aborting remove operation",
+ args.GetArgumentAtIndex(i));
+ }
+ } else {
+ error.SetErrorString("remove operation takes one or more array indices");
+ }
+ break;
+
+ case eVarSetOperationClear:
+ Clear();
+ break;
+
+ case eVarSetOperationReplace:
+ if (argc > 1) {
+ uint32_t idx =
+ StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
+ const uint32_t count = GetSize();
+ if (idx > count) {
+ error.SetErrorStringWithFormat(
+ "invalid replace array index %u, index must be 0 through %u", idx,
+ count);
+ } else {
+ for (size_t i = 1; i < argc; ++i, ++idx) {
+ lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
+ args.GetArgumentAtIndex(i), m_type_mask, error));
+ if (value_sp) {
+ if (error.Fail())
+ return error;
+ if (idx < count)
+ m_values[idx] = value_sp;
+ else
+ m_values.push_back(value_sp);
+ } else {
+ error.SetErrorString(
+ "array of complex types must subclass OptionValueArray");
+ return error;
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("replace operation takes an array index followed by "
+ "one or more values");
+ }
+ break;
+
+ case eVarSetOperationAssign:
+ m_values.clear();
+ // Fall through to append case
+ LLVM_FALLTHROUGH;
+ case eVarSetOperationAppend:
+ for (size_t i = 0; i < argc; ++i) {
+ lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
+ args.GetArgumentAtIndex(i), m_type_mask, error));
+ if (value_sp) {
+ if (error.Fail())
+ return error;
+ m_value_was_set = true;
+ AppendValue(value_sp);
+ } else {
+ error.SetErrorString(
+ "array of complex types must subclass OptionValueArray");
+ }
+ }
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueArray::DeepCopy() const {
+ OptionValueArray *copied_array =
+ new OptionValueArray(m_type_mask, m_raw_value_dump);
+ lldb::OptionValueSP copied_value_sp(copied_array);
+ *static_cast<OptionValue *>(copied_array) = *this;
+ copied_array->m_callback = m_callback;
+ const uint32_t size = m_values.size();
+ for (uint32_t i = 0; i < size; ++i) {
+ copied_array->AppendValue(m_values[i]->DeepCopy());
+ }
+ return copied_value_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueBoolean.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueBoolean.cpp
new file mode 100644
index 000000000000..8be8220fb306
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueBoolean.cpp
@@ -0,0 +1,91 @@
+//===-- OptionValueBoolean.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueBoolean.h"
+
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueBoolean::DumpValue(const ExecutionContext *exe_ctx,
+ Stream &strm, uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ // if (dump_mask & eDumpOptionName)
+ // DumpQualifiedName (strm);
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+ strm.PutCString(m_current_value ? "true" : "false");
+ }
+}
+
+Status OptionValueBoolean::SetValueFromString(llvm::StringRef value_str,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign: {
+ bool success = false;
+ bool value = OptionArgParser::ToBoolean(value_str, false, &success);
+ if (success) {
+ m_value_was_set = true;
+ m_current_value = value;
+ NotifyValueChanged();
+ } else {
+ if (value_str.size() == 0)
+ error.SetErrorString("invalid boolean string value <empty>");
+ else
+ error.SetErrorStringWithFormat("invalid boolean string value: '%s'",
+ value_str.str().c_str());
+ }
+ } break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ case eVarSetOperationAppend:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value_str, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueBoolean::DeepCopy() const {
+ return OptionValueSP(new OptionValueBoolean(*this));
+}
+
+size_t OptionValueBoolean::AutoComplete(CommandInterpreter &interpreter,
+ CompletionRequest &request) {
+ request.SetWordComplete(false);
+ static const llvm::StringRef g_autocomplete_entries[] = {
+ "true", "false", "on", "off", "yes", "no", "1", "0"};
+
+ auto entries = llvm::makeArrayRef(g_autocomplete_entries);
+
+ // only suggest "true" or "false" by default
+ if (request.GetCursorArgumentPrefix().empty())
+ entries = entries.take_front(2);
+
+ for (auto entry : entries) {
+ if (entry.startswith_lower(request.GetCursorArgumentPrefix()))
+ request.AddCompletion(entry);
+ }
+ return request.GetNumberOfMatches();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueChar.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueChar.cpp
new file mode 100644
index 000000000000..23012e6e2ec3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueChar.cpp
@@ -0,0 +1,63 @@
+//===-- OptionValueChar.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueChar.h"
+
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueChar::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
+ uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+ if (m_current_value != '\0')
+ strm.PutChar(m_current_value);
+ else
+ strm.PutCString("(null)");
+ }
+}
+
+Status OptionValueChar::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign: {
+ bool success = false;
+ char char_value = OptionArgParser::ToChar(value, '\0', &success);
+ if (success) {
+ m_current_value = char_value;
+ m_value_was_set = true;
+ } else
+ error.SetErrorStringWithFormat("'%s' cannot be longer than 1 character",
+ value.str().c_str());
+ } break;
+
+ default:
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueChar::DeepCopy() const {
+ return OptionValueSP(new OptionValueChar(*this));
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueDictionary.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueDictionary.cpp
new file mode 100644
index 000000000000..eb66c485bfd1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueDictionary.cpp
@@ -0,0 +1,324 @@
+//===-- OptionValueDictionary.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueDictionary.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx,
+ Stream &strm, uint32_t dump_mask) {
+ const Type dict_type = ConvertTypeMaskToType(m_type_mask);
+ if (dump_mask & eDumpOptionType) {
+ if (m_type_mask != eTypeInvalid)
+ strm.Printf("(%s of %ss)", GetTypeAsCString(),
+ GetBuiltinTypeAsCString(dict_type));
+ else
+ strm.Printf("(%s)", GetTypeAsCString());
+ }
+ if (dump_mask & eDumpOptionValue) {
+ const bool one_line = dump_mask & eDumpOptionCommand;
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" =");
+
+ collection::iterator pos, end = m_values.end();
+
+ if (!one_line)
+ strm.IndentMore();
+
+ for (pos = m_values.begin(); pos != end; ++pos) {
+ OptionValue *option_value = pos->second.get();
+
+ if (one_line)
+ strm << ' ';
+ else
+ strm.EOL();
+
+ strm.Indent(pos->first.GetCString());
+
+ const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
+ switch (dict_type) {
+ default:
+ case eTypeArray:
+ case eTypeDictionary:
+ case eTypeProperties:
+ case eTypeFileSpecList:
+ case eTypePathMap:
+ strm.PutChar(' ');
+ option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
+ break;
+
+ case eTypeBoolean:
+ case eTypeChar:
+ case eTypeEnum:
+ case eTypeFileSpec:
+ case eTypeFormat:
+ case eTypeSInt64:
+ case eTypeString:
+ case eTypeUInt64:
+ case eTypeUUID:
+ // No need to show the type for dictionaries of simple items
+ strm.PutCString("=");
+ option_value->DumpValue(exe_ctx, strm,
+ (dump_mask & (~eDumpOptionType)) |
+ extra_dump_options);
+ break;
+ }
+ }
+ if (!one_line)
+ strm.IndentLess();
+ }
+}
+
+size_t OptionValueDictionary::GetArgs(Args &args) const {
+ args.Clear();
+ collection::const_iterator pos, end = m_values.end();
+ for (pos = m_values.begin(); pos != end; ++pos) {
+ StreamString strm;
+ strm.Printf("%s=", pos->first.GetCString());
+ pos->second->DumpValue(nullptr, strm, eDumpOptionValue | eDumpOptionRaw);
+ args.AppendArgument(strm.GetString());
+ }
+ return args.GetArgumentCount();
+}
+
+Status OptionValueDictionary::SetArgs(const Args &args,
+ VarSetOperationType op) {
+ Status error;
+ const size_t argc = args.GetArgumentCount();
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ break;
+
+ case eVarSetOperationAppend:
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign:
+ if (argc == 0) {
+ error.SetErrorString(
+ "assign operation takes one or more key=value arguments");
+ return error;
+ }
+ for (const auto &entry : args) {
+ if (entry.ref.empty()) {
+ error.SetErrorString("empty argument");
+ return error;
+ }
+ if (!entry.ref.contains('=')) {
+ error.SetErrorString(
+ "assign operation takes one or more key=value arguments");
+ return error;
+ }
+
+ llvm::StringRef key, value;
+ std::tie(key, value) = entry.ref.split('=');
+ bool key_valid = false;
+ if (key.empty()) {
+ error.SetErrorString("empty dictionary key");
+ return error;
+ }
+
+ if (key.front() == '[') {
+ // Key name starts with '[', so the key value must be in single or
+ // double quotes like: ['<key>'] ["<key>"]
+ if ((key.size() > 2) && (key.back() == ']')) {
+ // Strip leading '[' and trailing ']'
+ key = key.substr(1, key.size() - 2);
+ const char quote_char = key.front();
+ if ((quote_char == '\'') || (quote_char == '"')) {
+ if ((key.size() > 2) && (key.back() == quote_char)) {
+ // Strip the quotes
+ key = key.substr(1, key.size() - 2);
+ key_valid = true;
+ }
+ } else {
+ // square brackets, no quotes
+ key_valid = true;
+ }
+ }
+ } else {
+ // No square brackets or quotes
+ key_valid = true;
+ }
+ if (!key_valid) {
+ error.SetErrorStringWithFormat(
+ "invalid key \"%s\", the key must be a bare string or "
+ "surrounded by brackets with optional quotes: [<key>] or "
+ "['<key>'] or [\"<key>\"]",
+ key.str().c_str());
+ return error;
+ }
+
+ lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask(
+ value.str().c_str(), m_type_mask, error));
+ if (value_sp) {
+ if (error.Fail())
+ return error;
+ m_value_was_set = true;
+ SetValueForKey(ConstString(key), value_sp, true);
+ } else {
+ error.SetErrorString("dictionaries that can contain multiple types "
+ "must subclass OptionValueArray");
+ }
+ }
+ break;
+
+ case eVarSetOperationRemove:
+ if (argc > 0) {
+ for (size_t i = 0; i < argc; ++i) {
+ ConstString key(args.GetArgumentAtIndex(i));
+ if (!DeleteValueForKey(key)) {
+ error.SetErrorStringWithFormat(
+ "no value found named '%s', aborting remove operation",
+ key.GetCString());
+ break;
+ }
+ }
+ } else {
+ error.SetErrorString("remove operation takes one or more key arguments");
+ }
+ break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(llvm::StringRef(), op);
+ break;
+ }
+ return error;
+}
+
+Status OptionValueDictionary::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Args args(value.str());
+ Status error = SetArgs(args, op);
+ if (error.Success())
+ NotifyValueChanged();
+ return error;
+}
+
+lldb::OptionValueSP
+OptionValueDictionary::GetSubValue(const ExecutionContext *exe_ctx,
+ llvm::StringRef name, bool will_modify,
+ Status &error) const {
+ lldb::OptionValueSP value_sp;
+ if (name.empty())
+ return nullptr;
+
+ llvm::StringRef left, temp;
+ std::tie(left, temp) = name.split('[');
+ if (left.size() == name.size()) {
+ error.SetErrorStringWithFormat("invalid value path '%s', %s values only "
+ "support '[<key>]' subvalues where <key> "
+ "a string value optionally delimited by "
+ "single or double quotes",
+ name.str().c_str(), GetTypeAsCString());
+ return nullptr;
+ }
+ assert(!temp.empty());
+
+ llvm::StringRef key, quote_char;
+
+ if (temp[0] == '\"' || temp[0] == '\'') {
+ quote_char = temp.take_front();
+ temp = temp.drop_front();
+ }
+
+ llvm::StringRef sub_name;
+ std::tie(key, sub_name) = temp.split(']');
+
+ if (!key.consume_back(quote_char) || key.empty()) {
+ error.SetErrorStringWithFormat("invalid value path '%s', "
+ "key names must be formatted as ['<key>'] where <key> "
+ "is a string that doesn't contain quotes and the quote"
+ " char is optional", name.str().c_str());
+ return nullptr;
+ }
+
+ value_sp = GetValueForKey(ConstString(key));
+ if (!value_sp) {
+ error.SetErrorStringWithFormat(
+ "dictionary does not contain a value for the key name '%s'",
+ key.str().c_str());
+ return nullptr;
+ }
+
+ if (sub_name.empty())
+ return value_sp;
+ return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
+}
+
+Status OptionValueDictionary::SetSubValue(const ExecutionContext *exe_ctx,
+ VarSetOperationType op,
+ llvm::StringRef name,
+ llvm::StringRef value) {
+ Status error;
+ const bool will_modify = true;
+ lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error));
+ if (value_sp)
+ error = value_sp->SetValueFromString(value, op);
+ else {
+ if (error.AsCString() == nullptr)
+ error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());
+ }
+ return error;
+}
+
+lldb::OptionValueSP
+OptionValueDictionary::GetValueForKey(ConstString key) const {
+ lldb::OptionValueSP value_sp;
+ collection::const_iterator pos = m_values.find(key);
+ if (pos != m_values.end())
+ value_sp = pos->second;
+ return value_sp;
+}
+
+bool OptionValueDictionary::SetValueForKey(ConstString key,
+ const lldb::OptionValueSP &value_sp,
+ bool can_replace) {
+ // Make sure the value_sp object is allowed to contain values of the type
+ // passed in...
+ if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) {
+ if (!can_replace) {
+ collection::const_iterator pos = m_values.find(key);
+ if (pos != m_values.end())
+ return false;
+ }
+ m_values[key] = value_sp;
+ return true;
+ }
+ return false;
+}
+
+bool OptionValueDictionary::DeleteValueForKey(ConstString key) {
+ collection::iterator pos = m_values.find(key);
+ if (pos != m_values.end()) {
+ m_values.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+lldb::OptionValueSP OptionValueDictionary::DeepCopy() const {
+ OptionValueDictionary *copied_dict =
+ new OptionValueDictionary(m_type_mask, m_raw_value_dump);
+ lldb::OptionValueSP copied_value_sp(copied_dict);
+ collection::const_iterator pos, end = m_values.end();
+ for (pos = m_values.begin(); pos != end; ++pos) {
+ StreamString strm;
+ strm.Printf("%s=", pos->first.GetCString());
+ copied_dict->SetValueForKey(pos->first, pos->second->DeepCopy(), true);
+ }
+ return copied_value_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueEnumeration.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueEnumeration.cpp
new file mode 100644
index 000000000000..0b76bd0601aa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueEnumeration.cpp
@@ -0,0 +1,122 @@
+//===-- OptionValueEnumeration.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueEnumeration.h"
+
+#include "lldb/Utility/StringList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionValueEnumeration::OptionValueEnumeration(
+ const OptionEnumValues &enumerators, enum_type value)
+ : OptionValue(), m_current_value(value), m_default_value(value),
+ m_enumerations() {
+ SetEnumerations(enumerators);
+}
+
+OptionValueEnumeration::~OptionValueEnumeration() {}
+
+void OptionValueEnumeration::DumpValue(const ExecutionContext *exe_ctx,
+ Stream &strm, uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+ const size_t count = m_enumerations.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ if (m_enumerations.GetValueAtIndexUnchecked(i).value == m_current_value) {
+ strm.PutCString(m_enumerations.GetCStringAtIndex(i).GetStringRef());
+ return;
+ }
+ }
+ strm.Printf("%" PRIu64, (uint64_t)m_current_value);
+ }
+}
+
+Status OptionValueEnumeration::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign: {
+ ConstString const_enumerator_name(value.trim());
+ const EnumerationMapEntry *enumerator_entry =
+ m_enumerations.FindFirstValueForName(const_enumerator_name);
+ if (enumerator_entry) {
+ m_current_value = enumerator_entry->value.value;
+ NotifyValueChanged();
+ } else {
+ StreamString error_strm;
+ error_strm.Printf("invalid enumeration value '%s'", value.str().c_str());
+ const size_t count = m_enumerations.GetSize();
+ if (count) {
+ error_strm.Printf(", valid values are: %s",
+ m_enumerations.GetCStringAtIndex(0).GetCString());
+ for (size_t i = 1; i < count; ++i) {
+ error_strm.Printf(", %s",
+ m_enumerations.GetCStringAtIndex(i).GetCString());
+ }
+ }
+ error.SetErrorString(error_strm.GetString());
+ }
+ break;
+ }
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ case eVarSetOperationAppend:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+ }
+ return error;
+}
+
+void OptionValueEnumeration::SetEnumerations(
+ const OptionEnumValues &enumerators) {
+ m_enumerations.Clear();
+
+ for (const auto &enumerator : enumerators) {
+ ConstString const_enumerator_name(enumerator.string_value);
+ EnumeratorInfo enumerator_info = {enumerator.value, enumerator.usage};
+ m_enumerations.Append(const_enumerator_name, enumerator_info);
+ }
+
+ m_enumerations.Sort();
+}
+
+lldb::OptionValueSP OptionValueEnumeration::DeepCopy() const {
+ return OptionValueSP(new OptionValueEnumeration(*this));
+}
+
+size_t OptionValueEnumeration::AutoComplete(CommandInterpreter &interpreter,
+ CompletionRequest &request) {
+ request.SetWordComplete(false);
+
+ const uint32_t num_enumerators = m_enumerations.GetSize();
+ if (!request.GetCursorArgumentPrefix().empty()) {
+ for (size_t i = 0; i < num_enumerators; ++i) {
+ llvm::StringRef name = m_enumerations.GetCStringAtIndex(i).GetStringRef();
+ if (name.startswith(request.GetCursorArgumentPrefix()))
+ request.AddCompletion(name);
+ }
+ } else {
+ // only suggest "true" or "false" by default
+ for (size_t i = 0; i < num_enumerators; ++i)
+ request.AddCompletion(m_enumerations.GetCStringAtIndex(i).GetStringRef());
+ }
+ return request.GetNumberOfMatches();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpec.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpec.cpp
new file mode 100644
index 000000000000..062d7ccdf2aa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpec.cpp
@@ -0,0 +1,120 @@
+//===-- OptionValueFileSpec.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueFileSpec.h"
+
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionValueFileSpec::OptionValueFileSpec(bool resolve)
+ : OptionValue(), m_current_value(), m_default_value(), m_data_sp(),
+ m_data_mod_time(),
+ m_completion_mask(CommandCompletions::eDiskFileCompletion),
+ m_resolve(resolve) {}
+
+OptionValueFileSpec::OptionValueFileSpec(const FileSpec &value, bool resolve)
+ : OptionValue(), m_current_value(value), m_default_value(value),
+ m_data_sp(), m_data_mod_time(),
+ m_completion_mask(CommandCompletions::eDiskFileCompletion),
+ m_resolve(resolve) {}
+
+OptionValueFileSpec::OptionValueFileSpec(const FileSpec &current_value,
+ const FileSpec &default_value,
+ bool resolve)
+ : OptionValue(), m_current_value(current_value),
+ m_default_value(default_value), m_data_sp(), m_data_mod_time(),
+ m_completion_mask(CommandCompletions::eDiskFileCompletion),
+ m_resolve(resolve) {}
+
+void OptionValueFileSpec::DumpValue(const ExecutionContext *exe_ctx,
+ Stream &strm, uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+
+ if (m_current_value) {
+ strm << '"' << m_current_value.GetPath().c_str() << '"';
+ }
+ }
+}
+
+Status OptionValueFileSpec::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign:
+ if (value.size() > 0) {
+ // The setting value may have whitespace, double-quotes, or single-quotes
+ // around the file path to indicate that internal spaces are not word
+ // breaks. Strip off any ws & quotes from the start and end of the file
+ // path - we aren't doing any word // breaking here so the quoting is
+ // unnecessary. NB this will cause a problem if someone tries to specify
+ // a file path that legitimately begins or ends with a " or ' character,
+ // or whitespace.
+ value = value.trim("\"' \t");
+ m_value_was_set = true;
+ m_current_value.SetFile(value.str(), FileSpec::Style::native);
+ if (m_resolve)
+ FileSystem::Instance().Resolve(m_current_value);
+ m_data_sp.reset();
+ m_data_mod_time = llvm::sys::TimePoint<>();
+ NotifyValueChanged();
+ } else {
+ error.SetErrorString("invalid value string");
+ }
+ break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ case eVarSetOperationAppend:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueFileSpec::DeepCopy() const {
+ return OptionValueSP(new OptionValueFileSpec(*this));
+}
+
+size_t OptionValueFileSpec::AutoComplete(CommandInterpreter &interpreter,
+ CompletionRequest &request) {
+ request.SetWordComplete(false);
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ interpreter, m_completion_mask, request, nullptr);
+ return request.GetNumberOfMatches();
+}
+
+const lldb::DataBufferSP &OptionValueFileSpec::GetFileContents() {
+ if (m_current_value) {
+ const auto file_mod_time = FileSystem::Instance().GetModificationTime(m_current_value);
+ if (m_data_sp && m_data_mod_time == file_mod_time)
+ return m_data_sp;
+ m_data_sp =
+ FileSystem::Instance().CreateDataBuffer(m_current_value.GetPath());
+ m_data_mod_time = file_mod_time;
+ }
+ return m_data_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpecLIst.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpecLIst.cpp
new file mode 100644
index 000000000000..a95188870f0b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpecLIst.cpp
@@ -0,0 +1,170 @@
+//===-- OptionValueFileSpecLIst.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueFileSpecList.h"
+
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueFileSpecList::DumpValue(const ExecutionContext *exe_ctx,
+ Stream &strm, uint32_t dump_mask) {
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ const bool one_line = dump_mask & eDumpOptionCommand;
+ const uint32_t size = m_current_value.GetSize();
+ if (dump_mask & eDumpOptionType)
+ strm.Printf(" =%s",
+ (m_current_value.GetSize() > 0 && !one_line) ? "\n" : "");
+ if (!one_line)
+ strm.IndentMore();
+ for (uint32_t i = 0; i < size; ++i) {
+ if (!one_line) {
+ strm.Indent();
+ strm.Printf("[%u]: ", i);
+ }
+ m_current_value.GetFileSpecAtIndex(i).Dump(&strm);
+ if (one_line)
+ strm << ' ';
+ }
+ if (!one_line)
+ strm.IndentLess();
+ }
+}
+
+Status OptionValueFileSpecList::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+ Status error;
+ Args args(value.str());
+ const size_t argc = args.GetArgumentCount();
+
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ if (argc > 1) {
+ uint32_t idx =
+ StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
+ const uint32_t count = m_current_value.GetSize();
+ if (idx > count) {
+ error.SetErrorStringWithFormat(
+ "invalid file list index %u, index must be 0 through %u", idx,
+ count);
+ } else {
+ for (size_t i = 1; i < argc; ++i, ++idx) {
+ FileSpec file(args.GetArgumentAtIndex(i));
+ if (idx < count)
+ m_current_value.Replace(idx, file);
+ else
+ m_current_value.Append(file);
+ }
+ NotifyValueChanged();
+ }
+ } else {
+ error.SetErrorString("replace operation takes an array index followed by "
+ "one or more values");
+ }
+ break;
+
+ case eVarSetOperationAssign:
+ m_current_value.Clear();
+ // Fall through to append case
+ LLVM_FALLTHROUGH;
+ case eVarSetOperationAppend:
+ if (argc > 0) {
+ m_value_was_set = true;
+ for (size_t i = 0; i < argc; ++i) {
+ FileSpec file(args.GetArgumentAtIndex(i));
+ m_current_value.Append(file);
+ }
+ NotifyValueChanged();
+ } else {
+ error.SetErrorString(
+ "assign operation takes at least one file path argument");
+ }
+ break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ if (argc > 1) {
+ uint32_t idx =
+ StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
+ const uint32_t count = m_current_value.GetSize();
+ if (idx > count) {
+ error.SetErrorStringWithFormat(
+ "invalid insert file list index %u, index must be 0 through %u",
+ idx, count);
+ } else {
+ if (op == eVarSetOperationInsertAfter)
+ ++idx;
+ for (size_t i = 1; i < argc; ++i, ++idx) {
+ FileSpec file(args.GetArgumentAtIndex(i));
+ m_current_value.Insert(idx, file);
+ }
+ NotifyValueChanged();
+ }
+ } else {
+ error.SetErrorString("insert operation takes an array index followed by "
+ "one or more values");
+ }
+ break;
+
+ case eVarSetOperationRemove:
+ if (argc > 0) {
+ std::vector<int> remove_indexes;
+ bool all_indexes_valid = true;
+ size_t i;
+ for (i = 0; all_indexes_valid && i < argc; ++i) {
+ const int idx =
+ StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX);
+ if (idx == INT32_MAX)
+ all_indexes_valid = false;
+ else
+ remove_indexes.push_back(idx);
+ }
+
+ if (all_indexes_valid) {
+ size_t num_remove_indexes = remove_indexes.size();
+ if (num_remove_indexes) {
+ // Sort and then erase in reverse so indexes are always valid
+ llvm::sort(remove_indexes.begin(), remove_indexes.end());
+ for (size_t j = num_remove_indexes - 1; j < num_remove_indexes; ++j) {
+ m_current_value.Remove(j);
+ }
+ }
+ NotifyValueChanged();
+ } else {
+ error.SetErrorStringWithFormat(
+ "invalid array index '%s', aborting remove operation",
+ args.GetArgumentAtIndex(i));
+ }
+ } else {
+ error.SetErrorString("remove operation takes one or more array index");
+ }
+ break;
+
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueFileSpecList::DeepCopy() const {
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+ return OptionValueSP(new OptionValueFileSpecList(m_current_value));
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormat.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormat.cpp
new file mode 100644
index 000000000000..ba5a44c270da
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormat.cpp
@@ -0,0 +1,62 @@
+//===-- OptionValueFormat.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueFormat.h"
+
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueFormat::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
+ uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+ strm.PutCString(FormatManager::GetFormatAsCString(m_current_value));
+ }
+}
+
+Status OptionValueFormat::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign: {
+ Format new_format;
+ error = OptionArgParser::ToFormat(value.str().c_str(), new_format, nullptr);
+ if (error.Success()) {
+ m_value_was_set = true;
+ m_current_value = new_format;
+ NotifyValueChanged();
+ }
+ } break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ case eVarSetOperationAppend:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueFormat::DeepCopy() const {
+ return OptionValueSP(new OptionValueFormat(*this));
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormatEntity.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormatEntity.cpp
new file mode 100644
index 000000000000..1bb8c9f6955a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormatEntity.cpp
@@ -0,0 +1,122 @@
+//===-- OptionValueFormatEntity.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueFormatEntity.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+using namespace lldb;
+using namespace lldb_private;
+
+OptionValueFormatEntity::OptionValueFormatEntity(const char *default_format)
+ : OptionValue(), m_current_format(), m_default_format(), m_current_entry(),
+ m_default_entry() {
+ if (default_format && default_format[0]) {
+ llvm::StringRef default_format_str(default_format);
+ Status error = FormatEntity::Parse(default_format_str, m_default_entry);
+ if (error.Success()) {
+ m_default_format = default_format;
+ m_current_format = default_format;
+ m_current_entry = m_default_entry;
+ }
+ }
+}
+
+bool OptionValueFormatEntity::Clear() {
+ m_current_entry = m_default_entry;
+ m_current_format = m_default_format;
+ m_value_was_set = false;
+ return true;
+}
+
+static void EscapeBackticks(llvm::StringRef str, std::string &dst) {
+ dst.clear();
+ dst.reserve(str.size());
+
+ for (size_t i = 0, e = str.size(); i != e; ++i) {
+ char c = str[i];
+ if (c == '`') {
+ if (i == 0 || str[i - 1] != '\\')
+ dst += '\\';
+ }
+ dst += c;
+ }
+}
+
+void OptionValueFormatEntity::DumpValue(const ExecutionContext *exe_ctx,
+ Stream &strm, uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+ std::string escaped;
+ EscapeBackticks(m_current_format, escaped);
+ strm << '"' << escaped << '"';
+ }
+}
+
+Status OptionValueFormatEntity::SetValueFromString(llvm::StringRef value_str,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign: {
+ // Check if the string starts with a quote character after removing leading
+ // and trailing spaces. If it does start with a quote character, make sure
+ // it ends with the same quote character and remove the quotes before we
+ // parse the format string. If the string doesn't start with a quote, leave
+ // the string alone and parse as is.
+ llvm::StringRef trimmed_value_str = value_str.trim();
+ if (!trimmed_value_str.empty()) {
+ const char first_char = trimmed_value_str[0];
+ if (first_char == '"' || first_char == '\'') {
+ const size_t trimmed_len = trimmed_value_str.size();
+ if (trimmed_len == 1 || value_str[trimmed_len - 1] != first_char) {
+ error.SetErrorStringWithFormat("mismatched quotes");
+ return error;
+ }
+ value_str = trimmed_value_str.substr(1, trimmed_len - 2);
+ }
+ }
+ FormatEntity::Entry entry;
+ error = FormatEntity::Parse(value_str, entry);
+ if (error.Success()) {
+ m_current_entry = std::move(entry);
+ m_current_format = value_str;
+ m_value_was_set = true;
+ NotifyValueChanged();
+ }
+ } break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ case eVarSetOperationAppend:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value_str, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueFormatEntity::DeepCopy() const {
+ return OptionValueSP(new OptionValueFormatEntity(*this));
+}
+
+size_t OptionValueFormatEntity::AutoComplete(CommandInterpreter &interpreter,
+ CompletionRequest &request) {
+ return FormatEntity::AutoComplete(request);
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueLanguage.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueLanguage.cpp
new file mode 100644
index 000000000000..d935d5e23496
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueLanguage.cpp
@@ -0,0 +1,77 @@
+//===-- OptionValueLanguage.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueLanguage.h"
+
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueLanguage::DumpValue(const ExecutionContext *exe_ctx,
+ Stream &strm, uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+ if (m_current_value != eLanguageTypeUnknown)
+ strm.PutCString(Language::GetNameForLanguageType(m_current_value));
+ }
+}
+
+Status OptionValueLanguage::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign: {
+ ConstString lang_name(value.trim());
+ std::set<lldb::LanguageType> languages_for_types;
+ std::set<lldb::LanguageType> languages_for_expressions;
+ Language::GetLanguagesSupportingTypeSystems(languages_for_types,
+ languages_for_expressions);
+
+ LanguageType new_type =
+ Language::GetLanguageTypeFromString(lang_name.GetStringRef());
+ if (new_type && languages_for_types.count(new_type)) {
+ m_value_was_set = true;
+ m_current_value = new_type;
+ } else {
+ StreamString error_strm;
+ error_strm.Printf("invalid language type '%s', ", value.str().c_str());
+ error_strm.Printf("valid values are:\n");
+ for (lldb::LanguageType language : languages_for_types) {
+ error_strm.Printf("%s%s%s", " ",
+ Language::GetNameForLanguageType(language), "\n");
+ }
+ error.SetErrorString(error_strm.GetString());
+ }
+ } break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ case eVarSetOperationAppend:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueLanguage::DeepCopy() const {
+ return OptionValueSP(new OptionValueLanguage(*this));
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValuePathMappings.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValuePathMappings.cpp
new file mode 100644
index 000000000000..75fcf0247475
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValuePathMappings.cpp
@@ -0,0 +1,204 @@
+//===-- OptionValuePathMappings.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValuePathMappings.h"
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+namespace {
+static bool VerifyPathExists(const char *path) {
+ if (path && path[0])
+ return FileSystem::Instance().Exists(path);
+ else
+ return false;
+}
+}
+
+void OptionValuePathMappings::DumpValue(const ExecutionContext *exe_ctx,
+ Stream &strm, uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf(" =%s", (m_path_mappings.GetSize() > 0) ? "\n" : "");
+ m_path_mappings.Dump(&strm);
+ }
+}
+
+Status OptionValuePathMappings::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+ Args args(value.str());
+ const size_t argc = args.GetArgumentCount();
+
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ // Must be at least one index + 1 pair of paths, and the pair count must be
+ // even
+ if (argc >= 3 && (((argc - 1) & 1) == 0)) {
+ uint32_t idx =
+ StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
+ const uint32_t count = m_path_mappings.GetSize();
+ if (idx > count) {
+ error.SetErrorStringWithFormat(
+ "invalid file list index %u, index must be 0 through %u", idx,
+ count);
+ } else {
+ bool changed = false;
+ for (size_t i = 1; i < argc; i += 2, ++idx) {
+ const char *orginal_path = args.GetArgumentAtIndex(i);
+ const char *replace_path = args.GetArgumentAtIndex(i + 1);
+ if (VerifyPathExists(replace_path)) {
+ ConstString a(orginal_path);
+ ConstString b(replace_path);
+ if (!m_path_mappings.Replace(a, b, idx, m_notify_changes))
+ m_path_mappings.Append(a, b, m_notify_changes);
+ changed = true;
+ } else {
+ error.SetErrorStringWithFormat(
+ "the replacement path doesn't exist: \"%s\"", replace_path);
+ break;
+ }
+ }
+ if (changed)
+ NotifyValueChanged();
+ }
+ } else {
+ error.SetErrorString("replace operation takes an array index followed by "
+ "one or more path pairs");
+ }
+ break;
+
+ case eVarSetOperationAssign:
+ if (argc < 2 || (argc & 1)) {
+ error.SetErrorString("assign operation takes one or more path pairs");
+ break;
+ }
+ m_path_mappings.Clear(m_notify_changes);
+ // Fall through to append case
+ LLVM_FALLTHROUGH;
+ case eVarSetOperationAppend:
+ if (argc < 2 || (argc & 1)) {
+ error.SetErrorString("append operation takes one or more path pairs");
+ break;
+ } else {
+ bool changed = false;
+ for (size_t i = 0; i < argc; i += 2) {
+ const char *orginal_path = args.GetArgumentAtIndex(i);
+ const char *replace_path = args.GetArgumentAtIndex(i + 1);
+ if (VerifyPathExists(replace_path)) {
+ ConstString a(orginal_path);
+ ConstString b(replace_path);
+ m_path_mappings.Append(a, b, m_notify_changes);
+ m_value_was_set = true;
+ changed = true;
+ } else {
+ error.SetErrorStringWithFormat(
+ "the replacement path doesn't exist: \"%s\"", replace_path);
+ break;
+ }
+ }
+ if (changed)
+ NotifyValueChanged();
+ }
+ break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ // Must be at least one index + 1 pair of paths, and the pair count must be
+ // even
+ if (argc >= 3 && (((argc - 1) & 1) == 0)) {
+ uint32_t idx =
+ StringConvert::ToUInt32(args.GetArgumentAtIndex(0), UINT32_MAX);
+ const uint32_t count = m_path_mappings.GetSize();
+ if (idx > count) {
+ error.SetErrorStringWithFormat(
+ "invalid file list index %u, index must be 0 through %u", idx,
+ count);
+ } else {
+ bool changed = false;
+ if (op == eVarSetOperationInsertAfter)
+ ++idx;
+ for (size_t i = 1; i < argc; i += 2, ++idx) {
+ const char *orginal_path = args.GetArgumentAtIndex(i);
+ const char *replace_path = args.GetArgumentAtIndex(i + 1);
+ if (VerifyPathExists(replace_path)) {
+ ConstString a(orginal_path);
+ ConstString b(replace_path);
+ m_path_mappings.Insert(a, b, idx, m_notify_changes);
+ changed = true;
+ } else {
+ error.SetErrorStringWithFormat(
+ "the replacement path doesn't exist: \"%s\"", replace_path);
+ break;
+ }
+ }
+ if (changed)
+ NotifyValueChanged();
+ }
+ } else {
+ error.SetErrorString("insert operation takes an array index followed by "
+ "one or more path pairs");
+ }
+ break;
+
+ case eVarSetOperationRemove:
+ if (argc > 0) {
+ std::vector<int> remove_indexes;
+ bool all_indexes_valid = true;
+ size_t i;
+ for (i = 0; all_indexes_valid && i < argc; ++i) {
+ const int idx =
+ StringConvert::ToSInt32(args.GetArgumentAtIndex(i), INT32_MAX);
+ if (idx == INT32_MAX)
+ all_indexes_valid = false;
+ else
+ remove_indexes.push_back(idx);
+ }
+
+ if (all_indexes_valid) {
+ size_t num_remove_indexes = remove_indexes.size();
+ if (num_remove_indexes) {
+ // Sort and then erase in reverse so indexes are always valid
+ llvm::sort(remove_indexes.begin(), remove_indexes.end());
+ for (size_t j = num_remove_indexes - 1; j < num_remove_indexes; ++j) {
+ m_path_mappings.Remove(j, m_notify_changes);
+ }
+ }
+ NotifyValueChanged();
+ } else {
+ error.SetErrorStringWithFormat(
+ "invalid array index '%s', aborting remove operation",
+ args.GetArgumentAtIndex(i));
+ }
+ } else {
+ error.SetErrorString("remove operation takes one or more array index");
+ }
+ break;
+
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValuePathMappings::DeepCopy() const {
+ return OptionValueSP(new OptionValuePathMappings(*this));
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueProperties.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueProperties.cpp
new file mode 100644
index 000000000000..4dae930c3a6f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueProperties.cpp
@@ -0,0 +1,677 @@
+//===-- OptionValueProperties.cpp --------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueProperties.h"
+
+#include "lldb/Utility/Flags.h"
+
+#include "lldb/Core/UserSettingsController.h"
+#include "lldb/Interpreter/OptionValues.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OptionValueProperties::OptionValueProperties(ConstString name)
+ : OptionValue(), m_name(name), m_properties(), m_name_to_index() {}
+
+OptionValueProperties::OptionValueProperties(
+ const OptionValueProperties &global_properties)
+ : OptionValue(global_properties),
+ std::enable_shared_from_this<OptionValueProperties>(),
+ m_name(global_properties.m_name),
+ m_properties(global_properties.m_properties),
+ m_name_to_index(global_properties.m_name_to_index) {
+ // We now have an exact copy of "global_properties". We need to now find all
+ // non-global settings and copy the property values so that all non-global
+ // settings get new OptionValue instances created for them.
+ const size_t num_properties = m_properties.size();
+ for (size_t i = 0; i < num_properties; ++i) {
+ // Duplicate any values that are not global when constructing properties
+ // from a global copy
+ if (!m_properties[i].IsGlobal()) {
+ lldb::OptionValueSP new_value_sp(m_properties[i].GetValue()->DeepCopy());
+ m_properties[i].SetOptionValue(new_value_sp);
+ }
+ }
+}
+
+size_t OptionValueProperties::GetNumProperties() const {
+ return m_properties.size();
+}
+
+void OptionValueProperties::Initialize(const PropertyDefinitions &defs) {
+ for (const auto &definition : defs) {
+ Property property(definition);
+ assert(property.IsValid());
+ m_name_to_index.Append(ConstString(property.GetName()), m_properties.size());
+ property.GetValue()->SetParent(shared_from_this());
+ m_properties.push_back(property);
+ }
+ m_name_to_index.Sort();
+}
+
+void OptionValueProperties::SetValueChangedCallback(
+ uint32_t property_idx, OptionValueChangedCallback callback, void *baton) {
+ Property *property = ProtectedGetPropertyAtIndex(property_idx);
+ if (property)
+ property->SetValueChangedCallback(callback, baton);
+}
+
+void OptionValueProperties::AppendProperty(ConstString name,
+ ConstString desc,
+ bool is_global,
+ const OptionValueSP &value_sp) {
+ Property property(name, desc, is_global, value_sp);
+ m_name_to_index.Append(name, m_properties.size());
+ m_properties.push_back(property);
+ value_sp->SetParent(shared_from_this());
+ m_name_to_index.Sort();
+}
+
+// bool
+// OptionValueProperties::GetQualifiedName (Stream &strm)
+//{
+// bool dumped_something = false;
+//// lldb::OptionValuePropertiesSP parent_sp(GetParent ());
+//// if (parent_sp)
+//// {
+//// parent_sp->GetQualifiedName (strm);
+//// strm.PutChar('.');
+//// dumped_something = true;
+//// }
+// if (m_name)
+// {
+// strm << m_name;
+// dumped_something = true;
+// }
+// return dumped_something;
+//}
+//
+lldb::OptionValueSP
+OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx,
+ ConstString key,
+ bool will_modify) const {
+ lldb::OptionValueSP value_sp;
+ size_t idx = m_name_to_index.Find(key, SIZE_MAX);
+ if (idx < m_properties.size())
+ value_sp = GetPropertyAtIndex(exe_ctx, will_modify, idx)->GetValue();
+ return value_sp;
+}
+
+lldb::OptionValueSP
+OptionValueProperties::GetSubValue(const ExecutionContext *exe_ctx,
+ llvm::StringRef name, bool will_modify,
+ Status &error) const {
+ lldb::OptionValueSP value_sp;
+ if (name.empty())
+ return OptionValueSP();
+
+ llvm::StringRef sub_name;
+ ConstString key;
+ size_t key_len = name.find_first_of(".[{");
+ if (key_len != llvm::StringRef::npos) {
+ key.SetString(name.take_front(key_len));
+ sub_name = name.drop_front(key_len);
+ } else
+ key.SetString(name);
+
+ value_sp = GetValueForKey(exe_ctx, key, will_modify);
+ if (sub_name.empty() || !value_sp)
+ return value_sp;
+
+ switch (sub_name[0]) {
+ case '.': {
+ lldb::OptionValueSP return_val_sp;
+ return_val_sp =
+ value_sp->GetSubValue(exe_ctx, sub_name.drop_front(), will_modify, error);
+ if (!return_val_sp) {
+ if (Properties::IsSettingExperimental(sub_name.drop_front())) {
+ size_t experimental_len =
+ strlen(Properties::GetExperimentalSettingsName());
+ if (sub_name[experimental_len + 1] == '.')
+ return_val_sp = value_sp->GetSubValue(
+ exe_ctx, sub_name.drop_front(experimental_len + 2), will_modify, error);
+ // It isn't an error if an experimental setting is not present.
+ if (!return_val_sp)
+ error.Clear();
+ }
+ }
+ return return_val_sp;
+ }
+ case '{':
+ // Predicate matching for predicates like
+ // "<setting-name>{<predicate>}"
+ // strings are parsed by the current OptionValueProperties subclass to mean
+ // whatever they want to. For instance a subclass of OptionValueProperties
+ // for a lldb_private::Target might implement: "target.run-
+ // args{arch==i386}" -- only set run args if the arch is i386 "target
+ // .run-args{path=/tmp/a/b/c/a.out}" -- only set run args if the path
+ // matches "target.run-args{basename==test&&arch==x86_64}" -- only set run
+ // args if executable basename is "test" and arch is "x86_64"
+ if (sub_name[1]) {
+ llvm::StringRef predicate_start = sub_name.drop_front();
+ size_t pos = predicate_start.find('}');
+ if (pos != llvm::StringRef::npos) {
+ auto predicate = predicate_start.take_front(pos);
+ auto rest = predicate_start.drop_front(pos);
+ if (PredicateMatches(exe_ctx, predicate)) {
+ if (!rest.empty()) {
+ // Still more subvalue string to evaluate
+ return value_sp->GetSubValue(exe_ctx, rest,
+ will_modify, error);
+ } else {
+ // We have a match!
+ break;
+ }
+ }
+ }
+ }
+ // Predicate didn't match or wasn't correctly formed
+ value_sp.reset();
+ break;
+
+ case '[':
+ // Array or dictionary access for subvalues like: "[12]" -- access
+ // 12th array element "['hello']" -- dictionary access of key named hello
+ return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
+
+ default:
+ value_sp.reset();
+ break;
+ }
+ return value_sp;
+}
+
+Status OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx,
+ VarSetOperationType op,
+ llvm::StringRef name,
+ llvm::StringRef value) {
+ Status error;
+ const bool will_modify = true;
+ llvm::SmallVector<llvm::StringRef, 8> components;
+ name.split(components, '.');
+ bool name_contains_experimental = false;
+ for (const auto &part : components)
+ if (Properties::IsSettingExperimental(part))
+ name_contains_experimental = true;
+
+ lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error));
+ if (value_sp)
+ error = value_sp->SetValueFromString(value, op);
+ else {
+ // Don't set an error if the path contained .experimental. - those are
+ // allowed to be missing and should silently fail.
+ if (!name_contains_experimental && error.AsCString() == nullptr) {
+ error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str());
+ }
+ }
+ return error;
+}
+
+uint32_t
+OptionValueProperties::GetPropertyIndex(ConstString name) const {
+ return m_name_to_index.Find(name, SIZE_MAX);
+}
+
+const Property *
+OptionValueProperties::GetProperty(const ExecutionContext *exe_ctx,
+ bool will_modify,
+ ConstString name) const {
+ return GetPropertyAtIndex(
+ exe_ctx, will_modify,
+ m_name_to_index.Find(name, SIZE_MAX));
+}
+
+const Property *OptionValueProperties::GetPropertyAtIndex(
+ const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
+ return ProtectedGetPropertyAtIndex(idx);
+}
+
+lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex(
+ const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
+ const Property *setting = GetPropertyAtIndex(exe_ctx, will_modify, idx);
+ if (setting)
+ return setting->GetValue();
+ return OptionValueSP();
+}
+
+OptionValuePathMappings *
+OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings(
+ const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
+ OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
+ if (value_sp)
+ return value_sp->GetAsPathMappings();
+ return nullptr;
+}
+
+OptionValueFileSpecList *
+OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList(
+ const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
+ OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
+ if (value_sp)
+ return value_sp->GetAsFileSpecList();
+ return nullptr;
+}
+
+OptionValueArch *OptionValueProperties::GetPropertyAtIndexAsOptionValueArch(
+ const ExecutionContext *exe_ctx, uint32_t idx) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property)
+ return property->GetValue()->GetAsArch();
+ return nullptr;
+}
+
+OptionValueLanguage *
+OptionValueProperties::GetPropertyAtIndexAsOptionValueLanguage(
+ const ExecutionContext *exe_ctx, uint32_t idx) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property)
+ return property->GetValue()->GetAsLanguage();
+ return nullptr;
+}
+
+bool OptionValueProperties::GetPropertyAtIndexAsArgs(
+ const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value) {
+ const OptionValueArray *array = value->GetAsArray();
+ if (array)
+ return array->GetArgs(args);
+ else {
+ const OptionValueDictionary *dict = value->GetAsDictionary();
+ if (dict)
+ return dict->GetArgs(args);
+ }
+ }
+ }
+ return false;
+}
+
+bool OptionValueProperties::SetPropertyAtIndexFromArgs(
+ const ExecutionContext *exe_ctx, uint32_t idx, const Args &args) {
+ const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value) {
+ OptionValueArray *array = value->GetAsArray();
+ if (array)
+ return array->SetArgs(args, eVarSetOperationAssign).Success();
+ else {
+ OptionValueDictionary *dict = value->GetAsDictionary();
+ if (dict)
+ return dict->SetArgs(args, eVarSetOperationAssign).Success();
+ }
+ }
+ }
+ return false;
+}
+
+bool OptionValueProperties::GetPropertyAtIndexAsBoolean(
+ const ExecutionContext *exe_ctx, uint32_t idx, bool fail_value) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->GetBooleanValue(fail_value);
+ }
+ return fail_value;
+}
+
+bool OptionValueProperties::SetPropertyAtIndexAsBoolean(
+ const ExecutionContext *exe_ctx, uint32_t idx, bool new_value) {
+ const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value) {
+ value->SetBooleanValue(new_value);
+ return true;
+ }
+ }
+ return false;
+}
+
+OptionValueDictionary *
+OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary(
+ const ExecutionContext *exe_ctx, uint32_t idx) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property)
+ return property->GetValue()->GetAsDictionary();
+ return nullptr;
+}
+
+int64_t OptionValueProperties::GetPropertyAtIndexAsEnumeration(
+ const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->GetEnumerationValue(fail_value);
+ }
+ return fail_value;
+}
+
+bool OptionValueProperties::SetPropertyAtIndexAsEnumeration(
+ const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
+ const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->SetEnumerationValue(new_value);
+ }
+ return false;
+}
+
+const FormatEntity::Entry *
+OptionValueProperties::GetPropertyAtIndexAsFormatEntity(
+ const ExecutionContext *exe_ctx, uint32_t idx) {
+ const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->GetFormatEntity();
+ }
+ return nullptr;
+}
+
+OptionValueFileSpec *
+OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec(
+ const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->GetAsFileSpec();
+ }
+ return nullptr;
+}
+
+FileSpec OptionValueProperties::GetPropertyAtIndexAsFileSpec(
+ const ExecutionContext *exe_ctx, uint32_t idx) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->GetFileSpecValue();
+ }
+ return FileSpec();
+}
+
+bool OptionValueProperties::SetPropertyAtIndexAsFileSpec(
+ const ExecutionContext *exe_ctx, uint32_t idx,
+ const FileSpec &new_file_spec) {
+ const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->SetFileSpecValue(new_file_spec);
+ }
+ return false;
+}
+
+const RegularExpression *
+OptionValueProperties::GetPropertyAtIndexAsOptionValueRegex(
+ const ExecutionContext *exe_ctx, uint32_t idx) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->GetRegexValue();
+ }
+ return nullptr;
+}
+
+OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64(
+ const ExecutionContext *exe_ctx, uint32_t idx) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->GetAsSInt64();
+ }
+ return nullptr;
+}
+
+int64_t OptionValueProperties::GetPropertyAtIndexAsSInt64(
+ const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->GetSInt64Value(fail_value);
+ }
+ return fail_value;
+}
+
+bool OptionValueProperties::SetPropertyAtIndexAsSInt64(
+ const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
+ const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->SetSInt64Value(new_value);
+ }
+ return false;
+}
+
+llvm::StringRef OptionValueProperties::GetPropertyAtIndexAsString(
+ const ExecutionContext *exe_ctx, uint32_t idx,
+ llvm::StringRef fail_value) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->GetStringValue(fail_value);
+ }
+ return fail_value;
+}
+
+bool OptionValueProperties::SetPropertyAtIndexAsString(
+ const ExecutionContext *exe_ctx, uint32_t idx, llvm::StringRef new_value) {
+ const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->SetStringValue(new_value);
+ }
+ return false;
+}
+
+OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString(
+ const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
+ OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
+ if (value_sp)
+ return value_sp->GetAsString();
+ return nullptr;
+}
+
+uint64_t OptionValueProperties::GetPropertyAtIndexAsUInt64(
+ const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->GetUInt64Value(fail_value);
+ }
+ return fail_value;
+}
+
+bool OptionValueProperties::SetPropertyAtIndexAsUInt64(
+ const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value) {
+ const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
+ if (property) {
+ OptionValue *value = property->GetValue().get();
+ if (value)
+ return value->SetUInt64Value(new_value);
+ }
+ return false;
+}
+
+bool OptionValueProperties::Clear() {
+ const size_t num_properties = m_properties.size();
+ for (size_t i = 0; i < num_properties; ++i)
+ m_properties[i].GetValue()->Clear();
+ return true;
+}
+
+Status OptionValueProperties::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+
+ // Args args(value_cstr);
+ // const size_t argc = args.GetArgumentCount();
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign:
+ case eVarSetOperationRemove:
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationAppend:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+ }
+
+ return error;
+}
+
+void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx,
+ Stream &strm, uint32_t dump_mask) {
+ const size_t num_properties = m_properties.size();
+ for (size_t i = 0; i < num_properties; ++i) {
+ const Property *property = GetPropertyAtIndex(exe_ctx, false, i);
+ if (property) {
+ OptionValue *option_value = property->GetValue().get();
+ assert(option_value);
+ const bool transparent_value = option_value->ValueIsTransparent();
+ property->Dump(exe_ctx, strm, dump_mask);
+ if (!transparent_value)
+ strm.EOL();
+ }
+ }
+}
+
+Status OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx,
+ Stream &strm,
+ llvm::StringRef property_path,
+ uint32_t dump_mask) {
+ Status error;
+ const bool will_modify = false;
+ lldb::OptionValueSP value_sp(
+ GetSubValue(exe_ctx, property_path, will_modify, error));
+ if (value_sp) {
+ if (!value_sp->ValueIsTransparent()) {
+ if (dump_mask & eDumpOptionName)
+ strm.PutCString(property_path);
+ if (dump_mask & ~eDumpOptionName)
+ strm.PutChar(' ');
+ }
+ value_sp->DumpValue(exe_ctx, strm, dump_mask);
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueProperties::DeepCopy() const {
+ llvm_unreachable("this shouldn't happen");
+}
+
+const Property *OptionValueProperties::GetPropertyAtPath(
+ const ExecutionContext *exe_ctx, bool will_modify, llvm::StringRef name) const {
+ const Property *property = nullptr;
+ if (name.empty())
+ return nullptr;
+ llvm::StringRef sub_name;
+ ConstString key;
+ size_t key_len = name.find_first_of(".[{");
+
+ if (key_len != llvm::StringRef::npos) {
+ key.SetString(name.take_front(key_len));
+ sub_name = name.drop_front(key_len);
+ } else
+ key.SetString(name);
+
+ property = GetProperty(exe_ctx, will_modify, key);
+ if (sub_name.empty() || !property)
+ return property;
+
+ if (sub_name[0] == '.') {
+ OptionValueProperties *sub_properties =
+ property->GetValue()->GetAsProperties();
+ if (sub_properties)
+ return sub_properties->GetPropertyAtPath(exe_ctx, will_modify,
+ sub_name.drop_front());
+ }
+ return nullptr;
+}
+
+void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
+ Stream &strm) const {
+ size_t max_name_len = 0;
+ const size_t num_properties = m_properties.size();
+ for (size_t i = 0; i < num_properties; ++i) {
+ const Property *property = ProtectedGetPropertyAtIndex(i);
+ if (property)
+ max_name_len = std::max<size_t>(property->GetName().size(), max_name_len);
+ }
+ for (size_t i = 0; i < num_properties; ++i) {
+ const Property *property = ProtectedGetPropertyAtIndex(i);
+ if (property)
+ property->DumpDescription(interpreter, strm, max_name_len, false);
+ }
+}
+
+void OptionValueProperties::Apropos(
+ llvm::StringRef keyword,
+ std::vector<const Property *> &matching_properties) const {
+ const size_t num_properties = m_properties.size();
+ StreamString strm;
+ for (size_t i = 0; i < num_properties; ++i) {
+ const Property *property = ProtectedGetPropertyAtIndex(i);
+ if (property) {
+ const OptionValueProperties *properties =
+ property->GetValue()->GetAsProperties();
+ if (properties) {
+ properties->Apropos(keyword, matching_properties);
+ } else {
+ bool match = false;
+ llvm::StringRef name = property->GetName();
+ if (name.contains_lower(keyword))
+ match = true;
+ else {
+ llvm::StringRef desc = property->GetDescription();
+ if (desc.contains_lower(keyword))
+ match = true;
+ }
+ if (match) {
+ matching_properties.push_back(property);
+ }
+ }
+ }
+ }
+}
+
+lldb::OptionValuePropertiesSP
+OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx,
+ ConstString name) {
+ lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name, false));
+ if (option_value_sp) {
+ OptionValueProperties *ov_properties = option_value_sp->GetAsProperties();
+ if (ov_properties)
+ return ov_properties->shared_from_this();
+ }
+ return lldb::OptionValuePropertiesSP();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueRegex.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueRegex.cpp
new file mode 100644
index 000000000000..bbe3fa715019
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueRegex.cpp
@@ -0,0 +1,67 @@
+//===-- OptionValueRegex.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueRegex.h"
+
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueRegex::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
+ uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+ if (m_regex.IsValid()) {
+ llvm::StringRef regex_text = m_regex.GetText();
+ strm.Printf("%s", regex_text.str().c_str());
+ }
+ }
+}
+
+Status OptionValueRegex::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationInvalid:
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ case eVarSetOperationAppend:
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign:
+ if (m_regex.Compile(value)) {
+ m_value_was_set = true;
+ NotifyValueChanged();
+ } else {
+ char regex_error[1024];
+ if (m_regex.GetErrorAsCString(regex_error, sizeof(regex_error)))
+ error.SetErrorString(regex_error);
+ else
+ error.SetErrorStringWithFormat("regex error %u",
+ m_regex.GetErrorCode());
+ }
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueRegex::DeepCopy() const {
+ return OptionValueSP(new OptionValueRegex(m_regex.GetText().str().c_str()));
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueSInt64.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueSInt64.cpp
new file mode 100644
index 000000000000..d26fc08a3132
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueSInt64.cpp
@@ -0,0 +1,76 @@
+//===-- OptionValueSInt64.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueSInt64.h"
+
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueSInt64::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
+ uint32_t dump_mask) {
+ // printf ("%p: DumpValue (exe_ctx=%p, strm, mask) m_current_value = %"
+ // PRIi64
+ // "\n", this, exe_ctx, m_current_value);
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ // if (dump_mask & eDumpOptionName)
+ // DumpQualifiedName (strm);
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+ strm.Printf("%" PRIi64, m_current_value);
+ }
+}
+
+Status OptionValueSInt64::SetValueFromString(llvm::StringRef value_ref,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign: {
+ bool success = false;
+ std::string value_str = value_ref.trim().str();
+ int64_t value = StringConvert::ToSInt64(value_str.c_str(), 0, 0, &success);
+ if (success) {
+ if (value >= m_min_value && value <= m_max_value) {
+ m_value_was_set = true;
+ m_current_value = value;
+ NotifyValueChanged();
+ } else
+ error.SetErrorStringWithFormat(
+ "%" PRIi64 " is out of range, valid values must be between %" PRIi64
+ " and %" PRIi64 ".",
+ value, m_min_value, m_max_value);
+ } else {
+ error.SetErrorStringWithFormat("invalid int64_t string value: '%s'",
+ value_ref.str().c_str());
+ }
+ } break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ case eVarSetOperationAppend:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value_ref, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueSInt64::DeepCopy() const {
+ return OptionValueSP(new OptionValueSInt64(*this));
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueString.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueString.cpp
new file mode 100644
index 000000000000..a519249ab472
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueString.cpp
@@ -0,0 +1,148 @@
+//===-- OptionValueString.cpp ------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueString.h"
+
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueString::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
+ uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+ if (!m_current_value.empty() || m_value_was_set) {
+ if (m_options.Test(eOptionEncodeCharacterEscapeSequences)) {
+ std::string expanded_escape_value;
+ Args::ExpandEscapedCharacters(m_current_value.c_str(),
+ expanded_escape_value);
+ if (dump_mask & eDumpOptionRaw)
+ strm.Printf("%s", expanded_escape_value.c_str());
+ else
+ strm.Printf("\"%s\"", expanded_escape_value.c_str());
+ } else {
+ if (dump_mask & eDumpOptionRaw)
+ strm.Printf("%s", m_current_value.c_str());
+ else
+ strm.Printf("\"%s\"", m_current_value.c_str());
+ }
+ }
+ }
+}
+
+Status OptionValueString::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+
+ std::string value_str = value.str();
+ value = value.trim();
+ if (value.size() > 0) {
+ switch (value.front()) {
+ case '"':
+ case '\'': {
+ if (value.size() <= 1 || value.back() != value.front()) {
+ error.SetErrorString("mismatched quotes");
+ return error;
+ }
+ value = value.drop_front().drop_back();
+ } break;
+ }
+ value_str = value.str();
+ }
+
+ switch (op) {
+ case eVarSetOperationInvalid:
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ if (m_validator) {
+ error = m_validator(value_str.c_str(), m_validator_baton);
+ if (error.Fail())
+ return error;
+ }
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+
+ case eVarSetOperationAppend: {
+ std::string new_value(m_current_value);
+ if (value.size() > 0) {
+ if (m_options.Test(eOptionEncodeCharacterEscapeSequences)) {
+ std::string str;
+ Args::EncodeEscapeSequences(value_str.c_str(), str);
+ new_value.append(str);
+ } else
+ new_value.append(value);
+ }
+ if (m_validator) {
+ error = m_validator(new_value.c_str(), m_validator_baton);
+ if (error.Fail())
+ return error;
+ }
+ m_current_value.assign(new_value);
+ NotifyValueChanged();
+ } break;
+
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign:
+ if (m_validator) {
+ error = m_validator(value_str.c_str(), m_validator_baton);
+ if (error.Fail())
+ return error;
+ }
+ m_value_was_set = true;
+ if (m_options.Test(eOptionEncodeCharacterEscapeSequences)) {
+ Args::EncodeEscapeSequences(value_str.c_str(), m_current_value);
+ } else {
+ SetCurrentValue(value_str);
+ }
+ NotifyValueChanged();
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueString::DeepCopy() const {
+ return OptionValueSP(new OptionValueString(*this));
+}
+
+Status OptionValueString::SetCurrentValue(llvm::StringRef value) {
+ if (m_validator) {
+ Status error(m_validator(value.str().c_str(), m_validator_baton));
+ if (error.Fail())
+ return error;
+ }
+ m_current_value.assign(value);
+ return Status();
+}
+
+Status OptionValueString::AppendToCurrentValue(const char *value) {
+ if (value && value[0]) {
+ if (m_validator) {
+ std::string new_value(m_current_value);
+ new_value.append(value);
+ Status error(m_validator(value, m_validator_baton));
+ if (error.Fail())
+ return error;
+ m_current_value.assign(new_value);
+ } else
+ m_current_value.append(value);
+ }
+ return Status();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueUInt64.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueUInt64.cpp
new file mode 100644
index 000000000000..3be0772daa8c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueUInt64.cpp
@@ -0,0 +1,75 @@
+//===-- OptionValueUInt64.cpp ------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueUInt64.h"
+
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+lldb::OptionValueSP OptionValueUInt64::Create(llvm::StringRef value_str,
+ Status &error) {
+ lldb::OptionValueSP value_sp(new OptionValueUInt64());
+ error = value_sp->SetValueFromString(value_str);
+ if (error.Fail())
+ value_sp.reset();
+ return value_sp;
+}
+
+void OptionValueUInt64::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
+ uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+ strm.Printf("%" PRIu64, m_current_value);
+ }
+}
+
+Status OptionValueUInt64::SetValueFromString(llvm::StringRef value_ref,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign: {
+ bool success = false;
+ std::string value_str = value_ref.trim().str();
+ uint64_t value = StringConvert::ToUInt64(value_str.c_str(), 0, 0, &success);
+ if (success) {
+ m_value_was_set = true;
+ m_current_value = value;
+ NotifyValueChanged();
+ } else {
+ error.SetErrorStringWithFormat("invalid uint64_t string value: '%s'",
+ value_str.c_str());
+ }
+ } break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ case eVarSetOperationAppend:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value_ref, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueUInt64::DeepCopy() const {
+ return OptionValueSP(new OptionValueUInt64(*this));
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueUUID.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueUUID.cpp
new file mode 100644
index 000000000000..f39b66b77bb0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueUUID.cpp
@@ -0,0 +1,91 @@
+//===-- OptionValueUUID.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionValueUUID.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OptionValueUUID::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
+ uint32_t dump_mask) {
+ if (dump_mask & eDumpOptionType)
+ strm.Printf("(%s)", GetTypeAsCString());
+ if (dump_mask & eDumpOptionValue) {
+ if (dump_mask & eDumpOptionType)
+ strm.PutCString(" = ");
+ m_uuid.Dump(&strm);
+ }
+}
+
+Status OptionValueUUID::SetValueFromString(llvm::StringRef value,
+ VarSetOperationType op) {
+ Status error;
+ switch (op) {
+ case eVarSetOperationClear:
+ Clear();
+ NotifyValueChanged();
+ break;
+
+ case eVarSetOperationReplace:
+ case eVarSetOperationAssign: {
+ if (m_uuid.SetFromStringRef(value) == 0)
+ error.SetErrorStringWithFormat("invalid uuid string value '%s'",
+ value.str().c_str());
+ else {
+ m_value_was_set = true;
+ NotifyValueChanged();
+ }
+ } break;
+
+ case eVarSetOperationInsertBefore:
+ case eVarSetOperationInsertAfter:
+ case eVarSetOperationRemove:
+ case eVarSetOperationAppend:
+ case eVarSetOperationInvalid:
+ error = OptionValue::SetValueFromString(value, op);
+ break;
+ }
+ return error;
+}
+
+lldb::OptionValueSP OptionValueUUID::DeepCopy() const {
+ return OptionValueSP(new OptionValueUUID(*this));
+}
+
+size_t OptionValueUUID::AutoComplete(CommandInterpreter &interpreter,
+ CompletionRequest &request) {
+ request.SetWordComplete(false);
+ ExecutionContext exe_ctx(interpreter.GetExecutionContext());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ auto prefix = request.GetCursorArgumentPrefix();
+ llvm::SmallVector<uint8_t, 20> uuid_bytes;
+ if (UUID::DecodeUUIDBytesFromString(prefix, uuid_bytes).empty()) {
+ const size_t num_modules = target->GetImages().GetSize();
+ for (size_t i = 0; i < num_modules; ++i) {
+ ModuleSP module_sp(target->GetImages().GetModuleAtIndex(i));
+ if (module_sp) {
+ const UUID &module_uuid = module_sp->GetUUID();
+ if (module_uuid.IsValid()) {
+ llvm::ArrayRef<uint8_t> module_bytes = module_uuid.GetBytes();
+ if (module_bytes.size() >= uuid_bytes.size() &&
+ module_bytes.take_front(uuid_bytes.size()).equals(uuid_bytes)) {
+ request.AddCompletion(module_uuid.GetAsString());
+ }
+ }
+ }
+ }
+ }
+ }
+ return request.GetNumberOfMatches();
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/Options.cpp b/contrib/llvm-project/lldb/source/Interpreter/Options.cpp
new file mode 100644
index 000000000000..ba15c020f2da
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/Options.cpp
@@ -0,0 +1,1425 @@
+//===-- Options.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/Options.h"
+
+#include <algorithm>
+#include <bitset>
+#include <map>
+#include <set>
+
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Options
+Options::Options() : m_getopt_table() { BuildValidOptionSets(); }
+
+Options::~Options() {}
+
+void Options::NotifyOptionParsingStarting(ExecutionContext *execution_context) {
+ m_seen_options.clear();
+ // Let the subclass reset its option values
+ OptionParsingStarting(execution_context);
+}
+
+Status
+Options::NotifyOptionParsingFinished(ExecutionContext *execution_context) {
+ return OptionParsingFinished(execution_context);
+}
+
+void Options::OptionSeen(int option_idx) { m_seen_options.insert(option_idx); }
+
+// Returns true is set_a is a subset of set_b; Otherwise returns false.
+
+bool Options::IsASubset(const OptionSet &set_a, const OptionSet &set_b) {
+ bool is_a_subset = true;
+ OptionSet::const_iterator pos_a;
+ OptionSet::const_iterator pos_b;
+
+ // set_a is a subset of set_b if every member of set_a is also a member of
+ // set_b
+
+ for (pos_a = set_a.begin(); pos_a != set_a.end() && is_a_subset; ++pos_a) {
+ pos_b = set_b.find(*pos_a);
+ if (pos_b == set_b.end())
+ is_a_subset = false;
+ }
+
+ return is_a_subset;
+}
+
+// Returns the set difference set_a - set_b, i.e. { x | ElementOf (x, set_a) &&
+// !ElementOf (x, set_b) }
+
+size_t Options::OptionsSetDiff(const OptionSet &set_a, const OptionSet &set_b,
+ OptionSet &diffs) {
+ size_t num_diffs = 0;
+ OptionSet::const_iterator pos_a;
+ OptionSet::const_iterator pos_b;
+
+ for (pos_a = set_a.begin(); pos_a != set_a.end(); ++pos_a) {
+ pos_b = set_b.find(*pos_a);
+ if (pos_b == set_b.end()) {
+ ++num_diffs;
+ diffs.insert(*pos_a);
+ }
+ }
+
+ return num_diffs;
+}
+
+// Returns the union of set_a and set_b. Does not put duplicate members into
+// the union.
+
+void Options::OptionsSetUnion(const OptionSet &set_a, const OptionSet &set_b,
+ OptionSet &union_set) {
+ OptionSet::const_iterator pos;
+ OptionSet::iterator pos_union;
+
+ // Put all the elements of set_a into the union.
+
+ for (pos = set_a.begin(); pos != set_a.end(); ++pos)
+ union_set.insert(*pos);
+
+ // Put all the elements of set_b that are not already there into the union.
+ for (pos = set_b.begin(); pos != set_b.end(); ++pos) {
+ pos_union = union_set.find(*pos);
+ if (pos_union == union_set.end())
+ union_set.insert(*pos);
+ }
+}
+
+bool Options::VerifyOptions(CommandReturnObject &result) {
+ bool options_are_valid = false;
+
+ int num_levels = GetRequiredOptions().size();
+ if (num_levels) {
+ for (int i = 0; i < num_levels && !options_are_valid; ++i) {
+ // This is the correct set of options if: 1). m_seen_options contains
+ // all of m_required_options[i] (i.e. all the required options at this
+ // level are a subset of m_seen_options); AND 2). { m_seen_options -
+ // m_required_options[i] is a subset of m_options_options[i] (i.e. all
+ // the rest of m_seen_options are in the set of optional options at this
+ // level.
+
+ // Check to see if all of m_required_options[i] are a subset of
+ // m_seen_options
+ if (IsASubset(GetRequiredOptions()[i], m_seen_options)) {
+ // Construct the set difference: remaining_options = {m_seen_options} -
+ // {m_required_options[i]}
+ OptionSet remaining_options;
+ OptionsSetDiff(m_seen_options, GetRequiredOptions()[i],
+ remaining_options);
+ // Check to see if remaining_options is a subset of
+ // m_optional_options[i]
+ if (IsASubset(remaining_options, GetOptionalOptions()[i]))
+ options_are_valid = true;
+ }
+ }
+ } else {
+ options_are_valid = true;
+ }
+
+ if (options_are_valid) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ } else {
+ result.AppendError("invalid combination of options for the given command");
+ result.SetStatus(eReturnStatusFailed);
+ }
+
+ return options_are_valid;
+}
+
+// This is called in the Options constructor, though we could call it lazily if
+// that ends up being a performance problem.
+
+void Options::BuildValidOptionSets() {
+ // Check to see if we already did this.
+ if (m_required_options.size() != 0)
+ return;
+
+ // Check to see if there are any options.
+ int num_options = NumCommandOptions();
+ if (num_options == 0)
+ return;
+
+ auto opt_defs = GetDefinitions();
+ m_required_options.resize(1);
+ m_optional_options.resize(1);
+
+ // First count the number of option sets we've got. Ignore
+ // LLDB_ALL_OPTION_SETS...
+
+ uint32_t num_option_sets = 0;
+
+ for (const auto &def : opt_defs) {
+ uint32_t this_usage_mask = def.usage_mask;
+ if (this_usage_mask == LLDB_OPT_SET_ALL) {
+ if (num_option_sets == 0)
+ num_option_sets = 1;
+ } else {
+ for (uint32_t j = 0; j < LLDB_MAX_NUM_OPTION_SETS; j++) {
+ if (this_usage_mask & (1 << j)) {
+ if (num_option_sets <= j)
+ num_option_sets = j + 1;
+ }
+ }
+ }
+ }
+
+ if (num_option_sets > 0) {
+ m_required_options.resize(num_option_sets);
+ m_optional_options.resize(num_option_sets);
+
+ for (const auto &def : opt_defs) {
+ for (uint32_t j = 0; j < num_option_sets; j++) {
+ if (def.usage_mask & 1 << j) {
+ if (def.required)
+ m_required_options[j].insert(def.short_option);
+ else
+ m_optional_options[j].insert(def.short_option);
+ }
+ }
+ }
+ }
+}
+
+uint32_t Options::NumCommandOptions() { return GetDefinitions().size(); }
+
+Option *Options::GetLongOptions() {
+ // Check to see if this has already been done.
+ if (m_getopt_table.empty()) {
+ auto defs = GetDefinitions();
+ if (defs.empty())
+ return nullptr;
+
+ std::map<int, uint32_t> option_seen;
+
+ m_getopt_table.resize(defs.size() + 1);
+ for (size_t i = 0; i < defs.size(); ++i) {
+ const int short_opt = defs[i].short_option;
+
+ m_getopt_table[i].definition = &defs[i];
+ m_getopt_table[i].flag = nullptr;
+ m_getopt_table[i].val = short_opt;
+
+ if (option_seen.find(short_opt) == option_seen.end()) {
+ option_seen[short_opt] = i;
+ } else if (short_opt) {
+ m_getopt_table[i].val = 0;
+ std::map<int, uint32_t>::const_iterator pos =
+ option_seen.find(short_opt);
+ StreamString strm;
+ if (isprint8(short_opt))
+ Host::SystemLog(Host::eSystemLogError,
+ "option[%u] --%s has a short option -%c that "
+ "conflicts with option[%u] --%s, short option won't "
+ "be used for --%s\n",
+ (int)i, defs[i].long_option, short_opt, pos->second,
+ m_getopt_table[pos->second].definition->long_option,
+ defs[i].long_option);
+ else
+ Host::SystemLog(Host::eSystemLogError,
+ "option[%u] --%s has a short option 0x%x that "
+ "conflicts with option[%u] --%s, short option won't "
+ "be used for --%s\n",
+ (int)i, defs[i].long_option, short_opt, pos->second,
+ m_getopt_table[pos->second].definition->long_option,
+ defs[i].long_option);
+ }
+ }
+
+ // getopt_long_only requires a NULL final entry in the table:
+
+ m_getopt_table.back().definition = nullptr;
+ m_getopt_table.back().flag = nullptr;
+ m_getopt_table.back().val = 0;
+ }
+
+ if (m_getopt_table.empty())
+ return nullptr;
+
+ return &m_getopt_table.front();
+}
+
+// This function takes INDENT, which tells how many spaces to output at the
+// front of each line; SPACES, which is a string containing 80 spaces; and
+// TEXT, which is the text that is to be output. It outputs the text, on
+// multiple lines if necessary, to RESULT, with INDENT spaces at the front of
+// each line. It breaks lines on spaces, tabs or newlines, shortening the line
+// if necessary to not break in the middle of a word. It assumes that each
+// output line should contain a maximum of OUTPUT_MAX_COLUMNS characters.
+
+void Options::OutputFormattedUsageText(Stream &strm,
+ const OptionDefinition &option_def,
+ uint32_t output_max_columns) {
+ std::string actual_text;
+ if (option_def.validator) {
+ const char *condition = option_def.validator->ShortConditionString();
+ if (condition) {
+ actual_text = "[";
+ actual_text.append(condition);
+ actual_text.append("] ");
+ }
+ }
+ actual_text.append(option_def.usage_text);
+
+ // Will it all fit on one line?
+
+ if (static_cast<uint32_t>(actual_text.length() + strm.GetIndentLevel()) <
+ output_max_columns) {
+ // Output it as a single line.
+ strm.Indent(actual_text.c_str());
+ strm.EOL();
+ } else {
+ // We need to break it up into multiple lines.
+
+ int text_width = output_max_columns - strm.GetIndentLevel() - 1;
+ int start = 0;
+ int end = start;
+ int final_end = actual_text.length();
+ int sub_len;
+
+ while (end < final_end) {
+ // Don't start the 'text' on a space, since we're already outputting the
+ // indentation.
+ while ((start < final_end) && (actual_text[start] == ' '))
+ start++;
+
+ end = start + text_width;
+ if (end > final_end)
+ end = final_end;
+ else {
+ // If we're not at the end of the text, make sure we break the line on
+ // white space.
+ while (end > start && actual_text[end] != ' ' &&
+ actual_text[end] != '\t' && actual_text[end] != '\n')
+ end--;
+ }
+
+ sub_len = end - start;
+ if (start != 0)
+ strm.EOL();
+ strm.Indent();
+ assert(start < final_end);
+ assert(start + sub_len <= final_end);
+ strm.Write(actual_text.c_str() + start, sub_len);
+ start = end + 1;
+ }
+ strm.EOL();
+ }
+}
+
+bool Options::SupportsLongOption(const char *long_option) {
+ if (!long_option || !long_option[0])
+ return false;
+
+ auto opt_defs = GetDefinitions();
+ if (opt_defs.empty())
+ return false;
+
+ const char *long_option_name = long_option;
+ if (long_option[0] == '-' && long_option[1] == '-')
+ long_option_name += 2;
+
+ for (auto &def : opt_defs) {
+ if (!def.long_option)
+ continue;
+
+ if (strcmp(def.long_option, long_option_name) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+enum OptionDisplayType {
+ eDisplayBestOption,
+ eDisplayShortOption,
+ eDisplayLongOption
+};
+
+static bool PrintOption(const OptionDefinition &opt_def,
+ OptionDisplayType display_type, const char *header,
+ const char *footer, bool show_optional, Stream &strm) {
+ const bool has_short_option = isprint8(opt_def.short_option) != 0;
+
+ if (display_type == eDisplayShortOption && !has_short_option)
+ return false;
+
+ if (header && header[0])
+ strm.PutCString(header);
+
+ if (show_optional && !opt_def.required)
+ strm.PutChar('[');
+ const bool show_short_option =
+ has_short_option && display_type != eDisplayLongOption;
+ if (show_short_option)
+ strm.Printf("-%c", opt_def.short_option);
+ else
+ strm.Printf("--%s", opt_def.long_option);
+ switch (opt_def.option_has_arg) {
+ case OptionParser::eNoArgument:
+ break;
+ case OptionParser::eRequiredArgument:
+ strm.Printf(" <%s>", CommandObject::GetArgumentName(opt_def.argument_type));
+ break;
+
+ case OptionParser::eOptionalArgument:
+ strm.Printf("%s[<%s>]", show_short_option ? "" : "=",
+ CommandObject::GetArgumentName(opt_def.argument_type));
+ break;
+ }
+ if (show_optional && !opt_def.required)
+ strm.PutChar(']');
+ if (footer && footer[0])
+ strm.PutCString(footer);
+ return true;
+}
+
+void Options::GenerateOptionUsage(Stream &strm, CommandObject *cmd,
+ uint32_t screen_width) {
+ const bool only_print_args = cmd->IsDashDashCommand();
+
+ auto opt_defs = GetDefinitions();
+ const uint32_t save_indent_level = strm.GetIndentLevel();
+ llvm::StringRef name;
+
+ StreamString arguments_str;
+
+ if (cmd) {
+ name = cmd->GetCommandName();
+ cmd->GetFormattedCommandArguments(arguments_str);
+ } else
+ name = "";
+
+ strm.PutCString("\nCommand Options Usage:\n");
+
+ strm.IndentMore(2);
+
+ // First, show each usage level set of options, e.g. <cmd> [options-for-
+ // level-0]
+ // <cmd>
+ // [options-for-level-1]
+ // etc.
+
+ const uint32_t num_options = NumCommandOptions();
+ if (num_options == 0)
+ return;
+
+ uint32_t num_option_sets = GetRequiredOptions().size();
+
+ uint32_t i;
+
+ if (!only_print_args) {
+ for (uint32_t opt_set = 0; opt_set < num_option_sets; ++opt_set) {
+ uint32_t opt_set_mask;
+
+ opt_set_mask = 1 << opt_set;
+ if (opt_set > 0)
+ strm.Printf("\n");
+ strm.Indent(name);
+
+ // Different option sets may require different args.
+ StreamString args_str;
+ if (cmd)
+ cmd->GetFormattedCommandArguments(args_str, opt_set_mask);
+
+ // First go through and print all options that take no arguments as a
+ // single string. If a command has "-a" "-b" and "-c", this will show up
+ // as [-abc]
+
+ std::set<int> options;
+ std::set<int>::const_iterator options_pos, options_end;
+ for (auto &def : opt_defs) {
+ if (def.usage_mask & opt_set_mask && isprint8(def.short_option)) {
+ // Add current option to the end of out_stream.
+
+ if (def.required && def.option_has_arg == OptionParser::eNoArgument) {
+ options.insert(def.short_option);
+ }
+ }
+ }
+
+ if (!options.empty()) {
+ // We have some required options with no arguments
+ strm.PutCString(" -");
+ for (i = 0; i < 2; ++i)
+ for (options_pos = options.begin(), options_end = options.end();
+ options_pos != options_end; ++options_pos) {
+ if (i == 0 && ::islower(*options_pos))
+ continue;
+ if (i == 1 && ::isupper(*options_pos))
+ continue;
+ strm << (char)*options_pos;
+ }
+ }
+
+ options.clear();
+ for (auto &def : opt_defs) {
+ if (def.usage_mask & opt_set_mask && isprint8(def.short_option)) {
+ // Add current option to the end of out_stream.
+
+ if (!def.required &&
+ def.option_has_arg == OptionParser::eNoArgument) {
+ options.insert(def.short_option);
+ }
+ }
+ }
+
+ if (!options.empty()) {
+ // We have some required options with no arguments
+ strm.PutCString(" [-");
+ for (i = 0; i < 2; ++i)
+ for (options_pos = options.begin(), options_end = options.end();
+ options_pos != options_end; ++options_pos) {
+ if (i == 0 && ::islower(*options_pos))
+ continue;
+ if (i == 1 && ::isupper(*options_pos))
+ continue;
+ strm << (char)*options_pos;
+ }
+ strm.PutChar(']');
+ }
+
+ // First go through and print the required options (list them up front).
+
+ for (auto &def : opt_defs) {
+ if (def.usage_mask & opt_set_mask && isprint8(def.short_option)) {
+ if (def.required && def.option_has_arg != OptionParser::eNoArgument)
+ PrintOption(def, eDisplayBestOption, " ", nullptr, true, strm);
+ }
+ }
+
+ // Now go through again, and this time only print the optional options.
+
+ for (auto &def : opt_defs) {
+ if (def.usage_mask & opt_set_mask) {
+ // Add current option to the end of out_stream.
+
+ if (!def.required && def.option_has_arg != OptionParser::eNoArgument)
+ PrintOption(def, eDisplayBestOption, " ", nullptr, true, strm);
+ }
+ }
+
+ if (args_str.GetSize() > 0) {
+ if (cmd->WantsRawCommandString() && !only_print_args)
+ strm.Printf(" --");
+
+ strm << " " << args_str.GetString();
+ if (only_print_args)
+ break;
+ }
+ }
+ }
+
+ if (cmd && (only_print_args || cmd->WantsRawCommandString()) &&
+ arguments_str.GetSize() > 0) {
+ if (!only_print_args)
+ strm.PutChar('\n');
+ strm.Indent(name);
+ strm << " " << arguments_str.GetString();
+ }
+
+ strm.Printf("\n\n");
+
+ if (!only_print_args) {
+ // Now print out all the detailed information about the various options:
+ // long form, short form and help text:
+ // -short <argument> ( --long_name <argument> )
+ // help text
+
+ // This variable is used to keep track of which options' info we've printed
+ // out, because some options can be in more than one usage level, but we
+ // only want to print the long form of its information once.
+
+ std::multimap<int, uint32_t> options_seen;
+ strm.IndentMore(5);
+
+ // Put the unique command options in a vector & sort it, so we can output
+ // them alphabetically (by short_option) when writing out detailed help for
+ // each option.
+
+ i = 0;
+ for (auto &def : opt_defs)
+ options_seen.insert(std::make_pair(def.short_option, i++));
+
+ // Go through the unique'd and alphabetically sorted vector of options,
+ // find the table entry for each option and write out the detailed help
+ // information for that option.
+
+ bool first_option_printed = false;
+
+ for (auto pos : options_seen) {
+ i = pos.second;
+ // Print out the help information for this option.
+
+ // Put a newline separation between arguments
+ if (first_option_printed)
+ strm.EOL();
+ else
+ first_option_printed = true;
+
+ CommandArgumentType arg_type = opt_defs[i].argument_type;
+
+ StreamString arg_name_str;
+ arg_name_str.Printf("<%s>", CommandObject::GetArgumentName(arg_type));
+
+ strm.Indent();
+ if (opt_defs[i].short_option && isprint8(opt_defs[i].short_option)) {
+ PrintOption(opt_defs[i], eDisplayShortOption, nullptr, nullptr, false,
+ strm);
+ PrintOption(opt_defs[i], eDisplayLongOption, " ( ", " )", false, strm);
+ } else {
+ // Short option is not printable, just print long option
+ PrintOption(opt_defs[i], eDisplayLongOption, nullptr, nullptr, false,
+ strm);
+ }
+ strm.EOL();
+
+ strm.IndentMore(5);
+
+ if (opt_defs[i].usage_text)
+ OutputFormattedUsageText(strm, opt_defs[i], screen_width);
+ if (!opt_defs[i].enum_values.empty()) {
+ strm.Indent();
+ strm.Printf("Values: ");
+ bool is_first = true;
+ for (const auto &enum_value : opt_defs[i].enum_values) {
+ if (is_first) {
+ strm.Printf("%s", enum_value.string_value);
+ is_first = false;
+ }
+ else
+ strm.Printf(" | %s", enum_value.string_value);
+ }
+ strm.EOL();
+ }
+ strm.IndentLess(5);
+ }
+ }
+
+ // Restore the indent level
+ strm.SetIndentLevel(save_indent_level);
+}
+
+// This function is called when we have been given a potentially incomplete set
+// of options, such as when an alias has been defined (more options might be
+// added at at the time the alias is invoked). We need to verify that the
+// options in the set m_seen_options are all part of a set that may be used
+// together, but m_seen_options may be missing some of the "required" options.
+
+bool Options::VerifyPartialOptions(CommandReturnObject &result) {
+ bool options_are_valid = false;
+
+ int num_levels = GetRequiredOptions().size();
+ if (num_levels) {
+ for (int i = 0; i < num_levels && !options_are_valid; ++i) {
+ // In this case we are treating all options as optional rather than
+ // required. Therefore a set of options is correct if m_seen_options is a
+ // subset of the union of m_required_options and m_optional_options.
+ OptionSet union_set;
+ OptionsSetUnion(GetRequiredOptions()[i], GetOptionalOptions()[i],
+ union_set);
+ if (IsASubset(m_seen_options, union_set))
+ options_are_valid = true;
+ }
+ }
+
+ return options_are_valid;
+}
+
+bool Options::HandleOptionCompletion(CompletionRequest &request,
+ OptionElementVector &opt_element_vector,
+ CommandInterpreter &interpreter) {
+ request.SetWordComplete(true);
+
+ // For now we just scan the completions to see if the cursor position is in
+ // an option or its argument. Otherwise we'll call HandleArgumentCompletion.
+ // In the future we can use completion to validate options as well if we
+ // want.
+
+ auto opt_defs = GetDefinitions();
+
+ std::string cur_opt_std_str = request.GetCursorArgumentPrefix().str();
+ const char *cur_opt_str = cur_opt_std_str.c_str();
+
+ for (size_t i = 0; i < opt_element_vector.size(); i++) {
+ int opt_pos = opt_element_vector[i].opt_pos;
+ int opt_arg_pos = opt_element_vector[i].opt_arg_pos;
+ int opt_defs_index = opt_element_vector[i].opt_defs_index;
+ if (opt_pos == request.GetCursorIndex()) {
+ // We're completing the option itself.
+
+ if (opt_defs_index == OptionArgElement::eBareDash) {
+ // We're completing a bare dash. That means all options are open.
+ // FIXME: We should scan the other options provided and only complete
+ // options
+ // within the option group they belong to.
+ char opt_str[3] = {'-', 'a', '\0'};
+
+ for (auto &def : opt_defs) {
+ if (!def.short_option)
+ continue;
+ opt_str[1] = def.short_option;
+ request.AddCompletion(opt_str);
+ }
+
+ return true;
+ } else if (opt_defs_index == OptionArgElement::eBareDoubleDash) {
+ std::string full_name("--");
+ for (auto &def : opt_defs) {
+ if (!def.short_option)
+ continue;
+
+ full_name.erase(full_name.begin() + 2, full_name.end());
+ full_name.append(def.long_option);
+ request.AddCompletion(full_name.c_str());
+ }
+ return true;
+ } else if (opt_defs_index != OptionArgElement::eUnrecognizedArg) {
+ // We recognized it, if it an incomplete long option, complete it
+ // anyway (getopt_long_only is happy with shortest unique string, but
+ // it's still a nice thing to do.) Otherwise return The string so the
+ // upper level code will know this is a full match and add the " ".
+ if (cur_opt_str && strlen(cur_opt_str) > 2 && cur_opt_str[0] == '-' &&
+ cur_opt_str[1] == '-' &&
+ strcmp(opt_defs[opt_defs_index].long_option, cur_opt_str) != 0) {
+ std::string full_name("--");
+ full_name.append(opt_defs[opt_defs_index].long_option);
+ request.AddCompletion(full_name.c_str());
+ return true;
+ } else {
+ request.AddCompletion(request.GetCursorArgument());
+ return true;
+ }
+ } else {
+ // FIXME - not handling wrong options yet:
+ // Check to see if they are writing a long option & complete it.
+ // I think we will only get in here if the long option table has two
+ // elements
+ // that are not unique up to this point. getopt_long_only does
+ // shortest unique match for long options already.
+
+ if (cur_opt_str && strlen(cur_opt_str) > 2 && cur_opt_str[0] == '-' &&
+ cur_opt_str[1] == '-') {
+ for (auto &def : opt_defs) {
+ if (!def.long_option)
+ continue;
+
+ if (strstr(def.long_option, cur_opt_str + 2) == def.long_option) {
+ std::string full_name("--");
+ full_name.append(def.long_option);
+ request.AddCompletion(full_name.c_str());
+ }
+ }
+ }
+ return true;
+ }
+
+ } else if (opt_arg_pos == request.GetCursorIndex()) {
+ // Okay the cursor is on the completion of an argument. See if it has a
+ // completion, otherwise return no matches.
+
+ CompletionRequest subrequest = request;
+ subrequest.SetCursorCharPosition(subrequest.GetCursorArgument().size());
+ if (opt_defs_index != -1) {
+ HandleOptionArgumentCompletion(subrequest, opt_element_vector, i,
+ interpreter);
+ request.SetWordComplete(subrequest.GetWordComplete());
+ return true;
+ } else {
+ // No completion callback means no completions...
+ return true;
+ }
+
+ } else {
+ // Not the last element, keep going.
+ continue;
+ }
+ }
+ return false;
+}
+
+bool Options::HandleOptionArgumentCompletion(
+ CompletionRequest &request, OptionElementVector &opt_element_vector,
+ int opt_element_index, CommandInterpreter &interpreter) {
+ auto opt_defs = GetDefinitions();
+ std::unique_ptr<SearchFilter> filter_up;
+
+ int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos;
+ int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index;
+
+ // See if this is an enumeration type option, and if so complete it here:
+
+ const auto &enum_values = opt_defs[opt_defs_index].enum_values;
+ if (!enum_values.empty()) {
+ bool return_value = false;
+ std::string match_string(
+ request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos),
+ request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos) +
+ request.GetCursorCharPosition());
+
+ for (const auto &enum_value : enum_values) {
+ if (strstr(enum_value.string_value, match_string.c_str()) ==
+ enum_value.string_value) {
+ request.AddCompletion(enum_value.string_value);
+ return_value = true;
+ }
+ }
+ return return_value;
+ }
+
+ // If this is a source file or symbol type completion, and there is a -shlib
+ // option somewhere in the supplied arguments, then make a search filter for
+ // that shared library.
+ // FIXME: Do we want to also have an "OptionType" so we don't have to match
+ // string names?
+
+ uint32_t completion_mask = opt_defs[opt_defs_index].completion_type;
+
+ if (completion_mask == 0) {
+ lldb::CommandArgumentType option_arg_type =
+ opt_defs[opt_defs_index].argument_type;
+ if (option_arg_type != eArgTypeNone) {
+ const CommandObject::ArgumentTableEntry *arg_entry =
+ CommandObject::FindArgumentDataByType(
+ opt_defs[opt_defs_index].argument_type);
+ if (arg_entry)
+ completion_mask = arg_entry->completion_type;
+ }
+ }
+
+ if (completion_mask & CommandCompletions::eSourceFileCompletion ||
+ completion_mask & CommandCompletions::eSymbolCompletion) {
+ for (size_t i = 0; i < opt_element_vector.size(); i++) {
+ int cur_defs_index = opt_element_vector[i].opt_defs_index;
+
+ // trying to use <0 indices will definitely cause problems
+ if (cur_defs_index == OptionArgElement::eUnrecognizedArg ||
+ cur_defs_index == OptionArgElement::eBareDash ||
+ cur_defs_index == OptionArgElement::eBareDoubleDash)
+ continue;
+
+ int cur_arg_pos = opt_element_vector[i].opt_arg_pos;
+ const char *cur_opt_name = opt_defs[cur_defs_index].long_option;
+
+ // If this is the "shlib" option and there was an argument provided,
+ // restrict it to that shared library.
+ if (cur_opt_name && strcmp(cur_opt_name, "shlib") == 0 &&
+ cur_arg_pos != -1) {
+ const char *module_name =
+ request.GetParsedLine().GetArgumentAtIndex(cur_arg_pos);
+ if (module_name) {
+ FileSpec module_spec(module_name);
+ lldb::TargetSP target_sp =
+ interpreter.GetDebugger().GetSelectedTarget();
+ // Search filters require a target...
+ if (target_sp)
+ filter_up.reset(new SearchFilterByModule(target_sp, module_spec));
+ }
+ break;
+ }
+ }
+ }
+
+ return CommandCompletions::InvokeCommonCompletionCallbacks(
+ interpreter, completion_mask, request, filter_up.get());
+}
+
+void OptionGroupOptions::Append(OptionGroup *group) {
+ auto group_option_defs = group->GetDefinitions();
+ for (uint32_t i = 0; i < group_option_defs.size(); ++i) {
+ m_option_infos.push_back(OptionInfo(group, i));
+ m_option_defs.push_back(group_option_defs[i]);
+ }
+}
+
+const OptionGroup *OptionGroupOptions::GetGroupWithOption(char short_opt) {
+ for (uint32_t i = 0; i < m_option_defs.size(); i++) {
+ OptionDefinition opt_def = m_option_defs[i];
+ if (opt_def.short_option == short_opt)
+ return m_option_infos[i].option_group;
+ }
+ return nullptr;
+}
+
+void OptionGroupOptions::Append(OptionGroup *group, uint32_t src_mask,
+ uint32_t dst_mask) {
+ auto group_option_defs = group->GetDefinitions();
+ for (uint32_t i = 0; i < group_option_defs.size(); ++i) {
+ if (group_option_defs[i].usage_mask & src_mask) {
+ m_option_infos.push_back(OptionInfo(group, i));
+ m_option_defs.push_back(group_option_defs[i]);
+ m_option_defs.back().usage_mask = dst_mask;
+ }
+ }
+}
+
+void OptionGroupOptions::Finalize() {
+ m_did_finalize = true;
+}
+
+Status OptionGroupOptions::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_value,
+ ExecutionContext *execution_context) {
+ // After calling OptionGroupOptions::Append(...), you must finalize the
+ // groups by calling OptionGroupOptions::Finlize()
+ assert(m_did_finalize);
+ Status error;
+ if (option_idx < m_option_infos.size()) {
+ error = m_option_infos[option_idx].option_group->SetOptionValue(
+ m_option_infos[option_idx].option_index, option_value,
+ execution_context);
+
+ } else {
+ error.SetErrorString("invalid option index"); // Shouldn't happen...
+ }
+ return error;
+}
+
+void OptionGroupOptions::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ std::set<OptionGroup *> group_set;
+ OptionInfos::iterator pos, end = m_option_infos.end();
+ for (pos = m_option_infos.begin(); pos != end; ++pos) {
+ OptionGroup *group = pos->option_group;
+ if (group_set.find(group) == group_set.end()) {
+ group->OptionParsingStarting(execution_context);
+ group_set.insert(group);
+ }
+ }
+}
+Status
+OptionGroupOptions::OptionParsingFinished(ExecutionContext *execution_context) {
+ std::set<OptionGroup *> group_set;
+ Status error;
+ OptionInfos::iterator pos, end = m_option_infos.end();
+ for (pos = m_option_infos.begin(); pos != end; ++pos) {
+ OptionGroup *group = pos->option_group;
+ if (group_set.find(group) == group_set.end()) {
+ error = group->OptionParsingFinished(execution_context);
+ group_set.insert(group);
+ if (error.Fail())
+ return error;
+ }
+ }
+ return error;
+}
+
+// OptionParser permutes the arguments while processing them, so we create a
+// temporary array holding to avoid modification of the input arguments. The
+// options themselves are never modified, but the API expects a char * anyway,
+// hence the const_cast.
+static std::vector<char *> GetArgvForParsing(const Args &args) {
+ std::vector<char *> result;
+ // OptionParser always skips the first argument as it is based on getopt().
+ result.push_back(const_cast<char *>("<FAKE-ARG0>"));
+ for (const Args::ArgEntry &entry : args)
+ result.push_back(const_cast<char *>(entry.c_str()));
+ result.push_back(nullptr);
+ return result;
+}
+
+// Given a permuted argument, find it's position in the original Args vector.
+static Args::const_iterator FindOriginalIter(const char *arg,
+ const Args &original) {
+ return llvm::find_if(
+ original, [arg](const Args::ArgEntry &D) { return D.c_str() == arg; });
+}
+
+// Given a permuted argument, find it's index in the original Args vector.
+static size_t FindOriginalIndex(const char *arg, const Args &original) {
+ return std::distance(original.begin(), FindOriginalIter(arg, original));
+}
+
+// Construct a new Args object, consisting of the entries from the original
+// arguments, but in the permuted order.
+static Args ReconstituteArgsAfterParsing(llvm::ArrayRef<char *> parsed,
+ const Args &original) {
+ Args result;
+ for (const char *arg : parsed) {
+ auto pos = FindOriginalIter(arg, original);
+ assert(pos != original.end());
+ result.AppendArgument(pos->ref, pos->quote);
+ }
+ return result;
+}
+
+static size_t FindArgumentIndexForOption(const Args &args,
+ const Option &long_option) {
+ std::string short_opt = llvm::formatv("-{0}", char(long_option.val)).str();
+ std::string long_opt =
+ llvm::formatv("--{0}", long_option.definition->long_option);
+ for (const auto &entry : llvm::enumerate(args)) {
+ if (entry.value().ref.startswith(short_opt) ||
+ entry.value().ref.startswith(long_opt))
+ return entry.index();
+ }
+
+ return size_t(-1);
+}
+
+static std::string BuildShortOptions(const Option *long_options) {
+ std::string storage;
+ llvm::raw_string_ostream sstr(storage);
+
+ // Leading : tells getopt to return a : for a missing option argument AND to
+ // suppress error messages.
+ sstr << ":";
+
+ for (size_t i = 0; long_options[i].definition != nullptr; ++i) {
+ if (long_options[i].flag == nullptr) {
+ sstr << (char)long_options[i].val;
+ switch (long_options[i].definition->option_has_arg) {
+ default:
+ case OptionParser::eNoArgument:
+ break;
+ case OptionParser::eRequiredArgument:
+ sstr << ":";
+ break;
+ case OptionParser::eOptionalArgument:
+ sstr << "::";
+ break;
+ }
+ }
+ }
+ return std::move(sstr.str());
+}
+
+llvm::Expected<Args> Options::ParseAlias(const Args &args,
+ OptionArgVector *option_arg_vector,
+ std::string &input_line) {
+ Option *long_options = GetLongOptions();
+
+ if (long_options == nullptr) {
+ return llvm::make_error<llvm::StringError>("Invalid long options",
+ llvm::inconvertibleErrorCode());
+ }
+
+ std::string short_options = BuildShortOptions(long_options);
+
+ Args args_copy = args;
+ std::vector<char *> argv = GetArgvForParsing(args);
+
+ std::unique_lock<std::mutex> lock;
+ OptionParser::Prepare(lock);
+ int val;
+ while (true) {
+ int long_options_index = -1;
+ val = OptionParser::Parse(argv, short_options, long_options,
+ &long_options_index);
+
+ if (val == ':') {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "last option requires an argument");
+ }
+
+ if (val == -1)
+ break;
+
+ if (val == '?') {
+ return llvm::make_error<llvm::StringError>(
+ "Unknown or ambiguous option", llvm::inconvertibleErrorCode());
+ }
+
+ if (val == 0)
+ continue;
+
+ OptionSeen(val);
+
+ // Look up the long option index
+ if (long_options_index == -1) {
+ for (int j = 0; long_options[j].definition || long_options[j].flag ||
+ long_options[j].val;
+ ++j) {
+ if (long_options[j].val == val) {
+ long_options_index = j;
+ break;
+ }
+ }
+ }
+
+ // See if the option takes an argument, and see if one was supplied.
+ if (long_options_index == -1) {
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv("Invalid option with value '{0}'.", char(val)).str(),
+ llvm::inconvertibleErrorCode());
+ }
+
+ StreamString option_str;
+ option_str.Printf("-%c", val);
+ const OptionDefinition *def = long_options[long_options_index].definition;
+ int has_arg =
+ (def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg;
+
+ const char *option_arg = nullptr;
+ switch (has_arg) {
+ case OptionParser::eRequiredArgument:
+ if (OptionParser::GetOptionArgument() == nullptr) {
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv("Option '{0}' is missing argument specifier.",
+ option_str.GetString())
+ .str(),
+ llvm::inconvertibleErrorCode());
+ }
+ LLVM_FALLTHROUGH;
+ case OptionParser::eOptionalArgument:
+ option_arg = OptionParser::GetOptionArgument();
+ LLVM_FALLTHROUGH;
+ case OptionParser::eNoArgument:
+ break;
+ default:
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv("error with options table; invalid value in has_arg "
+ "field for option '{0}'.",
+ char(val))
+ .str(),
+ llvm::inconvertibleErrorCode());
+ }
+ if (!option_arg)
+ option_arg = "<no-argument>";
+ option_arg_vector->emplace_back(option_str.GetString(), has_arg,
+ option_arg);
+
+ // Find option in the argument list; also see if it was supposed to take an
+ // argument and if one was supplied. Remove option (and argument, if
+ // given) from the argument list. Also remove them from the
+ // raw_input_string, if one was passed in.
+ size_t idx =
+ FindArgumentIndexForOption(args_copy, long_options[long_options_index]);
+ if (idx == size_t(-1))
+ continue;
+
+ if (!input_line.empty()) {
+ auto tmp_arg = args_copy[idx].ref;
+ size_t pos = input_line.find(tmp_arg);
+ if (pos != std::string::npos)
+ input_line.erase(pos, tmp_arg.size());
+ }
+ args_copy.DeleteArgumentAtIndex(idx);
+ if ((long_options[long_options_index].definition->option_has_arg !=
+ OptionParser::eNoArgument) &&
+ (OptionParser::GetOptionArgument() != nullptr) &&
+ (idx < args_copy.GetArgumentCount()) &&
+ (args_copy[idx].ref == OptionParser::GetOptionArgument())) {
+ if (input_line.size() > 0) {
+ auto tmp_arg = args_copy[idx].ref;
+ size_t pos = input_line.find(tmp_arg);
+ if (pos != std::string::npos)
+ input_line.erase(pos, tmp_arg.size());
+ }
+ args_copy.DeleteArgumentAtIndex(idx);
+ }
+ }
+
+ return std::move(args_copy);
+}
+
+OptionElementVector Options::ParseForCompletion(const Args &args,
+ uint32_t cursor_index) {
+ OptionElementVector option_element_vector;
+ Option *long_options = GetLongOptions();
+ option_element_vector.clear();
+
+ if (long_options == nullptr)
+ return option_element_vector;
+
+ std::string short_options = BuildShortOptions(long_options);
+
+ std::unique_lock<std::mutex> lock;
+ OptionParser::Prepare(lock);
+ OptionParser::EnableError(false);
+
+ int val;
+ auto opt_defs = GetDefinitions();
+
+ std::vector<char *> dummy_vec = GetArgvForParsing(args);
+
+ bool failed_once = false;
+ uint32_t dash_dash_pos = -1;
+
+ while (true) {
+ bool missing_argument = false;
+ int long_options_index = -1;
+
+ val = OptionParser::Parse(dummy_vec, short_options, long_options,
+ &long_options_index);
+
+ if (val == -1) {
+ // When we're completing a "--" which is the last option on line,
+ if (failed_once)
+ break;
+
+ failed_once = true;
+
+ // If this is a bare "--" we mark it as such so we can complete it
+ // successfully later. Handling the "--" is a little tricky, since that
+ // may mean end of options or arguments, or the user might want to
+ // complete options by long name. I make this work by checking whether
+ // the cursor is in the "--" argument, and if so I assume we're
+ // completing the long option, otherwise I let it pass to
+ // OptionParser::Parse which will terminate the option parsing. Note, in
+ // either case we continue parsing the line so we can figure out what
+ // other options were passed. This will be useful when we come to
+ // restricting completions based on what other options we've seen on the
+ // line.
+
+ if (static_cast<size_t>(OptionParser::GetOptionIndex()) <
+ dummy_vec.size() &&
+ (strcmp(dummy_vec[OptionParser::GetOptionIndex() - 1], "--") == 0)) {
+ dash_dash_pos = FindOriginalIndex(
+ dummy_vec[OptionParser::GetOptionIndex() - 1], args);
+ if (dash_dash_pos == cursor_index) {
+ option_element_vector.push_back(
+ OptionArgElement(OptionArgElement::eBareDoubleDash, dash_dash_pos,
+ OptionArgElement::eBareDoubleDash));
+ continue;
+ } else
+ break;
+ } else
+ break;
+ } else if (val == '?') {
+ option_element_vector.push_back(OptionArgElement(
+ OptionArgElement::eUnrecognizedArg,
+ FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],
+ args),
+ OptionArgElement::eUnrecognizedArg));
+ continue;
+ } else if (val == 0) {
+ continue;
+ } else if (val == ':') {
+ // This is a missing argument.
+ val = OptionParser::GetOptionErrorCause();
+ missing_argument = true;
+ }
+
+ OptionSeen(val);
+
+ // Look up the long option index
+ if (long_options_index == -1) {
+ for (int j = 0; long_options[j].definition || long_options[j].flag ||
+ long_options[j].val;
+ ++j) {
+ if (long_options[j].val == val) {
+ long_options_index = j;
+ break;
+ }
+ }
+ }
+
+ // See if the option takes an argument, and see if one was supplied.
+ if (long_options_index >= 0) {
+ int opt_defs_index = -1;
+ for (size_t i = 0; i < opt_defs.size(); i++) {
+ if (opt_defs[i].short_option != val)
+ continue;
+ opt_defs_index = i;
+ break;
+ }
+
+ const OptionDefinition *def = long_options[long_options_index].definition;
+ int has_arg =
+ (def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg;
+ switch (has_arg) {
+ case OptionParser::eNoArgument:
+ option_element_vector.push_back(OptionArgElement(
+ opt_defs_index,
+ FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],
+ args),
+ 0));
+ break;
+ case OptionParser::eRequiredArgument:
+ if (OptionParser::GetOptionArgument() != nullptr) {
+ int arg_index;
+ if (missing_argument)
+ arg_index = -1;
+ else
+ arg_index = OptionParser::GetOptionIndex() - 2;
+
+ option_element_vector.push_back(OptionArgElement(
+ opt_defs_index,
+ FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2],
+ args),
+ arg_index));
+ } else {
+ option_element_vector.push_back(OptionArgElement(
+ opt_defs_index,
+ FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],
+ args),
+ -1));
+ }
+ break;
+ case OptionParser::eOptionalArgument:
+ if (OptionParser::GetOptionArgument() != nullptr) {
+ option_element_vector.push_back(OptionArgElement(
+ opt_defs_index,
+ FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2],
+ args),
+ FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],
+ args)));
+ } else {
+ option_element_vector.push_back(OptionArgElement(
+ opt_defs_index,
+ FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2],
+ args),
+ FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],
+ args)));
+ }
+ break;
+ default:
+ // The options table is messed up. Here we'll just continue
+ option_element_vector.push_back(OptionArgElement(
+ OptionArgElement::eUnrecognizedArg,
+ FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],
+ args),
+ OptionArgElement::eUnrecognizedArg));
+ break;
+ }
+ } else {
+ option_element_vector.push_back(OptionArgElement(
+ OptionArgElement::eUnrecognizedArg,
+ FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],
+ args),
+ OptionArgElement::eUnrecognizedArg));
+ }
+ }
+
+ // Finally we have to handle the case where the cursor index points at a
+ // single "-". We want to mark that in the option_element_vector, but only
+ // if it is not after the "--". But it turns out that OptionParser::Parse
+ // just ignores an isolated "-". So we have to look it up by hand here. We
+ // only care if it is AT the cursor position. Note, a single quoted dash is
+ // not the same as a single dash...
+
+ const Args::ArgEntry &cursor = args[cursor_index];
+ if ((static_cast<int32_t>(dash_dash_pos) == -1 ||
+ cursor_index < dash_dash_pos) &&
+ !cursor.IsQuoted() && cursor.ref == "-") {
+ option_element_vector.push_back(
+ OptionArgElement(OptionArgElement::eBareDash, cursor_index,
+ OptionArgElement::eBareDash));
+ }
+ return option_element_vector;
+}
+
+llvm::Expected<Args> Options::Parse(const Args &args,
+ ExecutionContext *execution_context,
+ lldb::PlatformSP platform_sp,
+ bool require_validation) {
+ Status error;
+ Option *long_options = GetLongOptions();
+ if (long_options == nullptr) {
+ return llvm::make_error<llvm::StringError>("Invalid long options.",
+ llvm::inconvertibleErrorCode());
+ }
+
+ std::string short_options = BuildShortOptions(long_options);
+ std::vector<char *> argv = GetArgvForParsing(args);
+ std::unique_lock<std::mutex> lock;
+ OptionParser::Prepare(lock);
+ int val;
+ while (true) {
+ int long_options_index = -1;
+ val = OptionParser::Parse(argv, short_options, long_options,
+ &long_options_index);
+
+ if (val == ':') {
+ error.SetErrorStringWithFormat("last option requires an argument");
+ break;
+ }
+
+ if (val == -1)
+ break;
+
+ // Did we get an error?
+ if (val == '?') {
+ error.SetErrorStringWithFormat("unknown or ambiguous option");
+ break;
+ }
+ // The option auto-set itself
+ if (val == 0)
+ continue;
+
+ OptionSeen(val);
+
+ // Lookup the long option index
+ if (long_options_index == -1) {
+ for (int i = 0; long_options[i].definition || long_options[i].flag ||
+ long_options[i].val;
+ ++i) {
+ if (long_options[i].val == val) {
+ long_options_index = i;
+ break;
+ }
+ }
+ }
+ // Call the callback with the option
+ if (long_options_index >= 0 &&
+ long_options[long_options_index].definition) {
+ const OptionDefinition *def = long_options[long_options_index].definition;
+
+ if (!platform_sp) {
+ // User did not pass in an explicit platform. Try to grab from the
+ // execution context.
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ platform_sp = target_sp ? target_sp->GetPlatform() : PlatformSP();
+ }
+ OptionValidator *validator = def->validator;
+
+ if (!platform_sp && require_validation) {
+ // Caller requires validation but we cannot validate as we don't have
+ // the mandatory platform against which to validate.
+ return llvm::make_error<llvm::StringError>(
+ "cannot validate options: no platform available",
+ llvm::inconvertibleErrorCode());
+ }
+
+ bool validation_failed = false;
+ if (platform_sp) {
+ // Ensure we have an execution context, empty or not.
+ ExecutionContext dummy_context;
+ ExecutionContext *exe_ctx_p =
+ execution_context ? execution_context : &dummy_context;
+ if (validator && !validator->IsValid(*platform_sp, *exe_ctx_p)) {
+ validation_failed = true;
+ error.SetErrorStringWithFormat("Option \"%s\" invalid. %s",
+ def->long_option,
+ def->validator->LongConditionString());
+ }
+ }
+
+ // As long as validation didn't fail, we set the option value.
+ if (!validation_failed)
+ error =
+ SetOptionValue(long_options_index,
+ (def->option_has_arg == OptionParser::eNoArgument)
+ ? nullptr
+ : OptionParser::GetOptionArgument(),
+ execution_context);
+ } else {
+ error.SetErrorStringWithFormat("invalid option with value '%i'", val);
+ }
+ }
+
+ if (error.Fail())
+ return error.ToError();
+
+ argv.pop_back();
+ argv.erase(argv.begin(), argv.begin() + OptionParser::GetOptionIndex());
+ return ReconstituteArgsAfterParsing(argv, args);
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/Property.cpp b/contrib/llvm-project/lldb/source/Interpreter/Property.cpp
new file mode 100644
index 000000000000..78209311e2e5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/Property.cpp
@@ -0,0 +1,299 @@
+//===-- Property.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/Property.h"
+
+#include "lldb/Core/UserSettingsController.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValues.h"
+#include "lldb/Target/Language.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+Property::Property(const PropertyDefinition &definition)
+ : m_name(definition.name), m_description(definition.description),
+ m_value_sp(), m_is_global(definition.global) {
+ switch (definition.type) {
+ case OptionValue::eTypeInvalid:
+ case OptionValue::eTypeProperties:
+ break;
+ case OptionValue::eTypeArch:
+ // "definition.default_uint_value" is not used
+ // "definition.default_cstr_value" as a string value that represents the
+ // default string value for the architecture/triple
+ m_value_sp =
+ std::make_shared<OptionValueArch>(definition.default_cstr_value);
+ break;
+
+ case OptionValue::eTypeArgs:
+ // "definition.default_uint_value" is always a OptionValue::Type
+ m_value_sp = std::make_shared<OptionValueArgs>();
+ break;
+
+ case OptionValue::eTypeArray:
+ // "definition.default_uint_value" is always a OptionValue::Type
+ m_value_sp =
+ std::make_shared<OptionValueArray>(OptionValue::ConvertTypeToMask(
+ (OptionValue::Type)definition.default_uint_value));
+ break;
+
+ case OptionValue::eTypeBoolean:
+ // "definition.default_uint_value" is the default boolean value if
+ // "definition.default_cstr_value" is NULL, otherwise interpret
+ // "definition.default_cstr_value" as a string value that represents the
+ // default value.
+ if (definition.default_cstr_value)
+ m_value_sp =
+ std::make_shared<OptionValueBoolean>(OptionArgParser::ToBoolean(
+ llvm::StringRef(definition.default_cstr_value), false, nullptr));
+ else
+ m_value_sp = std::make_shared<OptionValueBoolean>(
+ definition.default_uint_value != 0);
+ break;
+
+ case OptionValue::eTypeChar: {
+ llvm::StringRef s(definition.default_cstr_value ? definition.default_cstr_value : "");
+ m_value_sp = std::make_shared<OptionValueChar>(
+ OptionArgParser::ToChar(s, '\0', nullptr));
+ break;
+ }
+ case OptionValue::eTypeDictionary:
+ // "definition.default_uint_value" is always a OptionValue::Type
+ m_value_sp =
+ std::make_shared<OptionValueDictionary>(OptionValue::ConvertTypeToMask(
+ (OptionValue::Type)definition.default_uint_value));
+ break;
+
+ case OptionValue::eTypeEnum:
+ // "definition.default_uint_value" is the default enumeration value if
+ // "definition.default_cstr_value" is NULL, otherwise interpret
+ // "definition.default_cstr_value" as a string value that represents the
+ // default value.
+ {
+ OptionValueEnumeration *enum_value = new OptionValueEnumeration(
+ definition.enum_values, definition.default_uint_value);
+ m_value_sp.reset(enum_value);
+ if (definition.default_cstr_value) {
+ if (enum_value
+ ->SetValueFromString(
+ llvm::StringRef(definition.default_cstr_value))
+ .Success()) {
+ enum_value->SetDefaultValue(enum_value->GetCurrentValue());
+ // Call Clear() since we don't want the value to appear as having
+ // been set since we called SetValueFromString() above. Clear will
+ // set the current value to the default and clear the boolean that
+ // says that the value has been set.
+ enum_value->Clear();
+ }
+ }
+ }
+ break;
+
+ case OptionValue::eTypeFileSpec: {
+ // "definition.default_uint_value" represents if the
+ // "definition.default_cstr_value" should be resolved or not
+ const bool resolve = definition.default_uint_value != 0;
+ FileSpec file_spec = FileSpec(definition.default_cstr_value);
+ if (resolve)
+ FileSystem::Instance().Resolve(file_spec);
+ m_value_sp = std::make_shared<OptionValueFileSpec>(file_spec, resolve);
+ break;
+ }
+
+ case OptionValue::eTypeFileSpecList:
+ // "definition.default_uint_value" is not used for a
+ // OptionValue::eTypeFileSpecList
+ m_value_sp = std::make_shared<OptionValueFileSpecList>();
+ break;
+
+ case OptionValue::eTypeFormat:
+ // "definition.default_uint_value" is the default format enumeration value
+ // if "definition.default_cstr_value" is NULL, otherwise interpret
+ // "definition.default_cstr_value" as a string value that represents the
+ // default value.
+ {
+ Format new_format = eFormatInvalid;
+ if (definition.default_cstr_value)
+ OptionArgParser::ToFormat(definition.default_cstr_value, new_format,
+ nullptr);
+ else
+ new_format = (Format)definition.default_uint_value;
+ m_value_sp = std::make_shared<OptionValueFormat>(new_format);
+ }
+ break;
+
+ case OptionValue::eTypeLanguage:
+ // "definition.default_uint_value" is the default language enumeration
+ // value if "definition.default_cstr_value" is NULL, otherwise interpret
+ // "definition.default_cstr_value" as a string value that represents the
+ // default value.
+ {
+ LanguageType new_lang = eLanguageTypeUnknown;
+ if (definition.default_cstr_value)
+ Language::GetLanguageTypeFromString(
+ llvm::StringRef(definition.default_cstr_value));
+ else
+ new_lang = (LanguageType)definition.default_uint_value;
+ m_value_sp = std::make_shared<OptionValueLanguage>(new_lang);
+ }
+ break;
+
+ case OptionValue::eTypeFormatEntity:
+ // "definition.default_cstr_value" as a string value that represents the
+ // default
+ m_value_sp = std::make_shared<OptionValueFormatEntity>(
+ definition.default_cstr_value);
+ break;
+
+ case OptionValue::eTypePathMap:
+ // "definition.default_uint_value" tells us if notifications should occur
+ // for path mappings
+ m_value_sp = std::make_shared<OptionValuePathMappings>(
+ definition.default_uint_value != 0);
+ break;
+
+ case OptionValue::eTypeRegex:
+ // "definition.default_uint_value" is used to the regular expression flags
+ // "definition.default_cstr_value" the default regular expression value
+ // value.
+ m_value_sp =
+ std::make_shared<OptionValueRegex>(definition.default_cstr_value);
+ break;
+
+ case OptionValue::eTypeSInt64:
+ // "definition.default_uint_value" is the default integer value if
+ // "definition.default_cstr_value" is NULL, otherwise interpret
+ // "definition.default_cstr_value" as a string value that represents the
+ // default value.
+ m_value_sp = std::make_shared<OptionValueSInt64>(
+ definition.default_cstr_value
+ ? StringConvert::ToSInt64(definition.default_cstr_value)
+ : definition.default_uint_value);
+ break;
+
+ case OptionValue::eTypeUInt64:
+ // "definition.default_uint_value" is the default unsigned integer value if
+ // "definition.default_cstr_value" is NULL, otherwise interpret
+ // "definition.default_cstr_value" as a string value that represents the
+ // default value.
+ m_value_sp = std::make_shared<OptionValueUInt64>(
+ definition.default_cstr_value
+ ? StringConvert::ToUInt64(definition.default_cstr_value)
+ : definition.default_uint_value);
+ break;
+
+ case OptionValue::eTypeUUID:
+ // "definition.default_uint_value" is not used for a OptionValue::eTypeUUID
+ // "definition.default_cstr_value" can contain a default UUID value
+ {
+ UUID uuid;
+ if (definition.default_cstr_value)
+ uuid.SetFromStringRef(definition.default_cstr_value);
+ m_value_sp = std::make_shared<OptionValueUUID>(uuid);
+ }
+ break;
+
+ case OptionValue::eTypeString:
+ // "definition.default_uint_value" can contain the string option flags
+ // OR'ed together "definition.default_cstr_value" can contain a default
+ // string value
+ {
+ OptionValueString *string_value =
+ new OptionValueString(definition.default_cstr_value);
+ if (definition.default_uint_value != 0)
+ string_value->GetOptions().Reset(definition.default_uint_value);
+ m_value_sp.reset(string_value);
+ }
+ break;
+ }
+}
+
+Property::Property(ConstString name, ConstString desc,
+ bool is_global, const lldb::OptionValueSP &value_sp)
+ : m_name(name), m_description(desc), m_value_sp(value_sp),
+ m_is_global(is_global) {}
+
+bool Property::DumpQualifiedName(Stream &strm) const {
+ if (m_name) {
+ if (m_value_sp->DumpQualifiedName(strm))
+ strm.PutChar('.');
+ strm << m_name;
+ return true;
+ }
+ return false;
+}
+
+void Property::Dump(const ExecutionContext *exe_ctx, Stream &strm,
+ uint32_t dump_mask) const {
+ if (m_value_sp) {
+ const bool dump_desc = dump_mask & OptionValue::eDumpOptionDescription;
+ const bool dump_cmd = dump_mask & OptionValue::eDumpOptionCommand;
+ const bool transparent = m_value_sp->ValueIsTransparent();
+ if (dump_cmd && !transparent)
+ strm << "settings set -f ";
+ if (dump_desc || !transparent) {
+ if ((dump_mask & OptionValue::eDumpOptionName) && m_name) {
+ DumpQualifiedName(strm);
+ if (dump_mask & ~OptionValue::eDumpOptionName)
+ strm.PutChar(' ');
+ }
+ }
+ if (dump_desc) {
+ llvm::StringRef desc = GetDescription();
+ if (!desc.empty())
+ strm << "-- " << desc;
+
+ if (transparent && (dump_mask == (OptionValue::eDumpOptionName |
+ OptionValue::eDumpOptionDescription)))
+ strm.EOL();
+ }
+ m_value_sp->DumpValue(exe_ctx, strm, dump_mask);
+ }
+}
+
+void Property::DumpDescription(CommandInterpreter &interpreter, Stream &strm,
+ uint32_t output_width,
+ bool display_qualified_name) const {
+ if (!m_value_sp)
+ return;
+ llvm::StringRef desc = GetDescription();
+
+ if (desc.empty())
+ return;
+
+ StreamString qualified_name;
+ const OptionValueProperties *sub_properties = m_value_sp->GetAsProperties();
+ if (sub_properties) {
+ strm.EOL();
+
+ if (m_value_sp->DumpQualifiedName(qualified_name))
+ strm.Printf("'%s' variables:\n\n", qualified_name.GetData());
+ sub_properties->DumpAllDescriptions(interpreter, strm);
+ } else {
+ if (display_qualified_name) {
+ StreamString qualified_name;
+ DumpQualifiedName(qualified_name);
+ interpreter.OutputFormattedHelpText(strm, qualified_name.GetString(),
+ "--", desc, output_width);
+ } else {
+ interpreter.OutputFormattedHelpText(strm, m_name.GetStringRef(), "--",
+ desc, output_width);
+ }
+ }
+}
+
+void Property::SetValueChangedCallback(OptionValueChangedCallback callback,
+ void *baton) {
+ if (m_value_sp)
+ m_value_sp->SetValueChangedCallback(callback, baton);
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/ScriptInterpreter.cpp b/contrib/llvm-project/lldb/source/Interpreter/ScriptInterpreter.cpp
new file mode 100644
index 000000000000..d04baec76e60
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -0,0 +1,96 @@
+//===-- ScriptInterpreter.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/ScriptInterpreter.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+#include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ScriptInterpreter::ScriptInterpreter(Debugger &debugger,
+ lldb::ScriptLanguage script_lang)
+ : m_debugger(debugger), m_script_lang(script_lang) {}
+
+ScriptInterpreter::~ScriptInterpreter() {}
+
+void ScriptInterpreter::CollectDataForBreakpointCommandCallback(
+ std::vector<BreakpointOptions *> &bp_options_vec,
+ CommandReturnObject &result) {
+ result.SetStatus(eReturnStatusFailed);
+ result.AppendError(
+ "ScriptInterpreter::GetScriptCommands(StringList &) is not implemented.");
+}
+
+void ScriptInterpreter::CollectDataForWatchpointCommandCallback(
+ WatchpointOptions *bp_options, CommandReturnObject &result) {
+ result.SetStatus(eReturnStatusFailed);
+ result.AppendError(
+ "ScriptInterpreter::GetScriptCommands(StringList &) is not implemented.");
+}
+
+std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {
+ std::string return_value;
+
+ switch (language) {
+ case eScriptLanguageNone:
+ return_value = "None";
+ break;
+ case eScriptLanguagePython:
+ return_value = "Python";
+ break;
+ case eScriptLanguageUnknown:
+ return_value = "Unknown";
+ break;
+ }
+
+ return return_value;
+}
+
+lldb::ScriptLanguage
+ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
+ if (language.equals_lower(LanguageToString(eScriptLanguageNone)))
+ return eScriptLanguageNone;
+ if (language.equals_lower(LanguageToString(eScriptLanguagePython)))
+ return eScriptLanguagePython;
+ return eScriptLanguageUnknown;
+}
+
+Status ScriptInterpreter::SetBreakpointCommandCallback(
+ std::vector<BreakpointOptions *> &bp_options_vec,
+ const char *callback_text) {
+ Status return_error;
+ for (BreakpointOptions *bp_options : bp_options_vec) {
+ return_error = SetBreakpointCommandCallback(bp_options, callback_text);
+ if (return_error.Success())
+ break;
+ }
+ return return_error;
+}
+
+void ScriptInterpreter::SetBreakpointCommandCallbackFunction(
+ std::vector<BreakpointOptions *> &bp_options_vec,
+ const char *function_name) {
+ for (BreakpointOptions *bp_options : bp_options_vec) {
+ SetBreakpointCommandCallbackFunction(bp_options, function_name);
+ }
+}
+
+std::unique_ptr<ScriptInterpreterLocker>
+ScriptInterpreter::AcquireInterpreterLock() {
+ return std::unique_ptr<ScriptInterpreterLocker>(
+ new ScriptInterpreterLocker());
+}
diff --git a/contrib/llvm-project/lldb/source/Interpreter/embedded_interpreter.py b/contrib/llvm-project/lldb/source/Interpreter/embedded_interpreter.py
new file mode 100644
index 000000000000..8a1195d83c6f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Interpreter/embedded_interpreter.py
@@ -0,0 +1,134 @@
+import sys
+if sys.version_info[0] < 3:
+ import __builtin__ as builtins
+else:
+ import builtins
+import code
+import lldb
+import traceback
+
+try:
+ import readline
+ import rlcompleter
+except ImportError:
+ have_readline = False
+except AttributeError:
+ # This exception gets hit by the rlcompleter when Linux is using
+ # the readline suppression import.
+ have_readline = False
+else:
+ have_readline = True
+ if 'libedit' in readline.__doc__:
+ readline.parse_and_bind('bind ^I rl_complete')
+ else:
+ readline.parse_and_bind('tab: complete')
+
+g_builtin_override_called = False
+
+
+class LLDBQuitter(object):
+
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ self()
+
+ def __call__(self, code=None):
+ global g_builtin_override_called
+ g_builtin_override_called = True
+ raise SystemExit(-1)
+
+
+def setquit():
+ '''Redefine builtin functions 'quit()' and 'exit()' to print a message and raise an EOFError exception.'''
+ # This function will be called prior to each interactive
+ # interpreter loop or each single line, so we set the global
+ # g_builtin_override_called to False so we know if a SystemExit
+ # is thrown, we can catch it and tell the difference between
+ # a call to "quit()" or "exit()" and something like
+ # "sys.exit(123)"
+ global g_builtin_override_called
+ g_builtin_override_called = False
+ builtins.quit = LLDBQuitter('quit')
+ builtins.exit = LLDBQuitter('exit')
+
+# When running one line, we might place the string to run in this string
+# in case it would be hard to correctly escape a string's contents
+
+g_run_one_line_str = None
+
+
+def get_terminal_size(fd):
+ try:
+ import fcntl
+ import termios
+ import struct
+ hw = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
+ except:
+ hw = (0, 0)
+ return hw
+
+
+def readfunc_stdio(prompt):
+ sys.stdout.write(prompt)
+ sys.stdout.flush()
+ return sys.stdin.readline().rstrip()
+
+
+def run_python_interpreter(local_dict):
+ # Pass in the dictionary, for continuity from one session to the next.
+ setquit()
+ try:
+ fd = sys.stdin.fileno()
+ interacted = False
+ if get_terminal_size(fd)[1] == 0:
+ try:
+ import termios
+ old = termios.tcgetattr(fd)
+ if old[3] & termios.ECHO:
+ # Need to turn off echoing and restore
+ new = termios.tcgetattr(fd)
+ new[3] = new[3] & ~termios.ECHO
+ try:
+ termios.tcsetattr(fd, termios.TCSADRAIN, new)
+ interacted = True
+ code.interact(
+ banner="Python Interactive Interpreter. To exit, type 'quit()', 'exit()'.",
+ readfunc=readfunc_stdio,
+ local=local_dict)
+ finally:
+ termios.tcsetattr(fd, termios.TCSADRAIN, old)
+ except:
+ pass
+ # Don't need to turn off echoing
+ if not interacted:
+ code.interact(
+ banner="Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.",
+ readfunc=readfunc_stdio,
+ local=local_dict)
+ else:
+ # We have a real interactive terminal
+ code.interact(
+ banner="Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.",
+ local=local_dict)
+ except SystemExit as e:
+ global g_builtin_override_called
+ if not g_builtin_override_called:
+ print('Script exited with %s' % (e))
+
+
+def run_one_line(local_dict, input_string):
+ global g_run_one_line_str
+ setquit()
+ try:
+ repl = code.InteractiveConsole(local_dict)
+ if input_string:
+ repl.runsource(input_string)
+ elif g_run_one_line_str:
+ repl.runsource(g_run_one_line_str)
+
+ except SystemExit as e:
+ global g_builtin_override_called
+ if not g_builtin_override_called:
+ print('Script exited with %s' % (e))
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp
new file mode 100644
index 000000000000..362a80be4b0d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp
@@ -0,0 +1,2052 @@
+//===-- ABIMacOSX_arm.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABIMacOSX_arm.h"
+
+#include <vector>
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "Utility/ARM_DWARF_Registers.h"
+#include "Utility/ARM_ehframe_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME
+ // DWARF GENERIC PROCESS PLUGIN
+ // LLDB NATIVE
+ // ========== ======= == === ============= ============
+ // ======================= =================== ===========================
+ // ======================= ======================
+ {"r0",
+ "arg1",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ "arg2",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ "arg3",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ "arg4",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "r13",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r14",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "r15",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s0",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpscr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d29",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d30",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d31",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r8_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r9_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r10_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r11_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r12_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_usr",
+ "sp_usr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_usr",
+ "lr_usr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r8_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r9_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r10_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r11_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r12_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_fiq",
+ "sp_fiq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_fiq",
+ "lr_fiq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_irq",
+ "sp_irq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_irq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_irq",
+ "lr_irq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_irq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_abt",
+ "sp_abt",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_abt, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_abt",
+ "lr_abt",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_abt, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_und",
+ "sp_und",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_und, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_und",
+ "lr_und",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_und, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_svc",
+ "sp_svc",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_svc, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_svc",
+ "lr_svc",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_svc, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABIMacOSX_arm::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABIMacOSX_arm::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABIMacOSX_arm::CreateInstance(ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ const llvm::Triple::VendorType vendor_type = arch.GetTriple().getVendor();
+
+ if (vendor_type == llvm::Triple::Apple) {
+ if ((arch_type == llvm::Triple::arm) ||
+ (arch_type == llvm::Triple::thumb)) {
+ return ABISP(new ABIMacOSX_arm(process_sp));
+ }
+ }
+
+ return ABISP();
+}
+
+bool ABIMacOSX_arm::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t function_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const uint32_t ra_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+
+ RegisterValue reg_value;
+
+ const char *reg_names[] = {"r0", "r1", "r2", "r3"};
+
+ llvm::ArrayRef<addr_t>::iterator ai = args.begin(), ae = args.end();
+
+ for (size_t i = 0; i < llvm::array_lengthof(reg_names); ++i) {
+ if (ai == ae)
+ break;
+
+ reg_value.SetUInt32(*ai);
+ if (!reg_ctx->WriteRegister(reg_ctx->GetRegisterInfoByName(reg_names[i]),
+ reg_value))
+ return false;
+
+ ++ai;
+ }
+
+ if (ai != ae) {
+ // Spill onto the stack
+ size_t num_stack_regs = ae - ai;
+
+ sp -= (num_stack_regs * 4);
+ // Keep the stack 16 byte aligned
+ sp &= ~(16ull - 1ull);
+
+ // just using arg1 to get the right size
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+
+ addr_t arg_pos = sp;
+
+ for (; ai != ae; ++ai) {
+ reg_value.SetUInt32(*ai);
+ if (reg_ctx
+ ->WriteRegisterValueToMemory(reg_info, arg_pos,
+ reg_info->byte_size, reg_value)
+ .Fail())
+ return false;
+ arg_pos += reg_info->byte_size;
+ }
+ }
+
+ TargetSP target_sp(thread.CalculateTarget());
+ Address so_addr;
+
+ // Figure out if our return address is ARM or Thumb by using the
+ // Address::GetCallableLoadAddress(Target*) which will figure out the ARM
+ // thumb-ness and set the correct address bits for us.
+ so_addr.SetLoadAddress(return_addr, target_sp.get());
+ return_addr = so_addr.GetCallableLoadAddress(target_sp.get());
+
+ // Set "lr" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(ra_reg_num, return_addr))
+ return false;
+
+ // If bit zero or 1 is set, this must be a thumb function, no need to figure
+ // this out from the symbols.
+ so_addr.SetLoadAddress(function_addr, target_sp.get());
+ function_addr = so_addr.GetCallableLoadAddress(target_sp.get());
+
+ const RegisterInfo *cpsr_reg_info = reg_ctx->GetRegisterInfoByName("cpsr");
+ const uint32_t curr_cpsr = reg_ctx->ReadRegisterAsUnsigned(cpsr_reg_info, 0);
+
+ // Make a new CPSR and mask out any Thumb IT (if/then) bits
+ uint32_t new_cpsr = curr_cpsr & ~MASK_CPSR_IT_MASK;
+ // If bit zero or 1 is set, this must be thumb...
+ if (function_addr & 1ull)
+ new_cpsr |= MASK_CPSR_T; // Set T bit in CPSR
+ else
+ new_cpsr &= ~MASK_CPSR_T; // Clear T bit in CPSR
+
+ if (new_cpsr != curr_cpsr) {
+ if (!reg_ctx->WriteRegisterFromUnsigned(cpsr_reg_info, new_cpsr))
+ return false;
+ }
+
+ function_addr &=
+ ~1ull; // clear bit zero since the CPSR will take care of the mode for us
+
+ // Update the sp - stack pointer - to be aligned to 16-bytes
+ sp &= ~(0xfull);
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp))
+ return false;
+
+ // Set "pc" to the address requested
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, function_addr))
+ return false;
+
+ return true;
+}
+
+bool ABIMacOSX_arm::GetArgumentValues(Thread &thread, ValueList &values) const {
+ uint32_t num_values = values.GetSize();
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ // For now, assume that the types in the AST values come from the Target's
+ // scratch AST.
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ addr_t sp = 0;
+
+ for (uint32_t value_idx = 0; value_idx < num_values; ++value_idx) {
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ Value *value = values.GetValueAtIndex(value_idx);
+
+ if (!value)
+ return false;
+
+ CompilerType compiler_type = value->GetCompilerType();
+ if (compiler_type) {
+ bool is_signed = false;
+ size_t bit_width = 0;
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed))
+ bit_width = *bit_size;
+ else if (compiler_type.IsPointerOrReferenceType())
+ bit_width = *bit_size;
+ else
+ // We only handle integer, pointer and reference types currently...
+ return false;
+
+ if (bit_width <= (exe_ctx.GetProcessRef().GetAddressByteSize() * 8)) {
+ if (value_idx < 4) {
+ // Arguments 1-4 are in r0-r3...
+ const RegisterInfo *arg_reg_info = nullptr;
+ // Search by generic ID first, then fall back to by name
+ uint32_t arg_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + value_idx);
+ if (arg_reg_num != LLDB_INVALID_REGNUM) {
+ arg_reg_info = reg_ctx->GetRegisterInfoAtIndex(arg_reg_num);
+ } else {
+ switch (value_idx) {
+ case 0:
+ arg_reg_info = reg_ctx->GetRegisterInfoByName("r0");
+ break;
+ case 1:
+ arg_reg_info = reg_ctx->GetRegisterInfoByName("r1");
+ break;
+ case 2:
+ arg_reg_info = reg_ctx->GetRegisterInfoByName("r2");
+ break;
+ case 3:
+ arg_reg_info = reg_ctx->GetRegisterInfoByName("r3");
+ break;
+ }
+ }
+
+ if (arg_reg_info) {
+ RegisterValue reg_value;
+
+ if (reg_ctx->ReadRegister(arg_reg_info, reg_value)) {
+ if (is_signed)
+ reg_value.SignExtend(bit_width);
+ if (!reg_value.GetScalarValue(value->GetScalar()))
+ return false;
+ continue;
+ }
+ }
+ return false;
+ } else {
+ if (sp == 0) {
+ // Read the stack pointer if it already hasn't been read
+ sp = reg_ctx->GetSP(0);
+ if (sp == 0)
+ return false;
+ }
+
+ // Arguments 5 on up are on the stack
+ const uint32_t arg_byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory(
+ sp, arg_byte_size, is_signed, value->GetScalar(), error))
+ return false;
+
+ sp += arg_byte_size;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool ABIMacOSX_arm::IsArmv7kProcess() const {
+ bool is_armv7k = false;
+ ProcessSP process_sp(GetProcessSP());
+ if (process_sp) {
+ const ArchSpec &arch(process_sp->GetTarget().GetArchitecture());
+ const ArchSpec::Core system_core = arch.GetCore();
+ if (system_core == ArchSpec::eCore_arm_armv7k) {
+ is_armv7k = true;
+ }
+ }
+ return is_armv7k;
+}
+
+ValueObjectSP ABIMacOSX_arm::GetReturnValueObjectImpl(
+ Thread &thread, lldb_private::CompilerType &compiler_type) const {
+ Value value;
+ ValueObjectSP return_valobj_sp;
+
+ if (!compiler_type)
+ return return_valobj_sp;
+
+ value.SetCompilerType(compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ bool is_signed;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ const RegisterInfo *r0_reg_info = reg_ctx->GetRegisterInfoByName("r0", 0);
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ llvm::Optional<uint64_t> bit_width = compiler_type.GetBitSize(&thread);
+ if (!bit_width)
+ return return_valobj_sp;
+
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 128:
+ if (IsArmv7kProcess()) {
+ // "A composite type not larger than 16 bytes is returned in r0-r3. The
+ // format is as if the result had been stored in memory at a word-
+ // aligned address and then loaded into r0-r3 with an ldm instruction"
+ {
+ const RegisterInfo *r1_reg_info =
+ reg_ctx->GetRegisterInfoByName("r1", 0);
+ const RegisterInfo *r2_reg_info =
+ reg_ctx->GetRegisterInfoByName("r2", 0);
+ const RegisterInfo *r3_reg_info =
+ reg_ctx->GetRegisterInfoByName("r3", 0);
+ if (r1_reg_info && r2_reg_info && r3_reg_info) {
+ llvm::Optional<uint64_t> byte_size =
+ compiler_type.GetByteSize(&thread);
+ if (!byte_size)
+ return return_valobj_sp;
+ ProcessSP process_sp(thread.GetProcess());
+ if (*byte_size <= r0_reg_info->byte_size + r1_reg_info->byte_size +
+ r2_reg_info->byte_size +
+ r3_reg_info->byte_size &&
+ process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue r0_reg_value;
+ RegisterValue r1_reg_value;
+ RegisterValue r2_reg_value;
+ RegisterValue r3_reg_value;
+ if (reg_ctx->ReadRegister(r0_reg_info, r0_reg_value) &&
+ reg_ctx->ReadRegister(r1_reg_info, r1_reg_value) &&
+ reg_ctx->ReadRegister(r2_reg_info, r2_reg_value) &&
+ reg_ctx->ReadRegister(r3_reg_info, r3_reg_value)) {
+ Status error;
+ if (r0_reg_value.GetAsMemoryData(r0_reg_info,
+ heap_data_up->GetBytes() + 0,
+ 4, byte_order, error) &&
+ r1_reg_value.GetAsMemoryData(r1_reg_info,
+ heap_data_up->GetBytes() + 4,
+ 4, byte_order, error) &&
+ r2_reg_value.GetAsMemoryData(r2_reg_info,
+ heap_data_up->GetBytes() + 8,
+ 4, byte_order, error) &&
+ r3_reg_value.GetAsMemoryData(r3_reg_info,
+ heap_data_up->GetBytes() + 12,
+ 4, byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetAddressByteSize());
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, compiler_type, ConstString(""), data);
+ return return_valobj_sp;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ return return_valobj_sp;
+ }
+ break;
+ case 64: {
+ const RegisterInfo *r1_reg_info = reg_ctx->GetRegisterInfoByName("r1", 0);
+ uint64_t raw_value;
+ raw_value = reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r1_reg_info, 0) &
+ UINT32_MAX))
+ << 32;
+ if (is_signed)
+ value.GetScalar() = (int64_t)raw_value;
+ else
+ value.GetScalar() = (uint64_t)raw_value;
+ } break;
+ case 32:
+ if (is_signed)
+ value.GetScalar() = (int32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX);
+ break;
+ case 16:
+ if (is_signed)
+ value.GetScalar() = (int16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX);
+ break;
+ case 8:
+ if (is_signed)
+ value.GetScalar() = (int8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX);
+ break;
+ }
+ } else if (compiler_type.IsPointerType()) {
+ uint32_t ptr =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(r0_reg_info, 0) &
+ UINT32_MAX;
+ value.GetScalar() = ptr;
+ } else {
+ // not handled yet
+ return return_valobj_sp;
+ }
+
+ // If we get here, we have a valid Value, so make our ValueObject out of it:
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ return return_valobj_sp;
+}
+
+Status ABIMacOSX_arm::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("r0", 0);
+ if (num_bytes <= 4) {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r0_info, raw_value))
+ set_it_simple = true;
+ } else {
+ uint32_t raw_value = data.GetMaxU32(&offset, 4);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r0_info, raw_value)) {
+ const RegisterInfo *r1_info = reg_ctx->GetRegisterInfoByName("r1", 0);
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r1_info, raw_value))
+ set_it_simple = true;
+ }
+ }
+ } else if (num_bytes <= 16 && IsArmv7kProcess()) {
+ // "A composite type not larger than 16 bytes is returned in r0-r3. The
+ // format is as if the result had been stored in memory at a word-aligned
+ // address and then loaded into r0-r3 with an ldm instruction"
+
+ const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("r0", 0);
+ const RegisterInfo *r1_info = reg_ctx->GetRegisterInfoByName("r1", 0);
+ const RegisterInfo *r2_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+ const RegisterInfo *r3_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+ lldb::offset_t offset = 0;
+ uint32_t bytes_written = 4;
+ uint32_t raw_value = data.GetMaxU64(&offset, 4);
+ if (reg_ctx->WriteRegisterFromUnsigned(r0_info, raw_value) &&
+ bytes_written <= num_bytes) {
+ bytes_written += 4;
+ raw_value = data.GetMaxU64(&offset, 4);
+ if (bytes_written <= num_bytes &&
+ reg_ctx->WriteRegisterFromUnsigned(r1_info, raw_value)) {
+ bytes_written += 4;
+ raw_value = data.GetMaxU64(&offset, 4);
+ if (bytes_written <= num_bytes &&
+ reg_ctx->WriteRegisterFromUnsigned(r2_info, raw_value)) {
+ bytes_written += 4;
+ raw_value = data.GetMaxU64(&offset, 4);
+ if (bytes_written <= num_bytes &&
+ reg_ctx->WriteRegisterFromUnsigned(r3_info, raw_value)) {
+ set_it_simple = true;
+ }
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else
+ error.SetErrorString(
+ "We don't support returning float values at present");
+ }
+
+ if (!set_it_simple)
+ error.SetErrorString(
+ "We only support setting simple integer return types at present.");
+
+ return error;
+}
+
+bool ABIMacOSX_arm::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num = dwarf_lr;
+ uint32_t sp_reg_num = dwarf_sp;
+ uint32_t pc_reg_num = dwarf_pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ // The previous PC is in the LR
+ row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("arm at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABIMacOSX_arm::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num =
+ dwarf_r7; // apple uses r7 for all frames. Normal arm uses r11
+ uint32_t pc_reg_num = dwarf_pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 4;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("arm-apple-ios default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+
+ return true;
+}
+
+// cf. "ARMv6 Function Calling Conventions"
+// https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARMv6FunctionCallingConventions.html
+// and "ARMv7 Function Calling Conventions"
+// https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARMv7FunctionCallingConventions.html
+
+// ARMv7 on iOS general purpose reg rules:
+// r0-r3 not preserved (used for argument passing)
+// r4-r6 preserved
+// r7 preserved (frame pointer)
+// r8 preserved
+// r9 not preserved (usable as volatile scratch register with iOS 3.x and
+// later)
+// r10-r11 preserved
+// r12 not presrved
+// r13 preserved (stack pointer)
+// r14 not preserved (link register)
+// r15 preserved (pc)
+// cpsr not preserved (different rules for different bits)
+
+// ARMv7 on iOS floating point rules:
+// d0-d7 not preserved (aka s0-s15, q0-q3)
+// d8-d15 preserved (aka s16-s31, q4-q7)
+// d16-d31 not preserved (aka q8-q15)
+
+bool ABIMacOSX_arm::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Volatile registers are: r0, r1, r2, r3, r9, r12, r13 (aka sp)
+ const char *name = reg_info->name;
+ if (name[0] == 'r') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // r0
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // r1
+ case '2':
+ case '3':
+ return name[3] == '\0'; // r12, r13 (sp)
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ return name[2] == '\0'; // r2
+ case '3':
+ return name[2] == '\0'; // r3
+ case '9':
+ return name[2] == '\0'; // r9 (apple-ios only...)
+
+ break;
+ }
+ } else if (name[0] == 'd') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // d0 is volatile
+
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // d1 is volatile
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[3] == '\0'; // d16 - d19 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ switch (name[2]) {
+ case '\0':
+ return true; // d2 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[3] == '\0'; // d20 - d29 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '3':
+ switch (name[2]) {
+ case '\0':
+ return true; // d3 is volatile
+ case '0':
+ case '1':
+ return name[3] == '\0'; // d30 - d31 are volatile
+ default:
+ break;
+ }
+ break;
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ return name[2] == '\0'; // d4 - d7 are volatile
+
+ default:
+ break;
+ }
+ } else if (name[0] == 's') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // s0 is volatile
+
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // s1 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return name[3] == '\0'; // s10 - s15 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[2] == '\0'; // s2 - s9 are volatile
+
+ default:
+ break;
+ }
+ } else if (name[0] == 'q') {
+ switch (name[1]) {
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // q1 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return true; // q10-q15 are volatile
+ default:
+ break;
+ };
+ break;
+ case '0':
+ case '2':
+ case '3':
+ return name[2] == '\0'; // q0-q3 are volatile
+ case '8':
+ case '9':
+ return name[2] == '\0'; // q8-q9 are volatile
+ default:
+ break;
+ }
+ } else if (name[0] == 's' && name[1] == 'p' && name[2] == '\0')
+ return true;
+ }
+ return false;
+}
+
+void ABIMacOSX_arm::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "Mac OS X ABI for arm targets", CreateInstance);
+}
+
+void ABIMacOSX_arm::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABIMacOSX_arm::GetPluginNameStatic() {
+ static ConstString g_name("macosx-arm");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABIMacOSX_arm::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABIMacOSX_arm::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h
new file mode 100644
index 000000000000..ac9ba00b9d91
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h
@@ -0,0 +1,93 @@
+//===-- ABIMacOSX_arm.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABIMacOSX_arm_h_
+#define liblldb_ABIMacOSX_arm_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABIMacOSX_arm : public lldb_private::ABI {
+public:
+ ~ABIMacOSX_arm() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t func_addr, lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are are 4 byte aligned
+ if (cfa & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Just make sure the address is a valid 32 bit address. Bit zero
+ // might be set due to Thumb function calls, so don't enforce 2 byte
+ // alignment
+ return pc <= UINT32_MAX;
+ }
+
+ lldb::addr_t FixCodeAddress(lldb::addr_t pc) override {
+ // ARM uses bit zero to signify a code address is thumb, so we must
+ // strip bit zero in any code addresses.
+ return pc & ~(lldb::addr_t)1;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ bool IsArmv7kProcess() const;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const override;
+
+private:
+ ABIMacOSX_arm(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABIMacOSX_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp
new file mode 100644
index 000000000000..368e37213249
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp
@@ -0,0 +1,2447 @@
+//===-- ABIMacOSX_arm64.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABIMacOSX_arm64.h"
+
+#include <vector>
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+#include "Utility/ARM64_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static const char *pluginDesc = "Mac OS X ABI for arm64 targets";
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT
+ // EH_FRAME DWARF GENERIC
+ // PROCESS PLUGIN LLDB NATIVE
+ // ========== ======= == === ============= ===================
+ // =================== ====================== ===========================
+ // ======================= ======================
+ {"x0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x0, LLDB_REGNUM_GENERIC_ARG1,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x1, LLDB_REGNUM_GENERIC_ARG2,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x2, LLDB_REGNUM_GENERIC_ARG3,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x3, LLDB_REGNUM_GENERIC_ARG4,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x4, LLDB_REGNUM_GENERIC_ARG5,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x5, LLDB_REGNUM_GENERIC_ARG6,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x6, LLDB_REGNUM_GENERIC_ARG7,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x7, LLDB_REGNUM_GENERIC_ARG8,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x9, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x10, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x16, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x17, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x18, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x19, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x20, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x21, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x22, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x23, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x24, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x25, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x26, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x27, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x28, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fp",
+ "x29",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x29, LLDB_REGNUM_GENERIC_FP,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "x30",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x30, LLDB_REGNUM_GENERIC_RA,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "x31",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x31, LLDB_REGNUM_GENERIC_SP,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::pc, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::cpsr, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"v0",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v0, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v1",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v2",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v3",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v4",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v5",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v5, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v6",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v6, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v7",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v7, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v8",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v9",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v9, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v10",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v10, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v11",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v12",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v13",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v14",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v15",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v16",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v16, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v17",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v17, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v18",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v18, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v19",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v19, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v20",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v20, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v21",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v21, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v22",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v22, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v23",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v23, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v24",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v24, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v25",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v25, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v26",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v26, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v27",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v27, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v28",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v28, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v29",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v29, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v30",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v30, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v31",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v31, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"fpsr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpcr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"s0",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"d0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d29",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d30",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d31",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABIMacOSX_arm64::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABIMacOSX_arm64::GetRedZoneSize() const { return 128; }
+
+// Static Functions
+
+ABISP
+ABIMacOSX_arm64::CreateInstance(ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ const llvm::Triple::VendorType vendor_type = arch.GetTriple().getVendor();
+
+ if (vendor_type == llvm::Triple::Apple) {
+ if (arch_type == llvm::Triple::aarch64) {
+ return ABISP(new ABIMacOSX_arm64(process_sp));
+ }
+ }
+
+ return ABISP();
+}
+
+bool ABIMacOSX_arm64::PrepareTrivialCall(
+ Thread &thread, lldb::addr_t sp, lldb::addr_t func_addr,
+ lldb::addr_t return_addr, llvm::ArrayRef<lldb::addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_x86_64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%d = 0x%" PRIx64, static_cast<int>(i + 1), args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ const uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const uint32_t ra_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+
+ // x0 - x7 contain first 8 simple args
+ if (args.size() > 8) // TODO handle more than 6 arguments
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%d (0x%" PRIx64 ") into %s",
+ static_cast<int>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // Set "lr" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfoAtIndex(ra_reg_num), return_addr))
+ return false;
+
+ // Set "sp" to the requested value
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfoAtIndex(sp_reg_num), sp))
+ return false;
+
+ // Set "pc" to the address requested
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfoAtIndex(pc_reg_num), func_addr))
+ return false;
+
+ return true;
+}
+
+bool ABIMacOSX_arm64::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ uint32_t num_values = values.GetSize();
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ addr_t sp = 0;
+
+ for (uint32_t value_idx = 0; value_idx < num_values; ++value_idx) {
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ Value *value = values.GetValueAtIndex(value_idx);
+
+ if (!value)
+ return false;
+
+ CompilerType value_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = value_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+
+ bool is_signed = false;
+ size_t bit_width = 0;
+ if (value_type.IsIntegerOrEnumerationType(is_signed)) {
+ bit_width = *bit_size;
+ } else if (value_type.IsPointerOrReferenceType()) {
+ bit_width = *bit_size;
+ } else {
+ // We only handle integer, pointer and reference types currently...
+ return false;
+ }
+
+ if (bit_width <= (exe_ctx.GetProcessRef().GetAddressByteSize() * 8)) {
+ if (value_idx < 8) {
+ // Arguments 1-6 are in x0-x5...
+ const RegisterInfo *reg_info = nullptr;
+ // Search by generic ID first, then fall back to by name
+ uint32_t arg_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + value_idx);
+ if (arg_reg_num != LLDB_INVALID_REGNUM) {
+ reg_info = reg_ctx->GetRegisterInfoAtIndex(arg_reg_num);
+ } else {
+ switch (value_idx) {
+ case 0:
+ reg_info = reg_ctx->GetRegisterInfoByName("x0");
+ break;
+ case 1:
+ reg_info = reg_ctx->GetRegisterInfoByName("x1");
+ break;
+ case 2:
+ reg_info = reg_ctx->GetRegisterInfoByName("x2");
+ break;
+ case 3:
+ reg_info = reg_ctx->GetRegisterInfoByName("x3");
+ break;
+ case 4:
+ reg_info = reg_ctx->GetRegisterInfoByName("x4");
+ break;
+ case 5:
+ reg_info = reg_ctx->GetRegisterInfoByName("x5");
+ break;
+ case 6:
+ reg_info = reg_ctx->GetRegisterInfoByName("x6");
+ break;
+ case 7:
+ reg_info = reg_ctx->GetRegisterInfoByName("x7");
+ break;
+ }
+ }
+
+ if (reg_info) {
+ RegisterValue reg_value;
+
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ if (is_signed)
+ reg_value.SignExtend(bit_width);
+ if (!reg_value.GetScalarValue(value->GetScalar()))
+ return false;
+ continue;
+ }
+ }
+ return false;
+ } else {
+ if (sp == 0) {
+ // Read the stack pointer if we already haven't read it
+ sp = reg_ctx->GetSP(0);
+ if (sp == 0)
+ return false;
+ }
+
+ // Arguments 5 on up are on the stack
+ const uint32_t arg_byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory(
+ sp, arg_byte_size, is_signed, value->GetScalar(), error))
+ return false;
+
+ sp += arg_byte_size;
+ // Align up to the next 8 byte boundary if needed
+ if (sp % 8) {
+ sp >>= 3;
+ sp += 1;
+ sp <<= 3;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+Status
+ABIMacOSX_arm64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType return_value_type = new_value_sp->GetCompilerType();
+ if (!return_value_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ if (reg_ctx) {
+ DataExtractor data;
+ Status data_error;
+ const uint64_t byte_size = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ const uint32_t type_flags = return_value_type.GetTypeInfo(nullptr);
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ // Extract the register context so we can read arguments from registers
+ lldb::offset_t offset = 0;
+ if (byte_size <= 16) {
+ const RegisterInfo *x0_info = reg_ctx->GetRegisterInfoByName("x0", 0);
+ if (byte_size <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, byte_size);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(x0_info, raw_value))
+ error.SetErrorString("failed to write register x0");
+ } else {
+ uint64_t raw_value = data.GetMaxU64(&offset, 8);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(x0_info, raw_value)) {
+ const RegisterInfo *x1_info =
+ reg_ctx->GetRegisterInfoByName("x1", 0);
+ raw_value = data.GetMaxU64(&offset, byte_size - offset);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(x1_info, raw_value))
+ error.SetErrorString("failed to write register x1");
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 128 bit "
+ "integer values at present.");
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ error.SetErrorString(
+ "returning complex float values are not supported");
+ } else {
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ if (byte_size <= 16) {
+ if (byte_size <= RegisterValue::GetMaxByteSize()) {
+ RegisterValue reg_value;
+ error = reg_value.SetValueFromData(v0_info, data, 0, true);
+ if (error.Success()) {
+ if (!reg_ctx->WriteRegister(v0_info, reg_value))
+ error.SetErrorString("failed to write register v0");
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "returning float values with a byte size of %" PRIu64
+ " are not supported",
+ byte_size);
+ }
+ } else {
+ error.SetErrorString("returning float values longer than 128 "
+ "bits are not supported");
+ }
+ } else {
+ error.SetErrorString("v0 register is not available on this target");
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsVector) {
+ if (byte_size > 0) {
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ if (byte_size <= v0_info->byte_size) {
+ RegisterValue reg_value;
+ error = reg_value.SetValueFromData(v0_info, data, 0, true);
+ if (error.Success()) {
+ if (!reg_ctx->WriteRegister(v0_info, reg_value))
+ error.SetErrorString("failed to write register v0");
+ }
+ }
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("no registers are available");
+ }
+
+ return error;
+}
+
+bool ABIMacOSX_arm64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num = arm64_dwarf::lr;
+ uint32_t sp_reg_num = arm64_dwarf::sp;
+ uint32_t pc_reg_num = arm64_dwarf::pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ // Our previous PC is in the LR
+ row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true);
+
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("arm64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABIMacOSX_arm64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = arm64_dwarf::fp;
+ uint32_t pc_reg_num = arm64_dwarf::pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 8;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("arm64-apple-darwin default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+// AAPCS64 (Procedure Call Standard for the ARM 64-bit Architecture) says
+// registers x19 through x28 and sp are callee preserved. v8-v15 are non-
+// volatile (and specifically only the lower 8 bytes of these regs), the rest
+// of the fp/SIMD registers are volatile.
+
+// We treat x29 as callee preserved also, else the unwinder won't try to
+// retrieve fp saves.
+
+bool ABIMacOSX_arm64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ const char *name = reg_info->name;
+
+ // Sometimes we'll be called with the "alternate" name for these registers;
+ // recognize them as non-volatile.
+
+ if (name[0] == 'p' && name[1] == 'c') // pc
+ return false;
+ if (name[0] == 'f' && name[1] == 'p') // fp
+ return false;
+ if (name[0] == 's' && name[1] == 'p') // sp
+ return false;
+ if (name[0] == 'l' && name[1] == 'r') // lr
+ return false;
+
+ if (name[0] == 'x') {
+ // Volatile registers: x0-x18, x30 (lr)
+ // Return false for the non-volatile gpr regs, true for everything else
+ switch (name[1]) {
+ case '1':
+ switch (name[2]) {
+ case '9':
+ return false; // x19 is non-volatile
+ default:
+ return true;
+ }
+ break;
+ case '2':
+ switch (name[2]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ return false; // x20 - 28 are non-volatile
+ case '9':
+ return false; // x29 aka fp treat as non-volatile on Darwin
+ default:
+ return true;
+ }
+ case '3': // x30 aka lr treat as non-volatile
+ if (name[2] == '0')
+ return false;
+ break;
+ default:
+ return true;
+ }
+ } else if (name[0] == 'v' || name[0] == 's' || name[0] == 'd') {
+ // Volatile registers: v0-7, v16-v31
+ // Return false for non-volatile fp/SIMD regs, true for everything else
+ switch (name[1]) {
+ case '8':
+ case '9':
+ return false; // v8-v9 are non-volatile
+ case '1':
+ switch (name[2]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return false; // v10-v15 are non-volatile
+ default:
+ return true;
+ }
+ default:
+ return true;
+ }
+ }
+ }
+ return true;
+}
+
+static bool LoadValueFromConsecutiveGPRRegisters(
+ ExecutionContext &exe_ctx, RegisterContext *reg_ctx,
+ const CompilerType &value_type,
+ bool is_return_value, // false => parameter, true => return value
+ uint32_t &NGRN, // NGRN (see ABI documentation)
+ uint32_t &NSRN, // NSRN (see ABI documentation)
+ DataExtractor &data) {
+ llvm::Optional<uint64_t> byte_size = value_type.GetByteSize(nullptr);
+ if (!byte_size || *byte_size == 0)
+ return false;
+
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder();
+ Status error;
+
+ CompilerType base_type;
+ const uint32_t homogeneous_count =
+ value_type.IsHomogeneousAggregate(&base_type);
+ if (homogeneous_count > 0 && homogeneous_count <= 8) {
+ // Make sure we have enough registers
+ if (NSRN < 8 && (8 - NSRN) >= homogeneous_count) {
+ if (!base_type)
+ return false;
+ llvm::Optional<uint64_t> base_byte_size = base_type.GetByteSize(nullptr);
+ if (!base_byte_size)
+ return false;
+ uint32_t data_offset = 0;
+
+ for (uint32_t i = 0; i < homogeneous_count; ++i) {
+ char v_name[8];
+ ::snprintf(v_name, sizeof(v_name), "v%u", NSRN);
+ const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoByName(v_name, 0);
+ if (reg_info == nullptr)
+ return false;
+
+ if (*base_byte_size > reg_info->byte_size)
+ return false;
+
+ RegisterValue reg_value;
+
+ if (!reg_ctx->ReadRegister(reg_info, reg_value))
+ return false;
+
+ // Make sure we have enough room in "heap_data_up"
+ if ((data_offset + *base_byte_size) <= heap_data_up->GetByteSize()) {
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, heap_data_up->GetBytes() + data_offset, *base_byte_size,
+ byte_order, error);
+ if (bytes_copied != *base_byte_size)
+ return false;
+ data_offset += bytes_copied;
+ ++NSRN;
+ } else
+ return false;
+ }
+ data.SetByteOrder(byte_order);
+ data.SetAddressByteSize(exe_ctx.GetProcessRef().GetAddressByteSize());
+ data.SetData(DataBufferSP(heap_data_up.release()));
+ return true;
+ }
+ }
+
+ const size_t max_reg_byte_size = 16;
+ if (*byte_size <= max_reg_byte_size) {
+ size_t bytes_left = *byte_size;
+ uint32_t data_offset = 0;
+ while (data_offset < *byte_size) {
+ if (NGRN >= 8)
+ return false;
+
+ uint32_t reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + NGRN);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return false;
+
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num);
+ if (reg_info == nullptr)
+ return false;
+
+ RegisterValue reg_value;
+
+ if (!reg_ctx->ReadRegister(reg_info, reg_value))
+ return false;
+
+ const size_t curr_byte_size = std::min<size_t>(8, bytes_left);
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, heap_data_up->GetBytes() + data_offset, curr_byte_size,
+ byte_order, error);
+ if (bytes_copied == 0)
+ return false;
+ if (bytes_copied >= bytes_left)
+ break;
+ data_offset += bytes_copied;
+ bytes_left -= bytes_copied;
+ ++NGRN;
+ }
+ } else {
+ const RegisterInfo *reg_info = nullptr;
+ if (is_return_value) {
+ // We are assuming we are decoding this immediately after returning from
+ // a function call and that the address of the structure is in x8
+ reg_info = reg_ctx->GetRegisterInfoByName("x8", 0);
+ } else {
+ // We are assuming we are stopped at the first instruction in a function
+ // and that the ABI is being respected so all parameters appear where
+ // they should be (functions with no external linkage can legally violate
+ // the ABI).
+ if (NGRN >= 8)
+ return false;
+
+ uint32_t reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + NGRN);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return false;
+ reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num);
+ if (reg_info == nullptr)
+ return false;
+ ++NGRN;
+ }
+
+ if (reg_info == nullptr)
+ return false;
+
+ const lldb::addr_t value_addr =
+ reg_ctx->ReadRegisterAsUnsigned(reg_info, LLDB_INVALID_ADDRESS);
+
+ if (value_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (exe_ctx.GetProcessRef().ReadMemory(
+ value_addr, heap_data_up->GetBytes(), heap_data_up->GetByteSize(),
+ error) != heap_data_up->GetByteSize()) {
+ return false;
+ }
+ }
+
+ data.SetByteOrder(byte_order);
+ data.SetAddressByteSize(exe_ctx.GetProcessRef().GetAddressByteSize());
+ data.SetData(DataBufferSP(heap_data_up.release()));
+ return true;
+}
+
+ValueObjectSP ABIMacOSX_arm64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ if (exe_ctx.GetTargetPtr() == nullptr || exe_ctx.GetProcessPtr() == nullptr)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType, return_compiler_type);
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo(nullptr);
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ // Extract the register context so we can read arguments from registers
+ if (*byte_size <= 8) {
+ const RegisterInfo *x0_reg_info =
+ reg_ctx->GetRegisterInfoByName("x0", 0);
+ if (x0_reg_info) {
+ uint64_t raw_value =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(x0_reg_info,
+ 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+ case 16: // uint128_t
+ // In register x0 and x1
+ {
+ const RegisterInfo *x1_reg_info =
+ reg_ctx->GetRegisterInfoByName("x1", 0);
+
+ if (x1_reg_info) {
+ if (*byte_size <=
+ x0_reg_info->byte_size + x1_reg_info->byte_size) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order =
+ exe_ctx.GetProcessRef().GetByteOrder();
+ RegisterValue x0_reg_value;
+ RegisterValue x1_reg_value;
+ if (reg_ctx->ReadRegister(x0_reg_info, x0_reg_value) &&
+ reg_ctx->ReadRegister(x1_reg_info, x1_reg_value)) {
+ Status error;
+ if (x0_reg_value.GetAsMemoryData(
+ x0_reg_info, heap_data_up->GetBytes() + 0, 8,
+ byte_order, error) &&
+ x1_reg_value.GetAsMemoryData(
+ x1_reg_info, heap_data_up->GetBytes() + 8, 8,
+ byte_order, error)) {
+ DataExtractor data(
+ DataBufferSP(heap_data_up.release()), byte_order,
+ exe_ctx.GetProcessRef().GetAddressByteSize());
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ return return_valobj_sp;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ if (*byte_size <= sizeof(long double)) {
+ const RegisterInfo *v0_reg_info =
+ reg_ctx->GetRegisterInfoByName("v0", 0);
+ RegisterValue v0_value;
+ if (reg_ctx->ReadRegister(v0_reg_info, v0_value)) {
+ DataExtractor data;
+ if (v0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = data.GetDouble(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(long double)) {
+ value.GetScalar() = data.GetLongDouble(&offset);
+ success = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsVector) {
+ if (*byte_size > 0) {
+
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ if (*byte_size <= v0_info->byte_size) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(v0_info, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(v0_info, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(),
+ byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ exe_ctx.GetProcessRef().GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass) {
+ DataExtractor data;
+
+ uint32_t NGRN = 0; // Search ABI docs for NGRN
+ uint32_t NSRN = 0; // Search ABI docs for NSRN
+ const bool is_return_value = true;
+ if (LoadValueFromConsecutiveGPRRegisters(
+ exe_ctx, reg_ctx, return_compiler_type, is_return_value, NGRN, NSRN,
+ data)) {
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ return return_valobj_sp;
+}
+
+void ABIMacOSX_arm64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(), pluginDesc,
+ CreateInstance);
+}
+
+void ABIMacOSX_arm64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+// PluginInterface protocol
+
+ConstString ABIMacOSX_arm64::GetPluginNameStatic() {
+ static ConstString g_plugin_name("ABIMacOSX_arm64");
+ return g_plugin_name;
+}
+
+uint32_t ABIMacOSX_arm64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h
new file mode 100644
index 000000000000..bfacbcd54a94
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h
@@ -0,0 +1,101 @@
+//===-- ABIMacOSX_arm64.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABIMacOSX_arm64_h_
+#define liblldb_ABIMacOSX_arm64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-private.h"
+
+class ABIMacOSX_arm64 : public lldb_private::ABI {
+public:
+ ~ABIMacOSX_arm64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The arm64 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ if (pc & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+
+ // Anything else if fair game..
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ // PluginInterface protocol
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ uint32_t GetPluginVersion() override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const override;
+
+private:
+ ABIMacOSX_arm64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABIMacOSX_arm64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp
new file mode 100644
index 000000000000..67371b432ff8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp
@@ -0,0 +1,1130 @@
+//===-- ABIMacOSX_i386.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABIMacOSX_i386.h"
+
+#include <vector>
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum {
+ ehframe_eax = 0,
+ ehframe_ecx,
+ ehframe_edx,
+ ehframe_ebx,
+ ehframe_ebp, // Different from DWARF the regnums - eh_frame esp/ebp had their
+ // regnums switched on i386 darwin
+ ehframe_esp, // Different from DWARF the regnums - eh_frame esp/ebp had their
+ // regnums switched on i386 darwin
+ ehframe_esi,
+ ehframe_edi,
+ ehframe_eip,
+ ehframe_eflags
+};
+
+enum {
+ dwarf_eax = 0,
+ dwarf_ecx,
+ dwarf_edx,
+ dwarf_ebx,
+ dwarf_esp,
+ dwarf_ebp,
+ dwarf_esi,
+ dwarf_edi,
+ dwarf_eip,
+ dwarf_eflags,
+ dwarf_stmm0 = 11,
+ dwarf_stmm1,
+ dwarf_stmm2,
+ dwarf_stmm3,
+ dwarf_stmm4,
+ dwarf_stmm5,
+ dwarf_stmm6,
+ dwarf_stmm7,
+ dwarf_xmm0 = 21,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7,
+ dwarf_ymm0 = dwarf_xmm0,
+ dwarf_ymm1 = dwarf_xmm1,
+ dwarf_ymm2 = dwarf_xmm2,
+ dwarf_ymm3 = dwarf_xmm3,
+ dwarf_ymm4 = dwarf_xmm4,
+ dwarf_ymm5 = dwarf_xmm5,
+ dwarf_ymm6 = dwarf_xmm6,
+ dwarf_ymm7 = dwarf_xmm7
+};
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT
+ // EH_FRAME DWARF GENERIC
+ // PROCESS PLUGIN LLDB NATIVE
+ // ====== ======= == === ============= ============
+ // ===================== ===================== ============================
+ // ==================== ======================
+ {"eax",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_eax, dwarf_eax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ebx",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ecx",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_ecx, dwarf_ecx, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"edx",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_edx, dwarf_edx, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"esi",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_esi, dwarf_esi, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"edi",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_edi, dwarf_edi, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ebp",
+ "fp",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_ebp, dwarf_ebp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"esp",
+ "sp",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_esp, dwarf_esp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"eip",
+ "pc",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_eip, dwarf_eip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"eflags",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ss",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ds",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"es",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"gs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm0",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm0, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm1",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm2",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm3",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm4",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm5",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm5, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm6",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm6, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm7",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm7, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fctrl",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fstat",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ftag",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fiseg",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fioff",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"foseg",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fooff",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fop",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm0",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm1",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm2",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm3",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm4",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm5",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm6",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm7",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"mxcsr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm0",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm1",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm2",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm3",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm4",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm5",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm6",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm7",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABIMacOSX_i386::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABIMacOSX_i386::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABIMacOSX_i386::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if ((arch.GetTriple().getArch() == llvm::Triple::x86) &&
+ (arch.GetTriple().isMacOSX() || arch.GetTriple().isiOS() ||
+ arch.GetTriple().isWatchOS())) {
+ return ABISP(new ABIMacOSX_i386(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABIMacOSX_i386::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+ uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+
+ // When writing a register value down to memory, the register info used to
+ // write memory just needs to have the correct size of a 32 bit register, the
+ // actual register it pertains to is not important, just the size needs to be
+ // correct. Here we use "eax"...
+ const RegisterInfo *reg_info_32 = reg_ctx->GetRegisterInfoByName("eax");
+ if (!reg_info_32)
+ return false; // TODO this should actually never happen
+
+ // Make room for the argument(s) on the stack
+
+ Status error;
+ RegisterValue reg_value;
+
+ // Write any arguments onto the stack
+ sp -= 4 * args.size();
+
+ // Align the SP
+ sp &= ~(16ull - 1ull); // 16-byte alignment
+
+ addr_t arg_pos = sp;
+
+ for (addr_t arg : args) {
+ reg_value.SetUInt32(arg);
+ error = reg_ctx->WriteRegisterValueToMemory(
+ reg_info_32, arg_pos, reg_info_32->byte_size, reg_value);
+ if (error.Fail())
+ return false;
+ arg_pos += 4;
+ }
+
+ // The return address is pushed onto the stack (yes after we just set the
+ // alignment above!).
+ sp -= 4;
+ reg_value.SetUInt32(return_addr);
+ error = reg_ctx->WriteRegisterValueToMemory(
+ reg_info_32, sp, reg_info_32->byte_size, reg_value);
+ if (error.Fail())
+ return false;
+
+ // %esp is set to the actual stack value.
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp))
+ return false;
+
+ // %eip is set to the address of the called function.
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Process *process,
+ addr_t &current_stack_argument) {
+
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (process->ReadScalarIntegerFromMemory(current_stack_argument, byte_size,
+ is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+}
+
+bool ABIMacOSX_i386::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 4; // jump over return address
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ CompilerType compiler_type(value->GetCompilerType());
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (bit_size) {
+ bool is_signed;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed))
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed,
+ thread.GetProcess().get(), current_stack_argument);
+ else if (compiler_type.IsPointerType())
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false,
+ thread.GetProcess().get(), current_stack_argument);
+ }
+ }
+
+ return true;
+}
+
+Status ABIMacOSX_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0);
+ if (num_bytes <= 4) {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value))
+ set_it_simple = true;
+ } else {
+ uint32_t raw_value = data.GetMaxU32(&offset, 4);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value)) {
+ const RegisterInfo *edx_info =
+ reg_ctx->GetRegisterInfoByName("edx", 0);
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(edx_info, raw_value))
+ set_it_simple = true;
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else
+ error.SetErrorString(
+ "We don't support returning float values at present");
+ }
+
+ if (!set_it_simple)
+ error.SetErrorString(
+ "We only support setting simple integer return types at present.");
+
+ return error;
+}
+
+ValueObjectSP
+ABIMacOSX_i386::GetReturnValueObjectImpl(Thread &thread,
+ CompilerType &compiler_type) const {
+ Value value;
+ ValueObjectSP return_valobj_sp;
+
+ if (!compiler_type)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType,
+ // compiler_type.GetOpaqueQualType());
+ value.SetCompilerType(compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ bool is_signed;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ llvm::Optional<uint64_t> bit_width = compiler_type.GetBitSize(&thread);
+ if (!bit_width)
+ return return_valobj_sp;
+ unsigned eax_id =
+ reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];
+ unsigned edx_id =
+ reg_ctx->GetRegisterInfoByName("edx", 0)->kinds[eRegisterKindLLDB];
+
+ switch (*bit_width) {
+ default:
+ case 128:
+ // Scalar can't hold 128-bit literals, so we don't handle this
+ return return_valobj_sp;
+ case 64:
+ uint64_t raw_value;
+ raw_value =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff;
+ raw_value |=
+ (thread.GetRegisterContext()->ReadRegisterAsUnsigned(edx_id, 0) &
+ 0xffffffff)
+ << 32;
+ if (is_signed)
+ value.GetScalar() = (int64_t)raw_value;
+ else
+ value.GetScalar() = (uint64_t)raw_value;
+ break;
+ case 32:
+ if (is_signed)
+ value.GetScalar() = (int32_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff);
+ else
+ value.GetScalar() = (uint32_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff);
+ break;
+ case 16:
+ if (is_signed)
+ value.GetScalar() = (int16_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffff);
+ else
+ value.GetScalar() = (uint16_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffff);
+ break;
+ case 8:
+ if (is_signed)
+ value.GetScalar() = (int8_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xff);
+ else
+ value.GetScalar() = (uint8_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xff);
+ break;
+ }
+ } else if (compiler_type.IsPointerType()) {
+ unsigned eax_id =
+ reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];
+ uint32_t ptr =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff;
+ value.GetScalar() = ptr;
+ } else {
+ // not handled yet
+ return return_valobj_sp;
+ }
+
+ // If we get here, we have a valid Value, so make our ValueObject out of it:
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ return return_valobj_sp;
+}
+
+// This defines the CFA as esp+4
+// the saved pc is at CFA-4 (i.e. esp+0)
+// The saved esp is CFA+0
+
+bool ABIMacOSX_i386::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num = dwarf_esp;
+ uint32_t pc_reg_num = dwarf_eip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 4);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("i386 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+// This defines the CFA as ebp+8
+// The saved pc is at CFA-4 (i.e. ebp+4)
+// The saved ebp is at CFA-8 (i.e. ebp+0)
+// The saved esp is CFA+0
+
+bool ABIMacOSX_i386::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = dwarf_ebp;
+ uint32_t sp_reg_num = dwarf_esp;
+ uint32_t pc_reg_num = dwarf_eip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 4;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("i386 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+bool ABIMacOSX_i386::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+// v.
+// http://developer.apple.com/library/mac/#documentation/developertools/Conceptual/LowLevelABI/130
+// -IA-
+// 32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4
+//
+// This document ("OS X ABI Function Call Guide", chapter "IA-32 Function
+// Calling Conventions") says that the following registers on i386 are
+// preserved aka non-volatile aka callee-saved:
+//
+// ebx, ebp, esi, edi, esp
+
+bool ABIMacOSX_i386::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Saved registers are ebx, ebp, esi, edi, esp, eip
+ const char *name = reg_info->name;
+ if (name[0] == 'e') {
+ switch (name[1]) {
+ case 'b':
+ if (name[2] == 'x' || name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ case 'd':
+ if (name[2] == 'i')
+ return name[3] == '\0';
+ break;
+ case 'i':
+ if (name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ case 's':
+ if (name[2] == 'i' || name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ }
+ }
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp
+ return true;
+ if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc
+ return true;
+ }
+ return false;
+}
+
+void ABIMacOSX_i386::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "Mac OS X ABI for i386 targets", CreateInstance);
+}
+
+void ABIMacOSX_i386::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABIMacOSX_i386::GetPluginNameStatic() {
+ static ConstString g_short_name("abi.macosx-i386");
+ return g_short_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABIMacOSX_i386::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABIMacOSX_i386::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h
new file mode 100644
index 000000000000..57def683283f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h
@@ -0,0 +1,100 @@
+//===-- ABIMacOSX_i386.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABIMacOSX_i386_h_
+#define liblldb_ABIMacOSX_i386_h_
+
+#include "lldb/Core/Value.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABIMacOSX_i386 : public lldb_private::ABI {
+public:
+ ~ABIMacOSX_i386() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t func_addr, lldb::addr_t return_addr,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The Darwin i386 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (4-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ //
+ // If we were to enforce 16-byte alignment, we also need to relax to 4-byte
+ // alignment for non-darwin i386 targets.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are are 4 byte aligned
+ if (cfa & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Just make sure the address is a valid 32 bit address.
+ return pc <= UINT32_MAX;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ // PluginInterface protocol
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const override;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABIMacOSX_i386(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABIMacOSX_i386_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.cpp
new file mode 100644
index 000000000000..dd47ac7cbe3c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.cpp
@@ -0,0 +1,2156 @@
+//===-- ABISysV_arm.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_arm.h"
+
+#include <vector>
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "Utility/ARM_DWARF_Registers.h"
+#include "Utility/ARM_ehframe_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME
+ // DWARF GENERIC PROCESS PLUGIN
+ // LLDB NATIVE VALUE REGS INVALIDATE REGS
+ // ========== ======= == === ============= ============
+ // ======================= =================== ===========================
+ // ======================= ====================== ==========
+ // ===============
+ {"r0",
+ "arg1",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ "arg2",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ "arg3",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ "arg4",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "r13",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r14",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "r15",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s0",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpscr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d29",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d30",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d31",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r8_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r9_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r10_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r11_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r12_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_usr",
+ "sp_usr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_usr",
+ "lr_usr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r8_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r9_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r10_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r11_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r12_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_fiq",
+ "sp_fiq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_fiq",
+ "lr_fiq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_irq",
+ "sp_irq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_irq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_irq",
+ "lr_irq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_irq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_abt",
+ "sp_abt",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_abt, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_abt",
+ "lr_abt",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_abt, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_und",
+ "sp_und",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_und, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_und",
+ "lr_und",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_und, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_svc",
+ "sp_svc",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_svc, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_svc",
+ "lr_svc",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_svc, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_arm::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABISysV_arm::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABISysV_arm::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ const llvm::Triple::VendorType vendor_type = arch.GetTriple().getVendor();
+
+ if (vendor_type != llvm::Triple::Apple) {
+ if ((arch_type == llvm::Triple::arm) ||
+ (arch_type == llvm::Triple::thumb)) {
+ return ABISP(new ABISysV_arm(process_sp));
+ }
+ }
+
+ return ABISP();
+}
+
+bool ABISysV_arm::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t function_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const uint32_t ra_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+
+ RegisterValue reg_value;
+
+ const uint8_t reg_names[] = {
+ LLDB_REGNUM_GENERIC_ARG1, LLDB_REGNUM_GENERIC_ARG2,
+ LLDB_REGNUM_GENERIC_ARG3, LLDB_REGNUM_GENERIC_ARG4};
+
+ llvm::ArrayRef<addr_t>::iterator ai = args.begin(), ae = args.end();
+
+ for (size_t i = 0; i < llvm::array_lengthof(reg_names); ++i) {
+ if (ai == ae)
+ break;
+
+ reg_value.SetUInt32(*ai);
+ if (!reg_ctx->WriteRegister(
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, reg_names[i]),
+ reg_value))
+ return false;
+
+ ++ai;
+ }
+
+ if (ai != ae) {
+ // Spill onto the stack
+ size_t num_stack_regs = ae - ai;
+
+ sp -= (num_stack_regs * 4);
+ // Keep the stack 8 byte aligned, not that we need to
+ sp &= ~(8ull - 1ull);
+
+ // just using arg1 to get the right size
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+
+ addr_t arg_pos = sp;
+
+ for (; ai != ae; ++ai) {
+ reg_value.SetUInt32(*ai);
+ if (reg_ctx
+ ->WriteRegisterValueToMemory(reg_info, arg_pos,
+ reg_info->byte_size, reg_value)
+ .Fail())
+ return false;
+ arg_pos += reg_info->byte_size;
+ }
+ }
+
+ TargetSP target_sp(thread.CalculateTarget());
+ Address so_addr;
+
+ // Figure out if our return address is ARM or Thumb by using the
+ // Address::GetCallableLoadAddress(Target*) which will figure out the ARM
+ // thumb-ness and set the correct address bits for us.
+ so_addr.SetLoadAddress(return_addr, target_sp.get());
+ return_addr = so_addr.GetCallableLoadAddress(target_sp.get());
+
+ // Set "lr" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(ra_reg_num, return_addr))
+ return false;
+
+ // Set "sp" to the requested value
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp))
+ return false;
+
+ // If bit zero or 1 is set, this must be a thumb function, no need to figure
+ // this out from the symbols.
+ so_addr.SetLoadAddress(function_addr, target_sp.get());
+ function_addr = so_addr.GetCallableLoadAddress(target_sp.get());
+
+ const RegisterInfo *cpsr_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
+ const uint32_t curr_cpsr = reg_ctx->ReadRegisterAsUnsigned(cpsr_reg_info, 0);
+
+ // Make a new CPSR and mask out any Thumb IT (if/then) bits
+ uint32_t new_cpsr = curr_cpsr & ~MASK_CPSR_IT_MASK;
+ // If bit zero or 1 is set, this must be thumb...
+ if (function_addr & 1ull)
+ new_cpsr |= MASK_CPSR_T; // Set T bit in CPSR
+ else
+ new_cpsr &= ~MASK_CPSR_T; // Clear T bit in CPSR
+
+ if (new_cpsr != curr_cpsr) {
+ if (!reg_ctx->WriteRegisterFromUnsigned(cpsr_reg_info, new_cpsr))
+ return false;
+ }
+
+ function_addr &=
+ ~1ull; // clear bit zero since the CPSR will take care of the mode for us
+
+ // Set "pc" to the address requested
+ return reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, function_addr);
+}
+
+bool ABISysV_arm::GetArgumentValues(Thread &thread, ValueList &values) const {
+ uint32_t num_values = values.GetSize();
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ // For now, assume that the types in the AST values come from the Target's
+ // scratch AST.
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ addr_t sp = 0;
+
+ for (uint32_t value_idx = 0; value_idx < num_values; ++value_idx) {
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ Value *value = values.GetValueAtIndex(value_idx);
+
+ if (!value)
+ return false;
+
+ CompilerType compiler_type = value->GetCompilerType();
+ if (compiler_type) {
+ bool is_signed = false;
+ size_t bit_width = 0;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerOrReferenceType()) {
+ if (llvm::Optional<uint64_t> size = compiler_type.GetBitSize(&thread))
+ bit_width = *size;
+ } else {
+ // We only handle integer, pointer and reference types currently...
+ return false;
+ }
+
+ if (bit_width <= (exe_ctx.GetProcessRef().GetAddressByteSize() * 8)) {
+ if (value_idx < 4) {
+ // Arguments 1-4 are in r0-r3...
+ const RegisterInfo *arg_reg_info = nullptr;
+ arg_reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + value_idx);
+ if (arg_reg_info) {
+ RegisterValue reg_value;
+
+ if (reg_ctx->ReadRegister(arg_reg_info, reg_value)) {
+ if (is_signed)
+ reg_value.SignExtend(bit_width);
+ if (!reg_value.GetScalarValue(value->GetScalar()))
+ return false;
+ continue;
+ }
+ }
+ return false;
+ } else {
+ if (sp == 0) {
+ // Read the stack pointer if it already hasn't been read
+ sp = reg_ctx->GetSP(0);
+ if (sp == 0)
+ return false;
+ }
+
+ // Arguments 5 on up are on the stack
+ const uint32_t arg_byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory(
+ sp, arg_byte_size, is_signed, value->GetScalar(), error))
+ return false;
+
+ sp += arg_byte_size;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+static bool GetReturnValuePassedInMemory(Thread &thread,
+ RegisterContext *reg_ctx,
+ size_t byte_size, Value &value) {
+ Status error;
+ DataBufferHeap buffer(byte_size, 0);
+
+ const RegisterInfo *r0_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+ uint32_t address =
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ thread.GetProcess()->ReadMemory(address, buffer.GetBytes(),
+ buffer.GetByteSize(), error);
+
+ if (error.Fail())
+ return false;
+
+ value.SetBytes(buffer.GetBytes(), buffer.GetByteSize());
+ return true;
+}
+
+bool ABISysV_arm::IsArmHardFloat(Thread &thread) const {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ const ArchSpec &arch(process_sp->GetTarget().GetArchitecture());
+
+ return (arch.GetFlags() & ArchSpec::eARM_abi_hard_float) != 0;
+ }
+
+ return false;
+}
+
+ValueObjectSP ABISysV_arm::GetReturnValueObjectImpl(
+ Thread &thread, lldb_private::CompilerType &compiler_type) const {
+ Value value;
+ ValueObjectSP return_valobj_sp;
+
+ if (!compiler_type)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType,
+ // compiler_type.GetOpaqueQualType());
+ value.SetCompilerType(compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ bool is_signed;
+ bool is_complex;
+ uint32_t float_count;
+ bool is_vfp_candidate = false;
+ uint8_t vfp_count = 0;
+ uint8_t vfp_byte_size = 0;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ const RegisterInfo *r0_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+ llvm::Optional<uint64_t> bit_width = compiler_type.GetBitSize(&thread);
+ llvm::Optional<uint64_t> byte_size = compiler_type.GetByteSize(&thread);
+ if (!bit_width || !byte_size)
+ return return_valobj_sp;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 64: {
+ const RegisterInfo *r1_reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2);
+ uint64_t raw_value;
+ raw_value = reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r1_reg_info, 0) &
+ UINT32_MAX))
+ << 32;
+ if (is_signed)
+ value.GetScalar() = (int64_t)raw_value;
+ else
+ value.GetScalar() = (uint64_t)raw_value;
+ } break;
+ case 32:
+ if (is_signed)
+ value.GetScalar() = (int32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX);
+ break;
+ case 16:
+ if (is_signed)
+ value.GetScalar() = (int16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX);
+ break;
+ case 8:
+ if (is_signed)
+ value.GetScalar() = (int8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX);
+ break;
+ }
+ } else if (compiler_type.IsPointerType()) {
+ uint32_t ptr =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(r0_reg_info, 0) &
+ UINT32_MAX;
+ value.GetScalar() = ptr;
+ } else if (compiler_type.IsVectorType(nullptr, nullptr)) {
+ if (IsArmHardFloat(thread) && (*byte_size == 8 || *byte_size == 16)) {
+ is_vfp_candidate = true;
+ vfp_byte_size = 8;
+ vfp_count = (*byte_size == 8 ? 1 : 2);
+ } else if (*byte_size <= 16) {
+ DataBufferHeap buffer(16, 0);
+ uint32_t *buffer_ptr = (uint32_t *)buffer.GetBytes();
+
+ for (uint32_t i = 0; 4 * i < *byte_size; ++i) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i);
+ buffer_ptr[i] =
+ reg_ctx->ReadRegisterAsUnsigned(reg_info, 0) & UINT32_MAX;
+ }
+ value.SetBytes(buffer.GetBytes(), *byte_size);
+ } else {
+ if (!GetReturnValuePassedInMemory(thread, reg_ctx, *byte_size, value))
+ return return_valobj_sp;
+ }
+ } else if (compiler_type.IsFloatingPointType(float_count, is_complex)) {
+ if (float_count == 1 && !is_complex) {
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 64: {
+ static_assert(sizeof(double) == sizeof(uint64_t), "");
+
+ if (IsArmHardFloat(thread)) {
+ RegisterValue reg_value;
+ const RegisterInfo *d0_reg_info =
+ reg_ctx->GetRegisterInfoByName("d0", 0);
+ reg_ctx->ReadRegister(d0_reg_info, reg_value);
+ value.GetScalar() = reg_value.GetAsDouble();
+ } else {
+ uint64_t raw_value;
+ const RegisterInfo *r1_reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2);
+ raw_value =
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ raw_value |=
+ ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r1_reg_info, 0) &
+ UINT32_MAX))
+ << 32;
+ value.GetScalar() = *reinterpret_cast<double *>(&raw_value);
+ }
+ break;
+ }
+ case 16: // Half precision returned after a conversion to single precision
+ case 32: {
+ static_assert(sizeof(float) == sizeof(uint32_t), "");
+
+ if (IsArmHardFloat(thread)) {
+ RegisterValue reg_value;
+ const RegisterInfo *s0_reg_info =
+ reg_ctx->GetRegisterInfoByName("s0", 0);
+ reg_ctx->ReadRegister(s0_reg_info, reg_value);
+ value.GetScalar() = reg_value.GetAsFloat();
+ } else {
+ uint32_t raw_value;
+ raw_value =
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ value.GetScalar() = *reinterpret_cast<float *>(&raw_value);
+ }
+ break;
+ }
+ }
+ } else if (is_complex && float_count == 2) {
+ if (IsArmHardFloat(thread)) {
+ is_vfp_candidate = true;
+ vfp_byte_size = *byte_size / 2;
+ vfp_count = 2;
+ } else if (!GetReturnValuePassedInMemory(thread, reg_ctx, *bit_width / 8,
+ value))
+ return return_valobj_sp;
+ } else
+ // not handled yet
+ return return_valobj_sp;
+ } else if (compiler_type.IsAggregateType()) {
+ if (IsArmHardFloat(thread)) {
+ CompilerType base_type;
+ const uint32_t homogeneous_count =
+ compiler_type.IsHomogeneousAggregate(&base_type);
+
+ if (homogeneous_count > 0 && homogeneous_count <= 4) {
+ llvm::Optional<uint64_t> base_byte_size =
+ base_type.GetByteSize(nullptr);
+ if (base_type.IsVectorType(nullptr, nullptr)) {
+ if (base_byte_size &&
+ (*base_byte_size == 8 || *base_byte_size == 16)) {
+ is_vfp_candidate = true;
+ vfp_byte_size = 8;
+ vfp_count = (*base_byte_size == 8 ? homogeneous_count
+ : homogeneous_count * 2);
+ }
+ } else if (base_type.IsFloatingPointType(float_count, is_complex)) {
+ if (float_count == 1 && !is_complex) {
+ is_vfp_candidate = true;
+ if (base_byte_size)
+ vfp_byte_size = *base_byte_size;
+ vfp_count = homogeneous_count;
+ }
+ }
+ } else if (homogeneous_count == 0) {
+ const uint32_t num_children = compiler_type.GetNumFields();
+
+ if (num_children > 0 && num_children <= 2) {
+ uint32_t index = 0;
+ for (index = 0; index < num_children; index++) {
+ std::string name;
+ base_type = compiler_type.GetFieldAtIndex(index, name, nullptr,
+ nullptr, nullptr);
+
+ if (base_type.IsFloatingPointType(float_count, is_complex)) {
+ llvm::Optional<uint64_t> base_byte_size =
+ base_type.GetByteSize(nullptr);
+ if (float_count == 2 && is_complex) {
+ if (index != 0 && base_byte_size &&
+ vfp_byte_size != *base_byte_size)
+ break;
+ else if (base_byte_size)
+ vfp_byte_size = *base_byte_size;
+ } else
+ break;
+ } else
+ break;
+ }
+
+ if (index == num_children) {
+ is_vfp_candidate = true;
+ vfp_byte_size = (vfp_byte_size >> 1);
+ vfp_count = (num_children << 1);
+ }
+ }
+ }
+ }
+
+ if (*byte_size <= 4) {
+ RegisterValue r0_reg_value;
+ uint32_t raw_value =
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ value.SetBytes(&raw_value, *byte_size);
+ } else if (!is_vfp_candidate) {
+ if (!GetReturnValuePassedInMemory(thread, reg_ctx, *byte_size, value))
+ return return_valobj_sp;
+ }
+ } else {
+ // not handled yet
+ return return_valobj_sp;
+ }
+
+ if (is_vfp_candidate) {
+ ProcessSP process_sp(thread.GetProcess());
+ ByteOrder byte_order = process_sp->GetByteOrder();
+
+ DataBufferSP data_sp(new DataBufferHeap(*byte_size, 0));
+ uint32_t data_offset = 0;
+
+ for (uint32_t reg_index = 0; reg_index < vfp_count; reg_index++) {
+ uint32_t regnum = 0;
+
+ if (vfp_byte_size == 4)
+ regnum = dwarf_s0 + reg_index;
+ else if (vfp_byte_size == 8)
+ regnum = dwarf_d0 + reg_index;
+ else
+ break;
+
+ const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindDWARF, regnum);
+ if (reg_info == nullptr)
+ break;
+
+ RegisterValue reg_value;
+ if (!reg_ctx->ReadRegister(reg_info, reg_value))
+ break;
+
+ // Make sure we have enough room in "data_sp"
+ if ((data_offset + vfp_byte_size) <= data_sp->GetByteSize()) {
+ Status error;
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, data_sp->GetBytes() + data_offset, vfp_byte_size,
+ byte_order, error);
+ if (bytes_copied != vfp_byte_size)
+ break;
+
+ data_offset += bytes_copied;
+ }
+ }
+
+ if (data_offset == *byte_size) {
+ DataExtractor data;
+ data.SetByteOrder(byte_order);
+ data.SetAddressByteSize(process_sp->GetAddressByteSize());
+ data.SetData(data_sp);
+
+ return ValueObjectConstResult::Create(&thread, compiler_type,
+ ConstString(""), data);
+ } else { // Some error occurred while getting values from registers
+ return return_valobj_sp;
+ }
+ }
+
+ // If we get here, we have a valid Value, so make our ValueObject out of it:
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ return return_valobj_sp;
+}
+
+Status ABISysV_arm::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ const RegisterInfo *r0_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+ if (num_bytes <= 4) {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r0_info, raw_value))
+ set_it_simple = true;
+ } else {
+ uint32_t raw_value = data.GetMaxU32(&offset, 4);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r0_info, raw_value)) {
+ const RegisterInfo *r1_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2);
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r1_info, raw_value))
+ set_it_simple = true;
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else
+ error.SetErrorString(
+ "We don't support returning float values at present");
+ }
+
+ if (!set_it_simple)
+ error.SetErrorString(
+ "We only support setting simple integer return types at present.");
+
+ return error;
+}
+
+bool ABISysV_arm::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num = dwarf_lr;
+ uint32_t sp_reg_num = dwarf_sp;
+ uint32_t pc_reg_num = dwarf_pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ // The previous PC is in the LR
+ row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("arm at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABISysV_arm::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ // TODO: Handle thumb
+ uint32_t fp_reg_num = dwarf_r11;
+ uint32_t pc_reg_num = dwarf_pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 4;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("arm default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+
+ return true;
+}
+
+// cf. "ARMv6 Function Calling Conventions"
+
+// ARMv7 on GNU/Linux general purpose reg rules:
+// r0-r3 not preserved (used for argument passing)
+// r4-r11 preserved (v1-v8)
+// r12 not presrved
+// r13 preserved (stack pointer)
+// r14 preserved (link register)
+// r15 preserved (pc)
+// cpsr not preserved (different rules for different bits)
+
+// ARMv7 VFP register rules:
+// d0-d7 not preserved (aka s0-s15, q0-q3)
+// d8-d15 preserved (aka s16-s31, q4-q7)
+// d16-d31 not preserved (aka q8-q15)
+
+bool ABISysV_arm::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Volatile registers are: r0, r1, r2, r3, r9, r12, r13 (aka sp)
+ const char *name = reg_info->name;
+ if (name[0] == 'r') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // r0
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // r1
+ case '2':
+ return name[3] == '\0'; // r12
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ return name[2] == '\0'; // r2
+ case '3':
+ return name[2] == '\0'; // r3
+ default:
+ break;
+ }
+ } else if (name[0] == 'd') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // d0 is volatile
+
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // d1 is volatile
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[3] == '\0'; // d16 - d19 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ switch (name[2]) {
+ case '\0':
+ return true; // d2 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[3] == '\0'; // d20 - d29 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '3':
+ switch (name[2]) {
+ case '\0':
+ return true; // d3 is volatile
+ case '0':
+ case '1':
+ return name[3] == '\0'; // d30 - d31 are volatile
+ default:
+ break;
+ }
+ break;
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ return name[2] == '\0'; // d4 - d7 are volatile
+
+ default:
+ break;
+ }
+ } else if (name[0] == 's') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // s0 is volatile
+
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // s1 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return name[3] == '\0'; // s10 - s15 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[2] == '\0'; // s2 - s9 are volatile
+
+ default:
+ break;
+ }
+ } else if (name[0] == 'q') {
+ switch (name[1]) {
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // q1 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return true; // q10-q15 are volatile
+ default:
+ return false;
+ }
+ break;
+
+ case '0':
+ case '2':
+ case '3':
+ return name[2] == '\0'; // q0-q3 are volatile
+ case '8':
+ case '9':
+ return name[2] == '\0'; // q8-q9 are volatile
+ default:
+ break;
+ }
+ } else if (name[0] == 's' && name[1] == 'p' && name[2] == '\0')
+ return true;
+ }
+ return false;
+}
+
+void ABISysV_arm::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "SysV ABI for arm targets", CreateInstance);
+}
+
+void ABISysV_arm::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_arm::GetPluginNameStatic() {
+ static ConstString g_name("SysV-arm");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_arm::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_arm::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.h
new file mode 100644
index 000000000000..a0f00c8f227d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.h
@@ -0,0 +1,93 @@
+//===-- ABISysV_arm.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABISysV_arm_h_
+#define liblldb_ABISysV_arm_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_arm : public lldb_private::ABI {
+public:
+ ~ABISysV_arm() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t func_addr, lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are are 4 byte aligned
+ if (cfa & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Just make sure the address is a valid 32 bit address. Bit zero
+ // might be set due to Thumb function calls, so don't enforce 2 byte
+ // alignment
+ return pc <= UINT32_MAX;
+ }
+
+ lldb::addr_t FixCodeAddress(lldb::addr_t pc) override {
+ // ARM uses bit zero to signify a code address is thumb, so we must
+ // strip bit zero in any code addresses.
+ return pc & ~(lldb::addr_t)1;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ bool IsArmHardFloat(lldb_private::Thread &thread) const;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const override;
+
+private:
+ ABISysV_arm(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp
new file mode 100644
index 000000000000..1d547121e231
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp
@@ -0,0 +1,2417 @@
+//===-- ABISysV_arm64.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_arm64.h"
+
+#include <vector>
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+#include "Utility/ARM64_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT
+ // EH_FRAME DWARF GENERIC
+ // PROCESS PLUGIN LLDB NATIVE
+ // ========== ======= == === ============= ===================
+ // =================== ====================== ===========================
+ // ======================= ======================
+ {"x0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x0, LLDB_REGNUM_GENERIC_ARG1,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x1, LLDB_REGNUM_GENERIC_ARG2,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x2, LLDB_REGNUM_GENERIC_ARG3,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x3, LLDB_REGNUM_GENERIC_ARG4,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x4, LLDB_REGNUM_GENERIC_ARG5,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x5, LLDB_REGNUM_GENERIC_ARG6,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x6, LLDB_REGNUM_GENERIC_ARG7,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x7, LLDB_REGNUM_GENERIC_ARG8,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x9, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x10, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x16, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x17, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x18, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x19, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x20, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x21, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x22, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x23, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x24, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x25, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x26, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x27, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x28, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fp",
+ "x29",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x29, LLDB_REGNUM_GENERIC_FP,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "x30",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x30, LLDB_REGNUM_GENERIC_RA,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "x31",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x31, LLDB_REGNUM_GENERIC_SP,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::pc, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::cpsr, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"v0",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v0, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v1",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v2",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v3",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v4",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v5",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v5, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v6",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v6, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v7",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v7, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v8",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v9",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v9, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v10",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v10, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v11",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v12",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v13",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v14",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v15",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v16",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v16, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v17",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v17, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v18",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v18, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v19",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v19, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v20",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v20, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v21",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v21, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v22",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v22, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v23",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v23, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v24",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v24, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v25",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v25, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v26",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v26, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v27",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v27, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v28",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v28, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v29",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v29, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v30",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v30, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v31",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v31, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"fpsr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpcr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"s0",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"d0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d29",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d30",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d31",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_arm64::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+bool ABISysV_arm64::GetPointerReturnRegister(const char *&name) {
+ name = "x0";
+ return true;
+}
+
+size_t ABISysV_arm64::GetRedZoneSize() const { return 128; }
+
+// Static Functions
+
+ABISP
+ABISysV_arm64::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ const llvm::Triple::VendorType vendor_type = arch.GetTriple().getVendor();
+
+ if (vendor_type != llvm::Triple::Apple) {
+ if (arch_type == llvm::Triple::aarch64) {
+ return ABISP(new ABISysV_arm64(process_sp));
+ }
+ }
+
+ return ABISP();
+}
+
+bool ABISysV_arm64::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_arm64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%d = 0x%" PRIx64, static_cast<int>(i + 1), args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ // x0 - x7 contain first 8 simple args
+ if (args.size() > 8)
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%d (0x%" PRIx64 ") into %s",
+ static_cast<int>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // Set "lr" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA),
+ return_addr))
+ return false;
+
+ // Set "sp" to the requested value
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP),
+ sp))
+ return false;
+
+ // Set "pc" to the address requested
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC),
+ func_addr))
+ return false;
+
+ return true;
+}
+
+// TODO: We dont support fp/SIMD arguments in v0-v7
+bool ABISysV_arm64::GetArgumentValues(Thread &thread, ValueList &values) const {
+ uint32_t num_values = values.GetSize();
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ addr_t sp = 0;
+
+ for (uint32_t value_idx = 0; value_idx < num_values; ++value_idx) {
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ Value *value = values.GetValueAtIndex(value_idx);
+
+ if (!value)
+ return false;
+
+ CompilerType value_type = value->GetCompilerType();
+ if (value_type) {
+ bool is_signed = false;
+ size_t bit_width = 0;
+ llvm::Optional<uint64_t> bit_size = value_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ if (value_type.IsIntegerOrEnumerationType(is_signed)) {
+ bit_width = *bit_size;
+ } else if (value_type.IsPointerOrReferenceType()) {
+ bit_width = *bit_size;
+ } else {
+ // We only handle integer, pointer and reference types currently...
+ return false;
+ }
+
+ if (bit_width <= (exe_ctx.GetProcessRef().GetAddressByteSize() * 8)) {
+ if (value_idx < 8) {
+ // Arguments 1-8 are in x0-x7...
+ const RegisterInfo *reg_info = nullptr;
+ reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + value_idx);
+
+ if (reg_info) {
+ RegisterValue reg_value;
+
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ if (is_signed)
+ reg_value.SignExtend(bit_width);
+ if (!reg_value.GetScalarValue(value->GetScalar()))
+ return false;
+ continue;
+ }
+ }
+ return false;
+ } else {
+ // TODO: Verify for stack layout for SysV
+ if (sp == 0) {
+ // Read the stack pointer if we already haven't read it
+ sp = reg_ctx->GetSP(0);
+ if (sp == 0)
+ return false;
+ }
+
+ // Arguments 5 on up are on the stack
+ const uint32_t arg_byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory(
+ sp, arg_byte_size, is_signed, value->GetScalar(), error))
+ return false;
+
+ sp += arg_byte_size;
+ // Align up to the next 8 byte boundary if needed
+ if (sp % 8) {
+ sp >>= 3;
+ sp += 1;
+ sp <<= 3;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+Status ABISysV_arm64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType return_value_type = new_value_sp->GetCompilerType();
+ if (!return_value_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ if (reg_ctx) {
+ DataExtractor data;
+ Status data_error;
+ const uint64_t byte_size = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ const uint32_t type_flags = return_value_type.GetTypeInfo(nullptr);
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ // Extract the register context so we can read arguments from registers
+ lldb::offset_t offset = 0;
+ if (byte_size <= 16) {
+ const RegisterInfo *x0_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+ if (byte_size <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, byte_size);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(x0_info, raw_value))
+ error.SetErrorString("failed to write register x0");
+ } else {
+ uint64_t raw_value = data.GetMaxU64(&offset, 8);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(x0_info, raw_value)) {
+ const RegisterInfo *x1_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2);
+ raw_value = data.GetMaxU64(&offset, byte_size - offset);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(x1_info, raw_value))
+ error.SetErrorString("failed to write register x1");
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 128 bit "
+ "integer values at present.");
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ error.SetErrorString(
+ "returning complex float values are not supported");
+ } else {
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ if (byte_size <= 16) {
+ if (byte_size <= RegisterValue::GetMaxByteSize()) {
+ RegisterValue reg_value;
+ error = reg_value.SetValueFromData(v0_info, data, 0, true);
+ if (error.Success()) {
+ if (!reg_ctx->WriteRegister(v0_info, reg_value))
+ error.SetErrorString("failed to write register v0");
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "returning float values with a byte size of %" PRIu64
+ " are not supported",
+ byte_size);
+ }
+ } else {
+ error.SetErrorString("returning float values longer than 128 "
+ "bits are not supported");
+ }
+ } else {
+ error.SetErrorString("v0 register is not available on this target");
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsVector) {
+ if (byte_size > 0) {
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ if (byte_size <= v0_info->byte_size) {
+ RegisterValue reg_value;
+ error = reg_value.SetValueFromData(v0_info, data, 0, true);
+ if (error.Success()) {
+ if (!reg_ctx->WriteRegister(v0_info, reg_value))
+ error.SetErrorString("failed to write register v0");
+ }
+ }
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("no registers are available");
+ }
+
+ return error;
+}
+
+bool ABISysV_arm64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num = arm64_dwarf::lr;
+ uint32_t sp_reg_num = arm64_dwarf::sp;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetReturnAddressRegister(lr_reg_num);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("arm64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABISysV_arm64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = arm64_dwarf::fp;
+ uint32_t pc_reg_num = arm64_dwarf::pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 8;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("arm64 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+
+ return true;
+}
+
+// AAPCS64 (Procedure Call Standard for the ARM 64-bit Architecture) says
+// registers x19 through x28 and sp are callee preserved. v8-v15 are non-
+// volatile (and specifically only the lower 8 bytes of these regs), the rest
+// of the fp/SIMD registers are volatile.
+
+// We treat x29 as callee preserved also, else the unwinder won't try to
+// retrieve fp saves.
+
+bool ABISysV_arm64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ const char *name = reg_info->name;
+
+ // Sometimes we'll be called with the "alternate" name for these registers;
+ // recognize them as non-volatile.
+
+ if (name[0] == 'p' && name[1] == 'c') // pc
+ return false;
+ if (name[0] == 'f' && name[1] == 'p') // fp
+ return false;
+ if (name[0] == 's' && name[1] == 'p') // sp
+ return false;
+ if (name[0] == 'l' && name[1] == 'r') // lr
+ return false;
+
+ if (name[0] == 'x' || name[0] == 'r') {
+ // Volatile registers: x0-x18
+ // Although documentation says only x19-28 + sp are callee saved We ll
+ // also have to treat x30 as non-volatile. Each dwarf frame has its own
+ // value of lr. Return false for the non-volatile gpr regs, true for
+ // everything else
+ switch (name[1]) {
+ case '1':
+ switch (name[2]) {
+ case '9':
+ return false; // x19 is non-volatile
+ default:
+ return true;
+ }
+ break;
+ case '2':
+ switch (name[2]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ return false; // x20 - 28 are non-volatile
+ case '9':
+ return false; // x29 aka fp treat as non-volatile
+ default:
+ return true;
+ }
+ case '3': // x30 (lr) and x31 (sp) treat as non-volatile
+ if (name[2] == '0' || name[2] == '1')
+ return false;
+ break;
+ default:
+ return true; // all volatile cases not handled above fall here.
+ }
+ } else if (name[0] == 'v' || name[0] == 's' || name[0] == 'd') {
+ // Volatile registers: v0-7, v16-v31
+ // Return false for non-volatile fp/SIMD regs, true for everything else
+ switch (name[1]) {
+ case '8':
+ case '9':
+ return false; // v8-v9 are non-volatile
+ case '1':
+ switch (name[2]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return false; // v10-v15 are non-volatile
+ default:
+ return true;
+ }
+ default:
+ return true;
+ }
+ }
+ }
+ return true;
+}
+
+static bool LoadValueFromConsecutiveGPRRegisters(
+ ExecutionContext &exe_ctx, RegisterContext *reg_ctx,
+ const CompilerType &value_type,
+ bool is_return_value, // false => parameter, true => return value
+ uint32_t &NGRN, // NGRN (see ABI documentation)
+ uint32_t &NSRN, // NSRN (see ABI documentation)
+ DataExtractor &data) {
+ llvm::Optional<uint64_t> byte_size = value_type.GetByteSize(nullptr);
+
+ if (byte_size || *byte_size == 0)
+ return false;
+
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder();
+ Status error;
+
+ CompilerType base_type;
+ const uint32_t homogeneous_count =
+ value_type.IsHomogeneousAggregate(&base_type);
+ if (homogeneous_count > 0 && homogeneous_count <= 8) {
+ // Make sure we have enough registers
+ if (NSRN < 8 && (8 - NSRN) >= homogeneous_count) {
+ if (!base_type)
+ return false;
+ llvm::Optional<uint64_t> base_byte_size = base_type.GetByteSize(nullptr);
+ if (!base_byte_size)
+ return false;
+ uint32_t data_offset = 0;
+
+ for (uint32_t i = 0; i < homogeneous_count; ++i) {
+ char v_name[8];
+ ::snprintf(v_name, sizeof(v_name), "v%u", NSRN);
+ const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoByName(v_name, 0);
+ if (reg_info == nullptr)
+ return false;
+
+ if (*base_byte_size > reg_info->byte_size)
+ return false;
+
+ RegisterValue reg_value;
+
+ if (!reg_ctx->ReadRegister(reg_info, reg_value))
+ return false;
+
+ // Make sure we have enough room in "heap_data_up"
+ if ((data_offset + *base_byte_size) <= heap_data_up->GetByteSize()) {
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, heap_data_up->GetBytes() + data_offset, *base_byte_size,
+ byte_order, error);
+ if (bytes_copied != *base_byte_size)
+ return false;
+ data_offset += bytes_copied;
+ ++NSRN;
+ } else
+ return false;
+ }
+ data.SetByteOrder(byte_order);
+ data.SetAddressByteSize(exe_ctx.GetProcessRef().GetAddressByteSize());
+ data.SetData(DataBufferSP(heap_data_up.release()));
+ return true;
+ }
+ }
+
+ const size_t max_reg_byte_size = 16;
+ if (*byte_size <= max_reg_byte_size) {
+ size_t bytes_left = *byte_size;
+ uint32_t data_offset = 0;
+ while (data_offset < *byte_size) {
+ if (NGRN >= 8)
+ return false;
+
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + NGRN);
+ if (reg_info == nullptr)
+ return false;
+
+ RegisterValue reg_value;
+
+ if (!reg_ctx->ReadRegister(reg_info, reg_value))
+ return false;
+
+ const size_t curr_byte_size = std::min<size_t>(8, bytes_left);
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, heap_data_up->GetBytes() + data_offset, curr_byte_size,
+ byte_order, error);
+ if (bytes_copied == 0)
+ return false;
+ if (bytes_copied >= bytes_left)
+ break;
+ data_offset += bytes_copied;
+ bytes_left -= bytes_copied;
+ ++NGRN;
+ }
+ } else {
+ const RegisterInfo *reg_info = nullptr;
+ if (is_return_value) {
+ // We are assuming we are decoding this immediately after returning from
+ // a function call and that the address of the structure is in x8
+ reg_info = reg_ctx->GetRegisterInfoByName("x8", 0);
+ } else {
+ // We are assuming we are stopped at the first instruction in a function
+ // and that the ABI is being respected so all parameters appear where
+ // they should be (functions with no external linkage can legally violate
+ // the ABI).
+ if (NGRN >= 8)
+ return false;
+
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + NGRN);
+ if (reg_info == nullptr)
+ return false;
+ ++NGRN;
+ }
+
+ if (reg_info == nullptr)
+ return false;
+
+ const lldb::addr_t value_addr =
+ reg_ctx->ReadRegisterAsUnsigned(reg_info, LLDB_INVALID_ADDRESS);
+
+ if (value_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (exe_ctx.GetProcessRef().ReadMemory(
+ value_addr, heap_data_up->GetBytes(), heap_data_up->GetByteSize(),
+ error) != heap_data_up->GetByteSize()) {
+ return false;
+ }
+ }
+
+ data.SetByteOrder(byte_order);
+ data.SetAddressByteSize(exe_ctx.GetProcessRef().GetAddressByteSize());
+ data.SetData(DataBufferSP(heap_data_up.release()));
+ return true;
+}
+
+ValueObjectSP ABISysV_arm64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ if (exe_ctx.GetTargetPtr() == nullptr || exe_ctx.GetProcessPtr() == nullptr)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType, return_compiler_type);
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo(nullptr);
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ // Extract the register context so we can read arguments from registers
+ if (*byte_size <= 8) {
+ const RegisterInfo *x0_reg_info = nullptr;
+ x0_reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1);
+ if (x0_reg_info) {
+ uint64_t raw_value =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(x0_reg_info,
+ 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+ case 16: // uint128_t
+ // In register x0 and x1
+ {
+ const RegisterInfo *x1_reg_info = nullptr;
+ x1_reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG2);
+
+ if (x1_reg_info) {
+ if (*byte_size <=
+ x0_reg_info->byte_size + x1_reg_info->byte_size) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order =
+ exe_ctx.GetProcessRef().GetByteOrder();
+ RegisterValue x0_reg_value;
+ RegisterValue x1_reg_value;
+ if (reg_ctx->ReadRegister(x0_reg_info, x0_reg_value) &&
+ reg_ctx->ReadRegister(x1_reg_info, x1_reg_value)) {
+ Status error;
+ if (x0_reg_value.GetAsMemoryData(
+ x0_reg_info, heap_data_up->GetBytes() + 0, 8,
+ byte_order, error) &&
+ x1_reg_value.GetAsMemoryData(
+ x1_reg_info, heap_data_up->GetBytes() + 8, 8,
+ byte_order, error)) {
+ DataExtractor data(
+ DataBufferSP(heap_data_up.release()), byte_order,
+ exe_ctx.GetProcessRef().GetAddressByteSize());
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ return return_valobj_sp;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ if (*byte_size <= sizeof(long double)) {
+ const RegisterInfo *v0_reg_info =
+ reg_ctx->GetRegisterInfoByName("v0", 0);
+ RegisterValue v0_value;
+ if (reg_ctx->ReadRegister(v0_reg_info, v0_value)) {
+ DataExtractor data;
+ if (v0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = data.GetDouble(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(long double)) {
+ value.GetScalar() = data.GetLongDouble(&offset);
+ success = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsVector && *byte_size <= 16) {
+ if (*byte_size > 0) {
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(v0_info, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(v0_info, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(), byte_order,
+ error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()), byte_order,
+ exe_ctx.GetProcessRef().GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass ||
+ (type_flags & eTypeIsVector && *byte_size > 16)) {
+ DataExtractor data;
+
+ uint32_t NGRN = 0; // Search ABI docs for NGRN
+ uint32_t NSRN = 0; // Search ABI docs for NSRN
+ const bool is_return_value = true;
+ if (LoadValueFromConsecutiveGPRRegisters(
+ exe_ctx, reg_ctx, return_compiler_type, is_return_value, NGRN, NSRN,
+ data)) {
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ return return_valobj_sp;
+}
+
+void ABISysV_arm64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "SysV ABI for AArch64 targets", CreateInstance);
+}
+
+void ABISysV_arm64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_arm64::GetPluginNameStatic() {
+ static ConstString g_name("SysV-arm64");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+ConstString ABISysV_arm64::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t ABISysV_arm64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h
new file mode 100644
index 000000000000..1fbdc793ed6e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h
@@ -0,0 +1,100 @@
+//===-- ABISysV_arm64.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABISysV_arm64_h_
+#define liblldb_ABISysV_arm64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_arm64 : public lldb_private::ABI {
+public:
+ ~ABISysV_arm64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The arm64 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ if (pc & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+
+ // Anything else if fair game..
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ bool GetPointerReturnRegister(const char *&name) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const override;
+
+private:
+ ABISysV_arm64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_arm64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.cpp
new file mode 100644
index 000000000000..93647564fe25
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.cpp
@@ -0,0 +1,1353 @@
+//===-- ABISysV_hexagon.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_hexagon.h"
+
+#include "llvm/ADT/Triple.h"
+#include "llvm/IR/DerivedTypes.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static RegisterInfo g_register_infos[] = {
+ // hexagon-core.xml
+ {"r00",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {0, 0, LLDB_INVALID_REGNUM, 0, 0},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r01",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {1, 1, LLDB_INVALID_REGNUM, 1, 1},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r02",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {2, 2, LLDB_INVALID_REGNUM, 2, 2},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r03",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {3, 3, LLDB_INVALID_REGNUM, 3, 3},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r04",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {4, 4, LLDB_INVALID_REGNUM, 4, 4},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r05",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {5, 5, LLDB_INVALID_REGNUM, 5, 5},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r06",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {6, 6, LLDB_INVALID_REGNUM, 6, 6},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r07",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {7, 7, LLDB_INVALID_REGNUM, 7, 7},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r08",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {8, 8, LLDB_INVALID_REGNUM, 8, 8},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r09",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {9, 9, LLDB_INVALID_REGNUM, 9, 9},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {10, 10, LLDB_INVALID_REGNUM, 10, 10},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {11, 11, LLDB_INVALID_REGNUM, 11, 11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {12, 12, LLDB_INVALID_REGNUM, 12, 12},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {13, 13, LLDB_INVALID_REGNUM, 13, 13},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {14, 14, LLDB_INVALID_REGNUM, 14, 14},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r15",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {15, 15, LLDB_INVALID_REGNUM, 15, 15},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r16",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {16, 16, LLDB_INVALID_REGNUM, 16, 16},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r17",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {17, 17, LLDB_INVALID_REGNUM, 17, 17},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r18",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {18, 18, LLDB_INVALID_REGNUM, 18, 18},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r19",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {19, 19, LLDB_INVALID_REGNUM, 19, 19},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r20",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {20, 20, LLDB_INVALID_REGNUM, 20, 20},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r21",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {21, 21, LLDB_INVALID_REGNUM, 21, 21},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r22",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {22, 22, LLDB_INVALID_REGNUM, 22, 22},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r23",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {23, 23, LLDB_INVALID_REGNUM, 23, 23},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r24",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {24, 24, LLDB_INVALID_REGNUM, 24, 24},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r25",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {25, 25, LLDB_INVALID_REGNUM, 25, 25},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r26",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {26, 26, LLDB_INVALID_REGNUM, 26, 26},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r27",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {27, 27, LLDB_INVALID_REGNUM, 27, 27},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r28",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {28, 28, LLDB_INVALID_REGNUM, 28, 28},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "r29",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {29, 29, LLDB_REGNUM_GENERIC_SP, 29, 29},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fp",
+ "r30",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {30, 30, LLDB_REGNUM_GENERIC_FP, 30, 30},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r31",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {31, 31, LLDB_REGNUM_GENERIC_RA, 31, 31},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sa0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {32, 32, LLDB_INVALID_REGNUM, 32, 32},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lc0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {33, 33, LLDB_INVALID_REGNUM, 33, 33},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sa1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {34, 34, LLDB_INVALID_REGNUM, 34, 34},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lc1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {35, 35, LLDB_INVALID_REGNUM, 35, 35},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // --> hexagon-v4/5/55/56-sim.xml
+ {"p3_0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {36, 36, LLDB_INVALID_REGNUM, 36, 36},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // PADDING {
+ {"p00",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {37, 37, LLDB_INVALID_REGNUM, 37, 37},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // }
+ {"m0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {38, 38, LLDB_INVALID_REGNUM, 38, 38},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"m1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {39, 39, LLDB_INVALID_REGNUM, 39, 39},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"usr",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {40, 40, LLDB_INVALID_REGNUM, 40, 40},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {41, 41, LLDB_REGNUM_GENERIC_PC, 41, 41},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ugp",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {42, 42, LLDB_INVALID_REGNUM, 42, 42},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"gp",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {43, 43, LLDB_INVALID_REGNUM, 43, 43},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cs0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {44, 44, LLDB_INVALID_REGNUM, 44, 44},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cs1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {45, 45, LLDB_INVALID_REGNUM, 45, 45},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // PADDING {
+ {"p01",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {46, 46, LLDB_INVALID_REGNUM, 46, 46},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p02",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {47, 47, LLDB_INVALID_REGNUM, 47, 47},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p03",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {48, 48, LLDB_INVALID_REGNUM, 48, 48},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p04",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {49, 49, LLDB_INVALID_REGNUM, 49, 49},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p05",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {50, 50, LLDB_INVALID_REGNUM, 50, 50},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p06",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {51, 51, LLDB_INVALID_REGNUM, 51, 51},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p07",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {52, 52, LLDB_INVALID_REGNUM, 52, 52},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p08",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {53, 53, LLDB_INVALID_REGNUM, 53, 53},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p09",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {54, 54, LLDB_INVALID_REGNUM, 54, 54},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p10",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {55, 55, LLDB_INVALID_REGNUM, 55, 55},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p11",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {56, 56, LLDB_INVALID_REGNUM, 56, 56},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p12",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {57, 57, LLDB_INVALID_REGNUM, 57, 57},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p13",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {58, 58, LLDB_INVALID_REGNUM, 58, 58},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p14",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {59, 59, LLDB_INVALID_REGNUM, 59, 59},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p15",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {60, 60, LLDB_INVALID_REGNUM, 60, 60},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p16",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {61, 61, LLDB_INVALID_REGNUM, 61, 61},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p17",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {62, 62, LLDB_INVALID_REGNUM, 62, 62},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p18",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {63, 63, LLDB_INVALID_REGNUM, 63, 63},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // }
+ {"sgp0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {64, 64, LLDB_INVALID_REGNUM, 64, 64},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // PADDING {
+ {"p19",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {65, 65, LLDB_INVALID_REGNUM, 65, 65},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // }
+ {"stid",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {66, 66, LLDB_INVALID_REGNUM, 66, 66},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"elr",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {67, 67, LLDB_INVALID_REGNUM, 67, 67},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"badva0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {68, 68, LLDB_INVALID_REGNUM, 68, 68},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"badva1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {69, 69, LLDB_INVALID_REGNUM, 69, 69},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ssr",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {70, 70, LLDB_INVALID_REGNUM, 70, 70},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ccr",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {71, 71, LLDB_INVALID_REGNUM, 71, 71},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"htid",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {72, 72, LLDB_INVALID_REGNUM, 72, 72},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // PADDING {
+ {"p20",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {73, 73, LLDB_INVALID_REGNUM, 73, 73},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // }
+ {"imask",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {74, 74, LLDB_INVALID_REGNUM, 74, 74},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // PADDING {
+ {"p21",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {75, 75, LLDB_INVALID_REGNUM, 75, 75},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p22",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {76, 76, LLDB_INVALID_REGNUM, 76, 76},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p23",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {77, 77, LLDB_INVALID_REGNUM, 77, 77},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p24",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {78, 78, LLDB_INVALID_REGNUM, 78, 78},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p25",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {79, 79, LLDB_INVALID_REGNUM, 79, 79},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // }
+ {"g0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {80, 80, LLDB_INVALID_REGNUM, 80, 80},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"g1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {81, 81, LLDB_INVALID_REGNUM, 81, 81},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"g2",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {82, 82, LLDB_INVALID_REGNUM, 82, 82},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"g3",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {83, 83, LLDB_INVALID_REGNUM, 83, 83},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ sizeof(g_register_infos) / sizeof(RegisterInfo);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_hexagon::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+/*
+ http://en.wikipedia.org/wiki/Red_zone_%28computing%29
+
+ In computing, a red zone is a fixed size area in memory beyond the stack
+ pointer that has not been
+ "allocated". This region of memory is not to be modified by
+ interrupt/exception/signal handlers.
+ This allows the space to be used for temporary data without the extra
+ overhead of modifying the
+ stack pointer. The x86-64 ABI mandates a 128 byte red zone.[1] The OpenRISC
+ toolchain assumes a
+ 128 byte red zone though it is not documented.
+*/
+size_t ABISysV_hexagon::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABISysV_hexagon::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::hexagon) {
+ return ABISP(new ABISysV_hexagon(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABISysV_hexagon::PrepareTrivialCall(Thread &thread, lldb::addr_t sp,
+ lldb::addr_t pc, lldb::addr_t ra,
+ llvm::ArrayRef<addr_t> args) const {
+ // we don't use the traditional trivial call specialized for jit
+ return false;
+}
+
+/*
+
+// AD:
+// . safeguard the current stack
+// . how can we know that the called function will create its own frame
+properly?
+// . we could manually make a new stack first:
+// 2. push RA
+// 3. push FP
+// 4. FP = SP
+// 5. SP = SP ( since no locals in our temp frame )
+
+// AD 6/05/2014
+// . variable argument list parameters are not passed via registers, they are
+passed on
+// the stack. This presents us with a problem, since we need to know when
+the valist
+// starts. Currently I can find out if a function is varg, but not how many
+// real parameters it takes. Thus I don't know when to start spilling the
+vargs. For
+// the time being, to progress, I will assume that it takes on real parameter
+before
+// the vargs list starts.
+
+// AD 06/05/2014
+// . how do we adhere to the stack alignment requirements
+
+// AD 06/05/2014
+// . handle 64bit values and their register / stack requirements
+
+*/
+#define HEX_ABI_DEBUG 0
+bool ABISysV_hexagon::PrepareTrivialCall(
+ Thread &thread, lldb::addr_t sp, lldb::addr_t pc, lldb::addr_t ra,
+ llvm::Type &prototype, llvm::ArrayRef<ABI::CallArgument> args) const {
+ // default number of register passed arguments for varg functions
+ const int nVArgRegParams = 1;
+ Status error;
+
+ // grab the process so we have access to the memory for spilling
+ lldb::ProcessSP proc = thread.GetProcess();
+
+ // get the register context for modifying all of the registers
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ uint32_t pc_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ if (pc_reg == LLDB_INVALID_REGNUM)
+ return false;
+
+ uint32_t ra_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+ if (ra_reg == LLDB_INVALID_REGNUM)
+ return false;
+
+ uint32_t sp_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ if (sp_reg == LLDB_INVALID_REGNUM)
+ return false;
+
+ // push host data onto target
+ for (size_t i = 0; i < args.size(); i++) {
+ const ABI::CallArgument &arg = args[i];
+ // skip over target values
+ if (arg.type == ABI::CallArgument::TargetValue)
+ continue;
+ // round up to 8 byte multiple
+ size_t argSize = (arg.size | 0x7) + 1;
+
+ // create space on the stack for this data
+ sp -= argSize;
+
+ // write this argument onto the stack of the host process
+ proc->WriteMemory(sp, arg.data_up.get(), arg.size, error);
+ if (error.Fail())
+ return false;
+
+ // update the argument with the target pointer
+ // XXX: This is a gross hack for getting around the const
+ *const_cast<lldb::addr_t *>(&arg.value) = sp;
+ }
+
+#if HEX_ABI_DEBUG
+ // print the original stack pointer
+ printf("sp : %04" PRIx64 " \n", sp);
+#endif
+
+ // make sure number of parameters matches prototype
+ assert(prototype.getFunctionNumParams() == args.size());
+
+ // check if this is a variable argument function
+ bool isVArg = prototype.isFunctionVarArg();
+
+ // number of arguments passed by register
+ int nRegArgs = nVArgRegParams;
+ if (!isVArg) {
+ // number of arguments is limited by [R0 : R5] space
+ nRegArgs = args.size();
+ if (nRegArgs > 6)
+ nRegArgs = 6;
+ }
+
+ // pass arguments that are passed via registers
+ for (int i = 0; i < nRegArgs; i++) {
+ // get the parameter as a u32
+ uint32_t param = (uint32_t)args[i].value;
+ // write argument into register
+ if (!reg_ctx->WriteRegisterFromUnsigned(i, param))
+ return false;
+ }
+
+ // number of arguments to spill onto stack
+ int nSpillArgs = args.size() - nRegArgs;
+ // make space on the stack for arguments
+ sp -= 4 * nSpillArgs;
+ // align stack on an 8 byte boundary
+ if (sp & 7)
+ sp -= 4;
+
+ // arguments that are passed on the stack
+ for (size_t i = nRegArgs, offs = 0; i < args.size(); i++) {
+ // get the parameter as a u32
+ uint32_t param = (uint32_t)args[i].value;
+ // write argument to stack
+ proc->WriteMemory(sp + offs, (void *)&param, sizeof(param), error);
+ if (!error.Success())
+ return false;
+ //
+ offs += 4;
+ }
+
+ // update registers with current function call state
+ reg_ctx->WriteRegisterFromUnsigned(pc_reg, pc);
+ reg_ctx->WriteRegisterFromUnsigned(ra_reg, ra);
+ reg_ctx->WriteRegisterFromUnsigned(sp_reg, sp);
+
+#if HEX_ABI_DEBUG
+ // quick and dirty stack dumper for debugging
+ for (int i = -8; i < 8; i++) {
+ uint32_t data = 0;
+ lldb::addr_t addr = sp + i * 4;
+ proc->ReadMemory(addr, (void *)&data, sizeof(data), error);
+ printf("\n0x%04" PRIx64 " 0x%08x ", addr, data);
+ if (i == 0)
+ printf("<<-- sp");
+ }
+ printf("\n");
+#endif
+
+ return true;
+}
+
+bool ABISysV_hexagon::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ return false;
+}
+
+Status
+ABISysV_hexagon::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ return error;
+}
+
+ValueObjectSP ABISysV_hexagon::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_hexagon::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ return return_valobj_sp;
+}
+
+// called when we are on the first instruction of a new function for hexagon
+// the return address is in RA (R31)
+bool ABISysV_hexagon::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindGeneric);
+ unwind_plan.SetReturnAddressRegister(LLDB_REGNUM_GENERIC_RA);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(LLDB_REGNUM_GENERIC_SP, 4);
+ row->SetOffset(0);
+
+ // The previous PC is in the LR
+ row->SetRegisterLocationToRegister(LLDB_REGNUM_GENERIC_PC,
+ LLDB_REGNUM_GENERIC_RA, true);
+ unwind_plan.AppendRow(row);
+
+ unwind_plan.SetSourceName("hexagon at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+bool ABISysV_hexagon::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindGeneric);
+
+ uint32_t fp_reg_num = LLDB_REGNUM_GENERIC_FP;
+ uint32_t sp_reg_num = LLDB_REGNUM_GENERIC_SP;
+ uint32_t pc_reg_num = LLDB_REGNUM_GENERIC_PC;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(LLDB_REGNUM_GENERIC_FP, 8);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, -8, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("hexagon default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+/*
+ Register Usage Saved By
+
+ R0 - R5 parameters(a) -
+ R6 - R15 Scratch(b) Caller
+ R16 - R27 Scratch Callee
+ R28 Scratch(b) Caller
+ R29 - R31 Stack Frames Callee(c)
+ P3:0 Processor State Caller
+
+ a = the caller can change parameter values
+ b = R14 - R15 and R28 are used by the procedure linkage table
+ c = R29 - R31 are saved and restored by allocframe() and deallocframe()
+*/
+bool ABISysV_hexagon::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+bool ABISysV_hexagon::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ int reg = ((reg_info->byte_offset) / 4);
+
+ bool save = (reg >= 16) && (reg <= 27);
+ save |= (reg >= 29) && (reg <= 32);
+
+ return save;
+}
+
+void ABISysV_hexagon::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "System V ABI for hexagon targets",
+ CreateInstance);
+}
+
+void ABISysV_hexagon::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_hexagon::GetPluginNameStatic() {
+ static ConstString g_name("sysv-hexagon");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_hexagon::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_hexagon::GetPluginVersion() { return 1; }
+
+// get value object specialized to work with llvm IR types
+lldb::ValueObjectSP
+ABISysV_hexagon::GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ llvm::Type &retType) const {
+ Value value;
+ ValueObjectSP vObjSP;
+
+ // get the current register context
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return vObjSP;
+
+ // for now just pop R0 to find the return value
+ const lldb_private::RegisterInfo *r0_info =
+ reg_ctx->GetRegisterInfoAtIndex(0);
+ if (r0_info == nullptr)
+ return vObjSP;
+
+ // void return type
+ if (retType.isVoidTy()) {
+ value.GetScalar() = 0;
+ }
+ // integer / pointer return type
+ else if (retType.isIntegerTy() || retType.isPointerTy()) {
+ // read r0 register value
+ lldb_private::RegisterValue r0_value;
+ if (!reg_ctx->ReadRegister(r0_info, r0_value))
+ return vObjSP;
+
+ // push r0 into value
+ uint32_t r0_u32 = r0_value.GetAsUInt32();
+
+ // account for integer size
+ if (retType.isIntegerTy() && retType.isSized()) {
+ uint64_t size = retType.getScalarSizeInBits();
+ uint64_t mask = (1ull << size) - 1;
+ // mask out higher order bits then the type we expect
+ r0_u32 &= mask;
+ }
+
+ value.GetScalar() = r0_u32;
+ }
+ // unsupported return type
+ else
+ return vObjSP;
+
+ // pack the value into a ValueObjectSP
+ vObjSP = ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(),
+ value, ConstString(""));
+ return vObjSP;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h
new file mode 100644
index 000000000000..459b6315dba2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h
@@ -0,0 +1,105 @@
+//===-- ABISysV_hexagon.h ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABISysV_hexagon_h_
+#define liblldb_ABISysV_hexagon_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_hexagon : public lldb_private::ABI {
+public:
+ ~ABISysV_hexagon() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ // special thread plan for GDB style non-jit function calls
+ bool
+ PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress, lldb::addr_t returnAddress,
+ llvm::Type &prototype,
+ llvm::ArrayRef<ABI::CallArgument> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ // specialized to work with llvm IR types
+ lldb::ValueObjectSP GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ llvm::Type &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & 0x07)
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // We have a 64 bit address space, so anything is valid as opcodes
+ // aren't fixed width...
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_hexagon(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_hexagon_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.cpp
new file mode 100644
index 000000000000..05f5dba90687
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.cpp
@@ -0,0 +1,849 @@
+//===----------------------- ABISysV_i386.cpp -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_i386.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// This source file uses the following document as a reference:
+//====================================================================
+// System V Application Binary Interface
+// Intel386 Architecture Processor Supplement, Version 1.0
+// Edited by
+// H.J. Lu, David L Kreitzer, Milind Girkar, Zia Ansari
+//
+// (Based on
+// System V Application Binary Interface,
+// AMD64 Architecture Processor Supplement,
+// Edited by
+// H.J. Lu, Michael Matz, Milind Girkar, Jan Hubicka,
+// Andreas Jaeger, Mark Mitchell)
+//
+// February 3, 2015
+//====================================================================
+
+// DWARF Register Number Mapping
+// See Table 2.14 of the reference document (specified on top of this file)
+// Comment: Table 2.14 is followed till 'mm' entries. After that, all entries
+// are ignored here.
+
+enum dwarf_regnums {
+ dwarf_eax = 0,
+ dwarf_ecx,
+ dwarf_edx,
+ dwarf_ebx,
+ dwarf_esp,
+ dwarf_ebp,
+ dwarf_esi,
+ dwarf_edi,
+ dwarf_eip,
+ dwarf_eflags,
+
+ dwarf_st0 = 11,
+ dwarf_st1,
+ dwarf_st2,
+ dwarf_st3,
+ dwarf_st4,
+ dwarf_st5,
+ dwarf_st6,
+ dwarf_st7,
+
+ dwarf_xmm0 = 21,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7,
+ dwarf_ymm0 = dwarf_xmm0,
+ dwarf_ymm1 = dwarf_xmm1,
+ dwarf_ymm2 = dwarf_xmm2,
+ dwarf_ymm3 = dwarf_xmm3,
+ dwarf_ymm4 = dwarf_xmm4,
+ dwarf_ymm5 = dwarf_xmm5,
+ dwarf_ymm6 = dwarf_xmm6,
+ dwarf_ymm7 = dwarf_xmm7,
+
+ dwarf_mm0 = 29,
+ dwarf_mm1,
+ dwarf_mm2,
+ dwarf_mm3,
+ dwarf_mm4,
+ dwarf_mm5,
+ dwarf_mm6,
+ dwarf_mm7,
+
+ dwarf_bnd0 = 101,
+ dwarf_bnd1,
+ dwarf_bnd2,
+ dwarf_bnd3
+};
+
+static RegisterInfo g_register_infos[] = {
+ // clang-format off
+ //NAME ALT SZ OFF ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB NATIVE VALUE INVAL DYN EXPR SZ
+ //========== ======= == === ============= ==================== =================== =================== ========================= =================== =================== ======= ======= ======== ==
+ {"eax", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_eax, dwarf_eax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ebx", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ecx", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_ecx, dwarf_ecx, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"edx", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_edx, dwarf_edx, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"esi", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_esi, dwarf_esi, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"edi", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_edi, dwarf_edi, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ebp", "fp", 4, 0, eEncodingUint, eFormatHex, {dwarf_ebp, dwarf_ebp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"esp", "sp", 4, 0, eEncodingUint, eFormatHex, {dwarf_esp, dwarf_esp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"eip", "pc", 4, 0, eEncodingUint, eFormatHex, {dwarf_eip, dwarf_eip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"eflags", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS,LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"cs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ss", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ds", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"es", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"gs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st0", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st1", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st2", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st3", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st4", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st5", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st6", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st7", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fctrl", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fstat", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ftag", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fiseg", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fioff", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"foseg", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fooff", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fop", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm4", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm5", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm6", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm7", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"mxcsr", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm0", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm1", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm2", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm3", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm4", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm5", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm6", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm7", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64,{dwarf_bnd0, dwarf_bnd0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64,{dwarf_bnd1, dwarf_bnd1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64,{dwarf_bnd2, dwarf_bnd2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64,{dwarf_bnd3, dwarf_bnd3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bndcfgu", nullptr, 8, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bndstatus",nullptr, 8, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}
+ // clang-format on
+};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_i386::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+// Static Functions
+
+ABISP
+ABISysV_i386::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().getVendor() != llvm::Triple::Apple) {
+ if (arch.GetTriple().getArch() == llvm::Triple::x86) {
+ return ABISP(new ABISysV_i386(process_sp));
+ }
+ }
+ return ABISP();
+}
+
+bool ABISysV_i386::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+
+ // While using register info to write a register value to memory, the
+ // register info just needs to have the correct size of a 32 bit register,
+ // the actual register it pertains to is not important, just the size needs
+ // to be correct. "eax" is used here for this purpose.
+ const RegisterInfo *reg_info_32 = reg_ctx->GetRegisterInfoByName("eax");
+ if (!reg_info_32)
+ return false; // TODO this should actually never happen
+
+ Status error;
+ RegisterValue reg_value;
+
+ // Make room for the argument(s) on the stack
+ sp -= 4 * args.size();
+
+ // SP Alignment
+ sp &= ~(16ull - 1ull); // 16-byte alignment
+
+ // Write arguments onto the stack
+ addr_t arg_pos = sp;
+ for (addr_t arg : args) {
+ reg_value.SetUInt32(arg);
+ error = reg_ctx->WriteRegisterValueToMemory(
+ reg_info_32, arg_pos, reg_info_32->byte_size, reg_value);
+ if (error.Fail())
+ return false;
+ arg_pos += 4;
+ }
+
+ // The return address is pushed onto the stack
+ sp -= 4;
+ reg_value.SetUInt32(return_addr);
+ error = reg_ctx->WriteRegisterValueToMemory(
+ reg_info_32, sp, reg_info_32->byte_size, reg_value);
+ if (error.Fail())
+ return false;
+
+ // Setting %esp to the actual stack value.
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp))
+ return false;
+
+ // Setting %eip to the address of the called function.
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Process *process,
+ addr_t &current_stack_argument) {
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+
+ if (!process)
+ return false;
+
+ if (process->ReadScalarIntegerFromMemory(current_stack_argument, byte_size,
+ is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+}
+
+bool ABISysV_i386::GetArgumentValues(Thread &thread, ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get pointer to the first stack argument
+ addr_t sp = reg_ctx->GetSP(0);
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 4; // jump over return address
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // Currently: Support for extracting values with Clang QualTypes only.
+ CompilerType compiler_type(value->GetCompilerType());
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (bit_size) {
+ bool is_signed;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed,
+ thread.GetProcess().get(), current_stack_argument);
+ } else if (compiler_type.IsPointerType()) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false,
+ thread.GetProcess().get(), current_stack_argument);
+ }
+ }
+ }
+ return true;
+}
+
+Status ABISysV_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ const uint32_t type_flags = compiler_type.GetTypeInfo();
+ Thread *thread = frame_sp->GetThread().get();
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ bool register_write_successful = true;
+
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ // Following "IF ELSE" block categorizes various 'Fundamental Data Types'.
+ // The terminology 'Fundamental Data Types' used here is adopted from Table
+ // 2.1 of the reference document (specified on top of this file)
+
+ if (type_flags & eTypeIsPointer) // 'Pointer'
+ {
+ if (num_bytes != sizeof(uint32_t)) {
+ error.SetErrorString("Pointer to be returned is not 4 bytes wide");
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0);
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+ register_write_successful =
+ reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value);
+ } else if ((type_flags & eTypeIsScalar) ||
+ (type_flags & eTypeIsEnumeration)) //'Integral' + 'Floating Point'
+ {
+ lldb::offset_t offset = 0;
+ const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0);
+
+ if (type_flags & eTypeIsInteger) // 'Integral' except enum
+ {
+ switch (num_bytes) {
+ default:
+ break;
+ case 16:
+ // For clang::BuiltinType::UInt128 & Int128 ToDo: Need to decide how to
+ // handle it
+ break;
+ case 8: {
+ uint32_t raw_value_low = data.GetMaxU32(&offset, 4);
+ const RegisterInfo *edx_info = reg_ctx->GetRegisterInfoByName("edx", 0);
+ uint32_t raw_value_high = data.GetMaxU32(&offset, num_bytes - offset);
+ register_write_successful =
+ (reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value_low) &&
+ reg_ctx->WriteRegisterFromUnsigned(edx_info, raw_value_high));
+ break;
+ }
+ case 4:
+ case 2:
+ case 1: {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+ register_write_successful =
+ reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value);
+ break;
+ }
+ }
+ } else if (type_flags & eTypeIsEnumeration) // handles enum
+ {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+ register_write_successful =
+ reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value);
+ } else if (type_flags & eTypeIsFloat) // 'Floating Point'
+ {
+ RegisterValue st0_value, fstat_value, ftag_value;
+ const RegisterInfo *st0_info = reg_ctx->GetRegisterInfoByName("st0", 0);
+ const RegisterInfo *fstat_info =
+ reg_ctx->GetRegisterInfoByName("fstat", 0);
+ const RegisterInfo *ftag_info = reg_ctx->GetRegisterInfoByName("ftag", 0);
+
+ /* According to Page 3-12 of document
+ System V Application Binary Interface, Intel386 Architecture Processor
+ Supplement, Fourth Edition
+ To return Floating Point values, all st% registers except st0 should be
+ empty after exiting from
+ a function. This requires setting fstat and ftag registers to specific
+ values.
+ fstat: The TOP field of fstat should be set to a value [0,7]. ABI doesn't
+ specify the specific
+ value of TOP in case of function return. Hence, we set the TOP field to 7
+ by our choice. */
+ uint32_t value_fstat_u32 = 0x00003800;
+
+ /* ftag: Implication of setting TOP to 7 and indicating all st% registers
+ empty except st0 is to set
+ 7th bit of 4th byte of FXSAVE area to 1 and all other bits of this byte to
+ 0. This is in accordance
+ with the document Intel 64 and IA-32 Architectures Software Developer's
+ Manual, January 2015 */
+ uint32_t value_ftag_u32 = 0x00000080;
+
+ if (num_bytes <= 12) // handles float, double, long double, __float80
+ {
+ long double value_long_dbl = 0.0;
+ if (num_bytes == 4)
+ value_long_dbl = data.GetFloat(&offset);
+ else if (num_bytes == 8)
+ value_long_dbl = data.GetDouble(&offset);
+ else if (num_bytes == 12)
+ value_long_dbl = data.GetLongDouble(&offset);
+ else {
+ error.SetErrorString("Invalid number of bytes for this return type");
+ return error;
+ }
+ st0_value.SetLongDouble(value_long_dbl);
+ fstat_value.SetUInt32(value_fstat_u32);
+ ftag_value.SetUInt32(value_ftag_u32);
+ register_write_successful =
+ reg_ctx->WriteRegister(st0_info, st0_value) &&
+ reg_ctx->WriteRegister(fstat_info, fstat_value) &&
+ reg_ctx->WriteRegister(ftag_info, ftag_value);
+ } else if (num_bytes == 16) // handles __float128
+ {
+ error.SetErrorString("Implementation is missing for this clang type.");
+ }
+ } else {
+ // Neither 'Integral' nor 'Floating Point'. If flow reaches here then
+ // check type_flags. This type_flags is not a valid type.
+ error.SetErrorString("Invalid clang type");
+ }
+ } else {
+ /* 'Complex Floating Point', 'Packed', 'Decimal Floating Point' and
+ 'Aggregate' data types
+ are yet to be implemented */
+ error.SetErrorString("Currently only Integral and Floating Point clang "
+ "types are supported.");
+ }
+ if (!register_write_successful)
+ error.SetErrorString("Register writing failed");
+ return error;
+}
+
+ValueObjectSP ABISysV_i386::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo();
+
+ unsigned eax_id =
+ reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];
+ unsigned edx_id =
+ reg_ctx->GetRegisterInfoByName("edx", 0)->kinds[eRegisterKindLLDB];
+
+ // Following "IF ELSE" block categorizes various 'Fundamental Data Types'.
+ // The terminology 'Fundamental Data Types' used here is adopted from Table
+ // 2.1 of the reference document (specified on top of this file)
+
+ if (type_flags & eTypeIsPointer) // 'Pointer'
+ {
+ uint32_t ptr =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff;
+ value.SetValueType(Value::eValueTypeScalar);
+ value.GetScalar() = ptr;
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if ((type_flags & eTypeIsScalar) ||
+ (type_flags & eTypeIsEnumeration)) //'Integral' + 'Floating Point'
+ {
+ value.SetValueType(Value::eValueTypeScalar);
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ bool success = false;
+
+ if (type_flags & eTypeIsInteger) // 'Integral' except enum
+ {
+ const bool is_signed = ((type_flags & eTypeIsSigned) != 0);
+ uint64_t raw_value =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff;
+ raw_value |=
+ (thread.GetRegisterContext()->ReadRegisterAsUnsigned(edx_id, 0) &
+ 0xffffffff)
+ << 32;
+
+ switch (*byte_size) {
+ default:
+ break;
+
+ case 16:
+ // For clang::BuiltinType::UInt128 & Int128 ToDo: Need to decide how to
+ // handle it
+ break;
+
+ case 8:
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case 4:
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case 2:
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case 1:
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsEnumeration) // handles enum
+ {
+ uint32_t enm =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff;
+ value.SetValueType(Value::eValueTypeScalar);
+ value.GetScalar() = enm;
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsFloat) // 'Floating Point'
+ {
+ if (*byte_size <= 12) // handles float, double, long double, __float80
+ {
+ const RegisterInfo *st0_info = reg_ctx->GetRegisterInfoByName("st0", 0);
+ RegisterValue st0_value;
+
+ if (reg_ctx->ReadRegister(st0_info, st0_value)) {
+ DataExtractor data;
+ if (st0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ long double value_long_double = data.GetLongDouble(&offset);
+
+ // float is 4 bytes.
+ if (*byte_size == 4) {
+ float value_float = (float)value_long_double;
+ value.GetScalar() = value_float;
+ success = true;
+ } else if (*byte_size == 8) {
+ // double is 8 bytes
+ // On Android Platform: long double is also 8 bytes It will be
+ // handled here only.
+ double value_double = (double)value_long_double;
+ value.GetScalar() = value_double;
+ success = true;
+ } else if (*byte_size == 12) {
+ // long double and __float80 are 12 bytes on i386.
+ value.GetScalar() = value_long_double;
+ success = true;
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (*byte_size == 16) // handles __float128
+ {
+ lldb::addr_t storage_addr = (uint32_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+ } else // Neither 'Integral' nor 'Floating Point'
+ {
+ // If flow reaches here then check type_flags This type_flags is
+ // unhandled
+ }
+ } else if (type_flags & eTypeIsComplex) // 'Complex Floating Point'
+ {
+ // ToDo: Yet to be implemented
+ } else if (type_flags & eTypeIsVector) // 'Packed'
+ {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size > 0) {
+ const RegisterInfo *vec_reg = reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ if (vec_reg == nullptr)
+ vec_reg = reg_ctx->GetRegisterInfoByName("mm0", 0);
+
+ if (vec_reg) {
+ if (*byte_size <= vec_reg->byte_size) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(vec_reg, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(vec_reg, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(),
+ byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ } else if (*byte_size <= vec_reg->byte_size * 2) {
+ const RegisterInfo *vec_reg2 =
+ reg_ctx->GetRegisterInfoByName("xmm1", 0);
+ if (vec_reg2) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ RegisterValue reg_value2;
+ if (reg_ctx->ReadRegister(vec_reg, reg_value) &&
+ reg_ctx->ReadRegister(vec_reg2, reg_value2)) {
+
+ Status error;
+ if (reg_value.GetAsMemoryData(vec_reg, heap_data_up->GetBytes(),
+ vec_reg->byte_size, byte_order,
+ error) &&
+ reg_value2.GetAsMemoryData(
+ vec_reg2, heap_data_up->GetBytes() + vec_reg->byte_size,
+ heap_data_up->GetByteSize() - vec_reg->byte_size,
+ byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else // 'Decimal Floating Point'
+ {
+ // ToDo: Yet to be implemented
+ }
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_i386::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type);
+ if (return_valobj_sp)
+ return return_valobj_sp;
+
+ RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
+ if (!reg_ctx_sp)
+ return return_valobj_sp;
+
+ if (return_compiler_type.IsAggregateType()) {
+ unsigned eax_id =
+ reg_ctx_sp->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];
+ lldb::addr_t storage_addr = (uint32_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+
+ return return_valobj_sp;
+}
+
+// This defines CFA as esp+4
+// The saved pc is at CFA-4 (i.e. esp+0)
+// The saved esp is CFA+0
+
+bool ABISysV_i386::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num = dwarf_esp;
+ uint32_t pc_reg_num = dwarf_eip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 4);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("i386 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+// This defines CFA as ebp+8
+// The saved pc is at CFA-4 (i.e. ebp+4)
+// The saved ebp is at CFA-8 (i.e. ebp+0)
+// The saved esp is CFA+0
+
+bool ABISysV_i386::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = dwarf_ebp;
+ uint32_t sp_reg_num = dwarf_esp;
+ uint32_t pc_reg_num = dwarf_eip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 4;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("i386 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+// According to "Register Usage" in reference document (specified on top of
+// this source file) ebx, ebp, esi, edi and esp registers are preserved i.e.
+// non-volatile i.e. callee-saved on i386
+bool ABISysV_i386::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (!reg_info)
+ return false;
+
+ // Saved registers are ebx, ebp, esi, edi, esp, eip
+ const char *name = reg_info->name;
+ if (name[0] == 'e') {
+ switch (name[1]) {
+ case 'b':
+ if (name[2] == 'x' || name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ case 'd':
+ if (name[2] == 'i')
+ return name[3] == '\0';
+ break;
+ case 'i':
+ if (name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ case 's':
+ if (name[2] == 'i' || name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ }
+ }
+
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp
+ return true;
+ if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc
+ return true;
+
+ return false;
+}
+
+void ABISysV_i386::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for i386 targets", CreateInstance);
+}
+
+void ABISysV_i386::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_i386::GetPluginNameStatic() {
+ static ConstString g_name("sysv-i386");
+ return g_name;
+}
+
+lldb_private::ConstString ABISysV_i386::GetPluginName() {
+ return GetPluginNameStatic();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.h
new file mode 100644
index 000000000000..982bdd676b74
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.h
@@ -0,0 +1,108 @@
+//===------------------- ABISysV_i386.h -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABISysV_i386_h_
+#define liblldb_ABISysV_i386_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_i386 : public lldb_private::ABI {
+public:
+ ~ABISysV_i386() override = default;
+
+ size_t GetRedZoneSize() const override {
+ return 0; // There is no red zone for i386 Architecture
+ }
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override {
+ return !RegisterIsCalleeSaved(reg_info);
+ }
+
+ // The SysV i386 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (4-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+
+ // ToDo: When __m256 arguments are passed then stack frames should be
+ // 32 byte aligned. Decide what to do for 32 byte alignment checking
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 4 byte aligned
+ if (cfa & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Check whether the address is a valid 32 bit address
+ return (pc <= UINT32_MAX);
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ // PluginInterface protocol
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_i386(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_i386_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.cpp
new file mode 100644
index 000000000000..121c7300b968
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.cpp
@@ -0,0 +1,1069 @@
+//===-- ABISysV_mips.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_mips.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ dwarf_r0 = 0,
+ dwarf_r1,
+ dwarf_r2,
+ dwarf_r3,
+ dwarf_r4,
+ dwarf_r5,
+ dwarf_r6,
+ dwarf_r7,
+ dwarf_r8,
+ dwarf_r9,
+ dwarf_r10,
+ dwarf_r11,
+ dwarf_r12,
+ dwarf_r13,
+ dwarf_r14,
+ dwarf_r15,
+ dwarf_r16,
+ dwarf_r17,
+ dwarf_r18,
+ dwarf_r19,
+ dwarf_r20,
+ dwarf_r21,
+ dwarf_r22,
+ dwarf_r23,
+ dwarf_r24,
+ dwarf_r25,
+ dwarf_r26,
+ dwarf_r27,
+ dwarf_r28,
+ dwarf_r29,
+ dwarf_r30,
+ dwarf_r31,
+ dwarf_sr,
+ dwarf_lo,
+ dwarf_hi,
+ dwarf_bad,
+ dwarf_cause,
+ dwarf_pc
+};
+
+static const RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME
+ // DWARF GENERIC PROCESS PLUGINS
+ // LLDB NATIVE VALUE REGS INVALIDATE REGS
+ // ======== ====== == === ============= =========== ============
+ // ============== ============ =================
+ // =================== ========== =================
+ {"r0",
+ "zero",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r0, dwarf_r0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ "AT",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r1, dwarf_r1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ "v0",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r2, dwarf_r2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ "v1",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r3, dwarf_r3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ "arg1",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r4, dwarf_r4, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ "arg2",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r5, dwarf_r5, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ "arg3",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r6, dwarf_r6, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ "arg4",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r7, dwarf_r7, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ "arg5",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ "arg6",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ "arg7",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ "arg8",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r15",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r16",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r16, dwarf_r16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r17",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r17, dwarf_r17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r18",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r18, dwarf_r18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r19",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r19, dwarf_r19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r20",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r20, dwarf_r20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r21",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r21, dwarf_r21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r22",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r22, dwarf_r22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r23",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r23, dwarf_r23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r24",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r24, dwarf_r24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r25",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r25, dwarf_r25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r26",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r26, dwarf_r26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r27",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r27, dwarf_r27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r28",
+ "gp",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r28, dwarf_r28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r29",
+ "sp",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r29, dwarf_r29, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r30",
+ "fp",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r30, dwarf_r30, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r31",
+ "ra",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r31, dwarf_r31, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_sr, dwarf_sr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lo",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_lo, dwarf_lo, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"hi",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_hi, dwarf_hi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bad",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_bad, dwarf_bad, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cause",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_cause, dwarf_cause, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+
+const lldb_private::RegisterInfo *
+ABISysV_mips::GetRegisterInfoArray(uint32_t &count) {
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABISysV_mips::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABISysV_mips::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ if ((arch_type == llvm::Triple::mips) ||
+ (arch_type == llvm::Triple::mipsel)) {
+ return ABISP(new ABISysV_mips(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABISysV_mips::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_mips::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%zd = 0x%" PRIx64, i + 1, args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ RegisterValue reg_value;
+
+ // Argument registers
+ const char *reg_names[] = {"r4", "r5", "r6", "r7"};
+
+ llvm::ArrayRef<addr_t>::iterator ai = args.begin(), ae = args.end();
+
+ // Write arguments to registers
+ for (size_t i = 0; i < llvm::array_lengthof(reg_names); ++i) {
+ if (ai == ae)
+ break;
+
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%zd (0x%" PRIx64 ") into %s", i + 1,
+ args[i], reg_info->name);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+
+ ++ai;
+ }
+
+ // If we have more than 4 arguments --Spill onto the stack
+ if (ai != ae) {
+ // No of arguments to go on stack
+ size_t num_stack_regs = args.size();
+
+ // Allocate needed space for args on the stack
+ sp -= (num_stack_regs * 4);
+
+ // Keep the stack 8 byte aligned
+ sp &= ~(8ull - 1ull);
+
+ // just using arg1 to get the right size
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+
+ addr_t arg_pos = sp + 16;
+
+ size_t i = 4;
+ for (; ai != ae; ++ai) {
+ reg_value.SetUInt32(*ai);
+ if (log)
+ log->Printf("About to write arg%zd (0x%" PRIx64 ") at 0x%" PRIx64 "",
+ i + 1, args[i], arg_pos);
+
+ if (reg_ctx
+ ->WriteRegisterValueToMemory(reg_info, arg_pos,
+ reg_info->byte_size, reg_value)
+ .Fail())
+ return false;
+ arg_pos += reg_info->byte_size;
+ i++;
+ }
+ }
+
+ Status error;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const RegisterInfo *ra_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+ const RegisterInfo *r25_info = reg_ctx->GetRegisterInfoByName("r25", 0);
+ const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("zero", 0);
+
+ if (log)
+ log->Printf("Writing R0: 0x%" PRIx64, (uint64_t)0);
+
+ /* Write r0 with 0, in case we are stopped in syscall,
+ * such setting prevents automatic decrement of the PC.
+ * This clears the bug 23659 for MIPS.
+ */
+ if (!reg_ctx->WriteRegisterFromUnsigned(r0_info, (uint64_t)0))
+ return false;
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ // Set "sp" to the requested value
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ if (log)
+ log->Printf("Writing RA: 0x%" PRIx64, (uint64_t)return_addr);
+
+ // Set "ra" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(ra_reg_info, return_addr))
+ return false;
+
+ if (log)
+ log->Printf("Writing PC: 0x%" PRIx64, (uint64_t)func_addr);
+
+ // Set pc to the address of the called function.
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ if (log)
+ log->Printf("Writing r25: 0x%" PRIx64, (uint64_t)func_addr);
+
+ // All callers of position independent functions must place the address of
+ // the called function in t9 (r25)
+ if (!reg_ctx->WriteRegisterFromUnsigned(r25_info, func_addr))
+ return false;
+
+ return true;
+}
+
+bool ABISysV_mips::GetArgumentValues(Thread &thread, ValueList &values) const {
+ return false;
+}
+
+Status ABISysV_mips::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ const RegisterInfo *r2_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+ if (num_bytes <= 4) {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r2_info, raw_value))
+ set_it_simple = true;
+ } else {
+ uint32_t raw_value = data.GetMaxU32(&offset, 4);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r2_info, raw_value)) {
+ const RegisterInfo *r3_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r3_info, raw_value))
+ set_it_simple = true;
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else
+ error.SetErrorString(
+ "We don't support returning float values at present");
+ }
+
+ if (!set_it_simple)
+ error.SetErrorString(
+ "We only support setting simple integer return types at present.");
+
+ return error;
+}
+
+ValueObjectSP ABISysV_mips::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_mips::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ if (exe_ctx.GetTargetPtr() == nullptr || exe_ctx.GetProcessPtr() == nullptr)
+ return return_valobj_sp;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ const ArchSpec target_arch = target->GetArchitecture();
+ ByteOrder target_byte_order = target_arch.GetByteOrder();
+ value.SetCompilerType(return_compiler_type);
+ uint32_t fp_flag =
+ target_arch.GetFlags() & lldb_private::ArchSpec::eMIPS_ABI_FP_mask;
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ bool is_signed = false;
+ bool is_complex = false;
+ uint32_t count = 0;
+
+ // In MIPS register "r2" (v0) holds the integer function return values
+ const RegisterInfo *r2_reg_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+ llvm::Optional<uint64_t> bit_width = return_compiler_type.GetBitSize(&thread);
+ if (!bit_width)
+ return return_valobj_sp;
+ if (return_compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 64: {
+ const RegisterInfo *r3_reg_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+ uint64_t raw_value;
+ raw_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX;
+ raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r3_reg_info, 0) &
+ UINT32_MAX))
+ << 32;
+ if (is_signed)
+ value.GetScalar() = (int64_t)raw_value;
+ else
+ value.GetScalar() = (uint64_t)raw_value;
+ } break;
+ case 32:
+ if (is_signed)
+ value.GetScalar() = (int32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX);
+ break;
+ case 16:
+ if (is_signed)
+ value.GetScalar() = (int16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT16_MAX);
+ break;
+ case 8:
+ if (is_signed)
+ value.GetScalar() = (int8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT8_MAX);
+ break;
+ }
+ } else if (return_compiler_type.IsPointerType()) {
+ uint32_t ptr =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(r2_reg_info, 0) &
+ UINT32_MAX;
+ value.GetScalar() = ptr;
+ } else if (return_compiler_type.IsAggregateType()) {
+ // Structure/Vector is always passed in memory and pointer to that memory
+ // is passed in r2.
+ uint64_t mem_address = reg_ctx->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("r2", 0), 0);
+ // We have got the address. Create a memory object out of it
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(mem_address, nullptr), return_compiler_type);
+ return return_valobj_sp;
+ } else if (return_compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (IsSoftFloat(fp_flag)) {
+ uint64_t raw_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0);
+ if (count != 1 && is_complex)
+ return return_valobj_sp;
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 32:
+ static_assert(sizeof(float) == sizeof(uint32_t), "");
+ value.GetScalar() = *((float *)(&raw_value));
+ break;
+ case 64:
+ static_assert(sizeof(double) == sizeof(uint64_t), "");
+ const RegisterInfo *r3_reg_info =
+ reg_ctx->GetRegisterInfoByName("r3", 0);
+ if (target_byte_order == eByteOrderLittle)
+ raw_value =
+ ((reg_ctx->ReadRegisterAsUnsigned(r3_reg_info, 0)) << 32) |
+ raw_value;
+ else
+ raw_value = (raw_value << 32) |
+ reg_ctx->ReadRegisterAsUnsigned(r3_reg_info, 0);
+ value.GetScalar() = *((double *)(&raw_value));
+ break;
+ }
+ }
+
+ else {
+ const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0);
+ RegisterValue f0_value;
+ DataExtractor f0_data;
+ reg_ctx->ReadRegister(f0_info, f0_value);
+ f0_value.GetData(f0_data);
+ lldb::offset_t offset = 0;
+
+ if (count == 1 && !is_complex) {
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 64: {
+ static_assert(sizeof(double) == sizeof(uint64_t), "");
+ const RegisterInfo *f1_info = reg_ctx->GetRegisterInfoByName("f1", 0);
+ RegisterValue f1_value;
+ DataExtractor f1_data;
+ reg_ctx->ReadRegister(f1_info, f1_value);
+ DataExtractor *copy_from_extractor = nullptr;
+ DataBufferSP data_sp(new DataBufferHeap(8, 0));
+ DataExtractor return_ext(
+ data_sp, target_byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ if (target_byte_order == eByteOrderLittle) {
+ copy_from_extractor = &f0_data;
+ copy_from_extractor->CopyByteOrderedData(
+ offset, 4, data_sp->GetBytes(), 4, target_byte_order);
+ f1_value.GetData(f1_data);
+ copy_from_extractor = &f1_data;
+ copy_from_extractor->CopyByteOrderedData(
+ offset, 4, data_sp->GetBytes() + 4, 4, target_byte_order);
+ } else {
+ copy_from_extractor = &f0_data;
+ copy_from_extractor->CopyByteOrderedData(
+ offset, 4, data_sp->GetBytes() + 4, 4, target_byte_order);
+ f1_value.GetData(f1_data);
+ copy_from_extractor = &f1_data;
+ copy_from_extractor->CopyByteOrderedData(
+ offset, 4, data_sp->GetBytes(), 4, target_byte_order);
+ }
+ value.GetScalar() = (double)return_ext.GetDouble(&offset);
+ break;
+ }
+ case 32: {
+ static_assert(sizeof(float) == sizeof(uint32_t), "");
+ value.GetScalar() = (float)f0_data.GetFloat(&offset);
+ break;
+ }
+ }
+ } else {
+ // not handled yet
+ return return_valobj_sp;
+ }
+ }
+ } else {
+ // not handled yet
+ return return_valobj_sp;
+ }
+
+ // If we get here, we have a valid Value, so make our ValueObject out of it:
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ return return_valobj_sp;
+}
+
+bool ABISysV_mips::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0);
+
+ // The previous PC is in the RA
+ row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("mips at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetReturnAddressRegister(dwarf_r31);
+ return true;
+}
+
+bool ABISysV_mips::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0);
+
+ row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("mips default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+bool ABISysV_mips::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+bool ABISysV_mips::IsSoftFloat(uint32_t fp_flags) const {
+ return (fp_flags == lldb_private::ArchSpec::eMIPS_ABI_FP_SOFT);
+}
+
+bool ABISysV_mips::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Preserved registers are :
+ // r16-r23, r28, r29, r30, r31
+ const char *name = reg_info->name;
+
+ if (name[0] == 'r') {
+ switch (name[1]) {
+ case '1':
+ if (name[2] == '6' || name[2] == '7' || name[2] == '8' ||
+ name[2] == '9') // r16-r19
+ return name[3] == '\0';
+ break;
+ case '2':
+ if (name[2] == '0' || name[2] == '1' || name[2] == '2' ||
+ name[2] == '3' // r20-r23
+ || name[2] == '8' || name[2] == '9') // r28 and r29
+ return name[3] == '\0';
+ break;
+ case '3':
+ if (name[2] == '0' || name[2] == '1') // r30 and r31
+ return name[3] == '\0';
+ break;
+ }
+
+ if (name[0] == 'g' && name[1] == 'p' && name[2] == '\0') // gp (r28)
+ return true;
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp (r29)
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp (r30)
+ return true;
+ if (name[0] == 'r' && name[1] == 'a' && name[2] == '\0') // ra (r31)
+ return true;
+ }
+ }
+ return false;
+}
+
+void ABISysV_mips::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for mips targets", CreateInstance);
+}
+
+void ABISysV_mips::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_mips::GetPluginNameStatic() {
+ static ConstString g_name("sysv-mips");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_mips::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_mips::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.h
new file mode 100644
index 000000000000..6cd9c19c22ac
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.h
@@ -0,0 +1,95 @@
+//===-- ABISysV_mips.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABISysV_mips_h_
+#define liblldb_ABISysV_mips_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_mips : public lldb_private::ABI {
+public:
+ ~ABISysV_mips() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool IsSoftFloat(uint32_t fp_flag) const;
+
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Just make sure the address is a valid 32 bit address. Bit zero
+ // might be set due to MicroMIPS function calls, so don't enforce alignment.
+ return (pc <= UINT32_MAX);
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_mips(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_mips_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.cpp
new file mode 100644
index 000000000000..18011cfb6b9e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.cpp
@@ -0,0 +1,1217 @@
+//===-- ABISysV_mips64.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_mips64.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ dwarf_r0 = 0,
+ dwarf_r1,
+ dwarf_r2,
+ dwarf_r3,
+ dwarf_r4,
+ dwarf_r5,
+ dwarf_r6,
+ dwarf_r7,
+ dwarf_r8,
+ dwarf_r9,
+ dwarf_r10,
+ dwarf_r11,
+ dwarf_r12,
+ dwarf_r13,
+ dwarf_r14,
+ dwarf_r15,
+ dwarf_r16,
+ dwarf_r17,
+ dwarf_r18,
+ dwarf_r19,
+ dwarf_r20,
+ dwarf_r21,
+ dwarf_r22,
+ dwarf_r23,
+ dwarf_r24,
+ dwarf_r25,
+ dwarf_r26,
+ dwarf_r27,
+ dwarf_r28,
+ dwarf_r29,
+ dwarf_r30,
+ dwarf_r31,
+ dwarf_sr,
+ dwarf_lo,
+ dwarf_hi,
+ dwarf_bad,
+ dwarf_cause,
+ dwarf_pc
+};
+
+static const RegisterInfo g_register_infos_mips64[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME
+ // DWARF GENERIC PROCESS PLUGIN
+ // LLDB NATIVE
+ // ======== ====== == === ============= ========== =============
+ // ================= ==================== =================
+ // ====================
+ {"r0",
+ "zero",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r0, dwarf_r0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ "AT",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r1, dwarf_r1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ "v0",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r2, dwarf_r2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ "v1",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r3, dwarf_r3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ "arg1",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r4, dwarf_r4, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ "arg2",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r5, dwarf_r5, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ "arg3",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r6, dwarf_r6, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ "arg4",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r7, dwarf_r7, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ "arg5",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r8, dwarf_r8, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ "arg6",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r9, dwarf_r9, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ "arg7",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r10, dwarf_r10, LLDB_REGNUM_GENERIC_ARG7, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ "arg8",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r11, dwarf_r11, LLDB_REGNUM_GENERIC_ARG8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r16, dwarf_r16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r17, dwarf_r17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r18, dwarf_r18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r19, dwarf_r19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r20, dwarf_r20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r21, dwarf_r21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r22, dwarf_r22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r23, dwarf_r23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r24, dwarf_r24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r25, dwarf_r25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r26, dwarf_r26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r27, dwarf_r27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r28",
+ "gp",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r28, dwarf_r28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r29",
+ "sp",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r29, dwarf_r29, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r30",
+ "fp",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r30, dwarf_r30, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r31",
+ "ra",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r31, dwarf_r31, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_sr, dwarf_sr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lo",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_lo, dwarf_lo, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"hi",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_hi, dwarf_hi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bad",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_bad, dwarf_bad, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cause",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_cause, dwarf_cause, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos_mips64);
+
+const lldb_private::RegisterInfo *
+ABISysV_mips64::GetRegisterInfoArray(uint32_t &count) {
+ count = k_num_register_infos;
+ return g_register_infos_mips64;
+}
+
+size_t ABISysV_mips64::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABISysV_mips64::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().isMIPS64())
+ return ABISP(new ABISysV_mips64(process_sp));
+ return ABISP();
+}
+
+bool ABISysV_mips64::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_mips64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%zd = 0x%" PRIx64, i + 1, args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ if (args.size() > 8) // TODO handle more than 8 arguments
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%zd (0x%" PRIx64 ") into %s", i + 1,
+ args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // First, align the SP
+
+ if (log)
+ log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)(sp & ~0xfull));
+
+ sp &= ~(0xfull); // 16-byte alignment
+
+ Status error;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const RegisterInfo *ra_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+ const RegisterInfo *r25_info = reg_ctx->GetRegisterInfoByName("r25", 0);
+ const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("zero", 0);
+
+ if (log)
+ log->Printf("Writing R0: 0x%" PRIx64, (uint64_t)0);
+
+ /* Write r0 with 0, in case we are stopped in syscall,
+ * such setting prevents automatic decrement of the PC.
+ * This clears the bug 23659 for MIPS.
+ */
+ if (!reg_ctx->WriteRegisterFromUnsigned(r0_info, (uint64_t)0))
+ return false;
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ // Set "sp" to the requested value
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ if (log)
+ log->Printf("Writing RA: 0x%" PRIx64, (uint64_t)return_addr);
+
+ // Set "ra" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(ra_reg_info, return_addr))
+ return false;
+
+ if (log)
+ log->Printf("Writing PC: 0x%" PRIx64, (uint64_t)func_addr);
+
+ // Set pc to the address of the called function.
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ if (log)
+ log->Printf("Writing r25: 0x%" PRIx64, (uint64_t)func_addr);
+
+ // All callers of position independent functions must place the address of
+ // the called function in t9 (r25)
+ if (!reg_ctx->WriteRegisterFromUnsigned(r25_info, func_addr))
+ return false;
+
+ return true;
+}
+
+bool ABISysV_mips64::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ return false;
+}
+
+Status ABISysV_mips64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ if (!reg_ctx)
+ error.SetErrorString("no registers are available");
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ const uint32_t type_flags = compiler_type.GetTypeInfo(nullptr);
+
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ lldb::offset_t offset = 0;
+
+ if (num_bytes <= 16) {
+ const RegisterInfo *r2_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(r2_info, raw_value))
+ error.SetErrorString("failed to write register r2");
+ } else {
+ uint64_t raw_value = data.GetMaxU64(&offset, 8);
+ if (reg_ctx->WriteRegisterFromUnsigned(r2_info, raw_value)) {
+ const RegisterInfo *r3_info =
+ reg_ctx->GetRegisterInfoByName("r3", 0);
+ raw_value = data.GetMaxU64(&offset, num_bytes - offset);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(r3_info, raw_value))
+ error.SetErrorString("failed to write register r3");
+ } else
+ error.SetErrorString("failed to write register r2");
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 128 bit "
+ "integer values at present.");
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ error.SetErrorString("TODO: Handle Float Types.");
+ }
+ } else if (type_flags & eTypeIsVector) {
+ error.SetErrorString("returning vector values are not supported");
+ }
+
+ return error;
+}
+
+ValueObjectSP ABISysV_mips64::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_mips64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+ Status error;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ if (exe_ctx.GetTargetPtr() == nullptr || exe_ctx.GetProcessPtr() == nullptr)
+ return return_valobj_sp;
+
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ const ArchSpec target_arch = target->GetArchitecture();
+ ByteOrder target_byte_order = target_arch.GetByteOrder();
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo(nullptr);
+ uint32_t fp_flag =
+ target_arch.GetFlags() & lldb_private::ArchSpec::eMIPS_ABI_FP_mask;
+
+ const RegisterInfo *r2_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+ const RegisterInfo *r3_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ // Extract the register context so we can read arguments from registers
+ // In MIPS register "r2" (v0) holds the integer function return values
+
+ uint64_t raw_value = reg_ctx->ReadRegisterAsUnsigned(r2_info, 0);
+
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else if (IsSoftFloat(fp_flag)) {
+ uint64_t raw_value = reg_ctx->ReadRegisterAsUnsigned(r2_info, 0);
+ switch (*byte_size) {
+ case 4:
+ value.GetScalar() = *((float *)(&raw_value));
+ success = true;
+ break;
+ case 8:
+ value.GetScalar() = *((double *)(&raw_value));
+ success = true;
+ break;
+ case 16:
+ uint64_t result[2];
+ if (target_byte_order == eByteOrderLittle) {
+ result[0] = raw_value;
+ result[1] = reg_ctx->ReadRegisterAsUnsigned(r3_info, 0);
+ value.GetScalar() = *((long double *)(result));
+ } else {
+ result[0] = reg_ctx->ReadRegisterAsUnsigned(r3_info, 0);
+ result[1] = raw_value;
+ value.GetScalar() = *((long double *)(result));
+ }
+ success = true;
+ break;
+ }
+
+ } else {
+ if (*byte_size <= sizeof(long double)) {
+ const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0);
+
+ RegisterValue f0_value;
+ DataExtractor f0_data;
+
+ reg_ctx->ReadRegister(f0_info, f0_value);
+
+ f0_value.GetData(f0_data);
+
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = (float)f0_data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = (double)f0_data.GetDouble(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(long double)) {
+ const RegisterInfo *f2_info =
+ reg_ctx->GetRegisterInfoByName("f2", 0);
+ RegisterValue f2_value;
+ DataExtractor f2_data;
+ reg_ctx->ReadRegister(f2_info, f2_value);
+ DataExtractor *copy_from_extractor = nullptr;
+ DataBufferSP data_sp(new DataBufferHeap(16, 0));
+ DataExtractor return_ext(
+ data_sp, target_byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ if (target_byte_order == eByteOrderLittle) {
+ copy_from_extractor = &f0_data;
+ copy_from_extractor->CopyByteOrderedData(
+ 0, 8, data_sp->GetBytes(), *byte_size - 8, target_byte_order);
+ f2_value.GetData(f2_data);
+ copy_from_extractor = &f2_data;
+ copy_from_extractor->CopyByteOrderedData(
+ 0, 8, data_sp->GetBytes() + 8, *byte_size - 8,
+ target_byte_order);
+ } else {
+ copy_from_extractor = &f0_data;
+ copy_from_extractor->CopyByteOrderedData(
+ 0, 8, data_sp->GetBytes() + 8, *byte_size - 8,
+ target_byte_order);
+ f2_value.GetData(f2_data);
+ copy_from_extractor = &f2_data;
+ copy_from_extractor->CopyByteOrderedData(
+ 0, 8, data_sp->GetBytes(), *byte_size - 8, target_byte_order);
+ }
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+ return return_valobj_sp;
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass ||
+ type_flags & eTypeIsVector) {
+ // Any structure of up to 16 bytes in size is returned in the registers.
+ if (*byte_size <= 16) {
+ DataBufferSP data_sp(new DataBufferHeap(16, 0));
+ DataExtractor return_ext(data_sp, target_byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ RegisterValue r2_value, r3_value, f0_value, f1_value, f2_value;
+ // Tracks how much bytes of r2 and r3 registers we've consumed so far
+ uint32_t integer_bytes = 0;
+
+ // True if return values are in FP return registers.
+ bool use_fp_regs = false;
+ // True if we found any non floating point field in structure.
+ bool found_non_fp_field = false;
+ // True if return values are in r2 register.
+ bool use_r2 = false;
+ // True if return values are in r3 register.
+ bool use_r3 = false;
+ // True if the result is copied into our data buffer
+ bool sucess = false;
+ std::string name;
+ bool is_complex;
+ uint32_t count;
+ const uint32_t num_children = return_compiler_type.GetNumFields();
+
+ // A structure consisting of one or two FP values (and nothing else) will
+ // be returned in the two FP return-value registers i.e fp0 and fp2.
+ if (num_children <= 2) {
+ uint64_t field_bit_offset = 0;
+
+ // Check if this structure contains only floating point fields
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ CompilerType field_compiler_type =
+ return_compiler_type.GetFieldAtIndex(idx, name, &field_bit_offset,
+ nullptr, nullptr);
+
+ if (field_compiler_type.IsFloatingPointType(count, is_complex))
+ use_fp_regs = true;
+ else
+ found_non_fp_field = true;
+ }
+
+ if (use_fp_regs && !found_non_fp_field) {
+ // We have one or two FP-only values in this structure. Get it from
+ // f0/f2 registers.
+ DataExtractor f0_data, f1_data, f2_data;
+ const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0);
+ const RegisterInfo *f1_info = reg_ctx->GetRegisterInfoByName("f1", 0);
+ const RegisterInfo *f2_info = reg_ctx->GetRegisterInfoByName("f2", 0);
+
+ reg_ctx->ReadRegister(f0_info, f0_value);
+ reg_ctx->ReadRegister(f2_info, f2_value);
+
+ f0_value.GetData(f0_data);
+
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ CompilerType field_compiler_type =
+ return_compiler_type.GetFieldAtIndex(
+ idx, name, &field_bit_offset, nullptr, nullptr);
+ llvm::Optional<uint64_t> field_byte_width =
+ field_compiler_type.GetByteSize(nullptr);
+ if (!field_byte_width)
+ return return_valobj_sp;
+
+ DataExtractor *copy_from_extractor = nullptr;
+ uint64_t return_value[2];
+ offset_t offset = 0;
+
+ if (idx == 0) {
+ // This case is for long double type.
+ if (*field_byte_width == 16) {
+
+ // If structure contains long double type, then it is returned
+ // in fp0/fp1 registers.
+ if (target_byte_order == eByteOrderLittle) {
+ return_value[0] = f0_data.GetU64(&offset);
+ reg_ctx->ReadRegister(f1_info, f1_value);
+ f1_value.GetData(f1_data);
+ offset = 0;
+ return_value[1] = f1_data.GetU64(&offset);
+ } else {
+ return_value[1] = f0_data.GetU64(&offset);
+ reg_ctx->ReadRegister(f1_info, f1_value);
+ f1_value.GetData(f1_data);
+ offset = 0;
+ return_value[0] = f1_data.GetU64(&offset);
+ }
+
+ f0_data.SetData(return_value, *field_byte_width,
+ target_byte_order);
+ }
+ copy_from_extractor = &f0_data; // This is in f0, copy from
+ // register to our result
+ // structure
+ } else {
+ f2_value.GetData(f2_data);
+ // This is in f2, copy from register to our result structure
+ copy_from_extractor = &f2_data;
+ }
+
+ // Sanity check to avoid crash
+ if (!copy_from_extractor ||
+ *field_byte_width > copy_from_extractor->GetByteSize())
+ return return_valobj_sp;
+
+ // copy the register contents into our data buffer
+ copy_from_extractor->CopyByteOrderedData(
+ 0, *field_byte_width,
+ data_sp->GetBytes() + (field_bit_offset / 8), *field_byte_width,
+ target_byte_order);
+ }
+
+ // The result is in our data buffer. Create a variable object out of
+ // it
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+
+ return return_valobj_sp;
+ }
+ }
+
+ // If we reach here, it means this structure either contains more than
+ // two fields or it contains at least one non floating point type. In
+ // that case, all fields are returned in GP return registers.
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ uint64_t field_bit_offset = 0;
+ bool is_signed;
+ uint32_t padding;
+
+ CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex(
+ idx, name, &field_bit_offset, nullptr, nullptr);
+ llvm::Optional<uint64_t> field_byte_width =
+ field_compiler_type.GetByteSize(nullptr);
+
+ // if we don't know the size of the field (e.g. invalid type), just
+ // bail out
+ if (!field_byte_width || *field_byte_width == 0)
+ break;
+
+ uint32_t field_byte_offset = field_bit_offset / 8;
+
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType() ||
+ field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ padding = field_byte_offset - integer_bytes;
+
+ if (integer_bytes < 8) {
+ // We have not yet consumed r2 completely.
+ if (integer_bytes + *field_byte_width + padding <= 8) {
+ // This field fits in r2, copy its value from r2 to our result
+ // structure
+ integer_bytes = integer_bytes + *field_byte_width +
+ padding; // Increase the consumed bytes.
+ use_r2 = true;
+ } else {
+ // There isn't enough space left in r2 for this field, so this
+ // will be in r3.
+ integer_bytes = integer_bytes + *field_byte_width +
+ padding; // Increase the consumed bytes.
+ use_r3 = true;
+ }
+ }
+ // We already have consumed at-least 8 bytes that means r2 is done,
+ // and this field will be in r3. Check if this field can fit in r3.
+ else if (integer_bytes + *field_byte_width + padding <= 16) {
+ integer_bytes = integer_bytes + *field_byte_width + padding;
+ use_r3 = true;
+ } else {
+ // There isn't any space left for this field, this should not
+ // happen as we have already checked the overall size is not
+ // greater than 16 bytes. For now, return a nullptr return value
+ // object.
+ return return_valobj_sp;
+ }
+ }
+ }
+ // Vector types up to 16 bytes are returned in GP return registers
+ if (type_flags & eTypeIsVector) {
+ if (*byte_size <= 8)
+ use_r2 = true;
+ else {
+ use_r2 = true;
+ use_r3 = true;
+ }
+ }
+
+ if (use_r2) {
+ reg_ctx->ReadRegister(r2_info, r2_value);
+
+ const size_t bytes_copied = r2_value.GetAsMemoryData(
+ r2_info, data_sp->GetBytes(), r2_info->byte_size, target_byte_order,
+ error);
+ if (bytes_copied != r2_info->byte_size)
+ return return_valobj_sp;
+ sucess = true;
+ }
+ if (use_r3) {
+ reg_ctx->ReadRegister(r3_info, r3_value);
+ const size_t bytes_copied = r3_value.GetAsMemoryData(
+ r3_info, data_sp->GetBytes() + r2_info->byte_size,
+ r3_info->byte_size, target_byte_order, error);
+
+ if (bytes_copied != r3_info->byte_size)
+ return return_valobj_sp;
+ sucess = true;
+ }
+ if (sucess) {
+ // The result is in our data buffer. Create a variable object out of
+ // it
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+ }
+ return return_valobj_sp;
+ }
+
+ // Any structure/vector greater than 16 bytes in size is returned in
+ // memory. The pointer to that memory is returned in r2.
+ uint64_t mem_address = reg_ctx->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("r2", 0), 0);
+
+ // We have got the address. Create a memory object out of it
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(mem_address, nullptr), return_compiler_type);
+ }
+ return return_valobj_sp;
+}
+
+bool ABISysV_mips64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0);
+
+ // The previous PC is in the RA
+ row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("mips64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetReturnAddressRegister(dwarf_r31);
+ return true;
+}
+
+bool ABISysV_mips64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0);
+
+ row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("mips64 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+bool ABISysV_mips64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+bool ABISysV_mips64::IsSoftFloat(uint32_t fp_flag) const {
+ return (fp_flag == lldb_private::ArchSpec::eMIPS_ABI_FP_SOFT);
+}
+
+bool ABISysV_mips64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Preserved registers are :
+ // r16-r23, r28, r29, r30, r31
+
+ int reg = ((reg_info->byte_offset) / 8);
+
+ bool save = (reg >= 16) && (reg <= 23);
+ save |= (reg >= 28) && (reg <= 31);
+
+ return save;
+ }
+ return false;
+}
+
+void ABISysV_mips64::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for mips64 targets", CreateInstance);
+}
+
+void ABISysV_mips64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_mips64::GetPluginNameStatic() {
+ static ConstString g_name("sysv-mips64");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_mips64::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_mips64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.h
new file mode 100644
index 000000000000..7da71b36b4b7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.h
@@ -0,0 +1,108 @@
+//===-- ABISysV_mips64.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABISysV_mips64_h_
+#define liblldb_ABISysV_mips64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_mips64 : public lldb_private::ABI {
+public:
+ ~ABISysV_mips64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool IsSoftFloat(uint32_t fp_flag) const;
+
+ // The SysV mips ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ if (pc & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+
+ // Anything else if fair game..
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_mips64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_mips64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.cpp
new file mode 100644
index 000000000000..faa995033ac2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.cpp
@@ -0,0 +1,982 @@
+//===-- ABISysV_ppc.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_ppc.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ dwarf_r0 = 0,
+ dwarf_r1,
+ dwarf_r2,
+ dwarf_r3,
+ dwarf_r4,
+ dwarf_r5,
+ dwarf_r6,
+ dwarf_r7,
+ dwarf_r8,
+ dwarf_r9,
+ dwarf_r10,
+ dwarf_r11,
+ dwarf_r12,
+ dwarf_r13,
+ dwarf_r14,
+ dwarf_r15,
+ dwarf_r16,
+ dwarf_r17,
+ dwarf_r18,
+ dwarf_r19,
+ dwarf_r20,
+ dwarf_r21,
+ dwarf_r22,
+ dwarf_r23,
+ dwarf_r24,
+ dwarf_r25,
+ dwarf_r26,
+ dwarf_r27,
+ dwarf_r28,
+ dwarf_r29,
+ dwarf_r30,
+ dwarf_r31,
+ dwarf_f0,
+ dwarf_f1,
+ dwarf_f2,
+ dwarf_f3,
+ dwarf_f4,
+ dwarf_f5,
+ dwarf_f6,
+ dwarf_f7,
+ dwarf_f8,
+ dwarf_f9,
+ dwarf_f10,
+ dwarf_f11,
+ dwarf_f12,
+ dwarf_f13,
+ dwarf_f14,
+ dwarf_f15,
+ dwarf_f16,
+ dwarf_f17,
+ dwarf_f18,
+ dwarf_f19,
+ dwarf_f20,
+ dwarf_f21,
+ dwarf_f22,
+ dwarf_f23,
+ dwarf_f24,
+ dwarf_f25,
+ dwarf_f26,
+ dwarf_f27,
+ dwarf_f28,
+ dwarf_f29,
+ dwarf_f30,
+ dwarf_f31,
+ dwarf_cr,
+ dwarf_fpscr,
+ dwarf_xer = 101,
+ dwarf_lr = 108,
+ dwarf_ctr,
+ dwarf_pc,
+ dwarf_cfa,
+};
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, 8, 0, eEncodingUint, eFormatHex, {kind1, kind2, kind3, kind4 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+static const RegisterInfo g_register_infos[] = {
+ // General purpose registers. eh_frame, DWARF,
+ // Generic, Process Plugin
+ DEFINE_GPR(r0, nullptr, dwarf_r0, dwarf_r0, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r1, "sp", dwarf_r1, dwarf_r1, LLDB_REGNUM_GENERIC_SP,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r2, nullptr, dwarf_r2, dwarf_r2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r3, "arg1", dwarf_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG1,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r4, "arg2", dwarf_r4, dwarf_r4, LLDB_REGNUM_GENERIC_ARG2,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r5, "arg3", dwarf_r5, dwarf_r5, LLDB_REGNUM_GENERIC_ARG3,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r6, "arg4", dwarf_r6, dwarf_r6, LLDB_REGNUM_GENERIC_ARG4,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r7, "arg5", dwarf_r7, dwarf_r7, LLDB_REGNUM_GENERIC_ARG5,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r8, "arg6", dwarf_r8, dwarf_r8, LLDB_REGNUM_GENERIC_ARG6,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r9, "arg7", dwarf_r9, dwarf_r9, LLDB_REGNUM_GENERIC_ARG7,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r10, "arg8", dwarf_r10, dwarf_r10, LLDB_REGNUM_GENERIC_ARG8,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r11, nullptr, dwarf_r11, dwarf_r11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r12, nullptr, dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, nullptr, dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, nullptr, dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, nullptr, dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r16, nullptr, dwarf_r16, dwarf_r16, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r17, nullptr, dwarf_r17, dwarf_r17, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r18, nullptr, dwarf_r18, dwarf_r18, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r19, nullptr, dwarf_r19, dwarf_r19, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r20, nullptr, dwarf_r20, dwarf_r20, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r21, nullptr, dwarf_r21, dwarf_r21, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r22, nullptr, dwarf_r22, dwarf_r22, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r23, nullptr, dwarf_r23, dwarf_r23, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r24, nullptr, dwarf_r24, dwarf_r24, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r25, nullptr, dwarf_r25, dwarf_r25, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r26, nullptr, dwarf_r26, dwarf_r26, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r27, nullptr, dwarf_r27, dwarf_r27, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r28, nullptr, dwarf_r28, dwarf_r28, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r29, nullptr, dwarf_r29, dwarf_r29, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r30, nullptr, dwarf_r30, dwarf_r30, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r31, nullptr, dwarf_r31, dwarf_r31, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(lr, "lr", dwarf_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(cr, "cr", dwarf_cr, dwarf_cr, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(xer, "xer", dwarf_xer, dwarf_xer, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ctr, "ctr", dwarf_ctr, dwarf_ctr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(pc, "pc", dwarf_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_REGNUM),
+ {nullptr,
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_cfa, dwarf_cfa, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+
+const lldb_private::RegisterInfo *
+ABISysV_ppc::GetRegisterInfoArray(uint32_t &count) {
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABISysV_ppc::GetRedZoneSize() const { return 224; }
+
+// Static Functions
+
+ABISP
+ABISysV_ppc::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::ppc) {
+ return ABISP(new ABISysV_ppc(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABISysV_ppc::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_ppc::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1),
+ args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ if (args.size() > 8) // TODO handle more than 8 arguments
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s",
+ static_cast<uint64_t>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // First, align the SP
+
+ if (log)
+ log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)(sp & ~0xfull));
+
+ sp &= ~(0xfull); // 16-byte alignment
+
+ sp -= 8;
+
+ Status error;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ ProcessSP process_sp(thread.GetProcess());
+
+ RegisterValue reg_value;
+
+ if (log)
+ log->Printf("Pushing the return address onto the stack: 0x%" PRIx64
+ ": 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)return_addr);
+
+ // Save return address onto the stack
+ if (!process_sp->WritePointerToMemory(sp, return_addr, error))
+ return false;
+
+ // %r1 is set to the actual stack value.
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ // %pc is set to the address of the called function.
+
+ if (log)
+ log->Printf("Writing IP: 0x%" PRIx64, (uint64_t)func_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Thread &thread,
+ uint32_t *argument_register_ids,
+ unsigned int &current_argument_register,
+ addr_t &current_stack_argument) {
+ if (bit_width > 64)
+ return false; // Scalar can't hold large integer arguments
+
+ if (current_argument_register < 6) {
+ scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ argument_register_ids[current_argument_register], 0);
+ current_argument_register++;
+ if (is_signed)
+ scalar.SignExtend(bit_width);
+ } else {
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (thread.GetProcess()->ReadScalarIntegerFromMemory(
+ current_stack_argument, byte_size, is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool ABISysV_ppc::GetArgumentValues(Thread &thread, ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 48; // jump over return address
+
+ uint32_t argument_register_ids[8];
+
+ argument_register_ids[0] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[1] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[2] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[3] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[4] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG5)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[5] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG6)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[6] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG7)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[7] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG8)
+ ->kinds[eRegisterKindLLDB];
+
+ unsigned int current_argument_register = 0;
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ CompilerType compiler_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ bool is_signed;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed))
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ else if (compiler_type.IsPointerType())
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ }
+
+ return true;
+}
+
+Status ABISysV_ppc::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(reg_info, raw_value))
+ set_it_simple = true;
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else {
+ llvm::Optional<uint64_t> bit_width =
+ compiler_type.GetBitSize(frame_sp.get());
+ if (!bit_width) {
+ error.SetErrorString("can't get type size");
+ return error;
+ }
+ if (*bit_width <= 64) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ unsigned char buffer[16];
+ ByteOrder byte_order = data.GetByteOrder();
+
+ data.CopyByteOrderedData(0, num_bytes, buffer, 16, byte_order);
+ set_it_simple = true;
+ } else {
+ // FIXME - don't know how to do 80 bit long doubles yet.
+ error.SetErrorString(
+ "We don't support returning float values > 64 bits at present");
+ }
+ }
+ }
+
+ if (!set_it_simple) {
+ // Okay we've got a structure or something that doesn't fit in a simple
+ // register. We should figure out where it really goes, but we don't
+ // support this yet.
+ error.SetErrorString("We only support setting simple integer and float "
+ "return types at present.");
+ }
+
+ return error;
+}
+
+ValueObjectSP ABISysV_ppc::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType, return_value_type);
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo();
+ if (type_flags & eTypeIsScalar) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger) {
+ // Extract the register context so we can read arguments from registers
+
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("r3", 0), 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size <= sizeof(long double)) {
+ const RegisterInfo *f1_info = reg_ctx->GetRegisterInfoByName("f1", 0);
+ RegisterValue f1_value;
+ if (reg_ctx->ReadRegister(f1_info, f1_value)) {
+ DataExtractor data;
+ if (f1_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = (float)data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = (double)data.GetDouble(&offset);
+ success = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsPointer) {
+ unsigned r3_id =
+ reg_ctx->GetRegisterInfoByName("r3", 0)->kinds[eRegisterKindLLDB];
+ value.GetScalar() =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r3_id, 0);
+ value.SetValueType(Value::eValueTypeScalar);
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsVector) {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size > 0) {
+ const RegisterInfo *altivec_reg = reg_ctx->GetRegisterInfoByName("v2", 0);
+ if (altivec_reg) {
+ if (*byte_size <= altivec_reg->byte_size) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(altivec_reg, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(
+ altivec_reg, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(), byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_ppc::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type);
+ if (return_valobj_sp)
+ return return_valobj_sp;
+
+ RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
+ if (!reg_ctx_sp)
+ return return_valobj_sp;
+
+ llvm::Optional<uint64_t> bit_width = return_compiler_type.GetBitSize(&thread);
+ if (!bit_width)
+ return return_valobj_sp;
+ if (return_compiler_type.IsAggregateType()) {
+ Target *target = exe_ctx.GetTargetPtr();
+ bool is_memory = true;
+ if (*bit_width <= 128) {
+ ByteOrder target_byte_order = target->GetArchitecture().GetByteOrder();
+ DataBufferSP data_sp(new DataBufferHeap(16, 0));
+ DataExtractor return_ext(data_sp, target_byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ const RegisterInfo *r3_info = reg_ctx_sp->GetRegisterInfoByName("r3", 0);
+ const RegisterInfo *rdx_info =
+ reg_ctx_sp->GetRegisterInfoByName("rdx", 0);
+
+ RegisterValue r3_value, rdx_value;
+ reg_ctx_sp->ReadRegister(r3_info, r3_value);
+ reg_ctx_sp->ReadRegister(rdx_info, rdx_value);
+
+ DataExtractor r3_data, rdx_data;
+
+ r3_value.GetData(r3_data);
+ rdx_value.GetData(rdx_data);
+
+ uint32_t fp_bytes =
+ 0; // Tracks how much of the xmm registers we've consumed so far
+ uint32_t integer_bytes =
+ 0; // Tracks how much of the r3/rds registers we've consumed so far
+
+ const uint32_t num_children = return_compiler_type.GetNumFields();
+
+ // Since we are in the small struct regime, assume we are not in memory.
+ is_memory = false;
+
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ std::string name;
+ uint64_t field_bit_offset = 0;
+ bool is_signed;
+ bool is_complex;
+ uint32_t count;
+
+ CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex(
+ idx, name, &field_bit_offset, nullptr, nullptr);
+ llvm::Optional<uint64_t> field_bit_width =
+ field_compiler_type.GetBitSize(&thread);
+ if (!field_bit_width)
+ return return_valobj_sp;
+
+ // If there are any unaligned fields, this is stored in memory.
+ if (field_bit_offset % *field_bit_width != 0) {
+ is_memory = true;
+ break;
+ }
+
+ uint32_t field_byte_width = *field_bit_width / 8;
+ uint32_t field_byte_offset = field_bit_offset / 8;
+
+ DataExtractor *copy_from_extractor = nullptr;
+ uint32_t copy_from_offset = 0;
+
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType()) {
+ if (integer_bytes < 8) {
+ if (integer_bytes + field_byte_width <= 8) {
+ // This is in RAX, copy from register to our result structure:
+ copy_from_extractor = &r3_data;
+ copy_from_offset = integer_bytes;
+ integer_bytes += field_byte_width;
+ } else {
+ // The next field wouldn't fit in the remaining space, so we
+ // pushed it to rdx.
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = 0;
+ integer_bytes = 8 + field_byte_width;
+ }
+ } else if (integer_bytes + field_byte_width <= 16) {
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = integer_bytes - 8;
+ integer_bytes += field_byte_width;
+ } else {
+ // The last field didn't fit. I can't see how that would happen
+ // w/o the overall size being greater than 16 bytes. For now,
+ // return a nullptr return value object.
+ return return_valobj_sp;
+ }
+ } else if (field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ // Structs with long doubles are always passed in memory.
+ if (*field_bit_width == 128) {
+ is_memory = true;
+ break;
+ } else if (*field_bit_width == 64) {
+ copy_from_offset = 0;
+ fp_bytes += field_byte_width;
+ } else if (*field_bit_width == 32) {
+ // This one is kind of complicated. If we are in an "eightbyte"
+ // with another float, we'll be stuffed into an xmm register with
+ // it. If we are in an "eightbyte" with one or more ints, then we
+ // will be stuffed into the appropriate GPR with them.
+ bool in_gpr;
+ if (field_byte_offset % 8 == 0) {
+ // We are at the beginning of one of the eightbytes, so check the
+ // next element (if any)
+ if (idx == num_children - 1)
+ in_gpr = false;
+ else {
+ uint64_t next_field_bit_offset = 0;
+ CompilerType next_field_compiler_type =
+ return_compiler_type.GetFieldAtIndex(idx + 1, name,
+ &next_field_bit_offset,
+ nullptr, nullptr);
+ if (next_field_compiler_type.IsIntegerOrEnumerationType(
+ is_signed))
+ in_gpr = true;
+ else {
+ copy_from_offset = 0;
+ in_gpr = false;
+ }
+ }
+ } else if (field_byte_offset % 4 == 0) {
+ // We are inside of an eightbyte, so see if the field before us
+ // is floating point: This could happen if somebody put padding
+ // in the structure.
+ if (idx == 0)
+ in_gpr = false;
+ else {
+ uint64_t prev_field_bit_offset = 0;
+ CompilerType prev_field_compiler_type =
+ return_compiler_type.GetFieldAtIndex(idx - 1, name,
+ &prev_field_bit_offset,
+ nullptr, nullptr);
+ if (prev_field_compiler_type.IsIntegerOrEnumerationType(
+ is_signed))
+ in_gpr = true;
+ else {
+ copy_from_offset = 4;
+ in_gpr = false;
+ }
+ }
+ } else {
+ is_memory = true;
+ continue;
+ }
+
+ // Okay, we've figured out whether we are in GPR or XMM, now figure
+ // out which one.
+ if (in_gpr) {
+ if (integer_bytes < 8) {
+ // This is in RAX, copy from register to our result structure:
+ copy_from_extractor = &r3_data;
+ copy_from_offset = integer_bytes;
+ integer_bytes += field_byte_width;
+ } else {
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = integer_bytes - 8;
+ integer_bytes += field_byte_width;
+ }
+ } else {
+ fp_bytes += field_byte_width;
+ }
+ }
+ }
+
+ // These two tests are just sanity checks. If I somehow get the type
+ // calculation wrong above it is better to just return nothing than to
+ // assert or crash.
+ if (!copy_from_extractor)
+ return return_valobj_sp;
+ if (copy_from_offset + field_byte_width >
+ copy_from_extractor->GetByteSize())
+ return return_valobj_sp;
+
+ copy_from_extractor->CopyByteOrderedData(
+ copy_from_offset, field_byte_width,
+ data_sp->GetBytes() + field_byte_offset, field_byte_width,
+ target_byte_order);
+ }
+
+ if (!is_memory) {
+ // The result is in our data buffer. Let's make a variable object out
+ // of it:
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+ }
+ }
+
+ // FIXME: This is just taking a guess, r3 may very well no longer hold the
+ // return storage location.
+ // If we are going to do this right, when we make a new frame we should
+ // check to see if it uses a memory return, and if we are at the first
+ // instruction and if so stash away the return location. Then we would
+ // only return the memory return value if we know it is valid.
+
+ if (is_memory) {
+ unsigned r3_id =
+ reg_ctx_sp->GetRegisterInfoByName("r3", 0)->kinds[eRegisterKindLLDB];
+ lldb::addr_t storage_addr =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r3_id,
+ 0);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+ }
+
+ return return_valobj_sp;
+}
+
+bool ABISysV_ppc::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num = dwarf_lr;
+ uint32_t sp_reg_num = dwarf_r1;
+ uint32_t pc_reg_num = dwarf_pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ // The previous PC is in the LR
+ row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("ppc at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABISysV_ppc::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num = dwarf_r1;
+ uint32_t pc_reg_num = dwarf_lr;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ const int32_t ptr_size = 4;
+ row->GetCFAValue().SetIsRegisterDereferenced(sp_reg_num);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * 1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("ppc default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ unwind_plan.SetReturnAddressRegister(dwarf_lr);
+ return true;
+}
+
+bool ABISysV_ppc::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+// See "Register Usage" in the
+// "System V Application Binary Interface"
+// "64-bit PowerPC ELF Application Binary Interface Supplement" current version
+// is 1.9 released 2004 at http://refspecs.linuxfoundation.org/ELF/ppc/PPC-
+// elf64abi-1.9.pdf
+
+bool ABISysV_ppc::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Preserved registers are :
+ // r1,r2,r13-r31
+ // f14-f31 (not yet)
+ // v20-v31 (not yet)
+ // vrsave (not yet)
+
+ const char *name = reg_info->name;
+ if (name[0] == 'r') {
+ if ((name[1] == '1' || name[1] == '2') && name[2] == '\0')
+ return true;
+ if (name[1] == '1' && name[2] > '2')
+ return true;
+ if ((name[1] == '2' || name[1] == '3') && name[2] != '\0')
+ return true;
+ }
+
+ if (name[0] == 'f' && name[1] >= '0' && name[1] <= '9') {
+ if (name[3] == '1' && name[4] >= '4')
+ return true;
+ if ((name[3] == '2' || name[3] == '3') && name[4] != '\0')
+ return true;
+ }
+
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp
+ return true;
+ if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc
+ return true;
+ }
+ return false;
+}
+
+void ABISysV_ppc::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "System V ABI for ppc targets", CreateInstance);
+}
+
+void ABISysV_ppc::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_ppc::GetPluginNameStatic() {
+ static ConstString g_name("sysv-ppc");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_ppc::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_ppc::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.h
new file mode 100644
index 000000000000..3b199852c30d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.h
@@ -0,0 +1,104 @@
+//===-- ABISysV_ppc.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABISysV_ppc_h_
+#define liblldb_ABISysV_ppc_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_ppc : public lldb_private::ABI {
+public:
+ ~ABISysV_ppc() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The SysV ppc ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // We have a 64 bit address space, so anything is valid as opcodes
+ // aren't fixed width...
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_ppc(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_ppc_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.cpp
new file mode 100644
index 000000000000..aa7907550f29
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.cpp
@@ -0,0 +1,1091 @@
+//===-- ABISysV_ppc64.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_ppc64.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "Utility/PPC64LE_DWARF_Registers.h"
+#include "Utility/PPC64_DWARF_Registers.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+
+#define DECLARE_REGISTER_INFOS_PPC64_STRUCT
+#include "Plugins/Process/Utility/RegisterInfos_ppc64.h"
+#undef DECLARE_REGISTER_INFOS_PPC64_STRUCT
+
+#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h"
+#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+
+using namespace lldb;
+using namespace lldb_private;
+
+const lldb_private::RegisterInfo *
+ABISysV_ppc64::GetRegisterInfoArray(uint32_t &count) {
+ if (GetByteOrder() == lldb::eByteOrderLittle) {
+ count = llvm::array_lengthof(g_register_infos_ppc64le);
+ return g_register_infos_ppc64le;
+ } else {
+ count = llvm::array_lengthof(g_register_infos_ppc64);
+ return g_register_infos_ppc64;
+ }
+}
+
+size_t ABISysV_ppc64::GetRedZoneSize() const { return 224; }
+
+lldb::ByteOrder ABISysV_ppc64::GetByteOrder() const {
+ return GetProcessSP()->GetByteOrder();
+}
+
+// Static Functions
+
+ABISP
+ABISysV_ppc64::CreateInstance(lldb::ProcessSP process_sp,
+ const ArchSpec &arch) {
+ if (arch.GetTriple().isPPC64())
+ return ABISP(new ABISysV_ppc64(process_sp));
+ return ABISP();
+}
+
+bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_ppc64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1),
+ args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ if (args.size() > 8) // TODO handle more than 8 arguments
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s",
+ static_cast<uint64_t>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // First, align the SP
+
+ if (log)
+ log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)(sp & ~0xfull));
+
+ sp &= ~(0xfull); // 16-byte alignment
+
+ sp -= 544; // allocate frame to save TOC, RA and SP.
+
+ Status error;
+ uint64_t reg_value;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ ProcessSP process_sp(thread.GetProcess());
+ const RegisterInfo *lr_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+ const RegisterInfo *r2_reg_info = reg_ctx->GetRegisterInfoAtIndex(2);
+ const RegisterInfo *r12_reg_info = reg_ctx->GetRegisterInfoAtIndex(12);
+
+ // Save return address onto the stack.
+ if (log)
+ log->Printf("Pushing the return address onto the stack: 0x%" PRIx64
+ "(+16): 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)return_addr);
+ if (!process_sp->WritePointerToMemory(sp + 16, return_addr, error))
+ return false;
+
+ // Write the return address to link register.
+ if (log)
+ log->Printf("Writing LR: 0x%" PRIx64, (uint64_t)return_addr);
+ if (!reg_ctx->WriteRegisterFromUnsigned(lr_reg_info, return_addr))
+ return false;
+
+ // Write target address to %r12 register.
+ if (log)
+ log->Printf("Writing R12: 0x%" PRIx64, (uint64_t)func_addr);
+ if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr))
+ return false;
+
+ // Read TOC pointer value.
+ reg_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0);
+
+ // Write TOC pointer onto the stack.
+ uint64_t stack_offset;
+ if (GetByteOrder() == lldb::eByteOrderLittle)
+ stack_offset = 24;
+ else
+ stack_offset = 40;
+
+ if (log)
+ log->Printf("Writing R2 (TOC) at SP(0x%" PRIx64 ")+%d: 0x%" PRIx64,
+ (uint64_t)(sp + stack_offset), (int)stack_offset,
+ (uint64_t)reg_value);
+ if (!process_sp->WritePointerToMemory(sp + stack_offset, reg_value, error))
+ return false;
+
+ // Read the current SP value.
+ reg_value = reg_ctx->ReadRegisterAsUnsigned(sp_reg_info, 0);
+
+ // Save current SP onto the stack.
+ if (log)
+ log->Printf("Writing SP at SP(0x%" PRIx64 ")+0: 0x%" PRIx64, (uint64_t)sp,
+ (uint64_t)reg_value);
+ if (!process_sp->WritePointerToMemory(sp, reg_value, error))
+ return false;
+
+ // %r1 is set to the actual stack value.
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ // %pc is set to the address of the called function.
+
+ if (log)
+ log->Printf("Writing IP: 0x%" PRIx64, (uint64_t)func_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Thread &thread,
+ uint32_t *argument_register_ids,
+ unsigned int &current_argument_register,
+ addr_t &current_stack_argument) {
+ if (bit_width > 64)
+ return false; // Scalar can't hold large integer arguments
+
+ if (current_argument_register < 6) {
+ scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ argument_register_ids[current_argument_register], 0);
+ current_argument_register++;
+ if (is_signed)
+ scalar.SignExtend(bit_width);
+ } else {
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (thread.GetProcess()->ReadScalarIntegerFromMemory(
+ current_stack_argument, byte_size, is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool ABISysV_ppc64::GetArgumentValues(Thread &thread, ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ uint64_t stack_offset;
+ if (GetByteOrder() == lldb::eByteOrderLittle)
+ stack_offset = 32;
+ else
+ stack_offset = 48;
+
+ // jump over return address.
+ addr_t current_stack_argument = sp + stack_offset;
+ uint32_t argument_register_ids[8];
+
+ for (size_t i = 0; i < 8; ++i) {
+ argument_register_ids[i] =
+ reg_ctx
+ ->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i)
+ ->kinds[eRegisterKindLLDB];
+ }
+
+ unsigned int current_argument_register = 0;
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ CompilerType compiler_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ bool is_signed;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ } else if (compiler_type.IsPointerType()) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ }
+ }
+
+ return true;
+}
+
+Status ABISysV_ppc64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(reg_info, raw_value))
+ set_it_simple = true;
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else {
+ llvm::Optional<uint64_t> bit_width =
+ compiler_type.GetBitSize(frame_sp.get());
+ if (!bit_width) {
+ error.SetErrorString("can't get size of type");
+ return error;
+ }
+ if (*bit_width <= 64) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ unsigned char buffer[16];
+ ByteOrder byte_order = data.GetByteOrder();
+
+ data.CopyByteOrderedData(0, num_bytes, buffer, 16, byte_order);
+ set_it_simple = true;
+ } else {
+ // FIXME - don't know how to do 80 bit long doubles yet.
+ error.SetErrorString(
+ "We don't support returning float values > 64 bits at present");
+ }
+ }
+ }
+
+ if (!set_it_simple) {
+ // Okay we've got a structure or something that doesn't fit in a simple
+ // register. We should figure out where it really goes, but we don't
+ // support this yet.
+ error.SetErrorString("We only support setting simple integer and float "
+ "return types at present.");
+ }
+
+ return error;
+}
+
+//
+// ReturnValueExtractor
+//
+
+namespace {
+
+#define LOG_PREFIX "ReturnValueExtractor: "
+
+class ReturnValueExtractor {
+ // This class represents a register, from which data may be extracted.
+ //
+ // It may be constructed by directly specifying its index (where 0 is the
+ // first register used to return values) or by specifying the offset of a
+ // given struct field, in which case the appropriated register index will be
+ // calculated.
+ class Register {
+ public:
+ enum Type {
+ GPR, // General Purpose Register
+ FPR // Floating Point Register
+ };
+
+ // main constructor
+ //
+ // offs - field offset in struct
+ Register(Type ty, uint32_t index, uint32_t offs, RegisterContext *reg_ctx,
+ ByteOrder byte_order)
+ : m_index(index), m_offs(offs % sizeof(uint64_t)),
+ m_avail(sizeof(uint64_t) - m_offs), m_type(ty), m_reg_ctx(reg_ctx),
+ m_byte_order(byte_order) {}
+
+ // explicit index, no offset
+ Register(Type ty, uint32_t index, RegisterContext *reg_ctx,
+ ByteOrder byte_order)
+ : Register(ty, index, 0, reg_ctx, byte_order) {}
+
+ // GPR, calculate index from offs
+ Register(uint32_t offs, RegisterContext *reg_ctx, ByteOrder byte_order)
+ : Register(GPR, offs / sizeof(uint64_t), offs, reg_ctx, byte_order) {}
+
+ uint32_t Index() const { return m_index; }
+
+ // register offset where data is located
+ uint32_t Offs() const { return m_offs; }
+
+ // available bytes in this register
+ uint32_t Avail() const { return m_avail; }
+
+ bool IsValid() const {
+ if (m_index > 7) {
+ LLDB_LOG(m_log, LOG_PREFIX
+ "No more than 8 registers should be used to return values");
+ return false;
+ }
+ return true;
+ }
+
+ std::string GetName() const {
+ if (m_type == GPR)
+ return ("r" + llvm::Twine(m_index + 3)).str();
+ else
+ return ("f" + llvm::Twine(m_index + 1)).str();
+ }
+
+ // get raw register data
+ bool GetRawData(uint64_t &raw_data) {
+ const RegisterInfo *reg_info =
+ m_reg_ctx->GetRegisterInfoByName(GetName());
+ if (!reg_info) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to get RegisterInfo");
+ return false;
+ }
+
+ RegisterValue reg_val;
+ if (!m_reg_ctx->ReadRegister(reg_info, reg_val)) {
+ LLDB_LOG(m_log, LOG_PREFIX "ReadRegister() failed");
+ return false;
+ }
+
+ Status error;
+ uint32_t rc = reg_val.GetAsMemoryData(
+ reg_info, &raw_data, sizeof(raw_data), m_byte_order, error);
+ if (rc != sizeof(raw_data)) {
+ LLDB_LOG(m_log, LOG_PREFIX "GetAsMemoryData() failed");
+ return false;
+ }
+
+ return true;
+ }
+
+ private:
+ uint32_t m_index;
+ uint32_t m_offs;
+ uint32_t m_avail;
+ Type m_type;
+ RegisterContext *m_reg_ctx;
+ ByteOrder m_byte_order;
+ Log *m_log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ };
+
+ Register GetGPR(uint32_t index) const {
+ return Register(Register::GPR, index, m_reg_ctx, m_byte_order);
+ }
+
+ Register GetFPR(uint32_t index) const {
+ return Register(Register::FPR, index, m_reg_ctx, m_byte_order);
+ }
+
+ Register GetGPRByOffs(uint32_t offs) const {
+ return Register(offs, m_reg_ctx, m_byte_order);
+ }
+
+public:
+ // factory
+ static llvm::Expected<ReturnValueExtractor> Create(Thread &thread,
+ CompilerType &type) {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return llvm::make_error<llvm::StringError>(
+ LOG_PREFIX "Failed to get RegisterContext",
+ llvm::inconvertibleErrorCode());
+
+ ProcessSP process_sp = thread.GetProcess();
+ if (!process_sp)
+ return llvm::make_error<llvm::StringError>(
+ LOG_PREFIX "GetProcess() failed", llvm::inconvertibleErrorCode());
+
+ return ReturnValueExtractor(thread, type, reg_ctx, process_sp);
+ }
+
+ // main method: get value of the type specified at construction time
+ ValueObjectSP GetValue() {
+ const uint32_t type_flags = m_type.GetTypeInfo();
+
+ // call the appropriate type handler
+ ValueSP value_sp;
+ ValueObjectSP valobj_sp;
+ if (type_flags & eTypeIsScalar) {
+ if (type_flags & eTypeIsInteger) {
+ value_sp = GetIntegerValue(0);
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ LLDB_LOG(m_log, LOG_PREFIX "Complex numbers are not supported yet");
+ return ValueObjectSP();
+ } else {
+ value_sp = GetFloatValue(m_type, 0);
+ }
+ }
+ } else if (type_flags & eTypeIsPointer) {
+ value_sp = GetPointerValue(0);
+ }
+
+ if (value_sp) {
+ valobj_sp = ValueObjectConstResult::Create(
+ m_thread.GetStackFrameAtIndex(0).get(), *value_sp, ConstString(""));
+ } else if (type_flags & eTypeIsVector) {
+ valobj_sp = GetVectorValueObject();
+ } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass) {
+ valobj_sp = GetStructValueObject();
+ }
+
+ return valobj_sp;
+ }
+
+private:
+ // data
+ Thread &m_thread;
+ CompilerType &m_type;
+ uint64_t m_byte_size;
+ std::unique_ptr<DataBufferHeap> m_data_up;
+ int32_t m_src_offs = 0;
+ int32_t m_dst_offs = 0;
+ bool m_packed = false;
+ Log *m_log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ RegisterContext *m_reg_ctx;
+ ProcessSP m_process_sp;
+ ByteOrder m_byte_order;
+ uint32_t m_addr_size;
+
+ // methods
+
+ // constructor
+ ReturnValueExtractor(Thread &thread, CompilerType &type,
+ RegisterContext *reg_ctx, ProcessSP process_sp)
+ : m_thread(thread), m_type(type),
+ m_byte_size(m_type.GetByteSize(nullptr).getValueOr(0)),
+ m_data_up(new DataBufferHeap(m_byte_size, 0)), m_reg_ctx(reg_ctx),
+ m_process_sp(process_sp), m_byte_order(process_sp->GetByteOrder()),
+ m_addr_size(
+ process_sp->GetTarget().GetArchitecture().GetAddressByteSize()) {}
+
+ // build a new scalar value
+ ValueSP NewScalarValue(CompilerType &type) {
+ ValueSP value_sp(new Value);
+ value_sp->SetCompilerType(type);
+ value_sp->SetValueType(Value::eValueTypeScalar);
+ return value_sp;
+ }
+
+ // get an integer value in the specified register
+ ValueSP GetIntegerValue(uint32_t reg_index) {
+ uint64_t raw_value;
+ auto reg = GetGPR(reg_index);
+ if (!reg.GetRawData(raw_value))
+ return ValueSP();
+
+ // build value from data
+ ValueSP value_sp(NewScalarValue(m_type));
+
+ uint32_t type_flags = m_type.GetTypeInfo();
+ bool is_signed = (type_flags & eTypeIsSigned) != 0;
+
+ switch (m_byte_size) {
+ case sizeof(uint64_t):
+ if (is_signed)
+ value_sp->GetScalar() = (int64_t)(raw_value);
+ else
+ value_sp->GetScalar() = (uint64_t)(raw_value);
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value_sp->GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value_sp->GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value_sp->GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value_sp->GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value_sp->GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value_sp->GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ break;
+
+ default:
+ llvm_unreachable("Invalid integer size");
+ }
+
+ return value_sp;
+ }
+
+ // get a floating point value on the specified register
+ ValueSP GetFloatValue(CompilerType &type, uint32_t reg_index) {
+ uint64_t raw_data;
+ auto reg = GetFPR(reg_index);
+ if (!reg.GetRawData(raw_data))
+ return {};
+
+ // build value from data
+ ValueSP value_sp(NewScalarValue(type));
+
+ DataExtractor de(&raw_data, sizeof(raw_data), m_byte_order, m_addr_size);
+
+ offset_t offset = 0;
+ llvm::Optional<uint64_t> byte_size = type.GetByteSize(nullptr);
+ if (!byte_size)
+ return {};
+ switch (*byte_size) {
+ case sizeof(float):
+ value_sp->GetScalar() = (float)de.GetDouble(&offset);
+ break;
+
+ case sizeof(double):
+ value_sp->GetScalar() = de.GetDouble(&offset);
+ break;
+
+ default:
+ llvm_unreachable("Invalid floating point size");
+ }
+
+ return value_sp;
+ }
+
+ // get pointer value from register
+ ValueSP GetPointerValue(uint32_t reg_index) {
+ uint64_t raw_data;
+ auto reg = GetGPR(reg_index);
+ if (!reg.GetRawData(raw_data))
+ return ValueSP();
+
+ // build value from raw data
+ ValueSP value_sp(NewScalarValue(m_type));
+ value_sp->GetScalar() = raw_data;
+ return value_sp;
+ }
+
+ // build the ValueObject from our data buffer
+ ValueObjectSP BuildValueObject() {
+ DataExtractor de(DataBufferSP(m_data_up.release()), m_byte_order,
+ m_addr_size);
+ return ValueObjectConstResult::Create(&m_thread, m_type, ConstString(""),
+ de);
+ }
+
+ // get a vector return value
+ ValueObjectSP GetVectorValueObject() {
+ const uint32_t MAX_VRS = 2;
+
+ // get first V register used to return values
+ const RegisterInfo *vr[MAX_VRS];
+ vr[0] = m_reg_ctx->GetRegisterInfoByName("vr2");
+ if (!vr[0]) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to get vr2 RegisterInfo");
+ return ValueObjectSP();
+ }
+
+ const uint32_t vr_size = vr[0]->byte_size;
+ size_t vrs = 1;
+ if (m_byte_size > 2 * vr_size) {
+ LLDB_LOG(
+ m_log, LOG_PREFIX
+ "Returning vectors that don't fit in 2 VR regs is not supported");
+ return ValueObjectSP();
+ }
+
+ // load vr3, if needed
+ if (m_byte_size > vr_size) {
+ vrs++;
+ vr[1] = m_reg_ctx->GetRegisterInfoByName("vr3");
+ if (!vr[1]) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to get vr3 RegisterInfo");
+ return ValueObjectSP();
+ }
+ }
+
+ // Get the whole contents of vector registers and let the logic here
+ // arrange the data properly.
+
+ RegisterValue vr_val[MAX_VRS];
+ Status error;
+ std::unique_ptr<DataBufferHeap> vr_data(
+ new DataBufferHeap(vrs * vr_size, 0));
+
+ for (uint32_t i = 0; i < vrs; i++) {
+ if (!m_reg_ctx->ReadRegister(vr[i], vr_val[i])) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to read vector register contents");
+ return ValueObjectSP();
+ }
+ if (!vr_val[i].GetAsMemoryData(vr[i], vr_data->GetBytes() + i * vr_size,
+ vr_size, m_byte_order, error)) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to extract vector register bytes");
+ return ValueObjectSP();
+ }
+ }
+
+ // The compiler generated code seems to always put the vector elements at
+ // the end of the vector register, in case they don't occupy all of it.
+ // This offset variable handles this.
+ uint32_t offs = 0;
+ if (m_byte_size < vr_size)
+ offs = vr_size - m_byte_size;
+
+ // copy extracted data to our buffer
+ memcpy(m_data_up->GetBytes(), vr_data->GetBytes() + offs, m_byte_size);
+ return BuildValueObject();
+ }
+
+ // get a struct return value
+ ValueObjectSP GetStructValueObject() {
+ // case 1: get from stack
+ if (m_byte_size > 2 * sizeof(uint64_t)) {
+ uint64_t addr;
+ auto reg = GetGPR(0);
+ if (!reg.GetRawData(addr))
+ return {};
+
+ Status error;
+ size_t rc = m_process_sp->ReadMemory(addr, m_data_up->GetBytes(),
+ m_byte_size, error);
+ if (rc != m_byte_size) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to read memory pointed by r3");
+ return ValueObjectSP();
+ }
+ return BuildValueObject();
+ }
+
+ // get number of children
+ const bool omit_empty_base_classes = true;
+ uint32_t n = m_type.GetNumChildren(omit_empty_base_classes, nullptr);
+ if (!n) {
+ LLDB_LOG(m_log, LOG_PREFIX "No children found in struct");
+ return {};
+ }
+
+ // case 2: homogeneous double or float aggregate
+ CompilerType elem_type;
+ if (m_type.IsHomogeneousAggregate(&elem_type)) {
+ uint32_t type_flags = elem_type.GetTypeInfo();
+ llvm::Optional<uint64_t> elem_size = elem_type.GetByteSize(nullptr);
+ if (!elem_size)
+ return {};
+ if (type_flags & eTypeIsComplex || !(type_flags & eTypeIsFloat)) {
+ LLDB_LOG(m_log,
+ LOG_PREFIX "Unexpected type found in homogeneous aggregate");
+ return {};
+ }
+
+ for (uint32_t i = 0; i < n; i++) {
+ ValueSP val_sp = GetFloatValue(elem_type, i);
+ if (!val_sp)
+ return {};
+
+ // copy to buffer
+ Status error;
+ size_t rc = val_sp->GetScalar().GetAsMemoryData(
+ m_data_up->GetBytes() + m_dst_offs, *elem_size, m_byte_order,
+ error);
+ if (rc != *elem_size) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to get float data");
+ return {};
+ }
+ m_dst_offs += *elem_size;
+ }
+ return BuildValueObject();
+ }
+
+ // case 3: get from GPRs
+
+ // first, check if this is a packed struct or not
+ ClangASTContext *ast =
+ llvm::dyn_cast<ClangASTContext>(m_type.GetTypeSystem());
+ if (ast) {
+ clang::RecordDecl *record_decl = ClangASTContext::GetAsRecordDecl(m_type);
+
+ if (record_decl) {
+ auto attrs = record_decl->attrs();
+ for (const auto &attr : attrs) {
+ if (attr->getKind() == clang::attr::Packed) {
+ m_packed = true;
+ break;
+ }
+ }
+ }
+ }
+
+ LLDB_LOG(m_log, LOG_PREFIX "{0} struct",
+ m_packed ? "packed" : "not packed");
+
+ for (uint32_t i = 0; i < n; i++) {
+ std::string name;
+ uint32_t size;
+ GetChildType(i, name, size);
+ // NOTE: the offset returned by GetChildCompilerTypeAtIndex()
+ // can't be used because it never considers alignment bytes
+ // between struct fields.
+ LLDB_LOG(m_log, LOG_PREFIX "field={0}, size={1}", name, size);
+ if (!ExtractField(size))
+ return ValueObjectSP();
+ }
+
+ return BuildValueObject();
+ }
+
+ // extract 'size' bytes at 'offs' from GPRs
+ bool ExtractFromRegs(int32_t offs, uint32_t size, void *buf) {
+ while (size) {
+ auto reg = GetGPRByOffs(offs);
+ if (!reg.IsValid())
+ return false;
+
+ uint32_t n = std::min(reg.Avail(), size);
+ uint64_t raw_data;
+
+ if (!reg.GetRawData(raw_data))
+ return false;
+
+ memcpy(buf, (char *)&raw_data + reg.Offs(), n);
+ offs += n;
+ size -= n;
+ buf = (char *)buf + n;
+ }
+ return true;
+ }
+
+ // extract one field from GPRs and put it in our buffer
+ bool ExtractField(uint32_t size) {
+ auto reg = GetGPRByOffs(m_src_offs);
+ if (!reg.IsValid())
+ return false;
+
+ // handle padding
+ if (!m_packed) {
+ uint32_t n = m_src_offs % size;
+
+ // not 'size' bytes aligned
+ if (n) {
+ LLDB_LOG(m_log,
+ LOG_PREFIX "Extracting {0} alignment bytes at offset {1}", n,
+ m_src_offs);
+ // get alignment bytes
+ if (!ExtractFromRegs(m_src_offs, n, m_data_up->GetBytes() + m_dst_offs))
+ return false;
+ m_src_offs += n;
+ m_dst_offs += n;
+ }
+ }
+
+ // get field
+ LLDB_LOG(m_log, LOG_PREFIX "Extracting {0} field bytes at offset {1}", size,
+ m_src_offs);
+ if (!ExtractFromRegs(m_src_offs, size, m_data_up->GetBytes() + m_dst_offs))
+ return false;
+ m_src_offs += size;
+ m_dst_offs += size;
+ return true;
+ }
+
+ // get child
+ CompilerType GetChildType(uint32_t i, std::string &name, uint32_t &size) {
+ // GetChild constant inputs
+ const bool transparent_pointers = false;
+ const bool omit_empty_base_classes = true;
+ const bool ignore_array_bounds = false;
+ // GetChild output params
+ int32_t child_offs;
+ uint32_t child_bitfield_bit_size;
+ uint32_t child_bitfield_bit_offset;
+ bool child_is_base_class;
+ bool child_is_deref_of_parent;
+ ValueObject *valobj = nullptr;
+ uint64_t language_flags;
+ ExecutionContext exe_ctx;
+ m_thread.CalculateExecutionContext(exe_ctx);
+
+ return m_type.GetChildCompilerTypeAtIndex(
+ &exe_ctx, i, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, name, size, child_offs, child_bitfield_bit_size,
+ child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, valobj, language_flags);
+ }
+};
+
+#undef LOG_PREFIX
+
+} // anonymous namespace
+
+ValueObjectSP
+ABISysV_ppc64::GetReturnValueObjectSimple(Thread &thread,
+ CompilerType &type) const {
+ if (!type)
+ return ValueObjectSP();
+
+ auto exp_extractor = ReturnValueExtractor::Create(thread, type);
+ if (!exp_extractor) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ LLDB_LOG_ERROR(log, exp_extractor.takeError(),
+ "Extracting return value failed: {0}");
+ return ValueObjectSP();
+ }
+
+ return exp_extractor.get().GetValue();
+}
+
+ValueObjectSP ABISysV_ppc64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ return GetReturnValueObjectSimple(thread, return_compiler_type);
+}
+
+bool ABISysV_ppc64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num;
+ uint32_t sp_reg_num;
+ uint32_t pc_reg_num;
+
+ if (GetByteOrder() == lldb::eByteOrderLittle) {
+ lr_reg_num = ppc64le_dwarf::dwarf_lr_ppc64le;
+ sp_reg_num = ppc64le_dwarf::dwarf_r1_ppc64le;
+ pc_reg_num = ppc64le_dwarf::dwarf_pc_ppc64le;
+ } else {
+ lr_reg_num = ppc64_dwarf::dwarf_lr_ppc64;
+ sp_reg_num = ppc64_dwarf::dwarf_r1_ppc64;
+ pc_reg_num = ppc64_dwarf::dwarf_pc_ppc64;
+ }
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ // The previous PC is in the LR
+ row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("ppc64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABISysV_ppc64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num;
+ uint32_t pc_reg_num;
+ uint32_t cr_reg_num;
+
+ if (GetByteOrder() == lldb::eByteOrderLittle) {
+ sp_reg_num = ppc64le_dwarf::dwarf_r1_ppc64le;
+ pc_reg_num = ppc64le_dwarf::dwarf_lr_ppc64le;
+ cr_reg_num = ppc64le_dwarf::dwarf_cr_ppc64le;
+ } else {
+ sp_reg_num = ppc64_dwarf::dwarf_r1_ppc64;
+ pc_reg_num = ppc64_dwarf::dwarf_lr_ppc64;
+ cr_reg_num = ppc64_dwarf::dwarf_cr_ppc64;
+ }
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 8;
+ row->GetCFAValue().SetIsRegisterDereferenced(sp_reg_num);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * 2, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(cr_reg_num, ptr_size, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("ppc64 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ unwind_plan.SetReturnAddressRegister(pc_reg_num);
+ return true;
+}
+
+bool ABISysV_ppc64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+// See "Register Usage" in the
+// "System V Application Binary Interface"
+// "64-bit PowerPC ELF Application Binary Interface Supplement" current version
+// is 2 released 2015 at
+// https://members.openpowerfoundation.org/document/dl/576
+bool ABISysV_ppc64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Preserved registers are :
+ // r1,r2,r13-r31
+ // cr2-cr4 (partially preserved)
+ // f14-f31 (not yet)
+ // v20-v31 (not yet)
+ // vrsave (not yet)
+
+ const char *name = reg_info->name;
+ if (name[0] == 'r') {
+ if ((name[1] == '1' || name[1] == '2') && name[2] == '\0')
+ return true;
+ if (name[1] == '1' && name[2] > '2')
+ return true;
+ if ((name[1] == '2' || name[1] == '3') && name[2] != '\0')
+ return true;
+ }
+
+ if (name[0] == 'f' && name[1] >= '0' && name[2] <= '9') {
+ if (name[2] == '\0')
+ return false;
+ if (name[1] == '1' && name[2] >= '4')
+ return true;
+ if ((name[1] == '2' || name[1] == '3') && name[2] != '\0')
+ return true;
+ }
+
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp
+ return false;
+ if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc
+ return true;
+ }
+ return false;
+}
+
+void ABISysV_ppc64::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for ppc64 targets", CreateInstance);
+}
+
+void ABISysV_ppc64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_ppc64::GetPluginNameStatic() {
+ static ConstString g_name("sysv-ppc64");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_ppc64::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_ppc64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h
new file mode 100644
index 000000000000..d5fb09eec0d0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h
@@ -0,0 +1,106 @@
+//===-- ABISysV_ppc64.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABISysV_ppc64_h_
+#define liblldb_ABISysV_ppc64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_ppc64 : public lldb_private::ABI {
+public:
+ ~ABISysV_ppc64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The SysV ppc64 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // We have a 64 bit address space, so anything is valid as opcodes
+ // aren't fixed width...
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_ppc64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+
+ lldb::ByteOrder GetByteOrder() const;
+};
+
+#endif // liblldb_ABISysV_ppc64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp
new file mode 100644
index 000000000000..abe847b386a8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp
@@ -0,0 +1,749 @@
+//===-- ABISysV_s390x.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_s390x.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ // General Purpose Registers
+ dwarf_r0_s390x = 0,
+ dwarf_r1_s390x,
+ dwarf_r2_s390x,
+ dwarf_r3_s390x,
+ dwarf_r4_s390x,
+ dwarf_r5_s390x,
+ dwarf_r6_s390x,
+ dwarf_r7_s390x,
+ dwarf_r8_s390x,
+ dwarf_r9_s390x,
+ dwarf_r10_s390x,
+ dwarf_r11_s390x,
+ dwarf_r12_s390x,
+ dwarf_r13_s390x,
+ dwarf_r14_s390x,
+ dwarf_r15_s390x,
+ // Floating Point Registers / Vector Registers 0-15
+ dwarf_f0_s390x = 16,
+ dwarf_f2_s390x,
+ dwarf_f4_s390x,
+ dwarf_f6_s390x,
+ dwarf_f1_s390x,
+ dwarf_f3_s390x,
+ dwarf_f5_s390x,
+ dwarf_f7_s390x,
+ dwarf_f8_s390x,
+ dwarf_f10_s390x,
+ dwarf_f12_s390x,
+ dwarf_f14_s390x,
+ dwarf_f9_s390x,
+ dwarf_f11_s390x,
+ dwarf_f13_s390x,
+ dwarf_f15_s390x,
+ // Access Registers
+ dwarf_acr0_s390x = 48,
+ dwarf_acr1_s390x,
+ dwarf_acr2_s390x,
+ dwarf_acr3_s390x,
+ dwarf_acr4_s390x,
+ dwarf_acr5_s390x,
+ dwarf_acr6_s390x,
+ dwarf_acr7_s390x,
+ dwarf_acr8_s390x,
+ dwarf_acr9_s390x,
+ dwarf_acr10_s390x,
+ dwarf_acr11_s390x,
+ dwarf_acr12_s390x,
+ dwarf_acr13_s390x,
+ dwarf_acr14_s390x,
+ dwarf_acr15_s390x,
+ // Program Status Word
+ dwarf_pswm_s390x = 64,
+ dwarf_pswa_s390x,
+ // Vector Registers 16-31
+ dwarf_v16_s390x = 68,
+ dwarf_v18_s390x,
+ dwarf_v20_s390x,
+ dwarf_v22_s390x,
+ dwarf_v17_s390x,
+ dwarf_v19_s390x,
+ dwarf_v21_s390x,
+ dwarf_v23_s390x,
+ dwarf_v24_s390x,
+ dwarf_v26_s390x,
+ dwarf_v28_s390x,
+ dwarf_v30_s390x,
+ dwarf_v25_s390x,
+ dwarf_v27_s390x,
+ dwarf_v29_s390x,
+ dwarf_v31_s390x,
+};
+
+// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB
+
+#define DEFINE_REG(name, size, alt, generic) \
+ { \
+ #name, alt, size, 0, eEncodingUint, eFormatHex, \
+ {dwarf_##name##_s390x, dwarf_##name##_s390x, generic, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+static RegisterInfo g_register_infos[] = {
+ DEFINE_REG(r0, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r1, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r2, 8, "arg1", LLDB_REGNUM_GENERIC_ARG1),
+ DEFINE_REG(r3, 8, "arg2", LLDB_REGNUM_GENERIC_ARG2),
+ DEFINE_REG(r4, 8, "arg3", LLDB_REGNUM_GENERIC_ARG3),
+ DEFINE_REG(r5, 8, "arg4", LLDB_REGNUM_GENERIC_ARG4),
+ DEFINE_REG(r6, 8, "arg5", LLDB_REGNUM_GENERIC_ARG5),
+ DEFINE_REG(r7, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r8, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r9, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r10, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r11, 8, "fp", LLDB_REGNUM_GENERIC_FP),
+ DEFINE_REG(r12, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r13, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r14, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r15, 8, "sp", LLDB_REGNUM_GENERIC_SP),
+ DEFINE_REG(acr0, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr1, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr2, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr3, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr4, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr5, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr6, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr7, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr8, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr9, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr10, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr11, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr12, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr13, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr14, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr15, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(pswm, 8, "flags", LLDB_REGNUM_GENERIC_FLAGS),
+ DEFINE_REG(pswa, 8, "pc", LLDB_REGNUM_GENERIC_PC),
+ DEFINE_REG(f0, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f1, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f2, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f3, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f4, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f5, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f6, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f7, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f8, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f9, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f10, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f11, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f12, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f13, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f14, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f15, 8, nullptr, LLDB_INVALID_REGNUM),
+};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_s390x::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABISysV_s390x::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABISysV_s390x::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::systemz) {
+ return ABISP(new ABISysV_s390x(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABISysV_s390x::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_s390x::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1),
+ args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const RegisterInfo *ra_reg_info = reg_ctx->GetRegisterInfoByName("r14", 0);
+ ProcessSP process_sp(thread.GetProcess());
+
+ // Allocate a new stack frame and space for stack arguments if necessary
+
+ addr_t arg_pos = 0;
+ if (args.size() > 5) {
+ sp -= 8 * (args.size() - 5);
+ arg_pos = sp;
+ }
+
+ sp -= 160;
+
+ // Process arguments
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ if (i < 5) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s",
+ static_cast<uint64_t>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ } else {
+ Status error;
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") onto stack",
+ static_cast<uint64_t>(i + 1), args[i]);
+ if (!process_sp->WritePointerToMemory(arg_pos, args[i], error))
+ return false;
+ arg_pos += 8;
+ }
+ }
+
+ // %r14 is set to the return address
+
+ if (log)
+ log->Printf("Writing RA: 0x%" PRIx64, (uint64_t)return_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(ra_reg_info, return_addr))
+ return false;
+
+ // %r15 is set to the actual stack value.
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ // %pc is set to the address of the called function.
+
+ if (log)
+ log->Printf("Writing PC: 0x%" PRIx64, (uint64_t)func_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Thread &thread,
+ uint32_t *argument_register_ids,
+ unsigned int &current_argument_register,
+ addr_t &current_stack_argument) {
+ if (bit_width > 64)
+ return false; // Scalar can't hold large integer arguments
+
+ if (current_argument_register < 5) {
+ scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ argument_register_ids[current_argument_register], 0);
+ current_argument_register++;
+ if (is_signed)
+ scalar.SignExtend(bit_width);
+ } else {
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (thread.GetProcess()->ReadScalarIntegerFromMemory(
+ current_stack_argument + 8 - byte_size, byte_size, is_signed,
+ scalar, error)) {
+ current_stack_argument += 8;
+ return true;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool ABISysV_s390x::GetArgumentValues(Thread &thread, ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 160;
+
+ uint32_t argument_register_ids[5];
+
+ argument_register_ids[0] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[1] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[2] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[3] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[4] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG5)
+ ->kinds[eRegisterKindLLDB];
+
+ unsigned int current_argument_register = 0;
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ CompilerType compiler_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ bool is_signed;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ } else if (compiler_type.IsPointerType()) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ }
+ }
+
+ return true;
+}
+
+Status ABISysV_s390x::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(reg_info, raw_value))
+ set_it_simple = true;
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else {
+ llvm::Optional<uint64_t> bit_width =
+ compiler_type.GetBitSize(frame_sp.get());
+ if (!bit_width) {
+ error.SetErrorString("can't get type size");
+ return error;
+ }
+ if (*bit_width <= 64) {
+ const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0);
+ RegisterValue f0_value;
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ unsigned char buffer[8];
+ ByteOrder byte_order = data.GetByteOrder();
+
+ data.CopyByteOrderedData(0, num_bytes, buffer, 8, byte_order);
+ f0_value.SetBytes(buffer, 8, byte_order);
+ reg_ctx->WriteRegister(f0_info, f0_value);
+ set_it_simple = true;
+ } else {
+ // FIXME - don't know how to do long doubles yet.
+ error.SetErrorString(
+ "We don't support returning float values > 64 bits at present");
+ }
+ }
+ }
+
+ if (!set_it_simple) {
+ // Okay we've got a structure or something that doesn't fit in a simple
+ // register. We should figure out where it really goes, but we don't
+ // support this yet.
+ error.SetErrorString("We only support setting simple integer and float "
+ "return types at present.");
+ }
+
+ return error;
+}
+
+ValueObjectSP ABISysV_s390x::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType, return_value_type);
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo();
+ if (type_flags & eTypeIsScalar) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger) {
+ // Extract the register context so we can read arguments from registers.
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("r2", 0), 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size <= sizeof(long double)) {
+ const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0);
+ RegisterValue f0_value;
+ if (reg_ctx->ReadRegister(f0_info, f0_value)) {
+ DataExtractor data;
+ if (f0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = (float)data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = (double)data.GetDouble(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(long double)) {
+ // Don't handle long double yet.
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsPointer) {
+ unsigned r2_id =
+ reg_ctx->GetRegisterInfoByName("r2", 0)->kinds[eRegisterKindLLDB];
+ value.GetScalar() =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r2_id, 0);
+ value.SetValueType(Value::eValueTypeScalar);
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ }
+
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_s390x::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type);
+ if (return_valobj_sp)
+ return return_valobj_sp;
+
+ RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
+ if (!reg_ctx_sp)
+ return return_valobj_sp;
+
+ if (return_compiler_type.IsAggregateType()) {
+ // FIXME: This is just taking a guess, r2 may very well no longer hold the
+ // return storage location.
+ // If we are going to do this right, when we make a new frame we should
+ // check to see if it uses a memory return, and if we are at the first
+ // instruction and if so stash away the return location. Then we would
+ // only return the memory return value if we know it is valid.
+
+ unsigned r2_id =
+ reg_ctx_sp->GetRegisterInfoByName("r2", 0)->kinds[eRegisterKindLLDB];
+ lldb::addr_t storage_addr =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r2_id, 0);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+
+ return return_valobj_sp;
+}
+
+bool ABISysV_s390x::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value + 160
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r15_s390x, 160);
+
+ // The previous PC is in r14
+ row->SetRegisterLocationToRegister(dwarf_pswa_s390x, dwarf_r14_s390x, true);
+
+ // All other registers are the same.
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("s390x at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+bool ABISysV_s390x::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ // There's really no default way to unwind on s390x. Trust the .eh_frame CFI,
+ // which should always be good.
+ return false;
+}
+
+bool ABISysV_s390x::GetFallbackRegisterLocation(
+ const RegisterInfo *reg_info,
+ UnwindPlan::Row::RegisterLocation &unwind_regloc) {
+ // If a volatile register is being requested, we don't want to forward the
+ // next frame's register contents up the stack -- the register is not
+ // retrievable at this frame.
+ if (RegisterIsVolatile(reg_info)) {
+ unwind_regloc.SetUndefined();
+ return true;
+ }
+
+ return false;
+}
+
+bool ABISysV_s390x::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+bool ABISysV_s390x::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Preserved registers are :
+ // r6-r13, r15
+ // f8-f15
+
+ const char *name = reg_info->name;
+ if (name[0] == 'r') {
+ switch (name[1]) {
+ case '6': // r6
+ case '7': // r7
+ case '8': // r8
+ case '9': // r9
+ return name[2] == '\0';
+
+ case '1': // r10, r11, r12, r13, r15
+ if ((name[2] >= '0' && name[2] <= '3') || name[2] == '5')
+ return name[3] == '\0';
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (name[0] == 'f') {
+ switch (name[1]) {
+ case '8': // r8
+ case '9': // r9
+ return name[2] == '\0';
+
+ case '1': // r10, r11, r12, r13, r14, r15
+ if (name[2] >= '0' && name[2] <= '5')
+ return name[3] == '\0';
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Accept shorter-variant versions
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp
+ return true;
+ if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc
+ return true;
+ }
+ return false;
+}
+
+void ABISysV_s390x::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for s390x targets", CreateInstance);
+}
+
+void ABISysV_s390x::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_s390x::GetPluginNameStatic() {
+ static ConstString g_name("sysv-s390x");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_s390x::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_s390x::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h
new file mode 100644
index 000000000000..13df477e84bc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h
@@ -0,0 +1,96 @@
+//===-- ABISysV_s390x.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABISysV_s390x_h_
+#define liblldb_ABISysV_s390x_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_s390x : public lldb_private::ABI {
+public:
+ ~ABISysV_s390x() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool GetFallbackRegisterLocation(
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::UnwindPlan::Row::RegisterLocation &unwind_regloc) override;
+
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Code addressed must be 2 byte aligned
+ return (pc & 1ull) == 0;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_s390x(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_s390x_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp
new file mode 100644
index 000000000000..6c7b45f63399
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp
@@ -0,0 +1,1086 @@
+//===-- ABISysV_x86_64.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_x86_64.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ dwarf_rax = 0,
+ dwarf_rdx,
+ dwarf_rcx,
+ dwarf_rbx,
+ dwarf_rsi,
+ dwarf_rdi,
+ dwarf_rbp,
+ dwarf_rsp,
+ dwarf_r8,
+ dwarf_r9,
+ dwarf_r10,
+ dwarf_r11,
+ dwarf_r12,
+ dwarf_r13,
+ dwarf_r14,
+ dwarf_r15,
+ dwarf_rip,
+ dwarf_xmm0,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7,
+ dwarf_xmm8,
+ dwarf_xmm9,
+ dwarf_xmm10,
+ dwarf_xmm11,
+ dwarf_xmm12,
+ dwarf_xmm13,
+ dwarf_xmm14,
+ dwarf_xmm15,
+ dwarf_stmm0,
+ dwarf_stmm1,
+ dwarf_stmm2,
+ dwarf_stmm3,
+ dwarf_stmm4,
+ dwarf_stmm5,
+ dwarf_stmm6,
+ dwarf_stmm7,
+ dwarf_ymm0,
+ dwarf_ymm1,
+ dwarf_ymm2,
+ dwarf_ymm3,
+ dwarf_ymm4,
+ dwarf_ymm5,
+ dwarf_ymm6,
+ dwarf_ymm7,
+ dwarf_ymm8,
+ dwarf_ymm9,
+ dwarf_ymm10,
+ dwarf_ymm11,
+ dwarf_ymm12,
+ dwarf_ymm13,
+ dwarf_ymm14,
+ dwarf_ymm15,
+ dwarf_bnd0 = 126,
+ dwarf_bnd1,
+ dwarf_bnd2,
+ dwarf_bnd3
+};
+
+static RegisterInfo g_register_infos[] = {
+ // clang-format off
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME DWARF GENERIC LLDB NATIVE
+ // ======== ======= == === ============= =================== ======================= ===================== =========================== ===================== ======================
+ {"rax", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_rax, dwarf_rax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rbx", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_rbx, dwarf_rbx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rcx", "arg4", 8, 0, eEncodingUint, eFormatHex, {dwarf_rcx, dwarf_rcx, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rdx", "arg3", 8, 0, eEncodingUint, eFormatHex, {dwarf_rdx, dwarf_rdx, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rsi", "arg2", 8, 0, eEncodingUint, eFormatHex, {dwarf_rsi, dwarf_rsi, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rdi", "arg1", 8, 0, eEncodingUint, eFormatHex, {dwarf_rdi, dwarf_rdi, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rbp", "fp", 8, 0, eEncodingUint, eFormatHex, {dwarf_rbp, dwarf_rbp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rsp", "sp", 8, 0, eEncodingUint, eFormatHex, {dwarf_rsp, dwarf_rsp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r8", "arg5", 8, 0, eEncodingUint, eFormatHex, {dwarf_r8, dwarf_r8, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r9", "arg6", 8, 0, eEncodingUint, eFormatHex, {dwarf_r9, dwarf_r9, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r10", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r11", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r12", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r13", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r14", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r15", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rip", "pc", 8, 0, eEncodingUint, eFormatHex, {dwarf_rip, dwarf_rip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rflags", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"cs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ss", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ds", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"es", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"gs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm0", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm0, dwarf_stmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm1", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm1, dwarf_stmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm2", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm2, dwarf_stmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm3", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm3, dwarf_stmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm4", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm4, dwarf_stmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm5", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm5, dwarf_stmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm6", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm6, dwarf_stmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm7", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm7, dwarf_stmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fctrl", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fstat", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ftag", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fiseg", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fioff", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"foseg", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fooff", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fop", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm0, dwarf_xmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm1, dwarf_xmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm2, dwarf_xmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm3, dwarf_xmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm4", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm4, dwarf_xmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm5", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm5, dwarf_xmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm6", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm6, dwarf_xmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm7", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm7, dwarf_xmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm8", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm8, dwarf_xmm8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm9", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm9, dwarf_xmm9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm10", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm10, dwarf_xmm10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm11", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm11, dwarf_xmm11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm12", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm12, dwarf_xmm12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm13", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm13, dwarf_xmm13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm14", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm14, dwarf_xmm14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm15", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm15, dwarf_xmm15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"mxcsr", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm0", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm0, dwarf_ymm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm1", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm1, dwarf_ymm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm2", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm2, dwarf_ymm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm3", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm3, dwarf_ymm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm4", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm4, dwarf_ymm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm5", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm5, dwarf_ymm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm6", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm6, dwarf_ymm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm7", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm7, dwarf_ymm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm8", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm8, dwarf_ymm8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm9", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm9, dwarf_ymm9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm10", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm10, dwarf_ymm10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm11", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm11, dwarf_ymm11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm12", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm12, dwarf_ymm12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm13", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm13, dwarf_ymm13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm14", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm14, dwarf_ymm14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm15", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm15, dwarf_ymm15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64, {dwarf_bnd0, dwarf_bnd0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64, {dwarf_bnd1, dwarf_bnd1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64, {dwarf_bnd2, dwarf_bnd2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64, {dwarf_bnd3, dwarf_bnd3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bndcfgu", nullptr, 8, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bndstatus",nullptr, 8, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ // clang-format on
+};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_x86_64::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+bool ABISysV_x86_64::GetPointerReturnRegister(const char *&name) {
+ name = "rax";
+ return true;
+}
+
+size_t ABISysV_x86_64::GetRedZoneSize() const { return 128; }
+
+// Static Functions
+
+ABISP
+ABISysV_x86_64::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ const llvm::Triple::OSType os_type = arch.GetTriple().getOS();
+ if (arch_type == llvm::Triple::x86_64) {
+ switch(os_type) {
+ case llvm::Triple::OSType::MacOSX:
+ case llvm::Triple::OSType::Linux:
+ case llvm::Triple::OSType::FreeBSD:
+ case llvm::Triple::OSType::NetBSD:
+ case llvm::Triple::OSType::Solaris:
+ case llvm::Triple::OSType::UnknownOS:
+ return ABISP(new ABISysV_x86_64(process_sp));
+ default:
+ return ABISP();
+ }
+ }
+ return ABISP();
+}
+
+bool ABISysV_x86_64::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_x86_64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1),
+ args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ if (args.size() > 6) // TODO handle more than 6 arguments
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s",
+ static_cast<uint64_t>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // First, align the SP
+
+ if (log)
+ log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)(sp & ~0xfull));
+
+ sp &= ~(0xfull); // 16-byte alignment
+
+ sp -= 8;
+
+ Status error;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ ProcessSP process_sp(thread.GetProcess());
+
+ RegisterValue reg_value;
+ if (log)
+ log->Printf("Pushing the return address onto the stack: 0x%" PRIx64
+ ": 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)return_addr);
+
+ // Save return address onto the stack
+ if (!process_sp->WritePointerToMemory(sp, return_addr, error))
+ return false;
+
+ // %rsp is set to the actual stack value.
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ // %rip is set to the address of the called function.
+
+ if (log)
+ log->Printf("Writing IP: 0x%" PRIx64, (uint64_t)func_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Thread &thread,
+ uint32_t *argument_register_ids,
+ unsigned int &current_argument_register,
+ addr_t &current_stack_argument) {
+ if (bit_width > 64)
+ return false; // Scalar can't hold large integer arguments
+
+ if (current_argument_register < 6) {
+ scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ argument_register_ids[current_argument_register], 0);
+ current_argument_register++;
+ if (is_signed)
+ scalar.SignExtend(bit_width);
+ } else {
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (thread.GetProcess()->ReadScalarIntegerFromMemory(
+ current_stack_argument, byte_size, is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool ABISysV_x86_64::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 8; // jump over return address
+
+ uint32_t argument_register_ids[6];
+
+ argument_register_ids[0] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[1] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[2] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[3] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[4] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG5)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[5] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG6)
+ ->kinds[eRegisterKindLLDB];
+
+ unsigned int current_argument_register = 0;
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ CompilerType compiler_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ bool is_signed;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ } else if (compiler_type.IsPointerType()) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ }
+ }
+
+ return true;
+}
+
+Status ABISysV_x86_64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("rax", 0);
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(reg_info, raw_value))
+ set_it_simple = true;
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else {
+ llvm::Optional<uint64_t> bit_width =
+ compiler_type.GetBitSize(frame_sp.get());
+ if (!bit_width) {
+ error.SetErrorString("can't get type size");
+ return error;
+ }
+ if (*bit_width <= 64) {
+ const RegisterInfo *xmm0_info =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ RegisterValue xmm0_value;
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ unsigned char buffer[16];
+ ByteOrder byte_order = data.GetByteOrder();
+
+ data.CopyByteOrderedData(0, num_bytes, buffer, 16, byte_order);
+ xmm0_value.SetBytes(buffer, 16, byte_order);
+ reg_ctx->WriteRegister(xmm0_info, xmm0_value);
+ set_it_simple = true;
+ } else {
+ // FIXME - don't know how to do 80 bit long doubles yet.
+ error.SetErrorString(
+ "We don't support returning float values > 64 bits at present");
+ }
+ }
+ }
+
+ if (!set_it_simple) {
+ // Okay we've got a structure or something that doesn't fit in a simple
+ // register. We should figure out where it really goes, but we don't
+ // support this yet.
+ error.SetErrorString("We only support setting simple integer and float "
+ "return types at present.");
+ }
+
+ return error;
+}
+
+ValueObjectSP ABISysV_x86_64::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType, return_value_type);
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo();
+ if (type_flags & eTypeIsScalar) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger) {
+ // Extract the register context so we can read arguments from registers
+
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("rax", 0), 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size <= sizeof(long double)) {
+ const RegisterInfo *xmm0_info =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ RegisterValue xmm0_value;
+ if (reg_ctx->ReadRegister(xmm0_info, xmm0_value)) {
+ DataExtractor data;
+ if (xmm0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = (float)data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = (double)data.GetDouble(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(long double)) {
+ // Don't handle long double since that can be encoded as 80 bit
+ // floats...
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsPointer) {
+ unsigned rax_id =
+ reg_ctx->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB];
+ value.GetScalar() =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id,
+ 0);
+ value.SetValueType(Value::eValueTypeScalar);
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsVector) {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size > 0) {
+ const RegisterInfo *altivec_reg =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ if (altivec_reg == nullptr)
+ altivec_reg = reg_ctx->GetRegisterInfoByName("mm0", 0);
+
+ if (altivec_reg) {
+ if (*byte_size <= altivec_reg->byte_size) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(altivec_reg, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(
+ altivec_reg, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(), byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ } else if (*byte_size <= altivec_reg->byte_size * 2) {
+ const RegisterInfo *altivec_reg2 =
+ reg_ctx->GetRegisterInfoByName("xmm1", 0);
+ if (altivec_reg2) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ RegisterValue reg_value2;
+ if (reg_ctx->ReadRegister(altivec_reg, reg_value) &&
+ reg_ctx->ReadRegister(altivec_reg2, reg_value2)) {
+
+ Status error;
+ if (reg_value.GetAsMemoryData(
+ altivec_reg, heap_data_up->GetBytes(),
+ altivec_reg->byte_size, byte_order, error) &&
+ reg_value2.GetAsMemoryData(
+ altivec_reg2,
+ heap_data_up->GetBytes() + altivec_reg->byte_size,
+ heap_data_up->GetByteSize() - altivec_reg->byte_size,
+ byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return return_valobj_sp;
+}
+
+// The compiler will flatten the nested aggregate type into single
+// layer and push the value to stack
+// This helper function will flatten an aggregate type
+// and return true if it can be returned in register(s) by value
+// return false if the aggregate is in memory
+static bool FlattenAggregateType(
+ Thread &thread, ExecutionContext &exe_ctx,
+ CompilerType &return_compiler_type,
+ uint32_t data_byte_offset,
+ std::vector<uint32_t> &aggregate_field_offsets,
+ std::vector<CompilerType> &aggregate_compiler_types) {
+
+ const uint32_t num_children = return_compiler_type.GetNumFields();
+ for (uint32_t idx = 0; idx < num_children; ++idx) {
+ std::string name;
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ uint64_t field_bit_offset = 0;
+ CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex(
+ idx, name, &field_bit_offset, nullptr, nullptr);
+ llvm::Optional<uint64_t> field_bit_width =
+ field_compiler_type.GetBitSize(&thread);
+
+ // if we don't know the size of the field (e.g. invalid type), exit
+ if (!field_bit_width || *field_bit_width == 0) {
+ return false;
+ }
+
+ uint32_t field_byte_offset = field_bit_offset / 8 + data_byte_offset;
+
+ const uint32_t field_type_flags = field_compiler_type.GetTypeInfo();
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType() ||
+ field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ aggregate_field_offsets.push_back(field_byte_offset);
+ aggregate_compiler_types.push_back(field_compiler_type);
+ } else if (field_type_flags & eTypeHasChildren) {
+ if (!FlattenAggregateType(thread, exe_ctx, field_compiler_type,
+ field_byte_offset, aggregate_field_offsets,
+ aggregate_compiler_types)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type);
+ if (return_valobj_sp)
+ return return_valobj_sp;
+
+ RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
+ if (!reg_ctx_sp)
+ return return_valobj_sp;
+
+ llvm::Optional<uint64_t> bit_width = return_compiler_type.GetBitSize(&thread);
+ if (!bit_width)
+ return return_valobj_sp;
+ if (return_compiler_type.IsAggregateType()) {
+ Target *target = exe_ctx.GetTargetPtr();
+ bool is_memory = true;
+ std::vector<uint32_t> aggregate_field_offsets;
+ std::vector<CompilerType> aggregate_compiler_types;
+ if (return_compiler_type.GetTypeSystem()->CanPassInRegisters(
+ return_compiler_type) &&
+ *bit_width <= 128 &&
+ FlattenAggregateType(thread, exe_ctx, return_compiler_type,
+ 0, aggregate_field_offsets,
+ aggregate_compiler_types)) {
+ ByteOrder byte_order = target->GetArchitecture().GetByteOrder();
+ DataBufferSP data_sp(new DataBufferHeap(16, 0));
+ DataExtractor return_ext(data_sp, byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ const RegisterInfo *rax_info =
+ reg_ctx_sp->GetRegisterInfoByName("rax", 0);
+ const RegisterInfo *rdx_info =
+ reg_ctx_sp->GetRegisterInfoByName("rdx", 0);
+ const RegisterInfo *xmm0_info =
+ reg_ctx_sp->GetRegisterInfoByName("xmm0", 0);
+ const RegisterInfo *xmm1_info =
+ reg_ctx_sp->GetRegisterInfoByName("xmm1", 0);
+
+ RegisterValue rax_value, rdx_value, xmm0_value, xmm1_value;
+ reg_ctx_sp->ReadRegister(rax_info, rax_value);
+ reg_ctx_sp->ReadRegister(rdx_info, rdx_value);
+ reg_ctx_sp->ReadRegister(xmm0_info, xmm0_value);
+ reg_ctx_sp->ReadRegister(xmm1_info, xmm1_value);
+
+ DataExtractor rax_data, rdx_data, xmm0_data, xmm1_data;
+
+ rax_value.GetData(rax_data);
+ rdx_value.GetData(rdx_data);
+ xmm0_value.GetData(xmm0_data);
+ xmm1_value.GetData(xmm1_data);
+
+ uint32_t fp_bytes =
+ 0; // Tracks how much of the xmm registers we've consumed so far
+ uint32_t integer_bytes =
+ 0; // Tracks how much of the rax/rds registers we've consumed so far
+
+ // in case of the returned type is a subclass of non-abstract-base class
+ // it will have a padding to skip the base content
+ if (aggregate_field_offsets.size()) {
+ fp_bytes = aggregate_field_offsets[0];
+ integer_bytes = aggregate_field_offsets[0];
+ }
+
+ const uint32_t num_children = aggregate_compiler_types.size();
+
+ // Since we are in the small struct regime, assume we are not in memory.
+ is_memory = false;
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ CompilerType field_compiler_type = aggregate_compiler_types[idx];
+ uint32_t field_byte_width = (uint32_t) (*field_compiler_type.GetByteSize(&thread));
+ uint32_t field_byte_offset = aggregate_field_offsets[idx];
+
+ uint32_t field_bit_width = field_byte_width * 8;
+
+ DataExtractor *copy_from_extractor = nullptr;
+ uint32_t copy_from_offset = 0;
+
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType()) {
+ if (integer_bytes < 8) {
+ if (integer_bytes + field_byte_width <= 8) {
+ // This is in RAX, copy from register to our result structure:
+ copy_from_extractor = &rax_data;
+ copy_from_offset = integer_bytes;
+ integer_bytes += field_byte_width;
+ } else {
+ // The next field wouldn't fit in the remaining space, so we
+ // pushed it to rdx.
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = 0;
+ integer_bytes = 8 + field_byte_width;
+ }
+ } else if (integer_bytes + field_byte_width <= 16) {
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = integer_bytes - 8;
+ integer_bytes += field_byte_width;
+ } else {
+ // The last field didn't fit. I can't see how that would happen
+ // w/o the overall size being greater than 16 bytes. For now,
+ // return a nullptr return value object.
+ return return_valobj_sp;
+ }
+ } else if (field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ // Structs with long doubles are always passed in memory.
+ if (field_bit_width == 128) {
+ is_memory = true;
+ break;
+ } else if (field_bit_width == 64) {
+ // These have to be in a single xmm register.
+ if (fp_bytes == 0)
+ copy_from_extractor = &xmm0_data;
+ else
+ copy_from_extractor = &xmm1_data;
+
+ copy_from_offset = 0;
+ fp_bytes += field_byte_width;
+ } else if (field_bit_width == 32) {
+ // This one is kind of complicated. If we are in an "eightbyte"
+ // with another float, we'll be stuffed into an xmm register with
+ // it. If we are in an "eightbyte" with one or more ints, then we
+ // will be stuffed into the appropriate GPR with them.
+ bool in_gpr;
+ if (field_byte_offset % 8 == 0) {
+ // We are at the beginning of one of the eightbytes, so check the
+ // next element (if any)
+ if (idx == num_children - 1) {
+ in_gpr = false;
+ } else {
+ CompilerType next_field_compiler_type =
+ aggregate_compiler_types[idx + 1];
+ if (next_field_compiler_type.IsIntegerOrEnumerationType(
+ is_signed)) {
+ in_gpr = true;
+ } else {
+ copy_from_offset = 0;
+ in_gpr = false;
+ }
+ }
+ } else if (field_byte_offset % 4 == 0) {
+ // We are inside of an eightbyte, so see if the field before us
+ // is floating point: This could happen if somebody put padding
+ // in the structure.
+ if (idx == 0) {
+ in_gpr = false;
+ } else {
+ CompilerType prev_field_compiler_type =
+ aggregate_compiler_types[idx - 1];
+ if (prev_field_compiler_type.IsIntegerOrEnumerationType(
+ is_signed)) {
+ in_gpr = true;
+ } else {
+ copy_from_offset = 4;
+ in_gpr = false;
+ }
+ }
+ } else {
+ is_memory = true;
+ continue;
+ }
+
+ // Okay, we've figured out whether we are in GPR or XMM, now figure
+ // out which one.
+ if (in_gpr) {
+ if (integer_bytes < 8) {
+ // This is in RAX, copy from register to our result structure:
+ copy_from_extractor = &rax_data;
+ copy_from_offset = integer_bytes;
+ integer_bytes += field_byte_width;
+ } else {
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = integer_bytes - 8;
+ integer_bytes += field_byte_width;
+ }
+ } else {
+ if (fp_bytes < 8)
+ copy_from_extractor = &xmm0_data;
+ else
+ copy_from_extractor = &xmm1_data;
+
+ fp_bytes += field_byte_width;
+ }
+ }
+ }
+ // These two tests are just sanity checks. If I somehow get the type
+ // calculation wrong above it is better to just return nothing than to
+ // assert or crash.
+ if (!copy_from_extractor)
+ return return_valobj_sp;
+ if (copy_from_offset + field_byte_width >
+ copy_from_extractor->GetByteSize())
+ return return_valobj_sp;
+ copy_from_extractor->CopyByteOrderedData(
+ copy_from_offset, field_byte_width,
+ data_sp->GetBytes() + field_byte_offset, field_byte_width,
+ byte_order);
+ }
+ if (!is_memory) {
+ // The result is in our data buffer. Let's make a variable object out
+ // of it:
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+ }
+ }
+
+ // FIXME: This is just taking a guess, rax may very well no longer hold the
+ // return storage location.
+ // If we are going to do this right, when we make a new frame we should
+ // check to see if it uses a memory return, and if we are at the first
+ // instruction and if so stash away the return location. Then we would
+ // only return the memory return value if we know it is valid.
+
+ if (is_memory) {
+ unsigned rax_id =
+ reg_ctx_sp->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB];
+ lldb::addr_t storage_addr =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id,
+ 0);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+ }
+
+ return return_valobj_sp;
+}
+
+// This defines the CFA as rsp+8
+// the saved pc is at CFA-8 (i.e. rsp+0)
+// The saved rsp is CFA+0
+
+bool ABISysV_x86_64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num = dwarf_rsp;
+ uint32_t pc_reg_num = dwarf_rip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 8);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -8, false);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("x86_64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+// This defines the CFA as rbp+16
+// The saved pc is at CFA-8 (i.e. rbp+8)
+// The saved rbp is at CFA-16 (i.e. rbp+0)
+// The saved rsp is CFA+0
+
+bool ABISysV_x86_64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = dwarf_rbp;
+ uint32_t sp_reg_num = dwarf_rsp;
+ uint32_t pc_reg_num = dwarf_rip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ const int32_t ptr_size = 8;
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_rbp, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("x86_64 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+bool ABISysV_x86_64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+// See "Register Usage" in the
+// "System V Application Binary Interface"
+// "AMD64 Architecture Processor Supplement" (or "x86-64(tm) Architecture
+// Processor Supplement" in earlier revisions) (this doc is also commonly
+// referred to as the x86-64/AMD64 psABI) Edited by Michael Matz, Jan Hubicka,
+// Andreas Jaeger, and Mark Mitchell current version is 0.99.6 released
+// 2012-07-02 at http://refspecs.linuxfoundation.org/elf/x86-64-abi-0.99.pdf
+// It's being revised & updated at https://github.com/hjl-tools/x86-psABI/
+
+bool ABISysV_x86_64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (!reg_info)
+ return false;
+ assert(reg_info->name != nullptr && "unnamed register?");
+ 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)
+ .Default(false);
+ return IsCalleeSaved;
+}
+
+void ABISysV_x86_64::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for x86_64 targets", CreateInstance);
+}
+
+void ABISysV_x86_64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_x86_64::GetPluginNameStatic() {
+ static ConstString g_name("sysv-x86_64");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_x86_64::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_x86_64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h
new file mode 100644
index 000000000000..f6704aff348c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h
@@ -0,0 +1,106 @@
+//===-- ABISysV_x86_64.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABISysV_x86_64_h_
+#define liblldb_ABISysV_x86_64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_x86_64 : public lldb_private::ABI {
+public:
+ ~ABISysV_x86_64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The SysV x86_64 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // We have a 64 bit address space, so anything is valid as opcodes
+ // aren't fixed width...
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ bool GetPointerReturnRegister(const char *&name) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_x86_64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_x86_64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.cpp
new file mode 100644
index 000000000000..5dc7717d865d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.cpp
@@ -0,0 +1,1805 @@
+//===-- ABIWindows_x86_64.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABIWindows_x86_64.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ dwarf_rax = 0,
+ dwarf_rdx,
+ dwarf_rcx,
+ dwarf_rbx,
+ dwarf_rsi,
+ dwarf_rdi,
+ dwarf_rbp,
+ dwarf_rsp,
+ dwarf_r8,
+ dwarf_r9,
+ dwarf_r10,
+ dwarf_r11,
+ dwarf_r12,
+ dwarf_r13,
+ dwarf_r14,
+ dwarf_r15,
+ dwarf_rip,
+ dwarf_xmm0,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7,
+ dwarf_xmm8,
+ dwarf_xmm9,
+ dwarf_xmm10,
+ dwarf_xmm11,
+ dwarf_xmm12,
+ dwarf_xmm13,
+ dwarf_xmm14,
+ dwarf_xmm15,
+ dwarf_stmm0,
+ dwarf_stmm1,
+ dwarf_stmm2,
+ dwarf_stmm3,
+ dwarf_stmm4,
+ dwarf_stmm5,
+ dwarf_stmm6,
+ dwarf_stmm7,
+ dwarf_ymm0,
+ dwarf_ymm1,
+ dwarf_ymm2,
+ dwarf_ymm3,
+ dwarf_ymm4,
+ dwarf_ymm5,
+ dwarf_ymm6,
+ dwarf_ymm7,
+ dwarf_ymm8,
+ dwarf_ymm9,
+ dwarf_ymm10,
+ dwarf_ymm11,
+ dwarf_ymm12,
+ dwarf_ymm13,
+ dwarf_ymm14,
+ dwarf_ymm15,
+ dwarf_bnd0 = 126,
+ dwarf_bnd1,
+ dwarf_bnd2,
+ dwarf_bnd3
+};
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME
+ // DWARF GENERIC PROCESS PLUGIN
+ // LLDB NATIVE
+ // ======== ======= == === ============= ===================
+ // ======================= =====================
+ // =========================== ===================== ======================
+ {"rax",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rax, dwarf_rax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rbx",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rbx, dwarf_rbx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rcx",
+ "arg1",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rcx, dwarf_rcx, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rdx",
+ "arg2",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rdx, dwarf_rdx, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rsi",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rsi, dwarf_rsi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rdi",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rdi, dwarf_rdi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rbp",
+ "fp",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rbp, dwarf_rbp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rsp",
+ "sp",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rsp, dwarf_rsp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ "arg3",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r8, dwarf_r8, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ "arg4",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r9, dwarf_r9, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rip",
+ "pc",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rip, dwarf_rip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rflags",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ss",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ds",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"es",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"gs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm0",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm0, dwarf_stmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm1",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm1, dwarf_stmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm2",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm2, dwarf_stmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm3",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm3, dwarf_stmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm4",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm4, dwarf_stmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm5",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm5, dwarf_stmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm6",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm6, dwarf_stmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm7",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm7, dwarf_stmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fctrl",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fstat",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ftag",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fiseg",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fioff",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"foseg",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fooff",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fop",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm0",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm0, dwarf_xmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm1",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm1, dwarf_xmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm2",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm2, dwarf_xmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm3",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm3, dwarf_xmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm4",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm4, dwarf_xmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm5",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm5, dwarf_xmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm6",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm6, dwarf_xmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm7",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm7, dwarf_xmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm8",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm8, dwarf_xmm8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm9",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm9, dwarf_xmm9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm10",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm10, dwarf_xmm10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm11",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm11, dwarf_xmm11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm12",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm12, dwarf_xmm12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm13",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm13, dwarf_xmm13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm14",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm14, dwarf_xmm14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm15",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm15, dwarf_xmm15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"mxcsr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm0",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm0, dwarf_ymm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm1",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm1, dwarf_ymm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm2",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm2, dwarf_ymm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm3",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm3, dwarf_ymm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm4",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm4, dwarf_ymm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm5",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm5, dwarf_ymm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm6",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm6, dwarf_ymm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm7",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm7, dwarf_ymm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm8",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm8, dwarf_ymm8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm9",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm9, dwarf_ymm9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm10",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm10, dwarf_ymm10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm11",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm11, dwarf_ymm11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm12",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm12, dwarf_ymm12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm13",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm13, dwarf_ymm13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm14",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm14, dwarf_ymm14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm15",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm15, dwarf_ymm15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bnd0",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt64,
+ {dwarf_bnd0, dwarf_bnd0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bnd1",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt64,
+ {dwarf_bnd1, dwarf_bnd1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bnd2",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt64,
+ {dwarf_bnd2, dwarf_bnd2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bnd3",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt64,
+ {dwarf_bnd3, dwarf_bnd3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bndcfgu",
+ nullptr,
+ 8,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bndstatus",
+ nullptr,
+ 8,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABIWindows_x86_64::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+bool ABIWindows_x86_64::GetPointerReturnRegister(const char *&name) {
+ name = "rax";
+ return true;
+}
+
+size_t ABIWindows_x86_64::GetRedZoneSize() const { return 0; }
+
+//------------------------------------------------------------------
+// Static Functions
+//------------------------------------------------------------------
+
+ABISP
+ABIWindows_x86_64::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::x86_64 &&
+ arch.GetTriple().isOSWindows()) {
+ return ABISP(new ABIWindows_x86_64(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABIWindows_x86_64::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABIWindows_x86_64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1),
+ args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ if (args.size() > 4) // Windows x64 only put first 4 arguments into registers
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s",
+ static_cast<uint64_t>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // First, align the SP
+
+ if (log)
+ log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)(sp & ~0xfull));
+
+ sp &= ~(0xfull); // 16-byte alignment
+
+ sp -= 8; // return address
+
+ Status error;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ ProcessSP process_sp(thread.GetProcess());
+
+ RegisterValue reg_value;
+ if (log)
+ log->Printf("Pushing the return address onto the stack: 0x%" PRIx64
+ ": 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)return_addr);
+
+ // Save return address onto the stack
+ if (!process_sp->WritePointerToMemory(sp, return_addr, error))
+ return false;
+
+ // %rsp is set to the actual stack value.
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ // %rip is set to the address of the called function.
+
+ if (log)
+ log->Printf("Writing IP: 0x%" PRIx64, (uint64_t)func_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Thread &thread,
+ uint32_t *argument_register_ids,
+ unsigned int &current_argument_register,
+ addr_t &current_stack_argument) {
+ if (bit_width > 64)
+ return false; // Scalar can't hold large integer arguments
+
+ if (current_argument_register < 4) { // Windows pass first 4 arguments to register
+ scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ argument_register_ids[current_argument_register], 0);
+ current_argument_register++;
+ if (is_signed)
+ scalar.SignExtend(bit_width);
+ return true;
+ }
+ uint32_t byte_size = (bit_width + (CHAR_BIT - 1)) / CHAR_BIT;
+ Status error;
+ if (thread.GetProcess()->ReadScalarIntegerFromMemory(
+ current_stack_argument, byte_size, is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+}
+
+bool ABIWindows_x86_64::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 8; // jump over return address
+
+ uint32_t argument_register_ids[4];
+
+ argument_register_ids[0] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[1] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[2] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[3] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4)
+ ->kinds[eRegisterKindLLDB];
+
+ unsigned int current_argument_register = 0;
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ CompilerType compiler_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ bool is_signed;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ } else if (compiler_type.IsPointerType()) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ }
+ }
+
+ return true;
+}
+
+Status ABIWindows_x86_64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("rax", 0);
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(reg_info, raw_value))
+ set_it_simple = true;
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else {
+ llvm::Optional<uint64_t> bit_width =
+ compiler_type.GetBitSize(frame_sp.get());
+ if (!bit_width) {
+ error.SetErrorString("can't get type size");
+ return error;
+ }
+ if (*bit_width <= 64) {
+ const RegisterInfo *xmm0_info =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ RegisterValue xmm0_value;
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ unsigned char buffer[16];
+ ByteOrder byte_order = data.GetByteOrder();
+
+ data.CopyByteOrderedData(0, num_bytes, buffer, 16, byte_order);
+ xmm0_value.SetBytes(buffer, 16, byte_order);
+ reg_ctx->WriteRegister(xmm0_info, xmm0_value);
+ set_it_simple = true;
+ } else {
+ // Windows doesn't support 80 bit FP
+ error.SetErrorString(
+ "Windows-x86_64 doesn't allow FP larger than 64 bits.");
+ }
+ }
+ }
+
+ if (!set_it_simple) {
+ // Okay we've got a structure or something that doesn't fit in a simple
+ // register.
+ // TODO(wanyi): On Windows, if the return type is a struct:
+ // 1) smaller that 64 bits and return by value -> RAX
+ // 2) bigger than 64 bits, the caller will allocate memory for that struct
+ // and pass the struct pointer in RCX then return the pointer in RAX
+ error.SetErrorString("We only support setting simple integer and float "
+ "return types at present.");
+ }
+
+ return error;
+}
+
+ValueObjectSP ABIWindows_x86_64::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo();
+ if (type_flags & eTypeIsScalar) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger) {
+ // Extract the register context so we can read arguments from registers
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("rax", 0), 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size <= sizeof(long double)) {
+ const RegisterInfo *xmm0_info =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ RegisterValue xmm0_value;
+ if (reg_ctx->ReadRegister(xmm0_info, xmm0_value)) {
+ DataExtractor data;
+ if (xmm0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = (float)data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ // double and long double are the same on windows
+ value.GetScalar() = (double)data.GetDouble(&offset);
+ success = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if ((type_flags & eTypeIsPointer) ||
+ (type_flags & eTypeInstanceIsPointer)) {
+ unsigned rax_id =
+ reg_ctx->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB];
+ value.GetScalar() =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id,
+ 0);
+ value.SetValueType(Value::eValueTypeScalar);
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsVector) {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size > 0) {
+ const RegisterInfo *xmm_reg =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ if (xmm_reg == nullptr)
+ xmm_reg = reg_ctx->GetRegisterInfoByName("mm0", 0);
+
+ if (xmm_reg) {
+ if (*byte_size <= xmm_reg->byte_size) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(xmm_reg, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(
+ xmm_reg, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(), byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return return_valobj_sp;
+}
+
+// The compiler will flatten the nested aggregate type into single
+// layer and push the value to stack
+// This helper function will flatten an aggregate type
+// and return true if it can be returned in register(s) by value
+// return false if the aggregate is in memory
+static bool FlattenAggregateType(
+ Thread &thread, ExecutionContext &exe_ctx,
+ CompilerType &return_compiler_type,
+ uint32_t data_byte_offset,
+ std::vector<uint32_t> &aggregate_field_offsets,
+ std::vector<CompilerType> &aggregate_compiler_types) {
+
+ const uint32_t num_children = return_compiler_type.GetNumFields();
+ for (uint32_t idx = 0; idx < num_children; ++idx) {
+ std::string name;
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ uint64_t field_bit_offset = 0;
+ CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex(
+ idx, name, &field_bit_offset, nullptr, nullptr);
+ llvm::Optional<uint64_t> field_bit_width =
+ field_compiler_type.GetBitSize(&thread);
+
+ // if we don't know the size of the field (e.g. invalid type), exit
+ if (!field_bit_width || *field_bit_width == 0) {
+ return false;
+ }
+ // If there are any unaligned fields, this is stored in memory.
+ if (field_bit_offset % *field_bit_width != 0) {
+ return false;
+ }
+
+ // add overall offset
+ uint32_t field_byte_offset = field_bit_offset / 8 + data_byte_offset;
+
+ const uint32_t field_type_flags = field_compiler_type.GetTypeInfo();
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType() ||
+ field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ aggregate_field_offsets.push_back(field_byte_offset);
+ aggregate_compiler_types.push_back(field_compiler_type);
+ } else if (field_type_flags & eTypeHasChildren) {
+ if (!FlattenAggregateType(thread, exe_ctx, field_compiler_type,
+ field_byte_offset, aggregate_field_offsets,
+ aggregate_compiler_types)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+ValueObjectSP ABIWindows_x86_64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+
+ if (!return_compiler_type) {
+ return return_valobj_sp;
+ }
+
+ // try extract value as if it's a simple type
+ return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type);
+ if (return_valobj_sp) {
+ return return_valobj_sp;
+ }
+
+ RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
+ if (!reg_ctx_sp) {
+ return return_valobj_sp;
+ }
+
+ llvm::Optional<uint64_t> bit_width = return_compiler_type.GetBitSize(&thread);
+ if (!bit_width) {
+ return return_valobj_sp;
+ }
+
+ // if it's not simple or aggregate type, then we don't know how to handle it
+ if (!return_compiler_type.IsAggregateType()) {
+ return return_valobj_sp;
+ }
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ uint32_t max_register_value_bit_width = 64;
+
+ // The scenario here is to have a struct/class which is POD
+ // if the return struct/class size is larger than 64 bits,
+ // the caller will allocate memory for it and pass the return addr in RCX
+ // then return the address in RAX
+
+ // if the struct is returned by value in register (RAX)
+ // its size has to be: 1, 2, 4, 8, 16, 32, or 64 bits (aligned)
+ // for floating point, the return value will be copied over to RAX
+ bool is_memory = *bit_width > max_register_value_bit_width ||
+ *bit_width & (*bit_width - 1);
+ std::vector<uint32_t> aggregate_field_offsets;
+ std::vector<CompilerType> aggregate_compiler_types;
+ if (!is_memory &&
+ FlattenAggregateType(thread, exe_ctx, return_compiler_type,
+ 0, aggregate_field_offsets,
+ aggregate_compiler_types)) {
+ ByteOrder byte_order = target->GetArchitecture().GetByteOrder();
+ DataBufferSP data_sp(
+ new DataBufferHeap(max_register_value_bit_width / 8, 0));
+ DataExtractor return_ext(data_sp, byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ // The only register used to return struct/class by value
+ const RegisterInfo *rax_info =
+ reg_ctx_sp->GetRegisterInfoByName("rax", 0);
+ RegisterValue rax_value;
+ reg_ctx_sp->ReadRegister(rax_info, rax_value);
+ DataExtractor rax_data;
+ rax_value.GetData(rax_data);
+
+ uint32_t used_bytes =
+ 0; // Tracks how much of the rax registers we've consumed so far
+
+ // in case of the returned type is a subclass of non-abstract-base class
+ // it will have a padding to skip the base content
+ if (aggregate_field_offsets.size())
+ used_bytes = aggregate_field_offsets[0];
+
+ const uint32_t num_children = aggregate_compiler_types.size();
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ bool is_signed;
+ bool is_complex;
+ uint32_t count;
+
+ CompilerType field_compiler_type = aggregate_compiler_types[idx];
+ uint32_t field_byte_width = (uint32_t) (*field_compiler_type.GetByteSize(&thread));
+ uint32_t field_byte_offset = aggregate_field_offsets[idx];
+
+ // this is unlikely w/o the overall size being greater than 8 bytes
+ // For now, return a nullptr return value object.
+ if (used_bytes >= 8 || used_bytes + field_byte_width > 8) {
+ return return_valobj_sp;
+ }
+
+ DataExtractor *copy_from_extractor = nullptr;
+ uint32_t copy_from_offset = 0;
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType() ||
+ field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ copy_from_extractor = &rax_data;
+ copy_from_offset = used_bytes;
+ used_bytes += field_byte_width;
+ }
+ // These two tests are just sanity checks. If I somehow get the type
+ // calculation wrong above it is better to just return nothing than to
+ // assert or crash.
+ if (!copy_from_extractor) {
+ return return_valobj_sp;
+ }
+ if (copy_from_offset + field_byte_width >
+ copy_from_extractor->GetByteSize()) {
+ return return_valobj_sp;
+ }
+ copy_from_extractor->CopyByteOrderedData(copy_from_offset,
+ field_byte_width, data_sp->GetBytes() + field_byte_offset,
+ field_byte_width, byte_order);
+ }
+ if (!is_memory) {
+ // The result is in our data buffer. Let's make a variable object out
+ // of it:
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+ }
+ }
+
+ // The Windows x86_64 ABI specifies that the return address for MEMORY
+ // objects be placed in rax on exit from the function.
+
+ // FIXME: This is just taking a guess, rax may very well no longer hold the
+ // return storage location.
+ // If we are going to do this right, when we make a new frame we should
+ // check to see if it uses a memory return, and if we are at the first
+ // instruction and if so stash away the return location. Then we would
+ // only return the memory return value if we know it is valid.
+ if (is_memory) {
+ unsigned rax_id =
+ reg_ctx_sp->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB];
+ lldb::addr_t storage_addr =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id,
+ 0);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+ return return_valobj_sp;
+}
+
+// This defines the CFA as rsp+8
+// the saved pc is at CFA-8 (i.e. rsp+0)
+// The saved rsp is CFA+0
+
+bool ABIWindows_x86_64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num = dwarf_rsp;
+ uint32_t pc_reg_num = dwarf_rip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 8);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -8, false);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("x86_64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+// Windows-x86_64 doesn't use %rbp
+// No available Unwind information for Windows-x86_64 (section .pdata)
+// Let's use SysV-x86_64 one for now
+bool ABIWindows_x86_64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = dwarf_rbp;
+ uint32_t sp_reg_num = dwarf_rsp;
+ uint32_t pc_reg_num = dwarf_rip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ const int32_t ptr_size = 8;
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_rbp, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("x86_64 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABIWindows_x86_64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+bool ABIWindows_x86_64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (!reg_info)
+ return false;
+ assert(reg_info->name != nullptr && "unnamed register?");
+ 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)
+ .Default(false);
+ return IsCalleeSaved;
+}
+
+void ABIWindows_x86_64::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "Windows ABI for x86_64 targets", CreateInstance);
+}
+
+void ABIWindows_x86_64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABIWindows_x86_64::GetPluginNameStatic() {
+ static ConstString g_name("windows-x86_64");
+ return g_name;
+}
+
+//------------------------------------------------------------------
+// PluginInterface protocol
+//------------------------------------------------------------------
+
+lldb_private::ConstString ABIWindows_x86_64::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABIWindows_x86_64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.h
new file mode 100644
index 000000000000..9f6b2ceef299
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.h
@@ -0,0 +1,99 @@
+//===-- ABIWindows_x86_64.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABIWindows_x86_64_h_
+#define liblldb_ABIWindows_x86_64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABIWindows_x86_64 : public lldb_private::ABI {
+public:
+ ~ABIWindows_x86_64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // In Windows_x86_64 ABI, stack will always be maintained 16-byte aligned
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ if (cfa & (16ull - 1ull))
+ return false; // Not 16 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // We have a 64 bit address space, so anything is valid as opcodes
+ // aren't fixed width...
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ bool GetPointerReturnRegister(const char *&name) override;
+
+ //------------------------------------------------------------------
+ // Static Functions
+ //------------------------------------------------------------------
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ //------------------------------------------------------------------
+ // PluginInterface protocol
+ //------------------------------------------------------------------
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABIWindows_x86_64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_x86_64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp b/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp
new file mode 100644
index 000000000000..5b86df6c5273
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp
@@ -0,0 +1,157 @@
+//===-- ArchitectureArm.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/Architecture/Arm/ArchitectureArm.h"
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "Plugins/Process/Utility/InstructionUtils.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+ConstString ArchitectureArm::GetPluginNameStatic() {
+ return ConstString("arm");
+}
+
+void ArchitectureArm::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "Arm-specific algorithms",
+ &ArchitectureArm::Create);
+}
+
+void ArchitectureArm::Terminate() {
+ PluginManager::UnregisterPlugin(&ArchitectureArm::Create);
+}
+
+std::unique_ptr<Architecture> ArchitectureArm::Create(const ArchSpec &arch) {
+ if (arch.GetMachine() != llvm::Triple::arm)
+ return nullptr;
+ return std::unique_ptr<Architecture>(new ArchitectureArm());
+}
+
+ConstString ArchitectureArm::GetPluginName() { return GetPluginNameStatic(); }
+uint32_t ArchitectureArm::GetPluginVersion() { return 1; }
+
+void ArchitectureArm::OverrideStopInfo(Thread &thread) const {
+ // We need to check if we are stopped in Thumb mode in a IT instruction and
+ // detect if the condition doesn't pass. If this is the case it means we
+ // won't actually execute this instruction. If this happens we need to clear
+ // the stop reason to no thread plans think we are stopped for a reason and
+ // the plans should keep going.
+ //
+ // We do this because when single stepping many ARM processes, debuggers
+ // often use the BVR/BCR registers that says "stop when the PC is not equal
+ // to its current value". This method of stepping means we can end up
+ // stopping on instructions inside an if/then block that wouldn't get
+ // executed. By fixing this we can stop the debugger from seeming like you
+ // stepped through both the "if" _and_ the "else" clause when source level
+ // stepping because the debugger stops regardless due to the BVR/BCR
+ // triggering a stop.
+ //
+ // It also means we can set breakpoints on instructions inside an an if/then
+ // block and correctly skip them if we use the BKPT instruction. The ARM and
+ // Thumb BKPT instructions are unconditional even when executed in a Thumb IT
+ // block.
+ //
+ // If your debugger inserts software traps in ARM/Thumb code, it will need to
+ // use 16 and 32 bit instruction for 16 and 32 bit thumb instructions
+ // respectively. If your debugger inserts a 16 bit thumb trap on top of a 32
+ // bit thumb instruction for an opcode that is inside an if/then, it will
+ // change the it/then to conditionally execute your
+ // 16 bit trap and then cause your program to crash if it executes the
+ // trailing 16 bits (the second half of the 32 bit thumb instruction you
+ // partially overwrote).
+
+ RegisterContextSP reg_ctx_sp(thread.GetRegisterContext());
+ if (!reg_ctx_sp)
+ return;
+
+ const uint32_t cpsr = reg_ctx_sp->GetFlags(0);
+ if (cpsr == 0)
+ return;
+
+ // Read the J and T bits to get the ISETSTATE
+ const uint32_t J = Bit32(cpsr, 24);
+ const uint32_t T = Bit32(cpsr, 5);
+ const uint32_t ISETSTATE = J << 1 | T;
+ if (ISETSTATE == 0) {
+// NOTE: I am pretty sure we want to enable the code below
+// that detects when we stop on an instruction in ARM mode that is conditional
+// and the condition doesn't pass. This can happen if you set a breakpoint on
+// an instruction that is conditional. We currently will _always_ stop on the
+// instruction which is bad. You can also run into this while single stepping
+// and you could appear to run code in the "if" and in the "else" clause
+// because it would stop at all of the conditional instructions in both. In
+// such cases, we really don't want to stop at this location.
+// I will check with the lldb-dev list first before I enable this.
+#if 0
+ // ARM mode: check for condition on instruction
+ const addr_t pc = reg_ctx_sp->GetPC();
+ Status error;
+ // If we fail to read the opcode we will get UINT64_MAX as the result in
+ // "opcode" which we can use to detect if we read a valid opcode.
+ const uint64_t opcode = thread.GetProcess()->ReadUnsignedIntegerFromMemory(pc, 4, UINT64_MAX, error);
+ if (opcode <= UINT32_MAX)
+ {
+ const uint32_t condition = Bits32((uint32_t)opcode, 31, 28);
+ if (!ARMConditionPassed(condition, cpsr))
+ {
+ // We ARE stopped on an ARM instruction whose condition doesn't
+ // pass so this instruction won't get executed. Regardless of why
+ // it stopped, we need to clear the stop info
+ thread.SetStopInfo (StopInfoSP());
+ }
+ }
+#endif
+ } else if (ISETSTATE == 1) {
+ // Thumb mode
+ const uint32_t ITSTATE = Bits32(cpsr, 15, 10) << 2 | Bits32(cpsr, 26, 25);
+ if (ITSTATE != 0) {
+ const uint32_t condition = Bits32(ITSTATE, 7, 4);
+ if (!ARMConditionPassed(condition, cpsr)) {
+ // We ARE stopped in a Thumb IT instruction on an instruction whose
+ // condition doesn't pass so this instruction won't get executed.
+ // Regardless of why it stopped, we need to clear the stop info
+ thread.SetStopInfo(StopInfoSP());
+ }
+ }
+ }
+}
+
+addr_t ArchitectureArm::GetCallableLoadAddress(addr_t code_addr,
+ AddressClass addr_class) const {
+ bool is_alternate_isa = false;
+
+ switch (addr_class) {
+ case AddressClass::eData:
+ case AddressClass::eDebug:
+ return LLDB_INVALID_ADDRESS;
+ case AddressClass::eCodeAlternateISA:
+ is_alternate_isa = true;
+ break;
+ default: break;
+ }
+
+ if ((code_addr & 2u) || is_alternate_isa)
+ return code_addr | 1u;
+ return code_addr;
+}
+
+addr_t ArchitectureArm::GetOpcodeLoadAddress(addr_t opcode_addr,
+ AddressClass addr_class) const {
+ switch (addr_class) {
+ case AddressClass::eData:
+ case AddressClass::eDebug:
+ return LLDB_INVALID_ADDRESS;
+ default: break;
+ }
+ return opcode_addr & ~(1ull);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h b/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h
new file mode 100644
index 000000000000..03e79ce524a7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h
@@ -0,0 +1,40 @@
+//===-- ArchitectureArm.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGIN_ARCHITECTURE_ARM_H
+#define LLDB_PLUGIN_ARCHITECTURE_ARM_H
+
+#include "lldb/Core/Architecture.h"
+
+namespace lldb_private {
+
+class ArchitectureArm : public Architecture {
+public:
+ static ConstString GetPluginNameStatic();
+ static void Initialize();
+ static void Terminate();
+
+ ConstString GetPluginName() override;
+ uint32_t GetPluginVersion() override;
+
+ void OverrideStopInfo(Thread &thread) const override;
+
+ lldb::addr_t GetCallableLoadAddress(lldb::addr_t load_addr,
+ AddressClass addr_class) const override;
+
+ lldb::addr_t GetOpcodeLoadAddress(lldb::addr_t load_addr,
+ AddressClass addr_class) const override;
+
+private:
+ static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
+ ArchitectureArm() = default;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_PLUGIN_ARCHITECTURE_ARM_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.cpp b/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.cpp
new file mode 100644
index 000000000000..60f1a2eb7572
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.cpp
@@ -0,0 +1,240 @@
+//===-- ArchitectureMips.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/Architecture/Mips/ArchitectureMips.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+ConstString ArchitectureMips::GetPluginNameStatic() {
+ return ConstString("mips");
+}
+
+void ArchitectureMips::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "Mips-specific algorithms",
+ &ArchitectureMips::Create);
+}
+
+void ArchitectureMips::Terminate() {
+ PluginManager::UnregisterPlugin(&ArchitectureMips::Create);
+}
+
+std::unique_ptr<Architecture> ArchitectureMips::Create(const ArchSpec &arch) {
+ return arch.IsMIPS() ?
+ std::unique_ptr<Architecture>(new ArchitectureMips(arch)) : nullptr;
+}
+
+ConstString ArchitectureMips::GetPluginName() { return GetPluginNameStatic(); }
+uint32_t ArchitectureMips::GetPluginVersion() { return 1; }
+
+addr_t ArchitectureMips::GetCallableLoadAddress(addr_t code_addr,
+ AddressClass addr_class) const {
+ bool is_alternate_isa = false;
+
+ switch (addr_class) {
+ case AddressClass::eData:
+ case AddressClass::eDebug:
+ return LLDB_INVALID_ADDRESS;
+ case AddressClass::eCodeAlternateISA:
+ is_alternate_isa = true;
+ break;
+ default: break;
+ }
+
+ if ((code_addr & 2ull) || is_alternate_isa)
+ return code_addr | 1u;
+ return code_addr;
+}
+
+addr_t ArchitectureMips::GetOpcodeLoadAddress(addr_t opcode_addr,
+ AddressClass addr_class) const {
+ switch (addr_class) {
+ case AddressClass::eData:
+ case AddressClass::eDebug:
+ return LLDB_INVALID_ADDRESS;
+ default: break;
+ }
+ return opcode_addr & ~(1ull);
+}
+
+lldb::addr_t ArchitectureMips::GetBreakableLoadAddress(lldb::addr_t addr,
+ Target &target) const {
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+ Address resolved_addr;
+
+ SectionLoadList &section_load_list = target.GetSectionLoadList();
+ if (section_load_list.IsEmpty())
+ // No sections are loaded, so we must assume we are not running yet and
+ // need to operate only on file address.
+ target.ResolveFileAddress(addr, resolved_addr);
+ else
+ target.ResolveLoadAddress(addr, resolved_addr);
+
+ addr_t current_offset = 0;
+
+ // Get the function boundaries to make sure we don't scan back before the
+ // beginning of the current function.
+ ModuleSP temp_addr_module_sp(resolved_addr.GetModule());
+ if (temp_addr_module_sp) {
+ SymbolContext sc;
+ SymbolContextItem resolve_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ temp_addr_module_sp->ResolveSymbolContextForAddress(resolved_addr,
+ resolve_scope, sc);
+ Address sym_addr;
+ if (sc.function)
+ sym_addr = sc.function->GetAddressRange().GetBaseAddress();
+ else if (sc.symbol)
+ sym_addr = sc.symbol->GetAddress();
+
+ addr_t function_start = sym_addr.GetLoadAddress(&target);
+ if (function_start == LLDB_INVALID_ADDRESS)
+ function_start = sym_addr.GetFileAddress();
+
+ if (function_start)
+ current_offset = addr - function_start;
+ }
+
+ // If breakpoint address is start of function then we dont have to do
+ // anything.
+ if (current_offset == 0)
+ return addr;
+
+ ExecutionContext ctx;
+ target.CalculateExecutionContext(ctx);
+ auto insn = GetInstructionAtAddress(ctx, current_offset, addr);
+
+ if (nullptr == insn || !insn->HasDelaySlot())
+ return addr;
+
+ // Adjust the breakable address
+ uint64_t breakable_addr = addr - insn->GetOpcode().GetByteSize();
+ if (log)
+ log->Printf("Target::%s Breakpoint at 0x%8.8" PRIx64
+ " is adjusted to 0x%8.8" PRIx64 " due to delay slot\n",
+ __FUNCTION__, addr, breakable_addr);
+
+ return breakable_addr;
+}
+
+Instruction *ArchitectureMips::GetInstructionAtAddress(
+ const ExecutionContext &exe_ctx, const Address &resolved_addr,
+ addr_t symbol_offset) const {
+
+ auto loop_count = symbol_offset / 2;
+
+ uint32_t arch_flags = m_arch.GetFlags();
+ bool IsMips16 = arch_flags & ArchSpec::eMIPSAse_mips16;
+ bool IsMicromips = arch_flags & ArchSpec::eMIPSAse_micromips;
+
+ if (loop_count > 3) {
+ // Scan previous 6 bytes
+ if (IsMips16 | IsMicromips)
+ loop_count = 3;
+ // For mips-only, instructions are always 4 bytes, so scan previous 4
+ // bytes only.
+ else
+ loop_count = 2;
+ }
+
+ // Create Disassembler Instance
+ lldb::DisassemblerSP disasm_sp(
+ Disassembler::FindPlugin(m_arch, nullptr, nullptr));
+
+ InstructionList instruction_list;
+ InstructionSP prev_insn;
+ bool prefer_file_cache = true; // Read from file
+ uint32_t inst_to_choose = 0;
+
+ Address addr = resolved_addr;
+
+ for (uint32_t i = 1; i <= loop_count; i++) {
+ // Adjust the address to read from.
+ addr.Slide(-2);
+ AddressRange range(addr, i * 2);
+ uint32_t insn_size = 0;
+
+ disasm_sp->ParseInstructions(&exe_ctx, range, nullptr, prefer_file_cache);
+
+ uint32_t num_insns = disasm_sp->GetInstructionList().GetSize();
+ if (num_insns) {
+ prev_insn = disasm_sp->GetInstructionList().GetInstructionAtIndex(0);
+ insn_size = prev_insn->GetOpcode().GetByteSize();
+ if (i == 1 && insn_size == 2) {
+ // This looks like a valid 2-byte instruction (but it could be a part
+ // of upper 4 byte instruction).
+ instruction_list.Append(prev_insn);
+ inst_to_choose = 1;
+ }
+ else if (i == 2) {
+ // Here we may get one 4-byte instruction or two 2-byte instructions.
+ if (num_insns == 2) {
+ // Looks like there are two 2-byte instructions above our
+ // breakpoint target address. Now the upper 2-byte instruction is
+ // either a valid 2-byte instruction or could be a part of it's
+ // upper 4-byte instruction. In both cases we don't care because in
+ // this case lower 2-byte instruction is definitely a valid
+ // instruction and whatever i=1 iteration has found out is true.
+ inst_to_choose = 1;
+ break;
+ }
+ else if (insn_size == 4) {
+ // This instruction claims its a valid 4-byte instruction. But it
+ // could be a part of it's upper 4-byte instruction. Lets try
+ // scanning upper 2 bytes to verify this.
+ instruction_list.Append(prev_insn);
+ inst_to_choose = 2;
+ }
+ }
+ else if (i == 3) {
+ if (insn_size == 4)
+ // FIXME: We reached here that means instruction at [target - 4] has
+ // already claimed to be a 4-byte instruction, and now instruction
+ // at [target - 6] is also claiming that it's a 4-byte instruction.
+ // This can not be true. In this case we can not decide the valid
+ // previous instruction so we let lldb set the breakpoint at the
+ // address given by user.
+ inst_to_choose = 0;
+ else
+ // This is straight-forward
+ inst_to_choose = 2;
+ break;
+ }
+ }
+ else {
+ // Decode failed, bytes do not form a valid instruction. So whatever
+ // previous iteration has found out is true.
+ if (i > 1) {
+ inst_to_choose = i - 1;
+ break;
+ }
+ }
+ }
+
+ // Check if we are able to find any valid instruction.
+ if (inst_to_choose) {
+ if (inst_to_choose > instruction_list.GetSize())
+ inst_to_choose--;
+ return instruction_list.GetInstructionAtIndex(inst_to_choose - 1).get();
+ }
+
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.h b/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.h
new file mode 100644
index 000000000000..a15991ff9ebf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.h
@@ -0,0 +1,51 @@
+//===-- ArchitectureMips.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGIN_ARCHITECTURE_MIPS_H
+#define LLDB_PLUGIN_ARCHITECTURE_MIPS_H
+
+#include "lldb/Core/Architecture.h"
+#include "lldb/Utility/ArchSpec.h"
+
+namespace lldb_private {
+
+class ArchitectureMips : public Architecture {
+public:
+ static ConstString GetPluginNameStatic();
+ static void Initialize();
+ static void Terminate();
+
+ ConstString GetPluginName() override;
+ uint32_t GetPluginVersion() override;
+
+ void OverrideStopInfo(Thread &thread) const override {}
+
+ lldb::addr_t GetBreakableLoadAddress(lldb::addr_t addr,
+ Target &) const override;
+
+ lldb::addr_t GetCallableLoadAddress(lldb::addr_t load_addr,
+ AddressClass addr_class) const override;
+
+ lldb::addr_t GetOpcodeLoadAddress(lldb::addr_t load_addr,
+ AddressClass addr_class) const override;
+
+private:
+ Instruction *GetInstructionAtAddress(const ExecutionContext &exe_ctx,
+ const Address &resolved_addr,
+ lldb::addr_t symbol_offset) const;
+
+
+ static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
+ ArchitectureMips(const ArchSpec &arch) : m_arch(arch) {}
+
+ ArchSpec m_arch;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_PLUGIN_ARCHITECTURE_MIPS_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.cpp b/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.cpp
new file mode 100644
index 000000000000..76eaa44546eb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.cpp
@@ -0,0 +1,67 @@
+//===-- ArchitecturePPC64.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/Architecture/PPC64/ArchitecturePPC64.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+
+#include "llvm/BinaryFormat/ELF.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+ConstString ArchitecturePPC64::GetPluginNameStatic() {
+ return ConstString("ppc64");
+}
+
+void ArchitecturePPC64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "PPC64-specific algorithms",
+ &ArchitecturePPC64::Create);
+}
+
+void ArchitecturePPC64::Terminate() {
+ PluginManager::UnregisterPlugin(&ArchitecturePPC64::Create);
+}
+
+std::unique_ptr<Architecture> ArchitecturePPC64::Create(const ArchSpec &arch) {
+ if (arch.GetTriple().isPPC64() &&
+ arch.GetTriple().getObjectFormat() == llvm::Triple::ObjectFormatType::ELF)
+ return std::unique_ptr<Architecture>(new ArchitecturePPC64());
+ return nullptr;
+}
+
+ConstString ArchitecturePPC64::GetPluginName() { return GetPluginNameStatic(); }
+uint32_t ArchitecturePPC64::GetPluginVersion() { return 1; }
+
+static int32_t GetLocalEntryOffset(const Symbol &sym) {
+ unsigned char other = sym.GetFlags() >> 8 & 0xFF;
+ return llvm::ELF::decodePPC64LocalEntryOffset(other);
+}
+
+size_t ArchitecturePPC64::GetBytesToSkip(Symbol &func,
+ const Address &curr_addr) const {
+ if (curr_addr.GetFileAddress() ==
+ func.GetFileAddress() + GetLocalEntryOffset(func))
+ return func.GetPrologueByteSize();
+ return 0;
+}
+
+void ArchitecturePPC64::AdjustBreakpointAddress(const Symbol &func,
+ Address &addr) const {
+ int32_t loffs = GetLocalEntryOffset(func);
+ if (!loffs)
+ return;
+
+ addr.SetOffset(addr.GetOffset() + loffs);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.h b/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.h
new file mode 100644
index 000000000000..dc663b849c4a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.h
@@ -0,0 +1,41 @@
+//===-- ArchitecturePPC64.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGIN_ARCHITECTURE_PPC64_H
+#define LLDB_PLUGIN_ARCHITECTURE_PPC64_H
+
+#include "lldb/Core/Architecture.h"
+
+namespace lldb_private {
+
+class ArchitecturePPC64 : public Architecture {
+public:
+ static ConstString GetPluginNameStatic();
+ static void Initialize();
+ static void Terminate();
+
+ ConstString GetPluginName() override;
+ uint32_t GetPluginVersion() override;
+
+ void OverrideStopInfo(Thread &thread) const override {}
+
+ /// This method compares current address with current function's
+ /// local entry point, returning the bytes to skip if they match.
+ size_t GetBytesToSkip(Symbol &func, const Address &curr_addr) const override;
+
+ void AdjustBreakpointAddress(const Symbol &func,
+ Address &addr) const override;
+
+private:
+ static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
+ ArchitecturePPC64() = default;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_PLUGIN_ARCHITECTURE_PPC64_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp b/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
new file mode 100644
index 000000000000..44c75fc953c8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
@@ -0,0 +1,1433 @@
+//===-- DisassemblerLLVMC.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DisassemblerLLVMC.h"
+
+#include "llvm-c/Disassembler.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCDisassembler/MCExternalSymbolizer.h"
+#include "llvm/MC/MCDisassembler/MCRelocationInfo.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class DisassemblerLLVMC::MCDisasmInstance {
+public:
+ static std::unique_ptr<MCDisasmInstance>
+ Create(const char *triple, const char *cpu, const char *features_str,
+ unsigned flavor, DisassemblerLLVMC &owner);
+
+ ~MCDisasmInstance() = default;
+
+ uint64_t GetMCInst(const uint8_t *opcode_data, size_t opcode_data_len,
+ lldb::addr_t pc, llvm::MCInst &mc_inst) const;
+ void PrintMCInst(llvm::MCInst &mc_inst, std::string &inst_string,
+ std::string &comments_string);
+ void SetStyle(bool use_hex_immed, HexImmediateStyle hex_style);
+ bool CanBranch(llvm::MCInst &mc_inst) const;
+ bool HasDelaySlot(llvm::MCInst &mc_inst) const;
+ bool IsCall(llvm::MCInst &mc_inst) const;
+
+private:
+ MCDisasmInstance(std::unique_ptr<llvm::MCInstrInfo> &&instr_info_up,
+ std::unique_ptr<llvm::MCRegisterInfo> &&reg_info_up,
+ std::unique_ptr<llvm::MCSubtargetInfo> &&subtarget_info_up,
+ std::unique_ptr<llvm::MCAsmInfo> &&asm_info_up,
+ std::unique_ptr<llvm::MCContext> &&context_up,
+ std::unique_ptr<llvm::MCDisassembler> &&disasm_up,
+ std::unique_ptr<llvm::MCInstPrinter> &&instr_printer_up);
+
+ std::unique_ptr<llvm::MCInstrInfo> m_instr_info_up;
+ std::unique_ptr<llvm::MCRegisterInfo> m_reg_info_up;
+ std::unique_ptr<llvm::MCSubtargetInfo> m_subtarget_info_up;
+ std::unique_ptr<llvm::MCAsmInfo> m_asm_info_up;
+ std::unique_ptr<llvm::MCContext> m_context_up;
+ std::unique_ptr<llvm::MCDisassembler> m_disasm_up;
+ std::unique_ptr<llvm::MCInstPrinter> m_instr_printer_up;
+};
+
+class InstructionLLVMC : public lldb_private::Instruction {
+public:
+ InstructionLLVMC(DisassemblerLLVMC &disasm,
+ const lldb_private::Address &address,
+ AddressClass addr_class)
+ : Instruction(address, addr_class),
+ m_disasm_wp(std::static_pointer_cast<DisassemblerLLVMC>(
+ disasm.shared_from_this())),
+ m_does_branch(eLazyBoolCalculate), m_has_delay_slot(eLazyBoolCalculate),
+ m_is_call(eLazyBoolCalculate), m_is_valid(false),
+ m_using_file_addr(false) {}
+
+ ~InstructionLLVMC() override = default;
+
+ bool DoesBranch() override {
+ if (m_does_branch == eLazyBoolCalculate) {
+ DisassemblerScope disasm(*this);
+ if (disasm) {
+ DataExtractor data;
+ if (m_opcode.GetData(data)) {
+ bool is_alternate_isa;
+ lldb::addr_t pc = m_address.GetFileAddress();
+
+ DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr =
+ GetDisasmToUse(is_alternate_isa, disasm);
+ const uint8_t *opcode_data = data.GetDataStart();
+ const size_t opcode_data_len = data.GetByteSize();
+ llvm::MCInst inst;
+ const size_t inst_size =
+ mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst);
+ // Be conservative, if we didn't understand the instruction, say it
+ // might branch...
+ if (inst_size == 0)
+ m_does_branch = eLazyBoolYes;
+ else {
+ const bool can_branch = mc_disasm_ptr->CanBranch(inst);
+ if (can_branch)
+ m_does_branch = eLazyBoolYes;
+ else
+ m_does_branch = eLazyBoolNo;
+ }
+ }
+ }
+ }
+ return m_does_branch == eLazyBoolYes;
+ }
+
+ bool HasDelaySlot() override {
+ if (m_has_delay_slot == eLazyBoolCalculate) {
+ DisassemblerScope disasm(*this);
+ if (disasm) {
+ DataExtractor data;
+ if (m_opcode.GetData(data)) {
+ bool is_alternate_isa;
+ lldb::addr_t pc = m_address.GetFileAddress();
+
+ DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr =
+ GetDisasmToUse(is_alternate_isa, disasm);
+ const uint8_t *opcode_data = data.GetDataStart();
+ const size_t opcode_data_len = data.GetByteSize();
+ llvm::MCInst inst;
+ const size_t inst_size =
+ mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst);
+ // if we didn't understand the instruction, say it doesn't have a
+ // delay slot...
+ if (inst_size == 0)
+ m_has_delay_slot = eLazyBoolNo;
+ else {
+ const bool has_delay_slot = mc_disasm_ptr->HasDelaySlot(inst);
+ if (has_delay_slot)
+ m_has_delay_slot = eLazyBoolYes;
+ else
+ m_has_delay_slot = eLazyBoolNo;
+ }
+ }
+ }
+ }
+ return m_has_delay_slot == eLazyBoolYes;
+ }
+
+ DisassemblerLLVMC::MCDisasmInstance *GetDisasmToUse(bool &is_alternate_isa) {
+ DisassemblerScope disasm(*this);
+ return GetDisasmToUse(is_alternate_isa, disasm);
+ }
+
+ size_t Decode(const lldb_private::Disassembler &disassembler,
+ const lldb_private::DataExtractor &data,
+ lldb::offset_t data_offset) override {
+ // All we have to do is read the opcode which can be easy for some
+ // architectures
+ bool got_op = false;
+ DisassemblerScope disasm(*this);
+ if (disasm) {
+ const ArchSpec &arch = disasm->GetArchitecture();
+ const lldb::ByteOrder byte_order = data.GetByteOrder();
+
+ const uint32_t min_op_byte_size = arch.GetMinimumOpcodeByteSize();
+ const uint32_t max_op_byte_size = arch.GetMaximumOpcodeByteSize();
+ if (min_op_byte_size == max_op_byte_size) {
+ // Fixed size instructions, just read that amount of data.
+ if (!data.ValidOffsetForDataOfSize(data_offset, min_op_byte_size))
+ return false;
+
+ switch (min_op_byte_size) {
+ case 1:
+ m_opcode.SetOpcode8(data.GetU8(&data_offset), byte_order);
+ got_op = true;
+ break;
+
+ case 2:
+ m_opcode.SetOpcode16(data.GetU16(&data_offset), byte_order);
+ got_op = true;
+ break;
+
+ case 4:
+ m_opcode.SetOpcode32(data.GetU32(&data_offset), byte_order);
+ got_op = true;
+ break;
+
+ case 8:
+ m_opcode.SetOpcode64(data.GetU64(&data_offset), byte_order);
+ got_op = true;
+ break;
+
+ default:
+ m_opcode.SetOpcodeBytes(data.PeekData(data_offset, min_op_byte_size),
+ min_op_byte_size);
+ got_op = true;
+ break;
+ }
+ }
+ if (!got_op) {
+ bool is_alternate_isa = false;
+ DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr =
+ GetDisasmToUse(is_alternate_isa, disasm);
+
+ const llvm::Triple::ArchType machine = arch.GetMachine();
+ if (machine == llvm::Triple::arm || machine == llvm::Triple::thumb) {
+ if (machine == llvm::Triple::thumb || is_alternate_isa) {
+ uint32_t thumb_opcode = data.GetU16(&data_offset);
+ if ((thumb_opcode & 0xe000) != 0xe000 ||
+ ((thumb_opcode & 0x1800u) == 0)) {
+ m_opcode.SetOpcode16(thumb_opcode, byte_order);
+ m_is_valid = true;
+ } else {
+ thumb_opcode <<= 16;
+ thumb_opcode |= data.GetU16(&data_offset);
+ m_opcode.SetOpcode16_2(thumb_opcode, byte_order);
+ m_is_valid = true;
+ }
+ } else {
+ m_opcode.SetOpcode32(data.GetU32(&data_offset), byte_order);
+ m_is_valid = true;
+ }
+ } else {
+ // The opcode isn't evenly sized, so we need to actually use the llvm
+ // disassembler to parse it and get the size.
+ uint8_t *opcode_data =
+ const_cast<uint8_t *>(data.PeekData(data_offset, 1));
+ const size_t opcode_data_len = data.BytesLeft(data_offset);
+ const addr_t pc = m_address.GetFileAddress();
+ llvm::MCInst inst;
+
+ const size_t inst_size =
+ mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst);
+ if (inst_size == 0)
+ m_opcode.Clear();
+ else {
+ m_opcode.SetOpcodeBytes(opcode_data, inst_size);
+ m_is_valid = true;
+ }
+ }
+ }
+ return m_opcode.GetByteSize();
+ }
+ return 0;
+ }
+
+ void AppendComment(std::string &description) {
+ if (m_comment.empty())
+ m_comment.swap(description);
+ else {
+ m_comment.append(", ");
+ m_comment.append(description);
+ }
+ }
+
+ void CalculateMnemonicOperandsAndComment(
+ const lldb_private::ExecutionContext *exe_ctx) override {
+ DataExtractor data;
+ const AddressClass address_class = GetAddressClass();
+
+ if (m_opcode.GetData(data)) {
+ std::string out_string;
+ std::string comment_string;
+
+ DisassemblerScope disasm(*this, exe_ctx);
+ if (disasm) {
+ DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr;
+
+ if (address_class == AddressClass::eCodeAlternateISA)
+ mc_disasm_ptr = disasm->m_alternate_disasm_up.get();
+ else
+ mc_disasm_ptr = disasm->m_disasm_up.get();
+
+ lldb::addr_t pc = m_address.GetFileAddress();
+ m_using_file_addr = true;
+
+ const bool data_from_file = disasm->m_data_from_file;
+ bool use_hex_immediates = true;
+ Disassembler::HexImmediateStyle hex_style = Disassembler::eHexStyleC;
+
+ if (exe_ctx) {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target) {
+ use_hex_immediates = target->GetUseHexImmediates();
+ hex_style = target->GetHexImmediateStyle();
+
+ if (!data_from_file) {
+ const lldb::addr_t load_addr = m_address.GetLoadAddress(target);
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ pc = load_addr;
+ m_using_file_addr = false;
+ }
+ }
+ }
+ }
+
+ const uint8_t *opcode_data = data.GetDataStart();
+ const size_t opcode_data_len = data.GetByteSize();
+ llvm::MCInst inst;
+ size_t inst_size =
+ mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst);
+
+ if (inst_size > 0) {
+ mc_disasm_ptr->SetStyle(use_hex_immediates, hex_style);
+ mc_disasm_ptr->PrintMCInst(inst, out_string, comment_string);
+
+ if (!comment_string.empty()) {
+ AppendComment(comment_string);
+ }
+ }
+
+ if (inst_size == 0) {
+ m_comment.assign("unknown opcode");
+ inst_size = m_opcode.GetByteSize();
+ StreamString mnemonic_strm;
+ lldb::offset_t offset = 0;
+ lldb::ByteOrder byte_order = data.GetByteOrder();
+ switch (inst_size) {
+ case 1: {
+ const uint8_t uval8 = data.GetU8(&offset);
+ m_opcode.SetOpcode8(uval8, byte_order);
+ m_opcode_name.assign(".byte");
+ mnemonic_strm.Printf("0x%2.2x", uval8);
+ } break;
+ case 2: {
+ const uint16_t uval16 = data.GetU16(&offset);
+ m_opcode.SetOpcode16(uval16, byte_order);
+ m_opcode_name.assign(".short");
+ mnemonic_strm.Printf("0x%4.4x", uval16);
+ } break;
+ case 4: {
+ const uint32_t uval32 = data.GetU32(&offset);
+ m_opcode.SetOpcode32(uval32, byte_order);
+ m_opcode_name.assign(".long");
+ mnemonic_strm.Printf("0x%8.8x", uval32);
+ } break;
+ case 8: {
+ const uint64_t uval64 = data.GetU64(&offset);
+ m_opcode.SetOpcode64(uval64, byte_order);
+ m_opcode_name.assign(".quad");
+ mnemonic_strm.Printf("0x%16.16" PRIx64, uval64);
+ } break;
+ default:
+ if (inst_size == 0)
+ return;
+ else {
+ const uint8_t *bytes = data.PeekData(offset, inst_size);
+ if (bytes == nullptr)
+ return;
+ m_opcode_name.assign(".byte");
+ m_opcode.SetOpcodeBytes(bytes, inst_size);
+ mnemonic_strm.Printf("0x%2.2x", bytes[0]);
+ for (uint32_t i = 1; i < inst_size; ++i)
+ mnemonic_strm.Printf(" 0x%2.2x", bytes[i]);
+ }
+ break;
+ }
+ m_mnemonics = mnemonic_strm.GetString();
+ return;
+ } else {
+ if (m_does_branch == eLazyBoolCalculate) {
+ const bool can_branch = mc_disasm_ptr->CanBranch(inst);
+ if (can_branch)
+ m_does_branch = eLazyBoolYes;
+ else
+ m_does_branch = eLazyBoolNo;
+ }
+ }
+
+ static RegularExpression s_regex(
+ llvm::StringRef("[ \t]*([^ ^\t]+)[ \t]*([^ ^\t].*)?"));
+
+ RegularExpression::Match matches(3);
+
+ if (s_regex.Execute(out_string, &matches)) {
+ matches.GetMatchAtIndex(out_string.c_str(), 1, m_opcode_name);
+ matches.GetMatchAtIndex(out_string.c_str(), 2, m_mnemonics);
+ }
+ }
+ }
+ }
+
+ bool IsValid() const { return m_is_valid; }
+
+ bool UsingFileAddress() const { return m_using_file_addr; }
+ size_t GetByteSize() const { return m_opcode.GetByteSize(); }
+
+ /// Grants exclusive access to the disassembler and initializes it with the
+ /// given InstructionLLVMC and an optional ExecutionContext.
+ class DisassemblerScope {
+ std::shared_ptr<DisassemblerLLVMC> m_disasm;
+
+ public:
+ explicit DisassemblerScope(
+ InstructionLLVMC &i,
+ const lldb_private::ExecutionContext *exe_ctx = nullptr)
+ : m_disasm(i.m_disasm_wp.lock()) {
+ m_disasm->m_mutex.lock();
+ m_disasm->m_inst = &i;
+ m_disasm->m_exe_ctx = exe_ctx;
+ }
+ ~DisassemblerScope() { m_disasm->m_mutex.unlock(); }
+
+ /// Evaluates to true if this scope contains a valid disassembler.
+ operator bool() const { return static_cast<bool>(m_disasm); }
+
+ std::shared_ptr<DisassemblerLLVMC> operator->() { return m_disasm; }
+ };
+
+ static llvm::StringRef::const_iterator
+ ConsumeWhitespace(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ while (osi != ose) {
+ switch (*osi) {
+ default:
+ return osi;
+ case ' ':
+ case '\t':
+ break;
+ }
+ ++osi;
+ }
+
+ return osi;
+ }
+
+ static std::pair<bool, llvm::StringRef::const_iterator>
+ ConsumeChar(llvm::StringRef::const_iterator osi, const char c,
+ llvm::StringRef::const_iterator ose) {
+ bool found = false;
+
+ osi = ConsumeWhitespace(osi, ose);
+ if (osi != ose && *osi == c) {
+ found = true;
+ ++osi;
+ }
+
+ return std::make_pair(found, osi);
+ }
+
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseRegisterName(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ Operand ret;
+ ret.m_type = Operand::Type::Register;
+ std::string str;
+
+ osi = ConsumeWhitespace(osi, ose);
+
+ while (osi != ose) {
+ if (*osi >= '0' && *osi <= '9') {
+ if (str.empty()) {
+ return std::make_pair(Operand(), osi);
+ } else {
+ str.push_back(*osi);
+ }
+ } else if (*osi >= 'a' && *osi <= 'z') {
+ str.push_back(*osi);
+ } else {
+ switch (*osi) {
+ default:
+ if (str.empty()) {
+ return std::make_pair(Operand(), osi);
+ } else {
+ ret.m_register = ConstString(str);
+ return std::make_pair(ret, osi);
+ }
+ case '%':
+ if (!str.empty()) {
+ return std::make_pair(Operand(), osi);
+ }
+ break;
+ }
+ }
+ ++osi;
+ }
+
+ ret.m_register = ConstString(str);
+ return std::make_pair(ret, osi);
+ }
+
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseImmediate(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ Operand ret;
+ ret.m_type = Operand::Type::Immediate;
+ std::string str;
+ bool is_hex = false;
+
+ osi = ConsumeWhitespace(osi, ose);
+
+ while (osi != ose) {
+ if (*osi >= '0' && *osi <= '9') {
+ str.push_back(*osi);
+ } else if (*osi >= 'a' && *osi <= 'f') {
+ if (is_hex) {
+ str.push_back(*osi);
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+ } else {
+ switch (*osi) {
+ default:
+ if (str.empty()) {
+ return std::make_pair(Operand(), osi);
+ } else {
+ ret.m_immediate = strtoull(str.c_str(), nullptr, 0);
+ return std::make_pair(ret, osi);
+ }
+ case 'x':
+ if (!str.compare("0")) {
+ is_hex = true;
+ str.push_back(*osi);
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+ break;
+ case '#':
+ case '$':
+ if (!str.empty()) {
+ return std::make_pair(Operand(), osi);
+ }
+ break;
+ case '-':
+ if (str.empty()) {
+ ret.m_negative = true;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+ }
+ }
+ ++osi;
+ }
+
+ ret.m_immediate = strtoull(str.c_str(), nullptr, 0);
+ return std::make_pair(ret, osi);
+ }
+
+ // -0x5(%rax,%rax,2)
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseIntelIndexedAccess(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator =
+ ParseImmediate(osi, ose);
+ if (offset_and_iterator.first.IsValid()) {
+ osi = offset_and_iterator.second;
+ }
+
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '(', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator =
+ ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid()) {
+ osi = base_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ',', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> index_and_iterator =
+ ParseRegisterName(osi, ose);
+ if (index_and_iterator.first.IsValid()) {
+ osi = index_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ',', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator>
+ multiplier_and_iterator = ParseImmediate(osi, ose);
+ if (index_and_iterator.first.IsValid()) {
+ osi = index_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ')', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ Operand product;
+ product.m_type = Operand::Type::Product;
+ product.m_children.push_back(index_and_iterator.first);
+ product.m_children.push_back(multiplier_and_iterator.first);
+
+ Operand index;
+ index.m_type = Operand::Type::Sum;
+ index.m_children.push_back(base_and_iterator.first);
+ index.m_children.push_back(product);
+
+ if (offset_and_iterator.first.IsValid()) {
+ Operand offset;
+ offset.m_type = Operand::Type::Sum;
+ offset.m_children.push_back(offset_and_iterator.first);
+ offset.m_children.push_back(index);
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(offset);
+ return std::make_pair(deref, osi);
+ } else {
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(index);
+ return std::make_pair(deref, osi);
+ }
+ }
+
+ // -0x10(%rbp)
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseIntelDerefAccess(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator =
+ ParseImmediate(osi, ose);
+ if (offset_and_iterator.first.IsValid()) {
+ osi = offset_and_iterator.second;
+ }
+
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '(', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator =
+ ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid()) {
+ osi = base_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ')', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ if (offset_and_iterator.first.IsValid()) {
+ Operand offset;
+ offset.m_type = Operand::Type::Sum;
+ offset.m_children.push_back(offset_and_iterator.first);
+ offset.m_children.push_back(base_and_iterator.first);
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(offset);
+ return std::make_pair(deref, osi);
+ } else {
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(base_and_iterator.first);
+ return std::make_pair(deref, osi);
+ }
+ }
+
+ // [sp, #8]!
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseARMOffsetAccess(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '[', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator =
+ ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid()) {
+ osi = base_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ',', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator =
+ ParseImmediate(osi, ose);
+ if (offset_and_iterator.first.IsValid()) {
+ osi = offset_and_iterator.second;
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ']', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ Operand offset;
+ offset.m_type = Operand::Type::Sum;
+ offset.m_children.push_back(offset_and_iterator.first);
+ offset.m_children.push_back(base_and_iterator.first);
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(offset);
+ return std::make_pair(deref, osi);
+ }
+
+ // [sp]
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseARMDerefAccess(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '[', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator =
+ ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid()) {
+ osi = base_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ']', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(base_and_iterator.first);
+ return std::make_pair(deref, osi);
+ }
+
+ static void DumpOperand(const Operand &op, Stream &s) {
+ switch (op.m_type) {
+ case Operand::Type::Dereference:
+ s.PutCString("*");
+ DumpOperand(op.m_children[0], s);
+ break;
+ case Operand::Type::Immediate:
+ if (op.m_negative) {
+ s.PutCString("-");
+ }
+ s.PutCString(llvm::to_string(op.m_immediate));
+ break;
+ case Operand::Type::Invalid:
+ s.PutCString("Invalid");
+ break;
+ case Operand::Type::Product:
+ s.PutCString("(");
+ DumpOperand(op.m_children[0], s);
+ s.PutCString("*");
+ DumpOperand(op.m_children[1], s);
+ s.PutCString(")");
+ break;
+ case Operand::Type::Register:
+ s.PutCString(op.m_register.AsCString());
+ break;
+ case Operand::Type::Sum:
+ s.PutCString("(");
+ DumpOperand(op.m_children[0], s);
+ s.PutCString("+");
+ DumpOperand(op.m_children[1], s);
+ s.PutCString(")");
+ break;
+ }
+ }
+
+ bool ParseOperands(
+ llvm::SmallVectorImpl<Instruction::Operand> &operands) override {
+ const char *operands_string = GetOperands(nullptr);
+
+ if (!operands_string) {
+ return false;
+ }
+
+ llvm::StringRef operands_ref(operands_string);
+
+ llvm::StringRef::const_iterator osi = operands_ref.begin();
+ llvm::StringRef::const_iterator ose = operands_ref.end();
+
+ while (osi != ose) {
+ Operand operand;
+ llvm::StringRef::const_iterator iter;
+
+ if ((std::tie(operand, iter) = ParseIntelIndexedAccess(osi, ose),
+ operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseIntelDerefAccess(osi, ose),
+ operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseARMOffsetAccess(osi, ose),
+ operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseARMDerefAccess(osi, ose),
+ operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseRegisterName(osi, ose),
+ operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseImmediate(osi, ose),
+ operand.IsValid())) {
+ osi = iter;
+ operands.push_back(operand);
+ } else {
+ return false;
+ }
+
+ std::pair<bool, llvm::StringRef::const_iterator> found_and_iter =
+ ConsumeChar(osi, ',', ose);
+ if (found_and_iter.first) {
+ osi = found_and_iter.second;
+ }
+
+ osi = ConsumeWhitespace(osi, ose);
+ }
+
+ DisassemblerSP disasm_sp = m_disasm_wp.lock();
+
+ if (disasm_sp && operands.size() > 1) {
+ // TODO tie this into the MC Disassembler's notion of clobbers.
+ switch (disasm_sp->GetArchitecture().GetMachine()) {
+ default:
+ break;
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ operands[operands.size() - 1].m_clobbered = true;
+ break;
+ case llvm::Triple::arm:
+ operands[0].m_clobbered = true;
+ break;
+ }
+ }
+
+ if (Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)) {
+ StreamString ss;
+
+ ss.Printf("[%s] expands to %zu operands:\n", operands_string,
+ operands.size());
+ for (const Operand &operand : operands) {
+ ss.PutCString(" ");
+ DumpOperand(operand, ss);
+ ss.PutCString("\n");
+ }
+
+ log->PutString(ss.GetString());
+ }
+
+ return true;
+ }
+
+ bool IsCall() override {
+ if (m_is_call == eLazyBoolCalculate) {
+ DisassemblerScope disasm(*this);
+ if (disasm) {
+ DataExtractor data;
+ if (m_opcode.GetData(data)) {
+ bool is_alternate_isa;
+ lldb::addr_t pc = m_address.GetFileAddress();
+
+ DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr =
+ GetDisasmToUse(is_alternate_isa, disasm);
+ const uint8_t *opcode_data = data.GetDataStart();
+ const size_t opcode_data_len = data.GetByteSize();
+ llvm::MCInst inst;
+ const size_t inst_size =
+ mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst);
+ if (inst_size == 0) {
+ m_is_call = eLazyBoolNo;
+ } else {
+ if (mc_disasm_ptr->IsCall(inst))
+ m_is_call = eLazyBoolYes;
+ else
+ m_is_call = eLazyBoolNo;
+ }
+ }
+ }
+ }
+ return m_is_call == eLazyBoolYes;
+ }
+
+protected:
+ std::weak_ptr<DisassemblerLLVMC> m_disasm_wp;
+ LazyBool m_does_branch;
+ LazyBool m_has_delay_slot;
+ LazyBool m_is_call;
+ bool m_is_valid;
+ bool m_using_file_addr;
+
+private:
+ DisassemblerLLVMC::MCDisasmInstance *
+ GetDisasmToUse(bool &is_alternate_isa, DisassemblerScope &disasm) {
+ is_alternate_isa = false;
+ if (disasm) {
+ if (disasm->m_alternate_disasm_up) {
+ const AddressClass address_class = GetAddressClass();
+
+ if (address_class == AddressClass::eCodeAlternateISA) {
+ is_alternate_isa = true;
+ return disasm->m_alternate_disasm_up.get();
+ }
+ }
+ return disasm->m_disasm_up.get();
+ }
+ return nullptr;
+ }
+};
+
+std::unique_ptr<DisassemblerLLVMC::MCDisasmInstance>
+DisassemblerLLVMC::MCDisasmInstance::Create(const char *triple, const char *cpu,
+ const char *features_str,
+ unsigned flavor,
+ DisassemblerLLVMC &owner) {
+ using Instance = std::unique_ptr<DisassemblerLLVMC::MCDisasmInstance>;
+
+ std::string Status;
+ const llvm::Target *curr_target =
+ llvm::TargetRegistry::lookupTarget(triple, Status);
+ if (!curr_target)
+ return Instance();
+
+ std::unique_ptr<llvm::MCInstrInfo> instr_info_up(
+ curr_target->createMCInstrInfo());
+ if (!instr_info_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCRegisterInfo> reg_info_up(
+ curr_target->createMCRegInfo(triple));
+ if (!reg_info_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCSubtargetInfo> subtarget_info_up(
+ curr_target->createMCSubtargetInfo(triple, cpu, features_str));
+ if (!subtarget_info_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCAsmInfo> asm_info_up(
+ curr_target->createMCAsmInfo(*reg_info_up, triple));
+ if (!asm_info_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCContext> context_up(
+ new llvm::MCContext(asm_info_up.get(), reg_info_up.get(), nullptr));
+ if (!context_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCDisassembler> disasm_up(
+ curr_target->createMCDisassembler(*subtarget_info_up, *context_up));
+ if (!disasm_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCRelocationInfo> rel_info_up(
+ curr_target->createMCRelocationInfo(triple, *context_up));
+ if (!rel_info_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCSymbolizer> symbolizer_up(
+ curr_target->createMCSymbolizer(
+ triple, nullptr, DisassemblerLLVMC::SymbolLookupCallback, &owner,
+ context_up.get(), std::move(rel_info_up)));
+ disasm_up->setSymbolizer(std::move(symbolizer_up));
+
+ unsigned asm_printer_variant =
+ flavor == ~0U ? asm_info_up->getAssemblerDialect() : flavor;
+
+ std::unique_ptr<llvm::MCInstPrinter> instr_printer_up(
+ curr_target->createMCInstPrinter(llvm::Triple{triple},
+ asm_printer_variant, *asm_info_up,
+ *instr_info_up, *reg_info_up));
+ if (!instr_printer_up)
+ return Instance();
+
+ return Instance(
+ new MCDisasmInstance(std::move(instr_info_up), std::move(reg_info_up),
+ std::move(subtarget_info_up), std::move(asm_info_up),
+ std::move(context_up), std::move(disasm_up),
+ std::move(instr_printer_up)));
+}
+
+DisassemblerLLVMC::MCDisasmInstance::MCDisasmInstance(
+ std::unique_ptr<llvm::MCInstrInfo> &&instr_info_up,
+ std::unique_ptr<llvm::MCRegisterInfo> &&reg_info_up,
+ std::unique_ptr<llvm::MCSubtargetInfo> &&subtarget_info_up,
+ std::unique_ptr<llvm::MCAsmInfo> &&asm_info_up,
+ std::unique_ptr<llvm::MCContext> &&context_up,
+ std::unique_ptr<llvm::MCDisassembler> &&disasm_up,
+ std::unique_ptr<llvm::MCInstPrinter> &&instr_printer_up)
+ : m_instr_info_up(std::move(instr_info_up)),
+ m_reg_info_up(std::move(reg_info_up)),
+ m_subtarget_info_up(std::move(subtarget_info_up)),
+ m_asm_info_up(std::move(asm_info_up)),
+ m_context_up(std::move(context_up)), m_disasm_up(std::move(disasm_up)),
+ m_instr_printer_up(std::move(instr_printer_up)) {
+ assert(m_instr_info_up && m_reg_info_up && m_subtarget_info_up &&
+ m_asm_info_up && m_context_up && m_disasm_up && m_instr_printer_up);
+}
+
+uint64_t DisassemblerLLVMC::MCDisasmInstance::GetMCInst(
+ const uint8_t *opcode_data, size_t opcode_data_len, lldb::addr_t pc,
+ llvm::MCInst &mc_inst) const {
+ llvm::ArrayRef<uint8_t> data(opcode_data, opcode_data_len);
+ llvm::MCDisassembler::DecodeStatus status;
+
+ uint64_t new_inst_size;
+ status = m_disasm_up->getInstruction(mc_inst, new_inst_size, data, pc,
+ llvm::nulls(), llvm::nulls());
+ if (status == llvm::MCDisassembler::Success)
+ return new_inst_size;
+ else
+ return 0;
+}
+
+void DisassemblerLLVMC::MCDisasmInstance::PrintMCInst(
+ llvm::MCInst &mc_inst, std::string &inst_string,
+ std::string &comments_string) {
+ llvm::raw_string_ostream inst_stream(inst_string);
+ llvm::raw_string_ostream comments_stream(comments_string);
+
+ m_instr_printer_up->setCommentStream(comments_stream);
+ m_instr_printer_up->printInst(&mc_inst, inst_stream, llvm::StringRef(),
+ *m_subtarget_info_up);
+ m_instr_printer_up->setCommentStream(llvm::nulls());
+ comments_stream.flush();
+
+ static std::string g_newlines("\r\n");
+
+ for (size_t newline_pos = 0;
+ (newline_pos = comments_string.find_first_of(g_newlines, newline_pos)) !=
+ comments_string.npos;
+ /**/) {
+ comments_string.replace(comments_string.begin() + newline_pos,
+ comments_string.begin() + newline_pos + 1, 1, ' ');
+ }
+}
+
+void DisassemblerLLVMC::MCDisasmInstance::SetStyle(
+ bool use_hex_immed, HexImmediateStyle hex_style) {
+ m_instr_printer_up->setPrintImmHex(use_hex_immed);
+ switch (hex_style) {
+ case eHexStyleC:
+ m_instr_printer_up->setPrintHexStyle(llvm::HexStyle::C);
+ break;
+ case eHexStyleAsm:
+ m_instr_printer_up->setPrintHexStyle(llvm::HexStyle::Asm);
+ break;
+ }
+}
+
+bool DisassemblerLLVMC::MCDisasmInstance::CanBranch(
+ llvm::MCInst &mc_inst) const {
+ return m_instr_info_up->get(mc_inst.getOpcode())
+ .mayAffectControlFlow(mc_inst, *m_reg_info_up);
+}
+
+bool DisassemblerLLVMC::MCDisasmInstance::HasDelaySlot(
+ llvm::MCInst &mc_inst) const {
+ return m_instr_info_up->get(mc_inst.getOpcode()).hasDelaySlot();
+}
+
+bool DisassemblerLLVMC::MCDisasmInstance::IsCall(llvm::MCInst &mc_inst) const {
+ return m_instr_info_up->get(mc_inst.getOpcode()).isCall();
+}
+
+DisassemblerLLVMC::DisassemblerLLVMC(const ArchSpec &arch,
+ const char *flavor_string)
+ : Disassembler(arch, flavor_string), m_exe_ctx(nullptr), m_inst(nullptr),
+ m_data_from_file(false) {
+ if (!FlavorValidForArchSpec(arch, m_flavor.c_str())) {
+ m_flavor.assign("default");
+ }
+
+ unsigned flavor = ~0U;
+ llvm::Triple triple = arch.GetTriple();
+
+ // So far the only supported flavor is "intel" on x86. The base class will
+ // set this correctly coming in.
+ if (triple.getArch() == llvm::Triple::x86 ||
+ triple.getArch() == llvm::Triple::x86_64) {
+ if (m_flavor == "intel") {
+ flavor = 1;
+ } else if (m_flavor == "att") {
+ flavor = 0;
+ }
+ }
+
+ ArchSpec thumb_arch(arch);
+ if (triple.getArch() == llvm::Triple::arm) {
+ std::string thumb_arch_name(thumb_arch.GetTriple().getArchName().str());
+ // Replace "arm" with "thumb" so we get all thumb variants correct
+ if (thumb_arch_name.size() > 3) {
+ thumb_arch_name.erase(0, 3);
+ thumb_arch_name.insert(0, "thumb");
+ } else {
+ thumb_arch_name = "thumbv8.2a";
+ }
+ thumb_arch.GetTriple().setArchName(llvm::StringRef(thumb_arch_name));
+ }
+
+ // If no sub architecture specified then use the most recent arm architecture
+ // so the disassembler will return all instruction. Without it we will see a
+ // lot of unknow opcode in case the code uses instructions which are not
+ // available in the oldest arm version (used when no sub architecture is
+ // specified)
+ if (triple.getArch() == llvm::Triple::arm &&
+ triple.getSubArch() == llvm::Triple::NoSubArch)
+ triple.setArchName("armv8.2a");
+
+ std::string features_str = "";
+ const char *triple_str = triple.getTriple().c_str();
+
+ // ARM Cortex M0-M7 devices only execute thumb instructions
+ if (arch.IsAlwaysThumbInstructions()) {
+ triple_str = thumb_arch.GetTriple().getTriple().c_str();
+ features_str += "+fp-armv8,";
+ }
+
+ const char *cpu = "";
+
+ switch (arch.GetCore()) {
+ case ArchSpec::eCore_mips32:
+ case ArchSpec::eCore_mips32el:
+ cpu = "mips32";
+ break;
+ case ArchSpec::eCore_mips32r2:
+ case ArchSpec::eCore_mips32r2el:
+ cpu = "mips32r2";
+ break;
+ case ArchSpec::eCore_mips32r3:
+ case ArchSpec::eCore_mips32r3el:
+ cpu = "mips32r3";
+ break;
+ case ArchSpec::eCore_mips32r5:
+ case ArchSpec::eCore_mips32r5el:
+ cpu = "mips32r5";
+ break;
+ case ArchSpec::eCore_mips32r6:
+ case ArchSpec::eCore_mips32r6el:
+ cpu = "mips32r6";
+ break;
+ case ArchSpec::eCore_mips64:
+ case ArchSpec::eCore_mips64el:
+ cpu = "mips64";
+ break;
+ case ArchSpec::eCore_mips64r2:
+ case ArchSpec::eCore_mips64r2el:
+ cpu = "mips64r2";
+ break;
+ case ArchSpec::eCore_mips64r3:
+ case ArchSpec::eCore_mips64r3el:
+ cpu = "mips64r3";
+ break;
+ case ArchSpec::eCore_mips64r5:
+ case ArchSpec::eCore_mips64r5el:
+ cpu = "mips64r5";
+ break;
+ case ArchSpec::eCore_mips64r6:
+ case ArchSpec::eCore_mips64r6el:
+ cpu = "mips64r6";
+ break;
+ default:
+ cpu = "";
+ break;
+ }
+
+ if (arch.IsMIPS()) {
+ uint32_t arch_flags = arch.GetFlags();
+ if (arch_flags & ArchSpec::eMIPSAse_msa)
+ features_str += "+msa,";
+ if (arch_flags & ArchSpec::eMIPSAse_dsp)
+ features_str += "+dsp,";
+ if (arch_flags & ArchSpec::eMIPSAse_dspr2)
+ features_str += "+dspr2,";
+ }
+
+ // If any AArch64 variant, enable the ARMv8.5 ISA with SVE extensions so we
+ // can disassemble newer instructions.
+ if (triple.getArch() == llvm::Triple::aarch64)
+ features_str += "+v8.5a,+sve2";
+
+ if (triple.getArch() == llvm::Triple::aarch64
+ && triple.getVendor() == llvm::Triple::Apple) {
+ cpu = "apple-latest";
+ }
+
+ // We use m_disasm_up.get() to tell whether we are valid or not, so if this
+ // isn't good for some reason, we won't be valid and FindPlugin will fail and
+ // we won't get used.
+ m_disasm_up = MCDisasmInstance::Create(triple_str, cpu, features_str.c_str(),
+ flavor, *this);
+
+ llvm::Triple::ArchType llvm_arch = triple.getArch();
+
+ // For arm CPUs that can execute arm or thumb instructions, also create a
+ // thumb instruction disassembler.
+ if (llvm_arch == llvm::Triple::arm) {
+ std::string thumb_triple(thumb_arch.GetTriple().getTriple());
+ m_alternate_disasm_up =
+ MCDisasmInstance::Create(thumb_triple.c_str(), "", features_str.c_str(),
+ flavor, *this);
+ if (!m_alternate_disasm_up)
+ m_disasm_up.reset();
+
+ } else if (arch.IsMIPS()) {
+ /* Create alternate disassembler for MIPS16 and microMIPS */
+ uint32_t arch_flags = arch.GetFlags();
+ if (arch_flags & ArchSpec::eMIPSAse_mips16)
+ features_str += "+mips16,";
+ else if (arch_flags & ArchSpec::eMIPSAse_micromips)
+ features_str += "+micromips,";
+
+ m_alternate_disasm_up = MCDisasmInstance::Create(
+ triple_str, cpu, features_str.c_str(), flavor, *this);
+ if (!m_alternate_disasm_up)
+ m_disasm_up.reset();
+ }
+}
+
+DisassemblerLLVMC::~DisassemblerLLVMC() = default;
+
+Disassembler *DisassemblerLLVMC::CreateInstance(const ArchSpec &arch,
+ const char *flavor) {
+ if (arch.GetTriple().getArch() != llvm::Triple::UnknownArch) {
+ std::unique_ptr<DisassemblerLLVMC> disasm_up(
+ new DisassemblerLLVMC(arch, flavor));
+
+ if (disasm_up.get() && disasm_up->IsValid())
+ return disasm_up.release();
+ }
+ return nullptr;
+}
+
+size_t DisassemblerLLVMC::DecodeInstructions(const Address &base_addr,
+ const DataExtractor &data,
+ lldb::offset_t data_offset,
+ size_t num_instructions,
+ bool append, bool data_from_file) {
+ if (!append)
+ m_instruction_list.Clear();
+
+ if (!IsValid())
+ return 0;
+
+ m_data_from_file = data_from_file;
+ uint32_t data_cursor = data_offset;
+ const size_t data_byte_size = data.GetByteSize();
+ uint32_t instructions_parsed = 0;
+ Address inst_addr(base_addr);
+
+ while (data_cursor < data_byte_size &&
+ instructions_parsed < num_instructions) {
+
+ AddressClass address_class = AddressClass::eCode;
+
+ if (m_alternate_disasm_up)
+ address_class = inst_addr.GetAddressClass();
+
+ InstructionSP inst_sp(
+ new InstructionLLVMC(*this, inst_addr, address_class));
+
+ if (!inst_sp)
+ break;
+
+ uint32_t inst_size = inst_sp->Decode(*this, data, data_cursor);
+
+ if (inst_size == 0)
+ break;
+
+ m_instruction_list.Append(inst_sp);
+ data_cursor += inst_size;
+ inst_addr.Slide(inst_size);
+ instructions_parsed++;
+ }
+
+ return data_cursor - data_offset;
+}
+
+void DisassemblerLLVMC::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "Disassembler that uses LLVM MC to disassemble "
+ "i386, x86_64, ARM, and ARM64.",
+ CreateInstance);
+
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmParsers();
+ llvm::InitializeAllDisassemblers();
+}
+
+void DisassemblerLLVMC::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString DisassemblerLLVMC::GetPluginNameStatic() {
+ static ConstString g_name("llvm-mc");
+ return g_name;
+}
+
+int DisassemblerLLVMC::OpInfoCallback(void *disassembler, uint64_t pc,
+ uint64_t offset, uint64_t size,
+ int tag_type, void *tag_bug) {
+ return static_cast<DisassemblerLLVMC *>(disassembler)
+ ->OpInfo(pc, offset, size, tag_type, tag_bug);
+}
+
+const char *DisassemblerLLVMC::SymbolLookupCallback(void *disassembler,
+ uint64_t value,
+ uint64_t *type, uint64_t pc,
+ const char **name) {
+ return static_cast<DisassemblerLLVMC *>(disassembler)
+ ->SymbolLookup(value, type, pc, name);
+}
+
+bool DisassemblerLLVMC::FlavorValidForArchSpec(
+ const lldb_private::ArchSpec &arch, const char *flavor) {
+ llvm::Triple triple = arch.GetTriple();
+ if (flavor == nullptr || strcmp(flavor, "default") == 0)
+ return true;
+
+ if (triple.getArch() == llvm::Triple::x86 ||
+ triple.getArch() == llvm::Triple::x86_64) {
+ return strcmp(flavor, "intel") == 0 || strcmp(flavor, "att") == 0;
+ } else
+ return false;
+}
+
+bool DisassemblerLLVMC::IsValid() const { return m_disasm_up.operator bool(); }
+
+int DisassemblerLLVMC::OpInfo(uint64_t PC, uint64_t Offset, uint64_t Size,
+ int tag_type, void *tag_bug) {
+ switch (tag_type) {
+ default:
+ break;
+ case 1:
+ memset(tag_bug, 0, sizeof(::LLVMOpInfo1));
+ break;
+ }
+ return 0;
+}
+
+const char *DisassemblerLLVMC::SymbolLookup(uint64_t value, uint64_t *type_ptr,
+ uint64_t pc, const char **name) {
+ if (*type_ptr) {
+ if (m_exe_ctx && m_inst) {
+ // std::string remove_this_prior_to_checkin;
+ Target *target = m_exe_ctx ? m_exe_ctx->GetTargetPtr() : nullptr;
+ Address value_so_addr;
+ Address pc_so_addr;
+ if (m_inst->UsingFileAddress()) {
+ ModuleSP module_sp(m_inst->GetAddress().GetModule());
+ if (module_sp) {
+ module_sp->ResolveFileAddress(value, value_so_addr);
+ module_sp->ResolveFileAddress(pc, pc_so_addr);
+ }
+ } else if (target && !target->GetSectionLoadList().IsEmpty()) {
+ target->GetSectionLoadList().ResolveLoadAddress(value, value_so_addr);
+ target->GetSectionLoadList().ResolveLoadAddress(pc, pc_so_addr);
+ }
+
+ SymbolContext sym_ctx;
+ const SymbolContextItem resolve_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ if (pc_so_addr.IsValid() && pc_so_addr.GetModule()) {
+ pc_so_addr.GetModule()->ResolveSymbolContextForAddress(
+ pc_so_addr, resolve_scope, sym_ctx);
+ }
+
+ if (value_so_addr.IsValid() && value_so_addr.GetSection()) {
+ StreamString ss;
+
+ bool format_omitting_current_func_name = false;
+ if (sym_ctx.symbol || sym_ctx.function) {
+ AddressRange range;
+ if (sym_ctx.GetAddressRange(resolve_scope, 0, false, range) &&
+ range.GetBaseAddress().IsValid() &&
+ range.ContainsLoadAddress(value_so_addr, target)) {
+ format_omitting_current_func_name = true;
+ }
+ }
+
+ // If the "value" address (the target address we're symbolicating) is
+ // inside the same SymbolContext as the current instruction pc
+ // (pc_so_addr), don't print the full function name - just print it
+ // with DumpStyleNoFunctionName style, e.g. "<+36>".
+ if (format_omitting_current_func_name) {
+ value_so_addr.Dump(&ss, target, Address::DumpStyleNoFunctionName,
+ Address::DumpStyleSectionNameOffset);
+ } else {
+ value_so_addr.Dump(
+ &ss, target,
+ Address::DumpStyleResolvedDescriptionNoFunctionArguments,
+ Address::DumpStyleSectionNameOffset);
+ }
+
+ if (!ss.GetString().empty()) {
+ // If Address::Dump returned a multi-line description, most commonly
+ // seen when we have multiple levels of inlined functions at an
+ // address, only show the first line.
+ std::string str = ss.GetString();
+ size_t first_eol_char = str.find_first_of("\r\n");
+ if (first_eol_char != std::string::npos) {
+ str.erase(first_eol_char);
+ }
+ m_inst->AppendComment(str);
+ }
+ }
+ }
+ }
+
+ *type_ptr = LLVMDisassembler_ReferenceType_InOut_None;
+ *name = nullptr;
+ return nullptr;
+}
+
+// PluginInterface protocol
+ConstString DisassemblerLLVMC::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t DisassemblerLLVMC::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h b/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h
new file mode 100644
index 000000000000..fd5775056d33
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h
@@ -0,0 +1,85 @@
+//===-- DisassemblerLLVMC.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_DisassemblerLLVMC_h_
+#define liblldb_DisassemblerLLVMC_h_
+
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/PluginManager.h"
+
+class InstructionLLVMC;
+
+class DisassemblerLLVMC : public lldb_private::Disassembler {
+public:
+ DisassemblerLLVMC(const lldb_private::ArchSpec &arch,
+ const char *flavor /* = NULL */);
+
+ ~DisassemblerLLVMC() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static lldb_private::Disassembler *
+ CreateInstance(const lldb_private::ArchSpec &arch, const char *flavor);
+
+ size_t DecodeInstructions(const lldb_private::Address &base_addr,
+ const lldb_private::DataExtractor &data,
+ lldb::offset_t data_offset, size_t num_instructions,
+ bool append, bool data_from_file) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ friend class InstructionLLVMC;
+
+ bool FlavorValidForArchSpec(const lldb_private::ArchSpec &arch,
+ const char *flavor) override;
+
+ bool IsValid() const;
+
+ int OpInfo(uint64_t PC, uint64_t Offset, uint64_t Size, int TagType,
+ void *TagBug);
+
+ const char *SymbolLookup(uint64_t ReferenceValue, uint64_t *ReferenceType,
+ uint64_t ReferencePC, const char **ReferenceName);
+
+ static int OpInfoCallback(void *DisInfo, uint64_t PC, uint64_t Offset,
+ uint64_t Size, int TagType, void *TagBug);
+
+ static const char *SymbolLookupCallback(void *DisInfo,
+ uint64_t ReferenceValue,
+ uint64_t *ReferenceType,
+ uint64_t ReferencePC,
+ const char **ReferenceName);
+
+ const lldb_private::ExecutionContext *m_exe_ctx;
+ InstructionLLVMC *m_inst;
+ std::mutex m_mutex;
+ bool m_data_from_file;
+
+ // Since we need to make two actual MC Disassemblers for ARM (ARM & THUMB),
+ // and there's a bit of goo to set up and own in the MC disassembler world,
+ // this class was added to manage the actual disassemblers.
+ class MCDisasmInstance;
+ std::unique_ptr<MCDisasmInstance> m_disasm_up;
+ std::unique_ptr<MCDisasmInstance> m_alternate_disasm_up;
+};
+
+#endif // liblldb_DisassemblerLLVM_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp
new file mode 100644
index 000000000000..23c8416f4986
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp
@@ -0,0 +1,618 @@
+//===-- DynamicLoaderHexagonDYLD.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Utility/Log.h"
+
+#include "DynamicLoaderHexagonDYLD.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Aidan 21/05/2014
+//
+// Notes about hexagon dynamic loading:
+//
+// When we connect to a target we find the dyld breakpoint address. We put
+// a
+// breakpoint there with a callback 'RendezvousBreakpointHit()'.
+//
+// It is possible to find the dyld structure address from the ELF symbol
+// table,
+// but in the case of the simulator it has not been initialized before the
+// target calls dlinit().
+//
+// We can only safely parse the dyld structure after we hit the dyld
+// breakpoint
+// since at that time we know dlinit() must have been called.
+//
+
+// Find the load address of a symbol
+static lldb::addr_t findSymbolAddress(Process *proc, ConstString findName) {
+ assert(proc != nullptr);
+
+ ModuleSP module = proc->GetTarget().GetExecutableModule();
+ assert(module.get() != nullptr);
+
+ ObjectFile *exe = module->GetObjectFile();
+ assert(exe != nullptr);
+
+ lldb_private::Symtab *symtab = exe->GetSymtab();
+ assert(symtab != nullptr);
+
+ for (size_t i = 0; i < symtab->GetNumSymbols(); i++) {
+ const Symbol *sym = symtab->SymbolAtIndex(i);
+ assert(sym != nullptr);
+ ConstString symName = sym->GetName();
+
+ if (ConstString::Compare(findName, symName) == 0) {
+ Address addr = sym->GetAddress();
+ return addr.GetLoadAddress(&proc->GetTarget());
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+void DynamicLoaderHexagonDYLD::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void DynamicLoaderHexagonDYLD::Terminate() {}
+
+lldb_private::ConstString DynamicLoaderHexagonDYLD::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+lldb_private::ConstString DynamicLoaderHexagonDYLD::GetPluginNameStatic() {
+ static ConstString g_name("hexagon-dyld");
+ return g_name;
+}
+
+const char *DynamicLoaderHexagonDYLD::GetPluginDescriptionStatic() {
+ return "Dynamic loader plug-in that watches for shared library "
+ "loads/unloads in Hexagon processes.";
+}
+
+uint32_t DynamicLoaderHexagonDYLD::GetPluginVersion() { return 1; }
+
+DynamicLoader *DynamicLoaderHexagonDYLD::CreateInstance(Process *process,
+ bool force) {
+ bool create = force;
+ if (!create) {
+ const llvm::Triple &triple_ref =
+ process->GetTarget().GetArchitecture().GetTriple();
+ if (triple_ref.getArch() == llvm::Triple::hexagon)
+ create = true;
+ }
+
+ if (create)
+ return new DynamicLoaderHexagonDYLD(process);
+ return nullptr;
+}
+
+DynamicLoaderHexagonDYLD::DynamicLoaderHexagonDYLD(Process *process)
+ : DynamicLoader(process), m_rendezvous(process),
+ m_load_offset(LLDB_INVALID_ADDRESS), m_entry_point(LLDB_INVALID_ADDRESS),
+ m_dyld_bid(LLDB_INVALID_BREAK_ID) {}
+
+DynamicLoaderHexagonDYLD::~DynamicLoaderHexagonDYLD() {
+ if (m_dyld_bid != LLDB_INVALID_BREAK_ID) {
+ m_process->GetTarget().RemoveBreakpointByID(m_dyld_bid);
+ m_dyld_bid = LLDB_INVALID_BREAK_ID;
+ }
+}
+
+void DynamicLoaderHexagonDYLD::DidAttach() {
+ ModuleSP executable;
+ addr_t load_offset;
+
+ executable = GetTargetExecutable();
+
+ // Find the difference between the desired load address in the elf file and
+ // the real load address in memory
+ load_offset = ComputeLoadOffset();
+
+ // Check that there is a valid executable
+ if (executable.get() == nullptr)
+ return;
+
+ // Disable JIT for hexagon targets because its not supported
+ m_process->SetCanJIT(false);
+
+ // Enable Interpreting of function call expressions
+ m_process->SetCanInterpretFunctionCalls(true);
+
+ // Add the current executable to the module list
+ ModuleList module_list;
+ module_list.Append(executable);
+
+ // Map the loaded sections of this executable
+ if (load_offset != LLDB_INVALID_ADDRESS)
+ UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset, true);
+
+ // AD: confirm this?
+ // Load into LLDB all of the currently loaded executables in the stub
+ LoadAllCurrentModules();
+
+ // AD: confirm this?
+ // Callback for the target to give it the loaded module list
+ m_process->GetTarget().ModulesDidLoad(module_list);
+
+ // Try to set a breakpoint at the rendezvous breakpoint. DidLaunch uses
+ // ProbeEntry() instead. That sets a breakpoint, at the dyld breakpoint
+ // address, with a callback so that when hit, the dyld structure can be
+ // parsed.
+ if (!SetRendezvousBreakpoint()) {
+ // fail
+ }
+}
+
+void DynamicLoaderHexagonDYLD::DidLaunch() {}
+
+/// Checks to see if the target module has changed, updates the target
+/// accordingly and returns the target executable module.
+ModuleSP DynamicLoaderHexagonDYLD::GetTargetExecutable() {
+ Target &target = m_process->GetTarget();
+ ModuleSP executable = target.GetExecutableModule();
+
+ // There is no executable
+ if (!executable.get())
+ return executable;
+
+ // The target executable file does not exits
+ if (!FileSystem::Instance().Exists(executable->GetFileSpec()))
+ return executable;
+
+ // Prep module for loading
+ ModuleSpec module_spec(executable->GetFileSpec(),
+ executable->GetArchitecture());
+ ModuleSP module_sp(new Module(module_spec));
+
+ // Check if the executable has changed and set it to the target executable if
+ // they differ.
+ if (module_sp.get() && module_sp->GetUUID().IsValid() &&
+ executable->GetUUID().IsValid()) {
+ // if the executable has changed ??
+ if (module_sp->GetUUID() != executable->GetUUID())
+ executable.reset();
+ } else if (executable->FileHasChanged())
+ executable.reset();
+
+ if (executable.get())
+ return executable;
+
+ // TODO: What case is this code used?
+ executable = target.GetOrCreateModule(module_spec, true /* notify */);
+ if (executable.get() != target.GetExecutableModulePointer()) {
+ // Don't load dependent images since we are in dyld where we will know and
+ // find out about all images that are loaded
+ target.SetExecutableModule(executable, eLoadDependentsNo);
+ }
+
+ return executable;
+}
+
+// AD: Needs to be updated?
+Status DynamicLoaderHexagonDYLD::CanLoadImage() { return Status(); }
+
+void DynamicLoaderHexagonDYLD::UpdateLoadedSections(ModuleSP module,
+ addr_t link_map_addr,
+ addr_t base_addr,
+ bool base_addr_is_offset) {
+ Target &target = m_process->GetTarget();
+ const SectionList *sections = GetSectionListFromModule(module);
+
+ assert(sections && "SectionList missing from loaded module.");
+
+ m_loaded_modules[module] = link_map_addr;
+
+ const size_t num_sections = sections->GetSize();
+
+ for (unsigned i = 0; i < num_sections; ++i) {
+ SectionSP section_sp(sections->GetSectionAtIndex(i));
+ lldb::addr_t new_load_addr = section_sp->GetFileAddress() + base_addr;
+
+ // AD: 02/05/14
+ // since our memory map starts from address 0, we must not ignore
+ // sections that load to address 0. This violates the reference
+ // ELF spec, however is used for Hexagon.
+
+ // If the file address of the section is zero then this is not an
+ // allocatable/loadable section (property of ELF sh_addr). Skip it.
+ // if (new_load_addr == base_addr)
+ // continue;
+
+ target.SetSectionLoadAddress(section_sp, new_load_addr);
+ }
+}
+
+/// Removes the loaded sections from the target in \p module.
+///
+/// \param module The module to traverse.
+void DynamicLoaderHexagonDYLD::UnloadSections(const ModuleSP module) {
+ Target &target = m_process->GetTarget();
+ const SectionList *sections = GetSectionListFromModule(module);
+
+ assert(sections && "SectionList missing from unloaded module.");
+
+ m_loaded_modules.erase(module);
+
+ const size_t num_sections = sections->GetSize();
+ for (size_t i = 0; i < num_sections; ++i) {
+ SectionSP section_sp(sections->GetSectionAtIndex(i));
+ target.SetSectionUnloaded(section_sp);
+ }
+}
+
+// Place a breakpoint on <_rtld_debug_state>
+bool DynamicLoaderHexagonDYLD::SetRendezvousBreakpoint() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ // This is the original code, which want to look in the rendezvous structure
+ // to find the breakpoint address. Its backwards for us, since we can easily
+ // find the breakpoint address, since it is exported in our executable. We
+ // however know that we cant read the Rendezvous structure until we have hit
+ // the breakpoint once.
+ const ConstString dyldBpName("_rtld_debug_state");
+ addr_t break_addr = findSymbolAddress(m_process, dyldBpName);
+
+ Target &target = m_process->GetTarget();
+
+ // Do not try to set the breakpoint if we don't know where to put it
+ if (break_addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("Unable to locate _rtld_debug_state breakpoint address");
+
+ return false;
+ }
+
+ // Save the address of the rendezvous structure
+ m_rendezvous.SetBreakAddress(break_addr);
+
+ // If we haven't set the breakpoint before then set it
+ if (m_dyld_bid == LLDB_INVALID_BREAK_ID) {
+ Breakpoint *dyld_break =
+ target.CreateBreakpoint(break_addr, true, false).get();
+ dyld_break->SetCallback(RendezvousBreakpointHit, this, true);
+ dyld_break->SetBreakpointKind("shared-library-event");
+ m_dyld_bid = dyld_break->GetID();
+
+ // Make sure our breakpoint is at the right address.
+ assert(target.GetBreakpointByID(m_dyld_bid)
+ ->FindLocationByAddress(break_addr)
+ ->GetBreakpoint()
+ .GetID() == m_dyld_bid);
+
+ if (log && dyld_break == nullptr)
+ log->Printf("Failed to create _rtld_debug_state breakpoint");
+
+ // check we have successfully set bp
+ return (dyld_break != nullptr);
+ } else
+ // rendezvous already set
+ return true;
+}
+
+// We have just hit our breakpoint at <_rtld_debug_state>
+bool DynamicLoaderHexagonDYLD::RendezvousBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ if (log)
+ log->Printf("Rendezvous breakpoint hit!");
+
+ DynamicLoaderHexagonDYLD *dyld_instance = nullptr;
+ dyld_instance = static_cast<DynamicLoaderHexagonDYLD *>(baton);
+
+ // if the dyld_instance is still not valid then try to locate it on the
+ // symbol table
+ if (!dyld_instance->m_rendezvous.IsValid()) {
+ Process *proc = dyld_instance->m_process;
+
+ const ConstString dyldStructName("_rtld_debug");
+ addr_t structAddr = findSymbolAddress(proc, dyldStructName);
+
+ if (structAddr != LLDB_INVALID_ADDRESS) {
+ dyld_instance->m_rendezvous.SetRendezvousAddress(structAddr);
+
+ if (log)
+ log->Printf("Found _rtld_debug structure @ 0x%08" PRIx64, structAddr);
+ } else {
+ if (log)
+ log->Printf("Unable to resolve the _rtld_debug structure");
+ }
+ }
+
+ dyld_instance->RefreshModules();
+
+ // Return true to stop the target, false to just let the target run.
+ return dyld_instance->GetStopWhenImagesChange();
+}
+
+/// Helper method for RendezvousBreakpointHit. Updates LLDB's current set
+/// of loaded modules.
+void DynamicLoaderHexagonDYLD::RefreshModules() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ if (!m_rendezvous.Resolve())
+ return;
+
+ HexagonDYLDRendezvous::iterator I;
+ HexagonDYLDRendezvous::iterator E;
+
+ ModuleList &loaded_modules = m_process->GetTarget().GetImages();
+
+ if (m_rendezvous.ModulesDidLoad()) {
+ ModuleList new_modules;
+
+ E = m_rendezvous.loaded_end();
+ for (I = m_rendezvous.loaded_begin(); I != E; ++I) {
+ FileSpec file(I->path);
+ FileSystem::Instance().Resolve(file);
+ ModuleSP module_sp =
+ LoadModuleAtAddress(file, I->link_addr, I->base_addr, true);
+ if (module_sp.get()) {
+ loaded_modules.AppendIfNeeded(module_sp);
+ new_modules.Append(module_sp);
+ }
+
+ if (log) {
+ log->Printf("Target is loading '%s'", I->path.c_str());
+ if (!module_sp.get())
+ log->Printf("LLDB failed to load '%s'", I->path.c_str());
+ else
+ log->Printf("LLDB successfully loaded '%s'", I->path.c_str());
+ }
+ }
+ m_process->GetTarget().ModulesDidLoad(new_modules);
+ }
+
+ if (m_rendezvous.ModulesDidUnload()) {
+ ModuleList old_modules;
+
+ E = m_rendezvous.unloaded_end();
+ for (I = m_rendezvous.unloaded_begin(); I != E; ++I) {
+ FileSpec file(I->path);
+ FileSystem::Instance().Resolve(file);
+ ModuleSpec module_spec(file);
+ ModuleSP module_sp = loaded_modules.FindFirstModule(module_spec);
+
+ if (module_sp.get()) {
+ old_modules.Append(module_sp);
+ UnloadSections(module_sp);
+ }
+
+ if (log)
+ log->Printf("Target is unloading '%s'", I->path.c_str());
+ }
+ loaded_modules.Remove(old_modules);
+ m_process->GetTarget().ModulesDidUnload(old_modules, false);
+ }
+}
+
+// AD: This is very different to the Static Loader code.
+// It may be wise to look over this and its relation to stack
+// unwinding.
+ThreadPlanSP
+DynamicLoaderHexagonDYLD::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop) {
+ ThreadPlanSP thread_plan_sp;
+
+ StackFrame *frame = thread.GetStackFrameAtIndex(0).get();
+ const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol);
+ Symbol *sym = context.symbol;
+
+ if (sym == nullptr || !sym->IsTrampoline())
+ return thread_plan_sp;
+
+ const ConstString sym_name = sym->GetMangled().GetName(
+ lldb::eLanguageTypeUnknown, Mangled::ePreferMangled);
+ if (!sym_name)
+ return thread_plan_sp;
+
+ SymbolContextList target_symbols;
+ Target &target = thread.GetProcess()->GetTarget();
+ const ModuleList &images = target.GetImages();
+
+ images.FindSymbolsWithNameAndType(sym_name, eSymbolTypeCode, target_symbols);
+ size_t num_targets = target_symbols.GetSize();
+ if (!num_targets)
+ return thread_plan_sp;
+
+ typedef std::vector<lldb::addr_t> AddressVector;
+ AddressVector addrs;
+ for (size_t i = 0; i < num_targets; ++i) {
+ SymbolContext context;
+ AddressRange range;
+ if (target_symbols.GetContextAtIndex(i, context)) {
+ context.GetAddressRange(eSymbolContextEverything, 0, false, range);
+ lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(&target);
+ if (addr != LLDB_INVALID_ADDRESS)
+ addrs.push_back(addr);
+ }
+ }
+
+ if (addrs.size() > 0) {
+ AddressVector::iterator start = addrs.begin();
+ AddressVector::iterator end = addrs.end();
+
+ llvm::sort(start, end);
+ addrs.erase(std::unique(start, end), end);
+ thread_plan_sp =
+ std::make_shared<ThreadPlanRunToAddress>(thread, addrs, stop);
+ }
+
+ return thread_plan_sp;
+}
+
+/// Helper for the entry breakpoint callback. Resolves the load addresses
+/// of all dependent modules.
+void DynamicLoaderHexagonDYLD::LoadAllCurrentModules() {
+ HexagonDYLDRendezvous::iterator I;
+ HexagonDYLDRendezvous::iterator E;
+ ModuleList module_list;
+
+ if (!m_rendezvous.Resolve()) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf(
+ "DynamicLoaderHexagonDYLD::%s unable to resolve rendezvous address",
+ __FUNCTION__);
+ return;
+ }
+
+ // The rendezvous class doesn't enumerate the main module, so track that
+ // ourselves here.
+ ModuleSP executable = GetTargetExecutable();
+ m_loaded_modules[executable] = m_rendezvous.GetLinkMapAddress();
+
+ for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) {
+ const char *module_path = I->path.c_str();
+ FileSpec file(module_path);
+ ModuleSP module_sp =
+ LoadModuleAtAddress(file, I->link_addr, I->base_addr, true);
+ if (module_sp.get()) {
+ module_list.Append(module_sp);
+ } else {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderHexagonDYLD::%s failed loading module %s at "
+ "0x%" PRIx64,
+ __FUNCTION__, module_path, I->base_addr);
+ }
+ }
+
+ m_process->GetTarget().ModulesDidLoad(module_list);
+}
+
+/// Computes a value for m_load_offset returning the computed address on
+/// success and LLDB_INVALID_ADDRESS on failure.
+addr_t DynamicLoaderHexagonDYLD::ComputeLoadOffset() {
+ // Here we could send a GDB packet to know the load offset
+ //
+ // send: $qOffsets#4b
+ // get: Text=0;Data=0;Bss=0
+ //
+ // Currently qOffsets is not supported by pluginProcessGDBRemote
+ //
+ return 0;
+}
+
+// Here we must try to read the entry point directly from the elf header. This
+// is possible if the process is not relocatable or dynamically linked.
+//
+// an alternative is to look at the PC if we can be sure that we have connected
+// when the process is at the entry point.
+// I dont think that is reliable for us.
+addr_t DynamicLoaderHexagonDYLD::GetEntryPoint() {
+ if (m_entry_point != LLDB_INVALID_ADDRESS)
+ return m_entry_point;
+ // check we have a valid process
+ if (m_process == nullptr)
+ return LLDB_INVALID_ADDRESS;
+ // Get the current executable module
+ Module &module = *(m_process->GetTarget().GetExecutableModule().get());
+ // Get the object file (elf file) for this module
+ lldb_private::ObjectFile &object = *(module.GetObjectFile());
+ // Check if the file is executable (ie, not shared object or relocatable)
+ if (object.IsExecutable()) {
+ // Get the entry point address for this object
+ lldb_private::Address entry = object.GetEntryPointAddress();
+ // Return the entry point address
+ return entry.GetFileAddress();
+ }
+ // No idea so back out
+ return LLDB_INVALID_ADDRESS;
+}
+
+const SectionList *DynamicLoaderHexagonDYLD::GetSectionListFromModule(
+ const ModuleSP module) const {
+ SectionList *sections = nullptr;
+ if (module.get()) {
+ ObjectFile *obj_file = module->GetObjectFile();
+ if (obj_file) {
+ sections = obj_file->GetSectionList();
+ }
+ }
+ return sections;
+}
+
+static int ReadInt(Process *process, addr_t addr) {
+ Status error;
+ int value = (int)process->ReadUnsignedIntegerFromMemory(
+ addr, sizeof(uint32_t), 0, error);
+ if (error.Fail())
+ return -1;
+ else
+ return value;
+}
+
+lldb::addr_t
+DynamicLoaderHexagonDYLD::GetThreadLocalData(const lldb::ModuleSP module,
+ const lldb::ThreadSP thread,
+ lldb::addr_t tls_file_addr) {
+ auto it = m_loaded_modules.find(module);
+ if (it == m_loaded_modules.end())
+ return LLDB_INVALID_ADDRESS;
+
+ addr_t link_map = it->second;
+ if (link_map == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ const HexagonDYLDRendezvous::ThreadInfo &metadata =
+ m_rendezvous.GetThreadInfo();
+ if (!metadata.valid)
+ return LLDB_INVALID_ADDRESS;
+
+ // Get the thread pointer.
+ addr_t tp = thread->GetThreadPointer();
+ if (tp == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ // Find the module's modid.
+ int modid = ReadInt(m_process, link_map + metadata.modid_offset);
+ if (modid == -1)
+ return LLDB_INVALID_ADDRESS;
+
+ // Lookup the DTV structure for this thread.
+ addr_t dtv_ptr = tp + metadata.dtv_offset;
+ addr_t dtv = ReadPointer(dtv_ptr);
+ if (dtv == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ // Find the TLS block for this module.
+ addr_t dtv_slot = dtv + metadata.dtv_slot_size * modid;
+ addr_t tls_block = ReadPointer(dtv_slot + metadata.tls_offset);
+
+ Module *mod = module.get();
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderHexagonDYLD::Performed TLS lookup: "
+ "module=%s, link_map=0x%" PRIx64 ", tp=0x%" PRIx64
+ ", modid=%i, tls_block=0x%" PRIx64,
+ mod->GetObjectName().AsCString(""), link_map, tp, modid,
+ tls_block);
+
+ if (tls_block == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+ else
+ return tls_block + tls_file_addr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.h
new file mode 100644
index 000000000000..c171513c5499
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.h
@@ -0,0 +1,138 @@
+//===-- DynamicLoaderHexagonDYLD.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_DynamicLoaderHexagonDYLD_h_
+#define liblldb_DynamicLoaderHexagonDYLD_h_
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Target/DynamicLoader.h"
+
+#include "HexagonDYLDRendezvous.h"
+
+class DynamicLoaderHexagonDYLD : public lldb_private::DynamicLoader {
+public:
+ DynamicLoaderHexagonDYLD(lldb_private::Process *process);
+
+ ~DynamicLoaderHexagonDYLD() override;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::DynamicLoader *
+ CreateInstance(lldb_private::Process *process, bool force);
+
+ // DynamicLoader protocol
+
+ void DidAttach() override;
+
+ void DidLaunch() override;
+
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread,
+ bool stop_others) override;
+
+ lldb_private::Status CanLoadImage() override;
+
+ lldb::addr_t GetThreadLocalData(const lldb::ModuleSP module,
+ const lldb::ThreadSP thread,
+ lldb::addr_t tls_file_addr) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ /// Runtime linker rendezvous structure.
+ HexagonDYLDRendezvous m_rendezvous;
+
+ /// Virtual load address of the inferior process.
+ lldb::addr_t m_load_offset;
+
+ /// Virtual entry address of the inferior process.
+ lldb::addr_t m_entry_point;
+
+ /// Rendezvous breakpoint.
+ lldb::break_id_t m_dyld_bid;
+
+ /// Loaded module list. (link map for each module)
+ std::map<lldb::ModuleWP, lldb::addr_t, std::owner_less<lldb::ModuleWP>>
+ m_loaded_modules;
+
+ /// Enables a breakpoint on a function called by the runtime
+ /// linker each time a module is loaded or unloaded.
+ bool SetRendezvousBreakpoint();
+
+ /// Callback routine which updates the current list of loaded modules based
+ /// on the information supplied by the runtime linker.
+ static bool RendezvousBreakpointHit(
+ void *baton, lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ /// Helper method for RendezvousBreakpointHit. Updates LLDB's current set
+ /// of loaded modules.
+ void RefreshModules();
+
+ /// Updates the load address of every allocatable section in \p module.
+ ///
+ /// \param module The module to traverse.
+ ///
+ /// \param link_map_addr The virtual address of the link map for the @p
+ /// module.
+ ///
+ /// \param base_addr The virtual base address \p module is loaded at.
+ void UpdateLoadedSections(lldb::ModuleSP module, lldb::addr_t link_map_addr,
+ lldb::addr_t base_addr,
+ bool base_addr_is_offset) override;
+
+ /// Removes the loaded sections from the target in \p module.
+ ///
+ /// \param module The module to traverse.
+ void UnloadSections(const lldb::ModuleSP module) override;
+
+ /// Callback routine invoked when we hit the breakpoint on process entry.
+ ///
+ /// This routine is responsible for resolving the load addresses of all
+ /// dependent modules required by the inferior and setting up the rendezvous
+ /// breakpoint.
+ static bool
+ EntryBreakpointHit(void *baton,
+ lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ /// Helper for the entry breakpoint callback. Resolves the load addresses
+ /// of all dependent modules.
+ void LoadAllCurrentModules();
+
+ /// Computes a value for m_load_offset returning the computed address on
+ /// success and LLDB_INVALID_ADDRESS on failure.
+ lldb::addr_t ComputeLoadOffset();
+
+ /// Computes a value for m_entry_point returning the computed address on
+ /// success and LLDB_INVALID_ADDRESS on failure.
+ lldb::addr_t GetEntryPoint();
+
+ /// Checks to see if the target module has changed, updates the target
+ /// accordingly and returns the target executable module.
+ lldb::ModuleSP GetTargetExecutable();
+
+ /// return the address of the Rendezvous breakpoint
+ lldb::addr_t FindRendezvousBreakpointAddress();
+
+private:
+ const lldb_private::SectionList *
+ GetSectionListFromModule(const lldb::ModuleSP module) const;
+
+ DISALLOW_COPY_AND_ASSIGN(DynamicLoaderHexagonDYLD);
+};
+
+#endif // liblldb_DynamicLoaderHexagonDYLD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.cpp
new file mode 100644
index 000000000000..844a06c2b37d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.cpp
@@ -0,0 +1,367 @@
+//===-- HexagonDYLDRendezvous.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+
+#include "HexagonDYLDRendezvous.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+/// Locates the address of the rendezvous structure. Returns the address on
+/// success and LLDB_INVALID_ADDRESS on failure.
+static addr_t ResolveRendezvousAddress(Process *process) {
+ addr_t info_location;
+ addr_t info_addr;
+ Status error;
+
+ info_location = process->GetImageInfoAddress();
+
+ if (info_location == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ info_addr = process->ReadPointerFromMemory(info_location, error);
+ if (error.Fail())
+ return LLDB_INVALID_ADDRESS;
+
+ if (info_addr == 0)
+ return LLDB_INVALID_ADDRESS;
+
+ return info_addr;
+}
+
+HexagonDYLDRendezvous::HexagonDYLDRendezvous(Process *process)
+ : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), m_current(),
+ m_previous(), m_soentries(), m_added_soentries(), m_removed_soentries() {
+ m_thread_info.valid = false;
+
+ // Cache a copy of the executable path
+ if (m_process) {
+ Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer();
+ if (exe_mod)
+ exe_mod->GetFileSpec().GetPath(m_exe_path, PATH_MAX);
+ }
+}
+
+bool HexagonDYLDRendezvous::Resolve() {
+ const size_t word_size = 4;
+ Rendezvous info;
+ size_t address_size;
+ size_t padding;
+ addr_t info_addr;
+ addr_t cursor;
+
+ address_size = m_process->GetAddressByteSize();
+ padding = address_size - word_size;
+
+ if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
+ cursor = info_addr = ResolveRendezvousAddress(m_process);
+ else
+ cursor = info_addr = m_rendezvous_addr;
+
+ if (cursor == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (!(cursor = ReadWord(cursor, &info.version, word_size)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor + padding, &info.map_addr)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor, &info.brk)))
+ return false;
+
+ if (!(cursor = ReadWord(cursor, &info.state, word_size)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor + padding, &info.ldbase)))
+ return false;
+
+ // The rendezvous was successfully read. Update our internal state.
+ m_rendezvous_addr = info_addr;
+ m_previous = m_current;
+ m_current = info;
+
+ return UpdateSOEntries();
+}
+
+void HexagonDYLDRendezvous::SetRendezvousAddress(lldb::addr_t addr) {
+ m_rendezvous_addr = addr;
+}
+
+bool HexagonDYLDRendezvous::IsValid() {
+ return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
+}
+
+bool HexagonDYLDRendezvous::UpdateSOEntries() {
+ SOEntry entry;
+
+ if (m_current.map_addr == 0)
+ return false;
+
+ // When the previous and current states are consistent this is the first time
+ // we have been asked to update. Just take a snapshot of the currently
+ // loaded modules.
+ if (m_previous.state == eConsistent && m_current.state == eConsistent)
+ return TakeSnapshot(m_soentries);
+
+ // If we are about to add or remove a shared object clear out the current
+ // state and take a snapshot of the currently loaded images.
+ if (m_current.state == eAdd || m_current.state == eDelete) {
+ // this is a fudge so that we can clear the assert below.
+ m_previous.state = eConsistent;
+ // We hit this assert on the 2nd run of this function after running the
+ // calc example
+ assert(m_previous.state == eConsistent);
+ m_soentries.clear();
+ m_added_soentries.clear();
+ m_removed_soentries.clear();
+ return TakeSnapshot(m_soentries);
+ }
+ assert(m_current.state == eConsistent);
+
+ // Otherwise check the previous state to determine what to expect and update
+ // accordingly.
+ if (m_previous.state == eAdd)
+ return UpdateSOEntriesForAddition();
+ else if (m_previous.state == eDelete)
+ return UpdateSOEntriesForDeletion();
+
+ return false;
+}
+
+bool HexagonDYLDRendezvous::UpdateSOEntriesForAddition() {
+ SOEntry entry;
+ iterator pos;
+
+ assert(m_previous.state == eAdd);
+
+ if (m_current.map_addr == 0)
+ return false;
+
+ for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
+ if (!ReadSOEntryFromMemory(cursor, entry))
+ return false;
+
+ // Only add shared libraries and not the executable. On Linux this is
+ // indicated by an empty path in the entry. On FreeBSD it is the name of
+ // the executable.
+ if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
+ continue;
+
+ pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
+ if (pos == m_soentries.end()) {
+ m_soentries.push_back(entry);
+ m_added_soentries.push_back(entry);
+ }
+ }
+
+ return true;
+}
+
+bool HexagonDYLDRendezvous::UpdateSOEntriesForDeletion() {
+ SOEntryList entry_list;
+ iterator pos;
+
+ assert(m_previous.state == eDelete);
+
+ if (!TakeSnapshot(entry_list))
+ return false;
+
+ for (iterator I = begin(); I != end(); ++I) {
+ pos = std::find(entry_list.begin(), entry_list.end(), *I);
+ if (pos == entry_list.end())
+ m_removed_soentries.push_back(*I);
+ }
+
+ m_soentries = entry_list;
+ return true;
+}
+
+bool HexagonDYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) {
+ SOEntry entry;
+
+ if (m_current.map_addr == 0)
+ return false;
+
+ for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
+ if (!ReadSOEntryFromMemory(cursor, entry))
+ return false;
+
+ // Only add shared libraries and not the executable. On Linux this is
+ // indicated by an empty path in the entry. On FreeBSD it is the name of
+ // the executable.
+ if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
+ continue;
+
+ entry_list.push_back(entry);
+ }
+
+ return true;
+}
+
+addr_t HexagonDYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst,
+ size_t size) {
+ Status error;
+
+ *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
+ if (error.Fail())
+ return 0;
+
+ return addr + size;
+}
+
+addr_t HexagonDYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) {
+ Status error;
+
+ *dst = m_process->ReadPointerFromMemory(addr, error);
+ if (error.Fail())
+ return 0;
+
+ return addr + m_process->GetAddressByteSize();
+}
+
+std::string HexagonDYLDRendezvous::ReadStringFromMemory(addr_t addr) {
+ std::string str;
+ Status error;
+ size_t size;
+ char c;
+
+ if (addr == LLDB_INVALID_ADDRESS)
+ return std::string();
+
+ for (;;) {
+ size = m_process->DoReadMemory(addr, &c, 1, error);
+ if (size != 1 || error.Fail())
+ return std::string();
+ if (c == 0)
+ break;
+ else {
+ str.push_back(c);
+ addr++;
+ }
+ }
+
+ return str;
+}
+
+bool HexagonDYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr,
+ SOEntry &entry) {
+ entry.clear();
+ entry.link_addr = addr;
+
+ if (!(addr = ReadPointer(addr, &entry.base_addr)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.path_addr)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.next)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.prev)))
+ return false;
+
+ entry.path = ReadStringFromMemory(entry.path_addr);
+
+ return true;
+}
+
+bool HexagonDYLDRendezvous::FindMetadata(const char *name, PThreadField field,
+ uint32_t &value) {
+ Target &target = m_process->GetTarget();
+
+ SymbolContextList list;
+ if (!target.GetImages().FindSymbolsWithNameAndType(ConstString(name),
+ eSymbolTypeAny, list))
+ return false;
+
+ Address address = list[0].symbol->GetAddress();
+ addr_t addr = address.GetLoadAddress(&target);
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ Status error;
+ value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(
+ addr + field * sizeof(uint32_t), sizeof(uint32_t), 0, error);
+ if (error.Fail())
+ return false;
+
+ if (field == eSize)
+ value /= 8; // convert bits to bytes
+
+ return true;
+}
+
+const HexagonDYLDRendezvous::ThreadInfo &
+HexagonDYLDRendezvous::GetThreadInfo() {
+ if (!m_thread_info.valid) {
+ bool ok = true;
+
+ ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset,
+ m_thread_info.dtv_offset);
+ ok &=
+ FindMetadata("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
+ ok &= FindMetadata("_thread_db_link_map_l_tls_modid", eOffset,
+ m_thread_info.modid_offset);
+ ok &= FindMetadata("_thread_db_dtv_t_pointer_val", eOffset,
+ m_thread_info.tls_offset);
+
+ if (ok)
+ m_thread_info.valid = true;
+ }
+
+ return m_thread_info;
+}
+
+void HexagonDYLDRendezvous::DumpToLog(Log *log) const {
+ int state = GetState();
+
+ if (!log)
+ return;
+
+ log->PutCString("HexagonDYLDRendezvous:");
+ log->Printf(" Address: %" PRIx64, GetRendezvousAddress());
+ log->Printf(" Version: %" PRIu64, GetVersion());
+ log->Printf(" Link : %" PRIx64, GetLinkMapAddress());
+ log->Printf(" Break : %" PRIx64, GetBreakAddress());
+ log->Printf(" LDBase : %" PRIx64, GetLDBase());
+ log->Printf(" State : %s",
+ (state == eConsistent)
+ ? "consistent"
+ : (state == eAdd) ? "add" : (state == eDelete) ? "delete"
+ : "unknown");
+
+ iterator I = begin();
+ iterator E = end();
+
+ if (I != E)
+ log->PutCString("HexagonDYLDRendezvous SOEntries:");
+
+ for (int i = 1; I != E; ++I, ++i) {
+ log->Printf("\n SOEntry [%d] %s", i, I->path.c_str());
+ log->Printf(" Base : %" PRIx64, I->base_addr);
+ log->Printf(" Path : %" PRIx64, I->path_addr);
+ log->Printf(" Dyn : %" PRIx64, I->dyn_addr);
+ log->Printf(" Next : %" PRIx64, I->next);
+ log->Printf(" Prev : %" PRIx64, I->prev);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.h
new file mode 100644
index 000000000000..70fc12b7fab7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.h
@@ -0,0 +1,246 @@
+//===-- HexagonDYLDRendezvous.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_HexagonDYLDRendezvous_H_
+#define liblldb_HexagonDYLDRendezvous_H_
+
+#include <limits.h>
+#include <list>
+#include <map>
+#include <string>
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+class Process;
+}
+
+/// \class HexagonDYLDRendezvous
+/// Interface to the runtime linker.
+///
+/// A structure is present in a processes memory space which is updated by the
+/// runtime liker each time a module is loaded or unloaded. This class
+/// provides an interface to this structure and maintains a consistent
+/// snapshot of the currently loaded modules.
+class HexagonDYLDRendezvous {
+
+ // This structure is used to hold the contents of the debug rendezvous
+ // information (struct r_debug) as found in the inferiors memory. Note that
+ // the layout of this struct is not binary compatible, it is simply large
+ // enough to hold the information on both 32 and 64 bit platforms.
+ struct Rendezvous {
+ uint64_t version;
+ lldb::addr_t map_addr;
+ lldb::addr_t brk;
+ uint64_t state;
+ lldb::addr_t ldbase;
+
+ Rendezvous()
+ : version(0), map_addr(LLDB_INVALID_ADDRESS), brk(LLDB_INVALID_ADDRESS),
+ state(0), ldbase(0) {}
+ };
+
+public:
+ // Various metadata supplied by the inferior's threading library to describe
+ // the per-thread state.
+ struct ThreadInfo {
+ bool valid; // whether we read valid metadata
+ uint32_t dtv_offset; // offset of DTV pointer within pthread
+ uint32_t dtv_slot_size; // size of one DTV slot
+ uint32_t modid_offset; // offset of module ID within link_map
+ uint32_t tls_offset; // offset of TLS pointer within DTV slot
+ };
+
+ HexagonDYLDRendezvous(lldb_private::Process *process);
+
+ /// Update the internal snapshot of runtime linker rendezvous and recompute
+ /// the currently loaded modules.
+ ///
+ /// This method should be called once one start up, then once each time the
+ /// runtime linker enters the function given by GetBreakAddress().
+ ///
+ /// \returns true on success and false on failure.
+ ///
+ /// \see GetBreakAddress().
+ bool Resolve();
+
+ /// \returns true if this rendezvous has been located in the inferiors
+ /// address space and false otherwise.
+ bool IsValid();
+
+ /// \returns the address of the rendezvous structure in the inferiors
+ /// address space.
+ lldb::addr_t GetRendezvousAddress() const { return m_rendezvous_addr; }
+
+ /// Provide the dyld structure address
+ void SetRendezvousAddress(lldb::addr_t);
+
+ /// \returns the version of the rendezvous protocol being used.
+ uint64_t GetVersion() const { return m_current.version; }
+
+ /// \returns address in the inferiors address space containing the linked
+ /// list of shared object descriptors.
+ lldb::addr_t GetLinkMapAddress() const { return m_current.map_addr; }
+
+ /// A breakpoint should be set at this address and Resolve called on each
+ /// hit.
+ ///
+ /// \returns the address of a function called by the runtime linker each
+ /// time a module is loaded/unloaded, or about to be loaded/unloaded.
+ ///
+ /// \see Resolve()
+ lldb::addr_t GetBreakAddress() const { return m_current.brk; }
+
+ /// In hexagon it is possible that we can know the dyld breakpoint without
+ /// having to find it from the rendezvous structure
+ ///
+ void SetBreakAddress(lldb::addr_t addr) { m_current.brk = addr; }
+
+ /// Returns the current state of the rendezvous structure.
+ uint64_t GetState() const { return m_current.state; }
+
+ /// \returns the base address of the runtime linker in the inferiors address
+ /// space.
+ lldb::addr_t GetLDBase() const { return m_current.ldbase; }
+
+ /// \returns the thread layout metadata from the inferiors thread library.
+ const ThreadInfo &GetThreadInfo();
+
+ /// \returns true if modules have been loaded into the inferior since the
+ /// last call to Resolve().
+ bool ModulesDidLoad() const { return !m_added_soentries.empty(); }
+
+ /// \returns true if modules have been unloaded from the inferior since the
+ /// last call to Resolve().
+ bool ModulesDidUnload() const { return !m_removed_soentries.empty(); }
+
+ void DumpToLog(lldb_private::Log *log) const;
+
+ /// Constants describing the state of the rendezvous.
+ ///
+ /// \see GetState().
+ enum RendezvousState {
+ eConsistent = 0,
+ eAdd,
+ eDelete,
+ };
+
+ /// Structure representing the shared objects currently loaded into the
+ /// inferior process.
+ ///
+ /// This object is a rough analogue to the struct link_map object which
+ /// actually lives in the inferiors memory.
+ struct SOEntry {
+ lldb::addr_t link_addr; ///< Address of this link_map.
+ lldb::addr_t base_addr; ///< Base address of the loaded object.
+ lldb::addr_t path_addr; ///< String naming the shared object.
+ lldb::addr_t dyn_addr; ///< Dynamic section of shared object.
+ lldb::addr_t next; ///< Address of next so_entry.
+ lldb::addr_t prev; ///< Address of previous so_entry.
+ std::string path; ///< File name of shared object.
+
+ SOEntry() { clear(); }
+
+ bool operator==(const SOEntry &entry) { return this->path == entry.path; }
+
+ void clear() {
+ link_addr = 0;
+ base_addr = 0;
+ path_addr = 0;
+ dyn_addr = 0;
+ next = 0;
+ prev = 0;
+ path.clear();
+ }
+ };
+
+protected:
+ typedef std::list<SOEntry> SOEntryList;
+
+public:
+ typedef SOEntryList::const_iterator iterator;
+
+ /// Iterators over all currently loaded modules.
+ iterator begin() const { return m_soentries.begin(); }
+ iterator end() const { return m_soentries.end(); }
+
+ /// Iterators over all modules loaded into the inferior since the last call
+ /// to Resolve().
+ iterator loaded_begin() const { return m_added_soentries.begin(); }
+ iterator loaded_end() const { return m_added_soentries.end(); }
+
+ /// Iterators over all modules unloaded from the inferior since the last
+ /// call to Resolve().
+ iterator unloaded_begin() const { return m_removed_soentries.begin(); }
+ iterator unloaded_end() const { return m_removed_soentries.end(); }
+
+protected:
+ lldb_private::Process *m_process;
+
+ // Cached copy of executable pathname
+ char m_exe_path[PATH_MAX];
+
+ /// Location of the r_debug structure in the inferiors address space.
+ lldb::addr_t m_rendezvous_addr;
+
+ /// Current and previous snapshots of the rendezvous structure.
+ Rendezvous m_current;
+ Rendezvous m_previous;
+
+ /// List of SOEntry objects corresponding to the current link map state.
+ SOEntryList m_soentries;
+
+ /// List of SOEntry's added to the link map since the last call to
+ /// Resolve().
+ SOEntryList m_added_soentries;
+
+ /// List of SOEntry's removed from the link map since the last call to
+ /// Resolve().
+ SOEntryList m_removed_soentries;
+
+ /// Threading metadata read from the inferior.
+ ThreadInfo m_thread_info;
+
+ /// Reads an unsigned integer of \p size bytes from the inferior's address
+ /// space starting at \p addr.
+ ///
+ /// \returns addr + size if the read was successful and false otherwise.
+ lldb::addr_t ReadWord(lldb::addr_t addr, uint64_t *dst, size_t size);
+
+ /// Reads an address from the inferior's address space starting at \p addr.
+ ///
+ /// \returns addr + target address size if the read was successful and
+ /// 0 otherwise.
+ lldb::addr_t ReadPointer(lldb::addr_t addr, lldb::addr_t *dst);
+
+ /// Reads a null-terminated C string from the memory location starting at @p
+ /// addr.
+ std::string ReadStringFromMemory(lldb::addr_t addr);
+
+ /// Reads an SOEntry starting at \p addr.
+ bool ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry);
+
+ /// Updates the current set of SOEntries, the set of added entries, and the
+ /// set of removed entries.
+ bool UpdateSOEntries();
+
+ bool UpdateSOEntriesForAddition();
+
+ bool UpdateSOEntriesForDeletion();
+
+ /// Reads the current list of shared objects according to the link map
+ /// supplied by the runtime linker.
+ bool TakeSnapshot(SOEntryList &entry_list);
+
+ enum PThreadField { eSize, eNElem, eOffset };
+
+ bool FindMetadata(const char *name, PThreadField field, uint32_t &value);
+};
+
+#endif // liblldb_HexagonDYLDRendezvous_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
new file mode 100644
index 000000000000..0d736738ebb5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
@@ -0,0 +1,597 @@
+//===-- DYLDRendezvous.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/Support/Path.h"
+
+#include "DYLDRendezvous.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+/// Locates the address of the rendezvous structure. Returns the address on
+/// success and LLDB_INVALID_ADDRESS on failure.
+static addr_t ResolveRendezvousAddress(Process *process) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ addr_t info_location;
+ addr_t info_addr;
+ Status error;
+
+ if (!process) {
+ if (log)
+ log->Printf("%s null process provided", __FUNCTION__);
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ // Try to get it from our process. This might be a remote process and might
+ // grab it via some remote-specific mechanism.
+ info_location = process->GetImageInfoAddress();
+ if (log)
+ log->Printf("%s info_location = 0x%" PRIx64, __FUNCTION__, info_location);
+
+ // If the process fails to return an address, fall back to seeing if the
+ // local object file can help us find it.
+ if (info_location == LLDB_INVALID_ADDRESS) {
+ Target *target = &process->GetTarget();
+ if (target) {
+ ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
+ Address addr = obj_file->GetImageInfoAddress(target);
+
+ if (addr.IsValid()) {
+ info_location = addr.GetLoadAddress(target);
+ if (log)
+ log->Printf(
+ "%s resolved via direct object file approach to 0x%" PRIx64,
+ __FUNCTION__, info_location);
+ } else {
+ if (log)
+ log->Printf("%s FAILED - direct object file approach did not yield a "
+ "valid address",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ if (info_location == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("%s FAILED - invalid info address", __FUNCTION__);
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ if (log)
+ log->Printf("%s reading pointer (%" PRIu32 " bytes) from 0x%" PRIx64,
+ __FUNCTION__, process->GetAddressByteSize(), info_location);
+
+ info_addr = process->ReadPointerFromMemory(info_location, error);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("%s FAILED - could not read from the info location: %s",
+ __FUNCTION__, error.AsCString());
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ if (info_addr == 0) {
+ if (log)
+ log->Printf("%s FAILED - the rendezvous address contained at 0x%" PRIx64
+ " returned a null value",
+ __FUNCTION__, info_location);
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ return info_addr;
+}
+
+DYLDRendezvous::DYLDRendezvous(Process *process)
+ : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), m_current(),
+ m_previous(), m_loaded_modules(), m_soentries(), m_added_soentries(),
+ m_removed_soentries() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ m_thread_info.valid = false;
+
+ // Cache a copy of the executable path
+ if (m_process) {
+ Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer();
+ if (exe_mod) {
+ m_exe_file_spec = exe_mod->GetPlatformFileSpec();
+ if (log)
+ log->Printf("DYLDRendezvous::%s exe module executable path set: '%s'",
+ __FUNCTION__, m_exe_file_spec.GetCString());
+ } else {
+ if (log)
+ log->Printf("DYLDRendezvous::%s cannot cache exe module path: null "
+ "executable module pointer",
+ __FUNCTION__);
+ }
+ }
+}
+
+bool DYLDRendezvous::Resolve() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ const size_t word_size = 4;
+ Rendezvous info;
+ size_t address_size;
+ size_t padding;
+ addr_t info_addr;
+ addr_t cursor;
+
+ address_size = m_process->GetAddressByteSize();
+ padding = address_size - word_size;
+ if (log)
+ log->Printf("DYLDRendezvous::%s address size: %" PRIu64
+ ", padding %" PRIu64,
+ __FUNCTION__, uint64_t(address_size), uint64_t(padding));
+
+ if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
+ cursor = info_addr = ResolveRendezvousAddress(m_process);
+ else
+ cursor = info_addr = m_rendezvous_addr;
+ if (log)
+ log->Printf("DYLDRendezvous::%s cursor = 0x%" PRIx64, __FUNCTION__, cursor);
+
+ if (cursor == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (!(cursor = ReadWord(cursor, &info.version, word_size)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor + padding, &info.map_addr)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor, &info.brk)))
+ return false;
+
+ if (!(cursor = ReadWord(cursor, &info.state, word_size)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor + padding, &info.ldbase)))
+ return false;
+
+ // The rendezvous was successfully read. Update our internal state.
+ m_rendezvous_addr = info_addr;
+ m_previous = m_current;
+ m_current = info;
+
+ if (UpdateSOEntries(true))
+ return true;
+
+ return UpdateSOEntries();
+}
+
+bool DYLDRendezvous::IsValid() {
+ return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
+}
+
+bool DYLDRendezvous::UpdateSOEntries(bool fromRemote) {
+ SOEntry entry;
+ LoadedModuleInfoList module_list;
+
+ // If we can't get the SO info from the remote, return failure.
+ if (fromRemote && m_process->LoadModules(module_list) == 0)
+ return false;
+
+ if (!fromRemote && m_current.map_addr == 0)
+ return false;
+
+ // When the previous and current states are consistent this is the first time
+ // we have been asked to update. Just take a snapshot of the currently
+ // loaded modules.
+ if (m_previous.state == eConsistent && m_current.state == eConsistent)
+ return fromRemote ? SaveSOEntriesFromRemote(module_list)
+ : TakeSnapshot(m_soentries);
+
+ // If we are about to add or remove a shared object clear out the current
+ // state and take a snapshot of the currently loaded images.
+ if (m_current.state == eAdd || m_current.state == eDelete) {
+ // Some versions of the android dynamic linker might send two notifications
+ // with state == eAdd back to back. Ignore them until we get an eConsistent
+ // notification.
+ if (!(m_previous.state == eConsistent ||
+ (m_previous.state == eAdd && m_current.state == eDelete)))
+ return false;
+
+ m_soentries.clear();
+ if (fromRemote)
+ return SaveSOEntriesFromRemote(module_list);
+
+ m_added_soentries.clear();
+ m_removed_soentries.clear();
+ return TakeSnapshot(m_soentries);
+ }
+ assert(m_current.state == eConsistent);
+
+ // Otherwise check the previous state to determine what to expect and update
+ // accordingly.
+ if (m_previous.state == eAdd)
+ return fromRemote ? AddSOEntriesFromRemote(module_list) : AddSOEntries();
+ else if (m_previous.state == eDelete)
+ return fromRemote ? RemoveSOEntriesFromRemote(module_list)
+ : RemoveSOEntries();
+
+ return false;
+}
+
+bool DYLDRendezvous::FillSOEntryFromModuleInfo(
+ LoadedModuleInfoList::LoadedModuleInfo const &modInfo, SOEntry &entry) {
+ addr_t link_map_addr;
+ addr_t base_addr;
+ addr_t dyn_addr;
+ std::string name;
+
+ if (!modInfo.get_link_map(link_map_addr) || !modInfo.get_base(base_addr) ||
+ !modInfo.get_dynamic(dyn_addr) || !modInfo.get_name(name))
+ return false;
+
+ entry.link_addr = link_map_addr;
+ entry.base_addr = base_addr;
+ entry.dyn_addr = dyn_addr;
+
+ entry.file_spec.SetFile(name, FileSpec::Style::native);
+
+ UpdateBaseAddrIfNecessary(entry, name);
+
+ // not needed if we're using ModuleInfos
+ entry.next = 0;
+ entry.prev = 0;
+ entry.path_addr = 0;
+
+ return true;
+}
+
+bool DYLDRendezvous::SaveSOEntriesFromRemote(
+ LoadedModuleInfoList &module_list) {
+ for (auto const &modInfo : module_list.m_list) {
+ SOEntry entry;
+ if (!FillSOEntryFromModuleInfo(modInfo, entry))
+ return false;
+
+ // Only add shared libraries and not the executable.
+ if (!SOEntryIsMainExecutable(entry))
+ m_soentries.push_back(entry);
+ }
+
+ m_loaded_modules = module_list;
+ return true;
+}
+
+bool DYLDRendezvous::AddSOEntriesFromRemote(LoadedModuleInfoList &module_list) {
+ for (auto const &modInfo : module_list.m_list) {
+ bool found = false;
+ for (auto const &existing : m_loaded_modules.m_list) {
+ if (modInfo == existing) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+
+ SOEntry entry;
+ if (!FillSOEntryFromModuleInfo(modInfo, entry))
+ return false;
+
+ // Only add shared libraries and not the executable.
+ if (!SOEntryIsMainExecutable(entry))
+ m_soentries.push_back(entry);
+ }
+
+ m_loaded_modules = module_list;
+ return true;
+}
+
+bool DYLDRendezvous::RemoveSOEntriesFromRemote(
+ LoadedModuleInfoList &module_list) {
+ for (auto const &existing : m_loaded_modules.m_list) {
+ bool found = false;
+ for (auto const &modInfo : module_list.m_list) {
+ if (modInfo == existing) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+
+ SOEntry entry;
+ if (!FillSOEntryFromModuleInfo(existing, entry))
+ return false;
+
+ // Only add shared libraries and not the executable.
+ if (!SOEntryIsMainExecutable(entry)) {
+ auto pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
+ if (pos == m_soentries.end())
+ return false;
+
+ m_soentries.erase(pos);
+ }
+ }
+
+ m_loaded_modules = module_list;
+ return true;
+}
+
+bool DYLDRendezvous::AddSOEntries() {
+ SOEntry entry;
+ iterator pos;
+
+ assert(m_previous.state == eAdd);
+
+ if (m_current.map_addr == 0)
+ return false;
+
+ for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
+ if (!ReadSOEntryFromMemory(cursor, entry))
+ return false;
+
+ // Only add shared libraries and not the executable.
+ if (SOEntryIsMainExecutable(entry))
+ continue;
+
+ pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
+ if (pos == m_soentries.end()) {
+ m_soentries.push_back(entry);
+ m_added_soentries.push_back(entry);
+ }
+ }
+
+ return true;
+}
+
+bool DYLDRendezvous::RemoveSOEntries() {
+ SOEntryList entry_list;
+ iterator pos;
+
+ assert(m_previous.state == eDelete);
+
+ if (!TakeSnapshot(entry_list))
+ return false;
+
+ for (iterator I = begin(); I != end(); ++I) {
+ pos = std::find(entry_list.begin(), entry_list.end(), *I);
+ if (pos == entry_list.end())
+ m_removed_soentries.push_back(*I);
+ }
+
+ m_soentries = entry_list;
+ return true;
+}
+
+bool DYLDRendezvous::SOEntryIsMainExecutable(const SOEntry &entry) {
+ // On some systes the executable is indicated by an empty path in the entry.
+ // On others it is the full path to the executable.
+
+ auto triple = m_process->GetTarget().GetArchitecture().GetTriple();
+ switch (triple.getOS()) {
+ case llvm::Triple::FreeBSD:
+ case llvm::Triple::NetBSD:
+ return entry.file_spec == m_exe_file_spec;
+ case llvm::Triple::Linux:
+ if (triple.isAndroid())
+ return entry.file_spec == m_exe_file_spec;
+ return !entry.file_spec;
+ default:
+ return false;
+ }
+}
+
+bool DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) {
+ SOEntry entry;
+
+ if (m_current.map_addr == 0)
+ return false;
+
+ // Clear previous entries since we are about to obtain an up to date list.
+ entry_list.clear();
+
+ for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
+ if (!ReadSOEntryFromMemory(cursor, entry))
+ return false;
+
+ // Only add shared libraries and not the executable.
+ if (SOEntryIsMainExecutable(entry))
+ continue;
+
+ entry_list.push_back(entry);
+ }
+
+ return true;
+}
+
+addr_t DYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst, size_t size) {
+ Status error;
+
+ *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
+ if (error.Fail())
+ return 0;
+
+ return addr + size;
+}
+
+addr_t DYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) {
+ Status error;
+
+ *dst = m_process->ReadPointerFromMemory(addr, error);
+ if (error.Fail())
+ return 0;
+
+ return addr + m_process->GetAddressByteSize();
+}
+
+std::string DYLDRendezvous::ReadStringFromMemory(addr_t addr) {
+ std::string str;
+ Status error;
+
+ if (addr == LLDB_INVALID_ADDRESS)
+ return std::string();
+
+ m_process->ReadCStringFromMemory(addr, str, error);
+
+ return str;
+}
+
+// Returns true if the load bias reported by the linker is incorrect for the
+// given entry. This function is used to handle cases where we want to work
+// around a bug in the system linker.
+static bool isLoadBiasIncorrect(Target &target, const std::string &file_path) {
+ // On Android L (API 21, 22) the load address of the "/system/bin/linker"
+ // isn't filled in correctly.
+ unsigned os_major = target.GetPlatform()->GetOSVersion().getMajor();
+ return target.GetArchitecture().GetTriple().isAndroid() &&
+ (os_major == 21 || os_major == 22) &&
+ (file_path == "/system/bin/linker" ||
+ file_path == "/system/bin/linker64");
+}
+
+void DYLDRendezvous::UpdateBaseAddrIfNecessary(SOEntry &entry,
+ std::string const &file_path) {
+ // If the load bias reported by the linker is incorrect then fetch the load
+ // address of the file from the proc file system.
+ if (isLoadBiasIncorrect(m_process->GetTarget(), file_path)) {
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ bool is_loaded = false;
+ Status error =
+ m_process->GetFileLoadAddress(entry.file_spec, is_loaded, load_addr);
+ if (error.Success() && is_loaded)
+ entry.base_addr = load_addr;
+ }
+}
+
+bool DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) {
+ entry.clear();
+
+ entry.link_addr = addr;
+
+ if (!(addr = ReadPointer(addr, &entry.base_addr)))
+ return false;
+
+ // mips adds an extra load offset field to the link map struct on FreeBSD and
+ // NetBSD (need to validate other OSes).
+ // http://svnweb.freebsd.org/base/head/sys/sys/link_elf.h?revision=217153&view=markup#l57
+ const ArchSpec &arch = m_process->GetTarget().GetArchitecture();
+ if ((arch.GetTriple().getOS() == llvm::Triple::FreeBSD ||
+ arch.GetTriple().getOS() == llvm::Triple::NetBSD) &&
+ arch.IsMIPS()) {
+ addr_t mips_l_offs;
+ if (!(addr = ReadPointer(addr, &mips_l_offs)))
+ return false;
+ if (mips_l_offs != 0 && mips_l_offs != entry.base_addr)
+ return false;
+ }
+
+ if (!(addr = ReadPointer(addr, &entry.path_addr)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.next)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.prev)))
+ return false;
+
+ std::string file_path = ReadStringFromMemory(entry.path_addr);
+ entry.file_spec.SetFile(file_path, FileSpec::Style::native);
+
+ UpdateBaseAddrIfNecessary(entry, file_path);
+
+ return true;
+}
+
+bool DYLDRendezvous::FindMetadata(const char *name, PThreadField field,
+ uint32_t &value) {
+ Target &target = m_process->GetTarget();
+
+ SymbolContextList list;
+ if (!target.GetImages().FindSymbolsWithNameAndType(ConstString(name),
+ eSymbolTypeAny, list))
+ return false;
+
+ Address address = list[0].symbol->GetAddress();
+ addr_t addr = address.GetLoadAddress(&target);
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ Status error;
+ value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(
+ addr + field * sizeof(uint32_t), sizeof(uint32_t), 0, error);
+ if (error.Fail())
+ return false;
+
+ if (field == eSize)
+ value /= 8; // convert bits to bytes
+
+ return true;
+}
+
+const DYLDRendezvous::ThreadInfo &DYLDRendezvous::GetThreadInfo() {
+ if (!m_thread_info.valid) {
+ bool ok = true;
+
+ ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset,
+ m_thread_info.dtv_offset);
+ ok &=
+ FindMetadata("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
+ ok &= FindMetadata("_thread_db_link_map_l_tls_modid", eOffset,
+ m_thread_info.modid_offset);
+ ok &= FindMetadata("_thread_db_dtv_t_pointer_val", eOffset,
+ m_thread_info.tls_offset);
+
+ if (ok)
+ m_thread_info.valid = true;
+ }
+
+ return m_thread_info;
+}
+
+void DYLDRendezvous::DumpToLog(Log *log) const {
+ int state = GetState();
+
+ if (!log)
+ return;
+
+ log->PutCString("DYLDRendezvous:");
+ log->Printf(" Address: %" PRIx64, GetRendezvousAddress());
+ log->Printf(" Version: %" PRIu64, GetVersion());
+ log->Printf(" Link : %" PRIx64, GetLinkMapAddress());
+ log->Printf(" Break : %" PRIx64, GetBreakAddress());
+ log->Printf(" LDBase : %" PRIx64, GetLDBase());
+ log->Printf(" State : %s",
+ (state == eConsistent)
+ ? "consistent"
+ : (state == eAdd) ? "add" : (state == eDelete) ? "delete"
+ : "unknown");
+
+ iterator I = begin();
+ iterator E = end();
+
+ if (I != E)
+ log->PutCString("DYLDRendezvous SOEntries:");
+
+ for (int i = 1; I != E; ++I, ++i) {
+ log->Printf("\n SOEntry [%d] %s", i, I->file_spec.GetCString());
+ log->Printf(" Base : %" PRIx64, I->base_addr);
+ log->Printf(" Path : %" PRIx64, I->path_addr);
+ log->Printf(" Dyn : %" PRIx64, I->dyn_addr);
+ log->Printf(" Next : %" PRIx64, I->next);
+ log->Printf(" Prev : %" PRIx64, I->prev);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
new file mode 100644
index 000000000000..993e62f5e9f9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
@@ -0,0 +1,253 @@
+//===-- DYLDRendezvous.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_Rendezvous_H_
+#define liblldb_Rendezvous_H_
+
+#include <list>
+#include <string>
+
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+#include "lldb/Core/LoadedModuleInfoList.h"
+
+using lldb_private::LoadedModuleInfoList;
+
+namespace lldb_private {
+class Process;
+}
+
+/// \class DYLDRendezvous
+/// Interface to the runtime linker.
+///
+/// A structure is present in a processes memory space which is updated by the
+/// runtime liker each time a module is loaded or unloaded. This class
+/// provides an interface to this structure and maintains a consistent
+/// snapshot of the currently loaded modules.
+class DYLDRendezvous {
+
+ // This structure is used to hold the contents of the debug rendezvous
+ // information (struct r_debug) as found in the inferiors memory. Note that
+ // the layout of this struct is not binary compatible, it is simply large
+ // enough to hold the information on both 32 and 64 bit platforms.
+ struct Rendezvous {
+ uint64_t version;
+ lldb::addr_t map_addr;
+ lldb::addr_t brk;
+ uint64_t state;
+ lldb::addr_t ldbase;
+
+ Rendezvous() : version(0), map_addr(0), brk(0), state(0), ldbase(0) {}
+ };
+
+public:
+ // Various metadata supplied by the inferior's threading library to describe
+ // the per-thread state.
+ struct ThreadInfo {
+ bool valid; // whether we read valid metadata
+ uint32_t dtv_offset; // offset of DTV pointer within pthread
+ uint32_t dtv_slot_size; // size of one DTV slot
+ uint32_t modid_offset; // offset of module ID within link_map
+ uint32_t tls_offset; // offset of TLS pointer within DTV slot
+ };
+
+ DYLDRendezvous(lldb_private::Process *process);
+
+ /// Update the internal snapshot of runtime linker rendezvous and recompute
+ /// the currently loaded modules.
+ ///
+ /// This method should be called once one start up, then once each time the
+ /// runtime linker enters the function given by GetBreakAddress().
+ ///
+ /// \returns true on success and false on failure.
+ ///
+ /// \see GetBreakAddress().
+ bool Resolve();
+
+ /// \returns true if this rendezvous has been located in the inferiors
+ /// address space and false otherwise.
+ bool IsValid();
+
+ /// \returns the address of the rendezvous structure in the inferiors
+ /// address space.
+ lldb::addr_t GetRendezvousAddress() const { return m_rendezvous_addr; }
+
+ /// \returns the version of the rendezvous protocol being used.
+ uint64_t GetVersion() const { return m_current.version; }
+
+ /// \returns address in the inferiors address space containing the linked
+ /// list of shared object descriptors.
+ lldb::addr_t GetLinkMapAddress() const { return m_current.map_addr; }
+
+ /// A breakpoint should be set at this address and Resolve called on each
+ /// hit.
+ ///
+ /// \returns the address of a function called by the runtime linker each
+ /// time a module is loaded/unloaded, or about to be loaded/unloaded.
+ ///
+ /// \see Resolve()
+ lldb::addr_t GetBreakAddress() const { return m_current.brk; }
+
+ /// Returns the current state of the rendezvous structure.
+ uint64_t GetState() const { return m_current.state; }
+
+ /// \returns the base address of the runtime linker in the inferiors address
+ /// space.
+ lldb::addr_t GetLDBase() const { return m_current.ldbase; }
+
+ /// \returns the thread layout metadata from the inferiors thread library.
+ const ThreadInfo &GetThreadInfo();
+
+ /// \returns true if modules have been loaded into the inferior since the
+ /// last call to Resolve().
+ bool ModulesDidLoad() const { return !m_added_soentries.empty(); }
+
+ /// \returns true if modules have been unloaded from the inferior since the
+ /// last call to Resolve().
+ bool ModulesDidUnload() const { return !m_removed_soentries.empty(); }
+
+ void DumpToLog(lldb_private::Log *log) const;
+
+ /// Constants describing the state of the rendezvous.
+ ///
+ /// \see GetState().
+ enum RendezvousState { eConsistent, eAdd, eDelete };
+
+ /// Structure representing the shared objects currently loaded into the
+ /// inferior process.
+ ///
+ /// This object is a rough analogue to the struct link_map object which
+ /// actually lives in the inferiors memory.
+ struct SOEntry {
+ lldb::addr_t link_addr; ///< Address of this link_map.
+ lldb::addr_t base_addr; ///< Base address of the loaded object.
+ lldb::addr_t path_addr; ///< String naming the shared object.
+ lldb::addr_t dyn_addr; ///< Dynamic section of shared object.
+ lldb::addr_t next; ///< Address of next so_entry.
+ lldb::addr_t prev; ///< Address of previous so_entry.
+ lldb_private::FileSpec file_spec; ///< File spec of shared object.
+
+ SOEntry() { clear(); }
+
+ bool operator==(const SOEntry &entry) {
+ return file_spec == entry.file_spec;
+ }
+
+ void clear() {
+ link_addr = 0;
+ base_addr = 0;
+ path_addr = 0;
+ dyn_addr = 0;
+ next = 0;
+ prev = 0;
+ file_spec.Clear();
+ }
+ };
+
+protected:
+ typedef std::list<SOEntry> SOEntryList;
+
+public:
+ typedef SOEntryList::const_iterator iterator;
+
+ /// Iterators over all currently loaded modules.
+ iterator begin() const { return m_soentries.begin(); }
+ iterator end() const { return m_soentries.end(); }
+
+ /// Iterators over all modules loaded into the inferior since the last call
+ /// to Resolve().
+ iterator loaded_begin() const { return m_added_soentries.begin(); }
+ iterator loaded_end() const { return m_added_soentries.end(); }
+
+ /// Iterators over all modules unloaded from the inferior since the last
+ /// call to Resolve().
+ iterator unloaded_begin() const { return m_removed_soentries.begin(); }
+ iterator unloaded_end() const { return m_removed_soentries.end(); }
+
+protected:
+ lldb_private::Process *m_process;
+
+ // Cached copy of executable file spec
+ lldb_private::FileSpec m_exe_file_spec;
+
+ /// Location of the r_debug structure in the inferiors address space.
+ lldb::addr_t m_rendezvous_addr;
+
+ /// Current and previous snapshots of the rendezvous structure.
+ Rendezvous m_current;
+ Rendezvous m_previous;
+
+ /// List of currently loaded SO modules
+ LoadedModuleInfoList m_loaded_modules;
+
+ /// List of SOEntry objects corresponding to the current link map state.
+ SOEntryList m_soentries;
+
+ /// List of SOEntry's added to the link map since the last call to
+ /// Resolve().
+ SOEntryList m_added_soentries;
+
+ /// List of SOEntry's removed from the link map since the last call to
+ /// Resolve().
+ SOEntryList m_removed_soentries;
+
+ /// Threading metadata read from the inferior.
+ ThreadInfo m_thread_info;
+
+ /// Reads an unsigned integer of \p size bytes from the inferior's address
+ /// space starting at \p addr.
+ ///
+ /// \returns addr + size if the read was successful and false otherwise.
+ lldb::addr_t ReadWord(lldb::addr_t addr, uint64_t *dst, size_t size);
+
+ /// Reads an address from the inferior's address space starting at \p addr.
+ ///
+ /// \returns addr + target address size if the read was successful and
+ /// 0 otherwise.
+ lldb::addr_t ReadPointer(lldb::addr_t addr, lldb::addr_t *dst);
+
+ /// Reads a null-terminated C string from the memory location starting at @p
+ /// addr.
+ std::string ReadStringFromMemory(lldb::addr_t addr);
+
+ /// Reads an SOEntry starting at \p addr.
+ bool ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry);
+
+ /// Updates the current set of SOEntries, the set of added entries, and the
+ /// set of removed entries.
+ bool UpdateSOEntries(bool fromRemote = false);
+
+ bool FillSOEntryFromModuleInfo(
+ LoadedModuleInfoList::LoadedModuleInfo const &modInfo, SOEntry &entry);
+
+ bool SaveSOEntriesFromRemote(LoadedModuleInfoList &module_list);
+
+ bool AddSOEntriesFromRemote(LoadedModuleInfoList &module_list);
+
+ bool RemoveSOEntriesFromRemote(LoadedModuleInfoList &module_list);
+
+ bool AddSOEntries();
+
+ bool RemoveSOEntries();
+
+ void UpdateBaseAddrIfNecessary(SOEntry &entry, std::string const &file_path);
+
+ bool SOEntryIsMainExecutable(const SOEntry &entry);
+
+ /// Reads the current list of shared objects according to the link map
+ /// supplied by the runtime linker.
+ bool TakeSnapshot(SOEntryList &entry_list);
+
+ enum PThreadField { eSize, eNElem, eOffset };
+
+ bool FindMetadata(const char *name, PThreadField field, uint32_t &value);
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
new file mode 100644
index 000000000000..b55660899d0d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
@@ -0,0 +1,773 @@
+//===-- DynamicLoaderPOSIXDYLD.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Main header include
+#include "DynamicLoaderPOSIXDYLD.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/ProcessInfo.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+void DynamicLoaderPOSIXDYLD::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void DynamicLoaderPOSIXDYLD::Terminate() {}
+
+lldb_private::ConstString DynamicLoaderPOSIXDYLD::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+lldb_private::ConstString DynamicLoaderPOSIXDYLD::GetPluginNameStatic() {
+ static ConstString g_name("linux-dyld");
+ return g_name;
+}
+
+const char *DynamicLoaderPOSIXDYLD::GetPluginDescriptionStatic() {
+ return "Dynamic loader plug-in that watches for shared library "
+ "loads/unloads in POSIX processes.";
+}
+
+uint32_t DynamicLoaderPOSIXDYLD::GetPluginVersion() { return 1; }
+
+DynamicLoader *DynamicLoaderPOSIXDYLD::CreateInstance(Process *process,
+ bool force) {
+ bool create = force;
+ if (!create) {
+ const llvm::Triple &triple_ref =
+ process->GetTarget().GetArchitecture().GetTriple();
+ if (triple_ref.getOS() == llvm::Triple::FreeBSD ||
+ triple_ref.getOS() == llvm::Triple::Linux ||
+ triple_ref.getOS() == llvm::Triple::NetBSD)
+ create = true;
+ }
+
+ if (create)
+ return new DynamicLoaderPOSIXDYLD(process);
+ return nullptr;
+}
+
+DynamicLoaderPOSIXDYLD::DynamicLoaderPOSIXDYLD(Process *process)
+ : DynamicLoader(process), m_rendezvous(process),
+ m_load_offset(LLDB_INVALID_ADDRESS), m_entry_point(LLDB_INVALID_ADDRESS),
+ m_auxv(), m_dyld_bid(LLDB_INVALID_BREAK_ID),
+ m_vdso_base(LLDB_INVALID_ADDRESS),
+ m_interpreter_base(LLDB_INVALID_ADDRESS) {}
+
+DynamicLoaderPOSIXDYLD::~DynamicLoaderPOSIXDYLD() {
+ if (m_dyld_bid != LLDB_INVALID_BREAK_ID) {
+ m_process->GetTarget().RemoveBreakpointByID(m_dyld_bid);
+ m_dyld_bid = LLDB_INVALID_BREAK_ID;
+ }
+}
+
+void DynamicLoaderPOSIXDYLD::DidAttach() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s() pid %" PRIu64, __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+ m_auxv = llvm::make_unique<AuxVector>(m_process->GetAuxvData());
+
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " reloaded auxv data",
+ __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+
+ // ask the process if it can load any of its own modules
+ m_process->LoadModules();
+
+ ModuleSP executable_sp = GetTargetExecutable();
+ ResolveExecutableModule(executable_sp);
+
+ // find the main process load offset
+ addr_t load_offset = ComputeLoadOffset();
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " executable '%s', load_offset 0x%" PRIx64,
+ __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID,
+ executable_sp ? executable_sp->GetFileSpec().GetPath().c_str()
+ : "<null executable>",
+ load_offset);
+
+ EvalSpecialModulesStatus();
+
+ // if we dont have a load address we cant re-base
+ bool rebase_exec = load_offset != LLDB_INVALID_ADDRESS;
+
+ // if we have a valid executable
+ if (executable_sp.get()) {
+ lldb_private::ObjectFile *obj = executable_sp->GetObjectFile();
+ if (obj) {
+ // don't rebase if the module already has a load address
+ Target &target = m_process->GetTarget();
+ Address addr = obj->GetImageInfoAddress(&target);
+ if (addr.GetLoadAddress(&target) != LLDB_INVALID_ADDRESS)
+ rebase_exec = false;
+ }
+ } else {
+ // no executable, nothing to re-base
+ rebase_exec = false;
+ }
+
+ // if the target executable should be re-based
+ if (rebase_exec) {
+ ModuleList module_list;
+
+ module_list.Append(executable_sp);
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " added executable '%s' to module load list",
+ __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID,
+ executable_sp->GetFileSpec().GetPath().c_str());
+
+ UpdateLoadedSections(executable_sp, LLDB_INVALID_ADDRESS, load_offset,
+ true);
+
+ LoadAllCurrentModules();
+
+ m_process->GetTarget().ModulesDidLoad(module_list);
+ if (log) {
+ log->Printf("DynamicLoaderPOSIXDYLD::%s told the target about the "
+ "modules that loaded:",
+ __FUNCTION__);
+ for (auto module_sp : module_list.Modules()) {
+ log->Printf("-- [module] %s (pid %" PRIu64 ")",
+ module_sp ? module_sp->GetFileSpec().GetPath().c_str()
+ : "<null>",
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+ }
+ }
+ }
+
+ if (executable_sp.get()) {
+ if (!SetRendezvousBreakpoint()) {
+ // If we cannot establish rendezvous breakpoint right now we'll try again
+ // at entry point.
+ ProbeEntry();
+ }
+ }
+}
+
+void DynamicLoaderPOSIXDYLD::DidLaunch() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s()", __FUNCTION__);
+
+ ModuleSP executable;
+ addr_t load_offset;
+
+ m_auxv = llvm::make_unique<AuxVector>(m_process->GetAuxvData());
+
+ executable = GetTargetExecutable();
+ load_offset = ComputeLoadOffset();
+ EvalSpecialModulesStatus();
+
+ if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) {
+ ModuleList module_list;
+ module_list.Append(executable);
+ UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset, true);
+
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s about to call ProbeEntry()",
+ __FUNCTION__);
+
+ if (!SetRendezvousBreakpoint()) {
+ // If we cannot establish rendezvous breakpoint right now we'll try again
+ // at entry point.
+ ProbeEntry();
+ }
+
+ LoadVDSO();
+ m_process->GetTarget().ModulesDidLoad(module_list);
+ }
+}
+
+Status DynamicLoaderPOSIXDYLD::CanLoadImage() { return Status(); }
+
+void DynamicLoaderPOSIXDYLD::UpdateLoadedSections(ModuleSP module,
+ addr_t link_map_addr,
+ addr_t base_addr,
+ bool base_addr_is_offset) {
+ m_loaded_modules[module] = link_map_addr;
+ UpdateLoadedSectionsCommon(module, base_addr, base_addr_is_offset);
+}
+
+void DynamicLoaderPOSIXDYLD::UnloadSections(const ModuleSP module) {
+ m_loaded_modules.erase(module);
+
+ UnloadSectionsCommon(module);
+}
+
+void DynamicLoaderPOSIXDYLD::ProbeEntry() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ const addr_t entry = GetEntryPoint();
+ if (entry == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf(
+ "DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " GetEntryPoint() returned no address, not setting entry breakpoint",
+ __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+ return;
+ }
+
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " GetEntryPoint() returned address 0x%" PRIx64
+ ", setting entry breakpoint",
+ __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID,
+ entry);
+
+ if (m_process) {
+ Breakpoint *const entry_break =
+ m_process->GetTarget().CreateBreakpoint(entry, true, false).get();
+ entry_break->SetCallback(EntryBreakpointHit, this, true);
+ entry_break->SetBreakpointKind("shared-library-event");
+
+ // Shoudn't hit this more than once.
+ entry_break->SetOneShot(true);
+ }
+}
+
+// The runtime linker has run and initialized the rendezvous structure once the
+// process has hit its entry point. When we hit the corresponding breakpoint
+// we interrogate the rendezvous structure to get the load addresses of all
+// dependent modules for the process. Similarly, we can discover the runtime
+// linker function and setup a breakpoint to notify us of any dynamically
+// loaded modules (via dlopen).
+bool DynamicLoaderPOSIXDYLD::EntryBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ DynamicLoaderPOSIXDYLD *const dyld_instance =
+ static_cast<DynamicLoaderPOSIXDYLD *>(baton);
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s called for pid %" PRIu64,
+ __FUNCTION__,
+ dyld_instance->m_process ? dyld_instance->m_process->GetID()
+ : LLDB_INVALID_PROCESS_ID);
+
+ // Disable the breakpoint --- if a stop happens right after this, which we've
+ // seen on occasion, we don't want the breakpoint stepping thread-plan logic
+ // to show a breakpoint instruction at the disassembled entry point to the
+ // program. Disabling it prevents it. (One-shot is not enough - one-shot
+ // removal logic only happens after the breakpoint goes public, which wasn't
+ // happening in our scenario).
+ if (dyld_instance->m_process) {
+ BreakpointSP breakpoint_sp =
+ dyld_instance->m_process->GetTarget().GetBreakpointByID(break_id);
+ if (breakpoint_sp) {
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " disabling breakpoint id %" PRIu64,
+ __FUNCTION__, dyld_instance->m_process->GetID(), break_id);
+ breakpoint_sp->SetEnabled(false);
+ } else {
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " failed to find breakpoint for breakpoint id %" PRIu64,
+ __FUNCTION__, dyld_instance->m_process->GetID(), break_id);
+ }
+ } else {
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s breakpoint id %" PRIu64
+ " no Process instance! Cannot disable breakpoint",
+ __FUNCTION__, break_id);
+ }
+
+ dyld_instance->LoadAllCurrentModules();
+ dyld_instance->SetRendezvousBreakpoint();
+ return false; // Continue running.
+}
+
+bool DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (m_dyld_bid != LLDB_INVALID_BREAK_ID) {
+ LLDB_LOG(log,
+ "Rendezvous breakpoint breakpoint id {0} for pid {1}"
+ "is already set.",
+ m_dyld_bid,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+ return true;
+ }
+
+ addr_t break_addr;
+ Target &target = m_process->GetTarget();
+ BreakpointSP dyld_break;
+ if (m_rendezvous.IsValid()) {
+ break_addr = m_rendezvous.GetBreakAddress();
+ LLDB_LOG(log, "Setting rendezvous break address for pid {0} at {1:x}",
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID,
+ break_addr);
+ dyld_break = target.CreateBreakpoint(break_addr, true, false);
+ } else {
+ LLDB_LOG(log, "Rendezvous structure is not set up yet. "
+ "Trying to locate rendezvous breakpoint in the interpreter "
+ "by symbol name.");
+ ModuleSP interpreter = LoadInterpreterModule();
+ if (!interpreter) {
+ LLDB_LOG(log, "Can't find interpreter, rendezvous breakpoint isn't set.");
+ return false;
+ }
+
+ // Function names from different dynamic loaders that are known to be used
+ // as rendezvous between the loader and debuggers.
+ static std::vector<std::string> DebugStateCandidates{
+ "_dl_debug_state", "rtld_db_dlactivity", "__dl_rtld_db_dlactivity",
+ "r_debug_state", "_r_debug_state", "_rtld_debug_state",
+ };
+
+ FileSpecList containingModules;
+ containingModules.Append(interpreter->GetFileSpec());
+ dyld_break = target.CreateBreakpoint(
+ &containingModules, nullptr /* containingSourceFiles */,
+ DebugStateCandidates, eFunctionNameTypeFull, eLanguageTypeC,
+ 0, /* offset */
+ eLazyBoolNo, /* skip_prologue */
+ true, /* internal */
+ false /* request_hardware */);
+ }
+
+ if (dyld_break->GetNumResolvedLocations() != 1) {
+ LLDB_LOG(
+ log,
+ "Rendezvous breakpoint has abnormal number of"
+ " resolved locations ({0}) in pid {1}. It's supposed to be exactly 1.",
+ dyld_break->GetNumResolvedLocations(),
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+
+ target.RemoveBreakpointByID(dyld_break->GetID());
+ return false;
+ }
+
+ BreakpointLocationSP location = dyld_break->GetLocationAtIndex(0);
+ LLDB_LOG(log,
+ "Successfully set rendezvous breakpoint at address {0:x} "
+ "for pid {1}",
+ location->GetLoadAddress(),
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+
+ dyld_break->SetCallback(RendezvousBreakpointHit, this, true);
+ dyld_break->SetBreakpointKind("shared-library-event");
+ m_dyld_bid = dyld_break->GetID();
+ return true;
+}
+
+bool DynamicLoaderPOSIXDYLD::RendezvousBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ DynamicLoaderPOSIXDYLD *const dyld_instance =
+ static_cast<DynamicLoaderPOSIXDYLD *>(baton);
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s called for pid %" PRIu64,
+ __FUNCTION__,
+ dyld_instance->m_process ? dyld_instance->m_process->GetID()
+ : LLDB_INVALID_PROCESS_ID);
+
+ dyld_instance->RefreshModules();
+
+ // Return true to stop the target, false to just let the target run.
+ const bool stop_when_images_change = dyld_instance->GetStopWhenImagesChange();
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " stop_when_images_change=%s",
+ __FUNCTION__,
+ dyld_instance->m_process ? dyld_instance->m_process->GetID()
+ : LLDB_INVALID_PROCESS_ID,
+ stop_when_images_change ? "true" : "false");
+ return stop_when_images_change;
+}
+
+void DynamicLoaderPOSIXDYLD::RefreshModules() {
+ if (!m_rendezvous.Resolve())
+ return;
+
+ DYLDRendezvous::iterator I;
+ DYLDRendezvous::iterator E;
+
+ ModuleList &loaded_modules = m_process->GetTarget().GetImages();
+
+ if (m_rendezvous.ModulesDidLoad()) {
+ ModuleList new_modules;
+
+ E = m_rendezvous.loaded_end();
+ for (I = m_rendezvous.loaded_begin(); I != E; ++I) {
+ ModuleSP module_sp =
+ LoadModuleAtAddress(I->file_spec, I->link_addr, I->base_addr, true);
+ if (module_sp.get()) {
+ loaded_modules.AppendIfNeeded(module_sp);
+ new_modules.Append(module_sp);
+ }
+ }
+ m_process->GetTarget().ModulesDidLoad(new_modules);
+ }
+
+ if (m_rendezvous.ModulesDidUnload()) {
+ ModuleList old_modules;
+
+ E = m_rendezvous.unloaded_end();
+ for (I = m_rendezvous.unloaded_begin(); I != E; ++I) {
+ ModuleSpec module_spec{I->file_spec};
+ ModuleSP module_sp = loaded_modules.FindFirstModule(module_spec);
+
+ if (module_sp.get()) {
+ old_modules.Append(module_sp);
+ UnloadSections(module_sp);
+ }
+ }
+ loaded_modules.Remove(old_modules);
+ m_process->GetTarget().ModulesDidUnload(old_modules, false);
+ }
+}
+
+ThreadPlanSP
+DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop) {
+ ThreadPlanSP thread_plan_sp;
+
+ StackFrame *frame = thread.GetStackFrameAtIndex(0).get();
+ const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol);
+ Symbol *sym = context.symbol;
+
+ if (sym == nullptr || !sym->IsTrampoline())
+ return thread_plan_sp;
+
+ ConstString sym_name = sym->GetName();
+ if (!sym_name)
+ return thread_plan_sp;
+
+ SymbolContextList target_symbols;
+ Target &target = thread.GetProcess()->GetTarget();
+ const ModuleList &images = target.GetImages();
+
+ images.FindSymbolsWithNameAndType(sym_name, eSymbolTypeCode, target_symbols);
+ size_t num_targets = target_symbols.GetSize();
+ if (!num_targets)
+ return thread_plan_sp;
+
+ typedef std::vector<lldb::addr_t> AddressVector;
+ AddressVector addrs;
+ for (size_t i = 0; i < num_targets; ++i) {
+ SymbolContext context;
+ AddressRange range;
+ if (target_symbols.GetContextAtIndex(i, context)) {
+ context.GetAddressRange(eSymbolContextEverything, 0, false, range);
+ lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(&target);
+ if (addr != LLDB_INVALID_ADDRESS)
+ addrs.push_back(addr);
+ }
+ }
+
+ if (addrs.size() > 0) {
+ AddressVector::iterator start = addrs.begin();
+ AddressVector::iterator end = addrs.end();
+
+ llvm::sort(start, end);
+ addrs.erase(std::unique(start, end), end);
+ thread_plan_sp =
+ std::make_shared<ThreadPlanRunToAddress>(thread, addrs, stop);
+ }
+
+ return thread_plan_sp;
+}
+
+void DynamicLoaderPOSIXDYLD::LoadVDSO() {
+ if (m_vdso_base == LLDB_INVALID_ADDRESS)
+ return;
+
+ FileSpec file("[vdso]");
+
+ MemoryRegionInfo info;
+ Status status = m_process->GetMemoryRegionInfo(m_vdso_base, info);
+ if (status.Fail()) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ LLDB_LOG(log, "Failed to get vdso region info: {0}", status);
+ return;
+ }
+
+ if (ModuleSP module_sp = m_process->ReadModuleFromMemory(
+ file, m_vdso_base, info.GetRange().GetByteSize())) {
+ UpdateLoadedSections(module_sp, LLDB_INVALID_ADDRESS, m_vdso_base, false);
+ m_process->GetTarget().GetImages().AppendIfNeeded(module_sp);
+ }
+}
+
+ModuleSP DynamicLoaderPOSIXDYLD::LoadInterpreterModule() {
+ if (m_interpreter_base == LLDB_INVALID_ADDRESS)
+ return nullptr;
+
+ MemoryRegionInfo info;
+ Target &target = m_process->GetTarget();
+ Status status = m_process->GetMemoryRegionInfo(m_interpreter_base, info);
+ if (status.Fail() || info.GetMapped() != MemoryRegionInfo::eYes ||
+ info.GetName().IsEmpty()) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ LLDB_LOG(log, "Failed to get interpreter region info: {0}", status);
+ return nullptr;
+ }
+
+ FileSpec file(info.GetName().GetCString());
+ ModuleSpec module_spec(file, target.GetArchitecture());
+
+ if (ModuleSP module_sp = target.GetOrCreateModule(module_spec,
+ true /* notify */)) {
+ UpdateLoadedSections(module_sp, LLDB_INVALID_ADDRESS, m_interpreter_base,
+ false);
+ return module_sp;
+ }
+ return nullptr;
+}
+
+void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() {
+ DYLDRendezvous::iterator I;
+ DYLDRendezvous::iterator E;
+ ModuleList module_list;
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ LoadVDSO();
+
+ if (!m_rendezvous.Resolve()) {
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s unable to resolve POSIX DYLD "
+ "rendezvous address",
+ __FUNCTION__);
+ return;
+ }
+
+ // The rendezvous class doesn't enumerate the main module, so track that
+ // ourselves here.
+ ModuleSP executable = GetTargetExecutable();
+ m_loaded_modules[executable] = m_rendezvous.GetLinkMapAddress();
+
+ std::vector<FileSpec> module_names;
+ for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I)
+ module_names.push_back(I->file_spec);
+ m_process->PrefetchModuleSpecs(
+ module_names, m_process->GetTarget().GetArchitecture().GetTriple());
+
+ for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) {
+ ModuleSP module_sp =
+ LoadModuleAtAddress(I->file_spec, I->link_addr, I->base_addr, true);
+ if (module_sp.get()) {
+ LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}",
+ I->file_spec.GetFilename());
+ module_list.Append(module_sp);
+ } else {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf(
+ "DynamicLoaderPOSIXDYLD::%s failed loading module %s at 0x%" PRIx64,
+ __FUNCTION__, I->file_spec.GetCString(), I->base_addr);
+ }
+ }
+
+ m_process->GetTarget().ModulesDidLoad(module_list);
+}
+
+addr_t DynamicLoaderPOSIXDYLD::ComputeLoadOffset() {
+ addr_t virt_entry;
+
+ if (m_load_offset != LLDB_INVALID_ADDRESS)
+ return m_load_offset;
+
+ if ((virt_entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ ModuleSP module = m_process->GetTarget().GetExecutableModule();
+ if (!module)
+ return LLDB_INVALID_ADDRESS;
+
+ ObjectFile *exe = module->GetObjectFile();
+ if (!exe)
+ return LLDB_INVALID_ADDRESS;
+
+ Address file_entry = exe->GetEntryPointAddress();
+
+ if (!file_entry.IsValid())
+ return LLDB_INVALID_ADDRESS;
+
+ m_load_offset = virt_entry - file_entry.GetFileAddress();
+ return m_load_offset;
+}
+
+void DynamicLoaderPOSIXDYLD::EvalSpecialModulesStatus() {
+ if (llvm::Optional<uint64_t> vdso_base =
+ m_auxv->GetAuxValue(AuxVector::AUXV_AT_SYSINFO_EHDR))
+ m_vdso_base = *vdso_base;
+
+ if (llvm::Optional<uint64_t> interpreter_base =
+ m_auxv->GetAuxValue(AuxVector::AUXV_AT_BASE))
+ m_interpreter_base = *interpreter_base;
+}
+
+addr_t DynamicLoaderPOSIXDYLD::GetEntryPoint() {
+ if (m_entry_point != LLDB_INVALID_ADDRESS)
+ return m_entry_point;
+
+ if (m_auxv == nullptr)
+ return LLDB_INVALID_ADDRESS;
+
+ llvm::Optional<uint64_t> entry_point =
+ m_auxv->GetAuxValue(AuxVector::AUXV_AT_ENTRY);
+ if (!entry_point)
+ return LLDB_INVALID_ADDRESS;
+
+ m_entry_point = static_cast<addr_t>(*entry_point);
+
+ const ArchSpec &arch = m_process->GetTarget().GetArchitecture();
+
+ // On ppc64, the entry point is actually a descriptor. Dereference it.
+ if (arch.GetMachine() == llvm::Triple::ppc64)
+ m_entry_point = ReadUnsignedIntWithSizeInBytes(m_entry_point, 8);
+
+ return m_entry_point;
+}
+
+lldb::addr_t
+DynamicLoaderPOSIXDYLD::GetThreadLocalData(const lldb::ModuleSP module_sp,
+ const lldb::ThreadSP thread,
+ lldb::addr_t tls_file_addr) {
+ auto it = m_loaded_modules.find(module_sp);
+ if (it == m_loaded_modules.end())
+ return LLDB_INVALID_ADDRESS;
+
+ addr_t link_map = it->second;
+ if (link_map == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ const DYLDRendezvous::ThreadInfo &metadata = m_rendezvous.GetThreadInfo();
+ if (!metadata.valid)
+ return LLDB_INVALID_ADDRESS;
+
+ // Get the thread pointer.
+ addr_t tp = thread->GetThreadPointer();
+ if (tp == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ // Find the module's modid.
+ int modid_size = 4; // FIXME(spucci): This isn't right for big-endian 64-bit
+ int64_t modid = ReadUnsignedIntWithSizeInBytes(
+ link_map + metadata.modid_offset, modid_size);
+ if (modid == -1)
+ return LLDB_INVALID_ADDRESS;
+
+ // Lookup the DTV structure for this thread.
+ addr_t dtv_ptr = tp + metadata.dtv_offset;
+ addr_t dtv = ReadPointer(dtv_ptr);
+ if (dtv == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ // Find the TLS block for this module.
+ addr_t dtv_slot = dtv + metadata.dtv_slot_size * modid;
+ addr_t tls_block = ReadPointer(dtv_slot + metadata.tls_offset);
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::Performed TLS lookup: "
+ "module=%s, link_map=0x%" PRIx64 ", tp=0x%" PRIx64
+ ", modid=%" PRId64 ", tls_block=0x%" PRIx64 "\n",
+ module_sp->GetObjectName().AsCString(""), link_map, tp,
+ (int64_t)modid, tls_block);
+
+ if (tls_block == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+ else
+ return tls_block + tls_file_addr;
+}
+
+void DynamicLoaderPOSIXDYLD::ResolveExecutableModule(
+ lldb::ModuleSP &module_sp) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ if (m_process == nullptr)
+ return;
+
+ auto &target = m_process->GetTarget();
+ const auto platform_sp = target.GetPlatform();
+
+ ProcessInstanceInfo process_info;
+ if (!m_process->GetProcessInfo(process_info)) {
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s - failed to get process info for "
+ "pid %" PRIu64,
+ __FUNCTION__, m_process->GetID());
+ return;
+ }
+
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64
+ ": %s",
+ __FUNCTION__, m_process->GetID(),
+ process_info.GetExecutableFile().GetPath().c_str());
+
+ ModuleSpec module_spec(process_info.GetExecutableFile(),
+ process_info.GetArchitecture());
+ if (module_sp && module_sp->MatchesModuleSpec(module_spec))
+ return;
+
+ const auto executable_search_paths(Target::GetDefaultExecutableSearchPaths());
+ auto error = platform_sp->ResolveExecutable(
+ module_spec, module_sp,
+ !executable_search_paths.IsEmpty() ? &executable_search_paths : nullptr);
+ if (error.Fail()) {
+ StreamString stream;
+ module_spec.Dump(stream);
+
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s - failed to resolve executable "
+ "with module spec \"%s\": %s",
+ __FUNCTION__, stream.GetData(), error.AsCString());
+ return;
+ }
+
+ target.SetExecutableModule(module_sp, eLoadDependentsNo);
+}
+
+bool DynamicLoaderPOSIXDYLD::AlwaysRelyOnEHUnwindInfo(
+ lldb_private::SymbolContext &sym_ctx) {
+ ModuleSP module_sp;
+ if (sym_ctx.symbol)
+ module_sp = sym_ctx.symbol->GetAddressRef().GetModule();
+ if (!module_sp && sym_ctx.function)
+ module_sp =
+ sym_ctx.function->GetAddressRange().GetBaseAddress().GetModule();
+ if (!module_sp)
+ return false;
+
+ return module_sp->GetFileSpec().GetPath() == "[vdso]";
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h
new file mode 100644
index 000000000000..0630d1eb11d1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h
@@ -0,0 +1,165 @@
+//===-- DynamicLoaderPOSIXDYLD.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_DynamicLoaderPOSIXDYLD_h_
+#define liblldb_DynamicLoaderPOSIXDYLD_h_
+
+#include <map>
+#include <memory>
+
+#include "DYLDRendezvous.h"
+#include "Plugins/Process/Utility/AuxVector.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Target/DynamicLoader.h"
+
+class AuxVector;
+
+class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader {
+public:
+ DynamicLoaderPOSIXDYLD(lldb_private::Process *process);
+
+ ~DynamicLoaderPOSIXDYLD() override;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::DynamicLoader *
+ CreateInstance(lldb_private::Process *process, bool force);
+
+ // DynamicLoader protocol
+
+ void DidAttach() override;
+
+ void DidLaunch() override;
+
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread,
+ bool stop_others) override;
+
+ lldb_private::Status CanLoadImage() override;
+
+ lldb::addr_t GetThreadLocalData(const lldb::ModuleSP module,
+ const lldb::ThreadSP thread,
+ lldb::addr_t tls_file_addr) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ /// Runtime linker rendezvous structure.
+ DYLDRendezvous m_rendezvous;
+
+ /// Virtual load address of the inferior process.
+ lldb::addr_t m_load_offset;
+
+ /// Virtual entry address of the inferior process.
+ lldb::addr_t m_entry_point;
+
+ /// Auxiliary vector of the inferior process.
+ std::unique_ptr<AuxVector> m_auxv;
+
+ /// Rendezvous breakpoint.
+ lldb::break_id_t m_dyld_bid;
+
+ /// Contains AT_SYSINFO_EHDR, which means a vDSO has been
+ /// mapped to the address space
+ lldb::addr_t m_vdso_base;
+
+ /// Contains AT_BASE, which means a dynamic loader has been
+ /// mapped to the address space
+ lldb::addr_t m_interpreter_base;
+
+ /// Loaded module list. (link map for each module)
+ std::map<lldb::ModuleWP, lldb::addr_t, std::owner_less<lldb::ModuleWP>>
+ m_loaded_modules;
+
+ /// If possible sets a breakpoint on a function called by the runtime
+ /// linker each time a module is loaded or unloaded.
+ bool SetRendezvousBreakpoint();
+
+ /// Callback routine which updates the current list of loaded modules based
+ /// on the information supplied by the runtime linker.
+ static bool RendezvousBreakpointHit(
+ void *baton, lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ /// Helper method for RendezvousBreakpointHit. Updates LLDB's current set
+ /// of loaded modules.
+ void RefreshModules();
+
+ /// Updates the load address of every allocatable section in \p module.
+ ///
+ /// \param module The module to traverse.
+ ///
+ /// \param link_map_addr The virtual address of the link map for the @p
+ /// module.
+ ///
+ /// \param base_addr The virtual base address \p module is loaded at.
+ void UpdateLoadedSections(lldb::ModuleSP module, lldb::addr_t link_map_addr,
+ lldb::addr_t base_addr,
+ bool base_addr_is_offset) override;
+
+ /// Removes the loaded sections from the target in \p module.
+ ///
+ /// \param module The module to traverse.
+ void UnloadSections(const lldb::ModuleSP module) override;
+
+ /// Resolves the entry point for the current inferior process and sets a
+ /// breakpoint at that address.
+ void ProbeEntry();
+
+ /// Callback routine invoked when we hit the breakpoint on process entry.
+ ///
+ /// This routine is responsible for resolving the load addresses of all
+ /// dependent modules required by the inferior and setting up the rendezvous
+ /// breakpoint.
+ static bool
+ EntryBreakpointHit(void *baton,
+ lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ /// Helper for the entry breakpoint callback. Resolves the load addresses
+ /// of all dependent modules.
+ virtual void LoadAllCurrentModules();
+
+ void LoadVDSO();
+
+ // Loading an interpreter module (if present) assumming m_interpreter_base
+ // already points to its base address.
+ lldb::ModuleSP LoadInterpreterModule();
+
+ /// Computes a value for m_load_offset returning the computed address on
+ /// success and LLDB_INVALID_ADDRESS on failure.
+ lldb::addr_t ComputeLoadOffset();
+
+ /// Computes a value for m_entry_point returning the computed address on
+ /// success and LLDB_INVALID_ADDRESS on failure.
+ lldb::addr_t GetEntryPoint();
+
+ /// Evaluate if Aux vectors contain vDSO and LD information
+ /// in case they do, read and assign the address to m_vdso_base
+ /// and m_interpreter_base.
+ void EvalSpecialModulesStatus();
+
+ /// Loads Module from inferior process.
+ void ResolveExecutableModule(lldb::ModuleSP &module_sp);
+
+ bool AlwaysRelyOnEHUnwindInfo(lldb_private::SymbolContext &sym_ctx) override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(DynamicLoaderPOSIXDYLD);
+};
+
+#endif // liblldb_DynamicLoaderPOSIXDYLD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp
new file mode 100644
index 000000000000..6bc951c4d35b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp
@@ -0,0 +1,154 @@
+//===-- DynamicLoaderStatic.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Target.h"
+
+#include "DynamicLoaderStatic.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Create an instance of this class. This function is filled into the plugin
+// info class that gets handed out by the plugin factory and allows the lldb to
+// instantiate an instance of this class.
+DynamicLoader *DynamicLoaderStatic::CreateInstance(Process *process,
+ bool force) {
+ bool create = force;
+ if (!create) {
+ const llvm::Triple &triple_ref =
+ process->GetTarget().GetArchitecture().GetTriple();
+ const llvm::Triple::OSType os_type = triple_ref.getOS();
+ if ((os_type == llvm::Triple::UnknownOS))
+ create = true;
+ }
+
+ if (!create) {
+ Module *exe_module = process->GetTarget().GetExecutableModulePointer();
+ if (exe_module) {
+ ObjectFile *object_file = exe_module->GetObjectFile();
+ if (object_file) {
+ create = (object_file->GetStrata() == ObjectFile::eStrataRawImage);
+ }
+ }
+ }
+
+ if (create)
+ return new DynamicLoaderStatic(process);
+ return nullptr;
+}
+
+// Constructor
+DynamicLoaderStatic::DynamicLoaderStatic(Process *process)
+ : DynamicLoader(process) {}
+
+// Destructor
+DynamicLoaderStatic::~DynamicLoaderStatic() {}
+
+/// Called after attaching a process.
+///
+/// Allow DynamicLoader plug-ins to execute some code after
+/// attaching to a process.
+void DynamicLoaderStatic::DidAttach() { LoadAllImagesAtFileAddresses(); }
+
+/// Called after attaching a process.
+///
+/// Allow DynamicLoader plug-ins to execute some code after
+/// attaching to a process.
+void DynamicLoaderStatic::DidLaunch() { LoadAllImagesAtFileAddresses(); }
+
+void DynamicLoaderStatic::LoadAllImagesAtFileAddresses() {
+ const ModuleList &module_list = m_process->GetTarget().GetImages();
+
+ ModuleList loaded_module_list;
+
+ // Disable JIT for static dynamic loader targets
+ m_process->SetCanJIT(false);
+
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+
+ const size_t num_modules = module_list.GetSize();
+ for (uint32_t idx = 0; idx < num_modules; ++idx) {
+ ModuleSP module_sp(module_list.GetModuleAtIndexUnlocked(idx));
+ if (module_sp) {
+ bool changed = false;
+ ObjectFile *image_object_file = module_sp->GetObjectFile();
+ if (image_object_file) {
+ SectionList *section_list = image_object_file->GetSectionList();
+ if (section_list) {
+ // All sections listed in the dyld image info structure will all
+ // either be fixed up already, or they will all be off by a single
+ // slide amount that is determined by finding the first segment that
+ // is at file offset zero which also has bytes (a file size that is
+ // greater than zero) in the object file.
+
+ // Determine the slide amount (if any)
+ const size_t num_sections = section_list->GetSize();
+ size_t sect_idx = 0;
+ for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
+ // Iterate through the object file sections to find the first
+ // section that starts of file offset zero and that has bytes in
+ // the file...
+ SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
+ if (section_sp) {
+ if (m_process->GetTarget().SetSectionLoadAddress(
+ section_sp, section_sp->GetFileAddress()))
+ changed = true;
+ }
+ }
+ }
+ }
+
+ if (changed)
+ loaded_module_list.AppendIfNeeded(module_sp);
+ }
+ }
+
+ m_process->GetTarget().ModulesDidLoad(loaded_module_list);
+}
+
+ThreadPlanSP
+DynamicLoaderStatic::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) {
+ return ThreadPlanSP();
+}
+
+Status DynamicLoaderStatic::CanLoadImage() {
+ Status error;
+ error.SetErrorString("can't load images on with a static debug session");
+ return error;
+}
+
+void DynamicLoaderStatic::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void DynamicLoaderStatic::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString DynamicLoaderStatic::GetPluginNameStatic() {
+ static ConstString g_name("static");
+ return g_name;
+}
+
+const char *DynamicLoaderStatic::GetPluginDescriptionStatic() {
+ return "Dynamic loader plug-in that will load any images at the static "
+ "addresses contained in each image.";
+}
+
+// PluginInterface protocol
+lldb_private::ConstString DynamicLoaderStatic::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t DynamicLoaderStatic::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h
new file mode 100644
index 000000000000..fa9aded7286c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h
@@ -0,0 +1,59 @@
+//===-- DynamicLoaderStatic.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_DynamicLoaderStatic_h_
+#define liblldb_DynamicLoaderStatic_h_
+
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/UUID.h"
+
+class DynamicLoaderStatic : public lldb_private::DynamicLoader {
+public:
+ DynamicLoaderStatic(lldb_private::Process *process);
+
+ ~DynamicLoaderStatic() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::DynamicLoader *
+ CreateInstance(lldb_private::Process *process, bool force);
+
+ /// Called after attaching a process.
+ ///
+ /// Allow DynamicLoader plug-ins to execute some code after
+ /// attaching to a process.
+ void DidAttach() override;
+
+ void DidLaunch() override;
+
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread,
+ bool stop_others) override;
+
+ lldb_private::Status CanLoadImage() override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+private:
+ void LoadAllImagesAtFileAddresses();
+
+ DISALLOW_COPY_AND_ASSIGN(DynamicLoaderStatic);
+};
+
+#endif // liblldb_DynamicLoaderStatic_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp
new file mode 100644
index 000000000000..fa3fbe0d9fa6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp
@@ -0,0 +1,225 @@
+//===-- DynamicLoaderWindowsDYLD.cpp --------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DynamicLoaderWindowsDYLD.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlanStepInstruction.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/ADT/Triple.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+DynamicLoaderWindowsDYLD::DynamicLoaderWindowsDYLD(Process *process)
+ : DynamicLoader(process) {}
+
+DynamicLoaderWindowsDYLD::~DynamicLoaderWindowsDYLD() {}
+
+void DynamicLoaderWindowsDYLD::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void DynamicLoaderWindowsDYLD::Terminate() {}
+
+ConstString DynamicLoaderWindowsDYLD::GetPluginNameStatic() {
+ static ConstString g_plugin_name("windows-dyld");
+ return g_plugin_name;
+}
+
+const char *DynamicLoaderWindowsDYLD::GetPluginDescriptionStatic() {
+ return "Dynamic loader plug-in that watches for shared library "
+ "loads/unloads in Windows processes.";
+}
+
+DynamicLoader *DynamicLoaderWindowsDYLD::CreateInstance(Process *process,
+ bool force) {
+ bool should_create = force;
+ if (!should_create) {
+ const llvm::Triple &triple_ref =
+ process->GetTarget().GetArchitecture().GetTriple();
+ if (triple_ref.getOS() == llvm::Triple::Win32)
+ should_create = true;
+ }
+
+ if (should_create)
+ return new DynamicLoaderWindowsDYLD(process);
+
+ return nullptr;
+}
+
+void DynamicLoaderWindowsDYLD::OnLoadModule(lldb::ModuleSP module_sp,
+ const ModuleSpec module_spec,
+ lldb::addr_t module_addr) {
+
+ // Resolve the module unless we already have one.
+ if (!module_sp) {
+ Status error;
+ module_sp = m_process->GetTarget().GetOrCreateModule(module_spec,
+ true /* notify */, &error);
+ if (error.Fail())
+ return;
+ }
+
+ m_loaded_modules[module_sp] = module_addr;
+ UpdateLoadedSectionsCommon(module_sp, module_addr, false);
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ m_process->GetTarget().ModulesDidLoad(module_list);
+}
+
+void DynamicLoaderWindowsDYLD::OnUnloadModule(lldb::addr_t module_addr) {
+ Address resolved_addr;
+ if (!m_process->GetTarget().ResolveLoadAddress(module_addr, resolved_addr))
+ return;
+
+ ModuleSP module_sp = resolved_addr.GetModule();
+ if (module_sp) {
+ m_loaded_modules.erase(module_sp);
+ UnloadSectionsCommon(module_sp);
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ m_process->GetTarget().ModulesDidUnload(module_list, false);
+ }
+}
+
+lldb::addr_t DynamicLoaderWindowsDYLD::GetLoadAddress(ModuleSP executable) {
+ // First, see if the load address is already cached.
+ auto it = m_loaded_modules.find(executable);
+ if (it != m_loaded_modules.end() && it->second != LLDB_INVALID_ADDRESS)
+ return it->second;
+
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+
+ // Second, try to get it through the process plugins. For a remote process,
+ // the remote platform will be responsible for providing it.
+ FileSpec file_spec(executable->GetPlatformFileSpec());
+ bool is_loaded = false;
+ Status status =
+ m_process->GetFileLoadAddress(file_spec, is_loaded, load_addr);
+ // Servers other than lldb server could respond with a bogus address.
+ if (status.Success() && is_loaded && load_addr != LLDB_INVALID_ADDRESS) {
+ m_loaded_modules[executable] = load_addr;
+ return load_addr;
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+void DynamicLoaderWindowsDYLD::DidAttach() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderWindowsDYLD::%s()", __FUNCTION__);
+
+ ModuleSP executable = GetTargetExecutable();
+
+ if (!executable.get())
+ return;
+
+ // Try to fetch the load address of the file from the process, since there
+ // could be randomization of the load address.
+ lldb::addr_t load_addr = GetLoadAddress(executable);
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ return;
+
+ // Request the process base address.
+ lldb::addr_t image_base = m_process->GetImageInfoAddress();
+ if (image_base == load_addr)
+ return;
+
+ // Rebase the process's modules if there is a mismatch.
+ UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false);
+
+ ModuleList module_list;
+ module_list.Append(executable);
+ m_process->GetTarget().ModulesDidLoad(module_list);
+ m_process->LoadModules();
+}
+
+void DynamicLoaderWindowsDYLD::DidLaunch() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderWindowsDYLD::%s()", __FUNCTION__);
+
+ ModuleSP executable = GetTargetExecutable();
+ if (!executable.get())
+ return;
+
+ lldb::addr_t load_addr = GetLoadAddress(executable);
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ // Update the loaded sections so that the breakpoints can be resolved.
+ UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false);
+
+ ModuleList module_list;
+ module_list.Append(executable);
+ m_process->GetTarget().ModulesDidLoad(module_list);
+ m_process->LoadModules();
+ }
+}
+
+Status DynamicLoaderWindowsDYLD::CanLoadImage() { return Status(); }
+
+ConstString DynamicLoaderWindowsDYLD::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t DynamicLoaderWindowsDYLD::GetPluginVersion() { return 1; }
+
+ThreadPlanSP
+DynamicLoaderWindowsDYLD::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop) {
+ auto arch = m_process->GetTarget().GetArchitecture();
+ if (arch.GetMachine() != llvm::Triple::x86) {
+ return ThreadPlanSP();
+ }
+
+ uint64_t pc = thread.GetRegisterContext()->GetPC();
+ // Max size of an instruction in x86 is 15 bytes.
+ AddressRange range(pc, 2 * 15);
+
+ ExecutionContext exe_ctx(m_process->GetTarget());
+ DisassemblerSP disassembler_sp = Disassembler::DisassembleRange(
+ arch, nullptr, nullptr, exe_ctx, range, true);
+ if (!disassembler_sp) {
+ return ThreadPlanSP();
+ }
+
+ InstructionList *insn_list = &disassembler_sp->GetInstructionList();
+ if (insn_list == nullptr) {
+ return ThreadPlanSP();
+ }
+
+ // First instruction in a x86 Windows trampoline is going to be an indirect
+ // jump through the IAT and the next one will be a nop (usually there for
+ // alignment purposes). e.g.:
+ // 0x70ff4cfc <+956>: jmpl *0x7100c2a8
+ // 0x70ff4d02 <+962>: nop
+
+ auto first_insn = insn_list->GetInstructionAtIndex(0);
+ auto second_insn = insn_list->GetInstructionAtIndex(1);
+
+ if (first_insn == nullptr || second_insn == nullptr ||
+ strcmp(first_insn->GetMnemonic(&exe_ctx), "jmpl") != 0 ||
+ strcmp(second_insn->GetMnemonic(&exe_ctx), "nop") != 0) {
+ return ThreadPlanSP();
+ }
+
+ assert(first_insn->DoesBranch() && !second_insn->DoesBranch());
+
+ return ThreadPlanSP(new ThreadPlanStepInstruction(
+ thread, false, false, eVoteNoOpinion, eVoteNoOpinion));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h
new file mode 100644
index 000000000000..100689a63128
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h
@@ -0,0 +1,54 @@
+//===-- DynamicLoaderWindowsDYLD.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_Plugins_Process_Windows_DynamicLoaderWindowsDYLD_h_
+#define liblldb_Plugins_Process_Windows_DynamicLoaderWindowsDYLD_h_
+
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/lldb-forward.h"
+
+#include <map>
+
+namespace lldb_private {
+
+class DynamicLoaderWindowsDYLD : public DynamicLoader {
+public:
+ DynamicLoaderWindowsDYLD(Process *process);
+
+ ~DynamicLoaderWindowsDYLD() override;
+
+ static void Initialize();
+ static void Terminate();
+ static ConstString GetPluginNameStatic();
+ static const char *GetPluginDescriptionStatic();
+
+ static DynamicLoader *CreateInstance(Process *process, bool force);
+
+ void OnLoadModule(lldb::ModuleSP module_sp, const ModuleSpec module_spec,
+ lldb::addr_t module_addr);
+ void OnUnloadModule(lldb::addr_t module_addr);
+
+ void DidAttach() override;
+ void DidLaunch() override;
+ Status CanLoadImage() override;
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop) override;
+
+ ConstString GetPluginName() override;
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::addr_t GetLoadAddress(lldb::ModuleSP executable);
+
+private:
+ std::map<lldb::ModuleSP, lldb::addr_t> m_loaded_modules;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_Plugins_Process_Windows_DynamicLoaderWindowsDYLD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.cpp
new file mode 100644
index 000000000000..369f88327dd9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.cpp
@@ -0,0 +1,108 @@
+//===-- ASTDumper.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTDumper.h"
+
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/Support/raw_ostream.h"
+
+using namespace lldb_private;
+
+ASTDumper::ASTDumper(clang::Decl *decl) {
+ clang::DeclContext *decl_ctx = llvm::dyn_cast<clang::DeclContext>(decl);
+
+ bool has_external_lexical_storage;
+ bool has_external_visible_storage;
+
+ if (decl_ctx) {
+ has_external_lexical_storage = decl_ctx->hasExternalLexicalStorage();
+ has_external_visible_storage = decl_ctx->hasExternalVisibleStorage();
+ decl_ctx->setHasExternalLexicalStorage(false);
+ decl_ctx->setHasExternalVisibleStorage(false);
+ }
+
+ llvm::raw_string_ostream os(m_dump);
+ decl->print(os);
+ os.flush();
+
+ if (decl_ctx) {
+ decl_ctx->setHasExternalLexicalStorage(has_external_lexical_storage);
+ decl_ctx->setHasExternalVisibleStorage(has_external_visible_storage);
+ }
+}
+
+ASTDumper::ASTDumper(clang::DeclContext *decl_ctx) {
+ bool has_external_lexical_storage = decl_ctx->hasExternalLexicalStorage();
+ bool has_external_visible_storage = decl_ctx->hasExternalVisibleStorage();
+
+ decl_ctx->setHasExternalLexicalStorage(false);
+ decl_ctx->setHasExternalVisibleStorage(false);
+
+ if (clang::Decl *decl = llvm::dyn_cast<clang::Decl>(decl_ctx)) {
+ llvm::raw_string_ostream os(m_dump);
+ decl->print(os);
+ os.flush();
+ } else {
+ m_dump.assign("<DeclContext is not a Decl>");
+ }
+
+ decl_ctx->setHasExternalLexicalStorage(has_external_lexical_storage);
+ decl_ctx->setHasExternalVisibleStorage(has_external_visible_storage);
+}
+
+ASTDumper::ASTDumper(const clang::Type *type) {
+ m_dump = clang::QualType(type, 0).getAsString();
+}
+
+ASTDumper::ASTDumper(clang::QualType type) { m_dump = type.getAsString(); }
+
+ASTDumper::ASTDumper(lldb::opaque_compiler_type_t type) {
+ m_dump = clang::QualType::getFromOpaquePtr(type).getAsString();
+}
+
+ASTDumper::ASTDumper(const CompilerType &compiler_type) {
+ m_dump = ClangUtil::GetQualType(compiler_type).getAsString();
+}
+
+const char *ASTDumper::GetCString() { return m_dump.c_str(); }
+
+void ASTDumper::ToSTDERR() { fprintf(stderr, "%s\n", m_dump.c_str()); }
+
+void ASTDumper::ToLog(Log *log, const char *prefix) {
+ size_t len = m_dump.length() + 1;
+
+ char *alloc = (char *)malloc(len);
+ char *str = alloc;
+
+ memcpy(str, m_dump.c_str(), len);
+
+ char *end = nullptr;
+
+ end = strchr(str, '\n');
+
+ while (end) {
+ *end = '\0';
+
+ log->Printf("%s%s", prefix, str);
+
+ *end = '\n';
+
+ str = end + 1;
+ end = strchr(str, '\n');
+ }
+
+ log->Printf("%s%s", prefix, str);
+
+ free(alloc);
+}
+
+void ASTDumper::ToStream(lldb::StreamSP &stream) { stream->PutCString(m_dump); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.h
new file mode 100644
index 000000000000..ddf055d9c0c3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.h
@@ -0,0 +1,40 @@
+//===-- ASTDumper.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ASTDumper_h_
+#define liblldb_ASTDumper_h_
+
+#include "clang/AST/DeclVisitor.h"
+#include "clang/AST/TypeVisitor.h"
+
+#include "lldb/Utility/Stream.h"
+#include "llvm/ADT/DenseSet.h"
+
+namespace lldb_private {
+
+class ASTDumper {
+public:
+ ASTDumper(clang::Decl *decl);
+ ASTDumper(clang::DeclContext *decl_ctx);
+ ASTDumper(const clang::Type *type);
+ ASTDumper(clang::QualType type);
+ ASTDumper(lldb::opaque_compiler_type_t type);
+ ASTDumper(const CompilerType &compiler_type);
+
+ const char *GetCString();
+ void ToSTDERR();
+ void ToLog(Log *log, const char *prefix);
+ void ToStream(lldb::StreamSP &stream);
+
+private:
+ std::string m_dump;
+};
+
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp
new file mode 100644
index 000000000000..526ef90782ef
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp
@@ -0,0 +1,516 @@
+//===-- ASTResultSynthesizer.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTResultSynthesizer.h"
+
+#include "ClangPersistentVariables.h"
+
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "stdlib.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace lldb_private;
+
+ASTResultSynthesizer::ASTResultSynthesizer(ASTConsumer *passthrough,
+ bool top_level, Target &target)
+ : m_ast_context(nullptr), m_passthrough(passthrough),
+ m_passthrough_sema(nullptr), m_target(target), m_sema(nullptr),
+ m_top_level(top_level) {
+ if (!m_passthrough)
+ return;
+
+ m_passthrough_sema = dyn_cast<SemaConsumer>(passthrough);
+}
+
+ASTResultSynthesizer::~ASTResultSynthesizer() {}
+
+void ASTResultSynthesizer::Initialize(ASTContext &Context) {
+ m_ast_context = &Context;
+
+ if (m_passthrough)
+ m_passthrough->Initialize(Context);
+}
+
+void ASTResultSynthesizer::TransformTopLevelDecl(Decl *D) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (NamedDecl *named_decl = dyn_cast<NamedDecl>(D)) {
+ if (log && log->GetVerbose()) {
+ if (named_decl->getIdentifier())
+ log->Printf("TransformTopLevelDecl(%s)",
+ named_decl->getIdentifier()->getNameStart());
+ else if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D))
+ log->Printf("TransformTopLevelDecl(%s)",
+ method_decl->getSelector().getAsString().c_str());
+ else
+ log->Printf("TransformTopLevelDecl(<complex>)");
+ }
+
+ if (m_top_level) {
+ RecordPersistentDecl(named_decl);
+ }
+ }
+
+ if (LinkageSpecDecl *linkage_spec_decl = dyn_cast<LinkageSpecDecl>(D)) {
+ RecordDecl::decl_iterator decl_iterator;
+
+ for (decl_iterator = linkage_spec_decl->decls_begin();
+ decl_iterator != linkage_spec_decl->decls_end(); ++decl_iterator) {
+ TransformTopLevelDecl(*decl_iterator);
+ }
+ } else if (!m_top_level) {
+ if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D)) {
+ if (m_ast_context &&
+ !method_decl->getSelector().getAsString().compare("$__lldb_expr:")) {
+ RecordPersistentTypes(method_decl);
+ SynthesizeObjCMethodResult(method_decl);
+ }
+ } else if (FunctionDecl *function_decl = dyn_cast<FunctionDecl>(D)) {
+ // When completing user input the body of the function may be a nullptr.
+ if (m_ast_context && function_decl->hasBody() &&
+ !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) {
+ RecordPersistentTypes(function_decl);
+ SynthesizeFunctionResult(function_decl);
+ }
+ }
+ }
+}
+
+bool ASTResultSynthesizer::HandleTopLevelDecl(DeclGroupRef D) {
+ DeclGroupRef::iterator decl_iterator;
+
+ for (decl_iterator = D.begin(); decl_iterator != D.end(); ++decl_iterator) {
+ Decl *decl = *decl_iterator;
+
+ TransformTopLevelDecl(decl);
+ }
+
+ if (m_passthrough)
+ return m_passthrough->HandleTopLevelDecl(D);
+ return true;
+}
+
+bool ASTResultSynthesizer::SynthesizeFunctionResult(FunctionDecl *FunDecl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!m_sema)
+ return false;
+
+ FunctionDecl *function_decl = FunDecl;
+
+ if (!function_decl)
+ return false;
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream os(s);
+
+ function_decl->print(os);
+
+ os.flush();
+
+ log->Printf("Untransformed function AST:\n%s", s.c_str());
+ }
+
+ Stmt *function_body = function_decl->getBody();
+ CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(function_body);
+
+ bool ret = SynthesizeBodyResult(compound_stmt, function_decl);
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream os(s);
+
+ function_decl->print(os);
+
+ os.flush();
+
+ log->Printf("Transformed function AST:\n%s", s.c_str());
+ }
+
+ return ret;
+}
+
+bool ASTResultSynthesizer::SynthesizeObjCMethodResult(
+ ObjCMethodDecl *MethodDecl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!m_sema)
+ return false;
+
+ if (!MethodDecl)
+ return false;
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream os(s);
+
+ MethodDecl->print(os);
+
+ os.flush();
+
+ log->Printf("Untransformed method AST:\n%s", s.c_str());
+ }
+
+ Stmt *method_body = MethodDecl->getBody();
+
+ if (!method_body)
+ return false;
+
+ CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(method_body);
+
+ bool ret = SynthesizeBodyResult(compound_stmt, MethodDecl);
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream os(s);
+
+ MethodDecl->print(os);
+
+ os.flush();
+
+ log->Printf("Transformed method AST:\n%s", s.c_str());
+ }
+
+ return ret;
+}
+
+bool ASTResultSynthesizer::SynthesizeBodyResult(CompoundStmt *Body,
+ DeclContext *DC) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ ASTContext &Ctx(*m_ast_context);
+
+ if (!Body)
+ return false;
+
+ if (Body->body_empty())
+ return false;
+
+ Stmt **last_stmt_ptr = Body->body_end() - 1;
+ Stmt *last_stmt = *last_stmt_ptr;
+
+ while (dyn_cast<NullStmt>(last_stmt)) {
+ if (last_stmt_ptr != Body->body_begin()) {
+ last_stmt_ptr--;
+ last_stmt = *last_stmt_ptr;
+ } else {
+ return false;
+ }
+ }
+
+ Expr *last_expr = dyn_cast<Expr>(last_stmt);
+
+ if (!last_expr)
+ // No auxiliary variable necessary; expression returns void
+ return true;
+
+ // In C++11, last_expr can be a LValueToRvalue implicit cast. Strip that off
+ // if that's the case.
+
+ do {
+ ImplicitCastExpr *implicit_cast = dyn_cast<ImplicitCastExpr>(last_expr);
+
+ if (!implicit_cast)
+ break;
+
+ if (implicit_cast->getCastKind() != CK_LValueToRValue)
+ break;
+
+ last_expr = implicit_cast->getSubExpr();
+ } while (false);
+
+ // is_lvalue is used to record whether the expression returns an assignable
+ // Lvalue or an Rvalue. This is relevant because they are handled
+ // differently.
+ //
+ // For Lvalues
+ //
+ // - In AST result synthesis (here!) the expression E is transformed into an
+ // initialization
+ // T *$__lldb_expr_result_ptr = &E.
+ //
+ // - In structure allocation, a pointer-sized slot is allocated in the
+ // struct that is to be
+ // passed into the expression.
+ //
+ // - In IR transformations, reads and writes to $__lldb_expr_result_ptr are
+ // redirected at
+ // an entry in the struct ($__lldb_arg) passed into the expression.
+ // (Other persistent
+ // variables are treated similarly, having been materialized as
+ // references, but in those
+ // cases the value of the reference itself is never modified.)
+ //
+ // - During materialization, $0 (the result persistent variable) is ignored.
+ //
+ // - During dematerialization, $0 is marked up as a load address with value
+ // equal to the
+ // contents of the structure entry.
+ //
+ // For Rvalues
+ //
+ // - In AST result synthesis the expression E is transformed into an
+ // initialization
+ // static T $__lldb_expr_result = E.
+ //
+ // - In structure allocation, a pointer-sized slot is allocated in the
+ // struct that is to be
+ // passed into the expression.
+ //
+ // - In IR transformations, an instruction is inserted at the beginning of
+ // the function to
+ // dereference the pointer resident in the slot. Reads and writes to
+ // $__lldb_expr_result
+ // are redirected at that dereferenced version. Guard variables for the
+ // static variable
+ // are excised.
+ //
+ // - During materialization, $0 (the result persistent variable) is
+ // populated with the location
+ // of a newly-allocated area of memory.
+ //
+ // - During dematerialization, $0 is ignored.
+
+ bool is_lvalue = last_expr->getValueKind() == VK_LValue &&
+ last_expr->getObjectKind() == OK_Ordinary;
+
+ QualType expr_qual_type = last_expr->getType();
+ const clang::Type *expr_type = expr_qual_type.getTypePtr();
+
+ if (!expr_type)
+ return false;
+
+ if (expr_type->isVoidType())
+ return true;
+
+ if (log) {
+ std::string s = expr_qual_type.getAsString();
+
+ log->Printf("Last statement is an %s with type: %s",
+ (is_lvalue ? "lvalue" : "rvalue"), s.c_str());
+ }
+
+ clang::VarDecl *result_decl = nullptr;
+
+ if (is_lvalue) {
+ IdentifierInfo *result_ptr_id;
+
+ if (expr_type->isFunctionType())
+ result_ptr_id =
+ &Ctx.Idents.get("$__lldb_expr_result"); // functions actually should
+ // be treated like function
+ // pointers
+ else
+ result_ptr_id = &Ctx.Idents.get("$__lldb_expr_result_ptr");
+
+ m_sema->RequireCompleteType(SourceLocation(), expr_qual_type,
+ clang::diag::err_incomplete_type);
+
+ QualType ptr_qual_type;
+
+ if (expr_qual_type->getAs<ObjCObjectType>() != nullptr)
+ ptr_qual_type = Ctx.getObjCObjectPointerType(expr_qual_type);
+ else
+ ptr_qual_type = Ctx.getPointerType(expr_qual_type);
+
+ result_decl =
+ VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(),
+ result_ptr_id, ptr_qual_type, nullptr, SC_Static);
+
+ if (!result_decl)
+ return false;
+
+ ExprResult address_of_expr =
+ m_sema->CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, last_expr);
+ if (address_of_expr.get())
+ m_sema->AddInitializerToDecl(result_decl, address_of_expr.get(), true);
+ else
+ return false;
+ } else {
+ IdentifierInfo &result_id = Ctx.Idents.get("$__lldb_expr_result");
+
+ result_decl =
+ VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(), &result_id,
+ expr_qual_type, nullptr, SC_Static);
+
+ if (!result_decl)
+ return false;
+
+ m_sema->AddInitializerToDecl(result_decl, last_expr, true);
+ }
+
+ DC->addDecl(result_decl);
+
+ ///////////////////////////////
+ // call AddInitializerToDecl
+ //
+
+ // m_sema->AddInitializerToDecl(result_decl, last_expr);
+
+ /////////////////////////////////
+ // call ConvertDeclToDeclGroup
+ //
+
+ Sema::DeclGroupPtrTy result_decl_group_ptr;
+
+ result_decl_group_ptr = m_sema->ConvertDeclToDeclGroup(result_decl);
+
+ ////////////////////////
+ // call ActOnDeclStmt
+ //
+
+ StmtResult result_initialization_stmt_result(m_sema->ActOnDeclStmt(
+ result_decl_group_ptr, SourceLocation(), SourceLocation()));
+
+ ////////////////////////////////////////////////
+ // replace the old statement with the new one
+ //
+
+ *last_stmt_ptr =
+ reinterpret_cast<Stmt *>(result_initialization_stmt_result.get());
+
+ return true;
+}
+
+void ASTResultSynthesizer::HandleTranslationUnit(ASTContext &Ctx) {
+ if (m_passthrough)
+ m_passthrough->HandleTranslationUnit(Ctx);
+}
+
+void ASTResultSynthesizer::RecordPersistentTypes(DeclContext *FunDeclCtx) {
+ typedef DeclContext::specific_decl_iterator<TypeDecl> TypeDeclIterator;
+
+ for (TypeDeclIterator i = TypeDeclIterator(FunDeclCtx->decls_begin()),
+ e = TypeDeclIterator(FunDeclCtx->decls_end());
+ i != e; ++i) {
+ MaybeRecordPersistentType(*i);
+ }
+}
+
+void ASTResultSynthesizer::MaybeRecordPersistentType(TypeDecl *D) {
+ if (!D->getIdentifier())
+ return;
+
+ StringRef name = D->getName();
+
+ if (name.size() == 0 || name[0] != '$')
+ return;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ ConstString name_cs(name.str().c_str());
+
+ if (log)
+ log->Printf("Recording persistent type %s\n", name_cs.GetCString());
+
+ m_decls.push_back(D);
+}
+
+void ASTResultSynthesizer::RecordPersistentDecl(NamedDecl *D) {
+ lldbassert(m_top_level);
+
+ if (!D->getIdentifier())
+ return;
+
+ StringRef name = D->getName();
+
+ if (name.size() == 0)
+ return;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ ConstString name_cs(name.str().c_str());
+
+ if (log)
+ log->Printf("Recording persistent decl %s\n", name_cs.GetCString());
+
+ m_decls.push_back(D);
+}
+
+void ASTResultSynthesizer::CommitPersistentDecls() {
+ for (clang::NamedDecl *decl : m_decls) {
+ StringRef name = decl->getName();
+ ConstString name_cs(name.str().c_str());
+
+ Decl *D_scratch = m_target.GetClangASTImporter()->DeportDecl(
+ m_target.GetScratchClangASTContext()->getASTContext(), m_ast_context,
+ decl);
+
+ if (!D_scratch) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ std::string s;
+ llvm::raw_string_ostream ss(s);
+ decl->dump(ss);
+ ss.flush();
+
+ log->Printf("Couldn't commit persistent decl: %s\n", s.c_str());
+ }
+
+ continue;
+ }
+
+ if (NamedDecl *NamedDecl_scratch = dyn_cast<NamedDecl>(D_scratch))
+ llvm::cast<ClangPersistentVariables>(
+ m_target.GetPersistentExpressionStateForLanguage(
+ lldb::eLanguageTypeC))
+ ->RegisterPersistentDecl(name_cs, NamedDecl_scratch);
+ }
+}
+
+void ASTResultSynthesizer::HandleTagDeclDefinition(TagDecl *D) {
+ if (m_passthrough)
+ m_passthrough->HandleTagDeclDefinition(D);
+}
+
+void ASTResultSynthesizer::CompleteTentativeDefinition(VarDecl *D) {
+ if (m_passthrough)
+ m_passthrough->CompleteTentativeDefinition(D);
+}
+
+void ASTResultSynthesizer::HandleVTable(CXXRecordDecl *RD) {
+ if (m_passthrough)
+ m_passthrough->HandleVTable(RD);
+}
+
+void ASTResultSynthesizer::PrintStats() {
+ if (m_passthrough)
+ m_passthrough->PrintStats();
+}
+
+void ASTResultSynthesizer::InitializeSema(Sema &S) {
+ m_sema = &S;
+
+ if (m_passthrough_sema)
+ m_passthrough_sema->InitializeSema(S);
+}
+
+void ASTResultSynthesizer::ForgetSema() {
+ m_sema = nullptr;
+
+ if (m_passthrough_sema)
+ m_passthrough_sema->ForgetSema();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.h
new file mode 100644
index 000000000000..670ba6dce72e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.h
@@ -0,0 +1,173 @@
+//===-- ASTResultSynthesizer.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ASTResultSynthesizer_h_
+#define liblldb_ASTResultSynthesizer_h_
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Target/Target.h"
+#include "clang/Sema/SemaConsumer.h"
+
+namespace lldb_private {
+
+/// \class ASTResultSynthesizer ASTResultSynthesizer.h
+/// "lldb/Expression/ASTResultSynthesizer.h" Adds a result variable
+/// declaration to the ASTs for an expression.
+///
+/// Users expect the expression "i + 3" to return a result, even if a result
+/// variable wasn't specifically declared. To fulfil this requirement, LLDB
+/// adds a result variable to the expression, transforming it to "int
+/// $__lldb_expr_result = i + 3." The IR transformers ensure that the
+/// resulting variable is mapped to the right piece of memory.
+/// ASTResultSynthesizer's job is to add the variable and its initialization
+/// to the ASTs for the expression, and it does so by acting as a SemaConsumer
+/// for Clang.
+class ASTResultSynthesizer : public clang::SemaConsumer {
+public:
+ /// Constructor
+ ///
+ /// \param[in] passthrough
+ /// Since the ASTs must typically go through to the Clang code generator
+ /// in order to produce LLVM IR, this SemaConsumer must allow them to
+ /// pass to the next step in the chain after processing. Passthrough is
+ /// the next ASTConsumer, or NULL if none is required.
+ ///
+ /// \param[in] top_level
+ /// If true, register all top-level Decls and don't try to handle the
+ /// main function.
+ ///
+ /// \param[in] target
+ /// The target, which contains the persistent variable store and the
+ /// AST importer.
+ ASTResultSynthesizer(clang::ASTConsumer *passthrough, bool top_level,
+ Target &target);
+
+ /// Destructor
+ ~ASTResultSynthesizer() override;
+
+ /// Link this consumer with a particular AST context
+ ///
+ /// \param[in] Context
+ /// This AST context will be used for types and identifiers, and also
+ /// forwarded to the passthrough consumer, if one exists.
+ void Initialize(clang::ASTContext &Context) override;
+
+ /// Examine a list of Decls to find the function $__lldb_expr and transform
+ /// its code
+ ///
+ /// \param[in] D
+ /// The list of Decls to search. These may contain LinkageSpecDecls,
+ /// which need to be searched recursively. That job falls to
+ /// TransformTopLevelDecl.
+ bool HandleTopLevelDecl(clang::DeclGroupRef D) override;
+
+ /// Passthrough stub
+ void HandleTranslationUnit(clang::ASTContext &Ctx) override;
+
+ /// Passthrough stub
+ void HandleTagDeclDefinition(clang::TagDecl *D) override;
+
+ /// Passthrough stub
+ void CompleteTentativeDefinition(clang::VarDecl *D) override;
+
+ /// Passthrough stub
+ void HandleVTable(clang::CXXRecordDecl *RD) override;
+
+ /// Passthrough stub
+ void PrintStats() override;
+
+ /// Set the Sema object to use when performing transforms, and pass it on
+ ///
+ /// \param[in] S
+ /// The Sema to use. Because Sema isn't externally visible, this class
+ /// casts it to an Action for actual use.
+ void InitializeSema(clang::Sema &S) override;
+
+ /// Reset the Sema to NULL now that transformations are done
+ void ForgetSema() override;
+
+ /// The parse has succeeded, so record its persistent decls
+ void CommitPersistentDecls();
+
+private:
+ /// Hunt the given Decl for FunctionDecls named $__lldb_expr, recursing as
+ /// necessary through LinkageSpecDecls, and calling SynthesizeResult on
+ /// anything that was found
+ ///
+ /// \param[in] D
+ /// The Decl to hunt.
+ void TransformTopLevelDecl(clang::Decl *D);
+
+ /// Process an Objective-C method and produce the result variable and
+ /// initialization
+ ///
+ /// \param[in] MethodDecl
+ /// The method to process.
+ bool SynthesizeObjCMethodResult(clang::ObjCMethodDecl *MethodDecl);
+
+ /// Process a function and produce the result variable and initialization
+ ///
+ /// \param[in] FunDecl
+ /// The function to process.
+ bool SynthesizeFunctionResult(clang::FunctionDecl *FunDecl);
+
+ /// Process a function body and produce the result variable and
+ /// initialization
+ ///
+ /// \param[in] Body
+ /// The body of the function.
+ ///
+ /// \param[in] DC
+ /// The DeclContext of the function, into which the result variable
+ /// is inserted.
+ bool SynthesizeBodyResult(clang::CompoundStmt *Body, clang::DeclContext *DC);
+
+ /// Given a DeclContext for a function or method, find all types declared in
+ /// the context and record any persistent types found.
+ ///
+ /// \param[in] FunDeclCtx
+ /// The context for the function to process.
+ void RecordPersistentTypes(clang::DeclContext *FunDeclCtx);
+
+ /// Given a TypeDecl, if it declares a type whose name starts with a dollar
+ /// sign, register it as a pointer type in the target's scratch
+ /// AST context.
+ ///
+ /// \param[in] Body
+ /// The body of the function.
+ void MaybeRecordPersistentType(clang::TypeDecl *D);
+
+ /// Given a NamedDecl, register it as a pointer type in the target's scratch
+ /// AST context.
+ ///
+ /// \param[in] Body
+ /// The body of the function.
+ void RecordPersistentDecl(clang::NamedDecl *D);
+
+ clang::ASTContext
+ *m_ast_context; ///< The AST context to use for identifiers and types.
+ clang::ASTConsumer *m_passthrough; ///< The ASTConsumer down the chain, for
+ ///passthrough. NULL if it's a
+ ///SemaConsumer.
+ clang::SemaConsumer *m_passthrough_sema; ///< The SemaConsumer down the chain,
+ ///for passthrough. NULL if it's an
+ ///ASTConsumer.
+
+ std::vector<clang::NamedDecl *> m_decls; ///< Persistent declarations to
+ ///register assuming the expression
+ ///succeeds.
+
+ Target &m_target; ///< The target, which contains the persistent variable
+ ///store and the
+ clang::Sema *m_sema; ///< The Sema to use.
+ bool m_top_level;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ASTResultSynthesizer_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.cpp
new file mode 100644
index 000000000000..190eacaa2b62
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.cpp
@@ -0,0 +1,185 @@
+//===-- ASTStructExtractor.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTStructExtractor.h"
+
+#include "lldb/Utility/Log.h"
+#include "stdlib.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace lldb_private;
+
+ASTStructExtractor::ASTStructExtractor(ASTConsumer *passthrough,
+ const char *struct_name,
+ ClangFunctionCaller &function)
+ : m_ast_context(nullptr), m_passthrough(passthrough),
+ m_passthrough_sema(nullptr), m_sema(nullptr), m_action(nullptr),
+ m_function(function), m_struct_name(struct_name) {
+ if (!m_passthrough)
+ return;
+
+ m_passthrough_sema = dyn_cast<SemaConsumer>(passthrough);
+}
+
+ASTStructExtractor::~ASTStructExtractor() {}
+
+void ASTStructExtractor::Initialize(ASTContext &Context) {
+ m_ast_context = &Context;
+
+ if (m_passthrough)
+ m_passthrough->Initialize(Context);
+}
+
+void ASTStructExtractor::ExtractFromFunctionDecl(FunctionDecl *F) {
+ if (!F->hasBody())
+ return;
+
+ Stmt *body_stmt = F->getBody();
+ CompoundStmt *body_compound_stmt = dyn_cast<CompoundStmt>(body_stmt);
+
+ if (!body_compound_stmt)
+ return; // do we have to handle this?
+
+ RecordDecl *struct_decl = nullptr;
+
+ StringRef desired_name(m_struct_name);
+
+ for (CompoundStmt::const_body_iterator bi = body_compound_stmt->body_begin(),
+ be = body_compound_stmt->body_end();
+ bi != be; ++bi) {
+ Stmt *curr_stmt = *bi;
+ DeclStmt *curr_decl_stmt = dyn_cast<DeclStmt>(curr_stmt);
+ if (!curr_decl_stmt)
+ continue;
+ DeclGroupRef decl_group = curr_decl_stmt->getDeclGroup();
+ for (Decl *candidate_decl : decl_group) {
+ RecordDecl *candidate_record_decl = dyn_cast<RecordDecl>(candidate_decl);
+ if (!candidate_record_decl)
+ continue;
+ if (candidate_record_decl->getName() == desired_name) {
+ struct_decl = candidate_record_decl;
+ break;
+ }
+ }
+ if (struct_decl)
+ break;
+ }
+
+ if (!struct_decl)
+ return;
+
+ const ASTRecordLayout *struct_layout(
+ &m_ast_context->getASTRecordLayout(struct_decl));
+
+ if (!struct_layout)
+ return;
+
+ m_function.m_struct_size =
+ struct_layout->getSize()
+ .getQuantity(); // TODO Store m_struct_size as CharUnits
+ m_function.m_return_offset =
+ struct_layout->getFieldOffset(struct_layout->getFieldCount() - 1) / 8;
+ m_function.m_return_size =
+ struct_layout->getDataSize().getQuantity() - m_function.m_return_offset;
+
+ for (unsigned field_index = 0, num_fields = struct_layout->getFieldCount();
+ field_index < num_fields; ++field_index) {
+ m_function.m_member_offsets.push_back(
+ struct_layout->getFieldOffset(field_index) / 8);
+ }
+
+ m_function.m_struct_valid = true;
+}
+
+void ASTStructExtractor::ExtractFromTopLevelDecl(Decl *D) {
+ LinkageSpecDecl *linkage_spec_decl = dyn_cast<LinkageSpecDecl>(D);
+
+ if (linkage_spec_decl) {
+ RecordDecl::decl_iterator decl_iterator;
+
+ for (decl_iterator = linkage_spec_decl->decls_begin();
+ decl_iterator != linkage_spec_decl->decls_end(); ++decl_iterator) {
+ ExtractFromTopLevelDecl(*decl_iterator);
+ }
+ }
+
+ FunctionDecl *function_decl = dyn_cast<FunctionDecl>(D);
+
+ if (m_ast_context && function_decl &&
+ !m_function.m_wrapper_function_name.compare(
+ function_decl->getNameAsString())) {
+ ExtractFromFunctionDecl(function_decl);
+ }
+}
+
+bool ASTStructExtractor::HandleTopLevelDecl(DeclGroupRef D) {
+ DeclGroupRef::iterator decl_iterator;
+
+ for (decl_iterator = D.begin(); decl_iterator != D.end(); ++decl_iterator) {
+ Decl *decl = *decl_iterator;
+
+ ExtractFromTopLevelDecl(decl);
+ }
+
+ if (m_passthrough)
+ return m_passthrough->HandleTopLevelDecl(D);
+ return true;
+}
+
+void ASTStructExtractor::HandleTranslationUnit(ASTContext &Ctx) {
+ if (m_passthrough)
+ m_passthrough->HandleTranslationUnit(Ctx);
+}
+
+void ASTStructExtractor::HandleTagDeclDefinition(TagDecl *D) {
+ if (m_passthrough)
+ m_passthrough->HandleTagDeclDefinition(D);
+}
+
+void ASTStructExtractor::CompleteTentativeDefinition(VarDecl *D) {
+ if (m_passthrough)
+ m_passthrough->CompleteTentativeDefinition(D);
+}
+
+void ASTStructExtractor::HandleVTable(CXXRecordDecl *RD) {
+ if (m_passthrough)
+ m_passthrough->HandleVTable(RD);
+}
+
+void ASTStructExtractor::PrintStats() {
+ if (m_passthrough)
+ m_passthrough->PrintStats();
+}
+
+void ASTStructExtractor::InitializeSema(Sema &S) {
+ m_sema = &S;
+ m_action = reinterpret_cast<Action *>(m_sema);
+
+ if (m_passthrough_sema)
+ m_passthrough_sema->InitializeSema(S);
+}
+
+void ASTStructExtractor::ForgetSema() {
+ m_sema = nullptr;
+ m_action = nullptr;
+
+ if (m_passthrough_sema)
+ m_passthrough_sema->ForgetSema();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.h
new file mode 100644
index 000000000000..7aef2e254e1f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.h
@@ -0,0 +1,134 @@
+//===-- ASTStructExtractor.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ASTStructExtractor_h_
+#define liblldb_ASTStructExtractor_h_
+
+#include "ClangExpressionVariable.h"
+#include "ClangFunctionCaller.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "clang/Sema/SemaConsumer.h"
+
+namespace lldb_private {
+
+/// \class ASTStructExtractor ASTStructExtractor.h
+/// "lldb/Expression/ASTStructExtractor.h" Extracts and describes the argument
+/// structure for a wrapped function.
+///
+/// This pass integrates with ClangFunctionCaller, which calls functions with
+/// custom sets of arguments. To avoid having to implement the full calling
+/// convention for the target's architecture, ClangFunctionCaller writes a
+/// simple wrapper function that takes a pointer to an argument structure that
+/// contains room for the address of the function to be called, the values of
+/// all its arguments, and room for the function's return value.
+///
+/// The definition of this struct is itself in the body of the wrapper
+/// function, so Clang does the structure layout itself. ASTStructExtractor
+/// reads through the AST for the wrapper function and finds the struct.
+class ASTStructExtractor : public clang::SemaConsumer {
+public:
+ /// Constructor
+ ///
+ /// \param[in] passthrough
+ /// Since the ASTs must typically go through to the Clang code generator
+ /// in order to produce LLVM IR, this SemaConsumer must allow them to
+ /// pass to the next step in the chain after processing. Passthrough is
+ /// the next ASTConsumer, or NULL if none is required.
+ ///
+ /// \param[in] struct_name
+ /// The name of the structure to extract from the wrapper function.
+ ///
+ /// \param[in] function
+ /// The caller object whose members should be populated with information
+ /// about the argument struct. ClangFunctionCaller friends
+ /// ASTStructExtractor
+ /// for this purpose.
+ ASTStructExtractor(clang::ASTConsumer *passthrough, const char *struct_name,
+ ClangFunctionCaller &function);
+
+ /// Destructor
+ ~ASTStructExtractor() override;
+
+ /// Link this consumer with a particular AST context
+ ///
+ /// \param[in] Context
+ /// This AST context will be used for types and identifiers, and also
+ /// forwarded to the passthrough consumer, if one exists.
+ void Initialize(clang::ASTContext &Context) override;
+
+ /// Examine a list of Decls to find the function $__lldb_expr and transform
+ /// its code
+ ///
+ /// \param[in] D
+ /// The list of Decls to search. These may contain LinkageSpecDecls,
+ /// which need to be searched recursively. That job falls to
+ /// TransformTopLevelDecl.
+ bool HandleTopLevelDecl(clang::DeclGroupRef D) override;
+
+ /// Passthrough stub
+ void HandleTranslationUnit(clang::ASTContext &Ctx) override;
+
+ /// Passthrough stub
+ void HandleTagDeclDefinition(clang::TagDecl *D) override;
+
+ /// Passthrough stub
+ void CompleteTentativeDefinition(clang::VarDecl *D) override;
+
+ /// Passthrough stub
+ void HandleVTable(clang::CXXRecordDecl *RD) override;
+
+ /// Passthrough stub
+ void PrintStats() override;
+
+ /// Set the Sema object to use when performing transforms, and pass it on
+ ///
+ /// \param[in] S
+ /// The Sema to use. Because Sema isn't externally visible, this class
+ /// casts it to an Action for actual use.
+ void InitializeSema(clang::Sema &S) override;
+
+ /// Reset the Sema to NULL now that transformations are done
+ void ForgetSema() override;
+
+private:
+ /// Hunt the given FunctionDecl for the argument struct and place
+ /// information about it into m_function
+ ///
+ /// \param[in] F
+ /// The FunctionDecl to hunt.
+ void ExtractFromFunctionDecl(clang::FunctionDecl *F);
+
+ /// Hunt the given Decl for FunctionDecls named the same as the wrapper
+ /// function name, recursing as necessary through LinkageSpecDecls, and
+ /// calling ExtractFromFunctionDecl on anything that was found
+ ///
+ /// \param[in] D
+ /// The Decl to hunt.
+ void ExtractFromTopLevelDecl(clang::Decl *D);
+
+ clang::ASTContext
+ *m_ast_context; ///< The AST context to use for identifiers and types.
+ clang::ASTConsumer *m_passthrough; ///< The ASTConsumer down the chain, for
+ ///passthrough. NULL if it's a
+ ///SemaConsumer.
+ clang::SemaConsumer *m_passthrough_sema; ///< The SemaConsumer down the chain,
+ ///for passthrough. NULL if it's an
+ ///ASTConsumer.
+ clang::Sema *m_sema; ///< The Sema to use.
+ clang::Action
+ *m_action; ///< The Sema to use, cast to an Action so it's usable.
+
+ ClangFunctionCaller &m_function; ///< The function to populate with
+ ///information about the argument structure.
+ std::string m_struct_name; ///< The name of the structure to extract.
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ASTStructExtractor_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp
new file mode 100644
index 000000000000..bbdf4e31c5a4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp
@@ -0,0 +1,26 @@
+//===-- ASTUtils.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTUtils.h"
+
+lldb_private::ExternalASTSourceWrapper::~ExternalASTSourceWrapper() {}
+
+void lldb_private::ExternalASTSourceWrapper::PrintStats() {
+ m_Source->PrintStats();
+}
+
+lldb_private::ASTConsumerForwarder::~ASTConsumerForwarder() {}
+
+void lldb_private::ASTConsumerForwarder::PrintStats() { m_c->PrintStats(); }
+
+lldb_private::SemaSourceWithPriorities::~SemaSourceWithPriorities() {}
+
+void lldb_private::SemaSourceWithPriorities::PrintStats() {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->PrintStats();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h
new file mode 100644
index 000000000000..d429e8c3855f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h
@@ -0,0 +1,579 @@
+//===-- ASTUtils.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ASTUtils_h_
+#define liblldb_ASTUtils_h_
+
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/MultiplexExternalSemaSource.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaConsumer.h"
+
+namespace lldb_private {
+
+/// Wraps an ExternalASTSource into an ExternalSemaSource. Doesn't take
+/// ownership of the provided source.
+class ExternalASTSourceWrapper : public clang::ExternalSemaSource {
+ ExternalASTSource *m_Source;
+
+public:
+ ExternalASTSourceWrapper(ExternalASTSource *Source) : m_Source(Source) {
+ assert(m_Source && "Can't wrap nullptr ExternalASTSource");
+ }
+
+ ~ExternalASTSourceWrapper() override;
+
+ clang::Decl *GetExternalDecl(uint32_t ID) override {
+ return m_Source->GetExternalDecl(ID);
+ }
+
+ clang::Selector GetExternalSelector(uint32_t ID) override {
+ return m_Source->GetExternalSelector(ID);
+ }
+
+ uint32_t GetNumExternalSelectors() override {
+ return m_Source->GetNumExternalSelectors();
+ }
+
+ clang::Stmt *GetExternalDeclStmt(uint64_t Offset) override {
+ return m_Source->GetExternalDeclStmt(Offset);
+ }
+
+ clang::CXXCtorInitializer **
+ GetExternalCXXCtorInitializers(uint64_t Offset) override {
+ return m_Source->GetExternalCXXCtorInitializers(Offset);
+ }
+
+ clang::CXXBaseSpecifier *
+ GetExternalCXXBaseSpecifiers(uint64_t Offset) override {
+ return m_Source->GetExternalCXXBaseSpecifiers(Offset);
+ }
+
+ void updateOutOfDateIdentifier(clang::IdentifierInfo &II) override {
+ m_Source->updateOutOfDateIdentifier(II);
+ }
+
+ bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
+ clang::DeclarationName Name) override {
+ return m_Source->FindExternalVisibleDeclsByName(DC, Name);
+ }
+
+ void completeVisibleDeclsMap(const clang::DeclContext *DC) override {
+ m_Source->completeVisibleDeclsMap(DC);
+ }
+
+ clang::Module *getModule(unsigned ID) override {
+ return m_Source->getModule(ID);
+ }
+
+ llvm::Optional<ASTSourceDescriptor>
+ getSourceDescriptor(unsigned ID) override {
+ return m_Source->getSourceDescriptor(ID);
+ }
+
+ ExtKind hasExternalDefinitions(const clang::Decl *D) override {
+ return m_Source->hasExternalDefinitions(D);
+ }
+
+ void FindExternalLexicalDecls(
+ const clang::DeclContext *DC,
+ llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
+ llvm::SmallVectorImpl<clang::Decl *> &Result) override {
+ m_Source->FindExternalLexicalDecls(DC, IsKindWeWant, Result);
+ }
+
+ void
+ FindFileRegionDecls(clang::FileID File, unsigned Offset, unsigned Length,
+ llvm::SmallVectorImpl<clang::Decl *> &Decls) override {
+ m_Source->FindFileRegionDecls(File, Offset, Length, Decls);
+ }
+
+ void CompleteRedeclChain(const clang::Decl *D) override {
+ m_Source->CompleteRedeclChain(D);
+ }
+
+ void CompleteType(clang::TagDecl *Tag) override {
+ m_Source->CompleteType(Tag);
+ }
+
+ void CompleteType(clang::ObjCInterfaceDecl *Class) override {
+ m_Source->CompleteType(Class);
+ }
+
+ void ReadComments() override { m_Source->ReadComments(); }
+
+ void StartedDeserializing() override { m_Source->StartedDeserializing(); }
+
+ void FinishedDeserializing() override { m_Source->FinishedDeserializing(); }
+
+ void StartTranslationUnit(clang::ASTConsumer *Consumer) override {
+ m_Source->StartTranslationUnit(Consumer);
+ }
+
+ void PrintStats() override;
+
+ bool layoutRecordType(
+ const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &BaseOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &VirtualBaseOffsets) override {
+ return m_Source->layoutRecordType(Record, Size, Alignment, FieldOffsets,
+ BaseOffsets, VirtualBaseOffsets);
+ }
+};
+
+/// Wraps an ASTConsumer into an SemaConsumer. Doesn't take ownership of the
+/// provided consumer. If the provided ASTConsumer is also a SemaConsumer,
+/// the wrapper will also forward SemaConsumer functions.
+class ASTConsumerForwarder : public clang::SemaConsumer {
+ clang::ASTConsumer *m_c;
+ clang::SemaConsumer *m_sc;
+
+public:
+ ASTConsumerForwarder(clang::ASTConsumer *c) : m_c(c) {
+ m_sc = llvm::dyn_cast<clang::SemaConsumer>(m_c);
+ }
+
+ ~ASTConsumerForwarder() override;
+
+ void Initialize(clang::ASTContext &Context) override {
+ m_c->Initialize(Context);
+ }
+
+ bool HandleTopLevelDecl(clang::DeclGroupRef D) override {
+ return m_c->HandleTopLevelDecl(D);
+ }
+
+ void HandleInlineFunctionDefinition(clang::FunctionDecl *D) override {
+ m_c->HandleInlineFunctionDefinition(D);
+ }
+
+ void HandleInterestingDecl(clang::DeclGroupRef D) override {
+ m_c->HandleInterestingDecl(D);
+ }
+
+ void HandleTranslationUnit(clang::ASTContext &Ctx) override {
+ m_c->HandleTranslationUnit(Ctx);
+ }
+
+ void HandleTagDeclDefinition(clang::TagDecl *D) override {
+ m_c->HandleTagDeclDefinition(D);
+ }
+
+ void HandleTagDeclRequiredDefinition(const clang::TagDecl *D) override {
+ m_c->HandleTagDeclRequiredDefinition(D);
+ }
+
+ void HandleCXXImplicitFunctionInstantiation(clang::FunctionDecl *D) override {
+ m_c->HandleCXXImplicitFunctionInstantiation(D);
+ }
+
+ void HandleTopLevelDeclInObjCContainer(clang::DeclGroupRef D) override {
+ m_c->HandleTopLevelDeclInObjCContainer(D);
+ }
+
+ void HandleImplicitImportDecl(clang::ImportDecl *D) override {
+ m_c->HandleImplicitImportDecl(D);
+ }
+
+ void CompleteTentativeDefinition(clang::VarDecl *D) override {
+ m_c->CompleteTentativeDefinition(D);
+ }
+
+ void AssignInheritanceModel(clang::CXXRecordDecl *RD) override {
+ m_c->AssignInheritanceModel(RD);
+ }
+
+ void HandleCXXStaticMemberVarInstantiation(clang::VarDecl *D) override {
+ m_c->HandleCXXStaticMemberVarInstantiation(D);
+ }
+
+ void HandleVTable(clang::CXXRecordDecl *RD) override {
+ m_c->HandleVTable(RD);
+ }
+
+ clang::ASTMutationListener *GetASTMutationListener() override {
+ return m_c->GetASTMutationListener();
+ }
+
+ clang::ASTDeserializationListener *GetASTDeserializationListener() override {
+ return m_c->GetASTDeserializationListener();
+ }
+
+ void PrintStats() override;
+
+ void InitializeSema(clang::Sema &S) override {
+ if (m_sc)
+ m_sc->InitializeSema(S);
+ }
+
+ /// Inform the semantic consumer that Sema is no longer available.
+ void ForgetSema() override {
+ if (m_sc)
+ m_sc->ForgetSema();
+ }
+
+ bool shouldSkipFunctionBody(clang::Decl *D) override {
+ return m_c->shouldSkipFunctionBody(D);
+ }
+};
+
+/// A ExternalSemaSource multiplexer that prioritizes its sources.
+///
+/// This ExternalSemaSource will forward all requests to its attached sources.
+/// However, unlike a normal multiplexer it will not forward a request to all
+/// sources, but instead give priority to certain sources. If a source with a
+/// higher priority can fulfill a request, all sources with a lower priority
+/// will not receive the request.
+///
+/// This class is mostly use to multiplex between sources of different
+/// 'quality', e.g. a C++ modules and debug information. The C++ module will
+/// provide more accurate replies to the requests, but might not be able to
+/// answer all requests. The debug information will be used as a fallback then
+/// to provide information that is not in the C++ module.
+class SemaSourceWithPriorities : public clang::ExternalSemaSource {
+
+private:
+ /// The sources ordered in decreasing priority.
+ llvm::SmallVector<clang::ExternalSemaSource *, 2> Sources;
+
+public:
+ /// Construct a SemaSourceWithPriorities with a 'high quality' source that
+ /// has the higher priority and a 'low quality' source that will be used
+ /// as a fallback.
+ SemaSourceWithPriorities(clang::ExternalSemaSource &high_quality_source,
+ clang::ExternalSemaSource &low_quality_source) {
+ Sources.push_back(&high_quality_source);
+ Sources.push_back(&low_quality_source);
+ }
+
+ ~SemaSourceWithPriorities() override;
+
+ void addSource(clang::ExternalSemaSource &source) {
+ Sources.push_back(&source);
+ }
+
+ //===--------------------------------------------------------------------===//
+ // ExternalASTSource.
+ //===--------------------------------------------------------------------===//
+
+ clang::Decl *GetExternalDecl(uint32_t ID) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (clang::Decl *Result = Sources[i]->GetExternalDecl(ID))
+ return Result;
+ return nullptr;
+ }
+
+ void CompleteRedeclChain(const clang::Decl *D) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->CompleteRedeclChain(D);
+ }
+
+ clang::Selector GetExternalSelector(uint32_t ID) override {
+ clang::Selector Sel;
+ for (size_t i = 0; i < Sources.size(); ++i) {
+ Sel = Sources[i]->GetExternalSelector(ID);
+ if (!Sel.isNull())
+ return Sel;
+ }
+ return Sel;
+ }
+
+ uint32_t GetNumExternalSelectors() override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (uint32_t total = Sources[i]->GetNumExternalSelectors())
+ return total;
+ return 0;
+ }
+
+ clang::Stmt *GetExternalDeclStmt(uint64_t Offset) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (clang::Stmt *Result = Sources[i]->GetExternalDeclStmt(Offset))
+ return Result;
+ return nullptr;
+ }
+
+ clang::CXXBaseSpecifier *
+ GetExternalCXXBaseSpecifiers(uint64_t Offset) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (clang::CXXBaseSpecifier *R =
+ Sources[i]->GetExternalCXXBaseSpecifiers(Offset))
+ return R;
+ return nullptr;
+ }
+
+ clang::CXXCtorInitializer **
+ GetExternalCXXCtorInitializers(uint64_t Offset) override {
+ for (auto *S : Sources)
+ if (auto *R = S->GetExternalCXXCtorInitializers(Offset))
+ return R;
+ return nullptr;
+ }
+
+ ExtKind hasExternalDefinitions(const clang::Decl *D) override {
+ for (const auto &S : Sources)
+ if (auto EK = S->hasExternalDefinitions(D))
+ if (EK != EK_ReplyHazy)
+ return EK;
+ return EK_ReplyHazy;
+ }
+
+ bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
+ clang::DeclarationName Name) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (Sources[i]->FindExternalVisibleDeclsByName(DC, Name))
+ return true;
+ return false;
+ }
+
+ void completeVisibleDeclsMap(const clang::DeclContext *DC) override {
+ // FIXME: Only one source should be able to complete the decls map.
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->completeVisibleDeclsMap(DC);
+ }
+
+ void FindExternalLexicalDecls(
+ const clang::DeclContext *DC,
+ llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
+ llvm::SmallVectorImpl<clang::Decl *> &Result) override {
+ for (size_t i = 0; i < Sources.size(); ++i) {
+ Sources[i]->FindExternalLexicalDecls(DC, IsKindWeWant, Result);
+ if (!Result.empty())
+ return;
+ }
+ }
+
+ void
+ FindFileRegionDecls(clang::FileID File, unsigned Offset, unsigned Length,
+ llvm::SmallVectorImpl<clang::Decl *> &Decls) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->FindFileRegionDecls(File, Offset, Length, Decls);
+ }
+
+ void CompleteType(clang::TagDecl *Tag) override {
+ while (!Tag->isCompleteDefinition())
+ for (size_t i = 0; i < Sources.size(); ++i) {
+ // FIXME: We are technically supposed to loop here too until
+ // Tag->isCompleteDefinition() is true, but if our low quality source
+ // is failing to complete the tag this code will deadlock.
+ Sources[i]->CompleteType(Tag);
+ if (Tag->isCompleteDefinition())
+ break;
+ }
+ }
+
+ void CompleteType(clang::ObjCInterfaceDecl *Class) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->CompleteType(Class);
+ }
+
+ void ReadComments() override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->ReadComments();
+ }
+
+ void StartedDeserializing() override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->StartedDeserializing();
+ }
+
+ void FinishedDeserializing() override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->FinishedDeserializing();
+ }
+
+ void StartTranslationUnit(clang::ASTConsumer *Consumer) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->StartTranslationUnit(Consumer);
+ }
+
+ void PrintStats() override;
+
+ clang::Module *getModule(unsigned ID) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (auto M = Sources[i]->getModule(ID))
+ return M;
+ return nullptr;
+ }
+
+ bool DeclIsFromPCHWithObjectFile(const clang::Decl *D) override {
+ for (auto *S : Sources)
+ if (S->DeclIsFromPCHWithObjectFile(D))
+ return true;
+ return false;
+ }
+
+ bool layoutRecordType(
+ const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &BaseOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &VirtualBaseOffsets) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (Sources[i]->layoutRecordType(Record, Size, Alignment, FieldOffsets,
+ BaseOffsets, VirtualBaseOffsets))
+ return true;
+ return false;
+ }
+
+ void getMemoryBufferSizes(MemoryBufferSizes &sizes) const override {
+ for (auto &Source : Sources)
+ Source->getMemoryBufferSizes(sizes);
+ }
+
+ //===--------------------------------------------------------------------===//
+ // ExternalSemaSource.
+ //===--------------------------------------------------------------------===//
+
+ void InitializeSema(clang::Sema &S) override {
+ for (auto &Source : Sources)
+ Source->InitializeSema(S);
+ }
+
+ void ForgetSema() override {
+ for (auto &Source : Sources)
+ Source->ForgetSema();
+ }
+
+ void ReadMethodPool(clang::Selector Sel) override {
+ for (auto &Source : Sources)
+ Source->ReadMethodPool(Sel);
+ }
+
+ void updateOutOfDateSelector(clang::Selector Sel) override {
+ for (auto &Source : Sources)
+ Source->updateOutOfDateSelector(Sel);
+ }
+
+ void ReadKnownNamespaces(
+ llvm::SmallVectorImpl<clang::NamespaceDecl *> &Namespaces) override {
+ for (auto &Source : Sources)
+ Source->ReadKnownNamespaces(Namespaces);
+ }
+
+ void ReadUndefinedButUsed(
+ llvm::MapVector<clang::NamedDecl *, clang::SourceLocation> &Undefined)
+ override {
+ for (auto &Source : Sources)
+ Source->ReadUndefinedButUsed(Undefined);
+ }
+
+ void ReadMismatchingDeleteExpressions(
+ llvm::MapVector<clang::FieldDecl *,
+ llvm::SmallVector<std::pair<clang::SourceLocation, bool>,
+ 4>> &Exprs) override {
+ for (auto &Source : Sources)
+ Source->ReadMismatchingDeleteExpressions(Exprs);
+ }
+
+ bool LookupUnqualified(clang::LookupResult &R, clang::Scope *S) override {
+ for (auto &Source : Sources) {
+ Source->LookupUnqualified(R, S);
+ if (!R.empty())
+ break;
+ }
+
+ return !R.empty();
+ }
+
+ void ReadTentativeDefinitions(
+ llvm::SmallVectorImpl<clang::VarDecl *> &Defs) override {
+ for (auto &Source : Sources)
+ Source->ReadTentativeDefinitions(Defs);
+ }
+
+ void ReadUnusedFileScopedDecls(
+ llvm::SmallVectorImpl<const clang::DeclaratorDecl *> &Decls) override {
+ for (auto &Source : Sources)
+ Source->ReadUnusedFileScopedDecls(Decls);
+ }
+
+ void ReadDelegatingConstructors(
+ llvm::SmallVectorImpl<clang::CXXConstructorDecl *> &Decls) override {
+ for (auto &Source : Sources)
+ Source->ReadDelegatingConstructors(Decls);
+ }
+
+ void ReadExtVectorDecls(
+ llvm::SmallVectorImpl<clang::TypedefNameDecl *> &Decls) override {
+ for (auto &Source : Sources)
+ Source->ReadExtVectorDecls(Decls);
+ }
+
+ void ReadUnusedLocalTypedefNameCandidates(
+ llvm::SmallSetVector<const clang::TypedefNameDecl *, 4> &Decls) override {
+ for (auto &Source : Sources)
+ Source->ReadUnusedLocalTypedefNameCandidates(Decls);
+ }
+
+ void ReadReferencedSelectors(
+ llvm::SmallVectorImpl<std::pair<clang::Selector, clang::SourceLocation>>
+ &Sels) override {
+ for (auto &Source : Sources)
+ Source->ReadReferencedSelectors(Sels);
+ }
+
+ void ReadWeakUndeclaredIdentifiers(
+ llvm::SmallVectorImpl<std::pair<clang::IdentifierInfo *, clang::WeakInfo>>
+ &WI) override {
+ for (auto &Source : Sources)
+ Source->ReadWeakUndeclaredIdentifiers(WI);
+ }
+
+ void ReadUsedVTables(
+ llvm::SmallVectorImpl<clang::ExternalVTableUse> &VTables) override {
+ for (auto &Source : Sources)
+ Source->ReadUsedVTables(VTables);
+ }
+
+ void ReadPendingInstantiations(
+ llvm::SmallVectorImpl<
+ std::pair<clang::ValueDecl *, clang::SourceLocation>> &Pending)
+ override {
+ for (auto &Source : Sources)
+ Source->ReadPendingInstantiations(Pending);
+ }
+
+ void ReadLateParsedTemplates(
+ llvm::MapVector<const clang::FunctionDecl *,
+ std::unique_ptr<clang::LateParsedTemplate>> &LPTMap)
+ override {
+ for (auto &Source : Sources)
+ Source->ReadLateParsedTemplates(LPTMap);
+ }
+
+ clang::TypoCorrection
+ CorrectTypo(const clang::DeclarationNameInfo &Typo, int LookupKind,
+ clang::Scope *S, clang::CXXScopeSpec *SS,
+ clang::CorrectionCandidateCallback &CCC,
+ clang::DeclContext *MemberContext, bool EnteringContext,
+ const clang::ObjCObjectPointerType *OPT) override {
+ for (auto &Source : Sources) {
+ if (clang::TypoCorrection C =
+ Source->CorrectTypo(Typo, LookupKind, S, SS, CCC,
+ MemberContext, EnteringContext, OPT))
+ return C;
+ }
+ return clang::TypoCorrection();
+ }
+
+ bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc,
+ clang::QualType T) override {
+ for (auto &Source : Sources) {
+ if (Source->MaybeDiagnoseMissingCompleteType(Loc, T))
+ return true;
+ }
+ return false;
+ }
+};
+
+} // namespace lldb_private
+#endif // liblldb_ASTUtils_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
new file mode 100644
index 000000000000..c5778f86bb62
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
@@ -0,0 +1,2237 @@
+//===-- ClangASTSource.cpp ---------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangASTSource.h"
+
+#include "ASTDumper.h"
+#include "ClangModulesDeclVendor.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompilerDeclContext.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TaggedASTType.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecordLayout.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include <memory>
+#include <vector>
+
+using namespace clang;
+using namespace lldb_private;
+
+// Scoped class that will remove an active lexical decl from the set when it
+// goes out of scope.
+namespace {
+class ScopedLexicalDeclEraser {
+public:
+ ScopedLexicalDeclEraser(std::set<const clang::Decl *> &decls,
+ const clang::Decl *decl)
+ : m_active_lexical_decls(decls), m_decl(decl) {}
+
+ ~ScopedLexicalDeclEraser() { m_active_lexical_decls.erase(m_decl); }
+
+private:
+ std::set<const clang::Decl *> &m_active_lexical_decls;
+ const clang::Decl *m_decl;
+};
+}
+
+ClangASTSource::ClangASTSource(const lldb::TargetSP &target)
+ : m_import_in_progress(false), m_lookups_enabled(false), m_target(target),
+ m_ast_context(nullptr), m_active_lexical_decls(), m_active_lookups() {
+ if (!target->GetUseModernTypeLookup()) {
+ m_ast_importer_sp = m_target->GetClangASTImporter();
+ }
+}
+
+void ClangASTSource::InstallASTContext(clang::ASTContext &ast_context,
+ clang::FileManager &file_manager,
+ bool is_shared_context) {
+ m_ast_context = &ast_context;
+ m_file_manager = &file_manager;
+ if (m_target->GetUseModernTypeLookup()) {
+ // Configure the ExternalASTMerger. The merger needs to be able to import
+ // types from any source that we would do lookups in, which includes the
+ // persistent AST context as well as the modules and Objective-C runtime
+ // AST contexts.
+
+ lldbassert(!m_merger_up);
+ clang::ExternalASTMerger::ImporterTarget target = {ast_context,
+ file_manager};
+ std::vector<clang::ExternalASTMerger::ImporterSource> sources;
+ for (lldb::ModuleSP module_sp : m_target->GetImages().Modules()) {
+ if (auto *module_ast_ctx = llvm::cast_or_null<ClangASTContext>(
+ module_sp->GetTypeSystemForLanguage(lldb::eLanguageTypeC))) {
+ lldbassert(module_ast_ctx->getASTContext());
+ lldbassert(module_ast_ctx->getFileManager());
+ sources.push_back({*module_ast_ctx->getASTContext(),
+ *module_ast_ctx->getFileManager(),
+ module_ast_ctx->GetOriginMap()
+ });
+ }
+ }
+
+ do {
+ lldb::ProcessSP process(m_target->GetProcessSP());
+
+ if (!process)
+ break;
+
+ ObjCLanguageRuntime *language_runtime(ObjCLanguageRuntime::Get(*process));
+
+ if (!language_runtime)
+ break;
+
+ DeclVendor *runtime_decl_vendor = language_runtime->GetDeclVendor();
+
+ if (!runtime_decl_vendor)
+ break;
+
+ sources.push_back(runtime_decl_vendor->GetImporterSource());
+ } while (false);
+
+ do {
+ DeclVendor *modules_decl_vendor =
+ m_target->GetClangModulesDeclVendor();
+
+ if (!modules_decl_vendor)
+ break;
+
+ sources.push_back(modules_decl_vendor->GetImporterSource());
+ } while (false);
+
+ if (!is_shared_context) {
+ // Update the scratch AST context's merger to reflect any new sources we
+ // might have come across since the last time an expression was parsed.
+
+ auto scratch_ast_context = static_cast<ClangASTContextForExpressions*>(
+ m_target->GetScratchClangASTContext());
+
+ scratch_ast_context->GetMergerUnchecked().AddSources(sources);
+
+ sources.push_back({*scratch_ast_context->getASTContext(),
+ *scratch_ast_context->getFileManager(),
+ scratch_ast_context->GetOriginMap()});
+ }
+ while (false)
+ ;
+
+ m_merger_up =
+ llvm::make_unique<clang::ExternalASTMerger>(target, sources);
+ } else {
+ m_ast_importer_sp->InstallMapCompleter(&ast_context, *this);
+ }
+}
+
+ClangASTSource::~ClangASTSource() {
+ if (m_ast_importer_sp)
+ m_ast_importer_sp->ForgetDestination(m_ast_context);
+
+ // We are in the process of destruction, don't create clang ast context on
+ // demand by passing false to
+ // Target::GetScratchClangASTContext(create_on_demand).
+ ClangASTContext *scratch_clang_ast_context =
+ m_target->GetScratchClangASTContext(false);
+
+ if (!scratch_clang_ast_context)
+ return;
+
+ clang::ASTContext *scratch_ast_context =
+ scratch_clang_ast_context->getASTContext();
+
+ if (!scratch_ast_context)
+ return;
+
+ if (m_ast_context != scratch_ast_context && m_ast_importer_sp)
+ m_ast_importer_sp->ForgetSource(scratch_ast_context, m_ast_context);
+}
+
+void ClangASTSource::StartTranslationUnit(ASTConsumer *Consumer) {
+ if (!m_ast_context)
+ return;
+
+ m_ast_context->getTranslationUnitDecl()->setHasExternalVisibleStorage();
+ m_ast_context->getTranslationUnitDecl()->setHasExternalLexicalStorage();
+}
+
+// The core lookup interface.
+bool ClangASTSource::FindExternalVisibleDeclsByName(
+ const DeclContext *decl_ctx, DeclarationName clang_decl_name) {
+ if (!m_ast_context) {
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+
+ if (GetImportInProgress()) {
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+
+ std::string decl_name(clang_decl_name.getAsString());
+
+ // if (m_decl_map.DoingASTImport ())
+ // return DeclContext::lookup_result();
+ //
+ switch (clang_decl_name.getNameKind()) {
+ // Normal identifiers.
+ case DeclarationName::Identifier: {
+ clang::IdentifierInfo *identifier_info =
+ clang_decl_name.getAsIdentifierInfo();
+
+ if (!identifier_info || identifier_info->getBuiltinID() != 0) {
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+ } break;
+
+ // Operator names.
+ case DeclarationName::CXXOperatorName:
+ case DeclarationName::CXXLiteralOperatorName:
+ break;
+
+ // Using directives found in this context.
+ // Tell Sema we didn't find any or we'll end up getting asked a *lot*.
+ case DeclarationName::CXXUsingDirective:
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+
+ case DeclarationName::ObjCZeroArgSelector:
+ case DeclarationName::ObjCOneArgSelector:
+ case DeclarationName::ObjCMultiArgSelector: {
+ llvm::SmallVector<NamedDecl *, 1> method_decls;
+
+ NameSearchContext method_search_context(*this, method_decls,
+ clang_decl_name, decl_ctx);
+
+ FindObjCMethodDecls(method_search_context);
+
+ SetExternalVisibleDeclsForName(decl_ctx, clang_decl_name, method_decls);
+ return (method_decls.size() > 0);
+ }
+ // These aren't possible in the global context.
+ case DeclarationName::CXXConstructorName:
+ case DeclarationName::CXXDestructorName:
+ case DeclarationName::CXXConversionFunctionName:
+ case DeclarationName::CXXDeductionGuideName:
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+
+ if (!GetLookupsEnabled()) {
+ // Wait until we see a '$' at the start of a name before we start doing any
+ // lookups so we can avoid lookup up all of the builtin types.
+ if (!decl_name.empty() && decl_name[0] == '$') {
+ SetLookupsEnabled(true);
+ } else {
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+ }
+
+ ConstString const_decl_name(decl_name.c_str());
+
+ const char *uniqued_const_decl_name = const_decl_name.GetCString();
+ if (m_active_lookups.find(uniqued_const_decl_name) !=
+ m_active_lookups.end()) {
+ // We are currently looking up this name...
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+ m_active_lookups.insert(uniqued_const_decl_name);
+ // static uint32_t g_depth = 0;
+ // ++g_depth;
+ // printf("[%5u] FindExternalVisibleDeclsByName() \"%s\"\n", g_depth,
+ // uniqued_const_decl_name);
+ llvm::SmallVector<NamedDecl *, 4> name_decls;
+ NameSearchContext name_search_context(*this, name_decls, clang_decl_name,
+ decl_ctx);
+ FindExternalVisibleDecls(name_search_context);
+ SetExternalVisibleDeclsForName(decl_ctx, clang_decl_name, name_decls);
+ // --g_depth;
+ m_active_lookups.erase(uniqued_const_decl_name);
+ return (name_decls.size() != 0);
+}
+
+void ClangASTSource::CompleteType(TagDecl *tag_decl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ if (log) {
+ log->Printf(" CompleteTagDecl[%u] on (ASTContext*)%p Completing "
+ "(TagDecl*)%p named %s",
+ current_id, static_cast<void *>(m_ast_context),
+ static_cast<void *>(tag_decl),
+ tag_decl->getName().str().c_str());
+
+ log->Printf(" CTD[%u] Before:", current_id);
+ ASTDumper dumper((Decl *)tag_decl);
+ dumper.ToLog(log, " [CTD] ");
+ }
+
+ auto iter = m_active_lexical_decls.find(tag_decl);
+ if (iter != m_active_lexical_decls.end())
+ return;
+ m_active_lexical_decls.insert(tag_decl);
+ ScopedLexicalDeclEraser eraser(m_active_lexical_decls, tag_decl);
+
+ if (!m_ast_importer_sp) {
+ if (HasMerger()) {
+ GetMergerUnchecked().CompleteType(tag_decl);
+ }
+ return;
+ }
+
+ if (!m_ast_importer_sp->CompleteTagDecl(tag_decl)) {
+ // We couldn't complete the type. Maybe there's a definition somewhere
+ // else that can be completed.
+
+ if (log)
+ log->Printf(" CTD[%u] Type could not be completed in the module in "
+ "which it was first found.",
+ current_id);
+
+ bool found = false;
+
+ DeclContext *decl_ctx = tag_decl->getDeclContext();
+
+ if (const NamespaceDecl *namespace_context =
+ dyn_cast<NamespaceDecl>(decl_ctx)) {
+ ClangASTImporter::NamespaceMapSP namespace_map =
+ m_ast_importer_sp->GetNamespaceMap(namespace_context);
+
+ if (log && log->GetVerbose())
+ log->Printf(" CTD[%u] Inspecting namespace map %p (%d entries)",
+ current_id, static_cast<void *>(namespace_map.get()),
+ static_cast<int>(namespace_map->size()));
+
+ if (!namespace_map)
+ return;
+
+ for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(),
+ e = namespace_map->end();
+ i != e && !found; ++i) {
+ if (log)
+ log->Printf(" CTD[%u] Searching namespace %s in module %s",
+ current_id, i->second.GetName().AsCString(),
+ i->first->GetFileSpec().GetFilename().GetCString());
+
+ TypeList types;
+
+ ConstString name(tag_decl->getName().str().c_str());
+
+ i->first->FindTypesInNamespace(name, &i->second, UINT32_MAX, types);
+
+ for (uint32_t ti = 0, te = types.GetSize(); ti != te && !found; ++ti) {
+ lldb::TypeSP type = types.GetTypeAtIndex(ti);
+
+ if (!type)
+ continue;
+
+ CompilerType clang_type(type->GetFullCompilerType());
+
+ if (!ClangUtil::IsClangType(clang_type))
+ continue;
+
+ const TagType *tag_type =
+ ClangUtil::GetQualType(clang_type)->getAs<TagType>();
+
+ if (!tag_type)
+ continue;
+
+ TagDecl *candidate_tag_decl =
+ const_cast<TagDecl *>(tag_type->getDecl());
+
+ if (m_ast_importer_sp->CompleteTagDeclWithOrigin(tag_decl,
+ candidate_tag_decl))
+ found = true;
+ }
+ }
+ } else {
+ TypeList types;
+
+ ConstString name(tag_decl->getName().str().c_str());
+ CompilerDeclContext namespace_decl;
+
+ const ModuleList &module_list = m_target->GetImages();
+
+ bool exact_match = false;
+ llvm::DenseSet<SymbolFile *> searched_symbol_files;
+ module_list.FindTypes(nullptr, name, exact_match, UINT32_MAX,
+ searched_symbol_files, types);
+
+ for (uint32_t ti = 0, te = types.GetSize(); ti != te && !found; ++ti) {
+ lldb::TypeSP type = types.GetTypeAtIndex(ti);
+
+ if (!type)
+ continue;
+
+ CompilerType clang_type(type->GetFullCompilerType());
+
+ if (!ClangUtil::IsClangType(clang_type))
+ continue;
+
+ const TagType *tag_type =
+ ClangUtil::GetQualType(clang_type)->getAs<TagType>();
+
+ if (!tag_type)
+ continue;
+
+ TagDecl *candidate_tag_decl =
+ const_cast<TagDecl *>(tag_type->getDecl());
+
+ // We have found a type by basename and we need to make sure the decl
+ // contexts are the same before we can try to complete this type with
+ // another
+ if (!ClangASTContext::DeclsAreEquivalent(tag_decl, candidate_tag_decl))
+ continue;
+
+ if (m_ast_importer_sp->CompleteTagDeclWithOrigin(tag_decl,
+ candidate_tag_decl))
+ found = true;
+ }
+ }
+ }
+
+ if (log) {
+ log->Printf(" [CTD] After:");
+ ASTDumper dumper((Decl *)tag_decl);
+ dumper.ToLog(log, " [CTD] ");
+ }
+}
+
+void ClangASTSource::CompleteType(clang::ObjCInterfaceDecl *interface_decl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ log->Printf(" [CompleteObjCInterfaceDecl] on (ASTContext*)%p Completing "
+ "an ObjCInterfaceDecl named %s",
+ static_cast<void *>(m_ast_context),
+ interface_decl->getName().str().c_str());
+ log->Printf(" [COID] Before:");
+ ASTDumper dumper((Decl *)interface_decl);
+ dumper.ToLog(log, " [COID] ");
+ }
+
+ if (!m_ast_importer_sp) {
+ if (HasMerger()) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(interface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != interface_decl)) {
+ m_merger_up->ForceRecordOrigin(interface_decl, {complete_iface_decl, &complete_iface_decl->getASTContext()});
+ }
+
+ GetMergerUnchecked().CompleteType(interface_decl);
+ } else {
+ lldbassert(0 && "No mechanism for completing a type!");
+ }
+ return;
+ }
+
+ Decl *original_decl = nullptr;
+ ASTContext *original_ctx = nullptr;
+
+ if (m_ast_importer_sp->ResolveDeclOrigin(interface_decl, &original_decl,
+ &original_ctx)) {
+ if (ObjCInterfaceDecl *original_iface_decl =
+ dyn_cast<ObjCInterfaceDecl>(original_decl)) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(original_iface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != original_iface_decl)) {
+ m_ast_importer_sp->SetDeclOrigin(interface_decl, complete_iface_decl);
+ }
+ }
+ }
+
+ m_ast_importer_sp->CompleteObjCInterfaceDecl(interface_decl);
+
+ if (interface_decl->getSuperClass() &&
+ interface_decl->getSuperClass() != interface_decl)
+ CompleteType(interface_decl->getSuperClass());
+
+ if (log) {
+ log->Printf(" [COID] After:");
+ ASTDumper dumper((Decl *)interface_decl);
+ dumper.ToLog(log, " [COID] ");
+ }
+}
+
+clang::ObjCInterfaceDecl *ClangASTSource::GetCompleteObjCInterface(
+ const clang::ObjCInterfaceDecl *interface_decl) {
+ lldb::ProcessSP process(m_target->GetProcessSP());
+
+ if (!process)
+ return nullptr;
+
+ ObjCLanguageRuntime *language_runtime(ObjCLanguageRuntime::Get(*process));
+
+ if (!language_runtime)
+ return nullptr;
+
+ ConstString class_name(interface_decl->getNameAsString().c_str());
+
+ lldb::TypeSP complete_type_sp(
+ language_runtime->LookupInCompleteClassCache(class_name));
+
+ if (!complete_type_sp)
+ return nullptr;
+
+ TypeFromUser complete_type =
+ TypeFromUser(complete_type_sp->GetFullCompilerType());
+ lldb::opaque_compiler_type_t complete_opaque_type =
+ complete_type.GetOpaqueQualType();
+
+ if (!complete_opaque_type)
+ return nullptr;
+
+ const clang::Type *complete_clang_type =
+ QualType::getFromOpaquePtr(complete_opaque_type).getTypePtr();
+ const ObjCInterfaceType *complete_interface_type =
+ dyn_cast<ObjCInterfaceType>(complete_clang_type);
+
+ if (!complete_interface_type)
+ return nullptr;
+
+ ObjCInterfaceDecl *complete_iface_decl(complete_interface_type->getDecl());
+
+ return complete_iface_decl;
+}
+
+void ClangASTSource::FindExternalLexicalDecls(
+ const DeclContext *decl_context,
+ llvm::function_ref<bool(Decl::Kind)> predicate,
+ llvm::SmallVectorImpl<Decl *> &decls) {
+
+ if (HasMerger()) {
+ if (auto *interface_decl = dyn_cast<ObjCInterfaceDecl>(decl_context)) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(interface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != interface_decl)) {
+ m_merger_up->ForceRecordOrigin(interface_decl, {complete_iface_decl, &complete_iface_decl->getASTContext()});
+ }
+ }
+ return GetMergerUnchecked().FindExternalLexicalDecls(decl_context,
+ predicate,
+ decls);
+ } else if (!m_ast_importer_sp)
+ return;
+
+ ClangASTMetrics::RegisterLexicalQuery();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ const Decl *context_decl = dyn_cast<Decl>(decl_context);
+
+ if (!context_decl)
+ return;
+
+ auto iter = m_active_lexical_decls.find(context_decl);
+ if (iter != m_active_lexical_decls.end())
+ return;
+ m_active_lexical_decls.insert(context_decl);
+ ScopedLexicalDeclEraser eraser(m_active_lexical_decls, context_decl);
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ if (log) {
+ if (const NamedDecl *context_named_decl = dyn_cast<NamedDecl>(context_decl))
+ log->Printf(
+ "FindExternalLexicalDecls[%u] on (ASTContext*)%p in '%s' (%sDecl*)%p",
+ current_id, static_cast<void *>(m_ast_context),
+ context_named_decl->getNameAsString().c_str(),
+ context_decl->getDeclKindName(),
+ static_cast<const void *>(context_decl));
+ else if (context_decl)
+ log->Printf(
+ "FindExternalLexicalDecls[%u] on (ASTContext*)%p in (%sDecl*)%p",
+ current_id, static_cast<void *>(m_ast_context),
+ context_decl->getDeclKindName(),
+ static_cast<const void *>(context_decl));
+ else
+ log->Printf(
+ "FindExternalLexicalDecls[%u] on (ASTContext*)%p in a NULL context",
+ current_id, static_cast<const void *>(m_ast_context));
+ }
+
+ Decl *original_decl = nullptr;
+ ASTContext *original_ctx = nullptr;
+
+ if (!m_ast_importer_sp->ResolveDeclOrigin(context_decl, &original_decl,
+ &original_ctx))
+ return;
+
+ if (log) {
+ log->Printf(" FELD[%u] Original decl (ASTContext*)%p (Decl*)%p:",
+ current_id, static_cast<void *>(original_ctx),
+ static_cast<void *>(original_decl));
+ ASTDumper(original_decl).ToLog(log, " ");
+ }
+
+ if (ObjCInterfaceDecl *original_iface_decl =
+ dyn_cast<ObjCInterfaceDecl>(original_decl)) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(original_iface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != original_iface_decl)) {
+ original_decl = complete_iface_decl;
+ original_ctx = &complete_iface_decl->getASTContext();
+
+ m_ast_importer_sp->SetDeclOrigin(context_decl, complete_iface_decl);
+ }
+ }
+
+ if (TagDecl *original_tag_decl = dyn_cast<TagDecl>(original_decl)) {
+ ExternalASTSource *external_source = original_ctx->getExternalSource();
+
+ if (external_source)
+ external_source->CompleteType(original_tag_decl);
+ }
+
+ const DeclContext *original_decl_context =
+ dyn_cast<DeclContext>(original_decl);
+
+ if (!original_decl_context)
+ return;
+
+ // Indicates whether we skipped any Decls of the original DeclContext.
+ bool SkippedDecls = false;
+ for (TagDecl::decl_iterator iter = original_decl_context->decls_begin();
+ iter != original_decl_context->decls_end(); ++iter) {
+ Decl *decl = *iter;
+
+ // The predicate function returns true if the passed declaration kind is
+ // the one we are looking for.
+ // See clang::ExternalASTSource::FindExternalLexicalDecls()
+ if (predicate(decl->getKind())) {
+ if (log) {
+ ASTDumper ast_dumper(decl);
+ if (const NamedDecl *context_named_decl =
+ dyn_cast<NamedDecl>(context_decl))
+ log->Printf(" FELD[%d] Adding [to %sDecl %s] lexical %sDecl %s",
+ current_id, context_named_decl->getDeclKindName(),
+ context_named_decl->getNameAsString().c_str(),
+ decl->getDeclKindName(), ast_dumper.GetCString());
+ else
+ log->Printf(" FELD[%d] Adding lexical %sDecl %s", current_id,
+ decl->getDeclKindName(), ast_dumper.GetCString());
+ }
+
+ Decl *copied_decl = CopyDecl(decl);
+
+ if (!copied_decl)
+ continue;
+
+ if (FieldDecl *copied_field = dyn_cast<FieldDecl>(copied_decl)) {
+ QualType copied_field_type = copied_field->getType();
+
+ m_ast_importer_sp->RequireCompleteType(copied_field_type);
+ }
+ } else {
+ SkippedDecls = true;
+ }
+ }
+
+ // CopyDecl may build a lookup table which may set up ExternalLexicalStorage
+ // to false. However, since we skipped some of the external Decls we must
+ // set it back!
+ if (SkippedDecls) {
+ decl_context->setHasExternalLexicalStorage(true);
+ // This sets HasLazyExternalLexicalLookups to true. By setting this bit we
+ // ensure that the lookup table is rebuilt, which means the external source
+ // is consulted again when a clang::DeclContext::lookup is called.
+ const_cast<DeclContext *>(decl_context)->setMustBuildLookupTable();
+ }
+
+ return;
+}
+
+void ClangASTSource::FindExternalVisibleDecls(NameSearchContext &context) {
+ assert(m_ast_context);
+
+ ClangASTMetrics::RegisterVisibleQuery();
+
+ const ConstString name(context.m_decl_name.getAsString().c_str());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ if (log) {
+ if (!context.m_decl_context)
+ log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on "
+ "(ASTContext*)%p for '%s' in a NULL DeclContext",
+ current_id, static_cast<void *>(m_ast_context),
+ name.GetCString());
+ else if (const NamedDecl *context_named_decl =
+ dyn_cast<NamedDecl>(context.m_decl_context))
+ log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on "
+ "(ASTContext*)%p for '%s' in '%s'",
+ current_id, static_cast<void *>(m_ast_context),
+ name.GetCString(),
+ context_named_decl->getNameAsString().c_str());
+ else
+ log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on "
+ "(ASTContext*)%p for '%s' in a '%s'",
+ current_id, static_cast<void *>(m_ast_context),
+ name.GetCString(), context.m_decl_context->getDeclKindName());
+ }
+
+ if (HasMerger() && !isa<TranslationUnitDecl>(context.m_decl_context)
+ /* possibly handle NamespaceDecls here? */) {
+ if (auto *interface_decl =
+ dyn_cast<ObjCInterfaceDecl>(context.m_decl_context)) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(interface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != interface_decl)) {
+ GetMergerUnchecked().ForceRecordOrigin(
+ interface_decl,
+ {complete_iface_decl, &complete_iface_decl->getASTContext()});
+ }
+ }
+
+ GetMergerUnchecked().FindExternalVisibleDeclsByName(context.m_decl_context,
+ context.m_decl_name);
+ return; // otherwise we may need to fall back
+ }
+
+ context.m_namespace_map = std::make_shared<ClangASTImporter::NamespaceMap>();
+
+ if (const NamespaceDecl *namespace_context =
+ dyn_cast<NamespaceDecl>(context.m_decl_context)) {
+ ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer_sp ?
+ m_ast_importer_sp->GetNamespaceMap(namespace_context) : nullptr;
+
+ if (log && log->GetVerbose())
+ log->Printf(" CAS::FEVD[%u] Inspecting namespace map %p (%d entries)",
+ current_id, static_cast<void *>(namespace_map.get()),
+ static_cast<int>(namespace_map->size()));
+
+ if (!namespace_map)
+ return;
+
+ for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(),
+ e = namespace_map->end();
+ i != e; ++i) {
+ if (log)
+ log->Printf(" CAS::FEVD[%u] Searching namespace %s in module %s",
+ current_id, i->second.GetName().AsCString(),
+ i->first->GetFileSpec().GetFilename().GetCString());
+
+ FindExternalVisibleDecls(context, i->first, i->second, current_id);
+ }
+ } else if (isa<ObjCInterfaceDecl>(context.m_decl_context) && !HasMerger()) {
+ FindObjCPropertyAndIvarDecls(context);
+ } else if (!isa<TranslationUnitDecl>(context.m_decl_context)) {
+ // we shouldn't be getting FindExternalVisibleDecls calls for these
+ return;
+ } else {
+ CompilerDeclContext namespace_decl;
+
+ if (log)
+ log->Printf(" CAS::FEVD[%u] Searching the root namespace", current_id);
+
+ FindExternalVisibleDecls(context, lldb::ModuleSP(), namespace_decl,
+ current_id);
+ }
+
+ if (!context.m_namespace_map->empty()) {
+ if (log && log->GetVerbose())
+ log->Printf(" CAS::FEVD[%u] Registering namespace map %p (%d entries)",
+ current_id,
+ static_cast<void *>(context.m_namespace_map.get()),
+ static_cast<int>(context.m_namespace_map->size()));
+
+ NamespaceDecl *clang_namespace_decl =
+ AddNamespace(context, context.m_namespace_map);
+
+ if (clang_namespace_decl)
+ clang_namespace_decl->setHasExternalVisibleStorage();
+ }
+}
+
+clang::Sema *ClangASTSource::getSema() {
+ return ClangASTContext::GetASTContext(m_ast_context)->getSema();
+}
+
+bool ClangASTSource::IgnoreName(const ConstString name,
+ bool ignore_all_dollar_names) {
+ static const ConstString id_name("id");
+ static const ConstString Class_name("Class");
+
+ if (m_ast_context->getLangOpts().ObjC)
+ if (name == id_name || name == Class_name)
+ return true;
+
+ StringRef name_string_ref = name.GetStringRef();
+
+ // The ClangASTSource is not responsible for finding $-names.
+ return name_string_ref.empty() ||
+ (ignore_all_dollar_names && name_string_ref.startswith("$")) ||
+ name_string_ref.startswith("_$");
+}
+
+void ClangASTSource::FindExternalVisibleDecls(
+ NameSearchContext &context, lldb::ModuleSP module_sp,
+ CompilerDeclContext &namespace_decl, unsigned int current_id) {
+ assert(m_ast_context);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ SymbolContextList sc_list;
+
+ const ConstString name(context.m_decl_name.getAsString().c_str());
+ if (IgnoreName(name, true))
+ return;
+
+ if (module_sp && namespace_decl) {
+ CompilerDeclContext found_namespace_decl;
+
+ SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor();
+
+ if (symbol_vendor) {
+ found_namespace_decl =
+ symbol_vendor->FindNamespace(name, &namespace_decl);
+
+ if (found_namespace_decl) {
+ context.m_namespace_map->push_back(
+ std::pair<lldb::ModuleSP, CompilerDeclContext>(
+ module_sp, found_namespace_decl));
+
+ if (log)
+ log->Printf(" CAS::FEVD[%u] Found namespace %s in module %s",
+ current_id, name.GetCString(),
+ module_sp->GetFileSpec().GetFilename().GetCString());
+ }
+ }
+ } else if (!HasMerger()) {
+ const ModuleList &target_images = m_target->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_images.GetMutex());
+
+ for (size_t i = 0, e = target_images.GetSize(); i < e; ++i) {
+ lldb::ModuleSP image = target_images.GetModuleAtIndexUnlocked(i);
+
+ if (!image)
+ continue;
+
+ CompilerDeclContext found_namespace_decl;
+
+ SymbolVendor *symbol_vendor = image->GetSymbolVendor();
+
+ if (!symbol_vendor)
+ continue;
+
+ found_namespace_decl =
+ symbol_vendor->FindNamespace(name, &namespace_decl);
+
+ if (found_namespace_decl) {
+ context.m_namespace_map->push_back(
+ std::pair<lldb::ModuleSP, CompilerDeclContext>(
+ image, found_namespace_decl));
+
+ if (log)
+ log->Printf(" CAS::FEVD[%u] Found namespace %s in module %s",
+ current_id, name.GetCString(),
+ image->GetFileSpec().GetFilename().GetCString());
+ }
+ }
+ }
+
+ do {
+ if (context.m_found.type)
+ break;
+
+ TypeList types;
+ const bool exact_match = true;
+ llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
+ if (module_sp && namespace_decl)
+ module_sp->FindTypesInNamespace(name, &namespace_decl, 1, types);
+ else {
+ m_target->GetImages().FindTypes(module_sp.get(), name, exact_match, 1,
+ searched_symbol_files, types);
+ }
+
+ if (size_t num_types = types.GetSize()) {
+ for (size_t ti = 0; ti < num_types; ++ti) {
+ lldb::TypeSP type_sp = types.GetTypeAtIndex(ti);
+
+ if (log) {
+ const char *name_string = type_sp->GetName().GetCString();
+
+ log->Printf(" CAS::FEVD[%u] Matching type found for \"%s\": %s",
+ current_id, name.GetCString(),
+ (name_string ? name_string : "<anonymous>"));
+ }
+
+ CompilerType full_type = type_sp->GetFullCompilerType();
+
+ CompilerType copied_clang_type(GuardedCopyType(full_type));
+
+ if (!copied_clang_type) {
+ if (log)
+ log->Printf(" CAS::FEVD[%u] - Couldn't export a type", current_id);
+
+ continue;
+ }
+
+ context.AddTypeDecl(copied_clang_type);
+
+ context.m_found.type = true;
+ break;
+ }
+ }
+
+ if (!context.m_found.type) {
+ // Try the modules next.
+
+ do {
+ if (ClangModulesDeclVendor *modules_decl_vendor =
+ m_target->GetClangModulesDeclVendor()) {
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!modules_decl_vendor->FindDecls(name, append, max_matches, decls))
+ break;
+
+ if (log) {
+ log->Printf(" CAS::FEVD[%u] Matching entity found for \"%s\" in "
+ "the modules",
+ current_id, name.GetCString());
+ }
+
+ clang::NamedDecl *const decl_from_modules = decls[0];
+
+ if (llvm::isa<clang::TypeDecl>(decl_from_modules) ||
+ llvm::isa<clang::ObjCContainerDecl>(decl_from_modules) ||
+ llvm::isa<clang::EnumConstantDecl>(decl_from_modules)) {
+ clang::Decl *copied_decl = CopyDecl(decl_from_modules);
+ clang::NamedDecl *copied_named_decl =
+ copied_decl ? dyn_cast<clang::NamedDecl>(copied_decl) : nullptr;
+
+ if (!copied_named_decl) {
+ if (log)
+ log->Printf(
+ " CAS::FEVD[%u] - Couldn't export a type from the modules",
+ current_id);
+
+ break;
+ }
+
+ context.AddNamedDecl(copied_named_decl);
+
+ context.m_found.type = true;
+ }
+ }
+ } while (false);
+ }
+
+ if (!context.m_found.type) {
+ do {
+ // Couldn't find any types elsewhere. Try the Objective-C runtime if
+ // one exists.
+
+ lldb::ProcessSP process(m_target->GetProcessSP());
+
+ if (!process)
+ break;
+
+ ObjCLanguageRuntime *language_runtime(
+ ObjCLanguageRuntime::Get(*process));
+
+ if (!language_runtime)
+ break;
+
+ DeclVendor *decl_vendor = language_runtime->GetDeclVendor();
+
+ if (!decl_vendor)
+ break;
+
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!decl_vendor->FindDecls(name, append, max_matches, decls))
+ break;
+
+ if (log) {
+ log->Printf(
+ " CAS::FEVD[%u] Matching type found for \"%s\" in the runtime",
+ current_id, name.GetCString());
+ }
+
+ clang::Decl *copied_decl = CopyDecl(decls[0]);
+ clang::NamedDecl *copied_named_decl =
+ copied_decl ? dyn_cast<clang::NamedDecl>(copied_decl) : nullptr;
+
+ if (!copied_named_decl) {
+ if (log)
+ log->Printf(
+ " CAS::FEVD[%u] - Couldn't export a type from the runtime",
+ current_id);
+
+ break;
+ }
+
+ context.AddNamedDecl(copied_named_decl);
+ } while (false);
+ }
+
+ } while (false);
+}
+
+template <class D> class TaggedASTDecl {
+public:
+ TaggedASTDecl() : decl(nullptr) {}
+ TaggedASTDecl(D *_decl) : decl(_decl) {}
+ bool IsValid() const { return (decl != nullptr); }
+ bool IsInvalid() const { return !IsValid(); }
+ D *operator->() const { return decl; }
+ D *decl;
+};
+
+template <class D2, template <class D> class TD, class D1>
+TD<D2> DynCast(TD<D1> source) {
+ return TD<D2>(dyn_cast<D2>(source.decl));
+}
+
+template <class D = Decl> class DeclFromParser;
+template <class D = Decl> class DeclFromUser;
+
+template <class D> class DeclFromParser : public TaggedASTDecl<D> {
+public:
+ DeclFromParser() : TaggedASTDecl<D>() {}
+ DeclFromParser(D *_decl) : TaggedASTDecl<D>(_decl) {}
+
+ DeclFromUser<D> GetOrigin(ClangASTSource &source);
+};
+
+template <class D> class DeclFromUser : public TaggedASTDecl<D> {
+public:
+ DeclFromUser() : TaggedASTDecl<D>() {}
+ DeclFromUser(D *_decl) : TaggedASTDecl<D>(_decl) {}
+
+ DeclFromParser<D> Import(ClangASTSource &source);
+};
+
+template <class D>
+DeclFromUser<D> DeclFromParser<D>::GetOrigin(ClangASTSource &source) {
+ DeclFromUser<> origin_decl;
+ source.ResolveDeclOrigin(this->decl, &origin_decl.decl, nullptr);
+ if (origin_decl.IsInvalid())
+ return DeclFromUser<D>();
+ return DeclFromUser<D>(dyn_cast<D>(origin_decl.decl));
+}
+
+template <class D>
+DeclFromParser<D> DeclFromUser<D>::Import(ClangASTSource &source) {
+ DeclFromParser<> parser_generic_decl(source.CopyDecl(this->decl));
+ if (parser_generic_decl.IsInvalid())
+ return DeclFromParser<D>();
+ return DeclFromParser<D>(dyn_cast<D>(parser_generic_decl.decl));
+}
+
+bool ClangASTSource::FindObjCMethodDeclsWithOrigin(
+ unsigned int current_id, NameSearchContext &context,
+ ObjCInterfaceDecl *original_interface_decl, const char *log_info) {
+ const DeclarationName &decl_name(context.m_decl_name);
+ clang::ASTContext *original_ctx = &original_interface_decl->getASTContext();
+
+ Selector original_selector;
+
+ if (decl_name.isObjCZeroArgSelector()) {
+ IdentifierInfo *ident = &original_ctx->Idents.get(decl_name.getAsString());
+ original_selector = original_ctx->Selectors.getSelector(0, &ident);
+ } else if (decl_name.isObjCOneArgSelector()) {
+ const std::string &decl_name_string = decl_name.getAsString();
+ std::string decl_name_string_without_colon(decl_name_string.c_str(),
+ decl_name_string.length() - 1);
+ IdentifierInfo *ident =
+ &original_ctx->Idents.get(decl_name_string_without_colon);
+ original_selector = original_ctx->Selectors.getSelector(1, &ident);
+ } else {
+ SmallVector<IdentifierInfo *, 4> idents;
+
+ clang::Selector sel = decl_name.getObjCSelector();
+
+ unsigned num_args = sel.getNumArgs();
+
+ for (unsigned i = 0; i != num_args; ++i) {
+ idents.push_back(&original_ctx->Idents.get(sel.getNameForSlot(i)));
+ }
+
+ original_selector =
+ original_ctx->Selectors.getSelector(num_args, idents.data());
+ }
+
+ DeclarationName original_decl_name(original_selector);
+
+ llvm::SmallVector<NamedDecl *, 1> methods;
+
+ ClangASTContext::GetCompleteDecl(original_ctx, original_interface_decl);
+
+ if (ObjCMethodDecl *instance_method_decl =
+ original_interface_decl->lookupInstanceMethod(original_selector)) {
+ methods.push_back(instance_method_decl);
+ } else if (ObjCMethodDecl *class_method_decl =
+ original_interface_decl->lookupClassMethod(
+ original_selector)) {
+ methods.push_back(class_method_decl);
+ }
+
+ if (methods.empty()) {
+ return false;
+ }
+
+ for (NamedDecl *named_decl : methods) {
+ if (!named_decl)
+ continue;
+
+ ObjCMethodDecl *result_method = dyn_cast<ObjCMethodDecl>(named_decl);
+
+ if (!result_method)
+ continue;
+
+ Decl *copied_decl = CopyDecl(result_method);
+
+ if (!copied_decl)
+ continue;
+
+ ObjCMethodDecl *copied_method_decl = dyn_cast<ObjCMethodDecl>(copied_decl);
+
+ if (!copied_method_decl)
+ continue;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ ASTDumper dumper((Decl *)copied_method_decl);
+ log->Printf(" CAS::FOMD[%d] found (%s) %s", current_id, log_info,
+ dumper.GetCString());
+ }
+
+ context.AddNamedDecl(copied_method_decl);
+ }
+
+ return true;
+}
+
+void ClangASTSource::FindObjCMethodDecls(NameSearchContext &context) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (HasMerger()) {
+ if (auto *interface_decl = dyn_cast<ObjCInterfaceDecl>(context.m_decl_context)) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(interface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != context.m_decl_context)) {
+ m_merger_up->ForceRecordOrigin(interface_decl, {complete_iface_decl, &complete_iface_decl->getASTContext()});
+ }
+ }
+
+ GetMergerUnchecked().FindExternalVisibleDeclsByName(context.m_decl_context,
+ context.m_decl_name);
+ return;
+ }
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ const DeclarationName &decl_name(context.m_decl_name);
+ const DeclContext *decl_ctx(context.m_decl_context);
+
+ const ObjCInterfaceDecl *interface_decl =
+ dyn_cast<ObjCInterfaceDecl>(decl_ctx);
+
+ if (!interface_decl)
+ return;
+
+ do {
+ Decl *original_decl = nullptr;
+ ASTContext *original_ctx = nullptr;
+
+ m_ast_importer_sp->ResolveDeclOrigin(interface_decl, &original_decl,
+ &original_ctx);
+
+ if (!original_decl)
+ break;
+
+ ObjCInterfaceDecl *original_interface_decl =
+ dyn_cast<ObjCInterfaceDecl>(original_decl);
+
+ if (FindObjCMethodDeclsWithOrigin(current_id, context,
+ original_interface_decl, "at origin"))
+ return; // found it, no need to look any further
+ } while (false);
+
+ StreamString ss;
+
+ if (decl_name.isObjCZeroArgSelector()) {
+ ss.Printf("%s", decl_name.getAsString().c_str());
+ } else if (decl_name.isObjCOneArgSelector()) {
+ ss.Printf("%s", decl_name.getAsString().c_str());
+ } else {
+ clang::Selector sel = decl_name.getObjCSelector();
+
+ for (unsigned i = 0, e = sel.getNumArgs(); i != e; ++i) {
+ llvm::StringRef r = sel.getNameForSlot(i);
+ ss.Printf("%s:", r.str().c_str());
+ }
+ }
+ ss.Flush();
+
+ if (ss.GetString().contains("$__lldb"))
+ return; // we don't need any results
+
+ ConstString selector_name(ss.GetString());
+
+ if (log)
+ log->Printf("ClangASTSource::FindObjCMethodDecls[%d] on (ASTContext*)%p "
+ "for selector [%s %s]",
+ current_id, static_cast<void *>(m_ast_context),
+ interface_decl->getNameAsString().c_str(),
+ selector_name.AsCString());
+ SymbolContextList sc_list;
+
+ const bool include_symbols = false;
+ const bool include_inlines = false;
+ const bool append = false;
+
+ std::string interface_name = interface_decl->getNameAsString();
+
+ do {
+ StreamString ms;
+ ms.Printf("-[%s %s]", interface_name.c_str(), selector_name.AsCString());
+ ms.Flush();
+ ConstString instance_method_name(ms.GetString());
+
+ m_target->GetImages().FindFunctions(
+ instance_method_name, lldb::eFunctionNameTypeFull, include_symbols,
+ include_inlines, append, sc_list);
+
+ if (sc_list.GetSize())
+ break;
+
+ ms.Clear();
+ ms.Printf("+[%s %s]", interface_name.c_str(), selector_name.AsCString());
+ ms.Flush();
+ ConstString class_method_name(ms.GetString());
+
+ m_target->GetImages().FindFunctions(
+ class_method_name, lldb::eFunctionNameTypeFull, include_symbols,
+ include_inlines, append, sc_list);
+
+ if (sc_list.GetSize())
+ break;
+
+ // Fall back and check for methods in categories. If we find methods this
+ // way, we need to check that they're actually in categories on the desired
+ // class.
+
+ SymbolContextList candidate_sc_list;
+
+ m_target->GetImages().FindFunctions(
+ selector_name, lldb::eFunctionNameTypeSelector, include_symbols,
+ include_inlines, append, candidate_sc_list);
+
+ for (uint32_t ci = 0, ce = candidate_sc_list.GetSize(); ci != ce; ++ci) {
+ SymbolContext candidate_sc;
+
+ if (!candidate_sc_list.GetContextAtIndex(ci, candidate_sc))
+ continue;
+
+ if (!candidate_sc.function)
+ continue;
+
+ const char *candidate_name = candidate_sc.function->GetName().AsCString();
+
+ const char *cursor = candidate_name;
+
+ if (*cursor != '+' && *cursor != '-')
+ continue;
+
+ ++cursor;
+
+ if (*cursor != '[')
+ continue;
+
+ ++cursor;
+
+ size_t interface_len = interface_name.length();
+
+ if (strncmp(cursor, interface_name.c_str(), interface_len))
+ continue;
+
+ cursor += interface_len;
+
+ if (*cursor == ' ' || *cursor == '(')
+ sc_list.Append(candidate_sc);
+ }
+ } while (false);
+
+ if (sc_list.GetSize()) {
+ // We found a good function symbol. Use that.
+
+ for (uint32_t i = 0, e = sc_list.GetSize(); i != e; ++i) {
+ SymbolContext sc;
+
+ if (!sc_list.GetContextAtIndex(i, sc))
+ continue;
+
+ if (!sc.function)
+ continue;
+
+ CompilerDeclContext function_decl_ctx = sc.function->GetDeclContext();
+ if (!function_decl_ctx)
+ continue;
+
+ ObjCMethodDecl *method_decl =
+ ClangASTContext::DeclContextGetAsObjCMethodDecl(function_decl_ctx);
+
+ if (!method_decl)
+ continue;
+
+ ObjCInterfaceDecl *found_interface_decl =
+ method_decl->getClassInterface();
+
+ if (!found_interface_decl)
+ continue;
+
+ if (found_interface_decl->getName() == interface_decl->getName()) {
+ Decl *copied_decl = CopyDecl(method_decl);
+
+ if (!copied_decl)
+ continue;
+
+ ObjCMethodDecl *copied_method_decl =
+ dyn_cast<ObjCMethodDecl>(copied_decl);
+
+ if (!copied_method_decl)
+ continue;
+
+ if (log) {
+ ASTDumper dumper((Decl *)copied_method_decl);
+ log->Printf(" CAS::FOMD[%d] found (in symbols) %s", current_id,
+ dumper.GetCString());
+ }
+
+ context.AddNamedDecl(copied_method_decl);
+ }
+ }
+
+ return;
+ }
+
+ // Try the debug information.
+
+ do {
+ ObjCInterfaceDecl *complete_interface_decl = GetCompleteObjCInterface(
+ const_cast<ObjCInterfaceDecl *>(interface_decl));
+
+ if (!complete_interface_decl)
+ break;
+
+ // We found the complete interface. The runtime never needs to be queried
+ // in this scenario.
+
+ DeclFromUser<const ObjCInterfaceDecl> complete_iface_decl(
+ complete_interface_decl);
+
+ if (complete_interface_decl == interface_decl)
+ break; // already checked this one
+
+ if (log)
+ log->Printf("CAS::FOPD[%d] trying origin "
+ "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...",
+ current_id, static_cast<void *>(complete_interface_decl),
+ static_cast<void *>(&complete_iface_decl->getASTContext()));
+
+ FindObjCMethodDeclsWithOrigin(current_id, context, complete_interface_decl,
+ "in debug info");
+
+ return;
+ } while (false);
+
+ do {
+ // Check the modules only if the debug information didn't have a complete
+ // interface.
+
+ if (ClangModulesDeclVendor *modules_decl_vendor =
+ m_target->GetClangModulesDeclVendor()) {
+ ConstString interface_name(interface_decl->getNameAsString().c_str());
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!modules_decl_vendor->FindDecls(interface_name, append, max_matches,
+ decls))
+ break;
+
+ ObjCInterfaceDecl *interface_decl_from_modules =
+ dyn_cast<ObjCInterfaceDecl>(decls[0]);
+
+ if (!interface_decl_from_modules)
+ break;
+
+ if (FindObjCMethodDeclsWithOrigin(
+ current_id, context, interface_decl_from_modules, "in modules"))
+ return;
+ }
+ } while (false);
+
+ do {
+ // Check the runtime only if the debug information didn't have a complete
+ // interface and the modules don't get us anywhere.
+
+ lldb::ProcessSP process(m_target->GetProcessSP());
+
+ if (!process)
+ break;
+
+ ObjCLanguageRuntime *language_runtime(ObjCLanguageRuntime::Get(*process));
+
+ if (!language_runtime)
+ break;
+
+ DeclVendor *decl_vendor = language_runtime->GetDeclVendor();
+
+ if (!decl_vendor)
+ break;
+
+ ConstString interface_name(interface_decl->getNameAsString().c_str());
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!decl_vendor->FindDecls(interface_name, append, max_matches, decls))
+ break;
+
+ ObjCInterfaceDecl *runtime_interface_decl =
+ dyn_cast<ObjCInterfaceDecl>(decls[0]);
+
+ if (!runtime_interface_decl)
+ break;
+
+ FindObjCMethodDeclsWithOrigin(current_id, context, runtime_interface_decl,
+ "in runtime");
+ } while (false);
+}
+
+static bool FindObjCPropertyAndIvarDeclsWithOrigin(
+ unsigned int current_id, NameSearchContext &context, ClangASTSource &source,
+ DeclFromUser<const ObjCInterfaceDecl> &origin_iface_decl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (origin_iface_decl.IsInvalid())
+ return false;
+
+ std::string name_str = context.m_decl_name.getAsString();
+ StringRef name(name_str);
+ IdentifierInfo &name_identifier(
+ origin_iface_decl->getASTContext().Idents.get(name));
+
+ DeclFromUser<ObjCPropertyDecl> origin_property_decl(
+ origin_iface_decl->FindPropertyDeclaration(
+ &name_identifier, ObjCPropertyQueryKind::OBJC_PR_query_instance));
+
+ bool found = false;
+
+ if (origin_property_decl.IsValid()) {
+ DeclFromParser<ObjCPropertyDecl> parser_property_decl(
+ origin_property_decl.Import(source));
+ if (parser_property_decl.IsValid()) {
+ if (log) {
+ ASTDumper dumper((Decl *)parser_property_decl.decl);
+ log->Printf(" CAS::FOPD[%d] found %s", current_id,
+ dumper.GetCString());
+ }
+
+ context.AddNamedDecl(parser_property_decl.decl);
+ found = true;
+ }
+ }
+
+ DeclFromUser<ObjCIvarDecl> origin_ivar_decl(
+ origin_iface_decl->getIvarDecl(&name_identifier));
+
+ if (origin_ivar_decl.IsValid()) {
+ DeclFromParser<ObjCIvarDecl> parser_ivar_decl(
+ origin_ivar_decl.Import(source));
+ if (parser_ivar_decl.IsValid()) {
+ if (log) {
+ ASTDumper dumper((Decl *)parser_ivar_decl.decl);
+ log->Printf(" CAS::FOPD[%d] found %s", current_id,
+ dumper.GetCString());
+ }
+
+ context.AddNamedDecl(parser_ivar_decl.decl);
+ found = true;
+ }
+ }
+
+ return found;
+}
+
+void ClangASTSource::FindObjCPropertyAndIvarDecls(NameSearchContext &context) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ DeclFromParser<const ObjCInterfaceDecl> parser_iface_decl(
+ cast<ObjCInterfaceDecl>(context.m_decl_context));
+ DeclFromUser<const ObjCInterfaceDecl> origin_iface_decl(
+ parser_iface_decl.GetOrigin(*this));
+
+ ConstString class_name(parser_iface_decl->getNameAsString().c_str());
+
+ if (log)
+ log->Printf("ClangASTSource::FindObjCPropertyAndIvarDecls[%d] on "
+ "(ASTContext*)%p for '%s.%s'",
+ current_id, static_cast<void *>(m_ast_context),
+ parser_iface_decl->getNameAsString().c_str(),
+ context.m_decl_name.getAsString().c_str());
+
+ if (FindObjCPropertyAndIvarDeclsWithOrigin(
+ current_id, context, *this, origin_iface_decl))
+ return;
+
+ if (log)
+ log->Printf("CAS::FOPD[%d] couldn't find the property on origin "
+ "(ObjCInterfaceDecl*)%p/(ASTContext*)%p, searching "
+ "elsewhere...",
+ current_id, static_cast<const void *>(origin_iface_decl.decl),
+ static_cast<void *>(&origin_iface_decl->getASTContext()));
+
+ SymbolContext null_sc;
+ TypeList type_list;
+
+ do {
+ ObjCInterfaceDecl *complete_interface_decl = GetCompleteObjCInterface(
+ const_cast<ObjCInterfaceDecl *>(parser_iface_decl.decl));
+
+ if (!complete_interface_decl)
+ break;
+
+ // We found the complete interface. The runtime never needs to be queried
+ // in this scenario.
+
+ DeclFromUser<const ObjCInterfaceDecl> complete_iface_decl(
+ complete_interface_decl);
+
+ if (complete_iface_decl.decl == origin_iface_decl.decl)
+ break; // already checked this one
+
+ if (log)
+ log->Printf("CAS::FOPD[%d] trying origin "
+ "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...",
+ current_id,
+ static_cast<const void *>(complete_iface_decl.decl),
+ static_cast<void *>(&complete_iface_decl->getASTContext()));
+
+ FindObjCPropertyAndIvarDeclsWithOrigin(current_id, context, *this,
+ complete_iface_decl);
+
+ return;
+ } while (false);
+
+ do {
+ // Check the modules only if the debug information didn't have a complete
+ // interface.
+
+ ClangModulesDeclVendor *modules_decl_vendor =
+ m_target->GetClangModulesDeclVendor();
+
+ if (!modules_decl_vendor)
+ break;
+
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!modules_decl_vendor->FindDecls(class_name, append, max_matches, decls))
+ break;
+
+ DeclFromUser<const ObjCInterfaceDecl> interface_decl_from_modules(
+ dyn_cast<ObjCInterfaceDecl>(decls[0]));
+
+ if (!interface_decl_from_modules.IsValid())
+ break;
+
+ if (log)
+ log->Printf(
+ "CAS::FOPD[%d] trying module "
+ "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...",
+ current_id,
+ static_cast<const void *>(interface_decl_from_modules.decl),
+ static_cast<void *>(&interface_decl_from_modules->getASTContext()));
+
+ if (FindObjCPropertyAndIvarDeclsWithOrigin(current_id, context, *this,
+ interface_decl_from_modules))
+ return;
+ } while (false);
+
+ do {
+ // Check the runtime only if the debug information didn't have a complete
+ // interface and nothing was in the modules.
+
+ lldb::ProcessSP process(m_target->GetProcessSP());
+
+ if (!process)
+ return;
+
+ ObjCLanguageRuntime *language_runtime(ObjCLanguageRuntime::Get(*process));
+
+ if (!language_runtime)
+ return;
+
+ DeclVendor *decl_vendor = language_runtime->GetDeclVendor();
+
+ if (!decl_vendor)
+ break;
+
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!decl_vendor->FindDecls(class_name, append, max_matches, decls))
+ break;
+
+ DeclFromUser<const ObjCInterfaceDecl> interface_decl_from_runtime(
+ dyn_cast<ObjCInterfaceDecl>(decls[0]));
+
+ if (!interface_decl_from_runtime.IsValid())
+ break;
+
+ if (log)
+ log->Printf(
+ "CAS::FOPD[%d] trying runtime "
+ "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...",
+ current_id,
+ static_cast<const void *>(interface_decl_from_runtime.decl),
+ static_cast<void *>(&interface_decl_from_runtime->getASTContext()));
+
+ if (FindObjCPropertyAndIvarDeclsWithOrigin(
+ current_id, context, *this, interface_decl_from_runtime))
+ return;
+ } while (false);
+}
+
+typedef llvm::DenseMap<const FieldDecl *, uint64_t> FieldOffsetMap;
+typedef llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsetMap;
+
+template <class D, class O>
+static bool ImportOffsetMap(llvm::DenseMap<const D *, O> &destination_map,
+ llvm::DenseMap<const D *, O> &source_map,
+ ClangASTSource &source) {
+ // When importing fields into a new record, clang has a hard requirement that
+ // fields be imported in field offset order. Since they are stored in a
+ // DenseMap with a pointer as the key type, this means we cannot simply
+ // iterate over the map, as the order will be non-deterministic. Instead we
+ // have to sort by the offset and then insert in sorted order.
+ typedef llvm::DenseMap<const D *, O> MapType;
+ typedef typename MapType::value_type PairType;
+ std::vector<PairType> sorted_items;
+ sorted_items.reserve(source_map.size());
+ sorted_items.assign(source_map.begin(), source_map.end());
+ llvm::sort(sorted_items.begin(), sorted_items.end(),
+ [](const PairType &lhs, const PairType &rhs) {
+ return lhs.second < rhs.second;
+ });
+
+ for (const auto &item : sorted_items) {
+ DeclFromUser<D> user_decl(const_cast<D *>(item.first));
+ DeclFromParser<D> parser_decl(user_decl.Import(source));
+ if (parser_decl.IsInvalid())
+ return false;
+ destination_map.insert(
+ std::pair<const D *, O>(parser_decl.decl, item.second));
+ }
+
+ return true;
+}
+
+template <bool IsVirtual>
+bool ExtractBaseOffsets(const ASTRecordLayout &record_layout,
+ DeclFromUser<const CXXRecordDecl> &record,
+ BaseOffsetMap &base_offsets) {
+ for (CXXRecordDecl::base_class_const_iterator
+ bi = (IsVirtual ? record->vbases_begin() : record->bases_begin()),
+ be = (IsVirtual ? record->vbases_end() : record->bases_end());
+ bi != be; ++bi) {
+ if (!IsVirtual && bi->isVirtual())
+ continue;
+
+ const clang::Type *origin_base_type = bi->getType().getTypePtr();
+ const clang::RecordType *origin_base_record_type =
+ origin_base_type->getAs<RecordType>();
+
+ if (!origin_base_record_type)
+ return false;
+
+ DeclFromUser<RecordDecl> origin_base_record(
+ origin_base_record_type->getDecl());
+
+ if (origin_base_record.IsInvalid())
+ return false;
+
+ DeclFromUser<CXXRecordDecl> origin_base_cxx_record(
+ DynCast<CXXRecordDecl>(origin_base_record));
+
+ if (origin_base_cxx_record.IsInvalid())
+ return false;
+
+ CharUnits base_offset;
+
+ if (IsVirtual)
+ base_offset =
+ record_layout.getVBaseClassOffset(origin_base_cxx_record.decl);
+ else
+ base_offset =
+ record_layout.getBaseClassOffset(origin_base_cxx_record.decl);
+
+ base_offsets.insert(std::pair<const CXXRecordDecl *, CharUnits>(
+ origin_base_cxx_record.decl, base_offset));
+ }
+
+ return true;
+}
+
+bool ClangASTSource::layoutRecordType(const RecordDecl *record, uint64_t &size,
+ uint64_t &alignment,
+ FieldOffsetMap &field_offsets,
+ BaseOffsetMap &base_offsets,
+ BaseOffsetMap &virtual_base_offsets) {
+ ClangASTMetrics::RegisterRecordLayout();
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("LayoutRecordType[%u] on (ASTContext*)%p for (RecordDecl*)%p "
+ "[name = '%s']",
+ current_id, static_cast<void *>(m_ast_context),
+ static_cast<const void *>(record),
+ record->getNameAsString().c_str());
+
+ DeclFromParser<const RecordDecl> parser_record(record);
+ DeclFromUser<const RecordDecl> origin_record(
+ parser_record.GetOrigin(*this));
+
+ if (origin_record.IsInvalid())
+ return false;
+
+ FieldOffsetMap origin_field_offsets;
+ BaseOffsetMap origin_base_offsets;
+ BaseOffsetMap origin_virtual_base_offsets;
+
+ ClangASTContext::GetCompleteDecl(
+ &origin_record->getASTContext(),
+ const_cast<RecordDecl *>(origin_record.decl));
+
+ clang::RecordDecl *definition = origin_record.decl->getDefinition();
+ if (!definition || !definition->isCompleteDefinition())
+ return false;
+
+ const ASTRecordLayout &record_layout(
+ origin_record->getASTContext().getASTRecordLayout(origin_record.decl));
+
+ int field_idx = 0, field_count = record_layout.getFieldCount();
+
+ for (RecordDecl::field_iterator fi = origin_record->field_begin(),
+ fe = origin_record->field_end();
+ fi != fe; ++fi) {
+ if (field_idx >= field_count)
+ return false; // Layout didn't go well. Bail out.
+
+ uint64_t field_offset = record_layout.getFieldOffset(field_idx);
+
+ origin_field_offsets.insert(
+ std::pair<const FieldDecl *, uint64_t>(*fi, field_offset));
+
+ field_idx++;
+ }
+
+ lldbassert(&record->getASTContext() == m_ast_context);
+
+ DeclFromUser<const CXXRecordDecl> origin_cxx_record(
+ DynCast<const CXXRecordDecl>(origin_record));
+
+ if (origin_cxx_record.IsValid()) {
+ if (!ExtractBaseOffsets<false>(record_layout, origin_cxx_record,
+ origin_base_offsets) ||
+ !ExtractBaseOffsets<true>(record_layout, origin_cxx_record,
+ origin_virtual_base_offsets))
+ return false;
+ }
+
+ if (!ImportOffsetMap(field_offsets, origin_field_offsets, *this) ||
+ !ImportOffsetMap(base_offsets, origin_base_offsets, *this) ||
+ !ImportOffsetMap(virtual_base_offsets, origin_virtual_base_offsets,
+ *this))
+ return false;
+
+ size = record_layout.getSize().getQuantity() * m_ast_context->getCharWidth();
+ alignment = record_layout.getAlignment().getQuantity() *
+ m_ast_context->getCharWidth();
+
+ if (log) {
+ log->Printf("LRT[%u] returned:", current_id);
+ log->Printf("LRT[%u] Original = (RecordDecl*)%p", current_id,
+ static_cast<const void *>(origin_record.decl));
+ log->Printf("LRT[%u] Size = %" PRId64, current_id, size);
+ log->Printf("LRT[%u] Alignment = %" PRId64, current_id, alignment);
+ log->Printf("LRT[%u] Fields:", current_id);
+ for (RecordDecl::field_iterator fi = record->field_begin(),
+ fe = record->field_end();
+ fi != fe; ++fi) {
+ log->Printf("LRT[%u] (FieldDecl*)%p, Name = '%s', Offset = %" PRId64
+ " bits",
+ current_id, static_cast<void *>(*fi),
+ fi->getNameAsString().c_str(), field_offsets[*fi]);
+ }
+ DeclFromParser<const CXXRecordDecl> parser_cxx_record =
+ DynCast<const CXXRecordDecl>(parser_record);
+ if (parser_cxx_record.IsValid()) {
+ log->Printf("LRT[%u] Bases:", current_id);
+ for (CXXRecordDecl::base_class_const_iterator
+ bi = parser_cxx_record->bases_begin(),
+ be = parser_cxx_record->bases_end();
+ bi != be; ++bi) {
+ bool is_virtual = bi->isVirtual();
+
+ QualType base_type = bi->getType();
+ const RecordType *base_record_type = base_type->getAs<RecordType>();
+ DeclFromParser<RecordDecl> base_record(base_record_type->getDecl());
+ DeclFromParser<CXXRecordDecl> base_cxx_record =
+ DynCast<CXXRecordDecl>(base_record);
+
+ log->Printf(
+ "LRT[%u] %s(CXXRecordDecl*)%p, Name = '%s', Offset = %" PRId64
+ " chars",
+ current_id, (is_virtual ? "Virtual " : ""),
+ static_cast<void *>(base_cxx_record.decl),
+ base_cxx_record.decl->getNameAsString().c_str(),
+ (is_virtual
+ ? virtual_base_offsets[base_cxx_record.decl].getQuantity()
+ : base_offsets[base_cxx_record.decl].getQuantity()));
+ }
+ } else {
+ log->Printf("LRD[%u] Not a CXXRecord, so no bases", current_id);
+ }
+ }
+
+ return true;
+}
+
+void ClangASTSource::CompleteNamespaceMap(
+ ClangASTImporter::NamespaceMapSP &namespace_map, ConstString name,
+ ClangASTImporter::NamespaceMapSP &parent_map) const {
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ if (parent_map && parent_map->size())
+ log->Printf("CompleteNamespaceMap[%u] on (ASTContext*)%p Searching for "
+ "namespace %s in namespace %s",
+ current_id, static_cast<void *>(m_ast_context),
+ name.GetCString(),
+ parent_map->begin()->second.GetName().AsCString());
+ else
+ log->Printf("CompleteNamespaceMap[%u] on (ASTContext*)%p Searching for "
+ "namespace %s",
+ current_id, static_cast<void *>(m_ast_context),
+ name.GetCString());
+ }
+
+ if (parent_map) {
+ for (ClangASTImporter::NamespaceMap::iterator i = parent_map->begin(),
+ e = parent_map->end();
+ i != e; ++i) {
+ CompilerDeclContext found_namespace_decl;
+
+ lldb::ModuleSP module_sp = i->first;
+ CompilerDeclContext module_parent_namespace_decl = i->second;
+
+ SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor();
+
+ if (!symbol_vendor)
+ continue;
+
+ found_namespace_decl =
+ symbol_vendor->FindNamespace(name, &module_parent_namespace_decl);
+
+ if (!found_namespace_decl)
+ continue;
+
+ namespace_map->push_back(std::pair<lldb::ModuleSP, CompilerDeclContext>(
+ module_sp, found_namespace_decl));
+
+ if (log)
+ log->Printf(" CMN[%u] Found namespace %s in module %s", current_id,
+ name.GetCString(),
+ module_sp->GetFileSpec().GetFilename().GetCString());
+ }
+ } else {
+ const ModuleList &target_images = m_target->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_images.GetMutex());
+
+ CompilerDeclContext null_namespace_decl;
+
+ for (size_t i = 0, e = target_images.GetSize(); i < e; ++i) {
+ lldb::ModuleSP image = target_images.GetModuleAtIndexUnlocked(i);
+
+ if (!image)
+ continue;
+
+ CompilerDeclContext found_namespace_decl;
+
+ SymbolVendor *symbol_vendor = image->GetSymbolVendor();
+
+ if (!symbol_vendor)
+ continue;
+
+ found_namespace_decl =
+ symbol_vendor->FindNamespace(name, &null_namespace_decl);
+
+ if (!found_namespace_decl)
+ continue;
+
+ namespace_map->push_back(std::pair<lldb::ModuleSP, CompilerDeclContext>(
+ image, found_namespace_decl));
+
+ if (log)
+ log->Printf(" CMN[%u] Found namespace %s in module %s", current_id,
+ name.GetCString(),
+ image->GetFileSpec().GetFilename().GetCString());
+ }
+ }
+}
+
+NamespaceDecl *ClangASTSource::AddNamespace(
+ NameSearchContext &context,
+ ClangASTImporter::NamespaceMapSP &namespace_decls) {
+ if (!namespace_decls)
+ return nullptr;
+
+ const CompilerDeclContext &namespace_decl = namespace_decls->begin()->second;
+
+ clang::ASTContext *src_ast =
+ ClangASTContext::DeclContextGetClangASTContext(namespace_decl);
+ if (!src_ast)
+ return nullptr;
+ clang::NamespaceDecl *src_namespace_decl =
+ ClangASTContext::DeclContextGetAsNamespaceDecl(namespace_decl);
+
+ if (!src_namespace_decl)
+ return nullptr;
+
+ Decl *copied_decl = CopyDecl(src_namespace_decl);
+
+ if (!copied_decl)
+ return nullptr;
+
+ NamespaceDecl *copied_namespace_decl = dyn_cast<NamespaceDecl>(copied_decl);
+
+ if (!copied_namespace_decl)
+ return nullptr;
+
+ context.m_decls.push_back(copied_namespace_decl);
+
+ m_ast_importer_sp->RegisterNamespaceMap(copied_namespace_decl,
+ namespace_decls);
+
+ return dyn_cast<NamespaceDecl>(copied_decl);
+}
+
+clang::QualType ClangASTSource::CopyTypeWithMerger(
+ clang::ASTContext &from_context,
+ clang::ExternalASTMerger &merger,
+ clang::QualType type) {
+ if (!merger.HasImporterForOrigin(from_context)) {
+ lldbassert(0 && "Couldn't find the importer for a source context!");
+ return QualType();
+ }
+
+ if (llvm::Expected<QualType> type_or_error =
+ merger.ImporterForOrigin(from_context).Import(type)) {
+ return *type_or_error;
+ } else {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ LLDB_LOG_ERROR(log, type_or_error.takeError(), "Couldn't import type: {0}");
+ return QualType();
+ }
+}
+
+clang::Decl *ClangASTSource::CopyDecl(Decl *src_decl) {
+ clang::ASTContext &from_context = src_decl->getASTContext();
+ if (m_ast_importer_sp) {
+ return m_ast_importer_sp->CopyDecl(m_ast_context, &from_context, src_decl);
+ } else if (m_merger_up) {
+ if (!m_merger_up->HasImporterForOrigin(from_context)) {
+ lldbassert(0 && "Couldn't find the importer for a source context!");
+ return nullptr;
+ }
+
+ if (llvm::Expected<Decl *> decl_or_error =
+ m_merger_up->ImporterForOrigin(from_context).Import(src_decl)) {
+ return *decl_or_error;
+ } else {
+ Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ LLDB_LOG_ERROR(log, decl_or_error.takeError(),
+ "Couldn't import decl: {0}");
+ return nullptr;
+ }
+ } else {
+ lldbassert(0 && "No mechanism for copying a decl!");
+ return nullptr;
+ }
+}
+
+bool ClangASTSource::ResolveDeclOrigin(const clang::Decl *decl,
+ clang::Decl **original_decl,
+ clang::ASTContext **original_ctx) {
+ if (m_ast_importer_sp) {
+ return m_ast_importer_sp->ResolveDeclOrigin(decl, original_decl,
+ original_ctx);
+ } else if (m_merger_up) {
+ return false; // Implement this correctly in ExternalASTMerger
+ } else {
+ // this can happen early enough that no ExternalASTSource is installed.
+ return false;
+ }
+}
+
+clang::ExternalASTMerger &ClangASTSource::GetMergerUnchecked() {
+ lldbassert(m_merger_up != nullptr);
+ return *m_merger_up;
+}
+
+CompilerType ClangASTSource::GuardedCopyType(const CompilerType &src_type) {
+ ClangASTContext *src_ast =
+ llvm::dyn_cast_or_null<ClangASTContext>(src_type.GetTypeSystem());
+ if (src_ast == nullptr)
+ return CompilerType();
+
+ ClangASTMetrics::RegisterLLDBImport();
+
+ SetImportInProgress(true);
+
+ QualType copied_qual_type;
+
+ if (m_ast_importer_sp) {
+ copied_qual_type =
+ m_ast_importer_sp->CopyType(m_ast_context, src_ast->getASTContext(),
+ ClangUtil::GetQualType(src_type));
+ } else if (m_merger_up) {
+ copied_qual_type =
+ CopyTypeWithMerger(*src_ast->getASTContext(), *m_merger_up,
+ ClangUtil::GetQualType(src_type));
+ } else {
+ lldbassert(0 && "No mechanism for copying a type!");
+ return CompilerType();
+ }
+
+ SetImportInProgress(false);
+
+ if (copied_qual_type.getAsOpaquePtr() &&
+ copied_qual_type->getCanonicalTypeInternal().isNull())
+ // this shouldn't happen, but we're hardening because the AST importer
+ // seems to be generating bad types on occasion.
+ return CompilerType();
+
+ return CompilerType(m_ast_context, copied_qual_type);
+}
+
+clang::NamedDecl *NameSearchContext::AddVarDecl(const CompilerType &type) {
+ assert(type && "Type for variable must be valid!");
+
+ if (!type.IsValid())
+ return nullptr;
+
+ ClangASTContext *lldb_ast =
+ llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (!lldb_ast)
+ return nullptr;
+
+ IdentifierInfo *ii = m_decl_name.getAsIdentifierInfo();
+
+ clang::ASTContext *ast = lldb_ast->getASTContext();
+
+ clang::NamedDecl *Decl = VarDecl::Create(
+ *ast, const_cast<DeclContext *>(m_decl_context), SourceLocation(),
+ SourceLocation(), ii, ClangUtil::GetQualType(type), nullptr, SC_Static);
+ m_decls.push_back(Decl);
+
+ return Decl;
+}
+
+clang::NamedDecl *NameSearchContext::AddFunDecl(const CompilerType &type,
+ bool extern_c) {
+ assert(type && "Type for variable must be valid!");
+
+ if (!type.IsValid())
+ return nullptr;
+
+ if (m_function_types.count(type))
+ return nullptr;
+
+ ClangASTContext *lldb_ast =
+ llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (!lldb_ast)
+ return nullptr;
+
+ m_function_types.insert(type);
+
+ QualType qual_type(ClangUtil::GetQualType(type));
+
+ clang::ASTContext *ast = lldb_ast->getASTContext();
+
+ const bool isInlineSpecified = false;
+ const bool hasWrittenPrototype = true;
+ const bool isConstexprSpecified = false;
+
+ clang::DeclContext *context = const_cast<DeclContext *>(m_decl_context);
+
+ if (extern_c) {
+ context = LinkageSpecDecl::Create(
+ *ast, context, SourceLocation(), SourceLocation(),
+ clang::LinkageSpecDecl::LanguageIDs::lang_c, false);
+ }
+
+ // Pass the identifier info for functions the decl_name is needed for
+ // operators
+ clang::DeclarationName decl_name =
+ m_decl_name.getNameKind() == DeclarationName::Identifier
+ ? m_decl_name.getAsIdentifierInfo()
+ : m_decl_name;
+
+ clang::FunctionDecl *func_decl = FunctionDecl::Create(
+ *ast, context, SourceLocation(), SourceLocation(), decl_name, qual_type,
+ nullptr, SC_Extern, isInlineSpecified, hasWrittenPrototype,
+ isConstexprSpecified ? CSK_constexpr : CSK_unspecified);
+
+ // We have to do more than just synthesize the FunctionDecl. We have to
+ // synthesize ParmVarDecls for all of the FunctionDecl's arguments. To do
+ // this, we raid the function's FunctionProtoType for types.
+
+ const FunctionProtoType *func_proto_type =
+ qual_type.getTypePtr()->getAs<FunctionProtoType>();
+
+ if (func_proto_type) {
+ unsigned NumArgs = func_proto_type->getNumParams();
+ unsigned ArgIndex;
+
+ SmallVector<ParmVarDecl *, 5> parm_var_decls;
+
+ for (ArgIndex = 0; ArgIndex < NumArgs; ++ArgIndex) {
+ QualType arg_qual_type(func_proto_type->getParamType(ArgIndex));
+
+ parm_var_decls.push_back(
+ ParmVarDecl::Create(*ast, const_cast<DeclContext *>(context),
+ SourceLocation(), SourceLocation(), nullptr,
+ arg_qual_type, nullptr, SC_Static, nullptr));
+ }
+
+ func_decl->setParams(ArrayRef<ParmVarDecl *>(parm_var_decls));
+ } else {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("Function type wasn't a FunctionProtoType");
+ }
+
+ // If this is an operator (e.g. operator new or operator==), only insert the
+ // declaration we inferred from the symbol if we can provide the correct
+ // number of arguments. We shouldn't really inject random decl(s) for
+ // functions that are analyzed semantically in a special way, otherwise we
+ // will crash in clang.
+ clang::OverloadedOperatorKind op_kind = clang::NUM_OVERLOADED_OPERATORS;
+ if (func_proto_type &&
+ ClangASTContext::IsOperator(decl_name.getAsString().c_str(), op_kind)) {
+ if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount(
+ false, op_kind, func_proto_type->getNumParams()))
+ return nullptr;
+ }
+ m_decls.push_back(func_decl);
+
+ return func_decl;
+}
+
+clang::NamedDecl *NameSearchContext::AddGenericFunDecl() {
+ FunctionProtoType::ExtProtoInfo proto_info;
+
+ proto_info.Variadic = true;
+
+ QualType generic_function_type(m_ast_source.m_ast_context->getFunctionType(
+ m_ast_source.m_ast_context->UnknownAnyTy, // result
+ ArrayRef<QualType>(), // argument types
+ proto_info));
+
+ return AddFunDecl(
+ CompilerType(m_ast_source.m_ast_context, generic_function_type), true);
+}
+
+clang::NamedDecl *
+NameSearchContext::AddTypeDecl(const CompilerType &clang_type) {
+ if (ClangUtil::IsClangType(clang_type)) {
+ QualType qual_type = ClangUtil::GetQualType(clang_type);
+
+ if (const TypedefType *typedef_type =
+ llvm::dyn_cast<TypedefType>(qual_type)) {
+ TypedefNameDecl *typedef_name_decl = typedef_type->getDecl();
+
+ m_decls.push_back(typedef_name_decl);
+
+ return (NamedDecl *)typedef_name_decl;
+ } else if (const TagType *tag_type = qual_type->getAs<TagType>()) {
+ TagDecl *tag_decl = tag_type->getDecl();
+
+ m_decls.push_back(tag_decl);
+
+ return tag_decl;
+ } else if (const ObjCObjectType *objc_object_type =
+ qual_type->getAs<ObjCObjectType>()) {
+ ObjCInterfaceDecl *interface_decl = objc_object_type->getInterface();
+
+ m_decls.push_back((NamedDecl *)interface_decl);
+
+ return (NamedDecl *)interface_decl;
+ }
+ }
+ return nullptr;
+}
+
+void NameSearchContext::AddLookupResult(clang::DeclContextLookupResult result) {
+ for (clang::NamedDecl *decl : result)
+ m_decls.push_back(decl);
+}
+
+void NameSearchContext::AddNamedDecl(clang::NamedDecl *decl) {
+ m_decls.push_back(decl);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h
new file mode 100644
index 000000000000..7a8bacf48a8f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h
@@ -0,0 +1,528 @@
+//===-- ClangASTSource.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangASTSource_h_
+#define liblldb_ClangASTSource_h_
+
+#include <set>
+
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/Target.h"
+#include "clang/AST/ExternalASTMerger.h"
+#include "clang/Basic/IdentifierTable.h"
+
+#include "llvm/ADT/SmallSet.h"
+
+namespace lldb_private {
+
+/// \class ClangASTSource ClangASTSource.h "lldb/Expression/ClangASTSource.h"
+/// Provider for named objects defined in the debug info for Clang
+///
+/// As Clang parses an expression, it may encounter names that are not defined
+/// inside the expression, including variables, functions, and types. Clang
+/// knows the name it is looking for, but nothing else. The ExternalSemaSource
+/// class provides Decls (VarDecl, FunDecl, TypeDecl) to Clang for these
+/// names, consulting the ClangExpressionDeclMap to do the actual lookups.
+class ClangASTSource : public ClangExternalASTSourceCommon,
+ public ClangASTImporter::MapCompleter {
+public:
+ /// Constructor
+ ///
+ /// Initializes class variables.
+ ///
+ /// \param[in] target
+ /// A reference to the target containing debug information to use.
+ ClangASTSource(const lldb::TargetSP &target);
+
+ /// Destructor
+ ~ClangASTSource() override;
+
+ /// Interface stubs.
+ clang::Decl *GetExternalDecl(uint32_t) override { return nullptr; }
+ clang::Stmt *GetExternalDeclStmt(uint64_t) override { return nullptr; }
+ clang::Selector GetExternalSelector(uint32_t) override {
+ return clang::Selector();
+ }
+ uint32_t GetNumExternalSelectors() override { return 0; }
+ clang::CXXBaseSpecifier *
+ GetExternalCXXBaseSpecifiers(uint64_t Offset) override {
+ return nullptr;
+ }
+ void MaterializeVisibleDecls(const clang::DeclContext *DC) { return; }
+
+ void InstallASTContext(clang::ASTContext &ast_context,
+ clang::FileManager &file_manager,
+ bool is_shared_context = false);
+
+ //
+ // APIs for ExternalASTSource
+ //
+
+ /// Look up all Decls that match a particular name. Only handles
+ /// Identifiers and DeclContexts that are either NamespaceDecls or
+ /// TranslationUnitDecls. Calls SetExternalVisibleDeclsForName with the
+ /// result.
+ ///
+ /// The work for this function is done by
+ /// void FindExternalVisibleDecls (NameSearchContext &);
+ ///
+ /// \param[in] DC
+ /// The DeclContext to register the found Decls in.
+ ///
+ /// \param[in] Name
+ /// The name to find entries for.
+ ///
+ /// \return
+ /// Whatever SetExternalVisibleDeclsForName returns.
+ bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
+ clang::DeclarationName Name) override;
+
+ /// Enumerate all Decls in a given lexical context.
+ ///
+ /// \param[in] DC
+ /// The DeclContext being searched.
+ ///
+ /// \param[in] isKindWeWant
+ /// A callback function that returns true given the
+ /// DeclKinds of desired Decls, and false otherwise.
+ ///
+ /// \param[in] Decls
+ /// A vector that is filled in with matching Decls.
+ void FindExternalLexicalDecls(
+ const clang::DeclContext *DC,
+ llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
+ llvm::SmallVectorImpl<clang::Decl *> &Decls) override;
+
+ /// Specify the layout of the contents of a RecordDecl.
+ ///
+ /// \param[in] Record
+ /// The record (in the parser's AST context) that needs to be
+ /// laid out.
+ ///
+ /// \param[out] Size
+ /// The total size of the record in bits.
+ ///
+ /// \param[out] Alignment
+ /// The alignment of the record in bits.
+ ///
+ /// \param[in] FieldOffsets
+ /// A map that must be populated with pairs of the record's
+ /// fields (in the parser's AST context) and their offsets
+ /// (measured in bits).
+ ///
+ /// \param[in] BaseOffsets
+ /// A map that must be populated with pairs of the record's
+ /// C++ concrete base classes (in the parser's AST context,
+ /// and only if the record is a CXXRecordDecl and has base
+ /// classes) and their offsets (measured in bytes).
+ ///
+ /// \param[in] VirtualBaseOffsets
+ /// A map that must be populated with pairs of the record's
+ /// C++ virtual base classes (in the parser's AST context,
+ /// and only if the record is a CXXRecordDecl and has base
+ /// classes) and their offsets (measured in bytes).
+ ///
+ /// \return
+ /// True <=> the layout is valid.
+ bool layoutRecordType(
+ const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &BaseOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &VirtualBaseOffsets) override;
+
+ /// Complete a TagDecl.
+ ///
+ /// \param[in] Tag
+ /// The Decl to be completed in place.
+ void CompleteType(clang::TagDecl *Tag) override;
+
+ /// Complete an ObjCInterfaceDecl.
+ ///
+ /// \param[in] Class
+ /// The Decl to be completed in place.
+ void CompleteType(clang::ObjCInterfaceDecl *Class) override;
+
+ /// Called on entering a translation unit. Tells Clang by calling
+ /// setHasExternalVisibleStorage() and setHasExternalLexicalStorage() that
+ /// this object has something to say about undefined names.
+ ///
+ /// \param[in] ASTConsumer
+ /// Unused.
+ void StartTranslationUnit(clang::ASTConsumer *Consumer) override;
+
+ //
+ // APIs for NamespaceMapCompleter
+ //
+
+ /// Look up the modules containing a given namespace and put the appropriate
+ /// entries in the namespace map.
+ ///
+ /// \param[in] namespace_map
+ /// The map to be completed.
+ ///
+ /// \param[in] name
+ /// The name of the namespace to be found.
+ ///
+ /// \param[in] parent_map
+ /// The map for the namespace's parent namespace, if there is
+ /// one.
+ void CompleteNamespaceMap(
+ ClangASTImporter::NamespaceMapSP &namespace_map, ConstString name,
+ ClangASTImporter::NamespaceMapSP &parent_map) const override;
+
+ //
+ // Helper APIs
+ //
+
+ clang::NamespaceDecl *
+ AddNamespace(NameSearchContext &context,
+ ClangASTImporter::NamespaceMapSP &namespace_decls);
+
+ /// The worker function for FindExternalVisibleDeclsByName.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when filing results.
+ virtual void FindExternalVisibleDecls(NameSearchContext &context);
+
+ clang::Sema *getSema();
+
+ void SetImportInProgress(bool import_in_progress) {
+ m_import_in_progress = import_in_progress;
+ }
+ bool GetImportInProgress() { return m_import_in_progress; }
+
+ void SetLookupsEnabled(bool lookups_enabled) {
+ m_lookups_enabled = lookups_enabled;
+ }
+ bool GetLookupsEnabled() { return m_lookups_enabled; }
+
+ /// \class ClangASTSourceProxy ClangASTSource.h
+ /// "lldb/Expression/ClangASTSource.h" Proxy for ClangASTSource
+ ///
+ /// Clang AST contexts like to own their AST sources, so this is a state-
+ /// free proxy object.
+ class ClangASTSourceProxy : public ClangExternalASTSourceCommon {
+ public:
+ ClangASTSourceProxy(ClangASTSource &original) : m_original(original) {}
+
+ bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
+ clang::DeclarationName Name) override {
+ return m_original.FindExternalVisibleDeclsByName(DC, Name);
+ }
+
+ void FindExternalLexicalDecls(
+ const clang::DeclContext *DC,
+ llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
+ llvm::SmallVectorImpl<clang::Decl *> &Decls) override {
+ return m_original.FindExternalLexicalDecls(DC, IsKindWeWant, Decls);
+ }
+
+ void CompleteType(clang::TagDecl *Tag) override {
+ return m_original.CompleteType(Tag);
+ }
+
+ void CompleteType(clang::ObjCInterfaceDecl *Class) override {
+ return m_original.CompleteType(Class);
+ }
+
+ bool layoutRecordType(
+ const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &BaseOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &VirtualBaseOffsets) override {
+ return m_original.layoutRecordType(Record, Size, Alignment, FieldOffsets,
+ BaseOffsets, VirtualBaseOffsets);
+ }
+
+ void StartTranslationUnit(clang::ASTConsumer *Consumer) override {
+ return m_original.StartTranslationUnit(Consumer);
+ }
+
+ ClangASTMetadata *GetMetadata(const void *object) {
+ return m_original.GetMetadata(object);
+ }
+
+ void SetMetadata(const void *object, ClangASTMetadata &metadata) {
+ return m_original.SetMetadata(object, metadata);
+ }
+
+ bool HasMetadata(const void *object) {
+ return m_original.HasMetadata(object);
+ }
+
+ private:
+ ClangASTSource &m_original;
+ };
+
+ clang::ExternalASTSource *CreateProxy() {
+ return new ClangASTSourceProxy(*this);
+ }
+
+protected:
+ /// Look for the complete version of an Objective-C interface, and return it
+ /// if found.
+ ///
+ /// \param[in] interface_decl
+ /// An ObjCInterfaceDecl that may not be the complete one.
+ ///
+ /// \return
+ /// NULL if the complete interface couldn't be found;
+ /// the complete interface otherwise.
+ clang::ObjCInterfaceDecl *
+ GetCompleteObjCInterface(const clang::ObjCInterfaceDecl *interface_decl);
+
+ /// Find all entities matching a given name in a given module, using a
+ /// NameSearchContext to make Decls for them.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext that can construct Decls for this name.
+ ///
+ /// \param[in] module
+ /// If non-NULL, the module to query.
+ ///
+ /// \param[in] namespace_decl
+ /// If valid and module is non-NULL, the parent namespace.
+ ///
+ /// \param[in] current_id
+ /// The ID for the current FindExternalVisibleDecls invocation,
+ /// for logging purposes.
+ void FindExternalVisibleDecls(NameSearchContext &context,
+ lldb::ModuleSP module,
+ CompilerDeclContext &namespace_decl,
+ unsigned int current_id);
+
+ /// Find all Objective-C methods matching a given selector.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext that can construct Decls for this name.
+ /// Its m_decl_name contains the selector and its m_decl_context
+ /// is the containing object.
+ void FindObjCMethodDecls(NameSearchContext &context);
+
+ /// Find all Objective-C properties and ivars with a given name.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext that can construct Decls for this name.
+ /// Its m_decl_name contains the name and its m_decl_context
+ /// is the containing object.
+ void FindObjCPropertyAndIvarDecls(NameSearchContext &context);
+
+ /// A wrapper for ClangASTContext::CopyType that sets a flag that
+ /// indicates that we should not respond to queries during import.
+ ///
+ /// \param[in] dest_context
+ /// The target AST context, typically the parser's AST context.
+ ///
+ /// \param[in] source_context
+ /// The source AST context, typically the AST context of whatever
+ /// symbol file the type was found in.
+ ///
+ /// \param[in] src_type
+ /// The source type.
+ ///
+ /// \return
+ /// The imported type.
+ CompilerType GuardedCopyType(const CompilerType &src_type);
+
+public:
+ /// Returns true if a name should be ignored by name lookup.
+ ///
+ /// \param[in] name
+ /// The name to be considered.
+ ///
+ /// \param[in] ignore_all_dollar_nmmes
+ /// True if $-names of all sorts should be ignored.
+ ///
+ /// \return
+ /// True if the name is one of a class of names that are ignored by
+ /// global lookup for performance reasons.
+ bool IgnoreName(const ConstString name, bool ignore_all_dollar_names);
+
+public:
+ /// Copies a single Decl into the parser's AST context.
+ ///
+ /// \param[in] src_decl
+ /// The Decl to copy.
+ ///
+ /// \return
+ /// A copy of the Decl in m_ast_context, or NULL if the copy failed.
+ clang::Decl *CopyDecl(clang::Decl *src_decl);
+
+ /// Copies a single Type to the target of the given ExternalASTMerger.
+ ///
+ /// \param[in] src_context
+ /// The ASTContext containing the type.
+ ///
+ /// \param[in] merger
+ /// The merger to use. This isn't just *m_merger_up because it might be
+ /// the persistent AST context's merger.
+ ///
+ /// \param[in] type
+ /// The type to copy.
+ ///
+ /// \return
+ /// A copy of the Type in the merger's target context.
+ clang::QualType CopyTypeWithMerger(clang::ASTContext &src_context,
+ clang::ExternalASTMerger &merger,
+ clang::QualType type);
+
+ /// Determined the origin of a single Decl, if it can be found.
+ ///
+ /// \param[in] decl
+ /// The Decl whose origin is to be found.
+ ///
+ /// \param[out] original_decl
+ /// A pointer whose target is filled in with the original Decl.
+ ///
+ /// \param[in] original_ctx
+ /// A pointer whose target is filled in with the original's ASTContext.
+ ///
+ /// \return
+ /// True if lookup succeeded; false otherwise.
+ bool ResolveDeclOrigin(const clang::Decl *decl, clang::Decl **original_decl,
+ clang::ASTContext **original_ctx);
+
+ /// Returns m_merger_up. Only call this if the target is configured to use
+ /// modern lookup,
+ clang::ExternalASTMerger &GetMergerUnchecked();
+
+ /// Returns true if there is a merger. This only occurs if the target is
+ /// using modern lookup.
+ bool HasMerger() { return (bool)m_merger_up; }
+
+protected:
+ bool FindObjCMethodDeclsWithOrigin(
+ unsigned int current_id, NameSearchContext &context,
+ clang::ObjCInterfaceDecl *original_interface_decl, const char *log_info);
+
+ friend struct NameSearchContext;
+
+ bool m_import_in_progress;
+ bool m_lookups_enabled;
+
+ const lldb::TargetSP
+ m_target; ///< The target to use in finding variables and types.
+ clang::ASTContext
+ *m_ast_context; ///< The AST context requests are coming in for.
+ clang::FileManager
+ *m_file_manager; ///< The file manager paired with the AST context.
+ lldb::ClangASTImporterSP m_ast_importer_sp; ///< The target's AST importer.
+ std::unique_ptr<clang::ExternalASTMerger> m_merger_up;
+ ///< The ExternalASTMerger for this parse.
+ std::set<const clang::Decl *> m_active_lexical_decls;
+ std::set<const char *> m_active_lookups;
+};
+
+/// \class NameSearchContext ClangASTSource.h
+/// "lldb/Expression/ClangASTSource.h" Container for all objects relevant to a
+/// single name lookup
+///
+/// LLDB needs to create Decls for entities it finds. This class communicates
+/// what name is being searched for and provides helper functions to construct
+/// Decls given appropriate type information.
+struct NameSearchContext {
+ ClangASTSource &m_ast_source; ///< The AST source making the request
+ llvm::SmallVectorImpl<clang::NamedDecl *>
+ &m_decls; ///< The list of declarations already constructed
+ ClangASTImporter::NamespaceMapSP m_namespace_map; ///< The mapping of all
+ ///namespaces found for this
+ ///request back to their
+ ///modules
+ const clang::DeclarationName &m_decl_name; ///< The name being looked for
+ const clang::DeclContext
+ *m_decl_context; ///< The DeclContext to put declarations into
+ llvm::SmallSet<CompilerType, 5> m_function_types; ///< All the types of
+ ///functions that have been
+ ///reported, so we don't
+ ///report conflicts
+
+ struct {
+ bool variable : 1;
+ bool function_with_type_info : 1;
+ bool function : 1;
+ bool local_vars_nsp : 1;
+ bool type : 1;
+ } m_found;
+
+ /// Constructor
+ ///
+ /// Initializes class variables.
+ ///
+ /// \param[in] astSource
+ /// A reference to the AST source making a request.
+ ///
+ /// \param[in] decls
+ /// A reference to a list into which new Decls will be placed. This
+ /// list is typically empty when the function is called.
+ ///
+ /// \param[in] name
+ /// The name being searched for (always an Identifier).
+ ///
+ /// \param[in] dc
+ /// The DeclContext to register Decls in.
+ NameSearchContext(ClangASTSource &astSource,
+ llvm::SmallVectorImpl<clang::NamedDecl *> &decls,
+ clang::DeclarationName &name, const clang::DeclContext *dc)
+ : m_ast_source(astSource), m_decls(decls), m_decl_name(name),
+ m_decl_context(dc) {
+ memset(&m_found, 0, sizeof(m_found));
+ }
+
+ /// Create a VarDecl with the name being searched for and the provided type
+ /// and register it in the right places.
+ ///
+ /// \param[in] type
+ /// The opaque QualType for the VarDecl being registered.
+ clang::NamedDecl *AddVarDecl(const CompilerType &type);
+
+ /// Create a FunDecl with the name being searched for and the provided type
+ /// and register it in the right places.
+ ///
+ /// \param[in] type
+ /// The opaque QualType for the FunDecl being registered.
+ ///
+ /// \param[in] extern_c
+ /// If true, build an extern "C" linkage specification for this.
+ clang::NamedDecl *AddFunDecl(const CompilerType &type, bool extern_c = false);
+
+ /// Create a FunDecl with the name being searched for and generic type (i.e.
+ /// intptr_t NAME_GOES_HERE(...)) and register it in the right places.
+ clang::NamedDecl *AddGenericFunDecl();
+
+ /// Create a TypeDecl with the name being searched for and the provided type
+ /// and register it in the right places.
+ ///
+ /// \param[in] compiler_type
+ /// The opaque QualType for the TypeDecl being registered.
+ clang::NamedDecl *AddTypeDecl(const CompilerType &compiler_type);
+
+ /// Add Decls from the provided DeclContextLookupResult to the list of
+ /// results.
+ ///
+ /// \param[in] result
+ /// The DeclContextLookupResult, usually returned as the result
+ /// of querying a DeclContext.
+ void AddLookupResult(clang::DeclContextLookupResult result);
+
+ /// Add a NamedDecl to the list of results.
+ ///
+ /// \param[in] decl
+ /// The NamedDecl, usually returned as the result
+ /// of querying a DeclContext.
+ void AddNamedDecl(clang::NamedDecl *decl);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangASTSource_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h
new file mode 100644
index 000000000000..db50c2aa3e90
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h
@@ -0,0 +1,49 @@
+//===-- ClangDiagnostic.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_ClangDiagnostic_h
+#define lldb_ClangDiagnostic_h
+
+#include <vector>
+
+#include "clang/Basic/Diagnostic.h"
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+#include "lldb/Expression/DiagnosticManager.h"
+
+namespace lldb_private {
+
+class ClangDiagnostic : public Diagnostic {
+public:
+ typedef std::vector<clang::FixItHint> FixItList;
+
+ static inline bool classof(const ClangDiagnostic *) { return true; }
+ static inline bool classof(const Diagnostic *diag) {
+ return diag->getKind() == eDiagnosticOriginClang;
+ }
+
+ ClangDiagnostic(const char *message, DiagnosticSeverity severity,
+ uint32_t compiler_id)
+ : Diagnostic(message, severity, eDiagnosticOriginClang, compiler_id) {}
+
+ ~ClangDiagnostic() override = default;
+
+ bool HasFixIts() const override { return !m_fixit_vec.empty(); }
+
+ void AddFixitHint(const clang::FixItHint &fixit) {
+ m_fixit_vec.push_back(fixit);
+ }
+
+ const FixItList &FixIts() const { return m_fixit_vec; }
+ FixItList m_fixit_vec;
+};
+
+} // namespace lldb_private
+#endif /* lldb_ClangDiagnostic_h */
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
new file mode 100644
index 000000000000..a49a7029e0d2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
@@ -0,0 +1,2254 @@
+//===-- ClangExpressionDeclMap.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangExpressionDeclMap.h"
+
+#include "ASTDumper.h"
+#include "ClangASTSource.h"
+#include "ClangModulesDeclVendor.h"
+#include "ClangPersistentVariables.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Expression/Materializer.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/CompilerDecl.h"
+#include "lldb/Symbol/CompilerDeclContext.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-private.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTImporter.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace clang;
+
+namespace {
+const char *g_lldb_local_vars_namespace_cstr = "$__lldb_local_vars";
+} // anonymous namespace
+
+ClangExpressionDeclMap::ClangExpressionDeclMap(
+ bool keep_result_in_memory,
+ Materializer::PersistentVariableDelegate *result_delegate,
+ ExecutionContext &exe_ctx, ValueObject *ctx_obj)
+ : ClangASTSource(exe_ctx.GetTargetSP()), m_found_entities(),
+ m_struct_members(), m_keep_result_in_memory(keep_result_in_memory),
+ m_result_delegate(result_delegate), m_ctx_obj(ctx_obj), m_parser_vars(),
+ m_struct_vars() {
+ EnableStructVars();
+}
+
+ClangExpressionDeclMap::~ClangExpressionDeclMap() {
+ // Note: The model is now that the parser's AST context and all associated
+ // data does not vanish until the expression has been executed. This means
+ // that valuable lookup data (like namespaces) doesn't vanish, but
+
+ DidParse();
+ DisableStructVars();
+}
+
+bool ClangExpressionDeclMap::WillParse(ExecutionContext &exe_ctx,
+ Materializer *materializer) {
+ ClangASTMetrics::ClearLocalCounters();
+
+ EnableParserVars();
+ m_parser_vars->m_exe_ctx = exe_ctx;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (exe_ctx.GetFramePtr())
+ m_parser_vars->m_sym_ctx =
+ exe_ctx.GetFramePtr()->GetSymbolContext(lldb::eSymbolContextEverything);
+ else if (exe_ctx.GetThreadPtr() &&
+ exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0))
+ m_parser_vars->m_sym_ctx =
+ exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)->GetSymbolContext(
+ lldb::eSymbolContextEverything);
+ else if (exe_ctx.GetProcessPtr()) {
+ m_parser_vars->m_sym_ctx.Clear(true);
+ m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP();
+ } else if (target) {
+ m_parser_vars->m_sym_ctx.Clear(true);
+ m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP();
+ }
+
+ if (target) {
+ m_parser_vars->m_persistent_vars = llvm::cast<ClangPersistentVariables>(
+ target->GetPersistentExpressionStateForLanguage(eLanguageTypeC));
+
+ if (!target->GetScratchClangASTContext())
+ return false;
+ }
+
+ m_parser_vars->m_target_info = GetTargetInfo();
+ m_parser_vars->m_materializer = materializer;
+
+ return true;
+}
+
+void ClangExpressionDeclMap::InstallCodeGenerator(
+ clang::ASTConsumer *code_gen) {
+ assert(m_parser_vars);
+ m_parser_vars->m_code_gen = code_gen;
+}
+
+void ClangExpressionDeclMap::DidParse() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ ClangASTMetrics::DumpCounters(log);
+
+ if (m_parser_vars) {
+ for (size_t entity_index = 0, num_entities = m_found_entities.GetSize();
+ entity_index < num_entities; ++entity_index) {
+ ExpressionVariableSP var_sp(
+ m_found_entities.GetVariableAtIndex(entity_index));
+ if (var_sp)
+ llvm::cast<ClangExpressionVariable>(var_sp.get())
+ ->DisableParserVars(GetParserID());
+ }
+
+ for (size_t pvar_index = 0,
+ num_pvars = m_parser_vars->m_persistent_vars->GetSize();
+ pvar_index < num_pvars; ++pvar_index) {
+ ExpressionVariableSP pvar_sp(
+ m_parser_vars->m_persistent_vars->GetVariableAtIndex(pvar_index));
+ if (ClangExpressionVariable *clang_var =
+ llvm::dyn_cast<ClangExpressionVariable>(pvar_sp.get()))
+ clang_var->DisableParserVars(GetParserID());
+ }
+
+ DisableParserVars();
+ }
+}
+
+// Interface for IRForTarget
+
+ClangExpressionDeclMap::TargetInfo ClangExpressionDeclMap::GetTargetInfo() {
+ assert(m_parser_vars.get());
+
+ TargetInfo ret;
+
+ ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx;
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ ret.byte_order = process->GetByteOrder();
+ ret.address_byte_size = process->GetAddressByteSize();
+ } else {
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ ret.byte_order = target->GetArchitecture().GetByteOrder();
+ ret.address_byte_size = target->GetArchitecture().GetAddressByteSize();
+ }
+ }
+
+ return ret;
+}
+
+namespace {
+/// This class walks an AST and ensures that all DeclContexts defined inside the
+/// current source file are properly complete.
+///
+/// This is used to ensure that persistent types defined in the current source
+/// file migrate completely to the persistent AST context before they are
+/// reused. If that didn't happen, it would be impoossible to complete them
+/// because their origin would be gone.
+///
+/// The stragtegy used by this class is to check the SourceLocation (to be
+/// specific, the FileID) and see if it's the FileID for the current expression.
+/// Alternate strategies could include checking whether an ExternalASTMerger,
+/// set up to not have the current context as a source, can find an original for
+/// the type.
+class Completer : public clang::RecursiveASTVisitor<Completer> {
+private:
+ clang::ASTImporter &m_exporter; /// Used to import Decl contents
+ clang::FileID m_file; /// The file that's going away
+ llvm::DenseSet<clang::Decl *> m_completed; /// Visited Decls, to avoid cycles
+
+ bool ImportAndCheckCompletable(clang::Decl *decl) {
+ (void)m_exporter.Import(decl);
+ if (m_completed.count(decl))
+ return false;
+ if (!llvm::isa<DeclContext>(decl))
+ return false;
+ const clang::SourceLocation loc = decl->getLocation();
+ if (!loc.isValid())
+ return false;
+ const clang::FileID file =
+ m_exporter.getFromContext().getSourceManager().getFileID(loc);
+ if (file != m_file)
+ return false;
+ // We are assuming the Decl was parsed in this very expression, so it
+ // should not have external storage.
+ lldbassert(!llvm::cast<DeclContext>(decl)->hasExternalLexicalStorage());
+ return true;
+ }
+
+ void Complete(clang::Decl *decl) {
+ m_completed.insert(decl);
+ auto *decl_context = llvm::cast<DeclContext>(decl);
+ (void)m_exporter.Import(decl);
+ m_exporter.CompleteDecl(decl);
+ for (Decl *child : decl_context->decls())
+ if (ImportAndCheckCompletable(child))
+ Complete(child);
+ }
+
+ void MaybeComplete(clang::Decl *decl) {
+ if (ImportAndCheckCompletable(decl))
+ Complete(decl);
+ }
+
+public:
+ Completer(clang::ASTImporter &exporter, clang::FileID file)
+ : m_exporter(exporter), m_file(file) {}
+
+ // Implements the RecursiveASTVisitor's core API. It is called on each Decl
+ // that the RecursiveASTVisitor encounters, and returns true if the traversal
+ // should continue.
+ bool VisitDecl(clang::Decl *decl) {
+ MaybeComplete(decl);
+ return true;
+ }
+};
+}
+
+static void CompleteAllDeclContexts(clang::ASTImporter &exporter,
+ clang::FileID file,
+ clang::QualType root) {
+ clang::QualType canonical_type = root.getCanonicalType();
+ if (clang::TagDecl *tag_decl = canonical_type->getAsTagDecl()) {
+ Completer(exporter, file).TraverseDecl(tag_decl);
+ } else if (auto interface_type = llvm::dyn_cast<ObjCInterfaceType>(
+ canonical_type.getTypePtr())) {
+ Completer(exporter, file).TraverseDecl(interface_type->getDecl());
+ } else {
+ Completer(exporter, file).TraverseType(canonical_type);
+ }
+}
+
+static clang::QualType ExportAllDeclaredTypes(
+ clang::ExternalASTMerger &merger,
+ clang::ASTContext &source, clang::FileManager &source_file_manager,
+ const clang::ExternalASTMerger::OriginMap &source_origin_map,
+ clang::FileID file, clang::QualType root) {
+ clang::ExternalASTMerger::ImporterSource importer_source =
+ { source, source_file_manager, source_origin_map };
+ merger.AddSources(importer_source);
+ clang::ASTImporter &exporter = merger.ImporterForOrigin(source);
+ CompleteAllDeclContexts(exporter, file, root);
+ llvm::Expected<clang::QualType> ret_or_error = exporter.Import(root);
+ merger.RemoveSources(importer_source);
+ if (ret_or_error) {
+ return *ret_or_error;
+ } else {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ LLDB_LOG_ERROR(log, ret_or_error.takeError(), "Couldn't import type: {0}");
+ return clang::QualType();
+ }
+}
+
+TypeFromUser ClangExpressionDeclMap::DeportType(ClangASTContext &target,
+ ClangASTContext &source,
+ TypeFromParser parser_type) {
+ assert (&target == m_target->GetScratchClangASTContext());
+ assert ((TypeSystem*)&source == parser_type.GetTypeSystem());
+ assert (source.getASTContext() == m_ast_context);
+
+ if (m_ast_importer_sp) {
+ return TypeFromUser(m_ast_importer_sp->DeportType(
+ target.getASTContext(), source.getASTContext(),
+ parser_type.GetOpaqueQualType()),
+ &target);
+ } else if (m_merger_up) {
+ clang::FileID source_file =
+ source.getASTContext()->getSourceManager().getFileID(
+ source.getASTContext()->getTranslationUnitDecl()->getLocation());
+ auto scratch_ast_context = static_cast<ClangASTContextForExpressions*>(
+ m_target->GetScratchClangASTContext());
+ clang::QualType exported_type = ExportAllDeclaredTypes(
+ scratch_ast_context->GetMergerUnchecked(),
+ *source.getASTContext(), *source.getFileManager(),
+ m_merger_up->GetOrigins(),
+ source_file,
+ clang::QualType::getFromOpaquePtr(parser_type.GetOpaqueQualType()));
+ return TypeFromUser(exported_type.getAsOpaquePtr(), &target);
+ } else {
+ lldbassert(0 && "No mechanism for deporting a type!");
+ return TypeFromUser();
+ }
+}
+
+bool ClangExpressionDeclMap::AddPersistentVariable(const NamedDecl *decl,
+ ConstString name,
+ TypeFromParser parser_type,
+ bool is_result,
+ bool is_lvalue) {
+ assert(m_parser_vars.get());
+
+ ClangASTContext *ast =
+ llvm::dyn_cast_or_null<ClangASTContext>(parser_type.GetTypeSystem());
+ if (ast == nullptr)
+ return false;
+
+ if (m_parser_vars->m_materializer && is_result) {
+ Status err;
+
+ ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx;
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target == nullptr)
+ return false;
+
+ TypeFromUser user_type =
+ DeportType(*target->GetScratchClangASTContext(), *ast, parser_type);
+
+ uint32_t offset = m_parser_vars->m_materializer->AddResultVariable(
+ user_type, is_lvalue, m_keep_result_in_memory, m_result_delegate, err);
+
+ ClangExpressionVariable *var = new ClangExpressionVariable(
+ exe_ctx.GetBestExecutionContextScope(), name, user_type,
+ m_parser_vars->m_target_info.byte_order,
+ m_parser_vars->m_target_info.address_byte_size);
+
+ m_found_entities.AddNewlyConstructedVariable(var);
+
+ var->EnableParserVars(GetParserID());
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ var->GetParserVars(GetParserID());
+
+ parser_vars->m_named_decl = decl;
+ parser_vars->m_parser_type = parser_type;
+
+ var->EnableJITVars(GetParserID());
+
+ ClangExpressionVariable::JITVars *jit_vars = var->GetJITVars(GetParserID());
+
+ jit_vars->m_offset = offset;
+
+ return true;
+ }
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+ ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx;
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target == nullptr)
+ return false;
+
+ ClangASTContext *context(target->GetScratchClangASTContext());
+
+ TypeFromUser user_type = DeportType(*context, *ast, parser_type);
+
+ if (!user_type.GetOpaqueQualType()) {
+ if (log)
+ log->Printf("Persistent variable's type wasn't copied successfully");
+ return false;
+ }
+
+ if (!m_parser_vars->m_target_info.IsValid())
+ return false;
+
+ ClangExpressionVariable *var = llvm::cast<ClangExpressionVariable>(
+ m_parser_vars->m_persistent_vars
+ ->CreatePersistentVariable(
+ exe_ctx.GetBestExecutionContextScope(), name, user_type,
+ m_parser_vars->m_target_info.byte_order,
+ m_parser_vars->m_target_info.address_byte_size)
+ .get());
+
+ if (!var)
+ return false;
+
+ var->m_frozen_sp->SetHasCompleteType();
+
+ if (is_result)
+ var->m_flags |= ClangExpressionVariable::EVNeedsFreezeDry;
+ else
+ var->m_flags |=
+ ClangExpressionVariable::EVKeepInTarget; // explicitly-declared
+ // persistent variables should
+ // persist
+
+ if (is_lvalue) {
+ var->m_flags |= ClangExpressionVariable::EVIsProgramReference;
+ } else {
+ var->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated;
+ var->m_flags |= ClangExpressionVariable::EVNeedsAllocation;
+ }
+
+ if (m_keep_result_in_memory) {
+ var->m_flags |= ClangExpressionVariable::EVKeepInTarget;
+ }
+
+ if (log)
+ log->Printf("Created persistent variable with flags 0x%hx", var->m_flags);
+
+ var->EnableParserVars(GetParserID());
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ var->GetParserVars(GetParserID());
+
+ parser_vars->m_named_decl = decl;
+ parser_vars->m_parser_type = parser_type;
+
+ return true;
+}
+
+bool ClangExpressionDeclMap::AddValueToStruct(const NamedDecl *decl,
+ ConstString name,
+ llvm::Value *value, size_t size,
+ lldb::offset_t alignment) {
+ assert(m_struct_vars.get());
+ assert(m_parser_vars.get());
+
+ bool is_persistent_variable = false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ m_struct_vars->m_struct_laid_out = false;
+
+ if (ClangExpressionVariable::FindVariableInList(m_struct_members, decl,
+ GetParserID()))
+ return true;
+
+ ClangExpressionVariable *var(ClangExpressionVariable::FindVariableInList(
+ m_found_entities, decl, GetParserID()));
+
+ if (!var) {
+ var = ClangExpressionVariable::FindVariableInList(
+ *m_parser_vars->m_persistent_vars, decl, GetParserID());
+ is_persistent_variable = true;
+ }
+
+ if (!var)
+ return false;
+
+ if (log)
+ log->Printf("Adding value for (NamedDecl*)%p [%s - %s] to the structure",
+ static_cast<const void *>(decl), name.GetCString(),
+ var->GetName().GetCString());
+
+ // We know entity->m_parser_vars is valid because we used a parser variable
+ // to find it
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ llvm::cast<ClangExpressionVariable>(var)->GetParserVars(GetParserID());
+
+ parser_vars->m_llvm_value = value;
+
+ if (ClangExpressionVariable::JITVars *jit_vars =
+ llvm::cast<ClangExpressionVariable>(var)->GetJITVars(GetParserID())) {
+ // We already laid this out; do not touch
+
+ if (log)
+ log->Printf("Already placed at 0x%llx",
+ (unsigned long long)jit_vars->m_offset);
+ }
+
+ llvm::cast<ClangExpressionVariable>(var)->EnableJITVars(GetParserID());
+
+ ClangExpressionVariable::JITVars *jit_vars =
+ llvm::cast<ClangExpressionVariable>(var)->GetJITVars(GetParserID());
+
+ jit_vars->m_alignment = alignment;
+ jit_vars->m_size = size;
+
+ m_struct_members.AddVariable(var->shared_from_this());
+
+ if (m_parser_vars->m_materializer) {
+ uint32_t offset = 0;
+
+ Status err;
+
+ if (is_persistent_variable) {
+ ExpressionVariableSP var_sp(var->shared_from_this());
+ offset = m_parser_vars->m_materializer->AddPersistentVariable(
+ var_sp, nullptr, err);
+ } else {
+ if (const lldb_private::Symbol *sym = parser_vars->m_lldb_sym)
+ offset = m_parser_vars->m_materializer->AddSymbol(*sym, err);
+ else if (const RegisterInfo *reg_info = var->GetRegisterInfo())
+ offset = m_parser_vars->m_materializer->AddRegister(*reg_info, err);
+ else if (parser_vars->m_lldb_var)
+ offset = m_parser_vars->m_materializer->AddVariable(
+ parser_vars->m_lldb_var, err);
+ }
+
+ if (!err.Success())
+ return false;
+
+ if (log)
+ log->Printf("Placed at 0x%llx", (unsigned long long)offset);
+
+ jit_vars->m_offset =
+ offset; // TODO DoStructLayout() should not change this.
+ }
+
+ return true;
+}
+
+bool ClangExpressionDeclMap::DoStructLayout() {
+ assert(m_struct_vars.get());
+
+ if (m_struct_vars->m_struct_laid_out)
+ return true;
+
+ if (!m_parser_vars->m_materializer)
+ return false;
+
+ m_struct_vars->m_struct_alignment =
+ m_parser_vars->m_materializer->GetStructAlignment();
+ m_struct_vars->m_struct_size =
+ m_parser_vars->m_materializer->GetStructByteSize();
+ m_struct_vars->m_struct_laid_out = true;
+ return true;
+}
+
+bool ClangExpressionDeclMap::GetStructInfo(uint32_t &num_elements, size_t &size,
+ lldb::offset_t &alignment) {
+ assert(m_struct_vars.get());
+
+ if (!m_struct_vars->m_struct_laid_out)
+ return false;
+
+ num_elements = m_struct_members.GetSize();
+ size = m_struct_vars->m_struct_size;
+ alignment = m_struct_vars->m_struct_alignment;
+
+ return true;
+}
+
+bool ClangExpressionDeclMap::GetStructElement(const NamedDecl *&decl,
+ llvm::Value *&value,
+ lldb::offset_t &offset,
+ ConstString &name,
+ uint32_t index) {
+ assert(m_struct_vars.get());
+
+ if (!m_struct_vars->m_struct_laid_out)
+ return false;
+
+ if (index >= m_struct_members.GetSize())
+ return false;
+
+ ExpressionVariableSP member_sp(m_struct_members.GetVariableAtIndex(index));
+
+ if (!member_sp)
+ return false;
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ llvm::cast<ClangExpressionVariable>(member_sp.get())
+ ->GetParserVars(GetParserID());
+ ClangExpressionVariable::JITVars *jit_vars =
+ llvm::cast<ClangExpressionVariable>(member_sp.get())
+ ->GetJITVars(GetParserID());
+
+ if (!parser_vars || !jit_vars || !member_sp->GetValueObject())
+ return false;
+
+ decl = parser_vars->m_named_decl;
+ value = parser_vars->m_llvm_value;
+ offset = jit_vars->m_offset;
+ name = member_sp->GetName();
+
+ return true;
+}
+
+bool ClangExpressionDeclMap::GetFunctionInfo(const NamedDecl *decl,
+ uint64_t &ptr) {
+ ClangExpressionVariable *entity(ClangExpressionVariable::FindVariableInList(
+ m_found_entities, decl, GetParserID()));
+
+ if (!entity)
+ return false;
+
+ // We know m_parser_vars is valid since we searched for the variable by its
+ // NamedDecl
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ entity->GetParserVars(GetParserID());
+
+ ptr = parser_vars->m_lldb_value.GetScalar().ULongLong();
+
+ return true;
+}
+
+addr_t ClangExpressionDeclMap::GetSymbolAddress(Target &target,
+ Process *process,
+ ConstString name,
+ lldb::SymbolType symbol_type,
+ lldb_private::Module *module) {
+ SymbolContextList sc_list;
+
+ if (module)
+ module->FindSymbolsWithNameAndType(name, symbol_type, sc_list);
+ else
+ target.GetImages().FindSymbolsWithNameAndType(name, symbol_type, sc_list);
+
+ const uint32_t num_matches = sc_list.GetSize();
+ addr_t symbol_load_addr = LLDB_INVALID_ADDRESS;
+
+ for (uint32_t i = 0;
+ i < num_matches &&
+ (symbol_load_addr == 0 || symbol_load_addr == LLDB_INVALID_ADDRESS);
+ i++) {
+ SymbolContext sym_ctx;
+ sc_list.GetContextAtIndex(i, sym_ctx);
+
+ const Address sym_address = sym_ctx.symbol->GetAddress();
+
+ if (!sym_address.IsValid())
+ continue;
+
+ switch (sym_ctx.symbol->GetType()) {
+ case eSymbolTypeCode:
+ case eSymbolTypeTrampoline:
+ symbol_load_addr = sym_address.GetCallableLoadAddress(&target);
+ break;
+
+ case eSymbolTypeResolver:
+ symbol_load_addr = sym_address.GetCallableLoadAddress(&target, true);
+ break;
+
+ case eSymbolTypeReExported: {
+ ConstString reexport_name = sym_ctx.symbol->GetReExportedSymbolName();
+ if (reexport_name) {
+ ModuleSP reexport_module_sp;
+ ModuleSpec reexport_module_spec;
+ reexport_module_spec.GetPlatformFileSpec() =
+ sym_ctx.symbol->GetReExportedSymbolSharedLibrary();
+ if (reexport_module_spec.GetPlatformFileSpec()) {
+ reexport_module_sp =
+ target.GetImages().FindFirstModule(reexport_module_spec);
+ if (!reexport_module_sp) {
+ reexport_module_spec.GetPlatformFileSpec().GetDirectory().Clear();
+ reexport_module_sp =
+ target.GetImages().FindFirstModule(reexport_module_spec);
+ }
+ }
+ symbol_load_addr = GetSymbolAddress(
+ target, process, sym_ctx.symbol->GetReExportedSymbolName(),
+ symbol_type, reexport_module_sp.get());
+ }
+ } break;
+
+ case eSymbolTypeData:
+ case eSymbolTypeRuntime:
+ case eSymbolTypeVariable:
+ case eSymbolTypeLocal:
+ case eSymbolTypeParam:
+ case eSymbolTypeInvalid:
+ case eSymbolTypeAbsolute:
+ case eSymbolTypeException:
+ case eSymbolTypeSourceFile:
+ case eSymbolTypeHeaderFile:
+ case eSymbolTypeObjectFile:
+ case eSymbolTypeCommonBlock:
+ case eSymbolTypeBlock:
+ case eSymbolTypeVariableType:
+ case eSymbolTypeLineEntry:
+ case eSymbolTypeLineHeader:
+ case eSymbolTypeScopeBegin:
+ case eSymbolTypeScopeEnd:
+ case eSymbolTypeAdditional:
+ case eSymbolTypeCompiler:
+ case eSymbolTypeInstrumentation:
+ case eSymbolTypeUndefined:
+ case eSymbolTypeObjCClass:
+ case eSymbolTypeObjCMetaClass:
+ case eSymbolTypeObjCIVar:
+ symbol_load_addr = sym_address.GetLoadAddress(&target);
+ break;
+ }
+ }
+
+ if (symbol_load_addr == LLDB_INVALID_ADDRESS && process) {
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process);
+
+ if (runtime) {
+ symbol_load_addr = runtime->LookupRuntimeSymbol(name);
+ }
+ }
+
+ return symbol_load_addr;
+}
+
+addr_t ClangExpressionDeclMap::GetSymbolAddress(ConstString name,
+ lldb::SymbolType symbol_type) {
+ assert(m_parser_vars.get());
+
+ if (!m_parser_vars->m_exe_ctx.GetTargetPtr())
+ return false;
+
+ return GetSymbolAddress(m_parser_vars->m_exe_ctx.GetTargetRef(),
+ m_parser_vars->m_exe_ctx.GetProcessPtr(), name,
+ symbol_type);
+}
+
+lldb::VariableSP ClangExpressionDeclMap::FindGlobalVariable(
+ Target &target, ModuleSP &module, ConstString name,
+ CompilerDeclContext *namespace_decl, TypeFromUser *type) {
+ VariableList vars;
+
+ if (module && namespace_decl)
+ module->FindGlobalVariables(name, namespace_decl, -1, vars);
+ else
+ target.GetImages().FindGlobalVariables(name, -1, vars);
+
+ if (vars.GetSize()) {
+ if (type) {
+ for (size_t i = 0; i < vars.GetSize(); ++i) {
+ VariableSP var_sp = vars.GetVariableAtIndex(i);
+
+ if (ClangASTContext::AreTypesSame(
+ *type, var_sp->GetType()->GetFullCompilerType()))
+ return var_sp;
+ }
+ } else {
+ return vars.GetVariableAtIndex(0);
+ }
+ }
+
+ return VariableSP();
+}
+
+ClangASTContext *ClangExpressionDeclMap::GetClangASTContext() {
+ StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr();
+ if (frame == nullptr)
+ return nullptr;
+
+ SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction |
+ lldb::eSymbolContextBlock);
+ if (sym_ctx.block == nullptr)
+ return nullptr;
+
+ CompilerDeclContext frame_decl_context = sym_ctx.block->GetDeclContext();
+ if (!frame_decl_context)
+ return nullptr;
+
+ return llvm::dyn_cast_or_null<ClangASTContext>(
+ frame_decl_context.GetTypeSystem());
+}
+
+// Interface for ClangASTSource
+
+void ClangExpressionDeclMap::FindExternalVisibleDecls(
+ NameSearchContext &context) {
+ assert(m_ast_context);
+
+ ClangASTMetrics::RegisterVisibleQuery();
+
+ const ConstString name(context.m_decl_name.getAsString().c_str());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (GetImportInProgress()) {
+ if (log && log->GetVerbose())
+ log->Printf("Ignoring a query during an import");
+ return;
+ }
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ if (log) {
+ if (!context.m_decl_context)
+ log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for "
+ "'%s' in a NULL DeclContext",
+ current_id, name.GetCString());
+ else if (const NamedDecl *context_named_decl =
+ dyn_cast<NamedDecl>(context.m_decl_context))
+ log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for "
+ "'%s' in '%s'",
+ current_id, name.GetCString(),
+ context_named_decl->getNameAsString().c_str());
+ else
+ log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for "
+ "'%s' in a '%s'",
+ current_id, name.GetCString(),
+ context.m_decl_context->getDeclKindName());
+ }
+
+ if (const NamespaceDecl *namespace_context =
+ dyn_cast<NamespaceDecl>(context.m_decl_context)) {
+ if (namespace_context->getName().str() ==
+ std::string(g_lldb_local_vars_namespace_cstr)) {
+ CompilerDeclContext compiler_decl_ctx(
+ GetClangASTContext(), const_cast<void *>(static_cast<const void *>(
+ context.m_decl_context)));
+ FindExternalVisibleDecls(context, lldb::ModuleSP(), compiler_decl_ctx,
+ current_id);
+ return;
+ }
+
+ ClangASTImporter::NamespaceMapSP namespace_map =
+ m_ast_importer_sp
+ ? m_ast_importer_sp->GetNamespaceMap(namespace_context)
+ : ClangASTImporter::NamespaceMapSP();
+
+ if (!namespace_map)
+ return;
+
+ if (log && log->GetVerbose())
+ log->Printf(" CEDM::FEVD[%u] Inspecting (NamespaceMap*)%p (%d entries)",
+ current_id, static_cast<void *>(namespace_map.get()),
+ (int)namespace_map->size());
+
+ for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(),
+ e = namespace_map->end();
+ i != e; ++i) {
+ if (log)
+ log->Printf(" CEDM::FEVD[%u] Searching namespace %s in module %s",
+ current_id, i->second.GetName().AsCString(),
+ i->first->GetFileSpec().GetFilename().GetCString());
+
+ FindExternalVisibleDecls(context, i->first, i->second, current_id);
+ }
+ } else if (isa<TranslationUnitDecl>(context.m_decl_context)) {
+ CompilerDeclContext namespace_decl;
+
+ if (log)
+ log->Printf(" CEDM::FEVD[%u] Searching the root namespace", current_id);
+
+ FindExternalVisibleDecls(context, lldb::ModuleSP(), namespace_decl,
+ current_id);
+ }
+
+ ClangASTSource::FindExternalVisibleDecls(context);
+}
+
+void ClangExpressionDeclMap::FindExternalVisibleDecls(
+ NameSearchContext &context, lldb::ModuleSP module_sp,
+ CompilerDeclContext &namespace_decl, unsigned int current_id) {
+ assert(m_ast_context);
+
+ std::function<void(clang::FunctionDecl *)> MaybeRegisterFunctionBody =
+ [this](clang::FunctionDecl *copied_function_decl) {
+ if (copied_function_decl->getBody() && m_parser_vars->m_code_gen) {
+ DeclGroupRef decl_group_ref(copied_function_decl);
+ m_parser_vars->m_code_gen->HandleTopLevelDecl(decl_group_ref);
+ }
+ };
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ SymbolContextList sc_list;
+
+ const ConstString name(context.m_decl_name.getAsString().c_str());
+ if (IgnoreName(name, false))
+ return;
+
+ // Only look for functions by name out in our symbols if the function doesn't
+ // start with our phony prefix of '$'
+ Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
+ StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr();
+ SymbolContext sym_ctx;
+ if (frame != nullptr)
+ sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction |
+ lldb::eSymbolContextBlock);
+
+ // Try the persistent decls, which take precedence over all else.
+ if (!namespace_decl) {
+ do {
+ if (!target)
+ break;
+
+ ClangASTContext *scratch_clang_ast_context =
+ target->GetScratchClangASTContext();
+
+ if (!scratch_clang_ast_context)
+ break;
+
+ ASTContext *scratch_ast_context =
+ scratch_clang_ast_context->getASTContext();
+
+ if (!scratch_ast_context)
+ break;
+
+ NamedDecl *persistent_decl =
+ m_parser_vars->m_persistent_vars->GetPersistentDecl(name);
+
+ if (!persistent_decl)
+ break;
+
+ Decl *parser_persistent_decl = CopyDecl(persistent_decl);
+
+ if (!parser_persistent_decl)
+ break;
+
+ NamedDecl *parser_named_decl =
+ dyn_cast<NamedDecl>(parser_persistent_decl);
+
+ if (!parser_named_decl)
+ break;
+
+ if (clang::FunctionDecl *parser_function_decl =
+ llvm::dyn_cast<clang::FunctionDecl>(parser_named_decl)) {
+ MaybeRegisterFunctionBody(parser_function_decl);
+ }
+
+ if (log)
+ log->Printf(" CEDM::FEVD[%u] Found persistent decl %s", current_id,
+ name.GetCString());
+
+ context.AddNamedDecl(parser_named_decl);
+ } while (false);
+ }
+
+ if (name.GetCString()[0] == '$' && !namespace_decl) {
+ static ConstString g_lldb_class_name("$__lldb_class");
+
+ if (name == g_lldb_class_name) {
+ if (m_ctx_obj) {
+ Status status;
+ lldb::ValueObjectSP ctx_obj_ptr = m_ctx_obj->AddressOf(status);
+ if (!ctx_obj_ptr || status.Fail())
+ return;
+
+ AddThisType(context, TypeFromUser(m_ctx_obj->GetCompilerType()),
+ current_id);
+
+ m_struct_vars->m_object_pointer_type =
+ TypeFromUser(ctx_obj_ptr->GetCompilerType());
+
+ return;
+ }
+
+ // Clang is looking for the type of "this"
+
+ if (frame == nullptr)
+ return;
+
+ // Find the block that defines the function represented by "sym_ctx"
+ Block *function_block = sym_ctx.GetFunctionBlock();
+
+ if (!function_block)
+ return;
+
+ CompilerDeclContext function_decl_ctx = function_block->GetDeclContext();
+
+ if (!function_decl_ctx)
+ return;
+
+ clang::CXXMethodDecl *method_decl =
+ ClangASTContext::DeclContextGetAsCXXMethodDecl(function_decl_ctx);
+
+ if (method_decl) {
+ clang::CXXRecordDecl *class_decl = method_decl->getParent();
+
+ QualType class_qual_type(class_decl->getTypeForDecl(), 0);
+
+ TypeFromUser class_user_type(
+ class_qual_type.getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&class_decl->getASTContext()));
+
+ if (log) {
+ ASTDumper ast_dumper(class_qual_type);
+ log->Printf(" CEDM::FEVD[%u] Adding type for $__lldb_class: %s",
+ current_id, ast_dumper.GetCString());
+ }
+
+ AddThisType(context, class_user_type, current_id);
+
+ if (method_decl->isInstance()) {
+ // self is a pointer to the object
+
+ QualType class_pointer_type =
+ method_decl->getASTContext().getPointerType(class_qual_type);
+
+ TypeFromUser self_user_type(
+ class_pointer_type.getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&method_decl->getASTContext()));
+
+ m_struct_vars->m_object_pointer_type = self_user_type;
+ }
+ } else {
+ // This branch will get hit if we are executing code in the context of
+ // a function that claims to have an object pointer (through
+ // DW_AT_object_pointer?) but is not formally a method of the class.
+ // In that case, just look up the "this" variable in the current scope
+ // and use its type.
+ // FIXME: This code is formally correct, but clang doesn't currently
+ // emit DW_AT_object_pointer
+ // for C++ so it hasn't actually been tested.
+
+ VariableList *vars = frame->GetVariableList(false);
+
+ lldb::VariableSP this_var = vars->FindVariable(ConstString("this"));
+
+ if (this_var && this_var->IsInScope(frame) &&
+ this_var->LocationIsValidForFrame(frame)) {
+ Type *this_type = this_var->GetType();
+
+ if (!this_type)
+ return;
+
+ TypeFromUser pointee_type =
+ this_type->GetForwardCompilerType().GetPointeeType();
+
+ if (pointee_type.IsValid()) {
+ if (log) {
+ ASTDumper ast_dumper(pointee_type);
+ log->Printf(" FEVD[%u] Adding type for $__lldb_class: %s",
+ current_id, ast_dumper.GetCString());
+ }
+
+ AddThisType(context, pointee_type, current_id);
+ TypeFromUser this_user_type(this_type->GetFullCompilerType());
+ m_struct_vars->m_object_pointer_type = this_user_type;
+ return;
+ }
+ }
+ }
+
+ return;
+ }
+
+ static ConstString g_lldb_objc_class_name("$__lldb_objc_class");
+ if (name == g_lldb_objc_class_name) {
+ if (m_ctx_obj) {
+ Status status;
+ lldb::ValueObjectSP ctx_obj_ptr = m_ctx_obj->AddressOf(status);
+ if (!ctx_obj_ptr || status.Fail())
+ return;
+
+ AddOneType(context, TypeFromUser(m_ctx_obj->GetCompilerType()),
+ current_id);
+
+ m_struct_vars->m_object_pointer_type =
+ TypeFromUser(ctx_obj_ptr->GetCompilerType());
+
+ return;
+ }
+
+ // Clang is looking for the type of "*self"
+
+ if (!frame)
+ return;
+
+ SymbolContext sym_ctx = frame->GetSymbolContext(
+ lldb::eSymbolContextFunction | lldb::eSymbolContextBlock);
+
+ // Find the block that defines the function represented by "sym_ctx"
+ Block *function_block = sym_ctx.GetFunctionBlock();
+
+ if (!function_block)
+ return;
+
+ CompilerDeclContext function_decl_ctx = function_block->GetDeclContext();
+
+ if (!function_decl_ctx)
+ return;
+
+ clang::ObjCMethodDecl *method_decl =
+ ClangASTContext::DeclContextGetAsObjCMethodDecl(function_decl_ctx);
+
+ if (method_decl) {
+ ObjCInterfaceDecl *self_interface = method_decl->getClassInterface();
+
+ if (!self_interface)
+ return;
+
+ const clang::Type *interface_type = self_interface->getTypeForDecl();
+
+ if (!interface_type)
+ return; // This is unlikely, but we have seen crashes where this
+ // occurred
+
+ TypeFromUser class_user_type(
+ QualType(interface_type, 0).getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&method_decl->getASTContext()));
+
+ if (log) {
+ ASTDumper ast_dumper(interface_type);
+ log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s",
+ current_id, ast_dumper.GetCString());
+ }
+
+ AddOneType(context, class_user_type, current_id);
+
+ if (method_decl->isInstanceMethod()) {
+ // self is a pointer to the object
+
+ QualType class_pointer_type =
+ method_decl->getASTContext().getObjCObjectPointerType(
+ QualType(interface_type, 0));
+
+ TypeFromUser self_user_type(
+ class_pointer_type.getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&method_decl->getASTContext()));
+
+ m_struct_vars->m_object_pointer_type = self_user_type;
+ } else {
+ // self is a Class pointer
+ QualType class_type = method_decl->getASTContext().getObjCClassType();
+
+ TypeFromUser self_user_type(
+ class_type.getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&method_decl->getASTContext()));
+
+ m_struct_vars->m_object_pointer_type = self_user_type;
+ }
+
+ return;
+ } else {
+ // This branch will get hit if we are executing code in the context of
+ // a function that claims to have an object pointer (through
+ // DW_AT_object_pointer?) but is not formally a method of the class.
+ // In that case, just look up the "self" variable in the current scope
+ // and use its type.
+
+ VariableList *vars = frame->GetVariableList(false);
+
+ lldb::VariableSP self_var = vars->FindVariable(ConstString("self"));
+
+ if (self_var && self_var->IsInScope(frame) &&
+ self_var->LocationIsValidForFrame(frame)) {
+ Type *self_type = self_var->GetType();
+
+ if (!self_type)
+ return;
+
+ CompilerType self_clang_type = self_type->GetFullCompilerType();
+
+ if (ClangASTContext::IsObjCClassType(self_clang_type)) {
+ return;
+ } else if (ClangASTContext::IsObjCObjectPointerType(
+ self_clang_type)) {
+ self_clang_type = self_clang_type.GetPointeeType();
+
+ if (!self_clang_type)
+ return;
+
+ if (log) {
+ ASTDumper ast_dumper(self_type->GetFullCompilerType());
+ log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s",
+ current_id, ast_dumper.GetCString());
+ }
+
+ TypeFromUser class_user_type(self_clang_type);
+
+ AddOneType(context, class_user_type, current_id);
+
+ TypeFromUser self_user_type(self_type->GetFullCompilerType());
+
+ m_struct_vars->m_object_pointer_type = self_user_type;
+ return;
+ }
+ }
+ }
+
+ return;
+ }
+
+ if (name == ConstString(g_lldb_local_vars_namespace_cstr)) {
+ CompilerDeclContext frame_decl_context =
+ sym_ctx.block != nullptr ? sym_ctx.block->GetDeclContext()
+ : CompilerDeclContext();
+
+ if (frame_decl_context) {
+ ClangASTContext *ast = llvm::dyn_cast_or_null<ClangASTContext>(
+ frame_decl_context.GetTypeSystem());
+
+ if (ast) {
+ clang::NamespaceDecl *namespace_decl =
+ ClangASTContext::GetUniqueNamespaceDeclaration(
+ m_ast_context, name.GetCString(), nullptr);
+ if (namespace_decl) {
+ context.AddNamedDecl(namespace_decl);
+ clang::DeclContext *clang_decl_ctx =
+ clang::Decl::castToDeclContext(namespace_decl);
+ clang_decl_ctx->setHasExternalVisibleStorage(true);
+ context.m_found.local_vars_nsp = true;
+ }
+ }
+ }
+
+ return;
+ }
+
+ // any other $__lldb names should be weeded out now
+ if (name.GetStringRef().startswith("$__lldb"))
+ return;
+
+ ExpressionVariableSP pvar_sp(
+ m_parser_vars->m_persistent_vars->GetVariable(name));
+
+ if (pvar_sp) {
+ AddOneVariable(context, pvar_sp, current_id);
+ return;
+ }
+
+ const char *reg_name(&name.GetCString()[1]);
+
+ if (m_parser_vars->m_exe_ctx.GetRegisterContext()) {
+ const RegisterInfo *reg_info(
+ m_parser_vars->m_exe_ctx.GetRegisterContext()->GetRegisterInfoByName(
+ reg_name));
+
+ if (reg_info) {
+ if (log)
+ log->Printf(" CEDM::FEVD[%u] Found register %s", current_id,
+ reg_info->name);
+
+ AddOneRegister(context, reg_info, current_id);
+ }
+ }
+ } else {
+ ValueObjectSP valobj;
+ VariableSP var;
+
+ bool local_var_lookup =
+ !namespace_decl || (namespace_decl.GetName() ==
+ ConstString(g_lldb_local_vars_namespace_cstr));
+ if (frame && local_var_lookup) {
+ CompilerDeclContext compiler_decl_context =
+ sym_ctx.block != nullptr ? sym_ctx.block->GetDeclContext()
+ : CompilerDeclContext();
+
+ if (compiler_decl_context) {
+ // Make sure that the variables are parsed so that we have the
+ // declarations.
+ VariableListSP vars = frame->GetInScopeVariableList(true);
+ for (size_t i = 0; i < vars->GetSize(); i++)
+ vars->GetVariableAtIndex(i)->GetDecl();
+
+ // Search for declarations matching the name. Do not include imported
+ // decls in the search if we are looking for decls in the artificial
+ // namespace $__lldb_local_vars.
+ std::vector<CompilerDecl> found_decls =
+ compiler_decl_context.FindDeclByName(name,
+ namespace_decl.IsValid());
+
+ bool variable_found = false;
+ for (CompilerDecl decl : found_decls) {
+ for (size_t vi = 0, ve = vars->GetSize(); vi != ve; ++vi) {
+ VariableSP candidate_var = vars->GetVariableAtIndex(vi);
+ if (candidate_var->GetDecl() == decl) {
+ var = candidate_var;
+ break;
+ }
+ }
+
+ if (var && !variable_found) {
+ variable_found = true;
+ valobj = ValueObjectVariable::Create(frame, var);
+ AddOneVariable(context, var, valobj, current_id);
+ context.m_found.variable = true;
+ }
+ }
+ if (variable_found)
+ return;
+ }
+ }
+ if (target) {
+ var = FindGlobalVariable(*target, module_sp, name, &namespace_decl,
+ nullptr);
+
+ if (var) {
+ valobj = ValueObjectVariable::Create(target, var);
+ AddOneVariable(context, var, valobj, current_id);
+ context.m_found.variable = true;
+ return;
+ }
+ }
+
+ std::vector<clang::NamedDecl *> decls_from_modules;
+
+ if (target) {
+ if (ClangModulesDeclVendor *decl_vendor =
+ target->GetClangModulesDeclVendor()) {
+ decl_vendor->FindDecls(name, false, UINT32_MAX, decls_from_modules);
+ }
+ }
+
+ const bool include_inlines = false;
+ const bool append = false;
+
+ if (namespace_decl && module_sp) {
+ const bool include_symbols = false;
+
+ module_sp->FindFunctions(name, &namespace_decl, eFunctionNameTypeBase,
+ include_symbols, include_inlines, append,
+ sc_list);
+ } else if (target && !namespace_decl) {
+ const bool include_symbols = true;
+
+ // TODO Fix FindFunctions so that it doesn't return
+ // instance methods for eFunctionNameTypeBase.
+
+ target->GetImages().FindFunctions(name, eFunctionNameTypeFull,
+ include_symbols, include_inlines,
+ append, sc_list);
+ }
+
+ // If we found more than one function, see if we can use the frame's decl
+ // context to remove functions that are shadowed by other functions which
+ // match in type but are nearer in scope.
+ //
+ // AddOneFunction will not add a function whose type has already been
+ // added, so if there's another function in the list with a matching type,
+ // check to see if their decl context is a parent of the current frame's or
+ // was imported via a and using statement, and pick the best match
+ // according to lookup rules.
+ if (sc_list.GetSize() > 1) {
+ // Collect some info about our frame's context.
+ StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr();
+ SymbolContext frame_sym_ctx;
+ if (frame != nullptr)
+ frame_sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction |
+ lldb::eSymbolContextBlock);
+ CompilerDeclContext frame_decl_context =
+ frame_sym_ctx.block != nullptr ? frame_sym_ctx.block->GetDeclContext()
+ : CompilerDeclContext();
+
+ // We can't do this without a compiler decl context for our frame.
+ if (frame_decl_context) {
+ clang::DeclContext *frame_decl_ctx =
+ (clang::DeclContext *)frame_decl_context.GetOpaqueDeclContext();
+ ClangASTContext *ast = llvm::dyn_cast_or_null<ClangASTContext>(
+ frame_decl_context.GetTypeSystem());
+
+ // Structure to hold the info needed when comparing function
+ // declarations.
+ struct FuncDeclInfo {
+ ConstString m_name;
+ CompilerType m_copied_type;
+ uint32_t m_decl_lvl;
+ SymbolContext m_sym_ctx;
+ };
+
+ // First, symplify things by looping through the symbol contexts to
+ // remove unwanted functions and separate out the functions we want to
+ // compare and prune into a separate list. Cache the info needed about
+ // the function declarations in a vector for efficiency.
+ SymbolContextList sc_sym_list;
+ uint32_t num_indices = sc_list.GetSize();
+ std::vector<FuncDeclInfo> fdi_cache;
+ fdi_cache.reserve(num_indices);
+ for (uint32_t index = 0; index < num_indices; ++index) {
+ FuncDeclInfo fdi;
+ SymbolContext sym_ctx;
+ sc_list.GetContextAtIndex(index, sym_ctx);
+
+ // We don't know enough about symbols to compare them, but we should
+ // keep them in the list.
+ Function *function = sym_ctx.function;
+ if (!function) {
+ sc_sym_list.Append(sym_ctx);
+ continue;
+ }
+ // Filter out functions without declaration contexts, as well as
+ // class/instance methods, since they'll be skipped in the code that
+ // follows anyway.
+ CompilerDeclContext func_decl_context = function->GetDeclContext();
+ if (!func_decl_context ||
+ func_decl_context.IsClassMethod(nullptr, nullptr, nullptr))
+ continue;
+ // We can only prune functions for which we can copy the type.
+ CompilerType func_clang_type =
+ function->GetType()->GetFullCompilerType();
+ CompilerType copied_func_type = GuardedCopyType(func_clang_type);
+ if (!copied_func_type) {
+ sc_sym_list.Append(sym_ctx);
+ continue;
+ }
+
+ fdi.m_sym_ctx = sym_ctx;
+ fdi.m_name = function->GetName();
+ fdi.m_copied_type = copied_func_type;
+ fdi.m_decl_lvl = LLDB_INVALID_DECL_LEVEL;
+ if (fdi.m_copied_type && func_decl_context) {
+ // Call CountDeclLevels to get the number of parent scopes we have
+ // to look through before we find the function declaration. When
+ // comparing functions of the same type, the one with a lower count
+ // will be closer to us in the lookup scope and shadows the other.
+ clang::DeclContext *func_decl_ctx =
+ (clang::DeclContext *)func_decl_context.GetOpaqueDeclContext();
+ fdi.m_decl_lvl = ast->CountDeclLevels(
+ frame_decl_ctx, func_decl_ctx, &fdi.m_name, &fdi.m_copied_type);
+ }
+ fdi_cache.emplace_back(fdi);
+ }
+
+ // Loop through the functions in our cache looking for matching types,
+ // then compare their scope levels to see which is closer.
+ std::multimap<CompilerType, const FuncDeclInfo *> matches;
+ for (const FuncDeclInfo &fdi : fdi_cache) {
+ const CompilerType t = fdi.m_copied_type;
+ auto q = matches.find(t);
+ if (q != matches.end()) {
+ if (q->second->m_decl_lvl > fdi.m_decl_lvl)
+ // This function is closer; remove the old set.
+ matches.erase(t);
+ else if (q->second->m_decl_lvl < fdi.m_decl_lvl)
+ // The functions in our set are closer - skip this one.
+ continue;
+ }
+ matches.insert(std::make_pair(t, &fdi));
+ }
+
+ // Loop through our matches and add their symbol contexts to our list.
+ SymbolContextList sc_func_list;
+ for (const auto &q : matches)
+ sc_func_list.Append(q.second->m_sym_ctx);
+
+ // Rejoin the lists with the functions in front.
+ sc_list = sc_func_list;
+ sc_list.Append(sc_sym_list);
+ }
+ }
+
+ if (sc_list.GetSize()) {
+ Symbol *extern_symbol = nullptr;
+ Symbol *non_extern_symbol = nullptr;
+
+ for (uint32_t index = 0, num_indices = sc_list.GetSize();
+ index < num_indices; ++index) {
+ SymbolContext sym_ctx;
+ sc_list.GetContextAtIndex(index, sym_ctx);
+
+ if (sym_ctx.function) {
+ CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext();
+
+ if (!decl_ctx)
+ continue;
+
+ // Filter out class/instance methods.
+ if (decl_ctx.IsClassMethod(nullptr, nullptr, nullptr))
+ continue;
+
+ AddOneFunction(context, sym_ctx.function, nullptr, current_id);
+ context.m_found.function_with_type_info = true;
+ context.m_found.function = true;
+ } else if (sym_ctx.symbol) {
+ if (sym_ctx.symbol->GetType() == eSymbolTypeReExported && target) {
+ sym_ctx.symbol = sym_ctx.symbol->ResolveReExportedSymbol(*target);
+ if (sym_ctx.symbol == nullptr)
+ continue;
+ }
+
+ if (sym_ctx.symbol->IsExternal())
+ extern_symbol = sym_ctx.symbol;
+ else
+ non_extern_symbol = sym_ctx.symbol;
+ }
+ }
+
+ if (!context.m_found.function_with_type_info) {
+ for (clang::NamedDecl *decl : decls_from_modules) {
+ if (llvm::isa<clang::FunctionDecl>(decl)) {
+ clang::NamedDecl *copied_decl =
+ llvm::cast_or_null<FunctionDecl>(CopyDecl(decl));
+ if (copied_decl) {
+ context.AddNamedDecl(copied_decl);
+ context.m_found.function_with_type_info = true;
+ }
+ }
+ }
+ }
+
+ if (!context.m_found.function_with_type_info) {
+ if (extern_symbol) {
+ AddOneFunction(context, nullptr, extern_symbol, current_id);
+ context.m_found.function = true;
+ } else if (non_extern_symbol) {
+ AddOneFunction(context, nullptr, non_extern_symbol, current_id);
+ context.m_found.function = true;
+ }
+ }
+ }
+
+ if (!context.m_found.function_with_type_info) {
+ // Try the modules next.
+
+ do {
+ if (ClangModulesDeclVendor *modules_decl_vendor =
+ m_target->GetClangModulesDeclVendor()) {
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!modules_decl_vendor->FindDecls(name, append, max_matches, decls))
+ break;
+
+ clang::NamedDecl *const decl_from_modules = decls[0];
+
+ if (llvm::isa<clang::FunctionDecl>(decl_from_modules)) {
+ if (log) {
+ log->Printf(" CAS::FEVD[%u] Matching function found for "
+ "\"%s\" in the modules",
+ current_id, name.GetCString());
+ }
+
+ clang::Decl *copied_decl = CopyDecl(decl_from_modules);
+ clang::FunctionDecl *copied_function_decl =
+ copied_decl ? dyn_cast<clang::FunctionDecl>(copied_decl)
+ : nullptr;
+
+ if (!copied_function_decl) {
+ if (log)
+ log->Printf(" CAS::FEVD[%u] - Couldn't export a function "
+ "declaration from the modules",
+ current_id);
+
+ break;
+ }
+
+ MaybeRegisterFunctionBody(copied_function_decl);
+
+ context.AddNamedDecl(copied_function_decl);
+
+ context.m_found.function_with_type_info = true;
+ context.m_found.function = true;
+ } else if (llvm::isa<clang::VarDecl>(decl_from_modules)) {
+ if (log) {
+ log->Printf(" CAS::FEVD[%u] Matching variable found for "
+ "\"%s\" in the modules",
+ current_id, name.GetCString());
+ }
+
+ clang::Decl *copied_decl = CopyDecl(decl_from_modules);
+ clang::VarDecl *copied_var_decl =
+ copied_decl ? dyn_cast_or_null<clang::VarDecl>(copied_decl)
+ : nullptr;
+
+ if (!copied_var_decl) {
+ if (log)
+ log->Printf(" CAS::FEVD[%u] - Couldn't export a variable "
+ "declaration from the modules",
+ current_id);
+
+ break;
+ }
+
+ context.AddNamedDecl(copied_var_decl);
+
+ context.m_found.variable = true;
+ }
+ }
+ } while (false);
+ }
+
+ if (target && !context.m_found.variable && !namespace_decl) {
+ // We couldn't find a non-symbol variable for this. Now we'll hunt for a
+ // generic data symbol, and -- if it is found -- treat it as a variable.
+ Status error;
+
+ const Symbol *data_symbol =
+ m_parser_vars->m_sym_ctx.FindBestGlobalDataSymbol(name, error);
+
+ if (!error.Success()) {
+ const unsigned diag_id =
+ m_ast_context->getDiagnostics().getCustomDiagID(
+ clang::DiagnosticsEngine::Level::Error, "%0");
+ m_ast_context->getDiagnostics().Report(diag_id) << error.AsCString();
+ }
+
+ if (data_symbol) {
+ std::string warning("got name from symbols: ");
+ warning.append(name.AsCString());
+ const unsigned diag_id =
+ m_ast_context->getDiagnostics().getCustomDiagID(
+ clang::DiagnosticsEngine::Level::Warning, "%0");
+ m_ast_context->getDiagnostics().Report(diag_id) << warning.c_str();
+ AddOneGenericVariable(context, *data_symbol, current_id);
+ context.m_found.variable = true;
+ }
+ }
+ }
+}
+
+bool ClangExpressionDeclMap::GetVariableValue(VariableSP &var,
+ lldb_private::Value &var_location,
+ TypeFromUser *user_type,
+ TypeFromParser *parser_type) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ Type *var_type = var->GetType();
+
+ if (!var_type) {
+ if (log)
+ log->PutCString("Skipped a definition because it has no type");
+ return false;
+ }
+
+ CompilerType var_clang_type = var_type->GetFullCompilerType();
+
+ if (!var_clang_type) {
+ if (log)
+ log->PutCString("Skipped a definition because it has no Clang type");
+ return false;
+ }
+
+ ClangASTContext *clang_ast = llvm::dyn_cast_or_null<ClangASTContext>(
+ var_type->GetForwardCompilerType().GetTypeSystem());
+
+ if (!clang_ast) {
+ if (log)
+ log->PutCString("Skipped a definition because it has no Clang AST");
+ return false;
+ }
+
+ ASTContext *ast = clang_ast->getASTContext();
+
+ if (!ast) {
+ if (log)
+ log->PutCString(
+ "There is no AST context for the current execution context");
+ return false;
+ }
+
+ DWARFExpression &var_location_expr = var->LocationExpression();
+
+ Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
+ Status err;
+
+ if (var->GetLocationIsConstantValueData()) {
+ DataExtractor const_value_extractor;
+
+ if (var_location_expr.GetExpressionData(const_value_extractor)) {
+ var_location = Value(const_value_extractor.GetDataStart(),
+ const_value_extractor.GetByteSize());
+ var_location.SetValueType(Value::eValueTypeHostAddress);
+ } else {
+ if (log)
+ log->Printf("Error evaluating constant variable: %s", err.AsCString());
+ return false;
+ }
+ }
+
+ CompilerType type_to_use = GuardedCopyType(var_clang_type);
+
+ if (!type_to_use) {
+ if (log)
+ log->Printf(
+ "Couldn't copy a variable's type into the parser's AST context");
+
+ return false;
+ }
+
+ if (parser_type)
+ *parser_type = TypeFromParser(type_to_use);
+
+ if (var_location.GetContextType() == Value::eContextTypeInvalid)
+ var_location.SetCompilerType(type_to_use);
+
+ if (var_location.GetValueType() == Value::eValueTypeFileAddress) {
+ SymbolContext var_sc;
+ var->CalculateSymbolContext(&var_sc);
+
+ if (!var_sc.module_sp)
+ return false;
+
+ Address so_addr(var_location.GetScalar().ULongLong(),
+ var_sc.module_sp->GetSectionList());
+
+ lldb::addr_t load_addr = so_addr.GetLoadAddress(target);
+
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ var_location.GetScalar() = load_addr;
+ var_location.SetValueType(Value::eValueTypeLoadAddress);
+ }
+ }
+
+ if (user_type)
+ *user_type = TypeFromUser(var_clang_type);
+
+ return true;
+}
+
+void ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context,
+ VariableSP var,
+ ValueObjectSP valobj,
+ unsigned int current_id) {
+ assert(m_parser_vars.get());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ TypeFromUser ut;
+ TypeFromParser pt;
+ Value var_location;
+
+ if (!GetVariableValue(var, var_location, &ut, &pt))
+ return;
+
+ clang::QualType parser_opaque_type =
+ QualType::getFromOpaquePtr(pt.GetOpaqueQualType());
+
+ if (parser_opaque_type.isNull())
+ return;
+
+ if (const clang::Type *parser_type = parser_opaque_type.getTypePtr()) {
+ if (const TagType *tag_type = dyn_cast<TagType>(parser_type))
+ CompleteType(tag_type->getDecl());
+ if (const ObjCObjectPointerType *objc_object_ptr_type =
+ dyn_cast<ObjCObjectPointerType>(parser_type))
+ CompleteType(objc_object_ptr_type->getInterfaceDecl());
+ }
+
+ bool is_reference = pt.IsReferenceType();
+
+ NamedDecl *var_decl = nullptr;
+ if (is_reference)
+ var_decl = context.AddVarDecl(pt);
+ else
+ var_decl = context.AddVarDecl(pt.GetLValueReferenceType());
+
+ std::string decl_name(context.m_decl_name.getAsString());
+ ConstString entity_name(decl_name.c_str());
+ ClangExpressionVariable *entity(new ClangExpressionVariable(valobj));
+ m_found_entities.AddNewlyConstructedVariable(entity);
+
+ assert(entity);
+ entity->EnableParserVars(GetParserID());
+ ClangExpressionVariable::ParserVars *parser_vars =
+ entity->GetParserVars(GetParserID());
+ parser_vars->m_parser_type = pt;
+ parser_vars->m_named_decl = var_decl;
+ parser_vars->m_llvm_value = nullptr;
+ parser_vars->m_lldb_value = var_location;
+ parser_vars->m_lldb_var = var;
+
+ if (is_reference)
+ entity->m_flags |= ClangExpressionVariable::EVTypeIsReference;
+
+ if (log) {
+ ASTDumper orig_dumper(ut.GetOpaqueQualType());
+ ASTDumper ast_dumper(var_decl);
+ log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s (original %s)",
+ current_id, decl_name.c_str(), ast_dumper.GetCString(),
+ orig_dumper.GetCString());
+ }
+}
+
+void ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context,
+ ExpressionVariableSP &pvar_sp,
+ unsigned int current_id) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ TypeFromUser user_type(
+ llvm::cast<ClangExpressionVariable>(pvar_sp.get())->GetTypeFromUser());
+
+ TypeFromParser parser_type(GuardedCopyType(user_type));
+
+ if (!parser_type.GetOpaqueQualType()) {
+ if (log)
+ log->Printf(" CEDM::FEVD[%u] Couldn't import type for pvar %s",
+ current_id, pvar_sp->GetName().GetCString());
+ return;
+ }
+
+ NamedDecl *var_decl =
+ context.AddVarDecl(parser_type.GetLValueReferenceType());
+
+ llvm::cast<ClangExpressionVariable>(pvar_sp.get())
+ ->EnableParserVars(GetParserID());
+ ClangExpressionVariable::ParserVars *parser_vars =
+ llvm::cast<ClangExpressionVariable>(pvar_sp.get())
+ ->GetParserVars(GetParserID());
+ parser_vars->m_parser_type = parser_type;
+ parser_vars->m_named_decl = var_decl;
+ parser_vars->m_llvm_value = nullptr;
+ parser_vars->m_lldb_value.Clear();
+
+ if (log) {
+ ASTDumper ast_dumper(var_decl);
+ log->Printf(" CEDM::FEVD[%u] Added pvar %s, returned %s", current_id,
+ pvar_sp->GetName().GetCString(), ast_dumper.GetCString());
+ }
+}
+
+void ClangExpressionDeclMap::AddOneGenericVariable(NameSearchContext &context,
+ const Symbol &symbol,
+ unsigned int current_id) {
+ assert(m_parser_vars.get());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
+
+ if (target == nullptr)
+ return;
+
+ ASTContext *scratch_ast_context =
+ target->GetScratchClangASTContext()->getASTContext();
+
+ TypeFromUser user_type(
+ ClangASTContext::GetBasicType(scratch_ast_context, eBasicTypeVoid)
+ .GetPointerType()
+ .GetLValueReferenceType());
+ TypeFromParser parser_type(
+ ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid)
+ .GetPointerType()
+ .GetLValueReferenceType());
+ NamedDecl *var_decl = context.AddVarDecl(parser_type);
+
+ std::string decl_name(context.m_decl_name.getAsString());
+ ConstString entity_name(decl_name.c_str());
+ ClangExpressionVariable *entity(new ClangExpressionVariable(
+ m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), entity_name,
+ user_type, m_parser_vars->m_target_info.byte_order,
+ m_parser_vars->m_target_info.address_byte_size));
+ m_found_entities.AddNewlyConstructedVariable(entity);
+
+ entity->EnableParserVars(GetParserID());
+ ClangExpressionVariable::ParserVars *parser_vars =
+ entity->GetParserVars(GetParserID());
+
+ const Address symbol_address = symbol.GetAddress();
+ lldb::addr_t symbol_load_addr = symbol_address.GetLoadAddress(target);
+
+ // parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType,
+ // user_type.GetOpaqueQualType());
+ parser_vars->m_lldb_value.SetCompilerType(user_type);
+ parser_vars->m_lldb_value.GetScalar() = symbol_load_addr;
+ parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress);
+
+ parser_vars->m_parser_type = parser_type;
+ parser_vars->m_named_decl = var_decl;
+ parser_vars->m_llvm_value = nullptr;
+ parser_vars->m_lldb_sym = &symbol;
+
+ if (log) {
+ ASTDumper ast_dumper(var_decl);
+
+ log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s", current_id,
+ decl_name.c_str(), ast_dumper.GetCString());
+ }
+}
+
+bool ClangExpressionDeclMap::ResolveUnknownTypes() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+ Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
+
+ ClangASTContextForExpressions *scratch_ast_context =
+ static_cast<ClangASTContextForExpressions*>(
+ target->GetScratchClangASTContext());
+
+ for (size_t index = 0, num_entities = m_found_entities.GetSize();
+ index < num_entities; ++index) {
+ ExpressionVariableSP entity = m_found_entities.GetVariableAtIndex(index);
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ llvm::cast<ClangExpressionVariable>(entity.get())
+ ->GetParserVars(GetParserID());
+
+ if (entity->m_flags & ClangExpressionVariable::EVUnknownType) {
+ const NamedDecl *named_decl = parser_vars->m_named_decl;
+ const VarDecl *var_decl = dyn_cast<VarDecl>(named_decl);
+
+ if (!var_decl) {
+ if (log)
+ log->Printf("Entity of unknown type does not have a VarDecl");
+ return false;
+ }
+
+ if (log) {
+ ASTDumper ast_dumper(const_cast<VarDecl *>(var_decl));
+ log->Printf("Variable of unknown type now has Decl %s",
+ ast_dumper.GetCString());
+ }
+
+ QualType var_type = var_decl->getType();
+ TypeFromParser parser_type(
+ var_type.getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&var_decl->getASTContext()));
+
+ lldb::opaque_compiler_type_t copied_type = nullptr;
+ if (m_ast_importer_sp) {
+ copied_type = m_ast_importer_sp->CopyType(
+ scratch_ast_context->getASTContext(), &var_decl->getASTContext(),
+ var_type.getAsOpaquePtr());
+ } else if (HasMerger()) {
+ copied_type = CopyTypeWithMerger(
+ var_decl->getASTContext(),
+ scratch_ast_context->GetMergerUnchecked(),
+ var_type).getAsOpaquePtr();
+ } else {
+ lldbassert(0 && "No mechanism to copy a resolved unknown type!");
+ return false;
+ }
+
+ if (!copied_type) {
+ if (log)
+ log->Printf("ClangExpressionDeclMap::ResolveUnknownType - Couldn't "
+ "import the type for a variable");
+
+ return (bool)lldb::ExpressionVariableSP();
+ }
+
+ TypeFromUser user_type(copied_type, scratch_ast_context);
+
+ // parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType,
+ // user_type.GetOpaqueQualType());
+ parser_vars->m_lldb_value.SetCompilerType(user_type);
+ parser_vars->m_parser_type = parser_type;
+
+ entity->SetCompilerType(user_type);
+
+ entity->m_flags &= ~(ClangExpressionVariable::EVUnknownType);
+ }
+ }
+
+ return true;
+}
+
+void ClangExpressionDeclMap::AddOneRegister(NameSearchContext &context,
+ const RegisterInfo *reg_info,
+ unsigned int current_id) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ CompilerType clang_type =
+ ClangASTContext::GetBuiltinTypeForEncodingAndBitSize(
+ m_ast_context, reg_info->encoding, reg_info->byte_size * 8);
+
+ if (!clang_type) {
+ if (log)
+ log->Printf(" Tried to add a type for %s, but couldn't get one",
+ context.m_decl_name.getAsString().c_str());
+ return;
+ }
+
+ TypeFromParser parser_clang_type(clang_type);
+
+ NamedDecl *var_decl = context.AddVarDecl(parser_clang_type);
+
+ ClangExpressionVariable *entity(new ClangExpressionVariable(
+ m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(),
+ m_parser_vars->m_target_info.byte_order,
+ m_parser_vars->m_target_info.address_byte_size));
+ m_found_entities.AddNewlyConstructedVariable(entity);
+
+ std::string decl_name(context.m_decl_name.getAsString());
+ entity->SetName(ConstString(decl_name.c_str()));
+ entity->SetRegisterInfo(reg_info);
+ entity->EnableParserVars(GetParserID());
+ ClangExpressionVariable::ParserVars *parser_vars =
+ entity->GetParserVars(GetParserID());
+ parser_vars->m_parser_type = parser_clang_type;
+ parser_vars->m_named_decl = var_decl;
+ parser_vars->m_llvm_value = nullptr;
+ parser_vars->m_lldb_value.Clear();
+ entity->m_flags |= ClangExpressionVariable::EVBareRegister;
+
+ if (log) {
+ ASTDumper ast_dumper(var_decl);
+ log->Printf(" CEDM::FEVD[%d] Added register %s, returned %s", current_id,
+ context.m_decl_name.getAsString().c_str(),
+ ast_dumper.GetCString());
+ }
+}
+
+void ClangExpressionDeclMap::AddOneFunction(NameSearchContext &context,
+ Function *function, Symbol *symbol,
+ unsigned int current_id) {
+ assert(m_parser_vars.get());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ NamedDecl *function_decl = nullptr;
+ Address fun_address;
+ CompilerType function_clang_type;
+
+ bool is_indirect_function = false;
+
+ if (function) {
+ Type *function_type = function->GetType();
+
+ const auto lang = function->GetCompileUnit()->GetLanguage();
+ const auto name = function->GetMangled().GetMangledName().AsCString();
+ const bool extern_c = (Language::LanguageIsC(lang) &&
+ !CPlusPlusLanguage::IsCPPMangledName(name)) ||
+ (Language::LanguageIsObjC(lang) &&
+ !Language::LanguageIsCPlusPlus(lang));
+
+ if (!extern_c) {
+ TypeSystem *type_system = function->GetDeclContext().GetTypeSystem();
+ if (llvm::isa<ClangASTContext>(type_system)) {
+ clang::DeclContext *src_decl_context =
+ (clang::DeclContext *)function->GetDeclContext()
+ .GetOpaqueDeclContext();
+ clang::FunctionDecl *src_function_decl =
+ llvm::dyn_cast_or_null<clang::FunctionDecl>(src_decl_context);
+ if (src_function_decl &&
+ src_function_decl->getTemplateSpecializationInfo()) {
+ clang::FunctionTemplateDecl *function_template =
+ src_function_decl->getTemplateSpecializationInfo()->getTemplate();
+ clang::FunctionTemplateDecl *copied_function_template =
+ llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(
+ CopyDecl(function_template));
+ if (copied_function_template) {
+ if (log) {
+ ASTDumper ast_dumper((clang::Decl *)copied_function_template);
+
+ StreamString ss;
+
+ function->DumpSymbolContext(&ss);
+
+ log->Printf(" CEDM::FEVD[%u] Imported decl for function template"
+ " %s (description %s), returned %s",
+ current_id,
+ copied_function_template->getNameAsString().c_str(),
+ ss.GetData(), ast_dumper.GetCString());
+ }
+
+ context.AddNamedDecl(copied_function_template);
+ }
+ } else if (src_function_decl) {
+ if (clang::FunctionDecl *copied_function_decl =
+ llvm::dyn_cast_or_null<clang::FunctionDecl>(
+ CopyDecl(src_function_decl))) {
+ if (log) {
+ ASTDumper ast_dumper((clang::Decl *)copied_function_decl);
+
+ StreamString ss;
+
+ function->DumpSymbolContext(&ss);
+
+ log->Printf(" CEDM::FEVD[%u] Imported decl for function %s "
+ "(description %s), returned %s",
+ current_id,
+ copied_function_decl->getNameAsString().c_str(),
+ ss.GetData(), ast_dumper.GetCString());
+ }
+
+ context.AddNamedDecl(copied_function_decl);
+ return;
+ } else {
+ if (log) {
+ log->Printf(" Failed to import the function decl for '%s'",
+ src_function_decl->getName().str().c_str());
+ }
+ }
+ }
+ }
+ }
+
+ if (!function_type) {
+ if (log)
+ log->PutCString(" Skipped a function because it has no type");
+ return;
+ }
+
+ function_clang_type = function_type->GetFullCompilerType();
+
+ if (!function_clang_type) {
+ if (log)
+ log->PutCString(" Skipped a function because it has no Clang type");
+ return;
+ }
+
+ fun_address = function->GetAddressRange().GetBaseAddress();
+
+ CompilerType copied_function_type = GuardedCopyType(function_clang_type);
+ if (copied_function_type) {
+ function_decl = context.AddFunDecl(copied_function_type, extern_c);
+
+ if (!function_decl) {
+ if (log) {
+ log->Printf(
+ " Failed to create a function decl for '%s' {0x%8.8" PRIx64 "}",
+ function_type->GetName().GetCString(), function_type->GetID());
+ }
+
+ return;
+ }
+ } else {
+ // We failed to copy the type we found
+ if (log) {
+ log->Printf(" Failed to import the function type '%s' {0x%8.8" PRIx64
+ "} into the expression parser AST contenxt",
+ function_type->GetName().GetCString(),
+ function_type->GetID());
+ }
+
+ return;
+ }
+ } else if (symbol) {
+ fun_address = symbol->GetAddress();
+ function_decl = context.AddGenericFunDecl();
+ is_indirect_function = symbol->IsIndirect();
+ } else {
+ if (log)
+ log->PutCString(" AddOneFunction called with no function and no symbol");
+ return;
+ }
+
+ Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
+
+ lldb::addr_t load_addr =
+ fun_address.GetCallableLoadAddress(target, is_indirect_function);
+
+ ClangExpressionVariable *entity(new ClangExpressionVariable(
+ m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(),
+ m_parser_vars->m_target_info.byte_order,
+ m_parser_vars->m_target_info.address_byte_size));
+ m_found_entities.AddNewlyConstructedVariable(entity);
+
+ std::string decl_name(context.m_decl_name.getAsString());
+ entity->SetName(ConstString(decl_name.c_str()));
+ entity->SetCompilerType(function_clang_type);
+ entity->EnableParserVars(GetParserID());
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ entity->GetParserVars(GetParserID());
+
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress);
+ parser_vars->m_lldb_value.GetScalar() = load_addr;
+ } else {
+ // We have to try finding a file address.
+
+ lldb::addr_t file_addr = fun_address.GetFileAddress();
+
+ parser_vars->m_lldb_value.SetValueType(Value::eValueTypeFileAddress);
+ parser_vars->m_lldb_value.GetScalar() = file_addr;
+ }
+
+ parser_vars->m_named_decl = function_decl;
+ parser_vars->m_llvm_value = nullptr;
+
+ if (log) {
+ std::string function_str =
+ function_decl ? ASTDumper(function_decl).GetCString() : "nullptr";
+
+ StreamString ss;
+
+ fun_address.Dump(&ss,
+ m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(),
+ Address::DumpStyleResolvedDescription);
+
+ log->Printf(
+ " CEDM::FEVD[%u] Found %s function %s (description %s), returned %s",
+ current_id, (function ? "specific" : "generic"), decl_name.c_str(),
+ ss.GetData(), function_str.c_str());
+ }
+}
+
+void ClangExpressionDeclMap::AddThisType(NameSearchContext &context,
+ const TypeFromUser &ut,
+ unsigned int current_id) {
+ CompilerType copied_clang_type = GuardedCopyType(ut);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!copied_clang_type) {
+ if (log)
+ log->Printf(
+ "ClangExpressionDeclMap::AddThisType - Couldn't import the type");
+
+ return;
+ }
+
+ if (copied_clang_type.IsAggregateType() &&
+ copied_clang_type.GetCompleteType()) {
+ CompilerType void_clang_type =
+ ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid);
+ CompilerType void_ptr_clang_type = void_clang_type.GetPointerType();
+
+ CompilerType method_type = ClangASTContext::CreateFunctionType(
+ m_ast_context, void_clang_type, &void_ptr_clang_type, 1, false, 0);
+
+ const bool is_virtual = false;
+ const bool is_static = false;
+ const bool is_inline = false;
+ const bool is_explicit = false;
+ const bool is_attr_used = true;
+ const bool is_artificial = false;
+
+ CXXMethodDecl *method_decl =
+ ClangASTContext::GetASTContext(m_ast_context)
+ ->AddMethodToCXXRecordType(
+ copied_clang_type.GetOpaqueQualType(), "$__lldb_expr", nullptr,
+ method_type, lldb::eAccessPublic, is_virtual, is_static,
+ is_inline, is_explicit, is_attr_used, is_artificial);
+
+ if (log) {
+ ASTDumper method_ast_dumper((clang::Decl *)method_decl);
+ ASTDumper type_ast_dumper(copied_clang_type);
+
+ log->Printf(" CEDM::AddThisType Added function $__lldb_expr "
+ "(description %s) for this type %s",
+ method_ast_dumper.GetCString(), type_ast_dumper.GetCString());
+ }
+ }
+
+ if (!copied_clang_type.IsValid())
+ return;
+
+ TypeSourceInfo *type_source_info = m_ast_context->getTrivialTypeSourceInfo(
+ QualType::getFromOpaquePtr(copied_clang_type.GetOpaqueQualType()));
+
+ if (!type_source_info)
+ return;
+
+ // Construct a typedef type because if "*this" is a templated type we can't
+ // just return ClassTemplateSpecializationDecls in response to name queries.
+ // Using a typedef makes this much more robust.
+
+ TypedefDecl *typedef_decl = TypedefDecl::Create(
+ *m_ast_context, m_ast_context->getTranslationUnitDecl(), SourceLocation(),
+ SourceLocation(), context.m_decl_name.getAsIdentifierInfo(),
+ type_source_info);
+
+ if (!typedef_decl)
+ return;
+
+ context.AddNamedDecl(typedef_decl);
+
+ return;
+}
+
+void ClangExpressionDeclMap::AddOneType(NameSearchContext &context,
+ const TypeFromUser &ut,
+ unsigned int current_id) {
+ CompilerType copied_clang_type = GuardedCopyType(ut);
+
+ if (!copied_clang_type) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf(
+ "ClangExpressionDeclMap::AddOneType - Couldn't import the type");
+
+ return;
+ }
+
+ context.AddTypeDecl(copied_clang_type);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
new file mode 100644
index 000000000000..03b73e6be391
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
@@ -0,0 +1,564 @@
+//===-- ClangExpressionDeclMap.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangExpressionDeclMap_h_
+#define liblldb_ClangExpressionDeclMap_h_
+
+#include <signal.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "ClangASTSource.h"
+#include "ClangExpressionVariable.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/Materializer.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/TaggedASTType.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/lldb-public.h"
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace lldb_private {
+
+/// \class ClangExpressionDeclMap ClangExpressionDeclMap.h
+/// "lldb/Expression/ClangExpressionDeclMap.h" Manages named entities that are
+/// defined in LLDB's debug information.
+///
+/// The Clang parser uses the ClangASTSource as an interface to request named
+/// entities from outside an expression. The ClangASTSource reports back,
+/// listing all possible objects corresponding to a particular name. But it
+/// in turn relies on ClangExpressionDeclMap, which performs several important
+/// functions.
+///
+/// First, it records what variables and functions were looked up and what
+/// Decls were returned for them.
+///
+/// Second, it constructs a struct on behalf of IRForTarget, recording which
+/// variables should be placed where and relaying this information back so
+/// that IRForTarget can generate context-independent code.
+///
+/// Third, it "materializes" this struct on behalf of the expression command,
+/// finding the current values of each variable and placing them into the
+/// struct so that it can be passed to the JITted version of the IR.
+///
+/// Fourth and finally, it "dematerializes" the struct after the JITted code
+/// has has executed, placing the new values back where it found the old ones.
+class ClangExpressionDeclMap : public ClangASTSource {
+public:
+ /// Constructor
+ ///
+ /// Initializes class variables.
+ ///
+ /// \param[in] keep_result_in_memory
+ /// If true, inhibits the normal deallocation of the memory for
+ /// the result persistent variable, and instead marks the variable
+ /// as persisting.
+ ///
+ /// \param[in] delegate
+ /// If non-NULL, use this delegate to report result values. This
+ /// allows the client ClangUserExpression to report a result.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to use when parsing.
+ ///
+ /// \param[in] ctx_obj
+ /// If not empty, then expression is evaluated in context of this object.
+ /// See the comment to `UserExpression::Evaluate` for details.
+ ClangExpressionDeclMap(
+ bool keep_result_in_memory,
+ Materializer::PersistentVariableDelegate *result_delegate,
+ ExecutionContext &exe_ctx,
+ ValueObject *ctx_obj);
+
+ /// Destructor
+ ~ClangExpressionDeclMap() override;
+
+ /// Enable the state needed for parsing and IR transformation.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to use when finding types for variables.
+ /// Also used to find a "scratch" AST context to store result types.
+ ///
+ /// \param[in] materializer
+ /// If non-NULL, the materializer to populate with information about
+ /// the variables to use
+ ///
+ /// \return
+ /// True if parsing is possible; false if it is unsafe to continue.
+ bool WillParse(ExecutionContext &exe_ctx, Materializer *materializer);
+
+ void InstallCodeGenerator(clang::ASTConsumer *code_gen);
+
+ /// [Used by ClangExpressionParser] For each variable that had an unknown
+ /// type at the beginning of parsing, determine its final type now.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool ResolveUnknownTypes();
+
+ /// Disable the state needed for parsing and IR transformation.
+ void DidParse();
+
+ /// [Used by IRForTarget] Add a variable to the list of persistent
+ /// variables for the process.
+ ///
+ /// \param[in] decl
+ /// The Clang declaration for the persistent variable, used for
+ /// lookup during parsing.
+ ///
+ /// \param[in] name
+ /// The name of the persistent variable, usually $something.
+ ///
+ /// \param[in] type
+ /// The type of the variable, in the Clang parser's context.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool AddPersistentVariable(const clang::NamedDecl *decl,
+ ConstString name, TypeFromParser type,
+ bool is_result, bool is_lvalue);
+
+ /// [Used by IRForTarget] Add a variable to the struct that needs to
+ /// be materialized each time the expression runs.
+ ///
+ /// \param[in] decl
+ /// The Clang declaration for the variable.
+ ///
+ /// \param[in] name
+ /// The name of the variable.
+ ///
+ /// \param[in] value
+ /// The LLVM IR value for this variable.
+ ///
+ /// \param[in] size
+ /// The size of the variable in bytes.
+ ///
+ /// \param[in] alignment
+ /// The required alignment of the variable in bytes.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool AddValueToStruct(const clang::NamedDecl *decl, ConstString name,
+ llvm::Value *value, size_t size,
+ lldb::offset_t alignment);
+
+ /// [Used by IRForTarget] Finalize the struct, laying out the position of
+ /// each object in it.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool DoStructLayout();
+
+ /// [Used by IRForTarget] Get general information about the laid-out struct
+ /// after DoStructLayout() has been called.
+ ///
+ /// \param[out] num_elements
+ /// The number of elements in the struct.
+ ///
+ /// \param[out] size
+ /// The size of the struct, in bytes.
+ ///
+ /// \param[out] alignment
+ /// The alignment of the struct, in bytes.
+ ///
+ /// \return
+ /// True if the information could be retrieved; false otherwise.
+ bool GetStructInfo(uint32_t &num_elements, size_t &size,
+ lldb::offset_t &alignment);
+
+ /// [Used by IRForTarget] Get specific information about one field of the
+ /// laid-out struct after DoStructLayout() has been called.
+ ///
+ /// \param[out] decl
+ /// The parsed Decl for the field, as generated by ClangASTSource
+ /// on ClangExpressionDeclMap's behalf. In the case of the result
+ /// value, this will have the name $__lldb_result even if the
+ /// result value ends up having the name $1. This is an
+ /// implementation detail of IRForTarget.
+ ///
+ /// \param[out] value
+ /// The IR value for the field (usually a GlobalVariable). In
+ /// the case of the result value, this will have the correct
+ /// name ($1, for instance). This is an implementation detail
+ /// of IRForTarget.
+ ///
+ /// \param[out] offset
+ /// The offset of the field from the beginning of the struct.
+ /// As long as the struct is aligned according to its required
+ /// alignment, this offset will align the field correctly.
+ ///
+ /// \param[out] name
+ /// The name of the field as used in materialization.
+ ///
+ /// \param[in] index
+ /// The index of the field about which information is requested.
+ ///
+ /// \return
+ /// True if the information could be retrieved; false otherwise.
+ bool GetStructElement(const clang::NamedDecl *&decl, llvm::Value *&value,
+ lldb::offset_t &offset, ConstString &name,
+ uint32_t index);
+
+ /// [Used by IRForTarget] Get information about a function given its Decl.
+ ///
+ /// \param[in] decl
+ /// The parsed Decl for the Function, as generated by ClangASTSource
+ /// on ClangExpressionDeclMap's behalf.
+ ///
+ /// \param[out] ptr
+ /// The absolute address of the function in the target.
+ ///
+ /// \return
+ /// True if the information could be retrieved; false otherwise.
+ bool GetFunctionInfo(const clang::NamedDecl *decl, uint64_t &ptr);
+
+ /// [Used by IRForTarget] Get the address of a symbol given nothing but its
+ /// name.
+ ///
+ /// \param[in] target
+ /// The target to find the symbol in. If not provided,
+ /// then the current parsing context's Target.
+ ///
+ /// \param[in] process
+ /// The process to use. For Objective-C symbols, the process's
+ /// Objective-C language runtime may be queried if the process
+ /// is non-NULL.
+ ///
+ /// \param[in] name
+ /// The name of the symbol.
+ ///
+ /// \param[in] module
+ /// The module to limit the search to. This can be NULL
+ ///
+ /// \return
+ /// Valid load address for the symbol
+ lldb::addr_t GetSymbolAddress(Target &target, Process *process,
+ ConstString name, lldb::SymbolType symbol_type,
+ Module *module = nullptr);
+
+ lldb::addr_t GetSymbolAddress(ConstString name,
+ lldb::SymbolType symbol_type);
+
+ /// [Used by IRInterpreter] Get basic target information.
+ ///
+ /// \param[out] byte_order
+ /// The byte order of the target.
+ ///
+ /// \param[out] address_byte_size
+ /// The size of a pointer in bytes.
+ ///
+ /// \return
+ /// True if the information could be determined; false
+ /// otherwise.
+ struct TargetInfo {
+ lldb::ByteOrder byte_order;
+ size_t address_byte_size;
+
+ TargetInfo() : byte_order(lldb::eByteOrderInvalid), address_byte_size(0) {}
+
+ bool IsValid() {
+ return (byte_order != lldb::eByteOrderInvalid && address_byte_size != 0);
+ }
+ };
+ TargetInfo GetTargetInfo();
+
+ /// [Used by ClangASTSource] Find all entities matching a given name, using
+ /// a NameSearchContext to make Decls for them.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext that can construct Decls for this name.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ void FindExternalVisibleDecls(NameSearchContext &context) override;
+
+ /// Find all entities matching a given name in a given module/namespace,
+ /// using a NameSearchContext to make Decls for them.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext that can construct Decls for this name.
+ ///
+ /// \param[in] module
+ /// If non-NULL, the module to query.
+ ///
+ /// \param[in] namespace_decl
+ /// If valid and module is non-NULL, the parent namespace.
+ ///
+ /// \param[in] current_id
+ /// The ID for the current FindExternalVisibleDecls invocation,
+ /// for logging purposes.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ void FindExternalVisibleDecls(NameSearchContext &context,
+ lldb::ModuleSP module,
+ CompilerDeclContext &namespace_decl,
+ unsigned int current_id);
+
+private:
+ ExpressionVariableList
+ m_found_entities; ///< All entities that were looked up for the parser.
+ ExpressionVariableList
+ m_struct_members; ///< All entities that need to be placed in the struct.
+ bool m_keep_result_in_memory; ///< True if result persistent variables
+ ///generated by this expression should stay in
+ ///memory.
+ Materializer::PersistentVariableDelegate
+ *m_result_delegate; ///< If non-NULL, used to report expression results to
+ ///ClangUserExpression.
+ ValueObject *m_ctx_obj; ///< If not empty, then expression is
+ ///evaluated in context of this object.
+ ///For details see the comment to
+ ///`UserExpression::Evaluate`.
+
+ /// The following values should not live beyond parsing
+ class ParserVars {
+ public:
+ ParserVars() {}
+
+ Target *GetTarget() {
+ if (m_exe_ctx.GetTargetPtr())
+ return m_exe_ctx.GetTargetPtr();
+ else if (m_sym_ctx.target_sp)
+ m_sym_ctx.target_sp.get();
+ return nullptr;
+ }
+
+ ExecutionContext m_exe_ctx; ///< The execution context to use when parsing.
+ SymbolContext m_sym_ctx; ///< The symbol context to use in finding variables
+ ///and types.
+ ClangPersistentVariables *m_persistent_vars =
+ nullptr; ///< The persistent variables for the process.
+ bool m_enable_lookups = false; ///< Set to true during parsing if we have
+ ///found the first "$__lldb" name.
+ TargetInfo m_target_info; ///< Basic information about the target.
+ Materializer *m_materializer = nullptr; ///< If non-NULL, the materializer
+ ///to use when reporting used
+ ///variables.
+ clang::ASTConsumer *m_code_gen = nullptr; ///< If non-NULL, a code generator
+ ///that receives new top-level
+ ///functions.
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ParserVars);
+ };
+
+ std::unique_ptr<ParserVars> m_parser_vars;
+
+ /// Activate parser-specific variables
+ void EnableParserVars() {
+ if (!m_parser_vars.get())
+ m_parser_vars = llvm::make_unique<ParserVars>();
+ }
+
+ /// Deallocate parser-specific variables
+ void DisableParserVars() { m_parser_vars.reset(); }
+
+ /// The following values contain layout information for the materialized
+ /// struct, but are not specific to a single materialization
+ struct StructVars {
+ StructVars()
+ : m_struct_alignment(0), m_struct_size(0), m_struct_laid_out(false),
+ m_result_name(), m_object_pointer_type(nullptr, nullptr) {}
+
+ lldb::offset_t
+ m_struct_alignment; ///< The alignment of the struct in bytes.
+ size_t m_struct_size; ///< The size of the struct in bytes.
+ bool m_struct_laid_out; ///< True if the struct has been laid out and the
+ ///layout is valid (that is, no new fields have been
+ ///added since).
+ ConstString
+ m_result_name; ///< The name of the result variable ($1, for example)
+ TypeFromUser m_object_pointer_type; ///< The type of the "this" variable, if
+ ///one exists
+ };
+
+ std::unique_ptr<StructVars> m_struct_vars;
+
+ /// Activate struct variables
+ void EnableStructVars() {
+ if (!m_struct_vars.get())
+ m_struct_vars.reset(new struct StructVars);
+ }
+
+ /// Deallocate struct variables
+ void DisableStructVars() { m_struct_vars.reset(); }
+
+ /// Get this parser's ID for use in extracting parser- and JIT-specific data
+ /// from persistent variables.
+ uint64_t GetParserID() { return (uint64_t) this; }
+
+ /// Given a target, find a variable that matches the given name and type.
+ ///
+ /// \param[in] target
+ /// The target to use as a basis for finding the variable.
+ ///
+ /// \param[in] module
+ /// If non-NULL, the module to search.
+ ///
+ /// \param[in] name
+ /// The name as a plain C string.
+ ///
+ /// \param[in] namespace_decl
+ /// If non-NULL and module is non-NULL, the parent namespace.
+ ///
+ /// \param[in] type
+ /// The required type for the variable. This function may be called
+ /// during parsing, in which case we don't know its type; hence the
+ /// default.
+ ///
+ /// \return
+ /// The LLDB Variable found, or NULL if none was found.
+ lldb::VariableSP FindGlobalVariable(Target &target, lldb::ModuleSP &module,
+ ConstString name,
+ CompilerDeclContext *namespace_decl,
+ TypeFromUser *type = nullptr);
+
+ /// Get the value of a variable in a given execution context and return the
+ /// associated Types if needed.
+ ///
+ /// \param[in] var
+ /// The variable to evaluate.
+ ///
+ /// \param[out] var_location
+ /// The variable location value to fill in
+ ///
+ /// \param[out] found_type
+ /// The type of the found value, as it was found in the user process.
+ /// This is only useful when the variable is being inspected on behalf
+ /// of the parser, hence the default.
+ ///
+ /// \param[out] parser_type
+ /// The type of the found value, as it was copied into the parser's
+ /// AST context. This is only useful when the variable is being
+ /// inspected on behalf of the parser, hence the default.
+ ///
+ /// \param[in] decl
+ /// The Decl to be looked up.
+ ///
+ /// \return
+ /// Return true if the value was successfully filled in.
+ bool GetVariableValue(lldb::VariableSP &var,
+ lldb_private::Value &var_location,
+ TypeFromUser *found_type = nullptr,
+ TypeFromParser *parser_type = nullptr);
+
+ /// Use the NameSearchContext to generate a Decl for the given LLDB
+ /// Variable, and put it in the Tuple list.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] var
+ /// The LLDB Variable that needs a Decl.
+ ///
+ /// \param[in] valobj
+ /// The LLDB ValueObject for that variable.
+ void AddOneVariable(NameSearchContext &context, lldb::VariableSP var,
+ lldb::ValueObjectSP valobj, unsigned int current_id);
+
+ /// Use the NameSearchContext to generate a Decl for the given persistent
+ /// variable, and put it in the list of found entities.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] pvar
+ /// The persistent variable that needs a Decl.
+ ///
+ /// \param[in] current_id
+ /// The ID of the current invocation of FindExternalVisibleDecls
+ /// for logging purposes.
+ void AddOneVariable(NameSearchContext &context,
+ lldb::ExpressionVariableSP &pvar_sp,
+ unsigned int current_id);
+
+ /// Use the NameSearchContext to generate a Decl for the given LLDB symbol
+ /// (treated as a variable), and put it in the list of found entities.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] var
+ /// The LLDB Variable that needs a Decl.
+ void AddOneGenericVariable(NameSearchContext &context, const Symbol &symbol,
+ unsigned int current_id);
+
+ /// Use the NameSearchContext to generate a Decl for the given function.
+ /// (Functions are not placed in the Tuple list.) Can handle both fully
+ /// typed functions and generic functions.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] fun
+ /// The Function that needs to be created. If non-NULL, this is
+ /// a fully-typed function.
+ ///
+ /// \param[in] sym
+ /// The Symbol that corresponds to a function that needs to be
+ /// created with generic type (unitptr_t foo(...)).
+ void AddOneFunction(NameSearchContext &context, Function *fun, Symbol *sym,
+ unsigned int current_id);
+
+ /// Use the NameSearchContext to generate a Decl for the given register.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] reg_info
+ /// The information corresponding to that register.
+ void AddOneRegister(NameSearchContext &context, const RegisterInfo *reg_info,
+ unsigned int current_id);
+
+ /// Use the NameSearchContext to generate a Decl for the given type. (Types
+ /// are not placed in the Tuple list.)
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] type
+ /// The type that needs to be created.
+ void AddOneType(NameSearchContext &context, const TypeFromUser &type,
+ unsigned int current_id);
+
+ /// Generate a Decl for "*this" and add a member function declaration to it
+ /// for the expression, then report it.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] type
+ /// The type for *this.
+ void AddThisType(NameSearchContext &context, const TypeFromUser &type,
+ unsigned int current_id);
+
+ /// Move a type out of the current ASTContext into another, but make sure to
+ /// export all components of the type also.
+ ///
+ /// \param[in] target
+ /// The ClangASTContext to move to.
+ /// \param[in] source
+ /// The ClangASTContext to move from. This is assumed to be going away.
+ /// \param[in] parser_type
+ /// The type as it appears in the source context.
+ ///
+ /// \return
+ /// Returns the moved type, or an empty type if there was a problem.
+ TypeFromUser DeportType(ClangASTContext &target, ClangASTContext &source,
+ TypeFromParser parser_type);
+
+ ClangASTContext *GetClangASTContext();
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangExpressionDeclMap_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionHelper.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionHelper.h
new file mode 100644
index 000000000000..48da5abb9126
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionHelper.h
@@ -0,0 +1,60 @@
+//===-- ClangExpressionHelper.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangExpression_h_
+#define liblldb_ClangExpression_h_
+
+#include <map>
+#include <string>
+#include <vector>
+
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Expression/ExpressionTypeSystemHelper.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class RecordingMemoryManager;
+
+// ClangExpressionHelper
+class ClangExpressionHelper : public ExpressionTypeSystemHelper {
+public:
+ static bool classof(const ExpressionTypeSystemHelper *ts) {
+ return ts->getKind() == eKindClangHelper;
+ }
+
+ ClangExpressionHelper()
+ : ExpressionTypeSystemHelper(
+ ExpressionTypeSystemHelper::LLVMCastKind::eKindClangHelper) {}
+
+ /// Destructor
+ virtual ~ClangExpressionHelper() {}
+
+ /// Return the object that the parser should use when resolving external
+ /// values. May be NULL if everything should be self-contained.
+ virtual ClangExpressionDeclMap *DeclMap() = 0;
+
+ /// Return the object that the parser should allow to access ASTs.
+ /// May be NULL if the ASTs do not need to be transformed.
+ ///
+ /// \param[in] passthrough
+ /// The ASTConsumer that the returned transformer should send
+ /// the ASTs to after transformation.
+ virtual clang::ASTConsumer *
+ ASTTransformer(clang::ASTConsumer *passthrough) = 0;
+
+ virtual void CommitPersistentDecls() {}
+
+protected:
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangExpression_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
new file mode 100644
index 000000000000..7d13891ded8d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -0,0 +1,1383 @@
+//===-- ClangExpressionParser.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTDiagnostic.h"
+#include "clang/AST/ExternalASTSource.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/Version.h"
+#include "clang/CodeGen/CodeGenAction.h"
+#include "clang/CodeGen/ModuleBuilder.h"
+#include "clang/Edit/Commit.h"
+#include "clang/Edit/EditedSource.h"
+#include "clang/Edit/EditsReceiver.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Rewrite/Frontend/FrontendActions.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaConsumer.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ExecutionEngine/ExecutionEngine.h"
+#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Signals.h"
+
+#include "ClangDiagnostic.h"
+#include "ClangExpressionParser.h"
+#include "ClangUserExpression.h"
+
+#include "ASTUtils.h"
+#include "ClangASTSource.h"
+#include "ClangDiagnostic.h"
+#include "ClangExpressionDeclMap.h"
+#include "ClangExpressionHelper.h"
+#include "ClangExpressionParser.h"
+#include "ClangHost.h"
+#include "ClangModulesDeclVendor.h"
+#include "ClangPersistentVariables.h"
+#include "IRDynamicChecks.h"
+#include "IRForTarget.h"
+#include "ModuleDependencyCollector.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Expression/IRInterpreter.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringList.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include <cctype>
+#include <memory>
+
+using namespace clang;
+using namespace llvm;
+using namespace lldb_private;
+
+//===----------------------------------------------------------------------===//
+// Utility Methods for Clang
+//===----------------------------------------------------------------------===//
+
+class ClangExpressionParser::LLDBPreprocessorCallbacks : public PPCallbacks {
+ ClangModulesDeclVendor &m_decl_vendor;
+ ClangPersistentVariables &m_persistent_vars;
+ StreamString m_error_stream;
+ bool m_has_errors = false;
+
+public:
+ LLDBPreprocessorCallbacks(ClangModulesDeclVendor &decl_vendor,
+ ClangPersistentVariables &persistent_vars)
+ : m_decl_vendor(decl_vendor), m_persistent_vars(persistent_vars) {}
+
+ void moduleImport(SourceLocation import_location, clang::ModuleIdPath path,
+ const clang::Module * /*null*/) override {
+ SourceModule module;
+
+ for (const std::pair<IdentifierInfo *, SourceLocation> &component : path)
+ module.path.push_back(ConstString(component.first->getName()));
+
+ StreamString error_stream;
+
+ ClangModulesDeclVendor::ModuleVector exported_modules;
+ if (!m_decl_vendor.AddModule(module, &exported_modules, m_error_stream))
+ m_has_errors = true;
+
+ for (ClangModulesDeclVendor::ModuleID module : exported_modules)
+ m_persistent_vars.AddHandLoadedClangModule(module);
+ }
+
+ bool hasErrors() { return m_has_errors; }
+
+ llvm::StringRef getErrorString() { return m_error_stream.GetString(); }
+};
+
+class ClangDiagnosticManagerAdapter : public clang::DiagnosticConsumer {
+public:
+ ClangDiagnosticManagerAdapter()
+ : m_passthrough(new clang::TextDiagnosticBuffer) {}
+
+ ClangDiagnosticManagerAdapter(
+ const std::shared_ptr<clang::TextDiagnosticBuffer> &passthrough)
+ : m_passthrough(passthrough) {}
+
+ void ResetManager(DiagnosticManager *manager = nullptr) {
+ m_manager = manager;
+ }
+
+ void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+ const clang::Diagnostic &Info) override {
+ if (m_manager) {
+ llvm::SmallVector<char, 32> diag_str;
+ Info.FormatDiagnostic(diag_str);
+ diag_str.push_back('\0');
+ const char *data = diag_str.data();
+
+ lldb_private::DiagnosticSeverity severity;
+ bool make_new_diagnostic = true;
+
+ switch (DiagLevel) {
+ case DiagnosticsEngine::Level::Fatal:
+ case DiagnosticsEngine::Level::Error:
+ severity = eDiagnosticSeverityError;
+ break;
+ case DiagnosticsEngine::Level::Warning:
+ severity = eDiagnosticSeverityWarning;
+ break;
+ case DiagnosticsEngine::Level::Remark:
+ case DiagnosticsEngine::Level::Ignored:
+ severity = eDiagnosticSeverityRemark;
+ break;
+ case DiagnosticsEngine::Level::Note:
+ m_manager->AppendMessageToDiagnostic(data);
+ make_new_diagnostic = false;
+ }
+ if (make_new_diagnostic) {
+ ClangDiagnostic *new_diagnostic =
+ new ClangDiagnostic(data, severity, Info.getID());
+ m_manager->AddDiagnostic(new_diagnostic);
+
+ // Don't store away warning fixits, since the compiler doesn't have
+ // enough context in an expression for the warning to be useful.
+ // FIXME: Should we try to filter out FixIts that apply to our generated
+ // code, and not the user's expression?
+ if (severity == eDiagnosticSeverityError) {
+ size_t num_fixit_hints = Info.getNumFixItHints();
+ for (size_t i = 0; i < num_fixit_hints; i++) {
+ const clang::FixItHint &fixit = Info.getFixItHint(i);
+ if (!fixit.isNull())
+ new_diagnostic->AddFixitHint(fixit);
+ }
+ }
+ }
+ }
+
+ m_passthrough->HandleDiagnostic(DiagLevel, Info);
+ }
+
+ void FlushDiagnostics(DiagnosticsEngine &Diags) {
+ m_passthrough->FlushDiagnostics(Diags);
+ }
+
+ DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
+ return new ClangDiagnosticManagerAdapter(m_passthrough);
+ }
+
+ clang::TextDiagnosticBuffer *GetPassthrough() { return m_passthrough.get(); }
+
+private:
+ DiagnosticManager *m_manager = nullptr;
+ std::shared_ptr<clang::TextDiagnosticBuffer> m_passthrough;
+};
+
+static void
+SetupModuleHeaderPaths(CompilerInstance *compiler,
+ std::vector<ConstString> include_directories,
+ lldb::TargetSP target_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ HeaderSearchOptions &search_opts = compiler->getHeaderSearchOpts();
+
+ for (ConstString dir : include_directories) {
+ search_opts.AddPath(dir.AsCString(), frontend::System, false, true);
+ LLDB_LOG(log, "Added user include dir: {0}", dir);
+ }
+
+ llvm::SmallString<128> module_cache;
+ auto props = ModuleList::GetGlobalModuleListProperties();
+ props.GetClangModulesCachePath().GetPath(module_cache);
+ search_opts.ModuleCachePath = module_cache.str();
+ LLDB_LOG(log, "Using module cache path: {0}", module_cache.c_str());
+
+ FileSpec clang_resource_dir = GetClangResourceDir();
+ std::string resource_dir = clang_resource_dir.GetPath();
+ if (FileSystem::Instance().IsDirectory(resource_dir)) {
+ search_opts.ResourceDir = resource_dir;
+ std::string resource_include = resource_dir + "/include";
+ search_opts.AddPath(resource_include, frontend::System, false, true);
+
+ LLDB_LOG(log, "Added resource include dir: {0}", resource_include);
+ }
+
+ search_opts.ImplicitModuleMaps = true;
+
+ std::vector<std::string> system_include_directories =
+ target_sp->GetPlatform()->GetSystemIncludeDirectories(
+ lldb::eLanguageTypeC_plus_plus);
+
+ for (const std::string &include_dir : system_include_directories) {
+ search_opts.AddPath(include_dir, frontend::System, false, true);
+
+ LLDB_LOG(log, "Added system include dir: {0}", include_dir);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Implementation of ClangExpressionParser
+//===----------------------------------------------------------------------===//
+
+ClangExpressionParser::ClangExpressionParser(
+ ExecutionContextScope *exe_scope, Expression &expr,
+ bool generate_debug_info, std::vector<ConstString> include_directories)
+ : ExpressionParser(exe_scope, expr, generate_debug_info), m_compiler(),
+ m_pp_callbacks(nullptr),
+ m_include_directories(std::move(include_directories)) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ // We can't compile expressions without a target. So if the exe_scope is
+ // null or doesn't have a target, then we just need to get out of here. I'll
+ // lldb_assert and not make any of the compiler objects since
+ // I can't return errors directly from the constructor. Further calls will
+ // check if the compiler was made and
+ // bag out if it wasn't.
+
+ if (!exe_scope) {
+ lldb_assert(exe_scope, "Can't make an expression parser with a null scope.",
+ __FUNCTION__, __FILE__, __LINE__);
+ return;
+ }
+
+ lldb::TargetSP target_sp;
+ target_sp = exe_scope->CalculateTarget();
+ if (!target_sp) {
+ lldb_assert(target_sp.get(),
+ "Can't make an expression parser with a null target.",
+ __FUNCTION__, __FILE__, __LINE__);
+ return;
+ }
+
+ // 1. Create a new compiler instance.
+ m_compiler.reset(new CompilerInstance());
+
+ // When capturing a reproducer, hook up the file collector with clang to
+ // collector modules and headers.
+ if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
+ repro::FileProvider &fp = g->GetOrCreate<repro::FileProvider>();
+ m_compiler->setModuleDepCollector(
+ std::make_shared<ModuleDependencyCollectorAdaptor>(
+ fp.GetFileCollector()));
+ DependencyOutputOptions &opts = m_compiler->getDependencyOutputOpts();
+ opts.IncludeSystemHeaders = true;
+ opts.IncludeModuleFiles = true;
+ }
+
+ // Make sure clang uses the same VFS as LLDB.
+ m_compiler->createFileManager(FileSystem::Instance().GetVirtualFileSystem());
+
+ lldb::LanguageType frame_lang =
+ expr.Language(); // defaults to lldb::eLanguageTypeUnknown
+ bool overridden_target_opts = false;
+ lldb_private::LanguageRuntime *lang_rt = nullptr;
+
+ std::string abi;
+ ArchSpec target_arch;
+ target_arch = target_sp->GetArchitecture();
+
+ const auto target_machine = target_arch.GetMachine();
+
+ // If the expression is being evaluated in the context of an existing stack
+ // frame, we introspect to see if the language runtime is available.
+
+ lldb::StackFrameSP frame_sp = exe_scope->CalculateStackFrame();
+ lldb::ProcessSP process_sp = exe_scope->CalculateProcess();
+
+ // Make sure the user hasn't provided a preferred execution language with
+ // `expression --language X -- ...`
+ if (frame_sp && frame_lang == lldb::eLanguageTypeUnknown)
+ frame_lang = frame_sp->GetLanguage();
+
+ if (process_sp && frame_lang != lldb::eLanguageTypeUnknown) {
+ lang_rt = process_sp->GetLanguageRuntime(frame_lang);
+ if (log)
+ log->Printf("Frame has language of type %s",
+ Language::GetNameForLanguageType(frame_lang));
+ }
+
+ // 2. Configure the compiler with a set of default options that are
+ // appropriate for most situations.
+ if (target_arch.IsValid()) {
+ std::string triple = target_arch.GetTriple().str();
+ m_compiler->getTargetOpts().Triple = triple;
+ if (log)
+ log->Printf("Using %s as the target triple",
+ m_compiler->getTargetOpts().Triple.c_str());
+ } else {
+ // If we get here we don't have a valid target and just have to guess.
+ // Sometimes this will be ok to just use the host target triple (when we
+ // evaluate say "2+3", but other expressions like breakpoint conditions and
+ // other things that _are_ target specific really shouldn't just be using
+ // the host triple. In such a case the language runtime should expose an
+ // overridden options set (3), below.
+ m_compiler->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
+ if (log)
+ log->Printf("Using default target triple of %s",
+ m_compiler->getTargetOpts().Triple.c_str());
+ }
+ // Now add some special fixes for known architectures: Any arm32 iOS
+ // environment, but not on arm64
+ if (m_compiler->getTargetOpts().Triple.find("arm64") == std::string::npos &&
+ m_compiler->getTargetOpts().Triple.find("arm") != std::string::npos &&
+ m_compiler->getTargetOpts().Triple.find("ios") != std::string::npos) {
+ m_compiler->getTargetOpts().ABI = "apcs-gnu";
+ }
+ // Supported subsets of x86
+ if (target_machine == llvm::Triple::x86 ||
+ target_machine == llvm::Triple::x86_64) {
+ m_compiler->getTargetOpts().Features.push_back("+sse");
+ m_compiler->getTargetOpts().Features.push_back("+sse2");
+ }
+
+ // Set the target CPU to generate code for. This will be empty for any CPU
+ // that doesn't really need to make a special
+ // CPU string.
+ m_compiler->getTargetOpts().CPU = target_arch.GetClangTargetCPU();
+
+ // Set the target ABI
+ abi = GetClangTargetABI(target_arch);
+ if (!abi.empty())
+ m_compiler->getTargetOpts().ABI = abi;
+
+ // 3. Now allow the runtime to provide custom configuration options for the
+ // target. In this case, a specialized language runtime is available and we
+ // can query it for extra options. For 99% of use cases, this will not be
+ // needed and should be provided when basic platform detection is not enough.
+ if (lang_rt)
+ overridden_target_opts =
+ lang_rt->GetOverrideExprOptions(m_compiler->getTargetOpts());
+
+ if (overridden_target_opts)
+ if (log && log->GetVerbose()) {
+ LLDB_LOGV(
+ log, "Using overridden target options for the expression evaluation");
+
+ auto opts = m_compiler->getTargetOpts();
+ LLDB_LOGV(log, "Triple: '{0}'", opts.Triple);
+ LLDB_LOGV(log, "CPU: '{0}'", opts.CPU);
+ LLDB_LOGV(log, "FPMath: '{0}'", opts.FPMath);
+ LLDB_LOGV(log, "ABI: '{0}'", opts.ABI);
+ LLDB_LOGV(log, "LinkerVersion: '{0}'", opts.LinkerVersion);
+ StringList::LogDump(log, opts.FeaturesAsWritten, "FeaturesAsWritten");
+ StringList::LogDump(log, opts.Features, "Features");
+ }
+
+ // 4. Create and install the target on the compiler.
+ m_compiler->createDiagnostics();
+ auto target_info = TargetInfo::CreateTargetInfo(
+ m_compiler->getDiagnostics(), m_compiler->getInvocation().TargetOpts);
+ if (log) {
+ log->Printf("Using SIMD alignment: %d", target_info->getSimdDefaultAlign());
+ log->Printf("Target datalayout string: '%s'",
+ target_info->getDataLayout().getStringRepresentation().c_str());
+ log->Printf("Target ABI: '%s'", target_info->getABI().str().c_str());
+ log->Printf("Target vector alignment: %d",
+ target_info->getMaxVectorAlign());
+ }
+ m_compiler->setTarget(target_info);
+
+ assert(m_compiler->hasTarget());
+
+ // 5. Set language options.
+ lldb::LanguageType language = expr.Language();
+ LangOptions &lang_opts = m_compiler->getLangOpts();
+
+ switch (language) {
+ case lldb::eLanguageTypeC:
+ case lldb::eLanguageTypeC89:
+ case lldb::eLanguageTypeC99:
+ case lldb::eLanguageTypeC11:
+ // FIXME: the following language option is a temporary workaround,
+ // to "ask for C, get C++."
+ // For now, the expression parser must use C++ anytime the language is a C
+ // family language, because the expression parser uses features of C++ to
+ // capture values.
+ lang_opts.CPlusPlus = true;
+ break;
+ case lldb::eLanguageTypeObjC:
+ lang_opts.ObjC = true;
+ // FIXME: the following language option is a temporary workaround,
+ // to "ask for ObjC, get ObjC++" (see comment above).
+ lang_opts.CPlusPlus = true;
+
+ // 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
+ // as it's a C++11 feature). Currently lldb evaluates C++14 as C++11 (see
+ // two lines below) so we decide to be consistent with that, but this could
+ // be re-evaluated in the future.
+ lang_opts.CPlusPlus11 = true;
+ break;
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeC_plus_plus_11:
+ case lldb::eLanguageTypeC_plus_plus_14:
+ lang_opts.CPlusPlus11 = true;
+ m_compiler->getHeaderSearchOpts().UseLibcxx = true;
+ LLVM_FALLTHROUGH;
+ case lldb::eLanguageTypeC_plus_plus_03:
+ lang_opts.CPlusPlus = true;
+ if (process_sp)
+ lang_opts.ObjC =
+ process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC) != nullptr;
+ break;
+ case lldb::eLanguageTypeObjC_plus_plus:
+ case lldb::eLanguageTypeUnknown:
+ default:
+ lang_opts.ObjC = true;
+ lang_opts.CPlusPlus = true;
+ lang_opts.CPlusPlus11 = true;
+ m_compiler->getHeaderSearchOpts().UseLibcxx = true;
+ break;
+ }
+
+ lang_opts.Bool = true;
+ lang_opts.WChar = true;
+ lang_opts.Blocks = true;
+ lang_opts.DebuggerSupport =
+ true; // Features specifically for debugger clients
+ if (expr.DesiredResultType() == Expression::eResultTypeId)
+ lang_opts.DebuggerCastResultToId = true;
+
+ lang_opts.CharIsSigned = ArchSpec(m_compiler->getTargetOpts().Triple.c_str())
+ .CharIsSignedByDefault();
+
+ // Spell checking is a nice feature, but it ends up completing a lot of types
+ // that we didn't strictly speaking need to complete. As a result, we spend a
+ // long time parsing and importing debug information.
+ lang_opts.SpellChecking = false;
+
+ auto *clang_expr = dyn_cast<ClangUserExpression>(&m_expr);
+ if (clang_expr && clang_expr->DidImportCxxModules()) {
+ LLDB_LOG(log, "Adding lang options for importing C++ modules");
+
+ lang_opts.Modules = true;
+ // We want to implicitly build modules.
+ lang_opts.ImplicitModules = true;
+ // To automatically import all submodules when we import 'std'.
+ lang_opts.ModulesLocalVisibility = false;
+
+ // We use the @import statements, so we need this:
+ // FIXME: We could use the modules-ts, but that currently doesn't work.
+ lang_opts.ObjC = true;
+
+ // Options we need to parse libc++ code successfully.
+ // FIXME: We should ask the driver for the appropriate default flags.
+ lang_opts.GNUMode = true;
+ lang_opts.GNUKeywords = true;
+ lang_opts.DoubleSquareBracketAttributes = true;
+ lang_opts.CPlusPlus11 = true;
+
+ SetupModuleHeaderPaths(m_compiler.get(), m_include_directories,
+ target_sp);
+ }
+
+ if (process_sp && lang_opts.ObjC) {
+ if (auto *runtime = ObjCLanguageRuntime::Get(*process_sp)) {
+ if (runtime->GetRuntimeVersion() ==
+ ObjCLanguageRuntime::ObjCRuntimeVersions::eAppleObjC_V2)
+ lang_opts.ObjCRuntime.set(ObjCRuntime::MacOSX, VersionTuple(10, 7));
+ else
+ lang_opts.ObjCRuntime.set(ObjCRuntime::FragileMacOSX,
+ VersionTuple(10, 7));
+
+ if (runtime->HasNewLiteralsAndIndexing())
+ lang_opts.DebuggerObjCLiteral = true;
+ }
+ }
+
+ lang_opts.ThreadsafeStatics = false;
+ lang_opts.AccessControl = false; // Debuggers get universal access
+ lang_opts.DollarIdents = true; // $ indicates a persistent variable name
+ // We enable all builtin functions beside the builtins from libc/libm (e.g.
+ // 'fopen'). Those libc functions are already correctly handled by LLDB, and
+ // additionally enabling them as expandable builtins is breaking Clang.
+ lang_opts.NoBuiltin = true;
+
+ // Set CodeGen options
+ m_compiler->getCodeGenOpts().EmitDeclMetadata = true;
+ m_compiler->getCodeGenOpts().InstrumentFunctions = false;
+ m_compiler->getCodeGenOpts().DisableFPElim = true;
+ m_compiler->getCodeGenOpts().OmitLeafFramePointer = false;
+ if (generate_debug_info)
+ m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo);
+ else
+ m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::NoDebugInfo);
+
+ // Disable some warnings.
+ m_compiler->getDiagnostics().setSeverityForGroup(
+ clang::diag::Flavor::WarningOrError, "unused-value",
+ clang::diag::Severity::Ignored, SourceLocation());
+ m_compiler->getDiagnostics().setSeverityForGroup(
+ clang::diag::Flavor::WarningOrError, "odr",
+ clang::diag::Severity::Ignored, SourceLocation());
+
+ // Inform the target of the language options
+ //
+ // FIXME: We shouldn't need to do this, the target should be immutable once
+ // created. This complexity should be lifted elsewhere.
+ m_compiler->getTarget().adjust(m_compiler->getLangOpts());
+
+ // 6. Set up the diagnostic buffer for reporting errors
+
+ m_compiler->getDiagnostics().setClient(new ClangDiagnosticManagerAdapter);
+
+ // 7. Set up the source management objects inside the compiler
+ m_compiler->createFileManager();
+ if (!m_compiler->hasSourceManager())
+ m_compiler->createSourceManager(m_compiler->getFileManager());
+ m_compiler->createPreprocessor(TU_Complete);
+
+ if (ClangModulesDeclVendor *decl_vendor =
+ target_sp->GetClangModulesDeclVendor()) {
+ ClangPersistentVariables *clang_persistent_vars =
+ llvm::cast<ClangPersistentVariables>(
+ target_sp->GetPersistentExpressionStateForLanguage(
+ lldb::eLanguageTypeC));
+ std::unique_ptr<PPCallbacks> pp_callbacks(
+ new LLDBPreprocessorCallbacks(*decl_vendor, *clang_persistent_vars));
+ m_pp_callbacks =
+ static_cast<LLDBPreprocessorCallbacks *>(pp_callbacks.get());
+ m_compiler->getPreprocessor().addPPCallbacks(std::move(pp_callbacks));
+ }
+
+ // 8. Most of this we get from the CompilerInstance, but we also want to give
+ // the context an ExternalASTSource.
+
+ auto &PP = m_compiler->getPreprocessor();
+ auto &builtin_context = PP.getBuiltinInfo();
+ builtin_context.initializeBuiltins(PP.getIdentifierTable(),
+ m_compiler->getLangOpts());
+
+ m_compiler->createASTContext();
+ clang::ASTContext &ast_context = m_compiler->getASTContext();
+
+ m_ast_context.reset(
+ new ClangASTContext(m_compiler->getTargetOpts().Triple.c_str()));
+ m_ast_context->setASTContext(&ast_context);
+
+ std::string module_name("$__lldb_module");
+
+ m_llvm_context.reset(new LLVMContext());
+ m_code_generator.reset(CreateLLVMCodeGen(
+ m_compiler->getDiagnostics(), module_name,
+ m_compiler->getHeaderSearchOpts(), m_compiler->getPreprocessorOpts(),
+ m_compiler->getCodeGenOpts(), *m_llvm_context));
+}
+
+ClangExpressionParser::~ClangExpressionParser() {}
+
+namespace {
+
+/// \class CodeComplete
+///
+/// A code completion consumer for the clang Sema that is responsible for
+/// creating the completion suggestions when a user requests completion
+/// of an incomplete `expr` invocation.
+class CodeComplete : public CodeCompleteConsumer {
+ CodeCompletionTUInfo m_info;
+
+ std::string m_expr;
+ unsigned m_position = 0;
+ CompletionRequest &m_request;
+ /// The printing policy we use when printing declarations for our completion
+ /// descriptions.
+ clang::PrintingPolicy m_desc_policy;
+
+ /// Returns true if the given character can be used in an identifier.
+ /// This also returns true for numbers because for completion we usually
+ /// just iterate backwards over iterators.
+ ///
+ /// Note: lldb uses '$' in its internal identifiers, so we also allow this.
+ static bool IsIdChar(char c) {
+ return c == '_' || std::isalnum(c) || c == '$';
+ }
+
+ /// Returns true if the given character is used to separate arguments
+ /// in the command line of lldb.
+ static bool IsTokenSeparator(char c) { return c == ' ' || c == '\t'; }
+
+ /// Drops all tokens in front of the expression that are unrelated for
+ /// the completion of the cmd line. 'unrelated' means here that the token
+ /// is not interested for the lldb completion API result.
+ StringRef dropUnrelatedFrontTokens(StringRef cmd) {
+ if (cmd.empty())
+ return cmd;
+
+ // If we are at the start of a word, then all tokens are unrelated to
+ // the current completion logic.
+ if (IsTokenSeparator(cmd.back()))
+ return StringRef();
+
+ // Remove all previous tokens from the string as they are unrelated
+ // to completing the current token.
+ StringRef to_remove = cmd;
+ while (!to_remove.empty() && !IsTokenSeparator(to_remove.back())) {
+ to_remove = to_remove.drop_back();
+ }
+ cmd = cmd.drop_front(to_remove.size());
+
+ return cmd;
+ }
+
+ /// Removes the last identifier token from the given cmd line.
+ StringRef removeLastToken(StringRef cmd) {
+ while (!cmd.empty() && IsIdChar(cmd.back())) {
+ cmd = cmd.drop_back();
+ }
+ return cmd;
+ }
+
+ /// Attemps to merge the given completion from the given position into the
+ /// existing command. Returns the completion string that can be returned to
+ /// the lldb completion API.
+ std::string mergeCompletion(StringRef existing, unsigned pos,
+ StringRef completion) {
+ StringRef existing_command = existing.substr(0, pos);
+ // We rewrite the last token with the completion, so let's drop that
+ // token from the command.
+ existing_command = removeLastToken(existing_command);
+ // We also should remove all previous tokens from the command as they
+ // would otherwise be added to the completion that already has the
+ // completion.
+ existing_command = dropUnrelatedFrontTokens(existing_command);
+ return existing_command.str() + completion.str();
+ }
+
+public:
+ /// Constructs a CodeComplete consumer that can be attached to a Sema.
+ /// \param[out] matches
+ /// The list of matches that the lldb completion API expects as a result.
+ /// This may already contain matches, so it's only allowed to append
+ /// to this variable.
+ /// \param[out] expr
+ /// The whole expression string that we are currently parsing. This
+ /// string needs to be equal to the input the user typed, and NOT the
+ /// final code that Clang is parsing.
+ /// \param[out] position
+ /// The character position of the user cursor in the `expr` parameter.
+ ///
+ CodeComplete(CompletionRequest &request, clang::LangOptions ops,
+ std::string expr, unsigned position)
+ : CodeCompleteConsumer(CodeCompleteOptions()),
+ m_info(std::make_shared<GlobalCodeCompletionAllocator>()), m_expr(expr),
+ m_position(position), m_request(request), m_desc_policy(ops) {
+
+ // Ensure that the printing policy is producing a description that is as
+ // short as possible.
+ m_desc_policy.SuppressScope = true;
+ m_desc_policy.SuppressTagKeyword = true;
+ m_desc_policy.FullyQualifiedName = false;
+ m_desc_policy.TerseOutput = true;
+ m_desc_policy.IncludeNewlines = false;
+ m_desc_policy.UseVoidForZeroParams = false;
+ m_desc_policy.Bool = true;
+ }
+
+ /// Deregisters and destroys this code-completion consumer.
+ ~CodeComplete() override {}
+
+ /// \name Code-completion filtering
+ /// Check if the result should be filtered out.
+ bool isResultFilteredOut(StringRef Filter,
+ CodeCompletionResult Result) override {
+ // This code is mostly copied from CodeCompleteConsumer.
+ switch (Result.Kind) {
+ case CodeCompletionResult::RK_Declaration:
+ return !(
+ Result.Declaration->getIdentifier() &&
+ Result.Declaration->getIdentifier()->getName().startswith(Filter));
+ case CodeCompletionResult::RK_Keyword:
+ return !StringRef(Result.Keyword).startswith(Filter);
+ case CodeCompletionResult::RK_Macro:
+ return !Result.Macro->getName().startswith(Filter);
+ case CodeCompletionResult::RK_Pattern:
+ return !StringRef(Result.Pattern->getAsString()).startswith(Filter);
+ }
+ // If we trigger this assert or the above switch yields a warning, then
+ // CodeCompletionResult has been enhanced with more kinds of completion
+ // results. Expand the switch above in this case.
+ assert(false && "Unknown completion result type?");
+ // If we reach this, then we should just ignore whatever kind of unknown
+ // result we got back. We probably can't turn it into any kind of useful
+ // completion suggestion with the existing code.
+ return true;
+ }
+
+ /// \name Code-completion callbacks
+ /// Process the finalized code-completion results.
+ void ProcessCodeCompleteResults(Sema &SemaRef, CodeCompletionContext Context,
+ CodeCompletionResult *Results,
+ unsigned NumResults) override {
+
+ // The Sema put the incomplete token we try to complete in here during
+ // lexing, so we need to retrieve it here to know what we are completing.
+ StringRef Filter = SemaRef.getPreprocessor().getCodeCompletionFilter();
+
+ // Iterate over all the results. Filter out results we don't want and
+ // process the rest.
+ for (unsigned I = 0; I != NumResults; ++I) {
+ // Filter the results with the information from the Sema.
+ if (!Filter.empty() && isResultFilteredOut(Filter, Results[I]))
+ continue;
+
+ CodeCompletionResult &R = Results[I];
+ std::string ToInsert;
+ std::string Description;
+ // Handle the different completion kinds that come from the Sema.
+ switch (R.Kind) {
+ case CodeCompletionResult::RK_Declaration: {
+ const NamedDecl *D = R.Declaration;
+ ToInsert = R.Declaration->getNameAsString();
+ // If we have a function decl that has no arguments we want to
+ // complete the empty parantheses for the user. If the function has
+ // arguments, we at least complete the opening bracket.
+ if (const FunctionDecl *F = dyn_cast<FunctionDecl>(D)) {
+ if (F->getNumParams() == 0)
+ ToInsert += "()";
+ else
+ ToInsert += "(";
+ raw_string_ostream OS(Description);
+ F->print(OS, m_desc_policy, false);
+ OS.flush();
+ } else if (const VarDecl *V = dyn_cast<VarDecl>(D)) {
+ Description = V->getType().getAsString(m_desc_policy);
+ } else if (const FieldDecl *F = dyn_cast<FieldDecl>(D)) {
+ Description = F->getType().getAsString(m_desc_policy);
+ } else if (const NamespaceDecl *N = dyn_cast<NamespaceDecl>(D)) {
+ // If we try to complete a namespace, then we can directly append
+ // the '::'.
+ if (!N->isAnonymousNamespace())
+ ToInsert += "::";
+ }
+ break;
+ }
+ case CodeCompletionResult::RK_Keyword:
+ ToInsert = R.Keyword;
+ break;
+ case CodeCompletionResult::RK_Macro:
+ ToInsert = R.Macro->getName().str();
+ break;
+ case CodeCompletionResult::RK_Pattern:
+ ToInsert = R.Pattern->getTypedText();
+ break;
+ }
+ // At this point all information is in the ToInsert string.
+
+ // We also filter some internal lldb identifiers here. The user
+ // shouldn't see these.
+ if (StringRef(ToInsert).startswith("$__lldb_"))
+ continue;
+ if (!ToInsert.empty()) {
+ // Merge the suggested Token into the existing command line to comply
+ // with the kind of result the lldb API expects.
+ std::string CompletionSuggestion =
+ mergeCompletion(m_expr, m_position, ToInsert);
+ m_request.AddCompletion(CompletionSuggestion, Description);
+ }
+ }
+ }
+
+ /// \param S the semantic-analyzer object for which code-completion is being
+ /// done.
+ ///
+ /// \param CurrentArg the index of the current argument.
+ ///
+ /// \param Candidates an array of overload candidates.
+ ///
+ /// \param NumCandidates the number of overload candidates
+ void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
+ OverloadCandidate *Candidates,
+ unsigned NumCandidates,
+ SourceLocation OpenParLoc) override {
+ // At the moment we don't filter out any overloaded candidates.
+ }
+
+ CodeCompletionAllocator &getAllocator() override {
+ return m_info.getAllocator();
+ }
+
+ CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return m_info; }
+};
+} // namespace
+
+bool ClangExpressionParser::Complete(CompletionRequest &request, unsigned line,
+ unsigned pos, unsigned typed_pos) {
+ DiagnosticManager mgr;
+ // We need the raw user expression here because that's what the CodeComplete
+ // class uses to provide completion suggestions.
+ // However, the `Text` method only gives us the transformed expression here.
+ // To actually get the raw user input here, we have to cast our expression to
+ // the LLVMUserExpression which exposes the right API. This should never fail
+ // as we always have a ClangUserExpression whenever we call this.
+ ClangUserExpression *llvm_expr = cast<ClangUserExpression>(&m_expr);
+ CodeComplete CC(request, m_compiler->getLangOpts(), llvm_expr->GetUserText(),
+ typed_pos);
+ // We don't need a code generator for parsing.
+ m_code_generator.reset();
+ // Start parsing the expression with our custom code completion consumer.
+ ParseInternal(mgr, &CC, line, pos);
+ return true;
+}
+
+unsigned ClangExpressionParser::Parse(DiagnosticManager &diagnostic_manager) {
+ return ParseInternal(diagnostic_manager);
+}
+
+unsigned
+ClangExpressionParser::ParseInternal(DiagnosticManager &diagnostic_manager,
+ CodeCompleteConsumer *completion_consumer,
+ unsigned completion_line,
+ unsigned completion_column) {
+ ClangDiagnosticManagerAdapter *adapter =
+ static_cast<ClangDiagnosticManagerAdapter *>(
+ m_compiler->getDiagnostics().getClient());
+ clang::TextDiagnosticBuffer *diag_buf = adapter->GetPassthrough();
+ diag_buf->FlushDiagnostics(m_compiler->getDiagnostics());
+
+ adapter->ResetManager(&diagnostic_manager);
+
+ const char *expr_text = m_expr.Text();
+
+ clang::SourceManager &source_mgr = m_compiler->getSourceManager();
+ bool created_main_file = false;
+
+ // Clang wants to do completion on a real file known by Clang's file manager,
+ // so we have to create one to make this work.
+ // TODO: We probably could also simulate to Clang's file manager that there
+ // is a real file that contains our code.
+ bool should_create_file = completion_consumer != nullptr;
+
+ // We also want a real file on disk if we generate full debug info.
+ should_create_file |= m_compiler->getCodeGenOpts().getDebugInfo() ==
+ codegenoptions::FullDebugInfo;
+
+ if (should_create_file) {
+ int temp_fd = -1;
+ llvm::SmallString<128> result_path;
+ if (FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir()) {
+ tmpdir_file_spec.AppendPathComponent("lldb-%%%%%%.expr");
+ std::string temp_source_path = tmpdir_file_spec.GetPath();
+ llvm::sys::fs::createUniqueFile(temp_source_path, temp_fd, result_path);
+ } else {
+ llvm::sys::fs::createTemporaryFile("lldb", "expr", temp_fd, result_path);
+ }
+
+ if (temp_fd != -1) {
+ lldb_private::File file(temp_fd, true);
+ const size_t expr_text_len = strlen(expr_text);
+ size_t bytes_written = expr_text_len;
+ if (file.Write(expr_text, bytes_written).Success()) {
+ if (bytes_written == expr_text_len) {
+ file.Close();
+ source_mgr.setMainFileID(source_mgr.createFileID(
+ m_compiler->getFileManager().getFile(result_path),
+ SourceLocation(), SrcMgr::C_User));
+ created_main_file = true;
+ }
+ }
+ }
+ }
+
+ if (!created_main_file) {
+ std::unique_ptr<MemoryBuffer> memory_buffer =
+ MemoryBuffer::getMemBufferCopy(expr_text, "<lldb-expr>");
+ source_mgr.setMainFileID(source_mgr.createFileID(std::move(memory_buffer)));
+ }
+
+ diag_buf->BeginSourceFile(m_compiler->getLangOpts(),
+ &m_compiler->getPreprocessor());
+
+ ClangExpressionHelper *type_system_helper =
+ dyn_cast<ClangExpressionHelper>(m_expr.GetTypeSystemHelper());
+
+ // If we want to parse for code completion, we need to attach our code
+ // completion consumer to the Sema and specify a completion position.
+ // While parsing the Sema will call this consumer with the provided
+ // completion suggestions.
+ if (completion_consumer) {
+ auto main_file = source_mgr.getFileEntryForID(source_mgr.getMainFileID());
+ auto &PP = m_compiler->getPreprocessor();
+ // Lines and columns start at 1 in Clang, but code completion positions are
+ // indexed from 0, so we need to add 1 to the line and column here.
+ ++completion_line;
+ ++completion_column;
+ PP.SetCodeCompletionPoint(main_file, completion_line, completion_column);
+ }
+
+ ASTConsumer *ast_transformer =
+ type_system_helper->ASTTransformer(m_code_generator.get());
+
+ std::unique_ptr<clang::ASTConsumer> Consumer;
+ if (ast_transformer) {
+ Consumer.reset(new ASTConsumerForwarder(ast_transformer));
+ } else if (m_code_generator) {
+ Consumer.reset(new ASTConsumerForwarder(m_code_generator.get()));
+ } else {
+ Consumer.reset(new ASTConsumer());
+ }
+
+ clang::ASTContext &ast_context = m_compiler->getASTContext();
+
+ m_compiler->setSema(new Sema(m_compiler->getPreprocessor(), ast_context,
+ *Consumer, TU_Complete, completion_consumer));
+ m_compiler->setASTConsumer(std::move(Consumer));
+
+ if (ast_context.getLangOpts().Modules) {
+ m_compiler->createModuleManager();
+ m_ast_context->setSema(&m_compiler->getSema());
+ }
+
+ ClangExpressionDeclMap *decl_map = type_system_helper->DeclMap();
+ if (decl_map) {
+ decl_map->InstallCodeGenerator(&m_compiler->getASTConsumer());
+
+ clang::ExternalASTSource *ast_source = decl_map->CreateProxy();
+
+ if (ast_context.getExternalSource()) {
+ auto module_wrapper =
+ new ExternalASTSourceWrapper(ast_context.getExternalSource());
+
+ auto ast_source_wrapper = new ExternalASTSourceWrapper(ast_source);
+
+ auto multiplexer =
+ new SemaSourceWithPriorities(*module_wrapper, *ast_source_wrapper);
+ IntrusiveRefCntPtr<ExternalASTSource> Source(multiplexer);
+ ast_context.setExternalSource(Source);
+ } else {
+ ast_context.setExternalSource(ast_source);
+ }
+ decl_map->InstallASTContext(ast_context, m_compiler->getFileManager());
+ }
+
+ // Check that the ASTReader is properly attached to ASTContext and Sema.
+ if (ast_context.getLangOpts().Modules) {
+ assert(m_compiler->getASTContext().getExternalSource() &&
+ "ASTContext doesn't know about the ASTReader?");
+ assert(m_compiler->getSema().getExternalSource() &&
+ "Sema doesn't know about the ASTReader?");
+ }
+
+ {
+ llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(
+ &m_compiler->getSema());
+ ParseAST(m_compiler->getSema(), false, false);
+ }
+
+ // Make sure we have no pointer to the Sema we are about to destroy.
+ if (ast_context.getLangOpts().Modules)
+ m_ast_context->setSema(nullptr);
+ // Destroy the Sema. This is necessary because we want to emulate the
+ // original behavior of ParseAST (which also destroys the Sema after parsing).
+ m_compiler->setSema(nullptr);
+
+ diag_buf->EndSourceFile();
+
+ unsigned num_errors = diag_buf->getNumErrors();
+
+ if (m_pp_callbacks && m_pp_callbacks->hasErrors()) {
+ num_errors++;
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "while importing modules:");
+ diagnostic_manager.AppendMessageToDiagnostic(
+ m_pp_callbacks->getErrorString());
+ }
+
+ if (!num_errors) {
+ if (type_system_helper->DeclMap() &&
+ !type_system_helper->DeclMap()->ResolveUnknownTypes()) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "Couldn't infer the type of a variable");
+ num_errors++;
+ }
+ }
+
+ if (!num_errors) {
+ type_system_helper->CommitPersistentDecls();
+ }
+
+ adapter->ResetManager();
+
+ return num_errors;
+}
+
+std::string
+ClangExpressionParser::GetClangTargetABI(const ArchSpec &target_arch) {
+ std::string abi;
+
+ if (target_arch.IsMIPS()) {
+ switch (target_arch.GetFlags() & ArchSpec::eMIPSABI_mask) {
+ case ArchSpec::eMIPSABI_N64:
+ abi = "n64";
+ break;
+ case ArchSpec::eMIPSABI_N32:
+ abi = "n32";
+ break;
+ case ArchSpec::eMIPSABI_O32:
+ abi = "o32";
+ break;
+ default:
+ break;
+ }
+ }
+ return abi;
+}
+
+bool ClangExpressionParser::RewriteExpression(
+ DiagnosticManager &diagnostic_manager) {
+ clang::SourceManager &source_manager = m_compiler->getSourceManager();
+ clang::edit::EditedSource editor(source_manager, m_compiler->getLangOpts(),
+ nullptr);
+ clang::edit::Commit commit(editor);
+ clang::Rewriter rewriter(source_manager, m_compiler->getLangOpts());
+
+ class RewritesReceiver : public edit::EditsReceiver {
+ Rewriter &rewrite;
+
+ public:
+ RewritesReceiver(Rewriter &in_rewrite) : rewrite(in_rewrite) {}
+
+ void insert(SourceLocation loc, StringRef text) override {
+ rewrite.InsertText(loc, text);
+ }
+ void replace(CharSourceRange range, StringRef text) override {
+ rewrite.ReplaceText(range.getBegin(), rewrite.getRangeSize(range), text);
+ }
+ };
+
+ RewritesReceiver rewrites_receiver(rewriter);
+
+ const DiagnosticList &diagnostics = diagnostic_manager.Diagnostics();
+ size_t num_diags = diagnostics.size();
+ if (num_diags == 0)
+ return false;
+
+ for (const Diagnostic *diag : diagnostic_manager.Diagnostics()) {
+ const ClangDiagnostic *diagnostic = llvm::dyn_cast<ClangDiagnostic>(diag);
+ if (diagnostic && diagnostic->HasFixIts()) {
+ for (const FixItHint &fixit : diagnostic->FixIts()) {
+ // This is cobbed from clang::Rewrite::FixItRewriter.
+ if (fixit.CodeToInsert.empty()) {
+ if (fixit.InsertFromRange.isValid()) {
+ commit.insertFromRange(fixit.RemoveRange.getBegin(),
+ fixit.InsertFromRange, /*afterToken=*/false,
+ fixit.BeforePreviousInsertions);
+ } else
+ commit.remove(fixit.RemoveRange);
+ } else {
+ if (fixit.RemoveRange.isTokenRange() ||
+ fixit.RemoveRange.getBegin() != fixit.RemoveRange.getEnd())
+ commit.replace(fixit.RemoveRange, fixit.CodeToInsert);
+ else
+ commit.insert(fixit.RemoveRange.getBegin(), fixit.CodeToInsert,
+ /*afterToken=*/false, fixit.BeforePreviousInsertions);
+ }
+ }
+ }
+ }
+
+ // FIXME - do we want to try to propagate specific errors here?
+ if (!commit.isCommitable())
+ return false;
+ else if (!editor.commit(commit))
+ return false;
+
+ // Now play all the edits, and stash the result in the diagnostic manager.
+ editor.applyRewrites(rewrites_receiver);
+ RewriteBuffer &main_file_buffer =
+ rewriter.getEditBuffer(source_manager.getMainFileID());
+
+ std::string fixed_expression;
+ llvm::raw_string_ostream out_stream(fixed_expression);
+
+ main_file_buffer.write(out_stream);
+ out_stream.flush();
+ diagnostic_manager.SetFixedExpression(fixed_expression);
+
+ return true;
+}
+
+static bool FindFunctionInModule(ConstString &mangled_name,
+ llvm::Module *module, const char *orig_name) {
+ for (const auto &func : module->getFunctionList()) {
+ const StringRef &name = func.getName();
+ if (name.find(orig_name) != StringRef::npos) {
+ mangled_name.SetString(name);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+lldb_private::Status ClangExpressionParser::PrepareForExecution(
+ lldb::addr_t &func_addr, lldb::addr_t &func_end,
+ lldb::IRExecutionUnitSP &execution_unit_sp, ExecutionContext &exe_ctx,
+ bool &can_interpret, ExecutionPolicy execution_policy) {
+ func_addr = LLDB_INVALID_ADDRESS;
+ func_end = LLDB_INVALID_ADDRESS;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ lldb_private::Status err;
+
+ std::unique_ptr<llvm::Module> llvm_module_up(
+ m_code_generator->ReleaseModule());
+
+ if (!llvm_module_up) {
+ err.SetErrorToGenericError();
+ err.SetErrorString("IR doesn't contain a module");
+ return err;
+ }
+
+ ConstString function_name;
+
+ if (execution_policy != eExecutionPolicyTopLevel) {
+ // Find the actual name of the function (it's often mangled somehow)
+
+ if (!FindFunctionInModule(function_name, llvm_module_up.get(),
+ m_expr.FunctionName())) {
+ err.SetErrorToGenericError();
+ err.SetErrorStringWithFormat("Couldn't find %s() in the module",
+ m_expr.FunctionName());
+ return err;
+ } else {
+ if (log)
+ log->Printf("Found function %s for %s", function_name.AsCString(),
+ m_expr.FunctionName());
+ }
+ }
+
+ SymbolContext sc;
+
+ if (lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP()) {
+ sc = frame_sp->GetSymbolContext(lldb::eSymbolContextEverything);
+ } else if (lldb::TargetSP target_sp = exe_ctx.GetTargetSP()) {
+ sc.target_sp = target_sp;
+ }
+
+ LLVMUserExpression::IRPasses custom_passes;
+ {
+ auto lang = m_expr.Language();
+ if (log)
+ log->Printf("%s - Current expression language is %s\n", __FUNCTION__,
+ Language::GetNameForLanguageType(lang));
+ lldb::ProcessSP process_sp = exe_ctx.GetProcessSP();
+ if (process_sp && lang != lldb::eLanguageTypeUnknown) {
+ auto runtime = process_sp->GetLanguageRuntime(lang);
+ if (runtime)
+ runtime->GetIRPasses(custom_passes);
+ }
+ }
+
+ if (custom_passes.EarlyPasses) {
+ if (log)
+ log->Printf("%s - Running Early IR Passes from LanguageRuntime on "
+ "expression module '%s'",
+ __FUNCTION__, m_expr.FunctionName());
+
+ custom_passes.EarlyPasses->run(*llvm_module_up);
+ }
+
+ execution_unit_sp = std::make_shared<IRExecutionUnit>(
+ m_llvm_context, // handed off here
+ llvm_module_up, // handed off here
+ function_name, exe_ctx.GetTargetSP(), sc,
+ m_compiler->getTargetOpts().Features);
+
+ ClangExpressionHelper *type_system_helper =
+ dyn_cast<ClangExpressionHelper>(m_expr.GetTypeSystemHelper());
+ ClangExpressionDeclMap *decl_map =
+ type_system_helper->DeclMap(); // result can be NULL
+
+ if (decl_map) {
+ Stream *error_stream = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ error_stream = target->GetDebugger().GetErrorFile().get();
+
+ IRForTarget ir_for_target(decl_map, m_expr.NeedsVariableResolution(),
+ *execution_unit_sp, *error_stream,
+ function_name.AsCString());
+
+ bool ir_can_run =
+ ir_for_target.runOnModule(*execution_unit_sp->GetModule());
+
+ if (!ir_can_run) {
+ err.SetErrorString(
+ "The expression could not be prepared to run in the target");
+ return err;
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (execution_policy != eExecutionPolicyAlways &&
+ execution_policy != eExecutionPolicyTopLevel) {
+ lldb_private::Status interpret_error;
+
+ bool interpret_function_calls =
+ !process ? false : process->CanInterpretFunctionCalls();
+ can_interpret = IRInterpreter::CanInterpret(
+ *execution_unit_sp->GetModule(), *execution_unit_sp->GetFunction(),
+ interpret_error, interpret_function_calls);
+
+ if (!can_interpret && execution_policy == eExecutionPolicyNever) {
+ err.SetErrorStringWithFormat("Can't run the expression locally: %s",
+ interpret_error.AsCString());
+ return err;
+ }
+ }
+
+ if (!process && execution_policy == eExecutionPolicyAlways) {
+ err.SetErrorString("Expression needed to run in the target, but the "
+ "target can't be run");
+ return err;
+ }
+
+ if (!process && execution_policy == eExecutionPolicyTopLevel) {
+ err.SetErrorString("Top-level code needs to be inserted into a runnable "
+ "target, but the target can't be run");
+ return err;
+ }
+
+ if (execution_policy == eExecutionPolicyAlways ||
+ (execution_policy != eExecutionPolicyTopLevel && !can_interpret)) {
+ if (m_expr.NeedsValidation() && process) {
+ if (!process->GetDynamicCheckers()) {
+ ClangDynamicCheckerFunctions *dynamic_checkers =
+ new ClangDynamicCheckerFunctions();
+
+ DiagnosticManager install_diagnostics;
+
+ if (!dynamic_checkers->Install(install_diagnostics, exe_ctx)) {
+ if (install_diagnostics.Diagnostics().size())
+ err.SetErrorString(install_diagnostics.GetString().c_str());
+ else
+ err.SetErrorString("couldn't install checkers, unknown error");
+
+ return err;
+ }
+
+ process->SetDynamicCheckers(dynamic_checkers);
+
+ if (log)
+ log->Printf("== [ClangExpressionParser::PrepareForExecution] "
+ "Finished installing dynamic checkers ==");
+ }
+
+ if (auto *checker_funcs = llvm::dyn_cast<ClangDynamicCheckerFunctions>(
+ process->GetDynamicCheckers())) {
+ IRDynamicChecks ir_dynamic_checks(*checker_funcs,
+ function_name.AsCString());
+
+ llvm::Module *module = execution_unit_sp->GetModule();
+ if (!module || !ir_dynamic_checks.runOnModule(*module)) {
+ err.SetErrorToGenericError();
+ err.SetErrorString("Couldn't add dynamic checks to the expression");
+ return err;
+ }
+
+ if (custom_passes.LatePasses) {
+ if (log)
+ log->Printf("%s - Running Late IR Passes from LanguageRuntime on "
+ "expression module '%s'",
+ __FUNCTION__, m_expr.FunctionName());
+
+ custom_passes.LatePasses->run(*module);
+ }
+ }
+ }
+ }
+
+ if (execution_policy == eExecutionPolicyAlways ||
+ execution_policy == eExecutionPolicyTopLevel || !can_interpret) {
+ execution_unit_sp->GetRunnableInfo(err, func_addr, func_end);
+ }
+ } else {
+ execution_unit_sp->GetRunnableInfo(err, func_addr, func_end);
+ }
+
+ return err;
+}
+
+lldb_private::Status ClangExpressionParser::RunStaticInitializers(
+ lldb::IRExecutionUnitSP &execution_unit_sp, ExecutionContext &exe_ctx) {
+ lldb_private::Status err;
+
+ lldbassert(execution_unit_sp.get());
+ lldbassert(exe_ctx.HasThreadScope());
+
+ if (!execution_unit_sp.get()) {
+ err.SetErrorString(
+ "can't run static initializers for a NULL execution unit");
+ return err;
+ }
+
+ if (!exe_ctx.HasThreadScope()) {
+ err.SetErrorString("can't run static initializers without a thread");
+ return err;
+ }
+
+ std::vector<lldb::addr_t> static_initializers;
+
+ execution_unit_sp->GetStaticInitializers(static_initializers);
+
+ for (lldb::addr_t static_initializer : static_initializers) {
+ EvaluateExpressionOptions options;
+
+ lldb::ThreadPlanSP call_static_initializer(new ThreadPlanCallFunction(
+ exe_ctx.GetThreadRef(), Address(static_initializer), CompilerType(),
+ llvm::ArrayRef<lldb::addr_t>(), options));
+
+ DiagnosticManager execution_errors;
+ lldb::ExpressionResults results =
+ exe_ctx.GetThreadRef().GetProcess()->RunThreadPlan(
+ exe_ctx, call_static_initializer, options, execution_errors);
+
+ if (results != lldb::eExpressionCompleted) {
+ err.SetErrorStringWithFormat("couldn't run static initializer: %s",
+ execution_errors.GetString().c_str());
+ return err;
+ }
+ }
+
+ return err;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
new file mode 100644
index 000000000000..a42c2190ffb8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
@@ -0,0 +1,184 @@
+//===-- ClangExpressionParser.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangExpressionParser_h_
+#define liblldb_ClangExpressionParser_h_
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/ExpressionParser.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-public.h"
+
+#include <string>
+#include <vector>
+
+namespace clang {
+class CodeCompleteConsumer;
+}
+
+namespace lldb_private {
+
+class IRExecutionUnit;
+
+/// \class ClangExpressionParser ClangExpressionParser.h
+/// "lldb/Expression/ClangExpressionParser.h" Encapsulates an instance of
+/// Clang that can parse expressions.
+///
+/// ClangExpressionParser is responsible for preparing an instance of
+/// ClangExpression for execution. ClangExpressionParser uses ClangExpression
+/// as a glorified parameter list, performing the required parsing and
+/// conversion to formats (DWARF bytecode, or JIT compiled machine code) that
+/// can be executed.
+class ClangExpressionParser : public ExpressionParser {
+public:
+ /// Constructor
+ ///
+ /// Initializes class variables.
+ ///
+ /// \param[in] exe_scope,
+ /// If non-NULL, an execution context scope that can help to
+ /// correctly create an expression with a valid process for
+ /// optional tuning Objective-C runtime support. Can be NULL.
+ ///
+ /// \param[in] expr
+ /// The expression to be parsed.
+ ///
+ /// @param[in] include_directories
+ /// List of include directories that should be used when parsing the
+ /// expression.
+ ClangExpressionParser(ExecutionContextScope *exe_scope, Expression &expr,
+ bool generate_debug_info,
+ std::vector<ConstString> include_directories = {});
+
+ /// Destructor
+ ~ClangExpressionParser() override;
+
+ bool Complete(CompletionRequest &request, unsigned line, unsigned pos,
+ unsigned typed_pos) override;
+
+ /// Parse a single expression and convert it to IR using Clang. Don't wrap
+ /// the expression in anything at all.
+ ///
+ /// \param[in] diagnostic_manager
+ /// The diagnostic manager to report errors to.
+ ///
+ /// \return
+ /// The number of errors encountered during parsing. 0 means
+ /// success.
+ unsigned Parse(DiagnosticManager &diagnostic_manager) override;
+
+ bool RewriteExpression(DiagnosticManager &diagnostic_manager) override;
+
+ /// Ready an already-parsed expression for execution, possibly evaluating it
+ /// statically.
+ ///
+ /// \param[out] func_addr
+ /// The address to which the function has been written.
+ ///
+ /// \param[out] func_end
+ /// The end of the function's allocated memory region. (func_addr
+ /// and func_end do not delimit an allocated region; the allocated
+ /// region may begin before func_addr.)
+ ///
+ /// \param[in] execution_unit_sp
+ /// After parsing, ownership of the execution unit for
+ /// for the expression is handed to this shared pointer.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to write the function into.
+ ///
+ /// \param[out] evaluated_statically
+ /// Set to true if the expression could be interpreted statically;
+ /// untouched otherwise.
+ ///
+ /// \param[out] const_result
+ /// If the result of the expression is constant, and the
+ /// expression has no side effects, this is set to the result of the
+ /// expression.
+ ///
+ /// \param[in] execution_policy
+ /// Determines whether the expression must be JIT-compiled, must be
+ /// evaluated statically, or whether this decision may be made
+ /// opportunistically.
+ ///
+ /// \return
+ /// An error code indicating the success or failure of the operation.
+ /// Test with Success().
+ Status
+ PrepareForExecution(lldb::addr_t &func_addr, lldb::addr_t &func_end,
+ lldb::IRExecutionUnitSP &execution_unit_sp,
+ ExecutionContext &exe_ctx, bool &can_interpret,
+ lldb_private::ExecutionPolicy execution_policy) override;
+
+ /// Run all static initializers for an execution unit.
+ ///
+ /// \param[in] execution_unit_sp
+ /// The execution unit.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to use when running them. Thread can't be null.
+ ///
+ /// \return
+ /// The error code indicating the
+ Status RunStaticInitializers(lldb::IRExecutionUnitSP &execution_unit_sp,
+ ExecutionContext &exe_ctx);
+
+ /// Returns a string representing current ABI.
+ ///
+ /// \param[in] target_arch
+ /// The target architecture.
+ ///
+ /// \return
+ /// A string representing target ABI for the current architecture.
+ std::string GetClangTargetABI(const ArchSpec &target_arch);
+
+private:
+ /// Parses the expression.
+ ///
+ /// \param[in] diagnostic_manager
+ /// The diagnostic manager that should receive the diagnostics
+ /// from the parsing process.
+ ///
+ /// \param[in] completion
+ /// The completion consumer that should be used during parsing
+ /// (or a nullptr if no consumer should be attached).
+ ///
+ /// \param[in] completion_line
+ /// The line in which the completion marker should be placed.
+ /// The first line is represented by the value 0.
+ ///
+ /// \param[in] completion_column
+ /// The column in which the completion marker should be placed.
+ /// The first column is represented by the value 0.
+ ///
+ /// \return
+ /// The number of parsing errors.
+ unsigned ParseInternal(DiagnosticManager &diagnostic_manager,
+ clang::CodeCompleteConsumer *completion = nullptr,
+ unsigned completion_line = 0,
+ unsigned completion_column = 0);
+
+ std::unique_ptr<llvm::LLVMContext>
+ m_llvm_context; ///< The LLVM context to generate IR into
+ std::unique_ptr<clang::CompilerInstance>
+ m_compiler; ///< The Clang compiler used to parse expressions into IR
+ std::unique_ptr<clang::CodeGenerator>
+ m_code_generator; ///< The Clang object that generates IR
+
+ class LLDBPreprocessorCallbacks;
+ LLDBPreprocessorCallbacks *m_pp_callbacks; ///< Called when the preprocessor
+ ///encounters module imports
+ std::unique_ptr<ClangASTContext> m_ast_context;
+
+ std::vector<ConstString> m_include_directories;
+};
+}
+
+#endif // liblldb_ClangExpressionParser_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
new file mode 100644
index 000000000000..f513b1eea360
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
@@ -0,0 +1,497 @@
+//===-- ClangExpressionSourceCode.cpp ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangExpressionSourceCode.h"
+
+#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
+
+#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
+#include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/DebugMacros.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+
+const char *ClangExpressionSourceCode::g_expression_prefix = R"(
+#ifndef NULL
+#define NULL (__null)
+#endif
+#ifndef Nil
+#define Nil (__null)
+#endif
+#ifndef nil
+#define nil (__null)
+#endif
+#ifndef YES
+#define YES ((BOOL)1)
+#endif
+#ifndef NO
+#define NO ((BOOL)0)
+#endif
+typedef __INT8_TYPE__ int8_t;
+typedef __UINT8_TYPE__ uint8_t;
+typedef __INT16_TYPE__ int16_t;
+typedef __UINT16_TYPE__ uint16_t;
+typedef __INT32_TYPE__ int32_t;
+typedef __UINT32_TYPE__ uint32_t;
+typedef __INT64_TYPE__ int64_t;
+typedef __UINT64_TYPE__ uint64_t;
+typedef __INTPTR_TYPE__ intptr_t;
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __SIZE_TYPE__ size_t;
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef unsigned short unichar;
+extern "C"
+{
+ int printf(const char * __restrict, ...);
+}
+)";
+
+static const char *c_start_marker = " /*LLDB_BODY_START*/\n ";
+static const char *c_end_marker = ";\n /*LLDB_BODY_END*/\n";
+
+namespace {
+
+class AddMacroState {
+ enum State {
+ CURRENT_FILE_NOT_YET_PUSHED,
+ CURRENT_FILE_PUSHED,
+ CURRENT_FILE_POPPED
+ };
+
+public:
+ AddMacroState(const FileSpec &current_file, const uint32_t current_file_line)
+ : m_state(CURRENT_FILE_NOT_YET_PUSHED), m_current_file(current_file),
+ m_current_file_line(current_file_line) {}
+
+ void StartFile(const FileSpec &file) {
+ m_file_stack.push_back(file);
+ if (file == m_current_file)
+ m_state = CURRENT_FILE_PUSHED;
+ }
+
+ void EndFile() {
+ if (m_file_stack.size() == 0)
+ return;
+
+ FileSpec old_top = m_file_stack.back();
+ m_file_stack.pop_back();
+ if (old_top == m_current_file)
+ m_state = CURRENT_FILE_POPPED;
+ }
+
+ // An entry is valid if it occurs before the current line in the current
+ // file.
+ bool IsValidEntry(uint32_t line) {
+ switch (m_state) {
+ case CURRENT_FILE_NOT_YET_PUSHED:
+ return true;
+ case CURRENT_FILE_PUSHED:
+ // If we are in file included in the current file, the entry should be
+ // added.
+ if (m_file_stack.back() != m_current_file)
+ return true;
+
+ return line < m_current_file_line;
+ default:
+ return false;
+ }
+ }
+
+private:
+ std::vector<FileSpec> m_file_stack;
+ State m_state;
+ FileSpec m_current_file;
+ uint32_t m_current_file_line;
+};
+
+} // anonymous namespace
+
+static void AddMacros(const DebugMacros *dm, CompileUnit *comp_unit,
+ AddMacroState &state, StreamString &stream) {
+ if (dm == nullptr)
+ return;
+
+ for (size_t i = 0; i < dm->GetNumMacroEntries(); i++) {
+ const DebugMacroEntry &entry = dm->GetMacroEntryAtIndex(i);
+ uint32_t line;
+
+ switch (entry.GetType()) {
+ case DebugMacroEntry::DEFINE:
+ if (state.IsValidEntry(entry.GetLineNumber()))
+ stream.Printf("#define %s\n", entry.GetMacroString().AsCString());
+ else
+ return;
+ break;
+ case DebugMacroEntry::UNDEF:
+ if (state.IsValidEntry(entry.GetLineNumber()))
+ stream.Printf("#undef %s\n", entry.GetMacroString().AsCString());
+ else
+ return;
+ break;
+ case DebugMacroEntry::START_FILE:
+ line = entry.GetLineNumber();
+ if (state.IsValidEntry(line))
+ state.StartFile(entry.GetFileSpec(comp_unit));
+ else
+ return;
+ break;
+ case DebugMacroEntry::END_FILE:
+ state.EndFile();
+ break;
+ case DebugMacroEntry::INDIRECT:
+ AddMacros(entry.GetIndirectDebugMacros(), comp_unit, state, stream);
+ break;
+ default:
+ // This is an unknown/invalid entry. Ignore.
+ break;
+ }
+ }
+}
+
+namespace {
+/// Allows checking if a token is contained in a given expression.
+class TokenVerifier {
+ /// The tokens we found in the expression.
+ llvm::StringSet<> m_tokens;
+
+public:
+ TokenVerifier(std::string body);
+ /// Returns true iff the given expression body contained a token with the
+ /// given content.
+ bool hasToken(llvm::StringRef token) const {
+ return m_tokens.find(token) != m_tokens.end();
+ }
+};
+} // namespace
+
+TokenVerifier::TokenVerifier(std::string body) {
+ using namespace clang;
+
+ // We only care about tokens and not their original source locations. If we
+ // move the whole expression to only be in one line we can simplify the
+ // following code that extracts the token contents.
+ std::replace(body.begin(), body.end(), '\n', ' ');
+ std::replace(body.begin(), body.end(), '\r', ' ');
+
+ FileSystemOptions file_opts;
+ FileManager file_mgr(file_opts,
+ FileSystem::Instance().GetVirtualFileSystem());
+
+ // Let's build the actual source code Clang needs and setup some utility
+ // objects.
+ llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs());
+ llvm::IntrusiveRefCntPtr<DiagnosticOptions> diags_opts(
+ new DiagnosticOptions());
+ DiagnosticsEngine diags(diag_ids, diags_opts);
+ clang::SourceManager SM(diags, file_mgr);
+ auto buf = llvm::MemoryBuffer::getMemBuffer(body);
+
+ FileID FID = SM.createFileID(clang::SourceManager::Unowned, buf.get());
+
+ // Let's just enable the latest ObjC and C++ which should get most tokens
+ // right.
+ LangOptions Opts;
+ Opts.ObjC = true;
+ Opts.DollarIdents = true;
+ Opts.CPlusPlus17 = true;
+ Opts.LineComment = true;
+
+ Lexer lex(FID, buf.get(), SM, Opts);
+
+ Token token;
+ bool exit = false;
+ while (!exit) {
+ // Returns true if this is the last token we get from the lexer.
+ exit = lex.LexFromRawLexer(token);
+
+ // Extract the column number which we need to extract the token content.
+ // Our expression is just one line, so we don't need to handle any line
+ // numbers here.
+ bool invalid = false;
+ unsigned start = SM.getSpellingColumnNumber(token.getLocation(), &invalid);
+ if (invalid)
+ continue;
+ // Column numbers start at 1, but indexes in our string start at 0.
+ --start;
+
+ // Annotations don't have a length, so let's skip them.
+ if (token.isAnnotation())
+ continue;
+
+ // Extract the token string from our source code and store it.
+ std::string token_str = body.substr(start, token.getLength());
+ if (token_str.empty())
+ continue;
+ m_tokens.insert(token_str);
+ }
+}
+
+static void AddLocalVariableDecls(const lldb::VariableListSP &var_list_sp,
+ StreamString &stream,
+ const std::string &expr,
+ lldb::LanguageType wrapping_language) {
+ TokenVerifier tokens(expr);
+
+ for (size_t i = 0; i < var_list_sp->GetSize(); i++) {
+ lldb::VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
+
+ ConstString var_name = var_sp->GetName();
+
+
+ // We can check for .block_descriptor w/o checking for langauge since this
+ // is not a valid identifier in either C or C++.
+ if (!var_name || var_name == ".block_descriptor")
+ continue;
+
+ if (!expr.empty() && !tokens.hasToken(var_name.GetStringRef()))
+ continue;
+
+ if ((var_name == "self" || var_name == "_cmd") &&
+ (wrapping_language == lldb::eLanguageTypeObjC ||
+ wrapping_language == lldb::eLanguageTypeObjC_plus_plus))
+ continue;
+
+ if (var_name == "this" &&
+ wrapping_language == lldb::eLanguageTypeC_plus_plus)
+ continue;
+
+ stream.Printf("using $__lldb_local_vars::%s;\n", var_name.AsCString());
+ }
+}
+
+bool ClangExpressionSourceCode::GetText(
+ std::string &text, lldb::LanguageType wrapping_language, bool static_method,
+ ExecutionContext &exe_ctx, bool add_locals, bool force_add_all_locals,
+ llvm::ArrayRef<std::string> modules) const {
+ const char *target_specific_defines = "typedef signed char BOOL;\n";
+ std::string module_macros;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ if (target->GetArchitecture().GetMachine() == llvm::Triple::aarch64) {
+ target_specific_defines = "typedef bool BOOL;\n";
+ }
+ if (target->GetArchitecture().GetMachine() == llvm::Triple::x86_64) {
+ if (lldb::PlatformSP platform_sp = target->GetPlatform()) {
+ static ConstString g_platform_ios_simulator("ios-simulator");
+ if (platform_sp->GetPluginName() == g_platform_ios_simulator) {
+ target_specific_defines = "typedef bool BOOL;\n";
+ }
+ }
+ }
+
+ if (ClangModulesDeclVendor *decl_vendor =
+ target->GetClangModulesDeclVendor()) {
+ ClangPersistentVariables *persistent_vars =
+ llvm::cast<ClangPersistentVariables>(
+ target->GetPersistentExpressionStateForLanguage(
+ lldb::eLanguageTypeC));
+ const ClangModulesDeclVendor::ModuleVector &hand_imported_modules =
+ persistent_vars->GetHandLoadedClangModules();
+ ClangModulesDeclVendor::ModuleVector modules_for_macros;
+
+ for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) {
+ modules_for_macros.push_back(module);
+ }
+
+ if (target->GetEnableAutoImportClangModules()) {
+ if (StackFrame *frame = exe_ctx.GetFramePtr()) {
+ if (Block *block = frame->GetFrameBlock()) {
+ SymbolContext sc;
+
+ block->CalculateSymbolContext(&sc);
+
+ if (sc.comp_unit) {
+ StreamString error_stream;
+
+ decl_vendor->AddModulesForCompileUnit(
+ *sc.comp_unit, modules_for_macros, error_stream);
+ }
+ }
+ }
+ }
+
+ decl_vendor->ForEachMacro(
+ modules_for_macros,
+ [&module_macros](const std::string &expansion) -> bool {
+ module_macros.append(expansion);
+ module_macros.append("\n");
+ return false;
+ });
+ }
+ }
+
+ StreamString debug_macros_stream;
+ StreamString lldb_local_var_decls;
+ if (StackFrame *frame = exe_ctx.GetFramePtr()) {
+ const SymbolContext &sc = frame->GetSymbolContext(
+ lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry);
+
+ if (sc.comp_unit && sc.line_entry.IsValid()) {
+ DebugMacros *dm = sc.comp_unit->GetDebugMacros();
+ if (dm) {
+ AddMacroState state(sc.line_entry.file, sc.line_entry.line);
+ AddMacros(dm, sc.comp_unit, state, debug_macros_stream);
+ }
+ }
+
+ if (add_locals)
+ if (target->GetInjectLocalVariables(&exe_ctx)) {
+ lldb::VariableListSP var_list_sp =
+ frame->GetInScopeVariableList(false, true);
+ AddLocalVariableDecls(var_list_sp, lldb_local_var_decls,
+ force_add_all_locals ? "" : m_body,
+ wrapping_language);
+ }
+ }
+
+ if (m_wrap) {
+ switch (wrapping_language) {
+ default:
+ return false;
+ case lldb::eLanguageTypeC:
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeObjC:
+ break;
+ }
+
+ // Generate a list of @import statements that will import the specified
+ // module into our expression.
+ std::string module_imports;
+ for (const std::string &module : modules) {
+ module_imports.append("@import ");
+ module_imports.append(module);
+ module_imports.append(";\n");
+ }
+
+ StreamString wrap_stream;
+
+ wrap_stream.Printf("%s\n%s\n%s\n%s\n%s\n", module_macros.c_str(),
+ debug_macros_stream.GetData(), g_expression_prefix,
+ target_specific_defines, m_prefix.c_str());
+
+ // First construct a tagged form of the user expression so we can find it
+ // later:
+ std::string tagged_body;
+ switch (wrapping_language) {
+ default:
+ tagged_body = m_body;
+ break;
+ case lldb::eLanguageTypeC:
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeObjC:
+ tagged_body.append(c_start_marker);
+ tagged_body.append(m_body);
+ tagged_body.append(c_end_marker);
+ break;
+ }
+ switch (wrapping_language) {
+ default:
+ break;
+ case lldb::eLanguageTypeC:
+ wrap_stream.Printf("%s"
+ "void \n"
+ "%s(void *$__lldb_arg) \n"
+ "{ \n"
+ " %s; \n"
+ "%s"
+ "} \n",
+ module_imports.c_str(), m_name.c_str(),
+ lldb_local_var_decls.GetData(), tagged_body.c_str());
+ break;
+ case lldb::eLanguageTypeC_plus_plus:
+ wrap_stream.Printf("%s"
+ "void \n"
+ "$__lldb_class::%s(void *$__lldb_arg) \n"
+ "{ \n"
+ " %s; \n"
+ "%s"
+ "} \n",
+ module_imports.c_str(), m_name.c_str(),
+ lldb_local_var_decls.GetData(), tagged_body.c_str());
+ break;
+ case lldb::eLanguageTypeObjC:
+ if (static_method) {
+ wrap_stream.Printf(
+ "%s"
+ "@interface $__lldb_objc_class ($__lldb_category) \n"
+ "+(void)%s:(void *)$__lldb_arg; \n"
+ "@end \n"
+ "@implementation $__lldb_objc_class ($__lldb_category) \n"
+ "+(void)%s:(void *)$__lldb_arg \n"
+ "{ \n"
+ " %s; \n"
+ "%s"
+ "} \n"
+ "@end \n",
+ module_imports.c_str(), m_name.c_str(), m_name.c_str(),
+ lldb_local_var_decls.GetData(), tagged_body.c_str());
+ } else {
+ wrap_stream.Printf(
+ "%s"
+ "@interface $__lldb_objc_class ($__lldb_category) \n"
+ "-(void)%s:(void *)$__lldb_arg; \n"
+ "@end \n"
+ "@implementation $__lldb_objc_class ($__lldb_category) \n"
+ "-(void)%s:(void *)$__lldb_arg \n"
+ "{ \n"
+ " %s; \n"
+ "%s"
+ "} \n"
+ "@end \n",
+ module_imports.c_str(), m_name.c_str(), m_name.c_str(),
+ lldb_local_var_decls.GetData(), tagged_body.c_str());
+ }
+ break;
+ }
+
+ text = wrap_stream.GetString();
+ } else {
+ text.append(m_body);
+ }
+
+ return true;
+}
+
+bool ClangExpressionSourceCode::GetOriginalBodyBounds(
+ std::string transformed_text, lldb::LanguageType wrapping_language,
+ size_t &start_loc, size_t &end_loc) {
+ const char *start_marker;
+ const char *end_marker;
+
+ switch (wrapping_language) {
+ default:
+ return false;
+ case lldb::eLanguageTypeC:
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeObjC:
+ start_marker = c_start_marker;
+ end_marker = c_end_marker;
+ break;
+ }
+
+ start_loc = transformed_text.find(start_marker);
+ if (start_loc == std::string::npos)
+ return false;
+ start_loc += strlen(start_marker);
+ end_loc = transformed_text.find(end_marker);
+ return end_loc != std::string::npos;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
new file mode 100644
index 000000000000..894290295837
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
@@ -0,0 +1,71 @@
+//===-- ClangExpressionSourceCode.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangExpressionSourceCode_h
+#define liblldb_ClangExpressionSourceCode_h
+
+#include "lldb/Expression/Expression.h"
+#include "lldb/Expression/ExpressionSourceCode.h"
+#include "lldb/lldb-enumerations.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <string>
+
+namespace lldb_private {
+
+class ExecutionContext;
+
+class ClangExpressionSourceCode : public ExpressionSourceCode {
+public:
+ static const char *g_expression_prefix;
+
+ static ClangExpressionSourceCode *CreateWrapped(const char *prefix,
+ const char *body) {
+ return new ClangExpressionSourceCode("$__lldb_expr", prefix, body, true);
+ }
+
+ uint32_t GetNumBodyLines();
+
+ /// Generates the source code that will evaluate the expression.
+ ///
+ /// \param text output parameter containing the source code string.
+ /// \param wrapping_language If the expression is supossed to be wrapped,
+ /// then this is the language that should be used for that.
+ /// \param static_method True iff the expression is valuated inside a static
+ /// Objective-C method.
+ /// \param exe_ctx The execution context in which the expression will be
+ /// evaluated.
+ /// \param add_locals True iff local variables should be injected into the
+ /// expression source code.
+ /// \param force_add_all_locals True iff all local variables should be
+ /// injected even if they are not used in the expression.
+ /// \param modules A list of (C++) modules that the expression should import.
+ ///
+ /// \return true iff the source code was successfully generated.
+ bool GetText(std::string &text, lldb::LanguageType wrapping_language,
+ bool static_method, ExecutionContext &exe_ctx, bool add_locals,
+ bool force_add_all_locals,
+ llvm::ArrayRef<std::string> modules) const;
+
+ // Given a string returned by GetText, find the beginning and end of the body
+ // passed to CreateWrapped. Return true if the bounds could be found. This
+ // will also work on text with FixItHints applied.
+ static bool GetOriginalBodyBounds(std::string transformed_text,
+ lldb::LanguageType wrapping_language,
+ size_t &start_loc, size_t &end_loc);
+
+protected:
+ ClangExpressionSourceCode(const char *name, const char *prefix, const char *body,
+ bool wrap) :
+ ExpressionSourceCode(name, prefix, body, wrap) {}
+};
+
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp
new file mode 100644
index 000000000000..b5a2c80b5349
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp
@@ -0,0 +1,66 @@
+//===-- ClangExpressionVariable.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangExpressionVariable.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Stream.h"
+#include "clang/AST/ASTContext.h"
+
+using namespace lldb_private;
+using namespace clang;
+
+ClangExpressionVariable::ClangExpressionVariable(
+ ExecutionContextScope *exe_scope, lldb::ByteOrder byte_order,
+ uint32_t addr_byte_size)
+ : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(),
+ m_jit_vars() {
+ m_flags = EVNone;
+ m_frozen_sp =
+ ValueObjectConstResult::Create(exe_scope, byte_order, addr_byte_size);
+}
+
+ClangExpressionVariable::ClangExpressionVariable(
+ ExecutionContextScope *exe_scope, Value &value, ConstString name,
+ uint16_t flags)
+ : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(),
+ m_jit_vars() {
+ m_flags = flags;
+ m_frozen_sp = ValueObjectConstResult::Create(exe_scope, value, name);
+}
+
+ClangExpressionVariable::ClangExpressionVariable(
+ const lldb::ValueObjectSP &valobj_sp)
+ : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(),
+ m_jit_vars() {
+ m_flags = EVNone;
+ m_frozen_sp = valobj_sp;
+}
+
+ClangExpressionVariable::ClangExpressionVariable(
+ ExecutionContextScope *exe_scope, ConstString name,
+ const TypeFromUser &user_type, lldb::ByteOrder byte_order,
+ uint32_t addr_byte_size)
+ : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(),
+ m_jit_vars() {
+ m_flags = EVNone;
+ m_frozen_sp =
+ ValueObjectConstResult::Create(exe_scope, byte_order, addr_byte_size);
+ SetName(name);
+ SetCompilerType(user_type);
+}
+
+TypeFromUser ClangExpressionVariable::GetTypeFromUser() {
+ TypeFromUser tfu(m_frozen_sp->GetCompilerType());
+ return tfu;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h
new file mode 100644
index 000000000000..eb7f74f20a20
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h
@@ -0,0 +1,207 @@
+//===-- ClangExpressionVariable.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangExpressionVariable_h_
+#define liblldb_ClangExpressionVariable_h_
+
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "llvm/Support/Casting.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Symbol/TaggedASTType.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-public.h"
+
+namespace llvm {
+class Value;
+}
+
+namespace lldb_private {
+
+class ValueObjectConstResult;
+
+/// \class ClangExpressionVariable ClangExpressionVariable.h
+/// "lldb/Expression/ClangExpressionVariable.h" Encapsulates one variable for
+/// the expression parser.
+///
+/// The expression parser uses variables in three different contexts:
+///
+/// First, it stores persistent variables along with the process for use in
+/// expressions. These persistent variables contain their own data and are
+/// typed.
+///
+/// Second, in an interpreted expression, it stores the local variables for
+/// the expression along with the expression. These variables contain their
+/// own data and are typed.
+///
+/// Third, in a JIT-compiled expression, it stores the variables that the
+/// expression needs to have materialized and dematerialized at each
+/// execution. These do not contain their own data but are named and typed.
+///
+/// This class supports all of these use cases using simple type polymorphism,
+/// and provides necessary support methods. Its interface is RTTI-neutral.
+class ClangExpressionVariable : public ExpressionVariable {
+public:
+ ClangExpressionVariable(ExecutionContextScope *exe_scope,
+ lldb::ByteOrder byte_order, uint32_t addr_byte_size);
+
+ ClangExpressionVariable(ExecutionContextScope *exe_scope, Value &value,
+ ConstString name, uint16_t flags = EVNone);
+
+ ClangExpressionVariable(const lldb::ValueObjectSP &valobj_sp);
+
+ ClangExpressionVariable(ExecutionContextScope *exe_scope,
+ ConstString name,
+ const TypeFromUser &user_type,
+ lldb::ByteOrder byte_order, uint32_t addr_byte_size);
+
+ /// Utility functions for dealing with ExpressionVariableLists in Clang-
+ /// specific ways
+
+ /// Finds a variable by NamedDecl in the list.
+ ///
+ /// \param[in] name
+ /// The name of the requested variable.
+ ///
+ /// \return
+ /// The variable requested, or NULL if that variable is not in the list.
+ static ClangExpressionVariable *
+ FindVariableInList(ExpressionVariableList &list, const clang::NamedDecl *decl,
+ uint64_t parser_id) {
+ lldb::ExpressionVariableSP var_sp;
+ for (size_t index = 0, size = list.GetSize(); index < size; ++index) {
+ var_sp = list.GetVariableAtIndex(index);
+
+ if (ClangExpressionVariable *clang_var =
+ llvm::dyn_cast<ClangExpressionVariable>(var_sp.get())) {
+ ClangExpressionVariable::ParserVars *parser_vars =
+ clang_var->GetParserVars(parser_id);
+
+ if (parser_vars && parser_vars->m_named_decl == decl)
+ return clang_var;
+ }
+ }
+ return nullptr;
+ }
+
+ /// If the variable contains its own data, make a Value point at it. If \a
+ /// exe_ctx in not NULL, the value will be resolved in with that execution
+ /// context.
+ ///
+ /// \param[in] value
+ /// The value to point at the data.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to use to resolve \a value.
+ ///
+ /// \return
+ /// True on success; false otherwise (in particular, if this variable
+ /// does not contain its own data).
+ bool PointValueAtData(Value &value, ExecutionContext *exe_ctx);
+
+ /// The following values should not live beyond parsing
+ class ParserVars {
+ public:
+ ParserVars()
+ : m_parser_type(), m_named_decl(nullptr), m_llvm_value(nullptr),
+ m_lldb_value(), m_lldb_var(), m_lldb_sym(nullptr) {}
+
+ TypeFromParser
+ m_parser_type; ///< The type of the variable according to the parser
+ const clang::NamedDecl
+ *m_named_decl; ///< The Decl corresponding to this variable
+ llvm::Value *m_llvm_value; ///< The IR value corresponding to this variable;
+ ///usually a GlobalValue
+ lldb_private::Value
+ m_lldb_value; ///< The value found in LLDB for this variable
+ lldb::VariableSP m_lldb_var; ///< The original variable for this variable
+ const lldb_private::Symbol *m_lldb_sym; ///< The original symbol for this
+ ///variable, if it was a symbol
+ };
+
+private:
+ typedef std::map<uint64_t, ParserVars> ParserVarMap;
+ ParserVarMap m_parser_vars;
+
+public:
+ /// Make this variable usable by the parser by allocating space for parser-
+ /// specific variables
+ void EnableParserVars(uint64_t parser_id) {
+ m_parser_vars.insert(std::make_pair(parser_id, ParserVars()));
+ }
+
+ /// Deallocate parser-specific variables
+ void DisableParserVars(uint64_t parser_id) { m_parser_vars.erase(parser_id); }
+
+ /// Access parser-specific variables
+ ParserVars *GetParserVars(uint64_t parser_id) {
+ ParserVarMap::iterator i = m_parser_vars.find(parser_id);
+
+ if (i == m_parser_vars.end())
+ return nullptr;
+ else
+ return &i->second;
+ }
+
+ /// The following values are valid if the variable is used by JIT code
+ struct JITVars {
+ JITVars() : m_alignment(0), m_size(0), m_offset(0) {}
+
+ lldb::offset_t
+ m_alignment; ///< The required alignment of the variable, in bytes
+ size_t m_size; ///< The space required for the variable, in bytes
+ lldb::offset_t
+ m_offset; ///< The offset of the variable in the struct, in bytes
+ };
+
+private:
+ typedef std::map<uint64_t, JITVars> JITVarMap;
+ JITVarMap m_jit_vars;
+
+public:
+ /// Make this variable usable for materializing for the JIT by allocating
+ /// space for JIT-specific variables
+ void EnableJITVars(uint64_t parser_id) {
+ m_jit_vars.insert(std::make_pair(parser_id, JITVars()));
+ }
+
+ /// Deallocate JIT-specific variables
+ void DisableJITVars(uint64_t parser_id) { m_jit_vars.erase(parser_id); }
+
+ JITVars *GetJITVars(uint64_t parser_id) {
+ JITVarMap::iterator i = m_jit_vars.find(parser_id);
+
+ if (i == m_jit_vars.end())
+ return nullptr;
+ else
+ return &i->second;
+ }
+
+ TypeFromUser GetTypeFromUser();
+
+ // llvm casting support
+ static bool classof(const ExpressionVariable *ev) {
+ return ev->getKind() == ExpressionVariable::eKindClang;
+ }
+
+ /// Members
+ DISALLOW_COPY_AND_ASSIGN(ClangExpressionVariable);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangExpressionVariable_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp
new file mode 100644
index 000000000000..eabc96aa8e51
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp
@@ -0,0 +1,215 @@
+//===-- ClangFunctionCaller.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangFunctionCaller.h"
+
+#include "ASTStructExtractor.h"
+#include "ClangExpressionParser.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/CodeGen/CodeGenAction.h"
+#include "clang/CodeGen/ModuleBuilder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ExecutionEngine/ExecutionEngine.h"
+#include "llvm/IR/Module.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectList.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb_private;
+
+// ClangFunctionCaller constructor
+ClangFunctionCaller::ClangFunctionCaller(ExecutionContextScope &exe_scope,
+ const CompilerType &return_type,
+ const Address &functionAddress,
+ const ValueList &arg_value_list,
+ const char *name)
+ : FunctionCaller(exe_scope, return_type, functionAddress, arg_value_list,
+ name),
+ m_type_system_helper(*this) {
+ m_jit_process_wp = lldb::ProcessWP(exe_scope.CalculateProcess());
+ // Can't make a ClangFunctionCaller without a process.
+ assert(m_jit_process_wp.lock());
+}
+
+// Destructor
+ClangFunctionCaller::~ClangFunctionCaller() {}
+
+unsigned
+
+ClangFunctionCaller::CompileFunction(lldb::ThreadSP thread_to_use_sp,
+ DiagnosticManager &diagnostic_manager) {
+ if (m_compiled)
+ return 0;
+
+ // Compilation might call code, make sure to keep on the thread the caller
+ // indicated.
+ ThreadList::ExpressionExecutionThreadPusher execution_thread_pusher(
+ thread_to_use_sp);
+
+ // FIXME: How does clang tell us there's no return value? We need to handle
+ // that case.
+ unsigned num_errors = 0;
+
+ std::string return_type_str(
+ m_function_return_type.GetTypeName().AsCString(""));
+
+ // Cons up the function we're going to wrap our call in, then compile it...
+ // We declare the function "extern "C"" because the compiler might be in C++
+ // mode which would mangle the name and then we couldn't find it again...
+ m_wrapper_function_text.clear();
+ m_wrapper_function_text.append("extern \"C\" void ");
+ m_wrapper_function_text.append(m_wrapper_function_name);
+ m_wrapper_function_text.append(" (void *input)\n{\n struct ");
+ m_wrapper_function_text.append(m_wrapper_struct_name);
+ m_wrapper_function_text.append(" \n {\n");
+ m_wrapper_function_text.append(" ");
+ m_wrapper_function_text.append(return_type_str);
+ m_wrapper_function_text.append(" (*fn_ptr) (");
+
+ // Get the number of arguments. If we have a function type and it is
+ // prototyped, trust that, otherwise use the values we were given.
+
+ // FIXME: This will need to be extended to handle Variadic functions. We'll
+ // need
+ // to pull the defined arguments out of the function, then add the types from
+ // the arguments list for the variable arguments.
+
+ uint32_t num_args = UINT32_MAX;
+ bool trust_function = false;
+ // GetArgumentCount returns -1 for an unprototyped function.
+ CompilerType function_clang_type;
+ if (m_function_ptr) {
+ function_clang_type = m_function_ptr->GetCompilerType();
+ if (function_clang_type) {
+ int num_func_args = function_clang_type.GetFunctionArgumentCount();
+ if (num_func_args >= 0) {
+ trust_function = true;
+ num_args = num_func_args;
+ }
+ }
+ }
+
+ if (num_args == UINT32_MAX)
+ num_args = m_arg_values.GetSize();
+
+ std::string args_buffer; // This one stores the definition of all the args in
+ // "struct caller".
+ std::string args_list_buffer; // This one stores the argument list called from
+ // the structure.
+ for (size_t i = 0; i < num_args; i++) {
+ std::string type_name;
+
+ if (trust_function) {
+ type_name = function_clang_type.GetFunctionArgumentTypeAtIndex(i)
+ .GetTypeName()
+ .AsCString("");
+ } else {
+ CompilerType clang_qual_type =
+ m_arg_values.GetValueAtIndex(i)->GetCompilerType();
+ if (clang_qual_type) {
+ type_name = clang_qual_type.GetTypeName().AsCString("");
+ } else {
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityError,
+ "Could not determine type of input value %" PRIu64 ".",
+ (uint64_t)i);
+ return 1;
+ }
+ }
+
+ m_wrapper_function_text.append(type_name);
+ if (i < num_args - 1)
+ m_wrapper_function_text.append(", ");
+
+ char arg_buf[32];
+ args_buffer.append(" ");
+ args_buffer.append(type_name);
+ snprintf(arg_buf, 31, "arg_%" PRIu64, (uint64_t)i);
+ args_buffer.push_back(' ');
+ args_buffer.append(arg_buf);
+ args_buffer.append(";\n");
+
+ args_list_buffer.append("__lldb_fn_data->");
+ args_list_buffer.append(arg_buf);
+ if (i < num_args - 1)
+ args_list_buffer.append(", ");
+ }
+ m_wrapper_function_text.append(
+ ");\n"); // Close off the function calling prototype.
+
+ m_wrapper_function_text.append(args_buffer);
+
+ m_wrapper_function_text.append(" ");
+ m_wrapper_function_text.append(return_type_str);
+ m_wrapper_function_text.append(" return_value;");
+ m_wrapper_function_text.append("\n };\n struct ");
+ m_wrapper_function_text.append(m_wrapper_struct_name);
+ m_wrapper_function_text.append("* __lldb_fn_data = (struct ");
+ m_wrapper_function_text.append(m_wrapper_struct_name);
+ m_wrapper_function_text.append(" *) input;\n");
+
+ m_wrapper_function_text.append(
+ " __lldb_fn_data->return_value = __lldb_fn_data->fn_ptr (");
+ m_wrapper_function_text.append(args_list_buffer);
+ m_wrapper_function_text.append(");\n}\n");
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+ if (log)
+ log->Printf("Expression: \n\n%s\n\n", m_wrapper_function_text.c_str());
+
+ // Okay, now compile this expression
+
+ lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock());
+ if (jit_process_sp) {
+ const bool generate_debug_info = true;
+ m_parser.reset(new ClangExpressionParser(jit_process_sp.get(), *this,
+ generate_debug_info));
+
+ num_errors = m_parser->Parse(diagnostic_manager);
+ } else {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "no process - unable to inject function");
+ num_errors = 1;
+ }
+
+ m_compiled = (num_errors == 0);
+
+ if (!m_compiled)
+ return num_errors;
+
+ return num_errors;
+}
+
+clang::ASTConsumer *
+ClangFunctionCaller::ClangFunctionCallerHelper::ASTTransformer(
+ clang::ASTConsumer *passthrough) {
+ m_struct_extractor.reset(new ASTStructExtractor(
+ passthrough, m_owner.GetWrapperStructName(), m_owner));
+
+ return m_struct_extractor.get();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.h
new file mode 100644
index 000000000000..24f6f2eb91b3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.h
@@ -0,0 +1,153 @@
+//===-- ClangFunctionCaller.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangFunctionCaller_h_
+#define liblldb_ClangFunctionCaller_h_
+
+#include "ClangExpressionHelper.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectList.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/Process.h"
+
+namespace lldb_private {
+
+class ASTStructExtractor;
+class ClangExpressionParser;
+
+/// \class ClangFunctionCaller ClangFunctionCaller.h
+/// "lldb/Expression/ClangFunctionCaller.h" Encapsulates a function that can
+/// be called.
+///
+/// A given ClangFunctionCaller object can handle a single function signature.
+/// Once constructed, it can set up any number of concurrent calls to
+/// functions with that signature.
+///
+/// It performs the call by synthesizing a structure that contains the pointer
+/// to the function and the arguments that should be passed to that function,
+/// and producing a special-purpose JIT-compiled function that accepts a void*
+/// pointing to this struct as its only argument and calls the function in the
+/// struct with the written arguments. This method lets Clang handle the
+/// vagaries of function calling conventions.
+///
+/// The simplest use of the ClangFunctionCaller is to construct it with a
+/// function representative of the signature you want to use, then call
+/// ExecuteFunction(ExecutionContext &, Stream &, Value &).
+///
+/// If you need to reuse the arguments for several calls, you can call
+/// InsertFunction() followed by WriteFunctionArguments(), which will return
+/// the location of the args struct for the wrapper function in args_addr_ref.
+///
+/// If you need to call the function on the thread plan stack, you can also
+/// call InsertFunction() followed by GetThreadPlanToCallFunction().
+///
+/// Any of the methods that take arg_addr_ptr or arg_addr_ref can be passed a
+/// pointer set to LLDB_INVALID_ADDRESS and new structure will be allocated
+/// and its address returned in that variable.
+///
+/// Any of the methods that take arg_addr_ptr can be passed NULL, and the
+/// argument space will be managed for you.
+class ClangFunctionCaller : public FunctionCaller {
+ friend class ASTStructExtractor;
+
+ /// LLVM-style RTTI support.
+ static bool classof(const Expression *E) {
+ return E->getKind() == eKindClangFunctionCaller;
+ }
+
+ class ClangFunctionCallerHelper : public ClangExpressionHelper {
+ public:
+ ClangFunctionCallerHelper(ClangFunctionCaller &owner) : m_owner(owner) {}
+
+ ~ClangFunctionCallerHelper() override = default;
+
+ /// Return the object that the parser should use when resolving external
+ /// values. May be NULL if everything should be self-contained.
+ ClangExpressionDeclMap *DeclMap() override { return nullptr; }
+
+ /// Return the object that the parser should allow to access ASTs. May be
+ /// NULL if the ASTs do not need to be transformed.
+ ///
+ /// \param[in] passthrough
+ /// The ASTConsumer that the returned transformer should send
+ /// the ASTs to after transformation.
+ clang::ASTConsumer *
+ ASTTransformer(clang::ASTConsumer *passthrough) override;
+
+ private:
+ ClangFunctionCaller &m_owner;
+ std::unique_ptr<ASTStructExtractor> m_struct_extractor; ///< The class that
+ ///generates the
+ ///argument struct
+ ///layout.
+ };
+
+public:
+ /// Constructor
+ ///
+ /// \param[in] exe_scope
+ /// An execution context scope that gets us at least a target and
+ /// process.
+ ///
+ /// \param[in] ast_context
+ /// The AST context to evaluate argument types in.
+ ///
+ /// \param[in] return_qualtype
+ /// An opaque Clang QualType for the function result. Should be
+ /// defined in ast_context.
+ ///
+ /// \param[in] function_address
+ /// The address of the function to call.
+ ///
+ /// \param[in] arg_value_list
+ /// The default values to use when calling this function. Can
+ /// be overridden using WriteFunctionArguments().
+ ClangFunctionCaller(ExecutionContextScope &exe_scope,
+ const CompilerType &return_type,
+ const Address &function_address,
+ const ValueList &arg_value_list, const char *name);
+
+ ~ClangFunctionCaller() override;
+
+ /// Compile the wrapper function
+ ///
+ /// \param[in] thread_to_use_sp
+ /// Compilation might end up calling functions. Pass in the thread you
+ /// want the compilation to use. If you pass in an empty ThreadSP it will
+ /// use the currently selected thread.
+ ///
+ /// \param[in] diagnostic_manager
+ /// The diagnostic manager to report parser errors to.
+ ///
+ /// \return
+ /// The number of errors.
+ unsigned CompileFunction(lldb::ThreadSP thread_to_use_sp,
+ DiagnosticManager &diagnostic_manager) override;
+
+ ExpressionTypeSystemHelper *GetTypeSystemHelper() override {
+ return &m_type_system_helper;
+ }
+
+protected:
+ const char *GetWrapperStructName() { return m_wrapper_struct_name.c_str(); }
+
+private:
+ // For ClangFunctionCaller only
+
+ // Note: the parser needs to be destructed before the execution unit, so
+ // declare the execution unit first.
+ ClangFunctionCallerHelper m_type_system_helper;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangFunctionCaller_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp
new file mode 100644
index 000000000000..65c547391831
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp
@@ -0,0 +1,168 @@
+//===-- ClangHost.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangHost.h"
+
+#include "clang/Basic/Version.h"
+#include "clang/Config/config.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+
+#include <string>
+
+using namespace lldb_private;
+
+static bool VerifyClangPath(const llvm::Twine &clang_path) {
+ if (FileSystem::Instance().IsDirectory(clang_path))
+ return true;
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ if (log)
+ log->Printf("VerifyClangPath(): "
+ "failed to stat clang resource directory at \"%s\"",
+ clang_path.str().c_str());
+ return false;
+}
+
+///
+/// This will compute the clang resource directory assuming that clang was
+/// installed with the same prefix as lldb.
+///
+/// If verify is true, the first candidate resource directory will be returned.
+/// This mode is only used for testing.
+///
+static bool DefaultComputeClangResourceDirectory(FileSpec &lldb_shlib_spec,
+ FileSpec &file_spec,
+ bool verify) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ std::string raw_path = lldb_shlib_spec.GetPath();
+ llvm::StringRef parent_dir = llvm::sys::path::parent_path(raw_path);
+
+ static const llvm::StringRef kResourceDirSuffixes[] = {
+ // LLVM.org's build of LLDB uses the clang resource directory placed
+ // in $install_dir/lib{,64}/clang/$clang_version.
+ "lib" CLANG_LIBDIR_SUFFIX "/clang/" CLANG_VERSION_STRING,
+ // swift-lldb uses the clang resource directory copied from swift, which
+ // by default is placed in $install_dir/lib{,64}/lldb/clang. LLDB places
+ // it there, so we use LLDB_LIBDIR_SUFFIX.
+ "lib" LLDB_LIBDIR_SUFFIX "/lldb/clang",
+ };
+
+ for (const auto &Suffix : kResourceDirSuffixes) {
+ llvm::SmallString<256> clang_dir(parent_dir);
+ llvm::SmallString<32> relative_path(Suffix);
+ llvm::sys::path::native(relative_path);
+ llvm::sys::path::append(clang_dir, relative_path);
+ if (!verify || VerifyClangPath(clang_dir)) {
+ if (log)
+ log->Printf("DefaultComputeClangResourceDir: Setting ClangResourceDir "
+ "to \"%s\", verify = %s",
+ clang_dir.str().str().c_str(), verify ? "true" : "false");
+ file_spec.GetDirectory().SetString(clang_dir);
+ FileSystem::Instance().Resolve(file_spec);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::ComputeClangResourceDirectory(FileSpec &lldb_shlib_spec,
+ FileSpec &file_spec, bool verify) {
+#if !defined(__APPLE__)
+ return DefaultComputeClangResourceDirectory(lldb_shlib_spec, file_spec,
+ verify);
+#else
+ std::string raw_path = lldb_shlib_spec.GetPath();
+
+ auto rev_it = llvm::sys::path::rbegin(raw_path);
+ auto r_end = llvm::sys::path::rend(raw_path);
+
+ // Check for a Posix-style build of LLDB.
+ while (rev_it != r_end) {
+ if (*rev_it == "LLDB.framework")
+ break;
+ ++rev_it;
+ }
+
+ // We found a non-framework build of LLDB
+ if (rev_it == r_end)
+ return DefaultComputeClangResourceDirectory(lldb_shlib_spec, file_spec,
+ verify);
+
+ // Inside Xcode and in Xcode toolchains LLDB is always in lockstep
+ // with the Swift compiler, so it can reuse its Clang resource
+ // directory. This allows LLDB and the Swift compiler to share the
+ // same Clang module cache.
+ llvm::SmallString<256> clang_path;
+ const char *swift_clang_resource_dir = "usr/lib/swift/clang";
+ auto parent = std::next(rev_it);
+ if (parent != r_end && *parent == "SharedFrameworks") {
+ // This is the top-level LLDB in the Xcode.app bundle.
+ // E.g., "Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A"
+ raw_path.resize(parent - r_end);
+ llvm::sys::path::append(clang_path, raw_path,
+ "Developer/Toolchains/XcodeDefault.xctoolchain",
+ swift_clang_resource_dir);
+ if (!verify || VerifyClangPath(clang_path)) {
+ file_spec.GetDirectory().SetString(clang_path.c_str());
+ FileSystem::Instance().Resolve(file_spec);
+ return true;
+ }
+ } else if (parent != r_end && *parent == "PrivateFrameworks" &&
+ std::distance(parent, r_end) > 2) {
+ ++parent;
+ ++parent;
+ if (*parent == "System") {
+ // This is LLDB inside an Xcode toolchain.
+ // E.g., "Xcode.app/Contents/Developer/Toolchains/" \
+ // "My.xctoolchain/System/Library/PrivateFrameworks/LLDB.framework"
+ raw_path.resize(parent - r_end);
+ llvm::sys::path::append(clang_path, raw_path, swift_clang_resource_dir);
+ if (!verify || VerifyClangPath(clang_path)) {
+ file_spec.GetDirectory().SetString(clang_path.c_str());
+ FileSystem::Instance().Resolve(file_spec);
+ return true;
+ }
+ raw_path = lldb_shlib_spec.GetPath();
+ }
+ raw_path.resize(rev_it - r_end);
+ } else {
+ raw_path.resize(rev_it - r_end);
+ }
+
+ // Fall back to the Clang resource directory inside the framework.
+ raw_path.append("LLDB.framework/Resources/Clang");
+ file_spec.GetDirectory().SetString(raw_path.c_str());
+ FileSystem::Instance().Resolve(file_spec);
+ return true;
+#endif // __APPLE__
+}
+
+FileSpec lldb_private::GetClangResourceDir() {
+ static FileSpec g_cached_resource_dir;
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ if (FileSpec lldb_file_spec = HostInfo::GetShlibDir())
+ ComputeClangResourceDirectory(lldb_file_spec, g_cached_resource_dir,
+ true);
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ if (log)
+ log->Printf("GetClangResourceDir() => '%s'",
+ g_cached_resource_dir.GetPath().c_str());
+ });
+ return g_cached_resource_dir;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.h
new file mode 100644
index 000000000000..9d49188178cd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.h
@@ -0,0 +1,23 @@
+//===-- ClangHost.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGHOST_H
+#define LLDB_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGHOST_H
+
+namespace lldb_private {
+
+class FileSpec;
+
+bool ComputeClangResourceDirectory(FileSpec &lldb_shlib_spec,
+ FileSpec &file_spec, bool verify);
+
+FileSpec GetClangResourceDir();
+
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp
new file mode 100644
index 000000000000..4a220790e50d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp
@@ -0,0 +1,716 @@
+//===-- ClangModulesDeclVendor.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <mutex>
+
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Serialization/ASTReader.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Threading.h"
+
+#include "ClangHost.h"
+#include "ClangModulesDeclVendor.h"
+#include "ModuleDependencyCollector.h"
+
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/SourceModule.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+
+namespace {
+// Any Clang compiler requires a consumer for diagnostics. This one stores
+// them as strings so we can provide them to the user in case a module failed
+// to load.
+class StoringDiagnosticConsumer : public clang::DiagnosticConsumer {
+public:
+ StoringDiagnosticConsumer();
+
+ void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel,
+ const clang::Diagnostic &info) override;
+
+ void ClearDiagnostics();
+
+ void DumpDiagnostics(Stream &error_stream);
+
+private:
+ typedef std::pair<clang::DiagnosticsEngine::Level, std::string>
+ IDAndDiagnostic;
+ std::vector<IDAndDiagnostic> m_diagnostics;
+ Log *m_log;
+};
+
+// The private implementation of our ClangModulesDeclVendor. Contains all the
+// Clang state required to load modules.
+class ClangModulesDeclVendorImpl : public ClangModulesDeclVendor {
+public:
+ ClangModulesDeclVendorImpl(
+ llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_engine,
+ std::shared_ptr<clang::CompilerInvocation> compiler_invocation,
+ std::unique_ptr<clang::CompilerInstance> compiler_instance,
+ std::unique_ptr<clang::Parser> parser);
+
+ ~ClangModulesDeclVendorImpl() override = default;
+
+ bool AddModule(const SourceModule &module, ModuleVector *exported_modules,
+ Stream &error_stream) override;
+
+ bool AddModulesForCompileUnit(CompileUnit &cu, ModuleVector &exported_modules,
+ Stream &error_stream) override;
+
+ uint32_t FindDecls(ConstString name, bool append, uint32_t max_matches,
+ std::vector<clang::NamedDecl *> &decls) override;
+
+ void ForEachMacro(const ModuleVector &modules,
+ std::function<bool(const std::string &)> handler) override;
+
+ clang::ExternalASTMerger::ImporterSource GetImporterSource() override;
+private:
+ void
+ ReportModuleExportsHelper(std::set<ClangModulesDeclVendor::ModuleID> &exports,
+ clang::Module *module);
+
+ void ReportModuleExports(ModuleVector &exports, clang::Module *module);
+
+ clang::ModuleLoadResult DoGetModule(clang::ModuleIdPath path,
+ bool make_visible);
+
+ bool m_enabled = false;
+
+ llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> m_diagnostics_engine;
+ std::shared_ptr<clang::CompilerInvocation> m_compiler_invocation;
+ std::unique_ptr<clang::CompilerInstance> m_compiler_instance;
+ std::unique_ptr<clang::Parser> m_parser;
+ size_t m_source_location_index =
+ 0; // used to give name components fake SourceLocations
+
+ typedef std::vector<ConstString> ImportedModule;
+ typedef std::map<ImportedModule, clang::Module *> ImportedModuleMap;
+ typedef std::set<ModuleID> ImportedModuleSet;
+ ImportedModuleMap m_imported_modules;
+ ImportedModuleSet m_user_imported_modules;
+ const clang::ExternalASTMerger::OriginMap m_origin_map;
+};
+} // anonymous namespace
+
+StoringDiagnosticConsumer::StoringDiagnosticConsumer() {
+ m_log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+}
+
+void StoringDiagnosticConsumer::HandleDiagnostic(
+ clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) {
+ llvm::SmallVector<char, 256> diagnostic_string;
+
+ info.FormatDiagnostic(diagnostic_string);
+
+ m_diagnostics.push_back(
+ IDAndDiagnostic(DiagLevel, std::string(diagnostic_string.data(),
+ diagnostic_string.size())));
+}
+
+void StoringDiagnosticConsumer::ClearDiagnostics() { m_diagnostics.clear(); }
+
+void StoringDiagnosticConsumer::DumpDiagnostics(Stream &error_stream) {
+ for (IDAndDiagnostic &diag : m_diagnostics) {
+ switch (diag.first) {
+ default:
+ error_stream.PutCString(diag.second);
+ error_stream.PutChar('\n');
+ break;
+ case clang::DiagnosticsEngine::Level::Ignored:
+ break;
+ }
+ }
+}
+
+ClangModulesDeclVendor::ClangModulesDeclVendor() {}
+
+ClangModulesDeclVendor::~ClangModulesDeclVendor() {}
+
+ClangModulesDeclVendorImpl::ClangModulesDeclVendorImpl(
+ llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_engine,
+ std::shared_ptr<clang::CompilerInvocation> compiler_invocation,
+ std::unique_ptr<clang::CompilerInstance> compiler_instance,
+ std::unique_ptr<clang::Parser> parser)
+ : m_diagnostics_engine(std::move(diagnostics_engine)),
+ m_compiler_invocation(std::move(compiler_invocation)),
+ m_compiler_instance(std::move(compiler_instance)),
+ m_parser(std::move(parser)), m_origin_map() {}
+
+void ClangModulesDeclVendorImpl::ReportModuleExportsHelper(
+ std::set<ClangModulesDeclVendor::ModuleID> &exports,
+ clang::Module *module) {
+ if (exports.count(reinterpret_cast<ClangModulesDeclVendor::ModuleID>(module)))
+ return;
+
+ exports.insert(reinterpret_cast<ClangModulesDeclVendor::ModuleID>(module));
+
+ llvm::SmallVector<clang::Module *, 2> sub_exports;
+
+ module->getExportedModules(sub_exports);
+
+ for (clang::Module *module : sub_exports) {
+ ReportModuleExportsHelper(exports, module);
+ }
+}
+
+void ClangModulesDeclVendorImpl::ReportModuleExports(
+ ClangModulesDeclVendor::ModuleVector &exports, clang::Module *module) {
+ std::set<ClangModulesDeclVendor::ModuleID> exports_set;
+
+ ReportModuleExportsHelper(exports_set, module);
+
+ for (ModuleID module : exports_set) {
+ exports.push_back(module);
+ }
+}
+
+bool ClangModulesDeclVendorImpl::AddModule(const SourceModule &module,
+ ModuleVector *exported_modules,
+ Stream &error_stream) {
+ // Fail early.
+
+ if (m_compiler_instance->hadModuleLoaderFatalFailure()) {
+ error_stream.PutCString("error: Couldn't load a module because the module "
+ "loader is in a fatal state.\n");
+ return false;
+ }
+
+ // Check if we've already imported this module.
+
+ std::vector<ConstString> imported_module;
+
+ for (ConstString path_component : module.path) {
+ imported_module.push_back(path_component);
+ }
+
+ {
+ ImportedModuleMap::iterator mi = m_imported_modules.find(imported_module);
+
+ if (mi != m_imported_modules.end()) {
+ if (exported_modules) {
+ ReportModuleExports(*exported_modules, mi->second);
+ }
+ return true;
+ }
+ }
+
+ clang::HeaderSearch &HS =
+ m_compiler_instance->getPreprocessor().getHeaderSearchInfo();
+
+ if (module.search_path) {
+ auto path_begin = llvm::sys::path::begin(module.search_path.GetStringRef());
+ auto path_end = llvm::sys::path::end(module.search_path.GetStringRef());
+ auto sysroot_begin = llvm::sys::path::begin(module.sysroot.GetStringRef());
+ auto sysroot_end = llvm::sys::path::end(module.sysroot.GetStringRef());
+ // FIXME: Use C++14 std::equal(it, it, it, it) variant once it's available.
+ bool is_system_module = (std::distance(path_begin, path_end) >=
+ std::distance(sysroot_begin, sysroot_end)) &&
+ std::equal(sysroot_begin, sysroot_end, path_begin);
+ // No need to inject search paths to modules in the sysroot.
+ if (!is_system_module) {
+ auto error = [&]() {
+ error_stream.Printf("error: No module map file in %s\n",
+ module.search_path.AsCString());
+ return false;
+ };
+
+ bool is_system = true;
+ bool is_framework = false;
+ auto *dir =
+ HS.getFileMgr().getDirectory(module.search_path.GetStringRef());
+ if (!dir)
+ return error();
+ auto *file = HS.lookupModuleMapFile(dir, is_framework);
+ if (!file)
+ return error();
+ if (!HS.loadModuleMapFile(file, is_system))
+ return error();
+ }
+ }
+ if (!HS.lookupModule(module.path.front().GetStringRef())) {
+ error_stream.Printf("error: Header search couldn't locate module %s\n",
+ module.path.front().AsCString());
+ return false;
+ }
+
+ llvm::SmallVector<std::pair<clang::IdentifierInfo *, clang::SourceLocation>,
+ 4>
+ clang_path;
+
+ {
+ clang::SourceManager &source_manager =
+ m_compiler_instance->getASTContext().getSourceManager();
+
+ for (ConstString path_component : module.path) {
+ clang_path.push_back(std::make_pair(
+ &m_compiler_instance->getASTContext().Idents.get(
+ path_component.GetStringRef()),
+ source_manager.getLocForStartOfFile(source_manager.getMainFileID())
+ .getLocWithOffset(m_source_location_index++)));
+ }
+ }
+
+ StoringDiagnosticConsumer *diagnostic_consumer =
+ static_cast<StoringDiagnosticConsumer *>(
+ m_compiler_instance->getDiagnostics().getClient());
+
+ diagnostic_consumer->ClearDiagnostics();
+
+ clang::Module *top_level_module = DoGetModule(clang_path.front(), false);
+
+ if (!top_level_module) {
+ diagnostic_consumer->DumpDiagnostics(error_stream);
+ error_stream.Printf("error: Couldn't load top-level module %s\n",
+ module.path.front().AsCString());
+ return false;
+ }
+
+ clang::Module *submodule = top_level_module;
+
+ for (auto &component : llvm::ArrayRef<ConstString>(module.path).drop_front()) {
+ submodule = submodule->findSubmodule(component.GetStringRef());
+ if (!submodule) {
+ diagnostic_consumer->DumpDiagnostics(error_stream);
+ error_stream.Printf("error: Couldn't load submodule %s\n",
+ component.GetCString());
+ return false;
+ }
+ }
+
+ clang::Module *requested_module = DoGetModule(clang_path, true);
+
+ if (requested_module != nullptr) {
+ if (exported_modules) {
+ ReportModuleExports(*exported_modules, requested_module);
+ }
+
+ m_imported_modules[imported_module] = requested_module;
+
+ m_enabled = true;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool ClangModulesDeclVendor::LanguageSupportsClangModules(
+ lldb::LanguageType language) {
+ switch (language) {
+ default:
+ return false;
+ case lldb::LanguageType::eLanguageTypeC:
+ case lldb::LanguageType::eLanguageTypeC11:
+ case lldb::LanguageType::eLanguageTypeC89:
+ case lldb::LanguageType::eLanguageTypeC99:
+ case lldb::LanguageType::eLanguageTypeC_plus_plus:
+ case lldb::LanguageType::eLanguageTypeC_plus_plus_03:
+ case lldb::LanguageType::eLanguageTypeC_plus_plus_11:
+ case lldb::LanguageType::eLanguageTypeC_plus_plus_14:
+ case lldb::LanguageType::eLanguageTypeObjC:
+ case lldb::LanguageType::eLanguageTypeObjC_plus_plus:
+ return true;
+ }
+}
+
+bool ClangModulesDeclVendorImpl::AddModulesForCompileUnit(
+ CompileUnit &cu, ClangModulesDeclVendor::ModuleVector &exported_modules,
+ Stream &error_stream) {
+ if (LanguageSupportsClangModules(cu.GetLanguage())) {
+ for (auto &imported_module : cu.GetImportedModules())
+ if (!AddModule(imported_module, &exported_modules, error_stream))
+ return false;
+ }
+ return true;
+}
+
+// ClangImporter::lookupValue
+
+uint32_t
+ClangModulesDeclVendorImpl::FindDecls(ConstString name, bool append,
+ uint32_t max_matches,
+ std::vector<clang::NamedDecl *> &decls) {
+ if (!m_enabled) {
+ return 0;
+ }
+
+ if (!append)
+ decls.clear();
+
+ clang::IdentifierInfo &ident =
+ m_compiler_instance->getASTContext().Idents.get(name.GetStringRef());
+
+ clang::LookupResult lookup_result(
+ m_compiler_instance->getSema(), clang::DeclarationName(&ident),
+ clang::SourceLocation(), clang::Sema::LookupOrdinaryName);
+
+ m_compiler_instance->getSema().LookupName(
+ lookup_result,
+ m_compiler_instance->getSema().getScopeForContext(
+ m_compiler_instance->getASTContext().getTranslationUnitDecl()));
+
+ uint32_t num_matches = 0;
+
+ for (clang::NamedDecl *named_decl : lookup_result) {
+ if (num_matches >= max_matches)
+ return num_matches;
+
+ decls.push_back(named_decl);
+ ++num_matches;
+ }
+
+ return num_matches;
+}
+
+void ClangModulesDeclVendorImpl::ForEachMacro(
+ const ClangModulesDeclVendor::ModuleVector &modules,
+ std::function<bool(const std::string &)> handler) {
+ if (!m_enabled) {
+ return;
+ }
+
+ typedef std::map<ModuleID, ssize_t> ModulePriorityMap;
+ ModulePriorityMap module_priorities;
+
+ ssize_t priority = 0;
+
+ for (ModuleID module : modules) {
+ module_priorities[module] = priority++;
+ }
+
+ if (m_compiler_instance->getPreprocessor().getExternalSource()) {
+ m_compiler_instance->getPreprocessor()
+ .getExternalSource()
+ ->ReadDefinedMacros();
+ }
+
+ for (clang::Preprocessor::macro_iterator
+ mi = m_compiler_instance->getPreprocessor().macro_begin(),
+ me = m_compiler_instance->getPreprocessor().macro_end();
+ mi != me; ++mi) {
+ const clang::IdentifierInfo *ii = nullptr;
+
+ {
+ if (clang::IdentifierInfoLookup *lookup =
+ m_compiler_instance->getPreprocessor()
+ .getIdentifierTable()
+ .getExternalIdentifierLookup()) {
+ lookup->get(mi->first->getName());
+ }
+ if (!ii) {
+ ii = mi->first;
+ }
+ }
+
+ ssize_t found_priority = -1;
+ clang::MacroInfo *macro_info = nullptr;
+
+ for (clang::ModuleMacro *module_macro :
+ m_compiler_instance->getPreprocessor().getLeafModuleMacros(ii)) {
+ clang::Module *module = module_macro->getOwningModule();
+
+ {
+ ModulePriorityMap::iterator pi =
+ module_priorities.find(reinterpret_cast<ModuleID>(module));
+
+ if (pi != module_priorities.end() && pi->second > found_priority) {
+ macro_info = module_macro->getMacroInfo();
+ found_priority = pi->second;
+ }
+ }
+
+ clang::Module *top_level_module = module->getTopLevelModule();
+
+ if (top_level_module != module) {
+ ModulePriorityMap::iterator pi = module_priorities.find(
+ reinterpret_cast<ModuleID>(top_level_module));
+
+ if ((pi != module_priorities.end()) && pi->second > found_priority) {
+ macro_info = module_macro->getMacroInfo();
+ found_priority = pi->second;
+ }
+ }
+ }
+
+ if (macro_info) {
+ std::string macro_expansion = "#define ";
+ macro_expansion.append(mi->first->getName().str());
+
+ {
+ if (macro_info->isFunctionLike()) {
+ macro_expansion.append("(");
+
+ bool first_arg = true;
+
+ for (auto pi = macro_info->param_begin(),
+ pe = macro_info->param_end();
+ pi != pe; ++pi) {
+ if (!first_arg) {
+ macro_expansion.append(", ");
+ } else {
+ first_arg = false;
+ }
+
+ macro_expansion.append((*pi)->getName().str());
+ }
+
+ if (macro_info->isC99Varargs()) {
+ if (first_arg) {
+ macro_expansion.append("...");
+ } else {
+ macro_expansion.append(", ...");
+ }
+ } else if (macro_info->isGNUVarargs()) {
+ macro_expansion.append("...");
+ }
+
+ macro_expansion.append(")");
+ }
+
+ macro_expansion.append(" ");
+
+ bool first_token = true;
+
+ for (clang::MacroInfo::tokens_iterator ti = macro_info->tokens_begin(),
+ te = macro_info->tokens_end();
+ ti != te; ++ti) {
+ if (!first_token) {
+ macro_expansion.append(" ");
+ } else {
+ first_token = false;
+ }
+
+ if (ti->isLiteral()) {
+ if (const char *literal_data = ti->getLiteralData()) {
+ std::string token_str(literal_data, ti->getLength());
+ macro_expansion.append(token_str);
+ } else {
+ bool invalid = false;
+ const char *literal_source =
+ m_compiler_instance->getSourceManager().getCharacterData(
+ ti->getLocation(), &invalid);
+
+ if (invalid) {
+ lldbassert(0 && "Unhandled token kind");
+ macro_expansion.append("<unknown literal value>");
+ } else {
+ macro_expansion.append(
+ std::string(literal_source, ti->getLength()));
+ }
+ }
+ } else if (const char *punctuator_spelling =
+ clang::tok::getPunctuatorSpelling(ti->getKind())) {
+ macro_expansion.append(punctuator_spelling);
+ } else if (const char *keyword_spelling =
+ clang::tok::getKeywordSpelling(ti->getKind())) {
+ macro_expansion.append(keyword_spelling);
+ } else {
+ switch (ti->getKind()) {
+ case clang::tok::TokenKind::identifier:
+ macro_expansion.append(ti->getIdentifierInfo()->getName().str());
+ break;
+ case clang::tok::TokenKind::raw_identifier:
+ macro_expansion.append(ti->getRawIdentifier().str());
+ break;
+ default:
+ macro_expansion.append(ti->getName());
+ break;
+ }
+ }
+ }
+
+ if (handler(macro_expansion)) {
+ return;
+ }
+ }
+ }
+ }
+}
+
+clang::ModuleLoadResult
+ClangModulesDeclVendorImpl::DoGetModule(clang::ModuleIdPath path,
+ bool make_visible) {
+ clang::Module::NameVisibilityKind visibility =
+ make_visible ? clang::Module::AllVisible : clang::Module::Hidden;
+
+ const bool is_inclusion_directive = false;
+
+ return m_compiler_instance->loadModule(path.front().second, path, visibility,
+ is_inclusion_directive);
+}
+
+clang::ExternalASTMerger::ImporterSource
+ClangModulesDeclVendorImpl::GetImporterSource() {
+ return {m_compiler_instance->getASTContext(),
+ m_compiler_instance->getFileManager(), m_origin_map};
+}
+
+static const char *ModuleImportBufferName = "LLDBModulesMemoryBuffer";
+
+lldb_private::ClangModulesDeclVendor *
+ClangModulesDeclVendor::Create(Target &target) {
+ // FIXME we should insure programmatically that the expression parser's
+ // compiler and the modules runtime's
+ // compiler are both initialized in the same way – preferably by the same
+ // code.
+
+ if (!target.GetPlatform()->SupportsModules())
+ return nullptr;
+
+ const ArchSpec &arch = target.GetArchitecture();
+
+ std::vector<std::string> compiler_invocation_arguments = {
+ "clang",
+ "-fmodules",
+ "-fimplicit-module-maps",
+ "-fcxx-modules",
+ "-fsyntax-only",
+ "-femit-all-decls",
+ "-target",
+ arch.GetTriple().str(),
+ "-fmodules-validate-system-headers",
+ "-Werror=non-modular-include-in-framework-module"};
+
+ target.GetPlatform()->AddClangModuleCompilationOptions(
+ &target, compiler_invocation_arguments);
+
+ compiler_invocation_arguments.push_back(ModuleImportBufferName);
+
+ // Add additional search paths with { "-I", path } or { "-F", path } here.
+
+ {
+ llvm::SmallString<128> path;
+ auto props = ModuleList::GetGlobalModuleListProperties();
+ props.GetClangModulesCachePath().GetPath(path);
+ std::string module_cache_argument("-fmodules-cache-path=");
+ module_cache_argument.append(path.str());
+ compiler_invocation_arguments.push_back(module_cache_argument);
+ }
+
+ FileSpecList module_search_paths = target.GetClangModuleSearchPaths();
+
+ for (size_t spi = 0, spe = module_search_paths.GetSize(); spi < spe; ++spi) {
+ const FileSpec &search_path = module_search_paths.GetFileSpecAtIndex(spi);
+
+ std::string search_path_argument = "-I";
+ search_path_argument.append(search_path.GetPath());
+
+ compiler_invocation_arguments.push_back(search_path_argument);
+ }
+
+ {
+ FileSpec clang_resource_dir = GetClangResourceDir();
+
+ if (FileSystem::Instance().IsDirectory(clang_resource_dir.GetPath())) {
+ compiler_invocation_arguments.push_back("-resource-dir");
+ compiler_invocation_arguments.push_back(clang_resource_dir.GetPath());
+ }
+ }
+
+ llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_engine =
+ clang::CompilerInstance::createDiagnostics(new clang::DiagnosticOptions,
+ new StoringDiagnosticConsumer);
+
+ std::vector<const char *> compiler_invocation_argument_cstrs;
+ compiler_invocation_argument_cstrs.reserve(
+ compiler_invocation_arguments.size());
+ for (const std::string &arg : compiler_invocation_arguments)
+ compiler_invocation_argument_cstrs.push_back(arg.c_str());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+ LLDB_LOG(log, "ClangModulesDeclVendor's compiler flags {0:$[ ]}",
+ llvm::make_range(compiler_invocation_arguments.begin(),
+ compiler_invocation_arguments.end()));
+
+ std::shared_ptr<clang::CompilerInvocation> invocation =
+ clang::createInvocationFromCommandLine(compiler_invocation_argument_cstrs,
+ diagnostics_engine);
+
+ if (!invocation)
+ return nullptr;
+
+ std::unique_ptr<llvm::MemoryBuffer> source_buffer =
+ llvm::MemoryBuffer::getMemBuffer(
+ "extern int __lldb __attribute__((unavailable));",
+ ModuleImportBufferName);
+
+ invocation->getPreprocessorOpts().addRemappedFile(ModuleImportBufferName,
+ source_buffer.release());
+
+ std::unique_ptr<clang::CompilerInstance> instance(
+ new clang::CompilerInstance);
+
+ // When capturing a reproducer, hook up the file collector with clang to
+ // collector modules and headers.
+ if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
+ repro::FileProvider &fp = g->GetOrCreate<repro::FileProvider>();
+ instance->setModuleDepCollector(
+ std::make_shared<ModuleDependencyCollectorAdaptor>(
+ fp.GetFileCollector()));
+ clang::DependencyOutputOptions &opts = instance->getDependencyOutputOpts();
+ opts.IncludeSystemHeaders = true;
+ opts.IncludeModuleFiles = true;
+ }
+
+ // Make sure clang uses the same VFS as LLDB.
+ instance->createFileManager(FileSystem::Instance().GetVirtualFileSystem());
+ instance->setDiagnostics(diagnostics_engine.get());
+ instance->setInvocation(invocation);
+
+ std::unique_ptr<clang::FrontendAction> action(new clang::SyntaxOnlyAction);
+
+ instance->setTarget(clang::TargetInfo::CreateTargetInfo(
+ *diagnostics_engine, instance->getInvocation().TargetOpts));
+
+ if (!instance->hasTarget())
+ return nullptr;
+
+ instance->getTarget().adjust(instance->getLangOpts());
+
+ if (!action->BeginSourceFile(*instance,
+ instance->getFrontendOpts().Inputs[0]))
+ return nullptr;
+
+ instance->getPreprocessor().enableIncrementalProcessing();
+
+ instance->createModuleManager();
+
+ instance->createSema(action->getTranslationUnitKind(), nullptr);
+
+ const bool skipFunctionBodies = false;
+ std::unique_ptr<clang::Parser> parser(new clang::Parser(
+ instance->getPreprocessor(), instance->getSema(), skipFunctionBodies));
+
+ instance->getPreprocessor().EnterMainSourceFile();
+ parser->Initialize();
+
+ clang::Parser::DeclGroupPtrTy parsed;
+
+ while (!parser->ParseTopLevelDecl(parsed))
+ ;
+
+ return new ClangModulesDeclVendorImpl(std::move(diagnostics_engine),
+ std::move(invocation),
+ std::move(instance), std::move(parser));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h
new file mode 100644
index 000000000000..d5c8757bdcd0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h
@@ -0,0 +1,111 @@
+//===-- ClangModulesDeclVendor.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangModulesDeclVendor_h
+#define liblldb_ClangModulesDeclVendor_h
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Symbol/DeclVendor.h"
+#include "lldb/Symbol/SourceModule.h"
+#include "lldb/Target/Platform.h"
+
+#include <set>
+#include <vector>
+
+namespace lldb_private {
+
+class ClangModulesDeclVendor : public DeclVendor {
+public:
+ // Constructors and Destructors
+ ClangModulesDeclVendor();
+
+ ~ClangModulesDeclVendor() override;
+
+ static ClangModulesDeclVendor *Create(Target &target);
+
+ typedef std::vector<ConstString> ModulePath;
+ typedef uintptr_t ModuleID;
+ typedef std::vector<ModuleID> ModuleVector;
+
+ /// Add a module to the list of modules to search.
+ ///
+ /// \param[in] module
+ /// The path to the exact module to be loaded. E.g., if the desired
+ /// module is std.io, then this should be { "std", "io" }.
+ ///
+ /// \param[in] exported_modules
+ /// If non-NULL, a pointer to a vector to populate with the ID of every
+ /// module that is re-exported by the specified module.
+ ///
+ /// \param[in] error_stream
+ /// A stream to populate with the output of the Clang parser when
+ /// it tries to load the module.
+ ///
+ /// \return
+ /// True if the module could be loaded; false if not. If the
+ /// compiler encountered a fatal error during a previous module
+ /// load, then this will always return false for this ModuleImporter.
+ virtual bool AddModule(const SourceModule &module,
+ ModuleVector *exported_modules,
+ Stream &error_stream) = 0;
+
+ /// Add all modules referred to in a given compilation unit to the list
+ /// of modules to search.
+ ///
+ /// \param[in] cu
+ /// The compilation unit to scan for imported modules.
+ ///
+ /// \param[in] exported_modules
+ /// A vector to populate with the ID of each module loaded (directly
+ /// and via re-exports) in this way.
+ ///
+ /// \param[in] error_stream
+ /// A stream to populate with the output of the Clang parser when
+ /// it tries to load the modules.
+ ///
+ /// \return
+ /// True if all modules referred to by the compilation unit could be
+ /// loaded; false if one could not be loaded. If the compiler
+ /// encountered a fatal error during a previous module
+ /// load, then this will always return false for this ModuleImporter.
+ virtual bool AddModulesForCompileUnit(CompileUnit &cu,
+ ModuleVector &exported_modules,
+ Stream &error_stream) = 0;
+
+ /// Enumerate all the macros that are defined by a given set of modules
+ /// that are already imported.
+ ///
+ /// \param[in] modules
+ /// The unique IDs for all modules to query. Later modules have higher
+ /// priority, just as if you @imported them in that order. This matters
+ /// if module A #defines a macro and module B #undefs it.
+ ///
+ /// \param[in] handler
+ /// A function to call with the text of each #define (including the
+ /// #define directive). #undef directives are not included; we simply
+ /// elide any corresponding #define. If this function returns true,
+ /// we stop the iteration immediately.
+ virtual void
+ ForEachMacro(const ModuleVector &modules,
+ std::function<bool(const std::string &)> handler) = 0;
+
+ /// Query whether Clang supports modules for a particular language.
+ /// LLDB uses this to decide whether to try to find the modules loaded
+ /// by a given compile unit.
+ ///
+ /// \param[in] language
+ /// The language to query for.
+ ///
+ /// \return
+ /// True if Clang has modules for the given language.
+ static bool LanguageSupportsClangModules(lldb::LanguageType language);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangModulesDeclVendor_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp
new file mode 100644
index 000000000000..742a14992dc9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp
@@ -0,0 +1,94 @@
+//===-- ClangPersistentVariables.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangPersistentVariables.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "clang/AST/Decl.h"
+
+#include "llvm/ADT/StringMap.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ClangPersistentVariables::ClangPersistentVariables()
+ : lldb_private::PersistentExpressionState(LLVMCastKind::eKindClang),
+ m_next_persistent_variable_id(0) {}
+
+ExpressionVariableSP ClangPersistentVariables::CreatePersistentVariable(
+ const lldb::ValueObjectSP &valobj_sp) {
+ return AddNewlyConstructedVariable(new ClangExpressionVariable(valobj_sp));
+}
+
+ExpressionVariableSP ClangPersistentVariables::CreatePersistentVariable(
+ ExecutionContextScope *exe_scope, ConstString name,
+ const CompilerType &compiler_type, lldb::ByteOrder byte_order,
+ uint32_t addr_byte_size) {
+ return AddNewlyConstructedVariable(new ClangExpressionVariable(
+ exe_scope, name, compiler_type, byte_order, addr_byte_size));
+}
+
+void ClangPersistentVariables::RemovePersistentVariable(
+ lldb::ExpressionVariableSP variable) {
+ RemoveVariable(variable);
+
+ const char *name = variable->GetName().AsCString();
+
+ if (*name != '$')
+ return;
+ name++;
+
+ if (strtoul(name, nullptr, 0) == m_next_persistent_variable_id - 1)
+ m_next_persistent_variable_id--;
+}
+
+llvm::Optional<CompilerType>
+ClangPersistentVariables::GetCompilerTypeFromPersistentDecl(
+ ConstString type_name) {
+ CompilerType compiler_type;
+ if (clang::TypeDecl *tdecl = llvm::dyn_cast_or_null<clang::TypeDecl>(
+ GetPersistentDecl(type_name))) {
+ compiler_type.SetCompilerType(
+ ClangASTContext::GetASTContext(&tdecl->getASTContext()),
+ reinterpret_cast<lldb::opaque_compiler_type_t>(
+ const_cast<clang::Type *>(tdecl->getTypeForDecl())));
+ return compiler_type;
+ }
+ return llvm::None;
+}
+
+void ClangPersistentVariables::RegisterPersistentDecl(ConstString name,
+ clang::NamedDecl *decl) {
+ m_persistent_decls.insert(
+ std::pair<const char *, clang::NamedDecl *>(name.GetCString(), decl));
+
+ if (clang::EnumDecl *enum_decl = llvm::dyn_cast<clang::EnumDecl>(decl)) {
+ for (clang::EnumConstantDecl *enumerator_decl : enum_decl->enumerators()) {
+ m_persistent_decls.insert(std::pair<const char *, clang::NamedDecl *>(
+ ConstString(enumerator_decl->getNameAsString()).GetCString(),
+ enumerator_decl));
+ }
+ }
+}
+
+clang::NamedDecl *
+ClangPersistentVariables::GetPersistentDecl(ConstString name) {
+ PersistentDeclMap::const_iterator i =
+ m_persistent_decls.find(name.GetCString());
+
+ if (i == m_persistent_decls.end())
+ return nullptr;
+ else
+ return i->second;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h
new file mode 100644
index 000000000000..b39f89ad7eef
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h
@@ -0,0 +1,84 @@
+//===-- ClangPersistentVariables.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangPersistentVariables_h_
+#define liblldb_ClangPersistentVariables_h_
+
+#include "llvm/ADT/DenseMap.h"
+
+#include "ClangExpressionVariable.h"
+#include "ClangModulesDeclVendor.h"
+
+#include "lldb/Expression/ExpressionVariable.h"
+
+namespace lldb_private {
+
+/// \class ClangPersistentVariables ClangPersistentVariables.h
+/// "lldb/Expression/ClangPersistentVariables.h" Manages persistent values
+/// that need to be preserved between expression invocations.
+///
+/// A list of variables that can be accessed and updated by any expression. See
+/// ClangPersistentVariable for more discussion. Also provides an increasing,
+/// 0-based counter for naming result variables.
+class ClangPersistentVariables : public PersistentExpressionState {
+public:
+ ClangPersistentVariables();
+
+ ~ClangPersistentVariables() override = default;
+
+ // llvm casting support
+ static bool classof(const PersistentExpressionState *pv) {
+ return pv->getKind() == PersistentExpressionState::eKindClang;
+ }
+
+ lldb::ExpressionVariableSP
+ CreatePersistentVariable(const lldb::ValueObjectSP &valobj_sp) override;
+
+ lldb::ExpressionVariableSP CreatePersistentVariable(
+ ExecutionContextScope *exe_scope, ConstString name,
+ const CompilerType &compiler_type, lldb::ByteOrder byte_order,
+ uint32_t addr_byte_size) override;
+
+ void RemovePersistentVariable(lldb::ExpressionVariableSP variable) override;
+ llvm::StringRef
+ GetPersistentVariablePrefix(bool is_error) const override {
+ return "$";
+ }
+
+ llvm::Optional<CompilerType>
+ GetCompilerTypeFromPersistentDecl(ConstString type_name) override;
+
+ void RegisterPersistentDecl(ConstString name, clang::NamedDecl *decl);
+
+ clang::NamedDecl *GetPersistentDecl(ConstString name);
+
+ void AddHandLoadedClangModule(ClangModulesDeclVendor::ModuleID module) {
+ m_hand_loaded_clang_modules.push_back(module);
+ }
+
+ const ClangModulesDeclVendor::ModuleVector &GetHandLoadedClangModules() {
+ return m_hand_loaded_clang_modules;
+ }
+
+private:
+ uint32_t m_next_persistent_variable_id; ///< The counter used by
+ ///GetNextResultName().
+
+ typedef llvm::DenseMap<const char *, clang::NamedDecl *> PersistentDeclMap;
+ PersistentDeclMap
+ m_persistent_decls; ///< Persistent entities declared by the user.
+
+ ClangModulesDeclVendor::ModuleVector
+ m_hand_loaded_clang_modules; ///< These are Clang modules we hand-loaded;
+ ///these are the highest-
+ ///< priority source for macros.
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangPersistentVariables_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
new file mode 100644
index 000000000000..2dae5b7022f3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
@@ -0,0 +1,903 @@
+//===-- ClangUserExpression.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <cstdlib>
+#include <map>
+#include <string>
+
+#include "ClangUserExpression.h"
+
+#include "ASTResultSynthesizer.h"
+#include "ClangDiagnostic.h"
+#include "ClangExpressionDeclMap.h"
+#include "ClangExpressionParser.h"
+#include "ClangExpressionSourceCode.h"
+#include "ClangModulesDeclVendor.h"
+#include "ClangPersistentVariables.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Expression/ExpressionSourceCode.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Expression/IRInterpreter.h"
+#include "lldb/Expression/Materializer.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallUserExpression.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+
+#include "llvm/ADT/ScopeExit.h"
+
+using namespace lldb_private;
+
+ClangUserExpression::ClangUserExpression(
+ ExecutionContextScope &exe_scope, llvm::StringRef expr,
+ llvm::StringRef prefix, lldb::LanguageType language,
+ ResultType desired_type, const EvaluateExpressionOptions &options,
+ ValueObject *ctx_obj)
+ : LLVMUserExpression(exe_scope, expr, prefix, language, desired_type,
+ options, eKindClangUserExpression),
+ m_type_system_helper(*m_target_wp.lock(), options.GetExecutionPolicy() ==
+ eExecutionPolicyTopLevel),
+ m_result_delegate(exe_scope.CalculateTarget()), m_ctx_obj(ctx_obj) {
+ switch (m_language) {
+ case lldb::eLanguageTypeC_plus_plus:
+ m_allow_cxx = true;
+ break;
+ case lldb::eLanguageTypeObjC:
+ m_allow_objc = true;
+ break;
+ case lldb::eLanguageTypeObjC_plus_plus:
+ default:
+ m_allow_cxx = true;
+ m_allow_objc = true;
+ break;
+ }
+}
+
+ClangUserExpression::~ClangUserExpression() {}
+
+void ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Status &err) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("ClangUserExpression::ScanContext()");
+
+ m_target = exe_ctx.GetTargetPtr();
+
+ if (!(m_allow_cxx || m_allow_objc)) {
+ if (log)
+ log->Printf(" [CUE::SC] Settings inhibit C++ and Objective-C");
+ return;
+ }
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame == nullptr) {
+ if (log)
+ log->Printf(" [CUE::SC] Null stack frame");
+ return;
+ }
+
+ SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction |
+ lldb::eSymbolContextBlock);
+
+ if (!sym_ctx.function) {
+ if (log)
+ log->Printf(" [CUE::SC] Null function");
+ return;
+ }
+
+ // Find the block that defines the function represented by "sym_ctx"
+ Block *function_block = sym_ctx.GetFunctionBlock();
+
+ if (!function_block) {
+ if (log)
+ log->Printf(" [CUE::SC] Null function block");
+ return;
+ }
+
+ CompilerDeclContext decl_context = function_block->GetDeclContext();
+
+ if (!decl_context) {
+ if (log)
+ log->Printf(" [CUE::SC] Null decl context");
+ return;
+ }
+
+ if (m_ctx_obj) {
+ switch (m_ctx_obj->GetObjectRuntimeLanguage()) {
+ case lldb::eLanguageTypeC:
+ case lldb::eLanguageTypeC89:
+ case lldb::eLanguageTypeC99:
+ case lldb::eLanguageTypeC11:
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeC_plus_plus_03:
+ case lldb::eLanguageTypeC_plus_plus_11:
+ case lldb::eLanguageTypeC_plus_plus_14:
+ m_in_cplusplus_method = true;
+ break;
+ case lldb::eLanguageTypeObjC:
+ case lldb::eLanguageTypeObjC_plus_plus:
+ m_in_objectivec_method = true;
+ break;
+ default:
+ break;
+ }
+ m_needs_object_ptr = true;
+ } else if (clang::CXXMethodDecl *method_decl =
+ ClangASTContext::DeclContextGetAsCXXMethodDecl(decl_context)) {
+ if (m_allow_cxx && method_decl->isInstance()) {
+ if (m_enforce_valid_object) {
+ lldb::VariableListSP variable_list_sp(
+ function_block->GetBlockVariableList(true));
+
+ const char *thisErrorString = "Stopped in a C++ method, but 'this' "
+ "isn't available; pretending we are in a "
+ "generic context";
+
+ if (!variable_list_sp) {
+ err.SetErrorString(thisErrorString);
+ return;
+ }
+
+ lldb::VariableSP this_var_sp(
+ variable_list_sp->FindVariable(ConstString("this")));
+
+ if (!this_var_sp || !this_var_sp->IsInScope(frame) ||
+ !this_var_sp->LocationIsValidForFrame(frame)) {
+ err.SetErrorString(thisErrorString);
+ return;
+ }
+ }
+
+ m_in_cplusplus_method = true;
+ m_needs_object_ptr = true;
+ }
+ } else if (clang::ObjCMethodDecl *method_decl =
+ ClangASTContext::DeclContextGetAsObjCMethodDecl(
+ decl_context)) {
+ if (m_allow_objc) {
+ if (m_enforce_valid_object) {
+ lldb::VariableListSP variable_list_sp(
+ function_block->GetBlockVariableList(true));
+
+ const char *selfErrorString = "Stopped in an Objective-C method, but "
+ "'self' isn't available; pretending we "
+ "are in a generic context";
+
+ if (!variable_list_sp) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+
+ lldb::VariableSP self_variable_sp =
+ variable_list_sp->FindVariable(ConstString("self"));
+
+ if (!self_variable_sp || !self_variable_sp->IsInScope(frame) ||
+ !self_variable_sp->LocationIsValidForFrame(frame)) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+ }
+
+ m_in_objectivec_method = true;
+ m_needs_object_ptr = true;
+
+ if (!method_decl->isInstanceMethod())
+ m_in_static_method = true;
+ }
+ } else if (clang::FunctionDecl *function_decl =
+ ClangASTContext::DeclContextGetAsFunctionDecl(decl_context)) {
+ // We might also have a function that said in the debug information that it
+ // captured an object pointer. The best way to deal with getting to the
+ // ivars at present is by pretending that this is a method of a class in
+ // whatever runtime the debug info says the object pointer belongs to. Do
+ // that here.
+
+ ClangASTMetadata *metadata =
+ ClangASTContext::DeclContextGetMetaData(decl_context, function_decl);
+ if (metadata && metadata->HasObjectPtr()) {
+ lldb::LanguageType language = metadata->GetObjectPtrLanguage();
+ if (language == lldb::eLanguageTypeC_plus_plus) {
+ if (m_enforce_valid_object) {
+ lldb::VariableListSP variable_list_sp(
+ function_block->GetBlockVariableList(true));
+
+ const char *thisErrorString = "Stopped in a context claiming to "
+ "capture a C++ object pointer, but "
+ "'this' isn't available; pretending we "
+ "are in a generic context";
+
+ if (!variable_list_sp) {
+ err.SetErrorString(thisErrorString);
+ return;
+ }
+
+ lldb::VariableSP this_var_sp(
+ variable_list_sp->FindVariable(ConstString("this")));
+
+ if (!this_var_sp || !this_var_sp->IsInScope(frame) ||
+ !this_var_sp->LocationIsValidForFrame(frame)) {
+ err.SetErrorString(thisErrorString);
+ return;
+ }
+ }
+
+ m_in_cplusplus_method = true;
+ m_needs_object_ptr = true;
+ } else if (language == lldb::eLanguageTypeObjC) {
+ if (m_enforce_valid_object) {
+ lldb::VariableListSP variable_list_sp(
+ function_block->GetBlockVariableList(true));
+
+ const char *selfErrorString =
+ "Stopped in a context claiming to capture an Objective-C object "
+ "pointer, but 'self' isn't available; pretending we are in a "
+ "generic context";
+
+ if (!variable_list_sp) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+
+ lldb::VariableSP self_variable_sp =
+ variable_list_sp->FindVariable(ConstString("self"));
+
+ if (!self_variable_sp || !self_variable_sp->IsInScope(frame) ||
+ !self_variable_sp->LocationIsValidForFrame(frame)) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+
+ Type *self_type = self_variable_sp->GetType();
+
+ if (!self_type) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+
+ CompilerType self_clang_type = self_type->GetForwardCompilerType();
+
+ if (!self_clang_type) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+
+ if (ClangASTContext::IsObjCClassType(self_clang_type)) {
+ return;
+ } else if (ClangASTContext::IsObjCObjectPointerType(
+ self_clang_type)) {
+ m_in_objectivec_method = true;
+ m_needs_object_ptr = true;
+ } else {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+ } else {
+ m_in_objectivec_method = true;
+ m_needs_object_ptr = true;
+ }
+ }
+ }
+ }
+}
+
+// This is a really nasty hack, meant to fix Objective-C expressions of the
+// form (int)[myArray count]. Right now, because the type information for
+// count is not available, [myArray count] returns id, which can't be directly
+// cast to int without causing a clang error.
+static void ApplyObjcCastHack(std::string &expr) {
+#define OBJC_CAST_HACK_FROM "(int)["
+#define OBJC_CAST_HACK_TO "(int)(long long)["
+
+ size_t from_offset;
+
+ while ((from_offset = expr.find(OBJC_CAST_HACK_FROM)) != expr.npos)
+ expr.replace(from_offset, sizeof(OBJC_CAST_HACK_FROM) - 1,
+ OBJC_CAST_HACK_TO);
+
+#undef OBJC_CAST_HACK_TO
+#undef OBJC_CAST_HACK_FROM
+}
+
+bool ClangUserExpression::SetupPersistentState(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx) {
+ if (Target *target = exe_ctx.GetTargetPtr()) {
+ if (PersistentExpressionState *persistent_state =
+ target->GetPersistentExpressionStateForLanguage(
+ lldb::eLanguageTypeC)) {
+ m_result_delegate.RegisterPersistentState(persistent_state);
+ } else {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "couldn't start parsing (no persistent data)");
+ return false;
+ }
+ } else {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "error: couldn't start parsing (no target)");
+ return false;
+ }
+ return true;
+}
+
+static void SetupDeclVendor(ExecutionContext &exe_ctx, Target *target) {
+ if (ClangModulesDeclVendor *decl_vendor =
+ target->GetClangModulesDeclVendor()) {
+ const ClangModulesDeclVendor::ModuleVector &hand_imported_modules =
+ llvm::cast<ClangPersistentVariables>(
+ target->GetPersistentExpressionStateForLanguage(
+ lldb::eLanguageTypeC))
+ ->GetHandLoadedClangModules();
+ ClangModulesDeclVendor::ModuleVector modules_for_macros;
+
+ for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) {
+ modules_for_macros.push_back(module);
+ }
+
+ if (target->GetEnableAutoImportClangModules()) {
+ if (StackFrame *frame = exe_ctx.GetFramePtr()) {
+ if (Block *block = frame->GetFrameBlock()) {
+ SymbolContext sc;
+
+ block->CalculateSymbolContext(&sc);
+
+ if (sc.comp_unit) {
+ StreamString error_stream;
+
+ decl_vendor->AddModulesForCompileUnit(
+ *sc.comp_unit, modules_for_macros, error_stream);
+ }
+ }
+ }
+ }
+ }
+}
+
+void ClangUserExpression::UpdateLanguageForExpr(
+ DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
+ std::vector<std::string> modules_to_import, bool for_completion) {
+ m_expr_lang = lldb::LanguageType::eLanguageTypeUnknown;
+
+ std::string prefix = m_expr_prefix;
+
+ if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) {
+ m_transformed_text = m_expr_text;
+ } else {
+ std::unique_ptr<ClangExpressionSourceCode> source_code(
+ ClangExpressionSourceCode::CreateWrapped(prefix.c_str(),
+ m_expr_text.c_str()));
+
+ if (m_in_cplusplus_method)
+ m_expr_lang = lldb::eLanguageTypeC_plus_plus;
+ else if (m_in_objectivec_method)
+ m_expr_lang = lldb::eLanguageTypeObjC;
+ else
+ m_expr_lang = lldb::eLanguageTypeC;
+
+ if (!source_code->GetText(m_transformed_text, m_expr_lang,
+ m_in_static_method, exe_ctx, !m_ctx_obj,
+ for_completion, modules_to_import)) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "couldn't construct expression body");
+ return;
+ }
+
+ // Find and store the start position of the original code inside the
+ // transformed code. We need this later for the code completion.
+ std::size_t original_start;
+ std::size_t original_end;
+ bool found_bounds = source_code->GetOriginalBodyBounds(
+ m_transformed_text, m_expr_lang, original_start, original_end);
+ if (found_bounds)
+ m_user_expression_start_pos = original_start;
+ }
+}
+
+static bool SupportsCxxModuleImport(lldb::LanguageType language) {
+ switch (language) {
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeC_plus_plus_03:
+ case lldb::eLanguageTypeC_plus_plus_11:
+ case lldb::eLanguageTypeC_plus_plus_14:
+ case lldb::eLanguageTypeObjC_plus_plus:
+ return true;
+ default:
+ return false;
+ }
+}
+
+std::vector<std::string>
+ClangUserExpression::GetModulesToImport(ExecutionContext &exe_ctx) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!SupportsCxxModuleImport(Language()))
+ return {};
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (!target || !target->GetEnableImportStdModule())
+ return {};
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (!frame)
+ return {};
+
+ Block *block = frame->GetFrameBlock();
+ if (!block)
+ return {};
+
+ SymbolContext sc;
+ block->CalculateSymbolContext(&sc);
+ if (!sc.comp_unit)
+ return {};
+
+ if (log) {
+ for (const SourceModule &m : sc.comp_unit->GetImportedModules()) {
+ LLDB_LOG(log, "Found module in compile unit: {0:$[.]} - include dir: {1}",
+ llvm::make_range(m.path.begin(), m.path.end()), m.search_path);
+ }
+ }
+
+ for (const SourceModule &m : sc.comp_unit->GetImportedModules())
+ m_include_directories.push_back(m.search_path);
+
+ // Check if we imported 'std' or any of its submodules.
+ // We currently don't support importing any other modules in the expression
+ // parser.
+ for (const SourceModule &m : sc.comp_unit->GetImportedModules())
+ if (!m.path.empty() && m.path.front() == "std")
+ return {"std"};
+
+ return {};
+}
+
+bool ClangUserExpression::PrepareForParsing(
+ DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
+ bool for_completion) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ InstallContext(exe_ctx);
+
+ if (!SetupPersistentState(diagnostic_manager, exe_ctx))
+ return false;
+
+ Status err;
+ ScanContext(exe_ctx, err);
+
+ if (!err.Success()) {
+ diagnostic_manager.PutString(eDiagnosticSeverityWarning, err.AsCString());
+ }
+
+ ////////////////////////////////////
+ // Generate the expression
+ //
+
+ ApplyObjcCastHack(m_expr_text);
+
+ SetupDeclVendor(exe_ctx, m_target);
+
+ std::vector<std::string> used_modules = GetModulesToImport(exe_ctx);
+ m_imported_cpp_modules = !used_modules.empty();
+
+ LLDB_LOG(log, "List of imported modules in expression: {0}",
+ llvm::make_range(used_modules.begin(), used_modules.end()));
+
+ UpdateLanguageForExpr(diagnostic_manager, exe_ctx, used_modules,
+ for_completion);
+ return true;
+}
+
+bool ClangUserExpression::Parse(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx,
+ lldb_private::ExecutionPolicy execution_policy,
+ bool keep_result_in_memory,
+ bool generate_debug_info) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!PrepareForParsing(diagnostic_manager, exe_ctx, /*for_completion*/ false))
+ return false;
+
+ if (log)
+ log->Printf("Parsing the following code:\n%s", m_transformed_text.c_str());
+
+ ////////////////////////////////////
+ // Set up the target and compiler
+ //
+
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError, "invalid target");
+ return false;
+ }
+
+ //////////////////////////
+ // Parse the expression
+ //
+
+ m_materializer_up.reset(new Materializer());
+
+ ResetDeclMap(exe_ctx, m_result_delegate, keep_result_in_memory);
+
+ auto on_exit = llvm::make_scope_exit([this]() { ResetDeclMap(); });
+
+ if (!DeclMap()->WillParse(exe_ctx, m_materializer_up.get())) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "current process state is unsuitable for expression parsing");
+ return false;
+ }
+
+ if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) {
+ DeclMap()->SetLookupsEnabled(true);
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+ ExecutionContextScope *exe_scope = process;
+
+ if (!exe_scope)
+ exe_scope = exe_ctx.GetTargetPtr();
+
+ // We use a shared pointer here so we can use the original parser - if it
+ // succeeds or the rewrite parser we might make if it fails. But the
+ // parser_sp will never be empty.
+
+ ClangExpressionParser parser(exe_scope, *this, generate_debug_info,
+ m_include_directories);
+
+ unsigned num_errors = parser.Parse(diagnostic_manager);
+
+ // Check here for FixItHints. If there are any try to apply the fixits and
+ // set the fixed text in m_fixed_text before returning an error.
+ if (num_errors) {
+ if (diagnostic_manager.HasFixIts()) {
+ if (parser.RewriteExpression(diagnostic_manager)) {
+ size_t fixed_start;
+ size_t fixed_end;
+ const std::string &fixed_expression =
+ diagnostic_manager.GetFixedExpression();
+ if (ClangExpressionSourceCode::GetOriginalBodyBounds(
+ fixed_expression, m_expr_lang, fixed_start, fixed_end))
+ m_fixed_text =
+ fixed_expression.substr(fixed_start, fixed_end - fixed_start);
+ }
+ }
+ return false;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Prepare the output of the parser for execution, evaluating it statically
+ // if possible
+ //
+
+ {
+ Status jit_error = parser.PrepareForExecution(
+ m_jit_start_addr, m_jit_end_addr, m_execution_unit_sp, exe_ctx,
+ m_can_interpret, execution_policy);
+
+ if (!jit_error.Success()) {
+ const char *error_cstr = jit_error.AsCString();
+ if (error_cstr && error_cstr[0])
+ diagnostic_manager.PutString(eDiagnosticSeverityError, error_cstr);
+ else
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "expression can't be interpreted or run");
+ return false;
+ }
+ }
+
+ if (exe_ctx.GetProcessPtr() && execution_policy == eExecutionPolicyTopLevel) {
+ Status static_init_error =
+ parser.RunStaticInitializers(m_execution_unit_sp, exe_ctx);
+
+ if (!static_init_error.Success()) {
+ const char *error_cstr = static_init_error.AsCString();
+ if (error_cstr && error_cstr[0])
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "couldn't run static initializers: %s\n",
+ error_cstr);
+ else
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "couldn't run static initializers\n");
+ return false;
+ }
+ }
+
+ if (m_execution_unit_sp) {
+ bool register_execution_unit = false;
+
+ if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) {
+ register_execution_unit = true;
+ }
+
+ // If there is more than one external function in the execution unit, it
+ // needs to keep living even if it's not top level, because the result
+ // could refer to that function.
+
+ if (m_execution_unit_sp->GetJittedFunctions().size() > 1) {
+ register_execution_unit = true;
+ }
+
+ if (register_execution_unit) {
+ llvm::cast<PersistentExpressionState>(
+ exe_ctx.GetTargetPtr()->GetPersistentExpressionStateForLanguage(
+ m_language))
+ ->RegisterExecutionUnit(m_execution_unit_sp);
+ }
+ }
+
+ if (generate_debug_info) {
+ lldb::ModuleSP jit_module_sp(m_execution_unit_sp->GetJITModule());
+
+ if (jit_module_sp) {
+ ConstString const_func_name(FunctionName());
+ FileSpec jit_file;
+ jit_file.GetFilename() = const_func_name;
+ jit_module_sp->SetFileSpecAndObjectName(jit_file, ConstString());
+ m_jit_module_wp = jit_module_sp;
+ target->GetImages().Append(jit_module_sp);
+ }
+ }
+
+ if (process && m_jit_start_addr != LLDB_INVALID_ADDRESS)
+ m_jit_process_wp = lldb::ProcessWP(process->shared_from_this());
+ return true;
+}
+
+/// Converts an absolute position inside a given code string into
+/// a column/line pair.
+///
+/// \param[in] abs_pos
+/// A absolute position in the code string that we want to convert
+/// to a column/line pair.
+///
+/// \param[in] code
+/// A multi-line string usually representing source code.
+///
+/// \param[out] line
+/// The line in the code that contains the given absolute position.
+/// The first line in the string is indexed as 1.
+///
+/// \param[out] column
+/// The column in the line that contains the absolute position.
+/// The first character in a line is indexed as 0.
+static void AbsPosToLineColumnPos(size_t abs_pos, llvm::StringRef code,
+ unsigned &line, unsigned &column) {
+ // Reset to code position to beginning of the file.
+ line = 0;
+ column = 0;
+
+ assert(abs_pos <= code.size() && "Absolute position outside code string?");
+
+ // We have to walk up to the position and count lines/columns.
+ for (std::size_t i = 0; i < abs_pos; ++i) {
+ // If we hit a line break, we go back to column 0 and enter a new line.
+ // We only handle \n because that's what we internally use to make new
+ // lines for our temporary code strings.
+ if (code[i] == '\n') {
+ ++line;
+ column = 0;
+ continue;
+ }
+ ++column;
+ }
+}
+
+bool ClangUserExpression::Complete(ExecutionContext &exe_ctx,
+ CompletionRequest &request,
+ unsigned complete_pos) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ // We don't want any visible feedback when completing an expression. Mostly
+ // because the results we get from an incomplete invocation are probably not
+ // correct.
+ DiagnosticManager diagnostic_manager;
+
+ if (!PrepareForParsing(diagnostic_manager, exe_ctx, /*for_completion*/ true))
+ return false;
+
+ if (log)
+ log->Printf("Parsing the following code:\n%s", m_transformed_text.c_str());
+
+ //////////////////////////
+ // Parse the expression
+ //
+
+ m_materializer_up.reset(new Materializer());
+
+ ResetDeclMap(exe_ctx, m_result_delegate, /*keep result in memory*/ true);
+
+ auto on_exit = llvm::make_scope_exit([this]() { ResetDeclMap(); });
+
+ if (!DeclMap()->WillParse(exe_ctx, m_materializer_up.get())) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "current process state is unsuitable for expression parsing");
+
+ return false;
+ }
+
+ if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) {
+ DeclMap()->SetLookupsEnabled(true);
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+ ExecutionContextScope *exe_scope = process;
+
+ if (!exe_scope)
+ exe_scope = exe_ctx.GetTargetPtr();
+
+ ClangExpressionParser parser(exe_scope, *this, false);
+
+ // We have to find the source code location where the user text is inside
+ // the transformed expression code. When creating the transformed text, we
+ // already stored the absolute position in the m_transformed_text string. The
+ // only thing left to do is to transform it into the line:column format that
+ // Clang expects.
+
+ // The line and column of the user expression inside the transformed source
+ // code.
+ unsigned user_expr_line, user_expr_column;
+ if (m_user_expression_start_pos.hasValue())
+ AbsPosToLineColumnPos(*m_user_expression_start_pos, m_transformed_text,
+ user_expr_line, user_expr_column);
+ else
+ return false;
+
+ // The actual column where we have to complete is the start column of the
+ // user expression + the offset inside the user code that we were given.
+ const unsigned completion_column = user_expr_column + complete_pos;
+ parser.Complete(request, user_expr_line, completion_column, complete_pos);
+
+ return true;
+}
+
+bool ClangUserExpression::AddArguments(ExecutionContext &exe_ctx,
+ std::vector<lldb::addr_t> &args,
+ lldb::addr_t struct_address,
+ DiagnosticManager &diagnostic_manager) {
+ lldb::addr_t object_ptr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t cmd_ptr = LLDB_INVALID_ADDRESS;
+
+ if (m_needs_object_ptr) {
+ lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP();
+ if (!frame_sp)
+ return true;
+
+ ConstString object_name;
+
+ if (m_in_cplusplus_method) {
+ object_name.SetCString("this");
+ } else if (m_in_objectivec_method) {
+ object_name.SetCString("self");
+ } else {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "need object pointer but don't know the language");
+ return false;
+ }
+
+ Status object_ptr_error;
+
+ if (m_ctx_obj) {
+ AddressType address_type;
+ object_ptr = m_ctx_obj->GetAddressOf(false, &address_type);
+ if (object_ptr == LLDB_INVALID_ADDRESS ||
+ address_type != eAddressTypeLoad)
+ object_ptr_error.SetErrorString("Can't get context object's "
+ "debuggee address");
+ } else
+ object_ptr = GetObjectPointer(frame_sp, object_name, object_ptr_error);
+
+ if (!object_ptr_error.Success()) {
+ exe_ctx.GetTargetRef().GetDebugger().GetAsyncOutputStream()->Printf(
+ "warning: `%s' is not accessible (substituting 0)\n",
+ object_name.AsCString());
+ object_ptr = 0;
+ }
+
+ if (m_in_objectivec_method) {
+ ConstString cmd_name("_cmd");
+
+ cmd_ptr = GetObjectPointer(frame_sp, cmd_name, object_ptr_error);
+
+ if (!object_ptr_error.Success()) {
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityWarning,
+ "couldn't get cmd pointer (substituting NULL): %s",
+ object_ptr_error.AsCString());
+ cmd_ptr = 0;
+ }
+ }
+
+ args.push_back(object_ptr);
+
+ if (m_in_objectivec_method)
+ args.push_back(cmd_ptr);
+
+ args.push_back(struct_address);
+ } else {
+ args.push_back(struct_address);
+ }
+ return true;
+}
+
+lldb::ExpressionVariableSP ClangUserExpression::GetResultAfterDematerialization(
+ ExecutionContextScope *exe_scope) {
+ return m_result_delegate.GetVariable();
+}
+
+void ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap(
+ ExecutionContext &exe_ctx,
+ Materializer::PersistentVariableDelegate &delegate,
+ bool keep_result_in_memory,
+ ValueObject *ctx_obj) {
+ m_expr_decl_map_up.reset(
+ new ClangExpressionDeclMap(keep_result_in_memory, &delegate, exe_ctx,
+ ctx_obj));
+}
+
+clang::ASTConsumer *
+ClangUserExpression::ClangUserExpressionHelper::ASTTransformer(
+ clang::ASTConsumer *passthrough) {
+ m_result_synthesizer_up.reset(
+ new ASTResultSynthesizer(passthrough, m_top_level, m_target));
+
+ return m_result_synthesizer_up.get();
+}
+
+void ClangUserExpression::ClangUserExpressionHelper::CommitPersistentDecls() {
+ if (m_result_synthesizer_up) {
+ m_result_synthesizer_up->CommitPersistentDecls();
+ }
+}
+
+ConstString ClangUserExpression::ResultDelegate::GetName() {
+ auto prefix = m_persistent_state->GetPersistentVariablePrefix();
+ return m_persistent_state->GetNextPersistentVariableName(*m_target_sp,
+ prefix);
+}
+
+void ClangUserExpression::ResultDelegate::DidDematerialize(
+ lldb::ExpressionVariableSP &variable) {
+ m_variable = variable;
+}
+
+void ClangUserExpression::ResultDelegate::RegisterPersistentState(
+ PersistentExpressionState *persistent_state) {
+ m_persistent_state = persistent_state;
+}
+
+lldb::ExpressionVariableSP &ClangUserExpression::ResultDelegate::GetVariable() {
+ return m_variable;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
new file mode 100644
index 000000000000..24c152bdb45d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
@@ -0,0 +1,226 @@
+//===-- ClangUserExpression.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangUserExpression_h_
+#define liblldb_ClangUserExpression_h_
+
+#include <vector>
+
+#include "ASTResultSynthesizer.h"
+#include "ASTStructExtractor.h"
+#include "ClangExpressionDeclMap.h"
+#include "ClangExpressionHelper.h"
+#include "ClangExpressionVariable.h"
+#include "IRForTarget.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Expression/LLVMUserExpression.h"
+#include "lldb/Expression/Materializer.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+/// \class ClangUserExpression ClangUserExpression.h
+/// "lldb/Expression/ClangUserExpression.h" Encapsulates a single expression
+/// for use with Clang
+///
+/// LLDB uses expressions for various purposes, notably to call functions
+/// and as a backend for the expr command. ClangUserExpression encapsulates
+/// the objects needed to parse and interpret or JIT an expression. It uses
+/// the Clang parser to produce LLVM IR from the expression.
+class ClangUserExpression : public LLVMUserExpression {
+public:
+ /// LLVM-style RTTI support.
+ static bool classof(const Expression *E) {
+ return E->getKind() == eKindClangUserExpression;
+ }
+
+ enum { kDefaultTimeout = 500000u };
+
+ class ClangUserExpressionHelper : public ClangExpressionHelper {
+ public:
+ ClangUserExpressionHelper(Target &target, bool top_level)
+ : m_target(target), m_top_level(top_level) {}
+
+ ~ClangUserExpressionHelper() override = default;
+
+ /// Return the object that the parser should use when resolving external
+ /// values. May be NULL if everything should be self-contained.
+ ClangExpressionDeclMap *DeclMap() override {
+ return m_expr_decl_map_up.get();
+ }
+
+ void ResetDeclMap() { m_expr_decl_map_up.reset(); }
+
+ void ResetDeclMap(ExecutionContext &exe_ctx,
+ Materializer::PersistentVariableDelegate &result_delegate,
+ bool keep_result_in_memory,
+ ValueObject *ctx_obj);
+
+ /// Return the object that the parser should allow to access ASTs. May be
+ /// NULL if the ASTs do not need to be transformed.
+ ///
+ /// \param[in] passthrough
+ /// The ASTConsumer that the returned transformer should send
+ /// the ASTs to after transformation.
+ clang::ASTConsumer *
+ ASTTransformer(clang::ASTConsumer *passthrough) override;
+
+ void CommitPersistentDecls() override;
+
+ private:
+ Target &m_target;
+ std::unique_ptr<ClangExpressionDeclMap> m_expr_decl_map_up;
+ std::unique_ptr<ASTStructExtractor> m_struct_extractor_up; ///< The class
+ ///that generates
+ ///the argument
+ ///struct layout.
+ std::unique_ptr<ASTResultSynthesizer> m_result_synthesizer_up;
+ bool m_top_level;
+ };
+
+ /// Constructor
+ ///
+ /// \param[in] expr
+ /// The expression to parse.
+ ///
+ /// \param[in] expr_prefix
+ /// If non-NULL, a C string containing translation-unit level
+ /// definitions to be included when the expression is parsed.
+ ///
+ /// \param[in] language
+ /// If not eLanguageTypeUnknown, a language to use when parsing
+ /// the expression. Currently restricted to those languages
+ /// supported by Clang.
+ ///
+ /// \param[in] desired_type
+ /// If not eResultTypeAny, the type to use for the expression
+ /// result.
+ ///
+ /// \param[in] ctx_obj
+ /// The object (if any) in which context the expression
+ /// must be evaluated. For details see the comment to
+ /// `UserExpression::Evaluate`.
+ ClangUserExpression(ExecutionContextScope &exe_scope, llvm::StringRef expr,
+ llvm::StringRef prefix, lldb::LanguageType language,
+ ResultType desired_type,
+ const EvaluateExpressionOptions &options,
+ ValueObject *ctx_obj);
+
+ ~ClangUserExpression() override;
+
+ /// Parse the expression
+ ///
+ /// \param[in] diagnostic_manager
+ /// A diagnostic manager to report parse errors and warnings to.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to use when looking up entities that
+ /// are needed for parsing (locations of functions, types of
+ /// variables, persistent variables, etc.)
+ ///
+ /// \param[in] execution_policy
+ /// Determines whether interpretation is possible or mandatory.
+ ///
+ /// \param[in] keep_result_in_memory
+ /// True if the resulting persistent variable should reside in
+ /// target memory, if applicable.
+ ///
+ /// \return
+ /// True on success (no errors); false otherwise.
+ bool Parse(DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
+ lldb_private::ExecutionPolicy execution_policy,
+ bool keep_result_in_memory, bool generate_debug_info) override;
+
+ bool Complete(ExecutionContext &exe_ctx, CompletionRequest &request,
+ unsigned complete_pos) override;
+
+ ExpressionTypeSystemHelper *GetTypeSystemHelper() override {
+ return &m_type_system_helper;
+ }
+
+ ClangExpressionDeclMap *DeclMap() { return m_type_system_helper.DeclMap(); }
+
+ void ResetDeclMap() { m_type_system_helper.ResetDeclMap(); }
+
+ void ResetDeclMap(ExecutionContext &exe_ctx,
+ Materializer::PersistentVariableDelegate &result_delegate,
+ bool keep_result_in_memory) {
+ m_type_system_helper.ResetDeclMap(exe_ctx, result_delegate,
+ keep_result_in_memory,
+ m_ctx_obj);
+ }
+
+ lldb::ExpressionVariableSP
+ GetResultAfterDematerialization(ExecutionContextScope *exe_scope) override;
+
+ bool DidImportCxxModules() const { return m_imported_cpp_modules; }
+
+private:
+ /// Populate m_in_cplusplus_method and m_in_objectivec_method based on the
+ /// environment.
+
+ void ScanContext(ExecutionContext &exe_ctx,
+ lldb_private::Status &err) override;
+
+ bool AddArguments(ExecutionContext &exe_ctx, std::vector<lldb::addr_t> &args,
+ lldb::addr_t struct_address,
+ DiagnosticManager &diagnostic_manager) override;
+
+ std::vector<std::string> GetModulesToImport(ExecutionContext &exe_ctx);
+ void UpdateLanguageForExpr(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx,
+ std::vector<std::string> modules_to_import,
+ bool for_completion);
+ bool SetupPersistentState(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx);
+ bool PrepareForParsing(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx, bool for_completion);
+
+ ClangUserExpressionHelper m_type_system_helper;
+
+ class ResultDelegate : public Materializer::PersistentVariableDelegate {
+ public:
+ ResultDelegate(lldb::TargetSP target) : m_target_sp(target) {}
+ ConstString GetName() override;
+ void DidDematerialize(lldb::ExpressionVariableSP &variable) override;
+
+ void RegisterPersistentState(PersistentExpressionState *persistent_state);
+ lldb::ExpressionVariableSP &GetVariable();
+
+ private:
+ PersistentExpressionState *m_persistent_state;
+ lldb::ExpressionVariableSP m_variable;
+ lldb::TargetSP m_target_sp;
+ };
+
+ /// The language type of the current expression.
+ lldb::LanguageType m_expr_lang = lldb::eLanguageTypeUnknown;
+ /// The include directories that should be used when parsing the expression.
+ std::vector<ConstString> m_include_directories;
+
+ /// The absolute character position in the transformed source code where the
+ /// user code (as typed by the user) starts. If the variable is empty, then we
+ /// were not able to calculate this position.
+ llvm::Optional<size_t> m_user_expression_start_pos;
+ ResultDelegate m_result_delegate;
+
+ /// The object (if any) in which context the expression is evaluated.
+ /// See the comment to `UserExpression::Evaluate` for details.
+ ValueObject *m_ctx_obj;
+
+ /// True iff this expression explicitly imported C++ modules.
+ bool m_imported_cpp_modules = false;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangUserExpression_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp
new file mode 100644
index 000000000000..5eec224477fc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp
@@ -0,0 +1,161 @@
+//===-- ClangUtilityFunction.cpp ---------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangUtilityFunction.h"
+#include "ClangExpressionDeclMap.h"
+#include "ClangExpressionParser.h"
+#include "ClangExpressionSourceCode.h"
+
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+
+/// Constructor
+///
+/// \param[in] text
+/// The text of the function. Must be a full translation unit.
+///
+/// \param[in] name
+/// The name of the function, as used in the text.
+ClangUtilityFunction::ClangUtilityFunction(ExecutionContextScope &exe_scope,
+ const char *text, const char *name)
+ : UtilityFunction(exe_scope, text, name, eKindClangUtilityFunction) {
+ m_function_text.assign(ClangExpressionSourceCode::g_expression_prefix);
+ if (text && text[0])
+ m_function_text.append(text);
+}
+
+ClangUtilityFunction::~ClangUtilityFunction() {}
+
+/// Install the utility function into a process
+///
+/// \param[in] diagnostic_manager
+/// A diagnostic manager to report errors and warnings to.
+///
+/// \param[in] exe_ctx
+/// The execution context to install the utility function to.
+///
+/// \return
+/// True on success (no errors); false otherwise.
+bool ClangUtilityFunction::Install(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx) {
+ if (m_jit_start_addr != LLDB_INVALID_ADDRESS) {
+ diagnostic_manager.PutString(eDiagnosticSeverityWarning,
+ "already installed");
+ return false;
+ }
+
+ ////////////////////////////////////
+ // Set up the target and compiler
+ //
+
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError, "invalid target");
+ return false;
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (!process) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError, "invalid process");
+ return false;
+ }
+
+ //////////////////////////
+ // Parse the expression
+ //
+
+ bool keep_result_in_memory = false;
+
+ ResetDeclMap(exe_ctx, keep_result_in_memory);
+
+ if (!DeclMap()->WillParse(exe_ctx, nullptr)) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "current process state is unsuitable for expression parsing");
+ return false;
+ }
+
+ const bool generate_debug_info = true;
+ ClangExpressionParser parser(exe_ctx.GetBestExecutionContextScope(), *this,
+ generate_debug_info);
+
+ unsigned num_errors = parser.Parse(diagnostic_manager);
+
+ if (num_errors) {
+ ResetDeclMap();
+
+ return false;
+ }
+
+ //////////////////////////////////
+ // JIT the output of the parser
+ //
+
+ bool can_interpret = false; // should stay that way
+
+ Status jit_error = parser.PrepareForExecution(
+ m_jit_start_addr, m_jit_end_addr, m_execution_unit_sp, exe_ctx,
+ can_interpret, eExecutionPolicyAlways);
+
+ if (m_jit_start_addr != LLDB_INVALID_ADDRESS) {
+ m_jit_process_wp = process->shared_from_this();
+ if (parser.GetGenerateDebugInfo()) {
+ lldb::ModuleSP jit_module_sp(m_execution_unit_sp->GetJITModule());
+
+ if (jit_module_sp) {
+ ConstString const_func_name(FunctionName());
+ FileSpec jit_file;
+ jit_file.GetFilename() = const_func_name;
+ jit_module_sp->SetFileSpecAndObjectName(jit_file, ConstString());
+ m_jit_module_wp = jit_module_sp;
+ target->GetImages().Append(jit_module_sp);
+ }
+ }
+ }
+
+ DeclMap()->DidParse();
+
+ ResetDeclMap();
+
+ if (jit_error.Success()) {
+ return true;
+ } else {
+ const char *error_cstr = jit_error.AsCString();
+ if (error_cstr && error_cstr[0]) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError, "%s", error_cstr);
+ } else {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "expression can't be interpreted or run");
+ }
+ return false;
+ }
+}
+
+void ClangUtilityFunction::ClangUtilityFunctionHelper::ResetDeclMap(
+ ExecutionContext &exe_ctx, bool keep_result_in_memory) {
+ m_expr_decl_map_up.reset(
+ new ClangExpressionDeclMap(keep_result_in_memory, nullptr, exe_ctx,
+ nullptr));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h
new file mode 100644
index 000000000000..70ebb2f3ad8a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h
@@ -0,0 +1,107 @@
+//===-- ClangUtilityFunction.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangUtilityFunction_h_
+#define liblldb_ClangUtilityFunction_h_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "ClangExpressionHelper.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+/// \class ClangUtilityFunction ClangUtilityFunction.h
+/// "lldb/Expression/ClangUtilityFunction.h" Encapsulates a single expression
+/// for use with Clang
+///
+/// LLDB uses expressions for various purposes, notably to call functions
+/// and as a backend for the expr command. ClangUtilityFunction encapsulates
+/// a self-contained function meant to be used from other code. Utility
+/// functions can perform error-checking for ClangUserExpressions, or can
+/// simply provide a way to push a function into the target for the debugger
+/// to call later on.
+class ClangUtilityFunction : public UtilityFunction {
+public:
+ /// LLVM-style RTTI support.
+ static bool classof(const Expression *E) {
+ return E->getKind() == eKindClangUtilityFunction;
+ }
+
+ class ClangUtilityFunctionHelper : public ClangExpressionHelper {
+ public:
+ ClangUtilityFunctionHelper() {}
+
+ ~ClangUtilityFunctionHelper() override {}
+
+ /// Return the object that the parser should use when resolving external
+ /// values. May be NULL if everything should be self-contained.
+ ClangExpressionDeclMap *DeclMap() override {
+ return m_expr_decl_map_up.get();
+ }
+
+ void ResetDeclMap() { m_expr_decl_map_up.reset(); }
+
+ void ResetDeclMap(ExecutionContext &exe_ctx, bool keep_result_in_memory);
+
+ /// Return the object that the parser should allow to access ASTs. May be
+ /// NULL if the ASTs do not need to be transformed.
+ ///
+ /// \param[in] passthrough
+ /// The ASTConsumer that the returned transformer should send
+ /// the ASTs to after transformation.
+ clang::ASTConsumer *
+ ASTTransformer(clang::ASTConsumer *passthrough) override {
+ return nullptr;
+ }
+
+ private:
+ std::unique_ptr<ClangExpressionDeclMap> m_expr_decl_map_up;
+ };
+ /// Constructor
+ ///
+ /// \param[in] text
+ /// The text of the function. Must be a full translation unit.
+ ///
+ /// \param[in] name
+ /// The name of the function, as used in the text.
+ ClangUtilityFunction(ExecutionContextScope &exe_scope, const char *text,
+ const char *name);
+
+ ~ClangUtilityFunction() override;
+
+ ExpressionTypeSystemHelper *GetTypeSystemHelper() override {
+ return &m_type_system_helper;
+ }
+
+ ClangExpressionDeclMap *DeclMap() { return m_type_system_helper.DeclMap(); }
+
+ void ResetDeclMap() { m_type_system_helper.ResetDeclMap(); }
+
+ void ResetDeclMap(ExecutionContext &exe_ctx, bool keep_result_in_memory) {
+ m_type_system_helper.ResetDeclMap(exe_ctx, keep_result_in_memory);
+ }
+
+ bool Install(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx) override;
+
+private:
+ ClangUtilityFunctionHelper m_type_system_helper; ///< The map to use when
+ ///parsing and materializing
+ ///the expression.
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangUtilityFunction_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp
new file mode 100644
index 000000000000..f8e004fe7d4a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp
@@ -0,0 +1,595 @@
+//===-- IRDynamicChecks.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "IRDynamicChecks.h"
+
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+using namespace llvm;
+using namespace lldb_private;
+
+static char ID;
+
+#define VALID_POINTER_CHECK_NAME "_$__lldb_valid_pointer_check"
+#define VALID_OBJC_OBJECT_CHECK_NAME "$__lldb_objc_object_check"
+
+static const char g_valid_pointer_check_text[] =
+ "extern \"C\" void\n"
+ "_$__lldb_valid_pointer_check (unsigned char *$__lldb_arg_ptr)\n"
+ "{\n"
+ " unsigned char $__lldb_local_val = *$__lldb_arg_ptr;\n"
+ "}";
+
+ClangDynamicCheckerFunctions::ClangDynamicCheckerFunctions()
+ : DynamicCheckerFunctions(DCF_Clang) {}
+
+ClangDynamicCheckerFunctions::~ClangDynamicCheckerFunctions() = default;
+
+bool ClangDynamicCheckerFunctions::Install(
+ DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx) {
+ Status error;
+ m_valid_pointer_check.reset(
+ exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(
+ g_valid_pointer_check_text, lldb::eLanguageTypeC,
+ VALID_POINTER_CHECK_NAME, error));
+ if (error.Fail())
+ return false;
+
+ if (!m_valid_pointer_check->Install(diagnostic_manager, exe_ctx))
+ return false;
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (process) {
+ ObjCLanguageRuntime *objc_language_runtime =
+ ObjCLanguageRuntime::Get(*process);
+
+ if (objc_language_runtime) {
+ m_objc_object_check.reset(objc_language_runtime->CreateObjectChecker(
+ VALID_OBJC_OBJECT_CHECK_NAME));
+
+ if (!m_objc_object_check->Install(diagnostic_manager, exe_ctx))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ClangDynamicCheckerFunctions::DoCheckersExplainStop(lldb::addr_t addr,
+ Stream &message) {
+ // FIXME: We have to get the checkers to know why they scotched the call in
+ // more detail,
+ // so we can print a better message here.
+ if (m_valid_pointer_check && m_valid_pointer_check->ContainsAddress(addr)) {
+ message.Printf("Attempted to dereference an invalid pointer.");
+ return true;
+ } else if (m_objc_object_check &&
+ m_objc_object_check->ContainsAddress(addr)) {
+ message.Printf("Attempted to dereference an invalid ObjC Object or send it "
+ "an unrecognized selector");
+ return true;
+ }
+ return false;
+}
+
+static std::string PrintValue(llvm::Value *V, bool truncate = false) {
+ std::string s;
+ raw_string_ostream rso(s);
+ V->print(rso);
+ rso.flush();
+ if (truncate)
+ s.resize(s.length() - 1);
+ return s;
+}
+
+/// \class Instrumenter IRDynamicChecks.cpp
+/// Finds and instruments individual LLVM IR instructions
+///
+/// When instrumenting LLVM IR, it is frequently desirable to first search for
+/// instructions, and then later modify them. This way iterators remain
+/// intact, and multiple passes can look at the same code base without
+/// treading on each other's toes.
+///
+/// The Instrumenter class implements this functionality. A client first
+/// calls Inspect on a function, which populates a list of instructions to be
+/// instrumented. Then, later, when all passes' Inspect functions have been
+/// called, the client calls Instrument, which adds the desired
+/// instrumentation.
+///
+/// A subclass of Instrumenter must override InstrumentInstruction, which
+/// is responsible for adding whatever instrumentation is necessary.
+///
+/// A subclass of Instrumenter may override:
+///
+/// - InspectInstruction [default: does nothing]
+///
+/// - InspectBasicBlock [default: iterates through the instructions in a
+/// basic block calling InspectInstruction]
+///
+/// - InspectFunction [default: iterates through the basic blocks in a
+/// function calling InspectBasicBlock]
+class Instrumenter {
+public:
+ /// Constructor
+ ///
+ /// \param[in] module
+ /// The module being instrumented.
+ Instrumenter(llvm::Module &module,
+ std::shared_ptr<UtilityFunction> checker_function)
+ : m_module(module), m_checker_function(checker_function),
+ m_i8ptr_ty(nullptr), m_intptr_ty(nullptr) {}
+
+ virtual ~Instrumenter() = default;
+
+ /// Inspect a function to find instructions to instrument
+ ///
+ /// \param[in] function
+ /// The function to inspect.
+ ///
+ /// \return
+ /// True on success; false on error.
+ bool Inspect(llvm::Function &function) { return InspectFunction(function); }
+
+ /// Instrument all the instructions found by Inspect()
+ ///
+ /// \return
+ /// True on success; false on error.
+ bool Instrument() {
+ for (InstIterator ii = m_to_instrument.begin(),
+ last_ii = m_to_instrument.end();
+ ii != last_ii; ++ii) {
+ if (!InstrumentInstruction(*ii))
+ return false;
+ }
+
+ return true;
+ }
+
+protected:
+ /// Add instrumentation to a single instruction
+ ///
+ /// \param[in] inst
+ /// The instruction to be instrumented.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ virtual bool InstrumentInstruction(llvm::Instruction *inst) = 0;
+
+ /// Register a single instruction to be instrumented
+ ///
+ /// \param[in] inst
+ /// The instruction to be instrumented.
+ void RegisterInstruction(llvm::Instruction &i) {
+ m_to_instrument.push_back(&i);
+ }
+
+ /// Determine whether a single instruction is interesting to instrument,
+ /// and, if so, call RegisterInstruction
+ ///
+ /// \param[in] i
+ /// The instruction to be inspected.
+ ///
+ /// \return
+ /// False if there was an error scanning; true otherwise.
+ virtual bool InspectInstruction(llvm::Instruction &i) { return true; }
+
+ /// Scan a basic block to see if any instructions are interesting
+ ///
+ /// \param[in] bb
+ /// The basic block to be inspected.
+ ///
+ /// \return
+ /// False if there was an error scanning; true otherwise.
+ virtual bool InspectBasicBlock(llvm::BasicBlock &bb) {
+ for (llvm::BasicBlock::iterator ii = bb.begin(), last_ii = bb.end();
+ ii != last_ii; ++ii) {
+ if (!InspectInstruction(*ii))
+ return false;
+ }
+
+ return true;
+ }
+
+ /// Scan a function to see if any instructions are interesting
+ ///
+ /// \param[in] f
+ /// The function to be inspected.
+ ///
+ /// \return
+ /// False if there was an error scanning; true otherwise.
+ virtual bool InspectFunction(llvm::Function &f) {
+ for (llvm::Function::iterator bbi = f.begin(), last_bbi = f.end();
+ bbi != last_bbi; ++bbi) {
+ if (!InspectBasicBlock(*bbi))
+ return false;
+ }
+
+ return true;
+ }
+
+ /// Build a function pointer for a function with signature void
+ /// (*)(uint8_t*) with a given address
+ ///
+ /// \param[in] start_address
+ /// The address of the function.
+ ///
+ /// \return
+ /// The function pointer, for use in a CallInst.
+ llvm::FunctionCallee BuildPointerValidatorFunc(lldb::addr_t start_address) {
+ llvm::Type *param_array[1];
+
+ param_array[0] = const_cast<llvm::PointerType *>(GetI8PtrTy());
+
+ ArrayRef<llvm::Type *> params(param_array, 1);
+
+ FunctionType *fun_ty = FunctionType::get(
+ llvm::Type::getVoidTy(m_module.getContext()), params, true);
+ PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty);
+ Constant *fun_addr_int =
+ ConstantInt::get(GetIntptrTy(), start_address, false);
+ return {fun_ty, ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty)};
+ }
+
+ /// Build a function pointer for a function with signature void
+ /// (*)(uint8_t*, uint8_t*) with a given address
+ ///
+ /// \param[in] start_address
+ /// The address of the function.
+ ///
+ /// \return
+ /// The function pointer, for use in a CallInst.
+ llvm::FunctionCallee BuildObjectCheckerFunc(lldb::addr_t start_address) {
+ llvm::Type *param_array[2];
+
+ param_array[0] = const_cast<llvm::PointerType *>(GetI8PtrTy());
+ param_array[1] = const_cast<llvm::PointerType *>(GetI8PtrTy());
+
+ ArrayRef<llvm::Type *> params(param_array, 2);
+
+ FunctionType *fun_ty = FunctionType::get(
+ llvm::Type::getVoidTy(m_module.getContext()), params, true);
+ PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty);
+ Constant *fun_addr_int =
+ ConstantInt::get(GetIntptrTy(), start_address, false);
+ return {fun_ty, ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty)};
+ }
+
+ PointerType *GetI8PtrTy() {
+ if (!m_i8ptr_ty)
+ m_i8ptr_ty = llvm::Type::getInt8PtrTy(m_module.getContext());
+
+ return m_i8ptr_ty;
+ }
+
+ IntegerType *GetIntptrTy() {
+ if (!m_intptr_ty) {
+ llvm::DataLayout data_layout(&m_module);
+
+ m_intptr_ty = llvm::Type::getIntNTy(m_module.getContext(),
+ data_layout.getPointerSizeInBits());
+ }
+
+ return m_intptr_ty;
+ }
+
+ typedef std::vector<llvm::Instruction *> InstVector;
+ typedef InstVector::iterator InstIterator;
+
+ InstVector m_to_instrument; ///< List of instructions the inspector found
+ llvm::Module &m_module; ///< The module which is being instrumented
+ std::shared_ptr<UtilityFunction>
+ m_checker_function; ///< The dynamic checker function for the process
+
+private:
+ PointerType *m_i8ptr_ty;
+ IntegerType *m_intptr_ty;
+};
+
+class ValidPointerChecker : public Instrumenter {
+public:
+ ValidPointerChecker(llvm::Module &module,
+ std::shared_ptr<UtilityFunction> checker_function)
+ : Instrumenter(module, checker_function),
+ m_valid_pointer_check_func(nullptr) {}
+
+ ~ValidPointerChecker() override = default;
+
+protected:
+ bool InstrumentInstruction(llvm::Instruction *inst) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("Instrumenting load/store instruction: %s\n",
+ PrintValue(inst).c_str());
+
+ if (!m_valid_pointer_check_func)
+ m_valid_pointer_check_func =
+ BuildPointerValidatorFunc(m_checker_function->StartAddress());
+
+ llvm::Value *dereferenced_ptr = nullptr;
+
+ if (llvm::LoadInst *li = dyn_cast<llvm::LoadInst>(inst))
+ dereferenced_ptr = li->getPointerOperand();
+ else if (llvm::StoreInst *si = dyn_cast<llvm::StoreInst>(inst))
+ dereferenced_ptr = si->getPointerOperand();
+ else
+ return false;
+
+ // Insert an instruction to cast the loaded value to int8_t*
+
+ BitCastInst *bit_cast =
+ new BitCastInst(dereferenced_ptr, GetI8PtrTy(), "", inst);
+
+ // Insert an instruction to call the helper with the result
+
+ llvm::Value *arg_array[1];
+
+ arg_array[0] = bit_cast;
+
+ llvm::ArrayRef<llvm::Value *> args(arg_array, 1);
+
+ CallInst::Create(m_valid_pointer_check_func, args, "", inst);
+
+ return true;
+ }
+
+ bool InspectInstruction(llvm::Instruction &i) override {
+ if (dyn_cast<llvm::LoadInst>(&i) || dyn_cast<llvm::StoreInst>(&i))
+ RegisterInstruction(i);
+
+ return true;
+ }
+
+private:
+ llvm::FunctionCallee m_valid_pointer_check_func;
+};
+
+class ObjcObjectChecker : public Instrumenter {
+public:
+ ObjcObjectChecker(llvm::Module &module,
+ std::shared_ptr<UtilityFunction> checker_function)
+ : Instrumenter(module, checker_function),
+ m_objc_object_check_func(nullptr) {}
+
+ ~ObjcObjectChecker() override = default;
+
+ enum msgSend_type {
+ eMsgSend = 0,
+ eMsgSendSuper,
+ eMsgSendSuper_stret,
+ eMsgSend_fpret,
+ eMsgSend_stret
+ };
+
+ std::map<llvm::Instruction *, msgSend_type> msgSend_types;
+
+protected:
+ bool InstrumentInstruction(llvm::Instruction *inst) override {
+ CallInst *call_inst = dyn_cast<CallInst>(inst);
+
+ if (!call_inst)
+ return false; // call_inst really shouldn't be nullptr, because otherwise
+ // InspectInstruction wouldn't have registered it
+
+ if (!m_objc_object_check_func)
+ m_objc_object_check_func =
+ BuildObjectCheckerFunc(m_checker_function->StartAddress());
+
+ // id objc_msgSend(id theReceiver, SEL theSelector, ...)
+
+ llvm::Value *target_object;
+ llvm::Value *selector;
+
+ switch (msgSend_types[inst]) {
+ case eMsgSend:
+ case eMsgSend_fpret:
+ // On arm64, clang uses objc_msgSend for scalar and struct return
+ // calls. The call instruction will record which was used.
+ if (call_inst->hasStructRetAttr()) {
+ target_object = call_inst->getArgOperand(1);
+ selector = call_inst->getArgOperand(2);
+ } else {
+ target_object = call_inst->getArgOperand(0);
+ selector = call_inst->getArgOperand(1);
+ }
+ break;
+ case eMsgSend_stret:
+ target_object = call_inst->getArgOperand(1);
+ selector = call_inst->getArgOperand(2);
+ break;
+ case eMsgSendSuper:
+ case eMsgSendSuper_stret:
+ return true;
+ }
+
+ // These objects should always be valid according to Sean Calannan
+ assert(target_object);
+ assert(selector);
+
+ // Insert an instruction to cast the receiver id to int8_t*
+
+ BitCastInst *bit_cast =
+ new BitCastInst(target_object, GetI8PtrTy(), "", inst);
+
+ // Insert an instruction to call the helper with the result
+
+ llvm::Value *arg_array[2];
+
+ arg_array[0] = bit_cast;
+ arg_array[1] = selector;
+
+ ArrayRef<llvm::Value *> args(arg_array, 2);
+
+ CallInst::Create(m_objc_object_check_func, args, "", inst);
+
+ return true;
+ }
+
+ static llvm::Function *GetFunction(llvm::Value *value) {
+ if (llvm::Function *function = llvm::dyn_cast<llvm::Function>(value)) {
+ return function;
+ }
+
+ if (llvm::ConstantExpr *const_expr =
+ llvm::dyn_cast<llvm::ConstantExpr>(value)) {
+ switch (const_expr->getOpcode()) {
+ default:
+ return nullptr;
+ case llvm::Instruction::BitCast:
+ return GetFunction(const_expr->getOperand(0));
+ }
+ }
+
+ return nullptr;
+ }
+
+ static llvm::Function *GetCalledFunction(llvm::CallInst *inst) {
+ return GetFunction(inst->getCalledValue());
+ }
+
+ bool InspectInstruction(llvm::Instruction &i) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ CallInst *call_inst = dyn_cast<CallInst>(&i);
+
+ if (call_inst) {
+ const llvm::Function *called_function = GetCalledFunction(call_inst);
+
+ if (!called_function)
+ return true;
+
+ std::string name_str = called_function->getName().str();
+ const char *name_cstr = name_str.c_str();
+
+ if (log)
+ log->Printf("Found call to %s: %s\n", name_cstr,
+ PrintValue(call_inst).c_str());
+
+ if (name_str.find("objc_msgSend") == std::string::npos)
+ return true;
+
+ if (!strcmp(name_cstr, "objc_msgSend")) {
+ RegisterInstruction(i);
+ msgSend_types[&i] = eMsgSend;
+ return true;
+ }
+
+ if (!strcmp(name_cstr, "objc_msgSend_stret")) {
+ RegisterInstruction(i);
+ msgSend_types[&i] = eMsgSend_stret;
+ return true;
+ }
+
+ if (!strcmp(name_cstr, "objc_msgSend_fpret")) {
+ RegisterInstruction(i);
+ msgSend_types[&i] = eMsgSend_fpret;
+ return true;
+ }
+
+ if (!strcmp(name_cstr, "objc_msgSendSuper")) {
+ RegisterInstruction(i);
+ msgSend_types[&i] = eMsgSendSuper;
+ return true;
+ }
+
+ if (!strcmp(name_cstr, "objc_msgSendSuper_stret")) {
+ RegisterInstruction(i);
+ msgSend_types[&i] = eMsgSendSuper_stret;
+ return true;
+ }
+
+ if (log)
+ log->Printf(
+ "Function name '%s' contains 'objc_msgSend' but is not handled",
+ name_str.c_str());
+
+ return true;
+ }
+
+ return true;
+ }
+
+private:
+ llvm::FunctionCallee m_objc_object_check_func;
+};
+
+IRDynamicChecks::IRDynamicChecks(
+ ClangDynamicCheckerFunctions &checker_functions, const char *func_name)
+ : ModulePass(ID), m_func_name(func_name),
+ m_checker_functions(checker_functions) {}
+
+IRDynamicChecks::~IRDynamicChecks() = default;
+
+bool IRDynamicChecks::runOnModule(llvm::Module &M) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ llvm::Function *function = M.getFunction(StringRef(m_func_name));
+
+ if (!function) {
+ if (log)
+ log->Printf("Couldn't find %s() in the module", m_func_name.c_str());
+
+ return false;
+ }
+
+ if (m_checker_functions.m_valid_pointer_check) {
+ ValidPointerChecker vpc(M, m_checker_functions.m_valid_pointer_check);
+
+ if (!vpc.Inspect(*function))
+ return false;
+
+ if (!vpc.Instrument())
+ return false;
+ }
+
+ if (m_checker_functions.m_objc_object_check) {
+ ObjcObjectChecker ooc(M, m_checker_functions.m_objc_object_check);
+
+ if (!ooc.Inspect(*function))
+ return false;
+
+ if (!ooc.Instrument())
+ return false;
+ }
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ M.print(oss, nullptr);
+
+ oss.flush();
+
+ log->Printf("Module after dynamic checks: \n%s", s.c_str());
+ }
+
+ return true;
+}
+
+void IRDynamicChecks::assignPassManager(PMStack &PMS, PassManagerType T) {}
+
+PassManagerType IRDynamicChecks::getPotentialPassManagerType() const {
+ return PMT_ModulePassManager;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.h
new file mode 100644
index 000000000000..60c0691b21c1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.h
@@ -0,0 +1,131 @@
+//===-- IRDynamicChecks.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_IRDynamicChecks_h_
+#define liblldb_IRDynamicChecks_h_
+
+#include "lldb/Expression/DynamicCheckerFunctions.h"
+#include "lldb/lldb-types.h"
+#include "llvm/Pass.h"
+
+namespace llvm {
+class BasicBlock;
+class Module;
+}
+
+namespace lldb_private {
+
+class ExecutionContext;
+class Stream;
+
+class ClangDynamicCheckerFunctions
+ : public lldb_private::DynamicCheckerFunctions {
+public:
+ /// Constructor
+ ClangDynamicCheckerFunctions();
+
+ /// Destructor
+ virtual ~ClangDynamicCheckerFunctions();
+
+ static bool classof(const DynamicCheckerFunctions *checker_funcs) {
+ return checker_funcs->GetKind() == DCF_Clang;
+ }
+
+ /// Install the utility functions into a process. This binds the instance
+ /// of DynamicCheckerFunctions to that process.
+ ///
+ /// \param[in] diagnostic_manager
+ /// A diagnostic manager to report errors to.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to install the functions into.
+ ///
+ /// \return
+ /// True on success; false on failure, or if the functions have
+ /// already been installed.
+ bool Install(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx) override;
+
+ bool DoCheckersExplainStop(lldb::addr_t addr, Stream &message) override;
+
+ std::shared_ptr<UtilityFunction> m_valid_pointer_check;
+ std::shared_ptr<UtilityFunction> m_objc_object_check;
+};
+
+/// \class IRDynamicChecks IRDynamicChecks.h
+/// "lldb/Expression/IRDynamicChecks.h" Adds dynamic checks to a user-entered
+/// expression to reduce its likelihood of crashing
+///
+/// When an IR function is executed in the target process, it may cause
+/// crashes or hangs by dereferencing NULL pointers, trying to call
+/// Objective-C methods on objects that do not respond to them, and so forth.
+///
+/// IRDynamicChecks adds calls to the functions in DynamicCheckerFunctions to
+/// appropriate locations in an expression's IR.
+class IRDynamicChecks : public llvm::ModulePass {
+public:
+ /// Constructor
+ ///
+ /// \param[in] checker_functions
+ /// The checker functions for the target process.
+ ///
+ /// \param[in] func_name
+ /// The name of the function to prepare for execution in the target.
+ ///
+ /// \param[in] decl_map
+ /// The mapping used to look up entities in the target process. In
+ /// this case, used to find objc_msgSend
+ IRDynamicChecks(ClangDynamicCheckerFunctions &checker_functions,
+ const char *func_name = "$__lldb_expr");
+
+ /// Destructor
+ ~IRDynamicChecks() override;
+
+ /// Run this IR transformer on a single module
+ ///
+ /// \param[in] M
+ /// The module to run on. This module is searched for the function
+ /// $__lldb_expr, and that function is passed to the passes one by
+ /// one.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool runOnModule(llvm::Module &M) override;
+
+ /// Interface stub
+ void assignPassManager(
+ llvm::PMStack &PMS,
+ llvm::PassManagerType T = llvm::PMT_ModulePassManager) override;
+
+ /// Returns PMT_ModulePassManager
+ llvm::PassManagerType getPotentialPassManagerType() const override;
+
+private:
+ /// A basic block-level pass to find all pointer dereferences and
+ /// validate them before use.
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] M
+ /// The module currently being processed.
+ ///
+ /// \param[in] BB
+ /// The basic block currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool FindDataLoads(llvm::Module &M, llvm::BasicBlock &BB);
+
+ std::string m_func_name; ///< The name of the function to add checks to
+ ClangDynamicCheckerFunctions
+ &m_checker_functions; ///< The checker functions for the process
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_IRDynamicChecks_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
new file mode 100644
index 000000000000..07acb2e1030f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
@@ -0,0 +1,2270 @@
+//===-- IRForTarget.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IRForTarget.h"
+
+#include "ClangExpressionDeclMap.h"
+
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/ValueSymbolTable.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/IPO.h"
+
+#include "clang/AST/ASTContext.h"
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Expression/IRInterpreter.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <map>
+
+using namespace llvm;
+
+static char ID;
+
+IRForTarget::FunctionValueCache::FunctionValueCache(Maker const &maker)
+ : m_maker(maker), m_values() {}
+
+IRForTarget::FunctionValueCache::~FunctionValueCache() {}
+
+llvm::Value *
+IRForTarget::FunctionValueCache::GetValue(llvm::Function *function) {
+ if (!m_values.count(function)) {
+ llvm::Value *ret = m_maker(function);
+ m_values[function] = ret;
+ return ret;
+ }
+ return m_values[function];
+}
+
+static llvm::Value *FindEntryInstruction(llvm::Function *function) {
+ if (function->empty())
+ return nullptr;
+
+ return function->getEntryBlock().getFirstNonPHIOrDbg();
+}
+
+IRForTarget::IRForTarget(lldb_private::ClangExpressionDeclMap *decl_map,
+ bool resolve_vars,
+ lldb_private::IRExecutionUnit &execution_unit,
+ lldb_private::Stream &error_stream,
+ const char *func_name)
+ : ModulePass(ID), m_resolve_vars(resolve_vars), m_func_name(func_name),
+ m_module(nullptr), m_decl_map(decl_map),
+ m_CFStringCreateWithBytes(nullptr), m_sel_registerName(nullptr),
+ m_objc_getClass(nullptr), m_intptr_ty(nullptr),
+ m_error_stream(error_stream), m_execution_unit(execution_unit),
+ m_result_store(nullptr), m_result_is_pointer(false),
+ m_reloc_placeholder(nullptr),
+ m_entry_instruction_finder(FindEntryInstruction) {}
+
+/* Handy utility functions used at several places in the code */
+
+static std::string PrintValue(const Value *value, bool truncate = false) {
+ std::string s;
+ if (value) {
+ raw_string_ostream rso(s);
+ value->print(rso);
+ rso.flush();
+ if (truncate)
+ s.resize(s.length() - 1);
+ }
+ return s;
+}
+
+static std::string PrintType(const llvm::Type *type, bool truncate = false) {
+ std::string s;
+ raw_string_ostream rso(s);
+ type->print(rso);
+ rso.flush();
+ if (truncate)
+ s.resize(s.length() - 1);
+ return s;
+}
+
+IRForTarget::~IRForTarget() {}
+
+bool IRForTarget::FixFunctionLinkage(llvm::Function &llvm_function) {
+ llvm_function.setLinkage(GlobalValue::ExternalLinkage);
+
+ return true;
+}
+
+clang::NamedDecl *IRForTarget::DeclForGlobal(const GlobalValue *global_val,
+ Module *module) {
+ NamedMDNode *named_metadata =
+ module->getNamedMetadata("clang.global.decl.ptrs");
+
+ if (!named_metadata)
+ return nullptr;
+
+ unsigned num_nodes = named_metadata->getNumOperands();
+ unsigned node_index;
+
+ for (node_index = 0; node_index < num_nodes; ++node_index) {
+ llvm::MDNode *metadata_node =
+ dyn_cast<llvm::MDNode>(named_metadata->getOperand(node_index));
+ if (!metadata_node)
+ return nullptr;
+
+ if (metadata_node->getNumOperands() != 2)
+ continue;
+
+ if (mdconst::dyn_extract_or_null<GlobalValue>(
+ metadata_node->getOperand(0)) != global_val)
+ continue;
+
+ ConstantInt *constant_int =
+ mdconst::dyn_extract<ConstantInt>(metadata_node->getOperand(1));
+
+ if (!constant_int)
+ return nullptr;
+
+ uintptr_t ptr = constant_int->getZExtValue();
+
+ return reinterpret_cast<clang::NamedDecl *>(ptr);
+ }
+
+ return nullptr;
+}
+
+clang::NamedDecl *IRForTarget::DeclForGlobal(GlobalValue *global_val) {
+ return DeclForGlobal(global_val, m_module);
+}
+
+bool IRForTarget::CreateResultVariable(llvm::Function &llvm_function) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!m_resolve_vars)
+ return true;
+
+ // Find the result variable. If it doesn't exist, we can give up right here.
+
+ ValueSymbolTable &value_symbol_table = m_module->getValueSymbolTable();
+
+ std::string result_name_str;
+ const char *result_name = nullptr;
+
+ for (ValueSymbolTable::iterator vi = value_symbol_table.begin(),
+ ve = value_symbol_table.end();
+ vi != ve; ++vi) {
+ result_name_str = vi->first().str();
+ const char *value_name = result_name_str.c_str();
+
+ if (strstr(value_name, "$__lldb_expr_result_ptr") &&
+ strncmp(value_name, "_ZGV", 4)) {
+ result_name = value_name;
+ m_result_is_pointer = true;
+ break;
+ }
+
+ if (strstr(value_name, "$__lldb_expr_result") &&
+ strncmp(value_name, "_ZGV", 4)) {
+ result_name = value_name;
+ m_result_is_pointer = false;
+ break;
+ }
+ }
+
+ if (!result_name) {
+ if (log)
+ log->PutCString("Couldn't find result variable");
+
+ return true;
+ }
+
+ if (log)
+ log->Printf("Result name: \"%s\"", result_name);
+
+ Value *result_value = m_module->getNamedValue(result_name);
+
+ if (!result_value) {
+ if (log)
+ log->PutCString("Result variable had no data");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Result variable's "
+ "name (%s) exists, but not its definition\n",
+ result_name);
+
+ return false;
+ }
+
+ if (log)
+ log->Printf("Found result in the IR: \"%s\"",
+ PrintValue(result_value, false).c_str());
+
+ GlobalVariable *result_global = dyn_cast<GlobalVariable>(result_value);
+
+ if (!result_global) {
+ if (log)
+ log->PutCString("Result variable isn't a GlobalVariable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Result variable (%s) "
+ "is defined, but is not a global variable\n",
+ result_name);
+
+ return false;
+ }
+
+ clang::NamedDecl *result_decl = DeclForGlobal(result_global);
+ if (!result_decl) {
+ if (log)
+ log->PutCString("Result variable doesn't have a corresponding Decl");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Result variable (%s) "
+ "does not have a corresponding Clang entity\n",
+ result_name);
+
+ return false;
+ }
+
+ if (log) {
+ std::string decl_desc_str;
+ raw_string_ostream decl_desc_stream(decl_desc_str);
+ result_decl->print(decl_desc_stream);
+ decl_desc_stream.flush();
+
+ log->Printf("Found result decl: \"%s\"", decl_desc_str.c_str());
+ }
+
+ clang::VarDecl *result_var = dyn_cast<clang::VarDecl>(result_decl);
+ if (!result_var) {
+ if (log)
+ log->PutCString("Result variable Decl isn't a VarDecl");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Result variable "
+ "(%s)'s corresponding Clang entity isn't a "
+ "variable\n",
+ result_name);
+
+ return false;
+ }
+
+ // Get the next available result name from m_decl_map and create the
+ // persistent variable for it
+
+ // If the result is an Lvalue, it is emitted as a pointer; see
+ // ASTResultSynthesizer::SynthesizeBodyResult.
+ if (m_result_is_pointer) {
+ clang::QualType pointer_qual_type = result_var->getType();
+ const clang::Type *pointer_type = pointer_qual_type.getTypePtr();
+
+ const clang::PointerType *pointer_pointertype =
+ pointer_type->getAs<clang::PointerType>();
+ const clang::ObjCObjectPointerType *pointer_objcobjpointertype =
+ pointer_type->getAs<clang::ObjCObjectPointerType>();
+
+ if (pointer_pointertype) {
+ clang::QualType element_qual_type = pointer_pointertype->getPointeeType();
+
+ m_result_type = lldb_private::TypeFromParser(
+ element_qual_type.getAsOpaquePtr(),
+ lldb_private::ClangASTContext::GetASTContext(
+ &result_decl->getASTContext()));
+ } else if (pointer_objcobjpointertype) {
+ clang::QualType element_qual_type =
+ clang::QualType(pointer_objcobjpointertype->getObjectType(), 0);
+
+ m_result_type = lldb_private::TypeFromParser(
+ element_qual_type.getAsOpaquePtr(),
+ lldb_private::ClangASTContext::GetASTContext(
+ &result_decl->getASTContext()));
+ } else {
+ if (log)
+ log->PutCString("Expected result to have pointer type, but it did not");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Lvalue result (%s) "
+ "is not a pointer variable\n",
+ result_name);
+
+ return false;
+ }
+ } else {
+ m_result_type = lldb_private::TypeFromParser(
+ result_var->getType().getAsOpaquePtr(),
+ lldb_private::ClangASTContext::GetASTContext(
+ &result_decl->getASTContext()));
+ }
+
+ lldb::TargetSP target_sp(m_execution_unit.GetTarget());
+ lldb_private::ExecutionContext exe_ctx(target_sp, true);
+ llvm::Optional<uint64_t> bit_size =
+ m_result_type.GetBitSize(exe_ctx.GetBestExecutionContextScope());
+ if (!bit_size) {
+ lldb_private::StreamString type_desc_stream;
+ m_result_type.DumpTypeDescription(&type_desc_stream);
+
+ if (log)
+ log->Printf("Result type has unknown size");
+
+ m_error_stream.Printf("Error [IRForTarget]: Size of result type '%s' "
+ "couldn't be determined\n",
+ type_desc_stream.GetData());
+ return false;
+ }
+
+ if (log) {
+ lldb_private::StreamString type_desc_stream;
+ m_result_type.DumpTypeDescription(&type_desc_stream);
+
+ log->Printf("Result decl type: \"%s\"", type_desc_stream.GetData());
+ }
+
+ m_result_name = lldb_private::ConstString("$RESULT_NAME");
+
+ if (log)
+ log->Printf("Creating a new result global: \"%s\" with size 0x%" PRIx64,
+ m_result_name.GetCString(),
+ m_result_type.GetByteSize(nullptr).getValueOr(0));
+
+ // Construct a new result global and set up its metadata
+
+ GlobalVariable *new_result_global = new GlobalVariable(
+ (*m_module), result_global->getType()->getElementType(),
+ false, /* not constant */
+ GlobalValue::ExternalLinkage, nullptr, /* no initializer */
+ m_result_name.GetCString());
+
+ // It's too late in compilation to create a new VarDecl for this, but we
+ // don't need to. We point the metadata at the old VarDecl. This creates an
+ // odd anomaly: a variable with a Value whose name is something like $0 and a
+ // Decl whose name is $__lldb_expr_result. This condition is handled in
+ // ClangExpressionDeclMap::DoMaterialize, and the name of the variable is
+ // fixed up.
+
+ ConstantInt *new_constant_int =
+ ConstantInt::get(llvm::Type::getInt64Ty(m_module->getContext()),
+ reinterpret_cast<uint64_t>(result_decl), false);
+
+ llvm::Metadata *values[2];
+ values[0] = ConstantAsMetadata::get(new_result_global);
+ values[1] = ConstantAsMetadata::get(new_constant_int);
+
+ ArrayRef<Metadata *> value_ref(values, 2);
+
+ MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref);
+ NamedMDNode *named_metadata =
+ m_module->getNamedMetadata("clang.global.decl.ptrs");
+ named_metadata->addOperand(persistent_global_md);
+
+ if (log)
+ log->Printf("Replacing \"%s\" with \"%s\"",
+ PrintValue(result_global).c_str(),
+ PrintValue(new_result_global).c_str());
+
+ if (result_global->use_empty()) {
+ // We need to synthesize a store for this variable, because otherwise
+ // there's nothing to put into its equivalent persistent variable.
+
+ BasicBlock &entry_block(llvm_function.getEntryBlock());
+ Instruction *first_entry_instruction(entry_block.getFirstNonPHIOrDbg());
+
+ if (!first_entry_instruction)
+ return false;
+
+ if (!result_global->hasInitializer()) {
+ if (log)
+ log->Printf("Couldn't find initializer for unused variable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Result variable "
+ "(%s) has no writes and no initializer\n",
+ result_name);
+
+ return false;
+ }
+
+ Constant *initializer = result_global->getInitializer();
+
+ StoreInst *synthesized_store =
+ new StoreInst(initializer, new_result_global, first_entry_instruction);
+
+ if (log)
+ log->Printf("Synthesized result store \"%s\"\n",
+ PrintValue(synthesized_store).c_str());
+ } else {
+ result_global->replaceAllUsesWith(new_result_global);
+ }
+
+ if (!m_decl_map->AddPersistentVariable(
+ result_decl, m_result_name, m_result_type, true, m_result_is_pointer))
+ return false;
+
+ result_global->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewriteObjCConstString(llvm::GlobalVariable *ns_str,
+ llvm::GlobalVariable *cstr) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ Type *ns_str_ty = ns_str->getType();
+
+ Type *i8_ptr_ty = Type::getInt8PtrTy(m_module->getContext());
+ Type *i32_ty = Type::getInt32Ty(m_module->getContext());
+ Type *i8_ty = Type::getInt8Ty(m_module->getContext());
+
+ if (!m_CFStringCreateWithBytes) {
+ lldb::addr_t CFStringCreateWithBytes_addr;
+
+ static lldb_private::ConstString g_CFStringCreateWithBytes_str(
+ "CFStringCreateWithBytes");
+
+ bool missing_weak = false;
+ CFStringCreateWithBytes_addr =
+ m_execution_unit.FindSymbol(g_CFStringCreateWithBytes_str,
+ missing_weak);
+ if (CFStringCreateWithBytes_addr == LLDB_INVALID_ADDRESS || missing_weak) {
+ if (log)
+ log->PutCString("Couldn't find CFStringCreateWithBytes in the target");
+
+ m_error_stream.Printf("Error [IRForTarget]: Rewriting an Objective-C "
+ "constant string requires "
+ "CFStringCreateWithBytes\n");
+
+ return false;
+ }
+
+ if (log)
+ log->Printf("Found CFStringCreateWithBytes at 0x%" PRIx64,
+ CFStringCreateWithBytes_addr);
+
+ // Build the function type:
+ //
+ // CFStringRef CFStringCreateWithBytes (
+ // CFAllocatorRef alloc,
+ // const UInt8 *bytes,
+ // CFIndex numBytes,
+ // CFStringEncoding encoding,
+ // Boolean isExternalRepresentation
+ // );
+ //
+ // We make the following substitutions:
+ //
+ // CFStringRef -> i8*
+ // CFAllocatorRef -> i8*
+ // UInt8 * -> i8*
+ // CFIndex -> long (i32 or i64, as appropriate; we ask the module for its
+ // pointer size for now) CFStringEncoding -> i32 Boolean -> i8
+
+ Type *arg_type_array[5];
+
+ arg_type_array[0] = i8_ptr_ty;
+ arg_type_array[1] = i8_ptr_ty;
+ arg_type_array[2] = m_intptr_ty;
+ arg_type_array[3] = i32_ty;
+ arg_type_array[4] = i8_ty;
+
+ ArrayRef<Type *> CFSCWB_arg_types(arg_type_array, 5);
+
+ llvm::FunctionType *CFSCWB_ty =
+ FunctionType::get(ns_str_ty, CFSCWB_arg_types, false);
+
+ // Build the constant containing the pointer to the function
+ PointerType *CFSCWB_ptr_ty = PointerType::getUnqual(CFSCWB_ty);
+ Constant *CFSCWB_addr_int =
+ ConstantInt::get(m_intptr_ty, CFStringCreateWithBytes_addr, false);
+ m_CFStringCreateWithBytes = {
+ CFSCWB_ty, ConstantExpr::getIntToPtr(CFSCWB_addr_int, CFSCWB_ptr_ty)};
+ }
+
+ ConstantDataSequential *string_array = nullptr;
+
+ if (cstr)
+ string_array = dyn_cast<ConstantDataSequential>(cstr->getInitializer());
+
+ Constant *alloc_arg = Constant::getNullValue(i8_ptr_ty);
+ Constant *bytes_arg = cstr ? ConstantExpr::getBitCast(cstr, i8_ptr_ty)
+ : Constant::getNullValue(i8_ptr_ty);
+ Constant *numBytes_arg = ConstantInt::get(
+ m_intptr_ty, cstr ? (string_array->getNumElements() - 1) * string_array->getElementByteSize() : 0, false);
+ int encoding_flags = 0;
+ switch (cstr ? string_array->getElementByteSize() : 1) {
+ case 1:
+ encoding_flags = 0x08000100; /* 0x08000100 is kCFStringEncodingUTF8 */
+ break;
+ case 2:
+ encoding_flags = 0x0100; /* 0x0100 is kCFStringEncodingUTF16 */
+ break;
+ case 4:
+ encoding_flags = 0x0c000100; /* 0x0c000100 is kCFStringEncodingUTF32 */
+ break;
+ default:
+ encoding_flags = 0x0600; /* fall back to 0x0600, kCFStringEncodingASCII */
+ LLDB_LOG(log, "Encountered an Objective-C constant string with unusual "
+ "element size {0}",
+ string_array->getElementByteSize());
+ }
+ Constant *encoding_arg = ConstantInt::get(i32_ty, encoding_flags, false);
+ Constant *isExternal_arg =
+ ConstantInt::get(i8_ty, 0x0, false); /* 0x0 is false */
+
+ Value *argument_array[5];
+
+ argument_array[0] = alloc_arg;
+ argument_array[1] = bytes_arg;
+ argument_array[2] = numBytes_arg;
+ argument_array[3] = encoding_arg;
+ argument_array[4] = isExternal_arg;
+
+ ArrayRef<Value *> CFSCWB_arguments(argument_array, 5);
+
+ FunctionValueCache CFSCWB_Caller(
+ [this, &CFSCWB_arguments](llvm::Function *function) -> llvm::Value * {
+ return CallInst::Create(
+ m_CFStringCreateWithBytes, CFSCWB_arguments,
+ "CFStringCreateWithBytes",
+ llvm::cast<Instruction>(
+ m_entry_instruction_finder.GetValue(function)));
+ });
+
+ if (!UnfoldConstant(ns_str, nullptr, CFSCWB_Caller, m_entry_instruction_finder,
+ m_error_stream)) {
+ if (log)
+ log->PutCString(
+ "Couldn't replace the NSString with the result of the call");
+
+ m_error_stream.Printf("error [IRForTarget internal]: Couldn't replace an "
+ "Objective-C constant string with a dynamic "
+ "string\n");
+
+ return false;
+ }
+
+ ns_str->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewriteObjCConstStrings() {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ ValueSymbolTable &value_symbol_table = m_module->getValueSymbolTable();
+
+ for (ValueSymbolTable::iterator vi = value_symbol_table.begin(),
+ ve = value_symbol_table.end();
+ vi != ve; ++vi) {
+ std::string value_name = vi->first().str();
+ const char *value_name_cstr = value_name.c_str();
+
+ if (strstr(value_name_cstr, "_unnamed_cfstring_")) {
+ Value *nsstring_value = vi->second;
+
+ GlobalVariable *nsstring_global =
+ dyn_cast<GlobalVariable>(nsstring_value);
+
+ if (!nsstring_global) {
+ if (log)
+ log->PutCString("NSString variable is not a GlobalVariable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string is not a global variable\n");
+
+ return false;
+ }
+
+ if (!nsstring_global->hasInitializer()) {
+ if (log)
+ log->PutCString("NSString variable does not have an initializer");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string does not have an initializer\n");
+
+ return false;
+ }
+
+ ConstantStruct *nsstring_struct =
+ dyn_cast<ConstantStruct>(nsstring_global->getInitializer());
+
+ if (!nsstring_struct) {
+ if (log)
+ log->PutCString(
+ "NSString variable's initializer is not a ConstantStruct");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string is not a structure constant\n");
+
+ return false;
+ }
+
+ // We expect the following structure:
+ //
+ // struct {
+ // int *isa;
+ // int flags;
+ // char *str;
+ // long length;
+ // };
+
+ if (nsstring_struct->getNumOperands() != 4) {
+ if (log)
+ log->Printf("NSString variable's initializer structure has an "
+ "unexpected number of members. Should be 4, is %d",
+ nsstring_struct->getNumOperands());
+
+ m_error_stream.Printf("Internal error [IRForTarget]: The struct for an "
+ "Objective-C constant string is not as "
+ "expected\n");
+
+ return false;
+ }
+
+ Constant *nsstring_member = nsstring_struct->getOperand(2);
+
+ if (!nsstring_member) {
+ if (log)
+ log->PutCString("NSString initializer's str element was empty");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string does not have a string "
+ "initializer\n");
+
+ return false;
+ }
+
+ ConstantExpr *nsstring_expr = dyn_cast<ConstantExpr>(nsstring_member);
+
+ if (!nsstring_expr) {
+ if (log)
+ log->PutCString(
+ "NSString initializer's str element is not a ConstantExpr");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string's string initializer is not "
+ "constant\n");
+
+ return false;
+ }
+
+ GlobalVariable *cstr_global = nullptr;
+
+ if (nsstring_expr->getOpcode() == Instruction::GetElementPtr) {
+ Constant *nsstring_cstr = nsstring_expr->getOperand(0);
+ cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr);
+ } else if (nsstring_expr->getOpcode() == Instruction::BitCast) {
+ Constant *nsstring_cstr = nsstring_expr->getOperand(0);
+ cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr);
+ }
+
+ if (!cstr_global) {
+ if (log)
+ log->PutCString(
+ "NSString initializer's str element is not a GlobalVariable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Unhandled"
+ "constant string initializer\n");
+
+ return false;
+ }
+
+ if (!cstr_global->hasInitializer()) {
+ if (log)
+ log->PutCString("NSString initializer's str element does not have an "
+ "initializer");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string's string initializer doesn't "
+ "point to initialized data\n");
+
+ return false;
+ }
+
+ /*
+ if (!cstr_array)
+ {
+ if (log)
+ log->PutCString("NSString initializer's str element is not a
+ ConstantArray");
+
+ if (m_error_stream)
+ m_error_stream.Printf("Internal error [IRForTarget]: An
+ Objective-C constant string's string initializer doesn't point to an
+ array\n");
+
+ return false;
+ }
+
+ if (!cstr_array->isCString())
+ {
+ if (log)
+ log->PutCString("NSString initializer's str element is not a C
+ string array");
+
+ if (m_error_stream)
+ m_error_stream.Printf("Internal error [IRForTarget]: An
+ Objective-C constant string's string initializer doesn't point to a C
+ string\n");
+
+ return false;
+ }
+ */
+
+ ConstantDataArray *cstr_array =
+ dyn_cast<ConstantDataArray>(cstr_global->getInitializer());
+
+ if (log) {
+ if (cstr_array)
+ log->Printf("Found NSString constant %s, which contains \"%s\"",
+ value_name_cstr, cstr_array->getAsString().str().c_str());
+ else
+ log->Printf("Found NSString constant %s, which contains \"\"",
+ value_name_cstr);
+ }
+
+ if (!cstr_array)
+ cstr_global = nullptr;
+
+ if (!RewriteObjCConstString(nsstring_global, cstr_global)) {
+ if (log)
+ log->PutCString("Error rewriting the constant string");
+
+ // We don't print an error message here because RewriteObjCConstString
+ // has done so for us.
+
+ return false;
+ }
+ }
+ }
+
+ for (ValueSymbolTable::iterator vi = value_symbol_table.begin(),
+ ve = value_symbol_table.end();
+ vi != ve; ++vi) {
+ std::string value_name = vi->first().str();
+ const char *value_name_cstr = value_name.c_str();
+
+ if (!strcmp(value_name_cstr, "__CFConstantStringClassReference")) {
+ GlobalVariable *gv = dyn_cast<GlobalVariable>(vi->second);
+
+ if (!gv) {
+ if (log)
+ log->PutCString(
+ "__CFConstantStringClassReference is not a global variable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Found a "
+ "CFConstantStringClassReference, but it is not a "
+ "global object\n");
+
+ return false;
+ }
+
+ gv->eraseFromParent();
+
+ break;
+ }
+ }
+
+ return true;
+}
+
+static bool IsObjCSelectorRef(Value *value) {
+ GlobalVariable *global_variable = dyn_cast<GlobalVariable>(value);
+
+ return !(!global_variable || !global_variable->hasName() ||
+ !global_variable->getName().startswith("OBJC_SELECTOR_REFERENCES_"));
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::RewriteObjCSelector(Instruction *selector_load) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ LoadInst *load = dyn_cast<LoadInst>(selector_load);
+
+ if (!load)
+ return false;
+
+ // Unpack the message name from the selector. In LLVM IR, an objc_msgSend
+ // gets represented as
+ //
+ // %tmp = load i8** @"OBJC_SELECTOR_REFERENCES_" ; <i8*> %call = call
+ // i8* (i8*, i8*, ...)* @objc_msgSend(i8* %obj, i8* %tmp, ...) ; <i8*>
+ //
+ // where %obj is the object pointer and %tmp is the selector.
+ //
+ // @"OBJC_SELECTOR_REFERENCES_" is a pointer to a character array called
+ // @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_".
+ // @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_" contains the string.
+
+ // Find the pointer's initializer (a ConstantExpr with opcode GetElementPtr)
+ // and get the string from its target
+
+ GlobalVariable *_objc_selector_references_ =
+ dyn_cast<GlobalVariable>(load->getPointerOperand());
+
+ if (!_objc_selector_references_ ||
+ !_objc_selector_references_->hasInitializer())
+ return false;
+
+ Constant *osr_initializer = _objc_selector_references_->getInitializer();
+
+ ConstantExpr *osr_initializer_expr = dyn_cast<ConstantExpr>(osr_initializer);
+
+ if (!osr_initializer_expr ||
+ osr_initializer_expr->getOpcode() != Instruction::GetElementPtr)
+ return false;
+
+ Value *osr_initializer_base = osr_initializer_expr->getOperand(0);
+
+ if (!osr_initializer_base)
+ return false;
+
+ // Find the string's initializer (a ConstantArray) and get the string from it
+
+ GlobalVariable *_objc_meth_var_name_ =
+ dyn_cast<GlobalVariable>(osr_initializer_base);
+
+ if (!_objc_meth_var_name_ || !_objc_meth_var_name_->hasInitializer())
+ return false;
+
+ Constant *omvn_initializer = _objc_meth_var_name_->getInitializer();
+
+ ConstantDataArray *omvn_initializer_array =
+ dyn_cast<ConstantDataArray>(omvn_initializer);
+
+ if (!omvn_initializer_array->isString())
+ return false;
+
+ std::string omvn_initializer_string = omvn_initializer_array->getAsString();
+
+ if (log)
+ log->Printf("Found Objective-C selector reference \"%s\"",
+ omvn_initializer_string.c_str());
+
+ // Construct a call to sel_registerName
+
+ if (!m_sel_registerName) {
+ lldb::addr_t sel_registerName_addr;
+
+ bool missing_weak = false;
+ static lldb_private::ConstString g_sel_registerName_str("sel_registerName");
+ sel_registerName_addr = m_execution_unit.FindSymbol(g_sel_registerName_str,
+ missing_weak);
+ if (sel_registerName_addr == LLDB_INVALID_ADDRESS || missing_weak)
+ return false;
+
+ if (log)
+ log->Printf("Found sel_registerName at 0x%" PRIx64,
+ sel_registerName_addr);
+
+ // Build the function type: struct objc_selector
+ // *sel_registerName(uint8_t*)
+
+ // The below code would be "more correct," but in actuality what's required
+ // is uint8_t*
+ // Type *sel_type = StructType::get(m_module->getContext());
+ // Type *sel_ptr_type = PointerType::getUnqual(sel_type);
+ Type *sel_ptr_type = Type::getInt8PtrTy(m_module->getContext());
+
+ Type *type_array[1];
+
+ type_array[0] = llvm::Type::getInt8PtrTy(m_module->getContext());
+
+ ArrayRef<Type *> srN_arg_types(type_array, 1);
+
+ llvm::FunctionType *srN_type =
+ FunctionType::get(sel_ptr_type, srN_arg_types, false);
+
+ // Build the constant containing the pointer to the function
+ PointerType *srN_ptr_ty = PointerType::getUnqual(srN_type);
+ Constant *srN_addr_int =
+ ConstantInt::get(m_intptr_ty, sel_registerName_addr, false);
+ m_sel_registerName = {srN_type,
+ ConstantExpr::getIntToPtr(srN_addr_int, srN_ptr_ty)};
+ }
+
+ Value *argument_array[1];
+
+ Constant *omvn_pointer = ConstantExpr::getBitCast(
+ _objc_meth_var_name_, Type::getInt8PtrTy(m_module->getContext()));
+
+ argument_array[0] = omvn_pointer;
+
+ ArrayRef<Value *> srN_arguments(argument_array, 1);
+
+ CallInst *srN_call = CallInst::Create(m_sel_registerName, srN_arguments,
+ "sel_registerName", selector_load);
+
+ // Replace the load with the call in all users
+
+ selector_load->replaceAllUsesWith(srN_call);
+
+ selector_load->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewriteObjCSelectors(BasicBlock &basic_block) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ BasicBlock::iterator ii;
+
+ typedef SmallVector<Instruction *, 2> InstrList;
+ typedef InstrList::iterator InstrIterator;
+
+ InstrList selector_loads;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ if (LoadInst *load = dyn_cast<LoadInst>(&inst))
+ if (IsObjCSelectorRef(load->getPointerOperand()))
+ selector_loads.push_back(&inst);
+ }
+
+ InstrIterator iter;
+
+ for (iter = selector_loads.begin(); iter != selector_loads.end(); ++iter) {
+ if (!RewriteObjCSelector(*iter)) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't change a "
+ "static reference to an Objective-C selector to a "
+ "dynamic reference\n");
+
+ if (log)
+ log->PutCString(
+ "Couldn't rewrite a reference to an Objective-C selector");
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool IsObjCClassReference(Value *value) {
+ GlobalVariable *global_variable = dyn_cast<GlobalVariable>(value);
+
+ return !(!global_variable || !global_variable->hasName() ||
+ !global_variable->getName().startswith("OBJC_CLASS_REFERENCES_"));
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::RewriteObjCClassReference(Instruction *class_load) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ LoadInst *load = dyn_cast<LoadInst>(class_load);
+
+ if (!load)
+ return false;
+
+ // Unpack the class name from the reference. In LLVM IR, a reference to an
+ // Objective-C class gets represented as
+ //
+ // %tmp = load %struct._objc_class*,
+ // %struct._objc_class** @OBJC_CLASS_REFERENCES_, align 4
+ //
+ // @"OBJC_CLASS_REFERENCES_ is a bitcast of a character array called
+ // @OBJC_CLASS_NAME_. @OBJC_CLASS_NAME contains the string.
+
+ // Find the pointer's initializer (a ConstantExpr with opcode BitCast) and
+ // get the string from its target
+
+ GlobalVariable *_objc_class_references_ =
+ dyn_cast<GlobalVariable>(load->getPointerOperand());
+
+ if (!_objc_class_references_ ||
+ !_objc_class_references_->hasInitializer())
+ return false;
+
+ Constant *ocr_initializer = _objc_class_references_->getInitializer();
+
+ ConstantExpr *ocr_initializer_expr = dyn_cast<ConstantExpr>(ocr_initializer);
+
+ if (!ocr_initializer_expr ||
+ ocr_initializer_expr->getOpcode() != Instruction::BitCast)
+ return false;
+
+ Value *ocr_initializer_base = ocr_initializer_expr->getOperand(0);
+
+ if (!ocr_initializer_base)
+ return false;
+
+ // Find the string's initializer (a ConstantArray) and get the string from it
+
+ GlobalVariable *_objc_class_name_ =
+ dyn_cast<GlobalVariable>(ocr_initializer_base);
+
+ if (!_objc_class_name_ || !_objc_class_name_->hasInitializer())
+ return false;
+
+ Constant *ocn_initializer = _objc_class_name_->getInitializer();
+
+ ConstantDataArray *ocn_initializer_array =
+ dyn_cast<ConstantDataArray>(ocn_initializer);
+
+ if (!ocn_initializer_array->isString())
+ return false;
+
+ std::string ocn_initializer_string = ocn_initializer_array->getAsString();
+
+ if (log)
+ log->Printf("Found Objective-C class reference \"%s\"",
+ ocn_initializer_string.c_str());
+
+ // Construct a call to objc_getClass
+
+ if (!m_objc_getClass) {
+ lldb::addr_t objc_getClass_addr;
+
+ bool missing_weak = false;
+ static lldb_private::ConstString g_objc_getClass_str("objc_getClass");
+ objc_getClass_addr = m_execution_unit.FindSymbol(g_objc_getClass_str,
+ missing_weak);
+ if (objc_getClass_addr == LLDB_INVALID_ADDRESS || missing_weak)
+ return false;
+
+ if (log)
+ log->Printf("Found objc_getClass at 0x%" PRIx64,
+ objc_getClass_addr);
+
+ // Build the function type: %struct._objc_class *objc_getClass(i8*)
+
+ Type *class_type = load->getType();
+ Type *type_array[1];
+ type_array[0] = llvm::Type::getInt8PtrTy(m_module->getContext());
+
+ ArrayRef<Type *> ogC_arg_types(type_array, 1);
+
+ llvm::FunctionType *ogC_type =
+ FunctionType::get(class_type, ogC_arg_types, false);
+
+ // Build the constant containing the pointer to the function
+ PointerType *ogC_ptr_ty = PointerType::getUnqual(ogC_type);
+ Constant *ogC_addr_int =
+ ConstantInt::get(m_intptr_ty, objc_getClass_addr, false);
+ m_objc_getClass = {ogC_type,
+ ConstantExpr::getIntToPtr(ogC_addr_int, ogC_ptr_ty)};
+ }
+
+ Value *argument_array[1];
+
+ Constant *ocn_pointer = ConstantExpr::getBitCast(
+ _objc_class_name_, Type::getInt8PtrTy(m_module->getContext()));
+
+ argument_array[0] = ocn_pointer;
+
+ ArrayRef<Value *> ogC_arguments(argument_array, 1);
+
+ CallInst *ogC_call = CallInst::Create(m_objc_getClass, ogC_arguments,
+ "objc_getClass", class_load);
+
+ // Replace the load with the call in all users
+
+ class_load->replaceAllUsesWith(ogC_call);
+
+ class_load->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewriteObjCClassReferences(BasicBlock &basic_block) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ BasicBlock::iterator ii;
+
+ typedef SmallVector<Instruction *, 2> InstrList;
+ typedef InstrList::iterator InstrIterator;
+
+ InstrList class_loads;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ if (LoadInst *load = dyn_cast<LoadInst>(&inst))
+ if (IsObjCClassReference(load->getPointerOperand()))
+ class_loads.push_back(&inst);
+ }
+
+ InstrIterator iter;
+
+ for (iter = class_loads.begin(); iter != class_loads.end(); ++iter) {
+ if (!RewriteObjCClassReference(*iter)) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't change a "
+ "static reference to an Objective-C class to a "
+ "dynamic reference\n");
+
+ if (log)
+ log->PutCString(
+ "Couldn't rewrite a reference to an Objective-C class");
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::RewritePersistentAlloc(llvm::Instruction *persistent_alloc) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ AllocaInst *alloc = dyn_cast<AllocaInst>(persistent_alloc);
+
+ MDNode *alloc_md = alloc->getMetadata("clang.decl.ptr");
+
+ if (!alloc_md || !alloc_md->getNumOperands())
+ return false;
+
+ ConstantInt *constant_int =
+ mdconst::dyn_extract<ConstantInt>(alloc_md->getOperand(0));
+
+ if (!constant_int)
+ return false;
+
+ // We attempt to register this as a new persistent variable with the DeclMap.
+
+ uintptr_t ptr = constant_int->getZExtValue();
+
+ clang::VarDecl *decl = reinterpret_cast<clang::VarDecl *>(ptr);
+
+ lldb_private::TypeFromParser result_decl_type(
+ decl->getType().getAsOpaquePtr(),
+ lldb_private::ClangASTContext::GetASTContext(&decl->getASTContext()));
+
+ StringRef decl_name(decl->getName());
+ lldb_private::ConstString persistent_variable_name(decl_name.data(),
+ decl_name.size());
+ if (!m_decl_map->AddPersistentVariable(decl, persistent_variable_name,
+ result_decl_type, false, false))
+ return false;
+
+ GlobalVariable *persistent_global = new GlobalVariable(
+ (*m_module), alloc->getType(), false, /* not constant */
+ GlobalValue::ExternalLinkage, nullptr, /* no initializer */
+ alloc->getName().str());
+
+ // What we're going to do here is make believe this was a regular old
+ // external variable. That means we need to make the metadata valid.
+
+ NamedMDNode *named_metadata =
+ m_module->getOrInsertNamedMetadata("clang.global.decl.ptrs");
+
+ llvm::Metadata *values[2];
+ values[0] = ConstantAsMetadata::get(persistent_global);
+ values[1] = ConstantAsMetadata::get(constant_int);
+
+ ArrayRef<llvm::Metadata *> value_ref(values, 2);
+
+ MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref);
+ named_metadata->addOperand(persistent_global_md);
+
+ // Now, since the variable is a pointer variable, we will drop in a load of
+ // that pointer variable.
+
+ LoadInst *persistent_load = new LoadInst(persistent_global, "", alloc);
+
+ if (log)
+ log->Printf("Replacing \"%s\" with \"%s\"", PrintValue(alloc).c_str(),
+ PrintValue(persistent_load).c_str());
+
+ alloc->replaceAllUsesWith(persistent_load);
+ alloc->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewritePersistentAllocs(llvm::BasicBlock &basic_block) {
+ if (!m_resolve_vars)
+ return true;
+
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ BasicBlock::iterator ii;
+
+ typedef SmallVector<Instruction *, 2> InstrList;
+ typedef InstrList::iterator InstrIterator;
+
+ InstrList pvar_allocs;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ if (AllocaInst *alloc = dyn_cast<AllocaInst>(&inst)) {
+ llvm::StringRef alloc_name = alloc->getName();
+
+ if (alloc_name.startswith("$") && !alloc_name.startswith("$__lldb")) {
+ if (alloc_name.find_first_of("0123456789") == 1) {
+ if (log)
+ log->Printf("Rejecting a numeric persistent variable.");
+
+ m_error_stream.Printf("Error [IRForTarget]: Names starting with $0, "
+ "$1, ... are reserved for use as result "
+ "names\n");
+
+ return false;
+ }
+
+ pvar_allocs.push_back(alloc);
+ }
+ }
+ }
+
+ InstrIterator iter;
+
+ for (iter = pvar_allocs.begin(); iter != pvar_allocs.end(); ++iter) {
+ if (!RewritePersistentAlloc(*iter)) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't rewrite "
+ "the creation of a persistent variable\n");
+
+ if (log)
+ log->PutCString(
+ "Couldn't rewrite the creation of a persistent variable");
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool IRForTarget::MaterializeInitializer(uint8_t *data, Constant *initializer) {
+ if (!initializer)
+ return true;
+
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log && log->GetVerbose())
+ log->Printf(" MaterializeInitializer(%p, %s)", (void *)data,
+ PrintValue(initializer).c_str());
+
+ Type *initializer_type = initializer->getType();
+
+ if (ConstantInt *int_initializer = dyn_cast<ConstantInt>(initializer)) {
+ size_t constant_size = m_target_data->getTypeStoreSize(initializer_type);
+ lldb_private::Scalar scalar = int_initializer->getValue().zextOrTrunc(
+ llvm::NextPowerOf2(constant_size) * 8);
+
+ lldb_private::Status get_data_error;
+ return scalar.GetAsMemoryData(data, constant_size,
+ lldb_private::endian::InlHostByteOrder(),
+ get_data_error) != 0;
+ } else if (ConstantDataArray *array_initializer =
+ dyn_cast<ConstantDataArray>(initializer)) {
+ if (array_initializer->isString()) {
+ std::string array_initializer_string = array_initializer->getAsString();
+ memcpy(data, array_initializer_string.c_str(),
+ m_target_data->getTypeStoreSize(initializer_type));
+ } else {
+ ArrayType *array_initializer_type = array_initializer->getType();
+ Type *array_element_type = array_initializer_type->getElementType();
+
+ size_t element_size = m_target_data->getTypeAllocSize(array_element_type);
+
+ for (unsigned i = 0; i < array_initializer->getNumOperands(); ++i) {
+ Value *operand_value = array_initializer->getOperand(i);
+ Constant *operand_constant = dyn_cast<Constant>(operand_value);
+
+ if (!operand_constant)
+ return false;
+
+ if (!MaterializeInitializer(data + (i * element_size),
+ operand_constant))
+ return false;
+ }
+ }
+ return true;
+ } else if (ConstantStruct *struct_initializer =
+ dyn_cast<ConstantStruct>(initializer)) {
+ StructType *struct_initializer_type = struct_initializer->getType();
+ const StructLayout *struct_layout =
+ m_target_data->getStructLayout(struct_initializer_type);
+
+ for (unsigned i = 0; i < struct_initializer->getNumOperands(); ++i) {
+ if (!MaterializeInitializer(data + struct_layout->getElementOffset(i),
+ struct_initializer->getOperand(i)))
+ return false;
+ }
+ return true;
+ } else if (isa<ConstantAggregateZero>(initializer)) {
+ memset(data, 0, m_target_data->getTypeStoreSize(initializer_type));
+ return true;
+ }
+ return false;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::MaybeHandleVariable(Value *llvm_value_ptr) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("MaybeHandleVariable (%s)", PrintValue(llvm_value_ptr).c_str());
+
+ if (ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(llvm_value_ptr)) {
+ switch (constant_expr->getOpcode()) {
+ default:
+ break;
+ case Instruction::GetElementPtr:
+ case Instruction::BitCast:
+ Value *s = constant_expr->getOperand(0);
+ if (!MaybeHandleVariable(s))
+ return false;
+ }
+ } else if (GlobalVariable *global_variable =
+ dyn_cast<GlobalVariable>(llvm_value_ptr)) {
+ if (!GlobalValue::isExternalLinkage(global_variable->getLinkage()))
+ return true;
+
+ clang::NamedDecl *named_decl = DeclForGlobal(global_variable);
+
+ if (!named_decl) {
+ if (IsObjCSelectorRef(llvm_value_ptr))
+ return true;
+
+ if (!global_variable->hasExternalLinkage())
+ return true;
+
+ if (log)
+ log->Printf("Found global variable \"%s\" without metadata",
+ global_variable->getName().str().c_str());
+
+ return false;
+ }
+
+ std::string name(named_decl->getName().str());
+
+ clang::ValueDecl *value_decl = dyn_cast<clang::ValueDecl>(named_decl);
+ if (value_decl == nullptr)
+ return false;
+
+ lldb_private::CompilerType compiler_type(&value_decl->getASTContext(),
+ value_decl->getType());
+
+ const Type *value_type = nullptr;
+
+ if (name[0] == '$') {
+ // The $__lldb_expr_result name indicates the return value has allocated
+ // as a static variable. Per the comment at
+ // ASTResultSynthesizer::SynthesizeBodyResult, accesses to this static
+ // variable need to be redirected to the result of dereferencing a
+ // pointer that is passed in as one of the arguments.
+ //
+ // Consequently, when reporting the size of the type, we report a pointer
+ // type pointing to the type of $__lldb_expr_result, not the type itself.
+ //
+ // We also do this for any user-declared persistent variables.
+ compiler_type = compiler_type.GetPointerType();
+ value_type = PointerType::get(global_variable->getType(), 0);
+ } else {
+ value_type = global_variable->getType();
+ }
+
+ llvm::Optional<uint64_t> value_size = compiler_type.GetByteSize(nullptr);
+ if (!value_size)
+ return false;
+ lldb::offset_t value_alignment =
+ (compiler_type.GetTypeBitAlign() + 7ull) / 8ull;
+
+ if (log) {
+ log->Printf("Type of \"%s\" is [clang \"%s\", llvm \"%s\"] [size %" PRIu64
+ ", align %" PRIu64 "]",
+ name.c_str(),
+ lldb_private::ClangUtil::GetQualType(compiler_type)
+ .getAsString()
+ .c_str(),
+ PrintType(value_type).c_str(), *value_size, value_alignment);
+ }
+
+ if (named_decl &&
+ !m_decl_map->AddValueToStruct(
+ named_decl, lldb_private::ConstString(name.c_str()), llvm_value_ptr,
+ *value_size, value_alignment)) {
+ if (!global_variable->hasExternalLinkage())
+ return true;
+ else
+ return true;
+ }
+ } else if (dyn_cast<llvm::Function>(llvm_value_ptr)) {
+ if (log)
+ log->Printf("Function pointers aren't handled right now");
+
+ return false;
+ }
+
+ return true;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::HandleSymbol(Value *symbol) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ lldb_private::ConstString name(symbol->getName().str().c_str());
+
+ lldb::addr_t symbol_addr =
+ m_decl_map->GetSymbolAddress(name, lldb::eSymbolTypeAny);
+
+ if (symbol_addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("Symbol \"%s\" had no address", name.GetCString());
+
+ return false;
+ }
+
+ if (log)
+ log->Printf("Found \"%s\" at 0x%" PRIx64, name.GetCString(), symbol_addr);
+
+ Type *symbol_type = symbol->getType();
+
+ Constant *symbol_addr_int = ConstantInt::get(m_intptr_ty, symbol_addr, false);
+
+ Value *symbol_addr_ptr =
+ ConstantExpr::getIntToPtr(symbol_addr_int, symbol_type);
+
+ if (log)
+ log->Printf("Replacing %s with %s", PrintValue(symbol).c_str(),
+ PrintValue(symbol_addr_ptr).c_str());
+
+ symbol->replaceAllUsesWith(symbol_addr_ptr);
+
+ return true;
+}
+
+bool IRForTarget::MaybeHandleCallArguments(CallInst *Old) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("MaybeHandleCallArguments(%s)", PrintValue(Old).c_str());
+
+ for (unsigned op_index = 0, num_ops = Old->getNumArgOperands();
+ op_index < num_ops; ++op_index)
+ if (!MaybeHandleVariable(Old->getArgOperand(
+ op_index))) // conservatively believe that this is a store
+ {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't rewrite "
+ "one of the arguments of a function call.\n");
+
+ return false;
+ }
+
+ return true;
+}
+
+bool IRForTarget::HandleObjCClass(Value *classlist_reference) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ GlobalVariable *global_variable =
+ dyn_cast<GlobalVariable>(classlist_reference);
+
+ if (!global_variable)
+ return false;
+
+ Constant *initializer = global_variable->getInitializer();
+
+ if (!initializer)
+ return false;
+
+ if (!initializer->hasName())
+ return false;
+
+ StringRef name(initializer->getName());
+ lldb_private::ConstString name_cstr(name.str().c_str());
+ lldb::addr_t class_ptr =
+ m_decl_map->GetSymbolAddress(name_cstr, lldb::eSymbolTypeObjCClass);
+
+ if (log)
+ log->Printf("Found reference to Objective-C class %s (0x%llx)",
+ name_cstr.AsCString(), (unsigned long long)class_ptr);
+
+ if (class_ptr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (global_variable->use_empty())
+ return false;
+
+ SmallVector<LoadInst *, 2> load_instructions;
+
+ for (llvm::User *u : global_variable->users()) {
+ if (LoadInst *load_instruction = dyn_cast<LoadInst>(u))
+ load_instructions.push_back(load_instruction);
+ }
+
+ if (load_instructions.empty())
+ return false;
+
+ Constant *class_addr = ConstantInt::get(m_intptr_ty, (uint64_t)class_ptr);
+
+ for (LoadInst *load_instruction : load_instructions) {
+ Constant *class_bitcast =
+ ConstantExpr::getIntToPtr(class_addr, load_instruction->getType());
+
+ load_instruction->replaceAllUsesWith(class_bitcast);
+
+ load_instruction->eraseFromParent();
+ }
+
+ return true;
+}
+
+bool IRForTarget::RemoveCXAAtExit(BasicBlock &basic_block) {
+ BasicBlock::iterator ii;
+
+ std::vector<CallInst *> calls_to_remove;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ CallInst *call = dyn_cast<CallInst>(&inst);
+
+ // MaybeHandleCallArguments handles error reporting; we are silent here
+ if (!call)
+ continue;
+
+ bool remove = false;
+
+ llvm::Function *func = call->getCalledFunction();
+
+ if (func && func->getName() == "__cxa_atexit")
+ remove = true;
+
+ llvm::Value *val = call->getCalledValue();
+
+ if (val && val->getName() == "__cxa_atexit")
+ remove = true;
+
+ if (remove)
+ calls_to_remove.push_back(call);
+ }
+
+ for (std::vector<CallInst *>::iterator ci = calls_to_remove.begin(),
+ ce = calls_to_remove.end();
+ ci != ce; ++ci) {
+ (*ci)->eraseFromParent();
+ }
+
+ return true;
+}
+
+bool IRForTarget::ResolveCalls(BasicBlock &basic_block) {
+ /////////////////////////////////////////////////////////////////////////
+ // Prepare the current basic block for execution in the remote process
+ //
+
+ BasicBlock::iterator ii;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ CallInst *call = dyn_cast<CallInst>(&inst);
+
+ // MaybeHandleCallArguments handles error reporting; we are silent here
+ if (call && !MaybeHandleCallArguments(call))
+ return false;
+ }
+
+ return true;
+}
+
+bool IRForTarget::ResolveExternals(Function &llvm_function) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ for (GlobalVariable &global_var : m_module->globals()) {
+ std::string global_name = global_var.getName().str();
+
+ if (log)
+ log->Printf("Examining %s, DeclForGlobalValue returns %p",
+ global_name.c_str(),
+ static_cast<void *>(DeclForGlobal(&global_var)));
+
+ if (global_name.find("OBJC_IVAR") == 0) {
+ if (!HandleSymbol(&global_var)) {
+ m_error_stream.Printf("Error [IRForTarget]: Couldn't find Objective-C "
+ "indirect ivar symbol %s\n",
+ global_name.c_str());
+
+ return false;
+ }
+ } else if (global_name.find("OBJC_CLASSLIST_REFERENCES_$") !=
+ global_name.npos) {
+ if (!HandleObjCClass(&global_var)) {
+ m_error_stream.Printf("Error [IRForTarget]: Couldn't resolve the class "
+ "for an Objective-C static method call\n");
+
+ return false;
+ }
+ } else if (global_name.find("OBJC_CLASSLIST_SUP_REFS_$") !=
+ global_name.npos) {
+ if (!HandleObjCClass(&global_var)) {
+ m_error_stream.Printf("Error [IRForTarget]: Couldn't resolve the class "
+ "for an Objective-C static method call\n");
+
+ return false;
+ }
+ } else if (DeclForGlobal(&global_var)) {
+ if (!MaybeHandleVariable(&global_var)) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't rewrite "
+ "external variable %s\n",
+ global_name.c_str());
+
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool isGuardVariableRef(Value *V) {
+ Constant *Old = nullptr;
+
+ if (!(Old = dyn_cast<Constant>(V)))
+ return false;
+
+ ConstantExpr *CE = nullptr;
+
+ if ((CE = dyn_cast<ConstantExpr>(V))) {
+ if (CE->getOpcode() != Instruction::BitCast)
+ return false;
+
+ Old = CE->getOperand(0);
+ }
+
+ GlobalVariable *GV = dyn_cast<GlobalVariable>(Old);
+
+ if (!GV || !GV->hasName() ||
+ (!GV->getName().startswith("_ZGV") && // Itanium ABI guard variable
+ !GV->getName().endswith("@4IA"))) // Microsoft ABI guard variable
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void IRForTarget::TurnGuardLoadIntoZero(llvm::Instruction *guard_load) {
+ Constant *zero(Constant::getNullValue(guard_load->getType()));
+ guard_load->replaceAllUsesWith(zero);
+ guard_load->eraseFromParent();
+}
+
+static void ExciseGuardStore(Instruction *guard_store) {
+ guard_store->eraseFromParent();
+}
+
+bool IRForTarget::RemoveGuards(BasicBlock &basic_block) {
+ ///////////////////////////////////////////////////////
+ // Eliminate any reference to guard variables found.
+ //
+
+ BasicBlock::iterator ii;
+
+ typedef SmallVector<Instruction *, 2> InstrList;
+ typedef InstrList::iterator InstrIterator;
+
+ InstrList guard_loads;
+ InstrList guard_stores;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ if (LoadInst *load = dyn_cast<LoadInst>(&inst))
+ if (isGuardVariableRef(load->getPointerOperand()))
+ guard_loads.push_back(&inst);
+
+ if (StoreInst *store = dyn_cast<StoreInst>(&inst))
+ if (isGuardVariableRef(store->getPointerOperand()))
+ guard_stores.push_back(&inst);
+ }
+
+ InstrIterator iter;
+
+ for (iter = guard_loads.begin(); iter != guard_loads.end(); ++iter)
+ TurnGuardLoadIntoZero(*iter);
+
+ for (iter = guard_stores.begin(); iter != guard_stores.end(); ++iter)
+ ExciseGuardStore(*iter);
+
+ return true;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::UnfoldConstant(Constant *old_constant,
+ llvm::Function *llvm_function,
+ FunctionValueCache &value_maker,
+ FunctionValueCache &entry_instruction_finder,
+ lldb_private::Stream &error_stream) {
+ SmallVector<User *, 16> users;
+
+ // We do this because the use list might change, invalidating our iterator.
+ // Much better to keep a work list ourselves.
+ for (llvm::User *u : old_constant->users())
+ users.push_back(u);
+
+ for (size_t i = 0; i < users.size(); ++i) {
+ User *user = users[i];
+
+ if (Constant *constant = dyn_cast<Constant>(user)) {
+ // synthesize a new non-constant equivalent of the constant
+
+ if (ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(constant)) {
+ switch (constant_expr->getOpcode()) {
+ default:
+ error_stream.Printf("error [IRForTarget internal]: Unhandled "
+ "constant expression type: \"%s\"",
+ PrintValue(constant_expr).c_str());
+ return false;
+ case Instruction::BitCast: {
+ FunctionValueCache bit_cast_maker(
+ [&value_maker, &entry_instruction_finder, old_constant,
+ constant_expr](llvm::Function *function) -> llvm::Value * {
+ // UnaryExpr
+ // OperandList[0] is value
+
+ if (constant_expr->getOperand(0) != old_constant)
+ return constant_expr;
+
+ return new BitCastInst(
+ value_maker.GetValue(function), constant_expr->getType(),
+ "", llvm::cast<Instruction>(
+ entry_instruction_finder.GetValue(function)));
+ });
+
+ if (!UnfoldConstant(constant_expr, llvm_function, bit_cast_maker,
+ entry_instruction_finder, error_stream))
+ return false;
+ } break;
+ case Instruction::GetElementPtr: {
+ // GetElementPtrConstantExpr
+ // OperandList[0] is base
+ // OperandList[1]... are indices
+
+ FunctionValueCache get_element_pointer_maker(
+ [&value_maker, &entry_instruction_finder, old_constant,
+ constant_expr](llvm::Function *function) -> llvm::Value * {
+ Value *ptr = constant_expr->getOperand(0);
+
+ if (ptr == old_constant)
+ ptr = value_maker.GetValue(function);
+
+ std::vector<Value *> index_vector;
+
+ unsigned operand_index;
+ unsigned num_operands = constant_expr->getNumOperands();
+
+ for (operand_index = 1; operand_index < num_operands;
+ ++operand_index) {
+ Value *operand = constant_expr->getOperand(operand_index);
+
+ if (operand == old_constant)
+ operand = value_maker.GetValue(function);
+
+ index_vector.push_back(operand);
+ }
+
+ ArrayRef<Value *> indices(index_vector);
+
+ return GetElementPtrInst::Create(
+ nullptr, ptr, indices, "",
+ llvm::cast<Instruction>(
+ entry_instruction_finder.GetValue(function)));
+ });
+
+ if (!UnfoldConstant(constant_expr, llvm_function,
+ get_element_pointer_maker,
+ entry_instruction_finder, error_stream))
+ return false;
+ } break;
+ }
+ } else {
+ error_stream.Printf(
+ "error [IRForTarget internal]: Unhandled constant type: \"%s\"",
+ PrintValue(constant).c_str());
+ return false;
+ }
+ } else {
+ if (Instruction *inst = llvm::dyn_cast<Instruction>(user)) {
+ if (llvm_function && inst->getParent()->getParent() != llvm_function) {
+ error_stream.PutCString("error: Capturing non-local variables in "
+ "expressions is unsupported.\n");
+ return false;
+ }
+ inst->replaceUsesOfWith(
+ old_constant, value_maker.GetValue(inst->getParent()->getParent()));
+ } else {
+ error_stream.Printf(
+ "error [IRForTarget internal]: Unhandled non-constant type: \"%s\"",
+ PrintValue(user).c_str());
+ return false;
+ }
+ }
+ }
+
+ if (!isa<GlobalValue>(old_constant)) {
+ old_constant->destroyConstant();
+ }
+
+ return true;
+}
+
+bool IRForTarget::ReplaceVariables(Function &llvm_function) {
+ if (!m_resolve_vars)
+ return true;
+
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ m_decl_map->DoStructLayout();
+
+ if (log)
+ log->Printf("Element arrangement:");
+
+ uint32_t num_elements;
+ uint32_t element_index;
+
+ size_t size;
+ lldb::offset_t alignment;
+
+ if (!m_decl_map->GetStructInfo(num_elements, size, alignment))
+ return false;
+
+ Function::arg_iterator iter(llvm_function.arg_begin());
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes no "
+ "arguments (should take at least a struct pointer)");
+
+ return false;
+ }
+
+ Argument *argument = &*iter;
+
+ if (argument->getName().equals("this")) {
+ ++iter;
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only "
+ "'this' argument (should take a struct pointer "
+ "too)");
+
+ return false;
+ }
+
+ argument = &*iter;
+ } else if (argument->getName().equals("self")) {
+ ++iter;
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only "
+ "'self' argument (should take '_cmd' and a struct "
+ "pointer too)");
+
+ return false;
+ }
+
+ if (!iter->getName().equals("_cmd")) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes '%s' "
+ "after 'self' argument (should take '_cmd')",
+ iter->getName().str().c_str());
+
+ return false;
+ }
+
+ ++iter;
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only "
+ "'self' and '_cmd' arguments (should take a struct "
+ "pointer too)");
+
+ return false;
+ }
+
+ argument = &*iter;
+ }
+
+ if (!argument->getName().equals("$__lldb_arg")) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes an "
+ "argument named '%s' instead of the struct pointer",
+ argument->getName().str().c_str());
+
+ return false;
+ }
+
+ if (log)
+ log->Printf("Arg: \"%s\"", PrintValue(argument).c_str());
+
+ BasicBlock &entry_block(llvm_function.getEntryBlock());
+ Instruction *FirstEntryInstruction(entry_block.getFirstNonPHIOrDbg());
+
+ if (!FirstEntryInstruction) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't find the "
+ "first instruction in the wrapper for use in "
+ "rewriting");
+
+ return false;
+ }
+
+ LLVMContext &context(m_module->getContext());
+ IntegerType *offset_type(Type::getInt32Ty(context));
+
+ if (!offset_type) {
+ m_error_stream.Printf(
+ "Internal error [IRForTarget]: Couldn't produce an offset type");
+
+ return false;
+ }
+
+ for (element_index = 0; element_index < num_elements; ++element_index) {
+ const clang::NamedDecl *decl = nullptr;
+ Value *value = nullptr;
+ lldb::offset_t offset;
+ lldb_private::ConstString name;
+
+ if (!m_decl_map->GetStructElement(decl, value, offset, name,
+ element_index)) {
+ m_error_stream.Printf(
+ "Internal error [IRForTarget]: Structure information is incomplete");
+
+ return false;
+ }
+
+ if (log)
+ log->Printf(" \"%s\" (\"%s\") placed at %" PRIu64, name.GetCString(),
+ decl->getNameAsString().c_str(), offset);
+
+ if (value) {
+ if (log)
+ log->Printf(" Replacing [%s]", PrintValue(value).c_str());
+
+ FunctionValueCache body_result_maker(
+ [this, name, offset_type, offset, argument,
+ value](llvm::Function *function) -> llvm::Value * {
+ // Per the comment at ASTResultSynthesizer::SynthesizeBodyResult,
+ // in cases where the result variable is an rvalue, we have to
+ // synthesize a dereference of the appropriate structure entry in
+ // order to produce the static variable that the AST thinks it is
+ // accessing.
+
+ llvm::Instruction *entry_instruction = llvm::cast<Instruction>(
+ m_entry_instruction_finder.GetValue(function));
+
+ ConstantInt *offset_int(
+ ConstantInt::get(offset_type, offset, true));
+ GetElementPtrInst *get_element_ptr = GetElementPtrInst::Create(
+ nullptr, argument, offset_int, "", entry_instruction);
+
+ if (name == m_result_name && !m_result_is_pointer) {
+ BitCastInst *bit_cast = new BitCastInst(
+ get_element_ptr, value->getType()->getPointerTo(), "",
+ entry_instruction);
+
+ LoadInst *load = new LoadInst(bit_cast, "", entry_instruction);
+
+ return load;
+ } else {
+ BitCastInst *bit_cast = new BitCastInst(
+ get_element_ptr, value->getType(), "", entry_instruction);
+
+ return bit_cast;
+ }
+ });
+
+ if (Constant *constant = dyn_cast<Constant>(value)) {
+ if (!UnfoldConstant(constant, &llvm_function, body_result_maker,
+ m_entry_instruction_finder, m_error_stream)) {
+ return false;
+ }
+ } else if (Instruction *instruction = dyn_cast<Instruction>(value)) {
+ if (instruction->getParent()->getParent() != &llvm_function) {
+ m_error_stream.PutCString("error: Capturing non-local variables in "
+ "expressions is unsupported.\n");
+ return false;
+ }
+ value->replaceAllUsesWith(
+ body_result_maker.GetValue(instruction->getParent()->getParent()));
+ } else {
+ if (log)
+ log->Printf("Unhandled non-constant type: \"%s\"",
+ PrintValue(value).c_str());
+ return false;
+ }
+
+ if (GlobalVariable *var = dyn_cast<GlobalVariable>(value))
+ var->eraseFromParent();
+ }
+ }
+
+ if (log)
+ log->Printf("Total structure [align %" PRId64 ", size %" PRIu64 "]",
+ (int64_t)alignment, (uint64_t)size);
+
+ return true;
+}
+
+llvm::Constant *IRForTarget::BuildRelocation(llvm::Type *type,
+ uint64_t offset) {
+ llvm::Constant *offset_int = ConstantInt::get(m_intptr_ty, offset);
+
+ llvm::Constant *offset_array[1];
+
+ offset_array[0] = offset_int;
+
+ llvm::ArrayRef<llvm::Constant *> offsets(offset_array, 1);
+ llvm::Type *char_type = llvm::Type::getInt8Ty(m_module->getContext());
+ llvm::Type *char_pointer_type = char_type->getPointerTo();
+
+ llvm::Constant *reloc_placeholder_bitcast =
+ ConstantExpr::getBitCast(m_reloc_placeholder, char_pointer_type);
+ llvm::Constant *reloc_getelementptr = ConstantExpr::getGetElementPtr(
+ char_type, reloc_placeholder_bitcast, offsets);
+ llvm::Constant *reloc_bitcast =
+ ConstantExpr::getBitCast(reloc_getelementptr, type);
+
+ return reloc_bitcast;
+}
+
+bool IRForTarget::runOnModule(Module &llvm_module) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ m_module = &llvm_module;
+ m_target_data.reset(new DataLayout(m_module));
+ m_intptr_ty = llvm::Type::getIntNTy(m_module->getContext(),
+ m_target_data->getPointerSizeInBits());
+
+ if (log) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ m_module->print(oss, nullptr);
+
+ oss.flush();
+
+ log->Printf("Module as passed in to IRForTarget: \n\"%s\"", s.c_str());
+ }
+
+ Function *const main_function =
+ m_func_name.IsEmpty() ? nullptr
+ : m_module->getFunction(m_func_name.GetStringRef());
+
+ if (!m_func_name.IsEmpty() && !main_function) {
+ if (log)
+ log->Printf("Couldn't find \"%s()\" in the module",
+ m_func_name.AsCString());
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't find wrapper "
+ "'%s' in the module",
+ m_func_name.AsCString());
+
+ return false;
+ }
+
+ if (main_function) {
+ if (!FixFunctionLinkage(*main_function)) {
+ if (log)
+ log->Printf("Couldn't fix the linkage for the function");
+
+ return false;
+ }
+ }
+
+ llvm::Type *int8_ty = Type::getInt8Ty(m_module->getContext());
+
+ m_reloc_placeholder = new llvm::GlobalVariable(
+ (*m_module), int8_ty, false /* IsConstant */,
+ GlobalVariable::InternalLinkage, Constant::getNullValue(int8_ty),
+ "reloc_placeholder", nullptr /* InsertBefore */,
+ GlobalVariable::NotThreadLocal /* ThreadLocal */, 0 /* AddressSpace */);
+
+ ////////////////////////////////////////////////////////////
+ // Replace $__lldb_expr_result with a persistent variable
+ //
+
+ if (main_function) {
+ if (!CreateResultVariable(*main_function)) {
+ if (log)
+ log->Printf("CreateResultVariable() failed");
+
+ // CreateResultVariable() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ m_module->print(oss, nullptr);
+
+ oss.flush();
+
+ log->Printf("Module after creating the result variable: \n\"%s\"",
+ s.c_str());
+ }
+
+ for (Module::iterator fi = m_module->begin(), fe = m_module->end(); fi != fe;
+ ++fi) {
+ llvm::Function *function = &*fi;
+
+ if (function->begin() == function->end())
+ continue;
+
+ Function::iterator bbi;
+
+ for (bbi = function->begin(); bbi != function->end(); ++bbi) {
+ if (!RemoveGuards(*bbi)) {
+ if (log)
+ log->Printf("RemoveGuards() failed");
+
+ // RemoveGuards() reports its own errors, so we don't do so here
+
+ return false;
+ }
+
+ if (!RewritePersistentAllocs(*bbi)) {
+ if (log)
+ log->Printf("RewritePersistentAllocs() failed");
+
+ // RewritePersistentAllocs() reports its own errors, so we don't do so
+ // here
+
+ return false;
+ }
+
+ if (!RemoveCXAAtExit(*bbi)) {
+ if (log)
+ log->Printf("RemoveCXAAtExit() failed");
+
+ // RemoveCXAAtExit() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Fix all Objective-C constant strings to use NSStringWithCString:encoding:
+ //
+
+ if (!RewriteObjCConstStrings()) {
+ if (log)
+ log->Printf("RewriteObjCConstStrings() failed");
+
+ // RewriteObjCConstStrings() reports its own errors, so we don't do so here
+
+ return false;
+ }
+
+ for (Module::iterator fi = m_module->begin(), fe = m_module->end(); fi != fe;
+ ++fi) {
+ llvm::Function *function = &*fi;
+
+ for (llvm::Function::iterator bbi = function->begin(),
+ bbe = function->end();
+ bbi != bbe; ++bbi) {
+ if (!RewriteObjCSelectors(*bbi)) {
+ if (log)
+ log->Printf("RewriteObjCSelectors() failed");
+
+ // RewriteObjCSelectors() reports its own errors, so we don't do so
+ // here
+
+ return false;
+ }
+
+ if (!RewriteObjCClassReferences(*bbi)) {
+ if (log)
+ log->Printf("RewriteObjCClassReferences() failed");
+
+ // RewriteObjCClasses() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+ }
+
+ for (Module::iterator fi = m_module->begin(), fe = m_module->end(); fi != fe;
+ ++fi) {
+ llvm::Function *function = &*fi;
+
+ for (llvm::Function::iterator bbi = function->begin(),
+ bbe = function->end();
+ bbi != bbe; ++bbi) {
+ if (!ResolveCalls(*bbi)) {
+ if (log)
+ log->Printf("ResolveCalls() failed");
+
+ // ResolveCalls() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Run function-level passes that only make sense on the main function
+ //
+
+ if (main_function) {
+ if (!ResolveExternals(*main_function)) {
+ if (log)
+ log->Printf("ResolveExternals() failed");
+
+ // ResolveExternals() reports its own errors, so we don't do so here
+
+ return false;
+ }
+
+ if (!ReplaceVariables(*main_function)) {
+ if (log)
+ log->Printf("ReplaceVariables() failed");
+
+ // ReplaceVariables() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ m_module->print(oss, nullptr);
+
+ oss.flush();
+
+ log->Printf("Module after preparing for execution: \n\"%s\"", s.c_str());
+ }
+
+ return true;
+}
+
+void IRForTarget::assignPassManager(PMStack &pass_mgr_stack,
+ PassManagerType pass_mgr_type) {}
+
+PassManagerType IRForTarget::getPotentialPassManagerType() const {
+ return PMT_ModulePassManager;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h
new file mode 100644
index 000000000000..f87fd8ac32cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h
@@ -0,0 +1,567 @@
+//===-- IRForTarget.h ---------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_IRForTarget_h_
+#define liblldb_IRForTarget_h_
+
+#include "lldb/Symbol/TaggedASTType.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-public.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/Pass.h"
+
+#include <functional>
+#include <map>
+
+namespace llvm {
+class BasicBlock;
+class CallInst;
+class Constant;
+class ConstantInt;
+class Function;
+class GlobalValue;
+class GlobalVariable;
+class Instruction;
+class Module;
+class StoreInst;
+class DataLayout;
+class Value;
+}
+
+namespace lldb_private {
+class ClangExpressionDeclMap;
+class IRExecutionUnit;
+class IRMemoryMap;
+}
+
+/// \class IRForTarget IRForTarget.h "lldb/Expression/IRForTarget.h"
+/// Transforms the IR for a function to run in the target
+///
+/// Once an expression has been parsed and converted to IR, it can run in two
+/// contexts: interpreted by LLDB as a DWARF location expression, or compiled
+/// by the JIT and inserted into the target process for execution.
+///
+/// IRForTarget makes the second possible, by applying a series of
+/// transformations to the IR which make it relocatable. These
+/// transformations are discussed in more detail next to their relevant
+/// functions.
+class IRForTarget : public llvm::ModulePass {
+public:
+ enum class LookupResult { Success, Fail, Ignore };
+
+ /// Constructor
+ ///
+ /// \param[in] decl_map
+ /// The list of externally-referenced variables for the expression,
+ /// for use in looking up globals and allocating the argument
+ /// struct. See the documentation for ClangExpressionDeclMap.
+ ///
+ /// \param[in] resolve_vars
+ /// True if the external variable references (including persistent
+ /// variables) should be resolved. If not, only external functions
+ /// are resolved.
+ ///
+ /// \param[in] execution_policy
+ /// Determines whether an IR interpreter can be used to statically
+ /// evaluate the expression.
+ ///
+ /// \param[in] const_result
+ /// This variable is populated with the statically-computed result
+ /// of the function, if it has no side-effects and the result can
+ /// be computed statically.
+ ///
+ /// \param[in] execution_unit
+ /// The holder for raw data associated with the expression.
+ ///
+ /// \param[in] error_stream
+ /// If non-NULL, a stream on which errors can be printed.
+ ///
+ /// \param[in] func_name
+ /// The name of the function to prepare for execution in the target.
+ IRForTarget(lldb_private::ClangExpressionDeclMap *decl_map, bool resolve_vars,
+ lldb_private::IRExecutionUnit &execution_unit,
+ lldb_private::Stream &error_stream,
+ const char *func_name = "$__lldb_expr");
+
+ /// Destructor
+ ~IRForTarget() override;
+
+ /// Run this IR transformer on a single module
+ ///
+ /// Implementation of the llvm::ModulePass::runOnModule() function.
+ ///
+ /// \param[in] llvm_module
+ /// The module to run on. This module is searched for the function
+ /// $__lldb_expr, and that function is passed to the passes one by
+ /// one.
+ ///
+ /// \param[in] interpreter_error
+ /// An error. If the expression fails to be interpreted, this error
+ /// is set to a reason why.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool runOnModule(llvm::Module &llvm_module) override;
+
+ /// Interface stub
+ ///
+ /// Implementation of the llvm::ModulePass::assignPassManager() function.
+ void assignPassManager(llvm::PMStack &pass_mgr_stack,
+ llvm::PassManagerType pass_mgr_type =
+ llvm::PMT_ModulePassManager) override;
+
+ /// Returns PMT_ModulePassManager
+ ///
+ /// Implementation of the llvm::ModulePass::getPotentialPassManagerType()
+ /// function.
+ llvm::PassManagerType getPotentialPassManagerType() const override;
+
+private:
+ /// Ensures that the current function's linkage is set to external.
+ /// Otherwise the JIT may not return an address for it.
+ ///
+ /// \param[in] llvm_function
+ /// The function whose linkage is to be fixed.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool FixFunctionLinkage(llvm::Function &llvm_function);
+
+ /// A module-level pass to replace all function pointers with their
+ /// integer equivalents.
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] llvm_module
+ /// The module currently being processed.
+ ///
+ /// \param[in] llvm_function
+ /// The function currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool HasSideEffects(llvm::Function &llvm_function);
+
+ /// A function-level pass to check whether the function has side
+ /// effects.
+
+ /// Get the address of a function, and a location to put the complete Value
+ /// of the function if one is available.
+ ///
+ /// \param[in] function
+ /// The function to find the location of.
+ ///
+ /// \param[out] ptr
+ /// The location of the function in the target.
+ ///
+ /// \param[out] name
+ /// The resolved name of the function (matters for intrinsics).
+ ///
+ /// \param[out] value_ptr
+ /// A variable to put the function's completed Value* in, or NULL
+ /// if the Value* shouldn't be stored anywhere.
+ ///
+ /// \return
+ /// The pointer.
+ LookupResult GetFunctionAddress(llvm::Function *function, uint64_t &ptr,
+ lldb_private::ConstString &name,
+ llvm::Constant **&value_ptr);
+
+ /// A function-level pass to take the generated global value
+ /// $__lldb_expr_result and make it into a persistent variable. Also see
+ /// ASTResultSynthesizer.
+
+ /// Find the NamedDecl corresponding to a Value. This interface is exposed
+ /// for the IR interpreter.
+ ///
+ /// \param[in] module
+ /// The module containing metadata to search
+ ///
+ /// \param[in] global
+ /// The global entity to search for
+ ///
+ /// \return
+ /// The corresponding variable declaration
+public:
+ static clang::NamedDecl *DeclForGlobal(const llvm::GlobalValue *global_val,
+ llvm::Module *module);
+
+private:
+ clang::NamedDecl *DeclForGlobal(llvm::GlobalValue *global);
+
+ /// Set the constant result variable m_const_result to the provided
+ /// constant, assuming it can be evaluated. The result variable will be
+ /// reset to NULL later if the expression has side effects.
+ ///
+ /// \param[in] initializer
+ /// The constant initializer for the variable.
+ ///
+ /// \param[in] name
+ /// The name of the result variable.
+ ///
+ /// \param[in] type
+ /// The Clang type of the result variable.
+ void MaybeSetConstantResult(llvm::Constant *initializer,
+ lldb_private::ConstString name,
+ lldb_private::TypeFromParser type);
+
+ /// If the IR represents a cast of a variable, set m_const_result to the
+ /// result of the cast. The result variable will be reset to
+ /// NULL latger if the expression has side effects.
+ ///
+ /// \param[in] type
+ /// The Clang type of the result variable.
+ void MaybeSetCastResult(lldb_private::TypeFromParser type);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] llvm_function
+ /// The function currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool CreateResultVariable(llvm::Function &llvm_function);
+
+ /// A module-level pass to find Objective-C constant strings and
+ /// transform them to calls to CFStringCreateWithBytes.
+
+ /// Rewrite a single Objective-C constant string.
+ ///
+ /// \param[in] NSStr
+ /// The constant NSString to be transformed
+ ///
+ /// \param[in] CStr
+ /// The constant C string inside the NSString. This will be
+ /// passed as the bytes argument to CFStringCreateWithBytes.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCConstString(llvm::GlobalVariable *NSStr,
+ llvm::GlobalVariable *CStr);
+
+ /// The top-level pass implementation
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCConstStrings();
+
+ /// A basic block-level pass to find all Objective-C method calls and
+ /// rewrite them to use sel_registerName instead of statically allocated
+ /// selectors. The reason is that the selectors are created on the
+ /// assumption that the Objective-C runtime will scan the appropriate
+ /// section and prepare them. This doesn't happen when code is copied into
+ /// the target, though, and there's no easy way to induce the runtime to
+ /// scan them. So instead we get our selectors from sel_registerName.
+
+ /// Replace a single selector reference
+ ///
+ /// \param[in] selector_load
+ /// The load of the statically-allocated selector.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCSelector(llvm::Instruction *selector_load);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] basic_block
+ /// The basic block currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCSelectors(llvm::BasicBlock &basic_block);
+
+ /// A basic block-level pass to find all Objective-C class references that
+ /// use the old-style Objective-C runtime and rewrite them to use
+ /// class_getClass instead of statically allocated class references.
+
+ /// Replace a single old-style class reference
+ ///
+ /// \param[in] selector_load
+ /// The load of the statically-allocated selector.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCClassReference(llvm::Instruction *class_load);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] basic_block
+ /// The basic block currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCClassReferences(llvm::BasicBlock &basic_block);
+
+ /// A basic block-level pass to find all newly-declared persistent
+ /// variables and register them with the ClangExprDeclMap. This allows them
+ /// to be materialized and dematerialized like normal external variables.
+ /// Before transformation, these persistent variables look like normal
+ /// locals, so they have an allocation. This pass excises these allocations
+ /// and makes references look like external references where they will be
+ /// resolved -- like all other external references -- by ResolveExternals().
+
+ /// Handle a single allocation of a persistent variable
+ ///
+ /// \param[in] persistent_alloc
+ /// The allocation of the persistent variable.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewritePersistentAlloc(llvm::Instruction *persistent_alloc);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] basic_block
+ /// The basic block currently being processed.
+ bool RewritePersistentAllocs(llvm::BasicBlock &basic_block);
+
+ /// A function-level pass to find all external variables and functions
+ /// used in the IR. Each found external variable is added to the struct,
+ /// and each external function is resolved in place, its call replaced with
+ /// a call to a function pointer whose value is the address of the function
+ /// in the target process.
+
+ /// Write an initializer to a memory array of assumed sufficient size.
+ ///
+ /// \param[in] data
+ /// A pointer to the data to write to.
+ ///
+ /// \param[in] initializer
+ /// The initializer itself.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool MaterializeInitializer(uint8_t *data, llvm::Constant *initializer);
+
+ /// Move an internal variable into the static allocation section.
+ ///
+ /// \param[in] global_variable
+ /// The variable.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool MaterializeInternalVariable(llvm::GlobalVariable *global_variable);
+
+ /// Handle a single externally-defined variable
+ ///
+ /// \param[in] value
+ /// The variable.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool MaybeHandleVariable(llvm::Value *value);
+
+ /// Handle a single externally-defined symbol
+ ///
+ /// \param[in] symbol
+ /// The symbol.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool HandleSymbol(llvm::Value *symbol);
+
+ /// Handle a single externally-defined Objective-C class
+ ///
+ /// \param[in] classlist_reference
+ /// The reference, usually "01L_OBJC_CLASSLIST_REFERENCES_$_n"
+ /// where n (if present) is an index.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool HandleObjCClass(llvm::Value *classlist_reference);
+
+ /// Handle all the arguments to a function call
+ ///
+ /// \param[in] C
+ /// The call instruction.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool MaybeHandleCallArguments(llvm::CallInst *call_inst);
+
+ /// Resolve variable references in calls to external functions
+ ///
+ /// \param[in] basic_block
+ /// The basic block currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool ResolveCalls(llvm::BasicBlock &basic_block);
+
+ /// Remove calls to __cxa_atexit, which should never be generated by
+ /// expressions.
+ ///
+ /// \param[in] call_inst
+ /// The call instruction.
+ ///
+ /// \return
+ /// True if the scan was successful; false if some operation
+ /// failed
+ bool RemoveCXAAtExit(llvm::BasicBlock &basic_block);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] basic_block
+ /// The function currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool ResolveExternals(llvm::Function &llvm_function);
+
+ /// A basic block-level pass to excise guard variables from the code.
+ /// The result for the function is passed through Clang as a static
+ /// variable. Static variables normally have guard variables to ensure that
+ /// they are only initialized once.
+
+ /// Rewrite a load to a guard variable to return constant 0.
+ ///
+ /// \param[in] guard_load
+ /// The load instruction to zero out.
+ void TurnGuardLoadIntoZero(llvm::Instruction *guard_load);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] basic_block
+ /// The basic block currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RemoveGuards(llvm::BasicBlock &basic_block);
+
+ /// A function-level pass to make all external variable references
+ /// point at the correct offsets from the void* passed into the function.
+ /// ClangExpressionDeclMap::DoStructLayout() must be called beforehand, so
+ /// that the offsets are valid.
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] llvm_function
+ /// The function currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool ReplaceVariables(llvm::Function &llvm_function);
+
+ /// Flags
+ bool m_resolve_vars; ///< True if external variable references and persistent
+ ///variable references should be resolved
+ lldb_private::ConstString
+ m_func_name; ///< The name of the function to translate
+ lldb_private::ConstString
+ m_result_name; ///< The name of the result variable ($0, $1, ...)
+ lldb_private::TypeFromParser
+ m_result_type; ///< The type of the result variable.
+ llvm::Module *m_module; ///< The module being processed, or NULL if that has
+ ///not been determined yet.
+ std::unique_ptr<llvm::DataLayout> m_target_data; ///< The target data for the
+ ///module being processed, or
+ ///NULL if there is no
+ ///module.
+ lldb_private::ClangExpressionDeclMap
+ *m_decl_map; ///< The DeclMap containing the Decls
+ llvm::FunctionCallee
+ m_CFStringCreateWithBytes; ///< The address of the function
+ /// CFStringCreateWithBytes, cast to the
+ /// appropriate function pointer type
+ llvm::FunctionCallee m_sel_registerName; ///< The address of the function
+ /// sel_registerName, cast to the
+ /// appropriate function pointer type
+ llvm::FunctionCallee m_objc_getClass; ///< The address of the function
+ /// objc_getClass, cast to the
+ /// appropriate function pointer type
+ llvm::IntegerType
+ *m_intptr_ty; ///< The type of an integer large enough to hold a pointer.
+ lldb_private::Stream
+ &m_error_stream; ///< The stream on which errors should be printed
+ lldb_private::IRExecutionUnit &
+ m_execution_unit; ///< The execution unit containing the IR being created.
+
+ llvm::StoreInst *m_result_store; ///< If non-NULL, the store instruction that
+ ///writes to the result variable. If
+ /// m_has_side_effects is true, this is
+ /// NULL.
+ bool m_result_is_pointer; ///< True if the function's result in the AST is a
+ ///pointer (see comments in
+ /// ASTResultSynthesizer::SynthesizeBodyResult)
+
+ llvm::GlobalVariable *m_reloc_placeholder; ///< A placeholder that will be
+ ///replaced by a pointer to the
+ ///final
+ /// location of the static allocation.
+
+ /// UnfoldConstant operates on a constant [Old] which has just been replaced
+ /// with a value [New]. We assume that new_value has been properly placed
+ /// early in the function, in front of the first instruction in the entry
+ /// basic block [FirstEntryInstruction].
+ ///
+ /// UnfoldConstant reads through the uses of Old and replaces Old in those
+ /// uses with New. Where those uses are constants, the function generates
+ /// new instructions to compute the result of the new, non-constant
+ /// expression and places them before FirstEntryInstruction. These
+ /// instructions replace the constant uses, so UnfoldConstant calls itself
+ /// recursively for those.
+ ///
+ /// \param[in] llvm_function
+ /// The function currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+
+ class FunctionValueCache {
+ public:
+ typedef std::function<llvm::Value *(llvm::Function *)> Maker;
+
+ FunctionValueCache(Maker const &maker);
+ ~FunctionValueCache();
+ llvm::Value *GetValue(llvm::Function *function);
+
+ private:
+ Maker const m_maker;
+ typedef std::map<llvm::Function *, llvm::Value *> FunctionValueMap;
+ FunctionValueMap m_values;
+ };
+
+ FunctionValueCache m_entry_instruction_finder;
+
+ static bool UnfoldConstant(llvm::Constant *old_constant,
+ llvm::Function *llvm_function,
+ FunctionValueCache &value_maker,
+ FunctionValueCache &entry_instruction_finder,
+ lldb_private::Stream &error_stream);
+
+ /// Construct a reference to m_reloc_placeholder with a given type and
+ /// offset. This typically happens after inserting data into
+ /// m_data_allocator.
+ ///
+ /// \param[in] type
+ /// The type of the value being loaded.
+ ///
+ /// \param[in] offset
+ /// The offset of the value from the base of m_data_allocator.
+ ///
+ /// \return
+ /// The Constant for the reference, usually a ConstantExpr.
+ llvm::Constant *BuildRelocation(llvm::Type *type, uint64_t offset);
+
+ /// Commit the allocation in m_data_allocator and use its final location to
+ /// replace m_reloc_placeholder.
+ ///
+ /// \param[in] module
+ /// The module that m_data_allocator resides in
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool CompleteDataAllocation();
+};
+
+#endif // liblldb_IRForTarget_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h
new file mode 100644
index 000000000000..0e959f86fd2a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h
@@ -0,0 +1,38 @@
+//===-- ModuleDependencyCollector.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ModuleDependencyCollector_h_
+#define liblldb_ModuleDependencyCollector_h_
+
+#include "lldb/Utility/FileCollector.h"
+#include "clang/Frontend/Utils.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_private {
+class ModuleDependencyCollectorAdaptor
+ : public clang::ModuleDependencyCollector {
+public:
+ ModuleDependencyCollectorAdaptor(FileCollector &file_collector)
+ : clang::ModuleDependencyCollector(""), m_file_collector(file_collector) {
+ }
+
+ void addFile(llvm::StringRef Filename,
+ llvm::StringRef FileDst = {}) override {
+ m_file_collector.AddFile(Filename);
+ }
+
+ bool insertSeen(llvm::StringRef Filename) override { return false; }
+ void addFileMapping(llvm::StringRef VPath, llvm::StringRef RPath) override {}
+ void writeFileMap() override {}
+
+private:
+ FileCollector &m_file_collector;
+};
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp
new file mode 100644
index 000000000000..6323889c2e09
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp
@@ -0,0 +1,14512 @@
+//===-- EmulateInstructionARM.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdlib.h>
+
+#include "EmulateInstructionARM.h"
+#include "EmulationStateARM.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Interpreter/OptionValueArray.h"
+#include "lldb/Interpreter/OptionValueDictionary.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "Plugins/Process/Utility/ARMUtils.h"
+#include "Utility/ARM_DWARF_Registers.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/MathExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Convenient macro definitions.
+#define APSR_C Bit32(m_opcode_cpsr, CPSR_C_POS)
+#define APSR_V Bit32(m_opcode_cpsr, CPSR_V_POS)
+
+#define AlignPC(pc_val) (pc_val & 0xFFFFFFFC)
+
+//
+// ITSession implementation
+//
+
+static bool GetARMDWARFRegisterInfo(unsigned reg_num, RegisterInfo &reg_info) {
+ ::memset(&reg_info, 0, sizeof(RegisterInfo));
+ ::memset(reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds));
+
+ if (reg_num >= dwarf_q0 && reg_num <= dwarf_q15) {
+ reg_info.byte_size = 16;
+ reg_info.format = eFormatVectorOfUInt8;
+ reg_info.encoding = eEncodingVector;
+ }
+
+ if (reg_num >= dwarf_d0 && reg_num <= dwarf_d31) {
+ reg_info.byte_size = 8;
+ reg_info.format = eFormatFloat;
+ reg_info.encoding = eEncodingIEEE754;
+ } else if (reg_num >= dwarf_s0 && reg_num <= dwarf_s31) {
+ reg_info.byte_size = 4;
+ reg_info.format = eFormatFloat;
+ reg_info.encoding = eEncodingIEEE754;
+ } else if (reg_num >= dwarf_f0 && reg_num <= dwarf_f7) {
+ reg_info.byte_size = 12;
+ reg_info.format = eFormatFloat;
+ reg_info.encoding = eEncodingIEEE754;
+ } else {
+ reg_info.byte_size = 4;
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ }
+
+ reg_info.kinds[eRegisterKindDWARF] = reg_num;
+
+ switch (reg_num) {
+ case dwarf_r0:
+ reg_info.name = "r0";
+ break;
+ case dwarf_r1:
+ reg_info.name = "r1";
+ break;
+ case dwarf_r2:
+ reg_info.name = "r2";
+ break;
+ case dwarf_r3:
+ reg_info.name = "r3";
+ break;
+ case dwarf_r4:
+ reg_info.name = "r4";
+ break;
+ case dwarf_r5:
+ reg_info.name = "r5";
+ break;
+ case dwarf_r6:
+ reg_info.name = "r6";
+ break;
+ case dwarf_r7:
+ reg_info.name = "r7";
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ break;
+ case dwarf_r8:
+ reg_info.name = "r8";
+ break;
+ case dwarf_r9:
+ reg_info.name = "r9";
+ break;
+ case dwarf_r10:
+ reg_info.name = "r10";
+ break;
+ case dwarf_r11:
+ reg_info.name = "r11";
+ break;
+ case dwarf_r12:
+ reg_info.name = "r12";
+ break;
+ case dwarf_sp:
+ reg_info.name = "sp";
+ reg_info.alt_name = "r13";
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ break;
+ case dwarf_lr:
+ reg_info.name = "lr";
+ reg_info.alt_name = "r14";
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA;
+ break;
+ case dwarf_pc:
+ reg_info.name = "pc";
+ reg_info.alt_name = "r15";
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ break;
+ case dwarf_cpsr:
+ reg_info.name = "cpsr";
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ break;
+
+ case dwarf_s0:
+ reg_info.name = "s0";
+ break;
+ case dwarf_s1:
+ reg_info.name = "s1";
+ break;
+ case dwarf_s2:
+ reg_info.name = "s2";
+ break;
+ case dwarf_s3:
+ reg_info.name = "s3";
+ break;
+ case dwarf_s4:
+ reg_info.name = "s4";
+ break;
+ case dwarf_s5:
+ reg_info.name = "s5";
+ break;
+ case dwarf_s6:
+ reg_info.name = "s6";
+ break;
+ case dwarf_s7:
+ reg_info.name = "s7";
+ break;
+ case dwarf_s8:
+ reg_info.name = "s8";
+ break;
+ case dwarf_s9:
+ reg_info.name = "s9";
+ break;
+ case dwarf_s10:
+ reg_info.name = "s10";
+ break;
+ case dwarf_s11:
+ reg_info.name = "s11";
+ break;
+ case dwarf_s12:
+ reg_info.name = "s12";
+ break;
+ case dwarf_s13:
+ reg_info.name = "s13";
+ break;
+ case dwarf_s14:
+ reg_info.name = "s14";
+ break;
+ case dwarf_s15:
+ reg_info.name = "s15";
+ break;
+ case dwarf_s16:
+ reg_info.name = "s16";
+ break;
+ case dwarf_s17:
+ reg_info.name = "s17";
+ break;
+ case dwarf_s18:
+ reg_info.name = "s18";
+ break;
+ case dwarf_s19:
+ reg_info.name = "s19";
+ break;
+ case dwarf_s20:
+ reg_info.name = "s20";
+ break;
+ case dwarf_s21:
+ reg_info.name = "s21";
+ break;
+ case dwarf_s22:
+ reg_info.name = "s22";
+ break;
+ case dwarf_s23:
+ reg_info.name = "s23";
+ break;
+ case dwarf_s24:
+ reg_info.name = "s24";
+ break;
+ case dwarf_s25:
+ reg_info.name = "s25";
+ break;
+ case dwarf_s26:
+ reg_info.name = "s26";
+ break;
+ case dwarf_s27:
+ reg_info.name = "s27";
+ break;
+ case dwarf_s28:
+ reg_info.name = "s28";
+ break;
+ case dwarf_s29:
+ reg_info.name = "s29";
+ break;
+ case dwarf_s30:
+ reg_info.name = "s30";
+ break;
+ case dwarf_s31:
+ reg_info.name = "s31";
+ break;
+
+ // FPA Registers 0-7
+ case dwarf_f0:
+ reg_info.name = "f0";
+ break;
+ case dwarf_f1:
+ reg_info.name = "f1";
+ break;
+ case dwarf_f2:
+ reg_info.name = "f2";
+ break;
+ case dwarf_f3:
+ reg_info.name = "f3";
+ break;
+ case dwarf_f4:
+ reg_info.name = "f4";
+ break;
+ case dwarf_f5:
+ reg_info.name = "f5";
+ break;
+ case dwarf_f6:
+ reg_info.name = "f6";
+ break;
+ case dwarf_f7:
+ reg_info.name = "f7";
+ break;
+
+ // Intel wireless MMX general purpose registers 0 - 7 XScale accumulator
+ // register 0 - 7 (they do overlap with wCGR0 - wCGR7)
+ case dwarf_wCGR0:
+ reg_info.name = "wCGR0/ACC0";
+ break;
+ case dwarf_wCGR1:
+ reg_info.name = "wCGR1/ACC1";
+ break;
+ case dwarf_wCGR2:
+ reg_info.name = "wCGR2/ACC2";
+ break;
+ case dwarf_wCGR3:
+ reg_info.name = "wCGR3/ACC3";
+ break;
+ case dwarf_wCGR4:
+ reg_info.name = "wCGR4/ACC4";
+ break;
+ case dwarf_wCGR5:
+ reg_info.name = "wCGR5/ACC5";
+ break;
+ case dwarf_wCGR6:
+ reg_info.name = "wCGR6/ACC6";
+ break;
+ case dwarf_wCGR7:
+ reg_info.name = "wCGR7/ACC7";
+ break;
+
+ // Intel wireless MMX data registers 0 - 15
+ case dwarf_wR0:
+ reg_info.name = "wR0";
+ break;
+ case dwarf_wR1:
+ reg_info.name = "wR1";
+ break;
+ case dwarf_wR2:
+ reg_info.name = "wR2";
+ break;
+ case dwarf_wR3:
+ reg_info.name = "wR3";
+ break;
+ case dwarf_wR4:
+ reg_info.name = "wR4";
+ break;
+ case dwarf_wR5:
+ reg_info.name = "wR5";
+ break;
+ case dwarf_wR6:
+ reg_info.name = "wR6";
+ break;
+ case dwarf_wR7:
+ reg_info.name = "wR7";
+ break;
+ case dwarf_wR8:
+ reg_info.name = "wR8";
+ break;
+ case dwarf_wR9:
+ reg_info.name = "wR9";
+ break;
+ case dwarf_wR10:
+ reg_info.name = "wR10";
+ break;
+ case dwarf_wR11:
+ reg_info.name = "wR11";
+ break;
+ case dwarf_wR12:
+ reg_info.name = "wR12";
+ break;
+ case dwarf_wR13:
+ reg_info.name = "wR13";
+ break;
+ case dwarf_wR14:
+ reg_info.name = "wR14";
+ break;
+ case dwarf_wR15:
+ reg_info.name = "wR15";
+ break;
+
+ case dwarf_spsr:
+ reg_info.name = "spsr";
+ break;
+ case dwarf_spsr_fiq:
+ reg_info.name = "spsr_fiq";
+ break;
+ case dwarf_spsr_irq:
+ reg_info.name = "spsr_irq";
+ break;
+ case dwarf_spsr_abt:
+ reg_info.name = "spsr_abt";
+ break;
+ case dwarf_spsr_und:
+ reg_info.name = "spsr_und";
+ break;
+ case dwarf_spsr_svc:
+ reg_info.name = "spsr_svc";
+ break;
+
+ case dwarf_r8_usr:
+ reg_info.name = "r8_usr";
+ break;
+ case dwarf_r9_usr:
+ reg_info.name = "r9_usr";
+ break;
+ case dwarf_r10_usr:
+ reg_info.name = "r10_usr";
+ break;
+ case dwarf_r11_usr:
+ reg_info.name = "r11_usr";
+ break;
+ case dwarf_r12_usr:
+ reg_info.name = "r12_usr";
+ break;
+ case dwarf_r13_usr:
+ reg_info.name = "r13_usr";
+ break;
+ case dwarf_r14_usr:
+ reg_info.name = "r14_usr";
+ break;
+ case dwarf_r8_fiq:
+ reg_info.name = "r8_fiq";
+ break;
+ case dwarf_r9_fiq:
+ reg_info.name = "r9_fiq";
+ break;
+ case dwarf_r10_fiq:
+ reg_info.name = "r10_fiq";
+ break;
+ case dwarf_r11_fiq:
+ reg_info.name = "r11_fiq";
+ break;
+ case dwarf_r12_fiq:
+ reg_info.name = "r12_fiq";
+ break;
+ case dwarf_r13_fiq:
+ reg_info.name = "r13_fiq";
+ break;
+ case dwarf_r14_fiq:
+ reg_info.name = "r14_fiq";
+ break;
+ case dwarf_r13_irq:
+ reg_info.name = "r13_irq";
+ break;
+ case dwarf_r14_irq:
+ reg_info.name = "r14_irq";
+ break;
+ case dwarf_r13_abt:
+ reg_info.name = "r13_abt";
+ break;
+ case dwarf_r14_abt:
+ reg_info.name = "r14_abt";
+ break;
+ case dwarf_r13_und:
+ reg_info.name = "r13_und";
+ break;
+ case dwarf_r14_und:
+ reg_info.name = "r14_und";
+ break;
+ case dwarf_r13_svc:
+ reg_info.name = "r13_svc";
+ break;
+ case dwarf_r14_svc:
+ reg_info.name = "r14_svc";
+ break;
+
+ // Intel wireless MMX control register in co-processor 0 - 7
+ case dwarf_wC0:
+ reg_info.name = "wC0";
+ break;
+ case dwarf_wC1:
+ reg_info.name = "wC1";
+ break;
+ case dwarf_wC2:
+ reg_info.name = "wC2";
+ break;
+ case dwarf_wC3:
+ reg_info.name = "wC3";
+ break;
+ case dwarf_wC4:
+ reg_info.name = "wC4";
+ break;
+ case dwarf_wC5:
+ reg_info.name = "wC5";
+ break;
+ case dwarf_wC6:
+ reg_info.name = "wC6";
+ break;
+ case dwarf_wC7:
+ reg_info.name = "wC7";
+ break;
+
+ // VFP-v3/Neon
+ case dwarf_d0:
+ reg_info.name = "d0";
+ break;
+ case dwarf_d1:
+ reg_info.name = "d1";
+ break;
+ case dwarf_d2:
+ reg_info.name = "d2";
+ break;
+ case dwarf_d3:
+ reg_info.name = "d3";
+ break;
+ case dwarf_d4:
+ reg_info.name = "d4";
+ break;
+ case dwarf_d5:
+ reg_info.name = "d5";
+ break;
+ case dwarf_d6:
+ reg_info.name = "d6";
+ break;
+ case dwarf_d7:
+ reg_info.name = "d7";
+ break;
+ case dwarf_d8:
+ reg_info.name = "d8";
+ break;
+ case dwarf_d9:
+ reg_info.name = "d9";
+ break;
+ case dwarf_d10:
+ reg_info.name = "d10";
+ break;
+ case dwarf_d11:
+ reg_info.name = "d11";
+ break;
+ case dwarf_d12:
+ reg_info.name = "d12";
+ break;
+ case dwarf_d13:
+ reg_info.name = "d13";
+ break;
+ case dwarf_d14:
+ reg_info.name = "d14";
+ break;
+ case dwarf_d15:
+ reg_info.name = "d15";
+ break;
+ case dwarf_d16:
+ reg_info.name = "d16";
+ break;
+ case dwarf_d17:
+ reg_info.name = "d17";
+ break;
+ case dwarf_d18:
+ reg_info.name = "d18";
+ break;
+ case dwarf_d19:
+ reg_info.name = "d19";
+ break;
+ case dwarf_d20:
+ reg_info.name = "d20";
+ break;
+ case dwarf_d21:
+ reg_info.name = "d21";
+ break;
+ case dwarf_d22:
+ reg_info.name = "d22";
+ break;
+ case dwarf_d23:
+ reg_info.name = "d23";
+ break;
+ case dwarf_d24:
+ reg_info.name = "d24";
+ break;
+ case dwarf_d25:
+ reg_info.name = "d25";
+ break;
+ case dwarf_d26:
+ reg_info.name = "d26";
+ break;
+ case dwarf_d27:
+ reg_info.name = "d27";
+ break;
+ case dwarf_d28:
+ reg_info.name = "d28";
+ break;
+ case dwarf_d29:
+ reg_info.name = "d29";
+ break;
+ case dwarf_d30:
+ reg_info.name = "d30";
+ break;
+ case dwarf_d31:
+ reg_info.name = "d31";
+ break;
+
+ // NEON 128-bit vector registers (overlays the d registers)
+ case dwarf_q0:
+ reg_info.name = "q0";
+ break;
+ case dwarf_q1:
+ reg_info.name = "q1";
+ break;
+ case dwarf_q2:
+ reg_info.name = "q2";
+ break;
+ case dwarf_q3:
+ reg_info.name = "q3";
+ break;
+ case dwarf_q4:
+ reg_info.name = "q4";
+ break;
+ case dwarf_q5:
+ reg_info.name = "q5";
+ break;
+ case dwarf_q6:
+ reg_info.name = "q6";
+ break;
+ case dwarf_q7:
+ reg_info.name = "q7";
+ break;
+ case dwarf_q8:
+ reg_info.name = "q8";
+ break;
+ case dwarf_q9:
+ reg_info.name = "q9";
+ break;
+ case dwarf_q10:
+ reg_info.name = "q10";
+ break;
+ case dwarf_q11:
+ reg_info.name = "q11";
+ break;
+ case dwarf_q12:
+ reg_info.name = "q12";
+ break;
+ case dwarf_q13:
+ reg_info.name = "q13";
+ break;
+ case dwarf_q14:
+ reg_info.name = "q14";
+ break;
+ case dwarf_q15:
+ reg_info.name = "q15";
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+// A8.6.50
+// Valid return values are {1, 2, 3, 4}, with 0 signifying an error condition.
+static uint32_t CountITSize(uint32_t ITMask) {
+ // First count the trailing zeros of the IT mask.
+ uint32_t TZ = llvm::countTrailingZeros(ITMask);
+ if (TZ > 3) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ printf("Encoding error: IT Mask '0000'\n");
+#endif
+ return 0;
+ }
+ return (4 - TZ);
+}
+
+// Init ITState. Note that at least one bit is always 1 in mask.
+bool ITSession::InitIT(uint32_t bits7_0) {
+ ITCounter = CountITSize(Bits32(bits7_0, 3, 0));
+ if (ITCounter == 0)
+ return false;
+
+ // A8.6.50 IT
+ unsigned short FirstCond = Bits32(bits7_0, 7, 4);
+ if (FirstCond == 0xF) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ printf("Encoding error: IT FirstCond '1111'\n");
+#endif
+ return false;
+ }
+ if (FirstCond == 0xE && ITCounter != 1) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ printf("Encoding error: IT FirstCond '1110' && Mask != '1000'\n");
+#endif
+ return false;
+ }
+
+ ITState = bits7_0;
+ return true;
+}
+
+// Update ITState if necessary.
+void ITSession::ITAdvance() {
+ // assert(ITCounter);
+ --ITCounter;
+ if (ITCounter == 0)
+ ITState = 0;
+ else {
+ unsigned short NewITState4_0 = Bits32(ITState, 4, 0) << 1;
+ SetBits32(ITState, 4, 0, NewITState4_0);
+ }
+}
+
+// Return true if we're inside an IT Block.
+bool ITSession::InITBlock() { return ITCounter != 0; }
+
+// Return true if we're the last instruction inside an IT Block.
+bool ITSession::LastInITBlock() { return ITCounter == 1; }
+
+// Get condition bits for the current thumb instruction.
+uint32_t ITSession::GetCond() {
+ if (InITBlock())
+ return Bits32(ITState, 7, 4);
+ else
+ return COND_AL;
+}
+
+// ARM constants used during decoding
+#define REG_RD 0
+#define LDM_REGLIST 1
+#define SP_REG 13
+#define LR_REG 14
+#define PC_REG 15
+#define PC_REGLIST_BIT 0x8000
+
+#define ARMv4 (1u << 0)
+#define ARMv4T (1u << 1)
+#define ARMv5T (1u << 2)
+#define ARMv5TE (1u << 3)
+#define ARMv5TEJ (1u << 4)
+#define ARMv6 (1u << 5)
+#define ARMv6K (1u << 6)
+#define ARMv6T2 (1u << 7)
+#define ARMv7 (1u << 8)
+#define ARMv7S (1u << 9)
+#define ARMv8 (1u << 10)
+#define ARMvAll (0xffffffffu)
+
+#define ARMV4T_ABOVE \
+ (ARMv4T | ARMv5T | ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | \
+ ARMv7S | ARMv8)
+#define ARMV5_ABOVE \
+ (ARMv5T | ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | \
+ ARMv8)
+#define ARMV5TE_ABOVE \
+ (ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8)
+#define ARMV5J_ABOVE \
+ (ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8)
+#define ARMV6_ABOVE (ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8)
+#define ARMV6T2_ABOVE (ARMv6T2 | ARMv7 | ARMv7S | ARMv8)
+#define ARMV7_ABOVE (ARMv7 | ARMv7S | ARMv8)
+
+#define No_VFP 0
+#define VFPv1 (1u << 1)
+#define VFPv2 (1u << 2)
+#define VFPv3 (1u << 3)
+#define AdvancedSIMD (1u << 4)
+
+#define VFPv1_ABOVE (VFPv1 | VFPv2 | VFPv3 | AdvancedSIMD)
+#define VFPv2_ABOVE (VFPv2 | VFPv3 | AdvancedSIMD)
+#define VFPv2v3 (VFPv2 | VFPv3)
+
+//
+// EmulateInstructionARM implementation
+//
+
+void EmulateInstructionARM::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void EmulateInstructionARM::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString EmulateInstructionARM::GetPluginNameStatic() {
+ static ConstString g_name("arm");
+ return g_name;
+}
+
+const char *EmulateInstructionARM::GetPluginDescriptionStatic() {
+ return "Emulate instructions for the ARM architecture.";
+}
+
+EmulateInstruction *
+EmulateInstructionARM::CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type) {
+ if (EmulateInstructionARM::SupportsEmulatingInstructionsOfTypeStatic(
+ inst_type)) {
+ if (arch.GetTriple().getArch() == llvm::Triple::arm) {
+ std::unique_ptr<EmulateInstructionARM> emulate_insn_up(
+ new EmulateInstructionARM(arch));
+
+ if (emulate_insn_up)
+ return emulate_insn_up.release();
+ } else if (arch.GetTriple().getArch() == llvm::Triple::thumb) {
+ std::unique_ptr<EmulateInstructionARM> emulate_insn_up(
+ new EmulateInstructionARM(arch));
+
+ if (emulate_insn_up)
+ return emulate_insn_up.release();
+ }
+ }
+
+ return nullptr;
+}
+
+bool EmulateInstructionARM::SetTargetTriple(const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::arm)
+ return true;
+ else if (arch.GetTriple().getArch() == llvm::Triple::thumb)
+ return true;
+
+ return false;
+}
+
+// Write "bits (32) UNKNOWN" to memory address "address". Helper function for
+// many ARM instructions.
+bool EmulateInstructionARM::WriteBits32UnknownToMemory(addr_t address) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextWriteMemoryRandomBits;
+ context.SetNoArgs();
+
+ uint32_t random_data = rand();
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ return MemAWrite(context, address, random_data, addr_byte_size);
+}
+
+// Write "bits (32) UNKNOWN" to register n. Helper function for many ARM
+// instructions.
+bool EmulateInstructionARM::WriteBits32Unknown(int n) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextWriteRegisterRandomBits;
+ context.SetNoArgs();
+
+ bool success;
+ uint32_t data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, data))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionARM::GetRegisterInfo(lldb::RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterInfo &reg_info) {
+ if (reg_kind == eRegisterKindGeneric) {
+ switch (reg_num) {
+ case LLDB_REGNUM_GENERIC_PC:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_pc;
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_sp;
+ break;
+ case LLDB_REGNUM_GENERIC_FP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_r7;
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_lr;
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_cpsr;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ if (reg_kind == eRegisterKindDWARF)
+ return GetARMDWARFRegisterInfo(reg_num, reg_info);
+ return false;
+}
+
+uint32_t EmulateInstructionARM::GetFramePointerRegisterNumber() const {
+ if (m_arch.GetTriple().isAndroid())
+ return LLDB_INVALID_REGNUM; // Don't use frame pointer on android
+ bool is_apple = false;
+ if (m_arch.GetTriple().getVendor() == llvm::Triple::Apple)
+ is_apple = true;
+ switch (m_arch.GetTriple().getOS()) {
+ case llvm::Triple::Darwin:
+ case llvm::Triple::MacOSX:
+ case llvm::Triple::IOS:
+ case llvm::Triple::TvOS:
+ case llvm::Triple::WatchOS:
+ // NEED_BRIDGEOS_TRIPLE case llvm::Triple::BridgeOS:
+ is_apple = true;
+ break;
+ default:
+ break;
+ }
+
+ /* On Apple iOS et al, the frame pointer register is always r7.
+ * Typically on other ARM systems, thumb code uses r7; arm code uses r11.
+ */
+
+ uint32_t fp_regnum = 11;
+
+ if (is_apple)
+ fp_regnum = 7;
+
+ if (m_opcode_mode == eModeThumb)
+ fp_regnum = 7;
+
+ return fp_regnum;
+}
+
+uint32_t EmulateInstructionARM::GetFramePointerDWARFRegisterNumber() const {
+ bool is_apple = false;
+ if (m_arch.GetTriple().getVendor() == llvm::Triple::Apple)
+ is_apple = true;
+ switch (m_arch.GetTriple().getOS()) {
+ case llvm::Triple::Darwin:
+ case llvm::Triple::MacOSX:
+ case llvm::Triple::IOS:
+ is_apple = true;
+ break;
+ default:
+ break;
+ }
+
+ /* On Apple iOS et al, the frame pointer register is always r7.
+ * Typically on other ARM systems, thumb code uses r7; arm code uses r11.
+ */
+
+ uint32_t fp_regnum = dwarf_r11;
+
+ if (is_apple)
+ fp_regnum = dwarf_r7;
+
+ if (m_opcode_mode == eModeThumb)
+ fp_regnum = dwarf_r7;
+
+ return fp_regnum;
+}
+
+// Push Multiple Registers stores multiple registers to the stack, storing to
+// consecutive memory locations ending just below the address in SP, and
+// updates
+// SP to point to the start of the stored data.
+bool EmulateInstructionARM::EmulatePUSH(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ NullCheckIfThumbEE(13);
+ address = SP - 4*BitCount(registers);
+
+ for (i = 0 to 14)
+ {
+ if (registers<i> == '1')
+ {
+ if i == 13 && i != LowestSetBit(registers) // Only possible for encoding A1
+ MemA[address,4] = bits(32) UNKNOWN;
+ else
+ MemA[address,4] = R[i];
+ address = address + 4;
+ }
+ }
+
+ if (registers<15> == '1') // Only possible for encoding A1 or A2
+ MemA[address,4] = PCStoreValue();
+
+ SP = SP - 4*BitCount(registers);
+ }
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t registers = 0;
+ uint32_t Rt; // the source register
+ switch (encoding) {
+ case eEncodingT1:
+ registers = Bits32(opcode, 7, 0);
+ // The M bit represents LR.
+ if (Bit32(opcode, 8))
+ registers |= (1u << 14);
+ // if BitCount(registers) < 1 then UNPREDICTABLE;
+ if (BitCount(registers) < 1)
+ return false;
+ break;
+ case eEncodingT2:
+ // Ignore bits 15 & 13.
+ registers = Bits32(opcode, 15, 0) & ~0xa000;
+ // if BitCount(registers) < 2 then UNPREDICTABLE;
+ if (BitCount(registers) < 2)
+ return false;
+ break;
+ case eEncodingT3:
+ Rt = Bits32(opcode, 15, 12);
+ // if BadReg(t) then UNPREDICTABLE;
+ if (BadReg(Rt))
+ return false;
+ registers = (1u << Rt);
+ break;
+ case eEncodingA1:
+ registers = Bits32(opcode, 15, 0);
+ // Instead of return false, let's handle the following case as well,
+ // which amounts to pushing one reg onto the full descending stacks.
+ // if BitCount(register_list) < 2 then SEE STMDB / STMFD;
+ break;
+ case eEncodingA2:
+ Rt = Bits32(opcode, 15, 12);
+ // if t == 13 then UNPREDICTABLE;
+ if (Rt == dwarf_sp)
+ return false;
+ registers = (1u << Rt);
+ break;
+ default:
+ return false;
+ }
+ addr_t sp_offset = addr_byte_size * BitCount(registers);
+ addr_t addr = sp - sp_offset;
+ uint32_t i;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextPushRegisterOnStack;
+ RegisterInfo reg_info;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ for (i = 0; i < 15; ++i) {
+ if (BitIsSet(registers, i)) {
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i, reg_info);
+ context.SetRegisterToRegisterPlusOffset(reg_info, sp_reg, addr - sp);
+ uint32_t reg_value = ReadCoreReg(i, &success);
+ if (!success)
+ return false;
+ if (!MemAWrite(context, addr, reg_value, addr_byte_size))
+ return false;
+ addr += addr_byte_size;
+ }
+ }
+
+ if (BitIsSet(registers, 15)) {
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, reg_info);
+ context.SetRegisterToRegisterPlusOffset(reg_info, sp_reg, addr - sp);
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+ if (!MemAWrite(context, addr, pc, addr_byte_size))
+ return false;
+ }
+
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(-sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, sp - sp_offset))
+ return false;
+ }
+ return true;
+}
+
+// Pop Multiple Registers loads multiple registers from the stack, loading from
+// consecutive memory locations staring at the address in SP, and updates
+// SP to point just above the loaded data.
+bool EmulateInstructionARM::EmulatePOP(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations(); NullCheckIfThumbEE(13);
+ address = SP;
+ for i = 0 to 14
+ if registers<i> == '1' then
+ R[i] = if UnalignedAllowed then MemU[address,4] else MemA[address,4]; address = address + 4;
+ if registers<15> == '1' then
+ if UnalignedAllowed then
+ LoadWritePC(MemU[address,4]);
+ else
+ LoadWritePC(MemA[address,4]);
+ if registers<13> == '0' then SP = SP + 4*BitCount(registers);
+ if registers<13> == '1' then SP = bits(32) UNKNOWN;
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t registers = 0;
+ uint32_t Rt; // the destination register
+ switch (encoding) {
+ case eEncodingT1:
+ registers = Bits32(opcode, 7, 0);
+ // The P bit represents PC.
+ if (Bit32(opcode, 8))
+ registers |= (1u << 15);
+ // if BitCount(registers) < 1 then UNPREDICTABLE;
+ if (BitCount(registers) < 1)
+ return false;
+ break;
+ case eEncodingT2:
+ // Ignore bit 13.
+ registers = Bits32(opcode, 15, 0) & ~0x2000;
+ // if BitCount(registers) < 2 || (P == '1' && M == '1') then
+ // UNPREDICTABLE;
+ if (BitCount(registers) < 2 || (Bit32(opcode, 15) && Bit32(opcode, 14)))
+ return false;
+ // if registers<15> == '1' && InITBlock() && !LastInITBlock() then
+ // UNPREDICTABLE;
+ if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingT3:
+ Rt = Bits32(opcode, 15, 12);
+ // if t == 13 || (t == 15 && InITBlock() && !LastInITBlock()) then
+ // UNPREDICTABLE;
+ if (Rt == 13)
+ return false;
+ if (Rt == 15 && InITBlock() && !LastInITBlock())
+ return false;
+ registers = (1u << Rt);
+ break;
+ case eEncodingA1:
+ registers = Bits32(opcode, 15, 0);
+ // Instead of return false, let's handle the following case as well,
+ // which amounts to popping one reg from the full descending stacks.
+ // if BitCount(register_list) < 2 then SEE LDM / LDMIA / LDMFD;
+
+ // if registers<13> == '1' && ArchVersion() >= 7 then UNPREDICTABLE;
+ if (BitIsSet(opcode, 13) && ArchVersion() >= ARMv7)
+ return false;
+ break;
+ case eEncodingA2:
+ Rt = Bits32(opcode, 15, 12);
+ // if t == 13 then UNPREDICTABLE;
+ if (Rt == dwarf_sp)
+ return false;
+ registers = (1u << Rt);
+ break;
+ default:
+ return false;
+ }
+ addr_t sp_offset = addr_byte_size * BitCount(registers);
+ addr_t addr = sp;
+ uint32_t i, data;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextPopRegisterOffStack;
+
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+
+ for (i = 0; i < 15; ++i) {
+ if (BitIsSet(registers, i)) {
+ context.SetAddress(addr);
+ data = MemARead(context, addr, 4, 0, &success);
+ if (!success)
+ return false;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i,
+ data))
+ return false;
+ addr += addr_byte_size;
+ }
+ }
+
+ if (BitIsSet(registers, 15)) {
+ context.SetRegisterPlusOffset(sp_reg, addr - sp);
+ data = MemARead(context, addr, 4, 0, &success);
+ if (!success)
+ return false;
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ // addr += addr_byte_size;
+ }
+
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, sp + sp_offset))
+ return false;
+ }
+ return true;
+}
+
+// Set r7 or ip to point to saved value residing within the stack.
+// ADD (SP plus immediate)
+bool EmulateInstructionARM::EmulateADDRdSPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(SP, imm32, '0');
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t Rd; // the destination register
+ uint32_t imm32;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = 7;
+ imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32)
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+ addr_t sp_offset = imm32;
+ addr_t addr = sp + sp_offset; // a pointer to the stack area
+
+ EmulateInstruction::Context context;
+ if (Rd == GetFramePointerRegisterNumber())
+ context.type = eContextSetFramePointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ context.SetRegisterPlusOffset(sp_reg, sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rd,
+ addr))
+ return false;
+ }
+ return true;
+}
+
+// Set r7 or ip to the current stack pointer.
+// MOV (register)
+bool EmulateInstructionARM::EmulateMOVRdSP(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ result = R[m];
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ // APSR.C unchanged
+ // APSR.V unchanged
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t Rd; // the destination register
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = 7;
+ break;
+ case eEncodingA1:
+ Rd = 12;
+ break;
+ default:
+ return false;
+ }
+
+ EmulateInstruction::Context context;
+ if (Rd == GetFramePointerRegisterNumber())
+ context.type = EmulateInstruction::eContextSetFramePointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ context.SetRegisterPlusOffset(sp_reg, 0);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rd, sp))
+ return false;
+ }
+ return true;
+}
+
+// Move from high register (r8-r15) to low register (r0-r7).
+// MOV (register)
+bool EmulateInstructionARM::EmulateMOVLowHigh(const uint32_t opcode,
+ const ARMEncoding encoding) {
+ return EmulateMOVRdRm(opcode, encoding);
+}
+
+// Move from register to register.
+// MOV (register)
+bool EmulateInstructionARM::EmulateMOVRdRm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ result = R[m];
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ // APSR.C unchanged
+ // APSR.V unchanged
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rm; // the source register
+ uint32_t Rd; // the destination register
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 6, 3);
+ setflags = false;
+ if (Rd == 15 && InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = true;
+ if (InITBlock())
+ return false;
+ break;
+ case eEncodingT3:
+ Rd = Bits32(opcode, 11, 8);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ // if setflags && (BadReg(d) || BadReg(m)) then UNPREDICTABLE;
+ if (setflags && (BadReg(Rd) || BadReg(Rm)))
+ return false;
+ // if !setflags && (d == 15 || m == 15 || (d == 13 && m == 13)) then
+ // UNPREDICTABLE;
+ if (!setflags && (Rd == 15 || Rm == 15 || (Rd == 13 && Rm == 13)))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ uint32_t result = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ // The context specifies that Rm is to be moved into Rd.
+ EmulateInstruction::Context context;
+ if (Rd == 13)
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, 0);
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags))
+ return false;
+ }
+ return true;
+}
+
+// Move (immediate) writes an immediate value to the destination register. It
+// can optionally update the condition flags based on the value.
+// MOV (immediate)
+bool EmulateInstructionARM::EmulateMOVRdImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ result = imm32;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd; // the destination register
+ uint32_t imm32; // the immediate value to be written to Rd
+ uint32_t carry =
+ 0; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C.
+ // for setflags == false, this value is a don't care initialized to
+ // 0 to silence the static analyzer
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 10, 8);
+ setflags = !InITBlock();
+ imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32)
+ carry = APSR_C;
+
+ break;
+
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(opcode, APSR_C, carry);
+ if (BadReg(Rd))
+ return false;
+
+ break;
+
+ case eEncodingT3: {
+ // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:i:imm3:imm8,
+ // 32);
+ Rd = Bits32(opcode, 11, 8);
+ setflags = false;
+ uint32_t imm4 = Bits32(opcode, 19, 16);
+ uint32_t imm3 = Bits32(opcode, 14, 12);
+ uint32_t i = Bit32(opcode, 26);
+ uint32_t imm8 = Bits32(opcode, 7, 0);
+ imm32 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
+
+ // if BadReg(d) then UNPREDICTABLE;
+ if (BadReg(Rd))
+ return false;
+ } break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); setflags = (S == '1'); (imm32, carry) =
+ // ARMExpandImm_C(imm12, APSR.C);
+ Rd = Bits32(opcode, 15, 12);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm_C(opcode, APSR_C, carry);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if ((Rd == 15) && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+
+ break;
+
+ case eEncodingA2: {
+ // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:imm12, 32);
+ Rd = Bits32(opcode, 15, 12);
+ setflags = false;
+ uint32_t imm4 = Bits32(opcode, 19, 16);
+ uint32_t imm12 = Bits32(opcode, 11, 0);
+ imm32 = (imm4 << 12) | imm12;
+
+ // if d == 15 then UNPREDICTABLE;
+ if (Rd == 15)
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+ uint32_t result = imm32;
+
+ // The context specifies that an immediate is to be moved into Rd.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// MUL multiplies two register values. The least significant 32 bits of the
+// result are written to the destination
+// register. These 32 bits do not depend on whether the source register values
+// are considered to be signed values or unsigned values.
+//
+// Optionally, it can update the condition flags based on the result. In the
+// Thumb instruction set, this option is limited to only a few forms of the
+// instruction.
+bool EmulateInstructionARM::EmulateMUL(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final results
+ operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final results
+ result = operand1 * operand2;
+ R[d] = result<31:0>;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ if ArchVersion() == 4 then
+ APSR.C = bit UNKNOWN;
+ // else APSR.C unchanged
+ // APSR.V always unchanged
+#endif
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool setflags;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rdm); n = UInt(Rn); m = UInt(Rdm); setflags = !InITBlock();
+ d = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 2, 0);
+ setflags = !InITBlock();
+
+ // if ArchVersion() < 6 && d == n then UNPREDICTABLE;
+ if ((ArchVersion() < ARMv6) && (d == n))
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = FALSE;
+ d = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ setflags = false;
+
+ // if BadReg(d) || BadReg(n) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(n) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == '1');
+ d = Bits32(opcode, 19, 16);
+ n = Bits32(opcode, 3, 0);
+ m = Bits32(opcode, 11, 8);
+ setflags = BitIsSet(opcode, 20);
+
+ // if d == 15 || n == 15 || m == 15 then UNPREDICTABLE;
+ if ((d == 15) || (n == 15) || (m == 15))
+ return false;
+
+ // if ArchVersion() < 6 && d == n then UNPREDICTABLE;
+ if ((ArchVersion() < ARMv6) && (d == n))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ bool success = false;
+
+ // operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final
+ // results
+ uint64_t operand1 =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ // operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final
+ // results
+ uint64_t operand2 =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // result = operand1 * operand2;
+ uint64_t result = operand1 * operand2;
+
+ // R[d] = result<31:0>;
+ RegisterInfo op1_reg;
+ RegisterInfo op2_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, op1_reg);
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, op2_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ context.SetRegisterRegisterOperands(op1_reg, op2_reg);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ (0x0000ffff & result)))
+ return false;
+
+ // if setflags then
+ if (setflags) {
+ // APSR.N = result<31>;
+ // APSR.Z = IsZeroBit(result);
+ m_new_inst_cpsr = m_opcode_cpsr;
+ SetBit32(m_new_inst_cpsr, CPSR_N_POS, Bit32(result, 31));
+ SetBit32(m_new_inst_cpsr, CPSR_Z_POS, result == 0 ? 1 : 0);
+ if (m_new_inst_cpsr != m_opcode_cpsr) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
+ return false;
+ }
+
+ // if ArchVersion() == 4 then
+ // APSR.C = bit UNKNOWN;
+ }
+ }
+ return true;
+}
+
+// Bitwise NOT (immediate) writes the bitwise inverse of an immediate value to
+// the destination register. It can optionally update the condition flags based
+// on the value.
+bool EmulateInstructionARM::EmulateMVNImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ result = NOT(imm32);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd; // the destination register
+ uint32_t imm32; // the output after ThumbExpandImm_C or ARMExpandImm_C
+ uint32_t carry; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(opcode, APSR_C, carry);
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm_C(opcode, APSR_C, carry);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ uint32_t result = ~imm32;
+
+ // The context specifies that an immediate is to be moved into Rd.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise NOT (register) writes the bitwise inverse of a register value to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateMVNReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = NOT(shifted);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rm; // the source register
+ uint32_t Rd; // the destination register
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ uint32_t carry; // the carry bit after the shift operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ if (InITBlock())
+ return false;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE;
+ if (BadReg(Rd) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+ bool success = false;
+ uint32_t value = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted =
+ Shift_C(value, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = ~shifted;
+
+ // The context specifies that an immediate is to be moved into Rd.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// PC relative immediate load into register, possibly followed by ADD (SP plus
+// register).
+// LDR (literal)
+bool EmulateInstructionARM::EmulateLDRRtPCRelative(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ base = Align(PC,4);
+ address = if add then (base + imm32) else (base - imm32);
+ data = MemU[address,4];
+ if t == 15 then
+ if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ elsif UnalignedSupport() || address<1:0> = '00' then
+ R[t] = data;
+ else // Can only apply before ARMv7
+ if CurrentInstrSet() == InstrSet_ARM then
+ R[t] = ROR(data, 8*UInt(address<1:0>));
+ else
+ R[t] = bits(32) UNKNOWN;
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ bool success = false;
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ // PC relative immediate load context
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo pc_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, pc_reg);
+ context.SetRegisterPlusOffset(pc_reg, 0);
+
+ uint32_t Rt; // the destination register
+ uint32_t imm32; // immediate offset from the PC
+ bool add; // +imm32 or -imm32?
+ addr_t base; // the base address
+ addr_t address; // the PC relative address
+ uint32_t data; // the literal data value from the PC relative load
+ switch (encoding) {
+ case eEncodingT1:
+ Rt = Bits32(opcode, 10, 8);
+ imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32);
+ add = true;
+ break;
+ case eEncodingT2:
+ Rt = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0) << 2; // imm32 = ZeroExtend(imm12, 32);
+ add = BitIsSet(opcode, 23);
+ if (Rt == 15 && InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ base = Align(pc, 4);
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ context.SetRegisterPlusOffset(pc_reg, address - base);
+ data = MemURead(context, address, 4, 0, &success);
+ if (!success)
+ return false;
+
+ if (Rt == 15) {
+ if (Bits32(address, 1, 0) == 0) {
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ } else
+ return false;
+ } else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rt,
+ data))
+ return false;
+ } else // We don't handle ARM for now.
+ return false;
+ }
+ return true;
+}
+
+// An add operation to adjust the SP.
+// ADD (SP plus immediate)
+bool EmulateInstructionARM::EmulateADDSPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(SP, imm32, '0');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t imm32; // the immediate operand
+ uint32_t d;
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm8:'00', 32);
+ d = Bits32(opcode, 10, 8);
+ imm32 = (Bits32(opcode, 7, 0) << 2);
+ setflags = false;
+ break;
+
+ case eEncodingT2:
+ // d = 13; setflags = FALSE; imm32 = ZeroExtend(imm7:'00', 32);
+ d = 13;
+ imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32)
+ setflags = false;
+ break;
+
+ case eEncodingT3:
+ // d = UInt(Rd); setflags = (S == "1"); imm32 =
+ // ThumbExpandImm(i:imm3:imm8);
+ d = Bits32(opcode, 11, 8);
+ imm32 = ThumbExpandImm(opcode);
+ setflags = Bit32(opcode, 20);
+
+ // if Rd == "1111" && S == "1" then SEE CMN (immediate);
+ if (d == 15 && setflags == 1)
+ return false; // CMN (immediate) not yet supported
+
+ // if d == 15 && S == "0" then UNPREDICTABLE;
+ if (d == 15 && setflags == 0)
+ return false;
+ break;
+
+ case eEncodingT4: {
+ // if Rn == '1111' then SEE ADR;
+ // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(i:imm3:imm8, 32);
+ d = Bits32(opcode, 11, 8);
+ setflags = false;
+ uint32_t i = Bit32(opcode, 26);
+ uint32_t imm3 = Bits32(opcode, 14, 12);
+ uint32_t imm8 = Bits32(opcode, 7, 0);
+ imm32 = (i << 11) | (imm3 << 8) | imm8;
+
+ // if d == 15 then UNPREDICTABLE;
+ if (d == 15)
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+ // (result, carry, overflow) = AddWithCarry(R[n], imm32, '0');
+ AddWithCarryResult res = AddWithCarry(sp, imm32, 0);
+
+ EmulateInstruction::Context context;
+ if (d == 13)
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ context.SetRegisterPlusOffset(sp_reg, res.result - sp);
+
+ if (d == 15) {
+ if (!ALUWritePC(context, res.result))
+ return false;
+ } else {
+ // R[d] = result;
+ // if setflags then
+ // APSR.N = result<31>;
+ // APSR.Z = IsZeroBit(result);
+ // APSR.C = carry;
+ // APSR.V = overflow;
+ if (!WriteCoreRegOptionalFlags(context, res.result, d, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ }
+ return true;
+}
+
+// An add operation to adjust the SP.
+// ADD (SP plus register)
+bool EmulateInstructionARM::EmulateADDSPRm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(SP, shifted, '0');
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t Rm; // the second operand
+ switch (encoding) {
+ case eEncodingT2:
+ Rm = Bits32(opcode, 6, 3);
+ break;
+ default:
+ return false;
+ }
+ int32_t reg_value = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ addr_t addr = (int32_t)sp + reg_value; // the adjusted stack pointer value
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+
+ RegisterInfo other_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, other_reg);
+ context.SetRegisterRegisterOperands(sp_reg, other_reg);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, addr))
+ return false;
+ }
+ return true;
+}
+
+// Branch with Link and Exchange Instruction Sets (immediate) calls a
+// subroutine at a PC-relative address, and changes instruction set from ARM to
+// Thumb, or from Thumb to ARM.
+// BLX (immediate)
+bool EmulateInstructionARM::EmulateBLXImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ if CurrentInstrSet() == InstrSet_ARM then
+ LR = PC - 4;
+ else
+ LR = PC<31:1> : '1';
+ if targetInstrSet == InstrSet_ARM then
+ targetAddress = Align(PC,4) + imm32;
+ else
+ targetAddress = PC + imm32;
+ SelectInstrSet(targetInstrSet);
+ BranchWritePC(targetAddress);
+ }
+#endif
+
+ bool success = true;
+
+ if (ConditionPassed(opcode)) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+ addr_t lr; // next instruction address
+ addr_t target; // target address
+ int32_t imm32; // PC-relative offset
+ switch (encoding) {
+ case eEncodingT1: {
+ lr = pc | 1u; // return address
+ uint32_t S = Bit32(opcode, 26);
+ uint32_t imm10 = Bits32(opcode, 25, 16);
+ uint32_t J1 = Bit32(opcode, 13);
+ uint32_t J2 = Bit32(opcode, 11);
+ uint32_t imm11 = Bits32(opcode, 10, 0);
+ uint32_t I1 = !(J1 ^ S);
+ uint32_t I2 = !(J2 ^ S);
+ uint32_t imm25 =
+ (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
+ imm32 = llvm::SignExtend32<25>(imm25);
+ target = pc + imm32;
+ SelectInstrSet(eModeThumb);
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ }
+ case eEncodingT2: {
+ lr = pc | 1u; // return address
+ uint32_t S = Bit32(opcode, 26);
+ uint32_t imm10H = Bits32(opcode, 25, 16);
+ uint32_t J1 = Bit32(opcode, 13);
+ uint32_t J2 = Bit32(opcode, 11);
+ uint32_t imm10L = Bits32(opcode, 10, 1);
+ uint32_t I1 = !(J1 ^ S);
+ uint32_t I2 = !(J2 ^ S);
+ uint32_t imm25 =
+ (S << 24) | (I1 << 23) | (I2 << 22) | (imm10H << 12) | (imm10L << 2);
+ imm32 = llvm::SignExtend32<25>(imm25);
+ target = Align(pc, 4) + imm32;
+ SelectInstrSet(eModeARM);
+ context.SetISAAndImmediateSigned(eModeARM, 4 + imm32);
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ }
+ case eEncodingA1:
+ lr = pc - 4; // return address
+ imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2);
+ target = Align(pc, 4) + imm32;
+ SelectInstrSet(eModeARM);
+ context.SetISAAndImmediateSigned(eModeARM, 8 + imm32);
+ break;
+ case eEncodingA2:
+ lr = pc - 4; // return address
+ imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2 |
+ Bits32(opcode, 24, 24) << 1);
+ target = pc + imm32;
+ SelectInstrSet(eModeThumb);
+ context.SetISAAndImmediateSigned(eModeThumb, 8 + imm32);
+ break;
+ default:
+ return false;
+ }
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA, lr))
+ return false;
+ if (!BranchWritePC(context, target))
+ return false;
+ if (m_opcode_cpsr != m_new_inst_cpsr)
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
+ return false;
+ }
+ return true;
+}
+
+// Branch with Link and Exchange (register) calls a subroutine at an address
+// and instruction set specified by a register.
+// BLX (register)
+bool EmulateInstructionARM::EmulateBLXRm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ target = R[m];
+ if CurrentInstrSet() == InstrSet_ARM then
+ next_instr_addr = PC - 4;
+ LR = next_instr_addr;
+ else
+ next_instr_addr = PC - 2;
+ LR = next_instr_addr<31:1> : '1';
+ BXWritePC(target);
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextAbsoluteBranchRegister;
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ addr_t lr; // next instruction address
+ if (!success)
+ return false;
+ uint32_t Rm; // the register with the target address
+ switch (encoding) {
+ case eEncodingT1:
+ lr = (pc - 2) | 1u; // return address
+ Rm = Bits32(opcode, 6, 3);
+ // if m == 15 then UNPREDICTABLE;
+ if (Rm == 15)
+ return false;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingA1:
+ lr = pc - 4; // return address
+ Rm = Bits32(opcode, 3, 0);
+ // if m == 15 then UNPREDICTABLE;
+ if (Rm == 15)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ addr_t target = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
+ context.SetRegister(dwarf_reg);
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA, lr))
+ return false;
+ if (!BXWritePC(context, target))
+ return false;
+ }
+ return true;
+}
+
+// Branch and Exchange causes a branch to an address and instruction set
+// specified by a register.
+bool EmulateInstructionARM::EmulateBXRm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ BXWritePC(R[m]);
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextAbsoluteBranchRegister;
+ uint32_t Rm; // the register with the target address
+ switch (encoding) {
+ case eEncodingT1:
+ Rm = Bits32(opcode, 6, 3);
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingA1:
+ Rm = Bits32(opcode, 3, 0);
+ break;
+ default:
+ return false;
+ }
+ bool success = false;
+ addr_t target = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
+ context.SetRegister(dwarf_reg);
+ if (!BXWritePC(context, target))
+ return false;
+ }
+ return true;
+}
+
+// Branch and Exchange Jazelle attempts to change to Jazelle state. If the
+// attempt fails, it branches to an address and instruction set specified by a
+// register as though it were a BX instruction.
+//
+// TODO: Emulate Jazelle architecture?
+// We currently assume that switching to Jazelle state fails, thus
+// treating BXJ as a BX operation.
+bool EmulateInstructionARM::EmulateBXJRm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ if JMCR.JE == '0' || CurrentInstrSet() == InstrSet_ThumbEE then
+ BXWritePC(R[m]);
+ else
+ if JazelleAcceptsExecution() then
+ SwitchToJazelleExecution();
+ else
+ SUBARCHITECTURE_DEFINED handler call;
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextAbsoluteBranchRegister;
+ uint32_t Rm; // the register with the target address
+ switch (encoding) {
+ case eEncodingT1:
+ Rm = Bits32(opcode, 19, 16);
+ if (BadReg(Rm))
+ return false;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingA1:
+ Rm = Bits32(opcode, 3, 0);
+ if (Rm == 15)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ bool success = false;
+ addr_t target = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
+ context.SetRegister(dwarf_reg);
+ if (!BXWritePC(context, target))
+ return false;
+ }
+ return true;
+}
+
+// Set r7 to point to some ip offset.
+// SUB (immediate)
+bool EmulateInstructionARM::EmulateSUBR7IPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ bool success = false;
+ const addr_t ip = ReadCoreReg(12, &success);
+ if (!success)
+ return false;
+ uint32_t imm32;
+ switch (encoding) {
+ case eEncodingA1:
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+ addr_t ip_offset = imm32;
+ addr_t addr = ip - ip_offset; // the adjusted ip value
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r12, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, -ip_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r7, addr))
+ return false;
+ }
+ return true;
+}
+
+// Set ip to point to some stack offset.
+// SUB (SP minus immediate)
+bool EmulateInstructionARM::EmulateSUBIPSPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ bool success = false;
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t imm32;
+ switch (encoding) {
+ case eEncodingA1:
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+ addr_t sp_offset = imm32;
+ addr_t addr = sp - sp_offset; // the adjusted stack pointer value
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, -sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r12, addr))
+ return false;
+ }
+ return true;
+}
+
+// This instruction subtracts an immediate value from the SP value, and writes
+// the result to the destination register.
+//
+// If Rd == 13 => A sub operation to adjust the SP -- allocate space for local
+// storage.
+bool EmulateInstructionARM::EmulateSUBSPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+
+ uint32_t Rd;
+ bool setflags;
+ uint32_t imm32;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = 13;
+ setflags = false;
+ imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32)
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (Rd == 15 && setflags)
+ return EmulateCMPImm(opcode, eEncodingT2);
+ if (Rd == 15 && !setflags)
+ return false;
+ break;
+ case eEncodingT3:
+ Rd = Bits32(opcode, 11, 8);
+ setflags = false;
+ imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32)
+ if (Rd == 15)
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ AddWithCarryResult res = AddWithCarry(sp, ~imm32, 1);
+
+ EmulateInstruction::Context context;
+ if (Rd == 13) {
+ uint64_t imm64 = imm32; // Need to expand it to 64 bits before attempting
+ // to negate it, or the wrong
+ // value gets passed down to context.SetImmediateSigned.
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(-imm64); // the stack pointer offset
+ } else {
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ }
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// A store operation to the stack that also updates the SP.
+bool EmulateInstructionARM::EmulateSTRRtSP(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ MemU[address,4] = if t == 15 then PCStoreValue() else R[t];
+ if wback then R[n] = offset_addr;
+ }
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t Rt; // the source register
+ uint32_t imm12;
+ uint32_t
+ Rn; // This function assumes Rn is the SP, but we should verify that.
+
+ bool index;
+ bool add;
+ bool wback;
+ switch (encoding) {
+ case eEncodingA1:
+ Rt = Bits32(opcode, 15, 12);
+ imm12 = Bits32(opcode, 11, 0);
+ Rn = Bits32(opcode, 19, 16);
+
+ if (Rn != 13) // 13 is the SP reg on ARM. Verify that Rn == SP.
+ return false;
+
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ if (wback && ((Rn == 15) || (Rn == Rt)))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ addr_t offset_addr;
+ if (add)
+ offset_addr = sp + imm12;
+ else
+ offset_addr = sp - imm12;
+
+ addr_t addr;
+ if (index)
+ addr = offset_addr;
+ else
+ addr = sp;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextPushRegisterOnStack;
+ RegisterInfo sp_reg;
+ RegisterInfo dwarf_reg;
+
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rt, dwarf_reg);
+ context.SetRegisterToRegisterPlusOffset(dwarf_reg, sp_reg, addr - sp);
+ if (Rt != 15) {
+ uint32_t reg_value = ReadCoreReg(Rt, &success);
+ if (!success)
+ return false;
+ if (!MemUWrite(context, addr, reg_value, addr_byte_size))
+ return false;
+ } else {
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+ if (!MemUWrite(context, addr, pc, addr_byte_size))
+ return false;
+ }
+
+ if (wback) {
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(addr - sp);
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// Vector Push stores multiple extension registers to the stack. It also
+// updates SP to point to the start of the stored data.
+bool EmulateInstructionARM::EmulateVPUSH(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13);
+ address = SP - imm32;
+ SP = SP - imm32;
+ if single_regs then
+ for r = 0 to regs-1
+ MemA[address,4] = S[d+r]; address = address+4;
+ else
+ for r = 0 to regs-1
+ // Store as two word-aligned words in the correct order for
+ // current endianness.
+ MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>;
+ MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>;
+ address = address+8;
+ }
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ bool single_regs;
+ uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register
+ uint32_t imm32; // stack offset
+ uint32_t regs; // number of registers
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ single_regs = false;
+ d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 7, 0) * addr_byte_size;
+ // If UInt(imm8) is odd, see "FSTMX".
+ regs = Bits32(opcode, 7, 0) / 2;
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if (regs == 0 || regs > 16 || (d + regs) > 32)
+ return false;
+ break;
+ case eEncodingT2:
+ case eEncodingA2:
+ single_regs = true;
+ d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22);
+ imm32 = Bits32(opcode, 7, 0) * addr_byte_size;
+ regs = Bits32(opcode, 7, 0);
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if (regs == 0 || regs > 16 || (d + regs) > 32)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0;
+ uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2;
+ addr_t sp_offset = imm32;
+ addr_t addr = sp - sp_offset;
+ uint32_t i;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextPushRegisterOnStack;
+
+ RegisterInfo dwarf_reg;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ for (i = 0; i < regs; ++i) {
+ GetRegisterInfo(eRegisterKindDWARF, start_reg + d + i, dwarf_reg);
+ context.SetRegisterToRegisterPlusOffset(dwarf_reg, sp_reg, addr - sp);
+ // uint64_t to accommodate 64-bit registers.
+ uint64_t reg_value = ReadRegisterUnsigned(&dwarf_reg, 0, &success);
+ if (!success)
+ return false;
+ if (!MemAWrite(context, addr, reg_value, reg_byte_size))
+ return false;
+ addr += reg_byte_size;
+ }
+
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(-sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, sp - sp_offset))
+ return false;
+ }
+ return true;
+}
+
+// Vector Pop loads multiple extension registers from the stack. It also
+// updates SP to point just above the loaded data.
+bool EmulateInstructionARM::EmulateVPOP(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13);
+ address = SP;
+ SP = SP + imm32;
+ if single_regs then
+ for r = 0 to regs-1
+ S[d+r] = MemA[address,4]; address = address+4;
+ else
+ for r = 0 to regs-1
+ word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8;
+ // Combine the word-aligned words in the correct order for
+ // current endianness.
+ D[d+r] = if BigEndian() then word1:word2 else word2:word1;
+ }
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ bool single_regs;
+ uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register
+ uint32_t imm32; // stack offset
+ uint32_t regs; // number of registers
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ single_regs = false;
+ d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 7, 0) * addr_byte_size;
+ // If UInt(imm8) is odd, see "FLDMX".
+ regs = Bits32(opcode, 7, 0) / 2;
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if (regs == 0 || regs > 16 || (d + regs) > 32)
+ return false;
+ break;
+ case eEncodingT2:
+ case eEncodingA2:
+ single_regs = true;
+ d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22);
+ imm32 = Bits32(opcode, 7, 0) * addr_byte_size;
+ regs = Bits32(opcode, 7, 0);
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if (regs == 0 || regs > 16 || (d + regs) > 32)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0;
+ uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2;
+ addr_t sp_offset = imm32;
+ addr_t addr = sp;
+ uint32_t i;
+ uint64_t data; // uint64_t to accommodate 64-bit registers.
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextPopRegisterOffStack;
+
+ RegisterInfo dwarf_reg;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ for (i = 0; i < regs; ++i) {
+ GetRegisterInfo(eRegisterKindDWARF, start_reg + d + i, dwarf_reg);
+ context.SetAddress(addr);
+ data = MemARead(context, addr, reg_byte_size, 0, &success);
+ if (!success)
+ return false;
+ if (!WriteRegisterUnsigned(context, &dwarf_reg, data))
+ return false;
+ addr += reg_byte_size;
+ }
+
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, sp + sp_offset))
+ return false;
+ }
+ return true;
+}
+
+// SVC (previously SWI)
+bool EmulateInstructionARM::EmulateSVC(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ CallSupervisor();
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ addr_t lr; // next instruction address
+ if (!success)
+ return false;
+ uint32_t imm32; // the immediate constant
+ uint32_t mode; // ARM or Thumb mode
+ switch (encoding) {
+ case eEncodingT1:
+ lr = (pc + 2) | 1u; // return address
+ imm32 = Bits32(opcode, 7, 0);
+ mode = eModeThumb;
+ break;
+ case eEncodingA1:
+ lr = pc + 4; // return address
+ imm32 = Bits32(opcode, 23, 0);
+ mode = eModeARM;
+ break;
+ default:
+ return false;
+ }
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextSupervisorCall;
+ context.SetISAAndImmediate(mode, imm32);
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA, lr))
+ return false;
+ }
+ return true;
+}
+
+// If Then makes up to four following instructions (the IT block) conditional.
+bool EmulateInstructionARM::EmulateIT(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ EncodingSpecificOperations();
+ ITSTATE.IT<7:0> = firstcond:mask;
+#endif
+
+ m_it_session.InitIT(Bits32(opcode, 7, 0));
+ return true;
+}
+
+bool EmulateInstructionARM::EmulateNop(const uint32_t opcode,
+ const ARMEncoding encoding) {
+ // NOP, nothing to do...
+ return true;
+}
+
+// Branch causes a branch to a target address.
+bool EmulateInstructionARM::EmulateB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ BranchWritePC(PC + imm32);
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+ addr_t target; // target address
+ int32_t imm32; // PC-relative offset
+ switch (encoding) {
+ case eEncodingT1:
+ // The 'cond' field is handled in EmulateInstructionARM::CurrentCond().
+ imm32 = llvm::SignExtend32<9>(Bits32(opcode, 7, 0) << 1);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ break;
+ case eEncodingT2:
+ imm32 = llvm::SignExtend32<12>(Bits32(opcode, 10, 0) << 1);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ break;
+ case eEncodingT3:
+ // The 'cond' field is handled in EmulateInstructionARM::CurrentCond().
+ {
+ if (Bits32(opcode, 25, 23) == 7)
+ return false; // See Branches and miscellaneous control on page
+ // A6-235.
+
+ uint32_t S = Bit32(opcode, 26);
+ uint32_t imm6 = Bits32(opcode, 21, 16);
+ uint32_t J1 = Bit32(opcode, 13);
+ uint32_t J2 = Bit32(opcode, 11);
+ uint32_t imm11 = Bits32(opcode, 10, 0);
+ uint32_t imm21 =
+ (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1);
+ imm32 = llvm::SignExtend32<21>(imm21);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ break;
+ }
+ case eEncodingT4: {
+ uint32_t S = Bit32(opcode, 26);
+ uint32_t imm10 = Bits32(opcode, 25, 16);
+ uint32_t J1 = Bit32(opcode, 13);
+ uint32_t J2 = Bit32(opcode, 11);
+ uint32_t imm11 = Bits32(opcode, 10, 0);
+ uint32_t I1 = !(J1 ^ S);
+ uint32_t I2 = !(J2 ^ S);
+ uint32_t imm25 =
+ (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
+ imm32 = llvm::SignExtend32<25>(imm25);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ break;
+ }
+ case eEncodingA1:
+ imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeARM, 8 + imm32);
+ break;
+ default:
+ return false;
+ }
+ if (!BranchWritePC(context, target))
+ return false;
+ }
+ return true;
+}
+
+// Compare and Branch on Nonzero and Compare and Branch on Zero compare the
+// value in a register with zero and conditionally branch forward a constant
+// value. They do not affect the condition flags. CBNZ, CBZ
+bool EmulateInstructionARM::EmulateCB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ EncodingSpecificOperations();
+ if nonzero ^ IsZero(R[n]) then
+ BranchWritePC(PC + imm32);
+#endif
+
+ bool success = false;
+
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Bits32(opcode, 2, 0), &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ addr_t target; // target address
+ uint32_t imm32; // PC-relative offset to branch forward
+ bool nonzero;
+ switch (encoding) {
+ case eEncodingT1:
+ imm32 = Bit32(opcode, 9) << 6 | Bits32(opcode, 7, 3) << 1;
+ nonzero = BitIsSet(opcode, 11);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ break;
+ default:
+ return false;
+ }
+ if (m_ignore_conditions || (nonzero ^ (reg_val == 0)))
+ if (!BranchWritePC(context, target))
+ return false;
+
+ return true;
+}
+
+// Table Branch Byte causes a PC-relative forward branch using a table of
+// single byte offsets.
+// A base register provides a pointer to the table, and a second register
+// supplies an index into the table.
+// The branch length is twice the value of the byte returned from the table.
+//
+// Table Branch Halfword causes a PC-relative forward branch using a table of
+// single halfword offsets.
+// A base register provides a pointer to the table, and a second register
+// supplies an index into the table.
+// The branch length is twice the value of the halfword returned from the
+// table. TBB, TBH
+bool EmulateInstructionARM::EmulateTB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ if is_tbh then
+ halfwords = UInt(MemU[R[n]+LSL(R[m],1), 2]);
+ else
+ halfwords = UInt(MemU[R[n]+R[m], 1]);
+ BranchWritePC(PC + 2*halfwords);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rn; // the base register which contains the address of the table of
+ // branch lengths
+ uint32_t Rm; // the index register which contains an integer pointing to a
+ // byte/halfword in the table
+ bool is_tbh; // true if table branch halfword
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ is_tbh = BitIsSet(opcode, 4);
+ if (Rn == 13 || BadReg(Rm))
+ return false;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ // Read the address of the table from the operand register Rn. The PC can
+ // be used, in which case the table immediately follows this instruction.
+ uint32_t base = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // the table index
+ uint32_t index = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ // the offsetted table address
+ addr_t addr = base + (is_tbh ? index * 2 : index);
+
+ // PC-relative offset to branch forward
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextTableBranchReadMemory;
+ uint32_t offset = MemURead(context, addr, is_tbh ? 2 : 1, 0, &success) * 2;
+ if (!success)
+ return false;
+
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ // target address
+ addr_t target = pc + offset;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + offset);
+
+ if (!BranchWritePC(context, target))
+ return false;
+ }
+
+ return true;
+}
+
+// This instruction adds an immediate value to a register value, and writes the
+// result to the destination register. It can optionally update the condition
+// flags based on the result.
+bool EmulateInstructionARM::EmulateADDImmThumb(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], imm32, '0');
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t n;
+ bool setflags;
+ uint32_t imm32;
+ uint32_t carry_out;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); n = UInt(Rn); setflags = !InITBlock(); imm32 =
+ // ZeroExtend(imm3, 32);
+ d = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ imm32 = Bits32(opcode, 8, 6);
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rdn); n = UInt(Rdn); setflags = !InITBlock(); imm32 =
+ // ZeroExtend(imm8, 32);
+ d = Bits32(opcode, 10, 8);
+ n = Bits32(opcode, 10, 8);
+ setflags = !InITBlock();
+ imm32 = Bits32(opcode, 7, 0);
+
+ break;
+
+ case eEncodingT3:
+ // if Rd == '1111' && S == '1' then SEE CMN (immediate);
+ // d = UInt(Rd); n = UInt(Rn); setflags = (S == '1'); imm32 =
+ // ThumbExpandImm(i:imm3:imm8);
+ d = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(opcode, APSR_C, carry_out);
+
+ // if Rn == '1101' then SEE ADD (SP plus immediate);
+ if (n == 13)
+ return EmulateADDSPImm(opcode, eEncodingT3);
+
+ // if BadReg(d) || n == 15 then UNPREDICTABLE;
+ if (BadReg(d) || (n == 15))
+ return false;
+
+ break;
+
+ case eEncodingT4: {
+ // if Rn == '1111' then SEE ADR;
+ // d = UInt(Rd); n = UInt(Rn); setflags = FALSE; imm32 =
+ // ZeroExtend(i:imm3:imm8, 32);
+ d = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ setflags = false;
+ uint32_t i = Bit32(opcode, 26);
+ uint32_t imm3 = Bits32(opcode, 14, 12);
+ uint32_t imm8 = Bits32(opcode, 7, 0);
+ imm32 = (i << 11) | (imm3 << 8) | imm8;
+
+ // if Rn == '1101' then SEE ADD (SP plus immediate);
+ if (n == 13)
+ return EmulateADDSPImm(opcode, eEncodingT4);
+
+ // if BadReg(d) then UNPREDICTABLE;
+ if (BadReg(d))
+ return false;
+
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ //(result, carry, overflow) = AddWithCarry(R[n], imm32, '0');
+ AddWithCarryResult res = AddWithCarry(Rn, imm32, 0);
+
+ RegisterInfo reg_n;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, reg_n);
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ context.SetRegisterPlusOffset(reg_n, imm32);
+
+ // R[d] = result;
+ // if setflags then
+ // APSR.N = result<31>;
+ // APSR.Z = IsZeroBit(result);
+ // APSR.C = carry;
+ // APSR.V = overflow;
+ if (!WriteCoreRegOptionalFlags(context, res.result, d, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// This instruction adds an immediate value to a register value, and writes the
+// result to the destination register. It can optionally update the condition
+// flags based on the result.
+bool EmulateInstructionARM::EmulateADDImmARM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], imm32, '0');
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t
+ imm32; // the immediate value to be added to the value obtained from Rn
+ bool setflags;
+ switch (encoding) {
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(val1, imm32, 0);
+
+ EmulateInstruction::Context context;
+ if (Rd == 13)
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ else if (Rd == GetFramePointerRegisterNumber())
+ context.type = EmulateInstruction::eContextSetFramePointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, Rn, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, imm32);
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// This instruction adds a register value and an optionally-shifted register
+// value, and writes the result to the destination register. It can optionally
+// update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateADDReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], shifted, '0');
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 2, 0);
+ Rn = Bits32(opcode, 5, 3);
+ Rm = Bits32(opcode, 8, 6);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 6, 3);
+ setflags = false;
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ if (Rn == 15 && Rm == 15)
+ return false;
+ if (Rd == 15 && InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(val1, shifted, 0);
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ RegisterInfo op1_reg;
+ RegisterInfo op2_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rn, op1_reg);
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, op2_reg);
+ context.SetRegisterRegisterOperands(op1_reg, op2_reg);
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// Compare Negative (immediate) adds a register value and an immediate value.
+// It updates the condition flags based on the result, and discards the result.
+bool EmulateInstructionARM::EmulateCMNImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], imm32, '0');
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rn; // the first operand
+ uint32_t imm32; // the immediate value to be compared with
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (Rn == 15)
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(reg_val, imm32, 0);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteFlags(context, res.result, res.carry_out, res.overflow);
+}
+
+// Compare Negative (register) adds a register value and an optionally-shifted
+// register value. It updates the condition flags based on the result, and
+// discards the result.
+bool EmulateInstructionARM::EmulateCMNReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], shifted, '0');
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rn; // the first operand
+ uint32_t Rm; // the second operand
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if n == 15 || BadReg(m) then UNPREDICTABLE;
+ if (Rn == 15 || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from register Rn.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the register value from register Rm.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(val1, shifted, 0);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteFlags(context, res.result, res.carry_out, res.overflow);
+}
+
+// Compare (immediate) subtracts an immediate value from a register value. It
+// updates the condition flags based on the result, and discards the result.
+bool EmulateInstructionARM::EmulateCMPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1');
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rn; // the first operand
+ uint32_t imm32; // the immediate value to be compared with
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 10, 8);
+ imm32 = Bits32(opcode, 7, 0);
+ break;
+ case eEncodingT2:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (Rn == 15)
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteFlags(context, res.result, res.carry_out, res.overflow);
+}
+
+// Compare (register) subtracts an optionally-shifted register value from a
+// register value. It updates the condition flags based on the result, and
+// discards the result.
+bool EmulateInstructionARM::EmulateCMPReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1');
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rn; // the first operand
+ uint32_t Rm; // the second operand
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 6, 3);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ if (Rn < 8 && Rm < 8)
+ return false;
+ if (Rn == 15 || Rm == 15)
+ return false;
+ break;
+ case eEncodingT3:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (Rn == 15 || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from register Rn.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the register value from register Rm.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(val1, ~shifted, 1);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteFlags(context, res.result, res.carry_out, res.overflow);
+}
+
+// Arithmetic Shift Right (immediate) shifts a register value right by an
+// immediate number of bits, shifting in copies of its sign bit, and writes the
+// result to the destination register. It can optionally update the condition
+// flags based on the result.
+bool EmulateInstructionARM::EmulateASRImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftImm(opcode, encoding, SRType_ASR);
+}
+
+// Arithmetic Shift Right (register) shifts a register value right by a
+// variable number of bits, shifting in copies of its sign bit, and writes the
+// result to the destination register. The variable number of bits is read from
+// the bottom byte of a register. It can optionally update the condition flags
+// based on the result.
+bool EmulateInstructionARM::EmulateASRReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shift_n = UInt(R[m]<7:0>);
+ (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C);
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftReg(opcode, encoding, SRType_ASR);
+}
+
+// Logical Shift Left (immediate) shifts a register value left by an immediate
+// number of bits, shifting in zeros, and writes the result to the destination
+// register. It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateLSLImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftImm(opcode, encoding, SRType_LSL);
+}
+
+// Logical Shift Left (register) shifts a register value left by a variable
+// number of bits, shifting in zeros, and writes the result to the destination
+// register. The variable number of bits is read from the bottom byte of a
+// register. It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateLSLReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shift_n = UInt(R[m]<7:0>);
+ (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C);
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftReg(opcode, encoding, SRType_LSL);
+}
+
+// Logical Shift Right (immediate) shifts a register value right by an
+// immediate number of bits, shifting in zeros, and writes the result to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateLSRImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftImm(opcode, encoding, SRType_LSR);
+}
+
+// Logical Shift Right (register) shifts a register value right by a variable
+// number of bits, shifting in zeros, and writes the result to the destination
+// register. The variable number of bits is read from the bottom byte of a
+// register. It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateLSRReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shift_n = UInt(R[m]<7:0>);
+ (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C);
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftReg(opcode, encoding, SRType_LSR);
+}
+
+// Rotate Right (immediate) provides the value of the contents of a register
+// rotated by a constant value. The bits that are rotated off the right end are
+// inserted into the vacated bit positions on the left. It can optionally
+// update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateRORImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftImm(opcode, encoding, SRType_ROR);
+}
+
+// Rotate Right (register) provides the value of the contents of a register
+// rotated by a variable number of bits. The bits that are rotated off the
+// right end are inserted into the vacated bit positions on the left. The
+// variable number of bits is read from the bottom byte of a register. It can
+// optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateRORReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shift_n = UInt(R[m]<7:0>);
+ (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C);
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftReg(opcode, encoding, SRType_ROR);
+}
+
+// Rotate Right with Extend provides the value of the contents of a register
+// shifted right by one place, with the carry flag shifted into bit [31].
+//
+// RRX can optionally update the condition flags based on the result.
+// In that case, bit [0] is shifted into the carry flag.
+bool EmulateInstructionARM::EmulateRRX(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry) = Shift_C(R[m], SRType_RRX, 1, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftImm(opcode, encoding, SRType_RRX);
+}
+
+bool EmulateInstructionARM::EmulateShiftImm(const uint32_t opcode,
+ const ARMEncoding encoding,
+ ARM_ShifterType shift_type) {
+ // assert(shift_type == SRType_ASR
+ // || shift_type == SRType_LSL
+ // || shift_type == SRType_LSR
+ // || shift_type == SRType_ROR
+ // || shift_type == SRType_RRX);
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd; // the destination register
+ uint32_t Rm; // the first operand register
+ uint32_t imm5; // encoding for the shift amount
+ uint32_t carry; // the carry bit after the shift operation
+ bool setflags;
+
+ // Special case handling!
+ // A8.6.139 ROR (immediate) -- Encoding T1
+ ARMEncoding use_encoding = encoding;
+ if (shift_type == SRType_ROR && use_encoding == eEncodingT1) {
+ // Morph the T1 encoding from the ARM Architecture Manual into T2
+ // encoding to have the same decoding of bit fields as the other Thumb2
+ // shift operations.
+ use_encoding = eEncodingT2;
+ }
+
+ switch (use_encoding) {
+ case eEncodingT1:
+ // Due to the above special case handling!
+ if (shift_type == SRType_ROR)
+ return false;
+
+ Rd = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ imm5 = Bits32(opcode, 10, 6);
+ break;
+ case eEncodingT2:
+ // A8.6.141 RRX
+ // There's no imm form of RRX instructions.
+ if (shift_type == SRType_RRX)
+ return false;
+
+ Rd = Bits32(opcode, 11, 8);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ imm5 = Bits32(opcode, 14, 12) << 2 | Bits32(opcode, 7, 6);
+ if (BadReg(Rd) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ imm5 = Bits32(opcode, 11, 7);
+ break;
+ default:
+ return false;
+ }
+
+ // A8.6.139 ROR (immediate)
+ if (shift_type == SRType_ROR && imm5 == 0)
+ shift_type = SRType_RRX;
+
+ // Get the first operand.
+ uint32_t value = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ // Decode the shift amount if not RRX.
+ uint32_t amt =
+ (shift_type == SRType_RRX ? 1 : DecodeImmShift(shift_type, imm5));
+
+ uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success);
+ if (!success)
+ return false;
+
+ // The context specifies that an immediate is to be moved into Rd.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM::EmulateShiftReg(const uint32_t opcode,
+ const ARMEncoding encoding,
+ ARM_ShifterType shift_type) {
+ // assert(shift_type == SRType_ASR
+ // || shift_type == SRType_LSL
+ // || shift_type == SRType_LSR
+ // || shift_type == SRType_ROR);
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand register
+ uint32_t
+ Rm; // the register whose bottom byte contains the amount to shift by
+ uint32_t carry; // the carry bit after the shift operation
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 2, 0);
+ Rn = Rd;
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 3, 0);
+ Rm = Bits32(opcode, 11, 8);
+ setflags = BitIsSet(opcode, 20);
+ if (Rd == 15 || Rn == 15 || Rm == 15)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ // Get the first operand.
+ uint32_t value = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+ // Get the Rm register content.
+ uint32_t val = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ // Get the shift amount.
+ uint32_t amt = Bits32(val, 7, 0);
+
+ uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success);
+ if (!success)
+ return false;
+
+ // The context specifies that an immediate is to be moved into Rd.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// LDM loads multiple registers from consecutive memory locations, using an
+// address from a base register. Optionally the address just above the highest
+// of those locations can be written back to the base register.
+bool EmulateInstructionARM::EmulateLDM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed()
+ EncodingSpecificOperations(); NullCheckIfThumbEE (n);
+ address = R[n];
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ R[i] = MemA[address, 4]; address = address + 4;
+ if registers<15> == '1' then
+ LoadWritePC (MemA[address, 4]);
+
+ if wback && registers<n> == '0' then R[n] = R[n] + 4 * BitCount (registers);
+ if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1
+
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ switch (encoding) {
+ case eEncodingT1:
+ // n = UInt(Rn); registers = '00000000':register_list; wback =
+ // (registers<n> == '0');
+ n = Bits32(opcode, 10, 8);
+ registers = Bits32(opcode, 7, 0);
+ registers = registers & 0x00ff; // Make sure the top 8 bits are zeros.
+ wback = BitIsClear(registers, n);
+ // if BitCount(registers) < 1 then UNPREDICTABLE;
+ if (BitCount(registers) < 1)
+ return false;
+ break;
+ case eEncodingT2:
+ // if W == '1' && Rn == '1101' then SEE POP;
+ // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ registers = registers & 0xdfff; // Make sure bit 13 is zero.
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then
+ // UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 2) ||
+ (BitIsSet(opcode, 14) && BitIsSet(opcode, 15)))
+ return false;
+
+ // if registers<15> == '1' && InITBlock() && !LastInITBlock() then
+ // UNPREDICTABLE;
+ if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock())
+ return false;
+
+ // if wback && registers<n> == '1' then UNPREDICTABLE;
+ if (wback && BitIsSet(registers, n))
+ return false;
+ break;
+
+ case eEncodingA1:
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ int32_t offset = 0;
+ const addr_t base_address =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+
+ for (int i = 0; i < 14; ++i) {
+ if (BitIsSet(registers, i)) {
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+ if (wback && (n == 13)) // Pop Instruction
+ {
+ context.type = EmulateInstruction::eContextPopRegisterOffStack;
+ context.SetAddress(base_address + offset);
+ }
+
+ // R[i] = MemA [address, 4]; address = address + 4;
+ uint32_t data = MemARead(context, base_address + offset, addr_byte_size,
+ 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i,
+ data))
+ return false;
+
+ offset += addr_byte_size;
+ }
+ }
+
+ if (BitIsSet(registers, 15)) {
+ // LoadWritePC (MemA [address, 4]);
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+ uint32_t data =
+ MemARead(context, base_address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ }
+
+ if (wback && BitIsClear(registers, n)) {
+ // R[n] = R[n] + 4 * BitCount (registers)
+ int32_t offset = addr_byte_size * BitCount(registers);
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ base_address + offset))
+ return false;
+ }
+ if (wback && BitIsSet(registers, n))
+ // R[n] bits(32) UNKNOWN;
+ return WriteBits32Unknown(n);
+ }
+ return true;
+}
+
+// LDMDA loads multiple registers from consecutive memory locations using an
+// address from a base register.
+// The consecutive memory locations end at this address and the address just
+// below the lowest of those locations can optionally be written back to the
+// base register.
+bool EmulateInstructionARM::EmulateLDMDA(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ address = R[n] - 4*BitCount(registers) + 4;
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ R[i] = MemA[address,4]; address = address + 4;
+
+ if registers<15> == '1' then
+ LoadWritePC(MemA[address,4]);
+
+ if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers);
+ if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+ // address = R[n] - 4*BitCount(registers) + 4;
+
+ int32_t offset = 0;
+ addr_t Rn = ReadCoreReg(n, &success);
+
+ if (!success)
+ return false;
+
+ addr_t address =
+ Rn - (addr_byte_size * BitCount(registers)) + addr_byte_size;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+
+ // for i = 0 to 14
+ for (int i = 0; i < 14; ++i) {
+ // if registers<i> == '1' then
+ if (BitIsSet(registers, i)) {
+ // R[i] = MemA[address,4]; address = address + 4;
+ context.SetRegisterPlusOffset(dwarf_reg, Rn - (address + offset));
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i,
+ data))
+ return false;
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then
+ // LoadWritePC(MemA[address,4]);
+ if (BitIsSet(registers, 15)) {
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ }
+
+ // if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers);
+ if (wback && BitIsClear(registers, n)) {
+ if (!success)
+ return false;
+
+ offset = (addr_byte_size * BitCount(registers)) * -1;
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t addr = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ addr))
+ return false;
+ }
+
+ // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN;
+ if (wback && BitIsSet(registers, n))
+ return WriteBits32Unknown(n);
+ }
+ return true;
+}
+
+// LDMDB loads multiple registers from consecutive memory locations using an
+// address from a base register. The
+// consecutive memory locations end just below this address, and the address of
+// the lowest of those locations can be optionally written back to the base
+// register.
+bool EmulateInstructionARM::EmulateLDMDB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ address = R[n] - 4*BitCount(registers);
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ R[i] = MemA[address,4]; address = address + 4;
+ if registers<15> == '1' then
+ LoadWritePC(MemA[address,4]);
+
+ if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers);
+ if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ switch (encoding) {
+ case eEncodingT1:
+ // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ registers = registers & 0xdfff; // Make sure bit 13 is a zero.
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then
+ // UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 2) ||
+ (BitIsSet(opcode, 14) && BitIsSet(opcode, 15)))
+ return false;
+
+ // if registers<15> == '1' && InITBlock() && !LastInITBlock() then
+ // UNPREDICTABLE;
+ if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock())
+ return false;
+
+ // if wback && registers<n> == '1' then UNPREDICTABLE;
+ if (wback && BitIsSet(registers, n))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // address = R[n] - 4*BitCount(registers);
+
+ int32_t offset = 0;
+ addr_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+
+ if (!success)
+ return false;
+
+ addr_t address = Rn - (addr_byte_size * BitCount(registers));
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, Rn - address);
+
+ for (int i = 0; i < 14; ++i) {
+ if (BitIsSet(registers, i)) {
+ // R[i] = MemA[address,4]; address = address + 4;
+ context.SetRegisterPlusOffset(dwarf_reg, Rn - (address + offset));
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i,
+ data))
+ return false;
+
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then
+ // LoadWritePC(MemA[address,4]);
+ if (BitIsSet(registers, 15)) {
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ }
+
+ // if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers);
+ if (wback && BitIsClear(registers, n)) {
+ if (!success)
+ return false;
+
+ offset = (addr_byte_size * BitCount(registers)) * -1;
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t addr = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ addr))
+ return false;
+ }
+
+ // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only
+ // possible for encoding A1
+ if (wback && BitIsSet(registers, n))
+ return WriteBits32Unknown(n);
+ }
+ return true;
+}
+
+// LDMIB loads multiple registers from consecutive memory locations using an
+// address from a base register. The
+// consecutive memory locations start just above this address, and thea ddress
+// of the last of those locations can optinoally be written back to the base
+// register.
+bool EmulateInstructionARM::EmulateLDMIB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ address = R[n] + 4;
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ R[i] = MemA[address,4]; address = address + 4;
+ if registers<15> == '1' then
+ LoadWritePC(MemA[address,4]);
+
+ if wback && registers<n> == '0' then R[n] = R[n] + 4*BitCount(registers);
+ if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ switch (encoding) {
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+
+ break;
+ default:
+ return false;
+ }
+ // address = R[n] + 4;
+
+ int32_t offset = 0;
+ addr_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+
+ if (!success)
+ return false;
+
+ addr_t address = Rn + addr_byte_size;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+
+ for (int i = 0; i < 14; ++i) {
+ if (BitIsSet(registers, i)) {
+ // R[i] = MemA[address,4]; address = address + 4;
+
+ context.SetRegisterPlusOffset(dwarf_reg, offset + addr_byte_size);
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i,
+ data))
+ return false;
+
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then
+ // LoadWritePC(MemA[address,4]);
+ if (BitIsSet(registers, 15)) {
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ }
+
+ // if wback && registers<n> == '0' then R[n] = R[n] + 4*BitCount(registers);
+ if (wback && BitIsClear(registers, n)) {
+ if (!success)
+ return false;
+
+ offset = addr_byte_size * BitCount(registers);
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t addr = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ addr))
+ return false;
+ }
+
+ // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only
+ // possible for encoding A1
+ if (wback && BitIsSet(registers, n))
+ return WriteBits32Unknown(n);
+ }
+ return true;
+}
+
+// Load Register (immediate) calculates an address from a base register value
+// and an immediate offset, loads a word from memory, and writes to a register.
+// LDR (immediate, Thumb)
+bool EmulateInstructionARM::EmulateLDRRtRnImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,4];
+ if wback then R[n] = offset_addr;
+ if t == 15 then
+ if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ elsif UnalignedSupport() || address<1:0> = '00' then
+ R[t] = data;
+ else R[t] = bits(32) UNKNOWN; // Can only apply before ARMv7
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rt; // the destination register
+ uint32_t Rn; // the base register
+ uint32_t imm32; // the immediate offset used to form the address
+ addr_t offset_addr; // the offset address
+ addr_t address; // the calculated address
+ uint32_t data; // the literal data value from memory load
+ bool add, index, wback;
+ switch (encoding) {
+ case eEncodingT1:
+ Rt = Bits32(opcode, 2, 0);
+ Rn = Bits32(opcode, 5, 3);
+ imm32 = Bits32(opcode, 10, 6) << 2; // imm32 = ZeroExtend(imm5:'00', 32);
+ // index = TRUE; add = TRUE; wback = FALSE
+ add = true;
+ index = true;
+ wback = false;
+
+ break;
+
+ case eEncodingT2:
+ // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32);
+ Rt = Bits32(opcode, 10, 8);
+ Rn = 13;
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ break;
+
+ case eEncodingT3:
+ // if Rn == '1111' then SEE LDR (literal);
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ Rt = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE;
+ if ((Rt == 15) && InITBlock() && !LastInITBlock())
+ return false;
+
+ break;
+
+ case eEncodingT4:
+ // if Rn == '1111' then SEE LDR (literal);
+ // if P == '1' && U == '1' && W == '0' then SEE LDRT;
+ // if Rn == '1101' && P == '0' && U == '1' && W == '1' && imm8 ==
+ // '00000100' then SEE POP;
+ // if P == '0' && W == '0' then UNDEFINED;
+ if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ Rt = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if (wback && n == t) || (t == 15 && InITBlock() && !LastInITBlock())
+ // then UNPREDICTABLE;
+ if ((wback && (Rn == Rt)) ||
+ ((Rt == 15) && InITBlock() && !LastInITBlock()))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+ uint32_t base = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+ if (add)
+ offset_addr = base + imm32;
+ else
+ offset_addr = base - imm32;
+
+ address = (index ? offset_addr : base);
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rn, base_reg);
+ if (wback) {
+ EmulateInstruction::Context ctx;
+ if (Rn == 13) {
+ ctx.type = eContextAdjustStackPointer;
+ ctx.SetImmediateSigned((int32_t)(offset_addr - base));
+ } else if (Rn == GetFramePointerRegisterNumber()) {
+ ctx.type = eContextSetFramePointer;
+ ctx.SetRegisterPlusOffset(base_reg, (int32_t)(offset_addr - base));
+ } else {
+ ctx.type = EmulateInstruction::eContextAdjustBaseRegister;
+ ctx.SetRegisterPlusOffset(base_reg, (int32_t)(offset_addr - base));
+ }
+
+ if (!WriteRegisterUnsigned(ctx, eRegisterKindDWARF, dwarf_r0 + Rn,
+ offset_addr))
+ return false;
+ }
+
+ // Prepare to write to the Rt register.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, (int32_t)(offset_addr - base));
+
+ // Read memory from the address.
+ data = MemURead(context, address, 4, 0, &success);
+ if (!success)
+ return false;
+
+ if (Rt == 15) {
+ if (Bits32(address, 1, 0) == 0) {
+ if (!LoadWritePC(context, data))
+ return false;
+ } else
+ return false;
+ } else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rt,
+ data))
+ return false;
+ } else
+ WriteBits32Unknown(Rt);
+ }
+ return true;
+}
+
+// STM (Store Multiple Increment After) stores multiple registers to consecutive
+// memory locations using an address
+// from a base register. The consecutive memory locations start at this
+// address, and the address just above the last of those locations can
+// optionally be written back to the base register.
+bool EmulateInstructionARM::EmulateSTM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ address = R[n];
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ if i == n && wback && i != LowestSetBit(registers) then
+ MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings T1 and A1
+ else
+ MemA[address,4] = R[i];
+ address = address + 4;
+
+ if registers<15> == '1' then // Only possible for encoding A1
+ MemA[address,4] = PCStoreValue();
+ if wback then R[n] = R[n] + 4*BitCount(registers);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // n = UInt(Rn); registers = '00000000':register_list; wback = TRUE;
+ n = Bits32(opcode, 10, 8);
+ registers = Bits32(opcode, 7, 0);
+ registers = registers & 0x00ff; // Make sure the top 8 bits are zeros.
+ wback = true;
+
+ // if BitCount(registers) < 1 then UNPREDICTABLE;
+ if (BitCount(registers) < 1)
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros.
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 2))
+ return false;
+
+ // if wback && registers<n> == '1' then UNPREDICTABLE;
+ if (wback && BitIsSet(registers, n))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // address = R[n];
+ int32_t offset = 0;
+ const addr_t address =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ // for i = 0 to 14
+ uint32_t lowest_set_bit = 14;
+ for (uint32_t i = 0; i < 14; ++i) {
+ // if registers<i> == '1' then
+ if (BitIsSet(registers, i)) {
+ if (i < lowest_set_bit)
+ lowest_set_bit = i;
+ // if i == n && wback && i != LowestSetBit(registers) then
+ if ((i == n) && wback && (i != lowest_set_bit))
+ // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings
+ // T1 and A1
+ WriteBits32UnknownToMemory(address + offset);
+ else {
+ // MemA[address,4] = R[i];
+ uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i,
+ 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, offset);
+ if (!MemAWrite(context, address + offset, data, addr_byte_size))
+ return false;
+ }
+
+ // address = address + 4;
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then // Only possible for encoding A1
+ // MemA[address,4] = PCStoreValue();
+ if (BitIsSet(registers, 15)) {
+ RegisterInfo pc_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, pc_reg);
+ context.SetRegisterPlusOffset(pc_reg, 8);
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address + offset, pc, addr_byte_size))
+ return false;
+ }
+
+ // if wback then R[n] = R[n] + 4*BitCount(registers);
+ if (wback) {
+ offset = addr_byte_size * BitCount(registers);
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t data = address + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// STMDA (Store Multiple Decrement After) stores multiple registers to
+// consecutive memory locations using an address from a base register. The
+// consecutive memory locations end at this address, and the address just below
+// the lowest of those locations can optionally be written back to the base
+// register.
+bool EmulateInstructionARM::EmulateSTMDA(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ address = R[n] - 4*BitCount(registers) + 4;
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ if i == n && wback && i != LowestSetBit(registers) then
+ MemA[address,4] = bits(32) UNKNOWN;
+ else
+ MemA[address,4] = R[i];
+ address = address + 4;
+
+ if registers<15> == '1' then
+ MemA[address,4] = PCStoreValue();
+
+ if wback then R[n] = R[n] - 4*BitCount(registers);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ // address = R[n] - 4*BitCount(registers) + 4;
+ int32_t offset = 0;
+ addr_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t address = Rn - (addr_byte_size * BitCount(registers)) + 4;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ // for i = 0 to 14
+ uint32_t lowest_bit_set = 14;
+ for (uint32_t i = 0; i < 14; ++i) {
+ // if registers<i> == '1' then
+ if (BitIsSet(registers, i)) {
+ if (i < lowest_bit_set)
+ lowest_bit_set = i;
+ // if i == n && wback && i != LowestSetBit(registers) then
+ if ((i == n) && wback && (i != lowest_bit_set))
+ // MemA[address,4] = bits(32) UNKNOWN;
+ WriteBits32UnknownToMemory(address + offset);
+ else {
+ // MemA[address,4] = R[i];
+ uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i,
+ 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ Rn - (address + offset));
+ if (!MemAWrite(context, address + offset, data, addr_byte_size))
+ return false;
+ }
+
+ // address = address + 4;
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then
+ // MemA[address,4] = PCStoreValue();
+ if (BitIsSet(registers, 15)) {
+ RegisterInfo pc_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, pc_reg);
+ context.SetRegisterPlusOffset(pc_reg, 8);
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address + offset, pc, addr_byte_size))
+ return false;
+ }
+
+ // if wback then R[n] = R[n] - 4*BitCount(registers);
+ if (wback) {
+ offset = (addr_byte_size * BitCount(registers)) * -1;
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t data = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// STMDB (Store Multiple Decrement Before) stores multiple registers to
+// consecutive memory locations using an address from a base register. The
+// consecutive memory locations end just below this address, and the address of
+// the first of those locations can optionally be written back to the base
+// register.
+bool EmulateInstructionARM::EmulateSTMDB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ address = R[n] - 4*BitCount(registers);
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ if i == n && wback && i != LowestSetBit(registers) then
+ MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding A1
+ else
+ MemA[address,4] = R[i];
+ address = address + 4;
+
+ if registers<15> == '1' then // Only possible for encoding A1
+ MemA[address,4] = PCStoreValue();
+
+ if wback then R[n] = R[n] - 4*BitCount(registers);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if W == '1' && Rn == '1101' then SEE PUSH;
+ if ((BitIsSet(opcode, 21)) && (Bits32(opcode, 19, 16) == 13)) {
+ // See PUSH
+ }
+ // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros.
+ wback = BitIsSet(opcode, 21);
+ // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE;
+ if ((n == 15) || BitCount(registers) < 2)
+ return false;
+ // if wback && registers<n> == '1' then UNPREDICTABLE;
+ if (wback && BitIsSet(registers, n))
+ return false;
+ break;
+
+ case eEncodingA1:
+ // if W == '1' && Rn == '1101' && BitCount(register_list) >= 2 then SEE
+ // PUSH;
+ if (BitIsSet(opcode, 21) && (Bits32(opcode, 19, 16) == 13) &&
+ BitCount(Bits32(opcode, 15, 0)) >= 2) {
+ // See Push
+ }
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || BitCount(registers) < 1)
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ // address = R[n] - 4*BitCount(registers);
+
+ int32_t offset = 0;
+ addr_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t address = Rn - (addr_byte_size * BitCount(registers));
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ // for i = 0 to 14
+ uint32_t lowest_set_bit = 14;
+ for (uint32_t i = 0; i < 14; ++i) {
+ // if registers<i> == '1' then
+ if (BitIsSet(registers, i)) {
+ if (i < lowest_set_bit)
+ lowest_set_bit = i;
+ // if i == n && wback && i != LowestSetBit(registers) then
+ if ((i == n) && wback && (i != lowest_set_bit))
+ // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding
+ // A1
+ WriteBits32UnknownToMemory(address + offset);
+ else {
+ // MemA[address,4] = R[i];
+ uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i,
+ 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ Rn - (address + offset));
+ if (!MemAWrite(context, address + offset, data, addr_byte_size))
+ return false;
+ }
+
+ // address = address + 4;
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then // Only possible for encoding A1
+ // MemA[address,4] = PCStoreValue();
+ if (BitIsSet(registers, 15)) {
+ RegisterInfo pc_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, pc_reg);
+ context.SetRegisterPlusOffset(pc_reg, 8);
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address + offset, pc, addr_byte_size))
+ return false;
+ }
+
+ // if wback then R[n] = R[n] - 4*BitCount(registers);
+ if (wback) {
+ offset = (addr_byte_size * BitCount(registers)) * -1;
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t data = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// STMIB (Store Multiple Increment Before) stores multiple registers to
+// consecutive memory locations using an address from a base register. The
+// consecutive memory locations start just above this address, and the address
+// of the last of those locations can optionally be written back to the base
+// register.
+bool EmulateInstructionARM::EmulateSTMIB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ address = R[n] + 4;
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ if i == n && wback && i != LowestSetBit(registers) then
+ MemA[address,4] = bits(32) UNKNOWN;
+ else
+ MemA[address,4] = R[i];
+ address = address + 4;
+
+ if registers<15> == '1' then
+ MemA[address,4] = PCStoreValue();
+
+ if wback then R[n] = R[n] + 4*BitCount(registers);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) && (BitCount(registers) < 1))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ // address = R[n] + 4;
+
+ int32_t offset = 0;
+ addr_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t address = Rn + addr_byte_size;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t lowest_set_bit = 14;
+ // for i = 0 to 14
+ for (uint32_t i = 0; i < 14; ++i) {
+ // if registers<i> == '1' then
+ if (BitIsSet(registers, i)) {
+ if (i < lowest_set_bit)
+ lowest_set_bit = i;
+ // if i == n && wback && i != LowestSetBit(registers) then
+ if ((i == n) && wback && (i != lowest_set_bit))
+ // MemA[address,4] = bits(32) UNKNOWN;
+ WriteBits32UnknownToMemory(address + offset);
+ // else
+ else {
+ // MemA[address,4] = R[i];
+ uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i,
+ 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ offset + addr_byte_size);
+ if (!MemAWrite(context, address + offset, data, addr_byte_size))
+ return false;
+ }
+
+ // address = address + 4;
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then
+ // MemA[address,4] = PCStoreValue();
+ if (BitIsSet(registers, 15)) {
+ RegisterInfo pc_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, pc_reg);
+ context.SetRegisterPlusOffset(pc_reg, 8);
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address + offset, pc, addr_byte_size))
+ return false;
+ }
+
+ // if wback then R[n] = R[n] + 4*BitCount(registers);
+ if (wback) {
+ offset = addr_byte_size * BitCount(registers);
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t data = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// STR (store immediate) calculates an address from a base register value and an
+// immediate offset, and stores a word
+// from a register to memory. It can use offset, post-indexed, or pre-indexed
+// addressing.
+bool EmulateInstructionARM::EmulateSTRThumb(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ if UnalignedSupport() || address<1:0> == '00' then
+ MemU[address,4] = R[t];
+ else // Can only occur before ARMv7
+ MemU[address,4] = bits(32) UNKNOWN;
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+ // EncodingSpecificOperations (); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'00', 32);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ imm32 = Bits32(opcode, 10, 6) << 2;
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = false;
+ wback = false;
+ break;
+
+ case eEncodingT2:
+ // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32);
+ t = Bits32(opcode, 10, 8);
+ n = 13;
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+ break;
+
+ case eEncodingT3:
+ // if Rn == '1111' then UNDEFINED;
+ if (Bits32(opcode, 19, 16) == 15)
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+ break;
+
+ case eEncodingT4:
+ // if P == '1' && U == '1' && W == '0' then SEE STRT;
+ // if Rn == '1101' && P == '1' && U == '0' && W == '1' && imm8 ==
+ // '00000100' then SEE PUSH;
+ // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED;
+ if ((Bits32(opcode, 19, 16) == 15) ||
+ (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if t == 15 || (wback && n == t) then UNPREDICTABLE;
+ if ((t == 15) || (wback && (n == t)))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint32_t base_address = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ if (add)
+ offset_addr = base_address + imm32;
+ else
+ offset_addr = base_address - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = base_address;
+
+ EmulateInstruction::Context context;
+ if (n == 13)
+ context.type = eContextPushRegisterOnStack;
+ else
+ context.type = eContextRegisterStore;
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ // if UnalignedSupport() || address<1:0> == '00' then
+ if (UnalignedSupport() ||
+ (BitIsClear(address, 1) && BitIsClear(address, 0))) {
+ // MemU[address,4] = R[t];
+ uint32_t data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ int32_t offset = address - base_address;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, offset);
+ if (!MemUWrite(context, address, data, addr_byte_size))
+ return false;
+ } else {
+ // MemU[address,4] = bits(32) UNKNOWN;
+ WriteBits32UnknownToMemory(address);
+ }
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ if (n == 13)
+ context.type = eContextAdjustStackPointer;
+ else
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// STR (Store Register) calculates an address from a base register value and an
+// offset register value, stores a
+// word from a register to memory. The offset register value can optionally
+// be shifted.
+bool EmulateInstructionARM::EmulateSTRRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ if t == 15 then // Only possible for encoding A1
+ data = PCStoreValue();
+ else
+ data = R[t];
+ if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == InstrSet_ARM then
+ MemU[address,4] = data;
+ else // Can only occur before ARMv7
+ MemU[address,4] = bits(32) UNKNOWN;
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+ bool index;
+ bool add;
+ bool wback;
+
+ // EncodingSpecificOperations (); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation
+ // in ThumbEE";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then UNDEFINED;
+ if (Bits32(opcode, 19, 16) == 15)
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if t == 15 || BadReg(m) then UNPREDICTABLE;
+ if ((t == 15) || (BadReg(m)))
+ return false;
+ break;
+
+ case eEncodingA1: {
+ // if P == '0' && W == '1' then SEE STRT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ uint32_t typ = Bits32(opcode, 6, 5);
+ uint32_t imm5 = Bits32(opcode, 11, 7);
+ shift_n = DecodeImmShift(typ, imm5, shift_t);
+
+ // if m == 15 then UNPREDICTABLE;
+ if (m == 15)
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+ }
+ default:
+ return false;
+ }
+
+ addr_t offset_addr;
+ addr_t address;
+ int32_t offset = 0;
+
+ addr_t base_address =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ uint32_t Rm_data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset = Shift(Rm_data, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ if (add)
+ offset_addr = base_address + offset;
+ else
+ offset_addr = base_address - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = base_address;
+
+ uint32_t data;
+ // if t == 15 then // Only possible for encoding A1
+ if (t == 15)
+ // data = PCStoreValue();
+ data = ReadCoreReg(PC_REG, &success);
+ else
+ // data = R[t];
+ data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success);
+
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+
+ // if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() ==
+ // InstrSet_ARM then
+ if (UnalignedSupport() ||
+ (BitIsClear(address, 1) && BitIsClear(address, 0)) ||
+ CurrentInstrSet() == eModeARM) {
+ // MemU[address,4] = data;
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - base_address);
+ if (!MemUWrite(context, address, data, addr_byte_size))
+ return false;
+
+ } else
+ // MemU[address,4] = bits(32) UNKNOWN;
+ WriteBits32UnknownToMemory(address);
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextRegisterLoad;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool EmulateInstructionARM::EmulateSTRBThumb(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ MemU[address,1] = R[t]<7:0>;
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ imm32 = Bits32(opcode, 10, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then UNDEFINED;
+ if (Bits32(opcode, 19, 16) == 15)
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if BadReg(t) then UNPREDICTABLE;
+ if (BadReg(t))
+ return false;
+ break;
+
+ case eEncodingT3:
+ // if P == '1' && U == '1' && W == '0' then SEE STRBT;
+ // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED;
+ if (Bits32(opcode, 19, 16) == 15)
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if BadReg(t) || (wback && n == t) then UNPREDICTABLE
+ if ((BadReg(t)) || (wback && (n == t)))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ addr_t offset_addr;
+ addr_t address;
+ addr_t base_address =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ if (add)
+ offset_addr = base_address + imm32;
+ else
+ offset_addr = base_address - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = base_address;
+
+ // MemU[address,1] = R[t]<7:0>
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - base_address);
+
+ uint32_t data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success);
+ if (!success)
+ return false;
+
+ data = Bits32(data, 7, 0);
+
+ if (!MemUWrite(context, address, data, 1))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextRegisterLoad;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// STRH (register) calculates an address from a base register value and an
+// offset register value, and stores a
+// halfword from a register to memory. The offset register value can be
+// shifted left by 0, 1, 2, or 3 bits.
+bool EmulateInstructionARM::EmulateSTRHRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ if UnalignedSupport() || address<0> == '0' then
+ MemU[address,2] = R[t]<15:0>;
+ else // Can only occur before ARMv7
+ MemU[address,2] = bits(16) UNKNOWN;
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation
+ // in ThumbEE";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then UNDEFINED;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ if (n == 15)
+ return false;
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if BadReg(t) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(t) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE STRHT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ // if t == 15 || m == 15 then UNPREDICTABLE;
+ if ((t == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ uint32_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+
+ // if UnalignedSupport() || address<0> == '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // MemU[address,2] = R[t]<15:0>;
+ uint32_t Rt = ReadCoreReg(t, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ context.SetRegisterToRegisterPlusIndirectOffset(base_reg, offset_reg,
+ data_reg);
+
+ if (!MemUWrite(context, address, Bits32(Rt, 15, 0), 2))
+ return false;
+ } else // Can only occur before ARMv7
+ {
+ // MemU[address,2] = bits(16) UNKNOWN;
+ }
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Add with Carry (immediate) adds an immediate value and the carry flag value
+// to a register value, and writes the result to the destination register. It
+// can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateADCImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], imm32, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t
+ imm32; // the immediate value to be added to the value obtained from Rn
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (BadReg(Rd) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ int32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(val1, imm32, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// Add with Carry (register) adds a register value, the carry flag value, and
+// an optionally-shifted register value, and writes the result to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateADCReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], shifted, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ int32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ int32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(val1, shifted, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// This instruction adds an immediate value to the PC value to form a PC-
+// relative address, and writes the result to the destination register.
+bool EmulateInstructionARM::EmulateADR(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = if add then (Align(PC,4) + imm32) else (Align(PC,4) - imm32);
+ if d == 15 then // Can only occur for ARM encodings
+ ALUWritePC(result);
+ else
+ R[d] = result;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd;
+ uint32_t imm32; // the immediate value to be added/subtracted to/from the PC
+ bool add;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 10, 8);
+ imm32 = ThumbImm8Scaled(opcode); // imm32 = ZeroExtend(imm8:'00', 32)
+ add = true;
+ break;
+ case eEncodingT2:
+ case eEncodingT3:
+ Rd = Bits32(opcode, 11, 8);
+ imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32)
+ add = (Bits32(opcode, 24, 21) == 0); // 0b0000 => ADD; 0b0101 => SUB
+ if (BadReg(Rd))
+ return false;
+ break;
+ case eEncodingA1:
+ case eEncodingA2:
+ Rd = Bits32(opcode, 15, 12);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ add = (Bits32(opcode, 24, 21) == 0x4); // 0b0100 => ADD; 0b0010 => SUB
+ break;
+ default:
+ return false;
+ }
+
+ // Read the PC value.
+ uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = (add ? Align(pc, 4) + imm32 : Align(pc, 4) - imm32);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreReg(context, result, Rd))
+ return false;
+ }
+ return true;
+}
+
+// This instruction performs a bitwise AND of a register value and an immediate
+// value, and writes the result to the destination register. It can optionally
+// update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateANDImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] AND imm32;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t
+ imm32; // the immediate value to be ANDed to the value obtained from Rn
+ bool setflags;
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ // if Rd == '1111' && S == '1' then SEE TST (immediate);
+ if (Rd == 15 && setflags)
+ return EmulateTSTImm(opcode, eEncodingT1);
+ if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 & imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// This instruction performs a bitwise AND of a register value and an
+// optionally-shifted register value, and writes the result to the destination
+// register. It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateANDReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] AND shifted;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if Rd == '1111' && S == '1' then SEE TST (register);
+ if (Rd == 15 && setflags)
+ return EmulateTSTReg(opcode, eEncodingT2);
+ if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 & shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise Bit Clear (immediate) performs a bitwise AND of a register value and
+// the complement of an immediate value, and writes the result to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateBICImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] AND NOT(imm32);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t imm32; // the immediate value to be bitwise inverted and ANDed to
+ // the value obtained from Rn
+ bool setflags;
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ if (BadReg(Rd) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 & ~imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise Bit Clear (register) performs a bitwise AND of a register value and
+// the complement of an optionally-shifted register value, and writes the
+// result to the destination register. It can optionally update the condition
+// flags based on the result.
+bool EmulateInstructionARM::EmulateBICReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] AND NOT(shifted);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 & ~shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// LDR (immediate, ARM) calculates an address from a base register value and an
+// immediate offset, loads a word
+// from memory, and writes it to a register. It can use offset, post-indexed,
+// or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateLDRImmediateARM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,4];
+ if wback then R[n] = offset_addr;
+ if t == 15 then
+ if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ elsif UnalignedSupport() || address<1:0> = '00' then
+ R[t] = data;
+ else // Can only apply before ARMv7
+ R[t] = ROR(data, 8*UInt(address<1:0>));
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingA1:
+ // if Rn == '1111' then SEE LDR (literal);
+ // if P == '0' && W == '1' then SEE LDRT;
+ // if Rn == '1101' && P == '0' && U == '1' && W == '0' && imm12 ==
+ // '000000000100' then SEE POP;
+ // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // if wback && n == t then UNPREDICTABLE;
+ if (wback && (n == t))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ addr_t address;
+ addr_t offset_addr;
+ addr_t base_address = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ if (add)
+ offset_addr = base_address + imm32;
+ else
+ offset_addr = base_address - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = base_address;
+
+ // data = MemU[address,4];
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base_address);
+
+ uint64_t data = MemURead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if t == 15 then
+ if (t == 15) {
+ // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ if (BitIsClear(address, 1) && BitIsClear(address, 0)) {
+ // LoadWritePC (data);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base_address);
+ LoadWritePC(context, data);
+ } else
+ return false;
+ }
+ // elsif UnalignedSupport() || address<1:0> = '00' then
+ else if (UnalignedSupport() ||
+ (BitIsClear(address, 1) && BitIsClear(address, 0))) {
+ // R[t] = data;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base_address);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ }
+ // else // Can only apply before ARMv7
+ else {
+ // R[t] = ROR(data, 8*UInt(address<1:0>));
+ data = ROR(data, Bits32(address, 1, 0), &success);
+ if (!success)
+ return false;
+ context.type = eContextRegisterLoad;
+ context.SetImmediate(data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// LDR (register) calculates an address from a base register value and an offset
+// register value, loads a word
+// from memory, and writes it to a register. The offset register value can
+// optionally be shifted.
+bool EmulateInstructionARM::EmulateLDRRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,4];
+ if wback then R[n] = offset_addr;
+ if t == 15 then
+ if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ elsif UnalignedSupport() || address<1:0> = '00' then
+ R[t] = data;
+ else // Can only apply before ARMv7
+ if CurrentInstrSet() == InstrSet_ARM then
+ R[t] = ROR(data, 8*UInt(address<1:0>));
+ else
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation
+ // in ThumbEE";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then SEE LDR (literal);
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if BadReg(m) then UNPREDICTABLE;
+ if (BadReg(m))
+ return false;
+
+ // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE;
+ if ((t == 15) && InITBlock() && !LastInITBlock())
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ // if P == '0' && W == '1' then SEE LDRT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ uint32_t type = Bits32(opcode, 6, 5);
+ uint32_t imm5 = Bits32(opcode, 11, 7);
+ shift_n = DecodeImmShift(type, imm5, shift_t);
+
+ // if m == 15 then UNPREDICTABLE;
+ if (m == 15)
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+
+ uint32_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ uint32_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C); -- Note "The APSR is
+ // an application level alias for the CPSR".
+ addr_t offset =
+ Shift(Rm, shift_t, shift_n, Bit32(m_opcode_cpsr, APSR_C), &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // data = MemU[address,4];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t data = MemURead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if t == 15 then
+ if (t == 15) {
+ // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ if (BitIsClear(address, 1) && BitIsClear(address, 0)) {
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ LoadWritePC(context, data);
+ } else
+ return false;
+ }
+ // elsif UnalignedSupport() || address<1:0> = '00' then
+ else if (UnalignedSupport() ||
+ (BitIsClear(address, 1) && BitIsClear(address, 0))) {
+ // R[t] = data;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // if CurrentInstrSet() == InstrSet_ARM then
+ if (CurrentInstrSet() == eModeARM) {
+ // R[t] = ROR(data, 8*UInt(address<1:0>));
+ data = ROR(data, Bits32(address, 1, 0), &success);
+ if (!success)
+ return false;
+ context.type = eContextRegisterLoad;
+ context.SetImmediate(data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ } else {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ }
+ return true;
+}
+
+// LDRB (immediate, Thumb)
+bool EmulateInstructionARM::EmulateLDRBImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ R[t] = ZeroExtend(MemU[address,1], 32);
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ imm32 = Bits32(opcode, 10, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ break;
+
+ case eEncodingT2:
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if Rt == '1111' then SEE PLD;
+ if (t == 15)
+ return false; // PLD is not implemented yet
+
+ // if Rn == '1111' then SEE LDRB (literal);
+ if (n == 15)
+ return EmulateLDRBLiteral(opcode, eEncodingT1);
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingT3:
+ // if P == '1' && U == '1' && W == '0' then SEE LDRBT;
+ // if P == '0' && W == '0' then UNDEFINED;
+ if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLD;
+ if (t == 15)
+ return false; // PLD is not implemented yet
+
+ // if Rn == '1111' then SEE LDRB (literal);
+ if (n == 15)
+ return EmulateLDRBLiteral(opcode, eEncodingT1);
+
+ // if BadReg(t) || (wback && n == t) then UNPREDICTABLE;
+ if (BadReg(t) || (wback && (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint32_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t address;
+ addr_t offset_addr;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // R[t] = ZeroExtend(MemU[address,1], 32);
+ RegisterInfo base_reg;
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ uint64_t data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// LDRB (literal) calculates an address from the PC value and an immediate
+// offset, loads a byte from memory,
+// zero-extends it to form a 32-bit word and writes it to a register.
+bool EmulateInstructionARM::EmulateLDRBLiteral(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ base = Align(PC,4);
+ address = if add then (base + imm32) else (base - imm32);
+ R[t] = ZeroExtend(MemU[address,1], 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t imm32;
+ bool add;
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0);
+ add = BitIsSet(opcode, 23);
+
+ // if Rt == '1111' then SEE PLD;
+ if (t == 15)
+ return false; // PLD is not implemented yet
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // t == UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0);
+ add = BitIsSet(opcode, 23);
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ // base = Align(PC,4);
+ uint32_t pc_val = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ uint32_t base = AlignPC(pc_val);
+
+ addr_t address;
+ // address = if add then (base + imm32) else (base - imm32);
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ // R[t] = ZeroExtend(MemU[address,1], 32);
+ EmulateInstruction::Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(address - base);
+
+ uint64_t data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data))
+ return false;
+ }
+ return true;
+}
+
+// LDRB (register) calculates an address from a base register value and an
+// offset rigister value, loads a byte from memory, zero-extends it to form a
+// 32-bit word, and writes it to a register. The offset register value can
+// optionally be shifted.
+bool EmulateInstructionARM::EmulateLDRBRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ R[t] = ZeroExtend(MemU[address,1],32);
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+
+ case eEncodingT2:
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if Rt == '1111' then SEE PLD;
+ if (t == 15)
+ return false; // PLD is not implemented yet
+
+ // if Rn == '1111' then SEE LDRB (literal);
+ if (n == 15)
+ return EmulateLDRBLiteral(opcode, eEncodingT1);
+
+ // if t == 13 || BadReg(m) then UNPREDICTABLE;
+ if ((t == 13) || BadReg(m))
+ return false;
+ break;
+
+ case eEncodingA1: {
+ // if P == '0' && W == '1' then SEE LDRBT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ uint32_t type = Bits32(opcode, 6, 5);
+ uint32_t imm5 = Bits32(opcode, 11, 7);
+ shift_n = DecodeImmShift(type, imm5, shift_t);
+
+ // if t == 15 || m == 15 then UNPREDICTABLE;
+ if ((t == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ uint32_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ uint32_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // R[t] = ZeroExtend(MemU[address,1],32);
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// LDRH (immediate, Thumb) calculates an address from a base register value and
+// an immediate offset, loads a
+// halfword from memory, zero-extends it to form a 32-bit word, and writes it
+// to a register. It can use offset, post-indexed, or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateLDRHImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,2];
+ if wback then R[n] = offset_addr;
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = ZeroExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'0', 32);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ imm32 = Bits32(opcode, 10, 6) << 1;
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ break;
+
+ case eEncodingT2:
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // if Rn == '1111' then SEE LDRH (literal);
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+ break;
+
+ case eEncodingT3:
+ // if Rn == '1111' then SEE LDRH (literal);
+ // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE
+ // "Unallocated memory hints";
+ // if P == '1' && U == '1' && W == '0' then SEE LDRHT;
+ // if P == '0' && W == '0' then UNDEFINED;
+ if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if BadReg(t) || (wback && n == t) then UNPREDICTABLE;
+ if (BadReg(t) || (wback && (n == t)))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint32_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = ZeroExtend(data, 32);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// LDRH (literal) caculates an address from the PC value and an immediate
+// offset, loads a halfword from memory,
+// zero-extends it to form a 32-bit word, and writes it to a register.
+bool EmulateInstructionARM::EmulateLDRHLiteral(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ base = Align(PC,4);
+ address = if add then (base + imm32) else (base - imm32);
+ data = MemU[address,2];
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = ZeroExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t imm32;
+ bool add;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ switch (encoding) {
+ case eEncodingT1:
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0);
+ add = BitIsSet(opcode, 23);
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ uint32_t imm4H = Bits32(opcode, 11, 8);
+ uint32_t imm4L = Bits32(opcode, 3, 0);
+
+ // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = (imm4H << 4) | imm4L;
+ add = BitIsSet(opcode, 23);
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ // base = Align(PC,4);
+ uint64_t pc_value = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ addr_t base = AlignPC(pc_value);
+ addr_t address;
+
+ // address = if add then (base + imm32) else (base - imm32);
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base);
+
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = ZeroExtend(data, 32);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// LDRH (literal) calculates an address from a base register value and an offset
+// register value, loads a halfword
+// from memory, zero-extends it to form a 32-bit word, and writes it to a
+// register. The offset register value can be shifted left by 0, 1, 2, or 3
+// bits.
+bool EmulateInstructionARM::EmulateLDRHRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,2];
+ if wback then R[n] = offset_addr;
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = ZeroExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation
+ // in ThumbEE";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then SEE LDRH (literal);
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if t == 13 || BadReg(m) then UNPREDICTABLE;
+ if ((t == 13) || BadReg(m))
+ return false;
+ break;
+
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE LDRHT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ // if t == 15 || m == 15 then UNPREDICTABLE;
+ if ((t == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusIndirectOffset(base_reg, offset_reg);
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = ZeroExtend(data, 32);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusIndirectOffset(base_reg, offset_reg);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// LDRSB (immediate) calculates an address from a base register value and an
+// immediate offset, loads a byte from
+// memory, sign-extends it to form a 32-bit word, and writes it to a register.
+// It can use offset, post-indexed, or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateLDRSBImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ R[t] = SignExtend(MemU[address,1], 32);
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if Rt == '1111' then SEE PLI;
+ // if Rn == '1111' then SEE LDRSB (literal);
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLI;
+ // if Rn == '1111' then SEE LDRSB (literal);
+ // if P == '1' && U == '1' && W == '0' then SEE LDRSBT;
+ // if P == '0' && W == '0' then UNDEFINED;
+ if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if BadReg(t) || (wback && n == t) then UNPREDICTABLE;
+ if (((t == 13) ||
+ ((t == 15) && (BitIsClear(opcode, 10) || BitIsSet(opcode, 9) ||
+ BitIsSet(opcode, 8)))) ||
+ (wback && (n == t)))
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ // if Rn == '1111' then SEE LDRSB (literal);
+ // if P == '0' && W == '1' then SEE LDRSBT;
+ // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+
+ uint32_t imm4H = Bits32(opcode, 11, 8);
+ uint32_t imm4L = Bits32(opcode, 3, 0);
+ imm32 = (imm4H << 4) | imm4L;
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // if t == 15 || (wback && n == t) then UNPREDICTABLE;
+ if ((t == 15) || (wback && (n == t)))
+ return false;
+
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ uint64_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // R[t] = SignExtend(MemU[address,1], 32);
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t unsigned_data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ int64_t signed_data = llvm::SignExtend64<8>(unsigned_data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// LDRSB (literal) calculates an address from the PC value and an immediate
+// offset, loads a byte from memory,
+// sign-extends it to form a 32-bit word, and writes tit to a register.
+bool EmulateInstructionARM::EmulateLDRSBLiteral(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ base = Align(PC,4);
+ address = if add then (base + imm32) else (base - imm32);
+ R[t] = SignExtend(MemU[address,1], 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t imm32;
+ bool add;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ switch (encoding) {
+ case eEncodingT1:
+ // if Rt == '1111' then SEE PLI;
+ // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0);
+ add = BitIsSet(opcode, 23);
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ uint32_t imm4H = Bits32(opcode, 11, 8);
+ uint32_t imm4L = Bits32(opcode, 3, 0);
+ imm32 = (imm4H << 4) | imm4L;
+ add = BitIsSet(opcode, 23);
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ // base = Align(PC,4);
+ uint64_t pc_value = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+ uint64_t base = AlignPC(pc_value);
+
+ // address = if add then (base + imm32) else (base - imm32);
+ addr_t address;
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ // R[t] = SignExtend(MemU[address,1], 32);
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base);
+
+ uint64_t unsigned_data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ int64_t signed_data = llvm::SignExtend64<8>(unsigned_data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+ }
+ return true;
+}
+
+// LDRSB (register) calculates an address from a base register value and an
+// offset register value, loadsa byte from
+// memory, sign-extends it to form a 32-bit word, and writes it to a register.
+// The offset register value can be shifted left by 0, 1, 2, or 3 bits.
+bool EmulateInstructionARM::EmulateLDRSBRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ R[t] = SignExtend(MemU[address,1], 32);
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // if Rt == '1111' then SEE PLI;
+ // if Rn == '1111' then SEE LDRSB (literal);
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if t == 13 || BadReg(m) then UNPREDICTABLE;
+ if ((t == 13) || BadReg(m))
+ return false;
+ break;
+
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE LDRSBT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ // if t == 15 || m == 15 then UNPREDICTABLE;
+ if ((t == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // R[t] = SignExtend(MemU[address,1], 32);
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusIndirectOffset(base_reg, offset_reg);
+
+ uint64_t unsigned_data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ int64_t signed_data = llvm::SignExtend64<8>(unsigned_data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// LDRSH (immediate) calculates an address from a base register value and an
+// immediate offset, loads a halfword from
+// memory, sign-extends it to form a 32-bit word, and writes it to a register.
+// It can use offset, post-indexed, or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateLDRSHImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,2];
+ if wback then R[n] = offset_addr;
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = SignExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if Rn == '1111' then SEE LDRSH (literal);
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then SEE LDRSH (literal);
+ // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE
+ // "Unallocated memory hints";
+ // if P == '1' && U == '1' && W == '0' then SEE LDRSHT;
+ // if P == '0' && W == '0' then UNDEFINED;
+ if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if BadReg(t) || (wback && n == t) then UNPREDICTABLE;
+ if (BadReg(t) || (wback && (n == t)))
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ // if Rn == '1111' then SEE LDRSH (literal);
+ // if P == '0' && W == '1' then SEE LDRSHT;
+ // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ uint32_t imm4H = Bits32(opcode, 11, 8);
+ uint32_t imm4L = Bits32(opcode, 3, 0);
+ imm32 = (imm4H << 4) | imm4L;
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if t == 15 || (wback && n == t) then UNPREDICTABLE;
+ if ((t == 15) || (wback && (n == t)))
+ return false;
+
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = SignExtend(data, 32);
+ int64_t signed_data = llvm::SignExtend64<16>(data);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// LDRSH (literal) calculates an address from the PC value and an immediate
+// offset, loads a halfword from memory,
+// sign-extends it to from a 32-bit word, and writes it to a register.
+bool EmulateInstructionARM::EmulateLDRSHLiteral(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ base = Align(PC,4);
+ address = if add then (base + imm32) else (base - imm32);
+ data = MemU[address,2];
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = SignExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t imm32;
+ bool add;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ switch (encoding) {
+ case eEncodingT1:
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0);
+ add = BitIsSet(opcode, 23);
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ uint32_t imm4H = Bits32(opcode, 11, 8);
+ uint32_t imm4L = Bits32(opcode, 3, 0);
+ imm32 = (imm4H << 4) | imm4L;
+ add = BitIsSet(opcode, 23);
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+
+ break;
+ }
+ default:
+ return false;
+ }
+
+ // base = Align(PC,4);
+ uint64_t pc_value = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ uint64_t base = AlignPC(pc_value);
+
+ addr_t address;
+ // address = if add then (base + imm32) else (base - imm32);
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, imm32);
+
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = SignExtend(data, 32);
+ int64_t signed_data = llvm::SignExtend64<16>(data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// LDRSH (register) calculates an address from a base register value and an
+// offset register value, loads a halfword
+// from memory, sign-extends it to form a 32-bit word, and writes it to a
+// register. The offset register value can be shifted left by 0, 1, 2, or 3
+// bits.
+bool EmulateInstructionARM::EmulateLDRSHRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,2];
+ if wback then R[n] = offset_addr;
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = SignExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation
+ // in ThumbEE";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then SEE LDRSH (literal);
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if t == 13 || BadReg(m) then UNPREDICTABLE;
+ if ((t == 13) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE LDRSHT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ // if t == 15 || m == 15 then UNPREDICTABLE;
+ if ((t == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusIndirectOffset(base_reg, offset_reg);
+
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = SignExtend(data, 32);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusIndirectOffset(base_reg, offset_reg);
+
+ int64_t signed_data = llvm::SignExtend64<16>(data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// SXTB extracts an 8-bit value from a register, sign-extends it to 32 bits, and
+// writes the result to the destination
+// register. You can specifiy a rotation by 0, 8, 16, or 24 bits before
+// extracting the 8-bit value.
+bool EmulateInstructionARM::EmulateSXTB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ rotated = ROR(R[m], rotation);
+ R[d] = SignExtend(rotated<7:0>, 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t m;
+ uint32_t rotation;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = 0;
+ d = Bits32(opcode, 2, 0);
+ m = Bits32(opcode, 5, 3);
+ rotation = 0;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 11, 8);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 5, 4) << 3;
+
+ // if BadReg(d) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 15, 12);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 11, 10) << 3;
+
+ // if d == 15 || m == 15 then UNPREDICTABLE;
+ if ((d == 15) || (m == 15))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // rotated = ROR(R[m], rotation);
+ uint64_t rotated = ROR(Rm, rotation, &success);
+ if (!success)
+ return false;
+
+ // R[d] = SignExtend(rotated<7:0>, 32);
+ int64_t data = llvm::SignExtend64<8>(rotated);
+
+ RegisterInfo source_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, source_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegister(source_reg);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ (uint64_t)data))
+ return false;
+ }
+ return true;
+}
+
+// SXTH extracts a 16-bit value from a register, sign-extends it to 32 bits, and
+// writes the result to the destination
+// register. You can specify a rotation by 0, 8, 16, or 24 bits before
+// extracting the 16-bit value.
+bool EmulateInstructionARM::EmulateSXTH(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ rotated = ROR(R[m], rotation);
+ R[d] = SignExtend(rotated<15:0>, 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t m;
+ uint32_t rotation;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = 0;
+ d = Bits32(opcode, 2, 0);
+ m = Bits32(opcode, 5, 3);
+ rotation = 0;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 11, 8);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 5, 4) << 3;
+
+ // if BadReg(d) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 15, 12);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 11, 10) << 3;
+
+ // if d == 15 || m == 15 then UNPREDICTABLE;
+ if ((d == 15) || (m == 15))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // rotated = ROR(R[m], rotation);
+ uint64_t rotated = ROR(Rm, rotation, &success);
+ if (!success)
+ return false;
+
+ // R[d] = SignExtend(rotated<15:0>, 32);
+ RegisterInfo source_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, source_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegister(source_reg);
+
+ int64_t data = llvm::SignExtend64<16>(rotated);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ (uint64_t)data))
+ return false;
+ }
+
+ return true;
+}
+
+// UXTB extracts an 8-bit value from a register, zero-extneds it to 32 bits, and
+// writes the result to the destination
+// register. You can specify a rotation by 0, 8, 16, or 24 bits before
+// extracting the 8-bit value.
+bool EmulateInstructionARM::EmulateUXTB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ rotated = ROR(R[m], rotation);
+ R[d] = ZeroExtend(rotated<7:0>, 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t m;
+ uint32_t rotation;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = 0;
+ d = Bits32(opcode, 2, 0);
+ m = Bits32(opcode, 5, 3);
+ rotation = 0;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 11, 8);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 5, 4) << 3;
+
+ // if BadReg(d) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 15, 12);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 11, 10) << 3;
+
+ // if d == 15 || m == 15 then UNPREDICTABLE;
+ if ((d == 15) || (m == 15))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // rotated = ROR(R[m], rotation);
+ uint64_t rotated = ROR(Rm, rotation, &success);
+ if (!success)
+ return false;
+
+ // R[d] = ZeroExtend(rotated<7:0>, 32);
+ RegisterInfo source_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, source_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegister(source_reg);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ Bits32(rotated, 7, 0)))
+ return false;
+ }
+ return true;
+}
+
+// UXTH extracts a 16-bit value from a register, zero-extends it to 32 bits, and
+// writes the result to the destination
+// register. You can specify a rotation by 0, 8, 16, or 24 bits before
+// extracting the 16-bit value.
+bool EmulateInstructionARM::EmulateUXTH(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ rotated = ROR(R[m], rotation);
+ R[d] = ZeroExtend(rotated<15:0>, 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t m;
+ uint32_t rotation;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = 0;
+ d = Bits32(opcode, 2, 0);
+ m = Bits32(opcode, 5, 3);
+ rotation = 0;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 11, 8);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 5, 4) << 3;
+
+ // if BadReg(d) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 15, 12);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 11, 10) << 3;
+
+ // if d == 15 || m == 15 then UNPREDICTABLE;
+ if ((d == 15) || (m == 15))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // rotated = ROR(R[m], rotation);
+ uint64_t rotated = ROR(Rm, rotation, &success);
+ if (!success)
+ return false;
+
+ // R[d] = ZeroExtend(rotated<15:0>, 32);
+ RegisterInfo source_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, source_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegister(source_reg);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ Bits32(rotated, 15, 0)))
+ return false;
+ }
+ return true;
+}
+
+// RFE (Return From Exception) loads the PC and the CPSR from the word at the
+// specified address and the following
+// word respectively.
+bool EmulateInstructionARM::EmulateRFE(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE then
+ UNPREDICTABLE;
+ else
+ address = if increment then R[n] else R[n]-8;
+ if wordhigher then address = address+4;
+ CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE);
+ BranchWritePC(MemA[address,4]);
+ if wback then R[n] = if increment then R[n]+8 else R[n]-8;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ bool wback;
+ bool increment;
+ bool wordhigher;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // n = UInt(Rn); wback = (W == '1'); increment = FALSE; wordhigher =
+ // FALSE;
+ n = Bits32(opcode, 19, 16);
+ wback = BitIsSet(opcode, 21);
+ increment = false;
+ wordhigher = false;
+
+ // if n == 15 then UNPREDICTABLE;
+ if (n == 15)
+ return false;
+
+ // if InITBlock() && !LastInITBlock() then UNPREDICTABLE;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ // n = UInt(Rn); wback = (W == '1'); increment = TRUE; wordhigher = FALSE;
+ n = Bits32(opcode, 19, 16);
+ wback = BitIsSet(opcode, 21);
+ increment = true;
+ wordhigher = false;
+
+ // if n == 15 then UNPREDICTABLE;
+ if (n == 15)
+ return false;
+
+ // if InITBlock() && !LastInITBlock() then UNPREDICTABLE;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // n = UInt(Rn);
+ n = Bits32(opcode, 19, 16);
+
+ // wback = (W == '1'); inc = (U == '1'); wordhigher = (P == U);
+ wback = BitIsSet(opcode, 21);
+ increment = BitIsSet(opcode, 23);
+ wordhigher = (Bit32(opcode, 24) == Bit32(opcode, 23));
+
+ // if n == 15 then UNPREDICTABLE;
+ if (n == 15)
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE
+ // then
+ if (!CurrentModeIsPrivileged())
+ // UNPREDICTABLE;
+ return false;
+ else {
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t address;
+ // address = if increment then R[n] else R[n]-8;
+ if (increment)
+ address = Rn;
+ else
+ address = Rn - 8;
+
+ // if wordhigher then address = address+4;
+ if (wordhigher)
+ address = address + 4;
+
+ // CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE);
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextReturnFromException;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t data = MemARead(context, address + 4, 4, 0, &success);
+ if (!success)
+ return false;
+
+ CPSRWriteByInstr(data, 15, true);
+
+ // BranchWritePC(MemA[address,4]);
+ uint64_t data2 = MemARead(context, address, 4, 0, &success);
+ if (!success)
+ return false;
+
+ BranchWritePC(context, data2);
+
+ // if wback then R[n] = if increment then R[n]+8 else R[n]-8;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ if (increment) {
+ context.SetOffset(8);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ Rn + 8))
+ return false;
+ } else {
+ context.SetOffset(-8);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ Rn - 8))
+ return false;
+ }
+ } // if wback
+ }
+ } // if ConditionPassed()
+ return true;
+}
+
+// Bitwise Exclusive OR (immediate) performs a bitwise exclusive OR of a
+// register value and an immediate value, and writes the result to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateEORImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] EOR imm32;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t
+ imm32; // the immediate value to be ORed to the value obtained from Rn
+ bool setflags;
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ // if Rd == '1111' && S == '1' then SEE TEQ (immediate);
+ if (Rd == 15 && setflags)
+ return EmulateTEQImm(opcode, eEncodingT1);
+ if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 ^ imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise Exclusive OR (register) performs a bitwise exclusive OR of a
+// register value and an optionally-shifted register value, and writes the
+// result to the destination register. It can optionally update the condition
+// flags based on the result.
+bool EmulateInstructionARM::EmulateEORReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] EOR shifted;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if Rd == '1111' && S == '1' then SEE TEQ (register);
+ if (Rd == 15 && setflags)
+ return EmulateTEQReg(opcode, eEncodingT1);
+ if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 ^ shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise OR (immediate) performs a bitwise (inclusive) OR of a register value
+// and an immediate value, and writes the result to the destination register.
+// It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateORRImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] OR imm32;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t
+ imm32; // the immediate value to be ORed to the value obtained from Rn
+ bool setflags;
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ // if Rn == '1111' then SEE MOV (immediate);
+ if (Rn == 15)
+ return EmulateMOVRdImm(opcode, eEncodingT2);
+ if (BadReg(Rd) || Rn == 13)
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 | imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise OR (register) performs a bitwise (inclusive) OR of a register value
+// and an optionally-shifted register value, and writes the result to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateORRReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] OR shifted;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if Rn == '1111' then SEE MOV (register);
+ if (Rn == 15)
+ return EmulateMOVRdRm(opcode, eEncodingT3);
+ if (BadReg(Rd) || Rn == 13 || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 | shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Reverse Subtract (immediate) subtracts a register value from an immediate
+// value, and writes the result to the destination register. It can optionally
+// update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateRSBImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ bool setflags;
+ uint32_t
+ imm32; // the immediate value to be added to the value obtained from Rn
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 2, 0);
+ Rn = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ imm32 = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (BadReg(Rd) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(~reg_val, imm32, 1);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// Reverse Subtract (register) subtracts a register value from an optionally-
+// shifted register value, and writes the result to the destination register.
+// It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateRSBReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ uint32_t Rm; // the second operand
+ bool setflags;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE;
+ if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from register Rn.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the register value from register Rm.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(~val1, shifted, 1);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// Reverse Subtract with Carry (immediate) subtracts a register value and the
+// value of NOT (Carry flag) from an immediate value, and writes the result to
+// the destination register. It can optionally update the condition flags based
+// on the result.
+bool EmulateInstructionARM::EmulateRSCImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, APSR.C);
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ bool setflags;
+ uint32_t
+ imm32; // the immediate value to be added to the value obtained from Rn
+ switch (encoding) {
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(~reg_val, imm32, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// Reverse Subtract with Carry (register) subtracts a register value and the
+// value of NOT (Carry flag) from an optionally-shifted register value, and
+// writes the result to the destination register. It can optionally update the
+// condition flags based on the result.
+bool EmulateInstructionARM::EmulateRSCReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, APSR.C);
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ uint32_t Rm; // the second operand
+ bool setflags;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ switch (encoding) {
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from register Rn.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the register value from register Rm.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(~val1, shifted, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// Subtract with Carry (immediate) subtracts an immediate value and the value
+// of
+// NOT (Carry flag) from a register value, and writes the result to the
+// destination register.
+// It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateSBCImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ bool setflags;
+ uint32_t
+ imm32; // the immediate value to be added to the value obtained from Rn
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (BadReg(Rd) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// Subtract with Carry (register) subtracts an optionally-shifted register
+// value and the value of
+// NOT (Carry flag) from a register value, and writes the result to the
+// destination register.
+// It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateSBCReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ uint32_t Rm; // the second operand
+ bool setflags;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from register Rn.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the register value from register Rm.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(val1, ~shifted, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// This instruction subtracts an immediate value from a register value, and
+// writes the result to the destination register. It can optionally update the
+// condition flags based on the result.
+bool EmulateInstructionARM::EmulateSUBImmThumb(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1');
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ bool setflags;
+ uint32_t imm32; // the immediate value to be subtracted from the value
+ // obtained from Rn
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 2, 0);
+ Rn = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ imm32 = Bits32(opcode, 8, 6); // imm32 = ZeroExtend(imm3, 32)
+ break;
+ case eEncodingT2:
+ Rd = Rn = Bits32(opcode, 10, 8);
+ setflags = !InITBlock();
+ imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32)
+ break;
+ case eEncodingT3:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+
+ // if Rd == '1111' && S == '1' then SEE CMP (immediate);
+ if (Rd == 15 && setflags)
+ return EmulateCMPImm(opcode, eEncodingT2);
+
+ // if Rn == '1101' then SEE SUB (SP minus immediate);
+ if (Rn == 13)
+ return EmulateSUBSPImm(opcode, eEncodingT2);
+
+ // if d == 13 || (d == 15 && S == '0') || n == 15 then UNPREDICTABLE;
+ if (Rd == 13 || (Rd == 15 && !setflags) || Rn == 15)
+ return false;
+ break;
+ case eEncodingT4:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32)
+
+ // if Rn == '1111' then SEE ADR;
+ if (Rn == 15)
+ return EmulateADR(opcode, eEncodingT2);
+
+ // if Rn == '1101' then SEE SUB (SP minus immediate);
+ if (Rn == 13)
+ return EmulateSUBSPImm(opcode, eEncodingT3);
+
+ if (BadReg(Rd))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// This instruction subtracts an immediate value from a register value, and
+// writes the result to the destination register. It can optionally update the
+// condition flags based on the result.
+bool EmulateInstructionARM::EmulateSUBImmARM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1');
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ bool setflags;
+ uint32_t imm32; // the immediate value to be subtracted from the value
+ // obtained from Rn
+ switch (encoding) {
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ // if Rn == '1111' && S == '0' then SEE ADR;
+ if (Rn == 15 && !setflags)
+ return EmulateADR(opcode, eEncodingA2);
+
+ // if Rn == '1101' then SEE SUB (SP minus immediate);
+ if (Rn == 13)
+ return EmulateSUBSPImm(opcode, eEncodingA1);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1);
+
+ EmulateInstruction::Context context;
+ if (Rd == 13)
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, Rn, dwarf_reg);
+ int64_t imm32_signed = imm32;
+ context.SetRegisterPlusOffset(dwarf_reg, -imm32_signed);
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// Test Equivalence (immediate) performs a bitwise exclusive OR operation on a
+// register value and an immediate value. It updates the condition flags based
+// on the result, and discards the result.
+bool EmulateInstructionARM::EmulateTEQImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] EOR imm32;
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rn;
+ uint32_t
+ imm32; // the immediate value to be ANDed to the value obtained from Rn
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ if (BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 ^ imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteFlags(context, result, carry))
+ return false;
+ }
+ return true;
+}
+
+// Test Equivalence (register) performs a bitwise exclusive OR operation on a
+// register value and an optionally-shifted register value. It updates the
+// condition flags based on the result, and discards the result.
+bool EmulateInstructionARM::EmulateTEQReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] EOR shifted;
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 ^ shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteFlags(context, result, carry))
+ return false;
+ }
+ return true;
+}
+
+// Test (immediate) performs a bitwise AND operation on a register value and an
+// immediate value. It updates the condition flags based on the result, and
+// discards the result.
+bool EmulateInstructionARM::EmulateTSTImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] AND imm32;
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rn;
+ uint32_t
+ imm32; // the immediate value to be ANDed to the value obtained from Rn
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ if (BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 & imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteFlags(context, result, carry))
+ return false;
+ }
+ return true;
+}
+
+// Test (register) performs a bitwise AND operation on a register value and an
+// optionally-shifted register value. It updates the condition flags based on
+// the result, and discards the result.
+bool EmulateInstructionARM::EmulateTSTReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] AND shifted;
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 & shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteFlags(context, result, carry))
+ return false;
+ }
+ return true;
+}
+
+// A8.6.216 SUB (SP minus register)
+bool EmulateInstructionARM::EmulateSUBSPReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t m;
+ bool setflags;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); m = UInt(Rm); setflags = (S == '1');
+ d = Bits32(opcode, 11, 8);
+ m = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+
+ // if d == 13 && (shift_t != SRType_LSL || shift_n > 3) then
+ // UNPREDICTABLE;
+ if ((d == 13) && ((shift_t != SRType_LSL) || (shift_n > 3)))
+ return false;
+
+ // if d == 15 || BadReg(m) then UNPREDICTABLE;
+ if ((d == 15) || BadReg(m))
+ return false;
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); m = UInt(Rm); setflags = (S == '1');
+ d = Bits32(opcode, 15, 12);
+ m = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (d == 15 && setflags)
+ EmulateSUBSPcLrEtc(opcode, encoding);
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+
+ default:
+ return false;
+ }
+
+ // shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), '1');
+ uint32_t sp_val = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(sp_val, ~shifted, 1);
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, dwarf_reg);
+ context.SetRegisterRegisterOperands(sp_reg, dwarf_reg);
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, dwarf_r0 + d, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// A8.6.7 ADD (register-shifted register)
+bool EmulateInstructionARM::EmulateADDRegShift(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shift_n = UInt(R[s]<7:0>);
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], shifted, '0');
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ uint32_t s;
+ bool setflags;
+ ARM_ShifterType shift_t;
+
+ switch (encoding) {
+ case eEncodingA1:
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); s = UInt(Rs);
+ d = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ s = Bits32(opcode, 11, 8);
+
+ // setflags = (S == '1'); shift_t = DecodeRegShift(type);
+ setflags = BitIsSet(opcode, 20);
+ shift_t = DecodeRegShift(Bits32(opcode, 6, 5));
+
+ // if d == 15 || n == 15 || m == 15 || s == 15 then UNPREDICTABLE;
+ if ((d == 15) || (n == 15) || (m == 15) || (s == 15))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ // shift_n = UInt(R[s]<7:0>);
+ uint32_t Rs = ReadCoreReg(s, &success);
+ if (!success)
+ return false;
+
+ uint32_t shift_n = Bits32(Rs, 7, 0);
+
+ // shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // (result, carry, overflow) = AddWithCarry(R[n], shifted, '0');
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(Rn, shifted, 0);
+
+ // R[d] = result;
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ RegisterInfo reg_n;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, reg_n);
+ RegisterInfo reg_m;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, reg_m);
+
+ context.SetRegisterRegisterOperands(reg_n, reg_m);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ res.result))
+ return false;
+
+ // if setflags then
+ // APSR.N = result<31>;
+ // APSR.Z = IsZeroBit(result);
+ // APSR.C = carry;
+ // APSR.V = overflow;
+ if (setflags)
+ return WriteFlags(context, res.result, res.carry_out, res.overflow);
+ }
+ return true;
+}
+
+// A8.6.213 SUB (register)
+bool EmulateInstructionARM::EmulateSUBReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool setflags;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = !InITBlock();
+ d = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+ setflags = !InITBlock();
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S =="1");
+ d = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+
+ // if Rd == "1111" && S == "1" then SEE CMP (register);
+ if (d == 15 && setflags == 1)
+ return EmulateCMPImm(opcode, eEncodingT3);
+
+ // if Rn == "1101" then SEE SUB (SP minus register);
+ if (n == 13)
+ return EmulateSUBSPReg(opcode, eEncodingT1);
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+
+ // if d == 13 || (d == 15 && S == '0') || n == 15 || BadReg(m) then
+ // UNPREDICTABLE;
+ if ((d == 13) || ((d == 15) && BitIsClear(opcode, 20)) || (n == 15) ||
+ BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // if Rn == '1101' then SEE SUB (SP minus register);
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == '1');
+ d = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if ((d == 15) && setflags)
+ EmulateSUBSPcLrEtc(opcode, encoding);
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1');
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(Rn, ~shifted, 1);
+
+ // if d == 15 then // Can only occur for ARM encoding ALUWritePC(result);
+ // // setflags is always FALSE here else
+ // R[d] = result;
+ // if setflags then
+ // APSR.N = result<31>;
+ // APSR.Z = IsZeroBit(result);
+ // APSR.C = carry;
+ // APSR.V = overflow;
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ RegisterInfo reg_n;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, reg_n);
+ RegisterInfo reg_m;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, reg_m);
+ context.SetRegisterRegisterOperands(reg_n, reg_m);
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, dwarf_r0 + d, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// A8.6.202 STREX
+// Store Register Exclusive calculates an address from a base register value
+// and an immediate offset, and stores a word from a register to memory if the
+// executing processor has exclusive access to the memory addressed.
+bool EmulateInstructionARM::EmulateSTREX(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ address = R[n] + imm32;
+ if ExclusiveMonitorsPass(address,4) then
+ MemA[address,4] = R[t];
+ R[d] = 0;
+ else
+ R[d] = 1;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 =
+ // ZeroExtend(imm8:'00',
+ // 32);
+ d = Bits32(opcode, 11, 8);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // if BadReg(d) || BadReg(t) || n == 15 then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(t) || (n == 15))
+ return false;
+
+ // if d == n || d == t then UNPREDICTABLE;
+ if ((d == n) || (d == t))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = Zeros(32); // Zero
+ // offset
+ d = Bits32(opcode, 15, 12);
+ t = Bits32(opcode, 3, 0);
+ n = Bits32(opcode, 19, 16);
+ imm32 = 0;
+
+ // if d == 15 || t == 15 || n == 15 then UNPREDICTABLE;
+ if ((d == 15) || (t == 15) || (n == 15))
+ return false;
+
+ // if d == n || d == t then UNPREDICTABLE;
+ if ((d == n) || (d == t))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // address = R[n] + imm32;
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t address = Rn + imm32;
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, imm32);
+
+ // if ExclusiveMonitorsPass(address,4) then if (ExclusiveMonitorsPass
+ // (address, addr_byte_size)) -- For now, for the sake of emulation, we
+ // will say this
+ // always return
+ // true.
+ if (true) {
+ // MemA[address,4] = R[t];
+ uint32_t Rt =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address, Rt, addr_byte_size))
+ return false;
+
+ // R[d] = 0;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, 0))
+ return false;
+ }
+#if 0 // unreachable because if true
+ else
+ {
+ // R[d] = 1;
+ if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 1))
+ return false;
+ }
+#endif // unreachable because if true
+ }
+ return true;
+}
+
+// A8.6.197 STRB (immediate, ARM)
+bool EmulateInstructionARM::EmulateSTRBImmARM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ MemU[address,1] = R[t]<7:0>;
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE STRBT;
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // MemU[address,1] = R[t]<7:0>;
+ uint32_t Rt = ReadCoreReg(t, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ if (!MemUWrite(context, address, Bits32(Rt, 7, 0), 1))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.194 STR (immediate, ARM)
+bool EmulateInstructionARM::EmulateSTRImmARM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ MemU[address,4] = if t == 15 then PCStoreValue() else R[t];
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ switch (encoding) {
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE STRT;
+ // if Rn == '1101' && P == '1' && U == '0' && W == '1' && imm12 ==
+ // '000000000100' then SEE PUSH;
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ // MemU[address,4] = if t == 15 then PCStoreValue() else R[t];
+ uint32_t Rt = ReadCoreReg(t, &success);
+ if (!success)
+ return false;
+
+ if (t == 15) {
+ uint32_t pc_value = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ if (!MemUWrite(context, address, pc_value, addr_byte_size))
+ return false;
+ } else {
+ if (!MemUWrite(context, address, Rt, addr_byte_size))
+ return false;
+ }
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetImmediate(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.66 LDRD (immediate)
+// Load Register Dual (immediate) calculates an address from a base register
+// value and an immediate offset, loads two words from memory, and writes them
+// to two registers. It can use offset, post-indexed, or pre-indexed
+// addressing.
+bool EmulateInstructionARM::EmulateLDRDImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ R[t] = MemA[address,4];
+ R[t2] = MemA[address+4,4];
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t t2;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // if P == '0' && W == '0' then SEE 'Related encodings';
+ // if Rn == '1111' then SEE LDRD (literal);
+ // t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 =
+ // ZeroExtend(imm8:'00', 32);
+ t = Bits32(opcode, 15, 12);
+ t2 = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+
+ // if wback && (n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == t) || (n == t2)))
+ return false;
+
+ // if BadReg(t) || BadReg(t2) || t == t2 then UNPREDICTABLE;
+ if (BadReg(t) || BadReg(t2) || (t == t2))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // if Rn == '1111' then SEE LDRD (literal);
+ // if Rt<0> == '1' then UNPREDICTABLE;
+ // t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L,
+ // 32);
+ t = Bits32(opcode, 15, 12);
+ if (BitIsSet(t, 0))
+ return false;
+ t2 = t + 1;
+ n = Bits32(opcode, 19, 16);
+ imm32 = (Bits32(opcode, 11, 8) << 4) | Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if P == '0' && W == '1' then UNPREDICTABLE;
+ if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21))
+ return false;
+
+ // if wback && (n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == t) || (n == t2)))
+ return false;
+
+ // if t2 == 15 then UNPREDICTABLE;
+ if (t2 == 15)
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // R[t] = MemA[address,4];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ if (n == 13)
+ context.type = eContextPopRegisterOffStack;
+ else
+ context.type = eContextRegisterLoad;
+ context.SetAddress(address);
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t data = MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data))
+ return false;
+
+ // R[t2] = MemA[address+4,4];
+ context.SetAddress(address + 4);
+ data = MemARead(context, address + 4, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t2,
+ data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.68 LDRD (register)
+// Load Register Dual (register) calculates an address from a base register
+// value and a register offset, loads two words from memory, and writes them to
+// two registers. It can use offset, post-indexed or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateLDRDRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]);
+ address = if index then offset_addr else R[n];
+ R[t] = MemA[address,4];
+ R[t2] = MemA[address+4,4];
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t t2;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingA1:
+ // if Rt<0> == '1' then UNPREDICTABLE;
+ // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ if (BitIsSet(t, 0))
+ return false;
+ t2 = t + 1;
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if P == '0' && W == '1' then UNPREDICTABLE;
+ if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21))
+ return false;
+
+ // if t2 == 15 || m == 15 || m == t || m == t2 then UNPREDICTABLE;
+ if ((t2 == 15) || (m == 15) || (m == t) || (m == t2))
+ return false;
+
+ // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t) || (n == t2)))
+ return false;
+
+ // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE;
+ if ((ArchVersion() < 6) && wback && (m == n))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+
+ // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]);
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + Rm;
+ else
+ offset_addr = Rn - Rm;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ EmulateInstruction::Context context;
+ if (n == 13)
+ context.type = eContextPopRegisterOffStack;
+ else
+ context.type = eContextRegisterLoad;
+ context.SetAddress(address);
+
+ // R[t] = MemA[address,4];
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t data = MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data))
+ return false;
+
+ // R[t2] = MemA[address+4,4];
+
+ data = MemARead(context, address + 4, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t2,
+ data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.200 STRD (immediate)
+// Store Register Dual (immediate) calculates an address from a base register
+// value and an immediate offset, and stores two words from two registers to
+// memory. It can use offset, post-indexed, or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateSTRDImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ MemA[address,4] = R[t];
+ MemA[address+4,4] = R[t2];
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t t2;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // if P == '0' && W == '0' then SEE 'Related encodings';
+ // t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 =
+ // ZeroExtend(imm8:'00', 32);
+ t = Bits32(opcode, 15, 12);
+ t2 = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+
+ // if wback && (n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == t) || (n == t2)))
+ return false;
+
+ // if n == 15 || BadReg(t) || BadReg(t2) then UNPREDICTABLE;
+ if ((n == 15) || BadReg(t) || BadReg(t2))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // if Rt<0> == '1' then UNPREDICTABLE;
+ // t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L,
+ // 32);
+ t = Bits32(opcode, 15, 12);
+ if (BitIsSet(t, 0))
+ return false;
+
+ t2 = t + 1;
+ n = Bits32(opcode, 19, 16);
+ imm32 = (Bits32(opcode, 11, 8) << 4) | Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if P == '0' && W == '1' then UNPREDICTABLE;
+ if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21))
+ return false;
+
+ // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t) || (n == t2)))
+ return false;
+
+ // if t2 == 15 then UNPREDICTABLE;
+ if (t2 == 15)
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // MemA[address,4] = R[t];
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+
+ uint32_t data = ReadCoreReg(t, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ if (n == 13)
+ context.type = eContextPushRegisterOnStack;
+ else
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ if (!MemAWrite(context, address, data, addr_byte_size))
+ return false;
+
+ // MemA[address+4,4] = R[t2];
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t2, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ (address + 4) - Rn);
+
+ data = ReadCoreReg(t2, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address + 4, data, addr_byte_size))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ if (n == 13)
+ context.type = eContextAdjustStackPointer;
+ else
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.201 STRD (register)
+bool EmulateInstructionARM::EmulateSTRDReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]);
+ address = if index then offset_addr else R[n];
+ MemA[address,4] = R[t];
+ MemA[address+4,4] = R[t2];
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t t2;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingA1:
+ // if Rt<0> == '1' then UNPREDICTABLE;
+ // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ if (BitIsSet(t, 0))
+ return false;
+
+ t2 = t + 1;
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if P == '0' && W == '1' then UNPREDICTABLE;
+ if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21))
+ return false;
+
+ // if t2 == 15 || m == 15 then UNPREDICTABLE;
+ if ((t2 == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t) || (n == t2)))
+ return false;
+
+ // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE;
+ if ((ArchVersion() < 6) && wback && (m == n))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+ RegisterInfo data_reg;
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]);
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + Rm;
+ else
+ offset_addr = Rn - Rm;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+ // MemA[address,4] = R[t];
+ uint32_t Rt = ReadCoreReg(t, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ if (t == 13)
+ context.type = eContextPushRegisterOnStack;
+ else
+ context.type = eContextRegisterStore;
+
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ context.SetRegisterToRegisterPlusIndirectOffset(base_reg, offset_reg,
+ data_reg);
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ if (!MemAWrite(context, address, Rt, addr_byte_size))
+ return false;
+
+ // MemA[address+4,4] = R[t2];
+ uint32_t Rt2 = ReadCoreReg(t2, &success);
+ if (!success)
+ return false;
+
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t2, data_reg);
+
+ context.SetRegisterToRegisterPlusIndirectOffset(base_reg, offset_reg,
+ data_reg);
+
+ if (!MemAWrite(context, address + 4, Rt2, addr_byte_size))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.319 VLDM
+// Vector Load Multiple loads multiple extension registers from consecutive
+// memory locations using an address from an ARM core register.
+bool EmulateInstructionARM::EmulateVLDM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n);
+ address = if add then R[n] else R[n]-imm32;
+ if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32;
+ for r = 0 to regs-1
+ if single_regs then
+ S[d+r] = MemA[address,4]; address = address+4;
+ else
+ word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8;
+ // Combine the word-aligned words in the correct order for
+ // current endianness.
+ D[d+r] = if BigEndian() then word1:word2 else word2:word1;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ bool single_regs;
+ bool add;
+ bool wback;
+ uint32_t d;
+ uint32_t n;
+ uint32_t imm32;
+ uint32_t regs;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings';
+ // if P == '0' && U == '1' && W == '1' && Rn == '1101' then SEE VPOP;
+ // if P == '1' && W == '0' then SEE VLDR;
+ // if P == U && W == '1' then UNDEFINED;
+ if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21))
+ return false;
+
+ // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with
+ // !), 101 (DB with !)
+ // single_regs = FALSE; add = (U == '1'); wback = (W == '1');
+ single_regs = false;
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+
+ // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see 'FLDMX'.
+ regs = Bits32(opcode, 7, 0) / 2;
+
+ // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then
+ // UNPREDICTABLE;
+ if (n == 15 && (wback || CurrentInstrSet() != eModeARM))
+ return false;
+
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if ((regs == 0) || (regs > 16) || ((d + regs) > 32))
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ case eEncodingA2:
+ // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings';
+ // if P == '0' && U == '1' && W == '1' && Rn == '1101' then SEE VPOP;
+ // if P == '1' && W == '0' then SEE VLDR;
+ // if P == U && W == '1' then UNDEFINED;
+ if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21))
+ return false;
+
+ // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with
+ // !), 101 (DB with !) single_regs = TRUE; add = (U == '1'); wback = (W
+ // == '1'); d =
+ // UInt(Vd:D); n = UInt(Rn);
+ single_regs = true;
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+ d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22);
+ n = Bits32(opcode, 19, 16);
+
+ // imm32 = ZeroExtend(imm8:'00', 32); regs = UInt(imm8);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+ regs = Bits32(opcode, 7, 0);
+
+ // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then
+ // UNPREDICTABLE;
+ if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM)))
+ return false;
+
+ // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE;
+ if ((regs == 0) || ((d + regs) > 32))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = if add then R[n] else R[n]-imm32;
+ addr_t address;
+ if (add)
+ address = Rn;
+ else
+ address = Rn - imm32;
+
+ // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32;
+ EmulateInstruction::Context context;
+
+ if (wback) {
+ uint32_t value;
+ if (add)
+ value = Rn + imm32;
+ else
+ value = Rn - imm32;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetImmediateSigned(value - Rn);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ value))
+ return false;
+ }
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0;
+
+ context.type = eContextRegisterLoad;
+
+ // for r = 0 to regs-1
+ for (uint32_t r = 0; r < regs; ++r) {
+ if (single_regs) {
+ // S[d+r] = MemA[address,4]; address = address+4;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint32_t data = MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ start_reg + d + r, data))
+ return false;
+
+ address = address + 4;
+ } else {
+ // word1 = MemA[address,4]; word2 = MemA[address+4,4]; address =
+ // address+8;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ uint32_t word1 =
+ MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ context.SetRegisterPlusOffset(base_reg, (address + 4) - Rn);
+ uint32_t word2 =
+ MemARead(context, address + 4, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ address = address + 8;
+ // // Combine the word-aligned words in the correct order for current
+ // endianness.
+ // D[d+r] = if BigEndian() then word1:word2 else word2:word1;
+ uint64_t data;
+ if (GetByteOrder() == eByteOrderBig) {
+ data = word1;
+ data = (data << 32) | word2;
+ } else {
+ data = word2;
+ data = (data << 32) | word1;
+ }
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ start_reg + d + r, data))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// A8.6.399 VSTM
+// Vector Store Multiple stores multiple extension registers to consecutive
+// memory locations using an address from an
+// ARM core register.
+bool EmulateInstructionARM::EmulateVSTM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n);
+ address = if add then R[n] else R[n]-imm32;
+ if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32;
+ for r = 0 to regs-1
+ if single_regs then
+ MemA[address,4] = S[d+r]; address = address+4;
+ else
+ // Store as two word-aligned words in the correct order for
+ // current endianness.
+ MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>;
+ MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>;
+ address = address+8;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ bool single_regs;
+ bool add;
+ bool wback;
+ uint32_t d;
+ uint32_t n;
+ uint32_t imm32;
+ uint32_t regs;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings';
+ // if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE VPUSH;
+ // if P == '1' && W == '0' then SEE VSTR;
+ // if P == U && W == '1' then UNDEFINED;
+ if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21))
+ return false;
+
+ // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with
+ // !), 101 (DB with !)
+ // single_regs = FALSE; add = (U == '1'); wback = (W == '1');
+ single_regs = false;
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+
+ // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see 'FSTMX'.
+ regs = Bits32(opcode, 7, 0) / 2;
+
+ // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then
+ // UNPREDICTABLE;
+ if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM)))
+ return false;
+
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if ((regs == 0) || (regs > 16) || ((d + regs) > 32))
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ case eEncodingA2:
+ // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings';
+ // if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE VPUSH;
+ // if P == '1' && W == '0' then SEE VSTR;
+ // if P == U && W == '1' then UNDEFINED;
+ if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21))
+ return false;
+
+ // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with
+ // !), 101 (DB with !) single_regs = TRUE; add = (U == '1'); wback = (W
+ // == '1'); d =
+ // UInt(Vd:D); n = UInt(Rn);
+ single_regs = true;
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+ d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22);
+ n = Bits32(opcode, 19, 16);
+
+ // imm32 = ZeroExtend(imm8:'00', 32); regs = UInt(imm8);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+ regs = Bits32(opcode, 7, 0);
+
+ // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then
+ // UNPREDICTABLE;
+ if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM)))
+ return false;
+
+ // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE;
+ if ((regs == 0) || ((d + regs) > 32))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = if add then R[n] else R[n]-imm32;
+ addr_t address;
+ if (add)
+ address = Rn;
+ else
+ address = Rn - imm32;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32;
+ if (wback) {
+ uint32_t value;
+ if (add)
+ value = Rn + imm32;
+ else
+ value = Rn - imm32;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, value - Rn);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ value))
+ return false;
+ }
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0;
+
+ context.type = eContextRegisterStore;
+ // for r = 0 to regs-1
+ for (uint32_t r = 0; r < regs; ++r) {
+
+ if (single_regs) {
+ // MemA[address,4] = S[d+r]; address = address+4;
+ uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF,
+ start_reg + d + r, 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, start_reg + d + r, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - Rn);
+ if (!MemAWrite(context, address, data, addr_byte_size))
+ return false;
+
+ address = address + 4;
+ } else {
+ // // Store as two word-aligned words in the correct order for current
+ // endianness. MemA[address,4] = if BigEndian() then D[d+r]<63:32> else
+ // D[d+r]<31:0>;
+ // MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else
+ // D[d+r]<63:32>;
+ uint64_t data = ReadRegisterUnsigned(eRegisterKindDWARF,
+ start_reg + d + r, 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, start_reg + d + r, data_reg);
+
+ if (GetByteOrder() == eByteOrderBig) {
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - Rn);
+ if (!MemAWrite(context, address, Bits64(data, 63, 32),
+ addr_byte_size))
+ return false;
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ (address + 4) - Rn);
+ if (!MemAWrite(context, address + 4, Bits64(data, 31, 0),
+ addr_byte_size))
+ return false;
+ } else {
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - Rn);
+ if (!MemAWrite(context, address, Bits64(data, 31, 0), addr_byte_size))
+ return false;
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ (address + 4) - Rn);
+ if (!MemAWrite(context, address + 4, Bits64(data, 63, 32),
+ addr_byte_size))
+ return false;
+ }
+ // address = address+8;
+ address = address + 8;
+ }
+ }
+ }
+ return true;
+}
+
+// A8.6.320
+// This instruction loads a single extension register from memory, using an
+// address from an ARM core register, with an optional offset.
+bool EmulateInstructionARM::EmulateVLDR(const uint32_t opcode,
+ ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n);
+ base = if n == 15 then Align(PC,4) else R[n];
+ address = if add then (base + imm32) else (base - imm32);
+ if single_reg then
+ S[d] = MemA[address,4];
+ else
+ word1 = MemA[address,4]; word2 = MemA[address+4,4];
+ // Combine the word-aligned words in the correct order for current
+ // endianness.
+ D[d] = if BigEndian() then word1:word2 else word2:word1;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ bool single_reg;
+ bool add;
+ uint32_t imm32;
+ uint32_t d;
+ uint32_t n;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ // single_reg = FALSE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00',
+ // 32);
+ single_reg = false;
+ add = BitIsSet(opcode, 23);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // d = UInt(D:Vd); n = UInt(Rn);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+
+ break;
+
+ case eEncodingT2:
+ case eEncodingA2:
+ // single_reg = TRUE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', 32);
+ single_reg = true;
+ add = BitIsSet(opcode, 23);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // d = UInt(Vd:D); n = UInt(Rn);
+ d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22);
+ n = Bits32(opcode, 19, 16);
+
+ break;
+
+ default:
+ return false;
+ }
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // base = if n == 15 then Align(PC,4) else R[n];
+ uint32_t base;
+ if (n == 15)
+ base = AlignPC(Rn);
+ else
+ base = Rn;
+
+ // address = if add then (base + imm32) else (base - imm32);
+ addr_t address;
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0;
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base);
+
+ if (single_reg) {
+ // S[d] = MemA[address,4];
+ uint32_t data = MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, start_reg + d,
+ data))
+ return false;
+ } else {
+ // word1 = MemA[address,4]; word2 = MemA[address+4,4];
+ uint32_t word1 = MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ context.SetRegisterPlusOffset(base_reg, (address + 4) - base);
+ uint32_t word2 =
+ MemARead(context, address + 4, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ // // Combine the word-aligned words in the correct order for current
+ // endianness.
+ // D[d] = if BigEndian() then word1:word2 else word2:word1;
+ uint64_t data64;
+ if (GetByteOrder() == eByteOrderBig) {
+ data64 = word1;
+ data64 = (data64 << 32) | word2;
+ } else {
+ data64 = word2;
+ data64 = (data64 << 32) | word1;
+ }
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, start_reg + d,
+ data64))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.400 VSTR
+// This instruction stores a signle extension register to memory, using an
+// address from an ARM core register, with an optional offset.
+bool EmulateInstructionARM::EmulateVSTR(const uint32_t opcode,
+ ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n);
+ address = if add then (R[n] + imm32) else (R[n] - imm32);
+ if single_reg then
+ MemA[address,4] = S[d];
+ else
+ // Store as two word-aligned words in the correct order for current
+ // endianness.
+ MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>;
+ MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ bool single_reg;
+ bool add;
+ uint32_t imm32;
+ uint32_t d;
+ uint32_t n;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ // single_reg = FALSE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00',
+ // 32);
+ single_reg = false;
+ add = BitIsSet(opcode, 23);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // d = UInt(D:Vd); n = UInt(Rn);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+
+ // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE;
+ if ((n == 15) && (CurrentInstrSet() != eModeARM))
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ case eEncodingA2:
+ // single_reg = TRUE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', 32);
+ single_reg = true;
+ add = BitIsSet(opcode, 23);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // d = UInt(Vd:D); n = UInt(Rn);
+ d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22);
+ n = Bits32(opcode, 19, 16);
+
+ // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE;
+ if ((n == 15) && (CurrentInstrSet() != eModeARM))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = if add then (R[n] + imm32) else (R[n] - imm32);
+ addr_t address;
+ if (add)
+ address = Rn + imm32;
+ else
+ address = Rn - imm32;
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, start_reg + d, data_reg);
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ if (single_reg) {
+ // MemA[address,4] = S[d];
+ uint32_t data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, start_reg + d, 0, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address, data, addr_byte_size))
+ return false;
+ } else {
+ // // Store as two word-aligned words in the correct order for current
+ // endianness.
+ // MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>;
+ // MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>;
+ uint64_t data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, start_reg + d, 0, &success);
+ if (!success)
+ return false;
+
+ if (GetByteOrder() == eByteOrderBig) {
+ if (!MemAWrite(context, address, Bits64(data, 63, 32), addr_byte_size))
+ return false;
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ (address + 4) - Rn);
+ if (!MemAWrite(context, address + 4, Bits64(data, 31, 0),
+ addr_byte_size))
+ return false;
+ } else {
+ if (!MemAWrite(context, address, Bits64(data, 31, 0), addr_byte_size))
+ return false;
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ (address + 4) - Rn);
+ if (!MemAWrite(context, address + 4, Bits64(data, 63, 32),
+ addr_byte_size))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// A8.6.307 VLDI1 (multiple single elements) This instruction loads elements
+// from memory into one, two, three or four registers, without de-interleaving.
+// Every element of each register is loaded.
+bool EmulateInstructionARM::EmulateVLD1Multiple(const uint32_t opcode,
+ ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n);
+ address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException();
+ if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs);
+ for r = 0 to regs-1
+ for e = 0 to elements-1
+ Elem[D[d+r],e,esize] = MemU[address,ebytes];
+ address = address + ebytes;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t regs;
+ uint32_t alignment;
+ uint32_t ebytes;
+ uint32_t esize;
+ uint32_t elements;
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool wback;
+ bool register_index;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1: {
+ // case type of
+ // when '0111'
+ // regs = 1; if align<1> == '1' then UNDEFINED;
+ // when '1010'
+ // regs = 2; if align == '11' then UNDEFINED;
+ // when '0110'
+ // regs = 3; if align<1> == '1' then UNDEFINED;
+ // when '0010'
+ // regs = 4;
+ // otherwise
+ // SEE 'Related encodings';
+ uint32_t type = Bits32(opcode, 11, 8);
+ uint32_t align = Bits32(opcode, 5, 4);
+ if (type == 7) // '0111'
+ {
+ regs = 1;
+ if (BitIsSet(align, 1))
+ return false;
+ } else if (type == 10) // '1010'
+ {
+ regs = 2;
+ if (align == 3)
+ return false;
+
+ } else if (type == 6) // '0110'
+ {
+ regs = 3;
+ if (BitIsSet(align, 1))
+ return false;
+ } else if (type == 2) // '0010'
+ {
+ regs = 4;
+ } else
+ return false;
+
+ // alignment = if align == '00' then 1 else 4 << UInt(align);
+ if (align == 0)
+ alignment = 1;
+ else
+ alignment = 4 << align;
+
+ // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes;
+ ebytes = 1 << Bits32(opcode, 7, 6);
+ esize = 8 * ebytes;
+ elements = 8 / ebytes;
+
+ // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 15);
+ m = Bits32(opcode, 3, 0);
+
+ // wback = (m != 15); register_index = (m != 15 && m != 13);
+ wback = (m != 15);
+ register_index = ((m != 15) && (m != 13));
+
+ // if d+regs > 32 then UNPREDICTABLE;
+ if ((d + regs) > 32)
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = R[n]; if (address MOD alignment) != 0 then
+ // GenerateAlignmentException();
+ addr_t address = Rn;
+ if ((address % alignment) != 0)
+ return false;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs);
+ if (wback) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t offset;
+ if (register_index)
+ offset = Rm;
+ else
+ offset = 8 * regs;
+
+ uint32_t value = Rn + offset;
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ value))
+ return false;
+ }
+
+ // for r = 0 to regs-1
+ for (uint32_t r = 0; r < regs; ++r) {
+ // for e = 0 to elements-1
+ uint64_t assembled_data = 0;
+ for (uint32_t e = 0; e < elements; ++e) {
+ // Elem[D[d+r],e,esize] = MemU[address,ebytes];
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ uint64_t data = MemURead(context, address, ebytes, 0, &success);
+ if (!success)
+ return false;
+
+ assembled_data =
+ (data << (e * esize)) |
+ assembled_data; // New data goes to the left of existing data
+
+ // address = address + ebytes;
+ address = address + ebytes;
+ }
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_d0 + d + r,
+ assembled_data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.308 VLD1 (single element to one lane)
+//
+bool EmulateInstructionARM::EmulateVLD1Single(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n);
+ address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException();
+ if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ Elem[D[d],index,esize] = MemU[address,ebytes];
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t ebytes;
+ uint32_t esize;
+ uint32_t index;
+ uint32_t alignment;
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool wback;
+ bool register_index;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1: {
+ uint32_t size = Bits32(opcode, 11, 10);
+ uint32_t index_align = Bits32(opcode, 7, 4);
+ // if size == '11' then SEE VLD1 (single element to all lanes);
+ if (size == 3)
+ return EmulateVLD1SingleAll(opcode, encoding);
+ // case size of
+ if (size == 0) // when '00'
+ {
+ // if index_align<0> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 0))
+ return false;
+
+ // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1;
+ ebytes = 1;
+ esize = 8;
+ index = Bits32(index_align, 3, 1);
+ alignment = 1;
+ } else if (size == 1) // when '01'
+ {
+ // if index_align<1> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 1))
+ return false;
+
+ // ebytes = 2; esize = 16; index = UInt(index_align<3:2>);
+ ebytes = 2;
+ esize = 16;
+ index = Bits32(index_align, 3, 2);
+
+ // alignment = if index_align<0> == '0' then 1 else 2;
+ if (BitIsClear(index_align, 0))
+ alignment = 1;
+ else
+ alignment = 2;
+ } else if (size == 2) // when '10'
+ {
+ // if index_align<2> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 2))
+ return false;
+
+ // if index_align<1:0> != '00' && index_align<1:0> != '11' then
+ // UNDEFINED;
+ if ((Bits32(index_align, 1, 0) != 0) &&
+ (Bits32(index_align, 1, 0) != 3))
+ return false;
+
+ // ebytes = 4; esize = 32; index = UInt(index_align<3>);
+ ebytes = 4;
+ esize = 32;
+ index = Bit32(index_align, 3);
+
+ // alignment = if index_align<1:0> == '00' then 1 else 4;
+ if (Bits32(index_align, 1, 0) == 0)
+ alignment = 1;
+ else
+ alignment = 4;
+ } else {
+ return false;
+ }
+ // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15
+ // then UNPREDICTABLE;
+ wback = (m != 15);
+ register_index = ((m != 15) && (m != 13));
+
+ if (n == 15)
+ return false;
+
+ } break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = R[n]; if (address MOD alignment) != 0 then
+ // GenerateAlignmentException();
+ addr_t address = Rn;
+ if ((address % alignment) != 0)
+ return false;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ if (wback) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t offset;
+ if (register_index)
+ offset = Rm;
+ else
+ offset = ebytes;
+
+ uint32_t value = Rn + offset;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ value))
+ return false;
+ }
+
+ // Elem[D[d],index,esize] = MemU[address,ebytes];
+ uint32_t element = MemURead(context, address, esize, 0, &success);
+ if (!success)
+ return false;
+
+ element = element << (index * esize);
+
+ uint64_t reg_data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_d0 + d, 0, &success);
+ if (!success)
+ return false;
+
+ uint64_t all_ones = -1;
+ uint64_t mask = all_ones
+ << ((index + 1) * esize); // mask is all 1's to left of
+ // where 'element' goes, & all 0's
+ // at element & to the right of element.
+ if (index > 0)
+ mask = mask | Bits64(all_ones, (index * esize) - 1,
+ 0); // add 1's to the right of where 'element' goes.
+ // now mask should be 0's where element goes & 1's everywhere else.
+
+ uint64_t masked_reg =
+ reg_data & mask; // Take original reg value & zero out 'element' bits
+ reg_data =
+ masked_reg & element; // Put 'element' into those bits in reg_data.
+
+ context.type = eContextRegisterLoad;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ reg_data))
+ return false;
+ }
+ return true;
+}
+
+// A8.6.391 VST1 (multiple single elements) Vector Store (multiple single
+// elements) stores elements to memory from one, two, three, or four registers,
+// without interleaving. Every element of each register is stored.
+bool EmulateInstructionARM::EmulateVST1Multiple(const uint32_t opcode,
+ ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n);
+ address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException();
+ if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs);
+ for r = 0 to regs-1
+ for e = 0 to elements-1
+ MemU[address,ebytes] = Elem[D[d+r],e,esize];
+ address = address + ebytes;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t regs;
+ uint32_t alignment;
+ uint32_t ebytes;
+ uint32_t esize;
+ uint32_t elements;
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool wback;
+ bool register_index;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1: {
+ uint32_t type = Bits32(opcode, 11, 8);
+ uint32_t align = Bits32(opcode, 5, 4);
+
+ // case type of
+ if (type == 7) // when '0111'
+ {
+ // regs = 1; if align<1> == '1' then UNDEFINED;
+ regs = 1;
+ if (BitIsSet(align, 1))
+ return false;
+ } else if (type == 10) // when '1010'
+ {
+ // regs = 2; if align == '11' then UNDEFINED;
+ regs = 2;
+ if (align == 3)
+ return false;
+ } else if (type == 6) // when '0110'
+ {
+ // regs = 3; if align<1> == '1' then UNDEFINED;
+ regs = 3;
+ if (BitIsSet(align, 1))
+ return false;
+ } else if (type == 2) // when '0010'
+ // regs = 4;
+ regs = 4;
+ else // otherwise
+ // SEE 'Related encodings';
+ return false;
+
+ // alignment = if align == '00' then 1 else 4 << UInt(align);
+ if (align == 0)
+ alignment = 1;
+ else
+ alignment = 4 << align;
+
+ // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes;
+ ebytes = 1 << Bits32(opcode, 7, 6);
+ esize = 8 * ebytes;
+ elements = 8 / ebytes;
+
+ // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // wback = (m != 15); register_index = (m != 15 && m != 13);
+ wback = (m != 15);
+ register_index = ((m != 15) && (m != 13));
+
+ // if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE;
+ if ((d + regs) > 32)
+ return false;
+
+ if (n == 15)
+ return false;
+
+ } break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = R[n]; if (address MOD alignment) != 0 then
+ // GenerateAlignmentException();
+ addr_t address = Rn;
+ if ((address % alignment) != 0)
+ return false;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs);
+ if (wback) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t offset;
+ if (register_index)
+ offset = Rm;
+ else
+ offset = 8 * regs;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ Rn + offset))
+ return false;
+ }
+
+ RegisterInfo data_reg;
+ context.type = eContextRegisterStore;
+ // for r = 0 to regs-1
+ for (uint32_t r = 0; r < regs; ++r) {
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_d0 + d + r, data_reg);
+ uint64_t register_data = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_d0 + d + r, 0, &success);
+ if (!success)
+ return false;
+
+ // for e = 0 to elements-1
+ for (uint32_t e = 0; e < elements; ++e) {
+ // MemU[address,ebytes] = Elem[D[d+r],e,esize];
+ uint64_t word = Bits64(register_data, ((e + 1) * esize) - 1, e * esize);
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - Rn);
+ if (!MemUWrite(context, address, word, ebytes))
+ return false;
+
+ // address = address + ebytes;
+ address = address + ebytes;
+ }
+ }
+ }
+ return true;
+}
+
+// A8.6.392 VST1 (single element from one lane) This instruction stores one
+// element to memory from one element of a register.
+bool EmulateInstructionARM::EmulateVST1Single(const uint32_t opcode,
+ ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n);
+ address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException();
+ if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ MemU[address,ebytes] = Elem[D[d],index,esize];
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t ebytes;
+ uint32_t esize;
+ uint32_t index;
+ uint32_t alignment;
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool wback;
+ bool register_index;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1: {
+ uint32_t size = Bits32(opcode, 11, 10);
+ uint32_t index_align = Bits32(opcode, 7, 4);
+
+ // if size == '11' then UNDEFINED;
+ if (size == 3)
+ return false;
+
+ // case size of
+ if (size == 0) // when '00'
+ {
+ // if index_align<0> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 0))
+ return false;
+ // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1;
+ ebytes = 1;
+ esize = 8;
+ index = Bits32(index_align, 3, 1);
+ alignment = 1;
+ } else if (size == 1) // when '01'
+ {
+ // if index_align<1> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 1))
+ return false;
+
+ // ebytes = 2; esize = 16; index = UInt(index_align<3:2>);
+ ebytes = 2;
+ esize = 16;
+ index = Bits32(index_align, 3, 2);
+
+ // alignment = if index_align<0> == '0' then 1 else 2;
+ if (BitIsClear(index_align, 0))
+ alignment = 1;
+ else
+ alignment = 2;
+ } else if (size == 2) // when '10'
+ {
+ // if index_align<2> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 2))
+ return false;
+
+ // if index_align<1:0> != '00' && index_align<1:0> != '11' then
+ // UNDEFINED;
+ if ((Bits32(index_align, 1, 0) != 0) &&
+ (Bits32(index_align, 1, 0) != 3))
+ return false;
+
+ // ebytes = 4; esize = 32; index = UInt(index_align<3>);
+ ebytes = 4;
+ esize = 32;
+ index = Bit32(index_align, 3);
+
+ // alignment = if index_align<1:0> == '00' then 1 else 4;
+ if (Bits32(index_align, 1, 0) == 0)
+ alignment = 1;
+ else
+ alignment = 4;
+ } else {
+ return false;
+ }
+ // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15
+ // then UNPREDICTABLE;
+ wback = (m != 15);
+ register_index = ((m != 15) && (m != 13));
+
+ if (n == 15)
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = R[n]; if (address MOD alignment) != 0 then
+ // GenerateAlignmentException();
+ addr_t address = Rn;
+ if ((address % alignment) != 0)
+ return false;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ if (wback) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t offset;
+ if (register_index)
+ offset = Rm;
+ else
+ offset = ebytes;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ Rn + offset))
+ return false;
+ }
+
+ // MemU[address,ebytes] = Elem[D[d],index,esize];
+ uint64_t register_data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_d0 + d, 0, &success);
+ if (!success)
+ return false;
+
+ uint64_t word =
+ Bits64(register_data, ((index + 1) * esize) - 1, index * esize);
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_d0 + d, data_reg);
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ if (!MemUWrite(context, address, word, ebytes))
+ return false;
+ }
+ return true;
+}
+
+// A8.6.309 VLD1 (single element to all lanes) This instruction loads one
+// element from memory into every element of one or two vectors.
+bool EmulateInstructionARM::EmulateVLD1SingleAll(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n);
+ address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException();
+ if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ replicated_element = Replicate(MemU[address,ebytes], elements);
+ for r = 0 to regs-1
+ D[d+r] = replicated_element;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t ebytes;
+ uint32_t elements;
+ uint32_t regs;
+ uint32_t alignment;
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool wback;
+ bool register_index;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1: {
+ // if size == '11' || (size == '00' && a == '1') then UNDEFINED;
+ uint32_t size = Bits32(opcode, 7, 6);
+ if ((size == 3) || ((size == 0) && BitIsSet(opcode, 4)))
+ return false;
+
+ // ebytes = 1 << UInt(size); elements = 8 DIV ebytes; regs = if T == '0'
+ // then 1 else 2;
+ ebytes = 1 << size;
+ elements = 8 / ebytes;
+ if (BitIsClear(opcode, 5))
+ regs = 1;
+ else
+ regs = 2;
+
+ // alignment = if a == '0' then 1 else ebytes;
+ if (BitIsClear(opcode, 4))
+ alignment = 1;
+ else
+ alignment = ebytes;
+
+ // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // wback = (m != 15); register_index = (m != 15 && m != 13);
+ wback = (m != 15);
+ register_index = ((m != 15) && (m != 13));
+
+ // if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE;
+ if ((d + regs) > 32)
+ return false;
+
+ if (n == 15)
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = R[n]; if (address MOD alignment) != 0 then
+ // GenerateAlignmentException();
+ addr_t address = Rn;
+ if ((address % alignment) != 0)
+ return false;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ if (wback) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t offset;
+ if (register_index)
+ offset = Rm;
+ else
+ offset = ebytes;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ Rn + offset))
+ return false;
+ }
+
+ // replicated_element = Replicate(MemU[address,ebytes], elements);
+
+ context.type = eContextRegisterLoad;
+ uint64_t word = MemURead(context, address, ebytes, 0, &success);
+ if (!success)
+ return false;
+
+ uint64_t replicated_element = 0;
+ uint32_t esize = ebytes * 8;
+ for (uint32_t e = 0; e < elements; ++e)
+ replicated_element =
+ (replicated_element << esize) | Bits64(word, esize - 1, 0);
+
+ // for r = 0 to regs-1
+ for (uint32_t r = 0; r < regs; ++r) {
+ // D[d+r] = replicated_element;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_d0 + d + r,
+ replicated_element))
+ return false;
+ }
+ }
+ return true;
+}
+
+// B6.2.13 SUBS PC, LR and related instructions The SUBS PC, LR, #<const?
+// instruction provides an exception return without the use of the stack. It
+// subtracts the immediate constant from the LR, branches to the resulting
+// address, and also copies the SPSR to the CPSR.
+bool EmulateInstructionARM::EmulateSUBSPcLrEtc(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ if CurrentInstrSet() == InstrSet_ThumbEE then
+ UNPREDICTABLE;
+ operand2 = if register_form then Shift(R[m], shift_t, shift_n, APSR.C) else imm32;
+ case opcode of
+ when '0000' result = R[n] AND operand2; // AND
+ when '0001' result = R[n] EOR operand2; // EOR
+ when '0010' (result, -, -) = AddWithCarry(R[n], NOT(operand2), '1'); // SUB
+ when '0011' (result, -, -) = AddWithCarry(NOT(R[n]), operand2, '1'); // RSB
+ when '0100' (result, -, -) = AddWithCarry(R[n], operand2, '0'); // ADD
+ when '0101' (result, -, -) = AddWithCarry(R[n], operand2, APSR.c); // ADC
+ when '0110' (result, -, -) = AddWithCarry(R[n], NOT(operand2), APSR.C); // SBC
+ when '0111' (result, -, -) = AddWithCarry(NOT(R[n]), operand2, APSR.C); // RSC
+ when '1100' result = R[n] OR operand2; // ORR
+ when '1101' result = operand2; // MOV
+ when '1110' result = R[n] AND NOT(operand2); // BIC
+ when '1111' result = NOT(operand2); // MVN
+ CPSRWriteByInstr(SPSR[], '1111', TRUE);
+ BranchWritePC(result);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t m;
+ uint32_t imm32;
+ bool register_form;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+ uint32_t code;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then UNPREDICTABLE n = 14;
+ // imm32 = ZeroExtend(imm8, 32); register_form = FALSE; opcode = '0010';
+ // // = SUB
+ n = 14;
+ imm32 = Bits32(opcode, 7, 0);
+ register_form = false;
+ code = 2;
+
+ // if InITBlock() && !LastInITBlock() then UNPREDICTABLE;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // n = UInt(Rn); imm32 = ARMExpandImm(imm12); register_form = FALSE;
+ n = Bits32(opcode, 19, 16);
+ imm32 = ARMExpandImm(opcode);
+ register_form = false;
+ code = Bits32(opcode, 24, 21);
+
+ break;
+
+ case eEncodingA2:
+ // n = UInt(Rn); m = UInt(Rm); register_form = TRUE;
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ register_form = true;
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // operand2 = if register_form then Shift(R[m], shift_t, shift_n, APSR.C)
+ // else imm32;
+ uint32_t operand2;
+ if (register_form) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ operand2 = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ } else {
+ operand2 = imm32;
+ }
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult result;
+
+ // case opcode of
+ switch (code) {
+ case 0: // when '0000'
+ // result = R[n] AND operand2; // AND
+ result.result = Rn & operand2;
+ break;
+
+ case 1: // when '0001'
+ // result = R[n] EOR operand2; // EOR
+ result.result = Rn ^ operand2;
+ break;
+
+ case 2: // when '0010'
+ // (result, -, -) = AddWithCarry(R[n], NOT(operand2), '1'); // SUB
+ result = AddWithCarry(Rn, ~(operand2), 1);
+ break;
+
+ case 3: // when '0011'
+ // (result, -, -) = AddWithCarry(NOT(R[n]), operand2, '1'); // RSB
+ result = AddWithCarry(~(Rn), operand2, 1);
+ break;
+
+ case 4: // when '0100'
+ // (result, -, -) = AddWithCarry(R[n], operand2, '0'); // ADD
+ result = AddWithCarry(Rn, operand2, 0);
+ break;
+
+ case 5: // when '0101'
+ // (result, -, -) = AddWithCarry(R[n], operand2, APSR.c); // ADC
+ result = AddWithCarry(Rn, operand2, APSR_C);
+ break;
+
+ case 6: // when '0110'
+ // (result, -, -) = AddWithCarry(R[n], NOT(operand2), APSR.C); // SBC
+ result = AddWithCarry(Rn, ~(operand2), APSR_C);
+ break;
+
+ case 7: // when '0111'
+ // (result, -, -) = AddWithCarry(NOT(R[n]), operand2, APSR.C); // RSC
+ result = AddWithCarry(~(Rn), operand2, APSR_C);
+ break;
+
+ case 10: // when '1100'
+ // result = R[n] OR operand2; // ORR
+ result.result = Rn | operand2;
+ break;
+
+ case 11: // when '1101'
+ // result = operand2; // MOV
+ result.result = operand2;
+ break;
+
+ case 12: // when '1110'
+ // result = R[n] AND NOT(operand2); // BIC
+ result.result = Rn & ~(operand2);
+ break;
+
+ case 15: // when '1111'
+ // result = NOT(operand2); // MVN
+ result.result = ~(operand2);
+ break;
+
+ default:
+ return false;
+ }
+ // CPSRWriteByInstr(SPSR[], '1111', TRUE);
+
+ // For now, in emulation mode, we don't have access to the SPSR, so we will
+ // use the CPSR instead, and hope for the best.
+ uint32_t spsr =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_cpsr, 0, &success);
+ if (!success)
+ return false;
+
+ CPSRWriteByInstr(spsr, 15, true);
+
+ // BranchWritePC(result);
+ EmulateInstruction::Context context;
+ context.type = eContextAdjustPC;
+ context.SetImmediate(result.result);
+
+ BranchWritePC(context, result.result);
+ }
+ return true;
+}
+
+EmulateInstructionARM::ARMOpcode *
+EmulateInstructionARM::GetARMOpcodeForInstruction(const uint32_t opcode,
+ uint32_t arm_isa) {
+ static ARMOpcode g_arm_opcodes[] = {
+ // Prologue instructions
+
+ // push register(s)
+ {0x0fff0000, 0x092d0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePUSH, "push <registers>"},
+ {0x0fff0fff, 0x052d0004, ARMvAll, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePUSH, "push <register>"},
+
+ // set r7 to point to a stack offset
+ {0x0ffff000, 0x028d7000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #<const>"},
+ {0x0ffff000, 0x024c7000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBR7IPImm, "sub r7, ip, #<const>"},
+ // copy the stack pointer to ip
+ {0x0fffffff, 0x01a0c00d, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdSP, "mov ip, sp"},
+ {0x0ffff000, 0x028dc000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDRdSPImm, "add ip, sp, #<const>"},
+ {0x0ffff000, 0x024dc000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBIPSPImm, "sub ip, sp, #<const>"},
+
+ // adjust the stack pointer
+ {0x0ffff000, 0x024dd000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #<const>"},
+ {0x0fef0010, 0x004d0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPReg,
+ "sub{s}<c> <Rd>, sp, <Rm>{,<shift>}"},
+
+ // push one register
+ // if Rn == '1101' && imm12 == '000000000100' then SEE PUSH;
+ {0x0e5f0000, 0x040d0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRRtSP, "str Rt, [sp, #-imm12]!"},
+
+ // vector push consecutive extension register(s)
+ {0x0fbf0f00, 0x0d2d0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPUSH, "vpush.64 <list>"},
+ {0x0fbf0f00, 0x0d2d0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPUSH, "vpush.32 <list>"},
+
+ // Epilogue instructions
+
+ {0x0fff0000, 0x08bd0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePOP, "pop <registers>"},
+ {0x0fff0fff, 0x049d0004, ARMvAll, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePOP, "pop <register>"},
+ {0x0fbf0f00, 0x0cbd0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPOP, "vpop.64 <list>"},
+ {0x0fbf0f00, 0x0cbd0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPOP, "vpop.32 <list>"},
+
+ // Supervisor Call (previously Software Interrupt)
+ {0x0f000000, 0x0f000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSVC, "svc #imm24"},
+
+ // Branch instructions
+ // To resolve ambiguity, "blx <label>" should come before "b #imm24" and
+ // "bl <label>".
+ {0xfe000000, 0xfa000000, ARMV5_ABOVE, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBLXImmediate, "blx <label>"},
+ {0x0f000000, 0x0a000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateB, "b #imm24"},
+ {0x0f000000, 0x0b000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBLXImmediate, "bl <label>"},
+ {0x0ffffff0, 0x012fff30, ARMV5_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBLXRm, "blx <Rm>"},
+ // for example, "bx lr"
+ {0x0ffffff0, 0x012fff10, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBXRm, "bx <Rm>"},
+ // bxj
+ {0x0ffffff0, 0x012fff20, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBXJRm, "bxj <Rm>"},
+
+ // Data-processing instructions
+ // adc (immediate)
+ {0x0fe00000, 0x02a00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADCImm, "adc{s}<c> <Rd>, <Rn>, #const"},
+ // adc (register)
+ {0x0fe00010, 0x00a00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADCReg,
+ "adc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // add (immediate)
+ {0x0fe00000, 0x02800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDImmARM,
+ "add{s}<c> <Rd>, <Rn>, #const"},
+ // add (register)
+ {0x0fe00010, 0x00800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDReg,
+ "add{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // add (register-shifted register)
+ {0x0fe00090, 0x00800010, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDRegShift,
+ "add{s}<c> <Rd>, <Rn>, <Rm>, <type> <RS>"},
+ // adr
+ {0x0fff0000, 0x028f0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"},
+ {0x0fff0000, 0x024f0000, ARMvAll, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADR, "sub<c> <Rd>, PC, #<const>"},
+ // and (immediate)
+ {0x0fe00000, 0x02000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateANDImm, "and{s}<c> <Rd>, <Rn>, #const"},
+ // and (register)
+ {0x0fe00010, 0x00000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateANDReg,
+ "and{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // bic (immediate)
+ {0x0fe00000, 0x03c00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBICImm, "bic{s}<c> <Rd>, <Rn>, #const"},
+ // bic (register)
+ {0x0fe00010, 0x01c00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBICReg,
+ "bic{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // eor (immediate)
+ {0x0fe00000, 0x02200000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateEORImm, "eor{s}<c> <Rd>, <Rn>, #const"},
+ // eor (register)
+ {0x0fe00010, 0x00200000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateEORReg,
+ "eor{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // orr (immediate)
+ {0x0fe00000, 0x03800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateORRImm, "orr{s}<c> <Rd>, <Rn>, #const"},
+ // orr (register)
+ {0x0fe00010, 0x01800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateORRReg,
+ "orr{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // rsb (immediate)
+ {0x0fe00000, 0x02600000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSBImm, "rsb{s}<c> <Rd>, <Rn>, #<const>"},
+ // rsb (register)
+ {0x0fe00010, 0x00600000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSBReg,
+ "rsb{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // rsc (immediate)
+ {0x0fe00000, 0x02e00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSCImm, "rsc{s}<c> <Rd>, <Rn>, #<const>"},
+ // rsc (register)
+ {0x0fe00010, 0x00e00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSCReg,
+ "rsc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // sbc (immediate)
+ {0x0fe00000, 0x02c00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSBCImm, "sbc{s}<c> <Rd>, <Rn>, #<const>"},
+ // sbc (register)
+ {0x0fe00010, 0x00c00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSBCReg,
+ "sbc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // sub (immediate, ARM)
+ {0x0fe00000, 0x02400000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBImmARM,
+ "sub{s}<c> <Rd>, <Rn>, #<const>"},
+ // sub (sp minus immediate)
+ {0x0fef0000, 0x024d0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "sub{s}<c> <Rd>, sp, #<const>"},
+ // sub (register)
+ {0x0fe00010, 0x00400000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBReg,
+ "sub{s}<c> <Rd>, <Rn>, <Rm>{,<shift>}"},
+ // teq (immediate)
+ {0x0ff0f000, 0x03300000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTEQImm, "teq<c> <Rn>, #const"},
+ // teq (register)
+ {0x0ff0f010, 0x01300000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTEQReg, "teq<c> <Rn>, <Rm> {,<shift>}"},
+ // tst (immediate)
+ {0x0ff0f000, 0x03100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTSTImm, "tst<c> <Rn>, #const"},
+ // tst (register)
+ {0x0ff0f010, 0x01100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTSTReg, "tst<c> <Rn>, <Rm> {,<shift>}"},
+
+ // mov (immediate)
+ {0x0fef0000, 0x03a00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdImm, "mov{s}<c> <Rd>, #<const>"},
+ {0x0ff00000, 0x03000000, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdImm, "movw<c> <Rd>, #<imm16>"},
+ // mov (register)
+ {0x0fef0ff0, 0x01a00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdRm, "mov{s}<c> <Rd>, <Rm>"},
+ // mvn (immediate)
+ {0x0fef0000, 0x03e00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMVNImm, "mvn{s}<c> <Rd>, #<const>"},
+ // mvn (register)
+ {0x0fef0010, 0x01e00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMVNReg,
+ "mvn{s}<c> <Rd>, <Rm> {,<shift>}"},
+ // cmn (immediate)
+ {0x0ff0f000, 0x03700000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMNImm, "cmn<c> <Rn>, #<const>"},
+ // cmn (register)
+ {0x0ff0f010, 0x01700000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm> {,<shift>}"},
+ // cmp (immediate)
+ {0x0ff0f000, 0x03500000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMPImm, "cmp<c> <Rn>, #<const>"},
+ // cmp (register)
+ {0x0ff0f010, 0x01500000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm> {,<shift>}"},
+ // asr (immediate)
+ {0x0fef0070, 0x01a00040, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateASRImm, "asr{s}<c> <Rd>, <Rm>, #imm"},
+ // asr (register)
+ {0x0fef00f0, 0x01a00050, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateASRReg, "asr{s}<c> <Rd>, <Rn>, <Rm>"},
+ // lsl (immediate)
+ {0x0fef0070, 0x01a00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSLImm, "lsl{s}<c> <Rd>, <Rm>, #imm"},
+ // lsl (register)
+ {0x0fef00f0, 0x01a00010, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSLReg, "lsl{s}<c> <Rd>, <Rn>, <Rm>"},
+ // lsr (immediate)
+ {0x0fef0070, 0x01a00020, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSRImm, "lsr{s}<c> <Rd>, <Rm>, #imm"},
+ // lsr (register)
+ {0x0fef00f0, 0x01a00050, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSRReg, "lsr{s}<c> <Rd>, <Rn>, <Rm>"},
+ // rrx is a special case encoding of ror (immediate)
+ {0x0fef0ff0, 0x01a00060, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRRX, "rrx{s}<c> <Rd>, <Rm>"},
+ // ror (immediate)
+ {0x0fef0070, 0x01a00060, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRORImm, "ror{s}<c> <Rd>, <Rm>, #imm"},
+ // ror (register)
+ {0x0fef00f0, 0x01a00070, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRORReg, "ror{s}<c> <Rd>, <Rn>, <Rm>"},
+ // mul
+ {0x0fe000f0, 0x00000090, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMUL, "mul{s}<c> <Rd>,<R>,<Rm>"},
+
+ // subs pc, lr and related instructions
+ {0x0e10f000, 0x0210f000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPcLrEtc,
+ "<opc>S<c> PC,#<const> | <Rn>,#<const>"},
+ {0x0e10f010, 0x0010f000, ARMvAll, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPcLrEtc,
+ "<opc>S<c> PC,<Rn>,<Rm{,<shift>}"},
+
+ // Load instructions
+ {0x0fd00000, 0x08900000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDM, "ldm<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x08100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDMDA, "ldmda<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x09100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDMDB, "ldmdb<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x09900000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDMIB, "ldmib<c> <Rn<{!} <registers>"},
+ {0x0e500000, 0x04100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRImmediateARM,
+ "ldr<c> <Rt> [<Rn> {#+/-<imm12>}]"},
+ {0x0e500010, 0x06100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRRegister,
+ "ldr<c> <Rt> [<Rn> +/-<Rm> {<shift>}] {!}"},
+ {0x0e5f0000, 0x045f0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBLiteral, "ldrb<c> <Rt>, [...]"},
+ {0xfe500010, 0x06500000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBRegister,
+ "ldrb<c> <Rt>, [<Rn>,+/-<Rm>{, <shift>}]{!}"},
+ {0x0e5f00f0, 0x005f00b0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHLiteral, "ldrh<c> <Rt>, <label>"},
+ {0x0e5000f0, 0x001000b0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHRegister,
+ "ldrh<c> <Rt>,[<Rn>,+/-<Rm>]{!}"},
+ {0x0e5000f0, 0x005000d0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBImmediate,
+ "ldrsb<c> <Rt>, [<Rn>{,#+/-<imm8>}]"},
+ {0x0e5f00f0, 0x005f00d0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBLiteral, "ldrsb<c> <Rt> <label>"},
+ {0x0e5000f0, 0x001000d0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBRegister,
+ "ldrsb<c> <Rt>,[<Rn>,+/-<Rm>]{!}"},
+ {0x0e5000f0, 0x005000f0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHImmediate,
+ "ldrsh<c> <Rt>,[<Rn>{,#+/-<imm8>}]"},
+ {0x0e5f00f0, 0x005f00f0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHLiteral, "ldrsh<c> <Rt>,<label>"},
+ {0x0e5000f0, 0x001000f0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHRegister,
+ "ldrsh<c> <Rt>,[<Rn>,+/-<Rm>]{!}"},
+ {0x0e5000f0, 0x004000d0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRDImmediate,
+ "ldrd<c> <Rt>, <Rt2>, [<Rn>,#+/-<imm8>]!"},
+ {0x0e500ff0, 0x000000d0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRDRegister,
+ "ldrd<c> <Rt>, <Rt2>, [<Rn>, +/-<Rm>]{!}"},
+ {0x0e100f00, 0x0c100b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"},
+ {0x0e100f00, 0x0c100a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"},
+ {0x0f300f00, 0x0d100b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Dd>, [<Rn>{,#+/-<imm>}]"},
+ {0x0f300f00, 0x0d100a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Sd>, [<Rn>{,#+/-<imm>}]"},
+ {0xffb00000, 0xf4200000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1Multiple,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+ {0xffb00300, 0xf4a00000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1Single,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+ {0xffb00f00, 0xf4a00c00, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1SingleAll,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+
+ // Store instructions
+ {0x0fd00000, 0x08800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTM, "stm<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x08000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTMDA, "stmda<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x09000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTMDB, "stmdb<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x09800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTMIB, "stmib<c> <Rn>{!} <registers>"},
+ {0x0e500010, 0x06000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRRegister,
+ "str<c> <Rt> [<Rn> +/-<Rm> {<shift>}]{!}"},
+ {0x0e5000f0, 0x000000b0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRHRegister,
+ "strh<c> <Rt>,[<Rn>,+/-<Rm>[{!}"},
+ {0x0ff00ff0, 0x01800f90, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTREX, "strex<c> <Rd>, <Rt>, [<Rn>]"},
+ {0x0e500000, 0x04400000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRBImmARM,
+ "strb<c> <Rt>,[<Rn>,#+/-<imm12>]!"},
+ {0x0e500000, 0x04000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRImmARM,
+ "str<c> <Rt>,[<Rn>,#+/-<imm12>]!"},
+ {0x0e5000f0, 0x004000f0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRDImm,
+ "strd<c> <Rt>, <Rt2>, [<Rn> #+/-<imm8>]!"},
+ {0x0e500ff0, 0x000000f0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRDReg,
+ "strd<c> <Rt>, <Rt2>, [<Rn>, +/-<Rm>]{!}"},
+ {0x0e100f00, 0x0c000b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!} <list>"},
+ {0x0e100f00, 0x0c000a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!} <list>"},
+ {0x0f300f00, 0x0d000b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Dd> [<Rn>{,#+/-<imm>}]"},
+ {0x0f300f00, 0x0d000a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Sd> [<Rn>{,#+/-<imm>}]"},
+ {0xffb00000, 0xf4000000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVST1Multiple,
+ "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+ {0xffb00300, 0xf4800000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVST1Single,
+ "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+
+ // Other instructions
+ {0x0fff00f0, 0x06af00f0, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSXTB, "sxtb<c> <Rd>,<Rm>{,<rotation>}"},
+ {0x0fff00f0, 0x06bf0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSXTH, "sxth<c> <Rd>,<Rm>{,<rotation>}"},
+ {0x0fff00f0, 0x06ef0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateUXTB, "uxtb<c> <Rd>,<Rm>{,<rotation>}"},
+ {0x0fff00f0, 0x06ff0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateUXTH, "uxth<c> <Rd>,<Rm>{,<rotation>}"},
+ {0xfe500000, 0xf8100000, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRFE, "rfe{<amode>} <Rn>{!}"}
+
+ };
+ static const size_t k_num_arm_opcodes = llvm::array_lengthof(g_arm_opcodes);
+
+ for (size_t i = 0; i < k_num_arm_opcodes; ++i) {
+ if ((g_arm_opcodes[i].mask & opcode) == g_arm_opcodes[i].value &&
+ (g_arm_opcodes[i].variants & arm_isa) != 0)
+ return &g_arm_opcodes[i];
+ }
+ return nullptr;
+}
+
+EmulateInstructionARM::ARMOpcode *
+EmulateInstructionARM::GetThumbOpcodeForInstruction(const uint32_t opcode,
+ uint32_t arm_isa) {
+
+ static ARMOpcode g_thumb_opcodes[] = {
+ // Prologue instructions
+
+ // push register(s)
+ {0xfffffe00, 0x0000b400, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulatePUSH, "push <registers>"},
+ {0xffff0000, 0xe92d0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePUSH, "push.w <registers>"},
+ {0xffff0fff, 0xf84d0d04, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePUSH, "push.w <register>"},
+
+ // set r7 to point to a stack offset
+ {0xffffff00, 0x0000af00, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #imm"},
+ // copy the stack pointer to r7
+ {0xffffffff, 0x0000466f, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMOVRdSP, "mov r7, sp"},
+ // move from high register to low register (comes after "mov r7, sp" to
+ // resolve ambiguity)
+ {0xffffffc0, 0x00004640, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMOVLowHigh, "mov r0-r7, r8-r15"},
+
+ // PC-relative load into register (see also EmulateADDSPRm)
+ {0xfffff800, 0x00004800, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRRtPCRelative, "ldr <Rt>, [PC, #imm]"},
+
+ // adjust the stack pointer
+ {0xffffff87, 0x00004485, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDSPRm, "add sp, <Rm>"},
+ {0xffffff80, 0x0000b080, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #imm"},
+ {0xfbef8f00, 0xf1ad0d00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "sub.w sp, sp, #<const>"},
+ {0xfbff8f00, 0xf2ad0d00, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "subw sp, sp, #imm12"},
+ {0xffef8000, 0xebad0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPReg,
+ "sub{s}<c> <Rd>, sp, <Rm>{,<shift>}"},
+
+ // vector push consecutive extension register(s)
+ {0xffbf0f00, 0xed2d0b00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPUSH, "vpush.64 <list>"},
+ {0xffbf0f00, 0xed2d0a00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPUSH, "vpush.32 <list>"},
+
+ // Epilogue instructions
+
+ {0xfffff800, 0x0000a800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDSPImm, "add<c> <Rd>, sp, #imm"},
+ {0xffffff80, 0x0000b000, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDSPImm, "add sp, #imm"},
+ {0xfffffe00, 0x0000bc00, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulatePOP, "pop <registers>"},
+ {0xffff0000, 0xe8bd0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePOP, "pop.w <registers>"},
+ {0xffff0fff, 0xf85d0d04, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePOP, "pop.w <register>"},
+ {0xffbf0f00, 0xecbd0b00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPOP, "vpop.64 <list>"},
+ {0xffbf0f00, 0xecbd0a00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPOP, "vpop.32 <list>"},
+
+ // Supervisor Call (previously Software Interrupt)
+ {0xffffff00, 0x0000df00, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSVC, "svc #imm8"},
+
+ // If Then makes up to four following instructions conditional.
+ // The next 5 opcode _must_ come before the if then instruction
+ {0xffffffff, 0x0000bf00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateNop, "nop"},
+ {0xffffffff, 0x0000bf10, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateNop, "nop YIELD (yield hint)"},
+ {0xffffffff, 0x0000bf20, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateNop, "nop WFE (wait for event hint)"},
+ {0xffffffff, 0x0000bf30, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateNop, "nop WFI (wait for interrupt hint)"},
+ {0xffffffff, 0x0000bf40, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateNop, "nop SEV (send event hint)"},
+ {0xffffff00, 0x0000bf00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateIT, "it{<x>{<y>{<z>}}} <firstcond>"},
+
+ // Branch instructions
+ // To resolve ambiguity, "b<c> #imm8" should come after "svc #imm8".
+ {0xfffff000, 0x0000d000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateB, "b<c> #imm8 (outside IT)"},
+ {0xfffff800, 0x0000e000, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateB, "b<c> #imm11 (outside or last in IT)"},
+ {0xf800d000, 0xf0008000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateB, "b<c>.w #imm8 (outside IT)"},
+ {0xf800d000, 0xf0009000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateB,
+ "b<c>.w #imm8 (outside or last in IT)"},
+ // J1 == J2 == 1
+ {0xf800d000, 0xf000d000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBLXImmediate, "bl <label>"},
+ // J1 == J2 == 1
+ {0xf800d001, 0xf000c000, ARMV5_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBLXImmediate, "blx <label>"},
+ {0xffffff87, 0x00004780, ARMV5_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateBLXRm, "blx <Rm>"},
+ // for example, "bx lr"
+ {0xffffff87, 0x00004700, ARMvAll, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBXRm, "bx <Rm>"},
+ // bxj
+ {0xfff0ffff, 0xf3c08f00, ARMV5J_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBXJRm, "bxj <Rm>"},
+ // compare and branch
+ {0xfffff500, 0x0000b100, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCB, "cb{n}z <Rn>, <label>"},
+ // table branch byte
+ {0xfff0fff0, 0xe8d0f000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTB, "tbb<c> <Rn>, <Rm>"},
+ // table branch halfword
+ {0xfff0fff0, 0xe8d0f010, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTB, "tbh<c> <Rn>, <Rm>, lsl #1"},
+
+ // Data-processing instructions
+ // adc (immediate)
+ {0xfbe08000, 0xf1400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADCImm, "adc{s}<c> <Rd>, <Rn>, #<const>"},
+ // adc (register)
+ {0xffffffc0, 0x00004140, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADCReg, "adcs|adc<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xeb400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADCReg,
+ "adc{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // add (register)
+ {0xfffffe00, 0x00001800, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDReg, "adds|add<c> <Rd>, <Rn>, <Rm>"},
+ // Make sure "add sp, <Rm>" comes before this instruction, so there's no
+ // ambiguity decoding the two.
+ {0xffffff00, 0x00004400, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDReg, "add<c> <Rdn>, <Rm>"},
+ // adr
+ {0xfffff800, 0x0000a000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"},
+ {0xfbff8000, 0xf2af0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADR, "sub<c> <Rd>, PC, #<const>"},
+ {0xfbff8000, 0xf20f0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"},
+ // and (immediate)
+ {0xfbe08000, 0xf0000000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateANDImm, "and{s}<c> <Rd>, <Rn>, #<const>"},
+ // and (register)
+ {0xffffffc0, 0x00004000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateANDReg, "ands|and<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xea000000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateANDReg,
+ "and{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // bic (immediate)
+ {0xfbe08000, 0xf0200000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBICImm, "bic{s}<c> <Rd>, <Rn>, #<const>"},
+ // bic (register)
+ {0xffffffc0, 0x00004380, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateBICReg, "bics|bic<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xea200000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBICReg,
+ "bic{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // eor (immediate)
+ {0xfbe08000, 0xf0800000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateEORImm, "eor{s}<c> <Rd>, <Rn>, #<const>"},
+ // eor (register)
+ {0xffffffc0, 0x00004040, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateEORReg, "eors|eor<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xea800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateEORReg,
+ "eor{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // orr (immediate)
+ {0xfbe08000, 0xf0400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateORRImm, "orr{s}<c> <Rd>, <Rn>, #<const>"},
+ // orr (register)
+ {0xffffffc0, 0x00004300, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateORRReg, "orrs|orr<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xea400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateORRReg,
+ "orr{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // rsb (immediate)
+ {0xffffffc0, 0x00004240, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateRSBImm, "rsbs|rsb<c> <Rd>, <Rn>, #0"},
+ {0xfbe08000, 0xf1c00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSBImm,
+ "rsb{s}<c>.w <Rd>, <Rn>, #<const>"},
+ // rsb (register)
+ {0xffe08000, 0xea400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSBReg,
+ "rsb{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // sbc (immediate)
+ {0xfbe08000, 0xf1600000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSBCImm, "sbc{s}<c> <Rd>, <Rn>, #<const>"},
+ // sbc (register)
+ {0xffffffc0, 0x00004180, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSBCReg, "sbcs|sbc<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xeb600000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSBCReg,
+ "sbc{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // add (immediate, Thumb)
+ {0xfffffe00, 0x00001c00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDImmThumb,
+ "adds|add<c> <Rd>,<Rn>,#<imm3>"},
+ {0xfffff800, 0x00003000, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDImmThumb, "adds|add<c> <Rdn>,#<imm8>"},
+ {0xfbe08000, 0xf1000000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDImmThumb,
+ "add{s}<c>.w <Rd>,<Rn>,#<const>"},
+ {0xfbf08000, 0xf2000000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDImmThumb,
+ "addw<c> <Rd>,<Rn>,#<imm12>"},
+ // sub (immediate, Thumb)
+ {0xfffffe00, 0x00001e00, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSUBImmThumb,
+ "subs|sub<c> <Rd>, <Rn> #imm3"},
+ {0xfffff800, 0x00003800, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSUBImmThumb, "subs|sub<c> <Rdn>, #imm8"},
+ {0xfbe08000, 0xf1a00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBImmThumb,
+ "sub{s}<c>.w <Rd>, <Rn>, #<const>"},
+ {0xfbf08000, 0xf2a00000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBImmThumb,
+ "subw<c> <Rd>, <Rn>, #imm12"},
+ // sub (sp minus immediate)
+ {0xfbef8000, 0xf1ad0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "sub{s}.w <Rd>, sp, #<const>"},
+ {0xfbff8000, 0xf2ad0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "subw<c> <Rd>, sp, #imm12"},
+ // sub (register)
+ {0xfffffe00, 0x00001a00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSUBReg, "subs|sub<c> <Rd>, <Rn>, <Rm>"},
+ {0xffe08000, 0xeba00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBReg,
+ "sub{s}<c>.w <Rd>, <Rn>, <Rm>{,<shift>}"},
+ // teq (immediate)
+ {0xfbf08f00, 0xf0900f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTEQImm, "teq<c> <Rn>, #<const>"},
+ // teq (register)
+ {0xfff08f00, 0xea900f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTEQReg, "teq<c> <Rn>, <Rm> {,<shift>}"},
+ // tst (immediate)
+ {0xfbf08f00, 0xf0100f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTSTImm, "tst<c> <Rn>, #<const>"},
+ // tst (register)
+ {0xffffffc0, 0x00004200, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateTSTReg, "tst<c> <Rdn>, <Rm>"},
+ {0xfff08f00, 0xea100f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTSTReg, "tst<c>.w <Rn>, <Rm> {,<shift>}"},
+
+ // move from high register to high register
+ {0xffffff00, 0x00004600, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMOVRdRm, "mov<c> <Rd>, <Rm>"},
+ // move from low register to low register
+ {0xffffffc0, 0x00000000, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMOVRdRm, "movs <Rd>, <Rm>"},
+ // mov{s}<c>.w <Rd>, <Rm>
+ {0xffeff0f0, 0xea4f0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdRm, "mov{s}<c>.w <Rd>, <Rm>"},
+ // move immediate
+ {0xfffff800, 0x00002000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMOVRdImm, "movs|mov<c> <Rd>, #imm8"},
+ {0xfbef8000, 0xf04f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdImm, "mov{s}<c>.w <Rd>, #<const>"},
+ {0xfbf08000, 0xf2400000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdImm, "movw<c> <Rd>,#<imm16>"},
+ // mvn (immediate)
+ {0xfbef8000, 0xf06f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMVNImm, "mvn{s} <Rd>, #<const>"},
+ // mvn (register)
+ {0xffffffc0, 0x000043c0, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMVNReg, "mvns|mvn<c> <Rd>, <Rm>"},
+ {0xffef8000, 0xea6f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMVNReg,
+ "mvn{s}<c>.w <Rd>, <Rm> {,<shift>}"},
+ // cmn (immediate)
+ {0xfbf08f00, 0xf1100f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMNImm, "cmn<c> <Rn>, #<const>"},
+ // cmn (register)
+ {0xffffffc0, 0x000042c0, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm>"},
+ {0xfff08f00, 0xeb100f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm> {,<shift>}"},
+ // cmp (immediate)
+ {0xfffff800, 0x00002800, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCMPImm, "cmp<c> <Rn>, #imm8"},
+ {0xfbf08f00, 0xf1b00f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMPImm, "cmp<c>.w <Rn>, #<const>"},
+ // cmp (register) (Rn and Rm both from r0-r7)
+ {0xffffffc0, 0x00004280, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm>"},
+ // cmp (register) (Rn and Rm not both from r0-r7)
+ {0xffffff00, 0x00004500, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm>"},
+ {0xfff08f00, 0xebb00f00, ARMvAll, eEncodingT3, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCMPReg,
+ "cmp<c>.w <Rn>, <Rm> {, <shift>}"},
+ // asr (immediate)
+ {0xfffff800, 0x00001000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateASRImm, "asrs|asr<c> <Rd>, <Rm>, #imm"},
+ {0xffef8030, 0xea4f0020, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateASRImm, "asr{s}<c>.w <Rd>, <Rm>, #imm"},
+ // asr (register)
+ {0xffffffc0, 0x00004100, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateASRReg, "asrs|asr<c> <Rdn>, <Rm>"},
+ {0xffe0f0f0, 0xfa40f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateASRReg, "asr{s}<c>.w <Rd>, <Rn>, <Rm>"},
+ // lsl (immediate)
+ {0xfffff800, 0x00000000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLSLImm, "lsls|lsl<c> <Rd>, <Rm>, #imm"},
+ {0xffef8030, 0xea4f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSLImm, "lsl{s}<c>.w <Rd>, <Rm>, #imm"},
+ // lsl (register)
+ {0xffffffc0, 0x00004080, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLSLReg, "lsls|lsl<c> <Rdn>, <Rm>"},
+ {0xffe0f0f0, 0xfa00f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSLReg, "lsl{s}<c>.w <Rd>, <Rn>, <Rm>"},
+ // lsr (immediate)
+ {0xfffff800, 0x00000800, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLSRImm, "lsrs|lsr<c> <Rd>, <Rm>, #imm"},
+ {0xffef8030, 0xea4f0010, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSRImm, "lsr{s}<c>.w <Rd>, <Rm>, #imm"},
+ // lsr (register)
+ {0xffffffc0, 0x000040c0, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLSRReg, "lsrs|lsr<c> <Rdn>, <Rm>"},
+ {0xffe0f0f0, 0xfa20f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSRReg, "lsr{s}<c>.w <Rd>, <Rn>, <Rm>"},
+ // rrx is a special case encoding of ror (immediate)
+ {0xffeff0f0, 0xea4f0030, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRRX, "rrx{s}<c>.w <Rd>, <Rm>"},
+ // ror (immediate)
+ {0xffef8030, 0xea4f0030, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRORImm, "ror{s}<c>.w <Rd>, <Rm>, #imm"},
+ // ror (register)
+ {0xffffffc0, 0x000041c0, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateRORReg, "rors|ror<c> <Rdn>, <Rm>"},
+ {0xffe0f0f0, 0xfa60f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRORReg, "ror{s}<c>.w <Rd>, <Rn>, <Rm>"},
+ // mul
+ {0xffffffc0, 0x00004340, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMUL, "muls <Rdm>,<Rn>,<Rdm>"},
+ // mul
+ {0xfff0f0f0, 0xfb00f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMUL, "mul<c> <Rd>,<Rn>,<Rm>"},
+
+ // subs pc, lr and related instructions
+ {0xffffff00, 0xf3de8f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPcLrEtc, "SUBS<c> PC, LR, #<imm8>"},
+
+ // RFE instructions *** IMPORTANT *** THESE MUST BE LISTED **BEFORE** THE
+ // LDM.. Instructions in this table;
+ // otherwise the wrong instructions will be selected.
+
+ {0xffd0ffff, 0xe810c000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRFE, "rfedb<c> <Rn>{!}"},
+ {0xffd0ffff, 0xe990c000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRFE, "rfe{ia}<c> <Rn>{!}"},
+
+ // Load instructions
+ {0xfffff800, 0x0000c800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDM, "ldm<c> <Rn>{!} <registers>"},
+ {0xffd02000, 0xe8900000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDM, "ldm<c>.w <Rn>{!} <registers>"},
+ {0xffd00000, 0xe9100000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDMDB, "ldmdb<c> <Rn>{!} <registers>"},
+ {0xfffff800, 0x00006800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRRtRnImm, "ldr<c> <Rt>, [<Rn>{,#imm}]"},
+ {0xfffff800, 0x00009800, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRRtRnImm, "ldr<c> <Rt>, [SP{,#imm}]"},
+ {0xfff00000, 0xf8d00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRRtRnImm,
+ "ldr<c>.w <Rt>, [<Rn>{,#imm12}]"},
+ {0xfff00800, 0xf8500800, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRRtRnImm,
+ "ldr<c> <Rt>, [<Rn>{,#+/-<imm8>}]{!}"},
+ // Thumb2 PC-relative load into register
+ {0xff7f0000, 0xf85f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRRtPCRelative,
+ "ldr<c>.w <Rt>, [PC, +/-#imm}]"},
+ {0xfffffe00, 0x00005800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRRegister, "ldr<c> <Rt>, [<Rn>, <Rm>]"},
+ {0xfff00fc0, 0xf8500000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRRegister,
+ "ldr<c>.w <Rt>, [<Rn>,<Rm>{,LSL #<imm2>}]"},
+ {0xfffff800, 0x00007800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRBImmediate,
+ "ldrb<c> <Rt>,[<Rn>{,#<imm5>}]"},
+ {0xfff00000, 0xf8900000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBImmediate,
+ "ldrb<c>.w <Rt>,[<Rn>{,#<imm12>}]"},
+ {0xfff00800, 0xf8100800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBImmediate,
+ "ldrb<c> <Rt>,[<Rn>, #+/-<imm8>]{!}"},
+ {0xff7f0000, 0xf81f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBLiteral, "ldrb<c> <Rt>,[...]"},
+ {0xfffffe00, 0x00005c00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRBRegister, "ldrb<c> <Rt>,[<Rn>,<Rm>]"},
+ {0xfff00fc0, 0xf8100000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBRegister,
+ "ldrb<c>.w <Rt>,[<Rn>,<Rm>{,LSL #imm2>}]"},
+ {0xfffff800, 0x00008800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRHImmediate,
+ "ldrh<c> <Rt>, [<Rn>{,#<imm>}]"},
+ {0xfff00000, 0xf8b00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHImmediate,
+ "ldrh<c>.w <Rt>,[<Rn>{,#<imm12>}]"},
+ {0xfff00800, 0xf8300800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHImmediate,
+ "ldrh<c> <Rt>,[<Rn>,#+/-<imm8>]{!}"},
+ {0xff7f0000, 0xf83f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHLiteral, "ldrh<c> <Rt>, <label>"},
+ {0xfffffe00, 0x00005a00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRHRegister,
+ "ldrh<c> <Rt>, [<Rn>,<Rm>]"},
+ {0xfff00fc0, 0xf8300000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHRegister,
+ "ldrh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]"},
+ {0xfff00000, 0xf9900000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBImmediate,
+ "ldrsb<c> <Rt>,[<Rn>,#<imm12>]"},
+ {0xfff00800, 0xf9100800, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBImmediate,
+ "ldrsb<c> <Rt>,[<Rn>,#+/-<imm8>]"},
+ {0xff7f0000, 0xf91f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBLiteral, "ldrsb<c> <Rt>, <label>"},
+ {0xfffffe00, 0x00005600, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRSBRegister,
+ "ldrsb<c> <Rt>,[<Rn>,<Rm>]"},
+ {0xfff00fc0, 0xf9100000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBRegister,
+ "ldrsb<c>.w <Rt>,[<Rn>,<Rm>{,LSL #imm2>}]"},
+ {0xfff00000, 0xf9b00000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHImmediate,
+ "ldrsh<c> <Rt>,[<Rn>,#<imm12>]"},
+ {0xfff00800, 0xf9300800, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHImmediate,
+ "ldrsh<c> <Rt>,[<Rn>,#+/-<imm8>]"},
+ {0xff7f0000, 0xf93f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHLiteral, "ldrsh<c> <Rt>,<label>"},
+ {0xfffffe00, 0x00005e00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRSHRegister,
+ "ldrsh<c> <Rt>,[<Rn>,<Rm>]"},
+ {0xfff00fc0, 0xf9300000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHRegister,
+ "ldrsh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]"},
+ {0xfe500000, 0xe8500000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRDImmediate,
+ "ldrd<c> <Rt>, <Rt2>, [<Rn>,#+/-<imm>]!"},
+ {0xfe100f00, 0xec100b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"},
+ {0xfe100f00, 0xec100a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"},
+ {0xffe00f00, 0xed100b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Dd>, [<Rn>{,#+/-<imm>}]"},
+ {0xff300f00, 0xed100a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Sd>, {<Rn>{,#+/-<imm>}]"},
+ {0xffb00000, 0xf9200000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1Multiple,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}],<Rm>"},
+ {0xffb00300, 0xf9a00000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1Single,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}],<Rm>"},
+ {0xffb00f00, 0xf9a00c00, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1SingleAll,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+
+ // Store instructions
+ {0xfffff800, 0x0000c000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTM, "stm<c> <Rn>{!} <registers>"},
+ {0xffd00000, 0xe8800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTM, "stm<c>.w <Rn>{!} <registers>"},
+ {0xffd00000, 0xe9000000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTMDB, "stmdb<c> <Rn>{!} <registers>"},
+ {0xfffff800, 0x00006000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTRThumb, "str<c> <Rt>, [<Rn>{,#<imm>}]"},
+ {0xfffff800, 0x00009000, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTRThumb, "str<c> <Rt>, [SP,#<imm>]"},
+ {0xfff00000, 0xf8c00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRThumb,
+ "str<c>.w <Rt>, [<Rn>,#<imm12>]"},
+ {0xfff00800, 0xf8400800, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRThumb,
+ "str<c> <Rt>, [<Rn>,#+/-<imm8>]"},
+ {0xfffffe00, 0x00005000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTRRegister, "str<c> <Rt> ,{<Rn>, <Rm>]"},
+ {0xfff00fc0, 0xf8400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRRegister,
+ "str<c>.w <Rt>, [<Rn>, <Rm> {lsl #imm2>}]"},
+ {0xfffff800, 0x00007000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTRBThumb,
+ "strb<c> <Rt>, [<Rn>, #<imm5>]"},
+ {0xfff00000, 0xf8800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRBThumb,
+ "strb<c>.w <Rt>, [<Rn>, #<imm12>]"},
+ {0xfff00800, 0xf8000800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRBThumb,
+ "strb<c> <Rt> ,[<Rn>, #+/-<imm8>]{!}"},
+ {0xfffffe00, 0x00005200, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTRHRegister, "strh<c> <Rt>,[<Rn>,<Rm>]"},
+ {0xfff00fc0, 0xf8200000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRHRegister,
+ "strh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]"},
+ {0xfff00000, 0xe8400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTREX,
+ "strex<c> <Rd>, <Rt>, [<Rn{,#<imm>}]"},
+ {0xfe500000, 0xe8400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRDImm,
+ "strd<c> <Rt>, <Rt2>, [<Rn>, #+/-<imm>]!"},
+ {0xfe100f00, 0xec000b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!}, <list>"},
+ {0xfea00f00, 0xec000a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!}, <list>"},
+ {0xff300f00, 0xed000b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Dd>, [<Rn>{,#+/-<imm>}]"},
+ {0xff300f00, 0xed000a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Sd>, [<Rn>{,#+/-<imm>}]"},
+ {0xffb00000, 0xf9000000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVST1Multiple,
+ "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+ {0xffb00300, 0xf9800000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVST1Single,
+ "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+
+ // Other instructions
+ {0xffffffc0, 0x0000b240, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSXTB, "sxtb<c> <Rd>,<Rm>"},
+ {0xfffff080, 0xfa4ff080, ARMV6_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSXTB, "sxtb<c>.w <Rd>,<Rm>{,<rotation>}"},
+ {0xffffffc0, 0x0000b200, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSXTH, "sxth<c> <Rd>,<Rm>"},
+ {0xfffff080, 0xfa0ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSXTH, "sxth<c>.w <Rd>,<Rm>{,<rotation>}"},
+ {0xffffffc0, 0x0000b2c0, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateUXTB, "uxtb<c> <Rd>,<Rm>"},
+ {0xfffff080, 0xfa5ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateUXTB, "uxtb<c>.w <Rd>,<Rm>{,<rotation>}"},
+ {0xffffffc0, 0x0000b280, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateUXTH, "uxth<c> <Rd>,<Rm>"},
+ {0xfffff080, 0xfa1ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateUXTH, "uxth<c>.w <Rd>,<Rm>{,<rotation>}"},
+ };
+
+ const size_t k_num_thumb_opcodes = llvm::array_lengthof(g_thumb_opcodes);
+ for (size_t i = 0; i < k_num_thumb_opcodes; ++i) {
+ if ((g_thumb_opcodes[i].mask & opcode) == g_thumb_opcodes[i].value &&
+ (g_thumb_opcodes[i].variants & arm_isa) != 0)
+ return &g_thumb_opcodes[i];
+ }
+ return nullptr;
+}
+
+bool EmulateInstructionARM::SetArchitecture(const ArchSpec &arch) {
+ m_arch = arch;
+ m_arm_isa = 0;
+ const char *arch_cstr = arch.GetArchitectureName();
+ if (arch_cstr) {
+ if (0 == ::strcasecmp(arch_cstr, "armv4t"))
+ m_arm_isa = ARMv4T;
+ else if (0 == ::strcasecmp(arch_cstr, "armv5tej"))
+ m_arm_isa = ARMv5TEJ;
+ else if (0 == ::strcasecmp(arch_cstr, "armv5te"))
+ m_arm_isa = ARMv5TE;
+ else if (0 == ::strcasecmp(arch_cstr, "armv5t"))
+ m_arm_isa = ARMv5T;
+ else if (0 == ::strcasecmp(arch_cstr, "armv6k"))
+ m_arm_isa = ARMv6K;
+ else if (0 == ::strcasecmp(arch_cstr, "armv6t2"))
+ m_arm_isa = ARMv6T2;
+ else if (0 == ::strcasecmp(arch_cstr, "armv7s"))
+ m_arm_isa = ARMv7S;
+ else if (0 == ::strcasecmp(arch_cstr, "arm"))
+ m_arm_isa = ARMvAll;
+ else if (0 == ::strcasecmp(arch_cstr, "thumb"))
+ m_arm_isa = ARMvAll;
+ else if (0 == ::strncasecmp(arch_cstr, "armv4", 5))
+ m_arm_isa = ARMv4;
+ else if (0 == ::strncasecmp(arch_cstr, "armv6", 5))
+ m_arm_isa = ARMv6;
+ else if (0 == ::strncasecmp(arch_cstr, "armv7", 5))
+ m_arm_isa = ARMv7;
+ else if (0 == ::strncasecmp(arch_cstr, "armv8", 5))
+ m_arm_isa = ARMv8;
+ }
+ return m_arm_isa != 0;
+}
+
+bool EmulateInstructionARM::SetInstruction(const Opcode &insn_opcode,
+ const Address &inst_addr,
+ Target *target) {
+ if (EmulateInstruction::SetInstruction(insn_opcode, inst_addr, target)) {
+ if (m_arch.GetTriple().getArch() == llvm::Triple::thumb ||
+ m_arch.IsAlwaysThumbInstructions())
+ m_opcode_mode = eModeThumb;
+ else {
+ AddressClass addr_class = inst_addr.GetAddressClass();
+
+ if ((addr_class == AddressClass::eCode) ||
+ (addr_class == AddressClass::eUnknown))
+ m_opcode_mode = eModeARM;
+ else if (addr_class == AddressClass::eCodeAlternateISA)
+ m_opcode_mode = eModeThumb;
+ else
+ return false;
+ }
+ if (m_opcode_mode == eModeThumb || m_arch.IsAlwaysThumbInstructions())
+ m_opcode_cpsr = CPSR_MODE_USR | MASK_CPSR_T;
+ else
+ m_opcode_cpsr = CPSR_MODE_USR;
+ return true;
+ }
+ return false;
+}
+
+bool EmulateInstructionARM::ReadInstruction() {
+ bool success = false;
+ m_opcode_cpsr = ReadRegisterUnsigned(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, 0, &success);
+ if (success) {
+ addr_t pc =
+ ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_ADDRESS, &success);
+ if (success) {
+ Context read_inst_context;
+ read_inst_context.type = eContextReadOpcode;
+ read_inst_context.SetNoArgs();
+
+ if ((m_opcode_cpsr & MASK_CPSR_T) || m_arch.IsAlwaysThumbInstructions()) {
+ m_opcode_mode = eModeThumb;
+ uint32_t thumb_opcode = MemARead(read_inst_context, pc, 2, 0, &success);
+
+ if (success) {
+ if ((thumb_opcode & 0xe000) != 0xe000 ||
+ ((thumb_opcode & 0x1800u) == 0)) {
+ m_opcode.SetOpcode16(thumb_opcode, GetByteOrder());
+ } else {
+ m_opcode.SetOpcode32(
+ (thumb_opcode << 16) |
+ MemARead(read_inst_context, pc + 2, 2, 0, &success),
+ GetByteOrder());
+ }
+ }
+ } else {
+ m_opcode_mode = eModeARM;
+ m_opcode.SetOpcode32(MemARead(read_inst_context, pc, 4, 0, &success),
+ GetByteOrder());
+ }
+
+ if (!m_ignore_conditions) {
+ // If we are not ignoreing the conditions then init the it session from
+ // the current value of cpsr.
+ uint32_t it = (Bits32(m_opcode_cpsr, 15, 10) << 2) |
+ Bits32(m_opcode_cpsr, 26, 25);
+ if (it != 0)
+ m_it_session.InitIT(it);
+ }
+ }
+ }
+ if (!success) {
+ m_opcode_mode = eModeInvalid;
+ m_addr = LLDB_INVALID_ADDRESS;
+ }
+ return success;
+}
+
+uint32_t EmulateInstructionARM::ArchVersion() { return m_arm_isa; }
+
+bool EmulateInstructionARM::ConditionPassed(const uint32_t opcode) {
+ // If we are ignoring conditions, then always return true. this allows us to
+ // iterate over disassembly code and still emulate an instruction even if we
+ // don't have all the right bits set in the CPSR register...
+ if (m_ignore_conditions)
+ return true;
+
+ const uint32_t cond = CurrentCond(opcode);
+ if (cond == UINT32_MAX)
+ return false;
+
+ bool result = false;
+ switch (UnsignedBits(cond, 3, 1)) {
+ case 0:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else
+ result = (m_opcode_cpsr & MASK_CPSR_Z) != 0;
+ break;
+ case 1:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else
+ result = (m_opcode_cpsr & MASK_CPSR_C) != 0;
+ break;
+ case 2:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else
+ result = (m_opcode_cpsr & MASK_CPSR_N) != 0;
+ break;
+ case 3:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else
+ result = (m_opcode_cpsr & MASK_CPSR_V) != 0;
+ break;
+ case 4:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else
+ result = ((m_opcode_cpsr & MASK_CPSR_C) != 0) &&
+ ((m_opcode_cpsr & MASK_CPSR_Z) == 0);
+ break;
+ case 5:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else {
+ bool n = (m_opcode_cpsr & MASK_CPSR_N);
+ bool v = (m_opcode_cpsr & MASK_CPSR_V);
+ result = n == v;
+ }
+ break;
+ case 6:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else {
+ bool n = (m_opcode_cpsr & MASK_CPSR_N);
+ bool v = (m_opcode_cpsr & MASK_CPSR_V);
+ result = n == v && ((m_opcode_cpsr & MASK_CPSR_Z) == 0);
+ }
+ break;
+ case 7:
+ // Always execute (cond == 0b1110, or the special 0b1111 which gives
+ // opcodes different meanings, but always means execution happens.
+ return true;
+ }
+
+ if (cond & 1)
+ result = !result;
+ return result;
+}
+
+uint32_t EmulateInstructionARM::CurrentCond(const uint32_t opcode) {
+ switch (m_opcode_mode) {
+ case eModeInvalid:
+ break;
+
+ case eModeARM:
+ return UnsignedBits(opcode, 31, 28);
+
+ case eModeThumb:
+ // For T1 and T3 encodings of the Branch instruction, it returns the 4-bit
+ // 'cond' field of the encoding.
+ {
+ const uint32_t byte_size = m_opcode.GetByteSize();
+ if (byte_size == 2) {
+ if (Bits32(opcode, 15, 12) == 0x0d && Bits32(opcode, 11, 8) != 0x0f)
+ return Bits32(opcode, 11, 8);
+ } else if (byte_size == 4) {
+ if (Bits32(opcode, 31, 27) == 0x1e && Bits32(opcode, 15, 14) == 0x02 &&
+ Bits32(opcode, 12, 12) == 0x00 && Bits32(opcode, 25, 22) <= 0x0d) {
+ return Bits32(opcode, 25, 22);
+ }
+ } else
+ // We have an invalid thumb instruction, let's bail out.
+ break;
+
+ return m_it_session.GetCond();
+ }
+ }
+ return UINT32_MAX; // Return invalid value
+}
+
+bool EmulateInstructionARM::InITBlock() {
+ return CurrentInstrSet() == eModeThumb && m_it_session.InITBlock();
+}
+
+bool EmulateInstructionARM::LastInITBlock() {
+ return CurrentInstrSet() == eModeThumb && m_it_session.LastInITBlock();
+}
+
+bool EmulateInstructionARM::BadMode(uint32_t mode) {
+
+ switch (mode) {
+ case 16:
+ return false; // '10000'
+ case 17:
+ return false; // '10001'
+ case 18:
+ return false; // '10010'
+ case 19:
+ return false; // '10011'
+ case 22:
+ return false; // '10110'
+ case 23:
+ return false; // '10111'
+ case 27:
+ return false; // '11011'
+ case 31:
+ return false; // '11111'
+ default:
+ return true;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM::CurrentModeIsPrivileged() {
+ uint32_t mode = Bits32(m_opcode_cpsr, 4, 0);
+
+ if (BadMode(mode))
+ return false;
+
+ if (mode == 16)
+ return false;
+
+ return true;
+}
+
+void EmulateInstructionARM::CPSRWriteByInstr(uint32_t value, uint32_t bytemask,
+ bool affect_execstate) {
+ bool privileged = CurrentModeIsPrivileged();
+
+ uint32_t tmp_cpsr = Bits32(m_opcode_cpsr, 23, 20) << 20;
+
+ if (BitIsSet(bytemask, 3)) {
+ tmp_cpsr = tmp_cpsr | (Bits32(value, 31, 27) << 27);
+ if (affect_execstate)
+ tmp_cpsr = tmp_cpsr | (Bits32(value, 26, 24) << 24);
+ }
+
+ if (BitIsSet(bytemask, 2)) {
+ tmp_cpsr = tmp_cpsr | (Bits32(value, 19, 16) << 16);
+ }
+
+ if (BitIsSet(bytemask, 1)) {
+ if (affect_execstate)
+ tmp_cpsr = tmp_cpsr | (Bits32(value, 15, 10) << 10);
+ tmp_cpsr = tmp_cpsr | (Bit32(value, 9) << 9);
+ if (privileged)
+ tmp_cpsr = tmp_cpsr | (Bit32(value, 8) << 8);
+ }
+
+ if (BitIsSet(bytemask, 0)) {
+ if (privileged)
+ tmp_cpsr = tmp_cpsr | (Bits32(value, 7, 6) << 6);
+ if (affect_execstate)
+ tmp_cpsr = tmp_cpsr | (Bit32(value, 5) << 5);
+ if (privileged)
+ tmp_cpsr = tmp_cpsr | Bits32(value, 4, 0);
+ }
+
+ m_opcode_cpsr = tmp_cpsr;
+}
+
+bool EmulateInstructionARM::BranchWritePC(const Context &context,
+ uint32_t addr) {
+ addr_t target;
+
+ // Check the current instruction set.
+ if (CurrentInstrSet() == eModeARM)
+ target = addr & 0xfffffffc;
+ else
+ target = addr & 0xfffffffe;
+
+ return WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC, target);
+}
+
+// As a side effect, BXWritePC sets context.arg2 to eModeARM or eModeThumb by
+// inspecting addr.
+bool EmulateInstructionARM::BXWritePC(Context &context, uint32_t addr) {
+ addr_t target;
+ // If the CPSR is changed due to switching between ARM and Thumb ISETSTATE,
+ // we want to record it and issue a WriteRegister callback so the clients can
+ // track the mode changes accordingly.
+ bool cpsr_changed = false;
+
+ if (BitIsSet(addr, 0)) {
+ if (CurrentInstrSet() != eModeThumb) {
+ SelectInstrSet(eModeThumb);
+ cpsr_changed = true;
+ }
+ target = addr & 0xfffffffe;
+ context.SetISA(eModeThumb);
+ } else if (BitIsClear(addr, 1)) {
+ if (CurrentInstrSet() != eModeARM) {
+ SelectInstrSet(eModeARM);
+ cpsr_changed = true;
+ }
+ target = addr & 0xfffffffc;
+ context.SetISA(eModeARM);
+ } else
+ return false; // address<1:0> == '10' => UNPREDICTABLE
+
+ if (cpsr_changed) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
+ return false;
+ }
+ return WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC, target);
+}
+
+// Dispatches to either BXWritePC or BranchWritePC based on architecture
+// versions.
+bool EmulateInstructionARM::LoadWritePC(Context &context, uint32_t addr) {
+ if (ArchVersion() >= ARMv5T)
+ return BXWritePC(context, addr);
+ else
+ return BranchWritePC((const Context)context, addr);
+}
+
+// Dispatches to either BXWritePC or BranchWritePC based on architecture
+// versions and current instruction set.
+bool EmulateInstructionARM::ALUWritePC(Context &context, uint32_t addr) {
+ if (ArchVersion() >= ARMv7 && CurrentInstrSet() == eModeARM)
+ return BXWritePC(context, addr);
+ else
+ return BranchWritePC((const Context)context, addr);
+}
+
+EmulateInstructionARM::Mode EmulateInstructionARM::CurrentInstrSet() {
+ return m_opcode_mode;
+}
+
+// Set the 'T' bit of our CPSR. The m_opcode_mode gets updated when the next
+// ReadInstruction() is performed. This function has a side effect of updating
+// the m_new_inst_cpsr member variable if necessary.
+bool EmulateInstructionARM::SelectInstrSet(Mode arm_or_thumb) {
+ m_new_inst_cpsr = m_opcode_cpsr;
+ switch (arm_or_thumb) {
+ default:
+ return false;
+ case eModeARM:
+ // Clear the T bit.
+ m_new_inst_cpsr &= ~MASK_CPSR_T;
+ break;
+ case eModeThumb:
+ // Set the T bit.
+ m_new_inst_cpsr |= MASK_CPSR_T;
+ break;
+ }
+ return true;
+}
+
+// This function returns TRUE if the processor currently provides support for
+// unaligned memory accesses, or FALSE otherwise. This is always TRUE in ARMv7,
+// controllable by the SCTLR.U bit in ARMv6, and always FALSE before ARMv6.
+bool EmulateInstructionARM::UnalignedSupport() {
+ return (ArchVersion() >= ARMv7);
+}
+
+// The main addition and subtraction instructions can produce status
+// information about both unsigned carry and signed overflow conditions. This
+// status information can be used to synthesize multi-word additions and
+// subtractions.
+EmulateInstructionARM::AddWithCarryResult
+EmulateInstructionARM::AddWithCarry(uint32_t x, uint32_t y, uint8_t carry_in) {
+ uint32_t result;
+ uint8_t carry_out;
+ uint8_t overflow;
+
+ uint64_t unsigned_sum = x + y + carry_in;
+ int64_t signed_sum = (int32_t)x + (int32_t)y + (int32_t)carry_in;
+
+ result = UnsignedBits(unsigned_sum, 31, 0);
+ // carry_out = (result == unsigned_sum ? 0 : 1);
+ overflow = ((int32_t)result == signed_sum ? 0 : 1);
+
+ if (carry_in)
+ carry_out = ((int32_t)x >= (int32_t)(~y)) ? 1 : 0;
+ else
+ carry_out = ((int32_t)x > (int32_t)y) ? 1 : 0;
+
+ AddWithCarryResult res = {result, carry_out, overflow};
+ return res;
+}
+
+uint32_t EmulateInstructionARM::ReadCoreReg(uint32_t num, bool *success) {
+ lldb::RegisterKind reg_kind;
+ uint32_t reg_num;
+ switch (num) {
+ case SP_REG:
+ reg_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_SP;
+ break;
+ case LR_REG:
+ reg_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_RA;
+ break;
+ case PC_REG:
+ reg_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_PC;
+ break;
+ default:
+ if (num < SP_REG) {
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_r0 + num;
+ } else {
+ // assert(0 && "Invalid register number");
+ *success = false;
+ return UINT32_MAX;
+ }
+ break;
+ }
+
+ // Read our register.
+ uint32_t val = ReadRegisterUnsigned(reg_kind, reg_num, 0, success);
+
+ // When executing an ARM instruction , PC reads as the address of the current
+ // instruction plus 8. When executing a Thumb instruction , PC reads as the
+ // address of the current instruction plus 4.
+ if (num == 15) {
+ if (CurrentInstrSet() == eModeARM)
+ val += 8;
+ else
+ val += 4;
+ }
+
+ return val;
+}
+
+// Write the result to the ARM core register Rd, and optionally update the
+// condition flags based on the result.
+//
+// This helper method tries to encapsulate the following pseudocode from the
+// ARM Architecture Reference Manual:
+//
+// if d == 15 then // Can only occur for encoding A1
+// ALUWritePC(result); // setflags is always FALSE here
+// else
+// R[d] = result;
+// if setflags then
+// APSR.N = result<31>;
+// APSR.Z = IsZeroBit(result);
+// APSR.C = carry;
+// // APSR.V unchanged
+//
+// In the above case, the API client does not pass in the overflow arg, which
+// defaults to ~0u.
+bool EmulateInstructionARM::WriteCoreRegOptionalFlags(
+ Context &context, const uint32_t result, const uint32_t Rd, bool setflags,
+ const uint32_t carry, const uint32_t overflow) {
+ if (Rd == 15) {
+ if (!ALUWritePC(context, result))
+ return false;
+ } else {
+ lldb::RegisterKind reg_kind;
+ uint32_t reg_num;
+ switch (Rd) {
+ case SP_REG:
+ reg_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_SP;
+ break;
+ case LR_REG:
+ reg_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_RA;
+ break;
+ default:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_r0 + Rd;
+ }
+ if (!WriteRegisterUnsigned(context, reg_kind, reg_num, result))
+ return false;
+ if (setflags)
+ return WriteFlags(context, result, carry, overflow);
+ }
+ return true;
+}
+
+// This helper method tries to encapsulate the following pseudocode from the
+// ARM Architecture Reference Manual:
+//
+// APSR.N = result<31>;
+// APSR.Z = IsZeroBit(result);
+// APSR.C = carry;
+// APSR.V = overflow
+//
+// Default arguments can be specified for carry and overflow parameters, which
+// means not to update the respective flags.
+bool EmulateInstructionARM::WriteFlags(Context &context, const uint32_t result,
+ const uint32_t carry,
+ const uint32_t overflow) {
+ m_new_inst_cpsr = m_opcode_cpsr;
+ SetBit32(m_new_inst_cpsr, CPSR_N_POS, Bit32(result, CPSR_N_POS));
+ SetBit32(m_new_inst_cpsr, CPSR_Z_POS, result == 0 ? 1 : 0);
+ if (carry != ~0u)
+ SetBit32(m_new_inst_cpsr, CPSR_C_POS, carry);
+ if (overflow != ~0u)
+ SetBit32(m_new_inst_cpsr, CPSR_V_POS, overflow);
+ if (m_new_inst_cpsr != m_opcode_cpsr) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
+ return false;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM::EvaluateInstruction(uint32_t evaluate_options) {
+ ARMOpcode *opcode_data = nullptr;
+
+ if (m_opcode_mode == eModeThumb)
+ opcode_data =
+ GetThumbOpcodeForInstruction(m_opcode.GetOpcode32(), m_arm_isa);
+ else if (m_opcode_mode == eModeARM)
+ opcode_data = GetARMOpcodeForInstruction(m_opcode.GetOpcode32(), m_arm_isa);
+
+ const bool auto_advance_pc =
+ evaluate_options & eEmulateInstructionOptionAutoAdvancePC;
+ m_ignore_conditions =
+ evaluate_options & eEmulateInstructionOptionIgnoreConditions;
+
+ bool success = false;
+ if (m_opcode_cpsr == 0 || !m_ignore_conditions) {
+ m_opcode_cpsr =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_cpsr, 0, &success);
+ }
+
+ // Only return false if we are unable to read the CPSR if we care about
+ // conditions
+ if (!success && !m_ignore_conditions)
+ return false;
+
+ uint32_t orig_pc_value = 0;
+ if (auto_advance_pc) {
+ orig_pc_value =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc, 0, &success);
+ if (!success)
+ return false;
+ }
+
+ // Call the Emulate... function if we managed to decode the opcode.
+ if (opcode_data) {
+ success = (this->*opcode_data->callback)(m_opcode.GetOpcode32(),
+ opcode_data->encoding);
+ if (!success)
+ return false;
+ }
+
+ // Advance the ITSTATE bits to their values for the next instruction if we
+ // haven't just executed an IT instruction what initialized it.
+ if (m_opcode_mode == eModeThumb && m_it_session.InITBlock() &&
+ (opcode_data == nullptr ||
+ opcode_data->callback != &EmulateInstructionARM::EmulateIT))
+ m_it_session.ITAdvance();
+
+ if (auto_advance_pc) {
+ uint32_t after_pc_value =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc, 0, &success);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc && (after_pc_value == orig_pc_value)) {
+ after_pc_value += m_opcode.GetByteSize();
+
+ EmulateInstruction::Context context;
+ context.type = eContextAdvancePC;
+ context.SetNoArgs();
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc,
+ after_pc_value))
+ return false;
+ }
+ }
+ return true;
+}
+
+EmulateInstruction::InstructionCondition
+EmulateInstructionARM::GetInstructionCondition() {
+ const uint32_t cond = CurrentCond(m_opcode.GetOpcode32());
+ if (cond == 0xe || cond == 0xf || cond == UINT32_MAX)
+ return EmulateInstruction::UnconditionalCondition;
+ return cond;
+}
+
+bool EmulateInstructionARM::TestEmulation(Stream *out_stream, ArchSpec &arch,
+ OptionValueDictionary *test_data) {
+ if (!test_data) {
+ out_stream->Printf("TestEmulation: Missing test data.\n");
+ return false;
+ }
+
+ static ConstString opcode_key("opcode");
+ static ConstString before_key("before_state");
+ static ConstString after_key("after_state");
+
+ OptionValueSP value_sp = test_data->GetValueForKey(opcode_key);
+
+ uint32_t test_opcode;
+ if ((value_sp.get() == nullptr) ||
+ (value_sp->GetType() != OptionValue::eTypeUInt64)) {
+ out_stream->Printf("TestEmulation: Error reading opcode from test file.\n");
+ return false;
+ }
+ test_opcode = value_sp->GetUInt64Value();
+
+ if (arch.GetTriple().getArch() == llvm::Triple::thumb ||
+ arch.IsAlwaysThumbInstructions()) {
+ m_opcode_mode = eModeThumb;
+ if (test_opcode < 0x10000)
+ m_opcode.SetOpcode16(test_opcode, endian::InlHostByteOrder());
+ else
+ m_opcode.SetOpcode32(test_opcode, endian::InlHostByteOrder());
+ } else if (arch.GetTriple().getArch() == llvm::Triple::arm) {
+ m_opcode_mode = eModeARM;
+ m_opcode.SetOpcode32(test_opcode, endian::InlHostByteOrder());
+ } else {
+ out_stream->Printf("TestEmulation: Invalid arch.\n");
+ return false;
+ }
+
+ EmulationStateARM before_state;
+ EmulationStateARM after_state;
+
+ value_sp = test_data->GetValueForKey(before_key);
+ if ((value_sp.get() == nullptr) ||
+ (value_sp->GetType() != OptionValue::eTypeDictionary)) {
+ out_stream->Printf("TestEmulation: Failed to find 'before' state.\n");
+ return false;
+ }
+
+ OptionValueDictionary *state_dictionary = value_sp->GetAsDictionary();
+ if (!before_state.LoadStateFromDictionary(state_dictionary)) {
+ out_stream->Printf("TestEmulation: Failed loading 'before' state.\n");
+ return false;
+ }
+
+ value_sp = test_data->GetValueForKey(after_key);
+ if ((value_sp.get() == nullptr) ||
+ (value_sp->GetType() != OptionValue::eTypeDictionary)) {
+ out_stream->Printf("TestEmulation: Failed to find 'after' state.\n");
+ return false;
+ }
+
+ state_dictionary = value_sp->GetAsDictionary();
+ if (!after_state.LoadStateFromDictionary(state_dictionary)) {
+ out_stream->Printf("TestEmulation: Failed loading 'after' state.\n");
+ return false;
+ }
+
+ SetBaton((void *)&before_state);
+ SetCallbacks(&EmulationStateARM::ReadPseudoMemory,
+ &EmulationStateARM::WritePseudoMemory,
+ &EmulationStateARM::ReadPseudoRegister,
+ &EmulationStateARM::WritePseudoRegister);
+
+ bool success = EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
+ if (!success) {
+ out_stream->Printf("TestEmulation: EvaluateInstruction() failed.\n");
+ return false;
+ }
+
+ success = before_state.CompareState(after_state);
+ if (!success)
+ out_stream->Printf(
+ "TestEmulation: 'before' and 'after' states do not match.\n");
+
+ return success;
+}
+//
+//
+// const char *
+// EmulateInstructionARM::GetRegisterName (uint32_t reg_kind, uint32_t reg_num)
+//{
+// if (reg_kind == eRegisterKindGeneric)
+// {
+// switch (reg_num)
+// {
+// case LLDB_REGNUM_GENERIC_PC: return "pc";
+// case LLDB_REGNUM_GENERIC_SP: return "sp";
+// case LLDB_REGNUM_GENERIC_FP: return "fp";
+// case LLDB_REGNUM_GENERIC_RA: return "lr";
+// case LLDB_REGNUM_GENERIC_FLAGS: return "cpsr";
+// default: return NULL;
+// }
+// }
+// else if (reg_kind == eRegisterKindDWARF)
+// {
+// return GetARMDWARFRegisterName (reg_num);
+// }
+// return NULL;
+//}
+//
+bool EmulateInstructionARM::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_sp, 0);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("EmulateInstructionARM");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ unwind_plan.SetReturnAddressRegister(dwarf_lr);
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h
new file mode 100644
index 000000000000..13d7fc061bea
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h
@@ -0,0 +1,786 @@
+//===-- EmulateInstructionARM.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_EmulateInstructionARM_h_
+#define lldb_EmulateInstructionARM_h_
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+
+namespace lldb_private {
+
+// ITSession - Keep track of the IT Block progression.
+class ITSession {
+public:
+ ITSession() : ITCounter(0), ITState(0) {}
+ ~ITSession() {}
+
+ // InitIT - Initializes ITCounter/ITState.
+ bool InitIT(uint32_t bits7_0);
+
+ // ITAdvance - Updates ITCounter/ITState as IT Block progresses.
+ void ITAdvance();
+
+ // InITBlock - Returns true if we're inside an IT Block.
+ bool InITBlock();
+
+ // LastInITBlock - Returns true if we're the last instruction inside an IT
+ // Block.
+ bool LastInITBlock();
+
+ // GetCond - Gets condition bits for the current thumb instruction.
+ uint32_t GetCond();
+
+private:
+ uint32_t ITCounter; // Possible values: 0, 1, 2, 3, 4.
+ uint32_t ITState; // A2.5.2 Consists of IT[7:5] and IT[4:0] initially.
+};
+
+class EmulateInstructionARM : public EmulateInstruction {
+public:
+ enum ARMEncoding {
+ eEncodingA1,
+ eEncodingA2,
+ eEncodingA3,
+ eEncodingA4,
+ eEncodingA5,
+ eEncodingT1,
+ eEncodingT2,
+ eEncodingT3,
+ eEncodingT4,
+ eEncodingT5
+ };
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::EmulateInstruction *
+ CreateInstance(const lldb_private::ArchSpec &arch, InstructionType inst_type);
+
+ static bool
+ SupportsEmulatingInstructionsOfTypeStatic(InstructionType inst_type) {
+ switch (inst_type) {
+ case eInstructionTypeAny:
+ case eInstructionTypePrologueEpilogue:
+ case eInstructionTypePCModifying:
+ return true;
+
+ case eInstructionTypeAll:
+ return false;
+ }
+ return false;
+ }
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ bool SetTargetTriple(const ArchSpec &arch) override;
+
+ enum Mode { eModeInvalid = -1, eModeARM, eModeThumb };
+
+ EmulateInstructionARM(const ArchSpec &arch)
+ : EmulateInstruction(arch), m_arm_isa(0), m_opcode_mode(eModeInvalid),
+ m_opcode_cpsr(0), m_it_session(), m_ignore_conditions(false) {
+ SetArchitecture(arch);
+ }
+
+ // EmulateInstructionARM (const ArchSpec &arch,
+ // bool ignore_conditions,
+ // void *baton,
+ // ReadMemory read_mem_callback,
+ // WriteMemory write_mem_callback,
+ // ReadRegister read_reg_callback,
+ // WriteRegister write_reg_callback) :
+ // EmulateInstruction (arch,
+ // ignore_conditions,
+ // baton,
+ // read_mem_callback,
+ // write_mem_callback,
+ // read_reg_callback,
+ // write_reg_callback),
+ // m_arm_isa (0),
+ // m_opcode_mode (eModeInvalid),
+ // m_opcode_cpsr (0),
+ // m_it_session ()
+ // {
+ // }
+
+ bool SupportsEmulatingInstructionsOfType(InstructionType inst_type) override {
+ return SupportsEmulatingInstructionsOfTypeStatic(inst_type);
+ }
+
+ virtual bool SetArchitecture(const ArchSpec &arch);
+
+ bool ReadInstruction() override;
+
+ bool SetInstruction(const Opcode &insn_opcode, const Address &inst_addr,
+ Target *target) override;
+
+ bool EvaluateInstruction(uint32_t evaluate_options) override;
+
+ InstructionCondition GetInstructionCondition() override;
+
+ bool TestEmulation(Stream *out_stream, ArchSpec &arch,
+ OptionValueDictionary *test_data) override;
+
+ bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
+ RegisterInfo &reg_info) override;
+
+ bool CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) override;
+
+ uint32_t ArchVersion();
+
+ bool ConditionPassed(const uint32_t opcode);
+
+ uint32_t CurrentCond(const uint32_t opcode);
+
+ // InITBlock - Returns true if we're in Thumb mode and inside an IT Block.
+ bool InITBlock();
+
+ // LastInITBlock - Returns true if we're in Thumb mode and the last
+ // instruction inside an IT Block.
+ bool LastInITBlock();
+
+ bool BadMode(uint32_t mode);
+
+ bool CurrentModeIsPrivileged();
+
+ void CPSRWriteByInstr(uint32_t value, uint32_t bytemask,
+ bool affect_execstate);
+
+ bool BranchWritePC(const Context &context, uint32_t addr);
+
+ bool BXWritePC(Context &context, uint32_t addr);
+
+ bool LoadWritePC(Context &context, uint32_t addr);
+
+ bool ALUWritePC(Context &context, uint32_t addr);
+
+ Mode CurrentInstrSet();
+
+ bool SelectInstrSet(Mode arm_or_thumb);
+
+ bool WriteBits32Unknown(int n);
+
+ bool WriteBits32UnknownToMemory(lldb::addr_t address);
+
+ bool UnalignedSupport();
+
+ typedef struct {
+ uint32_t result;
+ uint8_t carry_out;
+ uint8_t overflow;
+ } AddWithCarryResult;
+
+ AddWithCarryResult AddWithCarry(uint32_t x, uint32_t y, uint8_t carry_in);
+
+ // Helper method to read the content of an ARM core register.
+ uint32_t ReadCoreReg(uint32_t regnum, bool *success);
+
+ // See A8.6.96 MOV (immediate) Operation.
+ // Default arguments are specified for carry and overflow parameters, which
+ // means
+ // not to update the respective flags even if setflags is true.
+ bool WriteCoreRegOptionalFlags(Context &context, const uint32_t result,
+ const uint32_t Rd, bool setflags,
+ const uint32_t carry = ~0u,
+ const uint32_t overflow = ~0u);
+
+ bool WriteCoreReg(Context &context, const uint32_t result,
+ const uint32_t Rd) {
+ // Don't set the flags.
+ return WriteCoreRegOptionalFlags(context, result, Rd, false);
+ }
+
+ // See A8.6.35 CMP (immediate) Operation.
+ // Default arguments are specified for carry and overflow parameters, which
+ // means
+ // not to update the respective flags.
+ bool WriteFlags(Context &context, const uint32_t result,
+ const uint32_t carry = ~0u, const uint32_t overflow = ~0u);
+
+ inline uint64_t MemARead(EmulateInstruction::Context &context,
+ lldb::addr_t address, uint32_t size,
+ uint64_t fail_value, bool *success_ptr) {
+ // This is a stub function corresponding to "MemA[]" in the ARM manual
+ // pseudocode, for
+ // aligned reads from memory. Since we are not trying to write a full
+ // hardware simulator, and since
+ // we are running in User mode (rather than Kernel mode) and therefore won't
+ // have access to many of the
+ // system registers we would need in order to fully implement this function,
+ // we will just call
+ // ReadMemoryUnsigned from here. In the future, if we decide we do need to
+ // do more faithful emulation of
+ // the hardware, we can update this function appropriately.
+
+ return ReadMemoryUnsigned(context, address, size, fail_value, success_ptr);
+ }
+
+ inline bool MemAWrite(EmulateInstruction::Context &context,
+ lldb::addr_t address, uint64_t data_val, uint32_t size)
+
+ {
+ // This is a stub function corresponding to "MemA[]" in the ARM manual
+ // pseudocode, for
+ // aligned writes to memory. Since we are not trying to write a full
+ // hardware simulator, and since
+ // we are running in User mode (rather than Kernel mode) and therefore won't
+ // have access to many of the
+ // system registers we would need in order to fully implement this function,
+ // we will just call
+ // WriteMemoryUnsigned from here. In the future, if we decide we do need to
+ // do more faithful emulation of
+ // the hardware, we can update this function appropriately.
+
+ return WriteMemoryUnsigned(context, address, data_val, size);
+ }
+
+ inline uint64_t MemURead(EmulateInstruction::Context &context,
+ lldb::addr_t address, uint32_t size,
+ uint64_t fail_value, bool *success_ptr) {
+ // This is a stub function corresponding to "MemU[]" in the ARM manual
+ // pseudocode, for
+ // unaligned reads from memory. Since we are not trying to write a full
+ // hardware simulator, and since
+ // we are running in User mode (rather than Kernel mode) and therefore won't
+ // have access to many of the
+ // system registers we would need in order to fully implement this function,
+ // we will just call
+ // ReadMemoryUnsigned from here. In the future, if we decide we do need to
+ // do more faithful emulation of
+ // the hardware, we can update this function appropriately.
+
+ return ReadMemoryUnsigned(context, address, size, fail_value, success_ptr);
+ }
+
+ inline bool MemUWrite(EmulateInstruction::Context &context,
+ lldb::addr_t address, uint64_t data_val, uint32_t size)
+
+ {
+ // This is a stub function corresponding to "MemU[]" in the ARM manual
+ // pseudocode, for
+ // unaligned writes to memory. Since we are not trying to write a full
+ // hardware simulator, and since
+ // we are running in User mode (rather than Kernel mode) and therefore won't
+ // have access to many of the
+ // system registers we would need in order to fully implement this function,
+ // we will just call
+ // WriteMemoryUnsigned from here. In the future, if we decide we do need to
+ // do more faithful emulation of
+ // the hardware, we can update this function appropriately.
+
+ return WriteMemoryUnsigned(context, address, data_val, size);
+ }
+
+protected:
+ // Typedef for the callback function used during the emulation.
+ // Pass along (ARMEncoding)encoding as the callback data.
+ enum ARMInstrSize { eSize16, eSize32 };
+
+ typedef struct {
+ uint32_t mask;
+ uint32_t value;
+ uint32_t variants;
+ EmulateInstructionARM::ARMEncoding encoding;
+ uint32_t vfp_variants;
+ ARMInstrSize size;
+ bool (EmulateInstructionARM::*callback)(
+ const uint32_t opcode,
+ const EmulateInstructionARM::ARMEncoding encoding);
+ const char *name;
+ } ARMOpcode;
+
+ uint32_t GetFramePointerRegisterNumber() const;
+
+ uint32_t GetFramePointerDWARFRegisterNumber() const;
+
+ static ARMOpcode *GetARMOpcodeForInstruction(const uint32_t opcode,
+ uint32_t isa_mask);
+
+ static ARMOpcode *GetThumbOpcodeForInstruction(const uint32_t opcode,
+ uint32_t isa_mask);
+
+ // A8.6.123 PUSH
+ bool EmulatePUSH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.122 POP
+ bool EmulatePOP(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.8 ADD (SP plus immediate)
+ bool EmulateADDRdSPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.97 MOV (register) -- Rd == r7|ip and Rm == sp
+ bool EmulateMOVRdSP(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.97 MOV (register) -- move from r8-r15 to r0-r7
+ bool EmulateMOVLowHigh(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.59 LDR (literal)
+ bool EmulateLDRRtPCRelative(const uint32_t opcode,
+ const ARMEncoding encoding);
+
+ // A8.6.8 ADD (SP plus immediate)
+ bool EmulateADDSPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.9 ADD (SP plus register)
+ bool EmulateADDSPRm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.23 BL, BLX (immediate)
+ bool EmulateBLXImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.24 BLX (register)
+ bool EmulateBLXRm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.25 BX
+ bool EmulateBXRm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.26 BXJ
+ bool EmulateBXJRm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.212 SUB (immediate, ARM) -- Rd == r7 and Rm == ip
+ bool EmulateSUBR7IPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.215 SUB (SP minus immediate) -- Rd == ip
+ bool EmulateSUBIPSPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.215 SUB (SP minus immediate)
+ bool EmulateSUBSPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.216 SUB (SP minus register)
+ bool EmulateSUBSPReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.194 STR (immediate, ARM) -- Rn == sp
+ bool EmulateSTRRtSP(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.355 VPUSH
+ bool EmulateVPUSH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.354 VPOP
+ bool EmulateVPOP(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.218 SVC (previously SWI)
+ bool EmulateSVC(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.50 IT
+ bool EmulateIT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // NOP
+ bool EmulateNop(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.16 B
+ bool EmulateB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.27 CBNZ, CBZ
+ bool EmulateCB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.226 TBB, TBH
+ bool EmulateTB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.4 ADD (immediate, Thumb)
+ bool EmulateADDImmThumb(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.5 ADD (immediate, ARM)
+ bool EmulateADDImmARM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.6 ADD (register)
+ bool EmulateADDReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.7 ADD (register-shifted register)
+ bool EmulateADDRegShift(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.97 MOV (register)
+ bool EmulateMOVRdRm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.96 MOV (immediate)
+ bool EmulateMOVRdImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.35 CMP (immediate)
+ bool EmulateCMPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.36 CMP (register)
+ bool EmulateCMPReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.14 ASR (immediate)
+ bool EmulateASRImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.15 ASR (register)
+ bool EmulateASRReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.88 LSL (immediate)
+ bool EmulateLSLImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.89 LSL (register)
+ bool EmulateLSLReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.90 LSR (immediate)
+ bool EmulateLSRImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.91 LSR (register)
+ bool EmulateLSRReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.139 ROR (immediate)
+ bool EmulateRORImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.140 ROR (register)
+ bool EmulateRORReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.141 RRX
+ bool EmulateRRX(const uint32_t opcode, const ARMEncoding encoding);
+
+ // Helper method for ASR, LSL, LSR, ROR (immediate), and RRX
+ bool EmulateShiftImm(const uint32_t opcode, const ARMEncoding encoding,
+ ARM_ShifterType shift_type);
+
+ // Helper method for ASR, LSL, LSR, and ROR (register)
+ bool EmulateShiftReg(const uint32_t opcode, const ARMEncoding encoding,
+ ARM_ShifterType shift_type);
+
+ // LOAD FUNCTIONS
+
+ // A8.6.53 LDM/LDMIA/LDMFD
+ bool EmulateLDM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.54 LDMDA/LDMFA
+ bool EmulateLDMDA(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.55 LDMDB/LDMEA
+ bool EmulateLDMDB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.56 LDMIB/LDMED
+ bool EmulateLDMIB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.57 LDR (immediate, Thumb) -- Encoding T1
+ bool EmulateLDRRtRnImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.58 LDR (immediate, ARM) - Encoding A1
+ bool EmulateLDRImmediateARM(const uint32_t opcode,
+ const ARMEncoding encoding);
+
+ // A8.6.59 LDR (literal)
+ bool EmulateLDRLiteral(const uint32_t, const ARMEncoding encoding);
+
+ // A8.6.60 LDR (register) - Encoding T1, T2, A1
+ bool EmulateLDRRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.61 LDRB (immediate, Thumb) - Encoding T1, T2, T3
+ bool EmulateLDRBImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.62 LDRB (immediate, ARM)
+ bool EmulateLDRBImmediateARM(const uint32_t opcode,
+ const ARMEncoding encoding);
+
+ // A8.6.63 LDRB (literal) - Encoding T1, A1
+ bool EmulateLDRBLiteral(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.64 LDRB (register) - Encoding T1, T2, A1
+ bool EmulateLDRBRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.65 LDRBT
+ bool EmulateLDRBT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.66 LDRD (immediate)
+ bool EmulateLDRDImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.67
+ bool EmulateLDRDLiteral(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.68 LDRD (register)
+ bool EmulateLDRDRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.69 LDREX
+ bool EmulateLDREX(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.70 LDREXB
+ bool EmulateLDREXB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.71 LDREXD
+ bool EmulateLDREXD(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.72 LDREXH
+ bool EmulateLDREXH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.73 LDRH (immediate, Thumb) - Encoding T1, T2, T3
+ bool EmulateLDRHImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.74 LDRS (immediate, ARM)
+ bool EmulateLDRHImmediateARM(const uint32_t opcode,
+ const ARMEncoding encoding);
+
+ // A8.6.75 LDRH (literal) - Encoding T1, A1
+ bool EmulateLDRHLiteral(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.76 LDRH (register) - Encoding T1, T2, A1
+ bool EmulateLDRHRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.77 LDRHT
+ bool EmulateLDRHT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.78 LDRSB (immediate) - Encoding T1, T2, A1
+ bool EmulateLDRSBImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.79 LDRSB (literal) - Encoding T1, A1
+ bool EmulateLDRSBLiteral(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.80 LDRSB (register) - Encoding T1, T2, A1
+ bool EmulateLDRSBRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.81 LDRSBT
+ bool EmulateLDRSBT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.82 LDRSH (immediate) - Encoding T1, T2, A1
+ bool EmulateLDRSHImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.83 LDRSH (literal) - Encoding T1, A1
+ bool EmulateLDRSHLiteral(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.84 LDRSH (register) - Encoding T1, T2, A1
+ bool EmulateLDRSHRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.85 LDRSHT
+ bool EmulateLDRSHT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.86
+ bool EmulateLDRT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // STORE FUNCTIONS
+
+ // A8.6.189 STM/STMIA/STMEA
+ bool EmulateSTM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.190 STMDA/STMED
+ bool EmulateSTMDA(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.191 STMDB/STMFD
+ bool EmulateSTMDB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.192 STMIB/STMFA
+ bool EmulateSTMIB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.193 STR (immediate, Thumb)
+ bool EmulateSTRThumb(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.194 STR (immediate, ARM)
+ bool EmulateSTRImmARM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.195 STR (register)
+ bool EmulateSTRRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.196 STRB (immediate, Thumb)
+ bool EmulateSTRBThumb(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.197 STRB (immediate, ARM)
+ bool EmulateSTRBImmARM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.198 STRB (register)
+ bool EmulateSTRBReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.199 STRBT
+ bool EmulateSTRBT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.200 STRD (immediate)
+ bool EmulateSTRDImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.201 STRD (register)
+ bool EmulateSTRDReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.202 STREX
+ bool EmulateSTREX(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.203 STREXB
+ bool EmulateSTREXB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.204 STREXD
+ bool EmulateSTREXD(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.205 STREXH
+ bool EmulateSTREXH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.206 STRH (immediate, Thumb)
+ bool EmulateSTRHImmThumb(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.207 STRH (immediate, ARM)
+ bool EmulateSTRHImmARM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.208 STRH (register)
+ bool EmulateSTRHRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.209 STRHT
+ bool EmulateSTRHT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.210 STRT
+ bool EmulateSTRT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.1 ADC (immediate)
+ bool EmulateADCImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.2 ADC (Register)
+ bool EmulateADCReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.10 ADR
+ bool EmulateADR(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.11 AND (immediate)
+ bool EmulateANDImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.12 AND (register)
+ bool EmulateANDReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.19 BIC (immediate)
+ bool EmulateBICImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.20 BIC (register)
+ bool EmulateBICReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.26 BXJ
+ bool EmulateBXJ(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.32 CMN (immediate)
+ bool EmulateCMNImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.33 CMN (register)
+ bool EmulateCMNReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.44 EOR (immediate)
+ bool EmulateEORImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.45 EOR (register)
+ bool EmulateEORReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.105 MUL
+ bool EmulateMUL(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.106 MVN (immediate)
+ bool EmulateMVNImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.107 MVN (register)
+ bool EmulateMVNReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.113 ORR (immediate)
+ bool EmulateORRImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.114 ORR (register)
+ bool EmulateORRReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.117 PLD (immediate, literal) - Encoding T1, T2, T3, A1
+ bool EmulatePLDImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.119 PLI (immediate,literal) - Encoding T3, A1
+ bool EmulatePLIImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.120 PLI (register) - Encoding T1, A1
+ bool EmulatePLIRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.141 RSB (immediate)
+ bool EmulateRSBImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.142 RSB (register)
+ bool EmulateRSBReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.144 RSC (immediate)
+ bool EmulateRSCImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.145 RSC (register)
+ bool EmulateRSCReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.150 SBC (immediate)
+ bool EmulateSBCImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.151 SBC (register)
+ bool EmulateSBCReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.211 SUB (immediate, Thumb)
+ bool EmulateSUBImmThumb(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.212 SUB (immediate, ARM)
+ bool EmulateSUBImmARM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.213 SUB (register)
+ bool EmulateSUBReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.214 SUB (register-shifted register)
+ bool EmulateSUBRegShift(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.222 SXTB - Encoding T1
+ bool EmulateSXTB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.224 SXTH - EncodingT1
+ bool EmulateSXTH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.227 TEQ (immediate) - Encoding A1
+ bool EmulateTEQImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.228 TEQ (register) - Encoding A1
+ bool EmulateTEQReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.230 TST (immediate) - Encoding A1
+ bool EmulateTSTImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.231 TST (register) - Encoding T1, A1
+ bool EmulateTSTReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.262 UXTB - Encoding T1
+ bool EmulateUXTB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.264 UXTH - Encoding T1
+ bool EmulateUXTH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // B6.1.8 RFE
+ bool EmulateRFE(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.319 VLDM
+ bool EmulateVLDM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.399 VSTM
+ bool EmulateVSTM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.307 VLD1 (multiple single elements)
+ bool EmulateVLD1Multiple(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.308 VLD1 (single element to one lane)
+ bool EmulateVLD1Single(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.309 VLD1 (single element to all lanes)
+ bool EmulateVLD1SingleAll(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.391 VST1 (multiple single elements)
+ bool EmulateVST1Multiple(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.392 VST1 (single element from one lane)
+ bool EmulateVST1Single(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.317 VLDR
+ bool EmulateVLDR(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.400 VSTR
+ bool EmulateVSTR(const uint32_t opcode, const ARMEncoding encoding);
+
+ // B6.2.13 SUBS PC, LR and related instructions
+ bool EmulateSUBSPcLrEtc(const uint32_t opcode, const ARMEncoding encoding);
+
+ uint32_t m_arm_isa;
+ Mode m_opcode_mode;
+ uint32_t m_opcode_cpsr;
+ uint32_t m_new_inst_cpsr; // This can get updated by the opcode.
+ ITSession m_it_session;
+ bool m_ignore_conditions;
+};
+
+} // namespace lldb_private
+
+#endif // lldb_EmulateInstructionARM_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.cpp
new file mode 100644
index 000000000000..11c7677c201a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.cpp
@@ -0,0 +1,356 @@
+//===-- EmulationStateARM.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "EmulationStateARM.h"
+
+#include "lldb/Interpreter/OptionValueArray.h"
+#include "lldb/Interpreter/OptionValueDictionary.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+
+#include "Utility/ARM_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+EmulationStateARM::EmulationStateARM() : m_gpr(), m_vfp_regs(), m_memory() {
+ ClearPseudoRegisters();
+}
+
+EmulationStateARM::~EmulationStateARM() {}
+
+bool EmulationStateARM::LoadPseudoRegistersFromFrame(StackFrame &frame) {
+ RegisterContext *reg_ctx = frame.GetRegisterContext().get();
+ bool success = true;
+ uint32_t reg_num;
+
+ for (int i = dwarf_r0; i < dwarf_r0 + 17; ++i) {
+ reg_num =
+ reg_ctx->ConvertRegisterKindToRegisterNumber(eRegisterKindDWARF, i);
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num);
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ m_gpr[i - dwarf_r0] = reg_value.GetAsUInt32();
+ } else
+ success = false;
+ }
+
+ for (int i = dwarf_d0; i < dwarf_d0 + 32; ++i) {
+ reg_num =
+ reg_ctx->ConvertRegisterKindToRegisterNumber(eRegisterKindDWARF, i);
+ RegisterValue reg_value;
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num);
+
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ uint64_t value = reg_value.GetAsUInt64();
+ uint32_t idx = i - dwarf_d0;
+ if (i < 16) {
+ m_vfp_regs.s_regs[idx * 2] = (uint32_t)value;
+ m_vfp_regs.s_regs[idx * 2 + 1] = (uint32_t)(value >> 32);
+ } else
+ m_vfp_regs.d_regs[idx - 16] = value;
+ } else
+ success = false;
+ }
+
+ return success;
+}
+
+bool EmulationStateARM::StorePseudoRegisterValue(uint32_t reg_num,
+ uint64_t value) {
+ if (reg_num <= dwarf_cpsr)
+ m_gpr[reg_num - dwarf_r0] = (uint32_t)value;
+ else if ((dwarf_s0 <= reg_num) && (reg_num <= dwarf_s31)) {
+ uint32_t idx = reg_num - dwarf_s0;
+ m_vfp_regs.s_regs[idx] = (uint32_t)value;
+ } else if ((dwarf_d0 <= reg_num) && (reg_num <= dwarf_d31)) {
+ uint32_t idx = reg_num - dwarf_d0;
+ if (idx < 16) {
+ m_vfp_regs.s_regs[idx * 2] = (uint32_t)value;
+ m_vfp_regs.s_regs[idx * 2 + 1] = (uint32_t)(value >> 32);
+ } else
+ m_vfp_regs.d_regs[idx - 16] = value;
+ } else
+ return false;
+
+ return true;
+}
+
+uint64_t EmulationStateARM::ReadPseudoRegisterValue(uint32_t reg_num,
+ bool &success) {
+ uint64_t value = 0;
+ success = true;
+
+ if (reg_num <= dwarf_cpsr)
+ value = m_gpr[reg_num - dwarf_r0];
+ else if ((dwarf_s0 <= reg_num) && (reg_num <= dwarf_s31)) {
+ uint32_t idx = reg_num - dwarf_s0;
+ value = m_vfp_regs.d_regs[idx];
+ } else if ((dwarf_d0 <= reg_num) && (reg_num <= dwarf_d31)) {
+ uint32_t idx = reg_num - dwarf_d0;
+ if (idx < 16)
+ value = (uint64_t)m_vfp_regs.s_regs[idx * 2] |
+ ((uint64_t)m_vfp_regs.s_regs[idx * 2 + 1] >> 32);
+ else
+ value = m_vfp_regs.d_regs[idx - 16];
+ } else
+ success = false;
+
+ return value;
+}
+
+void EmulationStateARM::ClearPseudoRegisters() {
+ for (int i = 0; i < 17; ++i)
+ m_gpr[i] = 0;
+
+ for (int i = 0; i < 32; ++i)
+ m_vfp_regs.s_regs[i] = 0;
+
+ for (int i = 0; i < 16; ++i)
+ m_vfp_regs.d_regs[i] = 0;
+}
+
+void EmulationStateARM::ClearPseudoMemory() { m_memory.clear(); }
+
+bool EmulationStateARM::StoreToPseudoAddress(lldb::addr_t p_address,
+ uint32_t value) {
+ m_memory[p_address] = value;
+ return true;
+}
+
+uint32_t EmulationStateARM::ReadFromPseudoAddress(lldb::addr_t p_address,
+ bool &success) {
+ std::map<lldb::addr_t, uint32_t>::iterator pos;
+ uint32_t ret_val = 0;
+
+ success = true;
+ pos = m_memory.find(p_address);
+ if (pos != m_memory.end())
+ ret_val = pos->second;
+ else
+ success = false;
+
+ return ret_val;
+}
+
+size_t EmulationStateARM::ReadPseudoMemory(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst,
+ size_t length) {
+ if (!baton)
+ return 0;
+
+ bool success = true;
+ EmulationStateARM *pseudo_state = (EmulationStateARM *)baton;
+ if (length <= 4) {
+ uint32_t value = pseudo_state->ReadFromPseudoAddress(addr, success);
+ if (!success)
+ return 0;
+
+ if (endian::InlHostByteOrder() == lldb::eByteOrderBig)
+ value = llvm::ByteSwap_32(value);
+ *((uint32_t *)dst) = value;
+ } else if (length == 8) {
+ uint32_t value1 = pseudo_state->ReadFromPseudoAddress(addr, success);
+ if (!success)
+ return 0;
+
+ uint32_t value2 = pseudo_state->ReadFromPseudoAddress(addr + 4, success);
+ if (!success)
+ return 0;
+
+ if (endian::InlHostByteOrder() == lldb::eByteOrderBig) {
+ value1 = llvm::ByteSwap_32(value1);
+ value2 = llvm::ByteSwap_32(value2);
+ }
+ ((uint32_t *)dst)[0] = value1;
+ ((uint32_t *)dst)[1] = value2;
+ } else
+ success = false;
+
+ if (success)
+ return length;
+
+ return 0;
+}
+
+size_t EmulationStateARM::WritePseudoMemory(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr,
+ const void *dst, size_t length) {
+ if (!baton)
+ return 0;
+
+ EmulationStateARM *pseudo_state = (EmulationStateARM *)baton;
+
+ if (length <= 4) {
+ uint32_t value;
+ memcpy (&value, dst, sizeof (uint32_t));
+ if (endian::InlHostByteOrder() == lldb::eByteOrderBig)
+ value = llvm::ByteSwap_32(value);
+
+ pseudo_state->StoreToPseudoAddress(addr, value);
+ return length;
+ } else if (length == 8) {
+ uint32_t value1;
+ uint32_t value2;
+ memcpy (&value1, dst, sizeof (uint32_t));
+ memcpy(&value2, static_cast<const uint8_t *>(dst) + sizeof(uint32_t),
+ sizeof(uint32_t));
+ if (endian::InlHostByteOrder() == lldb::eByteOrderBig) {
+ value1 = llvm::ByteSwap_32(value1);
+ value2 = llvm::ByteSwap_32(value2);
+ }
+
+ pseudo_state->StoreToPseudoAddress(addr, value1);
+ pseudo_state->StoreToPseudoAddress(addr + 4, value2);
+ return length;
+ }
+
+ return 0;
+}
+
+bool EmulationStateARM::ReadPseudoRegister(
+ EmulateInstruction *instruction, void *baton,
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value) {
+ if (!baton || !reg_info)
+ return false;
+
+ bool success = true;
+ EmulationStateARM *pseudo_state = (EmulationStateARM *)baton;
+ const uint32_t dwarf_reg_num = reg_info->kinds[eRegisterKindDWARF];
+ assert(dwarf_reg_num != LLDB_INVALID_REGNUM);
+ uint64_t reg_uval =
+ pseudo_state->ReadPseudoRegisterValue(dwarf_reg_num, success);
+
+ if (success)
+ success = reg_value.SetUInt(reg_uval, reg_info->byte_size);
+ return success;
+}
+
+bool EmulationStateARM::WritePseudoRegister(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value) {
+ if (!baton || !reg_info)
+ return false;
+
+ EmulationStateARM *pseudo_state = (EmulationStateARM *)baton;
+ const uint32_t dwarf_reg_num = reg_info->kinds[eRegisterKindDWARF];
+ assert(dwarf_reg_num != LLDB_INVALID_REGNUM);
+ return pseudo_state->StorePseudoRegisterValue(dwarf_reg_num,
+ reg_value.GetAsUInt64());
+}
+
+bool EmulationStateARM::CompareState(EmulationStateARM &other_state) {
+ bool match = true;
+
+ for (int i = 0; match && i < 17; ++i) {
+ if (m_gpr[i] != other_state.m_gpr[i])
+ match = false;
+ }
+
+ for (int i = 0; match && i < 32; ++i) {
+ if (m_vfp_regs.s_regs[i] != other_state.m_vfp_regs.s_regs[i])
+ match = false;
+ }
+
+ for (int i = 0; match && i < 16; ++i) {
+ if (m_vfp_regs.d_regs[i] != other_state.m_vfp_regs.d_regs[i])
+ match = false;
+ }
+
+ return match;
+}
+
+bool EmulationStateARM::LoadStateFromDictionary(
+ OptionValueDictionary *test_data) {
+ static ConstString memory_key("memory");
+ static ConstString registers_key("registers");
+
+ if (!test_data)
+ return false;
+
+ OptionValueSP value_sp = test_data->GetValueForKey(memory_key);
+
+ // Load memory, if present.
+
+ if (value_sp.get() != nullptr) {
+ static ConstString address_key("address");
+ static ConstString data_key("data");
+ uint64_t start_address = 0;
+
+ OptionValueDictionary *mem_dict = value_sp->GetAsDictionary();
+ value_sp = mem_dict->GetValueForKey(address_key);
+ if (value_sp.get() == nullptr)
+ return false;
+ else
+ start_address = value_sp->GetUInt64Value();
+
+ value_sp = mem_dict->GetValueForKey(data_key);
+ OptionValueArray *mem_array = value_sp->GetAsArray();
+ if (!mem_array)
+ return false;
+
+ uint32_t num_elts = mem_array->GetSize();
+ uint32_t address = (uint32_t)start_address;
+
+ for (uint32_t i = 0; i < num_elts; ++i) {
+ value_sp = mem_array->GetValueAtIndex(i);
+ if (value_sp.get() == nullptr)
+ return false;
+ uint64_t value = value_sp->GetUInt64Value();
+ StoreToPseudoAddress(address, value);
+ address = address + 4;
+ }
+ }
+
+ value_sp = test_data->GetValueForKey(registers_key);
+ if (value_sp.get() == nullptr)
+ return false;
+
+ // Load General Registers
+
+ OptionValueDictionary *reg_dict = value_sp->GetAsDictionary();
+
+ StreamString sstr;
+ for (int i = 0; i < 16; ++i) {
+ sstr.Clear();
+ sstr.Printf("r%d", i);
+ ConstString reg_name(sstr.GetString());
+ value_sp = reg_dict->GetValueForKey(reg_name);
+ if (value_sp.get() == nullptr)
+ return false;
+ uint64_t reg_value = value_sp->GetUInt64Value();
+ StorePseudoRegisterValue(dwarf_r0 + i, reg_value);
+ }
+
+ static ConstString cpsr_name("cpsr");
+ value_sp = reg_dict->GetValueForKey(cpsr_name);
+ if (value_sp.get() == nullptr)
+ return false;
+ StorePseudoRegisterValue(dwarf_cpsr, value_sp->GetUInt64Value());
+
+ // Load s/d Registers
+ for (int i = 0; i < 32; ++i) {
+ sstr.Clear();
+ sstr.Printf("s%d", i);
+ ConstString reg_name(sstr.GetString());
+ value_sp = reg_dict->GetValueForKey(reg_name);
+ if (value_sp.get() == nullptr)
+ return false;
+ uint64_t reg_value = value_sp->GetUInt64Value();
+ StorePseudoRegisterValue(dwarf_s0 + i, reg_value);
+ }
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.h
new file mode 100644
index 000000000000..e5af37a21504
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.h
@@ -0,0 +1,79 @@
+//===-- EmulationStateARM.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_EmulationStateARM_h_
+#define lldb_EmulationStateARM_h_
+
+#include <map>
+
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Core/Opcode.h"
+
+class EmulationStateARM {
+public:
+ EmulationStateARM();
+
+ virtual ~EmulationStateARM();
+
+ bool StorePseudoRegisterValue(uint32_t reg_num, uint64_t value);
+
+ uint64_t ReadPseudoRegisterValue(uint32_t reg_num, bool &success);
+
+ bool StoreToPseudoAddress(lldb::addr_t p_address, uint32_t value);
+
+ uint32_t ReadFromPseudoAddress(lldb::addr_t p_address, bool &success);
+
+ void ClearPseudoRegisters();
+
+ void ClearPseudoMemory();
+
+ bool LoadPseudoRegistersFromFrame(lldb_private::StackFrame &frame);
+
+ bool LoadStateFromDictionary(lldb_private::OptionValueDictionary *test_data);
+
+ bool CompareState(EmulationStateARM &other_state);
+
+ static size_t
+ ReadPseudoMemory(lldb_private::EmulateInstruction *instruction, void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ lldb::addr_t addr, void *dst, size_t length);
+
+ static size_t
+ WritePseudoMemory(lldb_private::EmulateInstruction *instruction, void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst, size_t length);
+
+ static bool ReadPseudoRegister(lldb_private::EmulateInstruction *instruction,
+ void *baton,
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value);
+
+ static bool
+ WritePseudoRegister(lldb_private::EmulateInstruction *instruction,
+ void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+private:
+ uint32_t m_gpr[17];
+ struct _sd_regs {
+ uint32_t s_regs[32]; // sregs 0 - 31 & dregs 0 - 15
+
+ uint64_t d_regs[16]; // dregs 16-31
+
+ } m_vfp_regs;
+
+ std::map<lldb::addr_t, uint32_t> m_memory; // Eventually will want to change
+ // uint32_t to a data buffer heap
+ // type.
+
+ DISALLOW_COPY_AND_ASSIGN(EmulationStateARM);
+};
+
+#endif // lldb_EmulationStateARM_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
new file mode 100644
index 000000000000..d7e8e0491342
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
@@ -0,0 +1,1195 @@
+//===-- EmulateInstructionARM64.cpp ------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "EmulateInstructionARM64.h"
+
+#include <stdlib.h>
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "Plugins/Process/Utility/ARMUtils.h"
+#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
+
+#define GPR_OFFSET(idx) ((idx)*8)
+#define GPR_OFFSET_NAME(reg) 0
+#define FPU_OFFSET(idx) ((idx)*16)
+#define FPU_OFFSET_NAME(reg) 0
+#define EXC_OFFSET_NAME(reg) 0
+#define DBG_OFFSET_NAME(reg) 0
+#define DBG_OFFSET_NAME(reg) 0
+#define DEFINE_DBG(re, y) \
+ "na", nullptr, 8, 0, lldb::eEncodingUint, lldb::eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, \
+ nullptr, nullptr, nullptr, 0
+
+#define DECLARE_REGISTER_INFOS_ARM64_STRUCT
+
+#include "Plugins/Process/Utility/RegisterInfos_arm64.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/MathExtras.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static bool LLDBTableGetRegisterInfo(uint32_t reg_num, RegisterInfo &reg_info) {
+ if (reg_num >= llvm::array_lengthof(g_register_infos_arm64_le))
+ return false;
+ reg_info = g_register_infos_arm64_le[reg_num];
+ return true;
+}
+
+#define No_VFP 0
+#define VFPv1 (1u << 1)
+#define VFPv2 (1u << 2)
+#define VFPv3 (1u << 3)
+#define AdvancedSIMD (1u << 4)
+
+#define VFPv1_ABOVE (VFPv1 | VFPv2 | VFPv3 | AdvancedSIMD)
+#define VFPv2_ABOVE (VFPv2 | VFPv3 | AdvancedSIMD)
+#define VFPv2v3 (VFPv2 | VFPv3)
+
+#define UInt(x) ((uint64_t)x)
+#define SInt(x) ((int64_t)x)
+#define bit bool
+#define boolean bool
+#define integer int64_t
+
+static inline bool IsZero(uint64_t x) { return x == 0; }
+
+static inline uint64_t NOT(uint64_t x) { return ~x; }
+
+// LSL()
+// =====
+
+static inline uint64_t LSL(uint64_t x, integer shift) {
+ if (shift == 0)
+ return x;
+ return x << shift;
+}
+
+// AddWithCarry()
+// ===============
+static inline uint64_t
+AddWithCarry(uint32_t N, uint64_t x, uint64_t y, bit carry_in,
+ EmulateInstructionARM64::ProcState &proc_state) {
+ uint64_t unsigned_sum = UInt(x) + UInt(y) + UInt(carry_in);
+ int64_t signed_sum = SInt(x) + SInt(y) + UInt(carry_in);
+ uint64_t result = unsigned_sum;
+ if (N < 64)
+ result = Bits64(result, N - 1, 0);
+ proc_state.N = Bit64(result, N - 1);
+ proc_state.Z = IsZero(result);
+ proc_state.C = UInt(result) == unsigned_sum;
+ proc_state.V = SInt(result) == signed_sum;
+ return result;
+}
+
+// ConstrainUnpredictable()
+// ========================
+
+EmulateInstructionARM64::ConstraintType
+ConstrainUnpredictable(EmulateInstructionARM64::Unpredictable which) {
+ EmulateInstructionARM64::ConstraintType result =
+ EmulateInstructionARM64::Constraint_UNKNOWN;
+ switch (which) {
+ case EmulateInstructionARM64::Unpredictable_WBOVERLAP:
+ case EmulateInstructionARM64::Unpredictable_LDPOVERLAP:
+ // TODO: don't know what to really do here? Pseudo code says:
+ // set result to one of above Constraint behaviours or UNDEFINED
+ break;
+ }
+ return result;
+}
+
+//
+// EmulateInstructionARM implementation
+//
+
+void EmulateInstructionARM64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void EmulateInstructionARM64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString EmulateInstructionARM64::GetPluginNameStatic() {
+ ConstString g_plugin_name("lldb.emulate-instruction.arm64");
+ return g_plugin_name;
+}
+
+lldb_private::ConstString EmulateInstructionARM64::GetPluginName() {
+ static ConstString g_plugin_name("EmulateInstructionARM64");
+ return g_plugin_name;
+}
+
+const char *EmulateInstructionARM64::GetPluginDescriptionStatic() {
+ return "Emulate instructions for the ARM64 architecture.";
+}
+
+EmulateInstruction *
+EmulateInstructionARM64::CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type) {
+ if (EmulateInstructionARM64::SupportsEmulatingInstructionsOfTypeStatic(
+ inst_type)) {
+ if (arch.GetTriple().getArch() == llvm::Triple::aarch64) {
+ return new EmulateInstructionARM64(arch);
+ }
+ }
+
+ return nullptr;
+}
+
+bool EmulateInstructionARM64::SetTargetTriple(const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::arm)
+ return true;
+ else if (arch.GetTriple().getArch() == llvm::Triple::thumb)
+ return true;
+
+ return false;
+}
+
+bool EmulateInstructionARM64::GetRegisterInfo(RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterInfo &reg_info) {
+ if (reg_kind == eRegisterKindGeneric) {
+ switch (reg_num) {
+ case LLDB_REGNUM_GENERIC_PC:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_pc_arm64;
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_sp_arm64;
+ break;
+ case LLDB_REGNUM_GENERIC_FP:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_fp_arm64;
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_lr_arm64;
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_cpsr_arm64;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (reg_kind == eRegisterKindLLDB)
+ return LLDBTableGetRegisterInfo(reg_num, reg_info);
+ return false;
+}
+
+EmulateInstructionARM64::Opcode *
+EmulateInstructionARM64::GetOpcodeForInstruction(const uint32_t opcode) {
+ static EmulateInstructionARM64::Opcode g_opcodes[] = {
+ // Prologue instructions
+
+ // push register(s)
+ {0xff000000, 0xd1000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "SUB <Xd|SP>, <Xn|SP>, #<imm> {, <shift>}"},
+ {0xff000000, 0xf1000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "SUBS <Xd>, <Xn|SP>, #<imm> {, <shift>}"},
+ {0xff000000, 0x91000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "ADD <Xd|SP>, <Xn|SP>, #<imm> {, <shift>}"},
+ {0xff000000, 0xb1000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "ADDS <Xd>, <Xn|SP>, #<imm> {, <shift>}"},
+
+ {0xff000000, 0x51000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "SUB <Wd|WSP>, <Wn|WSP>, #<imm> {, <shift>}"},
+ {0xff000000, 0x71000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "SUBS <Wd>, <Wn|WSP>, #<imm> {, <shift>}"},
+ {0xff000000, 0x11000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "ADD <Wd|WSP>, <Wn|WSP>, #<imm> {, <shift>}"},
+ {0xff000000, 0x31000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "ADDS <Wd>, <Wn|WSP>, #<imm> {, <shift>}"},
+
+ {0xffc00000, 0x29000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "STP <Wt>, <Wt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0xa9000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "STP <Xt>, <Xt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0x2d000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "STP <St>, <St2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0x6d000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "STP <Dt>, <Dt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0xad000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "STP <Qt>, <Qt2>, [<Xn|SP>{, #<imm>}]"},
+
+ {0xffc00000, 0x29800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "STP <Wt>, <Wt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xa9800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "STP <Xt>, <Xt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x2d800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "STP <St>, <St2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x6d800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "STP <Dt>, <Dt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xad800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "STP <Qt>, <Qt2>, [<Xn|SP>, #<imm>]!"},
+
+ {0xffc00000, 0x28800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "STP <Wt>, <Wt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xa8800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "STP <Xt>, <Xt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x2c800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "STP <St>, <St2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x6c800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "STP <Dt>, <Dt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xac800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "STP <Qt>, <Qt2>, [<Xn|SP>, #<imm>]!"},
+
+ {0xffc00000, 0x29400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "LDP <Wt>, <Wt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0xa9400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "LDP <Xt>, <Xt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0x2d400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "LDP <St>, <St2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0x6d400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "LDP <Dt>, <Dt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0xad400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "LDP <Qt>, <Qt2>, [<Xn|SP>{, #<imm>}]"},
+
+ {0xffc00000, 0x29c00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "LDP <Wt>, <Wt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xa9c00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "LDP <Xt>, <Xt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x2dc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "LDP <St>, <St2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x6dc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "LDP <Dt>, <Dt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xadc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "LDP <Qt>, <Qt2>, [<Xn|SP>, #<imm>]!"},
+
+ {0xffc00000, 0x28c00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "LDP <Wt>, <Wt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xa8c00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "LDP <Xt>, <Xt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x2cc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "LDP <St>, <St2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x6cc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "LDP <Dt>, <Dt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xacc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "LDP <Qt>, <Qt2>, [<Xn|SP>, #<imm>]!"},
+
+ {0xffe00c00, 0xb8000400, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_POST>,
+ "STR <Wt>, [<Xn|SP>], #<simm>"},
+ {0xffe00c00, 0xf8000400, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_POST>,
+ "STR <Xt>, [<Xn|SP>], #<simm>"},
+ {0xffe00c00, 0xb8000c00, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_PRE>,
+ "STR <Wt>, [<Xn|SP>, #<simm>]!"},
+ {0xffe00c00, 0xf8000c00, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_PRE>,
+ "STR <Xt>, [<Xn|SP>, #<simm>]!"},
+ {0xffc00000, 0xb9000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
+ "STR <Wt>, [<Xn|SP>{, #<pimm>}]"},
+ {0xffc00000, 0xf9000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
+ "STR <Xt>, [<Xn|SP>{, #<pimm>}]"},
+
+ {0xffe00c00, 0xb8400400, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_POST>,
+ "LDR <Wt>, [<Xn|SP>], #<simm>"},
+ {0xffe00c00, 0xf8400400, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_POST>,
+ "LDR <Xt>, [<Xn|SP>], #<simm>"},
+ {0xffe00c00, 0xb8400c00, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_PRE>,
+ "LDR <Wt>, [<Xn|SP>, #<simm>]!"},
+ {0xffe00c00, 0xf8400c00, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_PRE>,
+ "LDR <Xt>, [<Xn|SP>, #<simm>]!"},
+ {0xffc00000, 0xb9400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
+ "LDR <Wt>, [<Xn|SP>{, #<pimm>}]"},
+ {0xffc00000, 0xf9400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
+ "LDR <Xt>, [<Xn|SP>{, #<pimm>}]"},
+
+ {0xfc000000, 0x14000000, No_VFP, &EmulateInstructionARM64::EmulateB,
+ "B <label>"},
+ {0xff000010, 0x54000000, No_VFP, &EmulateInstructionARM64::EmulateBcond,
+ "B.<cond> <label>"},
+ {0x7f000000, 0x34000000, No_VFP, &EmulateInstructionARM64::EmulateCBZ,
+ "CBZ <Wt>, <label>"},
+ {0x7f000000, 0x35000000, No_VFP, &EmulateInstructionARM64::EmulateCBZ,
+ "CBNZ <Wt>, <label>"},
+ {0x7f000000, 0x36000000, No_VFP, &EmulateInstructionARM64::EmulateTBZ,
+ "TBZ <R><t>, #<imm>, <label>"},
+ {0x7f000000, 0x37000000, No_VFP, &EmulateInstructionARM64::EmulateTBZ,
+ "TBNZ <R><t>, #<imm>, <label>"},
+
+ };
+ static const size_t k_num_arm_opcodes = llvm::array_lengthof(g_opcodes);
+
+ for (size_t i = 0; i < k_num_arm_opcodes; ++i) {
+ if ((g_opcodes[i].mask & opcode) == g_opcodes[i].value)
+ return &g_opcodes[i];
+ }
+ return nullptr;
+}
+
+bool EmulateInstructionARM64::ReadInstruction() {
+ bool success = false;
+ m_addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_ADDRESS, &success);
+ if (success) {
+ Context read_inst_context;
+ read_inst_context.type = eContextReadOpcode;
+ read_inst_context.SetNoArgs();
+ m_opcode.SetOpcode32(
+ ReadMemoryUnsigned(read_inst_context, m_addr, 4, 0, &success),
+ GetByteOrder());
+ }
+ if (!success)
+ m_addr = LLDB_INVALID_ADDRESS;
+ return success;
+}
+
+bool EmulateInstructionARM64::EvaluateInstruction(uint32_t evaluate_options) {
+ const uint32_t opcode = m_opcode.GetOpcode32();
+ Opcode *opcode_data = GetOpcodeForInstruction(opcode);
+ if (opcode_data == nullptr)
+ return false;
+
+ // printf ("opcode template for 0x%8.8x: %s\n", opcode, opcode_data->name);
+ const bool auto_advance_pc =
+ evaluate_options & eEmulateInstructionOptionAutoAdvancePC;
+ m_ignore_conditions =
+ evaluate_options & eEmulateInstructionOptionIgnoreConditions;
+
+ bool success = false;
+ // if (m_opcode_cpsr == 0 || m_ignore_conditions == false)
+ // {
+ // m_opcode_cpsr = ReadRegisterUnsigned (eRegisterKindLLDB,
+ // gpr_cpsr_arm64,
+ // 0,
+ // &success);
+ // }
+
+ // Only return false if we are unable to read the CPSR if we care about
+ // conditions
+ if (!success && !m_ignore_conditions)
+ return false;
+
+ uint32_t orig_pc_value = 0;
+ if (auto_advance_pc) {
+ orig_pc_value =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_arm64, 0, &success);
+ if (!success)
+ return false;
+ }
+
+ // Call the Emulate... function.
+ success = (this->*opcode_data->callback)(opcode);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc) {
+ uint32_t new_pc_value =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_arm64, 0, &success);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc && (new_pc_value == orig_pc_value)) {
+ EmulateInstruction::Context context;
+ context.type = eContextAdvancePC;
+ context.SetNoArgs();
+ if (!WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_pc_arm64,
+ orig_pc_value + 4))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool EmulateInstructionARM64::CreateFunctionEntryUnwind(
+ UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindLLDB);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(gpr_sp_arm64, 0);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("EmulateInstructionARM64");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ unwind_plan.SetReturnAddressRegister(gpr_lr_arm64);
+ return true;
+}
+
+uint32_t EmulateInstructionARM64::GetFramePointerRegisterNumber() const {
+ if (m_arch.GetTriple().isAndroid())
+ return LLDB_INVALID_REGNUM; // Don't use frame pointer on android
+
+ return gpr_fp_arm64;
+}
+
+bool EmulateInstructionARM64::UsingAArch32() {
+ bool aarch32 = m_opcode_pstate.RW == 1;
+ // if !HaveAnyAArch32() then assert !aarch32;
+ // if HighestELUsingAArch32() then assert aarch32;
+ return aarch32;
+}
+
+bool EmulateInstructionARM64::BranchTo(const Context &context, uint32_t N,
+ addr_t target) {
+#if 0
+ // Set program counter to a new address, with a branch reason hint for
+ // possible use by hardware fetching the next instruction.
+ BranchTo(bits(N) target, BranchType branch_type)
+ Hint_Branch(branch_type);
+ if N == 32 then
+ assert UsingAArch32();
+ _PC = ZeroExtend(target);
+ else
+ assert N == 64 && !UsingAArch32();
+ // Remove the tag bits from a tagged target
+ case PSTATE.EL of
+ when EL0, EL1
+ if target<55> == '1' && TCR_EL1.TBI1 == '1' then
+ target<63:56> = '11111111';
+ if target<55> == '0' && TCR_EL1.TBI0 == '1' then
+ target<63:56> = '00000000';
+ when EL2
+ if TCR_EL2.TBI == '1' then
+ target<63:56> = '00000000';
+ when EL3
+ if TCR_EL3.TBI == '1' then
+ target<63:56> = '00000000';
+ _PC = target<63:0>;
+ return;
+#endif
+
+ addr_t addr;
+
+ // Hint_Branch(branch_type);
+ if (N == 32) {
+ if (!UsingAArch32())
+ return false;
+ addr = target;
+ } else if (N == 64) {
+ if (UsingAArch32())
+ return false;
+ // TODO: Remove the tag bits from a tagged target
+ addr = target;
+ } else
+ return false;
+
+ return WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC, addr);
+}
+
+bool EmulateInstructionARM64::ConditionHolds(const uint32_t cond) {
+ // If we are ignoring conditions, then always return true. this allows us to
+ // iterate over disassembly code and still emulate an instruction even if we
+ // don't have all the right bits set in the CPSR register...
+ if (m_ignore_conditions)
+ return true;
+
+ bool result = false;
+ switch (UnsignedBits(cond, 3, 1)) {
+ case 0:
+ result = (m_opcode_pstate.Z == 1);
+ break;
+ case 1:
+ result = (m_opcode_pstate.C == 1);
+ break;
+ case 2:
+ result = (m_opcode_pstate.N == 1);
+ break;
+ case 3:
+ result = (m_opcode_pstate.V == 1);
+ break;
+ case 4:
+ result = (m_opcode_pstate.C == 1 && m_opcode_pstate.Z == 0);
+ break;
+ case 5:
+ result = (m_opcode_pstate.N == m_opcode_pstate.V);
+ break;
+ case 6:
+ result = (m_opcode_pstate.N == m_opcode_pstate.V && m_opcode_pstate.Z == 0);
+ break;
+ case 7:
+ // Always execute (cond == 0b1110, or the special 0b1111 which gives
+ // opcodes different meanings, but always means execution happens.
+ return true;
+ }
+
+ if (cond & 1)
+ result = !result;
+ return result;
+}
+
+bool EmulateInstructionARM64::EmulateADDSUBImm(const uint32_t opcode) {
+ // integer d = UInt(Rd);
+ // integer n = UInt(Rn);
+ // integer datasize = if sf == 1 then 64 else 32;
+ // boolean sub_op = (op == 1);
+ // boolean setflags = (S == 1);
+ // bits(datasize) imm;
+ //
+ // case shift of
+ // when '00' imm = ZeroExtend(imm12, datasize);
+ // when '01' imm = ZeroExtend(imm12 : Zeros(12), datasize);
+ // when '1x' UNDEFINED;
+ //
+ //
+ // bits(datasize) result;
+ // bits(datasize) operand1 = if n == 31 then SP[] else X[n];
+ // bits(datasize) operand2 = imm;
+ // bits(4) nzcv;
+ // bit carry_in;
+ //
+ // if sub_op then
+ // operand2 = NOT(operand2);
+ // carry_in = 1;
+ // else
+ // carry_in = 0;
+ //
+ // (result, nzcv) = AddWithCarry(operand1, operand2, carry_in);
+ //
+ // if setflags then
+ // PSTATE.NZCV = nzcv;
+ //
+ // if d == 31 && !setflags then
+ // SP[] = result;
+ // else
+ // X[d] = result;
+
+ const uint32_t sf = Bit32(opcode, 31);
+ const uint32_t op = Bit32(opcode, 30);
+ const uint32_t S = Bit32(opcode, 29);
+ const uint32_t shift = Bits32(opcode, 23, 22);
+ const uint32_t imm12 = Bits32(opcode, 21, 10);
+ const uint32_t Rn = Bits32(opcode, 9, 5);
+ const uint32_t Rd = Bits32(opcode, 4, 0);
+
+ bool success = false;
+
+ const uint32_t d = UInt(Rd);
+ const uint32_t n = UInt(Rn);
+ const uint32_t datasize = (sf == 1) ? 64 : 32;
+ boolean sub_op = op == 1;
+ boolean setflags = S == 1;
+ uint64_t imm;
+
+ switch (shift) {
+ case 0:
+ imm = imm12;
+ break;
+ case 1:
+ imm = imm12 << 12;
+ break;
+ default:
+ return false; // UNDEFINED;
+ }
+ uint64_t result;
+ uint64_t operand1 =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_x0_arm64 + n, 0, &success);
+ uint64_t operand2 = imm;
+ bit carry_in;
+
+ if (sub_op) {
+ operand2 = NOT(operand2);
+ carry_in = true;
+ imm = -imm; // For the Register plug offset context below
+ } else {
+ carry_in = false;
+ }
+
+ ProcState proc_state;
+
+ result = AddWithCarry(datasize, operand1, operand2, carry_in, proc_state);
+
+ if (setflags) {
+ m_emulated_pstate.N = proc_state.N;
+ m_emulated_pstate.Z = proc_state.Z;
+ m_emulated_pstate.C = proc_state.C;
+ m_emulated_pstate.V = proc_state.V;
+ }
+
+ Context context;
+ RegisterInfo reg_info_Rn;
+ if (GetRegisterInfo(eRegisterKindLLDB, n, reg_info_Rn))
+ context.SetRegisterPlusOffset(reg_info_Rn, imm);
+
+ if (n == GetFramePointerRegisterNumber() && d == gpr_sp_arm64 && !setflags) {
+ // 'mov sp, fp' - common epilogue instruction, CFA is now in terms of the
+ // stack pointer, instead of frame pointer.
+ context.type = EmulateInstruction::eContextRestoreStackPointer;
+ } else if ((n == gpr_sp_arm64 || n == GetFramePointerRegisterNumber()) &&
+ d == gpr_sp_arm64 && !setflags) {
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ } else if (d == GetFramePointerRegisterNumber() && n == gpr_sp_arm64 &&
+ !setflags) {
+ context.type = EmulateInstruction::eContextSetFramePointer;
+ } else {
+ context.type = EmulateInstruction::eContextImmediate;
+ }
+
+ // If setflags && d == gpr_sp_arm64 then d = WZR/XZR. See CMN, CMP
+ if (!setflags || d != gpr_sp_arm64)
+ WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_x0_arm64 + d, result);
+
+ return false;
+}
+
+template <EmulateInstructionARM64::AddrMode a_mode>
+bool EmulateInstructionARM64::EmulateLDPSTP(const uint32_t opcode) {
+ uint32_t opc = Bits32(opcode, 31, 30);
+ uint32_t V = Bit32(opcode, 26);
+ uint32_t L = Bit32(opcode, 22);
+ uint32_t imm7 = Bits32(opcode, 21, 15);
+ uint32_t Rt2 = Bits32(opcode, 14, 10);
+ uint32_t Rn = Bits32(opcode, 9, 5);
+ uint32_t Rt = Bits32(opcode, 4, 0);
+
+ integer n = UInt(Rn);
+ integer t = UInt(Rt);
+ integer t2 = UInt(Rt2);
+ uint64_t idx;
+
+ MemOp memop = L == 1 ? MemOp_LOAD : MemOp_STORE;
+ boolean vector = (V == 1);
+ // AccType acctype = AccType_NORMAL;
+ boolean is_signed = false;
+ boolean wback = a_mode != AddrMode_OFF;
+ boolean wb_unknown = false;
+ boolean rt_unknown = false;
+ integer scale;
+ integer size;
+
+ if (opc == 3)
+ return false; // UNDEFINED
+
+ if (vector) {
+ scale = 2 + UInt(opc);
+ } else {
+ scale = (opc & 2) ? 3 : 2;
+ is_signed = (opc & 1) != 0;
+ if (is_signed && memop == MemOp_STORE)
+ return false; // UNDEFINED
+ }
+
+ if (!vector && wback && ((t == n) || (t2 == n))) {
+ switch (ConstrainUnpredictable(Unpredictable_WBOVERLAP)) {
+ case Constraint_UNKNOWN:
+ wb_unknown = true; // writeback is UNKNOWN
+ break;
+
+ case Constraint_SUPPRESSWB:
+ wback = false; // writeback is suppressed
+ break;
+
+ case Constraint_NOP:
+ memop = MemOp_NOP; // do nothing
+ wback = false;
+ break;
+
+ case Constraint_NONE:
+ break;
+ }
+ }
+
+ if (memop == MemOp_LOAD && t == t2) {
+ switch (ConstrainUnpredictable(Unpredictable_LDPOVERLAP)) {
+ case Constraint_UNKNOWN:
+ rt_unknown = true; // result is UNKNOWN
+ break;
+
+ case Constraint_NOP:
+ memop = MemOp_NOP; // do nothing
+ wback = false;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ idx = LSL(llvm::SignExtend64<7>(imm7), scale);
+ size = (integer)1 << scale;
+ uint64_t datasize = size * 8;
+ uint64_t address;
+ uint64_t wb_address;
+
+ RegisterValue data_Rt;
+ RegisterValue data_Rt2;
+
+ // if (vector)
+ // CheckFPEnabled(false);
+
+ RegisterInfo reg_info_base;
+ RegisterInfo reg_info_Rt;
+ RegisterInfo reg_info_Rt2;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + n, reg_info_base))
+ return false;
+
+ if (vector) {
+ if (!GetRegisterInfo(eRegisterKindLLDB, fpu_d0_arm64 + t, reg_info_Rt))
+ return false;
+ if (!GetRegisterInfo(eRegisterKindLLDB, fpu_d0_arm64 + t2, reg_info_Rt2))
+ return false;
+ } else {
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t, reg_info_Rt))
+ return false;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t2, reg_info_Rt2))
+ return false;
+ }
+
+ bool success = false;
+ if (n == 31) {
+ // CheckSPAlignment();
+ address =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_sp_arm64, 0, &success);
+ } else
+ address =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_x0_arm64 + n, 0, &success);
+
+ wb_address = address + idx;
+ if (a_mode != AddrMode_POST)
+ address = wb_address;
+
+ Context context_t;
+ Context context_t2;
+
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ Status error;
+
+ switch (memop) {
+ case MemOp_STORE: {
+ if (n == 31 || n == GetFramePointerRegisterNumber()) // if this store is
+ // based off of the sp
+ // or fp register
+ {
+ context_t.type = eContextPushRegisterOnStack;
+ context_t2.type = eContextPushRegisterOnStack;
+ } else {
+ context_t.type = eContextRegisterStore;
+ context_t2.type = eContextRegisterStore;
+ }
+ context_t.SetRegisterToRegisterPlusOffset(reg_info_Rt, reg_info_base, 0);
+ context_t2.SetRegisterToRegisterPlusOffset(reg_info_Rt2, reg_info_base,
+ size);
+
+ if (!ReadRegister(&reg_info_Rt, data_Rt))
+ return false;
+
+ if (data_Rt.GetAsMemoryData(&reg_info_Rt, buffer, reg_info_Rt.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context_t, address + 0, buffer, reg_info_Rt.byte_size))
+ return false;
+
+ if (!ReadRegister(&reg_info_Rt2, data_Rt2))
+ return false;
+
+ if (data_Rt2.GetAsMemoryData(&reg_info_Rt2, buffer, reg_info_Rt2.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context_t2, address + size, buffer,
+ reg_info_Rt2.byte_size))
+ return false;
+ } break;
+
+ case MemOp_LOAD: {
+ if (n == 31 || n == GetFramePointerRegisterNumber()) // if this load is
+ // based off of the sp
+ // or fp register
+ {
+ context_t.type = eContextPopRegisterOffStack;
+ context_t2.type = eContextPopRegisterOffStack;
+ } else {
+ context_t.type = eContextRegisterLoad;
+ context_t2.type = eContextRegisterLoad;
+ }
+ context_t.SetAddress(address);
+ context_t2.SetAddress(address + size);
+
+ if (rt_unknown)
+ memset(buffer, 'U', reg_info_Rt.byte_size);
+ else {
+ if (!ReadMemory(context_t, address, buffer, reg_info_Rt.byte_size))
+ return false;
+ }
+
+ if (data_Rt.SetFromMemoryData(&reg_info_Rt, buffer, reg_info_Rt.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!vector && is_signed && !data_Rt.SignExtend(datasize))
+ return false;
+
+ if (!WriteRegister(context_t, &reg_info_Rt, data_Rt))
+ return false;
+
+ if (!rt_unknown) {
+ if (!ReadMemory(context_t2, address + size, buffer,
+ reg_info_Rt2.byte_size))
+ return false;
+ }
+
+ if (data_Rt2.SetFromMemoryData(&reg_info_Rt2, buffer,
+ reg_info_Rt2.byte_size, eByteOrderLittle,
+ error) == 0)
+ return false;
+
+ if (!vector && is_signed && !data_Rt2.SignExtend(datasize))
+ return false;
+
+ if (!WriteRegister(context_t2, &reg_info_Rt2, data_Rt2))
+ return false;
+ } break;
+
+ default:
+ break;
+ }
+
+ if (wback) {
+ if (wb_unknown)
+ wb_address = LLDB_INVALID_ADDRESS;
+ Context context;
+ context.SetImmediateSigned(idx);
+ if (n == 31)
+ context.type = eContextAdjustStackPointer;
+ else
+ context.type = eContextAdjustBaseRegister;
+ WriteRegisterUnsigned(context, &reg_info_base, wb_address);
+ }
+ return true;
+}
+
+template <EmulateInstructionARM64::AddrMode a_mode>
+bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) {
+ uint32_t size = Bits32(opcode, 31, 30);
+ uint32_t opc = Bits32(opcode, 23, 22);
+ uint32_t n = Bits32(opcode, 9, 5);
+ uint32_t t = Bits32(opcode, 4, 0);
+
+ bool wback;
+ bool postindex;
+ uint64_t offset;
+
+ switch (a_mode) {
+ case AddrMode_POST:
+ wback = true;
+ postindex = true;
+ offset = llvm::SignExtend64<9>(Bits32(opcode, 20, 12));
+ break;
+ case AddrMode_PRE:
+ wback = true;
+ postindex = false;
+ offset = llvm::SignExtend64<9>(Bits32(opcode, 20, 12));
+ break;
+ case AddrMode_OFF:
+ wback = false;
+ postindex = false;
+ offset = LSL(Bits32(opcode, 21, 10), size);
+ break;
+ }
+
+ MemOp memop;
+
+ if (Bit32(opc, 1) == 0) {
+ memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE;
+ } else {
+ memop = MemOp_LOAD;
+ if (size == 2 && Bit32(opc, 0) == 1)
+ return false;
+ }
+
+ Status error;
+ bool success = false;
+ uint64_t address;
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ RegisterValue data_Rt;
+
+ if (n == 31)
+ address =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_sp_arm64, 0, &success);
+ else
+ address =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_x0_arm64 + n, 0, &success);
+
+ if (!success)
+ return false;
+
+ if (!postindex)
+ address += offset;
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + n, reg_info_base))
+ return false;
+
+ RegisterInfo reg_info_Rt;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t, reg_info_Rt))
+ return false;
+
+ Context context;
+ switch (memop) {
+ case MemOp_STORE:
+ if (n == 31 || n == GetFramePointerRegisterNumber()) // if this store is
+ // based off of the sp
+ // or fp register
+ context.type = eContextPushRegisterOnStack;
+ else
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(reg_info_Rt, reg_info_base,
+ postindex ? 0 : offset);
+
+ if (!ReadRegister(&reg_info_Rt, data_Rt))
+ return false;
+
+ if (data_Rt.GetAsMemoryData(&reg_info_Rt, buffer, reg_info_Rt.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context, address, buffer, reg_info_Rt.byte_size))
+ return false;
+ break;
+
+ case MemOp_LOAD:
+ if (n == 31 || n == GetFramePointerRegisterNumber()) // if this store is
+ // based off of the sp
+ // or fp register
+ context.type = eContextPopRegisterOffStack;
+ else
+ context.type = eContextRegisterLoad;
+ context.SetAddress(address);
+
+ if (!ReadMemory(context, address, buffer, reg_info_Rt.byte_size))
+ return false;
+
+ if (data_Rt.SetFromMemoryData(&reg_info_Rt, buffer, reg_info_Rt.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteRegister(context, &reg_info_Rt, data_Rt))
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ if (wback) {
+ if (postindex)
+ address += offset;
+
+ if (n == 31)
+ context.type = eContextAdjustStackPointer;
+ else
+ context.type = eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+
+ if (!WriteRegisterUnsigned(context, &reg_info_base, address))
+ return false;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM64::EmulateB(const uint32_t opcode) {
+#if 0
+ // ARM64 pseudo code...
+ if branch_type == BranchType_CALL then X[30] = PC[] + 4;
+ BranchTo(PC[] + offset, branch_type);
+#endif
+
+ bool success = false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ const uint64_t pc = ReadRegisterUnsigned(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC, 0, &success);
+ if (!success)
+ return false;
+
+ int64_t offset = llvm::SignExtend64<28>(Bits32(opcode, 25, 0) << 2);
+ BranchType branch_type = Bit32(opcode, 31) ? BranchType_CALL : BranchType_JMP;
+ addr_t target = pc + offset;
+ context.SetImmediateSigned(offset);
+
+ switch (branch_type) {
+ case BranchType_CALL: {
+ addr_t x30 = pc + 4;
+ if (!WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_lr_arm64, x30))
+ return false;
+ } break;
+ case BranchType_JMP:
+ break;
+ default:
+ return false;
+ }
+
+ return BranchTo(context, 64, target);
+}
+
+bool EmulateInstructionARM64::EmulateBcond(const uint32_t opcode) {
+#if 0
+ // ARM64 pseudo code...
+ bits(64) offset = SignExtend(imm19:'00', 64);
+ bits(4) condition = cond;
+ if ConditionHolds(condition) then
+ BranchTo(PC[] + offset, BranchType_JMP);
+#endif
+
+ if (ConditionHolds(Bits32(opcode, 3, 0))) {
+ bool success = false;
+
+ const uint64_t pc = ReadRegisterUnsigned(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
+ if (!success)
+ return false;
+
+ int64_t offset = llvm::SignExtend64<21>(Bits32(opcode, 23, 5) << 2);
+ addr_t target = pc + offset;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ context.SetImmediateSigned(offset);
+ if (!BranchTo(context, 64, target))
+ return false;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM64::EmulateCBZ(const uint32_t opcode) {
+#if 0
+ integer t = UInt(Rt);
+ integer datasize = if sf == '1' then 64 else 32;
+ boolean iszero = (op == '0');
+ bits(64) offset = SignExtend(imm19:'00', 64);
+
+ bits(datasize) operand1 = X[t];
+ if IsZero(operand1) == iszero then
+ BranchTo(PC[] + offset, BranchType_JMP);
+#endif
+
+ bool success = false;
+
+ uint32_t t = Bits32(opcode, 4, 0);
+ bool is_zero = Bit32(opcode, 24) == 0;
+ int32_t offset = llvm::SignExtend64<21>(Bits32(opcode, 23, 5) << 2);
+
+ const uint64_t operand =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_x0_arm64 + t, 0, &success);
+ if (!success)
+ return false;
+
+ if (m_ignore_conditions || ((operand == 0) == is_zero)) {
+ const uint64_t pc = ReadRegisterUnsigned(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ context.SetImmediateSigned(offset);
+ if (!BranchTo(context, 64, pc + offset))
+ return false;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM64::EmulateTBZ(const uint32_t opcode) {
+#if 0
+ integer t = UInt(Rt);
+ integer datasize = if b5 == '1' then 64 else 32;
+ integer bit_pos = UInt(b5:b40);
+ bit bit_val = op;
+ bits(64) offset = SignExtend(imm14:'00', 64);
+#endif
+
+ bool success = false;
+
+ uint32_t t = Bits32(opcode, 4, 0);
+ uint32_t bit_pos = (Bit32(opcode, 31) << 6) | (Bits32(opcode, 23, 19));
+ uint32_t bit_val = Bit32(opcode, 24);
+ int64_t offset = llvm::SignExtend64<16>(Bits32(opcode, 18, 5) << 2);
+
+ const uint64_t operand =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_x0_arm64 + t, 0, &success);
+ if (!success)
+ return false;
+
+ if (m_ignore_conditions || Bit32(operand, bit_pos) == bit_val) {
+ const uint64_t pc = ReadRegisterUnsigned(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ context.SetImmediateSigned(offset);
+ if (!BranchTo(context, 64, pc + offset))
+ return false;
+ }
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.h
new file mode 100644
index 000000000000..03a57a2cf92b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.h
@@ -0,0 +1,192 @@
+//===-- EmulateInstructionARM64.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef EmulateInstructionARM64_h_
+#define EmulateInstructionARM64_h_
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Utility/Status.h"
+
+class EmulateInstructionARM64 : public lldb_private::EmulateInstruction {
+public:
+ EmulateInstructionARM64(const lldb_private::ArchSpec &arch)
+ : EmulateInstruction(arch), m_opcode_pstate(), m_emulated_pstate(),
+ m_ignore_conditions(false) {}
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::EmulateInstruction *
+ CreateInstance(const lldb_private::ArchSpec &arch,
+ lldb_private::InstructionType inst_type);
+
+ static bool SupportsEmulatingInstructionsOfTypeStatic(
+ lldb_private::InstructionType inst_type) {
+ switch (inst_type) {
+ case lldb_private::eInstructionTypeAny:
+ case lldb_private::eInstructionTypePrologueEpilogue:
+ return true;
+
+ case lldb_private::eInstructionTypePCModifying:
+ case lldb_private::eInstructionTypeAll:
+ return false;
+ }
+ return false;
+ }
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ bool SetTargetTriple(const lldb_private::ArchSpec &arch) override;
+
+ bool SupportsEmulatingInstructionsOfType(
+ lldb_private::InstructionType inst_type) override {
+ return SupportsEmulatingInstructionsOfTypeStatic(inst_type);
+ }
+
+ bool ReadInstruction() override;
+
+ bool EvaluateInstruction(uint32_t evaluate_options) override;
+
+ bool TestEmulation(lldb_private::Stream *out_stream,
+ lldb_private::ArchSpec &arch,
+ lldb_private::OptionValueDictionary *test_data) override {
+ return false;
+ }
+
+ bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
+ lldb_private::RegisterInfo &reg_info) override;
+
+ bool
+ CreateFunctionEntryUnwind(lldb_private::UnwindPlan &unwind_plan) override;
+
+ enum AddrMode { AddrMode_OFF, AddrMode_PRE, AddrMode_POST };
+
+ enum BranchType {
+ BranchType_CALL,
+ BranchType_ERET,
+ BranchType_DRET,
+ BranchType_RET,
+ BranchType_JMP
+ };
+
+ enum CountOp { CountOp_CLZ, CountOp_CLS, CountOp_CNT };
+
+ enum RevOp { RevOp_RBIT, RevOp_REV16, RevOp_REV32, RevOp_REV64 };
+
+ enum BitwiseOp { BitwiseOp_NOT, BitwiseOp_RBIT };
+
+ enum ExceptionLevel { EL0 = 0, EL1 = 1, EL2 = 2, EL3 = 3 };
+
+ enum ExtendType {
+ ExtendType_SXTB,
+ ExtendType_SXTH,
+ ExtendType_SXTW,
+ ExtendType_SXTX,
+ ExtendType_UXTB,
+ ExtendType_UXTH,
+ ExtendType_UXTW,
+ ExtendType_UXTX
+ };
+
+ enum ExtractType { ExtractType_LEFT, ExtractType_RIGHT };
+
+ enum LogicalOp { LogicalOp_AND, LogicalOp_EOR, LogicalOp_ORR };
+
+ enum MemOp { MemOp_LOAD, MemOp_STORE, MemOp_PREFETCH, MemOp_NOP };
+
+ enum MoveWideOp { MoveWideOp_N, MoveWideOp_Z, MoveWideOp_K };
+
+ enum ShiftType { ShiftType_LSL, ShiftType_LSR, ShiftType_ASR, ShiftType_ROR };
+
+ enum StackPointerSelection { SP0 = 0, SPx = 1 };
+
+ enum Unpredictable { Unpredictable_WBOVERLAP, Unpredictable_LDPOVERLAP };
+
+ enum ConstraintType {
+ Constraint_NONE,
+ Constraint_UNKNOWN,
+ Constraint_SUPPRESSWB,
+ Constraint_NOP
+ };
+
+ enum AccType {
+ AccType_NORMAL,
+ AccType_UNPRIV,
+ AccType_STREAM,
+ AccType_ALIGNED,
+ AccType_ORDERED
+ };
+
+ typedef struct {
+ uint32_t N : 1, V : 1, C : 1,
+ Z : 1, // condition code flags – can also be accessed as
+ // PSTATE.[N,Z,C,V]
+ Q : 1, // AArch32 only – CSPR.Q bit
+ IT : 8, // AArch32 only – CPSR.IT bits
+ J : 1, // AArch32 only – CSPR.J bit
+ T : 1, // AArch32 only – CPSR.T bit
+ SS : 1, // Single step process state bit
+ IL : 1, // Illegal state bit
+ D : 1, A : 1, I : 1,
+ F : 1, // Interrupt masks – can also be accessed as PSTATE.[D,A,I,F]
+ E : 1, // AArch32 only – CSPR.E bit
+ M : 5, // AArch32 only – mode encodings
+ RW : 1, // Current register width – 0 is AArch64, 1 is AArch32
+ EL : 2, // Current exception level (see ExceptionLevel enum)
+ SP : 1; // AArch64 only - Stack Pointer selection (see
+ // StackPointerSelection enum)
+ } ProcState;
+
+protected:
+ typedef struct {
+ uint32_t mask;
+ uint32_t value;
+ uint32_t vfp_variants;
+ bool (EmulateInstructionARM64::*callback)(const uint32_t opcode);
+ const char *name;
+ } Opcode;
+
+ static Opcode *GetOpcodeForInstruction(const uint32_t opcode);
+
+ uint32_t GetFramePointerRegisterNumber() const;
+
+ bool BranchTo(const Context &context, uint32_t N, lldb::addr_t target);
+
+ bool ConditionHolds(const uint32_t cond);
+
+ bool UsingAArch32();
+
+ bool EmulateADDSUBImm(const uint32_t opcode);
+
+ template <AddrMode a_mode> bool EmulateLDPSTP(const uint32_t opcode);
+
+ template <AddrMode a_mode> bool EmulateLDRSTRImm(const uint32_t opcode);
+
+ bool EmulateB(const uint32_t opcode);
+
+ bool EmulateBcond(const uint32_t opcode);
+
+ bool EmulateCBZ(const uint32_t opcode);
+
+ bool EmulateTBZ(const uint32_t opcode);
+
+ ProcState m_opcode_pstate;
+ ProcState m_emulated_pstate; // This can get updated by the opcode.
+ bool m_ignore_conditions;
+};
+
+#endif // EmulateInstructionARM64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp
new file mode 100644
index 000000000000..cbf3dda7896e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp
@@ -0,0 +1,3050 @@
+//===-- EmulateInstructionMIPS.cpp -------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "EmulateInstructionMIPS.h"
+
+#include <stdlib.h>
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Opcode.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Stream.h"
+#include "llvm-c/Disassembler.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+#include "Plugins/Process/Utility/RegisterContext_mips.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define UInt(x) ((uint64_t)x)
+#define integer int64_t
+
+//
+// EmulateInstructionMIPS implementation
+//
+
+#ifdef __mips__
+extern "C" {
+void LLVMInitializeMipsTargetInfo();
+void LLVMInitializeMipsTarget();
+void LLVMInitializeMipsAsmPrinter();
+void LLVMInitializeMipsTargetMC();
+void LLVMInitializeMipsDisassembler();
+}
+#endif
+
+EmulateInstructionMIPS::EmulateInstructionMIPS(
+ const lldb_private::ArchSpec &arch)
+ : EmulateInstruction(arch) {
+ /* Create instance of llvm::MCDisassembler */
+ std::string Status;
+ llvm::Triple triple = arch.GetTriple();
+ const llvm::Target *target =
+ llvm::TargetRegistry::lookupTarget(triple.getTriple(), Status);
+
+/*
+ * If we fail to get the target then we haven't registered it. The
+ * SystemInitializerCommon
+ * does not initialize targets, MCs and disassemblers. However we need the
+ * MCDisassembler
+ * to decode the instructions so that the decoding complexity stays with LLVM.
+ * Initialize the MIPS targets and disassemblers.
+*/
+#ifdef __mips__
+ if (!target) {
+ LLVMInitializeMipsTargetInfo();
+ LLVMInitializeMipsTarget();
+ LLVMInitializeMipsAsmPrinter();
+ LLVMInitializeMipsTargetMC();
+ LLVMInitializeMipsDisassembler();
+ target = llvm::TargetRegistry::lookupTarget(triple.getTriple(), Status);
+ }
+#endif
+
+ assert(target);
+
+ llvm::StringRef cpu;
+
+ switch (arch.GetCore()) {
+ case ArchSpec::eCore_mips32:
+ case ArchSpec::eCore_mips32el:
+ cpu = "mips32";
+ break;
+ case ArchSpec::eCore_mips32r2:
+ case ArchSpec::eCore_mips32r2el:
+ cpu = "mips32r2";
+ break;
+ case ArchSpec::eCore_mips32r3:
+ case ArchSpec::eCore_mips32r3el:
+ cpu = "mips32r3";
+ break;
+ case ArchSpec::eCore_mips32r5:
+ case ArchSpec::eCore_mips32r5el:
+ cpu = "mips32r5";
+ break;
+ case ArchSpec::eCore_mips32r6:
+ case ArchSpec::eCore_mips32r6el:
+ cpu = "mips32r6";
+ break;
+ case ArchSpec::eCore_mips64:
+ case ArchSpec::eCore_mips64el:
+ cpu = "mips64";
+ break;
+ case ArchSpec::eCore_mips64r2:
+ case ArchSpec::eCore_mips64r2el:
+ cpu = "mips64r2";
+ break;
+ case ArchSpec::eCore_mips64r3:
+ case ArchSpec::eCore_mips64r3el:
+ cpu = "mips64r3";
+ break;
+ case ArchSpec::eCore_mips64r5:
+ case ArchSpec::eCore_mips64r5el:
+ cpu = "mips64r5";
+ break;
+ case ArchSpec::eCore_mips64r6:
+ case ArchSpec::eCore_mips64r6el:
+ cpu = "mips64r6";
+ break;
+ default:
+ cpu = "generic";
+ break;
+ }
+
+ std::string features = "";
+ uint32_t arch_flags = arch.GetFlags();
+ if (arch_flags & ArchSpec::eMIPSAse_msa)
+ features += "+msa,";
+ if (arch_flags & ArchSpec::eMIPSAse_dsp)
+ features += "+dsp,";
+ if (arch_flags & ArchSpec::eMIPSAse_dspr2)
+ features += "+dspr2,";
+
+ m_reg_info.reset(target->createMCRegInfo(triple.getTriple()));
+ assert(m_reg_info.get());
+
+ m_insn_info.reset(target->createMCInstrInfo());
+ assert(m_insn_info.get());
+
+ m_asm_info.reset(target->createMCAsmInfo(*m_reg_info, triple.getTriple()));
+ m_subtype_info.reset(
+ target->createMCSubtargetInfo(triple.getTriple(), cpu, features));
+ assert(m_asm_info.get() && m_subtype_info.get());
+
+ m_context.reset(
+ new llvm::MCContext(m_asm_info.get(), m_reg_info.get(), nullptr));
+ assert(m_context.get());
+
+ m_disasm.reset(target->createMCDisassembler(*m_subtype_info, *m_context));
+ assert(m_disasm.get());
+
+ /* Create alternate disassembler for microMIPS */
+ if (arch_flags & ArchSpec::eMIPSAse_mips16)
+ features += "+mips16,";
+ else if (arch_flags & ArchSpec::eMIPSAse_micromips)
+ features += "+micromips,";
+
+ m_alt_subtype_info.reset(
+ target->createMCSubtargetInfo(triple.getTriple(), cpu, features));
+ assert(m_alt_subtype_info.get());
+
+ m_alt_disasm.reset(
+ target->createMCDisassembler(*m_alt_subtype_info, *m_context));
+ assert(m_alt_disasm.get());
+
+ m_next_inst_size = 0;
+ m_use_alt_disaasm = false;
+}
+
+void EmulateInstructionMIPS::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void EmulateInstructionMIPS::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString EmulateInstructionMIPS::GetPluginNameStatic() {
+ ConstString g_plugin_name("lldb.emulate-instruction.mips32");
+ return g_plugin_name;
+}
+
+lldb_private::ConstString EmulateInstructionMIPS::GetPluginName() {
+ static ConstString g_plugin_name("EmulateInstructionMIPS");
+ return g_plugin_name;
+}
+
+const char *EmulateInstructionMIPS::GetPluginDescriptionStatic() {
+ return "Emulate instructions for the MIPS32 architecture.";
+}
+
+EmulateInstruction *
+EmulateInstructionMIPS::CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type) {
+ if (EmulateInstructionMIPS::SupportsEmulatingInstructionsOfTypeStatic(
+ inst_type)) {
+ if (arch.GetTriple().getArch() == llvm::Triple::mips ||
+ arch.GetTriple().getArch() == llvm::Triple::mipsel) {
+ return new EmulateInstructionMIPS(arch);
+ }
+ }
+
+ return nullptr;
+}
+
+bool EmulateInstructionMIPS::SetTargetTriple(const ArchSpec &arch) {
+ return arch.GetTriple().getArch() == llvm::Triple::mips ||
+ arch.GetTriple().getArch() == llvm::Triple::mipsel;
+}
+
+const char *EmulateInstructionMIPS::GetRegisterName(unsigned reg_num,
+ bool alternate_name) {
+ if (alternate_name) {
+ switch (reg_num) {
+ case dwarf_sp_mips:
+ return "r29";
+ case dwarf_r30_mips:
+ return "r30";
+ case dwarf_ra_mips:
+ return "r31";
+ case dwarf_f0_mips:
+ return "f0";
+ case dwarf_f1_mips:
+ return "f1";
+ case dwarf_f2_mips:
+ return "f2";
+ case dwarf_f3_mips:
+ return "f3";
+ case dwarf_f4_mips:
+ return "f4";
+ case dwarf_f5_mips:
+ return "f5";
+ case dwarf_f6_mips:
+ return "f6";
+ case dwarf_f7_mips:
+ return "f7";
+ case dwarf_f8_mips:
+ return "f8";
+ case dwarf_f9_mips:
+ return "f9";
+ case dwarf_f10_mips:
+ return "f10";
+ case dwarf_f11_mips:
+ return "f11";
+ case dwarf_f12_mips:
+ return "f12";
+ case dwarf_f13_mips:
+ return "f13";
+ case dwarf_f14_mips:
+ return "f14";
+ case dwarf_f15_mips:
+ return "f15";
+ case dwarf_f16_mips:
+ return "f16";
+ case dwarf_f17_mips:
+ return "f17";
+ case dwarf_f18_mips:
+ return "f18";
+ case dwarf_f19_mips:
+ return "f19";
+ case dwarf_f20_mips:
+ return "f20";
+ case dwarf_f21_mips:
+ return "f21";
+ case dwarf_f22_mips:
+ return "f22";
+ case dwarf_f23_mips:
+ return "f23";
+ case dwarf_f24_mips:
+ return "f24";
+ case dwarf_f25_mips:
+ return "f25";
+ case dwarf_f26_mips:
+ return "f26";
+ case dwarf_f27_mips:
+ return "f27";
+ case dwarf_f28_mips:
+ return "f28";
+ case dwarf_f29_mips:
+ return "f29";
+ case dwarf_f30_mips:
+ return "f30";
+ case dwarf_f31_mips:
+ return "f31";
+ case dwarf_w0_mips:
+ return "w0";
+ case dwarf_w1_mips:
+ return "w1";
+ case dwarf_w2_mips:
+ return "w2";
+ case dwarf_w3_mips:
+ return "w3";
+ case dwarf_w4_mips:
+ return "w4";
+ case dwarf_w5_mips:
+ return "w5";
+ case dwarf_w6_mips:
+ return "w6";
+ case dwarf_w7_mips:
+ return "w7";
+ case dwarf_w8_mips:
+ return "w8";
+ case dwarf_w9_mips:
+ return "w9";
+ case dwarf_w10_mips:
+ return "w10";
+ case dwarf_w11_mips:
+ return "w11";
+ case dwarf_w12_mips:
+ return "w12";
+ case dwarf_w13_mips:
+ return "w13";
+ case dwarf_w14_mips:
+ return "w14";
+ case dwarf_w15_mips:
+ return "w15";
+ case dwarf_w16_mips:
+ return "w16";
+ case dwarf_w17_mips:
+ return "w17";
+ case dwarf_w18_mips:
+ return "w18";
+ case dwarf_w19_mips:
+ return "w19";
+ case dwarf_w20_mips:
+ return "w20";
+ case dwarf_w21_mips:
+ return "w21";
+ case dwarf_w22_mips:
+ return "w22";
+ case dwarf_w23_mips:
+ return "w23";
+ case dwarf_w24_mips:
+ return "w24";
+ case dwarf_w25_mips:
+ return "w25";
+ case dwarf_w26_mips:
+ return "w26";
+ case dwarf_w27_mips:
+ return "w27";
+ case dwarf_w28_mips:
+ return "w28";
+ case dwarf_w29_mips:
+ return "w29";
+ case dwarf_w30_mips:
+ return "w30";
+ case dwarf_w31_mips:
+ return "w31";
+ case dwarf_mir_mips:
+ return "mir";
+ case dwarf_mcsr_mips:
+ return "mcsr";
+ case dwarf_config5_mips:
+ return "config5";
+ default:
+ break;
+ }
+ return nullptr;
+ }
+
+ switch (reg_num) {
+ case dwarf_zero_mips:
+ return "r0";
+ case dwarf_r1_mips:
+ return "r1";
+ case dwarf_r2_mips:
+ return "r2";
+ case dwarf_r3_mips:
+ return "r3";
+ case dwarf_r4_mips:
+ return "r4";
+ case dwarf_r5_mips:
+ return "r5";
+ case dwarf_r6_mips:
+ return "r6";
+ case dwarf_r7_mips:
+ return "r7";
+ case dwarf_r8_mips:
+ return "r8";
+ case dwarf_r9_mips:
+ return "r9";
+ case dwarf_r10_mips:
+ return "r10";
+ case dwarf_r11_mips:
+ return "r11";
+ case dwarf_r12_mips:
+ return "r12";
+ case dwarf_r13_mips:
+ return "r13";
+ case dwarf_r14_mips:
+ return "r14";
+ case dwarf_r15_mips:
+ return "r15";
+ case dwarf_r16_mips:
+ return "r16";
+ case dwarf_r17_mips:
+ return "r17";
+ case dwarf_r18_mips:
+ return "r18";
+ case dwarf_r19_mips:
+ return "r19";
+ case dwarf_r20_mips:
+ return "r20";
+ case dwarf_r21_mips:
+ return "r21";
+ case dwarf_r22_mips:
+ return "r22";
+ case dwarf_r23_mips:
+ return "r23";
+ case dwarf_r24_mips:
+ return "r24";
+ case dwarf_r25_mips:
+ return "r25";
+ case dwarf_r26_mips:
+ return "r26";
+ case dwarf_r27_mips:
+ return "r27";
+ case dwarf_gp_mips:
+ return "gp";
+ case dwarf_sp_mips:
+ return "sp";
+ case dwarf_r30_mips:
+ return "fp";
+ case dwarf_ra_mips:
+ return "ra";
+ case dwarf_sr_mips:
+ return "sr";
+ case dwarf_lo_mips:
+ return "lo";
+ case dwarf_hi_mips:
+ return "hi";
+ case dwarf_bad_mips:
+ return "bad";
+ case dwarf_cause_mips:
+ return "cause";
+ case dwarf_pc_mips:
+ return "pc";
+ case dwarf_f0_mips:
+ return "f0";
+ case dwarf_f1_mips:
+ return "f1";
+ case dwarf_f2_mips:
+ return "f2";
+ case dwarf_f3_mips:
+ return "f3";
+ case dwarf_f4_mips:
+ return "f4";
+ case dwarf_f5_mips:
+ return "f5";
+ case dwarf_f6_mips:
+ return "f6";
+ case dwarf_f7_mips:
+ return "f7";
+ case dwarf_f8_mips:
+ return "f8";
+ case dwarf_f9_mips:
+ return "f9";
+ case dwarf_f10_mips:
+ return "f10";
+ case dwarf_f11_mips:
+ return "f11";
+ case dwarf_f12_mips:
+ return "f12";
+ case dwarf_f13_mips:
+ return "f13";
+ case dwarf_f14_mips:
+ return "f14";
+ case dwarf_f15_mips:
+ return "f15";
+ case dwarf_f16_mips:
+ return "f16";
+ case dwarf_f17_mips:
+ return "f17";
+ case dwarf_f18_mips:
+ return "f18";
+ case dwarf_f19_mips:
+ return "f19";
+ case dwarf_f20_mips:
+ return "f20";
+ case dwarf_f21_mips:
+ return "f21";
+ case dwarf_f22_mips:
+ return "f22";
+ case dwarf_f23_mips:
+ return "f23";
+ case dwarf_f24_mips:
+ return "f24";
+ case dwarf_f25_mips:
+ return "f25";
+ case dwarf_f26_mips:
+ return "f26";
+ case dwarf_f27_mips:
+ return "f27";
+ case dwarf_f28_mips:
+ return "f28";
+ case dwarf_f29_mips:
+ return "f29";
+ case dwarf_f30_mips:
+ return "f30";
+ case dwarf_f31_mips:
+ return "f31";
+ case dwarf_fcsr_mips:
+ return "fcsr";
+ case dwarf_fir_mips:
+ return "fir";
+ case dwarf_w0_mips:
+ return "w0";
+ case dwarf_w1_mips:
+ return "w1";
+ case dwarf_w2_mips:
+ return "w2";
+ case dwarf_w3_mips:
+ return "w3";
+ case dwarf_w4_mips:
+ return "w4";
+ case dwarf_w5_mips:
+ return "w5";
+ case dwarf_w6_mips:
+ return "w6";
+ case dwarf_w7_mips:
+ return "w7";
+ case dwarf_w8_mips:
+ return "w8";
+ case dwarf_w9_mips:
+ return "w9";
+ case dwarf_w10_mips:
+ return "w10";
+ case dwarf_w11_mips:
+ return "w11";
+ case dwarf_w12_mips:
+ return "w12";
+ case dwarf_w13_mips:
+ return "w13";
+ case dwarf_w14_mips:
+ return "w14";
+ case dwarf_w15_mips:
+ return "w15";
+ case dwarf_w16_mips:
+ return "w16";
+ case dwarf_w17_mips:
+ return "w17";
+ case dwarf_w18_mips:
+ return "w18";
+ case dwarf_w19_mips:
+ return "w19";
+ case dwarf_w20_mips:
+ return "w20";
+ case dwarf_w21_mips:
+ return "w21";
+ case dwarf_w22_mips:
+ return "w22";
+ case dwarf_w23_mips:
+ return "w23";
+ case dwarf_w24_mips:
+ return "w24";
+ case dwarf_w25_mips:
+ return "w25";
+ case dwarf_w26_mips:
+ return "w26";
+ case dwarf_w27_mips:
+ return "w27";
+ case dwarf_w28_mips:
+ return "w28";
+ case dwarf_w29_mips:
+ return "w29";
+ case dwarf_w30_mips:
+ return "w30";
+ case dwarf_w31_mips:
+ return "w31";
+ case dwarf_mcsr_mips:
+ return "mcsr";
+ case dwarf_mir_mips:
+ return "mir";
+ case dwarf_config5_mips:
+ return "config5";
+ }
+ return nullptr;
+}
+
+bool EmulateInstructionMIPS::GetRegisterInfo(RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterInfo &reg_info) {
+ if (reg_kind == eRegisterKindGeneric) {
+ switch (reg_num) {
+ case LLDB_REGNUM_GENERIC_PC:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_pc_mips;
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_sp_mips;
+ break;
+ case LLDB_REGNUM_GENERIC_FP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_r30_mips;
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_ra_mips;
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_sr_mips;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ if (reg_kind == eRegisterKindDWARF) {
+ ::memset(&reg_info, 0, sizeof(RegisterInfo));
+ ::memset(reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds));
+
+ if (reg_num == dwarf_sr_mips || reg_num == dwarf_fcsr_mips ||
+ reg_num == dwarf_fir_mips || reg_num == dwarf_mcsr_mips ||
+ reg_num == dwarf_mir_mips || reg_num == dwarf_config5_mips) {
+ reg_info.byte_size = 4;
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ } else if ((int)reg_num >= dwarf_zero_mips &&
+ (int)reg_num <= dwarf_f31_mips) {
+ reg_info.byte_size = 4;
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ } else if ((int)reg_num >= dwarf_w0_mips &&
+ (int)reg_num <= dwarf_w31_mips) {
+ reg_info.byte_size = 16;
+ reg_info.format = eFormatVectorOfUInt8;
+ reg_info.encoding = eEncodingVector;
+ } else {
+ return false;
+ }
+
+ reg_info.name = GetRegisterName(reg_num, false);
+ reg_info.alt_name = GetRegisterName(reg_num, true);
+ reg_info.kinds[eRegisterKindDWARF] = reg_num;
+
+ switch (reg_num) {
+ case dwarf_r30_mips:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ break;
+ case dwarf_ra_mips:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA;
+ break;
+ case dwarf_sp_mips:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ break;
+ case dwarf_pc_mips:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ break;
+ case dwarf_sr_mips:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+EmulateInstructionMIPS::MipsOpcode *
+EmulateInstructionMIPS::GetOpcodeForInstruction(const char *op_name) {
+ static EmulateInstructionMIPS::MipsOpcode g_opcodes[] = {
+ // Prologue/Epilogue instructions
+ {"ADDiu", &EmulateInstructionMIPS::Emulate_ADDiu,
+ "ADDIU rt, rs, immediate"},
+ {"SW", &EmulateInstructionMIPS::Emulate_SW, "SW rt, offset(rs)"},
+ {"LW", &EmulateInstructionMIPS::Emulate_LW, "LW rt, offset(base)"},
+ {"SUBU", &EmulateInstructionMIPS::Emulate_SUBU_ADDU, "SUBU rd, rs, rt"},
+ {"ADDU", &EmulateInstructionMIPS::Emulate_SUBU_ADDU, "ADDU rd, rs, rt"},
+ {"LUI", &EmulateInstructionMIPS::Emulate_LUI, "LUI rt, immediate"},
+
+ // MicroMIPS Prologue/Epilogue instructions
+ {"ADDIUSP_MM", &EmulateInstructionMIPS::Emulate_ADDIUSP,
+ "ADDIU immediate"},
+ {"ADDIUS5_MM", &EmulateInstructionMIPS::Emulate_ADDIUS5,
+ "ADDIUS5 rd,immediate"},
+ {"SWSP_MM", &EmulateInstructionMIPS::Emulate_SWSP, "SWSP rt,offset(sp)"},
+ {"SWM16_MM", &EmulateInstructionMIPS::Emulate_SWM16_32,
+ "SWM16 reglist,offset(sp)"},
+ {"SWM32_MM", &EmulateInstructionMIPS::Emulate_SWM16_32,
+ "SWM32 reglist,offset(base)"},
+ {"SWP_MM", &EmulateInstructionMIPS::Emulate_SWM16_32,
+ "SWP rs1,offset(base)"},
+ {"LWSP_MM", &EmulateInstructionMIPS::Emulate_LWSP, "LWSP rt,offset(sp)"},
+ {"LWM16_MM", &EmulateInstructionMIPS::Emulate_LWM16_32,
+ "LWM16 reglist,offset(sp)"},
+ {"LWM32_MM", &EmulateInstructionMIPS::Emulate_LWM16_32,
+ "LWM32 reglist,offset(base)"},
+ {"LWP_MM", &EmulateInstructionMIPS::Emulate_LWM16_32,
+ "LWP rd,offset(base)"},
+ {"JRADDIUSP", &EmulateInstructionMIPS::Emulate_JRADDIUSP,
+ "JRADDIUSP immediate"},
+
+ // Load/Store instructions
+ /* Following list of emulated instructions are required by implementation
+ of hardware watchpoint
+ for MIPS in lldb. As we just need the address accessed by instructions,
+ we have generalised
+ all these instructions in 2 functions depending on their addressing
+ modes */
+
+ {"LB", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LB rt, offset(base)"},
+ {"LBE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LBE rt, offset(base)"},
+ {"LBU", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LBU rt, offset(base)"},
+ {"LBUE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LBUE rt, offset(base)"},
+ {"LDC1", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LDC1 ft, offset(base)"},
+ {"LD", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LD rt, offset(base)"},
+ {"LDL", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LDL rt, offset(base)"},
+ {"LDR", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LDR rt, offset(base)"},
+ {"LLD", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LLD rt, offset(base)"},
+ {"LDC2", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LDC2 rt, offset(base)"},
+ {"LDXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "LDXC1 fd, index (base)"},
+ {"LH", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LH rt, offset(base)"},
+ {"LHE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LHE rt, offset(base)"},
+ {"LHU", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LHU rt, offset(base)"},
+ {"LHUE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LHUE rt, offset(base)"},
+ {"LL", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LL rt, offset(base)"},
+ {"LLE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LLE rt, offset(base)"},
+ {"LUXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "LUXC1 fd, index (base)"},
+ {"LW", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LW rt, offset(base)"},
+ {"LWC1", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWC1 ft, offset(base)"},
+ {"LWC2", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWC2 rt, offset(base)"},
+ {"LWE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWE rt, offset(base)"},
+ {"LWL", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWL rt, offset(base)"},
+ {"LWLE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWLE rt, offset(base)"},
+ {"LWR", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWR rt, offset(base)"},
+ {"LWRE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWRE rt, offset(base)"},
+ {"LWXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "LWXC1 fd, index (base)"},
+ {"LLX", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LLX rt, offset(base)"},
+ {"LLXE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LLXE rt, offset(base)"},
+ {"LLDX", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LLDX rt, offset(base)"},
+
+ {"SB", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SB rt, offset(base)"},
+ {"SBE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SBE rt, offset(base)"},
+ {"SC", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SC rt, offset(base)"},
+ {"SCE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SCE rt, offset(base)"},
+ {"SCD", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SCD rt, offset(base)"},
+ {"SD", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SD rt, offset(base)"},
+ {"SDL", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SDL rt, offset(base)"},
+ {"SDR", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SDR rt, offset(base)"},
+ {"SDC1", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SDC1 ft, offset(base)"},
+ {"SDC2", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SDC2 rt, offset(base)"},
+ {"SDXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "SDXC1 fs, index(base)"},
+ {"SH", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SH rt, offset(base)"},
+ {"SHE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SHE rt, offset(base)"},
+ {"SUXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "SUXC1 fs, index (base)"},
+ {"SWC1", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWC1 ft, offset(base)"},
+ {"SWC2", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWC2 rt, offset(base)"},
+ {"SWE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWE rt, offset(base)"},
+ {"SWL", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWL rt, offset(base)"},
+ {"SWLE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWLE rt, offset(base)"},
+ {"SWR", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWR rt, offset(base)"},
+ {"SWRE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWRE rt, offset(base)"},
+ {"SWXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "SWXC1 fs, index (base)"},
+ {"SCX", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SCX rt, offset(base)"},
+ {"SCXE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SCXE rt, offset(base)"},
+ {"SCDX", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SCDX rt, offset(base)"},
+
+ // MicroMIPS Load/Store instructions
+ {"LBU16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LBU16 rt, decoded_offset(base)"},
+ {"LHU16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LHU16 rt, left_shifted_offset(base)"},
+ {"LW16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LW16 rt, left_shifted_offset(base)"},
+ {"LWGP_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWGP rt, left_shifted_offset(gp)"},
+ {"SH16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SH16 rt, left_shifted_offset(base)"},
+ {"SW16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SW16 rt, left_shifted_offset(base)"},
+ {"SW_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWSP rt, left_shifted_offset(base)"},
+ {"SB16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SB16 rt, offset(base)"},
+
+ // Branch instructions
+ {"BEQ", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQ rs,rt,offset"},
+ {"BNE", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNE rs,rt,offset"},
+ {"BEQL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQL rs,rt,offset"},
+ {"BNEL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNEL rs,rt,offset"},
+ {"BGEZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link,
+ "BGEZALL rt,offset"},
+ {"BAL", &EmulateInstructionMIPS::Emulate_BAL, "BAL offset"},
+ {"BGEZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link,
+ "BGEZAL rs,offset"},
+ {"BALC", &EmulateInstructionMIPS::Emulate_BALC, "BALC offset"},
+ {"BC", &EmulateInstructionMIPS::Emulate_BC, "BC offset"},
+ {"BGEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZ rs,offset"},
+ {"BLEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BLEZALC rs,offset"},
+ {"BGEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BGEZALC rs,offset"},
+ {"BLTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BLTZALC rs,offset"},
+ {"BGTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BGTZALC rs,offset"},
+ {"BEQZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BEQZALC rs,offset"},
+ {"BNEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BNEZALC rs,offset"},
+ {"BEQC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BEQC rs,rt,offset"},
+ {"BNEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BNEC rs,rt,offset"},
+ {"BLTC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BLTC rs,rt,offset"},
+ {"BGEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BGEC rs,rt,offset"},
+ {"BLTUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BLTUC rs,rt,offset"},
+ {"BGEUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BGEUC rs,rt,offset"},
+ {"BLTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLTZC rt,offset"},
+ {"BLEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLEZC rt,offset"},
+ {"BGEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGEZC rt,offset"},
+ {"BGTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGTZC rt,offset"},
+ {"BEQZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BEQZC rt,offset"},
+ {"BNEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BNEZC rt,offset"},
+ {"BGEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZL rt,offset"},
+ {"BGTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZ rt,offset"},
+ {"BGTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZL rt,offset"},
+ {"BLEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZ rt,offset"},
+ {"BLEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZL rt,offset"},
+ {"BLTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZ rt,offset"},
+ {"BLTZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link,
+ "BLTZAL rt,offset"},
+ {"BLTZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link,
+ "BLTZALL rt,offset"},
+ {"BLTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZL rt,offset"},
+ {"BOVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BOVC rs,rt,offset"},
+ {"BNVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BNVC rs,rt,offset"},
+ {"J", &EmulateInstructionMIPS::Emulate_J, "J target"},
+ {"JAL", &EmulateInstructionMIPS::Emulate_JAL, "JAL target"},
+ {"JALX", &EmulateInstructionMIPS::Emulate_JAL, "JALX target"},
+ {"JALR", &EmulateInstructionMIPS::Emulate_JALR, "JALR target"},
+ {"JALR_HB", &EmulateInstructionMIPS::Emulate_JALR, "JALR.HB target"},
+ {"JIALC", &EmulateInstructionMIPS::Emulate_JIALC, "JIALC rt,offset"},
+ {"JIC", &EmulateInstructionMIPS::Emulate_JIC, "JIC rt,offset"},
+ {"JR", &EmulateInstructionMIPS::Emulate_JR, "JR target"},
+ {"JR_HB", &EmulateInstructionMIPS::Emulate_JR, "JR.HB target"},
+ {"BC1F", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1F cc, offset"},
+ {"BC1T", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1T cc, offset"},
+ {"BC1FL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1FL cc, offset"},
+ {"BC1TL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1TL cc, offset"},
+ {"BC1EQZ", &EmulateInstructionMIPS::Emulate_BC1EQZ, "BC1EQZ ft, offset"},
+ {"BC1NEZ", &EmulateInstructionMIPS::Emulate_BC1NEZ, "BC1NEZ ft, offset"},
+ {"BC1ANY2F", &EmulateInstructionMIPS::Emulate_3D_branch,
+ "BC1ANY2F cc, offset"},
+ {"BC1ANY2T", &EmulateInstructionMIPS::Emulate_3D_branch,
+ "BC1ANY2T cc, offset"},
+ {"BC1ANY4F", &EmulateInstructionMIPS::Emulate_3D_branch,
+ "BC1ANY4F cc, offset"},
+ {"BC1ANY4T", &EmulateInstructionMIPS::Emulate_3D_branch,
+ "BC1ANY4T cc, offset"},
+ {"BNZ_B", &EmulateInstructionMIPS::Emulate_BNZB, "BNZ.b wt,s16"},
+ {"BNZ_H", &EmulateInstructionMIPS::Emulate_BNZH, "BNZ.h wt,s16"},
+ {"BNZ_W", &EmulateInstructionMIPS::Emulate_BNZW, "BNZ.w wt,s16"},
+ {"BNZ_D", &EmulateInstructionMIPS::Emulate_BNZD, "BNZ.d wt,s16"},
+ {"BZ_B", &EmulateInstructionMIPS::Emulate_BZB, "BZ.b wt,s16"},
+ {"BZ_H", &EmulateInstructionMIPS::Emulate_BZH, "BZ.h wt,s16"},
+ {"BZ_W", &EmulateInstructionMIPS::Emulate_BZW, "BZ.w wt,s16"},
+ {"BZ_D", &EmulateInstructionMIPS::Emulate_BZD, "BZ.d wt,s16"},
+ {"BNZ_V", &EmulateInstructionMIPS::Emulate_BNZV, "BNZ.V wt,s16"},
+ {"BZ_V", &EmulateInstructionMIPS::Emulate_BZV, "BZ.V wt,s16"},
+
+ // MicroMIPS Branch instructions
+ {"B16_MM", &EmulateInstructionMIPS::Emulate_B16_MM, "B16 offset"},
+ {"BEQZ16_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BEQZ16 rs, offset"},
+ {"BNEZ16_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BNEZ16 rs, offset"},
+ {"BEQZC_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BEQZC rs, offset"},
+ {"BNEZC_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BNEZC rs, offset"},
+ {"BGEZALS_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BGEZALS rs, offset"},
+ {"BLTZALS_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BLTZALS rs, offset"},
+ {"JALR16_MM", &EmulateInstructionMIPS::Emulate_JALRx16_MM, "JALR16 rs"},
+ {"JALRS16_MM", &EmulateInstructionMIPS::Emulate_JALRx16_MM, "JALRS16 rs"},
+ {"JR16_MM", &EmulateInstructionMIPS::Emulate_JR, "JR16 rs rs"},
+ {"JRC16_MM", &EmulateInstructionMIPS::Emulate_JR, "JRC16 rs rs"},
+ {"JALS_MM", &EmulateInstructionMIPS::Emulate_JALx, "JALS target"},
+ {"JALX_MM", &EmulateInstructionMIPS::Emulate_JALx, "JALX target"},
+ {"JALRS_MM", &EmulateInstructionMIPS::Emulate_JALRS, "JALRS rt, rs"},
+ };
+
+ static const size_t k_num_mips_opcodes = llvm::array_lengthof(g_opcodes);
+
+ for (size_t i = 0; i < k_num_mips_opcodes; ++i) {
+ if (!strcasecmp(g_opcodes[i].op_name, op_name))
+ return &g_opcodes[i];
+ }
+
+ return nullptr;
+}
+
+uint32_t
+EmulateInstructionMIPS::GetSizeOfInstruction(lldb_private::DataExtractor &data,
+ uint64_t inst_addr) {
+ uint64_t next_inst_size = 0;
+ llvm::MCInst mc_insn;
+ llvm::MCDisassembler::DecodeStatus decode_status;
+ llvm::ArrayRef<uint8_t> raw_insn(data.GetDataStart(), data.GetByteSize());
+
+ if (m_use_alt_disaasm)
+ decode_status =
+ m_alt_disasm->getInstruction(mc_insn, next_inst_size, raw_insn,
+ inst_addr, llvm::nulls(), llvm::nulls());
+ else
+ decode_status =
+ m_disasm->getInstruction(mc_insn, next_inst_size, raw_insn, inst_addr,
+ llvm::nulls(), llvm::nulls());
+
+ if (decode_status != llvm::MCDisassembler::Success)
+ return false;
+
+ return m_insn_info->get(mc_insn.getOpcode()).getSize();
+}
+
+bool EmulateInstructionMIPS::SetInstruction(const Opcode &insn_opcode,
+ const Address &inst_addr,
+ Target *target) {
+ m_use_alt_disaasm = false;
+
+ if (EmulateInstruction::SetInstruction(insn_opcode, inst_addr, target)) {
+ if (inst_addr.GetAddressClass() == AddressClass::eCodeAlternateISA) {
+ Status error;
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+
+ /*
+ * The address belongs to microMIPS function. To find the size of
+ * next instruction use microMIPS disassembler.
+ */
+ m_use_alt_disaasm = true;
+
+ uint32_t current_inst_size = insn_opcode.GetByteSize();
+ uint8_t buf[sizeof(uint32_t)];
+ uint64_t next_inst_addr = (m_addr & (~1ull)) + current_inst_size;
+ Address next_addr(next_inst_addr);
+
+ const size_t bytes_read =
+ target->ReadMemory(next_addr, /* Address of next instruction */
+ true, /* prefer_file_cache */
+ buf, sizeof(uint32_t), error, &load_addr);
+
+ if (bytes_read == 0)
+ return true;
+
+ DataExtractor data(buf, sizeof(uint32_t), GetByteOrder(),
+ GetAddressByteSize());
+ m_next_inst_size = GetSizeOfInstruction(data, next_inst_addr);
+ return true;
+ } else {
+ /*
+ * If the address class is not AddressClass::eCodeAlternateISA then
+ * the function is not microMIPS. In this case instruction size is
+ * always 4 bytes.
+ */
+ m_next_inst_size = 4;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool EmulateInstructionMIPS::ReadInstruction() {
+ bool success = false;
+ m_addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_ADDRESS, &success);
+ if (success) {
+ Context read_inst_context;
+ read_inst_context.type = eContextReadOpcode;
+ read_inst_context.SetNoArgs();
+ m_opcode.SetOpcode32(
+ ReadMemoryUnsigned(read_inst_context, m_addr, 4, 0, &success),
+ GetByteOrder());
+ }
+ if (!success)
+ m_addr = LLDB_INVALID_ADDRESS;
+ return success;
+}
+
+bool EmulateInstructionMIPS::EvaluateInstruction(uint32_t evaluate_options) {
+ bool success = false;
+ llvm::MCInst mc_insn;
+ uint64_t insn_size;
+ DataExtractor data;
+
+ /* Keep the complexity of the decode logic with the llvm::MCDisassembler
+ * class. */
+ if (m_opcode.GetData(data)) {
+ llvm::MCDisassembler::DecodeStatus decode_status;
+ llvm::ArrayRef<uint8_t> raw_insn(data.GetDataStart(), data.GetByteSize());
+ if (m_use_alt_disaasm)
+ decode_status = m_alt_disasm->getInstruction(
+ mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls());
+ else
+ decode_status = m_disasm->getInstruction(
+ mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls());
+
+ if (decode_status != llvm::MCDisassembler::Success)
+ return false;
+ }
+
+ /*
+ * mc_insn.getOpcode() returns decoded opcode. However to make use
+ * of llvm::Mips::<insn> we would need "MipsGenInstrInfo.inc".
+ */
+ const char *op_name = m_insn_info->getName(mc_insn.getOpcode()).data();
+
+ if (op_name == nullptr)
+ return false;
+
+ /*
+ * Decoding has been done already. Just get the call-back function
+ * and emulate the instruction.
+ */
+ MipsOpcode *opcode_data = GetOpcodeForInstruction(op_name);
+
+ if (opcode_data == nullptr)
+ return false;
+
+ uint64_t old_pc = 0, new_pc = 0;
+ const bool auto_advance_pc =
+ evaluate_options & eEmulateInstructionOptionAutoAdvancePC;
+
+ if (auto_advance_pc) {
+ old_pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+ }
+
+ /* emulate instruction */
+ success = (this->*opcode_data->callback)(mc_insn);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc) {
+ new_pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ /* If we haven't changed the PC, change it here */
+ if (old_pc == new_pc) {
+ new_pc += 4;
+ Context context;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ new_pc))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::CreateFunctionEntryUnwind(
+ UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const bool can_replace = false;
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_sp_mips, 0);
+
+ // Our previous PC is in the RA
+ row->SetRegisterLocationToRegister(dwarf_pc_mips, dwarf_ra_mips, can_replace);
+
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+ unwind_plan.SetSourceName("EmulateInstructionMIPS");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ unwind_plan.SetReturnAddressRegister(dwarf_ra_mips);
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::nonvolatile_reg_p(uint32_t regnum) {
+ switch (regnum) {
+ case dwarf_r16_mips:
+ case dwarf_r17_mips:
+ case dwarf_r18_mips:
+ case dwarf_r19_mips:
+ case dwarf_r20_mips:
+ case dwarf_r21_mips:
+ case dwarf_r22_mips:
+ case dwarf_r23_mips:
+ case dwarf_gp_mips:
+ case dwarf_sp_mips:
+ case dwarf_r30_mips:
+ case dwarf_ra_mips:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+bool EmulateInstructionMIPS::Emulate_ADDiu(llvm::MCInst &insn) {
+ // ADDIU rt, rs, immediate
+ // GPR[rt] <- GPR[rs] + sign_extend(immediate)
+
+ uint8_t dst, src;
+ bool success = false;
+ const uint32_t imm16 = insn.getOperand(2).getImm();
+ int64_t imm = SignedBits(imm16, 15, 0);
+
+ dst = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ src = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ // If immediate value is greater then 2^16 - 1 then clang generate LUI,
+ // ADDIU, SUBU instructions in prolog. Example lui $1, 0x2 addiu $1, $1,
+ // -0x5920 subu $sp, $sp, $1 In this case, ADDIU dst and src will be same
+ // and not equal to sp
+ if (dst == src) {
+ Context context;
+
+ /* read <src> register */
+ const int64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* Check if this is daddiu sp, sp, imm16 */
+ if (dst == dwarf_sp_mips) {
+ uint64_t result = src_opd_val + imm;
+ RegisterInfo reg_info_sp;
+
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, imm);
+
+ /* We are allocating bytes on stack */
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result);
+ return true;
+ }
+
+ imm += src_opd_val;
+ context.SetImmediateSigned(imm);
+ context.type = eContextImmediate;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips + dst, imm))
+ return false;
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_SW(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t imm16 = insn.getOperand(2).getImm();
+ uint32_t imm = SignedBits(imm16, 15, 0);
+ uint32_t src, base;
+ int32_t address;
+ Context bad_vaddr_context;
+
+ RegisterInfo reg_info_base;
+
+ src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ /* read base register */
+ address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ /* We look for sp based non-volatile register stores */
+ if (nonvolatile_reg_p(src)) {
+
+ RegisterInfo reg_info_src;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src,
+ reg_info_src))
+ return false;
+
+ Context context;
+ RegisterValue data_src;
+ context.type = eContextPushRegisterOnStack;
+ context.SetRegisterToRegisterPlusOffset(reg_info_src, reg_info_base, 0);
+
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ Status error;
+
+ if (!ReadRegister(&reg_info_base, data_src))
+ return false;
+
+ if (data_src.GetAsMemoryData(&reg_info_src, buffer, reg_info_src.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context, address, buffer, reg_info_src.byte_size))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool EmulateInstructionMIPS::Emulate_LW(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t src, base;
+ int32_t imm, address;
+ Context bad_vaddr_context;
+
+ src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ imm = insn.getOperand(2).getImm();
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ /* read base register */
+ address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ if (nonvolatile_reg_p(src)) {
+ RegisterValue data_src;
+ RegisterInfo reg_info_src;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src,
+ reg_info_src))
+ return false;
+
+ Context context;
+ context.type = eContextPopRegisterOffStack;
+ context.SetAddress(address);
+
+ return WriteRegister(context, &reg_info_src, data_src);
+ }
+
+ return false;
+}
+
+bool EmulateInstructionMIPS::Emulate_SUBU_ADDU(llvm::MCInst &insn) {
+ // SUBU sp, <src>, <rt>
+ // ADDU sp, <src>, <rt>
+ // ADDU dst, sp, <rt>
+
+ bool success = false;
+ uint64_t result;
+ uint8_t src, dst, rt;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ dst = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ src = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ /* Check if sp is destination register */
+ if (dst == dwarf_sp_mips) {
+ rt = m_reg_info->getEncodingValue(insn.getOperand(2).getReg());
+
+ /* read <src> register */
+ uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* read <rt > register */
+ uint64_t rt_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "SUBU"))
+ result = src_opd_val - rt_opd_val;
+ else
+ result = src_opd_val + rt_opd_val;
+
+ Context context;
+ RegisterInfo reg_info_sp;
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, rt_opd_val);
+
+ /* We are allocating bytes on stack */
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result);
+
+ return true;
+ } else if (src == dwarf_sp_mips) {
+ rt = m_reg_info->getEncodingValue(insn.getOperand(2).getReg());
+
+ /* read <src> register */
+ uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* read <rt> register */
+ uint64_t rt_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ if (!strcasecmp(op_name, "SUBU"))
+ result = src_opd_val - rt_opd_val;
+ else
+ result = src_opd_val + rt_opd_val;
+
+ context.SetImmediateSigned(result);
+ context.type = eContextImmediate;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips + dst, result))
+ return false;
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_LUI(llvm::MCInst &insn) {
+ // LUI rt, immediate
+ // GPR[rt] <- sign_extend(immediate << 16)
+
+ const uint32_t imm32 = insn.getOperand(1).getImm() << 16;
+ int64_t imm = SignedBits(imm32, 31, 0);
+ uint8_t rt;
+ Context context;
+
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ context.SetImmediateSigned(imm);
+ context.type = eContextImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips + rt, imm);
+}
+
+bool EmulateInstructionMIPS::Emulate_ADDIUSP(llvm::MCInst &insn) {
+ bool success = false;
+ const uint32_t imm9 = insn.getOperand(0).getImm();
+ uint64_t result;
+
+ // This instruction operates implicitly on stack pointer, so read <sp>
+ // register.
+ uint64_t src_opd_val =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_sp_mips, 0, &success);
+ if (!success)
+ return false;
+
+ result = src_opd_val + imm9;
+
+ Context context;
+ RegisterInfo reg_info_sp;
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, imm9);
+
+ // We are adjusting the stack.
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result);
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_ADDIUS5(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t base;
+ const uint32_t imm4 = insn.getOperand(2).getImm();
+ uint64_t result;
+
+ // The source and destination register is same for this instruction.
+ base = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+
+ // We are looking for stack adjustment only
+ if (base == dwarf_sp_mips) {
+ // Read stack pointer register
+ uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ result = src_opd_val + imm4;
+
+ Context context;
+ RegisterInfo reg_info_sp;
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, imm4);
+
+ // We are adjusting the stack.
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result);
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_SWSP(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t imm5 = insn.getOperand(2).getImm();
+ uint32_t src, base;
+ Context bad_vaddr_context;
+ uint32_t address;
+
+ src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ RegisterInfo reg_info_base;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ // read base register
+ address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + base, 0,
+ &success);
+ if (!success)
+ return false;
+
+ // destination address
+ address = address + imm5;
+
+ // We use bad_vaddr_context to store base address which is used by H/W
+ // watchpoint Set the bad_vaddr register with base address used in the
+ // instruction
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ // We look for sp based non-volatile register stores.
+ if (base == dwarf_sp_mips && nonvolatile_reg_p(src)) {
+ RegisterInfo reg_info_src = {};
+ Context context;
+ RegisterValue data_src;
+ context.type = eContextPushRegisterOnStack;
+ context.SetRegisterToRegisterPlusOffset(reg_info_src, reg_info_base, 0);
+
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ Status error;
+
+ if (!ReadRegister(&reg_info_base, data_src))
+ return false;
+
+ if (data_src.GetAsMemoryData(&reg_info_src, buffer, reg_info_src.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context, address, buffer, reg_info_src.byte_size))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+/* Emulate SWM16,SWM32 and SWP instruction.
+
+ SWM16 always has stack pointer as a base register (but it is still available
+ in MCInst as an operand).
+ SWM32 and SWP can have base register other than stack pointer.
+*/
+bool EmulateInstructionMIPS::Emulate_SWM16_32(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t src, base;
+ uint32_t num_operands = insn.getNumOperands(); // No of operands vary based on
+ // no of regs to store.
+
+ // Base register is second last operand of the instruction.
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+
+ // We are looking for sp based stores so if base is not a stack pointer then
+ // don't proceed.
+ if (base != dwarf_sp_mips)
+ return false;
+
+ // offset is always the last operand.
+ uint32_t offset = insn.getOperand(num_operands - 1).getImm();
+
+ RegisterInfo reg_info_base;
+ RegisterInfo reg_info_src;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ // read SP
+ uint32_t base_address = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ // Resulting base addrss
+ base_address = base_address + offset;
+
+ // Total no of registers to be stored are num_operands-2.
+ for (uint32_t i = 0; i < num_operands - 2; i++) {
+ // Get the register number to be stored.
+ src = m_reg_info->getEncodingValue(insn.getOperand(i).getReg());
+
+ /*
+ Record only non-volatile stores.
+ This check is required for SWP instruction because source operand could
+ be any register.
+ SWM16 and SWM32 instruction always has saved registers as source
+ operands.
+ */
+ if (!nonvolatile_reg_p(src))
+ return false;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src,
+ reg_info_src))
+ return false;
+
+ Context context;
+ RegisterValue data_src;
+ context.type = eContextPushRegisterOnStack;
+ context.SetRegisterToRegisterPlusOffset(reg_info_src, reg_info_base, 0);
+
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ Status error;
+
+ if (!ReadRegister(&reg_info_base, data_src))
+ return false;
+
+ if (data_src.GetAsMemoryData(&reg_info_src, buffer, reg_info_src.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context, base_address, buffer, reg_info_src.byte_size))
+ return false;
+
+ // Stack address for next register
+ base_address = base_address + reg_info_src.byte_size;
+ }
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_LWSP(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ uint32_t base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ uint32_t imm5 = insn.getOperand(2).getImm();
+ Context bad_vaddr_context;
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ // read base register
+ uint32_t base_address = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ base_address = base_address + imm5;
+
+ // We use bad_vaddr_context to store base address which is used by H/W
+ // watchpoint Set the bad_vaddr register with base address used in the
+ // instruction
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ base_address);
+
+ if (base == dwarf_sp_mips && nonvolatile_reg_p(src)) {
+ RegisterValue data_src;
+ RegisterInfo reg_info_src;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src,
+ reg_info_src))
+ return false;
+
+ Context context;
+ context.type = eContextPopRegisterOffStack;
+ context.SetAddress(base_address);
+
+ return WriteRegister(context, &reg_info_src, data_src);
+ }
+
+ return false;
+}
+
+/* Emulate LWM16, LWM32 and LWP instructions.
+
+ LWM16 always has stack pointer as a base register (but it is still available
+ in MCInst as an operand).
+ LWM32 and LWP can have base register other than stack pointer.
+*/
+bool EmulateInstructionMIPS::Emulate_LWM16_32(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t dst, base;
+ uint32_t num_operands = insn.getNumOperands(); // No of operands vary based on
+ // no of regs to store.
+ uint32_t imm = insn.getOperand(num_operands - 1)
+ .getImm(); // imm is the last operand in the instruction.
+
+ // Base register is second last operand of the instruction.
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+
+ // We are looking for sp based loads so if base is not a stack pointer then
+ // don't proceed.
+ if (base != dwarf_sp_mips)
+ return false;
+
+ uint32_t base_address = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ base_address = base_address + imm;
+
+ RegisterValue data_dst;
+ RegisterInfo reg_info_dst;
+
+ // Total no of registers to be re-stored are num_operands-2.
+ for (uint32_t i = 0; i < num_operands - 2; i++) {
+ // Get the register number to be re-stored.
+ dst = m_reg_info->getEncodingValue(insn.getOperand(i).getReg());
+
+ /*
+ Record only non-volatile loads.
+ This check is required for LWP instruction because destination operand
+ could be any register.
+ LWM16 and LWM32 instruction always has saved registers as destination
+ operands.
+ */
+ if (!nonvolatile_reg_p(dst))
+ return false;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + dst,
+ reg_info_dst))
+ return false;
+
+ Context context;
+ context.type = eContextPopRegisterOffStack;
+ context.SetAddress(base_address + (i * 4));
+
+ if (!WriteRegister(context, &reg_info_dst, data_dst))
+ return false;
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_JRADDIUSP(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t imm5 = insn.getOperand(0).getImm();
+
+ /* JRADDIUSP immediate
+ * PC <- RA
+ * SP <- SP + zero_extend(Immediate << 2)
+ */
+
+ // This instruction operates implicitly on stack pointer, so read <sp>
+ // register.
+ int32_t src_opd_val =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_sp_mips, 0, &success);
+ if (!success)
+ return false;
+
+ int32_t ra_val =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_ra_mips, 0, &success);
+ if (!success)
+ return false;
+
+ int32_t result = src_opd_val + imm5;
+
+ Context context;
+
+ // Update the PC
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ ra_val))
+ return false;
+
+ RegisterInfo reg_info_sp;
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, imm5);
+
+ // We are adjusting stack
+ context.type = eContextAdjustStackPointer;
+
+ // update SP
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips,
+ result);
+}
+
+static int IsAdd64bitOverflow(int32_t a, int32_t b) {
+ int32_t r = (uint32_t)a + (uint32_t)b;
+ return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0);
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BEQ, BNE : Branch on condition
+ BEQL, BNEL : Branch likely
+*/
+bool EmulateInstructionMIPS::Emulate_BXX_3ops(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ int32_t offset, pc, target = 0, rs_val, rt_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rt = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ offset = insn.getOperand(2).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BEQ") || !strcasecmp(op_name, "BEQL")) {
+ if (rs_val == rt_val)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BNE") || !strcasecmp(op_name, "BNEL")) {
+ if (rs_val != rt_val)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BEQC, BNEC, BLTC, BGEC, BLTUC, BGEUC, BOVC, BNVC: Compact branch
+ instructions with no delay slot
+*/
+bool EmulateInstructionMIPS::Emulate_BXX_3ops_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ int32_t offset, pc, target = 0, rs_val, rt_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rt = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ offset = insn.getOperand(2).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BEQC")) {
+ if (rs_val == rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEC")) {
+ if (rs_val != rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTC")) {
+ if (rs_val < rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEC")) {
+ if (rs_val >= rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTUC")) {
+ if (rs_val < rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEUC")) {
+ if ((uint32_t)rs_val >= (uint32_t)rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BOVC")) {
+ if (IsAdd64bitOverflow(rs_val, rt_val))
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNVC")) {
+ if (!IsAdd64bitOverflow(rs_val, rt_val))
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+/*
+ Emulate below MIPS conditional branch and link instructions.
+ BLEZALC, BGEZALC, BLTZALC, BGTZALC, BEQZALC, BNEZALC : Compact branches
+*/
+bool EmulateInstructionMIPS::Emulate_Bcond_Link_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int32_t offset, pc, target = 0;
+ int32_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLEZALC")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEZALC")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTZALC")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGTZALC")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BEQZALC")) {
+ if (rs_val == 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEZALC")) {
+ if (rs_val != 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+/*
+ Emulate below MIPS Non-Compact conditional branch and link instructions.
+ BLTZAL, BGEZAL :
+ BLTZALL, BGEZALL : Branch likely
+*/
+bool EmulateInstructionMIPS::Emulate_Bcond_Link(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int32_t offset, pc, target = 0;
+ int32_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZAL") || !strcasecmp(op_name, "BLTZALL")) {
+ if ((int32_t)rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGEZAL") ||
+ !strcasecmp(op_name, "BGEZALL")) {
+ if ((int32_t)rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BLTZL, BGEZL, BGTZL, BLEZL : Branch likely
+ BLTZ, BGEZ, BGTZ, BLEZ : Non-compact branches
+*/
+bool EmulateInstructionMIPS::Emulate_BXX_2ops(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int32_t offset, pc, target = 0;
+ int32_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZL") || !strcasecmp(op_name, "BLTZ")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGEZL") || !strcasecmp(op_name, "BGEZ")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGTZL") || !strcasecmp(op_name, "BGTZ")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BLEZL") || !strcasecmp(op_name, "BLEZ")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BLTZC, BLEZC, BGEZC, BGTZC, BEQZC, BNEZC : Compact Branches
+*/
+bool EmulateInstructionMIPS::Emulate_BXX_2ops_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int32_t offset, pc, target = 0;
+ int32_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZC")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLEZC")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEZC")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGTZC")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BEQZC")) {
+ if (rs_val == 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEZC")) {
+ if (rs_val != 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_B16_MM(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t offset, pc, target;
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ // unconditional branch
+ target = pc + offset;
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+/*
+ BEQZC, BNEZC are 32 bit compact instructions without a delay slot.
+ BEQZ16, BNEZ16 are 16 bit instructions with delay slot.
+ BGEZALS, BLTZALS are 16 bit instructions with short (2-byte) delay slot.
+*/
+bool EmulateInstructionMIPS::Emulate_Branch_MM(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t target = 0;
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+ bool update_ra = false;
+ uint32_t ra_offset = 0;
+
+ /*
+ * BEQZ16 rs, offset
+ * condition <- (GPR[rs] = 0)
+ * if condition then
+ * PC = PC + sign_ext (offset || 0)
+ *
+ * BNEZ16 rs, offset
+ * condition <- (GPR[rs] != 0)
+ * if condition then
+ * PC = PC + sign_ext (offset || 0)
+ *
+ * BEQZC rs, offset (compact instruction: No delay slot)
+ * condition <- (GPR[rs] == 0)
+ * if condition then
+ * PC = PC + 4 + sign_ext (offset || 0)
+ */
+
+ uint32_t rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ int32_t offset = insn.getOperand(1).getImm();
+
+ int32_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ int32_t rs_val = (int32_t)ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BEQZ16_MM")) {
+ if (rs_val == 0)
+ target = pc + offset;
+ else
+ target = pc + current_inst_size +
+ m_next_inst_size; // Skip delay slot instruction.
+ } else if (!strcasecmp(op_name, "BNEZ16_MM")) {
+ if (rs_val != 0)
+ target = pc + offset;
+ else
+ target = pc + current_inst_size +
+ m_next_inst_size; // Skip delay slot instruction.
+ } else if (!strcasecmp(op_name, "BEQZC_MM")) {
+ if (rs_val == 0)
+ target = pc + 4 + offset;
+ else
+ target =
+ pc +
+ 4; // 32 bit instruction and does not have delay slot instruction.
+ } else if (!strcasecmp(op_name, "BNEZC_MM")) {
+ if (rs_val != 0)
+ target = pc + 4 + offset;
+ else
+ target =
+ pc +
+ 4; // 32 bit instruction and does not have delay slot instruction.
+ } else if (!strcasecmp(op_name, "BGEZALS_MM")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 6; // 32 bit instruction with short (2-byte) delay slot
+
+ update_ra = true;
+ ra_offset = 6;
+ } else if (!strcasecmp(op_name, "BLTZALS_MM")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 6; // 32 bit instruction with short (2-byte) delay slot
+
+ update_ra = true;
+ ra_offset = 6;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (update_ra) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + ra_offset))
+ return false;
+ }
+ return true;
+}
+
+/* Emulate micromips jump instructions.
+ JALR16,JALRS16
+*/
+bool EmulateInstructionMIPS::Emulate_JALRx16_MM(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t ra_offset = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ uint32_t rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+
+ uint32_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ uint32_t rs_val = ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "JALR16_MM"))
+ ra_offset = 6; // 2-byte instruction with 4-byte delay slot.
+ else if (!strcasecmp(op_name, "JALRS16_MM"))
+ ra_offset = 4; // 2-byte instruction with 2-byte delay slot.
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ rs_val))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + ra_offset))
+ return false;
+
+ return true;
+}
+
+/* Emulate JALS and JALX instructions.
+ JALS 32 bit instruction with short (2-byte) delay slot.
+ JALX 32 bit instruction with 4-byte delay slot.
+*/
+bool EmulateInstructionMIPS::Emulate_JALx(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t offset = 0, target = 0, pc = 0, ra_offset = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ /*
+ * JALS target
+ * RA = PC + 6
+ * offset = sign_ext (offset << 1)
+ * PC = PC[31-27] | offset
+ * JALX target
+ * RA = PC + 8
+ * offset = sign_ext (offset << 2)
+ * PC = PC[31-28] | offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ // These are PC-region branches and not PC-relative.
+ if (!strcasecmp(op_name, "JALS_MM")) {
+ // target address is in the “current” 128 MB-aligned region
+ target = (pc & 0xF8000000UL) | offset;
+ ra_offset = 6;
+ } else if (!strcasecmp(op_name, "JALX_MM")) {
+ // target address is in the “current” 256 MB-aligned region
+ target = (pc & 0xF0000000UL) | offset;
+ ra_offset = 8;
+ }
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + ra_offset))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_JALRS(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs = 0, rt = 0;
+ int32_t pc = 0, rs_val = 0;
+
+ /*
+ JALRS rt, rs
+ GPR[rt] <- PC + 6
+ PC <- GPR[rs]
+ */
+
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rs = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ rs_val))
+ return false;
+
+ // This is 4-byte instruction with 2-byte delay slot.
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_zero_mips + rt,
+ pc + 6))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_BAL(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t offset, pc, target;
+
+ /*
+ * BAL offset
+ * offset = sign_ext (offset << 2)
+ * RA = PC + 8
+ * PC = PC + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_BALC(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t offset, pc, target;
+
+ /*
+ * BALC offset
+ * offset = sign_ext (offset << 2)
+ * RA = PC + 4
+ * PC = PC + 4 + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_BC(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t offset, pc, target;
+
+ /*
+ * BC offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC + 4 + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_J(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t offset, pc;
+
+ /*
+ * J offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC[63-28] | offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ /* This is a PC-region branch and not PC-relative */
+ pc = (pc & 0xF0000000UL) | offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, pc);
+}
+
+bool EmulateInstructionMIPS::Emulate_JAL(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t offset, target, pc;
+
+ /*
+ * JAL offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC[63-28] | offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ /* This is a PC-region branch and not PC-relative */
+ target = (pc & 0xF0000000UL) | offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_JALR(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ uint32_t pc, rs_val;
+
+ /*
+ * JALR rt, rs
+ * GPR[rt] = PC + 8
+ * PC = GPR[rs]
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rs = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + rs, 0,
+ &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ rs_val))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_zero_mips + rt,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_JIALC(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rt;
+ int32_t target, offset, pc, rt_val;
+
+ /*
+ * JIALC rt, offset
+ * offset = sign_ext (offset)
+ * PC = GPR[rt] + offset
+ * RA = PC + 4
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ target = rt_val + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_JIC(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rt;
+ int32_t target, offset, rt_val;
+
+ /*
+ * JIC rt, offset
+ * offset = sign_ext (offset)
+ * PC = GPR[rt] + offset
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ target = rt_val + offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_JR(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ uint32_t rs_val;
+
+ /*
+ * JR rs
+ * PC = GPR[rs]
+ */
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+
+ rs_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + rs, 0,
+ &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ rs_val);
+}
+
+/*
+ Emulate Branch on FP True/False
+ BC1F, BC1FL : Branch on FP False (L stands for branch likely)
+ BC1T, BC1TL : Branch on FP True (L stands for branch likely)
+*/
+bool EmulateInstructionMIPS::Emulate_FP_branch(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t cc, fcsr;
+ int32_t pc, offset, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ cc = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ fcsr = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success);
+ if (!success)
+ return false;
+
+ /* fcsr[23], fcsr[25-31] are vaild condition bits */
+ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01);
+
+ if (!strcasecmp(op_name, "BC1F") || !strcasecmp(op_name, "BC1FL")) {
+ if ((fcsr & (1 << cc)) == 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1T") || !strcasecmp(op_name, "BC1TL")) {
+ if ((fcsr & (1 << cc)) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_BC1EQZ(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t ft;
+ uint32_t ft_val;
+ int32_t target, pc, offset;
+
+ /*
+ * BC1EQZ ft, offset
+ * condition <- (FPR[ft].bit0 == 0)
+ * if condition then
+ * offset = sign_ext (offset)
+ * PC = PC + 4 + offset
+ */
+ ft = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ ft_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + ft, 0,
+ &success);
+ if (!success)
+ return false;
+
+ if ((ft_val & 1) == 0)
+ target = pc + 4 + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_BC1NEZ(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t ft;
+ uint32_t ft_val;
+ int32_t target, pc, offset;
+
+ /*
+ * BC1NEZ ft, offset
+ * condition <- (FPR[ft].bit0 != 0)
+ * if condition then
+ * offset = sign_ext (offset)
+ * PC = PC + 4 + offset
+ */
+ ft = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ ft_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + ft, 0,
+ &success);
+ if (!success)
+ return false;
+
+ if ((ft_val & 1) != 0)
+ target = pc + 4 + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+/*
+ Emulate MIPS-3D Branch instructions
+ BC1ANY2F, BC1ANY2T : Branch on Any of Two Floating Point Condition Codes
+ False/True
+ BC1ANY4F, BC1ANY4T : Branch on Any of Four Floating Point Condition Codes
+ False/True
+*/
+bool EmulateInstructionMIPS::Emulate_3D_branch(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t cc, fcsr;
+ int32_t pc, offset, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ cc = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ fcsr = (uint32_t)ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_fcsr_mips, 0,
+ &success);
+ if (!success)
+ return false;
+
+ /* fcsr[23], fcsr[25-31] are vaild condition bits */
+ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01);
+
+ if (!strcasecmp(op_name, "BC1ANY2F")) {
+ /* if any one bit is 0 */
+ if (((fcsr >> cc) & 3) != 3)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY2T")) {
+ /* if any one bit is 1 */
+ if (((fcsr >> cc) & 3) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY4F")) {
+ /* if any one bit is 0 */
+ if (((fcsr >> cc) & 0xf) != 0xf)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY4T")) {
+ /* if any one bit is 1 */
+ if (((fcsr >> cc) & 0xf) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_BNZB(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 1, true);
+}
+
+bool EmulateInstructionMIPS::Emulate_BNZH(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 2, true);
+}
+
+bool EmulateInstructionMIPS::Emulate_BNZW(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 4, true);
+}
+
+bool EmulateInstructionMIPS::Emulate_BNZD(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 8, true);
+}
+
+bool EmulateInstructionMIPS::Emulate_BZB(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 1, false);
+}
+
+bool EmulateInstructionMIPS::Emulate_BZH(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 2, false);
+}
+
+bool EmulateInstructionMIPS::Emulate_BZW(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 4, false);
+}
+
+bool EmulateInstructionMIPS::Emulate_BZD(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 8, false);
+}
+
+bool EmulateInstructionMIPS::Emulate_MSA_Branch_DF(llvm::MCInst &insn,
+ int element_byte_size,
+ bool bnz) {
+ bool success = false, branch_hit = true;
+ int32_t target = 0;
+ RegisterValue reg_value;
+ const uint8_t *ptr = nullptr;
+
+ uint32_t wt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ int32_t offset = insn.getOperand(1).getImm();
+
+ int32_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ if (ReadRegister(eRegisterKindDWARF, dwarf_w0_mips + wt, reg_value))
+ ptr = (const uint8_t *)reg_value.GetBytes();
+ else
+ return false;
+
+ for (int i = 0; i < 16 / element_byte_size; i++) {
+ switch (element_byte_size) {
+ case 1:
+ if ((*ptr == 0 && bnz) || (*ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 2:
+ if ((*(const uint16_t *)ptr == 0 && bnz) ||
+ (*(const uint16_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 4:
+ if ((*(const uint32_t *)ptr == 0 && bnz) ||
+ (*(const uint32_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 8:
+ if ((*(const uint64_t *)ptr == 0 && bnz) ||
+ (*(const uint64_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ }
+ if (!branch_hit)
+ break;
+ ptr = ptr + element_byte_size;
+ }
+
+ if (branch_hit)
+ target = pc + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_BNZV(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_V(insn, true);
+}
+
+bool EmulateInstructionMIPS::Emulate_BZV(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_V(insn, false);
+}
+
+bool EmulateInstructionMIPS::Emulate_MSA_Branch_V(llvm::MCInst &insn,
+ bool bnz) {
+ bool success = false;
+ int32_t target = 0;
+ llvm::APInt wr_val = llvm::APInt::getNullValue(128);
+ llvm::APInt fail_value = llvm::APInt::getMaxValue(128);
+ llvm::APInt zero_value = llvm::APInt::getNullValue(128);
+ RegisterValue reg_value;
+
+ uint32_t wt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ int32_t offset = insn.getOperand(1).getImm();
+
+ int32_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ if (ReadRegister(eRegisterKindDWARF, dwarf_w0_mips + wt, reg_value))
+ wr_val = reg_value.GetAsUInt128(fail_value);
+ else
+ return false;
+
+ if ((llvm::APInt::isSameValue(zero_value, wr_val) && !bnz) ||
+ (!llvm::APInt::isSameValue(zero_value, wr_val) && bnz))
+ target = pc + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_LDST_Imm(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t base;
+ int32_t imm, address;
+ Context bad_vaddr_context;
+
+ uint32_t num_operands = insn.getNumOperands();
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+ imm = insn.getOperand(num_operands - 1).getImm();
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ /* read base register */
+ address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_LDST_Reg(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t base, index;
+ int32_t address, index_address;
+ Context bad_vaddr_context;
+
+ uint32_t num_operands = insn.getNumOperands();
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+ index =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 1).getReg());
+
+ RegisterInfo reg_info_base, reg_info_index;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + index,
+ reg_info_index))
+ return false;
+
+ /* read base register */
+ address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ /* read index register */
+ index_address = (int32_t)ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + index, 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + index_address;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h
new file mode 100644
index 000000000000..cd447ae4975e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h
@@ -0,0 +1,221 @@
+//===-- EmulateInstructionMIPS.h ------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef EmulateInstructionMIPS_h_
+#define EmulateInstructionMIPS_h_
+
+namespace llvm {
+class MCDisassembler;
+class MCSubtargetInfo;
+class MCRegisterInfo;
+class MCAsmInfo;
+class MCContext;
+class MCInstrInfo;
+class MCInst;
+}
+
+namespace lldb_private {
+ class OptionValueDictionary;
+}
+
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Utility/Status.h"
+
+class EmulateInstructionMIPS : public lldb_private::EmulateInstruction {
+public:
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::EmulateInstruction *
+ CreateInstance(const lldb_private::ArchSpec &arch,
+ lldb_private::InstructionType inst_type);
+
+ static bool SupportsEmulatingInstructionsOfTypeStatic(
+ lldb_private::InstructionType inst_type) {
+ switch (inst_type) {
+ case lldb_private::eInstructionTypeAny:
+ case lldb_private::eInstructionTypePrologueEpilogue:
+ case lldb_private::eInstructionTypePCModifying:
+ return true;
+
+ case lldb_private::eInstructionTypeAll:
+ return false;
+ }
+ return false;
+ }
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ bool SetTargetTriple(const lldb_private::ArchSpec &arch) override;
+
+ EmulateInstructionMIPS(const lldb_private::ArchSpec &arch);
+
+ bool SupportsEmulatingInstructionsOfType(
+ lldb_private::InstructionType inst_type) override {
+ return SupportsEmulatingInstructionsOfTypeStatic(inst_type);
+ }
+
+ bool ReadInstruction() override;
+
+ bool EvaluateInstruction(uint32_t evaluate_options) override;
+
+ bool SetInstruction(const lldb_private::Opcode &insn_opcode,
+ const lldb_private::Address &inst_addr,
+ lldb_private::Target *target) override;
+
+ bool TestEmulation(lldb_private::Stream *out_stream,
+ lldb_private::ArchSpec &arch,
+ lldb_private::OptionValueDictionary *test_data) override {
+ return false;
+ }
+
+ bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
+ lldb_private::RegisterInfo &reg_info) override;
+
+ bool
+ CreateFunctionEntryUnwind(lldb_private::UnwindPlan &unwind_plan) override;
+
+protected:
+ typedef struct {
+ const char *op_name;
+ bool (EmulateInstructionMIPS::*callback)(llvm::MCInst &insn);
+ const char *insn_name;
+ } MipsOpcode;
+
+ static MipsOpcode *GetOpcodeForInstruction(const char *op_name);
+
+ uint32_t GetSizeOfInstruction(lldb_private::DataExtractor &data,
+ uint64_t inst_addr);
+
+ bool Emulate_ADDiu(llvm::MCInst &insn);
+
+ bool Emulate_SUBU_ADDU(llvm::MCInst &insn);
+
+ bool Emulate_LUI(llvm::MCInst &insn);
+
+ bool Emulate_SW(llvm::MCInst &insn);
+
+ bool Emulate_LW(llvm::MCInst &insn);
+
+ bool Emulate_ADDIUSP(llvm::MCInst &insn);
+
+ bool Emulate_ADDIUS5(llvm::MCInst &insn);
+
+ bool Emulate_SWSP(llvm::MCInst &insn);
+
+ bool Emulate_SWM16_32(llvm::MCInst &insn);
+
+ bool Emulate_LWSP(llvm::MCInst &insn);
+
+ bool Emulate_LWM16_32(llvm::MCInst &insn);
+
+ bool Emulate_JRADDIUSP(llvm::MCInst &insn);
+
+ bool Emulate_LDST_Imm(llvm::MCInst &insn);
+
+ bool Emulate_LDST_Reg(llvm::MCInst &insn);
+
+ bool Emulate_BXX_3ops(llvm::MCInst &insn);
+
+ bool Emulate_BXX_3ops_C(llvm::MCInst &insn);
+
+ bool Emulate_BXX_2ops(llvm::MCInst &insn);
+
+ bool Emulate_BXX_2ops_C(llvm::MCInst &insn);
+
+ bool Emulate_Bcond_Link_C(llvm::MCInst &insn);
+
+ bool Emulate_Bcond_Link(llvm::MCInst &insn);
+
+ bool Emulate_FP_branch(llvm::MCInst &insn);
+
+ bool Emulate_3D_branch(llvm::MCInst &insn);
+
+ bool Emulate_BAL(llvm::MCInst &insn);
+
+ bool Emulate_BALC(llvm::MCInst &insn);
+
+ bool Emulate_BC(llvm::MCInst &insn);
+
+ bool Emulate_J(llvm::MCInst &insn);
+
+ bool Emulate_JAL(llvm::MCInst &insn);
+
+ bool Emulate_JALR(llvm::MCInst &insn);
+
+ bool Emulate_JIALC(llvm::MCInst &insn);
+
+ bool Emulate_JIC(llvm::MCInst &insn);
+
+ bool Emulate_JR(llvm::MCInst &insn);
+
+ bool Emulate_BC1EQZ(llvm::MCInst &insn);
+
+ bool Emulate_BC1NEZ(llvm::MCInst &insn);
+
+ bool Emulate_BNZB(llvm::MCInst &insn);
+
+ bool Emulate_BNZH(llvm::MCInst &insn);
+
+ bool Emulate_BNZW(llvm::MCInst &insn);
+
+ bool Emulate_BNZD(llvm::MCInst &insn);
+
+ bool Emulate_BZB(llvm::MCInst &insn);
+
+ bool Emulate_BZH(llvm::MCInst &insn);
+
+ bool Emulate_BZW(llvm::MCInst &insn);
+
+ bool Emulate_BZD(llvm::MCInst &insn);
+
+ bool Emulate_MSA_Branch_DF(llvm::MCInst &insn, int element_byte_size,
+ bool bnz);
+
+ bool Emulate_BNZV(llvm::MCInst &insn);
+
+ bool Emulate_BZV(llvm::MCInst &insn);
+
+ bool Emulate_MSA_Branch_V(llvm::MCInst &insn, bool bnz);
+
+ bool Emulate_B16_MM(llvm::MCInst &insn);
+
+ bool Emulate_Branch_MM(llvm::MCInst &insn);
+
+ bool Emulate_JALRx16_MM(llvm::MCInst &insn);
+
+ bool Emulate_JALx(llvm::MCInst &insn);
+
+ bool Emulate_JALRS(llvm::MCInst &insn);
+
+ bool nonvolatile_reg_p(uint32_t regnum);
+
+ const char *GetRegisterName(unsigned reg_num, bool altnernate_name);
+
+private:
+ std::unique_ptr<llvm::MCDisassembler> m_disasm;
+ std::unique_ptr<llvm::MCDisassembler> m_alt_disasm;
+ std::unique_ptr<llvm::MCSubtargetInfo> m_subtype_info;
+ std::unique_ptr<llvm::MCSubtargetInfo> m_alt_subtype_info;
+ std::unique_ptr<llvm::MCRegisterInfo> m_reg_info;
+ std::unique_ptr<llvm::MCAsmInfo> m_asm_info;
+ std::unique_ptr<llvm::MCContext> m_context;
+ std::unique_ptr<llvm::MCInstrInfo> m_insn_info;
+ uint32_t m_next_inst_size;
+ bool m_use_alt_disaasm;
+};
+
+#endif // EmulateInstructionMIPS_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp
new file mode 100644
index 000000000000..69f0278d1437
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp
@@ -0,0 +1,2361 @@
+//===-- EmulateInstructionMIPS64.cpp -----------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "EmulateInstructionMIPS64.h"
+
+#include <stdlib.h>
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Opcode.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Stream.h"
+#include "llvm-c/Disassembler.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+#include "Plugins/Process/Utility/RegisterContext_mips.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define UInt(x) ((uint64_t)x)
+#define integer int64_t
+
+//
+// EmulateInstructionMIPS64 implementation
+//
+
+#ifdef __mips__
+extern "C" {
+void LLVMInitializeMipsTargetInfo();
+void LLVMInitializeMipsTarget();
+void LLVMInitializeMipsAsmPrinter();
+void LLVMInitializeMipsTargetMC();
+void LLVMInitializeMipsDisassembler();
+}
+#endif
+
+EmulateInstructionMIPS64::EmulateInstructionMIPS64(
+ const lldb_private::ArchSpec &arch)
+ : EmulateInstruction(arch) {
+ /* Create instance of llvm::MCDisassembler */
+ std::string Status;
+ llvm::Triple triple = arch.GetTriple();
+ const llvm::Target *target =
+ llvm::TargetRegistry::lookupTarget(triple.getTriple(), Status);
+
+/*
+ * If we fail to get the target then we haven't registered it. The
+ * SystemInitializerCommon
+ * does not initialize targets, MCs and disassemblers. However we need the
+ * MCDisassembler
+ * to decode the instructions so that the decoding complexity stays with LLVM.
+ * Initialize the MIPS targets and disassemblers.
+*/
+#ifdef __mips__
+ if (!target) {
+ LLVMInitializeMipsTargetInfo();
+ LLVMInitializeMipsTarget();
+ LLVMInitializeMipsAsmPrinter();
+ LLVMInitializeMipsTargetMC();
+ LLVMInitializeMipsDisassembler();
+ target = llvm::TargetRegistry::lookupTarget(triple.getTriple(), Status);
+ }
+#endif
+
+ assert(target);
+
+ llvm::StringRef cpu;
+
+ switch (arch.GetCore()) {
+ case ArchSpec::eCore_mips32:
+ case ArchSpec::eCore_mips32el:
+ cpu = "mips32";
+ break;
+ case ArchSpec::eCore_mips32r2:
+ case ArchSpec::eCore_mips32r2el:
+ cpu = "mips32r2";
+ break;
+ case ArchSpec::eCore_mips32r3:
+ case ArchSpec::eCore_mips32r3el:
+ cpu = "mips32r3";
+ break;
+ case ArchSpec::eCore_mips32r5:
+ case ArchSpec::eCore_mips32r5el:
+ cpu = "mips32r5";
+ break;
+ case ArchSpec::eCore_mips32r6:
+ case ArchSpec::eCore_mips32r6el:
+ cpu = "mips32r6";
+ break;
+ case ArchSpec::eCore_mips64:
+ case ArchSpec::eCore_mips64el:
+ cpu = "mips64";
+ break;
+ case ArchSpec::eCore_mips64r2:
+ case ArchSpec::eCore_mips64r2el:
+ cpu = "mips64r2";
+ break;
+ case ArchSpec::eCore_mips64r3:
+ case ArchSpec::eCore_mips64r3el:
+ cpu = "mips64r3";
+ break;
+ case ArchSpec::eCore_mips64r5:
+ case ArchSpec::eCore_mips64r5el:
+ cpu = "mips64r5";
+ break;
+ case ArchSpec::eCore_mips64r6:
+ case ArchSpec::eCore_mips64r6el:
+ cpu = "mips64r6";
+ break;
+ default:
+ cpu = "generic";
+ break;
+ }
+
+ std::string features = "";
+ uint32_t arch_flags = arch.GetFlags();
+ if (arch_flags & ArchSpec::eMIPSAse_msa)
+ features += "+msa,";
+ if (arch_flags & ArchSpec::eMIPSAse_dsp)
+ features += "+dsp,";
+ if (arch_flags & ArchSpec::eMIPSAse_dspr2)
+ features += "+dspr2,";
+ if (arch_flags & ArchSpec::eMIPSAse_mips16)
+ features += "+mips16,";
+ if (arch_flags & ArchSpec::eMIPSAse_micromips)
+ features += "+micromips,";
+
+ m_reg_info.reset(target->createMCRegInfo(triple.getTriple()));
+ assert(m_reg_info.get());
+
+ m_insn_info.reset(target->createMCInstrInfo());
+ assert(m_insn_info.get());
+
+ m_asm_info.reset(target->createMCAsmInfo(*m_reg_info, triple.getTriple()));
+ m_subtype_info.reset(
+ target->createMCSubtargetInfo(triple.getTriple(), cpu, features));
+ assert(m_asm_info.get() && m_subtype_info.get());
+
+ m_context.reset(
+ new llvm::MCContext(m_asm_info.get(), m_reg_info.get(), nullptr));
+ assert(m_context.get());
+
+ m_disasm.reset(target->createMCDisassembler(*m_subtype_info, *m_context));
+ assert(m_disasm.get());
+}
+
+void EmulateInstructionMIPS64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void EmulateInstructionMIPS64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString EmulateInstructionMIPS64::GetPluginNameStatic() {
+ ConstString g_plugin_name("lldb.emulate-instruction.mips64");
+ return g_plugin_name;
+}
+
+lldb_private::ConstString EmulateInstructionMIPS64::GetPluginName() {
+ static ConstString g_plugin_name("EmulateInstructionMIPS64");
+ return g_plugin_name;
+}
+
+const char *EmulateInstructionMIPS64::GetPluginDescriptionStatic() {
+ return "Emulate instructions for the MIPS64 architecture.";
+}
+
+EmulateInstruction *
+EmulateInstructionMIPS64::CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type) {
+ if (EmulateInstructionMIPS64::SupportsEmulatingInstructionsOfTypeStatic(
+ inst_type)) {
+ if (arch.GetTriple().getArch() == llvm::Triple::mips64 ||
+ arch.GetTriple().getArch() == llvm::Triple::mips64el) {
+ return new EmulateInstructionMIPS64(arch);
+ }
+ }
+
+ return nullptr;
+}
+
+bool EmulateInstructionMIPS64::SetTargetTriple(const ArchSpec &arch) {
+ return arch.GetTriple().getArch() == llvm::Triple::mips64 ||
+ arch.GetTriple().getArch() == llvm::Triple::mips64el;
+}
+
+const char *EmulateInstructionMIPS64::GetRegisterName(unsigned reg_num,
+ bool alternate_name) {
+ if (alternate_name) {
+ switch (reg_num) {
+ case dwarf_sp_mips64:
+ return "r29";
+ case dwarf_r30_mips64:
+ return "r30";
+ case dwarf_ra_mips64:
+ return "r31";
+ case dwarf_f0_mips64:
+ return "f0";
+ case dwarf_f1_mips64:
+ return "f1";
+ case dwarf_f2_mips64:
+ return "f2";
+ case dwarf_f3_mips64:
+ return "f3";
+ case dwarf_f4_mips64:
+ return "f4";
+ case dwarf_f5_mips64:
+ return "f5";
+ case dwarf_f6_mips64:
+ return "f6";
+ case dwarf_f7_mips64:
+ return "f7";
+ case dwarf_f8_mips64:
+ return "f8";
+ case dwarf_f9_mips64:
+ return "f9";
+ case dwarf_f10_mips64:
+ return "f10";
+ case dwarf_f11_mips64:
+ return "f11";
+ case dwarf_f12_mips64:
+ return "f12";
+ case dwarf_f13_mips64:
+ return "f13";
+ case dwarf_f14_mips64:
+ return "f14";
+ case dwarf_f15_mips64:
+ return "f15";
+ case dwarf_f16_mips64:
+ return "f16";
+ case dwarf_f17_mips64:
+ return "f17";
+ case dwarf_f18_mips64:
+ return "f18";
+ case dwarf_f19_mips64:
+ return "f19";
+ case dwarf_f20_mips64:
+ return "f20";
+ case dwarf_f21_mips64:
+ return "f21";
+ case dwarf_f22_mips64:
+ return "f22";
+ case dwarf_f23_mips64:
+ return "f23";
+ case dwarf_f24_mips64:
+ return "f24";
+ case dwarf_f25_mips64:
+ return "f25";
+ case dwarf_f26_mips64:
+ return "f26";
+ case dwarf_f27_mips64:
+ return "f27";
+ case dwarf_f28_mips64:
+ return "f28";
+ case dwarf_f29_mips64:
+ return "f29";
+ case dwarf_f30_mips64:
+ return "f30";
+ case dwarf_f31_mips64:
+ return "f31";
+ case dwarf_w0_mips64:
+ return "w0";
+ case dwarf_w1_mips64:
+ return "w1";
+ case dwarf_w2_mips64:
+ return "w2";
+ case dwarf_w3_mips64:
+ return "w3";
+ case dwarf_w4_mips64:
+ return "w4";
+ case dwarf_w5_mips64:
+ return "w5";
+ case dwarf_w6_mips64:
+ return "w6";
+ case dwarf_w7_mips64:
+ return "w7";
+ case dwarf_w8_mips64:
+ return "w8";
+ case dwarf_w9_mips64:
+ return "w9";
+ case dwarf_w10_mips64:
+ return "w10";
+ case dwarf_w11_mips64:
+ return "w11";
+ case dwarf_w12_mips64:
+ return "w12";
+ case dwarf_w13_mips64:
+ return "w13";
+ case dwarf_w14_mips64:
+ return "w14";
+ case dwarf_w15_mips64:
+ return "w15";
+ case dwarf_w16_mips64:
+ return "w16";
+ case dwarf_w17_mips64:
+ return "w17";
+ case dwarf_w18_mips64:
+ return "w18";
+ case dwarf_w19_mips64:
+ return "w19";
+ case dwarf_w20_mips64:
+ return "w20";
+ case dwarf_w21_mips64:
+ return "w21";
+ case dwarf_w22_mips64:
+ return "w22";
+ case dwarf_w23_mips64:
+ return "w23";
+ case dwarf_w24_mips64:
+ return "w24";
+ case dwarf_w25_mips64:
+ return "w25";
+ case dwarf_w26_mips64:
+ return "w26";
+ case dwarf_w27_mips64:
+ return "w27";
+ case dwarf_w28_mips64:
+ return "w28";
+ case dwarf_w29_mips64:
+ return "w29";
+ case dwarf_w30_mips64:
+ return "w30";
+ case dwarf_w31_mips64:
+ return "w31";
+ case dwarf_mir_mips64:
+ return "mir";
+ case dwarf_mcsr_mips64:
+ return "mcsr";
+ case dwarf_config5_mips64:
+ return "config5";
+ default:
+ break;
+ }
+ return nullptr;
+ }
+
+ switch (reg_num) {
+ case dwarf_zero_mips64:
+ return "r0";
+ case dwarf_r1_mips64:
+ return "r1";
+ case dwarf_r2_mips64:
+ return "r2";
+ case dwarf_r3_mips64:
+ return "r3";
+ case dwarf_r4_mips64:
+ return "r4";
+ case dwarf_r5_mips64:
+ return "r5";
+ case dwarf_r6_mips64:
+ return "r6";
+ case dwarf_r7_mips64:
+ return "r7";
+ case dwarf_r8_mips64:
+ return "r8";
+ case dwarf_r9_mips64:
+ return "r9";
+ case dwarf_r10_mips64:
+ return "r10";
+ case dwarf_r11_mips64:
+ return "r11";
+ case dwarf_r12_mips64:
+ return "r12";
+ case dwarf_r13_mips64:
+ return "r13";
+ case dwarf_r14_mips64:
+ return "r14";
+ case dwarf_r15_mips64:
+ return "r15";
+ case dwarf_r16_mips64:
+ return "r16";
+ case dwarf_r17_mips64:
+ return "r17";
+ case dwarf_r18_mips64:
+ return "r18";
+ case dwarf_r19_mips64:
+ return "r19";
+ case dwarf_r20_mips64:
+ return "r20";
+ case dwarf_r21_mips64:
+ return "r21";
+ case dwarf_r22_mips64:
+ return "r22";
+ case dwarf_r23_mips64:
+ return "r23";
+ case dwarf_r24_mips64:
+ return "r24";
+ case dwarf_r25_mips64:
+ return "r25";
+ case dwarf_r26_mips64:
+ return "r26";
+ case dwarf_r27_mips64:
+ return "r27";
+ case dwarf_gp_mips64:
+ return "gp";
+ case dwarf_sp_mips64:
+ return "sp";
+ case dwarf_r30_mips64:
+ return "fp";
+ case dwarf_ra_mips64:
+ return "ra";
+ case dwarf_sr_mips64:
+ return "sr";
+ case dwarf_lo_mips64:
+ return "lo";
+ case dwarf_hi_mips64:
+ return "hi";
+ case dwarf_bad_mips64:
+ return "bad";
+ case dwarf_cause_mips64:
+ return "cause";
+ case dwarf_pc_mips64:
+ return "pc";
+ case dwarf_f0_mips64:
+ return "f0";
+ case dwarf_f1_mips64:
+ return "f1";
+ case dwarf_f2_mips64:
+ return "f2";
+ case dwarf_f3_mips64:
+ return "f3";
+ case dwarf_f4_mips64:
+ return "f4";
+ case dwarf_f5_mips64:
+ return "f5";
+ case dwarf_f6_mips64:
+ return "f6";
+ case dwarf_f7_mips64:
+ return "f7";
+ case dwarf_f8_mips64:
+ return "f8";
+ case dwarf_f9_mips64:
+ return "f9";
+ case dwarf_f10_mips64:
+ return "f10";
+ case dwarf_f11_mips64:
+ return "f11";
+ case dwarf_f12_mips64:
+ return "f12";
+ case dwarf_f13_mips64:
+ return "f13";
+ case dwarf_f14_mips64:
+ return "f14";
+ case dwarf_f15_mips64:
+ return "f15";
+ case dwarf_f16_mips64:
+ return "f16";
+ case dwarf_f17_mips64:
+ return "f17";
+ case dwarf_f18_mips64:
+ return "f18";
+ case dwarf_f19_mips64:
+ return "f19";
+ case dwarf_f20_mips64:
+ return "f20";
+ case dwarf_f21_mips64:
+ return "f21";
+ case dwarf_f22_mips64:
+ return "f22";
+ case dwarf_f23_mips64:
+ return "f23";
+ case dwarf_f24_mips64:
+ return "f24";
+ case dwarf_f25_mips64:
+ return "f25";
+ case dwarf_f26_mips64:
+ return "f26";
+ case dwarf_f27_mips64:
+ return "f27";
+ case dwarf_f28_mips64:
+ return "f28";
+ case dwarf_f29_mips64:
+ return "f29";
+ case dwarf_f30_mips64:
+ return "f30";
+ case dwarf_f31_mips64:
+ return "f31";
+ case dwarf_fcsr_mips64:
+ return "fcsr";
+ case dwarf_fir_mips64:
+ return "fir";
+ case dwarf_w0_mips64:
+ return "w0";
+ case dwarf_w1_mips64:
+ return "w1";
+ case dwarf_w2_mips64:
+ return "w2";
+ case dwarf_w3_mips64:
+ return "w3";
+ case dwarf_w4_mips64:
+ return "w4";
+ case dwarf_w5_mips64:
+ return "w5";
+ case dwarf_w6_mips64:
+ return "w6";
+ case dwarf_w7_mips64:
+ return "w7";
+ case dwarf_w8_mips64:
+ return "w8";
+ case dwarf_w9_mips64:
+ return "w9";
+ case dwarf_w10_mips64:
+ return "w10";
+ case dwarf_w11_mips64:
+ return "w11";
+ case dwarf_w12_mips64:
+ return "w12";
+ case dwarf_w13_mips64:
+ return "w13";
+ case dwarf_w14_mips64:
+ return "w14";
+ case dwarf_w15_mips64:
+ return "w15";
+ case dwarf_w16_mips64:
+ return "w16";
+ case dwarf_w17_mips64:
+ return "w17";
+ case dwarf_w18_mips64:
+ return "w18";
+ case dwarf_w19_mips64:
+ return "w19";
+ case dwarf_w20_mips64:
+ return "w20";
+ case dwarf_w21_mips64:
+ return "w21";
+ case dwarf_w22_mips64:
+ return "w22";
+ case dwarf_w23_mips64:
+ return "w23";
+ case dwarf_w24_mips64:
+ return "w24";
+ case dwarf_w25_mips64:
+ return "w25";
+ case dwarf_w26_mips64:
+ return "w26";
+ case dwarf_w27_mips64:
+ return "w27";
+ case dwarf_w28_mips64:
+ return "w28";
+ case dwarf_w29_mips64:
+ return "w29";
+ case dwarf_w30_mips64:
+ return "w30";
+ case dwarf_w31_mips64:
+ return "w31";
+ case dwarf_mcsr_mips64:
+ return "mcsr";
+ case dwarf_mir_mips64:
+ return "mir";
+ case dwarf_config5_mips64:
+ return "config5";
+ }
+ return nullptr;
+}
+
+bool EmulateInstructionMIPS64::GetRegisterInfo(RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterInfo &reg_info) {
+ if (reg_kind == eRegisterKindGeneric) {
+ switch (reg_num) {
+ case LLDB_REGNUM_GENERIC_PC:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_pc_mips64;
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_sp_mips64;
+ break;
+ case LLDB_REGNUM_GENERIC_FP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_r30_mips64;
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_ra_mips64;
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_sr_mips64;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ if (reg_kind == eRegisterKindDWARF) {
+ ::memset(&reg_info, 0, sizeof(RegisterInfo));
+ ::memset(reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds));
+
+ if (reg_num == dwarf_sr_mips64 || reg_num == dwarf_fcsr_mips64 ||
+ reg_num == dwarf_fir_mips64 || reg_num == dwarf_mcsr_mips64 ||
+ reg_num == dwarf_mir_mips64 || reg_num == dwarf_config5_mips64) {
+ reg_info.byte_size = 4;
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ } else if ((int)reg_num >= dwarf_zero_mips64 &&
+ (int)reg_num <= dwarf_f31_mips64) {
+ reg_info.byte_size = 8;
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ } else if ((int)reg_num >= dwarf_w0_mips64 &&
+ (int)reg_num <= dwarf_w31_mips64) {
+ reg_info.byte_size = 16;
+ reg_info.format = eFormatVectorOfUInt8;
+ reg_info.encoding = eEncodingVector;
+ } else {
+ return false;
+ }
+
+ reg_info.name = GetRegisterName(reg_num, false);
+ reg_info.alt_name = GetRegisterName(reg_num, true);
+ reg_info.kinds[eRegisterKindDWARF] = reg_num;
+
+ switch (reg_num) {
+ case dwarf_r30_mips64:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ break;
+ case dwarf_ra_mips64:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA;
+ break;
+ case dwarf_sp_mips64:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ break;
+ case dwarf_pc_mips64:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ break;
+ case dwarf_sr_mips64:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+EmulateInstructionMIPS64::MipsOpcode *
+EmulateInstructionMIPS64::GetOpcodeForInstruction(const char *op_name) {
+ static EmulateInstructionMIPS64::MipsOpcode g_opcodes[] = {
+ // Prologue/Epilogue instructions
+ {"DADDiu", &EmulateInstructionMIPS64::Emulate_DADDiu,
+ "DADDIU rt, rs, immediate"},
+ {"ADDiu", &EmulateInstructionMIPS64::Emulate_DADDiu,
+ "ADDIU rt, rs, immediate"},
+ {"SD", &EmulateInstructionMIPS64::Emulate_SD, "SD rt, offset(rs)"},
+ {"LD", &EmulateInstructionMIPS64::Emulate_LD, "LD rt, offset(base)"},
+ {"DSUBU", &EmulateInstructionMIPS64::Emulate_DSUBU_DADDU,
+ "DSUBU rd, rs, rt"},
+ {"SUBU", &EmulateInstructionMIPS64::Emulate_DSUBU_DADDU,
+ "SUBU rd, rs, rt"},
+ {"DADDU", &EmulateInstructionMIPS64::Emulate_DSUBU_DADDU,
+ "DADDU rd, rs, rt"},
+ {"ADDU", &EmulateInstructionMIPS64::Emulate_DSUBU_DADDU,
+ "ADDU rd, rs, rt"},
+ {"LUI", &EmulateInstructionMIPS64::Emulate_LUI, "LUI rt, immediate"},
+
+ // Load/Store instructions
+ /* Following list of emulated instructions are required by implementation
+ of hardware watchpoint
+ for MIPS in lldb. As we just need the address accessed by instructions,
+ we have generalised
+ all these instructions in 2 functions depending on their addressing
+ modes */
+
+ {"LB", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LB rt, offset(base)"},
+ {"LBE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LBE rt, offset(base)"},
+ {"LBU", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LBU rt, offset(base)"},
+ {"LBUE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LBUE rt, offset(base)"},
+ {"LDC1", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LDC1 ft, offset(base)"},
+ {"LDL", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LDL rt, offset(base)"},
+ {"LDR", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LDR rt, offset(base)"},
+ {"LLD", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LLD rt, offset(base)"},
+ {"LDC2", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LDC2 rt, offset(base)"},
+ {"LDXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "LDXC1 fd, index (base)"},
+ {"LH", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LH rt, offset(base)"},
+ {"LHE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LHE rt, offset(base)"},
+ {"LHU", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LHU rt, offset(base)"},
+ {"LHUE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LHUE rt, offset(base)"},
+ {"LL", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LL rt, offset(base)"},
+ {"LLE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LLE rt, offset(base)"},
+ {"LUXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "LUXC1 fd, index (base)"},
+ {"LW", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LW rt, offset(rs)"},
+ {"LWC1", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWC1 ft, offset(base)"},
+ {"LWC2", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWC2 rt, offset(base)"},
+ {"LWE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWE rt, offset(base)"},
+ {"LWL", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWL rt, offset(base)"},
+ {"LWLE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWLE rt, offset(base)"},
+ {"LWR", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWR rt, offset(base)"},
+ {"LWRE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWRE rt, offset(base)"},
+ {"LWXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "LWXC1 fd, index (base)"},
+
+ {"SB", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SB rt, offset(base)"},
+ {"SBE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SBE rt, offset(base)"},
+ {"SC", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SC rt, offset(base)"},
+ {"SCE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SCE rt, offset(base)"},
+ {"SCD", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SCD rt, offset(base)"},
+ {"SDL", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SDL rt, offset(base)"},
+ {"SDR", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SDR rt, offset(base)"},
+ {"SDC1", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SDC1 ft, offset(base)"},
+ {"SDC2", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SDC2 rt, offset(base)"},
+ {"SDXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "SDXC1 fs, index (base)"},
+ {"SH", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SH rt, offset(base)"},
+ {"SHE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SHE rt, offset(base)"},
+ {"SUXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "SUXC1 fs, index (base)"},
+ {"SW", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SW rt, offset(rs)"},
+ {"SWC1", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWC1 ft, offset(base)"},
+ {"SWC2", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWC2 rt, offset(base)"},
+ {"SWE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWE rt, offset(base)"},
+ {"SWL", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWL rt, offset(base)"},
+ {"SWLE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWLE rt, offset(base)"},
+ {"SWR", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWR rt, offset(base)"},
+ {"SWRE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWRE rt, offset(base)"},
+ {"SWXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "SWXC1 fs, index (base)"},
+
+ // Branch instructions
+ {"BEQ", &EmulateInstructionMIPS64::Emulate_BXX_3ops, "BEQ rs,rt,offset"},
+ {"BEQ64", &EmulateInstructionMIPS64::Emulate_BXX_3ops, "BEQ rs,rt,offset"},
+ {"BNE", &EmulateInstructionMIPS64::Emulate_BXX_3ops, "BNE rs,rt,offset"},
+ {"BNE64", &EmulateInstructionMIPS64::Emulate_BXX_3ops, "BNE rs,rt,offset"},
+ {"BEQL", &EmulateInstructionMIPS64::Emulate_BXX_3ops,
+ "BEQL rs,rt,offset"},
+ {"BNEL", &EmulateInstructionMIPS64::Emulate_BXX_3ops,
+ "BNEL rs,rt,offset"},
+ {"BGEZALL", &EmulateInstructionMIPS64::Emulate_Bcond_Link,
+ "BGEZALL rt,offset"},
+ {"BAL", &EmulateInstructionMIPS64::Emulate_BAL, "BAL offset"},
+ {"BGEZAL", &EmulateInstructionMIPS64::Emulate_Bcond_Link,
+ "BGEZAL rs,offset"},
+ {"BALC", &EmulateInstructionMIPS64::Emulate_BALC, "BALC offset"},
+ {"BC", &EmulateInstructionMIPS64::Emulate_BC, "BC offset"},
+ {"BGEZ", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGEZ rs,offset"},
+ {"BGEZ64", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGEZ rs,offset"},
+ {"BLEZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BLEZALC rs,offset"},
+ {"BGEZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BGEZALC rs,offset"},
+ {"BLTZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BLTZALC rs,offset"},
+ {"BGTZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BGTZALC rs,offset"},
+ {"BEQZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BEQZALC rs,offset"},
+ {"BNEZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BNEZALC rs,offset"},
+ {"BEQC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BEQC rs,rt,offset"},
+ {"BEQC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BEQC rs,rt,offset"},
+ {"BNEC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BNEC rs,rt,offset"},
+ {"BNEC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BNEC rs,rt,offset"},
+ {"BLTC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BLTC rs,rt,offset"},
+ {"BLTC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BLTC rs,rt,offset"},
+ {"BGEC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BGEC rs,rt,offset"},
+ {"BGEC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BGEC rs,rt,offset"},
+ {"BLTUC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BLTUC rs,rt,offset"},
+ {"BLTUC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BLTUC rs,rt,offset"},
+ {"BGEUC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BGEUC rs,rt,offset"},
+ {"BGEUC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BGEUC rs,rt,offset"},
+ {"BLTZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BLTZC rt,offset"},
+ {"BLTZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BLTZC rt,offset"},
+ {"BLEZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BLEZC rt,offset"},
+ {"BLEZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BLEZC rt,offset"},
+ {"BGEZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BGEZC rt,offset"},
+ {"BGEZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BGEZC rt,offset"},
+ {"BGTZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BGTZC rt,offset"},
+ {"BGTZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BGTZC rt,offset"},
+ {"BEQZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BEQZC rt,offset"},
+ {"BEQZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BEQZC rt,offset"},
+ {"BNEZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BNEZC rt,offset"},
+ {"BNEZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BNEZC rt,offset"},
+ {"BGEZL", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGEZL rt,offset"},
+ {"BGTZ", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGTZ rt,offset"},
+ {"BGTZ64", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGTZ rt,offset"},
+ {"BGTZL", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGTZL rt,offset"},
+ {"BLEZ", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLEZ rt,offset"},
+ {"BLEZ64", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLEZ rt,offset"},
+ {"BLEZL", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLEZL rt,offset"},
+ {"BLTZ", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLTZ rt,offset"},
+ {"BLTZ64", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLTZ rt,offset"},
+ {"BLTZAL", &EmulateInstructionMIPS64::Emulate_Bcond_Link,
+ "BLTZAL rt,offset"},
+ {"BLTZALL", &EmulateInstructionMIPS64::Emulate_Bcond_Link,
+ "BLTZALL rt,offset"},
+ {"BLTZL", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLTZL rt,offset"},
+ {"BOVC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BOVC rs,rt,offset"},
+ {"BNVC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BNVC rs,rt,offset"},
+ {"J", &EmulateInstructionMIPS64::Emulate_J, "J target"},
+ {"JAL", &EmulateInstructionMIPS64::Emulate_JAL, "JAL target"},
+ {"JALX", &EmulateInstructionMIPS64::Emulate_JAL, "JALX target"},
+ {"JALR", &EmulateInstructionMIPS64::Emulate_JALR, "JALR target"},
+ {"JALR64", &EmulateInstructionMIPS64::Emulate_JALR, "JALR target"},
+ {"JALR_HB", &EmulateInstructionMIPS64::Emulate_JALR, "JALR.HB target"},
+ {"JIALC", &EmulateInstructionMIPS64::Emulate_JIALC, "JIALC rt,offset"},
+ {"JIALC64", &EmulateInstructionMIPS64::Emulate_JIALC, "JIALC rt,offset"},
+ {"JIC", &EmulateInstructionMIPS64::Emulate_JIC, "JIC rt,offset"},
+ {"JIC64", &EmulateInstructionMIPS64::Emulate_JIC, "JIC rt,offset"},
+ {"JR", &EmulateInstructionMIPS64::Emulate_JR, "JR target"},
+ {"JR64", &EmulateInstructionMIPS64::Emulate_JR, "JR target"},
+ {"JR_HB", &EmulateInstructionMIPS64::Emulate_JR, "JR.HB target"},
+ {"BC1F", &EmulateInstructionMIPS64::Emulate_FP_branch, "BC1F cc, offset"},
+ {"BC1T", &EmulateInstructionMIPS64::Emulate_FP_branch, "BC1T cc, offset"},
+ {"BC1FL", &EmulateInstructionMIPS64::Emulate_FP_branch,
+ "BC1FL cc, offset"},
+ {"BC1TL", &EmulateInstructionMIPS64::Emulate_FP_branch,
+ "BC1TL cc, offset"},
+ {"BC1EQZ", &EmulateInstructionMIPS64::Emulate_BC1EQZ,
+ "BC1EQZ ft, offset"},
+ {"BC1NEZ", &EmulateInstructionMIPS64::Emulate_BC1NEZ,
+ "BC1NEZ ft, offset"},
+ {"BC1ANY2F", &EmulateInstructionMIPS64::Emulate_3D_branch,
+ "BC1ANY2F cc, offset"},
+ {"BC1ANY2T", &EmulateInstructionMIPS64::Emulate_3D_branch,
+ "BC1ANY2T cc, offset"},
+ {"BC1ANY4F", &EmulateInstructionMIPS64::Emulate_3D_branch,
+ "BC1ANY4F cc, offset"},
+ {"BC1ANY4T", &EmulateInstructionMIPS64::Emulate_3D_branch,
+ "BC1ANY4T cc, offset"},
+ {"BNZ_B", &EmulateInstructionMIPS64::Emulate_BNZB, "BNZ.b wt,s16"},
+ {"BNZ_H", &EmulateInstructionMIPS64::Emulate_BNZH, "BNZ.h wt,s16"},
+ {"BNZ_W", &EmulateInstructionMIPS64::Emulate_BNZW, "BNZ.w wt,s16"},
+ {"BNZ_D", &EmulateInstructionMIPS64::Emulate_BNZD, "BNZ.d wt,s16"},
+ {"BZ_B", &EmulateInstructionMIPS64::Emulate_BZB, "BZ.b wt,s16"},
+ {"BZ_H", &EmulateInstructionMIPS64::Emulate_BZH, "BZ.h wt,s16"},
+ {"BZ_W", &EmulateInstructionMIPS64::Emulate_BZW, "BZ.w wt,s16"},
+ {"BZ_D", &EmulateInstructionMIPS64::Emulate_BZD, "BZ.d wt,s16"},
+ {"BNZ_V", &EmulateInstructionMIPS64::Emulate_BNZV, "BNZ.V wt,s16"},
+ {"BZ_V", &EmulateInstructionMIPS64::Emulate_BZV, "BZ.V wt,s16"},
+ };
+
+ static const size_t k_num_mips_opcodes = llvm::array_lengthof(g_opcodes);
+
+ for (size_t i = 0; i < k_num_mips_opcodes; ++i) {
+ if (!strcasecmp(g_opcodes[i].op_name, op_name))
+ return &g_opcodes[i];
+ }
+
+ return nullptr;
+}
+
+bool EmulateInstructionMIPS64::ReadInstruction() {
+ bool success = false;
+ m_addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_ADDRESS, &success);
+ if (success) {
+ Context read_inst_context;
+ read_inst_context.type = eContextReadOpcode;
+ read_inst_context.SetNoArgs();
+ m_opcode.SetOpcode32(
+ ReadMemoryUnsigned(read_inst_context, m_addr, 4, 0, &success),
+ GetByteOrder());
+ }
+ if (!success)
+ m_addr = LLDB_INVALID_ADDRESS;
+ return success;
+}
+
+bool EmulateInstructionMIPS64::EvaluateInstruction(uint32_t evaluate_options) {
+ bool success = false;
+ llvm::MCInst mc_insn;
+ uint64_t insn_size;
+ DataExtractor data;
+
+ /* Keep the complexity of the decode logic with the llvm::MCDisassembler
+ * class. */
+ if (m_opcode.GetData(data)) {
+ llvm::MCDisassembler::DecodeStatus decode_status;
+ llvm::ArrayRef<uint8_t> raw_insn(data.GetDataStart(), data.GetByteSize());
+ decode_status = m_disasm->getInstruction(
+ mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls());
+ if (decode_status != llvm::MCDisassembler::Success)
+ return false;
+ }
+
+ /*
+ * mc_insn.getOpcode() returns decoded opcode. However to make use
+ * of llvm::Mips::<insn> we would need "MipsGenInstrInfo.inc".
+ */
+ const char *op_name = m_insn_info->getName(mc_insn.getOpcode()).data();
+
+ if (op_name == nullptr)
+ return false;
+
+ /*
+ * Decoding has been done already. Just get the call-back function
+ * and emulate the instruction.
+ */
+ MipsOpcode *opcode_data = GetOpcodeForInstruction(op_name);
+
+ if (opcode_data == nullptr)
+ return false;
+
+ uint64_t old_pc = 0, new_pc = 0;
+ const bool auto_advance_pc =
+ evaluate_options & eEmulateInstructionOptionAutoAdvancePC;
+
+ if (auto_advance_pc) {
+ old_pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+ }
+
+ /* emulate instruction */
+ success = (this->*opcode_data->callback)(mc_insn);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc) {
+ new_pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ /* If we haven't changed the PC, change it here */
+ if (old_pc == new_pc) {
+ new_pc += 4;
+ Context context;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ new_pc))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::CreateFunctionEntryUnwind(
+ UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const bool can_replace = false;
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_sp_mips64, 0);
+
+ // Our previous PC is in the RA
+ row->SetRegisterLocationToRegister(dwarf_pc_mips64, dwarf_ra_mips64,
+ can_replace);
+
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+ unwind_plan.SetSourceName("EmulateInstructionMIPS64");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ unwind_plan.SetReturnAddressRegister(dwarf_ra_mips64);
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::nonvolatile_reg_p(uint64_t regnum) {
+ switch (regnum) {
+ case dwarf_r16_mips64:
+ case dwarf_r17_mips64:
+ case dwarf_r18_mips64:
+ case dwarf_r19_mips64:
+ case dwarf_r20_mips64:
+ case dwarf_r21_mips64:
+ case dwarf_r22_mips64:
+ case dwarf_r23_mips64:
+ case dwarf_gp_mips64:
+ case dwarf_sp_mips64:
+ case dwarf_r30_mips64:
+ case dwarf_ra_mips64:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+bool EmulateInstructionMIPS64::Emulate_DADDiu(llvm::MCInst &insn) {
+ // DADDIU rt, rs, immediate
+ // GPR[rt] <- GPR[rs] + sign_extend(immediate)
+
+ uint8_t dst, src;
+ bool success = false;
+ const uint32_t imm16 = insn.getOperand(2).getImm();
+ int64_t imm = SignedBits(imm16, 15, 0);
+
+ dst = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ src = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ // If immediate is greater than 2^16 - 1 then clang generate LUI,
+ // (D)ADDIU,(D)SUBU instructions in prolog. Example lui $1, 0x2 daddiu $1,
+ // $1, -0x5920 dsubu $sp, $sp, $1 In this case, (D)ADDIU dst and src will be
+ // same and not equal to sp
+ if (dst == src) {
+ Context context;
+
+ /* read <src> register */
+ const uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips64 + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* Check if this is daddiu sp, sp, imm16 */
+ if (dst == dwarf_sp_mips64) {
+ /*
+ * From the MIPS IV spec:
+ *
+ * The term “unsigned” in the instruction name is a misnomer; this
+ * operation is 64-bit modulo arithmetic that does not trap on overflow.
+ * It is appropriate for arithmetic which is not signed, such as address
+ * arithmetic, or integer arithmetic environments that ignore overflow,
+ * such as “C” language arithmetic.
+ *
+ * Assume 2's complement and rely on unsigned overflow here.
+ */
+ uint64_t result = src_opd_val + imm;
+ RegisterInfo reg_info_sp;
+
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips64, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, imm);
+
+ /* We are allocating bytes on stack */
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips64,
+ result);
+ return true;
+ }
+
+ imm += src_opd_val;
+ context.SetImmediateSigned(imm);
+ context.type = eContextImmediate;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips64 + dst, imm))
+ return false;
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_SD(llvm::MCInst &insn) {
+ uint64_t address;
+ RegisterInfo reg_info_base;
+ RegisterInfo reg_info_src;
+ bool success = false;
+ uint32_t imm16 = insn.getOperand(2).getImm();
+ uint64_t imm = SignedBits(imm16, 15, 0);
+ uint32_t src, base;
+ Context bad_vaddr_context;
+
+ src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips64 + base,
+ reg_info_base) ||
+ !GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips64 + src,
+ reg_info_src))
+ return false;
+
+ /* read SP */
+ address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + base,
+ 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* We look for sp based non-volatile register stores */
+ if (nonvolatile_reg_p(src)) {
+ Context context;
+ RegisterValue data_src;
+ context.type = eContextPushRegisterOnStack;
+ context.SetRegisterToRegisterPlusOffset(reg_info_src, reg_info_base, 0);
+
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ Status error;
+
+ if (!ReadRegister(&reg_info_base, data_src))
+ return false;
+
+ if (data_src.GetAsMemoryData(&reg_info_src, buffer, reg_info_src.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context, address, buffer, reg_info_src.byte_size))
+ return false;
+ }
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips64,
+ address);
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_LD(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t src, base;
+ int64_t imm, address;
+ Context bad_vaddr_context;
+
+ src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ imm = insn.getOperand(2).getImm();
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips64 + base,
+ reg_info_base))
+ return false;
+
+ /* read base register */
+ address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + base,
+ 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips64,
+ address);
+
+ if (nonvolatile_reg_p(src)) {
+ RegisterValue data_src;
+ RegisterInfo reg_info_src;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips64 + src,
+ reg_info_src))
+ return false;
+
+ Context context;
+ context.type = eContextRegisterLoad;
+
+ return WriteRegister(context, &reg_info_src, data_src);
+ }
+
+ return false;
+}
+
+bool EmulateInstructionMIPS64::Emulate_LUI(llvm::MCInst &insn) {
+ // LUI rt, immediate
+ // GPR[rt] <- sign_extend(immediate << 16)
+
+ const uint32_t imm32 = insn.getOperand(1).getImm() << 16;
+ int64_t imm = SignedBits(imm32, 31, 0);
+ uint8_t rt;
+ Context context;
+
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ context.SetImmediateSigned(imm);
+ context.type = eContextImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, imm);
+}
+
+bool EmulateInstructionMIPS64::Emulate_DSUBU_DADDU(llvm::MCInst &insn) {
+ // DSUBU sp, <src>, <rt>
+ // DADDU sp, <src>, <rt>
+ // DADDU dst, sp, <rt>
+
+ bool success = false;
+ uint64_t result;
+ uint8_t src, dst, rt;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ dst = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ src = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ /* Check if sp is destination register */
+ if (dst == dwarf_sp_mips64) {
+ rt = m_reg_info->getEncodingValue(insn.getOperand(2).getReg());
+
+ /* read <src> register */
+ uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips64 + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* read <rt > register */
+ uint64_t rt_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "DSUBU") || !strcasecmp(op_name, "SUBU"))
+ result = src_opd_val - rt_opd_val;
+ else
+ result = src_opd_val + rt_opd_val;
+
+ Context context;
+ RegisterInfo reg_info_sp;
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips64, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, rt_opd_val);
+
+ /* We are allocating bytes on stack */
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips64, result);
+
+ return true;
+ } else if (src == dwarf_sp_mips64) {
+ rt = m_reg_info->getEncodingValue(insn.getOperand(2).getReg());
+
+ /* read <src> register */
+ uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips64 + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* read <rt> register */
+ uint64_t rt_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ if (!strcasecmp(op_name, "DSUBU") || !strcasecmp(op_name, "SUBU"))
+ result = src_opd_val - rt_opd_val;
+ else
+ result = src_opd_val + rt_opd_val;
+
+ context.SetImmediateSigned(result);
+ context.type = eContextImmediate;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips64 + dst, result))
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BEQ, BNE : Branch on condition
+ BEQL, BNEL : Branch likely
+*/
+bool EmulateInstructionMIPS64::Emulate_BXX_3ops(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ int64_t offset, pc, rs_val, rt_val, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rt = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ offset = insn.getOperand(2).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BEQ") || !strcasecmp(op_name, "BEQL")
+ || !strcasecmp(op_name, "BEQ64") ) {
+ if (rs_val == rt_val)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BNE") || !strcasecmp(op_name, "BNEL")
+ || !strcasecmp(op_name, "BNE64")) {
+ if (rs_val != rt_val)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+/*
+ Emulate below MIPS Non-Compact conditional branch and link instructions.
+ BLTZAL, BGEZAL :
+ BLTZALL, BGEZALL : Branch likely
+*/
+bool EmulateInstructionMIPS64::Emulate_Bcond_Link(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int64_t offset, pc, target = 0;
+ int64_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZAL") || !strcasecmp(op_name, "BLTZALL")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGEZAL") ||
+ !strcasecmp(op_name, "BGEZALL")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_BAL(llvm::MCInst &insn) {
+ bool success = false;
+ int64_t offset, pc, target;
+
+ /*
+ * BAL offset
+ * offset = sign_ext (offset << 2)
+ * RA = PC + 8
+ * PC = PC + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_BALC(llvm::MCInst &insn) {
+ bool success = false;
+ int64_t offset, pc, target;
+
+ /*
+ * BALC offset
+ * offset = sign_ext (offset << 2)
+ * RA = PC + 4
+ * PC = PC + 4 + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+/*
+ Emulate below MIPS conditional branch and link instructions.
+ BLEZALC, BGEZALC, BLTZALC, BGTZALC, BEQZALC, BNEZALC : Compact branches
+*/
+bool EmulateInstructionMIPS64::Emulate_Bcond_Link_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int64_t offset, pc, rs_val, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLEZALC")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEZALC")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTZALC")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGTZALC")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BEQZALC")) {
+ if (rs_val == 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEZALC")) {
+ if (rs_val != 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BLTZL, BGEZL, BGTZL, BLEZL : Branch likely
+ BLTZ, BGEZ, BGTZ, BLEZ : Non-compact branches
+*/
+bool EmulateInstructionMIPS64::Emulate_BXX_2ops(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int64_t offset, pc, rs_val, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZL") || !strcasecmp(op_name, "BLTZ")
+ || !strcasecmp(op_name, "BLTZ64")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGEZL") || !strcasecmp(op_name, "BGEZ")
+ || !strcasecmp(op_name, "BGEZ64")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGTZL") || !strcasecmp(op_name, "BGTZ")
+ || !strcasecmp(op_name, "BGTZ64")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BLEZL") || !strcasecmp(op_name, "BLEZ")
+ || !strcasecmp(op_name, "BLEZ64")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BC(llvm::MCInst &insn) {
+ bool success = false;
+ int64_t offset, pc, target;
+
+ /*
+ * BC offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC + 4 + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+static int IsAdd64bitOverflow(int64_t a, int64_t b) {
+ int64_t r = (uint64_t)a + (uint64_t)b;
+ return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0);
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BEQC, BNEC, BLTC, BGEC, BLTUC, BGEUC, BOVC, BNVC: Compact branch
+ instructions with no delay slot
+*/
+bool EmulateInstructionMIPS64::Emulate_BXX_3ops_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ int64_t offset, pc, rs_val, rt_val, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rt = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ offset = insn.getOperand(2).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BEQC") || !strcasecmp(op_name, "BEQC64")) {
+ if (rs_val == rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEC") || !strcasecmp(op_name, "BNEC64")) {
+ if (rs_val != rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTC") || !strcasecmp(op_name, "BLTC64")) {
+ if (rs_val < rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEC64") || !strcasecmp(op_name, "BGEC")) {
+ if (rs_val >= rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTUC") || !strcasecmp(op_name, "BLTUC64")) {
+ if (rs_val < rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEUC") || !strcasecmp(op_name, "BGEUC64")) {
+ if ((uint32_t)rs_val >= (uint32_t)rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BOVC")) {
+ if (IsAdd64bitOverflow(rs_val, rt_val))
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNVC")) {
+ if (!IsAdd64bitOverflow(rs_val, rt_val))
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BLTZC, BLEZC, BGEZC, BGTZC, BEQZC, BNEZC : Compact Branches
+*/
+bool EmulateInstructionMIPS64::Emulate_BXX_2ops_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int64_t offset, pc, target = 0;
+ int64_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZC") || !strcasecmp(op_name, "BLTZC64")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLEZC") || !strcasecmp(op_name, "BLEZC64")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEZC") || !strcasecmp(op_name, "BGEZC64")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGTZC") || !strcasecmp(op_name, "BGTZC64")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BEQZC") || !strcasecmp(op_name, "BEQZC64")) {
+ if (rs_val == 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEZC") || !strcasecmp(op_name, "BNEZC64")) {
+ if (rs_val != 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_J(llvm::MCInst &insn) {
+ bool success = false;
+ uint64_t offset, pc;
+
+ /*
+ * J offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC[63-28] | offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ /* This is a PC-region branch and not PC-relative */
+ pc = (pc & 0xFFFFFFFFF0000000ULL) | offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ pc);
+}
+
+bool EmulateInstructionMIPS64::Emulate_JAL(llvm::MCInst &insn) {
+ bool success = false;
+ uint64_t offset, target, pc;
+
+ /*
+ * JAL offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC[63-28] | offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ /* This is a PC-region branch and not PC-relative */
+ target = (pc & 0xFFFFFFFFF0000000ULL) | offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_JALR(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ uint64_t pc, rs_val;
+
+ /*
+ * JALR rt, rs
+ * GPR[rt] = PC + 8
+ * PC = GPR[rs]
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rs = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + rs, 0,
+ &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ rs_val))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_JIALC(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rt;
+ int64_t target, offset, pc, rt_val;
+
+ /*
+ * JIALC rt, offset
+ * offset = sign_ext (offset)
+ * PC = GPR[rt] + offset
+ * RA = PC + 4
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ target = rt_val + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_JIC(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rt;
+ int64_t target, offset, rt_val;
+
+ /*
+ * JIC rt, offset
+ * offset = sign_ext (offset)
+ * PC = GPR[rt] + offset
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ rt_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ target = rt_val + offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_JR(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ uint64_t rs_val;
+
+ /*
+ * JR rs
+ * PC = GPR[rs]
+ */
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+
+ rs_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + rs, 0,
+ &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ rs_val);
+}
+
+/*
+ Emulate Branch on FP True/False
+ BC1F, BC1FL : Branch on FP False (L stands for branch likely)
+ BC1T, BC1TL : Branch on FP True (L stands for branch likely)
+*/
+bool EmulateInstructionMIPS64::Emulate_FP_branch(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t cc, fcsr;
+ int64_t pc, offset, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ /*
+ * BC1F cc, offset
+ * condition <- (FPConditionCode(cc) == 0)
+ * if condition then
+ * offset = sign_ext (offset)
+ * PC = PC + offset
+ */
+ cc = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ fcsr =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_fcsr_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ /* fcsr[23], fcsr[25-31] are vaild condition bits */
+ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01);
+
+ if (!strcasecmp(op_name, "BC1F") || !strcasecmp(op_name, "BC1FL")) {
+ if ((fcsr & (1 << cc)) == 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1T") || !strcasecmp(op_name, "BC1TL")) {
+ if ((fcsr & (1 << cc)) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BC1EQZ(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t ft;
+ uint64_t ft_val;
+ int64_t target, pc, offset;
+
+ /*
+ * BC1EQZ ft, offset
+ * condition <- (FPR[ft].bit0 == 0)
+ * if condition then
+ * offset = sign_ext (offset)
+ * PC = PC + 4 + offset
+ */
+ ft = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ ft_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + ft, 0,
+ &success);
+ if (!success)
+ return false;
+
+ if ((ft_val & 1) == 0)
+ target = pc + 4 + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BC1NEZ(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t ft;
+ uint64_t ft_val;
+ int64_t target, pc, offset;
+
+ /*
+ * BC1NEZ ft, offset
+ * condition <- (FPR[ft].bit0 != 0)
+ * if condition then
+ * offset = sign_ext (offset)
+ * PC = PC + 4 + offset
+ */
+ ft = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ ft_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + ft, 0,
+ &success);
+ if (!success)
+ return false;
+
+ if ((ft_val & 1) != 0)
+ target = pc + 4 + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+/*
+ Emulate MIPS-3D Branch instructions
+ BC1ANY2F, BC1ANY2T : Branch on Any of Two Floating Point Condition Codes
+ False/True
+ BC1ANY4F, BC1ANY4T : Branch on Any of Four Floating Point Condition Codes
+ False/True
+*/
+bool EmulateInstructionMIPS64::Emulate_3D_branch(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t cc, fcsr;
+ int64_t pc, offset, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ cc = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ fcsr = (uint32_t)ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_fcsr_mips64,
+ 0, &success);
+ if (!success)
+ return false;
+
+ /* fcsr[23], fcsr[25-31] are vaild condition bits */
+ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01);
+
+ if (!strcasecmp(op_name, "BC1ANY2F")) {
+ /* if any one bit is 0 */
+ if (((fcsr >> cc) & 3) != 3)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY2T")) {
+ /* if any one bit is 1 */
+ if (((fcsr >> cc) & 3) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY4F")) {
+ /* if any one bit is 0 */
+ if (((fcsr >> cc) & 0xf) != 0xf)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY4T")) {
+ /* if any one bit is 1 */
+ if (((fcsr >> cc) & 0xf) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BNZB(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 1, true);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BNZH(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 2, true);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BNZW(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 4, true);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BNZD(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 8, true);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BZB(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 1, false);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BZH(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 2, false);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BZW(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 4, false);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BZD(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 8, false);
+}
+
+bool EmulateInstructionMIPS64::Emulate_MSA_Branch_DF(llvm::MCInst &insn,
+ int element_byte_size,
+ bool bnz) {
+ bool success = false, branch_hit = true;
+ int64_t target = 0;
+ RegisterValue reg_value;
+ const uint8_t *ptr = nullptr;
+
+ uint32_t wt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ int64_t offset = insn.getOperand(1).getImm();
+
+ int64_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ if (ReadRegister(eRegisterKindDWARF, dwarf_w0_mips64 + wt, reg_value))
+ ptr = (const uint8_t *)reg_value.GetBytes();
+ else
+ return false;
+
+ for (int i = 0; i < 16 / element_byte_size; i++) {
+ switch (element_byte_size) {
+ case 1:
+ if ((*ptr == 0 && bnz) || (*ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 2:
+ if ((*(const uint16_t *)ptr == 0 && bnz) ||
+ (*(const uint16_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 4:
+ if ((*(const uint32_t *)ptr == 0 && bnz) ||
+ (*(const uint32_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 8:
+ if ((*(const uint64_t *)ptr == 0 && bnz) ||
+ (*(const uint64_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ }
+ if (!branch_hit)
+ break;
+ ptr = ptr + element_byte_size;
+ }
+
+ if (branch_hit)
+ target = pc + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BNZV(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_V(insn, true);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BZV(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_V(insn, false);
+}
+
+bool EmulateInstructionMIPS64::Emulate_MSA_Branch_V(llvm::MCInst &insn,
+ bool bnz) {
+ bool success = false;
+ int64_t target = 0;
+ llvm::APInt wr_val = llvm::APInt::getNullValue(128);
+ llvm::APInt fail_value = llvm::APInt::getMaxValue(128);
+ llvm::APInt zero_value = llvm::APInt::getNullValue(128);
+ RegisterValue reg_value;
+
+ uint32_t wt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ int64_t offset = insn.getOperand(1).getImm();
+
+ int64_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ if (ReadRegister(eRegisterKindDWARF, dwarf_w0_mips64 + wt, reg_value))
+ wr_val = reg_value.GetAsUInt128(fail_value);
+ else
+ return false;
+
+ if ((llvm::APInt::isSameValue(zero_value, wr_val) && !bnz) ||
+ (!llvm::APInt::isSameValue(zero_value, wr_val) && bnz))
+ target = pc + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_LDST_Imm(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t base;
+ int64_t imm, address;
+ Context bad_vaddr_context;
+
+ uint32_t num_operands = insn.getNumOperands();
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+ imm = insn.getOperand(num_operands - 1).getImm();
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ /* read base register */
+ address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + base, 0,
+ &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_LDST_Reg(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t base, index;
+ int64_t address, index_address;
+ Context bad_vaddr_context;
+
+ uint32_t num_operands = insn.getNumOperands();
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+ index =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 1).getReg());
+
+ RegisterInfo reg_info_base, reg_info_index;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + index,
+ reg_info_index))
+ return false;
+
+ /* read base register */
+ address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + base, 0,
+ &success);
+ if (!success)
+ return false;
+
+ /* read index register */
+ index_address = ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + index, 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + index_address;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h
new file mode 100644
index 000000000000..953a0935bd06
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h
@@ -0,0 +1,182 @@
+//===-- EmulateInstructionMIPS64.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef EmulateInstructionMIPS64_h_
+#define EmulateInstructionMIPS64_h_
+
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Utility/Status.h"
+
+namespace llvm {
+class MCDisassembler;
+class MCSubtargetInfo;
+class MCRegisterInfo;
+class MCAsmInfo;
+class MCContext;
+class MCInstrInfo;
+class MCInst;
+} // namespace llvm
+
+class EmulateInstructionMIPS64 : public lldb_private::EmulateInstruction {
+public:
+ EmulateInstructionMIPS64(const lldb_private::ArchSpec &arch);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::EmulateInstruction *
+ CreateInstance(const lldb_private::ArchSpec &arch,
+ lldb_private::InstructionType inst_type);
+
+ static bool SupportsEmulatingInstructionsOfTypeStatic(
+ lldb_private::InstructionType inst_type) {
+ switch (inst_type) {
+ case lldb_private::eInstructionTypeAny:
+ case lldb_private::eInstructionTypePrologueEpilogue:
+ case lldb_private::eInstructionTypePCModifying:
+ return true;
+
+ case lldb_private::eInstructionTypeAll:
+ return false;
+ }
+ return false;
+ }
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ bool SetTargetTriple(const lldb_private::ArchSpec &arch) override;
+
+ bool SupportsEmulatingInstructionsOfType(
+ lldb_private::InstructionType inst_type) override {
+ return SupportsEmulatingInstructionsOfTypeStatic(inst_type);
+ }
+
+ bool ReadInstruction() override;
+
+ bool EvaluateInstruction(uint32_t evaluate_options) override;
+
+ bool TestEmulation(lldb_private::Stream *out_stream,
+ lldb_private::ArchSpec &arch,
+ lldb_private::OptionValueDictionary *test_data) override {
+ return false;
+ }
+
+ bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
+ lldb_private::RegisterInfo &reg_info) override;
+
+ bool
+ CreateFunctionEntryUnwind(lldb_private::UnwindPlan &unwind_plan) override;
+
+protected:
+ typedef struct {
+ const char *op_name;
+ bool (EmulateInstructionMIPS64::*callback)(llvm::MCInst &insn);
+ const char *insn_name;
+ } MipsOpcode;
+
+ static MipsOpcode *GetOpcodeForInstruction(const char *op_name);
+
+ bool Emulate_DADDiu(llvm::MCInst &insn);
+
+ bool Emulate_DSUBU_DADDU(llvm::MCInst &insn);
+
+ bool Emulate_LUI(llvm::MCInst &insn);
+
+ bool Emulate_SD(llvm::MCInst &insn);
+
+ bool Emulate_LD(llvm::MCInst &insn);
+
+ bool Emulate_LDST_Imm(llvm::MCInst &insn);
+
+ bool Emulate_LDST_Reg(llvm::MCInst &insn);
+
+ bool Emulate_BXX_3ops(llvm::MCInst &insn);
+
+ bool Emulate_BXX_3ops_C(llvm::MCInst &insn);
+
+ bool Emulate_BXX_2ops(llvm::MCInst &insn);
+
+ bool Emulate_BXX_2ops_C(llvm::MCInst &insn);
+
+ bool Emulate_Bcond_Link_C(llvm::MCInst &insn);
+
+ bool Emulate_Bcond_Link(llvm::MCInst &insn);
+
+ bool Emulate_FP_branch(llvm::MCInst &insn);
+
+ bool Emulate_3D_branch(llvm::MCInst &insn);
+
+ bool Emulate_BAL(llvm::MCInst &insn);
+
+ bool Emulate_BALC(llvm::MCInst &insn);
+
+ bool Emulate_BC(llvm::MCInst &insn);
+
+ bool Emulate_J(llvm::MCInst &insn);
+
+ bool Emulate_JAL(llvm::MCInst &insn);
+
+ bool Emulate_JALR(llvm::MCInst &insn);
+
+ bool Emulate_JIALC(llvm::MCInst &insn);
+
+ bool Emulate_JIC(llvm::MCInst &insn);
+
+ bool Emulate_JR(llvm::MCInst &insn);
+
+ bool Emulate_BC1EQZ(llvm::MCInst &insn);
+
+ bool Emulate_BC1NEZ(llvm::MCInst &insn);
+
+ bool Emulate_BNZB(llvm::MCInst &insn);
+
+ bool Emulate_BNZH(llvm::MCInst &insn);
+
+ bool Emulate_BNZW(llvm::MCInst &insn);
+
+ bool Emulate_BNZD(llvm::MCInst &insn);
+
+ bool Emulate_BZB(llvm::MCInst &insn);
+
+ bool Emulate_BZH(llvm::MCInst &insn);
+
+ bool Emulate_BZW(llvm::MCInst &insn);
+
+ bool Emulate_BZD(llvm::MCInst &insn);
+
+ bool Emulate_MSA_Branch_DF(llvm::MCInst &insn, int element_byte_size,
+ bool bnz);
+
+ bool Emulate_BNZV(llvm::MCInst &insn);
+
+ bool Emulate_BZV(llvm::MCInst &insn);
+
+ bool Emulate_MSA_Branch_V(llvm::MCInst &insn, bool bnz);
+
+ bool nonvolatile_reg_p(uint64_t regnum);
+
+ const char *GetRegisterName(unsigned reg_num, bool altnernate_name);
+
+private:
+ std::unique_ptr<llvm::MCDisassembler> m_disasm;
+ std::unique_ptr<llvm::MCSubtargetInfo> m_subtype_info;
+ std::unique_ptr<llvm::MCRegisterInfo> m_reg_info;
+ std::unique_ptr<llvm::MCAsmInfo> m_asm_info;
+ std::unique_ptr<llvm::MCContext> m_context;
+ std::unique_ptr<llvm::MCInstrInfo> m_insn_info;
+};
+
+#endif // EmulateInstructionMIPS64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp
new file mode 100644
index 000000000000..c77fa04fc7d7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp
@@ -0,0 +1,397 @@
+//===-- EmulateInstructionPPC64.cpp ------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "EmulateInstructionPPC64.h"
+
+#include <stdlib.h>
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+
+#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h"
+
+#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+EmulateInstructionPPC64::EmulateInstructionPPC64(const ArchSpec &arch)
+ : EmulateInstruction(arch) {}
+
+void EmulateInstructionPPC64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void EmulateInstructionPPC64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString EmulateInstructionPPC64::GetPluginNameStatic() {
+ ConstString g_plugin_name("lldb.emulate-instruction.ppc64");
+ return g_plugin_name;
+}
+
+ConstString EmulateInstructionPPC64::GetPluginName() {
+ static ConstString g_plugin_name("EmulateInstructionPPC64");
+ return g_plugin_name;
+}
+
+const char *EmulateInstructionPPC64::GetPluginDescriptionStatic() {
+ return "Emulate instructions for the PPC64 architecture.";
+}
+
+EmulateInstruction *
+EmulateInstructionPPC64::CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type) {
+ if (EmulateInstructionPPC64::SupportsEmulatingInstructionsOfTypeStatic(
+ inst_type))
+ if (arch.GetTriple().isPPC64())
+ return new EmulateInstructionPPC64(arch);
+
+ return nullptr;
+}
+
+bool EmulateInstructionPPC64::SetTargetTriple(const ArchSpec &arch) {
+ return arch.GetTriple().isPPC64();
+}
+
+static bool LLDBTableGetRegisterInfo(uint32_t reg_num, RegisterInfo &reg_info) {
+ if (reg_num >= llvm::array_lengthof(g_register_infos_ppc64le))
+ return false;
+ reg_info = g_register_infos_ppc64le[reg_num];
+ return true;
+}
+
+bool EmulateInstructionPPC64::GetRegisterInfo(RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterInfo &reg_info) {
+ if (reg_kind == eRegisterKindGeneric) {
+ switch (reg_num) {
+ case LLDB_REGNUM_GENERIC_PC:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_pc_ppc64le;
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_r1_ppc64le;
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_lr_ppc64le;
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_cr_ppc64le;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (reg_kind == eRegisterKindLLDB)
+ return LLDBTableGetRegisterInfo(reg_num, reg_info);
+ return false;
+}
+
+bool EmulateInstructionPPC64::ReadInstruction() {
+ bool success = false;
+ m_addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_ADDRESS, &success);
+ if (success) {
+ Context ctx;
+ ctx.type = eContextReadOpcode;
+ ctx.SetNoArgs();
+ m_opcode.SetOpcode32(ReadMemoryUnsigned(ctx, m_addr, 4, 0, &success),
+ GetByteOrder());
+ }
+ if (!success)
+ m_addr = LLDB_INVALID_ADDRESS;
+ return success;
+}
+
+bool EmulateInstructionPPC64::CreateFunctionEntryUnwind(
+ UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindLLDB);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(gpr_r1_ppc64le, 0);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("EmulateInstructionPPC64");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ unwind_plan.SetReturnAddressRegister(gpr_lr_ppc64le);
+ return true;
+}
+
+EmulateInstructionPPC64::Opcode *
+EmulateInstructionPPC64::GetOpcodeForInstruction(uint32_t opcode) {
+ static EmulateInstructionPPC64::Opcode g_opcodes[] = {
+ {0xfc0007ff, 0x7c0002a6, &EmulateInstructionPPC64::EmulateMFSPR,
+ "mfspr RT, SPR"},
+ {0xfc000003, 0xf8000000, &EmulateInstructionPPC64::EmulateSTD,
+ "std RS, DS(RA)"},
+ {0xfc000003, 0xf8000001, &EmulateInstructionPPC64::EmulateSTD,
+ "stdu RS, DS(RA)"},
+ {0xfc0007fe, 0x7c000378, &EmulateInstructionPPC64::EmulateOR,
+ "or RA, RS, RB"},
+ {0xfc000000, 0x38000000, &EmulateInstructionPPC64::EmulateADDI,
+ "addi RT, RA, SI"},
+ {0xfc000003, 0xe8000000, &EmulateInstructionPPC64::EmulateLD,
+ "ld RT, DS(RA)"}};
+ static const size_t k_num_ppc_opcodes = llvm::array_lengthof(g_opcodes);
+
+ for (size_t i = 0; i < k_num_ppc_opcodes; ++i) {
+ if ((g_opcodes[i].mask & opcode) == g_opcodes[i].value)
+ return &g_opcodes[i];
+ }
+ return nullptr;
+}
+
+bool EmulateInstructionPPC64::EvaluateInstruction(uint32_t evaluate_options) {
+ const uint32_t opcode = m_opcode.GetOpcode32();
+ // LLDB_LOG(log, "PPC64::EvaluateInstruction: opcode={0:X+8}", opcode);
+ Opcode *opcode_data = GetOpcodeForInstruction(opcode);
+ if (!opcode_data)
+ return false;
+
+ // LLDB_LOG(log, "PPC64::EvaluateInstruction: {0}", opcode_data->name);
+ const bool auto_advance_pc =
+ evaluate_options & eEmulateInstructionOptionAutoAdvancePC;
+
+ bool success = false;
+
+ uint32_t orig_pc_value = 0;
+ if (auto_advance_pc) {
+ orig_pc_value =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success);
+ if (!success)
+ return false;
+ }
+
+ // Call the Emulate... function.
+ success = (this->*opcode_data->callback)(opcode);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc) {
+ uint32_t new_pc_value =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc && (new_pc_value == orig_pc_value)) {
+ EmulateInstruction::Context context;
+ context.type = eContextAdvancePC;
+ context.SetNoArgs();
+ if (!WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_pc_ppc64le,
+ orig_pc_value + 4))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool EmulateInstructionPPC64::EmulateMFSPR(uint32_t opcode) {
+ uint32_t rt = Bits32(opcode, 25, 21);
+ uint32_t spr = Bits32(opcode, 20, 11);
+
+ enum { SPR_LR = 0x100 };
+
+ // For now, we're only insterested in 'mfspr r0, lr'
+ if (rt != gpr_r0_ppc64le || spr != SPR_LR)
+ return false;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ LLDB_LOG(log, "EmulateMFSPR: {0:X+8}: mfspr r0, lr", m_addr);
+
+ bool success;
+ uint64_t lr =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success);
+ if (!success)
+ return false;
+ Context context;
+ context.type = eContextWriteRegisterRandomBits;
+ WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_r0_ppc64le, lr);
+ LLDB_LOG(log, "EmulateMFSPR: success!");
+ return true;
+}
+
+bool EmulateInstructionPPC64::EmulateLD(uint32_t opcode) {
+ uint32_t rt = Bits32(opcode, 25, 21);
+ uint32_t ra = Bits32(opcode, 20, 16);
+ uint32_t ds = Bits32(opcode, 15, 2);
+
+ int32_t ids = llvm::SignExtend32<16>(ds << 2);
+
+ // For now, tracking only loads from 0(r1) to r1 (0(r1) is the ABI defined
+ // location to save previous SP)
+ if (ra != gpr_r1_ppc64le || rt != gpr_r1_ppc64le || ids != 0)
+ return false;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ LLDB_LOG(log, "EmulateLD: {0:X+8}: ld r{1}, {2}(r{3})", m_addr, rt, ids, ra);
+
+ RegisterInfo r1_info;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_r1_ppc64le, r1_info))
+ return false;
+
+ // restore SP
+ Context ctx;
+ ctx.type = eContextRestoreStackPointer;
+ ctx.SetRegisterToRegisterPlusOffset(r1_info, r1_info, 0);
+
+ WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_ppc64le, 0);
+ LLDB_LOG(log, "EmulateLD: success!");
+ return true;
+}
+
+bool EmulateInstructionPPC64::EmulateSTD(uint32_t opcode) {
+ uint32_t rs = Bits32(opcode, 25, 21);
+ uint32_t ra = Bits32(opcode, 20, 16);
+ uint32_t ds = Bits32(opcode, 15, 2);
+ uint32_t u = Bits32(opcode, 1, 0);
+
+ // For now, tracking only stores to r1
+ if (ra != gpr_r1_ppc64le)
+ return false;
+ // ... and only stores of SP, FP and LR (moved into r0 by a previous mfspr)
+ if (rs != gpr_r1_ppc64le && rs != gpr_r31_ppc64le && rs != gpr_r30_ppc64le &&
+ rs != gpr_r0_ppc64le)
+ return false;
+
+ bool success;
+ uint64_t rs_val = ReadRegisterUnsigned(eRegisterKindLLDB, rs, 0, &success);
+ if (!success)
+ return false;
+
+ int32_t ids = llvm::SignExtend32<16>(ds << 2);
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ LLDB_LOG(log, "EmulateSTD: {0:X+8}: std{1} r{2}, {3}(r{4})", m_addr,
+ u ? "u" : "", rs, ids, ra);
+
+ // Make sure that r0 is really holding LR value (this won't catch unlikely
+ // cases, such as r0 being overwritten after mfspr)
+ uint32_t rs_num = rs;
+ if (rs == gpr_r0_ppc64le) {
+ uint64_t lr =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success);
+ if (!success || lr != rs_val)
+ return false;
+ rs_num = gpr_lr_ppc64le;
+ }
+
+ // set context
+ RegisterInfo rs_info;
+ if (!GetRegisterInfo(eRegisterKindLLDB, rs_num, rs_info))
+ return false;
+ RegisterInfo ra_info;
+ if (!GetRegisterInfo(eRegisterKindLLDB, ra, ra_info))
+ return false;
+
+ Context ctx;
+ ctx.type = eContextPushRegisterOnStack;
+ ctx.SetRegisterToRegisterPlusOffset(rs_info, ra_info, ids);
+
+ // store
+ uint64_t ra_val = ReadRegisterUnsigned(eRegisterKindLLDB, ra, 0, &success);
+ if (!success)
+ return false;
+
+ lldb::addr_t addr = ra_val + ids;
+ WriteMemory(ctx, addr, &rs_val, sizeof(rs_val));
+
+ // update RA?
+ if (u) {
+ Context ctx;
+ // NOTE Currently, RA will always be equal to SP(r1)
+ ctx.type = eContextAdjustStackPointer;
+ WriteRegisterUnsigned(ctx, eRegisterKindLLDB, ra, addr);
+ }
+
+ LLDB_LOG(log, "EmulateSTD: success!");
+ return true;
+}
+
+bool EmulateInstructionPPC64::EmulateOR(uint32_t opcode) {
+ uint32_t rs = Bits32(opcode, 25, 21);
+ uint32_t ra = Bits32(opcode, 20, 16);
+ uint32_t rb = Bits32(opcode, 15, 11);
+
+ // to be safe, process only the known 'mr r31/r30, r1' prologue instructions
+ if (m_fp != LLDB_INVALID_REGNUM || rs != rb ||
+ (ra != gpr_r30_ppc64le && ra != gpr_r31_ppc64le) || rb != gpr_r1_ppc64le)
+ return false;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ LLDB_LOG(log, "EmulateOR: {0:X+8}: mr r{1}, r{2}", m_addr, ra, rb);
+
+ // set context
+ RegisterInfo ra_info;
+ if (!GetRegisterInfo(eRegisterKindLLDB, ra, ra_info))
+ return false;
+
+ Context ctx;
+ ctx.type = eContextSetFramePointer;
+ ctx.SetRegister(ra_info);
+
+ // move
+ bool success;
+ uint64_t rb_val = ReadRegisterUnsigned(eRegisterKindLLDB, rb, 0, &success);
+ if (!success)
+ return false;
+ WriteRegisterUnsigned(ctx, eRegisterKindLLDB, ra, rb_val);
+ m_fp = ra;
+ LLDB_LOG(log, "EmulateOR: success!");
+ return true;
+}
+
+bool EmulateInstructionPPC64::EmulateADDI(uint32_t opcode) {
+ uint32_t rt = Bits32(opcode, 25, 21);
+ uint32_t ra = Bits32(opcode, 20, 16);
+ uint32_t si = Bits32(opcode, 15, 0);
+
+ // handle stack adjustments only
+ // (this is a typical epilogue operation, with ra == r1. If it's
+ // something else, then we won't know the correct value of ra)
+ if (rt != gpr_r1_ppc64le || ra != gpr_r1_ppc64le)
+ return false;
+
+ int32_t si_val = llvm::SignExtend32<16>(si);
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ LLDB_LOG(log, "EmulateADDI: {0:X+8}: addi r1, r1, {1}", m_addr, si_val);
+
+ // set context
+ RegisterInfo r1_info;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_r1_ppc64le, r1_info))
+ return false;
+
+ Context ctx;
+ ctx.type = eContextRestoreStackPointer;
+ ctx.SetRegisterToRegisterPlusOffset(r1_info, r1_info, 0);
+
+ // adjust SP
+ bool success;
+ uint64_t r1 =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_r1_ppc64le, 0, &success);
+ if (!success)
+ return false;
+ WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_ppc64le, r1 + si_val);
+ LLDB_LOG(log, "EmulateADDI: success!");
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h
new file mode 100644
index 000000000000..bf239770b933
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h
@@ -0,0 +1,92 @@
+//===-- EmulateInstructionPPC64.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef EmulateInstructionPPC64_h_
+#define EmulateInstructionPPC64_h_
+
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Utility/Log.h"
+
+namespace lldb_private {
+
+class EmulateInstructionPPC64 : public EmulateInstruction {
+public:
+ EmulateInstructionPPC64(const ArchSpec &arch);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static EmulateInstruction *CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type);
+
+ static bool
+ SupportsEmulatingInstructionsOfTypeStatic(InstructionType inst_type) {
+ switch (inst_type) {
+ case eInstructionTypeAny:
+ case eInstructionTypePrologueEpilogue:
+ return true;
+
+ case eInstructionTypePCModifying:
+ case eInstructionTypeAll:
+ return false;
+ }
+ return false;
+ }
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ bool SetTargetTriple(const ArchSpec &arch) override;
+
+ bool SupportsEmulatingInstructionsOfType(InstructionType inst_type) override {
+ return SupportsEmulatingInstructionsOfTypeStatic(inst_type);
+ }
+
+ bool ReadInstruction() override;
+
+ bool EvaluateInstruction(uint32_t evaluate_options) override;
+
+ bool TestEmulation(Stream *out_stream, ArchSpec &arch,
+ OptionValueDictionary *test_data) override {
+ return false;
+ }
+
+ bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
+ RegisterInfo &reg_info) override;
+
+ bool CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) override;
+
+private:
+ struct Opcode {
+ uint32_t mask;
+ uint32_t value;
+ bool (EmulateInstructionPPC64::*callback)(uint32_t opcode);
+ const char *name;
+ };
+
+ uint32_t m_fp = LLDB_INVALID_REGNUM;
+
+ Opcode *GetOpcodeForInstruction(uint32_t opcode);
+
+ bool EmulateMFSPR(uint32_t opcode);
+ bool EmulateLD(uint32_t opcode);
+ bool EmulateSTD(uint32_t opcode);
+ bool EmulateOR(uint32_t opcode);
+ bool EmulateADDI(uint32_t opcode);
+};
+
+} // namespace lldb_private
+
+#endif // EmulateInstructionPPC64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.cpp
new file mode 100644
index 000000000000..c8ac04641e68
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.cpp
@@ -0,0 +1,325 @@
+//===-- ASanRuntime.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASanRuntime.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/StringSwitch.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+lldb::InstrumentationRuntimeSP
+AddressSanitizerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) {
+ return InstrumentationRuntimeSP(new AddressSanitizerRuntime(process_sp));
+}
+
+void AddressSanitizerRuntime::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "AddressSanitizer instrumentation runtime plugin.",
+ CreateInstance, GetTypeStatic);
+}
+
+void AddressSanitizerRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString AddressSanitizerRuntime::GetPluginNameStatic() {
+ return ConstString("AddressSanitizer");
+}
+
+lldb::InstrumentationRuntimeType AddressSanitizerRuntime::GetTypeStatic() {
+ return eInstrumentationRuntimeTypeAddressSanitizer;
+}
+
+AddressSanitizerRuntime::~AddressSanitizerRuntime() { Deactivate(); }
+
+const RegularExpression &
+AddressSanitizerRuntime::GetPatternForRuntimeLibrary() {
+ // FIXME: This shouldn't include the "dylib" suffix.
+ static RegularExpression regex(
+ llvm::StringRef("libclang_rt.asan_(.*)_dynamic\\.dylib"));
+ return regex;
+}
+
+bool AddressSanitizerRuntime::CheckIfRuntimeIsValid(
+ const lldb::ModuleSP module_sp) {
+ const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
+ ConstString("__asan_get_alloc_stack"), lldb::eSymbolTypeAny);
+
+ return symbol != nullptr;
+}
+
+const char *address_sanitizer_retrieve_report_data_prefix = R"(
+extern "C"
+{
+int __asan_report_present();
+void *__asan_get_report_pc();
+void *__asan_get_report_bp();
+void *__asan_get_report_sp();
+void *__asan_get_report_address();
+const char *__asan_get_report_description();
+int __asan_get_report_access_type();
+size_t __asan_get_report_access_size();
+}
+)";
+
+const char *address_sanitizer_retrieve_report_data_command = R"(
+struct {
+ int present;
+ int access_type;
+ void *pc;
+ void *bp;
+ void *sp;
+ void *address;
+ size_t access_size;
+ const char *description;
+} t;
+
+t.present = __asan_report_present();
+t.access_type = __asan_get_report_access_type();
+t.pc = __asan_get_report_pc();
+t.bp = __asan_get_report_bp();
+t.sp = __asan_get_report_sp();
+t.address = __asan_get_report_address();
+t.access_size = __asan_get_report_access_size();
+t.description = __asan_get_report_description();
+t
+)";
+
+StructuredData::ObjectSP AddressSanitizerRuntime::RetrieveReportData() {
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return StructuredData::ObjectSP();
+
+ ThreadSP thread_sp =
+ process_sp->GetThreadList().GetExpressionExecutionThread();
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+
+ if (!frame_sp)
+ return StructuredData::ObjectSP();
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(true);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
+ options.SetPrefix(address_sanitizer_retrieve_report_data_prefix);
+ options.SetAutoApplyFixIts(false);
+ options.SetLanguage(eLanguageTypeObjC_plus_plus);
+
+ ValueObjectSP return_value_sp;
+ ExecutionContext exe_ctx;
+ Status eval_error;
+ frame_sp->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = UserExpression::Evaluate(
+ exe_ctx, options, address_sanitizer_retrieve_report_data_command, "",
+ return_value_sp, eval_error);
+ if (result != eExpressionCompleted) {
+ process_sp->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf(
+ "Warning: Cannot evaluate AddressSanitizer expression:\n%s\n",
+ eval_error.AsCString());
+ return StructuredData::ObjectSP();
+ }
+
+ int present = return_value_sp->GetValueForExpressionPath(".present")
+ ->GetValueAsUnsigned(0);
+ if (present != 1)
+ return StructuredData::ObjectSP();
+
+ addr_t pc =
+ return_value_sp->GetValueForExpressionPath(".pc")->GetValueAsUnsigned(0);
+ /* commented out because rdar://problem/18533301
+ addr_t bp =
+ return_value_sp->GetValueForExpressionPath(".bp")->GetValueAsUnsigned(0);
+ addr_t sp =
+ return_value_sp->GetValueForExpressionPath(".sp")->GetValueAsUnsigned(0);
+ */
+ addr_t address = return_value_sp->GetValueForExpressionPath(".address")
+ ->GetValueAsUnsigned(0);
+ addr_t access_type =
+ return_value_sp->GetValueForExpressionPath(".access_type")
+ ->GetValueAsUnsigned(0);
+ addr_t access_size =
+ return_value_sp->GetValueForExpressionPath(".access_size")
+ ->GetValueAsUnsigned(0);
+ addr_t description_ptr =
+ return_value_sp->GetValueForExpressionPath(".description")
+ ->GetValueAsUnsigned(0);
+ std::string description;
+ Status error;
+ process_sp->ReadCStringFromMemory(description_ptr, description, error);
+
+ StructuredData::Dictionary *dict = new StructuredData::Dictionary();
+ dict->AddStringItem("instrumentation_class", "AddressSanitizer");
+ dict->AddStringItem("stop_type", "fatal_error");
+ dict->AddIntegerItem("pc", pc);
+ /* commented out because rdar://problem/18533301
+ dict->AddIntegerItem("bp", bp);
+ dict->AddIntegerItem("sp", sp);
+ */
+ dict->AddIntegerItem("address", address);
+ dict->AddIntegerItem("access_type", access_type);
+ dict->AddIntegerItem("access_size", access_size);
+ dict->AddStringItem("description", description);
+
+ return StructuredData::ObjectSP(dict);
+}
+
+std::string
+AddressSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) {
+ std::string description = report->GetAsDictionary()
+ ->GetValueForKey("description")
+ ->GetAsString()
+ ->GetValue();
+ return llvm::StringSwitch<std::string>(description)
+ .Case("heap-use-after-free", "Use of deallocated memory")
+ .Case("heap-buffer-overflow", "Heap buffer overflow")
+ .Case("stack-buffer-underflow", "Stack buffer underflow")
+ .Case("initialization-order-fiasco", "Initialization order problem")
+ .Case("stack-buffer-overflow", "Stack buffer overflow")
+ .Case("stack-use-after-return", "Use of stack memory after return")
+ .Case("use-after-poison", "Use of poisoned memory")
+ .Case("container-overflow", "Container overflow")
+ .Case("stack-use-after-scope", "Use of out-of-scope stack memory")
+ .Case("global-buffer-overflow", "Global buffer overflow")
+ .Case("unknown-crash", "Invalid memory access")
+ .Case("stack-overflow", "Stack space exhausted")
+ .Case("null-deref", "Dereference of null pointer")
+ .Case("wild-jump", "Jump to non-executable address")
+ .Case("wild-addr-write", "Write through wild pointer")
+ .Case("wild-addr-read", "Read from wild pointer")
+ .Case("wild-addr", "Access through wild pointer")
+ .Case("signal", "Deadly signal")
+ .Case("double-free", "Deallocation of freed memory")
+ .Case("new-delete-type-mismatch",
+ "Deallocation size different from allocation size")
+ .Case("bad-free", "Deallocation of non-allocated memory")
+ .Case("alloc-dealloc-mismatch",
+ "Mismatch between allocation and deallocation APIs")
+ .Case("bad-malloc_usable_size", "Invalid argument to malloc_usable_size")
+ .Case("bad-__sanitizer_get_allocated_size",
+ "Invalid argument to __sanitizer_get_allocated_size")
+ .Case("param-overlap",
+ "Call to function disallowing overlapping memory ranges")
+ .Case("negative-size-param", "Negative size used when accessing memory")
+ .Case("bad-__sanitizer_annotate_contiguous_container",
+ "Invalid argument to __sanitizer_annotate_contiguous_container")
+ .Case("odr-violation", "Symbol defined in multiple translation units")
+ .Case(
+ "invalid-pointer-pair",
+ "Comparison or arithmetic on pointers from different memory regions")
+ // for unknown report codes just show the code
+ .Default("AddressSanitizer detected: " + description);
+}
+
+bool AddressSanitizerRuntime::NotifyBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false;
+
+ AddressSanitizerRuntime *const instance =
+ static_cast<AddressSanitizerRuntime *>(baton);
+
+ ProcessSP process_sp = instance->GetProcessSP();
+
+ if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
+ return false;
+
+ StructuredData::ObjectSP report = instance->RetrieveReportData();
+ std::string description;
+ if (report) {
+ description = instance->FormatDescription(report);
+ }
+ // Make sure this is the right process
+ if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) {
+ ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
+ if (thread_sp)
+ thread_sp->SetStopInfo(InstrumentationRuntimeStopInfo::
+ CreateStopReasonWithInstrumentationData(
+ *thread_sp, description, report));
+
+ StreamFileSP stream_sp(
+ process_sp->GetTarget().GetDebugger().GetOutputFile());
+ if (stream_sp) {
+ stream_sp->Printf("AddressSanitizer report breakpoint hit. Use 'thread "
+ "info -s' to get extended information about the "
+ "report.\n");
+ }
+ return true; // Return true to stop the target
+ } else
+ return false; // Let target run
+}
+
+void AddressSanitizerRuntime::Activate() {
+ if (IsActive())
+ return;
+
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return;
+
+ ConstString symbol_name("__asan::AsanDie()");
+ const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType(
+ symbol_name, eSymbolTypeCode);
+
+ if (symbol == nullptr)
+ return;
+
+ if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
+ return;
+
+ Target &target = process_sp->GetTarget();
+ addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
+
+ if (symbol_address == LLDB_INVALID_ADDRESS)
+ return;
+
+ bool internal = true;
+ bool hardware = false;
+ Breakpoint *breakpoint =
+ process_sp->GetTarget()
+ .CreateBreakpoint(symbol_address, internal, hardware)
+ .get();
+ breakpoint->SetCallback(AddressSanitizerRuntime::NotifyBreakpointHit, this,
+ true);
+ breakpoint->SetBreakpointKind("address-sanitizer-report");
+ SetBreakpointID(breakpoint->GetID());
+
+ SetActive(true);
+}
+
+void AddressSanitizerRuntime::Deactivate() {
+ if (GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID());
+ SetBreakpointID(LLDB_INVALID_BREAK_ID);
+ }
+ }
+ SetActive(false);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.h b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.h
new file mode 100644
index 000000000000..0771e624ff6b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.h
@@ -0,0 +1,66 @@
+//===-- ASanRuntime.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_AddressSanitizerRuntime_h_
+#define liblldb_AddressSanitizerRuntime_h_
+
+#include "lldb/Target/InstrumentationRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class AddressSanitizerRuntime : public lldb_private::InstrumentationRuntime {
+public:
+ ~AddressSanitizerRuntime() override;
+
+ static lldb::InstrumentationRuntimeSP
+ CreateInstance(const lldb::ProcessSP &process_sp);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static lldb::InstrumentationRuntimeType GetTypeStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+private:
+ AddressSanitizerRuntime(const lldb::ProcessSP &process_sp)
+ : lldb_private::InstrumentationRuntime(process_sp) {}
+
+ const RegularExpression &GetPatternForRuntimeLibrary() override;
+
+ bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override;
+
+ void Activate() override;
+
+ void Deactivate();
+
+ static bool NotifyBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ StructuredData::ObjectSP RetrieveReportData();
+
+ std::string FormatDescription(StructuredData::ObjectSP report);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AddressSanitizerRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.cpp
new file mode 100644
index 000000000000..dfe61316b042
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.cpp
@@ -0,0 +1,274 @@
+//===-- MainThreadCheckerRuntime.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MainThreadCheckerRuntime.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "Plugins/Process/Utility/HistoryThread.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+MainThreadCheckerRuntime::~MainThreadCheckerRuntime() {
+ Deactivate();
+}
+
+lldb::InstrumentationRuntimeSP
+MainThreadCheckerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) {
+ return InstrumentationRuntimeSP(new MainThreadCheckerRuntime(process_sp));
+}
+
+void MainThreadCheckerRuntime::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "MainThreadChecker instrumentation runtime plugin.",
+ CreateInstance, GetTypeStatic);
+}
+
+void MainThreadCheckerRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString MainThreadCheckerRuntime::GetPluginNameStatic() {
+ return ConstString("MainThreadChecker");
+}
+
+lldb::InstrumentationRuntimeType MainThreadCheckerRuntime::GetTypeStatic() {
+ return eInstrumentationRuntimeTypeMainThreadChecker;
+}
+
+const RegularExpression &
+MainThreadCheckerRuntime::GetPatternForRuntimeLibrary() {
+ static RegularExpression regex(llvm::StringRef("libMainThreadChecker.dylib"));
+ return regex;
+}
+
+bool MainThreadCheckerRuntime::CheckIfRuntimeIsValid(
+ const lldb::ModuleSP module_sp) {
+ static ConstString test_sym("__main_thread_checker_on_report");
+ const Symbol *symbol =
+ module_sp->FindFirstSymbolWithNameAndType(test_sym, lldb::eSymbolTypeAny);
+ return symbol != nullptr;
+}
+
+StructuredData::ObjectSP
+MainThreadCheckerRuntime::RetrieveReportData(ExecutionContextRef exe_ctx_ref) {
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return StructuredData::ObjectSP();
+
+ ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+ ModuleSP runtime_module_sp = GetRuntimeModuleSP();
+ Target &target = process_sp->GetTarget();
+
+ if (!frame_sp)
+ return StructuredData::ObjectSP();
+
+ RegisterContextSP regctx_sp = frame_sp->GetRegisterContext();
+ if (!regctx_sp)
+ return StructuredData::ObjectSP();
+
+ const RegisterInfo *reginfo = regctx_sp->GetRegisterInfoByName("arg1");
+ if (!reginfo)
+ return StructuredData::ObjectSP();
+
+ uint64_t apiname_ptr = regctx_sp->ReadRegisterAsUnsigned(reginfo, 0);
+ if (!apiname_ptr)
+ return StructuredData::ObjectSP();
+
+ std::string apiName = "";
+ Status read_error;
+ target.ReadCStringFromMemory(apiname_ptr, apiName, read_error);
+ if (read_error.Fail())
+ return StructuredData::ObjectSP();
+
+ std::string className = "";
+ std::string selector = "";
+ if (apiName.substr(0, 2) == "-[") {
+ size_t spacePos = apiName.find(" ");
+ if (spacePos != std::string::npos) {
+ className = apiName.substr(2, spacePos - 2);
+ selector = apiName.substr(spacePos + 1, apiName.length() - spacePos - 2);
+ }
+ }
+
+ // Gather the PCs of the user frames in the backtrace.
+ StructuredData::Array *trace = new StructuredData::Array();
+ auto trace_sp = StructuredData::ObjectSP(trace);
+ StackFrameSP responsible_frame;
+ for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) {
+ StackFrameSP frame = thread_sp->GetStackFrameAtIndex(I);
+ Address addr = frame->GetFrameCodeAddress();
+ if (addr.GetModule() == runtime_module_sp) // Skip PCs from the runtime.
+ continue;
+
+ // The first non-runtime frame is responsible for the bug.
+ if (!responsible_frame)
+ responsible_frame = frame;
+
+ // First frame in stacktrace should point to a real PC, not return address.
+ if (I != 0 && trace->GetSize() == 0) {
+ addr.Slide(-1);
+ }
+
+ lldb::addr_t PC = addr.GetLoadAddress(&target);
+ trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC)));
+ }
+
+ auto *d = new StructuredData::Dictionary();
+ auto dict_sp = StructuredData::ObjectSP(d);
+ d->AddStringItem("instrumentation_class", "MainThreadChecker");
+ d->AddStringItem("api_name", apiName);
+ d->AddStringItem("class_name", className);
+ d->AddStringItem("selector", selector);
+ d->AddStringItem("description",
+ apiName + " must be used from main thread only");
+ d->AddIntegerItem("tid", thread_sp->GetIndexID());
+ d->AddItem("trace", trace_sp);
+ return dict_sp;
+}
+
+bool MainThreadCheckerRuntime::NotifyBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false; //< false => resume execution.
+
+ MainThreadCheckerRuntime *const instance =
+ static_cast<MainThreadCheckerRuntime *>(baton);
+
+ ProcessSP process_sp = instance->GetProcessSP();
+ ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
+ if (!process_sp || !thread_sp ||
+ process_sp != context->exe_ctx_ref.GetProcessSP())
+ return false;
+
+ if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
+ return false;
+
+ StructuredData::ObjectSP report =
+ instance->RetrieveReportData(context->exe_ctx_ref);
+
+ if (report) {
+ std::string description = report->GetAsDictionary()
+ ->GetValueForKey("description")
+ ->GetAsString()
+ ->GetValue();
+ thread_sp->SetStopInfo(
+ InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
+ *thread_sp, description, report));
+ return true;
+ }
+
+ return false;
+}
+
+void MainThreadCheckerRuntime::Activate() {
+ if (IsActive())
+ return;
+
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return;
+
+ ModuleSP runtime_module_sp = GetRuntimeModuleSP();
+
+ ConstString symbol_name("__main_thread_checker_on_report");
+ const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType(
+ symbol_name, eSymbolTypeCode);
+
+ if (symbol == nullptr)
+ return;
+
+ if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
+ return;
+
+ Target &target = process_sp->GetTarget();
+ addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
+
+ if (symbol_address == LLDB_INVALID_ADDRESS)
+ return;
+
+ Breakpoint *breakpoint =
+ process_sp->GetTarget()
+ .CreateBreakpoint(symbol_address, /*internal=*/true,
+ /*hardware=*/false)
+ .get();
+ breakpoint->SetCallback(MainThreadCheckerRuntime::NotifyBreakpointHit, this,
+ true);
+ breakpoint->SetBreakpointKind("main-thread-checker-report");
+ SetBreakpointID(breakpoint->GetID());
+
+ SetActive(true);
+}
+
+void MainThreadCheckerRuntime::Deactivate() {
+ SetActive(false);
+
+ auto BID = GetBreakpointID();
+ if (BID == LLDB_INVALID_BREAK_ID)
+ return;
+
+ if (ProcessSP process_sp = GetProcessSP()) {
+ process_sp->GetTarget().RemoveBreakpointByID(BID);
+ SetBreakpointID(LLDB_INVALID_BREAK_ID);
+ }
+}
+
+lldb::ThreadCollectionSP
+MainThreadCheckerRuntime::GetBacktracesFromExtendedStopInfo(
+ StructuredData::ObjectSP info) {
+ ThreadCollectionSP threads;
+ threads = std::make_shared<ThreadCollection>();
+
+ ProcessSP process_sp = GetProcessSP();
+
+ if (info->GetObjectForDotSeparatedPath("instrumentation_class")
+ ->GetStringValue() != "MainThreadChecker")
+ return threads;
+
+ std::vector<lldb::addr_t> PCs;
+ auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray();
+ trace->ForEach([&PCs](StructuredData::Object *PC) -> bool {
+ PCs.push_back(PC->GetAsInteger()->GetValue());
+ return true;
+ });
+
+ if (PCs.empty())
+ return threads;
+
+ StructuredData::ObjectSP thread_id_obj =
+ info->GetObjectForDotSeparatedPath("tid");
+ tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0;
+
+ HistoryThread *history_thread = new HistoryThread(*process_sp, tid, PCs);
+ ThreadSP new_thread_sp(history_thread);
+
+ // Save this in the Process' ExtendedThreadList so a strong pointer retains
+ // the object
+ process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
+ threads->AddThread(new_thread_sp);
+
+ return threads;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h
new file mode 100644
index 000000000000..1dcbc0f6bc89
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h
@@ -0,0 +1,67 @@
+//===-- MainThreadCheckerRuntime.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MainThreadCheckerRuntime_h_
+#define liblldb_MainThreadCheckerRuntime_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/InstrumentationRuntime.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+ class MainThreadCheckerRuntime : public lldb_private::InstrumentationRuntime {
+ public:
+ ~MainThreadCheckerRuntime() override;
+
+ static lldb::InstrumentationRuntimeSP
+ CreateInstance(const lldb::ProcessSP &process_sp);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static lldb::InstrumentationRuntimeType GetTypeStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ lldb::ThreadCollectionSP
+ GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info) override;
+
+ private:
+ MainThreadCheckerRuntime(const lldb::ProcessSP &process_sp)
+ : lldb_private::InstrumentationRuntime(process_sp) {}
+
+ const RegularExpression &GetPatternForRuntimeLibrary() override;
+
+ bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override;
+
+ void Activate() override;
+
+ void Deactivate();
+
+ static bool NotifyBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ StructuredData::ObjectSP RetrieveReportData(ExecutionContextRef exe_ctx_ref);
+ };
+
+} // namespace lldb_private
+
+#endif // liblldb_MainThreadCheckerRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp
new file mode 100644
index 000000000000..89f2139db71b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp
@@ -0,0 +1,1066 @@
+//===-- TSanRuntime.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TSanRuntime.h"
+
+#include "Plugins/Process/Utility/HistoryThread.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+lldb::InstrumentationRuntimeSP
+ThreadSanitizerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) {
+ return InstrumentationRuntimeSP(new ThreadSanitizerRuntime(process_sp));
+}
+
+void ThreadSanitizerRuntime::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "ThreadSanitizer instrumentation runtime plugin.",
+ CreateInstance, GetTypeStatic);
+}
+
+void ThreadSanitizerRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ThreadSanitizerRuntime::GetPluginNameStatic() {
+ return ConstString("ThreadSanitizer");
+}
+
+lldb::InstrumentationRuntimeType ThreadSanitizerRuntime::GetTypeStatic() {
+ return eInstrumentationRuntimeTypeThreadSanitizer;
+}
+
+ThreadSanitizerRuntime::~ThreadSanitizerRuntime() { Deactivate(); }
+
+const char *thread_sanitizer_retrieve_report_data_prefix = R"(
+extern "C"
+{
+ void *__tsan_get_current_report();
+ int __tsan_get_report_data(void *report, const char **description, int *count,
+ int *stack_count, int *mop_count, int *loc_count,
+ int *mutex_count, int *thread_count,
+ int *unique_tid_count, void **sleep_trace,
+ unsigned long trace_size);
+ int __tsan_get_report_stack(void *report, unsigned long idx, void **trace,
+ unsigned long trace_size);
+ int __tsan_get_report_mop(void *report, unsigned long idx, int *tid, void **addr,
+ int *size, int *write, int *atomic, void **trace,
+ unsigned long trace_size);
+ int __tsan_get_report_loc(void *report, unsigned long idx, const char **type,
+ void **addr, unsigned long *start, unsigned long *size, int *tid,
+ int *fd, int *suppressable, void **trace,
+ unsigned long trace_size);
+ int __tsan_get_report_mutex(void *report, unsigned long idx, unsigned long *mutex_id, void **addr,
+ int *destroyed, void **trace, unsigned long trace_size);
+ int __tsan_get_report_thread(void *report, unsigned long idx, int *tid, unsigned long *os_id,
+ int *running, const char **name, int *parent_tid,
+ void **trace, unsigned long trace_size);
+ int __tsan_get_report_unique_tid(void *report, unsigned long idx, int *tid);
+
+ // TODO: dlsym won't work on Windows.
+ void *dlsym(void* handle, const char* symbol);
+ int (*ptr__tsan_get_report_loc_object_type)(void *report, unsigned long idx, const char **object_type);
+}
+
+const int REPORT_TRACE_SIZE = 128;
+const int REPORT_ARRAY_SIZE = 4;
+
+struct data {
+ void *report;
+ const char *description;
+ int report_count;
+
+ void *sleep_trace[REPORT_TRACE_SIZE];
+
+ int stack_count;
+ struct {
+ int idx;
+ void *trace[REPORT_TRACE_SIZE];
+ } stacks[REPORT_ARRAY_SIZE];
+
+ int mop_count;
+ struct {
+ int idx;
+ int tid;
+ int size;
+ int write;
+ int atomic;
+ void *addr;
+ void *trace[REPORT_TRACE_SIZE];
+ } mops[REPORT_ARRAY_SIZE];
+
+ int loc_count;
+ struct {
+ int idx;
+ const char *type;
+ void *addr;
+ unsigned long start;
+ unsigned long size;
+ int tid;
+ int fd;
+ int suppressable;
+ void *trace[REPORT_TRACE_SIZE];
+ const char *object_type;
+ } locs[REPORT_ARRAY_SIZE];
+
+ int mutex_count;
+ struct {
+ int idx;
+ unsigned long mutex_id;
+ void *addr;
+ int destroyed;
+ void *trace[REPORT_TRACE_SIZE];
+ } mutexes[REPORT_ARRAY_SIZE];
+
+ int thread_count;
+ struct {
+ int idx;
+ int tid;
+ unsigned long os_id;
+ int running;
+ const char *name;
+ int parent_tid;
+ void *trace[REPORT_TRACE_SIZE];
+ } threads[REPORT_ARRAY_SIZE];
+
+ int unique_tid_count;
+ struct {
+ int idx;
+ int tid;
+ } unique_tids[REPORT_ARRAY_SIZE];
+};
+)";
+
+const char *thread_sanitizer_retrieve_report_data_command = R"(
+data t = {0};
+
+ptr__tsan_get_report_loc_object_type = (typeof(ptr__tsan_get_report_loc_object_type))(void *)dlsym((void*)-2 /*RTLD_DEFAULT*/, "__tsan_get_report_loc_object_type");
+
+t.report = __tsan_get_current_report();
+__tsan_get_report_data(t.report, &t.description, &t.report_count, &t.stack_count, &t.mop_count, &t.loc_count, &t.mutex_count, &t.thread_count, &t.unique_tid_count, t.sleep_trace, REPORT_TRACE_SIZE);
+
+if (t.stack_count > REPORT_ARRAY_SIZE) t.stack_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.stack_count; i++) {
+ t.stacks[i].idx = i;
+ __tsan_get_report_stack(t.report, i, t.stacks[i].trace, REPORT_TRACE_SIZE);
+}
+
+if (t.mop_count > REPORT_ARRAY_SIZE) t.mop_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.mop_count; i++) {
+ t.mops[i].idx = i;
+ __tsan_get_report_mop(t.report, i, &t.mops[i].tid, &t.mops[i].addr, &t.mops[i].size, &t.mops[i].write, &t.mops[i].atomic, t.mops[i].trace, REPORT_TRACE_SIZE);
+}
+
+if (t.loc_count > REPORT_ARRAY_SIZE) t.loc_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.loc_count; i++) {
+ t.locs[i].idx = i;
+ __tsan_get_report_loc(t.report, i, &t.locs[i].type, &t.locs[i].addr, &t.locs[i].start, &t.locs[i].size, &t.locs[i].tid, &t.locs[i].fd, &t.locs[i].suppressable, t.locs[i].trace, REPORT_TRACE_SIZE);
+ if (ptr__tsan_get_report_loc_object_type)
+ ptr__tsan_get_report_loc_object_type(t.report, i, &t.locs[i].object_type);
+}
+
+if (t.mutex_count > REPORT_ARRAY_SIZE) t.mutex_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.mutex_count; i++) {
+ t.mutexes[i].idx = i;
+ __tsan_get_report_mutex(t.report, i, &t.mutexes[i].mutex_id, &t.mutexes[i].addr, &t.mutexes[i].destroyed, t.mutexes[i].trace, REPORT_TRACE_SIZE);
+}
+
+if (t.thread_count > REPORT_ARRAY_SIZE) t.thread_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.thread_count; i++) {
+ t.threads[i].idx = i;
+ __tsan_get_report_thread(t.report, i, &t.threads[i].tid, &t.threads[i].os_id, &t.threads[i].running, &t.threads[i].name, &t.threads[i].parent_tid, t.threads[i].trace, REPORT_TRACE_SIZE);
+}
+
+if (t.unique_tid_count > REPORT_ARRAY_SIZE) t.unique_tid_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.unique_tid_count; i++) {
+ t.unique_tids[i].idx = i;
+ __tsan_get_report_unique_tid(t.report, i, &t.unique_tids[i].tid);
+}
+
+t;
+)";
+
+static StructuredData::Array *
+CreateStackTrace(ValueObjectSP o,
+ const std::string &trace_item_name = ".trace") {
+ StructuredData::Array *trace = new StructuredData::Array();
+ ValueObjectSP trace_value_object =
+ o->GetValueForExpressionPath(trace_item_name.c_str());
+ size_t count = trace_value_object->GetNumChildren();
+ for (size_t j = 0; j < count; j++) {
+ addr_t trace_addr =
+ trace_value_object->GetChildAtIndex(j, true)->GetValueAsUnsigned(0);
+ if (trace_addr == 0)
+ break;
+ trace->AddItem(
+ StructuredData::ObjectSP(new StructuredData::Integer(trace_addr)));
+ }
+ return trace;
+}
+
+static StructuredData::Array *ConvertToStructuredArray(
+ ValueObjectSP return_value_sp, const std::string &items_name,
+ const std::string &count_name,
+ std::function<void(ValueObjectSP o, StructuredData::Dictionary *dict)> const
+ &callback) {
+ StructuredData::Array *array = new StructuredData::Array();
+ unsigned int count =
+ return_value_sp->GetValueForExpressionPath(count_name.c_str())
+ ->GetValueAsUnsigned(0);
+ ValueObjectSP objects =
+ return_value_sp->GetValueForExpressionPath(items_name.c_str());
+ for (unsigned int i = 0; i < count; i++) {
+ ValueObjectSP o = objects->GetChildAtIndex(i, true);
+ StructuredData::Dictionary *dict = new StructuredData::Dictionary();
+
+ callback(o, dict);
+
+ array->AddItem(StructuredData::ObjectSP(dict));
+ }
+ return array;
+}
+
+static std::string RetrieveString(ValueObjectSP return_value_sp,
+ ProcessSP process_sp,
+ const std::string &expression_path) {
+ addr_t ptr =
+ return_value_sp->GetValueForExpressionPath(expression_path.c_str())
+ ->GetValueAsUnsigned(0);
+ std::string str;
+ Status error;
+ process_sp->ReadCStringFromMemory(ptr, str, error);
+ return str;
+}
+
+static void
+GetRenumberedThreadIds(ProcessSP process_sp, ValueObjectSP data,
+ std::map<uint64_t, user_id_t> &thread_id_map) {
+ ConvertToStructuredArray(
+ data, ".threads", ".thread_count",
+ [process_sp, &thread_id_map](ValueObjectSP o,
+ StructuredData::Dictionary *dict) {
+ uint64_t thread_id =
+ o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0);
+ uint64_t thread_os_id =
+ o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0);
+ user_id_t lldb_user_id = 0;
+
+ bool can_update = true;
+ ThreadSP lldb_thread = process_sp->GetThreadList().FindThreadByID(
+ thread_os_id, can_update);
+ if (lldb_thread) {
+ lldb_user_id = lldb_thread->GetIndexID();
+ } else {
+ // This isn't a live thread anymore. Ask process to assign a new
+ // Index ID (or return an old one if we've already seen this
+ // thread_os_id). It will also make sure that no new threads are
+ // assigned this Index ID.
+ lldb_user_id = process_sp->AssignIndexIDToThread(thread_os_id);
+ }
+
+ thread_id_map[thread_id] = lldb_user_id;
+ });
+}
+
+static user_id_t Renumber(uint64_t id,
+ std::map<uint64_t, user_id_t> &thread_id_map) {
+ auto IT = thread_id_map.find(id);
+ if (IT == thread_id_map.end())
+ return 0;
+
+ return IT->second;
+}
+
+StructuredData::ObjectSP
+ThreadSanitizerRuntime::RetrieveReportData(ExecutionContextRef exe_ctx_ref) {
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return StructuredData::ObjectSP();
+
+ ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+
+ if (!frame_sp)
+ return StructuredData::ObjectSP();
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(true);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
+ options.SetPrefix(thread_sanitizer_retrieve_report_data_prefix);
+ options.SetAutoApplyFixIts(false);
+ options.SetLanguage(eLanguageTypeObjC_plus_plus);
+
+ ValueObjectSP main_value;
+ ExecutionContext exe_ctx;
+ Status eval_error;
+ frame_sp->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = UserExpression::Evaluate(
+ exe_ctx, options, thread_sanitizer_retrieve_report_data_command, "",
+ main_value, eval_error);
+ if (result != eExpressionCompleted) {
+ process_sp->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf(
+ "Warning: Cannot evaluate ThreadSanitizer expression:\n%s\n",
+ eval_error.AsCString());
+ return StructuredData::ObjectSP();
+ }
+
+ std::map<uint64_t, user_id_t> thread_id_map;
+ GetRenumberedThreadIds(process_sp, main_value, thread_id_map);
+
+ StructuredData::Dictionary *dict = new StructuredData::Dictionary();
+ dict->AddStringItem("instrumentation_class", "ThreadSanitizer");
+ dict->AddStringItem("issue_type",
+ RetrieveString(main_value, process_sp, ".description"));
+ dict->AddIntegerItem("report_count",
+ main_value->GetValueForExpressionPath(".report_count")
+ ->GetValueAsUnsigned(0));
+ dict->AddItem("sleep_trace", StructuredData::ObjectSP(CreateStackTrace(
+ main_value, ".sleep_trace")));
+
+ StructuredData::Array *stacks = ConvertToStructuredArray(
+ main_value, ".stacks", ".stack_count",
+ [thread_sp](ValueObjectSP o, StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o)));
+ // "stacks" happen on the current thread
+ dict->AddIntegerItem("thread_id", thread_sp->GetIndexID());
+ });
+ dict->AddItem("stacks", StructuredData::ObjectSP(stacks));
+
+ StructuredData::Array *mops = ConvertToStructuredArray(
+ main_value, ".mops", ".mop_count",
+ [&thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "thread_id",
+ Renumber(
+ o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),
+ thread_id_map));
+ dict->AddIntegerItem(
+ "size",
+ o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0));
+ dict->AddBooleanItem(
+ "is_write",
+ o->GetValueForExpressionPath(".write")->GetValueAsUnsigned(0));
+ dict->AddBooleanItem(
+ "is_atomic",
+ o->GetValueForExpressionPath(".atomic")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "address",
+ o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0));
+ dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o)));
+ });
+ dict->AddItem("mops", StructuredData::ObjectSP(mops));
+
+ StructuredData::Array *locs = ConvertToStructuredArray(
+ main_value, ".locs", ".loc_count",
+ [process_sp, &thread_id_map](ValueObjectSP o,
+ StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddStringItem("type", RetrieveString(o, process_sp, ".type"));
+ dict->AddIntegerItem(
+ "address",
+ o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "start",
+ o->GetValueForExpressionPath(".start")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "size",
+ o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "thread_id",
+ Renumber(
+ o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),
+ thread_id_map));
+ dict->AddIntegerItem(
+ "file_descriptor",
+ o->GetValueForExpressionPath(".fd")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem("suppressable",
+ o->GetValueForExpressionPath(".suppressable")
+ ->GetValueAsUnsigned(0));
+ dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o)));
+ dict->AddStringItem("object_type",
+ RetrieveString(o, process_sp, ".object_type"));
+ });
+ dict->AddItem("locs", StructuredData::ObjectSP(locs));
+
+ StructuredData::Array *mutexes = ConvertToStructuredArray(
+ main_value, ".mutexes", ".mutex_count",
+ [](ValueObjectSP o, StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "mutex_id",
+ o->GetValueForExpressionPath(".mutex_id")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "address",
+ o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "destroyed",
+ o->GetValueForExpressionPath(".destroyed")->GetValueAsUnsigned(0));
+ dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o)));
+ });
+ dict->AddItem("mutexes", StructuredData::ObjectSP(mutexes));
+
+ StructuredData::Array *threads = ConvertToStructuredArray(
+ main_value, ".threads", ".thread_count",
+ [process_sp, &thread_id_map](ValueObjectSP o,
+ StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "thread_id",
+ Renumber(
+ o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),
+ thread_id_map));
+ dict->AddIntegerItem(
+ "thread_os_id",
+ o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "running",
+ o->GetValueForExpressionPath(".running")->GetValueAsUnsigned(0));
+ dict->AddStringItem("name", RetrieveString(o, process_sp, ".name"));
+ dict->AddIntegerItem(
+ "parent_thread_id",
+ Renumber(o->GetValueForExpressionPath(".parent_tid")
+ ->GetValueAsUnsigned(0),
+ thread_id_map));
+ dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o)));
+ });
+ dict->AddItem("threads", StructuredData::ObjectSP(threads));
+
+ StructuredData::Array *unique_tids = ConvertToStructuredArray(
+ main_value, ".unique_tids", ".unique_tid_count",
+ [&thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "tid",
+ Renumber(
+ o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),
+ thread_id_map));
+ });
+ dict->AddItem("unique_tids", StructuredData::ObjectSP(unique_tids));
+
+ return StructuredData::ObjectSP(dict);
+}
+
+std::string
+ThreadSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) {
+ std::string description = report->GetAsDictionary()
+ ->GetValueForKey("issue_type")
+ ->GetAsString()
+ ->GetValue();
+
+ if (description == "data-race") {
+ return "Data race";
+ } else if (description == "data-race-vptr") {
+ return "Data race on C++ virtual pointer";
+ } else if (description == "heap-use-after-free") {
+ return "Use of deallocated memory";
+ } else if (description == "heap-use-after-free-vptr") {
+ return "Use of deallocated C++ virtual pointer";
+ } else if (description == "thread-leak") {
+ return "Thread leak";
+ } else if (description == "locked-mutex-destroy") {
+ return "Destruction of a locked mutex";
+ } else if (description == "mutex-double-lock") {
+ return "Double lock of a mutex";
+ } else if (description == "mutex-invalid-access") {
+ return "Use of an uninitialized or destroyed mutex";
+ } else if (description == "mutex-bad-unlock") {
+ return "Unlock of an unlocked mutex (or by a wrong thread)";
+ } else if (description == "mutex-bad-read-lock") {
+ return "Read lock of a write locked mutex";
+ } else if (description == "mutex-bad-read-unlock") {
+ return "Read unlock of a write locked mutex";
+ } else if (description == "signal-unsafe-call") {
+ return "Signal-unsafe call inside a signal handler";
+ } else if (description == "errno-in-signal-handler") {
+ return "Overwrite of errno in a signal handler";
+ } else if (description == "lock-order-inversion") {
+ return "Lock order inversion (potential deadlock)";
+ } else if (description == "external-race") {
+ return "Race on a library object";
+ } else if (description == "swift-access-race") {
+ return "Swift access race";
+ }
+
+ // for unknown report codes just show the code
+ return description;
+}
+
+static std::string Sprintf(const char *format, ...) {
+ StreamString s;
+ va_list args;
+ va_start(args, format);
+ s.PrintfVarArg(format, args);
+ va_end(args);
+ return s.GetString();
+}
+
+static std::string GetSymbolNameFromAddress(ProcessSP process_sp, addr_t addr) {
+ lldb_private::Address so_addr;
+ if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr,
+ so_addr))
+ return "";
+
+ lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol();
+ if (!symbol)
+ return "";
+
+ std::string sym_name = symbol->GetName().GetCString();
+ return sym_name;
+}
+
+static void GetSymbolDeclarationFromAddress(ProcessSP process_sp, addr_t addr,
+ Declaration &decl) {
+ lldb_private::Address so_addr;
+ if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr,
+ so_addr))
+ return;
+
+ lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol();
+ if (!symbol)
+ return;
+
+ ConstString sym_name = symbol->GetMangled().GetName(
+ lldb::eLanguageTypeUnknown, Mangled::ePreferMangled);
+
+ ModuleSP module = symbol->CalculateSymbolContextModule();
+ if (!module)
+ return;
+
+ VariableList var_list;
+ module->FindGlobalVariables(sym_name, nullptr, 1U, var_list);
+ if (var_list.GetSize() < 1)
+ return;
+
+ VariableSP var = var_list.GetVariableAtIndex(0);
+ decl = var->GetDeclaration();
+}
+
+addr_t ThreadSanitizerRuntime::GetFirstNonInternalFramePc(
+ StructuredData::ObjectSP trace, bool skip_one_frame) {
+ ProcessSP process_sp = GetProcessSP();
+ ModuleSP runtime_module_sp = GetRuntimeModuleSP();
+
+ StructuredData::Array *trace_array = trace->GetAsArray();
+ for (size_t i = 0; i < trace_array->GetSize(); i++) {
+ if (skip_one_frame && i == 0)
+ continue;
+
+ addr_t addr;
+ if (!trace_array->GetItemAtIndexAsInteger(i, addr))
+ continue;
+
+ lldb_private::Address so_addr;
+ if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(
+ addr, so_addr))
+ continue;
+
+ if (so_addr.GetModule() == runtime_module_sp)
+ continue;
+
+ return addr;
+ }
+
+ return 0;
+}
+
+std::string
+ThreadSanitizerRuntime::GenerateSummary(StructuredData::ObjectSP report) {
+ ProcessSP process_sp = GetProcessSP();
+
+ std::string summary = report->GetAsDictionary()
+ ->GetValueForKey("description")
+ ->GetAsString()
+ ->GetValue();
+ bool skip_one_frame =
+ report->GetObjectForDotSeparatedPath("issue_type")->GetStringValue() ==
+ "external-race";
+
+ addr_t pc = 0;
+ if (report->GetAsDictionary()
+ ->GetValueForKey("mops")
+ ->GetAsArray()
+ ->GetSize() > 0)
+ pc = GetFirstNonInternalFramePc(report->GetAsDictionary()
+ ->GetValueForKey("mops")
+ ->GetAsArray()
+ ->GetItemAtIndex(0)
+ ->GetAsDictionary()
+ ->GetValueForKey("trace"),
+ skip_one_frame);
+
+ if (report->GetAsDictionary()
+ ->GetValueForKey("stacks")
+ ->GetAsArray()
+ ->GetSize() > 0)
+ pc = GetFirstNonInternalFramePc(report->GetAsDictionary()
+ ->GetValueForKey("stacks")
+ ->GetAsArray()
+ ->GetItemAtIndex(0)
+ ->GetAsDictionary()
+ ->GetValueForKey("trace"),
+ skip_one_frame);
+
+ if (pc != 0) {
+ summary = summary + " in " + GetSymbolNameFromAddress(process_sp, pc);
+ }
+
+ if (report->GetAsDictionary()
+ ->GetValueForKey("locs")
+ ->GetAsArray()
+ ->GetSize() > 0) {
+ StructuredData::ObjectSP loc = report->GetAsDictionary()
+ ->GetValueForKey("locs")
+ ->GetAsArray()
+ ->GetItemAtIndex(0);
+ std::string object_type = loc->GetAsDictionary()
+ ->GetValueForKey("object_type")
+ ->GetAsString()
+ ->GetValue();
+ if (!object_type.empty()) {
+ summary = "Race on " + object_type + " object";
+ }
+ addr_t addr = loc->GetAsDictionary()
+ ->GetValueForKey("address")
+ ->GetAsInteger()
+ ->GetValue();
+ if (addr == 0)
+ addr = loc->GetAsDictionary()
+ ->GetValueForKey("start")
+ ->GetAsInteger()
+ ->GetValue();
+
+ if (addr != 0) {
+ std::string global_name = GetSymbolNameFromAddress(process_sp, addr);
+ if (!global_name.empty()) {
+ summary = summary + " at " + global_name;
+ } else {
+ summary = summary + " at " + Sprintf("0x%llx", addr);
+ }
+ } else {
+ int fd = loc->GetAsDictionary()
+ ->GetValueForKey("file_descriptor")
+ ->GetAsInteger()
+ ->GetValue();
+ if (fd != 0) {
+ summary = summary + " on file descriptor " + Sprintf("%d", fd);
+ }
+ }
+ }
+
+ return summary;
+}
+
+addr_t
+ThreadSanitizerRuntime::GetMainRacyAddress(StructuredData::ObjectSP report) {
+ addr_t result = (addr_t)-1;
+
+ report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach(
+ [&result](StructuredData::Object *o) -> bool {
+ addr_t addr =
+ o->GetObjectForDotSeparatedPath("address")->GetIntegerValue();
+ if (addr < result)
+ result = addr;
+ return true;
+ });
+
+ return (result == (addr_t)-1) ? 0 : result;
+}
+
+std::string ThreadSanitizerRuntime::GetLocationDescription(
+ StructuredData::ObjectSP report, addr_t &global_addr,
+ std::string &global_name, std::string &filename, uint32_t &line) {
+ std::string result = "";
+
+ ProcessSP process_sp = GetProcessSP();
+
+ if (report->GetAsDictionary()
+ ->GetValueForKey("locs")
+ ->GetAsArray()
+ ->GetSize() > 0) {
+ StructuredData::ObjectSP loc = report->GetAsDictionary()
+ ->GetValueForKey("locs")
+ ->GetAsArray()
+ ->GetItemAtIndex(0);
+ std::string type =
+ loc->GetAsDictionary()->GetValueForKey("type")->GetStringValue();
+ if (type == "global") {
+ global_addr = loc->GetAsDictionary()
+ ->GetValueForKey("address")
+ ->GetAsInteger()
+ ->GetValue();
+ global_name = GetSymbolNameFromAddress(process_sp, global_addr);
+ if (!global_name.empty()) {
+ result = Sprintf("'%s' is a global variable (0x%llx)",
+ global_name.c_str(), global_addr);
+ } else {
+ result = Sprintf("0x%llx is a global variable", global_addr);
+ }
+
+ Declaration decl;
+ GetSymbolDeclarationFromAddress(process_sp, global_addr, decl);
+ if (decl.GetFile()) {
+ filename = decl.GetFile().GetPath();
+ line = decl.GetLine();
+ }
+ } else if (type == "heap") {
+ addr_t addr = loc->GetAsDictionary()
+ ->GetValueForKey("start")
+ ->GetAsInteger()
+ ->GetValue();
+ long size = loc->GetAsDictionary()
+ ->GetValueForKey("size")
+ ->GetAsInteger()
+ ->GetValue();
+ std::string object_type = loc->GetAsDictionary()
+ ->GetValueForKey("object_type")
+ ->GetAsString()
+ ->GetValue();
+ if (!object_type.empty()) {
+ result = Sprintf("Location is a %ld-byte %s object at 0x%llx", size,
+ object_type.c_str(), addr);
+ } else {
+ result =
+ Sprintf("Location is a %ld-byte heap object at 0x%llx", size, addr);
+ }
+ } else if (type == "stack") {
+ int tid = loc->GetAsDictionary()
+ ->GetValueForKey("thread_id")
+ ->GetAsInteger()
+ ->GetValue();
+ result = Sprintf("Location is stack of thread %d", tid);
+ } else if (type == "tls") {
+ int tid = loc->GetAsDictionary()
+ ->GetValueForKey("thread_id")
+ ->GetAsInteger()
+ ->GetValue();
+ result = Sprintf("Location is TLS of thread %d", tid);
+ } else if (type == "fd") {
+ int fd = loc->GetAsDictionary()
+ ->GetValueForKey("file_descriptor")
+ ->GetAsInteger()
+ ->GetValue();
+ result = Sprintf("Location is file descriptor %d", fd);
+ }
+ }
+
+ return result;
+}
+
+bool ThreadSanitizerRuntime::NotifyBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false;
+
+ ThreadSanitizerRuntime *const instance =
+ static_cast<ThreadSanitizerRuntime *>(baton);
+
+ ProcessSP process_sp = instance->GetProcessSP();
+
+ if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
+ return false;
+
+ StructuredData::ObjectSP report =
+ instance->RetrieveReportData(context->exe_ctx_ref);
+ std::string stop_reason_description;
+ if (report) {
+ std::string issue_description = instance->FormatDescription(report);
+ report->GetAsDictionary()->AddStringItem("description", issue_description);
+ stop_reason_description = issue_description + " detected";
+ report->GetAsDictionary()->AddStringItem("stop_description",
+ stop_reason_description);
+ std::string summary = instance->GenerateSummary(report);
+ report->GetAsDictionary()->AddStringItem("summary", summary);
+ addr_t main_address = instance->GetMainRacyAddress(report);
+ report->GetAsDictionary()->AddIntegerItem("memory_address", main_address);
+
+ addr_t global_addr = 0;
+ std::string global_name = "";
+ std::string location_filename = "";
+ uint32_t location_line = 0;
+ std::string location_description = instance->GetLocationDescription(
+ report, global_addr, global_name, location_filename, location_line);
+ report->GetAsDictionary()->AddStringItem("location_description",
+ location_description);
+ if (global_addr != 0) {
+ report->GetAsDictionary()->AddIntegerItem("global_address", global_addr);
+ }
+ if (!global_name.empty()) {
+ report->GetAsDictionary()->AddStringItem("global_name", global_name);
+ }
+ if (location_filename != "") {
+ report->GetAsDictionary()->AddStringItem("location_filename",
+ location_filename);
+ report->GetAsDictionary()->AddIntegerItem("location_line", location_line);
+ }
+
+ bool all_addresses_are_same = true;
+ report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach(
+ [&all_addresses_are_same,
+ main_address](StructuredData::Object *o) -> bool {
+ addr_t addr =
+ o->GetObjectForDotSeparatedPath("address")->GetIntegerValue();
+ if (main_address != addr)
+ all_addresses_are_same = false;
+ return true;
+ });
+ report->GetAsDictionary()->AddBooleanItem("all_addresses_are_same",
+ all_addresses_are_same);
+ }
+
+ // Make sure this is the right process
+ if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) {
+ ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
+ if (thread_sp)
+ thread_sp->SetStopInfo(
+ InstrumentationRuntimeStopInfo::
+ CreateStopReasonWithInstrumentationData(
+ *thread_sp, stop_reason_description, report));
+
+ StreamFileSP stream_sp(
+ process_sp->GetTarget().GetDebugger().GetOutputFile());
+ if (stream_sp) {
+ stream_sp->Printf("ThreadSanitizer report breakpoint hit. Use 'thread "
+ "info -s' to get extended information about the "
+ "report.\n");
+ }
+ return true; // Return true to stop the target
+ } else
+ return false; // Let target run
+}
+
+const RegularExpression &ThreadSanitizerRuntime::GetPatternForRuntimeLibrary() {
+ static RegularExpression regex(llvm::StringRef("libclang_rt.tsan_"));
+ return regex;
+}
+
+bool ThreadSanitizerRuntime::CheckIfRuntimeIsValid(
+ const lldb::ModuleSP module_sp) {
+ static ConstString g_tsan_get_current_report("__tsan_get_current_report");
+ const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
+ g_tsan_get_current_report, lldb::eSymbolTypeAny);
+ return symbol != nullptr;
+}
+
+void ThreadSanitizerRuntime::Activate() {
+ if (IsActive())
+ return;
+
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return;
+
+ ConstString symbol_name("__tsan_on_report");
+ const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType(
+ symbol_name, eSymbolTypeCode);
+
+ if (symbol == nullptr)
+ return;
+
+ if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
+ return;
+
+ Target &target = process_sp->GetTarget();
+ addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
+
+ if (symbol_address == LLDB_INVALID_ADDRESS)
+ return;
+
+ bool internal = true;
+ bool hardware = false;
+ Breakpoint *breakpoint =
+ process_sp->GetTarget()
+ .CreateBreakpoint(symbol_address, internal, hardware)
+ .get();
+ breakpoint->SetCallback(ThreadSanitizerRuntime::NotifyBreakpointHit, this,
+ true);
+ breakpoint->SetBreakpointKind("thread-sanitizer-report");
+ SetBreakpointID(breakpoint->GetID());
+
+ SetActive(true);
+}
+
+void ThreadSanitizerRuntime::Deactivate() {
+ if (GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID());
+ SetBreakpointID(LLDB_INVALID_BREAK_ID);
+ }
+ }
+ SetActive(false);
+}
+static std::string GenerateThreadName(const std::string &path,
+ StructuredData::Object *o,
+ StructuredData::ObjectSP main_info) {
+ std::string result = "additional information";
+
+ if (path == "mops") {
+ int size = o->GetObjectForDotSeparatedPath("size")->GetIntegerValue();
+ int thread_id =
+ o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue();
+ bool is_write =
+ o->GetObjectForDotSeparatedPath("is_write")->GetBooleanValue();
+ bool is_atomic =
+ o->GetObjectForDotSeparatedPath("is_atomic")->GetBooleanValue();
+ addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue();
+
+ std::string addr_string = Sprintf(" at 0x%llx", addr);
+
+ if (main_info->GetObjectForDotSeparatedPath("all_addresses_are_same")
+ ->GetBooleanValue()) {
+ addr_string = "";
+ }
+
+ if (main_info->GetObjectForDotSeparatedPath("issue_type")
+ ->GetStringValue() == "external-race") {
+ result = Sprintf("%s access by thread %d",
+ is_write ? "mutating" : "read-only", thread_id);
+ } else if (main_info->GetObjectForDotSeparatedPath("issue_type")
+ ->GetStringValue() == "swift-access-race") {
+ result = Sprintf("modifying access by thread %d", thread_id);
+ } else {
+ result = Sprintf("%s%s of size %d%s by thread %d",
+ is_atomic ? "atomic " : "", is_write ? "write" : "read",
+ size, addr_string.c_str(), thread_id);
+ }
+ }
+
+ if (path == "threads") {
+ int thread_id =
+ o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue();
+ result = Sprintf("Thread %d created", thread_id);
+ }
+
+ if (path == "locs") {
+ std::string type =
+ o->GetAsDictionary()->GetValueForKey("type")->GetStringValue();
+ int thread_id =
+ o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue();
+ int fd =
+ o->GetObjectForDotSeparatedPath("file_descriptor")->GetIntegerValue();
+ if (type == "heap") {
+ result = Sprintf("Heap block allocated by thread %d", thread_id);
+ } else if (type == "fd") {
+ result =
+ Sprintf("File descriptor %d created by thread %t", fd, thread_id);
+ }
+ }
+
+ if (path == "mutexes") {
+ int mutex_id =
+ o->GetObjectForDotSeparatedPath("mutex_id")->GetIntegerValue();
+
+ result = Sprintf("Mutex M%d created", mutex_id);
+ }
+
+ if (path == "stacks") {
+ int thread_id =
+ o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue();
+ result = Sprintf("Thread %d", thread_id);
+ }
+
+ result[0] = toupper(result[0]);
+
+ return result;
+}
+
+static void AddThreadsForPath(const std::string &path,
+ ThreadCollectionSP threads, ProcessSP process_sp,
+ StructuredData::ObjectSP info) {
+ info->GetObjectForDotSeparatedPath(path)->GetAsArray()->ForEach(
+ [process_sp, threads, path, info](StructuredData::Object *o) -> bool {
+ std::vector<lldb::addr_t> pcs;
+ o->GetObjectForDotSeparatedPath("trace")->GetAsArray()->ForEach(
+ [&pcs](StructuredData::Object *pc) -> bool {
+ pcs.push_back(pc->GetAsInteger()->GetValue());
+ return true;
+ });
+
+ if (pcs.size() == 0)
+ return true;
+
+ StructuredData::ObjectSP thread_id_obj =
+ o->GetObjectForDotSeparatedPath("thread_os_id");
+ tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0;
+
+ HistoryThread *history_thread =
+ new HistoryThread(*process_sp, tid, pcs);
+ ThreadSP new_thread_sp(history_thread);
+ new_thread_sp->SetName(GenerateThreadName(path, o, info).c_str());
+
+ // Save this in the Process' ExtendedThreadList so a strong pointer
+ // retains the object
+ process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
+ threads->AddThread(new_thread_sp);
+
+ return true;
+ });
+}
+
+lldb::ThreadCollectionSP
+ThreadSanitizerRuntime::GetBacktracesFromExtendedStopInfo(
+ StructuredData::ObjectSP info) {
+ ThreadCollectionSP threads;
+ threads = std::make_shared<ThreadCollection>();
+
+ if (info->GetObjectForDotSeparatedPath("instrumentation_class")
+ ->GetStringValue() != "ThreadSanitizer")
+ return threads;
+
+ ProcessSP process_sp = GetProcessSP();
+
+ AddThreadsForPath("stacks", threads, process_sp, info);
+ AddThreadsForPath("mops", threads, process_sp, info);
+ AddThreadsForPath("locs", threads, process_sp, info);
+ AddThreadsForPath("mutexes", threads, process_sp, info);
+ AddThreadsForPath("threads", threads, process_sp, info);
+
+ return threads;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.h b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.h
new file mode 100644
index 000000000000..db8bb1db7996
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.h
@@ -0,0 +1,81 @@
+//===-- TSanRuntime.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadSanitizerRuntime_h_
+#define liblldb_ThreadSanitizerRuntime_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/InstrumentationRuntime.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class ThreadSanitizerRuntime : public lldb_private::InstrumentationRuntime {
+public:
+ ~ThreadSanitizerRuntime() override;
+
+ static lldb::InstrumentationRuntimeSP
+ CreateInstance(const lldb::ProcessSP &process_sp);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static lldb::InstrumentationRuntimeType GetTypeStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ lldb::ThreadCollectionSP
+ GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info) override;
+
+private:
+ ThreadSanitizerRuntime(const lldb::ProcessSP &process_sp)
+ : lldb_private::InstrumentationRuntime(process_sp) {}
+
+ const RegularExpression &GetPatternForRuntimeLibrary() override;
+
+ bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override;
+
+ void Activate() override;
+
+ void Deactivate();
+
+ static bool NotifyBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ StructuredData::ObjectSP RetrieveReportData(ExecutionContextRef exe_ctx_ref);
+
+ std::string FormatDescription(StructuredData::ObjectSP report);
+
+ std::string GenerateSummary(StructuredData::ObjectSP report);
+
+ lldb::addr_t GetMainRacyAddress(StructuredData::ObjectSP report);
+
+ std::string GetLocationDescription(StructuredData::ObjectSP report,
+ lldb::addr_t &global_addr,
+ std::string &global_name,
+ std::string &filename, uint32_t &line);
+
+ lldb::addr_t GetFirstNonInternalFramePc(StructuredData::ObjectSP trace,
+ bool skip_one_frame = false);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ThreadSanitizerRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp
new file mode 100644
index 000000000000..367098bb448e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp
@@ -0,0 +1,341 @@
+//===-- UBSanRuntime.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UBSanRuntime.h"
+
+#include "Plugins/Process/Utility/HistoryThread.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include <ctype.h>
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+UndefinedBehaviorSanitizerRuntime::~UndefinedBehaviorSanitizerRuntime() {
+ Deactivate();
+}
+
+lldb::InstrumentationRuntimeSP
+UndefinedBehaviorSanitizerRuntime::CreateInstance(
+ const lldb::ProcessSP &process_sp) {
+ return InstrumentationRuntimeSP(
+ new UndefinedBehaviorSanitizerRuntime(process_sp));
+}
+
+void UndefinedBehaviorSanitizerRuntime::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(),
+ "UndefinedBehaviorSanitizer instrumentation runtime plugin.",
+ CreateInstance, GetTypeStatic);
+}
+
+void UndefinedBehaviorSanitizerRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString
+UndefinedBehaviorSanitizerRuntime::GetPluginNameStatic() {
+ return ConstString("UndefinedBehaviorSanitizer");
+}
+
+lldb::InstrumentationRuntimeType
+UndefinedBehaviorSanitizerRuntime::GetTypeStatic() {
+ return eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer;
+}
+
+static const char *ub_sanitizer_retrieve_report_data_prefix = R"(
+extern "C" {
+void
+__ubsan_get_current_report_data(const char **OutIssueKind,
+ const char **OutMessage, const char **OutFilename, unsigned *OutLine,
+ unsigned *OutCol, char **OutMemoryAddr);
+}
+
+struct data {
+ const char *issue_kind;
+ const char *message;
+ const char *filename;
+ unsigned line;
+ unsigned col;
+ char *memory_addr;
+};
+)";
+
+static const char *ub_sanitizer_retrieve_report_data_command = R"(
+data t;
+__ubsan_get_current_report_data(&t.issue_kind, &t.message, &t.filename, &t.line,
+ &t.col, &t.memory_addr);
+t;
+)";
+
+static addr_t RetrieveUnsigned(ValueObjectSP return_value_sp,
+ ProcessSP process_sp,
+ const std::string &expression_path) {
+ return return_value_sp->GetValueForExpressionPath(expression_path.c_str())
+ ->GetValueAsUnsigned(0);
+}
+
+static std::string RetrieveString(ValueObjectSP return_value_sp,
+ ProcessSP process_sp,
+ const std::string &expression_path) {
+ addr_t ptr = RetrieveUnsigned(return_value_sp, process_sp, expression_path);
+ std::string str;
+ Status error;
+ process_sp->ReadCStringFromMemory(ptr, str, error);
+ return str;
+}
+
+StructuredData::ObjectSP UndefinedBehaviorSanitizerRuntime::RetrieveReportData(
+ ExecutionContextRef exe_ctx_ref) {
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return StructuredData::ObjectSP();
+
+ ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+ ModuleSP runtime_module_sp = GetRuntimeModuleSP();
+ Target &target = process_sp->GetTarget();
+
+ if (!frame_sp)
+ return StructuredData::ObjectSP();
+
+ StreamFileSP Stream(target.GetDebugger().GetOutputFile());
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(true);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
+ options.SetPrefix(ub_sanitizer_retrieve_report_data_prefix);
+ options.SetAutoApplyFixIts(false);
+ options.SetLanguage(eLanguageTypeObjC_plus_plus);
+
+ ValueObjectSP main_value;
+ ExecutionContext exe_ctx;
+ Status eval_error;
+ frame_sp->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = UserExpression::Evaluate(
+ exe_ctx, options, ub_sanitizer_retrieve_report_data_command, "",
+ main_value, eval_error);
+ if (result != eExpressionCompleted) {
+ target.GetDebugger().GetAsyncOutputStream()->Printf(
+ "Warning: Cannot evaluate UndefinedBehaviorSanitizer expression:\n%s\n",
+ eval_error.AsCString());
+ return StructuredData::ObjectSP();
+ }
+
+ // Gather the PCs of the user frames in the backtrace.
+ StructuredData::Array *trace = new StructuredData::Array();
+ auto trace_sp = StructuredData::ObjectSP(trace);
+ for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) {
+ const Address FCA =
+ thread_sp->GetStackFrameAtIndex(I)->GetFrameCodeAddress();
+ if (FCA.GetModule() == runtime_module_sp) // Skip PCs from the runtime.
+ continue;
+
+ lldb::addr_t PC = FCA.GetLoadAddress(&target);
+ trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC)));
+ }
+
+ std::string IssueKind = RetrieveString(main_value, process_sp, ".issue_kind");
+ std::string ErrMessage = RetrieveString(main_value, process_sp, ".message");
+ std::string Filename = RetrieveString(main_value, process_sp, ".filename");
+ unsigned Line = RetrieveUnsigned(main_value, process_sp, ".line");
+ unsigned Col = RetrieveUnsigned(main_value, process_sp, ".col");
+ uintptr_t MemoryAddr =
+ RetrieveUnsigned(main_value, process_sp, ".memory_addr");
+
+ auto *d = new StructuredData::Dictionary();
+ auto dict_sp = StructuredData::ObjectSP(d);
+ d->AddStringItem("instrumentation_class", "UndefinedBehaviorSanitizer");
+ d->AddStringItem("description", IssueKind);
+ d->AddStringItem("summary", ErrMessage);
+ d->AddStringItem("filename", Filename);
+ d->AddIntegerItem("line", Line);
+ d->AddIntegerItem("col", Col);
+ d->AddIntegerItem("memory_address", MemoryAddr);
+ d->AddIntegerItem("tid", thread_sp->GetID());
+ d->AddItem("trace", trace_sp);
+ return dict_sp;
+}
+
+static std::string GetStopReasonDescription(StructuredData::ObjectSP report) {
+ llvm::StringRef stop_reason_description_ref;
+ report->GetAsDictionary()->GetValueForKeyAsString("description",
+ stop_reason_description_ref);
+ std::string stop_reason_description = stop_reason_description_ref;
+
+ if (!stop_reason_description.size()) {
+ stop_reason_description = "Undefined behavior detected";
+ } else {
+ stop_reason_description[0] = toupper(stop_reason_description[0]);
+ for (unsigned I = 1; I < stop_reason_description.size(); ++I)
+ if (stop_reason_description[I] == '-')
+ stop_reason_description[I] = ' ';
+ }
+ return stop_reason_description;
+}
+
+bool UndefinedBehaviorSanitizerRuntime::NotifyBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false; //< false => resume execution.
+
+ UndefinedBehaviorSanitizerRuntime *const instance =
+ static_cast<UndefinedBehaviorSanitizerRuntime *>(baton);
+
+ ProcessSP process_sp = instance->GetProcessSP();
+ ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
+ if (!process_sp || !thread_sp ||
+ process_sp != context->exe_ctx_ref.GetProcessSP())
+ return false;
+
+ if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
+ return false;
+
+ StructuredData::ObjectSP report =
+ instance->RetrieveReportData(context->exe_ctx_ref);
+
+ if (report) {
+ thread_sp->SetStopInfo(
+ InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
+ *thread_sp, GetStopReasonDescription(report), report));
+ return true;
+ }
+
+ return false;
+}
+
+const RegularExpression &
+UndefinedBehaviorSanitizerRuntime::GetPatternForRuntimeLibrary() {
+ static RegularExpression regex(llvm::StringRef("libclang_rt\\.(a|t|ub)san_"));
+ return regex;
+}
+
+bool UndefinedBehaviorSanitizerRuntime::CheckIfRuntimeIsValid(
+ const lldb::ModuleSP module_sp) {
+ static ConstString ubsan_test_sym("__ubsan_on_report");
+ const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
+ ubsan_test_sym, lldb::eSymbolTypeAny);
+ return symbol != nullptr;
+}
+
+// FIXME: Factor out all the logic we have in common with the {a,t}san plugins.
+void UndefinedBehaviorSanitizerRuntime::Activate() {
+ if (IsActive())
+ return;
+
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return;
+
+ ModuleSP runtime_module_sp = GetRuntimeModuleSP();
+
+ ConstString symbol_name("__ubsan_on_report");
+ const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType(
+ symbol_name, eSymbolTypeCode);
+
+ if (symbol == nullptr)
+ return;
+
+ if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
+ return;
+
+ Target &target = process_sp->GetTarget();
+ addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
+
+ if (symbol_address == LLDB_INVALID_ADDRESS)
+ return;
+
+ Breakpoint *breakpoint =
+ process_sp->GetTarget()
+ .CreateBreakpoint(symbol_address, /*internal=*/true,
+ /*hardware=*/false)
+ .get();
+ breakpoint->SetCallback(
+ UndefinedBehaviorSanitizerRuntime::NotifyBreakpointHit, this, true);
+ breakpoint->SetBreakpointKind("undefined-behavior-sanitizer-report");
+ SetBreakpointID(breakpoint->GetID());
+
+ SetActive(true);
+}
+
+void UndefinedBehaviorSanitizerRuntime::Deactivate() {
+ SetActive(false);
+
+ auto BID = GetBreakpointID();
+ if (BID == LLDB_INVALID_BREAK_ID)
+ return;
+
+ if (ProcessSP process_sp = GetProcessSP()) {
+ process_sp->GetTarget().RemoveBreakpointByID(BID);
+ SetBreakpointID(LLDB_INVALID_BREAK_ID);
+ }
+}
+
+lldb::ThreadCollectionSP
+UndefinedBehaviorSanitizerRuntime::GetBacktracesFromExtendedStopInfo(
+ StructuredData::ObjectSP info) {
+ ThreadCollectionSP threads;
+ threads = std::make_shared<ThreadCollection>();
+
+ ProcessSP process_sp = GetProcessSP();
+
+ if (info->GetObjectForDotSeparatedPath("instrumentation_class")
+ ->GetStringValue() != "UndefinedBehaviorSanitizer")
+ return threads;
+
+ std::vector<lldb::addr_t> PCs;
+ auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray();
+ trace->ForEach([&PCs](StructuredData::Object *PC) -> bool {
+ PCs.push_back(PC->GetAsInteger()->GetValue());
+ return true;
+ });
+
+ if (PCs.empty())
+ return threads;
+
+ StructuredData::ObjectSP thread_id_obj =
+ info->GetObjectForDotSeparatedPath("tid");
+ tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0;
+
+ HistoryThread *history_thread = new HistoryThread(*process_sp, tid, PCs);
+ ThreadSP new_thread_sp(history_thread);
+ std::string stop_reason_description = GetStopReasonDescription(info);
+ new_thread_sp->SetName(stop_reason_description.c_str());
+
+ // Save this in the Process' ExtendedThreadList so a strong pointer retains
+ // the object
+ process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
+ threads->AddThread(new_thread_sp);
+
+ return threads;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h
new file mode 100644
index 000000000000..1d854b7bf06f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h
@@ -0,0 +1,68 @@
+//===-- UBSanRuntime.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_UndefinedBehaviorSanitizerRuntime_h_
+#define liblldb_UndefinedBehaviorSanitizerRuntime_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/InstrumentationRuntime.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class UndefinedBehaviorSanitizerRuntime
+ : public lldb_private::InstrumentationRuntime {
+public:
+ ~UndefinedBehaviorSanitizerRuntime() override;
+
+ static lldb::InstrumentationRuntimeSP
+ CreateInstance(const lldb::ProcessSP &process_sp);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static lldb::InstrumentationRuntimeType GetTypeStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ lldb::ThreadCollectionSP
+ GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info) override;
+
+private:
+ UndefinedBehaviorSanitizerRuntime(const lldb::ProcessSP &process_sp)
+ : lldb_private::InstrumentationRuntime(process_sp) {}
+
+ const RegularExpression &GetPatternForRuntimeLibrary() override;
+
+ bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override;
+
+ void Activate() override;
+
+ void Deactivate();
+
+ static bool NotifyBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ StructuredData::ObjectSP RetrieveReportData(ExecutionContextRef exe_ctx_ref);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_UndefinedBehaviorSanitizerRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp b/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp
new file mode 100644
index 000000000000..140d09ed43cf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp
@@ -0,0 +1,474 @@
+//===-- JITLoaderGDB.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "llvm/Support/MathExtras.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "JITLoaderGDB.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Debug Interface Structures
+enum jit_actions_t { JIT_NOACTION = 0, JIT_REGISTER_FN, JIT_UNREGISTER_FN };
+
+template <typename ptr_t> struct jit_code_entry {
+ ptr_t next_entry; // pointer
+ ptr_t prev_entry; // pointer
+ ptr_t symfile_addr; // pointer
+ uint64_t symfile_size;
+};
+
+template <typename ptr_t> struct jit_descriptor {
+ uint32_t version;
+ uint32_t action_flag; // Values are jit_action_t
+ ptr_t relevant_entry; // pointer
+ ptr_t first_entry; // pointer
+};
+
+namespace {
+
+enum EnableJITLoaderGDB {
+ eEnableJITLoaderGDBDefault,
+ eEnableJITLoaderGDBOn,
+ eEnableJITLoaderGDBOff,
+};
+
+static constexpr OptionEnumValueElement g_enable_jit_loader_gdb_enumerators[] = {
+ {eEnableJITLoaderGDBDefault, "default", "Enable JIT compilation interface "
+ "for all platforms except macOS"},
+ {eEnableJITLoaderGDBOn, "on", "Enable JIT compilation interface"},
+ {eEnableJITLoaderGDBOff, "off", "Disable JIT compilation interface"}
+ };
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"enable", OptionValue::eTypeEnum, true,
+ eEnableJITLoaderGDBDefault, nullptr,
+ OptionEnumValues(g_enable_jit_loader_gdb_enumerators),
+ "Enable GDB's JIT compilation interface (default: enabled on "
+ "all platforms except macOS)"}};
+
+enum { ePropertyEnable, ePropertyEnableJITBreakpoint };
+
+class PluginProperties : public Properties {
+public:
+ static ConstString GetSettingName() {
+ return JITLoaderGDB::GetPluginNameStatic();
+ }
+
+ PluginProperties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_properties);
+ }
+
+ EnableJITLoaderGDB GetEnable() const {
+ return (EnableJITLoaderGDB)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, ePropertyEnable,
+ g_properties[ePropertyEnable].default_uint_value);
+ }
+};
+
+typedef std::shared_ptr<PluginProperties> JITLoaderGDBPropertiesSP;
+
+static const JITLoaderGDBPropertiesSP &GetGlobalPluginProperties() {
+ static const auto g_settings_sp(std::make_shared<PluginProperties>());
+ return g_settings_sp;
+}
+
+template <typename ptr_t>
+bool ReadJITEntry(const addr_t from_addr, Process *process,
+ jit_code_entry<ptr_t> *entry) {
+ lldbassert(from_addr % sizeof(ptr_t) == 0);
+
+ ArchSpec::Core core = process->GetTarget().GetArchitecture().GetCore();
+ bool i386_target = ArchSpec::kCore_x86_32_first <= core &&
+ core <= ArchSpec::kCore_x86_32_last;
+ uint8_t uint64_align_bytes = i386_target ? 4 : 8;
+ const size_t data_byte_size =
+ llvm::alignTo(sizeof(ptr_t) * 3, uint64_align_bytes) + sizeof(uint64_t);
+
+ Status error;
+ DataBufferHeap data(data_byte_size, 0);
+ size_t bytes_read = process->ReadMemory(from_addr, data.GetBytes(),
+ data.GetByteSize(), error);
+ if (bytes_read != data_byte_size || !error.Success())
+ return false;
+
+ DataExtractor extractor(data.GetBytes(), data.GetByteSize(),
+ process->GetByteOrder(), sizeof(ptr_t));
+ lldb::offset_t offset = 0;
+ entry->next_entry = extractor.GetPointer(&offset);
+ entry->prev_entry = extractor.GetPointer(&offset);
+ entry->symfile_addr = extractor.GetPointer(&offset);
+ offset = llvm::alignTo(offset, uint64_align_bytes);
+ entry->symfile_size = extractor.GetU64(&offset);
+
+ return true;
+}
+
+} // anonymous namespace end
+
+JITLoaderGDB::JITLoaderGDB(lldb_private::Process *process)
+ : JITLoader(process), m_jit_objects(),
+ m_jit_break_id(LLDB_INVALID_BREAK_ID),
+ m_jit_descriptor_addr(LLDB_INVALID_ADDRESS) {}
+
+JITLoaderGDB::~JITLoaderGDB() {
+ if (LLDB_BREAK_ID_IS_VALID(m_jit_break_id))
+ m_process->GetTarget().RemoveBreakpointByID(m_jit_break_id);
+}
+
+void JITLoaderGDB::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForJITLoaderPlugin(
+ debugger, PluginProperties::GetSettingName())) {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForJITLoaderPlugin(
+ debugger, GetGlobalPluginProperties()->GetValueProperties(),
+ ConstString("Properties for the JIT LoaderGDB plug-in."),
+ is_global_setting);
+ }
+}
+
+void JITLoaderGDB::DidAttach() {
+ Target &target = m_process->GetTarget();
+ ModuleList &module_list = target.GetImages();
+ SetJITBreakpoint(module_list);
+}
+
+void JITLoaderGDB::DidLaunch() {
+ Target &target = m_process->GetTarget();
+ ModuleList &module_list = target.GetImages();
+ SetJITBreakpoint(module_list);
+}
+
+void JITLoaderGDB::ModulesDidLoad(ModuleList &module_list) {
+ if (!DidSetJITBreakpoint() && m_process->IsAlive())
+ SetJITBreakpoint(module_list);
+}
+
+// Setup the JIT Breakpoint
+void JITLoaderGDB::SetJITBreakpoint(lldb_private::ModuleList &module_list) {
+ if (DidSetJITBreakpoint())
+ return;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_JIT_LOADER));
+ if (log)
+ log->Printf("JITLoaderGDB::%s looking for JIT register hook", __FUNCTION__);
+
+ addr_t jit_addr = GetSymbolAddress(
+ module_list, ConstString("__jit_debug_register_code"), eSymbolTypeAny);
+ if (jit_addr == LLDB_INVALID_ADDRESS)
+ return;
+
+ m_jit_descriptor_addr = GetSymbolAddress(
+ module_list, ConstString("__jit_debug_descriptor"), eSymbolTypeData);
+ if (m_jit_descriptor_addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("JITLoaderGDB::%s failed to find JIT descriptor address",
+ __FUNCTION__);
+ return;
+ }
+
+ if (log)
+ log->Printf("JITLoaderGDB::%s setting JIT breakpoint", __FUNCTION__);
+
+ Breakpoint *bp =
+ m_process->GetTarget().CreateBreakpoint(jit_addr, true, false).get();
+ bp->SetCallback(JITDebugBreakpointHit, this, true);
+ bp->SetBreakpointKind("jit-debug-register");
+ m_jit_break_id = bp->GetID();
+
+ ReadJITDescriptor(true);
+}
+
+bool JITLoaderGDB::JITDebugBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ user_id_t break_id,
+ user_id_t break_loc_id) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_JIT_LOADER));
+ if (log)
+ log->Printf("JITLoaderGDB::%s hit JIT breakpoint", __FUNCTION__);
+ JITLoaderGDB *instance = static_cast<JITLoaderGDB *>(baton);
+ return instance->ReadJITDescriptor(false);
+}
+
+static void updateSectionLoadAddress(const SectionList &section_list,
+ Target &target, uint64_t symbolfile_addr,
+ uint64_t symbolfile_size,
+ uint64_t &vmaddrheuristic,
+ uint64_t &min_addr, uint64_t &max_addr) {
+ const uint32_t num_sections = section_list.GetSize();
+ for (uint32_t i = 0; i < num_sections; ++i) {
+ SectionSP section_sp(section_list.GetSectionAtIndex(i));
+ if (section_sp) {
+ if (section_sp->IsFake()) {
+ uint64_t lower = (uint64_t)-1;
+ uint64_t upper = 0;
+ updateSectionLoadAddress(section_sp->GetChildren(), target,
+ symbolfile_addr, symbolfile_size,
+ vmaddrheuristic, lower, upper);
+ if (lower < min_addr)
+ min_addr = lower;
+ if (upper > max_addr)
+ max_addr = upper;
+ const lldb::addr_t slide_amount = lower - section_sp->GetFileAddress();
+ section_sp->Slide(slide_amount, false);
+ section_sp->GetChildren().Slide(-slide_amount, false);
+ section_sp->SetByteSize(upper - lower);
+ } else {
+ vmaddrheuristic += 2 << section_sp->GetLog2Align();
+ uint64_t lower;
+ if (section_sp->GetFileAddress() > vmaddrheuristic)
+ lower = section_sp->GetFileAddress();
+ else {
+ lower = symbolfile_addr + section_sp->GetFileOffset();
+ section_sp->SetFileAddress(symbolfile_addr +
+ section_sp->GetFileOffset());
+ }
+ target.SetSectionLoadAddress(section_sp, lower, true);
+ uint64_t upper = lower + section_sp->GetByteSize();
+ if (lower < min_addr)
+ min_addr = lower;
+ if (upper > max_addr)
+ max_addr = upper;
+ // This is an upper bound, but a good enough heuristic
+ vmaddrheuristic += section_sp->GetByteSize();
+ }
+ }
+ }
+}
+
+bool JITLoaderGDB::ReadJITDescriptor(bool all_entries) {
+ if (m_process->GetTarget().GetArchitecture().GetAddressByteSize() == 8)
+ return ReadJITDescriptorImpl<uint64_t>(all_entries);
+ else
+ return ReadJITDescriptorImpl<uint32_t>(all_entries);
+}
+
+template <typename ptr_t>
+bool JITLoaderGDB::ReadJITDescriptorImpl(bool all_entries) {
+ if (m_jit_descriptor_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_JIT_LOADER));
+ Target &target = m_process->GetTarget();
+ ModuleList &module_list = target.GetImages();
+
+ jit_descriptor<ptr_t> jit_desc;
+ const size_t jit_desc_size = sizeof(jit_desc);
+ Status error;
+ size_t bytes_read = m_process->DoReadMemory(m_jit_descriptor_addr, &jit_desc,
+ jit_desc_size, error);
+ if (bytes_read != jit_desc_size || !error.Success()) {
+ if (log)
+ log->Printf("JITLoaderGDB::%s failed to read JIT descriptor",
+ __FUNCTION__);
+ return false;
+ }
+
+ jit_actions_t jit_action = (jit_actions_t)jit_desc.action_flag;
+ addr_t jit_relevant_entry = (addr_t)jit_desc.relevant_entry;
+ if (all_entries) {
+ jit_action = JIT_REGISTER_FN;
+ jit_relevant_entry = (addr_t)jit_desc.first_entry;
+ }
+
+ while (jit_relevant_entry != 0) {
+ jit_code_entry<ptr_t> jit_entry;
+ if (!ReadJITEntry(jit_relevant_entry, m_process, &jit_entry)) {
+ if (log)
+ log->Printf("JITLoaderGDB::%s failed to read JIT entry at 0x%" PRIx64,
+ __FUNCTION__, jit_relevant_entry);
+ return false;
+ }
+
+ const addr_t &symbolfile_addr = (addr_t)jit_entry.symfile_addr;
+ const size_t &symbolfile_size = (size_t)jit_entry.symfile_size;
+ ModuleSP module_sp;
+
+ if (jit_action == JIT_REGISTER_FN) {
+ if (log)
+ log->Printf("JITLoaderGDB::%s registering JIT entry at 0x%" PRIx64
+ " (%" PRIu64 " bytes)",
+ __FUNCTION__, symbolfile_addr, (uint64_t)symbolfile_size);
+
+ char jit_name[64];
+ snprintf(jit_name, 64, "JIT(0x%" PRIx64 ")", symbolfile_addr);
+ module_sp = m_process->ReadModuleFromMemory(
+ FileSpec(jit_name), symbolfile_addr, symbolfile_size);
+
+ if (module_sp && module_sp->GetObjectFile()) {
+ // Object formats (like ELF) have no representation for a JIT type.
+ // We will get it wrong, if we deduce it from the header.
+ module_sp->GetObjectFile()->SetType(ObjectFile::eTypeJIT);
+
+ // load the symbol table right away
+ module_sp->GetObjectFile()->GetSymtab();
+
+ m_jit_objects.insert(std::make_pair(symbolfile_addr, module_sp));
+ if (module_sp->GetObjectFile()->GetPluginName() ==
+ ConstString("mach-o")) {
+ ObjectFile *image_object_file = module_sp->GetObjectFile();
+ if (image_object_file) {
+ const SectionList *section_list =
+ image_object_file->GetSectionList();
+ if (section_list) {
+ uint64_t vmaddrheuristic = 0;
+ uint64_t lower = (uint64_t)-1;
+ uint64_t upper = 0;
+ updateSectionLoadAddress(*section_list, target, symbolfile_addr,
+ symbolfile_size, vmaddrheuristic, lower,
+ upper);
+ }
+ }
+ } else {
+ bool changed = false;
+ module_sp->SetLoadAddress(target, 0, true, changed);
+ }
+
+ module_list.AppendIfNeeded(module_sp);
+
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ target.ModulesDidLoad(module_list);
+ } else {
+ if (log)
+ log->Printf("JITLoaderGDB::%s failed to load module for "
+ "JIT entry at 0x%" PRIx64,
+ __FUNCTION__, symbolfile_addr);
+ }
+ } else if (jit_action == JIT_UNREGISTER_FN) {
+ if (log)
+ log->Printf("JITLoaderGDB::%s unregistering JIT entry at 0x%" PRIx64,
+ __FUNCTION__, symbolfile_addr);
+
+ JITObjectMap::iterator it = m_jit_objects.find(symbolfile_addr);
+ if (it != m_jit_objects.end()) {
+ module_sp = it->second;
+ ObjectFile *image_object_file = module_sp->GetObjectFile();
+ if (image_object_file) {
+ const SectionList *section_list = image_object_file->GetSectionList();
+ if (section_list) {
+ const uint32_t num_sections = section_list->GetSize();
+ for (uint32_t i = 0; i < num_sections; ++i) {
+ SectionSP section_sp(section_list->GetSectionAtIndex(i));
+ if (section_sp) {
+ target.GetSectionLoadList().SetSectionUnloaded(section_sp);
+ }
+ }
+ }
+ }
+ module_list.Remove(module_sp);
+ m_jit_objects.erase(it);
+ }
+ } else if (jit_action == JIT_NOACTION) {
+ // Nothing to do
+ } else {
+ assert(false && "Unknown jit action");
+ }
+
+ if (all_entries)
+ jit_relevant_entry = (addr_t)jit_entry.next_entry;
+ else
+ jit_relevant_entry = 0;
+ }
+
+ return false; // Continue Running.
+}
+
+// PluginInterface protocol
+lldb_private::ConstString JITLoaderGDB::GetPluginNameStatic() {
+ static ConstString g_name("gdb");
+ return g_name;
+}
+
+JITLoaderSP JITLoaderGDB::CreateInstance(Process *process, bool force) {
+ JITLoaderSP jit_loader_sp;
+ bool enable;
+ switch (GetGlobalPluginProperties()->GetEnable()) {
+ case EnableJITLoaderGDB::eEnableJITLoaderGDBOn:
+ enable = true;
+ break;
+ case EnableJITLoaderGDB::eEnableJITLoaderGDBOff:
+ enable = false;
+ break;
+ case EnableJITLoaderGDB::eEnableJITLoaderGDBDefault:
+ ArchSpec arch(process->GetTarget().GetArchitecture());
+ enable = arch.GetTriple().getVendor() != llvm::Triple::Apple;
+ break;
+ }
+ if (enable)
+ jit_loader_sp = std::make_shared<JITLoaderGDB>(process);
+ return jit_loader_sp;
+}
+
+const char *JITLoaderGDB::GetPluginDescriptionStatic() {
+ return "JIT loader plug-in that watches for JIT events using the GDB "
+ "interface.";
+}
+
+lldb_private::ConstString JITLoaderGDB::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t JITLoaderGDB::GetPluginVersion() { return 1; }
+
+void JITLoaderGDB::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+}
+
+void JITLoaderGDB::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+bool JITLoaderGDB::DidSetJITBreakpoint() const {
+ return LLDB_BREAK_ID_IS_VALID(m_jit_break_id);
+}
+
+addr_t JITLoaderGDB::GetSymbolAddress(ModuleList &module_list,
+ ConstString name,
+ SymbolType symbol_type) const {
+ SymbolContextList target_symbols;
+ Target &target = m_process->GetTarget();
+
+ if (!module_list.FindSymbolsWithNameAndType(name, symbol_type,
+ target_symbols))
+ return LLDB_INVALID_ADDRESS;
+
+ SymbolContext sym_ctx;
+ target_symbols.GetContextAtIndex(0, sym_ctx);
+
+ const Address jit_descriptor_addr = sym_ctx.symbol->GetAddress();
+ if (!jit_descriptor_addr.IsValid())
+ return LLDB_INVALID_ADDRESS;
+
+ const addr_t jit_addr = jit_descriptor_addr.GetLoadAddress(&target);
+ return jit_addr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.h b/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.h
new file mode 100644
index 000000000000..2a2537c3edd4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.h
@@ -0,0 +1,80 @@
+//===-- JITLoaderGDB.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_JITLoaderGDB_h_
+#define liblldb_JITLoaderGDB_h_
+
+#include <map>
+
+#include "lldb/Target/JITLoader.h"
+#include "lldb/Target/Process.h"
+
+class JITLoaderGDB : public lldb_private::JITLoader {
+public:
+ JITLoaderGDB(lldb_private::Process *process);
+
+ ~JITLoaderGDB() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb::JITLoaderSP CreateInstance(lldb_private::Process *process,
+ bool force);
+
+ static void DebuggerInitialize(lldb_private::Debugger &debugger);
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // JITLoader interface
+ void DidAttach() override;
+
+ void DidLaunch() override;
+
+ void ModulesDidLoad(lldb_private::ModuleList &module_list) override;
+
+private:
+ lldb::addr_t GetSymbolAddress(lldb_private::ModuleList &module_list,
+ lldb_private::ConstString name,
+ lldb::SymbolType symbol_type) const;
+
+ void SetJITBreakpoint(lldb_private::ModuleList &module_list);
+
+ bool DidSetJITBreakpoint() const;
+
+ bool ReadJITDescriptor(bool all_entries);
+
+ template <typename ptr_t> bool ReadJITDescriptorImpl(bool all_entries);
+
+ static bool
+ JITDebugBreakpointHit(void *baton,
+ lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ static void ProcessStateChangedCallback(void *baton,
+ lldb_private::Process *process,
+ lldb::StateType state);
+
+ // A collection of in-memory jitted object addresses and their corresponding
+ // modules
+ typedef std::map<lldb::addr_t, const lldb::ModuleSP> JITObjectMap;
+ JITObjectMap m_jit_objects;
+
+ lldb::user_id_t m_jit_break_id;
+ lldb::addr_t m_jit_descriptor_addr;
+};
+
+#endif // liblldb_JITLoaderGDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp
new file mode 100644
index 000000000000..87b5b5947f35
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp
@@ -0,0 +1,205 @@
+//===-- BlockPointer.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "BlockPointer.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Target.h"
+
+#include "lldb/Utility/LLDBAssert.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace lldb_private {
+namespace formatters {
+
+class BlockPointerSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ BlockPointerSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_block_struct_type() {
+ CompilerType block_pointer_type(m_backend.GetCompilerType());
+ CompilerType function_pointer_type;
+ block_pointer_type.IsBlockPointerType(&function_pointer_type);
+
+ TargetSP target_sp(m_backend.GetTargetSP());
+
+ if (!target_sp) {
+ return;
+ }
+
+ Status err;
+ TypeSystem *type_system = target_sp->GetScratchTypeSystemForLanguage(
+ &err, lldb::eLanguageTypeC_plus_plus);
+
+ if (!err.Success() || !type_system) {
+ return;
+ }
+
+ ClangASTContext *clang_ast_context =
+ llvm::dyn_cast<ClangASTContext>(type_system);
+
+ if (!clang_ast_context) {
+ return;
+ }
+
+ ClangASTImporterSP clang_ast_importer = target_sp->GetClangASTImporter();
+
+ if (!clang_ast_importer) {
+ return;
+ }
+
+ const char *const isa_name("__isa");
+ const CompilerType isa_type =
+ clang_ast_context->GetBasicType(lldb::eBasicTypeObjCClass);
+ const char *const flags_name("__flags");
+ const CompilerType flags_type =
+ clang_ast_context->GetBasicType(lldb::eBasicTypeInt);
+ const char *const reserved_name("__reserved");
+ const CompilerType reserved_type =
+ clang_ast_context->GetBasicType(lldb::eBasicTypeInt);
+ const char *const FuncPtr_name("__FuncPtr");
+ const CompilerType FuncPtr_type =
+ clang_ast_importer->CopyType(*clang_ast_context, function_pointer_type);
+
+ m_block_struct_type = clang_ast_context->CreateStructForIdentifier(
+ ConstString(), {{isa_name, isa_type},
+ {flags_name, flags_type},
+ {reserved_name, reserved_type},
+ {FuncPtr_name, FuncPtr_type}});
+ }
+
+ ~BlockPointerSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override {
+ const bool omit_empty_base_classes = false;
+ return m_block_struct_type.GetNumChildren(omit_empty_base_classes, nullptr);
+ }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ if (!m_block_struct_type.IsValid()) {
+ return lldb::ValueObjectSP();
+ }
+
+ if (idx >= CalculateNumChildren()) {
+ return lldb::ValueObjectSP();
+ }
+
+ const bool thread_and_frame_only_if_stopped = true;
+ ExecutionContext exe_ctx = m_backend.GetExecutionContextRef().Lock(
+ thread_and_frame_only_if_stopped);
+ const bool transparent_pointers = false;
+ const bool omit_empty_base_classes = false;
+ const bool ignore_array_bounds = false;
+ ValueObject *value_object = nullptr;
+
+ std::string child_name;
+ uint32_t child_byte_size = 0;
+ int32_t child_byte_offset = 0;
+ uint32_t child_bitfield_bit_size = 0;
+ uint32_t child_bitfield_bit_offset = 0;
+ bool child_is_base_class = false;
+ bool child_is_deref_of_parent = false;
+ uint64_t language_flags = 0;
+
+ const CompilerType child_type =
+ m_block_struct_type.GetChildCompilerTypeAtIndex(
+ &exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, child_is_deref_of_parent, value_object,
+ language_flags);
+
+ ValueObjectSP struct_pointer_sp =
+ m_backend.Cast(m_block_struct_type.GetPointerType());
+
+ if (!struct_pointer_sp) {
+ return lldb::ValueObjectSP();
+ }
+
+ Status err;
+ ValueObjectSP struct_sp = struct_pointer_sp->Dereference(err);
+
+ if (!struct_sp || !err.Success()) {
+ return lldb::ValueObjectSP();
+ }
+
+ ValueObjectSP child_sp(struct_sp->GetSyntheticChildAtOffset(
+ child_byte_offset, child_type, true,
+ ConstString(child_name.c_str(), child_name.size())));
+
+ return child_sp;
+ }
+
+ // return true if this object is now safe to use forever without ever
+ // updating again; the typical (and tested) answer here is 'false'
+ bool Update() override { return false; }
+
+ // maybe return false if the block pointer is, say, null
+ bool MightHaveChildren() override { return true; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ if (!m_block_struct_type.IsValid())
+ return UINT32_MAX;
+
+ const bool omit_empty_base_classes = false;
+ return m_block_struct_type.GetIndexOfChildWithName(name.AsCString(),
+ omit_empty_base_classes);
+ }
+
+private:
+ CompilerType m_block_struct_type;
+};
+
+} // namespace formatters
+} // namespace lldb_private
+
+bool lldb_private::formatters::BlockPointerSummaryProvider(
+ ValueObject &valobj, Stream &s, const TypeSummaryOptions &) {
+ lldb_private::SyntheticChildrenFrontEnd *synthetic_children =
+ BlockPointerSyntheticFrontEndCreator(nullptr, valobj.GetSP());
+ if (!synthetic_children) {
+ return false;
+ }
+
+ synthetic_children->Update();
+
+ static const ConstString s_FuncPtr_name("__FuncPtr");
+
+ lldb::ValueObjectSP child_sp = synthetic_children->GetChildAtIndex(
+ synthetic_children->GetIndexOfChildWithName(s_FuncPtr_name));
+
+ if (!child_sp) {
+ return false;
+ }
+
+ lldb::ValueObjectSP qualified_child_representation_sp =
+ child_sp->GetQualifiedRepresentationIfAvailable(
+ lldb::eDynamicDontRunTarget, true);
+
+ const char *child_value =
+ qualified_child_representation_sp->GetValueAsCString();
+
+ s.Printf("%s", child_value);
+
+ return true;
+}
+
+lldb_private::SyntheticChildrenFrontEnd *
+lldb_private::formatters::BlockPointerSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+ return new BlockPointerSyntheticFrontEnd(valobj_sp);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h
new file mode 100644
index 000000000000..624c17a6a6af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h
@@ -0,0 +1,25 @@
+//===-- BlockPointer.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_BlockPointer_h_
+#define liblldb_BlockPointer_h_
+
+#include "lldb/lldb-forward.h"
+
+namespace lldb_private {
+namespace formatters {
+bool BlockPointerSummaryProvider(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+SyntheticChildrenFrontEnd *
+BlockPointerSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_BlockPointer_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
new file mode 100644
index 000000000000..44b9e5e24ccd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -0,0 +1,1071 @@
+//===-- CPlusPlusLanguage.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CPlusPlusLanguage.h"
+
+#include <cctype>
+#include <cstring>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <set>
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Demangle/ItaniumDemangle.h"
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/DataFormatters/CXXFunctionPointer.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/VectorType.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+
+#include "BlockPointer.h"
+#include "CPlusPlusNameParser.h"
+#include "CxxStringTypes.h"
+#include "LibCxx.h"
+#include "LibCxxAtomic.h"
+#include "LibCxxVariant.h"
+#include "LibStdcpp.h"
+#include "MSVCUndecoratedNameParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+void CPlusPlusLanguage::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(), "C++ Language",
+ CreateInstance);
+}
+
+void CPlusPlusLanguage::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString CPlusPlusLanguage::GetPluginNameStatic() {
+ static ConstString g_name("cplusplus");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString CPlusPlusLanguage::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t CPlusPlusLanguage::GetPluginVersion() { return 1; }
+
+// Static Functions
+
+Language *CPlusPlusLanguage::CreateInstance(lldb::LanguageType language) {
+ if (Language::LanguageIsCPlusPlus(language))
+ return new CPlusPlusLanguage();
+ return nullptr;
+}
+
+void CPlusPlusLanguage::MethodName::Clear() {
+ m_full.Clear();
+ m_basename = llvm::StringRef();
+ m_context = llvm::StringRef();
+ m_arguments = llvm::StringRef();
+ m_qualifiers = llvm::StringRef();
+ m_parsed = false;
+ m_parse_error = false;
+}
+
+static bool ReverseFindMatchingChars(const llvm::StringRef &s,
+ const llvm::StringRef &left_right_chars,
+ size_t &left_pos, size_t &right_pos,
+ size_t pos = llvm::StringRef::npos) {
+ assert(left_right_chars.size() == 2);
+ left_pos = llvm::StringRef::npos;
+ const char left_char = left_right_chars[0];
+ const char right_char = left_right_chars[1];
+ pos = s.find_last_of(left_right_chars, pos);
+ if (pos == llvm::StringRef::npos || s[pos] == left_char)
+ return false;
+ right_pos = pos;
+ uint32_t depth = 1;
+ while (pos > 0 && depth > 0) {
+ pos = s.find_last_of(left_right_chars, pos);
+ if (pos == llvm::StringRef::npos)
+ return false;
+ if (s[pos] == left_char) {
+ if (--depth == 0) {
+ left_pos = pos;
+ return left_pos < right_pos;
+ }
+ } else if (s[pos] == right_char) {
+ ++depth;
+ }
+ }
+ return false;
+}
+
+static bool IsTrivialBasename(const llvm::StringRef &basename) {
+ // Check that the basename matches with the following regular expression
+ // "^~?([A-Za-z_][A-Za-z_0-9]*)$" We are using a hand written implementation
+ // because it is significantly more efficient then using the general purpose
+ // regular expression library.
+ size_t idx = 0;
+ if (basename.size() > 0 && basename[0] == '~')
+ idx = 1;
+
+ if (basename.size() <= idx)
+ return false; // Empty string or "~"
+
+ if (!std::isalpha(basename[idx]) && basename[idx] != '_')
+ return false; // First charater (after removing the possible '~'') isn't in
+ // [A-Za-z_]
+
+ // Read all characters matching [A-Za-z_0-9]
+ ++idx;
+ while (idx < basename.size()) {
+ if (!std::isalnum(basename[idx]) && basename[idx] != '_')
+ break;
+ ++idx;
+ }
+
+ // We processed all characters. It is a vaild basename.
+ return idx == basename.size();
+}
+
+bool CPlusPlusLanguage::MethodName::TrySimplifiedParse() {
+ // This method tries to parse simple method definitions which are presumably
+ // most comman in user programs. Definitions that can be parsed by this
+ // function don't have return types and templates in the name.
+ // A::B::C::fun(std::vector<T> &) const
+ size_t arg_start, arg_end;
+ llvm::StringRef full(m_full.GetCString());
+ llvm::StringRef parens("()", 2);
+ if (ReverseFindMatchingChars(full, parens, arg_start, arg_end)) {
+ m_arguments = full.substr(arg_start, arg_end - arg_start + 1);
+ if (arg_end + 1 < full.size())
+ m_qualifiers = full.substr(arg_end + 1).ltrim();
+
+ if (arg_start == 0)
+ return false;
+ size_t basename_end = arg_start;
+ size_t context_start = 0;
+ size_t context_end = full.rfind(':', basename_end);
+ if (context_end == llvm::StringRef::npos)
+ m_basename = full.substr(0, basename_end);
+ else {
+ if (context_start < context_end)
+ m_context = full.substr(context_start, context_end - 1 - context_start);
+ const size_t basename_begin = context_end + 1;
+ m_basename = full.substr(basename_begin, basename_end - basename_begin);
+ }
+
+ if (IsTrivialBasename(m_basename)) {
+ return true;
+ } else {
+ // The C++ basename doesn't match our regular expressions so this can't
+ // be a valid C++ method, clear everything out and indicate an error
+ m_context = llvm::StringRef();
+ m_basename = llvm::StringRef();
+ m_arguments = llvm::StringRef();
+ m_qualifiers = llvm::StringRef();
+ return false;
+ }
+ }
+ return false;
+}
+
+void CPlusPlusLanguage::MethodName::Parse() {
+ if (!m_parsed && m_full) {
+ if (TrySimplifiedParse()) {
+ m_parse_error = false;
+ } else {
+ CPlusPlusNameParser parser(m_full.GetStringRef());
+ if (auto function = parser.ParseAsFunctionDefinition()) {
+ m_basename = function.getValue().name.basename;
+ m_context = function.getValue().name.context;
+ m_arguments = function.getValue().arguments;
+ m_qualifiers = function.getValue().qualifiers;
+ m_parse_error = false;
+ } else {
+ m_parse_error = true;
+ }
+ }
+ m_parsed = true;
+ }
+}
+
+llvm::StringRef CPlusPlusLanguage::MethodName::GetBasename() {
+ if (!m_parsed)
+ Parse();
+ return m_basename;
+}
+
+llvm::StringRef CPlusPlusLanguage::MethodName::GetContext() {
+ if (!m_parsed)
+ Parse();
+ return m_context;
+}
+
+llvm::StringRef CPlusPlusLanguage::MethodName::GetArguments() {
+ if (!m_parsed)
+ Parse();
+ return m_arguments;
+}
+
+llvm::StringRef CPlusPlusLanguage::MethodName::GetQualifiers() {
+ if (!m_parsed)
+ Parse();
+ return m_qualifiers;
+}
+
+std::string CPlusPlusLanguage::MethodName::GetScopeQualifiedName() {
+ if (!m_parsed)
+ Parse();
+ if (m_context.empty())
+ return m_basename;
+
+ std::string res;
+ res += m_context;
+ res += "::";
+ res += m_basename;
+ return res;
+}
+
+bool CPlusPlusLanguage::IsCPPMangledName(const char *name) {
+ // FIXME!! we should really run through all the known C++ Language plugins
+ // and ask each one if this is a C++ mangled name
+
+ if (name == nullptr)
+ return false;
+
+ // MSVC style mangling
+ if (name[0] == '?')
+ return true;
+
+ return (name[0] != '\0' && name[0] == '_' && name[1] == 'Z');
+}
+
+bool CPlusPlusLanguage::ExtractContextAndIdentifier(
+ const char *name, llvm::StringRef &context, llvm::StringRef &identifier) {
+ if (MSVCUndecoratedNameParser::IsMSVCUndecoratedName(name))
+ return MSVCUndecoratedNameParser::ExtractContextAndIdentifier(name, context,
+ identifier);
+
+ CPlusPlusNameParser parser(name);
+ if (auto full_name = parser.ParseAsFullName()) {
+ identifier = full_name.getValue().basename;
+ context = full_name.getValue().context;
+ return true;
+ }
+ return false;
+}
+
+namespace {
+class NodeAllocator {
+ llvm::BumpPtrAllocator Alloc;
+
+public:
+ void reset() { Alloc.Reset(); }
+
+ template <typename T, typename... Args> T *makeNode(Args &&... args) {
+ return new (Alloc.Allocate(sizeof(T), alignof(T)))
+ T(std::forward<Args>(args)...);
+ }
+
+ void *allocateNodeArray(size_t sz) {
+ return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz,
+ alignof(llvm::itanium_demangle::Node *));
+ }
+};
+
+/// Given a mangled function `Mangled`, replace all the primitive function type
+/// arguments of `Search` with type `Replace`.
+class TypeSubstitutor
+ : public llvm::itanium_demangle::AbstractManglingParser<TypeSubstitutor,
+ NodeAllocator> {
+ /// Input character until which we have constructed the respective output
+ /// already
+ const char *Written;
+
+ llvm::StringRef Search;
+ llvm::StringRef Replace;
+ llvm::SmallString<128> Result;
+
+ /// Whether we have performed any substitutions.
+ bool Substituted;
+
+ void reset(llvm::StringRef Mangled, llvm::StringRef Search,
+ llvm::StringRef Replace) {
+ AbstractManglingParser::reset(Mangled.begin(), Mangled.end());
+ Written = Mangled.begin();
+ this->Search = Search;
+ this->Replace = Replace;
+ Result.clear();
+ Substituted = false;
+ }
+
+ void appendUnchangedInput() {
+ Result += llvm::StringRef(Written, First - Written);
+ Written = First;
+ }
+
+public:
+ TypeSubstitutor() : AbstractManglingParser(nullptr, nullptr) {}
+
+ ConstString substitute(llvm::StringRef Mangled, llvm::StringRef From,
+ llvm::StringRef To) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ reset(Mangled, From, To);
+ if (parse() == nullptr) {
+ LLDB_LOG(log, "Failed to substitute mangling in {0}", Mangled);
+ return ConstString();
+ }
+ if (!Substituted)
+ return ConstString();
+
+ // Append any trailing unmodified input.
+ appendUnchangedInput();
+ LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result);
+ return ConstString(Result);
+ }
+
+ llvm::itanium_demangle::Node *parseType() {
+ if (llvm::StringRef(First, numLeft()).startswith(Search)) {
+ // We found a match. Append unmodified input up to this point.
+ appendUnchangedInput();
+
+ // And then perform the replacement.
+ Result += Replace;
+ Written += Search.size();
+ Substituted = true;
+ }
+ return AbstractManglingParser::parseType();
+ }
+};
+}
+
+uint32_t CPlusPlusLanguage::FindAlternateFunctionManglings(
+ const ConstString mangled_name, std::set<ConstString> &alternates) {
+ const auto start_size = alternates.size();
+ /// Get a basic set of alternative manglings for the given symbol `name`, by
+ /// making a few basic possible substitutions on basic types, storage duration
+ /// and `const`ness for the given symbol. The output parameter `alternates`
+ /// is filled with a best-guess, non-exhaustive set of different manglings
+ /// for the given name.
+
+ // Maybe we're looking for a const symbol but the debug info told us it was
+ // non-const...
+ if (!strncmp(mangled_name.GetCString(), "_ZN", 3) &&
+ strncmp(mangled_name.GetCString(), "_ZNK", 4)) {
+ std::string fixed_scratch("_ZNK");
+ fixed_scratch.append(mangled_name.GetCString() + 3);
+ alternates.insert(ConstString(fixed_scratch));
+ }
+
+ // Maybe we're looking for a static symbol but we thought it was global...
+ if (!strncmp(mangled_name.GetCString(), "_Z", 2) &&
+ strncmp(mangled_name.GetCString(), "_ZL", 3)) {
+ std::string fixed_scratch("_ZL");
+ fixed_scratch.append(mangled_name.GetCString() + 2);
+ alternates.insert(ConstString(fixed_scratch));
+ }
+
+ TypeSubstitutor TS;
+ // `char` is implementation defined as either `signed` or `unsigned`. As a
+ // result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed
+ // char, 'h'-unsigned char. If we're looking for symbols with a signed char
+ // parameter, try finding matches which have the general case 'c'.
+ if (ConstString char_fixup =
+ TS.substitute(mangled_name.GetStringRef(), "a", "c"))
+ alternates.insert(char_fixup);
+
+ // long long parameter mangling 'x', may actually just be a long 'l' argument
+ if (ConstString long_fixup =
+ TS.substitute(mangled_name.GetStringRef(), "x", "l"))
+ alternates.insert(long_fixup);
+
+ // unsigned long long parameter mangling 'y', may actually just be unsigned
+ // long 'm' argument
+ if (ConstString ulong_fixup =
+ TS.substitute(mangled_name.GetStringRef(), "y", "m"))
+ alternates.insert(ulong_fixup);
+
+ return alternates.size() - start_size;
+}
+
+static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
+ if (!cpp_category_sp)
+ return;
+
+ TypeSummaryImpl::Flags stl_summary_flags;
+ stl_summary_flags.SetCascades(true)
+ .SetSkipPointers(false)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(true)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxStringSummaryProviderASCII,
+ "std::string summary provider",
+ ConstString("^std::__[[:alnum:]]+::string$"), stl_summary_flags,
+ true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxStringSummaryProviderASCII,
+ "std::string summary provider",
+ ConstString("^std::__[[:alnum:]]+::basic_string<char, "
+ "std::__[[:alnum:]]+::char_traits<char>, "
+ "std::__[[:alnum:]]+::allocator<char> >$"),
+ stl_summary_flags, true);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxStringSummaryProviderUTF16,
+ "std::u16string summary provider",
+ ConstString(
+ "^std::__[[:alnum:]]+::basic_string<char16_t, "
+ "std::__[[:alnum:]]+::char_traits<char16_t>, "
+ "std::__[[:alnum:]]+::allocator<char16_t> >$"),
+ stl_summary_flags, true);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxStringSummaryProviderUTF32,
+ "std::u32string summary provider",
+ ConstString(
+ "^std::__[[:alnum:]]+::basic_string<char32_t, "
+ "std::__[[:alnum:]]+::char_traits<char32_t>, "
+ "std::__[[:alnum:]]+::allocator<char32_t> >$"),
+ stl_summary_flags, true);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxWStringSummaryProvider,
+ "std::wstring summary provider",
+ ConstString("^std::__[[:alnum:]]+::wstring$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxWStringSummaryProvider,
+ "std::wstring summary provider",
+ ConstString("^std::__[[:alnum:]]+::basic_string<wchar_t, "
+ "std::__[[:alnum:]]+::char_traits<wchar_t>, "
+ "std::__[[:alnum:]]+::allocator<wchar_t> >$"),
+ stl_summary_flags, true);
+
+ SyntheticChildren::Flags stl_synth_flags;
+ stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
+ false);
+ SyntheticChildren::Flags stl_deref_flags = stl_synth_flags;
+ stl_deref_flags.SetFrontEndWantsDereference();
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxBitsetSyntheticFrontEndCreator,
+ "libc++ std::bitset synthetic children",
+ ConstString("^std::__[[:alnum:]]+::bitset<.+>(( )?&)?$"), stl_deref_flags,
+ true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator,
+ "libc++ std::vector synthetic children",
+ ConstString("^std::__[[:alnum:]]+::vector<.+>(( )?&)?$"), stl_deref_flags,
+ true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdForwardListSyntheticFrontEndCreator,
+ "libc++ std::forward_list synthetic children",
+ ConstString("^std::__[[:alnum:]]+::forward_list<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator,
+ "libc++ std::list synthetic children",
+ ConstString("^std::__[[:alnum:]]+::list<.+>(( )?&)?$"), stl_deref_flags,
+ true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
+ "libc++ std::map synthetic children",
+ ConstString("^std::__[[:alnum:]]+::map<.+> >(( )?&)?$"), stl_synth_flags,
+ true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
+ "libc++ std::set synthetic children",
+ ConstString("^std::__[[:alnum:]]+::set<.+> >(( )?&)?$"), stl_deref_flags,
+ true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
+ "libc++ std::multiset synthetic children",
+ ConstString("^std::__[[:alnum:]]+::multiset<.+> >(( )?&)?$"),
+ stl_deref_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
+ "libc++ std::multimap synthetic children",
+ ConstString("^std::__[[:alnum:]]+::multimap<.+> >(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator,
+ "libc++ std::unordered containers synthetic children",
+ ConstString("^(std::__[[:alnum:]]+::)unordered_(multi)?(map|set)<.+> >$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator,
+ "libc++ std::initializer_list synthetic children",
+ ConstString("^std::initializer_list<.+>(( )?&)?$"), stl_synth_flags,
+ true);
+ AddCXXSynthetic(cpp_category_sp, LibcxxQueueFrontEndCreator,
+ "libc++ std::queue synthetic children",
+ ConstString("^std::__[[:alnum:]]+::queue<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(cpp_category_sp, LibcxxTupleFrontEndCreator,
+ "libc++ std::tuple synthetic children",
+ ConstString("^std::__[[:alnum:]]+::tuple<.*>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(cpp_category_sp, LibcxxOptionalFrontEndCreator,
+ "libc++ std::optional synthetic children",
+ ConstString("^std::__[[:alnum:]]+::optional<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(cpp_category_sp, LibcxxVariantFrontEndCreator,
+ "libc++ std::variant synthetic children",
+ ConstString("^std::__[[:alnum:]]+::variant<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator,
+ "libc++ std::atomic synthetic children",
+ ConstString("^std::__[[:alnum:]]+::atomic<.+>$"), stl_synth_flags, true);
+
+ cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
+ RegularExpressionSP(new RegularExpression(
+ llvm::StringRef("^(std::__[[:alnum:]]+::)deque<.+>(( )?&)?$"))),
+ SyntheticChildrenSP(new ScriptedSyntheticChildren(
+ stl_synth_flags,
+ "lldb.formatters.cpp.libcxx.stddeque_SynthProvider")));
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator,
+ "shared_ptr synthetic children",
+ ConstString("^(std::__[[:alnum:]]+::)shared_ptr<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator,
+ "weak_ptr synthetic children",
+ ConstString("^(std::__[[:alnum:]]+::)weak_ptr<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::LibcxxFunctionSummaryProvider,
+ "libc++ std::function summary provider",
+ ConstString("^std::__[[:alnum:]]+::function<.+>$"), stl_summary_flags,
+ true);
+
+ stl_summary_flags.SetDontShowChildren(false);
+ stl_summary_flags.SetSkipPointers(false);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::bitset summary provider",
+ ConstString("^std::__[[:alnum:]]+::bitset<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::vector summary provider",
+ ConstString("^std::__[[:alnum:]]+::vector<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::list summary provider",
+ ConstString("^std::__[[:alnum:]]+::forward_list<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::list summary provider",
+ ConstString("^std::__[[:alnum:]]+::list<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::map summary provider",
+ ConstString("^std::__[[:alnum:]]+::map<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::deque summary provider",
+ ConstString("^std::__[[:alnum:]]+::deque<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::queue summary provider",
+ ConstString("^std::__[[:alnum:]]+::queue<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::set summary provider",
+ ConstString("^std::__[[:alnum:]]+::set<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::multiset summary provider",
+ ConstString("^std::__[[:alnum:]]+::multiset<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::multimap summary provider",
+ ConstString("^std::__[[:alnum:]]+::multimap<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::unordered containers summary provider",
+ ConstString("^(std::__[[:alnum:]]+::)unordered_(multi)?(map|set)<.+> >$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp, LibcxxContainerSummaryProvider,
+ "libc++ std::tuple summary provider",
+ ConstString("^std::__[[:alnum:]]+::tuple<.*>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::LibCxxAtomicSummaryProvider,
+ "libc++ std::atomic summary provider",
+ ConstString("^std::__[[:alnum:]]+::atomic<.+>$"), stl_summary_flags,
+ true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxOptionalSummaryProvider,
+ "libc++ std::optional summary provider",
+ ConstString("^std::__[[:alnum:]]+::optional<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxVariantSummaryProvider,
+ "libc++ std::variant summary provider",
+ ConstString("^std::__[[:alnum:]]+::variant<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+
+ stl_summary_flags.SetSkipPointers(true);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxSmartPointerSummaryProvider,
+ "libc++ std::shared_ptr summary provider",
+ ConstString("^std::__[[:alnum:]]+::shared_ptr<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxSmartPointerSummaryProvider,
+ "libc++ std::weak_ptr summary provider",
+ ConstString("^std::__[[:alnum:]]+::weak_ptr<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator,
+ "std::vector iterator synthetic children",
+ ConstString("^std::__[[:alnum:]]+::__wrap_iter<.+>$"), stl_synth_flags,
+ true);
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator,
+ "std::map iterator synthetic children",
+ ConstString("^std::__[[:alnum:]]+::__map_iterator<.+>$"), stl_synth_flags,
+ true);
+}
+
+static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
+ if (!cpp_category_sp)
+ return;
+
+ TypeSummaryImpl::Flags stl_summary_flags;
+ stl_summary_flags.SetCascades(true)
+ .SetSkipPointers(false)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(true)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ lldb::TypeSummaryImplSP std_string_summary_sp(
+ new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p}"));
+
+ lldb::TypeSummaryImplSP cxx11_string_summary_sp(new CXXFunctionSummaryFormat(
+ stl_summary_flags, LibStdcppStringSummaryProvider,
+ "libstdc++ c++11 std::string summary provider"));
+ lldb::TypeSummaryImplSP cxx11_wstring_summary_sp(new CXXFunctionSummaryFormat(
+ stl_summary_flags, LibStdcppWStringSummaryProvider,
+ "libstdc++ c++11 std::wstring summary provider"));
+
+ cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::string"),
+ std_string_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<char>"), std_string_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<char,std::char_traits<char>,std::"
+ "allocator<char> >"),
+ std_string_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<char, std::char_traits<char>, "
+ "std::allocator<char> >"),
+ std_string_summary_sp);
+
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::__cxx11::string"), cxx11_string_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::__cxx11::basic_string<char, std::char_traits<char>, "
+ "std::allocator<char> >"),
+ cxx11_string_summary_sp);
+
+ // making sure we force-pick the summary for printing wstring (_M_p is a
+ // wchar_t*)
+ lldb::TypeSummaryImplSP std_wstring_summary_sp(
+ new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p%S}"));
+
+ cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::wstring"),
+ std_wstring_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<wchar_t>"), std_wstring_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<wchar_t,std::char_traits<wchar_t>,std::"
+ "allocator<wchar_t> >"),
+ std_wstring_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<wchar_t, std::char_traits<wchar_t>, "
+ "std::allocator<wchar_t> >"),
+ std_wstring_summary_sp);
+
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::__cxx11::wstring"), cxx11_wstring_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::__cxx11::basic_string<wchar_t, "
+ "std::char_traits<wchar_t>, std::allocator<wchar_t> >"),
+ cxx11_wstring_summary_sp);
+
+ SyntheticChildren::Flags stl_synth_flags;
+ stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
+ false);
+
+ cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
+ RegularExpressionSP(
+ new RegularExpression(llvm::StringRef("^std::vector<.+>(( )?&)?$"))),
+ SyntheticChildrenSP(new ScriptedSyntheticChildren(
+ stl_synth_flags,
+ "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider")));
+ cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
+ RegularExpressionSP(
+ new RegularExpression(llvm::StringRef("^std::map<.+> >(( )?&)?$"))),
+ SyntheticChildrenSP(new ScriptedSyntheticChildren(
+ stl_synth_flags,
+ "lldb.formatters.cpp.gnu_libstdcpp.StdMapSynthProvider")));
+ cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
+ RegularExpressionSP(new RegularExpression(
+ llvm::StringRef("^std::(__cxx11::)?list<.+>(( )?&)?$"))),
+ SyntheticChildrenSP(new ScriptedSyntheticChildren(
+ stl_synth_flags,
+ "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider")));
+ stl_summary_flags.SetDontShowChildren(false);
+ stl_summary_flags.SetSkipPointers(true);
+ cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
+ RegularExpressionSP(
+ new RegularExpression(llvm::StringRef("^std::vector<.+>(( )?&)?$"))),
+ TypeSummaryImplSP(
+ new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
+ cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
+ RegularExpressionSP(
+ new RegularExpression(llvm::StringRef("^std::map<.+> >(( )?&)?$"))),
+ TypeSummaryImplSP(
+ new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
+ cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
+ RegularExpressionSP(new RegularExpression(
+ llvm::StringRef("^std::(__cxx11::)?list<.+>(( )?&)?$"))),
+ TypeSummaryImplSP(
+ new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator,
+ "std::vector iterator synthetic children",
+ ConstString("^__gnu_cxx::__normal_iterator<.+>$"), stl_synth_flags, true);
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator,
+ "std::map iterator synthetic children",
+ ConstString("^std::_Rb_tree_iterator<.+>$"), stl_synth_flags, true);
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator,
+ "std::unique_ptr synthetic children",
+ ConstString("^std::unique_ptr<.+>(( )?&)?$"), stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator,
+ "std::shared_ptr synthetic children",
+ ConstString("^std::shared_ptr<.+>(( )?&)?$"), stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator,
+ "std::weak_ptr synthetic children",
+ ConstString("^std::weak_ptr<.+>(( )?&)?$"), stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator,
+ "std::tuple synthetic children", ConstString("^std::tuple<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibStdcppUniquePointerSummaryProvider,
+ "libstdc++ std::unique_ptr summary provider",
+ ConstString("^std::unique_ptr<.+>(( )?&)?$"), stl_summary_flags,
+ true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibStdcppSmartPointerSummaryProvider,
+ "libstdc++ std::shared_ptr summary provider",
+ ConstString("^std::shared_ptr<.+>(( )?&)?$"), stl_summary_flags,
+ true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibStdcppSmartPointerSummaryProvider,
+ "libstdc++ std::weak_ptr summary provider",
+ ConstString("^std::weak_ptr<.+>(( )?&)?$"), stl_summary_flags,
+ true);
+}
+
+static void LoadSystemFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
+ if (!cpp_category_sp)
+ return;
+
+ TypeSummaryImpl::Flags string_flags;
+ string_flags.SetCascades(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(false)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ TypeSummaryImpl::Flags string_array_flags;
+ string_array_flags.SetCascades(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(true)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ // FIXME because of a bug in the FormattersContainer we need to add a summary
+ // for both X* and const X* (<rdar://problem/12717717>)
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char16StringSummaryProvider,
+ "char16_t * summary provider", ConstString("char16_t *"), string_flags);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::Char16StringSummaryProvider,
+ "char16_t [] summary provider",
+ ConstString("char16_t \\[[0-9]+\\]"), string_array_flags, true);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char32StringSummaryProvider,
+ "char32_t * summary provider", ConstString("char32_t *"), string_flags);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::Char32StringSummaryProvider,
+ "char32_t [] summary provider",
+ ConstString("char32_t \\[[0-9]+\\]"), string_array_flags, true);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::WCharStringSummaryProvider,
+ "wchar_t * summary provider", ConstString("wchar_t *"), string_flags);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::WCharStringSummaryProvider,
+ "wchar_t * summary provider",
+ ConstString("wchar_t \\[[0-9]+\\]"), string_array_flags, true);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char16StringSummaryProvider,
+ "unichar * summary provider", ConstString("unichar *"), string_flags);
+
+ TypeSummaryImpl::Flags widechar_flags;
+ widechar_flags.SetDontShowValue(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false)
+ .SetCascades(true)
+ .SetDontShowChildren(true)
+ .SetHideItemNames(true)
+ .SetShowMembersOneLiner(false);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char16SummaryProvider,
+ "char16_t summary provider", ConstString("char16_t"), widechar_flags);
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char32SummaryProvider,
+ "char32_t summary provider", ConstString("char32_t"), widechar_flags);
+ AddCXXSummary(cpp_category_sp, lldb_private::formatters::WCharSummaryProvider,
+ "wchar_t summary provider", ConstString("wchar_t"),
+ widechar_flags);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char16SummaryProvider,
+ "unichar summary provider", ConstString("unichar"), widechar_flags);
+}
+
+std::unique_ptr<Language::TypeScavenger> CPlusPlusLanguage::GetTypeScavenger() {
+ class CPlusPlusTypeScavenger : public Language::ImageListTypeScavenger {
+ public:
+ CompilerType AdjustForInclusion(CompilerType &candidate) override {
+ LanguageType lang_type(candidate.GetMinimumLanguage());
+ if (!Language::LanguageIsC(lang_type) &&
+ !Language::LanguageIsCPlusPlus(lang_type))
+ return CompilerType();
+ if (candidate.IsTypedefType())
+ return candidate.GetTypedefedType();
+ return candidate;
+ }
+ };
+
+ return std::unique_ptr<TypeScavenger>(new CPlusPlusTypeScavenger());
+}
+
+lldb::TypeCategoryImplSP CPlusPlusLanguage::GetFormatters() {
+ static llvm::once_flag g_initialize;
+ static TypeCategoryImplSP g_category;
+
+ llvm::call_once(g_initialize, [this]() -> void {
+ DataVisualization::Categories::GetCategory(GetPluginName(), g_category);
+ if (g_category) {
+ LoadLibStdcppFormatters(g_category);
+ LoadLibCxxFormatters(g_category);
+ LoadSystemFormatters(g_category);
+ }
+ });
+ return g_category;
+}
+
+HardcodedFormatters::HardcodedSummaryFinder
+CPlusPlusLanguage::GetHardcodedSummaries() {
+ static llvm::once_flag g_initialize;
+ static ConstString g_vectortypes("VectorTypes");
+ static HardcodedFormatters::HardcodedSummaryFinder g_formatters;
+
+ llvm::call_once(g_initialize, []() -> void {
+ g_formatters.push_back(
+ [](lldb_private::ValueObject &valobj, lldb::DynamicValueType,
+ FormatManager &) -> TypeSummaryImpl::SharedPointer {
+ static CXXFunctionSummaryFormat::SharedPointer formatter_sp(
+ new CXXFunctionSummaryFormat(
+ TypeSummaryImpl::Flags(),
+ lldb_private::formatters::CXXFunctionPointerSummaryProvider,
+ "Function pointer summary provider"));
+ if (valobj.GetCompilerType().IsFunctionPointerType()) {
+ return formatter_sp;
+ }
+ return nullptr;
+ });
+ g_formatters.push_back(
+ [](lldb_private::ValueObject &valobj, lldb::DynamicValueType,
+ FormatManager &fmt_mgr) -> TypeSummaryImpl::SharedPointer {
+ static CXXFunctionSummaryFormat::SharedPointer formatter_sp(
+ new CXXFunctionSummaryFormat(
+ TypeSummaryImpl::Flags()
+ .SetCascades(true)
+ .SetDontShowChildren(true)
+ .SetHideItemNames(true)
+ .SetShowMembersOneLiner(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false),
+ lldb_private::formatters::VectorTypeSummaryProvider,
+ "vector_type pointer summary provider"));
+ if (valobj.GetCompilerType().IsVectorType(nullptr, nullptr)) {
+ if (fmt_mgr.GetCategory(g_vectortypes)->IsEnabled())
+ return formatter_sp;
+ }
+ return nullptr;
+ });
+ g_formatters.push_back(
+ [](lldb_private::ValueObject &valobj, lldb::DynamicValueType,
+ FormatManager &fmt_mgr) -> TypeSummaryImpl::SharedPointer {
+ static CXXFunctionSummaryFormat::SharedPointer formatter_sp(
+ new CXXFunctionSummaryFormat(
+ TypeSummaryImpl::Flags()
+ .SetCascades(true)
+ .SetDontShowChildren(true)
+ .SetHideItemNames(true)
+ .SetShowMembersOneLiner(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false),
+ lldb_private::formatters::BlockPointerSummaryProvider,
+ "block pointer summary provider"));
+ if (valobj.GetCompilerType().IsBlockPointerType(nullptr)) {
+ return formatter_sp;
+ }
+ return nullptr;
+ });
+ });
+
+ return g_formatters;
+}
+
+HardcodedFormatters::HardcodedSyntheticFinder
+CPlusPlusLanguage::GetHardcodedSynthetics() {
+ static llvm::once_flag g_initialize;
+ static ConstString g_vectortypes("VectorTypes");
+ static HardcodedFormatters::HardcodedSyntheticFinder g_formatters;
+
+ llvm::call_once(g_initialize, []() -> void {
+ g_formatters.push_back([](lldb_private::ValueObject &valobj,
+ lldb::DynamicValueType,
+ FormatManager &
+ fmt_mgr) -> SyntheticChildren::SharedPointer {
+ static CXXSyntheticChildren::SharedPointer formatter_sp(
+ new CXXSyntheticChildren(
+ SyntheticChildren::Flags()
+ .SetCascades(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(true)
+ .SetNonCacheable(true),
+ "vector_type synthetic children",
+ lldb_private::formatters::VectorTypeSyntheticFrontEndCreator));
+ if (valobj.GetCompilerType().IsVectorType(nullptr, nullptr)) {
+ if (fmt_mgr.GetCategory(g_vectortypes)->IsEnabled())
+ return formatter_sp;
+ }
+ return nullptr;
+ });
+ g_formatters.push_back([](lldb_private::ValueObject &valobj,
+ lldb::DynamicValueType,
+ FormatManager &
+ fmt_mgr) -> SyntheticChildren::SharedPointer {
+ static CXXSyntheticChildren::SharedPointer formatter_sp(
+ new CXXSyntheticChildren(
+ SyntheticChildren::Flags()
+ .SetCascades(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(true)
+ .SetNonCacheable(true),
+ "block pointer synthetic children",
+ lldb_private::formatters::BlockPointerSyntheticFrontEndCreator));
+ if (valobj.GetCompilerType().IsBlockPointerType(nullptr)) {
+ return formatter_sp;
+ }
+ return nullptr;
+ });
+
+ });
+
+ return g_formatters;
+}
+
+bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ const auto suffixes = {".cpp", ".cxx", ".c++", ".cc", ".c",
+ ".h", ".hh", ".hpp", ".hxx", ".h++"};
+ for (auto suffix : suffixes) {
+ if (file_path.endswith_lower(suffix))
+ return true;
+ }
+
+ // Check if we're in a STL path (where the files usually have no extension
+ // that we could check for.
+ return file_path.contains("/usr/include/c++/");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
new file mode 100644
index 000000000000..d30e56080732
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
@@ -0,0 +1,136 @@
+//===-- CPlusPlusLanguage.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CPlusPlusLanguage_h_
+#define liblldb_CPlusPlusLanguage_h_
+
+#include <set>
+#include <vector>
+
+#include "llvm/ADT/StringRef.h"
+
+#include "Plugins/Language/ClangCommon/ClangHighlighter.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class CPlusPlusLanguage : public Language {
+ ClangHighlighter m_highlighter;
+
+public:
+ class MethodName {
+ public:
+ MethodName()
+ : m_full(), m_basename(), m_context(), m_arguments(), m_qualifiers(),
+ m_parsed(false), m_parse_error(false) {}
+
+ MethodName(ConstString s)
+ : m_full(s), m_basename(), m_context(), m_arguments(), m_qualifiers(),
+ m_parsed(false), m_parse_error(false) {}
+
+ void Clear();
+
+ bool IsValid() {
+ if (!m_parsed)
+ Parse();
+ if (m_parse_error)
+ return false;
+ return (bool)m_full;
+ }
+
+ ConstString GetFullName() const { return m_full; }
+
+ std::string GetScopeQualifiedName();
+
+ llvm::StringRef GetBasename();
+
+ llvm::StringRef GetContext();
+
+ llvm::StringRef GetArguments();
+
+ llvm::StringRef GetQualifiers();
+
+ protected:
+ void Parse();
+ bool TrySimplifiedParse();
+
+ ConstString m_full; // Full name:
+ // "lldb::SBTarget::GetBreakpointAtIndex(unsigned int)
+ // const"
+ llvm::StringRef m_basename; // Basename: "GetBreakpointAtIndex"
+ llvm::StringRef m_context; // Decl context: "lldb::SBTarget"
+ llvm::StringRef m_arguments; // Arguments: "(unsigned int)"
+ llvm::StringRef m_qualifiers; // Qualifiers: "const"
+ bool m_parsed;
+ bool m_parse_error;
+ };
+
+ CPlusPlusLanguage() = default;
+
+ ~CPlusPlusLanguage() override = default;
+
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeC_plus_plus;
+ }
+
+ std::unique_ptr<TypeScavenger> GetTypeScavenger() override;
+ lldb::TypeCategoryImplSP GetFormatters() override;
+
+ HardcodedFormatters::HardcodedSummaryFinder GetHardcodedSummaries() override;
+
+ HardcodedFormatters::HardcodedSyntheticFinder
+ GetHardcodedSynthetics() override;
+
+ bool IsSourceFile(llvm::StringRef file_path) const override;
+
+ const Highlighter *GetHighlighter() const override { return &m_highlighter; }
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::Language *CreateInstance(lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static bool IsCPPMangledName(const char *name);
+
+ // Extract C++ context and identifier from a string using heuristic matching
+ // (as opposed to
+ // CPlusPlusLanguage::MethodName which has to have a fully qualified C++ name
+ // with parens and arguments.
+ // If the name is a lone C identifier (e.g. C) or a qualified C identifier
+ // (e.g. A::B::C) it will return true,
+ // and identifier will be the identifier (C and C respectively) and the
+ // context will be "" and "A::B" respectively.
+ // If the name fails the heuristic matching for a qualified or unqualified
+ // C/C++ identifier, then it will return false
+ // and identifier and context will be unchanged.
+
+ static bool ExtractContextAndIdentifier(const char *name,
+ llvm::StringRef &context,
+ llvm::StringRef &identifier);
+
+ // Given a mangled function name, calculates some alternative manglings since
+ // the compiler mangling may not line up with the symbol we are expecting
+ static uint32_t
+ FindAlternateFunctionManglings(const ConstString mangled,
+ std::set<ConstString> &candidates);
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CPlusPlusLanguage_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp
new file mode 100644
index 000000000000..932db17b964a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp
@@ -0,0 +1,660 @@
+//===-- CPlusPlusNameParser.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CPlusPlusNameParser.h"
+
+#include "clang/Basic/IdentifierTable.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using llvm::Optional;
+using llvm::None;
+using ParsedFunction = lldb_private::CPlusPlusNameParser::ParsedFunction;
+using ParsedName = lldb_private::CPlusPlusNameParser::ParsedName;
+namespace tok = clang::tok;
+
+Optional<ParsedFunction> CPlusPlusNameParser::ParseAsFunctionDefinition() {
+ m_next_token_index = 0;
+ Optional<ParsedFunction> result(None);
+
+ // Try to parse the name as function without a return type specified e.g.
+ // main(int, char*[])
+ {
+ Bookmark start_position = SetBookmark();
+ result = ParseFunctionImpl(false);
+ if (result && !HasMoreTokens())
+ return result;
+ }
+
+ // Try to parse the name as function with function pointer return type e.g.
+ // void (*get_func(const char*))()
+ result = ParseFuncPtr(true);
+ if (result)
+ return result;
+
+ // Finally try to parse the name as a function with non-function return type
+ // e.g. int main(int, char*[])
+ result = ParseFunctionImpl(true);
+ if (HasMoreTokens())
+ return None;
+ return result;
+}
+
+Optional<ParsedName> CPlusPlusNameParser::ParseAsFullName() {
+ m_next_token_index = 0;
+ Optional<ParsedNameRanges> name_ranges = ParseFullNameImpl();
+ if (!name_ranges)
+ return None;
+ if (HasMoreTokens())
+ return None;
+ ParsedName result;
+ result.basename = GetTextForRange(name_ranges.getValue().basename_range);
+ result.context = GetTextForRange(name_ranges.getValue().context_range);
+ return result;
+}
+
+bool CPlusPlusNameParser::HasMoreTokens() {
+ return m_next_token_index < m_tokens.size();
+}
+
+void CPlusPlusNameParser::Advance() { ++m_next_token_index; }
+
+void CPlusPlusNameParser::TakeBack() { --m_next_token_index; }
+
+bool CPlusPlusNameParser::ConsumeToken(tok::TokenKind kind) {
+ if (!HasMoreTokens())
+ return false;
+
+ if (!Peek().is(kind))
+ return false;
+
+ Advance();
+ return true;
+}
+
+template <typename... Ts> bool CPlusPlusNameParser::ConsumeToken(Ts... kinds) {
+ if (!HasMoreTokens())
+ return false;
+
+ if (!Peek().isOneOf(kinds...))
+ return false;
+
+ Advance();
+ return true;
+}
+
+CPlusPlusNameParser::Bookmark CPlusPlusNameParser::SetBookmark() {
+ return Bookmark(m_next_token_index);
+}
+
+size_t CPlusPlusNameParser::GetCurrentPosition() { return m_next_token_index; }
+
+clang::Token &CPlusPlusNameParser::Peek() {
+ assert(HasMoreTokens());
+ return m_tokens[m_next_token_index];
+}
+
+Optional<ParsedFunction>
+CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) {
+ Bookmark start_position = SetBookmark();
+ if (expect_return_type) {
+ // Consume return type if it's expected.
+ if (!ConsumeTypename())
+ return None;
+ }
+
+ auto maybe_name = ParseFullNameImpl();
+ if (!maybe_name) {
+ return None;
+ }
+
+ size_t argument_start = GetCurrentPosition();
+ if (!ConsumeArguments()) {
+ return None;
+ }
+
+ size_t qualifiers_start = GetCurrentPosition();
+ SkipFunctionQualifiers();
+ size_t end_position = GetCurrentPosition();
+
+ ParsedFunction result;
+ result.name.basename = GetTextForRange(maybe_name.getValue().basename_range);
+ result.name.context = GetTextForRange(maybe_name.getValue().context_range);
+ result.arguments = GetTextForRange(Range(argument_start, qualifiers_start));
+ result.qualifiers = GetTextForRange(Range(qualifiers_start, end_position));
+ start_position.Remove();
+ return result;
+}
+
+Optional<ParsedFunction>
+CPlusPlusNameParser::ParseFuncPtr(bool expect_return_type) {
+ Bookmark start_position = SetBookmark();
+ if (expect_return_type) {
+ // Consume return type.
+ if (!ConsumeTypename())
+ return None;
+ }
+
+ if (!ConsumeToken(tok::l_paren))
+ return None;
+ if (!ConsumePtrsAndRefs())
+ return None;
+
+ {
+ Bookmark before_inner_function_pos = SetBookmark();
+ auto maybe_inner_function_name = ParseFunctionImpl(false);
+ if (maybe_inner_function_name)
+ if (ConsumeToken(tok::r_paren))
+ if (ConsumeArguments()) {
+ SkipFunctionQualifiers();
+ start_position.Remove();
+ before_inner_function_pos.Remove();
+ return maybe_inner_function_name;
+ }
+ }
+
+ auto maybe_inner_function_ptr_name = ParseFuncPtr(false);
+ if (maybe_inner_function_ptr_name)
+ if (ConsumeToken(tok::r_paren))
+ if (ConsumeArguments()) {
+ SkipFunctionQualifiers();
+ start_position.Remove();
+ return maybe_inner_function_ptr_name;
+ }
+ return None;
+}
+
+bool CPlusPlusNameParser::ConsumeArguments() {
+ return ConsumeBrackets(tok::l_paren, tok::r_paren);
+}
+
+bool CPlusPlusNameParser::ConsumeTemplateArgs() {
+ Bookmark start_position = SetBookmark();
+ if (!HasMoreTokens() || Peek().getKind() != tok::less)
+ return false;
+ Advance();
+
+ // Consuming template arguments is a bit trickier than consuming function
+ // arguments, because '<' '>' brackets are not always trivially balanced. In
+ // some rare cases tokens '<' and '>' can appear inside template arguments as
+ // arithmetic or shift operators not as template brackets. Examples:
+ // std::enable_if<(10u)<(64), bool>
+ // f<A<operator<(X,Y)::Subclass>>
+ // Good thing that compiler makes sure that really ambiguous cases of '>'
+ // usage should be enclosed within '()' brackets.
+ int template_counter = 1;
+ bool can_open_template = false;
+ while (HasMoreTokens() && template_counter > 0) {
+ tok::TokenKind kind = Peek().getKind();
+ switch (kind) {
+ case tok::greatergreater:
+ template_counter -= 2;
+ can_open_template = false;
+ Advance();
+ break;
+ case tok::greater:
+ --template_counter;
+ can_open_template = false;
+ Advance();
+ break;
+ case tok::less:
+ // '<' is an attempt to open a subteamplte
+ // check if parser is at the point where it's actually possible,
+ // otherwise it's just a part of an expression like 'sizeof(T)<(10)'. No
+ // need to do the same for '>' because compiler actually makes sure that
+ // '>' always surrounded by brackets to avoid ambiguity.
+ if (can_open_template)
+ ++template_counter;
+ can_open_template = false;
+ Advance();
+ break;
+ case tok::kw_operator: // C++ operator overloading.
+ if (!ConsumeOperator())
+ return false;
+ can_open_template = true;
+ break;
+ case tok::raw_identifier:
+ can_open_template = true;
+ Advance();
+ break;
+ case tok::l_square:
+ if (!ConsumeBrackets(tok::l_square, tok::r_square))
+ return false;
+ can_open_template = false;
+ break;
+ case tok::l_paren:
+ if (!ConsumeArguments())
+ return false;
+ can_open_template = false;
+ break;
+ default:
+ can_open_template = false;
+ Advance();
+ break;
+ }
+ }
+
+ if (template_counter != 0) {
+ return false;
+ }
+ start_position.Remove();
+ return true;
+}
+
+bool CPlusPlusNameParser::ConsumeAnonymousNamespace() {
+ Bookmark start_position = SetBookmark();
+ if (!ConsumeToken(tok::l_paren)) {
+ return false;
+ }
+ constexpr llvm::StringLiteral g_anonymous("anonymous");
+ if (HasMoreTokens() && Peek().is(tok::raw_identifier) &&
+ Peek().getRawIdentifier() == g_anonymous) {
+ Advance();
+ } else {
+ return false;
+ }
+
+ if (!ConsumeToken(tok::kw_namespace)) {
+ return false;
+ }
+
+ if (!ConsumeToken(tok::r_paren)) {
+ return false;
+ }
+ start_position.Remove();
+ return true;
+}
+
+bool CPlusPlusNameParser::ConsumeLambda() {
+ Bookmark start_position = SetBookmark();
+ if (!ConsumeToken(tok::l_brace)) {
+ return false;
+ }
+ constexpr llvm::StringLiteral g_lambda("lambda");
+ if (HasMoreTokens() && Peek().is(tok::raw_identifier) &&
+ Peek().getRawIdentifier() == g_lambda) {
+ // Put the matched brace back so we can use ConsumeBrackets
+ TakeBack();
+ } else {
+ return false;
+ }
+
+ if (!ConsumeBrackets(tok::l_brace, tok::r_brace)) {
+ return false;
+ }
+
+ start_position.Remove();
+ return true;
+}
+
+bool CPlusPlusNameParser::ConsumeBrackets(tok::TokenKind left,
+ tok::TokenKind right) {
+ Bookmark start_position = SetBookmark();
+ if (!HasMoreTokens() || Peek().getKind() != left)
+ return false;
+ Advance();
+
+ int counter = 1;
+ while (HasMoreTokens() && counter > 0) {
+ tok::TokenKind kind = Peek().getKind();
+ if (kind == right)
+ --counter;
+ else if (kind == left)
+ ++counter;
+ Advance();
+ }
+
+ assert(counter >= 0);
+ if (counter > 0) {
+ return false;
+ }
+ start_position.Remove();
+ return true;
+}
+
+bool CPlusPlusNameParser::ConsumeOperator() {
+ Bookmark start_position = SetBookmark();
+ if (!ConsumeToken(tok::kw_operator))
+ return false;
+
+ if (!HasMoreTokens()) {
+ return false;
+ }
+
+ const auto &token = Peek();
+ switch (token.getKind()) {
+ case tok::kw_new:
+ case tok::kw_delete:
+ // This is 'new' or 'delete' operators.
+ Advance();
+ // Check for array new/delete.
+ if (HasMoreTokens() && Peek().is(tok::l_square)) {
+ // Consume the '[' and ']'.
+ if (!ConsumeBrackets(tok::l_square, tok::r_square))
+ return false;
+ }
+ break;
+
+#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
+ case tok::Token: \
+ Advance(); \
+ break;
+#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemberOnly)
+#include "clang/Basic/OperatorKinds.def"
+#undef OVERLOADED_OPERATOR
+#undef OVERLOADED_OPERATOR_MULTI
+
+ case tok::l_paren:
+ // Call operator consume '(' ... ')'.
+ if (ConsumeBrackets(tok::l_paren, tok::r_paren))
+ break;
+ return false;
+
+ case tok::l_square:
+ // This is a [] operator.
+ // Consume the '[' and ']'.
+ if (ConsumeBrackets(tok::l_square, tok::r_square))
+ break;
+ return false;
+
+ default:
+ // This might be a cast operator.
+ if (ConsumeTypename())
+ break;
+ return false;
+ }
+ start_position.Remove();
+ return true;
+}
+
+void CPlusPlusNameParser::SkipTypeQualifiers() {
+ while (ConsumeToken(tok::kw_const, tok::kw_volatile))
+ ;
+}
+
+void CPlusPlusNameParser::SkipFunctionQualifiers() {
+ while (ConsumeToken(tok::kw_const, tok::kw_volatile, tok::amp, tok::ampamp))
+ ;
+}
+
+bool CPlusPlusNameParser::ConsumeBuiltinType() {
+ bool result = false;
+ bool continue_parsing = true;
+ // Built-in types can be made of a few keywords like 'unsigned long long
+ // int'. This function consumes all built-in type keywords without checking
+ // if they make sense like 'unsigned char void'.
+ while (continue_parsing && HasMoreTokens()) {
+ switch (Peek().getKind()) {
+ case tok::kw_short:
+ case tok::kw_long:
+ case tok::kw___int64:
+ case tok::kw___int128:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw_void:
+ case tok::kw_char:
+ case tok::kw_int:
+ case tok::kw_half:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw___float128:
+ case tok::kw_wchar_t:
+ case tok::kw_bool:
+ case tok::kw_char16_t:
+ case tok::kw_char32_t:
+ result = true;
+ Advance();
+ break;
+ default:
+ continue_parsing = false;
+ break;
+ }
+ }
+ return result;
+}
+
+void CPlusPlusNameParser::SkipPtrsAndRefs() {
+ // Ignoring result.
+ ConsumePtrsAndRefs();
+}
+
+bool CPlusPlusNameParser::ConsumePtrsAndRefs() {
+ bool found = false;
+ SkipTypeQualifiers();
+ while (ConsumeToken(tok::star, tok::amp, tok::ampamp, tok::kw_const,
+ tok::kw_volatile)) {
+ found = true;
+ SkipTypeQualifiers();
+ }
+ return found;
+}
+
+bool CPlusPlusNameParser::ConsumeDecltype() {
+ Bookmark start_position = SetBookmark();
+ if (!ConsumeToken(tok::kw_decltype))
+ return false;
+
+ if (!ConsumeArguments())
+ return false;
+
+ start_position.Remove();
+ return true;
+}
+
+bool CPlusPlusNameParser::ConsumeTypename() {
+ Bookmark start_position = SetBookmark();
+ SkipTypeQualifiers();
+ if (!ConsumeBuiltinType() && !ConsumeDecltype()) {
+ if (!ParseFullNameImpl())
+ return false;
+ }
+ SkipPtrsAndRefs();
+ start_position.Remove();
+ return true;
+}
+
+Optional<CPlusPlusNameParser::ParsedNameRanges>
+CPlusPlusNameParser::ParseFullNameImpl() {
+ // Name parsing state machine.
+ enum class State {
+ Beginning, // start of the name
+ AfterTwoColons, // right after ::
+ AfterIdentifier, // right after alphanumerical identifier ([a-z0-9_]+)
+ AfterTemplate, // right after template brackets (<something>)
+ AfterOperator, // right after name of C++ operator
+ };
+
+ Bookmark start_position = SetBookmark();
+ State state = State::Beginning;
+ bool continue_parsing = true;
+ Optional<size_t> last_coloncolon_position = None;
+
+ while (continue_parsing && HasMoreTokens()) {
+ const auto &token = Peek();
+ switch (token.getKind()) {
+ case tok::raw_identifier: // Just a name.
+ if (state != State::Beginning && state != State::AfterTwoColons) {
+ continue_parsing = false;
+ break;
+ }
+ Advance();
+ state = State::AfterIdentifier;
+ break;
+ case tok::l_paren: {
+ if (state == State::Beginning || state == State::AfterTwoColons) {
+ // (anonymous namespace)
+ if (ConsumeAnonymousNamespace()) {
+ state = State::AfterIdentifier;
+ break;
+ }
+ }
+
+ // Type declared inside a function 'func()::Type'
+ if (state != State::AfterIdentifier && state != State::AfterTemplate &&
+ state != State::AfterOperator) {
+ continue_parsing = false;
+ break;
+ }
+ Bookmark l_paren_position = SetBookmark();
+ // Consume the '(' ... ') [const]'.
+ if (!ConsumeArguments()) {
+ continue_parsing = false;
+ break;
+ }
+ SkipFunctionQualifiers();
+
+ // Consume '::'
+ size_t coloncolon_position = GetCurrentPosition();
+ if (!ConsumeToken(tok::coloncolon)) {
+ continue_parsing = false;
+ break;
+ }
+ l_paren_position.Remove();
+ last_coloncolon_position = coloncolon_position;
+ state = State::AfterTwoColons;
+ break;
+ }
+ case tok::l_brace:
+ if (state == State::Beginning || state == State::AfterTwoColons) {
+ if (ConsumeLambda()) {
+ state = State::AfterIdentifier;
+ break;
+ }
+ }
+ continue_parsing = false;
+ break;
+ case tok::coloncolon: // Type nesting delimiter.
+ if (state != State::Beginning && state != State::AfterIdentifier &&
+ state != State::AfterTemplate) {
+ continue_parsing = false;
+ break;
+ }
+ last_coloncolon_position = GetCurrentPosition();
+ Advance();
+ state = State::AfterTwoColons;
+ break;
+ case tok::less: // Template brackets.
+ if (state != State::AfterIdentifier && state != State::AfterOperator) {
+ continue_parsing = false;
+ break;
+ }
+ if (!ConsumeTemplateArgs()) {
+ continue_parsing = false;
+ break;
+ }
+ state = State::AfterTemplate;
+ break;
+ case tok::kw_operator: // C++ operator overloading.
+ if (state != State::Beginning && state != State::AfterTwoColons) {
+ continue_parsing = false;
+ break;
+ }
+ if (!ConsumeOperator()) {
+ continue_parsing = false;
+ break;
+ }
+ state = State::AfterOperator;
+ break;
+ case tok::tilde: // Destructor.
+ if (state != State::Beginning && state != State::AfterTwoColons) {
+ continue_parsing = false;
+ break;
+ }
+ Advance();
+ if (ConsumeToken(tok::raw_identifier)) {
+ state = State::AfterIdentifier;
+ } else {
+ TakeBack();
+ continue_parsing = false;
+ }
+ break;
+ default:
+ continue_parsing = false;
+ break;
+ }
+ }
+
+ if (state == State::AfterIdentifier || state == State::AfterOperator ||
+ state == State::AfterTemplate) {
+ ParsedNameRanges result;
+ if (last_coloncolon_position) {
+ result.context_range = Range(start_position.GetSavedPosition(),
+ last_coloncolon_position.getValue());
+ result.basename_range =
+ Range(last_coloncolon_position.getValue() + 1, GetCurrentPosition());
+ } else {
+ result.basename_range =
+ Range(start_position.GetSavedPosition(), GetCurrentPosition());
+ }
+ start_position.Remove();
+ return result;
+ } else {
+ return None;
+ }
+}
+
+llvm::StringRef CPlusPlusNameParser::GetTextForRange(const Range &range) {
+ if (range.empty())
+ return llvm::StringRef();
+ assert(range.begin_index < range.end_index);
+ assert(range.begin_index < m_tokens.size());
+ assert(range.end_index <= m_tokens.size());
+ clang::Token &first_token = m_tokens[range.begin_index];
+ clang::Token &last_token = m_tokens[range.end_index - 1];
+ clang::SourceLocation start_loc = first_token.getLocation();
+ clang::SourceLocation end_loc = last_token.getLocation();
+ unsigned start_pos = start_loc.getRawEncoding();
+ unsigned end_pos = end_loc.getRawEncoding() + last_token.getLength();
+ return m_text.take_front(end_pos).drop_front(start_pos);
+}
+
+static const clang::LangOptions &GetLangOptions() {
+ static clang::LangOptions g_options;
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ g_options.LineComment = true;
+ g_options.C99 = true;
+ g_options.C11 = true;
+ g_options.CPlusPlus = true;
+ g_options.CPlusPlus11 = true;
+ g_options.CPlusPlus14 = true;
+ g_options.CPlusPlus17 = true;
+ });
+ return g_options;
+}
+
+static const llvm::StringMap<tok::TokenKind> &GetKeywordsMap() {
+ static llvm::StringMap<tok::TokenKind> g_map{
+#define KEYWORD(Name, Flags) {llvm::StringRef(#Name), tok::kw_##Name},
+#include "clang/Basic/TokenKinds.def"
+#undef KEYWORD
+ };
+ return g_map;
+}
+
+void CPlusPlusNameParser::ExtractTokens() {
+ if (m_text.empty())
+ return;
+ clang::Lexer lexer(clang::SourceLocation(), GetLangOptions(), m_text.data(),
+ m_text.data(), m_text.data() + m_text.size());
+ const auto &kw_map = GetKeywordsMap();
+ clang::Token token;
+ for (lexer.LexFromRawLexer(token); !token.is(clang::tok::eof);
+ lexer.LexFromRawLexer(token)) {
+ if (token.is(clang::tok::raw_identifier)) {
+ auto it = kw_map.find(token.getRawIdentifier());
+ if (it != kw_map.end()) {
+ token.setKind(it->getValue());
+ }
+ }
+
+ m_tokens.push_back(token);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h
new file mode 100644
index 000000000000..414c3a009157
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h
@@ -0,0 +1,177 @@
+//===-- CPlusPlusNameParser.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CPlusPlusNameParser_h_
+#define liblldb_CPlusPlusNameParser_h_
+
+
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+// Helps to validate and obtain various parts of C++ definitions.
+class CPlusPlusNameParser {
+public:
+ CPlusPlusNameParser(llvm::StringRef text) : m_text(text) { ExtractTokens(); }
+
+ struct ParsedName {
+ llvm::StringRef basename;
+ llvm::StringRef context;
+ };
+
+ struct ParsedFunction {
+ ParsedName name;
+ llvm::StringRef arguments;
+ llvm::StringRef qualifiers;
+ };
+
+ // Treats given text as a function definition and parses it.
+ // Function definition might or might not have a return type and this should
+ // change parsing result.
+ // Examples:
+ // main(int, chat const*)
+ // T fun(int, bool)
+ // std::vector<int>::push_back(int)
+ // int& map<int, pair<short, int>>::operator[](short) const
+ // int (*get_function(const chat *))()
+ llvm::Optional<ParsedFunction> ParseAsFunctionDefinition();
+
+ // Treats given text as a potentially nested name of C++ entity (function,
+ // class, field) and parses it.
+ // Examples:
+ // main
+ // fun
+ // std::vector<int>::push_back
+ // map<int, pair<short, int>>::operator[]
+ // func<C>(int, C&)::nested_class::method
+ llvm::Optional<ParsedName> ParseAsFullName();
+
+private:
+ // A C++ definition to parse.
+ llvm::StringRef m_text;
+ // Tokens extracted from m_text.
+ llvm::SmallVector<clang::Token, 30> m_tokens;
+ // Index of the next token to look at from m_tokens.
+ size_t m_next_token_index = 0;
+
+ // Range of tokens saved in m_next_token_index.
+ struct Range {
+ size_t begin_index = 0;
+ size_t end_index = 0;
+
+ Range() {}
+ Range(size_t begin, size_t end) : begin_index(begin), end_index(end) {
+ assert(end >= begin);
+ }
+
+ size_t size() const { return end_index - begin_index; }
+
+ bool empty() const { return size() == 0; }
+ };
+
+ struct ParsedNameRanges {
+ Range basename_range;
+ Range context_range;
+ };
+
+ // Bookmark automatically restores parsing position (m_next_token_index)
+ // when destructed unless it's manually removed with Remove().
+ class Bookmark {
+ public:
+ Bookmark(size_t &position)
+ : m_position(position), m_position_value(position) {}
+ Bookmark(const Bookmark &) = delete;
+ Bookmark(Bookmark &&b)
+ : m_position(b.m_position), m_position_value(b.m_position_value),
+ m_restore(b.m_restore) {
+ b.Remove();
+ }
+ Bookmark &operator=(Bookmark &&) = delete;
+ Bookmark &operator=(const Bookmark &) = delete;
+
+ void Remove() { m_restore = false; }
+ size_t GetSavedPosition() { return m_position_value; }
+ ~Bookmark() {
+ if (m_restore) {
+ m_position = m_position_value;
+ }
+ }
+
+ private:
+ size_t &m_position;
+ size_t m_position_value;
+ bool m_restore = true;
+ };
+
+ bool HasMoreTokens();
+ void Advance();
+ void TakeBack();
+ bool ConsumeToken(clang::tok::TokenKind kind);
+ template <typename... Ts> bool ConsumeToken(Ts... kinds);
+ Bookmark SetBookmark();
+ size_t GetCurrentPosition();
+ clang::Token &Peek();
+ bool ConsumeBrackets(clang::tok::TokenKind left, clang::tok::TokenKind right);
+
+ llvm::Optional<ParsedFunction> ParseFunctionImpl(bool expect_return_type);
+
+ // Parses functions returning function pointers 'string (*f(int x))(float y)'
+ llvm::Optional<ParsedFunction> ParseFuncPtr(bool expect_return_type);
+
+ // Consumes function arguments enclosed within '(' ... ')'
+ bool ConsumeArguments();
+
+ // Consumes template arguments enclosed within '<' ... '>'
+ bool ConsumeTemplateArgs();
+
+ // Consumes '(anonymous namespace)'
+ bool ConsumeAnonymousNamespace();
+
+ // Consumes '{lambda ...}'
+ bool ConsumeLambda();
+
+ // Consumes operator declaration like 'operator *' or 'operator delete []'
+ bool ConsumeOperator();
+
+ // Skips 'const' and 'volatile'
+ void SkipTypeQualifiers();
+
+ // Skips 'const', 'volatile', '&', '&&' in the end of the function.
+ void SkipFunctionQualifiers();
+
+ // Consumes built-in types like 'int' or 'unsigned long long int'
+ bool ConsumeBuiltinType();
+
+ // Consumes types defined via decltype keyword.
+ bool ConsumeDecltype();
+
+ // Skips 'const' and 'volatile'
+ void SkipPtrsAndRefs();
+
+ // Consumes things like 'const * const &'
+ bool ConsumePtrsAndRefs();
+
+ // Consumes full type name like 'Namespace::Class<int>::Method()::InnerClass'
+ bool ConsumeTypename();
+
+ llvm::Optional<ParsedNameRanges> ParseFullNameImpl();
+ llvm::StringRef GetTextForRange(const Range &range);
+
+ // Populate m_tokens by calling clang lexer on m_text.
+ void ExtractTokens();
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CPlusPlusNameParser_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp
new file mode 100644
index 000000000000..959079070acc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp
@@ -0,0 +1,228 @@
+//===-- CxxStringTypes.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CxxStringTypes.h"
+
+#include "llvm/Support/ConvertUTF.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Host/Time.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include <algorithm>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+bool lldb_private::formatters::Char16StringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t valobj_addr = GetArrayAddressOrPointerValue(valobj);
+ if (valobj_addr == 0 || valobj_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ options.SetLocation(valobj_addr);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetPrefixToken("u");
+
+ if (!StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options)) {
+ stream.Printf("Summary Unavailable");
+ return true;
+ }
+
+ return true;
+}
+
+bool lldb_private::formatters::Char32StringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t valobj_addr = GetArrayAddressOrPointerValue(valobj);
+ if (valobj_addr == 0 || valobj_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ options.SetLocation(valobj_addr);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetPrefixToken("U");
+
+ if (!StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(options)) {
+ stream.Printf("Summary Unavailable");
+ return true;
+ }
+
+ return true;
+}
+
+bool lldb_private::formatters::WCharStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t valobj_addr = GetArrayAddressOrPointerValue(valobj);
+ if (valobj_addr == 0 || valobj_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ // Get a wchar_t basic type from the current type system
+ CompilerType wchar_compiler_type =
+ valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar);
+
+ if (!wchar_compiler_type)
+ return false;
+
+ // Safe to pass nullptr for exe_scope here.
+ llvm::Optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr);
+ if (!size)
+ return false;
+ const uint32_t wchar_size = *size;
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ options.SetLocation(valobj_addr);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetPrefixToken("L");
+
+ switch (wchar_size) {
+ case 8:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(options);
+ case 16:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ case 32:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(options);
+ default:
+ stream.Printf("size for wchar_t is not valid");
+ return true;
+ }
+ return true;
+}
+
+bool lldb_private::formatters::Char16SummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ DataExtractor data;
+ Status error;
+ valobj.GetData(data, error);
+
+ if (error.Fail())
+ return false;
+
+ std::string value;
+ valobj.GetValueAsCString(lldb::eFormatUnicode16, value);
+ if (!value.empty())
+ stream.Printf("%s ", value.c_str());
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
+ options.SetData(data);
+ options.SetStream(&stream);
+ options.SetPrefixToken("u");
+ options.SetQuote('\'');
+ options.SetSourceSize(1);
+ options.SetBinaryZeroIsTerminator(false);
+
+ return StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+}
+
+bool lldb_private::formatters::Char32SummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ DataExtractor data;
+ Status error;
+ valobj.GetData(data, error);
+
+ if (error.Fail())
+ return false;
+
+ std::string value;
+ valobj.GetValueAsCString(lldb::eFormatUnicode32, value);
+ if (!value.empty())
+ stream.Printf("%s ", value.c_str());
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
+ options.SetData(data);
+ options.SetStream(&stream);
+ options.SetPrefixToken("U");
+ options.SetQuote('\'');
+ options.SetSourceSize(1);
+ options.SetBinaryZeroIsTerminator(false);
+
+ return StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(options);
+}
+
+bool lldb_private::formatters::WCharSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ DataExtractor data;
+ Status error;
+ valobj.GetData(data, error);
+
+ if (error.Fail())
+ return false;
+
+ // Get a wchar_t basic type from the current type system
+ CompilerType wchar_compiler_type =
+ valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar);
+
+ if (!wchar_compiler_type)
+ return false;
+
+ // Safe to pass nullptr for exe_scope here.
+ llvm::Optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr);
+ if (!size)
+ return false;
+ const uint32_t wchar_size = *size;
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
+ options.SetData(data);
+ options.SetStream(&stream);
+ options.SetPrefixToken("L");
+ options.SetQuote('\'');
+ options.SetSourceSize(1);
+ options.SetBinaryZeroIsTerminator(false);
+
+ switch (wchar_size) {
+ case 8:
+ return StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(options);
+ case 16:
+ return StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ case 32:
+ return StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(options);
+ default:
+ stream.Printf("size for wchar_t is not valid");
+ return true;
+ }
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h
new file mode 100644
index 000000000000..92bef2382eac
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h
@@ -0,0 +1,43 @@
+//===-- CxxStringTypes.h ----------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CxxStringTypes_h_
+#define liblldb_CxxStringTypes_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+bool Char16StringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // char16_t* and unichar*
+
+bool Char32StringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // char32_t*
+
+bool WCharStringSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // wchar_t*
+
+bool Char16SummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // char16_t and unichar
+
+bool Char32SummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // char32_t
+
+bool WCharSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // wchar_t
+
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_CxxStringTypes_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
new file mode 100644
index 000000000000..abe89035c532
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
@@ -0,0 +1,681 @@
+//===-- LibCxx.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxx.h"
+
+#include "llvm/ADT/ScopeExit.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FormatEntity.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/VectorIterator.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+bool lldb_private::formatters::LibcxxOptionalSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+ if (!valobj_sp)
+ return false;
+
+ // An optional either contains a value or not, the member __engaged_ is
+ // a bool flag, it is true if the optional has a value and false otherwise.
+ ValueObjectSP engaged_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__engaged_"), true));
+
+ if (!engaged_sp)
+ return false;
+
+ llvm::StringRef engaged_as_cstring(
+ engaged_sp->GetValueAsUnsigned(0) == 1 ? "true" : "false");
+
+ stream.Printf(" Has Value=%s ", engaged_as_cstring.data());
+
+ return true;
+}
+
+bool lldb_private::formatters::LibcxxFunctionSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+
+ if (!valobj_sp)
+ return false;
+
+ ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (process == nullptr)
+ return false;
+
+ CPPLanguageRuntime *cpp_runtime = CPPLanguageRuntime::Get(*process);
+
+ if (!cpp_runtime)
+ return false;
+
+ CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info =
+ cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp);
+
+ switch (callable_info.callable_case) {
+ case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid:
+ stream.Printf(" __f_ = %" PRIu64, callable_info.member__f_pointer_value);
+ return false;
+ break;
+ case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda:
+ stream.Printf(
+ " Lambda in File %s at Line %u",
+ callable_info.callable_line_entry.file.GetFilename().GetCString(),
+ callable_info.callable_line_entry.line);
+ break;
+ case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject:
+ stream.Printf(
+ " Function in File %s at Line %u",
+ callable_info.callable_line_entry.file.GetFilename().GetCString(),
+ callable_info.callable_line_entry.line);
+ break;
+ case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction:
+ stream.Printf(" Function = %s ",
+ callable_info.callable_symbol.GetName().GetCString());
+ break;
+ }
+
+ return true;
+}
+
+bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+ if (!valobj_sp)
+ return false;
+ ValueObjectSP ptr_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true));
+ ValueObjectSP count_sp(valobj_sp->GetChildAtNamePath(
+ {ConstString("__cntrl_"), ConstString("__shared_owners_")}));
+ ValueObjectSP weakcount_sp(valobj_sp->GetChildAtNamePath(
+ {ConstString("__cntrl_"), ConstString("__shared_weak_owners_")}));
+
+ if (!ptr_sp)
+ return false;
+
+ if (ptr_sp->GetValueAsUnsigned(0) == 0) {
+ stream.Printf("nullptr");
+ return true;
+ } else {
+ bool print_pointee = false;
+ Status error;
+ ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
+ if (pointee_sp && error.Success()) {
+ if (pointee_sp->DumpPrintableRepresentation(
+ stream, ValueObject::eValueObjectRepresentationStyleSummary,
+ lldb::eFormatInvalid,
+ ValueObject::PrintableRepresentationSpecialCases::eDisable,
+ false))
+ print_pointee = true;
+ }
+ if (!print_pointee)
+ stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));
+ }
+
+ if (count_sp)
+ stream.Printf(" strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(0));
+
+ if (weakcount_sp)
+ stream.Printf(" weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(0));
+
+ return true;
+}
+
+/*
+ (lldb) fr var ibeg --raw --ptr-depth 1
+ (std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int,
+ std::__1::basic_string<char, std::__1::char_traits<char>,
+ std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::pair<int,
+ std::__1::basic_string<char, std::__1::char_traits<char>,
+ std::__1::allocator<char> > >, void *> *, long> >) ibeg = {
+ __i_ = {
+ __ptr_ = 0x0000000100103870 {
+ std::__1::__tree_node_base<void *> = {
+ std::__1::__tree_end_node<std::__1::__tree_node_base<void *> *> = {
+ __left_ = 0x0000000000000000
+ }
+ __right_ = 0x0000000000000000
+ __parent_ = 0x00000001001038b0
+ __is_black_ = true
+ }
+ __value_ = {
+ first = 0
+ second = { std::string }
+ */
+
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() {
+ if (valobj_sp)
+ Update();
+}
+
+bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {
+ m_pair_sp.reset();
+ m_pair_ptr = nullptr;
+
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+
+ TargetSP target_sp(valobj_sp->GetTargetSP());
+
+ if (!target_sp)
+ return false;
+
+ if (!valobj_sp)
+ return false;
+
+ static ConstString g___i_("__i_");
+
+ // this must be a ValueObject* because it is a child of the ValueObject we
+ // are producing children for it if were a ValueObjectSP, we would end up
+ // with a loop (iterator -> synthetic -> child -> parent == iterator) and
+ // that would in turn leak memory by never allowing the ValueObjects to die
+ // and free their memory
+ m_pair_ptr = valobj_sp
+ ->GetValueForExpressionPath(
+ ".__i_.__ptr_->__value_", nullptr, nullptr,
+ ValueObject::GetValueForExpressionPathOptions()
+ .DontCheckDotVsArrowSyntax()
+ .SetSyntheticChildrenTraversal(
+ ValueObject::GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::None),
+ nullptr)
+ .get();
+
+ if (!m_pair_ptr) {
+ m_pair_ptr = valobj_sp
+ ->GetValueForExpressionPath(
+ ".__i_.__ptr_", nullptr, nullptr,
+ ValueObject::GetValueForExpressionPathOptions()
+ .DontCheckDotVsArrowSyntax()
+ .SetSyntheticChildrenTraversal(
+ ValueObject::GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::None),
+ nullptr)
+ .get();
+ if (m_pair_ptr) {
+ auto __i_(valobj_sp->GetChildMemberWithName(g___i_, true));
+ if (!__i_) {
+ m_pair_ptr = nullptr;
+ return false;
+ }
+ CompilerType pair_type(
+ __i_->GetCompilerType().GetTypeTemplateArgument(0));
+ std::string name;
+ uint64_t bit_offset_ptr;
+ uint32_t bitfield_bit_size_ptr;
+ bool is_bitfield_ptr;
+ pair_type = pair_type.GetFieldAtIndex(
+ 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr);
+ if (!pair_type) {
+ m_pair_ptr = nullptr;
+ return false;
+ }
+
+ auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS));
+ m_pair_ptr = nullptr;
+ if (addr && addr != LLDB_INVALID_ADDRESS) {
+ ClangASTContext *ast_ctx =
+ llvm::dyn_cast_or_null<ClangASTContext>(pair_type.GetTypeSystem());
+ if (!ast_ctx)
+ return false;
+ CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(
+ ConstString(),
+ {{"ptr0",
+ ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"ptr1",
+ ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"ptr2",
+ ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)},
+ {"payload", pair_type}});
+ llvm::Optional<uint64_t> size = tree_node_type.GetByteSize(nullptr);
+ if (!size)
+ return false;
+ DataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ Status error;
+ process_sp->ReadMemory(addr, buffer_sp->GetBytes(),
+ buffer_sp->GetByteSize(), error);
+ if (error.Fail())
+ return false;
+ DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ auto pair_sp = CreateValueObjectFromData(
+ "pair", extractor, valobj_sp->GetExecutionContextRef(),
+ tree_node_type);
+ if (pair_sp)
+ m_pair_sp = pair_sp->GetChildAtIndex(4, true);
+ }
+ }
+ }
+
+ return false;
+}
+
+size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ CalculateNumChildren() {
+ return 2;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ if (m_pair_ptr)
+ return m_pair_ptr->GetChildAtIndex(idx, true);
+ if (m_pair_sp)
+ return m_pair_sp->GetChildAtIndex(idx, true);
+ return lldb::ValueObjectSP();
+}
+
+bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (name == "first")
+ return 0;
+ if (name == "second")
+ return 1;
+ return UINT32_MAX;
+}
+
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ ~LibCxxMapIteratorSyntheticFrontEnd() {
+ // this will be deleted when its parent dies (since it's a child object)
+ // delete m_pair_ptr;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
+
+/*
+ (lldb) fr var ibeg --raw --ptr-depth 1 -T
+ (std::__1::__wrap_iter<int *>) ibeg = {
+ (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 {
+ (int) *__i = 1
+ }
+ }
+*/
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ static ConstString g_item_name;
+ if (!g_item_name)
+ g_item_name.SetCString("__i");
+ return (valobj_sp
+ ? new VectorIteratorSyntheticFrontEnd(valobj_sp, g_item_name)
+ : nullptr);
+}
+
+lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
+ LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr), m_count_sp(),
+ m_weak_count_sp(), m_ptr_size(0), m_byte_order(lldb::eByteOrderInvalid) {
+ if (valobj_sp)
+ Update();
+}
+
+size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
+ CalculateNumChildren() {
+ return (m_cntrl ? 1 : 0);
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ if (!m_cntrl)
+ return lldb::ValueObjectSP();
+
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return lldb::ValueObjectSP();
+
+ if (idx == 0)
+ return valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true);
+
+ if (idx > 2)
+ return lldb::ValueObjectSP();
+
+ if (idx == 1) {
+ if (!m_count_sp) {
+ ValueObjectSP shared_owners_sp(m_cntrl->GetChildMemberWithName(
+ ConstString("__shared_owners_"), true));
+ if (!shared_owners_sp)
+ return lldb::ValueObjectSP();
+ uint64_t count = 1 + shared_owners_sp->GetValueAsUnsigned(0);
+ DataExtractor data(&count, 8, m_byte_order, m_ptr_size);
+ m_count_sp = CreateValueObjectFromData(
+ "count", data, valobj_sp->GetExecutionContextRef(),
+ shared_owners_sp->GetCompilerType());
+ }
+ return m_count_sp;
+ } else /* if (idx == 2) */
+ {
+ if (!m_weak_count_sp) {
+ ValueObjectSP shared_weak_owners_sp(m_cntrl->GetChildMemberWithName(
+ ConstString("__shared_weak_owners_"), true));
+ if (!shared_weak_owners_sp)
+ return lldb::ValueObjectSP();
+ uint64_t count = 1 + shared_weak_owners_sp->GetValueAsUnsigned(0);
+ DataExtractor data(&count, 8, m_byte_order, m_ptr_size);
+ m_weak_count_sp = CreateValueObjectFromData(
+ "count", data, valobj_sp->GetExecutionContextRef(),
+ shared_weak_owners_sp->GetCompilerType());
+ }
+ return m_weak_count_sp;
+ }
+}
+
+bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
+ m_count_sp.reset();
+ m_weak_count_sp.reset();
+ m_cntrl = nullptr;
+
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+
+ TargetSP target_sp(valobj_sp->GetTargetSP());
+ if (!target_sp)
+ return false;
+
+ m_byte_order = target_sp->GetArchitecture().GetByteOrder();
+ m_ptr_size = target_sp->GetArchitecture().GetAddressByteSize();
+
+ lldb::ValueObjectSP cntrl_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__cntrl_"), true));
+
+ m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular
+ // dependency
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (name == "__ptr_")
+ return 0;
+ if (name == "count")
+ return 1;
+ if (name == "weak_count")
+ return 2;
+ return UINT32_MAX;
+}
+
+lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
+ ~LibcxxSharedPtrSyntheticFrontEnd() = default;
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
+
+bool lldb_private::formatters::LibcxxContainerSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ if (valobj.IsPointerType()) {
+ uint64_t value = valobj.GetValueAsUnsigned(0);
+ if (!value)
+ return false;
+ stream.Printf("0x%016" PRIx64 " ", value);
+ }
+ return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr,
+ nullptr, nullptr, &valobj, false, false);
+}
+
+// the field layout in a libc++ string (cap, side, data or data, size, cap)
+enum LibcxxStringLayoutMode {
+ eLibcxxStringLayoutModeCSD = 0,
+ eLibcxxStringLayoutModeDSC = 1,
+ eLibcxxStringLayoutModeInvalid = 0xffff
+};
+
+// this function abstracts away the layout and mode details of a libc++ string
+// and returns the address of the data and the size ready for callers to
+// consume
+static bool ExtractLibcxxStringInfo(ValueObject &valobj,
+ ValueObjectSP &location_sp,
+ uint64_t &size) {
+ ValueObjectSP D(valobj.GetChildAtIndexPath({0, 0, 0, 0}));
+ if (!D)
+ return false;
+
+ ValueObjectSP layout_decider(
+ D->GetChildAtIndexPath(llvm::ArrayRef<size_t>({0, 0})));
+
+ // this child should exist
+ if (!layout_decider)
+ return false;
+
+ ConstString g_data_name("__data_");
+ ConstString g_size_name("__size_");
+ bool short_mode = false; // this means the string is in short-mode and the
+ // data is stored inline
+ LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name)
+ ? eLibcxxStringLayoutModeDSC
+ : eLibcxxStringLayoutModeCSD;
+ uint64_t size_mode_value = 0;
+
+ if (layout == eLibcxxStringLayoutModeDSC) {
+ ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 1, 0}));
+ if (!size_mode)
+ return false;
+
+ if (size_mode->GetName() != g_size_name) {
+ // we are hitting the padding structure, move along
+ size_mode = D->GetChildAtIndexPath({1, 1, 1});
+ if (!size_mode)
+ return false;
+ }
+
+ size_mode_value = (size_mode->GetValueAsUnsigned(0));
+ short_mode = ((size_mode_value & 0x80) == 0);
+ } else {
+ ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 0, 0}));
+ if (!size_mode)
+ return false;
+
+ size_mode_value = (size_mode->GetValueAsUnsigned(0));
+ short_mode = ((size_mode_value & 1) == 0);
+ }
+
+ if (short_mode) {
+ ValueObjectSP s(D->GetChildAtIndex(1, true));
+ if (!s)
+ return false;
+ location_sp = s->GetChildAtIndex(
+ (layout == eLibcxxStringLayoutModeDSC) ? 0 : 1, true);
+ size = (layout == eLibcxxStringLayoutModeDSC)
+ ? size_mode_value
+ : ((size_mode_value >> 1) % 256);
+ return (location_sp.get() != nullptr);
+ } else {
+ ValueObjectSP l(D->GetChildAtIndex(0, true));
+ if (!l)
+ return false;
+ // we can use the layout_decider object as the data pointer
+ location_sp = (layout == eLibcxxStringLayoutModeDSC)
+ ? layout_decider
+ : l->GetChildAtIndex(2, true);
+ ValueObjectSP size_vo(l->GetChildAtIndex(1, true));
+ if (!size_vo || !location_sp)
+ return false;
+ size = size_vo->GetValueAsUnsigned(0);
+ return true;
+ }
+}
+
+bool lldb_private::formatters::LibcxxWStringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options) {
+ uint64_t size = 0;
+ ValueObjectSP location_sp;
+ if (!ExtractLibcxxStringInfo(valobj, location_sp, size))
+ return false;
+ if (size == 0) {
+ stream.Printf("L\"\"");
+ return true;
+ }
+ if (!location_sp)
+ return false;
+
+ DataExtractor extractor;
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
+
+ if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
+ const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
+ if (size > max_size) {
+ size = max_size;
+ options.SetIsTruncated(true);
+ }
+ }
+ location_sp->GetPointeeData(extractor, 0, size);
+
+ // std::wstring::size() is measured in 'characters', not bytes
+ auto wchar_t_size = valobj.GetTargetSP()
+ ->GetScratchClangASTContext()
+ ->GetBasicType(lldb::eBasicTypeWChar)
+ .GetByteSize(nullptr);
+ if (!wchar_t_size)
+ return false;
+
+ options.SetData(extractor);
+ options.SetStream(&stream);
+ options.SetPrefixToken("L");
+ options.SetQuote('"');
+ options.SetSourceSize(size);
+ options.SetBinaryZeroIsTerminator(false);
+
+ switch (*wchar_t_size) {
+ case 1:
+ StringPrinter::ReadBufferAndDumpToStream<
+ lldb_private::formatters::StringPrinter::StringElementType::UTF8>(
+ options);
+ break;
+
+ case 2:
+ lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream<
+ lldb_private::formatters::StringPrinter::StringElementType::UTF16>(
+ options);
+ break;
+
+ case 4:
+ lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream<
+ lldb_private::formatters::StringPrinter::StringElementType::UTF32>(
+ options);
+ break;
+
+ default:
+ stream.Printf("size for wchar_t is not valid");
+ return true;
+ }
+
+ return true;
+}
+
+template <StringPrinter::StringElementType element_type>
+bool LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options,
+ std::string prefix_token = "") {
+ uint64_t size = 0;
+ ValueObjectSP location_sp;
+
+ if (!ExtractLibcxxStringInfo(valobj, location_sp, size))
+ return false;
+
+ if (size == 0) {
+ stream.Printf("\"\"");
+ return true;
+ }
+
+ if (!location_sp)
+ return false;
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
+
+ DataExtractor extractor;
+ if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
+ const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
+ if (size > max_size) {
+ size = max_size;
+ options.SetIsTruncated(true);
+ }
+ }
+ location_sp->GetPointeeData(extractor, 0, size);
+
+ options.SetData(extractor);
+ options.SetStream(&stream);
+
+ if (prefix_token.empty())
+ options.SetPrefixToken(nullptr);
+ else
+ options.SetPrefixToken(prefix_token);
+
+ options.SetQuote('"');
+ options.SetSourceSize(size);
+ options.SetBinaryZeroIsTerminator(false);
+ StringPrinter::ReadBufferAndDumpToStream<element_type>(options);
+
+ return true;
+}
+
+bool lldb_private::formatters::LibcxxStringSummaryProviderASCII(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options) {
+ return LibcxxStringSummaryProvider<StringPrinter::StringElementType::ASCII>(
+ valobj, stream, summary_options);
+}
+
+bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options) {
+ return LibcxxStringSummaryProvider<StringPrinter::StringElementType::UTF16>(
+ valobj, stream, summary_options, "u");
+}
+
+bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options) {
+ return LibcxxStringSummaryProvider<StringPrinter::StringElementType::UTF32>(
+ valobj, stream, summary_options, "U");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
new file mode 100644
index 000000000000..214f5512e4a5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
@@ -0,0 +1,159 @@
+//===-- LibCxx.h ---------------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_LibCxx_h_
+#define liblldb_LibCxx_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+
+bool LibcxxStringSummaryProviderASCII(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options); // libc++ std::string
+
+bool LibcxxStringSummaryProviderUTF16(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options); // libc++ std::u16string
+
+bool LibcxxStringSummaryProviderUTF32(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options); // libc++ std::u32string
+
+bool LibcxxWStringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libc++ std::wstring
+
+bool LibcxxOptionalSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libc++ std::optional<>
+
+bool LibcxxSmartPointerSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions
+ &options); // libc++ std::shared_ptr<> and std::weak_ptr<>
+
+bool LibcxxFunctionSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libc++ std::function<>
+
+SyntheticChildrenFrontEnd *
+LibcxxVectorBoolSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+bool LibcxxContainerSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+ ~LibCxxMapIteratorSyntheticFrontEnd() override;
+
+private:
+ ValueObject *m_pair_ptr;
+ lldb::ValueObjectSP m_pair_sp;
+};
+
+SyntheticChildrenFrontEnd *
+LibCxxMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibCxxVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+class LibcxxSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+ ~LibcxxSharedPtrSyntheticFrontEnd() override;
+
+private:
+ ValueObject *m_cntrl;
+ lldb::ValueObjectSP m_count_sp;
+ lldb::ValueObjectSP m_weak_count_sp;
+ uint8_t m_ptr_size;
+ lldb::ByteOrder m_byte_order;
+};
+
+SyntheticChildrenFrontEnd *
+LibcxxBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxStdVectorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxStdListSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxStdForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxStdMapSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxInitializerListSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *LibcxxQueueFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *LibcxxTupleFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxOptionalFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
+SyntheticChildrenFrontEnd *
+LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_LibCxx_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp
new file mode 100644
index 000000000000..b4e7a1703e46
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp
@@ -0,0 +1,159 @@
+//===-- LibCxxAtomic.cpp ------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxxAtomic.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+//
+// We are supporting two versions of libc++ std::atomic
+//
+// Given std::atomic<int> i;
+//
+// The previous version of std::atomic was laid out like this
+//
+// (lldb) frame var -L -R i
+// 0x00007ffeefbff9a0: (std::__1::atomic<int>) i = {
+// 0x00007ffeefbff9a0: std::__1::__atomic_base<int, true> = {
+// 0x00007ffeefbff9a0: std::__1::__atomic_base<int, false> = {
+// 0x00007ffeefbff9a0: __a_ = 5
+// }
+// }
+// }
+//
+// In this case we need to obtain __a_ and the current version is laid out as so
+//
+// (lldb) frame var -L -R i
+// 0x00007ffeefbff9b0: (std::__1::atomic<int>) i = {
+// 0x00007ffeefbff9b0: std::__1::__atomic_base<int, true> = {
+// 0x00007ffeefbff9b0: std::__1::__atomic_base<int, false> = {
+// 0x00007ffeefbff9b0: __a_ = {
+// 0x00007ffeefbff9b0: std::__1::__cxx_atomic_base_impl<int> = {
+// 0x00007ffeefbff9b0: __a_value = 5
+// }
+// }
+// }
+// }
+//}
+//
+// In this case we need to obtain __a_value
+//
+// The below method covers both cases and returns the relevant member as a
+// ValueObjectSP
+//
+ValueObjectSP
+lldb_private::formatters::GetLibCxxAtomicValue(ValueObject &valobj) {
+ ValueObjectSP non_sythetic = valobj.GetNonSyntheticValue();
+ if (!non_sythetic)
+ return {};
+
+ ValueObjectSP member__a_ =
+ non_sythetic->GetChildMemberWithName(ConstString("__a_"), true);
+ if (!member__a_)
+ return {};
+
+ ValueObjectSP member__a_value =
+ member__a_->GetChildMemberWithName(ConstString("__a_value"), true);
+ if (!member__a_value)
+ return member__a_;
+
+ return member__a_value;
+}
+
+bool lldb_private::formatters::LibCxxAtomicSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+
+ if (ValueObjectSP atomic_value = GetLibCxxAtomicValue(valobj)) {
+ std::string summary;
+ if (atomic_value->GetSummaryAsCString(summary, options) &&
+ summary.size() > 0) {
+ stream.Printf("%s", summary.c_str());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+namespace lldb_private {
+namespace formatters {
+class LibcxxStdAtomicSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxStdAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~LibcxxStdAtomicSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+ lldb::ValueObjectSP GetSyntheticValue() override;
+
+private:
+ ValueObject *m_real_child;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::
+ LibcxxStdAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_real_child(nullptr) {}
+
+bool lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::Update() {
+ ValueObjectSP atomic_value = GetLibCxxAtomicValue(m_backend);
+ if (atomic_value)
+ m_real_child = GetLibCxxAtomicValue(m_backend).get();
+
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::
+ CalculateNumChildren() {
+ return m_real_child ? m_real_child->GetNumChildren() : 0;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ return m_real_child ? m_real_child->GetChildAtIndex(idx, true) : nullptr;
+}
+
+size_t lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ return m_real_child ? m_real_child->GetIndexOfChildWithName(name)
+ : UINT32_MAX;
+}
+
+lldb::ValueObjectSP lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::
+ GetSyntheticValue() {
+ if (m_real_child && m_real_child->CanProvideValue())
+ return m_real_child->GetSP();
+ return nullptr;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new LibcxxStdAtomicSyntheticFrontEnd(valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h
new file mode 100644
index 000000000000..8be833dd82f6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h
@@ -0,0 +1,33 @@
+//===-- LibCxxAtomic.h -------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_LibCxxAtomic_h_
+#define liblldb_LibCxxAtomic_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+
+lldb::ValueObjectSP GetLibCxxAtomicValue(ValueObject &valobj);
+
+bool LibCxxAtomicSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+SyntheticChildrenFrontEnd *
+LibcxxAtomicSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_LibCxxAtomic_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp
new file mode 100644
index 000000000000..815dafb6c724
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp
@@ -0,0 +1,113 @@
+//===-- LibCxxBitset.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxx.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class BitsetFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ BitsetFrontEnd(ValueObject &valobj);
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return formatters::ExtractIndexFromString(name.GetCString());
+ }
+
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+ size_t CalculateNumChildren() override { return m_elements.size(); }
+ ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+private:
+ std::vector<ValueObjectSP> m_elements;
+ ValueObjectSP m_first;
+ CompilerType m_bool_type;
+ ByteOrder m_byte_order = eByteOrderInvalid;
+ uint8_t m_byte_size = 0;
+};
+} // namespace
+
+BitsetFrontEnd::BitsetFrontEnd(ValueObject &valobj)
+ : SyntheticChildrenFrontEnd(valobj) {
+ m_bool_type = valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeBool);
+ if (auto target_sp = m_backend.GetTargetSP()) {
+ m_byte_order = target_sp->GetArchitecture().GetByteOrder();
+ m_byte_size = target_sp->GetArchitecture().GetAddressByteSize();
+ Update();
+ }
+}
+
+bool BitsetFrontEnd::Update() {
+ m_elements.clear();
+ m_first.reset();
+
+ TargetSP target_sp = m_backend.GetTargetSP();
+ if (!target_sp)
+ return false;
+ size_t capping_size = target_sp->GetMaximumNumberOfChildrenToDisplay();
+
+ size_t size = 0;
+ if (auto arg = m_backend.GetCompilerType().GetIntegralTemplateArgument(0))
+ size = arg->value.getLimitedValue(capping_size);
+
+ m_elements.assign(size, ValueObjectSP());
+
+ m_first = m_backend.GetChildMemberWithName(ConstString("__first_"), true);
+ return false;
+}
+
+ValueObjectSP BitsetFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= m_elements.size() || !m_first)
+ return ValueObjectSP();
+
+ if (m_elements[idx])
+ return m_elements[idx];
+
+ ExecutionContext ctx = m_backend.GetExecutionContextRef().Lock(false);
+ CompilerType type;
+ ValueObjectSP chunk;
+ // For small bitsets __first_ is not an array, but a plain size_t.
+ if (m_first->GetCompilerType().IsArrayType(&type, nullptr, nullptr)) {
+ llvm::Optional<uint64_t> bit_size =
+ type.GetBitSize(ctx.GetBestExecutionContextScope());
+ if (!bit_size || *bit_size == 0)
+ return {};
+ chunk = m_first->GetChildAtIndex(idx / *bit_size, true);
+ } else {
+ type = m_first->GetCompilerType();
+ chunk = m_first;
+ }
+ if (!type || !chunk)
+ return {};
+
+ llvm::Optional<uint64_t> bit_size =
+ type.GetBitSize(ctx.GetBestExecutionContextScope());
+ if (!bit_size || *bit_size == 0)
+ return {};
+ size_t chunk_idx = idx % *bit_size;
+ uint8_t value = !!(chunk->GetValueAsUnsigned(0) & (uint64_t(1) << chunk_idx));
+ DataExtractor data(&value, sizeof(value), m_byte_order, m_byte_size);
+
+ m_elements[idx] = CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(),
+ data, ctx, m_bool_type);
+
+ return m_elements[idx];
+}
+
+SyntheticChildrenFrontEnd *formatters::LibcxxBitsetSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new BitsetFrontEnd(*valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp
new file mode 100644
index 000000000000..79c7434f617f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp
@@ -0,0 +1,122 @@
+//===-- LibCxxInitializerList.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxx.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Utility/ConstString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace lldb_private {
+namespace formatters {
+class LibcxxInitializerListSyntheticFrontEnd
+ : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~LibcxxInitializerListSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ ValueObject *m_start;
+ CompilerType m_element_type;
+ uint32_t m_element_size;
+ size_t m_num_elements;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ LibcxxInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_start(nullptr), m_element_type(),
+ m_element_size(0), m_num_elements(0) {
+ if (valobj_sp)
+ Update();
+}
+
+lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ ~LibcxxInitializerListSyntheticFrontEnd() {
+ // this needs to stay around because it's a child object who will follow its
+ // parent's life cycle
+ // delete m_start;
+}
+
+size_t lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ CalculateNumChildren() {
+ static ConstString g___size_("__size_");
+ m_num_elements = 0;
+ ValueObjectSP size_sp(m_backend.GetChildMemberWithName(g___size_, true));
+ if (size_sp)
+ m_num_elements = size_sp->GetValueAsUnsigned(0);
+ return m_num_elements;
+}
+
+lldb::ValueObjectSP lldb_private::formatters::
+ LibcxxInitializerListSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (!m_start)
+ return lldb::ValueObjectSP();
+
+ uint64_t offset = idx * m_element_size;
+ offset = offset + m_start->GetValueAsUnsigned(0);
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return CreateValueObjectFromAddress(name.GetString(), offset,
+ m_backend.GetExecutionContextRef(),
+ m_element_type);
+}
+
+bool lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ Update() {
+ static ConstString g___begin_("__begin_");
+
+ m_start = nullptr;
+ m_num_elements = 0;
+ m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0);
+ if (!m_element_type.IsValid())
+ return false;
+
+ if (llvm::Optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) {
+ m_element_size = *size;
+ // Store raw pointers or end up with a circular dependency.
+ m_start = m_backend.GetChildMemberWithName(g___begin_, true).get();
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (!m_start)
+ return UINT32_MAX;
+ return ExtractIndexFromString(name.GetCString());
+}
+
+lldb_private::SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibcxxInitializerListSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp
new file mode 100644
index 000000000000..f5281f2ce532
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp
@@ -0,0 +1,444 @@
+//===-- LibCxxList.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxx.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace {
+
+class ListEntry {
+public:
+ ListEntry() = default;
+ ListEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
+ ListEntry(const ListEntry &rhs) = default;
+ ListEntry(ValueObject *entry)
+ : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
+
+ ListEntry next() {
+ static ConstString g_next("__next_");
+
+ if (!m_entry_sp)
+ return ListEntry();
+ return ListEntry(m_entry_sp->GetChildMemberWithName(g_next, true));
+ }
+
+ ListEntry prev() {
+ static ConstString g_prev("__prev_");
+
+ if (!m_entry_sp)
+ return ListEntry();
+ return ListEntry(m_entry_sp->GetChildMemberWithName(g_prev, true));
+ }
+
+ uint64_t value() const {
+ if (!m_entry_sp)
+ return 0;
+ return m_entry_sp->GetValueAsUnsigned(0);
+ }
+
+ bool null() { return (value() == 0); }
+
+ explicit operator bool() { return GetEntry() && !null(); }
+
+ ValueObjectSP GetEntry() { return m_entry_sp; }
+
+ void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }
+
+ bool operator==(const ListEntry &rhs) const { return value() == rhs.value(); }
+
+ bool operator!=(const ListEntry &rhs) const { return !(*this == rhs); }
+
+private:
+ ValueObjectSP m_entry_sp;
+};
+
+class ListIterator {
+public:
+ ListIterator() = default;
+ ListIterator(ListEntry entry) : m_entry(entry) {}
+ ListIterator(ValueObjectSP entry) : m_entry(entry) {}
+ ListIterator(const ListIterator &rhs) = default;
+ ListIterator(ValueObject *entry) : m_entry(entry) {}
+
+ ValueObjectSP value() { return m_entry.GetEntry(); }
+
+ ValueObjectSP advance(size_t count) {
+ if (count == 0)
+ return m_entry.GetEntry();
+ if (count == 1) {
+ next();
+ return m_entry.GetEntry();
+ }
+ while (count > 0) {
+ next();
+ count--;
+ if (m_entry.null())
+ return lldb::ValueObjectSP();
+ }
+ return m_entry.GetEntry();
+ }
+
+ bool operator==(const ListIterator &rhs) const {
+ return (rhs.m_entry == m_entry);
+ }
+
+protected:
+ void next() { m_entry = m_entry.next(); }
+
+ void prev() { m_entry = m_entry.prev(); }
+
+private:
+ ListEntry m_entry;
+};
+
+class AbstractListFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return ExtractIndexFromString(name.GetCString());
+ }
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+
+protected:
+ AbstractListFrontEnd(ValueObject &valobj)
+ : SyntheticChildrenFrontEnd(valobj) {}
+
+ size_t m_count;
+ ValueObject *m_head;
+
+ static constexpr bool g_use_loop_detect = true;
+ size_t m_loop_detected; // The number of elements that have had loop detection
+ // run over them.
+ ListEntry m_slow_runner; // Used for loop detection
+ ListEntry m_fast_runner; // Used for loop detection
+
+ size_t m_list_capping_size;
+ CompilerType m_element_type;
+ std::map<size_t, ListIterator> m_iterators;
+
+ bool HasLoop(size_t count);
+ ValueObjectSP GetItem(size_t idx);
+};
+
+class ForwardListFrontEnd : public AbstractListFrontEnd {
+public:
+ ForwardListFrontEnd(ValueObject &valobj);
+
+ size_t CalculateNumChildren() override;
+ ValueObjectSP GetChildAtIndex(size_t idx) override;
+ bool Update() override;
+};
+
+class ListFrontEnd : public AbstractListFrontEnd {
+public:
+ ListFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~ListFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+private:
+ lldb::addr_t m_node_address;
+ ValueObject *m_tail;
+};
+
+} // end anonymous namespace
+
+bool AbstractListFrontEnd::Update() {
+ m_loop_detected = 0;
+ m_count = UINT32_MAX;
+ m_head = nullptr;
+ m_list_capping_size = 0;
+ m_slow_runner.SetEntry(nullptr);
+ m_fast_runner.SetEntry(nullptr);
+ m_iterators.clear();
+
+ if (m_backend.GetTargetSP())
+ m_list_capping_size =
+ m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
+ if (m_list_capping_size == 0)
+ m_list_capping_size = 255;
+
+ CompilerType list_type = m_backend.GetCompilerType();
+ if (list_type.IsReferenceType())
+ list_type = list_type.GetNonReferenceType();
+
+ if (list_type.GetNumTemplateArguments() == 0)
+ return false;
+ m_element_type = list_type.GetTypeTemplateArgument(0);
+
+ return false;
+}
+
+bool AbstractListFrontEnd::HasLoop(size_t count) {
+ if (!g_use_loop_detect)
+ return false;
+ // don't bother checking for a loop if we won't actually need to jump nodes
+ if (m_count < 2)
+ return false;
+
+ if (m_loop_detected == 0) {
+ // This is the first time we are being run (after the last update). Set up
+ // the loop invariant for the first element.
+ m_slow_runner = ListEntry(m_head).next();
+ m_fast_runner = m_slow_runner.next();
+ m_loop_detected = 1;
+ }
+
+ // Loop invariant:
+ // Loop detection has been run over the first m_loop_detected elements. If
+ // m_slow_runner == m_fast_runner then the loop has been detected after
+ // m_loop_detected elements.
+ const size_t steps_to_run = std::min(count, m_count);
+ while (m_loop_detected < steps_to_run && m_slow_runner && m_fast_runner &&
+ m_slow_runner != m_fast_runner) {
+
+ m_slow_runner = m_slow_runner.next();
+ m_fast_runner = m_fast_runner.next().next();
+ m_loop_detected++;
+ }
+ if (count <= m_loop_detected)
+ return false; // No loop in the first m_loop_detected elements.
+ if (!m_slow_runner || !m_fast_runner)
+ return false; // Reached the end of the list. Definitely no loops.
+ return m_slow_runner == m_fast_runner;
+}
+
+ValueObjectSP AbstractListFrontEnd::GetItem(size_t idx) {
+ size_t advance = idx;
+ ListIterator current(m_head);
+ if (idx > 0) {
+ auto cached_iterator = m_iterators.find(idx - 1);
+ if (cached_iterator != m_iterators.end()) {
+ current = cached_iterator->second;
+ advance = 1;
+ }
+ }
+ ValueObjectSP value_sp = current.advance(advance);
+ m_iterators[idx] = current;
+ return value_sp;
+}
+
+ForwardListFrontEnd::ForwardListFrontEnd(ValueObject &valobj)
+ : AbstractListFrontEnd(valobj) {
+ Update();
+}
+
+size_t ForwardListFrontEnd::CalculateNumChildren() {
+ if (m_count != UINT32_MAX)
+ return m_count;
+
+ ListEntry current(m_head);
+ m_count = 0;
+ while (current && m_count < m_list_capping_size) {
+ ++m_count;
+ current = current.next();
+ }
+ return m_count;
+}
+
+ValueObjectSP ForwardListFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= CalculateNumChildren())
+ return nullptr;
+
+ if (!m_head)
+ return nullptr;
+
+ if (HasLoop(idx + 1))
+ return nullptr;
+
+ ValueObjectSP current_sp = GetItem(idx);
+ if (!current_sp)
+ return nullptr;
+
+ current_sp = current_sp->GetChildAtIndex(1, true); // get the __value_ child
+ if (!current_sp)
+ return nullptr;
+
+ // we need to copy current_sp into a new object otherwise we will end up with
+ // all items named __value_
+ DataExtractor data;
+ Status error;
+ current_sp->GetData(data, error);
+ if (error.Fail())
+ return nullptr;
+
+ return CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), data,
+ m_backend.GetExecutionContextRef(),
+ m_element_type);
+}
+
+static ValueObjectSP GetValueOfCompressedPair(ValueObject &pair) {
+ ValueObjectSP value = pair.GetChildMemberWithName(ConstString("__value_"), true);
+ if (! value) {
+ // pre-r300140 member name
+ value = pair.GetChildMemberWithName(ConstString("__first_"), true);
+ }
+ return value;
+}
+
+bool ForwardListFrontEnd::Update() {
+ AbstractListFrontEnd::Update();
+
+ Status err;
+ ValueObjectSP backend_addr(m_backend.AddressOf(err));
+ if (err.Fail() || !backend_addr)
+ return false;
+
+ ValueObjectSP impl_sp(
+ m_backend.GetChildMemberWithName(ConstString("__before_begin_"), true));
+ if (!impl_sp)
+ return false;
+ impl_sp = GetValueOfCompressedPair(*impl_sp);
+ if (!impl_sp)
+ return false;
+ m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
+ return false;
+}
+
+ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : AbstractListFrontEnd(*valobj_sp), m_node_address(), m_tail(nullptr) {
+ if (valobj_sp)
+ Update();
+}
+
+size_t ListFrontEnd::CalculateNumChildren() {
+ if (m_count != UINT32_MAX)
+ return m_count;
+ if (!m_head || !m_tail || m_node_address == 0)
+ return 0;
+ ValueObjectSP size_alloc(
+ m_backend.GetChildMemberWithName(ConstString("__size_alloc_"), true));
+ if (size_alloc) {
+ ValueObjectSP value = GetValueOfCompressedPair(*size_alloc);
+ if (value) {
+ m_count = value->GetValueAsUnsigned(UINT32_MAX);
+ }
+ }
+ if (m_count != UINT32_MAX) {
+ return m_count;
+ } else {
+ uint64_t next_val = m_head->GetValueAsUnsigned(0);
+ uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
+ if (next_val == 0 || prev_val == 0)
+ return 0;
+ if (next_val == m_node_address)
+ return 0;
+ if (next_val == prev_val)
+ return 1;
+ uint64_t size = 2;
+ ListEntry current(m_head);
+ while (current.next() && current.next().value() != m_node_address) {
+ size++;
+ current = current.next();
+ if (size > m_list_capping_size)
+ break;
+ }
+ return m_count = (size - 1);
+ }
+}
+
+lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(size_t idx) {
+ static ConstString g_value("__value_");
+ static ConstString g_next("__next_");
+
+ if (idx >= CalculateNumChildren())
+ return lldb::ValueObjectSP();
+
+ if (!m_head || !m_tail || m_node_address == 0)
+ return lldb::ValueObjectSP();
+
+ if (HasLoop(idx + 1))
+ return lldb::ValueObjectSP();
+
+ ValueObjectSP current_sp = GetItem(idx);
+ if (!current_sp)
+ return lldb::ValueObjectSP();
+
+ current_sp = current_sp->GetChildAtIndex(1, true); // get the __value_ child
+ if (!current_sp)
+ return lldb::ValueObjectSP();
+
+ if (current_sp->GetName() == g_next) {
+ ProcessSP process_sp(current_sp->GetProcessSP());
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+
+ // if we grabbed the __next_ pointer, then the child is one pointer deep-er
+ lldb::addr_t addr = current_sp->GetParent()->GetPointerValue();
+ addr = addr + 2 * process_sp->GetAddressByteSize();
+ ExecutionContext exe_ctx(process_sp);
+ current_sp =
+ CreateValueObjectFromAddress("__value_", addr, exe_ctx, m_element_type);
+ if (!current_sp)
+ return lldb::ValueObjectSP();
+ }
+
+ // we need to copy current_sp into a new object otherwise we will end up with
+ // all items named __value_
+ DataExtractor data;
+ Status error;
+ current_sp->GetData(data, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return CreateValueObjectFromData(name.GetString(), data,
+ m_backend.GetExecutionContextRef(),
+ m_element_type);
+}
+
+bool ListFrontEnd::Update() {
+ AbstractListFrontEnd::Update();
+ m_tail = nullptr;
+ m_node_address = 0;
+
+ Status err;
+ ValueObjectSP backend_addr(m_backend.AddressOf(err));
+ if (err.Fail() || !backend_addr)
+ return false;
+ m_node_address = backend_addr->GetValueAsUnsigned(0);
+ if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS)
+ return false;
+ ValueObjectSP impl_sp(
+ m_backend.GetChildMemberWithName(ConstString("__end_"), true));
+ if (!impl_sp)
+ return false;
+ m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
+ m_tail = impl_sp->GetChildMemberWithName(ConstString("__prev_"), true).get();
+ return false;
+}
+
+SyntheticChildrenFrontEnd *formatters::LibcxxStdListSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new ListFrontEnd(valobj_sp) : nullptr);
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxStdForwardListSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return valobj_sp ? new ForwardListFrontEnd(*valobj_sp) : nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
new file mode 100644
index 000000000000..619c718a1c1b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
@@ -0,0 +1,463 @@
+//===-- LibCxxMap.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxx.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+class MapEntry {
+public:
+ MapEntry() = default;
+ explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
+ MapEntry(const MapEntry &rhs) = default;
+ explicit MapEntry(ValueObject *entry)
+ : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
+
+ ValueObjectSP left() const {
+ static ConstString g_left("__left_");
+ if (!m_entry_sp)
+ return m_entry_sp;
+ return m_entry_sp->GetSyntheticChildAtOffset(
+ 0, m_entry_sp->GetCompilerType(), true);
+ }
+
+ ValueObjectSP right() const {
+ static ConstString g_right("__right_");
+ if (!m_entry_sp)
+ return m_entry_sp;
+ return m_entry_sp->GetSyntheticChildAtOffset(
+ m_entry_sp->GetProcessSP()->GetAddressByteSize(),
+ m_entry_sp->GetCompilerType(), true);
+ }
+
+ ValueObjectSP parent() const {
+ static ConstString g_parent("__parent_");
+ if (!m_entry_sp)
+ return m_entry_sp;
+ return m_entry_sp->GetSyntheticChildAtOffset(
+ 2 * m_entry_sp->GetProcessSP()->GetAddressByteSize(),
+ m_entry_sp->GetCompilerType(), true);
+ }
+
+ uint64_t value() const {
+ if (!m_entry_sp)
+ return 0;
+ return m_entry_sp->GetValueAsUnsigned(0);
+ }
+
+ bool error() const {
+ if (!m_entry_sp)
+ return true;
+ return m_entry_sp->GetError().Fail();
+ }
+
+ bool null() const { return (value() == 0); }
+
+ ValueObjectSP GetEntry() const { return m_entry_sp; }
+
+ void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }
+
+ bool operator==(const MapEntry &rhs) const {
+ return (rhs.m_entry_sp.get() == m_entry_sp.get());
+ }
+
+private:
+ ValueObjectSP m_entry_sp;
+};
+
+class MapIterator {
+public:
+ MapIterator() = default;
+ MapIterator(MapEntry entry, size_t depth = 0)
+ : m_entry(entry), m_max_depth(depth), m_error(false) {}
+ MapIterator(ValueObjectSP entry, size_t depth = 0)
+ : m_entry(entry), m_max_depth(depth), m_error(false) {}
+ MapIterator(const MapIterator &rhs)
+ : m_entry(rhs.m_entry), m_max_depth(rhs.m_max_depth), m_error(false) {}
+ MapIterator(ValueObject *entry, size_t depth = 0)
+ : m_entry(entry), m_max_depth(depth), m_error(false) {}
+
+ ValueObjectSP value() { return m_entry.GetEntry(); }
+
+ ValueObjectSP advance(size_t count) {
+ ValueObjectSP fail;
+ if (m_error)
+ return fail;
+ size_t steps = 0;
+ while (count > 0) {
+ next();
+ count--, steps++;
+ if (m_error || m_entry.null() || (steps > m_max_depth))
+ return fail;
+ }
+ return m_entry.GetEntry();
+ }
+
+protected:
+ void next() {
+ if (m_entry.null())
+ return;
+ MapEntry right(m_entry.right());
+ if (!right.null()) {
+ m_entry = tree_min(std::move(right));
+ return;
+ }
+ size_t steps = 0;
+ while (!is_left_child(m_entry)) {
+ if (m_entry.error()) {
+ m_error = true;
+ return;
+ }
+ m_entry.SetEntry(m_entry.parent());
+ steps++;
+ if (steps > m_max_depth) {
+ m_entry = MapEntry();
+ return;
+ }
+ }
+ m_entry = MapEntry(m_entry.parent());
+ }
+
+private:
+ MapEntry tree_min(MapEntry &&x) {
+ if (x.null())
+ return MapEntry();
+ MapEntry left(x.left());
+ size_t steps = 0;
+ while (!left.null()) {
+ if (left.error()) {
+ m_error = true;
+ return MapEntry();
+ }
+ x = left;
+ left.SetEntry(x.left());
+ steps++;
+ if (steps > m_max_depth)
+ return MapEntry();
+ }
+ return x;
+ }
+
+ bool is_left_child(const MapEntry &x) {
+ if (x.null())
+ return false;
+ MapEntry rhs(x.parent());
+ rhs.SetEntry(rhs.left());
+ return x.value() == rhs.value();
+ }
+
+ MapEntry m_entry;
+ size_t m_max_depth;
+ bool m_error;
+};
+
+namespace lldb_private {
+namespace formatters {
+class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~LibcxxStdMapSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ bool GetDataType();
+
+ void GetValueOffset(const lldb::ValueObjectSP &node);
+
+ ValueObject *m_tree;
+ ValueObject *m_root_node;
+ CompilerType m_element_type;
+ uint32_t m_skip_size;
+ size_t m_count;
+ std::map<size_t, MapIterator> m_iterators;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
+ LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_tree(nullptr),
+ m_root_node(nullptr), m_element_type(), m_skip_size(UINT32_MAX),
+ m_count(UINT32_MAX), m_iterators() {
+ if (valobj_sp)
+ Update();
+}
+
+size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
+ CalculateNumChildren() {
+ static ConstString g___pair3_("__pair3_");
+ static ConstString g___first_("__first_");
+ static ConstString g___value_("__value_");
+
+ if (m_count != UINT32_MAX)
+ return m_count;
+ if (m_tree == nullptr)
+ return 0;
+ ValueObjectSP m_item(m_tree->GetChildMemberWithName(g___pair3_, true));
+ if (!m_item)
+ return 0;
+
+ switch (m_item->GetCompilerType().GetNumDirectBaseClasses()) {
+ case 1:
+ // Assume a pre llvm r300140 __compressed_pair implementation:
+ m_item = m_item->GetChildMemberWithName(g___first_, true);
+ break;
+ case 2: {
+ // Assume a post llvm r300140 __compressed_pair implementation:
+ ValueObjectSP first_elem_parent = m_item->GetChildAtIndex(0, true);
+ m_item = first_elem_parent->GetChildMemberWithName(g___value_, true);
+ break;
+ }
+ default:
+ return false;
+ }
+
+ if (!m_item)
+ return 0;
+ m_count = m_item->GetValueAsUnsigned(0);
+ return m_count;
+}
+
+bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() {
+ static ConstString g___value_("__value_");
+ static ConstString g_tree_("__tree_");
+ static ConstString g_pair3("__pair3_");
+
+ if (m_element_type.GetOpaqueQualType() && m_element_type.GetTypeSystem())
+ return true;
+ m_element_type.Clear();
+ ValueObjectSP deref;
+ Status error;
+ deref = m_root_node->Dereference(error);
+ if (!deref || error.Fail())
+ return false;
+ deref = deref->GetChildMemberWithName(g___value_, true);
+ if (deref) {
+ m_element_type = deref->GetCompilerType();
+ return true;
+ }
+ deref = m_backend.GetChildAtNamePath({g_tree_, g_pair3});
+ if (!deref)
+ return false;
+ m_element_type = deref->GetCompilerType()
+ .GetTypeTemplateArgument(1)
+ .GetTypeTemplateArgument(1);
+ if (m_element_type) {
+ std::string name;
+ uint64_t bit_offset_ptr;
+ uint32_t bitfield_bit_size_ptr;
+ bool is_bitfield_ptr;
+ m_element_type = m_element_type.GetFieldAtIndex(
+ 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr);
+ m_element_type = m_element_type.GetTypedefedType();
+ return m_element_type.IsValid();
+ } else {
+ m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0);
+ return m_element_type.IsValid();
+ }
+}
+
+void lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetValueOffset(
+ const lldb::ValueObjectSP &node) {
+ if (m_skip_size != UINT32_MAX)
+ return;
+ if (!node)
+ return;
+ CompilerType node_type(node->GetCompilerType());
+ uint64_t bit_offset;
+ if (node_type.GetIndexOfFieldWithName("__value_", nullptr, &bit_offset) !=
+ UINT32_MAX) {
+ m_skip_size = bit_offset / 8u;
+ } else {
+ ClangASTContext *ast_ctx =
+ llvm::dyn_cast_or_null<ClangASTContext>(node_type.GetTypeSystem());
+ if (!ast_ctx)
+ return;
+ CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(
+ ConstString(),
+ {{"ptr0", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"ptr1", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"ptr2", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)},
+ {"payload", (m_element_type.GetCompleteType(), m_element_type)}});
+ std::string child_name;
+ uint32_t child_byte_size;
+ int32_t child_byte_offset = 0;
+ uint32_t child_bitfield_bit_size;
+ uint32_t child_bitfield_bit_offset;
+ bool child_is_base_class;
+ bool child_is_deref_of_parent;
+ uint64_t language_flags;
+ if (tree_node_type
+ .GetChildCompilerTypeAtIndex(
+ nullptr, 4, true, true, true, child_name, child_byte_size,
+ child_byte_offset, child_bitfield_bit_size,
+ child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, nullptr, language_flags)
+ .IsValid())
+ m_skip_size = (uint32_t)child_byte_offset;
+ }
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ static ConstString g___cc("__cc");
+ static ConstString g___nc("__nc");
+ static ConstString g___value_("__value_");
+
+ if (idx >= CalculateNumChildren())
+ return lldb::ValueObjectSP();
+ if (m_tree == nullptr || m_root_node == nullptr)
+ return lldb::ValueObjectSP();
+
+ MapIterator iterator(m_root_node, CalculateNumChildren());
+
+ const bool need_to_skip = (idx > 0);
+ size_t actual_advancde = idx;
+ if (need_to_skip) {
+ auto cached_iterator = m_iterators.find(idx - 1);
+ if (cached_iterator != m_iterators.end()) {
+ iterator = cached_iterator->second;
+ actual_advancde = 1;
+ }
+ }
+
+ ValueObjectSP iterated_sp(iterator.advance(actual_advancde));
+ if (!iterated_sp) {
+ // this tree is garbage - stop
+ m_tree =
+ nullptr; // this will stop all future searches until an Update() happens
+ return iterated_sp;
+ }
+ if (GetDataType()) {
+ if (!need_to_skip) {
+ Status error;
+ iterated_sp = iterated_sp->Dereference(error);
+ if (!iterated_sp || error.Fail()) {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ GetValueOffset(iterated_sp);
+ auto child_sp = iterated_sp->GetChildMemberWithName(g___value_, true);
+ if (child_sp)
+ iterated_sp = child_sp;
+ else
+ iterated_sp = iterated_sp->GetSyntheticChildAtOffset(
+ m_skip_size, m_element_type, true);
+ if (!iterated_sp) {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ } else {
+ // because of the way our debug info is made, we need to read item 0
+ // first so that we can cache information used to generate other elements
+ if (m_skip_size == UINT32_MAX)
+ GetChildAtIndex(0);
+ if (m_skip_size == UINT32_MAX) {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ iterated_sp = iterated_sp->GetSyntheticChildAtOffset(
+ m_skip_size, m_element_type, true);
+ if (!iterated_sp) {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ }
+ } else {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ // at this point we have a valid
+ // we need to copy current_sp into a new object otherwise we will end up with
+ // all items named __value_
+ DataExtractor data;
+ Status error;
+ iterated_sp->GetData(data, error);
+ if (error.Fail()) {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ auto potential_child_sp = CreateValueObjectFromData(
+ name.GetString(), data, m_backend.GetExecutionContextRef(),
+ m_element_type);
+ if (potential_child_sp) {
+ switch (potential_child_sp->GetNumChildren()) {
+ case 1: {
+ auto child0_sp = potential_child_sp->GetChildAtIndex(0, true);
+ if (child0_sp && child0_sp->GetName() == g___cc)
+ potential_child_sp = child0_sp->Clone(ConstString(name.GetString()));
+ break;
+ }
+ case 2: {
+ auto child0_sp = potential_child_sp->GetChildAtIndex(0, true);
+ auto child1_sp = potential_child_sp->GetChildAtIndex(1, true);
+ if (child0_sp && child0_sp->GetName() == g___cc && child1_sp &&
+ child1_sp->GetName() == g___nc)
+ potential_child_sp = child0_sp->Clone(ConstString(name.GetString()));
+ break;
+ }
+ }
+ }
+ m_iterators[idx] = iterator;
+ return potential_child_sp;
+}
+
+bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() {
+ static ConstString g___tree_("__tree_");
+ static ConstString g___begin_node_("__begin_node_");
+ m_count = UINT32_MAX;
+ m_tree = m_root_node = nullptr;
+ m_iterators.clear();
+ m_tree = m_backend.GetChildMemberWithName(g___tree_, true).get();
+ if (!m_tree)
+ return false;
+ m_root_node = m_tree->GetChildMemberWithName(g___begin_node_, true).get();
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ return ExtractIndexFromString(name.GetCString());
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibcxxStdMapSyntheticFrontEnd(valobj_sp) : nullptr);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxOptional.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxOptional.cpp
new file mode 100644
index 000000000000..116021588848
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxOptional.cpp
@@ -0,0 +1,84 @@
+//===-- LibCxxOptional.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxx.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class OptionalFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ OptionalFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
+ Update();
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return formatters::ExtractIndexFromString(name.GetCString());
+ }
+
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+ size_t CalculateNumChildren() override { return m_size; }
+ ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+private:
+ size_t m_size = 0;
+ ValueObjectSP m_base_sp;
+};
+} // namespace
+
+bool OptionalFrontEnd::Update() {
+ ValueObjectSP engaged_sp(
+ m_backend.GetChildMemberWithName(ConstString("__engaged_"), true));
+
+ if (!engaged_sp)
+ return false;
+
+ // __engaged_ is a bool flag and is true if the optional contains a value.
+ // Converting it to unsigned gives us a size of 1 if it contains a value
+ // and 0 if not.
+ m_size = engaged_sp->GetValueAsUnsigned(0);
+
+ return false;
+}
+
+ValueObjectSP OptionalFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= m_size)
+ return ValueObjectSP();
+
+ // __val_ contains the underlying value of an optional if it has one.
+ // Currently because it is part of an anonymous union GetChildMemberWithName()
+ // does not peer through and find it unless we are at the parent itself.
+ // We can obtain the parent through __engaged_.
+ ValueObjectSP val_sp(
+ m_backend.GetChildMemberWithName(ConstString("__engaged_"), true)
+ ->GetParent()
+ ->GetChildAtIndex(0, true)
+ ->GetChildMemberWithName(ConstString("__val_"), true));
+
+ if (!val_sp)
+ return ValueObjectSP();
+
+ CompilerType holder_type = val_sp->GetCompilerType();
+
+ if (!holder_type)
+ return ValueObjectSP();
+
+ return val_sp->Clone(ConstString(llvm::formatv("Value").str()));
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxOptionalFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new OptionalFrontEnd(*valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp
new file mode 100644
index 000000000000..4b72089c6ba2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp
@@ -0,0 +1,60 @@
+//===-- LibCxxQueue.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxx.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class QueueFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ QueueFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
+ Update();
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return m_container_sp ? m_container_sp->GetIndexOfChildWithName(name)
+ : UINT32_MAX;
+ }
+
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+
+ size_t CalculateNumChildren() override {
+ return m_container_sp ? m_container_sp->GetNumChildren() : 0;
+ }
+
+ ValueObjectSP GetChildAtIndex(size_t idx) override {
+ return m_container_sp ? m_container_sp->GetChildAtIndex(idx, true)
+ : nullptr;
+ }
+
+private:
+ ValueObjectSP m_container_sp;
+};
+} // namespace
+
+bool QueueFrontEnd::Update() {
+ m_container_sp.reset();
+ ValueObjectSP c_sp = m_backend.GetChildMemberWithName(ConstString("c"), true);
+ if (!c_sp)
+ return false;
+ m_container_sp = c_sp->GetSyntheticValue();
+ return false;
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxQueueFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new QueueFrontEnd(*valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp
new file mode 100644
index 000000000000..8da7460f2275
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp
@@ -0,0 +1,82 @@
+//===-- LibCxxTuple.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxx.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class TupleFrontEnd: public SyntheticChildrenFrontEnd {
+public:
+ TupleFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
+ Update();
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return formatters::ExtractIndexFromString(name.GetCString());
+ }
+
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+ size_t CalculateNumChildren() override { return m_elements.size(); }
+ ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+private:
+ std::vector<ValueObjectSP> m_elements;
+ ValueObjectSP m_base_sp;
+};
+}
+
+bool TupleFrontEnd::Update() {
+ m_elements.clear();
+ m_base_sp = m_backend.GetChildMemberWithName(ConstString("__base_"), true);
+ if (! m_base_sp) {
+ // Pre r304382 name of the base element.
+ m_base_sp = m_backend.GetChildMemberWithName(ConstString("base_"), true);
+ }
+ if (! m_base_sp)
+ return false;
+ m_elements.assign(m_base_sp->GetCompilerType().GetNumDirectBaseClasses(),
+ ValueObjectSP());
+ return false;
+}
+
+ValueObjectSP TupleFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= m_elements.size())
+ return ValueObjectSP();
+ if (!m_base_sp)
+ return ValueObjectSP();
+ if (m_elements[idx])
+ return m_elements[idx];
+
+ CompilerType holder_type =
+ m_base_sp->GetCompilerType().GetDirectBaseClassAtIndex(idx, nullptr);
+ if (!holder_type)
+ return ValueObjectSP();
+ ValueObjectSP holder_sp = m_base_sp->GetChildAtIndex(idx, true);
+ if (!holder_sp)
+ return ValueObjectSP();
+
+ ValueObjectSP elem_sp = holder_sp->GetChildAtIndex(0, true);
+ if (elem_sp)
+ m_elements[idx] =
+ elem_sp->Clone(ConstString(llvm::formatv("[{0}]", idx).str()));
+
+ return m_elements[idx];
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxTupleFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new TupleFrontEnd(*valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
new file mode 100644
index 000000000000..b2c38c915c81
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
@@ -0,0 +1,221 @@
+//===-- LibCxxUnorderedMap.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxx.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace lldb_private {
+namespace formatters {
+class LibcxxStdUnorderedMapSyntheticFrontEnd
+ : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ CompilerType m_element_type;
+ CompilerType m_node_type;
+ ValueObject *m_tree;
+ size_t m_num_elements;
+ ValueObject *m_next_element;
+ std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+ LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), m_tree(nullptr),
+ m_num_elements(0), m_next_element(nullptr), m_elements_cache() {
+ if (valobj_sp)
+ Update();
+}
+
+size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+ CalculateNumChildren() {
+ if (m_num_elements != UINT32_MAX)
+ return m_num_elements;
+ return 0;
+}
+
+lldb::ValueObjectSP lldb_private::formatters::
+ LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= CalculateNumChildren())
+ return lldb::ValueObjectSP();
+ if (m_tree == nullptr)
+ return lldb::ValueObjectSP();
+
+ while (idx >= m_elements_cache.size()) {
+ if (m_next_element == nullptr)
+ return lldb::ValueObjectSP();
+
+ Status error;
+ ValueObjectSP node_sp = m_next_element->Dereference(error);
+ if (!node_sp || error.Fail())
+ return lldb::ValueObjectSP();
+
+ ValueObjectSP value_sp =
+ node_sp->GetChildMemberWithName(ConstString("__value_"), true);
+ ValueObjectSP hash_sp =
+ node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
+ if (!hash_sp || !value_sp) {
+ if (!m_element_type) {
+ auto p1_sp = m_backend.GetChildAtNamePath({ConstString("__table_"),
+ ConstString("__p1_")});
+ if (!p1_sp)
+ return nullptr;
+
+ ValueObjectSP first_sp = nullptr;
+ switch (p1_sp->GetCompilerType().GetNumDirectBaseClasses()) {
+ case 1:
+ // Assume a pre llvm r300140 __compressed_pair implementation:
+ first_sp = p1_sp->GetChildMemberWithName(ConstString("__first_"),
+ true);
+ break;
+ case 2: {
+ // Assume a post llvm r300140 __compressed_pair implementation:
+ ValueObjectSP first_elem_parent_sp =
+ p1_sp->GetChildAtIndex(0, true);
+ first_sp = p1_sp->GetChildMemberWithName(ConstString("__value_"),
+ true);
+ break;
+ }
+ default:
+ return nullptr;
+ }
+
+ if (!first_sp)
+ return nullptr;
+ m_element_type = first_sp->GetCompilerType();
+ m_element_type = m_element_type.GetTypeTemplateArgument(0);
+ m_element_type = m_element_type.GetPointeeType();
+ m_node_type = m_element_type;
+ m_element_type = m_element_type.GetTypeTemplateArgument(0);
+ std::string name;
+ m_element_type =
+ m_element_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr);
+ m_element_type = m_element_type.GetTypedefedType();
+ }
+ if (!m_node_type)
+ return nullptr;
+ node_sp = node_sp->Cast(m_node_type);
+ value_sp = node_sp->GetChildMemberWithName(ConstString("__value_"), true);
+ hash_sp = node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
+ if (!value_sp || !hash_sp)
+ return nullptr;
+ }
+ m_elements_cache.push_back(
+ {value_sp.get(), hash_sp->GetValueAsUnsigned(0)});
+ m_next_element =
+ node_sp->GetChildMemberWithName(ConstString("__next_"), true).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)
+ return lldb::ValueObjectSP();
+ StreamString stream;
+ stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ DataExtractor data;
+ Status error;
+ val_hash.first->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);
+ return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
+ val_hash.first->GetCompilerType());
+}
+
+bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+ Update() {
+ m_num_elements = UINT32_MAX;
+ m_next_element = nullptr;
+ m_elements_cache.clear();
+ ValueObjectSP table_sp =
+ m_backend.GetChildMemberWithName(ConstString("__table_"), true);
+ if (!table_sp)
+ return false;
+
+ ValueObjectSP p2_sp = table_sp->GetChildMemberWithName(
+ ConstString("__p2_"), true);
+ ValueObjectSP num_elements_sp = nullptr;
+ llvm::SmallVector<ConstString, 3> next_path;
+ switch (p2_sp->GetCompilerType().GetNumDirectBaseClasses()) {
+ case 1:
+ // Assume a pre llvm r300140 __compressed_pair implementation:
+ num_elements_sp = p2_sp->GetChildMemberWithName(
+ ConstString("__first_"), true);
+ next_path.append({ConstString("__p1_"), ConstString("__first_"),
+ ConstString("__next_")});
+ break;
+ case 2: {
+ // Assume a post llvm r300140 __compressed_pair implementation:
+ ValueObjectSP first_elem_parent = p2_sp->GetChildAtIndex(0, true);
+ num_elements_sp = first_elem_parent->GetChildMemberWithName(
+ ConstString("__value_"), true);
+ next_path.append({ConstString("__p1_"), ConstString("__value_"),
+ ConstString("__next_")});
+ break;
+ }
+ default:
+ return false;
+ }
+
+ if (!num_elements_sp)
+ return false;
+ m_num_elements = num_elements_sp->GetValueAsUnsigned(0);
+ m_tree = table_sp->GetChildAtNamePath(next_path).get();
+ if (m_num_elements > 0)
+ m_next_element =
+ table_sp->GetChildAtNamePath(next_path).get();
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ return ExtractIndexFromString(name.GetCString());
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
new file mode 100644
index 000000000000..491cf048e459
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
@@ -0,0 +1,255 @@
+//===-- LibCxxVariant.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxxVariant.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/ScopeExit.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// libc++ variant implementation contains two members that we care about both
+// are contained in the __impl member.
+// - __index which tells us which of the variadic template types is the active
+// type for the variant
+// - __data is a variadic union which recursively contains itself as member
+// which refers to the tailing variadic types.
+// - __head which refers to the leading non pack type
+// - __value refers to the actual value contained
+// - __tail which refers to the remaining pack types
+//
+// e.g. given std::variant<int,double,char> v1
+//
+// (lldb) frame var -R v1.__impl.__data
+//(... __union<... 0, int, double, char>) v1.__impl.__data = {
+// ...
+// __head = {
+// __value = ...
+// }
+// __tail = {
+// ...
+// __head = {
+// __value = ...
+// }
+// __tail = {
+// ...
+// __head = {
+// __value = ...
+// ...
+//
+// So given
+// - __index equal to 0 the active value is contained in
+//
+// __data.__head.__value
+//
+// - __index equal to 1 the active value is contained in
+//
+// __data.__tail.__head.__value
+//
+// - __index equal to 2 the active value is contained in
+//
+// __data.__tail.__tail.__head.__value
+//
+
+namespace {
+// libc++ std::variant index could have one of three states
+// 1) VALID, we can obtain it and its not variant_npos
+// 2) INVALID, we can't obtain it or it is not a type we expect
+// 3) NPOS, its value is variant_npos which means the variant has no value
+enum class LibcxxVariantIndexValidity { VALID, INVALID, NPOS };
+
+LibcxxVariantIndexValidity
+LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {
+ ValueObjectSP index_sp(
+ impl_sp->GetChildMemberWithName(ConstString("__index"), true));
+
+ if (!index_sp)
+ return LibcxxVariantIndexValidity::INVALID;
+
+ int64_t index_value = index_sp->GetValueAsSigned(0);
+
+ if (index_value == -1)
+ return LibcxxVariantIndexValidity::NPOS;
+
+ return LibcxxVariantIndexValidity::VALID;
+}
+
+llvm::Optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) {
+ ValueObjectSP index_sp(
+ impl_sp->GetChildMemberWithName(ConstString("__index"), true));
+
+ if (!index_sp)
+ return {};
+
+ return {index_sp->GetValueAsUnsigned(0)};
+}
+
+ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) {
+ ValueObjectSP data_sp(
+ impl_sp->GetChildMemberWithName(ConstString("__data"), true));
+
+ if (!data_sp)
+ return ValueObjectSP{};
+
+ ValueObjectSP current_level = data_sp;
+ for (uint64_t n = index; n != 0; --n) {
+ ValueObjectSP tail_sp(
+ current_level->GetChildMemberWithName(ConstString("__tail"), true));
+
+ if (!tail_sp)
+ return ValueObjectSP{};
+
+ current_level = tail_sp;
+ }
+
+ return current_level->GetChildMemberWithName(ConstString("__head"), true);
+}
+} // namespace
+
+namespace lldb_private {
+namespace formatters {
+bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options) {
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+ if (!valobj_sp)
+ return false;
+
+ ValueObjectSP impl_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__impl"), true));
+
+ if (!impl_sp)
+ return false;
+
+ LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
+
+ if (validity == LibcxxVariantIndexValidity::INVALID)
+ return false;
+
+ if (validity == LibcxxVariantIndexValidity::NPOS) {
+ stream.Printf(" No Value");
+ return true;
+ }
+
+ auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
+
+ if (!optional_index_value)
+ return false;
+
+ uint64_t index_value = *optional_index_value;
+
+ ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
+
+ if (!nth_head)
+ return false;
+
+ CompilerType head_type = nth_head->GetCompilerType();
+
+ if (!head_type)
+ return false;
+
+ CompilerType template_type = head_type.GetTypeTemplateArgument(1);
+
+ if (!template_type)
+ return false;
+
+ stream.Printf(" Active Type = %s ", template_type.GetTypeName().GetCString());
+
+ return true;
+}
+} // namespace formatters
+} // namespace lldb_private
+
+namespace {
+class VariantFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
+ Update();
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return formatters::ExtractIndexFromString(name.GetCString());
+ }
+
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+ size_t CalculateNumChildren() override { return m_size; }
+ ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+private:
+ size_t m_size = 0;
+ ValueObjectSP m_base_sp;
+};
+} // namespace
+
+bool VariantFrontEnd::Update() {
+ m_size = 0;
+ ValueObjectSP impl_sp(
+ m_backend.GetChildMemberWithName(ConstString("__impl"), true));
+ if (!impl_sp)
+ return false;
+
+ LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
+
+ if (validity == LibcxxVariantIndexValidity::INVALID)
+ return false;
+
+ if (validity == LibcxxVariantIndexValidity::NPOS)
+ return true;
+
+ m_size = 1;
+
+ return false;
+}
+
+ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= m_size)
+ return ValueObjectSP();
+
+ ValueObjectSP impl_sp(
+ m_backend.GetChildMemberWithName(ConstString("__impl"), true));
+
+ auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
+
+ if (!optional_index_value)
+ return ValueObjectSP();
+
+ uint64_t index_value = *optional_index_value;
+
+ ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
+
+ if (!nth_head)
+ return ValueObjectSP();
+
+ CompilerType head_type = nth_head->GetCompilerType();
+
+ if (!head_type)
+ return ValueObjectSP();
+
+ CompilerType template_type = head_type.GetTypeTemplateArgument(1);
+
+ if (!template_type)
+ return ValueObjectSP();
+
+ ValueObjectSP head_value(
+ nth_head->GetChildMemberWithName(ConstString("__value"), true));
+
+ if (!head_value)
+ return ValueObjectSP();
+
+ return head_value->Clone(ConstString(ConstString("Value").AsCString()));
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new VariantFrontEnd(*valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h
new file mode 100644
index 000000000000..65db5aeaa99d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h
@@ -0,0 +1,30 @@
+//===-- LibCxxVariant.h -------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_LibCxxVariant_h_
+#define liblldb_LibCxxVariant_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+bool LibcxxVariantSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libc++ std::variant<>
+
+SyntheticChildrenFrontEnd *LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_LibCxxVariant_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
new file mode 100644
index 000000000000..bcd7442bc669
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
@@ -0,0 +1,296 @@
+//===-- LibCxxVector.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxx.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Utility/ConstString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace lldb_private {
+namespace formatters {
+class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~LibcxxStdVectorSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ ValueObject *m_start;
+ ValueObject *m_finish;
+ CompilerType m_element_type;
+ uint32_t m_element_size;
+};
+
+class LibcxxVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override { return true; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ CompilerType m_bool_type;
+ ExecutionContextRef m_exe_ctx_ref;
+ uint64_t m_count;
+ lldb::addr_t m_base_data_address;
+ std::map<size_t, lldb::ValueObjectSP> m_children;
+};
+
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
+ LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_start(nullptr),
+ m_finish(nullptr), m_element_type(), m_element_size(0) {
+ if (valobj_sp)
+ Update();
+}
+
+lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
+ ~LibcxxStdVectorSyntheticFrontEnd() {
+ // these need to stay around because they are child objects who will follow
+ // their parent's life cycle
+ // delete m_start;
+ // delete m_finish;
+}
+
+size_t lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
+ CalculateNumChildren() {
+ if (!m_start || !m_finish)
+ return 0;
+ uint64_t start_val = m_start->GetValueAsUnsigned(0);
+ uint64_t finish_val = m_finish->GetValueAsUnsigned(0);
+
+ if (start_val == 0 || finish_val == 0)
+ return 0;
+
+ if (start_val >= finish_val)
+ return 0;
+
+ size_t num_children = (finish_val - start_val);
+ if (num_children % m_element_size)
+ return 0;
+ return num_children / m_element_size;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ if (!m_start || !m_finish)
+ return lldb::ValueObjectSP();
+
+ uint64_t offset = idx * m_element_size;
+ offset = offset + m_start->GetValueAsUnsigned(0);
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return CreateValueObjectFromAddress(name.GetString(), offset,
+ m_backend.GetExecutionContextRef(),
+ m_element_type);
+}
+
+bool lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() {
+ m_start = m_finish = nullptr;
+ ValueObjectSP data_type_finder_sp(
+ m_backend.GetChildMemberWithName(ConstString("__end_cap_"), true));
+ if (!data_type_finder_sp)
+ return false;
+
+ switch (data_type_finder_sp->GetCompilerType().GetNumDirectBaseClasses()) {
+ case 1:
+ // Assume a pre llvm r300140 __compressed_pair implementation:
+ data_type_finder_sp = data_type_finder_sp->GetChildMemberWithName(
+ ConstString("__first_"), true);
+ break;
+ case 2: {
+ // Assume a post llvm r300140 __compressed_pair implementation:
+ ValueObjectSP first_elem_parent_sp =
+ data_type_finder_sp->GetChildAtIndex(0, true);
+ data_type_finder_sp = first_elem_parent_sp->GetChildMemberWithName(
+ ConstString("__value_"), true);
+ break;
+ }
+ default:
+ return false;
+ }
+
+ if (!data_type_finder_sp)
+ return false;
+ m_element_type = data_type_finder_sp->GetCompilerType().GetPointeeType();
+ if (llvm::Optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) {
+ m_element_size = *size;
+
+ if (m_element_size > 0) {
+ // store raw pointers or end up with a circular dependency
+ m_start =
+ m_backend.GetChildMemberWithName(ConstString("__begin_"), true).get();
+ m_finish =
+ m_backend.GetChildMemberWithName(ConstString("__end_"), true).get();
+ }
+ }
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (!m_start || !m_finish)
+ return UINT32_MAX;
+ return ExtractIndexFromString(name.GetCString());
+}
+
+lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::
+ LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_bool_type(), m_exe_ctx_ref(),
+ m_count(0), m_base_data_address(0), m_children() {
+ if (valobj_sp) {
+ Update();
+ m_bool_type =
+ valobj_sp->GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeBool);
+ }
+}
+
+size_t lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::
+ CalculateNumChildren() {
+ return m_count;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ auto iter = m_children.find(idx), end = m_children.end();
+ if (iter != end)
+ return iter->second;
+ if (idx >= m_count)
+ return {};
+ if (m_base_data_address == 0 || m_count == 0)
+ return {};
+ if (!m_bool_type)
+ return {};
+ size_t byte_idx = (idx >> 3); // divide by 8 to get byte index
+ size_t bit_index = (idx & 7); // efficient idx % 8 for bit index
+ lldb::addr_t byte_location = m_base_data_address + byte_idx;
+ ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
+ if (!process_sp)
+ return {};
+ uint8_t byte = 0;
+ uint8_t mask = 0;
+ Status err;
+ size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err);
+ if (err.Fail() || bytes_read == 0)
+ return {};
+ mask = 1 << bit_index;
+ bool bit_set = ((byte & mask) != 0);
+ llvm::Optional<uint64_t> size = m_bool_type.GetByteSize(nullptr);
+ if (!size)
+ return {};
+ DataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
+ if (bit_set && buffer_sp && buffer_sp->GetBytes()) {
+ // regardless of endianness, anything non-zero is true
+ *(buffer_sp->GetBytes()) = 1;
+ }
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ ValueObjectSP retval_sp(CreateValueObjectFromData(
+ name.GetString(),
+ DataExtractor(buffer_sp, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize()),
+ m_exe_ctx_ref, m_bool_type));
+ if (retval_sp)
+ m_children[idx] = retval_sp;
+ return retval_sp;
+}
+
+/*(std::__1::vector<std::__1::allocator<bool> >) vBool = {
+ __begin_ = 0x00000001001000e0
+ __size_ = 56
+ __cap_alloc_ = {
+ std::__1::__libcpp_compressed_pair_imp<unsigned long,
+ std::__1::allocator<unsigned long> > = {
+ __first_ = 1
+ }
+ }
+ }*/
+
+bool lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::Update() {
+ m_children.clear();
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ ValueObjectSP size_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__size_"), true));
+ if (!size_sp)
+ return false;
+ m_count = size_sp->GetValueAsUnsigned(0);
+ if (!m_count)
+ return true;
+ ValueObjectSP begin_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__begin_"), true));
+ if (!begin_sp) {
+ m_count = 0;
+ return false;
+ }
+ m_base_data_address = begin_sp->GetValueAsUnsigned(0);
+ if (!m_base_data_address) {
+ m_count = 0;
+ return false;
+ }
+ return false;
+}
+
+size_t lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (!m_count || !m_base_data_address)
+ return UINT32_MAX;
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+lldb_private::SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+ CompilerType type = valobj_sp->GetCompilerType();
+ if (!type.IsValid() || type.GetNumTemplateArguments() == 0)
+ return nullptr;
+ CompilerType arg_type = type.GetTypeTemplateArgument(0);
+ if (arg_type.GetTypeName() == "bool")
+ return new LibcxxVectorBoolSyntheticFrontEnd(valobj_sp);
+ return new LibcxxStdVectorSyntheticFrontEnd(valobj_sp);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
new file mode 100644
index 000000000000..0e0f6663c924
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
@@ -0,0 +1,426 @@
+//===-- LibStdcpp.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibStdcpp.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/DataFormatters/VectorIterator.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace {
+
+class LibstdcppMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+ /*
+ (std::_Rb_tree_iterator<std::pair<const int, std::basic_string<char,
+ std::char_traits<char>, std::allocator<char> > > >) ibeg = {
+ (_Base_ptr) _M_node = 0x0000000100103910 {
+ (std::_Rb_tree_color) _M_color = _S_black
+ (std::_Rb_tree_node_base::_Base_ptr) _M_parent = 0x00000001001038c0
+ (std::_Rb_tree_node_base::_Base_ptr) _M_left = 0x0000000000000000
+ (std::_Rb_tree_node_base::_Base_ptr) _M_right = 0x0000000000000000
+ }
+ }
+ */
+
+public:
+ explicit LibstdcppMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ ExecutionContextRef m_exe_ctx_ref;
+ lldb::addr_t m_pair_address;
+ CompilerType m_pair_type;
+ lldb::ValueObjectSP m_pair_sp;
+};
+
+class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ explicit LibStdcppSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+};
+
+} // end of anonymous namespace
+
+LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_address(0),
+ m_pair_type(), m_pair_sp() {
+ if (valobj_sp)
+ Update();
+}
+
+bool LibstdcppMapIteratorSyntheticFrontEnd::Update() {
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+
+ TargetSP target_sp(valobj_sp->GetTargetSP());
+
+ if (!target_sp)
+ return false;
+
+ bool is_64bit = (target_sp->GetArchitecture().GetAddressByteSize() == 8);
+
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+
+ ValueObjectSP _M_node_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("_M_node"), true));
+ if (!_M_node_sp)
+ return false;
+
+ m_pair_address = _M_node_sp->GetValueAsUnsigned(0);
+ if (m_pair_address == 0)
+ return false;
+
+ m_pair_address += (is_64bit ? 32 : 16);
+
+ CompilerType my_type(valobj_sp->GetCompilerType());
+ if (my_type.GetNumTemplateArguments() >= 1) {
+ CompilerType pair_type = my_type.GetTypeTemplateArgument(0);
+ if (!pair_type)
+ return false;
+ m_pair_type = pair_type;
+ } else
+ return false;
+
+ return true;
+}
+
+size_t LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
+ return 2;
+}
+
+lldb::ValueObjectSP
+LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (m_pair_address != 0 && m_pair_type) {
+ if (!m_pair_sp)
+ m_pair_sp = CreateValueObjectFromAddress("pair", m_pair_address,
+ m_exe_ctx_ref, m_pair_type);
+ if (m_pair_sp)
+ return m_pair_sp->GetChildAtIndex(idx, true);
+ }
+ return lldb::ValueObjectSP();
+}
+
+bool LibstdcppMapIteratorSyntheticFrontEnd::MightHaveChildren() { return true; }
+
+size_t LibstdcppMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ if (name == "first")
+ return 0;
+ if (name == "second")
+ return 1;
+ return UINT32_MAX;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibstdcppMapIteratorSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
+
+/*
+ (lldb) fr var ibeg --ptr-depth 1
+ (__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >)
+ ibeg = {
+ _M_current = 0x00000001001037a0 {
+ *_M_current = 1
+ }
+ }
+ */
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ static ConstString g_item_name;
+ if (!g_item_name)
+ g_item_name.SetCString("_M_current");
+ return (valobj_sp
+ ? new VectorIteratorSyntheticFrontEnd(valobj_sp, g_item_name)
+ : nullptr);
+}
+
+lldb_private::formatters::VectorIteratorSyntheticFrontEnd::
+ VectorIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp,
+ ConstString item_name)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(),
+ m_item_name(item_name), m_item_sp() {
+ if (valobj_sp)
+ Update();
+}
+
+bool VectorIteratorSyntheticFrontEnd::Update() {
+ m_item_sp.reset();
+
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+
+ if (!valobj_sp)
+ return false;
+
+ ValueObjectSP item_ptr(valobj_sp->GetChildMemberWithName(m_item_name, true));
+ if (!item_ptr)
+ return false;
+ if (item_ptr->GetValueAsUnsigned(0) == 0)
+ return false;
+ Status err;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ m_item_sp = CreateValueObjectFromAddress(
+ "item", item_ptr->GetValueAsUnsigned(0), m_exe_ctx_ref,
+ item_ptr->GetCompilerType().GetPointeeType());
+ if (err.Fail())
+ m_item_sp.reset();
+ return false;
+}
+
+size_t VectorIteratorSyntheticFrontEnd::CalculateNumChildren() { return 1; }
+
+lldb::ValueObjectSP
+VectorIteratorSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx == 0)
+ return m_item_sp;
+ return lldb::ValueObjectSP();
+}
+
+bool VectorIteratorSyntheticFrontEnd::MightHaveChildren() { return true; }
+
+size_t VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ if (name == "item")
+ return 0;
+ return UINT32_MAX;
+}
+
+bool lldb_private::formatters::LibStdcppStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ const bool scalar_is_load_addr = true;
+ AddressType addr_type;
+ lldb::addr_t addr_of_string =
+ valobj.GetAddressOf(scalar_is_load_addr, &addr_type);
+ if (addr_of_string != LLDB_INVALID_ADDRESS) {
+ switch (addr_type) {
+ case eAddressTypeLoad: {
+ ProcessSP process_sp(valobj.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ Status error;
+ lldb::addr_t addr_of_data =
+ process_sp->ReadPointerFromMemory(addr_of_string, error);
+ if (error.Fail() || addr_of_data == 0 ||
+ addr_of_data == LLDB_INVALID_ADDRESS)
+ return false;
+ options.SetLocation(addr_of_data);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetNeedsZeroTermination(false);
+ options.SetBinaryZeroIsTerminator(true);
+ lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory(
+ addr_of_string + process_sp->GetAddressByteSize(), error);
+ if (error.Fail())
+ return false;
+ options.SetSourceSize(size_of_data);
+
+ if (!StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(options)) {
+ stream.Printf("Summary Unavailable");
+ return true;
+ } else
+ return true;
+ } break;
+ case eAddressTypeHost:
+ break;
+ case eAddressTypeInvalid:
+ case eAddressTypeFile:
+ break;
+ }
+ }
+ return false;
+}
+
+bool lldb_private::formatters::LibStdcppWStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ const bool scalar_is_load_addr = true;
+ AddressType addr_type;
+ lldb::addr_t addr_of_string =
+ valobj.GetAddressOf(scalar_is_load_addr, &addr_type);
+ if (addr_of_string != LLDB_INVALID_ADDRESS) {
+ switch (addr_type) {
+ case eAddressTypeLoad: {
+ ProcessSP process_sp(valobj.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ CompilerType wchar_compiler_type =
+ valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar);
+
+ if (!wchar_compiler_type)
+ return false;
+
+ // Safe to pass nullptr for exe_scope here.
+ llvm::Optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr);
+ if (!size)
+ return false;
+ const uint32_t wchar_size = *size;
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ Status error;
+ lldb::addr_t addr_of_data =
+ process_sp->ReadPointerFromMemory(addr_of_string, error);
+ if (error.Fail() || addr_of_data == 0 ||
+ addr_of_data == LLDB_INVALID_ADDRESS)
+ return false;
+ options.SetLocation(addr_of_data);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetNeedsZeroTermination(false);
+ options.SetBinaryZeroIsTerminator(false);
+ lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory(
+ addr_of_string + process_sp->GetAddressByteSize(), error);
+ if (error.Fail())
+ return false;
+ options.SetSourceSize(size_of_data);
+ options.SetPrefixToken("L");
+
+ switch (wchar_size) {
+ case 8:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(options);
+ case 16:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ case 32:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(options);
+ default:
+ stream.Printf("size for wchar_t is not valid");
+ return true;
+ }
+ return true;
+ } break;
+ case eAddressTypeHost:
+ break;
+ case eAddressTypeInvalid:
+ case eAddressTypeFile:
+ break;
+ }
+ }
+ return false;
+}
+
+LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {
+ if (valobj_sp)
+ Update();
+}
+
+size_t LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() { return 1; }
+
+lldb::ValueObjectSP
+LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return lldb::ValueObjectSP();
+
+ if (idx == 0)
+ return valobj_sp->GetChildMemberWithName(ConstString("_M_ptr"), true);
+ else
+ return lldb::ValueObjectSP();
+}
+
+bool LibStdcppSharedPtrSyntheticFrontEnd::Update() { return false; }
+
+bool LibStdcppSharedPtrSyntheticFrontEnd::MightHaveChildren() { return true; }
+
+size_t LibStdcppSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ if (name == "_M_ptr")
+ return 0;
+ return UINT32_MAX;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
+
+bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+ if (!valobj_sp)
+ return false;
+
+ ValueObjectSP ptr_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("_M_ptr"), true));
+ if (!ptr_sp)
+ return false;
+
+ ValueObjectSP usecount_sp(valobj_sp->GetChildAtNamePath(
+ {ConstString("_M_refcount"), ConstString("_M_pi"),
+ ConstString("_M_use_count")}));
+ if (!usecount_sp)
+ return false;
+
+ if (ptr_sp->GetValueAsUnsigned(0) == 0 ||
+ usecount_sp->GetValueAsUnsigned(0) == 0) {
+ stream.Printf("nullptr");
+ return true;
+ }
+
+ Status error;
+ ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
+ if (pointee_sp && error.Success()) {
+ if (pointee_sp->DumpPrintableRepresentation(
+ stream, ValueObject::eValueObjectRepresentationStyleSummary,
+ lldb::eFormatInvalid,
+ ValueObject::PrintableRepresentationSpecialCases::eDisable,
+ false)) {
+ return true;
+ }
+ }
+
+ stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h
new file mode 100644
index 000000000000..e7f88d667c14
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h
@@ -0,0 +1,59 @@
+//===-- LibStdcpp.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_LibStdCpp_h_
+#define liblldb_LibStdCpp_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+bool LibStdcppStringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libcstdc++ c++11 std::string
+
+bool LibStdcppWStringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libcstdc++ c++11 std::wstring
+
+bool LibStdcppSmartPointerSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions
+ &options); // libstdc++ std::shared_ptr<> and std::weak_ptr<>
+
+bool LibStdcppUniquePointerSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libstdc++ std::unique_ptr<>
+
+SyntheticChildrenFrontEnd *
+LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibStdcppTupleSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibStdcppVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibStdcppSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibStdcppUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_LibStdCpp_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp
new file mode 100644
index 000000000000..66624e5beb6d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp
@@ -0,0 +1,106 @@
+//===-- LibStdcppTuple.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibStdcpp.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/ConstString.h"
+
+#include <memory>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace {
+
+class LibStdcppTupleSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ explicit LibStdcppTupleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ std::vector<ValueObjectSP> m_members;
+};
+
+} // end of anonymous namespace
+
+LibStdcppTupleSyntheticFrontEnd::LibStdcppTupleSyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {
+ Update();
+}
+
+bool LibStdcppTupleSyntheticFrontEnd::Update() {
+ m_members.clear();
+
+ ValueObjectSP valobj_backend_sp = m_backend.GetSP();
+ if (!valobj_backend_sp)
+ return false;
+
+ ValueObjectSP next_child_sp = valobj_backend_sp->GetNonSyntheticValue();
+ while (next_child_sp != nullptr) {
+ ValueObjectSP current_child = next_child_sp;
+ next_child_sp = nullptr;
+
+ size_t child_count = current_child->GetNumChildren();
+ for (size_t i = 0; i < child_count; ++i) {
+ ValueObjectSP child_sp = current_child->GetChildAtIndex(i, true);
+ llvm::StringRef name_str = child_sp->GetName().GetStringRef();
+ if (name_str.startswith("std::_Tuple_impl<")) {
+ next_child_sp = child_sp;
+ } else if (name_str.startswith("std::_Head_base<")) {
+ ValueObjectSP value_sp =
+ child_sp->GetChildMemberWithName(ConstString("_M_head_impl"), true);
+ if (value_sp) {
+ StreamString name;
+ name.Printf("[%zd]", m_members.size());
+ m_members.push_back(value_sp->Clone(ConstString(name.GetString())));
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool LibStdcppTupleSyntheticFrontEnd::MightHaveChildren() { return true; }
+
+lldb::ValueObjectSP
+LibStdcppTupleSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx < m_members.size())
+ return m_members[idx];
+ return lldb::ValueObjectSP();
+}
+
+size_t LibStdcppTupleSyntheticFrontEnd::CalculateNumChildren() {
+ return m_members.size();
+}
+
+size_t LibStdcppTupleSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ return ExtractIndexFromString(name.GetCString());
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibStdcppTupleSyntheticFrontEnd(valobj_sp) : nullptr);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp
new file mode 100644
index 000000000000..3860f960cb3d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp
@@ -0,0 +1,169 @@
+//===-- LibStdcppUniquePointer.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibStdcpp.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/ConstString.h"
+
+#include <memory>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace {
+
+class LibStdcppUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ explicit LibStdcppUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+ bool GetSummary(Stream &stream, const TypeSummaryOptions &options);
+
+private:
+ ValueObjectSP m_ptr_obj;
+ ValueObjectSP m_obj_obj;
+ ValueObjectSP m_del_obj;
+
+ ValueObjectSP GetTuple();
+};
+
+} // end of anonymous namespace
+
+LibStdcppUniquePtrSyntheticFrontEnd::LibStdcppUniquePtrSyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {
+ Update();
+}
+
+ValueObjectSP LibStdcppUniquePtrSyntheticFrontEnd::GetTuple() {
+ ValueObjectSP valobj_backend_sp = m_backend.GetSP();
+
+ if (!valobj_backend_sp)
+ return nullptr;
+
+ ValueObjectSP valobj_sp = valobj_backend_sp->GetNonSyntheticValue();
+ if (!valobj_sp)
+ return nullptr;
+
+ ValueObjectSP obj_child_sp =
+ valobj_sp->GetChildMemberWithName(ConstString("_M_t"), true);
+ if (!obj_child_sp)
+ return nullptr;
+
+ ValueObjectSP obj_subchild_sp =
+ obj_child_sp->GetChildMemberWithName(ConstString("_M_t"), true);
+
+ // if there is a _M_t subchild, the tuple is found in the obj_subchild_sp
+ // (for libstdc++ 6.0.23).
+ if (obj_subchild_sp) {
+ return obj_subchild_sp;
+ }
+
+ return obj_child_sp;
+}
+
+bool LibStdcppUniquePtrSyntheticFrontEnd::Update() {
+ ValueObjectSP tuple_sp = GetTuple();
+
+ if (!tuple_sp)
+ return false;
+
+ std::unique_ptr<SyntheticChildrenFrontEnd> tuple_frontend(
+ LibStdcppTupleSyntheticFrontEndCreator(nullptr, tuple_sp));
+
+ ValueObjectSP ptr_obj = tuple_frontend->GetChildAtIndex(0);
+ if (ptr_obj)
+ m_ptr_obj = ptr_obj->Clone(ConstString("pointer"));
+
+ ValueObjectSP del_obj = tuple_frontend->GetChildAtIndex(1);
+ if (del_obj)
+ m_del_obj = del_obj->Clone(ConstString("deleter"));
+
+ if (m_ptr_obj) {
+ Status error;
+ ValueObjectSP obj_obj = m_ptr_obj->Dereference(error);
+ if (error.Success()) {
+ m_obj_obj = obj_obj->Clone(ConstString("object"));
+ }
+ }
+
+ return false;
+}
+
+bool LibStdcppUniquePtrSyntheticFrontEnd::MightHaveChildren() { return true; }
+
+lldb::ValueObjectSP
+LibStdcppUniquePtrSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx == 0)
+ return m_ptr_obj;
+ if (idx == 1)
+ return m_del_obj;
+ if (idx == 2)
+ return m_obj_obj;
+ return lldb::ValueObjectSP();
+}
+
+size_t LibStdcppUniquePtrSyntheticFrontEnd::CalculateNumChildren() {
+ if (m_del_obj)
+ return 2;
+ return 1;
+}
+
+size_t LibStdcppUniquePtrSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ if (name == "ptr" || name == "pointer")
+ return 0;
+ if (name == "del" || name == "deleter")
+ return 1;
+ if (name == "obj" || name == "object" || name == "$$dereference$$")
+ return 2;
+ return UINT32_MAX;
+}
+
+bool LibStdcppUniquePtrSyntheticFrontEnd::GetSummary(
+ Stream &stream, const TypeSummaryOptions &options) {
+ if (!m_ptr_obj)
+ return false;
+
+ bool success;
+ uint64_t ptr_value = m_ptr_obj->GetValueAsUnsigned(0, &success);
+ if (!success)
+ return false;
+ if (ptr_value == 0)
+ stream.Printf("nullptr");
+ else
+ stream.Printf("0x%" PRIx64, ptr_value);
+ return true;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibStdcppUniquePtrSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
+
+bool lldb_private::formatters::LibStdcppUniquePointerSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ LibStdcppUniquePtrSyntheticFrontEnd formatter(valobj.GetSP());
+ return formatter.GetSummary(stream, options);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp
new file mode 100644
index 000000000000..248c51acea42
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp
@@ -0,0 +1,98 @@
+//===-- MSVCUndecoratedNameParser.cpp ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MSVCUndecoratedNameParser.h"
+
+#include <stack>
+
+MSVCUndecoratedNameParser::MSVCUndecoratedNameParser(llvm::StringRef name) {
+ std::size_t last_base_start = 0;
+
+ std::stack<std::size_t> stack;
+ unsigned int open_angle_brackets = 0;
+ for (size_t i = 0; i < name.size(); i++) {
+ switch (name[i]) {
+ case '<':
+ // Do not treat `operator<' and `operator<<' as templates
+ // (sometimes they represented as `<' and `<<' in the name).
+ if (i == last_base_start ||
+ (i == last_base_start + 1 && name[last_base_start] == '<'))
+ break;
+
+ stack.push(i);
+ open_angle_brackets++;
+
+ break;
+ case '>':
+ if (!stack.empty() && name[stack.top()] == '<') {
+ open_angle_brackets--;
+ stack.pop();
+ }
+
+ break;
+ case '`':
+ stack.push(i);
+
+ break;
+ case '\'':
+ while (!stack.empty()) {
+ std::size_t top = stack.top();
+ if (name[top] == '<')
+ open_angle_brackets--;
+
+ stack.pop();
+
+ if (name[top] == '`')
+ break;
+ }
+
+ break;
+ case ':':
+ if (open_angle_brackets)
+ break;
+ if (i == 0 || name[i - 1] != ':')
+ break;
+
+ m_specifiers.emplace_back(name.take_front(i - 1),
+ name.slice(last_base_start, i - 1));
+
+ last_base_start = i + 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ m_specifiers.emplace_back(name, name.drop_front(last_base_start));
+}
+
+bool MSVCUndecoratedNameParser::IsMSVCUndecoratedName(llvm::StringRef name) {
+ return name.find('`') != llvm::StringRef::npos;
+}
+
+bool MSVCUndecoratedNameParser::ExtractContextAndIdentifier(
+ llvm::StringRef name, llvm::StringRef &context,
+ llvm::StringRef &identifier) {
+ MSVCUndecoratedNameParser parser(name);
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers();
+
+ std::size_t count = specs.size();
+ identifier = count > 0 ? specs[count - 1].GetBaseName() : "";
+ context = count > 1 ? specs[count - 2].GetFullName() : "";
+
+ return count;
+}
+
+llvm::StringRef MSVCUndecoratedNameParser::DropScope(llvm::StringRef name) {
+ MSVCUndecoratedNameParser parser(name);
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers();
+ if (specs.empty())
+ return "";
+
+ return specs[specs.size() - 1].GetBaseName();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h
new file mode 100644
index 000000000000..6e20877cae1b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h
@@ -0,0 +1,50 @@
+//===-- MSVCUndecoratedNameParser.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MSVCUndecoratedNameParser_h_
+#define liblldb_MSVCUndecoratedNameParser_h_
+
+#include <vector>
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+class MSVCUndecoratedNameSpecifier {
+public:
+ MSVCUndecoratedNameSpecifier(llvm::StringRef full_name,
+ llvm::StringRef base_name)
+ : m_full_name(full_name), m_base_name(base_name) {}
+
+ llvm::StringRef GetFullName() const { return m_full_name; }
+ llvm::StringRef GetBaseName() const { return m_base_name; }
+
+private:
+ llvm::StringRef m_full_name;
+ llvm::StringRef m_base_name;
+};
+
+class MSVCUndecoratedNameParser {
+public:
+ explicit MSVCUndecoratedNameParser(llvm::StringRef name);
+
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> GetSpecifiers() const {
+ return m_specifiers;
+ }
+
+ static bool IsMSVCUndecoratedName(llvm::StringRef name);
+ static bool ExtractContextAndIdentifier(llvm::StringRef name,
+ llvm::StringRef &context,
+ llvm::StringRef &identifier);
+
+ static llvm::StringRef DropScope(llvm::StringRef name);
+
+private:
+ std::vector<MSVCUndecoratedNameSpecifier> m_specifiers;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp
new file mode 100644
index 000000000000..a9a1b44731f2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp
@@ -0,0 +1,238 @@
+//===-- ClangHighlighter.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangHighlighter.h"
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/AnsiTerminal.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+using namespace lldb_private;
+
+bool ClangHighlighter::isKeyword(llvm::StringRef token) const {
+ return keywords.find(token) != keywords.end();
+}
+
+ClangHighlighter::ClangHighlighter() {
+#define KEYWORD(X, N) keywords.insert(#X);
+#include "clang/Basic/TokenKinds.def"
+}
+
+/// Determines which style should be applied to the given token.
+/// \param highlighter
+/// The current highlighter that should use the style.
+/// \param token
+/// The current token.
+/// \param tok_str
+/// The string in the source code the token represents.
+/// \param options
+/// The style we use for coloring the source code.
+/// \param in_pp_directive
+/// If we are currently in a preprocessor directive. NOTE: This is
+/// passed by reference and will be updated if the current token starts
+/// or ends a preprocessor directive.
+/// \return
+/// The ColorStyle that should be applied to the token.
+static HighlightStyle::ColorStyle
+determineClangStyle(const ClangHighlighter &highlighter,
+ const clang::Token &token, llvm::StringRef tok_str,
+ const HighlightStyle &options, bool &in_pp_directive) {
+ using namespace clang;
+
+ if (token.is(tok::comment)) {
+ // If we were in a preprocessor directive before, we now left it.
+ in_pp_directive = false;
+ return options.comment;
+ } else if (in_pp_directive || token.getKind() == tok::hash) {
+ // Let's assume that the rest of the line is a PP directive.
+ in_pp_directive = true;
+ // Preprocessor directives are hard to match, so we have to hack this in.
+ return options.pp_directive;
+ } else if (tok::isStringLiteral(token.getKind()))
+ return options.string_literal;
+ else if (tok::isLiteral(token.getKind()))
+ return options.scalar_literal;
+ else if (highlighter.isKeyword(tok_str))
+ return options.keyword;
+ else
+ switch (token.getKind()) {
+ case tok::raw_identifier:
+ case tok::identifier:
+ return options.identifier;
+ case tok::l_brace:
+ case tok::r_brace:
+ return options.braces;
+ case tok::l_square:
+ case tok::r_square:
+ return options.square_brackets;
+ case tok::l_paren:
+ case tok::r_paren:
+ return options.parentheses;
+ case tok::comma:
+ return options.comma;
+ case tok::coloncolon:
+ case tok::colon:
+ return options.colon;
+
+ case tok::amp:
+ case tok::ampamp:
+ case tok::ampequal:
+ case tok::star:
+ case tok::starequal:
+ case tok::plus:
+ case tok::plusplus:
+ case tok::plusequal:
+ case tok::minus:
+ case tok::arrow:
+ case tok::minusminus:
+ case tok::minusequal:
+ case tok::tilde:
+ case tok::exclaim:
+ case tok::exclaimequal:
+ case tok::slash:
+ case tok::slashequal:
+ case tok::percent:
+ case tok::percentequal:
+ case tok::less:
+ case tok::lessless:
+ case tok::lessequal:
+ case tok::lesslessequal:
+ case tok::spaceship:
+ case tok::greater:
+ case tok::greatergreater:
+ case tok::greaterequal:
+ case tok::greatergreaterequal:
+ case tok::caret:
+ case tok::caretequal:
+ case tok::pipe:
+ case tok::pipepipe:
+ case tok::pipeequal:
+ case tok::question:
+ case tok::equal:
+ case tok::equalequal:
+ return options.operators;
+ default:
+ break;
+ }
+ return HighlightStyle::ColorStyle();
+}
+
+void ClangHighlighter::Highlight(const HighlightStyle &options,
+ llvm::StringRef line,
+ llvm::Optional<size_t> cursor_pos,
+ llvm::StringRef previous_lines,
+ Stream &result) const {
+ using namespace clang;
+
+ FileSystemOptions file_opts;
+ FileManager file_mgr(file_opts,
+ FileSystem::Instance().GetVirtualFileSystem());
+
+ unsigned line_number = previous_lines.count('\n') + 1U;
+
+ // Let's build the actual source code Clang needs and setup some utility
+ // objects.
+ std::string full_source = previous_lines.str() + line.str();
+ llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs());
+ llvm::IntrusiveRefCntPtr<DiagnosticOptions> diags_opts(
+ new DiagnosticOptions());
+ DiagnosticsEngine diags(diag_ids, diags_opts);
+ clang::SourceManager SM(diags, file_mgr);
+ auto buf = llvm::MemoryBuffer::getMemBuffer(full_source);
+
+ FileID FID = SM.createFileID(clang::SourceManager::Unowned, buf.get());
+
+ // Let's just enable the latest ObjC and C++ which should get most tokens
+ // right.
+ LangOptions Opts;
+ Opts.ObjC = true;
+ // FIXME: This should probably set CPlusPlus, CPlusPlus11, ... too
+ Opts.CPlusPlus17 = true;
+ Opts.LineComment = true;
+
+ Lexer lex(FID, buf.get(), SM, Opts);
+ // The lexer should keep whitespace around.
+ lex.SetKeepWhitespaceMode(true);
+
+ // Keeps track if we have entered a PP directive.
+ bool in_pp_directive = false;
+
+ // True once we actually lexed the user provided line.
+ bool found_user_line = false;
+
+ // True if we already highlighted the token under the cursor, false otherwise.
+ bool highlighted_cursor = false;
+ Token token;
+ bool exit = false;
+ while (!exit) {
+ // Returns true if this is the last token we get from the lexer.
+ exit = lex.LexFromRawLexer(token);
+
+ bool invalid = false;
+ unsigned current_line_number =
+ SM.getSpellingLineNumber(token.getLocation(), &invalid);
+ if (current_line_number != line_number)
+ continue;
+ found_user_line = true;
+
+ // We don't need to print any tokens without a spelling line number.
+ if (invalid)
+ continue;
+
+ // Same as above but with the column number.
+ invalid = false;
+ unsigned start = SM.getSpellingColumnNumber(token.getLocation(), &invalid);
+ if (invalid)
+ continue;
+ // Column numbers start at 1, but indexes in our string start at 0.
+ --start;
+
+ // Annotations don't have a length, so let's skip them.
+ if (token.isAnnotation())
+ continue;
+
+ // Extract the token string from our source code.
+ llvm::StringRef tok_str = line.substr(start, token.getLength());
+
+ // If the token is just an empty string, we can skip all the work below.
+ if (tok_str.empty())
+ continue;
+
+ // If the cursor is inside this token, we have to apply the 'selected'
+ // highlight style before applying the actual token color.
+ llvm::StringRef to_print = tok_str;
+ StreamString storage;
+ auto end = start + token.getLength();
+ if (cursor_pos && end > *cursor_pos && !highlighted_cursor) {
+ highlighted_cursor = true;
+ options.selected.Apply(storage, tok_str);
+ to_print = storage.GetString();
+ }
+
+ // See how we are supposed to highlight this token.
+ HighlightStyle::ColorStyle color =
+ determineClangStyle(*this, token, tok_str, options, in_pp_directive);
+
+ color.Apply(result, to_print);
+ }
+
+ // If we went over the whole file but couldn't find our own file, then
+ // somehow our setup was wrong. When we're in release mode we just give the
+ // user the normal line and pretend we don't know how to highlight it. In
+ // debug mode we bail out with an assert as this should never happen.
+ if (!found_user_line) {
+ result << line;
+ assert(false && "We couldn't find the user line in the input file?");
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h
new file mode 100644
index 000000000000..f459f9424697
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h
@@ -0,0 +1,37 @@
+//===-- ClangHighlighter.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangHighlighter_h_
+#define liblldb_ClangHighlighter_h_
+
+#include "lldb/Utility/Stream.h"
+#include "llvm/ADT/StringSet.h"
+
+#include "lldb/Core/Highlighter.h"
+
+namespace lldb_private {
+
+class ClangHighlighter : public Highlighter {
+ llvm::StringSet<> keywords;
+
+public:
+ ClangHighlighter();
+ llvm::StringRef GetName() const override { return "clang"; }
+
+ void Highlight(const HighlightStyle &options, llvm::StringRef line,
+ llvm::Optional<size_t> cursor_pos,
+ llvm::StringRef previous_lines, Stream &s) const override;
+
+ /// Returns true if the given string represents a keywords in any Clang
+ /// supported language.
+ bool isKeyword(llvm::StringRef token) const;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangHighlighter_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp
new file mode 100644
index 000000000000..5bca260616ea
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp
@@ -0,0 +1,295 @@
+//===-- CF.cpp ----------------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CF.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+bool lldb_private::formatters::CFAbsoluteTimeSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ time_t epoch = GetOSXEpoch();
+ epoch = epoch + (time_t)valobj.GetValueAsUnsigned(0);
+ tm *tm_date = localtime(&epoch);
+ if (!tm_date)
+ return false;
+ std::string buffer(1024, 0);
+ if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
+ return false;
+ stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
+ tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
+ tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
+ return true;
+}
+
+bool lldb_private::formatters::CFBagSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ static ConstString g_TypeHint("CFBag");
+
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint32_t count = 0;
+
+ bool is_type_ok = false; // check to see if this is a CFBag we know about
+ if (descriptor->IsCFType()) {
+ ConstString type_name(valobj.GetTypeName());
+
+ static ConstString g___CFBag("__CFBag");
+ static ConstString g_conststruct__CFBag("const struct __CFBag");
+
+ if (type_name == g___CFBag || type_name == g_conststruct__CFBag) {
+ if (valobj.IsPointerType())
+ is_type_ok = true;
+ }
+ }
+
+ if (is_type_ok) {
+ lldb::addr_t offset = 2 * ptr_size + 4 + valobj_addr;
+ Status error;
+ count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
+ if (error.Fail())
+ return false;
+ } else
+ return false;
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s\"%u value%s\"%s", prefix.c_str(), count,
+ (count == 1 ? "" : "s"), suffix.c_str());
+ return true;
+}
+
+bool lldb_private::formatters::CFBitVectorSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint32_t count = 0;
+
+ bool is_type_ok = false; // check to see if this is a CFBag we know about
+ if (descriptor->IsCFType()) {
+ ConstString type_name(valobj.GetTypeName());
+ if (type_name == "__CFMutableBitVector" || type_name == "__CFBitVector" ||
+ type_name == "CFMutableBitVectorRef" || type_name == "CFBitVectorRef") {
+ if (valobj.IsPointerType())
+ is_type_ok = true;
+ }
+ }
+
+ if (!is_type_ok)
+ return false;
+
+ Status error;
+ count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + 2 * ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ uint64_t num_bytes = count / 8 + ((count & 7) ? 1 : 0);
+ addr_t data_ptr = process_sp->ReadPointerFromMemory(
+ valobj_addr + 2 * ptr_size + 2 * ptr_size, error);
+ if (error.Fail())
+ return false;
+ // make sure we do not try to read huge amounts of data
+ if (num_bytes > 1024)
+ num_bytes = 1024;
+ DataBufferSP buffer_sp(new DataBufferHeap(num_bytes, 0));
+ num_bytes =
+ process_sp->ReadMemory(data_ptr, buffer_sp->GetBytes(), num_bytes, error);
+ if (error.Fail() || num_bytes == 0)
+ return false;
+ uint8_t *bytes = buffer_sp->GetBytes();
+ for (uint64_t byte_idx = 0; byte_idx < num_bytes - 1; byte_idx++) {
+ uint8_t byte = bytes[byte_idx];
+ bool bit0 = (byte & 1) == 1;
+ bool bit1 = (byte & 2) == 2;
+ bool bit2 = (byte & 4) == 4;
+ bool bit3 = (byte & 8) == 8;
+ bool bit4 = (byte & 16) == 16;
+ bool bit5 = (byte & 32) == 32;
+ bool bit6 = (byte & 64) == 64;
+ bool bit7 = (byte & 128) == 128;
+ stream.Printf("%c%c%c%c %c%c%c%c ", (bit7 ? '1' : '0'), (bit6 ? '1' : '0'),
+ (bit5 ? '1' : '0'), (bit4 ? '1' : '0'), (bit3 ? '1' : '0'),
+ (bit2 ? '1' : '0'), (bit1 ? '1' : '0'), (bit0 ? '1' : '0'));
+ count -= 8;
+ }
+ {
+ // print the last byte ensuring we do not print spurious bits
+ uint8_t byte = bytes[num_bytes - 1];
+ bool bit0 = (byte & 1) == 1;
+ bool bit1 = (byte & 2) == 2;
+ bool bit2 = (byte & 4) == 4;
+ bool bit3 = (byte & 8) == 8;
+ bool bit4 = (byte & 16) == 16;
+ bool bit5 = (byte & 32) == 32;
+ bool bit6 = (byte & 64) == 64;
+ bool bit7 = (byte & 128) == 128;
+ if (count) {
+ stream.Printf("%c", bit7 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit6 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit5 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit4 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit3 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit2 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit1 ? '1' : '0');
+ count -= 1;
+ }
+ if (count)
+ stream.Printf("%c", bit0 ? '1' : '0');
+ }
+ return true;
+}
+
+bool lldb_private::formatters::CFBinaryHeapSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ static ConstString g_TypeHint("CFBinaryHeap");
+
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint32_t count = 0;
+
+ bool is_type_ok =
+ false; // check to see if this is a CFBinaryHeap we know about
+ if (descriptor->IsCFType()) {
+ ConstString type_name(valobj.GetTypeName());
+
+ static ConstString g___CFBinaryHeap("__CFBinaryHeap");
+ static ConstString g_conststruct__CFBinaryHeap(
+ "const struct __CFBinaryHeap");
+ static ConstString g_CFBinaryHeapRef("CFBinaryHeapRef");
+
+ if (type_name == g___CFBinaryHeap ||
+ type_name == g_conststruct__CFBinaryHeap ||
+ type_name == g_CFBinaryHeapRef) {
+ if (valobj.IsPointerType())
+ is_type_ok = true;
+ }
+ }
+
+ if (is_type_ok) {
+ lldb::addr_t offset = 2 * ptr_size + valobj_addr;
+ Status error;
+ count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
+ if (error.Fail())
+ return false;
+ } else
+ return false;
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s\"%u item%s\"%s", prefix.c_str(), count,
+ (count == 1 ? "" : "s"), suffix.c_str());
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h
new file mode 100644
index 000000000000..2abb56d407eb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h
@@ -0,0 +1,32 @@
+//===-- CF.h ---------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CF_h_
+#define liblldb_CF_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+bool CFBagSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool CFBinaryHeapSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool CFBitVectorSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool CFAbsoluteTimeSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_CF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp
new file mode 100644
index 000000000000..ddf3953bb512
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp
@@ -0,0 +1,1123 @@
+//===-- Cocoa.cpp -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Cocoa.h"
+
+#include "lldb/Core/Mangled.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Host/Time.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/bit.h"
+
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
+
+#include "NSString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+bool lldb_private::formatters::NSBundleSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ if (class_name == "NSBundle") {
+ uint64_t offset = 5 * ptr_size;
+ ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
+ offset,
+ valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID),
+ true));
+
+ StreamString summary_stream;
+ bool was_nsstring_ok =
+ NSStringSummaryProvider(*text, summary_stream, options);
+ if (was_nsstring_ok && summary_stream.GetSize() > 0) {
+ stream.Printf("%s", summary_stream.GetData());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::NSTimeZoneSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ if (class_name == "__NSTimeZone") {
+ uint64_t offset = ptr_size;
+ ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
+ offset, valobj.GetCompilerType(), true));
+ StreamString summary_stream;
+ bool was_nsstring_ok =
+ NSStringSummaryProvider(*text, summary_stream, options);
+ if (was_nsstring_ok && summary_stream.GetSize() > 0) {
+ stream.Printf("%s", summary_stream.GetData());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::NSNotificationSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ if (class_name == "NSConcreteNotification") {
+ uint64_t offset = ptr_size;
+ ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
+ offset, valobj.GetCompilerType(), true));
+ StreamString summary_stream;
+ bool was_nsstring_ok =
+ NSStringSummaryProvider(*text, summary_stream, options);
+ if (was_nsstring_ok && summary_stream.GetSize() > 0) {
+ stream.Printf("%s", summary_stream.GetData());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::NSMachPortSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ uint64_t port_number = 0;
+
+ if (class_name == "NSMachPort") {
+ uint64_t offset = (ptr_size == 4 ? 12 : 20);
+ Status error;
+ port_number = process_sp->ReadUnsignedIntegerFromMemory(
+ offset + valobj_addr, 4, 0, error);
+ if (error.Success()) {
+ stream.Printf("mach port: %u",
+ (uint32_t)(port_number & 0x00000000FFFFFFFF));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::NSIndexSetSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ uint64_t count = 0;
+
+ do {
+ if (class_name == "NSIndexSet" || class_name == "NSMutableIndexSet") {
+ Status error;
+ uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + ptr_size, 4, 0, error);
+ if (error.Fail())
+ return false;
+ // this means the set is empty - count = 0
+ if ((mode & 1) == 1) {
+ count = 0;
+ break;
+ }
+ if ((mode & 2) == 2)
+ mode = 1; // this means the set only has one range
+ else
+ mode = 2; // this means the set has multiple ranges
+ if (mode == 1) {
+ count = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + 3 * ptr_size, ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else {
+ // read a pointer to the data at 2*ptr_size
+ count = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + 2 * ptr_size, ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ // read the data at 2*ptr_size from the first location
+ count = process_sp->ReadUnsignedIntegerFromMemory(count + 2 * ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ }
+ } else
+ return false;
+ } while (false);
+ stream.Printf("%" PRIu64 " index%s", count, (count == 1 ? "" : "es"));
+ return true;
+}
+
+static void NSNumber_FormatChar(ValueObject &valobj, Stream &stream, char value,
+ lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:char");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%hhd%s", prefix.c_str(), value, suffix.c_str());
+}
+
+static void NSNumber_FormatShort(ValueObject &valobj, Stream &stream,
+ short value, lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:short");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%hd%s", prefix.c_str(), value, suffix.c_str());
+}
+
+static void NSNumber_FormatInt(ValueObject &valobj, Stream &stream, int value,
+ lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:int");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%d%s", prefix.c_str(), value, suffix.c_str());
+}
+
+static void NSNumber_FormatLong(ValueObject &valobj, Stream &stream,
+ uint64_t value, lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:long");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%" PRId64 "%s", prefix.c_str(), value, suffix.c_str());
+}
+
+static void NSNumber_FormatInt128(ValueObject &valobj, Stream &stream,
+ const llvm::APInt &value,
+ lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:int128_t");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.PutCString(prefix.c_str());
+ const int radix = 10;
+ const bool isSigned = true;
+ std::string str = value.toString(radix, isSigned);
+ stream.PutCString(str.c_str());
+ stream.PutCString(suffix.c_str());
+}
+
+static void NSNumber_FormatFloat(ValueObject &valobj, Stream &stream,
+ float value, lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:float");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%f%s", prefix.c_str(), value, suffix.c_str());
+}
+
+static void NSNumber_FormatDouble(ValueObject &valobj, Stream &stream,
+ double value, lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:double");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%g%s", prefix.c_str(), value, suffix.c_str());
+}
+
+bool lldb_private::formatters::NSNumberSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ if (class_name == "__NSCFBoolean")
+ return ObjCBooleanSummaryProvider(valobj, stream, options);
+
+ if (class_name == "NSDecimalNumber")
+ return NSDecimalNumberSummaryProvider(valobj, stream, options);
+
+ if (class_name == "NSNumber" || class_name == "__NSCFNumber") {
+ uint64_t value = 0;
+ uint64_t i_bits = 0;
+ if (descriptor->GetTaggedPointerInfo(&i_bits, &value)) {
+ switch (i_bits) {
+ case 0:
+ NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
+ break;
+ case 1:
+ case 4:
+ NSNumber_FormatShort(valobj, stream, (short)value,
+ options.GetLanguage());
+ break;
+ case 2:
+ case 8:
+ NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
+ break;
+ case 3:
+ case 12:
+ NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
+ break;
+ default:
+ return false;
+ }
+ return true;
+ } else {
+ Status error;
+
+ AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
+ ObjCLanguageRuntime::Get(*process_sp));
+
+ const bool new_format =
+ (runtime && runtime->GetFoundationVersion() >= 1400);
+
+ enum class TypeCodes : int {
+ sint8 = 0x0,
+ sint16 = 0x1,
+ sint32 = 0x2,
+ sint64 = 0x3,
+ f32 = 0x4,
+ f64 = 0x5,
+ sint128 = 0x6
+ };
+
+ uint64_t data_location = valobj_addr + 2 * ptr_size;
+ TypeCodes type_code;
+
+ if (new_format) {
+ uint64_t cfinfoa =
+ process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+
+ if (error.Fail())
+ return false;
+
+ bool is_preserved_number = cfinfoa & 0x8;
+ if (is_preserved_number) {
+ lldbassert(!static_cast<bool>("We should handle preserved numbers!"));
+ return false;
+ }
+
+ type_code = static_cast<TypeCodes>(cfinfoa & 0x7);
+ } else {
+ uint8_t data_type =
+ process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1,
+ 0, error) & 0x1F;
+
+ if (error.Fail())
+ return false;
+
+ switch (data_type) {
+ case 1: type_code = TypeCodes::sint8; break;
+ case 2: type_code = TypeCodes::sint16; break;
+ case 3: type_code = TypeCodes::sint32; break;
+ case 17: data_location += 8; LLVM_FALLTHROUGH;
+ case 4: type_code = TypeCodes::sint64; break;
+ case 5: type_code = TypeCodes::f32; break;
+ case 6: type_code = TypeCodes::f64; break;
+ default: return false;
+ }
+ }
+
+ uint64_t value = 0;
+ bool success = false;
+ switch (type_code) {
+ case TypeCodes::sint8:
+ value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0,
+ error);
+ if (error.Fail())
+ return false;
+ NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
+ success = true;
+ break;
+ case TypeCodes::sint16:
+ value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0,
+ error);
+ if (error.Fail())
+ return false;
+ NSNumber_FormatShort(valobj, stream, (short)value,
+ options.GetLanguage());
+ success = true;
+ break;
+ case TypeCodes::sint32:
+ value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0,
+ error);
+ if (error.Fail())
+ return false;
+ NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
+ success = true;
+ break;
+ case TypeCodes::sint64:
+ value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0,
+ error);
+ if (error.Fail())
+ return false;
+ NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
+ success = true;
+ break;
+ case TypeCodes::f32:
+ {
+ uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(
+ data_location, 4, 0, error);
+ if (error.Fail())
+ return false;
+ float flt_value = 0.0f;
+ memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));
+ NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
+ success = true;
+ break;
+ }
+ case TypeCodes::f64:
+ {
+ uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(
+ data_location, 8, 0, error);
+ if (error.Fail())
+ return false;
+ double dbl_value = 0.0;
+ memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));
+ NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
+ success = true;
+ break;
+ }
+ case TypeCodes::sint128: // internally, this is the same
+ {
+ uint64_t words[2];
+ words[1] = process_sp->ReadUnsignedIntegerFromMemory(
+ data_location, 8, 0, error);
+ if (error.Fail())
+ return false;
+ words[0] = process_sp->ReadUnsignedIntegerFromMemory(
+ data_location + 8, 8, 0, error);
+ if (error.Fail())
+ return false;
+ llvm::APInt i128_value(128, words);
+ NSNumber_FormatInt128(valobj, stream, i128_value, options.GetLanguage());
+ success = true;
+ break;
+ }
+ }
+ return success;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::NSDecimalNumberSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ Status error;
+ int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + ptr_size, 1, 0, error);
+ if (error.Fail())
+ return false;
+
+ uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + ptr_size + 1, 1, 0, error);
+ if (error.Fail())
+ return false;
+
+ // Fifth bit marks negativity.
+ const bool is_negative = (length_and_negative >> 4) & 1;
+
+ // Zero length and negative means NaN.
+ uint8_t length = length_and_negative & 0xf;
+ const bool is_nan = is_negative && (length == 0);
+
+ if (is_nan) {
+ stream.Printf("NaN");
+ return true;
+ }
+
+ if (length == 0) {
+ stream.Printf("0");
+ return true;
+ }
+
+ uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + ptr_size + 4, 8, 0, error);
+ if (error.Fail())
+ return false;
+
+ if (is_negative)
+ stream.Printf("-");
+
+ stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent);
+ return true;
+}
+
+bool lldb_private::formatters::NSURLSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name = descriptor->GetClassName().GetStringRef();
+
+ if (!class_name.equals("NSURL"))
+ return false;
+
+ uint64_t offset_text = ptr_size + ptr_size +
+ 8; // ISA + pointer + 8 bytes of data (even on 32bit)
+ uint64_t offset_base = offset_text + ptr_size;
+ CompilerType type(valobj.GetCompilerType());
+ ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
+ ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
+ if (!text)
+ return false;
+ if (text->GetValueAsUnsigned(0) == 0)
+ return false;
+ StreamString summary;
+ if (!NSStringSummaryProvider(*text, summary, options))
+ return false;
+ if (base && base->GetValueAsUnsigned(0)) {
+ std::string summary_str = summary.GetString();
+
+ if (!summary_str.empty())
+ summary_str.pop_back();
+ summary_str += " -- ";
+ StreamString base_summary;
+ if (NSURLSummaryProvider(*base, base_summary, options) &&
+ !base_summary.Empty()) {
+ llvm::StringRef base_str = base_summary.GetString();
+ if (base_str.size() > 2)
+ base_str = base_str.drop_front(2);
+ summary_str += base_str;
+ }
+ summary.Clear();
+ summary.PutCString(summary_str);
+ }
+ if (!summary.Empty()) {
+ stream.PutCString(summary.GetString());
+ return true;
+ }
+
+ return false;
+}
+
+/// Bias value for tagged pointer exponents.
+/// Recommended values:
+/// 0x3e3: encodes all dates between distantPast and distantFuture
+/// except for the range within about 1e-28 second of the reference date.
+/// 0x3ef: encodes all dates for a few million years beyond distantPast and
+/// distantFuture, except within about 1e-25 second of the reference date.
+const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef;
+
+struct DoubleBits {
+ uint64_t fraction : 52; // unsigned
+ uint64_t exponent : 11; // signed
+ uint64_t sign : 1;
+};
+
+struct TaggedDoubleBits {
+ uint64_t fraction : 52; // unsigned
+ uint64_t exponent : 7; // signed
+ uint64_t sign : 1;
+ uint64_t unused : 4; // placeholder for pointer tag bits
+};
+
+static uint64_t decodeExponent(uint64_t exp) {
+ // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits
+ // before performing arithmetic.
+ return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS;
+}
+
+static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) {
+ if (encodedTimeInterval == 0)
+ return 0.0;
+ if (encodedTimeInterval == std::numeric_limits<uint64_t>::max())
+ return (uint64_t)-0.0;
+
+ TaggedDoubleBits encodedBits =
+ llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval);
+ assert(encodedBits.unused == 0);
+
+ // Sign and fraction are represented exactly.
+ // Exponent is encoded.
+ DoubleBits decodedBits;
+ decodedBits.sign = encodedBits.sign;
+ decodedBits.fraction = encodedBits.fraction;
+ decodedBits.exponent = decodeExponent(encodedBits.exponent);
+
+ return llvm::bit_cast<double>(decodedBits);
+}
+
+bool lldb_private::formatters::NSDateSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint64_t date_value_bits = 0;
+ double date_value = 0.0;
+
+ ConstString class_name = descriptor->GetClassName();
+
+ static const ConstString g_NSDate("NSDate");
+ static const ConstString g___NSDate("__NSDate");
+ static const ConstString g___NSTaggedDate("__NSTaggedDate");
+ static const ConstString g_NSCalendarDate("NSCalendarDate");
+
+ if (class_name.IsEmpty())
+ return false;
+
+ uint64_t info_bits = 0, value_bits = 0;
+ if ((class_name == g_NSDate) || (class_name == g___NSDate) ||
+ (class_name == g___NSTaggedDate)) {
+ if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) {
+ date_value_bits = ((value_bits << 8) | (info_bits << 4));
+ memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
+ } else {
+ llvm::Triple triple(
+ process_sp->GetTarget().GetArchitecture().GetTriple());
+ uint32_t delta =
+ (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size;
+ Status error;
+ date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + delta, 8, 0, error);
+ memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
+ if (error.Fail())
+ return false;
+ }
+ } else if (class_name == g_NSCalendarDate) {
+ Status error;
+ date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + 2 * ptr_size, 8, 0, error);
+ memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
+ if (error.Fail())
+ return false;
+ } else
+ return false;
+
+ if (date_value == -63114076800) {
+ stream.Printf("0001-12-30 00:00:00 +0000");
+ return true;
+ }
+
+ // Accomodate for the __NSTaggedDate format introduced in Foundation 1600.
+ if (class_name == g___NSTaggedDate) {
+ auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
+ ObjCLanguageRuntime::Get(*process_sp));
+ if (runtime && runtime->GetFoundationVersion() >= 1600)
+ date_value = decodeTaggedTimeInterval(value_bits << 4);
+ }
+
+ // this snippet of code assumes that time_t == seconds since Jan-1-1970 this
+ // is generally true and POSIXly happy, but might break if a library vendor
+ // decides to get creative
+ time_t epoch = GetOSXEpoch();
+ epoch = epoch + (time_t)date_value;
+ tm *tm_date = gmtime(&epoch);
+ if (!tm_date)
+ return false;
+ std::string buffer(1024, 0);
+ if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
+ return false;
+ stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
+ tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
+ tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
+ return true;
+}
+
+bool lldb_private::formatters::ObjCClassSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0)));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ ConstString class_name = descriptor->GetClassName();
+
+ if (class_name.IsEmpty())
+ return false;
+
+ if (ConstString cs =
+ Mangled(class_name).GetDemangledName(lldb::eLanguageTypeUnknown))
+ class_name = cs;
+
+ stream.Printf("%s", class_name.AsCString("<unknown class>"));
+ return true;
+}
+
+class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {}
+
+ ~ObjCClassSyntheticChildrenFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override { return 0; }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ return lldb::ValueObjectSP();
+ }
+
+ bool Update() override { return false; }
+
+ bool MightHaveChildren() override { return false; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return UINT32_MAX;
+ }
+};
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::ObjCClassSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);
+}
+
+template <bool needs_at>
+bool lldb_private::formatters::NSDataSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ bool is_64bit = (process_sp->GetAddressByteSize() == 8);
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint64_t value = 0;
+
+ llvm::StringRef class_name = descriptor->GetClassName().GetCString();
+
+ if (class_name.empty())
+ return false;
+
+ bool isNSConcreteData = class_name == "NSConcreteData";
+ bool isNSConcreteMutableData = class_name == "NSConcreteMutableData";
+ bool isNSCFData = class_name == "__NSCFData";
+ if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) {
+ uint32_t offset;
+ if (isNSConcreteData)
+ offset = is_64bit ? 8 : 4;
+ else
+ offset = is_64bit ? 16 : 8;
+
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + offset, is_64bit ? 8 : 4, 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == "_NSInlineData") {
+ uint32_t offset = (is_64bit ? 8 : 4);
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2,
+ 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == "_NSZeroData") {
+ value = 0;
+ } else
+ return false;
+
+ stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value,
+ (value != 1 ? "s" : ""), (needs_at ? "\"" : ""));
+
+ return true;
+}
+
+bool lldb_private::formatters::ObjCBOOLSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo();
+
+ ValueObjectSP real_guy_sp = valobj.GetSP();
+
+ if (type_info & eTypeIsPointer) {
+ Status err;
+ real_guy_sp = valobj.Dereference(err);
+ if (err.Fail() || !real_guy_sp)
+ return false;
+ } else if (type_info & eTypeIsReference) {
+ real_guy_sp = valobj.GetChildAtIndex(0, true);
+ if (!real_guy_sp)
+ return false;
+ }
+ uint8_t value = (real_guy_sp->GetValueAsUnsigned(0) & 0xFF);
+ switch (value) {
+ case 0:
+ stream.Printf("NO");
+ break;
+ case 1:
+ stream.Printf("YES");
+ break;
+ default:
+ stream.Printf("%u", value);
+ break;
+ }
+ return true;
+}
+
+bool lldb_private::formatters::ObjCBooleanSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ lldb::addr_t valobj_ptr_value =
+ valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ if (valobj_ptr_value == LLDB_INVALID_ADDRESS)
+ return false;
+
+ ProcessSP process_sp(valobj.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ if (AppleObjCRuntime *objc_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
+ ObjCLanguageRuntime::Get(*process_sp))) {
+ lldb::addr_t cf_true = LLDB_INVALID_ADDRESS,
+ cf_false = LLDB_INVALID_ADDRESS;
+ objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false);
+ if (valobj_ptr_value == cf_true) {
+ stream.PutCString("YES");
+ return true;
+ }
+ if (valobj_ptr_value == cf_false) {
+ stream.PutCString("NO");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <bool is_sel_ptr>
+bool lldb_private::formatters::ObjCSELSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ lldb::ValueObjectSP valobj_sp;
+
+ CompilerType charstar(valobj.GetCompilerType()
+ .GetBasicTypeFromAST(eBasicTypeChar)
+ .GetPointerType());
+
+ if (!charstar)
+ return false;
+
+ ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
+
+ if (is_sel_ptr) {
+ lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ if (data_address == LLDB_INVALID_ADDRESS)
+ return false;
+ valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address,
+ exe_ctx, charstar);
+ } else {
+ DataExtractor data;
+ Status error;
+ valobj.GetData(data, error);
+ if (error.Fail())
+ return false;
+ valobj_sp =
+ ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar);
+ }
+
+ if (!valobj_sp)
+ return false;
+
+ stream.Printf("%s", valobj_sp->GetSummaryAsCString());
+ return true;
+}
+
+// POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001
+// this call gives the POSIX equivalent of the Cocoa epoch
+time_t lldb_private::formatters::GetOSXEpoch() {
+ static time_t epoch = 0;
+ if (!epoch) {
+#ifndef _WIN32
+ tzset();
+ tm tm_epoch;
+ tm_epoch.tm_sec = 0;
+ tm_epoch.tm_hour = 0;
+ tm_epoch.tm_min = 0;
+ tm_epoch.tm_mon = 0;
+ tm_epoch.tm_mday = 1;
+ tm_epoch.tm_year = 2001 - 1900;
+ tm_epoch.tm_isdst = -1;
+ tm_epoch.tm_gmtoff = 0;
+ tm_epoch.tm_zone = nullptr;
+ epoch = timegm(&tm_epoch);
+#endif
+ }
+ return epoch;
+}
+
+template bool lldb_private::formatters::NSDataSummaryProvider<true>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
+
+template bool lldb_private::formatters::NSDataSummaryProvider<false>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
+
+template bool lldb_private::formatters::ObjCSELSummaryProvider<true>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
+
+template bool lldb_private::formatters::ObjCSELSummaryProvider<false>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h
new file mode 100644
index 000000000000..388e6f03aa0f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h
@@ -0,0 +1,116 @@
+//===-- Cocoa.h ---------------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_Cocoa_h_
+#define liblldb_Cocoa_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+namespace formatters {
+bool NSIndexSetSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSArraySummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+template <bool needs_at>
+bool NSDataSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSNumberSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSDecimalNumberSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSNotificationSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSTimeZoneSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSMachPortSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSDateSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSBundleSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSURLSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+extern template bool NSDataSummaryProvider<true>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+extern template bool NSDataSummaryProvider<false>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+SyntheticChildrenFrontEnd *
+NSArraySyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+bool ObjCClassSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+SyntheticChildrenFrontEnd *
+ObjCClassSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP);
+
+bool ObjCBOOLSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool ObjCBooleanSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+template <bool is_sel_ptr>
+bool ObjCSELSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+extern template bool ObjCSELSummaryProvider<true>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+extern template bool ObjCSELSummaryProvider<false>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+bool NSError_SummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSException_SummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+SyntheticChildrenFrontEnd *
+NSErrorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
+SyntheticChildrenFrontEnd *
+NSExceptionSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
+class NSArray_Additionals {
+public:
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+ GetAdditionalSummaries();
+
+ static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
+ GetAdditionalSynthetics();
+};
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_Cocoa_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp
new file mode 100644
index 000000000000..d19290ec56fb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp
@@ -0,0 +1,95 @@
+//===-- CoreMedia.cpp --------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoreMedia.h"
+
+#include "lldb/Utility/Flags.h"
+
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Target.h"
+#include <inttypes.h>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+bool lldb_private::formatters::CMTimeSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ CompilerType type = valobj.GetCompilerType();
+ if (!type.IsValid())
+ return false;
+
+ TypeSystem *type_system =
+ valobj.GetExecutionContextRef()
+ .GetTargetSP()
+ ->GetScratchTypeSystemForLanguage(nullptr, lldb::eLanguageTypeC);
+ if (!type_system)
+ return false;
+
+ // fetch children by offset to compensate for potential lack of debug info
+ auto int64_ty =
+ type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 64);
+ auto int32_ty =
+ type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 32);
+
+ auto value_sp(valobj.GetSyntheticChildAtOffset(0, int64_ty, true));
+ auto timescale_sp(valobj.GetSyntheticChildAtOffset(8, int32_ty, true));
+ auto flags_sp(valobj.GetSyntheticChildAtOffset(12, int32_ty, true));
+
+ if (!value_sp || !timescale_sp || !flags_sp)
+ return false;
+
+ auto value = value_sp->GetValueAsUnsigned(0);
+ auto timescale = (int32_t)timescale_sp->GetValueAsUnsigned(
+ 0); // the timescale specifies the fraction of a second each unit in the
+ // numerator occupies
+ auto flags = Flags(flags_sp->GetValueAsUnsigned(0) &
+ 0x00000000000000FF); // the flags I need sit in the LSB
+
+ const unsigned int FlagPositiveInf = 4;
+ const unsigned int FlagNegativeInf = 8;
+ const unsigned int FlagIndefinite = 16;
+
+ if (flags.AnySet(FlagIndefinite)) {
+ stream.Printf("indefinite");
+ return true;
+ }
+
+ if (flags.AnySet(FlagPositiveInf)) {
+ stream.Printf("+oo");
+ return true;
+ }
+
+ if (flags.AnySet(FlagNegativeInf)) {
+ stream.Printf("-oo");
+ return true;
+ }
+
+ if (timescale == 0)
+ return false;
+
+ switch (timescale) {
+ case 0:
+ return false;
+ case 1:
+ stream.Printf("%" PRId64 " seconds", value);
+ return true;
+ case 2:
+ stream.Printf("%" PRId64 " half seconds", value);
+ return true;
+ case 3:
+ stream.Printf("%" PRId64 " third%sof a second", value,
+ value == 1 ? " " : "s ");
+ return true;
+ default:
+ stream.Printf("%" PRId64 " %" PRId32 "th%sof a second", value, timescale,
+ value == 1 ? " " : "s ");
+ return true;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h
new file mode 100644
index 000000000000..79abb67b9d7e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h
@@ -0,0 +1,25 @@
+//===-- CoreMedia.h -----------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CoreMedia_h_
+#define liblldb_CoreMedia_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+
+bool CMTimeSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_CF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp
new file mode 100644
index 000000000000..404dabf2870c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp
@@ -0,0 +1,870 @@
+//===-- NSArray.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+
+#include "Cocoa.h"
+
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace lldb_private {
+namespace formatters {
+std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+NSArray_Additionals::GetAdditionalSummaries() {
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
+ return g_map;
+}
+
+std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
+NSArray_Additionals::GetAdditionalSynthetics() {
+ static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback>
+ g_map;
+ return g_map;
+}
+
+class NSArrayMSyntheticFrontEndBase : public SyntheticChildrenFrontEnd {
+public:
+ NSArrayMSyntheticFrontEndBase(lldb::ValueObjectSP valobj_sp);
+
+ ~NSArrayMSyntheticFrontEndBase() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override = 0;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+protected:
+ virtual lldb::addr_t GetDataAddress() = 0;
+
+ virtual uint64_t GetUsedCount() = 0;
+
+ virtual uint64_t GetOffset() = 0;
+
+ virtual uint64_t GetSize() = 0;
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ CompilerType m_id_type;
+};
+
+template <typename D32, typename D64>
+class GenericNSArrayMSyntheticFrontEnd : public NSArrayMSyntheticFrontEndBase {
+public:
+ GenericNSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~GenericNSArrayMSyntheticFrontEnd() override;
+
+ bool Update() override;
+
+protected:
+ lldb::addr_t GetDataAddress() override;
+
+ uint64_t GetUsedCount() override;
+
+ uint64_t GetOffset() override;
+
+ uint64_t GetSize() override;
+
+private:
+ D32 *m_data_32;
+ D64 *m_data_64;
+};
+
+namespace Foundation109 {
+ struct DataDescriptor_32 {
+ uint32_t _used;
+ uint32_t _priv1 : 2;
+ uint32_t _size : 30;
+ uint32_t _priv2 : 2;
+ uint32_t _offset : 30;
+ uint32_t _priv3;
+ uint32_t _data;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used;
+ uint64_t _priv1 : 2;
+ uint64_t _size : 62;
+ uint64_t _priv2 : 2;
+ uint64_t _offset : 62;
+ uint32_t _priv3;
+ uint64_t _data;
+ };
+
+ using NSArrayMSyntheticFrontEnd =
+ GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1010 {
+ struct DataDescriptor_32 {
+ uint32_t _used;
+ uint32_t _offset;
+ uint32_t _size : 28;
+ uint64_t _priv1 : 4;
+ uint32_t _priv2;
+ uint32_t _data;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used;
+ uint64_t _offset;
+ uint64_t _size : 60;
+ uint64_t _priv1 : 4;
+ uint32_t _priv2;
+ uint64_t _data;
+ };
+
+ using NSArrayMSyntheticFrontEnd =
+ GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1428 {
+ struct DataDescriptor_32 {
+ uint32_t _used;
+ uint32_t _offset;
+ uint32_t _size;
+ uint32_t _data;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used;
+ uint64_t _offset;
+ uint64_t _size;
+ uint64_t _data;
+ };
+
+ using NSArrayMSyntheticFrontEnd =
+ GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1437 {
+ template <typename PtrType>
+ struct DataDescriptor {
+ PtrType _cow;
+ // __deque
+ PtrType _data;
+ uint32_t _offset;
+ uint32_t _size;
+ uint32_t _muts;
+ uint32_t _used;
+ };
+
+ using NSArrayMSyntheticFrontEnd =
+ GenericNSArrayMSyntheticFrontEnd<
+ DataDescriptor<uint32_t>, DataDescriptor<uint64_t>>;
+
+ template <typename DD>
+ uint64_t
+ __NSArrayMSize_Impl(lldb_private::Process &process,
+ lldb::addr_t valobj_addr, Status &error) {
+ const lldb::addr_t start_of_descriptor =
+ valobj_addr + process.GetAddressByteSize();
+ DD descriptor = DD();
+ process.ReadMemory(start_of_descriptor, &descriptor,
+ sizeof(descriptor), error);
+ if (error.Fail()) {
+ return 0;
+ }
+ return descriptor._used;
+ }
+
+ uint64_t
+ __NSArrayMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
+ Status &error) {
+ if (process.GetAddressByteSize() == 4) {
+ return __NSArrayMSize_Impl<DataDescriptor<uint32_t>>(process, valobj_addr,
+ error);
+ } else {
+ return __NSArrayMSize_Impl<DataDescriptor<uint64_t>>(process, valobj_addr,
+ error);
+ }
+ }
+
+}
+
+namespace CallStackArray {
+struct DataDescriptor_32 {
+ uint32_t _data;
+ uint32_t _used;
+ uint32_t _offset;
+ const uint32_t _size = 0;
+};
+
+struct DataDescriptor_64 {
+ uint64_t _data;
+ uint64_t _used;
+ uint64_t _offset;
+ const uint64_t _size = 0;
+};
+
+using NSCallStackArraySyntheticFrontEnd =
+ GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+} // namespace CallStackArray
+
+template <typename D32, typename D64, bool Inline>
+class GenericNSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ GenericNSArrayISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~GenericNSArrayISyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+
+ D32 *m_data_32;
+ D64 *m_data_64;
+ CompilerType m_id_type;
+};
+
+namespace Foundation1300 {
+ struct IDD32 {
+ uint32_t used;
+ uint32_t list;
+ };
+
+ struct IDD64 {
+ uint64_t used;
+ uint64_t list;
+ };
+
+ using NSArrayISyntheticFrontEnd =
+ GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, true>;
+}
+
+namespace Foundation1430 {
+ using NSArrayISyntheticFrontEnd =
+ Foundation1428::NSArrayMSyntheticFrontEnd;
+}
+
+namespace Foundation1436 {
+ struct IDD32 {
+ uint32_t used;
+ uint32_t list; // in Inline cases, this is the first element
+ };
+
+ struct IDD64 {
+ uint64_t used;
+ uint64_t list; // in Inline cases, this is the first element
+ };
+
+ using NSArrayI_TransferSyntheticFrontEnd =
+ GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, false>;
+
+ using NSArrayISyntheticFrontEnd =
+ GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, true>;
+
+ using NSFrozenArrayMSyntheticFrontEnd =
+ Foundation1437::NSArrayMSyntheticFrontEnd;
+
+ uint64_t
+ __NSFrozenArrayMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
+ Status &error) {
+ return Foundation1437::__NSArrayMSize(process, valobj_addr, error);
+ }
+}
+
+class NSArray0SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSArray0SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSArray0SyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+};
+
+class NSArray1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSArray1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSArray1SyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+bool lldb_private::formatters::NSArraySummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ static ConstString g_TypeHint("NSArray");
+
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint64_t value = 0;
+
+ ConstString class_name(descriptor->GetClassName());
+
+ static const ConstString g_NSArrayI("__NSArrayI");
+ static const ConstString g_NSArrayM("__NSArrayM");
+ static const ConstString g_NSArrayI_Transfer("__NSArrayI_Transfer");
+ static const ConstString g_NSFrozenArrayM("__NSFrozenArrayM");
+ static const ConstString g_NSArray0("__NSArray0");
+ static const ConstString g_NSArray1("__NSSingleObjectArrayI");
+ static const ConstString g_NSArrayCF("__NSCFArray");
+ static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy");
+ static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable");
+ static const ConstString g_NSCallStackArray("_NSCallStackArray");
+
+ if (class_name.IsEmpty())
+ return false;
+
+ if (class_name == g_NSArrayI) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSArrayM) {
+ AppleObjCRuntime *apple_runtime =
+ llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
+ Status error;
+ if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
+ value = Foundation1437::__NSArrayMSize(*process_sp, valobj_addr, error);
+ } else {
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ }
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSArrayI_Transfer) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSFrozenArrayM) {
+ Status error;
+ value = Foundation1436::__NSFrozenArrayMSize(*process_sp, valobj_addr, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSArrayMLegacy) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSArrayMImmutable) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSArray0) {
+ value = 0;
+ } else if (class_name == g_NSArray1) {
+ value = 1;
+ } else if (class_name == g_NSArrayCF || class_name == g_NSCallStackArray) {
+ // __NSCFArray and _NSCallStackArray store the number of elements as a
+ // pointer-sized value at offset `2 * ptr_size`.
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + 2 * ptr_size, ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else {
+ auto &map(NSArray_Additionals::GetAdditionalSummaries());
+ auto iter = map.find(class_name), end = map.end();
+ if (iter != end)
+ return iter->second(valobj, stream, options);
+ else
+ return false;
+ }
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element",
+ value == 1 ? "" : "s", suffix.c_str());
+ return true;
+}
+
+lldb_private::formatters::NSArrayMSyntheticFrontEndBase::NSArrayMSyntheticFrontEndBase(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_id_type() {
+ if (valobj_sp) {
+ clang::ASTContext *ast = valobj_sp->GetExecutionContextRef()
+ .GetTargetSP()
+ ->GetScratchClangASTContext()
+ ->getASTContext();
+ if (ast)
+ m_id_type = CompilerType(ast, ast->ObjCBuiltinIdTy);
+ if (valobj_sp->GetProcessSP())
+ m_ptr_size = valobj_sp->GetProcessSP()->GetAddressByteSize();
+ }
+}
+
+template <typename D32, typename D64>
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ GenericNSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : NSArrayMSyntheticFrontEndBase(valobj_sp), m_data_32(nullptr),
+ m_data_64(nullptr) {}
+
+size_t
+lldb_private::formatters::NSArrayMSyntheticFrontEndBase::CalculateNumChildren() {
+ return GetUsedCount();
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSArrayMSyntheticFrontEndBase::GetChildAtIndex(
+ size_t idx) {
+ if (idx >= CalculateNumChildren())
+ return lldb::ValueObjectSP();
+ lldb::addr_t object_at_idx = GetDataAddress();
+ size_t pyhs_idx = idx;
+ pyhs_idx += GetOffset();
+ if (GetSize() <= pyhs_idx)
+ pyhs_idx -= GetSize();
+ object_at_idx += (pyhs_idx * m_ptr_size);
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return CreateValueObjectFromAddress(idx_name.GetString(), object_at_idx,
+ m_exe_ctx_ref, m_id_type);
+}
+
+template <typename D32, typename D64>
+bool
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::Update() {
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ m_ptr_size = 0;
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new D32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
+ error);
+ } else {
+ m_data_64 = new D64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ return false;
+}
+
+bool
+lldb_private::formatters::NSArrayMSyntheticFrontEndBase::MightHaveChildren() {
+ return true;
+}
+
+size_t
+lldb_private::formatters::NSArrayMSyntheticFrontEndBase::GetIndexOfChildWithName(
+ ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+template <typename D32, typename D64>
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ ~GenericNSArrayMSyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+template <typename D32, typename D64>
+lldb::addr_t
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ GenericNSArrayMSyntheticFrontEnd::GetDataAddress() {
+ if (!m_data_32 && !m_data_64)
+ return LLDB_INVALID_ADDRESS;
+ return m_data_32 ? m_data_32->_data : m_data_64->_data;
+}
+
+template <typename D32, typename D64>
+uint64_t
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ GenericNSArrayMSyntheticFrontEnd::GetUsedCount() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return m_data_32 ? m_data_32->_used : m_data_64->_used;
+}
+
+template <typename D32, typename D64>
+uint64_t
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ GenericNSArrayMSyntheticFrontEnd::GetOffset() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return m_data_32 ? m_data_32->_offset : m_data_64->_offset;
+}
+
+template <typename D32, typename D64>
+uint64_t
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ GenericNSArrayMSyntheticFrontEnd::GetSize() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return m_data_32 ? m_data_32->_size : m_data_64->_size;
+}
+
+template <typename D32, typename D64, bool Inline>
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ GenericNSArrayISyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_data_32(nullptr), m_data_64(nullptr) {
+ if (valobj_sp) {
+ CompilerType type = valobj_sp->GetCompilerType();
+ if (type) {
+ ClangASTContext *ast = valobj_sp->GetExecutionContextRef()
+ .GetTargetSP()
+ ->GetScratchClangASTContext();
+ if (ast)
+ m_id_type = CompilerType(ast->getASTContext(),
+ ast->getASTContext()->ObjCBuiltinIdTy);
+ }
+ }
+}
+
+template <typename D32, typename D64, bool Inline>
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ ~GenericNSArrayISyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+template <typename D32, typename D64, bool Inline>
+size_t
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ GetIndexOfChildWithName(ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+template <typename D32, typename D64, bool Inline>
+size_t
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ CalculateNumChildren() {
+ return m_data_32 ? m_data_32->used : m_data_64->used;
+}
+
+template <typename D32, typename D64, bool Inline>
+bool
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ Update() {
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ m_ptr_size = 0;
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new D32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
+ error);
+ } else {
+ m_data_64 = new D64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ return false;
+}
+
+template <typename D32, typename D64, bool Inline>
+bool
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ MightHaveChildren() {
+ return true;
+}
+
+template <typename D32, typename D64, bool Inline>
+lldb::ValueObjectSP
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ GetChildAtIndex(size_t idx) {
+ if (idx >= CalculateNumChildren())
+ return lldb::ValueObjectSP();
+ lldb::addr_t object_at_idx;
+ if (Inline) {
+ object_at_idx = m_backend.GetSP()->GetValueAsUnsigned(0) + m_ptr_size;
+ object_at_idx += m_ptr_size == 4 ? sizeof(D32) : sizeof(D64); // skip the data header
+ object_at_idx -= m_ptr_size; // we treat the last entry in the data header as the first pointer
+ } else {
+ object_at_idx = m_data_32 ? m_data_32->list : m_data_64->list;
+ }
+ object_at_idx += (idx * m_ptr_size);
+
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return CreateValueObjectFromAddress(idx_name.GetString(), object_at_idx,
+ m_exe_ctx_ref, m_id_type);
+}
+
+lldb_private::formatters::NSArray0SyntheticFrontEnd::NSArray0SyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {}
+
+size_t
+lldb_private::formatters::NSArray0SyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ return UINT32_MAX;
+}
+
+size_t
+lldb_private::formatters::NSArray0SyntheticFrontEnd::CalculateNumChildren() {
+ return 0;
+}
+
+bool lldb_private::formatters::NSArray0SyntheticFrontEnd::Update() {
+ return false;
+}
+
+bool lldb_private::formatters::NSArray0SyntheticFrontEnd::MightHaveChildren() {
+ return false;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSArray0SyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ return lldb::ValueObjectSP();
+}
+
+lldb_private::formatters::NSArray1SyntheticFrontEnd::NSArray1SyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp.get()) {}
+
+size_t
+lldb_private::formatters::NSArray1SyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ static const ConstString g_zero("[0]");
+
+ if (name == g_zero)
+ return 0;
+
+ return UINT32_MAX;
+}
+
+size_t
+lldb_private::formatters::NSArray1SyntheticFrontEnd::CalculateNumChildren() {
+ return 1;
+}
+
+bool lldb_private::formatters::NSArray1SyntheticFrontEnd::Update() {
+ return false;
+}
+
+bool lldb_private::formatters::NSArray1SyntheticFrontEnd::MightHaveChildren() {
+ return true;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSArray1SyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ static const ConstString g_zero("[0]");
+
+ if (idx == 0) {
+ CompilerType id_type(
+ m_backend.GetTargetSP()->GetScratchClangASTContext()->GetBasicType(
+ lldb::eBasicTypeObjCID));
+ return m_backend.GetSyntheticChildAtOffset(
+ m_backend.GetProcessSP()->GetAddressByteSize(), id_type, true, g_zero);
+ }
+ return lldb::ValueObjectSP();
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::NSArraySyntheticFrontEndCreator(
+ CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+ AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
+ ObjCLanguageRuntime::Get(*process_sp));
+ if (!runtime)
+ return nullptr;
+
+ CompilerType valobj_type(valobj_sp->GetCompilerType());
+ Flags flags(valobj_type.GetTypeInfo());
+
+ if (flags.IsClear(eTypeIsPointer)) {
+ Status error;
+ valobj_sp = valobj_sp->AddressOf(error);
+ if (error.Fail() || !valobj_sp)
+ return nullptr;
+ }
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(*valobj_sp));
+
+ if (!descriptor || !descriptor->IsValid())
+ return nullptr;
+
+ ConstString class_name(descriptor->GetClassName());
+
+ static const ConstString g_NSArrayI("__NSArrayI");
+ static const ConstString g_NSArrayI_Transfer("__NSArrayI_Transfer");
+ static const ConstString g_NSFrozenArrayM("__NSFrozenArrayM");
+ static const ConstString g_NSArrayM("__NSArrayM");
+ static const ConstString g_NSArray0("__NSArray0");
+ static const ConstString g_NSArray1("__NSSingleObjectArrayI");
+ static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy");
+ static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable");
+ static const ConstString g_NSCallStackArray("_NSCallStackArray");
+
+ if (class_name.IsEmpty())
+ return nullptr;
+
+ if (class_name == g_NSArrayI) {
+ if (runtime->GetFoundationVersion() >= 1436)
+ return (new Foundation1436::NSArrayISyntheticFrontEnd(valobj_sp));
+ if (runtime->GetFoundationVersion() >= 1430)
+ return (new Foundation1430::NSArrayISyntheticFrontEnd(valobj_sp));
+ else
+ return (new Foundation1300::NSArrayISyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSArrayI_Transfer) {
+ return (new Foundation1436::NSArrayI_TransferSyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSArray0) {
+ } else if (class_name == g_NSFrozenArrayM) {
+ return (new Foundation1436::NSFrozenArrayMSyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSArray0) {
+ return (new NSArray0SyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSArray1) {
+ return (new NSArray1SyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSArrayM) {
+ if (runtime->GetFoundationVersion() >= 1437)
+ return (new Foundation1437::NSArrayMSyntheticFrontEnd(valobj_sp));
+ if (runtime->GetFoundationVersion() >= 1428)
+ return (new Foundation1428::NSArrayMSyntheticFrontEnd(valobj_sp));
+ if (runtime->GetFoundationVersion() >= 1100)
+ return (new Foundation1010::NSArrayMSyntheticFrontEnd(valobj_sp));
+ else
+ return (new Foundation109::NSArrayMSyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSCallStackArray) {
+ return (new CallStackArray::NSCallStackArraySyntheticFrontEnd(valobj_sp));
+ } else {
+ auto &map(NSArray_Additionals::GetAdditionalSynthetics());
+ auto iter = map.find(class_name), end = map.end();
+ if (iter != end)
+ return iter->second(synth, valobj_sp);
+ }
+
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp
new file mode 100644
index 000000000000..10f66c4a37f8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp
@@ -0,0 +1,1049 @@
+//===-- NSDictionary.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <mutex>
+
+#include "clang/AST/DeclCXX.h"
+
+#include "NSDictionary.h"
+
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix(
+ ConstString p)
+ : m_prefix(p) {}
+
+bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match(
+ ConstString class_name) {
+ return class_name.GetStringRef().startswith(m_prefix.GetStringRef());
+}
+
+NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n)
+ : m_name(n) {}
+
+bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match(
+ ConstString class_name) {
+ return (class_name == m_name);
+}
+
+NSDictionary_Additionals::AdditionalFormatters<
+ CXXFunctionSummaryFormat::Callback> &
+NSDictionary_Additionals::GetAdditionalSummaries() {
+ static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map;
+ return g_map;
+}
+
+NSDictionary_Additionals::AdditionalFormatters<
+ CXXSyntheticChildren::CreateFrontEndCallback> &
+NSDictionary_Additionals::GetAdditionalSynthetics() {
+ static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback>
+ g_map;
+ return g_map;
+}
+
+static CompilerType GetLLDBNSPairType(TargetSP target_sp) {
+ CompilerType compiler_type;
+
+ ClangASTContext *target_ast_context = target_sp->GetScratchClangASTContext();
+
+ if (target_ast_context) {
+ ConstString g___lldb_autogen_nspair("__lldb_autogen_nspair");
+
+ compiler_type =
+ target_ast_context->GetTypeForIdentifier<clang::CXXRecordDecl>(
+ g___lldb_autogen_nspair);
+
+ if (!compiler_type) {
+ compiler_type = target_ast_context->CreateRecordType(
+ nullptr, lldb::eAccessPublic, g___lldb_autogen_nspair.GetCString(),
+ clang::TTK_Struct, lldb::eLanguageTypeC);
+
+ if (compiler_type) {
+ ClangASTContext::StartTagDeclarationDefinition(compiler_type);
+ CompilerType id_compiler_type =
+ target_ast_context->GetBasicType(eBasicTypeObjCID);
+ ClangASTContext::AddFieldToRecordType(
+ compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0);
+ ClangASTContext::AddFieldToRecordType(
+ compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0);
+ ClangASTContext::CompleteTagDeclarationDefinition(compiler_type);
+ }
+ }
+ }
+ return compiler_type;
+}
+
+namespace lldb_private {
+namespace formatters {
+class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSDictionaryISyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _szidx : 6;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint32_t _szidx : 6;
+ };
+
+ struct DictionaryItemDescriptor {
+ lldb::addr_t key_ptr;
+ lldb::addr_t val_ptr;
+ lldb::ValueObjectSP valobj_sp;
+ };
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ lldb::ByteOrder m_order;
+ DataDescriptor_32 *m_data_32;
+ DataDescriptor_64 *m_data_64;
+ lldb::addr_t m_data_ptr;
+ CompilerType m_pair_type;
+ std::vector<DictionaryItemDescriptor> m_children;
+};
+
+class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSDictionary1SyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ ValueObjectSP m_pair;
+};
+
+template <typename D32, typename D64>
+class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~GenericNSDictionaryMSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ struct DictionaryItemDescriptor {
+ lldb::addr_t key_ptr;
+ lldb::addr_t val_ptr;
+ lldb::ValueObjectSP valobj_sp;
+ };
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ lldb::ByteOrder m_order;
+ D32 *m_data_32;
+ D64 *m_data_64;
+ CompilerType m_pair_type;
+ std::vector<DictionaryItemDescriptor> m_children;
+};
+
+namespace Foundation1100 {
+ class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+ public:
+ NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSDictionaryMSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+ private:
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _kvo : 1;
+ uint32_t _size;
+ uint32_t _mutations;
+ uint32_t _objs_addr;
+ uint32_t _keys_addr;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint32_t _kvo : 1;
+ uint64_t _size;
+ uint64_t _mutations;
+ uint64_t _objs_addr;
+ uint64_t _keys_addr;
+ };
+
+ struct DictionaryItemDescriptor {
+ lldb::addr_t key_ptr;
+ lldb::addr_t val_ptr;
+ lldb::ValueObjectSP valobj_sp;
+ };
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ lldb::ByteOrder m_order;
+ DataDescriptor_32 *m_data_32;
+ DataDescriptor_64 *m_data_64;
+ CompilerType m_pair_type;
+ std::vector<DictionaryItemDescriptor> m_children;
+ };
+}
+
+namespace Foundation1428 {
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _kvo : 1;
+ uint32_t _size;
+ uint32_t _buffer;
+ uint64_t GetSize() { return _size; }
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint32_t _kvo : 1;
+ uint64_t _size;
+ uint64_t _buffer;
+ uint64_t GetSize() { return _size; }
+ };
+
+
+
+ using NSDictionaryMSyntheticFrontEnd =
+ GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1437 {
+ static const uint64_t NSDictionaryCapacities[] = {
+ 0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
+ 2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
+ 214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
+ 6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
+ 111638519, 180634607, 292272623, 472907251
+ };
+
+ static const size_t NSDictionaryNumSizeBuckets = sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
+
+ struct DataDescriptor_32 {
+ uint32_t _buffer;
+ uint32_t _muts;
+ uint32_t _used : 25;
+ uint32_t _kvo : 1;
+ uint32_t _szidx : 6;
+
+ uint64_t GetSize() {
+ return (_szidx) >= NSDictionaryNumSizeBuckets ?
+ 0 : NSDictionaryCapacities[_szidx];
+ }
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _buffer;
+ uint32_t _muts;
+ uint32_t _used : 25;
+ uint32_t _kvo : 1;
+ uint32_t _szidx : 6;
+
+ uint64_t GetSize() {
+ return (_szidx) >= NSDictionaryNumSizeBuckets ?
+ 0 : NSDictionaryCapacities[_szidx];
+ }
+ };
+
+ using NSDictionaryMSyntheticFrontEnd =
+ GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+
+ template <typename DD>
+ uint64_t
+ __NSDictionaryMSize_Impl(lldb_private::Process &process,
+ lldb::addr_t valobj_addr, Status &error) {
+ const lldb::addr_t start_of_descriptor =
+ valobj_addr + process.GetAddressByteSize();
+ DD descriptor = DD();
+ process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
+ error);
+ if (error.Fail()) {
+ return 0;
+ }
+ return descriptor._used;
+ }
+
+ uint64_t
+ __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
+ Status &error) {
+ if (process.GetAddressByteSize() == 4) {
+ return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
+ error);
+ } else {
+ return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
+ error);
+ }
+ }
+
+}
+} // namespace formatters
+} // namespace lldb_private
+
+template <bool name_entries>
+bool lldb_private::formatters::NSDictionarySummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ static ConstString g_TypeHint("NSDictionary");
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+ bool is_64bit = (ptr_size == 8);
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint64_t value = 0;
+
+ ConstString class_name(descriptor->GetClassName());
+
+ static const ConstString g_DictionaryI("__NSDictionaryI");
+ static const ConstString g_DictionaryM("__NSDictionaryM");
+ static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
+ static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
+ static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
+ static const ConstString g_Dictionary0("__NSDictionary0");
+ static const ConstString g_DictionaryCF("__NSCFDictionary");
+
+ if (class_name.IsEmpty())
+ return false;
+
+ if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
+ } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
+ class_name == g_DictionaryCF) {
+ AppleObjCRuntime *apple_runtime =
+ llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
+ Status error;
+ if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
+ value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
+ error);
+ } else {
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
+ }
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_Dictionary1) {
+ value = 1;
+ } else if (class_name == g_Dictionary0) {
+ value = 0;
+ }
+ else {
+ auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
+ for (auto &candidate : map) {
+ if (candidate.first && candidate.first->Match(class_name))
+ return candidate.second(valobj, stream, options);
+ }
+ return false;
+ }
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "key/value pair",
+ value == 1 ? "" : "s", suffix.c_str());
+ return true;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
+ CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+ AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
+ ObjCLanguageRuntime::Get(*process_sp));
+ if (!runtime)
+ return nullptr;
+
+ CompilerType valobj_type(valobj_sp->GetCompilerType());
+ Flags flags(valobj_type.GetTypeInfo());
+
+ if (flags.IsClear(eTypeIsPointer)) {
+ Status error;
+ valobj_sp = valobj_sp->AddressOf(error);
+ if (error.Fail() || !valobj_sp)
+ return nullptr;
+ }
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(*valobj_sp));
+
+ if (!descriptor || !descriptor->IsValid())
+ return nullptr;
+
+ ConstString class_name(descriptor->GetClassName());
+
+ static const ConstString g_DictionaryI("__NSDictionaryI");
+ static const ConstString g_DictionaryM("__NSDictionaryM");
+ static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
+ static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
+ static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
+ static const ConstString g_Dictionary0("__NSDictionary0");
+
+ if (class_name.IsEmpty())
+ return nullptr;
+
+ if (class_name == g_DictionaryI) {
+ return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_DictionaryM) {
+ if (runtime->GetFoundationVersion() >= 1437) {
+ return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp));
+ } else if (runtime->GetFoundationVersion() >= 1428) {
+ return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp));
+ } else {
+ return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
+ }
+ } else if (class_name == g_DictionaryMLegacy) {
+ return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_Dictionary1) {
+ return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
+ } else {
+ auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
+ for (auto &candidate : map) {
+ if (candidate.first && candidate.first->Match((class_name)))
+ return candidate.second(synth, valobj_sp);
+ }
+ }
+
+ return nullptr;
+}
+
+lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
+ NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
+ m_pair_type() {}
+
+lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
+ ~NSDictionaryISyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
+ CalculateNumChildren() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return (m_data_32 ? m_data_32->_used : m_data_64->_used);
+}
+
+bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
+ m_children.clear();
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ m_ptr_size = 0;
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ m_order = process_sp->GetByteOrder();
+ uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new DataDescriptor_32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
+ error);
+ } else {
+ m_data_64 = new DataDescriptor_64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ m_data_ptr = data_location + m_ptr_size;
+ return false;
+}
+
+bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ uint32_t num_children = CalculateNumChildren();
+
+ if (idx >= num_children)
+ return lldb::ValueObjectSP();
+
+ if (m_children.empty()) {
+ // do the scan phase
+ lldb::addr_t key_at_idx = 0, val_at_idx = 0;
+
+ uint32_t tries = 0;
+ uint32_t test_idx = 0;
+
+ while (tries < num_children) {
+ key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
+ val_at_idx = key_at_idx + m_ptr_size;
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+ val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ test_idx++;
+
+ if (!key_at_idx || !val_at_idx)
+ continue;
+ tries++;
+
+ DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
+ lldb::ValueObjectSP()};
+
+ m_children.push_back(descriptor);
+ }
+ }
+
+ if (idx >= m_children.size()) // should never happen
+ return lldb::ValueObjectSP();
+
+ DictionaryItemDescriptor &dict_item = m_children[idx];
+ if (!dict_item.valobj_sp) {
+ if (!m_pair_type.IsValid()) {
+ TargetSP target_sp(m_backend.GetTargetSP());
+ if (!target_sp)
+ return ValueObjectSP();
+ m_pair_type = GetLLDBNSPairType(target_sp);
+ }
+ if (!m_pair_type.IsValid())
+ return ValueObjectSP();
+
+ DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
+
+ if (m_ptr_size == 8) {
+ uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ } else {
+ uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ }
+
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ DataExtractor data(buffer_sp, m_order, m_ptr_size);
+ dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
+ m_exe_ctx_ref, m_pair_type);
+ }
+ return dict_item.valobj_sp;
+}
+
+lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
+ NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
+
+size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ static const ConstString g_zero("[0]");
+ return name == g_zero ? 0 : UINT32_MAX;
+}
+
+size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
+ CalculateNumChildren() {
+ return 1;
+}
+
+bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
+ m_pair.reset();
+ return false;
+}
+
+bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ if (idx != 0)
+ return lldb::ValueObjectSP();
+
+ if (m_pair.get())
+ return m_pair;
+
+ auto process_sp(m_backend.GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+
+ auto ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t key_ptr =
+ m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
+ lldb::addr_t value_ptr = key_ptr + ptr_size;
+
+ Status error;
+
+ lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
+ if (error.Fail())
+ return nullptr;
+ lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
+ if (error.Fail())
+ return nullptr;
+
+ auto pair_type =
+ GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
+
+ DataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
+
+ if (ptr_size == 8) {
+ uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
+ *data_ptr = key_at_idx;
+ *(data_ptr + 1) = value_at_idx;
+ } else {
+ uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
+ *data_ptr = key_at_idx;
+ *(data_ptr + 1) = value_at_idx;
+ }
+
+ DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
+ m_pair = CreateValueObjectFromData(
+ "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
+
+ return m_pair;
+}
+
+template <typename D32, typename D64>
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
+ GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
+ m_pair_type() {}
+
+template <typename D32, typename D64>
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
+ ~GenericNSDictionaryMSyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+template <typename D32, typename D64>
+size_t
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>:: GetIndexOfChildWithName(ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+template <typename D32, typename D64>
+size_t
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::CalculateNumChildren() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return (m_data_32 ? m_data_32->_used : m_data_64->_used);
+}
+
+template <typename D32, typename D64>
+bool
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
+ Update() {
+ m_children.clear();
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ m_ptr_size = 0;
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ m_order = process_sp->GetByteOrder();
+ uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new D32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
+ error);
+ } else {
+ m_data_64 = new D64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ return false;
+}
+
+template <typename D32, typename D64>
+bool
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
+ MightHaveChildren() {
+ return true;
+}
+
+template <typename D32, typename D64>
+lldb::ValueObjectSP
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
+ GetChildAtIndex(
+ size_t idx) {
+ lldb::addr_t m_keys_ptr;
+ lldb::addr_t m_values_ptr;
+ if (m_data_32) {
+ uint32_t size = m_data_32->GetSize();
+ m_keys_ptr = m_data_32->_buffer;
+ m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
+ } else {
+ uint32_t size = m_data_64->GetSize();
+ m_keys_ptr = m_data_64->_buffer;
+ m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
+ }
+
+ uint32_t num_children = CalculateNumChildren();
+
+ if (idx >= num_children)
+ return lldb::ValueObjectSP();
+
+ if (m_children.empty()) {
+ // do the scan phase
+ lldb::addr_t key_at_idx = 0, val_at_idx = 0;
+
+ uint32_t tries = 0;
+ uint32_t test_idx = 0;
+
+ while (tries < num_children) {
+ key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
+ val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
+ ;
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+ val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ test_idx++;
+
+ if (!key_at_idx || !val_at_idx)
+ continue;
+ tries++;
+
+ DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
+ lldb::ValueObjectSP()};
+
+ m_children.push_back(descriptor);
+ }
+ }
+
+ if (idx >= m_children.size()) // should never happen
+ return lldb::ValueObjectSP();
+
+ DictionaryItemDescriptor &dict_item = m_children[idx];
+ if (!dict_item.valobj_sp) {
+ if (!m_pair_type.IsValid()) {
+ TargetSP target_sp(m_backend.GetTargetSP());
+ if (!target_sp)
+ return ValueObjectSP();
+ m_pair_type = GetLLDBNSPairType(target_sp);
+ }
+ if (!m_pair_type.IsValid())
+ return ValueObjectSP();
+
+ DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
+
+ if (m_ptr_size == 8) {
+ uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ } else {
+ uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ }
+
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ DataExtractor data(buffer_sp, m_order, m_ptr_size);
+ dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
+ m_exe_ctx_ref, m_pair_type);
+ }
+ return dict_item.valobj_sp;
+}
+
+
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::
+ NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
+ m_pair_type() {}
+
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+size_t
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+size_t
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return (m_data_32 ? m_data_32->_used : m_data_64->_used);
+}
+
+bool
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::Update() {
+ m_children.clear();
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ m_ptr_size = 0;
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ m_order = process_sp->GetByteOrder();
+ uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new DataDescriptor_32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
+ error);
+ } else {
+ m_data_64 = new DataDescriptor_64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ return false;
+}
+
+bool
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
+ return true;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ lldb::addr_t m_keys_ptr =
+ (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
+ lldb::addr_t m_values_ptr =
+ (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
+
+ uint32_t num_children = CalculateNumChildren();
+
+ if (idx >= num_children)
+ return lldb::ValueObjectSP();
+
+ if (m_children.empty()) {
+ // do the scan phase
+ lldb::addr_t key_at_idx = 0, val_at_idx = 0;
+
+ uint32_t tries = 0;
+ uint32_t test_idx = 0;
+
+ while (tries < num_children) {
+ key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
+ val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
+ ;
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+ val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ test_idx++;
+
+ if (!key_at_idx || !val_at_idx)
+ continue;
+ tries++;
+
+ DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
+ lldb::ValueObjectSP()};
+
+ m_children.push_back(descriptor);
+ }
+ }
+
+ if (idx >= m_children.size()) // should never happen
+ return lldb::ValueObjectSP();
+
+ DictionaryItemDescriptor &dict_item = m_children[idx];
+ if (!dict_item.valobj_sp) {
+ if (!m_pair_type.IsValid()) {
+ TargetSP target_sp(m_backend.GetTargetSP());
+ if (!target_sp)
+ return ValueObjectSP();
+ m_pair_type = GetLLDBNSPairType(target_sp);
+ }
+ if (!m_pair_type.IsValid())
+ return ValueObjectSP();
+
+ DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
+
+ if (m_ptr_size == 8) {
+ uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ } else {
+ uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ }
+
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ DataExtractor data(buffer_sp, m_order, m_ptr_size);
+ dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
+ m_exe_ctx_ref, m_pair_type);
+ }
+ return dict_item.valobj_sp;
+}
+
+template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
+
+template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h
new file mode 100644
index 000000000000..ecb3fccdf877
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h
@@ -0,0 +1,94 @@
+//===-- NSDictionary.h ---------------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NSDictionary_h_
+#define liblldb_NSDictionary_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+#include <map>
+#include <memory>
+
+namespace lldb_private {
+namespace formatters {
+template <bool name_entries>
+bool NSDictionarySummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+extern template bool
+NSDictionarySummaryProvider<true>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+extern template bool
+NSDictionarySummaryProvider<false>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+SyntheticChildrenFrontEnd *
+NSDictionarySyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+class NSDictionary_Additionals {
+public:
+ class AdditionalFormatterMatching {
+ public:
+ class Matcher {
+ public:
+ virtual ~Matcher() = default;
+ virtual bool Match(ConstString class_name) = 0;
+
+ typedef std::unique_ptr<Matcher> UP;
+ };
+ class Prefix : public Matcher {
+ public:
+ Prefix(ConstString p);
+ ~Prefix() override = default;
+ bool Match(ConstString class_name) override;
+
+ private:
+ ConstString m_prefix;
+ };
+ class Full : public Matcher {
+ public:
+ Full(ConstString n);
+ ~Full() override = default;
+ bool Match(ConstString class_name) override;
+
+ private:
+ ConstString m_name;
+ };
+ typedef Matcher::UP MatcherUP;
+
+ MatcherUP GetFullMatch(ConstString n) { return llvm::make_unique<Full>(n); }
+
+ MatcherUP GetPrefixMatch(ConstString p) {
+ return llvm::make_unique<Prefix>(p);
+ }
+ };
+
+ template <typename FormatterType>
+ using AdditionalFormatter =
+ std::pair<AdditionalFormatterMatching::MatcherUP, FormatterType>;
+
+ template <typename FormatterType>
+ using AdditionalFormatters = std::vector<AdditionalFormatter<FormatterType>>;
+
+ static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> &
+ GetAdditionalSummaries();
+
+ static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback> &
+ GetAdditionalSynthetics();
+};
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_NSDictionary_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp
new file mode 100644
index 000000000000..97df3be72c84
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp
@@ -0,0 +1,211 @@
+//===-- NSError.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/DeclCXX.h"
+
+#include "Cocoa.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/Language/ObjC/NSString.h"
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+static lldb::addr_t DerefToNSErrorPointer(ValueObject &valobj) {
+ CompilerType valobj_type(valobj.GetCompilerType());
+ Flags type_flags(valobj_type.GetTypeInfo());
+ if (type_flags.AllClear(eTypeHasValue)) {
+ if (valobj.IsBaseClass() && valobj.GetParent())
+ return valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ } else {
+ lldb::addr_t ptr_value = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ if (type_flags.AllSet(eTypeIsPointer)) {
+ CompilerType pointee_type(valobj_type.GetPointeeType());
+ Flags pointee_flags(pointee_type.GetTypeInfo());
+ if (pointee_flags.AllSet(eTypeIsPointer)) {
+ if (ProcessSP process_sp = valobj.GetProcessSP()) {
+ Status error;
+ ptr_value = process_sp->ReadPointerFromMemory(ptr_value, error);
+ }
+ }
+ }
+ return ptr_value;
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool lldb_private::formatters::NSError_SummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp(valobj.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t ptr_value = DerefToNSErrorPointer(valobj);
+ if (ptr_value == LLDB_INVALID_ADDRESS)
+ return false;
+
+ size_t ptr_size = process_sp->GetAddressByteSize();
+ lldb::addr_t code_location = ptr_value + 2 * ptr_size;
+ lldb::addr_t domain_location = ptr_value + 3 * ptr_size;
+
+ Status error;
+ uint64_t code = process_sp->ReadUnsignedIntegerFromMemory(code_location,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+
+ lldb::addr_t domain_str_value =
+ process_sp->ReadPointerFromMemory(domain_location, error);
+ if (error.Fail() || domain_str_value == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (!domain_str_value) {
+ stream.Printf("domain: nil - code: %" PRIu64, code);
+ return true;
+ }
+
+ InferiorSizedWord isw(domain_str_value, *process_sp);
+
+ ValueObjectSP domain_str_sp = ValueObject::CreateValueObjectFromData(
+ "domain_str", isw.GetAsData(process_sp->GetByteOrder()),
+ valobj.GetExecutionContextRef(), process_sp->GetTarget()
+ .GetScratchClangASTContext()
+ ->GetBasicType(lldb::eBasicTypeVoid)
+ .GetPointerType());
+
+ if (!domain_str_sp)
+ return false;
+
+ StreamString domain_str_summary;
+ if (NSStringSummaryProvider(*domain_str_sp, domain_str_summary, options) &&
+ !domain_str_summary.Empty()) {
+ stream.Printf("domain: %s - code: %" PRIu64, domain_str_summary.GetData(),
+ code);
+ return true;
+ } else {
+ stream.Printf("domain: nil - code: %" PRIu64, code);
+ return true;
+ }
+}
+
+class NSErrorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSErrorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {}
+
+ ~NSErrorSyntheticFrontEnd() override = default;
+ // no need to delete m_child_ptr - it's kept alive by the cluster manager on
+ // our behalf
+
+ size_t CalculateNumChildren() override {
+ if (m_child_ptr)
+ return 1;
+ if (m_child_sp)
+ return 1;
+ return 0;
+ }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ if (idx != 0)
+ return lldb::ValueObjectSP();
+
+ if (m_child_ptr)
+ return m_child_ptr->GetSP();
+ return m_child_sp;
+ }
+
+ bool Update() override {
+ m_child_ptr = nullptr;
+ m_child_sp.reset();
+
+ ProcessSP process_sp(m_backend.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t userinfo_location = DerefToNSErrorPointer(m_backend);
+ if (userinfo_location == LLDB_INVALID_ADDRESS)
+ return false;
+
+ size_t ptr_size = process_sp->GetAddressByteSize();
+
+ userinfo_location += 4 * ptr_size;
+ Status error;
+ lldb::addr_t userinfo =
+ process_sp->ReadPointerFromMemory(userinfo_location, error);
+ if (userinfo == LLDB_INVALID_ADDRESS || error.Fail())
+ return false;
+ InferiorSizedWord isw(userinfo, *process_sp);
+ m_child_sp = CreateValueObjectFromData(
+ "_userInfo", isw.GetAsData(process_sp->GetByteOrder()),
+ m_backend.GetExecutionContextRef(),
+ process_sp->GetTarget().GetScratchClangASTContext()->GetBasicType(
+ lldb::eBasicTypeObjCID));
+ return false;
+ }
+
+ bool MightHaveChildren() override { return true; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ static ConstString g___userInfo("_userInfo");
+ if (name == g___userInfo)
+ return 0;
+ return UINT32_MAX;
+ }
+
+private:
+ // the child here can be "real" (i.e. an actual child of the root) or
+ // synthetized from raw memory if the former, I need to store a plain pointer
+ // to it - or else a loop of references will cause this entire hierarchy of
+ // values to leak if the latter, then I need to store a SharedPointer to it -
+ // so that it only goes away when everyone else in the cluster goes away oh
+ // joy!
+ ValueObject *m_child_ptr;
+ ValueObjectSP m_child_sp;
+};
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::NSErrorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+ if (!runtime)
+ return nullptr;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(*valobj_sp.get()));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return nullptr;
+
+ const char *class_name = descriptor->GetClassName().GetCString();
+
+ if (!class_name || !*class_name)
+ return nullptr;
+
+ if (!strcmp(class_name, "NSError"))
+ return (new NSErrorSyntheticFrontEnd(valobj_sp));
+ else if (!strcmp(class_name, "__NSCFError"))
+ return (new NSErrorSyntheticFrontEnd(valobj_sp));
+
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp
new file mode 100644
index 000000000000..931794a12ab1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp
@@ -0,0 +1,205 @@
+//===-- NSException.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/DeclCXX.h"
+
+#include "Cocoa.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/Language/ObjC/NSString.h"
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+static bool ExtractFields(ValueObject &valobj, ValueObjectSP *name_sp,
+ ValueObjectSP *reason_sp, ValueObjectSP *userinfo_sp,
+ ValueObjectSP *reserved_sp) {
+ ProcessSP process_sp(valobj.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t ptr = LLDB_INVALID_ADDRESS;
+
+ CompilerType valobj_type(valobj.GetCompilerType());
+ Flags type_flags(valobj_type.GetTypeInfo());
+ if (type_flags.AllClear(eTypeHasValue)) {
+ if (valobj.IsBaseClass() && valobj.GetParent())
+ ptr = valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ } else {
+ ptr = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ }
+
+ if (ptr == LLDB_INVALID_ADDRESS)
+ return false;
+ size_t ptr_size = process_sp->GetAddressByteSize();
+
+ Status error;
+ auto name = process_sp->ReadPointerFromMemory(ptr + 1 * ptr_size, error);
+ if (error.Fail() || name == LLDB_INVALID_ADDRESS)
+ return false;
+ auto reason = process_sp->ReadPointerFromMemory(ptr + 2 * ptr_size, error);
+ if (error.Fail() || reason == LLDB_INVALID_ADDRESS)
+ return false;
+ auto userinfo = process_sp->ReadPointerFromMemory(ptr + 3 * ptr_size, error);
+ if (error.Fail() || userinfo == LLDB_INVALID_ADDRESS)
+ return false;
+ auto reserved = process_sp->ReadPointerFromMemory(ptr + 4 * ptr_size, error);
+ if (error.Fail() || reserved == LLDB_INVALID_ADDRESS)
+ return false;
+
+ InferiorSizedWord name_isw(name, *process_sp);
+ InferiorSizedWord reason_isw(reason, *process_sp);
+ InferiorSizedWord userinfo_isw(userinfo, *process_sp);
+ InferiorSizedWord reserved_isw(reserved, *process_sp);
+
+ CompilerType voidstar = process_sp->GetTarget()
+ .GetScratchClangASTContext()
+ ->GetBasicType(lldb::eBasicTypeVoid)
+ .GetPointerType();
+
+ if (name_sp)
+ *name_sp = ValueObject::CreateValueObjectFromData(
+ "name", name_isw.GetAsData(process_sp->GetByteOrder()),
+ valobj.GetExecutionContextRef(), voidstar);
+ if (reason_sp)
+ *reason_sp = ValueObject::CreateValueObjectFromData(
+ "reason", reason_isw.GetAsData(process_sp->GetByteOrder()),
+ valobj.GetExecutionContextRef(), voidstar);
+ if (userinfo_sp)
+ *userinfo_sp = ValueObject::CreateValueObjectFromData(
+ "userInfo", userinfo_isw.GetAsData(process_sp->GetByteOrder()),
+ valobj.GetExecutionContextRef(), voidstar);
+ if (reserved_sp)
+ *reserved_sp = ValueObject::CreateValueObjectFromData(
+ "reserved", reserved_isw.GetAsData(process_sp->GetByteOrder()),
+ valobj.GetExecutionContextRef(), voidstar);
+
+ return true;
+}
+
+bool lldb_private::formatters::NSException_SummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ lldb::ValueObjectSP name_sp;
+ lldb::ValueObjectSP reason_sp;
+ if (!ExtractFields(valobj, &name_sp, &reason_sp, nullptr, nullptr))
+ return false;
+
+ if (!name_sp || !reason_sp)
+ return false;
+
+ StreamString name_str_summary;
+ StreamString reason_str_summary;
+ if (NSStringSummaryProvider(*name_sp, name_str_summary, options) &&
+ NSStringSummaryProvider(*reason_sp, reason_str_summary, options) &&
+ !name_str_summary.Empty() && !reason_str_summary.Empty()) {
+ stream.Printf("name: %s - reason: %s", name_str_summary.GetData(),
+ reason_str_summary.GetData());
+ return true;
+ } else
+ return false;
+}
+
+class NSExceptionSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSExceptionSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {}
+
+ ~NSExceptionSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override {
+ return 4;
+ }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ switch (idx) {
+ case 0: return m_name_sp;
+ case 1: return m_reason_sp;
+ case 2: return m_userinfo_sp;
+ case 3: return m_reserved_sp;
+ }
+ return lldb::ValueObjectSP();
+ }
+
+ bool Update() override {
+ m_name_sp.reset();
+ m_reason_sp.reset();
+ m_userinfo_sp.reset();
+ m_reserved_sp.reset();
+
+ return ExtractFields(m_backend, &m_name_sp, &m_reason_sp, &m_userinfo_sp,
+ &m_reserved_sp);
+ }
+
+ bool MightHaveChildren() override { return true; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ // NSException has 4 members:
+ // NSString *name;
+ // NSString *reason;
+ // NSDictionary *userInfo;
+ // id reserved;
+ static ConstString g___name("name");
+ static ConstString g___reason("reason");
+ static ConstString g___userInfo("userInfo");
+ static ConstString g___reserved("reserved");
+ if (name == g___name) return 0;
+ if (name == g___reason) return 1;
+ if (name == g___userInfo) return 2;
+ if (name == g___reserved) return 3;
+ return UINT32_MAX;
+ }
+
+private:
+ ValueObjectSP m_name_sp;
+ ValueObjectSP m_reason_sp;
+ ValueObjectSP m_userinfo_sp;
+ ValueObjectSP m_reserved_sp;
+};
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::NSExceptionSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+ if (!runtime)
+ return nullptr;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(*valobj_sp.get()));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return nullptr;
+
+ const char *class_name = descriptor->GetClassName().GetCString();
+
+ if (!class_name || !*class_name)
+ return nullptr;
+
+ if (!strcmp(class_name, "NSException"))
+ return (new NSExceptionSyntheticFrontEnd(valobj_sp));
+ else if (!strcmp(class_name, "NSCFException"))
+ return (new NSExceptionSyntheticFrontEnd(valobj_sp));
+ else if (!strcmp(class_name, "__NSCFException"))
+ return (new NSExceptionSyntheticFrontEnd(valobj_sp));
+
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp
new file mode 100644
index 000000000000..9ee6021ae56b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp
@@ -0,0 +1,313 @@
+//===-- NSIndexPath.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Cocoa.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+static constexpr size_t PACKED_INDEX_SHIFT_64(size_t i) {
+ return (60 - (13 * (4 - i)));
+}
+
+static constexpr size_t PACKED_INDEX_SHIFT_32(size_t i) {
+ return (32 - (13 * (2 - i)));
+}
+
+class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSIndexPathSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_descriptor_sp(nullptr),
+ m_impl(), m_ptr_size(0), m_uint_star_type() {
+ m_ptr_size =
+ m_backend.GetTargetSP()->GetArchitecture().GetAddressByteSize();
+ }
+
+ ~NSIndexPathSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override { return m_impl.GetNumIndexes(); }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ return m_impl.GetIndexAtIndex(idx, m_uint_star_type);
+ }
+
+ bool Update() override {
+ m_impl.Clear();
+
+ TypeSystem *type_system = m_backend.GetCompilerType().GetTypeSystem();
+ if (!type_system)
+ return false;
+
+ ClangASTContext *ast = m_backend.GetExecutionContextRef()
+ .GetTargetSP()
+ ->GetScratchClangASTContext();
+ if (!ast)
+ return false;
+
+ m_uint_star_type = ast->GetPointerSizedIntType(false);
+
+ static ConstString g__indexes("_indexes");
+ static ConstString g__length("_length");
+
+ ProcessSP process_sp = m_backend.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(m_backend));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return false;
+
+ uint64_t info_bits(0), value_bits(0), payload(0);
+
+ if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits, &payload)) {
+ m_impl.m_inlined.SetIndexes(payload, *process_sp);
+ m_impl.m_mode = Mode::Inlined;
+ } else {
+ ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _indexes_id;
+ ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _length_id;
+
+ bool has_indexes(false), has_length(false);
+
+ for (size_t x = 0; x < descriptor->GetNumIVars(); x++) {
+ const auto &ivar = descriptor->GetIVarAtIndex(x);
+ if (ivar.m_name == g__indexes) {
+ _indexes_id = ivar;
+ has_indexes = true;
+ } else if (ivar.m_name == g__length) {
+ _length_id = ivar;
+ has_length = true;
+ }
+
+ if (has_length && has_indexes)
+ break;
+ }
+
+ if (has_length && has_indexes) {
+ m_impl.m_outsourced.m_indexes =
+ m_backend
+ .GetSyntheticChildAtOffset(_indexes_id.m_offset,
+ m_uint_star_type.GetPointerType(),
+ true)
+ .get();
+ ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset(
+ _length_id.m_offset, m_uint_star_type, true));
+ if (length_sp) {
+ m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(0);
+ if (m_impl.m_outsourced.m_indexes)
+ m_impl.m_mode = Mode::Outsourced;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+ }
+
+ lldb::ValueObjectSP GetSyntheticValue() override { return nullptr; }
+
+protected:
+ ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp;
+
+ enum class Mode { Inlined, Outsourced, Invalid };
+
+ struct Impl {
+ size_t GetNumIndexes() {
+ switch (m_mode) {
+ case Mode::Inlined:
+ return m_inlined.GetNumIndexes();
+ case Mode::Outsourced:
+ return m_outsourced.m_count;
+ default:
+ return 0;
+ }
+ }
+
+ lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
+ const CompilerType &desired_type) {
+ if (idx >= GetNumIndexes())
+ return nullptr;
+ switch (m_mode) {
+ default:
+ return nullptr;
+ case Mode::Inlined:
+ return m_inlined.GetIndexAtIndex(idx, desired_type);
+ case Mode::Outsourced:
+ return m_outsourced.GetIndexAtIndex(idx);
+ }
+ }
+
+ struct InlinedIndexes {
+ public:
+ void SetIndexes(uint64_t value, Process &p) {
+ m_indexes = value;
+ _lengthForInlinePayload(p.GetAddressByteSize());
+ m_process = &p;
+ }
+
+ size_t GetNumIndexes() { return m_count; }
+
+ lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
+ const CompilerType &desired_type) {
+ if (!m_process)
+ return nullptr;
+
+ std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx));
+ if (!value.second)
+ return nullptr;
+
+ Value v;
+ if (m_ptr_size == 8) {
+ Scalar scalar((unsigned long long)value.first);
+ v = Value(scalar);
+ } else {
+ Scalar scalar((unsigned int)value.first);
+ v = Value(scalar);
+ }
+
+ v.SetCompilerType(desired_type);
+
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+
+ return ValueObjectConstResult::Create(
+ m_process, v, ConstString(idx_name.GetString()));
+ }
+
+ void Clear() {
+ m_indexes = 0;
+ m_count = 0;
+ m_ptr_size = 0;
+ m_process = nullptr;
+ }
+
+ InlinedIndexes()
+ : m_indexes(0), m_count(0), m_ptr_size(0), m_process(nullptr) {}
+
+ private:
+ uint64_t m_indexes;
+ size_t m_count;
+ uint32_t m_ptr_size;
+ Process *m_process;
+
+ // cfr. Foundation for the details of this code
+ size_t _lengthForInlinePayload(uint32_t ptr_size) {
+ m_ptr_size = ptr_size;
+ if (m_ptr_size == 8)
+ m_count = ((m_indexes >> 3) & 0x7);
+ else
+ m_count = ((m_indexes >> 3) & 0x3);
+ return m_count;
+ }
+
+ std::pair<uint64_t, bool> _indexAtPositionForInlinePayload(size_t pos) {
+ static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1);
+ if (m_ptr_size == 8) {
+ switch (pos) {
+ case 3:
+ case 2:
+ case 1:
+ case 0:
+ return {(m_indexes >> PACKED_INDEX_SHIFT_64(pos)) &
+ PACKED_INDEX_MASK,
+ true};
+ default:
+ return {0, false};
+ }
+ } else {
+ switch (pos) {
+ case 0:
+ case 1:
+ return {(m_indexes >> PACKED_INDEX_SHIFT_32(pos)) &
+ PACKED_INDEX_MASK,
+ true};
+ default:
+ return {0, false};
+ }
+ }
+ return {0, false};
+ }
+ };
+
+ struct OutsourcedIndexes {
+ lldb::ValueObjectSP GetIndexAtIndex(size_t idx) {
+ if (m_indexes) {
+ ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true));
+ return index_sp;
+ }
+ return nullptr;
+ }
+
+ void Clear() {
+ m_indexes = nullptr;
+ m_count = 0;
+ }
+
+ OutsourcedIndexes() : m_indexes(nullptr), m_count(0) {}
+
+ ValueObject *m_indexes;
+ size_t m_count;
+ };
+
+ union {
+ struct InlinedIndexes m_inlined;
+ struct OutsourcedIndexes m_outsourced;
+ };
+
+ void Clear() {
+ m_mode = Mode::Invalid;
+ m_inlined.Clear();
+ m_outsourced.Clear();
+ }
+
+ Impl() : m_mode(Mode::Invalid) {}
+
+ Mode m_mode;
+ } m_impl;
+
+ uint32_t m_ptr_size;
+ CompilerType m_uint_star_type;
+};
+
+namespace lldb_private {
+namespace formatters {
+
+SyntheticChildrenFrontEnd *
+NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new NSIndexPathSyntheticFrontEnd(valobj_sp);
+ return nullptr;
+}
+
+} // namespace formatters
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp
new file mode 100644
index 000000000000..ebaa990fb74b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp
@@ -0,0 +1,675 @@
+//===-- NSSet.cpp -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NSSet.h"
+
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+NSSet_Additionals::GetAdditionalSummaries() {
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
+ return g_map;
+}
+
+std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
+NSSet_Additionals::GetAdditionalSynthetics() {
+ static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback>
+ g_map;
+ return g_map;
+}
+
+namespace lldb_private {
+namespace formatters {
+class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSSetISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSSetISyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _szidx : 6;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint32_t _szidx : 6;
+ };
+
+ struct SetItemDescriptor {
+ lldb::addr_t item_ptr;
+ lldb::ValueObjectSP valobj_sp;
+ };
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ DataDescriptor_32 *m_data_32;
+ DataDescriptor_64 *m_data_64;
+ lldb::addr_t m_data_ptr;
+ std::vector<SetItemDescriptor> m_children;
+};
+
+template <typename D32, typename D64>
+class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~GenericNSSetMSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+
+ struct SetItemDescriptor {
+ lldb::addr_t item_ptr;
+ lldb::ValueObjectSP valobj_sp;
+ };
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ D32 *m_data_32;
+ D64 *m_data_64;
+ std::vector<SetItemDescriptor> m_children;
+};
+
+namespace Foundation1300 {
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _size;
+ uint32_t _mutations;
+ uint32_t _objs_addr;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint64_t _size;
+ uint64_t _mutations;
+ uint64_t _objs_addr;
+ };
+
+ using NSSetMSyntheticFrontEnd =
+ GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1428 {
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _size;
+ uint32_t _objs_addr;
+ uint32_t _mutations;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint64_t _size;
+ uint64_t _objs_addr;
+ uint64_t _mutations;
+ };
+
+ using NSSetMSyntheticFrontEnd =
+ GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1437 {
+ struct DataDescriptor_32 {
+ uint32_t _cow;
+ // __table storage
+ uint32_t _objs_addr;
+ uint32_t _muts;
+ uint32_t _used : 26;
+ uint32_t _szidx : 6;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _cow;
+ // __Table storage
+ uint64_t _objs_addr;
+ uint32_t _muts;
+ uint32_t _used : 26;
+ uint32_t _szidx : 6;
+ };
+
+ using NSSetMSyntheticFrontEnd =
+ GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+
+ template <typename DD>
+ uint64_t
+ __NSSetMSize_Impl(lldb_private::Process &process, lldb::addr_t valobj_addr,
+ Status &error) {
+ const lldb::addr_t start_of_descriptor =
+ valobj_addr + process.GetAddressByteSize();
+ DD descriptor = DD();
+ process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
+ error);
+ if (error.Fail()) {
+ return 0;
+ }
+ return descriptor._used;
+ }
+
+ uint64_t
+ __NSSetMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
+ Status &error) {
+ if (process.GetAddressByteSize() == 4) {
+ return __NSSetMSize_Impl<DataDescriptor_32>(process, valobj_addr, error);
+ } else {
+ return __NSSetMSize_Impl<DataDescriptor_64>(process, valobj_addr, error);
+ }
+ }
+}
+
+class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSSetCodeRunningSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSSetCodeRunningSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+template <bool cf_style>
+bool lldb_private::formatters::NSSetSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ static ConstString g_TypeHint("NSSet");
+
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+ bool is_64bit = (ptr_size == 8);
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint64_t value = 0;
+
+ ConstString class_name_cs = descriptor->GetClassName();
+ const char *class_name = class_name_cs.GetCString();
+
+ if (!class_name || !*class_name)
+ return false;
+
+ if (!strcmp(class_name, "__NSSetI") ||
+ !strcmp(class_name, "__NSOrderedSetI")) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
+ } else if (!strcmp(class_name, "__NSSetM")) {
+ AppleObjCRuntime *apple_runtime =
+ llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
+ Status error;
+ if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
+ value = Foundation1437::__NSSetMSize(*process_sp, valobj_addr, error);
+ } else {
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
+ }
+ if (error.Fail())
+ return false;
+ } else {
+ auto &map(NSSet_Additionals::GetAdditionalSummaries());
+ auto iter = map.find(class_name_cs), end = map.end();
+ if (iter != end)
+ return iter->second(valobj, stream, options);
+ else
+ return false;
+ }
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element",
+ value == 1 ? "" : "s", suffix.c_str());
+ return true;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::NSSetSyntheticFrontEndCreator(
+ CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+ if (!runtime)
+ return nullptr;
+
+ CompilerType valobj_type(valobj_sp->GetCompilerType());
+ Flags flags(valobj_type.GetTypeInfo());
+
+ if (flags.IsClear(eTypeIsPointer)) {
+ Status error;
+ valobj_sp = valobj_sp->AddressOf(error);
+ if (error.Fail() || !valobj_sp)
+ return nullptr;
+ }
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(*valobj_sp));
+
+ if (!descriptor || !descriptor->IsValid())
+ return nullptr;
+
+ ConstString class_name_cs = descriptor->GetClassName();
+ const char *class_name = class_name_cs.GetCString();
+
+ if (!class_name || !*class_name)
+ return nullptr;
+
+ if (!strcmp(class_name, "__NSSetI") ||
+ !strcmp(class_name, "__NSOrderedSetI")) {
+ return (new NSSetISyntheticFrontEnd(valobj_sp));
+ } else if (!strcmp(class_name, "__NSSetM")) {
+ AppleObjCRuntime *apple_runtime =
+ llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
+ if (apple_runtime) {
+ if (apple_runtime->GetFoundationVersion() >= 1437)
+ return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp));
+ else if (apple_runtime->GetFoundationVersion() >= 1428)
+ return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp));
+ else
+ return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
+ } else {
+ return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
+ }
+ } else {
+ auto &map(NSSet_Additionals::GetAdditionalSynthetics());
+ auto iter = map.find(class_name_cs), end = map.end();
+ if (iter != end)
+ return iter->second(synth, valobj_sp);
+ return nullptr;
+ }
+}
+
+lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_data_32(nullptr), m_data_64(nullptr) {
+ if (valobj_sp)
+ Update();
+}
+
+lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+size_t
+lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+size_t
+lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return (m_data_32 ? m_data_32->_used : m_data_64->_used);
+}
+
+bool lldb_private::formatters::NSSetISyntheticFrontEnd::Update() {
+ m_children.clear();
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ m_ptr_size = 0;
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ if (valobj_sp->IsPointerType()) {
+ valobj_sp = valobj_sp->Dereference(error);
+ if (error.Fail() || !valobj_sp)
+ return false;
+ }
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new DataDescriptor_32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
+ error);
+ } else {
+ m_data_64 = new DataDescriptor_64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ m_data_ptr = data_location + m_ptr_size;
+ return false;
+}
+
+bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() {
+ return true;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ uint32_t num_children = CalculateNumChildren();
+
+ if (idx >= num_children)
+ return lldb::ValueObjectSP();
+
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+
+ if (m_children.empty()) {
+ // do the scan phase
+ lldb::addr_t obj_at_idx = 0;
+
+ uint32_t tries = 0;
+ uint32_t test_idx = 0;
+
+ while (tries < num_children) {
+ obj_at_idx = m_data_ptr + (test_idx * m_ptr_size);
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ test_idx++;
+
+ if (!obj_at_idx)
+ continue;
+ tries++;
+
+ SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
+
+ m_children.push_back(descriptor);
+ }
+ }
+
+ if (idx >= m_children.size()) // should never happen
+ return lldb::ValueObjectSP();
+
+ SetItemDescriptor &set_item = m_children[idx];
+ if (!set_item.valobj_sp) {
+ auto ptr_size = process_sp->GetAddressByteSize();
+ DataBufferHeap buffer(ptr_size, 0);
+ switch (ptr_size) {
+ case 0: // architecture has no clue?? - fail
+ return lldb::ValueObjectSP();
+ case 4:
+ *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
+ break;
+ case 8:
+ *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
+ break;
+ default:
+ assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
+ }
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+
+ DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
+ process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+
+ set_item.valobj_sp = CreateValueObjectFromData(
+ idx_name.GetString(), data, m_exe_ctx_ref,
+ m_backend.GetCompilerType().GetBasicTypeFromAST(
+ lldb::eBasicTypeObjCID));
+ }
+ return set_item.valobj_sp;
+}
+
+template <typename D32, typename D64>
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::GenericNSSetMSyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_data_32(nullptr), m_data_64(nullptr) {
+ if (valobj_sp)
+ Update();
+}
+
+template <typename D32, typename D64>
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::~GenericNSSetMSyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+template <typename D32, typename D64>
+size_t
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName(
+ ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+template <typename D32, typename D64>
+size_t
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::CalculateNumChildren() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return (m_data_32 ? m_data_32->_used : m_data_64->_used);
+}
+
+template <typename D32, typename D64>
+bool
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() {
+ m_children.clear();
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ m_ptr_size = 0;
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ if (!valobj_sp)
+ return false;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ if (valobj_sp->IsPointerType()) {
+ valobj_sp = valobj_sp->Dereference(error);
+ if (error.Fail() || !valobj_sp)
+ return false;
+ }
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new D32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
+ error);
+ } else {
+ m_data_64 = new D64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ return false;
+}
+
+template <typename D32, typename D64>
+bool
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() {
+ return true;
+}
+
+template <typename D32, typename D64>
+lldb::ValueObjectSP
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(size_t idx) {
+ lldb::addr_t m_objs_addr =
+ (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
+
+ uint32_t num_children = CalculateNumChildren();
+
+ if (idx >= num_children)
+ return lldb::ValueObjectSP();
+
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+
+ if (m_children.empty()) {
+ // do the scan phase
+ lldb::addr_t obj_at_idx = 0;
+
+ uint32_t tries = 0;
+ uint32_t test_idx = 0;
+
+ while (tries < num_children) {
+ obj_at_idx = m_objs_addr + (test_idx * m_ptr_size);
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ test_idx++;
+
+ if (!obj_at_idx)
+ continue;
+ tries++;
+
+ SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
+
+ m_children.push_back(descriptor);
+ }
+ }
+
+ if (idx >= m_children.size()) // should never happen
+ return lldb::ValueObjectSP();
+
+ SetItemDescriptor &set_item = m_children[idx];
+ if (!set_item.valobj_sp) {
+ auto ptr_size = process_sp->GetAddressByteSize();
+ DataBufferHeap buffer(ptr_size, 0);
+ switch (ptr_size) {
+ case 0: // architecture has no clue?? - fail
+ return lldb::ValueObjectSP();
+ case 4:
+ *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
+ break;
+ case 8:
+ *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
+ break;
+ default:
+ assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
+ }
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+
+ DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
+ process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+
+ set_item.valobj_sp = CreateValueObjectFromData(
+ idx_name.GetString(), data, m_exe_ctx_ref,
+ m_backend.GetCompilerType().GetBasicTypeFromAST(
+ lldb::eBasicTypeObjCID));
+ }
+ return set_item.valobj_sp;
+}
+
+template bool lldb_private::formatters::NSSetSummaryProvider<true>(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
+
+template bool lldb_private::formatters::NSSetSummaryProvider<false>(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h
new file mode 100644
index 000000000000..f11b6d406dec
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h
@@ -0,0 +1,39 @@
+//===-- NSSet.h ---------------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NSSet_h_
+#define liblldb_NSSet_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+template <bool cf_style>
+bool NSSetSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+SyntheticChildrenFrontEnd *NSSetSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+class NSSet_Additionals {
+public:
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+ GetAdditionalSummaries();
+
+ static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
+ GetAdditionalSynthetics();
+};
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_NSSet_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp
new file mode 100644
index 000000000000..4800c955e5f5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp
@@ -0,0 +1,396 @@
+//===-- NSString.cpp ----------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NSString.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+NSString_Additionals::GetAdditionalSummaries() {
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
+ return g_map;
+}
+
+static CompilerType GetNSPathStore2Type(Target &target) {
+ static ConstString g_type_name("__lldb_autogen_nspathstore2");
+
+ ClangASTContext *ast_ctx = target.GetScratchClangASTContext();
+
+ if (!ast_ctx)
+ return CompilerType();
+
+ CompilerType voidstar =
+ ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType();
+ CompilerType uint32 =
+ ast_ctx->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
+
+ return ast_ctx->GetOrCreateStructForIdentifier(
+ g_type_name,
+ {{"isa", voidstar}, {"lengthAndRef", uint32}, {"buffer", voidstar}});
+}
+
+bool lldb_private::formatters::NSStringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options) {
+ static ConstString g_TypeHint("NSString");
+
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ ConstString class_name_cs = descriptor->GetClassName();
+ const char *class_name = class_name_cs.GetCString();
+
+ if (!class_name || !*class_name)
+ return false;
+
+ bool is_tagged_ptr = (0 == strcmp(class_name, "NSTaggedPointerString")) &&
+ descriptor->GetTaggedPointerInfo();
+ // for a tagged pointer, the descriptor has everything we need
+ if (is_tagged_ptr)
+ return NSTaggedString_SummaryProvider(valobj, descriptor, stream,
+ summary_options);
+
+ auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());
+ auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();
+ if (iter != end)
+ return iter->second(valobj, stream, summary_options);
+
+ // if not a tagged pointer that we know about, try the normal route
+ uint64_t info_bits_location = valobj_addr + ptr_size;
+ if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
+ info_bits_location += 3;
+
+ Status error;
+
+ uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(
+ info_bits_location, 1, 0, error);
+ if (error.Fail())
+ return false;
+
+ bool is_mutable = (info_bits & 1) == 1;
+ bool is_inline = (info_bits & 0x60) == 0;
+ bool has_explicit_length = (info_bits & (1 | 4)) != 4;
+ bool is_unicode = (info_bits & 0x10) == 0x10;
+ bool is_path_store = strcmp(class_name, "NSPathStore2") == 0;
+ bool has_null = (info_bits & 8) == 8;
+
+ size_t explicit_length = 0;
+ if (!has_null && has_explicit_length && !is_path_store) {
+ lldb::addr_t explicit_length_offset = 2 * ptr_size;
+ if (is_mutable && !is_inline)
+ explicit_length_offset =
+ explicit_length_offset + ptr_size; // notInlineMutable.length;
+ else if (is_inline)
+ explicit_length = explicit_length + 0; // inline1.length;
+ else if (!is_inline && !is_mutable)
+ explicit_length_offset =
+ explicit_length_offset + ptr_size; // notInlineImmutable1.length;
+ else
+ explicit_length_offset = 0;
+
+ if (explicit_length_offset) {
+ explicit_length_offset = valobj_addr + explicit_length_offset;
+ explicit_length = process_sp->ReadUnsignedIntegerFromMemory(
+ explicit_length_offset, 4, 0, error);
+ }
+ }
+
+ if (strcmp(class_name, "NSString") && strcmp(class_name, "CFStringRef") &&
+ strcmp(class_name, "CFMutableStringRef") &&
+ strcmp(class_name, "__NSCFConstantString") &&
+ strcmp(class_name, "__NSCFString") &&
+ strcmp(class_name, "NSCFConstantString") &&
+ strcmp(class_name, "NSCFString") && strcmp(class_name, "NSPathStore2")) {
+ // not one of us - but tell me class name
+ stream.Printf("class name = %s", class_name);
+ return true;
+ }
+
+ std::string prefix, suffix;
+ if (Language *language =
+ Language::FindPlugin(summary_options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ options.SetPrefixToken(prefix);
+ options.SetSuffixToken(suffix);
+
+ if (is_mutable) {
+ uint64_t location = 2 * ptr_size + valobj_addr;
+ location = process_sp->ReadPointerFromMemory(location, error);
+ if (error.Fail())
+ return false;
+ if (has_explicit_length && is_unicode) {
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetQuote('"');
+ options.SetSourceSize(explicit_length);
+ options.SetNeedsZeroTermination(false);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetBinaryZeroIsTerminator(false);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ } else {
+ options.SetLocation(location + 1);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetSourceSize(explicit_length);
+ options.SetNeedsZeroTermination(false);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetBinaryZeroIsTerminator(false);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::ASCII>(options);
+ }
+ } else if (is_inline && has_explicit_length && !is_unicode &&
+ !is_path_store && !is_mutable) {
+ uint64_t location = 3 * ptr_size + valobj_addr;
+
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetQuote('"');
+ options.SetSourceSize(explicit_length);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::ASCII>(options);
+ } else if (is_unicode) {
+ uint64_t location = valobj_addr + 2 * ptr_size;
+ if (is_inline) {
+ if (!has_explicit_length) {
+ return false;
+ } else
+ location += ptr_size;
+ } else {
+ location = process_sp->ReadPointerFromMemory(location, error);
+ if (error.Fail())
+ return false;
+ }
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetQuote('"');
+ options.SetSourceSize(explicit_length);
+ options.SetNeedsZeroTermination(!has_explicit_length);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetBinaryZeroIsTerminator(!has_explicit_length);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ } else if (is_path_store) {
+ ProcessStructReader reader(valobj.GetProcessSP().get(),
+ valobj.GetValueAsUnsigned(0),
+ GetNSPathStore2Type(*valobj.GetTargetSP()));
+ explicit_length =
+ reader.GetField<uint32_t>(ConstString("lengthAndRef")) >> 20;
+ lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
+
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetQuote('"');
+ options.SetSourceSize(explicit_length);
+ options.SetNeedsZeroTermination(!has_explicit_length);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetBinaryZeroIsTerminator(!has_explicit_length);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ } else if (is_inline) {
+ uint64_t location = valobj_addr + 2 * ptr_size;
+ if (!has_explicit_length) {
+ // in this kind of string, the byte before the string content is a length
+ // byte so let's try and use it to handle the embedded NUL case
+ Status error;
+ explicit_length =
+ process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
+ has_explicit_length = !(error.Fail() || explicit_length == 0);
+ location++;
+ }
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetSourceSize(explicit_length);
+ options.SetNeedsZeroTermination(!has_explicit_length);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetBinaryZeroIsTerminator(!has_explicit_length);
+ options.SetLanguage(summary_options.GetLanguage());
+ if (has_explicit_length)
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(options);
+ else
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::ASCII>(options);
+ } else {
+ uint64_t location = valobj_addr + 2 * ptr_size;
+ location = process_sp->ReadPointerFromMemory(location, error);
+ if (error.Fail())
+ return false;
+ if (has_explicit_length && !has_null)
+ explicit_length++; // account for the fact that there is no NULL and we
+ // need to have one added
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetSourceSize(explicit_length);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::ASCII>(options);
+ }
+}
+
+bool lldb_private::formatters::NSAttributedStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ TargetSP target_sp(valobj.GetTargetSP());
+ if (!target_sp)
+ return false;
+ uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
+ uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
+ if (!pointer_value)
+ return false;
+ pointer_value += addr_size;
+ CompilerType type(valobj.GetCompilerType());
+ ExecutionContext exe_ctx(target_sp, false);
+ ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress(
+ "string_ptr", pointer_value, exe_ctx, type));
+ if (!child_ptr_sp)
+ return false;
+ DataExtractor data;
+ Status error;
+ child_ptr_sp->GetData(data, error);
+ if (error.Fail())
+ return false;
+ ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(
+ "string_data", data, exe_ctx, type));
+ child_sp->GetValueAsUnsigned(0);
+ if (child_sp)
+ return NSStringSummaryProvider(*child_sp, stream, options);
+ return false;
+}
+
+bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ return NSAttributedStringSummaryProvider(valobj, stream, options);
+}
+
+bool lldb_private::formatters::NSTaggedString_SummaryProvider(
+ ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor,
+ Stream &stream, const TypeSummaryOptions &summary_options) {
+ static ConstString g_TypeHint("NSString");
+
+ if (!descriptor)
+ return false;
+ uint64_t len_bits = 0, data_bits = 0;
+ if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))
+ return false;
+
+ static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN
+ static const int g_SixbitMaxLen = 9;
+ static const int g_fiveBitMaxLen = 11;
+
+ static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"
+ "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
+
+ if (len_bits > g_fiveBitMaxLen)
+ return false;
+
+ std::string prefix, suffix;
+ if (Language *language =
+ Language::FindPlugin(summary_options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ // this is a fairly ugly trick - pretend that the numeric value is actually a
+ // char* this works under a few assumptions: little endian architecture
+ // sizeof(uint64_t) > g_MaxNonBitmaskedLen
+ if (len_bits <= g_MaxNonBitmaskedLen) {
+ stream.Printf("%s", prefix.c_str());
+ stream.Printf("\"%s\"", (const char *)&data_bits);
+ stream.Printf("%s", suffix.c_str());
+ return true;
+ }
+
+ // if the data is bitmasked, we need to actually process the bytes
+ uint8_t bitmask = 0;
+ uint8_t shift_offset = 0;
+
+ if (len_bits <= g_SixbitMaxLen) {
+ bitmask = 0x03f;
+ shift_offset = 6;
+ } else {
+ bitmask = 0x01f;
+ shift_offset = 5;
+ }
+
+ std::vector<uint8_t> bytes;
+ bytes.resize(len_bits);
+ for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) {
+ uint8_t packed = data_bits & bitmask;
+ bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
+ }
+
+ stream.Printf("%s", prefix.c_str());
+ stream.Printf("\"%s\"", &bytes[0]);
+ stream.Printf("%s", suffix.c_str());
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h
new file mode 100644
index 000000000000..699d8eb36f88
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h
@@ -0,0 +1,42 @@
+//===-- NSString.h ---------------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NSString_h_
+#define liblldb_NSString_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+namespace formatters {
+bool NSStringSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSTaggedString_SummaryProvider(
+ ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor,
+ Stream &stream, const TypeSummaryOptions &summary_options);
+
+bool NSAttributedStringSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSMutableAttributedStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
+
+class NSString_Additionals {
+public:
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+ GetAdditionalSummaries();
+};
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_CF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp
new file mode 100644
index 000000000000..f9ab18688de7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp
@@ -0,0 +1,1094 @@
+//===-- ObjCLanguage.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <mutex>
+
+#include "ObjCLanguage.h"
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Support/Threading.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include "CF.h"
+#include "Cocoa.h"
+#include "CoreMedia.h"
+#include "NSDictionary.h"
+#include "NSSet.h"
+#include "NSString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+void ObjCLanguage::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(), "Objective-C Language",
+ CreateInstance);
+}
+
+void ObjCLanguage::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ObjCLanguage::GetPluginNameStatic() {
+ static ConstString g_name("objc");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ObjCLanguage::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ObjCLanguage::GetPluginVersion() { return 1; }
+
+// Static Functions
+
+Language *ObjCLanguage::CreateInstance(lldb::LanguageType language) {
+ switch (language) {
+ case lldb::eLanguageTypeObjC:
+ return new ObjCLanguage();
+ default:
+ return nullptr;
+ }
+}
+
+void ObjCLanguage::MethodName::Clear() {
+ m_full.Clear();
+ m_class.Clear();
+ m_category.Clear();
+ m_selector.Clear();
+ m_type = eTypeUnspecified;
+ m_category_is_valid = false;
+}
+
+bool ObjCLanguage::MethodName::SetName(llvm::StringRef name, bool strict) {
+ Clear();
+ if (name.empty())
+ return IsValid(strict);
+
+ // If "strict" is true. then the method must be specified with a '+' or '-'
+ // at the beginning. If "strict" is false, then the '+' or '-' can be omitted
+ bool valid_prefix = false;
+
+ if (name.size() > 1 && (name[0] == '+' || name[0] == '-')) {
+ valid_prefix = name[1] == '[';
+ if (name[0] == '+')
+ m_type = eTypeClassMethod;
+ else
+ m_type = eTypeInstanceMethod;
+ } else if (!strict) {
+ // "strict" is false, the name just needs to start with '['
+ valid_prefix = name[0] == '[';
+ }
+
+ if (valid_prefix) {
+ int name_len = name.size();
+ // Objective-C methods must have at least:
+ // "-[" or "+[" prefix
+ // One character for a class name
+ // One character for the space between the class name
+ // One character for the method name
+ // "]" suffix
+ if (name_len >= (5 + (strict ? 1 : 0)) && name.back() == ']') {
+ m_full.SetString(name);
+ }
+ }
+ return IsValid(strict);
+}
+
+bool ObjCLanguage::MethodName::SetName(const char *name, bool strict) {
+ return SetName(llvm::StringRef(name), strict);
+}
+
+ConstString ObjCLanguage::MethodName::GetClassName() {
+ if (!m_class) {
+ if (IsValid(false)) {
+ const char *full = m_full.GetCString();
+ const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
+ const char *paren_pos = strchr(class_start, '(');
+ if (paren_pos) {
+ m_class.SetCStringWithLength(class_start, paren_pos - class_start);
+ } else {
+ // No '(' was found in the full name, we can definitively say that our
+ // category was valid (and empty).
+ m_category_is_valid = true;
+ const char *space_pos = strchr(full, ' ');
+ if (space_pos) {
+ m_class.SetCStringWithLength(class_start, space_pos - class_start);
+ if (!m_class_category) {
+ // No category in name, so we can also fill in the m_class_category
+ m_class_category = m_class;
+ }
+ }
+ }
+ }
+ }
+ return m_class;
+}
+
+ConstString ObjCLanguage::MethodName::GetClassNameWithCategory() {
+ if (!m_class_category) {
+ if (IsValid(false)) {
+ const char *full = m_full.GetCString();
+ const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
+ const char *space_pos = strchr(full, ' ');
+ if (space_pos) {
+ m_class_category.SetCStringWithLength(class_start,
+ space_pos - class_start);
+ // If m_class hasn't been filled in and the class with category doesn't
+ // contain a '(', then we can also fill in the m_class
+ if (!m_class && strchr(m_class_category.GetCString(), '(') == nullptr) {
+ m_class = m_class_category;
+ // No '(' was found in the full name, we can definitively say that
+ // our category was valid (and empty).
+ m_category_is_valid = true;
+ }
+ }
+ }
+ }
+ return m_class_category;
+}
+
+ConstString ObjCLanguage::MethodName::GetSelector() {
+ if (!m_selector) {
+ if (IsValid(false)) {
+ const char *full = m_full.GetCString();
+ const char *space_pos = strchr(full, ' ');
+ if (space_pos) {
+ ++space_pos; // skip the space
+ m_selector.SetCStringWithLength(space_pos, m_full.GetLength() -
+ (space_pos - full) - 1);
+ }
+ }
+ }
+ return m_selector;
+}
+
+ConstString ObjCLanguage::MethodName::GetCategory() {
+ if (!m_category_is_valid && !m_category) {
+ if (IsValid(false)) {
+ m_category_is_valid = true;
+ const char *full = m_full.GetCString();
+ const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
+ const char *open_paren_pos = strchr(class_start, '(');
+ if (open_paren_pos) {
+ ++open_paren_pos; // Skip the open paren
+ const char *close_paren_pos = strchr(open_paren_pos, ')');
+ if (close_paren_pos)
+ m_category.SetCStringWithLength(open_paren_pos,
+ close_paren_pos - open_paren_pos);
+ }
+ }
+ }
+ return m_category;
+}
+
+ConstString ObjCLanguage::MethodName::GetFullNameWithoutCategory(
+ bool empty_if_no_category) {
+ if (IsValid(false)) {
+ if (HasCategory()) {
+ StreamString strm;
+ if (m_type == eTypeClassMethod)
+ strm.PutChar('+');
+ else if (m_type == eTypeInstanceMethod)
+ strm.PutChar('-');
+ strm.Printf("[%s %s]", GetClassName().GetCString(),
+ GetSelector().GetCString());
+ return ConstString(strm.GetString());
+ }
+
+ if (!empty_if_no_category) {
+ // Just return the full name since it doesn't have a category
+ return GetFullName();
+ }
+ }
+ return ConstString();
+}
+
+std::vector<ConstString>
+ObjCLanguage::GetMethodNameVariants(ConstString method_name) const {
+ std::vector<ConstString> variant_names;
+ ObjCLanguage::MethodName objc_method(method_name.GetCString(), false);
+ if (!objc_method.IsValid(false)) {
+ return variant_names;
+ }
+
+ const bool is_class_method =
+ objc_method.GetType() == MethodName::eTypeClassMethod;
+ const bool is_instance_method =
+ objc_method.GetType() == MethodName::eTypeInstanceMethod;
+ ConstString name_sans_category =
+ objc_method.GetFullNameWithoutCategory(/*empty_if_no_category*/ true);
+
+ if (is_class_method || is_instance_method) {
+ if (name_sans_category)
+ variant_names.emplace_back(name_sans_category);
+ } else {
+ StreamString strm;
+
+ strm.Printf("+%s", objc_method.GetFullName().GetCString());
+ variant_names.emplace_back(strm.GetString());
+ strm.Clear();
+
+ strm.Printf("-%s", objc_method.GetFullName().GetCString());
+ variant_names.emplace_back(strm.GetString());
+ strm.Clear();
+
+ if (name_sans_category) {
+ strm.Printf("+%s", name_sans_category.GetCString());
+ variant_names.emplace_back(strm.GetString());
+ strm.Clear();
+
+ strm.Printf("-%s", name_sans_category.GetCString());
+ variant_names.emplace_back(strm.GetString());
+ }
+ }
+
+ return variant_names;
+}
+
+static void LoadObjCFormatters(TypeCategoryImplSP objc_category_sp) {
+ if (!objc_category_sp)
+ return;
+
+ TypeSummaryImpl::Flags objc_flags;
+ objc_flags.SetCascades(false)
+ .SetSkipPointers(true)
+ .SetSkipReferences(true)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(true)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ lldb::TypeSummaryImplSP ObjC_BOOL_summary(new CXXFunctionSummaryFormat(
+ objc_flags, lldb_private::formatters::ObjCBOOLSummaryProvider, ""));
+ objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL"),
+ ObjC_BOOL_summary);
+ objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL &"),
+ ObjC_BOOL_summary);
+ objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL *"),
+ ObjC_BOOL_summary);
+
+ // we need to skip pointers here since we are special casing a SEL* when
+ // retrieving its value
+ objc_flags.SetSkipPointers(true);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::ObjCSELSummaryProvider<false>,
+ "SEL summary provider", ConstString("SEL"), objc_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider<false>,
+ "SEL summary provider", ConstString("struct objc_selector"), objc_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider<false>,
+ "SEL summary provider", ConstString("objc_selector"), objc_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider<true>,
+ "SEL summary provider", ConstString("objc_selector *"), objc_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::ObjCSELSummaryProvider<true>,
+ "SEL summary provider", ConstString("SEL *"), objc_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::ObjCClassSummaryProvider,
+ "Class summary provider", ConstString("Class"), objc_flags);
+
+ SyntheticChildren::Flags class_synth_flags;
+ class_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
+ false);
+
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::ObjCClassSyntheticFrontEndCreator,
+ "Class synthetic children", ConstString("Class"),
+ class_synth_flags);
+
+ objc_flags.SetSkipPointers(false);
+ objc_flags.SetCascades(true);
+ objc_flags.SetSkipReferences(false);
+
+ AddStringSummary(objc_category_sp, "${var.__FuncPtr%A}",
+ ConstString("__block_literal_generic"), objc_flags);
+
+ AddStringSummary(objc_category_sp, "${var.years} years, ${var.months} "
+ "months, ${var.days} days, ${var.hours} "
+ "hours, ${var.minutes} minutes "
+ "${var.seconds} seconds",
+ ConstString("CFGregorianUnits"), objc_flags);
+ AddStringSummary(objc_category_sp,
+ "location=${var.location} length=${var.length}",
+ ConstString("CFRange"), objc_flags);
+
+ AddStringSummary(objc_category_sp,
+ "location=${var.location}, length=${var.length}",
+ ConstString("NSRange"), objc_flags);
+ AddStringSummary(objc_category_sp, "(${var.origin}, ${var.size}), ...",
+ ConstString("NSRectArray"), objc_flags);
+
+ AddOneLineSummary(objc_category_sp, ConstString("NSPoint"), objc_flags);
+ AddOneLineSummary(objc_category_sp, ConstString("NSSize"), objc_flags);
+ AddOneLineSummary(objc_category_sp, ConstString("NSRect"), objc_flags);
+
+ AddOneLineSummary(objc_category_sp, ConstString("CGSize"), objc_flags);
+ AddOneLineSummary(objc_category_sp, ConstString("CGPoint"), objc_flags);
+ AddOneLineSummary(objc_category_sp, ConstString("CGRect"), objc_flags);
+
+ AddStringSummary(objc_category_sp,
+ "red=${var.red} green=${var.green} blue=${var.blue}",
+ ConstString("RGBColor"), objc_flags);
+ AddStringSummary(
+ objc_category_sp,
+ "(t=${var.top}, l=${var.left}, b=${var.bottom}, r=${var.right})",
+ ConstString("Rect"), objc_flags);
+ AddStringSummary(objc_category_sp, "{(v=${var.v}, h=${var.h})}",
+ ConstString("Point"), objc_flags);
+ AddStringSummary(objc_category_sp,
+ "${var.month}/${var.day}/${var.year} ${var.hour} "
+ ":${var.minute} :${var.second} dayOfWeek:${var.dayOfWeek}",
+ ConstString("DateTimeRect *"), objc_flags);
+ AddStringSummary(objc_category_sp, "${var.ld.month}/${var.ld.day}/"
+ "${var.ld.year} ${var.ld.hour} "
+ ":${var.ld.minute} :${var.ld.second} "
+ "dayOfWeek:${var.ld.dayOfWeek}",
+ ConstString("LongDateRect"), objc_flags);
+ AddStringSummary(objc_category_sp, "(x=${var.x}, y=${var.y})",
+ ConstString("HIPoint"), objc_flags);
+ AddStringSummary(objc_category_sp, "origin=${var.origin} size=${var.size}",
+ ConstString("HIRect"), objc_flags);
+
+ TypeSummaryImpl::Flags appkit_flags;
+ appkit_flags.SetCascades(true)
+ .SetSkipPointers(false)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(false)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ appkit_flags.SetDontShowChildren(false);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("NSArray"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("NSMutableArray"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("__NSArrayI"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("__NSArray0"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider",
+ ConstString("__NSSingleObjectArrayI"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("__NSArrayM"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("__NSCFArray"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("_NSCallStackArray"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("CFArrayRef"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("CFMutableArrayRef"),
+ appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider", ConstString("NSDictionary"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider",
+ ConstString("NSMutableDictionary"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider",
+ ConstString("__NSCFDictionary"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider", ConstString("__NSDictionaryI"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider",
+ ConstString("__NSSingleEntryDictionaryI"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider", ConstString("__NSDictionaryM"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<true>,
+ "NSDictionary summary provider", ConstString("CFDictionaryRef"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<true>,
+ "NSDictionary summary provider",
+ ConstString("CFMutableDictionaryRef"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSSetSummaryProvider<false>,
+ "NSSet summary", ConstString("NSSet"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "NSMutableSet summary", ConstString("NSMutableSet"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSSetSummaryProvider<true>,
+ "CFSetRef summary", ConstString("CFSetRef"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<true>,
+ "CFMutableSetRef summary", ConstString("CFMutableSetRef"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSSetSummaryProvider<false>,
+ "__NSCFSet summary", ConstString("__NSCFSet"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSSetSummaryProvider<false>,
+ "__NSSetI summary", ConstString("__NSSetI"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSSetSummaryProvider<false>,
+ "__NSSetM summary", ConstString("__NSSetM"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "NSCountedSet summary", ConstString("NSCountedSet"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "NSMutableSet summary", ConstString("NSMutableSet"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "NSOrderedSet summary", ConstString("NSOrderedSet"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "__NSOrderedSetI summary", ConstString("__NSOrderedSetI"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "__NSOrderedSetM summary", ConstString("__NSOrderedSetM"), appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSError_SummaryProvider,
+ "NSError summary provider", ConstString("NSError"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSException_SummaryProvider,
+ "NSException summary provider", ConstString("NSException"), appkit_flags);
+
+ // AddSummary(appkit_category_sp, "${var.key%@} -> ${var.value%@}",
+ // ConstString("$_lldb_typegen_nspair"), appkit_flags);
+
+ appkit_flags.SetDontShowChildren(true);
+
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("__NSArrayM"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("__NSArrayI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("__NSArray0"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children",
+ ConstString("__NSSingleObjectArrayI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("NSArray"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("NSMutableArray"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("__NSCFArray"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("_NSCallStackArray"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children",
+ ConstString("CFMutableArrayRef"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("CFArrayRef"),
+ ScriptedSyntheticChildren::Flags());
+
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("__NSDictionaryM"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("__NSDictionaryI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children",
+ ConstString("__NSSingleEntryDictionaryI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("__NSCFDictionary"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("NSDictionary"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("NSMutableDictionary"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("CFDictionaryRef"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("CFMutableDictionaryRef"),
+ ScriptedSyntheticChildren::Flags());
+
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSErrorSyntheticFrontEndCreator,
+ "NSError synthetic children", ConstString("NSError"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSExceptionSyntheticFrontEndCreator,
+ "NSException synthetic children", ConstString("NSException"),
+ ScriptedSyntheticChildren::Flags());
+
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "NSSet synthetic children", ConstString("NSSet"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "__NSSetI synthetic children", ConstString("__NSSetI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "__NSSetM synthetic children", ConstString("__NSSetM"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "NSMutableSet synthetic children", ConstString("NSMutableSet"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "NSOrderedSet synthetic children", ConstString("NSOrderedSet"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "__NSOrderedSetI synthetic children", ConstString("__NSOrderedSetI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "__NSOrderedSetM synthetic children", ConstString("__NSOrderedSetM"),
+ ScriptedSyntheticChildren::Flags());
+
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSIndexPathSyntheticFrontEndCreator,
+ "NSIndexPath synthetic children", ConstString("NSIndexPath"),
+ ScriptedSyntheticChildren::Flags());
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::CFBagSummaryProvider,
+ "CFBag summary provider", ConstString("CFBagRef"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBagSummaryProvider,
+ "CFBag summary provider", ConstString("__CFBag"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBagSummaryProvider,
+ "CFBag summary provider", ConstString("const struct __CFBag"),
+ appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::CFBagSummaryProvider,
+ "CFBag summary provider", ConstString("CFMutableBagRef"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBinaryHeapSummaryProvider,
+ "CFBinaryHeap summary provider", ConstString("CFBinaryHeapRef"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBinaryHeapSummaryProvider,
+ "CFBinaryHeap summary provider", ConstString("__CFBinaryHeap"),
+ appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("NSString"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("CFStringRef"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("__CFString"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("CFMutableStringRef"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("NSMutableString"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider",
+ ConstString("__NSCFConstantString"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("__NSCFString"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("NSCFConstantString"),
+ appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("NSCFString"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("NSPathStore2"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider",
+ ConstString("NSTaggedPointerString"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSAttributedStringSummaryProvider,
+ "NSAttributedString summary provider",
+ ConstString("NSAttributedString"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp,
+ lldb_private::formatters::NSMutableAttributedStringSummaryProvider,
+ "NSMutableAttributedString summary provider",
+ ConstString("NSMutableAttributedString"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp,
+ lldb_private::formatters::NSMutableAttributedStringSummaryProvider,
+ "NSMutableAttributedString summary provider",
+ ConstString("NSConcreteMutableAttributedString"), appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSBundleSummaryProvider,
+ "NSBundle summary provider", ConstString("NSBundle"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("NSData"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("_NSInlineData"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("NSConcreteData"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("NSConcreteMutableData"),
+ appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("NSMutableData"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("__NSCFData"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<true>,
+ "NSData summary provider", ConstString("CFDataRef"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<true>,
+ "NSData summary provider", ConstString("CFMutableDataRef"), appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSMachPortSummaryProvider,
+ "NSMachPort summary provider", ConstString("NSMachPort"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSNotificationSummaryProvider,
+ "NSNotification summary provider",
+ ConstString("NSNotification"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSNotificationSummaryProvider,
+ "NSNotification summary provider",
+ ConstString("NSConcreteNotification"), appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "NSNumber summary provider", ConstString("NSNumber"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "CFNumberRef summary provider", ConstString("CFNumberRef"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "NSNumber summary provider", ConstString("__NSCFBoolean"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "NSNumber summary provider", ConstString("__NSCFNumber"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "NSNumber summary provider", ConstString("NSCFBoolean"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "NSNumber summary provider", ConstString("NSCFNumber"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSNumberSummaryProvider,
+ "NSDecimalNumber summary provider",
+ ConstString("NSDecimalNumber"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSURLSummaryProvider,
+ "NSURL summary provider", ConstString("NSURL"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSURLSummaryProvider,
+ "NSURL summary provider", ConstString("CFURLRef"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDateSummaryProvider,
+ "NSDate summary provider", ConstString("NSDate"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDateSummaryProvider,
+ "NSDate summary provider", ConstString("__NSDate"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDateSummaryProvider,
+ "NSDate summary provider", ConstString("__NSTaggedDate"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDateSummaryProvider,
+ "NSDate summary provider", ConstString("NSCalendarDate"), appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider,
+ "NSTimeZone summary provider", ConstString("NSTimeZone"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSTimeZoneSummaryProvider,
+ "NSTimeZone summary provider", ConstString("CFTimeZoneRef"),
+ appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider,
+ "NSTimeZone summary provider", ConstString("__NSTimeZone"), appkit_flags);
+
+ // CFAbsoluteTime is actually a double rather than a pointer to an object we
+ // do not care about the numeric value, since it is probably meaningless to
+ // users
+ appkit_flags.SetDontShowValue(true);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFAbsoluteTimeSummaryProvider,
+ "CFAbsoluteTime summary provider",
+ ConstString("CFAbsoluteTime"), appkit_flags);
+ appkit_flags.SetDontShowValue(false);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSIndexSetSummaryProvider,
+ "NSIndexSet summary provider", ConstString("NSIndexSet"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSIndexSetSummaryProvider,
+ "NSIndexSet summary provider", ConstString("NSMutableIndexSet"),
+ appkit_flags);
+
+ AddStringSummary(objc_category_sp,
+ "@\"${var.month%d}/${var.day%d}/${var.year%d} "
+ "${var.hour%d}:${var.minute%d}:${var.second}\"",
+ ConstString("CFGregorianDate"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBitVectorSummaryProvider,
+ "CFBitVector summary provider", ConstString("CFBitVectorRef"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBitVectorSummaryProvider,
+ "CFBitVector summary provider",
+ ConstString("CFMutableBitVectorRef"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBitVectorSummaryProvider,
+ "CFBitVector summary provider", ConstString("__CFBitVector"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBitVectorSummaryProvider,
+ "CFBitVector summary provider",
+ ConstString("__CFMutableBitVector"), appkit_flags);
+}
+
+static void LoadCoreMediaFormatters(TypeCategoryImplSP objc_category_sp) {
+ if (!objc_category_sp)
+ return;
+
+ TypeSummaryImpl::Flags cm_flags;
+ cm_flags.SetCascades(true)
+ .SetDontShowChildren(false)
+ .SetDontShowValue(false)
+ .SetHideItemNames(false)
+ .SetShowMembersOneLiner(false)
+ .SetSkipPointers(false)
+ .SetSkipReferences(false);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CMTimeSummaryProvider,
+ "CMTime summary provider", ConstString("CMTime"), cm_flags);
+}
+
+lldb::TypeCategoryImplSP ObjCLanguage::GetFormatters() {
+ static llvm::once_flag g_initialize;
+ static TypeCategoryImplSP g_category;
+
+ llvm::call_once(g_initialize, [this]() -> void {
+ DataVisualization::Categories::GetCategory(GetPluginName(), g_category);
+ if (g_category) {
+ LoadCoreMediaFormatters(g_category);
+ LoadObjCFormatters(g_category);
+ }
+ });
+ return g_category;
+}
+
+std::vector<ConstString>
+ObjCLanguage::GetPossibleFormattersMatches(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ std::vector<ConstString> result;
+
+ if (use_dynamic == lldb::eNoDynamicValues)
+ return result;
+
+ CompilerType compiler_type(valobj.GetCompilerType());
+
+ const bool check_cpp = false;
+ const bool check_objc = true;
+ bool canBeObjCDynamic =
+ compiler_type.IsPossibleDynamicType(nullptr, check_cpp, check_objc);
+
+ if (canBeObjCDynamic) {
+ do {
+ lldb::ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ break;
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+ if (runtime == nullptr)
+ break;
+ ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp(
+ runtime->GetClassDescriptor(valobj));
+ if (!objc_class_sp)
+ break;
+ if (ConstString name = objc_class_sp->GetClassName())
+ result.push_back(name);
+ } while (false);
+ }
+
+ return result;
+}
+
+std::unique_ptr<Language::TypeScavenger> ObjCLanguage::GetTypeScavenger() {
+ class ObjCScavengerResult : public Language::TypeScavenger::Result {
+ public:
+ ObjCScavengerResult(CompilerType type)
+ : Language::TypeScavenger::Result(), m_compiler_type(type) {}
+
+ bool IsValid() override { return m_compiler_type.IsValid(); }
+
+ bool DumpToStream(Stream &stream, bool print_help_if_available) override {
+ if (IsValid()) {
+ m_compiler_type.DumpTypeDescription(&stream);
+ stream.EOL();
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ CompilerType m_compiler_type;
+ };
+
+ class ObjCRuntimeScavenger : public Language::TypeScavenger {
+ protected:
+ bool Find_Impl(ExecutionContextScope *exe_scope, const char *key,
+ ResultSet &results) override {
+ bool result = false;
+
+ if (auto *process = exe_scope->CalculateProcess().get()) {
+ if (auto *objc_runtime = ObjCLanguageRuntime::Get(*process)) {
+ if (auto *decl_vendor = objc_runtime->GetDeclVendor()) {
+ ConstString name(key);
+ for (const CompilerType &type :
+ decl_vendor->FindTypes(name, /*max_matches*/ UINT32_MAX)) {
+ result = true;
+ std::unique_ptr<Language::TypeScavenger::Result> result(
+ new ObjCScavengerResult(type));
+ results.insert(std::move(result));
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ friend class lldb_private::ObjCLanguage;
+ };
+
+ class ObjCModulesScavenger : public Language::TypeScavenger {
+ protected:
+ bool Find_Impl(ExecutionContextScope *exe_scope, const char *key,
+ ResultSet &results) override {
+ bool result = false;
+
+ if (auto *target = exe_scope->CalculateTarget().get()) {
+ if (auto *clang_modules_decl_vendor =
+ target->GetClangModulesDeclVendor()) {
+ ConstString key_cs(key);
+ auto types = clang_modules_decl_vendor->FindTypes(
+ key_cs, /*max_matches*/ UINT32_MAX);
+ if (!types.empty()) {
+ result = true;
+ std::unique_ptr<Language::TypeScavenger::Result> result(
+ new ObjCScavengerResult(types.front()));
+ results.insert(std::move(result));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ friend class lldb_private::ObjCLanguage;
+ };
+
+ class ObjCDebugInfoScavenger : public Language::ImageListTypeScavenger {
+ public:
+ CompilerType AdjustForInclusion(CompilerType &candidate) override {
+ LanguageType lang_type(candidate.GetMinimumLanguage());
+ if (!Language::LanguageIsObjC(lang_type))
+ return CompilerType();
+ if (candidate.IsTypedefType())
+ return candidate.GetTypedefedType();
+ return candidate;
+ }
+ };
+
+ return std::unique_ptr<TypeScavenger>(
+ new Language::EitherTypeScavenger<ObjCModulesScavenger,
+ ObjCRuntimeScavenger,
+ ObjCDebugInfoScavenger>());
+}
+
+bool ObjCLanguage::GetFormatterPrefixSuffix(ValueObject &valobj,
+ ConstString type_hint,
+ std::string &prefix,
+ std::string &suffix) {
+ static ConstString g_CFBag("CFBag");
+ static ConstString g_CFBinaryHeap("CFBinaryHeap");
+
+ static ConstString g_NSNumberChar("NSNumber:char");
+ static ConstString g_NSNumberShort("NSNumber:short");
+ static ConstString g_NSNumberInt("NSNumber:int");
+ static ConstString g_NSNumberLong("NSNumber:long");
+ static ConstString g_NSNumberInt128("NSNumber:int128_t");
+ static ConstString g_NSNumberFloat("NSNumber:float");
+ static ConstString g_NSNumberDouble("NSNumber:double");
+
+ static ConstString g_NSData("NSData");
+ static ConstString g_NSArray("NSArray");
+ static ConstString g_NSString("NSString");
+ static ConstString g_NSStringStar("NSString*");
+
+ if (type_hint.IsEmpty())
+ return false;
+
+ prefix.clear();
+ suffix.clear();
+
+ if (type_hint == g_CFBag || type_hint == g_CFBinaryHeap) {
+ prefix = "@";
+ return true;
+ }
+
+ if (type_hint == g_NSNumberChar) {
+ prefix = "(char)";
+ return true;
+ }
+ if (type_hint == g_NSNumberShort) {
+ prefix = "(short)";
+ return true;
+ }
+ if (type_hint == g_NSNumberInt) {
+ prefix = "(int)";
+ return true;
+ }
+ if (type_hint == g_NSNumberLong) {
+ prefix = "(long)";
+ return true;
+ }
+ if (type_hint == g_NSNumberInt128) {
+ prefix = "(int128_t)";
+ return true;
+ }
+ if (type_hint == g_NSNumberFloat) {
+ prefix = "(float)";
+ return true;
+ }
+ if (type_hint == g_NSNumberDouble) {
+ prefix = "(double)";
+ return true;
+ }
+
+ if (type_hint == g_NSData || type_hint == g_NSArray) {
+ prefix = "@\"";
+ suffix = "\"";
+ return true;
+ }
+
+ if (type_hint == g_NSString || type_hint == g_NSStringStar) {
+ prefix = "@";
+ return true;
+ }
+
+ return false;
+}
+
+bool ObjCLanguage::IsNilReference(ValueObject &valobj) {
+ const uint32_t mask = eTypeIsObjC | eTypeIsPointer;
+ bool isObjCpointer =
+ (((valobj.GetCompilerType().GetTypeInfo(nullptr)) & mask) == mask);
+ if (!isObjCpointer)
+ return false;
+ bool canReadValue = true;
+ bool isZero = valobj.GetValueAsUnsigned(0, &canReadValue) == 0;
+ return canReadValue && isZero;
+}
+
+bool ObjCLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ const auto suffixes = {".h", ".m", ".M"};
+ for (auto suffix : suffixes) {
+ if (file_path.endswith_lower(suffix))
+ return true;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
new file mode 100644
index 000000000000..3e2cc0972993
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
@@ -0,0 +1,163 @@
+//===-- ObjCLanguage.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ObjCLanguage_h_
+#define liblldb_ObjCLanguage_h_
+
+#include <cstring>
+#include <vector>
+
+#include "Plugins/Language/ClangCommon/ClangHighlighter.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class ObjCLanguage : public Language {
+ ClangHighlighter m_highlighter;
+
+public:
+ class MethodName {
+ public:
+ enum Type { eTypeUnspecified, eTypeClassMethod, eTypeInstanceMethod };
+
+ MethodName()
+ : m_full(), m_class(), m_category(), m_selector(),
+ m_type(eTypeUnspecified), m_category_is_valid(false) {}
+
+ MethodName(const char *name, bool strict)
+ : m_full(), m_class(), m_category(), m_selector(),
+ m_type(eTypeUnspecified), m_category_is_valid(false) {
+ SetName(name, strict);
+ }
+ MethodName(llvm::StringRef name, bool strict)
+ : m_full(), m_class(), m_category(), m_selector(),
+ m_type(eTypeUnspecified), m_category_is_valid(false) {
+ SetName(name, strict);
+ }
+
+ void Clear();
+
+ bool IsValid(bool strict) const {
+ // If "strict" is true, the name must have everything specified including
+ // the leading "+" or "-" on the method name
+ if (strict && m_type == eTypeUnspecified)
+ return false;
+ // Other than that, m_full will only be filled in if the objective C
+ // name is valid.
+ return (bool)m_full;
+ }
+
+ bool HasCategory() { return !GetCategory().IsEmpty(); }
+
+ Type GetType() const { return m_type; }
+
+ ConstString GetFullName() const { return m_full; }
+
+ ConstString GetFullNameWithoutCategory(bool empty_if_no_category);
+
+ bool SetName(const char *name, bool strict);
+ bool SetName(llvm::StringRef name, bool strict);
+
+ ConstString GetClassName();
+
+ ConstString GetClassNameWithCategory();
+
+ ConstString GetCategory();
+
+ ConstString GetSelector();
+
+ protected:
+ ConstString
+ m_full; // Full name: "+[NSString(my_additions) myStringWithCString:]"
+ ConstString m_class; // Class name: "NSString"
+ ConstString
+ m_class_category; // Class with category: "NSString(my_additions)"
+ ConstString m_category; // Category: "my_additions"
+ ConstString m_selector; // Selector: "myStringWithCString:"
+ Type m_type;
+ bool m_category_is_valid;
+ };
+
+ ObjCLanguage() = default;
+
+ ~ObjCLanguage() override = default;
+
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeObjC;
+ }
+
+ // Get all possible names for a method. Examples:
+ // If method_name is "+[NSString(my_additions) myStringWithCString:]"
+ // variant_names[0] => "+[NSString myStringWithCString:]"
+ // If name is specified without the leading '+' or '-' like
+ // "[NSString(my_additions) myStringWithCString:]"
+ // variant_names[0] => "+[NSString(my_additions) myStringWithCString:]"
+ // variant_names[1] => "-[NSString(my_additions) myStringWithCString:]"
+ // variant_names[2] => "+[NSString myStringWithCString:]"
+ // variant_names[3] => "-[NSString myStringWithCString:]"
+ std::vector<ConstString>
+ GetMethodNameVariants(ConstString method_name) const override;
+
+ lldb::TypeCategoryImplSP GetFormatters() override;
+
+ std::vector<ConstString>
+ GetPossibleFormattersMatches(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) override;
+
+ std::unique_ptr<TypeScavenger> GetTypeScavenger() override;
+
+ bool GetFormatterPrefixSuffix(ValueObject &valobj, ConstString type_hint,
+ std::string &prefix,
+ std::string &suffix) override;
+
+ bool IsNilReference(ValueObject &valobj) override;
+
+ bool IsSourceFile(llvm::StringRef file_path) const override;
+
+ const Highlighter *GetHighlighter() const override { return &m_highlighter; }
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::Language *CreateInstance(lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static bool IsPossibleObjCMethodName(const char *name) {
+ if (!name)
+ return false;
+ bool starts_right = (name[0] == '+' || name[0] == '-') && name[1] == '[';
+ bool ends_right = (name[strlen(name) - 1] == ']');
+ return (starts_right && ends_right);
+ }
+
+ static bool IsPossibleObjCSelector(const char *name) {
+ if (!name)
+ return false;
+
+ if (strchr(name, ':') == nullptr)
+ return true;
+ else if (name[strlen(name) - 1] == ':')
+ return true;
+ else
+ return false;
+ }
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ObjCLanguage_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp
new file mode 100644
index 000000000000..81b3c5807c41
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp
@@ -0,0 +1,56 @@
+//===-- ObjCPlusPlusLanguage.cpp --------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ObjCPlusPlusLanguage.h"
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Utility/ConstString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool ObjCPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ const auto suffixes = {".h", ".mm"};
+ for (auto suffix : suffixes) {
+ if (file_path.endswith_lower(suffix))
+ return true;
+ }
+ return false;
+}
+
+void ObjCPlusPlusLanguage::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(), "Objective-C++ Language",
+ CreateInstance);
+}
+
+void ObjCPlusPlusLanguage::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ObjCPlusPlusLanguage::GetPluginNameStatic() {
+ static ConstString g_name("objcplusplus");
+ return g_name;
+}
+
+// PluginInterface protocol
+lldb_private::ConstString ObjCPlusPlusLanguage::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ObjCPlusPlusLanguage::GetPluginVersion() { return 1; }
+
+// Static Functions
+Language *ObjCPlusPlusLanguage::CreateInstance(lldb::LanguageType language) {
+ switch (language) {
+ case lldb::eLanguageTypeObjC_plus_plus:
+ return new ObjCPlusPlusLanguage();
+ default:
+ return nullptr;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h
new file mode 100644
index 000000000000..6224a3f47b3b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h
@@ -0,0 +1,51 @@
+//===-- ObjCPlusPlusLanguage.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ObjCPlusPlusLanguage_h_
+#define liblldb_ObjCPlusPlusLanguage_h_
+
+#include "Plugins/Language/ClangCommon/ClangHighlighter.h"
+#include "lldb/Target/Language.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class ObjCPlusPlusLanguage : public Language {
+ ClangHighlighter m_highlighter;
+
+public:
+ ObjCPlusPlusLanguage() = default;
+
+ ~ObjCPlusPlusLanguage() override = default;
+
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeObjC_plus_plus;
+ }
+
+ bool IsSourceFile(llvm::StringRef file_path) const override;
+
+ const Highlighter *GetHighlighter() const override { return &m_highlighter; }
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::Language *CreateInstance(lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CPlusPlusLanguage_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
new file mode 100644
index 000000000000..b392282c3eb1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
@@ -0,0 +1,353 @@
+//===-- CPPLanguageRuntime.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 <string.h>
+
+#include <memory>
+
+#include "CPPLanguageRuntime.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Target/ThreadPlanStepInRange.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static ConstString g_this = ConstString("this");
+
+char CPPLanguageRuntime::ID = 0;
+
+// Destructor
+CPPLanguageRuntime::~CPPLanguageRuntime() {}
+
+CPPLanguageRuntime::CPPLanguageRuntime(Process *process)
+ : LanguageRuntime(process) {}
+
+bool CPPLanguageRuntime::IsWhitelistedRuntimeValue(ConstString name) {
+ return name == g_this;
+}
+
+bool CPPLanguageRuntime::GetObjectDescription(Stream &str,
+ ValueObject &object) {
+ // C++ has no generic way to do this.
+ return false;
+}
+
+bool CPPLanguageRuntime::GetObjectDescription(
+ Stream &str, Value &value, ExecutionContextScope *exe_scope) {
+ // C++ has no generic way to do this.
+ return false;
+}
+
+CPPLanguageRuntime::LibCppStdFunctionCallableInfo
+CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
+ lldb::ValueObjectSP &valobj_sp) {
+ LibCppStdFunctionCallableInfo optional_info;
+
+ if (!valobj_sp)
+ return optional_info;
+
+ // Member __f_ has type __base*, the contents of which will hold:
+ // 1) a vtable entry which may hold type information needed to discover the
+ // lambda being called
+ // 2) possibly hold a pointer to the callable object
+ // e.g.
+ //
+ // (lldb) frame var -R f_display
+ // (std::__1::function<void (int)>) f_display = {
+ // __buf_ = {
+ // …
+ // }
+ // __f_ = 0x00007ffeefbffa00
+ // }
+ // (lldb) memory read -fA 0x00007ffeefbffa00
+ // 0x7ffeefbffa00: ... `vtable for std::__1::__function::__func<void (*) ...
+ // 0x7ffeefbffa08: ... `print_num(int) at std_function_cppreference_exam ...
+ //
+ // We will be handling five cases below, std::function is wrapping:
+ //
+ // 1) a lambda we know at compile time. We will obtain the name of the lambda
+ // from the first template pameter from __func's vtable. We will look up
+ // the lambda's operator()() and obtain the line table entry.
+ // 2) a lambda we know at runtime. A pointer to the lambdas __invoke method
+ // will be stored after the vtable. We will obtain the lambdas name from
+ // this entry and lookup operator()() and obtain the line table entry.
+ // 3) a callable object via operator()(). We will obtain the name of the
+ // object from the first template parameter from __func's vtable. We will
+ // look up the objectc operator()() and obtain the line table entry.
+ // 4) a member function. A pointer to the function will stored after the
+ // we will obtain the name from this pointer.
+ // 5) a free function. A pointer to the function will stored after the vtable
+ // we will obtain the name from this pointer.
+ ValueObjectSP member__f_(
+ valobj_sp->GetChildMemberWithName(ConstString("__f_"), true));
+
+ if (member__f_) {
+ ValueObjectSP sub_member__f_(
+ member__f_->GetChildMemberWithName(ConstString("__f_"), true));
+
+ if (sub_member__f_)
+ member__f_ = sub_member__f_;
+ }
+
+ lldb::addr_t member__f_pointer_value = member__f_->GetValueAsUnsigned(0);
+
+ optional_info.member__f_pointer_value = member__f_pointer_value;
+
+ ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (process == nullptr)
+ return optional_info;
+
+ uint32_t address_size = process->GetAddressByteSize();
+ Status status;
+
+ // First item pointed to by __f_ should be the pointer to the vtable for
+ // a __base object.
+ lldb::addr_t vtable_address =
+ process->ReadPointerFromMemory(member__f_pointer_value, status);
+
+ if (status.Fail())
+ return optional_info;
+
+ lldb::addr_t address_after_vtable = member__f_pointer_value + address_size;
+ // As commened above we may not have a function pointer but if we do we will
+ // need it.
+ lldb::addr_t possible_function_address =
+ process->ReadPointerFromMemory(address_after_vtable, status);
+
+ if (status.Fail())
+ return optional_info;
+
+ Target &target = process->GetTarget();
+
+ if (target.GetSectionLoadList().IsEmpty())
+ return optional_info;
+
+ Address vtable_addr_resolved;
+ SymbolContext sc;
+ Symbol *symbol;
+
+ if (!target.GetSectionLoadList().ResolveLoadAddress(vtable_address,
+ vtable_addr_resolved))
+ return optional_info;
+
+ target.GetImages().ResolveSymbolContextForAddress(
+ vtable_addr_resolved, eSymbolContextEverything, sc);
+ symbol = sc.symbol;
+
+ if (symbol == nullptr)
+ return optional_info;
+
+ llvm::StringRef vtable_name(symbol->GetName().GetCString());
+ bool found_expected_start_string =
+ vtable_name.startswith("vtable for std::__1::__function::__func<");
+
+ if (!found_expected_start_string)
+ return optional_info;
+
+ // Given case 1 or 3 we have a vtable name, we are want to extract the first
+ // template parameter
+ //
+ // ... __func<main::$_0, std::__1::allocator<main::$_0> ...
+ // ^^^^^^^^^
+ //
+ // We do this by find the first < and , and extracting in between.
+ //
+ // This covers the case of the lambda known at compile time.
+ size_t first_open_angle_bracket = vtable_name.find('<') + 1;
+ size_t first_comma = vtable_name.find(',');
+
+ llvm::StringRef first_template_parameter =
+ vtable_name.slice(first_open_angle_bracket, first_comma);
+
+ Address function_address_resolved;
+
+ // Setup for cases 2, 4 and 5 we have a pointer to a function after the
+ // vtable. We will use a process of elimination to drop through each case
+ // and obtain the data we need.
+ if (target.GetSectionLoadList().ResolveLoadAddress(
+ possible_function_address, function_address_resolved)) {
+ target.GetImages().ResolveSymbolContextForAddress(
+ function_address_resolved, eSymbolContextEverything, sc);
+ symbol = sc.symbol;
+ }
+
+ auto get_name = [&first_template_parameter, &symbol]() {
+ // Given case 1:
+ //
+ // main::$_0
+ //
+ // we want to append ::operator()()
+ if (first_template_parameter.contains("$_"))
+ return llvm::Regex::escape(first_template_parameter.str()) +
+ R"(::operator\(\)\(.*\))";
+
+ if (symbol != nullptr &&
+ symbol->GetName().GetStringRef().contains("__invoke")) {
+
+ llvm::StringRef symbol_name = symbol->GetName().GetStringRef();
+ size_t pos2 = symbol_name.find_last_of(':');
+
+ // Given case 2:
+ //
+ // main::$_1::__invoke(...)
+ //
+ // We want to slice off __invoke(...) and append operator()()
+ std::string lambda_operator =
+ llvm::Regex::escape(symbol_name.slice(0, pos2 + 1).str()) +
+ R"(operator\(\)\(.*\))";
+
+ return lambda_operator;
+ }
+
+ // Case 3
+ return first_template_parameter.str() + R"(::operator\(\)\(.*\))";
+ ;
+ };
+
+ std::string func_to_match = get_name();
+
+ SymbolContextList scl;
+
+ target.GetImages().FindSymbolsMatchingRegExAndType(
+ RegularExpression{R"(^)" + func_to_match}, eSymbolTypeAny, scl, true);
+
+ // Case 1,2 or 3
+ if (scl.GetSize() >= 1) {
+ SymbolContext sc2 = scl[0];
+
+ AddressRange range;
+ sc2.GetAddressRange(eSymbolContextEverything, 0, false, range);
+
+ Address address = range.GetBaseAddress();
+
+ Address addr;
+ if (target.ResolveLoadAddress(address.GetCallableLoadAddress(&target),
+ addr)) {
+ LineEntry line_entry;
+ addr.CalculateSymbolContextLineEntry(line_entry);
+
+ if (first_template_parameter.contains("$_") ||
+ (symbol != nullptr &&
+ symbol->GetName().GetStringRef().contains("__invoke"))) {
+ // Case 1 and 2
+ optional_info.callable_case = LibCppStdFunctionCallableCase::Lambda;
+ } else {
+ // Case 3
+ optional_info.callable_case =
+ LibCppStdFunctionCallableCase::CallableObject;
+ }
+
+ optional_info.callable_symbol = *symbol;
+ optional_info.callable_line_entry = line_entry;
+ optional_info.callable_address = addr;
+ return optional_info;
+ }
+ }
+
+ // Case 4 or 5
+ if (symbol && !symbol->GetName().GetStringRef().startswith("vtable for")) {
+ optional_info.callable_case =
+ LibCppStdFunctionCallableCase::FreeOrMemberFunction;
+ optional_info.callable_address = function_address_resolved;
+ optional_info.callable_symbol = *symbol;
+
+ return optional_info;
+ }
+
+ return optional_info;
+}
+
+lldb::ThreadPlanSP
+CPPLanguageRuntime::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) {
+ ThreadPlanSP ret_plan_sp;
+
+ lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();
+
+ TargetSP target_sp(thread.CalculateTarget());
+
+ if (target_sp->GetSectionLoadList().IsEmpty())
+ return ret_plan_sp;
+
+ Address pc_addr_resolved;
+ SymbolContext sc;
+ Symbol *symbol;
+
+ if (!target_sp->GetSectionLoadList().ResolveLoadAddress(curr_pc,
+ pc_addr_resolved))
+ return ret_plan_sp;
+
+ target_sp->GetImages().ResolveSymbolContextForAddress(
+ pc_addr_resolved, eSymbolContextEverything, sc);
+ symbol = sc.symbol;
+
+ if (symbol == nullptr)
+ return ret_plan_sp;
+
+ llvm::StringRef function_name(symbol->GetName().GetCString());
+
+ // Handling the case where we are attempting to step into std::function.
+ // The behavior will be that we will attempt to obtain the wrapped
+ // callable via FindLibCppStdFunctionCallableInfo() and if we find it we
+ // will return a ThreadPlanRunToAddress to the callable. Therefore we will
+ // step into the wrapped callable.
+ //
+ bool found_expected_start_string =
+ function_name.startswith("std::__1::function<");
+
+ if (!found_expected_start_string)
+ return ret_plan_sp;
+
+ AddressRange range_of_curr_func;
+ sc.GetAddressRange(eSymbolContextEverything, 0, false, range_of_curr_func);
+
+ StackFrameSP frame = thread.GetStackFrameAtIndex(0);
+
+ if (frame) {
+ ValueObjectSP value_sp = frame->FindVariable(g_this);
+
+ CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info =
+ FindLibCppStdFunctionCallableInfo(value_sp);
+
+ if (callable_info.callable_case != LibCppStdFunctionCallableCase::Invalid &&
+ value_sp->GetValueIsValid()) {
+ // We found the std::function wrapped callable and we have its address.
+ // We now create a ThreadPlan to run to the callable.
+ ret_plan_sp = std::make_shared<ThreadPlanRunToAddress>(
+ thread, callable_info.callable_address, stop_others);
+ return ret_plan_sp;
+ } else {
+ // We are in std::function but we could not obtain the callable.
+ // We create a ThreadPlan to keep stepping through using the address range
+ // of the current function.
+ ret_plan_sp = std::make_shared<ThreadPlanStepInRange>(
+ thread, range_of_curr_func, sc, eOnlyThisThread, eLazyBoolYes,
+ eLazyBoolYes);
+ return ret_plan_sp;
+ }
+ }
+
+ return ret_plan_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h
new file mode 100644
index 000000000000..28526361efc4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h
@@ -0,0 +1,90 @@
+//===-- CPPLanguageRuntime.h
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CPPLanguageRuntime_h_
+#define liblldb_CPPLanguageRuntime_h_
+
+#include <vector>
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class CPPLanguageRuntime : public LanguageRuntime {
+public:
+ enum class LibCppStdFunctionCallableCase {
+ Lambda = 0,
+ CallableObject,
+ FreeOrMemberFunction,
+ Invalid
+ };
+
+ struct LibCppStdFunctionCallableInfo {
+ Symbol callable_symbol;
+ Address callable_address;
+ LineEntry callable_line_entry;
+ lldb::addr_t member__f_pointer_value = 0u;
+ LibCppStdFunctionCallableCase callable_case =
+ LibCppStdFunctionCallableCase::Invalid;
+ };
+
+ LibCppStdFunctionCallableInfo
+ FindLibCppStdFunctionCallableInfo(lldb::ValueObjectSP &valobj_sp);
+
+ ~CPPLanguageRuntime() override;
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || LanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeC_plus_plus;
+ }
+
+ static CPPLanguageRuntime *Get(Process &process) {
+ return llvm::cast_or_null<CPPLanguageRuntime>(
+ process.GetLanguageRuntime(lldb::eLanguageTypeC_plus_plus));
+ }
+
+ bool GetObjectDescription(Stream &str, ValueObject &object) override;
+
+ bool GetObjectDescription(Stream &str, Value &value,
+ ExecutionContextScope *exe_scope) override;
+
+ /// Obtain a ThreadPlan to get us into C++ constructs such as std::function.
+ ///
+ /// \param[in] thread
+ /// Curent thrad of execution.
+ ///
+ /// \param[in] stop_others
+ /// True if other threads should pause during execution.
+ ///
+ /// \return
+ /// A ThreadPlan Shared pointer
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) override;
+
+ bool IsWhitelistedRuntimeValue(ConstString name) override;
+protected:
+ // Classes that inherit from CPPLanguageRuntime can see and modify these
+ CPPLanguageRuntime(Process *process);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(CPPLanguageRuntime);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CPPLanguageRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
new file mode 100644
index 000000000000..41f38a4e3dcd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
@@ -0,0 +1,617 @@
+//===-- ItaniumABILanguageRuntime.cpp --------------------------------------*-
+//C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ItaniumABILanguageRuntime.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Mangled.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static const char *vtable_demangled_prefix = "vtable for ";
+
+char ItaniumABILanguageRuntime::ID = 0;
+
+bool ItaniumABILanguageRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
+ const bool check_cxx = true;
+ const bool check_objc = false;
+ return in_value.GetCompilerType().IsPossibleDynamicType(nullptr, check_cxx,
+ check_objc);
+}
+
+TypeAndOrName ItaniumABILanguageRuntime::GetTypeInfoFromVTableAddress(
+ ValueObject &in_value, lldb::addr_t original_ptr,
+ lldb::addr_t vtable_load_addr) {
+ if (m_process && vtable_load_addr != LLDB_INVALID_ADDRESS) {
+ // Find the symbol that contains the "vtable_load_addr" address
+ Address vtable_addr;
+ Target &target = m_process->GetTarget();
+ if (!target.GetSectionLoadList().IsEmpty()) {
+ if (target.GetSectionLoadList().ResolveLoadAddress(vtable_load_addr,
+ vtable_addr)) {
+ // See if we have cached info for this type already
+ TypeAndOrName type_info = GetDynamicTypeInfo(vtable_addr);
+ if (type_info)
+ return type_info;
+
+ SymbolContext sc;
+ target.GetImages().ResolveSymbolContextForAddress(
+ vtable_addr, eSymbolContextSymbol, sc);
+ Symbol *symbol = sc.symbol;
+ if (symbol != nullptr) {
+ const char *name =
+ symbol->GetMangled()
+ .GetDemangledName(lldb::eLanguageTypeC_plus_plus)
+ .AsCString();
+ if (name && strstr(name, vtable_demangled_prefix) == name) {
+ Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("0x%16.16" PRIx64
+ ": static-type = '%s' has vtable symbol '%s'\n",
+ original_ptr, in_value.GetTypeName().GetCString(),
+ name);
+ // We are a C++ class, that's good. Get the class name and look it
+ // up:
+ const char *class_name = name + strlen(vtable_demangled_prefix);
+ // We know the class name is absolute, so tell FindTypes that by
+ // prefixing it with the root namespace:
+ std::string lookup_name("::");
+ lookup_name.append(class_name);
+
+ type_info.SetName(class_name);
+ const bool exact_match = true;
+ TypeList class_types;
+
+ uint32_t num_matches = 0;
+ // First look in the module that the vtable symbol came from and
+ // look for a single exact match.
+ llvm::DenseSet<SymbolFile *> searched_symbol_files;
+ if (sc.module_sp) {
+ num_matches = sc.module_sp->FindTypes(
+ ConstString(lookup_name), exact_match, 1,
+ searched_symbol_files, class_types);
+ }
+
+ // If we didn't find a symbol, then move on to the entire module
+ // list in the target and get as many unique matches as possible
+ if (num_matches == 0) {
+ num_matches = target.GetImages().FindTypes(
+ nullptr, ConstString(lookup_name), exact_match, UINT32_MAX,
+ searched_symbol_files, class_types);
+ }
+
+ lldb::TypeSP type_sp;
+ if (num_matches == 0) {
+ if (log)
+ log->Printf("0x%16.16" PRIx64 ": is not dynamic\n",
+ original_ptr);
+ return TypeAndOrName();
+ }
+ if (num_matches == 1) {
+ type_sp = class_types.GetTypeAtIndex(0);
+ if (type_sp) {
+ if (ClangASTContext::IsCXXClassType(
+ type_sp->GetForwardCompilerType())) {
+ if (log)
+ log->Printf(
+ "0x%16.16" PRIx64
+ ": static-type = '%s' has dynamic type: uid={0x%" PRIx64
+ "}, type-name='%s'\n",
+ original_ptr, in_value.GetTypeName().AsCString(),
+ type_sp->GetID(), type_sp->GetName().GetCString());
+ type_info.SetTypeSP(type_sp);
+ }
+ }
+ } else if (num_matches > 1) {
+ size_t i;
+ if (log) {
+ for (i = 0; i < num_matches; i++) {
+ type_sp = class_types.GetTypeAtIndex(i);
+ if (type_sp) {
+ if (log)
+ log->Printf(
+ "0x%16.16" PRIx64
+ ": static-type = '%s' has multiple matching dynamic "
+ "types: uid={0x%" PRIx64 "}, type-name='%s'\n",
+ original_ptr, in_value.GetTypeName().AsCString(),
+ type_sp->GetID(), type_sp->GetName().GetCString());
+ }
+ }
+ }
+
+ for (i = 0; i < num_matches; i++) {
+ type_sp = class_types.GetTypeAtIndex(i);
+ if (type_sp) {
+ if (ClangASTContext::IsCXXClassType(
+ type_sp->GetForwardCompilerType())) {
+ if (log)
+ log->Printf(
+ "0x%16.16" PRIx64 ": static-type = '%s' has multiple "
+ "matching dynamic types, picking "
+ "this one: uid={0x%" PRIx64
+ "}, type-name='%s'\n",
+ original_ptr, in_value.GetTypeName().AsCString(),
+ type_sp->GetID(), type_sp->GetName().GetCString());
+ type_info.SetTypeSP(type_sp);
+ }
+ }
+ }
+
+ if (log && i == num_matches) {
+ log->Printf(
+ "0x%16.16" PRIx64
+ ": static-type = '%s' has multiple matching dynamic "
+ "types, didn't find a C++ match\n",
+ original_ptr, in_value.GetTypeName().AsCString());
+ }
+ }
+ if (type_info)
+ SetDynamicTypeInfo(vtable_addr, type_info);
+ return type_info;
+ }
+ }
+ }
+ }
+ }
+ return TypeAndOrName();
+}
+
+bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress(
+ ValueObject &in_value, lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &dynamic_address,
+ Value::ValueType &value_type) {
+ // For Itanium, if the type has a vtable pointer in the object, it will be at
+ // offset 0 in the object. That will point to the "address point" within the
+ // vtable (not the beginning of the vtable.) We can then look up the symbol
+ // containing this "address point" and that symbol's name demangled will
+ // contain the full class name. The second pointer above the "address point"
+ // is the "offset_to_top". We'll use that to get the start of the value
+ // object which holds the dynamic type.
+ //
+
+ class_type_or_name.Clear();
+ value_type = Value::ValueType::eValueTypeScalar;
+
+ // Only a pointer or reference type can have a different dynamic and static
+ // type:
+ if (!CouldHaveDynamicValue(in_value))
+ return false;
+
+ // First job, pull out the address at 0 offset from the object.
+ AddressType address_type;
+ lldb::addr_t original_ptr = in_value.GetPointerValue(&address_type);
+ if (original_ptr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ ExecutionContext exe_ctx(in_value.GetExecutionContextRef());
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (process == nullptr)
+ return false;
+
+ Status error;
+ const lldb::addr_t vtable_address_point =
+ process->ReadPointerFromMemory(original_ptr, error);
+
+ if (!error.Success() || vtable_address_point == LLDB_INVALID_ADDRESS)
+ return false;
+
+ class_type_or_name = GetTypeInfoFromVTableAddress(in_value, original_ptr,
+ vtable_address_point);
+
+ if (!class_type_or_name)
+ return false;
+
+ CompilerType type = class_type_or_name.GetCompilerType();
+ // There can only be one type with a given name, so we've just found
+ // duplicate definitions, and this one will do as well as any other. We
+ // don't consider something to have a dynamic type if it is the same as
+ // the static type. So compare against the value we were handed.
+ if (!type)
+ return true;
+
+ if (ClangASTContext::AreTypesSame(in_value.GetCompilerType(), type)) {
+ // The dynamic type we found was the same type, so we don't have a
+ // dynamic type here...
+ return false;
+ }
+
+ // The offset_to_top is two pointers above the vtable pointer.
+ const uint32_t addr_byte_size = process->GetAddressByteSize();
+ const lldb::addr_t offset_to_top_location =
+ vtable_address_point - 2 * addr_byte_size;
+ // Watch for underflow, offset_to_top_location should be less than
+ // vtable_address_point
+ if (offset_to_top_location >= vtable_address_point)
+ return false;
+ const int64_t offset_to_top = process->ReadSignedIntegerFromMemory(
+ offset_to_top_location, addr_byte_size, INT64_MIN, error);
+
+ if (offset_to_top == INT64_MIN)
+ return false;
+ // So the dynamic type is a value that starts at offset_to_top above
+ // the original address.
+ lldb::addr_t dynamic_addr = original_ptr + offset_to_top;
+ if (!process->GetTarget().GetSectionLoadList().ResolveLoadAddress(
+ dynamic_addr, dynamic_address)) {
+ dynamic_address.SetRawAddress(dynamic_addr);
+ }
+ return true;
+}
+
+TypeAndOrName ItaniumABILanguageRuntime::FixUpDynamicType(
+ const TypeAndOrName &type_and_or_name, ValueObject &static_value) {
+ CompilerType static_type(static_value.GetCompilerType());
+ Flags static_type_flags(static_type.GetTypeInfo());
+
+ TypeAndOrName ret(type_and_or_name);
+ if (type_and_or_name.HasType()) {
+ // The type will always be the type of the dynamic object. If our parent's
+ // type was a pointer, then our type should be a pointer to the type of the
+ // dynamic object. If a reference, then the original type should be
+ // okay...
+ CompilerType orig_type = type_and_or_name.GetCompilerType();
+ CompilerType corrected_type = orig_type;
+ if (static_type_flags.AllSet(eTypeIsPointer))
+ corrected_type = orig_type.GetPointerType();
+ else if (static_type_flags.AllSet(eTypeIsReference))
+ corrected_type = orig_type.GetLValueReferenceType();
+ ret.SetCompilerType(corrected_type);
+ } else {
+ // If we are here we need to adjust our dynamic type name to include the
+ // correct & or * symbol
+ std::string corrected_name(type_and_or_name.GetName().GetCString());
+ if (static_type_flags.AllSet(eTypeIsPointer))
+ corrected_name.append(" *");
+ else if (static_type_flags.AllSet(eTypeIsReference))
+ corrected_name.append(" &");
+ // the parent type should be a correctly pointer'ed or referenc'ed type
+ ret.SetCompilerType(static_type);
+ ret.SetName(corrected_name.c_str());
+ }
+ return ret;
+}
+
+// Static Functions
+LanguageRuntime *
+ItaniumABILanguageRuntime::CreateInstance(Process *process,
+ lldb::LanguageType language) {
+ // FIXME: We have to check the process and make sure we actually know that
+ // this process supports
+ // the Itanium ABI.
+ if (language == eLanguageTypeC_plus_plus ||
+ language == eLanguageTypeC_plus_plus_03 ||
+ language == eLanguageTypeC_plus_plus_11 ||
+ language == eLanguageTypeC_plus_plus_14)
+ return new ItaniumABILanguageRuntime(process);
+ else
+ return nullptr;
+}
+
+class CommandObjectMultiwordItaniumABI_Demangle : public CommandObjectParsed {
+public:
+ CommandObjectMultiwordItaniumABI_Demangle(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "demangle",
+ "Demangle a C++ mangled name.",
+ "language cplusplus demangle") {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeSymbol;
+ index_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectMultiwordItaniumABI_Demangle() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ bool demangled_any = false;
+ bool error_any = false;
+ for (auto &entry : command.entries()) {
+ if (entry.ref.empty())
+ continue;
+
+ // the actual Mangled class should be strict about this, but on the
+ // command line if you're copying mangled names out of 'nm' on Darwin,
+ // they will come out with an extra underscore - be willing to strip this
+ // on behalf of the user. This is the moral equivalent of the -_/-n
+ // options to c++filt
+ auto name = entry.ref;
+ if (name.startswith("__Z"))
+ name = name.drop_front();
+
+ Mangled mangled(name, true);
+ if (mangled.GuessLanguage() == lldb::eLanguageTypeC_plus_plus) {
+ ConstString demangled(
+ mangled.GetDisplayDemangledName(lldb::eLanguageTypeC_plus_plus));
+ demangled_any = true;
+ result.AppendMessageWithFormat("%s ---> %s\n", entry.ref.str().c_str(),
+ demangled.GetCString());
+ } else {
+ error_any = true;
+ result.AppendErrorWithFormat("%s is not a valid C++ mangled name\n",
+ entry.ref.str().c_str());
+ }
+ }
+
+ result.SetStatus(
+ error_any ? lldb::eReturnStatusFailed
+ : (demangled_any ? lldb::eReturnStatusSuccessFinishResult
+ : lldb::eReturnStatusSuccessFinishNoResult));
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectMultiwordItaniumABI : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordItaniumABI(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "cplusplus",
+ "Commands for operating on the C++ language runtime.",
+ "cplusplus <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "demangle",
+ CommandObjectSP(
+ new CommandObjectMultiwordItaniumABI_Demangle(interpreter)));
+ }
+
+ ~CommandObjectMultiwordItaniumABI() override = default;
+};
+
+void ItaniumABILanguageRuntime::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "Itanium ABI for the C++ language", CreateInstance,
+ [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP {
+ return CommandObjectSP(
+ new CommandObjectMultiwordItaniumABI(interpreter));
+ });
+}
+
+void ItaniumABILanguageRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ItaniumABILanguageRuntime::GetPluginNameStatic() {
+ static ConstString g_name("itanium");
+ return g_name;
+}
+
+// PluginInterface protocol
+lldb_private::ConstString ItaniumABILanguageRuntime::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ItaniumABILanguageRuntime::GetPluginVersion() { return 1; }
+
+BreakpointResolverSP ItaniumABILanguageRuntime::CreateExceptionResolver(
+ Breakpoint *bkpt, bool catch_bp, bool throw_bp) {
+ return CreateExceptionResolver(bkpt, catch_bp, throw_bp, false);
+}
+
+BreakpointResolverSP ItaniumABILanguageRuntime::CreateExceptionResolver(
+ Breakpoint *bkpt, bool catch_bp, bool throw_bp, bool for_expressions) {
+ // One complication here is that most users DON'T want to stop at
+ // __cxa_allocate_expression, but until we can do anything better with
+ // predicting unwinding the expression parser does. So we have two forms of
+ // the exception breakpoints, one for expressions that leaves out
+ // __cxa_allocate_exception, and one that includes it. The
+ // SetExceptionBreakpoints does the latter, the CreateExceptionBreakpoint in
+ // the runtime the former.
+ static const char *g_catch_name = "__cxa_begin_catch";
+ static const char *g_throw_name1 = "__cxa_throw";
+ static const char *g_throw_name2 = "__cxa_rethrow";
+ static const char *g_exception_throw_name = "__cxa_allocate_exception";
+ std::vector<const char *> exception_names;
+ exception_names.reserve(4);
+ if (catch_bp)
+ exception_names.push_back(g_catch_name);
+
+ if (throw_bp) {
+ exception_names.push_back(g_throw_name1);
+ exception_names.push_back(g_throw_name2);
+ }
+
+ if (for_expressions)
+ exception_names.push_back(g_exception_throw_name);
+
+ BreakpointResolverSP resolver_sp(new BreakpointResolverName(
+ bkpt, exception_names.data(), exception_names.size(),
+ eFunctionNameTypeBase, eLanguageTypeUnknown, 0, eLazyBoolNo));
+
+ return resolver_sp;
+}
+
+lldb::SearchFilterSP ItaniumABILanguageRuntime::CreateExceptionSearchFilter() {
+ Target &target = m_process->GetTarget();
+
+ FileSpecList filter_modules;
+ if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) {
+ // Limit the number of modules that are searched for these breakpoints for
+ // Apple binaries.
+ filter_modules.Append(FileSpec("libc++abi.dylib"));
+ filter_modules.Append(FileSpec("libSystem.B.dylib"));
+ }
+ return target.GetSearchFilterForModuleList(&filter_modules);
+}
+
+lldb::BreakpointSP ItaniumABILanguageRuntime::CreateExceptionBreakpoint(
+ bool catch_bp, bool throw_bp, bool for_expressions, bool is_internal) {
+ Target &target = m_process->GetTarget();
+ FileSpecList filter_modules;
+ BreakpointResolverSP exception_resolver_sp =
+ CreateExceptionResolver(nullptr, catch_bp, throw_bp, for_expressions);
+ SearchFilterSP filter_sp(CreateExceptionSearchFilter());
+ const bool hardware = false;
+ const bool resolve_indirect_functions = false;
+ return target.CreateBreakpoint(filter_sp, exception_resolver_sp, is_internal,
+ hardware, resolve_indirect_functions);
+}
+
+void ItaniumABILanguageRuntime::SetExceptionBreakpoints() {
+ if (!m_process)
+ return;
+
+ const bool catch_bp = false;
+ const bool throw_bp = true;
+ const bool is_internal = true;
+ const bool for_expressions = true;
+
+ // For the exception breakpoints set by the Expression parser, we'll be a
+ // little more aggressive and stop at exception allocation as well.
+
+ if (m_cxx_exception_bp_sp) {
+ m_cxx_exception_bp_sp->SetEnabled(true);
+ } else {
+ m_cxx_exception_bp_sp = CreateExceptionBreakpoint(
+ catch_bp, throw_bp, for_expressions, is_internal);
+ if (m_cxx_exception_bp_sp)
+ m_cxx_exception_bp_sp->SetBreakpointKind("c++ exception");
+ }
+}
+
+void ItaniumABILanguageRuntime::ClearExceptionBreakpoints() {
+ if (!m_process)
+ return;
+
+ if (m_cxx_exception_bp_sp) {
+ m_cxx_exception_bp_sp->SetEnabled(false);
+ }
+}
+
+bool ItaniumABILanguageRuntime::ExceptionBreakpointsAreSet() {
+ return m_cxx_exception_bp_sp && m_cxx_exception_bp_sp->IsEnabled();
+}
+
+bool ItaniumABILanguageRuntime::ExceptionBreakpointsExplainStop(
+ lldb::StopInfoSP stop_reason) {
+ if (!m_process)
+ return false;
+
+ if (!stop_reason || stop_reason->GetStopReason() != eStopReasonBreakpoint)
+ return false;
+
+ uint64_t break_site_id = stop_reason->GetValue();
+ return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint(
+ break_site_id, m_cxx_exception_bp_sp->GetID());
+}
+
+ValueObjectSP ItaniumABILanguageRuntime::GetExceptionObjectForThread(
+ ThreadSP thread_sp) {
+ if (!thread_sp->SafeToCallFunctions())
+ return {};
+
+ ClangASTContext *clang_ast_context =
+ m_process->GetTarget().GetScratchClangASTContext();
+ CompilerType voidstar =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+ DiagnosticManager diagnostics;
+ ExecutionContext exe_ctx;
+ EvaluateExpressionOptions options;
+
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetStopOthers(true);
+ options.SetTimeout(m_process->GetUtilityExpressionTimeout());
+ options.SetTryAllThreads(false);
+ thread_sp->CalculateExecutionContext(exe_ctx);
+
+ const ModuleList &modules = m_process->GetTarget().GetImages();
+ SymbolContextList contexts;
+ SymbolContext context;
+
+ modules.FindSymbolsWithNameAndType(
+ ConstString("__cxa_current_exception_type"), eSymbolTypeCode, contexts);
+ contexts.GetContextAtIndex(0, context);
+ Address addr = context.symbol->GetAddress();
+
+ Status error;
+ FunctionCaller *function_caller =
+ m_process->GetTarget().GetFunctionCallerForLanguage(
+ eLanguageTypeC, voidstar, addr, ValueList(), "caller", error);
+
+ ExpressionResults func_call_ret;
+ Value results;
+ func_call_ret = function_caller->ExecuteFunction(exe_ctx, nullptr, options,
+ diagnostics, results);
+ if (func_call_ret != eExpressionCompleted || !error.Success()) {
+ return ValueObjectSP();
+ }
+
+ size_t ptr_size = m_process->GetAddressByteSize();
+ addr_t result_ptr = results.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ addr_t exception_addr =
+ m_process->ReadPointerFromMemory(result_ptr - ptr_size, error);
+
+ if (!error.Success()) {
+ return ValueObjectSP();
+ }
+
+ lldb_private::formatters::InferiorSizedWord exception_isw(exception_addr,
+ *m_process);
+ ValueObjectSP exception = ValueObject::CreateValueObjectFromData(
+ "exception", exception_isw.GetAsData(m_process->GetByteOrder()), exe_ctx,
+ voidstar);
+ exception = exception->GetDynamicValue(eDynamicDontRunTarget);
+
+ return exception;
+}
+
+TypeAndOrName ItaniumABILanguageRuntime::GetDynamicTypeInfo(
+ const lldb_private::Address &vtable_addr) {
+ std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex);
+ DynamicTypeCache::const_iterator pos = m_dynamic_type_map.find(vtable_addr);
+ if (pos == m_dynamic_type_map.end())
+ return TypeAndOrName();
+ else
+ return pos->second;
+}
+
+void ItaniumABILanguageRuntime::SetDynamicTypeInfo(
+ const lldb_private::Address &vtable_addr, const TypeAndOrName &type_info) {
+ std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex);
+ m_dynamic_type_map[vtable_addr] = type_info;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
new file mode 100644
index 000000000000..97cc81b8681f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
@@ -0,0 +1,117 @@
+//===-- ItaniumABILanguageRuntime.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ItaniumABILanguageRuntime_h_
+#define liblldb_ItaniumABILanguageRuntime_h_
+
+#include <map>
+#include <mutex>
+#include <vector>
+
+#include "lldb/Breakpoint/BreakpointResolver.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
+
+namespace lldb_private {
+
+class ItaniumABILanguageRuntime : public lldb_private::CPPLanguageRuntime {
+public:
+ ~ItaniumABILanguageRuntime() override = default;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::LanguageRuntime *
+ CreateInstance(Process *process, lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || CPPLanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) override;
+
+ bool CouldHaveDynamicValue(ValueObject &in_value) override;
+
+ void SetExceptionBreakpoints() override;
+
+ void ClearExceptionBreakpoints() override;
+
+ bool ExceptionBreakpointsAreSet() override;
+
+ bool ExceptionBreakpointsExplainStop(lldb::StopInfoSP stop_reason) override;
+
+ lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt,
+ bool catch_bp,
+ bool throw_bp) override;
+
+ lldb::SearchFilterSP CreateExceptionSearchFilter() override;
+
+ lldb::ValueObjectSP GetExceptionObjectForThread(
+ lldb::ThreadSP thread_sp) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt,
+ bool catch_bp,
+ bool throw_bp,
+ bool for_expressions);
+
+ lldb::BreakpointSP CreateExceptionBreakpoint(bool catch_bp, bool throw_bp,
+ bool for_expressions,
+ bool is_internal);
+
+private:
+ typedef std::map<lldb_private::Address, TypeAndOrName> DynamicTypeCache;
+
+ ItaniumABILanguageRuntime(Process *process)
+ : // Call CreateInstance instead.
+ lldb_private::CPPLanguageRuntime(process), m_cxx_exception_bp_sp(),
+ m_dynamic_type_map(), m_dynamic_type_map_mutex() {}
+
+ lldb::BreakpointSP m_cxx_exception_bp_sp;
+ DynamicTypeCache m_dynamic_type_map;
+ std::mutex m_dynamic_type_map_mutex;
+
+ TypeAndOrName GetTypeInfoFromVTableAddress(ValueObject &in_value,
+ lldb::addr_t original_ptr,
+ lldb::addr_t vtable_addr);
+
+ TypeAndOrName GetDynamicTypeInfo(const lldb_private::Address &vtable_addr);
+
+ void SetDynamicTypeInfo(const lldb_private::Address &vtable_addr,
+ const TypeAndOrName &type_info);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ItaniumABILanguageRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
new file mode 100644
index 000000000000..93aa07f89165
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
@@ -0,0 +1,541 @@
+//===-- AppleObjCClassDescriptorV2.cpp -----------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AppleObjCClassDescriptorV2.h"
+
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool ClassDescriptorV2::Read_objc_class(
+ Process *process, std::unique_ptr<objc_class_t> &objc_class) const {
+ objc_class.reset(new objc_class_t);
+
+ bool ret = objc_class->Read(process, m_objc_class_ptr);
+
+ if (!ret)
+ objc_class.reset();
+
+ return ret;
+}
+
+static lldb::addr_t GetClassDataMask(Process *process) {
+ switch (process->GetAddressByteSize()) {
+ case 4:
+ return 0xfffffffcUL;
+ case 8:
+ return 0x00007ffffffffff8UL;
+ default:
+ break;
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool ClassDescriptorV2::objc_class_t::Read(Process *process,
+ lldb::addr_t addr) {
+ size_t ptr_size = process->GetAddressByteSize();
+
+ size_t objc_class_size = ptr_size // uintptr_t isa;
+ + ptr_size // Class superclass;
+ + ptr_size // void *cache;
+ + ptr_size // IMP *vtable;
+ + ptr_size; // uintptr_t data_NEVER_USE;
+
+ DataBufferHeap objc_class_buf(objc_class_size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, objc_class_buf.GetBytes(), objc_class_size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(objc_class_buf.GetBytes(), objc_class_size,
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_isa = extractor.GetAddress_unchecked(&cursor); // uintptr_t isa;
+ m_superclass = extractor.GetAddress_unchecked(&cursor); // Class superclass;
+ m_cache_ptr = extractor.GetAddress_unchecked(&cursor); // void *cache;
+ m_vtable_ptr = extractor.GetAddress_unchecked(&cursor); // IMP *vtable;
+ lldb::addr_t data_NEVER_USE =
+ extractor.GetAddress_unchecked(&cursor); // uintptr_t data_NEVER_USE;
+
+ m_flags = (uint8_t)(data_NEVER_USE & (lldb::addr_t)3);
+ m_data_ptr = data_NEVER_USE & GetClassDataMask(process);
+
+ return true;
+}
+
+bool ClassDescriptorV2::class_rw_t::Read(Process *process, lldb::addr_t addr) {
+ size_t ptr_size = process->GetAddressByteSize();
+
+ size_t size = sizeof(uint32_t) // uint32_t flags;
+ + sizeof(uint32_t) // uint32_t version;
+ + ptr_size // const class_ro_t *ro;
+ + ptr_size // union { method_list_t **method_lists;
+ // method_list_t *method_list; };
+ + ptr_size // struct chained_property_list *properties;
+ + ptr_size // const protocol_list_t **protocols;
+ + ptr_size // Class firstSubclass;
+ + ptr_size; // Class nextSiblingClass;
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_flags = extractor.GetU32_unchecked(&cursor);
+ m_version = extractor.GetU32_unchecked(&cursor);
+ m_ro_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_method_list_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_properties_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_firstSubclass = extractor.GetAddress_unchecked(&cursor);
+ m_nextSiblingClass = extractor.GetAddress_unchecked(&cursor);
+
+ return true;
+}
+
+bool ClassDescriptorV2::class_ro_t::Read(Process *process, lldb::addr_t addr) {
+ size_t ptr_size = process->GetAddressByteSize();
+
+ size_t size = sizeof(uint32_t) // uint32_t flags;
+ + sizeof(uint32_t) // uint32_t instanceStart;
+ + sizeof(uint32_t) // uint32_t instanceSize;
+ + (ptr_size == 8 ? sizeof(uint32_t)
+ : 0) // uint32_t reserved; // __LP64__ only
+ + ptr_size // const uint8_t *ivarLayout;
+ + ptr_size // const char *name;
+ + ptr_size // const method_list_t *baseMethods;
+ + ptr_size // const protocol_list_t *baseProtocols;
+ + ptr_size // const ivar_list_t *ivars;
+ + ptr_size // const uint8_t *weakIvarLayout;
+ + ptr_size; // const property_list_t *baseProperties;
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_flags = extractor.GetU32_unchecked(&cursor);
+ m_instanceStart = extractor.GetU32_unchecked(&cursor);
+ m_instanceSize = extractor.GetU32_unchecked(&cursor);
+ if (ptr_size == 8)
+ m_reserved = extractor.GetU32_unchecked(&cursor);
+ else
+ m_reserved = 0;
+ m_ivarLayout_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_name_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_baseMethods_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_baseProtocols_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_ivars_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_weakIvarLayout_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_baseProperties_ptr = extractor.GetAddress_unchecked(&cursor);
+
+ DataBufferHeap name_buf(1024, '\0');
+
+ process->ReadCStringFromMemory(m_name_ptr, (char *)name_buf.GetBytes(),
+ name_buf.GetByteSize(), error);
+
+ if (error.Fail()) {
+ return false;
+ }
+
+ m_name.assign((char *)name_buf.GetBytes());
+
+ return true;
+}
+
+bool ClassDescriptorV2::Read_class_row(
+ Process *process, const objc_class_t &objc_class,
+ std::unique_ptr<class_ro_t> &class_ro,
+ std::unique_ptr<class_rw_t> &class_rw) const {
+ class_ro.reset();
+ class_rw.reset();
+
+ Status error;
+ uint32_t class_row_t_flags = process->ReadUnsignedIntegerFromMemory(
+ objc_class.m_data_ptr, sizeof(uint32_t), 0, error);
+ if (!error.Success())
+ return false;
+
+ if (class_row_t_flags & RW_REALIZED) {
+ class_rw.reset(new class_rw_t);
+
+ if (!class_rw->Read(process, objc_class.m_data_ptr)) {
+ class_rw.reset();
+ return false;
+ }
+
+ class_ro.reset(new class_ro_t);
+
+ if (!class_ro->Read(process, class_rw->m_ro_ptr)) {
+ class_rw.reset();
+ class_ro.reset();
+ return false;
+ }
+ } else {
+ class_ro.reset(new class_ro_t);
+
+ if (!class_ro->Read(process, objc_class.m_data_ptr)) {
+ class_ro.reset();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ClassDescriptorV2::method_list_t::Read(Process *process,
+ lldb::addr_t addr) {
+ size_t size = sizeof(uint32_t) // uint32_t entsize_NEVER_USE;
+ + sizeof(uint32_t); // uint32_t count;
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_entsize = extractor.GetU32_unchecked(&cursor) & ~(uint32_t)3;
+ m_count = extractor.GetU32_unchecked(&cursor);
+ m_first_ptr = addr + cursor;
+
+ return true;
+}
+
+bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr) {
+ size_t size = GetSize(process);
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_name_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_types_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_imp_ptr = extractor.GetAddress_unchecked(&cursor);
+
+ process->ReadCStringFromMemory(m_name_ptr, m_name, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ process->ReadCStringFromMemory(m_types_ptr, m_types, error);
+ return !error.Fail();
+}
+
+bool ClassDescriptorV2::ivar_list_t::Read(Process *process, lldb::addr_t addr) {
+ size_t size = sizeof(uint32_t) // uint32_t entsize;
+ + sizeof(uint32_t); // uint32_t count;
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_entsize = extractor.GetU32_unchecked(&cursor);
+ m_count = extractor.GetU32_unchecked(&cursor);
+ m_first_ptr = addr + cursor;
+
+ return true;
+}
+
+bool ClassDescriptorV2::ivar_t::Read(Process *process, lldb::addr_t addr) {
+ size_t size = GetSize(process);
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_offset_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_name_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_type_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_alignment = extractor.GetU32_unchecked(&cursor);
+ m_size = extractor.GetU32_unchecked(&cursor);
+
+ process->ReadCStringFromMemory(m_name_ptr, m_name, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ process->ReadCStringFromMemory(m_type_ptr, m_type, error);
+ return !error.Fail();
+}
+
+bool ClassDescriptorV2::Describe(
+ std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
+ std::function<bool(const char *, const char *)> const &instance_method_func,
+ std::function<bool(const char *, const char *)> const &class_method_func,
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)> const &ivar_func) const {
+ lldb_private::Process *process = m_runtime.GetProcess();
+
+ std::unique_ptr<objc_class_t> objc_class;
+ std::unique_ptr<class_ro_t> class_ro;
+ std::unique_ptr<class_rw_t> class_rw;
+
+ if (!Read_objc_class(process, objc_class))
+ return false;
+ if (!Read_class_row(process, *objc_class, class_ro, class_rw))
+ return false;
+
+ static ConstString NSObject_name("NSObject");
+
+ if (m_name != NSObject_name && superclass_func)
+ superclass_func(objc_class->m_superclass);
+
+ if (instance_method_func) {
+ std::unique_ptr<method_list_t> base_method_list;
+
+ base_method_list.reset(new method_list_t);
+ if (!base_method_list->Read(process, class_ro->m_baseMethods_ptr))
+ return false;
+
+ if (base_method_list->m_entsize != method_t::GetSize(process))
+ return false;
+
+ std::unique_ptr<method_t> method;
+ method.reset(new method_t);
+
+ for (uint32_t i = 0, e = base_method_list->m_count; i < e; ++i) {
+ method->Read(process, base_method_list->m_first_ptr +
+ (i * base_method_list->m_entsize));
+
+ if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
+ break;
+ }
+ }
+
+ if (class_method_func) {
+ AppleObjCRuntime::ClassDescriptorSP metaclass(GetMetaclass());
+
+ // We don't care about the metaclass's superclass, or its class methods.
+ // Its instance methods are our class methods.
+
+ if (metaclass) {
+ metaclass->Describe(
+ std::function<void(ObjCLanguageRuntime::ObjCISA)>(nullptr),
+ class_method_func,
+ std::function<bool(const char *, const char *)>(nullptr),
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)>(nullptr));
+ }
+ }
+
+ if (ivar_func) {
+ if (class_ro->m_ivars_ptr != 0) {
+ ivar_list_t ivar_list;
+ if (!ivar_list.Read(process, class_ro->m_ivars_ptr))
+ return false;
+
+ if (ivar_list.m_entsize != ivar_t::GetSize(process))
+ return false;
+
+ ivar_t ivar;
+
+ for (uint32_t i = 0, e = ivar_list.m_count; i < e; ++i) {
+ ivar.Read(process, ivar_list.m_first_ptr + (i * ivar_list.m_entsize));
+
+ if (ivar_func(ivar.m_name.c_str(), ivar.m_type.c_str(),
+ ivar.m_offset_ptr, ivar.m_size))
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+ConstString ClassDescriptorV2::GetClassName() {
+ if (!m_name) {
+ lldb_private::Process *process = m_runtime.GetProcess();
+
+ if (process) {
+ std::unique_ptr<objc_class_t> objc_class;
+ std::unique_ptr<class_ro_t> class_ro;
+ std::unique_ptr<class_rw_t> class_rw;
+
+ if (!Read_objc_class(process, objc_class))
+ return m_name;
+ if (!Read_class_row(process, *objc_class, class_ro, class_rw))
+ return m_name;
+
+ m_name = ConstString(class_ro->m_name.c_str());
+ }
+ }
+ return m_name;
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetSuperclass() {
+ lldb_private::Process *process = m_runtime.GetProcess();
+
+ if (!process)
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ std::unique_ptr<objc_class_t> objc_class;
+
+ if (!Read_objc_class(process, objc_class))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ return m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA(
+ objc_class->m_superclass);
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetMetaclass() const {
+ lldb_private::Process *process = m_runtime.GetProcess();
+
+ if (!process)
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ std::unique_ptr<objc_class_t> objc_class;
+
+ if (!Read_objc_class(process, objc_class))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ lldb::addr_t candidate_isa = m_runtime.GetPointerISA(objc_class->m_isa);
+
+ return ObjCLanguageRuntime::ClassDescriptorSP(
+ new ClassDescriptorV2(m_runtime, candidate_isa, nullptr));
+}
+
+uint64_t ClassDescriptorV2::GetInstanceSize() {
+ lldb_private::Process *process = m_runtime.GetProcess();
+
+ if (process) {
+ std::unique_ptr<objc_class_t> objc_class;
+ std::unique_ptr<class_ro_t> class_ro;
+ std::unique_ptr<class_rw_t> class_rw;
+
+ if (!Read_objc_class(process, objc_class))
+ return 0;
+ if (!Read_class_row(process, *objc_class, class_ro, class_rw))
+ return 0;
+
+ return class_ro->m_instanceSize;
+ }
+
+ return 0;
+}
+
+ClassDescriptorV2::iVarsStorage::iVarsStorage()
+ : m_filled(false), m_ivars(), m_mutex() {}
+
+size_t ClassDescriptorV2::iVarsStorage::size() { return m_ivars.size(); }
+
+ClassDescriptorV2::iVarDescriptor &ClassDescriptorV2::iVarsStorage::
+operator[](size_t idx) {
+ return m_ivars[idx];
+}
+
+void ClassDescriptorV2::iVarsStorage::fill(AppleObjCRuntimeV2 &runtime,
+ ClassDescriptorV2 &descriptor) {
+ if (m_filled)
+ return;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
+ LLDB_LOGV(log, "class_name = {0}", descriptor.GetClassName());
+ m_filled = true;
+ ObjCLanguageRuntime::EncodingToTypeSP encoding_to_type_sp(
+ runtime.GetEncodingToType());
+ Process *process(runtime.GetProcess());
+ if (!encoding_to_type_sp)
+ return;
+ descriptor.Describe(nullptr, nullptr, nullptr, [this, process,
+ encoding_to_type_sp,
+ log](const char *name,
+ const char *type,
+ lldb::addr_t offset_ptr,
+ uint64_t size) -> bool {
+ const bool for_expression = false;
+ const bool stop_loop = false;
+ LLDB_LOGV(log, "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = {3}",
+ name, type, offset_ptr, size);
+ CompilerType ivar_type =
+ encoding_to_type_sp->RealizeType(type, for_expression);
+ if (ivar_type) {
+ LLDB_LOGV(log,
+ "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = "
+ "{3}, type_size = {4}",
+ name, type, offset_ptr, size,
+ ivar_type.GetByteSize(nullptr).getValueOr(0));
+ Scalar offset_scalar;
+ Status error;
+ const int offset_ptr_size = 4;
+ const bool is_signed = false;
+ size_t read = process->ReadScalarIntegerFromMemory(
+ offset_ptr, offset_ptr_size, is_signed, offset_scalar, error);
+ if (error.Success() && 4 == read) {
+ LLDB_LOGV(log, "offset_ptr = {0:x} --> {1}", offset_ptr,
+ offset_scalar.SInt());
+ m_ivars.push_back(
+ {ConstString(name), ivar_type, size, offset_scalar.SInt()});
+ } else
+ LLDB_LOGV(log, "offset_ptr = {0:x} --> read fail, read = %{1}",
+ offset_ptr, read);
+ }
+ return stop_loop;
+ });
+}
+
+void ClassDescriptorV2::GetIVarInformation() {
+ m_ivars_storage.fill(m_runtime, *this);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
new file mode 100644
index 000000000000..b8ba9dbb65f4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
@@ -0,0 +1,331 @@
+//===-- AppleObjCClassDescriptorV2.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_AppleObjCClassDescriptorV2_h_
+#define liblldb_AppleObjCClassDescriptorV2_h_
+
+#include <mutex>
+
+#include "AppleObjCRuntimeV2.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+
+class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor {
+public:
+ friend class lldb_private::AppleObjCRuntimeV2;
+
+ ~ClassDescriptorV2() override = default;
+
+ ConstString GetClassName() override;
+
+ ObjCLanguageRuntime::ClassDescriptorSP GetSuperclass() override;
+
+ ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override;
+
+ bool IsValid() override {
+ return true; // any Objective-C v2 runtime class descriptor we vend is valid
+ }
+
+ // a custom descriptor is used for tagged pointers
+ bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
+ uint64_t *value_bits = nullptr,
+ uint64_t *payload = nullptr) override {
+ return false;
+ }
+
+ uint64_t GetInstanceSize() override;
+
+ ObjCLanguageRuntime::ObjCISA GetISA() override { return m_objc_class_ptr; }
+
+ bool Describe(
+ std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
+ std::function<bool(const char *, const char *)> const
+ &instance_method_func,
+ std::function<bool(const char *, const char *)> const &class_method_func,
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)> const &ivar_func) const override;
+
+ size_t GetNumIVars() override {
+ GetIVarInformation();
+ return m_ivars_storage.size();
+ }
+
+ iVarDescriptor GetIVarAtIndex(size_t idx) override {
+ if (idx >= GetNumIVars())
+ return iVarDescriptor();
+ return m_ivars_storage[idx];
+ }
+
+protected:
+ void GetIVarInformation();
+
+private:
+ static const uint32_t RW_REALIZED = (1 << 31);
+
+ struct objc_class_t {
+ ObjCLanguageRuntime::ObjCISA m_isa; // The class's metaclass.
+ ObjCLanguageRuntime::ObjCISA m_superclass;
+ lldb::addr_t m_cache_ptr;
+ lldb::addr_t m_vtable_ptr;
+ lldb::addr_t m_data_ptr;
+ uint8_t m_flags;
+
+ objc_class_t()
+ : m_isa(0), m_superclass(0), m_cache_ptr(0), m_vtable_ptr(0),
+ m_data_ptr(0), m_flags(0) {}
+
+ void Clear() {
+ m_isa = 0;
+ m_superclass = 0;
+ m_cache_ptr = 0;
+ m_vtable_ptr = 0;
+ m_data_ptr = 0;
+ m_flags = 0;
+ }
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct class_ro_t {
+ uint32_t m_flags;
+ uint32_t m_instanceStart;
+ uint32_t m_instanceSize;
+ uint32_t m_reserved;
+
+ lldb::addr_t m_ivarLayout_ptr;
+ lldb::addr_t m_name_ptr;
+ lldb::addr_t m_baseMethods_ptr;
+ lldb::addr_t m_baseProtocols_ptr;
+ lldb::addr_t m_ivars_ptr;
+
+ lldb::addr_t m_weakIvarLayout_ptr;
+ lldb::addr_t m_baseProperties_ptr;
+
+ std::string m_name;
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct class_rw_t {
+ uint32_t m_flags;
+ uint32_t m_version;
+
+ lldb::addr_t m_ro_ptr;
+ union {
+ lldb::addr_t m_method_list_ptr;
+ lldb::addr_t m_method_lists_ptr;
+ };
+ lldb::addr_t m_properties_ptr;
+ lldb::addr_t m_protocols_ptr;
+
+ ObjCLanguageRuntime::ObjCISA m_firstSubclass;
+ ObjCLanguageRuntime::ObjCISA m_nextSiblingClass;
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct method_list_t {
+ uint32_t m_entsize;
+ uint32_t m_count;
+ lldb::addr_t m_first_ptr;
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct method_t {
+ lldb::addr_t m_name_ptr;
+ lldb::addr_t m_types_ptr;
+ lldb::addr_t m_imp_ptr;
+
+ std::string m_name;
+ std::string m_types;
+
+ static size_t GetSize(Process *process) {
+ size_t ptr_size = process->GetAddressByteSize();
+
+ return ptr_size // SEL name;
+ + ptr_size // const char *types;
+ + ptr_size; // IMP imp;
+ }
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct ivar_list_t {
+ uint32_t m_entsize;
+ uint32_t m_count;
+ lldb::addr_t m_first_ptr;
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct ivar_t {
+ lldb::addr_t m_offset_ptr;
+ lldb::addr_t m_name_ptr;
+ lldb::addr_t m_type_ptr;
+ uint32_t m_alignment;
+ uint32_t m_size;
+
+ std::string m_name;
+ std::string m_type;
+
+ static size_t GetSize(Process *process) {
+ size_t ptr_size = process->GetAddressByteSize();
+
+ return ptr_size // uintptr_t *offset;
+ + ptr_size // const char *name;
+ + ptr_size // const char *type;
+ + sizeof(uint32_t) // uint32_t alignment;
+ + sizeof(uint32_t); // uint32_t size;
+ }
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ class iVarsStorage {
+ public:
+ iVarsStorage();
+
+ size_t size();
+
+ iVarDescriptor &operator[](size_t idx);
+
+ void fill(AppleObjCRuntimeV2 &runtime, ClassDescriptorV2 &descriptor);
+
+ private:
+ bool m_filled;
+ std::vector<iVarDescriptor> m_ivars;
+ std::recursive_mutex m_mutex;
+ };
+
+ // The constructor should only be invoked by the runtime as it builds its
+ // caches
+ // or populates them. A ClassDescriptorV2 should only ever exist in a cache.
+ ClassDescriptorV2(AppleObjCRuntimeV2 &runtime,
+ ObjCLanguageRuntime::ObjCISA isa, const char *name)
+ : m_runtime(runtime), m_objc_class_ptr(isa), m_name(name),
+ m_ivars_storage() {}
+
+ bool Read_objc_class(Process *process,
+ std::unique_ptr<objc_class_t> &objc_class) const;
+
+ bool Read_class_row(Process *process, const objc_class_t &objc_class,
+ std::unique_ptr<class_ro_t> &class_ro,
+ std::unique_ptr<class_rw_t> &class_rw) const;
+
+ AppleObjCRuntimeV2
+ &m_runtime; // The runtime, so we can read information lazily.
+ lldb::addr_t m_objc_class_ptr; // The address of the objc_class_t. (I.e.,
+ // objects of this class type have this as
+ // their ISA)
+ ConstString m_name; // May be NULL
+ iVarsStorage m_ivars_storage;
+};
+
+// tagged pointer descriptor
+class ClassDescriptorV2Tagged : public ObjCLanguageRuntime::ClassDescriptor {
+public:
+ ClassDescriptorV2Tagged(ConstString class_name, uint64_t payload) {
+ m_name = class_name;
+ if (!m_name) {
+ m_valid = false;
+ return;
+ }
+ m_valid = true;
+ m_payload = payload;
+ m_info_bits = (m_payload & 0xF0ULL) >> 4;
+ m_value_bits = (m_payload & ~0x0000000000000000FFULL) >> 8;
+ }
+
+ ClassDescriptorV2Tagged(
+ ObjCLanguageRuntime::ClassDescriptorSP actual_class_sp,
+ uint64_t payload) {
+ if (!actual_class_sp) {
+ m_valid = false;
+ return;
+ }
+ m_name = actual_class_sp->GetClassName();
+ if (!m_name) {
+ m_valid = false;
+ return;
+ }
+ m_valid = true;
+ m_payload = payload;
+ m_info_bits = (m_payload & 0x0FULL);
+ m_value_bits = (m_payload & ~0x0FULL) >> 4;
+ }
+
+ ~ClassDescriptorV2Tagged() override = default;
+
+ ConstString GetClassName() override { return m_name; }
+
+ ObjCLanguageRuntime::ClassDescriptorSP GetSuperclass() override {
+ // tagged pointers can represent a class that has a superclass, but since
+ // that information is not
+ // stored in the object itself, we would have to query the runtime to
+ // discover the hierarchy
+ // for the time being, we skip this step in the interest of static discovery
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ }
+
+ ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override {
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ }
+
+ bool IsValid() override { return m_valid; }
+
+ bool IsKVO() override {
+ return false; // tagged pointers are not KVO'ed
+ }
+
+ bool IsCFType() override {
+ return false; // tagged pointers are not CF objects
+ }
+
+ bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
+ uint64_t *value_bits = nullptr,
+ uint64_t *payload = nullptr) override {
+ if (info_bits)
+ *info_bits = GetInfoBits();
+ if (value_bits)
+ *value_bits = GetValueBits();
+ if (payload)
+ *payload = GetPayload();
+ return true;
+ }
+
+ uint64_t GetInstanceSize() override {
+ return (IsValid() ? m_pointer_size : 0);
+ }
+
+ ObjCLanguageRuntime::ObjCISA GetISA() override {
+ return 0; // tagged pointers have no ISA
+ }
+
+ // these calls are not part of any formal tagged pointers specification
+ virtual uint64_t GetValueBits() { return (IsValid() ? m_value_bits : 0); }
+
+ virtual uint64_t GetInfoBits() { return (IsValid() ? m_info_bits : 0); }
+
+ virtual uint64_t GetPayload() { return (IsValid() ? m_payload : 0); }
+
+private:
+ ConstString m_name;
+ uint8_t m_pointer_size;
+ bool m_valid;
+ uint64_t m_info_bits;
+ uint64_t m_value_bits;
+ uint64_t m_payload;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCClassDescriptorV2_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp
new file mode 100644
index 000000000000..18f2a1829a41
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp
@@ -0,0 +1,662 @@
+//===-- AppleObjCDeclVendor.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AppleObjCDeclVendor.h"
+
+#include "Plugins/ExpressionParser/Clang/ASTDumper.h"
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclObjC.h"
+
+
+using namespace lldb_private;
+
+class lldb_private::AppleObjCExternalASTSource
+ : public ClangExternalASTSourceCommon {
+public:
+ AppleObjCExternalASTSource(AppleObjCDeclVendor &decl_vendor)
+ : m_decl_vendor(decl_vendor) {}
+
+ bool FindExternalVisibleDeclsByName(const clang::DeclContext *decl_ctx,
+ clang::DeclarationName name) override {
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(GetLogIfAllCategoriesSet(
+ LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
+
+ if (log) {
+ log->Printf("AppleObjCExternalASTSource::FindExternalVisibleDeclsByName[%"
+ "u] on (ASTContext*)%p Looking for %s in (%sDecl*)%p",
+ current_id,
+ static_cast<void *>(&decl_ctx->getParentASTContext()),
+ name.getAsString().c_str(), decl_ctx->getDeclKindName(),
+ static_cast<const void *>(decl_ctx));
+ }
+
+ do {
+ const clang::ObjCInterfaceDecl *interface_decl =
+ llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl_ctx);
+
+ if (!interface_decl)
+ break;
+
+ clang::ObjCInterfaceDecl *non_const_interface_decl =
+ const_cast<clang::ObjCInterfaceDecl *>(interface_decl);
+
+ if (!m_decl_vendor.FinishDecl(non_const_interface_decl))
+ break;
+
+ clang::DeclContext::lookup_result result =
+ non_const_interface_decl->lookup(name);
+
+ return (result.size() != 0);
+ } while (false);
+
+ SetNoExternalVisibleDeclsForName(decl_ctx, name);
+ return false;
+ }
+
+ void CompleteType(clang::TagDecl *tag_decl) override {
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(GetLogIfAllCategoriesSet(
+ LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
+
+ if (log) {
+ log->Printf("AppleObjCExternalASTSource::CompleteType[%u] on "
+ "(ASTContext*)%p Completing (TagDecl*)%p named %s",
+ current_id, static_cast<void *>(&tag_decl->getASTContext()),
+ static_cast<void *>(tag_decl),
+ tag_decl->getName().str().c_str());
+
+ log->Printf(" AOEAS::CT[%u] Before:", current_id);
+ ASTDumper dumper((clang::Decl *)tag_decl);
+ dumper.ToLog(log, " [CT] ");
+ }
+
+ if (log) {
+ log->Printf(" AOEAS::CT[%u] After:", current_id);
+ ASTDumper dumper((clang::Decl *)tag_decl);
+ dumper.ToLog(log, " [CT] ");
+ }
+ return;
+ }
+
+ void CompleteType(clang::ObjCInterfaceDecl *interface_decl) override {
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(GetLogIfAllCategoriesSet(
+ LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
+
+ if (log) {
+ log->Printf("AppleObjCExternalASTSource::CompleteType[%u] on "
+ "(ASTContext*)%p Completing (ObjCInterfaceDecl*)%p named %s",
+ current_id,
+ static_cast<void *>(&interface_decl->getASTContext()),
+ static_cast<void *>(interface_decl),
+ interface_decl->getName().str().c_str());
+
+ log->Printf(" AOEAS::CT[%u] Before:", current_id);
+ ASTDumper dumper((clang::Decl *)interface_decl);
+ dumper.ToLog(log, " [CT] ");
+ }
+
+ m_decl_vendor.FinishDecl(interface_decl);
+
+ if (log) {
+ log->Printf(" [CT] After:");
+ ASTDumper dumper((clang::Decl *)interface_decl);
+ dumper.ToLog(log, " [CT] ");
+ }
+ return;
+ }
+
+ bool layoutRecordType(
+ const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &BaseOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &VirtualBaseOffsets) override {
+ return false;
+ }
+
+ void StartTranslationUnit(clang::ASTConsumer *Consumer) override {
+ clang::TranslationUnitDecl *translation_unit_decl =
+ m_decl_vendor.m_ast_ctx.getASTContext()->getTranslationUnitDecl();
+ translation_unit_decl->setHasExternalVisibleStorage();
+ translation_unit_decl->setHasExternalLexicalStorage();
+ }
+
+private:
+ AppleObjCDeclVendor &m_decl_vendor;
+};
+
+AppleObjCDeclVendor::AppleObjCDeclVendor(ObjCLanguageRuntime &runtime)
+ : DeclVendor(), m_runtime(runtime), m_ast_ctx(runtime.GetProcess()
+ ->GetTarget()
+ .GetArchitecture()
+ .GetTriple()
+ .getTriple()
+ .c_str()),
+ m_type_realizer_sp(m_runtime.GetEncodingToType()) {
+ m_external_source = new AppleObjCExternalASTSource(*this);
+ llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> external_source_owning_ptr(
+ m_external_source);
+ m_ast_ctx.getASTContext()->setExternalSource(external_source_owning_ptr);
+}
+
+clang::ObjCInterfaceDecl *
+AppleObjCDeclVendor::GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa) {
+ ISAToInterfaceMap::const_iterator iter = m_isa_to_interface.find(isa);
+
+ if (iter != m_isa_to_interface.end())
+ return iter->second;
+
+ clang::ASTContext *ast_ctx = m_ast_ctx.getASTContext();
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor =
+ m_runtime.GetClassDescriptorFromISA(isa);
+
+ if (!descriptor)
+ return nullptr;
+
+ ConstString name(descriptor->GetClassName());
+
+ clang::IdentifierInfo &identifier_info =
+ ast_ctx->Idents.get(name.GetStringRef());
+
+ clang::ObjCInterfaceDecl *new_iface_decl = clang::ObjCInterfaceDecl::Create(
+ *ast_ctx, ast_ctx->getTranslationUnitDecl(), clang::SourceLocation(),
+ &identifier_info, nullptr, nullptr);
+
+ ClangASTMetadata meta_data;
+ meta_data.SetISAPtr(isa);
+ m_external_source->SetMetadata(new_iface_decl, meta_data);
+
+ new_iface_decl->setHasExternalVisibleStorage();
+ new_iface_decl->setHasExternalLexicalStorage();
+
+ ast_ctx->getTranslationUnitDecl()->addDecl(new_iface_decl);
+
+ m_isa_to_interface[isa] = new_iface_decl;
+
+ return new_iface_decl;
+}
+
+class ObjCRuntimeMethodType {
+public:
+ ObjCRuntimeMethodType(const char *types) : m_is_valid(false) {
+ const char *cursor = types;
+ enum ParserState { Start = 0, InType, InPos } state = Start;
+ const char *type = nullptr;
+ int brace_depth = 0;
+
+ uint32_t stepsLeft = 256;
+
+ while (true) {
+ if (--stepsLeft == 0) {
+ m_is_valid = false;
+ return;
+ }
+
+ switch (state) {
+ case Start: {
+ switch (*cursor) {
+ default:
+ state = InType;
+ type = cursor;
+ break;
+ case '\0':
+ m_is_valid = true;
+ return;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ m_is_valid = false;
+ return;
+ }
+ } break;
+ case InType: {
+ switch (*cursor) {
+ default:
+ ++cursor;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!brace_depth) {
+ state = InPos;
+ if (type) {
+ m_type_vector.push_back(std::string(type, (cursor - type)));
+ } else {
+ m_is_valid = false;
+ return;
+ }
+ type = nullptr;
+ } else {
+ ++cursor;
+ }
+ break;
+ case '[':
+ case '{':
+ case '(':
+ ++brace_depth;
+ ++cursor;
+ break;
+ case ']':
+ case '}':
+ case ')':
+ if (!brace_depth) {
+ m_is_valid = false;
+ return;
+ }
+ --brace_depth;
+ ++cursor;
+ break;
+ case '\0':
+ m_is_valid = false;
+ return;
+ }
+ } break;
+ case InPos: {
+ switch (*cursor) {
+ default:
+ state = InType;
+ type = cursor;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ++cursor;
+ break;
+ case '\0':
+ m_is_valid = true;
+ return;
+ }
+ } break;
+ }
+ }
+ }
+
+ clang::ObjCMethodDecl *
+ BuildMethod(clang::ObjCInterfaceDecl *interface_decl, const char *name,
+ bool instance,
+ ObjCLanguageRuntime::EncodingToTypeSP type_realizer_sp) {
+ if (!m_is_valid || m_type_vector.size() < 3)
+ return nullptr;
+
+ clang::ASTContext &ast_ctx(interface_decl->getASTContext());
+
+ const bool isInstance = instance;
+ const bool isVariadic = false;
+ const bool isSynthesized = false;
+ const bool isImplicitlyDeclared = true;
+ const bool isDefined = false;
+ const clang::ObjCMethodDecl::ImplementationControl impControl =
+ clang::ObjCMethodDecl::None;
+ const bool HasRelatedResultType = false;
+ const bool for_expression = true;
+
+ std::vector<clang::IdentifierInfo *> selector_components;
+
+ const char *name_cursor = name;
+ bool is_zero_argument = true;
+
+ while (*name_cursor != '\0') {
+ const char *colon_loc = strchr(name_cursor, ':');
+ if (!colon_loc) {
+ selector_components.push_back(
+ &ast_ctx.Idents.get(llvm::StringRef(name_cursor)));
+ break;
+ } else {
+ is_zero_argument = false;
+ selector_components.push_back(&ast_ctx.Idents.get(
+ llvm::StringRef(name_cursor, colon_loc - name_cursor)));
+ name_cursor = colon_loc + 1;
+ }
+ }
+
+ clang::IdentifierInfo **identifier_infos = selector_components.data();
+ if (!identifier_infos) {
+ return nullptr;
+ }
+
+ clang::Selector sel = ast_ctx.Selectors.getSelector(
+ is_zero_argument ? 0 : selector_components.size(),
+ identifier_infos);
+
+ clang::QualType ret_type =
+ ClangUtil::GetQualType(type_realizer_sp->RealizeType(
+ interface_decl->getASTContext(), m_type_vector[0].c_str(),
+ for_expression));
+
+ if (ret_type.isNull())
+ return nullptr;
+
+ clang::ObjCMethodDecl *ret = clang::ObjCMethodDecl::Create(
+ ast_ctx, clang::SourceLocation(), clang::SourceLocation(), sel,
+ ret_type, nullptr, interface_decl, isInstance, isVariadic,
+ isSynthesized, isImplicitlyDeclared, isDefined, impControl,
+ HasRelatedResultType);
+
+ std::vector<clang::ParmVarDecl *> parm_vars;
+
+ for (size_t ai = 3, ae = m_type_vector.size(); ai != ae; ++ai) {
+ const bool for_expression = true;
+ clang::QualType arg_type =
+ ClangUtil::GetQualType(type_realizer_sp->RealizeType(
+ ast_ctx, m_type_vector[ai].c_str(), for_expression));
+
+ if (arg_type.isNull())
+ return nullptr; // well, we just wasted a bunch of time. Wish we could
+ // delete the stuff we'd just made!
+
+ parm_vars.push_back(clang::ParmVarDecl::Create(
+ ast_ctx, ret, clang::SourceLocation(), clang::SourceLocation(),
+ nullptr, arg_type, nullptr, clang::SC_None, nullptr));
+ }
+
+ ret->setMethodParams(ast_ctx,
+ llvm::ArrayRef<clang::ParmVarDecl *>(parm_vars),
+ llvm::ArrayRef<clang::SourceLocation>());
+
+ return ret;
+ }
+
+ explicit operator bool() { return m_is_valid; }
+
+ size_t GetNumTypes() { return m_type_vector.size(); }
+
+ const char *GetTypeAtIndex(size_t idx) { return m_type_vector[idx].c_str(); }
+
+private:
+ typedef std::vector<std::string> TypeVector;
+
+ TypeVector m_type_vector;
+ bool m_is_valid;
+};
+
+bool AppleObjCDeclVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl) {
+ Log *log(GetLogIfAllCategoriesSet(
+ LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
+
+ ClangASTMetadata *metadata = m_external_source->GetMetadata(interface_decl);
+ ObjCLanguageRuntime::ObjCISA objc_isa = 0;
+ if (metadata)
+ objc_isa = metadata->GetISAPtr();
+
+ if (!objc_isa)
+ return false;
+
+ if (!interface_decl->hasExternalVisibleStorage())
+ return true;
+
+ interface_decl->startDefinition();
+
+ interface_decl->setHasExternalVisibleStorage(false);
+ interface_decl->setHasExternalLexicalStorage(false);
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor =
+ m_runtime.GetClassDescriptorFromISA(objc_isa);
+
+ if (!descriptor)
+ return false;
+
+ auto superclass_func = [interface_decl,
+ this](ObjCLanguageRuntime::ObjCISA isa) {
+ clang::ObjCInterfaceDecl *superclass_decl = GetDeclForISA(isa);
+
+ if (!superclass_decl)
+ return;
+
+ FinishDecl(superclass_decl);
+ clang::ASTContext *context = m_ast_ctx.getASTContext();
+ interface_decl->setSuperClass(context->getTrivialTypeSourceInfo(
+ context->getObjCInterfaceType(superclass_decl)));
+ };
+
+ auto instance_method_func =
+ [log, interface_decl, this](const char *name, const char *types) -> bool {
+ if (!name || !types)
+ return false; // skip this one
+
+ ObjCRuntimeMethodType method_type(types);
+
+ clang::ObjCMethodDecl *method_decl =
+ method_type.BuildMethod(interface_decl, name, true, m_type_realizer_sp);
+
+ if (log)
+ log->Printf("[ AOTV::FD] Instance method [%s] [%s]", name, types);
+
+ if (method_decl)
+ interface_decl->addDecl(method_decl);
+
+ return false;
+ };
+
+ auto class_method_func = [log, interface_decl,
+ this](const char *name, const char *types) -> bool {
+ if (!name || !types)
+ return false; // skip this one
+
+ ObjCRuntimeMethodType method_type(types);
+
+ clang::ObjCMethodDecl *method_decl = method_type.BuildMethod(
+ interface_decl, name, false, m_type_realizer_sp);
+
+ if (log)
+ log->Printf("[ AOTV::FD] Class method [%s] [%s]", name, types);
+
+ if (method_decl)
+ interface_decl->addDecl(method_decl);
+
+ return false;
+ };
+
+ auto ivar_func = [log, interface_decl,
+ this](const char *name, const char *type,
+ lldb::addr_t offset_ptr, uint64_t size) -> bool {
+ if (!name || !type)
+ return false;
+
+ const bool for_expression = false;
+
+ if (log)
+ log->Printf(
+ "[ AOTV::FD] Instance variable [%s] [%s], offset at %" PRIx64, name,
+ type, offset_ptr);
+
+ CompilerType ivar_type = m_runtime.GetEncodingToType()->RealizeType(
+ m_ast_ctx, type, for_expression);
+
+ if (ivar_type.IsValid()) {
+ clang::TypeSourceInfo *const type_source_info = nullptr;
+ const bool is_synthesized = false;
+ clang::ObjCIvarDecl *ivar_decl = clang::ObjCIvarDecl::Create(
+ *m_ast_ctx.getASTContext(), interface_decl, clang::SourceLocation(),
+ clang::SourceLocation(), &m_ast_ctx.getASTContext()->Idents.get(name),
+ ClangUtil::GetQualType(ivar_type),
+ type_source_info, // TypeSourceInfo *
+ clang::ObjCIvarDecl::Public, nullptr, is_synthesized);
+
+ if (ivar_decl) {
+ interface_decl->addDecl(ivar_decl);
+ }
+ }
+
+ return false;
+ };
+
+ if (log) {
+ ASTDumper method_dumper((clang::Decl *)interface_decl);
+
+ log->Printf("[AppleObjCDeclVendor::FinishDecl] Finishing Objective-C "
+ "interface for %s",
+ descriptor->GetClassName().AsCString());
+ }
+
+ if (!descriptor->Describe(superclass_func, instance_method_func,
+ class_method_func, ivar_func))
+ return false;
+
+ if (log) {
+ ASTDumper method_dumper((clang::Decl *)interface_decl);
+
+ log->Printf(
+ "[AppleObjCDeclVendor::FinishDecl] Finished Objective-C interface");
+
+ method_dumper.ToLog(log, " [AOTV::FD] ");
+ }
+
+ return true;
+}
+
+uint32_t
+AppleObjCDeclVendor::FindDecls(ConstString name, bool append,
+ uint32_t max_matches,
+ std::vector<clang::NamedDecl *> &decls) {
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(GetLogIfAllCategoriesSet(
+ LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
+
+ if (log)
+ log->Printf("AppleObjCDeclVendor::FindDecls [%u] ('%s', %s, %u, )",
+ current_id, (const char *)name.AsCString(),
+ append ? "true" : "false", max_matches);
+
+ if (!append)
+ decls.clear();
+
+ uint32_t ret = 0;
+
+ do {
+ // See if the type is already in our ASTContext.
+
+ clang::ASTContext *ast_ctx = m_ast_ctx.getASTContext();
+
+ clang::IdentifierInfo &identifier_info =
+ ast_ctx->Idents.get(name.GetStringRef());
+ clang::DeclarationName decl_name =
+ ast_ctx->DeclarationNames.getIdentifier(&identifier_info);
+
+ clang::DeclContext::lookup_result lookup_result =
+ ast_ctx->getTranslationUnitDecl()->lookup(decl_name);
+
+ if (!lookup_result.empty()) {
+ if (clang::ObjCInterfaceDecl *result_iface_decl =
+ llvm::dyn_cast<clang::ObjCInterfaceDecl>(lookup_result[0])) {
+ if (log) {
+ clang::QualType result_iface_type =
+ ast_ctx->getObjCInterfaceType(result_iface_decl);
+ ASTDumper dumper(result_iface_type);
+
+ uint64_t isa_value = LLDB_INVALID_ADDRESS;
+ ClangASTMetadata *metadata =
+ m_external_source->GetMetadata(result_iface_decl);
+ if (metadata)
+ isa_value = metadata->GetISAPtr();
+
+ log->Printf("AOCTV::FT [%u] Found %s (isa 0x%" PRIx64
+ ") in the ASTContext",
+ current_id, dumper.GetCString(), isa_value);
+ }
+
+ decls.push_back(result_iface_decl);
+ ret++;
+ break;
+ } else {
+ if (log)
+ log->Printf("AOCTV::FT [%u] There's something in the ASTContext, but "
+ "it's not something we know about",
+ current_id);
+ break;
+ }
+ } else if (log) {
+ log->Printf("AOCTV::FT [%u] Couldn't find %s in the ASTContext",
+ current_id, name.AsCString());
+ }
+
+ // It's not. If it exists, we have to put it into our ASTContext.
+
+ ObjCLanguageRuntime::ObjCISA isa = m_runtime.GetISA(name);
+
+ if (!isa) {
+ if (log)
+ log->Printf("AOCTV::FT [%u] Couldn't find the isa", current_id);
+
+ break;
+ }
+
+ clang::ObjCInterfaceDecl *iface_decl = GetDeclForISA(isa);
+
+ if (!iface_decl) {
+ if (log)
+ log->Printf("AOCTV::FT [%u] Couldn't get the Objective-C interface for "
+ "isa 0x%" PRIx64,
+ current_id, (uint64_t)isa);
+
+ break;
+ }
+
+ if (log) {
+ clang::QualType new_iface_type =
+ ast_ctx->getObjCInterfaceType(iface_decl);
+ ASTDumper dumper(new_iface_type);
+ log->Printf("AOCTV::FT [%u] Created %s (isa 0x%" PRIx64 ")", current_id,
+ dumper.GetCString(), (uint64_t)isa);
+ }
+
+ decls.push_back(iface_decl);
+ ret++;
+ break;
+ } while (false);
+
+ return ret;
+}
+
+clang::ExternalASTMerger::ImporterSource
+AppleObjCDeclVendor::GetImporterSource() {
+ return {*m_ast_ctx.getASTContext(),
+ *m_ast_ctx.getFileManager(),
+ m_ast_ctx.GetOriginMap()
+ };
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h
new file mode 100644
index 000000000000..77b30b7fde79
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h
@@ -0,0 +1,51 @@
+//===-- AppleObjCDeclVendor.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_AppleObjCDeclVendor_h_
+#define liblldb_AppleObjCDeclVendor_h_
+
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/DeclVendor.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+
+class AppleObjCExternalASTSource;
+
+class AppleObjCDeclVendor : public DeclVendor {
+public:
+ AppleObjCDeclVendor(ObjCLanguageRuntime &runtime);
+
+ uint32_t FindDecls(ConstString name, bool append, uint32_t max_matches,
+ std::vector<clang::NamedDecl *> &decls) override;
+
+ clang::ExternalASTMerger::ImporterSource GetImporterSource() override;
+
+ friend class AppleObjCExternalASTSource;
+
+private:
+ clang::ObjCInterfaceDecl *GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa);
+ bool FinishDecl(clang::ObjCInterfaceDecl *decl);
+
+ ObjCLanguageRuntime &m_runtime;
+ ClangASTContext m_ast_ctx;
+ ObjCLanguageRuntime::EncodingToTypeSP m_type_realizer_sp;
+ AppleObjCExternalASTSource *m_external_source;
+
+ typedef llvm::DenseMap<ObjCLanguageRuntime::ObjCISA,
+ clang::ObjCInterfaceDecl *>
+ ISAToInterfaceMap;
+
+ ISAToInterfaceMap m_isa_to_interface;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCDeclVendor_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
new file mode 100644
index 000000000000..52ed3628520f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
@@ -0,0 +1,583 @@
+//===-- AppleObjCRuntime.cpp -------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AppleObjCRuntime.h"
+#include "AppleObjCTrampolineHandler.h"
+
+#include "clang/AST/Type.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "Plugins/Process/Utility/HistoryThread.h"
+#include "Plugins/Language/ObjC/NSString.h"
+#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
+
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+char AppleObjCRuntime::ID = 0;
+
+AppleObjCRuntime::~AppleObjCRuntime() {}
+
+AppleObjCRuntime::AppleObjCRuntime(Process *process)
+ : ObjCLanguageRuntime(process), m_read_objc_library(false),
+ m_objc_trampoline_handler_up(), m_Foundation_major() {
+ ReadObjCLibraryIfNeeded(process->GetTarget().GetImages());
+}
+
+bool AppleObjCRuntime::GetObjectDescription(Stream &str, ValueObject &valobj) {
+ CompilerType compiler_type(valobj.GetCompilerType());
+ bool is_signed;
+ // ObjC objects can only be pointers (or numbers that actually represents
+ // pointers but haven't been typecast, because reasons..)
+ if (!compiler_type.IsIntegerType(is_signed) && !compiler_type.IsPointerType())
+ return false;
+
+ // Make the argument list: we pass one arg, the address of our pointer, to
+ // the print function.
+ Value val;
+
+ if (!valobj.ResolveValue(val.GetScalar()))
+ return false;
+
+ // Value Objects may not have a process in their ExecutionContextRef. But we
+ // need to have one in the ref we pass down to eventually call description.
+ // Get it from the target if it isn't present.
+ ExecutionContext exe_ctx;
+ if (valobj.GetProcessSP()) {
+ exe_ctx = ExecutionContext(valobj.GetExecutionContextRef());
+ } else {
+ exe_ctx.SetContext(valobj.GetTargetSP(), true);
+ if (!exe_ctx.HasProcessScope())
+ return false;
+ }
+ return GetObjectDescription(str, val, exe_ctx.GetBestExecutionContextScope());
+}
+bool AppleObjCRuntime::GetObjectDescription(Stream &strm, Value &value,
+ ExecutionContextScope *exe_scope) {
+ if (!m_read_objc_library)
+ return false;
+
+ ExecutionContext exe_ctx;
+ exe_scope->CalculateExecutionContext(exe_ctx);
+ Process *process = exe_ctx.GetProcessPtr();
+ if (!process)
+ return false;
+
+ // We need other parts of the exe_ctx, but the processes have to match.
+ assert(m_process == process);
+
+ // Get the function address for the print function.
+ const Address *function_address = GetPrintForDebuggerAddr();
+ if (!function_address)
+ return false;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ CompilerType compiler_type = value.GetCompilerType();
+ if (compiler_type) {
+ if (!ClangASTContext::IsObjCObjectPointerType(compiler_type)) {
+ strm.Printf("Value doesn't point to an ObjC object.\n");
+ return false;
+ }
+ } else {
+ // If it is not a pointer, see if we can make it into a pointer.
+ ClangASTContext *ast_context = target->GetScratchClangASTContext();
+ CompilerType opaque_type = ast_context->GetBasicType(eBasicTypeObjCID);
+ if (!opaque_type)
+ opaque_type = ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ // value.SetContext(Value::eContextTypeClangType, opaque_type_ptr);
+ value.SetCompilerType(opaque_type);
+ }
+
+ ValueList arg_value_list;
+ arg_value_list.PushValue(value);
+
+ // This is the return value:
+ ClangASTContext *ast_context = target->GetScratchClangASTContext();
+
+ CompilerType return_compiler_type = ast_context->GetCStringType(true);
+ Value ret;
+ // ret.SetContext(Value::eContextTypeClangType, return_compiler_type);
+ ret.SetCompilerType(return_compiler_type);
+
+ if (exe_ctx.GetFramePtr() == nullptr) {
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (thread == nullptr) {
+ exe_ctx.SetThreadSP(process->GetThreadList().GetSelectedThread());
+ thread = exe_ctx.GetThreadPtr();
+ }
+ if (thread) {
+ exe_ctx.SetFrameSP(thread->GetSelectedFrame());
+ }
+ }
+
+ // Now we're ready to call the function:
+
+ DiagnosticManager diagnostics;
+ lldb::addr_t wrapper_struct_addr = LLDB_INVALID_ADDRESS;
+
+ if (!m_print_object_caller_up) {
+ Status error;
+ m_print_object_caller_up.reset(
+ exe_scope->CalculateTarget()->GetFunctionCallerForLanguage(
+ eLanguageTypeObjC, return_compiler_type, *function_address,
+ arg_value_list, "objc-object-description", error));
+ if (error.Fail()) {
+ m_print_object_caller_up.reset();
+ strm.Printf("Could not get function runner to call print for debugger "
+ "function: %s.",
+ error.AsCString());
+ return false;
+ }
+ m_print_object_caller_up->InsertFunction(exe_ctx, wrapper_struct_addr,
+ diagnostics);
+ } else {
+ m_print_object_caller_up->WriteFunctionArguments(
+ exe_ctx, wrapper_struct_addr, arg_value_list, diagnostics);
+ }
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(true);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetIsForUtilityExpr(true);
+
+ ExpressionResults results = m_print_object_caller_up->ExecuteFunction(
+ exe_ctx, &wrapper_struct_addr, options, diagnostics, ret);
+ if (results != eExpressionCompleted) {
+ strm.Printf("Error evaluating Print Object function: %d.\n", results);
+ return false;
+ }
+
+ addr_t result_ptr = ret.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+
+ char buf[512];
+ size_t cstr_len = 0;
+ size_t full_buffer_len = sizeof(buf) - 1;
+ size_t curr_len = full_buffer_len;
+ while (curr_len == full_buffer_len) {
+ Status error;
+ curr_len = process->ReadCStringFromMemory(result_ptr + cstr_len, buf,
+ sizeof(buf), error);
+ strm.Write(buf, curr_len);
+ cstr_len += curr_len;
+ }
+ return cstr_len > 0;
+}
+
+lldb::ModuleSP AppleObjCRuntime::GetObjCModule() {
+ ModuleSP module_sp(m_objc_module_wp.lock());
+ if (module_sp)
+ return module_sp;
+
+ Process *process = GetProcess();
+ if (process) {
+ const ModuleList &modules = process->GetTarget().GetImages();
+ for (uint32_t idx = 0; idx < modules.GetSize(); idx++) {
+ module_sp = modules.GetModuleAtIndex(idx);
+ if (AppleObjCRuntime::AppleIsModuleObjCLibrary(module_sp)) {
+ m_objc_module_wp = module_sp;
+ return module_sp;
+ }
+ }
+ }
+ return ModuleSP();
+}
+
+Address *AppleObjCRuntime::GetPrintForDebuggerAddr() {
+ if (!m_PrintForDebugger_addr) {
+ const ModuleList &modules = m_process->GetTarget().GetImages();
+
+ SymbolContextList contexts;
+ SymbolContext context;
+
+ if ((!modules.FindSymbolsWithNameAndType(ConstString("_NSPrintForDebugger"),
+ eSymbolTypeCode, contexts)) &&
+ (!modules.FindSymbolsWithNameAndType(ConstString("_CFPrintForDebugger"),
+ eSymbolTypeCode, contexts)))
+ return nullptr;
+
+ contexts.GetContextAtIndex(0, context);
+
+ m_PrintForDebugger_addr.reset(new Address(context.symbol->GetAddress()));
+ }
+
+ return m_PrintForDebugger_addr.get();
+}
+
+bool AppleObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
+ return in_value.GetCompilerType().IsPossibleDynamicType(
+ nullptr,
+ false, // do not check C++
+ true); // check ObjC
+}
+
+bool AppleObjCRuntime::GetDynamicTypeAndAddress(
+ ValueObject &in_value, lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &address,
+ Value::ValueType &value_type) {
+ return false;
+}
+
+TypeAndOrName
+AppleObjCRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) {
+ CompilerType static_type(static_value.GetCompilerType());
+ Flags static_type_flags(static_type.GetTypeInfo());
+
+ TypeAndOrName ret(type_and_or_name);
+ if (type_and_or_name.HasType()) {
+ // The type will always be the type of the dynamic object. If our parent's
+ // type was a pointer, then our type should be a pointer to the type of the
+ // dynamic object. If a reference, then the original type should be
+ // okay...
+ CompilerType orig_type = type_and_or_name.GetCompilerType();
+ CompilerType corrected_type = orig_type;
+ if (static_type_flags.AllSet(eTypeIsPointer))
+ corrected_type = orig_type.GetPointerType();
+ ret.SetCompilerType(corrected_type);
+ } else {
+ // If we are here we need to adjust our dynamic type name to include the
+ // correct & or * symbol
+ std::string corrected_name(type_and_or_name.GetName().GetCString());
+ if (static_type_flags.AllSet(eTypeIsPointer))
+ corrected_name.append(" *");
+ // the parent type should be a correctly pointer'ed or referenc'ed type
+ ret.SetCompilerType(static_type);
+ ret.SetName(corrected_name.c_str());
+ }
+ return ret;
+}
+
+bool AppleObjCRuntime::AppleIsModuleObjCLibrary(const ModuleSP &module_sp) {
+ if (module_sp) {
+ const FileSpec &module_file_spec = module_sp->GetFileSpec();
+ static ConstString ObjCName("libobjc.A.dylib");
+
+ if (module_file_spec) {
+ if (module_file_spec.GetFilename() == ObjCName)
+ return true;
+ }
+ }
+ return false;
+}
+
+// we use the version of Foundation to make assumptions about the ObjC runtime
+// on a target
+uint32_t AppleObjCRuntime::GetFoundationVersion() {
+ if (!m_Foundation_major.hasValue()) {
+ const ModuleList &modules = m_process->GetTarget().GetImages();
+ for (uint32_t idx = 0; idx < modules.GetSize(); idx++) {
+ lldb::ModuleSP module_sp = modules.GetModuleAtIndex(idx);
+ if (!module_sp)
+ continue;
+ if (strcmp(module_sp->GetFileSpec().GetFilename().AsCString(""),
+ "Foundation") == 0) {
+ m_Foundation_major = module_sp->GetVersion().getMajor();
+ return *m_Foundation_major;
+ }
+ }
+ return LLDB_INVALID_MODULE_VERSION;
+ } else
+ return m_Foundation_major.getValue();
+}
+
+void AppleObjCRuntime::GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
+ lldb::addr_t &cf_false) {
+ cf_true = cf_false = LLDB_INVALID_ADDRESS;
+}
+
+bool AppleObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) {
+ return AppleIsModuleObjCLibrary(module_sp);
+}
+
+bool AppleObjCRuntime::ReadObjCLibrary(const ModuleSP &module_sp) {
+ // Maybe check here and if we have a handler already, and the UUID of this
+ // module is the same as the one in the current module, then we don't have to
+ // reread it?
+ m_objc_trampoline_handler_up.reset(
+ new AppleObjCTrampolineHandler(m_process->shared_from_this(), module_sp));
+ if (m_objc_trampoline_handler_up != nullptr) {
+ m_read_objc_library = true;
+ return true;
+ } else
+ return false;
+}
+
+ThreadPlanSP AppleObjCRuntime::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) {
+ ThreadPlanSP thread_plan_sp;
+ if (m_objc_trampoline_handler_up)
+ thread_plan_sp = m_objc_trampoline_handler_up->GetStepThroughDispatchPlan(
+ thread, stop_others);
+ return thread_plan_sp;
+}
+
+// Static Functions
+ObjCLanguageRuntime::ObjCRuntimeVersions
+AppleObjCRuntime::GetObjCVersion(Process *process, ModuleSP &objc_module_sp) {
+ if (!process)
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+
+ Target &target = process->GetTarget();
+ if (target.GetArchitecture().GetTriple().getVendor() !=
+ llvm::Triple::VendorType::Apple)
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+
+ const ModuleList &target_modules = target.GetImages();
+ std::lock_guard<std::recursive_mutex> gaurd(target_modules.GetMutex());
+
+ size_t num_images = target_modules.GetSize();
+ for (size_t i = 0; i < num_images; i++) {
+ ModuleSP module_sp = target_modules.GetModuleAtIndexUnlocked(i);
+ // One tricky bit here is that we might get called as part of the initial
+ // module loading, but before all the pre-run libraries get winnowed from
+ // the module list. So there might actually be an old and incorrect ObjC
+ // library sitting around in the list, and we don't want to look at that.
+ // That's why we call IsLoadedInTarget.
+
+ if (AppleIsModuleObjCLibrary(module_sp) &&
+ module_sp->IsLoadedInTarget(&target)) {
+ objc_module_sp = module_sp;
+ ObjectFile *ofile = module_sp->GetObjectFile();
+ if (!ofile)
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+
+ SectionList *sections = module_sp->GetSectionList();
+ if (!sections)
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+ SectionSP v1_telltale_section_sp =
+ sections->FindSectionByName(ConstString("__OBJC"));
+ if (v1_telltale_section_sp) {
+ return ObjCRuntimeVersions::eAppleObjC_V1;
+ }
+ return ObjCRuntimeVersions::eAppleObjC_V2;
+ }
+ }
+
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+}
+
+void AppleObjCRuntime::SetExceptionBreakpoints() {
+ const bool catch_bp = false;
+ const bool throw_bp = true;
+ const bool is_internal = true;
+
+ if (!m_objc_exception_bp_sp) {
+ m_objc_exception_bp_sp = LanguageRuntime::CreateExceptionBreakpoint(
+ m_process->GetTarget(), GetLanguageType(), catch_bp, throw_bp,
+ is_internal);
+ if (m_objc_exception_bp_sp)
+ m_objc_exception_bp_sp->SetBreakpointKind("ObjC exception");
+ } else
+ m_objc_exception_bp_sp->SetEnabled(true);
+}
+
+void AppleObjCRuntime::ClearExceptionBreakpoints() {
+ if (!m_process)
+ return;
+
+ if (m_objc_exception_bp_sp.get()) {
+ m_objc_exception_bp_sp->SetEnabled(false);
+ }
+}
+
+bool AppleObjCRuntime::ExceptionBreakpointsAreSet() {
+ return m_objc_exception_bp_sp && m_objc_exception_bp_sp->IsEnabled();
+}
+
+bool AppleObjCRuntime::ExceptionBreakpointsExplainStop(
+ lldb::StopInfoSP stop_reason) {
+ if (!m_process)
+ return false;
+
+ if (!stop_reason || stop_reason->GetStopReason() != eStopReasonBreakpoint)
+ return false;
+
+ uint64_t break_site_id = stop_reason->GetValue();
+ return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint(
+ break_site_id, m_objc_exception_bp_sp->GetID());
+}
+
+bool AppleObjCRuntime::CalculateHasNewLiteralsAndIndexing() {
+ if (!m_process)
+ return false;
+
+ Target &target(m_process->GetTarget());
+
+ static ConstString s_method_signature(
+ "-[NSDictionary objectForKeyedSubscript:]");
+ static ConstString s_arclite_method_signature(
+ "__arclite_objectForKeyedSubscript");
+
+ SymbolContextList sc_list;
+
+ return target.GetImages().FindSymbolsWithNameAndType(
+ s_method_signature, eSymbolTypeCode, sc_list) ||
+ target.GetImages().FindSymbolsWithNameAndType(
+ s_arclite_method_signature, eSymbolTypeCode, sc_list);
+}
+
+lldb::SearchFilterSP AppleObjCRuntime::CreateExceptionSearchFilter() {
+ Target &target = m_process->GetTarget();
+
+ FileSpecList filter_modules;
+ if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) {
+ filter_modules.Append(std::get<0>(GetExceptionThrowLocation()));
+ }
+ return target.GetSearchFilterForModuleList(&filter_modules);
+}
+
+ValueObjectSP AppleObjCRuntime::GetExceptionObjectForThread(
+ ThreadSP thread_sp) {
+ auto *cpp_runtime = m_process->GetLanguageRuntime(eLanguageTypeC_plus_plus);
+ if (!cpp_runtime) return ValueObjectSP();
+ auto cpp_exception = cpp_runtime->GetExceptionObjectForThread(thread_sp);
+ if (!cpp_exception) return ValueObjectSP();
+
+ auto descriptor = GetClassDescriptor(*cpp_exception);
+ if (!descriptor || !descriptor->IsValid()) return ValueObjectSP();
+
+ while (descriptor) {
+ ConstString class_name(descriptor->GetClassName());
+ if (class_name == "NSException")
+ return cpp_exception;
+ descriptor = descriptor->GetSuperclass();
+ }
+
+ return ValueObjectSP();
+}
+
+ThreadSP AppleObjCRuntime::GetBacktraceThreadFromException(
+ lldb::ValueObjectSP exception_sp) {
+ ValueObjectSP reserved_dict =
+ exception_sp->GetChildMemberWithName(ConstString("reserved"), true);
+ if (!reserved_dict) return ThreadSP();
+
+ reserved_dict = reserved_dict->GetSyntheticValue();
+ if (!reserved_dict) return ThreadSP();
+
+ CompilerType objc_id =
+ exception_sp->GetTargetSP()->GetScratchClangASTContext()->GetBasicType(
+ lldb::eBasicTypeObjCID);
+ ValueObjectSP return_addresses;
+
+ auto objc_object_from_address = [&exception_sp, &objc_id](uint64_t addr,
+ const char *name) {
+ Value value(addr);
+ value.SetCompilerType(objc_id);
+ auto object = ValueObjectConstResult::Create(
+ exception_sp->GetTargetSP().get(), value, ConstString(name));
+ object = object->GetDynamicValue(eDynamicDontRunTarget);
+ return object;
+ };
+
+ for (size_t idx = 0; idx < reserved_dict->GetNumChildren(); idx++) {
+ ValueObjectSP dict_entry = reserved_dict->GetChildAtIndex(idx, true);
+
+ DataExtractor data;
+ data.SetAddressByteSize(dict_entry->GetProcessSP()->GetAddressByteSize());
+ Status error;
+ dict_entry->GetData(data, error);
+ if (error.Fail()) return ThreadSP();
+
+ lldb::offset_t data_offset = 0;
+ auto dict_entry_key = data.GetPointer(&data_offset);
+ auto dict_entry_value = data.GetPointer(&data_offset);
+
+ auto key_nsstring = objc_object_from_address(dict_entry_key, "key");
+ StreamString key_summary;
+ if (lldb_private::formatters::NSStringSummaryProvider(
+ *key_nsstring, key_summary, TypeSummaryOptions()) &&
+ !key_summary.Empty()) {
+ if (key_summary.GetString() == "\"callStackReturnAddresses\"") {
+ return_addresses = objc_object_from_address(dict_entry_value,
+ "callStackReturnAddresses");
+ break;
+ }
+ }
+ }
+
+ if (!return_addresses) return ThreadSP();
+ auto frames_value =
+ return_addresses->GetChildMemberWithName(ConstString("_frames"), true);
+ addr_t frames_addr = frames_value->GetValueAsUnsigned(0);
+ auto count_value =
+ return_addresses->GetChildMemberWithName(ConstString("_cnt"), true);
+ size_t count = count_value->GetValueAsUnsigned(0);
+ auto ignore_value =
+ return_addresses->GetChildMemberWithName(ConstString("_ignore"), true);
+ size_t ignore = ignore_value->GetValueAsUnsigned(0);
+
+ size_t ptr_size = m_process->GetAddressByteSize();
+ std::vector<lldb::addr_t> pcs;
+ for (size_t idx = 0; idx < count; idx++) {
+ Status error;
+ addr_t pc = m_process->ReadPointerFromMemory(
+ frames_addr + (ignore + idx) * ptr_size, error);
+ pcs.push_back(pc);
+ }
+
+ if (pcs.empty()) return ThreadSP();
+
+ ThreadSP new_thread_sp(new HistoryThread(*m_process, 0, pcs));
+ m_process->GetExtendedThreadList().AddThread(new_thread_sp);
+ return new_thread_sp;
+}
+
+std::tuple<FileSpec, ConstString>
+AppleObjCRuntime::GetExceptionThrowLocation() {
+ return std::make_tuple(
+ FileSpec("libobjc.A.dylib"), ConstString("objc_exception_throw"));
+}
+
+void AppleObjCRuntime::ReadObjCLibraryIfNeeded(const ModuleList &module_list) {
+ if (!HasReadObjCLibrary()) {
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+
+ size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ auto mod = module_list.GetModuleAtIndex(i);
+ if (IsModuleObjCLibrary(mod)) {
+ ReadObjCLibrary(mod);
+ break;
+ }
+ }
+ }
+}
+
+void AppleObjCRuntime::ModulesDidLoad(const ModuleList &module_list) {
+ ReadObjCLibraryIfNeeded(module_list);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
new file mode 100644
index 000000000000..79ac53e1e440
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
@@ -0,0 +1,131 @@
+//===-- AppleObjCRuntime.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_AppleObjCRuntime_h_
+#define liblldb_AppleObjCRuntime_h_
+
+#include "llvm/ADT/Optional.h"
+
+#include "AppleObjCTrampolineHandler.h"
+#include "AppleThreadPlanStepThroughObjCTrampoline.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+
+class AppleObjCRuntime : public lldb_private::ObjCLanguageRuntime {
+public:
+ ~AppleObjCRuntime() override;
+
+ // Static Functions
+ // Note there is no CreateInstance, Initialize & Terminate functions here,
+ // because
+ // you can't make an instance of this generic runtime.
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || ObjCLanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ // These are generic runtime functions:
+ bool GetObjectDescription(Stream &str, Value &value,
+ ExecutionContextScope *exe_scope) override;
+
+ bool GetObjectDescription(Stream &str, ValueObject &object) override;
+
+ bool CouldHaveDynamicValue(ValueObject &in_value) override;
+
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) override;
+
+ // These are the ObjC specific functions.
+
+ bool IsModuleObjCLibrary(const lldb::ModuleSP &module_sp) override;
+
+ bool ReadObjCLibrary(const lldb::ModuleSP &module_sp) override;
+
+ bool HasReadObjCLibrary() override { return m_read_objc_library; }
+
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) override;
+
+ // Get the "libobjc.A.dylib" module from the current target if we can find
+ // it, also cache it once it is found to ensure quick lookups.
+ lldb::ModuleSP GetObjCModule();
+
+ // Sync up with the target
+
+ void ModulesDidLoad(const ModuleList &module_list) override;
+
+ void SetExceptionBreakpoints() override;
+
+ void ClearExceptionBreakpoints() override;
+
+ bool ExceptionBreakpointsAreSet() override;
+
+ bool ExceptionBreakpointsExplainStop(lldb::StopInfoSP stop_reason) override;
+
+ lldb::SearchFilterSP CreateExceptionSearchFilter() override;
+
+ static std::tuple<FileSpec, ConstString> GetExceptionThrowLocation();
+
+ lldb::ValueObjectSP GetExceptionObjectForThread(
+ lldb::ThreadSP thread_sp) override;
+
+ lldb::ThreadSP GetBacktraceThreadFromException(
+ lldb::ValueObjectSP thread_sp) override;
+
+ uint32_t GetFoundationVersion();
+
+ virtual void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
+ lldb::addr_t &cf_false);
+
+ virtual bool IsTaggedPointer (lldb::addr_t addr) { return false; }
+
+protected:
+ // Call CreateInstance instead.
+ AppleObjCRuntime(Process *process);
+
+ bool CalculateHasNewLiteralsAndIndexing() override;
+
+ static bool AppleIsModuleObjCLibrary(const lldb::ModuleSP &module_sp);
+
+ static ObjCRuntimeVersions GetObjCVersion(Process *process,
+ lldb::ModuleSP &objc_module_sp);
+
+ void ReadObjCLibraryIfNeeded(const ModuleList &module_list);
+
+ Address *GetPrintForDebuggerAddr();
+
+ std::unique_ptr<Address> m_PrintForDebugger_addr;
+ bool m_read_objc_library;
+ std::unique_ptr<lldb_private::AppleObjCTrampolineHandler>
+ m_objc_trampoline_handler_up;
+ lldb::BreakpointSP m_objc_exception_bp_sp;
+ lldb::ModuleWP m_objc_module_wp;
+ std::unique_ptr<FunctionCaller> m_print_object_caller_up;
+
+ llvm::Optional<uint32_t> m_Foundation_major;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp
new file mode 100644
index 000000000000..c8884fd5c9b9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp
@@ -0,0 +1,443 @@
+//===-- AppleObjCRuntimeV1.cpp --------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AppleObjCRuntimeV1.h"
+#include "AppleObjCDeclVendor.h"
+#include "AppleObjCTrampolineHandler.h"
+
+#include "clang/AST/Type.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <memory>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+char AppleObjCRuntimeV1::ID = 0;
+
+AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process)
+ : AppleObjCRuntime(process), m_hash_signature(),
+ m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS) {}
+
+// for V1 runtime we just try to return a class name as that is the minimum
+// level of support required for the data formatters to work
+bool AppleObjCRuntimeV1::GetDynamicTypeAndAddress(
+ ValueObject &in_value, lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &address,
+ Value::ValueType &value_type) {
+ class_type_or_name.Clear();
+ value_type = Value::ValueType::eValueTypeScalar;
+ if (CouldHaveDynamicValue(in_value)) {
+ auto class_descriptor(GetClassDescriptor(in_value));
+ if (class_descriptor && class_descriptor->IsValid() &&
+ class_descriptor->GetClassName()) {
+ const addr_t object_ptr = in_value.GetPointerValue();
+ address.SetRawAddress(object_ptr);
+ class_type_or_name.SetName(class_descriptor->GetClassName());
+ }
+ }
+ return !class_type_or_name.IsEmpty();
+}
+
+// Static Functions
+lldb_private::LanguageRuntime *
+AppleObjCRuntimeV1::CreateInstance(Process *process,
+ lldb::LanguageType language) {
+ // FIXME: This should be a MacOS or iOS process, and we need to look for the
+ // OBJC section to make
+ // sure we aren't using the V1 runtime.
+ if (language == eLanguageTypeObjC) {
+ ModuleSP objc_module_sp;
+
+ if (AppleObjCRuntime::GetObjCVersion(process, objc_module_sp) ==
+ ObjCRuntimeVersions::eAppleObjC_V1)
+ return new AppleObjCRuntimeV1(process);
+ else
+ return nullptr;
+ } else
+ return nullptr;
+}
+
+void AppleObjCRuntimeV1::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "Apple Objective-C Language Runtime - Version 1",
+ CreateInstance,
+ /*command_callback = */ nullptr, GetBreakpointExceptionPrecondition);
+}
+
+void AppleObjCRuntimeV1::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString AppleObjCRuntimeV1::GetPluginNameStatic() {
+ static ConstString g_name("apple-objc-v1");
+ return g_name;
+}
+
+// PluginInterface protocol
+ConstString AppleObjCRuntimeV1::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t AppleObjCRuntimeV1::GetPluginVersion() { return 1; }
+
+BreakpointResolverSP
+AppleObjCRuntimeV1::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp,
+ bool throw_bp) {
+ BreakpointResolverSP resolver_sp;
+
+ if (throw_bp)
+ resolver_sp = std::make_shared<BreakpointResolverName>(
+ bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(),
+ eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0,
+ eLazyBoolNo);
+ // FIXME: don't do catch yet.
+ return resolver_sp;
+}
+
+struct BufStruct {
+ char contents[2048];
+};
+
+UtilityFunction *AppleObjCRuntimeV1::CreateObjectChecker(const char *name) {
+ std::unique_ptr<BufStruct> buf(new BufStruct);
+
+ int strformatsize = snprintf(&buf->contents[0], sizeof(buf->contents),
+ "struct __objc_class "
+ " \n"
+ "{ "
+ " \n"
+ " struct __objc_class *isa; "
+ " \n"
+ " struct __objc_class *super_class; "
+ " \n"
+ " const char *name; "
+ " \n"
+ " // rest of struct elided because unused "
+ " \n"
+ "}; "
+ " \n"
+ " "
+ " \n"
+ "struct __objc_object "
+ " \n"
+ "{ "
+ " \n"
+ " struct __objc_class *isa; "
+ " \n"
+ "}; "
+ " \n"
+ " "
+ " \n"
+ "extern \"C\" void "
+ " \n"
+ "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) "
+ " \n"
+ "{ "
+ " \n"
+ " struct __objc_object *obj = (struct "
+ "__objc_object*)$__lldb_arg_obj; \n"
+ " if ($__lldb_arg_obj == (void *)0) "
+ " \n"
+ " return; // nil is ok "
+ " (int)strlen(obj->isa->name); "
+ " \n"
+ "} "
+ " \n",
+ name);
+ assert(strformatsize < (int)sizeof(buf->contents));
+ (void)strformatsize;
+
+ Status error;
+ return GetTargetRef().GetUtilityFunctionForLanguage(
+ buf->contents, eLanguageTypeObjC, name, error);
+}
+
+AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
+ ValueObject &isa_pointer) {
+ Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP());
+}
+
+AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
+ ObjCISA isa, lldb::ProcessSP process_sp) {
+ Initialize(isa, process_sp);
+}
+
+void AppleObjCRuntimeV1::ClassDescriptorV1::Initialize(
+ ObjCISA isa, lldb::ProcessSP process_sp) {
+ if (!isa || !process_sp) {
+ m_valid = false;
+ return;
+ }
+
+ m_valid = true;
+
+ Status error;
+
+ m_isa = process_sp->ReadPointerFromMemory(isa, error);
+
+ if (error.Fail()) {
+ m_valid = false;
+ return;
+ }
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ if (!IsPointerValid(m_isa, ptr_size)) {
+ m_valid = false;
+ return;
+ }
+
+ m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size, error);
+
+ if (error.Fail()) {
+ m_valid = false;
+ return;
+ }
+
+ if (!IsPointerValid(m_parent_isa, ptr_size, true)) {
+ m_valid = false;
+ return;
+ }
+
+ lldb::addr_t name_ptr =
+ process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size, error);
+
+ if (error.Fail()) {
+ m_valid = false;
+ return;
+ }
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
+
+ size_t count = process_sp->ReadCStringFromMemory(
+ name_ptr, (char *)buffer_sp->GetBytes(), 1024, error);
+
+ if (error.Fail()) {
+ m_valid = false;
+ return;
+ }
+
+ if (count)
+ m_name = ConstString((char *)buffer_sp->GetBytes());
+ else
+ m_name = ConstString();
+
+ m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(
+ m_isa + 5 * ptr_size, ptr_size, 0, error);
+
+ if (error.Fail()) {
+ m_valid = false;
+ return;
+ }
+
+ m_process_wp = lldb::ProcessWP(process_sp);
+}
+
+AppleObjCRuntime::ClassDescriptorSP
+AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() {
+ if (!m_valid)
+ return AppleObjCRuntime::ClassDescriptorSP();
+ ProcessSP process_sp = m_process_wp.lock();
+ if (!process_sp)
+ return AppleObjCRuntime::ClassDescriptorSP();
+ return ObjCLanguageRuntime::ClassDescriptorSP(
+ new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp));
+}
+
+AppleObjCRuntime::ClassDescriptorSP
+AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const {
+ return ClassDescriptorSP();
+}
+
+bool AppleObjCRuntimeV1::ClassDescriptorV1::Describe(
+ std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
+ std::function<bool(const char *, const char *)> const &instance_method_func,
+ std::function<bool(const char *, const char *)> const &class_method_func,
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)> const &ivar_func) const {
+ return false;
+}
+
+lldb::addr_t AppleObjCRuntimeV1::GetTaggedPointerObfuscator() {
+ return 0;
+}
+
+lldb::addr_t AppleObjCRuntimeV1::GetISAHashTablePointer() {
+ if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) {
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (!objc_module_sp)
+ return LLDB_INVALID_ADDRESS;
+
+ static ConstString g_objc_debug_class_hash("_objc_debug_class_hash");
+
+ const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
+ g_objc_debug_class_hash, lldb::eSymbolTypeData);
+ if (symbol && symbol->ValueIsAddress()) {
+ Process *process = GetProcess();
+ if (process) {
+
+ lldb::addr_t objc_debug_class_hash_addr =
+ symbol->GetAddressRef().GetLoadAddress(&process->GetTarget());
+
+ if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) {
+ Status error;
+ lldb::addr_t objc_debug_class_hash_ptr =
+ process->ReadPointerFromMemory(objc_debug_class_hash_addr, error);
+ if (objc_debug_class_hash_ptr != 0 &&
+ objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS) {
+ m_isa_hash_table_ptr = objc_debug_class_hash_ptr;
+ }
+ }
+ }
+ }
+ }
+ return m_isa_hash_table_ptr;
+}
+
+void AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() {
+ // TODO: implement HashTableSignature...
+ Process *process = GetProcess();
+
+ if (process) {
+ // Update the process stop ID that indicates the last time we updated the
+ // map, whether it was successful or not.
+ m_isa_to_descriptor_stop_id = process->GetStopID();
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ ProcessSP process_sp = process->shared_from_this();
+
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (!objc_module_sp)
+ return;
+
+ uint32_t isa_count = 0;
+
+ lldb::addr_t hash_table_ptr = GetISAHashTablePointer();
+ if (hash_table_ptr != LLDB_INVALID_ADDRESS) {
+ // Read the NXHashTable struct:
+ //
+ // typedef struct {
+ // const NXHashTablePrototype *prototype;
+ // unsigned count;
+ // unsigned nbBuckets;
+ // void *buckets;
+ // const void *info;
+ // } NXHashTable;
+
+ Status error;
+ DataBufferHeap buffer(1024, 0);
+ if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) ==
+ 20) {
+ const uint32_t addr_size = m_process->GetAddressByteSize();
+ const ByteOrder byte_order = m_process->GetByteOrder();
+ DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), byte_order,
+ addr_size);
+ lldb::offset_t offset = addr_size; // Skip prototype
+ const uint32_t count = data.GetU32(&offset);
+ const uint32_t num_buckets = data.GetU32(&offset);
+ const addr_t buckets_ptr = data.GetPointer(&offset);
+ if (m_hash_signature.NeedsUpdate(count, num_buckets, buckets_ptr)) {
+ m_hash_signature.UpdateSignature(count, num_buckets, buckets_ptr);
+
+ const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t);
+ buffer.SetByteSize(data_size);
+
+ if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size,
+ error) == data_size) {
+ data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order);
+ offset = 0;
+ for (uint32_t bucket_idx = 0; bucket_idx < num_buckets;
+ ++bucket_idx) {
+ const uint32_t bucket_isa_count = data.GetU32(&offset);
+ const lldb::addr_t bucket_data = data.GetU32(&offset);
+
+ if (bucket_isa_count == 0)
+ continue;
+
+ isa_count += bucket_isa_count;
+
+ ObjCISA isa;
+ if (bucket_isa_count == 1) {
+ // When we only have one entry in the bucket, the bucket data
+ // is the "isa"
+ isa = bucket_data;
+ if (isa) {
+ if (!ISAIsCached(isa)) {
+ ClassDescriptorSP descriptor_sp(
+ new ClassDescriptorV1(isa, process_sp));
+
+ if (log && log->GetVerbose())
+ log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
+ " from _objc_debug_class_hash to "
+ "isa->descriptor cache",
+ isa);
+
+ AddClass(isa, descriptor_sp);
+ }
+ }
+ } else {
+ // When we have more than one entry in the bucket, the bucket
+ // data is a pointer to an array of "isa" values
+ addr_t isa_addr = bucket_data;
+ for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count;
+ ++isa_idx, isa_addr += addr_size) {
+ isa = m_process->ReadPointerFromMemory(isa_addr, error);
+
+ if (isa && isa != LLDB_INVALID_ADDRESS) {
+ if (!ISAIsCached(isa)) {
+ ClassDescriptorSP descriptor_sp(
+ new ClassDescriptorV1(isa, process_sp));
+
+ if (log && log->GetVerbose())
+ log->Printf(
+ "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
+ " from _objc_debug_class_hash to isa->descriptor "
+ "cache",
+ isa);
+
+ AddClass(isa, descriptor_sp);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ m_isa_to_descriptor_stop_id = UINT32_MAX;
+ }
+}
+
+DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() {
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h
new file mode 100644
index 000000000000..6fdae63d4126
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h
@@ -0,0 +1,156 @@
+//===-- AppleObjCRuntimeV1.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_AppleObjCRuntimeV1_h_
+#define liblldb_AppleObjCRuntimeV1_h_
+
+#include "AppleObjCRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+
+class AppleObjCRuntimeV1 : public AppleObjCRuntime {
+public:
+ ~AppleObjCRuntimeV1() override = default;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::LanguageRuntime *
+ CreateInstance(Process *process, lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || AppleObjCRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ lldb::addr_t GetTaggedPointerObfuscator();
+
+ class ClassDescriptorV1 : public ObjCLanguageRuntime::ClassDescriptor {
+ public:
+ ClassDescriptorV1(ValueObject &isa_pointer);
+ ClassDescriptorV1(ObjCISA isa, lldb::ProcessSP process_sp);
+
+ ~ClassDescriptorV1() override = default;
+
+ ConstString GetClassName() override { return m_name; }
+
+ ClassDescriptorSP GetSuperclass() override;
+
+ ClassDescriptorSP GetMetaclass() const override;
+
+ bool IsValid() override { return m_valid; }
+
+ // v1 does not support tagged pointers
+ bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
+ uint64_t *value_bits = nullptr,
+ uint64_t *payload = nullptr) override {
+ return false;
+ }
+
+ uint64_t GetInstanceSize() override { return m_instance_size; }
+
+ ObjCISA GetISA() override { return m_isa; }
+
+ bool
+ Describe(std::function<void(ObjCLanguageRuntime::ObjCISA)> const
+ &superclass_func,
+ std::function<bool(const char *, const char *)> const
+ &instance_method_func,
+ std::function<bool(const char *, const char *)> const
+ &class_method_func,
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)> const &ivar_func) const override;
+
+ protected:
+ void Initialize(ObjCISA isa, lldb::ProcessSP process_sp);
+
+ private:
+ ConstString m_name;
+ ObjCISA m_isa;
+ ObjCISA m_parent_isa;
+ bool m_valid;
+ lldb::ProcessWP m_process_wp;
+ uint64_t m_instance_size;
+ };
+
+ // These are generic runtime functions:
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ UtilityFunction *CreateObjectChecker(const char *) override;
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ ObjCRuntimeVersions GetRuntimeVersion() const override {
+ return ObjCRuntimeVersions::eAppleObjC_V1;
+ }
+
+ void UpdateISAToDescriptorMapIfNeeded() override;
+
+ DeclVendor *GetDeclVendor() override;
+
+protected:
+ lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt,
+ bool catch_bp,
+ bool throw_bp) override;
+
+ class HashTableSignature {
+ public:
+ HashTableSignature()
+ : m_count(0), m_num_buckets(0), m_buckets_ptr(LLDB_INVALID_ADDRESS) {}
+
+ bool NeedsUpdate(uint32_t count, uint32_t num_buckets,
+ lldb::addr_t buckets_ptr) {
+ return m_count != count || m_num_buckets != num_buckets ||
+ m_buckets_ptr != buckets_ptr;
+ }
+
+ void UpdateSignature(uint32_t count, uint32_t num_buckets,
+ lldb::addr_t buckets_ptr) {
+ m_count = count;
+ m_num_buckets = num_buckets;
+ m_buckets_ptr = buckets_ptr;
+ }
+
+ protected:
+ uint32_t m_count;
+ uint32_t m_num_buckets;
+ lldb::addr_t m_buckets_ptr;
+ };
+
+ lldb::addr_t GetISAHashTablePointer();
+
+ HashTableSignature m_hash_signature;
+ lldb::addr_t m_isa_hash_table_ptr;
+ std::unique_ptr<DeclVendor> m_decl_vendor_up;
+
+private:
+ AppleObjCRuntimeV1(Process *process);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCRuntimeV1_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
new file mode 100644
index 000000000000..635eaff637bc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -0,0 +1,2736 @@
+//===-- AppleObjCRuntimeV2.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclObjC.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrameRecognizer.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+#include "AppleObjCClassDescriptorV2.h"
+#include "AppleObjCDeclVendor.h"
+#include "AppleObjCRuntimeV2.h"
+#include "AppleObjCTrampolineHandler.h"
+#include "AppleObjCTypeEncodingParser.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclObjC.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+char AppleObjCRuntimeV2::ID = 0;
+
+static const char *g_get_dynamic_class_info_name =
+ "__lldb_apple_objc_v2_get_dynamic_class_info";
+// Testing using the new C++11 raw string literals. If this breaks GCC then we
+// will need to revert to the code above...
+static const char *g_get_dynamic_class_info_body = R"(
+
+extern "C"
+{
+ size_t strlen(const char *);
+ char *strncpy (char * s1, const char * s2, size_t n);
+ int printf(const char * format, ...);
+}
+#define DEBUG_PRINTF(fmt, ...) if (should_log) printf(fmt, ## __VA_ARGS__)
+
+typedef struct _NXMapTable {
+ void *prototype;
+ unsigned num_classes;
+ unsigned num_buckets_minus_one;
+ void *buckets;
+} NXMapTable;
+
+#define NX_MAPNOTAKEY ((void *)(-1))
+
+typedef struct BucketInfo
+{
+ const char *name_ptr;
+ Class isa;
+} BucketInfo;
+
+struct ClassInfo
+{
+ Class isa;
+ uint32_t hash;
+} __attribute__((__packed__));
+
+uint32_t
+__lldb_apple_objc_v2_get_dynamic_class_info (void *gdb_objc_realized_classes_ptr,
+ void *class_infos_ptr,
+ uint32_t class_infos_byte_size,
+ uint32_t should_log)
+{
+ DEBUG_PRINTF ("gdb_objc_realized_classes_ptr = %p\n", gdb_objc_realized_classes_ptr);
+ DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr);
+ DEBUG_PRINTF ("class_infos_byte_size = %u\n", class_infos_byte_size);
+ const NXMapTable *grc = (const NXMapTable *)gdb_objc_realized_classes_ptr;
+ if (grc)
+ {
+ const unsigned num_classes = grc->num_classes;
+ if (class_infos_ptr)
+ {
+ const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo);
+ ClassInfo *class_infos = (ClassInfo *)class_infos_ptr;
+ BucketInfo *buckets = (BucketInfo *)grc->buckets;
+
+ uint32_t idx = 0;
+ for (unsigned i=0; i<=grc->num_buckets_minus_one; ++i)
+ {
+ if (buckets[i].name_ptr != NX_MAPNOTAKEY)
+ {
+ if (idx < max_class_infos)
+ {
+ const char *s = buckets[i].name_ptr;
+ uint32_t h = 5381;
+ for (unsigned char c = *s; c; c = *++s)
+ h = ((h << 5) + h) + c;
+ class_infos[idx].hash = h;
+ class_infos[idx].isa = buckets[i].isa;
+ }
+ ++idx;
+ }
+ }
+ if (idx < max_class_infos)
+ {
+ class_infos[idx].isa = NULL;
+ class_infos[idx].hash = 0;
+ }
+ }
+ return num_classes;
+ }
+ return 0;
+}
+
+)";
+
+// We'll substitute in class_getName or class_getNameRaw depending
+// on which is present.
+static const char *g_shared_cache_class_name_funcptr = R"(
+extern "C"
+{
+ const char *%s(void *objc_class);
+ const char *(*class_name_lookup_func)(void *) = %s;
+}
+)";
+
+static const char *g_get_shared_cache_class_info_name =
+ "__lldb_apple_objc_v2_get_shared_cache_class_info";
+// Testing using the new C++11 raw string literals. If this breaks GCC then we
+// will need to revert to the code above...
+static const char *g_get_shared_cache_class_info_body = R"(
+
+extern "C"
+{
+ size_t strlen(const char *);
+ char *strncpy (char * s1, const char * s2, size_t n);
+ int printf(const char * format, ...);
+}
+
+#define DEBUG_PRINTF(fmt, ...) if (should_log) printf(fmt, ## __VA_ARGS__)
+
+
+struct objc_classheader_t {
+ int32_t clsOffset;
+ int32_t hiOffset;
+};
+
+struct objc_clsopt_t {
+ uint32_t capacity;
+ uint32_t occupied;
+ uint32_t shift;
+ uint32_t mask;
+ uint32_t zero;
+ uint32_t unused;
+ uint64_t salt;
+ uint32_t scramble[256];
+ uint8_t tab[0]; // tab[mask+1]
+ // uint8_t checkbytes[capacity];
+ // int32_t offset[capacity];
+ // objc_classheader_t clsOffsets[capacity];
+ // uint32_t duplicateCount;
+ // objc_classheader_t duplicateOffsets[duplicateCount];
+};
+
+struct objc_opt_t {
+ uint32_t version;
+ int32_t selopt_offset;
+ int32_t headeropt_offset;
+ int32_t clsopt_offset;
+};
+
+struct objc_opt_v14_t {
+ uint32_t version;
+ uint32_t flags;
+ int32_t selopt_offset;
+ int32_t headeropt_offset;
+ int32_t clsopt_offset;
+};
+
+struct ClassInfo
+{
+ Class isa;
+ uint32_t hash;
+} __attribute__((__packed__));
+
+uint32_t
+__lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
+ void *class_infos_ptr,
+ uint32_t class_infos_byte_size,
+ uint32_t should_log)
+{
+ uint32_t idx = 0;
+ DEBUG_PRINTF ("objc_opt_ro_ptr = %p\n", objc_opt_ro_ptr);
+ DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr);
+ DEBUG_PRINTF ("class_infos_byte_size = %u (%llu class infos)\n", class_infos_byte_size, (uint64_t)(class_infos_byte_size/sizeof(ClassInfo)));
+ if (objc_opt_ro_ptr)
+ {
+ const objc_opt_t *objc_opt = (objc_opt_t *)objc_opt_ro_ptr;
+ const objc_opt_v14_t* objc_opt_v14 = (objc_opt_v14_t*)objc_opt_ro_ptr;
+ const bool is_v14_format = objc_opt->version >= 14;
+ if (is_v14_format)
+ {
+ DEBUG_PRINTF ("objc_opt->version = %u\n", objc_opt_v14->version);
+ DEBUG_PRINTF ("objc_opt->flags = %u\n", objc_opt_v14->flags);
+ DEBUG_PRINTF ("objc_opt->selopt_offset = %d\n", objc_opt_v14->selopt_offset);
+ DEBUG_PRINTF ("objc_opt->headeropt_offset = %d\n", objc_opt_v14->headeropt_offset);
+ DEBUG_PRINTF ("objc_opt->clsopt_offset = %d\n", objc_opt_v14->clsopt_offset);
+ }
+ else
+ {
+ DEBUG_PRINTF ("objc_opt->version = %u\n", objc_opt->version);
+ DEBUG_PRINTF ("objc_opt->selopt_offset = %d\n", objc_opt->selopt_offset);
+ DEBUG_PRINTF ("objc_opt->headeropt_offset = %d\n", objc_opt->headeropt_offset);
+ DEBUG_PRINTF ("objc_opt->clsopt_offset = %d\n", objc_opt->clsopt_offset);
+ }
+ if (objc_opt->version == 12 || objc_opt->version == 13 || objc_opt->version == 14 || objc_opt->version == 15)
+ {
+ const objc_clsopt_t* clsopt = NULL;
+ if (is_v14_format)
+ clsopt = (const objc_clsopt_t*)((uint8_t *)objc_opt_v14 + objc_opt_v14->clsopt_offset);
+ else
+ clsopt = (const objc_clsopt_t*)((uint8_t *)objc_opt + objc_opt->clsopt_offset);
+ const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo);
+ DEBUG_PRINTF("max_class_infos = %llu\n", (uint64_t)max_class_infos);
+ ClassInfo *class_infos = (ClassInfo *)class_infos_ptr;
+ int32_t invalidEntryOffset = 0;
+ // this is safe to do because the version field order is invariant
+ if (objc_opt->version == 12)
+ invalidEntryOffset = 16;
+ const uint8_t *checkbytes = &clsopt->tab[clsopt->mask+1];
+ const int32_t *offsets = (const int32_t *)(checkbytes + clsopt->capacity);
+ const objc_classheader_t *classOffsets = (const objc_classheader_t *)(offsets + clsopt->capacity);
+ DEBUG_PRINTF ("clsopt->capacity = %u\n", clsopt->capacity);
+ DEBUG_PRINTF ("clsopt->mask = 0x%8.8x\n", clsopt->mask);
+ DEBUG_PRINTF ("classOffsets = %p\n", classOffsets);
+ DEBUG_PRINTF("invalidEntryOffset = %d\n", invalidEntryOffset);
+ for (uint32_t i=0; i<clsopt->capacity; ++i)
+ {
+ const int32_t clsOffset = classOffsets[i].clsOffset;
+ DEBUG_PRINTF("clsOffset[%u] = %u\n", i, clsOffset);
+ if (clsOffset & 1)
+ {
+ DEBUG_PRINTF("clsOffset & 1\n");
+ continue; // duplicate
+ }
+ else if (clsOffset == invalidEntryOffset)
+ {
+ DEBUG_PRINTF("clsOffset == invalidEntryOffset\n");
+ continue; // invalid offset
+ }
+
+ if (class_infos && idx < max_class_infos)
+ {
+ class_infos[idx].isa = (Class)((uint8_t *)clsopt + clsOffset);
+ const char *name = class_name_lookup_func (class_infos[idx].isa);
+ DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name);
+ // Hash the class name so we don't have to read it
+ const char *s = name;
+ uint32_t h = 5381;
+ for (unsigned char c = *s; c; c = *++s)
+ {
+ // class_getName demangles swift names and the hash must
+ // be calculated on the mangled name. hash==0 means lldb
+ // will fetch the mangled name and compute the hash in
+ // ParseClassInfoArray.
+ if (c == '.')
+ {
+ h = 0;
+ break;
+ }
+ h = ((h << 5) + h) + c;
+ }
+ class_infos[idx].hash = h;
+ }
+ else
+ {
+ DEBUG_PRINTF("not(class_infos && idx < max_class_infos)\n");
+ }
+ ++idx;
+ }
+
+ const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity];
+ const uint32_t duplicate_count = *duplicate_count_ptr;
+ const objc_classheader_t *duplicateClassOffsets = (const objc_classheader_t *)(&duplicate_count_ptr[1]);
+ DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count);
+ DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets);
+ for (uint32_t i=0; i<duplicate_count; ++i)
+ {
+ const int32_t clsOffset = duplicateClassOffsets[i].clsOffset;
+ if (clsOffset & 1)
+ continue; // duplicate
+ else if (clsOffset == invalidEntryOffset)
+ continue; // invalid offset
+
+ if (class_infos && idx < max_class_infos)
+ {
+ class_infos[idx].isa = (Class)((uint8_t *)clsopt + clsOffset);
+ const char *name = class_name_lookup_func (class_infos[idx].isa);
+ DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name);
+ // Hash the class name so we don't have to read it
+ const char *s = name;
+ uint32_t h = 5381;
+ for (unsigned char c = *s; c; c = *++s)
+ {
+ // class_getName demangles swift names and the hash must
+ // be calculated on the mangled name. hash==0 means lldb
+ // will fetch the mangled name and compute the hash in
+ // ParseClassInfoArray.
+ if (c == '.')
+ {
+ h = 0;
+ break;
+ }
+ h = ((h << 5) + h) + c;
+ }
+ class_infos[idx].hash = h;
+ }
+ ++idx;
+ }
+ }
+ DEBUG_PRINTF ("%u class_infos\n", idx);
+ DEBUG_PRINTF ("done\n");
+ }
+ return idx;
+}
+
+
+)";
+
+static uint64_t
+ExtractRuntimeGlobalSymbol(Process *process, ConstString name,
+ const ModuleSP &module_sp, Status &error,
+ bool read_value = true, uint8_t byte_size = 0,
+ uint64_t default_value = LLDB_INVALID_ADDRESS,
+ SymbolType sym_type = lldb::eSymbolTypeData) {
+ if (!process) {
+ error.SetErrorString("no process");
+ return default_value;
+ }
+ if (!module_sp) {
+ error.SetErrorString("no module");
+ return default_value;
+ }
+ if (!byte_size)
+ byte_size = process->GetAddressByteSize();
+ const Symbol *symbol =
+ module_sp->FindFirstSymbolWithNameAndType(name, lldb::eSymbolTypeData);
+ if (symbol && symbol->ValueIsAddress()) {
+ lldb::addr_t symbol_load_addr =
+ symbol->GetAddressRef().GetLoadAddress(&process->GetTarget());
+ if (symbol_load_addr != LLDB_INVALID_ADDRESS) {
+ if (read_value)
+ return process->ReadUnsignedIntegerFromMemory(
+ symbol_load_addr, byte_size, default_value, error);
+ else
+ return symbol_load_addr;
+ } else {
+ error.SetErrorString("symbol address invalid");
+ return default_value;
+ }
+ } else {
+ error.SetErrorString("no symbol");
+ return default_value;
+ }
+}
+
+static void RegisterObjCExceptionRecognizer();
+
+AppleObjCRuntimeV2::AppleObjCRuntimeV2(Process *process,
+ const ModuleSP &objc_module_sp)
+ : AppleObjCRuntime(process), m_get_class_info_code(),
+ m_get_class_info_args(LLDB_INVALID_ADDRESS),
+ m_get_class_info_args_mutex(), m_get_shared_cache_class_info_code(),
+ m_get_shared_cache_class_info_args(LLDB_INVALID_ADDRESS),
+ m_get_shared_cache_class_info_args_mutex(), m_decl_vendor_up(),
+ m_tagged_pointer_obfuscator(LLDB_INVALID_ADDRESS),
+ m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS), m_hash_signature(),
+ m_has_object_getClass(false), m_loaded_objc_opt(false),
+ m_non_pointer_isa_cache_up(
+ NonPointerISACache::CreateInstance(*this, objc_module_sp)),
+ m_tagged_pointer_vendor_up(
+ TaggedPointerVendorV2::CreateInstance(*this, objc_module_sp)),
+ m_encoding_to_type_sp(), m_noclasses_warning_emitted(false),
+ m_CFBoolean_values() {
+ static const ConstString g_gdb_object_getClass("gdb_object_getClass");
+ m_has_object_getClass =
+ (objc_module_sp->FindFirstSymbolWithNameAndType(
+ g_gdb_object_getClass, eSymbolTypeCode) != nullptr);
+ RegisterObjCExceptionRecognizer();
+}
+
+bool AppleObjCRuntimeV2::GetDynamicTypeAndAddress(
+ ValueObject &in_value, lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &address,
+ Value::ValueType &value_type) {
+ // We should never get here with a null process...
+ assert(m_process != nullptr);
+
+ // The Runtime is attached to a particular process, you shouldn't pass in a
+ // value from another process. Note, however, the process might be NULL (e.g.
+ // if the value was made with SBTarget::EvaluateExpression...) in which case
+ // it is sufficient if the target's match:
+
+ Process *process = in_value.GetProcessSP().get();
+ if (process)
+ assert(process == m_process);
+ else
+ assert(in_value.GetTargetSP().get() == m_process->CalculateTarget().get());
+
+ class_type_or_name.Clear();
+ value_type = Value::ValueType::eValueTypeScalar;
+
+ // Make sure we can have a dynamic value before starting...
+ if (CouldHaveDynamicValue(in_value)) {
+ // First job, pull out the address at 0 offset from the object That will
+ // be the ISA pointer.
+ ClassDescriptorSP objc_class_sp(GetNonKVOClassDescriptor(in_value));
+ if (objc_class_sp) {
+ const addr_t object_ptr = in_value.GetPointerValue();
+ address.SetRawAddress(object_ptr);
+
+ ConstString class_name(objc_class_sp->GetClassName());
+ class_type_or_name.SetName(class_name);
+ TypeSP type_sp(objc_class_sp->GetType());
+ if (type_sp)
+ class_type_or_name.SetTypeSP(type_sp);
+ else {
+ type_sp = LookupInCompleteClassCache(class_name);
+ if (type_sp) {
+ objc_class_sp->SetType(type_sp);
+ class_type_or_name.SetTypeSP(type_sp);
+ } else {
+ // try to go for a CompilerType at least
+ if (auto *vendor = GetDeclVendor()) {
+ auto types = vendor->FindTypes(class_name, /*max_matches*/ 1);
+ if (!types.empty())
+ class_type_or_name.SetCompilerType(types.front());
+ }
+ }
+ }
+ }
+ }
+ return !class_type_or_name.IsEmpty();
+}
+
+// Static Functions
+LanguageRuntime *AppleObjCRuntimeV2::CreateInstance(Process *process,
+ LanguageType language) {
+ // FIXME: This should be a MacOS or iOS process, and we need to look for the
+ // OBJC section to make
+ // sure we aren't using the V1 runtime.
+ if (language == eLanguageTypeObjC) {
+ ModuleSP objc_module_sp;
+
+ if (AppleObjCRuntime::GetObjCVersion(process, objc_module_sp) ==
+ ObjCRuntimeVersions::eAppleObjC_V2)
+ return new AppleObjCRuntimeV2(process, objc_module_sp);
+ else
+ return nullptr;
+ } else
+ return nullptr;
+}
+
+static constexpr OptionDefinition g_objc_classtable_dump_options[] = {
+ {LLDB_OPT_SET_ALL, false, "verbose", 'v', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Print ivar and method information in detail"}};
+
+class CommandObjectObjC_ClassTable_Dump : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_verbose(false, false) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'v':
+ m_verbose.SetCurrentValue(true);
+ m_verbose.SetOptionWasSet();
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_verbose.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_objc_classtable_dump_options);
+ }
+
+ OptionValueBoolean m_verbose;
+ };
+
+ CommandObjectObjC_ClassTable_Dump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "dump", "Dump information on Objective-C classes "
+ "known to the current process.",
+ "language objc class-table dump",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeRegularExpression;
+ index_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectObjC_ClassTable_Dump() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ std::unique_ptr<RegularExpression> regex_up;
+ switch (command.GetArgumentCount()) {
+ case 0:
+ break;
+ case 1: {
+ regex_up.reset(new RegularExpression());
+ if (!regex_up->Compile(llvm::StringRef::withNullAsEmpty(
+ command.GetArgumentAtIndex(0)))) {
+ result.AppendError(
+ "invalid argument - please provide a valid regular expression");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ break;
+ }
+ default: {
+ result.AppendError("please provide 0 or 1 arguments");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process);
+ if (objc_runtime) {
+ auto iterators_pair = objc_runtime->GetDescriptorIteratorPair();
+ auto iterator = iterators_pair.first;
+ auto &std_out = result.GetOutputStream();
+ for (; iterator != iterators_pair.second; iterator++) {
+ if (iterator->second) {
+ const char *class_name =
+ iterator->second->GetClassName().AsCString("<unknown>");
+ if (regex_up && class_name &&
+ !regex_up->Execute(llvm::StringRef(class_name)))
+ continue;
+ std_out.Printf("isa = 0x%" PRIx64, iterator->first);
+ std_out.Printf(" name = %s", class_name);
+ std_out.Printf(" instance size = %" PRIu64,
+ iterator->second->GetInstanceSize());
+ std_out.Printf(" num ivars = %" PRIuPTR,
+ (uintptr_t)iterator->second->GetNumIVars());
+ if (auto superclass = iterator->second->GetSuperclass()) {
+ std_out.Printf(" superclass = %s",
+ superclass->GetClassName().AsCString("<unknown>"));
+ }
+ std_out.Printf("\n");
+ if (m_options.m_verbose) {
+ for (size_t i = 0; i < iterator->second->GetNumIVars(); i++) {
+ auto ivar = iterator->second->GetIVarAtIndex(i);
+ std_out.Printf(
+ " ivar name = %s type = %s size = %" PRIu64
+ " offset = %" PRId32 "\n",
+ ivar.m_name.AsCString("<unknown>"),
+ ivar.m_type.GetDisplayTypeName().AsCString("<unknown>"),
+ ivar.m_size, ivar.m_offset);
+ }
+ iterator->second->Describe(
+ nullptr,
+ [&std_out](const char *name, const char *type) -> bool {
+ std_out.Printf(" instance method name = %s type = %s\n",
+ name, type);
+ return false;
+ },
+ [&std_out](const char *name, const char *type) -> bool {
+ std_out.Printf(" class method name = %s type = %s\n", name,
+ type);
+ return false;
+ },
+ nullptr);
+ }
+ } else {
+ if (regex_up && !regex_up->Execute(llvm::StringRef()))
+ continue;
+ std_out.Printf("isa = 0x%" PRIx64 " has no associated class.\n",
+ iterator->first);
+ }
+ }
+ result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
+ return true;
+ } else {
+ result.AppendError("current process has no Objective-C runtime loaded");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ CommandOptions m_options;
+};
+
+class CommandObjectMultiwordObjC_TaggedPointer_Info
+ : public CommandObjectParsed {
+public:
+ CommandObjectMultiwordObjC_TaggedPointer_Info(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "info", "Dump information on a tagged pointer.",
+ "language objc tagged-pointer info",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeAddress;
+ index_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectMultiwordObjC_TaggedPointer_Info() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError("this command requires arguments");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ ExecutionContext exe_ctx(process);
+ ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process);
+ if (objc_runtime) {
+ ObjCLanguageRuntime::TaggedPointerVendor *tagged_ptr_vendor =
+ objc_runtime->GetTaggedPointerVendor();
+ if (tagged_ptr_vendor) {
+ for (size_t i = 0; i < command.GetArgumentCount(); i++) {
+ const char *arg_str = command.GetArgumentAtIndex(i);
+ if (!arg_str)
+ continue;
+ Status error;
+ lldb::addr_t arg_addr = OptionArgParser::ToAddress(
+ &exe_ctx, arg_str, LLDB_INVALID_ADDRESS, &error);
+ if (arg_addr == 0 || arg_addr == LLDB_INVALID_ADDRESS || error.Fail())
+ continue;
+ auto descriptor_sp = tagged_ptr_vendor->GetClassDescriptor(arg_addr);
+ if (!descriptor_sp)
+ continue;
+ uint64_t info_bits = 0;
+ uint64_t value_bits = 0;
+ uint64_t payload = 0;
+ if (descriptor_sp->GetTaggedPointerInfo(&info_bits, &value_bits,
+ &payload)) {
+ result.GetOutputStream().Printf(
+ "0x%" PRIx64 " is tagged.\n\tpayload = 0x%" PRIx64
+ "\n\tvalue = 0x%" PRIx64 "\n\tinfo bits = 0x%" PRIx64
+ "\n\tclass = %s\n",
+ (uint64_t)arg_addr, payload, value_bits, info_bits,
+ descriptor_sp->GetClassName().AsCString("<unknown>"));
+ } else {
+ result.GetOutputStream().Printf("0x%" PRIx64 " is not tagged.\n",
+ (uint64_t)arg_addr);
+ }
+ }
+ } else {
+ result.AppendError("current process has no tagged pointer support");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
+ return true;
+ } else {
+ result.AppendError("current process has no Objective-C runtime loaded");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ }
+};
+
+class CommandObjectMultiwordObjC_ClassTable : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordObjC_ClassTable(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "class-table",
+ "Commands for operating on the Objective-C class table.",
+ "class-table <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "dump",
+ CommandObjectSP(new CommandObjectObjC_ClassTable_Dump(interpreter)));
+ }
+
+ ~CommandObjectMultiwordObjC_ClassTable() override = default;
+};
+
+class CommandObjectMultiwordObjC_TaggedPointer : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordObjC_TaggedPointer(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "tagged-pointer",
+ "Commands for operating on Objective-C tagged pointers.",
+ "class-table <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "info",
+ CommandObjectSP(
+ new CommandObjectMultiwordObjC_TaggedPointer_Info(interpreter)));
+ }
+
+ ~CommandObjectMultiwordObjC_TaggedPointer() override = default;
+};
+
+class CommandObjectMultiwordObjC : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordObjC(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "objc",
+ "Commands for operating on the Objective-C language runtime.",
+ "objc <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("class-table",
+ CommandObjectSP(
+ new CommandObjectMultiwordObjC_ClassTable(interpreter)));
+ LoadSubCommand("tagged-pointer",
+ CommandObjectSP(new CommandObjectMultiwordObjC_TaggedPointer(
+ interpreter)));
+ }
+
+ ~CommandObjectMultiwordObjC() override = default;
+};
+
+void AppleObjCRuntimeV2::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "Apple Objective-C Language Runtime - Version 2",
+ CreateInstance,
+ [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP {
+ return CommandObjectSP(new CommandObjectMultiwordObjC(interpreter));
+ },
+ GetBreakpointExceptionPrecondition);
+}
+
+void AppleObjCRuntimeV2::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString AppleObjCRuntimeV2::GetPluginNameStatic() {
+ static ConstString g_name("apple-objc-v2");
+ return g_name;
+}
+
+// PluginInterface protocol
+lldb_private::ConstString AppleObjCRuntimeV2::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t AppleObjCRuntimeV2::GetPluginVersion() { return 1; }
+
+BreakpointResolverSP
+AppleObjCRuntimeV2::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp,
+ bool throw_bp) {
+ BreakpointResolverSP resolver_sp;
+
+ if (throw_bp)
+ resolver_sp = std::make_shared<BreakpointResolverName>(
+ bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(),
+ eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0,
+ eLazyBoolNo);
+ // FIXME: We don't do catch breakpoints for ObjC yet.
+ // Should there be some way for the runtime to specify what it can do in this
+ // regard?
+ return resolver_sp;
+}
+
+UtilityFunction *AppleObjCRuntimeV2::CreateObjectChecker(const char *name) {
+ char check_function_code[2048];
+
+ int len = 0;
+ if (m_has_object_getClass) {
+ len = ::snprintf(check_function_code, sizeof(check_function_code), R"(
+ extern "C" void *gdb_object_getClass(void *);
+ extern "C" int printf(const char *format, ...);
+ extern "C" void
+ %s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) {
+ if ($__lldb_arg_obj == (void *)0)
+ return; // nil is ok
+ if (!gdb_object_getClass($__lldb_arg_obj)) {
+ *((volatile int *)0) = 'ocgc';
+ } else if ($__lldb_arg_selector != (void *)0) {
+ signed char $responds = (signed char)
+ [(id)$__lldb_arg_obj respondsToSelector:
+ (void *) $__lldb_arg_selector];
+ if ($responds == (signed char) 0)
+ *((volatile int *)0) = 'ocgc';
+ }
+ })", name);
+ } else {
+ len = ::snprintf(check_function_code, sizeof(check_function_code), R"(
+ extern "C" void *gdb_class_getClass(void *);
+ extern "C" int printf(const char *format, ...);
+ extern "C" void
+ %s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) {
+ if ($__lldb_arg_obj == (void *)0)
+ return; // nil is ok
+ void **$isa_ptr = (void **)$__lldb_arg_obj;
+ if (*$isa_ptr == (void *)0 ||
+ !gdb_class_getClass(*$isa_ptr))
+ *((volatile int *)0) = 'ocgc';
+ else if ($__lldb_arg_selector != (void *)0) {
+ signed char $responds = (signed char)
+ [(id)$__lldb_arg_obj respondsToSelector:
+ (void *) $__lldb_arg_selector];
+ if ($responds == (signed char) 0)
+ *((volatile int *)0) = 'ocgc';
+ }
+ })", name);
+ }
+
+ assert(len < (int)sizeof(check_function_code));
+ UNUSED_IF_ASSERT_DISABLED(len);
+
+ Status error;
+ return GetTargetRef().GetUtilityFunctionForLanguage(
+ check_function_code, eLanguageTypeObjC, name, error);
+}
+
+size_t AppleObjCRuntimeV2::GetByteOffsetForIvar(CompilerType &parent_ast_type,
+ const char *ivar_name) {
+ uint32_t ivar_offset = LLDB_INVALID_IVAR_OFFSET;
+
+ const char *class_name = parent_ast_type.GetConstTypeName().AsCString();
+ if (class_name && class_name[0] && ivar_name && ivar_name[0]) {
+ // Make the objective C V2 mangled name for the ivar offset from the class
+ // name and ivar name
+ std::string buffer("OBJC_IVAR_$_");
+ buffer.append(class_name);
+ buffer.push_back('.');
+ buffer.append(ivar_name);
+ ConstString ivar_const_str(buffer.c_str());
+
+ // Try to get the ivar offset address from the symbol table first using the
+ // name we created above
+ SymbolContextList sc_list;
+ Target &target = m_process->GetTarget();
+ target.GetImages().FindSymbolsWithNameAndType(ivar_const_str,
+ eSymbolTypeObjCIVar, sc_list);
+
+ addr_t ivar_offset_address = LLDB_INVALID_ADDRESS;
+
+ Status error;
+ SymbolContext ivar_offset_symbol;
+ if (sc_list.GetSize() == 1 &&
+ sc_list.GetContextAtIndex(0, ivar_offset_symbol)) {
+ if (ivar_offset_symbol.symbol)
+ ivar_offset_address =
+ ivar_offset_symbol.symbol->GetLoadAddress(&target);
+ }
+
+ // If we didn't get the ivar offset address from the symbol table, fall
+ // back to getting it from the runtime
+ if (ivar_offset_address == LLDB_INVALID_ADDRESS)
+ ivar_offset_address = LookupRuntimeSymbol(ivar_const_str);
+
+ if (ivar_offset_address != LLDB_INVALID_ADDRESS)
+ ivar_offset = m_process->ReadUnsignedIntegerFromMemory(
+ ivar_offset_address, 4, LLDB_INVALID_IVAR_OFFSET, error);
+ }
+ return ivar_offset;
+}
+
+// tagged pointers are special not-a-real-pointer values that contain both type
+// and value information this routine attempts to check with as little
+// computational effort as possible whether something could possibly be a
+// tagged pointer - false positives are possible but false negatives shouldn't
+bool AppleObjCRuntimeV2::IsTaggedPointer(addr_t ptr) {
+ if (!m_tagged_pointer_vendor_up)
+ return false;
+ return m_tagged_pointer_vendor_up->IsPossibleTaggedPointer(ptr);
+}
+
+class RemoteNXMapTable {
+public:
+ RemoteNXMapTable()
+ : m_count(0), m_num_buckets_minus_one(0),
+ m_buckets_ptr(LLDB_INVALID_ADDRESS), m_process(nullptr),
+ m_end_iterator(*this, -1), m_load_addr(LLDB_INVALID_ADDRESS),
+ m_map_pair_size(0), m_invalid_key(0) {}
+
+ void Dump() {
+ printf("RemoteNXMapTable.m_load_addr = 0x%" PRIx64 "\n", m_load_addr);
+ printf("RemoteNXMapTable.m_count = %u\n", m_count);
+ printf("RemoteNXMapTable.m_num_buckets_minus_one = %u\n",
+ m_num_buckets_minus_one);
+ printf("RemoteNXMapTable.m_buckets_ptr = 0x%" PRIX64 "\n", m_buckets_ptr);
+ }
+
+ bool ParseHeader(Process *process, lldb::addr_t load_addr) {
+ m_process = process;
+ m_load_addr = load_addr;
+ m_map_pair_size = m_process->GetAddressByteSize() * 2;
+ m_invalid_key =
+ m_process->GetAddressByteSize() == 8 ? UINT64_MAX : UINT32_MAX;
+ Status err;
+
+ // This currently holds true for all platforms we support, but we might
+ // need to change this to use get the actually byte size of "unsigned" from
+ // the target AST...
+ const uint32_t unsigned_byte_size = sizeof(uint32_t);
+ // Skip the prototype as we don't need it (const struct
+ // +NXMapTablePrototype *prototype)
+
+ bool success = true;
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ success = false;
+ else {
+ lldb::addr_t cursor = load_addr + m_process->GetAddressByteSize();
+
+ // unsigned count;
+ m_count = m_process->ReadUnsignedIntegerFromMemory(
+ cursor, unsigned_byte_size, 0, err);
+ if (m_count) {
+ cursor += unsigned_byte_size;
+
+ // unsigned nbBucketsMinusOne;
+ m_num_buckets_minus_one = m_process->ReadUnsignedIntegerFromMemory(
+ cursor, unsigned_byte_size, 0, err);
+ cursor += unsigned_byte_size;
+
+ // void *buckets;
+ m_buckets_ptr = m_process->ReadPointerFromMemory(cursor, err);
+
+ success = m_count > 0 && m_buckets_ptr != LLDB_INVALID_ADDRESS;
+ }
+ }
+
+ if (!success) {
+ m_count = 0;
+ m_num_buckets_minus_one = 0;
+ m_buckets_ptr = LLDB_INVALID_ADDRESS;
+ }
+ return success;
+ }
+
+ // const_iterator mimics NXMapState and its code comes from NXInitMapState
+ // and NXNextMapState.
+ typedef std::pair<ConstString, ObjCLanguageRuntime::ObjCISA> element;
+
+ friend class const_iterator;
+ class const_iterator {
+ public:
+ const_iterator(RemoteNXMapTable &parent, int index)
+ : m_parent(parent), m_index(index) {
+ AdvanceToValidIndex();
+ }
+
+ const_iterator(const const_iterator &rhs)
+ : m_parent(rhs.m_parent), m_index(rhs.m_index) {
+ // AdvanceToValidIndex() has been called by rhs already.
+ }
+
+ const_iterator &operator=(const const_iterator &rhs) {
+ // AdvanceToValidIndex() has been called by rhs already.
+ assert(&m_parent == &rhs.m_parent);
+ m_index = rhs.m_index;
+ return *this;
+ }
+
+ bool operator==(const const_iterator &rhs) const {
+ if (&m_parent != &rhs.m_parent)
+ return false;
+ if (m_index != rhs.m_index)
+ return false;
+
+ return true;
+ }
+
+ bool operator!=(const const_iterator &rhs) const {
+ return !(operator==(rhs));
+ }
+
+ const_iterator &operator++() {
+ AdvanceToValidIndex();
+ return *this;
+ }
+
+ const element operator*() const {
+ if (m_index == -1) {
+ // TODO find a way to make this an error, but not an assert
+ return element();
+ }
+
+ lldb::addr_t pairs_ptr = m_parent.m_buckets_ptr;
+ size_t map_pair_size = m_parent.m_map_pair_size;
+ lldb::addr_t pair_ptr = pairs_ptr + (m_index * map_pair_size);
+
+ Status err;
+
+ lldb::addr_t key =
+ m_parent.m_process->ReadPointerFromMemory(pair_ptr, err);
+ if (!err.Success())
+ return element();
+ lldb::addr_t value = m_parent.m_process->ReadPointerFromMemory(
+ pair_ptr + m_parent.m_process->GetAddressByteSize(), err);
+ if (!err.Success())
+ return element();
+
+ std::string key_string;
+
+ m_parent.m_process->ReadCStringFromMemory(key, key_string, err);
+ if (!err.Success())
+ return element();
+
+ return element(ConstString(key_string.c_str()),
+ (ObjCLanguageRuntime::ObjCISA)value);
+ }
+
+ private:
+ void AdvanceToValidIndex() {
+ if (m_index == -1)
+ return;
+
+ const lldb::addr_t pairs_ptr = m_parent.m_buckets_ptr;
+ const size_t map_pair_size = m_parent.m_map_pair_size;
+ const lldb::addr_t invalid_key = m_parent.m_invalid_key;
+ Status err;
+
+ while (m_index--) {
+ lldb::addr_t pair_ptr = pairs_ptr + (m_index * map_pair_size);
+ lldb::addr_t key =
+ m_parent.m_process->ReadPointerFromMemory(pair_ptr, err);
+
+ if (!err.Success()) {
+ m_index = -1;
+ return;
+ }
+
+ if (key != invalid_key)
+ return;
+ }
+ }
+ RemoteNXMapTable &m_parent;
+ int m_index;
+ };
+
+ const_iterator begin() {
+ return const_iterator(*this, m_num_buckets_minus_one + 1);
+ }
+
+ const_iterator end() { return m_end_iterator; }
+
+ uint32_t GetCount() const { return m_count; }
+
+ uint32_t GetBucketCount() const { return m_num_buckets_minus_one; }
+
+ lldb::addr_t GetBucketDataPointer() const { return m_buckets_ptr; }
+
+ lldb::addr_t GetTableLoadAddress() const { return m_load_addr; }
+
+private:
+ // contents of _NXMapTable struct
+ uint32_t m_count;
+ uint32_t m_num_buckets_minus_one;
+ lldb::addr_t m_buckets_ptr;
+ lldb_private::Process *m_process;
+ const_iterator m_end_iterator;
+ lldb::addr_t m_load_addr;
+ size_t m_map_pair_size;
+ lldb::addr_t m_invalid_key;
+};
+
+AppleObjCRuntimeV2::HashTableSignature::HashTableSignature()
+ : m_count(0), m_num_buckets(0), m_buckets_ptr(0) {}
+
+void AppleObjCRuntimeV2::HashTableSignature::UpdateSignature(
+ const RemoteNXMapTable &hash_table) {
+ m_count = hash_table.GetCount();
+ m_num_buckets = hash_table.GetBucketCount();
+ m_buckets_ptr = hash_table.GetBucketDataPointer();
+}
+
+bool AppleObjCRuntimeV2::HashTableSignature::NeedsUpdate(
+ Process *process, AppleObjCRuntimeV2 *runtime,
+ RemoteNXMapTable &hash_table) {
+ if (!hash_table.ParseHeader(process, runtime->GetISAHashTablePointer())) {
+ return false; // Failed to parse the header, no need to update anything
+ }
+
+ // Check with out current signature and return true if the count, number of
+ // buckets or the hash table address changes.
+ if (m_count == hash_table.GetCount() &&
+ m_num_buckets == hash_table.GetBucketCount() &&
+ m_buckets_ptr == hash_table.GetBucketDataPointer()) {
+ // Hash table hasn't changed
+ return false;
+ }
+ // Hash table data has changed, we need to update
+ return true;
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::GetClassDescriptorFromISA(ObjCISA isa) {
+ ObjCLanguageRuntime::ClassDescriptorSP class_descriptor_sp;
+ if (m_non_pointer_isa_cache_up)
+ class_descriptor_sp = m_non_pointer_isa_cache_up->GetClassDescriptor(isa);
+ if (!class_descriptor_sp)
+ class_descriptor_sp = ObjCLanguageRuntime::GetClassDescriptorFromISA(isa);
+ return class_descriptor_sp;
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::GetClassDescriptor(ValueObject &valobj) {
+ ClassDescriptorSP objc_class_sp;
+ if (valobj.IsBaseClass()) {
+ ValueObject *parent = valobj.GetParent();
+ // if I am my own parent, bail out of here fast..
+ if (parent && parent != &valobj) {
+ ClassDescriptorSP parent_descriptor_sp = GetClassDescriptor(*parent);
+ if (parent_descriptor_sp)
+ return parent_descriptor_sp->GetSuperclass();
+ }
+ return nullptr;
+ }
+ // if we get an invalid VO (which might still happen when playing around with
+ // pointers returned by the expression parser, don't consider this a valid
+ // ObjC object)
+ if (valobj.GetCompilerType().IsValid()) {
+ addr_t isa_pointer = valobj.GetPointerValue();
+
+ // tagged pointer
+ if (IsTaggedPointer(isa_pointer)) {
+ return m_tagged_pointer_vendor_up->GetClassDescriptor(isa_pointer);
+ } else {
+ ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ Status error;
+ ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error);
+ if (isa != LLDB_INVALID_ADDRESS) {
+ objc_class_sp = GetClassDescriptorFromISA(isa);
+ if (isa && !objc_class_sp) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("0x%" PRIx64
+ ": AppleObjCRuntimeV2::GetClassDescriptor() ISA was "
+ "not in class descriptor cache 0x%" PRIx64,
+ isa_pointer, isa);
+ }
+ }
+ }
+ }
+ }
+ return objc_class_sp;
+}
+
+lldb::addr_t AppleObjCRuntimeV2::GetTaggedPointerObfuscator() {
+ if (m_tagged_pointer_obfuscator != LLDB_INVALID_ADDRESS)
+ return m_tagged_pointer_obfuscator;
+
+
+ Process *process = GetProcess();
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (!objc_module_sp)
+ return LLDB_INVALID_ADDRESS;
+
+ static ConstString g_gdb_objc_obfuscator("objc_debug_taggedpointer_obfuscator");
+
+ const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
+ g_gdb_objc_obfuscator, lldb::eSymbolTypeAny);
+ if (symbol) {
+ lldb::addr_t g_gdb_obj_obfuscator_ptr =
+ symbol->GetLoadAddress(&process->GetTarget());
+
+ if (g_gdb_obj_obfuscator_ptr != LLDB_INVALID_ADDRESS) {
+ Status error;
+ m_tagged_pointer_obfuscator = process->ReadPointerFromMemory(
+ g_gdb_obj_obfuscator_ptr, error);
+ }
+ }
+ // If we don't have a correct value at this point, there must be no obfuscation.
+ if (m_tagged_pointer_obfuscator == LLDB_INVALID_ADDRESS)
+ m_tagged_pointer_obfuscator = 0;
+
+ return m_tagged_pointer_obfuscator;
+}
+
+lldb::addr_t AppleObjCRuntimeV2::GetISAHashTablePointer() {
+ if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) {
+ Process *process = GetProcess();
+
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (!objc_module_sp)
+ return LLDB_INVALID_ADDRESS;
+
+ static ConstString g_gdb_objc_realized_classes("gdb_objc_realized_classes");
+
+ const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
+ g_gdb_objc_realized_classes, lldb::eSymbolTypeAny);
+ if (symbol) {
+ lldb::addr_t gdb_objc_realized_classes_ptr =
+ symbol->GetLoadAddress(&process->GetTarget());
+
+ if (gdb_objc_realized_classes_ptr != LLDB_INVALID_ADDRESS) {
+ Status error;
+ m_isa_hash_table_ptr = process->ReadPointerFromMemory(
+ gdb_objc_realized_classes_ptr, error);
+ }
+ }
+ }
+ return m_isa_hash_table_ptr;
+}
+
+AppleObjCRuntimeV2::DescriptorMapUpdateResult
+AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(
+ RemoteNXMapTable &hash_table) {
+ Process *process = GetProcess();
+
+ if (process == nullptr)
+ return DescriptorMapUpdateResult::Fail();
+
+ uint32_t num_class_infos = 0;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TYPES));
+
+ ExecutionContext exe_ctx;
+
+ ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
+
+ if (!thread_sp)
+ return DescriptorMapUpdateResult::Fail();
+
+ thread_sp->CalculateExecutionContext(exe_ctx);
+ ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
+
+ if (!ast)
+ return DescriptorMapUpdateResult::Fail();
+
+ Address function_address;
+
+ DiagnosticManager diagnostics;
+
+ const uint32_t addr_size = process->GetAddressByteSize();
+
+ Status err;
+
+ // Read the total number of classes from the hash table
+ const uint32_t num_classes = hash_table.GetCount();
+ if (num_classes == 0) {
+ if (log)
+ log->Printf("No dynamic classes found in gdb_objc_realized_classes.");
+ return DescriptorMapUpdateResult::Success(0);
+ }
+
+ // Make some types for our arguments
+ CompilerType clang_uint32_t_type =
+ ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
+ CompilerType clang_void_pointer_type =
+ ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+ ValueList arguments;
+ FunctionCaller *get_class_info_function = nullptr;
+
+ if (!m_get_class_info_code) {
+ Status error;
+ m_get_class_info_code.reset(GetTargetRef().GetUtilityFunctionForLanguage(
+ g_get_dynamic_class_info_body, eLanguageTypeObjC,
+ g_get_dynamic_class_info_name, error));
+ if (error.Fail()) {
+ if (log)
+ log->Printf(
+ "Failed to get Utility Function for implementation lookup: %s",
+ error.AsCString());
+ m_get_class_info_code.reset();
+ } else {
+ diagnostics.Clear();
+
+ if (!m_get_class_info_code->Install(diagnostics, exe_ctx)) {
+ if (log) {
+ log->Printf("Failed to install implementation lookup");
+ diagnostics.Dump(log);
+ }
+ m_get_class_info_code.reset();
+ }
+ }
+ if (!m_get_class_info_code)
+ return DescriptorMapUpdateResult::Fail();
+
+ // Next make the runner function for our implementation utility function.
+ Value value;
+ value.SetValueType(Value::eValueTypeScalar);
+ value.SetCompilerType(clang_void_pointer_type);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+
+ value.SetValueType(Value::eValueTypeScalar);
+ value.SetCompilerType(clang_uint32_t_type);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+
+ get_class_info_function = m_get_class_info_code->MakeFunctionCaller(
+ clang_uint32_t_type, arguments, thread_sp, error);
+
+ if (error.Fail()) {
+ if (log)
+ log->Printf(
+ "Failed to make function caller for implementation lookup: %s.",
+ error.AsCString());
+ return DescriptorMapUpdateResult::Fail();
+ }
+ } else {
+ get_class_info_function = m_get_class_info_code->GetFunctionCaller();
+ if (!get_class_info_function) {
+ if (log) {
+ log->Printf("Failed to get implementation lookup function caller.");
+ diagnostics.Dump(log);
+ }
+
+ return DescriptorMapUpdateResult::Fail();
+ }
+ arguments = get_class_info_function->GetArgumentValues();
+ }
+
+ diagnostics.Clear();
+
+ const uint32_t class_info_byte_size = addr_size + 4;
+ const uint32_t class_infos_byte_size = num_classes * class_info_byte_size;
+ lldb::addr_t class_infos_addr = process->AllocateMemory(
+ class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err);
+
+ if (class_infos_addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("unable to allocate %" PRIu32
+ " bytes in process for shared cache read",
+ class_infos_byte_size);
+ return DescriptorMapUpdateResult::Fail();
+ }
+
+ std::lock_guard<std::mutex> guard(m_get_class_info_args_mutex);
+
+ // Fill in our function argument values
+ arguments.GetValueAtIndex(0)->GetScalar() = hash_table.GetTableLoadAddress();
+ arguments.GetValueAtIndex(1)->GetScalar() = class_infos_addr;
+ arguments.GetValueAtIndex(2)->GetScalar() = class_infos_byte_size;
+
+ // Only dump the runtime classes from the expression evaluation if the log is
+ // verbose:
+ Log *type_log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES);
+ bool dump_log = type_log && type_log->GetVerbose();
+
+ arguments.GetValueAtIndex(3)->GetScalar() = dump_log ? 1 : 0;
+
+ bool success = false;
+
+ diagnostics.Clear();
+
+ // Write our function arguments into the process so we can run our function
+ if (get_class_info_function->WriteFunctionArguments(
+ exe_ctx, m_get_class_info_args, arguments, diagnostics)) {
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(false);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetIsForUtilityExpr(true);
+
+ Value return_value;
+ return_value.SetValueType(Value::eValueTypeScalar);
+ // return_value.SetContext (Value::eContextTypeClangType,
+ // clang_uint32_t_type);
+ return_value.SetCompilerType(clang_uint32_t_type);
+ return_value.GetScalar() = 0;
+
+ diagnostics.Clear();
+
+ // Run the function
+ ExpressionResults results = get_class_info_function->ExecuteFunction(
+ exe_ctx, &m_get_class_info_args, options, diagnostics, return_value);
+
+ if (results == eExpressionCompleted) {
+ // The result is the number of ClassInfo structures that were filled in
+ num_class_infos = return_value.GetScalar().ULong();
+ if (log)
+ log->Printf("Discovered %u ObjC classes\n", num_class_infos);
+ if (num_class_infos > 0) {
+ // Read the ClassInfo structures
+ DataBufferHeap buffer(num_class_infos * class_info_byte_size, 0);
+ if (process->ReadMemory(class_infos_addr, buffer.GetBytes(),
+ buffer.GetByteSize(),
+ err) == buffer.GetByteSize()) {
+ DataExtractor class_infos_data(buffer.GetBytes(),
+ buffer.GetByteSize(),
+ process->GetByteOrder(), addr_size);
+ ParseClassInfoArray(class_infos_data, num_class_infos);
+ }
+ }
+ success = true;
+ } else {
+ if (log) {
+ log->Printf("Error evaluating our find class name function.");
+ diagnostics.Dump(log);
+ }
+ }
+ } else {
+ if (log) {
+ log->Printf("Error writing function arguments.");
+ diagnostics.Dump(log);
+ }
+ }
+
+ // Deallocate the memory we allocated for the ClassInfo array
+ process->DeallocateMemory(class_infos_addr);
+
+ return DescriptorMapUpdateResult(success, num_class_infos);
+}
+
+uint32_t AppleObjCRuntimeV2::ParseClassInfoArray(const DataExtractor &data,
+ uint32_t num_class_infos) {
+ // Parses an array of "num_class_infos" packed ClassInfo structures:
+ //
+ // struct ClassInfo
+ // {
+ // Class isa;
+ // uint32_t hash;
+ // } __attribute__((__packed__));
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
+ bool should_log = log && log->GetVerbose();
+
+ uint32_t num_parsed = 0;
+
+ // Iterate through all ClassInfo structures
+ lldb::offset_t offset = 0;
+ for (uint32_t i = 0; i < num_class_infos; ++i) {
+ ObjCISA isa = data.GetPointer(&offset);
+
+ if (isa == 0) {
+ if (should_log)
+ log->Printf(
+ "AppleObjCRuntimeV2 found NULL isa, ignoring this class info");
+ continue;
+ }
+ // Check if we already know about this ISA, if we do, the info will never
+ // change, so we can just skip it.
+ if (ISAIsCached(isa)) {
+ if (should_log)
+ log->Printf("AppleObjCRuntimeV2 found cached isa=0x%" PRIx64
+ ", ignoring this class info",
+ isa);
+ offset += 4;
+ } else {
+ // Read the 32 bit hash for the class name
+ const uint32_t name_hash = data.GetU32(&offset);
+ ClassDescriptorSP descriptor_sp(
+ new ClassDescriptorV2(*this, isa, nullptr));
+
+ // The code in g_get_shared_cache_class_info_body sets the value of the hash
+ // to 0 to signal a demangled symbol. We use class_getName() in that code to
+ // find the class name, but this returns a demangled name for Swift symbols.
+ // For those symbols, recompute the hash here by reading their name from the
+ // runtime.
+ if (name_hash)
+ AddClass(isa, descriptor_sp, name_hash);
+ else
+ AddClass(isa, descriptor_sp, descriptor_sp->GetClassName().AsCString(nullptr));
+ num_parsed++;
+ if (should_log)
+ log->Printf("AppleObjCRuntimeV2 added isa=0x%" PRIx64
+ ", hash=0x%8.8x, name=%s",
+ isa, name_hash,
+ descriptor_sp->GetClassName().AsCString("<unknown>"));
+ }
+ }
+ if (should_log)
+ log->Printf("AppleObjCRuntimeV2 parsed %" PRIu32 " class infos",
+ num_parsed);
+ return num_parsed;
+}
+
+AppleObjCRuntimeV2::DescriptorMapUpdateResult
+AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache() {
+ Process *process = GetProcess();
+
+ if (process == nullptr)
+ return DescriptorMapUpdateResult::Fail();
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TYPES));
+
+ ExecutionContext exe_ctx;
+
+ ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
+
+ if (!thread_sp)
+ return DescriptorMapUpdateResult::Fail();
+
+ thread_sp->CalculateExecutionContext(exe_ctx);
+ ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
+
+ if (!ast)
+ return DescriptorMapUpdateResult::Fail();
+
+ Address function_address;
+
+ DiagnosticManager diagnostics;
+
+ const uint32_t addr_size = process->GetAddressByteSize();
+
+ Status err;
+
+ uint32_t num_class_infos = 0;
+
+ const lldb::addr_t objc_opt_ptr = GetSharedCacheReadOnlyAddress();
+
+ if (objc_opt_ptr == LLDB_INVALID_ADDRESS)
+ return DescriptorMapUpdateResult::Fail();
+
+ const uint32_t num_classes = 128 * 1024;
+
+ // Make some types for our arguments
+ CompilerType clang_uint32_t_type =
+ ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
+ CompilerType clang_void_pointer_type =
+ ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+ ValueList arguments;
+ FunctionCaller *get_shared_cache_class_info_function = nullptr;
+
+ if (!m_get_shared_cache_class_info_code) {
+ Status error;
+
+ // If the inferior objc.dylib has the class_getNameRaw function,
+ // use that in our jitted expression. Else fall back to the old
+ // class_getName.
+ static ConstString g_class_getName_symbol_name("class_getName");
+ static ConstString g_class_getNameRaw_symbol_name("class_getNameRaw");
+ ConstString class_name_getter_function_name = g_class_getName_symbol_name;
+
+ ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process);
+ if (objc_runtime) {
+ const ModuleList &images = process->GetTarget().GetImages();
+ std::lock_guard<std::recursive_mutex> guard(images.GetMutex());
+ for (size_t i = 0; i < images.GetSize(); ++i) {
+ lldb::ModuleSP mod_sp = images.GetModuleAtIndexUnlocked(i);
+ if (objc_runtime->IsModuleObjCLibrary(mod_sp)) {
+ const Symbol *symbol =
+ mod_sp->FindFirstSymbolWithNameAndType(g_class_getNameRaw_symbol_name,
+ lldb::eSymbolTypeCode);
+ if (symbol &&
+ (symbol->ValueIsAddress() || symbol->GetAddressRef().IsValid())) {
+ class_name_getter_function_name = g_class_getNameRaw_symbol_name;
+ }
+ }
+ }
+ }
+
+ // Substitute in the correct class_getName / class_getNameRaw function name,
+ // concatenate the two parts of our expression text. The format string
+ // has two %s's, so provide the name twice.
+ int prefix_string_size = snprintf (nullptr, 0,
+ g_shared_cache_class_name_funcptr,
+ class_name_getter_function_name.AsCString(),
+ class_name_getter_function_name.AsCString());
+
+ char *class_name_func_ptr_expr = (char*) malloc (prefix_string_size + 1);
+ snprintf (class_name_func_ptr_expr, prefix_string_size + 1,
+ g_shared_cache_class_name_funcptr,
+ class_name_getter_function_name.AsCString(),
+ class_name_getter_function_name.AsCString());
+ std::string shared_class_expression = class_name_func_ptr_expr;
+ shared_class_expression += g_get_shared_cache_class_info_body;
+ free (class_name_func_ptr_expr);
+
+ m_get_shared_cache_class_info_code.reset(
+ GetTargetRef().GetUtilityFunctionForLanguage(
+ shared_class_expression.c_str(), eLanguageTypeObjC,
+ g_get_shared_cache_class_info_name, error));
+ if (error.Fail()) {
+ if (log)
+ log->Printf(
+ "Failed to get Utility function for implementation lookup: %s.",
+ error.AsCString());
+ m_get_shared_cache_class_info_code.reset();
+ } else {
+ diagnostics.Clear();
+
+ if (!m_get_shared_cache_class_info_code->Install(diagnostics, exe_ctx)) {
+ if (log) {
+ log->Printf("Failed to install implementation lookup.");
+ diagnostics.Dump(log);
+ }
+ m_get_shared_cache_class_info_code.reset();
+ }
+ }
+
+ if (!m_get_shared_cache_class_info_code)
+ return DescriptorMapUpdateResult::Fail();
+
+ // Next make the function caller for our implementation utility function.
+ Value value;
+ value.SetValueType(Value::eValueTypeScalar);
+ // value.SetContext (Value::eContextTypeClangType, clang_void_pointer_type);
+ value.SetCompilerType(clang_void_pointer_type);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+
+ value.SetValueType(Value::eValueTypeScalar);
+ // value.SetContext (Value::eContextTypeClangType, clang_uint32_t_type);
+ value.SetCompilerType(clang_uint32_t_type);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+
+ get_shared_cache_class_info_function =
+ m_get_shared_cache_class_info_code->MakeFunctionCaller(
+ clang_uint32_t_type, arguments, thread_sp, error);
+
+ if (get_shared_cache_class_info_function == nullptr)
+ return DescriptorMapUpdateResult::Fail();
+
+ } else {
+ get_shared_cache_class_info_function =
+ m_get_shared_cache_class_info_code->GetFunctionCaller();
+ if (get_shared_cache_class_info_function == nullptr)
+ return DescriptorMapUpdateResult::Fail();
+ arguments = get_shared_cache_class_info_function->GetArgumentValues();
+ }
+
+ diagnostics.Clear();
+
+ const uint32_t class_info_byte_size = addr_size + 4;
+ const uint32_t class_infos_byte_size = num_classes * class_info_byte_size;
+ lldb::addr_t class_infos_addr = process->AllocateMemory(
+ class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err);
+
+ if (class_infos_addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("unable to allocate %" PRIu32
+ " bytes in process for shared cache read",
+ class_infos_byte_size);
+ return DescriptorMapUpdateResult::Fail();
+ }
+
+ std::lock_guard<std::mutex> guard(m_get_shared_cache_class_info_args_mutex);
+
+ // Fill in our function argument values
+ arguments.GetValueAtIndex(0)->GetScalar() = objc_opt_ptr;
+ arguments.GetValueAtIndex(1)->GetScalar() = class_infos_addr;
+ arguments.GetValueAtIndex(2)->GetScalar() = class_infos_byte_size;
+ // Only dump the runtime classes from the expression evaluation if the log is
+ // verbose:
+ Log *type_log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES);
+ bool dump_log = type_log && type_log->GetVerbose();
+
+ arguments.GetValueAtIndex(3)->GetScalar() = dump_log ? 1 : 0;
+
+ bool success = false;
+
+ diagnostics.Clear();
+
+ // Write our function arguments into the process so we can run our function
+ if (get_shared_cache_class_info_function->WriteFunctionArguments(
+ exe_ctx, m_get_shared_cache_class_info_args, arguments,
+ diagnostics)) {
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(false);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetIsForUtilityExpr(true);
+
+ Value return_value;
+ return_value.SetValueType(Value::eValueTypeScalar);
+ // return_value.SetContext (Value::eContextTypeClangType,
+ // clang_uint32_t_type);
+ return_value.SetCompilerType(clang_uint32_t_type);
+ return_value.GetScalar() = 0;
+
+ diagnostics.Clear();
+
+ // Run the function
+ ExpressionResults results =
+ get_shared_cache_class_info_function->ExecuteFunction(
+ exe_ctx, &m_get_shared_cache_class_info_args, options, diagnostics,
+ return_value);
+
+ if (results == eExpressionCompleted) {
+ // The result is the number of ClassInfo structures that were filled in
+ num_class_infos = return_value.GetScalar().ULong();
+ if (log)
+ log->Printf("Discovered %u ObjC classes in shared cache\n",
+ num_class_infos);
+ assert(num_class_infos <= num_classes);
+ if (num_class_infos > 0) {
+ if (num_class_infos > num_classes) {
+ num_class_infos = num_classes;
+
+ success = false;
+ } else {
+ success = true;
+ }
+
+ // Read the ClassInfo structures
+ DataBufferHeap buffer(num_class_infos * class_info_byte_size, 0);
+ if (process->ReadMemory(class_infos_addr, buffer.GetBytes(),
+ buffer.GetByteSize(),
+ err) == buffer.GetByteSize()) {
+ DataExtractor class_infos_data(buffer.GetBytes(),
+ buffer.GetByteSize(),
+ process->GetByteOrder(), addr_size);
+
+ ParseClassInfoArray(class_infos_data, num_class_infos);
+ }
+ } else {
+ success = true;
+ }
+ } else {
+ if (log) {
+ log->Printf("Error evaluating our find class name function.");
+ diagnostics.Dump(log);
+ }
+ }
+ } else {
+ if (log) {
+ log->Printf("Error writing function arguments.");
+ diagnostics.Dump(log);
+ }
+ }
+
+ // Deallocate the memory we allocated for the ClassInfo array
+ process->DeallocateMemory(class_infos_addr);
+
+ return DescriptorMapUpdateResult(success, num_class_infos);
+}
+
+bool AppleObjCRuntimeV2::UpdateISAToDescriptorMapFromMemory(
+ RemoteNXMapTable &hash_table) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TYPES));
+
+ Process *process = GetProcess();
+
+ if (process == nullptr)
+ return false;
+
+ uint32_t num_map_table_isas = 0;
+
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (objc_module_sp) {
+ for (RemoteNXMapTable::element elt : hash_table) {
+ ++num_map_table_isas;
+
+ if (ISAIsCached(elt.second))
+ continue;
+
+ ClassDescriptorSP descriptor_sp = ClassDescriptorSP(
+ new ClassDescriptorV2(*this, elt.second, elt.first.AsCString()));
+
+ if (log && log->GetVerbose())
+ log->Printf("AppleObjCRuntimeV2 added (ObjCISA)0x%" PRIx64
+ " (%s) from dynamic table to isa->descriptor cache",
+ elt.second, elt.first.AsCString());
+
+ AddClass(elt.second, descriptor_sp, elt.first.AsCString());
+ }
+ }
+
+ return num_map_table_isas > 0;
+}
+
+lldb::addr_t AppleObjCRuntimeV2::GetSharedCacheReadOnlyAddress() {
+ Process *process = GetProcess();
+
+ if (process) {
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (objc_module_sp) {
+ ObjectFile *objc_object = objc_module_sp->GetObjectFile();
+
+ if (objc_object) {
+ SectionList *section_list = objc_module_sp->GetSectionList();
+
+ if (section_list) {
+ SectionSP text_segment_sp(
+ section_list->FindSectionByName(ConstString("__TEXT")));
+
+ if (text_segment_sp) {
+ SectionSP objc_opt_section_sp(
+ text_segment_sp->GetChildren().FindSectionByName(
+ ConstString("__objc_opt_ro")));
+
+ if (objc_opt_section_sp) {
+ return objc_opt_section_sp->GetLoadBaseAddress(
+ &process->GetTarget());
+ }
+ }
+ }
+ }
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+void AppleObjCRuntimeV2::UpdateISAToDescriptorMapIfNeeded() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TYPES));
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ // Else we need to check with our process to see when the map was updated.
+ Process *process = GetProcess();
+
+ if (process) {
+ RemoteNXMapTable hash_table;
+
+ // Update the process stop ID that indicates the last time we updated the
+ // map, whether it was successful or not.
+ m_isa_to_descriptor_stop_id = process->GetStopID();
+
+ if (!m_hash_signature.NeedsUpdate(process, this, hash_table))
+ return;
+
+ m_hash_signature.UpdateSignature(hash_table);
+
+ // Grab the dynamically loaded objc classes from the hash table in memory
+ DescriptorMapUpdateResult dynamic_update_result =
+ UpdateISAToDescriptorMapDynamic(hash_table);
+
+ // Now get the objc classes that are baked into the Objective-C runtime in
+ // the shared cache, but only once per process as this data never changes
+ if (!m_loaded_objc_opt) {
+ // it is legitimately possible for the shared cache to be empty - in that
+ // case, the dynamic hash table will contain all the class information we
+ // need; the situation we're trying to detect is one where we aren't
+ // seeing class information from the runtime - in order to detect that
+ // vs. just the shared cache being empty or sparsely populated, we set an
+ // arbitrary (very low) threshold for the number of classes that we want
+ // to see in a "good" scenario - anything below that is suspicious
+ // (Foundation alone has thousands of classes)
+ const uint32_t num_classes_to_warn_at = 500;
+
+ DescriptorMapUpdateResult shared_cache_update_result =
+ UpdateISAToDescriptorMapSharedCache();
+
+ if (log)
+ log->Printf("attempted to read objc class data - results: "
+ "[dynamic_update]: ran: %s, count: %" PRIu32
+ " [shared_cache_update]: ran: %s, count: %" PRIu32,
+ dynamic_update_result.m_update_ran ? "yes" : "no",
+ dynamic_update_result.m_num_found,
+ shared_cache_update_result.m_update_ran ? "yes" : "no",
+ shared_cache_update_result.m_num_found);
+
+ // warn if:
+ // - we could not run either expression
+ // - we found fewer than num_classes_to_warn_at classes total
+ if ((!shared_cache_update_result.m_update_ran) ||
+ (!dynamic_update_result.m_update_ran))
+ WarnIfNoClassesCached(
+ SharedCacheWarningReason::eExpressionExecutionFailure);
+ else if (dynamic_update_result.m_num_found +
+ shared_cache_update_result.m_num_found <
+ num_classes_to_warn_at)
+ WarnIfNoClassesCached(SharedCacheWarningReason::eNotEnoughClassesRead);
+ else
+ m_loaded_objc_opt = true;
+ }
+ } else {
+ m_isa_to_descriptor_stop_id = UINT32_MAX;
+ }
+}
+
+static bool DoesProcessHaveSharedCache(Process &process) {
+ PlatformSP platform_sp = process.GetTarget().GetPlatform();
+ if (!platform_sp)
+ return true; // this should not happen
+
+ ConstString platform_plugin_name = platform_sp->GetPluginName();
+ if (platform_plugin_name) {
+ llvm::StringRef platform_plugin_name_sr =
+ platform_plugin_name.GetStringRef();
+ if (platform_plugin_name_sr.endswith("-simulator"))
+ return false;
+ }
+
+ return true;
+}
+
+void AppleObjCRuntimeV2::WarnIfNoClassesCached(
+ SharedCacheWarningReason reason) {
+ if (m_noclasses_warning_emitted)
+ return;
+
+ if (GetProcess() && !DoesProcessHaveSharedCache(*GetProcess())) {
+ // Simulators do not have the objc_opt_ro class table so don't actually
+ // complain to the user
+ m_noclasses_warning_emitted = true;
+ return;
+ }
+
+ Debugger &debugger(GetProcess()->GetTarget().GetDebugger());
+ if (auto stream = debugger.GetAsyncOutputStream()) {
+ switch (reason) {
+ case SharedCacheWarningReason::eNotEnoughClassesRead:
+ stream->PutCString("warning: could not find Objective-C class data in "
+ "the process. This may reduce the quality of type "
+ "information available.\n");
+ m_noclasses_warning_emitted = true;
+ break;
+ case SharedCacheWarningReason::eExpressionExecutionFailure:
+ stream->PutCString("warning: could not execute support code to read "
+ "Objective-C class data in the process. This may "
+ "reduce the quality of type information available.\n");
+ m_noclasses_warning_emitted = true;
+ break;
+ }
+ }
+}
+
+ConstString
+AppleObjCRuntimeV2::GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa) {
+ if (isa == g_objc_Tagged_ISA) {
+ static const ConstString g_objc_tagged_isa_name("_lldb_Tagged_ObjC_ISA");
+ return g_objc_tagged_isa_name;
+ }
+ if (isa == g_objc_Tagged_ISA_NSAtom) {
+ static const ConstString g_objc_tagged_isa_nsatom_name("NSAtom");
+ return g_objc_tagged_isa_nsatom_name;
+ }
+ if (isa == g_objc_Tagged_ISA_NSNumber) {
+ static const ConstString g_objc_tagged_isa_nsnumber_name("NSNumber");
+ return g_objc_tagged_isa_nsnumber_name;
+ }
+ if (isa == g_objc_Tagged_ISA_NSDateTS) {
+ static const ConstString g_objc_tagged_isa_nsdatets_name("NSDateTS");
+ return g_objc_tagged_isa_nsdatets_name;
+ }
+ if (isa == g_objc_Tagged_ISA_NSManagedObject) {
+ static const ConstString g_objc_tagged_isa_nsmanagedobject_name(
+ "NSManagedObject");
+ return g_objc_tagged_isa_nsmanagedobject_name;
+ }
+ if (isa == g_objc_Tagged_ISA_NSDate) {
+ static const ConstString g_objc_tagged_isa_nsdate_name("NSDate");
+ return g_objc_tagged_isa_nsdate_name;
+ }
+ return ObjCLanguageRuntime::GetActualTypeName(isa);
+}
+
+DeclVendor *AppleObjCRuntimeV2::GetDeclVendor() {
+ if (!m_decl_vendor_up)
+ m_decl_vendor_up.reset(new AppleObjCDeclVendor(*this));
+
+ return m_decl_vendor_up.get();
+}
+
+lldb::addr_t AppleObjCRuntimeV2::LookupRuntimeSymbol(ConstString name) {
+ lldb::addr_t ret = LLDB_INVALID_ADDRESS;
+
+ const char *name_cstr = name.AsCString();
+
+ if (name_cstr) {
+ llvm::StringRef name_strref(name_cstr);
+
+ static const llvm::StringRef ivar_prefix("OBJC_IVAR_$_");
+ static const llvm::StringRef class_prefix("OBJC_CLASS_$_");
+
+ if (name_strref.startswith(ivar_prefix)) {
+ llvm::StringRef ivar_skipped_prefix =
+ name_strref.substr(ivar_prefix.size());
+ std::pair<llvm::StringRef, llvm::StringRef> class_and_ivar =
+ ivar_skipped_prefix.split('.');
+
+ if (class_and_ivar.first.size() && class_and_ivar.second.size()) {
+ const ConstString class_name_cs(class_and_ivar.first);
+ ClassDescriptorSP descriptor =
+ ObjCLanguageRuntime::GetClassDescriptorFromClassName(class_name_cs);
+
+ if (descriptor) {
+ const ConstString ivar_name_cs(class_and_ivar.second);
+ const char *ivar_name_cstr = ivar_name_cs.AsCString();
+
+ auto ivar_func = [&ret, ivar_name_cstr](
+ const char *name, const char *type, lldb::addr_t offset_addr,
+ uint64_t size) -> lldb::addr_t {
+ if (!strcmp(name, ivar_name_cstr)) {
+ ret = offset_addr;
+ return true;
+ }
+ return false;
+ };
+
+ descriptor->Describe(
+ std::function<void(ObjCISA)>(nullptr),
+ std::function<bool(const char *, const char *)>(nullptr),
+ std::function<bool(const char *, const char *)>(nullptr),
+ ivar_func);
+ }
+ }
+ } else if (name_strref.startswith(class_prefix)) {
+ llvm::StringRef class_skipped_prefix =
+ name_strref.substr(class_prefix.size());
+ const ConstString class_name_cs(class_skipped_prefix);
+ ClassDescriptorSP descriptor =
+ GetClassDescriptorFromClassName(class_name_cs);
+
+ if (descriptor)
+ ret = descriptor->GetISA();
+ }
+ }
+
+ return ret;
+}
+
+AppleObjCRuntimeV2::NonPointerISACache *
+AppleObjCRuntimeV2::NonPointerISACache::CreateInstance(
+ AppleObjCRuntimeV2 &runtime, const lldb::ModuleSP &objc_module_sp) {
+ Process *process(runtime.GetProcess());
+
+ Status error;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
+
+ auto objc_debug_isa_magic_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_isa_magic_mask"), objc_module_sp, error);
+ if (error.Fail())
+ return nullptr;
+
+ auto objc_debug_isa_magic_value = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_isa_magic_value"), objc_module_sp,
+ error);
+ if (error.Fail())
+ return nullptr;
+
+ auto objc_debug_isa_class_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_isa_class_mask"), objc_module_sp, error);
+ if (error.Fail())
+ return nullptr;
+
+ if (log)
+ log->PutCString("AOCRT::NPI: Found all the non-indexed ISA masks");
+
+ bool foundError = false;
+ auto objc_debug_indexed_isa_magic_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_indexed_isa_magic_mask"), objc_module_sp,
+ error);
+ foundError |= error.Fail();
+
+ auto objc_debug_indexed_isa_magic_value = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_indexed_isa_magic_value"),
+ objc_module_sp, error);
+ foundError |= error.Fail();
+
+ auto objc_debug_indexed_isa_index_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_indexed_isa_index_mask"), objc_module_sp,
+ error);
+ foundError |= error.Fail();
+
+ auto objc_debug_indexed_isa_index_shift = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_indexed_isa_index_shift"),
+ objc_module_sp, error);
+ foundError |= error.Fail();
+
+ auto objc_indexed_classes =
+ ExtractRuntimeGlobalSymbol(process, ConstString("objc_indexed_classes"),
+ objc_module_sp, error, false);
+ foundError |= error.Fail();
+
+ if (log)
+ log->PutCString("AOCRT::NPI: Found all the indexed ISA masks");
+
+ // we might want to have some rules to outlaw these other values (e.g if the
+ // mask is zero but the value is non-zero, ...)
+
+ return new NonPointerISACache(
+ runtime, objc_module_sp, objc_debug_isa_class_mask,
+ objc_debug_isa_magic_mask, objc_debug_isa_magic_value,
+ objc_debug_indexed_isa_magic_mask, objc_debug_indexed_isa_magic_value,
+ objc_debug_indexed_isa_index_mask, objc_debug_indexed_isa_index_shift,
+ foundError ? 0 : objc_indexed_classes);
+}
+
+AppleObjCRuntimeV2::TaggedPointerVendorV2 *
+AppleObjCRuntimeV2::TaggedPointerVendorV2::CreateInstance(
+ AppleObjCRuntimeV2 &runtime, const lldb::ModuleSP &objc_module_sp) {
+ Process *process(runtime.GetProcess());
+
+ Status error;
+
+ auto objc_debug_taggedpointer_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_mask"), objc_module_sp,
+ error);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ auto objc_debug_taggedpointer_slot_shift = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_slot_shift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ auto objc_debug_taggedpointer_slot_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_slot_mask"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ auto objc_debug_taggedpointer_payload_lshift = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_payload_lshift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ auto objc_debug_taggedpointer_payload_rshift = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_payload_rshift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ auto objc_debug_taggedpointer_classes = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_classes"), objc_module_sp,
+ error, false);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ // try to detect the "extended tagged pointer" variables - if any are
+ // missing, use the non-extended vendor
+ do {
+ auto objc_debug_taggedpointer_ext_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_mask"),
+ objc_module_sp, error);
+ if (error.Fail())
+ break;
+
+ auto objc_debug_taggedpointer_ext_slot_shift = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_slot_shift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ break;
+
+ auto objc_debug_taggedpointer_ext_slot_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_slot_mask"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ break;
+
+ auto objc_debug_taggedpointer_ext_classes = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_classes"),
+ objc_module_sp, error, false);
+ if (error.Fail())
+ break;
+
+ auto objc_debug_taggedpointer_ext_payload_lshift =
+ ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_payload_lshift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ break;
+
+ auto objc_debug_taggedpointer_ext_payload_rshift =
+ ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_payload_rshift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ break;
+
+ return new TaggedPointerVendorExtended(
+ runtime, objc_debug_taggedpointer_mask,
+ objc_debug_taggedpointer_ext_mask, objc_debug_taggedpointer_slot_shift,
+ objc_debug_taggedpointer_ext_slot_shift,
+ objc_debug_taggedpointer_slot_mask,
+ objc_debug_taggedpointer_ext_slot_mask,
+ objc_debug_taggedpointer_payload_lshift,
+ objc_debug_taggedpointer_payload_rshift,
+ objc_debug_taggedpointer_ext_payload_lshift,
+ objc_debug_taggedpointer_ext_payload_rshift,
+ objc_debug_taggedpointer_classes, objc_debug_taggedpointer_ext_classes);
+ } while (false);
+
+ // we might want to have some rules to outlaw these values (e.g if the
+ // table's address is zero)
+
+ return new TaggedPointerVendorRuntimeAssisted(
+ runtime, objc_debug_taggedpointer_mask,
+ objc_debug_taggedpointer_slot_shift, objc_debug_taggedpointer_slot_mask,
+ objc_debug_taggedpointer_payload_lshift,
+ objc_debug_taggedpointer_payload_rshift,
+ objc_debug_taggedpointer_classes);
+}
+
+bool AppleObjCRuntimeV2::TaggedPointerVendorLegacy::IsPossibleTaggedPointer(
+ lldb::addr_t ptr) {
+ return (ptr & 1);
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::TaggedPointerVendorLegacy::GetClassDescriptor(
+ lldb::addr_t ptr) {
+ if (!IsPossibleTaggedPointer(ptr))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ uint32_t foundation_version = m_runtime.GetFoundationVersion();
+
+ if (foundation_version == LLDB_INVALID_MODULE_VERSION)
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ uint64_t class_bits = (ptr & 0xE) >> 1;
+ ConstString name;
+
+ static ConstString g_NSAtom("NSAtom");
+ static ConstString g_NSNumber("NSNumber");
+ static ConstString g_NSDateTS("NSDateTS");
+ static ConstString g_NSManagedObject("NSManagedObject");
+ static ConstString g_NSDate("NSDate");
+
+ if (foundation_version >= 900) {
+ switch (class_bits) {
+ case 0:
+ name = g_NSAtom;
+ break;
+ case 3:
+ name = g_NSNumber;
+ break;
+ case 4:
+ name = g_NSDateTS;
+ break;
+ case 5:
+ name = g_NSManagedObject;
+ break;
+ case 6:
+ name = g_NSDate;
+ break;
+ default:
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ }
+ } else {
+ switch (class_bits) {
+ case 1:
+ name = g_NSNumber;
+ break;
+ case 5:
+ name = g_NSManagedObject;
+ break;
+ case 6:
+ name = g_NSDate;
+ break;
+ case 7:
+ name = g_NSDateTS;
+ break;
+ default:
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ }
+ }
+
+ lldb::addr_t unobfuscated = ptr ^ m_runtime.GetTaggedPointerObfuscator();
+ return ClassDescriptorSP(new ClassDescriptorV2Tagged(name, unobfuscated));
+}
+
+AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted::
+ TaggedPointerVendorRuntimeAssisted(
+ AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask,
+ uint32_t objc_debug_taggedpointer_slot_shift,
+ uint32_t objc_debug_taggedpointer_slot_mask,
+ uint32_t objc_debug_taggedpointer_payload_lshift,
+ uint32_t objc_debug_taggedpointer_payload_rshift,
+ lldb::addr_t objc_debug_taggedpointer_classes)
+ : TaggedPointerVendorV2(runtime), m_cache(),
+ m_objc_debug_taggedpointer_mask(objc_debug_taggedpointer_mask),
+ m_objc_debug_taggedpointer_slot_shift(
+ objc_debug_taggedpointer_slot_shift),
+ m_objc_debug_taggedpointer_slot_mask(objc_debug_taggedpointer_slot_mask),
+ m_objc_debug_taggedpointer_payload_lshift(
+ objc_debug_taggedpointer_payload_lshift),
+ m_objc_debug_taggedpointer_payload_rshift(
+ objc_debug_taggedpointer_payload_rshift),
+ m_objc_debug_taggedpointer_classes(objc_debug_taggedpointer_classes) {}
+
+bool AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted::
+ IsPossibleTaggedPointer(lldb::addr_t ptr) {
+ return (ptr & m_objc_debug_taggedpointer_mask) != 0;
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted::GetClassDescriptor(
+ lldb::addr_t ptr) {
+ ClassDescriptorSP actual_class_descriptor_sp;
+ uint64_t data_payload;
+ uint64_t unobfuscated = (ptr) ^ m_runtime.GetTaggedPointerObfuscator();
+
+ if (!IsPossibleTaggedPointer(unobfuscated))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ uintptr_t slot = (ptr >> m_objc_debug_taggedpointer_slot_shift) &
+ m_objc_debug_taggedpointer_slot_mask;
+
+ CacheIterator iterator = m_cache.find(slot), end = m_cache.end();
+ if (iterator != end) {
+ actual_class_descriptor_sp = iterator->second;
+ } else {
+ Process *process(m_runtime.GetProcess());
+ uintptr_t slot_ptr = slot * process->GetAddressByteSize() +
+ m_objc_debug_taggedpointer_classes;
+ Status error;
+ uintptr_t slot_data = process->ReadPointerFromMemory(slot_ptr, error);
+ if (error.Fail() || slot_data == 0 ||
+ slot_data == uintptr_t(LLDB_INVALID_ADDRESS))
+ return nullptr;
+ actual_class_descriptor_sp =
+ m_runtime.GetClassDescriptorFromISA((ObjCISA)slot_data);
+ if (!actual_class_descriptor_sp)
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ m_cache[slot] = actual_class_descriptor_sp;
+ }
+
+ data_payload =
+ (((uint64_t)unobfuscated << m_objc_debug_taggedpointer_payload_lshift) >>
+ m_objc_debug_taggedpointer_payload_rshift);
+
+ return ClassDescriptorSP(
+ new ClassDescriptorV2Tagged(actual_class_descriptor_sp, data_payload));
+}
+
+AppleObjCRuntimeV2::TaggedPointerVendorExtended::TaggedPointerVendorExtended(
+ AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask,
+ uint64_t objc_debug_taggedpointer_ext_mask,
+ uint32_t objc_debug_taggedpointer_slot_shift,
+ uint32_t objc_debug_taggedpointer_ext_slot_shift,
+ uint32_t objc_debug_taggedpointer_slot_mask,
+ uint32_t objc_debug_taggedpointer_ext_slot_mask,
+ uint32_t objc_debug_taggedpointer_payload_lshift,
+ uint32_t objc_debug_taggedpointer_payload_rshift,
+ uint32_t objc_debug_taggedpointer_ext_payload_lshift,
+ uint32_t objc_debug_taggedpointer_ext_payload_rshift,
+ lldb::addr_t objc_debug_taggedpointer_classes,
+ lldb::addr_t objc_debug_taggedpointer_ext_classes)
+ : TaggedPointerVendorRuntimeAssisted(
+ runtime, objc_debug_taggedpointer_mask,
+ objc_debug_taggedpointer_slot_shift,
+ objc_debug_taggedpointer_slot_mask,
+ objc_debug_taggedpointer_payload_lshift,
+ objc_debug_taggedpointer_payload_rshift,
+ objc_debug_taggedpointer_classes),
+ m_ext_cache(),
+ m_objc_debug_taggedpointer_ext_mask(objc_debug_taggedpointer_ext_mask),
+ m_objc_debug_taggedpointer_ext_slot_shift(
+ objc_debug_taggedpointer_ext_slot_shift),
+ m_objc_debug_taggedpointer_ext_slot_mask(
+ objc_debug_taggedpointer_ext_slot_mask),
+ m_objc_debug_taggedpointer_ext_payload_lshift(
+ objc_debug_taggedpointer_ext_payload_lshift),
+ m_objc_debug_taggedpointer_ext_payload_rshift(
+ objc_debug_taggedpointer_ext_payload_rshift),
+ m_objc_debug_taggedpointer_ext_classes(
+ objc_debug_taggedpointer_ext_classes) {}
+
+bool AppleObjCRuntimeV2::TaggedPointerVendorExtended::
+ IsPossibleExtendedTaggedPointer(lldb::addr_t ptr) {
+ if (!IsPossibleTaggedPointer(ptr))
+ return false;
+
+ if (m_objc_debug_taggedpointer_ext_mask == 0)
+ return false;
+
+ return ((ptr & m_objc_debug_taggedpointer_ext_mask) ==
+ m_objc_debug_taggedpointer_ext_mask);
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::TaggedPointerVendorExtended::GetClassDescriptor(
+ lldb::addr_t ptr) {
+ ClassDescriptorSP actual_class_descriptor_sp;
+ uint64_t data_payload;
+ uint64_t unobfuscated = (ptr) ^ m_runtime.GetTaggedPointerObfuscator();
+
+ if (!IsPossibleTaggedPointer(unobfuscated))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ if (!IsPossibleExtendedTaggedPointer(unobfuscated))
+ return this->TaggedPointerVendorRuntimeAssisted::GetClassDescriptor(ptr);
+
+ uintptr_t slot = (ptr >> m_objc_debug_taggedpointer_ext_slot_shift) &
+ m_objc_debug_taggedpointer_ext_slot_mask;
+
+ CacheIterator iterator = m_ext_cache.find(slot), end = m_ext_cache.end();
+ if (iterator != end) {
+ actual_class_descriptor_sp = iterator->second;
+ } else {
+ Process *process(m_runtime.GetProcess());
+ uintptr_t slot_ptr = slot * process->GetAddressByteSize() +
+ m_objc_debug_taggedpointer_ext_classes;
+ Status error;
+ uintptr_t slot_data = process->ReadPointerFromMemory(slot_ptr, error);
+ if (error.Fail() || slot_data == 0 ||
+ slot_data == uintptr_t(LLDB_INVALID_ADDRESS))
+ return nullptr;
+ actual_class_descriptor_sp =
+ m_runtime.GetClassDescriptorFromISA((ObjCISA)slot_data);
+ if (!actual_class_descriptor_sp)
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ m_ext_cache[slot] = actual_class_descriptor_sp;
+ }
+
+ data_payload =
+ (((uint64_t)unobfuscated << m_objc_debug_taggedpointer_ext_payload_lshift) >>
+ m_objc_debug_taggedpointer_ext_payload_rshift);
+
+ return ClassDescriptorSP(
+ new ClassDescriptorV2Tagged(actual_class_descriptor_sp, data_payload));
+}
+
+AppleObjCRuntimeV2::NonPointerISACache::NonPointerISACache(
+ AppleObjCRuntimeV2 &runtime, const ModuleSP &objc_module_sp,
+ uint64_t objc_debug_isa_class_mask, uint64_t objc_debug_isa_magic_mask,
+ uint64_t objc_debug_isa_magic_value,
+ uint64_t objc_debug_indexed_isa_magic_mask,
+ uint64_t objc_debug_indexed_isa_magic_value,
+ uint64_t objc_debug_indexed_isa_index_mask,
+ uint64_t objc_debug_indexed_isa_index_shift,
+ lldb::addr_t objc_indexed_classes)
+ : m_runtime(runtime), m_cache(), m_objc_module_wp(objc_module_sp),
+ m_objc_debug_isa_class_mask(objc_debug_isa_class_mask),
+ m_objc_debug_isa_magic_mask(objc_debug_isa_magic_mask),
+ m_objc_debug_isa_magic_value(objc_debug_isa_magic_value),
+ m_objc_debug_indexed_isa_magic_mask(objc_debug_indexed_isa_magic_mask),
+ m_objc_debug_indexed_isa_magic_value(objc_debug_indexed_isa_magic_value),
+ m_objc_debug_indexed_isa_index_mask(objc_debug_indexed_isa_index_mask),
+ m_objc_debug_indexed_isa_index_shift(objc_debug_indexed_isa_index_shift),
+ m_objc_indexed_classes(objc_indexed_classes), m_indexed_isa_cache() {}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::NonPointerISACache::GetClassDescriptor(ObjCISA isa) {
+ ObjCISA real_isa = 0;
+ if (!EvaluateNonPointerISA(isa, real_isa))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ auto cache_iter = m_cache.find(real_isa);
+ if (cache_iter != m_cache.end())
+ return cache_iter->second;
+ auto descriptor_sp =
+ m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA(real_isa);
+ if (descriptor_sp) // cache only positive matches since the table might grow
+ m_cache[real_isa] = descriptor_sp;
+ return descriptor_sp;
+}
+
+bool AppleObjCRuntimeV2::NonPointerISACache::EvaluateNonPointerISA(
+ ObjCISA isa, ObjCISA &ret_isa) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
+
+ if (log)
+ log->Printf("AOCRT::NPI Evalulate(isa = 0x%" PRIx64 ")", (uint64_t)isa);
+
+ if ((isa & ~m_objc_debug_isa_class_mask) == 0)
+ return false;
+
+ // If all of the indexed ISA variables are set, then its possible that this
+ // ISA is indexed, and we should first try to get its value using the index.
+ // Note, we check these variables first as the ObjC runtime will set at least
+ // one of their values to 0 if they aren't needed.
+ if (m_objc_debug_indexed_isa_magic_mask &&
+ m_objc_debug_indexed_isa_magic_value &&
+ m_objc_debug_indexed_isa_index_mask &&
+ m_objc_debug_indexed_isa_index_shift && m_objc_indexed_classes) {
+ if ((isa & ~m_objc_debug_indexed_isa_index_mask) == 0)
+ return false;
+
+ if ((isa & m_objc_debug_indexed_isa_magic_mask) ==
+ m_objc_debug_indexed_isa_magic_value) {
+ // Magic bits are correct, so try extract the index.
+ uintptr_t index = (isa & m_objc_debug_indexed_isa_index_mask) >>
+ m_objc_debug_indexed_isa_index_shift;
+ // If the index is out of bounds of the length of the array then check if
+ // the array has been updated. If that is the case then we should try
+ // read the count again, and update the cache if the count has been
+ // updated.
+ if (index > m_indexed_isa_cache.size()) {
+ if (log)
+ log->Printf("AOCRT::NPI (index = %" PRIu64
+ ") exceeds cache (size = %" PRIu64 ")",
+ (uint64_t)index, (uint64_t)m_indexed_isa_cache.size());
+
+ Process *process(m_runtime.GetProcess());
+
+ ModuleSP objc_module_sp(m_objc_module_wp.lock());
+ if (!objc_module_sp)
+ return false;
+
+ Status error;
+ auto objc_indexed_classes_count = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_indexed_classes_count"), objc_module_sp,
+ error);
+ if (error.Fail())
+ return false;
+
+ if (log)
+ log->Printf("AOCRT::NPI (new class count = %" PRIu64 ")",
+ (uint64_t)objc_indexed_classes_count);
+
+ if (objc_indexed_classes_count > m_indexed_isa_cache.size()) {
+ // Read the class entries we don't have. We should just read all of
+ // them instead of just the one we need as then we can cache those we
+ // may need later.
+ auto num_new_classes =
+ objc_indexed_classes_count - m_indexed_isa_cache.size();
+ const uint32_t addr_size = process->GetAddressByteSize();
+ DataBufferHeap buffer(num_new_classes * addr_size, 0);
+
+ lldb::addr_t last_read_class =
+ m_objc_indexed_classes + (m_indexed_isa_cache.size() * addr_size);
+ size_t bytes_read = process->ReadMemory(
+ last_read_class, buffer.GetBytes(), buffer.GetByteSize(), error);
+ if (error.Fail() || bytes_read != buffer.GetByteSize())
+ return false;
+
+ if (log)
+ log->Printf("AOCRT::NPI (read new classes count = %" PRIu64 ")",
+ (uint64_t)num_new_classes);
+
+ // Append the new entries to the existing cache.
+ DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t offset = 0;
+ for (unsigned i = 0; i != num_new_classes; ++i)
+ m_indexed_isa_cache.push_back(data.GetPointer(&offset));
+ }
+ }
+
+ // If the index is still out of range then this isn't a pointer.
+ if (index > m_indexed_isa_cache.size())
+ return false;
+
+ if (log)
+ log->Printf("AOCRT::NPI Evalulate(ret_isa = 0x%" PRIx64 ")",
+ (uint64_t)m_indexed_isa_cache[index]);
+
+ ret_isa = m_indexed_isa_cache[index];
+ return (ret_isa != 0); // this is a pointer so 0 is not a valid value
+ }
+
+ return false;
+ }
+
+ // Definitely not an indexed ISA, so try to use a mask to extract the pointer
+ // from the ISA.
+ if ((isa & m_objc_debug_isa_magic_mask) == m_objc_debug_isa_magic_value) {
+ ret_isa = isa & m_objc_debug_isa_class_mask;
+ return (ret_isa != 0); // this is a pointer so 0 is not a valid value
+ }
+ return false;
+}
+
+ObjCLanguageRuntime::EncodingToTypeSP AppleObjCRuntimeV2::GetEncodingToType() {
+ if (!m_encoding_to_type_sp)
+ m_encoding_to_type_sp =
+ std::make_shared<AppleObjCTypeEncodingParser>(*this);
+ return m_encoding_to_type_sp;
+}
+
+lldb_private::AppleObjCRuntime::ObjCISA
+AppleObjCRuntimeV2::GetPointerISA(ObjCISA isa) {
+ ObjCISA ret = isa;
+
+ if (m_non_pointer_isa_cache_up)
+ m_non_pointer_isa_cache_up->EvaluateNonPointerISA(isa, ret);
+
+ return ret;
+}
+
+bool AppleObjCRuntimeV2::GetCFBooleanValuesIfNeeded() {
+ if (m_CFBoolean_values)
+ return true;
+
+ static ConstString g_kCFBooleanFalse("__kCFBooleanFalse");
+ static ConstString g_kCFBooleanTrue("__kCFBooleanTrue");
+
+ std::function<lldb::addr_t(ConstString)> get_symbol =
+ [this](ConstString sym) -> lldb::addr_t {
+ SymbolContextList sc_list;
+ if (GetProcess()->GetTarget().GetImages().FindSymbolsWithNameAndType(
+ sym, lldb::eSymbolTypeData, sc_list) == 1) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(0, sc);
+ if (sc.symbol)
+ return sc.symbol->GetLoadAddress(&GetProcess()->GetTarget());
+ }
+
+ return LLDB_INVALID_ADDRESS;
+ };
+
+ lldb::addr_t false_addr = get_symbol(g_kCFBooleanFalse);
+ lldb::addr_t true_addr = get_symbol(g_kCFBooleanTrue);
+
+ return (m_CFBoolean_values = {false_addr, true_addr}).operator bool();
+}
+
+void AppleObjCRuntimeV2::GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
+ lldb::addr_t &cf_false) {
+ if (GetCFBooleanValuesIfNeeded()) {
+ cf_true = m_CFBoolean_values->second;
+ cf_false = m_CFBoolean_values->first;
+ } else
+ this->AppleObjCRuntime::GetValuesForGlobalCFBooleans(cf_true, cf_false);
+}
+
+#pragma mark Frame recognizers
+
+class ObjCExceptionRecognizedStackFrame : public RecognizedStackFrame {
+ public:
+ ObjCExceptionRecognizedStackFrame(StackFrameSP frame_sp) {
+ ThreadSP thread_sp = frame_sp->GetThread();
+ ProcessSP process_sp = thread_sp->GetProcess();
+
+ const lldb::ABISP &abi = process_sp->GetABI();
+ if (!abi) return;
+
+ CompilerType voidstar = process_sp->GetTarget()
+ .GetScratchClangASTContext()
+ ->GetBasicType(lldb::eBasicTypeVoid)
+ .GetPointerType();
+
+ ValueList args;
+ Value input_value;
+ input_value.SetCompilerType(voidstar);
+ args.PushValue(input_value);
+
+ if (!abi->GetArgumentValues(*thread_sp, args)) return;
+
+ addr_t exception_addr = args.GetValueAtIndex(0)->GetScalar().ULongLong();
+
+ Value value(exception_addr);
+ value.SetCompilerType(voidstar);
+ exception = ValueObjectConstResult::Create(frame_sp.get(), value,
+ ConstString("exception"));
+ exception = ValueObjectRecognizerSynthesizedValue::Create(
+ *exception, eValueTypeVariableArgument);
+ exception = exception->GetDynamicValue(eDynamicDontRunTarget);
+
+ m_arguments = ValueObjectListSP(new ValueObjectList());
+ m_arguments->Append(exception);
+ }
+
+ ValueObjectSP exception;
+
+ lldb::ValueObjectSP GetExceptionObject() override { return exception; }
+};
+
+class ObjCExceptionThrowFrameRecognizer : public StackFrameRecognizer {
+ lldb::RecognizedStackFrameSP
+ RecognizeFrame(lldb::StackFrameSP frame) override {
+ return lldb::RecognizedStackFrameSP(
+ new ObjCExceptionRecognizedStackFrame(frame));
+ };
+};
+
+static void RegisterObjCExceptionRecognizer() {
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ FileSpec module;
+ ConstString function;
+ std::tie(module, function) = AppleObjCRuntime::GetExceptionThrowLocation();
+ StackFrameRecognizerManager::AddRecognizer(
+ StackFrameRecognizerSP(new ObjCExceptionThrowFrameRecognizer()),
+ module.GetFilename(), function, /*first_instruction_only*/ true);
+ });
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
new file mode 100644
index 000000000000..a0fd39dc03b2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
@@ -0,0 +1,342 @@
+//===-- AppleObjCRuntimeV2.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_AppleObjCRuntimeV2_h_
+#define liblldb_AppleObjCRuntimeV2_h_
+
+#include <map>
+#include <memory>
+#include <mutex>
+
+#include "AppleObjCRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+class RemoteNXMapTable;
+
+namespace lldb_private {
+
+class AppleObjCRuntimeV2 : public AppleObjCRuntime {
+public:
+ ~AppleObjCRuntimeV2() override = default;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::LanguageRuntime *
+ CreateInstance(Process *process, lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || AppleObjCRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ // These are generic runtime functions:
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ UtilityFunction *CreateObjectChecker(const char *) override;
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ ObjCRuntimeVersions GetRuntimeVersion() const override {
+ return ObjCRuntimeVersions::eAppleObjC_V2;
+ }
+
+ size_t GetByteOffsetForIvar(CompilerType &parent_qual_type,
+ const char *ivar_name) override;
+
+ void UpdateISAToDescriptorMapIfNeeded() override;
+
+ ConstString GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa) override;
+
+ ClassDescriptorSP GetClassDescriptor(ValueObject &in_value) override;
+
+ ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa) override;
+
+ DeclVendor *GetDeclVendor() override;
+
+ lldb::addr_t LookupRuntimeSymbol(ConstString name) override;
+
+ EncodingToTypeSP GetEncodingToType() override;
+
+ bool IsTaggedPointer(lldb::addr_t ptr) override;
+
+ TaggedPointerVendor *GetTaggedPointerVendor() override {
+ return m_tagged_pointer_vendor_up.get();
+ }
+
+ lldb::addr_t GetTaggedPointerObfuscator();
+
+ void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
+ lldb::addr_t &cf_false) override;
+
+ // none of these are valid ISAs - we use them to infer the type
+ // of tagged pointers - if we have something meaningful to say
+ // we report an actual type - otherwise, we just say tagged
+ // there is no connection between the values here and the tagged pointers map
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA = 1;
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSAtom = 2;
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSNumber = 3;
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDateTS = 4;
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSManagedObject =
+ 5;
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDate = 6;
+
+protected:
+ lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt,
+ bool catch_bp,
+ bool throw_bp) override;
+
+private:
+ class HashTableSignature {
+ public:
+ HashTableSignature();
+
+ bool NeedsUpdate(Process *process, AppleObjCRuntimeV2 *runtime,
+ RemoteNXMapTable &hash_table);
+
+ void UpdateSignature(const RemoteNXMapTable &hash_table);
+
+ protected:
+ uint32_t m_count;
+ uint32_t m_num_buckets;
+ lldb::addr_t m_buckets_ptr;
+ };
+
+ class NonPointerISACache {
+ public:
+ static NonPointerISACache *
+ CreateInstance(AppleObjCRuntimeV2 &runtime,
+ const lldb::ModuleSP &objc_module_sp);
+
+ ObjCLanguageRuntime::ClassDescriptorSP GetClassDescriptor(ObjCISA isa);
+
+ private:
+ NonPointerISACache(AppleObjCRuntimeV2 &runtime,
+ const lldb::ModuleSP &objc_module_sp,
+ uint64_t objc_debug_isa_class_mask,
+ uint64_t objc_debug_isa_magic_mask,
+ uint64_t objc_debug_isa_magic_value,
+ uint64_t objc_debug_indexed_isa_magic_mask,
+ uint64_t objc_debug_indexed_isa_magic_value,
+ uint64_t objc_debug_indexed_isa_index_mask,
+ uint64_t objc_debug_indexed_isa_index_shift,
+ lldb::addr_t objc_indexed_classes);
+
+ bool EvaluateNonPointerISA(ObjCISA isa, ObjCISA &ret_isa);
+
+ AppleObjCRuntimeV2 &m_runtime;
+ std::map<ObjCISA, ObjCLanguageRuntime::ClassDescriptorSP> m_cache;
+ lldb::ModuleWP m_objc_module_wp;
+ uint64_t m_objc_debug_isa_class_mask;
+ uint64_t m_objc_debug_isa_magic_mask;
+ uint64_t m_objc_debug_isa_magic_value;
+
+ uint64_t m_objc_debug_indexed_isa_magic_mask;
+ uint64_t m_objc_debug_indexed_isa_magic_value;
+ uint64_t m_objc_debug_indexed_isa_index_mask;
+ uint64_t m_objc_debug_indexed_isa_index_shift;
+ lldb::addr_t m_objc_indexed_classes;
+
+ std::vector<lldb::addr_t> m_indexed_isa_cache;
+
+ friend class AppleObjCRuntimeV2;
+
+ DISALLOW_COPY_AND_ASSIGN(NonPointerISACache);
+ };
+
+ class TaggedPointerVendorV2
+ : public ObjCLanguageRuntime::TaggedPointerVendor {
+ public:
+ ~TaggedPointerVendorV2() override = default;
+
+ static TaggedPointerVendorV2 *
+ CreateInstance(AppleObjCRuntimeV2 &runtime,
+ const lldb::ModuleSP &objc_module_sp);
+
+ protected:
+ AppleObjCRuntimeV2 &m_runtime;
+
+ TaggedPointerVendorV2(AppleObjCRuntimeV2 &runtime)
+ : TaggedPointerVendor(), m_runtime(runtime) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorV2);
+ };
+
+ class TaggedPointerVendorRuntimeAssisted : public TaggedPointerVendorV2 {
+ public:
+ bool IsPossibleTaggedPointer(lldb::addr_t ptr) override;
+
+ ObjCLanguageRuntime::ClassDescriptorSP
+ GetClassDescriptor(lldb::addr_t ptr) override;
+
+ protected:
+ TaggedPointerVendorRuntimeAssisted(
+ AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask,
+ uint32_t objc_debug_taggedpointer_slot_shift,
+ uint32_t objc_debug_taggedpointer_slot_mask,
+ uint32_t objc_debug_taggedpointer_payload_lshift,
+ uint32_t objc_debug_taggedpointer_payload_rshift,
+ lldb::addr_t objc_debug_taggedpointer_classes);
+
+ typedef std::map<uint8_t, ObjCLanguageRuntime::ClassDescriptorSP> Cache;
+ typedef Cache::iterator CacheIterator;
+ Cache m_cache;
+ uint64_t m_objc_debug_taggedpointer_mask;
+ uint32_t m_objc_debug_taggedpointer_slot_shift;
+ uint32_t m_objc_debug_taggedpointer_slot_mask;
+ uint32_t m_objc_debug_taggedpointer_payload_lshift;
+ uint32_t m_objc_debug_taggedpointer_payload_rshift;
+ lldb::addr_t m_objc_debug_taggedpointer_classes;
+
+ friend class AppleObjCRuntimeV2::TaggedPointerVendorV2;
+
+ DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorRuntimeAssisted);
+ };
+
+ class TaggedPointerVendorExtended
+ : public TaggedPointerVendorRuntimeAssisted {
+ public:
+ ObjCLanguageRuntime::ClassDescriptorSP
+ GetClassDescriptor(lldb::addr_t ptr) override;
+
+ protected:
+ TaggedPointerVendorExtended(
+ AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask,
+ uint64_t objc_debug_taggedpointer_ext_mask,
+ uint32_t objc_debug_taggedpointer_slot_shift,
+ uint32_t objc_debug_taggedpointer_ext_slot_shift,
+ uint32_t objc_debug_taggedpointer_slot_mask,
+ uint32_t objc_debug_taggedpointer_ext_slot_mask,
+ uint32_t objc_debug_taggedpointer_payload_lshift,
+ uint32_t objc_debug_taggedpointer_payload_rshift,
+ uint32_t objc_debug_taggedpointer_ext_payload_lshift,
+ uint32_t objc_debug_taggedpointer_ext_payload_rshift,
+ lldb::addr_t objc_debug_taggedpointer_classes,
+ lldb::addr_t objc_debug_taggedpointer_ext_classes);
+
+ bool IsPossibleExtendedTaggedPointer(lldb::addr_t ptr);
+
+ typedef std::map<uint8_t, ObjCLanguageRuntime::ClassDescriptorSP> Cache;
+ typedef Cache::iterator CacheIterator;
+ Cache m_ext_cache;
+ uint64_t m_objc_debug_taggedpointer_ext_mask;
+ uint32_t m_objc_debug_taggedpointer_ext_slot_shift;
+ uint32_t m_objc_debug_taggedpointer_ext_slot_mask;
+ uint32_t m_objc_debug_taggedpointer_ext_payload_lshift;
+ uint32_t m_objc_debug_taggedpointer_ext_payload_rshift;
+ lldb::addr_t m_objc_debug_taggedpointer_ext_classes;
+
+ friend class AppleObjCRuntimeV2::TaggedPointerVendorV2;
+
+ DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorExtended);
+ };
+
+ class TaggedPointerVendorLegacy : public TaggedPointerVendorV2 {
+ public:
+ bool IsPossibleTaggedPointer(lldb::addr_t ptr) override;
+
+ ObjCLanguageRuntime::ClassDescriptorSP
+ GetClassDescriptor(lldb::addr_t ptr) override;
+
+ protected:
+ TaggedPointerVendorLegacy(AppleObjCRuntimeV2 &runtime)
+ : TaggedPointerVendorV2(runtime) {}
+
+ friend class AppleObjCRuntimeV2::TaggedPointerVendorV2;
+
+ DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorLegacy);
+ };
+
+ struct DescriptorMapUpdateResult {
+ bool m_update_ran;
+ uint32_t m_num_found;
+
+ DescriptorMapUpdateResult(bool ran, uint32_t found) {
+ m_update_ran = ran;
+ m_num_found = found;
+ }
+
+ static DescriptorMapUpdateResult Fail() { return {false, 0}; }
+
+ static DescriptorMapUpdateResult Success(uint32_t found) {
+ return {true, found};
+ }
+ };
+
+ AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp);
+
+ ObjCISA GetPointerISA(ObjCISA isa);
+
+ lldb::addr_t GetISAHashTablePointer();
+
+ bool UpdateISAToDescriptorMapFromMemory(RemoteNXMapTable &hash_table);
+
+ DescriptorMapUpdateResult
+ UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table);
+
+ uint32_t ParseClassInfoArray(const lldb_private::DataExtractor &data,
+ uint32_t num_class_infos);
+
+ DescriptorMapUpdateResult UpdateISAToDescriptorMapSharedCache();
+
+ enum class SharedCacheWarningReason {
+ eExpressionExecutionFailure,
+ eNotEnoughClassesRead
+ };
+
+ void WarnIfNoClassesCached(SharedCacheWarningReason reason);
+
+ lldb::addr_t GetSharedCacheReadOnlyAddress();
+
+ bool GetCFBooleanValuesIfNeeded();
+
+ friend class ClassDescriptorV2;
+
+ std::unique_ptr<UtilityFunction> m_get_class_info_code;
+ lldb::addr_t m_get_class_info_args;
+ std::mutex m_get_class_info_args_mutex;
+
+ std::unique_ptr<UtilityFunction> m_get_shared_cache_class_info_code;
+ lldb::addr_t m_get_shared_cache_class_info_args;
+ std::mutex m_get_shared_cache_class_info_args_mutex;
+
+ std::unique_ptr<DeclVendor> m_decl_vendor_up;
+ lldb::addr_t m_tagged_pointer_obfuscator;
+ lldb::addr_t m_isa_hash_table_ptr;
+ HashTableSignature m_hash_signature;
+ bool m_has_object_getClass;
+ bool m_loaded_objc_opt;
+ std::unique_ptr<NonPointerISACache> m_non_pointer_isa_cache_up;
+ std::unique_ptr<TaggedPointerVendor> m_tagged_pointer_vendor_up;
+ EncodingToTypeSP m_encoding_to_type_sp;
+ bool m_noclasses_warning_emitted;
+ llvm::Optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCRuntimeV2_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp
new file mode 100644
index 000000000000..b3eb09caa86d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp
@@ -0,0 +1,1151 @@
+//===-- AppleObjCTrampolineHandler.cpp ----------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AppleObjCTrampolineHandler.h"
+#include "AppleThreadPlanStepThroughObjCTrampoline.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *AppleObjCTrampolineHandler::g_lookup_implementation_function_name =
+ "__lldb_objc_find_implementation_for_selector";
+const char *AppleObjCTrampolineHandler::
+ g_lookup_implementation_with_stret_function_code =
+ " \n\
+extern \"C\" \n\
+{ \n\
+ extern void *class_getMethodImplementation(void *objc_class, void *sel); \n\
+ extern void *class_getMethodImplementation_stret(void *objc_class, \n\
+ void *sel); \n\
+ extern void * object_getClass (id object); \n\
+ extern void * sel_getUid(char *name); \n\
+ extern int printf(const char *format, ...); \n\
+} \n\
+extern \"C\" void * __lldb_objc_find_implementation_for_selector ( \n\
+ void *object, \n\
+ void *sel, \n\
+ int is_stret, \n\
+ int is_super, \n\
+ int is_super2, \n\
+ int is_fixup, \n\
+ int is_fixed, \n\
+ int debug) \n\
+{ \n\
+ struct __lldb_imp_return_struct \n\
+ { \n\
+ void *class_addr; \n\
+ void *sel_addr; \n\
+ void *impl_addr; \n\
+ }; \n\
+ \n\
+ struct __lldb_objc_class { \n\
+ void *isa; \n\
+ void *super_ptr; \n\
+ }; \n\
+ struct __lldb_objc_super { \n\
+ void *receiver; \n\
+ struct __lldb_objc_class *class_ptr; \n\
+ }; \n\
+ struct __lldb_msg_ref { \n\
+ void *dont_know; \n\
+ void *sel; \n\
+ }; \n\
+ \n\
+ struct __lldb_imp_return_struct return_struct; \n\
+ \n\
+ if (debug) \n\
+ printf (\"\\n*** Called with obj: 0x%p sel: 0x%p is_stret: %d is_super: %d, \"\n\
+ \"is_super2: %d, is_fixup: %d, is_fixed: %d\\n\", \n\
+ object, sel, is_stret, is_super, is_super2, is_fixup, is_fixed);\n\
+ if (is_super) \n\
+ { \n\
+ if (is_super2) \n\
+ { \n\
+ return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr->super_ptr;\n\
+ } \n\
+ else \n\
+ { \n\
+ return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr;\n\
+ } \n\
+ } \n\
+ else \n\
+ { \n\
+ // This code seems a little funny, but has its reasons... \n\
+ \n\
+ // The call to [object class] is here because if this is a \n\
+ // class, and has not been called into yet, we need to do \n\
+ // something to force the class to initialize itself. \n\
+ // Then the call to object_getClass will actually return the \n\
+ // correct class, either the class if object is a class \n\
+ // instance, or the meta-class if it is a class pointer. \n\
+ void *class_ptr = (void *) [(id) object class]; \n\
+ return_struct.class_addr = (id) object_getClass((id) object); \n\
+ if (debug) \n\
+ { \n\
+ if (class_ptr == object) \n\
+ { \n\
+ printf (\"Found a class object, need to use the meta class %p -> %p\\n\",\n\
+ class_ptr, return_struct.class_addr); \n\
+ } \n\
+ else \n\
+ { \n\
+ printf (\"[object class] returned: %p object_getClass: %p.\\n\", \n\
+ class_ptr, return_struct.class_addr); \n\
+ } \n\
+ } \n\
+ } \n\
+ \n\
+ if (is_fixup) \n\
+ { \n\
+ if (is_fixed) \n\
+ { \n\
+ return_struct.sel_addr = ((__lldb_msg_ref *) sel)->sel; \n\
+ } \n\
+ else \n\
+ { \n\
+ char *sel_name = (char *) ((__lldb_msg_ref *) sel)->sel; \n\
+ return_struct.sel_addr = sel_getUid (sel_name); \n\
+ if (debug) \n\
+ printf (\"\\n*** Got fixed up selector: %p for name %s.\\n\",\n\
+ return_struct.sel_addr, sel_name); \n\
+ } \n\
+ } \n\
+ else \n\
+ { \n\
+ return_struct.sel_addr = sel; \n\
+ } \n\
+ \n\
+ if (is_stret) \n\
+ { \n\
+ return_struct.impl_addr = \n\
+ class_getMethodImplementation_stret (return_struct.class_addr, \n\
+ return_struct.sel_addr); \n\
+ } \n\
+ else \n\
+ { \n\
+ return_struct.impl_addr = \n\
+ class_getMethodImplementation (return_struct.class_addr, \n\
+ return_struct.sel_addr); \n\
+ } \n\
+ if (debug) \n\
+ printf (\"\\n*** Returning implementation: %p.\\n\", \n\
+ return_struct.impl_addr); \n\
+ \n\
+ return return_struct.impl_addr; \n\
+} \n\
+";
+const char *
+ AppleObjCTrampolineHandler::g_lookup_implementation_no_stret_function_code =
+ " \n\
+extern \"C\" \n\
+{ \n\
+ extern void *class_getMethodImplementation(void *objc_class, void *sel); \n\
+ extern void * object_getClass (id object); \n\
+ extern void * sel_getUid(char *name); \n\
+ extern int printf(const char *format, ...); \n\
+} \n\
+extern \"C\" void * __lldb_objc_find_implementation_for_selector (void *object, \n\
+ void *sel, \n\
+ int is_stret, \n\
+ int is_super, \n\
+ int is_super2, \n\
+ int is_fixup, \n\
+ int is_fixed, \n\
+ int debug) \n\
+{ \n\
+ struct __lldb_imp_return_struct \n\
+ { \n\
+ void *class_addr; \n\
+ void *sel_addr; \n\
+ void *impl_addr; \n\
+ }; \n\
+ \n\
+ struct __lldb_objc_class { \n\
+ void *isa; \n\
+ void *super_ptr; \n\
+ }; \n\
+ struct __lldb_objc_super { \n\
+ void *receiver; \n\
+ struct __lldb_objc_class *class_ptr; \n\
+ }; \n\
+ struct __lldb_msg_ref { \n\
+ void *dont_know; \n\
+ void *sel; \n\
+ }; \n\
+ \n\
+ struct __lldb_imp_return_struct return_struct; \n\
+ \n\
+ if (debug) \n\
+ printf (\"\\n*** Called with obj: 0x%p sel: 0x%p is_stret: %d is_super: %d, \" \n\
+ \"is_super2: %d, is_fixup: %d, is_fixed: %d\\n\", \n\
+ object, sel, is_stret, is_super, is_super2, is_fixup, is_fixed); \n\
+ if (is_super) \n\
+ { \n\
+ if (is_super2) \n\
+ { \n\
+ return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr->super_ptr; \n\
+ } \n\
+ else \n\
+ { \n\
+ return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr; \n\
+ } \n\
+ } \n\
+ else \n\
+ { \n\
+ // This code seems a little funny, but has its reasons... \n\
+ // The call to [object class] is here because if this is a class, and has not been called into \n\
+ // yet, we need to do something to force the class to initialize itself. \n\
+ // Then the call to object_getClass will actually return the correct class, either the class \n\
+ // if object is a class instance, or the meta-class if it is a class pointer. \n\
+ void *class_ptr = (void *) [(id) object class]; \n\
+ return_struct.class_addr = (id) object_getClass((id) object); \n\
+ if (debug) \n\
+ { \n\
+ if (class_ptr == object) \n\
+ { \n\
+ printf (\"Found a class object, need to return the meta class %p -> %p\\n\", \n\
+ class_ptr, return_struct.class_addr); \n\
+ } \n\
+ else \n\
+ { \n\
+ printf (\"[object class] returned: %p object_getClass: %p.\\n\", \n\
+ class_ptr, return_struct.class_addr); \n\
+ } \n\
+ } \n\
+ } \n\
+ \n\
+ if (is_fixup) \n\
+ { \n\
+ if (is_fixed) \n\
+ { \n\
+ return_struct.sel_addr = ((__lldb_msg_ref *) sel)->sel; \n\
+ } \n\
+ else \n\
+ { \n\
+ char *sel_name = (char *) ((__lldb_msg_ref *) sel)->sel; \n\
+ return_struct.sel_addr = sel_getUid (sel_name); \n\
+ if (debug) \n\
+ printf (\"\\n*** Got fixed up selector: %p for name %s.\\n\",\n\
+ return_struct.sel_addr, sel_name); \n\
+ } \n\
+ } \n\
+ else \n\
+ { \n\
+ return_struct.sel_addr = sel; \n\
+ } \n\
+ \n\
+ return_struct.impl_addr = \n\
+ class_getMethodImplementation (return_struct.class_addr, \n\
+ return_struct.sel_addr); \n\
+ if (debug) \n\
+ printf (\"\\n*** Returning implementation: 0x%p.\\n\", \n\
+ return_struct.impl_addr); \n\
+ \n\
+ return return_struct.impl_addr; \n\
+} \n\
+";
+
+AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::VTableRegion(
+ AppleObjCVTables *owner, lldb::addr_t header_addr)
+ : m_valid(true), m_owner(owner), m_header_addr(header_addr),
+ m_code_start_addr(0), m_code_end_addr(0), m_next_region(0) {
+ SetUpRegion();
+}
+
+AppleObjCTrampolineHandler::~AppleObjCTrampolineHandler() {}
+
+void AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::SetUpRegion() {
+ // The header looks like:
+ //
+ // uint16_t headerSize
+ // uint16_t descSize
+ // uint32_t descCount
+ // void * next
+ //
+ // First read in the header:
+
+ char memory_buffer[16];
+ ProcessSP process_sp = m_owner->GetProcessSP();
+ if (!process_sp)
+ return;
+ DataExtractor data(memory_buffer, sizeof(memory_buffer),
+ process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ size_t actual_size = 8 + process_sp->GetAddressByteSize();
+ Status error;
+ size_t bytes_read =
+ process_sp->ReadMemory(m_header_addr, memory_buffer, actual_size, error);
+ if (bytes_read != actual_size) {
+ m_valid = false;
+ return;
+ }
+
+ lldb::offset_t offset = 0;
+ const uint16_t header_size = data.GetU16(&offset);
+ const uint16_t descriptor_size = data.GetU16(&offset);
+ const size_t num_descriptors = data.GetU32(&offset);
+
+ m_next_region = data.GetPointer(&offset);
+
+ // If the header size is 0, that means we've come in too early before this
+ // data is set up.
+ // Set ourselves as not valid, and continue.
+ if (header_size == 0 || num_descriptors == 0) {
+ m_valid = false;
+ return;
+ }
+
+ // Now read in all the descriptors:
+ // The descriptor looks like:
+ //
+ // uint32_t offset
+ // uint32_t flags
+ //
+ // Where offset is either 0 - in which case it is unused, or it is
+ // the offset of the vtable code from the beginning of the
+ // descriptor record. Below, we'll convert that into an absolute
+ // code address, since I don't want to have to compute it over and
+ // over.
+
+ // Ingest the whole descriptor array:
+ const lldb::addr_t desc_ptr = m_header_addr + header_size;
+ const size_t desc_array_size = num_descriptors * descriptor_size;
+ DataBufferSP data_sp(new DataBufferHeap(desc_array_size, '\0'));
+ uint8_t *dst = (uint8_t *)data_sp->GetBytes();
+
+ DataExtractor desc_extractor(dst, desc_array_size, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ bytes_read = process_sp->ReadMemory(desc_ptr, dst, desc_array_size, error);
+ if (bytes_read != desc_array_size) {
+ m_valid = false;
+ return;
+ }
+
+ // The actual code for the vtables will be laid out consecutively, so I also
+ // compute the start and end of the whole code block.
+
+ offset = 0;
+ m_code_start_addr = 0;
+ m_code_end_addr = 0;
+
+ for (size_t i = 0; i < num_descriptors; i++) {
+ lldb::addr_t start_offset = offset;
+ uint32_t voffset = desc_extractor.GetU32(&offset);
+ uint32_t flags = desc_extractor.GetU32(&offset);
+ lldb::addr_t code_addr = desc_ptr + start_offset + voffset;
+ m_descriptors.push_back(VTableDescriptor(flags, code_addr));
+
+ if (m_code_start_addr == 0 || code_addr < m_code_start_addr)
+ m_code_start_addr = code_addr;
+ if (code_addr > m_code_end_addr)
+ m_code_end_addr = code_addr;
+
+ offset = start_offset + descriptor_size;
+ }
+ // Finally, a little bird told me that all the vtable code blocks
+ // are the same size. Let's compute the blocks and if they are all
+ // the same add the size to the code end address:
+ lldb::addr_t code_size = 0;
+ bool all_the_same = true;
+ for (size_t i = 0; i < num_descriptors - 1; i++) {
+ lldb::addr_t this_size =
+ m_descriptors[i + 1].code_start - m_descriptors[i].code_start;
+ if (code_size == 0)
+ code_size = this_size;
+ else {
+ if (this_size != code_size)
+ all_the_same = false;
+ if (this_size > code_size)
+ code_size = this_size;
+ }
+ }
+ if (all_the_same)
+ m_code_end_addr += code_size;
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::
+ AddressInRegion(lldb::addr_t addr, uint32_t &flags) {
+ if (!IsValid())
+ return false;
+
+ if (addr < m_code_start_addr || addr > m_code_end_addr)
+ return false;
+
+ std::vector<VTableDescriptor>::iterator pos, end = m_descriptors.end();
+ for (pos = m_descriptors.begin(); pos != end; pos++) {
+ if (addr <= (*pos).code_start) {
+ flags = (*pos).flags;
+ return true;
+ }
+ }
+ return false;
+}
+
+void AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::Dump(
+ Stream &s) {
+ s.Printf("Header addr: 0x%" PRIx64 " Code start: 0x%" PRIx64
+ " Code End: 0x%" PRIx64 " Next: 0x%" PRIx64 "\n",
+ m_header_addr, m_code_start_addr, m_code_end_addr, m_next_region);
+ size_t num_elements = m_descriptors.size();
+ for (size_t i = 0; i < num_elements; i++) {
+ s.Indent();
+ s.Printf("Code start: 0x%" PRIx64 " Flags: %d\n",
+ m_descriptors[i].code_start, m_descriptors[i].flags);
+ }
+}
+
+AppleObjCTrampolineHandler::AppleObjCVTables::AppleObjCVTables(
+ const ProcessSP &process_sp, const ModuleSP &objc_module_sp)
+ : m_process_wp(), m_trampoline_header(LLDB_INVALID_ADDRESS),
+ m_trampolines_changed_bp_id(LLDB_INVALID_BREAK_ID),
+ m_objc_module_sp(objc_module_sp) {
+ if (process_sp)
+ m_process_wp = process_sp;
+}
+
+AppleObjCTrampolineHandler::AppleObjCVTables::~AppleObjCVTables() {
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ if (m_trampolines_changed_bp_id != LLDB_INVALID_BREAK_ID)
+ process_sp->GetTarget().RemoveBreakpointByID(m_trampolines_changed_bp_id);
+ }
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::InitializeVTableSymbols() {
+ if (m_trampoline_header != LLDB_INVALID_ADDRESS)
+ return true;
+
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ Target &target = process_sp->GetTarget();
+
+ const ModuleList &target_modules = target.GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ size_t num_modules = target_modules.GetSize();
+ if (!m_objc_module_sp) {
+ for (size_t i = 0; i < num_modules; i++) {
+ if (ObjCLanguageRuntime::Get(*process_sp)
+ ->IsModuleObjCLibrary(
+ target_modules.GetModuleAtIndexUnlocked(i))) {
+ m_objc_module_sp = target_modules.GetModuleAtIndexUnlocked(i);
+ break;
+ }
+ }
+ }
+
+ if (m_objc_module_sp) {
+ ConstString trampoline_name("gdb_objc_trampolines");
+ const Symbol *trampoline_symbol =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(trampoline_name,
+ eSymbolTypeData);
+ if (trampoline_symbol != nullptr) {
+ m_trampoline_header = trampoline_symbol->GetLoadAddress(&target);
+ if (m_trampoline_header == LLDB_INVALID_ADDRESS)
+ return false;
+
+ // Next look up the "changed" symbol and set a breakpoint on that...
+ ConstString changed_name("gdb_objc_trampolines_changed");
+ const Symbol *changed_symbol =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(changed_name,
+ eSymbolTypeCode);
+ if (changed_symbol != nullptr) {
+ const Address changed_symbol_addr = changed_symbol->GetAddress();
+ if (!changed_symbol_addr.IsValid())
+ return false;
+
+ lldb::addr_t changed_addr =
+ changed_symbol_addr.GetOpcodeLoadAddress(&target);
+ if (changed_addr != LLDB_INVALID_ADDRESS) {
+ BreakpointSP trampolines_changed_bp_sp =
+ target.CreateBreakpoint(changed_addr, true, false);
+ if (trampolines_changed_bp_sp) {
+ m_trampolines_changed_bp_id = trampolines_changed_bp_sp->GetID();
+ trampolines_changed_bp_sp->SetCallback(RefreshTrampolines, this,
+ true);
+ trampolines_changed_bp_sp->SetBreakpointKind(
+ "objc-trampolines-changed");
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines(
+ void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ AppleObjCVTables *vtable_handler = (AppleObjCVTables *)baton;
+ if (vtable_handler->InitializeVTableSymbols()) {
+ // The Update function is called with the address of an added region. So we
+ // grab that address, and
+ // feed it into ReadRegions. Of course, our friend the ABI will get the
+ // values for us.
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Process *process = exe_ctx.GetProcessPtr();
+ const ABI *abi = process->GetABI().get();
+
+ ClangASTContext *clang_ast_context =
+ process->GetTarget().GetScratchClangASTContext();
+ ValueList argument_values;
+ Value input_value;
+ CompilerType clang_void_ptr_type =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+ input_value.SetValueType(Value::eValueTypeScalar);
+ // input_value.SetContext (Value::eContextTypeClangType,
+ // clang_void_ptr_type);
+ input_value.SetCompilerType(clang_void_ptr_type);
+ argument_values.PushValue(input_value);
+
+ bool success =
+ abi->GetArgumentValues(exe_ctx.GetThreadRef(), argument_values);
+ if (!success)
+ return false;
+
+ // Now get a pointer value from the zeroth argument.
+ Status error;
+ DataExtractor data;
+ error = argument_values.GetValueAtIndex(0)->GetValueAsData(&exe_ctx, data,
+ 0, nullptr);
+ lldb::offset_t offset = 0;
+ lldb::addr_t region_addr = data.GetPointer(&offset);
+
+ if (region_addr != 0)
+ vtable_handler->ReadRegions(region_addr);
+ }
+ return false;
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions() {
+ // The no argument version reads the start region from the value of
+ // the gdb_regions_header, and gets started from there.
+
+ m_regions.clear();
+ if (!InitializeVTableSymbols())
+ return false;
+ Status error;
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ lldb::addr_t region_addr =
+ process_sp->ReadPointerFromMemory(m_trampoline_header, error);
+ if (error.Success())
+ return ReadRegions(region_addr);
+ }
+ return false;
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions(
+ lldb::addr_t region_addr) {
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ // We aren't starting at the trampoline symbol.
+ InitializeVTableSymbols();
+ lldb::addr_t next_region = region_addr;
+
+ // Read in the sizes of the headers.
+ while (next_region != 0) {
+ m_regions.push_back(VTableRegion(this, next_region));
+ if (!m_regions.back().IsValid()) {
+ m_regions.clear();
+ return false;
+ }
+ if (log) {
+ StreamString s;
+ m_regions.back().Dump(s);
+ log->Printf("Read vtable region: \n%s", s.GetData());
+ }
+
+ next_region = m_regions.back().GetNextRegionAddr();
+ }
+
+ return true;
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::IsAddressInVTables(
+ lldb::addr_t addr, uint32_t &flags) {
+ region_collection::iterator pos, end = m_regions.end();
+ for (pos = m_regions.begin(); pos != end; pos++) {
+ if ((*pos).AddressInRegion(addr, flags))
+ return true;
+ }
+ return false;
+}
+
+const AppleObjCTrampolineHandler::DispatchFunction
+ AppleObjCTrampolineHandler::g_dispatch_functions[] = {
+ // NAME STRET SUPER SUPER2 FIXUP TYPE
+ {"objc_msgSend", false, false, false, DispatchFunction::eFixUpNone},
+ {"objc_msgSend_fixup", false, false, false,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSend_fixedup", false, false, false,
+ DispatchFunction::eFixUpFixed},
+ {"objc_msgSend_stret", true, false, false,
+ DispatchFunction::eFixUpNone},
+ {"objc_msgSend_stret_fixup", true, false, false,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSend_stret_fixedup", true, false, false,
+ DispatchFunction::eFixUpFixed},
+ {"objc_msgSend_fpret", false, false, false,
+ DispatchFunction::eFixUpNone},
+ {"objc_msgSend_fpret_fixup", false, false, false,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSend_fpret_fixedup", false, false, false,
+ DispatchFunction::eFixUpFixed},
+ {"objc_msgSend_fp2ret", false, false, true,
+ DispatchFunction::eFixUpNone},
+ {"objc_msgSend_fp2ret_fixup", false, false, true,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSend_fp2ret_fixedup", false, false, true,
+ DispatchFunction::eFixUpFixed},
+ {"objc_msgSendSuper", false, true, false, DispatchFunction::eFixUpNone},
+ {"objc_msgSendSuper_stret", true, true, false,
+ DispatchFunction::eFixUpNone},
+ {"objc_msgSendSuper2", false, true, true, DispatchFunction::eFixUpNone},
+ {"objc_msgSendSuper2_fixup", false, true, true,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSendSuper2_fixedup", false, true, true,
+ DispatchFunction::eFixUpFixed},
+ {"objc_msgSendSuper2_stret", true, true, true,
+ DispatchFunction::eFixUpNone},
+ {"objc_msgSendSuper2_stret_fixup", true, true, true,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSendSuper2_stret_fixedup", true, true, true,
+ DispatchFunction::eFixUpFixed},
+};
+
+AppleObjCTrampolineHandler::AppleObjCTrampolineHandler(
+ const ProcessSP &process_sp, const ModuleSP &objc_module_sp)
+ : m_process_wp(), m_objc_module_sp(objc_module_sp),
+ m_lookup_implementation_function_code(nullptr),
+ m_impl_fn_addr(LLDB_INVALID_ADDRESS),
+ m_impl_stret_fn_addr(LLDB_INVALID_ADDRESS),
+ m_msg_forward_addr(LLDB_INVALID_ADDRESS) {
+ if (process_sp)
+ m_process_wp = process_sp;
+ // Look up the known resolution functions:
+
+ ConstString get_impl_name("class_getMethodImplementation");
+ ConstString get_impl_stret_name("class_getMethodImplementation_stret");
+ ConstString msg_forward_name("_objc_msgForward");
+ ConstString msg_forward_stret_name("_objc_msgForward_stret");
+
+ Target *target = process_sp ? &process_sp->GetTarget() : nullptr;
+ const Symbol *class_getMethodImplementation =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(get_impl_name,
+ eSymbolTypeCode);
+ const Symbol *class_getMethodImplementation_stret =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(get_impl_stret_name,
+ eSymbolTypeCode);
+ const Symbol *msg_forward = m_objc_module_sp->FindFirstSymbolWithNameAndType(
+ msg_forward_name, eSymbolTypeCode);
+ const Symbol *msg_forward_stret =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(msg_forward_stret_name,
+ eSymbolTypeCode);
+
+ if (class_getMethodImplementation)
+ m_impl_fn_addr =
+ class_getMethodImplementation->GetAddress().GetOpcodeLoadAddress(
+ target);
+ if (class_getMethodImplementation_stret)
+ m_impl_stret_fn_addr =
+ class_getMethodImplementation_stret->GetAddress().GetOpcodeLoadAddress(
+ target);
+ if (msg_forward)
+ m_msg_forward_addr = msg_forward->GetAddress().GetOpcodeLoadAddress(target);
+ if (msg_forward_stret)
+ m_msg_forward_stret_addr =
+ msg_forward_stret->GetAddress().GetOpcodeLoadAddress(target);
+
+ // FIXME: Do some kind of logging here.
+ if (m_impl_fn_addr == LLDB_INVALID_ADDRESS) {
+ // If we can't even find the ordinary get method implementation function,
+ // then we aren't going to be able to
+ // step through any method dispatches. Warn to that effect and get out of
+ // here.
+ if (process_sp->CanJIT()) {
+ process_sp->GetTarget().GetDebugger().GetErrorFile()->Printf(
+ "Could not find implementation lookup function \"%s\""
+ " step in through ObjC method dispatch will not work.\n",
+ get_impl_name.AsCString());
+ }
+ return;
+ } else if (m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS) {
+ // It there is no stret return lookup function, assume that it is the same
+ // as the straight lookup:
+ m_impl_stret_fn_addr = m_impl_fn_addr;
+ // Also we will use the version of the lookup code that doesn't rely on the
+ // stret version of the function.
+ m_lookup_implementation_function_code =
+ g_lookup_implementation_no_stret_function_code;
+ } else {
+ m_lookup_implementation_function_code =
+ g_lookup_implementation_with_stret_function_code;
+ }
+
+ // Look up the addresses for the objc dispatch functions and cache
+ // them. For now I'm inspecting the symbol names dynamically to
+ // figure out how to dispatch to them. If it becomes more
+ // complicated than this we can turn the g_dispatch_functions char *
+ // array into a template table, and populate the DispatchFunction
+ // map from there.
+
+ for (size_t i = 0; i != llvm::array_lengthof(g_dispatch_functions); i++) {
+ ConstString name_const_str(g_dispatch_functions[i].name);
+ const Symbol *msgSend_symbol =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(name_const_str,
+ eSymbolTypeCode);
+ if (msgSend_symbol && msgSend_symbol->ValueIsAddress()) {
+ // FIXME: Make g_dispatch_functions static table of
+ // DispatchFunctions, and have the map be address->index.
+ // Problem is we also need to lookup the dispatch function. For
+ // now we could have a side table of stret & non-stret dispatch
+ // functions. If that's as complex as it gets, we're fine.
+
+ lldb::addr_t sym_addr =
+ msgSend_symbol->GetAddressRef().GetOpcodeLoadAddress(target);
+
+ m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i));
+ }
+ }
+
+ // Build our vtable dispatch handler here:
+ m_vtables_up.reset(new AppleObjCVTables(process_sp, m_objc_module_sp));
+ if (m_vtables_up)
+ m_vtables_up->ReadRegions();
+}
+
+lldb::addr_t
+AppleObjCTrampolineHandler::SetupDispatchFunction(Thread &thread,
+ ValueList &dispatch_values) {
+ ThreadSP thread_sp(thread.shared_from_this());
+ ExecutionContext exe_ctx(thread_sp);
+ DiagnosticManager diagnostics;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
+ FunctionCaller *impl_function_caller = nullptr;
+
+ // Scope for mutex locker:
+ {
+ std::lock_guard<std::mutex> guard(m_impl_function_mutex);
+
+ // First stage is to make the ClangUtility to hold our injected function:
+
+ if (!m_impl_code) {
+ if (m_lookup_implementation_function_code != nullptr) {
+ Status error;
+ m_impl_code.reset(exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(
+ m_lookup_implementation_function_code, eLanguageTypeObjC,
+ g_lookup_implementation_function_name, error));
+ if (error.Fail()) {
+ if (log)
+ log->Printf(
+ "Failed to get Utility Function for implementation lookup: %s.",
+ error.AsCString());
+ m_impl_code.reset();
+ return args_addr;
+ }
+
+ if (!m_impl_code->Install(diagnostics, exe_ctx)) {
+ if (log) {
+ log->Printf("Failed to install implementation lookup.");
+ diagnostics.Dump(log);
+ }
+ m_impl_code.reset();
+ return args_addr;
+ }
+ } else {
+ if (log)
+ log->Printf("No method lookup implementation code.");
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ // Next make the runner function for our implementation utility function.
+ ClangASTContext *clang_ast_context =
+ thread.GetProcess()->GetTarget().GetScratchClangASTContext();
+ CompilerType clang_void_ptr_type =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ Status error;
+
+ impl_function_caller = m_impl_code->MakeFunctionCaller(
+ clang_void_ptr_type, dispatch_values, thread_sp, error);
+ if (error.Fail()) {
+ if (log)
+ log->Printf(
+ "Error getting function caller for dispatch lookup: \"%s\".",
+ error.AsCString());
+ return args_addr;
+ }
+ } else {
+ impl_function_caller = m_impl_code->GetFunctionCaller();
+ }
+ }
+
+ diagnostics.Clear();
+
+ // Now write down the argument values for this particular call.
+ // This looks like it might be a race condition if other threads
+ // were calling into here, but actually it isn't because we allocate
+ // a new args structure for this call by passing args_addr =
+ // LLDB_INVALID_ADDRESS...
+
+ if (!impl_function_caller->WriteFunctionArguments(
+ exe_ctx, args_addr, dispatch_values, diagnostics)) {
+ if (log) {
+ log->Printf("Error writing function arguments.");
+ diagnostics.Dump(log);
+ }
+ return args_addr;
+ }
+
+ return args_addr;
+}
+
+ThreadPlanSP
+AppleObjCTrampolineHandler::GetStepThroughDispatchPlan(Thread &thread,
+ bool stop_others) {
+ ThreadPlanSP ret_plan_sp;
+ lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();
+
+ DispatchFunction this_dispatch;
+ bool found_it = false;
+
+ // First step is to look and see if we are in one of the known ObjC
+ // dispatch functions. We've already compiled a table of same, so
+ // consult it.
+
+ MsgsendMap::iterator pos;
+ pos = m_msgSend_map.find(curr_pc);
+ if (pos != m_msgSend_map.end()) {
+ this_dispatch = g_dispatch_functions[(*pos).second];
+ found_it = true;
+ }
+
+ // Next check to see if we are in a vtable region:
+
+ if (!found_it) {
+ uint32_t flags;
+ if (m_vtables_up) {
+ found_it = m_vtables_up->IsAddressInVTables(curr_pc, flags);
+ if (found_it) {
+ this_dispatch.name = "vtable";
+ this_dispatch.stret_return =
+ (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) ==
+ AppleObjCVTables::eOBJC_TRAMPOLINE_STRET;
+ this_dispatch.is_super = false;
+ this_dispatch.is_super2 = false;
+ this_dispatch.fixedup = DispatchFunction::eFixUpFixed;
+ }
+ }
+ }
+
+ if (found_it) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ // We are decoding a method dispatch. First job is to pull the
+ // arguments out:
+
+ lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
+
+ const ABI *abi = nullptr;
+ ProcessSP process_sp(thread.CalculateProcess());
+ if (process_sp)
+ abi = process_sp->GetABI().get();
+ if (abi == nullptr)
+ return ret_plan_sp;
+
+ TargetSP target_sp(thread.CalculateTarget());
+
+ ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext();
+ ValueList argument_values;
+ Value void_ptr_value;
+ CompilerType clang_void_ptr_type =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ void_ptr_value.SetValueType(Value::eValueTypeScalar);
+ // void_ptr_value.SetContext (Value::eContextTypeClangType,
+ // clang_void_ptr_type);
+ void_ptr_value.SetCompilerType(clang_void_ptr_type);
+
+ int obj_index;
+ int sel_index;
+
+ // If this is a struct return dispatch, then the first argument is
+ // the return struct pointer, and the object is the second, and
+ // the selector is the third. Otherwise the object is the first
+ // and the selector the second.
+ if (this_dispatch.stret_return) {
+ obj_index = 1;
+ sel_index = 2;
+ argument_values.PushValue(void_ptr_value);
+ argument_values.PushValue(void_ptr_value);
+ argument_values.PushValue(void_ptr_value);
+ } else {
+ obj_index = 0;
+ sel_index = 1;
+ argument_values.PushValue(void_ptr_value);
+ argument_values.PushValue(void_ptr_value);
+ }
+
+ bool success = abi->GetArgumentValues(thread, argument_values);
+ if (!success)
+ return ret_plan_sp;
+
+ lldb::addr_t obj_addr =
+ argument_values.GetValueAtIndex(obj_index)->GetScalar().ULongLong();
+ if (obj_addr == 0x0) {
+ if (log)
+ log->Printf(
+ "Asked to step to dispatch to nil object, returning empty plan.");
+ return ret_plan_sp;
+ }
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ // isa_addr will store the class pointer that the method is being
+ // dispatched to - so either the class directly or the super class
+ // if this is one of the objc_msgSendSuper flavors. That's mostly
+ // used to look up the class/selector pair in our cache.
+
+ lldb::addr_t isa_addr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t sel_addr =
+ argument_values.GetValueAtIndex(sel_index)->GetScalar().ULongLong();
+
+ // Figure out the class this is being dispatched to and see if
+ // we've already cached this method call, If so we can push a
+ // run-to-address plan directly. Otherwise we have to figure out
+ // where the implementation lives.
+
+ if (this_dispatch.is_super) {
+ if (this_dispatch.is_super2) {
+ // In the objc_msgSendSuper2 case, we don't get the object
+ // directly, we get a structure containing the object and the
+ // class to which the super message is being sent. So we need
+ // to dig the super out of the class and use that.
+
+ Value super_value(*(argument_values.GetValueAtIndex(obj_index)));
+ super_value.GetScalar() += process->GetAddressByteSize();
+ super_value.ResolveValue(&exe_ctx);
+
+ if (super_value.GetScalar().IsValid()) {
+
+ // isa_value now holds the class pointer. The second word of the
+ // class pointer is the super-class pointer:
+ super_value.GetScalar() += process->GetAddressByteSize();
+ super_value.ResolveValue(&exe_ctx);
+ if (super_value.GetScalar().IsValid())
+ isa_addr = super_value.GetScalar().ULongLong();
+ else {
+ if (log)
+ log->Printf("Failed to extract the super class value from the "
+ "class in objc_super.");
+ }
+ } else {
+ if (log)
+ log->Printf("Failed to extract the class value from objc_super.");
+ }
+ } else {
+ // In the objc_msgSendSuper case, we don't get the object
+ // directly, we get a two element structure containing the
+ // object and the super class to which the super message is
+ // being sent. So the class we want is the second element of
+ // this structure.
+
+ Value super_value(*(argument_values.GetValueAtIndex(obj_index)));
+ super_value.GetScalar() += process->GetAddressByteSize();
+ super_value.ResolveValue(&exe_ctx);
+
+ if (super_value.GetScalar().IsValid()) {
+ isa_addr = super_value.GetScalar().ULongLong();
+ } else {
+ if (log)
+ log->Printf("Failed to extract the class value from objc_super.");
+ }
+ }
+ } else {
+ // In the direct dispatch case, the object->isa is the class pointer we
+ // want.
+
+ // This is a little cheesy, but since object->isa is the first field,
+ // making the object value a load address value and resolving it will get
+ // the pointer sized data pointed to by that value...
+
+ // Note, it isn't a fatal error not to be able to get the
+ // address from the object, since this might be a "tagged
+ // pointer" which isn't a real object, but rather some word
+ // length encoded dingus.
+
+ Value isa_value(*(argument_values.GetValueAtIndex(obj_index)));
+
+ isa_value.SetValueType(Value::eValueTypeLoadAddress);
+ isa_value.ResolveValue(&exe_ctx);
+ if (isa_value.GetScalar().IsValid()) {
+ isa_addr = isa_value.GetScalar().ULongLong();
+ } else {
+ if (log)
+ log->Printf("Failed to extract the isa value from object.");
+ }
+ }
+
+ // Okay, we've got the address of the class for which we're resolving this,
+ // let's see if it's in our cache:
+ lldb::addr_t impl_addr = LLDB_INVALID_ADDRESS;
+
+ if (isa_addr != LLDB_INVALID_ADDRESS) {
+ if (log) {
+ log->Printf("Resolving call for class - 0x%" PRIx64
+ " and selector - 0x%" PRIx64,
+ isa_addr, sel_addr);
+ }
+ ObjCLanguageRuntime *objc_runtime =
+ ObjCLanguageRuntime::Get(*thread.GetProcess());
+ assert(objc_runtime != nullptr);
+
+ impl_addr = objc_runtime->LookupInMethodCache(isa_addr, sel_addr);
+ }
+
+ if (impl_addr != LLDB_INVALID_ADDRESS) {
+ // Yup, it was in the cache, so we can run to that address directly.
+
+ if (log)
+ log->Printf("Found implementation address in cache: 0x%" PRIx64,
+ impl_addr);
+
+ ret_plan_sp = std::make_shared<ThreadPlanRunToAddress>(thread, impl_addr,
+ stop_others);
+ } else {
+ // We haven't seen this class/selector pair yet. Look it up.
+ StreamString errors;
+ Address impl_code_address;
+
+ ValueList dispatch_values;
+
+ // We've will inject a little function in the target that takes the
+ // object, selector and some flags,
+ // and figures out the implementation. Looks like:
+ // void *__lldb_objc_find_implementation_for_selector (void *object,
+ // void *sel,
+ // int is_stret,
+ // int is_super,
+ // int is_super2,
+ // int is_fixup,
+ // int is_fixed,
+ // int debug)
+ // So set up the arguments for that call.
+
+ dispatch_values.PushValue(*(argument_values.GetValueAtIndex(obj_index)));
+ dispatch_values.PushValue(*(argument_values.GetValueAtIndex(sel_index)));
+
+ Value flag_value;
+ CompilerType clang_int_type =
+ clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(
+ lldb::eEncodingSint, 32);
+ flag_value.SetValueType(Value::eValueTypeScalar);
+ // flag_value.SetContext (Value::eContextTypeClangType, clang_int_type);
+ flag_value.SetCompilerType(clang_int_type);
+
+ if (this_dispatch.stret_return)
+ flag_value.GetScalar() = 1;
+ else
+ flag_value.GetScalar() = 0;
+ dispatch_values.PushValue(flag_value);
+
+ if (this_dispatch.is_super)
+ flag_value.GetScalar() = 1;
+ else
+ flag_value.GetScalar() = 0;
+ dispatch_values.PushValue(flag_value);
+
+ if (this_dispatch.is_super2)
+ flag_value.GetScalar() = 1;
+ else
+ flag_value.GetScalar() = 0;
+ dispatch_values.PushValue(flag_value);
+
+ switch (this_dispatch.fixedup) {
+ case DispatchFunction::eFixUpNone:
+ flag_value.GetScalar() = 0;
+ dispatch_values.PushValue(flag_value);
+ dispatch_values.PushValue(flag_value);
+ break;
+ case DispatchFunction::eFixUpFixed:
+ flag_value.GetScalar() = 1;
+ dispatch_values.PushValue(flag_value);
+ flag_value.GetScalar() = 1;
+ dispatch_values.PushValue(flag_value);
+ break;
+ case DispatchFunction::eFixUpToFix:
+ flag_value.GetScalar() = 1;
+ dispatch_values.PushValue(flag_value);
+ flag_value.GetScalar() = 0;
+ dispatch_values.PushValue(flag_value);
+ break;
+ }
+ if (log && log->GetVerbose())
+ flag_value.GetScalar() = 1;
+ else
+ flag_value.GetScalar() = 0; // FIXME - Set to 0 when debugging is done.
+ dispatch_values.PushValue(flag_value);
+
+ // The step through code might have to fill in the cache, so it
+ // is not safe to run only one thread. So we override the
+ // stop_others value passed in to us here:
+ const bool trampoline_stop_others = false;
+ ret_plan_sp = std::make_shared<AppleThreadPlanStepThroughObjCTrampoline>(
+ thread, this, dispatch_values, isa_addr, sel_addr,
+ trampoline_stop_others);
+ if (log) {
+ StreamString s;
+ ret_plan_sp->GetDescription(&s, eDescriptionLevelFull);
+ log->Printf("Using ObjC step plan: %s.\n", s.GetData());
+ }
+ }
+ }
+
+ return ret_plan_sp;
+}
+
+FunctionCaller *
+AppleObjCTrampolineHandler::GetLookupImplementationFunctionCaller() {
+ return m_impl_code->GetFunctionCaller();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h
new file mode 100644
index 000000000000..d120d671eeb3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h
@@ -0,0 +1,158 @@
+//===-- AppleObjCTrampolineHandler.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_AppleObjCTrampolineHandler_h_
+#define lldb_AppleObjCTrampolineHandler_h_
+
+#include <map>
+#include <mutex>
+#include <vector>
+
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/lldb-public.h"
+
+namespace lldb_private {
+
+class AppleObjCTrampolineHandler {
+public:
+ AppleObjCTrampolineHandler(const lldb::ProcessSP &process_sp,
+ const lldb::ModuleSP &objc_module_sp);
+
+ ~AppleObjCTrampolineHandler();
+
+ lldb::ThreadPlanSP GetStepThroughDispatchPlan(Thread &thread,
+ bool stop_others);
+
+ FunctionCaller *GetLookupImplementationFunctionCaller();
+
+ bool AddrIsMsgForward(lldb::addr_t addr) const {
+ return (addr == m_msg_forward_addr || addr == m_msg_forward_stret_addr);
+ }
+
+ struct DispatchFunction {
+ public:
+ enum FixUpState { eFixUpNone, eFixUpFixed, eFixUpToFix };
+
+ const char *name;
+ bool stret_return;
+ bool is_super;
+ bool is_super2;
+ FixUpState fixedup;
+ };
+
+ lldb::addr_t SetupDispatchFunction(Thread &thread,
+ ValueList &dispatch_values);
+
+private:
+ static const char *g_lookup_implementation_function_name;
+ static const char *g_lookup_implementation_with_stret_function_code;
+ static const char *g_lookup_implementation_no_stret_function_code;
+
+ class AppleObjCVTables {
+ public:
+ // These come from objc-gdb.h.
+ enum VTableFlags {
+ eOBJC_TRAMPOLINE_MESSAGE = (1 << 0), // trampoline acts like objc_msgSend
+ eOBJC_TRAMPOLINE_STRET = (1 << 1), // trampoline is struct-returning
+ eOBJC_TRAMPOLINE_VTABLE = (1 << 2) // trampoline is vtable dispatcher
+ };
+
+ private:
+ struct VTableDescriptor {
+ VTableDescriptor(uint32_t in_flags, lldb::addr_t in_code_start)
+ : flags(in_flags), code_start(in_code_start) {}
+
+ uint32_t flags;
+ lldb::addr_t code_start;
+ };
+
+ class VTableRegion {
+ public:
+ VTableRegion()
+ : m_valid(false), m_owner(nullptr),
+ m_header_addr(LLDB_INVALID_ADDRESS), m_code_start_addr(0),
+ m_code_end_addr(0), m_next_region(0) {}
+
+ VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr);
+
+ void SetUpRegion();
+
+ lldb::addr_t GetNextRegionAddr() { return m_next_region; }
+
+ lldb::addr_t GetCodeStart() { return m_code_start_addr; }
+
+ lldb::addr_t GetCodeEnd() { return m_code_end_addr; }
+
+ uint32_t GetFlagsForVTableAtAddress(lldb::addr_t address) { return 0; }
+
+ bool IsValid() { return m_valid; }
+
+ bool AddressInRegion(lldb::addr_t addr, uint32_t &flags);
+
+ void Dump(Stream &s);
+
+ public:
+ bool m_valid;
+ AppleObjCVTables *m_owner;
+ lldb::addr_t m_header_addr;
+ lldb::addr_t m_code_start_addr;
+ lldb::addr_t m_code_end_addr;
+ std::vector<VTableDescriptor> m_descriptors;
+ lldb::addr_t m_next_region;
+ };
+
+ public:
+ AppleObjCVTables(const lldb::ProcessSP &process_sp,
+ const lldb::ModuleSP &objc_module_sp);
+
+ ~AppleObjCVTables();
+
+ bool InitializeVTableSymbols();
+
+ static bool RefreshTrampolines(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+ bool ReadRegions();
+
+ bool ReadRegions(lldb::addr_t region_addr);
+
+ bool IsAddressInVTables(lldb::addr_t addr, uint32_t &flags);
+
+ lldb::ProcessSP GetProcessSP() { return m_process_wp.lock(); }
+
+ private:
+ lldb::ProcessWP m_process_wp;
+ typedef std::vector<VTableRegion> region_collection;
+ lldb::addr_t m_trampoline_header;
+ lldb::break_id_t m_trampolines_changed_bp_id;
+ region_collection m_regions;
+ lldb::ModuleSP m_objc_module_sp;
+ };
+
+ static const DispatchFunction g_dispatch_functions[];
+
+ typedef std::map<lldb::addr_t, int> MsgsendMap; // This table maps an dispatch
+ // fn address to the index in
+ // g_dispatch_functions
+ MsgsendMap m_msgSend_map;
+ lldb::ProcessWP m_process_wp;
+ lldb::ModuleSP m_objc_module_sp;
+ const char *m_lookup_implementation_function_code;
+ std::unique_ptr<UtilityFunction> m_impl_code;
+ std::mutex m_impl_function_mutex;
+ lldb::addr_t m_impl_fn_addr;
+ lldb::addr_t m_impl_stret_fn_addr;
+ lldb::addr_t m_msg_forward_addr;
+ lldb::addr_t m_msg_forward_stret_addr;
+ std::unique_ptr<AppleObjCVTables> m_vtables_up;
+};
+
+} // namespace lldb_private
+
+#endif // lldb_AppleObjCTrampolineHandler_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp
new file mode 100644
index 000000000000..26654e9212b9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp
@@ -0,0 +1,381 @@
+//===-- AppleObjCTypeEncodingParser.cpp -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AppleObjCTypeEncodingParser.h"
+
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StringLexer.h"
+
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb_utility;
+
+AppleObjCTypeEncodingParser::AppleObjCTypeEncodingParser(
+ ObjCLanguageRuntime &runtime)
+ : ObjCLanguageRuntime::EncodingToType(), m_runtime(runtime) {
+ if (!m_scratch_ast_ctx_up)
+ m_scratch_ast_ctx_up.reset(new ClangASTContext(runtime.GetProcess()
+ ->GetTarget()
+ .GetArchitecture()
+ .GetTriple()
+ .str()
+ .c_str()));
+}
+
+std::string
+AppleObjCTypeEncodingParser::ReadStructName(lldb_utility::StringLexer &type) {
+ StreamString buffer;
+ while (type.HasAtLeast(1) && type.Peek() != '=')
+ buffer.Printf("%c", type.Next());
+ return buffer.GetString();
+}
+
+std::string
+AppleObjCTypeEncodingParser::ReadQuotedString(lldb_utility::StringLexer &type) {
+ StreamString buffer;
+ while (type.HasAtLeast(1) && type.Peek() != '"')
+ buffer.Printf("%c", type.Next());
+ StringLexer::Character next = type.Next();
+ UNUSED_IF_ASSERT_DISABLED(next);
+ assert(next == '"');
+ return buffer.GetString();
+}
+
+uint32_t
+AppleObjCTypeEncodingParser::ReadNumber(lldb_utility::StringLexer &type) {
+ uint32_t total = 0;
+ while (type.HasAtLeast(1) && isdigit(type.Peek()))
+ total = 10 * total + (type.Next() - '0');
+ return total;
+}
+
+// as an extension to the published grammar recent runtimes emit structs like
+// this:
+// "{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}"
+
+AppleObjCTypeEncodingParser::StructElement::StructElement()
+ : name(""), type(clang::QualType()), bitfield(0) {}
+
+AppleObjCTypeEncodingParser::StructElement
+AppleObjCTypeEncodingParser::ReadStructElement(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression) {
+ StructElement retval;
+ if (type.NextIf('"'))
+ retval.name = ReadQuotedString(type);
+ if (!type.NextIf('"'))
+ return retval;
+ uint32_t bitfield_size = 0;
+ retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size);
+ retval.bitfield = bitfield_size;
+ return retval;
+}
+
+clang::QualType
+AppleObjCTypeEncodingParser::BuildStruct(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression) {
+ return BuildAggregate(ast_ctx, type, for_expression, '{', '}',
+ clang::TTK_Struct);
+}
+
+clang::QualType
+AppleObjCTypeEncodingParser::BuildUnion(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression) {
+ return BuildAggregate(ast_ctx, type, for_expression, '(', ')',
+ clang::TTK_Union);
+}
+
+clang::QualType AppleObjCTypeEncodingParser::BuildAggregate(
+ clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
+ bool for_expression, char opener, char closer, uint32_t kind) {
+ if (!type.NextIf(opener))
+ return clang::QualType();
+ std::string name(ReadStructName(type));
+
+ // We do not handle templated classes/structs at the moment. If the name has
+ // a < in it, we are going to abandon this. We're still obliged to parse it,
+ // so we just set a flag that means "Don't actually build anything."
+
+ const bool is_templated = name.find('<') != std::string::npos;
+
+ if (!type.NextIf('='))
+ return clang::QualType();
+ bool in_union = true;
+ std::vector<StructElement> elements;
+ while (in_union && type.HasAtLeast(1)) {
+ if (type.NextIf(closer)) {
+ in_union = false;
+ break;
+ } else {
+ auto element = ReadStructElement(ast_ctx, type, for_expression);
+ if (element.type.isNull())
+ break;
+ else
+ elements.push_back(element);
+ }
+ }
+ if (in_union)
+ return clang::QualType();
+
+ if (is_templated)
+ return clang::QualType(); // This is where we bail out. Sorry!
+
+ ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
+ if (!lldb_ctx)
+ return clang::QualType();
+ CompilerType union_type(lldb_ctx->CreateRecordType(
+ nullptr, lldb::eAccessPublic, name.c_str(), kind, lldb::eLanguageTypeC));
+ if (union_type) {
+ ClangASTContext::StartTagDeclarationDefinition(union_type);
+
+ unsigned int count = 0;
+ for (auto element : elements) {
+ if (element.name.empty()) {
+ StreamString elem_name;
+ elem_name.Printf("__unnamed_%u", count);
+ element.name = elem_name.GetString();
+ }
+ ClangASTContext::AddFieldToRecordType(
+ union_type, element.name.c_str(),
+ CompilerType(&ast_ctx, element.type), lldb::eAccessPublic,
+ element.bitfield);
+ ++count;
+ }
+ ClangASTContext::CompleteTagDeclarationDefinition(union_type);
+ }
+ return ClangUtil::GetQualType(union_type);
+}
+
+clang::QualType
+AppleObjCTypeEncodingParser::BuildArray(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression) {
+ if (!type.NextIf('['))
+ return clang::QualType();
+ uint32_t size = ReadNumber(type);
+ clang::QualType element_type(BuildType(ast_ctx, type, for_expression));
+ if (!type.NextIf(']'))
+ return clang::QualType();
+ ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
+ if (!lldb_ctx)
+ return clang::QualType();
+ CompilerType array_type(lldb_ctx->CreateArrayType(
+ CompilerType(&ast_ctx, element_type), size, false));
+ return ClangUtil::GetQualType(array_type);
+}
+
+// the runtime can emit these in the form of @"SomeType", giving more specifics
+// this would be interesting for expression parser interop, but since we
+// actually try to avoid exposing the ivar info to the expression evaluator,
+// consume but ignore the type info and always return an 'id'; if anything,
+// dynamic typing will resolve things for us anyway
+clang::QualType AppleObjCTypeEncodingParser::BuildObjCObjectPointerType(
+ clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
+ bool for_expression) {
+ if (!type.NextIf('@'))
+ return clang::QualType();
+
+ std::string name;
+
+ if (type.NextIf('"')) {
+ // We have to be careful here. We're used to seeing
+ // @"NSString"
+ // but in records it is possible that the string following an @ is the name
+ // of the next field and @ means "id". This is the case if anything
+ // unquoted except for "}", the end of the type, or another name follows
+ // the quoted string.
+ //
+ // E.g.
+ // - @"NSString"@ means "id, followed by a field named NSString of type id"
+ // - @"NSString"} means "a pointer to NSString and the end of the struct" -
+ // @"NSString""nextField" means "a pointer to NSString and a field named
+ // nextField" - @"NSString" followed by the end of the string means "a
+ // pointer to NSString"
+ //
+ // As a result, the rule is: If we see @ followed by a quoted string, we
+ // peek. - If we see }, ), ], the end of the string, or a quote ("), the
+ // quoted string is a class name. - If we see anything else, the quoted
+ // string is a field name and we push it back onto type.
+
+ name = ReadQuotedString(type);
+
+ if (type.HasAtLeast(1)) {
+ switch (type.Peek()) {
+ default:
+ // roll back
+ type.PutBack(name.length() +
+ 2); // undo our consumption of the string and of the quotes
+ name.clear();
+ break;
+ case '}':
+ case ')':
+ case ']':
+ case '"':
+ // the quoted string is a class name – see the rule
+ break;
+ }
+ } else {
+ // the quoted string is a class name – see the rule
+ }
+ }
+
+ if (for_expression && !name.empty()) {
+ size_t less_than_pos = name.find('<');
+
+ if (less_than_pos != std::string::npos) {
+ if (less_than_pos == 0)
+ return ast_ctx.getObjCIdType();
+ else
+ name.erase(less_than_pos);
+ }
+
+ DeclVendor *decl_vendor = m_runtime.GetDeclVendor();
+ if (!decl_vendor)
+ return clang::QualType();
+
+ auto types = decl_vendor->FindTypes(ConstString(name), /*max_matches*/ 1);
+
+// The user can forward-declare something that has no definition. The runtime
+// doesn't prohibit this at all. This is a rare and very weird case. We keep
+// this assert in debug builds so we catch other weird cases.
+#ifdef LLDB_CONFIGURATION_DEBUG
+ assert(!types.empty());
+#else
+ if (types.empty())
+ return ast_ctx.getObjCIdType();
+#endif
+
+ return ClangUtil::GetQualType(types.front().GetPointerType());
+ } else {
+ // We're going to resolve this dynamically anyway, so just smile and wave.
+ return ast_ctx.getObjCIdType();
+ }
+}
+
+clang::QualType
+AppleObjCTypeEncodingParser::BuildType(clang::ASTContext &ast_ctx,
+ StringLexer &type, bool for_expression,
+ uint32_t *bitfield_bit_size) {
+ if (!type.HasAtLeast(1))
+ return clang::QualType();
+
+ switch (type.Peek()) {
+ default:
+ break;
+ case '{':
+ return BuildStruct(ast_ctx, type, for_expression);
+ case '[':
+ return BuildArray(ast_ctx, type, for_expression);
+ case '(':
+ return BuildUnion(ast_ctx, type, for_expression);
+ case '@':
+ return BuildObjCObjectPointerType(ast_ctx, type, for_expression);
+ }
+
+ switch (type.Next()) {
+ default:
+ type.PutBack(1);
+ return clang::QualType();
+ case 'c':
+ return ast_ctx.CharTy;
+ case 'i':
+ return ast_ctx.IntTy;
+ case 's':
+ return ast_ctx.ShortTy;
+ case 'l':
+ return ast_ctx.getIntTypeForBitwidth(32, true);
+ // this used to be done like this:
+ // ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
+ // if (!lldb_ctx)
+ // return clang::QualType();
+ // return lldb_ctx->GetIntTypeFromBitSize(32, true).GetQualType();
+ // which uses one of the constants if one is available, but we don't think
+ // all this work is necessary.
+ case 'q':
+ return ast_ctx.LongLongTy;
+ case 'C':
+ return ast_ctx.UnsignedCharTy;
+ case 'I':
+ return ast_ctx.UnsignedIntTy;
+ case 'S':
+ return ast_ctx.UnsignedShortTy;
+ case 'L':
+ return ast_ctx.getIntTypeForBitwidth(32, false);
+ // see note for 'l'
+ case 'Q':
+ return ast_ctx.UnsignedLongLongTy;
+ case 'f':
+ return ast_ctx.FloatTy;
+ case 'd':
+ return ast_ctx.DoubleTy;
+ case 'B':
+ return ast_ctx.BoolTy;
+ case 'v':
+ return ast_ctx.VoidTy;
+ case '*':
+ return ast_ctx.getPointerType(ast_ctx.CharTy);
+ case '#':
+ return ast_ctx.getObjCClassType();
+ case ':':
+ return ast_ctx.getObjCSelType();
+ case 'b': {
+ uint32_t size = ReadNumber(type);
+ if (bitfield_bit_size) {
+ *bitfield_bit_size = size;
+ return ast_ctx.UnsignedIntTy; // FIXME: the spec is fairly vague here.
+ } else
+ return clang::QualType();
+ }
+ case 'r': {
+ clang::QualType target_type = BuildType(ast_ctx, type, for_expression);
+ if (target_type.isNull())
+ return clang::QualType();
+ else if (target_type == ast_ctx.UnknownAnyTy)
+ return ast_ctx.UnknownAnyTy;
+ else
+ return ast_ctx.getConstType(target_type);
+ }
+ case '^': {
+ if (!for_expression && type.NextIf('?')) {
+ // if we are not supporting the concept of unknownAny, but what is being
+ // created here is an unknownAny*, then we can just get away with a void*
+ // this is theoretically wrong (in the same sense as 'theoretically
+ // nothing exists') but is way better than outright failure in many
+ // practical cases
+ return ast_ctx.VoidPtrTy;
+ } else {
+ clang::QualType target_type = BuildType(ast_ctx, type, for_expression);
+ if (target_type.isNull())
+ return clang::QualType();
+ else if (target_type == ast_ctx.UnknownAnyTy)
+ return ast_ctx.UnknownAnyTy;
+ else
+ return ast_ctx.getPointerType(target_type);
+ }
+ }
+ case '?':
+ return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType();
+ }
+}
+
+CompilerType AppleObjCTypeEncodingParser::RealizeType(
+ clang::ASTContext &ast_ctx, const char *name, bool for_expression) {
+ if (name && name[0]) {
+ StringLexer lexer(name);
+ clang::QualType qual_type = BuildType(ast_ctx, lexer, for_expression);
+ return CompilerType(&ast_ctx, qual_type);
+ }
+ return CompilerType();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h
new file mode 100644
index 000000000000..e576e8f283f2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h
@@ -0,0 +1,83 @@
+//===-- AppleObjCTypeEncodingParser.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_AppleObjCTypeEncodingParser_h_
+#define liblldb_AppleObjCTypeEncodingParser_h_
+
+#include "clang/AST/ASTContext.h"
+
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_utility {
+class StringLexer;
+}
+
+namespace lldb_private {
+
+class AppleObjCTypeEncodingParser : public ObjCLanguageRuntime::EncodingToType {
+public:
+ AppleObjCTypeEncodingParser(ObjCLanguageRuntime &runtime);
+ ~AppleObjCTypeEncodingParser() override = default;
+
+ CompilerType RealizeType(clang::ASTContext &ast_ctx, const char *name,
+ bool for_expression) override;
+
+private:
+ struct StructElement {
+ std::string name;
+ clang::QualType type;
+ uint32_t bitfield;
+
+ StructElement();
+ ~StructElement() = default;
+ };
+
+ clang::QualType BuildType(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression,
+ uint32_t *bitfield_bit_size = nullptr);
+
+ clang::QualType BuildStruct(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression);
+
+ clang::QualType BuildAggregate(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression, char opener, char closer,
+ uint32_t kind);
+
+ clang::QualType BuildUnion(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression);
+
+ clang::QualType BuildArray(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression);
+
+ std::string ReadStructName(lldb_utility::StringLexer &type);
+
+ StructElement ReadStructElement(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression);
+
+ clang::QualType BuildObjCObjectPointerType(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression);
+
+ uint32_t ReadNumber(lldb_utility::StringLexer &type);
+
+ std::string ReadQuotedString(lldb_utility::StringLexer &type);
+
+ ObjCLanguageRuntime &m_runtime;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCTypeEncodingParser_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp
new file mode 100644
index 000000000000..d18435c9c6db
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp
@@ -0,0 +1,204 @@
+//===-- AppleThreadPlanStepThroughObjCTrampoline.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 "AppleThreadPlanStepThroughObjCTrampoline.h"
+
+#include "AppleObjCTrampolineHandler.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Utility/Log.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanStepThroughObjCTrampoline constructor
+AppleThreadPlanStepThroughObjCTrampoline::
+ AppleThreadPlanStepThroughObjCTrampoline(
+ Thread &thread, AppleObjCTrampolineHandler *trampoline_handler,
+ ValueList &input_values, lldb::addr_t isa_addr, lldb::addr_t sel_addr,
+ bool stop_others)
+ : ThreadPlan(ThreadPlan::eKindGeneric,
+ "MacOSX Step through ObjC Trampoline", thread, eVoteNoOpinion,
+ eVoteNoOpinion),
+ m_trampoline_handler(trampoline_handler),
+ m_args_addr(LLDB_INVALID_ADDRESS), m_input_values(input_values),
+ m_isa_addr(isa_addr), m_sel_addr(sel_addr), m_impl_function(nullptr),
+ m_stop_others(stop_others) {}
+
+// Destructor
+AppleThreadPlanStepThroughObjCTrampoline::
+ ~AppleThreadPlanStepThroughObjCTrampoline() {}
+
+void AppleThreadPlanStepThroughObjCTrampoline::DidPush() {
+ // Setting up the memory space for the called function text might require
+ // allocations, i.e. a nested function call. This needs to be done as a
+ // PreResumeAction.
+ m_thread.GetProcess()->AddPreResumeAction(PreResumeInitializeFunctionCaller,
+ (void *)this);
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::InitializeFunctionCaller() {
+ if (!m_func_sp) {
+ DiagnosticManager diagnostics;
+ m_args_addr =
+ m_trampoline_handler->SetupDispatchFunction(m_thread, m_input_values);
+
+ if (m_args_addr == LLDB_INVALID_ADDRESS) {
+ return false;
+ }
+ m_impl_function =
+ m_trampoline_handler->GetLookupImplementationFunctionCaller();
+ ExecutionContext exc_ctx;
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetStopOthers(m_stop_others);
+ m_thread.CalculateExecutionContext(exc_ctx);
+ m_func_sp = m_impl_function->GetThreadPlanToCallFunction(
+ exc_ctx, m_args_addr, options, diagnostics);
+ m_func_sp->SetOkayToDiscard(true);
+ m_thread.QueueThreadPlan(m_func_sp, false);
+ }
+ return true;
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::
+ PreResumeInitializeFunctionCaller(void *void_myself) {
+ AppleThreadPlanStepThroughObjCTrampoline *myself =
+ static_cast<AppleThreadPlanStepThroughObjCTrampoline *>(void_myself);
+ return myself->InitializeFunctionCaller();
+}
+
+void AppleThreadPlanStepThroughObjCTrampoline::GetDescription(
+ Stream *s, lldb::DescriptionLevel level) {
+ if (level == lldb::eDescriptionLevelBrief)
+ s->Printf("Step through ObjC trampoline");
+ else {
+ s->Printf("Stepping to implementation of ObjC method - obj: 0x%llx, isa: "
+ "0x%" PRIx64 ", sel: 0x%" PRIx64,
+ m_input_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
+ m_isa_addr, m_sel_addr);
+ }
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::ValidatePlan(Stream *error) {
+ return true;
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::DoPlanExplainsStop(
+ Event *event_ptr) {
+ // If we get asked to explain the stop it will be because something went
+ // wrong (like the implementation for selector function crashed... We're
+ // going to figure out what to do about that, so we do explain the stop.
+ return true;
+}
+
+lldb::StateType AppleThreadPlanStepThroughObjCTrampoline::GetPlanRunState() {
+ return eStateRunning;
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::ShouldStop(Event *event_ptr) {
+ // First stage: we are still handling the "call a function to get the target
+ // of the dispatch"
+ if (m_func_sp) {
+ if (!m_func_sp->IsPlanComplete()) {
+ return false;
+ } else {
+ if (!m_func_sp->PlanSucceeded()) {
+ SetPlanComplete(false);
+ return true;
+ }
+ m_func_sp.reset();
+ }
+ }
+
+ // Second stage, if all went well with the function calling, then fetch the
+ // target address, and queue up a "run to that address" plan.
+ if (!m_run_to_sp) {
+ Value target_addr_value;
+ ExecutionContext exc_ctx;
+ m_thread.CalculateExecutionContext(exc_ctx);
+ m_impl_function->FetchFunctionResults(exc_ctx, m_args_addr,
+ target_addr_value);
+ m_impl_function->DeallocateFunctionResults(exc_ctx, m_args_addr);
+ lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong();
+ Address target_so_addr;
+ target_so_addr.SetOpcodeLoadAddress(target_addr, exc_ctx.GetTargetPtr());
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (target_addr == 0) {
+ if (log)
+ log->Printf("Got target implementation of 0x0, stopping.");
+ SetPlanComplete();
+ return true;
+ }
+ if (m_trampoline_handler->AddrIsMsgForward(target_addr)) {
+ if (log)
+ log->Printf(
+ "Implementation lookup returned msgForward function: 0x%" PRIx64
+ ", stopping.",
+ target_addr);
+
+ SymbolContext sc = m_thread.GetStackFrameAtIndex(0)->GetSymbolContext(
+ eSymbolContextEverything);
+ Status status;
+ const bool abort_other_plans = false;
+ const bool first_insn = true;
+ const uint32_t frame_idx = 0;
+ m_run_to_sp = m_thread.QueueThreadPlanForStepOutNoShouldStop(
+ abort_other_plans, &sc, first_insn, m_stop_others, eVoteNoOpinion,
+ eVoteNoOpinion, frame_idx, status);
+ if (m_run_to_sp && status.Success())
+ m_run_to_sp->SetPrivate(true);
+ return false;
+ }
+
+ if (log)
+ log->Printf("Running to ObjC method implementation: 0x%" PRIx64,
+ target_addr);
+
+ ObjCLanguageRuntime *objc_runtime =
+ ObjCLanguageRuntime::Get(*GetThread().GetProcess());
+ assert(objc_runtime != nullptr);
+ objc_runtime->AddToMethodCache(m_isa_addr, m_sel_addr, target_addr);
+ if (log)
+ log->Printf("Adding {isa-addr=0x%" PRIx64 ", sel-addr=0x%" PRIx64
+ "} = addr=0x%" PRIx64 " to cache.",
+ m_isa_addr, m_sel_addr, target_addr);
+
+ // Extract the target address from the value:
+
+ m_run_to_sp = std::make_shared<ThreadPlanRunToAddress>(
+ m_thread, target_so_addr, m_stop_others);
+ m_thread.QueueThreadPlan(m_run_to_sp, false);
+ m_run_to_sp->SetPrivate(true);
+ return false;
+ } else if (m_thread.IsThreadPlanDone(m_run_to_sp.get())) {
+ // Third stage, work the run to target plan.
+ SetPlanComplete();
+ return true;
+ }
+ return false;
+}
+
+// The base class MischiefManaged does some cleanup - so you have to call it in
+// your MischiefManaged derived class.
+bool AppleThreadPlanStepThroughObjCTrampoline::MischiefManaged() {
+ return IsPlanComplete();
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::WillStop() { return true; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h
new file mode 100644
index 000000000000..96f37851a35f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h
@@ -0,0 +1,76 @@
+//===-- AppleThreadPlanStepThroughObjCTrampoline.h --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_AppleThreadPlanStepThroughObjCTrampoline_h_
+#define lldb_AppleThreadPlanStepThroughObjCTrampoline_h_
+
+#include "AppleObjCTrampolineHandler.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+class AppleThreadPlanStepThroughObjCTrampoline : public ThreadPlan {
+public:
+ AppleThreadPlanStepThroughObjCTrampoline(
+ Thread &thread, AppleObjCTrampolineHandler *trampoline_handler,
+ ValueList &values, lldb::addr_t isa_addr, lldb::addr_t sel_addr,
+ bool stop_others);
+
+ ~AppleThreadPlanStepThroughObjCTrampoline() override;
+
+ static bool PreResumeInitializeFunctionCaller(void *myself);
+
+ void GetDescription(Stream *s, lldb::DescriptionLevel level) override;
+
+ bool ValidatePlan(Stream *error) override;
+
+ lldb::StateType GetPlanRunState() override;
+
+ bool ShouldStop(Event *event_ptr) override;
+
+ bool StopOthers() override { return m_stop_others; }
+
+ // The base class MischiefManaged does some cleanup - so you have to call it
+ // in your MischiefManaged derived class.
+ bool MischiefManaged() override;
+
+ void DidPush() override;
+
+ bool WillStop() override;
+
+protected:
+ bool DoPlanExplainsStop(Event *event_ptr) override;
+
+private:
+ bool InitializeFunctionCaller();
+
+ AppleObjCTrampolineHandler *m_trampoline_handler; // FIXME - ensure this
+ // doesn't go away on us?
+ // SP maybe?
+ lldb::addr_t m_args_addr; // Stores the address for our step through function
+ // result structure.
+ // lldb::addr_t m_object_addr; // This is only for Description.
+ ValueList m_input_values;
+ lldb::addr_t m_isa_addr; // isa_addr and sel_addr are the keys we will use to
+ // cache the implementation.
+ lldb::addr_t m_sel_addr;
+ lldb::ThreadPlanSP m_func_sp; // This is the function call plan. We fill it
+ // at start, then set it
+ // to NULL when this plan is done. That way we know to go to:
+ lldb::ThreadPlanSP m_run_to_sp; // The plan that runs to the target.
+ FunctionCaller *m_impl_function; // This is a pointer to a impl function that
+ // is owned by the client that pushes this plan.
+ bool m_stop_others;
+};
+
+} // namespace lldb_private
+
+#endif // lldb_AppleThreadPlanStepThroughObjCTrampoline_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
new file mode 100644
index 000000000000..631c15c46ce8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
@@ -0,0 +1,436 @@
+//===-- ObjCLanguageRuntime.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "clang/AST/Type.h"
+
+#include "ObjCLanguageRuntime.h"
+
+#include "lldb/Core/MappedHash.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Timer.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/DJB.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+char ObjCLanguageRuntime::ID = 0;
+
+// Destructor
+ObjCLanguageRuntime::~ObjCLanguageRuntime() {}
+
+ObjCLanguageRuntime::ObjCLanguageRuntime(Process *process)
+ : LanguageRuntime(process), m_impl_cache(),
+ m_has_new_literals_and_indexing(eLazyBoolCalculate),
+ m_isa_to_descriptor(), m_hash_to_isa_map(), m_type_size_cache(),
+ m_isa_to_descriptor_stop_id(UINT32_MAX), m_complete_class_cache(),
+ m_negative_complete_class_cache() {}
+
+bool ObjCLanguageRuntime::IsWhitelistedRuntimeValue(ConstString name) {
+ static ConstString g_self = ConstString("self");
+ static ConstString g_cmd = ConstString("_cmd");
+ return name == g_self || name == g_cmd;
+}
+
+bool ObjCLanguageRuntime::AddClass(ObjCISA isa,
+ const ClassDescriptorSP &descriptor_sp,
+ const char *class_name) {
+ if (isa != 0) {
+ m_isa_to_descriptor[isa] = descriptor_sp;
+ // class_name is assumed to be valid
+ m_hash_to_isa_map.insert(std::make_pair(llvm::djbHash(class_name), isa));
+ return true;
+ }
+ return false;
+}
+
+void ObjCLanguageRuntime::AddToMethodCache(lldb::addr_t class_addr,
+ lldb::addr_t selector,
+ lldb::addr_t impl_addr) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log) {
+ log->Printf("Caching: class 0x%" PRIx64 " selector 0x%" PRIx64
+ " implementation 0x%" PRIx64 ".",
+ class_addr, selector, impl_addr);
+ }
+ m_impl_cache.insert(std::pair<ClassAndSel, lldb::addr_t>(
+ ClassAndSel(class_addr, selector), impl_addr));
+}
+
+lldb::addr_t ObjCLanguageRuntime::LookupInMethodCache(lldb::addr_t class_addr,
+ lldb::addr_t selector) {
+ MsgImplMap::iterator pos, end = m_impl_cache.end();
+ pos = m_impl_cache.find(ClassAndSel(class_addr, selector));
+ if (pos != end)
+ return (*pos).second;
+ return LLDB_INVALID_ADDRESS;
+}
+
+lldb::TypeSP
+ObjCLanguageRuntime::LookupInCompleteClassCache(ConstString &name) {
+ CompleteClassMap::iterator complete_class_iter =
+ m_complete_class_cache.find(name);
+
+ if (complete_class_iter != m_complete_class_cache.end()) {
+ // Check the weak pointer to make sure the type hasn't been unloaded
+ TypeSP complete_type_sp(complete_class_iter->second.lock());
+
+ if (complete_type_sp)
+ return complete_type_sp;
+ else
+ m_complete_class_cache.erase(name);
+ }
+
+ if (m_negative_complete_class_cache.count(name) > 0)
+ return TypeSP();
+
+ const ModuleList &modules = m_process->GetTarget().GetImages();
+
+ SymbolContextList sc_list;
+ const size_t matching_symbols =
+ modules.FindSymbolsWithNameAndType(name, eSymbolTypeObjCClass, sc_list);
+
+ if (matching_symbols) {
+ SymbolContext sc;
+
+ sc_list.GetContextAtIndex(0, sc);
+
+ ModuleSP module_sp(sc.module_sp);
+
+ if (!module_sp)
+ return TypeSP();
+
+ const bool exact_match = true;
+ const uint32_t max_matches = UINT32_MAX;
+ TypeList types;
+
+ llvm::DenseSet<SymbolFile *> searched_symbol_files;
+ const uint32_t num_types = module_sp->FindTypes(
+ name, exact_match, max_matches, searched_symbol_files, types);
+
+ if (num_types) {
+ uint32_t i;
+ for (i = 0; i < num_types; ++i) {
+ TypeSP type_sp(types.GetTypeAtIndex(i));
+
+ if (ClangASTContext::IsObjCObjectOrInterfaceType(
+ type_sp->GetForwardCompilerType())) {
+ if (type_sp->IsCompleteObjCClass()) {
+ m_complete_class_cache[name] = type_sp;
+ return type_sp;
+ }
+ }
+ }
+ }
+ }
+ m_negative_complete_class_cache.insert(name);
+ return TypeSP();
+}
+
+size_t ObjCLanguageRuntime::GetByteOffsetForIvar(CompilerType &parent_qual_type,
+ const char *ivar_name) {
+ return LLDB_INVALID_IVAR_OFFSET;
+}
+
+bool ObjCLanguageRuntime::ClassDescriptor::IsPointerValid(
+ lldb::addr_t value, uint32_t ptr_size, bool allow_NULLs, bool allow_tagged,
+ bool check_version_specific) const {
+ if (!value)
+ return allow_NULLs;
+ if ((value % 2) == 1 && allow_tagged)
+ return true;
+ if ((value % ptr_size) == 0)
+ return (check_version_specific ? CheckPointer(value, ptr_size) : true);
+ else
+ return false;
+}
+
+ObjCLanguageRuntime::ObjCISA
+ObjCLanguageRuntime::GetISA(ConstString name) {
+ ISAToDescriptorIterator pos = GetDescriptorIterator(name);
+ if (pos != m_isa_to_descriptor.end())
+ return pos->first;
+ return 0;
+}
+
+ObjCLanguageRuntime::ISAToDescriptorIterator
+ObjCLanguageRuntime::GetDescriptorIterator(ConstString name) {
+ ISAToDescriptorIterator end = m_isa_to_descriptor.end();
+
+ if (name) {
+ UpdateISAToDescriptorMap();
+ if (m_hash_to_isa_map.empty()) {
+ // No name hashes were provided, we need to just linearly power through
+ // the names and find a match
+ for (ISAToDescriptorIterator pos = m_isa_to_descriptor.begin();
+ pos != end; ++pos) {
+ if (pos->second->GetClassName() == name)
+ return pos;
+ }
+ } else {
+ // Name hashes were provided, so use them to efficiently lookup name to
+ // isa/descriptor
+ const uint32_t name_hash = llvm::djbHash(name.GetStringRef());
+ std::pair<HashToISAIterator, HashToISAIterator> range =
+ m_hash_to_isa_map.equal_range(name_hash);
+ for (HashToISAIterator range_pos = range.first; range_pos != range.second;
+ ++range_pos) {
+ ISAToDescriptorIterator pos =
+ m_isa_to_descriptor.find(range_pos->second);
+ if (pos != m_isa_to_descriptor.end()) {
+ if (pos->second->GetClassName() == name)
+ return pos;
+ }
+ }
+ }
+ }
+ return end;
+}
+
+std::pair<ObjCLanguageRuntime::ISAToDescriptorIterator,
+ ObjCLanguageRuntime::ISAToDescriptorIterator>
+ObjCLanguageRuntime::GetDescriptorIteratorPair(bool update_if_needed) {
+ if (update_if_needed)
+ UpdateISAToDescriptorMapIfNeeded();
+
+ return std::pair<ObjCLanguageRuntime::ISAToDescriptorIterator,
+ ObjCLanguageRuntime::ISAToDescriptorIterator>(
+ m_isa_to_descriptor.begin(), m_isa_to_descriptor.end());
+}
+
+ObjCLanguageRuntime::ObjCISA
+ObjCLanguageRuntime::GetParentClass(ObjCLanguageRuntime::ObjCISA isa) {
+ ClassDescriptorSP objc_class_sp(GetClassDescriptorFromISA(isa));
+ if (objc_class_sp) {
+ ClassDescriptorSP objc_super_class_sp(objc_class_sp->GetSuperclass());
+ if (objc_super_class_sp)
+ return objc_super_class_sp->GetISA();
+ }
+ return 0;
+}
+
+ConstString
+ObjCLanguageRuntime::GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa) {
+ ClassDescriptorSP objc_class_sp(GetNonKVOClassDescriptor(isa));
+ if (objc_class_sp)
+ return objc_class_sp->GetClassName();
+ return ConstString();
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetClassDescriptorFromClassName(
+ ConstString class_name) {
+ ISAToDescriptorIterator pos = GetDescriptorIterator(class_name);
+ if (pos != m_isa_to_descriptor.end())
+ return pos->second;
+ return ClassDescriptorSP();
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetClassDescriptor(ValueObject &valobj) {
+ ClassDescriptorSP objc_class_sp;
+ // if we get an invalid VO (which might still happen when playing around with
+ // pointers returned by the expression parser, don't consider this a valid
+ // ObjC object)
+ if (valobj.GetCompilerType().IsValid()) {
+ addr_t isa_pointer = valobj.GetPointerValue();
+ if (isa_pointer != LLDB_INVALID_ADDRESS) {
+ ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ Status error;
+ ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error);
+ if (isa != LLDB_INVALID_ADDRESS)
+ objc_class_sp = GetClassDescriptorFromISA(isa);
+ }
+ }
+ }
+ return objc_class_sp;
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetNonKVOClassDescriptor(ValueObject &valobj) {
+ ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp(
+ GetClassDescriptor(valobj));
+ if (objc_class_sp) {
+ if (!objc_class_sp->IsKVO())
+ return objc_class_sp;
+
+ ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());
+ if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())
+ return non_kvo_objc_class_sp;
+ }
+ return ClassDescriptorSP();
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetClassDescriptorFromISA(ObjCISA isa) {
+ if (isa) {
+ UpdateISAToDescriptorMap();
+ ObjCLanguageRuntime::ISAToDescriptorIterator pos =
+ m_isa_to_descriptor.find(isa);
+ if (pos != m_isa_to_descriptor.end())
+ return pos->second;
+ }
+ return ClassDescriptorSP();
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetNonKVOClassDescriptor(ObjCISA isa) {
+ if (isa) {
+ ClassDescriptorSP objc_class_sp = GetClassDescriptorFromISA(isa);
+ if (objc_class_sp && objc_class_sp->IsValid()) {
+ if (!objc_class_sp->IsKVO())
+ return objc_class_sp;
+
+ ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());
+ if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())
+ return non_kvo_objc_class_sp;
+ }
+ }
+ return ClassDescriptorSP();
+}
+
+CompilerType
+ObjCLanguageRuntime::EncodingToType::RealizeType(const char *name,
+ bool for_expression) {
+ if (m_scratch_ast_ctx_up)
+ return RealizeType(*m_scratch_ast_ctx_up, name, for_expression);
+ return CompilerType();
+}
+
+CompilerType ObjCLanguageRuntime::EncodingToType::RealizeType(
+ ClangASTContext &ast_ctx, const char *name, bool for_expression) {
+ clang::ASTContext *clang_ast = ast_ctx.getASTContext();
+ if (!clang_ast)
+ return CompilerType();
+ return RealizeType(*clang_ast, name, for_expression);
+}
+
+ObjCLanguageRuntime::EncodingToType::~EncodingToType() {}
+
+ObjCLanguageRuntime::EncodingToTypeSP ObjCLanguageRuntime::GetEncodingToType() {
+ return nullptr;
+}
+
+bool ObjCLanguageRuntime::GetTypeBitSize(const CompilerType &compiler_type,
+ uint64_t &size) {
+ void *opaque_ptr = compiler_type.GetOpaqueQualType();
+ size = m_type_size_cache.Lookup(opaque_ptr);
+ // an ObjC object will at least have an ISA, so 0 is definitely not OK
+ if (size > 0)
+ return true;
+
+ ClassDescriptorSP class_descriptor_sp =
+ GetClassDescriptorFromClassName(compiler_type.GetTypeName());
+ if (!class_descriptor_sp)
+ return false;
+
+ int32_t max_offset = INT32_MIN;
+ uint64_t sizeof_max = 0;
+ bool found = false;
+
+ for (size_t idx = 0; idx < class_descriptor_sp->GetNumIVars(); idx++) {
+ const auto &ivar = class_descriptor_sp->GetIVarAtIndex(idx);
+ int32_t cur_offset = ivar.m_offset;
+ if (cur_offset > max_offset) {
+ max_offset = cur_offset;
+ sizeof_max = ivar.m_size;
+ found = true;
+ }
+ }
+
+ size = 8 * (max_offset + sizeof_max);
+ if (found)
+ m_type_size_cache.Insert(opaque_ptr, size);
+
+ return found;
+}
+
+lldb::BreakpointPreconditionSP
+ObjCLanguageRuntime::GetBreakpointExceptionPrecondition(LanguageType language,
+ bool throw_bp) {
+ if (language != eLanguageTypeObjC)
+ return lldb::BreakpointPreconditionSP();
+ if (!throw_bp)
+ return lldb::BreakpointPreconditionSP();
+ BreakpointPreconditionSP precondition_sp(
+ new ObjCLanguageRuntime::ObjCExceptionPrecondition());
+ return precondition_sp;
+}
+
+// Exception breakpoint Precondition class for ObjC:
+void ObjCLanguageRuntime::ObjCExceptionPrecondition::AddClassName(
+ const char *class_name) {
+ m_class_names.insert(class_name);
+}
+
+ObjCLanguageRuntime::ObjCExceptionPrecondition::ObjCExceptionPrecondition() {}
+
+bool ObjCLanguageRuntime::ObjCExceptionPrecondition::EvaluatePrecondition(
+ StoppointCallbackContext &context) {
+ return true;
+}
+
+void ObjCLanguageRuntime::ObjCExceptionPrecondition::GetDescription(
+ Stream &stream, lldb::DescriptionLevel level) {}
+
+Status ObjCLanguageRuntime::ObjCExceptionPrecondition::ConfigurePrecondition(
+ Args &args) {
+ Status error;
+ if (args.GetArgumentCount() > 0)
+ error.SetErrorString(
+ "The ObjC Exception breakpoint doesn't support extra options.");
+ return error;
+}
+
+llvm::Optional<CompilerType>
+ObjCLanguageRuntime::GetRuntimeType(CompilerType base_type) {
+ CompilerType class_type;
+ bool is_pointer_type = false;
+
+ if (ClangASTContext::IsObjCObjectPointerType(base_type, &class_type))
+ is_pointer_type = true;
+ else if (ClangASTContext::IsObjCObjectOrInterfaceType(base_type))
+ class_type = base_type;
+ else
+ return llvm::None;
+
+ if (!class_type)
+ return llvm::None;
+
+ ConstString class_name(class_type.GetConstTypeName());
+ if (!class_name)
+ return llvm::None;
+
+ TypeSP complete_objc_class_type_sp = LookupInCompleteClassCache(class_name);
+ if (!complete_objc_class_type_sp)
+ return llvm::None;
+
+ 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;
+ }
+
+ return llvm::None;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
new file mode 100644
index 000000000000..1925c78ed342
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
@@ -0,0 +1,429 @@
+//===-- ObjCLanguageRuntime.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ObjCLanguageRuntime_h_
+#define liblldb_ObjCLanguageRuntime_h_
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <unordered_set>
+
+#include "llvm/Support/Casting.h"
+
+#include "lldb/Breakpoint/BreakpointPrecondition.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Core/ThreadSafeDenseMap.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+class CommandObjectObjC_ClassTable_Dump;
+
+namespace lldb_private {
+
+class UtilityFunction;
+
+class ObjCLanguageRuntime : public LanguageRuntime {
+public:
+ enum class ObjCRuntimeVersions {
+ eObjC_VersionUnknown = 0,
+ eAppleObjC_V1 = 1,
+ eAppleObjC_V2 = 2
+ };
+
+ typedef lldb::addr_t ObjCISA;
+
+ class ClassDescriptor;
+ typedef std::shared_ptr<ClassDescriptor> ClassDescriptorSP;
+
+ // the information that we want to support retrieving from an ObjC class this
+ // needs to be pure virtual since there are at least 2 different
+ // implementations of the runtime, and more might come
+ class ClassDescriptor {
+ public:
+ ClassDescriptor()
+ : m_is_kvo(eLazyBoolCalculate), m_is_cf(eLazyBoolCalculate),
+ m_type_wp() {}
+
+ virtual ~ClassDescriptor() = default;
+
+ virtual ConstString GetClassName() = 0;
+
+ virtual ClassDescriptorSP GetSuperclass() = 0;
+
+ virtual ClassDescriptorSP GetMetaclass() const = 0;
+
+ // virtual if any implementation has some other version-specific rules but
+ // for the known v1/v2 this is all that needs to be done
+ virtual bool IsKVO() {
+ if (m_is_kvo == eLazyBoolCalculate) {
+ const char *class_name = GetClassName().AsCString();
+ if (class_name && *class_name)
+ m_is_kvo =
+ (LazyBool)(strstr(class_name, "NSKVONotifying_") == class_name);
+ }
+ return (m_is_kvo == eLazyBoolYes);
+ }
+
+ // virtual if any implementation has some other version-specific rules but
+ // for the known v1/v2 this is all that needs to be done
+ virtual bool IsCFType() {
+ if (m_is_cf == eLazyBoolCalculate) {
+ const char *class_name = GetClassName().AsCString();
+ if (class_name && *class_name)
+ m_is_cf = (LazyBool)(strcmp(class_name, "__NSCFType") == 0 ||
+ strcmp(class_name, "NSCFType") == 0);
+ }
+ return (m_is_cf == eLazyBoolYes);
+ }
+
+ virtual bool IsValid() = 0;
+
+ virtual bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
+ uint64_t *value_bits = nullptr,
+ uint64_t *payload = nullptr) = 0;
+
+ virtual uint64_t GetInstanceSize() = 0;
+
+ // use to implement version-specific additional constraints on pointers
+ virtual bool CheckPointer(lldb::addr_t value, uint32_t ptr_size) const {
+ return true;
+ }
+
+ virtual ObjCISA GetISA() = 0;
+
+ // This should return true iff the interface could be completed
+ virtual bool
+ Describe(std::function<void(ObjCISA)> const &superclass_func,
+ std::function<bool(const char *, const char *)> const
+ &instance_method_func,
+ std::function<bool(const char *, const char *)> const
+ &class_method_func,
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)> const &ivar_func) const {
+ return false;
+ }
+
+ lldb::TypeSP GetType() { return m_type_wp.lock(); }
+
+ void SetType(const lldb::TypeSP &type_sp) { m_type_wp = type_sp; }
+
+ struct iVarDescriptor {
+ ConstString m_name;
+ CompilerType m_type;
+ uint64_t m_size;
+ int32_t m_offset;
+ };
+
+ virtual size_t GetNumIVars() { return 0; }
+
+ virtual iVarDescriptor GetIVarAtIndex(size_t idx) {
+ return iVarDescriptor();
+ }
+
+ protected:
+ bool IsPointerValid(lldb::addr_t value, uint32_t ptr_size,
+ bool allow_NULLs = false, bool allow_tagged = false,
+ bool check_version_specific = false) const;
+
+ private:
+ LazyBool m_is_kvo;
+ LazyBool m_is_cf;
+ lldb::TypeWP m_type_wp;
+ };
+
+ class EncodingToType {
+ public:
+ virtual ~EncodingToType();
+
+ virtual CompilerType RealizeType(ClangASTContext &ast_ctx, const char *name,
+ bool for_expression);
+ virtual CompilerType RealizeType(const char *name, bool for_expression);
+
+ virtual CompilerType RealizeType(clang::ASTContext &ast_ctx,
+ const char *name, bool for_expression) = 0;
+
+ protected:
+ std::unique_ptr<ClangASTContext> m_scratch_ast_ctx_up;
+ };
+
+ class ObjCExceptionPrecondition : public BreakpointPrecondition {
+ public:
+ ObjCExceptionPrecondition();
+
+ ~ObjCExceptionPrecondition() override = default;
+
+ bool EvaluatePrecondition(StoppointCallbackContext &context) override;
+ void GetDescription(Stream &stream, lldb::DescriptionLevel level) override;
+ Status ConfigurePrecondition(Args &args) override;
+
+ protected:
+ void AddClassName(const char *class_name);
+
+ private:
+ std::unordered_set<std::string> m_class_names;
+ };
+
+ static lldb::BreakpointPreconditionSP
+ GetBreakpointExceptionPrecondition(lldb::LanguageType language,
+ bool throw_bp);
+
+ class TaggedPointerVendor {
+ public:
+ virtual ~TaggedPointerVendor() = default;
+
+ virtual bool IsPossibleTaggedPointer(lldb::addr_t ptr) = 0;
+
+ virtual ObjCLanguageRuntime::ClassDescriptorSP
+ GetClassDescriptor(lldb::addr_t ptr) = 0;
+
+ protected:
+ TaggedPointerVendor() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendor);
+ };
+
+ ~ObjCLanguageRuntime() override;
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || LanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ static ObjCLanguageRuntime *Get(Process &process) {
+ return llvm::cast_or_null<ObjCLanguageRuntime>(
+ process.GetLanguageRuntime(lldb::eLanguageTypeObjC));
+ }
+
+ virtual TaggedPointerVendor *GetTaggedPointerVendor() { return nullptr; }
+
+ typedef std::shared_ptr<EncodingToType> EncodingToTypeSP;
+
+ virtual EncodingToTypeSP GetEncodingToType();
+
+ virtual ClassDescriptorSP GetClassDescriptor(ValueObject &in_value);
+
+ ClassDescriptorSP GetNonKVOClassDescriptor(ValueObject &in_value);
+
+ virtual ClassDescriptorSP
+ GetClassDescriptorFromClassName(ConstString class_name);
+
+ virtual ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa);
+
+ ClassDescriptorSP GetNonKVOClassDescriptor(ObjCISA isa);
+
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeObjC;
+ }
+
+ virtual bool IsModuleObjCLibrary(const lldb::ModuleSP &module_sp) = 0;
+
+ virtual bool ReadObjCLibrary(const lldb::ModuleSP &module_sp) = 0;
+
+ virtual bool HasReadObjCLibrary() = 0;
+
+ lldb::addr_t LookupInMethodCache(lldb::addr_t class_addr, lldb::addr_t sel);
+
+ void AddToMethodCache(lldb::addr_t class_addr, lldb::addr_t sel,
+ lldb::addr_t impl_addr);
+
+ TypeAndOrName LookupInClassNameCache(lldb::addr_t class_addr);
+
+ void AddToClassNameCache(lldb::addr_t class_addr, const char *name,
+ lldb::TypeSP type_sp);
+
+ void AddToClassNameCache(lldb::addr_t class_addr,
+ const TypeAndOrName &class_or_type_name);
+
+ lldb::TypeSP LookupInCompleteClassCache(ConstString &name);
+
+ llvm::Optional<CompilerType> GetRuntimeType(CompilerType base_type) override;
+
+ virtual UtilityFunction *CreateObjectChecker(const char *) = 0;
+
+ virtual ObjCRuntimeVersions GetRuntimeVersion() const {
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+ }
+
+ bool IsValidISA(ObjCISA isa) {
+ UpdateISAToDescriptorMap();
+ return m_isa_to_descriptor.count(isa) > 0;
+ }
+
+ virtual void UpdateISAToDescriptorMapIfNeeded() = 0;
+
+ void UpdateISAToDescriptorMap() {
+ if (m_process && m_process->GetStopID() != m_isa_to_descriptor_stop_id) {
+ UpdateISAToDescriptorMapIfNeeded();
+ }
+ }
+
+ virtual ObjCISA GetISA(ConstString name);
+
+ virtual ConstString GetActualTypeName(ObjCISA isa);
+
+ virtual ObjCISA GetParentClass(ObjCISA isa);
+
+ // Finds the byte offset of the child_type ivar in parent_type. If it can't
+ // find the offset, returns LLDB_INVALID_IVAR_OFFSET.
+
+ virtual size_t GetByteOffsetForIvar(CompilerType &parent_qual_type,
+ const char *ivar_name);
+
+ bool HasNewLiteralsAndIndexing() {
+ if (m_has_new_literals_and_indexing == eLazyBoolCalculate) {
+ if (CalculateHasNewLiteralsAndIndexing())
+ m_has_new_literals_and_indexing = eLazyBoolYes;
+ else
+ m_has_new_literals_and_indexing = eLazyBoolNo;
+ }
+
+ return (m_has_new_literals_and_indexing == eLazyBoolYes);
+ }
+
+ void SymbolsDidLoad(const ModuleList &module_list) override {
+ m_negative_complete_class_cache.clear();
+ }
+
+ bool GetTypeBitSize(const CompilerType &compiler_type,
+ uint64_t &size) override;
+
+ /// Check whether the name is "self" or "_cmd" and should show up in
+ /// "frame variable".
+ bool IsWhitelistedRuntimeValue(ConstString name) override;
+
+protected:
+ // Classes that inherit from ObjCLanguageRuntime can see and modify these
+ ObjCLanguageRuntime(Process *process);
+
+ virtual bool CalculateHasNewLiteralsAndIndexing() { return false; }
+
+ bool ISAIsCached(ObjCISA isa) const {
+ return m_isa_to_descriptor.find(isa) != m_isa_to_descriptor.end();
+ }
+
+ bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp) {
+ if (isa != 0) {
+ m_isa_to_descriptor[isa] = descriptor_sp;
+ return true;
+ }
+ return false;
+ }
+
+ bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp,
+ const char *class_name);
+
+ bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp,
+ uint32_t class_name_hash) {
+ if (isa != 0) {
+ m_isa_to_descriptor[isa] = descriptor_sp;
+ m_hash_to_isa_map.insert(std::make_pair(class_name_hash, isa));
+ return true;
+ }
+ return false;
+ }
+
+private:
+ // We keep a map of <Class,Selector>->Implementation so we don't have to call
+ // the resolver function over and over.
+
+ // FIXME: We need to watch for the loading of Protocols, and flush the cache
+ // for any
+ // class that we see so changed.
+
+ struct ClassAndSel {
+ ClassAndSel() {
+ sel_addr = LLDB_INVALID_ADDRESS;
+ class_addr = LLDB_INVALID_ADDRESS;
+ }
+
+ ClassAndSel(lldb::addr_t in_sel_addr, lldb::addr_t in_class_addr)
+ : class_addr(in_class_addr), sel_addr(in_sel_addr) {}
+
+ bool operator==(const ClassAndSel &rhs) {
+ if (class_addr == rhs.class_addr && sel_addr == rhs.sel_addr)
+ return true;
+ else
+ return false;
+ }
+
+ bool operator<(const ClassAndSel &rhs) const {
+ if (class_addr < rhs.class_addr)
+ return true;
+ else if (class_addr > rhs.class_addr)
+ return false;
+ else {
+ if (sel_addr < rhs.sel_addr)
+ return true;
+ else
+ return false;
+ }
+ }
+
+ lldb::addr_t class_addr;
+ lldb::addr_t sel_addr;
+ };
+
+ typedef std::map<ClassAndSel, lldb::addr_t> MsgImplMap;
+ typedef std::map<ObjCISA, ClassDescriptorSP> ISAToDescriptorMap;
+ typedef std::multimap<uint32_t, ObjCISA> HashToISAMap;
+ typedef ISAToDescriptorMap::iterator ISAToDescriptorIterator;
+ typedef HashToISAMap::iterator HashToISAIterator;
+ typedef ThreadSafeDenseMap<void *, uint64_t> TypeSizeCache;
+
+ MsgImplMap m_impl_cache;
+ LazyBool m_has_new_literals_and_indexing;
+ ISAToDescriptorMap m_isa_to_descriptor;
+ HashToISAMap m_hash_to_isa_map;
+ TypeSizeCache m_type_size_cache;
+
+protected:
+ uint32_t m_isa_to_descriptor_stop_id;
+
+ typedef std::map<ConstString, lldb::TypeWP> CompleteClassMap;
+ CompleteClassMap m_complete_class_cache;
+
+ struct ConstStringSetHelpers {
+ size_t operator()(ConstString arg) const // for hashing
+ {
+ return (size_t)arg.GetCString();
+ }
+ bool operator()(ConstString arg1,
+ ConstString arg2) const // for equality
+ {
+ return arg1.operator==(arg2);
+ }
+ };
+ typedef std::unordered_set<ConstString, ConstStringSetHelpers,
+ ConstStringSetHelpers>
+ CompleteClassSet;
+ CompleteClassSet m_negative_complete_class_cache;
+
+ ISAToDescriptorIterator GetDescriptorIterator(ConstString name);
+
+ friend class ::CommandObjectObjC_ClassTable_Dump;
+
+ std::pair<ISAToDescriptorIterator, ISAToDescriptorIterator>
+ GetDescriptorIteratorPair(bool update_if_needed = true);
+
+ void ReadObjCLibraryIfNeeded(const ModuleList &module_list);
+
+ DISALLOW_COPY_AND_ASSIGN(ObjCLanguageRuntime);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ObjCLanguageRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.cpp
new file mode 100644
index 000000000000..60549663db66
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.cpp
@@ -0,0 +1,191 @@
+//===-- RenderScriptExpressionOpts.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <string>
+
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+
+#include "clang/Basic/TargetOptions.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+
+#include "RenderScriptExpressionOpts.h"
+#include "RenderScriptRuntime.h"
+#include "RenderScriptx86ABIFixups.h"
+
+using namespace lldb_private;
+using namespace lldb_renderscript;
+
+// [``slang``](https://android.googlesource.com/platform/frameworks/compile/slang),
+// the compiler frontend for RenderScript embeds an ARM specific triple in IR
+// that is shipped in the app, after generating IR that has some assumptions
+// that an ARM device is the target. As the IR is then compiled on a device of
+// unknown (at time the IR was generated at least) architecture, when calling
+// RenderScript API function as part of debugger expressions, we have to
+// perform a fixup pass that removes those assumptions right before the module
+// is sent to be generated by the llvm backend.
+
+namespace {
+bool registerRSDefaultTargetOpts(clang::TargetOptions &proto,
+ const llvm::Triple::ArchType &arch) {
+ switch (arch) {
+ case llvm::Triple::ArchType::x86:
+ proto.Triple = "i686--linux-android";
+ proto.CPU = "atom";
+ proto.Features.push_back("+long64");
+ // Fallthrough for common x86 family features
+ LLVM_FALLTHROUGH;
+ case llvm::Triple::ArchType::x86_64:
+ proto.Features.push_back("+mmx");
+ proto.Features.push_back("+sse");
+ proto.Features.push_back("+sse2");
+ proto.Features.push_back("+sse3");
+ proto.Features.push_back("+ssse3");
+ proto.Features.push_back("+sse4.1");
+ proto.Features.push_back("+sse4.2");
+ break;
+ case llvm::Triple::ArchType::mipsel:
+ // pretend this is `arm' for the front-end
+ proto.Triple = "armv7-none-linux-android";
+ proto.CPU = "";
+ proto.Features.push_back("+long64");
+ break;
+ case llvm::Triple::ArchType::mips64el:
+ // pretend this is `aarch64' for the front-end
+ proto.Triple = "aarch64-none-linux-android";
+ proto.CPU = "";
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+} // end anonymous namespace
+
+bool RenderScriptRuntimeModulePass::runOnModule(llvm::Module &module) {
+ bool changed_module = false;
+ Log *log(
+ GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_EXPRESSIONS));
+
+ std::string err;
+ llvm::StringRef real_triple =
+ m_process_ptr->GetTarget().GetArchitecture().GetTriple().getTriple();
+ const llvm::Target *target_info =
+ llvm::TargetRegistry::lookupTarget(real_triple, err);
+ if (!target_info) {
+ if (log)
+ log->Warning("couldn't determine real target architecture: '%s'",
+ err.c_str());
+ return false;
+ }
+
+ llvm::Optional<llvm::Reloc::Model> reloc_model = llvm::None;
+ assert(m_process_ptr && "no available lldb process");
+ switch (m_process_ptr->GetTarget().GetArchitecture().GetMachine()) {
+ case llvm::Triple::ArchType::x86:
+ changed_module |= fixupX86FunctionCalls(module);
+ // For some reason this triple gets totally missed by the backend, and must
+ // be set manually. There a reference in bcc/Main.cpp about auto feature-
+ // detection being removed from LLVM3.5, but I can't see that discussion
+ // anywhere public.
+ real_triple = "i686--linux-android";
+ break;
+ case llvm::Triple::ArchType::x86_64:
+ changed_module |= fixupX86_64FunctionCalls(module);
+ break;
+ case llvm::Triple::ArchType::mipsel:
+ case llvm::Triple::ArchType::mips64el:
+ // No actual IR fixup pass is needed on MIPS, but the datalayout and
+ // targetmachine do need to be explicitly set.
+
+ // bcc explicitly compiles MIPS code to use the static relocation model due
+ // to an issue with relocations in mclinker. see
+ // libbcc/support/CompilerConfig.cpp for details
+ reloc_model = llvm::Reloc::Static;
+ changed_module = true;
+ break;
+ case llvm::Triple::ArchType::arm:
+ case llvm::Triple::ArchType::aarch64:
+ // ARM subtargets need no fixup passes as they are the initial target as
+ // generated by the
+ // slang compiler frontend.
+ break;
+ default:
+ if (log)
+ log->Warning("Ignoring unknown renderscript target");
+ return false;
+ }
+
+ if (changed_module) {
+ llvm::TargetOptions options;
+ llvm::TargetMachine *target_machine = target_info->createTargetMachine(
+ real_triple, "", "", options, reloc_model);
+ assert(target_machine &&
+ "failed to identify RenderScriptRuntime target machine");
+ // We've been using a triple and datalayout of some ARM variant all along,
+ // so we need to let the backend know that this is no longer the case.
+ if (log) {
+ log->Printf("%s - Changing RS target triple to '%s'", __FUNCTION__,
+ real_triple.str().c_str());
+ log->Printf(
+ "%s - Changing RS datalayout to '%s'", __FUNCTION__,
+ target_machine->createDataLayout().getStringRepresentation().c_str());
+ }
+ module.setTargetTriple(real_triple);
+ module.setDataLayout(target_machine->createDataLayout());
+ }
+ return changed_module;
+}
+
+char RenderScriptRuntimeModulePass::ID = 0;
+
+namespace lldb_private {
+
+bool RenderScriptRuntime::GetOverrideExprOptions(clang::TargetOptions &proto) {
+ auto *process = GetProcess();
+ assert(process);
+ return registerRSDefaultTargetOpts(
+ proto, process->GetTarget().GetArchitecture().GetMachine());
+}
+
+bool RenderScriptRuntime::GetIRPasses(LLVMUserExpression::IRPasses &passes) {
+ if (!m_ir_passes)
+ m_ir_passes = new RSIRPasses(GetProcess());
+ assert(m_ir_passes);
+
+ passes.EarlyPasses = m_ir_passes->EarlyPasses;
+ passes.LatePasses = m_ir_passes->LatePasses;
+
+ return true;
+}
+
+namespace lldb_renderscript {
+
+RSIRPasses::RSIRPasses(Process *process) {
+ IRPasses();
+ assert(process);
+
+ EarlyPasses = std::make_shared<llvm::legacy::PassManager>();
+ assert(EarlyPasses);
+ EarlyPasses->add(new RenderScriptRuntimeModulePass(process));
+}
+
+RSIRPasses::~RSIRPasses() {}
+
+} // namespace lldb_renderscript
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.h
new file mode 100644
index 000000000000..3ec4e37b6db0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.h
@@ -0,0 +1,52 @@
+//===-- RenderScriptExpressionOpts.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_RENDERSCRIPT_EXPROPTS_H
+#define LLDB_RENDERSCRIPT_EXPROPTS_H
+
+#include "llvm/IR/Module.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/lldb-private.h"
+
+#include "RenderScriptRuntime.h"
+#include "RenderScriptx86ABIFixups.h"
+
+// RenderScriptRuntimeModulePass is a simple llvm::ModulesPass that is used
+// during expression evaluation to apply RenderScript-specific fixes for
+// expression evaluation. In particular this is used to make expression IR
+// conformant with the ABI generated by the slang frontend. This ModulePass is
+// executed in ClangExpressionParser::PrepareForExecution whenever an
+// expression's DWARF language is eLanguageTypeExtRenderscript
+
+class RenderScriptRuntimeModulePass : public llvm::ModulePass {
+public:
+ static char ID;
+ RenderScriptRuntimeModulePass(const lldb_private::Process *process)
+ : ModulePass(ID), m_process_ptr(process) {}
+
+ bool runOnModule(llvm::Module &module) override;
+
+private:
+ const lldb_private::Process *m_process_ptr;
+};
+
+namespace lldb_private {
+namespace lldb_renderscript {
+struct RSIRPasses : public lldb_private::LLVMUserExpression::IRPasses {
+ RSIRPasses(lldb_private::Process *process);
+
+ ~RSIRPasses();
+};
+} // namespace lldb_renderscript
+} // namespace lldb_private
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp
new file mode 100644
index 000000000000..c9cd34cf379d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp
@@ -0,0 +1,5055 @@
+//===-- RenderScriptRuntime.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RenderScriptRuntime.h"
+#include "RenderScriptScriptGroup.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/DataFormatters/DumpValueObjectOptions.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/ADT/StringSwitch.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_renderscript;
+
+#define FMT_COORD "(%" PRIu32 ", %" PRIu32 ", %" PRIu32 ")"
+
+char RenderScriptRuntime::ID = 0;
+
+namespace {
+
+// The empirical_type adds a basic level of validation to arbitrary data
+// allowing us to track if data has been discovered and stored or not. An
+// empirical_type will be marked as valid only if it has been explicitly
+// assigned to.
+template <typename type_t> class empirical_type {
+public:
+ // Ctor. Contents is invalid when constructed.
+ empirical_type() : valid(false) {}
+
+ // Return true and copy contents to out if valid, else return false.
+ bool get(type_t &out) const {
+ if (valid)
+ out = data;
+ return valid;
+ }
+
+ // Return a pointer to the contents or nullptr if it was not valid.
+ const type_t *get() const { return valid ? &data : nullptr; }
+
+ // Assign data explicitly.
+ void set(const type_t in) {
+ data = in;
+ valid = true;
+ }
+
+ // Mark contents as invalid.
+ void invalidate() { valid = false; }
+
+ // Returns true if this type contains valid data.
+ bool isValid() const { return valid; }
+
+ // Assignment operator.
+ empirical_type<type_t> &operator=(const type_t in) {
+ set(in);
+ return *this;
+ }
+
+ // Dereference operator returns contents.
+ // Warning: Will assert if not valid so use only when you know data is valid.
+ const type_t &operator*() const {
+ assert(valid);
+ return data;
+ }
+
+protected:
+ bool valid;
+ type_t data;
+};
+
+// ArgItem is used by the GetArgs() function when reading function arguments
+// from the target.
+struct ArgItem {
+ enum { ePointer, eInt32, eInt64, eLong, eBool } type;
+
+ uint64_t value;
+
+ explicit operator uint64_t() const { return value; }
+};
+
+// Context structure to be passed into GetArgsXXX(), argument reading functions
+// below.
+struct GetArgsCtx {
+ RegisterContext *reg_ctx;
+ Process *process;
+};
+
+bool GetArgsX86(const GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ Status err;
+
+ // get the current stack pointer
+ uint64_t sp = ctx.reg_ctx->GetSP();
+
+ for (size_t i = 0; i < num_args; ++i) {
+ ArgItem &arg = arg_list[i];
+ // advance up the stack by one argument
+ sp += sizeof(uint32_t);
+ // get the argument type size
+ size_t arg_size = sizeof(uint32_t);
+ // read the argument from memory
+ arg.value = 0;
+ Status err;
+ size_t read =
+ ctx.process->ReadMemory(sp, &arg.value, sizeof(uint32_t), err);
+ if (read != arg_size || !err.Success()) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64 " '%s'",
+ __FUNCTION__, uint64_t(i), err.AsCString());
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgsX86_64(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ // number of arguments passed in registers
+ static const uint32_t args_in_reg = 6;
+ // register passing order
+ static const std::array<const char *, args_in_reg> reg_names{
+ {"rdi", "rsi", "rdx", "rcx", "r8", "r9"}};
+ // argument type to size mapping
+ static const std::array<size_t, 5> arg_size{{
+ 8, // ePointer,
+ 4, // eInt32,
+ 8, // eInt64,
+ 8, // eLong,
+ 4, // eBool,
+ }};
+
+ Status err;
+
+ // get the current stack pointer
+ uint64_t sp = ctx.reg_ctx->GetSP();
+ // step over the return address
+ sp += sizeof(uint64_t);
+
+ // check the stack alignment was correct (16 byte aligned)
+ if ((sp & 0xf) != 0x0) {
+ if (log)
+ log->Printf("%s - stack misaligned", __FUNCTION__);
+ return false;
+ }
+
+ // find the start of arguments on the stack
+ uint64_t sp_offset = 0;
+ for (uint32_t i = args_in_reg; i < num_args; ++i) {
+ sp_offset += arg_size[arg_list[i].type];
+ }
+ // round up to multiple of 16
+ sp_offset = (sp_offset + 0xf) & 0xf;
+ sp += sp_offset;
+
+ for (size_t i = 0; i < num_args; ++i) {
+ bool success = false;
+ ArgItem &arg = arg_list[i];
+ // arguments passed in registers
+ if (i < args_in_reg) {
+ const RegisterInfo *reg =
+ ctx.reg_ctx->GetRegisterInfoByName(reg_names[i]);
+ RegisterValue reg_val;
+ if (ctx.reg_ctx->ReadRegister(reg, reg_val))
+ arg.value = reg_val.GetAsUInt64(0, &success);
+ }
+ // arguments passed on the stack
+ else {
+ // get the argument type size
+ const size_t size = arg_size[arg_list[i].type];
+ // read the argument from memory
+ arg.value = 0;
+ // note: due to little endian layout reading 4 or 8 bytes will give the
+ // correct value.
+ size_t read = ctx.process->ReadMemory(sp, &arg.value, size, err);
+ success = (err.Success() && read == size);
+ // advance past this argument
+ sp -= size;
+ }
+ // fail if we couldn't read this argument
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s",
+ __FUNCTION__, uint64_t(i), err.AsCString("n/a"));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgsArm(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ // number of arguments passed in registers
+ static const uint32_t args_in_reg = 4;
+
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ Status err;
+
+ // get the current stack pointer
+ uint64_t sp = ctx.reg_ctx->GetSP();
+
+ for (size_t i = 0; i < num_args; ++i) {
+ bool success = false;
+ ArgItem &arg = arg_list[i];
+ // arguments passed in registers
+ if (i < args_in_reg) {
+ const RegisterInfo *reg = ctx.reg_ctx->GetRegisterInfoAtIndex(i);
+ RegisterValue reg_val;
+ if (ctx.reg_ctx->ReadRegister(reg, reg_val))
+ arg.value = reg_val.GetAsUInt32(0, &success);
+ }
+ // arguments passed on the stack
+ else {
+ // get the argument type size
+ const size_t arg_size = sizeof(uint32_t);
+ // clear all 64bits
+ arg.value = 0;
+ // read this argument from memory
+ size_t bytes_read =
+ ctx.process->ReadMemory(sp, &arg.value, arg_size, err);
+ success = (err.Success() && bytes_read == arg_size);
+ // advance the stack pointer
+ sp += sizeof(uint32_t);
+ }
+ // fail if we couldn't read this argument
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s",
+ __FUNCTION__, uint64_t(i), err.AsCString("n/a"));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgsAarch64(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ // number of arguments passed in registers
+ static const uint32_t args_in_reg = 8;
+
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ for (size_t i = 0; i < num_args; ++i) {
+ bool success = false;
+ ArgItem &arg = arg_list[i];
+ // arguments passed in registers
+ if (i < args_in_reg) {
+ const RegisterInfo *reg = ctx.reg_ctx->GetRegisterInfoAtIndex(i);
+ RegisterValue reg_val;
+ if (ctx.reg_ctx->ReadRegister(reg, reg_val))
+ arg.value = reg_val.GetAsUInt64(0, &success);
+ }
+ // arguments passed on the stack
+ else {
+ if (log)
+ log->Printf("%s - reading arguments spilled to stack not implemented",
+ __FUNCTION__);
+ }
+ // fail if we couldn't read this argument
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64, __FUNCTION__,
+ uint64_t(i));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgsMipsel(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ // number of arguments passed in registers
+ static const uint32_t args_in_reg = 4;
+ // register file offset to first argument
+ static const uint32_t reg_offset = 4;
+
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ Status err;
+
+ // find offset to arguments on the stack (+16 to skip over a0-a3 shadow
+ // space)
+ uint64_t sp = ctx.reg_ctx->GetSP() + 16;
+
+ for (size_t i = 0; i < num_args; ++i) {
+ bool success = false;
+ ArgItem &arg = arg_list[i];
+ // arguments passed in registers
+ if (i < args_in_reg) {
+ const RegisterInfo *reg =
+ ctx.reg_ctx->GetRegisterInfoAtIndex(i + reg_offset);
+ RegisterValue reg_val;
+ if (ctx.reg_ctx->ReadRegister(reg, reg_val))
+ arg.value = reg_val.GetAsUInt64(0, &success);
+ }
+ // arguments passed on the stack
+ else {
+ const size_t arg_size = sizeof(uint32_t);
+ arg.value = 0;
+ size_t bytes_read =
+ ctx.process->ReadMemory(sp, &arg.value, arg_size, err);
+ success = (err.Success() && bytes_read == arg_size);
+ // advance the stack pointer
+ sp += arg_size;
+ }
+ // fail if we couldn't read this argument
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s",
+ __FUNCTION__, uint64_t(i), err.AsCString("n/a"));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgsMips64el(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ // number of arguments passed in registers
+ static const uint32_t args_in_reg = 8;
+ // register file offset to first argument
+ static const uint32_t reg_offset = 4;
+
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ Status err;
+
+ // get the current stack pointer
+ uint64_t sp = ctx.reg_ctx->GetSP();
+
+ for (size_t i = 0; i < num_args; ++i) {
+ bool success = false;
+ ArgItem &arg = arg_list[i];
+ // arguments passed in registers
+ if (i < args_in_reg) {
+ const RegisterInfo *reg =
+ ctx.reg_ctx->GetRegisterInfoAtIndex(i + reg_offset);
+ RegisterValue reg_val;
+ if (ctx.reg_ctx->ReadRegister(reg, reg_val))
+ arg.value = reg_val.GetAsUInt64(0, &success);
+ }
+ // arguments passed on the stack
+ else {
+ // get the argument type size
+ const size_t arg_size = sizeof(uint64_t);
+ // clear all 64bits
+ arg.value = 0;
+ // read this argument from memory
+ size_t bytes_read =
+ ctx.process->ReadMemory(sp, &arg.value, arg_size, err);
+ success = (err.Success() && bytes_read == arg_size);
+ // advance the stack pointer
+ sp += arg_size;
+ }
+ // fail if we couldn't read this argument
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s",
+ __FUNCTION__, uint64_t(i), err.AsCString("n/a"));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgs(ExecutionContext &exe_ctx, ArgItem *arg_list, size_t num_args) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ // verify that we have a target
+ if (!exe_ctx.GetTargetPtr()) {
+ if (log)
+ log->Printf("%s - invalid target", __FUNCTION__);
+ return false;
+ }
+
+ GetArgsCtx ctx = {exe_ctx.GetRegisterContext(), exe_ctx.GetProcessPtr()};
+ assert(ctx.reg_ctx && ctx.process);
+
+ // dispatch based on architecture
+ switch (exe_ctx.GetTargetPtr()->GetArchitecture().GetMachine()) {
+ case llvm::Triple::ArchType::x86:
+ return GetArgsX86(ctx, arg_list, num_args);
+
+ case llvm::Triple::ArchType::x86_64:
+ return GetArgsX86_64(ctx, arg_list, num_args);
+
+ case llvm::Triple::ArchType::arm:
+ return GetArgsArm(ctx, arg_list, num_args);
+
+ case llvm::Triple::ArchType::aarch64:
+ return GetArgsAarch64(ctx, arg_list, num_args);
+
+ case llvm::Triple::ArchType::mipsel:
+ return GetArgsMipsel(ctx, arg_list, num_args);
+
+ case llvm::Triple::ArchType::mips64el:
+ return GetArgsMips64el(ctx, arg_list, num_args);
+
+ default:
+ // unsupported architecture
+ if (log) {
+ log->Printf(
+ "%s - architecture not supported: '%s'", __FUNCTION__,
+ exe_ctx.GetTargetRef().GetArchitecture().GetArchitectureName());
+ }
+ return false;
+ }
+}
+
+bool IsRenderScriptScriptModule(ModuleSP module) {
+ if (!module)
+ return false;
+ return module->FindFirstSymbolWithNameAndType(ConstString(".rs.info"),
+ eSymbolTypeData) != nullptr;
+}
+
+bool ParseCoordinate(llvm::StringRef coord_s, RSCoordinate &coord) {
+ // takes an argument of the form 'num[,num][,num]'. Where 'coord_s' is a
+ // comma separated 1,2 or 3-dimensional coordinate with the whitespace
+ // trimmed. Missing coordinates are defaulted to zero. If parsing of any
+ // elements fails the contents of &coord are undefined and `false` is
+ // returned, `true` otherwise
+
+ RegularExpression regex;
+ RegularExpression::Match regex_match(3);
+
+ bool matched = false;
+ if (regex.Compile(llvm::StringRef("^([0-9]+),([0-9]+),([0-9]+)$")) &&
+ regex.Execute(coord_s, &regex_match))
+ matched = true;
+ else if (regex.Compile(llvm::StringRef("^([0-9]+),([0-9]+)$")) &&
+ regex.Execute(coord_s, &regex_match))
+ matched = true;
+ else if (regex.Compile(llvm::StringRef("^([0-9]+)$")) &&
+ regex.Execute(coord_s, &regex_match))
+ matched = true;
+
+ if (!matched)
+ return false;
+
+ auto get_index = [&](int idx, uint32_t &i) -> bool {
+ std::string group;
+ errno = 0;
+ if (regex_match.GetMatchAtIndex(coord_s.str().c_str(), idx + 1, group))
+ return !llvm::StringRef(group).getAsInteger<uint32_t>(10, i);
+ return true;
+ };
+
+ return get_index(0, coord.x) && get_index(1, coord.y) &&
+ get_index(2, coord.z);
+}
+
+bool SkipPrologue(lldb::ModuleSP &module, Address &addr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+ SymbolContext sc;
+ uint32_t resolved_flags =
+ module->ResolveSymbolContextForAddress(addr, eSymbolContextFunction, sc);
+ if (resolved_flags & eSymbolContextFunction) {
+ if (sc.function) {
+ const uint32_t offset = sc.function->GetPrologueByteSize();
+ ConstString name = sc.GetFunctionName();
+ if (offset)
+ addr.Slide(offset);
+ if (log)
+ log->Printf("%s: Prologue offset for %s is %" PRIu32, __FUNCTION__,
+ name.AsCString(), offset);
+ }
+ return true;
+ } else
+ return false;
+}
+} // anonymous namespace
+
+// The ScriptDetails class collects data associated with a single script
+// instance.
+struct RenderScriptRuntime::ScriptDetails {
+ ~ScriptDetails() = default;
+
+ enum ScriptType { eScript, eScriptC };
+
+ // The derived type of the script.
+ empirical_type<ScriptType> type;
+ // The name of the original source file.
+ empirical_type<std::string> res_name;
+ // Path to script .so file on the device.
+ empirical_type<std::string> shared_lib;
+ // Directory where kernel objects are cached on device.
+ empirical_type<std::string> cache_dir;
+ // Pointer to the context which owns this script.
+ empirical_type<lldb::addr_t> context;
+ // Pointer to the script object itself.
+ empirical_type<lldb::addr_t> script;
+};
+
+// This Element class represents the Element object in RS, defining the type
+// associated with an Allocation.
+struct RenderScriptRuntime::Element {
+ // Taken from rsDefines.h
+ enum DataKind {
+ RS_KIND_USER,
+ RS_KIND_PIXEL_L = 7,
+ RS_KIND_PIXEL_A,
+ RS_KIND_PIXEL_LA,
+ RS_KIND_PIXEL_RGB,
+ RS_KIND_PIXEL_RGBA,
+ RS_KIND_PIXEL_DEPTH,
+ RS_KIND_PIXEL_YUV,
+ RS_KIND_INVALID = 100
+ };
+
+ // Taken from rsDefines.h
+ enum DataType {
+ RS_TYPE_NONE = 0,
+ RS_TYPE_FLOAT_16,
+ RS_TYPE_FLOAT_32,
+ RS_TYPE_FLOAT_64,
+ RS_TYPE_SIGNED_8,
+ RS_TYPE_SIGNED_16,
+ RS_TYPE_SIGNED_32,
+ RS_TYPE_SIGNED_64,
+ RS_TYPE_UNSIGNED_8,
+ RS_TYPE_UNSIGNED_16,
+ RS_TYPE_UNSIGNED_32,
+ RS_TYPE_UNSIGNED_64,
+ RS_TYPE_BOOLEAN,
+
+ RS_TYPE_UNSIGNED_5_6_5,
+ RS_TYPE_UNSIGNED_5_5_5_1,
+ RS_TYPE_UNSIGNED_4_4_4_4,
+
+ RS_TYPE_MATRIX_4X4,
+ RS_TYPE_MATRIX_3X3,
+ RS_TYPE_MATRIX_2X2,
+
+ RS_TYPE_ELEMENT = 1000,
+ RS_TYPE_TYPE,
+ RS_TYPE_ALLOCATION,
+ RS_TYPE_SAMPLER,
+ RS_TYPE_SCRIPT,
+ RS_TYPE_MESH,
+ RS_TYPE_PROGRAM_FRAGMENT,
+ RS_TYPE_PROGRAM_VERTEX,
+ RS_TYPE_PROGRAM_RASTER,
+ RS_TYPE_PROGRAM_STORE,
+ RS_TYPE_FONT,
+
+ RS_TYPE_INVALID = 10000
+ };
+
+ std::vector<Element> children; // Child Element fields for structs
+ empirical_type<lldb::addr_t>
+ element_ptr; // Pointer to the RS Element of the Type
+ empirical_type<DataType>
+ type; // Type of each data pointer stored by the allocation
+ empirical_type<DataKind>
+ type_kind; // Defines pixel type if Allocation is created from an image
+ empirical_type<uint32_t>
+ type_vec_size; // Vector size of each data point, e.g '4' for uchar4
+ empirical_type<uint32_t> field_count; // Number of Subelements
+ empirical_type<uint32_t> datum_size; // Size of a single Element with padding
+ empirical_type<uint32_t> padding; // Number of padding bytes
+ empirical_type<uint32_t>
+ array_size; // Number of items in array, only needed for structs
+ ConstString type_name; // Name of type, only needed for structs
+
+ static ConstString
+ GetFallbackStructName(); // Print this as the type name of a struct Element
+ // If we can't resolve the actual struct name
+
+ bool ShouldRefresh() const {
+ const bool valid_ptr = element_ptr.isValid() && *element_ptr.get() != 0x0;
+ const bool valid_type =
+ type.isValid() && type_vec_size.isValid() && type_kind.isValid();
+ return !valid_ptr || !valid_type || !datum_size.isValid();
+ }
+};
+
+// This AllocationDetails class collects data associated with a single
+// allocation instance.
+struct RenderScriptRuntime::AllocationDetails {
+ struct Dimension {
+ uint32_t dim_1;
+ uint32_t dim_2;
+ uint32_t dim_3;
+ uint32_t cube_map;
+
+ Dimension() {
+ dim_1 = 0;
+ dim_2 = 0;
+ dim_3 = 0;
+ cube_map = 0;
+ }
+ };
+
+ // The FileHeader struct specifies the header we use for writing allocations
+ // to a binary file. Our format begins with the ASCII characters "RSAD",
+ // identifying the file as an allocation dump. Member variables dims and
+ // hdr_size are then written consecutively, immediately followed by an
+ // instance of the ElementHeader struct. Because Elements can contain
+ // subelements, there may be more than one instance of the ElementHeader
+ // struct. With this first instance being the root element, and the other
+ // instances being the root's descendants. To identify which instances are an
+ // ElementHeader's children, each struct is immediately followed by a
+ // sequence of consecutive offsets to the start of its child structs. These
+ // offsets are
+ // 4 bytes in size, and the 0 offset signifies no more children.
+ struct FileHeader {
+ uint8_t ident[4]; // ASCII 'RSAD' identifying the file
+ uint32_t dims[3]; // Dimensions
+ uint16_t hdr_size; // Header size in bytes, including all element headers
+ };
+
+ struct ElementHeader {
+ uint16_t type; // DataType enum
+ uint32_t kind; // DataKind enum
+ uint32_t element_size; // Size of a single element, including padding
+ uint16_t vector_size; // Vector width
+ uint32_t array_size; // Number of elements in array
+ };
+
+ // Monotonically increasing from 1
+ static uint32_t ID;
+
+ // Maps Allocation DataType enum and vector size to printable strings using
+ // mapping from RenderScript numerical types summary documentation
+ static const char *RsDataTypeToString[][4];
+
+ // Maps Allocation DataKind enum to printable strings
+ static const char *RsDataKindToString[];
+
+ // Maps allocation types to format sizes for printing.
+ static const uint32_t RSTypeToFormat[][3];
+
+ // Give each allocation an ID as a way
+ // for commands to reference it.
+ const uint32_t id;
+
+ // Allocation Element type
+ RenderScriptRuntime::Element element;
+ // Dimensions of the Allocation
+ empirical_type<Dimension> dimension;
+ // Pointer to address of the RS Allocation
+ empirical_type<lldb::addr_t> address;
+ // Pointer to the data held by the Allocation
+ empirical_type<lldb::addr_t> data_ptr;
+ // Pointer to the RS Type of the Allocation
+ empirical_type<lldb::addr_t> type_ptr;
+ // Pointer to the RS Context of the Allocation
+ empirical_type<lldb::addr_t> context;
+ // Size of the allocation
+ empirical_type<uint32_t> size;
+ // Stride between rows of the allocation
+ empirical_type<uint32_t> stride;
+
+ // Give each allocation an id, so we can reference it in user commands.
+ AllocationDetails() : id(ID++) {}
+
+ bool ShouldRefresh() const {
+ bool valid_ptrs = data_ptr.isValid() && *data_ptr.get() != 0x0;
+ valid_ptrs = valid_ptrs && type_ptr.isValid() && *type_ptr.get() != 0x0;
+ return !valid_ptrs || !dimension.isValid() || !size.isValid() ||
+ element.ShouldRefresh();
+ }
+};
+
+ConstString RenderScriptRuntime::Element::GetFallbackStructName() {
+ static const ConstString FallbackStructName("struct");
+ return FallbackStructName;
+}
+
+uint32_t RenderScriptRuntime::AllocationDetails::ID = 1;
+
+const char *RenderScriptRuntime::AllocationDetails::RsDataKindToString[] = {
+ "User", "Undefined", "Undefined", "Undefined",
+ "Undefined", "Undefined", "Undefined", // Enum jumps from 0 to 7
+ "L Pixel", "A Pixel", "LA Pixel", "RGB Pixel",
+ "RGBA Pixel", "Pixel Depth", "YUV Pixel"};
+
+const char *RenderScriptRuntime::AllocationDetails::RsDataTypeToString[][4] = {
+ {"None", "None", "None", "None"},
+ {"half", "half2", "half3", "half4"},
+ {"float", "float2", "float3", "float4"},
+ {"double", "double2", "double3", "double4"},
+ {"char", "char2", "char3", "char4"},
+ {"short", "short2", "short3", "short4"},
+ {"int", "int2", "int3", "int4"},
+ {"long", "long2", "long3", "long4"},
+ {"uchar", "uchar2", "uchar3", "uchar4"},
+ {"ushort", "ushort2", "ushort3", "ushort4"},
+ {"uint", "uint2", "uint3", "uint4"},
+ {"ulong", "ulong2", "ulong3", "ulong4"},
+ {"bool", "bool2", "bool3", "bool4"},
+ {"packed_565", "packed_565", "packed_565", "packed_565"},
+ {"packed_5551", "packed_5551", "packed_5551", "packed_5551"},
+ {"packed_4444", "packed_4444", "packed_4444", "packed_4444"},
+ {"rs_matrix4x4", "rs_matrix4x4", "rs_matrix4x4", "rs_matrix4x4"},
+ {"rs_matrix3x3", "rs_matrix3x3", "rs_matrix3x3", "rs_matrix3x3"},
+ {"rs_matrix2x2", "rs_matrix2x2", "rs_matrix2x2", "rs_matrix2x2"},
+
+ // Handlers
+ {"RS Element", "RS Element", "RS Element", "RS Element"},
+ {"RS Type", "RS Type", "RS Type", "RS Type"},
+ {"RS Allocation", "RS Allocation", "RS Allocation", "RS Allocation"},
+ {"RS Sampler", "RS Sampler", "RS Sampler", "RS Sampler"},
+ {"RS Script", "RS Script", "RS Script", "RS Script"},
+
+ // Deprecated
+ {"RS Mesh", "RS Mesh", "RS Mesh", "RS Mesh"},
+ {"RS Program Fragment", "RS Program Fragment", "RS Program Fragment",
+ "RS Program Fragment"},
+ {"RS Program Vertex", "RS Program Vertex", "RS Program Vertex",
+ "RS Program Vertex"},
+ {"RS Program Raster", "RS Program Raster", "RS Program Raster",
+ "RS Program Raster"},
+ {"RS Program Store", "RS Program Store", "RS Program Store",
+ "RS Program Store"},
+ {"RS Font", "RS Font", "RS Font", "RS Font"}};
+
+// Used as an index into the RSTypeToFormat array elements
+enum TypeToFormatIndex { eFormatSingle = 0, eFormatVector, eElementSize };
+
+// { format enum of single element, format enum of element vector, size of
+// element}
+const uint32_t RenderScriptRuntime::AllocationDetails::RSTypeToFormat[][3] = {
+ // RS_TYPE_NONE
+ {eFormatHex, eFormatHex, 1},
+ // RS_TYPE_FLOAT_16
+ {eFormatFloat, eFormatVectorOfFloat16, 2},
+ // RS_TYPE_FLOAT_32
+ {eFormatFloat, eFormatVectorOfFloat32, sizeof(float)},
+ // RS_TYPE_FLOAT_64
+ {eFormatFloat, eFormatVectorOfFloat64, sizeof(double)},
+ // RS_TYPE_SIGNED_8
+ {eFormatDecimal, eFormatVectorOfSInt8, sizeof(int8_t)},
+ // RS_TYPE_SIGNED_16
+ {eFormatDecimal, eFormatVectorOfSInt16, sizeof(int16_t)},
+ // RS_TYPE_SIGNED_32
+ {eFormatDecimal, eFormatVectorOfSInt32, sizeof(int32_t)},
+ // RS_TYPE_SIGNED_64
+ {eFormatDecimal, eFormatVectorOfSInt64, sizeof(int64_t)},
+ // RS_TYPE_UNSIGNED_8
+ {eFormatDecimal, eFormatVectorOfUInt8, sizeof(uint8_t)},
+ // RS_TYPE_UNSIGNED_16
+ {eFormatDecimal, eFormatVectorOfUInt16, sizeof(uint16_t)},
+ // RS_TYPE_UNSIGNED_32
+ {eFormatDecimal, eFormatVectorOfUInt32, sizeof(uint32_t)},
+ // RS_TYPE_UNSIGNED_64
+ {eFormatDecimal, eFormatVectorOfUInt64, sizeof(uint64_t)},
+ // RS_TYPE_BOOL
+ {eFormatBoolean, eFormatBoolean, 1},
+ // RS_TYPE_UNSIGNED_5_6_5
+ {eFormatHex, eFormatHex, sizeof(uint16_t)},
+ // RS_TYPE_UNSIGNED_5_5_5_1
+ {eFormatHex, eFormatHex, sizeof(uint16_t)},
+ // RS_TYPE_UNSIGNED_4_4_4_4
+ {eFormatHex, eFormatHex, sizeof(uint16_t)},
+ // RS_TYPE_MATRIX_4X4
+ {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 16},
+ // RS_TYPE_MATRIX_3X3
+ {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 9},
+ // RS_TYPE_MATRIX_2X2
+ {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 4}};
+
+// Static Functions
+LanguageRuntime *
+RenderScriptRuntime::CreateInstance(Process *process,
+ lldb::LanguageType language) {
+
+ if (language == eLanguageTypeExtRenderScript)
+ return new RenderScriptRuntime(process);
+ else
+ return nullptr;
+}
+
+// Callback with a module to search for matching symbols. We first check that
+// the module contains RS kernels. Then look for a symbol which matches our
+// kernel name. The breakpoint address is finally set using the address of this
+// symbol.
+Searcher::CallbackReturn
+RSBreakpointResolver::SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *, bool) {
+ ModuleSP module = context.module_sp;
+
+ if (!module || !IsRenderScriptScriptModule(module))
+ return Searcher::eCallbackReturnContinue;
+
+ // Attempt to set a breakpoint on the kernel name symbol within the module
+ // library. If it's not found, it's likely debug info is unavailable - try to
+ // set a breakpoint on <name>.expand.
+ const Symbol *kernel_sym =
+ module->FindFirstSymbolWithNameAndType(m_kernel_name, eSymbolTypeCode);
+ if (!kernel_sym) {
+ std::string kernel_name_expanded(m_kernel_name.AsCString());
+ kernel_name_expanded.append(".expand");
+ kernel_sym = module->FindFirstSymbolWithNameAndType(
+ ConstString(kernel_name_expanded.c_str()), eSymbolTypeCode);
+ }
+
+ if (kernel_sym) {
+ Address bp_addr = kernel_sym->GetAddress();
+ if (filter.AddressPasses(bp_addr))
+ m_breakpoint->AddLocation(bp_addr);
+ }
+
+ return Searcher::eCallbackReturnContinue;
+}
+
+Searcher::CallbackReturn
+RSReduceBreakpointResolver::SearchCallback(lldb_private::SearchFilter &filter,
+ lldb_private::SymbolContext &context,
+ Address *, bool) {
+ // We need to have access to the list of reductions currently parsed, as
+ // reduce names don't actually exist as symbols in a module. They are only
+ // identifiable by parsing the .rs.info packet, or finding the expand symbol.
+ // We therefore need access to the list of parsed rs modules to properly
+ // resolve reduction names.
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ ModuleSP module = context.module_sp;
+
+ if (!module || !IsRenderScriptScriptModule(module))
+ return Searcher::eCallbackReturnContinue;
+
+ if (!m_rsmodules)
+ return Searcher::eCallbackReturnContinue;
+
+ for (const auto &module_desc : *m_rsmodules) {
+ if (module_desc->m_module != module)
+ continue;
+
+ for (const auto &reduction : module_desc->m_reductions) {
+ if (reduction.m_reduce_name != m_reduce_name)
+ continue;
+
+ std::array<std::pair<ConstString, int>, 5> funcs{
+ {{reduction.m_init_name, eKernelTypeInit},
+ {reduction.m_accum_name, eKernelTypeAccum},
+ {reduction.m_comb_name, eKernelTypeComb},
+ {reduction.m_outc_name, eKernelTypeOutC},
+ {reduction.m_halter_name, eKernelTypeHalter}}};
+
+ for (const auto &kernel : funcs) {
+ // Skip constituent functions that don't match our spec
+ if (!(m_kernel_types & kernel.second))
+ continue;
+
+ const auto kernel_name = kernel.first;
+ const auto symbol = module->FindFirstSymbolWithNameAndType(
+ kernel_name, eSymbolTypeCode);
+ if (!symbol)
+ continue;
+
+ auto address = symbol->GetAddress();
+ if (filter.AddressPasses(address)) {
+ bool new_bp;
+ if (!SkipPrologue(module, address)) {
+ if (log)
+ log->Printf("%s: Error trying to skip prologue", __FUNCTION__);
+ }
+ m_breakpoint->AddLocation(address, &new_bp);
+ if (log)
+ log->Printf("%s: %s reduction breakpoint on %s in %s", __FUNCTION__,
+ new_bp ? "new" : "existing", kernel_name.GetCString(),
+ address.GetModule()->GetFileSpec().GetCString());
+ }
+ }
+ }
+ }
+ return eCallbackReturnContinue;
+}
+
+Searcher::CallbackReturn RSScriptGroupBreakpointResolver::SearchCallback(
+ SearchFilter &filter, SymbolContext &context, Address *addr,
+ bool containing) {
+
+ if (!m_breakpoint)
+ return eCallbackReturnContinue;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ ModuleSP &module = context.module_sp;
+
+ if (!module || !IsRenderScriptScriptModule(module))
+ return Searcher::eCallbackReturnContinue;
+
+ std::vector<std::string> names;
+ m_breakpoint->GetNames(names);
+ if (names.empty())
+ return eCallbackReturnContinue;
+
+ for (auto &name : names) {
+ const RSScriptGroupDescriptorSP sg = FindScriptGroup(ConstString(name));
+ if (!sg) {
+ if (log)
+ log->Printf("%s: could not find script group for %s", __FUNCTION__,
+ name.c_str());
+ continue;
+ }
+
+ if (log)
+ log->Printf("%s: Found ScriptGroup for %s", __FUNCTION__, name.c_str());
+
+ for (const RSScriptGroupDescriptor::Kernel &k : sg->m_kernels) {
+ if (log) {
+ log->Printf("%s: Adding breakpoint for %s", __FUNCTION__,
+ k.m_name.AsCString());
+ log->Printf("%s: Kernel address 0x%" PRIx64, __FUNCTION__, k.m_addr);
+ }
+
+ const lldb_private::Symbol *sym =
+ module->FindFirstSymbolWithNameAndType(k.m_name, eSymbolTypeCode);
+ if (!sym) {
+ if (log)
+ log->Printf("%s: Unable to find symbol for %s", __FUNCTION__,
+ k.m_name.AsCString());
+ continue;
+ }
+
+ if (log) {
+ log->Printf("%s: Found symbol name is %s", __FUNCTION__,
+ sym->GetName().AsCString());
+ }
+
+ auto address = sym->GetAddress();
+ if (!SkipPrologue(module, address)) {
+ if (log)
+ log->Printf("%s: Error trying to skip prologue", __FUNCTION__);
+ }
+
+ bool new_bp;
+ m_breakpoint->AddLocation(address, &new_bp);
+
+ if (log)
+ log->Printf("%s: Placed %sbreakpoint on %s", __FUNCTION__,
+ new_bp ? "new " : "", k.m_name.AsCString());
+
+ // exit after placing the first breakpoint if we do not intend to stop on
+ // all kernels making up this script group
+ if (!m_stop_on_all)
+ break;
+ }
+ }
+
+ return eCallbackReturnContinue;
+}
+
+void RenderScriptRuntime::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "RenderScript language support", CreateInstance,
+ GetCommandObject);
+}
+
+void RenderScriptRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString RenderScriptRuntime::GetPluginNameStatic() {
+ static ConstString plugin_name("renderscript");
+ return plugin_name;
+}
+
+RenderScriptRuntime::ModuleKind
+RenderScriptRuntime::GetModuleKind(const lldb::ModuleSP &module_sp) {
+ if (module_sp) {
+ if (IsRenderScriptScriptModule(module_sp))
+ return eModuleKindKernelObj;
+
+ // Is this the main RS runtime library
+ const ConstString rs_lib("libRS.so");
+ if (module_sp->GetFileSpec().GetFilename() == rs_lib) {
+ return eModuleKindLibRS;
+ }
+
+ const ConstString rs_driverlib("libRSDriver.so");
+ if (module_sp->GetFileSpec().GetFilename() == rs_driverlib) {
+ return eModuleKindDriver;
+ }
+
+ const ConstString rs_cpureflib("libRSCpuRef.so");
+ if (module_sp->GetFileSpec().GetFilename() == rs_cpureflib) {
+ return eModuleKindImpl;
+ }
+ }
+ return eModuleKindIgnored;
+}
+
+bool RenderScriptRuntime::IsRenderScriptModule(
+ const lldb::ModuleSP &module_sp) {
+ return GetModuleKind(module_sp) != eModuleKindIgnored;
+}
+
+void RenderScriptRuntime::ModulesDidLoad(const ModuleList &module_list) {
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+
+ size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ auto mod = module_list.GetModuleAtIndex(i);
+ if (IsRenderScriptModule(mod)) {
+ LoadModule(mod);
+ }
+ }
+}
+
+// PluginInterface protocol
+lldb_private::ConstString RenderScriptRuntime::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t RenderScriptRuntime::GetPluginVersion() { return 1; }
+
+bool RenderScriptRuntime::GetDynamicTypeAndAddress(
+ ValueObject &in_value, lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &address,
+ Value::ValueType &value_type) {
+ return false;
+}
+
+TypeAndOrName
+RenderScriptRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) {
+ return type_and_or_name;
+}
+
+bool RenderScriptRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
+ return false;
+}
+
+lldb::BreakpointResolverSP
+RenderScriptRuntime::CreateExceptionResolver(Breakpoint *bp, bool catch_bp,
+ bool throw_bp) {
+ BreakpointResolverSP resolver_sp;
+ return resolver_sp;
+}
+
+const RenderScriptRuntime::HookDefn RenderScriptRuntime::s_runtimeHookDefns[] =
+ {
+ // rsdScript
+ {"rsdScriptInit", "_Z13rsdScriptInitPKN7android12renderscript7ContextEP"
+ "NS0_7ScriptCEPKcS7_PKhjj",
+ "_Z13rsdScriptInitPKN7android12renderscript7ContextEPNS0_"
+ "7ScriptCEPKcS7_PKhmj",
+ 0, RenderScriptRuntime::eModuleKindDriver,
+ &lldb_private::RenderScriptRuntime::CaptureScriptInit},
+ {"rsdScriptInvokeForEachMulti",
+ "_Z27rsdScriptInvokeForEachMultiPKN7android12renderscript7ContextEPNS0"
+ "_6ScriptEjPPKNS0_10AllocationEjPS6_PKvjPK12RsScriptCall",
+ "_Z27rsdScriptInvokeForEachMultiPKN7android12renderscript7ContextEPNS0"
+ "_6ScriptEjPPKNS0_10AllocationEmPS6_PKvmPK12RsScriptCall",
+ 0, RenderScriptRuntime::eModuleKindDriver,
+ &lldb_private::RenderScriptRuntime::CaptureScriptInvokeForEachMulti},
+ {"rsdScriptSetGlobalVar", "_Z21rsdScriptSetGlobalVarPKN7android12render"
+ "script7ContextEPKNS0_6ScriptEjPvj",
+ "_Z21rsdScriptSetGlobalVarPKN7android12renderscript7ContextEPKNS0_"
+ "6ScriptEjPvm",
+ 0, RenderScriptRuntime::eModuleKindDriver,
+ &lldb_private::RenderScriptRuntime::CaptureSetGlobalVar},
+
+ // rsdAllocation
+ {"rsdAllocationInit", "_Z17rsdAllocationInitPKN7android12renderscript7C"
+ "ontextEPNS0_10AllocationEb",
+ "_Z17rsdAllocationInitPKN7android12renderscript7ContextEPNS0_"
+ "10AllocationEb",
+ 0, RenderScriptRuntime::eModuleKindDriver,
+ &lldb_private::RenderScriptRuntime::CaptureAllocationInit},
+ {"rsdAllocationRead2D",
+ "_Z19rsdAllocationRead2DPKN7android12renderscript7ContextEPKNS0_"
+ "10AllocationEjjj23RsAllocationCubemapFacejjPvjj",
+ "_Z19rsdAllocationRead2DPKN7android12renderscript7ContextEPKNS0_"
+ "10AllocationEjjj23RsAllocationCubemapFacejjPvmm",
+ 0, RenderScriptRuntime::eModuleKindDriver, nullptr},
+ {"rsdAllocationDestroy", "_Z20rsdAllocationDestroyPKN7android12rendersc"
+ "ript7ContextEPNS0_10AllocationE",
+ "_Z20rsdAllocationDestroyPKN7android12renderscript7ContextEPNS0_"
+ "10AllocationE",
+ 0, RenderScriptRuntime::eModuleKindDriver,
+ &lldb_private::RenderScriptRuntime::CaptureAllocationDestroy},
+
+ // renderscript script groups
+ {"rsdDebugHintScriptGroup2", "_ZN7android12renderscript21debugHintScrip"
+ "tGroup2EPKcjPKPFvPK24RsExpandKernelDriver"
+ "InfojjjEj",
+ "_ZN7android12renderscript21debugHintScriptGroup2EPKcjPKPFvPK24RsExpan"
+ "dKernelDriverInfojjjEj",
+ 0, RenderScriptRuntime::eModuleKindImpl,
+ &lldb_private::RenderScriptRuntime::CaptureDebugHintScriptGroup2}};
+
+const size_t RenderScriptRuntime::s_runtimeHookCount =
+ sizeof(s_runtimeHookDefns) / sizeof(s_runtimeHookDefns[0]);
+
+bool RenderScriptRuntime::HookCallback(void *baton,
+ StoppointCallbackContext *ctx,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ RuntimeHook *hook = (RuntimeHook *)baton;
+ ExecutionContext exe_ctx(ctx->exe_ctx_ref);
+
+ RenderScriptRuntime *lang_rt = llvm::cast<RenderScriptRuntime>(
+ exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ lang_rt->HookCallback(hook, exe_ctx);
+
+ return false;
+}
+
+void RenderScriptRuntime::HookCallback(RuntimeHook *hook,
+ ExecutionContext &exe_ctx) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (log)
+ log->Printf("%s - '%s'", __FUNCTION__, hook->defn->name);
+
+ if (hook->defn->grabber) {
+ (this->*(hook->defn->grabber))(hook, exe_ctx);
+ }
+}
+
+void RenderScriptRuntime::CaptureDebugHintScriptGroup2(
+ RuntimeHook *hook_info, ExecutionContext &context) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ enum {
+ eGroupName = 0,
+ eGroupNameSize,
+ eKernel,
+ eKernelCount,
+ };
+
+ std::array<ArgItem, 4> args{{
+ {ArgItem::ePointer, 0}, // const char *groupName
+ {ArgItem::eInt32, 0}, // const uint32_t groupNameSize
+ {ArgItem::ePointer, 0}, // const ExpandFuncTy *kernel
+ {ArgItem::eInt32, 0}, // const uint32_t kernelCount
+ }};
+
+ if (!GetArgs(context, args.data(), args.size())) {
+ if (log)
+ log->Printf("%s - Error while reading the function parameters",
+ __FUNCTION__);
+ return;
+ } else if (log) {
+ log->Printf("%s - groupName : 0x%" PRIx64, __FUNCTION__,
+ addr_t(args[eGroupName]));
+ log->Printf("%s - groupNameSize: %" PRIu64, __FUNCTION__,
+ uint64_t(args[eGroupNameSize]));
+ log->Printf("%s - kernel : 0x%" PRIx64, __FUNCTION__,
+ addr_t(args[eKernel]));
+ log->Printf("%s - kernelCount : %" PRIu64, __FUNCTION__,
+ uint64_t(args[eKernelCount]));
+ }
+
+ // parse script group name
+ ConstString group_name;
+ {
+ Status err;
+ const uint64_t len = uint64_t(args[eGroupNameSize]);
+ std::unique_ptr<char[]> buffer(new char[uint32_t(len + 1)]);
+ m_process->ReadMemory(addr_t(args[eGroupName]), buffer.get(), len, err);
+ buffer.get()[len] = '\0';
+ if (!err.Success()) {
+ if (log)
+ log->Printf("Error reading scriptgroup name from target");
+ return;
+ } else {
+ if (log)
+ log->Printf("Extracted scriptgroup name %s", buffer.get());
+ }
+ // write back the script group name
+ group_name.SetCString(buffer.get());
+ }
+
+ // create or access existing script group
+ RSScriptGroupDescriptorSP group;
+ {
+ // search for existing script group
+ for (auto sg : m_scriptGroups) {
+ if (sg->m_name == group_name) {
+ group = sg;
+ break;
+ }
+ }
+ if (!group) {
+ group = std::make_shared<RSScriptGroupDescriptor>();
+ group->m_name = group_name;
+ m_scriptGroups.push_back(group);
+ } else {
+ // already have this script group
+ if (log)
+ log->Printf("Attempt to add duplicate script group %s",
+ group_name.AsCString());
+ return;
+ }
+ }
+ assert(group);
+
+ const uint32_t target_ptr_size = m_process->GetAddressByteSize();
+ std::vector<addr_t> kernels;
+ // parse kernel addresses in script group
+ for (uint64_t i = 0; i < uint64_t(args[eKernelCount]); ++i) {
+ RSScriptGroupDescriptor::Kernel kernel;
+ // extract script group kernel addresses from the target
+ const addr_t ptr_addr = addr_t(args[eKernel]) + i * target_ptr_size;
+ uint64_t kernel_addr = 0;
+ Status err;
+ size_t read =
+ m_process->ReadMemory(ptr_addr, &kernel_addr, target_ptr_size, err);
+ if (!err.Success() || read != target_ptr_size) {
+ if (log)
+ log->Printf("Error parsing kernel address %" PRIu64 " in script group",
+ i);
+ return;
+ }
+ if (log)
+ log->Printf("Extracted scriptgroup kernel address - 0x%" PRIx64,
+ kernel_addr);
+ kernel.m_addr = kernel_addr;
+
+ // try to resolve the associated kernel name
+ if (!ResolveKernelName(kernel.m_addr, kernel.m_name)) {
+ if (log)
+ log->Printf("Parsed scriptgroup kernel %" PRIu64 " - 0x%" PRIx64, i,
+ kernel_addr);
+ return;
+ }
+
+ // try to find the non '.expand' function
+ {
+ const llvm::StringRef expand(".expand");
+ const llvm::StringRef name_ref = kernel.m_name.GetStringRef();
+ if (name_ref.endswith(expand)) {
+ const ConstString base_kernel(name_ref.drop_back(expand.size()));
+ // verify this function is a valid kernel
+ if (IsKnownKernel(base_kernel)) {
+ kernel.m_name = base_kernel;
+ if (log)
+ log->Printf("%s - found non expand version '%s'", __FUNCTION__,
+ base_kernel.GetCString());
+ }
+ }
+ }
+ // add to a list of script group kernels we know about
+ group->m_kernels.push_back(kernel);
+ }
+
+ // Resolve any pending scriptgroup breakpoints
+ {
+ Target &target = m_process->GetTarget();
+ const BreakpointList &list = target.GetBreakpointList();
+ const size_t num_breakpoints = list.GetSize();
+ if (log)
+ log->Printf("Resolving %zu breakpoints", num_breakpoints);
+ for (size_t i = 0; i < num_breakpoints; ++i) {
+ const BreakpointSP bp = list.GetBreakpointAtIndex(i);
+ if (bp) {
+ if (bp->MatchesName(group_name.AsCString())) {
+ if (log)
+ log->Printf("Found breakpoint with name %s",
+ group_name.AsCString());
+ bp->ResolveBreakpoint();
+ }
+ }
+ }
+ }
+}
+
+void RenderScriptRuntime::CaptureScriptInvokeForEachMulti(
+ RuntimeHook *hook, ExecutionContext &exe_ctx) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ enum {
+ eRsContext = 0,
+ eRsScript,
+ eRsSlot,
+ eRsAIns,
+ eRsInLen,
+ eRsAOut,
+ eRsUsr,
+ eRsUsrLen,
+ eRsSc,
+ };
+
+ std::array<ArgItem, 9> args{{
+ ArgItem{ArgItem::ePointer, 0}, // const Context *rsc
+ ArgItem{ArgItem::ePointer, 0}, // Script *s
+ ArgItem{ArgItem::eInt32, 0}, // uint32_t slot
+ ArgItem{ArgItem::ePointer, 0}, // const Allocation **aIns
+ ArgItem{ArgItem::eInt32, 0}, // size_t inLen
+ ArgItem{ArgItem::ePointer, 0}, // Allocation *aout
+ ArgItem{ArgItem::ePointer, 0}, // const void *usr
+ ArgItem{ArgItem::eInt32, 0}, // size_t usrLen
+ ArgItem{ArgItem::ePointer, 0}, // const RsScriptCall *sc
+ }};
+
+ bool success = GetArgs(exe_ctx, &args[0], args.size());
+ if (!success) {
+ if (log)
+ log->Printf("%s - Error while reading the function parameters",
+ __FUNCTION__);
+ return;
+ }
+
+ const uint32_t target_ptr_size = m_process->GetAddressByteSize();
+ Status err;
+ std::vector<uint64_t> allocs;
+
+ // traverse allocation list
+ for (uint64_t i = 0; i < uint64_t(args[eRsInLen]); ++i) {
+ // calculate offest to allocation pointer
+ const addr_t addr = addr_t(args[eRsAIns]) + i * target_ptr_size;
+
+ // Note: due to little endian layout, reading 32bits or 64bits into res
+ // will give the correct results.
+ uint64_t result = 0;
+ size_t read = m_process->ReadMemory(addr, &result, target_ptr_size, err);
+ if (read != target_ptr_size || !err.Success()) {
+ if (log)
+ log->Printf(
+ "%s - Error while reading allocation list argument %" PRIu64,
+ __FUNCTION__, i);
+ } else {
+ allocs.push_back(result);
+ }
+ }
+
+ // if there is an output allocation track it
+ if (uint64_t alloc_out = uint64_t(args[eRsAOut])) {
+ allocs.push_back(alloc_out);
+ }
+
+ // for all allocations we have found
+ for (const uint64_t alloc_addr : allocs) {
+ AllocationDetails *alloc = LookUpAllocation(alloc_addr);
+ if (!alloc)
+ alloc = CreateAllocation(alloc_addr);
+
+ if (alloc) {
+ // save the allocation address
+ if (alloc->address.isValid()) {
+ // check the allocation address we already have matches
+ assert(*alloc->address.get() == alloc_addr);
+ } else {
+ alloc->address = alloc_addr;
+ }
+
+ // save the context
+ if (log) {
+ if (alloc->context.isValid() &&
+ *alloc->context.get() != addr_t(args[eRsContext]))
+ log->Printf("%s - Allocation used by multiple contexts",
+ __FUNCTION__);
+ }
+ alloc->context = addr_t(args[eRsContext]);
+ }
+ }
+
+ // make sure we track this script object
+ if (lldb_private::RenderScriptRuntime::ScriptDetails *script =
+ LookUpScript(addr_t(args[eRsScript]), true)) {
+ if (log) {
+ if (script->context.isValid() &&
+ *script->context.get() != addr_t(args[eRsContext]))
+ log->Printf("%s - Script used by multiple contexts", __FUNCTION__);
+ }
+ script->context = addr_t(args[eRsContext]);
+ }
+}
+
+void RenderScriptRuntime::CaptureSetGlobalVar(RuntimeHook *hook,
+ ExecutionContext &context) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ enum {
+ eRsContext,
+ eRsScript,
+ eRsId,
+ eRsData,
+ eRsLength,
+ };
+
+ std::array<ArgItem, 5> args{{
+ ArgItem{ArgItem::ePointer, 0}, // eRsContext
+ ArgItem{ArgItem::ePointer, 0}, // eRsScript
+ ArgItem{ArgItem::eInt32, 0}, // eRsId
+ ArgItem{ArgItem::ePointer, 0}, // eRsData
+ ArgItem{ArgItem::eInt32, 0}, // eRsLength
+ }};
+
+ bool success = GetArgs(context, &args[0], args.size());
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading the function parameters.", __FUNCTION__);
+ return;
+ }
+
+ if (log) {
+ log->Printf("%s - 0x%" PRIx64 ",0x%" PRIx64 " slot %" PRIu64 " = 0x%" PRIx64
+ ":%" PRIu64 "bytes.",
+ __FUNCTION__, uint64_t(args[eRsContext]),
+ uint64_t(args[eRsScript]), uint64_t(args[eRsId]),
+ uint64_t(args[eRsData]), uint64_t(args[eRsLength]));
+
+ addr_t script_addr = addr_t(args[eRsScript]);
+ if (m_scriptMappings.find(script_addr) != m_scriptMappings.end()) {
+ auto rsm = m_scriptMappings[script_addr];
+ if (uint64_t(args[eRsId]) < rsm->m_globals.size()) {
+ auto rsg = rsm->m_globals[uint64_t(args[eRsId])];
+ log->Printf("%s - Setting of '%s' within '%s' inferred", __FUNCTION__,
+ rsg.m_name.AsCString(),
+ rsm->m_module->GetFileSpec().GetFilename().AsCString());
+ }
+ }
+ }
+}
+
+void RenderScriptRuntime::CaptureAllocationInit(RuntimeHook *hook,
+ ExecutionContext &exe_ctx) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ enum { eRsContext, eRsAlloc, eRsForceZero };
+
+ std::array<ArgItem, 3> args{{
+ ArgItem{ArgItem::ePointer, 0}, // eRsContext
+ ArgItem{ArgItem::ePointer, 0}, // eRsAlloc
+ ArgItem{ArgItem::eBool, 0}, // eRsForceZero
+ }};
+
+ bool success = GetArgs(exe_ctx, &args[0], args.size());
+ if (!success) {
+ if (log)
+ log->Printf("%s - error while reading the function parameters",
+ __FUNCTION__);
+ return;
+ }
+
+ if (log)
+ log->Printf("%s - 0x%" PRIx64 ",0x%" PRIx64 ",0x%" PRIx64 " .",
+ __FUNCTION__, uint64_t(args[eRsContext]),
+ uint64_t(args[eRsAlloc]), uint64_t(args[eRsForceZero]));
+
+ AllocationDetails *alloc = CreateAllocation(uint64_t(args[eRsAlloc]));
+ if (alloc)
+ alloc->context = uint64_t(args[eRsContext]);
+}
+
+void RenderScriptRuntime::CaptureAllocationDestroy(RuntimeHook *hook,
+ ExecutionContext &exe_ctx) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ enum {
+ eRsContext,
+ eRsAlloc,
+ };
+
+ std::array<ArgItem, 2> args{{
+ ArgItem{ArgItem::ePointer, 0}, // eRsContext
+ ArgItem{ArgItem::ePointer, 0}, // eRsAlloc
+ }};
+
+ bool success = GetArgs(exe_ctx, &args[0], args.size());
+ if (!success) {
+ if (log)
+ log->Printf("%s - error while reading the function parameters.",
+ __FUNCTION__);
+ return;
+ }
+
+ if (log)
+ log->Printf("%s - 0x%" PRIx64 ", 0x%" PRIx64 ".", __FUNCTION__,
+ uint64_t(args[eRsContext]), uint64_t(args[eRsAlloc]));
+
+ for (auto iter = m_allocations.begin(); iter != m_allocations.end(); ++iter) {
+ auto &allocation_up = *iter; // get the unique pointer
+ if (allocation_up->address.isValid() &&
+ *allocation_up->address.get() == addr_t(args[eRsAlloc])) {
+ m_allocations.erase(iter);
+ if (log)
+ log->Printf("%s - deleted allocation entry.", __FUNCTION__);
+ return;
+ }
+ }
+
+ if (log)
+ log->Printf("%s - couldn't find destroyed allocation.", __FUNCTION__);
+}
+
+void RenderScriptRuntime::CaptureScriptInit(RuntimeHook *hook,
+ ExecutionContext &exe_ctx) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ Status err;
+ Process *process = exe_ctx.GetProcessPtr();
+
+ enum { eRsContext, eRsScript, eRsResNamePtr, eRsCachedDirPtr };
+
+ std::array<ArgItem, 4> args{
+ {ArgItem{ArgItem::ePointer, 0}, ArgItem{ArgItem::ePointer, 0},
+ ArgItem{ArgItem::ePointer, 0}, ArgItem{ArgItem::ePointer, 0}}};
+ bool success = GetArgs(exe_ctx, &args[0], args.size());
+ if (!success) {
+ if (log)
+ log->Printf("%s - error while reading the function parameters.",
+ __FUNCTION__);
+ return;
+ }
+
+ std::string res_name;
+ process->ReadCStringFromMemory(addr_t(args[eRsResNamePtr]), res_name, err);
+ if (err.Fail()) {
+ if (log)
+ log->Printf("%s - error reading res_name: %s.", __FUNCTION__,
+ err.AsCString());
+ }
+
+ std::string cache_dir;
+ process->ReadCStringFromMemory(addr_t(args[eRsCachedDirPtr]), cache_dir, err);
+ if (err.Fail()) {
+ if (log)
+ log->Printf("%s - error reading cache_dir: %s.", __FUNCTION__,
+ err.AsCString());
+ }
+
+ if (log)
+ log->Printf("%s - 0x%" PRIx64 ",0x%" PRIx64 " => '%s' at '%s' .",
+ __FUNCTION__, uint64_t(args[eRsContext]),
+ uint64_t(args[eRsScript]), res_name.c_str(), cache_dir.c_str());
+
+ if (res_name.size() > 0) {
+ StreamString strm;
+ strm.Printf("librs.%s.so", res_name.c_str());
+
+ ScriptDetails *script = LookUpScript(addr_t(args[eRsScript]), true);
+ if (script) {
+ script->type = ScriptDetails::eScriptC;
+ script->cache_dir = cache_dir;
+ script->res_name = res_name;
+ script->shared_lib = strm.GetString();
+ script->context = addr_t(args[eRsContext]);
+ }
+
+ if (log)
+ log->Printf("%s - '%s' tagged with context 0x%" PRIx64
+ " and script 0x%" PRIx64 ".",
+ __FUNCTION__, strm.GetData(), uint64_t(args[eRsContext]),
+ uint64_t(args[eRsScript]));
+ } else if (log) {
+ log->Printf("%s - resource name invalid, Script not tagged.", __FUNCTION__);
+ }
+}
+
+void RenderScriptRuntime::LoadRuntimeHooks(lldb::ModuleSP module,
+ ModuleKind kind) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!module) {
+ return;
+ }
+
+ Target &target = GetProcess()->GetTarget();
+ const llvm::Triple::ArchType machine = target.GetArchitecture().GetMachine();
+
+ if (machine != llvm::Triple::ArchType::x86 &&
+ machine != llvm::Triple::ArchType::arm &&
+ machine != llvm::Triple::ArchType::aarch64 &&
+ machine != llvm::Triple::ArchType::mipsel &&
+ machine != llvm::Triple::ArchType::mips64el &&
+ machine != llvm::Triple::ArchType::x86_64) {
+ if (log)
+ log->Printf("%s - unable to hook runtime functions.", __FUNCTION__);
+ return;
+ }
+
+ const uint32_t target_ptr_size =
+ target.GetArchitecture().GetAddressByteSize();
+
+ std::array<bool, s_runtimeHookCount> hook_placed;
+ hook_placed.fill(false);
+
+ for (size_t idx = 0; idx < s_runtimeHookCount; idx++) {
+ const HookDefn *hook_defn = &s_runtimeHookDefns[idx];
+ if (hook_defn->kind != kind) {
+ continue;
+ }
+
+ const char *symbol_name = (target_ptr_size == 4)
+ ? hook_defn->symbol_name_m32
+ : hook_defn->symbol_name_m64;
+
+ const Symbol *sym = module->FindFirstSymbolWithNameAndType(
+ ConstString(symbol_name), eSymbolTypeCode);
+ if (!sym) {
+ if (log) {
+ log->Printf("%s - symbol '%s' related to the function %s not found",
+ __FUNCTION__, symbol_name, hook_defn->name);
+ }
+ continue;
+ }
+
+ addr_t addr = sym->GetLoadAddress(&target);
+ if (addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("%s - unable to resolve the address of hook function '%s' "
+ "with symbol '%s'.",
+ __FUNCTION__, hook_defn->name, symbol_name);
+ continue;
+ } else {
+ if (log)
+ log->Printf("%s - function %s, address resolved at 0x%" PRIx64,
+ __FUNCTION__, hook_defn->name, addr);
+ }
+
+ RuntimeHookSP hook(new RuntimeHook());
+ hook->address = addr;
+ hook->defn = hook_defn;
+ hook->bp_sp = target.CreateBreakpoint(addr, true, false);
+ hook->bp_sp->SetCallback(HookCallback, hook.get(), true);
+ m_runtimeHooks[addr] = hook;
+ if (log) {
+ log->Printf("%s - successfully hooked '%s' in '%s' version %" PRIu64
+ " at 0x%" PRIx64 ".",
+ __FUNCTION__, hook_defn->name,
+ module->GetFileSpec().GetFilename().AsCString(),
+ (uint64_t)hook_defn->version, (uint64_t)addr);
+ }
+ hook_placed[idx] = true;
+ }
+
+ // log any unhooked function
+ if (log) {
+ for (size_t i = 0; i < hook_placed.size(); ++i) {
+ if (hook_placed[i])
+ continue;
+ const HookDefn &hook_defn = s_runtimeHookDefns[i];
+ if (hook_defn.kind != kind)
+ continue;
+ log->Printf("%s - function %s was not hooked", __FUNCTION__,
+ hook_defn.name);
+ }
+ }
+}
+
+void RenderScriptRuntime::FixupScriptDetails(RSModuleDescriptorSP rsmodule_sp) {
+ if (!rsmodule_sp)
+ return;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ const ModuleSP module = rsmodule_sp->m_module;
+ const FileSpec &file = module->GetPlatformFileSpec();
+
+ // Iterate over all of the scripts that we currently know of. Note: We cant
+ // push or pop to m_scripts here or it may invalidate rs_script.
+ for (const auto &rs_script : m_scripts) {
+ // Extract the expected .so file path for this script.
+ std::string shared_lib;
+ if (!rs_script->shared_lib.get(shared_lib))
+ continue;
+
+ // Only proceed if the module that has loaded corresponds to this script.
+ if (file.GetFilename() != ConstString(shared_lib.c_str()))
+ continue;
+
+ // Obtain the script address which we use as a key.
+ lldb::addr_t script;
+ if (!rs_script->script.get(script))
+ continue;
+
+ // If we have a script mapping for the current script.
+ if (m_scriptMappings.find(script) != m_scriptMappings.end()) {
+ // if the module we have stored is different to the one we just received.
+ if (m_scriptMappings[script] != rsmodule_sp) {
+ if (log)
+ log->Printf(
+ "%s - script %" PRIx64 " wants reassigned to new rsmodule '%s'.",
+ __FUNCTION__, (uint64_t)script,
+ rsmodule_sp->m_module->GetFileSpec().GetFilename().AsCString());
+ }
+ }
+ // We don't have a script mapping for the current script.
+ else {
+ // Obtain the script resource name.
+ std::string res_name;
+ if (rs_script->res_name.get(res_name))
+ // Set the modules resource name.
+ rsmodule_sp->m_resname = res_name;
+ // Add Script/Module pair to map.
+ m_scriptMappings[script] = rsmodule_sp;
+ if (log)
+ log->Printf(
+ "%s - script %" PRIx64 " associated with rsmodule '%s'.",
+ __FUNCTION__, (uint64_t)script,
+ rsmodule_sp->m_module->GetFileSpec().GetFilename().AsCString());
+ }
+ }
+}
+
+// Uses the Target API to evaluate the expression passed as a parameter to the
+// function The result of that expression is returned an unsigned 64 bit int,
+// via the result* parameter. Function returns true on success, and false on
+// failure
+bool RenderScriptRuntime::EvalRSExpression(const char *expr,
+ StackFrame *frame_ptr,
+ uint64_t *result) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+ if (log)
+ log->Printf("%s(%s)", __FUNCTION__, expr);
+
+ ValueObjectSP expr_result;
+ EvaluateExpressionOptions options;
+ options.SetLanguage(lldb::eLanguageTypeC_plus_plus);
+ // Perform the actual expression evaluation
+ auto &target = GetProcess()->GetTarget();
+ target.EvaluateExpression(expr, frame_ptr, expr_result, options);
+
+ if (!expr_result) {
+ if (log)
+ log->Printf("%s: couldn't evaluate expression.", __FUNCTION__);
+ return false;
+ }
+
+ // The result of the expression is invalid
+ if (!expr_result->GetError().Success()) {
+ Status err = expr_result->GetError();
+ // Expression returned is void, so this is actually a success
+ if (err.GetError() == UserExpression::kNoResult) {
+ if (log)
+ log->Printf("%s - expression returned void.", __FUNCTION__);
+
+ result = nullptr;
+ return true;
+ }
+
+ if (log)
+ log->Printf("%s - error evaluating expression result: %s", __FUNCTION__,
+ err.AsCString());
+ return false;
+ }
+
+ bool success = false;
+ // We only read the result as an uint32_t.
+ *result = expr_result->GetValueAsUnsigned(0, &success);
+
+ if (!success) {
+ if (log)
+ log->Printf("%s - couldn't convert expression result to uint32_t",
+ __FUNCTION__);
+ return false;
+ }
+
+ return true;
+}
+
+namespace {
+// Used to index expression format strings
+enum ExpressionStrings {
+ eExprGetOffsetPtr = 0,
+ eExprAllocGetType,
+ eExprTypeDimX,
+ eExprTypeDimY,
+ eExprTypeDimZ,
+ eExprTypeElemPtr,
+ eExprElementType,
+ eExprElementKind,
+ eExprElementVec,
+ eExprElementFieldCount,
+ eExprSubelementsId,
+ eExprSubelementsName,
+ eExprSubelementsArrSize,
+
+ _eExprLast // keep at the end, implicit size of the array runtime_expressions
+};
+
+// max length of an expanded expression
+const int jit_max_expr_size = 512;
+
+// Retrieve the string to JIT for the given expression
+#define JIT_TEMPLATE_CONTEXT "void* ctxt = (void*)rsDebugGetContextWrapper(0x%" PRIx64 "); "
+const char *JITTemplate(ExpressionStrings e) {
+ // Format strings containing the expressions we may need to evaluate.
+ static std::array<const char *, _eExprLast> runtime_expressions = {
+ {// Mangled GetOffsetPointer(Allocation*, xoff, yoff, zoff, lod, cubemap)
+ "(int*)_"
+ "Z12GetOffsetPtrPKN7android12renderscript10AllocationEjjjj23RsAllocation"
+ "CubemapFace"
+ "(0x%" PRIx64 ", %" PRIu32 ", %" PRIu32 ", %" PRIu32 ", 0, 0)", // eExprGetOffsetPtr
+
+ // Type* rsaAllocationGetType(Context*, Allocation*)
+ JIT_TEMPLATE_CONTEXT "(void*)rsaAllocationGetType(ctxt, 0x%" PRIx64 ")", // eExprAllocGetType
+
+ // rsaTypeGetNativeData(Context*, Type*, void* typeData, size) Pack the
+ // data in the following way mHal.state.dimX; mHal.state.dimY;
+ // mHal.state.dimZ; mHal.state.lodCount; mHal.state.faces; mElement;
+ // into typeData Need to specify 32 or 64 bit for uint_t since this
+ // differs between devices
+ JIT_TEMPLATE_CONTEXT
+ "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 6); data[0]", // eExprTypeDimX
+ JIT_TEMPLATE_CONTEXT
+ "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 6); data[1]", // eExprTypeDimY
+ JIT_TEMPLATE_CONTEXT
+ "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 6); data[2]", // eExprTypeDimZ
+ JIT_TEMPLATE_CONTEXT
+ "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 6); data[5]", // eExprTypeElemPtr
+
+ // rsaElementGetNativeData(Context*, Element*, uint32_t* elemData,size)
+ // Pack mType; mKind; mNormalized; mVectorSize; NumSubElements into
+ // elemData
+ JIT_TEMPLATE_CONTEXT
+ "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 5); data[0]", // eExprElementType
+ JIT_TEMPLATE_CONTEXT
+ "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 5); data[1]", // eExprElementKind
+ JIT_TEMPLATE_CONTEXT
+ "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 5); data[3]", // eExprElementVec
+ JIT_TEMPLATE_CONTEXT
+ "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 5); data[4]", // eExprElementFieldCount
+
+ // rsaElementGetSubElements(RsContext con, RsElement elem, uintptr_t
+ // *ids, const char **names, size_t *arraySizes, uint32_t dataSize)
+ // Needed for Allocations of structs to gather details about
+ // fields/Subelements Element* of field
+ JIT_TEMPLATE_CONTEXT "void* ids[%" PRIu32 "]; const char* names[%" PRIu32
+ "]; size_t arr_size[%" PRIu32 "];"
+ "(void*)rsaElementGetSubElements(ctxt, 0x%" PRIx64
+ ", ids, names, arr_size, %" PRIu32 "); ids[%" PRIu32 "]", // eExprSubelementsId
+
+ // Name of field
+ JIT_TEMPLATE_CONTEXT "void* ids[%" PRIu32 "]; const char* names[%" PRIu32
+ "]; size_t arr_size[%" PRIu32 "];"
+ "(void*)rsaElementGetSubElements(ctxt, 0x%" PRIx64
+ ", ids, names, arr_size, %" PRIu32 "); names[%" PRIu32 "]", // eExprSubelementsName
+
+ // Array size of field
+ JIT_TEMPLATE_CONTEXT "void* ids[%" PRIu32 "]; const char* names[%" PRIu32
+ "]; size_t arr_size[%" PRIu32 "];"
+ "(void*)rsaElementGetSubElements(ctxt, 0x%" PRIx64
+ ", ids, names, arr_size, %" PRIu32 "); arr_size[%" PRIu32 "]"}}; // eExprSubelementsArrSize
+
+ return runtime_expressions[e];
+}
+} // end of the anonymous namespace
+
+// JITs the RS runtime for the internal data pointer of an allocation. Is
+// passed x,y,z coordinates for the pointer to a specific element. Then sets
+// the data_ptr member in Allocation with the result. Returns true on success,
+// false otherwise
+bool RenderScriptRuntime::JITDataPointer(AllocationDetails *alloc,
+ StackFrame *frame_ptr, uint32_t x,
+ uint32_t y, uint32_t z) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!alloc->address.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ const char *fmt_str = JITTemplate(eExprGetOffsetPtr);
+ char expr_buf[jit_max_expr_size];
+
+ int written = snprintf(expr_buf, jit_max_expr_size, fmt_str,
+ *alloc->address.get(), x, y, z);
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ uint64_t result = 0;
+ if (!EvalRSExpression(expr_buf, frame_ptr, &result))
+ return false;
+
+ addr_t data_ptr = static_cast<lldb::addr_t>(result);
+ alloc->data_ptr = data_ptr;
+
+ return true;
+}
+
+// JITs the RS runtime for the internal pointer to the RS Type of an allocation
+// Then sets the type_ptr member in Allocation with the result. Returns true on
+// success, false otherwise
+bool RenderScriptRuntime::JITTypePointer(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!alloc->address.isValid() || !alloc->context.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ const char *fmt_str = JITTemplate(eExprAllocGetType);
+ char expr_buf[jit_max_expr_size];
+
+ int written = snprintf(expr_buf, jit_max_expr_size, fmt_str,
+ *alloc->context.get(), *alloc->address.get());
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ uint64_t result = 0;
+ if (!EvalRSExpression(expr_buf, frame_ptr, &result))
+ return false;
+
+ addr_t type_ptr = static_cast<lldb::addr_t>(result);
+ alloc->type_ptr = type_ptr;
+
+ return true;
+}
+
+// JITs the RS runtime for information about the dimensions and type of an
+// allocation Then sets dimension and element_ptr members in Allocation with
+// the result. Returns true on success, false otherwise
+bool RenderScriptRuntime::JITTypePacked(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!alloc->type_ptr.isValid() || !alloc->context.isValid()) {
+ if (log)
+ log->Printf("%s - Failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ // Expression is different depending on if device is 32 or 64 bit
+ uint32_t target_ptr_size =
+ GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize();
+ const uint32_t bits = target_ptr_size == 4 ? 32 : 64;
+
+ // We want 4 elements from packed data
+ const uint32_t num_exprs = 4;
+ static_assert(num_exprs == (eExprTypeElemPtr - eExprTypeDimX + 1),
+ "Invalid number of expressions");
+
+ char expr_bufs[num_exprs][jit_max_expr_size];
+ uint64_t results[num_exprs];
+
+ for (uint32_t i = 0; i < num_exprs; ++i) {
+ const char *fmt_str = JITTemplate(ExpressionStrings(eExprTypeDimX + i));
+ int written = snprintf(expr_bufs[i], jit_max_expr_size, fmt_str,
+ *alloc->context.get(), bits, *alloc->type_ptr.get());
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ // Perform expression evaluation
+ if (!EvalRSExpression(expr_bufs[i], frame_ptr, &results[i]))
+ return false;
+ }
+
+ // Assign results to allocation members
+ AllocationDetails::Dimension dims;
+ dims.dim_1 = static_cast<uint32_t>(results[0]);
+ dims.dim_2 = static_cast<uint32_t>(results[1]);
+ dims.dim_3 = static_cast<uint32_t>(results[2]);
+ alloc->dimension = dims;
+
+ addr_t element_ptr = static_cast<lldb::addr_t>(results[3]);
+ alloc->element.element_ptr = element_ptr;
+
+ if (log)
+ log->Printf("%s - dims (%" PRIu32 ", %" PRIu32 ", %" PRIu32
+ ") Element*: 0x%" PRIx64 ".",
+ __FUNCTION__, dims.dim_1, dims.dim_2, dims.dim_3, element_ptr);
+
+ return true;
+}
+
+// JITs the RS runtime for information about the Element of an allocation Then
+// sets type, type_vec_size, field_count and type_kind members in Element with
+// the result. Returns true on success, false otherwise
+bool RenderScriptRuntime::JITElementPacked(Element &elem,
+ const lldb::addr_t context,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!elem.element_ptr.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ // We want 4 elements from packed data
+ const uint32_t num_exprs = 4;
+ static_assert(num_exprs == (eExprElementFieldCount - eExprElementType + 1),
+ "Invalid number of expressions");
+
+ char expr_bufs[num_exprs][jit_max_expr_size];
+ uint64_t results[num_exprs];
+
+ for (uint32_t i = 0; i < num_exprs; i++) {
+ const char *fmt_str = JITTemplate(ExpressionStrings(eExprElementType + i));
+ int written = snprintf(expr_bufs[i], jit_max_expr_size, fmt_str, context,
+ *elem.element_ptr.get());
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ // Perform expression evaluation
+ if (!EvalRSExpression(expr_bufs[i], frame_ptr, &results[i]))
+ return false;
+ }
+
+ // Assign results to allocation members
+ elem.type = static_cast<RenderScriptRuntime::Element::DataType>(results[0]);
+ elem.type_kind =
+ static_cast<RenderScriptRuntime::Element::DataKind>(results[1]);
+ elem.type_vec_size = static_cast<uint32_t>(results[2]);
+ elem.field_count = static_cast<uint32_t>(results[3]);
+
+ if (log)
+ log->Printf("%s - data type %" PRIu32 ", pixel type %" PRIu32
+ ", vector size %" PRIu32 ", field count %" PRIu32,
+ __FUNCTION__, *elem.type.get(), *elem.type_kind.get(),
+ *elem.type_vec_size.get(), *elem.field_count.get());
+
+ // If this Element has subelements then JIT rsaElementGetSubElements() for
+ // details about its fields
+ return !(*elem.field_count.get() > 0 &&
+ !JITSubelements(elem, context, frame_ptr));
+}
+
+// JITs the RS runtime for information about the subelements/fields of a struct
+// allocation This is necessary for infering the struct type so we can pretty
+// print the allocation's contents. Returns true on success, false otherwise
+bool RenderScriptRuntime::JITSubelements(Element &elem,
+ const lldb::addr_t context,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!elem.element_ptr.isValid() || !elem.field_count.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ const short num_exprs = 3;
+ static_assert(num_exprs == (eExprSubelementsArrSize - eExprSubelementsId + 1),
+ "Invalid number of expressions");
+
+ char expr_buffer[jit_max_expr_size];
+ uint64_t results;
+
+ // Iterate over struct fields.
+ const uint32_t field_count = *elem.field_count.get();
+ for (uint32_t field_index = 0; field_index < field_count; ++field_index) {
+ Element child;
+ for (uint32_t expr_index = 0; expr_index < num_exprs; ++expr_index) {
+ const char *fmt_str =
+ JITTemplate(ExpressionStrings(eExprSubelementsId + expr_index));
+ int written = snprintf(expr_buffer, jit_max_expr_size, fmt_str,
+ context, field_count, field_count, field_count,
+ *elem.element_ptr.get(), field_count, field_index);
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ // Perform expression evaluation
+ if (!EvalRSExpression(expr_buffer, frame_ptr, &results))
+ return false;
+
+ if (log)
+ log->Printf("%s - expr result 0x%" PRIx64 ".", __FUNCTION__, results);
+
+ switch (expr_index) {
+ case 0: // Element* of child
+ child.element_ptr = static_cast<addr_t>(results);
+ break;
+ case 1: // Name of child
+ {
+ lldb::addr_t address = static_cast<addr_t>(results);
+ Status err;
+ std::string name;
+ GetProcess()->ReadCStringFromMemory(address, name, err);
+ if (!err.Fail())
+ child.type_name = ConstString(name);
+ else {
+ if (log)
+ log->Printf("%s - warning: Couldn't read field name.",
+ __FUNCTION__);
+ }
+ break;
+ }
+ case 2: // Array size of child
+ child.array_size = static_cast<uint32_t>(results);
+ break;
+ }
+ }
+
+ // We need to recursively JIT each Element field of the struct since
+ // structs can be nested inside structs.
+ if (!JITElementPacked(child, context, frame_ptr))
+ return false;
+ elem.children.push_back(child);
+ }
+
+ // Try to infer the name of the struct type so we can pretty print the
+ // allocation contents.
+ FindStructTypeName(elem, frame_ptr);
+
+ return true;
+}
+
+// JITs the RS runtime for the address of the last element in the allocation.
+// The `elem_size` parameter represents the size of a single element, including
+// padding. Which is needed as an offset from the last element pointer. Using
+// this offset minus the starting address we can calculate the size of the
+// allocation. Returns true on success, false otherwise
+bool RenderScriptRuntime::JITAllocationSize(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!alloc->address.isValid() || !alloc->dimension.isValid() ||
+ !alloc->data_ptr.isValid() || !alloc->element.datum_size.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ // Find dimensions
+ uint32_t dim_x = alloc->dimension.get()->dim_1;
+ uint32_t dim_y = alloc->dimension.get()->dim_2;
+ uint32_t dim_z = alloc->dimension.get()->dim_3;
+
+ // Our plan of jitting the last element address doesn't seem to work for
+ // struct Allocations` Instead try to infer the size ourselves without any
+ // inter element padding.
+ if (alloc->element.children.size() > 0) {
+ if (dim_x == 0)
+ dim_x = 1;
+ if (dim_y == 0)
+ dim_y = 1;
+ if (dim_z == 0)
+ dim_z = 1;
+
+ alloc->size = dim_x * dim_y * dim_z * *alloc->element.datum_size.get();
+
+ if (log)
+ log->Printf("%s - inferred size of struct allocation %" PRIu32 ".",
+ __FUNCTION__, *alloc->size.get());
+ return true;
+ }
+
+ const char *fmt_str = JITTemplate(eExprGetOffsetPtr);
+ char expr_buf[jit_max_expr_size];
+
+ // Calculate last element
+ dim_x = dim_x == 0 ? 0 : dim_x - 1;
+ dim_y = dim_y == 0 ? 0 : dim_y - 1;
+ dim_z = dim_z == 0 ? 0 : dim_z - 1;
+
+ int written = snprintf(expr_buf, jit_max_expr_size, fmt_str,
+ *alloc->address.get(), dim_x, dim_y, dim_z);
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ uint64_t result = 0;
+ if (!EvalRSExpression(expr_buf, frame_ptr, &result))
+ return false;
+
+ addr_t mem_ptr = static_cast<lldb::addr_t>(result);
+ // Find pointer to last element and add on size of an element
+ alloc->size = static_cast<uint32_t>(mem_ptr - *alloc->data_ptr.get()) +
+ *alloc->element.datum_size.get();
+
+ return true;
+}
+
+// JITs the RS runtime for information about the stride between rows in the
+// allocation. This is done to detect padding, since allocated memory is
+// 16-byte aligned. Returns true on success, false otherwise
+bool RenderScriptRuntime::JITAllocationStride(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!alloc->address.isValid() || !alloc->data_ptr.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ const char *fmt_str = JITTemplate(eExprGetOffsetPtr);
+ char expr_buf[jit_max_expr_size];
+
+ int written = snprintf(expr_buf, jit_max_expr_size, fmt_str,
+ *alloc->address.get(), 0, 1, 0);
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ uint64_t result = 0;
+ if (!EvalRSExpression(expr_buf, frame_ptr, &result))
+ return false;
+
+ addr_t mem_ptr = static_cast<lldb::addr_t>(result);
+ alloc->stride = static_cast<uint32_t>(mem_ptr - *alloc->data_ptr.get());
+
+ return true;
+}
+
+// JIT all the current runtime info regarding an allocation
+bool RenderScriptRuntime::RefreshAllocation(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ // GetOffsetPointer()
+ if (!JITDataPointer(alloc, frame_ptr))
+ return false;
+
+ // rsaAllocationGetType()
+ if (!JITTypePointer(alloc, frame_ptr))
+ return false;
+
+ // rsaTypeGetNativeData()
+ if (!JITTypePacked(alloc, frame_ptr))
+ return false;
+
+ // rsaElementGetNativeData()
+ if (!JITElementPacked(alloc->element, *alloc->context.get(), frame_ptr))
+ return false;
+
+ // Sets the datum_size member in Element
+ SetElementSize(alloc->element);
+
+ // Use GetOffsetPointer() to infer size of the allocation
+ return JITAllocationSize(alloc, frame_ptr);
+}
+
+// Function attempts to set the type_name member of the paramaterised Element
+// object. This string should be the name of the struct type the Element
+// represents. We need this string for pretty printing the Element to users.
+void RenderScriptRuntime::FindStructTypeName(Element &elem,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!elem.type_name.IsEmpty()) // Name already set
+ return;
+ else
+ elem.type_name = Element::GetFallbackStructName(); // Default type name if
+ // we don't succeed
+
+ // Find all the global variables from the script rs modules
+ VariableList var_list;
+ for (auto module_sp : m_rsmodules)
+ module_sp->m_module->FindGlobalVariables(
+ RegularExpression(llvm::StringRef(".")), UINT32_MAX, var_list);
+
+ // Iterate over all the global variables looking for one with a matching type
+ // to the Element. We make the assumption a match exists since there needs to
+ // be a global variable to reflect the struct type back into java host code.
+ for (uint32_t i = 0; i < var_list.GetSize(); ++i) {
+ const VariableSP var_sp(var_list.GetVariableAtIndex(i));
+ if (!var_sp)
+ continue;
+
+ ValueObjectSP valobj_sp = ValueObjectVariable::Create(frame_ptr, var_sp);
+ if (!valobj_sp)
+ continue;
+
+ // Find the number of variable fields.
+ // If it has no fields, or more fields than our Element, then it can't be
+ // the struct we're looking for. Don't check for equality since RS can add
+ // extra struct members for padding.
+ size_t num_children = valobj_sp->GetNumChildren();
+ if (num_children > elem.children.size() || num_children == 0)
+ continue;
+
+ // Iterate over children looking for members with matching field names. If
+ // all the field names match, this is likely the struct we want.
+ // TODO: This could be made more robust by also checking children data
+ // sizes, or array size
+ bool found = true;
+ for (size_t i = 0; i < num_children; ++i) {
+ ValueObjectSP child = valobj_sp->GetChildAtIndex(i, true);
+ if (!child || (child->GetName() != elem.children[i].type_name)) {
+ found = false;
+ break;
+ }
+ }
+
+ // RS can add extra struct members for padding in the format
+ // '#rs_padding_[0-9]+'
+ if (found && num_children < elem.children.size()) {
+ const uint32_t size_diff = elem.children.size() - num_children;
+ if (log)
+ log->Printf("%s - %" PRIu32 " padding struct entries", __FUNCTION__,
+ size_diff);
+
+ for (uint32_t i = 0; i < size_diff; ++i) {
+ ConstString name = elem.children[num_children + i].type_name;
+ if (strcmp(name.AsCString(), "#rs_padding") < 0)
+ found = false;
+ }
+ }
+
+ // We've found a global variable with matching type
+ if (found) {
+ // Dereference since our Element type isn't a pointer.
+ if (valobj_sp->IsPointerType()) {
+ Status err;
+ ValueObjectSP deref_valobj = valobj_sp->Dereference(err);
+ if (!err.Fail())
+ valobj_sp = deref_valobj;
+ }
+
+ // Save name of variable in Element.
+ elem.type_name = valobj_sp->GetTypeName();
+ if (log)
+ log->Printf("%s - element name set to %s", __FUNCTION__,
+ elem.type_name.AsCString());
+
+ return;
+ }
+ }
+}
+
+// Function sets the datum_size member of Element. Representing the size of a
+// single instance including padding. Assumes the relevant allocation
+// information has already been jitted.
+void RenderScriptRuntime::SetElementSize(Element &elem) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+ const Element::DataType type = *elem.type.get();
+ assert(type >= Element::RS_TYPE_NONE && type <= Element::RS_TYPE_FONT &&
+ "Invalid allocation type");
+
+ const uint32_t vec_size = *elem.type_vec_size.get();
+ uint32_t data_size = 0;
+ uint32_t padding = 0;
+
+ // Element is of a struct type, calculate size recursively.
+ if ((type == Element::RS_TYPE_NONE) && (elem.children.size() > 0)) {
+ for (Element &child : elem.children) {
+ SetElementSize(child);
+ const uint32_t array_size =
+ child.array_size.isValid() ? *child.array_size.get() : 1;
+ data_size += *child.datum_size.get() * array_size;
+ }
+ }
+ // These have been packed already
+ else if (type == Element::RS_TYPE_UNSIGNED_5_6_5 ||
+ type == Element::RS_TYPE_UNSIGNED_5_5_5_1 ||
+ type == Element::RS_TYPE_UNSIGNED_4_4_4_4) {
+ data_size = AllocationDetails::RSTypeToFormat[type][eElementSize];
+ } else if (type < Element::RS_TYPE_ELEMENT) {
+ data_size =
+ vec_size * AllocationDetails::RSTypeToFormat[type][eElementSize];
+ if (vec_size == 3)
+ padding = AllocationDetails::RSTypeToFormat[type][eElementSize];
+ } else
+ data_size =
+ GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize();
+
+ elem.padding = padding;
+ elem.datum_size = data_size + padding;
+ if (log)
+ log->Printf("%s - element size set to %" PRIu32, __FUNCTION__,
+ data_size + padding);
+}
+
+// Given an allocation, this function copies the allocation contents from
+// device into a buffer on the heap. Returning a shared pointer to the buffer
+// containing the data.
+std::shared_ptr<uint8_t>
+RenderScriptRuntime::GetAllocationData(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ // JIT all the allocation details
+ if (alloc->ShouldRefresh()) {
+ if (log)
+ log->Printf("%s - allocation details not calculated yet, jitting info",
+ __FUNCTION__);
+
+ if (!RefreshAllocation(alloc, frame_ptr)) {
+ if (log)
+ log->Printf("%s - couldn't JIT allocation details", __FUNCTION__);
+ return nullptr;
+ }
+ }
+
+ assert(alloc->data_ptr.isValid() && alloc->element.type.isValid() &&
+ alloc->element.type_vec_size.isValid() && alloc->size.isValid() &&
+ "Allocation information not available");
+
+ // Allocate a buffer to copy data into
+ const uint32_t size = *alloc->size.get();
+ std::shared_ptr<uint8_t> buffer(new uint8_t[size]);
+ if (!buffer) {
+ if (log)
+ log->Printf("%s - couldn't allocate a %" PRIu32 " byte buffer",
+ __FUNCTION__, size);
+ return nullptr;
+ }
+
+ // Read the inferior memory
+ Status err;
+ lldb::addr_t data_ptr = *alloc->data_ptr.get();
+ GetProcess()->ReadMemory(data_ptr, buffer.get(), size, err);
+ if (err.Fail()) {
+ if (log)
+ log->Printf("%s - '%s' Couldn't read %" PRIu32
+ " bytes of allocation data from 0x%" PRIx64,
+ __FUNCTION__, err.AsCString(), size, data_ptr);
+ return nullptr;
+ }
+
+ return buffer;
+}
+
+// Function copies data from a binary file into an allocation. There is a
+// header at the start of the file, FileHeader, before the data content itself.
+// Information from this header is used to display warnings to the user about
+// incompatibilities
+bool RenderScriptRuntime::LoadAllocation(Stream &strm, const uint32_t alloc_id,
+ const char *path,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ // Find allocation with the given id
+ AllocationDetails *alloc = FindAllocByID(strm, alloc_id);
+ if (!alloc)
+ return false;
+
+ if (log)
+ log->Printf("%s - found allocation 0x%" PRIx64, __FUNCTION__,
+ *alloc->address.get());
+
+ // JIT all the allocation details
+ if (alloc->ShouldRefresh()) {
+ if (log)
+ log->Printf("%s - allocation details not calculated yet, jitting info.",
+ __FUNCTION__);
+
+ if (!RefreshAllocation(alloc, frame_ptr)) {
+ if (log)
+ log->Printf("%s - couldn't JIT allocation details", __FUNCTION__);
+ return false;
+ }
+ }
+
+ assert(alloc->data_ptr.isValid() && alloc->element.type.isValid() &&
+ alloc->element.type_vec_size.isValid() && alloc->size.isValid() &&
+ alloc->element.datum_size.isValid() &&
+ "Allocation information not available");
+
+ // Check we can read from file
+ FileSpec file(path);
+ FileSystem::Instance().Resolve(file);
+ if (!FileSystem::Instance().Exists(file)) {
+ strm.Printf("Error: File %s does not exist", path);
+ strm.EOL();
+ return false;
+ }
+
+ if (!FileSystem::Instance().Readable(file)) {
+ strm.Printf("Error: File %s does not have readable permissions", path);
+ strm.EOL();
+ return false;
+ }
+
+ // Read file into data buffer
+ auto data_sp = FileSystem::Instance().CreateDataBuffer(file.GetPath());
+
+ // Cast start of buffer to FileHeader and use pointer to read metadata
+ void *file_buf = data_sp->GetBytes();
+ if (file_buf == nullptr ||
+ data_sp->GetByteSize() < (sizeof(AllocationDetails::FileHeader) +
+ sizeof(AllocationDetails::ElementHeader))) {
+ strm.Printf("Error: File %s does not contain enough data for header", path);
+ strm.EOL();
+ return false;
+ }
+ const AllocationDetails::FileHeader *file_header =
+ static_cast<AllocationDetails::FileHeader *>(file_buf);
+
+ // Check file starts with ascii characters "RSAD"
+ if (memcmp(file_header->ident, "RSAD", 4)) {
+ strm.Printf("Error: File doesn't contain identifier for an RS allocation "
+ "dump. Are you sure this is the correct file?");
+ strm.EOL();
+ return false;
+ }
+
+ // Look at the type of the root element in the header
+ AllocationDetails::ElementHeader root_el_hdr;
+ memcpy(&root_el_hdr, static_cast<uint8_t *>(file_buf) +
+ sizeof(AllocationDetails::FileHeader),
+ sizeof(AllocationDetails::ElementHeader));
+
+ if (log)
+ log->Printf("%s - header type %" PRIu32 ", element size %" PRIu32,
+ __FUNCTION__, root_el_hdr.type, root_el_hdr.element_size);
+
+ // Check if the target allocation and file both have the same number of bytes
+ // for an Element
+ if (*alloc->element.datum_size.get() != root_el_hdr.element_size) {
+ strm.Printf("Warning: Mismatched Element sizes - file %" PRIu32
+ " bytes, allocation %" PRIu32 " bytes",
+ root_el_hdr.element_size, *alloc->element.datum_size.get());
+ strm.EOL();
+ }
+
+ // Check if the target allocation and file both have the same type
+ const uint32_t alloc_type = static_cast<uint32_t>(*alloc->element.type.get());
+ const uint32_t file_type = root_el_hdr.type;
+
+ if (file_type > Element::RS_TYPE_FONT) {
+ strm.Printf("Warning: File has unknown allocation type");
+ strm.EOL();
+ } else if (alloc_type != file_type) {
+ // Enum value isn't monotonous, so doesn't always index RsDataTypeToString
+ // array
+ uint32_t target_type_name_idx = alloc_type;
+ uint32_t head_type_name_idx = file_type;
+ if (alloc_type >= Element::RS_TYPE_ELEMENT &&
+ alloc_type <= Element::RS_TYPE_FONT)
+ target_type_name_idx = static_cast<Element::DataType>(
+ (alloc_type - Element::RS_TYPE_ELEMENT) +
+ Element::RS_TYPE_MATRIX_2X2 + 1);
+
+ if (file_type >= Element::RS_TYPE_ELEMENT &&
+ file_type <= Element::RS_TYPE_FONT)
+ head_type_name_idx = static_cast<Element::DataType>(
+ (file_type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 +
+ 1);
+
+ const char *head_type_name =
+ AllocationDetails::RsDataTypeToString[head_type_name_idx][0];
+ const char *target_type_name =
+ AllocationDetails::RsDataTypeToString[target_type_name_idx][0];
+
+ strm.Printf(
+ "Warning: Mismatched Types - file '%s' type, allocation '%s' type",
+ head_type_name, target_type_name);
+ strm.EOL();
+ }
+
+ // Advance buffer past header
+ file_buf = static_cast<uint8_t *>(file_buf) + file_header->hdr_size;
+
+ // Calculate size of allocation data in file
+ size_t size = data_sp->GetByteSize() - file_header->hdr_size;
+
+ // Check if the target allocation and file both have the same total data
+ // size.
+ const uint32_t alloc_size = *alloc->size.get();
+ if (alloc_size != size) {
+ strm.Printf("Warning: Mismatched allocation sizes - file 0x%" PRIx64
+ " bytes, allocation 0x%" PRIx32 " bytes",
+ (uint64_t)size, alloc_size);
+ strm.EOL();
+ // Set length to copy to minimum
+ size = alloc_size < size ? alloc_size : size;
+ }
+
+ // Copy file data from our buffer into the target allocation.
+ lldb::addr_t alloc_data = *alloc->data_ptr.get();
+ Status err;
+ size_t written = GetProcess()->WriteMemory(alloc_data, file_buf, size, err);
+ if (!err.Success() || written != size) {
+ strm.Printf("Error: Couldn't write data to allocation %s", err.AsCString());
+ strm.EOL();
+ return false;
+ }
+
+ strm.Printf("Contents of file '%s' read into allocation %" PRIu32, path,
+ alloc->id);
+ strm.EOL();
+
+ return true;
+}
+
+// Function takes as parameters a byte buffer, which will eventually be written
+// to file as the element header, an offset into that buffer, and an Element
+// that will be saved into the buffer at the parametrised offset. Return value
+// is the new offset after writing the element into the buffer. Elements are
+// saved to the file as the ElementHeader struct followed by offsets to the
+// structs of all the element's children.
+size_t RenderScriptRuntime::PopulateElementHeaders(
+ const std::shared_ptr<uint8_t> header_buffer, size_t offset,
+ const Element &elem) {
+ // File struct for an element header with all the relevant details copied
+ // from elem. We assume members are valid already.
+ AllocationDetails::ElementHeader elem_header;
+ elem_header.type = *elem.type.get();
+ elem_header.kind = *elem.type_kind.get();
+ elem_header.element_size = *elem.datum_size.get();
+ elem_header.vector_size = *elem.type_vec_size.get();
+ elem_header.array_size =
+ elem.array_size.isValid() ? *elem.array_size.get() : 0;
+ const size_t elem_header_size = sizeof(AllocationDetails::ElementHeader);
+
+ // Copy struct into buffer and advance offset We assume that header_buffer
+ // has been checked for nullptr before this method is called
+ memcpy(header_buffer.get() + offset, &elem_header, elem_header_size);
+ offset += elem_header_size;
+
+ // Starting offset of child ElementHeader struct
+ size_t child_offset =
+ offset + ((elem.children.size() + 1) * sizeof(uint32_t));
+ for (const RenderScriptRuntime::Element &child : elem.children) {
+ // Recursively populate the buffer with the element header structs of
+ // children. Then save the offsets where they were set after the parent
+ // element header.
+ memcpy(header_buffer.get() + offset, &child_offset, sizeof(uint32_t));
+ offset += sizeof(uint32_t);
+
+ child_offset = PopulateElementHeaders(header_buffer, child_offset, child);
+ }
+
+ // Zero indicates no more children
+ memset(header_buffer.get() + offset, 0, sizeof(uint32_t));
+
+ return child_offset;
+}
+
+// Given an Element object this function returns the total size needed in the
+// file header to store the element's details. Taking into account the size of
+// the element header struct, plus the offsets to all the element's children.
+// Function is recursive so that the size of all ancestors is taken into
+// account.
+size_t RenderScriptRuntime::CalculateElementHeaderSize(const Element &elem) {
+ // Offsets to children plus zero terminator
+ size_t size = (elem.children.size() + 1) * sizeof(uint32_t);
+ // Size of header struct with type details
+ size += sizeof(AllocationDetails::ElementHeader);
+
+ // Calculate recursively for all descendants
+ for (const Element &child : elem.children)
+ size += CalculateElementHeaderSize(child);
+
+ return size;
+}
+
+// Function copies allocation contents into a binary file. This file can then
+// be loaded later into a different allocation. There is a header, FileHeader,
+// before the allocation data containing meta-data.
+bool RenderScriptRuntime::SaveAllocation(Stream &strm, const uint32_t alloc_id,
+ const char *path,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ // Find allocation with the given id
+ AllocationDetails *alloc = FindAllocByID(strm, alloc_id);
+ if (!alloc)
+ return false;
+
+ if (log)
+ log->Printf("%s - found allocation 0x%" PRIx64 ".", __FUNCTION__,
+ *alloc->address.get());
+
+ // JIT all the allocation details
+ if (alloc->ShouldRefresh()) {
+ if (log)
+ log->Printf("%s - allocation details not calculated yet, jitting info.",
+ __FUNCTION__);
+
+ if (!RefreshAllocation(alloc, frame_ptr)) {
+ if (log)
+ log->Printf("%s - couldn't JIT allocation details.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ assert(alloc->data_ptr.isValid() && alloc->element.type.isValid() &&
+ alloc->element.type_vec_size.isValid() &&
+ alloc->element.datum_size.get() &&
+ alloc->element.type_kind.isValid() && alloc->dimension.isValid() &&
+ "Allocation information not available");
+
+ // Check we can create writable file
+ FileSpec file_spec(path);
+ FileSystem::Instance().Resolve(file_spec);
+ File file;
+ FileSystem::Instance().Open(file, file_spec,
+ File::eOpenOptionWrite |
+ File::eOpenOptionCanCreate |
+ File::eOpenOptionTruncate);
+
+ if (!file) {
+ strm.Printf("Error: Failed to open '%s' for writing", path);
+ strm.EOL();
+ return false;
+ }
+
+ // Read allocation into buffer of heap memory
+ const std::shared_ptr<uint8_t> buffer = GetAllocationData(alloc, frame_ptr);
+ if (!buffer) {
+ strm.Printf("Error: Couldn't read allocation data into buffer");
+ strm.EOL();
+ return false;
+ }
+
+ // Create the file header
+ AllocationDetails::FileHeader head;
+ memcpy(head.ident, "RSAD", 4);
+ head.dims[0] = static_cast<uint32_t>(alloc->dimension.get()->dim_1);
+ head.dims[1] = static_cast<uint32_t>(alloc->dimension.get()->dim_2);
+ head.dims[2] = static_cast<uint32_t>(alloc->dimension.get()->dim_3);
+
+ const size_t element_header_size = CalculateElementHeaderSize(alloc->element);
+ assert((sizeof(AllocationDetails::FileHeader) + element_header_size) <
+ UINT16_MAX &&
+ "Element header too large");
+ head.hdr_size = static_cast<uint16_t>(sizeof(AllocationDetails::FileHeader) +
+ element_header_size);
+
+ // Write the file header
+ size_t num_bytes = sizeof(AllocationDetails::FileHeader);
+ if (log)
+ log->Printf("%s - writing File Header, 0x%" PRIx64 " bytes", __FUNCTION__,
+ (uint64_t)num_bytes);
+
+ Status err = file.Write(&head, num_bytes);
+ if (!err.Success()) {
+ strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), path);
+ strm.EOL();
+ return false;
+ }
+
+ // Create the headers describing the element type of the allocation.
+ std::shared_ptr<uint8_t> element_header_buffer(
+ new uint8_t[element_header_size]);
+ if (element_header_buffer == nullptr) {
+ strm.Printf("Internal Error: Couldn't allocate %" PRIu64
+ " bytes on the heap",
+ (uint64_t)element_header_size);
+ strm.EOL();
+ return false;
+ }
+
+ PopulateElementHeaders(element_header_buffer, 0, alloc->element);
+
+ // Write headers for allocation element type to file
+ num_bytes = element_header_size;
+ if (log)
+ log->Printf("%s - writing element headers, 0x%" PRIx64 " bytes.",
+ __FUNCTION__, (uint64_t)num_bytes);
+
+ err = file.Write(element_header_buffer.get(), num_bytes);
+ if (!err.Success()) {
+ strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), path);
+ strm.EOL();
+ return false;
+ }
+
+ // Write allocation data to file
+ num_bytes = static_cast<size_t>(*alloc->size.get());
+ if (log)
+ log->Printf("%s - writing 0x%" PRIx64 " bytes", __FUNCTION__,
+ (uint64_t)num_bytes);
+
+ err = file.Write(buffer.get(), num_bytes);
+ if (!err.Success()) {
+ strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), path);
+ strm.EOL();
+ return false;
+ }
+
+ strm.Printf("Allocation written to file '%s'", path);
+ strm.EOL();
+ return true;
+}
+
+bool RenderScriptRuntime::LoadModule(const lldb::ModuleSP &module_sp) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (module_sp) {
+ for (const auto &rs_module : m_rsmodules) {
+ if (rs_module->m_module == module_sp) {
+ // Check if the user has enabled automatically breaking on all RS
+ // kernels.
+ if (m_breakAllKernels)
+ BreakOnModuleKernels(rs_module);
+
+ return false;
+ }
+ }
+ bool module_loaded = false;
+ switch (GetModuleKind(module_sp)) {
+ case eModuleKindKernelObj: {
+ RSModuleDescriptorSP module_desc;
+ module_desc = std::make_shared<RSModuleDescriptor>(module_sp);
+ if (module_desc->ParseRSInfo()) {
+ m_rsmodules.push_back(module_desc);
+ module_desc->WarnIfVersionMismatch(GetProcess()
+ ->GetTarget()
+ .GetDebugger()
+ .GetAsyncOutputStream()
+ .get());
+ module_loaded = true;
+ }
+ if (module_loaded) {
+ FixupScriptDetails(module_desc);
+ }
+ break;
+ }
+ case eModuleKindDriver: {
+ if (!m_libRSDriver) {
+ m_libRSDriver = module_sp;
+ LoadRuntimeHooks(m_libRSDriver, RenderScriptRuntime::eModuleKindDriver);
+ }
+ break;
+ }
+ case eModuleKindImpl: {
+ if (!m_libRSCpuRef) {
+ m_libRSCpuRef = module_sp;
+ LoadRuntimeHooks(m_libRSCpuRef, RenderScriptRuntime::eModuleKindImpl);
+ }
+ break;
+ }
+ case eModuleKindLibRS: {
+ if (!m_libRS) {
+ m_libRS = module_sp;
+ static ConstString gDbgPresentStr("gDebuggerPresent");
+ const Symbol *debug_present = m_libRS->FindFirstSymbolWithNameAndType(
+ gDbgPresentStr, eSymbolTypeData);
+ if (debug_present) {
+ Status err;
+ uint32_t flag = 0x00000001U;
+ Target &target = GetProcess()->GetTarget();
+ addr_t addr = debug_present->GetLoadAddress(&target);
+ GetProcess()->WriteMemory(addr, &flag, sizeof(flag), err);
+ if (err.Success()) {
+ if (log)
+ log->Printf("%s - debugger present flag set on debugee.",
+ __FUNCTION__);
+
+ m_debuggerPresentFlagged = true;
+ } else if (log) {
+ log->Printf("%s - error writing debugger present flags '%s' ",
+ __FUNCTION__, err.AsCString());
+ }
+ } else if (log) {
+ log->Printf(
+ "%s - error writing debugger present flags - symbol not found",
+ __FUNCTION__);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (module_loaded)
+ Update();
+ return module_loaded;
+ }
+ return false;
+}
+
+void RenderScriptRuntime::Update() {
+ if (m_rsmodules.size() > 0) {
+ if (!m_initiated) {
+ Initiate();
+ }
+ }
+}
+
+void RSModuleDescriptor::WarnIfVersionMismatch(lldb_private::Stream *s) const {
+ if (!s)
+ return;
+
+ if (m_slang_version.empty() || m_bcc_version.empty()) {
+ s->PutCString("WARNING: Unknown bcc or slang (llvm-rs-cc) version; debug "
+ "experience may be unreliable");
+ s->EOL();
+ } else if (m_slang_version != m_bcc_version) {
+ s->Printf("WARNING: The debug info emitted by the slang frontend "
+ "(llvm-rs-cc) used to build this module (%s) does not match the "
+ "version of bcc used to generate the debug information (%s). "
+ "This is an unsupported configuration and may result in a poor "
+ "debugging experience; proceed with caution",
+ m_slang_version.c_str(), m_bcc_version.c_str());
+ s->EOL();
+ }
+}
+
+bool RSModuleDescriptor::ParsePragmaCount(llvm::StringRef *lines,
+ size_t n_lines) {
+ // Skip the pragma prototype line
+ ++lines;
+ for (; n_lines--; ++lines) {
+ const auto kv_pair = lines->split(" - ");
+ m_pragmas[kv_pair.first.trim().str()] = kv_pair.second.trim().str();
+ }
+ return true;
+}
+
+bool RSModuleDescriptor::ParseExportReduceCount(llvm::StringRef *lines,
+ size_t n_lines) {
+ // The list of reduction kernels in the `.rs.info` symbol is of the form
+ // "signature - accumulatordatasize - reduction_name - initializer_name -
+ // accumulator_name - combiner_name - outconverter_name - halter_name" Where
+ // a function is not explicitly named by the user, or is not generated by the
+ // compiler, it is named "." so the dash separated list should always be 8
+ // items long
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+ // Skip the exportReduceCount line
+ ++lines;
+ for (; n_lines--; ++lines) {
+ llvm::SmallVector<llvm::StringRef, 8> spec;
+ lines->split(spec, " - ");
+ if (spec.size() != 8) {
+ if (spec.size() < 8) {
+ if (log)
+ log->Error("Error parsing RenderScript reduction spec. wrong number "
+ "of fields");
+ return false;
+ } else if (log)
+ log->Warning("Extraneous members in reduction spec: '%s'",
+ lines->str().c_str());
+ }
+
+ const auto sig_s = spec[0];
+ uint32_t sig;
+ if (sig_s.getAsInteger(10, sig)) {
+ if (log)
+ log->Error("Error parsing Renderscript reduction spec: invalid kernel "
+ "signature: '%s'",
+ sig_s.str().c_str());
+ return false;
+ }
+
+ const auto accum_data_size_s = spec[1];
+ uint32_t accum_data_size;
+ if (accum_data_size_s.getAsInteger(10, accum_data_size)) {
+ if (log)
+ log->Error("Error parsing Renderscript reduction spec: invalid "
+ "accumulator data size %s",
+ accum_data_size_s.str().c_str());
+ return false;
+ }
+
+ if (log)
+ log->Printf("Found RenderScript reduction '%s'", spec[2].str().c_str());
+
+ m_reductions.push_back(RSReductionDescriptor(this, sig, accum_data_size,
+ spec[2], spec[3], spec[4],
+ spec[5], spec[6], spec[7]));
+ }
+ return true;
+}
+
+bool RSModuleDescriptor::ParseVersionInfo(llvm::StringRef *lines,
+ size_t n_lines) {
+ // Skip the versionInfo line
+ ++lines;
+ for (; n_lines--; ++lines) {
+ // We're only interested in bcc and slang versions, and ignore all other
+ // versionInfo lines
+ const auto kv_pair = lines->split(" - ");
+ if (kv_pair.first == "slang")
+ m_slang_version = kv_pair.second.str();
+ else if (kv_pair.first == "bcc")
+ m_bcc_version = kv_pair.second.str();
+ }
+ return true;
+}
+
+bool RSModuleDescriptor::ParseExportForeachCount(llvm::StringRef *lines,
+ size_t n_lines) {
+ // Skip the exportForeachCount line
+ ++lines;
+ for (; n_lines--; ++lines) {
+ uint32_t slot;
+ // `forEach` kernels are listed in the `.rs.info` packet as a "slot - name"
+ // pair per line
+ const auto kv_pair = lines->split(" - ");
+ if (kv_pair.first.getAsInteger(10, slot))
+ return false;
+ m_kernels.push_back(RSKernelDescriptor(this, kv_pair.second, slot));
+ }
+ return true;
+}
+
+bool RSModuleDescriptor::ParseExportVarCount(llvm::StringRef *lines,
+ size_t n_lines) {
+ // Skip the ExportVarCount line
+ ++lines;
+ for (; n_lines--; ++lines)
+ m_globals.push_back(RSGlobalDescriptor(this, *lines));
+ return true;
+}
+
+// The .rs.info symbol in renderscript modules contains a string which needs to
+// be parsed. The string is basic and is parsed on a line by line basis.
+bool RSModuleDescriptor::ParseRSInfo() {
+ assert(m_module);
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+ const Symbol *info_sym = m_module->FindFirstSymbolWithNameAndType(
+ ConstString(".rs.info"), eSymbolTypeData);
+ if (!info_sym)
+ return false;
+
+ const addr_t addr = info_sym->GetAddressRef().GetFileAddress();
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ const addr_t size = info_sym->GetByteSize();
+ const FileSpec fs = m_module->GetFileSpec();
+
+ auto buffer =
+ FileSystem::Instance().CreateDataBuffer(fs.GetPath(), size, addr);
+ if (!buffer)
+ return false;
+
+ // split rs.info. contents into lines
+ llvm::SmallVector<llvm::StringRef, 128> info_lines;
+ {
+ const llvm::StringRef raw_rs_info((const char *)buffer->GetBytes());
+ raw_rs_info.split(info_lines, '\n');
+ if (log)
+ log->Printf("'.rs.info symbol for '%s':\n%s",
+ m_module->GetFileSpec().GetCString(),
+ raw_rs_info.str().c_str());
+ }
+
+ enum {
+ eExportVar,
+ eExportForEach,
+ eExportReduce,
+ ePragma,
+ eBuildChecksum,
+ eObjectSlot,
+ eVersionInfo,
+ };
+
+ const auto rs_info_handler = [](llvm::StringRef name) -> int {
+ return llvm::StringSwitch<int>(name)
+ // The number of visible global variables in the script
+ .Case("exportVarCount", eExportVar)
+ // The number of RenderScrip `forEach` kernels __attribute__((kernel))
+ .Case("exportForEachCount", eExportForEach)
+ // The number of generalreductions: This marked in the script by
+ // `#pragma reduce()`
+ .Case("exportReduceCount", eExportReduce)
+ // Total count of all RenderScript specific `#pragmas` used in the
+ // script
+ .Case("pragmaCount", ePragma)
+ .Case("objectSlotCount", eObjectSlot)
+ .Case("versionInfo", eVersionInfo)
+ .Default(-1);
+ };
+
+ // parse all text lines of .rs.info
+ for (auto line = info_lines.begin(); line != info_lines.end(); ++line) {
+ const auto kv_pair = line->split(": ");
+ const auto key = kv_pair.first;
+ const auto val = kv_pair.second.trim();
+
+ const auto handler = rs_info_handler(key);
+ if (handler == -1)
+ continue;
+ // getAsInteger returns `true` on an error condition - we're only
+ // interested in numeric fields at the moment
+ uint64_t n_lines;
+ if (val.getAsInteger(10, n_lines)) {
+ LLDB_LOGV(log, "Failed to parse non-numeric '.rs.info' section {0}",
+ line->str());
+ continue;
+ }
+ if (info_lines.end() - (line + 1) < (ptrdiff_t)n_lines)
+ return false;
+
+ bool success = false;
+ switch (handler) {
+ case eExportVar:
+ success = ParseExportVarCount(line, n_lines);
+ break;
+ case eExportForEach:
+ success = ParseExportForeachCount(line, n_lines);
+ break;
+ case eExportReduce:
+ success = ParseExportReduceCount(line, n_lines);
+ break;
+ case ePragma:
+ success = ParsePragmaCount(line, n_lines);
+ break;
+ case eVersionInfo:
+ success = ParseVersionInfo(line, n_lines);
+ break;
+ default: {
+ if (log)
+ log->Printf("%s - skipping .rs.info field '%s'", __FUNCTION__,
+ line->str().c_str());
+ continue;
+ }
+ }
+ if (!success)
+ return false;
+ line += n_lines;
+ }
+ return info_lines.size() > 0;
+}
+
+void RenderScriptRuntime::DumpStatus(Stream &strm) const {
+ if (m_libRS) {
+ strm.Printf("Runtime Library discovered.");
+ strm.EOL();
+ }
+ if (m_libRSDriver) {
+ strm.Printf("Runtime Driver discovered.");
+ strm.EOL();
+ }
+ if (m_libRSCpuRef) {
+ strm.Printf("CPU Reference Implementation discovered.");
+ strm.EOL();
+ }
+
+ if (m_runtimeHooks.size()) {
+ strm.Printf("Runtime functions hooked:");
+ strm.EOL();
+ for (auto b : m_runtimeHooks) {
+ strm.Indent(b.second->defn->name);
+ strm.EOL();
+ }
+ } else {
+ strm.Printf("Runtime is not hooked.");
+ strm.EOL();
+ }
+}
+
+void RenderScriptRuntime::DumpContexts(Stream &strm) const {
+ strm.Printf("Inferred RenderScript Contexts:");
+ strm.EOL();
+ strm.IndentMore();
+
+ std::map<addr_t, uint64_t> contextReferences;
+
+ // Iterate over all of the currently discovered scripts. Note: We cant push
+ // or pop from m_scripts inside this loop or it may invalidate script.
+ for (const auto &script : m_scripts) {
+ if (!script->context.isValid())
+ continue;
+ lldb::addr_t context = *script->context;
+
+ if (contextReferences.find(context) != contextReferences.end()) {
+ contextReferences[context]++;
+ } else {
+ contextReferences[context] = 1;
+ }
+ }
+
+ for (const auto &cRef : contextReferences) {
+ strm.Printf("Context 0x%" PRIx64 ": %" PRIu64 " script instances",
+ cRef.first, cRef.second);
+ strm.EOL();
+ }
+ strm.IndentLess();
+}
+
+void RenderScriptRuntime::DumpKernels(Stream &strm) const {
+ strm.Printf("RenderScript Kernels:");
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &module : m_rsmodules) {
+ strm.Printf("Resource '%s':", module->m_resname.c_str());
+ strm.EOL();
+ for (const auto &kernel : module->m_kernels) {
+ strm.Indent(kernel.m_name.AsCString());
+ strm.EOL();
+ }
+ }
+ strm.IndentLess();
+}
+
+RenderScriptRuntime::AllocationDetails *
+RenderScriptRuntime::FindAllocByID(Stream &strm, const uint32_t alloc_id) {
+ AllocationDetails *alloc = nullptr;
+
+ // See if we can find allocation using id as an index;
+ if (alloc_id <= m_allocations.size() && alloc_id != 0 &&
+ m_allocations[alloc_id - 1]->id == alloc_id) {
+ alloc = m_allocations[alloc_id - 1].get();
+ return alloc;
+ }
+
+ // Fallback to searching
+ for (const auto &a : m_allocations) {
+ if (a->id == alloc_id) {
+ alloc = a.get();
+ break;
+ }
+ }
+
+ if (alloc == nullptr) {
+ strm.Printf("Error: Couldn't find allocation with id matching %" PRIu32,
+ alloc_id);
+ strm.EOL();
+ }
+
+ return alloc;
+}
+
+// Prints the contents of an allocation to the output stream, which may be a
+// file
+bool RenderScriptRuntime::DumpAllocation(Stream &strm, StackFrame *frame_ptr,
+ const uint32_t id) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ // Check we can find the desired allocation
+ AllocationDetails *alloc = FindAllocByID(strm, id);
+ if (!alloc)
+ return false; // FindAllocByID() will print error message for us here
+
+ if (log)
+ log->Printf("%s - found allocation 0x%" PRIx64, __FUNCTION__,
+ *alloc->address.get());
+
+ // Check we have information about the allocation, if not calculate it
+ if (alloc->ShouldRefresh()) {
+ if (log)
+ log->Printf("%s - allocation details not calculated yet, jitting info.",
+ __FUNCTION__);
+
+ // JIT all the allocation information
+ if (!RefreshAllocation(alloc, frame_ptr)) {
+ strm.Printf("Error: Couldn't JIT allocation details");
+ strm.EOL();
+ return false;
+ }
+ }
+
+ // Establish format and size of each data element
+ const uint32_t vec_size = *alloc->element.type_vec_size.get();
+ const Element::DataType type = *alloc->element.type.get();
+
+ assert(type >= Element::RS_TYPE_NONE && type <= Element::RS_TYPE_FONT &&
+ "Invalid allocation type");
+
+ lldb::Format format;
+ if (type >= Element::RS_TYPE_ELEMENT)
+ format = eFormatHex;
+ else
+ format = vec_size == 1
+ ? static_cast<lldb::Format>(
+ AllocationDetails::RSTypeToFormat[type][eFormatSingle])
+ : static_cast<lldb::Format>(
+ AllocationDetails::RSTypeToFormat[type][eFormatVector]);
+
+ const uint32_t data_size = *alloc->element.datum_size.get();
+
+ if (log)
+ log->Printf("%s - element size %" PRIu32 " bytes, including padding",
+ __FUNCTION__, data_size);
+
+ // Allocate a buffer to copy data into
+ std::shared_ptr<uint8_t> buffer = GetAllocationData(alloc, frame_ptr);
+ if (!buffer) {
+ strm.Printf("Error: Couldn't read allocation data");
+ strm.EOL();
+ return false;
+ }
+
+ // Calculate stride between rows as there may be padding at end of rows since
+ // allocated memory is 16-byte aligned
+ if (!alloc->stride.isValid()) {
+ if (alloc->dimension.get()->dim_2 == 0) // We only have one dimension
+ alloc->stride = 0;
+ else if (!JITAllocationStride(alloc, frame_ptr)) {
+ strm.Printf("Error: Couldn't calculate allocation row stride");
+ strm.EOL();
+ return false;
+ }
+ }
+ const uint32_t stride = *alloc->stride.get();
+ const uint32_t size = *alloc->size.get(); // Size of whole allocation
+ const uint32_t padding =
+ alloc->element.padding.isValid() ? *alloc->element.padding.get() : 0;
+ if (log)
+ log->Printf("%s - stride %" PRIu32 " bytes, size %" PRIu32
+ " bytes, padding %" PRIu32,
+ __FUNCTION__, stride, size, padding);
+
+ // Find dimensions used to index loops, so need to be non-zero
+ uint32_t dim_x = alloc->dimension.get()->dim_1;
+ dim_x = dim_x == 0 ? 1 : dim_x;
+
+ uint32_t dim_y = alloc->dimension.get()->dim_2;
+ dim_y = dim_y == 0 ? 1 : dim_y;
+
+ uint32_t dim_z = alloc->dimension.get()->dim_3;
+ dim_z = dim_z == 0 ? 1 : dim_z;
+
+ // Use data extractor to format output
+ const uint32_t target_ptr_size =
+ GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize();
+ DataExtractor alloc_data(buffer.get(), size, GetProcess()->GetByteOrder(),
+ target_ptr_size);
+
+ uint32_t offset = 0; // Offset in buffer to next element to be printed
+ uint32_t prev_row = 0; // Offset to the start of the previous row
+
+ // Iterate over allocation dimensions, printing results to user
+ strm.Printf("Data (X, Y, Z):");
+ for (uint32_t z = 0; z < dim_z; ++z) {
+ for (uint32_t y = 0; y < dim_y; ++y) {
+ // Use stride to index start of next row.
+ if (!(y == 0 && z == 0))
+ offset = prev_row + stride;
+ prev_row = offset;
+
+ // Print each element in the row individually
+ for (uint32_t x = 0; x < dim_x; ++x) {
+ strm.Printf("\n(%" PRIu32 ", %" PRIu32 ", %" PRIu32 ") = ", x, y, z);
+ if ((type == Element::RS_TYPE_NONE) &&
+ (alloc->element.children.size() > 0) &&
+ (alloc->element.type_name != Element::GetFallbackStructName())) {
+ // Here we are dumping an Element of struct type. This is done using
+ // expression evaluation with the name of the struct type and pointer
+ // to element. Don't print the name of the resulting expression,
+ // since this will be '$[0-9]+'
+ DumpValueObjectOptions expr_options;
+ expr_options.SetHideName(true);
+
+ // Setup expression as dereferencing a pointer cast to element
+ // address.
+ char expr_char_buffer[jit_max_expr_size];
+ int written =
+ snprintf(expr_char_buffer, jit_max_expr_size, "*(%s*) 0x%" PRIx64,
+ alloc->element.type_name.AsCString(),
+ *alloc->data_ptr.get() + offset);
+
+ if (written < 0 || written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - error in snprintf().", __FUNCTION__);
+ continue;
+ }
+
+ // Evaluate expression
+ ValueObjectSP expr_result;
+ GetProcess()->GetTarget().EvaluateExpression(expr_char_buffer,
+ frame_ptr, expr_result);
+
+ // Print the results to our stream.
+ expr_result->Dump(strm, expr_options);
+ } else {
+ DumpDataExtractor(alloc_data, &strm, offset, format,
+ data_size - padding, 1, 1, LLDB_INVALID_ADDRESS, 0,
+ 0);
+ }
+ offset += data_size;
+ }
+ }
+ }
+ strm.EOL();
+
+ return true;
+}
+
+// Function recalculates all our cached information about allocations by
+// jitting the RS runtime regarding each allocation we know about. Returns true
+// if all allocations could be recomputed, false otherwise.
+bool RenderScriptRuntime::RecomputeAllAllocations(Stream &strm,
+ StackFrame *frame_ptr) {
+ bool success = true;
+ for (auto &alloc : m_allocations) {
+ // JIT current allocation information
+ if (!RefreshAllocation(alloc.get(), frame_ptr)) {
+ strm.Printf("Error: Couldn't evaluate details for allocation %" PRIu32
+ "\n",
+ alloc->id);
+ success = false;
+ }
+ }
+
+ if (success)
+ strm.Printf("All allocations successfully recomputed");
+ strm.EOL();
+
+ return success;
+}
+
+// Prints information regarding currently loaded allocations. These details are
+// gathered by jitting the runtime, which has as latency. Index parameter
+// specifies a single allocation ID to print, or a zero value to print them all
+void RenderScriptRuntime::ListAllocations(Stream &strm, StackFrame *frame_ptr,
+ const uint32_t index) {
+ strm.Printf("RenderScript Allocations:");
+ strm.EOL();
+ strm.IndentMore();
+
+ for (auto &alloc : m_allocations) {
+ // index will only be zero if we want to print all allocations
+ if (index != 0 && index != alloc->id)
+ continue;
+
+ // JIT current allocation information
+ if (alloc->ShouldRefresh() && !RefreshAllocation(alloc.get(), frame_ptr)) {
+ strm.Printf("Error: Couldn't evaluate details for allocation %" PRIu32,
+ alloc->id);
+ strm.EOL();
+ continue;
+ }
+
+ strm.Printf("%" PRIu32 ":", alloc->id);
+ strm.EOL();
+ strm.IndentMore();
+
+ strm.Indent("Context: ");
+ if (!alloc->context.isValid())
+ strm.Printf("unknown\n");
+ else
+ strm.Printf("0x%" PRIx64 "\n", *alloc->context.get());
+
+ strm.Indent("Address: ");
+ if (!alloc->address.isValid())
+ strm.Printf("unknown\n");
+ else
+ strm.Printf("0x%" PRIx64 "\n", *alloc->address.get());
+
+ strm.Indent("Data pointer: ");
+ if (!alloc->data_ptr.isValid())
+ strm.Printf("unknown\n");
+ else
+ strm.Printf("0x%" PRIx64 "\n", *alloc->data_ptr.get());
+
+ strm.Indent("Dimensions: ");
+ if (!alloc->dimension.isValid())
+ strm.Printf("unknown\n");
+ else
+ strm.Printf("(%" PRId32 ", %" PRId32 ", %" PRId32 ")\n",
+ alloc->dimension.get()->dim_1, alloc->dimension.get()->dim_2,
+ alloc->dimension.get()->dim_3);
+
+ strm.Indent("Data Type: ");
+ if (!alloc->element.type.isValid() ||
+ !alloc->element.type_vec_size.isValid())
+ strm.Printf("unknown\n");
+ else {
+ const int vector_size = *alloc->element.type_vec_size.get();
+ Element::DataType type = *alloc->element.type.get();
+
+ if (!alloc->element.type_name.IsEmpty())
+ strm.Printf("%s\n", alloc->element.type_name.AsCString());
+ else {
+ // Enum value isn't monotonous, so doesn't always index
+ // RsDataTypeToString array
+ if (type >= Element::RS_TYPE_ELEMENT && type <= Element::RS_TYPE_FONT)
+ type =
+ static_cast<Element::DataType>((type - Element::RS_TYPE_ELEMENT) +
+ Element::RS_TYPE_MATRIX_2X2 + 1);
+
+ if (type >= (sizeof(AllocationDetails::RsDataTypeToString) /
+ sizeof(AllocationDetails::RsDataTypeToString[0])) ||
+ vector_size > 4 || vector_size < 1)
+ strm.Printf("invalid type\n");
+ else
+ strm.Printf(
+ "%s\n",
+ AllocationDetails::RsDataTypeToString[static_cast<uint32_t>(type)]
+ [vector_size - 1]);
+ }
+ }
+
+ strm.Indent("Data Kind: ");
+ if (!alloc->element.type_kind.isValid())
+ strm.Printf("unknown\n");
+ else {
+ const Element::DataKind kind = *alloc->element.type_kind.get();
+ if (kind < Element::RS_KIND_USER || kind > Element::RS_KIND_PIXEL_YUV)
+ strm.Printf("invalid kind\n");
+ else
+ strm.Printf(
+ "%s\n",
+ AllocationDetails::RsDataKindToString[static_cast<uint32_t>(kind)]);
+ }
+
+ strm.EOL();
+ strm.IndentLess();
+ }
+ strm.IndentLess();
+}
+
+// Set breakpoints on every kernel found in RS module
+void RenderScriptRuntime::BreakOnModuleKernels(
+ const RSModuleDescriptorSP rsmodule_sp) {
+ for (const auto &kernel : rsmodule_sp->m_kernels) {
+ // Don't set breakpoint on 'root' kernel
+ if (strcmp(kernel.m_name.AsCString(), "root") == 0)
+ continue;
+
+ CreateKernelBreakpoint(kernel.m_name);
+ }
+}
+
+// Method is internally called by the 'kernel breakpoint all' command to enable
+// or disable breaking on all kernels. When do_break is true we want to enable
+// this functionality. When do_break is false we want to disable it.
+void RenderScriptRuntime::SetBreakAllKernels(bool do_break, TargetSP target) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS));
+
+ InitSearchFilter(target);
+
+ // Set breakpoints on all the kernels
+ if (do_break && !m_breakAllKernels) {
+ m_breakAllKernels = true;
+
+ for (const auto &module : m_rsmodules)
+ BreakOnModuleKernels(module);
+
+ if (log)
+ log->Printf("%s(True) - breakpoints set on all currently loaded kernels.",
+ __FUNCTION__);
+ } else if (!do_break &&
+ m_breakAllKernels) // Breakpoints won't be set on any new kernels.
+ {
+ m_breakAllKernels = false;
+
+ if (log)
+ log->Printf("%s(False) - breakpoints no longer automatically set.",
+ __FUNCTION__);
+ }
+}
+
+// Given the name of a kernel this function creates a breakpoint using our own
+// breakpoint resolver, and returns the Breakpoint shared pointer.
+BreakpointSP
+RenderScriptRuntime::CreateKernelBreakpoint(ConstString name) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS));
+
+ if (!m_filtersp) {
+ if (log)
+ log->Printf("%s - error, no breakpoint search filter set.", __FUNCTION__);
+ return nullptr;
+ }
+
+ BreakpointResolverSP resolver_sp(new RSBreakpointResolver(nullptr, name));
+ Target &target = GetProcess()->GetTarget();
+ BreakpointSP bp = target.CreateBreakpoint(
+ m_filtersp, resolver_sp, false, false, false);
+
+ // Give RS breakpoints a specific name, so the user can manipulate them as a
+ // group.
+ Status err;
+ target.AddNameToBreakpoint(bp, "RenderScriptKernel", err);
+ if (err.Fail() && log)
+ if (log)
+ log->Printf("%s - error setting break name, '%s'.", __FUNCTION__,
+ err.AsCString());
+
+ return bp;
+}
+
+BreakpointSP
+RenderScriptRuntime::CreateReductionBreakpoint(ConstString name,
+ int kernel_types) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS));
+
+ if (!m_filtersp) {
+ if (log)
+ log->Printf("%s - error, no breakpoint search filter set.", __FUNCTION__);
+ return nullptr;
+ }
+
+ BreakpointResolverSP resolver_sp(new RSReduceBreakpointResolver(
+ nullptr, name, &m_rsmodules, kernel_types));
+ Target &target = GetProcess()->GetTarget();
+ BreakpointSP bp = target.CreateBreakpoint(
+ m_filtersp, resolver_sp, false, false, false);
+
+ // Give RS breakpoints a specific name, so the user can manipulate them as a
+ // group.
+ Status err;
+ target.AddNameToBreakpoint(bp, "RenderScriptReduction", err);
+ if (err.Fail() && log)
+ log->Printf("%s - error setting break name, '%s'.", __FUNCTION__,
+ err.AsCString());
+
+ return bp;
+}
+
+// Given an expression for a variable this function tries to calculate the
+// variable's value. If this is possible it returns true and sets the uint64_t
+// parameter to the variables unsigned value. Otherwise function returns false.
+bool RenderScriptRuntime::GetFrameVarAsUnsigned(const StackFrameSP frame_sp,
+ const char *var_name,
+ uint64_t &val) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+ Status err;
+ VariableSP var_sp;
+
+ // Find variable in stack frame
+ ValueObjectSP value_sp(frame_sp->GetValueForVariableExpressionPath(
+ var_name, eNoDynamicValues,
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
+ var_sp, err));
+ if (!err.Success()) {
+ if (log)
+ log->Printf("%s - error, couldn't find '%s' in frame", __FUNCTION__,
+ var_name);
+ return false;
+ }
+
+ // Find the uint32_t value for the variable
+ bool success = false;
+ val = value_sp->GetValueAsUnsigned(0, &success);
+ if (!success) {
+ if (log)
+ log->Printf("%s - error, couldn't parse '%s' as an uint32_t.",
+ __FUNCTION__, var_name);
+ return false;
+ }
+
+ return true;
+}
+
+// Function attempts to find the current coordinate of a kernel invocation by
+// investigating the values of frame variables in the .expand function. These
+// coordinates are returned via the coord array reference parameter. Returns
+// true if the coordinates could be found, and false otherwise.
+bool RenderScriptRuntime::GetKernelCoordinate(RSCoordinate &coord,
+ Thread *thread_ptr) {
+ static const char *const x_expr = "rsIndex";
+ static const char *const y_expr = "p->current.y";
+ static const char *const z_expr = "p->current.z";
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!thread_ptr) {
+ if (log)
+ log->Printf("%s - Error, No thread pointer", __FUNCTION__);
+
+ return false;
+ }
+
+ // Walk the call stack looking for a function whose name has the suffix
+ // '.expand' and contains the variables we're looking for.
+ for (uint32_t i = 0; i < thread_ptr->GetStackFrameCount(); ++i) {
+ if (!thread_ptr->SetSelectedFrameByIndex(i))
+ continue;
+
+ StackFrameSP frame_sp = thread_ptr->GetSelectedFrame();
+ if (!frame_sp)
+ continue;
+
+ // Find the function name
+ const SymbolContext sym_ctx =
+ frame_sp->GetSymbolContext(eSymbolContextFunction);
+ const ConstString func_name = sym_ctx.GetFunctionName();
+ if (!func_name)
+ continue;
+
+ if (log)
+ log->Printf("%s - Inspecting function '%s'", __FUNCTION__,
+ func_name.GetCString());
+
+ // Check if function name has .expand suffix
+ if (!func_name.GetStringRef().endswith(".expand"))
+ continue;
+
+ if (log)
+ log->Printf("%s - Found .expand function '%s'", __FUNCTION__,
+ func_name.GetCString());
+
+ // Get values for variables in .expand frame that tell us the current
+ // kernel invocation
+ uint64_t x, y, z;
+ bool found = GetFrameVarAsUnsigned(frame_sp, x_expr, x) &&
+ GetFrameVarAsUnsigned(frame_sp, y_expr, y) &&
+ GetFrameVarAsUnsigned(frame_sp, z_expr, z);
+
+ if (found) {
+ // The RenderScript runtime uses uint32_t for these vars. If they're not
+ // within bounds, our frame parsing is garbage
+ assert(x <= UINT32_MAX && y <= UINT32_MAX && z <= UINT32_MAX);
+ coord.x = (uint32_t)x;
+ coord.y = (uint32_t)y;
+ coord.z = (uint32_t)z;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Callback when a kernel breakpoint hits and we're looking for a specific
+// coordinate. Baton parameter contains a pointer to the target coordinate we
+// want to break on. Function then checks the .expand frame for the current
+// coordinate and breaks to user if it matches. Parameter 'break_id' is the id
+// of the Breakpoint which made the callback. Parameter 'break_loc_id' is the
+// id for the BreakpointLocation which was hit, a single logical breakpoint can
+// have multiple addresses.
+bool RenderScriptRuntime::KernelBreakpointHit(void *baton,
+ StoppointCallbackContext *ctx,
+ user_id_t break_id,
+ user_id_t break_loc_id) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS));
+
+ assert(baton &&
+ "Error: null baton in conditional kernel breakpoint callback");
+
+ // Coordinate we want to stop on
+ RSCoordinate target_coord = *static_cast<RSCoordinate *>(baton);
+
+ if (log)
+ log->Printf("%s - Break ID %" PRIu64 ", " FMT_COORD, __FUNCTION__, break_id,
+ target_coord.x, target_coord.y, target_coord.z);
+
+ // Select current thread
+ ExecutionContext context(ctx->exe_ctx_ref);
+ Thread *thread_ptr = context.GetThreadPtr();
+ assert(thread_ptr && "Null thread pointer");
+
+ // Find current kernel invocation from .expand frame variables
+ RSCoordinate current_coord{};
+ if (!GetKernelCoordinate(current_coord, thread_ptr)) {
+ if (log)
+ log->Printf("%s - Error, couldn't select .expand stack frame",
+ __FUNCTION__);
+ return false;
+ }
+
+ if (log)
+ log->Printf("%s - " FMT_COORD, __FUNCTION__, current_coord.x,
+ current_coord.y, current_coord.z);
+
+ // Check if the current kernel invocation coordinate matches our target
+ // coordinate
+ if (target_coord == current_coord) {
+ if (log)
+ log->Printf("%s, BREAKING " FMT_COORD, __FUNCTION__, current_coord.x,
+ current_coord.y, current_coord.z);
+
+ BreakpointSP breakpoint_sp =
+ context.GetTargetPtr()->GetBreakpointByID(break_id);
+ assert(breakpoint_sp != nullptr &&
+ "Error: Couldn't find breakpoint matching break id for callback");
+ breakpoint_sp->SetEnabled(false); // Optimise since conditional breakpoint
+ // should only be hit once.
+ return true;
+ }
+
+ // No match on coordinate
+ return false;
+}
+
+void RenderScriptRuntime::SetConditional(BreakpointSP bp, Stream &messages,
+ const RSCoordinate &coord) {
+ messages.Printf("Conditional kernel breakpoint on coordinate " FMT_COORD,
+ coord.x, coord.y, coord.z);
+ messages.EOL();
+
+ // Allocate memory for the baton, and copy over coordinate
+ RSCoordinate *baton = new RSCoordinate(coord);
+
+ // Create a callback that will be invoked every time the breakpoint is hit.
+ // The baton object passed to the handler is the target coordinate we want to
+ // break on.
+ bp->SetCallback(KernelBreakpointHit, baton, true);
+
+ // Store a shared pointer to the baton, so the memory will eventually be
+ // cleaned up after destruction
+ m_conditional_breaks[bp->GetID()] = std::unique_ptr<RSCoordinate>(baton);
+}
+
+// Tries to set a breakpoint on the start of a kernel, resolved using the
+// kernel name. Argument 'coords', represents a three dimensional coordinate
+// which can be used to specify a single kernel instance to break on. If this
+// is set then we add a callback to the breakpoint.
+bool RenderScriptRuntime::PlaceBreakpointOnKernel(TargetSP target,
+ Stream &messages,
+ const char *name,
+ const RSCoordinate *coord) {
+ if (!name)
+ return false;
+
+ InitSearchFilter(target);
+
+ ConstString kernel_name(name);
+ BreakpointSP bp = CreateKernelBreakpoint(kernel_name);
+ if (!bp)
+ return false;
+
+ // We have a conditional breakpoint on a specific coordinate
+ if (coord)
+ SetConditional(bp, messages, *coord);
+
+ bp->GetDescription(&messages, lldb::eDescriptionLevelInitial, false);
+
+ return true;
+}
+
+BreakpointSP
+RenderScriptRuntime::CreateScriptGroupBreakpoint(ConstString name,
+ bool stop_on_all) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS));
+
+ if (!m_filtersp) {
+ if (log)
+ log->Printf("%s - error, no breakpoint search filter set.", __FUNCTION__);
+ return nullptr;
+ }
+
+ BreakpointResolverSP resolver_sp(new RSScriptGroupBreakpointResolver(
+ nullptr, name, m_scriptGroups, stop_on_all));
+ Target &target = GetProcess()->GetTarget();
+ BreakpointSP bp = target.CreateBreakpoint(
+ m_filtersp, resolver_sp, false, false, false);
+ // Give RS breakpoints a specific name, so the user can manipulate them as a
+ // group.
+ Status err;
+ target.AddNameToBreakpoint(bp, name.GetCString(), err);
+ if (err.Fail() && log)
+ log->Printf("%s - error setting break name, '%s'.", __FUNCTION__,
+ err.AsCString());
+ // ask the breakpoint to resolve itself
+ bp->ResolveBreakpoint();
+ return bp;
+}
+
+bool RenderScriptRuntime::PlaceBreakpointOnScriptGroup(TargetSP target,
+ Stream &strm,
+ ConstString name,
+ bool multi) {
+ InitSearchFilter(target);
+ BreakpointSP bp = CreateScriptGroupBreakpoint(name, multi);
+ if (bp)
+ bp->GetDescription(&strm, lldb::eDescriptionLevelInitial, false);
+ return bool(bp);
+}
+
+bool RenderScriptRuntime::PlaceBreakpointOnReduction(TargetSP target,
+ Stream &messages,
+ const char *reduce_name,
+ const RSCoordinate *coord,
+ int kernel_types) {
+ if (!reduce_name)
+ return false;
+
+ InitSearchFilter(target);
+ BreakpointSP bp =
+ CreateReductionBreakpoint(ConstString(reduce_name), kernel_types);
+ if (!bp)
+ return false;
+
+ if (coord)
+ SetConditional(bp, messages, *coord);
+
+ bp->GetDescription(&messages, lldb::eDescriptionLevelInitial, false);
+
+ return true;
+}
+
+void RenderScriptRuntime::DumpModules(Stream &strm) const {
+ strm.Printf("RenderScript Modules:");
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &module : m_rsmodules) {
+ module->Dump(strm);
+ }
+ strm.IndentLess();
+}
+
+RenderScriptRuntime::ScriptDetails *
+RenderScriptRuntime::LookUpScript(addr_t address, bool create) {
+ for (const auto &s : m_scripts) {
+ if (s->script.isValid())
+ if (*s->script == address)
+ return s.get();
+ }
+ if (create) {
+ std::unique_ptr<ScriptDetails> s(new ScriptDetails);
+ s->script = address;
+ m_scripts.push_back(std::move(s));
+ return m_scripts.back().get();
+ }
+ return nullptr;
+}
+
+RenderScriptRuntime::AllocationDetails *
+RenderScriptRuntime::LookUpAllocation(addr_t address) {
+ for (const auto &a : m_allocations) {
+ if (a->address.isValid())
+ if (*a->address == address)
+ return a.get();
+ }
+ return nullptr;
+}
+
+RenderScriptRuntime::AllocationDetails *
+RenderScriptRuntime::CreateAllocation(addr_t address) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ // Remove any previous allocation which contains the same address
+ auto it = m_allocations.begin();
+ while (it != m_allocations.end()) {
+ if (*((*it)->address) == address) {
+ if (log)
+ log->Printf("%s - Removing allocation id: %d, address: 0x%" PRIx64,
+ __FUNCTION__, (*it)->id, address);
+
+ it = m_allocations.erase(it);
+ } else {
+ it++;
+ }
+ }
+
+ std::unique_ptr<AllocationDetails> a(new AllocationDetails);
+ a->address = address;
+ m_allocations.push_back(std::move(a));
+ return m_allocations.back().get();
+}
+
+bool RenderScriptRuntime::ResolveKernelName(lldb::addr_t kernel_addr,
+ ConstString &name) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+
+ Target &target = GetProcess()->GetTarget();
+ Address resolved;
+ // RenderScript module
+ if (!target.GetSectionLoadList().ResolveLoadAddress(kernel_addr, resolved)) {
+ if (log)
+ log->Printf("%s: unable to resolve 0x%" PRIx64 " to a loaded symbol",
+ __FUNCTION__, kernel_addr);
+ return false;
+ }
+
+ Symbol *sym = resolved.CalculateSymbolContextSymbol();
+ if (!sym)
+ return false;
+
+ name = sym->GetName();
+ assert(IsRenderScriptModule(resolved.CalculateSymbolContextModule()));
+ if (log)
+ log->Printf("%s: 0x%" PRIx64 " resolved to the symbol '%s'", __FUNCTION__,
+ kernel_addr, name.GetCString());
+ return true;
+}
+
+void RSModuleDescriptor::Dump(Stream &strm) const {
+ int indent = strm.GetIndentLevel();
+
+ strm.Indent();
+ m_module->GetFileSpec().Dump(&strm);
+ strm.Indent(m_module->GetNumCompileUnits() ? "Debug info loaded."
+ : "Debug info does not exist.");
+ strm.EOL();
+ strm.IndentMore();
+
+ strm.Indent();
+ strm.Printf("Globals: %" PRIu64, static_cast<uint64_t>(m_globals.size()));
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &global : m_globals) {
+ global.Dump(strm);
+ }
+ strm.IndentLess();
+
+ strm.Indent();
+ strm.Printf("Kernels: %" PRIu64, static_cast<uint64_t>(m_kernels.size()));
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &kernel : m_kernels) {
+ kernel.Dump(strm);
+ }
+ strm.IndentLess();
+
+ strm.Indent();
+ strm.Printf("Pragmas: %" PRIu64, static_cast<uint64_t>(m_pragmas.size()));
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &key_val : m_pragmas) {
+ strm.Indent();
+ strm.Printf("%s: %s", key_val.first.c_str(), key_val.second.c_str());
+ strm.EOL();
+ }
+ strm.IndentLess();
+
+ strm.Indent();
+ strm.Printf("Reductions: %" PRIu64,
+ static_cast<uint64_t>(m_reductions.size()));
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &reduction : m_reductions) {
+ reduction.Dump(strm);
+ }
+
+ strm.SetIndentLevel(indent);
+}
+
+void RSGlobalDescriptor::Dump(Stream &strm) const {
+ strm.Indent(m_name.AsCString());
+ VariableList var_list;
+ m_module->m_module->FindGlobalVariables(m_name, nullptr, 1U, var_list);
+ if (var_list.GetSize() == 1) {
+ auto var = var_list.GetVariableAtIndex(0);
+ auto type = var->GetType();
+ if (type) {
+ strm.Printf(" - ");
+ type->DumpTypeName(&strm);
+ } else {
+ strm.Printf(" - Unknown Type");
+ }
+ } else {
+ strm.Printf(" - variable identified, but not found in binary");
+ const Symbol *s = m_module->m_module->FindFirstSymbolWithNameAndType(
+ m_name, eSymbolTypeData);
+ if (s) {
+ strm.Printf(" (symbol exists) ");
+ }
+ }
+
+ strm.EOL();
+}
+
+void RSKernelDescriptor::Dump(Stream &strm) const {
+ strm.Indent(m_name.AsCString());
+ strm.EOL();
+}
+
+void RSReductionDescriptor::Dump(lldb_private::Stream &stream) const {
+ stream.Indent(m_reduce_name.AsCString());
+ stream.IndentMore();
+ stream.EOL();
+ stream.Indent();
+ stream.Printf("accumulator: %s", m_accum_name.AsCString());
+ stream.EOL();
+ stream.Indent();
+ stream.Printf("initializer: %s", m_init_name.AsCString());
+ stream.EOL();
+ stream.Indent();
+ stream.Printf("combiner: %s", m_comb_name.AsCString());
+ stream.EOL();
+ stream.Indent();
+ stream.Printf("outconverter: %s", m_outc_name.AsCString());
+ stream.EOL();
+ // XXX This is currently unspecified by RenderScript, and unused
+ // stream.Indent();
+ // stream.Printf("halter: '%s'", m_init_name.AsCString());
+ // stream.EOL();
+ stream.IndentLess();
+}
+
+class CommandObjectRenderScriptRuntimeModuleDump : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeModuleDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript module dump",
+ "Dumps renderscript specific information for all modules.",
+ "renderscript module dump",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeModuleDump() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ runtime->DumpModules(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeModule : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeModule(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript module",
+ "Commands that deal with RenderScript modules.",
+ nullptr) {
+ LoadSubCommand(
+ "dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeModuleDump(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeModule() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeKernelList : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeKernelList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript kernel list",
+ "Lists renderscript kernel names and associated script resources.",
+ "renderscript kernel list",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeKernelList() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ runtime->DumpKernels(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+static constexpr OptionDefinition g_renderscript_reduction_bp_set_options[] = {
+ {LLDB_OPT_SET_1, false, "function-role", 't',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOneLiner,
+ "Break on a comma separated set of reduction kernel types "
+ "(accumulator,outcoverter,combiner,initializer"},
+ {LLDB_OPT_SET_1, false, "coordinate", 'c', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeValue,
+ "Set a breakpoint on a single invocation of the kernel with specified "
+ "coordinate.\n"
+ "Coordinate takes the form 'x[,y][,z] where x,y,z are positive "
+ "integers representing kernel dimensions. "
+ "Any unset dimensions will be defaulted to zero."}};
+
+class CommandObjectRenderScriptRuntimeReductionBreakpointSet
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeReductionBreakpointSet(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript reduction breakpoint set",
+ "Set a breakpoint on named RenderScript general reductions",
+ "renderscript reduction breakpoint set <kernel_name> [-t "
+ "<reduction_kernel_type,...>]",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options(){};
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(),
+ m_kernel_types(RSReduceBreakpointResolver::eKernelTypeAll) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *exe_ctx) override {
+ Status err;
+ StreamString err_str;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 't':
+ if (!ParseReductionTypes(option_arg, err_str))
+ err.SetErrorStringWithFormat(
+ "Unable to deduce reduction types for %s: %s",
+ option_arg.str().c_str(), err_str.GetData());
+ break;
+ case 'c': {
+ auto coord = RSCoordinate{};
+ if (!ParseCoordinate(option_arg, coord))
+ err.SetErrorStringWithFormat("unable to parse coordinate for %s",
+ option_arg.str().c_str());
+ else {
+ m_have_coord = true;
+ m_coord = coord;
+ }
+ break;
+ }
+ default:
+ err.SetErrorStringWithFormat("Invalid option '-%c'", short_option);
+ }
+ return err;
+ }
+
+ void OptionParsingStarting(ExecutionContext *exe_ctx) override {
+ m_have_coord = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_renderscript_reduction_bp_set_options);
+ }
+
+ bool ParseReductionTypes(llvm::StringRef option_val,
+ StreamString &err_str) {
+ m_kernel_types = RSReduceBreakpointResolver::eKernelTypeNone;
+ const auto reduce_name_to_type = [](llvm::StringRef name) -> int {
+ return llvm::StringSwitch<int>(name)
+ .Case("accumulator", RSReduceBreakpointResolver::eKernelTypeAccum)
+ .Case("initializer", RSReduceBreakpointResolver::eKernelTypeInit)
+ .Case("outconverter", RSReduceBreakpointResolver::eKernelTypeOutC)
+ .Case("combiner", RSReduceBreakpointResolver::eKernelTypeComb)
+ .Case("all", RSReduceBreakpointResolver::eKernelTypeAll)
+ // Currently not exposed by the runtime
+ // .Case("halter", RSReduceBreakpointResolver::eKernelTypeHalter)
+ .Default(0);
+ };
+
+ // Matching a comma separated list of known words is fairly
+ // straightforward with PCRE, but we're using ERE, so we end up with a
+ // little ugliness...
+ RegularExpression::Match match(/* max_matches */ 5);
+ RegularExpression match_type_list(
+ llvm::StringRef("^([[:alpha:]]+)(,[[:alpha:]]+){0,4}$"));
+
+ assert(match_type_list.IsValid());
+
+ if (!match_type_list.Execute(option_val, &match)) {
+ err_str.PutCString(
+ "a comma-separated list of kernel types is required");
+ return false;
+ }
+
+ // splitting on commas is much easier with llvm::StringRef than regex
+ llvm::SmallVector<llvm::StringRef, 5> type_names;
+ llvm::StringRef(option_val).split(type_names, ',');
+
+ for (const auto &name : type_names) {
+ const int type = reduce_name_to_type(name);
+ if (!type) {
+ err_str.Printf("unknown kernel type name %s", name.str().c_str());
+ return false;
+ }
+ m_kernel_types |= type;
+ }
+
+ return true;
+ }
+
+ int m_kernel_types;
+ llvm::StringRef m_reduce_name;
+ RSCoordinate m_coord;
+ bool m_have_coord;
+ };
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc < 1) {
+ result.AppendErrorWithFormat("'%s' takes 1 argument of reduction name, "
+ "and an optional kernel type list",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ auto &outstream = result.GetOutputStream();
+ auto name = command.GetArgumentAtIndex(0);
+ auto &target = m_exe_ctx.GetTargetSP();
+ auto coord = m_options.m_have_coord ? &m_options.m_coord : nullptr;
+ if (!runtime->PlaceBreakpointOnReduction(target, outstream, name, coord,
+ m_options.m_kernel_types)) {
+ result.SetStatus(eReturnStatusFailed);
+ result.AppendError("Error: unable to place breakpoint on reduction");
+ return false;
+ }
+ result.AppendMessage("Breakpoint(s) created");
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+static constexpr OptionDefinition g_renderscript_kernel_bp_set_options[] = {
+ {LLDB_OPT_SET_1, false, "coordinate", 'c', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeValue,
+ "Set a breakpoint on a single invocation of the kernel with specified "
+ "coordinate.\n"
+ "Coordinate takes the form 'x[,y][,z] where x,y,z are positive "
+ "integers representing kernel dimensions. "
+ "Any unset dimensions will be defaulted to zero."}};
+
+class CommandObjectRenderScriptRuntimeKernelBreakpointSet
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeKernelBreakpointSet(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript kernel breakpoint set",
+ "Sets a breakpoint on a renderscript kernel.",
+ "renderscript kernel breakpoint set <kernel_name> [-c x,y,z]",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options() {}
+
+ ~CommandObjectRenderScriptRuntimeKernelBreakpointSet() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *exe_ctx) override {
+ Status err;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c': {
+ auto coord = RSCoordinate{};
+ if (!ParseCoordinate(option_arg, coord))
+ err.SetErrorStringWithFormat(
+ "Couldn't parse coordinate '%s', should be in format 'x,y,z'.",
+ option_arg.str().c_str());
+ else {
+ m_have_coord = true;
+ m_coord = coord;
+ }
+ break;
+ }
+ default:
+ err.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+ return err;
+ }
+
+ void OptionParsingStarting(ExecutionContext *exe_ctx) override {
+ m_have_coord = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_renderscript_kernel_bp_set_options);
+ }
+
+ RSCoordinate m_coord;
+ bool m_have_coord;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc < 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes 1 argument of kernel name, and an optional coordinate.",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ auto &outstream = result.GetOutputStream();
+ auto &target = m_exe_ctx.GetTargetSP();
+ auto name = command.GetArgumentAtIndex(0);
+ auto coord = m_options.m_have_coord ? &m_options.m_coord : nullptr;
+ if (!runtime->PlaceBreakpointOnKernel(target, outstream, name, coord)) {
+ result.SetStatus(eReturnStatusFailed);
+ result.AppendErrorWithFormat(
+ "Error: unable to set breakpoint on kernel '%s'", name);
+ return false;
+ }
+
+ result.AppendMessage("Breakpoint(s) created");
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+class CommandObjectRenderScriptRuntimeKernelBreakpointAll
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeKernelBreakpointAll(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript kernel breakpoint all",
+ "Automatically sets a breakpoint on all renderscript kernels that "
+ "are or will be loaded.\n"
+ "Disabling option means breakpoints will no longer be set on any "
+ "kernels loaded in the future, "
+ "but does not remove currently set breakpoints.",
+ "renderscript kernel breakpoint all <enable/disable>",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectRenderScriptRuntimeKernelBreakpointAll() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes 1 argument of 'enable' or 'disable'", m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ bool do_break = false;
+ const char *argument = command.GetArgumentAtIndex(0);
+ if (strcmp(argument, "enable") == 0) {
+ do_break = true;
+ result.AppendMessage("Breakpoints will be set on all kernels.");
+ } else if (strcmp(argument, "disable") == 0) {
+ do_break = false;
+ result.AppendMessage("Breakpoints will not be set on any new kernels.");
+ } else {
+ result.AppendErrorWithFormat(
+ "Argument must be either 'enable' or 'disable'");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ runtime->SetBreakAllKernels(do_break, m_exe_ctx.GetTargetSP());
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeReductionBreakpoint
+ : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeReductionBreakpoint(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript reduction breakpoint",
+ "Commands that manipulate breakpoints on "
+ "renderscript general reductions.",
+ nullptr) {
+ LoadSubCommand(
+ "set", CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeReductionBreakpointSet(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeReductionBreakpoint() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeKernelCoordinate
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeKernelCoordinate(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript kernel coordinate",
+ "Shows the (x,y,z) coordinate of the current kernel invocation.",
+ "renderscript kernel coordinate",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectRenderScriptRuntimeKernelCoordinate() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RSCoordinate coord{};
+ bool success = RenderScriptRuntime::GetKernelCoordinate(
+ coord, m_exe_ctx.GetThreadPtr());
+ Stream &stream = result.GetOutputStream();
+
+ if (success) {
+ stream.Printf("Coordinate: " FMT_COORD, coord.x, coord.y, coord.z);
+ stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ stream.Printf("Error: Coordinate could not be found.");
+ stream.EOL();
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeKernelBreakpoint
+ : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeKernelBreakpoint(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "renderscript kernel",
+ "Commands that generate breakpoints on renderscript kernels.",
+ nullptr) {
+ LoadSubCommand(
+ "set",
+ CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpointSet(
+ interpreter)));
+ LoadSubCommand(
+ "all",
+ CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpointAll(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeKernelBreakpoint() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeKernel : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeKernel(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript kernel",
+ "Commands that deal with RenderScript kernels.",
+ nullptr) {
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelList(
+ interpreter)));
+ LoadSubCommand(
+ "coordinate",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeKernelCoordinate(interpreter)));
+ LoadSubCommand(
+ "breakpoint",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeKernelBreakpoint(interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeKernel() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeContextDump : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeContextDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript context dump",
+ "Dumps renderscript context information.",
+ "renderscript context dump",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeContextDump() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ runtime->DumpContexts(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+static constexpr OptionDefinition g_renderscript_runtime_alloc_dump_options[] = {
+ {LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeFilename,
+ "Print results to specified file instead of command line."}};
+
+class CommandObjectRenderScriptRuntimeContext : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeContext(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript context",
+ "Commands that deal with RenderScript contexts.",
+ nullptr) {
+ LoadSubCommand(
+ "dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeContextDump(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeContext() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeAllocationDump
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeAllocationDump(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript allocation dump",
+ "Displays the contents of a particular allocation",
+ "renderscript allocation dump <ID>",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched),
+ m_options() {}
+
+ ~CommandObjectRenderScriptRuntimeAllocationDump() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *exe_ctx) override {
+ Status err;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_outfile.SetFile(option_arg, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(m_outfile);
+ if (FileSystem::Instance().Exists(m_outfile)) {
+ m_outfile.Clear();
+ err.SetErrorStringWithFormat("file already exists: '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+ default:
+ err.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+ return err;
+ }
+
+ void OptionParsingStarting(ExecutionContext *exe_ctx) override {
+ m_outfile.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_renderscript_runtime_alloc_dump_options);
+ }
+
+ FileSpec m_outfile;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc < 1) {
+ result.AppendErrorWithFormat("'%s' takes 1 argument, an allocation ID. "
+ "As well as an optional -f argument",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ const char *id_cstr = command.GetArgumentAtIndex(0);
+ bool success = false;
+ const uint32_t id =
+ StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &success);
+ if (!success) {
+ result.AppendErrorWithFormat("invalid allocation id argument '%s'",
+ id_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Stream *output_strm = nullptr;
+ StreamFile outfile_stream;
+ const FileSpec &outfile_spec =
+ m_options.m_outfile; // Dump allocation to file instead
+ if (outfile_spec) {
+ // Open output file
+ std::string path = outfile_spec.GetPath();
+ auto error = FileSystem::Instance().Open(
+ outfile_stream.GetFile(), outfile_spec,
+ File::eOpenOptionWrite | File::eOpenOptionCanCreate);
+ if (error.Success()) {
+ output_strm = &outfile_stream;
+ result.GetOutputStream().Printf("Results written to '%s'",
+ path.c_str());
+ result.GetOutputStream().EOL();
+ } else {
+ result.AppendErrorWithFormat("Couldn't open file '%s'", path.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else
+ output_strm = &result.GetOutputStream();
+
+ assert(output_strm != nullptr);
+ bool dumped =
+ runtime->DumpAllocation(*output_strm, m_exe_ctx.GetFramePtr(), id);
+
+ if (dumped)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+
+ return true;
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+static constexpr OptionDefinition g_renderscript_runtime_alloc_list_options[] = {
+ {LLDB_OPT_SET_1, false, "id", 'i', OptionParser::eRequiredArgument, nullptr,
+ {}, 0, eArgTypeIndex,
+ "Only show details of a single allocation with specified id."}};
+
+class CommandObjectRenderScriptRuntimeAllocationList
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeAllocationList(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript allocation list",
+ "List renderscript allocations and their information.",
+ "renderscript allocation list",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched),
+ m_options() {}
+
+ ~CommandObjectRenderScriptRuntimeAllocationList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_id(0) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *exe_ctx) override {
+ Status err;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'i':
+ if (option_arg.getAsInteger(0, m_id))
+ err.SetErrorStringWithFormat("invalid integer value for option '%c'",
+ short_option);
+ break;
+ default:
+ err.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+ return err;
+ }
+
+ void OptionParsingStarting(ExecutionContext *exe_ctx) override { m_id = 0; }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_renderscript_runtime_alloc_list_options);
+ }
+
+ uint32_t m_id;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ runtime->ListAllocations(result.GetOutputStream(), m_exe_ctx.GetFramePtr(),
+ m_options.m_id);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+class CommandObjectRenderScriptRuntimeAllocationLoad
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeAllocationLoad(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript allocation load",
+ "Loads renderscript allocation contents from a file.",
+ "renderscript allocation load <ID> <filename>",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeAllocationLoad() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc != 2) {
+ result.AppendErrorWithFormat(
+ "'%s' takes 2 arguments, an allocation ID and filename to read from.",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ const char *id_cstr = command.GetArgumentAtIndex(0);
+ bool success = false;
+ const uint32_t id =
+ StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &success);
+ if (!success) {
+ result.AppendErrorWithFormat("invalid allocation id argument '%s'",
+ id_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *path = command.GetArgumentAtIndex(1);
+ bool loaded = runtime->LoadAllocation(result.GetOutputStream(), id, path,
+ m_exe_ctx.GetFramePtr());
+
+ if (loaded)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeAllocationSave
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeAllocationSave(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript allocation save",
+ "Write renderscript allocation contents to a file.",
+ "renderscript allocation save <ID> <filename>",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeAllocationSave() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc != 2) {
+ result.AppendErrorWithFormat(
+ "'%s' takes 2 arguments, an allocation ID and filename to read from.",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ const char *id_cstr = command.GetArgumentAtIndex(0);
+ bool success = false;
+ const uint32_t id =
+ StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &success);
+ if (!success) {
+ result.AppendErrorWithFormat("invalid allocation id argument '%s'",
+ id_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *path = command.GetArgumentAtIndex(1);
+ bool saved = runtime->SaveAllocation(result.GetOutputStream(), id, path,
+ m_exe_ctx.GetFramePtr());
+
+ if (saved)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeAllocationRefresh
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeAllocationRefresh(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript allocation refresh",
+ "Recomputes the details of all allocations.",
+ "renderscript allocation refresh",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeAllocationRefresh() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ bool success = runtime->RecomputeAllAllocations(result.GetOutputStream(),
+ m_exe_ctx.GetFramePtr());
+
+ if (success) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ } else {
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+};
+
+class CommandObjectRenderScriptRuntimeAllocation
+ : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeAllocation(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "renderscript allocation",
+ "Commands that deal with RenderScript allocations.", nullptr) {
+ LoadSubCommand(
+ "list",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeAllocationList(interpreter)));
+ LoadSubCommand(
+ "dump",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeAllocationDump(interpreter)));
+ LoadSubCommand(
+ "save",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeAllocationSave(interpreter)));
+ LoadSubCommand(
+ "load",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeAllocationLoad(interpreter)));
+ LoadSubCommand(
+ "refresh",
+ CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocationRefresh(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeAllocation() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeStatus : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeStatus(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript status",
+ "Displays current RenderScript runtime status.",
+ "renderscript status",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeStatus() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ runtime->DumpStatus(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeReduction
+ : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeReduction(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript reduction",
+ "Commands that handle general reduction kernels",
+ nullptr) {
+ LoadSubCommand(
+ "breakpoint",
+ CommandObjectSP(new CommandObjectRenderScriptRuntimeReductionBreakpoint(
+ interpreter)));
+ }
+ ~CommandObjectRenderScriptRuntimeReduction() override = default;
+};
+
+class CommandObjectRenderScriptRuntime : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntime(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "renderscript",
+ "Commands for operating on the RenderScript runtime.",
+ "renderscript <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "module", CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeModule(interpreter)));
+ LoadSubCommand(
+ "status", CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeStatus(interpreter)));
+ LoadSubCommand(
+ "kernel", CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeKernel(interpreter)));
+ LoadSubCommand("context",
+ CommandObjectSP(new CommandObjectRenderScriptRuntimeContext(
+ interpreter)));
+ LoadSubCommand(
+ "allocation",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeAllocation(interpreter)));
+ LoadSubCommand("scriptgroup",
+ NewCommandObjectRenderScriptScriptGroup(interpreter));
+ LoadSubCommand(
+ "reduction",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeReduction(interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntime() override = default;
+};
+
+void RenderScriptRuntime::Initiate() { assert(!m_initiated); }
+
+RenderScriptRuntime::RenderScriptRuntime(Process *process)
+ : lldb_private::CPPLanguageRuntime(process), m_initiated(false),
+ m_debuggerPresentFlagged(false), m_breakAllKernels(false),
+ m_ir_passes(nullptr) {
+ ModulesDidLoad(process->GetTarget().GetImages());
+}
+
+lldb::CommandObjectSP RenderScriptRuntime::GetCommandObject(
+ lldb_private::CommandInterpreter &interpreter) {
+ return CommandObjectSP(new CommandObjectRenderScriptRuntime(interpreter));
+}
+
+RenderScriptRuntime::~RenderScriptRuntime() = default;
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h
new file mode 100644
index 000000000000..3923221d4302
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h
@@ -0,0 +1,587 @@
+//===-- RenderScriptRuntime.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RenderScriptRuntime_h_
+#define liblldb_RenderScriptRuntime_h_
+
+#include <array>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Expression/LLVMUserExpression.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
+
+namespace lldb_private {
+namespace lldb_renderscript {
+
+typedef uint32_t RSSlot;
+class RSModuleDescriptor;
+struct RSGlobalDescriptor;
+struct RSKernelDescriptor;
+struct RSReductionDescriptor;
+struct RSScriptGroupDescriptor;
+
+typedef std::shared_ptr<RSModuleDescriptor> RSModuleDescriptorSP;
+typedef std::shared_ptr<RSGlobalDescriptor> RSGlobalDescriptorSP;
+typedef std::shared_ptr<RSKernelDescriptor> RSKernelDescriptorSP;
+typedef std::shared_ptr<RSScriptGroupDescriptor> RSScriptGroupDescriptorSP;
+
+struct RSCoordinate {
+ uint32_t x, y, z;
+
+ RSCoordinate() : x(), y(), z(){};
+
+ bool operator==(const lldb_renderscript::RSCoordinate &rhs) {
+ return x == rhs.x && y == rhs.y && z == rhs.z;
+ }
+};
+
+// Breakpoint Resolvers decide where a breakpoint is placed, so having our own
+// allows us to limit the search scope to RS kernel modules. As well as check
+// for .expand kernels as a fallback.
+class RSBreakpointResolver : public BreakpointResolver {
+public:
+ RSBreakpointResolver(Breakpoint *bp, ConstString name)
+ : BreakpointResolver(bp, BreakpointResolver::NameResolver),
+ m_kernel_name(name) {}
+
+ void GetDescription(Stream *strm) override {
+ if (strm)
+ strm->Printf("RenderScript kernel breakpoint for '%s'",
+ m_kernel_name.AsCString());
+ }
+
+ void Dump(Stream *s) const override {}
+
+ Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) override;
+
+ lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
+
+ lldb::BreakpointResolverSP
+ CopyForBreakpoint(Breakpoint &breakpoint) override {
+ lldb::BreakpointResolverSP ret_sp(
+ new RSBreakpointResolver(&breakpoint, m_kernel_name));
+ return ret_sp;
+ }
+
+protected:
+ ConstString m_kernel_name;
+};
+
+class RSReduceBreakpointResolver : public BreakpointResolver {
+public:
+ enum ReduceKernelTypeFlags {
+ eKernelTypeAll = ~(0),
+ eKernelTypeNone = 0,
+ eKernelTypeAccum = (1 << 0),
+ eKernelTypeInit = (1 << 1),
+ eKernelTypeComb = (1 << 2),
+ eKernelTypeOutC = (1 << 3),
+ eKernelTypeHalter = (1 << 4)
+ };
+
+ RSReduceBreakpointResolver(
+ Breakpoint *breakpoint, ConstString reduce_name,
+ std::vector<lldb_renderscript::RSModuleDescriptorSP> *rs_modules,
+ int kernel_types = eKernelTypeAll)
+ : BreakpointResolver(breakpoint, BreakpointResolver::NameResolver),
+ m_reduce_name(reduce_name), m_rsmodules(rs_modules),
+ m_kernel_types(kernel_types) {
+ // The reduce breakpoint resolver handles adding breakpoints for named
+ // reductions.
+ // Breakpoints will be resolved for all constituent kernels in the named
+ // reduction
+ }
+
+ void GetDescription(Stream *strm) override {
+ if (strm)
+ strm->Printf("RenderScript reduce breakpoint for '%s'",
+ m_reduce_name.AsCString());
+ }
+
+ void Dump(Stream *s) const override {}
+
+ Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) override;
+
+ lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
+
+ lldb::BreakpointResolverSP
+ CopyForBreakpoint(Breakpoint &breakpoint) override {
+ lldb::BreakpointResolverSP ret_sp(new RSReduceBreakpointResolver(
+ &breakpoint, m_reduce_name, m_rsmodules, m_kernel_types));
+ return ret_sp;
+ }
+
+private:
+ ConstString m_reduce_name; // The name of the reduction
+ std::vector<lldb_renderscript::RSModuleDescriptorSP> *m_rsmodules;
+ int m_kernel_types;
+};
+
+struct RSKernelDescriptor {
+public:
+ RSKernelDescriptor(const RSModuleDescriptor *module, llvm::StringRef name,
+ uint32_t slot)
+ : m_module(module), m_name(name), m_slot(slot) {}
+
+ void Dump(Stream &strm) const;
+
+ const RSModuleDescriptor *m_module;
+ ConstString m_name;
+ RSSlot m_slot;
+};
+
+struct RSGlobalDescriptor {
+public:
+ RSGlobalDescriptor(const RSModuleDescriptor *module, llvm::StringRef name)
+ : m_module(module), m_name(name) {}
+
+ void Dump(Stream &strm) const;
+
+ const RSModuleDescriptor *m_module;
+ ConstString m_name;
+};
+
+struct RSReductionDescriptor {
+ RSReductionDescriptor(const RSModuleDescriptor *module, uint32_t sig,
+ uint32_t accum_data_size, llvm::StringRef name,
+ llvm::StringRef init_name, llvm::StringRef accum_name,
+ llvm::StringRef comb_name, llvm::StringRef outc_name,
+ llvm::StringRef halter_name = ".")
+ : m_module(module), m_reduce_name(name), m_init_name(init_name),
+ m_accum_name(accum_name), m_comb_name(comb_name),
+ m_outc_name(outc_name), m_halter_name(halter_name) {
+ // TODO Check whether the combiner is an autogenerated name, and track
+ // this
+ }
+
+ void Dump(Stream &strm) const;
+
+ const RSModuleDescriptor *m_module;
+ ConstString m_reduce_name; // This is the name given to the general reduction
+ // as a group as passed to pragma
+ // reduce(m_reduce_name). There is no kernel function with this name
+ ConstString m_init_name; // The name of the initializer name. "." if no
+ // initializer given
+ ConstString m_accum_name; // The accumulator function name. "." if not given
+ ConstString m_comb_name; // The name of the combiner function. If this was not
+ // given, a name is generated by the
+ // compiler. TODO
+ ConstString m_outc_name; // The name of the outconverter
+
+ ConstString m_halter_name; // The name of the halter function. XXX This is not
+ // yet specified by the RenderScript
+ // compiler or runtime, and its semantics and existence is still under
+ // discussion by the
+ // RenderScript Contributors
+ RSSlot m_accum_sig; // metatdata signature for this reduction (bitwise mask of
+ // type information (see
+ // libbcc/include/bcinfo/MetadataExtractor.h
+ uint32_t m_accum_data_size; // Data size of the accumulator function input
+ bool m_comb_name_generated; // Was the combiner name generated by the compiler
+};
+
+class RSModuleDescriptor {
+ std::string m_slang_version;
+ std::string m_bcc_version;
+
+ bool ParseVersionInfo(llvm::StringRef *, size_t n_lines);
+
+ bool ParseExportForeachCount(llvm::StringRef *, size_t n_lines);
+
+ bool ParseExportVarCount(llvm::StringRef *, size_t n_lines);
+
+ bool ParseExportReduceCount(llvm::StringRef *, size_t n_lines);
+
+ bool ParseBuildChecksum(llvm::StringRef *, size_t n_lines);
+
+ bool ParsePragmaCount(llvm::StringRef *, size_t n_lines);
+
+public:
+ RSModuleDescriptor(const lldb::ModuleSP &module) : m_module(module) {}
+
+ ~RSModuleDescriptor() = default;
+
+ bool ParseRSInfo();
+
+ void Dump(Stream &strm) const;
+
+ void WarnIfVersionMismatch(Stream *s) const;
+
+ const lldb::ModuleSP m_module;
+ std::vector<RSKernelDescriptor> m_kernels;
+ std::vector<RSGlobalDescriptor> m_globals;
+ std::vector<RSReductionDescriptor> m_reductions;
+ std::map<std::string, std::string> m_pragmas;
+ std::string m_resname;
+};
+
+struct RSScriptGroupDescriptor {
+ struct Kernel {
+ ConstString m_name;
+ lldb::addr_t m_addr;
+ };
+ ConstString m_name;
+ std::vector<Kernel> m_kernels;
+};
+
+typedef std::vector<RSScriptGroupDescriptorSP> RSScriptGroupList;
+
+class RSScriptGroupBreakpointResolver : public BreakpointResolver {
+public:
+ RSScriptGroupBreakpointResolver(Breakpoint *bp, ConstString name,
+ const RSScriptGroupList &groups,
+ bool stop_on_all)
+ : BreakpointResolver(bp, BreakpointResolver::NameResolver),
+ m_group_name(name), m_script_groups(groups),
+ m_stop_on_all(stop_on_all) {}
+
+ void GetDescription(Stream *strm) override {
+ if (strm)
+ strm->Printf("RenderScript ScriptGroup breakpoint for '%s'",
+ m_group_name.AsCString());
+ }
+
+ void Dump(Stream *s) const override {}
+
+ Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) override;
+
+ lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
+
+ lldb::BreakpointResolverSP
+ CopyForBreakpoint(Breakpoint &breakpoint) override {
+ lldb::BreakpointResolverSP ret_sp(new RSScriptGroupBreakpointResolver(
+ &breakpoint, m_group_name, m_script_groups, m_stop_on_all));
+ return ret_sp;
+ }
+
+protected:
+ const RSScriptGroupDescriptorSP
+ FindScriptGroup(ConstString name) const {
+ for (auto sg : m_script_groups) {
+ if (ConstString::Compare(sg->m_name, name) == 0)
+ return sg;
+ }
+ return RSScriptGroupDescriptorSP();
+ }
+
+ ConstString m_group_name;
+ const RSScriptGroupList &m_script_groups;
+ bool m_stop_on_all;
+};
+} // namespace lldb_renderscript
+
+class RenderScriptRuntime : public lldb_private::CPPLanguageRuntime {
+public:
+ enum ModuleKind {
+ eModuleKindIgnored,
+ eModuleKindLibRS,
+ eModuleKindDriver,
+ eModuleKindImpl,
+ eModuleKindKernelObj
+ };
+
+ ~RenderScriptRuntime() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::LanguageRuntime *
+ CreateInstance(Process *process, lldb::LanguageType language);
+
+ static lldb::CommandObjectSP
+ GetCommandObject(CommandInterpreter &interpreter);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || CPPLanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ static bool IsRenderScriptModule(const lldb::ModuleSP &module_sp);
+
+ static ModuleKind GetModuleKind(const lldb::ModuleSP &module_sp);
+
+ static void ModulesDidLoad(const lldb::ProcessSP &process_sp,
+ const ModuleList &module_list);
+
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) override;
+
+ bool CouldHaveDynamicValue(ValueObject &in_value) override;
+
+ lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bp,
+ bool catch_bp,
+ bool throw_bp) override;
+
+ bool LoadModule(const lldb::ModuleSP &module_sp);
+
+ void DumpModules(Stream &strm) const;
+
+ void DumpContexts(Stream &strm) const;
+
+ void DumpKernels(Stream &strm) const;
+
+ bool DumpAllocation(Stream &strm, StackFrame *frame_ptr, const uint32_t id);
+
+ void ListAllocations(Stream &strm, StackFrame *frame_ptr,
+ const uint32_t index);
+
+ bool RecomputeAllAllocations(Stream &strm, StackFrame *frame_ptr);
+
+ bool PlaceBreakpointOnKernel(
+ lldb::TargetSP target, Stream &messages, const char *name,
+ const lldb_renderscript::RSCoordinate *coords = nullptr);
+
+ bool PlaceBreakpointOnReduction(
+ lldb::TargetSP target, Stream &messages, const char *reduce_name,
+ const lldb_renderscript::RSCoordinate *coords = nullptr,
+ int kernel_types = ~(0));
+
+ bool PlaceBreakpointOnScriptGroup(lldb::TargetSP target, Stream &strm,
+ ConstString name, bool stop_on_all);
+
+ void SetBreakAllKernels(bool do_break, lldb::TargetSP target);
+
+ void DumpStatus(Stream &strm) const;
+
+ void ModulesDidLoad(const ModuleList &module_list) override;
+
+ bool LoadAllocation(Stream &strm, const uint32_t alloc_id,
+ const char *filename, StackFrame *frame_ptr);
+
+ bool SaveAllocation(Stream &strm, const uint32_t alloc_id,
+ const char *filename, StackFrame *frame_ptr);
+
+ void Update();
+
+ void Initiate();
+
+ const lldb_renderscript::RSScriptGroupList &GetScriptGroups() const {
+ return m_scriptGroups;
+ };
+
+ bool IsKnownKernel(ConstString name) {
+ for (const auto &module : m_rsmodules)
+ for (const auto &kernel : module->m_kernels)
+ if (kernel.m_name == name)
+ return true;
+ return false;
+ }
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ static bool GetKernelCoordinate(lldb_renderscript::RSCoordinate &coord,
+ Thread *thread_ptr);
+
+ bool ResolveKernelName(lldb::addr_t kernel_address, ConstString &name);
+
+protected:
+ struct ScriptDetails;
+ struct AllocationDetails;
+ struct Element;
+
+ lldb_renderscript::RSScriptGroupList m_scriptGroups;
+
+ void InitSearchFilter(lldb::TargetSP target) {
+ if (!m_filtersp)
+ m_filtersp.reset(new SearchFilterForUnconstrainedSearches(target));
+ }
+
+ void FixupScriptDetails(lldb_renderscript::RSModuleDescriptorSP rsmodule_sp);
+
+ void LoadRuntimeHooks(lldb::ModuleSP module, ModuleKind kind);
+
+ bool RefreshAllocation(AllocationDetails *alloc, StackFrame *frame_ptr);
+
+ bool EvalRSExpression(const char *expression, StackFrame *frame_ptr,
+ uint64_t *result);
+
+ lldb::BreakpointSP CreateScriptGroupBreakpoint(ConstString name,
+ bool multi);
+
+ lldb::BreakpointSP CreateKernelBreakpoint(ConstString name);
+
+ lldb::BreakpointSP CreateReductionBreakpoint(ConstString name,
+ int kernel_types);
+
+ void BreakOnModuleKernels(
+ const lldb_renderscript::RSModuleDescriptorSP rsmodule_sp);
+
+ struct RuntimeHook;
+ typedef void (RenderScriptRuntime::*CaptureStateFn)(
+ RuntimeHook *hook_info,
+ ExecutionContext &context); // Please do this!
+
+ struct HookDefn {
+ const char *name;
+ const char *symbol_name_m32; // mangled name for the 32 bit architectures
+ const char *symbol_name_m64; // mangled name for the 64 bit archs
+ uint32_t version;
+ ModuleKind kind;
+ CaptureStateFn grabber;
+ };
+
+ struct RuntimeHook {
+ lldb::addr_t address;
+ const HookDefn *defn;
+ lldb::BreakpointSP bp_sp;
+ };
+
+ typedef std::shared_ptr<RuntimeHook> RuntimeHookSP;
+
+ lldb::ModuleSP m_libRS;
+ lldb::ModuleSP m_libRSDriver;
+ lldb::ModuleSP m_libRSCpuRef;
+ std::vector<lldb_renderscript::RSModuleDescriptorSP> m_rsmodules;
+
+ std::vector<std::unique_ptr<ScriptDetails>> m_scripts;
+ std::vector<std::unique_ptr<AllocationDetails>> m_allocations;
+
+ std::map<lldb::addr_t, lldb_renderscript::RSModuleDescriptorSP>
+ m_scriptMappings;
+ std::map<lldb::addr_t, RuntimeHookSP> m_runtimeHooks;
+ std::map<lldb::user_id_t, std::unique_ptr<lldb_renderscript::RSCoordinate>>
+ m_conditional_breaks;
+
+ lldb::SearchFilterSP
+ m_filtersp; // Needed to create breakpoints through Target API
+
+ bool m_initiated;
+ bool m_debuggerPresentFlagged;
+ bool m_breakAllKernels;
+ static const HookDefn s_runtimeHookDefns[];
+ static const size_t s_runtimeHookCount;
+ LLVMUserExpression::IRPasses *m_ir_passes;
+
+private:
+ RenderScriptRuntime(Process *process); // Call CreateInstance instead.
+
+ static bool HookCallback(void *baton, StoppointCallbackContext *ctx,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ static bool KernelBreakpointHit(void *baton, StoppointCallbackContext *ctx,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ void HookCallback(RuntimeHook *hook_info, ExecutionContext &context);
+
+ // Callback function when 'debugHintScriptGroup2' executes on the target.
+ void CaptureDebugHintScriptGroup2(RuntimeHook *hook_info,
+ ExecutionContext &context);
+
+ void CaptureScriptInit(RuntimeHook *hook_info, ExecutionContext &context);
+
+ void CaptureAllocationInit(RuntimeHook *hook_info, ExecutionContext &context);
+
+ void CaptureAllocationDestroy(RuntimeHook *hook_info,
+ ExecutionContext &context);
+
+ void CaptureSetGlobalVar(RuntimeHook *hook_info, ExecutionContext &context);
+
+ void CaptureScriptInvokeForEachMulti(RuntimeHook *hook_info,
+ ExecutionContext &context);
+
+ AllocationDetails *FindAllocByID(Stream &strm, const uint32_t alloc_id);
+
+ std::shared_ptr<uint8_t> GetAllocationData(AllocationDetails *alloc,
+ StackFrame *frame_ptr);
+
+ void SetElementSize(Element &elem);
+
+ static bool GetFrameVarAsUnsigned(const lldb::StackFrameSP,
+ const char *var_name, uint64_t &val);
+
+ void FindStructTypeName(Element &elem, StackFrame *frame_ptr);
+
+ size_t PopulateElementHeaders(const std::shared_ptr<uint8_t> header_buffer,
+ size_t offset, const Element &elem);
+
+ size_t CalculateElementHeaderSize(const Element &elem);
+
+ void SetConditional(lldb::BreakpointSP bp, lldb_private::Stream &messages,
+ const lldb_renderscript::RSCoordinate &coord);
+ //
+ // Helper functions for jitting the runtime
+ //
+
+ bool JITDataPointer(AllocationDetails *alloc, StackFrame *frame_ptr,
+ uint32_t x = 0, uint32_t y = 0, uint32_t z = 0);
+
+ bool JITTypePointer(AllocationDetails *alloc, StackFrame *frame_ptr);
+
+ bool JITTypePacked(AllocationDetails *alloc, StackFrame *frame_ptr);
+
+ bool JITElementPacked(Element &elem, const lldb::addr_t context,
+ StackFrame *frame_ptr);
+
+ bool JITAllocationSize(AllocationDetails *alloc, StackFrame *frame_ptr);
+
+ bool JITSubelements(Element &elem, const lldb::addr_t context,
+ StackFrame *frame_ptr);
+
+ bool JITAllocationStride(AllocationDetails *alloc, StackFrame *frame_ptr);
+
+ // Search for a script detail object using a target address.
+ // If a script does not currently exist this function will return nullptr.
+ // If 'create' is true and there is no previous script with this address,
+ // then a new Script detail object will be created for this address and
+ // returned.
+ ScriptDetails *LookUpScript(lldb::addr_t address, bool create);
+
+ // Search for a previously saved allocation detail object using a target
+ // address.
+ // If an allocation does not exist for this address then nullptr will be
+ // returned.
+ AllocationDetails *LookUpAllocation(lldb::addr_t address);
+
+ // Creates a new allocation with the specified address assigning a new ID and
+ // removes
+ // any previous stored allocation which has the same address.
+ AllocationDetails *CreateAllocation(lldb::addr_t address);
+
+ bool GetOverrideExprOptions(clang::TargetOptions &prototype) override;
+
+ bool GetIRPasses(LLVMUserExpression::IRPasses &passes) override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_RenderScriptRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp
new file mode 100644
index 000000000000..45d0d028d047
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp
@@ -0,0 +1,160 @@
+//===-- RenderScriptScriptGroup.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+
+#include "RenderScriptRuntime.h"
+#include "RenderScriptScriptGroup.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_renderscript;
+
+class CommandObjectRenderScriptScriptGroupBreakpointSet
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptScriptGroupBreakpointSet(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript scriptgroup breakpoint set",
+ "Place a breakpoint on all kernels forming a script group.",
+ "renderscript scriptgroup breakpoint set <group_name>",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptScriptGroupBreakpointSet() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Stream &stream = result.GetOutputStream();
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ assert(runtime);
+ auto &target = m_exe_ctx.GetTargetSP();
+ bool stop_on_all = false;
+ const llvm::StringRef long_stop_all("--stop-on-all"), short_stop_all("-a");
+ std::vector<ConstString> sites;
+ sites.reserve(command.GetArgumentCount());
+ for (size_t i = 0; i < command.GetArgumentCount(); ++i) {
+ const auto arg = command.GetArgumentAtIndex(i);
+ if (long_stop_all == arg || short_stop_all == arg)
+ stop_on_all = true;
+ else
+ sites.push_back(ConstString(arg));
+ }
+ for (const auto &name : sites) {
+ runtime->PlaceBreakpointOnScriptGroup(target, stream, name, stop_on_all);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptScriptGroupBreakpoint
+ : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptScriptGroupBreakpoint(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "renderscript scriptgroup breakpoint",
+ "Renderscript scriptgroup breakpoint interaction.",
+ "renderscript scriptgroup breakpoint set [--stop-on-all/-a]"
+ "<scriptgroup name> ...",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched) {
+ LoadSubCommand(
+ "set",
+ CommandObjectSP(new CommandObjectRenderScriptScriptGroupBreakpointSet(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptScriptGroupBreakpoint() override = default;
+};
+
+class CommandObjectRenderScriptScriptGroupList : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptScriptGroupList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript scriptgroup list",
+ "List all currently discovered script groups.",
+ "renderscript scriptgroup list",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptScriptGroupList() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Stream &stream = result.GetOutputStream();
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ assert(runtime);
+ const RSScriptGroupList &groups = runtime->GetScriptGroups();
+ // print script group count
+ stream.Printf("%" PRIu64 " script %s", uint64_t(groups.size()),
+ (groups.size() == 1) ? "group" : "groups");
+ stream.EOL();
+ // print script group details
+ stream.IndentMore();
+ for (const RSScriptGroupDescriptorSP &g : groups) {
+ if (g) {
+ stream.Indent();
+ // script group name
+ stream.Printf("%s", g->m_name.AsCString());
+ stream.EOL();
+ // print out the kernels
+ stream.IndentMore();
+ for (const auto &k : g->m_kernels) {
+ stream.Indent();
+ stream.Printf(". %s", k.m_name.AsCString());
+ stream.EOL();
+ }
+ stream.IndentLess();
+ }
+ }
+ stream.IndentLess();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptScriptGroup : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptScriptGroup(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript scriptgroup",
+ "Command set for interacting with scriptgroups.",
+ nullptr, eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {
+ LoadSubCommand(
+ "breakpoint",
+ CommandObjectSP(
+ new CommandObjectRenderScriptScriptGroupBreakpoint(interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(
+ new CommandObjectRenderScriptScriptGroupList(interpreter)));
+ }
+
+ ~CommandObjectRenderScriptScriptGroup() override = default;
+};
+
+lldb::CommandObjectSP NewCommandObjectRenderScriptScriptGroup(
+ lldb_private::CommandInterpreter &interpreter) {
+ return CommandObjectSP(new CommandObjectRenderScriptScriptGroup(interpreter));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.h
new file mode 100644
index 000000000000..c25e240f6d52
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.h
@@ -0,0 +1,17 @@
+//===-- RenderScriptScriptGroup.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RenderScriptScriptGroup_h_
+#define liblldb_RenderScriptScriptGroup_h_
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+
+lldb::CommandObjectSP NewCommandObjectRenderScriptScriptGroup(
+ lldb_private::CommandInterpreter &interpreter);
+
+#endif // liblldb_RenderScriptScriptGroup_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp
new file mode 100644
index 000000000000..4725e8c5b0eb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp
@@ -0,0 +1,281 @@
+//===-- RenderScriptx86ABIFixups.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <set>
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CallSite.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Pass.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb_private;
+namespace {
+
+bool isRSAPICall(llvm::Module &module, llvm::CallInst *call_inst) {
+ // TODO get the list of renderscript modules from lldb and check if
+ // this llvm::Module calls into any of them.
+ (void)module;
+ const auto func_name = call_inst->getCalledFunction()->getName();
+ if (func_name.startswith("llvm") || func_name.startswith("lldb"))
+ return false;
+
+ if (call_inst->getCalledFunction()->isIntrinsic())
+ return false;
+
+ return true;
+}
+
+bool isRSLargeReturnCall(llvm::Module &module, llvm::CallInst *call_inst) {
+ // i686 and x86_64 returns for large vectors in the RenderScript API are not
+ // handled as normal register pairs, but as a hidden sret type. This is not
+ // reflected in the debug info or mangled symbol name, and the android ABI
+ // for x86 and x86_64, (as well as the emulators) specifies there is no AVX,
+ // so bcc generates an sret function because we cannot natively return
+ // 256 bit vectors.
+ // This function simply checks whether a function has a > 128bit return type.
+ // It is perhaps an unreliable heuristic, and relies on bcc not generating
+ // AVX code, so if the android ABI one day provides for AVX, this function
+ // may go out of fashion.
+ (void)module;
+ if (!call_inst || !call_inst->getCalledFunction())
+ return false;
+
+ return call_inst->getCalledFunction()
+ ->getReturnType()
+ ->getPrimitiveSizeInBits() > 128;
+}
+
+bool isRSAllocationPtrTy(const llvm::Type *type) {
+ if (!type->isPointerTy())
+ return false;
+ auto ptr_type = type->getPointerElementType();
+
+ return ptr_type->isStructTy() &&
+ ptr_type->getStructName().startswith("struct.rs_allocation");
+}
+
+bool isRSAllocationTyCallSite(llvm::Module &module, llvm::CallInst *call_inst) {
+ (void)module;
+ if (!call_inst->hasByValArgument())
+ return false;
+ for (const auto &param : call_inst->operand_values())
+ if (isRSAllocationPtrTy(param->getType()))
+ return true;
+ return false;
+}
+
+llvm::FunctionType *cloneToStructRetFnTy(llvm::CallInst *call_inst) {
+ // on x86 StructReturn functions return a pointer to the return value, rather
+ // than the return value itself
+ // [ref](http://www.agner.org/optimize/calling_conventions.pdf section 6). We
+ // create a return type by getting the pointer type of the old return type,
+ // and inserting a new initial argument of pointer type of the original
+ // return type.
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_EXPRESSIONS));
+
+ assert(call_inst && "no CallInst");
+ llvm::Function *orig = call_inst->getCalledFunction();
+ assert(orig && "CallInst has no called function");
+ llvm::FunctionType *orig_type = orig->getFunctionType();
+ auto name = orig->getName();
+ if (log)
+ log->Printf("%s - cloning to StructRet function for '%s'", __FUNCTION__,
+ name.str().c_str());
+
+ unsigned num_params = orig_type->getNumParams();
+ std::vector<llvm::Type *> new_params{num_params + 1, nullptr};
+ std::vector<llvm::Type *> params{orig_type->param_begin(),
+ orig_type->param_end()};
+
+ // This may not work if the function is somehow declared void as llvm is
+ // strongly typed and represents void* with i8*
+ assert(!orig_type->getReturnType()->isVoidTy() &&
+ "Cannot add StructRet attribute to void function");
+ llvm::PointerType *return_type_ptr_type =
+ llvm::PointerType::getUnqual(orig->getReturnType());
+ assert(return_type_ptr_type &&
+ "failed to get function return type PointerType");
+ if (!return_type_ptr_type)
+ return nullptr;
+
+ if (log)
+ log->Printf("%s - return type pointer type for StructRet clone @ '0x%p':\n",
+ __FUNCTION__, (void *)return_type_ptr_type);
+ // put the sret pointer argument in place at the beginning of the
+ // argument list.
+ params.emplace(params.begin(), return_type_ptr_type);
+ assert(params.size() == num_params + 1);
+ return llvm::FunctionType::get(return_type_ptr_type, params,
+ orig->isVarArg());
+}
+
+bool findRSCallSites(llvm::Module &module,
+ std::set<llvm::CallInst *> &rs_callsites,
+ bool (*predicate)(llvm::Module &, llvm::CallInst *)) {
+ bool found = false;
+
+ for (auto &func : module.getFunctionList())
+ for (auto &block : func.getBasicBlockList())
+ for (auto &inst : block) {
+ llvm::CallInst *call_inst =
+ llvm::dyn_cast_or_null<llvm::CallInst>(&inst);
+ if (!call_inst || !call_inst->getCalledFunction())
+ // This is not the call-site you are looking for...
+ continue;
+ if (isRSAPICall(module, call_inst) && predicate(module, call_inst)) {
+ rs_callsites.insert(call_inst);
+ found = true;
+ }
+ }
+ return found;
+}
+
+bool fixupX86StructRetCalls(llvm::Module &module) {
+ bool changed = false;
+ // changing a basic block while iterating over it seems to have some
+ // undefined behaviour going on so we find all RS callsites first, then fix
+ // them up after consuming the iterator.
+ std::set<llvm::CallInst *> rs_callsites;
+ if (!findRSCallSites(module, rs_callsites, isRSLargeReturnCall))
+ return false;
+
+ for (auto call_inst : rs_callsites) {
+ llvm::FunctionType *new_func_type = cloneToStructRetFnTy(call_inst);
+ assert(new_func_type &&
+ "failed to clone functionType for Renderscript ABI fixup");
+
+ llvm::CallSite call_site(call_inst);
+ llvm::Function *func = call_inst->getCalledFunction();
+ assert(func && "cannot resolve function in RenderScriptRuntime");
+ // Copy the original call arguments
+ std::vector<llvm::Value *> new_call_args(call_site.arg_begin(),
+ call_site.arg_end());
+
+ // Allocate enough space to store the return value of the original function
+ // we pass a pointer to this allocation as the StructRet param, and then
+ // copy its value into the lldb return value
+ const llvm::DataLayout &DL = module.getDataLayout();
+ llvm::AllocaInst *return_value_alloc = new llvm::AllocaInst(
+ func->getReturnType(), DL.getAllocaAddrSpace(), "var_vector_return_alloc",
+ call_inst);
+ // use the new allocation as the new first argument
+ new_call_args.emplace(new_call_args.begin(),
+ llvm::cast<llvm::Value>(return_value_alloc));
+ llvm::PointerType *new_func_ptr_type =
+ llvm::PointerType::get(new_func_type, 0);
+ // Create the type cast from the old function type to the new one
+ llvm::Constant *new_func_cast = llvm::ConstantExpr::getCast(
+ llvm::Instruction::BitCast, func, new_func_ptr_type);
+ // create an allocation for a new function pointer
+ llvm::AllocaInst *new_func_ptr =
+ new llvm::AllocaInst(new_func_ptr_type, DL.getAllocaAddrSpace(),
+ "new_func_ptr", call_inst);
+ // store the new_func_cast to the newly allocated space
+ (new llvm::StoreInst(new_func_cast, new_func_ptr, call_inst))
+ ->setName("new_func_ptr_load_cast");
+ // load the new function address ready for a jump
+ llvm::LoadInst *new_func_addr_load =
+ new llvm::LoadInst(new_func_ptr, "load_func_pointer", call_inst);
+ // and create a callinstruction from it
+ llvm::CallInst *new_call_inst =
+ llvm::CallInst::Create(new_func_type, new_func_addr_load, new_call_args,
+ "new_func_call", call_inst);
+ new_call_inst->setCallingConv(call_inst->getCallingConv());
+ new_call_inst->setTailCall(call_inst->isTailCall());
+ llvm::LoadInst *lldb_save_result_address =
+ new llvm::LoadInst(return_value_alloc, "save_return_val", call_inst);
+
+ // Now remove the old broken call
+ call_inst->replaceAllUsesWith(lldb_save_result_address);
+ call_inst->eraseFromParent();
+ changed = true;
+ }
+ return changed;
+}
+
+bool fixupRSAllocationStructByValCalls(llvm::Module &module) {
+ // On x86_64, calls to functions in the RS runtime that take an
+ // `rs_allocation` type argument are actually handled as by-ref params by
+ // bcc, but appear to be passed by value by lldb (the callsite all use
+ // `struct byval`). On x86_64 Linux, struct arguments are transferred in
+ // registers if the struct size is no bigger than 128bits
+ // [ref](http://www.agner.org/optimize/calling_conventions.pdf) section 7.1
+ // "Passing and returning objects" otherwise passed on the stack. an object
+ // of type `rs_allocation` is actually 256bits, so should be passed on the
+ // stack. However, code generated by bcc actually treats formal params of
+ // type `rs_allocation` as `rs_allocation *` so we need to convert the
+ // calling convention to pass by reference, and remove any hint of byval from
+ // formal parameters.
+ bool changed = false;
+ std::set<llvm::CallInst *> rs_callsites;
+ if (!findRSCallSites(module, rs_callsites, isRSAllocationTyCallSite))
+ return false;
+
+ std::set<llvm::Function *> rs_functions;
+
+ // for all call instructions
+ for (auto call_inst : rs_callsites) {
+ // add the called function to a set so that we can strip its byval
+ // attributes in another pass
+ rs_functions.insert(call_inst->getCalledFunction());
+
+ // get the function attributes
+ llvm::AttributeList call_attribs = call_inst->getAttributes();
+
+ // iterate over the argument attributes
+ for (unsigned I = call_attribs.index_begin(); I != call_attribs.index_end();
+ I++) {
+ // if this argument is passed by val
+ if (call_attribs.hasAttribute(I, llvm::Attribute::ByVal)) {
+ // strip away the byval attribute
+ call_inst->removeAttribute(I, llvm::Attribute::ByVal);
+ changed = true;
+ }
+ }
+ }
+
+ // for all called function decls
+ for (auto func : rs_functions) {
+ // inspect all of the arguments in the call
+ for (auto &arg : func->args()) {
+ if (arg.hasByValAttr()) {
+ arg.removeAttr(llvm::Attribute::ByVal);
+ changed = true;
+ }
+ }
+ }
+ return changed;
+}
+} // end anonymous namespace
+
+namespace lldb_private {
+namespace lldb_renderscript {
+
+bool fixupX86FunctionCalls(llvm::Module &module) {
+ return fixupX86StructRetCalls(module);
+}
+
+bool fixupX86_64FunctionCalls(llvm::Module &module) {
+ bool changed = false;
+ changed |= fixupX86StructRetCalls(module);
+ changed |= fixupRSAllocationStructByValCalls(module);
+ return changed;
+}
+
+} // end namespace lldb_renderscript
+} // end namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.h
new file mode 100644
index 000000000000..a5efc999aea4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.h
@@ -0,0 +1,22 @@
+//===-- RenderScriptx86ABIFixups.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_RENDERSCRIPT_X86_H
+#define LLDB_RENDERSCRIPT_X86_H
+
+#include "llvm/IR/Module.h"
+
+namespace lldb_private {
+namespace lldb_renderscript {
+
+bool fixupX86FunctionCalls(llvm::Module &module);
+
+bool fixupX86_64FunctionCalls(llvm::Module &module);
+}
+}
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp b/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp
new file mode 100644
index 000000000000..e0d2c5d0eef8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp
@@ -0,0 +1,200 @@
+//===-- MemoryHistoryASan.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MemoryHistoryASan.h"
+
+#include "lldb/Target/MemoryHistory.h"
+
+#include "Plugins/Process/Utility/HistoryThread.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/lldb-private.h"
+
+#include <sstream>
+
+using namespace lldb;
+using namespace lldb_private;
+
+MemoryHistorySP MemoryHistoryASan::CreateInstance(const ProcessSP &process_sp) {
+ if (!process_sp.get())
+ return nullptr;
+
+ Target &target = process_sp->GetTarget();
+
+ const ModuleList &target_modules = target.GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ const size_t num_modules = target_modules.GetSize();
+ for (size_t i = 0; i < num_modules; ++i) {
+ Module *module_pointer = target_modules.GetModulePointerAtIndexUnlocked(i);
+
+ const Symbol *symbol = module_pointer->FindFirstSymbolWithNameAndType(
+ ConstString("__asan_get_alloc_stack"), lldb::eSymbolTypeAny);
+
+ if (symbol != nullptr)
+ return MemoryHistorySP(new MemoryHistoryASan(process_sp));
+ }
+
+ return MemoryHistorySP();
+}
+
+void MemoryHistoryASan::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "ASan memory history provider.", CreateInstance);
+}
+
+void MemoryHistoryASan::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString MemoryHistoryASan::GetPluginNameStatic() {
+ static ConstString g_name("asan");
+ return g_name;
+}
+
+MemoryHistoryASan::MemoryHistoryASan(const ProcessSP &process_sp) {
+ if (process_sp)
+ m_process_wp = process_sp;
+}
+
+const char *memory_history_asan_command_prefix = R"(
+ extern "C"
+ {
+ size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size, int *thread_id);
+ size_t __asan_get_free_stack(void *addr, void **trace, size_t size, int *thread_id);
+ }
+
+ struct data {
+ void *alloc_trace[256];
+ size_t alloc_count;
+ int alloc_tid;
+
+ void *free_trace[256];
+ size_t free_count;
+ int free_tid;
+ };
+)";
+
+const char *memory_history_asan_command_format =
+ R"(
+ data t;
+
+ t.alloc_count = __asan_get_alloc_stack((void *)0x%)" PRIx64
+ R"(, t.alloc_trace, 256, &t.alloc_tid);
+ t.free_count = __asan_get_free_stack((void *)0x%)" PRIx64
+ R"(, t.free_trace, 256, &t.free_tid);
+
+ t;
+)";
+
+static void CreateHistoryThreadFromValueObject(ProcessSP process_sp,
+ ValueObjectSP return_value_sp,
+ const char *type,
+ const char *thread_name,
+ HistoryThreads &result) {
+ std::string count_path = "." + std::string(type) + "_count";
+ std::string tid_path = "." + std::string(type) + "_tid";
+ std::string trace_path = "." + std::string(type) + "_trace";
+
+ ValueObjectSP count_sp =
+ return_value_sp->GetValueForExpressionPath(count_path.c_str());
+ ValueObjectSP tid_sp =
+ return_value_sp->GetValueForExpressionPath(tid_path.c_str());
+
+ if (!count_sp || !tid_sp)
+ return;
+
+ int count = count_sp->GetValueAsUnsigned(0);
+ tid_t tid = tid_sp->GetValueAsUnsigned(0) + 1;
+
+ if (count <= 0)
+ return;
+
+ ValueObjectSP trace_sp =
+ return_value_sp->GetValueForExpressionPath(trace_path.c_str());
+
+ if (!trace_sp)
+ return;
+
+ std::vector<lldb::addr_t> pcs;
+ for (int i = 0; i < count; i++) {
+ addr_t pc = trace_sp->GetChildAtIndex(i, true)->GetValueAsUnsigned(0);
+ if (pc == 0 || pc == 1 || pc == LLDB_INVALID_ADDRESS)
+ continue;
+ pcs.push_back(pc);
+ }
+
+ HistoryThread *history_thread = new HistoryThread(*process_sp, tid, pcs);
+ ThreadSP new_thread_sp(history_thread);
+ std::ostringstream thread_name_with_number;
+ thread_name_with_number << thread_name << " Thread " << tid;
+ history_thread->SetThreadName(thread_name_with_number.str().c_str());
+ // Save this in the Process' ExtendedThreadList so a strong pointer retains
+ // the object
+ process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
+ result.push_back(new_thread_sp);
+}
+
+HistoryThreads MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) {
+ HistoryThreads result;
+
+ ProcessSP process_sp = m_process_wp.lock();
+ if (!process_sp)
+ return result;
+
+ ThreadSP thread_sp =
+ process_sp->GetThreadList().GetExpressionExecutionThread();
+ if (!thread_sp)
+ return result;
+
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+ if (!frame_sp)
+ return result;
+
+ ExecutionContext exe_ctx(frame_sp);
+ ValueObjectSP return_value_sp;
+ StreamString expr;
+ Status eval_error;
+ expr.Printf(memory_history_asan_command_format, address, address);
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(true);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
+ options.SetPrefix(memory_history_asan_command_prefix);
+ options.SetAutoApplyFixIts(false);
+ options.SetLanguage(eLanguageTypeObjC_plus_plus);
+
+ ExpressionResults expr_result = UserExpression::Evaluate(
+ exe_ctx, options, expr.GetString(), "", return_value_sp, eval_error);
+ if (expr_result != eExpressionCompleted) {
+ process_sp->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf(
+ "Warning: Cannot evaluate AddressSanitizer expression:\n%s\n",
+ eval_error.AsCString());
+ return result;
+ }
+
+ if (!return_value_sp)
+ return result;
+
+ CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "free",
+ "Memory deallocated by", result);
+ CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "alloc",
+ "Memory allocated by", result);
+
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h b/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h
new file mode 100644
index 000000000000..266576b0cd96
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h
@@ -0,0 +1,48 @@
+//===-- MemoryHistoryASan.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MemoryHistoryASan_h_
+#define liblldb_MemoryHistoryASan_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/MemoryHistory.h"
+#include "lldb/Target/Process.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class MemoryHistoryASan : public lldb_private::MemoryHistory {
+public:
+ ~MemoryHistoryASan() override = default;
+
+ static lldb::MemoryHistorySP
+ CreateInstance(const lldb::ProcessSP &process_sp);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ lldb_private::HistoryThreads GetHistoryThreads(lldb::addr_t address) override;
+
+private:
+ MemoryHistoryASan(const lldb::ProcessSP &process_sp);
+
+ lldb::ProcessWP m_process_wp;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_MemoryHistoryASan_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp
new file mode 100644
index 000000000000..512b5bebf07f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp
@@ -0,0 +1,514 @@
+//===-- ObjectContainerBSDArchive.cpp ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ObjectContainerBSDArchive.h"
+
+#if defined(_WIN32) || defined(__ANDROID__)
+// Defines from ar, missing on Windows
+#define ARMAG "!<arch>\n"
+#define SARMAG 8
+#define ARFMAG "`\n"
+
+typedef struct ar_hdr {
+ char ar_name[16];
+ char ar_date[12];
+ char ar_uid[6], ar_gid[6];
+ char ar_mode[8];
+ char ar_size[10];
+ char ar_fmag[2];
+} ar_hdr;
+#else
+#include <ar.h>
+#endif
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+
+#include "llvm/Support/MemoryBuffer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ObjectContainerBSDArchive::Object::Object()
+ : ar_name(), modification_time(0), uid(0), gid(0), mode(0), size(0),
+ file_offset(0), file_size(0) {}
+
+void ObjectContainerBSDArchive::Object::Clear() {
+ ar_name.Clear();
+ modification_time = 0;
+ uid = 0;
+ gid = 0;
+ mode = 0;
+ size = 0;
+ file_offset = 0;
+ file_size = 0;
+}
+
+lldb::offset_t
+ObjectContainerBSDArchive::Object::Extract(const DataExtractor &data,
+ lldb::offset_t offset) {
+ size_t ar_name_len = 0;
+ std::string str;
+ char *err;
+
+ // File header
+ //
+ // The common format is as follows.
+ //
+ // Offset Length Name Format
+ // 0 16 File name ASCII right padded with spaces (no spaces
+ // allowed in file name)
+ // 16 12 File mod Decimal as cstring right padded with
+ // spaces
+ // 28 6 Owner ID Decimal as cstring right padded with
+ // spaces
+ // 34 6 Group ID Decimal as cstring right padded with
+ // spaces
+ // 40 8 File mode Octal as cstring right padded with
+ // spaces
+ // 48 10 File byte size Decimal as cstring right padded with
+ // spaces
+ // 58 2 File magic 0x60 0x0A
+
+ // Make sure there is enough data for the file header and bail if not
+ if (!data.ValidOffsetForDataOfSize(offset, 60))
+ return LLDB_INVALID_OFFSET;
+
+ str.assign((const char *)data.GetData(&offset, 16), 16);
+ if (str.find("#1/") == 0) {
+ // If the name is longer than 16 bytes, or contains an embedded space then
+ // it will use this format where the length of the name is here and the
+ // name characters are after this header.
+ ar_name_len = strtoul(str.c_str() + 3, &err, 10);
+ } else {
+ // Strip off any trailing spaces.
+ const size_t last_pos = str.find_last_not_of(' ');
+ if (last_pos != std::string::npos) {
+ if (last_pos + 1 < 16)
+ str.erase(last_pos + 1);
+ }
+ ar_name.SetCString(str.c_str());
+ }
+
+ str.assign((const char *)data.GetData(&offset, 12), 12);
+ modification_time = strtoul(str.c_str(), &err, 10);
+
+ str.assign((const char *)data.GetData(&offset, 6), 6);
+ uid = strtoul(str.c_str(), &err, 10);
+
+ str.assign((const char *)data.GetData(&offset, 6), 6);
+ gid = strtoul(str.c_str(), &err, 10);
+
+ str.assign((const char *)data.GetData(&offset, 8), 8);
+ mode = strtoul(str.c_str(), &err, 8);
+
+ str.assign((const char *)data.GetData(&offset, 10), 10);
+ size = strtoul(str.c_str(), &err, 10);
+
+ str.assign((const char *)data.GetData(&offset, 2), 2);
+ if (str == ARFMAG) {
+ if (ar_name_len > 0) {
+ const void *ar_name_ptr = data.GetData(&offset, ar_name_len);
+ // Make sure there was enough data for the string value and bail if not
+ if (ar_name_ptr == nullptr)
+ return LLDB_INVALID_OFFSET;
+ str.assign((const char *)ar_name_ptr, ar_name_len);
+ ar_name.SetCString(str.c_str());
+ }
+ file_offset = offset;
+ file_size = size - ar_name_len;
+ return offset;
+ }
+ return LLDB_INVALID_OFFSET;
+}
+
+ObjectContainerBSDArchive::Archive::Archive(const lldb_private::ArchSpec &arch,
+ const llvm::sys::TimePoint<> &time,
+ lldb::offset_t file_offset,
+ lldb_private::DataExtractor &data)
+ : m_arch(arch), m_modification_time(time), m_file_offset(file_offset),
+ m_objects(), m_data(data) {}
+
+ObjectContainerBSDArchive::Archive::~Archive() {}
+
+size_t ObjectContainerBSDArchive::Archive::ParseObjects() {
+ DataExtractor &data = m_data;
+ std::string str;
+ lldb::offset_t offset = 0;
+ str.assign((const char *)data.GetData(&offset, SARMAG), SARMAG);
+ if (str == ARMAG) {
+ Object obj;
+ do {
+ offset = obj.Extract(data, offset);
+ if (offset == LLDB_INVALID_OFFSET)
+ break;
+ size_t obj_idx = m_objects.size();
+ m_objects.push_back(obj);
+ // Insert all of the C strings out of order for now...
+ m_object_name_to_index_map.Append(obj.ar_name, obj_idx);
+ offset += obj.file_size;
+ obj.Clear();
+ } while (data.ValidOffset(offset));
+
+ // Now sort all of the object name pointers
+ m_object_name_to_index_map.Sort();
+ }
+ return m_objects.size();
+}
+
+ObjectContainerBSDArchive::Object *
+ObjectContainerBSDArchive::Archive::FindObject(
+ ConstString object_name, const llvm::sys::TimePoint<> &object_mod_time) {
+ const ObjectNameToIndexMap::Entry *match =
+ m_object_name_to_index_map.FindFirstValueForName(object_name);
+ if (!match)
+ return nullptr;
+ if (object_mod_time == llvm::sys::TimePoint<>())
+ return &m_objects[match->value];
+
+ const uint64_t object_modification_date = llvm::sys::toTimeT(object_mod_time);
+ if (m_objects[match->value].modification_time == object_modification_date)
+ return &m_objects[match->value];
+
+ const ObjectNameToIndexMap::Entry *next_match =
+ m_object_name_to_index_map.FindNextValueForName(match);
+ while (next_match) {
+ if (m_objects[next_match->value].modification_time ==
+ object_modification_date)
+ return &m_objects[next_match->value];
+ next_match = m_object_name_to_index_map.FindNextValueForName(next_match);
+ }
+
+ return nullptr;
+}
+
+ObjectContainerBSDArchive::Archive::shared_ptr
+ObjectContainerBSDArchive::Archive::FindCachedArchive(
+ const FileSpec &file, const ArchSpec &arch,
+ const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset) {
+ std::lock_guard<std::recursive_mutex> guard(Archive::GetArchiveCacheMutex());
+ shared_ptr archive_sp;
+ Archive::Map &archive_map = Archive::GetArchiveCache();
+ Archive::Map::iterator pos = archive_map.find(file);
+ // Don't cache a value for "archive_map.end()" below since we might delete an
+ // archive entry...
+ while (pos != archive_map.end() && pos->first == file) {
+ bool match = true;
+ if (arch.IsValid() &&
+ !pos->second->GetArchitecture().IsCompatibleMatch(arch))
+ match = false;
+ else if (file_offset != LLDB_INVALID_OFFSET &&
+ pos->second->GetFileOffset() != file_offset)
+ match = false;
+ if (match) {
+ if (pos->second->GetModificationTime() == time) {
+ return pos->second;
+ } else {
+ // We have a file at the same path with the same architecture whose
+ // modification time doesn't match. It doesn't make sense for us to
+ // continue to use this BSD archive since we cache only the object info
+ // which consists of file time info and also the file offset and file
+ // size of any contained objects. Since this information is now out of
+ // date, we won't get the correct information if we go and extract the
+ // file data, so we should remove the old and outdated entry.
+ archive_map.erase(pos);
+ pos = archive_map.find(file);
+ continue; // Continue to next iteration so we don't increment pos
+ // below...
+ }
+ }
+ ++pos;
+ }
+ return archive_sp;
+}
+
+ObjectContainerBSDArchive::Archive::shared_ptr
+ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile(
+ const FileSpec &file, const ArchSpec &arch,
+ const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset,
+ DataExtractor &data) {
+ shared_ptr archive_sp(new Archive(arch, time, file_offset, data));
+ if (archive_sp) {
+ const size_t num_objects = archive_sp->ParseObjects();
+ if (num_objects > 0) {
+ std::lock_guard<std::recursive_mutex> guard(
+ Archive::GetArchiveCacheMutex());
+ Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp));
+ } else {
+ archive_sp.reset();
+ }
+ }
+ return archive_sp;
+}
+
+ObjectContainerBSDArchive::Archive::Map &
+ObjectContainerBSDArchive::Archive::GetArchiveCache() {
+ static Archive::Map g_archive_map;
+ return g_archive_map;
+}
+
+std::recursive_mutex &
+ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex() {
+ static std::recursive_mutex g_archive_map_mutex;
+ return g_archive_map_mutex;
+}
+
+void ObjectContainerBSDArchive::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ GetModuleSpecifications);
+}
+
+void ObjectContainerBSDArchive::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ObjectContainerBSDArchive::GetPluginNameStatic() {
+ static ConstString g_name("bsd-archive");
+ return g_name;
+}
+
+const char *ObjectContainerBSDArchive::GetPluginDescriptionStatic() {
+ return "BSD Archive object container reader.";
+}
+
+ObjectContainer *ObjectContainerBSDArchive::CreateInstance(
+ const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t length) {
+ ConstString object_name(module_sp->GetObjectName());
+ if (!object_name)
+ return nullptr;
+
+ if (data_sp) {
+ // We have data, which means this is the first 512 bytes of the file Check
+ // to see if the magic bytes match and if they do, read the entire table of
+ // contents for the archive and cache it
+ DataExtractor data;
+ data.SetData(data_sp, data_offset, length);
+ if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data)) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat,
+ "ObjectContainerBSDArchive::CreateInstance (module = %s, file = "
+ "%p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")",
+ module_sp->GetFileSpec().GetPath().c_str(),
+ static_cast<const void *>(file), static_cast<uint64_t>(file_offset),
+ static_cast<uint64_t>(length));
+
+ // Map the entire .a file to be sure that we don't lose any data if the
+ // file gets updated by a new build while this .a file is being used for
+ // debugging
+ DataBufferSP archive_data_sp =
+ FileSystem::Instance().CreateDataBuffer(*file, length, file_offset);
+ if (!archive_data_sp)
+ return nullptr;
+
+ lldb::offset_t archive_data_offset = 0;
+
+ Archive::shared_ptr archive_sp(Archive::FindCachedArchive(
+ *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(),
+ file_offset));
+ std::unique_ptr<ObjectContainerBSDArchive> container_up(
+ new ObjectContainerBSDArchive(module_sp, archive_data_sp,
+ archive_data_offset, file, file_offset,
+ length));
+
+ if (container_up) {
+ if (archive_sp) {
+ // We already have this archive in our cache, use it
+ container_up->SetArchive(archive_sp);
+ return container_up.release();
+ } else if (container_up->ParseHeader())
+ return container_up.release();
+ }
+ }
+ } else {
+ // No data, just check for a cached archive
+ Archive::shared_ptr archive_sp(Archive::FindCachedArchive(
+ *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(),
+ file_offset));
+ if (archive_sp) {
+ std::unique_ptr<ObjectContainerBSDArchive> container_up(
+ new ObjectContainerBSDArchive(module_sp, data_sp, data_offset, file,
+ file_offset, length));
+
+ if (container_up) {
+ // We already have this archive in our cache, use it
+ container_up->SetArchive(archive_sp);
+ return container_up.release();
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool ObjectContainerBSDArchive::MagicBytesMatch(const DataExtractor &data) {
+ uint32_t offset = 0;
+ const char *armag = (const char *)data.PeekData(offset, sizeof(ar_hdr));
+ if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0) {
+ armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG;
+ if (strncmp(armag, ARFMAG, 2) == 0)
+ return true;
+ }
+ return false;
+}
+
+ObjectContainerBSDArchive::ObjectContainerBSDArchive(
+ const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const lldb_private::FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t size)
+ : ObjectContainer(module_sp, file, file_offset, size, data_sp, data_offset),
+ m_archive_sp() {}
+void ObjectContainerBSDArchive::SetArchive(Archive::shared_ptr &archive_sp) {
+ m_archive_sp = archive_sp;
+}
+
+ObjectContainerBSDArchive::~ObjectContainerBSDArchive() {}
+
+bool ObjectContainerBSDArchive::ParseHeader() {
+ if (m_archive_sp.get() == nullptr) {
+ if (m_data.GetByteSize() > 0) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ m_archive_sp = Archive::ParseAndCacheArchiveForFile(
+ m_file, module_sp->GetArchitecture(),
+ module_sp->GetModificationTime(), m_offset, m_data);
+ }
+ // Clear the m_data that contains the entire archive data and let our
+ // m_archive_sp hold onto the data.
+ m_data.Clear();
+ }
+ }
+ return m_archive_sp.get() != nullptr;
+}
+
+void ObjectContainerBSDArchive::Dump(Stream *s) const {
+ s->Printf("%p: ", static_cast<const void *>(this));
+ s->Indent();
+ const size_t num_archs = GetNumArchitectures();
+ const size_t num_objects = GetNumObjects();
+ s->Printf("ObjectContainerBSDArchive, num_archs = %" PRIu64
+ ", num_objects = %" PRIu64 "",
+ (uint64_t)num_archs, (uint64_t)num_objects);
+ uint32_t i;
+ ArchSpec arch;
+ s->IndentMore();
+ for (i = 0; i < num_archs; i++) {
+ s->Indent();
+ GetArchitectureAtIndex(i, arch);
+ s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName());
+ }
+ for (i = 0; i < num_objects; i++) {
+ s->Indent();
+ s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex(i));
+ }
+ s->IndentLess();
+ s->EOL();
+}
+
+ObjectFileSP ObjectContainerBSDArchive::GetObjectFile(const FileSpec *file) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ if (module_sp->GetObjectName() && m_archive_sp) {
+ Object *object = m_archive_sp->FindObject(
+ module_sp->GetObjectName(), module_sp->GetObjectModificationTime());
+ if (object) {
+ lldb::offset_t data_offset = object->file_offset;
+ return ObjectFile::FindPlugin(
+ module_sp, file, m_offset + object->file_offset, object->file_size,
+ m_archive_sp->GetData().GetSharedDataBuffer(), data_offset);
+ }
+ }
+ }
+ return ObjectFileSP();
+}
+
+// PluginInterface protocol
+lldb_private::ConstString ObjectContainerBSDArchive::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ObjectContainerBSDArchive::GetPluginVersion() { return 1; }
+
+size_t ObjectContainerBSDArchive::GetModuleSpecifications(
+ const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, lldb::offset_t file_offset,
+ lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) {
+
+ // We have data, which means this is the first 512 bytes of the file Check to
+ // see if the magic bytes match and if they do, read the entire table of
+ // contents for the archive and cache it
+ DataExtractor data;
+ data.SetData(data_sp, data_offset, data_sp->GetByteSize());
+ if (!file || !data_sp || !ObjectContainerBSDArchive::MagicBytesMatch(data))
+ return 0;
+
+ const size_t initial_count = specs.GetSize();
+ llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file);
+ Archive::shared_ptr archive_sp(
+ Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset));
+ bool set_archive_arch = false;
+ if (!archive_sp) {
+ set_archive_arch = true;
+ data_sp =
+ FileSystem::Instance().CreateDataBuffer(file, file_size, file_offset);
+ if (data_sp) {
+ data.SetData(data_sp, 0, data_sp->GetByteSize());
+ archive_sp = Archive::ParseAndCacheArchiveForFile(
+ file, ArchSpec(), file_mod_time, file_offset, data);
+ }
+ }
+
+ if (archive_sp) {
+ const size_t num_objects = archive_sp->GetNumObjects();
+ for (size_t idx = 0; idx < num_objects; ++idx) {
+ const Object *object = archive_sp->GetObjectAtIndex(idx);
+ if (object) {
+ const lldb::offset_t object_file_offset =
+ file_offset + object->file_offset;
+ if (object->file_offset < file_size && file_size > object_file_offset) {
+ if (ObjectFile::GetModuleSpecifications(
+ file, object_file_offset, file_size - object_file_offset,
+ specs)) {
+ ModuleSpec &spec =
+ specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1);
+ llvm::sys::TimePoint<> object_mod_time(
+ std::chrono::seconds(object->modification_time));
+ spec.GetObjectName() = object->ar_name;
+ spec.SetObjectOffset(object_file_offset);
+ spec.SetObjectSize(file_size - object_file_offset);
+ spec.GetObjectModificationTime() = object_mod_time;
+ }
+ }
+ }
+ }
+ }
+ const size_t end_count = specs.GetSize();
+ size_t num_specs_added = end_count - initial_count;
+ if (set_archive_arch && num_specs_added > 0) {
+ // The archive was created but we didn't have an architecture so we need to
+ // set it
+ for (size_t i = initial_count; i < end_count; ++i) {
+ ModuleSpec module_spec;
+ if (specs.GetModuleSpecAtIndex(i, module_spec)) {
+ if (module_spec.GetArchitecture().IsValid()) {
+ archive_sp->SetArchitecture(module_spec.GetArchitecture());
+ break;
+ }
+ }
+ }
+ }
+ return num_specs_added;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h b/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h
new file mode 100644
index 000000000000..5d9c01315a66
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h
@@ -0,0 +1,177 @@
+//===-- ObjectContainerBSDArchive.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ObjectContainerBSDArchive_h_
+#define liblldb_ObjectContainerBSDArchive_h_
+
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Symbol/ObjectContainer.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+
+#include "llvm/Support/Chrono.h"
+
+#include <map>
+#include <memory>
+#include <mutex>
+
+class ObjectContainerBSDArchive : public lldb_private::ObjectContainer {
+public:
+ ObjectContainerBSDArchive(const lldb::ModuleSP &module_sp,
+ lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ const lldb_private::FileSpec *file,
+ lldb::offset_t offset, lldb::offset_t length);
+
+ ~ObjectContainerBSDArchive() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::ObjectContainer *
+ CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const lldb_private::FileSpec *file,
+ lldb::offset_t offset, lldb::offset_t length);
+
+ static size_t GetModuleSpecifications(const lldb_private::FileSpec &file,
+ lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ lldb::offset_t file_offset,
+ lldb::offset_t length,
+ lldb_private::ModuleSpecList &specs);
+
+ static bool MagicBytesMatch(const lldb_private::DataExtractor &data);
+
+ // Member Functions
+ bool ParseHeader() override;
+
+ size_t GetNumObjects() const override {
+ if (m_archive_sp)
+ return m_archive_sp->GetNumObjects();
+ return 0;
+ }
+
+ void Dump(lldb_private::Stream *s) const override;
+
+ lldb::ObjectFileSP GetObjectFile(const lldb_private::FileSpec *file) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ struct Object {
+ Object();
+
+ void Clear();
+
+ lldb::offset_t Extract(const lldb_private::DataExtractor &data,
+ lldb::offset_t offset);
+ /// Object name in the archive.
+ lldb_private::ConstString ar_name;
+
+ /// Object modification time in the archive.
+ uint32_t modification_time;
+
+ /// Object user id in the archive.
+ uint16_t uid;
+
+ /// Object group id in the archive.
+ uint16_t gid;
+
+ /// Object octal file permissions in the archive.
+ uint16_t mode;
+
+ /// Object size in bytes in the archive.
+ uint32_t size;
+
+ /// File offset in bytes from the beginning of the file of the object data.
+ lldb::offset_t file_offset;
+
+ /// Length of the object data.
+ lldb::offset_t file_size;
+ };
+
+ class Archive {
+ public:
+ typedef std::shared_ptr<Archive> shared_ptr;
+ typedef std::multimap<lldb_private::FileSpec, shared_ptr> Map;
+
+ Archive(const lldb_private::ArchSpec &arch,
+ const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset,
+ lldb_private::DataExtractor &data);
+
+ ~Archive();
+
+ static Map &GetArchiveCache();
+
+ static std::recursive_mutex &GetArchiveCacheMutex();
+
+ static Archive::shared_ptr FindCachedArchive(
+ const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch,
+ const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset);
+
+ static Archive::shared_ptr ParseAndCacheArchiveForFile(
+ const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch,
+ const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset,
+ lldb_private::DataExtractor &data);
+
+ size_t GetNumObjects() const { return m_objects.size(); }
+
+ const Object *GetObjectAtIndex(size_t idx) {
+ if (idx < m_objects.size())
+ return &m_objects[idx];
+ return nullptr;
+ }
+
+ size_t ParseObjects();
+
+ Object *FindObject(lldb_private::ConstString object_name,
+ const llvm::sys::TimePoint<> &object_mod_time);
+
+ lldb::offset_t GetFileOffset() const { return m_file_offset; }
+
+ const llvm::sys::TimePoint<> &GetModificationTime() {
+ return m_modification_time;
+ }
+
+ const lldb_private::ArchSpec &GetArchitecture() const { return m_arch; }
+
+ void SetArchitecture(const lldb_private::ArchSpec &arch) { m_arch = arch; }
+
+ bool HasNoExternalReferences() const;
+
+ lldb_private::DataExtractor &GetData() { return m_data; }
+
+ protected:
+ typedef lldb_private::UniqueCStringMap<uint32_t> ObjectNameToIndexMap;
+ // Member Variables
+ lldb_private::ArchSpec m_arch;
+ llvm::sys::TimePoint<> m_modification_time;
+ lldb::offset_t m_file_offset;
+ std::vector<Object> m_objects;
+ ObjectNameToIndexMap m_object_name_to_index_map;
+ lldb_private::DataExtractor m_data; ///< The data for this object container
+ ///so we don't lose data if the .a files
+ ///gets modified
+ };
+
+ void SetArchive(Archive::shared_ptr &archive_sp);
+
+ Archive::shared_ptr m_archive_sp;
+};
+
+#endif // liblldb_ObjectContainerBSDArchive_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp
new file mode 100644
index 000000000000..d489eaf11115
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp
@@ -0,0 +1,411 @@
+//===-- BreakpadRecords.cpp ----------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace lldb_private;
+using namespace lldb_private::breakpad;
+
+namespace {
+enum class Token { Unknown, Module, Info, CodeID, File, Func, Public, Stack, CFI, Init };
+}
+
+template<typename T>
+static T stringTo(llvm::StringRef Str);
+
+template <> Token stringTo<Token>(llvm::StringRef Str) {
+ return llvm::StringSwitch<Token>(Str)
+ .Case("MODULE", Token::Module)
+ .Case("INFO", Token::Info)
+ .Case("CODE_ID", Token::CodeID)
+ .Case("FILE", Token::File)
+ .Case("FUNC", Token::Func)
+ .Case("PUBLIC", Token::Public)
+ .Case("STACK", Token::Stack)
+ .Case("CFI", Token::CFI)
+ .Case("INIT", Token::Init)
+ .Default(Token::Unknown);
+}
+
+template <>
+llvm::Triple::OSType stringTo<llvm::Triple::OSType>(llvm::StringRef Str) {
+ using llvm::Triple;
+ return llvm::StringSwitch<Triple::OSType>(Str)
+ .Case("Linux", Triple::Linux)
+ .Case("mac", Triple::MacOSX)
+ .Case("windows", Triple::Win32)
+ .Default(Triple::UnknownOS);
+}
+
+template <>
+llvm::Triple::ArchType stringTo<llvm::Triple::ArchType>(llvm::StringRef Str) {
+ using llvm::Triple;
+ return llvm::StringSwitch<Triple::ArchType>(Str)
+ .Case("arm", Triple::arm)
+ .Cases("arm64", "arm64e", Triple::aarch64)
+ .Case("mips", Triple::mips)
+ .Case("ppc", Triple::ppc)
+ .Case("ppc64", Triple::ppc64)
+ .Case("s390", Triple::systemz)
+ .Case("sparc", Triple::sparc)
+ .Case("sparcv9", Triple::sparcv9)
+ .Case("x86", Triple::x86)
+ .Case("x86_64", Triple::x86_64)
+ .Default(Triple::UnknownArch);
+}
+
+template<typename T>
+static T consume(llvm::StringRef &Str) {
+ llvm::StringRef Token;
+ std::tie(Token, Str) = getToken(Str);
+ return stringTo<T>(Token);
+}
+
+/// Return the number of hex digits needed to encode an (POD) object of a given
+/// type.
+template <typename T> static constexpr size_t hex_digits() {
+ return 2 * sizeof(T);
+}
+
+static UUID parseModuleId(llvm::Triple::OSType os, llvm::StringRef str) {
+ struct data_t {
+ using uuid_t = uint8_t[16];
+ uuid_t uuid;
+ llvm::support::ubig32_t age;
+ } data;
+ static_assert(sizeof(data) == 20, "");
+ // The textual module id encoding should be between 33 and 40 bytes long,
+ // depending on the size of the age field, which is of variable length.
+ // The first three chunks of the id are encoded in big endian, so we need to
+ // byte-swap those.
+ if (str.size() <= hex_digits<data_t::uuid_t>() ||
+ str.size() > hex_digits<data_t>())
+ return UUID();
+ if (!all_of(str, llvm::isHexDigit))
+ return UUID();
+
+ llvm::StringRef uuid_str = str.take_front(hex_digits<data_t::uuid_t>());
+ llvm::StringRef age_str = str.drop_front(hex_digits<data_t::uuid_t>());
+
+ llvm::copy(fromHex(uuid_str), data.uuid);
+ uint32_t age;
+ bool success = to_integer(age_str, age, 16);
+ assert(success);
+ (void)success;
+ data.age = age;
+
+ // On non-windows, the age field should always be zero, so we don't include to
+ // match the native uuid format of these platforms.
+ return UUID::fromData(&data, os == llvm::Triple::Win32 ? sizeof(data)
+ : sizeof(data.uuid));
+}
+
+llvm::Optional<Record::Kind> Record::classify(llvm::StringRef Line) {
+ Token Tok = consume<Token>(Line);
+ switch (Tok) {
+ case Token::Module:
+ return Record::Module;
+ case Token::Info:
+ return Record::Info;
+ case Token::File:
+ return Record::File;
+ case Token::Func:
+ return Record::Func;
+ case Token::Public:
+ return Record::Public;
+ case Token::Stack:
+ Tok = consume<Token>(Line);
+ switch (Tok) {
+ case Token::CFI:
+ return Record::StackCFI;
+ default:
+ return llvm::None;
+ }
+
+ case Token::Unknown:
+ // Optimistically assume that any unrecognised token means this is a line
+ // record, those don't have a special keyword and start directly with a
+ // hex number. CODE_ID should never be at the start of a line, but if it
+ // is, it can be treated the same way as a garbled line record.
+ return Record::Line;
+
+ case Token::CodeID:
+ case Token::CFI:
+ case Token::Init:
+ // These should never appear at the start of a valid record.
+ return llvm::None;
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+llvm::Optional<ModuleRecord> ModuleRecord::parse(llvm::StringRef Line) {
+ // MODULE Linux x86_64 E5894855C35DCCCCCCCCCCCCCCCCCCCC0 a.out
+ if (consume<Token>(Line) != Token::Module)
+ return llvm::None;
+
+ llvm::Triple::OSType OS = consume<llvm::Triple::OSType>(Line);
+ if (OS == llvm::Triple::UnknownOS)
+ return llvm::None;
+
+ llvm::Triple::ArchType Arch = consume<llvm::Triple::ArchType>(Line);
+ if (Arch == llvm::Triple::UnknownArch)
+ return llvm::None;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ UUID ID = parseModuleId(OS, Str);
+ if (!ID)
+ return llvm::None;
+
+ return ModuleRecord(OS, Arch, std::move(ID));
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const ModuleRecord &R) {
+ return OS << "MODULE " << llvm::Triple::getOSTypeName(R.OS) << " "
+ << llvm::Triple::getArchTypeName(R.Arch) << " "
+ << R.ID.GetAsString();
+}
+
+llvm::Optional<InfoRecord> InfoRecord::parse(llvm::StringRef Line) {
+ // INFO CODE_ID 554889E55DC3CCCCCCCCCCCCCCCCCCCC [a.exe]
+ if (consume<Token>(Line) != Token::Info)
+ return llvm::None;
+
+ if (consume<Token>(Line) != Token::CodeID)
+ return llvm::None;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ // If we don't have any text following the code ID (e.g. on linux), we should
+ // use this as the UUID. Otherwise, we should revert back to the module ID.
+ UUID ID;
+ if (Line.trim().empty()) {
+ if (Str.empty() || ID.SetFromStringRef(Str, Str.size() / 2) != Str.size())
+ return llvm::None;
+ }
+ return InfoRecord(std::move(ID));
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const InfoRecord &R) {
+ return OS << "INFO CODE_ID " << R.ID.GetAsString();
+}
+
+llvm::Optional<FileRecord> FileRecord::parse(llvm::StringRef Line) {
+ // FILE number name
+ if (consume<Token>(Line) != Token::File)
+ return llvm::None;
+
+ llvm::StringRef Str;
+ size_t Number;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Number))
+ return llvm::None;
+
+ llvm::StringRef Name = Line.trim();
+ if (Name.empty())
+ return llvm::None;
+
+ return FileRecord(Number, Name);
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const FileRecord &R) {
+ return OS << "FILE " << R.Number << " " << R.Name;
+}
+
+static bool parsePublicOrFunc(llvm::StringRef Line, bool &Multiple,
+ lldb::addr_t &Address, lldb::addr_t *Size,
+ lldb::addr_t &ParamSize, llvm::StringRef &Name) {
+ // PUBLIC [m] address param_size name
+ // or
+ // FUNC [m] address size param_size name
+
+ Token Tok = Size ? Token::Func : Token::Public;
+
+ if (consume<Token>(Line) != Tok)
+ return false;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ Multiple = Str == "m";
+
+ if (Multiple)
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Address, 16))
+ return false;
+
+ if (Tok == Token::Func) {
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, *Size, 16))
+ return false;
+ }
+
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, ParamSize, 16))
+ return false;
+
+ Name = Line.trim();
+ if (Name.empty())
+ return false;
+
+ return true;
+}
+
+llvm::Optional<FuncRecord> FuncRecord::parse(llvm::StringRef Line) {
+ bool Multiple;
+ lldb::addr_t Address, Size, ParamSize;
+ llvm::StringRef Name;
+
+ if (parsePublicOrFunc(Line, Multiple, Address, &Size, ParamSize, Name))
+ return FuncRecord(Multiple, Address, Size, ParamSize, Name);
+
+ return llvm::None;
+}
+
+bool breakpad::operator==(const FuncRecord &L, const FuncRecord &R) {
+ return L.Multiple == R.Multiple && L.Address == R.Address &&
+ L.Size == R.Size && L.ParamSize == R.ParamSize && L.Name == R.Name;
+}
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const FuncRecord &R) {
+ return OS << llvm::formatv("FUNC {0}{1:x-} {2:x-} {3:x-} {4}",
+ R.Multiple ? "m " : "", R.Address, R.Size,
+ R.ParamSize, R.Name);
+}
+
+llvm::Optional<LineRecord> LineRecord::parse(llvm::StringRef Line) {
+ lldb::addr_t Address;
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Address, 16))
+ return llvm::None;
+
+ lldb::addr_t Size;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Size, 16))
+ return llvm::None;
+
+ uint32_t LineNum;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, LineNum))
+ return llvm::None;
+
+ size_t FileNum;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, FileNum))
+ return llvm::None;
+
+ return LineRecord(Address, Size, LineNum, FileNum);
+}
+
+bool breakpad::operator==(const LineRecord &L, const LineRecord &R) {
+ return L.Address == R.Address && L.Size == R.Size && L.LineNum == R.LineNum &&
+ L.FileNum == R.FileNum;
+}
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const LineRecord &R) {
+ return OS << llvm::formatv("{0:x-} {1:x-} {2} {3}", R.Address, R.Size,
+ R.LineNum, R.FileNum);
+}
+
+llvm::Optional<PublicRecord> PublicRecord::parse(llvm::StringRef Line) {
+ bool Multiple;
+ lldb::addr_t Address, ParamSize;
+ llvm::StringRef Name;
+
+ if (parsePublicOrFunc(Line, Multiple, Address, nullptr, ParamSize, Name))
+ return PublicRecord(Multiple, Address, ParamSize, Name);
+
+ return llvm::None;
+}
+
+bool breakpad::operator==(const PublicRecord &L, const PublicRecord &R) {
+ return L.Multiple == R.Multiple && L.Address == R.Address &&
+ L.ParamSize == R.ParamSize && L.Name == R.Name;
+}
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const PublicRecord &R) {
+ return OS << llvm::formatv("PUBLIC {0}{1:x-} {2:x-} {3}",
+ R.Multiple ? "m " : "", R.Address, R.ParamSize,
+ R.Name);
+}
+
+llvm::Optional<StackCFIRecord> StackCFIRecord::parse(llvm::StringRef Line) {
+ // STACK CFI INIT address size reg1: expr1 reg2: expr2 ...
+ // or
+ // STACK CFI address reg1: expr1 reg2: expr2 ...
+ // No token in exprN ends with a colon.
+
+ if (consume<Token>(Line) != Token::Stack)
+ return llvm::None;
+ if (consume<Token>(Line) != Token::CFI)
+ return llvm::None;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+
+ bool IsInitRecord = stringTo<Token>(Str) == Token::Init;
+ if (IsInitRecord)
+ std::tie(Str, Line) = getToken(Line);
+
+ lldb::addr_t Address;
+ if (!to_integer(Str, Address, 16))
+ return llvm::None;
+
+ llvm::Optional<lldb::addr_t> Size;
+ if (IsInitRecord) {
+ Size.emplace();
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, *Size, 16))
+ return llvm::None;
+ }
+
+ return StackCFIRecord(Address, Size, Line.trim());
+}
+
+bool breakpad::operator==(const StackCFIRecord &L, const StackCFIRecord &R) {
+ return L.Address == R.Address && L.Size == R.Size &&
+ L.UnwindRules == R.UnwindRules;
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const StackCFIRecord &R) {
+ OS << "STACK CFI ";
+ if (R.Size)
+ OS << "INIT ";
+ OS << llvm::formatv("{0:x-} ", R.Address);
+ if (R.Size)
+ OS << llvm::formatv("{0:x-} ", *R.Size);
+ return OS << " " << R.UnwindRules;
+}
+
+llvm::StringRef breakpad::toString(Record::Kind K) {
+ switch (K) {
+ case Record::Module:
+ return "MODULE";
+ case Record::Info:
+ return "INFO";
+ case Record::File:
+ return "FILE";
+ case Record::Func:
+ return "FUNC";
+ case Record::Line:
+ return "LINE";
+ case Record::Public:
+ return "PUBLIC";
+ case Record::StackCFI:
+ return "STACK CFI";
+ }
+ llvm_unreachable("Unknown record kind!");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h
new file mode 100644
index 000000000000..5d5cdb319c10
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h
@@ -0,0 +1,163 @@
+//===-- BreakpadRecords.h ------------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
+#define LLDB_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
+
+#include "lldb/Utility/UUID.h"
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/FormatProviders.h"
+
+namespace lldb_private {
+namespace breakpad {
+
+class Record {
+public:
+ enum Kind { Module, Info, File, Func, Line, Public, StackCFI };
+
+ /// Attempt to guess the kind of the record present in the argument without
+ /// doing a full parse. The returned kind will always be correct for valid
+ /// records, but the full parse can still fail in case of corrupted input.
+ static llvm::Optional<Kind> classify(llvm::StringRef Line);
+
+protected:
+ Record(Kind K) : TheKind(K) {}
+
+ ~Record() = default;
+
+public:
+ Kind getKind() { return TheKind; }
+
+private:
+ Kind TheKind;
+};
+
+llvm::StringRef toString(Record::Kind K);
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Record::Kind K) {
+ OS << toString(K);
+ return OS;
+}
+
+class ModuleRecord : public Record {
+public:
+ static llvm::Optional<ModuleRecord> parse(llvm::StringRef Line);
+ ModuleRecord(llvm::Triple::OSType OS, llvm::Triple::ArchType Arch, UUID ID)
+ : Record(Module), OS(OS), Arch(Arch), ID(std::move(ID)) {}
+
+ llvm::Triple::OSType OS;
+ llvm::Triple::ArchType Arch;
+ UUID ID;
+};
+
+inline bool operator==(const ModuleRecord &L, const ModuleRecord &R) {
+ return L.OS == R.OS && L.Arch == R.Arch && L.ID == R.ID;
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ModuleRecord &R);
+
+class InfoRecord : public Record {
+public:
+ static llvm::Optional<InfoRecord> parse(llvm::StringRef Line);
+ InfoRecord(UUID ID) : Record(Info), ID(std::move(ID)) {}
+
+ UUID ID;
+};
+
+inline bool operator==(const InfoRecord &L, const InfoRecord &R) {
+ return L.ID == R.ID;
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const InfoRecord &R);
+
+class FileRecord : public Record {
+public:
+ static llvm::Optional<FileRecord> parse(llvm::StringRef Line);
+ FileRecord(size_t Number, llvm::StringRef Name)
+ : Record(File), Number(Number), Name(Name) {}
+
+ size_t Number;
+ llvm::StringRef Name;
+};
+
+inline bool operator==(const FileRecord &L, const FileRecord &R) {
+ return L.Number == R.Number && L.Name == R.Name;
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const FileRecord &R);
+
+class FuncRecord : public Record {
+public:
+ static llvm::Optional<FuncRecord> parse(llvm::StringRef Line);
+ FuncRecord(bool Multiple, lldb::addr_t Address, lldb::addr_t Size,
+ lldb::addr_t ParamSize, llvm::StringRef Name)
+ : Record(Module), Multiple(Multiple), Address(Address), Size(Size),
+ ParamSize(ParamSize), Name(Name) {}
+
+ bool Multiple;
+ lldb::addr_t Address;
+ lldb::addr_t Size;
+ lldb::addr_t ParamSize;
+ llvm::StringRef Name;
+};
+
+bool operator==(const FuncRecord &L, const FuncRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const FuncRecord &R);
+
+class LineRecord : public Record {
+public:
+ static llvm::Optional<LineRecord> parse(llvm::StringRef Line);
+ LineRecord(lldb::addr_t Address, lldb::addr_t Size, uint32_t LineNum,
+ size_t FileNum)
+ : Record(Line), Address(Address), Size(Size), LineNum(LineNum),
+ FileNum(FileNum) {}
+
+ lldb::addr_t Address;
+ lldb::addr_t Size;
+ uint32_t LineNum;
+ size_t FileNum;
+};
+
+bool operator==(const LineRecord &L, const LineRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const LineRecord &R);
+
+class PublicRecord : public Record {
+public:
+ static llvm::Optional<PublicRecord> parse(llvm::StringRef Line);
+ PublicRecord(bool Multiple, lldb::addr_t Address, lldb::addr_t ParamSize,
+ llvm::StringRef Name)
+ : Record(Module), Multiple(Multiple), Address(Address),
+ ParamSize(ParamSize), Name(Name) {}
+
+ bool Multiple;
+ lldb::addr_t Address;
+ lldb::addr_t ParamSize;
+ llvm::StringRef Name;
+};
+
+bool operator==(const PublicRecord &L, const PublicRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PublicRecord &R);
+
+class StackCFIRecord : public Record {
+public:
+ static llvm::Optional<StackCFIRecord> parse(llvm::StringRef Line);
+ StackCFIRecord(lldb::addr_t Address, llvm::Optional<lldb::addr_t> Size,
+ llvm::StringRef UnwindRules)
+ : Record(StackCFI), Address(Address), Size(Size),
+ UnwindRules(UnwindRules) {}
+
+ lldb::addr_t Address;
+ llvm::Optional<lldb::addr_t> Size;
+ llvm::StringRef UnwindRules;
+};
+
+bool operator==(const StackCFIRecord &L, const StackCFIRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const StackCFIRecord &R);
+
+} // namespace breakpad
+} // namespace lldb_private
+
+#endif // LLDB_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp
new file mode 100644
index 000000000000..60dd9f9cecf0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp
@@ -0,0 +1,168 @@
+//===-- ObjectFileBreakpad.cpp -------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::breakpad;
+
+namespace {
+struct Header {
+ ArchSpec arch;
+ UUID uuid;
+ static llvm::Optional<Header> parse(llvm::StringRef text);
+};
+} // namespace
+
+llvm::Optional<Header> Header::parse(llvm::StringRef text) {
+ llvm::StringRef line;
+ std::tie(line, text) = text.split('\n');
+ auto Module = ModuleRecord::parse(line);
+ if (!Module)
+ return llvm::None;
+
+ llvm::Triple triple;
+ triple.setArch(Module->Arch);
+ triple.setOS(Module->OS);
+
+ std::tie(line, text) = text.split('\n');
+
+ auto Info = InfoRecord::parse(line);
+ UUID uuid = Info && Info->ID ? Info->ID : Module->ID;
+ return Header{ArchSpec(triple), std::move(uuid)};
+}
+
+void ObjectFileBreakpad::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ CreateMemoryInstance, GetModuleSpecifications);
+}
+
+void ObjectFileBreakpad::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString ObjectFileBreakpad::GetPluginNameStatic() {
+ static ConstString g_name("breakpad");
+ return g_name;
+}
+
+ObjectFile *ObjectFileBreakpad::CreateInstance(
+ const ModuleSP &module_sp, DataBufferSP &data_sp, offset_t data_offset,
+ const FileSpec *file, offset_t file_offset, offset_t length) {
+ if (!data_sp) {
+ data_sp = MapFileData(*file, length, file_offset);
+ if (!data_sp)
+ return nullptr;
+ data_offset = 0;
+ }
+ auto text = toStringRef(data_sp->GetData());
+ llvm::Optional<Header> header = Header::parse(text);
+ if (!header)
+ return nullptr;
+
+ // Update the data to contain the entire file if it doesn't already
+ if (data_sp->GetByteSize() < length) {
+ data_sp = MapFileData(*file, length, file_offset);
+ if (!data_sp)
+ return nullptr;
+ data_offset = 0;
+ }
+
+ return new ObjectFileBreakpad(module_sp, data_sp, data_offset, file,
+ file_offset, length, std::move(header->arch),
+ std::move(header->uuid));
+}
+
+ObjectFile *ObjectFileBreakpad::CreateMemoryInstance(
+ const ModuleSP &module_sp, DataBufferSP &data_sp,
+ const ProcessSP &process_sp, addr_t header_addr) {
+ return nullptr;
+}
+
+size_t ObjectFileBreakpad::GetModuleSpecifications(
+ const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,
+ offset_t file_offset, offset_t length, ModuleSpecList &specs) {
+ auto text = toStringRef(data_sp->GetData());
+ llvm::Optional<Header> header = Header::parse(text);
+ if (!header)
+ return 0;
+ ModuleSpec spec(file, std::move(header->arch));
+ spec.GetUUID() = std::move(header->uuid);
+ specs.Append(spec);
+ return 1;
+}
+
+ObjectFileBreakpad::ObjectFileBreakpad(const ModuleSP &module_sp,
+ DataBufferSP &data_sp,
+ offset_t data_offset,
+ const FileSpec *file, offset_t offset,
+ offset_t length, ArchSpec arch,
+ UUID uuid)
+ : ObjectFile(module_sp, file, offset, length, data_sp, data_offset),
+ m_arch(std::move(arch)), m_uuid(std::move(uuid)) {}
+
+bool ObjectFileBreakpad::ParseHeader() {
+ // We already parsed the header during initialization.
+ return true;
+}
+
+Symtab *ObjectFileBreakpad::GetSymtab() {
+ // TODO
+ return nullptr;
+}
+
+void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) {
+ if (m_sections_up)
+ return;
+ m_sections_up = llvm::make_unique<SectionList>();
+
+ llvm::Optional<Record::Kind> current_section;
+ offset_t section_start;
+ llvm::StringRef text = toStringRef(m_data.GetData());
+ uint32_t next_section_id = 1;
+ auto maybe_add_section = [&](const uint8_t *end_ptr) {
+ if (!current_section)
+ return; // We have been called before parsing the first line.
+
+ offset_t end_offset = end_ptr - m_data.GetDataStart();
+ auto section_sp = std::make_shared<Section>(
+ GetModule(), this, next_section_id++,
+ ConstString(toString(*current_section)), eSectionTypeOther,
+ /*file_vm_addr*/ 0, /*vm_size*/ 0, section_start,
+ end_offset - section_start, /*log2align*/ 0, /*flags*/ 0);
+ m_sections_up->AddSection(section_sp);
+ unified_section_list.AddSection(section_sp);
+ };
+ while (!text.empty()) {
+ llvm::StringRef line;
+ std::tie(line, text) = text.split('\n');
+
+ llvm::Optional<Record::Kind> next_section = Record::classify(line);
+ if (next_section == Record::Line) {
+ // Line records logically belong to the preceding Func record, so we put
+ // them in the same section.
+ next_section = Record::Func;
+ }
+ if (next_section == current_section)
+ continue;
+
+ // Changing sections, finish off the previous one, if there was any.
+ maybe_add_section(line.bytes_begin());
+ // And start a new one.
+ current_section = next_section;
+ section_start = line.bytes_begin() - m_data.GetDataStart();
+ }
+ // Finally, add the last section.
+ maybe_add_section(m_data.GetDataEnd());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h
new file mode 100644
index 000000000000..e8885e0cc898
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h
@@ -0,0 +1,101 @@
+//===-- ObjectFileBreakpad.h ---------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H
+#define LLDB_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H
+
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/ArchSpec.h"
+
+namespace lldb_private {
+namespace breakpad {
+
+class ObjectFileBreakpad : public ObjectFile {
+public:
+ // Static Functions
+ static void Initialize();
+ static void Terminate();
+
+ static ConstString GetPluginNameStatic();
+ static const char *GetPluginDescriptionStatic() {
+ return "Breakpad object file reader.";
+ }
+
+ static ObjectFile *
+ CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t length);
+
+ static ObjectFile *CreateMemoryInstance(const lldb::ModuleSP &module_sp,
+ lldb::DataBufferSP &data_sp,
+ const lldb::ProcessSP &process_sp,
+ lldb::addr_t header_addr);
+
+ static size_t GetModuleSpecifications(const FileSpec &file,
+ lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ lldb::offset_t file_offset,
+ lldb::offset_t length,
+ ModuleSpecList &specs);
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override { return GetPluginNameStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ // ObjectFile Protocol.
+
+ bool ParseHeader() override;
+
+ lldb::ByteOrder GetByteOrder() const override {
+ return m_arch.GetByteOrder();
+ }
+
+ bool IsExecutable() const override { return false; }
+
+ uint32_t GetAddressByteSize() const override {
+ return m_arch.GetAddressByteSize();
+ }
+
+ AddressClass GetAddressClass(lldb::addr_t file_addr) override {
+ return AddressClass::eInvalid;
+ }
+
+ Symtab *GetSymtab() override;
+
+ bool IsStripped() override { return false; }
+
+ void CreateSections(SectionList &unified_section_list) override;
+
+ void Dump(Stream *s) override {}
+
+ ArchSpec GetArchitecture() override { return m_arch; }
+
+ UUID GetUUID() override { return m_uuid; }
+
+ FileSpecList GetDebugSymbolFilePaths() override { return FileSpecList(); }
+
+ uint32_t GetDependentModules(FileSpecList &files) override { return 0; }
+
+ Type CalculateType() override { return eTypeDebugInfo; }
+
+ Strata CalculateStrata() override { return eStrataUser; }
+
+private:
+ ArchSpec m_arch;
+ UUID m_uuid;
+
+ ObjectFileBreakpad(const lldb::ModuleSP &module_sp,
+ lldb::DataBufferSP &data_sp, lldb::offset_t data_offset,
+ const FileSpec *file, lldb::offset_t offset,
+ lldb::offset_t length, ArchSpec arch, UUID uuid);
+};
+
+} // namespace breakpad
+} // namespace lldb_private
+#endif // LLDB_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp
new file mode 100644
index 000000000000..aa9871071b0e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp
@@ -0,0 +1,435 @@
+//===-- ELFHeader.cpp ----------------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstring>
+
+#include "lldb/Core/Section.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Stream.h"
+
+#include "ELFHeader.h"
+
+using namespace elf;
+using namespace lldb;
+using namespace llvm::ELF;
+
+// Static utility functions.
+//
+// GetMaxU64 and GetMaxS64 wrap the similarly named methods from DataExtractor
+// with error handling code and provide for parsing a sequence of values.
+static bool GetMaxU64(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset, uint64_t *value,
+ uint32_t byte_size) {
+ const lldb::offset_t saved_offset = *offset;
+ *value = data.GetMaxU64(offset, byte_size);
+ return *offset != saved_offset;
+}
+
+static bool GetMaxU64(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset, uint64_t *value,
+ uint32_t byte_size, uint32_t count) {
+ lldb::offset_t saved_offset = *offset;
+
+ for (uint32_t i = 0; i < count; ++i, ++value) {
+ if (!GetMaxU64(data, offset, value, byte_size)) {
+ *offset = saved_offset;
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool GetMaxS64(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset, int64_t *value,
+ uint32_t byte_size) {
+ const lldb::offset_t saved_offset = *offset;
+ *value = data.GetMaxS64(offset, byte_size);
+ return *offset != saved_offset;
+}
+
+static bool GetMaxS64(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset, int64_t *value,
+ uint32_t byte_size, uint32_t count) {
+ lldb::offset_t saved_offset = *offset;
+
+ for (uint32_t i = 0; i < count; ++i, ++value) {
+ if (!GetMaxS64(data, offset, value, byte_size)) {
+ *offset = saved_offset;
+ return false;
+ }
+ }
+ return true;
+}
+
+// ELFHeader
+
+ELFHeader::ELFHeader() { memset(this, 0, sizeof(ELFHeader)); }
+
+ByteOrder ELFHeader::GetByteOrder() const {
+ if (e_ident[EI_DATA] == ELFDATA2MSB)
+ return eByteOrderBig;
+ if (e_ident[EI_DATA] == ELFDATA2LSB)
+ return eByteOrderLittle;
+ return eByteOrderInvalid;
+}
+
+bool ELFHeader::HasHeaderExtension() const {
+ bool result = false;
+
+ // Check if any of these values looks like sentinel.
+ result |= e_phnum_hdr == 0xFFFF; // PN_XNUM
+ result |= e_shnum_hdr == SHN_UNDEF;
+ result |= e_shstrndx_hdr == SHN_XINDEX;
+
+ // If header extension is present, the section offset cannot be null.
+ result &= e_shoff != 0;
+
+ // Done.
+ return result;
+}
+
+void ELFHeader::ParseHeaderExtension(lldb_private::DataExtractor &data) {
+ // Extract section #0 header.
+ ELFSectionHeader section_zero;
+ lldb::offset_t offset = 0;
+ lldb_private::DataExtractor sh_data(data, e_shoff, e_shentsize);
+ bool ok = section_zero.Parse(sh_data, &offset);
+
+ // If we succeeded, fix the header.
+ if (ok) {
+ if (e_phnum_hdr == 0xFFFF) // PN_XNUM
+ e_phnum = section_zero.sh_info;
+ if (e_shnum_hdr == SHN_UNDEF)
+ e_shnum = section_zero.sh_size;
+ if (e_shstrndx_hdr == SHN_XINDEX)
+ e_shstrndx = section_zero.sh_link;
+ }
+}
+
+bool ELFHeader::Parse(lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ // Read e_ident. This provides byte order and address size info.
+ if (data.GetU8(offset, &e_ident, EI_NIDENT) == nullptr)
+ return false;
+
+ const unsigned byte_size = Is32Bit() ? 4 : 8;
+ data.SetByteOrder(GetByteOrder());
+ data.SetAddressByteSize(byte_size);
+
+ // Read e_type and e_machine.
+ if (data.GetU16(offset, &e_type, 2) == nullptr)
+ return false;
+
+ // Read e_version.
+ if (data.GetU32(offset, &e_version, 1) == nullptr)
+ return false;
+
+ // Read e_entry, e_phoff and e_shoff.
+ if (!GetMaxU64(data, offset, &e_entry, byte_size, 3))
+ return false;
+
+ // Read e_flags.
+ if (data.GetU32(offset, &e_flags, 1) == nullptr)
+ return false;
+
+ // Read e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum and e_shstrndx.
+ if (data.GetU16(offset, &e_ehsize, 6) == nullptr)
+ return false;
+
+ // Initialize e_phnum, e_shnum, and e_shstrndx with the values read from the
+ // header.
+ e_phnum = e_phnum_hdr;
+ e_shnum = e_shnum_hdr;
+ e_shstrndx = e_shstrndx_hdr;
+
+ // See if we have extended header in section #0.
+ if (HasHeaderExtension())
+ ParseHeaderExtension(data);
+
+ return true;
+}
+
+bool ELFHeader::MagicBytesMatch(const uint8_t *magic) {
+ return memcmp(magic, ElfMagic, strlen(ElfMagic)) == 0;
+}
+
+unsigned ELFHeader::AddressSizeInBytes(const uint8_t *magic) {
+ unsigned address_size = 0;
+
+ switch (magic[EI_CLASS]) {
+ case ELFCLASS32:
+ address_size = 4;
+ break;
+
+ case ELFCLASS64:
+ address_size = 8;
+ break;
+ }
+ return address_size;
+}
+
+unsigned ELFHeader::GetRelocationJumpSlotType() const {
+ unsigned slot = 0;
+
+ switch (e_machine) {
+ default:
+ assert(false && "architecture not supported");
+ break;
+ case EM_PPC:
+ slot = R_PPC_JMP_SLOT;
+ break;
+ case EM_PPC64:
+ slot = R_PPC64_JMP_SLOT;
+ break;
+ case EM_386:
+ case EM_IAMCU: // FIXME: is this correct?
+ slot = R_386_JUMP_SLOT;
+ break;
+ case EM_X86_64:
+ slot = R_X86_64_JUMP_SLOT;
+ break;
+ case EM_ARM:
+ slot = R_ARM_JUMP_SLOT;
+ break;
+ case EM_HEXAGON:
+ slot = R_HEX_JMP_SLOT;
+ break;
+ case EM_AARCH64:
+ slot = R_AARCH64_JUMP_SLOT;
+ break;
+ case EM_MIPS:
+ slot = R_MIPS_JUMP_SLOT;
+ break;
+ case EM_S390:
+ slot = R_390_JMP_SLOT;
+ break;
+ }
+
+ return slot;
+}
+
+// ELFSectionHeader
+
+ELFSectionHeader::ELFSectionHeader() {
+ memset(this, 0, sizeof(ELFSectionHeader));
+}
+
+bool ELFSectionHeader::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const unsigned byte_size = data.GetAddressByteSize();
+
+ // Read sh_name and sh_type.
+ if (data.GetU32(offset, &sh_name, 2) == nullptr)
+ return false;
+
+ // Read sh_flags.
+ if (!GetMaxU64(data, offset, &sh_flags, byte_size))
+ return false;
+
+ // Read sh_addr, sh_off and sh_size.
+ if (!GetMaxU64(data, offset, &sh_addr, byte_size, 3))
+ return false;
+
+ // Read sh_link and sh_info.
+ if (data.GetU32(offset, &sh_link, 2) == nullptr)
+ return false;
+
+ // Read sh_addralign and sh_entsize.
+ if (!GetMaxU64(data, offset, &sh_addralign, byte_size, 2))
+ return false;
+
+ return true;
+}
+
+// ELFSymbol
+
+ELFSymbol::ELFSymbol() { memset(this, 0, sizeof(ELFSymbol)); }
+
+#define ENUM_TO_CSTR(e) \
+ case e: \
+ return #e
+
+const char *ELFSymbol::bindingToCString(unsigned char binding) {
+ switch (binding) {
+ ENUM_TO_CSTR(STB_LOCAL);
+ ENUM_TO_CSTR(STB_GLOBAL);
+ ENUM_TO_CSTR(STB_WEAK);
+ ENUM_TO_CSTR(STB_LOOS);
+ ENUM_TO_CSTR(STB_HIOS);
+ ENUM_TO_CSTR(STB_LOPROC);
+ ENUM_TO_CSTR(STB_HIPROC);
+ }
+ return "";
+}
+
+const char *ELFSymbol::typeToCString(unsigned char type) {
+ switch (type) {
+ ENUM_TO_CSTR(STT_NOTYPE);
+ ENUM_TO_CSTR(STT_OBJECT);
+ ENUM_TO_CSTR(STT_FUNC);
+ ENUM_TO_CSTR(STT_SECTION);
+ ENUM_TO_CSTR(STT_FILE);
+ ENUM_TO_CSTR(STT_COMMON);
+ ENUM_TO_CSTR(STT_TLS);
+ ENUM_TO_CSTR(STT_GNU_IFUNC);
+ ENUM_TO_CSTR(STT_HIOS);
+ ENUM_TO_CSTR(STT_LOPROC);
+ ENUM_TO_CSTR(STT_HIPROC);
+ }
+ return "";
+}
+
+const char *ELFSymbol::sectionIndexToCString(
+ elf_half shndx, const lldb_private::SectionList *section_list) {
+ switch (shndx) {
+ ENUM_TO_CSTR(SHN_UNDEF);
+ ENUM_TO_CSTR(SHN_LOPROC);
+ ENUM_TO_CSTR(SHN_HIPROC);
+ ENUM_TO_CSTR(SHN_LOOS);
+ ENUM_TO_CSTR(SHN_HIOS);
+ ENUM_TO_CSTR(SHN_ABS);
+ ENUM_TO_CSTR(SHN_COMMON);
+ ENUM_TO_CSTR(SHN_XINDEX);
+ default: {
+ const lldb_private::Section *section =
+ section_list->GetSectionAtIndex(shndx).get();
+ if (section)
+ return section->GetName().AsCString("");
+ } break;
+ }
+ return "";
+}
+
+void ELFSymbol::Dump(lldb_private::Stream *s, uint32_t idx,
+ const lldb_private::DataExtractor *strtab_data,
+ const lldb_private::SectionList *section_list) {
+ s->Printf("[%3u] 0x%16.16" PRIx64 " 0x%16.16" PRIx64
+ " 0x%8.8x 0x%2.2x (%-10s %-13s) 0x%2.2x 0x%4.4x (%-10s) %s\n",
+ idx, st_value, st_size, st_name, st_info,
+ bindingToCString(getBinding()), typeToCString(getType()), st_other,
+ st_shndx, sectionIndexToCString(st_shndx, section_list),
+ strtab_data ? strtab_data->PeekCStr(st_name) : "");
+}
+
+bool ELFSymbol::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const unsigned byte_size = data.GetAddressByteSize();
+ const bool parsing_32 = byte_size == 4;
+
+ // Read st_name.
+ if (data.GetU32(offset, &st_name, 1) == nullptr)
+ return false;
+
+ if (parsing_32) {
+ // Read st_value and st_size.
+ if (!GetMaxU64(data, offset, &st_value, byte_size, 2))
+ return false;
+
+ // Read st_info and st_other.
+ if (data.GetU8(offset, &st_info, 2) == nullptr)
+ return false;
+
+ // Read st_shndx.
+ if (data.GetU16(offset, &st_shndx, 1) == nullptr)
+ return false;
+ } else {
+ // Read st_info and st_other.
+ if (data.GetU8(offset, &st_info, 2) == nullptr)
+ return false;
+
+ // Read st_shndx.
+ if (data.GetU16(offset, &st_shndx, 1) == nullptr)
+ return false;
+
+ // Read st_value and st_size.
+ if (data.GetU64(offset, &st_value, 2) == nullptr)
+ return false;
+ }
+ return true;
+}
+
+// ELFProgramHeader
+
+ELFProgramHeader::ELFProgramHeader() {
+ memset(this, 0, sizeof(ELFProgramHeader));
+}
+
+bool ELFProgramHeader::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const uint32_t byte_size = data.GetAddressByteSize();
+ const bool parsing_32 = byte_size == 4;
+
+ // Read p_type;
+ if (data.GetU32(offset, &p_type, 1) == nullptr)
+ return false;
+
+ if (parsing_32) {
+ // Read p_offset, p_vaddr, p_paddr, p_filesz and p_memsz.
+ if (!GetMaxU64(data, offset, &p_offset, byte_size, 5))
+ return false;
+
+ // Read p_flags.
+ if (data.GetU32(offset, &p_flags, 1) == nullptr)
+ return false;
+
+ // Read p_align.
+ if (!GetMaxU64(data, offset, &p_align, byte_size))
+ return false;
+ } else {
+ // Read p_flags.
+ if (data.GetU32(offset, &p_flags, 1) == nullptr)
+ return false;
+
+ // Read p_offset, p_vaddr, p_paddr, p_filesz, p_memsz and p_align.
+ if (!GetMaxU64(data, offset, &p_offset, byte_size, 6))
+ return false;
+ }
+
+ return true;
+}
+
+// ELFDynamic
+
+ELFDynamic::ELFDynamic() { memset(this, 0, sizeof(ELFDynamic)); }
+
+bool ELFDynamic::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const unsigned byte_size = data.GetAddressByteSize();
+ return GetMaxS64(data, offset, &d_tag, byte_size, 2);
+}
+
+// ELFRel
+
+ELFRel::ELFRel() { memset(this, 0, sizeof(ELFRel)); }
+
+bool ELFRel::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const unsigned byte_size = data.GetAddressByteSize();
+
+ // Read r_offset and r_info.
+ return GetMaxU64(data, offset, &r_offset, byte_size, 2) != false;
+}
+
+// ELFRela
+
+ELFRela::ELFRela() { memset(this, 0, sizeof(ELFRela)); }
+
+bool ELFRela::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const unsigned byte_size = data.GetAddressByteSize();
+
+ // Read r_offset and r_info.
+ if (!GetMaxU64(data, offset, &r_offset, byte_size, 2))
+ return false;
+
+ // Read r_addend;
+ if (!GetMaxS64(data, offset, &r_addend, byte_size))
+ return false;
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h
new file mode 100644
index 000000000000..bb228e269d40
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h
@@ -0,0 +1,394 @@
+//===-- ELFHeader.h ------------------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Generic structures and typedefs for ELF files.
+///
+/// This file provides definitions for the various entities comprising an ELF
+/// file. The structures are generic in the sense that they do not correspond
+/// to the exact binary layout of an ELF, but can be used to hold the
+/// information present in both 32 and 64 bit variants of the format. Each
+/// entity provides a \c Parse method which is capable of transparently
+/// reading both 32 and 64 bit instances of the object.
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ELFHeader_h_
+#define liblldb_ELFHeader_h_
+
+#include "llvm/BinaryFormat/ELF.h"
+
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+class DataExtractor;
+} // End namespace lldb_private.
+
+namespace elf {
+
+/// \name ELF type definitions.
+///
+/// Types used to represent the various components of ELF structures. All
+/// types are signed or unsigned integral types wide enough to hold values
+/// from both
+/// 32 and 64 bit ELF variants.
+//@{
+typedef uint64_t elf_addr;
+typedef uint64_t elf_off;
+typedef uint16_t elf_half;
+typedef uint32_t elf_word;
+typedef int32_t elf_sword;
+typedef uint64_t elf_size;
+typedef uint64_t elf_xword;
+typedef int64_t elf_sxword;
+//@}
+
+/// \class ELFHeader
+/// Generic representation of an ELF file header.
+///
+/// This object is used to identify the general attributes on an ELF file and
+/// to locate additional sections within the file.
+struct ELFHeader {
+ unsigned char e_ident[llvm::ELF::EI_NIDENT]; ///< ELF file identification.
+ elf_addr e_entry; ///< Virtual address program entry point.
+ elf_off e_phoff; ///< File offset of program header table.
+ elf_off e_shoff; ///< File offset of section header table.
+ elf_word e_flags; ///< Processor specific flags.
+ elf_word e_version; ///< Version of object file (always 1).
+ elf_half e_type; ///< Object file type.
+ elf_half e_machine; ///< Target architecture.
+ elf_half e_ehsize; ///< Byte size of the ELF header.
+ elf_half e_phentsize; ///< Size of a program header table entry.
+ elf_half e_phnum_hdr; ///< Number of program header entries.
+ elf_half e_shentsize; ///< Size of a section header table entry.
+ elf_half e_shnum_hdr; ///< Number of section header entries.
+ elf_half e_shstrndx_hdr; ///< String table section index.
+
+ // In some cases these numbers do not fit in 16 bits and they are
+ // stored outside of the header in section #0. Here are the actual
+ // values.
+ elf_word e_phnum; ///< Number of program header entries.
+ elf_word e_shnum; ///< Number of section header entries.
+ elf_word e_shstrndx; ///< String table section index.
+
+ ELFHeader();
+
+ /// Returns true if this is a 32 bit ELF file header.
+ ///
+ /// \return
+ /// True if this is a 32 bit ELF file header.
+ bool Is32Bit() const {
+ return e_ident[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS32;
+ }
+
+ /// Returns true if this is a 64 bit ELF file header.
+ ///
+ /// \return
+ /// True if this is a 64 bit ELF file header.
+ bool Is64Bit() const {
+ return e_ident[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS64;
+ }
+
+ /// The byte order of this ELF file header.
+ ///
+ /// \return
+ /// The byte order of this ELF file as described by the header.
+ lldb::ByteOrder GetByteOrder() const;
+
+ /// The jump slot relocation type of this ELF.
+ unsigned GetRelocationJumpSlotType() const;
+
+ /// Check if there should be header extension in section header #0
+ ///
+ /// \return
+ /// True if parsing the ELFHeader requires reading header extension
+ /// and false otherwise.
+ bool HasHeaderExtension() const;
+
+ /// Parse an ELFHeader entry starting at position \p offset and update the
+ /// data extractor with the address size and byte order attributes as
+ /// defined by the header.
+ ///
+ /// \param[in,out] data
+ /// The DataExtractor to read from. Updated with the address size and
+ /// byte order attributes appropriate to this header.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFHeader was successfully read and false
+ /// otherwise.
+ bool Parse(lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ /// Examines at most EI_NIDENT bytes starting from the given pointer and
+ /// determines if the magic ELF identification exists.
+ ///
+ /// \return
+ /// True if the given sequence of bytes identifies an ELF file.
+ static bool MagicBytesMatch(const uint8_t *magic);
+
+ /// Examines at most EI_NIDENT bytes starting from the given address and
+ /// determines the address size of the underlying ELF file. This function
+ /// should only be called on an pointer for which MagicBytesMatch returns
+ /// true.
+ ///
+ /// \return
+ /// The number of bytes forming an address in the ELF file (either 4 or
+ /// 8), else zero if the address size could not be determined.
+ static unsigned AddressSizeInBytes(const uint8_t *magic);
+
+private:
+
+ /// Parse an ELFHeader header extension entry. This method is called by
+ /// Parse().
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from.
+ void ParseHeaderExtension(lldb_private::DataExtractor &data);
+};
+
+/// \class ELFSectionHeader
+/// Generic representation of an ELF section header.
+struct ELFSectionHeader {
+ elf_word sh_name; ///< Section name string index.
+ elf_word sh_type; ///< Section type.
+ elf_xword sh_flags; ///< Section attributes.
+ elf_addr sh_addr; ///< Virtual address of the section in memory.
+ elf_off sh_offset; ///< Start of section from beginning of file.
+ elf_xword sh_size; ///< Number of bytes occupied in the file.
+ elf_word sh_link; ///< Index of associated section.
+ elf_word sh_info; ///< Extra section info (overloaded).
+ elf_xword sh_addralign; ///< Power of two alignment constraint.
+ elf_xword sh_entsize; ///< Byte size of each section entry.
+
+ ELFSectionHeader();
+
+ /// Parse an ELFSectionHeader entry from the given DataExtracter starting at
+ /// position \p offset.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFSectionHeader was successfully read and false
+ /// otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+};
+
+/// \class ELFProgramHeader
+/// Generic representation of an ELF program header.
+struct ELFProgramHeader {
+ elf_word p_type; ///< Type of program segment.
+ elf_word p_flags; ///< Segment attributes.
+ elf_off p_offset; ///< Start of segment from beginning of file.
+ elf_addr p_vaddr; ///< Virtual address of segment in memory.
+ elf_addr p_paddr; ///< Physical address (for non-VM systems).
+ elf_xword p_filesz; ///< Byte size of the segment in file.
+ elf_xword p_memsz; ///< Byte size of the segment in memory.
+ elf_xword p_align; ///< Segment alignment constraint.
+
+ ELFProgramHeader();
+
+ /// Parse an ELFProgramHeader entry from the given DataExtractor starting at
+ /// position \p offset. The address size of the DataExtractor determines if
+ /// a 32 or 64 bit object is to be parsed.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFProgramHeader was successfully read and false
+ /// otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+};
+
+/// \class ELFSymbol
+/// Represents a symbol within an ELF symbol table.
+struct ELFSymbol {
+ elf_addr st_value; ///< Absolute or relocatable address.
+ elf_xword st_size; ///< Size of the symbol or zero.
+ elf_word st_name; ///< Symbol name string index.
+ unsigned char st_info; ///< Symbol type and binding attributes.
+ unsigned char st_other; ///< Reserved for future use.
+ elf_half st_shndx; ///< Section to which this symbol applies.
+
+ ELFSymbol();
+
+ /// Returns the binding attribute of the st_info member.
+ unsigned char getBinding() const { return st_info >> 4; }
+
+ /// Returns the type attribute of the st_info member.
+ unsigned char getType() const { return st_info & 0x0F; }
+
+ /// Sets the binding and type of the st_info member.
+ void setBindingAndType(unsigned char binding, unsigned char type) {
+ st_info = (binding << 4) + (type & 0x0F);
+ }
+
+ static const char *bindingToCString(unsigned char binding);
+
+ static const char *typeToCString(unsigned char type);
+
+ static const char *
+ sectionIndexToCString(elf_half shndx,
+ const lldb_private::SectionList *section_list);
+
+ /// Parse an ELFSymbol entry from the given DataExtractor starting at
+ /// position \p offset. The address size of the DataExtractor determines if
+ /// a 32 or 64 bit object is to be parsed.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFSymbol was successfully read and false otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ void Dump(lldb_private::Stream *s, uint32_t idx,
+ const lldb_private::DataExtractor *strtab_data,
+ const lldb_private::SectionList *section_list);
+};
+
+/// \class ELFDynamic
+/// Represents an entry in an ELF dynamic table.
+struct ELFDynamic {
+ elf_sxword d_tag; ///< Type of dynamic table entry.
+ union {
+ elf_xword d_val; ///< Integer value of the table entry.
+ elf_addr d_ptr; ///< Pointer value of the table entry.
+ };
+
+ ELFDynamic();
+
+ /// Parse an ELFDynamic entry from the given DataExtractor starting at
+ /// position \p offset. The address size of the DataExtractor determines if
+ /// a 32 or 64 bit object is to be parsed.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFDynamic entry was successfully read and false
+ /// otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+};
+
+/// \class ELFRel
+/// Represents a relocation entry with an implicit addend.
+struct ELFRel {
+ elf_addr r_offset; ///< Address of reference.
+ elf_xword r_info; ///< symbol index and type of relocation.
+
+ ELFRel();
+
+ /// Parse an ELFRel entry from the given DataExtractor starting at position
+ /// \p offset. The address size of the DataExtractor determines if a 32 or
+ /// 64 bit object is to be parsed.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFRel entry was successfully read and false otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ /// Returns the type when the given entry represents a 32-bit relocation.
+ static unsigned RelocType32(const ELFRel &rel) { return rel.r_info & 0x0ff; }
+
+ /// Returns the type when the given entry represents a 64-bit relocation.
+ static unsigned RelocType64(const ELFRel &rel) {
+ return rel.r_info & 0xffffffff;
+ }
+
+ /// Returns the symbol index when the given entry represents a 32-bit
+ /// relocation.
+ static unsigned RelocSymbol32(const ELFRel &rel) { return rel.r_info >> 8; }
+
+ /// Returns the symbol index when the given entry represents a 64-bit
+ /// relocation.
+ static unsigned RelocSymbol64(const ELFRel &rel) { return rel.r_info >> 32; }
+};
+
+/// \class ELFRela
+/// Represents a relocation entry with an explicit addend.
+struct ELFRela {
+ elf_addr r_offset; ///< Address of reference.
+ elf_xword r_info; ///< Symbol index and type of relocation.
+ elf_sxword r_addend; ///< Constant part of expression.
+
+ ELFRela();
+
+ /// Parse an ELFRela entry from the given DataExtractor starting at position
+ /// \p offset. The address size of the DataExtractor determines if a 32 or
+ /// 64 bit object is to be parsed.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFRela entry was successfully read and false otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ /// Returns the type when the given entry represents a 32-bit relocation.
+ static unsigned RelocType32(const ELFRela &rela) {
+ return rela.r_info & 0x0ff;
+ }
+
+ /// Returns the type when the given entry represents a 64-bit relocation.
+ static unsigned RelocType64(const ELFRela &rela) {
+ return rela.r_info & 0xffffffff;
+ }
+
+ /// Returns the symbol index when the given entry represents a 32-bit
+ /// relocation.
+ static unsigned RelocSymbol32(const ELFRela &rela) {
+ return rela.r_info >> 8;
+ }
+
+ /// Returns the symbol index when the given entry represents a 64-bit
+ /// relocation.
+ static unsigned RelocSymbol64(const ELFRela &rela) {
+ return rela.r_info >> 32;
+ }
+};
+
+} // End namespace elf.
+
+#endif // #ifndef liblldb_ELFHeader_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
new file mode 100644
index 000000000000..d62afa34bbe8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -0,0 +1,3380 @@
+//===-- ObjectFileELF.cpp ------------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ObjectFileELF.h"
+
+#include <algorithm>
+#include <cassert>
+#include <unordered_map>
+
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Symbol/DWARFCallFrameInfo.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+
+#include "llvm/ADT/IntervalMap.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Decompressor.h"
+#include "llvm/Support/ARMBuildAttributes.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/MipsABIFlags.h"
+
+#define CASE_AND_STREAM(s, def, width) \
+ case def: \
+ s->Printf("%-*s", width, #def); \
+ break;
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace elf;
+using namespace llvm::ELF;
+
+namespace {
+
+// ELF note owner definitions
+const char *const LLDB_NT_OWNER_FREEBSD = "FreeBSD";
+const char *const LLDB_NT_OWNER_GNU = "GNU";
+const char *const LLDB_NT_OWNER_NETBSD = "NetBSD";
+const char *const LLDB_NT_OWNER_NETBSDCORE = "NetBSD-CORE";
+const char *const LLDB_NT_OWNER_OPENBSD = "OpenBSD";
+const char *const LLDB_NT_OWNER_ANDROID = "Android";
+const char *const LLDB_NT_OWNER_CORE = "CORE";
+const char *const LLDB_NT_OWNER_LINUX = "LINUX";
+
+// ELF note type definitions
+const elf_word LLDB_NT_FREEBSD_ABI_TAG = 0x01;
+const elf_word LLDB_NT_FREEBSD_ABI_SIZE = 4;
+
+const elf_word LLDB_NT_GNU_ABI_TAG = 0x01;
+const elf_word LLDB_NT_GNU_ABI_SIZE = 16;
+
+const elf_word LLDB_NT_GNU_BUILD_ID_TAG = 0x03;
+
+const elf_word LLDB_NT_NETBSD_IDENT_TAG = 1;
+const elf_word LLDB_NT_NETBSD_IDENT_DESCSZ = 4;
+const elf_word LLDB_NT_NETBSD_IDENT_NAMESZ = 7;
+const elf_word LLDB_NT_NETBSD_PROCINFO = 1;
+
+// GNU ABI note OS constants
+const elf_word LLDB_NT_GNU_ABI_OS_LINUX = 0x00;
+const elf_word LLDB_NT_GNU_ABI_OS_HURD = 0x01;
+const elf_word LLDB_NT_GNU_ABI_OS_SOLARIS = 0x02;
+
+// LLDB_NT_OWNER_CORE and LLDB_NT_OWNER_LINUX note contants
+#define NT_PRSTATUS 1
+#define NT_PRFPREG 2
+#define NT_PRPSINFO 3
+#define NT_TASKSTRUCT 4
+#define NT_AUXV 6
+#define NT_SIGINFO 0x53494749
+#define NT_FILE 0x46494c45
+#define NT_PRXFPREG 0x46e62b7f
+#define NT_PPC_VMX 0x100
+#define NT_PPC_SPE 0x101
+#define NT_PPC_VSX 0x102
+#define NT_386_TLS 0x200
+#define NT_386_IOPERM 0x201
+#define NT_X86_XSTATE 0x202
+#define NT_S390_HIGH_GPRS 0x300
+#define NT_S390_TIMER 0x301
+#define NT_S390_TODCMP 0x302
+#define NT_S390_TODPREG 0x303
+#define NT_S390_CTRS 0x304
+#define NT_S390_PREFIX 0x305
+#define NT_S390_LAST_BREAK 0x306
+#define NT_S390_SYSTEM_CALL 0x307
+#define NT_S390_TDB 0x308
+#define NT_S390_VXRS_LOW 0x309
+#define NT_S390_VXRS_HIGH 0x30a
+#define NT_ARM_VFP 0x400
+#define NT_ARM_TLS 0x401
+#define NT_ARM_HW_BREAK 0x402
+#define NT_ARM_HW_WATCH 0x403
+#define NT_ARM_SYSTEM_CALL 0x404
+#define NT_METAG_CBUF 0x500
+#define NT_METAG_RPIPE 0x501
+#define NT_METAG_TLS 0x502
+
+//===----------------------------------------------------------------------===//
+/// \class ELFRelocation
+/// Generic wrapper for ELFRel and ELFRela.
+///
+/// This helper class allows us to parse both ELFRel and ELFRela relocation
+/// entries in a generic manner.
+class ELFRelocation {
+public:
+ /// Constructs an ELFRelocation entry with a personality as given by @p
+ /// type.
+ ///
+ /// \param type Either DT_REL or DT_RELA. Any other value is invalid.
+ ELFRelocation(unsigned type);
+
+ ~ELFRelocation();
+
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ static unsigned RelocType32(const ELFRelocation &rel);
+
+ static unsigned RelocType64(const ELFRelocation &rel);
+
+ static unsigned RelocSymbol32(const ELFRelocation &rel);
+
+ static unsigned RelocSymbol64(const ELFRelocation &rel);
+
+ static unsigned RelocOffset32(const ELFRelocation &rel);
+
+ static unsigned RelocOffset64(const ELFRelocation &rel);
+
+ static unsigned RelocAddend32(const ELFRelocation &rel);
+
+ static unsigned RelocAddend64(const ELFRelocation &rel);
+
+private:
+ typedef llvm::PointerUnion<ELFRel *, ELFRela *> RelocUnion;
+
+ RelocUnion reloc;
+};
+
+ELFRelocation::ELFRelocation(unsigned type) {
+ if (type == DT_REL || type == SHT_REL)
+ reloc = new ELFRel();
+ else if (type == DT_RELA || type == SHT_RELA)
+ reloc = new ELFRela();
+ else {
+ assert(false && "unexpected relocation type");
+ reloc = static_cast<ELFRel *>(nullptr);
+ }
+}
+
+ELFRelocation::~ELFRelocation() {
+ if (reloc.is<ELFRel *>())
+ delete reloc.get<ELFRel *>();
+ else
+ delete reloc.get<ELFRela *>();
+}
+
+bool ELFRelocation::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ if (reloc.is<ELFRel *>())
+ return reloc.get<ELFRel *>()->Parse(data, offset);
+ else
+ return reloc.get<ELFRela *>()->Parse(data, offset);
+}
+
+unsigned ELFRelocation::RelocType32(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return ELFRel::RelocType32(*rel.reloc.get<ELFRel *>());
+ else
+ return ELFRela::RelocType32(*rel.reloc.get<ELFRela *>());
+}
+
+unsigned ELFRelocation::RelocType64(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return ELFRel::RelocType64(*rel.reloc.get<ELFRel *>());
+ else
+ return ELFRela::RelocType64(*rel.reloc.get<ELFRela *>());
+}
+
+unsigned ELFRelocation::RelocSymbol32(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return ELFRel::RelocSymbol32(*rel.reloc.get<ELFRel *>());
+ else
+ return ELFRela::RelocSymbol32(*rel.reloc.get<ELFRela *>());
+}
+
+unsigned ELFRelocation::RelocSymbol64(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return ELFRel::RelocSymbol64(*rel.reloc.get<ELFRel *>());
+ else
+ return ELFRela::RelocSymbol64(*rel.reloc.get<ELFRela *>());
+}
+
+unsigned ELFRelocation::RelocOffset32(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return rel.reloc.get<ELFRel *>()->r_offset;
+ else
+ return rel.reloc.get<ELFRela *>()->r_offset;
+}
+
+unsigned ELFRelocation::RelocOffset64(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return rel.reloc.get<ELFRel *>()->r_offset;
+ else
+ return rel.reloc.get<ELFRela *>()->r_offset;
+}
+
+unsigned ELFRelocation::RelocAddend32(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return 0;
+ else
+ return rel.reloc.get<ELFRela *>()->r_addend;
+}
+
+unsigned ELFRelocation::RelocAddend64(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return 0;
+ else
+ return rel.reloc.get<ELFRela *>()->r_addend;
+}
+
+} // end anonymous namespace
+
+static user_id_t SegmentID(size_t PHdrIndex) { return ~PHdrIndex; }
+
+bool ELFNote::Parse(const DataExtractor &data, lldb::offset_t *offset) {
+ // Read all fields.
+ if (data.GetU32(offset, &n_namesz, 3) == nullptr)
+ return false;
+
+ // The name field is required to be nul-terminated, and n_namesz includes the
+ // terminating nul in observed implementations (contrary to the ELF-64 spec).
+ // A special case is needed for cores generated by some older Linux versions,
+ // which write a note named "CORE" without a nul terminator and n_namesz = 4.
+ if (n_namesz == 4) {
+ char buf[4];
+ if (data.ExtractBytes(*offset, 4, data.GetByteOrder(), buf) != 4)
+ return false;
+ if (strncmp(buf, "CORE", 4) == 0) {
+ n_name = "CORE";
+ *offset += 4;
+ return true;
+ }
+ }
+
+ const char *cstr = data.GetCStr(offset, llvm::alignTo(n_namesz, 4));
+ if (cstr == nullptr) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS));
+ if (log)
+ log->Printf("Failed to parse note name lacking nul terminator");
+
+ return false;
+ }
+ n_name = cstr;
+ return true;
+}
+
+static uint32_t mipsVariantFromElfFlags (const elf::ELFHeader &header) {
+ const uint32_t mips_arch = header.e_flags & llvm::ELF::EF_MIPS_ARCH;
+ uint32_t endian = header.e_ident[EI_DATA];
+ uint32_t arch_variant = ArchSpec::eMIPSSubType_unknown;
+ uint32_t fileclass = header.e_ident[EI_CLASS];
+
+ // If there aren't any elf flags available (e.g core elf file) then return
+ // default
+ // 32 or 64 bit arch (without any architecture revision) based on object file's class.
+ if (header.e_type == ET_CORE) {
+ switch (fileclass) {
+ case llvm::ELF::ELFCLASS32:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32el
+ : ArchSpec::eMIPSSubType_mips32;
+ case llvm::ELF::ELFCLASS64:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64el
+ : ArchSpec::eMIPSSubType_mips64;
+ default:
+ return arch_variant;
+ }
+ }
+
+ switch (mips_arch) {
+ case llvm::ELF::EF_MIPS_ARCH_1:
+ case llvm::ELF::EF_MIPS_ARCH_2:
+ case llvm::ELF::EF_MIPS_ARCH_32:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32el
+ : ArchSpec::eMIPSSubType_mips32;
+ case llvm::ELF::EF_MIPS_ARCH_32R2:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32r2el
+ : ArchSpec::eMIPSSubType_mips32r2;
+ case llvm::ELF::EF_MIPS_ARCH_32R6:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32r6el
+ : ArchSpec::eMIPSSubType_mips32r6;
+ case llvm::ELF::EF_MIPS_ARCH_3:
+ case llvm::ELF::EF_MIPS_ARCH_4:
+ case llvm::ELF::EF_MIPS_ARCH_5:
+ case llvm::ELF::EF_MIPS_ARCH_64:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64el
+ : ArchSpec::eMIPSSubType_mips64;
+ case llvm::ELF::EF_MIPS_ARCH_64R2:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64r2el
+ : ArchSpec::eMIPSSubType_mips64r2;
+ case llvm::ELF::EF_MIPS_ARCH_64R6:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64r6el
+ : ArchSpec::eMIPSSubType_mips64r6;
+ default:
+ break;
+ }
+
+ return arch_variant;
+}
+
+static uint32_t subTypeFromElfHeader(const elf::ELFHeader &header) {
+ if (header.e_machine == llvm::ELF::EM_MIPS)
+ return mipsVariantFromElfFlags(header);
+
+ return LLDB_INVALID_CPUTYPE;
+}
+
+// Arbitrary constant used as UUID prefix for core files.
+const uint32_t ObjectFileELF::g_core_uuid_magic(0xE210C);
+
+// Static methods.
+void ObjectFileELF::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ CreateMemoryInstance, GetModuleSpecifications);
+}
+
+void ObjectFileELF::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ObjectFileELF::GetPluginNameStatic() {
+ static ConstString g_name("elf");
+ return g_name;
+}
+
+const char *ObjectFileELF::GetPluginDescriptionStatic() {
+ return "ELF object file reader.";
+}
+
+ObjectFile *ObjectFileELF::CreateInstance(const lldb::ModuleSP &module_sp,
+ DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ const lldb_private::FileSpec *file,
+ lldb::offset_t file_offset,
+ lldb::offset_t length) {
+ if (!data_sp) {
+ data_sp = MapFileData(*file, length, file_offset);
+ if (!data_sp)
+ return nullptr;
+ data_offset = 0;
+ }
+
+ assert(data_sp);
+
+ if (data_sp->GetByteSize() <= (llvm::ELF::EI_NIDENT + data_offset))
+ return nullptr;
+
+ const uint8_t *magic = data_sp->GetBytes() + data_offset;
+ if (!ELFHeader::MagicBytesMatch(magic))
+ return nullptr;
+
+ // Update the data to contain the entire file if it doesn't already
+ if (data_sp->GetByteSize() < length) {
+ data_sp = MapFileData(*file, length, file_offset);
+ if (!data_sp)
+ return nullptr;
+ data_offset = 0;
+ magic = data_sp->GetBytes();
+ }
+
+ unsigned address_size = ELFHeader::AddressSizeInBytes(magic);
+ if (address_size == 4 || address_size == 8) {
+ std::unique_ptr<ObjectFileELF> objfile_up(new ObjectFileELF(
+ module_sp, data_sp, data_offset, file, file_offset, length));
+ ArchSpec spec = objfile_up->GetArchitecture();
+ if (spec && objfile_up->SetModulesArchitecture(spec))
+ return objfile_up.release();
+ }
+
+ return nullptr;
+}
+
+ObjectFile *ObjectFileELF::CreateMemoryInstance(
+ const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
+ const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) {
+ if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT)) {
+ const uint8_t *magic = data_sp->GetBytes();
+ if (ELFHeader::MagicBytesMatch(magic)) {
+ unsigned address_size = ELFHeader::AddressSizeInBytes(magic);
+ if (address_size == 4 || address_size == 8) {
+ std::unique_ptr<ObjectFileELF> objfile_up(
+ new ObjectFileELF(module_sp, data_sp, process_sp, header_addr));
+ ArchSpec spec = objfile_up->GetArchitecture();
+ if (spec && objfile_up->SetModulesArchitecture(spec))
+ return objfile_up.release();
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool ObjectFileELF::MagicBytesMatch(DataBufferSP &data_sp,
+ lldb::addr_t data_offset,
+ lldb::addr_t data_length) {
+ if (data_sp &&
+ data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT + data_offset)) {
+ const uint8_t *magic = data_sp->GetBytes() + data_offset;
+ return ELFHeader::MagicBytesMatch(magic);
+ }
+ return false;
+}
+
+/*
+ * crc function from http://svnweb.freebsd.org/base/head/sys/libkern/crc32.c
+ *
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+static uint32_t calc_crc32(uint32_t crc, const void *buf, size_t size) {
+ static const uint32_t g_crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
+ const uint8_t *p = (const uint8_t *)buf;
+
+ crc = crc ^ ~0U;
+ while (size--)
+ crc = g_crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+ return crc ^ ~0U;
+}
+
+static uint32_t calc_gnu_debuglink_crc32(const void *buf, size_t size) {
+ return calc_crc32(0U, buf, size);
+}
+
+uint32_t ObjectFileELF::CalculateELFNotesSegmentsCRC32(
+ const ProgramHeaderColl &program_headers, DataExtractor &object_data) {
+
+ uint32_t core_notes_crc = 0;
+
+ for (const ELFProgramHeader &H : program_headers) {
+ if (H.p_type == llvm::ELF::PT_NOTE) {
+ const elf_off ph_offset = H.p_offset;
+ const size_t ph_size = H.p_filesz;
+
+ DataExtractor segment_data;
+ if (segment_data.SetData(object_data, ph_offset, ph_size) != ph_size) {
+ // The ELF program header contained incorrect data, probably corefile
+ // is incomplete or corrupted.
+ break;
+ }
+
+ core_notes_crc = calc_crc32(core_notes_crc, segment_data.GetDataStart(),
+ segment_data.GetByteSize());
+ }
+ }
+
+ return core_notes_crc;
+}
+
+static const char *OSABIAsCString(unsigned char osabi_byte) {
+#define _MAKE_OSABI_CASE(x) \
+ case x: \
+ return #x
+ switch (osabi_byte) {
+ _MAKE_OSABI_CASE(ELFOSABI_NONE);
+ _MAKE_OSABI_CASE(ELFOSABI_HPUX);
+ _MAKE_OSABI_CASE(ELFOSABI_NETBSD);
+ _MAKE_OSABI_CASE(ELFOSABI_GNU);
+ _MAKE_OSABI_CASE(ELFOSABI_HURD);
+ _MAKE_OSABI_CASE(ELFOSABI_SOLARIS);
+ _MAKE_OSABI_CASE(ELFOSABI_AIX);
+ _MAKE_OSABI_CASE(ELFOSABI_IRIX);
+ _MAKE_OSABI_CASE(ELFOSABI_FREEBSD);
+ _MAKE_OSABI_CASE(ELFOSABI_TRU64);
+ _MAKE_OSABI_CASE(ELFOSABI_MODESTO);
+ _MAKE_OSABI_CASE(ELFOSABI_OPENBSD);
+ _MAKE_OSABI_CASE(ELFOSABI_OPENVMS);
+ _MAKE_OSABI_CASE(ELFOSABI_NSK);
+ _MAKE_OSABI_CASE(ELFOSABI_AROS);
+ _MAKE_OSABI_CASE(ELFOSABI_FENIXOS);
+ _MAKE_OSABI_CASE(ELFOSABI_C6000_ELFABI);
+ _MAKE_OSABI_CASE(ELFOSABI_C6000_LINUX);
+ _MAKE_OSABI_CASE(ELFOSABI_ARM);
+ _MAKE_OSABI_CASE(ELFOSABI_STANDALONE);
+ default:
+ return "<unknown-osabi>";
+ }
+#undef _MAKE_OSABI_CASE
+}
+
+//
+// WARNING : This function is being deprecated
+// It's functionality has moved to ArchSpec::SetArchitecture This function is
+// only being kept to validate the move.
+//
+// TODO : Remove this function
+static bool GetOsFromOSABI(unsigned char osabi_byte,
+ llvm::Triple::OSType &ostype) {
+ switch (osabi_byte) {
+ case ELFOSABI_AIX:
+ ostype = llvm::Triple::OSType::AIX;
+ break;
+ case ELFOSABI_FREEBSD:
+ ostype = llvm::Triple::OSType::FreeBSD;
+ break;
+ case ELFOSABI_GNU:
+ ostype = llvm::Triple::OSType::Linux;
+ break;
+ case ELFOSABI_NETBSD:
+ ostype = llvm::Triple::OSType::NetBSD;
+ break;
+ case ELFOSABI_OPENBSD:
+ ostype = llvm::Triple::OSType::OpenBSD;
+ break;
+ case ELFOSABI_SOLARIS:
+ ostype = llvm::Triple::OSType::Solaris;
+ break;
+ default:
+ ostype = llvm::Triple::OSType::UnknownOS;
+ }
+ return ostype != llvm::Triple::OSType::UnknownOS;
+}
+
+size_t ObjectFileELF::GetModuleSpecifications(
+ const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, lldb::offset_t file_offset,
+ lldb::offset_t length, lldb_private::ModuleSpecList &specs) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
+
+ const size_t initial_count = specs.GetSize();
+
+ if (ObjectFileELF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) {
+ DataExtractor data;
+ data.SetData(data_sp);
+ elf::ELFHeader header;
+ lldb::offset_t header_offset = data_offset;
+ if (header.Parse(data, &header_offset)) {
+ if (data_sp) {
+ ModuleSpec spec(file);
+
+ const uint32_t sub_type = subTypeFromElfHeader(header);
+ spec.GetArchitecture().SetArchitecture(
+ eArchTypeELF, header.e_machine, sub_type, header.e_ident[EI_OSABI]);
+
+ if (spec.GetArchitecture().IsValid()) {
+ llvm::Triple::OSType ostype;
+ llvm::Triple::VendorType vendor;
+ llvm::Triple::OSType spec_ostype =
+ spec.GetArchitecture().GetTriple().getOS();
+
+ if (log)
+ log->Printf("ObjectFileELF::%s file '%s' module OSABI: %s",
+ __FUNCTION__, file.GetPath().c_str(),
+ OSABIAsCString(header.e_ident[EI_OSABI]));
+
+ // SetArchitecture should have set the vendor to unknown
+ vendor = spec.GetArchitecture().GetTriple().getVendor();
+ assert(vendor == llvm::Triple::UnknownVendor);
+ UNUSED_IF_ASSERT_DISABLED(vendor);
+
+ //
+ // Validate it is ok to remove GetOsFromOSABI
+ GetOsFromOSABI(header.e_ident[EI_OSABI], ostype);
+ assert(spec_ostype == ostype);
+ if (spec_ostype != llvm::Triple::OSType::UnknownOS) {
+ if (log)
+ log->Printf("ObjectFileELF::%s file '%s' set ELF module OS type "
+ "from ELF header OSABI.",
+ __FUNCTION__, file.GetPath().c_str());
+ }
+
+ data_sp = MapFileData(file, -1, file_offset);
+ if (data_sp)
+ data.SetData(data_sp);
+ // In case there is header extension in the section #0, the header we
+ // parsed above could have sentinel values for e_phnum, e_shnum, and
+ // e_shstrndx. In this case we need to reparse the header with a
+ // bigger data source to get the actual values.
+ if (header.HasHeaderExtension()) {
+ lldb::offset_t header_offset = data_offset;
+ header.Parse(data, &header_offset);
+ }
+
+ uint32_t gnu_debuglink_crc = 0;
+ std::string gnu_debuglink_file;
+ SectionHeaderColl section_headers;
+ lldb_private::UUID &uuid = spec.GetUUID();
+
+ GetSectionHeaderInfo(section_headers, data, header, uuid,
+ gnu_debuglink_file, gnu_debuglink_crc,
+ spec.GetArchitecture());
+
+ llvm::Triple &spec_triple = spec.GetArchitecture().GetTriple();
+
+ if (log)
+ log->Printf("ObjectFileELF::%s file '%s' module set to triple: %s "
+ "(architecture %s)",
+ __FUNCTION__, file.GetPath().c_str(),
+ spec_triple.getTriple().c_str(),
+ spec.GetArchitecture().GetArchitectureName());
+
+ if (!uuid.IsValid()) {
+ uint32_t core_notes_crc = 0;
+
+ if (!gnu_debuglink_crc) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ lldb_private::Timer scoped_timer(
+ func_cat,
+ "Calculating module crc32 %s with size %" PRIu64 " KiB",
+ file.GetLastPathComponent().AsCString(),
+ (FileSystem::Instance().GetByteSize(file) - file_offset) /
+ 1024);
+
+ // For core files - which usually don't happen to have a
+ // gnu_debuglink, and are pretty bulky - calculating whole
+ // contents crc32 would be too much of luxury. Thus we will need
+ // to fallback to something simpler.
+ if (header.e_type == llvm::ELF::ET_CORE) {
+ ProgramHeaderColl program_headers;
+ GetProgramHeaderInfo(program_headers, data, header);
+
+ core_notes_crc =
+ CalculateELFNotesSegmentsCRC32(program_headers, data);
+ } else {
+ gnu_debuglink_crc = calc_gnu_debuglink_crc32(
+ data.GetDataStart(), data.GetByteSize());
+ }
+ }
+ using u32le = llvm::support::ulittle32_t;
+ if (gnu_debuglink_crc) {
+ // Use 4 bytes of crc from the .gnu_debuglink section.
+ u32le data(gnu_debuglink_crc);
+ uuid = UUID::fromData(&data, sizeof(data));
+ } else if (core_notes_crc) {
+ // Use 8 bytes - first 4 bytes for *magic* prefix, mainly to make
+ // it look different form .gnu_debuglink crc followed by 4 bytes
+ // of note segments crc.
+ u32le data[] = {u32le(g_core_uuid_magic), u32le(core_notes_crc)};
+ uuid = UUID::fromData(data, sizeof(data));
+ }
+ }
+
+ specs.Append(spec);
+ }
+ }
+ }
+ }
+
+ return specs.GetSize() - initial_count;
+}
+
+// PluginInterface protocol
+lldb_private::ConstString ObjectFileELF::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ObjectFileELF::GetPluginVersion() { return m_plugin_version; }
+// ObjectFile protocol
+
+ObjectFileELF::ObjectFileELF(const lldb::ModuleSP &module_sp,
+ DataBufferSP &data_sp, lldb::offset_t data_offset,
+ const FileSpec *file, lldb::offset_t file_offset,
+ lldb::offset_t length)
+ : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset),
+ m_header(), m_uuid(), m_gnu_debuglink_file(), m_gnu_debuglink_crc(0),
+ m_program_headers(), m_section_headers(), m_dynamic_symbols(),
+ m_filespec_up(), m_entry_point_address(), m_arch_spec() {
+ if (file)
+ m_file = *file;
+ ::memset(&m_header, 0, sizeof(m_header));
+}
+
+ObjectFileELF::ObjectFileELF(const lldb::ModuleSP &module_sp,
+ DataBufferSP &header_data_sp,
+ const lldb::ProcessSP &process_sp,
+ addr_t header_addr)
+ : ObjectFile(module_sp, process_sp, header_addr, header_data_sp),
+ m_header(), m_uuid(), m_gnu_debuglink_file(), m_gnu_debuglink_crc(0),
+ m_program_headers(), m_section_headers(), m_dynamic_symbols(),
+ m_filespec_up(), m_entry_point_address(), m_arch_spec() {
+ ::memset(&m_header, 0, sizeof(m_header));
+}
+
+ObjectFileELF::~ObjectFileELF() {}
+
+bool ObjectFileELF::IsExecutable() const {
+ return ((m_header.e_type & ET_EXEC) != 0) || (m_header.e_entry != 0);
+}
+
+bool ObjectFileELF::SetLoadAddress(Target &target, lldb::addr_t value,
+ bool value_is_offset) {
+ ModuleSP module_sp = GetModule();
+ if (module_sp) {
+ size_t num_loaded_sections = 0;
+ SectionList *section_list = GetSectionList();
+ if (section_list) {
+ if (!value_is_offset) {
+ addr_t base = GetBaseAddress().GetFileAddress();
+ if (base == LLDB_INVALID_ADDRESS)
+ return false;
+ value -= base;
+ }
+
+ const size_t num_sections = section_list->GetSize();
+ size_t sect_idx = 0;
+
+ for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
+ // Iterate through the object file sections to find all of the sections
+ // that have SHF_ALLOC in their flag bits.
+ SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
+ if (section_sp->Test(SHF_ALLOC) ||
+ section_sp->GetType() == eSectionTypeContainer) {
+ lldb::addr_t load_addr = section_sp->GetFileAddress();
+ // We don't want to update the load address of a section with type
+ // eSectionTypeAbsoluteAddress as they already have the absolute load
+ // address already specified
+ if (section_sp->GetType() != eSectionTypeAbsoluteAddress)
+ load_addr += value;
+
+ // On 32-bit systems the load address have to fit into 4 bytes. The
+ // rest of the bytes are the overflow from the addition.
+ if (GetAddressByteSize() == 4)
+ load_addr &= 0xFFFFFFFF;
+
+ if (target.GetSectionLoadList().SetSectionLoadAddress(section_sp,
+ load_addr))
+ ++num_loaded_sections;
+ }
+ }
+ return num_loaded_sections > 0;
+ }
+ }
+ return false;
+}
+
+ByteOrder ObjectFileELF::GetByteOrder() const {
+ if (m_header.e_ident[EI_DATA] == ELFDATA2MSB)
+ return eByteOrderBig;
+ if (m_header.e_ident[EI_DATA] == ELFDATA2LSB)
+ return eByteOrderLittle;
+ return eByteOrderInvalid;
+}
+
+uint32_t ObjectFileELF::GetAddressByteSize() const {
+ return m_data.GetAddressByteSize();
+}
+
+AddressClass ObjectFileELF::GetAddressClass(addr_t file_addr) {
+ Symtab *symtab = GetSymtab();
+ if (!symtab)
+ return AddressClass::eUnknown;
+
+ // The address class is determined based on the symtab. Ask it from the
+ // object file what contains the symtab information.
+ ObjectFile *symtab_objfile = symtab->GetObjectFile();
+ if (symtab_objfile != nullptr && symtab_objfile != this)
+ return symtab_objfile->GetAddressClass(file_addr);
+
+ auto res = ObjectFile::GetAddressClass(file_addr);
+ if (res != AddressClass::eCode)
+ return res;
+
+ auto ub = m_address_class_map.upper_bound(file_addr);
+ if (ub == m_address_class_map.begin()) {
+ // No entry in the address class map before the address. Return default
+ // address class for an address in a code section.
+ return AddressClass::eCode;
+ }
+
+ // Move iterator to the address class entry preceding address
+ --ub;
+
+ return ub->second;
+}
+
+size_t ObjectFileELF::SectionIndex(const SectionHeaderCollIter &I) {
+ return std::distance(m_section_headers.begin(), I);
+}
+
+size_t ObjectFileELF::SectionIndex(const SectionHeaderCollConstIter &I) const {
+ return std::distance(m_section_headers.begin(), I);
+}
+
+bool ObjectFileELF::ParseHeader() {
+ lldb::offset_t offset = 0;
+ return m_header.Parse(m_data, &offset);
+}
+
+UUID ObjectFileELF::GetUUID() {
+ // Need to parse the section list to get the UUIDs, so make sure that's been
+ // done.
+ if (!ParseSectionHeaders() && GetType() != ObjectFile::eTypeCoreFile)
+ return UUID();
+
+ if (!m_uuid) {
+ using u32le = llvm::support::ulittle32_t;
+ if (GetType() == ObjectFile::eTypeCoreFile) {
+ uint32_t core_notes_crc = 0;
+
+ if (!ParseProgramHeaders())
+ return UUID();
+
+ core_notes_crc =
+ CalculateELFNotesSegmentsCRC32(m_program_headers, m_data);
+
+ if (core_notes_crc) {
+ // Use 8 bytes - first 4 bytes for *magic* prefix, mainly to make it
+ // look different form .gnu_debuglink crc - followed by 4 bytes of note
+ // segments crc.
+ u32le data[] = {u32le(g_core_uuid_magic), u32le(core_notes_crc)};
+ m_uuid = UUID::fromData(data, sizeof(data));
+ }
+ } else {
+ if (!m_gnu_debuglink_crc)
+ m_gnu_debuglink_crc = calc_gnu_debuglink_crc32(m_data.GetDataStart(),
+ m_data.GetByteSize());
+ if (m_gnu_debuglink_crc) {
+ // Use 4 bytes of crc from the .gnu_debuglink section.
+ u32le data(m_gnu_debuglink_crc);
+ m_uuid = UUID::fromData(&data, sizeof(data));
+ }
+ }
+ }
+
+ return m_uuid;
+}
+
+lldb_private::FileSpecList ObjectFileELF::GetDebugSymbolFilePaths() {
+ FileSpecList file_spec_list;
+
+ if (!m_gnu_debuglink_file.empty()) {
+ FileSpec file_spec(m_gnu_debuglink_file);
+ file_spec_list.Append(file_spec);
+ }
+ return file_spec_list;
+}
+
+uint32_t ObjectFileELF::GetDependentModules(FileSpecList &files) {
+ size_t num_modules = ParseDependentModules();
+ uint32_t num_specs = 0;
+
+ for (unsigned i = 0; i < num_modules; ++i) {
+ if (files.AppendIfUnique(m_filespec_up->GetFileSpecAtIndex(i)))
+ num_specs++;
+ }
+
+ return num_specs;
+}
+
+Address ObjectFileELF::GetImageInfoAddress(Target *target) {
+ if (!ParseDynamicSymbols())
+ return Address();
+
+ SectionList *section_list = GetSectionList();
+ if (!section_list)
+ return Address();
+
+ // Find the SHT_DYNAMIC (.dynamic) section.
+ SectionSP dynsym_section_sp(
+ section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true));
+ if (!dynsym_section_sp)
+ return Address();
+ assert(dynsym_section_sp->GetObjectFile() == this);
+
+ user_id_t dynsym_id = dynsym_section_sp->GetID();
+ const ELFSectionHeaderInfo *dynsym_hdr = GetSectionHeaderByIndex(dynsym_id);
+ if (!dynsym_hdr)
+ return Address();
+
+ for (size_t i = 0; i < m_dynamic_symbols.size(); ++i) {
+ ELFDynamic &symbol = m_dynamic_symbols[i];
+
+ if (symbol.d_tag == DT_DEBUG) {
+ // Compute the offset as the number of previous entries plus the size of
+ // d_tag.
+ addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize();
+ return Address(dynsym_section_sp, offset);
+ }
+ // MIPS executables uses DT_MIPS_RLD_MAP_REL to support PIE. DT_MIPS_RLD_MAP
+ // exists in non-PIE.
+ else if ((symbol.d_tag == DT_MIPS_RLD_MAP ||
+ symbol.d_tag == DT_MIPS_RLD_MAP_REL) &&
+ target) {
+ addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize();
+ addr_t dyn_base = dynsym_section_sp->GetLoadBaseAddress(target);
+ if (dyn_base == LLDB_INVALID_ADDRESS)
+ return Address();
+
+ Status error;
+ if (symbol.d_tag == DT_MIPS_RLD_MAP) {
+ // DT_MIPS_RLD_MAP tag stores an absolute address of the debug pointer.
+ Address addr;
+ if (target->ReadPointerFromMemory(dyn_base + offset, false, error,
+ addr))
+ return addr;
+ }
+ if (symbol.d_tag == DT_MIPS_RLD_MAP_REL) {
+ // DT_MIPS_RLD_MAP_REL tag stores the offset to the debug pointer,
+ // relative to the address of the tag.
+ uint64_t rel_offset;
+ rel_offset = target->ReadUnsignedIntegerFromMemory(
+ dyn_base + offset, false, GetAddressByteSize(), UINT64_MAX, error);
+ if (error.Success() && rel_offset != UINT64_MAX) {
+ Address addr;
+ addr_t debug_ptr_address =
+ dyn_base + (offset - GetAddressByteSize()) + rel_offset;
+ addr.SetOffset(debug_ptr_address);
+ return addr;
+ }
+ }
+ }
+ }
+
+ return Address();
+}
+
+lldb_private::Address ObjectFileELF::GetEntryPointAddress() {
+ if (m_entry_point_address.IsValid())
+ return m_entry_point_address;
+
+ if (!ParseHeader() || !IsExecutable())
+ return m_entry_point_address;
+
+ SectionList *section_list = GetSectionList();
+ addr_t offset = m_header.e_entry;
+
+ if (!section_list)
+ m_entry_point_address.SetOffset(offset);
+ else
+ m_entry_point_address.ResolveAddressUsingFileSections(offset, section_list);
+ return m_entry_point_address;
+}
+
+Address ObjectFileELF::GetBaseAddress() {
+ for (const auto &EnumPHdr : llvm::enumerate(ProgramHeaders())) {
+ const ELFProgramHeader &H = EnumPHdr.value();
+ if (H.p_type != PT_LOAD)
+ continue;
+
+ return Address(
+ GetSectionList()->FindSectionByID(SegmentID(EnumPHdr.index())), 0);
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+// ParseDependentModules
+size_t ObjectFileELF::ParseDependentModules() {
+ if (m_filespec_up)
+ return m_filespec_up->GetSize();
+
+ m_filespec_up.reset(new FileSpecList());
+
+ if (!ParseSectionHeaders())
+ return 0;
+
+ SectionList *section_list = GetSectionList();
+ if (!section_list)
+ return 0;
+
+ // Find the SHT_DYNAMIC section.
+ Section *dynsym =
+ section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
+ .get();
+ if (!dynsym)
+ return 0;
+ assert(dynsym->GetObjectFile() == this);
+
+ const ELFSectionHeaderInfo *header = GetSectionHeaderByIndex(dynsym->GetID());
+ if (!header)
+ return 0;
+ // sh_link: section header index of string table used by entries in the
+ // section.
+ Section *dynstr = section_list->FindSectionByID(header->sh_link).get();
+ if (!dynstr)
+ return 0;
+
+ DataExtractor dynsym_data;
+ DataExtractor dynstr_data;
+ if (ReadSectionData(dynsym, dynsym_data) &&
+ ReadSectionData(dynstr, dynstr_data)) {
+ ELFDynamic symbol;
+ const lldb::offset_t section_size = dynsym_data.GetByteSize();
+ lldb::offset_t offset = 0;
+
+ // The only type of entries we are concerned with are tagged DT_NEEDED,
+ // yielding the name of a required library.
+ while (offset < section_size) {
+ if (!symbol.Parse(dynsym_data, &offset))
+ break;
+
+ if (symbol.d_tag != DT_NEEDED)
+ continue;
+
+ uint32_t str_index = static_cast<uint32_t>(symbol.d_val);
+ const char *lib_name = dynstr_data.PeekCStr(str_index);
+ FileSpec file_spec(lib_name);
+ FileSystem::Instance().Resolve(file_spec);
+ m_filespec_up->Append(file_spec);
+ }
+ }
+
+ return m_filespec_up->GetSize();
+}
+
+// GetProgramHeaderInfo
+size_t ObjectFileELF::GetProgramHeaderInfo(ProgramHeaderColl &program_headers,
+ DataExtractor &object_data,
+ const ELFHeader &header) {
+ // We have already parsed the program headers
+ if (!program_headers.empty())
+ return program_headers.size();
+
+ // If there are no program headers to read we are done.
+ if (header.e_phnum == 0)
+ return 0;
+
+ program_headers.resize(header.e_phnum);
+ if (program_headers.size() != header.e_phnum)
+ return 0;
+
+ const size_t ph_size = header.e_phnum * header.e_phentsize;
+ const elf_off ph_offset = header.e_phoff;
+ DataExtractor data;
+ if (data.SetData(object_data, ph_offset, ph_size) != ph_size)
+ return 0;
+
+ uint32_t idx;
+ lldb::offset_t offset;
+ for (idx = 0, offset = 0; idx < header.e_phnum; ++idx) {
+ if (!program_headers[idx].Parse(data, &offset))
+ break;
+ }
+
+ if (idx < program_headers.size())
+ program_headers.resize(idx);
+
+ return program_headers.size();
+}
+
+// ParseProgramHeaders
+bool ObjectFileELF::ParseProgramHeaders() {
+ return GetProgramHeaderInfo(m_program_headers, m_data, m_header) != 0;
+}
+
+lldb_private::Status
+ObjectFileELF::RefineModuleDetailsFromNote(lldb_private::DataExtractor &data,
+ lldb_private::ArchSpec &arch_spec,
+ lldb_private::UUID &uuid) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
+ Status error;
+
+ lldb::offset_t offset = 0;
+
+ while (true) {
+ // Parse the note header. If this fails, bail out.
+ const lldb::offset_t note_offset = offset;
+ ELFNote note = ELFNote();
+ if (!note.Parse(data, &offset)) {
+ // We're done.
+ return error;
+ }
+
+ if (log)
+ log->Printf("ObjectFileELF::%s parsing note name='%s', type=%" PRIu32,
+ __FUNCTION__, note.n_name.c_str(), note.n_type);
+
+ // Process FreeBSD ELF notes.
+ if ((note.n_name == LLDB_NT_OWNER_FREEBSD) &&
+ (note.n_type == LLDB_NT_FREEBSD_ABI_TAG) &&
+ (note.n_descsz == LLDB_NT_FREEBSD_ABI_SIZE)) {
+ // Pull out the min version info.
+ uint32_t version_info;
+ if (data.GetU32(&offset, &version_info, 1) == nullptr) {
+ error.SetErrorString("failed to read FreeBSD ABI note payload");
+ return error;
+ }
+
+ // Convert the version info into a major/minor number.
+ const uint32_t version_major = version_info / 100000;
+ const uint32_t version_minor = (version_info / 1000) % 100;
+
+ char os_name[32];
+ snprintf(os_name, sizeof(os_name), "freebsd%" PRIu32 ".%" PRIu32,
+ version_major, version_minor);
+
+ // Set the elf OS version to FreeBSD. Also clear the vendor.
+ arch_spec.GetTriple().setOSName(os_name);
+ arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::UnknownVendor);
+
+ if (log)
+ log->Printf("ObjectFileELF::%s detected FreeBSD %" PRIu32 ".%" PRIu32
+ ".%" PRIu32,
+ __FUNCTION__, version_major, version_minor,
+ static_cast<uint32_t>(version_info % 1000));
+ }
+ // Process GNU ELF notes.
+ else if (note.n_name == LLDB_NT_OWNER_GNU) {
+ switch (note.n_type) {
+ case LLDB_NT_GNU_ABI_TAG:
+ if (note.n_descsz == LLDB_NT_GNU_ABI_SIZE) {
+ // Pull out the min OS version supporting the ABI.
+ uint32_t version_info[4];
+ if (data.GetU32(&offset, &version_info[0], note.n_descsz / 4) ==
+ nullptr) {
+ error.SetErrorString("failed to read GNU ABI note payload");
+ return error;
+ }
+
+ // Set the OS per the OS field.
+ switch (version_info[0]) {
+ case LLDB_NT_GNU_ABI_OS_LINUX:
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ arch_spec.GetTriple().setVendor(
+ llvm::Triple::VendorType::UnknownVendor);
+ if (log)
+ log->Printf(
+ "ObjectFileELF::%s detected Linux, min version %" PRIu32
+ ".%" PRIu32 ".%" PRIu32,
+ __FUNCTION__, version_info[1], version_info[2],
+ version_info[3]);
+ // FIXME we have the minimal version number, we could be propagating
+ // that. version_info[1] = OS Major, version_info[2] = OS Minor,
+ // version_info[3] = Revision.
+ break;
+ case LLDB_NT_GNU_ABI_OS_HURD:
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::UnknownOS);
+ arch_spec.GetTriple().setVendor(
+ llvm::Triple::VendorType::UnknownVendor);
+ if (log)
+ log->Printf("ObjectFileELF::%s detected Hurd (unsupported), min "
+ "version %" PRIu32 ".%" PRIu32 ".%" PRIu32,
+ __FUNCTION__, version_info[1], version_info[2],
+ version_info[3]);
+ break;
+ case LLDB_NT_GNU_ABI_OS_SOLARIS:
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Solaris);
+ arch_spec.GetTriple().setVendor(
+ llvm::Triple::VendorType::UnknownVendor);
+ if (log)
+ log->Printf(
+ "ObjectFileELF::%s detected Solaris, min version %" PRIu32
+ ".%" PRIu32 ".%" PRIu32,
+ __FUNCTION__, version_info[1], version_info[2],
+ version_info[3]);
+ break;
+ default:
+ if (log)
+ log->Printf(
+ "ObjectFileELF::%s unrecognized OS in note, id %" PRIu32
+ ", min version %" PRIu32 ".%" PRIu32 ".%" PRIu32,
+ __FUNCTION__, version_info[0], version_info[1],
+ version_info[2], version_info[3]);
+ break;
+ }
+ }
+ break;
+
+ case LLDB_NT_GNU_BUILD_ID_TAG:
+ // Only bother processing this if we don't already have the uuid set.
+ if (!uuid.IsValid()) {
+ // 16 bytes is UUID|MD5, 20 bytes is SHA1. Other linkers may produce a
+ // build-id of a different length. Accept it as long as it's at least
+ // 4 bytes as it will be better than our own crc32.
+ if (note.n_descsz >= 4) {
+ if (const uint8_t *buf = data.PeekData(offset, note.n_descsz)) {
+ // Save the build id as the UUID for the module.
+ uuid = UUID::fromData(buf, note.n_descsz);
+ } else {
+ error.SetErrorString("failed to read GNU_BUILD_ID note payload");
+ return error;
+ }
+ }
+ }
+ break;
+ }
+ if (arch_spec.IsMIPS() &&
+ arch_spec.GetTriple().getOS() == llvm::Triple::OSType::UnknownOS)
+ // The note.n_name == LLDB_NT_OWNER_GNU is valid for Linux platform
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ }
+ // Process NetBSD ELF executables and shared libraries
+ else if ((note.n_name == LLDB_NT_OWNER_NETBSD) &&
+ (note.n_type == LLDB_NT_NETBSD_IDENT_TAG) &&
+ (note.n_descsz == LLDB_NT_NETBSD_IDENT_DESCSZ) &&
+ (note.n_namesz == LLDB_NT_NETBSD_IDENT_NAMESZ)) {
+ // Pull out the version info.
+ uint32_t version_info;
+ if (data.GetU32(&offset, &version_info, 1) == nullptr) {
+ error.SetErrorString("failed to read NetBSD ABI note payload");
+ return error;
+ }
+ // Convert the version info into a major/minor/patch number.
+ // #define __NetBSD_Version__ MMmmrrpp00
+ //
+ // M = major version
+ // m = minor version; a minor number of 99 indicates current.
+ // r = 0 (since NetBSD 3.0 not used)
+ // p = patchlevel
+ const uint32_t version_major = version_info / 100000000;
+ const uint32_t version_minor = (version_info % 100000000) / 1000000;
+ const uint32_t version_patch = (version_info % 10000) / 100;
+ // Set the elf OS version to NetBSD. Also clear the vendor.
+ arch_spec.GetTriple().setOSName(
+ llvm::formatv("netbsd{0}.{1}.{2}", version_major, version_minor,
+ version_patch).str());
+ arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::UnknownVendor);
+ }
+ // Process NetBSD ELF core(5) notes
+ else if ((note.n_name == LLDB_NT_OWNER_NETBSDCORE) &&
+ (note.n_type == LLDB_NT_NETBSD_PROCINFO)) {
+ // Set the elf OS version to NetBSD. Also clear the vendor.
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::NetBSD);
+ arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::UnknownVendor);
+ }
+ // Process OpenBSD ELF notes.
+ else if (note.n_name == LLDB_NT_OWNER_OPENBSD) {
+ // Set the elf OS version to OpenBSD. Also clear the vendor.
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::OpenBSD);
+ arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::UnknownVendor);
+ } else if (note.n_name == LLDB_NT_OWNER_ANDROID) {
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ arch_spec.GetTriple().setEnvironment(
+ llvm::Triple::EnvironmentType::Android);
+ } else if (note.n_name == LLDB_NT_OWNER_LINUX) {
+ // This is sometimes found in core files and usually contains extended
+ // register info
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ } else if (note.n_name == LLDB_NT_OWNER_CORE) {
+ // Parse the NT_FILE to look for stuff in paths to shared libraries As
+ // the contents look like this in a 64 bit ELF core file: count =
+ // 0x000000000000000a (10) page_size = 0x0000000000001000 (4096) Index
+ // start end file_ofs path =====
+ // 0x0000000000401000 0x0000000000000000 /tmp/a.out [ 1]
+ // 0x0000000000600000 0x0000000000601000 0x0000000000000000 /tmp/a.out [
+ // 2] 0x0000000000601000 0x0000000000602000 0x0000000000000001 /tmp/a.out
+ // [ 3] 0x00007fa79c9ed000 0x00007fa79cba8000 0x0000000000000000
+ // /lib/x86_64-linux-gnu/libc-2.19.so [ 4] 0x00007fa79cba8000
+ // 0x00007fa79cda7000 0x00000000000001bb /lib/x86_64-linux-
+ // gnu/libc-2.19.so [ 5] 0x00007fa79cda7000 0x00007fa79cdab000
+ // 0x00000000000001ba /lib/x86_64-linux-gnu/libc-2.19.so [ 6]
+ // 0x00007fa79cdab000 0x00007fa79cdad000 0x00000000000001be /lib/x86_64
+ // -linux-gnu/libc-2.19.so [ 7] 0x00007fa79cdb2000 0x00007fa79cdd5000
+ // 0x0000000000000000 /lib/x86_64-linux-gnu/ld-2.19.so [ 8]
+ // 0x00007fa79cfd4000 0x00007fa79cfd5000 0x0000000000000022 /lib/x86_64
+ // -linux-gnu/ld-2.19.so [ 9] 0x00007fa79cfd5000 0x00007fa79cfd6000
+ // 0x0000000000000023 /lib/x86_64-linux-gnu/ld-2.19.so In the 32 bit ELFs
+ // the count, page_size, start, end, file_ofs are uint32_t For reference:
+ // see readelf source code (in binutils).
+ if (note.n_type == NT_FILE) {
+ uint64_t count = data.GetAddress(&offset);
+ const char *cstr;
+ data.GetAddress(&offset); // Skip page size
+ offset += count * 3 *
+ data.GetAddressByteSize(); // Skip all start/end/file_ofs
+ for (size_t i = 0; i < count; ++i) {
+ cstr = data.GetCStr(&offset);
+ if (cstr == nullptr) {
+ error.SetErrorStringWithFormat("ObjectFileELF::%s trying to read "
+ "at an offset after the end "
+ "(GetCStr returned nullptr)",
+ __FUNCTION__);
+ return error;
+ }
+ llvm::StringRef path(cstr);
+ if (path.contains("/lib/x86_64-linux-gnu") || path.contains("/lib/i386-linux-gnu")) {
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ break;
+ }
+ }
+ if (arch_spec.IsMIPS() &&
+ arch_spec.GetTriple().getOS() == llvm::Triple::OSType::UnknownOS)
+ // In case of MIPSR6, the LLDB_NT_OWNER_GNU note is missing for some
+ // cases (e.g. compile with -nostdlib) Hence set OS to Linux
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ }
+ }
+
+ // Calculate the offset of the next note just in case "offset" has been
+ // used to poke at the contents of the note data
+ offset = note_offset + note.GetByteSize();
+ }
+
+ return error;
+}
+
+void ObjectFileELF::ParseARMAttributes(DataExtractor &data, uint64_t length,
+ ArchSpec &arch_spec) {
+ lldb::offset_t Offset = 0;
+
+ uint8_t FormatVersion = data.GetU8(&Offset);
+ if (FormatVersion != llvm::ARMBuildAttrs::Format_Version)
+ return;
+
+ Offset = Offset + sizeof(uint32_t); // Section Length
+ llvm::StringRef VendorName = data.GetCStr(&Offset);
+
+ if (VendorName != "aeabi")
+ return;
+
+ if (arch_spec.GetTriple().getEnvironment() ==
+ llvm::Triple::UnknownEnvironment)
+ arch_spec.GetTriple().setEnvironment(llvm::Triple::EABI);
+
+ while (Offset < length) {
+ uint8_t Tag = data.GetU8(&Offset);
+ uint32_t Size = data.GetU32(&Offset);
+
+ if (Tag != llvm::ARMBuildAttrs::File || Size == 0)
+ continue;
+
+ while (Offset < length) {
+ uint64_t Tag = data.GetULEB128(&Offset);
+ switch (Tag) {
+ default:
+ if (Tag < 32)
+ data.GetULEB128(&Offset);
+ else if (Tag % 2 == 0)
+ data.GetULEB128(&Offset);
+ else
+ data.GetCStr(&Offset);
+
+ break;
+
+ case llvm::ARMBuildAttrs::CPU_raw_name:
+ case llvm::ARMBuildAttrs::CPU_name:
+ data.GetCStr(&Offset);
+
+ break;
+
+ case llvm::ARMBuildAttrs::ABI_VFP_args: {
+ uint64_t VFPArgs = data.GetULEB128(&Offset);
+
+ if (VFPArgs == llvm::ARMBuildAttrs::BaseAAPCS) {
+ if (arch_spec.GetTriple().getEnvironment() ==
+ llvm::Triple::UnknownEnvironment ||
+ arch_spec.GetTriple().getEnvironment() == llvm::Triple::EABIHF)
+ arch_spec.GetTriple().setEnvironment(llvm::Triple::EABI);
+
+ arch_spec.SetFlags(ArchSpec::eARM_abi_soft_float);
+ } else if (VFPArgs == llvm::ARMBuildAttrs::HardFPAAPCS) {
+ if (arch_spec.GetTriple().getEnvironment() ==
+ llvm::Triple::UnknownEnvironment ||
+ arch_spec.GetTriple().getEnvironment() == llvm::Triple::EABI)
+ arch_spec.GetTriple().setEnvironment(llvm::Triple::EABIHF);
+
+ arch_spec.SetFlags(ArchSpec::eARM_abi_hard_float);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+}
+
+// GetSectionHeaderInfo
+size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
+ DataExtractor &object_data,
+ const elf::ELFHeader &header,
+ lldb_private::UUID &uuid,
+ std::string &gnu_debuglink_file,
+ uint32_t &gnu_debuglink_crc,
+ ArchSpec &arch_spec) {
+ // Don't reparse the section headers if we already did that.
+ if (!section_headers.empty())
+ return section_headers.size();
+
+ // Only initialize the arch_spec to okay defaults if they're not already set.
+ // We'll refine this with note data as we parse the notes.
+ if (arch_spec.GetTriple().getOS() == llvm::Triple::OSType::UnknownOS) {
+ llvm::Triple::OSType ostype;
+ llvm::Triple::OSType spec_ostype;
+ const uint32_t sub_type = subTypeFromElfHeader(header);
+ arch_spec.SetArchitecture(eArchTypeELF, header.e_machine, sub_type,
+ header.e_ident[EI_OSABI]);
+
+ // Validate if it is ok to remove GetOsFromOSABI. Note, that now the OS is
+ // determined based on EI_OSABI flag and the info extracted from ELF notes
+ // (see RefineModuleDetailsFromNote). However in some cases that still
+ // might be not enough: for example a shared library might not have any
+ // notes at all and have EI_OSABI flag set to System V, as result the OS
+ // will be set to UnknownOS.
+ GetOsFromOSABI(header.e_ident[EI_OSABI], ostype);
+ spec_ostype = arch_spec.GetTriple().getOS();
+ assert(spec_ostype == ostype);
+ UNUSED_IF_ASSERT_DISABLED(spec_ostype);
+ }
+
+ if (arch_spec.GetMachine() == llvm::Triple::mips ||
+ arch_spec.GetMachine() == llvm::Triple::mipsel ||
+ arch_spec.GetMachine() == llvm::Triple::mips64 ||
+ arch_spec.GetMachine() == llvm::Triple::mips64el) {
+ switch (header.e_flags & llvm::ELF::EF_MIPS_ARCH_ASE) {
+ case llvm::ELF::EF_MIPS_MICROMIPS:
+ arch_spec.SetFlags(ArchSpec::eMIPSAse_micromips);
+ break;
+ case llvm::ELF::EF_MIPS_ARCH_ASE_M16:
+ arch_spec.SetFlags(ArchSpec::eMIPSAse_mips16);
+ break;
+ case llvm::ELF::EF_MIPS_ARCH_ASE_MDMX:
+ arch_spec.SetFlags(ArchSpec::eMIPSAse_mdmx);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (arch_spec.GetMachine() == llvm::Triple::arm ||
+ arch_spec.GetMachine() == llvm::Triple::thumb) {
+ if (header.e_flags & llvm::ELF::EF_ARM_SOFT_FLOAT)
+ arch_spec.SetFlags(ArchSpec::eARM_abi_soft_float);
+ else if (header.e_flags & llvm::ELF::EF_ARM_VFP_FLOAT)
+ arch_spec.SetFlags(ArchSpec::eARM_abi_hard_float);
+ }
+
+ // If there are no section headers we are done.
+ if (header.e_shnum == 0)
+ return 0;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
+
+ section_headers.resize(header.e_shnum);
+ if (section_headers.size() != header.e_shnum)
+ return 0;
+
+ const size_t sh_size = header.e_shnum * header.e_shentsize;
+ const elf_off sh_offset = header.e_shoff;
+ DataExtractor sh_data;
+ if (sh_data.SetData(object_data, sh_offset, sh_size) != sh_size)
+ return 0;
+
+ uint32_t idx;
+ lldb::offset_t offset;
+ for (idx = 0, offset = 0; idx < header.e_shnum; ++idx) {
+ if (!section_headers[idx].Parse(sh_data, &offset))
+ break;
+ }
+ if (idx < section_headers.size())
+ section_headers.resize(idx);
+
+ const unsigned strtab_idx = header.e_shstrndx;
+ if (strtab_idx && strtab_idx < section_headers.size()) {
+ const ELFSectionHeaderInfo &sheader = section_headers[strtab_idx];
+ const size_t byte_size = sheader.sh_size;
+ const Elf64_Off offset = sheader.sh_offset;
+ lldb_private::DataExtractor shstr_data;
+
+ if (shstr_data.SetData(object_data, offset, byte_size) == byte_size) {
+ for (SectionHeaderCollIter I = section_headers.begin();
+ I != section_headers.end(); ++I) {
+ static ConstString g_sect_name_gnu_debuglink(".gnu_debuglink");
+ const ELFSectionHeaderInfo &sheader = *I;
+ const uint64_t section_size =
+ sheader.sh_type == SHT_NOBITS ? 0 : sheader.sh_size;
+ ConstString name(shstr_data.PeekCStr(I->sh_name));
+
+ I->section_name = name;
+
+ if (arch_spec.IsMIPS()) {
+ uint32_t arch_flags = arch_spec.GetFlags();
+ DataExtractor data;
+ if (sheader.sh_type == SHT_MIPS_ABIFLAGS) {
+
+ if (section_size && (data.SetData(object_data, sheader.sh_offset,
+ section_size) == section_size)) {
+ // MIPS ASE Mask is at offset 12 in MIPS.abiflags section
+ lldb::offset_t offset = 12; // MIPS ABI Flags Version: 0
+ arch_flags |= data.GetU32(&offset);
+
+ // The floating point ABI is at offset 7
+ offset = 7;
+ switch (data.GetU8(&offset)) {
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_ANY:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_ANY;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_DOUBLE:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_DOUBLE;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_SINGLE:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_SINGLE;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_SOFT:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_SOFT;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_OLD_64:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_OLD_64;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_XX:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_XX;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_64:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_64;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_64A:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_64A;
+ break;
+ }
+ }
+ }
+ // Settings appropriate ArchSpec ABI Flags
+ switch (header.e_flags & llvm::ELF::EF_MIPS_ABI) {
+ case llvm::ELF::EF_MIPS_ABI_O32:
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_O32;
+ break;
+ case EF_MIPS_ABI_O64:
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_O64;
+ break;
+ case EF_MIPS_ABI_EABI32:
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_EABI32;
+ break;
+ case EF_MIPS_ABI_EABI64:
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_EABI64;
+ break;
+ default:
+ // ABI Mask doesn't cover N32 and N64 ABI.
+ if (header.e_ident[EI_CLASS] == llvm::ELF::ELFCLASS64)
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_N64;
+ else if (header.e_flags & llvm::ELF::EF_MIPS_ABI2)
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_N32;
+ break;
+ }
+ arch_spec.SetFlags(arch_flags);
+ }
+
+ if (arch_spec.GetMachine() == llvm::Triple::arm ||
+ arch_spec.GetMachine() == llvm::Triple::thumb) {
+ DataExtractor data;
+
+ if (sheader.sh_type == SHT_ARM_ATTRIBUTES && section_size != 0 &&
+ data.SetData(object_data, sheader.sh_offset, section_size) == section_size)
+ ParseARMAttributes(data, section_size, arch_spec);
+ }
+
+ if (name == g_sect_name_gnu_debuglink) {
+ DataExtractor data;
+ if (section_size && (data.SetData(object_data, sheader.sh_offset,
+ section_size) == section_size)) {
+ lldb::offset_t gnu_debuglink_offset = 0;
+ gnu_debuglink_file = data.GetCStr(&gnu_debuglink_offset);
+ gnu_debuglink_offset = llvm::alignTo(gnu_debuglink_offset, 4);
+ data.GetU32(&gnu_debuglink_offset, &gnu_debuglink_crc, 1);
+ }
+ }
+
+ // Process ELF note section entries.
+ bool is_note_header = (sheader.sh_type == SHT_NOTE);
+
+ // The section header ".note.android.ident" is stored as a
+ // PROGBITS type header but it is actually a note header.
+ static ConstString g_sect_name_android_ident(".note.android.ident");
+ if (!is_note_header && name == g_sect_name_android_ident)
+ is_note_header = true;
+
+ if (is_note_header) {
+ // Allow notes to refine module info.
+ DataExtractor data;
+ if (section_size && (data.SetData(object_data, sheader.sh_offset,
+ section_size) == section_size)) {
+ Status error = RefineModuleDetailsFromNote(data, arch_spec, uuid);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("ObjectFileELF::%s ELF note processing failed: %s",
+ __FUNCTION__, error.AsCString());
+ }
+ }
+ }
+ }
+
+ // Make any unknown triple components to be unspecified unknowns.
+ if (arch_spec.GetTriple().getVendor() == llvm::Triple::UnknownVendor)
+ arch_spec.GetTriple().setVendorName(llvm::StringRef());
+ if (arch_spec.GetTriple().getOS() == llvm::Triple::UnknownOS)
+ arch_spec.GetTriple().setOSName(llvm::StringRef());
+
+ return section_headers.size();
+ }
+ }
+
+ section_headers.clear();
+ return 0;
+}
+
+llvm::StringRef
+ObjectFileELF::StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const {
+ size_t pos = symbol_name.find('@');
+ return symbol_name.substr(0, pos);
+}
+
+// ParseSectionHeaders
+size_t ObjectFileELF::ParseSectionHeaders() {
+ return GetSectionHeaderInfo(m_section_headers, m_data, m_header, m_uuid,
+ m_gnu_debuglink_file, m_gnu_debuglink_crc,
+ m_arch_spec);
+}
+
+const ObjectFileELF::ELFSectionHeaderInfo *
+ObjectFileELF::GetSectionHeaderByIndex(lldb::user_id_t id) {
+ if (!ParseSectionHeaders())
+ return nullptr;
+
+ if (id < m_section_headers.size())
+ return &m_section_headers[id];
+
+ return nullptr;
+}
+
+lldb::user_id_t ObjectFileELF::GetSectionIndexByName(const char *name) {
+ if (!name || !name[0] || !ParseSectionHeaders())
+ return 0;
+ for (size_t i = 1; i < m_section_headers.size(); ++i)
+ if (m_section_headers[i].section_name == ConstString(name))
+ return i;
+ return 0;
+}
+
+static SectionType GetSectionTypeFromName(llvm::StringRef Name) {
+ if (Name.consume_front(".debug_") || Name.consume_front(".zdebug_")) {
+ return llvm::StringSwitch<SectionType>(Name)
+ .Case("abbrev", eSectionTypeDWARFDebugAbbrev)
+ .Case("abbrev.dwo", eSectionTypeDWARFDebugAbbrevDwo)
+ .Case("addr", eSectionTypeDWARFDebugAddr)
+ .Case("aranges", eSectionTypeDWARFDebugAranges)
+ .Case("cu_index", eSectionTypeDWARFDebugCuIndex)
+ .Case("frame", eSectionTypeDWARFDebugFrame)
+ .Case("info", eSectionTypeDWARFDebugInfo)
+ .Case("info.dwo", eSectionTypeDWARFDebugInfoDwo)
+ .Cases("line", "line.dwo", eSectionTypeDWARFDebugLine)
+ .Cases("line_str", "line_str.dwo", eSectionTypeDWARFDebugLineStr)
+ .Cases("loc", "loc.dwo", eSectionTypeDWARFDebugLoc)
+ .Cases("loclists", "loclists.dwo", eSectionTypeDWARFDebugLocLists)
+ .Case("macinfo", eSectionTypeDWARFDebugMacInfo)
+ .Cases("macro", "macro.dwo", eSectionTypeDWARFDebugMacro)
+ .Case("names", eSectionTypeDWARFDebugNames)
+ .Case("pubnames", eSectionTypeDWARFDebugPubNames)
+ .Case("pubtypes", eSectionTypeDWARFDebugPubTypes)
+ .Case("ranges", eSectionTypeDWARFDebugRanges)
+ .Case("rnglists", eSectionTypeDWARFDebugRngLists)
+ .Case("str", eSectionTypeDWARFDebugStr)
+ .Case("str.dwo", eSectionTypeDWARFDebugStrDwo)
+ .Case("str_offsets", eSectionTypeDWARFDebugStrOffsets)
+ .Case("str_offsets.dwo", eSectionTypeDWARFDebugStrOffsetsDwo)
+ .Case("types", eSectionTypeDWARFDebugTypes)
+ .Case("types.dwo", eSectionTypeDWARFDebugTypesDwo)
+ .Default(eSectionTypeOther);
+ }
+ return llvm::StringSwitch<SectionType>(Name)
+ .Case(".ARM.exidx", eSectionTypeARMexidx)
+ .Case(".ARM.extab", eSectionTypeARMextab)
+ .Cases(".bss", ".tbss", eSectionTypeZeroFill)
+ .Cases(".data", ".tdata", eSectionTypeData)
+ .Case(".eh_frame", eSectionTypeEHFrame)
+ .Case(".gnu_debugaltlink", eSectionTypeDWARFGNUDebugAltLink)
+ .Case(".gosymtab", eSectionTypeGoSymtab)
+ .Case(".text", eSectionTypeCode)
+ .Default(eSectionTypeOther);
+}
+
+SectionType ObjectFileELF::GetSectionType(const ELFSectionHeaderInfo &H) const {
+ switch (H.sh_type) {
+ case SHT_PROGBITS:
+ if (H.sh_flags & SHF_EXECINSTR)
+ return eSectionTypeCode;
+ break;
+ case SHT_SYMTAB:
+ return eSectionTypeELFSymbolTable;
+ case SHT_DYNSYM:
+ return eSectionTypeELFDynamicSymbols;
+ case SHT_RELA:
+ case SHT_REL:
+ return eSectionTypeELFRelocationEntries;
+ case SHT_DYNAMIC:
+ return eSectionTypeELFDynamicLinkInfo;
+ }
+ return GetSectionTypeFromName(H.section_name.GetStringRef());
+}
+
+static uint32_t GetTargetByteSize(SectionType Type, const ArchSpec &arch) {
+ switch (Type) {
+ case eSectionTypeData:
+ case eSectionTypeZeroFill:
+ return arch.GetDataByteSize();
+ case eSectionTypeCode:
+ return arch.GetCodeByteSize();
+ default:
+ return 1;
+ }
+}
+
+static Permissions GetPermissions(const ELFSectionHeader &H) {
+ Permissions Perm = Permissions(0);
+ if (H.sh_flags & SHF_ALLOC)
+ Perm |= ePermissionsReadable;
+ if (H.sh_flags & SHF_WRITE)
+ Perm |= ePermissionsWritable;
+ if (H.sh_flags & SHF_EXECINSTR)
+ Perm |= ePermissionsExecutable;
+ return Perm;
+}
+
+static Permissions GetPermissions(const ELFProgramHeader &H) {
+ Permissions Perm = Permissions(0);
+ if (H.p_flags & PF_R)
+ Perm |= ePermissionsReadable;
+ if (H.p_flags & PF_W)
+ Perm |= ePermissionsWritable;
+ if (H.p_flags & PF_X)
+ Perm |= ePermissionsExecutable;
+ return Perm;
+}
+
+namespace {
+
+using VMRange = lldb_private::Range<addr_t, addr_t>;
+
+struct SectionAddressInfo {
+ SectionSP Segment;
+ VMRange Range;
+};
+
+// (Unlinked) ELF object files usually have 0 for every section address, meaning
+// we need to compute synthetic addresses in order for "file addresses" from
+// different sections to not overlap. This class handles that logic.
+class VMAddressProvider {
+ using VMMap = llvm::IntervalMap<addr_t, SectionSP, 4,
+ llvm::IntervalMapHalfOpenInfo<addr_t>>;
+
+ ObjectFile::Type ObjectType;
+ addr_t NextVMAddress = 0;
+ VMMap::Allocator Alloc;
+ VMMap Segments = VMMap(Alloc);
+ VMMap Sections = VMMap(Alloc);
+ lldb_private::Log *Log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES);
+
+ VMRange GetVMRange(const ELFSectionHeader &H) {
+ addr_t Address = H.sh_addr;
+ addr_t Size = H.sh_flags & SHF_ALLOC ? H.sh_size : 0;
+ if (ObjectType == ObjectFile::Type::eTypeObjectFile && Segments.empty() && (H.sh_flags & SHF_ALLOC)) {
+ NextVMAddress =
+ llvm::alignTo(NextVMAddress, std::max<addr_t>(H.sh_addralign, 1));
+ Address = NextVMAddress;
+ NextVMAddress += Size;
+ }
+ return VMRange(Address, Size);
+ }
+
+public:
+ VMAddressProvider(ObjectFile::Type Type) : ObjectType(Type) {}
+
+ llvm::Optional<VMRange> GetAddressInfo(const ELFProgramHeader &H) {
+ if (H.p_memsz == 0) {
+ LLDB_LOG(Log,
+ "Ignoring zero-sized PT_LOAD segment. Corrupt object file?");
+ return llvm::None;
+ }
+
+ if (Segments.overlaps(H.p_vaddr, H.p_vaddr + H.p_memsz)) {
+ LLDB_LOG(Log,
+ "Ignoring overlapping PT_LOAD segment. Corrupt object file?");
+ return llvm::None;
+ }
+ return VMRange(H.p_vaddr, H.p_memsz);
+ }
+
+ llvm::Optional<SectionAddressInfo> GetAddressInfo(const ELFSectionHeader &H) {
+ VMRange Range = GetVMRange(H);
+ SectionSP Segment;
+ auto It = Segments.find(Range.GetRangeBase());
+ if ((H.sh_flags & SHF_ALLOC) && It.valid()) {
+ addr_t MaxSize;
+ if (It.start() <= Range.GetRangeBase()) {
+ MaxSize = It.stop() - Range.GetRangeBase();
+ Segment = *It;
+ } else
+ MaxSize = It.start() - Range.GetRangeBase();
+ if (Range.GetByteSize() > MaxSize) {
+ LLDB_LOG(Log, "Shortening section crossing segment boundaries. "
+ "Corrupt object file?");
+ Range.SetByteSize(MaxSize);
+ }
+ }
+ if (Range.GetByteSize() > 0 &&
+ Sections.overlaps(Range.GetRangeBase(), Range.GetRangeEnd())) {
+ LLDB_LOG(Log, "Ignoring overlapping section. Corrupt object file?");
+ return llvm::None;
+ }
+ if (Segment)
+ Range.Slide(-Segment->GetFileAddress());
+ return SectionAddressInfo{Segment, Range};
+ }
+
+ void AddSegment(const VMRange &Range, SectionSP Seg) {
+ Segments.insert(Range.GetRangeBase(), Range.GetRangeEnd(), std::move(Seg));
+ }
+
+ void AddSection(SectionAddressInfo Info, SectionSP Sect) {
+ if (Info.Range.GetByteSize() == 0)
+ return;
+ if (Info.Segment)
+ Info.Range.Slide(Info.Segment->GetFileAddress());
+ Sections.insert(Info.Range.GetRangeBase(), Info.Range.GetRangeEnd(),
+ std::move(Sect));
+ }
+};
+}
+
+void ObjectFileELF::CreateSections(SectionList &unified_section_list) {
+ if (m_sections_up)
+ return;
+
+ m_sections_up = llvm::make_unique<SectionList>();
+ VMAddressProvider address_provider(GetType());
+
+ size_t LoadID = 0;
+ for (const auto &EnumPHdr : llvm::enumerate(ProgramHeaders())) {
+ const ELFProgramHeader &PHdr = EnumPHdr.value();
+ if (PHdr.p_type != PT_LOAD)
+ continue;
+
+ auto InfoOr = address_provider.GetAddressInfo(PHdr);
+ if (!InfoOr)
+ continue;
+
+ ConstString Name(("PT_LOAD[" + llvm::Twine(LoadID++) + "]").str());
+ uint32_t Log2Align = llvm::Log2_64(std::max<elf_xword>(PHdr.p_align, 1));
+ SectionSP Segment = std::make_shared<Section>(
+ GetModule(), this, SegmentID(EnumPHdr.index()), Name,
+ eSectionTypeContainer, InfoOr->GetRangeBase(), InfoOr->GetByteSize(),
+ PHdr.p_offset, PHdr.p_filesz, Log2Align, /*flags*/ 0);
+ Segment->SetPermissions(GetPermissions(PHdr));
+ m_sections_up->AddSection(Segment);
+
+ address_provider.AddSegment(*InfoOr, std::move(Segment));
+ }
+
+ ParseSectionHeaders();
+ if (m_section_headers.empty())
+ return;
+
+ for (SectionHeaderCollIter I = std::next(m_section_headers.begin());
+ I != m_section_headers.end(); ++I) {
+ const ELFSectionHeaderInfo &header = *I;
+
+ ConstString &name = I->section_name;
+ const uint64_t file_size =
+ header.sh_type == SHT_NOBITS ? 0 : header.sh_size;
+
+ auto InfoOr = address_provider.GetAddressInfo(header);
+ if (!InfoOr)
+ continue;
+
+ SectionType sect_type = GetSectionType(header);
+
+ const uint32_t target_bytes_size =
+ GetTargetByteSize(sect_type, m_arch_spec);
+
+ elf::elf_xword log2align =
+ (header.sh_addralign == 0) ? 0 : llvm::Log2_64(header.sh_addralign);
+
+ SectionSP section_sp(new Section(
+ InfoOr->Segment, GetModule(), // Module to which this section belongs.
+ this, // ObjectFile to which this section belongs and should
+ // read section data from.
+ SectionIndex(I), // Section ID.
+ name, // Section name.
+ sect_type, // Section type.
+ InfoOr->Range.GetRangeBase(), // VM address.
+ InfoOr->Range.GetByteSize(), // VM size in bytes of this section.
+ header.sh_offset, // Offset of this section in the file.
+ file_size, // Size of the section as found in the file.
+ log2align, // Alignment of the section
+ header.sh_flags, // Flags for this section.
+ target_bytes_size)); // Number of host bytes per target byte
+
+ section_sp->SetPermissions(GetPermissions(header));
+ section_sp->SetIsThreadSpecific(header.sh_flags & SHF_TLS);
+ (InfoOr->Segment ? InfoOr->Segment->GetChildren() : *m_sections_up)
+ .AddSection(section_sp);
+ address_provider.AddSection(std::move(*InfoOr), std::move(section_sp));
+ }
+
+ // For eTypeDebugInfo files, the Symbol Vendor will take care of updating the
+ // unified section list.
+ if (GetType() != eTypeDebugInfo)
+ unified_section_list = *m_sections_up;
+}
+
+// Find the arm/aarch64 mapping symbol character in the given symbol name.
+// Mapping symbols have the form of "$<char>[.<any>]*". Additionally we
+// recognize cases when the mapping symbol prefixed by an arbitrary string
+// because if a symbol prefix added to each symbol in the object file with
+// objcopy then the mapping symbols are also prefixed.
+static char FindArmAarch64MappingSymbol(const char *symbol_name) {
+ if (!symbol_name)
+ return '\0';
+
+ const char *dollar_pos = ::strchr(symbol_name, '$');
+ if (!dollar_pos || dollar_pos[1] == '\0')
+ return '\0';
+
+ if (dollar_pos[2] == '\0' || dollar_pos[2] == '.')
+ return dollar_pos[1];
+ return '\0';
+}
+
+#define STO_MIPS_ISA (3 << 6)
+#define STO_MICROMIPS (2 << 6)
+#define IS_MICROMIPS(ST_OTHER) (((ST_OTHER)&STO_MIPS_ISA) == STO_MICROMIPS)
+
+// private
+unsigned ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id,
+ SectionList *section_list,
+ const size_t num_symbols,
+ const DataExtractor &symtab_data,
+ const DataExtractor &strtab_data) {
+ ELFSymbol symbol;
+ lldb::offset_t offset = 0;
+
+ static ConstString text_section_name(".text");
+ static ConstString init_section_name(".init");
+ static ConstString fini_section_name(".fini");
+ static ConstString ctors_section_name(".ctors");
+ static ConstString dtors_section_name(".dtors");
+
+ static ConstString data_section_name(".data");
+ static ConstString rodata_section_name(".rodata");
+ static ConstString rodata1_section_name(".rodata1");
+ static ConstString data2_section_name(".data1");
+ static ConstString bss_section_name(".bss");
+ static ConstString opd_section_name(".opd"); // For ppc64
+
+ // On Android the oatdata and the oatexec symbols in the oat and odex files
+ // covers the full .text section what causes issues with displaying unusable
+ // symbol name to the user and very slow unwinding speed because the
+ // instruction emulation based unwind plans try to emulate all instructions
+ // in these symbols. Don't add these symbols to the symbol list as they have
+ // no use for the debugger and they are causing a lot of trouble. Filtering
+ // can't be restricted to Android because this special object file don't
+ // contain the note section specifying the environment to Android but the
+ // custom extension and file name makes it highly unlikely that this will
+ // collide with anything else.
+ ConstString file_extension = m_file.GetFileNameExtension();
+ bool skip_oatdata_oatexec =
+ file_extension == ".oat" || file_extension == ".odex";
+
+ ArchSpec arch = GetArchitecture();
+ ModuleSP module_sp(GetModule());
+ SectionList *module_section_list =
+ module_sp ? module_sp->GetSectionList() : nullptr;
+
+ // Local cache to avoid doing a FindSectionByName for each symbol. The "const
+ // char*" key must came from a ConstString object so they can be compared by
+ // pointer
+ std::unordered_map<const char *, lldb::SectionSP> section_name_to_section;
+
+ unsigned i;
+ for (i = 0; i < num_symbols; ++i) {
+ if (!symbol.Parse(symtab_data, &offset))
+ break;
+
+ const char *symbol_name = strtab_data.PeekCStr(symbol.st_name);
+ if (!symbol_name)
+ symbol_name = "";
+
+ // No need to add non-section symbols that have no names
+ if (symbol.getType() != STT_SECTION &&
+ (symbol_name == nullptr || symbol_name[0] == '\0'))
+ continue;
+
+ // Skipping oatdata and oatexec sections if it is requested. See details
+ // above the definition of skip_oatdata_oatexec for the reasons.
+ if (skip_oatdata_oatexec && (::strcmp(symbol_name, "oatdata") == 0 ||
+ ::strcmp(symbol_name, "oatexec") == 0))
+ continue;
+
+ SectionSP symbol_section_sp;
+ SymbolType symbol_type = eSymbolTypeInvalid;
+ Elf64_Half shndx = symbol.st_shndx;
+
+ switch (shndx) {
+ case SHN_ABS:
+ symbol_type = eSymbolTypeAbsolute;
+ break;
+ case SHN_UNDEF:
+ symbol_type = eSymbolTypeUndefined;
+ break;
+ default:
+ symbol_section_sp = section_list->FindSectionByID(shndx);
+ break;
+ }
+
+ // If a symbol is undefined do not process it further even if it has a STT
+ // type
+ if (symbol_type != eSymbolTypeUndefined) {
+ switch (symbol.getType()) {
+ default:
+ case STT_NOTYPE:
+ // The symbol's type is not specified.
+ break;
+
+ case STT_OBJECT:
+ // The symbol is associated with a data object, such as a variable, an
+ // array, etc.
+ symbol_type = eSymbolTypeData;
+ break;
+
+ case STT_FUNC:
+ // The symbol is associated with a function or other executable code.
+ symbol_type = eSymbolTypeCode;
+ break;
+
+ case STT_SECTION:
+ // The symbol is associated with a section. Symbol table entries of
+ // this type exist primarily for relocation and normally have STB_LOCAL
+ // binding.
+ break;
+
+ case STT_FILE:
+ // Conventionally, the symbol's name gives the name of the source file
+ // associated with the object file. A file symbol has STB_LOCAL
+ // binding, its section index is SHN_ABS, and it precedes the other
+ // STB_LOCAL symbols for the file, if it is present.
+ symbol_type = eSymbolTypeSourceFile;
+ break;
+
+ case STT_GNU_IFUNC:
+ // The symbol is associated with an indirect function. The actual
+ // function will be resolved if it is referenced.
+ symbol_type = eSymbolTypeResolver;
+ break;
+ }
+ }
+
+ if (symbol_type == eSymbolTypeInvalid && symbol.getType() != STT_SECTION) {
+ if (symbol_section_sp) {
+ ConstString sect_name = symbol_section_sp->GetName();
+ if (sect_name == text_section_name || sect_name == init_section_name ||
+ sect_name == fini_section_name || sect_name == ctors_section_name ||
+ sect_name == dtors_section_name) {
+ symbol_type = eSymbolTypeCode;
+ } else if (sect_name == data_section_name ||
+ sect_name == data2_section_name ||
+ sect_name == rodata_section_name ||
+ sect_name == rodata1_section_name ||
+ sect_name == bss_section_name) {
+ symbol_type = eSymbolTypeData;
+ }
+ }
+ }
+
+ int64_t symbol_value_offset = 0;
+ uint32_t additional_flags = 0;
+
+ if (arch.IsValid()) {
+ if (arch.GetMachine() == llvm::Triple::arm) {
+ if (symbol.getBinding() == STB_LOCAL) {
+ char mapping_symbol = FindArmAarch64MappingSymbol(symbol_name);
+ if (symbol_type == eSymbolTypeCode) {
+ switch (mapping_symbol) {
+ case 'a':
+ // $a[.<any>]* - marks an ARM instruction sequence
+ m_address_class_map[symbol.st_value] = AddressClass::eCode;
+ break;
+ case 'b':
+ case 't':
+ // $b[.<any>]* - marks a THUMB BL instruction sequence
+ // $t[.<any>]* - marks a THUMB instruction sequence
+ m_address_class_map[symbol.st_value] =
+ AddressClass::eCodeAlternateISA;
+ break;
+ case 'd':
+ // $d[.<any>]* - marks a data item sequence (e.g. lit pool)
+ m_address_class_map[symbol.st_value] = AddressClass::eData;
+ break;
+ }
+ }
+ if (mapping_symbol)
+ continue;
+ }
+ } else if (arch.GetMachine() == llvm::Triple::aarch64) {
+ if (symbol.getBinding() == STB_LOCAL) {
+ char mapping_symbol = FindArmAarch64MappingSymbol(symbol_name);
+ if (symbol_type == eSymbolTypeCode) {
+ switch (mapping_symbol) {
+ case 'x':
+ // $x[.<any>]* - marks an A64 instruction sequence
+ m_address_class_map[symbol.st_value] = AddressClass::eCode;
+ break;
+ case 'd':
+ // $d[.<any>]* - marks a data item sequence (e.g. lit pool)
+ m_address_class_map[symbol.st_value] = AddressClass::eData;
+ break;
+ }
+ }
+ if (mapping_symbol)
+ continue;
+ }
+ }
+
+ if (arch.GetMachine() == llvm::Triple::arm) {
+ if (symbol_type == eSymbolTypeCode) {
+ if (symbol.st_value & 1) {
+ // Subtracting 1 from the address effectively unsets the low order
+ // bit, which results in the address actually pointing to the
+ // beginning of the symbol. This delta will be used below in
+ // conjunction with symbol.st_value to produce the final
+ // symbol_value that we store in the symtab.
+ symbol_value_offset = -1;
+ m_address_class_map[symbol.st_value ^ 1] =
+ AddressClass::eCodeAlternateISA;
+ } else {
+ // This address is ARM
+ m_address_class_map[symbol.st_value] = AddressClass::eCode;
+ }
+ }
+ }
+
+ /*
+ * MIPS:
+ * The bit #0 of an address is used for ISA mode (1 for microMIPS, 0 for
+ * MIPS).
+ * This allows processor to switch between microMIPS and MIPS without any
+ * need
+ * for special mode-control register. However, apart from .debug_line,
+ * none of
+ * the ELF/DWARF sections set the ISA bit (for symbol or section). Use
+ * st_other
+ * flag to check whether the symbol is microMIPS and then set the address
+ * class
+ * accordingly.
+ */
+ if (arch.IsMIPS()) {
+ if (IS_MICROMIPS(symbol.st_other))
+ m_address_class_map[symbol.st_value] = AddressClass::eCodeAlternateISA;
+ else if ((symbol.st_value & 1) && (symbol_type == eSymbolTypeCode)) {
+ symbol.st_value = symbol.st_value & (~1ull);
+ m_address_class_map[symbol.st_value] = AddressClass::eCodeAlternateISA;
+ } else {
+ if (symbol_type == eSymbolTypeCode)
+ m_address_class_map[symbol.st_value] = AddressClass::eCode;
+ else if (symbol_type == eSymbolTypeData)
+ m_address_class_map[symbol.st_value] = AddressClass::eData;
+ else
+ m_address_class_map[symbol.st_value] = AddressClass::eUnknown;
+ }
+ }
+ }
+
+ // symbol_value_offset may contain 0 for ARM symbols or -1 for THUMB
+ // symbols. See above for more details.
+ uint64_t symbol_value = symbol.st_value + symbol_value_offset;
+
+ if (symbol_section_sp == nullptr && shndx == SHN_ABS &&
+ symbol.st_size != 0) {
+ // We don't have a section for a symbol with non-zero size. Create a new
+ // section for it so the address range covered by the symbol is also
+ // covered by the module (represented through the section list). It is
+ // needed so module lookup for the addresses covered by this symbol will
+ // be successfull. This case happens for absolute symbols.
+ ConstString fake_section_name(std::string(".absolute.") + symbol_name);
+ symbol_section_sp =
+ std::make_shared<Section>(module_sp, this, SHN_ABS, fake_section_name,
+ eSectionTypeAbsoluteAddress, symbol_value,
+ symbol.st_size, 0, 0, 0, SHF_ALLOC);
+
+ module_section_list->AddSection(symbol_section_sp);
+ section_list->AddSection(symbol_section_sp);
+ }
+
+ if (symbol_section_sp &&
+ CalculateType() != ObjectFile::Type::eTypeObjectFile)
+ symbol_value -= symbol_section_sp->GetFileAddress();
+
+ if (symbol_section_sp && module_section_list &&
+ module_section_list != section_list) {
+ ConstString sect_name = symbol_section_sp->GetName();
+ auto section_it = section_name_to_section.find(sect_name.GetCString());
+ if (section_it == section_name_to_section.end())
+ section_it =
+ section_name_to_section
+ .emplace(sect_name.GetCString(),
+ module_section_list->FindSectionByName(sect_name))
+ .first;
+ if (section_it->second)
+ symbol_section_sp = section_it->second;
+ }
+
+ bool is_global = symbol.getBinding() == STB_GLOBAL;
+ uint32_t flags = symbol.st_other << 8 | symbol.st_info | additional_flags;
+ bool is_mangled = (symbol_name[0] == '_' && symbol_name[1] == 'Z');
+
+ llvm::StringRef symbol_ref(symbol_name);
+
+ // Symbol names may contain @VERSION suffixes. Find those and strip them
+ // temporarily.
+ size_t version_pos = symbol_ref.find('@');
+ bool has_suffix = version_pos != llvm::StringRef::npos;
+ llvm::StringRef symbol_bare = symbol_ref.substr(0, version_pos);
+ Mangled mangled(ConstString(symbol_bare), is_mangled);
+
+ // Now append the suffix back to mangled and unmangled names. Only do it if
+ // the demangling was successful (string is not empty).
+ if (has_suffix) {
+ llvm::StringRef suffix = symbol_ref.substr(version_pos);
+
+ llvm::StringRef mangled_name = mangled.GetMangledName().GetStringRef();
+ if (!mangled_name.empty())
+ mangled.SetMangledName(ConstString((mangled_name + suffix).str()));
+
+ ConstString demangled =
+ mangled.GetDemangledName(lldb::eLanguageTypeUnknown);
+ llvm::StringRef demangled_name = demangled.GetStringRef();
+ if (!demangled_name.empty())
+ mangled.SetDemangledName(ConstString((demangled_name + suffix).str()));
+ }
+
+ // In ELF all symbol should have a valid size but it is not true for some
+ // function symbols coming from hand written assembly. As none of the
+ // function symbol should have 0 size we try to calculate the size for
+ // these symbols in the symtab with saying that their original size is not
+ // valid.
+ bool symbol_size_valid =
+ symbol.st_size != 0 || symbol.getType() != STT_FUNC;
+
+ Symbol dc_symbol(
+ i + start_id, // ID is the original symbol table index.
+ mangled,
+ symbol_type, // Type of this symbol
+ is_global, // Is this globally visible?
+ false, // Is this symbol debug info?
+ false, // Is this symbol a trampoline?
+ false, // Is this symbol artificial?
+ AddressRange(symbol_section_sp, // Section in which this symbol is
+ // defined or null.
+ symbol_value, // Offset in section or symbol value.
+ symbol.st_size), // Size in bytes of this symbol.
+ symbol_size_valid, // Symbol size is valid
+ has_suffix, // Contains linker annotations?
+ flags); // Symbol flags.
+ symtab->AddSymbol(dc_symbol);
+ }
+ return i;
+}
+
+unsigned ObjectFileELF::ParseSymbolTable(Symtab *symbol_table,
+ user_id_t start_id,
+ lldb_private::Section *symtab) {
+ if (symtab->GetObjectFile() != this) {
+ // If the symbol table section is owned by a different object file, have it
+ // do the parsing.
+ ObjectFileELF *obj_file_elf =
+ static_cast<ObjectFileELF *>(symtab->GetObjectFile());
+ return obj_file_elf->ParseSymbolTable(symbol_table, start_id, symtab);
+ }
+
+ // Get section list for this object file.
+ SectionList *section_list = m_sections_up.get();
+ if (!section_list)
+ return 0;
+
+ user_id_t symtab_id = symtab->GetID();
+ const ELFSectionHeaderInfo *symtab_hdr = GetSectionHeaderByIndex(symtab_id);
+ assert(symtab_hdr->sh_type == SHT_SYMTAB ||
+ symtab_hdr->sh_type == SHT_DYNSYM);
+
+ // sh_link: section header index of associated string table.
+ user_id_t strtab_id = symtab_hdr->sh_link;
+ Section *strtab = section_list->FindSectionByID(strtab_id).get();
+
+ if (symtab && strtab) {
+ assert(symtab->GetObjectFile() == this);
+ assert(strtab->GetObjectFile() == this);
+
+ DataExtractor symtab_data;
+ DataExtractor strtab_data;
+ if (ReadSectionData(symtab, symtab_data) &&
+ ReadSectionData(strtab, strtab_data)) {
+ size_t num_symbols = symtab_data.GetByteSize() / symtab_hdr->sh_entsize;
+
+ return ParseSymbols(symbol_table, start_id, section_list, num_symbols,
+ symtab_data, strtab_data);
+ }
+ }
+
+ return 0;
+}
+
+size_t ObjectFileELF::ParseDynamicSymbols() {
+ if (m_dynamic_symbols.size())
+ return m_dynamic_symbols.size();
+
+ SectionList *section_list = GetSectionList();
+ if (!section_list)
+ return 0;
+
+ // Find the SHT_DYNAMIC section.
+ Section *dynsym =
+ section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
+ .get();
+ if (!dynsym)
+ return 0;
+ assert(dynsym->GetObjectFile() == this);
+
+ ELFDynamic symbol;
+ DataExtractor dynsym_data;
+ if (ReadSectionData(dynsym, dynsym_data)) {
+ const lldb::offset_t section_size = dynsym_data.GetByteSize();
+ lldb::offset_t cursor = 0;
+
+ while (cursor < section_size) {
+ if (!symbol.Parse(dynsym_data, &cursor))
+ break;
+
+ m_dynamic_symbols.push_back(symbol);
+ }
+ }
+
+ return m_dynamic_symbols.size();
+}
+
+const ELFDynamic *ObjectFileELF::FindDynamicSymbol(unsigned tag) {
+ if (!ParseDynamicSymbols())
+ return nullptr;
+
+ DynamicSymbolCollIter I = m_dynamic_symbols.begin();
+ DynamicSymbolCollIter E = m_dynamic_symbols.end();
+ for (; I != E; ++I) {
+ ELFDynamic *symbol = &*I;
+
+ if (symbol->d_tag == tag)
+ return symbol;
+ }
+
+ return nullptr;
+}
+
+unsigned ObjectFileELF::PLTRelocationType() {
+ // DT_PLTREL
+ // This member specifies the type of relocation entry to which the
+ // procedure linkage table refers. The d_val member holds DT_REL or
+ // DT_RELA, as appropriate. All relocations in a procedure linkage table
+ // must use the same relocation.
+ const ELFDynamic *symbol = FindDynamicSymbol(DT_PLTREL);
+
+ if (symbol)
+ return symbol->d_val;
+
+ return 0;
+}
+
+// Returns the size of the normal plt entries and the offset of the first
+// normal plt entry. The 0th entry in the plt table is usually a resolution
+// entry which have different size in some architectures then the rest of the
+// plt entries.
+static std::pair<uint64_t, uint64_t>
+GetPltEntrySizeAndOffset(const ELFSectionHeader *rel_hdr,
+ const ELFSectionHeader *plt_hdr) {
+ const elf_xword num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize;
+
+ // Clang 3.3 sets entsize to 4 for 32-bit binaries, but the plt entries are
+ // 16 bytes. So round the entsize up by the alignment if addralign is set.
+ elf_xword plt_entsize =
+ plt_hdr->sh_addralign
+ ? llvm::alignTo(plt_hdr->sh_entsize, plt_hdr->sh_addralign)
+ : plt_hdr->sh_entsize;
+
+ // Some linkers e.g ld for arm, fill plt_hdr->sh_entsize field incorrectly.
+ // PLT entries relocation code in general requires multiple instruction and
+ // should be greater than 4 bytes in most cases. Try to guess correct size
+ // just in case.
+ if (plt_entsize <= 4) {
+ // The linker haven't set the plt_hdr->sh_entsize field. Try to guess the
+ // size of the plt entries based on the number of entries and the size of
+ // the plt section with the assumption that the size of the 0th entry is at
+ // least as big as the size of the normal entries and it isn't much bigger
+ // then that.
+ if (plt_hdr->sh_addralign)
+ plt_entsize = plt_hdr->sh_size / plt_hdr->sh_addralign /
+ (num_relocations + 1) * plt_hdr->sh_addralign;
+ else
+ plt_entsize = plt_hdr->sh_size / (num_relocations + 1);
+ }
+
+ elf_xword plt_offset = plt_hdr->sh_size - num_relocations * plt_entsize;
+
+ return std::make_pair(plt_entsize, plt_offset);
+}
+
+static unsigned ParsePLTRelocations(
+ Symtab *symbol_table, user_id_t start_id, unsigned rel_type,
+ const ELFHeader *hdr, const ELFSectionHeader *rel_hdr,
+ const ELFSectionHeader *plt_hdr, const ELFSectionHeader *sym_hdr,
+ const lldb::SectionSP &plt_section_sp, DataExtractor &rel_data,
+ DataExtractor &symtab_data, DataExtractor &strtab_data) {
+ ELFRelocation rel(rel_type);
+ ELFSymbol symbol;
+ lldb::offset_t offset = 0;
+
+ uint64_t plt_offset, plt_entsize;
+ std::tie(plt_entsize, plt_offset) =
+ GetPltEntrySizeAndOffset(rel_hdr, plt_hdr);
+ const elf_xword num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize;
+
+ typedef unsigned (*reloc_info_fn)(const ELFRelocation &rel);
+ reloc_info_fn reloc_type;
+ reloc_info_fn reloc_symbol;
+
+ if (hdr->Is32Bit()) {
+ reloc_type = ELFRelocation::RelocType32;
+ reloc_symbol = ELFRelocation::RelocSymbol32;
+ } else {
+ reloc_type = ELFRelocation::RelocType64;
+ reloc_symbol = ELFRelocation::RelocSymbol64;
+ }
+
+ unsigned slot_type = hdr->GetRelocationJumpSlotType();
+ unsigned i;
+ for (i = 0; i < num_relocations; ++i) {
+ if (!rel.Parse(rel_data, &offset))
+ break;
+
+ if (reloc_type(rel) != slot_type)
+ continue;
+
+ lldb::offset_t symbol_offset = reloc_symbol(rel) * sym_hdr->sh_entsize;
+ if (!symbol.Parse(symtab_data, &symbol_offset))
+ break;
+
+ const char *symbol_name = strtab_data.PeekCStr(symbol.st_name);
+ bool is_mangled =
+ symbol_name ? (symbol_name[0] == '_' && symbol_name[1] == 'Z') : false;
+ uint64_t plt_index = plt_offset + i * plt_entsize;
+
+ Symbol jump_symbol(
+ i + start_id, // Symbol table index
+ symbol_name, // symbol name.
+ is_mangled, // is the symbol name mangled?
+ eSymbolTypeTrampoline, // Type of this symbol
+ false, // Is this globally visible?
+ false, // Is this symbol debug info?
+ true, // Is this symbol a trampoline?
+ true, // Is this symbol artificial?
+ plt_section_sp, // Section in which this symbol is defined or null.
+ plt_index, // Offset in section or symbol value.
+ plt_entsize, // Size in bytes of this symbol.
+ true, // Size is valid
+ false, // Contains linker annotations?
+ 0); // Symbol flags.
+
+ symbol_table->AddSymbol(jump_symbol);
+ }
+
+ return i;
+}
+
+unsigned
+ObjectFileELF::ParseTrampolineSymbols(Symtab *symbol_table, user_id_t start_id,
+ const ELFSectionHeaderInfo *rel_hdr,
+ user_id_t rel_id) {
+ assert(rel_hdr->sh_type == SHT_RELA || rel_hdr->sh_type == SHT_REL);
+
+ // The link field points to the associated symbol table.
+ user_id_t symtab_id = rel_hdr->sh_link;
+
+ // If the link field doesn't point to the appropriate symbol name table then
+ // try to find it by name as some compiler don't fill in the link fields.
+ if (!symtab_id)
+ symtab_id = GetSectionIndexByName(".dynsym");
+
+ // Get PLT section. We cannot use rel_hdr->sh_info, since current linkers
+ // point that to the .got.plt or .got section instead of .plt.
+ user_id_t plt_id = GetSectionIndexByName(".plt");
+
+ if (!symtab_id || !plt_id)
+ return 0;
+
+ const ELFSectionHeaderInfo *plt_hdr = GetSectionHeaderByIndex(plt_id);
+ if (!plt_hdr)
+ return 0;
+
+ const ELFSectionHeaderInfo *sym_hdr = GetSectionHeaderByIndex(symtab_id);
+ if (!sym_hdr)
+ return 0;
+
+ SectionList *section_list = m_sections_up.get();
+ if (!section_list)
+ return 0;
+
+ Section *rel_section = section_list->FindSectionByID(rel_id).get();
+ if (!rel_section)
+ return 0;
+
+ SectionSP plt_section_sp(section_list->FindSectionByID(plt_id));
+ if (!plt_section_sp)
+ return 0;
+
+ Section *symtab = section_list->FindSectionByID(symtab_id).get();
+ if (!symtab)
+ return 0;
+
+ // sh_link points to associated string table.
+ Section *strtab = section_list->FindSectionByID(sym_hdr->sh_link).get();
+ if (!strtab)
+ return 0;
+
+ DataExtractor rel_data;
+ if (!ReadSectionData(rel_section, rel_data))
+ return 0;
+
+ DataExtractor symtab_data;
+ if (!ReadSectionData(symtab, symtab_data))
+ return 0;
+
+ DataExtractor strtab_data;
+ if (!ReadSectionData(strtab, strtab_data))
+ return 0;
+
+ unsigned rel_type = PLTRelocationType();
+ if (!rel_type)
+ return 0;
+
+ return ParsePLTRelocations(symbol_table, start_id, rel_type, &m_header,
+ rel_hdr, plt_hdr, sym_hdr, plt_section_sp,
+ rel_data, symtab_data, strtab_data);
+}
+
+unsigned ObjectFileELF::ApplyRelocations(
+ Symtab *symtab, const ELFHeader *hdr, const ELFSectionHeader *rel_hdr,
+ const ELFSectionHeader *symtab_hdr, const ELFSectionHeader *debug_hdr,
+ DataExtractor &rel_data, DataExtractor &symtab_data,
+ DataExtractor &debug_data, Section *rel_section) {
+ ELFRelocation rel(rel_hdr->sh_type);
+ lldb::addr_t offset = 0;
+ const unsigned num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize;
+ typedef unsigned (*reloc_info_fn)(const ELFRelocation &rel);
+ reloc_info_fn reloc_type;
+ reloc_info_fn reloc_symbol;
+
+ if (hdr->Is32Bit()) {
+ reloc_type = ELFRelocation::RelocType32;
+ reloc_symbol = ELFRelocation::RelocSymbol32;
+ } else {
+ reloc_type = ELFRelocation::RelocType64;
+ reloc_symbol = ELFRelocation::RelocSymbol64;
+ }
+
+ for (unsigned i = 0; i < num_relocations; ++i) {
+ if (!rel.Parse(rel_data, &offset))
+ break;
+
+ Symbol *symbol = nullptr;
+
+ if (hdr->Is32Bit()) {
+ switch (reloc_type(rel)) {
+ case R_386_32:
+ case R_386_PC32:
+ default:
+ // FIXME: This asserts with this input:
+ //
+ // foo.cpp
+ // int main(int argc, char **argv) { return 0; }
+ //
+ // clang++.exe --target=i686-unknown-linux-gnu -g -c foo.cpp -o foo.o
+ //
+ // and running this on the foo.o module.
+ assert(false && "unexpected relocation type");
+ }
+ } else {
+ switch (reloc_type(rel)) {
+ case R_AARCH64_ABS64:
+ case R_X86_64_64: {
+ symbol = symtab->FindSymbolByID(reloc_symbol(rel));
+ if (symbol) {
+ addr_t value = symbol->GetAddressRef().GetFileAddress();
+ DataBufferSP &data_buffer_sp = debug_data.GetSharedDataBuffer();
+ uint64_t *dst = reinterpret_cast<uint64_t *>(
+ data_buffer_sp->GetBytes() + rel_section->GetFileOffset() +
+ ELFRelocation::RelocOffset64(rel));
+ uint64_t val_offset = value + ELFRelocation::RelocAddend64(rel);
+ memcpy(dst, &val_offset, sizeof(uint64_t));
+ }
+ break;
+ }
+ case R_X86_64_32:
+ case R_X86_64_32S:
+ case R_AARCH64_ABS32: {
+ symbol = symtab->FindSymbolByID(reloc_symbol(rel));
+ if (symbol) {
+ addr_t value = symbol->GetAddressRef().GetFileAddress();
+ value += ELFRelocation::RelocAddend32(rel);
+ if ((reloc_type(rel) == R_X86_64_32 && (value > UINT32_MAX)) ||
+ (reloc_type(rel) == R_X86_64_32S &&
+ ((int64_t)value > INT32_MAX && (int64_t)value < INT32_MIN)) ||
+ (reloc_type(rel) == R_AARCH64_ABS32 &&
+ ((int64_t)value > INT32_MAX && (int64_t)value < INT32_MIN))) {
+ Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES);
+ log->Printf("Failed to apply debug info relocations");
+ break;
+ }
+ uint32_t truncated_addr = (value & 0xFFFFFFFF);
+ DataBufferSP &data_buffer_sp = debug_data.GetSharedDataBuffer();
+ uint32_t *dst = reinterpret_cast<uint32_t *>(
+ data_buffer_sp->GetBytes() + rel_section->GetFileOffset() +
+ ELFRelocation::RelocOffset32(rel));
+ memcpy(dst, &truncated_addr, sizeof(uint32_t));
+ }
+ break;
+ }
+ case R_X86_64_PC32:
+ default:
+ assert(false && "unexpected relocation type");
+ }
+ }
+ }
+
+ return 0;
+}
+
+unsigned ObjectFileELF::RelocateDebugSections(const ELFSectionHeader *rel_hdr,
+ user_id_t rel_id,
+ lldb_private::Symtab *thetab) {
+ assert(rel_hdr->sh_type == SHT_RELA || rel_hdr->sh_type == SHT_REL);
+
+ // Parse in the section list if needed.
+ SectionList *section_list = GetSectionList();
+ if (!section_list)
+ return 0;
+
+ user_id_t symtab_id = rel_hdr->sh_link;
+ user_id_t debug_id = rel_hdr->sh_info;
+
+ const ELFSectionHeader *symtab_hdr = GetSectionHeaderByIndex(symtab_id);
+ if (!symtab_hdr)
+ return 0;
+
+ const ELFSectionHeader *debug_hdr = GetSectionHeaderByIndex(debug_id);
+ if (!debug_hdr)
+ return 0;
+
+ Section *rel = section_list->FindSectionByID(rel_id).get();
+ if (!rel)
+ return 0;
+
+ Section *symtab = section_list->FindSectionByID(symtab_id).get();
+ if (!symtab)
+ return 0;
+
+ Section *debug = section_list->FindSectionByID(debug_id).get();
+ if (!debug)
+ return 0;
+
+ DataExtractor rel_data;
+ DataExtractor symtab_data;
+ DataExtractor debug_data;
+
+ if (GetData(rel->GetFileOffset(), rel->GetFileSize(), rel_data) &&
+ GetData(symtab->GetFileOffset(), symtab->GetFileSize(), symtab_data) &&
+ GetData(debug->GetFileOffset(), debug->GetFileSize(), debug_data)) {
+ ApplyRelocations(thetab, &m_header, rel_hdr, symtab_hdr, debug_hdr,
+ rel_data, symtab_data, debug_data, debug);
+ }
+
+ return 0;
+}
+
+Symtab *ObjectFileELF::GetSymtab() {
+ ModuleSP module_sp(GetModule());
+ if (!module_sp)
+ return nullptr;
+
+ // We always want to use the main object file so we (hopefully) only have one
+ // cached copy of our symtab, dynamic sections, etc.
+ ObjectFile *module_obj_file = module_sp->GetObjectFile();
+ if (module_obj_file && module_obj_file != this)
+ return module_obj_file->GetSymtab();
+
+ if (m_symtab_up == nullptr) {
+ SectionList *section_list = module_sp->GetSectionList();
+ if (!section_list)
+ return nullptr;
+
+ uint64_t symbol_id = 0;
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+
+ // Sharable objects and dynamic executables usually have 2 distinct symbol
+ // tables, one named ".symtab", and the other ".dynsym". The dynsym is a
+ // smaller version of the symtab that only contains global symbols. The
+ // information found in the dynsym is therefore also found in the symtab,
+ // while the reverse is not necessarily true.
+ Section *symtab =
+ section_list->FindSectionByType(eSectionTypeELFSymbolTable, true).get();
+ if (!symtab) {
+ // The symtab section is non-allocable and can be stripped, so if it
+ // doesn't exist then use the dynsym section which should always be
+ // there.
+ symtab =
+ section_list->FindSectionByType(eSectionTypeELFDynamicSymbols, true)
+ .get();
+ }
+ if (symtab) {
+ m_symtab_up.reset(new Symtab(symtab->GetObjectFile()));
+ symbol_id += ParseSymbolTable(m_symtab_up.get(), symbol_id, symtab);
+ }
+
+ // DT_JMPREL
+ // If present, this entry's d_ptr member holds the address of
+ // relocation
+ // entries associated solely with the procedure linkage table.
+ // Separating
+ // these relocation entries lets the dynamic linker ignore them during
+ // process initialization, if lazy binding is enabled. If this entry is
+ // present, the related entries of types DT_PLTRELSZ and DT_PLTREL must
+ // also be present.
+ const ELFDynamic *symbol = FindDynamicSymbol(DT_JMPREL);
+ if (symbol) {
+ // Synthesize trampoline symbols to help navigate the PLT.
+ addr_t addr = symbol->d_ptr;
+ Section *reloc_section =
+ section_list->FindSectionContainingFileAddress(addr).get();
+ if (reloc_section) {
+ user_id_t reloc_id = reloc_section->GetID();
+ const ELFSectionHeaderInfo *reloc_header =
+ GetSectionHeaderByIndex(reloc_id);
+ assert(reloc_header);
+
+ if (m_symtab_up == nullptr)
+ m_symtab_up.reset(new Symtab(reloc_section->GetObjectFile()));
+
+ ParseTrampolineSymbols(m_symtab_up.get(), symbol_id, reloc_header,
+ reloc_id);
+ }
+ }
+
+ if (DWARFCallFrameInfo *eh_frame =
+ GetModule()->GetUnwindTable().GetEHFrameInfo()) {
+ if (m_symtab_up == nullptr)
+ m_symtab_up.reset(new Symtab(this));
+ ParseUnwindSymbols(m_symtab_up.get(), eh_frame);
+ }
+
+ // If we still don't have any symtab then create an empty instance to avoid
+ // do the section lookup next time.
+ if (m_symtab_up == nullptr)
+ m_symtab_up.reset(new Symtab(this));
+
+ m_symtab_up->CalculateSymbolSizes();
+ }
+
+ return m_symtab_up.get();
+}
+
+void ObjectFileELF::RelocateSection(lldb_private::Section *section)
+{
+ static const char *debug_prefix = ".debug";
+
+ // Set relocated bit so we stop getting called, regardless of whether we
+ // actually relocate.
+ section->SetIsRelocated(true);
+
+ // We only relocate in ELF relocatable files
+ if (CalculateType() != eTypeObjectFile)
+ return;
+
+ const char *section_name = section->GetName().GetCString();
+ // Can't relocate that which can't be named
+ if (section_name == nullptr)
+ return;
+
+ // We don't relocate non-debug sections at the moment
+ if (strncmp(section_name, debug_prefix, strlen(debug_prefix)))
+ return;
+
+ // Relocation section names to look for
+ std::string needle = std::string(".rel") + section_name;
+ std::string needlea = std::string(".rela") + section_name;
+
+ for (SectionHeaderCollIter I = m_section_headers.begin();
+ I != m_section_headers.end(); ++I) {
+ if (I->sh_type == SHT_RELA || I->sh_type == SHT_REL) {
+ const char *hay_name = I->section_name.GetCString();
+ if (hay_name == nullptr)
+ continue;
+ if (needle == hay_name || needlea == hay_name) {
+ const ELFSectionHeader &reloc_header = *I;
+ user_id_t reloc_id = SectionIndex(I);
+ RelocateDebugSections(&reloc_header, reloc_id, GetSymtab());
+ break;
+ }
+ }
+ }
+}
+
+void ObjectFileELF::ParseUnwindSymbols(Symtab *symbol_table,
+ DWARFCallFrameInfo *eh_frame) {
+ SectionList *section_list = GetSectionList();
+ if (!section_list)
+ return;
+
+ // First we save the new symbols into a separate list and add them to the
+ // symbol table after we colleced all symbols we want to add. This is
+ // neccessary because adding a new symbol invalidates the internal index of
+ // the symtab what causing the next lookup to be slow because it have to
+ // recalculate the index first.
+ std::vector<Symbol> new_symbols;
+
+ eh_frame->ForEachFDEEntries([this, symbol_table, section_list, &new_symbols](
+ lldb::addr_t file_addr, uint32_t size, dw_offset_t) {
+ Symbol *symbol = symbol_table->FindSymbolAtFileAddress(file_addr);
+ if (symbol) {
+ if (!symbol->GetByteSizeIsValid()) {
+ symbol->SetByteSize(size);
+ symbol->SetSizeIsSynthesized(true);
+ }
+ } else {
+ SectionSP section_sp =
+ section_list->FindSectionContainingFileAddress(file_addr);
+ if (section_sp) {
+ addr_t offset = file_addr - section_sp->GetFileAddress();
+ const char *symbol_name = GetNextSyntheticSymbolName().GetCString();
+ uint64_t symbol_id = symbol_table->GetNumSymbols();
+ Symbol eh_symbol(
+ symbol_id, // Symbol table index.
+ symbol_name, // Symbol name.
+ false, // Is the symbol name mangled?
+ eSymbolTypeCode, // Type of this symbol.
+ true, // Is this globally visible?
+ false, // Is this symbol debug info?
+ false, // Is this symbol a trampoline?
+ true, // Is this symbol artificial?
+ section_sp, // Section in which this symbol is defined or null.
+ offset, // Offset in section or symbol value.
+ 0, // Size: Don't specify the size as an FDE can
+ false, // Size is valid: cover multiple symbols.
+ false, // Contains linker annotations?
+ 0); // Symbol flags.
+ new_symbols.push_back(eh_symbol);
+ }
+ }
+ return true;
+ });
+
+ for (const Symbol &s : new_symbols)
+ symbol_table->AddSymbol(s);
+}
+
+bool ObjectFileELF::IsStripped() {
+ // TODO: determine this for ELF
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// Dump
+//
+// Dump the specifics of the runtime file container (such as any headers
+// segments, sections, etc).
+void ObjectFileELF::Dump(Stream *s) {
+ ModuleSP module_sp(GetModule());
+ if (!module_sp) {
+ return;
+ }
+
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ s->Printf("%p: ", static_cast<void *>(this));
+ s->Indent();
+ s->PutCString("ObjectFileELF");
+
+ ArchSpec header_arch = GetArchitecture();
+
+ *s << ", file = '" << m_file
+ << "', arch = " << header_arch.GetArchitectureName() << "\n";
+
+ DumpELFHeader(s, m_header);
+ s->EOL();
+ DumpELFProgramHeaders(s);
+ s->EOL();
+ DumpELFSectionHeaders(s);
+ s->EOL();
+ SectionList *section_list = GetSectionList();
+ if (section_list)
+ section_list->Dump(s, nullptr, true, UINT32_MAX);
+ Symtab *symtab = GetSymtab();
+ if (symtab)
+ symtab->Dump(s, nullptr, eSortOrderNone);
+ s->EOL();
+ DumpDependentModules(s);
+ s->EOL();
+}
+
+// DumpELFHeader
+//
+// Dump the ELF header to the specified output stream
+void ObjectFileELF::DumpELFHeader(Stream *s, const ELFHeader &header) {
+ s->PutCString("ELF Header\n");
+ s->Printf("e_ident[EI_MAG0 ] = 0x%2.2x\n", header.e_ident[EI_MAG0]);
+ s->Printf("e_ident[EI_MAG1 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG1],
+ header.e_ident[EI_MAG1]);
+ s->Printf("e_ident[EI_MAG2 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG2],
+ header.e_ident[EI_MAG2]);
+ s->Printf("e_ident[EI_MAG3 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG3],
+ header.e_ident[EI_MAG3]);
+
+ s->Printf("e_ident[EI_CLASS ] = 0x%2.2x\n", header.e_ident[EI_CLASS]);
+ s->Printf("e_ident[EI_DATA ] = 0x%2.2x ", header.e_ident[EI_DATA]);
+ DumpELFHeader_e_ident_EI_DATA(s, header.e_ident[EI_DATA]);
+ s->Printf("\ne_ident[EI_VERSION] = 0x%2.2x\n", header.e_ident[EI_VERSION]);
+ s->Printf("e_ident[EI_PAD ] = 0x%2.2x\n", header.e_ident[EI_PAD]);
+
+ s->Printf("e_type = 0x%4.4x ", header.e_type);
+ DumpELFHeader_e_type(s, header.e_type);
+ s->Printf("\ne_machine = 0x%4.4x\n", header.e_machine);
+ s->Printf("e_version = 0x%8.8x\n", header.e_version);
+ s->Printf("e_entry = 0x%8.8" PRIx64 "\n", header.e_entry);
+ s->Printf("e_phoff = 0x%8.8" PRIx64 "\n", header.e_phoff);
+ s->Printf("e_shoff = 0x%8.8" PRIx64 "\n", header.e_shoff);
+ s->Printf("e_flags = 0x%8.8x\n", header.e_flags);
+ s->Printf("e_ehsize = 0x%4.4x\n", header.e_ehsize);
+ s->Printf("e_phentsize = 0x%4.4x\n", header.e_phentsize);
+ s->Printf("e_phnum = 0x%8.8x\n", header.e_phnum);
+ s->Printf("e_shentsize = 0x%4.4x\n", header.e_shentsize);
+ s->Printf("e_shnum = 0x%8.8x\n", header.e_shnum);
+ s->Printf("e_shstrndx = 0x%8.8x\n", header.e_shstrndx);
+}
+
+// DumpELFHeader_e_type
+//
+// Dump an token value for the ELF header member e_type
+void ObjectFileELF::DumpELFHeader_e_type(Stream *s, elf_half e_type) {
+ switch (e_type) {
+ case ET_NONE:
+ *s << "ET_NONE";
+ break;
+ case ET_REL:
+ *s << "ET_REL";
+ break;
+ case ET_EXEC:
+ *s << "ET_EXEC";
+ break;
+ case ET_DYN:
+ *s << "ET_DYN";
+ break;
+ case ET_CORE:
+ *s << "ET_CORE";
+ break;
+ default:
+ break;
+ }
+}
+
+// DumpELFHeader_e_ident_EI_DATA
+//
+// Dump an token value for the ELF header member e_ident[EI_DATA]
+void ObjectFileELF::DumpELFHeader_e_ident_EI_DATA(Stream *s,
+ unsigned char ei_data) {
+ switch (ei_data) {
+ case ELFDATANONE:
+ *s << "ELFDATANONE";
+ break;
+ case ELFDATA2LSB:
+ *s << "ELFDATA2LSB - Little Endian";
+ break;
+ case ELFDATA2MSB:
+ *s << "ELFDATA2MSB - Big Endian";
+ break;
+ default:
+ break;
+ }
+}
+
+// DumpELFProgramHeader
+//
+// Dump a single ELF program header to the specified output stream
+void ObjectFileELF::DumpELFProgramHeader(Stream *s,
+ const ELFProgramHeader &ph) {
+ DumpELFProgramHeader_p_type(s, ph.p_type);
+ s->Printf(" %8.8" PRIx64 " %8.8" PRIx64 " %8.8" PRIx64, ph.p_offset,
+ ph.p_vaddr, ph.p_paddr);
+ s->Printf(" %8.8" PRIx64 " %8.8" PRIx64 " %8.8x (", ph.p_filesz, ph.p_memsz,
+ ph.p_flags);
+
+ DumpELFProgramHeader_p_flags(s, ph.p_flags);
+ s->Printf(") %8.8" PRIx64, ph.p_align);
+}
+
+// DumpELFProgramHeader_p_type
+//
+// Dump an token value for the ELF program header member p_type which describes
+// the type of the program header
+void ObjectFileELF::DumpELFProgramHeader_p_type(Stream *s, elf_word p_type) {
+ const int kStrWidth = 15;
+ switch (p_type) {
+ CASE_AND_STREAM(s, PT_NULL, kStrWidth);
+ CASE_AND_STREAM(s, PT_LOAD, kStrWidth);
+ CASE_AND_STREAM(s, PT_DYNAMIC, kStrWidth);
+ CASE_AND_STREAM(s, PT_INTERP, kStrWidth);
+ CASE_AND_STREAM(s, PT_NOTE, kStrWidth);
+ CASE_AND_STREAM(s, PT_SHLIB, kStrWidth);
+ CASE_AND_STREAM(s, PT_PHDR, kStrWidth);
+ CASE_AND_STREAM(s, PT_TLS, kStrWidth);
+ CASE_AND_STREAM(s, PT_GNU_EH_FRAME, kStrWidth);
+ default:
+ s->Printf("0x%8.8x%*s", p_type, kStrWidth - 10, "");
+ break;
+ }
+}
+
+// DumpELFProgramHeader_p_flags
+//
+// Dump an token value for the ELF program header member p_flags
+void ObjectFileELF::DumpELFProgramHeader_p_flags(Stream *s, elf_word p_flags) {
+ *s << ((p_flags & PF_X) ? "PF_X" : " ")
+ << (((p_flags & PF_X) && (p_flags & PF_W)) ? '+' : ' ')
+ << ((p_flags & PF_W) ? "PF_W" : " ")
+ << (((p_flags & PF_W) && (p_flags & PF_R)) ? '+' : ' ')
+ << ((p_flags & PF_R) ? "PF_R" : " ");
+}
+
+// DumpELFProgramHeaders
+//
+// Dump all of the ELF program header to the specified output stream
+void ObjectFileELF::DumpELFProgramHeaders(Stream *s) {
+ if (!ParseProgramHeaders())
+ return;
+
+ s->PutCString("Program Headers\n");
+ s->PutCString("IDX p_type p_offset p_vaddr p_paddr "
+ "p_filesz p_memsz p_flags p_align\n");
+ s->PutCString("==== --------------- -------- -------- -------- "
+ "-------- -------- ------------------------- --------\n");
+
+ for (const auto &H : llvm::enumerate(m_program_headers)) {
+ s->Format("[{0,2}] ", H.index());
+ ObjectFileELF::DumpELFProgramHeader(s, H.value());
+ s->EOL();
+ }
+}
+
+// DumpELFSectionHeader
+//
+// Dump a single ELF section header to the specified output stream
+void ObjectFileELF::DumpELFSectionHeader(Stream *s,
+ const ELFSectionHeaderInfo &sh) {
+ s->Printf("%8.8x ", sh.sh_name);
+ DumpELFSectionHeader_sh_type(s, sh.sh_type);
+ s->Printf(" %8.8" PRIx64 " (", sh.sh_flags);
+ DumpELFSectionHeader_sh_flags(s, sh.sh_flags);
+ s->Printf(") %8.8" PRIx64 " %8.8" PRIx64 " %8.8" PRIx64, sh.sh_addr,
+ sh.sh_offset, sh.sh_size);
+ s->Printf(" %8.8x %8.8x", sh.sh_link, sh.sh_info);
+ s->Printf(" %8.8" PRIx64 " %8.8" PRIx64, sh.sh_addralign, sh.sh_entsize);
+}
+
+// DumpELFSectionHeader_sh_type
+//
+// Dump an token value for the ELF section header member sh_type which
+// describes the type of the section
+void ObjectFileELF::DumpELFSectionHeader_sh_type(Stream *s, elf_word sh_type) {
+ const int kStrWidth = 12;
+ switch (sh_type) {
+ CASE_AND_STREAM(s, SHT_NULL, kStrWidth);
+ CASE_AND_STREAM(s, SHT_PROGBITS, kStrWidth);
+ CASE_AND_STREAM(s, SHT_SYMTAB, kStrWidth);
+ CASE_AND_STREAM(s, SHT_STRTAB, kStrWidth);
+ CASE_AND_STREAM(s, SHT_RELA, kStrWidth);
+ CASE_AND_STREAM(s, SHT_HASH, kStrWidth);
+ CASE_AND_STREAM(s, SHT_DYNAMIC, kStrWidth);
+ CASE_AND_STREAM(s, SHT_NOTE, kStrWidth);
+ CASE_AND_STREAM(s, SHT_NOBITS, kStrWidth);
+ CASE_AND_STREAM(s, SHT_REL, kStrWidth);
+ CASE_AND_STREAM(s, SHT_SHLIB, kStrWidth);
+ CASE_AND_STREAM(s, SHT_DYNSYM, kStrWidth);
+ CASE_AND_STREAM(s, SHT_LOPROC, kStrWidth);
+ CASE_AND_STREAM(s, SHT_HIPROC, kStrWidth);
+ CASE_AND_STREAM(s, SHT_LOUSER, kStrWidth);
+ CASE_AND_STREAM(s, SHT_HIUSER, kStrWidth);
+ default:
+ s->Printf("0x%8.8x%*s", sh_type, kStrWidth - 10, "");
+ break;
+ }
+}
+
+// DumpELFSectionHeader_sh_flags
+//
+// Dump an token value for the ELF section header member sh_flags
+void ObjectFileELF::DumpELFSectionHeader_sh_flags(Stream *s,
+ elf_xword sh_flags) {
+ *s << ((sh_flags & SHF_WRITE) ? "WRITE" : " ")
+ << (((sh_flags & SHF_WRITE) && (sh_flags & SHF_ALLOC)) ? '+' : ' ')
+ << ((sh_flags & SHF_ALLOC) ? "ALLOC" : " ")
+ << (((sh_flags & SHF_ALLOC) && (sh_flags & SHF_EXECINSTR)) ? '+' : ' ')
+ << ((sh_flags & SHF_EXECINSTR) ? "EXECINSTR" : " ");
+}
+
+// DumpELFSectionHeaders
+//
+// Dump all of the ELF section header to the specified output stream
+void ObjectFileELF::DumpELFSectionHeaders(Stream *s) {
+ if (!ParseSectionHeaders())
+ return;
+
+ s->PutCString("Section Headers\n");
+ s->PutCString("IDX name type flags "
+ "addr offset size link info addralgn "
+ "entsize Name\n");
+ s->PutCString("==== -------- ------------ -------------------------------- "
+ "-------- -------- -------- -------- -------- -------- "
+ "-------- ====================\n");
+
+ uint32_t idx = 0;
+ for (SectionHeaderCollConstIter I = m_section_headers.begin();
+ I != m_section_headers.end(); ++I, ++idx) {
+ s->Printf("[%2u] ", idx);
+ ObjectFileELF::DumpELFSectionHeader(s, *I);
+ const char *section_name = I->section_name.AsCString("");
+ if (section_name)
+ *s << ' ' << section_name << "\n";
+ }
+}
+
+void ObjectFileELF::DumpDependentModules(lldb_private::Stream *s) {
+ size_t num_modules = ParseDependentModules();
+
+ if (num_modules > 0) {
+ s->PutCString("Dependent Modules:\n");
+ for (unsigned i = 0; i < num_modules; ++i) {
+ const FileSpec &spec = m_filespec_up->GetFileSpecAtIndex(i);
+ s->Printf(" %s\n", spec.GetFilename().GetCString());
+ }
+ }
+}
+
+ArchSpec ObjectFileELF::GetArchitecture() {
+ if (!ParseHeader())
+ return ArchSpec();
+
+ if (m_section_headers.empty()) {
+ // Allow elf notes to be parsed which may affect the detected architecture.
+ ParseSectionHeaders();
+ }
+
+ if (CalculateType() == eTypeCoreFile &&
+ !m_arch_spec.TripleOSWasSpecified()) {
+ // Core files don't have section headers yet they have PT_NOTE program
+ // headers that might shed more light on the architecture
+ for (const elf::ELFProgramHeader &H : ProgramHeaders()) {
+ if (H.p_type != PT_NOTE || H.p_offset == 0 || H.p_filesz == 0)
+ continue;
+ DataExtractor data;
+ if (data.SetData(m_data, H.p_offset, H.p_filesz) == H.p_filesz) {
+ UUID uuid;
+ RefineModuleDetailsFromNote(data, m_arch_spec, uuid);
+ }
+ }
+ }
+ return m_arch_spec;
+}
+
+ObjectFile::Type ObjectFileELF::CalculateType() {
+ switch (m_header.e_type) {
+ case llvm::ELF::ET_NONE:
+ // 0 - No file type
+ return eTypeUnknown;
+
+ case llvm::ELF::ET_REL:
+ // 1 - Relocatable file
+ return eTypeObjectFile;
+
+ case llvm::ELF::ET_EXEC:
+ // 2 - Executable file
+ return eTypeExecutable;
+
+ case llvm::ELF::ET_DYN:
+ // 3 - Shared object file
+ return eTypeSharedLibrary;
+
+ case ET_CORE:
+ // 4 - Core file
+ return eTypeCoreFile;
+
+ default:
+ break;
+ }
+ return eTypeUnknown;
+}
+
+ObjectFile::Strata ObjectFileELF::CalculateStrata() {
+ switch (m_header.e_type) {
+ case llvm::ELF::ET_NONE:
+ // 0 - No file type
+ return eStrataUnknown;
+
+ case llvm::ELF::ET_REL:
+ // 1 - Relocatable file
+ return eStrataUnknown;
+
+ case llvm::ELF::ET_EXEC:
+ // 2 - Executable file
+ // TODO: is there any way to detect that an executable is a kernel
+ // related executable by inspecting the program headers, section headers,
+ // symbols, or any other flag bits???
+ return eStrataUser;
+
+ case llvm::ELF::ET_DYN:
+ // 3 - Shared object file
+ // TODO: is there any way to detect that an shared library is a kernel
+ // related executable by inspecting the program headers, section headers,
+ // symbols, or any other flag bits???
+ return eStrataUnknown;
+
+ case ET_CORE:
+ // 4 - Core file
+ // TODO: is there any way to detect that an core file is a kernel
+ // related executable by inspecting the program headers, section headers,
+ // symbols, or any other flag bits???
+ return eStrataUnknown;
+
+ default:
+ break;
+ }
+ return eStrataUnknown;
+}
+
+size_t ObjectFileELF::ReadSectionData(Section *section,
+ lldb::offset_t section_offset, void *dst,
+ size_t dst_len) {
+ // If some other objectfile owns this data, pass this to them.
+ if (section->GetObjectFile() != this)
+ return section->GetObjectFile()->ReadSectionData(section, section_offset,
+ dst, dst_len);
+
+ if (!section->Test(SHF_COMPRESSED))
+ return ObjectFile::ReadSectionData(section, section_offset, dst, dst_len);
+
+ // For compressed sections we need to read to full data to be able to
+ // decompress.
+ DataExtractor data;
+ ReadSectionData(section, data);
+ return data.CopyData(section_offset, dst_len, dst);
+}
+
+size_t ObjectFileELF::ReadSectionData(Section *section,
+ DataExtractor &section_data) {
+ // If some other objectfile owns this data, pass this to them.
+ if (section->GetObjectFile() != this)
+ return section->GetObjectFile()->ReadSectionData(section, section_data);
+
+ size_t result = ObjectFile::ReadSectionData(section, section_data);
+ if (result == 0 || !llvm::object::Decompressor::isCompressedELFSection(
+ section->Get(), section->GetName().GetStringRef()))
+ return result;
+
+ auto Decompressor = llvm::object::Decompressor::create(
+ section->GetName().GetStringRef(),
+ {reinterpret_cast<const char *>(section_data.GetDataStart()),
+ size_t(section_data.GetByteSize())},
+ GetByteOrder() == eByteOrderLittle, GetAddressByteSize() == 8);
+ if (!Decompressor) {
+ GetModule()->ReportWarning(
+ "Unable to initialize decompressor for section '%s': %s",
+ section->GetName().GetCString(),
+ llvm::toString(Decompressor.takeError()).c_str());
+ section_data.Clear();
+ return 0;
+ }
+
+ auto buffer_sp =
+ std::make_shared<DataBufferHeap>(Decompressor->getDecompressedSize(), 0);
+ if (auto error = Decompressor->decompress(
+ {reinterpret_cast<char *>(buffer_sp->GetBytes()),
+ size_t(buffer_sp->GetByteSize())})) {
+ GetModule()->ReportWarning(
+ "Decompression of section '%s' failed: %s",
+ section->GetName().GetCString(),
+ llvm::toString(std::move(error)).c_str());
+ section_data.Clear();
+ return 0;
+ }
+
+ section_data.SetData(buffer_sp);
+ return buffer_sp->GetByteSize();
+}
+
+llvm::ArrayRef<ELFProgramHeader> ObjectFileELF::ProgramHeaders() {
+ ParseProgramHeaders();
+ return m_program_headers;
+}
+
+DataExtractor ObjectFileELF::GetSegmentData(const ELFProgramHeader &H) {
+ return DataExtractor(m_data, H.p_offset, H.p_filesz);
+}
+
+bool ObjectFileELF::AnySegmentHasPhysicalAddress() {
+ for (const ELFProgramHeader &H : ProgramHeaders()) {
+ if (H.p_paddr != 0)
+ return true;
+ }
+ return false;
+}
+
+std::vector<ObjectFile::LoadableData>
+ObjectFileELF::GetLoadableData(Target &target) {
+ // Create a list of loadable data from loadable segments, using physical
+ // addresses if they aren't all null
+ std::vector<LoadableData> loadables;
+ bool should_use_paddr = AnySegmentHasPhysicalAddress();
+ for (const ELFProgramHeader &H : ProgramHeaders()) {
+ LoadableData loadable;
+ if (H.p_type != llvm::ELF::PT_LOAD)
+ continue;
+ loadable.Dest = should_use_paddr ? H.p_paddr : H.p_vaddr;
+ if (loadable.Dest == LLDB_INVALID_ADDRESS)
+ continue;
+ if (H.p_filesz == 0)
+ continue;
+ auto segment_data = GetSegmentData(H);
+ loadable.Contents = llvm::ArrayRef<uint8_t>(segment_data.GetDataStart(),
+ segment_data.GetByteSize());
+ loadables.push_back(loadable);
+ }
+ return loadables;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
new file mode 100644
index 000000000000..b63a5d14d4f5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
@@ -0,0 +1,381 @@
+//===-- ObjectFileELF.h --------------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ObjectFileELF_h_
+#define liblldb_ObjectFileELF_h_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/UUID.h"
+#include "lldb/lldb-private.h"
+
+#include "ELFHeader.h"
+
+struct ELFNote {
+ elf::elf_word n_namesz;
+ elf::elf_word n_descsz;
+ elf::elf_word n_type;
+
+ std::string n_name;
+
+ ELFNote() : n_namesz(0), n_descsz(0), n_type(0) {}
+
+ /// Parse an ELFNote entry from the given DataExtractor starting at position
+ /// \p offset.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFRel entry was successfully read and false otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ size_t GetByteSize() const {
+ return 12 + llvm::alignTo(n_namesz, 4) + llvm::alignTo(n_descsz, 4);
+ }
+};
+
+/// \class ObjectFileELF
+/// Generic ELF object file reader.
+///
+/// This class provides a generic ELF (32/64 bit) reader plugin implementing
+/// the ObjectFile protocol.
+class ObjectFileELF : public lldb_private::ObjectFile {
+public:
+ ~ObjectFileELF() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::ObjectFile *
+ CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const lldb_private::FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t length);
+
+ static lldb_private::ObjectFile *CreateMemoryInstance(
+ const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ const lldb::ProcessSP &process_sp, lldb::addr_t header_addr);
+
+ static size_t GetModuleSpecifications(const lldb_private::FileSpec &file,
+ lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ lldb::offset_t file_offset,
+ lldb::offset_t length,
+ lldb_private::ModuleSpecList &specs);
+
+ static bool MagicBytesMatch(lldb::DataBufferSP &data_sp, lldb::addr_t offset,
+ lldb::addr_t length);
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // ObjectFile Protocol.
+ bool ParseHeader() override;
+
+ bool SetLoadAddress(lldb_private::Target &target, lldb::addr_t value,
+ bool value_is_offset) override;
+
+ lldb::ByteOrder GetByteOrder() const override;
+
+ bool IsExecutable() const override;
+
+ uint32_t GetAddressByteSize() const override;
+
+ lldb_private::AddressClass GetAddressClass(lldb::addr_t file_addr) override;
+
+ lldb_private::Symtab *GetSymtab() override;
+
+ bool IsStripped() override;
+
+ void CreateSections(lldb_private::SectionList &unified_section_list) override;
+
+ void Dump(lldb_private::Stream *s) override;
+
+ lldb_private::ArchSpec GetArchitecture() override;
+
+ lldb_private::UUID GetUUID() override;
+
+ lldb_private::FileSpecList GetDebugSymbolFilePaths() override;
+
+ uint32_t GetDependentModules(lldb_private::FileSpecList &files) override;
+
+ lldb_private::Address
+ GetImageInfoAddress(lldb_private::Target *target) override;
+
+ lldb_private::Address GetEntryPointAddress() override;
+
+ lldb_private::Address GetBaseAddress() override;
+
+ ObjectFile::Type CalculateType() override;
+
+ ObjectFile::Strata CalculateStrata() override;
+
+ size_t ReadSectionData(lldb_private::Section *section,
+ lldb::offset_t section_offset, void *dst,
+ size_t dst_len) override;
+
+ size_t ReadSectionData(lldb_private::Section *section,
+ lldb_private::DataExtractor &section_data) override;
+
+ llvm::ArrayRef<elf::ELFProgramHeader> ProgramHeaders();
+ lldb_private::DataExtractor GetSegmentData(const elf::ELFProgramHeader &H);
+
+ llvm::StringRef
+ StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const override;
+
+ void RelocateSection(lldb_private::Section *section) override;
+
+protected:
+
+ std::vector<LoadableData>
+ GetLoadableData(lldb_private::Target &target) override;
+
+private:
+ ObjectFileELF(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const lldb_private::FileSpec *file,
+ lldb::offset_t offset, lldb::offset_t length);
+
+ ObjectFileELF(const lldb::ModuleSP &module_sp,
+ lldb::DataBufferSP &header_data_sp,
+ const lldb::ProcessSP &process_sp, lldb::addr_t header_addr);
+
+ typedef std::vector<elf::ELFProgramHeader> ProgramHeaderColl;
+
+ struct ELFSectionHeaderInfo : public elf::ELFSectionHeader {
+ lldb_private::ConstString section_name;
+ };
+
+ typedef std::vector<ELFSectionHeaderInfo> SectionHeaderColl;
+ typedef SectionHeaderColl::iterator SectionHeaderCollIter;
+ typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter;
+
+ typedef std::vector<elf::ELFDynamic> DynamicSymbolColl;
+ typedef DynamicSymbolColl::iterator DynamicSymbolCollIter;
+ typedef DynamicSymbolColl::const_iterator DynamicSymbolCollConstIter;
+
+ typedef std::map<lldb::addr_t, lldb_private::AddressClass>
+ FileAddressToAddressClassMap;
+
+ /// Version of this reader common to all plugins based on this class.
+ static const uint32_t m_plugin_version = 1;
+ static const uint32_t g_core_uuid_magic;
+
+ /// ELF file header.
+ elf::ELFHeader m_header;
+
+ /// ELF build ID.
+ lldb_private::UUID m_uuid;
+
+ /// ELF .gnu_debuglink file and crc data if available.
+ std::string m_gnu_debuglink_file;
+ uint32_t m_gnu_debuglink_crc;
+
+ /// Collection of program headers.
+ ProgramHeaderColl m_program_headers;
+
+ /// Collection of section headers.
+ SectionHeaderColl m_section_headers;
+
+ /// Collection of symbols from the dynamic table.
+ DynamicSymbolColl m_dynamic_symbols;
+
+ /// List of file specifications corresponding to the modules (shared
+ /// libraries) on which this object file depends.
+ mutable std::unique_ptr<lldb_private::FileSpecList> m_filespec_up;
+
+ /// Cached value of the entry point for this module.
+ lldb_private::Address m_entry_point_address;
+
+ /// The architecture detected from parsing elf file contents.
+ lldb_private::ArchSpec m_arch_spec;
+
+ /// The address class for each symbol in the elf file
+ FileAddressToAddressClassMap m_address_class_map;
+
+ /// Returns the index of the given section header.
+ size_t SectionIndex(const SectionHeaderCollIter &I);
+
+ /// Returns the index of the given section header.
+ size_t SectionIndex(const SectionHeaderCollConstIter &I) const;
+
+ // Parses the ELF program headers.
+ static size_t GetProgramHeaderInfo(ProgramHeaderColl &program_headers,
+ lldb_private::DataExtractor &object_data,
+ const elf::ELFHeader &header);
+
+ // Finds PT_NOTE segments and calculates their crc sum.
+ static uint32_t
+ CalculateELFNotesSegmentsCRC32(const ProgramHeaderColl &program_headers,
+ lldb_private::DataExtractor &data);
+
+ /// Parses all section headers present in this object file and populates
+ /// m_program_headers. This method will compute the header list only once.
+ /// Returns true iff the headers have been successfully parsed.
+ bool ParseProgramHeaders();
+
+ /// Parses all section headers present in this object file and populates
+ /// m_section_headers. This method will compute the header list only once.
+ /// Returns the number of headers parsed.
+ size_t ParseSectionHeaders();
+
+ lldb::SectionType GetSectionType(const ELFSectionHeaderInfo &H) const;
+
+ static void ParseARMAttributes(lldb_private::DataExtractor &data,
+ uint64_t length,
+ lldb_private::ArchSpec &arch_spec);
+
+ /// Parses the elf section headers and returns the uuid, debug link name,
+ /// crc, archspec.
+ static size_t GetSectionHeaderInfo(SectionHeaderColl &section_headers,
+ lldb_private::DataExtractor &object_data,
+ const elf::ELFHeader &header,
+ lldb_private::UUID &uuid,
+ std::string &gnu_debuglink_file,
+ uint32_t &gnu_debuglink_crc,
+ lldb_private::ArchSpec &arch_spec);
+
+ /// Scans the dynamic section and locates all dependent modules (shared
+ /// libraries) populating m_filespec_up. This method will compute the
+ /// dependent module list only once. Returns the number of dependent
+ /// modules parsed.
+ size_t ParseDependentModules();
+
+ /// Parses the dynamic symbol table and populates m_dynamic_symbols. The
+ /// vector retains the order as found in the object file. Returns the
+ /// number of dynamic symbols parsed.
+ size_t ParseDynamicSymbols();
+
+ /// Populates m_symtab_up will all non-dynamic linker symbols. This method
+ /// will parse the symbols only once. Returns the number of symbols parsed.
+ unsigned ParseSymbolTable(lldb_private::Symtab *symbol_table,
+ lldb::user_id_t start_id,
+ lldb_private::Section *symtab);
+
+ /// Helper routine for ParseSymbolTable().
+ unsigned ParseSymbols(lldb_private::Symtab *symbol_table,
+ lldb::user_id_t start_id,
+ lldb_private::SectionList *section_list,
+ const size_t num_symbols,
+ const lldb_private::DataExtractor &symtab_data,
+ const lldb_private::DataExtractor &strtab_data);
+
+ /// Scans the relocation entries and adds a set of artificial symbols to the
+ /// given symbol table for each PLT slot. Returns the number of symbols
+ /// added.
+ unsigned ParseTrampolineSymbols(lldb_private::Symtab *symbol_table,
+ lldb::user_id_t start_id,
+ const ELFSectionHeaderInfo *rela_hdr,
+ lldb::user_id_t section_id);
+
+ void ParseUnwindSymbols(lldb_private::Symtab *symbol_table,
+ lldb_private::DWARFCallFrameInfo *eh_frame);
+
+ /// Relocates debug sections
+ unsigned RelocateDebugSections(const elf::ELFSectionHeader *rel_hdr,
+ lldb::user_id_t rel_id,
+ lldb_private::Symtab *thetab);
+
+ unsigned ApplyRelocations(lldb_private::Symtab *symtab,
+ const elf::ELFHeader *hdr,
+ const elf::ELFSectionHeader *rel_hdr,
+ const elf::ELFSectionHeader *symtab_hdr,
+ const elf::ELFSectionHeader *debug_hdr,
+ lldb_private::DataExtractor &rel_data,
+ lldb_private::DataExtractor &symtab_data,
+ lldb_private::DataExtractor &debug_data,
+ lldb_private::Section *rel_section);
+
+ /// Loads the section name string table into m_shstr_data. Returns the
+ /// number of bytes constituting the table.
+ size_t GetSectionHeaderStringTable();
+
+ /// Utility method for looking up a section given its name. Returns the
+ /// index of the corresponding section or zero if no section with the given
+ /// name can be found (note that section indices are always 1 based, and so
+ /// section index 0 is never valid).
+ lldb::user_id_t GetSectionIndexByName(const char *name);
+
+ // Returns the ID of the first section that has the given type.
+ lldb::user_id_t GetSectionIndexByType(unsigned type);
+
+ /// Returns the section header with the given id or NULL.
+ const ELFSectionHeaderInfo *GetSectionHeaderByIndex(lldb::user_id_t id);
+
+ /// \name ELF header dump routines
+ //@{
+ static void DumpELFHeader(lldb_private::Stream *s,
+ const elf::ELFHeader &header);
+
+ static void DumpELFHeader_e_ident_EI_DATA(lldb_private::Stream *s,
+ unsigned char ei_data);
+
+ static void DumpELFHeader_e_type(lldb_private::Stream *s,
+ elf::elf_half e_type);
+ //@}
+
+ /// \name ELF program header dump routines
+ //@{
+ void DumpELFProgramHeaders(lldb_private::Stream *s);
+
+ static void DumpELFProgramHeader(lldb_private::Stream *s,
+ const elf::ELFProgramHeader &ph);
+
+ static void DumpELFProgramHeader_p_type(lldb_private::Stream *s,
+ elf::elf_word p_type);
+
+ static void DumpELFProgramHeader_p_flags(lldb_private::Stream *s,
+ elf::elf_word p_flags);
+ //@}
+
+ /// \name ELF section header dump routines
+ //@{
+ void DumpELFSectionHeaders(lldb_private::Stream *s);
+
+ static void DumpELFSectionHeader(lldb_private::Stream *s,
+ const ELFSectionHeaderInfo &sh);
+
+ static void DumpELFSectionHeader_sh_type(lldb_private::Stream *s,
+ elf::elf_word sh_type);
+
+ static void DumpELFSectionHeader_sh_flags(lldb_private::Stream *s,
+ elf::elf_xword sh_flags);
+ //@}
+
+ /// ELF dependent module dump routine.
+ void DumpDependentModules(lldb_private::Stream *s);
+
+ const elf::ELFDynamic *FindDynamicSymbol(unsigned tag);
+
+ unsigned PLTRelocationType();
+
+ static lldb_private::Status
+ RefineModuleDetailsFromNote(lldb_private::DataExtractor &data,
+ lldb_private::ArchSpec &arch_spec,
+ lldb_private::UUID &uuid);
+
+ bool AnySegmentHasPhysicalAddress();
+};
+
+#endif // liblldb_ObjectFileELF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp
new file mode 100644
index 000000000000..eaf973da3835
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp
@@ -0,0 +1,258 @@
+//===-- ObjectFileJIT.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+
+#include "ObjectFileJIT.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+#include "lldb/Utility/UUID.h"
+
+#ifndef __APPLE__
+#include "Utility/UuidCompatibility.h"
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+void ObjectFileJIT::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ CreateMemoryInstance, GetModuleSpecifications);
+}
+
+void ObjectFileJIT::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ObjectFileJIT::GetPluginNameStatic() {
+ static ConstString g_name("jit");
+ return g_name;
+}
+
+const char *ObjectFileJIT::GetPluginDescriptionStatic() {
+ return "JIT code object file";
+}
+
+ObjectFile *ObjectFileJIT::CreateInstance(const lldb::ModuleSP &module_sp,
+ DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ const FileSpec *file,
+ lldb::offset_t file_offset,
+ lldb::offset_t length) {
+ // JIT'ed object file is backed by the ObjectFileJITDelegate, never read from
+ // a file
+ return nullptr;
+}
+
+ObjectFile *ObjectFileJIT::CreateMemoryInstance(const lldb::ModuleSP &module_sp,
+ DataBufferSP &data_sp,
+ const ProcessSP &process_sp,
+ lldb::addr_t header_addr) {
+ // JIT'ed object file is backed by the ObjectFileJITDelegate, never read from
+ // memory
+ return nullptr;
+}
+
+size_t ObjectFileJIT::GetModuleSpecifications(
+ const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, lldb::offset_t file_offset,
+ lldb::offset_t length, lldb_private::ModuleSpecList &specs) {
+ // JIT'ed object file can't be read from a file on disk
+ return 0;
+}
+
+ObjectFileJIT::ObjectFileJIT(const lldb::ModuleSP &module_sp,
+ const ObjectFileJITDelegateSP &delegate_sp)
+ : ObjectFile(module_sp, nullptr, 0, 0, DataBufferSP(), 0), m_delegate_wp() {
+ if (delegate_sp) {
+ m_delegate_wp = delegate_sp;
+ m_data.SetByteOrder(delegate_sp->GetByteOrder());
+ m_data.SetAddressByteSize(delegate_sp->GetAddressByteSize());
+ }
+}
+
+ObjectFileJIT::~ObjectFileJIT() {}
+
+bool ObjectFileJIT::ParseHeader() {
+ // JIT code is never in a file, nor is it required to have any header
+ return false;
+}
+
+ByteOrder ObjectFileJIT::GetByteOrder() const { return m_data.GetByteOrder(); }
+
+bool ObjectFileJIT::IsExecutable() const { return false; }
+
+uint32_t ObjectFileJIT::GetAddressByteSize() const {
+ return m_data.GetAddressByteSize();
+}
+
+Symtab *ObjectFileJIT::GetSymtab() {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_symtab_up == nullptr) {
+ m_symtab_up.reset(new Symtab(this));
+ std::lock_guard<std::recursive_mutex> symtab_guard(
+ m_symtab_up->GetMutex());
+ ObjectFileJITDelegateSP delegate_sp(m_delegate_wp.lock());
+ if (delegate_sp)
+ delegate_sp->PopulateSymtab(this, *m_symtab_up);
+ // TODO: get symbols from delegate
+ m_symtab_up->Finalize();
+ }
+ }
+ return m_symtab_up.get();
+}
+
+bool ObjectFileJIT::IsStripped() {
+ return false; // JIT code that is in a module is never stripped
+}
+
+void ObjectFileJIT::CreateSections(SectionList &unified_section_list) {
+ if (!m_sections_up) {
+ m_sections_up.reset(new SectionList());
+ ObjectFileJITDelegateSP delegate_sp(m_delegate_wp.lock());
+ if (delegate_sp) {
+ delegate_sp->PopulateSectionList(this, *m_sections_up);
+ unified_section_list = *m_sections_up;
+ }
+ }
+}
+
+void ObjectFileJIT::Dump(Stream *s) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ s->Printf("%p: ", static_cast<void *>(this));
+ s->Indent();
+ s->PutCString("ObjectFileJIT");
+
+ if (ArchSpec arch = GetArchitecture())
+ *s << ", arch = " << arch.GetArchitectureName();
+
+ s->EOL();
+
+ SectionList *sections = GetSectionList();
+ if (sections)
+ sections->Dump(s, nullptr, true, UINT32_MAX);
+
+ if (m_symtab_up)
+ m_symtab_up->Dump(s, nullptr, eSortOrderNone);
+ }
+}
+
+UUID ObjectFileJIT::GetUUID() {
+ // TODO: maybe get from delegate, not needed for first pass
+ return UUID();
+}
+
+uint32_t ObjectFileJIT::GetDependentModules(FileSpecList &files) {
+ // JIT modules don't have dependencies, but they could
+ // if external functions are called and we know where they are
+ files.Clear();
+ return 0;
+}
+
+lldb_private::Address ObjectFileJIT::GetEntryPointAddress() {
+ return Address();
+}
+
+lldb_private::Address ObjectFileJIT::GetBaseAddress() { return Address(); }
+
+ObjectFile::Type ObjectFileJIT::CalculateType() { return eTypeJIT; }
+
+ObjectFile::Strata ObjectFileJIT::CalculateStrata() { return eStrataJIT; }
+
+ArchSpec ObjectFileJIT::GetArchitecture() {
+ if (ObjectFileJITDelegateSP delegate_sp = m_delegate_wp.lock())
+ return delegate_sp->GetArchitecture();
+ return ArchSpec();
+}
+
+// PluginInterface protocol
+lldb_private::ConstString ObjectFileJIT::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ObjectFileJIT::GetPluginVersion() { return 1; }
+
+bool ObjectFileJIT::SetLoadAddress(Target &target, lldb::addr_t value,
+ bool value_is_offset) {
+ size_t num_loaded_sections = 0;
+ SectionList *section_list = GetSectionList();
+ if (section_list) {
+ const size_t num_sections = section_list->GetSize();
+ // "value" is an offset to apply to each top level segment
+ for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
+ // Iterate through the object file sections to find all of the sections
+ // that size on disk (to avoid __PAGEZERO) and load them
+ SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
+ if (section_sp && section_sp->GetFileSize() > 0 &&
+ !section_sp->IsThreadSpecific()) {
+ if (target.GetSectionLoadList().SetSectionLoadAddress(
+ section_sp, section_sp->GetFileAddress() + value))
+ ++num_loaded_sections;
+ }
+ }
+ }
+ return num_loaded_sections > 0;
+}
+
+size_t ObjectFileJIT::ReadSectionData(lldb_private::Section *section,
+ lldb::offset_t section_offset, void *dst,
+ size_t dst_len) {
+ lldb::offset_t file_size = section->GetFileSize();
+ if (section_offset < file_size) {
+ size_t src_len = file_size - section_offset;
+ if (src_len > dst_len)
+ src_len = dst_len;
+ const uint8_t *src =
+ ((uint8_t *)(uintptr_t)section->GetFileOffset()) + section_offset;
+
+ memcpy(dst, src, src_len);
+ return src_len;
+ }
+ return 0;
+}
+
+size_t ObjectFileJIT::ReadSectionData(
+ lldb_private::Section *section,
+ lldb_private::DataExtractor &section_data) {
+ if (section->GetFileSize()) {
+ const void *src = (void *)(uintptr_t)section->GetFileOffset();
+
+ DataBufferSP data_sp =
+ std::make_shared<DataBufferHeap>(src, section->GetFileSize());
+ section_data.SetData(data_sp, 0, data_sp->GetByteSize());
+ section_data.SetByteOrder(GetByteOrder());
+ section_data.SetAddressByteSize(GetAddressByteSize());
+ return section_data.GetByteSize();
+ }
+ section_data.Clear();
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h
new file mode 100644
index 000000000000..99241126cd1a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h
@@ -0,0 +1,100 @@
+//===-- ObjectFileJIT.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ObjectFileJIT_h_
+#define liblldb_ObjectFileJIT_h_
+
+#include "lldb/Core/Address.h"
+#include "lldb/Symbol/ObjectFile.h"
+
+// This class needs to be hidden as eventually belongs in a plugin that
+// will export the ObjectFile protocol
+class ObjectFileJIT : public lldb_private::ObjectFile {
+public:
+ ObjectFileJIT(const lldb::ModuleSP &module_sp,
+ const lldb::ObjectFileJITDelegateSP &delegate_sp);
+
+ ~ObjectFileJIT() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::ObjectFile *
+ CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const lldb_private::FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t length);
+
+ static lldb_private::ObjectFile *CreateMemoryInstance(
+ const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ const lldb::ProcessSP &process_sp, lldb::addr_t header_addr);
+
+ static size_t GetModuleSpecifications(const lldb_private::FileSpec &file,
+ lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ lldb::offset_t file_offset,
+ lldb::offset_t length,
+ lldb_private::ModuleSpecList &specs);
+
+ // Member Functions
+ bool ParseHeader() override;
+
+ bool SetLoadAddress(lldb_private::Target &target, lldb::addr_t value,
+ bool value_is_offset) override;
+
+ lldb::ByteOrder GetByteOrder() const override;
+
+ bool IsExecutable() const override;
+
+ uint32_t GetAddressByteSize() const override;
+
+ lldb_private::Symtab *GetSymtab() override;
+
+ bool IsStripped() override;
+
+ void CreateSections(lldb_private::SectionList &unified_section_list) override;
+
+ void Dump(lldb_private::Stream *s) override;
+
+ lldb_private::ArchSpec GetArchitecture() override;
+
+ lldb_private::UUID GetUUID() override;
+
+ uint32_t GetDependentModules(lldb_private::FileSpecList &files) override;
+
+ size_t ReadSectionData(lldb_private::Section *section,
+ lldb::offset_t section_offset, void *dst,
+ size_t dst_len) override;
+
+ size_t
+ ReadSectionData(lldb_private::Section *section,
+ lldb_private::DataExtractor &section_data) override;
+
+ lldb_private::Address GetEntryPointAddress() override;
+
+ lldb_private::Address GetBaseAddress() override;
+
+ ObjectFile::Type CalculateType() override;
+
+ ObjectFile::Strata CalculateStrata() override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::ObjectFileJITDelegateWP m_delegate_wp;
+};
+
+#endif // liblldb_ObjectFileJIT_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp b/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp
new file mode 100644
index 000000000000..c1fe0cc8ddda
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp
@@ -0,0 +1,419 @@
+//===-- OperatingSystemPython.cpp --------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_DISABLE_PYTHON
+
+#include "OperatingSystemPython.h"
+
+#include "Plugins/Process/Utility/DynamicRegisterInfo.h"
+#include "Plugins/Process/Utility/RegisterContextDummy.h"
+#include "Plugins/Process/Utility/RegisterContextMemory.h"
+#include "Plugins/Process/Utility/ThreadMemory.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StructuredData.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OperatingSystemPython::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ nullptr);
+}
+
+void OperatingSystemPython::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+OperatingSystem *OperatingSystemPython::CreateInstance(Process *process,
+ bool force) {
+ // Python OperatingSystem plug-ins must be requested by name, so force must
+ // be true
+ FileSpec python_os_plugin_spec(process->GetPythonOSPluginPath());
+ if (python_os_plugin_spec &&
+ FileSystem::Instance().Exists(python_os_plugin_spec)) {
+ std::unique_ptr<OperatingSystemPython> os_up(
+ new OperatingSystemPython(process, python_os_plugin_spec));
+ if (os_up.get() && os_up->IsValid())
+ return os_up.release();
+ }
+ return nullptr;
+}
+
+ConstString OperatingSystemPython::GetPluginNameStatic() {
+ static ConstString g_name("python");
+ return g_name;
+}
+
+const char *OperatingSystemPython::GetPluginDescriptionStatic() {
+ return "Operating system plug-in that gathers OS information from a python "
+ "class that implements the necessary OperatingSystem functionality.";
+}
+
+OperatingSystemPython::OperatingSystemPython(lldb_private::Process *process,
+ const FileSpec &python_module_path)
+ : OperatingSystem(process), m_thread_list_valobj_sp(), m_register_info_up(),
+ m_interpreter(nullptr), m_python_object_sp() {
+ if (!process)
+ return;
+ TargetSP target_sp = process->CalculateTarget();
+ if (!target_sp)
+ return;
+ m_interpreter = target_sp->GetDebugger().GetScriptInterpreter();
+ if (m_interpreter) {
+
+ std::string os_plugin_class_name(
+ python_module_path.GetFilename().AsCString(""));
+ if (!os_plugin_class_name.empty()) {
+ const bool init_session = false;
+ const bool allow_reload = true;
+ char python_module_path_cstr[PATH_MAX];
+ python_module_path.GetPath(python_module_path_cstr,
+ sizeof(python_module_path_cstr));
+ Status error;
+ if (m_interpreter->LoadScriptingModule(
+ python_module_path_cstr, allow_reload, init_session, error)) {
+ // Strip the ".py" extension if there is one
+ size_t py_extension_pos = os_plugin_class_name.rfind(".py");
+ if (py_extension_pos != std::string::npos)
+ os_plugin_class_name.erase(py_extension_pos);
+ // Add ".OperatingSystemPlugIn" to the module name to get a string like
+ // "modulename.OperatingSystemPlugIn"
+ os_plugin_class_name += ".OperatingSystemPlugIn";
+ StructuredData::ObjectSP object_sp =
+ m_interpreter->OSPlugin_CreatePluginObject(
+ os_plugin_class_name.c_str(), process->CalculateProcess());
+ if (object_sp && object_sp->IsValid())
+ m_python_object_sp = object_sp;
+ }
+ }
+ }
+}
+
+OperatingSystemPython::~OperatingSystemPython() {}
+
+DynamicRegisterInfo *OperatingSystemPython::GetDynamicRegisterInfo() {
+ if (m_register_info_up == nullptr) {
+ if (!m_interpreter || !m_python_object_sp)
+ return nullptr;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
+
+ if (log)
+ log->Printf("OperatingSystemPython::GetDynamicRegisterInfo() fetching "
+ "thread register definitions from python for pid %" PRIu64,
+ m_process->GetID());
+
+ StructuredData::DictionarySP dictionary =
+ m_interpreter->OSPlugin_RegisterInfo(m_python_object_sp);
+ if (!dictionary)
+ return nullptr;
+
+ m_register_info_up.reset(new DynamicRegisterInfo(
+ *dictionary, m_process->GetTarget().GetArchitecture()));
+ assert(m_register_info_up->GetNumRegisters() > 0);
+ assert(m_register_info_up->GetNumRegisterSets() > 0);
+ }
+ return m_register_info_up.get();
+}
+
+// PluginInterface protocol
+ConstString OperatingSystemPython::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t OperatingSystemPython::GetPluginVersion() { return 1; }
+
+bool OperatingSystemPython::UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &core_thread_list,
+ ThreadList &new_thread_list) {
+ if (!m_interpreter || !m_python_object_sp)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
+
+ // First thing we have to do is to try to get the API lock, and the
+ // interpreter lock. We're going to change the thread content of the process,
+ // and we're going to use python, which requires the API lock to do it. We
+ // need the interpreter lock to make sure thread_info_dict stays alive.
+ //
+ // If someone already has the API lock, that is ok, we just want to avoid
+ // external code from making new API calls while this call is happening.
+ //
+ // This is a recursive lock so we can grant it to any Python code called on
+ // the stack below us.
+ Target &target = m_process->GetTarget();
+ std::unique_lock<std::recursive_mutex> api_lock(target.GetAPIMutex(),
+ std::defer_lock);
+ api_lock.try_lock();
+ auto interpreter_lock = m_interpreter->AcquireInterpreterLock();
+
+ if (log)
+ log->Printf("OperatingSystemPython::UpdateThreadList() fetching thread "
+ "data from python for pid %" PRIu64,
+ m_process->GetID());
+
+ // The threads that are in "new_thread_list" upon entry are the threads from
+ // the lldb_private::Process subclass, no memory threads will be in this
+ // list.
+ StructuredData::ArraySP threads_list =
+ m_interpreter->OSPlugin_ThreadsInfo(m_python_object_sp);
+
+ const uint32_t num_cores = core_thread_list.GetSize(false);
+
+ // Make a map so we can keep track of which cores were used from the
+ // core_thread list. Any real threads/cores that weren't used should later be
+ // put back into the "new_thread_list".
+ std::vector<bool> core_used_map(num_cores, false);
+ if (threads_list) {
+ if (log) {
+ StreamString strm;
+ threads_list->Dump(strm);
+ log->Printf("threads_list = %s", strm.GetData());
+ }
+
+ const uint32_t num_threads = threads_list->GetSize();
+ for (uint32_t i = 0; i < num_threads; ++i) {
+ StructuredData::ObjectSP thread_dict_obj =
+ threads_list->GetItemAtIndex(i);
+ if (auto thread_dict = thread_dict_obj->GetAsDictionary()) {
+ ThreadSP thread_sp(CreateThreadFromThreadInfo(
+ *thread_dict, core_thread_list, old_thread_list, core_used_map,
+ nullptr));
+ if (thread_sp)
+ new_thread_list.AddThread(thread_sp);
+ }
+ }
+ }
+
+ // Any real core threads that didn't end up backing a memory thread should
+ // still be in the main thread list, and they should be inserted at the
+ // beginning of the list
+ uint32_t insert_idx = 0;
+ for (uint32_t core_idx = 0; core_idx < num_cores; ++core_idx) {
+ if (!core_used_map[core_idx]) {
+ new_thread_list.InsertThread(
+ core_thread_list.GetThreadAtIndex(core_idx, false), insert_idx);
+ ++insert_idx;
+ }
+ }
+
+ return new_thread_list.GetSize(false) > 0;
+}
+
+ThreadSP OperatingSystemPython::CreateThreadFromThreadInfo(
+ StructuredData::Dictionary &thread_dict, ThreadList &core_thread_list,
+ ThreadList &old_thread_list, std::vector<bool> &core_used_map,
+ bool *did_create_ptr) {
+ ThreadSP thread_sp;
+ tid_t tid = LLDB_INVALID_THREAD_ID;
+ if (!thread_dict.GetValueForKeyAsInteger("tid", tid))
+ return ThreadSP();
+
+ uint32_t core_number;
+ addr_t reg_data_addr;
+ llvm::StringRef name;
+ llvm::StringRef queue;
+
+ thread_dict.GetValueForKeyAsInteger("core", core_number, UINT32_MAX);
+ thread_dict.GetValueForKeyAsInteger("register_data_addr", reg_data_addr,
+ LLDB_INVALID_ADDRESS);
+ thread_dict.GetValueForKeyAsString("name", name);
+ thread_dict.GetValueForKeyAsString("queue", queue);
+
+ // See if a thread already exists for "tid"
+ thread_sp = old_thread_list.FindThreadByID(tid, false);
+ if (thread_sp) {
+ // A thread already does exist for "tid", make sure it was an operating
+ // system
+ // plug-in generated thread.
+ if (!IsOperatingSystemPluginThread(thread_sp)) {
+ // We have thread ID overlap between the protocol threads and the
+ // operating system threads, clear the thread so we create an operating
+ // system thread for this.
+ thread_sp.reset();
+ }
+ }
+
+ if (!thread_sp) {
+ if (did_create_ptr)
+ *did_create_ptr = true;
+ thread_sp = std::make_shared<ThreadMemory>(*m_process, tid, name, queue,
+ reg_data_addr);
+ }
+
+ if (core_number < core_thread_list.GetSize(false)) {
+ ThreadSP core_thread_sp(
+ core_thread_list.GetThreadAtIndex(core_number, false));
+ if (core_thread_sp) {
+ // Keep track of which cores were set as the backing thread for memory
+ // threads...
+ if (core_number < core_used_map.size())
+ core_used_map[core_number] = true;
+
+ ThreadSP backing_core_thread_sp(core_thread_sp->GetBackingThread());
+ if (backing_core_thread_sp) {
+ thread_sp->SetBackingThread(backing_core_thread_sp);
+ } else {
+ thread_sp->SetBackingThread(core_thread_sp);
+ }
+ }
+ }
+ return thread_sp;
+}
+
+void OperatingSystemPython::ThreadWasSelected(Thread *thread) {}
+
+RegisterContextSP
+OperatingSystemPython::CreateRegisterContextForThread(Thread *thread,
+ addr_t reg_data_addr) {
+ RegisterContextSP reg_ctx_sp;
+ if (!m_interpreter || !m_python_object_sp || !thread)
+ return reg_ctx_sp;
+
+ if (!IsOperatingSystemPluginThread(thread->shared_from_this()))
+ return reg_ctx_sp;
+
+ // First thing we have to do is to try to get the API lock, and the
+ // interpreter lock. We're going to change the thread content of the process,
+ // and we're going to use python, which requires the API lock to do it. We
+ // need the interpreter lock to make sure thread_info_dict stays alive.
+ //
+ // If someone already has the API lock, that is ok, we just want to avoid
+ // external code from making new API calls while this call is happening.
+ //
+ // This is a recursive lock so we can grant it to any Python code called on
+ // the stack below us.
+ Target &target = m_process->GetTarget();
+ std::unique_lock<std::recursive_mutex> api_lock(target.GetAPIMutex(),
+ std::defer_lock);
+ api_lock.try_lock();
+ auto interpreter_lock = m_interpreter->AcquireInterpreterLock();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ if (reg_data_addr != LLDB_INVALID_ADDRESS) {
+ // The registers data is in contiguous memory, just create the register
+ // context using the address provided
+ if (log)
+ log->Printf("OperatingSystemPython::CreateRegisterContextForThread (tid "
+ "= 0x%" PRIx64 ", 0x%" PRIx64 ", reg_data_addr = 0x%" PRIx64
+ ") creating memory register context",
+ thread->GetID(), thread->GetProtocolID(), reg_data_addr);
+ reg_ctx_sp = std::make_shared<RegisterContextMemory>(
+ *thread, 0, *GetDynamicRegisterInfo(), reg_data_addr);
+ } else {
+ // No register data address is provided, query the python plug-in to let it
+ // make up the data as it sees fit
+ if (log)
+ log->Printf("OperatingSystemPython::CreateRegisterContextForThread (tid "
+ "= 0x%" PRIx64 ", 0x%" PRIx64
+ ") fetching register data from python",
+ thread->GetID(), thread->GetProtocolID());
+
+ StructuredData::StringSP reg_context_data =
+ m_interpreter->OSPlugin_RegisterContextData(m_python_object_sp,
+ thread->GetID());
+ if (reg_context_data) {
+ std::string value = reg_context_data->GetValue();
+ DataBufferSP data_sp(new DataBufferHeap(value.c_str(), value.length()));
+ if (data_sp->GetByteSize()) {
+ RegisterContextMemory *reg_ctx_memory = new RegisterContextMemory(
+ *thread, 0, *GetDynamicRegisterInfo(), LLDB_INVALID_ADDRESS);
+ if (reg_ctx_memory) {
+ reg_ctx_sp.reset(reg_ctx_memory);
+ reg_ctx_memory->SetAllRegisterData(data_sp);
+ }
+ }
+ }
+ }
+ // if we still have no register data, fallback on a dummy context to avoid
+ // crashing
+ if (!reg_ctx_sp) {
+ if (log)
+ log->Printf("OperatingSystemPython::CreateRegisterContextForThread (tid "
+ "= 0x%" PRIx64 ") forcing a dummy register context",
+ thread->GetID());
+ reg_ctx_sp = std::make_shared<RegisterContextDummy>(
+ *thread, 0, target.GetArchitecture().GetAddressByteSize());
+ }
+ return reg_ctx_sp;
+}
+
+StopInfoSP
+OperatingSystemPython::CreateThreadStopReason(lldb_private::Thread *thread) {
+ // We should have gotten the thread stop info from the dictionary of data for
+ // the thread in the initial call to get_thread_info(), this should have been
+ // cached so we can return it here
+ StopInfoSP
+ stop_info_sp; //(StopInfo::CreateStopReasonWithSignal (*thread, SIGSTOP));
+ return stop_info_sp;
+}
+
+lldb::ThreadSP OperatingSystemPython::CreateThread(lldb::tid_t tid,
+ addr_t context) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ if (log)
+ log->Printf("OperatingSystemPython::CreateThread (tid = 0x%" PRIx64
+ ", context = 0x%" PRIx64 ") fetching register data from python",
+ tid, context);
+
+ if (m_interpreter && m_python_object_sp) {
+ // First thing we have to do is to try to get the API lock, and the
+ // interpreter lock. We're going to change the thread content of the
+ // process, and we're going to use python, which requires the API lock to
+ // do it. We need the interpreter lock to make sure thread_info_dict stays
+ // alive.
+ //
+ // If someone already has the API lock, that is ok, we just want to avoid
+ // external code from making new API calls while this call is happening.
+ //
+ // This is a recursive lock so we can grant it to any Python code called on
+ // the stack below us.
+ Target &target = m_process->GetTarget();
+ std::unique_lock<std::recursive_mutex> api_lock(target.GetAPIMutex(),
+ std::defer_lock);
+ api_lock.try_lock();
+ auto interpreter_lock = m_interpreter->AcquireInterpreterLock();
+
+ StructuredData::DictionarySP thread_info_dict =
+ m_interpreter->OSPlugin_CreateThread(m_python_object_sp, tid, context);
+ std::vector<bool> core_used_map;
+ if (thread_info_dict) {
+ ThreadList core_threads(m_process);
+ ThreadList &thread_list = m_process->GetThreadList();
+ bool did_create = false;
+ ThreadSP thread_sp(
+ CreateThreadFromThreadInfo(*thread_info_dict, core_threads,
+ thread_list, core_used_map, &did_create));
+ if (did_create)
+ thread_list.AddThread(thread_sp);
+ return thread_sp;
+ }
+ }
+ return ThreadSP();
+}
+
+#endif // #ifndef LLDB_DISABLE_PYTHON
diff --git a/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.h b/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.h
new file mode 100644
index 000000000000..e76227ddb981
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.h
@@ -0,0 +1,85 @@
+//===-- OperatingSystemPython.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_OperatingSystemPython_h_
+#define liblldb_OperatingSystemPython_h_
+
+#ifndef LLDB_DISABLE_PYTHON
+
+#include "lldb/Target/OperatingSystem.h"
+#include "lldb/Utility/StructuredData.h"
+
+class DynamicRegisterInfo;
+
+namespace lldb_private {
+class ScriptInterpreter;
+}
+
+class OperatingSystemPython : public lldb_private::OperatingSystem {
+public:
+ OperatingSystemPython(lldb_private::Process *process,
+ const lldb_private::FileSpec &python_module_path);
+
+ ~OperatingSystemPython() override;
+
+ // Static Functions
+ static lldb_private::OperatingSystem *
+ CreateInstance(lldb_private::Process *process, bool force);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ // lldb_private::PluginInterface Methods
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // lldb_private::OperatingSystem Methods
+ bool UpdateThreadList(lldb_private::ThreadList &old_thread_list,
+ lldb_private::ThreadList &real_thread_list,
+ lldb_private::ThreadList &new_thread_list) override;
+
+ void ThreadWasSelected(lldb_private::Thread *thread) override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForThread(lldb_private::Thread *thread,
+ lldb::addr_t reg_data_addr) override;
+
+ lldb::StopInfoSP
+ CreateThreadStopReason(lldb_private::Thread *thread) override;
+
+ // Method for lazy creation of threads on demand
+ lldb::ThreadSP CreateThread(lldb::tid_t tid, lldb::addr_t context) override;
+
+protected:
+ bool IsValid() const {
+ return m_python_object_sp && m_python_object_sp->IsValid();
+ }
+
+ lldb::ThreadSP CreateThreadFromThreadInfo(
+ lldb_private::StructuredData::Dictionary &thread_dict,
+ lldb_private::ThreadList &core_thread_list,
+ lldb_private::ThreadList &old_thread_list,
+ std::vector<bool> &core_used_map, bool *did_create_ptr);
+
+ DynamicRegisterInfo *GetDynamicRegisterInfo();
+
+ lldb::ValueObjectSP m_thread_list_valobj_sp;
+ std::unique_ptr<DynamicRegisterInfo> m_register_info_up;
+ lldb_private::ScriptInterpreter *m_interpreter;
+ lldb_private::StructuredData::ObjectSP m_python_object_sp;
+};
+
+#endif // LLDB_DISABLE_PYTHON
+
+#endif // liblldb_OperatingSystemPython_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
new file mode 100644
index 000000000000..946f0ea3a5cf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
@@ -0,0 +1,321 @@
+//===-- PlatformFreeBSD.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PlatformFreeBSD.h"
+#include "lldb/Host/Config.h"
+
+#include <stdio.h>
+#ifndef LLDB_DISABLE_POSIX
+#include <sys/utsname.h>
+#endif
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointSite.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+// Define these constants from FreeBSD mman.h for use when targeting remote
+// FreeBSD systems even when host has different values.
+#define MAP_PRIVATE 0x0002
+#define MAP_ANON 0x1000
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::platform_freebsd;
+
+static uint32_t g_initialize_count = 0;
+
+
+PlatformSP PlatformFreeBSD::CreateInstance(bool force, const ArchSpec *arch) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force,
+ arch ? arch->GetArchitectureName() : "<null>",
+ arch ? arch->GetTriple().getTriple() : "<null>");
+
+ bool create = force;
+ if (!create && arch && arch->IsValid()) {
+ const llvm::Triple &triple = arch->GetTriple();
+ switch (triple.getOS()) {
+ case llvm::Triple::FreeBSD:
+ create = true;
+ break;
+
+#if defined(__FreeBSD__)
+ // Only accept "unknown" for the OS if the host is BSD and it "unknown"
+ // wasn't specified (it was just returned because it was NOT specified)
+ case llvm::Triple::OSType::UnknownOS:
+ create = !arch->TripleOSWasSpecified();
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ LLDB_LOG(log, "create = {0}", create);
+ if (create) {
+ return PlatformSP(new PlatformFreeBSD(false));
+ }
+ return PlatformSP();
+}
+
+ConstString PlatformFreeBSD::GetPluginNameStatic(bool is_host) {
+ if (is_host) {
+ static ConstString g_host_name(Platform::GetHostPlatformName());
+ return g_host_name;
+ } else {
+ static ConstString g_remote_name("remote-freebsd");
+ return g_remote_name;
+ }
+}
+
+const char *PlatformFreeBSD::GetPluginDescriptionStatic(bool is_host) {
+ if (is_host)
+ return "Local FreeBSD user platform plug-in.";
+ else
+ return "Remote FreeBSD user platform plug-in.";
+}
+
+ConstString PlatformFreeBSD::GetPluginName() {
+ return GetPluginNameStatic(IsHost());
+}
+
+void PlatformFreeBSD::Initialize() {
+ Platform::Initialize();
+
+ if (g_initialize_count++ == 0) {
+#if defined(__FreeBSD__)
+ PlatformSP default_platform_sp(new PlatformFreeBSD(true));
+ default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
+ Platform::SetHostPlatform(default_platform_sp);
+#endif
+ PluginManager::RegisterPlugin(
+ PlatformFreeBSD::GetPluginNameStatic(false),
+ PlatformFreeBSD::GetPluginDescriptionStatic(false),
+ PlatformFreeBSD::CreateInstance, nullptr);
+ }
+}
+
+void PlatformFreeBSD::Terminate() {
+ if (g_initialize_count > 0) {
+ if (--g_initialize_count == 0) {
+ PluginManager::UnregisterPlugin(PlatformFreeBSD::CreateInstance);
+ }
+ }
+
+ PlatformPOSIX::Terminate();
+}
+
+/// Default Constructor
+PlatformFreeBSD::PlatformFreeBSD(bool is_host)
+ : PlatformPOSIX(is_host) // This is the local host platform
+{}
+
+PlatformFreeBSD::~PlatformFreeBSD() = default;
+
+bool PlatformFreeBSD::GetSupportedArchitectureAtIndex(uint32_t idx,
+ ArchSpec &arch) {
+ if (IsHost()) {
+ ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
+ if (hostArch.GetTriple().isOSFreeBSD()) {
+ if (idx == 0) {
+ arch = hostArch;
+ return arch.IsValid();
+ } else if (idx == 1) {
+ // If the default host architecture is 64-bit, look for a 32-bit
+ // variant
+ if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit()) {
+ arch = HostInfo::GetArchitecture(HostInfo::eArchKind32);
+ return arch.IsValid();
+ }
+ }
+ }
+ } else {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch);
+
+ llvm::Triple triple;
+ // Set the OS to FreeBSD
+ triple.setOS(llvm::Triple::FreeBSD);
+ // Set the architecture
+ switch (idx) {
+ case 0:
+ triple.setArchName("x86_64");
+ break;
+ case 1:
+ triple.setArchName("i386");
+ break;
+ case 2:
+ triple.setArchName("aarch64");
+ break;
+ case 3:
+ triple.setArchName("arm");
+ break;
+ case 4:
+ triple.setArchName("mips64");
+ break;
+ case 5:
+ triple.setArchName("mips");
+ break;
+ case 6:
+ triple.setArchName("ppc64");
+ break;
+ case 7:
+ triple.setArchName("ppc");
+ break;
+ default:
+ return false;
+ }
+ // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the
+ // vendor by calling triple.SetVendorName("unknown") so that it is a
+ // "unspecified unknown". This means when someone calls
+ // triple.GetVendorName() it will return an empty string which indicates
+ // that the vendor can be set when two architectures are merged
+
+ // Now set the triple into "arch" and return true
+ arch.SetTriple(triple);
+ return true;
+ }
+ return false;
+}
+
+void PlatformFreeBSD::GetStatus(Stream &strm) {
+ Platform::GetStatus(strm);
+
+#ifndef LLDB_DISABLE_POSIX
+ // Display local kernel information only when we are running in host mode.
+ // Otherwise, we would end up printing non-FreeBSD information (when running
+ // on Mac OS for example).
+ if (IsHost()) {
+ struct utsname un;
+
+ if (uname(&un))
+ return;
+
+ strm.Printf(" Kernel: %s\n", un.sysname);
+ strm.Printf(" Release: %s\n", un.release);
+ strm.Printf(" Version: %s\n", un.version);
+ }
+#endif
+}
+
+size_t
+PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode(Target &target,
+ BreakpointSite *bp_site) {
+ switch (target.GetArchitecture().GetMachine()) {
+ case llvm::Triple::arm: {
+ lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0));
+ AddressClass addr_class = AddressClass::eUnknown;
+
+ if (bp_loc_sp) {
+ addr_class = bp_loc_sp->GetAddress().GetAddressClass();
+ if (addr_class == AddressClass::eUnknown &&
+ (bp_loc_sp->GetAddress().GetFileAddress() & 1))
+ addr_class = AddressClass::eCodeAlternateISA;
+ }
+
+ if (addr_class == AddressClass::eCodeAlternateISA) {
+ // TODO: Enable when FreeBSD supports thumb breakpoints.
+ // FreeBSD kernel as of 10.x, does not support thumb breakpoints
+ return 0;
+ }
+
+ static const uint8_t g_arm_breakpoint_opcode[] = {0xFE, 0xDE, 0xFF, 0xE7};
+ size_t trap_opcode_size = sizeof(g_arm_breakpoint_opcode);
+ assert(bp_site);
+ if (bp_site->SetTrapOpcode(g_arm_breakpoint_opcode, trap_opcode_size))
+ return trap_opcode_size;
+ }
+ LLVM_FALLTHROUGH;
+ default:
+ return Platform::GetSoftwareBreakpointTrapOpcode(target, bp_site);
+ }
+}
+
+Status PlatformFreeBSD::LaunchProcess(ProcessLaunchInfo &launch_info) {
+ Status error;
+ if (IsHost()) {
+ error = Platform::LaunchProcess(launch_info);
+ } else {
+ if (m_remote_platform_sp)
+ error = m_remote_platform_sp->LaunchProcess(launch_info);
+ else
+ error.SetErrorString("the platform is not currently connected");
+ }
+ return error;
+}
+
+lldb::ProcessSP PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info,
+ Debugger &debugger, Target *target,
+ Status &error) {
+ lldb::ProcessSP process_sp;
+ if (IsHost()) {
+ if (target == nullptr) {
+ TargetSP new_target_sp;
+ ArchSpec emptyArchSpec;
+
+ error = debugger.GetTargetList().CreateTarget(
+ debugger, "", emptyArchSpec, eLoadDependentsNo, m_remote_platform_sp,
+ new_target_sp);
+ target = new_target_sp.get();
+ } else
+ error.Clear();
+
+ if (target && error.Success()) {
+ debugger.GetTargetList().SetSelectedTarget(target);
+ // The freebsd always currently uses the GDB remote debugger plug-in so
+ // even when debugging locally we are debugging remotely! Just like the
+ // darwin plugin.
+ process_sp = target->CreateProcess(
+ attach_info.GetListenerForProcess(debugger), "gdb-remote", nullptr);
+
+ if (process_sp)
+ error = process_sp->Attach(attach_info);
+ }
+ } else {
+ if (m_remote_platform_sp)
+ process_sp =
+ m_remote_platform_sp->Attach(attach_info, debugger, target, error);
+ else
+ error.SetErrorString("the platform is not currently connected");
+ }
+ return process_sp;
+}
+
+// FreeBSD processes cannot yet be launched by spawning and attaching.
+bool PlatformFreeBSD::CanDebugProcess() {
+ return false;
+}
+
+void PlatformFreeBSD::CalculateTrapHandlerSymbolNames() {
+ m_trap_handlers.push_back(ConstString("_sigtramp"));
+}
+
+MmapArgList PlatformFreeBSD::GetMmapArgumentList(const ArchSpec &arch,
+ addr_t addr, addr_t length,
+ unsigned prot, unsigned flags,
+ addr_t fd, addr_t offset) {
+ uint64_t flags_platform = 0;
+
+ if (flags & eMmapFlagsPrivate)
+ flags_platform |= MAP_PRIVATE;
+ if (flags & eMmapFlagsAnon)
+ flags_platform |= MAP_ANON;
+
+ MmapArgList args({addr, length, prot, flags_platform, fd, offset});
+ if (arch.GetTriple().getArch() == llvm::Triple::x86)
+ args.push_back(0);
+ return args;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h
new file mode 100644
index 000000000000..e3a3aa7145f0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h
@@ -0,0 +1,71 @@
+//===-- PlatformFreeBSD.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_PlatformFreeBSD_h_
+#define liblldb_PlatformFreeBSD_h_
+
+#include "Plugins/Platform/POSIX/PlatformPOSIX.h"
+
+namespace lldb_private {
+namespace platform_freebsd {
+
+class PlatformFreeBSD : public PlatformPOSIX {
+public:
+ PlatformFreeBSD(bool is_host);
+
+ ~PlatformFreeBSD() override;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ // lldb_private::PluginInterface functions
+ static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+
+ static ConstString GetPluginNameStatic(bool is_host);
+
+ static const char *GetPluginDescriptionStatic(bool is_host);
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ // lldb_private::Platform functions
+ const char *GetDescription() override {
+ return GetPluginDescriptionStatic(IsHost());
+ }
+
+ void GetStatus(Stream &strm) override;
+
+ bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override;
+
+ bool CanDebugProcess() override;
+
+ size_t GetSoftwareBreakpointTrapOpcode(Target &target,
+ BreakpointSite *bp_site) override;
+
+ Status LaunchProcess(ProcessLaunchInfo &launch_info) override;
+
+ lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger,
+ Target *target, Status &error) override;
+
+ void CalculateTrapHandlerSymbolNames() override;
+
+ MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr,
+ lldb::addr_t length, unsigned prot,
+ unsigned flags, lldb::addr_t fd,
+ lldb::addr_t offset) override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(PlatformFreeBSD);
+};
+
+} // namespace platform_freebsd
+} // namespace lldb_private
+
+#endif // liblldb_PlatformFreeBSD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.cpp
new file mode 100644
index 000000000000..63245d18fc5c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.cpp
@@ -0,0 +1,364 @@
+//===-- PlatformNetBSD.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PlatformNetBSD.h"
+#include "lldb/Host/Config.h"
+
+#include <stdio.h>
+#ifndef LLDB_DISABLE_POSIX
+#include <sys/utsname.h>
+#endif
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+// Define these constants from NetBSD mman.h for use when targeting remote
+// netbsd systems even when host has different values.
+#define MAP_PRIVATE 0x0002
+#define MAP_ANON 0x1000
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::platform_netbsd;
+
+static uint32_t g_initialize_count = 0;
+
+
+PlatformSP PlatformNetBSD::CreateInstance(bool force, const ArchSpec *arch) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force,
+ arch ? arch->GetArchitectureName() : "<null>",
+ arch ? arch->GetTriple().getTriple() : "<null>");
+
+ bool create = force;
+ if (!create && arch && arch->IsValid()) {
+ const llvm::Triple &triple = arch->GetTriple();
+ switch (triple.getOS()) {
+ case llvm::Triple::NetBSD:
+ create = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ LLDB_LOG(log, "create = {0}", create);
+ if (create) {
+ return PlatformSP(new PlatformNetBSD(false));
+ }
+ return PlatformSP();
+}
+
+ConstString PlatformNetBSD::GetPluginNameStatic(bool is_host) {
+ if (is_host) {
+ static ConstString g_host_name(Platform::GetHostPlatformName());
+ return g_host_name;
+ } else {
+ static ConstString g_remote_name("remote-netbsd");
+ return g_remote_name;
+ }
+}
+
+const char *PlatformNetBSD::GetPluginDescriptionStatic(bool is_host) {
+ if (is_host)
+ return "Local NetBSD user platform plug-in.";
+ else
+ return "Remote NetBSD user platform plug-in.";
+}
+
+ConstString PlatformNetBSD::GetPluginName() {
+ return GetPluginNameStatic(IsHost());
+}
+
+void PlatformNetBSD::Initialize() {
+ PlatformPOSIX::Initialize();
+
+ if (g_initialize_count++ == 0) {
+#if defined(__NetBSD__)
+ PlatformSP default_platform_sp(new PlatformNetBSD(true));
+ default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
+ Platform::SetHostPlatform(default_platform_sp);
+#endif
+ PluginManager::RegisterPlugin(
+ PlatformNetBSD::GetPluginNameStatic(false),
+ PlatformNetBSD::GetPluginDescriptionStatic(false),
+ PlatformNetBSD::CreateInstance, nullptr);
+ }
+}
+
+void PlatformNetBSD::Terminate() {
+ if (g_initialize_count > 0) {
+ if (--g_initialize_count == 0) {
+ PluginManager::UnregisterPlugin(PlatformNetBSD::CreateInstance);
+ }
+ }
+
+ PlatformPOSIX::Terminate();
+}
+
+/// Default Constructor
+PlatformNetBSD::PlatformNetBSD(bool is_host)
+ : PlatformPOSIX(is_host) // This is the local host platform
+{}
+
+PlatformNetBSD::~PlatformNetBSD() = default;
+
+bool PlatformNetBSD::GetSupportedArchitectureAtIndex(uint32_t idx,
+ ArchSpec &arch) {
+ if (IsHost()) {
+ ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
+ if (hostArch.GetTriple().isOSNetBSD()) {
+ if (idx == 0) {
+ arch = hostArch;
+ return arch.IsValid();
+ } else if (idx == 1) {
+ // If the default host architecture is 64-bit, look for a 32-bit
+ // variant
+ if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit()) {
+ arch = HostInfo::GetArchitecture(HostInfo::eArchKind32);
+ return arch.IsValid();
+ }
+ }
+ }
+ } else {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch);
+
+ llvm::Triple triple;
+ // Set the OS to NetBSD
+ triple.setOS(llvm::Triple::NetBSD);
+ // Set the architecture
+ switch (idx) {
+ case 0:
+ triple.setArchName("x86_64");
+ break;
+ case 1:
+ triple.setArchName("i386");
+ break;
+ default:
+ return false;
+ }
+ // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the
+ // vendor by calling triple.SetVendorName("unknown") so that it is a
+ // "unspecified unknown". This means when someone calls
+ // triple.GetVendorName() it will return an empty string which indicates
+ // that the vendor can be set when two architectures are merged
+
+ // Now set the triple into "arch" and return true
+ arch.SetTriple(triple);
+ return true;
+ }
+ return false;
+}
+
+void PlatformNetBSD::GetStatus(Stream &strm) {
+ Platform::GetStatus(strm);
+
+#ifndef LLDB_DISABLE_POSIX
+ // Display local kernel information only when we are running in host mode.
+ // Otherwise, we would end up printing non-NetBSD information (when running
+ // on Mac OS for example).
+ if (IsHost()) {
+ struct utsname un;
+
+ if (uname(&un))
+ return;
+
+ strm.Printf(" Kernel: %s\n", un.sysname);
+ strm.Printf(" Release: %s\n", un.release);
+ strm.Printf(" Version: %s\n", un.version);
+ }
+#endif
+}
+
+int32_t
+PlatformNetBSD::GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) {
+ int32_t resume_count = 0;
+
+ // Always resume past the initial stop when we use eLaunchFlagDebug
+ if (launch_info.GetFlags().Test(eLaunchFlagDebug)) {
+ // Resume past the stop for the final exec into the true inferior.
+ ++resume_count;
+ }
+
+ // If we're not launching a shell, we're done.
+ const FileSpec &shell = launch_info.GetShell();
+ if (!shell)
+ return resume_count;
+
+ std::string shell_string = shell.GetPath();
+ // We're in a shell, so for sure we have to resume past the shell exec.
+ ++resume_count;
+
+ // Figure out what shell we're planning on using.
+ const char *shell_name = strrchr(shell_string.c_str(), '/');
+ if (shell_name == nullptr)
+ shell_name = shell_string.c_str();
+ else
+ shell_name++;
+
+ if (strcmp(shell_name, "csh") == 0 || strcmp(shell_name, "tcsh") == 0 ||
+ strcmp(shell_name, "zsh") == 0 || strcmp(shell_name, "sh") == 0) {
+ // These shells seem to re-exec themselves. Add another resume.
+ ++resume_count;
+ }
+
+ return resume_count;
+}
+
+bool PlatformNetBSD::CanDebugProcess() {
+ if (IsHost()) {
+ return true;
+ } else {
+ // If we're connected, we can debug.
+ return IsConnected();
+ }
+}
+
+// For local debugging, NetBSD will override the debug logic to use llgs-launch
+// rather than lldb-launch, llgs-attach. This differs from current lldb-
+// launch, debugserver-attach approach on MacOSX.
+lldb::ProcessSP
+PlatformNetBSD::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a new
+ // target, else use existing one
+ Status &error) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ LLDB_LOG(log, "target {0}", target);
+
+ // If we're a remote host, use standard behavior from parent class.
+ if (!IsHost())
+ return PlatformPOSIX::DebugProcess(launch_info, debugger, target, error);
+
+ //
+ // For local debugging, we'll insist on having ProcessGDBRemote create the
+ // process.
+ //
+
+ ProcessSP process_sp;
+
+ // Make sure we stop at the entry point
+ launch_info.GetFlags().Set(eLaunchFlagDebug);
+
+ // We always launch the process we are going to debug in a separate process
+ // group, since then we can handle ^C interrupts ourselves w/o having to
+ // worry about the target getting them as well.
+ launch_info.SetLaunchInSeparateProcessGroup(true);
+
+ // Ensure we have a target.
+ if (target == nullptr) {
+ LLDB_LOG(log, "creating new target");
+ TargetSP new_target_sp;
+ error = debugger.GetTargetList().CreateTarget(
+ debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
+ if (error.Fail()) {
+ LLDB_LOG(log, "failed to create new target: {0}", error);
+ return process_sp;
+ }
+
+ target = new_target_sp.get();
+ if (!target) {
+ error.SetErrorString("CreateTarget() returned nullptr");
+ LLDB_LOG(log, "error: {0}", error);
+ return process_sp;
+ }
+ }
+
+ // Mark target as currently selected target.
+ debugger.GetTargetList().SetSelectedTarget(target);
+
+ // Now create the gdb-remote process.
+ LLDB_LOG(log, "having target create process with gdb-remote plugin");
+ process_sp =
+ target->CreateProcess(launch_info.GetListener(), "gdb-remote", nullptr);
+
+ if (!process_sp) {
+ error.SetErrorString("CreateProcess() failed for gdb-remote process");
+ LLDB_LOG(log, "error: {0}", error);
+ return process_sp;
+ }
+
+ LLDB_LOG(log, "successfully created process");
+ // Adjust launch for a hijacker.
+ ListenerSP listener_sp;
+ if (!launch_info.GetHijackListener()) {
+ LLDB_LOG(log, "setting up hijacker");
+ listener_sp =
+ Listener::MakeListener("lldb.PlatformNetBSD.DebugProcess.hijack");
+ launch_info.SetHijackListener(listener_sp);
+ process_sp->HijackProcessEvents(listener_sp);
+ }
+
+ // Log file actions.
+ if (log) {
+ LLDB_LOG(log, "launching process with the following file actions:");
+ StreamString stream;
+ size_t i = 0;
+ const FileAction *file_action;
+ while ((file_action = launch_info.GetFileActionAtIndex(i++)) != nullptr) {
+ file_action->Dump(stream);
+ LLDB_LOG(log, "{0}", stream.GetData());
+ stream.Clear();
+ }
+ }
+
+ // Do the launch.
+ error = process_sp->Launch(launch_info);
+ if (error.Success()) {
+ // Handle the hijacking of process events.
+ if (listener_sp) {
+ const StateType state = process_sp->WaitForProcessToStop(
+ llvm::None, nullptr, false, listener_sp);
+
+ LLDB_LOG(log, "pid {0} state {0}", process_sp->GetID(), state);
+ }
+
+ // Hook up process PTY if we have one (which we should for local debugging
+ // with llgs).
+ int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
+ if (pty_fd != PseudoTerminal::invalid_fd) {
+ process_sp->SetSTDIOFileDescriptor(pty_fd);
+ LLDB_LOG(log, "hooked up STDIO pty to process");
+ } else
+ LLDB_LOG(log, "not using process STDIO pty");
+ } else {
+ LLDB_LOG(log, "process launch failed: {0}", error);
+ // FIXME figure out appropriate cleanup here. Do we delete the target? Do
+ // we delete the process? Does our caller do that?
+ }
+
+ return process_sp;
+}
+
+void PlatformNetBSD::CalculateTrapHandlerSymbolNames() {
+ m_trap_handlers.push_back(ConstString("_sigtramp"));
+}
+
+MmapArgList PlatformNetBSD::GetMmapArgumentList(const ArchSpec &arch,
+ addr_t addr, addr_t length,
+ unsigned prot, unsigned flags,
+ addr_t fd, addr_t offset) {
+ uint64_t flags_platform = 0;
+
+ if (flags & eMmapFlagsPrivate)
+ flags_platform |= MAP_PRIVATE;
+ if (flags & eMmapFlagsAnon)
+ flags_platform |= MAP_ANON;
+
+ MmapArgList args({addr, length, prot, flags_platform, fd, offset});
+ return args;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.h
new file mode 100644
index 000000000000..0584d92d6c99
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.h
@@ -0,0 +1,69 @@
+//===-- PlatformNetBSD.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_PlatformNetBSD_h_
+#define liblldb_PlatformNetBSD_h_
+
+#include "Plugins/Platform/POSIX/PlatformPOSIX.h"
+
+namespace lldb_private {
+namespace platform_netbsd {
+
+class PlatformNetBSD : public PlatformPOSIX {
+public:
+ PlatformNetBSD(bool is_host);
+
+ ~PlatformNetBSD() override;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ // lldb_private::PluginInterface functions
+ static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+
+ static ConstString GetPluginNameStatic(bool is_host);
+
+ static const char *GetPluginDescriptionStatic(bool is_host);
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ // lldb_private::Platform functions
+ const char *GetDescription() override {
+ return GetPluginDescriptionStatic(IsHost());
+ }
+
+ void GetStatus(Stream &strm) override;
+
+ bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override;
+
+ int32_t GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) override;
+
+ bool CanDebugProcess() override;
+
+ lldb::ProcessSP DebugProcess(ProcessLaunchInfo &launch_info,
+ Debugger &debugger, Target *target,
+ Status &error) override;
+
+ void CalculateTrapHandlerSymbolNames() override;
+
+ MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr,
+ lldb::addr_t length, unsigned prot,
+ unsigned flags, lldb::addr_t fd,
+ lldb::addr_t offset) override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(PlatformNetBSD);
+};
+
+} // namespace platform_netbsd
+} // namespace lldb_private
+
+#endif // liblldb_PlatformNetBSD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp
new file mode 100644
index 000000000000..9dfb8844c574
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp
@@ -0,0 +1,215 @@
+//===-- PlatformOpenBSD.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PlatformOpenBSD.h"
+#include "lldb/Host/Config.h"
+
+#include <stdio.h>
+#ifndef LLDB_DISABLE_POSIX
+#include <sys/utsname.h>
+#endif
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+// Define these constants from OpenBSD mman.h for use when targeting remote
+// openbsd systems even when host has different values.
+#define MAP_PRIVATE 0x0002
+#define MAP_ANON 0x1000
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::platform_openbsd;
+
+static uint32_t g_initialize_count = 0;
+
+
+PlatformSP PlatformOpenBSD::CreateInstance(bool force, const ArchSpec *arch) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force,
+ arch ? arch->GetArchitectureName() : "<null>",
+ arch ? arch->GetTriple().getTriple() : "<null>");
+
+ bool create = force;
+ if (!create && arch && arch->IsValid()) {
+ const llvm::Triple &triple = arch->GetTriple();
+ switch (triple.getOS()) {
+ case llvm::Triple::OpenBSD:
+ create = true;
+ break;
+
+#if defined(__OpenBSD__)
+ // Only accept "unknown" for the OS if the host is BSD and it "unknown"
+ // wasn't specified (it was just returned because it was NOT specified)
+ case llvm::Triple::OSType::UnknownOS:
+ create = !arch->TripleOSWasSpecified();
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ LLDB_LOG(log, "create = {0}", create);
+ if (create) {
+ return PlatformSP(new PlatformOpenBSD(false));
+ }
+ return PlatformSP();
+}
+
+ConstString PlatformOpenBSD::GetPluginNameStatic(bool is_host) {
+ if (is_host) {
+ static ConstString g_host_name(Platform::GetHostPlatformName());
+ return g_host_name;
+ } else {
+ static ConstString g_remote_name("remote-openbsd");
+ return g_remote_name;
+ }
+}
+
+const char *PlatformOpenBSD::GetPluginDescriptionStatic(bool is_host) {
+ if (is_host)
+ return "Local OpenBSD user platform plug-in.";
+ else
+ return "Remote OpenBSD user platform plug-in.";
+}
+
+ConstString PlatformOpenBSD::GetPluginName() {
+ return GetPluginNameStatic(IsHost());
+}
+
+void PlatformOpenBSD::Initialize() {
+ Platform::Initialize();
+
+ if (g_initialize_count++ == 0) {
+#if defined(__OpenBSD__)
+ PlatformSP default_platform_sp(new PlatformOpenBSD(true));
+ default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
+ Platform::SetHostPlatform(default_platform_sp);
+#endif
+ PluginManager::RegisterPlugin(
+ PlatformOpenBSD::GetPluginNameStatic(false),
+ PlatformOpenBSD::GetPluginDescriptionStatic(false),
+ PlatformOpenBSD::CreateInstance, nullptr);
+ }
+}
+
+void PlatformOpenBSD::Terminate() {
+ if (g_initialize_count > 0) {
+ if (--g_initialize_count == 0) {
+ PluginManager::UnregisterPlugin(PlatformOpenBSD::CreateInstance);
+ }
+ }
+
+ PlatformPOSIX::Terminate();
+}
+
+/// Default Constructor
+PlatformOpenBSD::PlatformOpenBSD(bool is_host)
+ : PlatformPOSIX(is_host) // This is the local host platform
+{}
+
+PlatformOpenBSD::~PlatformOpenBSD() = default;
+
+bool PlatformOpenBSD::GetSupportedArchitectureAtIndex(uint32_t idx,
+ ArchSpec &arch) {
+ if (IsHost()) {
+ ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
+ if (hostArch.GetTriple().isOSOpenBSD()) {
+ if (idx == 0) {
+ arch = hostArch;
+ return arch.IsValid();
+ }
+ }
+ } else {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch);
+
+ llvm::Triple triple;
+ // Set the OS to OpenBSD
+ triple.setOS(llvm::Triple::OpenBSD);
+ // Set the architecture
+ switch (idx) {
+ case 0:
+ triple.setArchName("x86_64");
+ break;
+ case 1:
+ triple.setArchName("i386");
+ break;
+ case 2:
+ triple.setArchName("aarch64");
+ break;
+ case 3:
+ triple.setArchName("arm");
+ break;
+ default:
+ return false;
+ }
+ // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the
+ // vendor by calling triple.SetVendorName("unknown") so that it is a
+ // "unspecified unknown". This means when someone calls
+ // triple.GetVendorName() it will return an empty string which indicates
+ // that the vendor can be set when two architectures are merged
+
+ // Now set the triple into "arch" and return true
+ arch.SetTriple(triple);
+ return true;
+ }
+ return false;
+}
+
+void PlatformOpenBSD::GetStatus(Stream &strm) {
+ Platform::GetStatus(strm);
+
+#ifndef LLDB_DISABLE_POSIX
+ // Display local kernel information only when we are running in host mode.
+ // Otherwise, we would end up printing non-OpenBSD information (when running
+ // on Mac OS for example).
+ if (IsHost()) {
+ struct utsname un;
+
+ if (uname(&un))
+ return;
+
+ strm.Printf(" Kernel: %s\n", un.sysname);
+ strm.Printf(" Release: %s\n", un.release);
+ strm.Printf(" Version: %s\n", un.version);
+ }
+#endif
+}
+
+// OpenBSD processes cannot yet be launched by spawning and attaching.
+bool PlatformOpenBSD::CanDebugProcess() {
+ return false;
+}
+
+void PlatformOpenBSD::CalculateTrapHandlerSymbolNames() {
+ m_trap_handlers.push_back(ConstString("_sigtramp"));
+}
+
+MmapArgList PlatformOpenBSD::GetMmapArgumentList(const ArchSpec &arch,
+ addr_t addr, addr_t length,
+ unsigned prot, unsigned flags,
+ addr_t fd, addr_t offset) {
+ uint64_t flags_platform = 0;
+
+ if (flags & eMmapFlagsPrivate)
+ flags_platform |= MAP_PRIVATE;
+ if (flags & eMmapFlagsAnon)
+ flags_platform |= MAP_ANON;
+
+ MmapArgList args({addr, length, prot, flags_platform, fd, offset});
+ return args;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.h b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.h
new file mode 100644
index 000000000000..3cb724f30325
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.h
@@ -0,0 +1,63 @@
+//===-- PlatformOpenBSD.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_PlatformOpenBSD_h_
+#define liblldb_PlatformOpenBSD_h_
+
+#include "Plugins/Platform/POSIX/PlatformPOSIX.h"
+
+namespace lldb_private {
+namespace platform_openbsd {
+
+class PlatformOpenBSD : public PlatformPOSIX {
+public:
+ PlatformOpenBSD(bool is_host);
+
+ ~PlatformOpenBSD() override;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ // lldb_private::PluginInterface functions
+ static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+
+ static ConstString GetPluginNameStatic(bool is_host);
+
+ static const char *GetPluginDescriptionStatic(bool is_host);
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ // lldb_private::Platform functions
+ const char *GetDescription() override {
+ return GetPluginDescriptionStatic(IsHost());
+ }
+
+ void GetStatus(Stream &strm) override;
+
+ bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override;
+
+ bool CanDebugProcess() override;
+
+ void CalculateTrapHandlerSymbolNames() override;
+
+ MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr,
+ lldb::addr_t length, unsigned prot,
+ unsigned flags, lldb::addr_t fd,
+ lldb::addr_t offset) override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(PlatformOpenBSD);
+};
+
+} // namespace platform_openbsd
+} // namespace lldb_private
+
+#endif // liblldb_PlatformOpenBSD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
new file mode 100644
index 000000000000..d10557596ff8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
@@ -0,0 +1,1062 @@
+//===-- PlatformPOSIX.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PlatformPOSIX.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileCache.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/CleanUp.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+/// Default Constructor
+PlatformPOSIX::PlatformPOSIX(bool is_host)
+ : RemoteAwarePlatform(is_host), // This is the local host platform
+ m_option_group_platform_rsync(new OptionGroupPlatformRSync()),
+ m_option_group_platform_ssh(new OptionGroupPlatformSSH()),
+ m_option_group_platform_caching(new OptionGroupPlatformCaching()) {}
+
+/// Destructor.
+///
+/// The destructor is virtual since this class is designed to be
+/// inherited from by the plug-in instance.
+PlatformPOSIX::~PlatformPOSIX() {}
+
+lldb_private::OptionGroupOptions *PlatformPOSIX::GetConnectionOptions(
+ lldb_private::CommandInterpreter &interpreter) {
+ auto iter = m_options.find(&interpreter), end = m_options.end();
+ if (iter == end) {
+ std::unique_ptr<lldb_private::OptionGroupOptions> options(
+ new OptionGroupOptions());
+ options->Append(m_option_group_platform_rsync.get());
+ options->Append(m_option_group_platform_ssh.get());
+ options->Append(m_option_group_platform_caching.get());
+ m_options[&interpreter] = std::move(options);
+ }
+
+ return m_options.at(&interpreter).get();
+}
+
+Status
+PlatformPOSIX::ResolveExecutable(const ModuleSpec &module_spec,
+ lldb::ModuleSP &exe_module_sp,
+ const FileSpecList *module_search_paths_ptr) {
+ Status error;
+ // Nothing special to do here, just use the actual file and architecture
+
+ char exe_path[PATH_MAX];
+ ModuleSpec resolved_module_spec(module_spec);
+
+ if (IsHost()) {
+ // If we have "ls" as the exe_file, resolve the executable location based
+ // on the current path variables
+ if (!FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec())) {
+ resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path));
+ resolved_module_spec.GetFileSpec().SetFile(exe_path,
+ FileSpec::Style::native);
+ FileSystem::Instance().Resolve(resolved_module_spec.GetFileSpec());
+ }
+
+ if (!FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
+ FileSystem::Instance().ResolveExecutableLocation(
+ resolved_module_spec.GetFileSpec());
+
+ // Resolve any executable within a bundle on MacOSX
+ Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec());
+
+ if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
+ error.Clear();
+ else {
+ const uint32_t permissions = FileSystem::Instance().GetPermissions(
+ resolved_module_spec.GetFileSpec());
+ if (permissions && (permissions & eFilePermissionsEveryoneR) == 0)
+ error.SetErrorStringWithFormat(
+ "executable '%s' is not readable",
+ resolved_module_spec.GetFileSpec().GetPath().c_str());
+ else
+ error.SetErrorStringWithFormat(
+ "unable to find executable for '%s'",
+ resolved_module_spec.GetFileSpec().GetPath().c_str());
+ }
+ } else {
+ if (m_remote_platform_sp) {
+ error =
+ GetCachedExecutable(resolved_module_spec, exe_module_sp,
+ module_search_paths_ptr, *m_remote_platform_sp);
+ } else {
+ // We may connect to a process and use the provided executable (Don't use
+ // local $PATH).
+
+ // Resolve any executable within a bundle on MacOSX
+ Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec());
+
+ if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
+ error.Clear();
+ else
+ error.SetErrorStringWithFormat("the platform is not currently "
+ "connected, and '%s' doesn't exist in "
+ "the system root.",
+ exe_path);
+ }
+ }
+
+ if (error.Success()) {
+ if (resolved_module_spec.GetArchitecture().IsValid()) {
+ error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
+ module_search_paths_ptr, nullptr, nullptr);
+ if (error.Fail()) {
+ // If we failed, it may be because the vendor and os aren't known. If
+ // that is the case, try setting them to the host architecture and give
+ // it another try.
+ llvm::Triple &module_triple =
+ resolved_module_spec.GetArchitecture().GetTriple();
+ bool is_vendor_specified =
+ (module_triple.getVendor() != llvm::Triple::UnknownVendor);
+ bool is_os_specified =
+ (module_triple.getOS() != llvm::Triple::UnknownOS);
+ if (!is_vendor_specified || !is_os_specified) {
+ const llvm::Triple &host_triple =
+ HostInfo::GetArchitecture(HostInfo::eArchKindDefault).GetTriple();
+
+ if (!is_vendor_specified)
+ module_triple.setVendorName(host_triple.getVendorName());
+ if (!is_os_specified)
+ module_triple.setOSName(host_triple.getOSName());
+
+ error = ModuleList::GetSharedModule(resolved_module_spec,
+ exe_module_sp, module_search_paths_ptr, nullptr, nullptr);
+ }
+ }
+
+ // TODO find out why exe_module_sp might be NULL
+ if (error.Fail() || !exe_module_sp || !exe_module_sp->GetObjectFile()) {
+ exe_module_sp.reset();
+ error.SetErrorStringWithFormat(
+ "'%s' doesn't contain the architecture %s",
+ resolved_module_spec.GetFileSpec().GetPath().c_str(),
+ resolved_module_spec.GetArchitecture().GetArchitectureName());
+ }
+ } else {
+ // No valid architecture was specified, ask the platform for the
+ // architectures that we should be using (in the correct order) and see
+ // if we can find a match that way
+ StreamString arch_names;
+ for (uint32_t idx = 0; GetSupportedArchitectureAtIndex(
+ idx, resolved_module_spec.GetArchitecture());
+ ++idx) {
+ error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
+ module_search_paths_ptr, nullptr, nullptr);
+ // Did we find an executable using one of the
+ if (error.Success()) {
+ if (exe_module_sp && exe_module_sp->GetObjectFile())
+ break;
+ else
+ error.SetErrorToGenericError();
+ }
+
+ if (idx > 0)
+ arch_names.PutCString(", ");
+ arch_names.PutCString(
+ resolved_module_spec.GetArchitecture().GetArchitectureName());
+ }
+
+ if (error.Fail() || !exe_module_sp) {
+ if (FileSystem::Instance().Readable(
+ resolved_module_spec.GetFileSpec())) {
+ error.SetErrorStringWithFormat(
+ "'%s' doesn't contain any '%s' platform architectures: %s",
+ resolved_module_spec.GetFileSpec().GetPath().c_str(),
+ GetPluginName().GetCString(), arch_names.GetData());
+ } else {
+ error.SetErrorStringWithFormat(
+ "'%s' is not readable",
+ resolved_module_spec.GetFileSpec().GetPath().c_str());
+ }
+ }
+ }
+ }
+
+ return error;
+}
+
+static uint32_t chown_file(Platform *platform, const char *path,
+ uint32_t uid = UINT32_MAX,
+ uint32_t gid = UINT32_MAX) {
+ if (!platform || !path || *path == 0)
+ return UINT32_MAX;
+
+ if (uid == UINT32_MAX && gid == UINT32_MAX)
+ return 0; // pretend I did chown correctly - actually I just didn't care
+
+ StreamString command;
+ command.PutCString("chown ");
+ if (uid != UINT32_MAX)
+ command.Printf("%d", uid);
+ if (gid != UINT32_MAX)
+ command.Printf(":%d", gid);
+ command.Printf("%s", path);
+ int status;
+ platform->RunShellCommand(command.GetData(), nullptr, &status, nullptr,
+ nullptr, std::chrono::seconds(10));
+ return status;
+}
+
+lldb_private::Status
+PlatformPOSIX::PutFile(const lldb_private::FileSpec &source,
+ const lldb_private::FileSpec &destination, uint32_t uid,
+ uint32_t gid) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+
+ if (IsHost()) {
+ if (FileSpec::Equal(source, destination, true))
+ return Status();
+ // cp src dst
+ // chown uid:gid dst
+ std::string src_path(source.GetPath());
+ if (src_path.empty())
+ return Status("unable to get file path for source");
+ std::string dst_path(destination.GetPath());
+ if (dst_path.empty())
+ return Status("unable to get file path for destination");
+ StreamString command;
+ command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
+ int status;
+ RunShellCommand(command.GetData(), nullptr, &status, nullptr, nullptr,
+ std::chrono::seconds(10));
+ if (status != 0)
+ return Status("unable to perform copy");
+ if (uid == UINT32_MAX && gid == UINT32_MAX)
+ return Status();
+ if (chown_file(this, dst_path.c_str(), uid, gid) != 0)
+ return Status("unable to perform chown");
+ return Status();
+ } else if (m_remote_platform_sp) {
+ if (GetSupportsRSync()) {
+ std::string src_path(source.GetPath());
+ if (src_path.empty())
+ return Status("unable to get file path for source");
+ std::string dst_path(destination.GetPath());
+ if (dst_path.empty())
+ return Status("unable to get file path for destination");
+ StreamString command;
+ if (GetIgnoresRemoteHostname()) {
+ if (!GetRSyncPrefix())
+ command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
+ dst_path.c_str());
+ else
+ command.Printf("rsync %s %s %s%s", GetRSyncOpts(), src_path.c_str(),
+ GetRSyncPrefix(), dst_path.c_str());
+ } else
+ command.Printf("rsync %s %s %s:%s", GetRSyncOpts(), src_path.c_str(),
+ GetHostname(), dst_path.c_str());
+ if (log)
+ log->Printf("[PutFile] Running command: %s\n", command.GetData());
+ int retcode;
+ Host::RunShellCommand(command.GetData(), nullptr, &retcode, nullptr,
+ nullptr, std::chrono::minutes(1));
+ if (retcode == 0) {
+ // Don't chown a local file for a remote system
+ // if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
+ // return Status("unable to perform chown");
+ return Status();
+ }
+ // if we are still here rsync has failed - let's try the slow way before
+ // giving up
+ }
+ }
+ return Platform::PutFile(source, destination, uid, gid);
+}
+
+lldb_private::Status PlatformPOSIX::GetFile(
+ const lldb_private::FileSpec &source, // remote file path
+ const lldb_private::FileSpec &destination) // local file path
+{
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+
+ // Check the args, first.
+ std::string src_path(source.GetPath());
+ if (src_path.empty())
+ return Status("unable to get file path for source");
+ std::string dst_path(destination.GetPath());
+ if (dst_path.empty())
+ return Status("unable to get file path for destination");
+ if (IsHost()) {
+ if (FileSpec::Equal(source, destination, true))
+ return Status("local scenario->source and destination are the same file "
+ "path: no operation performed");
+ // cp src dst
+ StreamString cp_command;
+ cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
+ int status;
+ RunShellCommand(cp_command.GetData(), nullptr, &status, nullptr, nullptr,
+ std::chrono::seconds(10));
+ if (status != 0)
+ return Status("unable to perform copy");
+ return Status();
+ } else if (m_remote_platform_sp) {
+ if (GetSupportsRSync()) {
+ StreamString command;
+ if (GetIgnoresRemoteHostname()) {
+ if (!GetRSyncPrefix())
+ command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
+ dst_path.c_str());
+ else
+ command.Printf("rsync %s %s%s %s", GetRSyncOpts(), GetRSyncPrefix(),
+ src_path.c_str(), dst_path.c_str());
+ } else
+ command.Printf("rsync %s %s:%s %s", GetRSyncOpts(),
+ m_remote_platform_sp->GetHostname(), src_path.c_str(),
+ dst_path.c_str());
+ if (log)
+ log->Printf("[GetFile] Running command: %s\n", command.GetData());
+ int retcode;
+ Host::RunShellCommand(command.GetData(), nullptr, &retcode, nullptr,
+ nullptr, std::chrono::minutes(1));
+ if (retcode == 0)
+ return Status();
+ // If we are here, rsync has failed - let's try the slow way before
+ // giving up
+ }
+ // open src and dst
+ // read/write, read/write, read/write, ...
+ // close src
+ // close dst
+ if (log)
+ log->Printf("[GetFile] Using block by block transfer....\n");
+ Status error;
+ user_id_t fd_src = OpenFile(source, File::eOpenOptionRead,
+ lldb::eFilePermissionsFileDefault, error);
+
+ if (fd_src == UINT64_MAX)
+ return Status("unable to open source file");
+
+ uint32_t permissions = 0;
+ error = GetFilePermissions(source, permissions);
+
+ if (permissions == 0)
+ permissions = lldb::eFilePermissionsFileDefault;
+
+ user_id_t fd_dst = FileCache::GetInstance().OpenFile(
+ destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite |
+ File::eOpenOptionTruncate,
+ permissions, error);
+
+ if (fd_dst == UINT64_MAX) {
+ if (error.Success())
+ error.SetErrorString("unable to open destination file");
+ }
+
+ if (error.Success()) {
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
+ uint64_t offset = 0;
+ error.Clear();
+ while (error.Success()) {
+ const uint64_t n_read = ReadFile(fd_src, offset, buffer_sp->GetBytes(),
+ buffer_sp->GetByteSize(), error);
+ if (error.Fail())
+ break;
+ if (n_read == 0)
+ break;
+ if (FileCache::GetInstance().WriteFile(fd_dst, offset,
+ buffer_sp->GetBytes(), n_read,
+ error) != n_read) {
+ if (!error.Fail())
+ error.SetErrorString("unable to write to destination file");
+ break;
+ }
+ offset += n_read;
+ }
+ }
+ // Ignore the close error of src.
+ if (fd_src != UINT64_MAX)
+ CloseFile(fd_src, error);
+ // And close the dst file descriptot.
+ if (fd_dst != UINT64_MAX &&
+ !FileCache::GetInstance().CloseFile(fd_dst, error)) {
+ if (!error.Fail())
+ error.SetErrorString("unable to close destination file");
+ }
+ return error;
+ }
+ return Platform::GetFile(source, destination);
+}
+
+std::string PlatformPOSIX::GetPlatformSpecificConnectionInformation() {
+ StreamString stream;
+ if (GetSupportsRSync()) {
+ stream.PutCString("rsync");
+ if ((GetRSyncOpts() && *GetRSyncOpts()) ||
+ (GetRSyncPrefix() && *GetRSyncPrefix()) || GetIgnoresRemoteHostname()) {
+ stream.Printf(", options: ");
+ if (GetRSyncOpts() && *GetRSyncOpts())
+ stream.Printf("'%s' ", GetRSyncOpts());
+ stream.Printf(", prefix: ");
+ if (GetRSyncPrefix() && *GetRSyncPrefix())
+ stream.Printf("'%s' ", GetRSyncPrefix());
+ if (GetIgnoresRemoteHostname())
+ stream.Printf("ignore remote-hostname ");
+ }
+ }
+ if (GetSupportsSSH()) {
+ stream.PutCString("ssh");
+ if (GetSSHOpts() && *GetSSHOpts())
+ stream.Printf(", options: '%s' ", GetSSHOpts());
+ }
+ if (GetLocalCacheDirectory() && *GetLocalCacheDirectory())
+ stream.Printf("cache dir: %s", GetLocalCacheDirectory());
+ if (stream.GetSize())
+ return stream.GetString();
+ else
+ return "";
+}
+
+const lldb::UnixSignalsSP &PlatformPOSIX::GetRemoteUnixSignals() {
+ if (IsRemote() && m_remote_platform_sp)
+ return m_remote_platform_sp->GetRemoteUnixSignals();
+ return Platform::GetRemoteUnixSignals();
+}
+
+Status PlatformPOSIX::ConnectRemote(Args &args) {
+ Status error;
+ if (IsHost()) {
+ error.SetErrorStringWithFormat(
+ "can't connect to the host platform '%s', always connected",
+ GetPluginName().GetCString());
+ } else {
+ if (!m_remote_platform_sp)
+ m_remote_platform_sp =
+ Platform::Create(ConstString("remote-gdb-server"), error);
+
+ if (m_remote_platform_sp && error.Success())
+ error = m_remote_platform_sp->ConnectRemote(args);
+ else
+ error.SetErrorString("failed to create a 'remote-gdb-server' platform");
+
+ if (error.Fail())
+ m_remote_platform_sp.reset();
+ }
+
+ if (error.Success() && m_remote_platform_sp) {
+ if (m_option_group_platform_rsync.get() &&
+ m_option_group_platform_ssh.get() &&
+ m_option_group_platform_caching.get()) {
+ if (m_option_group_platform_rsync->m_rsync) {
+ SetSupportsRSync(true);
+ SetRSyncOpts(m_option_group_platform_rsync->m_rsync_opts.c_str());
+ SetRSyncPrefix(m_option_group_platform_rsync->m_rsync_prefix.c_str());
+ SetIgnoresRemoteHostname(
+ m_option_group_platform_rsync->m_ignores_remote_hostname);
+ }
+ if (m_option_group_platform_ssh->m_ssh) {
+ SetSupportsSSH(true);
+ SetSSHOpts(m_option_group_platform_ssh->m_ssh_opts.c_str());
+ }
+ SetLocalCacheDirectory(
+ m_option_group_platform_caching->m_cache_dir.c_str());
+ }
+ }
+
+ return error;
+}
+
+Status PlatformPOSIX::DisconnectRemote() {
+ Status error;
+
+ if (IsHost()) {
+ error.SetErrorStringWithFormat(
+ "can't disconnect from the host platform '%s', always connected",
+ GetPluginName().GetCString());
+ } else {
+ if (m_remote_platform_sp)
+ error = m_remote_platform_sp->DisconnectRemote();
+ else
+ error.SetErrorString("the platform is not currently connected");
+ }
+ return error;
+}
+
+lldb::ProcessSP PlatformPOSIX::Attach(ProcessAttachInfo &attach_info,
+ Debugger &debugger, Target *target,
+ Status &error) {
+ lldb::ProcessSP process_sp;
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+
+ if (IsHost()) {
+ if (target == nullptr) {
+ TargetSP new_target_sp;
+
+ error = debugger.GetTargetList().CreateTarget(
+ debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
+ target = new_target_sp.get();
+ if (log)
+ log->Printf("PlatformPOSIX::%s created new target", __FUNCTION__);
+ } else {
+ error.Clear();
+ if (log)
+ log->Printf("PlatformPOSIX::%s target already existed, setting target",
+ __FUNCTION__);
+ }
+
+ if (target && error.Success()) {
+ debugger.GetTargetList().SetSelectedTarget(target);
+ if (log) {
+ ModuleSP exe_module_sp = target->GetExecutableModule();
+ log->Printf("PlatformPOSIX::%s set selected target to %p %s",
+ __FUNCTION__, (void *)target,
+ exe_module_sp
+ ? exe_module_sp->GetFileSpec().GetPath().c_str()
+ : "<null>");
+ }
+
+ process_sp =
+ target->CreateProcess(attach_info.GetListenerForProcess(debugger),
+ attach_info.GetProcessPluginName(), nullptr);
+
+ if (process_sp) {
+ ListenerSP listener_sp = attach_info.GetHijackListener();
+ if (listener_sp == nullptr) {
+ listener_sp =
+ Listener::MakeListener("lldb.PlatformPOSIX.attach.hijack");
+ attach_info.SetHijackListener(listener_sp);
+ }
+ process_sp->HijackProcessEvents(listener_sp);
+ error = process_sp->Attach(attach_info);
+ }
+ }
+ } else {
+ if (m_remote_platform_sp)
+ process_sp =
+ m_remote_platform_sp->Attach(attach_info, debugger, target, error);
+ else
+ error.SetErrorString("the platform is not currently connected");
+ }
+ return process_sp;
+}
+
+lldb::ProcessSP
+PlatformPOSIX::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a new
+ // target, else use existing one
+ Status &error) {
+ ProcessSP process_sp;
+
+ if (IsHost()) {
+ // We are going to hand this process off to debugserver which will be in
+ // charge of setting the exit status. However, we still need to reap it
+ // from lldb. So, make sure we use a exit callback which does not set exit
+ // status.
+ const bool monitor_signals = false;
+ launch_info.SetMonitorProcessCallback(
+ &ProcessLaunchInfo::NoOpMonitorCallback, monitor_signals);
+ process_sp = Platform::DebugProcess(launch_info, debugger, target, error);
+ } else {
+ if (m_remote_platform_sp)
+ process_sp = m_remote_platform_sp->DebugProcess(launch_info, debugger,
+ target, error);
+ else
+ error.SetErrorString("the platform is not currently connected");
+ }
+ return process_sp;
+}
+
+void PlatformPOSIX::CalculateTrapHandlerSymbolNames() {
+ m_trap_handlers.push_back(ConstString("_sigtramp"));
+}
+
+Status PlatformPOSIX::EvaluateLibdlExpression(
+ lldb_private::Process *process, const char *expr_cstr,
+ llvm::StringRef expr_prefix, lldb::ValueObjectSP &result_valobj_sp) {
+ DynamicLoader *loader = process->GetDynamicLoader();
+ if (loader) {
+ Status error = loader->CanLoadImage();
+ if (error.Fail())
+ return error;
+ }
+
+ ThreadSP thread_sp(process->GetThreadList().GetExpressionExecutionThread());
+ if (!thread_sp)
+ return Status("Selected thread isn't valid");
+
+ StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0));
+ if (!frame_sp)
+ return Status("Frame 0 isn't valid");
+
+ ExecutionContext exe_ctx;
+ frame_sp->CalculateExecutionContext(exe_ctx);
+ EvaluateExpressionOptions expr_options;
+ expr_options.SetUnwindOnError(true);
+ expr_options.SetIgnoreBreakpoints(true);
+ expr_options.SetExecutionPolicy(eExecutionPolicyAlways);
+ expr_options.SetLanguage(eLanguageTypeC_plus_plus);
+ expr_options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
+ // don't do the work to trap them.
+ expr_options.SetTimeout(process->GetUtilityExpressionTimeout());
+
+ Status expr_error;
+ ExpressionResults result =
+ UserExpression::Evaluate(exe_ctx, expr_options, expr_cstr, expr_prefix,
+ result_valobj_sp, expr_error);
+ if (result != eExpressionCompleted)
+ return expr_error;
+
+ if (result_valobj_sp->GetError().Fail())
+ return result_valobj_sp->GetError();
+ return Status();
+}
+
+std::unique_ptr<UtilityFunction>
+PlatformPOSIX::MakeLoadImageUtilityFunction(ExecutionContext &exe_ctx,
+ Status &error) {
+ // Remember to prepend this with the prefix from
+ // GetLibdlFunctionDeclarations. The returned values are all in
+ // __lldb_dlopen_result for consistency. The wrapper returns a void * but
+ // doesn't use it because UtilityFunctions don't work with void returns at
+ // present.
+ static const char *dlopen_wrapper_code = R"(
+ struct __lldb_dlopen_result {
+ void *image_ptr;
+ const char *error_str;
+ };
+
+ extern void *memcpy(void *, const void *, size_t size);
+ extern size_t strlen(const char *);
+
+
+ void * __lldb_dlopen_wrapper (const char *name,
+ const char *path_strings,
+ char *buffer,
+ __lldb_dlopen_result *result_ptr)
+ {
+ // This is the case where the name is the full path:
+ if (!path_strings) {
+ result_ptr->image_ptr = dlopen(name, 2);
+ if (result_ptr->image_ptr)
+ result_ptr->error_str = nullptr;
+ return nullptr;
+ }
+
+ // This is the case where we have a list of paths:
+ size_t name_len = strlen(name);
+ while (path_strings && path_strings[0] != '\0') {
+ size_t path_len = strlen(path_strings);
+ memcpy((void *) buffer, (void *) path_strings, path_len);
+ buffer[path_len] = '/';
+ char *target_ptr = buffer+path_len+1;
+ memcpy((void *) target_ptr, (void *) name, name_len + 1);
+ result_ptr->image_ptr = dlopen(buffer, 2);
+ if (result_ptr->image_ptr) {
+ result_ptr->error_str = nullptr;
+ break;
+ }
+ result_ptr->error_str = dlerror();
+ path_strings = path_strings + path_len + 1;
+ }
+ return nullptr;
+ }
+ )";
+
+ static const char *dlopen_wrapper_name = "__lldb_dlopen_wrapper";
+ Process *process = exe_ctx.GetProcessSP().get();
+ // Insert the dlopen shim defines into our generic expression:
+ std::string expr(GetLibdlFunctionDeclarations(process));
+ expr.append(dlopen_wrapper_code);
+ Status utility_error;
+ DiagnosticManager diagnostics;
+
+ std::unique_ptr<UtilityFunction> dlopen_utility_func_up(process
+ ->GetTarget().GetUtilityFunctionForLanguage(expr.c_str(),
+ eLanguageTypeObjC,
+ dlopen_wrapper_name,
+ utility_error));
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not make utility"
+ "function: %s", utility_error.AsCString());
+ return nullptr;
+ }
+ if (!dlopen_utility_func_up->Install(diagnostics, exe_ctx)) {
+ error.SetErrorStringWithFormat("dlopen error: could not install utility"
+ "function: %s",
+ diagnostics.GetString().c_str());
+ return nullptr;
+ }
+
+ Value value;
+ ValueList arguments;
+ FunctionCaller *do_dlopen_function = nullptr;
+
+ // Fetch the clang types we will need:
+ ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
+
+ CompilerType clang_void_pointer_type
+ = ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+ CompilerType clang_char_pointer_type
+ = ast->GetBasicType(eBasicTypeChar).GetPointerType();
+
+ // We are passing four arguments, the basename, the list of places to look,
+ // a buffer big enough for all the path + name combos, and
+ // a pointer to the storage we've made for the result:
+ value.SetValueType(Value::eValueTypeScalar);
+ value.SetCompilerType(clang_void_pointer_type);
+ arguments.PushValue(value);
+ value.SetCompilerType(clang_char_pointer_type);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+
+ do_dlopen_function = dlopen_utility_func_up->MakeFunctionCaller(
+ clang_void_pointer_type, arguments, exe_ctx.GetThreadSP(), utility_error);
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not make function"
+ "caller: %s", utility_error.AsCString());
+ return nullptr;
+ }
+
+ do_dlopen_function = dlopen_utility_func_up->GetFunctionCaller();
+ if (!do_dlopen_function) {
+ error.SetErrorString("dlopen error: could not get function caller.");
+ return nullptr;
+ }
+
+ // We made a good utility function, so cache it in the process:
+ return dlopen_utility_func_up;
+}
+
+uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
+ const lldb_private::FileSpec &remote_file,
+ const std::vector<std::string> *paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_image) {
+ if (loaded_image)
+ loaded_image->Clear();
+
+ std::string path;
+ path = remote_file.GetPath();
+
+ ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
+ if (!thread_sp) {
+ error.SetErrorString("dlopen error: no thread available to call dlopen.");
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ DiagnosticManager diagnostics;
+
+ ExecutionContext exe_ctx;
+ thread_sp->CalculateExecutionContext(exe_ctx);
+
+ Status utility_error;
+ UtilityFunction *dlopen_utility_func;
+ ValueList arguments;
+ FunctionCaller *do_dlopen_function = nullptr;
+
+ // The UtilityFunction is held in the Process. Platforms don't track the
+ // lifespan of the Targets that use them, we can't put this in the Platform.
+ dlopen_utility_func = process->GetLoadImageUtilityFunction(
+ this, [&]() -> std::unique_ptr<UtilityFunction> {
+ return MakeLoadImageUtilityFunction(exe_ctx, error);
+ });
+ // If we couldn't make it, the error will be in error, so we can exit here.
+ if (!dlopen_utility_func)
+ return LLDB_INVALID_IMAGE_TOKEN;
+
+ do_dlopen_function = dlopen_utility_func->GetFunctionCaller();
+ if (!do_dlopen_function) {
+ error.SetErrorString("dlopen error: could not get function caller.");
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+ arguments = do_dlopen_function->GetArgumentValues();
+
+ // Now insert the path we are searching for and the result structure into the
+ // target.
+ uint32_t permissions = ePermissionsReadable|ePermissionsWritable;
+ size_t path_len = path.size() + 1;
+ lldb::addr_t path_addr = process->AllocateMemory(path_len,
+ permissions,
+ utility_error);
+ if (path_addr == LLDB_INVALID_ADDRESS) {
+ error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+ "for path: %s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make sure we deallocate the input string memory:
+ CleanUp path_cleanup([process, path_addr] {
+ process->DeallocateMemory(path_addr);
+ });
+
+ process->WriteMemory(path_addr, path.c_str(), path_len, utility_error);
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not write path string:"
+ " %s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make space for our return structure. It is two pointers big: the token
+ // and the error string.
+ const uint32_t addr_size = process->GetAddressByteSize();
+ lldb::addr_t return_addr = process->CallocateMemory(2*addr_size,
+ permissions,
+ utility_error);
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+ "for path: %s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make sure we deallocate the result structure memory
+ CleanUp return_cleanup([process, return_addr] {
+ process->DeallocateMemory(return_addr);
+ });
+
+ // This will be the address of the storage for paths, if we are using them,
+ // or nullptr to signal we aren't.
+ lldb::addr_t path_array_addr = 0x0;
+ llvm::Optional<CleanUp> path_array_cleanup;
+
+ // This is the address to a buffer large enough to hold the largest path
+ // conjoined with the library name we're passing in. This is a convenience
+ // to avoid having to call malloc in the dlopen function.
+ lldb::addr_t buffer_addr = 0x0;
+ llvm::Optional<CleanUp> buffer_cleanup;
+
+ // Set the values into our args and write them to the target:
+ if (paths != nullptr) {
+ // First insert the paths into the target. This is expected to be a
+ // continuous buffer with the strings laid out null terminated and
+ // end to end with an empty string terminating the buffer.
+ // We also compute the buffer's required size as we go.
+ size_t buffer_size = 0;
+ std::string path_array;
+ for (auto path : *paths) {
+ // Don't insert empty paths, they will make us abort the path
+ // search prematurely.
+ if (path.empty())
+ continue;
+ size_t path_size = path.size();
+ path_array.append(path);
+ path_array.push_back('\0');
+ if (path_size > buffer_size)
+ buffer_size = path_size;
+ }
+ path_array.push_back('\0');
+
+ path_array_addr = process->AllocateMemory(path_array.size(),
+ permissions,
+ utility_error);
+ if (path_array_addr == LLDB_INVALID_ADDRESS) {
+ error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+ "for path array: %s",
+ utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make sure we deallocate the paths array.
+ path_array_cleanup.emplace([process, path_array_addr] {
+ process->DeallocateMemory(path_array_addr);
+ });
+
+ process->WriteMemory(path_array_addr, path_array.data(),
+ path_array.size(), utility_error);
+
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not write path array:"
+ " %s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+ // Now make spaces in the target for the buffer. We need to add one for
+ // the '/' that the utility function will insert and one for the '\0':
+ buffer_size += path.size() + 2;
+
+ buffer_addr = process->AllocateMemory(buffer_size,
+ permissions,
+ utility_error);
+ if (buffer_addr == LLDB_INVALID_ADDRESS) {
+ error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+ "for buffer: %s",
+ utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make sure we deallocate the buffer memory:
+ buffer_cleanup.emplace([process, buffer_addr] {
+ process->DeallocateMemory(buffer_addr);
+ });
+ }
+
+ arguments.GetValueAtIndex(0)->GetScalar() = path_addr;
+ arguments.GetValueAtIndex(1)->GetScalar() = path_array_addr;
+ arguments.GetValueAtIndex(2)->GetScalar() = buffer_addr;
+ arguments.GetValueAtIndex(3)->GetScalar() = return_addr;
+
+ lldb::addr_t func_args_addr = LLDB_INVALID_ADDRESS;
+
+ diagnostics.Clear();
+ if (!do_dlopen_function->WriteFunctionArguments(exe_ctx,
+ func_args_addr,
+ arguments,
+ diagnostics)) {
+ error.SetErrorStringWithFormat("dlopen error: could not write function "
+ "arguments: %s",
+ diagnostics.GetString().c_str());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make sure we clean up the args structure. We can't reuse it because the
+ // Platform lives longer than the process and the Platforms don't get a
+ // signal to clean up cached data when a process goes away.
+ CleanUp args_cleanup([do_dlopen_function, &exe_ctx, func_args_addr] {
+ do_dlopen_function->DeallocateFunctionResults(exe_ctx, func_args_addr);
+ });
+
+ // Now run the caller:
+ EvaluateExpressionOptions options;
+ options.SetExecutionPolicy(eExecutionPolicyAlways);
+ options.SetLanguage(eLanguageTypeC_plus_plus);
+ options.SetIgnoreBreakpoints(true);
+ options.SetUnwindOnError(true);
+ options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
+ // don't do the work to trap them.
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetIsForUtilityExpr(true);
+
+ Value return_value;
+ // Fetch the clang types we will need:
+ ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
+
+ CompilerType clang_void_pointer_type
+ = ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+ return_value.SetCompilerType(clang_void_pointer_type);
+
+ ExpressionResults results = do_dlopen_function->ExecuteFunction(
+ exe_ctx, &func_args_addr, options, diagnostics, return_value);
+ if (results != eExpressionCompleted) {
+ error.SetErrorStringWithFormat("dlopen error: failed executing "
+ "dlopen wrapper function: %s",
+ diagnostics.GetString().c_str());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Read the dlopen token from the return area:
+ lldb::addr_t token = process->ReadPointerFromMemory(return_addr,
+ utility_error);
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not read the return "
+ "struct: %s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // The dlopen succeeded!
+ if (token != 0x0) {
+ if (loaded_image && buffer_addr != 0x0)
+ {
+ // Capture the image which was loaded. We leave it in the buffer on
+ // exit from the dlopen function, so we can just read it from there:
+ std::string name_string;
+ process->ReadCStringFromMemory(buffer_addr, name_string, utility_error);
+ if (utility_error.Success())
+ loaded_image->SetFile(name_string, llvm::sys::path::Style::posix);
+ }
+ return process->AddImageToken(token);
+ }
+
+ // We got an error, lets read in the error string:
+ std::string dlopen_error_str;
+ lldb::addr_t error_addr
+ = process->ReadPointerFromMemory(return_addr + addr_size, utility_error);
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not read error string: "
+ "%s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ size_t num_chars = process->ReadCStringFromMemory(error_addr + addr_size,
+ dlopen_error_str,
+ utility_error);
+ if (utility_error.Success() && num_chars > 0)
+ error.SetErrorStringWithFormat("dlopen error: %s",
+ dlopen_error_str.c_str());
+ else
+ error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
+
+ return LLDB_INVALID_IMAGE_TOKEN;
+}
+
+Status PlatformPOSIX::UnloadImage(lldb_private::Process *process,
+ uint32_t image_token) {
+ const addr_t image_addr = process->GetImagePtrFromToken(image_token);
+ if (image_addr == LLDB_INVALID_ADDRESS)
+ return Status("Invalid image token");
+
+ StreamString expr;
+ expr.Printf("dlclose((void *)0x%" PRIx64 ")", image_addr);
+ llvm::StringRef prefix = GetLibdlFunctionDeclarations(process);
+ lldb::ValueObjectSP result_valobj_sp;
+ Status error = EvaluateLibdlExpression(process, expr.GetData(), prefix,
+ result_valobj_sp);
+ if (error.Fail())
+ return error;
+
+ if (result_valobj_sp->GetError().Fail())
+ return result_valobj_sp->GetError();
+
+ Scalar scalar;
+ if (result_valobj_sp->ResolveValue(scalar)) {
+ if (scalar.UInt(1))
+ return Status("expression failed: \"%s\"", expr.GetData());
+ process->ResetImageToken(image_token);
+ }
+ return Status();
+}
+
+llvm::StringRef
+PlatformPOSIX::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
+ return R"(
+ extern "C" void* dlopen(const char*, int);
+ extern "C" void* dlsym(void*, const char*);
+ extern "C" int dlclose(void*);
+ extern "C" char* dlerror(void);
+ )";
+}
+
+size_t PlatformPOSIX::ConnectToWaitingProcesses(Debugger &debugger,
+ Status &error) {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->ConnectToWaitingProcesses(debugger, error);
+ return Platform::ConnectToWaitingProcesses(debugger, error);
+}
+
+ConstString PlatformPOSIX::GetFullNameForDylib(ConstString basename) {
+ if (basename.IsEmpty())
+ return basename;
+
+ StreamString stream;
+ stream.Printf("lib%s.so", basename.GetCString());
+ return ConstString(stream.GetString());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h
new file mode 100644
index 000000000000..5858f99088e6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h
@@ -0,0 +1,111 @@
+//===-- PlatformPOSIX.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_PlatformPOSIX_h_
+#define liblldb_PlatformPOSIX_h_
+
+#include <map>
+#include <memory>
+
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/RemoteAwarePlatform.h"
+
+class PlatformPOSIX : public lldb_private::RemoteAwarePlatform {
+public:
+ PlatformPOSIX(bool is_host);
+
+ ~PlatformPOSIX() override;
+
+ // lldb_private::Platform functions
+
+ lldb_private::OptionGroupOptions *
+ GetConnectionOptions(lldb_private::CommandInterpreter &interpreter) override;
+
+ lldb_private::Status PutFile(const lldb_private::FileSpec &source,
+ const lldb_private::FileSpec &destination,
+ uint32_t uid = UINT32_MAX,
+ uint32_t gid = UINT32_MAX) override;
+
+ lldb_private::Status
+ GetFile(const lldb_private::FileSpec &source,
+ const lldb_private::FileSpec &destination) override;
+
+ const lldb::UnixSignalsSP &GetRemoteUnixSignals() override;
+
+ lldb_private::Status ResolveExecutable(
+ const lldb_private::ModuleSpec &module_spec, lldb::ModuleSP &module_sp,
+ const lldb_private::FileSpecList *module_search_paths_ptr) override;
+
+ lldb::ProcessSP Attach(lldb_private::ProcessAttachInfo &attach_info,
+ lldb_private::Debugger &debugger,
+ lldb_private::Target *target, // Can be nullptr, if
+ // nullptr create a new
+ // target, else use
+ // existing one
+ lldb_private::Status &error) override;
+
+ lldb::ProcessSP DebugProcess(lldb_private::ProcessLaunchInfo &launch_info,
+ lldb_private::Debugger &debugger,
+ lldb_private::Target *target, // Can be nullptr,
+ // if nullptr
+ // create a new
+ // target, else use
+ // existing one
+ lldb_private::Status &error) override;
+
+ std::string GetPlatformSpecificConnectionInformation() override;
+
+ void CalculateTrapHandlerSymbolNames() override;
+
+ lldb_private::Status ConnectRemote(lldb_private::Args &args) override;
+
+ lldb_private::Status DisconnectRemote() override;
+
+ uint32_t DoLoadImage(lldb_private::Process *process,
+ const lldb_private::FileSpec &remote_file,
+ const std::vector<std::string> *paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_image) override;
+
+ lldb_private::Status UnloadImage(lldb_private::Process *process,
+ uint32_t image_token) override;
+
+ size_t ConnectToWaitingProcesses(lldb_private::Debugger &debugger,
+ lldb_private::Status &error) override;
+
+ lldb_private::ConstString GetFullNameForDylib(lldb_private::ConstString basename) override;
+
+protected:
+ std::unique_ptr<lldb_private::OptionGroupPlatformRSync>
+ m_option_group_platform_rsync;
+ std::unique_ptr<lldb_private::OptionGroupPlatformSSH>
+ m_option_group_platform_ssh;
+ std::unique_ptr<lldb_private::OptionGroupPlatformCaching>
+ m_option_group_platform_caching;
+
+ std::map<lldb_private::CommandInterpreter *,
+ std::unique_ptr<lldb_private::OptionGroupOptions>>
+ m_options;
+
+ lldb_private::Status
+ EvaluateLibdlExpression(lldb_private::Process *process, const char *expr_cstr,
+ llvm::StringRef expr_prefix,
+ lldb::ValueObjectSP &result_valobj_sp);
+
+ std::unique_ptr<lldb_private::UtilityFunction>
+ MakeLoadImageUtilityFunction(lldb_private::ExecutionContext &exe_ctx,
+ lldb_private::Status &error);
+
+ virtual
+ llvm::StringRef GetLibdlFunctionDeclarations(lldb_private::Process *process);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(PlatformPOSIX);
+};
+
+#endif // liblldb_PlatformPOSIX_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
new file mode 100644
index 000000000000..9c52b59e2b06
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
@@ -0,0 +1,858 @@
+//===-- PlatformRemoteGDBServer.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PlatformRemoteGDBServer.h"
+#include "lldb/Host/Config.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/UriParser.h"
+
+#include "Plugins/Process/Utility/GDBRemoteSignals.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::platform_gdb_server;
+
+static bool g_initialized = false;
+
+void PlatformRemoteGDBServer::Initialize() {
+ Platform::Initialize();
+
+ if (!g_initialized) {
+ g_initialized = true;
+ PluginManager::RegisterPlugin(
+ PlatformRemoteGDBServer::GetPluginNameStatic(),
+ PlatformRemoteGDBServer::GetDescriptionStatic(),
+ PlatformRemoteGDBServer::CreateInstance);
+ }
+}
+
+void PlatformRemoteGDBServer::Terminate() {
+ if (g_initialized) {
+ g_initialized = false;
+ PluginManager::UnregisterPlugin(PlatformRemoteGDBServer::CreateInstance);
+ }
+
+ Platform::Terminate();
+}
+
+PlatformSP PlatformRemoteGDBServer::CreateInstance(bool force,
+ const ArchSpec *arch) {
+ bool create = force;
+ if (!create) {
+ create = !arch->TripleVendorWasSpecified() && !arch->TripleOSWasSpecified();
+ }
+ if (create)
+ return PlatformSP(new PlatformRemoteGDBServer());
+ return PlatformSP();
+}
+
+ConstString PlatformRemoteGDBServer::GetPluginNameStatic() {
+ static ConstString g_name("remote-gdb-server");
+ return g_name;
+}
+
+const char *PlatformRemoteGDBServer::GetDescriptionStatic() {
+ return "A platform that uses the GDB remote protocol as the communication "
+ "transport.";
+}
+
+const char *PlatformRemoteGDBServer::GetDescription() {
+ if (m_platform_description.empty()) {
+ if (IsConnected()) {
+ // Send the get description packet
+ }
+ }
+
+ if (!m_platform_description.empty())
+ return m_platform_description.c_str();
+ return GetDescriptionStatic();
+}
+
+Status PlatformRemoteGDBServer::ResolveExecutable(
+ const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp,
+ const FileSpecList *module_search_paths_ptr) {
+ // copied from PlatformRemoteiOS
+
+ Status error;
+ // Nothing special to do here, just use the actual file and architecture
+
+ ModuleSpec resolved_module_spec(module_spec);
+
+ // Resolve any executable within an apk on Android?
+ // Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec());
+
+ if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()) ||
+ module_spec.GetUUID().IsValid()) {
+ if (resolved_module_spec.GetArchitecture().IsValid() ||
+ resolved_module_spec.GetUUID().IsValid()) {
+ error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
+ module_search_paths_ptr, nullptr,
+ nullptr);
+
+ if (exe_module_sp && exe_module_sp->GetObjectFile())
+ return error;
+ exe_module_sp.reset();
+ }
+ // No valid architecture was specified or the exact arch wasn't found so
+ // ask the platform for the architectures that we should be using (in the
+ // correct order) and see if we can find a match that way
+ StreamString arch_names;
+ for (uint32_t idx = 0; GetSupportedArchitectureAtIndex(
+ idx, resolved_module_spec.GetArchitecture());
+ ++idx) {
+ error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
+ module_search_paths_ptr, nullptr,
+ nullptr);
+ // Did we find an executable using one of the
+ if (error.Success()) {
+ if (exe_module_sp && exe_module_sp->GetObjectFile())
+ break;
+ else
+ error.SetErrorToGenericError();
+ }
+
+ if (idx > 0)
+ arch_names.PutCString(", ");
+ arch_names.PutCString(
+ resolved_module_spec.GetArchitecture().GetArchitectureName());
+ }
+
+ if (error.Fail() || !exe_module_sp) {
+ if (FileSystem::Instance().Readable(resolved_module_spec.GetFileSpec())) {
+ error.SetErrorStringWithFormat(
+ "'%s' doesn't contain any '%s' platform architectures: %s",
+ resolved_module_spec.GetFileSpec().GetPath().c_str(),
+ GetPluginName().GetCString(), arch_names.GetData());
+ } else {
+ error.SetErrorStringWithFormat(
+ "'%s' is not readable",
+ resolved_module_spec.GetFileSpec().GetPath().c_str());
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "'%s' does not exist",
+ resolved_module_spec.GetFileSpec().GetPath().c_str());
+ }
+
+ return error;
+}
+
+bool PlatformRemoteGDBServer::GetModuleSpec(const FileSpec &module_file_spec,
+ const ArchSpec &arch,
+ ModuleSpec &module_spec) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+
+ const auto module_path = module_file_spec.GetPath(false);
+
+ if (!m_gdb_client.GetModuleInfo(module_file_spec, arch, module_spec)) {
+ if (log)
+ log->Printf(
+ "PlatformRemoteGDBServer::%s - failed to get module info for %s:%s",
+ __FUNCTION__, module_path.c_str(),
+ arch.GetTriple().getTriple().c_str());
+ return false;
+ }
+
+ if (log) {
+ StreamString stream;
+ module_spec.Dump(stream);
+ log->Printf(
+ "PlatformRemoteGDBServer::%s - got module info for (%s:%s) : %s",
+ __FUNCTION__, module_path.c_str(), arch.GetTriple().getTriple().c_str(),
+ stream.GetData());
+ }
+
+ return true;
+}
+
+Status PlatformRemoteGDBServer::GetFileWithUUID(const FileSpec &platform_file,
+ const UUID *uuid_ptr,
+ FileSpec &local_file) {
+ // Default to the local case
+ local_file = platform_file;
+ return Status();
+}
+
+/// Default Constructor
+PlatformRemoteGDBServer::PlatformRemoteGDBServer()
+ : Platform(false), // This is a remote platform
+ m_gdb_client() {}
+
+/// Destructor.
+///
+/// The destructor is virtual since this class is designed to be
+/// inherited from by the plug-in instance.
+PlatformRemoteGDBServer::~PlatformRemoteGDBServer() {}
+
+bool PlatformRemoteGDBServer::GetSupportedArchitectureAtIndex(uint32_t idx,
+ ArchSpec &arch) {
+ ArchSpec remote_arch = m_gdb_client.GetSystemArchitecture();
+
+ if (idx == 0) {
+ arch = remote_arch;
+ return arch.IsValid();
+ } else if (idx == 1 && remote_arch.IsValid() &&
+ remote_arch.GetTriple().isArch64Bit()) {
+ arch.SetTriple(remote_arch.GetTriple().get32BitArchVariant());
+ return arch.IsValid();
+ }
+ return false;
+}
+
+size_t PlatformRemoteGDBServer::GetSoftwareBreakpointTrapOpcode(
+ Target &target, BreakpointSite *bp_site) {
+ // This isn't needed if the z/Z packets are supported in the GDB remote
+ // server. But we might need a packet to detect this.
+ return 0;
+}
+
+bool PlatformRemoteGDBServer::GetRemoteOSVersion() {
+ m_os_version = m_gdb_client.GetOSVersion();
+ return !m_os_version.empty();
+}
+
+bool PlatformRemoteGDBServer::GetRemoteOSBuildString(std::string &s) {
+ return m_gdb_client.GetOSBuildString(s);
+}
+
+bool PlatformRemoteGDBServer::GetRemoteOSKernelDescription(std::string &s) {
+ return m_gdb_client.GetOSKernelDescription(s);
+}
+
+// Remote Platform subclasses need to override this function
+ArchSpec PlatformRemoteGDBServer::GetRemoteSystemArchitecture() {
+ return m_gdb_client.GetSystemArchitecture();
+}
+
+FileSpec PlatformRemoteGDBServer::GetRemoteWorkingDirectory() {
+ if (IsConnected()) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ FileSpec working_dir;
+ if (m_gdb_client.GetWorkingDir(working_dir) && log)
+ log->Printf(
+ "PlatformRemoteGDBServer::GetRemoteWorkingDirectory() -> '%s'",
+ working_dir.GetCString());
+ return working_dir;
+ } else {
+ return Platform::GetRemoteWorkingDirectory();
+ }
+}
+
+bool PlatformRemoteGDBServer::SetRemoteWorkingDirectory(
+ const FileSpec &working_dir) {
+ if (IsConnected()) {
+ // Clear the working directory it case it doesn't get set correctly. This
+ // will for use to re-read it
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::SetRemoteWorkingDirectory('%s')",
+ working_dir.GetCString());
+ return m_gdb_client.SetWorkingDir(working_dir) == 0;
+ } else
+ return Platform::SetRemoteWorkingDirectory(working_dir);
+}
+
+bool PlatformRemoteGDBServer::IsConnected() const {
+ return m_gdb_client.IsConnected();
+}
+
+Status PlatformRemoteGDBServer::ConnectRemote(Args &args) {
+ Status error;
+ if (IsConnected()) {
+ error.SetErrorStringWithFormat("the platform is already connected to '%s', "
+ "execute 'platform disconnect' to close the "
+ "current connection",
+ GetHostname());
+ } else {
+ if (args.GetArgumentCount() == 1) {
+ m_gdb_client.SetConnection(new ConnectionFileDescriptor());
+ // we're going to reuse the hostname when we connect to the debugserver
+ int port;
+ std::string path;
+ const char *url = args.GetArgumentAtIndex(0);
+ if (!url)
+ return Status("URL is null.");
+ llvm::StringRef scheme, hostname, pathname;
+ if (!UriParser::Parse(url, scheme, hostname, port, pathname))
+ return Status("Invalid URL: %s", url);
+ m_platform_scheme = scheme;
+ m_platform_hostname = hostname;
+ path = pathname;
+
+ const ConnectionStatus status = m_gdb_client.Connect(url, &error);
+ if (status == eConnectionStatusSuccess) {
+ if (m_gdb_client.HandshakeWithServer(&error)) {
+ m_gdb_client.GetHostInfo();
+ // If a working directory was set prior to connecting, send it down
+ // now
+ if (m_working_dir)
+ m_gdb_client.SetWorkingDir(m_working_dir);
+ } else {
+ m_gdb_client.Disconnect();
+ if (error.Success())
+ error.SetErrorString("handshake failed");
+ }
+ }
+ } else {
+ error.SetErrorString(
+ "\"platform connect\" takes a single argument: <connect-url>");
+ }
+ }
+ return error;
+}
+
+Status PlatformRemoteGDBServer::DisconnectRemote() {
+ Status error;
+ m_gdb_client.Disconnect(&error);
+ m_remote_signals_sp.reset();
+ return error;
+}
+
+const char *PlatformRemoteGDBServer::GetHostname() {
+ m_gdb_client.GetHostname(m_name);
+ if (m_name.empty())
+ return nullptr;
+ return m_name.c_str();
+}
+
+llvm::Optional<std::string>
+PlatformRemoteGDBServer::DoGetUserName(UserIDResolver::id_t uid) {
+ std::string name;
+ if (m_gdb_client.GetUserName(uid, name))
+ return std::move(name);
+ return llvm::None;
+}
+
+llvm::Optional<std::string>
+PlatformRemoteGDBServer::DoGetGroupName(UserIDResolver::id_t gid) {
+ std::string name;
+ if (m_gdb_client.GetGroupName(gid, name))
+ return std::move(name);
+ return llvm::None;
+}
+
+uint32_t PlatformRemoteGDBServer::FindProcesses(
+ const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ return m_gdb_client.FindProcesses(match_info, process_infos);
+}
+
+bool PlatformRemoteGDBServer::GetProcessInfo(
+ lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ return m_gdb_client.GetProcessInfo(pid, process_info);
+}
+
+Status PlatformRemoteGDBServer::LaunchProcess(ProcessLaunchInfo &launch_info) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ Status error;
+
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::%s() called", __FUNCTION__);
+
+ auto num_file_actions = launch_info.GetNumFileActions();
+ for (decltype(num_file_actions) i = 0; i < num_file_actions; ++i) {
+ const auto file_action = launch_info.GetFileActionAtIndex(i);
+ if (file_action->GetAction() != FileAction::eFileActionOpen)
+ continue;
+ switch (file_action->GetFD()) {
+ case STDIN_FILENO:
+ m_gdb_client.SetSTDIN(file_action->GetFileSpec());
+ break;
+ case STDOUT_FILENO:
+ m_gdb_client.SetSTDOUT(file_action->GetFileSpec());
+ break;
+ case STDERR_FILENO:
+ m_gdb_client.SetSTDERR(file_action->GetFileSpec());
+ break;
+ }
+ }
+
+ m_gdb_client.SetDisableASLR(
+ launch_info.GetFlags().Test(eLaunchFlagDisableASLR));
+ m_gdb_client.SetDetachOnError(
+ launch_info.GetFlags().Test(eLaunchFlagDetachOnError));
+
+ FileSpec working_dir = launch_info.GetWorkingDirectory();
+ if (working_dir) {
+ m_gdb_client.SetWorkingDir(working_dir);
+ }
+
+ // Send the environment and the program + arguments after we connect
+ m_gdb_client.SendEnvironment(launch_info.GetEnvironment());
+
+ ArchSpec arch_spec = launch_info.GetArchitecture();
+ const char *arch_triple = arch_spec.GetTriple().str().c_str();
+
+ m_gdb_client.SendLaunchArchPacket(arch_triple);
+ if (log)
+ log->Printf(
+ "PlatformRemoteGDBServer::%s() set launch architecture triple to '%s'",
+ __FUNCTION__, arch_triple ? arch_triple : "<NULL>");
+
+ int arg_packet_err;
+ {
+ // Scope for the scoped timeout object
+ process_gdb_remote::GDBRemoteCommunication::ScopedTimeout timeout(
+ m_gdb_client, std::chrono::seconds(5));
+ arg_packet_err = m_gdb_client.SendArgumentsPacket(launch_info);
+ }
+
+ if (arg_packet_err == 0) {
+ std::string error_str;
+ if (m_gdb_client.GetLaunchSuccess(error_str)) {
+ const auto pid = m_gdb_client.GetCurrentProcessID(false);
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ launch_info.SetProcessID(pid);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::%s() pid %" PRIu64
+ " launched successfully",
+ __FUNCTION__, pid);
+ } else {
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::%s() launch succeeded but we "
+ "didn't get a valid process id back!",
+ __FUNCTION__);
+ error.SetErrorString("failed to get PID");
+ }
+ } else {
+ error.SetErrorString(error_str.c_str());
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::%s() launch failed: %s",
+ __FUNCTION__, error.AsCString());
+ }
+ } else {
+ error.SetErrorStringWithFormat("'A' packet returned an error: %i",
+ arg_packet_err);
+ }
+ return error;
+}
+
+Status PlatformRemoteGDBServer::KillProcess(const lldb::pid_t pid) {
+ if (!KillSpawnedProcess(pid))
+ return Status("failed to kill remote spawned process");
+ return Status();
+}
+
+lldb::ProcessSP PlatformRemoteGDBServer::DebugProcess(
+ ProcessLaunchInfo &launch_info, Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a new target, else use
+ // existing one
+ Status &error) {
+ lldb::ProcessSP process_sp;
+ if (IsRemote()) {
+ if (IsConnected()) {
+ lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ std::string connect_url;
+ if (!LaunchGDBServer(debugserver_pid, connect_url)) {
+ error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'",
+ GetHostname());
+ } else {
+ if (target == nullptr) {
+ TargetSP new_target_sp;
+
+ error = debugger.GetTargetList().CreateTarget(
+ debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
+ target = new_target_sp.get();
+ } else
+ error.Clear();
+
+ if (target && error.Success()) {
+ debugger.GetTargetList().SetSelectedTarget(target);
+
+ // The darwin always currently uses the GDB remote debugger plug-in
+ // so even when debugging locally we are debugging remotely!
+ process_sp = target->CreateProcess(launch_info.GetListener(),
+ "gdb-remote", nullptr);
+
+ if (process_sp) {
+ error = process_sp->ConnectRemote(nullptr, connect_url.c_str());
+ // Retry the connect remote one time...
+ if (error.Fail())
+ error = process_sp->ConnectRemote(nullptr, connect_url.c_str());
+ if (error.Success())
+ error = process_sp->Launch(launch_info);
+ else if (debugserver_pid != LLDB_INVALID_PROCESS_ID) {
+ printf("error: connect remote failed (%s)\n", error.AsCString());
+ KillSpawnedProcess(debugserver_pid);
+ }
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("not connected to remote gdb server");
+ }
+ }
+ return process_sp;
+}
+
+bool PlatformRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid,
+ std::string &connect_url) {
+ ArchSpec remote_arch = GetRemoteSystemArchitecture();
+ llvm::Triple &remote_triple = remote_arch.GetTriple();
+
+ uint16_t port = 0;
+ std::string socket_name;
+ bool launch_result = false;
+ if (remote_triple.getVendor() == llvm::Triple::Apple &&
+ remote_triple.getOS() == llvm::Triple::IOS) {
+ // When remote debugging to iOS, we use a USB mux that always talks to
+ // localhost, so we will need the remote debugserver to accept connections
+ // only from localhost, no matter what our current hostname is
+ launch_result =
+ m_gdb_client.LaunchGDBServer("127.0.0.1", pid, port, socket_name);
+ } else {
+ // All other hosts should use their actual hostname
+ launch_result =
+ m_gdb_client.LaunchGDBServer(nullptr, pid, port, socket_name);
+ }
+
+ if (!launch_result)
+ return false;
+
+ connect_url =
+ MakeGdbServerUrl(m_platform_scheme, m_platform_hostname, port,
+ (socket_name.empty()) ? nullptr : socket_name.c_str());
+ return true;
+}
+
+bool PlatformRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) {
+ return m_gdb_client.KillSpawnedProcess(pid);
+}
+
+lldb::ProcessSP PlatformRemoteGDBServer::Attach(
+ ProcessAttachInfo &attach_info, Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a new target, else use
+ // existing one
+ Status &error) {
+ lldb::ProcessSP process_sp;
+ if (IsRemote()) {
+ if (IsConnected()) {
+ lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ std::string connect_url;
+ if (!LaunchGDBServer(debugserver_pid, connect_url)) {
+ error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'",
+ GetHostname());
+ } else {
+ if (target == nullptr) {
+ TargetSP new_target_sp;
+
+ error = debugger.GetTargetList().CreateTarget(
+ debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
+ target = new_target_sp.get();
+ } else
+ error.Clear();
+
+ if (target && error.Success()) {
+ debugger.GetTargetList().SetSelectedTarget(target);
+
+ // The darwin always currently uses the GDB remote debugger plug-in
+ // so even when debugging locally we are debugging remotely!
+ process_sp =
+ target->CreateProcess(attach_info.GetListenerForProcess(debugger),
+ "gdb-remote", nullptr);
+ if (process_sp) {
+ error = process_sp->ConnectRemote(nullptr, connect_url.c_str());
+ if (error.Success()) {
+ ListenerSP listener_sp = attach_info.GetHijackListener();
+ if (listener_sp)
+ process_sp->HijackProcessEvents(listener_sp);
+ error = process_sp->Attach(attach_info);
+ }
+
+ if (error.Fail() && debugserver_pid != LLDB_INVALID_PROCESS_ID) {
+ KillSpawnedProcess(debugserver_pid);
+ }
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("not connected to remote gdb server");
+ }
+ }
+ return process_sp;
+}
+
+Status PlatformRemoteGDBServer::MakeDirectory(const FileSpec &file_spec,
+ uint32_t mode) {
+ Status error = m_gdb_client.MakeDirectory(file_spec, mode);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::MakeDirectory(path='%s', mode=%o) "
+ "error = %u (%s)",
+ file_spec.GetCString(), mode, error.GetError(),
+ error.AsCString());
+ return error;
+}
+
+Status PlatformRemoteGDBServer::GetFilePermissions(const FileSpec &file_spec,
+ uint32_t &file_permissions) {
+ Status error = m_gdb_client.GetFilePermissions(file_spec, file_permissions);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::GetFilePermissions(path='%s', "
+ "file_permissions=%o) error = %u (%s)",
+ file_spec.GetCString(), file_permissions, error.GetError(),
+ error.AsCString());
+ return error;
+}
+
+Status PlatformRemoteGDBServer::SetFilePermissions(const FileSpec &file_spec,
+ uint32_t file_permissions) {
+ Status error = m_gdb_client.SetFilePermissions(file_spec, file_permissions);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::SetFilePermissions(path='%s', "
+ "file_permissions=%o) error = %u (%s)",
+ file_spec.GetCString(), file_permissions, error.GetError(),
+ error.AsCString());
+ return error;
+}
+
+lldb::user_id_t PlatformRemoteGDBServer::OpenFile(const FileSpec &file_spec,
+ uint32_t flags, uint32_t mode,
+ Status &error) {
+ return m_gdb_client.OpenFile(file_spec, flags, mode, error);
+}
+
+bool PlatformRemoteGDBServer::CloseFile(lldb::user_id_t fd, Status &error) {
+ return m_gdb_client.CloseFile(fd, error);
+}
+
+lldb::user_id_t
+PlatformRemoteGDBServer::GetFileSize(const FileSpec &file_spec) {
+ return m_gdb_client.GetFileSize(file_spec);
+}
+
+uint64_t PlatformRemoteGDBServer::ReadFile(lldb::user_id_t fd, uint64_t offset,
+ void *dst, uint64_t dst_len,
+ Status &error) {
+ return m_gdb_client.ReadFile(fd, offset, dst, dst_len, error);
+}
+
+uint64_t PlatformRemoteGDBServer::WriteFile(lldb::user_id_t fd, uint64_t offset,
+ const void *src, uint64_t src_len,
+ Status &error) {
+ return m_gdb_client.WriteFile(fd, offset, src, src_len, error);
+}
+
+Status PlatformRemoteGDBServer::PutFile(const FileSpec &source,
+ const FileSpec &destination,
+ uint32_t uid, uint32_t gid) {
+ return Platform::PutFile(source, destination, uid, gid);
+}
+
+Status PlatformRemoteGDBServer::CreateSymlink(
+ const FileSpec &src, // The name of the link is in src
+ const FileSpec &dst) // The symlink points to dst
+{
+ Status error = m_gdb_client.CreateSymlink(src, dst);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::CreateSymlink(src='%s', dst='%s') "
+ "error = %u (%s)",
+ src.GetCString(), dst.GetCString(), error.GetError(),
+ error.AsCString());
+ return error;
+}
+
+Status PlatformRemoteGDBServer::Unlink(const FileSpec &file_spec) {
+ Status error = m_gdb_client.Unlink(file_spec);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::Unlink(path='%s') error = %u (%s)",
+ file_spec.GetCString(), error.GetError(), error.AsCString());
+ return error;
+}
+
+bool PlatformRemoteGDBServer::GetFileExists(const FileSpec &file_spec) {
+ return m_gdb_client.GetFileExists(file_spec);
+}
+
+Status PlatformRemoteGDBServer::RunShellCommand(
+ const char *command, // Shouldn't be NULL
+ const FileSpec &
+ working_dir, // Pass empty FileSpec to use the current working directory
+ int *status_ptr, // Pass NULL if you don't want the process exit status
+ int *signo_ptr, // Pass NULL if you don't want the signal that caused the
+ // process to exit
+ std::string
+ *command_output, // Pass NULL if you don't want the command output
+ const Timeout<std::micro> &timeout) {
+ return m_gdb_client.RunShellCommand(command, working_dir, status_ptr,
+ signo_ptr, command_output, timeout);
+}
+
+void PlatformRemoteGDBServer::CalculateTrapHandlerSymbolNames() {
+ m_trap_handlers.push_back(ConstString("_sigtramp"));
+}
+
+const UnixSignalsSP &PlatformRemoteGDBServer::GetRemoteUnixSignals() {
+ if (!IsConnected())
+ return Platform::GetRemoteUnixSignals();
+
+ if (m_remote_signals_sp)
+ return m_remote_signals_sp;
+
+ // If packet not implemented or JSON failed to parse, we'll guess the signal
+ // set based on the remote architecture.
+ m_remote_signals_sp = UnixSignals::Create(GetRemoteSystemArchitecture());
+
+ StringExtractorGDBRemote response;
+ auto result = m_gdb_client.SendPacketAndWaitForResponse("jSignalsInfo",
+ response, false);
+
+ if (result != decltype(result)::Success ||
+ response.GetResponseType() != response.eResponse)
+ return m_remote_signals_sp;
+
+ auto object_sp = StructuredData::ParseJSON(response.GetStringRef());
+ if (!object_sp || !object_sp->IsValid())
+ return m_remote_signals_sp;
+
+ auto array_sp = object_sp->GetAsArray();
+ if (!array_sp || !array_sp->IsValid())
+ return m_remote_signals_sp;
+
+ auto remote_signals_sp = std::make_shared<lldb_private::GDBRemoteSignals>();
+
+ bool done = array_sp->ForEach(
+ [&remote_signals_sp](StructuredData::Object *object) -> bool {
+ if (!object || !object->IsValid())
+ return false;
+
+ auto dict = object->GetAsDictionary();
+ if (!dict || !dict->IsValid())
+ return false;
+
+ // Signal number and signal name are required.
+ int signo;
+ if (!dict->GetValueForKeyAsInteger("signo", signo))
+ return false;
+
+ llvm::StringRef name;
+ if (!dict->GetValueForKeyAsString("name", name))
+ return false;
+
+ // We can live without short_name, description, etc.
+ bool suppress{false};
+ auto object_sp = dict->GetValueForKey("suppress");
+ if (object_sp && object_sp->IsValid())
+ suppress = object_sp->GetBooleanValue();
+
+ bool stop{false};
+ object_sp = dict->GetValueForKey("stop");
+ if (object_sp && object_sp->IsValid())
+ stop = object_sp->GetBooleanValue();
+
+ bool notify{false};
+ object_sp = dict->GetValueForKey("notify");
+ if (object_sp && object_sp->IsValid())
+ notify = object_sp->GetBooleanValue();
+
+ std::string description{""};
+ object_sp = dict->GetValueForKey("description");
+ if (object_sp && object_sp->IsValid())
+ description = object_sp->GetStringValue();
+
+ remote_signals_sp->AddSignal(signo, name.str().c_str(), suppress, stop,
+ notify, description.c_str());
+ return true;
+ });
+
+ if (done)
+ m_remote_signals_sp = std::move(remote_signals_sp);
+
+ return m_remote_signals_sp;
+}
+
+std::string PlatformRemoteGDBServer::MakeGdbServerUrl(
+ const std::string &platform_scheme, const std::string &platform_hostname,
+ uint16_t port, const char *socket_name) {
+ const char *override_scheme =
+ getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_SCHEME");
+ const char *override_hostname =
+ getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_HOSTNAME");
+ const char *port_offset_c_str =
+ getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_PORT_OFFSET");
+ int port_offset = port_offset_c_str ? ::atoi(port_offset_c_str) : 0;
+
+ return MakeUrl(override_scheme ? override_scheme : platform_scheme.c_str(),
+ override_hostname ? override_hostname
+ : platform_hostname.c_str(),
+ port + port_offset, socket_name);
+}
+
+std::string PlatformRemoteGDBServer::MakeUrl(const char *scheme,
+ const char *hostname,
+ uint16_t port, const char *path) {
+ StreamString result;
+ result.Printf("%s://%s", scheme, hostname);
+ if (port != 0)
+ result.Printf(":%u", port);
+ if (path)
+ result.Write(path, strlen(path));
+ return result.GetString();
+}
+
+lldb::ProcessSP PlatformRemoteGDBServer::ConnectProcess(
+ llvm::StringRef connect_url, llvm::StringRef plugin_name,
+ lldb_private::Debugger &debugger, lldb_private::Target *target,
+ lldb_private::Status &error) {
+ if (!IsRemote() || !IsConnected()) {
+ error.SetErrorString("Not connected to remote gdb server");
+ return nullptr;
+ }
+ return Platform::ConnectProcess(connect_url, plugin_name, debugger, target,
+ error);
+}
+
+size_t PlatformRemoteGDBServer::ConnectToWaitingProcesses(Debugger &debugger,
+ Status &error) {
+ std::vector<std::string> connection_urls;
+ GetPendingGdbServerList(connection_urls);
+
+ for (size_t i = 0; i < connection_urls.size(); ++i) {
+ ConnectProcess(connection_urls[i].c_str(), "", debugger, nullptr, error);
+ if (error.Fail())
+ return i; // We already connected to i process succsessfully
+ }
+ return connection_urls.size();
+}
+
+size_t PlatformRemoteGDBServer::GetPendingGdbServerList(
+ std::vector<std::string> &connection_urls) {
+ std::vector<std::pair<uint16_t, std::string>> remote_servers;
+ m_gdb_client.QueryGDBServer(remote_servers);
+ for (const auto &gdbserver : remote_servers) {
+ const char *socket_name_cstr =
+ gdbserver.second.empty() ? nullptr : gdbserver.second.c_str();
+ connection_urls.emplace_back(
+ MakeGdbServerUrl(m_platform_scheme, m_platform_hostname,
+ gdbserver.first, socket_name_cstr));
+ }
+ return connection_urls.size();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
new file mode 100644
index 000000000000..c774daa8ab73
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
@@ -0,0 +1,201 @@
+//===-- PlatformRemoteGDBServer.h ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_PlatformRemoteGDBServer_h_
+#define liblldb_PlatformRemoteGDBServer_h_
+
+#include <string>
+
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h"
+#include "Plugins/Process/Utility/GDBRemoteSignals.h"
+#include "lldb/Target/Platform.h"
+
+namespace lldb_private {
+namespace platform_gdb_server {
+
+class PlatformRemoteGDBServer : public Platform, private UserIDResolver {
+public:
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetDescriptionStatic();
+
+ PlatformRemoteGDBServer();
+
+ ~PlatformRemoteGDBServer() override;
+
+ // lldb_private::PluginInterface functions
+ ConstString GetPluginName() override { return GetPluginNameStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ // lldb_private::Platform functions
+ Status
+ ResolveExecutable(const ModuleSpec &module_spec, lldb::ModuleSP &module_sp,
+ const FileSpecList *module_search_paths_ptr) override;
+
+ bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch,
+ ModuleSpec &module_spec) override;
+
+ const char *GetDescription() override;
+
+ Status GetFileWithUUID(const FileSpec &platform_file, const UUID *uuid_ptr,
+ FileSpec &local_file) override;
+
+ bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &proc_info) override;
+
+ uint32_t FindProcesses(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) override;
+
+ Status LaunchProcess(ProcessLaunchInfo &launch_info) override;
+
+ Status KillProcess(const lldb::pid_t pid) override;
+
+ lldb::ProcessSP DebugProcess(ProcessLaunchInfo &launch_info,
+ Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a
+ // new target, else use existing
+ // one
+ Status &error) override;
+
+ lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a new
+ // target, else use existing one
+ Status &error) override;
+
+ bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override;
+
+ size_t GetSoftwareBreakpointTrapOpcode(Target &target,
+ BreakpointSite *bp_site) override;
+
+ bool GetRemoteOSVersion() override;
+
+ bool GetRemoteOSBuildString(std::string &s) override;
+
+ bool GetRemoteOSKernelDescription(std::string &s) override;
+
+ // Remote Platform subclasses need to override this function
+ ArchSpec GetRemoteSystemArchitecture() override;
+
+ FileSpec GetRemoteWorkingDirectory() override;
+
+ bool SetRemoteWorkingDirectory(const FileSpec &working_dir) override;
+
+ // Remote subclasses should override this and return a valid instance
+ // name if connected.
+ const char *GetHostname() override;
+
+ UserIDResolver &GetUserIDResolver() override { return *this; }
+
+ bool IsConnected() const override;
+
+ Status ConnectRemote(Args &args) override;
+
+ Status DisconnectRemote() override;
+
+ Status MakeDirectory(const FileSpec &file_spec,
+ uint32_t file_permissions) override;
+
+ Status GetFilePermissions(const FileSpec &file_spec,
+ uint32_t &file_permissions) override;
+
+ Status SetFilePermissions(const FileSpec &file_spec,
+ uint32_t file_permissions) override;
+
+ lldb::user_id_t OpenFile(const FileSpec &file_spec, uint32_t flags,
+ uint32_t mode, Status &error) override;
+
+ bool CloseFile(lldb::user_id_t fd, Status &error) override;
+
+ uint64_t ReadFile(lldb::user_id_t fd, uint64_t offset, void *data_ptr,
+ uint64_t len, Status &error) override;
+
+ uint64_t WriteFile(lldb::user_id_t fd, uint64_t offset, const void *data,
+ uint64_t len, Status &error) override;
+
+ lldb::user_id_t GetFileSize(const FileSpec &file_spec) override;
+
+ Status PutFile(const FileSpec &source, const FileSpec &destination,
+ uint32_t uid = UINT32_MAX, uint32_t gid = UINT32_MAX) override;
+
+ Status CreateSymlink(const FileSpec &src, const FileSpec &dst) override;
+
+ bool GetFileExists(const FileSpec &file_spec) override;
+
+ Status Unlink(const FileSpec &path) override;
+
+ Status RunShellCommand(
+ const char *command, // Shouldn't be NULL
+ const FileSpec &working_dir, // Pass empty FileSpec to use the current
+ // working directory
+ int *status_ptr, // Pass NULL if you don't want the process exit status
+ int *signo_ptr, // Pass NULL if you don't want the signal that caused the
+ // process to exit
+ std::string
+ *command_output, // Pass NULL if you don't want the command output
+ const lldb_private::Timeout<std::micro> &timeout) override;
+
+ void CalculateTrapHandlerSymbolNames() override;
+
+ const lldb::UnixSignalsSP &GetRemoteUnixSignals() override;
+
+ lldb::ProcessSP ConnectProcess(llvm::StringRef connect_url,
+ llvm::StringRef plugin_name,
+ lldb_private::Debugger &debugger,
+ lldb_private::Target *target,
+ lldb_private::Status &error) override;
+
+ size_t ConnectToWaitingProcesses(lldb_private::Debugger &debugger,
+ lldb_private::Status &error) override;
+
+ virtual size_t
+ GetPendingGdbServerList(std::vector<std::string> &connection_urls);
+
+protected:
+ process_gdb_remote::GDBRemoteCommunicationClient m_gdb_client;
+ std::string m_platform_description; // After we connect we can get a more
+ // complete description of what we are
+ // connected to
+ std::string m_platform_scheme;
+ std::string m_platform_hostname;
+
+ lldb::UnixSignalsSP m_remote_signals_sp;
+
+ // Launch the debug server on the remote host - caller connects to launched
+ // debug server using connect_url.
+ // Subclasses should override this method if they want to do extra actions
+ // before or
+ // after launching the debug server.
+ virtual bool LaunchGDBServer(lldb::pid_t &pid, std::string &connect_url);
+
+ virtual bool KillSpawnedProcess(lldb::pid_t pid);
+
+ virtual std::string MakeUrl(const char *scheme, const char *hostname,
+ uint16_t port, const char *path);
+
+private:
+ std::string MakeGdbServerUrl(const std::string &platform_scheme,
+ const std::string &platform_hostname,
+ uint16_t port, const char *socket_name);
+
+ llvm::Optional<std::string> DoGetUserName(UserIDResolver::id_t uid) override;
+ llvm::Optional<std::string> DoGetGroupName(UserIDResolver::id_t uid) override;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformRemoteGDBServer);
+};
+
+} // namespace platform_gdb_server
+} // namespace lldb_private
+
+#endif // liblldb_PlatformRemoteGDBServer_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.cpp
new file mode 100644
index 000000000000..3cdd2fa575e7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.cpp
@@ -0,0 +1,68 @@
+//===-- CFBundle.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFBundle.h"
+#include "CFString.h"
+
+// CFBundle constructor
+CFBundle::CFBundle(const char *path)
+ : CFReleaser<CFBundleRef>(), m_bundle_url() {
+ if (path && path[0])
+ SetPath(path);
+}
+
+// CFBundle copy constructor
+CFBundle::CFBundle(const CFBundle &rhs)
+ : CFReleaser<CFBundleRef>(rhs), m_bundle_url(rhs.m_bundle_url) {}
+
+// CFBundle copy constructor
+CFBundle &CFBundle::operator=(const CFBundle &rhs) {
+ *this = rhs;
+ return *this;
+}
+
+// Destructor
+CFBundle::~CFBundle() {}
+
+// Set the path for a bundle by supplying a
+bool CFBundle::SetPath(const char *path) {
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ // Release our old bundle and ULR
+ reset(); // This class is a CFReleaser<CFBundleRef>
+ m_bundle_url.reset();
+ // Make a CFStringRef from the supplied path
+ CFString cf_path;
+ cf_path.SetFileSystemRepresentation(path);
+ if (cf_path.get()) {
+ // Make our Bundle URL
+ m_bundle_url.reset(::CFURLCreateWithFileSystemPath(
+ alloc, cf_path.get(), kCFURLPOSIXPathStyle, true));
+ if (m_bundle_url.get()) {
+ reset(::CFBundleCreate(alloc, m_bundle_url.get()));
+ }
+ }
+ return get() != NULL;
+}
+
+CFStringRef CFBundle::GetIdentifier() const {
+ CFBundleRef bundle = get();
+ if (bundle != NULL)
+ return ::CFBundleGetIdentifier(bundle);
+ return NULL;
+}
+
+CFURLRef CFBundle::CopyExecutableURL() const {
+ CFBundleRef bundle = get();
+ if (bundle != NULL)
+ return CFBundleCopyExecutableURL(bundle);
+ return NULL;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.h
new file mode 100644
index 000000000000..f49dc30f1f8f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.h
@@ -0,0 +1,35 @@
+//===-- CFBundle.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFBundle_h__
+#define __CFBundle_h__
+
+#include "CFUtils.h"
+
+class CFBundle : public CFReleaser<CFBundleRef> {
+public:
+ // Constructors and Destructors
+ CFBundle(const char *path = NULL);
+ CFBundle(const CFBundle &rhs);
+ CFBundle &operator=(const CFBundle &rhs);
+ virtual ~CFBundle();
+ bool SetPath(const char *path);
+
+ CFStringRef GetIdentifier() const;
+
+ CFURLRef CopyExecutableURL() const;
+
+protected:
+ CFReleaser<CFURLRef> m_bundle_url;
+};
+
+#endif // #ifndef __CFBundle_h__
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.cpp
new file mode 100644
index 000000000000..4dcc05c86657
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.cpp
@@ -0,0 +1,153 @@
+//===-- CFString.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFString.h"
+#include <glob.h>
+#include <string>
+
+// CFString constructor
+CFString::CFString(CFStringRef s) : CFReleaser<CFStringRef>(s) {}
+
+// CFString copy constructor
+CFString::CFString(const CFString &rhs) : CFReleaser<CFStringRef>(rhs) {}
+
+// CFString copy constructor
+CFString &CFString::operator=(const CFString &rhs) {
+ if (this != &rhs)
+ *this = rhs;
+ return *this;
+}
+
+CFString::CFString(const char *cstr, CFStringEncoding cstr_encoding)
+ : CFReleaser<CFStringRef>() {
+ if (cstr && cstr[0]) {
+ reset(
+ ::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding));
+ }
+}
+
+// Destructor
+CFString::~CFString() {}
+
+const char *CFString::GetFileSystemRepresentation(std::string &s) {
+ return CFString::FileSystemRepresentation(get(), s);
+}
+
+CFStringRef CFString::SetFileSystemRepresentation(const char *path) {
+ CFStringRef new_value = NULL;
+ if (path && path[0])
+ new_value =
+ ::CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, path);
+ reset(new_value);
+ return get();
+}
+
+CFStringRef CFString::SetFileSystemRepresentationFromCFType(CFTypeRef cf_type) {
+ CFStringRef new_value = NULL;
+ if (cf_type != NULL) {
+ CFTypeID cf_type_id = ::CFGetTypeID(cf_type);
+
+ if (cf_type_id == ::CFStringGetTypeID()) {
+ // Retain since we are using the existing object
+ new_value = (CFStringRef)::CFRetain(cf_type);
+ } else if (cf_type_id == ::CFURLGetTypeID()) {
+ new_value =
+ ::CFURLCopyFileSystemPath((CFURLRef)cf_type, kCFURLPOSIXPathStyle);
+ }
+ }
+ reset(new_value);
+ return get();
+}
+
+CFStringRef
+CFString::SetFileSystemRepresentationAndExpandTilde(const char *path) {
+ std::string expanded_path;
+ if (CFString::GlobPath(path, expanded_path))
+ SetFileSystemRepresentation(expanded_path.c_str());
+ else
+ reset();
+ return get();
+}
+
+const char *CFString::UTF8(std::string &str) {
+ return CFString::UTF8(get(), str);
+}
+
+// Static function that puts a copy of the UTF8 contents of CF_STR into STR and
+// returns the C string pointer that is contained in STR when successful, else
+// NULL is returned. This allows the std::string parameter to own the extracted
+// string,
+// and also allows that string to be returned as a C string pointer that can be
+// used.
+
+const char *CFString::UTF8(CFStringRef cf_str, std::string &str) {
+ if (cf_str) {
+ const CFStringEncoding encoding = kCFStringEncodingUTF8;
+ CFIndex max_utf8_str_len = CFStringGetLength(cf_str);
+ max_utf8_str_len =
+ CFStringGetMaximumSizeForEncoding(max_utf8_str_len, encoding);
+ if (max_utf8_str_len > 0) {
+ str.resize(max_utf8_str_len);
+ if (!str.empty()) {
+ if (CFStringGetCString(cf_str, &str[0], str.size(), encoding)) {
+ str.resize(strlen(str.c_str()));
+ return str.c_str();
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+// Static function that puts a copy of the file system representation of CF_STR
+// into STR and returns the C string pointer that is contained in STR when
+// successful, else NULL is returned. This allows the std::string parameter to
+// own the extracted string, and also allows that string to be returned as a C
+// string pointer that can be used.
+
+const char *CFString::FileSystemRepresentation(CFStringRef cf_str,
+ std::string &str) {
+ if (cf_str) {
+ CFIndex max_length =
+ ::CFStringGetMaximumSizeOfFileSystemRepresentation(cf_str);
+ if (max_length > 0) {
+ str.resize(max_length);
+ if (!str.empty()) {
+ if (::CFStringGetFileSystemRepresentation(cf_str, &str[0],
+ str.size())) {
+ str.erase(::strlen(str.c_str()));
+ return str.c_str();
+ }
+ }
+ }
+ }
+ str.erase();
+ return NULL;
+}
+
+CFIndex CFString::GetLength() const {
+ CFStringRef str = get();
+ if (str)
+ return CFStringGetLength(str);
+ return 0;
+}
+
+const char *CFString::GlobPath(const char *path, std::string &expanded_path) {
+ glob_t globbuf;
+ if (::glob(path, GLOB_TILDE, NULL, &globbuf) == 0) {
+ expanded_path = globbuf.gl_pathv[0];
+ ::globfree(&globbuf);
+ } else
+ expanded_path.clear();
+
+ return expanded_path.c_str();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.h
new file mode 100644
index 000000000000..d1bd5682689e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.h
@@ -0,0 +1,40 @@
+//===-- CFString.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFString_h__
+#define __CFString_h__
+
+#include "CFUtils.h"
+#include <iosfwd>
+
+class CFString : public CFReleaser<CFStringRef> {
+public:
+ // Constructors and Destructors
+ CFString(CFStringRef cf_str = NULL);
+ CFString(const char *s, CFStringEncoding encoding = kCFStringEncodingUTF8);
+ CFString(const CFString &rhs);
+ CFString &operator=(const CFString &rhs);
+ virtual ~CFString();
+
+ const char *GetFileSystemRepresentation(std::string &str);
+ CFStringRef SetFileSystemRepresentation(const char *path);
+ CFStringRef SetFileSystemRepresentationFromCFType(CFTypeRef cf_type);
+ CFStringRef SetFileSystemRepresentationAndExpandTilde(const char *path);
+ const char *UTF8(std::string &str);
+ CFIndex GetLength() const;
+ static const char *UTF8(CFStringRef cf_str, std::string &str);
+ static const char *FileSystemRepresentation(CFStringRef cf_str,
+ std::string &str);
+ static const char *GlobPath(const char *path, std::string &expanded_path);
+};
+
+#endif // #ifndef __CFString_h__
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFUtils.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFUtils.h
new file mode 100644
index 000000000000..b567524ce63a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFUtils.h
@@ -0,0 +1,75 @@
+//===-- CFUtils.h -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/5/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFUtils_h__
+#define __CFUtils_h__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#ifdef __cplusplus
+
+// Templatized CF helper class that can own any CF pointer and will
+// call CFRelease() on any valid pointer it owns unless that pointer is
+// explicitly released using the release() member function.
+template <class T> class CFReleaser {
+public:
+ // Type names for the avlue
+ typedef T element_type;
+
+ // Constructors and destructors
+ CFReleaser(T ptr = NULL) : _ptr(ptr) {}
+ CFReleaser(const CFReleaser &copy) : _ptr(copy.get()) {
+ if (get())
+ ::CFRetain(get());
+ }
+ virtual ~CFReleaser() { reset(); }
+
+ // Assignments
+ CFReleaser &operator=(const CFReleaser<T> &copy) {
+ if (copy != *this) {
+ // Replace our owned pointer with the new one
+ reset(copy.get());
+ // Retain the current pointer that we own
+ if (get())
+ ::CFRetain(get());
+ }
+ }
+ // Get the address of the contained type
+ T *ptr_address() { return &_ptr; }
+
+ // Access the pointer itself
+ const T get() const { return _ptr; }
+ T get() { return _ptr; }
+
+ // Set a new value for the pointer and CFRelease our old
+ // value if we had a valid one.
+ void reset(T ptr = NULL) {
+ if (ptr != _ptr) {
+ if (_ptr != NULL)
+ ::CFRelease(_ptr);
+ _ptr = ptr;
+ }
+ }
+
+ // Release ownership without calling CFRelease
+ T release() {
+ T tmp = _ptr;
+ _ptr = NULL;
+ return tmp;
+ }
+
+private:
+ element_type _ptr;
+};
+
+#endif // #ifdef __cplusplus
+#endif // #ifndef __CFUtils_h__
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp
new file mode 100644
index 000000000000..3ec410fe7d76
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp
@@ -0,0 +1,642 @@
+//===-- DarwinProcessLauncher.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+//
+// DarwinProcessLauncher.cpp
+// lldb
+//
+// Created by Todd Fiala on 8/30/16.
+//
+//
+
+#include "DarwinProcessLauncher.h"
+
+// C includes
+#include <spawn.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+#endif
+
+// LLDB includes
+#include "lldb/lldb-enumerations.h"
+
+#include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/Support/Errno.h"
+
+#include "CFBundle.h"
+#include "CFString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+using namespace lldb_private::darwin_process_launcher;
+
+namespace {
+static LaunchFlavor g_launch_flavor = LaunchFlavor::Default;
+}
+
+namespace lldb_private {
+namespace darwin_process_launcher {
+
+static uint32_t GetCPUTypeForLocalProcess(::pid_t pid) {
+ int mib[CTL_MAXNAME] = {
+ 0,
+ };
+ size_t len = CTL_MAXNAME;
+ if (::sysctlnametomib("sysctl.proc_cputype", mib, &len))
+ return 0;
+
+ mib[len] = pid;
+ len++;
+
+ cpu_type_t cpu;
+ size_t cpu_len = sizeof(cpu);
+ if (::sysctl(mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0))
+ cpu = 0;
+ return cpu;
+}
+
+static bool ResolveExecutablePath(const char *path, char *resolved_path,
+ size_t resolved_path_size) {
+ if (path == NULL || path[0] == '\0')
+ return false;
+
+ char max_path[PATH_MAX];
+ std::string result;
+ CFString::GlobPath(path, result);
+
+ if (result.empty())
+ result = path;
+
+ struct stat path_stat;
+ if (::stat(path, &path_stat) == 0) {
+ if ((path_stat.st_mode & S_IFMT) == S_IFDIR) {
+ CFBundle bundle(path);
+ CFReleaser<CFURLRef> url(bundle.CopyExecutableURL());
+ if (url.get()) {
+ if (::CFURLGetFileSystemRepresentation(
+ url.get(), true, (UInt8 *)resolved_path, resolved_path_size))
+ return true;
+ }
+ }
+ }
+
+ if (realpath(path, max_path)) {
+ // Found the path relatively...
+ ::strncpy(resolved_path, max_path, resolved_path_size);
+ return strlen(resolved_path) + 1 < resolved_path_size;
+ } else {
+ // Not a relative path, check the PATH environment variable if the
+ const char *PATH = getenv("PATH");
+ if (PATH) {
+ const char *curr_path_start = PATH;
+ const char *curr_path_end;
+ while (curr_path_start && *curr_path_start) {
+ curr_path_end = strchr(curr_path_start, ':');
+ if (curr_path_end == NULL) {
+ result.assign(curr_path_start);
+ curr_path_start = NULL;
+ } else if (curr_path_end > curr_path_start) {
+ size_t len = curr_path_end - curr_path_start;
+ result.assign(curr_path_start, len);
+ curr_path_start += len + 1;
+ } else
+ break;
+
+ result += '/';
+ result += path;
+ struct stat s;
+ if (stat(result.c_str(), &s) == 0) {
+ ::strncpy(resolved_path, result.c_str(), resolved_path_size);
+ return result.size() + 1 < resolved_path_size;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+// TODO check if we have a general purpose fork and exec. We may be
+// able to get rid of this entirely.
+static Status ForkChildForPTraceDebugging(const char *path, char const *argv[],
+ char const *envp[], ::pid_t *pid,
+ int *pty_fd) {
+ Status error;
+ if (!path || !argv || !envp || !pid || !pty_fd) {
+ error.SetErrorString("invalid arguments");
+ return error;
+ }
+
+ // Use a fork that ties the child process's stdin/out/err to a pseudo
+ // terminal so we can read it in our MachProcess::STDIOThread as unbuffered
+ // io.
+ PseudoTerminal pty;
+ char fork_error[256];
+ memset(fork_error, 0, sizeof(fork_error));
+ *pid = static_cast<::pid_t>(pty.Fork(fork_error, sizeof(fork_error)));
+ if (*pid < 0) {
+ // Status during fork.
+ *pid = static_cast<::pid_t>(LLDB_INVALID_PROCESS_ID);
+ error.SetErrorStringWithFormat("%s(): fork failed: %s", __FUNCTION__,
+ fork_error);
+ return error;
+ } else if (pid == 0) {
+ // Child process
+
+ // Debug this process.
+ ::ptrace(PT_TRACE_ME, 0, 0, 0);
+
+ // Get BSD signals as mach exceptions.
+ ::ptrace(PT_SIGEXC, 0, 0, 0);
+
+ // If our parent is setgid, lets make sure we don't inherit those extra
+ // powers due to nepotism.
+ if (::setgid(getgid()) == 0) {
+ // Let the child have its own process group. We need to execute this call
+ // in both the child and parent to avoid a race condition between the two
+ // processes.
+
+ // Set the child process group to match its pid.
+ ::setpgid(0, 0);
+
+ // Sleep a bit to before the exec call.
+ ::sleep(1);
+
+ // Turn this process into the given executable.
+ ::execv(path, (char *const *)argv);
+ }
+ // Exit with error code. Child process should have taken over in above exec
+ // call and if the exec fails it will exit the child process below.
+ ::exit(127);
+ } else {
+ // Parent process
+ // Let the child have its own process group. We need to execute this call
+ // in both the child and parent to avoid a race condition between the two
+ // processes.
+
+ // Set the child process group to match its pid
+ ::setpgid(*pid, *pid);
+ if (pty_fd) {
+ // Release our master pty file descriptor so the pty class doesn't close
+ // it and so we can continue to use it in our STDIO thread
+ *pty_fd = pty.ReleaseMasterFileDescriptor();
+ }
+ }
+ return error;
+}
+
+static Status
+CreatePosixSpawnFileAction(const FileAction &action,
+ posix_spawn_file_actions_t *file_actions) {
+ Status error;
+
+ // Log it.
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log) {
+ StreamString stream;
+ stream.PutCString("converting file action for posix_spawn(): ");
+ action.Dump(stream);
+ stream.Flush();
+ log->PutCString(stream.GetString().c_str());
+ }
+
+ // Validate args.
+ if (!file_actions) {
+ error.SetErrorString("mandatory file_actions arg is null");
+ return error;
+ }
+
+ // Build the posix file action.
+ switch (action.GetAction()) {
+ case FileAction::eFileActionOpen: {
+ const int error_code = ::posix_spawn_file_actions_addopen(
+ file_actions, action.GetFD(), action.GetPath(),
+ action.GetActionArgument(), 0);
+ if (error_code != 0) {
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+ break;
+ }
+
+ case FileAction::eFileActionClose: {
+ const int error_code =
+ ::posix_spawn_file_actions_addclose(file_actions, action.GetFD());
+ if (error_code != 0) {
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+ break;
+ }
+
+ case FileAction::eFileActionDuplicate: {
+ const int error_code = ::posix_spawn_file_actions_adddup2(
+ file_actions, action.GetFD(), action.GetActionArgument());
+ if (error_code != 0) {
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+ break;
+ }
+
+ case FileAction::eFileActionNone:
+ default:
+ if (log)
+ log->Printf("%s(): unsupported file action %u", __FUNCTION__,
+ action.GetAction());
+ break;
+ }
+
+ return error;
+}
+
+static Status PosixSpawnChildForPTraceDebugging(const char *path,
+ ProcessLaunchInfo &launch_info,
+ ::pid_t *pid,
+ cpu_type_t *actual_cpu_type) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (!pid) {
+ error.SetErrorStringWithFormat("%s(): pid arg cannot be null",
+ __FUNCTION__);
+ return error;
+ }
+
+ posix_spawnattr_t attr;
+ short flags;
+ if (log) {
+ StreamString stream;
+ stream.Printf("%s(path='%s',...)\n", __FUNCTION__, path);
+ launch_info.Dump(stream, nullptr);
+ stream.Flush();
+ log->PutCString(stream.GetString().c_str());
+ }
+
+ int error_code;
+ if ((error_code = ::posix_spawnattr_init(&attr)) != 0) {
+ if (log)
+ log->Printf("::posix_spawnattr_init(&attr) failed");
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+
+ // Ensure we clean up the spawnattr structure however we exit this function.
+ std::unique_ptr<posix_spawnattr_t, int (*)(posix_spawnattr_t *)> spawnattr_up(
+ &attr, ::posix_spawnattr_destroy);
+
+ flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF |
+ POSIX_SPAWN_SETSIGMASK;
+ if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))
+ flags |= _POSIX_SPAWN_DISABLE_ASLR;
+
+ sigset_t no_signals;
+ sigset_t all_signals;
+ sigemptyset(&no_signals);
+ sigfillset(&all_signals);
+ ::posix_spawnattr_setsigmask(&attr, &no_signals);
+ ::posix_spawnattr_setsigdefault(&attr, &all_signals);
+
+ if ((error_code = ::posix_spawnattr_setflags(&attr, flags)) != 0) {
+ LLDB_LOG(log,
+ "::posix_spawnattr_setflags(&attr, "
+ "POSIX_SPAWN_START_SUSPENDED{0}) failed: {1}",
+ flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR"
+ : "",
+ llvm::sys::StrError(error_code));
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+
+#if !defined(__arm__)
+
+ // We don't need to do this for ARM, and we really shouldn't now that we have
+ // multiple CPU subtypes and no posix_spawnattr call that allows us to set
+ // which CPU subtype to launch...
+ cpu_type_t desired_cpu_type = launch_info.GetArchitecture().GetMachOCPUType();
+ if (desired_cpu_type != LLDB_INVALID_CPUTYPE) {
+ size_t ocount = 0;
+ error_code =
+ ::posix_spawnattr_setbinpref_np(&attr, 1, &desired_cpu_type, &ocount);
+ if (error_code != 0) {
+ LLDB_LOG(log,
+ "::posix_spawnattr_setbinpref_np(&attr, 1, "
+ "cpu_type = {0:x8}, count => {1}): {2}",
+ desired_cpu_type, ocount, llvm::sys::StrError(error_code));
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+ if (ocount != 1) {
+ error.SetErrorStringWithFormat("posix_spawnattr_setbinpref_np "
+ "did not set the expected number "
+ "of cpu_type entries: expected 1 "
+ "but was %zu",
+ ocount);
+ return error;
+ }
+ }
+#endif
+
+ posix_spawn_file_actions_t file_actions;
+ if ((error_code = ::posix_spawn_file_actions_init(&file_actions)) != 0) {
+ LLDB_LOG(log, "::posix_spawn_file_actions_init(&file_actions) failed: {0}",
+ llvm::sys::StrError(error_code));
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+
+ // Ensure we clean up file actions however we exit this. When the
+ // file_actions_up below goes out of scope, we'll get our file action
+ // cleanup.
+ std::unique_ptr<posix_spawn_file_actions_t,
+ int (*)(posix_spawn_file_actions_t *)>
+ file_actions_up(&file_actions, ::posix_spawn_file_actions_destroy);
+
+ // We assume the caller has setup the file actions appropriately. We are not
+ // in the business of figuring out what we really need here. lldb-server will
+ // have already called FinalizeFileActions() as well to button these up
+ // properly.
+ const size_t num_actions = launch_info.GetNumFileActions();
+ for (size_t action_index = 0; action_index < num_actions; ++action_index) {
+ const FileAction *const action =
+ launch_info.GetFileActionAtIndex(action_index);
+ if (!action)
+ continue;
+
+ error = CreatePosixSpawnFileAction(*action, &file_actions);
+ if (!error.Success()) {
+ if (log)
+ log->Printf("%s(): error converting FileAction to posix_spawn "
+ "file action: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ }
+
+ // TODO: Verify if we can set the working directory back immediately
+ // after the posix_spawnp call without creating a race condition???
+ const char *const working_directory =
+ launch_info.GetWorkingDirectory().GetCString();
+ if (working_directory && working_directory[0])
+ ::chdir(working_directory);
+
+ auto argv = launch_info.GetArguments().GetArgumentVector();
+ auto envp = launch_info.GetEnvironmentEntries().GetArgumentVector();
+ error_code = ::posix_spawnp(pid, path, &file_actions, &attr,
+ (char *const *)argv, (char *const *)envp);
+ if (error_code != 0) {
+ LLDB_LOG(log,
+ "::posix_spawnp(pid => {0}, path = '{1}', file_actions "
+ "= {2}, attr = {3}, argv = {4}, envp = {5}) failed: {6}",
+ pid, path, &file_actions, &attr, argv, envp,
+ llvm::sys::StrError(error_code));
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+
+ // Validate we got a pid.
+ if (pid == LLDB_INVALID_PROCESS_ID) {
+ error.SetErrorString("posix_spawn() did not indicate a failure but it "
+ "failed to return a pid, aborting.");
+ return error;
+ }
+
+ if (actual_cpu_type) {
+ *actual_cpu_type = GetCPUTypeForLocalProcess(*pid);
+ if (log)
+ log->Printf("%s(): cpu type for launched process pid=%i: "
+ "cpu_type=0x%8.8x",
+ __FUNCTION__, *pid, *actual_cpu_type);
+ }
+
+ return error;
+}
+
+Status LaunchInferior(ProcessLaunchInfo &launch_info, int *pty_master_fd,
+ LaunchFlavor *launch_flavor) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (!launch_flavor) {
+ error.SetErrorString("mandatory launch_flavor field was null");
+ return error;
+ }
+
+ if (log) {
+ StreamString stream;
+ stream.Printf("NativeProcessDarwin::%s(): launching with the "
+ "following launch info:",
+ __FUNCTION__);
+ launch_info.Dump(stream, nullptr);
+ stream.Flush();
+ log->PutCString(stream.GetString().c_str());
+ }
+
+ // Retrieve the binary name given to us.
+ char given_path[PATH_MAX];
+ given_path[0] = '\0';
+ launch_info.GetExecutableFile().GetPath(given_path, sizeof(given_path));
+
+ // Determine the manner in which we'll launch.
+ *launch_flavor = g_launch_flavor;
+ if (*launch_flavor == LaunchFlavor::Default) {
+ // Our default launch method is posix spawn
+ *launch_flavor = LaunchFlavor::PosixSpawn;
+#if defined WITH_FBS
+ // Check if we have an app bundle, if so launch using BackBoard Services.
+ if (strstr(given_path, ".app")) {
+ *launch_flavor = eLaunchFlavorFBS;
+ }
+#elif defined WITH_BKS
+ // Check if we have an app bundle, if so launch using BackBoard Services.
+ if (strstr(given_path, ".app")) {
+ *launch_flavor = eLaunchFlavorBKS;
+ }
+#elif defined WITH_SPRINGBOARD
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (strstr(given_path, ".app")) {
+ *launch_flavor = eLaunchFlavorSpringBoard;
+ }
+#endif
+ }
+
+ // Attempt to resolve the binary name to an absolute path.
+ char resolved_path[PATH_MAX];
+ resolved_path[0] = '\0';
+
+ if (log)
+ log->Printf("%s(): attempting to resolve given binary path: \"%s\"",
+ __FUNCTION__, given_path);
+
+ // If we fail to resolve the path to our executable, then just use what we
+ // were given and hope for the best
+ if (!ResolveExecutablePath(given_path, resolved_path,
+ sizeof(resolved_path))) {
+ if (log)
+ log->Printf("%s(): failed to resolve binary path, using "
+ "what was given verbatim and hoping for the best",
+ __FUNCTION__);
+ ::strncpy(resolved_path, given_path, sizeof(resolved_path));
+ } else {
+ if (log)
+ log->Printf("%s(): resolved given binary path to: \"%s\"", __FUNCTION__,
+ resolved_path);
+ }
+
+ char launch_err_str[PATH_MAX];
+ launch_err_str[0] = '\0';
+
+ // TODO figure out how to handle QSetProcessEvent
+ // const char *process_event = ctx.GetProcessEvent();
+
+ // Ensure the binary is there.
+ struct stat path_stat;
+ if (::stat(resolved_path, &path_stat) == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ // Fork a child process for debugging
+ // state_callback(eStateLaunching);
+
+ const auto argv = launch_info.GetArguments().GetConstArgumentVector();
+ const auto envp =
+ launch_info.GetEnvironmentEntries().GetConstArgumentVector();
+
+ switch (*launch_flavor) {
+ case LaunchFlavor::ForkExec: {
+ ::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ error = ForkChildForPTraceDebugging(resolved_path, argv, envp, &pid,
+ pty_master_fd);
+ if (error.Success()) {
+ launch_info.SetProcessID(static_cast<lldb::pid_t>(pid));
+ } else {
+ // Reset any variables that might have been set during a failed launch
+ // attempt.
+ if (pty_master_fd)
+ *pty_master_fd = -1;
+
+ // We're done.
+ return error;
+ }
+ } break;
+
+#ifdef WITH_FBS
+ case LaunchFlavor::FBS: {
+ const char *app_ext = strstr(path, ".app");
+ if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ m_flags |= eMachProcessFlagsUsingFBS;
+ if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp,
+ no_stdio, disable_aslr, event_data,
+ launch_err) != 0)
+ return m_pid; // A successful SBLaunchForDebug() returns and assigns a
+ // non-zero m_pid.
+ else
+ break; // We tried a FBS launch, but didn't succeed lets get out
+ }
+ } break;
+#endif
+
+#ifdef WITH_BKS
+ case LaunchFlavor::BKS: {
+ const char *app_ext = strstr(path, ".app");
+ if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ m_flags |= eMachProcessFlagsUsingBKS;
+ if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp,
+ no_stdio, disable_aslr, event_data,
+ launch_err) != 0)
+ return m_pid; // A successful SBLaunchForDebug() returns and assigns a
+ // non-zero m_pid.
+ else
+ break; // We tried a BKS launch, but didn't succeed lets get out
+ }
+ } break;
+#endif
+
+#ifdef WITH_SPRINGBOARD
+ case LaunchFlavor::SpringBoard: {
+ // .../whatever.app/whatever ?
+ // Or .../com.apple.whatever.app/whatever -- be careful of ".app" in
+ // "com.apple.whatever" here
+ const char *app_ext = strstr(path, ".app/");
+ if (app_ext == NULL) {
+ // .../whatever.app ?
+ int len = strlen(path);
+ if (len > 5) {
+ if (strcmp(path + len - 4, ".app") == 0) {
+ app_ext = path + len - 4;
+ }
+ }
+ }
+ if (app_ext) {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ if (SBLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio,
+ disable_aslr, launch_err) != 0)
+ return m_pid; // A successful SBLaunchForDebug() returns and assigns a
+ // non-zero m_pid.
+ else
+ break; // We tried a springboard launch, but didn't succeed lets get out
+ }
+ } break;
+#endif
+
+ case LaunchFlavor::PosixSpawn: {
+ ::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+ // Retrieve paths for stdin/stdout/stderr.
+ cpu_type_t actual_cpu_type = 0;
+ error = PosixSpawnChildForPTraceDebugging(resolved_path, launch_info, &pid,
+ &actual_cpu_type);
+ if (error.Success()) {
+ launch_info.SetProcessID(static_cast<lldb::pid_t>(pid));
+ if (pty_master_fd)
+ *pty_master_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
+ } else {
+ // Reset any variables that might have been set during a failed launch
+ // attempt.
+ if (pty_master_fd)
+ *pty_master_fd = -1;
+
+ // We're done.
+ return error;
+ }
+ break;
+ }
+
+ default:
+ // Invalid launch flavor.
+ error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): unknown "
+ "launch flavor %d",
+ __FUNCTION__, (int)*launch_flavor);
+ return error;
+ }
+
+ if (launch_info.GetProcessID() == LLDB_INVALID_PROCESS_ID) {
+ // If we don't have a valid process ID and no one has set the error, then
+ // return a generic error.
+ if (error.Success())
+ error.SetErrorStringWithFormat("%s(): failed to launch, no reason "
+ "specified",
+ __FUNCTION__);
+ }
+
+ // We're done with the launch side of the operation.
+ return error;
+}
+}
+} // namespaces
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.h
new file mode 100644
index 000000000000..0e65b56a143e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.h
@@ -0,0 +1,48 @@
+//===-- DarwinProcessLauncher.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DarwinProcessLauncher_h
+#define DarwinProcessLauncher_h
+
+// C headers
+#include <mach/machine.h>
+#include <sys/types.h>
+
+// C++ headers
+#include <functional>
+
+// LLDB headers
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+
+#include "LaunchFlavor.h"
+
+namespace lldb_private {
+namespace darwin_process_launcher {
+// =============================================================================
+/// Launches a process for debugging.
+///
+/// \param[inout] launch_info
+/// Specifies details about the process to launch (e.g. path, architecture,
+/// etc.). On output, includes the launched ProcessID (pid).
+///
+/// \param[out] pty_master_fd
+/// Returns the master side of the pseudo-terminal used to communicate
+/// with stdin/stdout from the launched process. May be nullptr.
+///
+/// \param[out] launch_flavor
+/// Contains the launch flavor used when launching the process.
+// =============================================================================
+Status
+LaunchInferior(ProcessLaunchInfo &launch_info, int *pty_master_fd,
+ lldb_private::process_darwin::LaunchFlavor *launch_flavor);
+
+} // darwin_process_launcher
+} // lldb_private
+
+#endif /* DarwinProcessLauncher_h */
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/LaunchFlavor.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/LaunchFlavor.h
new file mode 100644
index 000000000000..cfd76d1b9c3c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/LaunchFlavor.h
@@ -0,0 +1,32 @@
+//===-- LaunchFlavor.h ---------------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LaunchFlavor_h
+#define LaunchFlavor_h
+
+namespace lldb_private {
+namespace process_darwin {
+
+enum class LaunchFlavor {
+ Default = 0,
+ PosixSpawn = 1,
+ ForkExec = 2,
+#ifdef WITH_SPRINGBOARD
+ SpringBoard = 3,
+#endif
+#ifdef WITH_BKS
+ BKS = 4,
+#endif
+#ifdef WITH_FBS
+ FBS = 5
+#endif
+};
+}
+} // namespaces
+
+#endif /* LaunchFlavor_h */
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.cpp
new file mode 100644
index 000000000000..70ad6736a748
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.cpp
@@ -0,0 +1,504 @@
+//===-- MachException.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachException.h"
+
+// C includes
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+
+// C++ includes
+#include <mutex>
+
+// LLDB includes
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+
+// Routine mach_exception_raise
+extern "C" kern_return_t
+catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread,
+ mach_port_t task, exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt);
+
+extern "C" kern_return_t catch_mach_exception_raise_state(
+ mach_port_t exception_port, exception_type_t exception,
+ const mach_exception_data_t code, mach_msg_type_number_t codeCnt,
+ int *flavor, const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt);
+
+// Routine mach_exception_raise_state_identity
+extern "C" kern_return_t catch_mach_exception_raise_state_identity(
+ mach_port_t exception_port, mach_port_t thread, mach_port_t task,
+ exception_type_t exception, mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt, int *flavor, thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt);
+
+extern "C" boolean_t mach_exc_server(mach_msg_header_t *InHeadP,
+ mach_msg_header_t *OutHeadP);
+
+static MachException::Data *g_message = NULL;
+
+extern "C" kern_return_t catch_mach_exception_raise_state(
+ mach_port_t exc_port, exception_type_t exc_type,
+ const mach_exception_data_t exc_data, mach_msg_type_number_t exc_data_count,
+ int *flavor, const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt) {
+ // TODO change to LIBLLDB_LOG_EXCEPTION
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (log) {
+ log->Printf("::%s(exc_port = 0x%4.4x, exc_type = %d (%s), "
+ "exc_data = 0x%llx, exc_data_count = %d)",
+ __FUNCTION__, exc_port, exc_type, MachException::Name(exc_type),
+ (uint64_t)exc_data, exc_data_count);
+ }
+ return KERN_FAILURE;
+}
+
+extern "C" kern_return_t catch_mach_exception_raise_state_identity(
+ mach_port_t exc_port, mach_port_t thread_port, mach_port_t task_port,
+ exception_type_t exc_type, mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count, int *flavor,
+ thread_state_t old_state, mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (log) {
+ log->Printf("::%s(exc_port = 0x%4.4x, thd_port = 0x%4.4x, "
+ "tsk_port = 0x%4.4x, exc_type = %d (%s), exc_data[%d] = "
+ "{ 0x%llx, 0x%llx })",
+ __FUNCTION__, exc_port, thread_port, task_port, exc_type,
+ MachException::Name(exc_type), exc_data_count,
+ (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
+ (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
+ }
+
+ return KERN_FAILURE;
+}
+
+extern "C" kern_return_t
+catch_mach_exception_raise(mach_port_t exc_port, mach_port_t thread_port,
+ mach_port_t task_port, exception_type_t exc_type,
+ mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (log) {
+ log->Printf("::%s(exc_port = 0x%4.4x, thd_port = 0x%4.4x, "
+ "tsk_port = 0x%4.4x, exc_type = %d (%s), exc_data[%d] "
+ "= { 0x%llx, 0x%llx })",
+ __FUNCTION__, exc_port, thread_port, task_port, exc_type,
+ MachException::Name(exc_type), exc_data_count,
+ (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
+ (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
+ }
+
+ if (task_port == g_message->task_port) {
+ g_message->task_port = task_port;
+ g_message->thread_port = thread_port;
+ g_message->exc_type = exc_type;
+ g_message->exc_data.resize(exc_data_count);
+ ::memcpy(&g_message->exc_data[0], exc_data,
+ g_message->exc_data.size() * sizeof(mach_exception_data_type_t));
+ return KERN_SUCCESS;
+ }
+ return KERN_FAILURE;
+}
+
+bool MachException::Data::GetStopInfo(struct ThreadStopInfo *stop_info,
+ const UnixSignals &signals,
+ Stream &stream) const {
+ if (!stop_info)
+ return false;
+
+ // Zero out the structure.
+ memset(stop_info, 0, sizeof(struct ThreadStopInfo));
+
+ if (exc_type == 0) {
+ stop_info->reason = eStopReasonInvalid;
+ return true;
+ }
+
+ // We always stop with a mach exception.
+ stop_info->reason = eStopReasonException;
+ // Save the EXC_XXXX exception type.
+ stop_info->details.exception.type = exc_type;
+
+ // Fill in a text description
+ const char *exc_name = MachException::Name(exc_type);
+ if (exc_name)
+ stream.Printf("%s", exc_name);
+ else
+ stream.Printf("%i", exc_type);
+
+ stop_info->details.exception.data_count = exc_data.size();
+
+ int soft_signal = SoftSignal();
+ if (soft_signal) {
+ const char *sig_str = signals.GetSignalAsCString(soft_signal);
+ stream.Printf(" EXC_SOFT_SIGNAL( %i ( %s ))", soft_signal,
+ sig_str ? sig_str : "unknown signal");
+ } else {
+ // No special disassembly for exception data, just print it.
+ size_t idx;
+ stream.Printf(" data[%llu] = {",
+ (uint64_t)stop_info->details.exception.data_count);
+
+ for (idx = 0; idx < stop_info->details.exception.data_count; ++idx) {
+ stream.Printf(
+ "0x%llx%c", (uint64_t)exc_data[idx],
+ ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ','));
+ }
+ }
+
+ // Copy the exception data
+ for (size_t i = 0; i < stop_info->details.exception.data_count; i++)
+ stop_info->details.exception.data[i] = exc_data[i];
+
+ return true;
+}
+
+Status MachException::Message::Receive(mach_port_t port,
+ mach_msg_option_t options,
+ mach_msg_timeout_t timeout,
+ mach_port_t notify_port) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ mach_msg_timeout_t mach_msg_timeout =
+ options & MACH_RCV_TIMEOUT ? timeout : 0;
+ if (log && ((options & MACH_RCV_TIMEOUT) == 0)) {
+ // Dump this log message if we have no timeout in case it never returns
+ log->Printf("::mach_msg(msg->{bits = %#x, size = %u remote_port = %#x, "
+ "local_port = %#x, reserved = 0x%x, id = 0x%x}, "
+ "option = %#x, send_size = 0, rcv_size = %llu, "
+ "rcv_name = %#x, timeout = %u, notify = %#x)",
+ exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options,
+ (uint64_t)sizeof(exc_msg.data), port, mach_msg_timeout,
+ notify_port);
+ }
+
+ mach_msg_return_t mach_err =
+ ::mach_msg(&exc_msg.hdr,
+ options, // options
+ 0, // Send size
+ sizeof(exc_msg.data), // Receive size
+ port, // exception port to watch for
+ // exception on
+ mach_msg_timeout, // timeout in msec (obeyed only
+ // if MACH_RCV_TIMEOUT is ORed
+ // into the options parameter)
+ notify_port);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+
+ // Dump any errors we get
+ if (error.Fail() && log) {
+ log->Printf("::mach_msg(msg->{bits = %#x, size = %u remote_port = %#x, "
+ "local_port = %#x, reserved = 0x%x, id = 0x%x}, "
+ "option = %#x, send_size = %u, rcv_size = %lu, rcv_name "
+ "= %#x, timeout = %u, notify = %#x) failed: %s",
+ exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 0,
+ sizeof(exc_msg.data), port, mach_msg_timeout, notify_port,
+ error.AsCString());
+ }
+ return error;
+}
+
+void MachException::Message::Dump(Stream &stream) const {
+ stream.Printf(" exc_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = "
+ "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = "
+ "0x%8.8x }\n",
+ exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id);
+
+ stream.Printf(" reply_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = "
+ "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = "
+ "0x%8.8x }",
+ reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size,
+ reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port,
+ reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id);
+}
+
+bool MachException::Message::CatchExceptionRaise(task_t task) {
+ bool success = false;
+ state.task_port = task;
+ g_message = &state;
+ // The exc_server function is the MIG generated server handling function to
+ // handle messages from the kernel relating to the occurrence of an exception
+ // in a thread. Such messages are delivered to the exception port set via
+ // thread_set_exception_ports or task_set_exception_ports. When an exception
+ // occurs in a thread, the thread sends an exception message to its exception
+ // port, blocking in the kernel waiting for the receipt of a reply. The
+ // exc_server function performs all necessary argument handling for this
+ // kernel message and calls catch_exception_raise,
+ // catch_exception_raise_state or catch_exception_raise_state_identity, which
+ // should handle the exception. If the called routine returns KERN_SUCCESS, a
+ // reply message will be sent, allowing the thread to continue from the point
+ // of the exception; otherwise, no reply message is sent and the called
+ // routine must have dealt with the exception thread directly.
+ if (mach_exc_server(&exc_msg.hdr, &reply_msg.hdr)) {
+ success = true;
+ } else {
+ Log *log(
+ GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (log)
+ log->Printf("MachException::Message::%s(): mach_exc_server "
+ "returned zero...",
+ __FUNCTION__);
+ }
+ g_message = NULL;
+ return success;
+}
+
+Status MachException::Message::Reply(::pid_t inferior_pid, task_t inferior_task,
+ int signal) {
+ // Reply to the exception...
+ Status error;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ // If we had a soft signal, we need to update the thread first so it can
+ // continue without signaling
+ int soft_signal = state.SoftSignal();
+ if (soft_signal) {
+ int state_pid = -1;
+ if (inferior_task == state.task_port) {
+ // This is our task, so we can update the signal to send to it
+ state_pid = inferior_pid;
+ soft_signal = signal;
+ } else {
+ auto mach_err = ::pid_for_task(state.task_port, &state_pid);
+ if (mach_err) {
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (log)
+ log->Printf("MachException::Message::%s(): pid_for_task() "
+ "failed: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ }
+
+ lldbassert(state_pid != -1);
+ if (state_pid != -1) {
+ errno = 0;
+ caddr_t thread_port_caddr = (caddr_t)(uintptr_t)state.thread_port;
+ if (::ptrace(PT_THUPDATE, state_pid, thread_port_caddr, soft_signal) != 0)
+ error.SetError(errno, eErrorTypePOSIX);
+
+ if (!error.Success()) {
+ if (log)
+ log->Printf("::ptrace(request = PT_THUPDATE, pid = "
+ "0x%4.4x, tid = 0x%4.4x, signal = %i)",
+ state_pid, state.thread_port, soft_signal);
+ return error;
+ }
+ }
+ }
+
+ if (log)
+ log->Printf("::mach_msg ( msg->{bits = %#x, size = %u, remote_port "
+ "= %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, "
+ "option = %#x, send_size = %u, rcv_size = %u, rcv_name "
+ "= %#x, timeout = %u, notify = %#x)",
+ reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size,
+ reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port,
+ reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id,
+ MACH_SEND_MSG | MACH_SEND_INTERRUPT, reply_msg.hdr.msgh_size, 0,
+ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+ auto mach_err =
+ ::mach_msg(&reply_msg.hdr, MACH_SEND_MSG | MACH_SEND_INTERRUPT,
+ reply_msg.hdr.msgh_size, 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (mach_err)
+ error.SetError(mach_err, eErrorTypeMachKernel);
+
+ // Log our error if we have one.
+ if (error.Fail() && log) {
+ if (error.GetError() == MACH_SEND_INTERRUPTED) {
+ log->PutCString("::mach_msg() - send interrupted");
+ // TODO: keep retrying to reply???
+ } else if (state.task_port == inferior_task) {
+ log->Printf("mach_msg(): returned an error when replying "
+ "to a mach exception: error = %u (%s)",
+ error.GetError(), error.AsCString());
+ } else {
+ log->Printf("::mach_msg() - failed (child of task): %u (%s)",
+ error.GetError(), error.AsCString());
+ }
+ }
+
+ return error;
+}
+
+#define PREV_EXC_MASK_ALL \
+ (EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | \
+ EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT | \
+ EXC_MASK_SYSCALL | EXC_MASK_MACH_SYSCALL | EXC_MASK_RPC_ALERT | \
+ EXC_MASK_MACHINE)
+
+// Don't listen for EXC_RESOURCE, it should really get handled by the system
+// handler.
+
+#ifndef EXC_RESOURCE
+#define EXC_RESOURCE 11
+#endif
+
+#ifndef EXC_MASK_RESOURCE
+#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE)
+#endif
+
+#define LLDB_EXC_MASK (EXC_MASK_ALL & ~EXC_MASK_RESOURCE)
+
+Status MachException::PortInfo::Save(task_t task) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ if (log)
+ log->Printf("MachException::PortInfo::%s(task = 0x%4.4x)", __FUNCTION__,
+ task);
+
+ // Be careful to be able to have debugserver built on a newer OS than what it
+ // is currently running on by being able to start with all exceptions and
+ // back off to just what is supported on the current system
+ mask = LLDB_EXC_MASK;
+
+ count = (sizeof(ports) / sizeof(ports[0]));
+ auto mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports,
+ behaviors, flavors);
+ if (mach_err)
+ error.SetError(mach_err, eErrorTypeMachKernel);
+
+ if (log) {
+ if (error.Success()) {
+ log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = "
+ "0x%x, maskCnt => %u, ports, behaviors, flavors)",
+ task, mask, count);
+ } else {
+ log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = 0x%x, "
+ "maskCnt => %u, ports, behaviors, flavors) error: %u (%s)",
+ task, mask, count, error.GetError(), error.AsCString());
+ }
+ }
+
+ if ((error.GetError() == KERN_INVALID_ARGUMENT) &&
+ (mask != PREV_EXC_MASK_ALL)) {
+ mask = PREV_EXC_MASK_ALL;
+ count = (sizeof(ports) / sizeof(ports[0]));
+ mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports,
+ behaviors, flavors);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (log) {
+ if (error.Success()) {
+ log->Printf("::task_get_exception_ports(task = 0x%4.4x, "
+ "mask = 0x%x, maskCnt => %u, ports, behaviors, "
+ "flavors)",
+ task, mask, count);
+ } else {
+ log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = "
+ "0x%x, maskCnt => %u, ports, behaviors, flavors) "
+ "error: %u (%s)",
+ task, mask, count, error.GetError(), error.AsCString());
+ }
+ }
+ }
+ if (error.Fail()) {
+ mask = 0;
+ count = 0;
+ }
+ return error;
+}
+
+Status MachException::PortInfo::Restore(task_t task) {
+ Status error;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ if (log)
+ log->Printf("MachException::PortInfo::Restore(task = 0x%4.4x)", task);
+
+ uint32_t i = 0;
+ if (count > 0) {
+ for (i = 0; i < count; i++) {
+ auto mach_err = ::task_set_exception_ports(task, masks[i], ports[i],
+ behaviors[i], flavors[i]);
+ if (mach_err)
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (log) {
+ if (error.Success()) {
+ log->Printf("::task_set_exception_ports(task = 0x%4.4x, "
+ "exception_mask = 0x%8.8x, new_port = 0x%4.4x, "
+ "behavior = 0x%8.8x, new_flavor = 0x%8.8x)",
+ task, masks[i], ports[i], behaviors[i], flavors[i]);
+ } else {
+ log->Printf("::task_set_exception_ports(task = 0x%4.4x, "
+ "exception_mask = 0x%8.8x, new_port = 0x%4.4x, "
+ "behavior = 0x%8.8x, new_flavor = 0x%8.8x): "
+ "error %u (%s)",
+ task, masks[i], ports[i], behaviors[i], flavors[i],
+ error.GetError(), error.AsCString());
+ }
+ }
+
+ // Bail if we encounter any errors
+ if (error.Fail())
+ break;
+ }
+ }
+
+ count = 0;
+ return error;
+}
+
+const char *MachException::Name(exception_type_t exc_type) {
+ switch (exc_type) {
+ case EXC_BAD_ACCESS:
+ return "EXC_BAD_ACCESS";
+ case EXC_BAD_INSTRUCTION:
+ return "EXC_BAD_INSTRUCTION";
+ case EXC_ARITHMETIC:
+ return "EXC_ARITHMETIC";
+ case EXC_EMULATION:
+ return "EXC_EMULATION";
+ case EXC_SOFTWARE:
+ return "EXC_SOFTWARE";
+ case EXC_BREAKPOINT:
+ return "EXC_BREAKPOINT";
+ case EXC_SYSCALL:
+ return "EXC_SYSCALL";
+ case EXC_MACH_SYSCALL:
+ return "EXC_MACH_SYSCALL";
+ case EXC_RPC_ALERT:
+ return "EXC_RPC_ALERT";
+#ifdef EXC_CRASH
+ case EXC_CRASH:
+ return "EXC_CRASH";
+#endif
+ default:
+ break;
+ }
+ return NULL;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.h
new file mode 100644
index 000000000000..18e49173b020
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.h
@@ -0,0 +1,139 @@
+//===-- MachException.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachException_h__
+#define __MachException_h__
+
+#include <mach/mach.h>
+#include <vector>
+
+#include "lldb/Host/Debug.h"
+#include "lldb/lldb-private-forward.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+namespace process_darwin {
+
+typedef union MachMessageTag {
+ mach_msg_header_t hdr;
+ char data[1024];
+} MachMessage;
+
+class MachException {
+public:
+ struct PortInfo {
+ exception_mask_t mask; // the exception mask for this device which may be a
+ // subset of EXC_MASK_ALL...
+ exception_mask_t masks[EXC_TYPES_COUNT];
+ mach_port_t ports[EXC_TYPES_COUNT];
+ exception_behavior_t behaviors[EXC_TYPES_COUNT];
+ thread_state_flavor_t flavors[EXC_TYPES_COUNT];
+ mach_msg_type_number_t count;
+
+ Status Save(task_t task);
+
+ Status Restore(task_t task);
+ };
+
+ struct Data {
+ task_t task_port;
+ thread_t thread_port;
+ exception_type_t exc_type;
+ std::vector<mach_exception_data_type_t> exc_data;
+ Data()
+ : task_port(TASK_NULL), thread_port(THREAD_NULL), exc_type(0),
+ exc_data() {}
+
+ void Clear() {
+ task_port = TASK_NULL;
+ thread_port = THREAD_NULL;
+ exc_type = 0;
+ exc_data.clear();
+ }
+
+ bool IsValid() const {
+ return task_port != TASK_NULL && thread_port != THREAD_NULL &&
+ exc_type != 0;
+ }
+
+ // Return the SoftSignal for this MachException data, or zero if there is
+ // none
+ int SoftSignal() const {
+ if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 &&
+ exc_data[0] == EXC_SOFT_SIGNAL)
+ return static_cast<int>(exc_data[1]);
+ return 0;
+ }
+
+ bool IsBreakpoint() const {
+ return (exc_type == EXC_BREAKPOINT ||
+ ((exc_type == EXC_SOFTWARE) && exc_data[0] == 1));
+ }
+
+ bool GetStopInfo(ThreadStopInfo *stop_info, const UnixSignals &signals,
+ Stream &stream) const;
+ };
+
+ struct Message {
+ MachMessage exc_msg;
+ MachMessage reply_msg;
+ Data state;
+
+ Message() : state() {
+ memset(&exc_msg, 0, sizeof(exc_msg));
+ memset(&reply_msg, 0, sizeof(reply_msg));
+ }
+
+ bool CatchExceptionRaise(task_t task);
+
+ Status Reply(::pid_t inferior_pid, task_t inferior_task, int signal);
+
+ Status Receive(mach_port_t receive_port, mach_msg_option_t options,
+ mach_msg_timeout_t timeout,
+ mach_port_t notify_port = MACH_PORT_NULL);
+
+ void Dump(Stream &stream) const;
+
+ typedef std::vector<Message> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+ };
+
+ enum {
+ e_actionForward, // Forward signal to inferior process
+ e_actionStop, // Stop when this signal is received
+ };
+ struct Action {
+ task_t task_port; // Set to TASK_NULL for any TASK
+ thread_t thread_port; // Set to THREAD_NULL for any thread
+ exception_type_t exc_mask; // Mach exception mask to watch for
+ std::vector<mach_exception_data_type_t> exc_data_mask; // Mask to apply to
+ // exception data, or
+ // empty to ignore
+ // exc_data value for
+ // exception
+ std::vector<mach_exception_data_type_t> exc_data_value; // Value to compare
+ // to exception data
+ // after masking, or
+ // empty to ignore
+ // exc_data value
+ // for exception
+ uint8_t flags; // Action flags describing what to do with the exception
+ };
+
+ static const char *Name(exception_type_t exc_type);
+};
+
+} // namespace process_darwin
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp
new file mode 100644
index 000000000000..fe7de27e0ee6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp
@@ -0,0 +1,1541 @@
+//===-- NativeProcessDarwin.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeProcessDarwin.h"
+
+// C includes
+#include <mach/mach_init.h>
+#include <mach/mach_traps.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+// C++ includes
+// LLDB includes
+#include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "CFBundle.h"
+#include "CFString.h"
+#include "DarwinProcessLauncher.h"
+
+#include "MachException.h"
+
+#include "llvm/Support/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+using namespace lldb_private::darwin_process_launcher;
+
+// Hidden Impl
+
+namespace {
+struct hack_task_dyld_info {
+ mach_vm_address_t all_image_info_addr;
+ mach_vm_size_t all_image_info_size;
+};
+}
+
+// Public Static Methods
+
+Status NativeProcessProtocol::Launch(
+ ProcessLaunchInfo &launch_info,
+ NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop,
+ NativeProcessProtocolSP &native_process_sp) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ Status error;
+
+ // Verify the working directory is valid if one was specified.
+ FileSpec working_dir(launch_info.GetWorkingDirectory());
+ if (working_dir) {
+ FileInstance::Instance().Resolve(working_dir);
+ if (!FileSystem::Instance().IsDirectory(working_dir)) {
+ error.SetErrorStringWithFormat("No such file or directory: %s",
+ working_dir.GetCString());
+ return error;
+ }
+ }
+
+ // Launch the inferior.
+ int pty_master_fd = -1;
+ LaunchFlavor launch_flavor = LaunchFlavor::Default;
+
+ error = LaunchInferior(launch_info, &pty_master_fd, &launch_flavor);
+
+ // Handle launch failure.
+ if (!error.Success()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() failed to launch process: "
+ "%s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ // Handle failure to return a pid.
+ if (launch_info.GetProcessID() == LLDB_INVALID_PROCESS_ID) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() launch succeeded but no "
+ "pid was returned! Aborting.",
+ __FUNCTION__);
+ return error;
+ }
+
+ // Create the Darwin native process impl.
+ std::shared_ptr<NativeProcessDarwin> np_darwin_sp(
+ new NativeProcessDarwin(launch_info.GetProcessID(), pty_master_fd));
+ if (!np_darwin_sp->RegisterNativeDelegate(native_delegate)) {
+ native_process_sp.reset();
+ error.SetErrorStringWithFormat("failed to register the native delegate");
+ return error;
+ }
+
+ // Finalize the processing needed to debug the launched process with a
+ // NativeProcessDarwin instance.
+ error = np_darwin_sp->FinalizeLaunch(launch_flavor, mainloop);
+ if (!error.Success()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() aborting, failed to finalize"
+ " the launching of the process: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ // Return the process and process id to the caller through the launch args.
+ native_process_sp = np_darwin_sp;
+ return error;
+}
+
+Status NativeProcessProtocol::Attach(
+ lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate,
+ MainLoop &mainloop, NativeProcessProtocolSP &native_process_sp) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(pid = %" PRIi64 ")", __FUNCTION__,
+ pid);
+
+ // Retrieve the architecture for the running process.
+ ArchSpec process_arch;
+ Status error = ResolveProcessArchitecture(pid, process_arch);
+ if (!error.Success())
+ return error;
+
+ // TODO get attach to return this value.
+ const int pty_master_fd = -1;
+ std::shared_ptr<NativeProcessDarwin> native_process_darwin_sp(
+ new NativeProcessDarwin(pid, pty_master_fd));
+
+ if (!native_process_darwin_sp->RegisterNativeDelegate(native_delegate)) {
+ error.SetErrorStringWithFormat("failed to register the native "
+ "delegate");
+ return error;
+ }
+
+ native_process_darwin_sp->AttachToInferior(mainloop, pid, error);
+ if (!error.Success())
+ return error;
+
+ native_process_sp = native_process_darwin_sp;
+ return error;
+}
+
+// ctor/dtor
+
+NativeProcessDarwin::NativeProcessDarwin(lldb::pid_t pid, int pty_master_fd)
+ : NativeProcessProtocol(pid), m_task(TASK_NULL), m_did_exec(false),
+ m_cpu_type(0), m_exception_port(MACH_PORT_NULL), m_exc_port_info(),
+ m_exception_thread(nullptr), m_exception_messages_mutex(),
+ m_sent_interrupt_signo(0), m_auto_resume_signo(0), m_thread_list(),
+ m_thread_actions(), m_waitpid_pipe(), m_waitpid_thread(nullptr),
+ m_waitpid_reader_handle() {
+ // TODO add this to the NativeProcessProtocol constructor.
+ m_terminal_fd = pty_master_fd;
+}
+
+NativeProcessDarwin::~NativeProcessDarwin() {}
+
+// Instance methods
+
+Status NativeProcessDarwin::FinalizeLaunch(LaunchFlavor launch_flavor,
+ MainLoop &main_loop) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ error = StartExceptionThread();
+ if (!error.Success()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failure starting the "
+ "mach exception port monitor thread: %s",
+ __FUNCTION__, error.AsCString());
+
+ // Terminate the inferior process. There's nothing meaningful we can do if
+ // we can't receive signals and exceptions. Since we launched the process,
+ // it's fair game for us to kill it.
+ ::ptrace(PT_KILL, m_pid, 0, 0);
+ SetState(eStateExited);
+
+ return error;
+ }
+
+ StartSTDIOThread();
+
+ if (launch_flavor == LaunchFlavor::PosixSpawn) {
+ SetState(eStateAttaching);
+ errno = 0;
+ int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0) {
+ // m_flags |= eMachProcessFlagsAttached;
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): successfully spawned "
+ "process with pid %" PRIu64,
+ __FUNCTION__, m_pid);
+ } else {
+ error.SetErrorToErrno();
+ SetState(eStateExited);
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): error: failed to "
+ "attach to spawned pid %" PRIu64 " (error=%d (%s))",
+ __FUNCTION__, m_pid, (int)error.GetError(),
+ error.AsCString());
+ return error;
+ }
+ }
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): new pid is %" PRIu64 "...",
+ __FUNCTION__, m_pid);
+
+ // Spawn a thread to reap our child inferior process...
+ error = StartWaitpidThread(main_loop);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to start waitpid() "
+ "thread: %s",
+ __FUNCTION__, error.AsCString());
+ kill(SIGKILL, static_cast<::pid_t>(m_pid));
+ return error;
+ }
+
+ if (TaskPortForProcessID(error) == TASK_NULL) {
+ // We failed to get the task for our process ID which is bad. Kill our
+ // process; otherwise, it will be stopped at the entry point and get
+ // reparented to someone else and never go away.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): could not get task port "
+ "for process, sending SIGKILL and exiting: %s",
+ __FUNCTION__, error.AsCString());
+ kill(SIGKILL, static_cast<::pid_t>(m_pid));
+ return error;
+ }
+
+ // Indicate that we're stopped, as we always launch suspended.
+ SetState(eStateStopped);
+
+ // Success.
+ return error;
+}
+
+Status NativeProcessDarwin::SaveExceptionPortInfo() {
+ return m_exc_port_info.Save(m_task);
+}
+
+bool NativeProcessDarwin::ProcessUsingSpringBoard() const {
+ // TODO implement flags
+ // return (m_flags & eMachProcessFlagsUsingSBS) != 0;
+ return false;
+}
+
+bool NativeProcessDarwin::ProcessUsingBackBoard() const {
+ // TODO implement flags
+ // return (m_flags & eMachProcessFlagsUsingBKS) != 0;
+ return false;
+}
+
+// Called by the exception thread when an exception has been received from our
+// process. The exception message is completely filled and the exception data
+// has already been copied.
+void NativeProcessDarwin::ExceptionMessageReceived(
+ const MachException::Message &message) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+ if (m_exception_messages.empty()) {
+ // Suspend the task the moment we receive our first exception message.
+ SuspendTask();
+ }
+
+ // Use a locker to automatically unlock our mutex in case of exceptions Add
+ // the exception to our internal exception stack
+ m_exception_messages.push_back(message);
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): new queued message count: %lu",
+ __FUNCTION__, m_exception_messages.size());
+}
+
+void *NativeProcessDarwin::ExceptionThread(void *arg) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (!arg) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): cannot run mach exception "
+ "thread, mandatory process arg was null",
+ __FUNCTION__);
+ return nullptr;
+ }
+
+ return reinterpret_cast<NativeProcessDarwin *>(arg)->DoExceptionThread();
+}
+
+void *NativeProcessDarwin::DoExceptionThread() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(arg=%p) starting thread...",
+ __FUNCTION__, this);
+
+ pthread_setname_np("exception monitoring thread");
+
+ // Ensure we don't get CPU starved.
+ MaybeRaiseThreadPriority();
+
+ // We keep a count of the number of consecutive exceptions received so we
+ // know to grab all exceptions without a timeout. We do this to get a bunch
+ // of related exceptions on our exception port so we can process then
+ // together. When we have multiple threads, we can get an exception per
+ // thread and they will come in consecutively. The main loop in this thread
+ // can stop periodically if needed to service things related to this process.
+ //
+ // [did we lose some words here?]
+ //
+ // flag set in the options, so we will wait forever for an exception on
+ // 0 our exception port. After we get one exception, we then will use the
+ // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
+ // exceptions for our process. After we have received the last pending
+ // exception, we will get a timeout which enables us to then notify our main
+ // thread that we have an exception bundle available. We then wait for the
+ // main thread to tell this exception thread to start trying to get
+ // exceptions messages again and we start again with a mach_msg read with
+ // infinite timeout.
+ //
+ // We choose to park a thread on this, rather than polling, because the
+ // polling is expensive. On devices, we need to minimize overhead caused by
+ // the process monitor.
+ uint32_t num_exceptions_received = 0;
+ Status error;
+ task_t task = m_task;
+ mach_msg_timeout_t periodic_timeout = 0;
+
+#if defined(WITH_SPRINGBOARD) && !defined(WITH_BKS)
+ mach_msg_timeout_t watchdog_elapsed = 0;
+ mach_msg_timeout_t watchdog_timeout = 60 * 1000;
+ ::pid_t pid = (::pid_t)process->GetID();
+ CFReleaser<SBSWatchdogAssertionRef> watchdog;
+
+ if (process->ProcessUsingSpringBoard()) {
+ // Request a renewal for every 60 seconds if we attached using SpringBoard.
+ watchdog.reset(::SBSWatchdogAssertionCreateForPID(nullptr, pid, 60));
+ if (log)
+ log->Printf("::SBSWatchdogAssertionCreateForPID(NULL, %4.4x, 60) "
+ "=> %p",
+ pid, watchdog.get());
+
+ if (watchdog.get()) {
+ ::SBSWatchdogAssertionRenew(watchdog.get());
+
+ CFTimeInterval watchdogRenewalInterval =
+ ::SBSWatchdogAssertionGetRenewalInterval(watchdog.get());
+ if (log)
+ log->Printf("::SBSWatchdogAssertionGetRenewalInterval(%p) => "
+ "%g seconds",
+ watchdog.get(), watchdogRenewalInterval);
+ if (watchdogRenewalInterval > 0.0) {
+ watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000;
+ if (watchdog_timeout > 3000) {
+ // Give us a second to renew our timeout.
+ watchdog_timeout -= 1000;
+ } else if (watchdog_timeout > 1000) {
+ // Give us a quarter of a second to renew our timeout.
+ watchdog_timeout -= 250;
+ }
+ }
+ }
+ if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout)
+ periodic_timeout = watchdog_timeout;
+ }
+#endif // #if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+
+#ifdef WITH_BKS
+ CFReleaser<BKSWatchdogAssertionRef> watchdog;
+ if (process->ProcessUsingBackBoard()) {
+ ::pid_t pid = process->GetID();
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ watchdog.reset(::BKSWatchdogAssertionCreateForPID(alloc, pid));
+ }
+#endif // #ifdef WITH_BKS
+
+ // Do we want to use a weak pointer to the NativeProcessDarwin here, in which
+ // case we can guarantee we don't whack the process monitor if we race
+ // between this thread and the main one on shutdown?
+ while (IsExceptionPortValid()) {
+ ::pthread_testcancel();
+
+ MachException::Message exception_message;
+
+ if (num_exceptions_received > 0) {
+ // We don't want a timeout here, just receive as many exceptions as we
+ // can since we already have one. We want to get all currently available
+ // exceptions for this task at once.
+ error = exception_message.Receive(
+ GetExceptionPort(),
+ MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0);
+ } else if (periodic_timeout > 0) {
+ // We need to stop periodically in this loop, so try and get a mach
+ // message with a valid timeout (ms).
+ error = exception_message.Receive(GetExceptionPort(),
+ MACH_RCV_MSG | MACH_RCV_INTERRUPT |
+ MACH_RCV_TIMEOUT,
+ periodic_timeout);
+ } else {
+ // We don't need to parse all current exceptions or stop periodically,
+ // just wait for an exception forever.
+ error = exception_message.Receive(GetExceptionPort(),
+ MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0);
+ }
+
+ if (error.Success()) {
+ // We successfully received an exception.
+ if (exception_message.CatchExceptionRaise(task)) {
+ ++num_exceptions_received;
+ ExceptionMessageReceived(exception_message);
+ }
+ } else {
+ if (error.GetError() == MACH_RCV_INTERRUPTED) {
+ // We were interrupted.
+
+ // If we have no task port we should exit this thread, as it implies
+ // the inferior went down.
+ if (!IsExceptionPortValid()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): the inferior "
+ "exception port is no longer valid, "
+ "canceling exception thread...",
+ __FUNCTION__);
+ // Should we be setting a process state here?
+ break;
+ }
+
+ // Make sure the inferior task is still valid.
+ if (IsTaskValid()) {
+ // Task is still ok.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): interrupted, but "
+ "the inferior task iss till valid, "
+ "continuing...",
+ __FUNCTION__);
+ continue;
+ } else {
+ // The inferior task is no longer valid. Time to exit as the process
+ // has gone away.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): the inferior task "
+ "has exited, and so will we...",
+ __FUNCTION__);
+ // Does this race at all with our waitpid()?
+ SetState(eStateExited);
+ break;
+ }
+ } else if (error.GetError() == MACH_RCV_TIMED_OUT) {
+ // We timed out when waiting for exceptions.
+
+ if (num_exceptions_received > 0) {
+ // We were receiving all current exceptions with a timeout of zero.
+ // It is time to go back to our normal looping mode.
+ num_exceptions_received = 0;
+
+ // Notify our main thread we have a complete exception message bundle
+ // available. Get the possibly updated task port back from the
+ // process in case we exec'ed and our task port changed.
+ task = ExceptionMessageBundleComplete();
+
+ // In case we use a timeout value when getting exceptions, make sure
+ // our task is still valid.
+ if (IsTaskValid(task)) {
+ // Task is still ok.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): got a timeout, "
+ "continuing...",
+ __FUNCTION__);
+ continue;
+ } else {
+ // The inferior task is no longer valid. Time to exit as the
+ // process has gone away.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): the inferior "
+ "task has exited, and so will we...",
+ __FUNCTION__);
+ // Does this race at all with our waitpid()?
+ SetState(eStateExited);
+ break;
+ }
+ }
+
+#if defined(WITH_SPRINGBOARD) && !defined(WITH_BKS)
+ if (watchdog.get()) {
+ watchdog_elapsed += periodic_timeout;
+ if (watchdog_elapsed >= watchdog_timeout) {
+ if (log)
+ log->Printf("SBSWatchdogAssertionRenew(%p)", watchdog.get());
+ ::SBSWatchdogAssertionRenew(watchdog.get());
+ watchdog_elapsed = 0;
+ }
+ }
+#endif
+ } else {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): continuing after "
+ "receiving an unexpected error: %u (%s)",
+ __FUNCTION__, error.GetError(), error.AsCString());
+ // TODO: notify of error?
+ }
+ }
+ }
+
+#if defined(WITH_SPRINGBOARD) && !defined(WITH_BKS)
+ if (watchdog.get()) {
+ // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel
+ // when we
+ // all are up and running on systems that support it. The SBS framework has
+ // a #define that will forward SBSWatchdogAssertionRelease to
+ // SBSWatchdogAssertionCancel for now so it should still build either way.
+ DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionRelease(%p)",
+ watchdog.get());
+ ::SBSWatchdogAssertionRelease(watchdog.get());
+ }
+#endif // #if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(%p): thread exiting...", __FUNCTION__,
+ this);
+ return nullptr;
+}
+
+Status NativeProcessDarwin::StartExceptionThread() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() called", __FUNCTION__);
+
+ // Make sure we've looked up the inferior port.
+ TaskPortForProcessID(error);
+
+ // Ensure the inferior task is valid.
+ if (!IsTaskValid()) {
+ error.SetErrorStringWithFormat("cannot start exception thread: "
+ "task 0x%4.4x is not valid",
+ m_task);
+ return error;
+ }
+
+ // Get the mach port for the process monitor.
+ mach_port_t task_self = mach_task_self();
+
+ // Allocate an exception port that we will use to track our child process
+ auto mach_err = ::mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE,
+ &m_exception_port);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): mach_port_allocate("
+ "task_self=0x%4.4x, MACH_PORT_RIGHT_RECEIVE, "
+ "&m_exception_port) failed: %u (%s)",
+ __FUNCTION__, task_self, error.GetError(), error.AsCString());
+ return error;
+ }
+
+ // Add the ability to send messages on the new exception port
+ mach_err = ::mach_port_insert_right(
+ task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): mach_port_insert_right("
+ "task_self=0x%4.4x, m_exception_port=0x%4.4x, "
+ "m_exception_port=0x%4.4x, MACH_MSG_TYPE_MAKE_SEND) "
+ "failed: %u (%s)",
+ __FUNCTION__, task_self, m_exception_port, m_exception_port,
+ error.GetError(), error.AsCString());
+ return error;
+ }
+
+ // Save the original state of the exception ports for our child process.
+ error = SaveExceptionPortInfo();
+ if (error.Fail() || (m_exc_port_info.mask == 0)) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): SaveExceptionPortInfo() "
+ "failed, cannot install exception handler: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ // Set the ability to get all exceptions on this port.
+ mach_err = ::task_set_exception_ports(
+ m_task, m_exc_port_info.mask, m_exception_port,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("::task_set_exception_ports (task = 0x%4.4x, "
+ "exception_mask = 0x%8.8x, new_port = 0x%4.4x, "
+ "behavior = 0x%8.8x, new_flavor = 0x%8.8x) failed: "
+ "%u (%s)",
+ m_task, m_exc_port_info.mask, m_exception_port,
+ (EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES), THREAD_STATE_NONE,
+ error.GetError(), error.AsCString());
+ return error;
+ }
+
+ // Create the exception thread.
+ auto pthread_err =
+ ::pthread_create(&m_exception_thread, nullptr, ExceptionThread, this);
+ error.SetError(pthread_err, eErrorTypePOSIX);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to create Mach "
+ "exception-handling thread: %u (%s)",
+ __FUNCTION__, error.GetError(), error.AsCString());
+ }
+
+ return error;
+}
+
+lldb::addr_t
+NativeProcessDarwin::GetDYLDAllImageInfosAddress(Status &error) const {
+ error.Clear();
+
+ struct hack_task_dyld_info dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ // Make sure that COUNT isn't bigger than our hacked up struct
+ // hack_task_dyld_info. If it is, then make COUNT smaller to match.
+ if (count > (sizeof(struct hack_task_dyld_info) / sizeof(natural_t))) {
+ count = (sizeof(struct hack_task_dyld_info) / sizeof(natural_t));
+ }
+
+ TaskPortForProcessID(error);
+ if (error.Fail())
+ return LLDB_INVALID_ADDRESS;
+
+ auto mach_err =
+ ::task_info(m_task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Success()) {
+ // We now have the address of the all image infos structure.
+ return dyld_info.all_image_info_addr;
+ }
+
+ // We don't have it.
+ return LLDB_INVALID_ADDRESS;
+}
+
+uint32_t NativeProcessDarwin::GetCPUTypeForLocalProcess(::pid_t pid) {
+ int mib[CTL_MAXNAME] = {
+ 0,
+ };
+ size_t len = CTL_MAXNAME;
+
+ if (::sysctlnametomib("sysctl.proc_cputype", mib, &len))
+ return 0;
+
+ mib[len] = pid;
+ len++;
+
+ cpu_type_t cpu;
+ size_t cpu_len = sizeof(cpu);
+ if (::sysctl(mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0))
+ cpu = 0;
+ return cpu;
+}
+
+uint32_t NativeProcessDarwin::GetCPUType() const {
+ if (m_cpu_type == 0 && m_pid != 0)
+ m_cpu_type = GetCPUTypeForLocalProcess(m_pid);
+ return m_cpu_type;
+}
+
+task_t NativeProcessDarwin::ExceptionMessageBundleComplete() {
+ // We have a complete bundle of exceptions for our child process.
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): processing %lu exception "
+ "messages.",
+ __FUNCTION__, m_exception_messages.size());
+
+ if (m_exception_messages.empty()) {
+ // Not particularly useful...
+ return m_task;
+ }
+
+ bool auto_resume = false;
+ m_did_exec = false;
+
+ // First check for any SIGTRAP and make sure we didn't exec
+ const task_t task = m_task;
+ size_t i;
+ if (m_pid != 0) {
+ bool received_interrupt = false;
+ uint32_t num_task_exceptions = 0;
+ for (i = 0; i < m_exception_messages.size(); ++i) {
+ if (m_exception_messages[i].state.task_port != task) {
+ // This is an exception that is not for our inferior, ignore.
+ continue;
+ }
+
+ // This is an exception for the inferior.
+ ++num_task_exceptions;
+ const int signo = m_exception_messages[i].state.SoftSignal();
+ if (signo == SIGTRAP) {
+ // SIGTRAP could mean that we exec'ed. We need to check the
+ // dyld all_image_infos.infoArray to see if it is NULL and if so, say
+ // that we exec'ed.
+ const addr_t aii_addr = GetDYLDAllImageInfosAddress(error);
+ if (aii_addr == LLDB_INVALID_ADDRESS)
+ break;
+
+ const addr_t info_array_count_addr = aii_addr + 4;
+ uint32_t info_array_count = 0;
+ size_t bytes_read = 0;
+ Status read_error;
+ read_error = ReadMemory(info_array_count_addr, // source addr
+ &info_array_count, // dest addr
+ 4, // byte count
+ bytes_read); // #bytes read
+ if (read_error.Success() && (bytes_read == 4)) {
+ if (info_array_count == 0) {
+ // We got the all infos address, and there are zero entries. We
+ // think we exec'd.
+ m_did_exec = true;
+
+ // Force the task port to update itself in case the task port
+ // changed after exec
+ const task_t old_task = m_task;
+ const bool force_update = true;
+ const task_t new_task = TaskPortForProcessID(error, force_update);
+ if (old_task != new_task) {
+ if (log)
+ log->Printf("exec: inferior task port changed "
+ "from 0x%4.4x to 0x%4.4x",
+ old_task, new_task);
+ }
+ }
+ } else {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() warning: "
+ "failed to read all_image_infos."
+ "infoArrayCount from 0x%8.8llx",
+ __FUNCTION__, info_array_count_addr);
+ }
+ } else if ((m_sent_interrupt_signo != 0) &&
+ (signo == m_sent_interrupt_signo)) {
+ // We just received the interrupt that we sent to ourselves.
+ received_interrupt = true;
+ }
+ }
+
+ if (m_did_exec) {
+ cpu_type_t process_cpu_type = GetCPUTypeForLocalProcess(m_pid);
+ if (m_cpu_type != process_cpu_type) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): arch changed from "
+ "0x%8.8x to 0x%8.8x",
+ __FUNCTION__, m_cpu_type, process_cpu_type);
+ m_cpu_type = process_cpu_type;
+ // TODO figure out if we need to do something here.
+ // DNBArchProtocol::SetArchitecture (process_cpu_type);
+ }
+ m_thread_list.Clear();
+
+ // TODO hook up breakpoints.
+ // m_breakpoints.DisableAll();
+ }
+
+ if (m_sent_interrupt_signo != 0) {
+ if (received_interrupt) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): process "
+ "successfully interrupted with signal %i",
+ __FUNCTION__, m_sent_interrupt_signo);
+
+ // Mark that we received the interrupt signal
+ m_sent_interrupt_signo = 0;
+ // Now check if we had a case where:
+ // 1 - We called NativeProcessDarwin::Interrupt() but we stopped
+ // for another reason.
+ // 2 - We called NativeProcessDarwin::Resume() (but still
+ // haven't gotten the interrupt signal).
+ // 3 - We are now incorrectly stopped because we are handling
+ // the interrupt signal we missed.
+ // 4 - We might need to resume if we stopped only with the
+ // interrupt signal that we never handled.
+ if (m_auto_resume_signo != 0) {
+ // Only auto_resume if we stopped with _only_ the interrupt signal.
+ if (num_task_exceptions == 1) {
+ auto_resume = true;
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): auto "
+ "resuming due to unhandled interrupt "
+ "signal %i",
+ __FUNCTION__, m_auto_resume_signo);
+ }
+ m_auto_resume_signo = 0;
+ }
+ } else {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): didn't get signal "
+ "%i after MachProcess::Interrupt()",
+ __FUNCTION__, m_sent_interrupt_signo);
+ }
+ }
+ }
+
+ // Let all threads recover from stopping and do any clean up based on the
+ // previous thread state (if any).
+ m_thread_list.ProcessDidStop(*this);
+
+ // Let each thread know of any exceptions
+ for (i = 0; i < m_exception_messages.size(); ++i) {
+ // Let the thread list forward all exceptions on down to each thread.
+ if (m_exception_messages[i].state.task_port == task) {
+ // This exception is for our inferior.
+ m_thread_list.NotifyException(m_exception_messages[i].state);
+ }
+
+ if (log) {
+ StreamString stream;
+ m_exception_messages[i].Dump(stream);
+ stream.Flush();
+ log->PutCString(stream.GetString().c_str());
+ }
+ }
+
+ if (log) {
+ StreamString stream;
+ m_thread_list.Dump(stream);
+ stream.Flush();
+ log->PutCString(stream.GetString().c_str());
+ }
+
+ bool step_more = false;
+ if (m_thread_list.ShouldStop(step_more) && (auto_resume == false)) {
+// TODO - need to hook up event system here. !!!!
+#if 0
+ // Wait for the eEventProcessRunningStateChanged event to be reset
+ // before changing state to stopped to avoid race condition with very
+ // fast start/stops.
+ struct timespec timeout;
+
+ //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000); // Wait for 250 ms
+ DNBTimer::OffsetTimeOfDay(&timeout, 1, 0); // Wait for 250 ms
+ m_events.WaitForEventsToReset(eEventProcessRunningStateChanged,
+ &timeout);
+#endif
+ SetState(eStateStopped);
+ } else {
+ // Resume without checking our current state.
+ PrivateResume();
+ }
+
+ return m_task;
+}
+
+void NativeProcessDarwin::StartSTDIOThread() {
+ // TODO implement
+}
+
+Status NativeProcessDarwin::StartWaitpidThread(MainLoop &main_loop) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Strategy: create a thread that sits on waitpid(), waiting for the inferior
+ // process to die, reaping it in the process. Arrange for the thread to have
+ // a pipe file descriptor that it can send a byte over when the waitpid
+ // completes. Have the main loop have a read object for the other side of
+ // the pipe, and have the callback for the read do the process termination
+ // message sending.
+
+ // Create a single-direction communication channel.
+ const bool child_inherits = false;
+ error = m_waitpid_pipe.CreateNew(child_inherits);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to create waitpid "
+ "communication pipe: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ // Hook up the waitpid reader callback.
+
+ // TODO make PipePOSIX derive from IOObject. This is goofy here.
+ const bool transfer_ownership = false;
+ auto io_sp = IOObjectSP(
+ new File(m_waitpid_pipe.GetReadFileDescriptor(), transfer_ownership));
+ m_waitpid_reader_handle = main_loop.RegisterReadObject(
+ io_sp, [this](MainLoopBase &) { HandleWaitpidResult(); }, error);
+
+ // Create the thread.
+ auto pthread_err =
+ ::pthread_create(&m_waitpid_thread, nullptr, WaitpidThread, this);
+ error.SetError(pthread_err, eErrorTypePOSIX);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to create waitpid "
+ "handling thread: %u (%s)",
+ __FUNCTION__, error.GetError(), error.AsCString());
+ return error;
+ }
+
+ return error;
+}
+
+void *NativeProcessDarwin::WaitpidThread(void *arg) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (!arg) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): cannot run waitpid "
+ "thread, mandatory process arg was null",
+ __FUNCTION__);
+ return nullptr;
+ }
+
+ return reinterpret_cast<NativeProcessDarwin *>(arg)->DoWaitpidThread();
+}
+
+void NativeProcessDarwin::MaybeRaiseThreadPriority() {
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
+ &thread_param) == 0) {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+#endif
+}
+
+void *NativeProcessDarwin::DoWaitpidThread() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (m_pid == LLDB_INVALID_PROCESS_ID) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): inferior process ID is "
+ "not set, cannot waitpid on it",
+ __FUNCTION__);
+ return nullptr;
+ }
+
+ // Name the thread.
+ pthread_setname_np("waitpid thread");
+
+ // Ensure we don't get CPU starved.
+ MaybeRaiseThreadPriority();
+
+ Status error;
+ int status = -1;
+
+ while (1) {
+ // Do a waitpid.
+ ::pid_t child_pid = ::waitpid(m_pid, &status, 0);
+ if (child_pid < 0)
+ error.SetErrorToErrno();
+ if (error.Fail()) {
+ if (error.GetError() == EINTR) {
+ // This is okay, we can keep going.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64
+ ", &status, 0) interrupted, continuing",
+ __FUNCTION__, m_pid);
+ continue;
+ }
+
+ // This error is not okay, abort.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64
+ ", &status, 0) aborting due to error: %u (%s)",
+ __FUNCTION__, m_pid, error.GetError(), error.AsCString());
+ break;
+ }
+
+ // Log the successful result.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64
+ ", &status, 0) => %i, status = %i",
+ __FUNCTION__, m_pid, child_pid, status);
+
+ // Handle the result.
+ if (WIFSTOPPED(status)) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64
+ ") received a stop, continuing waitpid() loop",
+ __FUNCTION__, m_pid);
+ continue;
+ } else // if (WIFEXITED(status) || WIFSIGNALED(status))
+ {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(pid = %" PRIu64 "): "
+ "waitpid thread is setting exit status for pid = "
+ "%i to %i",
+ __FUNCTION__, m_pid, child_pid, status);
+
+ error = SendInferiorExitStatusToMainLoop(child_pid, status);
+ return nullptr;
+ }
+ }
+
+ // We should never exit as long as our child process is alive. If we get
+ // here, something completely unexpected went wrong and we should exit.
+ if (log)
+ log->Printf(
+ "NativeProcessDarwin::%s(): internal error: waitpid thread "
+ "exited out of its main loop in an unexpected way. pid = %" PRIu64
+ ". Sending exit status of -1.",
+ __FUNCTION__, m_pid);
+
+ error = SendInferiorExitStatusToMainLoop((::pid_t)m_pid, -1);
+ return nullptr;
+}
+
+Status NativeProcessDarwin::SendInferiorExitStatusToMainLoop(::pid_t pid,
+ int status) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ size_t bytes_written = 0;
+
+ // Send the pid.
+ error = m_waitpid_pipe.Write(&pid, sizeof(pid), bytes_written);
+ if (error.Fail() || (bytes_written < sizeof(pid))) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() - failed to write "
+ "waitpid exiting pid to the pipe. Client will not "
+ "hear about inferior exit status!",
+ __FUNCTION__);
+ return error;
+ }
+
+ // Send the status.
+ bytes_written = 0;
+ error = m_waitpid_pipe.Write(&status, sizeof(status), bytes_written);
+ if (error.Fail() || (bytes_written < sizeof(status))) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() - failed to write "
+ "waitpid exit result to the pipe. Client will not "
+ "hear about inferior exit status!",
+ __FUNCTION__);
+ }
+ return error;
+}
+
+Status NativeProcessDarwin::HandleWaitpidResult() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Read the pid.
+ const bool notify_status = true;
+
+ ::pid_t pid = -1;
+ size_t bytes_read = 0;
+ error = m_waitpid_pipe.Read(&pid, sizeof(pid), bytes_read);
+ if (error.Fail() || (bytes_read < sizeof(pid))) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() - failed to read "
+ "waitpid exiting pid from the pipe. Will notify "
+ "as if parent process died with exit status -1.",
+ __FUNCTION__);
+ SetExitStatus(WaitStatus(WaitStatus::Exit, -1), notify_status);
+ return error;
+ }
+
+ // Read the status.
+ int status = -1;
+ error = m_waitpid_pipe.Read(&status, sizeof(status), bytes_read);
+ if (error.Fail() || (bytes_read < sizeof(status))) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() - failed to read "
+ "waitpid exit status from the pipe. Will notify "
+ "as if parent process died with exit status -1.",
+ __FUNCTION__);
+ SetExitStatus(WaitStatus(WaitStatus::Exit, -1), notify_status);
+ return error;
+ }
+
+ // Notify the monitor that our state has changed.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): main loop received waitpid "
+ "exit status info: pid=%i (%s), status=%i",
+ __FUNCTION__, pid,
+ (pid == m_pid) ? "the inferior" : "not the inferior", status);
+
+ SetExitStatus(WaitStatus::Decode(status), notify_status);
+ return error;
+}
+
+task_t NativeProcessDarwin::TaskPortForProcessID(Status &error,
+ bool force) const {
+ if ((m_task == TASK_NULL) || force) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (m_pid == LLDB_INVALID_PROCESS_ID) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): cannot get task due "
+ "to invalid pid",
+ __FUNCTION__);
+ return TASK_NULL;
+ }
+
+ const uint32_t num_retries = 10;
+ const uint32_t usec_interval = 10000;
+
+ mach_port_t task_self = mach_task_self();
+ task_t task = TASK_NULL;
+
+ for (uint32_t i = 0; i < num_retries; i++) {
+ kern_return_t err = ::task_for_pid(task_self, m_pid, &task);
+ if (err == 0) {
+ // Succeeded. Save and return it.
+ error.Clear();
+ m_task = task;
+ log->Printf("NativeProcessDarwin::%s(): ::task_for_pid("
+ "stub_port = 0x%4.4x, pid = %llu, &task) "
+ "succeeded: inferior task port = 0x%4.4x",
+ __FUNCTION__, task_self, m_pid, m_task);
+ return m_task;
+ } else {
+ // Failed to get the task for the inferior process.
+ error.SetError(err, eErrorTypeMachKernel);
+ if (log) {
+ log->Printf("NativeProcessDarwin::%s(): ::task_for_pid("
+ "stub_port = 0x%4.4x, pid = %llu, &task) "
+ "failed, err = 0x%8.8x (%s)",
+ __FUNCTION__, task_self, m_pid, err, error.AsCString());
+ }
+ }
+
+ // Sleep a bit and try again
+ ::usleep(usec_interval);
+ }
+
+ // We failed to get the task for the inferior process. Ensure that it is
+ // cleared out.
+ m_task = TASK_NULL;
+ }
+ return m_task;
+}
+
+void NativeProcessDarwin::AttachToInferior(MainLoop &mainloop, lldb::pid_t pid,
+ Status &error) {
+ error.SetErrorString("TODO: implement");
+}
+
+Status NativeProcessDarwin::PrivateResume() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+ m_auto_resume_signo = m_sent_interrupt_signo;
+
+ if (log) {
+ if (m_auto_resume_signo)
+ log->Printf("NativeProcessDarwin::%s(): task 0x%x resuming (with "
+ "unhandled interrupt signal %i)...",
+ __FUNCTION__, m_task, m_auto_resume_signo);
+ else
+ log->Printf("NativeProcessDarwin::%s(): task 0x%x resuming...",
+ __FUNCTION__, m_task);
+ }
+
+ error = ReplyToAllExceptions();
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): aborting, failed to "
+ "reply to exceptions: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ // bool stepOverBreakInstruction = step;
+
+ // Let the thread prepare to resume and see if any threads want us to step
+ // over a breakpoint instruction (ProcessWillResume will modify the value of
+ // stepOverBreakInstruction).
+ m_thread_list.ProcessWillResume(*this, m_thread_actions);
+
+ // Set our state accordingly
+ if (m_thread_actions.NumActionsWithState(eStateStepping))
+ SetState(eStateStepping);
+ else
+ SetState(eStateRunning);
+
+ // Now resume our task.
+ error = ResumeTask();
+ return error;
+}
+
+Status NativeProcessDarwin::ReplyToAllExceptions() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ TaskPortForProcessID(error);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): no task port, aborting",
+ __FUNCTION__);
+ return error;
+ }
+
+ std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+ if (m_exception_messages.empty()) {
+ // We're done.
+ return error;
+ }
+
+ size_t index = 0;
+ for (auto &message : m_exception_messages) {
+ if (log) {
+ log->Printf("NativeProcessDarwin::%s(): replying to exception "
+ "%zu...",
+ __FUNCTION__, index++);
+ }
+
+ int thread_reply_signal = 0;
+
+ const tid_t tid =
+ m_thread_list.GetThreadIDByMachPortNumber(message.state.thread_port);
+ const ResumeAction *action = nullptr;
+ if (tid != LLDB_INVALID_THREAD_ID)
+ action = m_thread_actions.GetActionForThread(tid, false);
+
+ if (action) {
+ thread_reply_signal = action->signal;
+ if (thread_reply_signal)
+ m_thread_actions.SetSignalHandledForThread(tid);
+ }
+
+ error = message.Reply(m_pid, m_task, thread_reply_signal);
+ if (error.Fail() && log) {
+ // We log any error here, but we don't stop the exception response
+ // handling.
+ log->Printf("NativeProcessDarwin::%s(): failed to reply to "
+ "exception: %s",
+ __FUNCTION__, error.AsCString());
+ error.Clear();
+ }
+ }
+
+ // Erase all exception message as we should have used and replied to them all
+ // already.
+ m_exception_messages.clear();
+ return error;
+}
+
+Status NativeProcessDarwin::ResumeTask() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ TaskPortForProcessID(error);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to get task port "
+ "for process when attempting to resume: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ if (m_task == TASK_NULL) {
+ error.SetErrorString("task port retrieval succeeded but task port is "
+ "null when attempting to resume the task");
+ return error;
+ }
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): requesting resume of task "
+ "0x%4.4x",
+ __FUNCTION__, m_task);
+
+ // Get the BasicInfo struct to verify that we're suspended before we try to
+ // resume the task.
+ struct task_basic_info task_info;
+ error = GetTaskBasicInfo(m_task, &task_info);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to get task "
+ "BasicInfo when attempting to resume: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ // task_resume isn't counted like task_suspend calls are, so if the task is
+ // not suspended, don't try and resume it since it is already running
+ if (task_info.suspend_count > 0) {
+ auto mach_err = ::task_resume(m_task);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (log) {
+ if (error.Success())
+ log->Printf("::task_resume(target_task = 0x%4.4x): success", m_task);
+ else
+ log->Printf("::task_resume(target_task = 0x%4.4x) error: %s", m_task,
+ error.AsCString());
+ }
+ } else {
+ if (log)
+ log->Printf("::task_resume(target_task = 0x%4.4x): ignored, "
+ "already running",
+ m_task);
+ }
+
+ return error;
+}
+
+bool NativeProcessDarwin::IsTaskValid() const {
+ if (m_task == TASK_NULL)
+ return false;
+
+ struct task_basic_info task_info;
+ return GetTaskBasicInfo(m_task, &task_info).Success();
+}
+
+bool NativeProcessDarwin::IsTaskValid(task_t task) const {
+ if (task == TASK_NULL)
+ return false;
+
+ struct task_basic_info task_info;
+ return GetTaskBasicInfo(task, &task_info).Success();
+}
+
+mach_port_t NativeProcessDarwin::GetExceptionPort() const {
+ return m_exception_port;
+}
+
+bool NativeProcessDarwin::IsExceptionPortValid() const {
+ return MACH_PORT_VALID(m_exception_port);
+}
+
+Status
+NativeProcessDarwin::GetTaskBasicInfo(task_t task,
+ struct task_basic_info *info) const {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Validate args.
+ if (info == NULL) {
+ error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): mandatory "
+ "info arg is null",
+ __FUNCTION__);
+ return error;
+ }
+
+ // Grab the task if we don't already have it.
+ if (task == TASK_NULL) {
+ error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): given task "
+ "is invalid",
+ __FUNCTION__);
+ }
+
+ mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
+ auto err = ::task_info(m_task, TASK_BASIC_INFO, (task_info_t)info, &count);
+ error.SetError(err, eErrorTypeMachKernel);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("::task_info(target_task = 0x%4.4x, "
+ "flavor = TASK_BASIC_INFO, task_info_out => %p, "
+ "task_info_outCnt => %u) failed: %u (%s)",
+ m_task, info, count, error.GetError(), error.AsCString());
+ return error;
+ }
+
+ Log *verbose_log(
+ GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (verbose_log) {
+ float user = (float)info->user_time.seconds +
+ (float)info->user_time.microseconds / 1000000.0f;
+ float system = (float)info->user_time.seconds +
+ (float)info->user_time.microseconds / 1000000.0f;
+ verbose_log->Printf("task_basic_info = { suspend_count = %i, "
+ "virtual_size = 0x%8.8llx, resident_size = "
+ "0x%8.8llx, user_time = %f, system_time = %f }",
+ info->suspend_count, (uint64_t)info->virtual_size,
+ (uint64_t)info->resident_size, user, system);
+ }
+ return error;
+}
+
+Status NativeProcessDarwin::SuspendTask() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (m_task == TASK_NULL) {
+ error.SetErrorString("task port is null, cannot suspend task");
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() failed: %s", __FUNCTION__,
+ error.AsCString());
+ return error;
+ }
+
+ auto mach_err = ::task_suspend(m_task);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Fail() && log)
+ log->Printf("::task_suspend(target_task = 0x%4.4x)", m_task);
+
+ return error;
+}
+
+Status NativeProcessDarwin::Resume(const ResumeActionList &resume_actions) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() called", __FUNCTION__);
+
+ if (CanResume()) {
+ m_thread_actions = resume_actions;
+ error = PrivateResume();
+ return error;
+ }
+
+ auto state = GetState();
+ if (state == eStateRunning) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): task 0x%x is already "
+ "running, ignoring...",
+ __FUNCTION__, TaskPortForProcessID(error));
+ return error;
+ }
+
+ // We can't resume from this state.
+ error.SetErrorStringWithFormat("task 0x%x has state %s, can't resume",
+ TaskPortForProcessID(error),
+ StateAsCString(state));
+ return error;
+}
+
+Status NativeProcessDarwin::Halt() {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::Detach() {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::Signal(int signo) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::Interrupt() {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::Kill() {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::ReadMemory(lldb::addr_t addr, void *buf,
+ size_t size, size_t &bytes_read) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf,
+ size_t size,
+ size_t &bytes_read) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::WriteMemory(lldb::addr_t addr, const void *buf,
+ size_t size, size_t &bytes_written) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::AllocateMemory(size_t size, uint32_t permissions,
+ lldb::addr_t &addr) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::DeallocateMemory(lldb::addr_t addr) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+lldb::addr_t NativeProcessDarwin::GetSharedLibraryInfoAddress() {
+ return LLDB_INVALID_ADDRESS;
+}
+
+size_t NativeProcessDarwin::UpdateThreads() { return 0; }
+
+bool NativeProcessDarwin::GetArchitecture(ArchSpec &arch) const {
+ return false;
+}
+
+Status NativeProcessDarwin::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+ bool hardware) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+void NativeProcessDarwin::DoStopIDBumped(uint32_t newBumpId) {}
+
+Status NativeProcessDarwin::GetLoadedModuleFileSpec(const char *module_path,
+ FileSpec &file_spec) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::GetFileLoadAddress(const llvm::StringRef &file_name,
+ lldb::addr_t &load_addr) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+// NativeProcessProtocol protected interface
+Status NativeProcessDarwin::GetSoftwareBreakpointTrapOpcode(
+ size_t trap_opcode_size_hint, size_t &actual_opcode_size,
+ const uint8_t *&trap_opcode_bytes) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.h
new file mode 100644
index 000000000000..6741d4ddc5d8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.h
@@ -0,0 +1,337 @@
+//===-- NativeProcessDarwin.h --------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef NativeProcessDarwin_h
+#define NativeProcessDarwin_h
+
+// NOTE: this code should only be compiled on Apple Darwin systems. It is
+// not cross-platform code and is not intended to build on any other platform.
+// Therefore, platform-specific headers and code are okay here.
+
+// C includes
+#include <mach/mach_types.h>
+
+// C++ includes
+#include <mutex>
+#include <unordered_set>
+
+#include "lldb/Host/Debug.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/lldb-types.h"
+
+#include "LaunchFlavor.h"
+#include "MachException.h"
+#include "NativeThreadDarwin.h"
+#include "NativeThreadListDarwin.h"
+
+namespace lldb_private {
+class Status;
+class Scalar;
+
+namespace process_darwin {
+
+/// \class NativeProcessDarwin
+/// Manages communication with the inferior (debugee) process.
+///
+/// Upon construction, this class prepares and launches an inferior process
+/// for debugging.
+///
+/// Changes in the inferior process state are broadcasted.
+class NativeProcessDarwin : public NativeProcessProtocol {
+ friend Status NativeProcessProtocol::Launch(
+ ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate,
+ MainLoop &mainloop, NativeProcessProtocolSP &process_sp);
+
+ friend Status NativeProcessProtocol::Attach(
+ lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate,
+ MainLoop &mainloop, NativeProcessProtocolSP &process_sp);
+
+public:
+ ~NativeProcessDarwin() override;
+
+ // NativeProcessProtocol Interface
+ Status Resume(const ResumeActionList &resume_actions) override;
+
+ Status Halt() override;
+
+ Status Detach() override;
+
+ Status Signal(int signo) override;
+
+ Status Interrupt() override;
+
+ Status Kill() override;
+
+ Status GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) override;
+
+ Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ size_t &bytes_read) override;
+
+ Status ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size,
+ size_t &bytes_read) override;
+
+ Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
+ size_t &bytes_written) override;
+
+ Status AllocateMemory(size_t size, uint32_t permissions,
+ lldb::addr_t &addr) override;
+
+ Status DeallocateMemory(lldb::addr_t addr) override;
+
+ lldb::addr_t GetSharedLibraryInfoAddress() override;
+
+ size_t UpdateThreads() override;
+
+ bool GetArchitecture(ArchSpec &arch) const override;
+
+ Status SetBreakpoint(lldb::addr_t addr, uint32_t size,
+ bool hardware) override;
+
+ void DoStopIDBumped(uint32_t newBumpId) override;
+
+ Status GetLoadedModuleFileSpec(const char *module_path,
+ FileSpec &file_spec) override;
+
+ Status GetFileLoadAddress(const llvm::StringRef &file_name,
+ lldb::addr_t &load_addr) override;
+
+ NativeThreadDarwinSP GetThreadByID(lldb::tid_t id);
+
+ task_t GetTask() const { return m_task; }
+
+ // Interface used by NativeRegisterContext-derived classes.
+ static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr,
+ void *data = nullptr, size_t data_size = 0,
+ long *result = nullptr);
+
+ bool SupportHardwareSingleStepping() const;
+
+protected:
+ // NativeProcessProtocol protected interface
+ Status
+ GetSoftwareBreakpointTrapOpcode(size_t trap_opcode_size_hint,
+ size_t &actual_opcode_size,
+ const uint8_t *&trap_opcode_bytes) override;
+
+private:
+ /// Mach task-related Member Variables
+
+ // The task port for the inferior process.
+ mutable task_t m_task;
+
+ // True if the inferior process did an exec since we started
+ // monitoring it.
+ bool m_did_exec;
+
+ // The CPU type of this process.
+ mutable cpu_type_t m_cpu_type;
+
+ /// Exception/Signal Handling Member Variables
+
+ // Exception port on which we will receive child exceptions
+ mach_port_t m_exception_port;
+
+ // Saved state of the child exception port prior to us installing
+ // our own intercepting port.
+ MachException::PortInfo m_exc_port_info;
+
+ // The thread that runs the Mach exception read and reply handler.
+ pthread_t m_exception_thread;
+
+ // TODO see if we can remove this if we get the exception collection
+ // and distribution to happen in a single-threaded fashion.
+ std::recursive_mutex m_exception_messages_mutex;
+
+ // A collection of exception messages caught when listening to the
+ // exception port.
+ MachException::Message::collection m_exception_messages;
+
+ // When we call MachProcess::Interrupt(), we want to send this
+ // signal (if non-zero).
+ int m_sent_interrupt_signo;
+
+ // If we resume the process and still haven't received our
+ // interrupt signal (if this is non-zero).
+ int m_auto_resume_signo;
+
+ /// Thread-related Member Variables
+ NativeThreadListDarwin m_thread_list;
+ ResumeActionList m_thread_actions;
+
+ /// Process Lifetime Member Variable
+
+ // The pipe over which the waitpid thread and the main loop will
+ // communicate.
+ Pipe m_waitpid_pipe;
+
+ // The thread that runs the waitpid handler.
+ pthread_t m_waitpid_thread;
+
+ // waitpid reader callback handle.
+ MainLoop::ReadHandleUP m_waitpid_reader_handle;
+
+ // Private Instance Methods
+ NativeProcessDarwin(lldb::pid_t pid, int pty_master_fd);
+
+ /// Finalize the launch.
+ ///
+ /// This method associates the NativeProcessDarwin instance with the host
+ /// process that was just launched. It peforms actions like attaching a
+ /// listener to the inferior exception port, ptracing the process, and the
+ /// like.
+ ///
+ /// \param[in] launch_flavor
+ /// The launch flavor that was used to launch the process.
+ ///
+ /// \param[in] main_loop
+ /// The main loop that will run the process monitor. Work
+ /// that needs to be done (e.g. reading files) gets registered
+ /// here along with callbacks to process the work.
+ ///
+ /// \return
+ /// Any error that occurred during the aforementioned
+ /// operations. Failure here will force termination of the
+ /// launched process and debugging session.
+ Status FinalizeLaunch(LaunchFlavor launch_flavor, MainLoop &main_loop);
+
+ Status SaveExceptionPortInfo();
+
+ void ExceptionMessageReceived(const MachException::Message &message);
+
+ void MaybeRaiseThreadPriority();
+
+ Status StartExceptionThread();
+
+ Status SendInferiorExitStatusToMainLoop(::pid_t pid, int status);
+
+ Status HandleWaitpidResult();
+
+ bool ProcessUsingSpringBoard() const;
+
+ bool ProcessUsingBackBoard() const;
+
+ static void *ExceptionThread(void *arg);
+
+ void *DoExceptionThread();
+
+ lldb::addr_t GetDYLDAllImageInfosAddress(Status &error) const;
+
+ static uint32_t GetCPUTypeForLocalProcess(::pid_t pid);
+
+ uint32_t GetCPUType() const;
+
+ task_t ExceptionMessageBundleComplete();
+
+ void StartSTDIOThread();
+
+ Status StartWaitpidThread(MainLoop &main_loop);
+
+ static void *WaitpidThread(void *arg);
+
+ void *DoWaitpidThread();
+
+ task_t TaskPortForProcessID(Status &error, bool force = false) const;
+
+ /// Attaches to an existing process. Forms the implementation of
+ /// Process::DoAttach.
+ void AttachToInferior(MainLoop &mainloop, lldb::pid_t pid, Status &error);
+
+ ::pid_t Attach(lldb::pid_t pid, Status &error);
+
+ Status PrivateResume();
+
+ Status ReplyToAllExceptions();
+
+ Status ResumeTask();
+
+ bool IsTaskValid() const;
+
+ bool IsTaskValid(task_t task) const;
+
+ mach_port_t GetExceptionPort() const;
+
+ bool IsExceptionPortValid() const;
+
+ Status GetTaskBasicInfo(task_t task, struct task_basic_info *info) const;
+
+ Status SuspendTask();
+
+ static Status SetDefaultPtraceOpts(const lldb::pid_t);
+
+ static void *MonitorThread(void *baton);
+
+ void MonitorCallback(lldb::pid_t pid, bool exited, int signal, int status);
+
+ void WaitForNewThread(::pid_t tid);
+
+ void MonitorSIGTRAP(const siginfo_t &info, NativeThreadDarwin &thread);
+
+ void MonitorTrace(NativeThreadDarwin &thread);
+
+ void MonitorBreakpoint(NativeThreadDarwin &thread);
+
+ void MonitorWatchpoint(NativeThreadDarwin &thread, uint32_t wp_index);
+
+ void MonitorSignal(const siginfo_t &info, NativeThreadDarwin &thread,
+ bool exited);
+
+ Status SetupSoftwareSingleStepping(NativeThreadDarwin &thread);
+
+ bool HasThreadNoLock(lldb::tid_t thread_id);
+
+ bool StopTrackingThread(lldb::tid_t thread_id);
+
+ NativeThreadDarwinSP AddThread(lldb::tid_t thread_id);
+
+ Status GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size);
+
+ Status FixupBreakpointPCAsNeeded(NativeThreadDarwin &thread);
+
+ /// Writes a siginfo_t structure corresponding to the given thread
+ /// ID to the memory region pointed to by \p siginfo.
+ Status GetSignalInfo(lldb::tid_t tid, void *siginfo);
+
+ /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG)
+ /// corresponding to the given thread ID to the memory pointed to by @p
+ /// message.
+ Status GetEventMessage(lldb::tid_t tid, unsigned long *message);
+
+ void NotifyThreadDeath(lldb::tid_t tid);
+
+ Status Detach(lldb::tid_t tid);
+
+ // This method is requests a stop on all threads which are still
+ // running. It sets up a deferred delegate notification, which will
+ // fire once threads report as stopped. The triggerring_tid will be
+ // set as the current thread (main stop reason).
+ void StopRunningThreads(lldb::tid_t triggering_tid);
+
+ // Notify the delegate if all threads have stopped.
+ void SignalIfAllThreadsStopped();
+
+ // Resume the given thread, optionally passing it the given signal.
+ // The type of resume operation (continue, single-step) depends on
+ // the state parameter.
+ Status ResumeThread(NativeThreadDarwin &thread, lldb::StateType state,
+ int signo);
+
+ void ThreadWasCreated(NativeThreadDarwin &thread);
+
+ void SigchldHandler();
+};
+
+} // namespace process_darwin
+} // namespace lldb_private
+
+#endif /* NativeProcessDarwin_h */
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.cpp
new file mode 100644
index 000000000000..bcd6d8c2c4c1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.cpp
@@ -0,0 +1,281 @@
+//===-- NativeThreadDarwin.cpp -------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeThreadDarwin.h"
+
+// C includes
+#include <libproc.h>
+
+// LLDB includes
+#include "lldb/Utility/Stream.h"
+
+#include "NativeProcessDarwin.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+
+uint64_t NativeThreadDarwin::GetGloballyUniqueThreadIDForMachPortID(
+ ::thread_t mach_port_id) {
+ thread_identifier_info_data_t tident;
+ mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
+
+ auto mach_err = ::thread_info(mach_port_id, THREAD_IDENTIFIER_INFO,
+ (thread_info_t)&tident, &tident_count);
+ if (mach_err != KERN_SUCCESS) {
+ // When we fail to get thread info for the supposed port, assume it is
+ // really a globally unique thread id already, or return the best thing we
+ // can, which is the thread port.
+ return mach_port_id;
+ }
+ return tident.thread_id;
+}
+
+NativeThreadDarwin::NativeThreadDarwin(NativeProcessDarwin *process,
+ bool is_64_bit,
+ lldb::tid_t unique_thread_id,
+ ::thread_t mach_thread_port)
+ : NativeThreadProtocol(process, unique_thread_id),
+ m_mach_thread_port(mach_thread_port), m_basic_info(),
+ m_proc_threadinfo() {}
+
+bool NativeThreadDarwin::GetIdentifierInfo() {
+ // Don't try to get the thread info once and cache it for the life of the
+ // thread. It changes over time, for instance if the thread name changes,
+ // then the thread_handle also changes... So you have to refetch it every
+ // time.
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ kern_return_t kret = ::thread_info(m_mach_thread_port, THREAD_IDENTIFIER_INFO,
+ (thread_info_t)&m_ident_info, &count);
+ return kret == KERN_SUCCESS;
+
+ return false;
+}
+
+std::string NativeThreadDarwin::GetName() {
+ std::string name;
+
+ if (GetIdentifierInfo()) {
+ auto process_sp = GetProcess();
+ if (!process_sp) {
+ name = "<unavailable>";
+ return name;
+ }
+
+ int len = ::proc_pidinfo(process_sp->GetID(), PROC_PIDTHREADINFO,
+ m_ident_info.thread_handle, &m_proc_threadinfo,
+ sizeof(m_proc_threadinfo));
+
+ if (len && m_proc_threadinfo.pth_name[0])
+ name = m_proc_threadinfo.pth_name;
+ }
+ return name;
+}
+
+lldb::StateType NativeThreadDarwin::GetState() {
+ // TODO implement
+ return eStateInvalid;
+}
+
+bool NativeThreadDarwin::GetStopReason(ThreadStopInfo &stop_info,
+ std::string &description) {
+ // TODO implement
+ return false;
+}
+
+NativeRegisterContextSP NativeThreadDarwin::GetRegisterContext() {
+ // TODO implement
+ return NativeRegisterContextSP();
+}
+
+Status NativeThreadDarwin::SetWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags, bool hardware) {
+ Status error;
+ error.SetErrorString("not yet implemented");
+ return error;
+}
+
+Status NativeThreadDarwin::RemoveWatchpoint(lldb::addr_t addr) {
+ Status error;
+ error.SetErrorString("not yet implemented");
+ return error;
+}
+
+void NativeThreadDarwin::Dump(Stream &stream) const {
+// This is what we really want once we have the thread class wired up.
+#if 0
+ DNBLogThreaded("[%3u] #%3u tid: 0x%8.8" PRIx64 ", pc: 0x%16.16" PRIx64 ", sp: 0x%16.16" PRIx64 ", user: %d.%6.6d, system: %d.%6.6d, cpu: %2d, policy: %2d, run_state: %2d (%s), flags: %2d, suspend_count: %2d (current %2d), sleep_time: %d",
+ index,
+ m_seq_id,
+ m_unique_id,
+ GetPC(INVALID_NUB_ADDRESS),
+ GetSP(INVALID_NUB_ADDRESS),
+ m_basic_info.user_time.seconds, m_basic_info.user_time.microseconds,
+ m_basic_info.system_time.seconds, m_basic_info.system_time.microseconds,
+ m_basic_info.cpu_usage,
+ m_basic_info.policy,
+ m_basic_info.run_state,
+ thread_run_state,
+ m_basic_info.flags,
+ m_basic_info.suspend_count, m_suspend_count,
+ m_basic_info.sleep_time);
+
+#else
+ // Here's all we have right now.
+ stream.Printf("tid: 0x%8.8" PRIx64 ", thread port: 0x%4.4x", GetID(),
+ m_mach_thread_port);
+#endif
+}
+
+bool NativeThreadDarwin::NotifyException(MachException::Data &exc) {
+// TODO implement this.
+#if 0
+ // Allow the arch specific protocol to process (MachException::Data &)exc
+ // first before possible reassignment of m_stop_exception with exc. See
+ // also MachThread::GetStopException().
+ bool handled = m_arch_up->NotifyException(exc);
+
+ if (m_stop_exception.IsValid())
+ {
+ // We may have more than one exception for a thread, but we need to
+ // only remember the one that we will say is the reason we stopped. We
+ // may have been single stepping and also gotten a signal exception, so
+ // just remember the most pertinent one.
+ if (m_stop_exception.IsBreakpoint())
+ m_stop_exception = exc;
+ }
+ else
+ {
+ m_stop_exception = exc;
+ }
+
+ return handled;
+#else
+ // Pretend we handled it.
+ return true;
+#endif
+}
+
+bool NativeThreadDarwin::ShouldStop(bool &step_more) const {
+// TODO: implement this
+#if 0
+ // See if this thread is at a breakpoint?
+ DNBBreakpoint *bp = CurrentBreakpoint();
+
+ if (bp)
+ {
+ // This thread is sitting at a breakpoint, ask the breakpoint if we
+ // should be stopping here.
+ return true;
+ }
+ else
+ {
+ if (m_arch_up->StepNotComplete())
+ {
+ step_more = true;
+ return false;
+ }
+ // The thread state is used to let us know what the thread was trying
+ // to do. MachThread::ThreadWillResume() will set the thread state to
+ // various values depending if the thread was the current thread and if
+ // it was to be single stepped, or resumed.
+ if (GetState() == eStateRunning)
+ {
+ // If our state is running, then we should continue as we are in
+ // the process of stepping over a breakpoint.
+ return false;
+ }
+ else
+ {
+ // Stop if we have any kind of valid exception for this thread.
+ if (GetStopException().IsValid())
+ return true;
+ }
+ }
+ return false;
+#else
+ return false;
+#endif
+}
+
+void NativeThreadDarwin::ThreadDidStop() {
+// TODO implement this.
+#if 0
+ // This thread has existed prior to resuming under debug nub control, and
+ // has just been stopped. Do any cleanup that needs to be done after
+ // running.
+
+ // The thread state and breakpoint will still have the same values as they
+ // had prior to resuming the thread, so it makes it easy to check if we
+ // were trying to step a thread, or we tried to resume while being at a
+ // breakpoint.
+
+ // When this method gets called, the process state is still in the state it
+ // was in while running so we can act accordingly.
+ m_arch_up->ThreadDidStop();
+
+
+ // We may have suspended this thread so the primary thread could step
+ // without worrying about race conditions, so lets restore our suspend
+ // count.
+ RestoreSuspendCountAfterStop();
+
+ // Update the basic information for a thread
+ MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info);
+
+ if (m_basic_info.suspend_count > 0)
+ SetState(eStateSuspended);
+ else
+ SetState(eStateStopped);
+#endif
+}
+
+bool NativeThreadDarwin::MachPortNumberIsValid(::thread_t thread) {
+ return thread != (::thread_t)(0);
+}
+
+const struct thread_basic_info *NativeThreadDarwin::GetBasicInfo() const {
+ if (GetBasicInfo(m_mach_thread_port, &m_basic_info))
+ return &m_basic_info;
+ return NULL;
+}
+
+bool NativeThreadDarwin::GetBasicInfo(::thread_t thread,
+ struct thread_basic_info *basicInfoPtr) {
+ if (MachPortNumberIsValid(thread)) {
+ unsigned int info_count = THREAD_BASIC_INFO_COUNT;
+ kern_return_t err = ::thread_info(thread, THREAD_BASIC_INFO,
+ (thread_info_t)basicInfoPtr, &info_count);
+ if (err == KERN_SUCCESS)
+ return true;
+ }
+ ::memset(basicInfoPtr, 0, sizeof(struct thread_basic_info));
+ return false;
+}
+
+bool NativeThreadDarwin::IsUserReady() const {
+ if (m_basic_info.run_state == 0)
+ GetBasicInfo();
+
+ switch (m_basic_info.run_state) {
+ default:
+ case TH_STATE_UNINTERRUPTIBLE:
+ break;
+
+ case TH_STATE_RUNNING:
+ case TH_STATE_STOPPED:
+ case TH_STATE_WAITING:
+ case TH_STATE_HALTED:
+ return true;
+ }
+ return false;
+}
+
+NativeProcessDarwinSP NativeThreadDarwin::GetNativeProcessDarwinSP() {
+ return std::static_pointer_cast<NativeProcessDarwin>(GetProcess());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.h
new file mode 100644
index 000000000000..616a9a7b9bf0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.h
@@ -0,0 +1,165 @@
+//===-- NativeThreadDarwin.h ---------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef NativeThreadDarwin_H
+#define NativeThreadDarwin_H
+
+// C includes
+#include <mach/mach_types.h>
+#include <sched.h>
+#include <sys/proc_info.h>
+
+// C++ includes
+#include <map>
+#include <memory>
+#include <string>
+
+// LLDB includes
+#include "lldb/Host/common/NativeThreadProtocol.h"
+#include "lldb/lldb-private-forward.h"
+
+#include "MachException.h"
+
+namespace lldb_private {
+namespace process_darwin {
+
+class NativeProcessDarwin;
+using NativeProcessDarwinSP = std::shared_ptr<NativeProcessDarwin>;
+
+class NativeThreadListDarwin;
+
+class NativeThreadDarwin : public NativeThreadProtocol {
+ friend class NativeProcessDarwin;
+ friend class NativeThreadListDarwin;
+
+public:
+ static uint64_t
+ GetGloballyUniqueThreadIDForMachPortID(::thread_t mach_port_id);
+
+ NativeThreadDarwin(NativeProcessDarwin *process, bool is_64_bit,
+ lldb::tid_t unique_thread_id = 0,
+ ::thread_t mach_thread_port = 0);
+
+ // NativeThreadProtocol Interface
+ std::string GetName() override;
+
+ lldb::StateType GetState() override;
+
+ bool GetStopReason(ThreadStopInfo &stop_info,
+ std::string &description) override;
+
+ NativeRegisterContextSP GetRegisterContext() override;
+
+ Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags,
+ bool hardware) override;
+
+ Status RemoveWatchpoint(lldb::addr_t addr) override;
+
+ // New methods that are fine for others to call.
+ void Dump(Stream &stream) const;
+
+private:
+ // Interface for friend classes
+
+ /// Resumes the thread. If \p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread.
+ Status Resume(uint32_t signo);
+
+ /// Single steps the thread. If \p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread.
+ Status SingleStep(uint32_t signo);
+
+ bool NotifyException(MachException::Data &exc);
+
+ bool ShouldStop(bool &step_more) const;
+
+ void ThreadDidStop();
+
+ void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr);
+
+ /// Return true if the thread is stopped.
+ /// If stopped by a signal, indicate the signo in the signo
+ /// argument. Otherwise, return LLDB_INVALID_SIGNAL_NUMBER.
+ bool IsStopped(int *signo);
+
+ const struct thread_basic_info *GetBasicInfo() const;
+
+ static bool GetBasicInfo(::thread_t thread,
+ struct thread_basic_info *basicInfoPtr);
+
+ bool IsUserReady() const;
+
+ void SetStoppedByExec();
+
+ void SetStoppedByBreakpoint();
+
+ void SetStoppedByWatchpoint(uint32_t wp_index);
+
+ bool IsStoppedAtBreakpoint();
+
+ bool IsStoppedAtWatchpoint();
+
+ void SetStoppedByTrace();
+
+ void SetStoppedWithNoReason();
+
+ void SetExited();
+
+ Status RequestStop();
+
+ /// Return the mach thread port number for this thread.
+ ///
+ /// \return
+ /// The mach port number for this thread. Returns NULL_THREAD
+ /// when the thread is invalid.
+ thread_t GetMachPortNumber() const { return m_mach_thread_port; }
+
+ static bool MachPortNumberIsValid(::thread_t thread);
+
+ // Private interface
+ bool GetIdentifierInfo();
+
+ void MaybeLogStateChange(lldb::StateType new_state);
+
+ NativeProcessDarwinSP GetNativeProcessDarwinSP();
+
+ void SetStopped();
+
+ inline void MaybePrepareSingleStepWorkaround();
+
+ inline void MaybeCleanupSingleStepWorkaround();
+
+ // Member Variables
+
+ // The mach thread port for the thread.
+ ::thread_t m_mach_thread_port;
+
+ // The most recently-retrieved thread basic info.
+ mutable ::thread_basic_info m_basic_info;
+
+ struct proc_threadinfo m_proc_threadinfo;
+
+ thread_identifier_info_data_t m_ident_info;
+
+#if 0
+ lldb::StateType m_state;
+ ThreadStopInfo m_stop_info;
+ NativeRegisterContextSP m_reg_context_sp;
+ std::string m_stop_description;
+ using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>;
+ WatchpointIndexMap m_watchpoint_index_map;
+ // cpu_set_t m_original_cpu_set; // For single-step workaround.
+#endif
+};
+
+typedef std::shared_ptr<NativeThreadDarwin> NativeThreadDarwinSP;
+
+} // namespace process_darwin
+} // namespace lldb_private
+
+#endif // #ifndef NativeThreadDarwin_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.cpp
new file mode 100644
index 000000000000..89de92a6df4d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.cpp
@@ -0,0 +1,702 @@
+//===-- NativeThreadListDarwin.cpp ------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeThreadListDarwin.h"
+
+// C includes
+#include <inttypes.h>
+#include <mach/vm_map.h>
+#include <sys/sysctl.h>
+
+// LLDB includes
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "NativeProcessDarwin.h"
+#include "NativeThreadDarwin.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+
+NativeThreadListDarwin::NativeThreadListDarwin()
+ : m_threads(), m_threads_mutex(), m_is_64_bit(false) {}
+
+NativeThreadListDarwin::~NativeThreadListDarwin() {}
+
+// These methods will be accessed directly from NativeThreadDarwin
+#if 0
+nub_state_t
+NativeThreadListDarwin::GetState(nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetState();
+ return eStateInvalid;
+}
+
+const char *
+NativeThreadListDarwin::GetName (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetName();
+ return NULL;
+}
+#endif
+
+// TODO: figure out if we need to add this to NativeThreadDarwin yet.
+#if 0
+ThreadInfo::QoS
+NativeThreadListDarwin::GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetRequestedQoS(tsd, dti_qos_class_index);
+ return ThreadInfo::QoS();
+}
+
+nub_addr_t
+NativeThreadListDarwin::GetPThreadT (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetPThreadT();
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t
+NativeThreadListDarwin::GetDispatchQueueT (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetDispatchQueueT();
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t
+NativeThreadListDarwin::GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetTSDAddressForThread(plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size);
+ return INVALID_NUB_ADDRESS;
+}
+#endif
+
+// TODO implement these
+#if 0
+nub_thread_t
+NativeThreadListDarwin::SetCurrentThread(nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ {
+ m_current_thread = thread_sp;
+ return tid;
+ }
+ return INVALID_NUB_THREAD;
+}
+
+
+bool
+NativeThreadListDarwin::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetStopException().GetStopInfo(stop_info);
+ return false;
+}
+
+bool
+NativeThreadListDarwin::GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info)
+{
+ thread_t mach_port_number = GetMachPortNumberByThreadID (tid);
+
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ return ::thread_info (mach_port_number, THREAD_IDENTIFIER_INFO, (thread_info_t)ident_info, &count) == KERN_SUCCESS;
+}
+
+void
+NativeThreadListDarwin::DumpThreadStoppedReason (nub_thread_t tid) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ thread_sp->GetStopException().DumpStopReason();
+}
+
+const char *
+NativeThreadListDarwin::GetThreadInfo (nub_thread_t tid) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetBasicInfoAsString();
+ return NULL;
+}
+
+#endif
+
+NativeThreadDarwinSP
+NativeThreadListDarwin::GetThreadByID(lldb::tid_t tid) const {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ for (auto thread_sp : m_threads) {
+ if (thread_sp && (thread_sp->GetID() == tid))
+ return thread_sp;
+ }
+ return NativeThreadDarwinSP();
+}
+
+NativeThreadDarwinSP NativeThreadListDarwin::GetThreadByMachPortNumber(
+ ::thread_t mach_port_number) const {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ for (auto thread_sp : m_threads) {
+ if (thread_sp && (thread_sp->GetMachPortNumber() == mach_port_number))
+ return thread_sp;
+ }
+ return NativeThreadDarwinSP();
+}
+
+lldb::tid_t NativeThreadListDarwin::GetThreadIDByMachPortNumber(
+ ::thread_t mach_port_number) const {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ for (auto thread_sp : m_threads) {
+ if (thread_sp && (thread_sp->GetMachPortNumber() == mach_port_number))
+ return thread_sp->GetID();
+ }
+ return LLDB_INVALID_THREAD_ID;
+}
+
+// TODO implement
+#if 0
+thread_t
+NativeThreadListDarwin::GetMachPortNumberByThreadID (nub_thread_t globally_unique_id) const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ MachThreadSP thread_sp;
+ const size_t num_threads = m_threads.size();
+ for (size_t idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->ThreadID() == globally_unique_id)
+ {
+ return m_threads[idx]->MachPortNumber();
+ }
+ }
+ return 0;
+}
+
+bool
+NativeThreadListDarwin::GetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *reg_value ) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetRegisterValue(set, reg, reg_value);
+
+ return false;
+}
+
+bool
+NativeThreadListDarwin::SetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *reg_value ) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->SetRegisterValue(set, reg, reg_value);
+
+ return false;
+}
+
+nub_size_t
+NativeThreadListDarwin::GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetRegisterContext (buf, buf_len);
+ return 0;
+}
+
+nub_size_t
+NativeThreadListDarwin::SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->SetRegisterContext (buf, buf_len);
+ return 0;
+}
+
+uint32_t
+NativeThreadListDarwin::SaveRegisterState (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->SaveRegisterState ();
+ return 0;
+}
+
+bool
+NativeThreadListDarwin::RestoreRegisterState (nub_thread_t tid, uint32_t save_id)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->RestoreRegisterState (save_id);
+ return 0;
+}
+#endif
+
+size_t NativeThreadListDarwin::GetNumberOfThreads() const {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ return static_cast<size_t>(m_threads.size());
+}
+
+// TODO implement
+#if 0
+nub_thread_t
+NativeThreadListDarwin::ThreadIDAtIndex (nub_size_t idx) const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ if (idx < m_threads.size())
+ return m_threads[idx]->ThreadID();
+ return INVALID_NUB_THREAD;
+}
+
+nub_thread_t
+NativeThreadListDarwin::CurrentThreadID ( )
+{
+ MachThreadSP thread_sp;
+ CurrentThread(thread_sp);
+ if (thread_sp.get())
+ return thread_sp->ThreadID();
+ return INVALID_NUB_THREAD;
+}
+
+#endif
+
+bool NativeThreadListDarwin::NotifyException(MachException::Data &exc) {
+ auto thread_sp = GetThreadByMachPortNumber(exc.thread_port);
+ if (thread_sp) {
+ thread_sp->NotifyException(exc);
+ return true;
+ }
+ return false;
+}
+
+void NativeThreadListDarwin::Clear() {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ m_threads.clear();
+}
+
+uint32_t NativeThreadListDarwin::UpdateThreadList(NativeProcessDarwin &process,
+ bool update,
+ collection *new_threads) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ if (log)
+ log->Printf("NativeThreadListDarwin::%s() (pid = %" PRIu64 ", update = "
+ "%u) process stop count = %u",
+ __FUNCTION__, process.GetID(), update, process.GetStopID());
+
+ if (process.GetStopID() == 0) {
+ // On our first stop, we'll record details like 32/64 bitness and select
+ // the proper architecture implementation.
+ //
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)process.GetID()};
+
+ struct kinfo_proc processInfo;
+ size_t bufsize = sizeof(processInfo);
+ if ((sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo,
+ &bufsize, NULL, 0) == 0) &&
+ (bufsize > 0)) {
+ if (processInfo.kp_proc.p_flag & P_LP64)
+ m_is_64_bit = true;
+ }
+
+// TODO implement architecture selection and abstraction.
+#if 0
+#if defined(__i386__) || defined(__x86_64__)
+ if (m_is_64_bit)
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64);
+ else
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_I386);
+#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ if (m_is_64_bit)
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64);
+ else
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM);
+#endif
+#endif
+ }
+
+ if (m_threads.empty() || update) {
+ thread_array_t thread_list = nullptr;
+ mach_msg_type_number_t thread_list_count = 0;
+ task_t task = process.GetTask();
+
+ Status error;
+ auto mach_err = ::task_threads(task, &thread_list, &thread_list_count);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("::task_threads(task = 0x%4.4x, thread_list => %p, "
+ "thread_list_count => %u) failed: %u (%s)",
+ task, thread_list, thread_list_count, error.GetError(),
+ error.AsCString());
+ return 0;
+ }
+
+ if (thread_list_count > 0) {
+ collection currThreads;
+ size_t idx;
+ // Iterator through the current thread list and see which threads we
+ // already have in our list (keep them), which ones we don't (add them),
+ // and which ones are not around anymore (remove them).
+ for (idx = 0; idx < thread_list_count; ++idx) {
+ // Get the Mach thread port.
+ const ::thread_t mach_port_num = thread_list[idx];
+
+ // Get the unique thread id for the mach port number.
+ uint64_t unique_thread_id =
+ NativeThreadDarwin::GetGloballyUniqueThreadIDForMachPortID(
+ mach_port_num);
+
+ // Retrieve the thread if it exists.
+ auto thread_sp = GetThreadByID(unique_thread_id);
+ if (thread_sp) {
+ // We are already tracking it. Keep the existing native thread
+ // instance.
+ currThreads.push_back(thread_sp);
+ } else {
+ // We don't have a native thread instance for this thread. Create it
+ // now.
+ thread_sp.reset(new NativeThreadDarwin(
+ &process, m_is_64_bit, unique_thread_id, mach_port_num));
+
+ // Add the new thread regardless of its is user ready state. Make
+ // sure the thread is ready to be displayed and shown to users before
+ // we add this thread to our list...
+ if (thread_sp->IsUserReady()) {
+ if (new_threads)
+ new_threads->push_back(thread_sp);
+
+ currThreads.push_back(thread_sp);
+ }
+ }
+ }
+
+ m_threads.swap(currThreads);
+ m_current_thread.reset();
+
+ // Free the vm memory given to us by ::task_threads()
+ vm_size_t thread_list_size =
+ (vm_size_t)(thread_list_count * sizeof(::thread_t));
+ ::vm_deallocate(::mach_task_self(), (vm_address_t)thread_list,
+ thread_list_size);
+ }
+ }
+ return static_cast<uint32_t>(m_threads.size());
+}
+
+// TODO implement
+#if 0
+
+void
+NativeThreadListDarwin::CurrentThread (MachThreadSP& thread_sp)
+{
+ // locker will keep a mutex locked until it goes out of scope
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ if (m_current_thread.get() == NULL)
+ {
+ // Figure out which thread is going to be our current thread. This is
+ // currently done by finding the first thread in the list that has a
+ // valid exception.
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->GetStopException().IsValid())
+ {
+ m_current_thread = m_threads[idx];
+ break;
+ }
+ }
+ }
+ thread_sp = m_current_thread;
+}
+
+#endif
+
+void NativeThreadListDarwin::Dump(Stream &stream) const {
+ bool first = true;
+
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ for (auto thread_sp : m_threads) {
+ if (thread_sp) {
+ // Handle newlines between thread entries.
+ if (first)
+ first = false;
+ else
+ stream.PutChar('\n');
+ thread_sp->Dump(stream);
+ }
+ }
+}
+
+void NativeThreadListDarwin::ProcessWillResume(
+ NativeProcessDarwin &process, const ResumeActionList &thread_actions) {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+
+ // Update our thread list, because sometimes libdispatch or the kernel will
+ // spawn threads while a task is suspended.
+ NativeThreadListDarwin::collection new_threads;
+
+// TODO implement this.
+#if 0
+ // First figure out if we were planning on running only one thread, and if
+ // so, force that thread to resume.
+ bool run_one_thread;
+ thread_t solo_thread = THREAD_NULL;
+ if ((thread_actions.GetSize() > 0) &&
+ (thread_actions.NumActionsWithState(eStateStepping) +
+ thread_actions.NumActionsWithState (eStateRunning) == 1))
+ {
+ run_one_thread = true;
+ const DNBThreadResumeAction *action_ptr = thread_actions.GetFirst();
+ size_t num_actions = thread_actions.GetSize();
+ for (size_t i = 0; i < num_actions; i++, action_ptr++)
+ {
+ if (action_ptr->state == eStateStepping || action_ptr->state == eStateRunning)
+ {
+ solo_thread = action_ptr->tid;
+ break;
+ }
+ }
+ }
+ else
+ run_one_thread = false;
+#endif
+
+ UpdateThreadList(process, true, &new_threads);
+
+#if 0
+ DNBThreadResumeAction resume_new_threads = { -1U, eStateRunning, 0, INVALID_NUB_ADDRESS };
+ // If we are planning to run only one thread, any new threads should be
+ // suspended.
+ if (run_one_thread)
+ resume_new_threads.state = eStateSuspended;
+
+ const size_t num_new_threads = new_threads.size();
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ MachThread *thread = m_threads[idx].get();
+ bool handled = false;
+ for (uint32_t new_idx = 0; new_idx < num_new_threads; ++new_idx)
+ {
+ if (thread == new_threads[new_idx].get())
+ {
+ thread->ThreadWillResume(&resume_new_threads);
+ handled = true;
+ break;
+ }
+ }
+
+ if (!handled)
+ {
+ const DNBThreadResumeAction *thread_action = thread_actions.GetActionForThread (thread->ThreadID(), true);
+ // There must always be a thread action for every thread.
+ assert (thread_action);
+ bool others_stopped = false;
+ if (solo_thread == thread->ThreadID())
+ others_stopped = true;
+ thread->ThreadWillResume (thread_action, others_stopped);
+ }
+ }
+
+ if (new_threads.size())
+ {
+ for (uint32_t idx = 0; idx < num_new_threads; ++idx)
+ {
+ DNBLogThreadedIf (LOG_THREAD, "NativeThreadListDarwin::ProcessWillResume (pid = %4.4x) stop-id=%u, resuming newly discovered thread: 0x%8.8" PRIx64 ", thread-is-user-ready=%i)",
+ process->ProcessID(),
+ process->StopCount(),
+ new_threads[idx]->ThreadID(),
+ new_threads[idx]->IsUserReady());
+ }
+ }
+#endif
+}
+
+uint32_t NativeThreadListDarwin::ProcessDidStop(NativeProcessDarwin &process) {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+
+ // Update our thread list.
+ UpdateThreadList(process, true);
+
+ for (auto thread_sp : m_threads) {
+ if (thread_sp)
+ thread_sp->ThreadDidStop();
+ }
+ return (uint32_t)m_threads.size();
+}
+
+// Check each thread in our thread list to see if we should notify our client
+// of the current halt in execution.
+//
+// Breakpoints can have callback functions associated with them than can return
+// true to stop, or false to continue executing the inferior.
+//
+// RETURNS
+// true if we should stop and notify our clients
+// false if we should resume our child process and skip notification
+bool NativeThreadListDarwin::ShouldStop(bool &step_more) {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ for (auto thread_sp : m_threads) {
+ if (thread_sp && thread_sp->ShouldStop(step_more))
+ return true;
+ }
+ return false;
+}
+
+// Implement.
+#if 0
+
+void
+NativeThreadListDarwin::NotifyBreakpointChanged (const DNBBreakpoint *bp)
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ m_threads[idx]->NotifyBreakpointChanged(bp);
+ }
+}
+
+
+uint32_t
+NativeThreadListDarwin::EnableHardwareBreakpoint (const DNBBreakpoint* bp) const
+{
+ if (bp != NULL)
+ {
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->EnableHardwareBreakpoint(bp);
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool
+NativeThreadListDarwin::DisableHardwareBreakpoint (const DNBBreakpoint* bp) const
+{
+ if (bp != NULL)
+ {
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->DisableHardwareBreakpoint(bp);
+ }
+ return false;
+}
+
+// DNBWatchpointSet() -> MachProcess::CreateWatchpoint() ->
+// MachProcess::EnableWatchpoint() ->
+// NativeThreadListDarwin::EnableHardwareWatchpoint().
+uint32_t
+NativeThreadListDarwin::EnableHardwareWatchpoint (const DNBBreakpoint* wp) const
+{
+ uint32_t hw_index = INVALID_NUB_HW_INDEX;
+ if (wp != NULL)
+ {
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+ // On Mac OS X we have to prime the control registers for new threads.
+ // We do this using the control register data for the first thread, for
+ // lack of a better way of choosing.
+ bool also_set_on_task = true;
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ if ((hw_index = m_threads[idx]->EnableHardwareWatchpoint(wp, also_set_on_task)) == INVALID_NUB_HW_INDEX)
+ {
+ // We know that idx failed for some reason. Let's rollback the
+ // transaction for [0, idx).
+ for (uint32_t i = 0; i < idx; ++i)
+ m_threads[i]->RollbackTransForHWP();
+ return INVALID_NUB_HW_INDEX;
+ }
+ also_set_on_task = false;
+ }
+ // Notify each thread to commit the pending transaction.
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->FinishTransForHWP();
+
+ }
+ return hw_index;
+}
+
+bool
+NativeThreadListDarwin::DisableHardwareWatchpoint (const DNBBreakpoint* wp) const
+{
+ if (wp != NULL)
+ {
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+
+ // On Mac OS X we have to prime the control registers for new threads.
+ // We do this using the control register data for the first thread, for
+ // lack of a better way of choosing.
+ bool also_set_on_task = true;
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ if (!m_threads[idx]->DisableHardwareWatchpoint(wp, also_set_on_task))
+ {
+ // We know that idx failed for some reason. Let's rollback the
+ // transaction for [0, idx).
+ for (uint32_t i = 0; i < idx; ++i)
+ m_threads[i]->RollbackTransForHWP();
+ return false;
+ }
+ also_set_on_task = false;
+ }
+ // Notify each thread to commit the pending transaction.
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->FinishTransForHWP();
+
+ return true;
+ }
+ return false;
+}
+
+uint32_t
+NativeThreadListDarwin::NumSupportedHardwareWatchpoints () const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+ // Use an arbitrary thread to retrieve the number of supported hardware
+ // watchpoints.
+ if (num_threads)
+ return m_threads[0]->NumSupportedHardwareWatchpoints();
+ return 0;
+}
+
+uint32_t
+NativeThreadListDarwin::GetThreadIndexForThreadStoppedWithSignal (const int signo) const
+{
+ PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex);
+ uint32_t should_stop = false;
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->GetStopException().SoftSignal () == signo)
+ return idx;
+ }
+ return UINT32_MAX;
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.h
new file mode 100644
index 000000000000..9ab0a7c8c802
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.h
@@ -0,0 +1,138 @@
+//===-- NativeThreadListDarwin.h --------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __NativeThreadListDarwin_h__
+#define __NativeThreadListDarwin_h__
+
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include "lldb/lldb-private-forward.h"
+#include "lldb/lldb-types.h"
+
+#include "MachException.h"
+
+// #include "ThreadInfo.h"
+
+namespace lldb_private {
+namespace process_darwin {
+
+class NativeBreakpointDarwin;
+class NativeProcessDarwin;
+
+class NativeThreadDarwin;
+using NativeThreadDarwinSP = std::shared_ptr<NativeThreadDarwin>;
+
+class NativeThreadListDarwin {
+public:
+ NativeThreadListDarwin();
+ ~NativeThreadListDarwin();
+
+ void Clear();
+
+ void Dump(Stream &stream) const;
+
+// These methods will be accessed directly from NativeThreadDarwin
+#if 0
+ bool GetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, DNBRegisterValue *reg_value) const;
+ bool SetRegisterValue (nub_thread_t tid, uint32_t set, uint32_t reg, const DNBRegisterValue *reg_value) const;
+ nub_size_t GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len);
+ nub_size_t SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len);
+ uint32_t SaveRegisterState (nub_thread_t tid);
+ bool RestoreRegisterState (nub_thread_t tid, uint32_t save_id);
+#endif
+
+ const char *GetThreadInfo(lldb::tid_t tid) const;
+
+ void ProcessWillResume(NativeProcessDarwin &process,
+ const ResumeActionList &thread_actions);
+
+ uint32_t ProcessDidStop(NativeProcessDarwin &process);
+
+ bool NotifyException(MachException::Data &exc);
+
+ bool ShouldStop(bool &step_more);
+
+// These methods will be accessed directly from NativeThreadDarwin
+#if 0
+ const char * GetName (nub_thread_t tid);
+ nub_state_t GetState (nub_thread_t tid);
+ nub_thread_t SetCurrentThread (nub_thread_t tid);
+#endif
+
+// TODO: figure out if we need to add this to NativeThreadDarwin yet.
+#if 0
+ ThreadInfo::QoS GetRequestedQoS (nub_thread_t tid, nub_addr_t tsd, uint64_t dti_qos_class_index);
+ nub_addr_t GetPThreadT (nub_thread_t tid);
+ nub_addr_t GetDispatchQueueT (nub_thread_t tid);
+ nub_addr_t GetTSDAddressForThread (nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset, uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size);
+#endif
+
+// These methods will be accessed directly from NativeThreadDarwin
+#if 0
+ bool GetThreadStoppedReason (nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const;
+ void DumpThreadStoppedReason (nub_thread_t tid) const;
+ bool GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info);
+#endif
+
+ size_t GetNumberOfThreads() const;
+
+ lldb::tid_t ThreadIDAtIndex(size_t idx) const;
+
+ lldb::tid_t GetCurrentThreadID();
+
+ NativeThreadDarwinSP GetCurrentThread();
+
+ void NotifyBreakpointChanged(const NativeBreakpointDarwin *bp);
+
+ uint32_t EnableHardwareBreakpoint(const NativeBreakpointDarwin *bp) const;
+
+ bool DisableHardwareBreakpoint(const NativeBreakpointDarwin *bp) const;
+
+ uint32_t EnableHardwareWatchpoint(const NativeBreakpointDarwin *wp) const;
+
+ bool DisableHardwareWatchpoint(const NativeBreakpointDarwin *wp) const;
+
+ uint32_t GetNumberOfSupportedHardwareWatchpoints() const;
+
+ size_t GetThreadIndexForThreadStoppedWithSignal(const int signo) const;
+
+ NativeThreadDarwinSP GetThreadByID(lldb::tid_t tid) const;
+
+ NativeThreadDarwinSP
+ GetThreadByMachPortNumber(::thread_t mach_port_number) const;
+
+ lldb::tid_t GetThreadIDByMachPortNumber(::thread_t mach_port_number) const;
+
+ thread_t GetMachPortNumberByThreadID(lldb::tid_t globally_unique_id) const;
+
+protected:
+ typedef std::vector<NativeThreadDarwinSP> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+ // Consider having this return an lldb_private::Status.
+ uint32_t UpdateThreadList(NativeProcessDarwin &process, bool update,
+ collection *num_threads = nullptr);
+
+ collection m_threads;
+ mutable std::recursive_mutex m_threads_mutex;
+ NativeThreadDarwinSP m_current_thread;
+ bool m_is_64_bit;
+};
+
+} // namespace process_darwin
+} // namespace lldb_private
+
+#endif // #ifndef __NativeThreadListDarwin_h__
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp
new file mode 100644
index 000000000000..3c6264dcaa1a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp
@@ -0,0 +1,632 @@
+//===-- FreeBSDThread.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+#include <pthread.h>
+#include <pthread_np.h>
+#include <stdlib.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/State.h"
+
+#include "FreeBSDThread.h"
+#include "POSIXStopInfo.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
+#include "Plugins/Process/Utility/UnwindLLDB.h"
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_arm.h"
+#include "RegisterContextPOSIXProcessMonitor_arm64.h"
+#include "RegisterContextPOSIXProcessMonitor_mips64.h"
+#include "RegisterContextPOSIXProcessMonitor_powerpc.h"
+#include "RegisterContextPOSIXProcessMonitor_x86.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/HostNativeThread.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/State.h"
+#include "llvm/ADT/SmallString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+FreeBSDThread::FreeBSDThread(Process &process, lldb::tid_t tid)
+ : Thread(process, tid), m_frame_up(), m_breakpoint(),
+ m_thread_name_valid(false), m_thread_name(), m_posix_thread(nullptr) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ LLDB_LOGV(log, "tid = {0}", tid);
+
+ // Set the current watchpoints for this thread.
+ Target &target = GetProcess()->GetTarget();
+ const WatchpointList &wp_list = target.GetWatchpointList();
+ size_t wp_size = wp_list.GetSize();
+
+ for (uint32_t wp_idx = 0; wp_idx < wp_size; wp_idx++) {
+ lldb::WatchpointSP wp = wp_list.GetByIndex(wp_idx);
+ if (wp.get() && wp->IsEnabled()) {
+ // This watchpoint as been enabled; obviously this "new" thread has been
+ // created since that watchpoint was enabled. Since the
+ // POSIXBreakpointProtocol has yet to be initialized, its
+ // m_watchpoints_initialized member will be FALSE. Attempting to read
+ // the debug status register to determine if a watchpoint has been hit
+ // would result in the zeroing of that register. Since the active debug
+ // registers would have been cloned when this thread was created, simply
+ // force the m_watchpoints_initized member to TRUE and avoid resetting
+ // dr6 and dr7.
+ GetPOSIXBreakpointProtocol()->ForceWatchpointsInitialized();
+ }
+ }
+}
+
+FreeBSDThread::~FreeBSDThread() { DestroyThread(); }
+
+ProcessMonitor &FreeBSDThread::GetMonitor() {
+ ProcessSP base = GetProcess();
+ ProcessFreeBSD &process = static_cast<ProcessFreeBSD &>(*base);
+ return process.GetMonitor();
+}
+
+void FreeBSDThread::RefreshStateAfterStop() {
+ // Invalidate all registers in our register context. We don't set "force" to
+ // true because the stop reply packet might have had some register values
+ // that were expedited and these will already be copied into the register
+ // context by the time this function gets called. The KDPRegisterContext
+ // class has been made smart enough to detect when it needs to invalidate
+ // which registers are valid by putting hooks in the register read and
+ // register supply functions where they check the process stop ID and do the
+ // right thing. if (StateIsStoppedState(GetState())
+ {
+ const bool force = false;
+ GetRegisterContext()->InvalidateIfNeeded(force);
+ }
+}
+
+const char *FreeBSDThread::GetInfo() { return nullptr; }
+
+void FreeBSDThread::SetName(const char *name) {
+ m_thread_name_valid = (name && name[0]);
+ if (m_thread_name_valid)
+ m_thread_name.assign(name);
+ else
+ m_thread_name.clear();
+}
+
+const char *FreeBSDThread::GetName() {
+ if (!m_thread_name_valid) {
+ m_thread_name.clear();
+ int pid = GetProcess()->GetID();
+
+ struct kinfo_proc *kp = nullptr, *nkp;
+ size_t len = 0;
+ int error;
+ int ctl[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD,
+ pid};
+
+ while (1) {
+ error = sysctl(ctl, 4, kp, &len, nullptr, 0);
+ if (kp == nullptr || (error != 0 && errno == ENOMEM)) {
+ // Add extra space in case threads are added before next call.
+ len += sizeof(*kp) + len / 10;
+ nkp = (struct kinfo_proc *)realloc(kp, len);
+ if (nkp == nullptr) {
+ free(kp);
+ return nullptr;
+ }
+ kp = nkp;
+ continue;
+ }
+ if (error != 0)
+ len = 0;
+ break;
+ }
+
+ for (size_t i = 0; i < len / sizeof(*kp); i++) {
+ if (kp[i].ki_tid == (lwpid_t)GetID()) {
+ m_thread_name.append(kp[i].ki_tdname,
+ kp[i].ki_tdname + strlen(kp[i].ki_tdname));
+ break;
+ }
+ }
+ free(kp);
+ m_thread_name_valid = true;
+ }
+
+ if (m_thread_name.empty())
+ return nullptr;
+ return m_thread_name.c_str();
+}
+
+lldb::RegisterContextSP FreeBSDThread::GetRegisterContext() {
+ if (!m_reg_context_sp) {
+ m_posix_thread = nullptr;
+
+ RegisterInfoInterface *reg_interface = nullptr;
+ const ArchSpec &target_arch = GetProcess()->GetTarget().GetArchitecture();
+
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ reg_interface = new RegisterInfoPOSIX_arm64(target_arch);
+ break;
+ case llvm::Triple::arm:
+ reg_interface = new RegisterInfoPOSIX_arm(target_arch);
+ break;
+ case llvm::Triple::ppc:
+#ifndef __powerpc64__
+ reg_interface = new RegisterContextFreeBSD_powerpc32(target_arch);
+ break;
+#endif
+ case llvm::Triple::ppc64:
+ reg_interface = new RegisterContextFreeBSD_powerpc64(target_arch);
+ break;
+ case llvm::Triple::mips64:
+ reg_interface = new RegisterContextFreeBSD_mips64(target_arch);
+ break;
+ case llvm::Triple::x86:
+ reg_interface = new RegisterContextFreeBSD_i386(target_arch);
+ break;
+ case llvm::Triple::x86_64:
+ reg_interface = new RegisterContextFreeBSD_x86_64(target_arch);
+ break;
+ default:
+ llvm_unreachable("CPU not supported");
+ }
+
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::aarch64: {
+ RegisterContextPOSIXProcessMonitor_arm64 *reg_ctx =
+ new RegisterContextPOSIXProcessMonitor_arm64(*this, 0, reg_interface);
+ m_posix_thread = reg_ctx;
+ m_reg_context_sp.reset(reg_ctx);
+ break;
+ }
+ case llvm::Triple::arm: {
+ RegisterContextPOSIXProcessMonitor_arm *reg_ctx =
+ new RegisterContextPOSIXProcessMonitor_arm(*this, 0, reg_interface);
+ m_posix_thread = reg_ctx;
+ m_reg_context_sp.reset(reg_ctx);
+ break;
+ }
+ case llvm::Triple::mips64: {
+ RegisterContextPOSIXProcessMonitor_mips64 *reg_ctx =
+ new RegisterContextPOSIXProcessMonitor_mips64(*this, 0,
+ reg_interface);
+ m_posix_thread = reg_ctx;
+ m_reg_context_sp.reset(reg_ctx);
+ break;
+ }
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64: {
+ RegisterContextPOSIXProcessMonitor_powerpc *reg_ctx =
+ new RegisterContextPOSIXProcessMonitor_powerpc(*this, 0,
+ reg_interface);
+ m_posix_thread = reg_ctx;
+ m_reg_context_sp.reset(reg_ctx);
+ break;
+ }
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64: {
+ RegisterContextPOSIXProcessMonitor_x86_64 *reg_ctx =
+ new RegisterContextPOSIXProcessMonitor_x86_64(*this, 0,
+ reg_interface);
+ m_posix_thread = reg_ctx;
+ m_reg_context_sp.reset(reg_ctx);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return m_reg_context_sp;
+}
+
+lldb::RegisterContextSP
+FreeBSDThread::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) {
+ lldb::RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ LLDB_LOGV(log, "called");
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+ if (concrete_frame_idx == 0)
+ reg_ctx_sp = GetRegisterContext();
+ else {
+ assert(GetUnwinder());
+ reg_ctx_sp = GetUnwinder()->CreateRegisterContextForFrame(frame);
+ }
+
+ return reg_ctx_sp;
+}
+
+lldb::addr_t FreeBSDThread::GetThreadPointer() {
+ ProcessMonitor &monitor = GetMonitor();
+ addr_t addr;
+ if (monitor.ReadThreadPointer(GetID(), addr))
+ return addr;
+ else
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool FreeBSDThread::CalculateStopInfo() {
+ SetStopInfo(m_stop_info_sp);
+ return true;
+}
+
+Unwind *FreeBSDThread::GetUnwinder() {
+ if (!m_unwinder_up)
+ m_unwinder_up.reset(new UnwindLLDB(*this));
+
+ return m_unwinder_up.get();
+}
+
+void FreeBSDThread::DidStop() {
+ // Don't set the thread state to stopped unless we really stopped.
+}
+
+void FreeBSDThread::WillResume(lldb::StateType resume_state) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ if (log)
+ log->Printf("tid %" PRIu64 " resume_state = %s", GetID(),
+ lldb_private::StateAsCString(resume_state));
+ ProcessSP process_sp(GetProcess());
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(process_sp.get());
+ int signo = GetResumeSignal();
+ bool signo_valid = process->GetUnixSignals()->SignalIsValid(signo);
+
+ switch (resume_state) {
+ case eStateSuspended:
+ case eStateStopped:
+ process->m_suspend_tids.push_back(GetID());
+ break;
+ case eStateRunning:
+ process->m_run_tids.push_back(GetID());
+ if (signo_valid)
+ process->m_resume_signo = signo;
+ break;
+ case eStateStepping:
+ process->m_step_tids.push_back(GetID());
+ if (signo_valid)
+ process->m_resume_signo = signo;
+ break;
+ default:
+ break;
+ }
+}
+
+bool FreeBSDThread::Resume() {
+ lldb::StateType resume_state = GetResumeState();
+ ProcessMonitor &monitor = GetMonitor();
+ bool status;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ if (log)
+ log->Printf("FreeBSDThread::%s (), resume_state = %s", __FUNCTION__,
+ StateAsCString(resume_state));
+
+ switch (resume_state) {
+ default:
+ assert(false && "Unexpected state for resume!");
+ status = false;
+ break;
+
+ case lldb::eStateRunning:
+ SetState(resume_state);
+ status = monitor.Resume(GetID(), GetResumeSignal());
+ break;
+
+ case lldb::eStateStepping:
+ SetState(resume_state);
+ status = monitor.SingleStep(GetID(), GetResumeSignal());
+ break;
+ case lldb::eStateStopped:
+ case lldb::eStateSuspended:
+ status = true;
+ break;
+ }
+
+ return status;
+}
+
+void FreeBSDThread::Notify(const ProcessMessage &message) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ if (log)
+ log->Printf("FreeBSDThread::%s () message kind = '%s' for tid %" PRIu64,
+ __FUNCTION__, message.PrintKind(), GetID());
+
+ switch (message.GetKind()) {
+ default:
+ assert(false && "Unexpected message kind!");
+ break;
+
+ case ProcessMessage::eExitMessage:
+ // Nothing to be done.
+ break;
+
+ case ProcessMessage::eLimboMessage:
+ LimboNotify(message);
+ break;
+
+ case ProcessMessage::eCrashMessage:
+ case ProcessMessage::eSignalMessage:
+ SignalNotify(message);
+ break;
+
+ case ProcessMessage::eSignalDeliveredMessage:
+ SignalDeliveredNotify(message);
+ break;
+
+ case ProcessMessage::eTraceMessage:
+ TraceNotify(message);
+ break;
+
+ case ProcessMessage::eBreakpointMessage:
+ BreakNotify(message);
+ break;
+
+ case ProcessMessage::eWatchpointMessage:
+ WatchNotify(message);
+ break;
+
+ case ProcessMessage::eExecMessage:
+ ExecNotify(message);
+ break;
+ }
+}
+
+bool FreeBSDThread::EnableHardwareWatchpoint(Watchpoint *wp) {
+ bool wp_set = false;
+ if (wp) {
+ addr_t wp_addr = wp->GetLoadAddress();
+ size_t wp_size = wp->GetByteSize();
+ bool wp_read = wp->WatchpointRead();
+ bool wp_write = wp->WatchpointWrite();
+ uint32_t wp_hw_index = wp->GetHardwareIndex();
+ POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol();
+ if (reg_ctx)
+ wp_set = reg_ctx->SetHardwareWatchpointWithIndex(
+ wp_addr, wp_size, wp_read, wp_write, wp_hw_index);
+ }
+ return wp_set;
+}
+
+bool FreeBSDThread::DisableHardwareWatchpoint(Watchpoint *wp) {
+ bool result = false;
+ if (wp) {
+ lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext();
+ if (reg_ctx_sp.get())
+ result = reg_ctx_sp->ClearHardwareWatchpoint(wp->GetHardwareIndex());
+ }
+ return result;
+}
+
+uint32_t FreeBSDThread::NumSupportedHardwareWatchpoints() {
+ lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext();
+ if (reg_ctx_sp.get())
+ return reg_ctx_sp->NumSupportedHardwareWatchpoints();
+ return 0;
+}
+
+uint32_t FreeBSDThread::FindVacantWatchpointIndex() {
+ uint32_t hw_index = LLDB_INVALID_INDEX32;
+ uint32_t num_hw_wps = NumSupportedHardwareWatchpoints();
+ uint32_t wp_idx;
+ POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol();
+ if (reg_ctx) {
+ for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) {
+ if (reg_ctx->IsWatchpointVacant(wp_idx)) {
+ hw_index = wp_idx;
+ break;
+ }
+ }
+ }
+ return hw_index;
+}
+
+void FreeBSDThread::BreakNotify(const ProcessMessage &message) {
+ bool status;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+
+ assert(GetRegisterContext());
+ status = GetPOSIXBreakpointProtocol()->UpdateAfterBreakpoint();
+ assert(status && "Breakpoint update failed!");
+
+ // With our register state restored, resolve the breakpoint object
+ // corresponding to our current PC.
+ assert(GetRegisterContext());
+ lldb::addr_t pc = GetRegisterContext()->GetPC();
+ if (log)
+ log->Printf("FreeBSDThread::%s () PC=0x%8.8" PRIx64, __FUNCTION__, pc);
+ lldb::BreakpointSiteSP bp_site(
+ GetProcess()->GetBreakpointSiteList().FindByAddress(pc));
+
+ // If the breakpoint is for this thread, then we'll report the hit, but if it
+ // is for another thread, we create a stop reason with should_stop=false. If
+ // there is no breakpoint location, then report an invalid stop reason. We
+ // don't need to worry about stepping over the breakpoint here, that will be
+ // taken care of when the thread resumes and notices that there's a
+ // breakpoint under the pc.
+ if (bp_site) {
+ lldb::break_id_t bp_id = bp_site->GetID();
+ // If we have an operating system plug-in, we might have set a thread
+ // specific breakpoint using the operating system thread ID, so we can't
+ // make any assumptions about the thread ID so we must always report the
+ // breakpoint regardless of the thread.
+ if (bp_site->ValidForThisThread(this) ||
+ GetProcess()->GetOperatingSystem() != nullptr)
+ SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id));
+ else {
+ const bool should_stop = false;
+ SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id,
+ should_stop));
+ }
+ } else
+ SetStopInfo(StopInfoSP());
+}
+
+void FreeBSDThread::WatchNotify(const ProcessMessage &message) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+
+ lldb::addr_t halt_addr = message.GetHWAddress();
+ if (log)
+ log->Printf(
+ "FreeBSDThread::%s () Hardware Watchpoint Address = 0x%8.8" PRIx64,
+ __FUNCTION__, halt_addr);
+
+ POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol();
+ if (reg_ctx) {
+ uint32_t num_hw_wps = reg_ctx->NumSupportedHardwareWatchpoints();
+ uint32_t wp_idx;
+ for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) {
+ if (reg_ctx->IsWatchpointHit(wp_idx)) {
+ // Clear the watchpoint hit here
+ reg_ctx->ClearWatchpointHits();
+ break;
+ }
+ }
+
+ if (wp_idx == num_hw_wps)
+ return;
+
+ Target &target = GetProcess()->GetTarget();
+ lldb::addr_t wp_monitor_addr = reg_ctx->GetWatchpointAddress(wp_idx);
+ const WatchpointList &wp_list = target.GetWatchpointList();
+ lldb::WatchpointSP wp_sp = wp_list.FindByAddress(wp_monitor_addr);
+
+ assert(wp_sp.get() && "No watchpoint found");
+ SetStopInfo(
+ StopInfo::CreateStopReasonWithWatchpointID(*this, wp_sp->GetID()));
+ }
+}
+
+void FreeBSDThread::TraceNotify(const ProcessMessage &message) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+
+ // Try to resolve the breakpoint object corresponding to the current PC.
+ assert(GetRegisterContext());
+ lldb::addr_t pc = GetRegisterContext()->GetPC();
+ if (log)
+ log->Printf("FreeBSDThread::%s () PC=0x%8.8" PRIx64, __FUNCTION__, pc);
+ lldb::BreakpointSiteSP bp_site(
+ GetProcess()->GetBreakpointSiteList().FindByAddress(pc));
+
+ // If the current pc is a breakpoint site then set the StopInfo to
+ // Breakpoint. Otherwise, set the StopInfo to Watchpoint or Trace. If we have
+ // an operating system plug-in, we might have set a thread specific
+ // breakpoint using the operating system thread ID, so we can't make any
+ // assumptions about the thread ID so we must always report the breakpoint
+ // regardless of the thread.
+ if (bp_site && (bp_site->ValidForThisThread(this) ||
+ GetProcess()->GetOperatingSystem() != nullptr))
+ SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID(
+ *this, bp_site->GetID()));
+ else {
+ POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol();
+ if (reg_ctx) {
+ uint32_t num_hw_wps = reg_ctx->NumSupportedHardwareWatchpoints();
+ uint32_t wp_idx;
+ for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) {
+ if (reg_ctx->IsWatchpointHit(wp_idx)) {
+ WatchNotify(message);
+ return;
+ }
+ }
+ }
+ SetStopInfo(StopInfo::CreateStopReasonToTrace(*this));
+ }
+}
+
+void FreeBSDThread::LimboNotify(const ProcessMessage &message) {
+ SetStopInfo(lldb::StopInfoSP(new POSIXLimboStopInfo(*this)));
+}
+
+void FreeBSDThread::SignalNotify(const ProcessMessage &message) {
+ int signo = message.GetSignal();
+ if (message.GetKind() == ProcessMessage::eCrashMessage) {
+ std::string stop_description = GetCrashReasonString(
+ message.GetCrashReason(), message.GetFaultAddress());
+ SetStopInfo(StopInfo::CreateStopReasonWithSignal(
+ *this, signo, stop_description.c_str()));
+ } else {
+ SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, signo));
+ }
+}
+
+void FreeBSDThread::SignalDeliveredNotify(const ProcessMessage &message) {
+ int signo = message.GetSignal();
+ SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, signo));
+}
+
+unsigned FreeBSDThread::GetRegisterIndexFromOffset(unsigned offset) {
+ unsigned reg = LLDB_INVALID_REGNUM;
+ ArchSpec arch = HostInfo::GetArchitecture();
+
+ switch (arch.GetMachine()) {
+ default:
+ llvm_unreachable("CPU type not supported!");
+ break;
+
+ case llvm::Triple::aarch64:
+ case llvm::Triple::arm:
+ case llvm::Triple::mips64:
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64: {
+ POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol();
+ reg = reg_ctx->GetRegisterIndexFromOffset(offset);
+ } break;
+ }
+ return reg;
+}
+
+void FreeBSDThread::ExecNotify(const ProcessMessage &message) {
+ SetStopInfo(StopInfo::CreateStopReasonWithExec(*this));
+}
+
+const char *FreeBSDThread::GetRegisterName(unsigned reg) {
+ const char *name = nullptr;
+ ArchSpec arch = HostInfo::GetArchitecture();
+
+ switch (arch.GetMachine()) {
+ default:
+ assert(false && "CPU type not supported!");
+ break;
+
+ case llvm::Triple::aarch64:
+ case llvm::Triple::arm:
+ case llvm::Triple::mips64:
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ name = GetRegisterContext()->GetRegisterName(reg);
+ break;
+ }
+ return name;
+}
+
+const char *FreeBSDThread::GetRegisterNameFromOffset(unsigned offset) {
+ return GetRegisterName(GetRegisterIndexFromOffset(offset));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.h
new file mode 100644
index 000000000000..6d3c253a519e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.h
@@ -0,0 +1,113 @@
+//===-- FreeBSDThread.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_FreeBSDThread_H_
+#define liblldb_FreeBSDThread_H_
+
+#include <memory>
+#include <string>
+
+#include "RegisterContextPOSIX.h"
+#include "lldb/Target/Thread.h"
+
+class ProcessMessage;
+class ProcessMonitor;
+class POSIXBreakpointProtocol;
+
+// @class FreeBSDThread
+// Abstraction of a FreeBSD thread.
+class FreeBSDThread : public lldb_private::Thread {
+public:
+ // Constructors and destructors
+ FreeBSDThread(lldb_private::Process &process, lldb::tid_t tid);
+
+ virtual ~FreeBSDThread();
+
+ // POSIXThread
+ void RefreshStateAfterStop() override;
+
+ // This notifies the thread when a private stop occurs.
+ void DidStop() override;
+
+ const char *GetInfo() override;
+
+ void SetName(const char *name) override;
+
+ const char *GetName() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+
+ lldb::addr_t GetThreadPointer() override;
+
+ // These functions provide a mapping from the register offset
+ // back to the register index or name for use in debugging or log
+ // output.
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ const char *GetRegisterName(unsigned reg);
+
+ const char *GetRegisterNameFromOffset(unsigned offset);
+
+ // These methods form a specialized interface to POSIX threads.
+ //
+ bool Resume();
+
+ void Notify(const ProcessMessage &message);
+
+ // These methods provide an interface to watchpoints
+ //
+ bool EnableHardwareWatchpoint(lldb_private::Watchpoint *wp);
+
+ bool DisableHardwareWatchpoint(lldb_private::Watchpoint *wp);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+ uint32_t FindVacantWatchpointIndex();
+
+protected:
+ POSIXBreakpointProtocol *GetPOSIXBreakpointProtocol() {
+ if (!m_reg_context_sp)
+ m_reg_context_sp = GetRegisterContext();
+ return m_posix_thread;
+ }
+
+ std::unique_ptr<lldb_private::StackFrame> m_frame_up;
+
+ lldb::BreakpointSiteSP m_breakpoint;
+
+ bool m_thread_name_valid;
+ std::string m_thread_name;
+ POSIXBreakpointProtocol *m_posix_thread;
+
+ ProcessMonitor &GetMonitor();
+
+ bool CalculateStopInfo() override;
+
+ void BreakNotify(const ProcessMessage &message);
+ void WatchNotify(const ProcessMessage &message);
+ virtual void TraceNotify(const ProcessMessage &message);
+ void LimboNotify(const ProcessMessage &message);
+ void SignalNotify(const ProcessMessage &message);
+ void SignalDeliveredNotify(const ProcessMessage &message);
+ void CrashNotify(const ProcessMessage &message);
+ void ExitNotify(const ProcessMessage &message);
+ void ExecNotify(const ProcessMessage &message);
+
+ lldb_private::Unwind *GetUnwinder() override;
+
+ // FreeBSDThread internal API.
+
+ // POSIXThread override
+ virtual void WillResume(lldb::StateType resume_state) override;
+};
+
+#endif // #ifndef liblldb_FreeBSDThread_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.cpp
new file mode 100644
index 000000000000..71f012944a9a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.cpp
@@ -0,0 +1,44 @@
+//===-- POSIXStopInfo.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "POSIXStopInfo.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//===----------------------------------------------------------------------===//
+// POSIXLimboStopInfo
+
+POSIXLimboStopInfo::~POSIXLimboStopInfo() {}
+
+lldb::StopReason POSIXLimboStopInfo::GetStopReason() const {
+ return lldb::eStopReasonThreadExiting;
+}
+
+const char *POSIXLimboStopInfo::GetDescription() { return "thread exiting"; }
+
+bool POSIXLimboStopInfo::ShouldStop(Event *event_ptr) { return false; }
+
+bool POSIXLimboStopInfo::ShouldNotify(Event *event_ptr) { return false; }
+
+//===----------------------------------------------------------------------===//
+// POSIXNewThreadStopInfo
+
+POSIXNewThreadStopInfo::~POSIXNewThreadStopInfo() {}
+
+lldb::StopReason POSIXNewThreadStopInfo::GetStopReason() const {
+ return lldb::eStopReasonNone;
+}
+
+const char *POSIXNewThreadStopInfo::GetDescription() {
+ return "thread spawned";
+}
+
+bool POSIXNewThreadStopInfo::ShouldStop(Event *event_ptr) { return false; }
+
+bool POSIXNewThreadStopInfo::ShouldNotify(Event *event_ptr) { return false; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.h
new file mode 100644
index 000000000000..88fb7f31fe06
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.h
@@ -0,0 +1,66 @@
+//===-- POSIXStopInfo.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_POSIXStopInfo_H_
+#define liblldb_POSIXStopInfo_H_
+
+#include "FreeBSDThread.h"
+#include "Plugins/Process/POSIX/CrashReason.h"
+#include "lldb/Target/StopInfo.h"
+#include <string>
+
+//===----------------------------------------------------------------------===//
+/// \class POSIXStopInfo
+/// Simple base class for all POSIX-specific StopInfo objects.
+///
+class POSIXStopInfo : public lldb_private::StopInfo {
+public:
+ POSIXStopInfo(lldb_private::Thread &thread, uint32_t status)
+ : StopInfo(thread, status) {}
+};
+
+//===----------------------------------------------------------------------===//
+/// \class POSIXLimboStopInfo
+/// Represents the stop state of a process ready to exit.
+///
+class POSIXLimboStopInfo : public POSIXStopInfo {
+public:
+ POSIXLimboStopInfo(FreeBSDThread &thread) : POSIXStopInfo(thread, 0) {}
+
+ ~POSIXLimboStopInfo();
+
+ lldb::StopReason GetStopReason() const;
+
+ const char *GetDescription();
+
+ bool ShouldStop(lldb_private::Event *event_ptr);
+
+ bool ShouldNotify(lldb_private::Event *event_ptr);
+};
+
+//===----------------------------------------------------------------------===//
+/// \class POSIXNewThreadStopInfo
+/// Represents the stop state of process when a new thread is spawned.
+///
+
+class POSIXNewThreadStopInfo : public POSIXStopInfo {
+public:
+ POSIXNewThreadStopInfo(FreeBSDThread &thread) : POSIXStopInfo(thread, 0) {}
+
+ ~POSIXNewThreadStopInfo();
+
+ lldb::StopReason GetStopReason() const;
+
+ const char *GetDescription();
+
+ bool ShouldStop(lldb_private::Event *event_ptr);
+
+ bool ShouldNotify(lldb_private::Event *event_ptr);
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
new file mode 100644
index 000000000000..770794569f72
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
@@ -0,0 +1,1081 @@
+//===-- ProcessFreeBSD.cpp ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+#include <pthread.h>
+#include <pthread_np.h>
+#include <stdlib.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <machine/elf.h>
+
+#include <mutex>
+#include <unordered_map>
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/State.h"
+
+#include "FreeBSDThread.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "Plugins/Process/Utility/FreeBSDSignals.h"
+#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/State.h"
+
+#include "lldb/Host/posix/Fcntl.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+UnixSignalsSP &GetFreeBSDSignals() {
+ static UnixSignalsSP s_freebsd_signals_sp(new FreeBSDSignals());
+ return s_freebsd_signals_sp;
+}
+}
+
+// Static functions.
+
+lldb::ProcessSP
+ProcessFreeBSD::CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file_path) {
+ lldb::ProcessSP process_sp;
+ if (crash_file_path == NULL)
+ process_sp.reset(
+ new ProcessFreeBSD(target_sp, listener_sp, GetFreeBSDSignals()));
+ return process_sp;
+}
+
+void ProcessFreeBSD::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+ });
+}
+
+lldb_private::ConstString ProcessFreeBSD::GetPluginNameStatic() {
+ static ConstString g_name("freebsd");
+ return g_name;
+}
+
+const char *ProcessFreeBSD::GetPluginDescriptionStatic() {
+ return "Process plugin for FreeBSD";
+}
+
+// ProcessInterface protocol.
+
+lldb_private::ConstString ProcessFreeBSD::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ProcessFreeBSD::GetPluginVersion() { return 1; }
+
+void ProcessFreeBSD::Terminate() {}
+
+Status ProcessFreeBSD::DoDetach(bool keep_stopped) {
+ Status error;
+ if (keep_stopped) {
+ error.SetErrorString("Detaching with keep_stopped true is not currently "
+ "supported on FreeBSD.");
+ return error;
+ }
+
+ error = m_monitor->Detach(GetID());
+
+ if (error.Success())
+ SetPrivateState(eStateDetached);
+
+ return error;
+}
+
+Status ProcessFreeBSD::DoResume() {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ SetPrivateState(eStateRunning);
+
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ bool do_step = false;
+ bool software_single_step = !SupportHardwareSingleStepping();
+
+ for (tid_collection::const_iterator t_pos = m_run_tids.begin(),
+ t_end = m_run_tids.end();
+ t_pos != t_end; ++t_pos) {
+ m_monitor->ThreadSuspend(*t_pos, false);
+ }
+ for (tid_collection::const_iterator t_pos = m_step_tids.begin(),
+ t_end = m_step_tids.end();
+ t_pos != t_end; ++t_pos) {
+ m_monitor->ThreadSuspend(*t_pos, false);
+ do_step = true;
+ if (software_single_step) {
+ Status error = SetupSoftwareSingleStepping(*t_pos);
+ if (error.Fail())
+ return error;
+ }
+ }
+ for (tid_collection::const_iterator t_pos = m_suspend_tids.begin(),
+ t_end = m_suspend_tids.end();
+ t_pos != t_end; ++t_pos) {
+ m_monitor->ThreadSuspend(*t_pos, true);
+ // XXX Cannot PT_CONTINUE properly with suspended threads.
+ do_step = true;
+ }
+
+ if (log)
+ log->Printf("process %" PRIu64 " resuming (%s)", GetID(),
+ do_step ? "step" : "continue");
+ if (do_step && !software_single_step)
+ m_monitor->SingleStep(GetID(), m_resume_signo);
+ else
+ m_monitor->Resume(GetID(), m_resume_signo);
+
+ return Status();
+}
+
+bool ProcessFreeBSD::UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessFreeBSD::%s (pid = %" PRIu64 ")", __FUNCTION__,
+ GetID());
+
+ std::vector<lldb::pid_t> tds;
+ if (!GetMonitor().GetCurrentThreadIDs(tds)) {
+ return false;
+ }
+
+ ThreadList old_thread_list_copy(old_thread_list);
+ for (size_t i = 0; i < tds.size(); ++i) {
+ tid_t tid = tds[i];
+ ThreadSP thread_sp(old_thread_list_copy.RemoveThreadByID(tid, false));
+ if (!thread_sp) {
+ thread_sp.reset(new FreeBSDThread(*this, tid));
+ if (log)
+ log->Printf("ProcessFreeBSD::%s new tid = %" PRIu64, __FUNCTION__, tid);
+ } else {
+ if (log)
+ log->Printf("ProcessFreeBSD::%s existing tid = %" PRIu64, __FUNCTION__,
+ tid);
+ }
+ new_thread_list.AddThread(thread_sp);
+ }
+ for (size_t i = 0; i < old_thread_list_copy.GetSize(false); ++i) {
+ ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false));
+ if (old_thread_sp) {
+ if (log)
+ log->Printf("ProcessFreeBSD::%s remove tid", __FUNCTION__);
+ }
+ }
+
+ return true;
+}
+
+Status ProcessFreeBSD::WillResume() {
+ m_resume_signo = 0;
+ m_suspend_tids.clear();
+ m_run_tids.clear();
+ m_step_tids.clear();
+ return Process::WillResume();
+}
+
+void ProcessFreeBSD::SendMessage(const ProcessMessage &message) {
+ std::lock_guard<std::recursive_mutex> guard(m_message_mutex);
+
+ switch (message.GetKind()) {
+ case ProcessMessage::eInvalidMessage:
+ return;
+
+ case ProcessMessage::eAttachMessage:
+ SetPrivateState(eStateStopped);
+ return;
+
+ case ProcessMessage::eLimboMessage:
+ case ProcessMessage::eExitMessage:
+ SetExitStatus(message.GetExitStatus(), NULL);
+ break;
+
+ case ProcessMessage::eSignalMessage:
+ case ProcessMessage::eSignalDeliveredMessage:
+ case ProcessMessage::eBreakpointMessage:
+ case ProcessMessage::eTraceMessage:
+ case ProcessMessage::eWatchpointMessage:
+ case ProcessMessage::eCrashMessage:
+ SetPrivateState(eStateStopped);
+ break;
+
+ case ProcessMessage::eNewThreadMessage:
+ llvm_unreachable("eNewThreadMessage unexpected on FreeBSD");
+ break;
+
+ case ProcessMessage::eExecMessage:
+ SetPrivateState(eStateStopped);
+ break;
+ }
+
+ m_message_queue.push(message);
+}
+
+// Constructors and destructors.
+
+ProcessFreeBSD::ProcessFreeBSD(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ UnixSignalsSP &unix_signals_sp)
+ : Process(target_sp, listener_sp, unix_signals_sp),
+ m_byte_order(endian::InlHostByteOrder()), m_monitor(NULL), m_module(NULL),
+ m_message_mutex(), m_exit_now(false), m_seen_initial_stop(),
+ m_resume_signo(0) {
+ // FIXME: Putting this code in the ctor and saving the byte order in a
+ // member variable is a hack to avoid const qual issues in GetByteOrder.
+ lldb::ModuleSP module = GetTarget().GetExecutableModule();
+ if (module && module->GetObjectFile())
+ m_byte_order = module->GetObjectFile()->GetByteOrder();
+}
+
+ProcessFreeBSD::~ProcessFreeBSD() { delete m_monitor; }
+
+// Process protocol.
+void ProcessFreeBSD::Finalize() {
+ Process::Finalize();
+
+ if (m_monitor)
+ m_monitor->StopMonitor();
+}
+
+bool ProcessFreeBSD::CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) {
+ // For now we are just making sure the file exists for a given module
+ ModuleSP exe_module_sp(target_sp->GetExecutableModule());
+ if (exe_module_sp.get())
+ return FileSystem::Instance().Exists(exe_module_sp->GetFileSpec());
+ // If there is no executable module, we return true since we might be
+ // preparing to attach.
+ return true;
+}
+
+Status
+ProcessFreeBSD::DoAttachToProcessWithID(lldb::pid_t pid,
+ const ProcessAttachInfo &attach_info) {
+ Status error;
+ assert(m_monitor == NULL);
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOGV(log, "pid = {0}", GetID());
+
+ m_monitor = new ProcessMonitor(this, pid, error);
+
+ if (!error.Success())
+ return error;
+
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ assert(platform_sp.get());
+ if (!platform_sp)
+ return error; // FIXME: Detatch?
+
+ // Find out what we can about this process
+ ProcessInstanceInfo process_info;
+ platform_sp->GetProcessInfo(pid, process_info);
+
+ // Resolve the executable module
+ ModuleSP exe_module_sp;
+ FileSpecList executable_search_paths(
+ Target::GetDefaultExecutableSearchPaths());
+ ModuleSpec exe_module_spec(process_info.GetExecutableFile(),
+ GetTarget().GetArchitecture());
+ error = platform_sp->ResolveExecutable(
+ exe_module_spec, exe_module_sp,
+ executable_search_paths.GetSize() ? &executable_search_paths : NULL);
+ if (!error.Success())
+ return error;
+
+ // Fix the target architecture if necessary
+ const ArchSpec &module_arch = exe_module_sp->GetArchitecture();
+ if (module_arch.IsValid() &&
+ !GetTarget().GetArchitecture().IsExactMatch(module_arch))
+ GetTarget().SetArchitecture(module_arch);
+
+ // Initialize the target module list
+ GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsYes);
+
+ SetSTDIOFileDescriptor(m_monitor->GetTerminalFD());
+
+ SetID(pid);
+
+ return error;
+}
+
+Status ProcessFreeBSD::WillLaunch(Module *module) {
+ Status error;
+ return error;
+}
+
+FileSpec
+ProcessFreeBSD::GetFileSpec(const lldb_private::FileAction *file_action,
+ const FileSpec &default_file_spec,
+ const FileSpec &dbg_pts_file_spec) {
+ FileSpec file_spec{};
+
+ if (file_action && file_action->GetAction() == FileAction::eFileActionOpen) {
+ file_spec = file_action->GetFileSpec();
+ // By default the stdio paths passed in will be pseudo-terminal (/dev/pts).
+ // If so, convert to using a different default path instead to redirect I/O
+ // to the debugger console. This should also handle user overrides to
+ // /dev/null or a different file.
+ if (!file_spec || file_spec == dbg_pts_file_spec)
+ file_spec = default_file_spec;
+ }
+ return file_spec;
+}
+
+Status ProcessFreeBSD::DoLaunch(Module *module,
+ ProcessLaunchInfo &launch_info) {
+ Status error;
+ assert(m_monitor == NULL);
+
+ FileSpec working_dir = launch_info.GetWorkingDirectory();
+ if (working_dir) {
+ FileSystem::Instance().Resolve(working_dir);
+ if (!FileSystem::Instance().IsDirectory(working_dir.GetPath())) {
+ error.SetErrorStringWithFormat("No such file or directory: %s",
+ working_dir.GetCString());
+ return error;
+ }
+ }
+
+ SetPrivateState(eStateLaunching);
+
+ const lldb_private::FileAction *file_action;
+
+ // Default of empty will mean to use existing open file descriptors
+ FileSpec stdin_file_spec{};
+ FileSpec stdout_file_spec{};
+ FileSpec stderr_file_spec{};
+
+ const FileSpec dbg_pts_file_spec{launch_info.GetPTY().GetSlaveName(NULL, 0)};
+
+ file_action = launch_info.GetFileActionForFD(STDIN_FILENO);
+ stdin_file_spec =
+ GetFileSpec(file_action, stdin_file_spec, dbg_pts_file_spec);
+
+ file_action = launch_info.GetFileActionForFD(STDOUT_FILENO);
+ stdout_file_spec =
+ GetFileSpec(file_action, stdout_file_spec, dbg_pts_file_spec);
+
+ file_action = launch_info.GetFileActionForFD(STDERR_FILENO);
+ stderr_file_spec =
+ GetFileSpec(file_action, stderr_file_spec, dbg_pts_file_spec);
+
+ m_monitor = new ProcessMonitor(
+ this, module, launch_info.GetArguments().GetConstArgumentVector(),
+ launch_info.GetEnvironment(), stdin_file_spec, stdout_file_spec,
+ stderr_file_spec, working_dir, launch_info, error);
+
+ m_module = module;
+
+ if (!error.Success())
+ return error;
+
+ int terminal = m_monitor->GetTerminalFD();
+ if (terminal >= 0) {
+// The reader thread will close the file descriptor when done, so we pass it a
+// copy.
+#ifdef F_DUPFD_CLOEXEC
+ int stdio = fcntl(terminal, F_DUPFD_CLOEXEC, 0);
+ if (stdio == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+#else
+ // Special case when F_DUPFD_CLOEXEC does not exist (Debian kFreeBSD)
+ int stdio = fcntl(terminal, F_DUPFD, 0);
+ if (stdio == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+ stdio = fcntl(terminal, F_SETFD, FD_CLOEXEC);
+ if (stdio == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+#endif
+ SetSTDIOFileDescriptor(stdio);
+ }
+
+ SetID(m_monitor->GetPID());
+ return error;
+}
+
+void ProcessFreeBSD::DidLaunch() {}
+
+addr_t ProcessFreeBSD::GetImageInfoAddress() {
+ Target *target = &GetTarget();
+ ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
+ Address addr = obj_file->GetImageInfoAddress(target);
+
+ if (addr.IsValid())
+ return addr.GetLoadAddress(target);
+ return LLDB_INVALID_ADDRESS;
+}
+
+Status ProcessFreeBSD::DoHalt(bool &caused_stop) {
+ Status error;
+
+ if (IsStopped()) {
+ caused_stop = false;
+ } else if (kill(GetID(), SIGSTOP)) {
+ caused_stop = false;
+ error.SetErrorToErrno();
+ } else {
+ caused_stop = true;
+ }
+ return error;
+}
+
+Status ProcessFreeBSD::DoSignal(int signal) {
+ Status error;
+
+ if (kill(GetID(), signal))
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status ProcessFreeBSD::DoDestroy() {
+ Status error;
+
+ if (!HasExited()) {
+ assert(m_monitor);
+ m_exit_now = true;
+ if (GetID() == LLDB_INVALID_PROCESS_ID) {
+ error.SetErrorString("invalid process id");
+ return error;
+ }
+ if (!m_monitor->Kill()) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ SetPrivateState(eStateExited);
+ }
+
+ return error;
+}
+
+void ProcessFreeBSD::DoDidExec() {
+ Target *target = &GetTarget();
+ if (target) {
+ PlatformSP platform_sp(target->GetPlatform());
+ assert(platform_sp.get());
+ if (platform_sp) {
+ ProcessInstanceInfo process_info;
+ platform_sp->GetProcessInfo(GetID(), process_info);
+ ModuleSP exe_module_sp;
+ ModuleSpec exe_module_spec(process_info.GetExecutableFile(),
+ target->GetArchitecture());
+ FileSpecList executable_search_paths(
+ Target::GetDefaultExecutableSearchPaths());
+ Status error = platform_sp->ResolveExecutable(
+ exe_module_spec, exe_module_sp,
+ executable_search_paths.GetSize() ? &executable_search_paths : NULL);
+ if (!error.Success())
+ return;
+ target->SetExecutableModule(exe_module_sp, eLoadDependentsYes);
+ }
+ }
+}
+
+bool ProcessFreeBSD::AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid) {
+ bool added_to_set = false;
+ ThreadStopSet::iterator it = m_seen_initial_stop.find(stop_tid);
+ if (it == m_seen_initial_stop.end()) {
+ m_seen_initial_stop.insert(stop_tid);
+ added_to_set = true;
+ }
+ return added_to_set;
+}
+
+bool ProcessFreeBSD::WaitingForInitialStop(lldb::tid_t stop_tid) {
+ return (m_seen_initial_stop.find(stop_tid) == m_seen_initial_stop.end());
+}
+
+FreeBSDThread *
+ProcessFreeBSD::CreateNewFreeBSDThread(lldb_private::Process &process,
+ lldb::tid_t tid) {
+ return new FreeBSDThread(process, tid);
+}
+
+void ProcessFreeBSD::RefreshStateAfterStop() {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOGV(log, "message_queue size = {0}", m_message_queue.size());
+
+ std::lock_guard<std::recursive_mutex> guard(m_message_mutex);
+
+ // This method used to only handle one message. Changing it to loop allows
+ // it to handle the case where we hit a breakpoint while handling a different
+ // breakpoint.
+ while (!m_message_queue.empty()) {
+ ProcessMessage &message = m_message_queue.front();
+
+ // Resolve the thread this message corresponds to and pass it along.
+ lldb::tid_t tid = message.GetTID();
+ LLDB_LOGV(log, " message_queue size = {0}, pid = {1}",
+ m_message_queue.size(), tid);
+
+ m_thread_list.RefreshStateAfterStop();
+
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ GetThreadList().FindThreadByID(tid, false).get());
+ if (thread)
+ thread->Notify(message);
+
+ if (message.GetKind() == ProcessMessage::eExitMessage) {
+ // FIXME: We should tell the user about this, but the limbo message is
+ // probably better for that.
+ LLDB_LOG(log, "removing thread, tid = {0}", tid);
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+
+ ThreadSP thread_sp = m_thread_list.RemoveThreadByID(tid, false);
+ thread_sp.reset();
+ m_seen_initial_stop.erase(tid);
+ }
+
+ m_message_queue.pop();
+ }
+}
+
+bool ProcessFreeBSD::IsAlive() {
+ StateType state = GetPrivateState();
+ return state != eStateDetached && state != eStateExited &&
+ state != eStateInvalid && state != eStateUnloaded;
+}
+
+size_t ProcessFreeBSD::DoReadMemory(addr_t vm_addr, void *buf, size_t size,
+ Status &error) {
+ assert(m_monitor);
+ return m_monitor->ReadMemory(vm_addr, buf, size, error);
+}
+
+size_t ProcessFreeBSD::DoWriteMemory(addr_t vm_addr, const void *buf,
+ size_t size, Status &error) {
+ assert(m_monitor);
+ return m_monitor->WriteMemory(vm_addr, buf, size, error);
+}
+
+addr_t ProcessFreeBSD::DoAllocateMemory(size_t size, uint32_t permissions,
+ Status &error) {
+ addr_t allocated_addr = LLDB_INVALID_ADDRESS;
+
+ unsigned prot = 0;
+ if (permissions & lldb::ePermissionsReadable)
+ prot |= eMmapProtRead;
+ if (permissions & lldb::ePermissionsWritable)
+ prot |= eMmapProtWrite;
+ if (permissions & lldb::ePermissionsExecutable)
+ prot |= eMmapProtExec;
+
+ if (InferiorCallMmap(this, allocated_addr, 0, size, prot,
+ eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) {
+ m_addr_to_mmap_size[allocated_addr] = size;
+ error.Clear();
+ } else {
+ allocated_addr = LLDB_INVALID_ADDRESS;
+ error.SetErrorStringWithFormat(
+ "unable to allocate %zu bytes of memory with permissions %s", size,
+ GetPermissionsAsCString(permissions));
+ }
+
+ return allocated_addr;
+}
+
+Status ProcessFreeBSD::DoDeallocateMemory(lldb::addr_t addr) {
+ Status error;
+ MMapMap::iterator pos = m_addr_to_mmap_size.find(addr);
+ if (pos != m_addr_to_mmap_size.end() &&
+ InferiorCallMunmap(this, addr, pos->second))
+ m_addr_to_mmap_size.erase(pos);
+ else
+ error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64,
+ addr);
+
+ return error;
+}
+
+size_t
+ProcessFreeBSD::GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site) {
+ static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xD4};
+ static const uint8_t g_i386_opcode[] = {0xCC};
+
+ ArchSpec arch = GetTarget().GetArchitecture();
+ const uint8_t *opcode = NULL;
+ size_t opcode_size = 0;
+
+ switch (arch.GetMachine()) {
+ default:
+ assert(false && "CPU type not supported!");
+ break;
+
+ case llvm::Triple::arm: {
+ // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the
+ // linux kernel does otherwise.
+ static const uint8_t g_arm_breakpoint_opcode[] = {0xf0, 0x01, 0xf0, 0xe7};
+ static const uint8_t g_thumb_breakpoint_opcode[] = {0x01, 0xde};
+
+ lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0));
+ AddressClass addr_class = AddressClass::eUnknown;
+
+ if (bp_loc_sp)
+ addr_class = bp_loc_sp->GetAddress().GetAddressClass();
+
+ if (addr_class == AddressClass::eCodeAlternateISA ||
+ (addr_class == AddressClass::eUnknown &&
+ bp_loc_sp->GetAddress().GetOffset() & 1)) {
+ opcode = g_thumb_breakpoint_opcode;
+ opcode_size = sizeof(g_thumb_breakpoint_opcode);
+ } else {
+ opcode = g_arm_breakpoint_opcode;
+ opcode_size = sizeof(g_arm_breakpoint_opcode);
+ }
+ } break;
+ case llvm::Triple::aarch64:
+ opcode = g_aarch64_opcode;
+ opcode_size = sizeof(g_aarch64_opcode);
+ break;
+
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ opcode = g_i386_opcode;
+ opcode_size = sizeof(g_i386_opcode);
+ break;
+ }
+
+ bp_site->SetTrapOpcode(opcode, opcode_size);
+ return opcode_size;
+}
+
+Status ProcessFreeBSD::EnableBreakpointSite(BreakpointSite *bp_site) {
+ return EnableSoftwareBreakpoint(bp_site);
+}
+
+Status ProcessFreeBSD::DisableBreakpointSite(BreakpointSite *bp_site) {
+ return DisableSoftwareBreakpoint(bp_site);
+}
+
+Status ProcessFreeBSD::EnableWatchpoint(Watchpoint *wp, bool notify) {
+ Status error;
+ if (wp) {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("ProcessFreeBSD::EnableWatchpoint(watchID = %" PRIu64 ")",
+ watchID);
+ if (wp->IsEnabled()) {
+ if (log)
+ log->Printf("ProcessFreeBSD::EnableWatchpoint(watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.",
+ watchID, (uint64_t)addr);
+ return error;
+ }
+
+ // Try to find a vacant watchpoint slot in the inferiors' main thread
+ uint32_t wp_hw_index = LLDB_INVALID_INDEX32;
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ m_thread_list.GetThreadAtIndex(0, false).get());
+
+ if (thread)
+ wp_hw_index = thread->FindVacantWatchpointIndex();
+
+ if (wp_hw_index == LLDB_INVALID_INDEX32) {
+ error.SetErrorString("Setting hardware watchpoint failed.");
+ } else {
+ wp->SetHardwareIndex(wp_hw_index);
+ bool wp_enabled = true;
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i) {
+ thread = static_cast<FreeBSDThread *>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ if (thread)
+ wp_enabled &= thread->EnableHardwareWatchpoint(wp);
+ else
+ wp_enabled = false;
+ }
+ if (wp_enabled) {
+ wp->SetEnabled(true, notify);
+ return error;
+ } else {
+ // Watchpoint enabling failed on at least one of the threads so roll
+ // back all of them
+ DisableWatchpoint(wp, false);
+ error.SetErrorString("Setting hardware watchpoint failed");
+ }
+ }
+ } else
+ error.SetErrorString("Watchpoint argument was NULL.");
+ return error;
+}
+
+Status ProcessFreeBSD::DisableWatchpoint(Watchpoint *wp, bool notify) {
+ Status error;
+ if (wp) {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("ProcessFreeBSD::DisableWatchpoint(watchID = %" PRIu64 ")",
+ watchID);
+ if (!wp->IsEnabled()) {
+ if (log)
+ log->Printf("ProcessFreeBSD::DisableWatchpoint(watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 ": watchpoint already disabled.",
+ watchID, (uint64_t)addr);
+ // This is needed (for now) to keep watchpoints disabled correctly
+ wp->SetEnabled(false, notify);
+ return error;
+ }
+
+ if (wp->IsHardware()) {
+ bool wp_disabled = true;
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i) {
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ if (thread)
+ wp_disabled &= thread->DisableHardwareWatchpoint(wp);
+ else
+ wp_disabled = false;
+ }
+ if (wp_disabled) {
+ wp->SetHardwareIndex(LLDB_INVALID_INDEX32);
+ wp->SetEnabled(false, notify);
+ return error;
+ } else
+ error.SetErrorString("Disabling hardware watchpoint failed");
+ }
+ } else
+ error.SetErrorString("Watchpoint argument was NULL.");
+ return error;
+}
+
+Status ProcessFreeBSD::GetWatchpointSupportInfo(uint32_t &num) {
+ Status error;
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ m_thread_list.GetThreadAtIndex(0, false).get());
+ if (thread)
+ num = thread->NumSupportedHardwareWatchpoints();
+ else
+ error.SetErrorString("Process does not exist.");
+ return error;
+}
+
+Status ProcessFreeBSD::GetWatchpointSupportInfo(uint32_t &num, bool &after) {
+ Status error = GetWatchpointSupportInfo(num);
+ // Watchpoints trigger and halt the inferior after the corresponding
+ // instruction has been executed.
+ after = true;
+ return error;
+}
+
+uint32_t ProcessFreeBSD::UpdateThreadListIfNeeded() {
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ // Do not allow recursive updates.
+ return m_thread_list.GetSize(false);
+}
+
+ByteOrder ProcessFreeBSD::GetByteOrder() const {
+ // FIXME: We should be able to extract this value directly. See comment in
+ // ProcessFreeBSD().
+ return m_byte_order;
+}
+
+size_t ProcessFreeBSD::PutSTDIN(const char *buf, size_t len, Status &error) {
+ ssize_t status;
+ if ((status = write(m_monitor->GetTerminalFD(), buf, len)) < 0) {
+ error.SetErrorToErrno();
+ return 0;
+ }
+ return status;
+}
+
+// Utility functions.
+
+bool ProcessFreeBSD::HasExited() {
+ switch (GetPrivateState()) {
+ default:
+ break;
+
+ case eStateDetached:
+ case eStateExited:
+ return true;
+ }
+
+ return false;
+}
+
+bool ProcessFreeBSD::IsStopped() {
+ switch (GetPrivateState()) {
+ default:
+ break;
+
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ return true;
+ }
+
+ return false;
+}
+
+bool ProcessFreeBSD::IsAThreadRunning() {
+ bool is_running = false;
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i) {
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ StateType thread_state = thread->GetState();
+ if (thread_state == eStateRunning || thread_state == eStateStepping) {
+ is_running = true;
+ break;
+ }
+ }
+ return is_running;
+}
+
+lldb_private::DataExtractor ProcessFreeBSD::GetAuxvData() {
+ // If we're the local platform, we can ask the host for auxv data.
+ PlatformSP platform_sp = GetTarget().GetPlatform();
+ assert(platform_sp && platform_sp->IsHost());
+
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_AUXV, (int)m_process->GetID()};
+ size_t auxv_size = AT_COUNT * sizeof(Elf_Auxinfo);
+ DataBufferSP buf_sp(new DataBufferHeap(auxv_size, 0));
+
+ if (::sysctl(mib, 4, buf_sp->GetBytes(), &auxv_size, NULL, 0) != 0) {
+ perror("sysctl failed on auxv");
+ buf_sp.reset();
+ }
+
+ return DataExtractor(buf_sp, GetByteOrder(), GetAddressByteSize());
+}
+
+struct EmulatorBaton {
+ ProcessFreeBSD *m_process;
+ RegisterContext *m_reg_context;
+
+ // eRegisterKindDWARF -> RegisterValue
+ std::unordered_map<uint32_t, RegisterValue> m_register_values;
+
+ EmulatorBaton(ProcessFreeBSD *process, RegisterContext *reg_context)
+ : m_process(process), m_reg_context(reg_context) {}
+};
+
+static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ lldb::addr_t addr, void *dst, size_t length) {
+ EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
+
+ Status error;
+ size_t bytes_read =
+ emulator_baton->m_process->DoReadMemory(addr, dst, length, error);
+ if (!error.Success())
+ bytes_read = 0;
+ return bytes_read;
+}
+
+static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
+
+ auto it = emulator_baton->m_register_values.find(
+ reg_info->kinds[eRegisterKindDWARF]);
+ if (it != emulator_baton->m_register_values.end()) {
+ reg_value = it->second;
+ return true;
+ }
+
+ // The emulator only fills in the dwarf register numbers (and in some cases
+ // the generic register numbers). Get the full register info from the
+ // register context based on the dwarf register numbers.
+ const RegisterInfo *full_reg_info =
+ emulator_baton->m_reg_context->GetRegisterInfo(
+ eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]);
+
+ bool error =
+ emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value);
+ return error;
+}
+
+static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
+ emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] =
+ reg_value;
+ return true;
+}
+
+static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst,
+ size_t length) {
+ return length;
+}
+
+bool ProcessFreeBSD::SingleStepBreakpointHit(
+ void *baton, lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id) {
+ return false;
+}
+
+Status ProcessFreeBSD::SetSoftwareSingleStepBreakpoint(lldb::tid_t tid,
+ lldb::addr_t addr) {
+ Status error;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ if (log) {
+ log->Printf("ProcessFreeBSD::%s addr = 0x%" PRIx64, __FUNCTION__, addr);
+ log->Printf("SoftwareBreakpoint::%s addr = 0x%" PRIx64, __FUNCTION__, addr);
+ }
+
+ // Validate the address.
+ if (addr == LLDB_INVALID_ADDRESS)
+ return Status("ProcessFreeBSD::%s invalid load address specified.",
+ __FUNCTION__);
+
+ Breakpoint *const sw_step_break =
+ m_process->GetTarget().CreateBreakpoint(addr, true, false).get();
+ sw_step_break->SetCallback(SingleStepBreakpointHit, this, true);
+ sw_step_break->SetBreakpointKind("software-signle-step");
+
+ if (log)
+ log->Printf("ProcessFreeBSD::%s addr = 0x%" PRIx64 " -- SUCCESS",
+ __FUNCTION__, addr);
+
+ m_threads_stepping_with_breakpoint.insert({tid, sw_step_break->GetID()});
+ return Status();
+}
+
+bool ProcessFreeBSD::IsSoftwareStepBreakpoint(lldb::tid_t tid) {
+ ThreadSP thread = GetThreadList().FindThreadByID(tid);
+ if (!thread)
+ return false;
+
+ assert(thread->GetRegisterContext());
+ lldb::addr_t stop_pc = thread->GetRegisterContext()->GetPC();
+
+ const auto &iter = m_threads_stepping_with_breakpoint.find(tid);
+ if (iter == m_threads_stepping_with_breakpoint.end())
+ return false;
+
+ lldb::break_id_t bp_id = iter->second;
+ BreakpointSP bp = GetTarget().GetBreakpointByID(bp_id);
+ if (!bp)
+ return false;
+
+ BreakpointLocationSP bp_loc = bp->FindLocationByAddress(stop_pc);
+ if (!bp_loc)
+ return false;
+
+ GetTarget().RemoveBreakpointByID(bp_id);
+ m_threads_stepping_with_breakpoint.erase(tid);
+ return true;
+}
+
+bool ProcessFreeBSD::SupportHardwareSingleStepping() const {
+ lldb_private::ArchSpec arch = GetTarget().GetArchitecture();
+ if (arch.GetMachine() == llvm::Triple::arm || arch.IsMIPS())
+ return false;
+ return true;
+}
+
+Status ProcessFreeBSD::SetupSoftwareSingleStepping(lldb::tid_t tid) {
+ std::unique_ptr<EmulateInstruction> emulator_up(
+ EmulateInstruction::FindPlugin(GetTarget().GetArchitecture(),
+ eInstructionTypePCModifying, nullptr));
+
+ if (emulator_up == nullptr)
+ return Status("Instruction emulator not found!");
+
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ m_thread_list.FindThreadByID(tid, false).get());
+ if (thread == NULL)
+ return Status("Thread not found not found!");
+
+ lldb::RegisterContextSP register_context_sp = thread->GetRegisterContext();
+
+ EmulatorBaton baton(this, register_context_sp.get());
+ emulator_up->SetBaton(&baton);
+ emulator_up->SetReadMemCallback(&ReadMemoryCallback);
+ emulator_up->SetReadRegCallback(&ReadRegisterCallback);
+ emulator_up->SetWriteMemCallback(&WriteMemoryCallback);
+ emulator_up->SetWriteRegCallback(&WriteRegisterCallback);
+
+ if (!emulator_up->ReadInstruction())
+ return Status("Read instruction failed!");
+
+ bool emulation_result =
+ emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
+ const RegisterInfo *reg_info_pc = register_context_sp->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ auto pc_it =
+ baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
+
+ lldb::addr_t next_pc;
+ if (emulation_result) {
+ assert(pc_it != baton.m_register_values.end() &&
+ "Emulation was successful but PC wasn't updated");
+ next_pc = pc_it->second.GetAsUInt64();
+ } else if (pc_it == baton.m_register_values.end()) {
+ // Emulate instruction failed and it haven't changed PC. Advance PC with
+ // the size of the current opcode because the emulation of all
+ // PC modifying instruction should be successful. The failure most
+ // likely caused by a not supported instruction which don't modify PC.
+ next_pc =
+ register_context_sp->GetPC() + emulator_up->GetOpcode().GetByteSize();
+ } else {
+ // The instruction emulation failed after it modified the PC. It is an
+ // unknown error where we can't continue because the next instruction is
+ // modifying the PC but we don't know how.
+ return Status("Instruction emulation failed unexpectedly");
+ }
+
+ SetSoftwareSingleStepBreakpoint(tid, next_pc);
+ return Status();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h
new file mode 100644
index 000000000000..536da0c0aa70
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h
@@ -0,0 +1,220 @@
+//===-- ProcessFreeBSD.h ------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessFreeBSD_H_
+#define liblldb_ProcessFreeBSD_H_
+
+#include "Plugins/Process/POSIX/ProcessMessage.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/ThreadList.h"
+#include <mutex>
+#include <queue>
+#include <set>
+
+class ProcessMonitor;
+class FreeBSDThread;
+
+class ProcessFreeBSD : public lldb_private::Process {
+
+public:
+ // Static functions.
+ static lldb::ProcessSP
+ CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+ const lldb_private::FileSpec *crash_file_path);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ // Constructors and destructors
+ ProcessFreeBSD(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+ lldb::UnixSignalsSP &unix_signals_sp);
+
+ ~ProcessFreeBSD();
+
+ virtual lldb_private::Status WillResume() override;
+
+ // PluginInterface protocol
+ virtual lldb_private::ConstString GetPluginName() override;
+
+ virtual uint32_t GetPluginVersion() override;
+
+public:
+ // Process protocol.
+ void Finalize() override;
+
+ bool CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) override;
+
+ lldb_private::Status WillLaunch(lldb_private::Module *module) override;
+
+ lldb_private::Status DoAttachToProcessWithID(
+ lldb::pid_t pid,
+ const lldb_private::ProcessAttachInfo &attach_info) override;
+
+ lldb_private::Status
+ DoLaunch(lldb_private::Module *exe_module,
+ lldb_private::ProcessLaunchInfo &launch_info) override;
+
+ void DidLaunch() override;
+
+ lldb_private::Status DoResume() override;
+
+ lldb_private::Status DoHalt(bool &caused_stop) override;
+
+ lldb_private::Status DoDetach(bool keep_stopped) override;
+
+ lldb_private::Status DoSignal(int signal) override;
+
+ lldb_private::Status DoDestroy() override;
+
+ void DoDidExec() override;
+
+ void RefreshStateAfterStop() override;
+
+ bool IsAlive() override;
+
+ size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ lldb_private::Status &error) override;
+
+ size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
+ lldb_private::Status &error) override;
+
+ lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions,
+ lldb_private::Status &error) override;
+
+ lldb_private::Status DoDeallocateMemory(lldb::addr_t ptr) override;
+
+ virtual size_t
+ GetSoftwareBreakpointTrapOpcode(lldb_private::BreakpointSite *bp_site);
+
+ lldb_private::Status
+ EnableBreakpointSite(lldb_private::BreakpointSite *bp_site) override;
+
+ lldb_private::Status
+ DisableBreakpointSite(lldb_private::BreakpointSite *bp_site) override;
+
+ lldb_private::Status EnableWatchpoint(lldb_private::Watchpoint *wp,
+ bool notify = true) override;
+
+ lldb_private::Status DisableWatchpoint(lldb_private::Watchpoint *wp,
+ bool notify = true) override;
+
+ lldb_private::Status GetWatchpointSupportInfo(uint32_t &num) override;
+
+ lldb_private::Status GetWatchpointSupportInfo(uint32_t &num,
+ bool &after) override;
+
+ virtual uint32_t UpdateThreadListIfNeeded();
+
+ bool UpdateThreadList(lldb_private::ThreadList &old_thread_list,
+ lldb_private::ThreadList &new_thread_list) override;
+
+ virtual lldb::ByteOrder GetByteOrder() const;
+
+ lldb::addr_t GetImageInfoAddress() override;
+
+ size_t PutSTDIN(const char *buf, size_t len,
+ lldb_private::Status &error) override;
+
+ lldb_private::DataExtractor GetAuxvData() override;
+
+ // ProcessFreeBSD internal API.
+
+ /// Registers the given message with this process.
+ virtual void SendMessage(const ProcessMessage &message);
+
+ ProcessMonitor &GetMonitor() {
+ assert(m_monitor);
+ return *m_monitor;
+ }
+
+ lldb_private::FileSpec
+ GetFileSpec(const lldb_private::FileAction *file_action,
+ const lldb_private::FileSpec &default_file_spec,
+ const lldb_private::FileSpec &dbg_pts_file_spec);
+
+ /// Adds the thread to the list of threads for which we have received the
+ /// initial stopping signal.
+ /// The \p stop_tid parameter indicates the thread which the stop happened
+ /// for.
+ bool AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid);
+
+ bool WaitingForInitialStop(lldb::tid_t stop_tid);
+
+ virtual FreeBSDThread *CreateNewFreeBSDThread(lldb_private::Process &process,
+ lldb::tid_t tid);
+
+ static bool SingleStepBreakpointHit(
+ void *baton, lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ lldb_private::Status SetupSoftwareSingleStepping(lldb::tid_t tid);
+
+ lldb_private::Status SetSoftwareSingleStepBreakpoint(lldb::tid_t tid,
+ lldb::addr_t addr);
+
+ bool IsSoftwareStepBreakpoint(lldb::tid_t tid);
+
+ bool SupportHardwareSingleStepping() const;
+
+ typedef std::vector<lldb::tid_t> tid_collection;
+ tid_collection &GetStepTids() { return m_step_tids; }
+
+protected:
+ static const size_t MAX_TRAP_OPCODE_SIZE = 8;
+
+ /// Target byte order.
+ lldb::ByteOrder m_byte_order;
+
+ /// Process monitor;
+ ProcessMonitor *m_monitor;
+
+ /// The module we are executing.
+ lldb_private::Module *m_module;
+
+ /// Message queue notifying this instance of inferior process state changes.
+ std::recursive_mutex m_message_mutex;
+ std::queue<ProcessMessage> m_message_queue;
+
+ /// Drive any exit events to completion.
+ bool m_exit_now;
+
+ /// Returns true if the process has exited.
+ bool HasExited();
+
+ /// Returns true if the process is stopped.
+ bool IsStopped();
+
+ /// Returns true if at least one running is currently running
+ bool IsAThreadRunning();
+
+ typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap;
+ MMapMap m_addr_to_mmap_size;
+
+ typedef std::set<lldb::tid_t> ThreadStopSet;
+ /// Every thread begins with a stop signal. This keeps track
+ /// of the threads for which we have received the stop signal.
+ ThreadStopSet m_seen_initial_stop;
+
+ friend class FreeBSDThread;
+
+ tid_collection m_suspend_tids;
+ tid_collection m_run_tids;
+ tid_collection m_step_tids;
+ std::map<lldb::tid_t, lldb::break_id_t> m_threads_stepping_with_breakpoint;
+
+ int m_resume_signo;
+};
+
+#endif // liblldb_ProcessFreeBSD_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp
new file mode 100644
index 000000000000..2778ae13a9f2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp
@@ -0,0 +1,1437 @@
+//===-- ProcessMonitor.cpp ------------------------------------ -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "lldb/Host/Host.h"
+#include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "llvm/Support/Errno.h"
+
+#include "FreeBSDThread.h"
+#include "Plugins/Process/POSIX/CrashReason.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// We disable the tracing of ptrace calls for integration builds to avoid the
+// additional indirection and checks.
+#ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION
+// Wrapper for ptrace to catch errors and log calls.
+
+const char *Get_PT_IO_OP(int op) {
+ switch (op) {
+ case PIOD_READ_D:
+ return "READ_D";
+ case PIOD_WRITE_D:
+ return "WRITE_D";
+ case PIOD_READ_I:
+ return "READ_I";
+ case PIOD_WRITE_I:
+ return "WRITE_I";
+ default:
+ return "Unknown op";
+ }
+}
+
+// Wrapper for ptrace to catch errors and log calls. Note that ptrace sets
+// errno on error because -1 is reserved as a valid result.
+extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data,
+ const char *reqName, const char *file, int line) {
+ long int result;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ if (log) {
+ log->Printf("ptrace(%s, %" PRIu64 ", %p, %x) called from file %s line %d",
+ reqName, pid, addr, data, file, line);
+ if (req == PT_IO) {
+ struct ptrace_io_desc *pi = (struct ptrace_io_desc *)addr;
+
+ log->Printf("PT_IO: op=%s offs=%zx size=%zu", Get_PT_IO_OP(pi->piod_op),
+ (size_t)pi->piod_offs, pi->piod_len);
+ }
+ }
+
+ // PtraceDisplayBytes(req, data);
+
+ errno = 0;
+ result = ptrace(req, pid, (caddr_t)addr, data);
+
+ // PtraceDisplayBytes(req, data);
+
+ if (log && errno != 0) {
+ const char *str;
+ switch (errno) {
+ case ESRCH:
+ str = "ESRCH";
+ break;
+ case EINVAL:
+ str = "EINVAL";
+ break;
+ case EBUSY:
+ str = "EBUSY";
+ break;
+ case EPERM:
+ str = "EPERM";
+ break;
+ default:
+ str = "<unknown>";
+ }
+ log->Printf("ptrace() failed; errno=%d (%s)", errno, str);
+ }
+
+ if (log) {
+#ifdef __amd64__
+ if (req == PT_GETREGS) {
+ struct reg *r = (struct reg *)addr;
+
+ log->Printf("PT_GETREGS: rip=0x%lx rsp=0x%lx rbp=0x%lx rax=0x%lx",
+ r->r_rip, r->r_rsp, r->r_rbp, r->r_rax);
+ }
+ if (req == PT_GETDBREGS || req == PT_SETDBREGS) {
+ struct dbreg *r = (struct dbreg *)addr;
+ char setget = (req == PT_GETDBREGS) ? 'G' : 'S';
+
+ for (int i = 0; i <= 7; i++)
+ log->Printf("PT_%cETDBREGS: dr[%d]=0x%lx", setget, i, r->dr[i]);
+ }
+#endif
+ }
+
+ return result;
+}
+
+// Wrapper for ptrace when logging is not required. Sets errno to 0 prior to
+// calling ptrace.
+extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data) {
+ long result = 0;
+ errno = 0;
+ result = ptrace(req, pid, (caddr_t)addr, data);
+ return result;
+}
+
+#define PTRACE(req, pid, addr, data) \
+ PtraceWrapper((req), (pid), (addr), (data), #req, __FILE__, __LINE__)
+#else
+PtraceWrapper((req), (pid), (addr), (data))
+#endif
+
+// Static implementations of ProcessMonitor::ReadMemory and
+// ProcessMonitor::WriteMemory. This enables mutual recursion between these
+// functions without needed to go thru the thread funnel.
+
+static size_t DoReadMemory(lldb::pid_t pid, lldb::addr_t vm_addr, void *buf,
+ size_t size, Status &error) {
+ struct ptrace_io_desc pi_desc;
+
+ pi_desc.piod_op = PIOD_READ_D;
+ pi_desc.piod_offs = (void *)vm_addr;
+ pi_desc.piod_addr = buf;
+ pi_desc.piod_len = size;
+
+ if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) {
+ error.SetErrorToErrno();
+ return 0;
+ }
+ return pi_desc.piod_len;
+}
+
+static size_t DoWriteMemory(lldb::pid_t pid, lldb::addr_t vm_addr,
+ const void *buf, size_t size, Status &error) {
+ struct ptrace_io_desc pi_desc;
+
+ pi_desc.piod_op = PIOD_WRITE_D;
+ pi_desc.piod_offs = (void *)vm_addr;
+ pi_desc.piod_addr = const_cast<void *>(buf);
+ pi_desc.piod_len = size;
+
+ if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) {
+ error.SetErrorToErrno();
+ return 0;
+ }
+ return pi_desc.piod_len;
+}
+
+// Simple helper function to ensure flags are enabled on the given file
+// descriptor.
+static bool EnsureFDFlags(int fd, int flags, Status &error) {
+ int status;
+
+ if ((status = fcntl(fd, F_GETFL)) == -1) {
+ error.SetErrorToErrno();
+ return false;
+ }
+
+ if (fcntl(fd, F_SETFL, status | flags) == -1) {
+ error.SetErrorToErrno();
+ return false;
+ }
+
+ return true;
+}
+
+/// \class Operation
+/// Represents a ProcessMonitor operation.
+///
+/// Under FreeBSD, it is not possible to ptrace() from any other thread but
+/// the one that spawned or attached to the process from the start.
+/// Therefore, when a ProcessMonitor is asked to deliver or change the state
+/// of an inferior process the operation must be "funneled" to a specific
+/// thread to perform the task. The Operation class provides an abstract base
+/// for all services the ProcessMonitor must perform via the single virtual
+/// function Execute, thus encapsulating the code that needs to run in the
+/// privileged context.
+class Operation {
+public:
+ virtual ~Operation() {}
+ virtual void Execute(ProcessMonitor *monitor) = 0;
+};
+
+/// \class ReadOperation
+/// Implements ProcessMonitor::ReadMemory.
+class ReadOperation : public Operation {
+public:
+ ReadOperation(lldb::addr_t addr, void *buff, size_t size, Status &error,
+ size_t &result)
+ : m_addr(addr), m_buff(buff), m_size(size), m_error(error),
+ m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::addr_t m_addr;
+ void *m_buff;
+ size_t m_size;
+ Status &m_error;
+ size_t &m_result;
+};
+
+void ReadOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+
+ m_result = DoReadMemory(pid, m_addr, m_buff, m_size, m_error);
+}
+
+/// \class WriteOperation
+/// Implements ProcessMonitor::WriteMemory.
+class WriteOperation : public Operation {
+public:
+ WriteOperation(lldb::addr_t addr, const void *buff, size_t size,
+ Status &error, size_t &result)
+ : m_addr(addr), m_buff(buff), m_size(size), m_error(error),
+ m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::addr_t m_addr;
+ const void *m_buff;
+ size_t m_size;
+ Status &m_error;
+ size_t &m_result;
+};
+
+void WriteOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+
+ m_result = DoWriteMemory(pid, m_addr, m_buff, m_size, m_error);
+}
+
+/// \class ReadRegOperation
+/// Implements ProcessMonitor::ReadRegisterValue.
+class ReadRegOperation : public Operation {
+public:
+ ReadRegOperation(lldb::tid_t tid, unsigned offset, unsigned size,
+ RegisterValue &value, bool &result)
+ : m_tid(tid), m_offset(offset), m_size(size), m_value(value),
+ m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned m_offset;
+ unsigned m_size;
+ RegisterValue &m_value;
+ bool &m_result;
+};
+
+void ReadRegOperation::Execute(ProcessMonitor *monitor) {
+ struct reg regs;
+ int rc;
+
+ if ((rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)&regs, 0)) < 0) {
+ m_result = false;
+ } else {
+ // 'struct reg' contains only 32- or 64-bit register values. Punt on
+ // others. Also, not all entries may be uintptr_t sized, such as 32-bit
+ // processes on powerpc64 (probably the same for i386 on amd64)
+ if (m_size == sizeof(uint32_t))
+ m_value = *(uint32_t *)(((caddr_t)&regs) + m_offset);
+ else if (m_size == sizeof(uint64_t))
+ m_value = *(uint64_t *)(((caddr_t)&regs) + m_offset);
+ else
+ memcpy((void *)&m_value, (((caddr_t)&regs) + m_offset), m_size);
+ m_result = true;
+ }
+}
+
+/// \class WriteRegOperation
+/// Implements ProcessMonitor::WriteRegisterValue.
+class WriteRegOperation : public Operation {
+public:
+ WriteRegOperation(lldb::tid_t tid, unsigned offset,
+ const RegisterValue &value, bool &result)
+ : m_tid(tid), m_offset(offset), m_value(value), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned m_offset;
+ const RegisterValue &m_value;
+ bool &m_result;
+};
+
+void WriteRegOperation::Execute(ProcessMonitor *monitor) {
+ struct reg regs;
+
+ if (PTRACE(PT_GETREGS, m_tid, (caddr_t)&regs, 0) < 0) {
+ m_result = false;
+ return;
+ }
+ *(uintptr_t *)(((caddr_t)&regs) + m_offset) =
+ (uintptr_t)m_value.GetAsUInt64();
+ if (PTRACE(PT_SETREGS, m_tid, (caddr_t)&regs, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class ReadDebugRegOperation
+/// Implements ProcessMonitor::ReadDebugRegisterValue.
+class ReadDebugRegOperation : public Operation {
+public:
+ ReadDebugRegOperation(lldb::tid_t tid, unsigned offset, unsigned size,
+ RegisterValue &value, bool &result)
+ : m_tid(tid), m_offset(offset), m_size(size), m_value(value),
+ m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned m_offset;
+ unsigned m_size;
+ RegisterValue &m_value;
+ bool &m_result;
+};
+
+void ReadDebugRegOperation::Execute(ProcessMonitor *monitor) {
+ struct dbreg regs;
+ int rc;
+
+ if ((rc = PTRACE(PT_GETDBREGS, m_tid, (caddr_t)&regs, 0)) < 0) {
+ m_result = false;
+ } else {
+ if (m_size == sizeof(uintptr_t))
+ m_value = *(uintptr_t *)(((caddr_t)&regs) + m_offset);
+ else
+ memcpy((void *)&m_value, (((caddr_t)&regs) + m_offset), m_size);
+ m_result = true;
+ }
+}
+
+/// \class WriteDebugRegOperation
+/// Implements ProcessMonitor::WriteDebugRegisterValue.
+class WriteDebugRegOperation : public Operation {
+public:
+ WriteDebugRegOperation(lldb::tid_t tid, unsigned offset,
+ const RegisterValue &value, bool &result)
+ : m_tid(tid), m_offset(offset), m_value(value), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned m_offset;
+ const RegisterValue &m_value;
+ bool &m_result;
+};
+
+void WriteDebugRegOperation::Execute(ProcessMonitor *monitor) {
+ struct dbreg regs;
+
+ if (PTRACE(PT_GETDBREGS, m_tid, (caddr_t)&regs, 0) < 0) {
+ m_result = false;
+ return;
+ }
+ *(uintptr_t *)(((caddr_t)&regs) + m_offset) =
+ (uintptr_t)m_value.GetAsUInt64();
+ if (PTRACE(PT_SETDBREGS, m_tid, (caddr_t)&regs, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class ReadGPROperation
+/// Implements ProcessMonitor::ReadGPR.
+class ReadGPROperation : public Operation {
+public:
+ ReadGPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void ReadGPROperation::Execute(ProcessMonitor *monitor) {
+ int rc;
+
+ errno = 0;
+ rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)m_buf, 0);
+ if (errno != 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class ReadFPROperation
+/// Implements ProcessMonitor::ReadFPR.
+class ReadFPROperation : public Operation {
+public:
+ ReadFPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void ReadFPROperation::Execute(ProcessMonitor *monitor) {
+ if (PTRACE(PT_GETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class WriteGPROperation
+/// Implements ProcessMonitor::WriteGPR.
+class WriteGPROperation : public Operation {
+public:
+ WriteGPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void WriteGPROperation::Execute(ProcessMonitor *monitor) {
+ if (PTRACE(PT_SETREGS, m_tid, (caddr_t)m_buf, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class WriteFPROperation
+/// Implements ProcessMonitor::WriteFPR.
+class WriteFPROperation : public Operation {
+public:
+ WriteFPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void WriteFPROperation::Execute(ProcessMonitor *monitor) {
+ if (PTRACE(PT_SETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class ResumeOperation
+/// Implements ProcessMonitor::Resume.
+class ResumeOperation : public Operation {
+public:
+ ResumeOperation(uint32_t signo, bool &result)
+ : m_signo(signo), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ uint32_t m_signo;
+ bool &m_result;
+};
+
+void ResumeOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+ int data = 0;
+
+ if (m_signo != LLDB_INVALID_SIGNAL_NUMBER)
+ data = m_signo;
+
+ if (PTRACE(PT_CONTINUE, pid, (caddr_t)1, data)) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOG(log, "ResumeOperation ({0}) failed: {1}", pid,
+ llvm::sys::StrError(errno));
+ m_result = false;
+ } else
+ m_result = true;
+}
+
+/// \class SingleStepOperation
+/// Implements ProcessMonitor::SingleStep.
+class SingleStepOperation : public Operation {
+public:
+ SingleStepOperation(uint32_t signo, bool &result)
+ : m_signo(signo), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ uint32_t m_signo;
+ bool &m_result;
+};
+
+void SingleStepOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+ int data = 0;
+
+ if (m_signo != LLDB_INVALID_SIGNAL_NUMBER)
+ data = m_signo;
+
+ if (PTRACE(PT_STEP, pid, NULL, data))
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class LwpInfoOperation
+/// Implements ProcessMonitor::GetLwpInfo.
+class LwpInfoOperation : public Operation {
+public:
+ LwpInfoOperation(lldb::tid_t tid, void *info, bool &result, int &ptrace_err)
+ : m_tid(tid), m_info(info), m_result(result), m_err(ptrace_err) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_info;
+ bool &m_result;
+ int &m_err;
+};
+
+void LwpInfoOperation::Execute(ProcessMonitor *monitor) {
+ struct ptrace_lwpinfo plwp;
+
+ if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) {
+ m_result = false;
+ m_err = errno;
+ } else {
+ memcpy(m_info, &plwp, sizeof(plwp));
+ m_result = true;
+ }
+}
+
+/// \class ThreadSuspendOperation
+/// Implements ProcessMonitor::ThreadSuspend.
+class ThreadSuspendOperation : public Operation {
+public:
+ ThreadSuspendOperation(lldb::tid_t tid, bool suspend, bool &result)
+ : m_tid(tid), m_suspend(suspend), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ bool m_suspend;
+ bool &m_result;
+};
+
+void ThreadSuspendOperation::Execute(ProcessMonitor *monitor) {
+ m_result = !PTRACE(m_suspend ? PT_SUSPEND : PT_RESUME, m_tid, NULL, 0);
+}
+
+/// \class EventMessageOperation
+/// Implements ProcessMonitor::GetEventMessage.
+class EventMessageOperation : public Operation {
+public:
+ EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result)
+ : m_tid(tid), m_message(message), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned long *m_message;
+ bool &m_result;
+};
+
+void EventMessageOperation::Execute(ProcessMonitor *monitor) {
+ struct ptrace_lwpinfo plwp;
+
+ if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp)))
+ m_result = false;
+ else {
+ if (plwp.pl_flags & PL_FLAG_FORKED) {
+ *m_message = plwp.pl_child_pid;
+ m_result = true;
+ } else
+ m_result = false;
+ }
+}
+
+/// \class KillOperation
+/// Implements ProcessMonitor::Kill.
+class KillOperation : public Operation {
+public:
+ KillOperation(bool &result) : m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ bool &m_result;
+};
+
+void KillOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+
+ if (PTRACE(PT_KILL, pid, NULL, 0))
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class DetachOperation
+/// Implements ProcessMonitor::Detach.
+class DetachOperation : public Operation {
+public:
+ DetachOperation(Status &result) : m_error(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ Status &m_error;
+};
+
+void DetachOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+
+ if (PTRACE(PT_DETACH, pid, NULL, 0) < 0)
+ m_error.SetErrorToErrno();
+}
+
+ProcessMonitor::OperationArgs::OperationArgs(ProcessMonitor *monitor)
+ : m_monitor(monitor) {
+ sem_init(&m_semaphore, 0, 0);
+}
+
+ProcessMonitor::OperationArgs::~OperationArgs() { sem_destroy(&m_semaphore); }
+
+ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor,
+ lldb_private::Module *module,
+ char const **argv, Environment env,
+ const FileSpec &stdin_file_spec,
+ const FileSpec &stdout_file_spec,
+ const FileSpec &stderr_file_spec,
+ const FileSpec &working_dir)
+ : OperationArgs(monitor), m_module(module), m_argv(argv),
+ m_env(std::move(env)), m_stdin_file_spec(stdin_file_spec),
+ m_stdout_file_spec(stdout_file_spec),
+ m_stderr_file_spec(stderr_file_spec), m_working_dir(working_dir) {}
+
+ProcessMonitor::LaunchArgs::~LaunchArgs() {}
+
+ProcessMonitor::AttachArgs::AttachArgs(ProcessMonitor *monitor, lldb::pid_t pid)
+ : OperationArgs(monitor), m_pid(pid) {}
+
+ProcessMonitor::AttachArgs::~AttachArgs() {}
+
+/// The basic design of the ProcessMonitor is built around two threads.
+///
+/// One thread (@see SignalThread) simply blocks on a call to waitpid()
+/// looking for changes in the debugee state. When a change is detected a
+/// ProcessMessage is sent to the associated ProcessFreeBSD instance. This
+/// thread "drives" state changes in the debugger.
+///
+/// The second thread (@see OperationThread) is responsible for two things 1)
+/// launching or attaching to the inferior process, and then 2) servicing
+/// operations such as register reads/writes, stepping, etc. See the comments
+/// on the Operation class for more info as to why this is needed.
+ProcessMonitor::ProcessMonitor(
+ ProcessFreeBSD *process, Module *module, const char *argv[],
+ Environment env, const FileSpec &stdin_file_spec,
+ const FileSpec &stdout_file_spec, const FileSpec &stderr_file_spec,
+ const FileSpec &working_dir,
+ const lldb_private::ProcessLaunchInfo & /* launch_info */,
+ lldb_private::Status &error)
+ : m_process(static_cast<ProcessFreeBSD *>(process)),
+ m_operation_thread(), m_monitor_thread(), m_pid(LLDB_INVALID_PROCESS_ID), m_terminal_fd(-1), m_operation(0) {
+ using namespace std::placeholders;
+
+ std::unique_ptr<LaunchArgs> args(
+ new LaunchArgs(this, module, argv, std::move(env), stdin_file_spec,
+ stdout_file_spec, stderr_file_spec, working_dir));
+
+ sem_init(&m_operation_pending, 0, 0);
+ sem_init(&m_operation_done, 0, 0);
+
+ StartLaunchOpThread(args.get(), error);
+ if (!error.Success())
+ return;
+
+ if (llvm::sys::RetryAfterSignal(-1, sem_wait, &args->m_semaphore) == -1) {
+ error.SetErrorToErrno();
+ return;
+ }
+
+ // Check that the launch was a success.
+ if (!args->m_error.Success()) {
+ StopOpThread();
+ error = args->m_error;
+ return;
+ }
+
+ // Finally, start monitoring the child process for change in state.
+ llvm::Expected<lldb_private::HostThread> monitor_thread =
+ Host::StartMonitoringChildProcess(
+ std::bind(&ProcessMonitor::MonitorCallback, this, _1, _2, _3, _4),
+ GetPID(), true);
+ if (!monitor_thread || !monitor_thread->IsJoinable()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Process launch failed.");
+ return;
+ }
+ m_monitor_thread = *monitor_thread;
+}
+
+ProcessMonitor::ProcessMonitor(ProcessFreeBSD *process, lldb::pid_t pid,
+ lldb_private::Status &error)
+ : m_process(static_cast<ProcessFreeBSD *>(process)),
+ m_operation_thread(), m_monitor_thread(), m_pid(pid), m_terminal_fd(-1), m_operation(0) {
+ using namespace std::placeholders;
+
+ sem_init(&m_operation_pending, 0, 0);
+ sem_init(&m_operation_done, 0, 0);
+
+ std::unique_ptr<AttachArgs> args(new AttachArgs(this, pid));
+
+ StartAttachOpThread(args.get(), error);
+ if (!error.Success())
+ return;
+
+ if (llvm::sys::RetryAfterSignal(-1, sem_wait, &args->m_semaphore) == -1) {
+ error.SetErrorToErrno();
+ return;
+ }
+
+ // Check that the attach was a success.
+ if (!args->m_error.Success()) {
+ StopOpThread();
+ error = args->m_error;
+ return;
+ }
+
+ // Finally, start monitoring the child process for change in state.
+ llvm::Expected<lldb_private::HostThread> monitor_thread =
+ Host::StartMonitoringChildProcess(
+ std::bind(&ProcessMonitor::MonitorCallback, this, _1, _2, _3, _4),
+ GetPID(), true);
+ if (!monitor_thread || !monitor_thread->IsJoinable()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Process attach failed.");
+ return;
+ }
+ m_monitor_thread = *monitor_thread;
+}
+
+ProcessMonitor::~ProcessMonitor() { StopMonitor(); }
+
+// Thread setup and tear down.
+void ProcessMonitor::StartLaunchOpThread(LaunchArgs *args, Status &error) {
+ static const char *g_thread_name = "freebsd.op";
+
+ if (m_operation_thread && m_operation_thread->IsJoinable())
+ return;
+
+ llvm::Expected<lldb_private::HostThread> operation_thread =
+ ThreadLauncher::LaunchThread(g_thread_name, LaunchOpThread, args);
+ if (operation_thread)
+ m_operation_thread = *operation_thread;
+ else
+ error = operation_thread.takeError();
+}
+
+void *ProcessMonitor::LaunchOpThread(void *arg) {
+ LaunchArgs *args = static_cast<LaunchArgs *>(arg);
+
+ if (!Launch(args)) {
+ sem_post(&args->m_semaphore);
+ return NULL;
+ }
+
+ ServeOperation(args);
+ return NULL;
+}
+
+bool ProcessMonitor::Launch(LaunchArgs *args) {
+ ProcessMonitor *monitor = args->m_monitor;
+ ProcessFreeBSD &process = monitor->GetProcess();
+ const char **argv = args->m_argv;
+ const FileSpec &stdin_file_spec = args->m_stdin_file_spec;
+ const FileSpec &stdout_file_spec = args->m_stdout_file_spec;
+ const FileSpec &stderr_file_spec = args->m_stderr_file_spec;
+ const FileSpec &working_dir = args->m_working_dir;
+
+ PseudoTerminal terminal;
+ const size_t err_len = 1024;
+ char err_str[err_len];
+ ::pid_t pid;
+
+ // Propagate the environment if one is not supplied.
+ Environment::Envp envp =
+ (args->m_env.empty() ? Host::GetEnvironment() : args->m_env).getEnvp();
+
+ if ((pid = terminal.Fork(err_str, err_len)) == -1) {
+ args->m_error.SetErrorToGenericError();
+ args->m_error.SetErrorString("Process fork failed.");
+ goto FINISH;
+ }
+
+ // Recognized child exit status codes.
+ enum {
+ ePtraceFailed = 1,
+ eDupStdinFailed,
+ eDupStdoutFailed,
+ eDupStderrFailed,
+ eChdirFailed,
+ eExecFailed,
+ eSetGidFailed
+ };
+
+ // Child process.
+ if (pid == 0) {
+ // Trace this process.
+ if (PTRACE(PT_TRACE_ME, 0, NULL, 0) < 0)
+ exit(ePtraceFailed);
+
+ // terminal has already dupped the tty descriptors to stdin/out/err. This
+ // closes original fd from which they were copied (and avoids leaking
+ // descriptors to the debugged process.
+ terminal.CloseSlaveFileDescriptor();
+
+ // Do not inherit setgid powers.
+ if (setgid(getgid()) != 0)
+ exit(eSetGidFailed);
+
+ // Let us have our own process group.
+ setpgid(0, 0);
+
+ // Dup file descriptors if needed.
+ //
+ // FIXME: If two or more of the paths are the same we needlessly open
+ // the same file multiple times.
+ if (stdin_file_spec)
+ if (!DupDescriptor(stdin_file_spec, STDIN_FILENO, O_RDONLY))
+ exit(eDupStdinFailed);
+
+ if (stdout_file_spec)
+ if (!DupDescriptor(stdout_file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT))
+ exit(eDupStdoutFailed);
+
+ if (stderr_file_spec)
+ if (!DupDescriptor(stderr_file_spec, STDERR_FILENO, O_WRONLY | O_CREAT))
+ exit(eDupStderrFailed);
+
+ // Change working directory
+ if (working_dir && 0 != ::chdir(working_dir.GetCString()))
+ exit(eChdirFailed);
+
+ // Execute. We should never return.
+ execve(argv[0], const_cast<char *const *>(argv), envp);
+ exit(eExecFailed);
+ }
+
+ // Wait for the child process to to trap on its call to execve.
+ ::pid_t wpid;
+ int status;
+ if ((wpid = waitpid(pid, &status, 0)) < 0) {
+ args->m_error.SetErrorToErrno();
+ goto FINISH;
+ } else if (WIFEXITED(status)) {
+ // open, dup or execve likely failed for some reason.
+ args->m_error.SetErrorToGenericError();
+ switch (WEXITSTATUS(status)) {
+ case ePtraceFailed:
+ args->m_error.SetErrorString("Child ptrace failed.");
+ break;
+ case eDupStdinFailed:
+ args->m_error.SetErrorString("Child open stdin failed.");
+ break;
+ case eDupStdoutFailed:
+ args->m_error.SetErrorString("Child open stdout failed.");
+ break;
+ case eDupStderrFailed:
+ args->m_error.SetErrorString("Child open stderr failed.");
+ break;
+ case eChdirFailed:
+ args->m_error.SetErrorString("Child failed to set working directory.");
+ break;
+ case eExecFailed:
+ args->m_error.SetErrorString("Child exec failed.");
+ break;
+ case eSetGidFailed:
+ args->m_error.SetErrorString("Child setgid failed.");
+ break;
+ default:
+ args->m_error.SetErrorString("Child returned unknown exit status.");
+ break;
+ }
+ goto FINISH;
+ }
+ assert(WIFSTOPPED(status) && wpid == (::pid_t)pid &&
+ "Could not sync with inferior process.");
+
+#ifdef notyet
+ // Have the child raise an event on exit. This is used to keep the child in
+ // limbo until it is destroyed.
+ if (PTRACE(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXIT) < 0) {
+ args->m_error.SetErrorToErrno();
+ goto FINISH;
+ }
+#endif
+ // Release the master terminal descriptor and pass it off to the
+ // ProcessMonitor instance. Similarly stash the inferior pid.
+ monitor->m_terminal_fd = terminal.ReleaseMasterFileDescriptor();
+ monitor->m_pid = pid;
+
+ // Set the terminal fd to be in non blocking mode (it simplifies the
+ // implementation of ProcessFreeBSD::GetSTDOUT to have a non-blocking
+ // descriptor to read from).
+ if (!EnsureFDFlags(monitor->m_terminal_fd, O_NONBLOCK, args->m_error))
+ goto FINISH;
+
+ process.SendMessage(ProcessMessage::Attach(pid));
+
+FINISH:
+ return args->m_error.Success();
+}
+
+void ProcessMonitor::StartAttachOpThread(AttachArgs *args,
+ lldb_private::Status &error) {
+ static const char *g_thread_name = "freebsd.op";
+
+ if (m_operation_thread && m_operation_thread->IsJoinable())
+ return;
+
+ llvm::Expected<lldb_private::HostThread> operation_thread =
+ ThreadLauncher::LaunchThread(g_thread_name, AttachOpThread, args);
+ if (operation_thread)
+ m_operation_thread = *operation_thread;
+ else
+ error = operation_thread.takeError();
+}
+
+void *ProcessMonitor::AttachOpThread(void *arg) {
+ AttachArgs *args = static_cast<AttachArgs *>(arg);
+
+ Attach(args);
+
+ ServeOperation(args);
+ return NULL;
+}
+
+void ProcessMonitor::Attach(AttachArgs *args) {
+ lldb::pid_t pid = args->m_pid;
+
+ ProcessMonitor *monitor = args->m_monitor;
+ ProcessFreeBSD &process = monitor->GetProcess();
+
+ if (pid <= 1) {
+ args->m_error.SetErrorToGenericError();
+ args->m_error.SetErrorString("Attaching to process 1 is not allowed.");
+ return;
+ }
+
+ // Attach to the requested process.
+ if (PTRACE(PT_ATTACH, pid, NULL, 0) < 0) {
+ args->m_error.SetErrorToErrno();
+ return;
+ }
+
+ int status;
+ if ((status = waitpid(pid, NULL, 0)) < 0) {
+ args->m_error.SetErrorToErrno();
+ return;
+ }
+
+ process.SendMessage(ProcessMessage::Attach(pid));
+}
+
+size_t
+ProcessMonitor::GetCurrentThreadIDs(std::vector<lldb::tid_t> &thread_ids) {
+ lwpid_t *tids;
+ int tdcnt;
+
+ thread_ids.clear();
+
+ tdcnt = PTRACE(PT_GETNUMLWPS, m_pid, NULL, 0);
+ if (tdcnt <= 0)
+ return 0;
+ tids = (lwpid_t *)malloc(tdcnt * sizeof(*tids));
+ if (tids == NULL)
+ return 0;
+ if (PTRACE(PT_GETLWPLIST, m_pid, (void *)tids, tdcnt) < 0) {
+ free(tids);
+ return 0;
+ }
+ thread_ids = std::vector<lldb::tid_t>(tids, tids + tdcnt);
+ free(tids);
+ return thread_ids.size();
+}
+
+bool ProcessMonitor::MonitorCallback(ProcessMonitor *monitor, lldb::pid_t pid,
+ bool exited, int signal, int status) {
+ ProcessMessage message;
+ ProcessFreeBSD *process = monitor->m_process;
+ assert(process);
+ bool stop_monitoring;
+ struct ptrace_lwpinfo plwp;
+ int ptrace_err;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ if (exited) {
+ if (log)
+ log->Printf("ProcessMonitor::%s() got exit signal, tid = %" PRIu64,
+ __FUNCTION__, pid);
+ message = ProcessMessage::Exit(pid, status);
+ process->SendMessage(message);
+ return pid == process->GetID();
+ }
+
+ if (!monitor->GetLwpInfo(pid, &plwp, ptrace_err))
+ stop_monitoring = true; // pid is gone. Bail.
+ else {
+ switch (plwp.pl_siginfo.si_signo) {
+ case SIGTRAP:
+ message = MonitorSIGTRAP(monitor, &plwp.pl_siginfo, plwp.pl_lwpid);
+ break;
+
+ default:
+ message = MonitorSignal(monitor, &plwp.pl_siginfo, plwp.pl_lwpid);
+ break;
+ }
+
+ process->SendMessage(message);
+ stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage;
+ }
+
+ return stop_monitoring;
+}
+
+ProcessMessage ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor,
+ const siginfo_t *info,
+ lldb::tid_t tid) {
+ ProcessMessage message;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ assert(monitor);
+ assert(info && info->si_signo == SIGTRAP && "Unexpected child signal!");
+
+ switch (info->si_code) {
+ default:
+ assert(false && "Unexpected SIGTRAP code!");
+ break;
+
+ case (SIGTRAP /* | (PTRACE_EVENT_EXIT << 8) */): {
+ // The inferior process is about to exit. Maintain the process in a state
+ // of "limbo" until we are explicitly commanded to detach, destroy, resume,
+ // etc.
+ unsigned long data = 0;
+ if (!monitor->GetEventMessage(tid, &data))
+ data = -1;
+ if (log)
+ log->Printf("ProcessMonitor::%s() received exit? event, data = %lx, tid "
+ "= %" PRIu64,
+ __FUNCTION__, data, tid);
+ message = ProcessMessage::Limbo(tid, (data >> 8));
+ break;
+ }
+
+ case 0:
+ case TRAP_TRACE:
+#ifdef TRAP_CAP
+ // Map TRAP_CAP to a trace trap in the absense of a more specific handler.
+ case TRAP_CAP:
+#endif
+ if (log)
+ log->Printf("ProcessMonitor::%s() received trace event, tid = %" PRIu64
+ " : si_code = %d",
+ __FUNCTION__, tid, info->si_code);
+ message = ProcessMessage::Trace(tid);
+ break;
+
+ case SI_KERNEL:
+ case TRAP_BRKPT:
+ if (monitor->m_process->IsSoftwareStepBreakpoint(tid)) {
+ if (log)
+ log->Printf("ProcessMonitor::%s() received sw single step breakpoint "
+ "event, tid = %" PRIu64,
+ __FUNCTION__, tid);
+ message = ProcessMessage::Trace(tid);
+ } else {
+ if (log)
+ log->Printf(
+ "ProcessMonitor::%s() received breakpoint event, tid = %" PRIu64,
+ __FUNCTION__, tid);
+ message = ProcessMessage::Break(tid);
+ }
+ break;
+ }
+
+ return message;
+}
+
+ProcessMessage ProcessMonitor::MonitorSignal(ProcessMonitor *monitor,
+ const siginfo_t *info,
+ lldb::tid_t tid) {
+ ProcessMessage message;
+ int signo = info->si_signo;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ // POSIX says that process behaviour is undefined after it ignores a SIGFPE,
+ // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a kill(2)
+ // or raise(3). Similarly for tgkill(2) on FreeBSD.
+ //
+ // IOW, user generated signals never generate what we consider to be a
+ // "crash".
+ //
+ // Similarly, ACK signals generated by this monitor.
+ if (info->si_code == SI_USER) {
+ if (log)
+ log->Printf(
+ "ProcessMonitor::%s() received signal %s with code %s, pid = %d",
+ __FUNCTION__,
+ monitor->m_process->GetUnixSignals()->GetSignalAsCString(signo),
+ "SI_USER", info->si_pid);
+ if (info->si_pid == getpid())
+ return ProcessMessage::SignalDelivered(tid, signo);
+ else
+ return ProcessMessage::Signal(tid, signo);
+ }
+
+ if (log)
+ log->Printf(
+ "ProcessMonitor::%s() received signal %s", __FUNCTION__,
+ monitor->m_process->GetUnixSignals()->GetSignalAsCString(signo));
+
+ switch (signo) {
+ case SIGSEGV:
+ case SIGILL:
+ case SIGFPE:
+ case SIGBUS:
+ lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
+ const auto reason = GetCrashReason(*info);
+ if (reason != CrashReason::eInvalidCrashReason) {
+ return ProcessMessage::Crash(tid, reason, signo, fault_addr);
+ } // else; Use atleast si_signo info for other si_code
+ }
+
+ // Everything else is "normal" and does not require any special action on our
+ // part.
+ return ProcessMessage::Signal(tid, signo);
+}
+
+void ProcessMonitor::ServeOperation(OperationArgs *args) {
+ ProcessMonitor *monitor = args->m_monitor;
+
+ // We are finised with the arguments and are ready to go. Sync with the
+ // parent thread and start serving operations on the inferior.
+ sem_post(&args->m_semaphore);
+
+ for (;;) {
+ // wait for next pending operation
+ sem_wait(&monitor->m_operation_pending);
+
+ monitor->m_operation->Execute(monitor);
+
+ // notify calling thread that operation is complete
+ sem_post(&monitor->m_operation_done);
+ }
+}
+
+void ProcessMonitor::DoOperation(Operation *op) {
+ std::lock_guard<std::mutex> guard(m_operation_mutex);
+
+ m_operation = op;
+
+ // notify operation thread that an operation is ready to be processed
+ sem_post(&m_operation_pending);
+
+ // wait for operation to complete
+ sem_wait(&m_operation_done);
+}
+
+size_t ProcessMonitor::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ Status &error) {
+ size_t result;
+ ReadOperation op(vm_addr, buf, size, error, result);
+ DoOperation(&op);
+ return result;
+}
+
+size_t ProcessMonitor::WriteMemory(lldb::addr_t vm_addr, const void *buf,
+ size_t size, lldb_private::Status &error) {
+ size_t result;
+ WriteOperation op(vm_addr, buf, size, error, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ReadRegisterValue(lldb::tid_t tid, unsigned offset,
+ const char *reg_name, unsigned size,
+ RegisterValue &value) {
+ bool result;
+ ReadRegOperation op(tid, offset, size, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::WriteRegisterValue(lldb::tid_t tid, unsigned offset,
+ const char *reg_name,
+ const RegisterValue &value) {
+ bool result;
+ WriteRegOperation op(tid, offset, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ReadDebugRegisterValue(
+ lldb::tid_t tid, unsigned offset, const char *reg_name, unsigned size,
+ lldb_private::RegisterValue &value) {
+ bool result;
+ ReadDebugRegOperation op(tid, offset, size, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::WriteDebugRegisterValue(
+ lldb::tid_t tid, unsigned offset, const char *reg_name,
+ const lldb_private::RegisterValue &value) {
+ bool result;
+ WriteDebugRegOperation op(tid, offset, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size) {
+ bool result;
+ ReadGPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size) {
+ bool result;
+ ReadFPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ReadRegisterSet(lldb::tid_t tid, void *buf,
+ size_t buf_size, unsigned int regset) {
+ return false;
+}
+
+bool ProcessMonitor::WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size) {
+ bool result;
+ WriteGPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size) {
+ bool result;
+ WriteFPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::WriteRegisterSet(lldb::tid_t tid, void *buf,
+ size_t buf_size, unsigned int regset) {
+ return false;
+}
+
+bool ProcessMonitor::ReadThreadPointer(lldb::tid_t tid, lldb::addr_t &value) {
+ return false;
+}
+
+bool ProcessMonitor::Resume(lldb::tid_t unused, uint32_t signo) {
+ bool result;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ if (log) {
+ const char *signame =
+ m_process->GetUnixSignals()->GetSignalAsCString(signo);
+ if (signame == nullptr)
+ signame = "<none>";
+ log->Printf("ProcessMonitor::%s() resuming pid %" PRIu64 " with signal %s",
+ __FUNCTION__, GetPID(), signame);
+ }
+ ResumeOperation op(signo, result);
+ DoOperation(&op);
+ if (log)
+ log->Printf("ProcessMonitor::%s() resuming result = %s", __FUNCTION__,
+ result ? "true" : "false");
+ return result;
+}
+
+bool ProcessMonitor::SingleStep(lldb::tid_t unused, uint32_t signo) {
+ bool result;
+ SingleStepOperation op(signo, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::Kill() {
+ bool result;
+ KillOperation op(result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::GetLwpInfo(lldb::tid_t tid, void *lwpinfo,
+ int &ptrace_err) {
+ bool result;
+ LwpInfoOperation op(tid, lwpinfo, result, ptrace_err);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ThreadSuspend(lldb::tid_t tid, bool suspend) {
+ bool result;
+ ThreadSuspendOperation op(tid, suspend, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message) {
+ bool result;
+ EventMessageOperation op(tid, message, result);
+ DoOperation(&op);
+ return result;
+}
+
+lldb_private::Status ProcessMonitor::Detach(lldb::tid_t tid) {
+ lldb_private::Status error;
+ if (tid != LLDB_INVALID_THREAD_ID) {
+ DetachOperation op(error);
+ DoOperation(&op);
+ }
+ return error;
+}
+
+bool ProcessMonitor::DupDescriptor(const FileSpec &file_spec, int fd,
+ int flags) {
+ int target_fd = llvm::sys::RetryAfterSignal(-1, open,
+ file_spec.GetCString(), flags, 0666);
+
+ if (target_fd == -1)
+ return false;
+
+ if (dup2(target_fd, fd) == -1)
+ return false;
+
+ return (close(target_fd) == -1) ? false : true;
+}
+
+void ProcessMonitor::StopMonitoringChildProcess() {
+ if (m_monitor_thread && m_monitor_thread->IsJoinable()) {
+ m_monitor_thread->Cancel();
+ m_monitor_thread->Join(nullptr);
+ m_monitor_thread->Reset();
+ }
+}
+
+void ProcessMonitor::StopMonitor() {
+ StopMonitoringChildProcess();
+ StopOpThread();
+ sem_destroy(&m_operation_pending);
+ sem_destroy(&m_operation_done);
+ if (m_terminal_fd >= 0) {
+ close(m_terminal_fd);
+ m_terminal_fd = -1;
+ }
+}
+
+// FIXME: On Linux, when a new thread is created, we receive to notifications,
+// (1) a SIGTRAP|PTRACE_EVENT_CLONE from the main process thread with the child
+// thread id as additional information, and (2) a SIGSTOP|SI_USER from the new
+// child thread indicating that it has is stopped because we attached. We have
+// no guarantee of the order in which these arrive, but we need both before we
+// are ready to proceed. We currently keep a list of threads which have sent
+// the initial SIGSTOP|SI_USER event. Then when we receive the
+// SIGTRAP|PTRACE_EVENT_CLONE notification, if the initial stop has not
+// occurred we call ProcessMonitor::WaitForInitialTIDStop() to wait for it.
+//
+// Right now, the above logic is in ProcessPOSIX, so we need a definition of
+// this function in the FreeBSD ProcessMonitor implementation even if it isn't
+// logically needed.
+//
+// We really should figure out what actually happens on FreeBSD and move the
+// Linux-specific logic out of ProcessPOSIX as needed.
+
+bool ProcessMonitor::WaitForInitialTIDStop(lldb::tid_t tid) { return true; }
+
+void ProcessMonitor::StopOpThread() {
+ if (m_operation_thread && m_operation_thread->IsJoinable()) {
+ m_operation_thread->Cancel();
+ m_operation_thread->Join(nullptr);
+ m_operation_thread->Reset();
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h
new file mode 100644
index 000000000000..c5edfc0be95a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h
@@ -0,0 +1,279 @@
+//===-- ProcessMonitor.h -------------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessMonitor_H_
+#define liblldb_ProcessMonitor_H_
+
+#include <semaphore.h>
+#include <signal.h>
+
+#include <mutex>
+
+#include "lldb/Host/HostThread.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+class Status;
+class Module;
+class Scalar;
+} // End lldb_private namespace.
+
+class ProcessFreeBSD;
+class Operation;
+
+/// \class ProcessMonitor
+/// Manages communication with the inferior (debugee) process.
+///
+/// Upon construction, this class prepares and launches an inferior process
+/// for debugging.
+///
+/// Changes in the inferior process state are propagated to the associated
+/// ProcessFreeBSD instance by calling ProcessFreeBSD::SendMessage with the
+/// appropriate ProcessMessage events.
+///
+/// A purposely minimal set of operations are provided to interrogate and change
+/// the inferior process state.
+class ProcessMonitor {
+public:
+ /// Launches an inferior process ready for debugging. Forms the
+ /// implementation of Process::DoLaunch.
+ ProcessMonitor(ProcessFreeBSD *process, lldb_private::Module *module,
+ char const *argv[], lldb_private::Environment env,
+ const lldb_private::FileSpec &stdin_file_spec,
+ const lldb_private::FileSpec &stdout_file_spec,
+ const lldb_private::FileSpec &stderr_file_spec,
+ const lldb_private::FileSpec &working_dir,
+ const lldb_private::ProcessLaunchInfo &launch_info,
+ lldb_private::Status &error);
+
+ ProcessMonitor(ProcessFreeBSD *process, lldb::pid_t pid,
+ lldb_private::Status &error);
+
+ ~ProcessMonitor();
+
+ /// Provides the process number of debugee.
+ lldb::pid_t GetPID() const { return m_pid; }
+
+ /// Returns the process associated with this ProcessMonitor.
+ ProcessFreeBSD &GetProcess() { return *m_process; }
+
+ /// Returns a file descriptor to the controlling terminal of the inferior
+ /// process.
+ ///
+ /// Reads from this file descriptor yield both the standard output and
+ /// standard error of this debugee. Even if stderr and stdout were
+ /// redirected on launch it may still happen that data is available on this
+ /// descriptor (if the inferior process opens /dev/tty, for example). This
+ /// descriptor is closed after a call to StopMonitor().
+ ///
+ /// If this monitor was attached to an existing process this method returns
+ /// -1.
+ int GetTerminalFD() const { return m_terminal_fd; }
+
+ /// Reads \p size bytes from address @vm_adder in the inferior process
+ /// address space.
+ ///
+ /// This method is provided to implement Process::DoReadMemory.
+ size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ lldb_private::Status &error);
+
+ /// Writes \p size bytes from address \p vm_adder in the inferior process
+ /// address space.
+ ///
+ /// This method is provided to implement Process::DoWriteMemory.
+ size_t WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
+ lldb_private::Status &error);
+
+ /// Reads the contents from the register identified by the given
+ /// (architecture dependent) offset.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name,
+ unsigned size, lldb_private::RegisterValue &value);
+
+ /// Writes the given value to the register identified by the given
+ /// (architecture dependent) offset.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool WriteRegisterValue(lldb::tid_t tid, unsigned offset,
+ const char *reg_name,
+ const lldb_private::RegisterValue &value);
+
+ /// Reads the contents from the debug register identified by the given
+ /// (architecture dependent) offset.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool ReadDebugRegisterValue(lldb::tid_t tid, unsigned offset,
+ const char *reg_name, unsigned size,
+ lldb_private::RegisterValue &value);
+
+ /// Writes the given value to the debug register identified by the given
+ /// (architecture dependent) offset.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool WriteDebugRegisterValue(lldb::tid_t tid, unsigned offset,
+ const char *reg_name,
+ const lldb_private::RegisterValue &value);
+ /// Reads all general purpose registers into the specified buffer.
+ bool ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Reads all floating point registers into the specified buffer.
+ bool ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Reads the specified register set into the specified buffer.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size,
+ unsigned int regset);
+
+ /// Writes all general purpose registers into the specified buffer.
+ bool WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Writes all floating point registers into the specified buffer.
+ bool WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Writes the specified register set into the specified buffer.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size,
+ unsigned int regset);
+
+ /// Reads the value of the thread-specific pointer for a given thread ID.
+ bool ReadThreadPointer(lldb::tid_t tid, lldb::addr_t &value);
+
+ /// Returns current thread IDs in process
+ size_t GetCurrentThreadIDs(std::vector<lldb::tid_t> &thread_ids);
+
+ /// Writes a ptrace_lwpinfo structure corresponding to the given thread ID
+ /// to the memory region pointed to by \p lwpinfo.
+ bool GetLwpInfo(lldb::tid_t tid, void *lwpinfo, int &error_no);
+
+ /// Suspends or unsuspends a thread prior to process resume or step.
+ bool ThreadSuspend(lldb::tid_t tid, bool suspend);
+
+ /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG)
+ /// corresponding to the given thread IDto the memory pointed to by @p
+ /// message.
+ bool GetEventMessage(lldb::tid_t tid, unsigned long *message);
+
+ /// Resumes the process. If \p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the process.
+ bool Resume(lldb::tid_t unused, uint32_t signo);
+
+ /// Single steps the process. If \p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the process.
+ bool SingleStep(lldb::tid_t unused, uint32_t signo);
+
+ /// Terminate the traced process.
+ bool Kill();
+
+ lldb_private::Status Detach(lldb::tid_t tid);
+
+ void StopMonitor();
+
+ // Waits for the initial stop message from a new thread.
+ bool WaitForInitialTIDStop(lldb::tid_t tid);
+
+private:
+ ProcessFreeBSD *m_process;
+
+ llvm::Optional<lldb_private::HostThread> m_operation_thread;
+ llvm::Optional<lldb_private::HostThread> m_monitor_thread;
+ lldb::pid_t m_pid;
+
+ int m_terminal_fd;
+
+ // current operation which must be executed on the privileged thread
+ Operation *m_operation;
+ std::mutex m_operation_mutex;
+
+ // semaphores notified when Operation is ready to be processed and when
+ // the operation is complete.
+ sem_t m_operation_pending;
+ sem_t m_operation_done;
+
+ struct OperationArgs {
+ OperationArgs(ProcessMonitor *monitor);
+
+ ~OperationArgs();
+
+ ProcessMonitor *m_monitor; // The monitor performing the attach.
+ sem_t m_semaphore; // Posted to once operation complete.
+ lldb_private::Status m_error; // Set if process operation failed.
+ };
+
+ /// \class LauchArgs
+ ///
+ /// Simple structure to pass data to the thread responsible for launching a
+ /// child process.
+ struct LaunchArgs : OperationArgs {
+ LaunchArgs(ProcessMonitor *monitor, lldb_private::Module *module,
+ char const **argv, lldb_private::Environment env,
+ const lldb_private::FileSpec &stdin_file_spec,
+ const lldb_private::FileSpec &stdout_file_spec,
+ const lldb_private::FileSpec &stderr_file_spec,
+ const lldb_private::FileSpec &working_dir);
+
+ ~LaunchArgs();
+
+ lldb_private::Module *m_module; // The executable image to launch.
+ char const **m_argv; // Process arguments.
+ lldb_private::Environment m_env; // Process environment.
+ const lldb_private::FileSpec m_stdin_file_spec; // Redirect stdin or empty.
+ const lldb_private::FileSpec
+ m_stdout_file_spec; // Redirect stdout or empty.
+ const lldb_private::FileSpec
+ m_stderr_file_spec; // Redirect stderr or empty.
+ const lldb_private::FileSpec m_working_dir; // Working directory or empty.
+ };
+
+ void StartLaunchOpThread(LaunchArgs *args, lldb_private::Status &error);
+
+ static void *LaunchOpThread(void *arg);
+
+ static bool Launch(LaunchArgs *args);
+
+ struct AttachArgs : OperationArgs {
+ AttachArgs(ProcessMonitor *monitor, lldb::pid_t pid);
+
+ ~AttachArgs();
+
+ lldb::pid_t m_pid; // pid of the process to be attached.
+ };
+
+ void StartAttachOpThread(AttachArgs *args, lldb_private::Status &error);
+
+ static void *AttachOpThread(void *args);
+
+ static void Attach(AttachArgs *args);
+
+ static void ServeOperation(OperationArgs *args);
+
+ static bool DupDescriptor(const lldb_private::FileSpec &file_spec, int fd,
+ int flags);
+
+ static bool MonitorCallback(ProcessMonitor *monitor, lldb::pid_t pid,
+ bool exited, int signal, int status);
+
+ static ProcessMessage MonitorSIGTRAP(ProcessMonitor *monitor,
+ const siginfo_t *info, lldb::pid_t pid);
+
+ static ProcessMessage MonitorSignal(ProcessMonitor *monitor,
+ const siginfo_t *info, lldb::pid_t pid);
+
+ void DoOperation(Operation *op);
+
+ /// Stops the child monitor thread.
+ void StopMonitoringChildProcess();
+
+ /// Stops the operation thread used to attach/launch a process.
+ void StopOpThread();
+};
+
+#endif // #ifndef liblldb_ProcessMonitor_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIX.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIX.h
new file mode 100644
index 000000000000..cf52a065232c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIX.h
@@ -0,0 +1,63 @@
+//===-- RegisterContextPOSIX.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIX_H_
+#define liblldb_RegisterContextPOSIX_H_
+
+#include "Plugins/Process/Utility/RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/ArchSpec.h"
+
+/// \class POSIXBreakpointProtocol
+///
+/// Extends RegisterClass with a few virtual operations useful on POSIX.
+class POSIXBreakpointProtocol {
+public:
+ POSIXBreakpointProtocol() { m_watchpoints_initialized = false; }
+ virtual ~POSIXBreakpointProtocol() {}
+
+ /// Updates the register state of the associated thread after hitting a
+ /// breakpoint (if that make sense for the architecture). Default
+ /// implementation simply returns true for architectures which do not
+ /// require any update.
+ ///
+ /// \return
+ /// True if the operation succeeded and false otherwise.
+ virtual bool UpdateAfterBreakpoint() = 0;
+
+ /// Determines the index in lldb's register file given a kernel byte offset.
+ virtual unsigned GetRegisterIndexFromOffset(unsigned offset) = 0;
+
+ // Checks to see if a watchpoint specified by hw_index caused the inferior
+ // to stop.
+ virtual bool IsWatchpointHit(uint32_t hw_index) = 0;
+
+ // Resets any watchpoints that have been hit.
+ virtual bool ClearWatchpointHits() = 0;
+
+ // Returns the watchpoint address associated with a watchpoint hardware
+ // index.
+ virtual lldb::addr_t GetWatchpointAddress(uint32_t hw_index) = 0;
+
+ virtual bool IsWatchpointVacant(uint32_t hw_index) = 0;
+
+ virtual bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size,
+ bool read, bool write,
+ uint32_t hw_index) = 0;
+
+ // From lldb_private::RegisterContext
+ virtual uint32_t NumSupportedHardwareWatchpoints() = 0;
+
+ // Force m_watchpoints_initialized to TRUE
+ void ForceWatchpointsInitialized() { m_watchpoints_initialized = true; }
+
+protected:
+ bool m_watchpoints_initialized;
+};
+
+#endif // #ifndef liblldb_RegisterContextPOSIX_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.cpp
new file mode 100644
index 000000000000..f0c4526357cc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.cpp
@@ -0,0 +1,259 @@
+//===-- RegisterContextPOSIXProcessMonitor_arm.cpp -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_arm.h"
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+#define REG_CONTEXT_SIZE (GetGPRSize())
+
+RegisterContextPOSIXProcessMonitor_arm::RegisterContextPOSIXProcessMonitor_arm(
+ Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : RegisterContextPOSIX_arm(thread, concrete_frame_idx, register_info) {}
+
+ProcessMonitor &RegisterContextPOSIXProcessMonitor_arm::GetMonitor() {
+ ProcessSP base = CalculateProcess();
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(base.get());
+ return process->GetMonitor();
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ReadGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadGPR(m_thread.GetID(), &m_gpr_arm, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ReadFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadFPR(m_thread.GetID(), &m_fpr, sizeof(m_fpr));
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::WriteGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteGPR(m_thread.GetID(), &m_gpr_arm, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::WriteFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteFPR(m_thread.GetID(), &m_fpr, sizeof(m_fpr));
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ReadRegister(
+ const unsigned reg, RegisterValue &value) {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadRegisterValue(m_thread.GetID(), GetRegisterOffset(reg),
+ GetRegisterName(reg), GetRegisterSize(reg),
+ value);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::WriteRegister(
+ const unsigned reg, const RegisterValue &value) {
+ unsigned reg_to_write = reg;
+ RegisterValue value_to_write = value;
+
+ // Check if this is a subregister of a full register.
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) {
+ RegisterValue full_value;
+ uint32_t full_reg = reg_info->invalidate_regs[0];
+ const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg);
+
+ // Read the full register.
+ if (ReadRegister(full_reg_info, full_value)) {
+ Status error;
+ ByteOrder byte_order = GetByteOrder();
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the full register.
+ const uint32_t dest_size = full_value.GetAsMemoryData(
+ full_reg_info, dst, sizeof(dst), byte_order, error);
+ if (error.Success() && dest_size) {
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the source data.
+ const uint32_t src_size = value.GetAsMemoryData(
+ reg_info, src, sizeof(src), byte_order, error);
+ if (error.Success() && src_size && (src_size < dest_size)) {
+ // Copy the src bytes to the destination.
+ memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size);
+ // Set this full register as the value to write.
+ value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order);
+ value_to_write.SetType(full_reg_info);
+ reg_to_write = full_reg;
+ }
+ }
+ }
+ }
+
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg_to_write),
+ GetRegisterName(reg_to_write), value_to_write);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!reg_info)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsFPR(reg)) {
+ if (!ReadFPR())
+ return false;
+ } else {
+ return ReadRegister(reg, value);
+ }
+
+ // Get pointer to m_fpr variable and set the data from it.
+ assert(reg_info->byte_offset < sizeof m_fpr);
+ uint8_t *src = (uint8_t *)&m_fpr + reg_info->byte_offset;
+ switch (reg_info->byte_size) {
+ case 2:
+ value.SetUInt16(*(uint16_t *)src);
+ return true;
+ case 4:
+ value.SetUInt32(*(uint32_t *)src);
+ return true;
+ case 8:
+ value.SetUInt64(*(uint64_t *)src);
+ return true;
+ default:
+ assert(false && "Unhandled data size.");
+ return false;
+ }
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsGPR(reg)) {
+ return WriteRegister(reg, value);
+ } else if (IsFPR(reg)) {
+ return WriteFPR();
+ }
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ReadAllRegisterValues(
+ DataBufferSP &data_sp) {
+ bool success = false;
+ data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ if (ReadGPR() && ReadFPR()) {
+ uint8_t *dst = data_sp->GetBytes();
+ success = dst != 0;
+
+ if (success) {
+ ::memcpy(dst, &m_gpr_arm, GetGPRSize());
+ dst += GetGPRSize();
+ ::memcpy(dst, &m_fpr, sizeof(m_fpr));
+ }
+ }
+ return success;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::WriteAllRegisterValues(
+ const DataBufferSP &data_sp) {
+ bool success = false;
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ uint8_t *src = data_sp->GetBytes();
+ if (src) {
+ ::memcpy(&m_gpr_arm, src, GetGPRSize());
+
+ if (WriteGPR()) {
+ src += GetGPRSize();
+ ::memcpy(&m_fpr, src, sizeof(m_fpr));
+
+ success = WriteFPR();
+ }
+ }
+ }
+ return success;
+}
+
+uint32_t RegisterContextPOSIXProcessMonitor_arm::SetHardwareWatchpoint(
+ addr_t addr, size_t size, bool read, bool write) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ uint32_t hw_index;
+
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) {
+ if (IsWatchpointVacant(hw_index))
+ return SetHardwareWatchpointWithIndex(addr, size, read, write, hw_index);
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ClearHardwareWatchpoint(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::HardwareSingleStep(bool enable) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::UpdateAfterBreakpoint() {
+ lldb::addr_t pc;
+
+ if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
+ return false;
+
+ return true;
+}
+
+unsigned RegisterContextPOSIXProcessMonitor_arm::GetRegisterIndexFromOffset(
+ unsigned offset) {
+ unsigned reg;
+ for (reg = 0; reg < k_num_registers_arm; reg++) {
+ if (GetRegisterInfo()[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < k_num_registers_arm && "Invalid register offset.");
+ return reg;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::IsWatchpointHit(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ClearWatchpointHits() {
+ return false;
+}
+
+addr_t RegisterContextPOSIXProcessMonitor_arm::GetWatchpointAddress(
+ uint32_t hw_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::IsWatchpointVacant(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::SetHardwareWatchpointWithIndex(
+ addr_t addr, size_t size, bool read, bool write, uint32_t hw_index) {
+ return false;
+}
+
+uint32_t
+RegisterContextPOSIXProcessMonitor_arm::NumSupportedHardwareWatchpoints() {
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.h
new file mode 100644
index 000000000000..b376967df99c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.h
@@ -0,0 +1,76 @@
+//===-- RegisterContextPOSIXProcessMonitor_arm.h --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIXProcessMonitor_arm_H_
+#define liblldb_RegisterContextPOSIXProcessMonitor_arm_H_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm.h"
+#include "RegisterContextPOSIX.h"
+
+class RegisterContextPOSIXProcessMonitor_arm : public RegisterContextPOSIX_arm,
+ public POSIXBreakpointProtocol {
+public:
+ RegisterContextPOSIXProcessMonitor_arm(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+protected:
+ bool ReadGPR();
+
+ bool ReadFPR();
+
+ bool WriteGPR();
+
+ bool WriteFPR();
+
+ // lldb_private::RegisterContext
+ bool ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const unsigned reg,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write);
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index);
+
+ bool HardwareSingleStep(bool enable);
+
+ // POSIXBreakpointProtocol
+ bool UpdateAfterBreakpoint();
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ bool IsWatchpointHit(uint32_t hw_index);
+
+ bool ClearWatchpointHits();
+
+ lldb::addr_t GetWatchpointAddress(uint32_t hw_index);
+
+ bool IsWatchpointVacant(uint32_t hw_index);
+
+ bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read,
+ bool write, uint32_t hw_index);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+private:
+ ProcessMonitor &GetMonitor();
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp
new file mode 100644
index 000000000000..147f4b56a804
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp
@@ -0,0 +1,264 @@
+//===-- RegisterContextPOSIXProcessMonitor_arm64.cpp -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h"
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_arm64.h"
+
+#define REG_CONTEXT_SIZE (GetGPRSize())
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextPOSIXProcessMonitor_arm64::
+ RegisterContextPOSIXProcessMonitor_arm64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : RegisterContextPOSIX_arm64(thread, concrete_frame_idx, register_info) {}
+
+ProcessMonitor &RegisterContextPOSIXProcessMonitor_arm64::GetMonitor() {
+ lldb::ProcessSP base = CalculateProcess();
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(base.get());
+ return process->GetMonitor();
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ReadGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadGPR(m_thread.GetID(), &m_gpr_arm64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ReadFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadFPR(m_thread.GetID(), &m_fpr, sizeof m_fpr);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::WriteGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteGPR(m_thread.GetID(), &m_gpr_arm64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::WriteFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteFPR(m_thread.GetID(), &m_fpr, sizeof m_fpr);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ReadRegister(
+ const unsigned reg, lldb_private::RegisterValue &value) {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadRegisterValue(m_thread.GetID(), GetRegisterOffset(reg),
+ GetRegisterName(reg), GetRegisterSize(reg),
+ value);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::WriteRegister(
+ const unsigned reg, const lldb_private::RegisterValue &value) {
+ unsigned reg_to_write = reg;
+ lldb_private::RegisterValue value_to_write = value;
+
+ // Check if this is a subregister of a full register.
+ const lldb_private::RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) {
+ lldb_private::RegisterValue full_value;
+ uint32_t full_reg = reg_info->invalidate_regs[0];
+ const lldb_private::RegisterInfo *full_reg_info =
+ GetRegisterInfoAtIndex(full_reg);
+
+ // Read the full register.
+ if (ReadRegister(full_reg_info, full_value)) {
+ lldb_private::Status error;
+ lldb::ByteOrder byte_order = GetByteOrder();
+ uint8_t dst[lldb_private::RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the full register.
+ const uint32_t dest_size = full_value.GetAsMemoryData(
+ full_reg_info, dst, sizeof(dst), byte_order, error);
+ if (error.Success() && dest_size) {
+ uint8_t src[lldb_private::RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the source data.
+ const uint32_t src_size = value.GetAsMemoryData(
+ reg_info, src, sizeof(src), byte_order, error);
+ if (error.Success() && src_size && (src_size < dest_size)) {
+ // Copy the src bytes to the destination.
+ ::memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size);
+ // Set this full register as the value to write.
+ value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order);
+ value_to_write.SetType(full_reg_info);
+ reg_to_write = full_reg;
+ }
+ }
+ }
+ }
+
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg_to_write),
+ GetRegisterName(reg_to_write), value_to_write);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ReadRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) {
+ if (!reg_info)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+
+ if (IsFPR(reg)) {
+ if (!ReadFPR())
+ return false;
+ } else {
+ uint32_t full_reg = reg;
+ bool is_subreg = reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM);
+
+ if (is_subreg) {
+ // Read the full aligned 64-bit register.
+ full_reg = reg_info->invalidate_regs[0];
+ }
+ return ReadRegister(full_reg, value);
+ }
+
+ // Get pointer to m_fpr variable and set the data from it.
+ assert(reg_info->byte_offset < sizeof m_fpr);
+ uint8_t *src = (uint8_t *)&m_fpr + reg_info->byte_offset;
+ switch (reg_info->byte_size) {
+ case 2:
+ value.SetUInt16(*(uint16_t *)src);
+ return true;
+ case 4:
+ value.SetUInt32(*(uint32_t *)src);
+ return true;
+ case 8:
+ value.SetUInt64(*(uint64_t *)src);
+ return true;
+ default:
+ assert(false && "Unhandled data size.");
+ return false;
+ }
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::WriteRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+
+ if (IsGPR(reg))
+ return WriteRegister(reg, value);
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ bool success = false;
+ data_sp.reset(new lldb_private::DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ if (ReadGPR() && ReadFPR()) {
+ uint8_t *dst = data_sp->GetBytes();
+ success = dst != 0;
+
+ if (success) {
+ ::memcpy(dst, &m_gpr_arm64, GetGPRSize());
+ dst += GetGPRSize();
+ ::memcpy(dst, &m_fpr, sizeof m_fpr);
+ }
+ }
+ return success;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ bool success = false;
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ uint8_t *src = data_sp->GetBytes();
+ if (src) {
+ ::memcpy(&m_gpr_arm64, src, GetGPRSize());
+ if (WriteGPR()) {
+ src += GetGPRSize();
+ ::memcpy(&m_fpr, src, sizeof m_fpr);
+ success = WriteFPR();
+ }
+ }
+ }
+ return success;
+}
+
+uint32_t RegisterContextPOSIXProcessMonitor_arm64::SetHardwareWatchpoint(
+ lldb::addr_t addr, size_t size, bool read, bool write) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ uint32_t hw_index;
+
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) {
+ if (IsWatchpointVacant(hw_index))
+ return SetHardwareWatchpointWithIndex(addr, size, read, write, hw_index);
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ClearHardwareWatchpoint(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::HardwareSingleStep(bool enable) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::UpdateAfterBreakpoint() {
+ if (GetPC() == LLDB_INVALID_ADDRESS)
+ return false;
+
+ return true;
+}
+
+unsigned RegisterContextPOSIXProcessMonitor_arm64::GetRegisterIndexFromOffset(
+ unsigned offset) {
+ unsigned reg;
+ for (reg = 0; reg < k_num_registers_arm64; reg++) {
+ if (GetRegisterInfo()[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < k_num_registers_arm64 && "Invalid register offset.");
+ return reg;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::IsWatchpointHit(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ClearWatchpointHits() {
+ return false;
+}
+
+lldb::addr_t RegisterContextPOSIXProcessMonitor_arm64::GetWatchpointAddress(
+ uint32_t hw_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::IsWatchpointVacant(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::SetHardwareWatchpointWithIndex(
+ lldb::addr_t addr, size_t size, bool read, bool write, uint32_t hw_index) {
+ return false;
+}
+
+uint32_t
+RegisterContextPOSIXProcessMonitor_arm64::NumSupportedHardwareWatchpoints() {
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.h
new file mode 100644
index 000000000000..d54d34e89cad
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.h
@@ -0,0 +1,77 @@
+//===-- RegisterContextPOSIXProcessMonitor_arm64.h --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIXProcessMonitor_arm64_H_
+#define liblldb_RegisterContextPOSIXProcessMonitor_arm64_H_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h"
+#include "RegisterContextPOSIX.h"
+
+class RegisterContextPOSIXProcessMonitor_arm64
+ : public RegisterContextPOSIX_arm64,
+ public POSIXBreakpointProtocol {
+public:
+ RegisterContextPOSIXProcessMonitor_arm64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+protected:
+ bool ReadGPR();
+
+ bool ReadFPR();
+
+ bool WriteGPR();
+
+ bool WriteFPR();
+
+ // lldb_private::RegisterContext
+ bool ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const unsigned reg,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write);
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index);
+
+ bool HardwareSingleStep(bool enable);
+
+ // POSIXBreakpointProtocol
+ bool UpdateAfterBreakpoint();
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ bool IsWatchpointHit(uint32_t hw_index);
+
+ bool ClearWatchpointHits();
+
+ lldb::addr_t GetWatchpointAddress(uint32_t hw_index);
+
+ bool IsWatchpointVacant(uint32_t hw_index);
+
+ bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read,
+ bool write, uint32_t hw_index);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+private:
+ ProcessMonitor &GetMonitor();
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.cpp
new file mode 100644
index 000000000000..db9b5a6a038c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.cpp
@@ -0,0 +1,262 @@
+//===-- RegisterContextPOSIXProcessMonitor_mips64.cpp -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_mips64.h"
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_mips64.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+#define REG_CONTEXT_SIZE (GetGPRSize())
+
+RegisterContextPOSIXProcessMonitor_mips64::
+ RegisterContextPOSIXProcessMonitor_mips64(
+ Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : RegisterContextPOSIX_mips64(thread, concrete_frame_idx, register_info) {}
+
+ProcessMonitor &RegisterContextPOSIXProcessMonitor_mips64::GetMonitor() {
+ ProcessSP base = CalculateProcess();
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(base.get());
+ return process->GetMonitor();
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ReadGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadGPR(m_thread.GetID(), &m_gpr_mips64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ReadFPR() {
+ // XXX not yet implemented
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::WriteGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteGPR(m_thread.GetID(), &m_gpr_mips64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::WriteFPR() {
+ // XXX not yet implemented
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ReadRegister(
+ const unsigned reg, RegisterValue &value) {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadRegisterValue(m_thread.GetID(), GetRegisterOffset(reg),
+ GetRegisterName(reg), GetRegisterSize(reg),
+ value);
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::WriteRegister(
+ const unsigned reg, const RegisterValue &value) {
+ unsigned reg_to_write = reg;
+ RegisterValue value_to_write = value;
+
+ // Check if this is a subregister of a full register.
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) {
+ RegisterValue full_value;
+ uint32_t full_reg = reg_info->invalidate_regs[0];
+ const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg);
+
+ // Read the full register.
+ if (ReadRegister(full_reg_info, full_value)) {
+ Status error;
+ ByteOrder byte_order = GetByteOrder();
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the full register.
+ const uint32_t dest_size = full_value.GetAsMemoryData(
+ full_reg_info, dst, sizeof(dst), byte_order, error);
+ if (error.Success() && dest_size) {
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the source data.
+ const uint32_t src_size = value.GetAsMemoryData(
+ reg_info, src, sizeof(src), byte_order, error);
+ if (error.Success() && src_size && (src_size < dest_size)) {
+ // Copy the src bytes to the destination.
+ memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size);
+ // Set this full register as the value to write.
+ value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order);
+ value_to_write.SetType(full_reg_info);
+ reg_to_write = full_reg;
+ }
+ }
+ }
+ }
+
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg_to_write),
+ GetRegisterName(reg_to_write), value_to_write);
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!reg_info)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsFPR(reg)) {
+ if (!ReadFPR())
+ return false;
+ } else {
+ uint32_t full_reg = reg;
+ bool is_subreg = reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM);
+
+ if (is_subreg) {
+ // Read the full aligned 64-bit register.
+ full_reg = reg_info->invalidate_regs[0];
+ }
+
+ bool success = ReadRegister(full_reg, value);
+
+ if (success) {
+ // If our read was not aligned (for ah,bh,ch,dh), shift our returned
+ // value one byte to the right.
+ if (is_subreg && (reg_info->byte_offset & 0x1))
+ value.SetUInt64(value.GetAsUInt64() >> 8);
+
+ // If our return byte size was greater than the return value reg size,
+ // then use the type specified by reg_info rather than the uint64_t
+ // default
+ if (value.GetByteSize() > reg_info->byte_size)
+ value.SetType(reg_info);
+ }
+ return success;
+ }
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsGPR(reg))
+ return WriteRegister(reg, value);
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ReadAllRegisterValues(
+ DataBufferSP &data_sp) {
+ bool success = false;
+ data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ if (ReadGPR() && ReadFPR()) {
+ uint8_t *dst = data_sp->GetBytes();
+ success = dst != 0;
+
+ if (success) {
+ ::memcpy(dst, &m_gpr_mips64, GetGPRSize());
+ }
+ }
+ return success;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::WriteAllRegisterValues(
+ const DataBufferSP &data_sp) {
+ bool success = false;
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ uint8_t *src = data_sp->GetBytes();
+ if (src) {
+ ::memcpy(&m_gpr_mips64, src, GetGPRSize());
+
+ if (WriteGPR()) {
+ src += GetGPRSize();
+ }
+ }
+ }
+ return success;
+}
+
+uint32_t RegisterContextPOSIXProcessMonitor_mips64::SetHardwareWatchpoint(
+ addr_t addr, size_t size, bool read, bool write) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ uint32_t hw_index;
+
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) {
+ if (IsWatchpointVacant(hw_index))
+ return SetHardwareWatchpointWithIndex(addr, size, read, write, hw_index);
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ClearHardwareWatchpoint(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::HardwareSingleStep(
+ bool enable) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::UpdateAfterBreakpoint() {
+ // PC points one byte past the int3 responsible for the breakpoint.
+ lldb::addr_t pc;
+
+ if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
+ return false;
+
+ SetPC(pc - 1);
+ return true;
+}
+
+unsigned RegisterContextPOSIXProcessMonitor_mips64::GetRegisterIndexFromOffset(
+ unsigned offset) {
+ unsigned reg;
+ for (reg = 0; reg < k_num_registers_mips64; reg++) {
+ if (GetRegisterInfo()[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < k_num_registers_mips64 && "Invalid register offset.");
+ return reg;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::IsWatchpointHit(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ClearWatchpointHits() {
+ return false;
+}
+
+addr_t RegisterContextPOSIXProcessMonitor_mips64::GetWatchpointAddress(
+ uint32_t hw_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::IsWatchpointVacant(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::SetHardwareWatchpointWithIndex(
+ addr_t addr, size_t size, bool read, bool write, uint32_t hw_index) {
+ return false;
+}
+
+uint32_t
+RegisterContextPOSIXProcessMonitor_mips64::NumSupportedHardwareWatchpoints() {
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.h
new file mode 100644
index 000000000000..a482cd3f1e1c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.h
@@ -0,0 +1,80 @@
+//===-- RegisterContextPOSIXProcessMonitor_mips64.h -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIXProcessMonitor_mips64_H_
+#define liblldb_RegisterContextPOSIXProcessMonitor_mips64_H_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_mips64.h"
+#include "Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h"
+#include "RegisterContextPOSIX.h"
+
+class RegisterContextPOSIXProcessMonitor_mips64
+ : public RegisterContextPOSIX_mips64,
+ public POSIXBreakpointProtocol {
+public:
+ RegisterContextPOSIXProcessMonitor_mips64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+protected:
+ bool ReadGPR();
+
+ bool ReadFPR();
+
+ bool WriteGPR();
+
+ bool WriteFPR();
+
+ // lldb_private::RegisterContext
+ bool ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const unsigned reg,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write);
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index);
+
+ bool HardwareSingleStep(bool enable);
+
+ // POSIXBreakpointProtocol
+ bool UpdateAfterBreakpoint();
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ bool IsWatchpointHit(uint32_t hw_index);
+
+ bool ClearWatchpointHits();
+
+ lldb::addr_t GetWatchpointAddress(uint32_t hw_index);
+
+ bool IsWatchpointVacant(uint32_t hw_index);
+
+ bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read,
+ bool write, uint32_t hw_index);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+private:
+ uint64_t
+ m_gpr_mips64[k_num_gpr_registers_mips64]; // general purpose registers.
+ ProcessMonitor &GetMonitor();
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.cpp
new file mode 100644
index 000000000000..77694733fa39
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.cpp
@@ -0,0 +1,274 @@
+//===-- RegisterContextPOSIXProcessMonitor_powerpc.cpp ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_powerpc.h"
+#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+#define REG_CONTEXT_SIZE (GetGPRSize())
+
+RegisterContextPOSIXProcessMonitor_powerpc::
+ RegisterContextPOSIXProcessMonitor_powerpc(
+ Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : RegisterContextPOSIX_powerpc(thread, concrete_frame_idx, register_info) {}
+
+ProcessMonitor &RegisterContextPOSIXProcessMonitor_powerpc::GetMonitor() {
+ ProcessSP base = CalculateProcess();
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(base.get());
+ return process->GetMonitor();
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadGPR(m_thread.GetID(), &m_gpr_powerpc, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadFPR(m_thread.GetID(), &m_fpr_powerpc,
+ sizeof(m_fpr_powerpc));
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadVMX() {
+ // XXX: Need a way to read/write process VMX registers with ptrace.
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteGPR(m_thread.GetID(), &m_gpr_powerpc, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteFPR(m_thread.GetID(), &m_fpr_powerpc,
+ sizeof(m_fpr_powerpc));
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteVMX() {
+ // XXX: Need a way to read/write process VMX registers with ptrace.
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadRegister(
+ const unsigned reg, RegisterValue &value) {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadRegisterValue(m_thread.GetID(), GetRegisterOffset(reg),
+ GetRegisterName(reg), GetRegisterSize(reg),
+ value);
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteRegister(
+ const unsigned reg, const RegisterValue &value) {
+ unsigned reg_to_write = reg;
+ RegisterValue value_to_write = value;
+
+ // Check if this is a subregister of a full register.
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) {
+ RegisterValue full_value;
+ uint32_t full_reg = reg_info->invalidate_regs[0];
+ const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg);
+
+ // Read the full register.
+ if (ReadRegister(full_reg_info, full_value)) {
+ Status error;
+ ByteOrder byte_order = GetByteOrder();
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the full register.
+ const uint32_t dest_size = full_value.GetAsMemoryData(
+ full_reg_info, dst, sizeof(dst), byte_order, error);
+ if (error.Success() && dest_size) {
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the source data.
+ const uint32_t src_size = value.GetAsMemoryData(
+ reg_info, src, sizeof(src), byte_order, error);
+ if (error.Success() && src_size && (src_size < dest_size)) {
+ // Copy the src bytes to the destination.
+ memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size);
+ // Set this full register as the value to write.
+ value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order);
+ value_to_write.SetType(full_reg_info);
+ reg_to_write = full_reg;
+ }
+ }
+ }
+ }
+
+ ProcessMonitor &monitor = GetMonitor();
+ // Account for the fact that 32-bit targets on powerpc64 really use 64-bit
+ // registers in ptrace, but expose here 32-bit registers with a higher
+ // offset.
+ uint64_t offset = GetRegisterOffset(reg_to_write);
+ offset &= ~(sizeof(uintptr_t) - 1);
+ return monitor.WriteRegisterValue(
+ m_thread.GetID(), offset, GetRegisterName(reg_to_write), value_to_write);
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!reg_info)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsFPR(reg)) {
+ if (!ReadFPR())
+ return false;
+ uint8_t *src = (uint8_t *)&m_fpr_powerpc + reg_info->byte_offset;
+ value.SetUInt64(*(uint64_t *)src);
+ } else if (IsGPR(reg)) {
+ bool success = ReadRegister(reg, value);
+
+ if (success) {
+ // If our return byte size was greater than the return value reg size,
+ // then use the type specified by reg_info rather than the uint64_t
+ // default
+ if (value.GetByteSize() > reg_info->byte_size)
+ value.SetType(reg_info);
+ }
+ return success;
+ }
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsGPR(reg)) {
+ return WriteRegister(reg, value);
+ } else if (IsFPR(reg)) {
+ assert(reg_info->byte_offset < sizeof(m_fpr_powerpc));
+ uint8_t *dst = (uint8_t *)&m_fpr_powerpc + reg_info->byte_offset;
+ *(uint64_t *)dst = value.GetAsUInt64();
+ return WriteFPR();
+ }
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadAllRegisterValues(
+ DataBufferSP &data_sp) {
+ bool success = false;
+ data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ if (ReadGPR() && ReadFPR()) {
+ uint8_t *dst = data_sp->GetBytes();
+ success = dst != 0;
+
+ if (success) {
+ ::memcpy(dst, &m_gpr_powerpc, GetGPRSize());
+ dst += GetGPRSize();
+ }
+ }
+ return success;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteAllRegisterValues(
+ const DataBufferSP &data_sp) {
+ bool success = false;
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ uint8_t *src = data_sp->GetBytes();
+ if (src) {
+ ::memcpy(&m_gpr_powerpc, src, GetGPRSize());
+
+ if (WriteGPR()) {
+ src += GetGPRSize();
+ ::memcpy(&m_fpr_powerpc, src, sizeof(m_fpr_powerpc));
+
+ success = WriteFPR();
+ }
+ }
+ }
+ return success;
+}
+
+uint32_t RegisterContextPOSIXProcessMonitor_powerpc::SetHardwareWatchpoint(
+ addr_t addr, size_t size, bool read, bool write) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ uint32_t hw_index;
+
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) {
+ if (IsWatchpointVacant(hw_index))
+ return SetHardwareWatchpointWithIndex(addr, size, read, write, hw_index);
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ClearHardwareWatchpoint(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::HardwareSingleStep(
+ bool enable) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::UpdateAfterBreakpoint() {
+ lldb::addr_t pc;
+
+ if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
+ return false;
+
+ return true;
+}
+
+unsigned RegisterContextPOSIXProcessMonitor_powerpc::GetRegisterIndexFromOffset(
+ unsigned offset) {
+ unsigned reg;
+ for (reg = 0; reg < k_num_registers_powerpc; reg++) {
+ if (GetRegisterInfo()[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < k_num_registers_powerpc && "Invalid register offset.");
+ return reg;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::IsWatchpointHit(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ClearWatchpointHits() {
+ return false;
+}
+
+addr_t RegisterContextPOSIXProcessMonitor_powerpc::GetWatchpointAddress(
+ uint32_t hw_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::IsWatchpointVacant(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::SetHardwareWatchpointWithIndex(
+ addr_t addr, size_t size, bool read, bool write, uint32_t hw_index) {
+ return false;
+}
+
+uint32_t
+RegisterContextPOSIXProcessMonitor_powerpc::NumSupportedHardwareWatchpoints() {
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.h
new file mode 100644
index 000000000000..17b649feebf1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.h
@@ -0,0 +1,84 @@
+//===-- RegisterContextPOSIXProcessMonitor_powerpc.h -------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIXProcessMonitor_powerpc_H_
+#define liblldb_RegisterContextPOSIXProcessMonitor_powerpc_H_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h"
+#include "RegisterContextPOSIX.h"
+
+class RegisterContextPOSIXProcessMonitor_powerpc
+ : public RegisterContextPOSIX_powerpc,
+ public POSIXBreakpointProtocol {
+public:
+ RegisterContextPOSIXProcessMonitor_powerpc(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+protected:
+ bool IsVMX();
+
+ bool ReadGPR();
+
+ bool ReadFPR();
+
+ bool ReadVMX();
+
+ bool WriteGPR();
+
+ bool WriteFPR();
+
+ bool WriteVMX();
+
+ // lldb_private::RegisterContext
+ bool ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const unsigned reg,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write);
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index);
+
+ bool HardwareSingleStep(bool enable);
+
+ // POSIXBreakpointProtocol
+ bool UpdateAfterBreakpoint();
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ bool IsWatchpointHit(uint32_t hw_index);
+
+ bool ClearWatchpointHits();
+
+ lldb::addr_t GetWatchpointAddress(uint32_t hw_index);
+
+ bool IsWatchpointVacant(uint32_t hw_index);
+
+ bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read,
+ bool write, uint32_t hw_index);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+private:
+ ProcessMonitor &GetMonitor();
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.cpp
new file mode 100644
index 000000000000..3046d97f153c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.cpp
@@ -0,0 +1,613 @@
+//===-- RegisterContextPOSIXProcessMonitor_x86.cpp --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "Plugins/Process/FreeBSD/ProcessFreeBSD.h"
+#include "Plugins/Process/FreeBSD/ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_x86.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// Support ptrace extensions even when compiled without required kernel support
+#ifndef NT_X86_XSTATE
+#define NT_X86_XSTATE 0x202
+#endif
+
+#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof(FPR))
+
+static uint32_t size_and_rw_bits(size_t size, bool read, bool write) {
+ uint32_t rw;
+
+ if (read)
+ rw = 0x3; // READ or READ/WRITE
+ else if (write)
+ rw = 0x1; // WRITE
+ else
+ assert(0 && "read and write cannot both be false");
+
+ switch (size) {
+ case 1:
+ return rw;
+ case 2:
+ return (0x1 << 2) | rw;
+ case 4:
+ return (0x3 << 2) | rw;
+ case 8:
+ return (0x2 << 2) | rw;
+ default:
+ assert(0 && "invalid size, must be one of 1, 2, 4, or 8");
+ return 0; // Unreachable. Just to silence compiler.
+ }
+}
+
+RegisterContextPOSIXProcessMonitor_x86_64::
+ RegisterContextPOSIXProcessMonitor_x86_64(
+ Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : RegisterContextPOSIX_x86(thread, concrete_frame_idx, register_info) {
+ // Store byte offset of fctrl (i.e. first register of FPR) wrt 'UserArea'
+ const RegisterInfo *reg_info_fctrl = GetRegisterInfoByName("fctrl");
+ m_fctrl_offset_in_userarea = reg_info_fctrl->byte_offset;
+
+ m_iovec.iov_base = &m_fpr.xsave;
+ m_iovec.iov_len = sizeof(m_fpr.xsave);
+}
+
+ProcessMonitor &RegisterContextPOSIXProcessMonitor_x86_64::GetMonitor() {
+ ProcessSP base = CalculateProcess();
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(base.get());
+ return process->GetMonitor();
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ReadGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadGPR(m_thread.GetID(), &m_gpr_x86_64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ReadFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ if (GetFPRType() == eFXSAVE)
+ return monitor.ReadFPR(m_thread.GetID(), &m_fpr.fxsave,
+ sizeof(m_fpr.fxsave));
+
+ if (GetFPRType() == eXSAVE)
+ return monitor.ReadRegisterSet(m_thread.GetID(), &m_iovec,
+ sizeof(m_fpr.xsave), NT_X86_XSTATE);
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::WriteGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteGPR(m_thread.GetID(), &m_gpr_x86_64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::WriteFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ if (GetFPRType() == eFXSAVE)
+ return monitor.WriteFPR(m_thread.GetID(), &m_fpr.fxsave,
+ sizeof(m_fpr.fxsave));
+
+ if (GetFPRType() == eXSAVE)
+ return monitor.WriteRegisterSet(m_thread.GetID(), &m_iovec,
+ sizeof(m_fpr.xsave), NT_X86_XSTATE);
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ReadRegister(
+ const unsigned reg, RegisterValue &value) {
+ ProcessMonitor &monitor = GetMonitor();
+
+#if defined(__FreeBSD__)
+ if (reg >= m_reg_info.first_dr)
+ return monitor.ReadDebugRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg), GetRegisterName(reg),
+ GetRegisterSize(reg), value);
+#endif
+ return monitor.ReadRegisterValue(m_thread.GetID(), GetRegisterOffset(reg),
+ GetRegisterName(reg), GetRegisterSize(reg),
+ value);
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::WriteRegister(
+ const unsigned reg, const RegisterValue &value) {
+ unsigned reg_to_write = reg;
+ RegisterValue value_to_write = value;
+
+ // Check if this is a subregister of a full register.
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) {
+ RegisterValue full_value;
+ uint32_t full_reg = reg_info->invalidate_regs[0];
+ const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg);
+
+ // Read the full register.
+ if (ReadRegister(full_reg_info, full_value)) {
+ Status error;
+ ByteOrder byte_order = GetByteOrder();
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the full register.
+ const uint32_t dest_size = full_value.GetAsMemoryData(
+ full_reg_info, dst, sizeof(dst), byte_order, error);
+ if (error.Success() && dest_size) {
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the source data.
+ const uint32_t src_size = value.GetAsMemoryData(
+ reg_info, src, sizeof(src), byte_order, error);
+ if (error.Success() && src_size && (src_size < dest_size)) {
+ // Copy the src bytes to the destination.
+ memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size);
+ // Set this full register as the value to write.
+ value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order);
+ value_to_write.SetType(full_reg_info);
+ reg_to_write = full_reg;
+ }
+ }
+ }
+ }
+
+ ProcessMonitor &monitor = GetMonitor();
+#if defined(__FreeBSD__)
+ if (reg >= m_reg_info.first_dr)
+ return monitor.WriteDebugRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg_to_write),
+ GetRegisterName(reg_to_write), value_to_write);
+#endif
+ return monitor.WriteRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg_to_write),
+ GetRegisterName(reg_to_write), value_to_write);
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!reg_info)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsFPR(reg, GetFPRType())) {
+ if (!ReadFPR())
+ return false;
+ } else {
+ uint32_t full_reg = reg;
+ bool is_subreg = reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM);
+
+ if (is_subreg) {
+ // Read the full aligned 64-bit register.
+ full_reg = reg_info->invalidate_regs[0];
+ }
+
+ bool success = ReadRegister(full_reg, value);
+
+ if (success) {
+ // If our read was not aligned (for ah,bh,ch,dh), shift our returned
+ // value one byte to the right.
+ if (is_subreg && (reg_info->byte_offset & 0x1))
+ value.SetUInt64(value.GetAsUInt64() >> 8);
+
+ // If our return byte size was greater than the return value reg size,
+ // then use the type specified by reg_info rather than the uint64_t
+ // default
+ if (value.GetByteSize() > reg_info->byte_size)
+ value.SetType(reg_info);
+ }
+ return success;
+ }
+
+ if (reg_info->encoding == eEncodingVector) {
+ ByteOrder byte_order = GetByteOrder();
+
+ if (byte_order != ByteOrder::eByteOrderInvalid) {
+ if (reg >= m_reg_info.first_st && reg <= m_reg_info.last_st)
+ value.SetBytes(m_fpr.fxsave.stmm[reg - m_reg_info.first_st].bytes,
+ reg_info->byte_size, byte_order);
+ if (reg >= m_reg_info.first_mm && reg <= m_reg_info.last_mm)
+ value.SetBytes(m_fpr.fxsave.stmm[reg - m_reg_info.first_mm].bytes,
+ reg_info->byte_size, byte_order);
+ if (reg >= m_reg_info.first_xmm && reg <= m_reg_info.last_xmm)
+ value.SetBytes(m_fpr.fxsave.xmm[reg - m_reg_info.first_xmm].bytes,
+ reg_info->byte_size, byte_order);
+ if (reg >= m_reg_info.first_ymm && reg <= m_reg_info.last_ymm) {
+ // Concatenate ymm using the register halves in xmm.bytes and
+ // ymmh.bytes
+ if (GetFPRType() == eXSAVE && CopyXSTATEtoYMM(reg, byte_order))
+ value.SetBytes(m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes,
+ reg_info->byte_size, byte_order);
+ else
+ return false;
+ }
+ return value.GetType() == RegisterValue::eTypeBytes;
+ }
+ return false;
+ }
+
+ // Get pointer to m_fpr.fxsave variable and set the data from it. Byte
+ // offsets of all registers are calculated wrt 'UserArea' structure. However,
+ // ReadFPR() reads fpu registers {using ptrace(PT_GETFPREGS,..)} and stores
+ // them in 'm_fpr' (of type FPR structure). To extract values of fpu
+ // registers, m_fpr should be read at byte offsets calculated wrt to FPR
+ // structure.
+
+ // Since, FPR structure is also one of the member of UserArea structure.
+ // byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) -
+ // byte_offset(fctrl wrt UserArea)
+ assert((reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(m_fpr));
+ uint8_t *src =
+ (uint8_t *)&m_fpr + reg_info->byte_offset - m_fctrl_offset_in_userarea;
+ switch (reg_info->byte_size) {
+ case 1:
+ value.SetUInt8(*(uint8_t *)src);
+ return true;
+ case 2:
+ value.SetUInt16(*(uint16_t *)src);
+ return true;
+ case 4:
+ value.SetUInt32(*(uint32_t *)src);
+ return true;
+ case 8:
+ value.SetUInt64(*(uint64_t *)src);
+ return true;
+ default:
+ assert(false && "Unhandled data size.");
+ return false;
+ }
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsGPR(reg))
+ return WriteRegister(reg, value);
+
+ if (IsFPR(reg, GetFPRType())) {
+ if (reg_info->encoding == eEncodingVector) {
+ if (reg >= m_reg_info.first_st && reg <= m_reg_info.last_st)
+ ::memcpy(m_fpr.fxsave.stmm[reg - m_reg_info.first_st].bytes,
+ value.GetBytes(), value.GetByteSize());
+
+ if (reg >= m_reg_info.first_mm && reg <= m_reg_info.last_mm)
+ ::memcpy(m_fpr.fxsave.stmm[reg - m_reg_info.first_mm].bytes,
+ value.GetBytes(), value.GetByteSize());
+
+ if (reg >= m_reg_info.first_xmm && reg <= m_reg_info.last_xmm)
+ ::memcpy(m_fpr.fxsave.xmm[reg - m_reg_info.first_xmm].bytes,
+ value.GetBytes(), value.GetByteSize());
+
+ if (reg >= m_reg_info.first_ymm && reg <= m_reg_info.last_ymm) {
+ if (GetFPRType() != eXSAVE)
+ return false; // the target processor does not support AVX
+
+ // Store ymm register content, and split into the register halves in
+ // xmm.bytes and ymmh.bytes
+ ::memcpy(m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes,
+ value.GetBytes(), value.GetByteSize());
+ if (false == CopyYMMtoXSTATE(reg, GetByteOrder()))
+ return false;
+ }
+ } else {
+ // Get pointer to m_fpr.fxsave variable and set the data to it. Byte
+ // offsets of all registers are calculated wrt 'UserArea' structure.
+ // However, WriteFPR() takes m_fpr (of type FPR structure) and writes
+ // only fpu registers using ptrace(PT_SETFPREGS,..) API. Hence fpu
+ // registers should be written in m_fpr at byte offsets calculated wrt
+ // FPR structure.
+
+ // Since, FPR structure is also one of the member of UserArea structure.
+ // byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) -
+ // byte_offset(fctrl wrt UserArea)
+ assert((reg_info->byte_offset - m_fctrl_offset_in_userarea) <
+ sizeof(m_fpr));
+ uint8_t *dst = (uint8_t *)&m_fpr + reg_info->byte_offset -
+ m_fctrl_offset_in_userarea;
+ switch (reg_info->byte_size) {
+ case 1:
+ *(uint8_t *)dst = value.GetAsUInt8();
+ break;
+ case 2:
+ *(uint16_t *)dst = value.GetAsUInt16();
+ break;
+ case 4:
+ *(uint32_t *)dst = value.GetAsUInt32();
+ break;
+ case 8:
+ *(uint64_t *)dst = value.GetAsUInt64();
+ break;
+ default:
+ assert(false && "Unhandled data size.");
+ return false;
+ }
+ }
+
+ if (WriteFPR()) {
+ if (IsAVX(reg))
+ return CopyYMMtoXSTATE(reg, GetByteOrder());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ReadAllRegisterValues(
+ DataBufferSP &data_sp) {
+ bool success = false;
+ data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ if (ReadGPR() && ReadFPR()) {
+ uint8_t *dst = data_sp->GetBytes();
+ success = dst != 0;
+
+ if (success) {
+ ::memcpy(dst, &m_gpr_x86_64, GetGPRSize());
+ dst += GetGPRSize();
+ if (GetFPRType() == eFXSAVE)
+ ::memcpy(dst, &m_fpr.fxsave, sizeof(m_fpr.fxsave));
+ }
+
+ if (GetFPRType() == eXSAVE) {
+ ByteOrder byte_order = GetByteOrder();
+
+ // Assemble the YMM register content from the register halves.
+ for (uint32_t reg = m_reg_info.first_ymm;
+ success && reg <= m_reg_info.last_ymm; ++reg)
+ success = CopyXSTATEtoYMM(reg, byte_order);
+
+ if (success) {
+ // Copy the extended register state including the assembled ymm
+ // registers.
+ ::memcpy(dst, &m_fpr, sizeof(m_fpr));
+ }
+ }
+ }
+ return success;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::WriteAllRegisterValues(
+ const DataBufferSP &data_sp) {
+ bool success = false;
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ uint8_t *src = data_sp->GetBytes();
+ if (src) {
+ ::memcpy(&m_gpr_x86_64, src, GetGPRSize());
+
+ if (WriteGPR()) {
+ src += GetGPRSize();
+ if (GetFPRType() == eFXSAVE)
+ ::memcpy(&m_fpr.fxsave, src, sizeof(m_fpr.fxsave));
+ if (GetFPRType() == eXSAVE)
+ ::memcpy(&m_fpr.xsave, src, sizeof(m_fpr.xsave));
+
+ success = WriteFPR();
+ if (success) {
+ if (GetFPRType() == eXSAVE) {
+ ByteOrder byte_order = GetByteOrder();
+
+ // Parse the YMM register content from the register halves.
+ for (uint32_t reg = m_reg_info.first_ymm;
+ success && reg <= m_reg_info.last_ymm; ++reg)
+ success = CopyYMMtoXSTATE(reg, byte_order);
+ }
+ }
+ }
+ }
+ }
+ return success;
+}
+
+uint32_t RegisterContextPOSIXProcessMonitor_x86_64::SetHardwareWatchpoint(
+ addr_t addr, size_t size, bool read, bool write) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ uint32_t hw_index;
+
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) {
+ if (IsWatchpointVacant(hw_index))
+ return SetHardwareWatchpointWithIndex(addr, size, read, write, hw_index);
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ClearHardwareWatchpoint(
+ uint32_t hw_index) {
+ if (hw_index < NumSupportedHardwareWatchpoints()) {
+ RegisterValue current_dr7_bits;
+
+ if (ReadRegister(m_reg_info.first_dr + 7, current_dr7_bits)) {
+ uint64_t new_dr7_bits =
+ current_dr7_bits.GetAsUInt64() & ~(3 << (2 * hw_index));
+
+ if (WriteRegister(m_reg_info.first_dr + 7, RegisterValue(new_dr7_bits)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::HardwareSingleStep(
+ bool enable) {
+ enum { TRACE_BIT = 0x100 };
+ uint64_t rflags;
+
+ if ((rflags = ReadRegisterAsUnsigned(m_reg_info.gpr_flags, -1UL)) == -1UL)
+ return false;
+
+ if (enable) {
+ if (rflags & TRACE_BIT)
+ return true;
+
+ rflags |= TRACE_BIT;
+ } else {
+ if (!(rflags & TRACE_BIT))
+ return false;
+
+ rflags &= ~TRACE_BIT;
+ }
+
+ return WriteRegisterFromUnsigned(m_reg_info.gpr_flags, rflags);
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::UpdateAfterBreakpoint() {
+ // PC points one byte past the int3 responsible for the breakpoint.
+ lldb::addr_t pc;
+
+ if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
+ return false;
+
+ SetPC(pc - 1);
+ return true;
+}
+
+unsigned RegisterContextPOSIXProcessMonitor_x86_64::GetRegisterIndexFromOffset(
+ unsigned offset) {
+ unsigned reg;
+ for (reg = 0; reg < m_reg_info.num_registers; reg++) {
+ if (GetRegisterInfo()[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < m_reg_info.num_registers && "Invalid register offset.");
+ return reg;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::IsWatchpointHit(
+ uint32_t hw_index) {
+ bool is_hit = false;
+
+ if (m_watchpoints_initialized == false) {
+ // Reset the debug status and debug control registers
+ RegisterValue zero_bits = RegisterValue(uint64_t(0));
+ if (!WriteRegister(m_reg_info.first_dr + 6, zero_bits) ||
+ !WriteRegister(m_reg_info.first_dr + 7, zero_bits))
+ assert(false && "Could not initialize watchpoint registers");
+ m_watchpoints_initialized = true;
+ }
+
+ if (hw_index < NumSupportedHardwareWatchpoints()) {
+ RegisterValue value;
+
+ if (ReadRegister(m_reg_info.first_dr + 6, value)) {
+ uint64_t val = value.GetAsUInt64();
+ is_hit = val & (1 << hw_index);
+ }
+ }
+
+ return is_hit;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ClearWatchpointHits() {
+ return WriteRegister(m_reg_info.first_dr + 6, RegisterValue((uint64_t)0));
+}
+
+addr_t RegisterContextPOSIXProcessMonitor_x86_64::GetWatchpointAddress(
+ uint32_t hw_index) {
+ addr_t wp_monitor_addr = LLDB_INVALID_ADDRESS;
+
+ if (hw_index < NumSupportedHardwareWatchpoints()) {
+ if (!IsWatchpointVacant(hw_index)) {
+ RegisterValue value;
+
+ if (ReadRegister(m_reg_info.first_dr + hw_index, value))
+ wp_monitor_addr = value.GetAsUInt64();
+ }
+ }
+
+ return wp_monitor_addr;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::IsWatchpointVacant(
+ uint32_t hw_index) {
+ bool is_vacant = false;
+ RegisterValue value;
+
+ assert(hw_index < NumSupportedHardwareWatchpoints());
+
+ if (m_watchpoints_initialized == false) {
+ // Reset the debug status and debug control registers
+ RegisterValue zero_bits = RegisterValue(uint64_t(0));
+ if (!WriteRegister(m_reg_info.first_dr + 6, zero_bits) ||
+ !WriteRegister(m_reg_info.first_dr + 7, zero_bits))
+ assert(false && "Could not initialize watchpoint registers");
+ m_watchpoints_initialized = true;
+ }
+
+ if (ReadRegister(m_reg_info.first_dr + 7, value)) {
+ uint64_t val = value.GetAsUInt64();
+ is_vacant = (val & (3 << 2 * hw_index)) == 0;
+ }
+
+ return is_vacant;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::SetHardwareWatchpointWithIndex(
+ addr_t addr, size_t size, bool read, bool write, uint32_t hw_index) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ if (num_hw_watchpoints == 0 || hw_index >= num_hw_watchpoints)
+ return false;
+
+ if (!(size == 1 || size == 2 || size == 4 || size == 8))
+ return false;
+
+ if (read == false && write == false)
+ return false;
+
+ if (!IsWatchpointVacant(hw_index))
+ return false;
+
+ // Set both dr7 (debug control register) and dri (debug address register).
+
+ // dr7{7-0} encodes the local/global enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ //
+ // dr7{31-16} encodes the rw/len bits:
+ // b_x+3, b_x+2, b_x+1, b_x
+ // where bits{x+1, x} => rw
+ // 0b00: execute, 0b01: write, 0b11: read-or-write,
+ // 0b10: io read-or-write (unused)
+ // and bits{x+3, x+2} => len
+ // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte
+ //
+ // dr0 -> bits{19-16}
+ // dr1 -> bits{23-20}
+ // dr2 -> bits{27-24}
+ // dr3 -> bits{31-28}
+ if (hw_index < num_hw_watchpoints) {
+ RegisterValue current_dr7_bits;
+
+ if (ReadRegister(m_reg_info.first_dr + 7, current_dr7_bits)) {
+ uint64_t new_dr7_bits =
+ current_dr7_bits.GetAsUInt64() |
+ (1 << (2 * hw_index) |
+ size_and_rw_bits(size, read, write) << (16 + 4 * hw_index));
+
+ if (WriteRegister(m_reg_info.first_dr + hw_index, RegisterValue(addr)) &&
+ WriteRegister(m_reg_info.first_dr + 7, RegisterValue(new_dr7_bits)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+uint32_t
+RegisterContextPOSIXProcessMonitor_x86_64::NumSupportedHardwareWatchpoints() {
+ // Available debug address registers: dr0, dr1, dr2, dr3
+ return 4;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.h
new file mode 100644
index 000000000000..c9dc02a88e01
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.h
@@ -0,0 +1,81 @@
+//===-- RegisterContextPOSIXProcessMonitor_x86.h ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIXProcessMonitor_x86_H_
+#define liblldb_RegisterContextPOSIXProcessMonitor_x86_H_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_x86.h"
+#include "RegisterContextPOSIX.h"
+#include <sys/uio.h>
+
+class RegisterContextPOSIXProcessMonitor_x86_64
+ : public RegisterContextPOSIX_x86,
+ public POSIXBreakpointProtocol {
+public:
+ RegisterContextPOSIXProcessMonitor_x86_64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+protected:
+ bool ReadGPR();
+
+ bool ReadFPR();
+
+ bool WriteGPR();
+
+ bool WriteFPR();
+
+ // lldb_private::RegisterContext
+ bool ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const unsigned reg,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write);
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index);
+
+ bool HardwareSingleStep(bool enable);
+
+ // POSIXBreakpointProtocol
+ bool UpdateAfterBreakpoint();
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ bool IsWatchpointHit(uint32_t hw_index);
+
+ bool ClearWatchpointHits();
+
+ lldb::addr_t GetWatchpointAddress(uint32_t hw_index);
+
+ bool IsWatchpointVacant(uint32_t hw_index);
+
+ bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read,
+ bool write, uint32_t hw_index);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+private:
+ ProcessMonitor &GetMonitor();
+ uint32_t
+ m_fctrl_offset_in_userarea; // Offset of 'fctrl' in 'UserArea' Structure
+ struct iovec m_iovec;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
new file mode 100644
index 000000000000..32d20d2b1215
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
@@ -0,0 +1,798 @@
+//===-- NativeProcessNetBSD.cpp ------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeProcessNetBSD.h"
+
+
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/Host/posix/ProcessLauncherPosixFork.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/State.h"
+#include "llvm/Support/Errno.h"
+
+// System includes - They have to be included after framework includes because
+// they define some macros which collide with variable names in other modules
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+#include <uvm/uvm_prot.h>
+#include <elf.h>
+#include <util.h>
+// clang-format on
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_netbsd;
+using namespace llvm;
+
+// Simple helper function to ensure flags are enabled on the given file
+// descriptor.
+static Status EnsureFDFlags(int fd, int flags) {
+ Status error;
+
+ int status = fcntl(fd, F_GETFL);
+ if (status == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ if (fcntl(fd, F_SETFL, status | flags) == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ return error;
+}
+
+// Public Static Methods
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+NativeProcessNetBSD::Factory::Launch(ProcessLaunchInfo &launch_info,
+ NativeDelegate &native_delegate,
+ MainLoop &mainloop) const {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ Status status;
+ ::pid_t pid = ProcessLauncherPosixFork()
+ .LaunchProcess(launch_info, status)
+ .GetProcessId();
+ LLDB_LOG(log, "pid = {0:x}", pid);
+ if (status.Fail()) {
+ LLDB_LOG(log, "failed to launch process: {0}", status);
+ return status.ToError();
+ }
+
+ // Wait for the child process to trap on its call to execve.
+ int wstatus;
+ ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0);
+ assert(wpid == pid);
+ (void)wpid;
+ if (!WIFSTOPPED(wstatus)) {
+ LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}",
+ WaitStatus::Decode(wstatus));
+ return llvm::make_error<StringError>("Could not sync with inferior process",
+ llvm::inconvertibleErrorCode());
+ }
+ LLDB_LOG(log, "inferior started, now in stopped state");
+
+ ProcessInstanceInfo Info;
+ if (!Host::GetProcessInfo(pid, Info)) {
+ return llvm::make_error<StringError>("Cannot get process architecture",
+ llvm::inconvertibleErrorCode());
+ }
+
+ // Set the architecture to the exe architecture.
+ LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid,
+ Info.GetArchitecture().GetArchitectureName());
+
+ std::unique_ptr<NativeProcessNetBSD> process_up(new NativeProcessNetBSD(
+ pid, launch_info.GetPTY().ReleaseMasterFileDescriptor(), native_delegate,
+ Info.GetArchitecture(), mainloop));
+
+ status = process_up->ReinitializeThreads();
+ if (status.Fail())
+ return status.ToError();
+
+ for (const auto &thread : process_up->m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(SIGSTOP);
+ process_up->SetState(StateType::eStateStopped, false);
+
+ return std::move(process_up);
+}
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+NativeProcessNetBSD::Factory::Attach(
+ lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate,
+ MainLoop &mainloop) const {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOG(log, "pid = {0:x}", pid);
+
+ // Retrieve the architecture for the running process.
+ ProcessInstanceInfo Info;
+ if (!Host::GetProcessInfo(pid, Info)) {
+ return llvm::make_error<StringError>("Cannot get process architecture",
+ llvm::inconvertibleErrorCode());
+ }
+
+ std::unique_ptr<NativeProcessNetBSD> process_up(new NativeProcessNetBSD(
+ pid, -1, native_delegate, Info.GetArchitecture(), mainloop));
+
+ Status status = process_up->Attach();
+ if (!status.Success())
+ return status.ToError();
+
+ return std::move(process_up);
+}
+
+// Public Instance Methods
+
+NativeProcessNetBSD::NativeProcessNetBSD(::pid_t pid, int terminal_fd,
+ NativeDelegate &delegate,
+ const ArchSpec &arch,
+ MainLoop &mainloop)
+ : NativeProcessProtocol(pid, terminal_fd, delegate), m_arch(arch) {
+ if (m_terminal_fd != -1) {
+ Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
+ assert(status.Success());
+ }
+
+ Status status;
+ m_sigchld_handle = mainloop.RegisterSignal(
+ SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, status);
+ assert(m_sigchld_handle && status.Success());
+}
+
+// Handles all waitpid events from the inferior process.
+void NativeProcessNetBSD::MonitorCallback(lldb::pid_t pid, int signal) {
+ switch (signal) {
+ case SIGTRAP:
+ return MonitorSIGTRAP(pid);
+ case SIGSTOP:
+ return MonitorSIGSTOP(pid);
+ default:
+ return MonitorSignal(pid, signal);
+ }
+}
+
+void NativeProcessNetBSD::MonitorExited(lldb::pid_t pid, WaitStatus status) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ LLDB_LOG(log, "got exit signal({0}) , pid = {1}", status, pid);
+
+ /* Stop Tracking All Threads attached to Process */
+ m_threads.clear();
+
+ SetExitStatus(status, true);
+
+ // Notify delegate that our process has exited.
+ SetState(StateType::eStateExited, true);
+}
+
+void NativeProcessNetBSD::MonitorSIGSTOP(lldb::pid_t pid) {
+ ptrace_siginfo_t info;
+
+ const auto siginfo_err =
+ PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info));
+
+ // Get details on the signal raised.
+ if (siginfo_err.Success()) {
+ // Handle SIGSTOP from LLGS (LLDB GDB Server)
+ if (info.psi_siginfo.si_code == SI_USER &&
+ info.psi_siginfo.si_pid == ::getpid()) {
+ /* Stop Tracking all Threads attached to Process */
+ for (const auto &thread : m_threads) {
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(
+ SIGSTOP, &info.psi_siginfo);
+ }
+ }
+ }
+}
+
+void NativeProcessNetBSD::MonitorSIGTRAP(lldb::pid_t pid) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ ptrace_siginfo_t info;
+
+ const auto siginfo_err =
+ PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info));
+
+ // Get details on the signal raised.
+ if (siginfo_err.Fail()) {
+ return;
+ }
+
+ switch (info.psi_siginfo.si_code) {
+ case TRAP_BRKPT:
+ for (const auto &thread : m_threads) {
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedByBreakpoint();
+ FixupBreakpointPCAsNeeded(static_cast<NativeThreadNetBSD &>(*thread));
+ }
+ SetState(StateType::eStateStopped, true);
+ break;
+ case TRAP_TRACE:
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedByTrace();
+ SetState(StateType::eStateStopped, true);
+ break;
+ case TRAP_EXEC: {
+ Status error = ReinitializeThreads();
+ if (error.Fail()) {
+ SetState(StateType::eStateInvalid);
+ return;
+ }
+
+ // Let our delegate know we have just exec'd.
+ NotifyDidExec();
+
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedByExec();
+ SetState(StateType::eStateStopped, true);
+ } break;
+ case TRAP_DBREG: {
+ // Find the thread.
+ NativeThreadNetBSD* thread = nullptr;
+ for (const auto &t : m_threads) {
+ if (t->GetID() == info.psi_lwpid) {
+ thread = static_cast<NativeThreadNetBSD *>(t.get());
+ break;
+ }
+ }
+ if (!thread) {
+ LLDB_LOG(log,
+ "thread not found in m_threads, pid = {0}, LWP = {1}",
+ GetID(), info.psi_lwpid);
+ break;
+ }
+
+ // If a watchpoint was hit, report it
+ uint32_t wp_index = LLDB_INVALID_INDEX32;
+ Status error = thread->GetRegisterContext().GetWatchpointHitIndex(
+ wp_index, (uintptr_t)info.psi_siginfo.si_addr);
+ if (error.Fail())
+ LLDB_LOG(log,
+ "received error while checking for watchpoint hits, pid = "
+ "{0}, LWP = {1}, error = {2}",
+ GetID(), info.psi_lwpid, error);
+ if (wp_index != LLDB_INVALID_INDEX32) {
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedByWatchpoint(
+ wp_index);
+ SetState(StateType::eStateStopped, true);
+ break;
+ }
+
+ // If a breakpoint was hit, report it
+ uint32_t bp_index = LLDB_INVALID_INDEX32;
+ error = thread->GetRegisterContext().GetHardwareBreakHitIndex(
+ bp_index, (uintptr_t)info.psi_siginfo.si_addr);
+ if (error.Fail())
+ LLDB_LOG(log,
+ "received error while checking for hardware "
+ "breakpoint hits, pid = {0}, LWP = {1}, error = {2}",
+ GetID(), info.psi_lwpid, error);
+ if (bp_index != LLDB_INVALID_INDEX32) {
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedByBreakpoint();
+ SetState(StateType::eStateStopped, true);
+ break;
+ }
+ } break;
+ }
+}
+
+void NativeProcessNetBSD::MonitorSignal(lldb::pid_t pid, int signal) {
+ ptrace_siginfo_t info;
+ const auto siginfo_err =
+ PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info));
+
+ for (const auto &thread : m_threads) {
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(
+ info.psi_siginfo.si_signo, &info.psi_siginfo);
+ }
+ SetState(StateType::eStateStopped, true);
+}
+
+Status NativeProcessNetBSD::PtraceWrapper(int req, lldb::pid_t pid, void *addr,
+ int data, int *result) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ Status error;
+ int ret;
+
+ errno = 0;
+ ret = ptrace(req, static_cast<::pid_t>(pid), addr, data);
+
+ if (ret == -1)
+ error.SetErrorToErrno();
+
+ if (result)
+ *result = ret;
+
+ LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3})={4:x}", req, pid, addr, data, ret);
+
+ if (error.Fail())
+ LLDB_LOG(log, "ptrace() failed: {0}", error);
+
+ return error;
+}
+
+Status NativeProcessNetBSD::Resume(const ResumeActionList &resume_actions) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOG(log, "pid {0}", GetID());
+
+ const auto &thread = m_threads[0];
+ const ResumeAction *const action =
+ resume_actions.GetActionForThread(thread->GetID(), true);
+
+ if (action == nullptr) {
+ LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(),
+ thread->GetID());
+ return Status();
+ }
+
+ Status error;
+
+ switch (action->state) {
+ case eStateRunning: {
+ // Run the thread, possibly feeding it the signal.
+ error = NativeProcessNetBSD::PtraceWrapper(PT_CONTINUE, GetID(), (void *)1,
+ action->signal);
+ if (!error.Success())
+ return error;
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetRunning();
+ SetState(eStateRunning, true);
+ break;
+ }
+ case eStateStepping:
+ // Run the thread, possibly feeding it the signal.
+ error = NativeProcessNetBSD::PtraceWrapper(PT_STEP, GetID(), (void *)1,
+ action->signal);
+ if (!error.Success())
+ return error;
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStepping();
+ SetState(eStateStepping, true);
+ break;
+
+ case eStateSuspended:
+ case eStateStopped:
+ llvm_unreachable("Unexpected state");
+
+ default:
+ return Status("NativeProcessNetBSD::%s (): unexpected state %s specified "
+ "for pid %" PRIu64 ", tid %" PRIu64,
+ __FUNCTION__, StateAsCString(action->state), GetID(),
+ thread->GetID());
+ }
+
+ return Status();
+}
+
+Status NativeProcessNetBSD::Halt() {
+ Status error;
+
+ if (kill(GetID(), SIGSTOP) != 0)
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status NativeProcessNetBSD::Detach() {
+ Status error;
+
+ // Stop monitoring the inferior.
+ m_sigchld_handle.reset();
+
+ // Tell ptrace to detach from the process.
+ if (GetID() == LLDB_INVALID_PROCESS_ID)
+ return error;
+
+ return PtraceWrapper(PT_DETACH, GetID());
+}
+
+Status NativeProcessNetBSD::Signal(int signo) {
+ Status error;
+
+ if (kill(GetID(), signo))
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status NativeProcessNetBSD::Kill() {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOG(log, "pid {0}", GetID());
+
+ Status error;
+
+ switch (m_state) {
+ case StateType::eStateInvalid:
+ case StateType::eStateExited:
+ case StateType::eStateCrashed:
+ case StateType::eStateDetached:
+ case StateType::eStateUnloaded:
+ // Nothing to do - the process is already dead.
+ LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(),
+ StateAsCString(m_state));
+ return error;
+
+ case StateType::eStateConnected:
+ case StateType::eStateAttaching:
+ case StateType::eStateLaunching:
+ case StateType::eStateStopped:
+ case StateType::eStateRunning:
+ case StateType::eStateStepping:
+ case StateType::eStateSuspended:
+ // We can try to kill a process in these states.
+ break;
+ }
+
+ if (kill(GetID(), SIGKILL) != 0) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ return error;
+}
+
+Status NativeProcessNetBSD::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) {
+
+ if (m_supports_mem_region == LazyBool::eLazyBoolNo) {
+ // We're done.
+ return Status("unsupported");
+ }
+
+ Status error = PopulateMemoryRegionCache();
+ if (error.Fail()) {
+ return error;
+ }
+
+ lldb::addr_t prev_base_address = 0;
+ // FIXME start by finding the last region that is <= target address using
+ // binary search. Data is sorted.
+ // There can be a ton of regions on pthreads apps with lots of threads.
+ for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end();
+ ++it) {
+ MemoryRegionInfo &proc_entry_info = it->first;
+ // Sanity check assumption that memory map entries are ascending.
+ assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) &&
+ "descending memory map entries detected, unexpected");
+ prev_base_address = proc_entry_info.GetRange().GetRangeBase();
+ UNUSED_IF_ASSERT_DISABLED(prev_base_address);
+ // If the target address comes before this entry, indicate distance to next
+ // region.
+ if (load_addr < proc_entry_info.GetRange().GetRangeBase()) {
+ range_info.GetRange().SetRangeBase(load_addr);
+ range_info.GetRange().SetByteSize(
+ proc_entry_info.GetRange().GetRangeBase() - load_addr);
+ range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo);
+ return error;
+ } else if (proc_entry_info.GetRange().Contains(load_addr)) {
+ // The target address is within the memory region we're processing here.
+ range_info = proc_entry_info;
+ return error;
+ }
+ // The target memory address comes somewhere after the region we just
+ // parsed.
+ }
+ // If we made it here, we didn't find an entry that contained the given
+ // address. Return the load_addr as start and the amount of bytes betwwen
+ // load address and the end of the memory as size.
+ range_info.GetRange().SetRangeBase(load_addr);
+ range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS);
+ range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo);
+ return error;
+}
+
+Status NativeProcessNetBSD::PopulateMemoryRegionCache() {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ // If our cache is empty, pull the latest. There should always be at least
+ // one memory region if memory region handling is supported.
+ if (!m_mem_region_cache.empty()) {
+ LLDB_LOG(log, "reusing {0} cached memory region entries",
+ m_mem_region_cache.size());
+ return Status();
+ }
+
+ struct kinfo_vmentry *vm;
+ size_t count, i;
+ vm = kinfo_getvmmap(GetID(), &count);
+ if (vm == NULL) {
+ m_supports_mem_region = LazyBool::eLazyBoolNo;
+ Status error;
+ error.SetErrorString("not supported");
+ return error;
+ }
+ for (i = 0; i < count; i++) {
+ MemoryRegionInfo info;
+ info.Clear();
+ info.GetRange().SetRangeBase(vm[i].kve_start);
+ info.GetRange().SetRangeEnd(vm[i].kve_end);
+ info.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
+
+ if (vm[i].kve_protection & VM_PROT_READ)
+ info.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
+ else
+ info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+
+ if (vm[i].kve_protection & VM_PROT_WRITE)
+ info.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
+ else
+ info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+
+ if (vm[i].kve_protection & VM_PROT_EXECUTE)
+ info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
+ else
+ info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+
+ if (vm[i].kve_path[0])
+ info.SetName(vm[i].kve_path);
+
+ m_mem_region_cache.emplace_back(
+ info, FileSpec(info.GetName().GetCString()));
+ }
+ free(vm);
+
+ if (m_mem_region_cache.empty()) {
+ // No entries after attempting to read them. This shouldn't happen. Assume
+ // we don't support map entries.
+ LLDB_LOG(log, "failed to find any vmmap entries, assuming no support "
+ "for memory region metadata retrieval");
+ m_supports_mem_region = LazyBool::eLazyBoolNo;
+ Status error;
+ error.SetErrorString("not supported");
+ return error;
+ }
+ LLDB_LOG(log, "read {0} memory region entries from process {1}",
+ m_mem_region_cache.size(), GetID());
+ // We support memory retrieval, remember that.
+ m_supports_mem_region = LazyBool::eLazyBoolYes;
+ return Status();
+}
+
+Status NativeProcessNetBSD::AllocateMemory(size_t size, uint32_t permissions,
+ lldb::addr_t &addr) {
+ return Status("Unimplemented");
+}
+
+Status NativeProcessNetBSD::DeallocateMemory(lldb::addr_t addr) {
+ return Status("Unimplemented");
+}
+
+lldb::addr_t NativeProcessNetBSD::GetSharedLibraryInfoAddress() {
+ // punt on this for now
+ return LLDB_INVALID_ADDRESS;
+}
+
+size_t NativeProcessNetBSD::UpdateThreads() { return m_threads.size(); }
+
+Status NativeProcessNetBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+ bool hardware) {
+ if (hardware)
+ return Status("NativeProcessNetBSD does not support hardware breakpoints");
+ else
+ return SetSoftwareBreakpoint(addr, size);
+}
+
+Status NativeProcessNetBSD::GetLoadedModuleFileSpec(const char *module_path,
+ FileSpec &file_spec) {
+ return Status("Unimplemented");
+}
+
+Status NativeProcessNetBSD::GetFileLoadAddress(const llvm::StringRef &file_name,
+ lldb::addr_t &load_addr) {
+ load_addr = LLDB_INVALID_ADDRESS;
+ return Status();
+}
+
+void NativeProcessNetBSD::SigchldHandler() {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ // Process all pending waitpid notifications.
+ int status;
+ ::pid_t wait_pid =
+ llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status, WALLSIG | WNOHANG);
+
+ if (wait_pid == 0)
+ return; // We are done.
+
+ if (wait_pid == -1) {
+ Status error(errno, eErrorTypePOSIX);
+ LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error);
+ }
+
+ WaitStatus wait_status = WaitStatus::Decode(status);
+ bool exited = wait_status.type == WaitStatus::Exit ||
+ (wait_status.type == WaitStatus::Signal &&
+ wait_pid == static_cast<::pid_t>(GetID()));
+
+ LLDB_LOG(log,
+ "waitpid ({0}, &status, _) => pid = {1}, status = {2}, exited = {3}",
+ GetID(), wait_pid, status, exited);
+
+ if (exited)
+ MonitorExited(wait_pid, wait_status);
+ else {
+ assert(wait_status.type == WaitStatus::Stop);
+ MonitorCallback(wait_pid, wait_status.status);
+ }
+}
+
+bool NativeProcessNetBSD::HasThreadNoLock(lldb::tid_t thread_id) {
+ for (const auto &thread : m_threads) {
+ assert(thread && "thread list should not contain NULL threads");
+ if (thread->GetID() == thread_id) {
+ // We have this thread.
+ return true;
+ }
+ }
+
+ // We don't have this thread.
+ return false;
+}
+
+NativeThreadNetBSD &NativeProcessNetBSD::AddThread(lldb::tid_t thread_id) {
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id);
+
+ assert(!HasThreadNoLock(thread_id) &&
+ "attempted to add a thread by id that already exists");
+
+ // If this is the first thread, save it as the current thread
+ if (m_threads.empty())
+ SetCurrentThreadID(thread_id);
+
+ m_threads.push_back(llvm::make_unique<NativeThreadNetBSD>(*this, thread_id));
+ return static_cast<NativeThreadNetBSD &>(*m_threads.back());
+}
+
+Status NativeProcessNetBSD::Attach() {
+ // Attach to the requested process.
+ // An attach will cause the thread to stop with a SIGSTOP.
+ Status status = PtraceWrapper(PT_ATTACH, m_pid);
+ if (status.Fail())
+ return status;
+
+ int wstatus;
+ // Need to use WALLSIG otherwise we receive an error with errno=ECHLD At this
+ // point we should have a thread stopped if waitpid succeeds.
+ if ((wstatus = llvm::sys::RetryAfterSignal(-1, waitpid,
+ m_pid, nullptr, WALLSIG)) < 0)
+ return Status(errno, eErrorTypePOSIX);
+
+ /* Initialize threads */
+ status = ReinitializeThreads();
+ if (status.Fail())
+ return status;
+
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(SIGSTOP);
+
+ // Let our process instance know the thread has stopped.
+ SetState(StateType::eStateStopped);
+ return Status();
+}
+
+Status NativeProcessNetBSD::ReadMemory(lldb::addr_t addr, void *buf,
+ size_t size, size_t &bytes_read) {
+ unsigned char *dst = static_cast<unsigned char *>(buf);
+ struct ptrace_io_desc io;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY));
+ LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
+
+ bytes_read = 0;
+ io.piod_op = PIOD_READ_D;
+ io.piod_len = size;
+
+ do {
+ io.piod_offs = (void *)(addr + bytes_read);
+ io.piod_addr = dst + bytes_read;
+
+ Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io);
+ if (error.Fail() || io.piod_len == 0)
+ return error;
+
+ bytes_read += io.piod_len;
+ io.piod_len = size - bytes_read;
+ } while (bytes_read < size);
+
+ return Status();
+}
+
+Status NativeProcessNetBSD::WriteMemory(lldb::addr_t addr, const void *buf,
+ size_t size, size_t &bytes_written) {
+ const unsigned char *src = static_cast<const unsigned char *>(buf);
+ Status error;
+ struct ptrace_io_desc io;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY));
+ LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
+
+ bytes_written = 0;
+ io.piod_op = PIOD_WRITE_D;
+ io.piod_len = size;
+
+ do {
+ io.piod_addr = const_cast<void *>(static_cast<const void *>(src + bytes_written));
+ io.piod_offs = (void *)(addr + bytes_written);
+
+ Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io);
+ if (error.Fail() || io.piod_len == 0)
+ return error;
+
+ bytes_written += io.piod_len;
+ io.piod_len = size - bytes_written;
+ } while (bytes_written < size);
+
+ return error;
+}
+
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+NativeProcessNetBSD::GetAuxvData() const {
+ /*
+ * ELF_AUX_ENTRIES is currently restricted to kernel
+ * (<sys/exec_elf.h> r. 1.155 specifies 15)
+ *
+ * ptrace(2) returns the whole AUXV including extra fiels after AT_NULL this
+ * information isn't needed.
+ */
+ size_t auxv_size = 100 * sizeof(AuxInfo);
+
+ ErrorOr<std::unique_ptr<WritableMemoryBuffer>> buf =
+ llvm::WritableMemoryBuffer::getNewMemBuffer(auxv_size);
+
+ struct ptrace_io_desc io;
+ io.piod_op = PIOD_READ_AUXV;
+ io.piod_offs = 0;
+ io.piod_addr = static_cast<void *>(buf.get()->getBufferStart());
+ io.piod_len = auxv_size;
+
+ Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io);
+
+ if (error.Fail())
+ return std::error_code(error.GetError(), std::generic_category());
+
+ if (io.piod_len < 1)
+ return std::error_code(ECANCELED, std::generic_category());
+
+ return std::move(buf);
+}
+
+Status NativeProcessNetBSD::ReinitializeThreads() {
+ // Clear old threads
+ m_threads.clear();
+
+ // Initialize new thread
+ struct ptrace_lwpinfo info = {};
+ Status error = PtraceWrapper(PT_LWPINFO, GetID(), &info, sizeof(info));
+ if (error.Fail()) {
+ return error;
+ }
+ // Reinitialize from scratch threads and register them in process
+ while (info.pl_lwpid != 0) {
+ AddThread(info.pl_lwpid);
+ error = PtraceWrapper(PT_LWPINFO, GetID(), &info, sizeof(info));
+ if (error.Fail()) {
+ return error;
+ }
+ }
+
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
new file mode 100644
index 000000000000..e85ca3b7a925
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
@@ -0,0 +1,118 @@
+//===-- NativeProcessNetBSD.h --------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NativeProcessNetBSD_H_
+#define liblldb_NativeProcessNetBSD_H_
+
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/FileSpec.h"
+
+#include "NativeThreadNetBSD.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+
+namespace lldb_private {
+namespace process_netbsd {
+/// \class NativeProcessNetBSD
+/// Manages communication with the inferior (debugee) process.
+///
+/// Upon construction, this class prepares and launches an inferior process
+/// for debugging.
+///
+/// Changes in the inferior process state are broadcasted.
+class NativeProcessNetBSD : public NativeProcessProtocol {
+public:
+ class Factory : public NativeProcessProtocol::Factory {
+ public:
+ llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate,
+ MainLoop &mainloop) const override;
+
+ llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ Attach(lldb::pid_t pid, NativeDelegate &native_delegate,
+ MainLoop &mainloop) const override;
+ };
+
+ // NativeProcessProtocol Interface
+ Status Resume(const ResumeActionList &resume_actions) override;
+
+ Status Halt() override;
+
+ Status Detach() override;
+
+ Status Signal(int signo) override;
+
+ Status Kill() override;
+
+ Status GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) override;
+
+ Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ size_t &bytes_read) override;
+
+ Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
+ size_t &bytes_written) override;
+
+ Status AllocateMemory(size_t size, uint32_t permissions,
+ lldb::addr_t &addr) override;
+
+ Status DeallocateMemory(lldb::addr_t addr) override;
+
+ lldb::addr_t GetSharedLibraryInfoAddress() override;
+
+ size_t UpdateThreads() override;
+
+ const ArchSpec &GetArchitecture() const override { return m_arch; }
+
+ Status SetBreakpoint(lldb::addr_t addr, uint32_t size,
+ bool hardware) override;
+
+ Status GetLoadedModuleFileSpec(const char *module_path,
+ FileSpec &file_spec) override;
+
+ Status GetFileLoadAddress(const llvm::StringRef &file_name,
+ lldb::addr_t &load_addr) override;
+
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+ GetAuxvData() const override;
+
+ // Interface used by NativeRegisterContext-derived classes.
+ static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr,
+ int data = 0, int *result = nullptr);
+
+private:
+ MainLoop::SignalHandleUP m_sigchld_handle;
+ ArchSpec m_arch;
+ LazyBool m_supports_mem_region = eLazyBoolCalculate;
+ std::vector<std::pair<MemoryRegionInfo, FileSpec>> m_mem_region_cache;
+
+ // Private Instance Methods
+ NativeProcessNetBSD(::pid_t pid, int terminal_fd, NativeDelegate &delegate,
+ const ArchSpec &arch, MainLoop &mainloop);
+
+ bool HasThreadNoLock(lldb::tid_t thread_id);
+
+ NativeThreadNetBSD &AddThread(lldb::tid_t thread_id);
+
+ void MonitorCallback(lldb::pid_t pid, int signal);
+ void MonitorExited(lldb::pid_t pid, WaitStatus status);
+ void MonitorSIGSTOP(lldb::pid_t pid);
+ void MonitorSIGTRAP(lldb::pid_t pid);
+ void MonitorSignal(lldb::pid_t pid, int signal);
+
+ Status PopulateMemoryRegionCache();
+ void SigchldHandler();
+
+ Status Attach();
+ Status ReinitializeThreads();
+};
+
+} // namespace process_netbsd
+} // namespace lldb_private
+
+#endif // #ifndef liblldb_NativeProcessNetBSD_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp
new file mode 100644
index 000000000000..3a9caaad74c8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp
@@ -0,0 +1,38 @@
+//===-- NativeRegisterContextNetBSD.cpp -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeRegisterContextNetBSD.h"
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+
+using namespace lldb_private;
+using namespace lldb_private::process_netbsd;
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+// clang-format on
+
+NativeRegisterContextNetBSD::NativeRegisterContextNetBSD(
+ NativeThreadProtocol &native_thread,
+ RegisterInfoInterface *reg_info_interface_p)
+ : NativeRegisterContextRegisterInfo(native_thread,
+ reg_info_interface_p) {}
+
+Status NativeRegisterContextNetBSD::DoRegisterSet(int ptrace_req, void *buf) {
+ return NativeProcessNetBSD::PtraceWrapper(ptrace_req, GetProcessPid(), buf,
+ m_thread.GetID());
+}
+
+NativeProcessNetBSD &NativeRegisterContextNetBSD::GetProcess() {
+ return static_cast<NativeProcessNetBSD &>(m_thread.GetProcess());
+}
+
+::pid_t NativeRegisterContextNetBSD::GetProcessPid() {
+ return GetProcess().GetID();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h
new file mode 100644
index 000000000000..f5dd0c33b677
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h
@@ -0,0 +1,43 @@
+//===-- NativeRegisterContextNetBSD.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_NativeRegisterContextNetBSD_h
+#define lldb_NativeRegisterContextNetBSD_h
+
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h"
+#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
+
+namespace lldb_private {
+namespace process_netbsd {
+
+class NativeRegisterContextNetBSD : public NativeRegisterContextRegisterInfo {
+public:
+ NativeRegisterContextNetBSD(NativeThreadProtocol &native_thread,
+ RegisterInfoInterface *reg_info_interface_p);
+
+ // This function is implemented in the NativeRegisterContextNetBSD_*
+ // subclasses to create a new instance of the host specific
+ // NativeRegisterContextNetBSD. The implementations can't collide as only one
+ // NativeRegisterContextNetBSD_* variant should be compiled into the final
+ // executable.
+ static NativeRegisterContextNetBSD *
+ CreateHostNativeRegisterContextNetBSD(const ArchSpec &target_arch,
+ NativeThreadProtocol &native_thread);
+
+protected:
+ Status DoRegisterSet(int req, void *buf);
+ virtual NativeProcessNetBSD &GetProcess();
+ virtual ::pid_t GetProcessPid();
+};
+
+} // namespace process_netbsd
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextNetBSD_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp
new file mode 100644
index 000000000000..a7cd637bf826
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp
@@ -0,0 +1,933 @@
+//===-- NativeRegisterContextNetBSD_x86_64.cpp ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__x86_64__)
+
+#include "NativeRegisterContextNetBSD_x86_64.h"
+
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h"
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+#include <x86/cpu.h>
+#include <x86/cpu_extended_state.h>
+#include <x86/specialreg.h>
+#include <elf.h>
+#include <err.h>
+#include <stdint.h>
+#include <stdlib.h>
+// clang-format on
+
+using namespace lldb_private;
+using namespace lldb_private::process_netbsd;
+
+// Private namespace.
+
+namespace {
+// x86 64-bit general purpose registers.
+static const uint32_t g_gpr_regnums_x86_64[] = {
+ lldb_rax_x86_64, lldb_rbx_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64,
+ lldb_rdi_x86_64, lldb_rsi_x86_64, lldb_rbp_x86_64, lldb_rsp_x86_64,
+ lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64,
+ lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64,
+ lldb_rip_x86_64, lldb_rflags_x86_64, lldb_cs_x86_64, lldb_fs_x86_64,
+ lldb_gs_x86_64, lldb_ss_x86_64, lldb_ds_x86_64, lldb_es_x86_64,
+ lldb_eax_x86_64, lldb_ebx_x86_64, lldb_ecx_x86_64, lldb_edx_x86_64,
+ lldb_edi_x86_64, lldb_esi_x86_64, lldb_ebp_x86_64, lldb_esp_x86_64,
+ lldb_r8d_x86_64, // Low 32 bits or r8
+ lldb_r9d_x86_64, // Low 32 bits or r9
+ lldb_r10d_x86_64, // Low 32 bits or r10
+ lldb_r11d_x86_64, // Low 32 bits or r11
+ lldb_r12d_x86_64, // Low 32 bits or r12
+ lldb_r13d_x86_64, // Low 32 bits or r13
+ lldb_r14d_x86_64, // Low 32 bits or r14
+ lldb_r15d_x86_64, // Low 32 bits or r15
+ lldb_ax_x86_64, lldb_bx_x86_64, lldb_cx_x86_64, lldb_dx_x86_64,
+ lldb_di_x86_64, lldb_si_x86_64, lldb_bp_x86_64, lldb_sp_x86_64,
+ lldb_r8w_x86_64, // Low 16 bits or r8
+ lldb_r9w_x86_64, // Low 16 bits or r9
+ lldb_r10w_x86_64, // Low 16 bits or r10
+ lldb_r11w_x86_64, // Low 16 bits or r11
+ lldb_r12w_x86_64, // Low 16 bits or r12
+ lldb_r13w_x86_64, // Low 16 bits or r13
+ lldb_r14w_x86_64, // Low 16 bits or r14
+ lldb_r15w_x86_64, // Low 16 bits or r15
+ lldb_ah_x86_64, lldb_bh_x86_64, lldb_ch_x86_64, lldb_dh_x86_64,
+ lldb_al_x86_64, lldb_bl_x86_64, lldb_cl_x86_64, lldb_dl_x86_64,
+ lldb_dil_x86_64, lldb_sil_x86_64, lldb_bpl_x86_64, lldb_spl_x86_64,
+ lldb_r8l_x86_64, // Low 8 bits or r8
+ lldb_r9l_x86_64, // Low 8 bits or r9
+ lldb_r10l_x86_64, // Low 8 bits or r10
+ lldb_r11l_x86_64, // Low 8 bits or r11
+ lldb_r12l_x86_64, // Low 8 bits or r12
+ lldb_r13l_x86_64, // Low 8 bits or r13
+ lldb_r14l_x86_64, // Low 8 bits or r14
+ lldb_r15l_x86_64, // Low 8 bits or r15
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) -
+ 1 ==
+ k_num_gpr_registers_x86_64,
+ "g_gpr_regnums_x86_64 has wrong number of register infos");
+
+// Number of register sets provided by this context.
+enum { k_num_extended_register_sets = 2, k_num_register_sets = 4 };
+
+// Register sets for x86 64-bit.
+static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_x86_64,
+ g_gpr_regnums_x86_64},
+};
+
+#define REG_CONTEXT_SIZE (GetRegisterInfoInterface().GetGPRSize())
+} // namespace
+
+NativeRegisterContextNetBSD *
+NativeRegisterContextNetBSD::CreateHostNativeRegisterContextNetBSD(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
+ return new NativeRegisterContextNetBSD_x86_64(target_arch, native_thread);
+}
+
+// NativeRegisterContextNetBSD_x86_64 members.
+
+static RegisterInfoInterface *
+CreateRegisterInfoInterface(const ArchSpec &target_arch) {
+ assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) &&
+ "Register setting path assumes this is a 64-bit host");
+ // X86_64 hosts know how to work with 64-bit and 32-bit EXEs using the x86_64
+ // register context.
+ return new RegisterContextNetBSD_x86_64(target_arch);
+}
+
+NativeRegisterContextNetBSD_x86_64::NativeRegisterContextNetBSD_x86_64(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
+ : NativeRegisterContextNetBSD(native_thread,
+ CreateRegisterInfoInterface(target_arch)),
+ m_gpr_x86_64(), m_fpr_x86_64(), m_dbr_x86_64() {}
+
+// CONSIDER after local and llgs debugging are merged, register set support can
+// be moved into a base x86-64 class with IsRegisterSetAvailable made virtual.
+uint32_t NativeRegisterContextNetBSD_x86_64::GetRegisterSetCount() const {
+ uint32_t sets = 0;
+ for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) {
+ if (GetSetForNativeRegNum(set_index) != -1)
+ ++sets;
+ }
+
+ return sets;
+}
+
+const RegisterSet *
+NativeRegisterContextNetBSD_x86_64::GetRegisterSet(uint32_t set_index) const {
+ switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
+ case llvm::Triple::x86_64:
+ return &g_reg_sets_x86_64[set_index];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+
+ return nullptr;
+}
+
+int NativeRegisterContextNetBSD_x86_64::GetSetForNativeRegNum(
+ int reg_num) const {
+ if (reg_num <= k_last_gpr_x86_64)
+ return GPRegSet;
+ else if (reg_num <= k_last_fpr_x86_64)
+ return FPRegSet;
+ else if (reg_num <= k_last_avx_x86_64)
+ return XStateRegSet; // AVX
+ else if (reg_num <= k_last_mpxr_x86_64)
+ return -1; // MPXR
+ else if (reg_num <= k_last_mpxc_x86_64)
+ return -1; // MPXC
+ else if (reg_num <= lldb_dr7_x86_64)
+ return DBRegSet; // DBR
+ else
+ return -1;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::ReadRegisterSet(uint32_t set) {
+ switch (set) {
+ case GPRegSet:
+ return DoRegisterSet(PT_GETREGS, &m_gpr_x86_64);
+ case FPRegSet:
+ return DoRegisterSet(PT_GETFPREGS, &m_fpr_x86_64);
+ case DBRegSet:
+ return DoRegisterSet(PT_GETDBREGS, &m_dbr_x86_64);
+ case XStateRegSet:
+#ifdef HAVE_XSTATE
+ {
+ struct iovec iov = {&m_xstate_x86_64, sizeof(m_xstate_x86_64)};
+ return DoRegisterSet(PT_GETXSTATE, &iov);
+ }
+#else
+ return Status("XState is not supported by the kernel");
+#endif
+ }
+ llvm_unreachable("NativeRegisterContextNetBSD_x86_64::ReadRegisterSet");
+}
+
+Status NativeRegisterContextNetBSD_x86_64::WriteRegisterSet(uint32_t set) {
+ switch (set) {
+ case GPRegSet:
+ return DoRegisterSet(PT_SETREGS, &m_gpr_x86_64);
+ case FPRegSet:
+ return DoRegisterSet(PT_SETFPREGS, &m_fpr_x86_64);
+ case DBRegSet:
+ return DoRegisterSet(PT_SETDBREGS, &m_dbr_x86_64);
+ case XStateRegSet:
+#ifdef HAVE_XSTATE
+ {
+ struct iovec iov = {&m_xstate_x86_64, sizeof(m_xstate_x86_64)};
+ return DoRegisterSet(PT_SETXSTATE, &iov);
+ }
+#else
+ return Status("XState is not supported by the kernel");
+#endif
+ }
+ llvm_unreachable("NativeRegisterContextNetBSD_x86_64::WriteRegisterSet");
+}
+
+Status
+NativeRegisterContextNetBSD_x86_64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ Status error;
+
+ if (!reg_info) {
+ error.SetErrorString("reg_info NULL");
+ return error;
+ }
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM) {
+ // This is likely an internal register for lldb use only and should not be
+ // directly queried.
+ error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
+ "register, cannot read directly",
+ reg_info->name);
+ return error;
+ }
+
+ int set = GetSetForNativeRegNum(reg);
+ if (set == -1) {
+ // This is likely an internal register for lldb use only and should not be
+ // directly queried.
+ error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set",
+ reg_info->name);
+ return error;
+ }
+
+ error = ReadRegisterSet(set);
+ if (error.Fail())
+ return error;
+
+ switch (reg) {
+ case lldb_rax_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RAX];
+ break;
+ case lldb_rbx_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RBX];
+ break;
+ case lldb_rcx_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RCX];
+ break;
+ case lldb_rdx_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RDX];
+ break;
+ case lldb_rdi_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RDI];
+ break;
+ case lldb_rsi_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RSI];
+ break;
+ case lldb_rbp_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RBP];
+ break;
+ case lldb_rsp_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RSP];
+ break;
+ case lldb_r8_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R8];
+ break;
+ case lldb_r9_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R9];
+ break;
+ case lldb_r10_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R10];
+ break;
+ case lldb_r11_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R11];
+ break;
+ case lldb_r12_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R12];
+ break;
+ case lldb_r13_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R13];
+ break;
+ case lldb_r14_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R14];
+ break;
+ case lldb_r15_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R15];
+ break;
+ case lldb_rip_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RIP];
+ break;
+ case lldb_rflags_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RFLAGS];
+ break;
+ case lldb_cs_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_CS];
+ break;
+ case lldb_fs_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_FS];
+ break;
+ case lldb_gs_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_GS];
+ break;
+ case lldb_ss_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_SS];
+ break;
+ case lldb_ds_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_DS];
+ break;
+ case lldb_es_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_ES];
+ break;
+ case lldb_fctrl_x86_64:
+ reg_value = (uint16_t)m_fpr_x86_64.fxstate.fx_cw;
+ break;
+ case lldb_fstat_x86_64:
+ reg_value = (uint16_t)m_fpr_x86_64.fxstate.fx_sw;
+ break;
+ case lldb_ftag_x86_64:
+ reg_value = (uint8_t)m_fpr_x86_64.fxstate.fx_tw;
+ break;
+ case lldb_fop_x86_64:
+ reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_opcode;
+ break;
+ case lldb_fiseg_x86_64:
+ reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_ip.fa_64;
+ break;
+ case lldb_fioff_x86_64:
+ reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_ip.fa_32.fa_off;
+ break;
+ case lldb_foseg_x86_64:
+ reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_dp.fa_64;
+ break;
+ case lldb_fooff_x86_64:
+ reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_dp.fa_32.fa_off;
+ break;
+ case lldb_mxcsr_x86_64:
+ reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_mxcsr;
+ break;
+ case lldb_mxcsrmask_x86_64:
+ reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_mxcsr_mask;
+ break;
+ case lldb_st0_x86_64:
+ case lldb_st1_x86_64:
+ case lldb_st2_x86_64:
+ case lldb_st3_x86_64:
+ case lldb_st4_x86_64:
+ case lldb_st5_x86_64:
+ case lldb_st6_x86_64:
+ case lldb_st7_x86_64:
+ reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_st0_x86_64],
+ reg_info->byte_size, endian::InlHostByteOrder());
+ break;
+ case lldb_mm0_x86_64:
+ case lldb_mm1_x86_64:
+ case lldb_mm2_x86_64:
+ case lldb_mm3_x86_64:
+ case lldb_mm4_x86_64:
+ case lldb_mm5_x86_64:
+ case lldb_mm6_x86_64:
+ case lldb_mm7_x86_64:
+ reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_mm0_x86_64],
+ reg_info->byte_size, endian::InlHostByteOrder());
+ break;
+ case lldb_xmm0_x86_64:
+ case lldb_xmm1_x86_64:
+ case lldb_xmm2_x86_64:
+ case lldb_xmm3_x86_64:
+ case lldb_xmm4_x86_64:
+ case lldb_xmm5_x86_64:
+ case lldb_xmm6_x86_64:
+ case lldb_xmm7_x86_64:
+ case lldb_xmm8_x86_64:
+ case lldb_xmm9_x86_64:
+ case lldb_xmm10_x86_64:
+ case lldb_xmm11_x86_64:
+ case lldb_xmm12_x86_64:
+ case lldb_xmm13_x86_64:
+ case lldb_xmm14_x86_64:
+ case lldb_xmm15_x86_64:
+ reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_xmm[reg - lldb_xmm0_x86_64],
+ reg_info->byte_size, endian::InlHostByteOrder());
+ break;
+ case lldb_ymm0_x86_64:
+ case lldb_ymm1_x86_64:
+ case lldb_ymm2_x86_64:
+ case lldb_ymm3_x86_64:
+ case lldb_ymm4_x86_64:
+ case lldb_ymm5_x86_64:
+ case lldb_ymm6_x86_64:
+ case lldb_ymm7_x86_64:
+ case lldb_ymm8_x86_64:
+ case lldb_ymm9_x86_64:
+ case lldb_ymm10_x86_64:
+ case lldb_ymm11_x86_64:
+ case lldb_ymm12_x86_64:
+ case lldb_ymm13_x86_64:
+ case lldb_ymm14_x86_64:
+ case lldb_ymm15_x86_64:
+#ifdef HAVE_XSTATE
+ if (!(m_xstate_x86_64.xs_rfbm & XCR0_SSE) ||
+ !(m_xstate_x86_64.xs_rfbm & XCR0_YMM_Hi128)) {
+ error.SetErrorStringWithFormat("register \"%s\" not supported by CPU/kernel",
+ reg_info->name);
+ } else {
+ uint32_t reg_index = reg - lldb_ymm0_x86_64;
+ YMMReg ymm = XStateToYMM(
+ m_xstate_x86_64.xs_fxsave.fx_xmm[reg_index].xmm_bytes,
+ m_xstate_x86_64.xs_ymm_hi128.xs_ymm[reg_index].ymm_bytes);
+ reg_value.SetBytes(ymm.bytes, reg_info->byte_size,
+ endian::InlHostByteOrder());
+ }
+#else
+ error.SetErrorString("XState queries not supported by the kernel");
+#endif
+ break;
+ case lldb_dr0_x86_64:
+ case lldb_dr1_x86_64:
+ case lldb_dr2_x86_64:
+ case lldb_dr3_x86_64:
+ case lldb_dr4_x86_64:
+ case lldb_dr5_x86_64:
+ case lldb_dr6_x86_64:
+ case lldb_dr7_x86_64:
+ reg_value = (uint64_t)m_dbr_x86_64.dr[reg - lldb_dr0_x86_64];
+ break;
+ }
+
+ return error;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &reg_value) {
+
+ Status error;
+
+ if (!reg_info) {
+ error.SetErrorString("reg_info NULL");
+ return error;
+ }
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM) {
+ // This is likely an internal register for lldb use only and should not be
+ // directly queried.
+ error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
+ "register, cannot read directly",
+ reg_info->name);
+ return error;
+ }
+
+ int set = GetSetForNativeRegNum(reg);
+ if (set == -1) {
+ // This is likely an internal register for lldb use only and should not be
+ // directly queried.
+ error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set",
+ reg_info->name);
+ return error;
+ }
+
+ error = ReadRegisterSet(set);
+ if (error.Fail())
+ return error;
+
+ switch (reg) {
+ case lldb_rax_x86_64:
+ m_gpr_x86_64.regs[_REG_RAX] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rbx_x86_64:
+ m_gpr_x86_64.regs[_REG_RBX] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rcx_x86_64:
+ m_gpr_x86_64.regs[_REG_RCX] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rdx_x86_64:
+ m_gpr_x86_64.regs[_REG_RDX] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rdi_x86_64:
+ m_gpr_x86_64.regs[_REG_RDI] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rsi_x86_64:
+ m_gpr_x86_64.regs[_REG_RSI] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rbp_x86_64:
+ m_gpr_x86_64.regs[_REG_RBP] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rsp_x86_64:
+ m_gpr_x86_64.regs[_REG_RSP] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r8_x86_64:
+ m_gpr_x86_64.regs[_REG_R8] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r9_x86_64:
+ m_gpr_x86_64.regs[_REG_R9] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r10_x86_64:
+ m_gpr_x86_64.regs[_REG_R10] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r11_x86_64:
+ m_gpr_x86_64.regs[_REG_R11] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r12_x86_64:
+ m_gpr_x86_64.regs[_REG_R12] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r13_x86_64:
+ m_gpr_x86_64.regs[_REG_R13] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r14_x86_64:
+ m_gpr_x86_64.regs[_REG_R14] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r15_x86_64:
+ m_gpr_x86_64.regs[_REG_R15] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rip_x86_64:
+ m_gpr_x86_64.regs[_REG_RIP] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rflags_x86_64:
+ m_gpr_x86_64.regs[_REG_RFLAGS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_cs_x86_64:
+ m_gpr_x86_64.regs[_REG_CS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_fs_x86_64:
+ m_gpr_x86_64.regs[_REG_FS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_gs_x86_64:
+ m_gpr_x86_64.regs[_REG_GS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_ss_x86_64:
+ m_gpr_x86_64.regs[_REG_SS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_ds_x86_64:
+ m_gpr_x86_64.regs[_REG_DS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_es_x86_64:
+ m_gpr_x86_64.regs[_REG_ES] = reg_value.GetAsUInt64();
+ break;
+ case lldb_fctrl_x86_64:
+ m_fpr_x86_64.fxstate.fx_cw = reg_value.GetAsUInt16();
+ break;
+ case lldb_fstat_x86_64:
+ m_fpr_x86_64.fxstate.fx_sw = reg_value.GetAsUInt16();
+ break;
+ case lldb_ftag_x86_64:
+ m_fpr_x86_64.fxstate.fx_tw = reg_value.GetAsUInt8();
+ break;
+ case lldb_fop_x86_64:
+ m_fpr_x86_64.fxstate.fx_opcode = reg_value.GetAsUInt16();
+ break;
+ case lldb_fiseg_x86_64:
+ m_fpr_x86_64.fxstate.fx_ip.fa_64 = reg_value.GetAsUInt64();
+ break;
+ case lldb_fioff_x86_64:
+ m_fpr_x86_64.fxstate.fx_ip.fa_32.fa_off = reg_value.GetAsUInt32();
+ break;
+ case lldb_foseg_x86_64:
+ m_fpr_x86_64.fxstate.fx_dp.fa_64 = reg_value.GetAsUInt64();
+ break;
+ case lldb_fooff_x86_64:
+ m_fpr_x86_64.fxstate.fx_dp.fa_32.fa_off = reg_value.GetAsUInt32();
+ break;
+ case lldb_mxcsr_x86_64:
+ m_fpr_x86_64.fxstate.fx_mxcsr = reg_value.GetAsUInt32();
+ break;
+ case lldb_mxcsrmask_x86_64:
+ m_fpr_x86_64.fxstate.fx_mxcsr_mask = reg_value.GetAsUInt32();
+ break;
+ case lldb_st0_x86_64:
+ case lldb_st1_x86_64:
+ case lldb_st2_x86_64:
+ case lldb_st3_x86_64:
+ case lldb_st4_x86_64:
+ case lldb_st5_x86_64:
+ case lldb_st6_x86_64:
+ case lldb_st7_x86_64:
+ ::memcpy(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_st0_x86_64],
+ reg_value.GetBytes(), reg_value.GetByteSize());
+ break;
+ case lldb_mm0_x86_64:
+ case lldb_mm1_x86_64:
+ case lldb_mm2_x86_64:
+ case lldb_mm3_x86_64:
+ case lldb_mm4_x86_64:
+ case lldb_mm5_x86_64:
+ case lldb_mm6_x86_64:
+ case lldb_mm7_x86_64:
+ ::memcpy(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_mm0_x86_64],
+ reg_value.GetBytes(), reg_value.GetByteSize());
+ break;
+ case lldb_xmm0_x86_64:
+ case lldb_xmm1_x86_64:
+ case lldb_xmm2_x86_64:
+ case lldb_xmm3_x86_64:
+ case lldb_xmm4_x86_64:
+ case lldb_xmm5_x86_64:
+ case lldb_xmm6_x86_64:
+ case lldb_xmm7_x86_64:
+ case lldb_xmm8_x86_64:
+ case lldb_xmm9_x86_64:
+ case lldb_xmm10_x86_64:
+ case lldb_xmm11_x86_64:
+ case lldb_xmm12_x86_64:
+ case lldb_xmm13_x86_64:
+ case lldb_xmm14_x86_64:
+ case lldb_xmm15_x86_64:
+ ::memcpy(&m_fpr_x86_64.fxstate.fx_xmm[reg - lldb_xmm0_x86_64],
+ reg_value.GetBytes(), reg_value.GetByteSize());
+ break;
+ case lldb_ymm0_x86_64:
+ case lldb_ymm1_x86_64:
+ case lldb_ymm2_x86_64:
+ case lldb_ymm3_x86_64:
+ case lldb_ymm4_x86_64:
+ case lldb_ymm5_x86_64:
+ case lldb_ymm6_x86_64:
+ case lldb_ymm7_x86_64:
+ case lldb_ymm8_x86_64:
+ case lldb_ymm9_x86_64:
+ case lldb_ymm10_x86_64:
+ case lldb_ymm11_x86_64:
+ case lldb_ymm12_x86_64:
+ case lldb_ymm13_x86_64:
+ case lldb_ymm14_x86_64:
+ case lldb_ymm15_x86_64:
+#ifdef HAVE_XSTATE
+ if (!(m_xstate_x86_64.xs_rfbm & XCR0_SSE) ||
+ !(m_xstate_x86_64.xs_rfbm & XCR0_YMM_Hi128)) {
+ error.SetErrorStringWithFormat("register \"%s\" not supported by CPU/kernel",
+ reg_info->name);
+ } else {
+ uint32_t reg_index = reg - lldb_ymm0_x86_64;
+ YMMReg ymm;
+ ::memcpy(ymm.bytes, reg_value.GetBytes(), reg_value.GetByteSize());
+ YMMToXState(ymm,
+ m_xstate_x86_64.xs_fxsave.fx_xmm[reg_index].xmm_bytes,
+ m_xstate_x86_64.xs_ymm_hi128.xs_ymm[reg_index].ymm_bytes);
+ }
+#else
+ error.SetErrorString("XState not supported by the kernel");
+#endif
+ break;
+ case lldb_dr0_x86_64:
+ case lldb_dr1_x86_64:
+ case lldb_dr2_x86_64:
+ case lldb_dr3_x86_64:
+ case lldb_dr4_x86_64:
+ case lldb_dr5_x86_64:
+ case lldb_dr6_x86_64:
+ case lldb_dr7_x86_64:
+ m_dbr_x86_64.dr[reg - lldb_dr0_x86_64] = reg_value.GetAsUInt64();
+ break;
+ }
+
+ return WriteRegisterSet(set);
+}
+
+Status NativeRegisterContextNetBSD_x86_64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ Status error;
+
+ data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ error = ReadRegisterSet(GPRegSet);
+ if (error.Fail())
+ return error;
+
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &m_gpr_x86_64, GetRegisterInfoInterface().GetGPRSize());
+ dst += GetRegisterInfoInterface().GetGPRSize();
+
+ RegisterValue value((uint64_t)-1);
+ const RegisterInfo *reg_info =
+ GetRegisterInfoInterface().GetDynamicRegisterInfo("orig_eax");
+ if (reg_info == nullptr)
+ reg_info = GetRegisterInfoInterface().GetDynamicRegisterInfo("orig_rax");
+ return error;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ Status error;
+
+ if (!data_sp) {
+ error.SetErrorStringWithFormat(
+ "NativeRegisterContextNetBSD_x86_64::%s invalid data_sp provided",
+ __FUNCTION__);
+ return error;
+ }
+
+ if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {
+ error.SetErrorStringWithFormat(
+ "NativeRegisterContextNetBSD_x86_64::%s data_sp contained mismatched "
+ "data size, expected %" PRIu64 ", actual %" PRIu64,
+ __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());
+ return error;
+ }
+
+ uint8_t *src = data_sp->GetBytes();
+ if (src == nullptr) {
+ error.SetErrorStringWithFormat("NativeRegisterContextNetBSD_x86_64::%s "
+ "DataBuffer::GetBytes() returned a null "
+ "pointer",
+ __FUNCTION__);
+ return error;
+ }
+ ::memcpy(&m_gpr_x86_64, src, GetRegisterInfoInterface().GetGPRSize());
+
+ error = WriteRegisterSet(GPRegSet);
+ if (error.Fail())
+ return error;
+ src += GetRegisterInfoInterface().GetGPRSize();
+
+ return error;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::IsWatchpointHit(uint32_t wp_index,
+ bool &is_hit) {
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Status("Watchpoint index out of range");
+
+ RegisterValue reg_value;
+ const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(lldb_dr6_x86_64);
+ Status error = ReadRegister(reg_info, reg_value);
+ if (error.Fail()) {
+ is_hit = false;
+ return error;
+ }
+
+ uint64_t status_bits = reg_value.GetAsUInt64();
+
+ is_hit = status_bits & (1 << wp_index);
+
+ return error;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::GetWatchpointHitIndex(
+ uint32_t &wp_index, lldb::addr_t trap_addr) {
+ uint32_t num_hw_wps = NumSupportedHardwareWatchpoints();
+ for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) {
+ bool is_hit;
+ Status error = IsWatchpointHit(wp_index, is_hit);
+ if (error.Fail()) {
+ wp_index = LLDB_INVALID_INDEX32;
+ return error;
+ } else if (is_hit) {
+ return error;
+ }
+ }
+ wp_index = LLDB_INVALID_INDEX32;
+ return Status();
+}
+
+Status NativeRegisterContextNetBSD_x86_64::IsWatchpointVacant(uint32_t wp_index,
+ bool &is_vacant) {
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Status("Watchpoint index out of range");
+
+ RegisterValue reg_value;
+ const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(lldb_dr7_x86_64);
+ Status error = ReadRegister(reg_info, reg_value);
+ if (error.Fail()) {
+ is_vacant = false;
+ return error;
+ }
+
+ uint64_t control_bits = reg_value.GetAsUInt64();
+
+ is_vacant = !(control_bits & (1 << (2 * wp_index + 1)));
+
+ return error;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::SetHardwareWatchpointWithIndex(
+ lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
+
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Status("Watchpoint index out of range");
+
+ // Read only watchpoints aren't supported on x86_64. Fall back to read/write
+ // waitchpoints instead.
+ // TODO: Add logic to detect when a write happens and ignore that watchpoint
+ // hit.
+ if (watch_flags == 0x2)
+ watch_flags = 0x3;
+
+ if (watch_flags != 0x1 && watch_flags != 0x3)
+ return Status("Invalid read/write bits for watchpoint");
+
+ if (size != 1 && size != 2 && size != 4 && size != 8)
+ return Status("Invalid size for watchpoint");
+
+ bool is_vacant;
+ Status error = IsWatchpointVacant(wp_index, is_vacant);
+ if (error.Fail())
+ return error;
+ if (!is_vacant)
+ return Status("Watchpoint index not vacant");
+
+ RegisterValue reg_value;
+ const RegisterInfo *const reg_info_dr7 =
+ GetRegisterInfoAtIndex(lldb_dr7_x86_64);
+ error = ReadRegister(reg_info_dr7, reg_value);
+ if (error.Fail())
+ return error;
+
+ // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7
+ uint64_t enable_bit = 1 << (2 * wp_index + 1);
+
+ // set bits 16-17, 20-21, 24-25, or 28-29
+ // with 0b01 for write, and 0b11 for read/write
+ uint64_t rw_bits = watch_flags << (16 + 4 * wp_index);
+
+ // set bits 18-19, 22-23, 26-27, or 30-31
+ // with 0b00, 0b01, 0b10, or 0b11
+ // for 1, 2, 8 (if supported), or 4 bytes, respectively
+ uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index);
+
+ uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
+
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+
+ control_bits |= enable_bit | rw_bits | size_bits;
+
+ const RegisterInfo *const reg_info_drN =
+ GetRegisterInfoAtIndex(lldb_dr0_x86_64 + wp_index);
+ error = WriteRegister(reg_info_drN, RegisterValue(addr));
+ if (error.Fail())
+ return error;
+
+ error = WriteRegister(reg_info_dr7, RegisterValue(control_bits));
+ if (error.Fail())
+ return error;
+
+ error.Clear();
+ return error;
+}
+
+bool NativeRegisterContextNetBSD_x86_64::ClearHardwareWatchpoint(
+ uint32_t wp_index) {
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return false;
+
+ RegisterValue reg_value;
+
+ // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0, 1, 2, or 3 of
+ // the debug status register (DR6)
+ const RegisterInfo *const reg_info_dr6 =
+ GetRegisterInfoAtIndex(lldb_dr6_x86_64);
+ Status error = ReadRegister(reg_info_dr6, reg_value);
+ if (error.Fail())
+ return false;
+ uint64_t bit_mask = 1 << wp_index;
+ uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ error = WriteRegister(reg_info_dr6, RegisterValue(status_bits));
+ if (error.Fail())
+ return false;
+
+ // for watchpoints 0, 1, 2, or 3, respectively, clear bits {0-1,16-19},
+ // {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} of the debug control register
+ // (DR7)
+ const RegisterInfo *const reg_info_dr7 =
+ GetRegisterInfoAtIndex(lldb_dr7_x86_64);
+ error = ReadRegister(reg_info_dr7, reg_value);
+ if (error.Fail())
+ return false;
+ bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ return WriteRegister(reg_info_dr7, RegisterValue(control_bits)).Success();
+}
+
+Status NativeRegisterContextNetBSD_x86_64::ClearAllHardwareWatchpoints() {
+ RegisterValue reg_value;
+
+ // clear bits {0-4} of the debug status register (DR6)
+ const RegisterInfo *const reg_info_dr6 =
+ GetRegisterInfoAtIndex(lldb_dr6_x86_64);
+ Status error = ReadRegister(reg_info_dr6, reg_value);
+ if (error.Fail())
+ return error;
+ uint64_t bit_mask = 0xF;
+ uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ error = WriteRegister(reg_info_dr6, RegisterValue(status_bits));
+ if (error.Fail())
+ return error;
+
+ // clear bits {0-7,16-31} of the debug control register (DR7)
+ const RegisterInfo *const reg_info_dr7 =
+ GetRegisterInfoAtIndex(lldb_dr7_x86_64);
+ error = ReadRegister(reg_info_dr7, reg_value);
+ if (error.Fail())
+ return error;
+ bit_mask = 0xFF | (0xFFFF << 16);
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ return WriteRegister(reg_info_dr7, RegisterValue(control_bits));
+}
+
+uint32_t NativeRegisterContextNetBSD_x86_64::SetHardwareWatchpoint(
+ lldb::addr_t addr, size_t size, uint32_t watch_flags) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) {
+ bool is_vacant;
+ Status error = IsWatchpointVacant(wp_index, is_vacant);
+ if (is_vacant) {
+ error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index);
+ if (error.Success())
+ return wp_index;
+ }
+ if (error.Fail() && log) {
+ log->Printf("NativeRegisterContextNetBSD_x86_64::%s Error: %s",
+ __FUNCTION__, error.AsCString());
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+lldb::addr_t
+NativeRegisterContextNetBSD_x86_64::GetWatchpointAddress(uint32_t wp_index) {
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return LLDB_INVALID_ADDRESS;
+ RegisterValue reg_value;
+ const RegisterInfo *const reg_info_drN =
+ GetRegisterInfoAtIndex(lldb_dr0_x86_64 + wp_index);
+ if (ReadRegister(reg_info_drN, reg_value).Fail())
+ return LLDB_INVALID_ADDRESS;
+ return reg_value.GetAsUInt64();
+}
+
+uint32_t NativeRegisterContextNetBSD_x86_64::NumSupportedHardwareWatchpoints() {
+ // Available debug address registers: dr0, dr1, dr2, dr3
+ return 4;
+}
+
+#endif // defined(__x86_64__)
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h
new file mode 100644
index 000000000000..0fed16542a95
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h
@@ -0,0 +1,97 @@
+//===-- NativeRegisterContextNetBSD_x86_64.h --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__x86_64__)
+
+#ifndef lldb_NativeRegisterContextNetBSD_x86_64_h
+#define lldb_NativeRegisterContextNetBSD_x86_64_h
+
+// clang-format off
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <machine/reg.h>
+// clang-format on
+
+#include "Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h"
+#include "Plugins/Process/Utility/RegisterContext_x86.h"
+#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
+
+#if defined(PT_GETXSTATE) && defined(PT_SETXSTATE)
+#define HAVE_XSTATE
+#endif
+
+namespace lldb_private {
+namespace process_netbsd {
+
+class NativeProcessNetBSD;
+
+class NativeRegisterContextNetBSD_x86_64 : public NativeRegisterContextNetBSD {
+public:
+ NativeRegisterContextNetBSD_x86_64(const ArchSpec &target_arch,
+ NativeThreadProtocol &native_thread);
+ uint32_t GetRegisterSetCount() const override;
+
+ const RegisterSet *GetRegisterSet(uint32_t set_index) const override;
+
+ Status ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) override;
+
+ Status WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) override;
+
+ Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override;
+
+ Status GetWatchpointHitIndex(uint32_t &wp_index,
+ lldb::addr_t trap_addr) override;
+
+ Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override;
+
+ bool ClearHardwareWatchpoint(uint32_t wp_index) override;
+
+ Status ClearAllHardwareWatchpoints() override;
+
+ Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags,
+ uint32_t wp_index);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags) override;
+
+ lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override;
+
+ uint32_t NumSupportedHardwareWatchpoints() override;
+
+private:
+ // Private member types.
+ enum { GPRegSet, FPRegSet, DBRegSet, XStateRegSet };
+
+ // Private member variables.
+ struct reg m_gpr_x86_64;
+ struct fpreg m_fpr_x86_64;
+ struct dbreg m_dbr_x86_64;
+#ifdef HAVE_XSTATE
+ struct xstate m_xstate_x86_64;
+#endif
+
+ int GetSetForNativeRegNum(int reg_num) const;
+
+ Status ReadRegisterSet(uint32_t set);
+ Status WriteRegisterSet(uint32_t set);
+};
+
+} // namespace process_netbsd
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextNetBSD_x86_64_h
+
+#endif // defined(__x86_64__)
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp
new file mode 100644
index 000000000000..e25975c93446
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp
@@ -0,0 +1,205 @@
+//===-- NativeThreadNetBSD.cpp -------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeThreadNetBSD.h"
+#include "NativeRegisterContextNetBSD.h"
+
+#include "NativeProcessNetBSD.h"
+
+#include "Plugins/Process/POSIX/CrashReason.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/State.h"
+
+#include <sstream>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_netbsd;
+
+NativeThreadNetBSD::NativeThreadNetBSD(NativeProcessNetBSD &process,
+ lldb::tid_t tid)
+ : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid),
+ m_stop_info(), m_reg_context_up(
+NativeRegisterContextNetBSD::CreateHostNativeRegisterContextNetBSD(process.GetArchitecture(), *this)
+), m_stop_description() {}
+
+void NativeThreadNetBSD::SetStoppedBySignal(uint32_t signo,
+ const siginfo_t *info) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ LLDB_LOG(log, "tid = {0} in called with signal {1}", GetID(), signo);
+
+ SetStopped();
+
+ m_stop_info.reason = StopReason::eStopReasonSignal;
+ m_stop_info.details.signal.signo = signo;
+
+ m_stop_description.clear();
+ if (info) {
+ switch (signo) {
+ case SIGSEGV:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ const auto reason = GetCrashReason(*info);
+ m_stop_description = GetCrashReasonString(reason, *info);
+ break;
+ }
+ }
+}
+
+void NativeThreadNetBSD::SetStoppedByBreakpoint() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonBreakpoint;
+ m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void NativeThreadNetBSD::SetStoppedByTrace() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonTrace;
+ m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void NativeThreadNetBSD::SetStoppedByExec() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonExec;
+ m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void NativeThreadNetBSD::SetStoppedByWatchpoint(uint32_t wp_index) {
+ SetStopped();
+
+ lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid");
+
+ std::ostringstream ostr;
+ ostr << GetRegisterContext().GetWatchpointAddress(wp_index) << " ";
+ ostr << wp_index;
+
+ ostr << " " << GetRegisterContext().GetWatchpointHitAddress(wp_index);
+
+ m_stop_description = ostr.str();
+
+ m_stop_info.reason = StopReason::eStopReasonWatchpoint;
+ m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void NativeThreadNetBSD::SetStopped() {
+ const StateType new_state = StateType::eStateStopped;
+ m_state = new_state;
+ m_stop_description.clear();
+}
+
+void NativeThreadNetBSD::SetRunning() {
+ m_state = StateType::eStateRunning;
+ m_stop_info.reason = StopReason::eStopReasonNone;
+}
+
+void NativeThreadNetBSD::SetStepping() {
+ m_state = StateType::eStateStepping;
+ m_stop_info.reason = StopReason::eStopReasonNone;
+}
+
+std::string NativeThreadNetBSD::GetName() { return std::string(""); }
+
+lldb::StateType NativeThreadNetBSD::GetState() { return m_state; }
+
+bool NativeThreadNetBSD::GetStopReason(ThreadStopInfo &stop_info,
+ std::string &description) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+
+ description.clear();
+
+ switch (m_state) {
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateExited:
+ case eStateSuspended:
+ case eStateUnloaded:
+ stop_info = m_stop_info;
+ description = m_stop_description;
+
+ return true;
+
+ case eStateInvalid:
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateDetached:
+ LLDB_LOG(log, "tid = {0} in state {1} cannot answer stop reason", GetID(),
+ StateAsCString(m_state));
+ return false;
+ }
+ llvm_unreachable("unhandled StateType!");
+}
+
+NativeRegisterContext& NativeThreadNetBSD::GetRegisterContext() {
+ assert(m_reg_context_up);
+return *m_reg_context_up;
+}
+
+Status NativeThreadNetBSD::SetWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags, bool hardware) {
+ if (!hardware)
+ return Status("not implemented");
+ if (m_state == eStateLaunching)
+ return Status();
+ Status error = RemoveWatchpoint(addr);
+ if (error.Fail())
+ return error;
+ uint32_t wp_index = GetRegisterContext().SetHardwareWatchpoint(addr, size, watch_flags);
+ if (wp_index == LLDB_INVALID_INDEX32)
+ return Status("Setting hardware watchpoint failed.");
+ m_watchpoint_index_map.insert({addr, wp_index});
+ return Status();
+}
+
+Status NativeThreadNetBSD::RemoveWatchpoint(lldb::addr_t addr) {
+ auto wp = m_watchpoint_index_map.find(addr);
+ if (wp == m_watchpoint_index_map.end())
+ return Status();
+ uint32_t wp_index = wp->second;
+ m_watchpoint_index_map.erase(wp);
+ if (GetRegisterContext().ClearHardwareWatchpoint(wp_index))
+ return Status();
+ return Status("Clearing hardware watchpoint failed.");
+}
+
+Status NativeThreadNetBSD::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ if (m_state == eStateLaunching)
+ return Status();
+
+ Status error = RemoveHardwareBreakpoint(addr);
+ if (error.Fail())
+ return error;
+
+ uint32_t bp_index = GetRegisterContext().SetHardwareBreakpoint(addr, size);
+
+ if (bp_index == LLDB_INVALID_INDEX32)
+ return Status("Setting hardware breakpoint failed.");
+
+ m_hw_break_index_map.insert({addr, bp_index});
+ return Status();
+}
+
+Status NativeThreadNetBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+ auto bp = m_hw_break_index_map.find(addr);
+ if (bp == m_hw_break_index_map.end())
+ return Status();
+
+ uint32_t bp_index = bp->second;
+ if (GetRegisterContext().ClearHardwareBreakpoint(bp_index)) {
+ m_hw_break_index_map.erase(bp);
+ return Status();
+ }
+
+ return Status("Clearing hardware breakpoint failed.");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h
new file mode 100644
index 000000000000..015d8995db34
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h
@@ -0,0 +1,74 @@
+//===-- NativeThreadNetBSD.h ---------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NativeThreadNetBSD_H_
+#define liblldb_NativeThreadNetBSD_H_
+
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include <csignal>
+#include <map>
+#include <string>
+
+namespace lldb_private {
+namespace process_netbsd {
+
+class NativeProcessNetBSD;
+
+class NativeThreadNetBSD : public NativeThreadProtocol {
+ friend class NativeProcessNetBSD;
+
+public:
+ NativeThreadNetBSD(NativeProcessNetBSD &process, lldb::tid_t tid);
+
+ // NativeThreadProtocol Interface
+ std::string GetName() override;
+
+ lldb::StateType GetState() override;
+
+ bool GetStopReason(ThreadStopInfo &stop_info,
+ std::string &description) override;
+
+ NativeRegisterContext& GetRegisterContext() override;
+
+ Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags,
+ bool hardware) override;
+
+ Status RemoveWatchpoint(lldb::addr_t addr) override;
+
+ Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+
+ Status RemoveHardwareBreakpoint(lldb::addr_t addr) override;
+
+private:
+ // Interface for friend classes
+
+ void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr);
+ void SetStoppedByBreakpoint();
+ void SetStoppedByTrace();
+ void SetStoppedByExec();
+ void SetStoppedByWatchpoint(uint32_t wp_index);
+ void SetStopped();
+ void SetRunning();
+ void SetStepping();
+
+ // Member Variables
+ lldb::StateType m_state;
+ ThreadStopInfo m_stop_info;
+ std::unique_ptr<NativeRegisterContext> m_reg_context_up;
+ std::string m_stop_description;
+ using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>;
+ WatchpointIndexMap m_watchpoint_index_map;
+ WatchpointIndexMap m_hw_break_index_map;
+};
+
+typedef std::shared_ptr<NativeThreadNetBSD> NativeThreadNetBSDSP;
+} // namespace process_netbsd
+} // namespace lldb_private
+
+#endif // #ifndef liblldb_NativeThreadNetBSD_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.cpp
new file mode 100644
index 000000000000..70c2687e3b8c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.cpp
@@ -0,0 +1,337 @@
+//===-- CrashReason.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CrashReason.h"
+
+#include "llvm/Support/raw_ostream.h"
+
+#include <sstream>
+
+namespace {
+
+void AppendFaultAddr(std::string &str, lldb::addr_t addr) {
+ std::stringstream ss;
+ ss << " (fault address: 0x" << std::hex << addr << ")";
+ str += ss.str();
+}
+
+#if defined(si_lower) && defined(si_upper)
+void AppendBounds(std::string &str, lldb::addr_t lower_bound,
+ lldb::addr_t upper_bound, lldb::addr_t addr) {
+ llvm::raw_string_ostream stream(str);
+ if ((unsigned long)addr < lower_bound)
+ stream << ": lower bound violation ";
+ else
+ stream << ": upper bound violation ";
+ stream << "(fault address: 0x";
+ stream.write_hex(addr);
+ stream << ", lower bound: 0x";
+ stream.write_hex(lower_bound);
+ stream << ", upper bound: 0x";
+ stream.write_hex(upper_bound);
+ stream << ")";
+ stream.flush();
+}
+#endif
+
+CrashReason GetCrashReasonForSIGSEGV(const siginfo_t &info) {
+ assert(info.si_signo == SIGSEGV);
+
+ switch (info.si_code) {
+#ifdef SI_KERNEL
+ case SI_KERNEL:
+ // Some platforms will occasionally send nonstandard spurious SI_KERNEL
+ // codes. One way to get this is via unaligned SIMD loads.
+ return CrashReason::eInvalidAddress; // for lack of anything better
+#endif
+ case SEGV_MAPERR:
+ return CrashReason::eInvalidAddress;
+ case SEGV_ACCERR:
+ return CrashReason::ePrivilegedAddress;
+#ifndef SEGV_BNDERR
+#define SEGV_BNDERR 3
+#endif
+ case SEGV_BNDERR:
+ return CrashReason::eBoundViolation;
+ }
+
+ return CrashReason::eInvalidCrashReason;
+}
+
+CrashReason GetCrashReasonForSIGILL(const siginfo_t &info) {
+ assert(info.si_signo == SIGILL);
+
+ switch (info.si_code) {
+ case ILL_ILLOPC:
+ return CrashReason::eIllegalOpcode;
+ case ILL_ILLOPN:
+ return CrashReason::eIllegalOperand;
+ case ILL_ILLADR:
+ return CrashReason::eIllegalAddressingMode;
+ case ILL_ILLTRP:
+ return CrashReason::eIllegalTrap;
+ case ILL_PRVOPC:
+ return CrashReason::ePrivilegedOpcode;
+ case ILL_PRVREG:
+ return CrashReason::ePrivilegedRegister;
+ case ILL_COPROC:
+ return CrashReason::eCoprocessorError;
+ case ILL_BADSTK:
+ return CrashReason::eInternalStackError;
+ }
+
+ return CrashReason::eInvalidCrashReason;
+}
+
+CrashReason GetCrashReasonForSIGFPE(const siginfo_t &info) {
+ assert(info.si_signo == SIGFPE);
+
+ switch (info.si_code) {
+ case FPE_INTDIV:
+ return CrashReason::eIntegerDivideByZero;
+ case FPE_INTOVF:
+ return CrashReason::eIntegerOverflow;
+ case FPE_FLTDIV:
+ return CrashReason::eFloatDivideByZero;
+ case FPE_FLTOVF:
+ return CrashReason::eFloatOverflow;
+ case FPE_FLTUND:
+ return CrashReason::eFloatUnderflow;
+ case FPE_FLTRES:
+ return CrashReason::eFloatInexactResult;
+ case FPE_FLTINV:
+ return CrashReason::eFloatInvalidOperation;
+ case FPE_FLTSUB:
+ return CrashReason::eFloatSubscriptRange;
+ }
+
+ return CrashReason::eInvalidCrashReason;
+}
+
+CrashReason GetCrashReasonForSIGBUS(const siginfo_t &info) {
+ assert(info.si_signo == SIGBUS);
+
+ switch (info.si_code) {
+ case BUS_ADRALN:
+ return CrashReason::eIllegalAlignment;
+ case BUS_ADRERR:
+ return CrashReason::eIllegalAddress;
+ case BUS_OBJERR:
+ return CrashReason::eHardwareError;
+ }
+
+ return CrashReason::eInvalidCrashReason;
+}
+}
+
+std::string GetCrashReasonString(CrashReason reason, const siginfo_t &info) {
+ std::string str;
+
+// make sure that siginfo_t has the bound fields available.
+#if defined(si_lower) && defined(si_upper)
+ if (reason == CrashReason::eBoundViolation) {
+ str = "signal SIGSEGV";
+ AppendBounds(str, reinterpret_cast<lldb::addr_t>(info.si_lower),
+ reinterpret_cast<lldb::addr_t>(info.si_upper),
+ reinterpret_cast<lldb::addr_t>(info.si_addr));
+ return str;
+ }
+#endif
+
+ return GetCrashReasonString(reason,
+ reinterpret_cast<lldb::addr_t>(info.si_addr));
+}
+
+std::string GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr) {
+ std::string str;
+
+ switch (reason) {
+ default:
+ str = "unknown crash reason";
+ break;
+
+ case CrashReason::eInvalidAddress:
+ str = "signal SIGSEGV: invalid address";
+ AppendFaultAddr(str, fault_addr);
+ break;
+ case CrashReason::ePrivilegedAddress:
+ str = "signal SIGSEGV: address access protected";
+ AppendFaultAddr(str, fault_addr);
+ break;
+ case CrashReason::eBoundViolation:
+ str = "signal SIGSEGV: bound violation";
+ break;
+ case CrashReason::eIllegalOpcode:
+ str = "signal SIGILL: illegal instruction";
+ break;
+ case CrashReason::eIllegalOperand:
+ str = "signal SIGILL: illegal instruction operand";
+ break;
+ case CrashReason::eIllegalAddressingMode:
+ str = "signal SIGILL: illegal addressing mode";
+ break;
+ case CrashReason::eIllegalTrap:
+ str = "signal SIGILL: illegal trap";
+ break;
+ case CrashReason::ePrivilegedOpcode:
+ str = "signal SIGILL: privileged instruction";
+ break;
+ case CrashReason::ePrivilegedRegister:
+ str = "signal SIGILL: privileged register";
+ break;
+ case CrashReason::eCoprocessorError:
+ str = "signal SIGILL: coprocessor error";
+ break;
+ case CrashReason::eInternalStackError:
+ str = "signal SIGILL: internal stack error";
+ break;
+ case CrashReason::eIllegalAlignment:
+ str = "signal SIGBUS: illegal alignment";
+ break;
+ case CrashReason::eIllegalAddress:
+ str = "signal SIGBUS: illegal address";
+ break;
+ case CrashReason::eHardwareError:
+ str = "signal SIGBUS: hardware error";
+ break;
+ case CrashReason::eIntegerDivideByZero:
+ str = "signal SIGFPE: integer divide by zero";
+ break;
+ case CrashReason::eIntegerOverflow:
+ str = "signal SIGFPE: integer overflow";
+ break;
+ case CrashReason::eFloatDivideByZero:
+ str = "signal SIGFPE: floating point divide by zero";
+ break;
+ case CrashReason::eFloatOverflow:
+ str = "signal SIGFPE: floating point overflow";
+ break;
+ case CrashReason::eFloatUnderflow:
+ str = "signal SIGFPE: floating point underflow";
+ break;
+ case CrashReason::eFloatInexactResult:
+ str = "signal SIGFPE: inexact floating point result";
+ break;
+ case CrashReason::eFloatInvalidOperation:
+ str = "signal SIGFPE: invalid floating point operation";
+ break;
+ case CrashReason::eFloatSubscriptRange:
+ str = "signal SIGFPE: invalid floating point subscript range";
+ break;
+ }
+
+ return str;
+}
+
+const char *CrashReasonAsString(CrashReason reason) {
+#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION
+ // Just return the code in ascii for integration builds.
+ chcar str[8];
+ sprintf(str, "%d", reason);
+#else
+ const char *str = nullptr;
+
+ switch (reason) {
+ case CrashReason::eInvalidCrashReason:
+ str = "eInvalidCrashReason";
+ break;
+
+ // SIGSEGV crash reasons.
+ case CrashReason::eInvalidAddress:
+ str = "eInvalidAddress";
+ break;
+ case CrashReason::ePrivilegedAddress:
+ str = "ePrivilegedAddress";
+ break;
+ case CrashReason::eBoundViolation:
+ str = "eBoundViolation";
+ break;
+
+ // SIGILL crash reasons.
+ case CrashReason::eIllegalOpcode:
+ str = "eIllegalOpcode";
+ break;
+ case CrashReason::eIllegalOperand:
+ str = "eIllegalOperand";
+ break;
+ case CrashReason::eIllegalAddressingMode:
+ str = "eIllegalAddressingMode";
+ break;
+ case CrashReason::eIllegalTrap:
+ str = "eIllegalTrap";
+ break;
+ case CrashReason::ePrivilegedOpcode:
+ str = "ePrivilegedOpcode";
+ break;
+ case CrashReason::ePrivilegedRegister:
+ str = "ePrivilegedRegister";
+ break;
+ case CrashReason::eCoprocessorError:
+ str = "eCoprocessorError";
+ break;
+ case CrashReason::eInternalStackError:
+ str = "eInternalStackError";
+ break;
+
+ // SIGBUS crash reasons:
+ case CrashReason::eIllegalAlignment:
+ str = "eIllegalAlignment";
+ break;
+ case CrashReason::eIllegalAddress:
+ str = "eIllegalAddress";
+ break;
+ case CrashReason::eHardwareError:
+ str = "eHardwareError";
+ break;
+
+ // SIGFPE crash reasons:
+ case CrashReason::eIntegerDivideByZero:
+ str = "eIntegerDivideByZero";
+ break;
+ case CrashReason::eIntegerOverflow:
+ str = "eIntegerOverflow";
+ break;
+ case CrashReason::eFloatDivideByZero:
+ str = "eFloatDivideByZero";
+ break;
+ case CrashReason::eFloatOverflow:
+ str = "eFloatOverflow";
+ break;
+ case CrashReason::eFloatUnderflow:
+ str = "eFloatUnderflow";
+ break;
+ case CrashReason::eFloatInexactResult:
+ str = "eFloatInexactResult";
+ break;
+ case CrashReason::eFloatInvalidOperation:
+ str = "eFloatInvalidOperation";
+ break;
+ case CrashReason::eFloatSubscriptRange:
+ str = "eFloatSubscriptRange";
+ break;
+ }
+#endif
+
+ return str;
+}
+
+CrashReason GetCrashReason(const siginfo_t &info) {
+ switch (info.si_signo) {
+ case SIGSEGV:
+ return GetCrashReasonForSIGSEGV(info);
+ case SIGBUS:
+ return GetCrashReasonForSIGBUS(info);
+ case SIGFPE:
+ return GetCrashReasonForSIGFPE(info);
+ case SIGILL:
+ return GetCrashReasonForSIGILL(info);
+ }
+
+ assert(false && "unexpected signal");
+ return CrashReason::eInvalidCrashReason;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.h b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.h
new file mode 100644
index 000000000000..9b4784a1e68e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.h
@@ -0,0 +1,59 @@
+//===-- CrashReason.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CrashReason_H_
+#define liblldb_CrashReason_H_
+
+#include "lldb/lldb-types.h"
+
+#include <signal.h>
+
+#include <string>
+
+enum class CrashReason {
+ eInvalidCrashReason,
+
+ // SIGSEGV crash reasons.
+ eInvalidAddress,
+ ePrivilegedAddress,
+ eBoundViolation,
+
+ // SIGILL crash reasons.
+ eIllegalOpcode,
+ eIllegalOperand,
+ eIllegalAddressingMode,
+ eIllegalTrap,
+ ePrivilegedOpcode,
+ ePrivilegedRegister,
+ eCoprocessorError,
+ eInternalStackError,
+
+ // SIGBUS crash reasons,
+ eIllegalAlignment,
+ eIllegalAddress,
+ eHardwareError,
+
+ // SIGFPE crash reasons,
+ eIntegerDivideByZero,
+ eIntegerOverflow,
+ eFloatDivideByZero,
+ eFloatOverflow,
+ eFloatUnderflow,
+ eFloatInexactResult,
+ eFloatInvalidOperation,
+ eFloatSubscriptRange
+};
+
+std::string GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr);
+std::string GetCrashReasonString(CrashReason reason, const siginfo_t &info);
+
+const char *CrashReasonAsString(CrashReason reason);
+
+CrashReason GetCrashReason(const siginfo_t &info);
+
+#endif // #ifndef liblldb_CrashReason_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp
new file mode 100644
index 000000000000..559b16c8fd69
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp
@@ -0,0 +1,110 @@
+//===-- NativeProcessELF.cpp ---------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeProcessELF.h"
+
+#include "lldb/Utility/DataExtractor.h"
+
+namespace lldb_private {
+
+llvm::Optional<uint64_t>
+NativeProcessELF::GetAuxValue(enum AuxVector::EntryType type) {
+ if (m_aux_vector == nullptr) {
+ auto buffer_or_error = GetAuxvData();
+ if (!buffer_or_error)
+ return llvm::None;
+ DataExtractor auxv_data(buffer_or_error.get()->getBufferStart(),
+ buffer_or_error.get()->getBufferSize(),
+ GetByteOrder(), GetAddressByteSize());
+ m_aux_vector = llvm::make_unique<AuxVector>(auxv_data);
+ }
+
+ return m_aux_vector->GetAuxValue(type);
+}
+
+lldb::addr_t NativeProcessELF::GetSharedLibraryInfoAddress() {
+ if (!m_shared_library_info_addr.hasValue()) {
+ if (GetAddressByteSize() == 8)
+ m_shared_library_info_addr =
+ GetELFImageInfoAddress<llvm::ELF::Elf64_Ehdr, llvm::ELF::Elf64_Phdr,
+ llvm::ELF::Elf64_Dyn>();
+ else
+ m_shared_library_info_addr =
+ GetELFImageInfoAddress<llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr,
+ llvm::ELF::Elf32_Dyn>();
+ }
+
+ return m_shared_library_info_addr.getValue();
+}
+
+template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN>
+lldb::addr_t NativeProcessELF::GetELFImageInfoAddress() {
+ llvm::Optional<uint64_t> maybe_phdr_addr =
+ GetAuxValue(AuxVector::AUXV_AT_PHDR);
+ llvm::Optional<uint64_t> maybe_phdr_entry_size =
+ GetAuxValue(AuxVector::AUXV_AT_PHENT);
+ llvm::Optional<uint64_t> maybe_phdr_num_entries =
+ GetAuxValue(AuxVector::AUXV_AT_PHNUM);
+ if (!maybe_phdr_addr || !maybe_phdr_entry_size || !maybe_phdr_num_entries)
+ return LLDB_INVALID_ADDRESS;
+ lldb::addr_t phdr_addr = *maybe_phdr_addr;
+ size_t phdr_entry_size = *maybe_phdr_entry_size;
+ size_t phdr_num_entries = *maybe_phdr_num_entries;
+
+ // Find the PT_DYNAMIC segment (.dynamic section) in the program header and
+ // what the load bias by calculating the difference of the program header
+ // load address and its virtual address.
+ lldb::offset_t load_bias;
+ bool found_load_bias = false;
+ lldb::addr_t dynamic_section_addr = 0;
+ uint64_t dynamic_section_size = 0;
+ bool found_dynamic_section = false;
+ ELF_PHDR phdr_entry;
+ for (size_t i = 0; i < phdr_num_entries; i++) {
+ size_t bytes_read;
+ auto error = ReadMemory(phdr_addr + i * phdr_entry_size, &phdr_entry,
+ sizeof(phdr_entry), bytes_read);
+ if (!error.Success())
+ return LLDB_INVALID_ADDRESS;
+ if (phdr_entry.p_type == llvm::ELF::PT_PHDR) {
+ load_bias = phdr_addr - phdr_entry.p_vaddr;
+ found_load_bias = true;
+ }
+
+ if (phdr_entry.p_type == llvm::ELF::PT_DYNAMIC) {
+ dynamic_section_addr = phdr_entry.p_vaddr;
+ dynamic_section_size = phdr_entry.p_memsz;
+ found_dynamic_section = true;
+ }
+ }
+
+ if (!found_load_bias || !found_dynamic_section)
+ return LLDB_INVALID_ADDRESS;
+
+ // Find the DT_DEBUG entry in the .dynamic section
+ dynamic_section_addr += load_bias;
+ ELF_DYN dynamic_entry;
+ size_t dynamic_num_entries = dynamic_section_size / sizeof(dynamic_entry);
+ for (size_t i = 0; i < dynamic_num_entries; i++) {
+ size_t bytes_read;
+ auto error = ReadMemory(dynamic_section_addr + i * sizeof(dynamic_entry),
+ &dynamic_entry, sizeof(dynamic_entry), bytes_read);
+ if (!error.Success())
+ return LLDB_INVALID_ADDRESS;
+ // Return the &DT_DEBUG->d_ptr which points to r_debug which contains the
+ // link_map.
+ if (dynamic_entry.d_tag == llvm::ELF::DT_DEBUG) {
+ return dynamic_section_addr + i * sizeof(dynamic_entry) +
+ sizeof(dynamic_entry.d_tag);
+ }
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+} // namespace lldb_private \ No newline at end of file
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h
new file mode 100644
index 000000000000..84dc8d08a340
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h
@@ -0,0 +1,46 @@
+//===-- NativeProcessELF.h ------------------------------------ -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NativeProcessELF_H_
+#define liblldb_NativeProcessELF_H_
+
+#include "Plugins/Process/Utility/AuxVector.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "llvm/BinaryFormat/ELF.h"
+
+namespace lldb_private {
+
+/// \class NativeProcessELF
+/// Abstract class that extends \a NativeProcessProtocol with ELF specific
+/// logic. Meant to be subclassed by ELF based NativeProcess* implementations.
+class NativeProcessELF : public NativeProcessProtocol {
+ using NativeProcessProtocol::NativeProcessProtocol;
+
+protected:
+ template <typename T> struct ELFLinkMap {
+ T l_addr;
+ T l_name;
+ T l_ld;
+ T l_next;
+ T l_prev;
+ };
+
+ llvm::Optional<uint64_t> GetAuxValue(enum AuxVector::EntryType type);
+
+ lldb::addr_t GetSharedLibraryInfoAddress() override;
+
+ template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN>
+ lldb::addr_t GetELFImageInfoAddress();
+
+ std::unique_ptr<AuxVector> m_aux_vector;
+ llvm::Optional<lldb::addr_t> m_shared_library_info_addr;
+};
+
+} // namespace lldb_private
+
+#endif \ No newline at end of file
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp
new file mode 100644
index 000000000000..aa8449131a68
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp
@@ -0,0 +1,68 @@
+//===-- ProcessMessage.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProcessMessage.h"
+
+using namespace lldb_private;
+
+const char *ProcessMessage::PrintCrashReason() const {
+ return CrashReasonAsString(m_crash_reason);
+}
+
+const char *ProcessMessage::PrintKind(Kind kind) {
+#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION
+ // Just return the code in ascii for integration builds.
+ chcar str[8];
+ sprintf(str, "%d", reason);
+#else
+ const char *str = nullptr;
+
+ switch (kind) {
+ case eInvalidMessage:
+ str = "eInvalidMessage";
+ break;
+ case eAttachMessage:
+ str = "eAttachMessage";
+ break;
+ case eExitMessage:
+ str = "eExitMessage";
+ break;
+ case eLimboMessage:
+ str = "eLimboMessage";
+ break;
+ case eSignalMessage:
+ str = "eSignalMessage";
+ break;
+ case eSignalDeliveredMessage:
+ str = "eSignalDeliveredMessage";
+ break;
+ case eTraceMessage:
+ str = "eTraceMessage";
+ break;
+ case eBreakpointMessage:
+ str = "eBreakpointMessage";
+ break;
+ case eWatchpointMessage:
+ str = "eWatchpointMessage";
+ break;
+ case eCrashMessage:
+ str = "eCrashMessage";
+ break;
+ case eNewThreadMessage:
+ str = "eNewThreadMessage";
+ break;
+ case eExecMessage:
+ str = "eExecMessage";
+ break;
+ }
+#endif
+
+ return str;
+}
+
+const char *ProcessMessage::PrintKind() const { return PrintKind(m_kind); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.h b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.h
new file mode 100644
index 000000000000..d9c10caaa95e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.h
@@ -0,0 +1,168 @@
+//===-- ProcessMessage.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessMessage_H_
+#define liblldb_ProcessMessage_H_
+
+#include "CrashReason.h"
+
+#include <cassert>
+#include <string>
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+class ProcessMessage {
+public:
+ /// The type of signal this message can correspond to.
+ enum Kind {
+ eInvalidMessage,
+ eAttachMessage,
+ eExitMessage,
+ eLimboMessage,
+ eSignalMessage,
+ eSignalDeliveredMessage,
+ eTraceMessage,
+ eBreakpointMessage,
+ eWatchpointMessage,
+ eCrashMessage,
+ eNewThreadMessage,
+ eExecMessage
+ };
+
+ ProcessMessage()
+ : m_tid(LLDB_INVALID_PROCESS_ID), m_kind(eInvalidMessage),
+ m_crash_reason(CrashReason::eInvalidCrashReason), m_status(0),
+ m_addr(0) {}
+
+ Kind GetKind() const { return m_kind; }
+
+ lldb::tid_t GetTID() const { return m_tid; }
+
+ /// Indicates that the process \p pid has successfully attached.
+ static ProcessMessage Attach(lldb::pid_t pid) {
+ return ProcessMessage(pid, eAttachMessage);
+ }
+
+ /// Indicates that the thread \p tid is about to exit with status \p status.
+ static ProcessMessage Limbo(lldb::tid_t tid, int status) {
+ return ProcessMessage(tid, eLimboMessage, status);
+ }
+
+ /// Indicates that the thread \p tid had the signal \p signum delivered.
+ static ProcessMessage Signal(lldb::tid_t tid, int signum) {
+ return ProcessMessage(tid, eSignalMessage, signum);
+ }
+
+ /// Indicates that a signal \p signum generated by the debugging process was
+ /// delivered to the thread \p tid.
+ static ProcessMessage SignalDelivered(lldb::tid_t tid, int signum) {
+ return ProcessMessage(tid, eSignalDeliveredMessage, signum);
+ }
+
+ /// Indicates that the thread \p tid encountered a trace point.
+ static ProcessMessage Trace(lldb::tid_t tid) {
+ return ProcessMessage(tid, eTraceMessage);
+ }
+
+ /// Indicates that the thread \p tid encountered a break point.
+ static ProcessMessage Break(lldb::tid_t tid) {
+ return ProcessMessage(tid, eBreakpointMessage);
+ }
+
+ static ProcessMessage Watch(lldb::tid_t tid, lldb::addr_t wp_addr) {
+ return ProcessMessage(tid, eWatchpointMessage, 0, wp_addr);
+ }
+
+ /// Indicates that the thread \p tid crashed.
+ static ProcessMessage Crash(lldb::pid_t pid, CrashReason reason, int signo,
+ lldb::addr_t fault_addr) {
+ ProcessMessage message(pid, eCrashMessage, signo, fault_addr);
+ message.m_crash_reason = reason;
+ return message;
+ }
+
+ /// Indicates that the thread \p child_tid was spawned.
+ static ProcessMessage NewThread(lldb::tid_t parent_tid,
+ lldb::tid_t child_tid) {
+ return ProcessMessage(parent_tid, eNewThreadMessage, child_tid);
+ }
+
+ /// Indicates that the thread \p tid is about to exit with status \p status.
+ static ProcessMessage Exit(lldb::tid_t tid, int status) {
+ return ProcessMessage(tid, eExitMessage, status);
+ }
+
+ /// Indicates that the thread \p pid has exec'd.
+ static ProcessMessage Exec(lldb::tid_t tid) {
+ return ProcessMessage(tid, eExecMessage);
+ }
+
+ int GetExitStatus() const {
+ assert(GetKind() == eExitMessage || GetKind() == eLimboMessage);
+ return m_status;
+ }
+
+ int GetSignal() const {
+ assert(GetKind() == eSignalMessage || GetKind() == eCrashMessage ||
+ GetKind() == eSignalDeliveredMessage);
+ return m_status;
+ }
+
+ int GetStopStatus() const {
+ assert(GetKind() == eSignalMessage);
+ return m_status;
+ }
+
+ CrashReason GetCrashReason() const {
+ assert(GetKind() == eCrashMessage);
+ return m_crash_reason;
+ }
+
+ lldb::addr_t GetFaultAddress() const {
+ assert(GetKind() == eCrashMessage);
+ return m_addr;
+ }
+
+ lldb::addr_t GetHWAddress() const {
+ assert(GetKind() == eWatchpointMessage || GetKind() == eTraceMessage);
+ return m_addr;
+ }
+
+ lldb::tid_t GetChildTID() const {
+ assert(GetKind() == eNewThreadMessage);
+ return m_child_tid;
+ }
+
+ const char *PrintCrashReason() const;
+
+ const char *PrintKind() const;
+
+ static const char *PrintKind(Kind);
+
+private:
+ ProcessMessage(lldb::tid_t tid, Kind kind, int status = 0,
+ lldb::addr_t addr = 0)
+ : m_tid(tid), m_kind(kind),
+ m_crash_reason(CrashReason::eInvalidCrashReason), m_status(status),
+ m_addr(addr), m_child_tid(0) {}
+
+ ProcessMessage(lldb::tid_t tid, Kind kind, lldb::tid_t child_tid)
+ : m_tid(tid), m_kind(kind),
+ m_crash_reason(CrashReason::eInvalidCrashReason), m_status(0),
+ m_addr(0), m_child_tid(child_tid) {}
+
+ lldb::tid_t m_tid;
+ Kind m_kind : 8;
+ CrashReason m_crash_reason;
+ int m_status;
+ lldb::addr_t m_addr;
+ lldb::tid_t m_child_tid;
+};
+
+#endif // #ifndef liblldb_ProcessMessage_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp
new file mode 100644
index 000000000000..a17558bfe7c6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp
@@ -0,0 +1,31 @@
+//===-- ProcessPOSIXLog.cpp ---------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProcessPOSIXLog.h"
+
+#include "llvm/Support/Threading.h"
+
+using namespace lldb_private;
+
+static constexpr Log::Category g_categories[] = {
+ {{"break"}, {"log breakpoints"}, POSIX_LOG_BREAKPOINTS},
+ {{"memory"}, {"log memory reads and writes"}, POSIX_LOG_MEMORY},
+ {{"process"}, {"log process events and activities"}, POSIX_LOG_PROCESS},
+ {{"ptrace"}, {"log all calls to ptrace"}, POSIX_LOG_PTRACE},
+ {{"registers"}, {"log register read/writes"}, POSIX_LOG_REGISTERS},
+ {{"thread"}, {"log thread events and activities"}, POSIX_LOG_THREAD},
+ {{"watch"}, {"log watchpoint related activities"}, POSIX_LOG_WATCHPOINTS},
+};
+
+Log::Channel ProcessPOSIXLog::g_channel(g_categories, POSIX_LOG_DEFAULT);
+
+void ProcessPOSIXLog::Initialize() {
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() { Log::Register("posix", g_channel); });
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h
new file mode 100644
index 000000000000..c0147c43410f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h
@@ -0,0 +1,39 @@
+//===-- ProcessPOSIXLog.h -----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessPOSIXLog_h_
+#define liblldb_ProcessPOSIXLog_h_
+
+
+#include "lldb/Utility/Log.h"
+
+#define POSIX_LOG_PROCESS (1u << 1)
+#define POSIX_LOG_THREAD (1u << 2)
+#define POSIX_LOG_MEMORY (1u << 4) // Log memory reads/writes calls
+#define POSIX_LOG_PTRACE (1u << 5)
+#define POSIX_LOG_REGISTERS (1u << 6)
+#define POSIX_LOG_BREAKPOINTS (1u << 7)
+#define POSIX_LOG_WATCHPOINTS (1u << 8)
+#define POSIX_LOG_ALL (UINT32_MAX)
+#define POSIX_LOG_DEFAULT POSIX_LOG_PROCESS
+
+namespace lldb_private {
+class ProcessPOSIXLog {
+ static Log::Channel g_channel;
+
+public:
+ static void Initialize();
+
+ static Log *GetLogIfAllCategoriesSet(uint32_t mask) {
+ return g_channel.GetLogIfAll(mask);
+ }
+};
+}
+
+#endif // liblldb_ProcessPOSIXLog_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMDefines.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMDefines.h
new file mode 100644
index 000000000000..1f7eb54d10e7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMDefines.h
@@ -0,0 +1,191 @@
+//===-- ARMDefines.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_ARMDefines_h_
+#define lldb_ARMDefines_h_
+
+#include "llvm/Support/ErrorHandling.h"
+
+#include <cassert>
+#include <cstdint>
+
+// Common definitions for the ARM/Thumb Instruction Set Architecture.
+
+namespace lldb_private {
+
+// ARM shifter types
+enum ARM_ShifterType {
+ SRType_LSL,
+ SRType_LSR,
+ SRType_ASR,
+ SRType_ROR,
+ SRType_RRX,
+ SRType_Invalid
+};
+
+// ARM conditions // Meaning (integer) Meaning (floating-point)
+// Condition flags
+#define COND_EQ \
+ 0x0 // Equal Equal Z == 1
+#define COND_NE \
+ 0x1 // Not equal Not equal, or unordered Z == 0
+#define COND_CS \
+ 0x2 // Carry set >, ==, or unordered C == 1
+#define COND_HS 0x2
+#define COND_CC \
+ 0x3 // Carry clear Less than C == 0
+#define COND_LO 0x3
+#define COND_MI \
+ 0x4 // Minus, negative Less than N == 1
+#define COND_PL \
+ 0x5 // Plus, positive or zero >, ==, or unordered N == 0
+#define COND_VS \
+ 0x6 // Overflow Unordered V == 1
+#define COND_VC \
+ 0x7 // No overflow Not unordered V == 0
+#define COND_HI \
+ 0x8 // Unsigned higher Greater than, or unordered C == 1 and Z ==
+ // 0
+#define COND_LS \
+ 0x9 // Unsigned lower or same Less than or equal C == 0 or Z ==
+ // 1
+#define COND_GE \
+ 0xA // Greater than or equal Greater than or equal N == V
+#define COND_LT \
+ 0xB // Less than Less than, or unordered N != V
+#define COND_GT \
+ 0xC // Greater than Greater than Z == 0 and N ==
+ // V
+#define COND_LE \
+ 0xD // Less than or equal <, ==, or unordered Z == 1 or N !=
+ // V
+#define COND_AL \
+ 0xE // Always (unconditional) Always (unconditional) Any
+#define COND_UNCOND 0xF
+
+static inline const char *ARMCondCodeToString(uint32_t CC) {
+ switch (CC) {
+ case COND_EQ:
+ return "eq";
+ case COND_NE:
+ return "ne";
+ case COND_HS:
+ return "hs";
+ case COND_LO:
+ return "lo";
+ case COND_MI:
+ return "mi";
+ case COND_PL:
+ return "pl";
+ case COND_VS:
+ return "vs";
+ case COND_VC:
+ return "vc";
+ case COND_HI:
+ return "hi";
+ case COND_LS:
+ return "ls";
+ case COND_GE:
+ return "ge";
+ case COND_LT:
+ return "lt";
+ case COND_GT:
+ return "gt";
+ case COND_LE:
+ return "le";
+ case COND_AL:
+ return "al";
+ }
+ llvm_unreachable("Unknown condition code");
+}
+
+static inline bool ARMConditionPassed(const uint32_t condition,
+ const uint32_t cpsr) {
+ const uint32_t cpsr_n = (cpsr >> 31) & 1u; // Negative condition code flag
+ const uint32_t cpsr_z = (cpsr >> 30) & 1u; // Zero condition code flag
+ const uint32_t cpsr_c = (cpsr >> 29) & 1u; // Carry condition code flag
+ const uint32_t cpsr_v = (cpsr >> 28) & 1u; // Overflow condition code flag
+
+ switch (condition) {
+ case COND_EQ:
+ return (cpsr_z == 1);
+ case COND_NE:
+ return (cpsr_z == 0);
+ case COND_CS:
+ return (cpsr_c == 1);
+ case COND_CC:
+ return (cpsr_c == 0);
+ case COND_MI:
+ return (cpsr_n == 1);
+ case COND_PL:
+ return (cpsr_n == 0);
+ case COND_VS:
+ return (cpsr_v == 1);
+ case COND_VC:
+ return (cpsr_v == 0);
+ case COND_HI:
+ return ((cpsr_c == 1) && (cpsr_z == 0));
+ case COND_LS:
+ return ((cpsr_c == 0) || (cpsr_z == 1));
+ case COND_GE:
+ return (cpsr_n == cpsr_v);
+ case COND_LT:
+ return (cpsr_n != cpsr_v);
+ case COND_GT:
+ return ((cpsr_z == 0) && (cpsr_n == cpsr_v));
+ case COND_LE:
+ return ((cpsr_z == 1) || (cpsr_n != cpsr_v));
+ case COND_AL:
+ case COND_UNCOND:
+ default:
+ return true;
+ }
+ return false;
+}
+
+// Bit positions for CPSR
+#define CPSR_T_POS 5
+#define CPSR_F_POS 6
+#define CPSR_I_POS 7
+#define CPSR_A_POS 8
+#define CPSR_E_POS 9
+#define CPSR_J_POS 24
+#define CPSR_Q_POS 27
+#define CPSR_V_POS 28
+#define CPSR_C_POS 29
+#define CPSR_Z_POS 30
+#define CPSR_N_POS 31
+
+// CPSR mode definitions
+#define CPSR_MODE_USR 0x10u
+#define CPSR_MODE_FIQ 0x11u
+#define CPSR_MODE_IRQ 0x12u
+#define CPSR_MODE_SVC 0x13u
+#define CPSR_MODE_ABT 0x17u
+#define CPSR_MODE_UND 0x1bu
+#define CPSR_MODE_SYS 0x1fu
+
+// Masks for CPSR
+#define MASK_CPSR_MODE_MASK (0x0000001fu)
+#define MASK_CPSR_IT_MASK (0x0600fc00u)
+#define MASK_CPSR_T (1u << CPSR_T_POS)
+#define MASK_CPSR_F (1u << CPSR_F_POS)
+#define MASK_CPSR_I (1u << CPSR_I_POS)
+#define MASK_CPSR_A (1u << CPSR_A_POS)
+#define MASK_CPSR_E (1u << CPSR_E_POS)
+#define MASK_CPSR_GE_MASK (0x000f0000u)
+#define MASK_CPSR_J (1u << CPSR_J_POS)
+#define MASK_CPSR_Q (1u << CPSR_Q_POS)
+#define MASK_CPSR_V (1u << CPSR_V_POS)
+#define MASK_CPSR_C (1u << CPSR_C_POS)
+#define MASK_CPSR_Z (1u << CPSR_Z_POS)
+#define MASK_CPSR_N (1u << CPSR_N_POS)
+
+} // namespace lldb_private
+
+#endif // lldb_ARMDefines_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMUtils.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMUtils.h
new file mode 100644
index 000000000000..d860348818d3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMUtils.h
@@ -0,0 +1,374 @@
+//===-- ARMUtils.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_ARMUtils_h_
+#define lldb_ARMUtils_h_
+
+#include "ARMDefines.h"
+#include "InstructionUtils.h"
+#include "llvm/Support/MathExtras.h"
+
+// Common utilities for the ARM/Thumb Instruction Set Architecture.
+
+namespace lldb_private {
+
+static inline uint32_t Align(uint32_t val, uint32_t alignment) {
+ return alignment * (val / alignment);
+}
+
+static inline uint32_t DecodeImmShift(const uint32_t type, const uint32_t imm5,
+ ARM_ShifterType &shift_t) {
+ switch (type) {
+ default:
+ // assert(0 && "Invalid shift type");
+ case 0:
+ shift_t = SRType_LSL;
+ return imm5;
+ case 1:
+ shift_t = SRType_LSR;
+ return (imm5 == 0 ? 32 : imm5);
+ case 2:
+ shift_t = SRType_ASR;
+ return (imm5 == 0 ? 32 : imm5);
+ case 3:
+ if (imm5 == 0) {
+ shift_t = SRType_RRX;
+ return 1;
+ } else {
+ shift_t = SRType_ROR;
+ return imm5;
+ }
+ }
+ shift_t = SRType_Invalid;
+ return UINT32_MAX;
+}
+
+// A8.6.35 CMP (register) -- Encoding T3
+// Convenience function.
+static inline uint32_t DecodeImmShiftThumb(const uint32_t opcode,
+ ARM_ShifterType &shift_t) {
+ return DecodeImmShift(Bits32(opcode, 5, 4),
+ Bits32(opcode, 14, 12) << 2 | Bits32(opcode, 7, 6),
+ shift_t);
+}
+
+// A8.6.35 CMP (register) -- Encoding A1
+// Convenience function.
+static inline uint32_t DecodeImmShiftARM(const uint32_t opcode,
+ ARM_ShifterType &shift_t) {
+ return DecodeImmShift(Bits32(opcode, 6, 5), Bits32(opcode, 11, 7), shift_t);
+}
+
+static inline uint32_t DecodeImmShift(const ARM_ShifterType shift_t,
+ const uint32_t imm5) {
+ ARM_ShifterType dont_care;
+ return DecodeImmShift(shift_t, imm5, dont_care);
+}
+
+static inline ARM_ShifterType DecodeRegShift(const uint32_t type) {
+ switch (type) {
+ default:
+ // assert(0 && "Invalid shift type");
+ return SRType_Invalid;
+ case 0:
+ return SRType_LSL;
+ case 1:
+ return SRType_LSR;
+ case 2:
+ return SRType_ASR;
+ case 3:
+ return SRType_ROR;
+ }
+}
+
+static inline uint32_t LSL_C(const uint32_t value, const uint32_t amount,
+ uint32_t &carry_out, bool *success) {
+ if (amount == 0) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ carry_out = amount <= 32 ? Bit32(value, 32 - amount) : 0;
+ return value << amount;
+}
+
+static inline uint32_t LSL(const uint32_t value, const uint32_t amount,
+ bool *success) {
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = LSL_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t LSR_C(const uint32_t value, const uint32_t amount,
+ uint32_t &carry_out, bool *success) {
+ if (amount == 0) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ carry_out = amount <= 32 ? Bit32(value, amount - 1) : 0;
+ return value >> amount;
+}
+
+static inline uint32_t LSR(const uint32_t value, const uint32_t amount,
+ bool *success) {
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = LSR_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t ASR_C(const uint32_t value, const uint32_t amount,
+ uint32_t &carry_out, bool *success) {
+ if (amount == 0 || amount > 32) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ bool negative = BitIsSet(value, 31);
+ if (amount <= 32) {
+ carry_out = Bit32(value, amount - 1);
+ int64_t extended = llvm::SignExtend64<32>(value);
+ return UnsignedBits(extended, amount + 31, amount);
+ } else {
+ carry_out = (negative ? 1 : 0);
+ return (negative ? 0xffffffff : 0);
+ }
+}
+
+static inline uint32_t ASR(const uint32_t value, const uint32_t amount,
+ bool *success) {
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = ASR_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t ROR_C(const uint32_t value, const uint32_t amount,
+ uint32_t &carry_out, bool *success) {
+ if (amount == 0) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ uint32_t amt = amount % 32;
+ uint32_t result = Rotr32(value, amt);
+ carry_out = Bit32(value, 31);
+ return result;
+}
+
+static inline uint32_t ROR(const uint32_t value, const uint32_t amount,
+ bool *success) {
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = ROR_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t RRX_C(const uint32_t value, const uint32_t carry_in,
+ uint32_t &carry_out, bool *success) {
+ *success = true;
+ carry_out = Bit32(value, 0);
+ return Bit32(carry_in, 0) << 31 | Bits32(value, 31, 1);
+}
+
+static inline uint32_t RRX(const uint32_t value, const uint32_t carry_in,
+ bool *success) {
+ *success = true;
+ uint32_t dont_care;
+ uint32_t result = RRX_C(value, carry_in, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t Shift_C(const uint32_t value, ARM_ShifterType type,
+ const uint32_t amount, const uint32_t carry_in,
+ uint32_t &carry_out, bool *success) {
+ if (type == SRType_RRX && amount != 1) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+
+ if (amount == 0) {
+ carry_out = carry_in;
+ return value;
+ }
+ uint32_t result;
+ switch (type) {
+ case SRType_LSL:
+ result = LSL_C(value, amount, carry_out, success);
+ break;
+ case SRType_LSR:
+ result = LSR_C(value, amount, carry_out, success);
+ break;
+ case SRType_ASR:
+ result = ASR_C(value, amount, carry_out, success);
+ break;
+ case SRType_ROR:
+ result = ROR_C(value, amount, carry_out, success);
+ break;
+ case SRType_RRX:
+ result = RRX_C(value, carry_in, carry_out, success);
+ break;
+ default:
+ *success = false;
+ break;
+ }
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t Shift(const uint32_t value, ARM_ShifterType type,
+ const uint32_t amount, const uint32_t carry_in,
+ bool *success) {
+ // Don't care about carry out in this case.
+ uint32_t dont_care;
+ uint32_t result = Shift_C(value, type, amount, carry_in, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t bits(const uint32_t val, const uint32_t msbit,
+ const uint32_t lsbit) {
+ return Bits32(val, msbit, lsbit);
+}
+
+static inline uint32_t bit(const uint32_t val, const uint32_t msbit) {
+ return bits(val, msbit, msbit);
+}
+
+static uint32_t ror(uint32_t val, uint32_t N, uint32_t shift) {
+ uint32_t m = shift % N;
+ return (val >> m) | (val << (N - m));
+}
+
+// (imm32, carry_out) = ARMExpandImm_C(imm12, carry_in)
+static inline uint32_t ARMExpandImm_C(uint32_t opcode, uint32_t carry_in,
+ uint32_t &carry_out) {
+ uint32_t imm32; // the expanded result
+ uint32_t imm = bits(opcode, 7, 0); // immediate value
+ uint32_t amt = 2 * bits(opcode, 11, 8); // rotate amount
+ if (amt == 0) {
+ imm32 = imm;
+ carry_out = carry_in;
+ } else {
+ imm32 = ror(imm, 32, amt);
+ carry_out = Bit32(imm32, 31);
+ }
+ return imm32;
+}
+
+static inline uint32_t ARMExpandImm(uint32_t opcode) {
+ // 'carry_in' argument to following function call does not affect the imm32
+ // result.
+ uint32_t carry_in = 0;
+ uint32_t carry_out;
+ return ARMExpandImm_C(opcode, carry_in, carry_out);
+}
+
+// (imm32, carry_out) = ThumbExpandImm_C(imm12, carry_in)
+static inline uint32_t ThumbExpandImm_C(uint32_t opcode, uint32_t carry_in,
+ uint32_t &carry_out) {
+ uint32_t imm32; // the expanded result
+ const uint32_t i = bit(opcode, 26);
+ const uint32_t imm3 = bits(opcode, 14, 12);
+ const uint32_t abcdefgh = bits(opcode, 7, 0);
+ const uint32_t imm12 = i << 11 | imm3 << 8 | abcdefgh;
+
+ if (bits(imm12, 11, 10) == 0) {
+ switch (bits(imm12, 9, 8)) {
+ default: // Keep static analyzer happy with a default case
+ case 0:
+ imm32 = abcdefgh;
+ break;
+
+ case 1:
+ imm32 = abcdefgh << 16 | abcdefgh;
+ break;
+
+ case 2:
+ imm32 = abcdefgh << 24 | abcdefgh << 8;
+ break;
+
+ case 3:
+ imm32 = abcdefgh << 24 | abcdefgh << 16 | abcdefgh << 8 | abcdefgh;
+ break;
+ }
+ carry_out = carry_in;
+ } else {
+ const uint32_t unrotated_value = 0x80 | bits(imm12, 6, 0);
+ imm32 = ror(unrotated_value, 32, bits(imm12, 11, 7));
+ carry_out = Bit32(imm32, 31);
+ }
+ return imm32;
+}
+
+static inline uint32_t ThumbExpandImm(uint32_t opcode) {
+ // 'carry_in' argument to following function call does not affect the imm32
+ // result.
+ uint32_t carry_in = 0;
+ uint32_t carry_out;
+ return ThumbExpandImm_C(opcode, carry_in, carry_out);
+}
+
+// imm32 = ZeroExtend(i:imm3:imm8, 32)
+static inline uint32_t ThumbImm12(uint32_t opcode) {
+ const uint32_t i = bit(opcode, 26);
+ const uint32_t imm3 = bits(opcode, 14, 12);
+ const uint32_t imm8 = bits(opcode, 7, 0);
+ const uint32_t imm12 = i << 11 | imm3 << 8 | imm8;
+ return imm12;
+}
+
+// imm32 = ZeroExtend(imm7:'00', 32)
+static inline uint32_t ThumbImm7Scaled(uint32_t opcode) {
+ const uint32_t imm7 = bits(opcode, 6, 0);
+ return imm7 * 4;
+}
+
+// imm32 = ZeroExtend(imm8:'00', 32)
+static inline uint32_t ThumbImm8Scaled(uint32_t opcode) {
+ const uint32_t imm8 = bits(opcode, 7, 0);
+ return imm8 * 4;
+}
+
+// This function performs the check for the register numbers 13 and 15 that are
+// not permitted for many Thumb register specifiers.
+static inline bool BadReg(uint32_t n) { return n == 13 || n == 15; }
+
+} // namespace lldb_private
+
+#endif // lldb_ARMUtils_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.cpp
new file mode 100644
index 000000000000..aab164ff93a6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.cpp
@@ -0,0 +1,96 @@
+//===-- AuxVector.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AuxVector.h"
+
+AuxVector::AuxVector(const lldb_private::DataExtractor &data) {
+ ParseAuxv(data);
+}
+
+void AuxVector::ParseAuxv(const lldb_private::DataExtractor &data) {
+ lldb::offset_t offset = 0;
+ const size_t value_type_size = data.GetAddressByteSize() * 2;
+ while (data.ValidOffsetForDataOfSize(offset, value_type_size)) {
+ // We're not reading an address but an int that could be 32 or 64 bit
+ // depending on the address size, which is what GetAddress does.
+ const uint64_t type = data.GetAddress(&offset);
+ const uint64_t value = data.GetAddress(&offset);
+ if (type == AUXV_AT_NULL)
+ break;
+ if (type == AUXV_AT_IGNORE)
+ continue;
+
+ m_auxv_entries[type] = value;
+ }
+}
+
+llvm::Optional<uint64_t>
+AuxVector::GetAuxValue(enum EntryType entry_type) const {
+ auto it = m_auxv_entries.find(static_cast<uint64_t>(entry_type));
+ if (it != m_auxv_entries.end())
+ return it->second;
+ return llvm::None;
+}
+
+void AuxVector::DumpToLog(lldb_private::Log *log) const {
+ if (!log)
+ return;
+
+ log->PutCString("AuxVector: ");
+ for (auto entry : m_auxv_entries) {
+ log->Printf(" %s [%" PRIu64 "]: %" PRIx64,
+ GetEntryName(static_cast<EntryType>(entry.first)), entry.first,
+ entry.second);
+ }
+}
+
+const char *AuxVector::GetEntryName(EntryType type) const {
+ const char *name = "AT_???";
+
+#define ENTRY_NAME(_type) \
+ _type: \
+ name = &#_type[5]
+ switch (type) {
+ case ENTRY_NAME(AUXV_AT_NULL); break;
+ case ENTRY_NAME(AUXV_AT_IGNORE); break;
+ case ENTRY_NAME(AUXV_AT_EXECFD); break;
+ case ENTRY_NAME(AUXV_AT_PHDR); break;
+ case ENTRY_NAME(AUXV_AT_PHENT); break;
+ case ENTRY_NAME(AUXV_AT_PHNUM); break;
+ case ENTRY_NAME(AUXV_AT_PAGESZ); break;
+ case ENTRY_NAME(AUXV_AT_BASE); break;
+ case ENTRY_NAME(AUXV_AT_FLAGS); break;
+ case ENTRY_NAME(AUXV_AT_ENTRY); break;
+ case ENTRY_NAME(AUXV_AT_NOTELF); break;
+ case ENTRY_NAME(AUXV_AT_UID); break;
+ case ENTRY_NAME(AUXV_AT_EUID); break;
+ case ENTRY_NAME(AUXV_AT_GID); break;
+ case ENTRY_NAME(AUXV_AT_EGID); break;
+ case ENTRY_NAME(AUXV_AT_CLKTCK); break;
+ case ENTRY_NAME(AUXV_AT_PLATFORM); break;
+ case ENTRY_NAME(AUXV_AT_HWCAP); break;
+ case ENTRY_NAME(AUXV_AT_FPUCW); break;
+ case ENTRY_NAME(AUXV_AT_DCACHEBSIZE); break;
+ case ENTRY_NAME(AUXV_AT_ICACHEBSIZE); break;
+ case ENTRY_NAME(AUXV_AT_UCACHEBSIZE); break;
+ case ENTRY_NAME(AUXV_AT_IGNOREPPC); break;
+ case ENTRY_NAME(AUXV_AT_SECURE); break;
+ case ENTRY_NAME(AUXV_AT_BASE_PLATFORM); break;
+ case ENTRY_NAME(AUXV_AT_RANDOM); break;
+ case ENTRY_NAME(AUXV_AT_EXECFN); break;
+ case ENTRY_NAME(AUXV_AT_SYSINFO); break;
+ case ENTRY_NAME(AUXV_AT_SYSINFO_EHDR); break;
+ case ENTRY_NAME(AUXV_AT_L1I_CACHESHAPE); break;
+ case ENTRY_NAME(AUXV_AT_L1D_CACHESHAPE); break;
+ case ENTRY_NAME(AUXV_AT_L2_CACHESHAPE); break;
+ case ENTRY_NAME(AUXV_AT_L3_CACHESHAPE); break;
+ }
+#undef ENTRY_NAME
+
+ return name;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.h
new file mode 100644
index 000000000000..c16be68aedb1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.h
@@ -0,0 +1,73 @@
+//===-- AuxVector.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_AuxVector_H_
+#define liblldb_AuxVector_H_
+
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include <unordered_map>
+
+class AuxVector {
+
+public:
+ AuxVector(const lldb_private::DataExtractor &data);
+
+ /// Constants describing the type of entry.
+ /// On Linux, running "LD_SHOW_AUXV=1 ./executable" will spew AUX
+ /// information. Added AUXV prefix to avoid potential conflicts with system-
+ /// defined macros
+ enum EntryType {
+ AUXV_AT_NULL = 0, ///< End of auxv.
+ AUXV_AT_IGNORE = 1, ///< Ignore entry.
+ AUXV_AT_EXECFD = 2, ///< File descriptor of program.
+ AUXV_AT_PHDR = 3, ///< Program headers.
+ AUXV_AT_PHENT = 4, ///< Size of program header.
+ AUXV_AT_PHNUM = 5, ///< Number of program headers.
+ AUXV_AT_PAGESZ = 6, ///< Page size.
+ AUXV_AT_BASE = 7, ///< Interpreter base address.
+ AUXV_AT_FLAGS = 8, ///< Flags.
+ AUXV_AT_ENTRY = 9, ///< Program entry point.
+ AUXV_AT_NOTELF = 10, ///< Set if program is not an ELF.
+ AUXV_AT_UID = 11, ///< UID.
+ AUXV_AT_EUID = 12, ///< Effective UID.
+ AUXV_AT_GID = 13, ///< GID.
+ AUXV_AT_EGID = 14, ///< Effective GID.
+ AUXV_AT_CLKTCK = 17, ///< Clock frequency (e.g. times(2)).
+ AUXV_AT_PLATFORM = 15, ///< String identifying platform.
+ AUXV_AT_HWCAP =
+ 16, ///< Machine dependent hints about processor capabilities.
+ AUXV_AT_FPUCW = 18, ///< Used FPU control word.
+ AUXV_AT_DCACHEBSIZE = 19, ///< Data cache block size.
+ AUXV_AT_ICACHEBSIZE = 20, ///< Instruction cache block size.
+ AUXV_AT_UCACHEBSIZE = 21, ///< Unified cache block size.
+ AUXV_AT_IGNOREPPC = 22, ///< Entry should be ignored.
+ AUXV_AT_SECURE = 23, ///< Boolean, was exec setuid-like?
+ AUXV_AT_BASE_PLATFORM = 24, ///< String identifying real platforms.
+ AUXV_AT_RANDOM = 25, ///< Address of 16 random bytes.
+ AUXV_AT_EXECFN = 31, ///< Filename of executable.
+ AUXV_AT_SYSINFO = 32, ///< Pointer to the global system page used for system
+ /// calls and other nice things.
+ AUXV_AT_SYSINFO_EHDR = 33,
+ AUXV_AT_L1I_CACHESHAPE = 34, ///< Shapes of the caches.
+ AUXV_AT_L1D_CACHESHAPE = 35,
+ AUXV_AT_L2_CACHESHAPE = 36,
+ AUXV_AT_L3_CACHESHAPE = 37,
+ };
+
+ llvm::Optional<uint64_t> GetAuxValue(enum EntryType entry_type) const;
+ void DumpToLog(lldb_private::Log *log) const;
+ const char *GetEntryName(EntryType type) const;
+
+private:
+ void ParseAuxv(const lldb_private::DataExtractor &data);
+
+ std::unordered_map<uint64_t, uint64_t> m_auxv_entries;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp
new file mode 100644
index 000000000000..1afe4d920599
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp
@@ -0,0 +1,758 @@
+//===-- DynamicRegisterInfo.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DynamicRegisterInfo.h"
+
+#include "lldb/Core/StreamFile.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/StringExtractor.h"
+#include "lldb/Utility/StructuredData.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+DynamicRegisterInfo::DynamicRegisterInfo(
+ const lldb_private::StructuredData::Dictionary &dict,
+ const lldb_private::ArchSpec &arch) {
+ SetRegisterInfo(dict, arch);
+}
+
+DynamicRegisterInfo::DynamicRegisterInfo(DynamicRegisterInfo &&info) {
+ MoveFrom(std::move(info));
+}
+
+DynamicRegisterInfo &
+DynamicRegisterInfo::operator=(DynamicRegisterInfo &&info) {
+ MoveFrom(std::move(info));
+ return *this;
+}
+
+void DynamicRegisterInfo::MoveFrom(DynamicRegisterInfo &&info) {
+ m_regs = std::move(info.m_regs);
+ m_sets = std::move(info.m_sets);
+ m_set_reg_nums = std::move(info.m_set_reg_nums);
+ m_set_names = std::move(info.m_set_names);
+ m_value_regs_map = std::move(info.m_value_regs_map);
+ m_invalidate_regs_map = std::move(info.m_invalidate_regs_map);
+ m_dynamic_reg_size_map = std::move(info.m_dynamic_reg_size_map);
+
+ m_reg_data_byte_size = info.m_reg_data_byte_size;
+ m_finalized = info.m_finalized;
+
+ if (m_finalized) {
+ const size_t num_sets = m_sets.size();
+ for (size_t set = 0; set < num_sets; ++set)
+ m_sets[set].registers = m_set_reg_nums[set].data();
+ }
+
+ info.Clear();
+}
+
+size_t
+DynamicRegisterInfo::SetRegisterInfo(const StructuredData::Dictionary &dict,
+ const ArchSpec &arch) {
+ assert(!m_finalized);
+ StructuredData::Array *sets = nullptr;
+ if (dict.GetValueForKeyAsArray("sets", sets)) {
+ const uint32_t num_sets = sets->GetSize();
+ for (uint32_t i = 0; i < num_sets; ++i) {
+ ConstString set_name;
+ if (sets->GetItemAtIndexAsString(i, set_name) && !set_name.IsEmpty()) {
+ m_sets.push_back({set_name.AsCString(), nullptr, 0, nullptr});
+ } else {
+ Clear();
+ printf("error: register sets must have valid names\n");
+ return 0;
+ }
+ }
+ m_set_reg_nums.resize(m_sets.size());
+ }
+
+ StructuredData::Array *regs = nullptr;
+ if (!dict.GetValueForKeyAsArray("registers", regs))
+ return 0;
+
+ const uint32_t num_regs = regs->GetSize();
+ // typedef std::map<std::string, std::vector<std::string> >
+ // InvalidateNameMap;
+ // InvalidateNameMap invalidate_map;
+ for (uint32_t i = 0; i < num_regs; ++i) {
+ StructuredData::Dictionary *reg_info_dict = nullptr;
+ if (!regs->GetItemAtIndexAsDictionary(i, reg_info_dict)) {
+ Clear();
+ printf("error: items in the 'registers' array must be dictionaries\n");
+ regs->DumpToStdout();
+ return 0;
+ }
+
+ // { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16,
+ // 'encoding':'uint' , 'format':'hex' , 'set': 0, 'ehframe' : 2,
+ // 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', },
+ RegisterInfo reg_info;
+ std::vector<uint32_t> value_regs;
+ std::vector<uint32_t> invalidate_regs;
+ memset(&reg_info, 0, sizeof(reg_info));
+
+ ConstString name_val;
+ ConstString alt_name_val;
+ if (!reg_info_dict->GetValueForKeyAsString("name", name_val, nullptr)) {
+ Clear();
+ printf("error: registers must have valid names and offsets\n");
+ reg_info_dict->DumpToStdout();
+ return 0;
+ }
+ reg_info.name = name_val.GetCString();
+ reg_info_dict->GetValueForKeyAsString("alt-name", alt_name_val, nullptr);
+ reg_info.alt_name = alt_name_val.GetCString();
+
+ reg_info_dict->GetValueForKeyAsInteger("offset", reg_info.byte_offset,
+ UINT32_MAX);
+
+ const ByteOrder byte_order = arch.GetByteOrder();
+
+ if (reg_info.byte_offset == UINT32_MAX) {
+ // No offset for this register, see if the register has a value
+ // expression which indicates this register is part of another register.
+ // Value expressions are things like "rax[31:0]" which state that the
+ // current register's value is in a concrete register "rax" in bits 31:0.
+ // If there is a value expression we can calculate the offset
+ bool success = false;
+ llvm::StringRef slice_str;
+ if (reg_info_dict->GetValueForKeyAsString("slice", slice_str, nullptr)) {
+ // Slices use the following format:
+ // REGNAME[MSBIT:LSBIT]
+ // REGNAME - name of the register to grab a slice of
+ // MSBIT - the most significant bit at which the current register value
+ // starts at
+ // LSBIT - the least significant bit at which the current register value
+ // ends at
+ static RegularExpression g_bitfield_regex(
+ llvm::StringRef("([A-Za-z_][A-Za-z0-9_]*)\\[([0-9]+):([0-9]+)\\]"));
+ RegularExpression::Match regex_match(3);
+ if (g_bitfield_regex.Execute(slice_str, &regex_match)) {
+ llvm::StringRef reg_name_str;
+ std::string msbit_str;
+ std::string lsbit_str;
+ if (regex_match.GetMatchAtIndex(slice_str, 1, reg_name_str) &&
+ regex_match.GetMatchAtIndex(slice_str, 2, msbit_str) &&
+ regex_match.GetMatchAtIndex(slice_str, 3, lsbit_str)) {
+ const uint32_t msbit =
+ StringConvert::ToUInt32(msbit_str.c_str(), UINT32_MAX);
+ const uint32_t lsbit =
+ StringConvert::ToUInt32(lsbit_str.c_str(), UINT32_MAX);
+ if (msbit != UINT32_MAX && lsbit != UINT32_MAX) {
+ if (msbit > lsbit) {
+ const uint32_t msbyte = msbit / 8;
+ const uint32_t lsbyte = lsbit / 8;
+
+ ConstString containing_reg_name(reg_name_str);
+
+ const RegisterInfo *containing_reg_info =
+ GetRegisterInfo(containing_reg_name);
+ if (containing_reg_info) {
+ const uint32_t max_bit = containing_reg_info->byte_size * 8;
+ if (msbit < max_bit && lsbit < max_bit) {
+ m_invalidate_regs_map[containing_reg_info
+ ->kinds[eRegisterKindLLDB]]
+ .push_back(i);
+ m_value_regs_map[i].push_back(
+ containing_reg_info->kinds[eRegisterKindLLDB]);
+ m_invalidate_regs_map[i].push_back(
+ containing_reg_info->kinds[eRegisterKindLLDB]);
+
+ if (byte_order == eByteOrderLittle) {
+ success = true;
+ reg_info.byte_offset =
+ containing_reg_info->byte_offset + lsbyte;
+ } else if (byte_order == eByteOrderBig) {
+ success = true;
+ reg_info.byte_offset =
+ containing_reg_info->byte_offset + msbyte;
+ } else {
+ llvm_unreachable("Invalid byte order");
+ }
+ } else {
+ if (msbit > max_bit)
+ printf("error: msbit (%u) must be less than the bitsize "
+ "of the register (%u)\n",
+ msbit, max_bit);
+ else
+ printf("error: lsbit (%u) must be less than the bitsize "
+ "of the register (%u)\n",
+ lsbit, max_bit);
+ }
+ } else {
+ printf("error: invalid concrete register \"%s\"\n",
+ containing_reg_name.GetCString());
+ }
+ } else {
+ printf("error: msbit (%u) must be greater than lsbit (%u)\n",
+ msbit, lsbit);
+ }
+ } else {
+ printf("error: msbit (%u) and lsbit (%u) must be valid\n", msbit,
+ lsbit);
+ }
+ } else {
+ // TODO: print error invalid slice string that doesn't follow the
+ // format
+ printf("error: failed to extract regex matches for parsing the "
+ "register bitfield regex\n");
+ }
+ } else {
+ // TODO: print error invalid slice string that doesn't follow the
+ // format
+ printf("error: failed to match against register bitfield regex\n");
+ }
+ } else {
+ StructuredData::Array *composite_reg_list = nullptr;
+ if (reg_info_dict->GetValueForKeyAsArray("composite",
+ composite_reg_list)) {
+ const size_t num_composite_regs = composite_reg_list->GetSize();
+ if (num_composite_regs > 0) {
+ uint32_t composite_offset = UINT32_MAX;
+ for (uint32_t composite_idx = 0; composite_idx < num_composite_regs;
+ ++composite_idx) {
+ ConstString composite_reg_name;
+ if (composite_reg_list->GetItemAtIndexAsString(
+ composite_idx, composite_reg_name, nullptr)) {
+ const RegisterInfo *composite_reg_info =
+ GetRegisterInfo(composite_reg_name);
+ if (composite_reg_info) {
+ composite_offset = std::min(composite_offset,
+ composite_reg_info->byte_offset);
+ m_value_regs_map[i].push_back(
+ composite_reg_info->kinds[eRegisterKindLLDB]);
+ m_invalidate_regs_map[composite_reg_info
+ ->kinds[eRegisterKindLLDB]]
+ .push_back(i);
+ m_invalidate_regs_map[i].push_back(
+ composite_reg_info->kinds[eRegisterKindLLDB]);
+ } else {
+ // TODO: print error invalid slice string that doesn't follow
+ // the format
+ printf("error: failed to find composite register by name: "
+ "\"%s\"\n",
+ composite_reg_name.GetCString());
+ }
+ } else {
+ printf(
+ "error: 'composite' list value wasn't a python string\n");
+ }
+ }
+ if (composite_offset != UINT32_MAX) {
+ reg_info.byte_offset = composite_offset;
+ success = m_value_regs_map.find(i) != m_value_regs_map.end();
+ } else {
+ printf("error: 'composite' registers must specify at least one "
+ "real register\n");
+ }
+ } else {
+ printf("error: 'composite' list was empty\n");
+ }
+ }
+ }
+
+ if (!success) {
+ Clear();
+ reg_info_dict->DumpToStdout();
+ return 0;
+ }
+ }
+
+ int64_t bitsize = 0;
+ if (!reg_info_dict->GetValueForKeyAsInteger("bitsize", bitsize)) {
+ Clear();
+ printf("error: invalid or missing 'bitsize' key/value pair in register "
+ "dictionary\n");
+ reg_info_dict->DumpToStdout();
+ return 0;
+ }
+
+ reg_info.byte_size = bitsize / 8;
+
+ llvm::StringRef dwarf_opcode_string;
+ if (reg_info_dict->GetValueForKeyAsString("dynamic_size_dwarf_expr_bytes",
+ dwarf_opcode_string)) {
+ reg_info.dynamic_size_dwarf_len = dwarf_opcode_string.size() / 2;
+ assert(reg_info.dynamic_size_dwarf_len > 0);
+
+ std::vector<uint8_t> dwarf_opcode_bytes(reg_info.dynamic_size_dwarf_len);
+ uint32_t j;
+ StringExtractor opcode_extractor(dwarf_opcode_string);
+ uint32_t ret_val = opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes);
+ UNUSED_IF_ASSERT_DISABLED(ret_val);
+ assert(ret_val == reg_info.dynamic_size_dwarf_len);
+
+ for (j = 0; j < reg_info.dynamic_size_dwarf_len; ++j)
+ m_dynamic_reg_size_map[i].push_back(dwarf_opcode_bytes[j]);
+
+ reg_info.dynamic_size_dwarf_expr_bytes = m_dynamic_reg_size_map[i].data();
+ }
+
+ llvm::StringRef format_str;
+ if (reg_info_dict->GetValueForKeyAsString("format", format_str, nullptr)) {
+ if (OptionArgParser::ToFormat(format_str.str().c_str(), reg_info.format,
+ nullptr)
+ .Fail()) {
+ Clear();
+ printf("error: invalid 'format' value in register dictionary\n");
+ reg_info_dict->DumpToStdout();
+ return 0;
+ }
+ } else {
+ reg_info_dict->GetValueForKeyAsInteger("format", reg_info.format,
+ eFormatHex);
+ }
+
+ llvm::StringRef encoding_str;
+ if (reg_info_dict->GetValueForKeyAsString("encoding", encoding_str))
+ reg_info.encoding = Args::StringToEncoding(encoding_str, eEncodingUint);
+ else
+ reg_info_dict->GetValueForKeyAsInteger("encoding", reg_info.encoding,
+ eEncodingUint);
+
+ size_t set = 0;
+ if (!reg_info_dict->GetValueForKeyAsInteger<size_t>("set", set, -1) ||
+ set >= m_sets.size()) {
+ Clear();
+ printf("error: invalid 'set' value in register dictionary, valid values "
+ "are 0 - %i\n",
+ (int)set);
+ reg_info_dict->DumpToStdout();
+ return 0;
+ }
+
+ // Fill in the register numbers
+ reg_info.kinds[lldb::eRegisterKindLLDB] = i;
+ reg_info.kinds[lldb::eRegisterKindProcessPlugin] = i;
+ uint32_t eh_frame_regno = LLDB_INVALID_REGNUM;
+ reg_info_dict->GetValueForKeyAsInteger("gcc", eh_frame_regno,
+ LLDB_INVALID_REGNUM);
+ if (eh_frame_regno == LLDB_INVALID_REGNUM)
+ reg_info_dict->GetValueForKeyAsInteger("ehframe", eh_frame_regno,
+ LLDB_INVALID_REGNUM);
+ reg_info.kinds[lldb::eRegisterKindEHFrame] = eh_frame_regno;
+ reg_info_dict->GetValueForKeyAsInteger(
+ "dwarf", reg_info.kinds[lldb::eRegisterKindDWARF], LLDB_INVALID_REGNUM);
+ llvm::StringRef generic_str;
+ if (reg_info_dict->GetValueForKeyAsString("generic", generic_str))
+ reg_info.kinds[lldb::eRegisterKindGeneric] =
+ Args::StringToGenericRegister(generic_str);
+ else
+ reg_info_dict->GetValueForKeyAsInteger(
+ "generic", reg_info.kinds[lldb::eRegisterKindGeneric],
+ LLDB_INVALID_REGNUM);
+
+ // Check if this register invalidates any other register values when it is
+ // modified
+ StructuredData::Array *invalidate_reg_list = nullptr;
+ if (reg_info_dict->GetValueForKeyAsArray("invalidate-regs",
+ invalidate_reg_list)) {
+ const size_t num_regs = invalidate_reg_list->GetSize();
+ if (num_regs > 0) {
+ for (uint32_t idx = 0; idx < num_regs; ++idx) {
+ ConstString invalidate_reg_name;
+ uint64_t invalidate_reg_num;
+ if (invalidate_reg_list->GetItemAtIndexAsString(
+ idx, invalidate_reg_name)) {
+ const RegisterInfo *invalidate_reg_info =
+ GetRegisterInfo(invalidate_reg_name);
+ if (invalidate_reg_info) {
+ m_invalidate_regs_map[i].push_back(
+ invalidate_reg_info->kinds[eRegisterKindLLDB]);
+ } else {
+ // TODO: print error invalid slice string that doesn't follow the
+ // format
+ printf("error: failed to find a 'invalidate-regs' register for "
+ "\"%s\" while parsing register \"%s\"\n",
+ invalidate_reg_name.GetCString(), reg_info.name);
+ }
+ } else if (invalidate_reg_list->GetItemAtIndexAsInteger(
+ idx, invalidate_reg_num)) {
+ if (invalidate_reg_num != UINT64_MAX)
+ m_invalidate_regs_map[i].push_back(invalidate_reg_num);
+ else
+ printf("error: 'invalidate-regs' list value wasn't a valid "
+ "integer\n");
+ } else {
+ printf("error: 'invalidate-regs' list value wasn't a python string "
+ "or integer\n");
+ }
+ }
+ } else {
+ printf("error: 'invalidate-regs' contained an empty list\n");
+ }
+ }
+
+ // Calculate the register offset
+ const size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size;
+ if (m_reg_data_byte_size < end_reg_offset)
+ m_reg_data_byte_size = end_reg_offset;
+
+ m_regs.push_back(reg_info);
+ m_set_reg_nums[set].push_back(i);
+ }
+ Finalize(arch);
+ return m_regs.size();
+}
+
+void DynamicRegisterInfo::AddRegister(RegisterInfo &reg_info,
+ ConstString &reg_name,
+ ConstString &reg_alt_name,
+ ConstString &set_name) {
+ assert(!m_finalized);
+ const uint32_t reg_num = m_regs.size();
+ reg_info.name = reg_name.AsCString();
+ assert(reg_info.name);
+ reg_info.alt_name = reg_alt_name.AsCString(nullptr);
+ uint32_t i;
+ if (reg_info.value_regs) {
+ for (i = 0; reg_info.value_regs[i] != LLDB_INVALID_REGNUM; ++i)
+ m_value_regs_map[reg_num].push_back(reg_info.value_regs[i]);
+ }
+ if (reg_info.invalidate_regs) {
+ for (i = 0; reg_info.invalidate_regs[i] != LLDB_INVALID_REGNUM; ++i)
+ m_invalidate_regs_map[reg_num].push_back(reg_info.invalidate_regs[i]);
+ }
+ if (reg_info.dynamic_size_dwarf_expr_bytes) {
+ for (i = 0; i < reg_info.dynamic_size_dwarf_len; ++i)
+ m_dynamic_reg_size_map[reg_num].push_back(
+ reg_info.dynamic_size_dwarf_expr_bytes[i]);
+
+ reg_info.dynamic_size_dwarf_expr_bytes =
+ m_dynamic_reg_size_map[reg_num].data();
+ }
+
+ m_regs.push_back(reg_info);
+ uint32_t set = GetRegisterSetIndexByName(set_name, true);
+ assert(set < m_sets.size());
+ assert(set < m_set_reg_nums.size());
+ assert(set < m_set_names.size());
+ m_set_reg_nums[set].push_back(reg_num);
+ size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size;
+ if (m_reg_data_byte_size < end_reg_offset)
+ m_reg_data_byte_size = end_reg_offset;
+}
+
+void DynamicRegisterInfo::Finalize(const ArchSpec &arch) {
+ if (m_finalized)
+ return;
+
+ m_finalized = true;
+ const size_t num_sets = m_sets.size();
+ for (size_t set = 0; set < num_sets; ++set) {
+ assert(m_sets.size() == m_set_reg_nums.size());
+ m_sets[set].num_registers = m_set_reg_nums[set].size();
+ m_sets[set].registers = m_set_reg_nums[set].data();
+ }
+
+ // sort and unique all value registers and make sure each is terminated with
+ // LLDB_INVALID_REGNUM
+
+ for (reg_to_regs_map::iterator pos = m_value_regs_map.begin(),
+ end = m_value_regs_map.end();
+ pos != end; ++pos) {
+ if (pos->second.size() > 1) {
+ llvm::sort(pos->second.begin(), pos->second.end());
+ reg_num_collection::iterator unique_end =
+ std::unique(pos->second.begin(), pos->second.end());
+ if (unique_end != pos->second.end())
+ pos->second.erase(unique_end, pos->second.end());
+ }
+ assert(!pos->second.empty());
+ if (pos->second.back() != LLDB_INVALID_REGNUM)
+ pos->second.push_back(LLDB_INVALID_REGNUM);
+ }
+
+ // Now update all value_regs with each register info as needed
+ const size_t num_regs = m_regs.size();
+ for (size_t i = 0; i < num_regs; ++i) {
+ if (m_value_regs_map.find(i) != m_value_regs_map.end())
+ m_regs[i].value_regs = m_value_regs_map[i].data();
+ else
+ m_regs[i].value_regs = nullptr;
+ }
+
+ // Expand all invalidation dependencies
+ for (reg_to_regs_map::iterator pos = m_invalidate_regs_map.begin(),
+ end = m_invalidate_regs_map.end();
+ pos != end; ++pos) {
+ const uint32_t reg_num = pos->first;
+
+ if (m_regs[reg_num].value_regs) {
+ reg_num_collection extra_invalid_regs;
+ for (const uint32_t invalidate_reg_num : pos->second) {
+ reg_to_regs_map::iterator invalidate_pos =
+ m_invalidate_regs_map.find(invalidate_reg_num);
+ if (invalidate_pos != m_invalidate_regs_map.end()) {
+ for (const uint32_t concrete_invalidate_reg_num :
+ invalidate_pos->second) {
+ if (concrete_invalidate_reg_num != reg_num)
+ extra_invalid_regs.push_back(concrete_invalidate_reg_num);
+ }
+ }
+ }
+ pos->second.insert(pos->second.end(), extra_invalid_regs.begin(),
+ extra_invalid_regs.end());
+ }
+ }
+
+ // sort and unique all invalidate registers and make sure each is terminated
+ // with LLDB_INVALID_REGNUM
+ for (reg_to_regs_map::iterator pos = m_invalidate_regs_map.begin(),
+ end = m_invalidate_regs_map.end();
+ pos != end; ++pos) {
+ if (pos->second.size() > 1) {
+ llvm::sort(pos->second.begin(), pos->second.end());
+ reg_num_collection::iterator unique_end =
+ std::unique(pos->second.begin(), pos->second.end());
+ if (unique_end != pos->second.end())
+ pos->second.erase(unique_end, pos->second.end());
+ }
+ assert(!pos->second.empty());
+ if (pos->second.back() != LLDB_INVALID_REGNUM)
+ pos->second.push_back(LLDB_INVALID_REGNUM);
+ }
+
+ // Now update all invalidate_regs with each register info as needed
+ for (size_t i = 0; i < num_regs; ++i) {
+ if (m_invalidate_regs_map.find(i) != m_invalidate_regs_map.end())
+ m_regs[i].invalidate_regs = m_invalidate_regs_map[i].data();
+ else
+ m_regs[i].invalidate_regs = nullptr;
+ }
+
+ // Check if we need to automatically set the generic registers in case they
+ // weren't set
+ bool generic_regs_specified = false;
+ for (const auto &reg : m_regs) {
+ if (reg.kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) {
+ generic_regs_specified = true;
+ break;
+ }
+ }
+
+ if (!generic_regs_specified) {
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ case llvm::Triple::aarch64_be:
+ for (auto &reg : m_regs) {
+ if (strcmp(reg.name, "pc") == 0)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ else if ((strcmp(reg.name, "fp") == 0) ||
+ (strcmp(reg.name, "x29") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if ((strcmp(reg.name, "lr") == 0) ||
+ (strcmp(reg.name, "x30") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA;
+ else if ((strcmp(reg.name, "sp") == 0) ||
+ (strcmp(reg.name, "x31") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ else if (strcmp(reg.name, "cpsr") == 0)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ }
+ break;
+
+ case llvm::Triple::arm:
+ case llvm::Triple::armeb:
+ case llvm::Triple::thumb:
+ case llvm::Triple::thumbeb:
+ for (auto &reg : m_regs) {
+ if ((strcmp(reg.name, "pc") == 0) || (strcmp(reg.name, "r15") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ else if ((strcmp(reg.name, "sp") == 0) ||
+ (strcmp(reg.name, "r13") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ else if ((strcmp(reg.name, "lr") == 0) ||
+ (strcmp(reg.name, "r14") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA;
+ else if ((strcmp(reg.name, "r7") == 0) &&
+ arch.GetTriple().getVendor() == llvm::Triple::Apple)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if ((strcmp(reg.name, "r11") == 0) &&
+ arch.GetTriple().getVendor() != llvm::Triple::Apple)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if (strcmp(reg.name, "fp") == 0)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if (strcmp(reg.name, "cpsr") == 0)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ }
+ break;
+
+ case llvm::Triple::x86:
+ for (auto &reg : m_regs) {
+ if ((strcmp(reg.name, "eip") == 0) || (strcmp(reg.name, "pc") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ else if ((strcmp(reg.name, "esp") == 0) ||
+ (strcmp(reg.name, "sp") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ else if ((strcmp(reg.name, "ebp") == 0) ||
+ (strcmp(reg.name, "fp") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if ((strcmp(reg.name, "eflags") == 0) ||
+ (strcmp(reg.name, "flags") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ }
+ break;
+
+ case llvm::Triple::x86_64:
+ for (auto &reg : m_regs) {
+ if ((strcmp(reg.name, "rip") == 0) || (strcmp(reg.name, "pc") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ else if ((strcmp(reg.name, "rsp") == 0) ||
+ (strcmp(reg.name, "sp") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ else if ((strcmp(reg.name, "rbp") == 0) ||
+ (strcmp(reg.name, "fp") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if ((strcmp(reg.name, "rflags") == 0) ||
+ (strcmp(reg.name, "flags") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+size_t DynamicRegisterInfo::GetNumRegisters() const { return m_regs.size(); }
+
+size_t DynamicRegisterInfo::GetNumRegisterSets() const { return m_sets.size(); }
+
+size_t DynamicRegisterInfo::GetRegisterDataByteSize() const {
+ return m_reg_data_byte_size;
+}
+
+const RegisterInfo *
+DynamicRegisterInfo::GetRegisterInfoAtIndex(uint32_t i) const {
+ if (i < m_regs.size())
+ return &m_regs[i];
+ return nullptr;
+}
+
+RegisterInfo *DynamicRegisterInfo::GetRegisterInfoAtIndex(uint32_t i) {
+ if (i < m_regs.size())
+ return &m_regs[i];
+ return nullptr;
+}
+
+const RegisterSet *DynamicRegisterInfo::GetRegisterSet(uint32_t i) const {
+ if (i < m_sets.size())
+ return &m_sets[i];
+ return nullptr;
+}
+
+uint32_t DynamicRegisterInfo::GetRegisterSetIndexByName(ConstString &set_name,
+ bool can_create) {
+ name_collection::iterator pos, end = m_set_names.end();
+ for (pos = m_set_names.begin(); pos != end; ++pos) {
+ if (*pos == set_name)
+ return std::distance(m_set_names.begin(), pos);
+ }
+
+ m_set_names.push_back(set_name);
+ m_set_reg_nums.resize(m_set_reg_nums.size() + 1);
+ RegisterSet new_set = {set_name.AsCString(), nullptr, 0, nullptr};
+ m_sets.push_back(new_set);
+ return m_sets.size() - 1;
+}
+
+uint32_t
+DynamicRegisterInfo::ConvertRegisterKindToRegisterNumber(uint32_t kind,
+ uint32_t num) const {
+ reg_collection::const_iterator pos, end = m_regs.end();
+ for (pos = m_regs.begin(); pos != end; ++pos) {
+ if (pos->kinds[kind] == num)
+ return std::distance(m_regs.begin(), pos);
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
+
+void DynamicRegisterInfo::Clear() {
+ m_regs.clear();
+ m_sets.clear();
+ m_set_reg_nums.clear();
+ m_set_names.clear();
+ m_value_regs_map.clear();
+ m_invalidate_regs_map.clear();
+ m_dynamic_reg_size_map.clear();
+ m_reg_data_byte_size = 0;
+ m_finalized = false;
+}
+
+void DynamicRegisterInfo::Dump() const {
+ StreamFile s(stdout, false);
+ const size_t num_regs = m_regs.size();
+ s.Printf("%p: DynamicRegisterInfo contains %" PRIu64 " registers:\n",
+ static_cast<const void *>(this), static_cast<uint64_t>(num_regs));
+ for (size_t i = 0; i < num_regs; ++i) {
+ s.Printf("[%3" PRIu64 "] name = %-10s", (uint64_t)i, m_regs[i].name);
+ s.Printf(", size = %2u, offset = %4u, encoding = %u, format = %-10s",
+ m_regs[i].byte_size, m_regs[i].byte_offset, m_regs[i].encoding,
+ FormatManager::GetFormatAsCString(m_regs[i].format));
+ if (m_regs[i].kinds[eRegisterKindProcessPlugin] != LLDB_INVALID_REGNUM)
+ s.Printf(", process plugin = %3u",
+ m_regs[i].kinds[eRegisterKindProcessPlugin]);
+ if (m_regs[i].kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM)
+ s.Printf(", dwarf = %3u", m_regs[i].kinds[eRegisterKindDWARF]);
+ if (m_regs[i].kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM)
+ s.Printf(", ehframe = %3u", m_regs[i].kinds[eRegisterKindEHFrame]);
+ if (m_regs[i].kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM)
+ s.Printf(", generic = %3u", m_regs[i].kinds[eRegisterKindGeneric]);
+ if (m_regs[i].alt_name)
+ s.Printf(", alt-name = %s", m_regs[i].alt_name);
+ if (m_regs[i].value_regs) {
+ s.Printf(", value_regs = [ ");
+ for (size_t j = 0; m_regs[i].value_regs[j] != LLDB_INVALID_REGNUM; ++j) {
+ s.Printf("%s ", m_regs[m_regs[i].value_regs[j]].name);
+ }
+ s.Printf("]");
+ }
+ if (m_regs[i].invalidate_regs) {
+ s.Printf(", invalidate_regs = [ ");
+ for (size_t j = 0; m_regs[i].invalidate_regs[j] != LLDB_INVALID_REGNUM;
+ ++j) {
+ s.Printf("%s ", m_regs[m_regs[i].invalidate_regs[j]].name);
+ }
+ s.Printf("]");
+ }
+ s.EOL();
+ }
+
+ const size_t num_sets = m_sets.size();
+ s.Printf("%p: DynamicRegisterInfo contains %" PRIu64 " register sets:\n",
+ static_cast<const void *>(this), static_cast<uint64_t>(num_sets));
+ for (size_t i = 0; i < num_sets; ++i) {
+ s.Printf("set[%" PRIu64 "] name = %s, regs = [", (uint64_t)i,
+ m_sets[i].name);
+ for (size_t idx = 0; idx < m_sets[i].num_registers; ++idx) {
+ s.Printf("%s ", m_regs[m_sets[i].registers[idx]].name);
+ }
+ s.Printf("]\n");
+ }
+}
+
+const lldb_private::RegisterInfo *DynamicRegisterInfo::GetRegisterInfo(
+ lldb_private::ConstString reg_name) const {
+ for (auto &reg_info : m_regs) {
+ // We can use pointer comparison since we used a ConstString to set the
+ // "name" member in AddRegister()
+ if (reg_info.name == reg_name.GetCString()) {
+ return &reg_info;
+ }
+ }
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h
new file mode 100644
index 000000000000..aacf547e187d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h
@@ -0,0 +1,93 @@
+//===-- DynamicRegisterInfo.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_DynamicRegisterInfo_h_
+#define lldb_DynamicRegisterInfo_h_
+
+#include <map>
+#include <vector>
+
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private.h"
+
+class DynamicRegisterInfo {
+public:
+ DynamicRegisterInfo() = default;
+
+ DynamicRegisterInfo(const lldb_private::StructuredData::Dictionary &dict,
+ const lldb_private::ArchSpec &arch);
+
+ virtual ~DynamicRegisterInfo() = default;
+
+ DynamicRegisterInfo(DynamicRegisterInfo &) = delete;
+ void operator=(DynamicRegisterInfo &) = delete;
+
+ DynamicRegisterInfo(DynamicRegisterInfo &&info);
+ DynamicRegisterInfo &operator=(DynamicRegisterInfo &&info);
+
+ size_t SetRegisterInfo(const lldb_private::StructuredData::Dictionary &dict,
+ const lldb_private::ArchSpec &arch);
+
+ void AddRegister(lldb_private::RegisterInfo &reg_info,
+ lldb_private::ConstString &reg_name,
+ lldb_private::ConstString &reg_alt_name,
+ lldb_private::ConstString &set_name);
+
+ void Finalize(const lldb_private::ArchSpec &arch);
+
+ size_t GetNumRegisters() const;
+
+ size_t GetNumRegisterSets() const;
+
+ size_t GetRegisterDataByteSize() const;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(uint32_t i) const;
+
+ lldb_private::RegisterInfo *GetRegisterInfoAtIndex(uint32_t i);
+
+ const lldb_private::RegisterSet *GetRegisterSet(uint32_t i) const;
+
+ uint32_t GetRegisterSetIndexByName(lldb_private::ConstString &set_name,
+ bool can_create);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(uint32_t kind,
+ uint32_t num) const;
+
+ void Dump() const;
+
+ void Clear();
+
+protected:
+ // Classes that inherit from DynamicRegisterInfo can see and modify these
+ typedef std::vector<lldb_private::RegisterInfo> reg_collection;
+ typedef std::vector<lldb_private::RegisterSet> set_collection;
+ typedef std::vector<uint32_t> reg_num_collection;
+ typedef std::vector<reg_num_collection> set_reg_num_collection;
+ typedef std::vector<lldb_private::ConstString> name_collection;
+ typedef std::map<uint32_t, reg_num_collection> reg_to_regs_map;
+ typedef std::vector<uint8_t> dwarf_opcode;
+ typedef std::map<uint32_t, dwarf_opcode> dynamic_reg_size_map;
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfo(lldb_private::ConstString reg_name) const;
+
+ void MoveFrom(DynamicRegisterInfo &&info);
+
+ reg_collection m_regs;
+ set_collection m_sets;
+ set_reg_num_collection m_set_reg_nums;
+ name_collection m_set_names;
+ reg_to_regs_map m_value_regs_map;
+ reg_to_regs_map m_invalidate_regs_map;
+ dynamic_reg_size_map m_dynamic_reg_size_map;
+ size_t m_reg_data_byte_size = 0u; // The number of bytes required to store
+ // all registers
+ bool m_finalized = false;
+};
+#endif // lldb_DynamicRegisterInfo_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.cpp
new file mode 100644
index 000000000000..9f63a594e054
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.cpp
@@ -0,0 +1,86 @@
+//===-- FreeBSDSignals.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FreeBSDSignals.h"
+
+using namespace lldb_private;
+
+FreeBSDSignals::FreeBSDSignals() : UnixSignals() { Reset(); }
+
+void FreeBSDSignals::Reset() {
+ UnixSignals::Reset();
+
+ // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION
+ // ====== ============ ======== ====== ======
+ // ===================================================
+ AddSignal(32, "SIGTHR", false, false, false, "thread interrupt");
+ AddSignal(33, "SIGLIBRT", false, false, false,
+ "reserved by real-time library");
+ AddSignal(65, "SIGRTMIN", false, false, false, "real time signal 0");
+ AddSignal(66, "SIGRTMIN+1", false, false, false, "real time signal 1");
+ AddSignal(67, "SIGRTMIN+2", false, false, false, "real time signal 2");
+ AddSignal(68, "SIGRTMIN+3", false, false, false, "real time signal 3");
+ AddSignal(69, "SIGRTMIN+4", false, false, false, "real time signal 4");
+ AddSignal(70, "SIGRTMIN+5", false, false, false, "real time signal 5");
+ AddSignal(71, "SIGRTMIN+6", false, false, false, "real time signal 6");
+ AddSignal(72, "SIGRTMIN+7", false, false, false, "real time signal 7");
+ AddSignal(73, "SIGRTMIN+8", false, false, false, "real time signal 8");
+ AddSignal(74, "SIGRTMIN+9", false, false, false, "real time signal 9");
+ AddSignal(75, "SIGRTMIN+10", false, false, false, "real time signal 10");
+ AddSignal(76, "SIGRTMIN+11", false, false, false, "real time signal 11");
+ AddSignal(77, "SIGRTMIN+12", false, false, false, "real time signal 12");
+ AddSignal(78, "SIGRTMIN+13", false, false, false, "real time signal 13");
+ AddSignal(79, "SIGRTMIN+14", false, false, false, "real time signal 14");
+ AddSignal(80, "SIGRTMIN+15", false, false, false, "real time signal 15");
+ AddSignal(81, "SIGRTMIN+16", false, false, false, "real time signal 16");
+ AddSignal(82, "SIGRTMIN+17", false, false, false, "real time signal 17");
+ AddSignal(83, "SIGRTMIN+18", false, false, false, "real time signal 18");
+ AddSignal(84, "SIGRTMIN+19", false, false, false, "real time signal 19");
+ AddSignal(85, "SIGRTMIN+20", false, false, false, "real time signal 20");
+ AddSignal(86, "SIGRTMIN+21", false, false, false, "real time signal 21");
+ AddSignal(87, "SIGRTMIN+22", false, false, false, "real time signal 22");
+ AddSignal(88, "SIGRTMIN+23", false, false, false, "real time signal 23");
+ AddSignal(89, "SIGRTMIN+24", false, false, false, "real time signal 24");
+ AddSignal(90, "SIGRTMIN+25", false, false, false, "real time signal 25");
+ AddSignal(91, "SIGRTMIN+26", false, false, false, "real time signal 26");
+ AddSignal(92, "SIGRTMIN+27", false, false, false, "real time signal 27");
+ AddSignal(93, "SIGRTMIN+28", false, false, false, "real time signal 28");
+ AddSignal(94, "SIGRTMIN+29", false, false, false, "real time signal 29");
+ AddSignal(95, "SIGRTMIN+30", false, false, false, "real time signal 30");
+ AddSignal(96, "SIGRTMAX-30", false, false, false, "real time signal 31");
+ AddSignal(97, "SIGRTMAX-29", false, false, false, "real time signal 32");
+ AddSignal(98, "SIGRTMAX-28", false, false, false, "real time signal 33");
+ AddSignal(99, "SIGRTMAX-27", false, false, false, "real time signal 34");
+ AddSignal(100, "SIGRTMAX-26", false, false, false, "real time signal 35");
+ AddSignal(101, "SIGRTMAX-25", false, false, false, "real time signal 36");
+ AddSignal(102, "SIGRTMAX-24", false, false, false, "real time signal 37");
+ AddSignal(103, "SIGRTMAX-23", false, false, false, "real time signal 38");
+ AddSignal(104, "SIGRTMAX-22", false, false, false, "real time signal 39");
+ AddSignal(105, "SIGRTMAX-21", false, false, false, "real time signal 40");
+ AddSignal(106, "SIGRTMAX-20", false, false, false, "real time signal 41");
+ AddSignal(107, "SIGRTMAX-19", false, false, false, "real time signal 42");
+ AddSignal(108, "SIGRTMAX-18", false, false, false, "real time signal 43");
+ AddSignal(109, "SIGRTMAX-17", false, false, false, "real time signal 44");
+ AddSignal(110, "SIGRTMAX-16", false, false, false, "real time signal 45");
+ AddSignal(111, "SIGRTMAX-15", false, false, false, "real time signal 46");
+ AddSignal(112, "SIGRTMAX-14", false, false, false, "real time signal 47");
+ AddSignal(113, "SIGRTMAX-13", false, false, false, "real time signal 48");
+ AddSignal(114, "SIGRTMAX-12", false, false, false, "real time signal 49");
+ AddSignal(115, "SIGRTMAX-11", false, false, false, "real time signal 50");
+ AddSignal(116, "SIGRTMAX-10", false, false, false, "real time signal 51");
+ AddSignal(117, "SIGRTMAX-9", false, false, false, "real time signal 52");
+ AddSignal(118, "SIGRTMAX-8", false, false, false, "real time signal 53");
+ AddSignal(119, "SIGRTMAX-7", false, false, false, "real time signal 54");
+ AddSignal(120, "SIGRTMAX-6", false, false, false, "real time signal 55");
+ AddSignal(121, "SIGRTMAX-5", false, false, false, "real time signal 56");
+ AddSignal(122, "SIGRTMAX-4", false, false, false, "real time signal 57");
+ AddSignal(123, "SIGRTMAX-3", false, false, false, "real time signal 58");
+ AddSignal(124, "SIGRTMAX-2", false, false, false, "real time signal 59");
+ AddSignal(125, "SIGRTMAX-1", false, false, false, "real time signal 60");
+ AddSignal(126, "SIGRTMAX", false, false, false, "real time signal 61");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.h
new file mode 100644
index 000000000000..75462f3c76ff
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.h
@@ -0,0 +1,27 @@
+//===-- FreeBSDSignals.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_FreeBSDSignals_H_
+#define liblldb_FreeBSDSignals_H_
+
+#include "lldb/Target/UnixSignals.h"
+
+namespace lldb_private {
+
+/// FreeBSD specific set of Unix signals.
+class FreeBSDSignals : public UnixSignals {
+public:
+ FreeBSDSignals();
+
+private:
+ void Reset() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_FreeBSDSignals_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp
new file mode 100644
index 000000000000..ed35273ce3fe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp
@@ -0,0 +1,18 @@
+//===-- GDBRemoteSignals.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteSignals.h"
+
+using namespace lldb_private;
+
+GDBRemoteSignals::GDBRemoteSignals() : UnixSignals() { Reset(); }
+
+GDBRemoteSignals::GDBRemoteSignals(const lldb::UnixSignalsSP &rhs)
+ : UnixSignals(*rhs) {}
+
+void GDBRemoteSignals::Reset() { m_signals.clear(); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.h
new file mode 100644
index 000000000000..a02dd0604e67
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.h
@@ -0,0 +1,29 @@
+//===-- GDBRemoteSignals.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteSignals_H_
+#define liblldb_GDBRemoteSignals_H_
+
+#include "lldb/Target/UnixSignals.h"
+
+namespace lldb_private {
+
+/// Empty set of Unix signals to be filled by PlatformRemoteGDBServer
+class GDBRemoteSignals : public UnixSignals {
+public:
+ GDBRemoteSignals();
+
+ GDBRemoteSignals(const lldb::UnixSignalsSP &rhs);
+
+private:
+ void Reset() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteSignals_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.cpp
new file mode 100644
index 000000000000..3cb583172623
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.cpp
@@ -0,0 +1,84 @@
+//===-- HistoryThread.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-private.h"
+
+#include "Plugins/Process/Utility/HistoryThread.h"
+
+#include "Plugins/Process/Utility/HistoryUnwind.h"
+#include "Plugins/Process/Utility/RegisterContextHistory.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrameList.h"
+#include "lldb/Utility/Log.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Constructor
+
+HistoryThread::HistoryThread(lldb_private::Process &process, lldb::tid_t tid,
+ std::vector<lldb::addr_t> pcs)
+ : Thread(process, tid, true), m_framelist_mutex(), m_framelist(),
+ m_pcs(pcs), m_extended_unwind_token(LLDB_INVALID_ADDRESS), m_queue_name(),
+ m_thread_name(), m_originating_unique_thread_id(tid),
+ m_queue_id(LLDB_INVALID_QUEUE_ID) {
+ m_unwinder_up.reset(new HistoryUnwind(*this, pcs));
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p HistoryThread::HistoryThread", static_cast<void *>(this));
+}
+
+// Destructor
+
+HistoryThread::~HistoryThread() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p HistoryThread::~HistoryThread (tid=0x%" PRIx64 ")",
+ static_cast<void *>(this), GetID());
+ DestroyThread();
+}
+
+lldb::RegisterContextSP HistoryThread::GetRegisterContext() {
+ RegisterContextSP rctx;
+ if (m_pcs.size() > 0) {
+ rctx = std::make_shared<RegisterContextHistory>(
+ *this, 0, GetProcess()->GetAddressByteSize(), m_pcs[0]);
+ }
+ return rctx;
+}
+
+lldb::RegisterContextSP
+HistoryThread::CreateRegisterContextForFrame(StackFrame *frame) {
+ return m_unwinder_up->CreateRegisterContextForFrame(frame);
+}
+
+lldb::StackFrameListSP HistoryThread::GetStackFrameList() {
+ // FIXME do not throw away the lock after we acquire it..
+ std::unique_lock<std::mutex> lock(m_framelist_mutex);
+ lock.unlock();
+ if (m_framelist.get() == nullptr) {
+ m_framelist =
+ std::make_shared<StackFrameList>(*this, StackFrameListSP(), true);
+ }
+
+ return m_framelist;
+}
+
+uint32_t HistoryThread::GetExtendedBacktraceOriginatingIndexID() {
+ if (m_originating_unique_thread_id != LLDB_INVALID_THREAD_ID) {
+ if (GetProcess()->HasAssignedIndexIDToThread(
+ m_originating_unique_thread_id)) {
+ return GetProcess()->AssignIndexIDToThread(
+ m_originating_unique_thread_id);
+ }
+ }
+ return LLDB_INVALID_THREAD_ID;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.h
new file mode 100644
index 000000000000..1e2658640172
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.h
@@ -0,0 +1,91 @@
+//===-- HistoryThread.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_HistoryThread_h_
+#define liblldb_HistoryThread_h_
+
+#include <mutex>
+
+#include "lldb/Core/UserSettingsController.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/StackFrameList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Broadcaster.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/UserID.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+/// \class HistoryThread HistoryThread.h "HistoryThread.h"
+/// A thread object representing a backtrace from a previous point in the
+/// process execution
+///
+/// This subclass of Thread is used to provide a backtrace from earlier in
+/// process execution. It is given a backtrace list of pc addresses and it
+/// will create stack frames for them.
+
+class HistoryThread : public lldb_private::Thread {
+public:
+ HistoryThread(lldb_private::Process &process, lldb::tid_t tid,
+ std::vector<lldb::addr_t> pcs);
+
+ ~HistoryThread() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(StackFrame *frame) override;
+
+ void RefreshStateAfterStop() override {}
+
+ bool CalculateStopInfo() override { return false; }
+
+ void SetExtendedBacktraceToken(uint64_t token) override {
+ m_extended_unwind_token = token;
+ }
+
+ uint64_t GetExtendedBacktraceToken() override {
+ return m_extended_unwind_token;
+ }
+
+ const char *GetQueueName() override { return m_queue_name.c_str(); }
+
+ void SetQueueName(const char *name) override { m_queue_name = name; }
+
+ lldb::queue_id_t GetQueueID() override { return m_queue_id; }
+
+ void SetQueueID(lldb::queue_id_t queue) override { m_queue_id = queue; }
+
+ const char *GetThreadName() { return m_thread_name.c_str(); }
+
+ uint32_t GetExtendedBacktraceOriginatingIndexID() override;
+
+ void SetThreadName(const char *name) { m_thread_name = name; }
+
+ const char *GetName() override { return m_thread_name.c_str(); }
+
+ void SetName(const char *name) override { m_thread_name = name; }
+
+protected:
+ virtual lldb::StackFrameListSP GetStackFrameList();
+
+ mutable std::mutex m_framelist_mutex;
+ lldb::StackFrameListSP m_framelist;
+ std::vector<lldb::addr_t> m_pcs;
+
+ uint64_t m_extended_unwind_token;
+ std::string m_queue_name;
+ std::string m_thread_name;
+ lldb::tid_t m_originating_unique_thread_id;
+ lldb::queue_id_t m_queue_id;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_HistoryThread_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp
new file mode 100644
index 000000000000..7d473bff8200
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp
@@ -0,0 +1,66 @@
+//===-- HistoryUnwind.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-private.h"
+
+#include "Plugins/Process/Utility/HistoryUnwind.h"
+#include "Plugins/Process/Utility/RegisterContextHistory.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Constructor
+
+HistoryUnwind::HistoryUnwind(Thread &thread, std::vector<lldb::addr_t> pcs)
+ : Unwind(thread), m_pcs(pcs) {}
+
+// Destructor
+
+HistoryUnwind::~HistoryUnwind() {}
+
+void HistoryUnwind::DoClear() {
+ std::lock_guard<std::recursive_mutex> guard(m_unwind_mutex);
+ m_pcs.clear();
+}
+
+lldb::RegisterContextSP
+HistoryUnwind::DoCreateRegisterContextForFrame(StackFrame *frame) {
+ RegisterContextSP rctx;
+ if (frame) {
+ addr_t pc = frame->GetFrameCodeAddress().GetLoadAddress(
+ &frame->GetThread()->GetProcess()->GetTarget());
+ if (pc != LLDB_INVALID_ADDRESS) {
+ rctx = std::make_shared<RegisterContextHistory>(
+ *frame->GetThread().get(), frame->GetConcreteFrameIndex(),
+ frame->GetThread()->GetProcess()->GetAddressByteSize(), pc);
+ }
+ }
+ return rctx;
+}
+
+bool HistoryUnwind::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
+ lldb::addr_t &pc) {
+ // FIXME do not throw away the lock after we acquire it..
+ std::unique_lock<std::recursive_mutex> guard(m_unwind_mutex);
+ guard.unlock();
+ if (frame_idx < m_pcs.size()) {
+ cfa = frame_idx;
+ pc = m_pcs[frame_idx];
+ return true;
+ }
+ return false;
+}
+
+uint32_t HistoryUnwind::DoGetFrameCount() { return m_pcs.size(); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.h
new file mode 100644
index 000000000000..6c4522e6b35b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.h
@@ -0,0 +1,41 @@
+//===-- HistoryUnwind.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_HistoryUnwind_h_
+#define liblldb_HistoryUnwind_h_
+
+#include <vector>
+
+#include "lldb/Target/Unwind.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class HistoryUnwind : public lldb_private::Unwind {
+public:
+ HistoryUnwind(Thread &thread, std::vector<lldb::addr_t> pcs);
+
+ ~HistoryUnwind() override;
+
+protected:
+ void DoClear() override;
+
+ lldb::RegisterContextSP
+ DoCreateRegisterContextForFrame(StackFrame *frame) override;
+
+ bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
+ lldb::addr_t &pc) override;
+ uint32_t DoGetFrameCount() override;
+
+private:
+ std::vector<lldb::addr_t> m_pcs;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_HistoryUnwind_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
new file mode 100644
index 000000000000..9beaf2fc7ac8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
@@ -0,0 +1,237 @@
+//===-- InferiorCallPOSIX.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "InferiorCallPOSIX.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+
+#ifndef LLDB_DISABLE_POSIX
+#include <sys/mman.h>
+#else
+// define them
+#define PROT_NONE 0
+#define PROT_READ 1
+#define PROT_WRITE 2
+#define PROT_EXEC 4
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr,
+ addr_t addr, addr_t length, unsigned prot,
+ unsigned flags, addr_t fd, addr_t offset) {
+ Thread *thread =
+ process->GetThreadList().GetExpressionExecutionThread().get();
+ if (thread == nullptr)
+ return false;
+
+ const bool append = true;
+ const bool include_symbols = true;
+ const bool include_inlines = false;
+ SymbolContextList sc_list;
+ const uint32_t count = process->GetTarget().GetImages().FindFunctions(
+ ConstString("mmap"), eFunctionNameTypeFull, include_symbols,
+ include_inlines, append, sc_list);
+ if (count > 0) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(0, sc)) {
+ const uint32_t range_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ const bool use_inline_block_range = false;
+ EvaluateExpressionOptions options;
+ options.SetStopOthers(true);
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTryAllThreads(true);
+ options.SetDebug(false);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetTrapExceptions(false);
+
+ addr_t prot_arg;
+ if (prot == eMmapProtNone)
+ prot_arg = PROT_NONE;
+ else {
+ prot_arg = 0;
+ if (prot & eMmapProtExec)
+ prot_arg |= PROT_EXEC;
+ if (prot & eMmapProtRead)
+ prot_arg |= PROT_READ;
+ if (prot & eMmapProtWrite)
+ prot_arg |= PROT_WRITE;
+ }
+
+ AddressRange mmap_range;
+ if (sc.GetAddressRange(range_scope, 0, use_inline_block_range,
+ mmap_range)) {
+ ClangASTContext *clang_ast_context =
+ process->GetTarget().GetScratchClangASTContext();
+ CompilerType clang_void_ptr_type =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ const ArchSpec arch = process->GetTarget().GetArchitecture();
+ MmapArgList args =
+ process->GetTarget().GetPlatform()->GetMmapArgumentList(
+ arch, addr, length, prot_arg, flags, fd, offset);
+ lldb::ThreadPlanSP call_plan_sp(
+ new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(),
+ clang_void_ptr_type, args, options));
+ if (call_plan_sp) {
+ DiagnosticManager diagnostics;
+
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ if (frame) {
+ ExecutionContext exe_ctx;
+ frame->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = process->RunThreadPlan(
+ exe_ctx, call_plan_sp, options, diagnostics);
+ if (result == eExpressionCompleted) {
+
+ allocated_addr =
+ call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(
+ LLDB_INVALID_ADDRESS);
+ if (process->GetAddressByteSize() == 4) {
+ if (allocated_addr == UINT32_MAX)
+ return false;
+ } else if (process->GetAddressByteSize() == 8) {
+ if (allocated_addr == UINT64_MAX)
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr,
+ addr_t length) {
+ Thread *thread =
+ process->GetThreadList().GetExpressionExecutionThread().get();
+ if (thread == nullptr)
+ return false;
+
+ const bool append = true;
+ const bool include_symbols = true;
+ const bool include_inlines = false;
+ SymbolContextList sc_list;
+ const uint32_t count = process->GetTarget().GetImages().FindFunctions(
+ ConstString("munmap"), eFunctionNameTypeFull, include_symbols,
+ include_inlines, append, sc_list);
+ if (count > 0) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(0, sc)) {
+ const uint32_t range_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ const bool use_inline_block_range = false;
+ EvaluateExpressionOptions options;
+ options.SetStopOthers(true);
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTryAllThreads(true);
+ options.SetDebug(false);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetTrapExceptions(false);
+
+ AddressRange munmap_range;
+ if (sc.GetAddressRange(range_scope, 0, use_inline_block_range,
+ munmap_range)) {
+ lldb::addr_t args[] = {addr, length};
+ lldb::ThreadPlanSP call_plan_sp(
+ new ThreadPlanCallFunction(*thread, munmap_range.GetBaseAddress(),
+ CompilerType(), args, options));
+ if (call_plan_sp) {
+ DiagnosticManager diagnostics;
+
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ if (frame) {
+ ExecutionContext exe_ctx;
+ frame->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = process->RunThreadPlan(
+ exe_ctx, call_plan_sp, options, diagnostics);
+ if (result == eExpressionCompleted) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+// FIXME: This has nothing to do with Posix, it is just a convenience function
+// that calls a
+// function of the form "void * (*)(void)". We should find a better place to
+// put this.
+
+bool lldb_private::InferiorCall(Process *process, const Address *address,
+ addr_t &returned_func, bool trap_exceptions) {
+ Thread *thread =
+ process->GetThreadList().GetExpressionExecutionThread().get();
+ if (thread == nullptr || address == nullptr)
+ return false;
+
+ EvaluateExpressionOptions options;
+ options.SetStopOthers(true);
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTryAllThreads(true);
+ options.SetDebug(false);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetTrapExceptions(trap_exceptions);
+
+ ClangASTContext *clang_ast_context =
+ process->GetTarget().GetScratchClangASTContext();
+ CompilerType clang_void_ptr_type =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ lldb::ThreadPlanSP call_plan_sp(
+ new ThreadPlanCallFunction(*thread, *address, clang_void_ptr_type,
+ llvm::ArrayRef<addr_t>(), options));
+ if (call_plan_sp) {
+ DiagnosticManager diagnostics;
+
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ if (frame) {
+ ExecutionContext exe_ctx;
+ frame->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result =
+ process->RunThreadPlan(exe_ctx, call_plan_sp, options, diagnostics);
+ if (result == eExpressionCompleted) {
+ returned_func =
+ call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(
+ LLDB_INVALID_ADDRESS);
+
+ if (process->GetAddressByteSize() == 4) {
+ if (returned_func == UINT32_MAX)
+ return false;
+ } else if (process->GetAddressByteSize() == 8) {
+ if (returned_func == UINT64_MAX)
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.h
new file mode 100644
index 000000000000..04316801b351
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.h
@@ -0,0 +1,38 @@
+//===-- InferiorCallPOSIX.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_InferiorCallPOSIX_h_
+#define lldb_InferiorCallPOSIX_h_
+
+// Inferior execution of POSIX functions.
+
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+class Process;
+
+enum MmapProt {
+ eMmapProtNone = 0,
+ eMmapProtExec = 1,
+ eMmapProtRead = 2,
+ eMmapProtWrite = 4
+};
+
+bool InferiorCallMmap(Process *proc, lldb::addr_t &allocated_addr,
+ lldb::addr_t addr, lldb::addr_t length, unsigned prot,
+ unsigned flags, lldb::addr_t fd, lldb::addr_t offset);
+
+bool InferiorCallMunmap(Process *proc, lldb::addr_t addr, lldb::addr_t length);
+
+bool InferiorCall(Process *proc, const Address *address,
+ lldb::addr_t &returned_func, bool trap_exceptions = false);
+
+} // namespace lldb_private
+
+#endif // lldb_InferiorCallPOSIX_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InstructionUtils.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InstructionUtils.h
new file mode 100644
index 000000000000..f74933e691ee
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InstructionUtils.h
@@ -0,0 +1,116 @@
+//===-- InstructionUtils.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_InstructionUtils_h_
+#define lldb_InstructionUtils_h_
+
+#include <cassert>
+#include <cstdint>
+
+// Common utilities for manipulating instruction bit fields.
+
+namespace lldb_private {
+
+// Return the bit field(s) from the most significant bit (msbit) to the
+// least significant bit (lsbit) of a 64-bit unsigned value.
+static inline uint64_t Bits64(const uint64_t bits, const uint32_t msbit,
+ const uint32_t lsbit) {
+ assert(msbit < 64 && lsbit <= msbit);
+ return (bits >> lsbit) & ((1ull << (msbit - lsbit + 1)) - 1);
+}
+
+// Return the bit field(s) from the most significant bit (msbit) to the
+// least significant bit (lsbit) of a 32-bit unsigned value.
+static inline uint32_t Bits32(const uint32_t bits, const uint32_t msbit,
+ const uint32_t lsbit) {
+ assert(msbit < 32 && lsbit <= msbit);
+ return (bits >> lsbit) & ((1u << (msbit - lsbit + 1)) - 1);
+}
+
+// Return the bit value from the 'bit' position of a 32-bit unsigned value.
+static inline uint32_t Bit32(const uint32_t bits, const uint32_t bit) {
+ return (bits >> bit) & 1u;
+}
+
+static inline uint64_t Bit64(const uint64_t bits, const uint32_t bit) {
+ return (bits >> bit) & 1ull;
+}
+
+// Set the bit field(s) from the most significant bit (msbit) to the
+// least significant bit (lsbit) of a 32-bit unsigned value to 'val'.
+static inline void SetBits32(uint32_t &bits, const uint32_t msbit,
+ const uint32_t lsbit, const uint32_t val) {
+ assert(msbit < 32 && lsbit < 32 && msbit >= lsbit);
+ uint32_t mask = ((1u << (msbit - lsbit + 1)) - 1);
+ bits &= ~(mask << lsbit);
+ bits |= (val & mask) << lsbit;
+}
+
+// Set the 'bit' position of a 32-bit unsigned value to 'val'.
+static inline void SetBit32(uint32_t &bits, const uint32_t bit,
+ const uint32_t val) {
+ SetBits32(bits, bit, bit, val);
+}
+
+// Rotate a 32-bit unsigned value right by the specified amount.
+static inline uint32_t Rotr32(uint32_t bits, uint32_t amt) {
+ assert(amt < 32 && "Invalid rotate amount");
+ return (bits >> amt) | (bits << ((32 - amt) & 31));
+}
+
+// Rotate a 32-bit unsigned value left by the specified amount.
+static inline uint32_t Rotl32(uint32_t bits, uint32_t amt) {
+ assert(amt < 32 && "Invalid rotate amount");
+ return (bits << amt) | (bits >> ((32 - amt) & 31));
+}
+
+// Create a mask that starts at bit zero and includes "bit"
+static inline uint64_t MaskUpToBit(const uint64_t bit) {
+ if (bit >= 63)
+ return -1ll;
+ return (1ull << (bit + 1ull)) - 1ull;
+}
+
+// Return an integer result equal to the number of bits of x that are ones.
+static inline uint32_t BitCount(uint64_t x) {
+ // c accumulates the total bits set in x
+ uint32_t c;
+ for (c = 0; x; ++c) {
+ x &= x - 1; // clear the least significant bit set
+ }
+ return c;
+}
+
+static inline bool BitIsSet(const uint64_t value, const uint64_t bit) {
+ return (value & (1ull << bit)) != 0;
+}
+
+static inline bool BitIsClear(const uint64_t value, const uint64_t bit) {
+ return (value & (1ull << bit)) == 0;
+}
+
+static inline uint64_t UnsignedBits(const uint64_t value, const uint64_t msbit,
+ const uint64_t lsbit) {
+ uint64_t result = value >> lsbit;
+ result &= MaskUpToBit(msbit - lsbit);
+ return result;
+}
+
+static inline int64_t SignedBits(const uint64_t value, const uint64_t msbit,
+ const uint64_t lsbit) {
+ uint64_t result = UnsignedBits(value, msbit, lsbit);
+ if (BitIsSet(value, msbit)) {
+ // Sign extend
+ result |= ~MaskUpToBit(msbit - lsbit);
+ }
+ return result;
+}
+
+} // namespace lldb_private
+
+#endif // lldb_InstructionUtils_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp
new file mode 100644
index 000000000000..1ba432aa542b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp
@@ -0,0 +1,112 @@
+//===-- LinuxProcMaps.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LinuxProcMaps.h"
+#include "llvm/ADT/StringRef.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StringExtractor.h"
+
+using namespace lldb_private;
+
+static Status
+ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,
+ MemoryRegionInfo &memory_region_info) {
+ memory_region_info.Clear();
+
+ StringExtractor line_extractor(maps_line);
+
+ // Format: {address_start_hex}-{address_end_hex} perms offset dev inode
+ // pathname perms: rwxp (letter is present if set, '-' if not, final
+ // character is p=private, s=shared).
+
+ // Parse out the starting address
+ lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
+
+ // Parse out hyphen separating start and end address from range.
+ if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
+ return Status(
+ "malformed /proc/{pid}/maps entry, missing dash between address range");
+
+ // Parse out the ending address
+ lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
+
+ // Parse out the space after the address.
+ if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
+ return Status(
+ "malformed /proc/{pid}/maps entry, missing space after range");
+
+ // Save the range.
+ memory_region_info.GetRange().SetRangeBase(start_address);
+ memory_region_info.GetRange().SetRangeEnd(end_address);
+
+ // Any memory region in /proc/{pid}/maps is by definition mapped into the
+ // process.
+ memory_region_info.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
+
+ // Parse out each permission entry.
+ if (line_extractor.GetBytesLeft() < 4)
+ return Status("malformed /proc/{pid}/maps entry, missing some portion of "
+ "permissions");
+
+ // Handle read permission.
+ const char read_perm_char = line_extractor.GetChar();
+ if (read_perm_char == 'r')
+ memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
+ else if (read_perm_char == '-')
+ memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+ else
+ return Status("unexpected /proc/{pid}/maps read permission char");
+
+ // Handle write permission.
+ const char write_perm_char = line_extractor.GetChar();
+ if (write_perm_char == 'w')
+ memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
+ else if (write_perm_char == '-')
+ memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+ else
+ return Status("unexpected /proc/{pid}/maps write permission char");
+
+ // Handle execute permission.
+ const char exec_perm_char = line_extractor.GetChar();
+ if (exec_perm_char == 'x')
+ memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
+ else if (exec_perm_char == '-')
+ memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+ else
+ return Status("unexpected /proc/{pid}/maps exec permission char");
+
+ line_extractor.GetChar(); // Read the private bit
+ line_extractor.SkipSpaces(); // Skip the separator
+ line_extractor.GetHexMaxU64(false, 0); // Read the offset
+ line_extractor.GetHexMaxU64(false, 0); // Read the major device number
+ line_extractor.GetChar(); // Read the device id separator
+ line_extractor.GetHexMaxU64(false, 0); // Read the major device number
+ line_extractor.SkipSpaces(); // Skip the separator
+ line_extractor.GetU64(0, 10); // Read the inode number
+
+ line_extractor.SkipSpaces();
+ const char *name = line_extractor.Peek();
+ if (name)
+ memory_region_info.SetName(name);
+
+ return Status();
+}
+
+void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,
+ LinuxMapCallback const &callback) {
+ llvm::StringRef lines(linux_map);
+ llvm::StringRef line;
+ while (!lines.empty()) {
+ std::tie(line, lines) = lines.split('\n');
+ MemoryRegionInfo region;
+ Status error = ParseMemoryRegionInfoFromProcMapsLine(line, region);
+ if (!callback(region, error))
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.h
new file mode 100644
index 000000000000..e1f0e48ac5c9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.h
@@ -0,0 +1,27 @@
+//===-- LinuxProcMaps.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_LinuxProcMaps_H_
+#define liblldb_LinuxProcMaps_H_
+
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/StringRef.h"
+#include <functional>
+
+
+namespace lldb_private {
+
+typedef std::function<bool(const lldb_private::MemoryRegionInfo &,
+ const lldb_private::Status &)> LinuxMapCallback;
+
+void ParseLinuxMapRegions(llvm::StringRef linux_map,
+ LinuxMapCallback const &callback);
+
+} // namespace lldb_private
+
+#endif // liblldb_LinuxProcMaps_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp
new file mode 100644
index 000000000000..bef47cd26307
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp
@@ -0,0 +1,92 @@
+//===-- LinuxSignals.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LinuxSignals.h"
+
+using namespace lldb_private;
+
+LinuxSignals::LinuxSignals() : UnixSignals() { Reset(); }
+
+void LinuxSignals::Reset() {
+ m_signals.clear();
+ // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION ALIAS
+ // ===== =========== ======== ===== ======
+ // ====================================== ======
+ AddSignal(1, "SIGHUP", false, true, true, "hangup");
+ AddSignal(2, "SIGINT", true, true, true, "interrupt");
+ AddSignal(3, "SIGQUIT", false, true, true, "quit");
+ AddSignal(4, "SIGILL", false, true, true, "illegal instruction");
+ AddSignal(5, "SIGTRAP", true, true, true,
+ "trace trap (not reset when caught)");
+ AddSignal(6, "SIGABRT", false, true, true, "abort()/IOT trap", "SIGIOT");
+ AddSignal(7, "SIGBUS", false, true, true, "bus error");
+ AddSignal(8, "SIGFPE", false, true, true, "floating point exception");
+ AddSignal(9, "SIGKILL", false, true, true, "kill");
+ AddSignal(10, "SIGUSR1", false, true, true, "user defined signal 1");
+ AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation");
+ AddSignal(12, "SIGUSR2", false, true, true, "user defined signal 2");
+ AddSignal(13, "SIGPIPE", false, true, true,
+ "write to pipe with reading end closed");
+ AddSignal(14, "SIGALRM", false, false, false, "alarm");
+ AddSignal(15, "SIGTERM", false, true, true, "termination requested");
+ AddSignal(16, "SIGSTKFLT", false, true, true, "stack fault");
+ AddSignal(17, "SIGCHLD", false, false, true, "child status has changed",
+ "SIGCLD");
+ AddSignal(18, "SIGCONT", false, true, true, "process continue");
+ AddSignal(19, "SIGSTOP", true, true, true, "process stop");
+ AddSignal(20, "SIGTSTP", false, true, true, "tty stop");
+ AddSignal(21, "SIGTTIN", false, true, true, "background tty read");
+ AddSignal(22, "SIGTTOU", false, true, true, "background tty write");
+ AddSignal(23, "SIGURG", false, true, true, "urgent data on socket");
+ AddSignal(24, "SIGXCPU", false, true, true, "CPU resource exceeded");
+ 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(29, "SIGIO", false, true, true, "input/output ready/Pollable event",
+ "SIGPOLL");
+ AddSignal(30, "SIGPWR", false, true, true, "power failure");
+ AddSignal(31, "SIGSYS", false, true, true, "invalid system call");
+ AddSignal(32, "SIG32", false, false, false,
+ "threading library internal signal 1");
+ AddSignal(33, "SIG33", false, false, false,
+ "threading library internal signal 2");
+ AddSignal(34, "SIGRTMIN", false, false, false, "real time signal 0");
+ AddSignal(35, "SIGRTMIN+1", false, false, false, "real time signal 1");
+ AddSignal(36, "SIGRTMIN+2", false, false, false, "real time signal 2");
+ AddSignal(37, "SIGRTMIN+3", false, false, false, "real time signal 3");
+ AddSignal(38, "SIGRTMIN+4", false, false, false, "real time signal 4");
+ AddSignal(39, "SIGRTMIN+5", false, false, false, "real time signal 5");
+ AddSignal(40, "SIGRTMIN+6", false, false, false, "real time signal 6");
+ AddSignal(41, "SIGRTMIN+7", false, false, false, "real time signal 7");
+ AddSignal(42, "SIGRTMIN+8", false, false, false, "real time signal 8");
+ AddSignal(43, "SIGRTMIN+9", false, false, false, "real time signal 9");
+ AddSignal(44, "SIGRTMIN+10", false, false, false, "real time signal 10");
+ AddSignal(45, "SIGRTMIN+11", false, false, false, "real time signal 11");
+ AddSignal(46, "SIGRTMIN+12", false, false, false, "real time signal 12");
+ AddSignal(47, "SIGRTMIN+13", false, false, false, "real time signal 13");
+ AddSignal(48, "SIGRTMIN+14", false, false, false, "real time signal 14");
+ AddSignal(49, "SIGRTMIN+15", false, false, false, "real time signal 15");
+ AddSignal(50, "SIGRTMAX-14", false, false, false,
+ "real time signal 16"); // switching to SIGRTMAX-xxx to match "kill
+ // -l" output
+ AddSignal(51, "SIGRTMAX-13", false, false, false, "real time signal 17");
+ AddSignal(52, "SIGRTMAX-12", false, false, false, "real time signal 18");
+ AddSignal(53, "SIGRTMAX-11", false, false, false, "real time signal 19");
+ AddSignal(54, "SIGRTMAX-10", false, false, false, "real time signal 20");
+ AddSignal(55, "SIGRTMAX-9", false, false, false, "real time signal 21");
+ AddSignal(56, "SIGRTMAX-8", false, false, false, "real time signal 22");
+ AddSignal(57, "SIGRTMAX-7", false, false, false, "real time signal 23");
+ AddSignal(58, "SIGRTMAX-6", false, false, false, "real time signal 24");
+ AddSignal(59, "SIGRTMAX-5", false, false, false, "real time signal 25");
+ AddSignal(60, "SIGRTMAX-4", false, false, false, "real time signal 26");
+ AddSignal(61, "SIGRTMAX-3", false, false, false, "real time signal 27");
+ AddSignal(62, "SIGRTMAX-2", false, false, false, "real time signal 28");
+ AddSignal(63, "SIGRTMAX-1", false, false, false, "real time signal 29");
+ AddSignal(64, "SIGRTMAX", false, false, false, "real time signal 30");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.h
new file mode 100644
index 000000000000..7ad8cfcbef68
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.h
@@ -0,0 +1,27 @@
+//===-- LinuxSignals.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_LinuxSignals_H_
+#define liblldb_LinuxSignals_H_
+
+#include "lldb/Target/UnixSignals.h"
+
+namespace lldb_private {
+
+/// Linux specific set of Unix signals.
+class LinuxSignals : public UnixSignals {
+public:
+ LinuxSignals();
+
+private:
+ void Reset() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_LinuxSignals_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.cpp
new file mode 100644
index 000000000000..d8e5426ab5a5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.cpp
@@ -0,0 +1,93 @@
+//===-- MipsLinuxSignals.cpp ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MipsLinuxSignals.h"
+
+using namespace lldb_private;
+
+MipsLinuxSignals::MipsLinuxSignals() : UnixSignals() { Reset(); }
+
+void MipsLinuxSignals::Reset() {
+ m_signals.clear();
+ // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION ALIAS
+ // ===== =========== ======== ===== ======
+ // ====================================== ========
+ AddSignal(1, "SIGHUP", false, true, true, "hangup");
+ AddSignal(2, "SIGINT", true, true, true, "interrupt");
+ AddSignal(3, "SIGQUIT", false, true, true, "quit");
+ AddSignal(4, "SIGILL", false, true, true, "illegal instruction");
+ AddSignal(5, "SIGTRAP", true, true, true,
+ "trace trap (not reset when caught)");
+ AddSignal(6, "SIGABRT", false, true, true, "abort()/IOT trap", "SIGIOT");
+ AddSignal(7, "SIGEMT", false, true, true, "terminate process with core dump");
+ AddSignal(8, "SIGFPE", false, true, true, "floating point exception");
+ AddSignal(9, "SIGKILL", false, true, true, "kill");
+ AddSignal(10, "SIGBUS", false, true, true, "bus error");
+ AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation");
+ AddSignal(12, "SIGSYS", false, true, true, "invalid system call");
+ AddSignal(13, "SIGPIPE", false, true, true,
+ "write to pipe with reading end closed");
+ AddSignal(14, "SIGALRM", false, false, false, "alarm");
+ AddSignal(15, "SIGTERM", false, true, true, "termination requested");
+ AddSignal(16, "SIGUSR1", false, true, true, "user defined signal 1");
+ AddSignal(17, "SIGUSR2", false, true, true, "user defined signal 2");
+ AddSignal(18, "SIGCHLD", false, false, true, "child status has changed",
+ "SIGCLD");
+ AddSignal(19, "SIGPWR", false, true, true, "power failure");
+ AddSignal(20, "SIGWINCH", false, true, true, "window size changes");
+ AddSignal(21, "SIGURG", false, true, true, "urgent data on socket");
+ AddSignal(22, "SIGIO", false, true, true, "input/output ready/Pollable event",
+ "SIGPOLL");
+ AddSignal(23, "SIGSTOP", true, true, true, "process stop");
+ AddSignal(24, "SIGTSTP", false, true, true, "tty stop");
+ AddSignal(25, "SIGCONT", false, true, true, "process continue");
+ AddSignal(26, "SIGTTIN", false, true, true, "background tty read");
+ AddSignal(27, "SIGTTOU", false, true, true, "background tty write");
+ AddSignal(28, "SIGVTALRM", false, true, true, "virtual time alarm");
+ AddSignal(29, "SIGPROF", false, false, false, "profiling time alarm");
+ AddSignal(30, "SIGXCPU", false, true, true, "CPU resource exceeded");
+ AddSignal(31, "SIGXFSZ", false, true, true, "file size limit exceeded");
+ AddSignal(32, "SIG32", false, false, false,
+ "threading library internal signal 1");
+ AddSignal(33, "SIG33", false, false, false,
+ "threading library internal signal 2");
+ AddSignal(34, "SIGRTMIN", false, false, false, "real time signal 0");
+ AddSignal(35, "SIGRTMIN+1", false, false, false, "real time signal 1");
+ AddSignal(36, "SIGRTMIN+2", false, false, false, "real time signal 2");
+ AddSignal(37, "SIGRTMIN+3", false, false, false, "real time signal 3");
+ AddSignal(38, "SIGRTMIN+4", false, false, false, "real time signal 4");
+ AddSignal(39, "SIGRTMIN+5", false, false, false, "real time signal 5");
+ AddSignal(40, "SIGRTMIN+6", false, false, false, "real time signal 6");
+ AddSignal(41, "SIGRTMIN+7", false, false, false, "real time signal 7");
+ AddSignal(42, "SIGRTMIN+8", false, false, false, "real time signal 8");
+ AddSignal(43, "SIGRTMIN+9", false, false, false, "real time signal 9");
+ AddSignal(44, "SIGRTMIN+10", false, false, false, "real time signal 10");
+ AddSignal(45, "SIGRTMIN+11", false, false, false, "real time signal 11");
+ AddSignal(46, "SIGRTMIN+12", false, false, false, "real time signal 12");
+ AddSignal(47, "SIGRTMIN+13", false, false, false, "real time signal 13");
+ AddSignal(48, "SIGRTMIN+14", false, false, false, "real time signal 14");
+ AddSignal(49, "SIGRTMIN+15", false, false, false, "real time signal 15");
+ AddSignal(50, "SIGRTMAX-14", false, false, false,
+ "real time signal 16"); // switching to SIGRTMAX-xxx to match "kill
+ // -l" output
+ AddSignal(51, "SIGRTMAX-13", false, false, false, "real time signal 17");
+ AddSignal(52, "SIGRTMAX-12", false, false, false, "real time signal 18");
+ AddSignal(53, "SIGRTMAX-11", false, false, false, "real time signal 19");
+ AddSignal(54, "SIGRTMAX-10", false, false, false, "real time signal 20");
+ AddSignal(55, "SIGRTMAX-9", false, false, false, "real time signal 21");
+ AddSignal(56, "SIGRTMAX-8", false, false, false, "real time signal 22");
+ AddSignal(57, "SIGRTMAX-7", false, false, false, "real time signal 23");
+ AddSignal(58, "SIGRTMAX-6", false, false, false, "real time signal 24");
+ AddSignal(59, "SIGRTMAX-5", false, false, false, "real time signal 25");
+ AddSignal(60, "SIGRTMAX-4", false, false, false, "real time signal 26");
+ AddSignal(61, "SIGRTMAX-3", false, false, false, "real time signal 27");
+ AddSignal(62, "SIGRTMAX-2", false, false, false, "real time signal 28");
+ AddSignal(63, "SIGRTMAX-1", false, false, false, "real time signal 29");
+ AddSignal(64, "SIGRTMAX", false, false, false, "real time signal 30");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.h
new file mode 100644
index 000000000000..b5e3ed86f568
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.h
@@ -0,0 +1,28 @@
+//===-- MipsLinuxSignals.h ------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MipsLinuxSignals_H_
+#define liblldb_MipsLinuxSignals_H_
+
+#include "lldb/Target/UnixSignals.h"
+
+namespace lldb_private {
+
+/// Linux specific set of Unix signals.
+class MipsLinuxSignals : public UnixSignals {
+public:
+ MipsLinuxSignals();
+
+private:
+ void Reset() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_MipsLinuxSignals_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.cpp
new file mode 100644
index 000000000000..be61cfdd7374
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.cpp
@@ -0,0 +1,42 @@
+//===-- NativeRegisterContextRegisterInfo.cpp -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeRegisterContextRegisterInfo.h"
+#include "lldb/lldb-private-forward.h"
+#include "lldb/lldb-types.h"
+
+using namespace lldb_private;
+
+NativeRegisterContextRegisterInfo::NativeRegisterContextRegisterInfo(
+ NativeThreadProtocol &thread,
+ RegisterInfoInterface *register_info_interface)
+ : NativeRegisterContext(thread),
+ m_register_info_interface_up(register_info_interface) {
+ assert(register_info_interface && "null register_info_interface");
+}
+
+uint32_t NativeRegisterContextRegisterInfo::GetRegisterCount() const {
+ return m_register_info_interface_up->GetRegisterCount();
+}
+
+uint32_t NativeRegisterContextRegisterInfo::GetUserRegisterCount() const {
+ return m_register_info_interface_up->GetUserRegisterCount();
+}
+
+const RegisterInfo *NativeRegisterContextRegisterInfo::GetRegisterInfoAtIndex(
+ uint32_t reg_index) const {
+ if (reg_index <= GetRegisterCount())
+ return m_register_info_interface_up->GetRegisterInfo() + reg_index;
+ else
+ return nullptr;
+}
+
+const RegisterInfoInterface &
+NativeRegisterContextRegisterInfo::GetRegisterInfoInterface() const {
+ return *m_register_info_interface_up;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h
new file mode 100644
index 000000000000..b285c477cd96
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h
@@ -0,0 +1,40 @@
+//===-- NativeRegisterContextRegisterInfo.h ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_PROCESS_UTIILTY_NATIVE_REGISTER_CONTEXT_REGISTER_INFO
+#define LLDB_PLUGINS_PROCESS_UTIILTY_NATIVE_REGISTER_CONTEXT_REGISTER_INFO
+
+#include <memory>
+
+#include "RegisterInfoInterface.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+
+namespace lldb_private {
+class NativeRegisterContextRegisterInfo : public NativeRegisterContext {
+public:
+ ///
+ /// Construct a NativeRegisterContextRegisterInfo, taking ownership
+ /// of the register_info_interface pointer.
+ ///
+ NativeRegisterContextRegisterInfo(
+ NativeThreadProtocol &thread,
+ RegisterInfoInterface *register_info_interface);
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+ const RegisterInfo *GetRegisterInfoAtIndex(uint32_t reg_index) const override;
+
+ const RegisterInfoInterface &GetRegisterInfoInterface() const;
+
+private:
+ std::unique_ptr<RegisterInfoInterface> m_register_info_interface_up;
+};
+}
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.cpp
new file mode 100644
index 000000000000..29967deb7e9b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.cpp
@@ -0,0 +1,53 @@
+//===-- NetBSDSignals.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NetBSDSignals.h"
+
+using namespace lldb_private;
+
+NetBSDSignals::NetBSDSignals() : UnixSignals() { Reset(); }
+
+void NetBSDSignals::Reset() {
+ UnixSignals::Reset();
+ // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION
+ // ====== ============ ======== ====== ======
+ // ===================================================
+ AddSignal(32, "SIGPWR", false, true, true,
+ "power fail/restart (not reset when caught)");
+ AddSignal(33, "SIGRTMIN", false, false, false, "real time signal 0");
+ AddSignal(34, "SIGRTMIN+1", false, false, false, "real time signal 1");
+ AddSignal(35, "SIGRTMIN+2", false, false, false, "real time signal 2");
+ AddSignal(36, "SIGRTMIN+3", false, false, false, "real time signal 3");
+ AddSignal(37, "SIGRTMIN+4", false, false, false, "real time signal 4");
+ AddSignal(38, "SIGRTMIN+5", false, false, false, "real time signal 5");
+ AddSignal(39, "SIGRTMIN+6", false, false, false, "real time signal 6");
+ AddSignal(40, "SIGRTMIN+7", false, false, false, "real time signal 7");
+ AddSignal(41, "SIGRTMIN+8", false, false, false, "real time signal 8");
+ AddSignal(42, "SIGRTMIN+9", false, false, false, "real time signal 9");
+ AddSignal(43, "SIGRTMIN+10", false, false, false, "real time signal 10");
+ AddSignal(44, "SIGRTMIN+11", false, false, false, "real time signal 11");
+ AddSignal(45, "SIGRTMIN+12", false, false, false, "real time signal 12");
+ AddSignal(46, "SIGRTMIN+13", false, false, false, "real time signal 13");
+ AddSignal(47, "SIGRTMIN+14", false, false, false, "real time signal 14");
+ AddSignal(48, "SIGRTMIN+15", false, false, false, "real time signal 15");
+ AddSignal(49, "SIGRTMIN-14", false, false, false, "real time signal 16");
+ AddSignal(50, "SIGRTMAX-13", false, false, false, "real time signal 17");
+ AddSignal(51, "SIGRTMAX-12", false, false, false, "real time signal 18");
+ AddSignal(52, "SIGRTMAX-11", false, false, false, "real time signal 19");
+ AddSignal(53, "SIGRTMAX-10", false, false, false, "real time signal 20");
+ AddSignal(54, "SIGRTMAX-9", false, false, false, "real time signal 21");
+ AddSignal(55, "SIGRTMAX-8", false, false, false, "real time signal 22");
+ AddSignal(56, "SIGRTMAX-7", false, false, false, "real time signal 23");
+ AddSignal(57, "SIGRTMAX-6", false, false, false, "real time signal 24");
+ AddSignal(58, "SIGRTMAX-5", false, false, false, "real time signal 25");
+ AddSignal(59, "SIGRTMAX-4", false, false, false, "real time signal 26");
+ AddSignal(60, "SIGRTMAX-3", false, false, false, "real time signal 27");
+ AddSignal(61, "SIGRTMAX-2", false, false, false, "real time signal 28");
+ AddSignal(62, "SIGRTMAX-1", false, false, false, "real time signal 29");
+ AddSignal(63, "SIGRTMAX", false, false, false, "real time signal 30");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.h
new file mode 100644
index 000000000000..bf7399a89060
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.h
@@ -0,0 +1,27 @@
+//===-- NetBSDSignals.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_NetBSDSignals_H_
+#define liblldb_NetBSDSignals_H_
+
+#include "lldb/Target/UnixSignals.h"
+
+namespace lldb_private {
+
+/// NetBSD specific set of Unix signals.
+class NetBSDSignals : public UnixSignals {
+public:
+ NetBSDSignals();
+
+private:
+ void Reset() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_NetBSDSignals_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwinConstants.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwinConstants.h
new file mode 100644
index 000000000000..ef40162984f1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwinConstants.h
@@ -0,0 +1,25 @@
+//===-- RegisterContextDarwinConstants.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_REGISTERCONTEXTDARWINCONSTANTS_H
+#define LLDB_REGISTERCONTEXTDARWINCONSTANTS_H
+
+namespace lldb_private {
+
+/// Constants returned by various RegisterContextDarwin_*** functions.
+#ifndef KERN_SUCCESS
+#define KERN_SUCCESS 0
+#endif
+
+#ifndef KERN_INVALID_ARGUMENT
+#define KERN_INVALID_ARGUMENT 4
+#endif
+
+} // namespace lldb_private
+
+#endif // LLDB_REGISTERCONTEXTDARWINCONSTANTS_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp
new file mode 100644
index 000000000000..e804a4d251f7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp
@@ -0,0 +1,1757 @@
+//===-- RegisterContextDarwin_arm.cpp ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextDarwin_arm.h"
+#include "RegisterContextDarwinConstants.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+
+#include <memory>
+
+// Support building against older versions of LLVM, this macro was added
+// recently.
+#ifndef LLVM_EXTENSION
+#define LLVM_EXTENSION
+#endif
+
+#include "Utility/ARM_DWARF_Registers.h"
+#include "Utility/ARM_ehframe_Registers.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum {
+ gpr_r0 = 0,
+ gpr_r1,
+ gpr_r2,
+ gpr_r3,
+ gpr_r4,
+ gpr_r5,
+ gpr_r6,
+ gpr_r7,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13,
+ gpr_sp = gpr_r13,
+ gpr_r14,
+ gpr_lr = gpr_r14,
+ gpr_r15,
+ gpr_pc = gpr_r15,
+ gpr_cpsr,
+
+ fpu_s0,
+ fpu_s1,
+ fpu_s2,
+ fpu_s3,
+ fpu_s4,
+ fpu_s5,
+ fpu_s6,
+ fpu_s7,
+ fpu_s8,
+ fpu_s9,
+ fpu_s10,
+ fpu_s11,
+ fpu_s12,
+ fpu_s13,
+ fpu_s14,
+ fpu_s15,
+ fpu_s16,
+ fpu_s17,
+ fpu_s18,
+ fpu_s19,
+ fpu_s20,
+ fpu_s21,
+ fpu_s22,
+ fpu_s23,
+ fpu_s24,
+ fpu_s25,
+ fpu_s26,
+ fpu_s27,
+ fpu_s28,
+ fpu_s29,
+ fpu_s30,
+ fpu_s31,
+ fpu_fpscr,
+
+ exc_exception,
+ exc_fsr,
+ exc_far,
+
+ dbg_bvr0,
+ dbg_bvr1,
+ dbg_bvr2,
+ dbg_bvr3,
+ dbg_bvr4,
+ dbg_bvr5,
+ dbg_bvr6,
+ dbg_bvr7,
+ dbg_bvr8,
+ dbg_bvr9,
+ dbg_bvr10,
+ dbg_bvr11,
+ dbg_bvr12,
+ dbg_bvr13,
+ dbg_bvr14,
+ dbg_bvr15,
+
+ dbg_bcr0,
+ dbg_bcr1,
+ dbg_bcr2,
+ dbg_bcr3,
+ dbg_bcr4,
+ dbg_bcr5,
+ dbg_bcr6,
+ dbg_bcr7,
+ dbg_bcr8,
+ dbg_bcr9,
+ dbg_bcr10,
+ dbg_bcr11,
+ dbg_bcr12,
+ dbg_bcr13,
+ dbg_bcr14,
+ dbg_bcr15,
+
+ dbg_wvr0,
+ dbg_wvr1,
+ dbg_wvr2,
+ dbg_wvr3,
+ dbg_wvr4,
+ dbg_wvr5,
+ dbg_wvr6,
+ dbg_wvr7,
+ dbg_wvr8,
+ dbg_wvr9,
+ dbg_wvr10,
+ dbg_wvr11,
+ dbg_wvr12,
+ dbg_wvr13,
+ dbg_wvr14,
+ dbg_wvr15,
+
+ dbg_wcr0,
+ dbg_wcr1,
+ dbg_wcr2,
+ dbg_wcr3,
+ dbg_wcr4,
+ dbg_wcr5,
+ dbg_wcr6,
+ dbg_wcr7,
+ dbg_wcr8,
+ dbg_wcr9,
+ dbg_wcr10,
+ dbg_wcr11,
+ dbg_wcr12,
+ dbg_wcr13,
+ dbg_wcr14,
+ dbg_wcr15,
+
+ k_num_registers
+};
+
+#define GPR_OFFSET(idx) ((idx)*4)
+#define FPU_OFFSET(idx) ((idx)*4 + sizeof(RegisterContextDarwin_arm::GPR))
+#define EXC_OFFSET(idx) \
+ ((idx)*4 + sizeof(RegisterContextDarwin_arm::GPR) + \
+ sizeof(RegisterContextDarwin_arm::FPU))
+#define DBG_OFFSET(reg) \
+ ((LLVM_EXTENSION offsetof(RegisterContextDarwin_arm::DBG, reg) + \
+ sizeof(RegisterContextDarwin_arm::GPR) + \
+ sizeof(RegisterContextDarwin_arm::FPU) + \
+ sizeof(RegisterContextDarwin_arm::EXC)))
+
+#define DEFINE_DBG(reg, i) \
+ #reg, NULL, sizeof(((RegisterContextDarwin_arm::DBG *) NULL)->reg[i]), \
+ DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM }, \
+ nullptr, nullptr, nullptr, 0
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterContextDarwin_arm::GPR) + \
+ sizeof(RegisterContextDarwin_arm::FPU) + \
+ sizeof(RegisterContextDarwin_arm::EXC))
+
+static RegisterInfo g_register_infos[] = {
+ // General purpose registers
+ // NAME ALT SZ OFFSET ENCODING FORMAT
+ // EH_FRAME DWARF GENERIC
+ // PROCESS PLUGIN LLDB NATIVE
+ // ====== ======= == ============= ============= ============
+ // =============== =============== =========================
+ // ===================== =============
+ {"r0",
+ nullptr,
+ 4,
+ GPR_OFFSET(0),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r0, dwarf_r0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r0},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ nullptr,
+ 4,
+ GPR_OFFSET(1),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r1, dwarf_r1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r1},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ nullptr,
+ 4,
+ GPR_OFFSET(2),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r2, dwarf_r2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r2},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ nullptr,
+ 4,
+ GPR_OFFSET(3),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r3, dwarf_r3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r3},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ nullptr,
+ 4,
+ GPR_OFFSET(4),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r4},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ nullptr,
+ 4,
+ GPR_OFFSET(5),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r5},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ nullptr,
+ 4,
+ GPR_OFFSET(6),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r6},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ nullptr,
+ 4,
+ GPR_OFFSET(7),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ gpr_r7},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ nullptr,
+ 4,
+ GPR_OFFSET(8),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r8},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ nullptr,
+ 4,
+ GPR_OFFSET(9),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r9},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ nullptr,
+ 4,
+ GPR_OFFSET(10),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_r10},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ nullptr,
+ 4,
+ GPR_OFFSET(11),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_r11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 4,
+ GPR_OFFSET(12),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_r12},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "r13",
+ 4,
+ GPR_OFFSET(13),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ gpr_sp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r14",
+ 4,
+ GPR_OFFSET(14),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ gpr_lr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "r15",
+ 4,
+ GPR_OFFSET(15),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ gpr_pc},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ GPR_OFFSET(16),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ gpr_cpsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"s0",
+ nullptr,
+ 4,
+ FPU_OFFSET(0),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s0},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ FPU_OFFSET(1),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s1},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ FPU_OFFSET(2),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s2},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ FPU_OFFSET(3),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s3},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ FPU_OFFSET(4),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s4},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ FPU_OFFSET(5),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s5},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ FPU_OFFSET(6),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s6},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ FPU_OFFSET(7),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s7},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ FPU_OFFSET(8),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s8},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ FPU_OFFSET(9),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s9},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ FPU_OFFSET(10),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s10},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ FPU_OFFSET(11),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ FPU_OFFSET(12),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s12},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ FPU_OFFSET(13),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s13},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ FPU_OFFSET(14),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s14},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ FPU_OFFSET(15),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s15},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ FPU_OFFSET(16),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s16},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ FPU_OFFSET(17),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s17},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ FPU_OFFSET(18),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s18},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ FPU_OFFSET(19),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s19},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ FPU_OFFSET(20),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s20},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ FPU_OFFSET(21),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s21},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ FPU_OFFSET(22),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s22},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ FPU_OFFSET(23),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s23},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ FPU_OFFSET(24),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s24},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ FPU_OFFSET(25),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s25},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ FPU_OFFSET(26),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s26},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ FPU_OFFSET(27),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s27},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ FPU_OFFSET(28),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s28},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ FPU_OFFSET(29),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s29},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ FPU_OFFSET(30),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s30},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ FPU_OFFSET(31),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s31},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpscr",
+ nullptr,
+ 4,
+ FPU_OFFSET(32),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fpscr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"exception",
+ nullptr,
+ 4,
+ EXC_OFFSET(0),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_exception},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fsr",
+ nullptr,
+ 4,
+ EXC_OFFSET(1),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_fsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"far",
+ nullptr,
+ 4,
+ EXC_OFFSET(2),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_far},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {DEFINE_DBG(bvr, 0)},
+ {DEFINE_DBG(bvr, 1)},
+ {DEFINE_DBG(bvr, 2)},
+ {DEFINE_DBG(bvr, 3)},
+ {DEFINE_DBG(bvr, 4)},
+ {DEFINE_DBG(bvr, 5)},
+ {DEFINE_DBG(bvr, 6)},
+ {DEFINE_DBG(bvr, 7)},
+ {DEFINE_DBG(bvr, 8)},
+ {DEFINE_DBG(bvr, 9)},
+ {DEFINE_DBG(bvr, 10)},
+ {DEFINE_DBG(bvr, 11)},
+ {DEFINE_DBG(bvr, 12)},
+ {DEFINE_DBG(bvr, 13)},
+ {DEFINE_DBG(bvr, 14)},
+ {DEFINE_DBG(bvr, 15)},
+
+ {DEFINE_DBG(bcr, 0)},
+ {DEFINE_DBG(bcr, 1)},
+ {DEFINE_DBG(bcr, 2)},
+ {DEFINE_DBG(bcr, 3)},
+ {DEFINE_DBG(bcr, 4)},
+ {DEFINE_DBG(bcr, 5)},
+ {DEFINE_DBG(bcr, 6)},
+ {DEFINE_DBG(bcr, 7)},
+ {DEFINE_DBG(bcr, 8)},
+ {DEFINE_DBG(bcr, 9)},
+ {DEFINE_DBG(bcr, 10)},
+ {DEFINE_DBG(bcr, 11)},
+ {DEFINE_DBG(bcr, 12)},
+ {DEFINE_DBG(bcr, 13)},
+ {DEFINE_DBG(bcr, 14)},
+ {DEFINE_DBG(bcr, 15)},
+
+ {DEFINE_DBG(wvr, 0)},
+ {DEFINE_DBG(wvr, 1)},
+ {DEFINE_DBG(wvr, 2)},
+ {DEFINE_DBG(wvr, 3)},
+ {DEFINE_DBG(wvr, 4)},
+ {DEFINE_DBG(wvr, 5)},
+ {DEFINE_DBG(wvr, 6)},
+ {DEFINE_DBG(wvr, 7)},
+ {DEFINE_DBG(wvr, 8)},
+ {DEFINE_DBG(wvr, 9)},
+ {DEFINE_DBG(wvr, 10)},
+ {DEFINE_DBG(wvr, 11)},
+ {DEFINE_DBG(wvr, 12)},
+ {DEFINE_DBG(wvr, 13)},
+ {DEFINE_DBG(wvr, 14)},
+ {DEFINE_DBG(wvr, 15)},
+
+ {DEFINE_DBG(wcr, 0)},
+ {DEFINE_DBG(wcr, 1)},
+ {DEFINE_DBG(wcr, 2)},
+ {DEFINE_DBG(wcr, 3)},
+ {DEFINE_DBG(wcr, 4)},
+ {DEFINE_DBG(wcr, 5)},
+ {DEFINE_DBG(wcr, 6)},
+ {DEFINE_DBG(wcr, 7)},
+ {DEFINE_DBG(wcr, 8)},
+ {DEFINE_DBG(wcr, 9)},
+ {DEFINE_DBG(wcr, 10)},
+ {DEFINE_DBG(wcr, 11)},
+ {DEFINE_DBG(wcr, 12)},
+ {DEFINE_DBG(wcr, 13)},
+ {DEFINE_DBG(wcr, 14)},
+ {DEFINE_DBG(wcr, 15)}};
+
+// General purpose registers
+static uint32_t g_gpr_regnums[] = {
+ gpr_r0, gpr_r1, gpr_r2, gpr_r3, gpr_r4, gpr_r5, gpr_r6, gpr_r7, gpr_r8,
+ gpr_r9, gpr_r10, gpr_r11, gpr_r12, gpr_sp, gpr_lr, gpr_pc, gpr_cpsr};
+
+// Floating point registers
+static uint32_t g_fpu_regnums[] = {
+ fpu_s0, fpu_s1, fpu_s2, fpu_s3, fpu_s4, fpu_s5, fpu_s6,
+ fpu_s7, fpu_s8, fpu_s9, fpu_s10, fpu_s11, fpu_s12, fpu_s13,
+ fpu_s14, fpu_s15, fpu_s16, fpu_s17, fpu_s18, fpu_s19, fpu_s20,
+ fpu_s21, fpu_s22, fpu_s23, fpu_s24, fpu_s25, fpu_s26, fpu_s27,
+ fpu_s28, fpu_s29, fpu_s30, fpu_s31, fpu_fpscr,
+};
+
+// Exception registers
+
+static uint32_t g_exc_regnums[] = {
+ exc_exception, exc_fsr, exc_far,
+};
+
+static size_t k_num_register_infos = llvm::array_lengthof(g_register_infos);
+
+RegisterContextDarwin_arm::RegisterContextDarwin_arm(
+ Thread &thread, uint32_t concrete_frame_idx)
+ : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextDarwin_arm::~RegisterContextDarwin_arm() {}
+
+void RegisterContextDarwin_arm::InvalidateAllRegisters() {
+ InvalidateAllRegisterStates();
+}
+
+size_t RegisterContextDarwin_arm::GetRegisterCount() {
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_arm::GetRegisterInfoAtIndex(size_t reg) {
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return nullptr;
+}
+
+size_t RegisterContextDarwin_arm::GetRegisterInfosCount() {
+ return k_num_register_infos;
+}
+
+const RegisterInfo *RegisterContextDarwin_arm::GetRegisterInfos() {
+ return g_register_infos;
+}
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = llvm::array_lengthof(g_gpr_regnums);
+const size_t k_num_fpu_registers = llvm::array_lengthof(g_fpu_regnums);
+const size_t k_num_exc_registers = llvm::array_lengthof(g_exc_regnums);
+
+// Register set definitions. The first definitions at register set index of
+// zero is for all registers, followed by other registers sets. The register
+// information for the all register set need not be filled in.
+static const RegisterSet g_reg_sets[] = {
+ {
+ "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums,
+ },
+ {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums},
+ {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}};
+
+const size_t k_num_regsets = llvm::array_lengthof(g_reg_sets);
+
+size_t RegisterContextDarwin_arm::GetRegisterSetCount() {
+ return k_num_regsets;
+}
+
+const RegisterSet *RegisterContextDarwin_arm::GetRegisterSet(size_t reg_set) {
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return nullptr;
+}
+
+// Register information definitions for 32 bit i386.
+int RegisterContextDarwin_arm::GetSetForNativeRegNum(int reg) {
+ if (reg < fpu_s0)
+ return GPRRegSet;
+ else if (reg < exc_exception)
+ return FPURegSet;
+ else if (reg < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+int RegisterContextDarwin_arm::ReadGPR(bool force) {
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr));
+ }
+ return GetError(GPRRegSet, Read);
+}
+
+int RegisterContextDarwin_arm::ReadFPU(bool force) {
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu));
+ }
+ return GetError(FPURegSet, Read);
+}
+
+int RegisterContextDarwin_arm::ReadEXC(bool force) {
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadEXC(GetThreadID(), set, exc));
+ }
+ return GetError(EXCRegSet, Read);
+}
+
+int RegisterContextDarwin_arm::ReadDBG(bool force) {
+ int set = DBGRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadDBG(GetThreadID(), set, dbg));
+ }
+ return GetError(DBGRegSet, Read);
+}
+
+int RegisterContextDarwin_arm::WriteGPR() {
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr));
+ SetError(set, Read, -1);
+ return GetError(GPRRegSet, Write);
+}
+
+int RegisterContextDarwin_arm::WriteFPU() {
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu));
+ SetError(set, Read, -1);
+ return GetError(FPURegSet, Write);
+}
+
+int RegisterContextDarwin_arm::WriteEXC() {
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc));
+ SetError(set, Read, -1);
+ return GetError(EXCRegSet, Write);
+}
+
+int RegisterContextDarwin_arm::WriteDBG() {
+ int set = DBGRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteDBG(GetThreadID(), set, dbg));
+ SetError(set, Read, -1);
+ return GetError(DBGRegSet, Write);
+}
+
+int RegisterContextDarwin_arm::ReadRegisterSet(uint32_t set, bool force) {
+ switch (set) {
+ case GPRRegSet:
+ return ReadGPR(force);
+ case GPRAltRegSet:
+ return ReadGPR(force);
+ case FPURegSet:
+ return ReadFPU(force);
+ case EXCRegSet:
+ return ReadEXC(force);
+ case DBGRegSet:
+ return ReadDBG(force);
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+int RegisterContextDarwin_arm::WriteRegisterSet(uint32_t set) {
+ // Make sure we have a valid context to set.
+ if (RegisterSetIsCached(set)) {
+ switch (set) {
+ case GPRRegSet:
+ return WriteGPR();
+ case GPRAltRegSet:
+ return WriteGPR();
+ case FPURegSet:
+ return WriteFPU();
+ case EXCRegSet:
+ return WriteEXC();
+ case DBGRegSet:
+ return WriteDBG();
+ default:
+ break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+void RegisterContextDarwin_arm::LogDBGRegisters(Log *log, const DBG &dbg) {
+ if (log) {
+ for (uint32_t i = 0; i < 16; i++)
+ log->Printf("BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } WVR%-2u/WCR%-2u = { "
+ "0x%8.8x, 0x%8.8x }",
+ i, i, dbg.bvr[i], dbg.bcr[i], i, i, dbg.wvr[i], dbg.wcr[i]);
+ }
+}
+
+bool RegisterContextDarwin_arm::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_arm::GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg) {
+ case gpr_r0:
+ case gpr_r1:
+ case gpr_r2:
+ case gpr_r3:
+ case gpr_r4:
+ case gpr_r5:
+ case gpr_r6:
+ case gpr_r7:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ value.SetUInt32(gpr.r[reg - gpr_r0]);
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ value.SetUInt32(fpu.floats.s[reg], RegisterValue::eTypeFloat);
+ break;
+
+ case fpu_fpscr:
+ value.SetUInt32(fpu.fpscr);
+ break;
+
+ case exc_exception:
+ value.SetUInt32(exc.exception);
+ break;
+ case exc_fsr:
+ value.SetUInt32(exc.fsr);
+ break;
+ case exc_far:
+ value.SetUInt32(exc.far);
+ break;
+
+ default:
+ value.SetValueToInvalid();
+ return false;
+ }
+ return true;
+}
+
+bool RegisterContextDarwin_arm::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg) {
+ case gpr_r0:
+ case gpr_r1:
+ case gpr_r2:
+ case gpr_r3:
+ case gpr_r4:
+ case gpr_r5:
+ case gpr_r6:
+ case gpr_r7:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ gpr.r[reg - gpr_r0] = value.GetAsUInt32();
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ fpu.floats.s[reg] = value.GetAsUInt32();
+ break;
+
+ case fpu_fpscr:
+ fpu.fpscr = value.GetAsUInt32();
+ break;
+
+ case exc_exception:
+ exc.exception = value.GetAsUInt32();
+ break;
+ case exc_fsr:
+ exc.fsr = value.GetAsUInt32();
+ break;
+ case exc_far:
+ exc.far = value.GetAsUInt32();
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == KERN_SUCCESS;
+}
+
+bool RegisterContextDarwin_arm::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0);
+ if (data_sp && ReadGPR(false) == KERN_SUCCESS &&
+ ReadFPU(false) == KERN_SUCCESS && ReadEXC(false) == KERN_SUCCESS) {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextDarwin_arm::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy(&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy(&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy(&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteFPU() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteEXC() == KERN_SUCCESS)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+uint32_t RegisterContextDarwin_arm::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t reg) {
+ if (kind == eRegisterKindGeneric) {
+ switch (reg) {
+ case LLDB_REGNUM_GENERIC_PC:
+ return gpr_pc;
+ case LLDB_REGNUM_GENERIC_SP:
+ return gpr_sp;
+ case LLDB_REGNUM_GENERIC_FP:
+ return gpr_r7;
+ case LLDB_REGNUM_GENERIC_RA:
+ return gpr_lr;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ return gpr_cpsr;
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindDWARF) {
+ switch (reg) {
+ case dwarf_r0:
+ return gpr_r0;
+ case dwarf_r1:
+ return gpr_r1;
+ case dwarf_r2:
+ return gpr_r2;
+ case dwarf_r3:
+ return gpr_r3;
+ case dwarf_r4:
+ return gpr_r4;
+ case dwarf_r5:
+ return gpr_r5;
+ case dwarf_r6:
+ return gpr_r6;
+ case dwarf_r7:
+ return gpr_r7;
+ case dwarf_r8:
+ return gpr_r8;
+ case dwarf_r9:
+ return gpr_r9;
+ case dwarf_r10:
+ return gpr_r10;
+ case dwarf_r11:
+ return gpr_r11;
+ case dwarf_r12:
+ return gpr_r12;
+ case dwarf_sp:
+ return gpr_sp;
+ case dwarf_lr:
+ return gpr_lr;
+ case dwarf_pc:
+ return gpr_pc;
+ case dwarf_spsr:
+ return gpr_cpsr;
+
+ case dwarf_s0:
+ return fpu_s0;
+ case dwarf_s1:
+ return fpu_s1;
+ case dwarf_s2:
+ return fpu_s2;
+ case dwarf_s3:
+ return fpu_s3;
+ case dwarf_s4:
+ return fpu_s4;
+ case dwarf_s5:
+ return fpu_s5;
+ case dwarf_s6:
+ return fpu_s6;
+ case dwarf_s7:
+ return fpu_s7;
+ case dwarf_s8:
+ return fpu_s8;
+ case dwarf_s9:
+ return fpu_s9;
+ case dwarf_s10:
+ return fpu_s10;
+ case dwarf_s11:
+ return fpu_s11;
+ case dwarf_s12:
+ return fpu_s12;
+ case dwarf_s13:
+ return fpu_s13;
+ case dwarf_s14:
+ return fpu_s14;
+ case dwarf_s15:
+ return fpu_s15;
+ case dwarf_s16:
+ return fpu_s16;
+ case dwarf_s17:
+ return fpu_s17;
+ case dwarf_s18:
+ return fpu_s18;
+ case dwarf_s19:
+ return fpu_s19;
+ case dwarf_s20:
+ return fpu_s20;
+ case dwarf_s21:
+ return fpu_s21;
+ case dwarf_s22:
+ return fpu_s22;
+ case dwarf_s23:
+ return fpu_s23;
+ case dwarf_s24:
+ return fpu_s24;
+ case dwarf_s25:
+ return fpu_s25;
+ case dwarf_s26:
+ return fpu_s26;
+ case dwarf_s27:
+ return fpu_s27;
+ case dwarf_s28:
+ return fpu_s28;
+ case dwarf_s29:
+ return fpu_s29;
+ case dwarf_s30:
+ return fpu_s30;
+ case dwarf_s31:
+ return fpu_s31;
+
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindEHFrame) {
+ switch (reg) {
+ case ehframe_r0:
+ return gpr_r0;
+ case ehframe_r1:
+ return gpr_r1;
+ case ehframe_r2:
+ return gpr_r2;
+ case ehframe_r3:
+ return gpr_r3;
+ case ehframe_r4:
+ return gpr_r4;
+ case ehframe_r5:
+ return gpr_r5;
+ case ehframe_r6:
+ return gpr_r6;
+ case ehframe_r7:
+ return gpr_r7;
+ case ehframe_r8:
+ return gpr_r8;
+ case ehframe_r9:
+ return gpr_r9;
+ case ehframe_r10:
+ return gpr_r10;
+ case ehframe_r11:
+ return gpr_r11;
+ case ehframe_r12:
+ return gpr_r12;
+ case ehframe_sp:
+ return gpr_sp;
+ case ehframe_lr:
+ return gpr_lr;
+ case ehframe_pc:
+ return gpr_pc;
+ case ehframe_cpsr:
+ return gpr_cpsr;
+ }
+ } else if (kind == eRegisterKindLLDB) {
+ return reg;
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+uint32_t RegisterContextDarwin_arm::NumSupportedHardwareBreakpoints() {
+#if defined(__APPLE__) && defined(__arm__)
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many breakpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_breakpoints = UINT32_MAX;
+ if (g_num_supported_hw_breakpoints == UINT32_MAX) {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_breakpoints = 0;
+
+ uint32_t register_DBGDIDR;
+
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r"(register_DBGDIDR));
+ g_num_supported_hw_breakpoints = Bits32(register_DBGDIDR, 27, 24);
+ // Zero is reserved for the BRP count, so don't increment it if it is zero
+ if (g_num_supported_hw_breakpoints > 0)
+ g_num_supported_hw_breakpoints++;
+ // if (log) log->Printf ("DBGDIDR=0x%8.8x (number BRP pairs = %u)",
+ // register_DBGDIDR, g_num_supported_hw_breakpoints);
+ }
+ return g_num_supported_hw_breakpoints;
+#else
+ // TODO: figure out remote case here!
+ return 6;
+#endif
+}
+
+uint32_t RegisterContextDarwin_arm::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ // Make sure our address isn't bogus
+ if (addr & 1)
+ return LLDB_INVALID_INDEX32;
+
+ int kret = ReadDBG(false);
+
+ if (kret == KERN_SUCCESS) {
+ const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints();
+ uint32_t i;
+ for (i = 0; i < num_hw_breakpoints; ++i) {
+ if ((dbg.bcr[i] & BCR_ENABLE) == 0)
+ break; // We found an available hw breakpoint slot (in i)
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_breakpoints) {
+ // Make sure bits 1:0 are clear in our address
+ dbg.bvr[i] = addr & ~((lldb::addr_t)3);
+
+ if (size == 2 || addr & 2) {
+ uint32_t byte_addr_select = (addr & 2) ? BAS_IMVA_2_3 : BAS_IMVA_0_1;
+
+ // We have a thumb breakpoint
+ // We have an ARM breakpoint
+ dbg.bcr[i] = BCR_M_IMVA_MATCH | // Stop on address mismatch
+ byte_addr_select | // Set the correct byte address select
+ // so we only trigger on the correct
+ // opcode
+ S_USER | // Which modes should this breakpoint stop in?
+ BCR_ENABLE; // Enable this hardware breakpoint
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint(
+ // addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x /
+ // 0x%8.8x (Thumb)",
+ // addr,
+ // size,
+ // i,
+ // i,
+ // dbg.bvr[i],
+ // dbg.bcr[i]);
+ } else if (size == 4) {
+ // We have an ARM breakpoint
+ dbg.bcr[i] =
+ BCR_M_IMVA_MATCH | // Stop on address mismatch
+ BAS_IMVA_ALL | // Stop on any of the four bytes following the IMVA
+ S_USER | // Which modes should this breakpoint stop in?
+ BCR_ENABLE; // Enable this hardware breakpoint
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint(
+ // addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x /
+ // 0x%8.8x (ARM)",
+ // addr,
+ // size,
+ // i,
+ // i,
+ // dbg.bvr[i],
+ // dbg.bcr[i]);
+ }
+
+ kret = WriteDBG();
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint()
+ // WriteDBG() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ }
+ // else
+ // {
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint(addr =
+ // %8.8p, size = %u) => all hardware breakpoint resources are
+ // being used.", addr, size);
+ // }
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextDarwin_arm::ClearHardwareBreakpoint(uint32_t hw_index) {
+ int kret = ReadDBG(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
+ if (kret == KERN_SUCCESS) {
+ if (hw_index < num_hw_points) {
+ dbg.bcr[hw_index] = 0;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::SetHardwareBreakpoint( %u ) -
+ // BVR%u = 0x%8.8x BCR%u = 0x%8.8x",
+ // hw_index,
+ // hw_index,
+ // dbg.bvr[hw_index],
+ // hw_index,
+ // dbg.bcr[hw_index]);
+
+ kret = WriteDBG();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32_t RegisterContextDarwin_arm::NumSupportedHardwareWatchpoints() {
+#if defined(__APPLE__) && defined(__arm__)
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many watchpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_watchpoints = UINT32_MAX;
+ if (g_num_supported_hw_watchpoints == UINT32_MAX) {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_watchpoints = 0;
+
+ uint32_t register_DBGDIDR;
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r"(register_DBGDIDR));
+ g_num_supported_hw_watchpoints = Bits32(register_DBGDIDR, 31, 28) + 1;
+ // if (log) log->Printf ("DBGDIDR=0x%8.8x (number WRP pairs = %u)",
+ // register_DBGDIDR, g_num_supported_hw_watchpoints);
+ }
+ return g_num_supported_hw_watchpoints;
+#else
+ // TODO: figure out remote case here!
+ return 2;
+#endif
+}
+
+uint32_t RegisterContextDarwin_arm::SetHardwareWatchpoint(lldb::addr_t addr,
+ size_t size,
+ bool read,
+ bool write) {
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint(addr = %8.8p, size
+ // = %u, read = %u, write = %u)", addr, size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can't watch zero bytes
+ if (size == 0)
+ return LLDB_INVALID_INDEX32;
+
+ // We must watch for either read or write
+ if (!read && !write)
+ return LLDB_INVALID_INDEX32;
+
+ // Can't watch more than 4 bytes per WVR/WCR pair
+ if (size > 4)
+ return LLDB_INVALID_INDEX32;
+
+ // We can only watch up to four bytes that follow a 4 byte aligned address
+ // per watchpoint register pair. Since we have at most so we can only watch
+ // until the next 4 byte boundary and we need to make sure we can properly
+ // encode this.
+ uint32_t addr_word_offset = addr % 4;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() -
+ // addr_word_offset = 0x%8.8x", addr_word_offset);
+
+ uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() - byte_mask =
+ // 0x%8.8x", byte_mask);
+ if (byte_mask > 0xfu)
+ return LLDB_INVALID_INDEX32;
+
+ // Read the debug state
+ int kret = ReadDBG(false);
+
+ if (kret == KERN_SUCCESS) {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ for (i = 0; i < num_hw_watchpoints; ++i) {
+ if ((dbg.wcr[i] & WCR_ENABLE) == 0)
+ break; // We found an available hw breakpoint slot (in i)
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_watchpoints) {
+ // Make the byte_mask into a valid Byte Address Select mask
+ uint32_t byte_address_select = byte_mask << 5;
+ // Make sure bits 1:0 are clear in our address
+ dbg.wvr[i] = addr & ~((lldb::addr_t)3);
+ dbg.wcr[i] = byte_address_select | // Which bytes that follow the IMVA
+ // that we will watch
+ S_USER | // Stop only in user mode
+ (read ? WCR_LOAD : 0) | // Stop on read access?
+ (write ? WCR_STORE : 0) | // Stop on write access?
+ WCR_ENABLE; // Enable this watchpoint;
+
+ kret = WriteDBG();
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint()
+ // WriteDBG() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ } else {
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint(): All
+ // hardware resources (%u) are in use.", num_hw_watchpoints);
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextDarwin_arm::ClearHardwareWatchpoint(uint32_t hw_index) {
+ int kret = ReadDBG(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (kret == KERN_SUCCESS) {
+ if (hw_index < num_hw_points) {
+ dbg.wcr[hw_index] = 0;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::ClearHardwareWatchpoint( %u ) -
+ // WVR%u = 0x%8.8x WCR%u = 0x%8.8x",
+ // hw_index,
+ // hw_index,
+ // dbg.wvr[hw_index],
+ // hw_index,
+ // dbg.wcr[hw_index]);
+
+ kret = WriteDBG();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h
new file mode 100644
index 000000000000..d7c1809a3222
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h
@@ -0,0 +1,264 @@
+//===-- RegisterContextDarwin_arm.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextDarwin_arm_h_
+#define liblldb_RegisterContextDarwin_arm_h_
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+// BCR address match type
+#define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21))
+#define BCR_M_CONTEXT_ID_MATCH ((uint32_t)(1u << 21))
+#define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21))
+#define BCR_M_RESERVED ((uint32_t)(3u << 21))
+
+// Link a BVR/BCR or WVR/WCR pair to another
+#define E_ENABLE_LINKING ((uint32_t)(1u << 20))
+
+// Byte Address Select
+#define BAS_IMVA_PLUS_0 ((uint32_t)(1u << 5))
+#define BAS_IMVA_PLUS_1 ((uint32_t)(1u << 6))
+#define BAS_IMVA_PLUS_2 ((uint32_t)(1u << 7))
+#define BAS_IMVA_PLUS_3 ((uint32_t)(1u << 8))
+#define BAS_IMVA_0_1 ((uint32_t)(3u << 5))
+#define BAS_IMVA_2_3 ((uint32_t)(3u << 7))
+#define BAS_IMVA_ALL ((uint32_t)(0xfu << 5))
+
+// Break only in privileged or user mode
+#define S_RSVD ((uint32_t)(0u << 1))
+#define S_PRIV ((uint32_t)(1u << 1))
+#define S_USER ((uint32_t)(2u << 1))
+#define S_PRIV_USER ((S_PRIV) | (S_USER))
+
+#define BCR_ENABLE ((uint32_t)(1u))
+#define WCR_ENABLE ((uint32_t)(1u))
+
+// Watchpoint load/store
+#define WCR_LOAD ((uint32_t)(1u << 3))
+#define WCR_STORE ((uint32_t)(1u << 4))
+
+class RegisterContextDarwin_arm : public lldb_private::RegisterContext {
+public:
+ RegisterContextDarwin_arm(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ ~RegisterContextDarwin_arm() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ uint32_t NumSupportedHardwareBreakpoints() override;
+
+ uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+
+ bool ClearHardwareBreakpoint(uint32_t hw_idx) override;
+
+ uint32_t NumSupportedHardwareWatchpoints() override;
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write) override;
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index) override;
+
+ struct GPR {
+ uint32_t r[16]; // R0-R15
+ uint32_t cpsr; // CPSR
+ };
+
+ struct QReg {
+ uint8_t bytes[16];
+ };
+
+ struct FPU {
+ union {
+ uint32_t s[32];
+ uint64_t d[32];
+ QReg q[16]; // the 128-bit NEON registers
+ } floats;
+ uint32_t fpscr;
+ };
+
+ // struct NeonReg
+ // {
+ // uint8_t bytes[16];
+ // };
+ //
+ // struct VFPv3
+ // {
+ // union {
+ // uint32_t s[32];
+ // uint64_t d[32];
+ // NeonReg q[16];
+ // } v3;
+ // uint32_t fpscr;
+ // };
+
+ struct EXC {
+ uint32_t exception;
+ uint32_t fsr; /* Fault status */
+ uint32_t far; /* Virtual Fault Address */
+ };
+
+ struct DBG {
+ uint32_t bvr[16];
+ uint32_t bcr[16];
+ uint32_t wvr[16];
+ uint32_t wcr[16];
+ };
+
+ static void LogDBGRegisters(lldb_private::Log *log, const DBG &dbg);
+
+protected:
+ enum {
+ GPRRegSet = 1, // ARM_THREAD_STATE
+ GPRAltRegSet = 9, // ARM_THREAD_STATE32
+ FPURegSet = 2, // ARM_VFP_STATE
+ EXCRegSet = 3, // ARM_EXCEPTION_STATE
+ DBGRegSet = 4 // ARM_DEBUG_STATE
+ };
+
+ enum {
+ GPRWordCount = sizeof(GPR) / sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU) / sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC) / sizeof(uint32_t),
+ DBGWordCount = sizeof(DBG) / sizeof(uint32_t)
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ DBG dbg;
+ int gpr_errs[2]; // Read/Write errors
+ int fpu_errs[2]; // Read/Write errors
+ int exc_errs[2]; // Read/Write errors
+ int dbg_errs[2]; // Read/Write errors
+
+ void InvalidateAllRegisterStates() {
+ SetError(GPRRegSet, Read, -1);
+ SetError(FPURegSet, Read, -1);
+ SetError(EXCRegSet, Read, -1);
+ }
+
+ int GetError(int flavor, uint32_t err_idx) const {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case GPRRegSet:
+ return gpr_errs[err_idx];
+ case FPURegSet:
+ return fpu_errs[err_idx];
+ case EXCRegSet:
+ return exc_errs[err_idx];
+ case DBGRegSet:
+ return dbg_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+
+ bool SetError(int flavor, uint32_t err_idx, int err) {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case DBGRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+
+ bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; }
+
+ int ReadGPR(bool force);
+
+ int ReadFPU(bool force);
+
+ int ReadEXC(bool force);
+
+ int ReadDBG(bool force);
+
+ int WriteGPR();
+
+ int WriteFPU();
+
+ int WriteEXC();
+
+ int WriteDBG();
+
+ // Subclasses override these to do the actual reading.
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) { return -1; }
+
+ virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0;
+
+ virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0;
+
+ virtual int DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) = 0;
+
+ virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0;
+
+ virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0;
+
+ virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0;
+
+ virtual int DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) = 0;
+
+ int ReadRegisterSet(uint32_t set, bool force);
+
+ int WriteRegisterSet(uint32_t set);
+
+ static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num);
+
+ static int GetSetForNativeRegNum(int reg_num);
+
+ static size_t GetRegisterInfosCount();
+
+ static const lldb_private::RegisterInfo *GetRegisterInfos();
+};
+
+#endif // liblldb_RegisterContextDarwin_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.cpp
new file mode 100644
index 000000000000..85d518a487bf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.cpp
@@ -0,0 +1,1043 @@
+//===-- RegisterContextDarwin_arm64.cpp ---------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextDarwin_arm64.h"
+#include "RegisterContextDarwinConstants.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Compiler.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+
+#include <memory>
+
+// Support building against older versions of LLVM, this macro was added
+// recently.
+#ifndef LLVM_EXTENSION
+#define LLVM_EXTENSION
+#endif
+
+#include "Utility/ARM64_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define GPR_OFFSET(idx) ((idx)*8)
+#define GPR_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::GPR, reg))
+
+#define FPU_OFFSET(idx) ((idx)*16 + sizeof(RegisterContextDarwin_arm64::GPR))
+#define FPU_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::FPU, reg))
+
+#define EXC_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::EXC, reg) + \
+ sizeof(RegisterContextDarwin_arm64::GPR) + \
+ sizeof(RegisterContextDarwin_arm64::FPU))
+#define DBG_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::DBG, reg) + \
+ sizeof(RegisterContextDarwin_arm64::GPR) + \
+ sizeof(RegisterContextDarwin_arm64::FPU) + \
+ sizeof(RegisterContextDarwin_arm64::EXC))
+
+#define DEFINE_DBG(reg, i) \
+ #reg, NULL, \
+ sizeof(((RegisterContextDarwin_arm64::DBG *) NULL)->reg[i]), \
+ DBG_OFFSET_NAME(reg[i]), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM }, \
+ NULL, NULL, NULL, 0
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterContextDarwin_arm64::GPR) + \
+ sizeof(RegisterContextDarwin_arm64::FPU) + \
+ sizeof(RegisterContextDarwin_arm64::EXC))
+
+// Include RegisterInfos_arm64 to declare our g_register_infos_arm64 structure.
+#define DECLARE_REGISTER_INFOS_ARM64_STRUCT
+#include "RegisterInfos_arm64.h"
+#undef DECLARE_REGISTER_INFOS_ARM64_STRUCT
+
+// General purpose registers
+static uint32_t g_gpr_regnums[] = {
+ gpr_x0, gpr_x1, gpr_x2, gpr_x3, gpr_x4, gpr_x5, gpr_x6,
+ gpr_x7, gpr_x8, gpr_x9, gpr_x10, gpr_x11, gpr_x12, gpr_x13,
+ gpr_x14, gpr_x15, gpr_x16, gpr_x17, gpr_x18, gpr_x19, gpr_x20,
+ gpr_x21, gpr_x22, gpr_x23, gpr_x24, gpr_x25, gpr_x26, gpr_x27,
+ gpr_x28, gpr_fp, gpr_lr, gpr_sp, gpr_pc, gpr_cpsr};
+
+// Floating point registers
+static uint32_t g_fpu_regnums[] = {
+ fpu_v0, fpu_v1, fpu_v2, fpu_v3, fpu_v4, fpu_v5, fpu_v6,
+ fpu_v7, fpu_v8, fpu_v9, fpu_v10, fpu_v11, fpu_v12, fpu_v13,
+ fpu_v14, fpu_v15, fpu_v16, fpu_v17, fpu_v18, fpu_v19, fpu_v20,
+ fpu_v21, fpu_v22, fpu_v23, fpu_v24, fpu_v25, fpu_v26, fpu_v27,
+ fpu_v28, fpu_v29, fpu_v30, fpu_v31, fpu_fpsr, fpu_fpcr};
+
+// Exception registers
+
+static uint32_t g_exc_regnums[] = {exc_far, exc_esr, exc_exception};
+
+static size_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos_arm64_le);
+
+RegisterContextDarwin_arm64::RegisterContextDarwin_arm64(
+ Thread &thread, uint32_t concrete_frame_idx)
+ : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextDarwin_arm64::~RegisterContextDarwin_arm64() {}
+
+void RegisterContextDarwin_arm64::InvalidateAllRegisters() {
+ InvalidateAllRegisterStates();
+}
+
+size_t RegisterContextDarwin_arm64::GetRegisterCount() {
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_arm64::GetRegisterInfoAtIndex(size_t reg) {
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos_arm64_le[reg];
+ return nullptr;
+}
+
+size_t RegisterContextDarwin_arm64::GetRegisterInfosCount() {
+ return k_num_register_infos;
+}
+
+const RegisterInfo *RegisterContextDarwin_arm64::GetRegisterInfos() {
+ return g_register_infos_arm64_le;
+}
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = llvm::array_lengthof(g_gpr_regnums);
+const size_t k_num_fpu_registers = llvm::array_lengthof(g_fpu_regnums);
+const size_t k_num_exc_registers = llvm::array_lengthof(g_exc_regnums);
+
+// Register set definitions. The first definitions at register set index of
+// zero is for all registers, followed by other registers sets. The register
+// information for the all register set need not be filled in.
+static const RegisterSet g_reg_sets[] = {
+ {
+ "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums,
+ },
+ {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums},
+ {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}};
+
+const size_t k_num_regsets = llvm::array_lengthof(g_reg_sets);
+
+size_t RegisterContextDarwin_arm64::GetRegisterSetCount() {
+ return k_num_regsets;
+}
+
+const RegisterSet *RegisterContextDarwin_arm64::GetRegisterSet(size_t reg_set) {
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return nullptr;
+}
+
+// Register information definitions for arm64
+int RegisterContextDarwin_arm64::GetSetForNativeRegNum(int reg) {
+ if (reg < fpu_v0)
+ return GPRRegSet;
+ else if (reg < exc_far)
+ return FPURegSet;
+ else if (reg < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+int RegisterContextDarwin_arm64::ReadGPR(bool force) {
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr));
+ }
+ return GetError(GPRRegSet, Read);
+}
+
+int RegisterContextDarwin_arm64::ReadFPU(bool force) {
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu));
+ }
+ return GetError(FPURegSet, Read);
+}
+
+int RegisterContextDarwin_arm64::ReadEXC(bool force) {
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadEXC(GetThreadID(), set, exc));
+ }
+ return GetError(EXCRegSet, Read);
+}
+
+int RegisterContextDarwin_arm64::ReadDBG(bool force) {
+ int set = DBGRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadDBG(GetThreadID(), set, dbg));
+ }
+ return GetError(DBGRegSet, Read);
+}
+
+int RegisterContextDarwin_arm64::WriteGPR() {
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr));
+ SetError(set, Read, -1);
+ return GetError(GPRRegSet, Write);
+}
+
+int RegisterContextDarwin_arm64::WriteFPU() {
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu));
+ SetError(set, Read, -1);
+ return GetError(FPURegSet, Write);
+}
+
+int RegisterContextDarwin_arm64::WriteEXC() {
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc));
+ SetError(set, Read, -1);
+ return GetError(EXCRegSet, Write);
+}
+
+int RegisterContextDarwin_arm64::WriteDBG() {
+ int set = DBGRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteDBG(GetThreadID(), set, dbg));
+ SetError(set, Read, -1);
+ return GetError(DBGRegSet, Write);
+}
+
+int RegisterContextDarwin_arm64::ReadRegisterSet(uint32_t set, bool force) {
+ switch (set) {
+ case GPRRegSet:
+ return ReadGPR(force);
+ case FPURegSet:
+ return ReadFPU(force);
+ case EXCRegSet:
+ return ReadEXC(force);
+ case DBGRegSet:
+ return ReadDBG(force);
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+int RegisterContextDarwin_arm64::WriteRegisterSet(uint32_t set) {
+ // Make sure we have a valid context to set.
+ if (RegisterSetIsCached(set)) {
+ switch (set) {
+ case GPRRegSet:
+ return WriteGPR();
+ case FPURegSet:
+ return WriteFPU();
+ case EXCRegSet:
+ return WriteEXC();
+ case DBGRegSet:
+ return WriteDBG();
+ default:
+ break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+void RegisterContextDarwin_arm64::LogDBGRegisters(Log *log, const DBG &dbg) {
+ if (log) {
+ for (uint32_t i = 0; i < 16; i++)
+ log->Printf("BVR%-2u/BCR%-2u = { 0x%8.8" PRIu64 ", 0x%8.8" PRIu64
+ " } WVR%-2u/WCR%-2u "
+ "= { 0x%8.8" PRIu64 ", 0x%8.8" PRIu64 " }",
+ i, i, dbg.bvr[i], dbg.bcr[i], i, i, dbg.wvr[i], dbg.wcr[i]);
+ }
+}
+
+bool RegisterContextDarwin_arm64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_arm64::GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg) {
+ case gpr_x0:
+ case gpr_x1:
+ case gpr_x2:
+ case gpr_x3:
+ case gpr_x4:
+ case gpr_x5:
+ case gpr_x6:
+ case gpr_x7:
+ case gpr_x8:
+ case gpr_x9:
+ case gpr_x10:
+ case gpr_x11:
+ case gpr_x12:
+ case gpr_x13:
+ case gpr_x14:
+ case gpr_x15:
+ case gpr_x16:
+ case gpr_x17:
+ case gpr_x18:
+ case gpr_x19:
+ case gpr_x20:
+ case gpr_x21:
+ case gpr_x22:
+ case gpr_x23:
+ case gpr_x24:
+ case gpr_x25:
+ case gpr_x26:
+ case gpr_x27:
+ case gpr_x28:
+ value.SetUInt64(gpr.x[reg - gpr_x0]);
+ break;
+ case gpr_fp:
+ value.SetUInt64(gpr.fp);
+ break;
+ case gpr_sp:
+ value.SetUInt64(gpr.sp);
+ break;
+ case gpr_lr:
+ value.SetUInt64(gpr.lr);
+ break;
+ case gpr_pc:
+ value.SetUInt64(gpr.pc);
+ break;
+ case gpr_cpsr:
+ value.SetUInt64(gpr.cpsr);
+ break;
+
+ case gpr_w0:
+ case gpr_w1:
+ case gpr_w2:
+ case gpr_w3:
+ case gpr_w4:
+ case gpr_w5:
+ case gpr_w6:
+ case gpr_w7:
+ case gpr_w8:
+ case gpr_w9:
+ case gpr_w10:
+ case gpr_w11:
+ case gpr_w12:
+ case gpr_w13:
+ case gpr_w14:
+ case gpr_w15:
+ case gpr_w16:
+ case gpr_w17:
+ case gpr_w18:
+ case gpr_w19:
+ case gpr_w20:
+ case gpr_w21:
+ case gpr_w22:
+ case gpr_w23:
+ case gpr_w24:
+ case gpr_w25:
+ case gpr_w26:
+ case gpr_w27:
+ case gpr_w28: {
+ ProcessSP process_sp(m_thread.GetProcess());
+ if (process_sp.get()) {
+ DataExtractor regdata(&gpr.x[reg - gpr_w0], 8, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ offset_t offset = 0;
+ uint64_t retval = regdata.GetMaxU64(&offset, 8);
+ uint32_t retval_lower32 = static_cast<uint32_t>(retval & 0xffffffff);
+ value.SetUInt32(retval_lower32);
+ }
+ } break;
+
+ case fpu_v0:
+ case fpu_v1:
+ case fpu_v2:
+ case fpu_v3:
+ case fpu_v4:
+ case fpu_v5:
+ case fpu_v6:
+ case fpu_v7:
+ case fpu_v8:
+ case fpu_v9:
+ case fpu_v10:
+ case fpu_v11:
+ case fpu_v12:
+ case fpu_v13:
+ case fpu_v14:
+ case fpu_v15:
+ case fpu_v16:
+ case fpu_v17:
+ case fpu_v18:
+ case fpu_v19:
+ case fpu_v20:
+ case fpu_v21:
+ case fpu_v22:
+ case fpu_v23:
+ case fpu_v24:
+ case fpu_v25:
+ case fpu_v26:
+ case fpu_v27:
+ case fpu_v28:
+ case fpu_v29:
+ case fpu_v30:
+ case fpu_v31:
+ value.SetBytes(fpu.v[reg - fpu_v0].bytes.buffer, reg_info->byte_size,
+ endian::InlHostByteOrder());
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31: {
+ ProcessSP process_sp(m_thread.GetProcess());
+ if (process_sp.get()) {
+ DataExtractor regdata(&fpu.v[reg - fpu_s0], 4, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ offset_t offset = 0;
+ value.SetFloat(regdata.GetFloat(&offset));
+ }
+ } break;
+
+ case fpu_d0:
+ case fpu_d1:
+ case fpu_d2:
+ case fpu_d3:
+ case fpu_d4:
+ case fpu_d5:
+ case fpu_d6:
+ case fpu_d7:
+ case fpu_d8:
+ case fpu_d9:
+ case fpu_d10:
+ case fpu_d11:
+ case fpu_d12:
+ case fpu_d13:
+ case fpu_d14:
+ case fpu_d15:
+ case fpu_d16:
+ case fpu_d17:
+ case fpu_d18:
+ case fpu_d19:
+ case fpu_d20:
+ case fpu_d21:
+ case fpu_d22:
+ case fpu_d23:
+ case fpu_d24:
+ case fpu_d25:
+ case fpu_d26:
+ case fpu_d27:
+ case fpu_d28:
+ case fpu_d29:
+ case fpu_d30:
+ case fpu_d31: {
+ ProcessSP process_sp(m_thread.GetProcess());
+ if (process_sp.get()) {
+ DataExtractor regdata(&fpu.v[reg - fpu_s0], 8, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ offset_t offset = 0;
+ value.SetDouble(regdata.GetDouble(&offset));
+ }
+ } break;
+
+ case fpu_fpsr:
+ value.SetUInt32(fpu.fpsr);
+ break;
+
+ case fpu_fpcr:
+ value.SetUInt32(fpu.fpcr);
+ break;
+
+ case exc_exception:
+ value.SetUInt32(exc.exception);
+ break;
+ case exc_esr:
+ value.SetUInt32(exc.esr);
+ break;
+ case exc_far:
+ value.SetUInt64(exc.far);
+ break;
+
+ default:
+ value.SetValueToInvalid();
+ return false;
+ }
+ return true;
+}
+
+bool RegisterContextDarwin_arm64::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg) {
+ case gpr_x0:
+ case gpr_x1:
+ case gpr_x2:
+ case gpr_x3:
+ case gpr_x4:
+ case gpr_x5:
+ case gpr_x6:
+ case gpr_x7:
+ case gpr_x8:
+ case gpr_x9:
+ case gpr_x10:
+ case gpr_x11:
+ case gpr_x12:
+ case gpr_x13:
+ case gpr_x14:
+ case gpr_x15:
+ case gpr_x16:
+ case gpr_x17:
+ case gpr_x18:
+ case gpr_x19:
+ case gpr_x20:
+ case gpr_x21:
+ case gpr_x22:
+ case gpr_x23:
+ case gpr_x24:
+ case gpr_x25:
+ case gpr_x26:
+ case gpr_x27:
+ case gpr_x28:
+ case gpr_fp:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ gpr.x[reg - gpr_x0] = value.GetAsUInt64();
+ break;
+
+ case fpu_v0:
+ case fpu_v1:
+ case fpu_v2:
+ case fpu_v3:
+ case fpu_v4:
+ case fpu_v5:
+ case fpu_v6:
+ case fpu_v7:
+ case fpu_v8:
+ case fpu_v9:
+ case fpu_v10:
+ case fpu_v11:
+ case fpu_v12:
+ case fpu_v13:
+ case fpu_v14:
+ case fpu_v15:
+ case fpu_v16:
+ case fpu_v17:
+ case fpu_v18:
+ case fpu_v19:
+ case fpu_v20:
+ case fpu_v21:
+ case fpu_v22:
+ case fpu_v23:
+ case fpu_v24:
+ case fpu_v25:
+ case fpu_v26:
+ case fpu_v27:
+ case fpu_v28:
+ case fpu_v29:
+ case fpu_v30:
+ case fpu_v31:
+ ::memcpy(fpu.v[reg - fpu_v0].bytes.buffer, value.GetBytes(),
+ value.GetByteSize());
+ break;
+
+ case fpu_fpsr:
+ fpu.fpsr = value.GetAsUInt32();
+ break;
+
+ case fpu_fpcr:
+ fpu.fpcr = value.GetAsUInt32();
+ break;
+
+ case exc_exception:
+ exc.exception = value.GetAsUInt32();
+ break;
+ case exc_esr:
+ exc.esr = value.GetAsUInt32();
+ break;
+ case exc_far:
+ exc.far = value.GetAsUInt64();
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == KERN_SUCCESS;
+}
+
+bool RegisterContextDarwin_arm64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0);
+ if (ReadGPR(false) == KERN_SUCCESS && ReadFPU(false) == KERN_SUCCESS &&
+ ReadEXC(false) == KERN_SUCCESS) {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextDarwin_arm64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy(&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy(&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy(&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteFPU() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteEXC() == KERN_SUCCESS)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+uint32_t RegisterContextDarwin_arm64::ConvertRegisterKindToRegisterNumber(
+ RegisterKind kind, uint32_t reg) {
+ if (kind == eRegisterKindGeneric) {
+ switch (reg) {
+ case LLDB_REGNUM_GENERIC_PC:
+ return gpr_pc;
+ case LLDB_REGNUM_GENERIC_SP:
+ return gpr_sp;
+ case LLDB_REGNUM_GENERIC_FP:
+ return gpr_fp;
+ case LLDB_REGNUM_GENERIC_RA:
+ return gpr_lr;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ return gpr_cpsr;
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindDWARF) {
+ switch (reg) {
+ case arm64_dwarf::x0:
+ return gpr_x0;
+ case arm64_dwarf::x1:
+ return gpr_x1;
+ case arm64_dwarf::x2:
+ return gpr_x2;
+ case arm64_dwarf::x3:
+ return gpr_x3;
+ case arm64_dwarf::x4:
+ return gpr_x4;
+ case arm64_dwarf::x5:
+ return gpr_x5;
+ case arm64_dwarf::x6:
+ return gpr_x6;
+ case arm64_dwarf::x7:
+ return gpr_x7;
+ case arm64_dwarf::x8:
+ return gpr_x8;
+ case arm64_dwarf::x9:
+ return gpr_x9;
+ case arm64_dwarf::x10:
+ return gpr_x10;
+ case arm64_dwarf::x11:
+ return gpr_x11;
+ case arm64_dwarf::x12:
+ return gpr_x12;
+ case arm64_dwarf::x13:
+ return gpr_x13;
+ case arm64_dwarf::x14:
+ return gpr_x14;
+ case arm64_dwarf::x15:
+ return gpr_x15;
+ case arm64_dwarf::x16:
+ return gpr_x16;
+ case arm64_dwarf::x17:
+ return gpr_x17;
+ case arm64_dwarf::x18:
+ return gpr_x18;
+ case arm64_dwarf::x19:
+ return gpr_x19;
+ case arm64_dwarf::x20:
+ return gpr_x20;
+ case arm64_dwarf::x21:
+ return gpr_x21;
+ case arm64_dwarf::x22:
+ return gpr_x22;
+ case arm64_dwarf::x23:
+ return gpr_x23;
+ case arm64_dwarf::x24:
+ return gpr_x24;
+ case arm64_dwarf::x25:
+ return gpr_x25;
+ case arm64_dwarf::x26:
+ return gpr_x26;
+ case arm64_dwarf::x27:
+ return gpr_x27;
+ case arm64_dwarf::x28:
+ return gpr_x28;
+
+ case arm64_dwarf::fp:
+ return gpr_fp;
+ case arm64_dwarf::sp:
+ return gpr_sp;
+ case arm64_dwarf::lr:
+ return gpr_lr;
+ case arm64_dwarf::pc:
+ return gpr_pc;
+ case arm64_dwarf::cpsr:
+ return gpr_cpsr;
+
+ case arm64_dwarf::v0:
+ return fpu_v0;
+ case arm64_dwarf::v1:
+ return fpu_v1;
+ case arm64_dwarf::v2:
+ return fpu_v2;
+ case arm64_dwarf::v3:
+ return fpu_v3;
+ case arm64_dwarf::v4:
+ return fpu_v4;
+ case arm64_dwarf::v5:
+ return fpu_v5;
+ case arm64_dwarf::v6:
+ return fpu_v6;
+ case arm64_dwarf::v7:
+ return fpu_v7;
+ case arm64_dwarf::v8:
+ return fpu_v8;
+ case arm64_dwarf::v9:
+ return fpu_v9;
+ case arm64_dwarf::v10:
+ return fpu_v10;
+ case arm64_dwarf::v11:
+ return fpu_v11;
+ case arm64_dwarf::v12:
+ return fpu_v12;
+ case arm64_dwarf::v13:
+ return fpu_v13;
+ case arm64_dwarf::v14:
+ return fpu_v14;
+ case arm64_dwarf::v15:
+ return fpu_v15;
+ case arm64_dwarf::v16:
+ return fpu_v16;
+ case arm64_dwarf::v17:
+ return fpu_v17;
+ case arm64_dwarf::v18:
+ return fpu_v18;
+ case arm64_dwarf::v19:
+ return fpu_v19;
+ case arm64_dwarf::v20:
+ return fpu_v20;
+ case arm64_dwarf::v21:
+ return fpu_v21;
+ case arm64_dwarf::v22:
+ return fpu_v22;
+ case arm64_dwarf::v23:
+ return fpu_v23;
+ case arm64_dwarf::v24:
+ return fpu_v24;
+ case arm64_dwarf::v25:
+ return fpu_v25;
+ case arm64_dwarf::v26:
+ return fpu_v26;
+ case arm64_dwarf::v27:
+ return fpu_v27;
+ case arm64_dwarf::v28:
+ return fpu_v28;
+ case arm64_dwarf::v29:
+ return fpu_v29;
+ case arm64_dwarf::v30:
+ return fpu_v30;
+ case arm64_dwarf::v31:
+ return fpu_v31;
+
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindEHFrame) {
+ switch (reg) {
+ case arm64_ehframe::x0:
+ return gpr_x0;
+ case arm64_ehframe::x1:
+ return gpr_x1;
+ case arm64_ehframe::x2:
+ return gpr_x2;
+ case arm64_ehframe::x3:
+ return gpr_x3;
+ case arm64_ehframe::x4:
+ return gpr_x4;
+ case arm64_ehframe::x5:
+ return gpr_x5;
+ case arm64_ehframe::x6:
+ return gpr_x6;
+ case arm64_ehframe::x7:
+ return gpr_x7;
+ case arm64_ehframe::x8:
+ return gpr_x8;
+ case arm64_ehframe::x9:
+ return gpr_x9;
+ case arm64_ehframe::x10:
+ return gpr_x10;
+ case arm64_ehframe::x11:
+ return gpr_x11;
+ case arm64_ehframe::x12:
+ return gpr_x12;
+ case arm64_ehframe::x13:
+ return gpr_x13;
+ case arm64_ehframe::x14:
+ return gpr_x14;
+ case arm64_ehframe::x15:
+ return gpr_x15;
+ case arm64_ehframe::x16:
+ return gpr_x16;
+ case arm64_ehframe::x17:
+ return gpr_x17;
+ case arm64_ehframe::x18:
+ return gpr_x18;
+ case arm64_ehframe::x19:
+ return gpr_x19;
+ case arm64_ehframe::x20:
+ return gpr_x20;
+ case arm64_ehframe::x21:
+ return gpr_x21;
+ case arm64_ehframe::x22:
+ return gpr_x22;
+ case arm64_ehframe::x23:
+ return gpr_x23;
+ case arm64_ehframe::x24:
+ return gpr_x24;
+ case arm64_ehframe::x25:
+ return gpr_x25;
+ case arm64_ehframe::x26:
+ return gpr_x26;
+ case arm64_ehframe::x27:
+ return gpr_x27;
+ case arm64_ehframe::x28:
+ return gpr_x28;
+ case arm64_ehframe::fp:
+ return gpr_fp;
+ case arm64_ehframe::sp:
+ return gpr_sp;
+ case arm64_ehframe::lr:
+ return gpr_lr;
+ case arm64_ehframe::pc:
+ return gpr_pc;
+ case arm64_ehframe::cpsr:
+ return gpr_cpsr;
+ }
+ } else if (kind == eRegisterKindLLDB) {
+ return reg;
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+uint32_t RegisterContextDarwin_arm64::NumSupportedHardwareWatchpoints() {
+#if defined(__APPLE__) && (defined(__arm64__) || defined(__aarch64__))
+ // autodetect how many watchpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_watchpoints = UINT32_MAX;
+ if (g_num_supported_hw_watchpoints == UINT32_MAX) {
+ size_t len;
+ uint32_t n = 0;
+ len = sizeof(n);
+ if (::sysctlbyname("hw.optional.watchpoint", &n, &len, NULL, 0) == 0) {
+ g_num_supported_hw_watchpoints = n;
+ }
+ }
+ return g_num_supported_hw_watchpoints;
+#else
+ // TODO: figure out remote case here!
+ return 2;
+#endif
+}
+
+uint32_t RegisterContextDarwin_arm64::SetHardwareWatchpoint(lldb::addr_t addr,
+ size_t size,
+ bool read,
+ bool write) {
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint(addr = %8.8p,
+ // size = %u, read = %u, write = %u)", addr, size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can't watch zero bytes
+ if (size == 0)
+ return LLDB_INVALID_INDEX32;
+
+ // We must watch for either read or write
+ if (!read && !write)
+ return LLDB_INVALID_INDEX32;
+
+ // Can't watch more than 4 bytes per WVR/WCR pair
+ if (size > 4)
+ return LLDB_INVALID_INDEX32;
+
+ // We can only watch up to four bytes that follow a 4 byte aligned address
+ // per watchpoint register pair. Since we have at most so we can only watch
+ // until the next 4 byte boundary and we need to make sure we can properly
+ // encode this.
+ uint32_t addr_word_offset = addr % 4;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint() -
+ // addr_word_offset = 0x%8.8x", addr_word_offset);
+
+ uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint() - byte_mask =
+ // 0x%8.8x", byte_mask);
+ if (byte_mask > 0xfu)
+ return LLDB_INVALID_INDEX32;
+
+ // Read the debug state
+ int kret = ReadDBG(false);
+
+ if (kret == KERN_SUCCESS) {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ for (i = 0; i < num_hw_watchpoints; ++i) {
+ if ((dbg.wcr[i] & WCR_ENABLE) == 0)
+ break; // We found an available hw breakpoint slot (in i)
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_watchpoints) {
+ // Make the byte_mask into a valid Byte Address Select mask
+ uint32_t byte_address_select = byte_mask << 5;
+ // Make sure bits 1:0 are clear in our address
+ dbg.wvr[i] = addr & ~((lldb::addr_t)3);
+ dbg.wcr[i] = byte_address_select | // Which bytes that follow the IMVA
+ // that we will watch
+ S_USER | // Stop only in user mode
+ (read ? WCR_LOAD : 0) | // Stop on read access?
+ (write ? WCR_STORE : 0) | // Stop on write access?
+ WCR_ENABLE; // Enable this watchpoint;
+
+ kret = WriteDBG();
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint()
+ // WriteDBG() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ } else {
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint():
+ // All hardware resources (%u) are in use.",
+ // num_hw_watchpoints);
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextDarwin_arm64::ClearHardwareWatchpoint(uint32_t hw_index) {
+ int kret = ReadDBG(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (kret == KERN_SUCCESS) {
+ if (hw_index < num_hw_points) {
+ dbg.wcr[hw_index] = 0;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::ClearHardwareWatchpoint( %u )
+ // - WVR%u = 0x%8.8x WCR%u = 0x%8.8x",
+ // hw_index,
+ // hw_index,
+ // dbg.wvr[hw_index],
+ // hw_index,
+ // dbg.wcr[hw_index]);
+
+ kret = WriteDBG();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.h
new file mode 100644
index 000000000000..2f691c807d50
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.h
@@ -0,0 +1,231 @@
+//===-- RegisterContextDarwin_arm64.h -----------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextDarwin_arm64_h_
+#define liblldb_RegisterContextDarwin_arm64_h_
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+// Break only in privileged or user mode
+#define S_RSVD ((uint32_t)(0u << 1))
+#define S_PRIV ((uint32_t)(1u << 1))
+#define S_USER ((uint32_t)(2u << 1))
+#define S_PRIV_USER ((S_PRIV) | (S_USER))
+
+#define WCR_ENABLE ((uint32_t)(1u))
+
+// Watchpoint load/store
+#define WCR_LOAD ((uint32_t)(1u << 3))
+#define WCR_STORE ((uint32_t)(1u << 4))
+
+class RegisterContextDarwin_arm64 : public lldb_private::RegisterContext {
+public:
+ RegisterContextDarwin_arm64(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ ~RegisterContextDarwin_arm64() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ uint32_t NumSupportedHardwareWatchpoints() override;
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write) override;
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index) override;
+
+ // mirrors <mach/arm/thread_status.h> arm_thread_state64_t
+ struct GPR {
+ uint64_t x[29]; // x0-x28
+ uint64_t fp; // x29
+ uint64_t lr; // x30
+ uint64_t sp; // x31
+ uint64_t pc; // pc
+ uint32_t cpsr; // cpsr
+ };
+
+ struct VReg {
+ llvm::AlignedCharArray<16, 16> bytes;
+ };
+
+ // mirrors <mach/arm/thread_status.h> arm_neon_state64_t
+ struct FPU {
+ VReg v[32];
+ uint32_t fpsr;
+ uint32_t fpcr;
+ };
+
+ // mirrors <mach/arm/thread_status.h> arm_exception_state64_t
+ struct EXC {
+ uint64_t far; // Virtual Fault Address
+ uint32_t esr; // Exception syndrome
+ uint32_t exception; // number of arm exception token
+ };
+
+ // mirrors <mach/arm/thread_status.h> arm_debug_state64_t
+ struct DBG {
+ uint64_t bvr[16];
+ uint64_t bcr[16];
+ uint64_t wvr[16];
+ uint64_t wcr[16];
+ uint64_t mdscr_el1;
+ };
+
+ static void LogDBGRegisters(lldb_private::Log *log, const DBG &dbg);
+
+protected:
+ enum {
+ GPRRegSet = 6, // ARM_THREAD_STATE64
+ FPURegSet = 17, // ARM_NEON_STATE64
+ EXCRegSet = 7, // ARM_EXCEPTION_STATE64
+ DBGRegSet = 15 // ARM_DEBUG_STATE64
+ };
+
+ enum {
+ GPRWordCount = sizeof(GPR) / sizeof(uint32_t), // ARM_THREAD_STATE64_COUNT
+ FPUWordCount = sizeof(FPU) / sizeof(uint32_t), // ARM_NEON_STATE64_COUNT
+ EXCWordCount =
+ sizeof(EXC) / sizeof(uint32_t), // ARM_EXCEPTION_STATE64_COUNT
+ DBGWordCount = sizeof(DBG) / sizeof(uint32_t) // ARM_DEBUG_STATE64_COUNT
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ DBG dbg;
+ int gpr_errs[2]; // Read/Write errors
+ int fpu_errs[2]; // Read/Write errors
+ int exc_errs[2]; // Read/Write errors
+ int dbg_errs[2]; // Read/Write errors
+
+ void InvalidateAllRegisterStates() {
+ SetError(GPRRegSet, Read, -1);
+ SetError(FPURegSet, Read, -1);
+ SetError(EXCRegSet, Read, -1);
+ }
+
+ int GetError(int flavor, uint32_t err_idx) const {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case GPRRegSet:
+ return gpr_errs[err_idx];
+ case FPURegSet:
+ return fpu_errs[err_idx];
+ case EXCRegSet:
+ return exc_errs[err_idx];
+ case DBGRegSet:
+ return dbg_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+
+ bool SetError(int flavor, uint32_t err_idx, int err) {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case DBGRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+
+ bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; }
+
+ int ReadGPR(bool force);
+
+ int ReadFPU(bool force);
+
+ int ReadEXC(bool force);
+
+ int ReadDBG(bool force);
+
+ int WriteGPR();
+
+ int WriteFPU();
+
+ int WriteEXC();
+
+ int WriteDBG();
+
+ // Subclasses override these to do the actual reading.
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) { return -1; }
+
+ virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0;
+
+ virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0;
+
+ virtual int DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) = 0;
+
+ virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0;
+
+ virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0;
+
+ virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0;
+
+ virtual int DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) = 0;
+
+ int ReadRegisterSet(uint32_t set, bool force);
+
+ int WriteRegisterSet(uint32_t set);
+
+ static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num);
+
+ static int GetSetForNativeRegNum(int reg_num);
+
+ static size_t GetRegisterInfosCount();
+
+ static const lldb_private::RegisterInfo *GetRegisterInfos();
+};
+
+#endif // liblldb_RegisterContextDarwin_arm64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp
new file mode 100644
index 000000000000..820d280c37f7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp
@@ -0,0 +1,969 @@
+//===-- RegisterContextDarwin_i386.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Compiler.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+// Support building against older versions of LLVM, this macro was added
+// recently.
+#ifndef LLVM_EXTENSION
+#define LLVM_EXTENSION
+#endif
+
+#include "RegisterContextDarwin_i386.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum {
+ gpr_eax = 0,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_ss,
+ gpr_eflags,
+ gpr_eip,
+ gpr_cs,
+ gpr_ds,
+ gpr_es,
+ gpr_fs,
+ gpr_gs,
+
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+
+ k_num_registers,
+
+ // Aliases
+ fpu_fctrl = fpu_fcw,
+ fpu_fstat = fpu_fsw,
+ fpu_ftag = fpu_ftw,
+ fpu_fiseg = fpu_cs,
+ fpu_fioff = fpu_ip,
+ fpu_foseg = fpu_ds,
+ fpu_fooff = fpu_dp
+};
+
+enum {
+ ehframe_eax = 0,
+ ehframe_ecx,
+ ehframe_edx,
+ ehframe_ebx,
+ ehframe_ebp,
+ ehframe_esp,
+ ehframe_esi,
+ ehframe_edi,
+ ehframe_eip,
+ ehframe_eflags
+};
+
+enum {
+ dwarf_eax = 0,
+ dwarf_ecx,
+ dwarf_edx,
+ dwarf_ebx,
+ dwarf_esp,
+ dwarf_ebp,
+ dwarf_esi,
+ dwarf_edi,
+ dwarf_eip,
+ dwarf_eflags,
+ dwarf_stmm0 = 11,
+ dwarf_stmm1,
+ dwarf_stmm2,
+ dwarf_stmm3,
+ dwarf_stmm4,
+ dwarf_stmm5,
+ dwarf_stmm6,
+ dwarf_stmm7,
+ dwarf_xmm0 = 21,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7
+};
+
+#define GPR_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_i386::GPR, reg))
+#define FPU_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_i386::FPU, reg) + \
+ sizeof(RegisterContextDarwin_i386::GPR))
+#define EXC_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_i386::EXC, reg) + \
+ sizeof(RegisterContextDarwin_i386::GPR) + \
+ sizeof(RegisterContextDarwin_i386::FPU))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that the
+// register state structures are defined correctly and have the correct sizes
+// and offsets.
+#define DEFINE_GPR(reg, alt) \
+ #reg, alt, sizeof(((RegisterContextDarwin_i386::GPR *) NULL)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_UINT(reg) \
+ #reg, NULL, sizeof(((RegisterContextDarwin_i386::FPU *) NULL)->reg), \
+ FPU_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_VECT(reg, i) \
+ #reg #i, NULL, \
+ sizeof(((RegisterContextDarwin_i386::FPU *) NULL)->reg[i].bytes), \
+ FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {LLDB_INVALID_REGNUM, dwarf_##reg##i, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ fpu_##reg##i }, \
+ nullptr, nullptr, nullptr, 0
+
+#define DEFINE_EXC(reg) \
+ #reg, NULL, sizeof(((RegisterContextDarwin_i386::EXC *) NULL)->reg), \
+ EXC_OFFSET(reg), eEncodingUint, eFormatHex
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterContextDarwin_i386::GPR) + \
+ sizeof(RegisterContextDarwin_i386::FPU) + \
+ sizeof(RegisterContextDarwin_i386::EXC))
+
+static RegisterInfo g_register_infos[] = {
+ // Macro auto defines most stuff eh_frame DWARF
+ // GENERIC PROCESS PLUGIN LLDB
+ // =============================== =======================
+ // =================== ========================= ==================
+ // =================
+ {DEFINE_GPR(eax, nullptr),
+ {ehframe_eax, dwarf_eax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_eax},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(ebx, nullptr),
+ {ehframe_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_ebx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(ecx, nullptr),
+ {ehframe_ecx, dwarf_ecx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_ecx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(edx, nullptr),
+ {ehframe_edx, dwarf_edx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_edx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(edi, nullptr),
+ {ehframe_edi, dwarf_edi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_edi},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(esi, nullptr),
+ {ehframe_esi, dwarf_esi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_esi},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(ebp, "fp"),
+ {ehframe_ebp, dwarf_ebp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ gpr_ebp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(esp, "sp"),
+ {ehframe_esp, dwarf_esp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ gpr_esp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(ss, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_ss},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(eflags, "flags"),
+ {ehframe_eflags, dwarf_eflags, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, gpr_eflags},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(eip, "pc"),
+ {ehframe_eip, dwarf_eip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ gpr_eip},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(cs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_cs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(ds, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_ds},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(es, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_es},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(fs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_fs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(gs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_gs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {DEFINE_FPU_UINT(fcw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fcw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(fsw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fsw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ftw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ftw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(fop),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fop},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ip),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ip},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(cs),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_cs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(dp),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_dp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ds),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ds},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(mxcsr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_mxcsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(mxcsrmask),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_mxcsrmask},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_VECT(stmm, 0)},
+ {DEFINE_FPU_VECT(stmm, 1)},
+ {DEFINE_FPU_VECT(stmm, 2)},
+ {DEFINE_FPU_VECT(stmm, 3)},
+ {DEFINE_FPU_VECT(stmm, 4)},
+ {DEFINE_FPU_VECT(stmm, 5)},
+ {DEFINE_FPU_VECT(stmm, 6)},
+ {DEFINE_FPU_VECT(stmm, 7)},
+ {DEFINE_FPU_VECT(xmm, 0)},
+ {DEFINE_FPU_VECT(xmm, 1)},
+ {DEFINE_FPU_VECT(xmm, 2)},
+ {DEFINE_FPU_VECT(xmm, 3)},
+ {DEFINE_FPU_VECT(xmm, 4)},
+ {DEFINE_FPU_VECT(xmm, 5)},
+ {DEFINE_FPU_VECT(xmm, 6)},
+ {DEFINE_FPU_VECT(xmm, 7)},
+
+ {DEFINE_EXC(trapno),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_trapno},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_EXC(err),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_err},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_EXC(faultvaddr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_faultvaddr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static size_t k_num_register_infos = llvm::array_lengthof(g_register_infos);
+
+RegisterContextDarwin_i386::RegisterContextDarwin_i386(
+ Thread &thread, uint32_t concrete_frame_idx)
+ : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextDarwin_i386::~RegisterContextDarwin_i386() {}
+
+void RegisterContextDarwin_i386::InvalidateAllRegisters() {
+ InvalidateAllRegisterStates();
+}
+
+size_t RegisterContextDarwin_i386::GetRegisterCount() {
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_i386::GetRegisterInfoAtIndex(size_t reg) {
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return nullptr;
+}
+
+size_t RegisterContextDarwin_i386::GetRegisterInfosCount() {
+ return k_num_register_infos;
+}
+
+const RegisterInfo *RegisterContextDarwin_i386::GetRegisterInfos() {
+ return g_register_infos;
+}
+
+// General purpose registers
+static uint32_t g_gpr_regnums[] = {
+ gpr_eax, gpr_ebx, gpr_ecx, gpr_edx, gpr_edi, gpr_esi, gpr_ebp, gpr_esp,
+ gpr_ss, gpr_eflags, gpr_eip, gpr_cs, gpr_ds, gpr_es, gpr_fs, gpr_gs};
+
+// Floating point registers
+static uint32_t g_fpu_regnums[] = {
+ fpu_fcw, fpu_fsw, fpu_ftw, fpu_fop, fpu_ip, fpu_cs,
+ fpu_dp, fpu_ds, fpu_mxcsr, fpu_mxcsrmask, fpu_stmm0, fpu_stmm1,
+ fpu_stmm2, fpu_stmm3, fpu_stmm4, fpu_stmm5, fpu_stmm6, fpu_stmm7,
+ fpu_xmm0, fpu_xmm1, fpu_xmm2, fpu_xmm3, fpu_xmm4, fpu_xmm5,
+ fpu_xmm6, fpu_xmm7};
+
+// Exception registers
+
+static uint32_t g_exc_regnums[] = {exc_trapno, exc_err, exc_faultvaddr};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = llvm::array_lengthof(g_gpr_regnums);
+const size_t k_num_fpu_registers = llvm::array_lengthof(g_fpu_regnums);
+const size_t k_num_exc_registers = llvm::array_lengthof(g_exc_regnums);
+
+// Register set definitions. The first definitions at register set index of
+// zero is for all registers, followed by other registers sets. The register
+// information for the all register set need not be filled in.
+static const RegisterSet g_reg_sets[] = {
+ {
+ "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums,
+ },
+ {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums},
+ {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}};
+
+const size_t k_num_regsets = llvm::array_lengthof(g_reg_sets);
+
+size_t RegisterContextDarwin_i386::GetRegisterSetCount() {
+ return k_num_regsets;
+}
+
+const RegisterSet *RegisterContextDarwin_i386::GetRegisterSet(size_t reg_set) {
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return nullptr;
+}
+
+// Register information definitions for 32 bit i386.
+int RegisterContextDarwin_i386::GetSetForNativeRegNum(int reg_num) {
+ if (reg_num < fpu_fcw)
+ return GPRRegSet;
+ else if (reg_num < exc_trapno)
+ return FPURegSet;
+ else if (reg_num < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+void RegisterContextDarwin_i386::LogGPR(Log *log, const char *title) {
+ if (log) {
+ if (title)
+ log->Printf("%s", title);
+ for (uint32_t i = 0; i < k_num_gpr_registers; i++) {
+ uint32_t reg = gpr_eax + i;
+ log->Printf("%12s = 0x%8.8x", g_register_infos[reg].name,
+ (&gpr.eax)[reg]);
+ }
+ }
+}
+
+int RegisterContextDarwin_i386::ReadGPR(bool force) {
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr));
+ }
+ return GetError(set, Read);
+}
+
+int RegisterContextDarwin_i386::ReadFPU(bool force) {
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu));
+ }
+ return GetError(set, Read);
+}
+
+int RegisterContextDarwin_i386::ReadEXC(bool force) {
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadEXC(GetThreadID(), set, exc));
+ }
+ return GetError(set, Read);
+}
+
+int RegisterContextDarwin_i386::WriteGPR() {
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_i386::WriteFPU() {
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_i386::WriteEXC() {
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_i386::ReadRegisterSet(uint32_t set, bool force) {
+ switch (set) {
+ case GPRRegSet:
+ return ReadGPR(force);
+ case FPURegSet:
+ return ReadFPU(force);
+ case EXCRegSet:
+ return ReadEXC(force);
+ default:
+ break;
+ }
+ return -1;
+}
+
+int RegisterContextDarwin_i386::WriteRegisterSet(uint32_t set) {
+ // Make sure we have a valid context to set.
+ if (RegisterSetIsCached(set)) {
+ switch (set) {
+ case GPRRegSet:
+ return WriteGPR();
+ case FPURegSet:
+ return WriteFPU();
+ case EXCRegSet:
+ return WriteEXC();
+ default:
+ break;
+ }
+ }
+ return -1;
+}
+
+bool RegisterContextDarwin_i386::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_i386::GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg) {
+ case gpr_eax:
+ case gpr_ebx:
+ case gpr_ecx:
+ case gpr_edx:
+ case gpr_edi:
+ case gpr_esi:
+ case gpr_ebp:
+ case gpr_esp:
+ case gpr_ss:
+ case gpr_eflags:
+ case gpr_eip:
+ case gpr_cs:
+ case gpr_ds:
+ case gpr_es:
+ case gpr_fs:
+ case gpr_gs:
+ value = (&gpr.eax)[reg - gpr_eax];
+ break;
+
+ case fpu_fcw:
+ value = fpu.fcw;
+ break;
+
+ case fpu_fsw:
+ value = fpu.fsw;
+ break;
+
+ case fpu_ftw:
+ value = fpu.ftw;
+ break;
+
+ case fpu_fop:
+ value = fpu.fop;
+ break;
+
+ case fpu_ip:
+ value = fpu.ip;
+ break;
+
+ case fpu_cs:
+ value = fpu.cs;
+ break;
+
+ case fpu_dp:
+ value = fpu.dp;
+ break;
+
+ case fpu_ds:
+ value = fpu.ds;
+ break;
+
+ case fpu_mxcsr:
+ value = fpu.mxcsr;
+ break;
+
+ case fpu_mxcsrmask:
+ value = fpu.mxcsrmask;
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ // These values don't fit into scalar types,
+ // RegisterContext::ReadRegisterBytes() must be used for these registers
+ //::memcpy (reg_value.value.vector.uint8, fpu.stmm[reg - fpu_stmm0].bytes,
+ //10);
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ // These values don't fit into scalar types,
+ // RegisterContext::ReadRegisterBytes() must be used for these registers
+ //::memcpy (reg_value.value.vector.uint8, fpu.xmm[reg - fpu_xmm0].bytes,
+ //16);
+ return false;
+
+ case exc_trapno:
+ value = exc.trapno;
+ break;
+
+ case exc_err:
+ value = exc.err;
+ break;
+
+ case exc_faultvaddr:
+ value = exc.faultvaddr;
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegisterContextDarwin_i386::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg) {
+ case gpr_eax:
+ case gpr_ebx:
+ case gpr_ecx:
+ case gpr_edx:
+ case gpr_edi:
+ case gpr_esi:
+ case gpr_ebp:
+ case gpr_esp:
+ case gpr_ss:
+ case gpr_eflags:
+ case gpr_eip:
+ case gpr_cs:
+ case gpr_ds:
+ case gpr_es:
+ case gpr_fs:
+ case gpr_gs:
+ (&gpr.eax)[reg - gpr_eax] = value.GetAsUInt32();
+ break;
+
+ case fpu_fcw:
+ fpu.fcw = value.GetAsUInt16();
+ break;
+
+ case fpu_fsw:
+ fpu.fsw = value.GetAsUInt16();
+ break;
+
+ case fpu_ftw:
+ fpu.ftw = value.GetAsUInt8();
+ break;
+
+ case fpu_fop:
+ fpu.fop = value.GetAsUInt16();
+ break;
+
+ case fpu_ip:
+ fpu.ip = value.GetAsUInt32();
+ break;
+
+ case fpu_cs:
+ fpu.cs = value.GetAsUInt16();
+ break;
+
+ case fpu_dp:
+ fpu.dp = value.GetAsUInt32();
+ break;
+
+ case fpu_ds:
+ fpu.ds = value.GetAsUInt16();
+ break;
+
+ case fpu_mxcsr:
+ fpu.mxcsr = value.GetAsUInt32();
+ break;
+
+ case fpu_mxcsrmask:
+ fpu.mxcsrmask = value.GetAsUInt32();
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ // These values don't fit into scalar types,
+ // RegisterContext::ReadRegisterBytes() must be used for these registers
+ ::memcpy(fpu.stmm[reg - fpu_stmm0].bytes, value.GetBytes(),
+ value.GetByteSize());
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ // These values don't fit into scalar types,
+ // RegisterContext::ReadRegisterBytes() must be used for these registers
+ ::memcpy(fpu.xmm[reg - fpu_xmm0].bytes, value.GetBytes(),
+ value.GetByteSize());
+ return false;
+
+ case exc_trapno:
+ exc.trapno = value.GetAsUInt32();
+ break;
+
+ case exc_err:
+ exc.err = value.GetAsUInt32();
+ break;
+
+ case exc_faultvaddr:
+ exc.faultvaddr = value.GetAsUInt32();
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == 0;
+}
+
+bool RegisterContextDarwin_i386::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0);
+ if (ReadGPR(false) == 0 && ReadFPU(false) == 0 && ReadEXC(false) == 0) {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextDarwin_i386::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy(&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy(&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy(&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == 0)
+ ++success_count;
+ if (WriteFPU() == 0)
+ ++success_count;
+ if (WriteEXC() == 0)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+uint32_t RegisterContextDarwin_i386::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t reg) {
+ if (kind == eRegisterKindGeneric) {
+ switch (reg) {
+ case LLDB_REGNUM_GENERIC_PC:
+ return gpr_eip;
+ case LLDB_REGNUM_GENERIC_SP:
+ return gpr_esp;
+ case LLDB_REGNUM_GENERIC_FP:
+ return gpr_ebp;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ return gpr_eflags;
+ case LLDB_REGNUM_GENERIC_RA:
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindEHFrame || kind == eRegisterKindDWARF) {
+ switch (reg) {
+ case dwarf_eax:
+ return gpr_eax;
+ case dwarf_ecx:
+ return gpr_ecx;
+ case dwarf_edx:
+ return gpr_edx;
+ case dwarf_ebx:
+ return gpr_ebx;
+ case dwarf_esp:
+ return gpr_esp;
+ case dwarf_ebp:
+ return gpr_ebp;
+ case dwarf_esi:
+ return gpr_esi;
+ case dwarf_edi:
+ return gpr_edi;
+ case dwarf_eip:
+ return gpr_eip;
+ case dwarf_eflags:
+ return gpr_eflags;
+ case dwarf_stmm0:
+ return fpu_stmm0;
+ case dwarf_stmm1:
+ return fpu_stmm1;
+ case dwarf_stmm2:
+ return fpu_stmm2;
+ case dwarf_stmm3:
+ return fpu_stmm3;
+ case dwarf_stmm4:
+ return fpu_stmm4;
+ case dwarf_stmm5:
+ return fpu_stmm5;
+ case dwarf_stmm6:
+ return fpu_stmm6;
+ case dwarf_stmm7:
+ return fpu_stmm7;
+ case dwarf_xmm0:
+ return fpu_xmm0;
+ case dwarf_xmm1:
+ return fpu_xmm1;
+ case dwarf_xmm2:
+ return fpu_xmm2;
+ case dwarf_xmm3:
+ return fpu_xmm3;
+ case dwarf_xmm4:
+ return fpu_xmm4;
+ case dwarf_xmm5:
+ return fpu_xmm5;
+ case dwarf_xmm6:
+ return fpu_xmm6;
+ case dwarf_xmm7:
+ return fpu_xmm7;
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindLLDB) {
+ return reg;
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+bool RegisterContextDarwin_i386::HardwareSingleStep(bool enable) {
+ if (ReadGPR(false) != 0)
+ return false;
+
+ const uint32_t trace_bit = 0x100u;
+ if (enable) {
+ // If the trace bit is already set, there is nothing to do
+ if (gpr.eflags & trace_bit)
+ return true;
+ else
+ gpr.eflags |= trace_bit;
+ } else {
+ // If the trace bit is already cleared, there is nothing to do
+ if (gpr.eflags & trace_bit)
+ gpr.eflags &= ~trace_bit;
+ else
+ return true;
+ }
+
+ return WriteGPR() == 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h
new file mode 100644
index 000000000000..e52f0fe63250
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h
@@ -0,0 +1,208 @@
+//===-- RegisterContextDarwin_i386.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextDarwin_i386_h_
+#define liblldb_RegisterContextDarwin_i386_h_
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+class RegisterContextDarwin_i386 : public lldb_private::RegisterContext {
+public:
+ RegisterContextDarwin_i386(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ ~RegisterContextDarwin_i386() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+ struct GPR {
+ uint32_t eax;
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t esp;
+ uint32_t ss;
+ uint32_t eflags;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t ds;
+ uint32_t es;
+ uint32_t fs;
+ uint32_t gs;
+ };
+
+ struct MMSReg {
+ uint8_t bytes[10];
+ uint8_t pad[6];
+ };
+
+ struct XMMReg {
+ uint8_t bytes[16];
+ };
+
+ struct FPU {
+ uint32_t pad[2];
+ uint16_t fcw;
+ uint16_t fsw;
+ uint8_t ftw;
+ uint8_t pad1;
+ uint16_t fop;
+ uint32_t ip;
+ uint16_t cs;
+ uint16_t pad2;
+ uint32_t dp;
+ uint16_t ds;
+ uint16_t pad3;
+ uint32_t mxcsr;
+ uint32_t mxcsrmask;
+ MMSReg stmm[8];
+ XMMReg xmm[8];
+ uint8_t pad4[14 * 16];
+ int pad5;
+ };
+
+ struct EXC {
+ uint32_t trapno;
+ uint32_t err;
+ uint32_t faultvaddr;
+ };
+
+protected:
+ enum { GPRRegSet = 1, FPURegSet = 2, EXCRegSet = 3 };
+
+ enum {
+ GPRWordCount = sizeof(GPR) / sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU) / sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC) / sizeof(uint32_t)
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ int gpr_errs[2]; // Read/Write errors
+ int fpu_errs[2]; // Read/Write errors
+ int exc_errs[2]; // Read/Write errors
+
+ void InvalidateAllRegisterStates() {
+ SetError(GPRRegSet, Read, -1);
+ SetError(FPURegSet, Read, -1);
+ SetError(EXCRegSet, Read, -1);
+ }
+
+ int GetError(int flavor, uint32_t err_idx) const {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case GPRRegSet:
+ return gpr_errs[err_idx];
+ case FPURegSet:
+ return fpu_errs[err_idx];
+ case EXCRegSet:
+ return exc_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+
+ bool SetError(int flavor, uint32_t err_idx, int err) {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+
+ bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; }
+
+ void LogGPR(lldb_private::Log *log, const char *title);
+
+ int ReadGPR(bool force);
+
+ int ReadFPU(bool force);
+
+ int ReadEXC(bool force);
+
+ int WriteGPR();
+
+ int WriteFPU();
+
+ int WriteEXC();
+
+ // Subclasses override these to do the actual reading.
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) = 0;
+
+ virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0;
+
+ virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0;
+
+ virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0;
+
+ virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0;
+
+ virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0;
+
+ int ReadRegisterSet(uint32_t set, bool force);
+
+ int WriteRegisterSet(uint32_t set);
+
+ static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num);
+
+ static int GetSetForNativeRegNum(int reg_num);
+
+ static size_t GetRegisterInfosCount();
+
+ static const lldb_private::RegisterInfo *GetRegisterInfos();
+};
+
+#endif // liblldb_RegisterContextDarwin_i386_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp
new file mode 100644
index 000000000000..62e512adc9f7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp
@@ -0,0 +1,1078 @@
+//===-- RegisterContextDarwin_x86_64.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <memory>
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Compiler.h"
+
+// Support building against older versions of LLVM, this macro was added
+// recently.
+#ifndef LLVM_EXTENSION
+#define LLVM_EXTENSION
+#endif
+
+#include "RegisterContextDarwin_x86_64.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum {
+ gpr_rax = 0,
+ gpr_rbx,
+ gpr_rcx,
+ gpr_rdx,
+ gpr_rdi,
+ gpr_rsi,
+ gpr_rbp,
+ gpr_rsp,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13,
+ gpr_r14,
+ gpr_r15,
+ gpr_rip,
+ gpr_rflags,
+ gpr_cs,
+ gpr_fs,
+ gpr_gs,
+
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ fpu_xmm8,
+ fpu_xmm9,
+ fpu_xmm10,
+ fpu_xmm11,
+ fpu_xmm12,
+ fpu_xmm13,
+ fpu_xmm14,
+ fpu_xmm15,
+
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+
+ k_num_registers,
+
+ // Aliases
+ fpu_fctrl = fpu_fcw,
+ fpu_fstat = fpu_fsw,
+ fpu_ftag = fpu_ftw,
+ fpu_fiseg = fpu_cs,
+ fpu_fioff = fpu_ip,
+ fpu_foseg = fpu_ds,
+ fpu_fooff = fpu_dp
+};
+
+enum ehframe_dwarf_regnums {
+ ehframe_dwarf_gpr_rax = 0,
+ ehframe_dwarf_gpr_rdx,
+ ehframe_dwarf_gpr_rcx,
+ ehframe_dwarf_gpr_rbx,
+ ehframe_dwarf_gpr_rsi,
+ ehframe_dwarf_gpr_rdi,
+ ehframe_dwarf_gpr_rbp,
+ ehframe_dwarf_gpr_rsp,
+ ehframe_dwarf_gpr_r8,
+ ehframe_dwarf_gpr_r9,
+ ehframe_dwarf_gpr_r10,
+ ehframe_dwarf_gpr_r11,
+ ehframe_dwarf_gpr_r12,
+ ehframe_dwarf_gpr_r13,
+ ehframe_dwarf_gpr_r14,
+ ehframe_dwarf_gpr_r15,
+ ehframe_dwarf_gpr_rip,
+ ehframe_dwarf_fpu_xmm0,
+ ehframe_dwarf_fpu_xmm1,
+ ehframe_dwarf_fpu_xmm2,
+ ehframe_dwarf_fpu_xmm3,
+ ehframe_dwarf_fpu_xmm4,
+ ehframe_dwarf_fpu_xmm5,
+ ehframe_dwarf_fpu_xmm6,
+ ehframe_dwarf_fpu_xmm7,
+ ehframe_dwarf_fpu_xmm8,
+ ehframe_dwarf_fpu_xmm9,
+ ehframe_dwarf_fpu_xmm10,
+ ehframe_dwarf_fpu_xmm11,
+ ehframe_dwarf_fpu_xmm12,
+ ehframe_dwarf_fpu_xmm13,
+ ehframe_dwarf_fpu_xmm14,
+ ehframe_dwarf_fpu_xmm15,
+ ehframe_dwarf_fpu_stmm0,
+ ehframe_dwarf_fpu_stmm1,
+ ehframe_dwarf_fpu_stmm2,
+ ehframe_dwarf_fpu_stmm3,
+ ehframe_dwarf_fpu_stmm4,
+ ehframe_dwarf_fpu_stmm5,
+ ehframe_dwarf_fpu_stmm6,
+ ehframe_dwarf_fpu_stmm7
+
+};
+
+#define GPR_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_x86_64::GPR, reg))
+#define FPU_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_x86_64::FPU, reg) + \
+ sizeof(RegisterContextDarwin_x86_64::GPR))
+#define EXC_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_x86_64::EXC, reg) + \
+ sizeof(RegisterContextDarwin_x86_64::GPR) + \
+ sizeof(RegisterContextDarwin_x86_64::FPU))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that the
+// register state structures are defined correctly and have the correct sizes
+// and offsets.
+#define DEFINE_GPR(reg, alt) \
+ #reg, alt, sizeof(((RegisterContextDarwin_x86_64::GPR *) NULL)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_UINT(reg) \
+ #reg, NULL, sizeof(((RegisterContextDarwin_x86_64::FPU *) NULL)->reg), \
+ FPU_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_VECT(reg, i) \
+ #reg #i, NULL, \
+ sizeof(((RegisterContextDarwin_x86_64::FPU *) NULL)->reg[i].bytes), \
+ FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {ehframe_dwarf_fpu_##reg##i, \
+ ehframe_dwarf_fpu_##reg##i, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, fpu_##reg##i }, \
+ nullptr, nullptr, nullptr, 0
+#define DEFINE_EXC(reg) \
+ #reg, NULL, sizeof(((RegisterContextDarwin_x86_64::EXC *) NULL)->reg), \
+ EXC_OFFSET(reg), eEncodingUint, eFormatHex
+
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterContextDarwin_x86_64::GPR) + \
+ sizeof(RegisterContextDarwin_x86_64::FPU) + \
+ sizeof(RegisterContextDarwin_x86_64::EXC))
+
+// General purpose registers for 64 bit
+static RegisterInfo g_register_infos[] = {
+ // Macro auto defines most stuff EH_FRAME DWARF
+ // GENERIC PROCESS PLUGIN LLDB
+ // =============================== ======================
+ // =================== ========================== ====================
+ // ===================
+ {DEFINE_GPR(rax, nullptr),
+ {ehframe_dwarf_gpr_rax, ehframe_dwarf_gpr_rax, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rax},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rbx, nullptr),
+ {ehframe_dwarf_gpr_rbx, ehframe_dwarf_gpr_rbx, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rbx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rcx, nullptr),
+ {ehframe_dwarf_gpr_rcx, ehframe_dwarf_gpr_rcx, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rcx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rdx, nullptr),
+ {ehframe_dwarf_gpr_rdx, ehframe_dwarf_gpr_rdx, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rdx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rdi, nullptr),
+ {ehframe_dwarf_gpr_rdi, ehframe_dwarf_gpr_rdi, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rdi},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rsi, nullptr),
+ {ehframe_dwarf_gpr_rsi, ehframe_dwarf_gpr_rsi, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rsi},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rbp, "fp"),
+ {ehframe_dwarf_gpr_rbp, ehframe_dwarf_gpr_rbp, LLDB_REGNUM_GENERIC_FP,
+ LLDB_INVALID_REGNUM, gpr_rbp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rsp, "sp"),
+ {ehframe_dwarf_gpr_rsp, ehframe_dwarf_gpr_rsp, LLDB_REGNUM_GENERIC_SP,
+ LLDB_INVALID_REGNUM, gpr_rsp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r8, nullptr),
+ {ehframe_dwarf_gpr_r8, ehframe_dwarf_gpr_r8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r8},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r9, nullptr),
+ {ehframe_dwarf_gpr_r9, ehframe_dwarf_gpr_r9, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r9},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r10, nullptr),
+ {ehframe_dwarf_gpr_r10, ehframe_dwarf_gpr_r10, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r10},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r11, nullptr),
+ {ehframe_dwarf_gpr_r11, ehframe_dwarf_gpr_r11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r12, nullptr),
+ {ehframe_dwarf_gpr_r12, ehframe_dwarf_gpr_r12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r12},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r13, nullptr),
+ {ehframe_dwarf_gpr_r13, ehframe_dwarf_gpr_r13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r13},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r14, nullptr),
+ {ehframe_dwarf_gpr_r14, ehframe_dwarf_gpr_r14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r14},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r15, nullptr),
+ {ehframe_dwarf_gpr_r15, ehframe_dwarf_gpr_r15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r15},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rip, "pc"),
+ {ehframe_dwarf_gpr_rip, ehframe_dwarf_gpr_rip, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_REGNUM, gpr_rip},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rflags, "flags"),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, gpr_rflags},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(cs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_cs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(fs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_fs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(gs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_gs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {DEFINE_FPU_UINT(fcw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fcw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(fsw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fsw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ftw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ftw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(fop),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fop},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ip),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ip},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(cs),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_cs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(dp),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_dp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ds),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ds},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(mxcsr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_mxcsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(mxcsrmask),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_mxcsrmask},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_VECT(stmm, 0)},
+ {DEFINE_FPU_VECT(stmm, 1)},
+ {DEFINE_FPU_VECT(stmm, 2)},
+ {DEFINE_FPU_VECT(stmm, 3)},
+ {DEFINE_FPU_VECT(stmm, 4)},
+ {DEFINE_FPU_VECT(stmm, 5)},
+ {DEFINE_FPU_VECT(stmm, 6)},
+ {DEFINE_FPU_VECT(stmm, 7)},
+ {DEFINE_FPU_VECT(xmm, 0)},
+ {DEFINE_FPU_VECT(xmm, 1)},
+ {DEFINE_FPU_VECT(xmm, 2)},
+ {DEFINE_FPU_VECT(xmm, 3)},
+ {DEFINE_FPU_VECT(xmm, 4)},
+ {DEFINE_FPU_VECT(xmm, 5)},
+ {DEFINE_FPU_VECT(xmm, 6)},
+ {DEFINE_FPU_VECT(xmm, 7)},
+ {DEFINE_FPU_VECT(xmm, 8)},
+ {DEFINE_FPU_VECT(xmm, 9)},
+ {DEFINE_FPU_VECT(xmm, 10)},
+ {DEFINE_FPU_VECT(xmm, 11)},
+ {DEFINE_FPU_VECT(xmm, 12)},
+ {DEFINE_FPU_VECT(xmm, 13)},
+ {DEFINE_FPU_VECT(xmm, 14)},
+ {DEFINE_FPU_VECT(xmm, 15)},
+
+ {DEFINE_EXC(trapno),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_trapno},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_EXC(err),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_err},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_EXC(faultvaddr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_faultvaddr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static size_t k_num_register_infos = llvm::array_lengthof(g_register_infos);
+
+RegisterContextDarwin_x86_64::RegisterContextDarwin_x86_64(
+ Thread &thread, uint32_t concrete_frame_idx)
+ : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextDarwin_x86_64::~RegisterContextDarwin_x86_64() {}
+
+void RegisterContextDarwin_x86_64::InvalidateAllRegisters() {
+ InvalidateAllRegisterStates();
+}
+
+size_t RegisterContextDarwin_x86_64::GetRegisterCount() {
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_x86_64::GetRegisterInfoAtIndex(size_t reg) {
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return nullptr;
+}
+
+size_t RegisterContextDarwin_x86_64::GetRegisterInfosCount() {
+ return k_num_register_infos;
+}
+
+const lldb_private::RegisterInfo *
+RegisterContextDarwin_x86_64::GetRegisterInfos() {
+ return g_register_infos;
+}
+
+static uint32_t g_gpr_regnums[] = {
+ gpr_rax, gpr_rbx, gpr_rcx, gpr_rdx, gpr_rdi, gpr_rsi, gpr_rbp,
+ gpr_rsp, gpr_r8, gpr_r9, gpr_r10, gpr_r11, gpr_r12, gpr_r13,
+ gpr_r14, gpr_r15, gpr_rip, gpr_rflags, gpr_cs, gpr_fs, gpr_gs};
+
+static uint32_t g_fpu_regnums[] = {
+ fpu_fcw, fpu_fsw, fpu_ftw, fpu_fop, fpu_ip, fpu_cs,
+ fpu_dp, fpu_ds, fpu_mxcsr, fpu_mxcsrmask, fpu_stmm0, fpu_stmm1,
+ fpu_stmm2, fpu_stmm3, fpu_stmm4, fpu_stmm5, fpu_stmm6, fpu_stmm7,
+ fpu_xmm0, fpu_xmm1, fpu_xmm2, fpu_xmm3, fpu_xmm4, fpu_xmm5,
+ fpu_xmm6, fpu_xmm7, fpu_xmm8, fpu_xmm9, fpu_xmm10, fpu_xmm11,
+ fpu_xmm12, fpu_xmm13, fpu_xmm14, fpu_xmm15};
+
+static uint32_t g_exc_regnums[] = {exc_trapno, exc_err, exc_faultvaddr};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = llvm::array_lengthof(g_gpr_regnums);
+const size_t k_num_fpu_registers = llvm::array_lengthof(g_fpu_regnums);
+const size_t k_num_exc_registers = llvm::array_lengthof(g_exc_regnums);
+
+// Register set definitions. The first definitions at register set index of
+// zero is for all registers, followed by other registers sets. The register
+// information for the all register set need not be filled in.
+static const RegisterSet g_reg_sets[] = {
+ {
+ "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums,
+ },
+ {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums},
+ {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}};
+
+const size_t k_num_regsets = llvm::array_lengthof(g_reg_sets);
+
+size_t RegisterContextDarwin_x86_64::GetRegisterSetCount() {
+ return k_num_regsets;
+}
+
+const RegisterSet *
+RegisterContextDarwin_x86_64::GetRegisterSet(size_t reg_set) {
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return nullptr;
+}
+
+int RegisterContextDarwin_x86_64::GetSetForNativeRegNum(int reg_num) {
+ if (reg_num < fpu_fcw)
+ return GPRRegSet;
+ else if (reg_num < exc_trapno)
+ return FPURegSet;
+ else if (reg_num < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+void RegisterContextDarwin_x86_64::LogGPR(Log *log, const char *format, ...) {
+ if (log) {
+ if (format) {
+ va_list args;
+ va_start(args, format);
+ log->VAPrintf(format, args);
+ va_end(args);
+ }
+ for (uint32_t i = 0; i < k_num_gpr_registers; i++) {
+ uint32_t reg = gpr_rax + i;
+ log->Printf("%12s = 0x%16.16" PRIx64, g_register_infos[reg].name,
+ (&gpr.rax)[reg]);
+ }
+ }
+}
+
+int RegisterContextDarwin_x86_64::ReadGPR(bool force) {
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr));
+ }
+ return GetError(GPRRegSet, Read);
+}
+
+int RegisterContextDarwin_x86_64::ReadFPU(bool force) {
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu));
+ }
+ return GetError(FPURegSet, Read);
+}
+
+int RegisterContextDarwin_x86_64::ReadEXC(bool force) {
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadEXC(GetThreadID(), set, exc));
+ }
+ return GetError(EXCRegSet, Read);
+}
+
+int RegisterContextDarwin_x86_64::WriteGPR() {
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_x86_64::WriteFPU() {
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_x86_64::WriteEXC() {
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_x86_64::ReadRegisterSet(uint32_t set, bool force) {
+ switch (set) {
+ case GPRRegSet:
+ return ReadGPR(force);
+ case FPURegSet:
+ return ReadFPU(force);
+ case EXCRegSet:
+ return ReadEXC(force);
+ default:
+ break;
+ }
+ return -1;
+}
+
+int RegisterContextDarwin_x86_64::WriteRegisterSet(uint32_t set) {
+ // Make sure we have a valid context to set.
+ switch (set) {
+ case GPRRegSet:
+ return WriteGPR();
+ case FPURegSet:
+ return WriteFPU();
+ case EXCRegSet:
+ return WriteEXC();
+ default:
+ break;
+ }
+ return -1;
+}
+
+bool RegisterContextDarwin_x86_64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_x86_64::GetSetForNativeRegNum(reg);
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg) {
+ case gpr_rax:
+ case gpr_rbx:
+ case gpr_rcx:
+ case gpr_rdx:
+ case gpr_rdi:
+ case gpr_rsi:
+ case gpr_rbp:
+ case gpr_rsp:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_r13:
+ case gpr_r14:
+ case gpr_r15:
+ case gpr_rip:
+ case gpr_rflags:
+ case gpr_cs:
+ case gpr_fs:
+ case gpr_gs:
+ value = (&gpr.rax)[reg - gpr_rax];
+ break;
+
+ case fpu_fcw:
+ value = fpu.fcw;
+ break;
+
+ case fpu_fsw:
+ value = fpu.fsw;
+ break;
+
+ case fpu_ftw:
+ value = fpu.ftw;
+ break;
+
+ case fpu_fop:
+ value = fpu.fop;
+ break;
+
+ case fpu_ip:
+ value = fpu.ip;
+ break;
+
+ case fpu_cs:
+ value = fpu.cs;
+ break;
+
+ case fpu_dp:
+ value = fpu.dp;
+ break;
+
+ case fpu_ds:
+ value = fpu.ds;
+ break;
+
+ case fpu_mxcsr:
+ value = fpu.mxcsr;
+ break;
+
+ case fpu_mxcsrmask:
+ value = fpu.mxcsrmask;
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ value.SetBytes(fpu.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size,
+ endian::InlHostByteOrder());
+ break;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ value.SetBytes(fpu.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size,
+ endian::InlHostByteOrder());
+ break;
+
+ case exc_trapno:
+ value = exc.trapno;
+ break;
+
+ case exc_err:
+ value = exc.err;
+ break;
+
+ case exc_faultvaddr:
+ value = exc.faultvaddr;
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegisterContextDarwin_x86_64::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_x86_64::GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg) {
+ case gpr_rax:
+ case gpr_rbx:
+ case gpr_rcx:
+ case gpr_rdx:
+ case gpr_rdi:
+ case gpr_rsi:
+ case gpr_rbp:
+ case gpr_rsp:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_r13:
+ case gpr_r14:
+ case gpr_r15:
+ case gpr_rip:
+ case gpr_rflags:
+ case gpr_cs:
+ case gpr_fs:
+ case gpr_gs:
+ (&gpr.rax)[reg - gpr_rax] = value.GetAsUInt64();
+ break;
+
+ case fpu_fcw:
+ fpu.fcw = value.GetAsUInt16();
+ break;
+
+ case fpu_fsw:
+ fpu.fsw = value.GetAsUInt16();
+ break;
+
+ case fpu_ftw:
+ fpu.ftw = value.GetAsUInt8();
+ break;
+
+ case fpu_fop:
+ fpu.fop = value.GetAsUInt16();
+ break;
+
+ case fpu_ip:
+ fpu.ip = value.GetAsUInt32();
+ break;
+
+ case fpu_cs:
+ fpu.cs = value.GetAsUInt16();
+ break;
+
+ case fpu_dp:
+ fpu.dp = value.GetAsUInt32();
+ break;
+
+ case fpu_ds:
+ fpu.ds = value.GetAsUInt16();
+ break;
+
+ case fpu_mxcsr:
+ fpu.mxcsr = value.GetAsUInt32();
+ break;
+
+ case fpu_mxcsrmask:
+ fpu.mxcsrmask = value.GetAsUInt32();
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ ::memcpy(fpu.stmm[reg - fpu_stmm0].bytes, value.GetBytes(),
+ value.GetByteSize());
+ break;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ ::memcpy(fpu.xmm[reg - fpu_xmm0].bytes, value.GetBytes(),
+ value.GetByteSize());
+ return false;
+
+ case exc_trapno:
+ exc.trapno = value.GetAsUInt32();
+ break;
+
+ case exc_err:
+ exc.err = value.GetAsUInt32();
+ break;
+
+ case exc_faultvaddr:
+ exc.faultvaddr = value.GetAsUInt64();
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == 0;
+}
+
+bool RegisterContextDarwin_x86_64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0);
+ if (ReadGPR(false) == 0 && ReadFPU(false) == 0 && ReadEXC(false) == 0) {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextDarwin_x86_64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy(&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy(&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy(&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == 0)
+ ++success_count;
+ if (WriteFPU() == 0)
+ ++success_count;
+ if (WriteEXC() == 0)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+uint32_t RegisterContextDarwin_x86_64::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t reg) {
+ if (kind == eRegisterKindGeneric) {
+ switch (reg) {
+ case LLDB_REGNUM_GENERIC_PC:
+ return gpr_rip;
+ case LLDB_REGNUM_GENERIC_SP:
+ return gpr_rsp;
+ case LLDB_REGNUM_GENERIC_FP:
+ return gpr_rbp;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ return gpr_rflags;
+ case LLDB_REGNUM_GENERIC_RA:
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindEHFrame || kind == eRegisterKindDWARF) {
+ switch (reg) {
+ case ehframe_dwarf_gpr_rax:
+ return gpr_rax;
+ case ehframe_dwarf_gpr_rdx:
+ return gpr_rdx;
+ case ehframe_dwarf_gpr_rcx:
+ return gpr_rcx;
+ case ehframe_dwarf_gpr_rbx:
+ return gpr_rbx;
+ case ehframe_dwarf_gpr_rsi:
+ return gpr_rsi;
+ case ehframe_dwarf_gpr_rdi:
+ return gpr_rdi;
+ case ehframe_dwarf_gpr_rbp:
+ return gpr_rbp;
+ case ehframe_dwarf_gpr_rsp:
+ return gpr_rsp;
+ case ehframe_dwarf_gpr_r8:
+ return gpr_r8;
+ case ehframe_dwarf_gpr_r9:
+ return gpr_r9;
+ case ehframe_dwarf_gpr_r10:
+ return gpr_r10;
+ case ehframe_dwarf_gpr_r11:
+ return gpr_r11;
+ case ehframe_dwarf_gpr_r12:
+ return gpr_r12;
+ case ehframe_dwarf_gpr_r13:
+ return gpr_r13;
+ case ehframe_dwarf_gpr_r14:
+ return gpr_r14;
+ case ehframe_dwarf_gpr_r15:
+ return gpr_r15;
+ case ehframe_dwarf_gpr_rip:
+ return gpr_rip;
+ case ehframe_dwarf_fpu_xmm0:
+ return fpu_xmm0;
+ case ehframe_dwarf_fpu_xmm1:
+ return fpu_xmm1;
+ case ehframe_dwarf_fpu_xmm2:
+ return fpu_xmm2;
+ case ehframe_dwarf_fpu_xmm3:
+ return fpu_xmm3;
+ case ehframe_dwarf_fpu_xmm4:
+ return fpu_xmm4;
+ case ehframe_dwarf_fpu_xmm5:
+ return fpu_xmm5;
+ case ehframe_dwarf_fpu_xmm6:
+ return fpu_xmm6;
+ case ehframe_dwarf_fpu_xmm7:
+ return fpu_xmm7;
+ case ehframe_dwarf_fpu_xmm8:
+ return fpu_xmm8;
+ case ehframe_dwarf_fpu_xmm9:
+ return fpu_xmm9;
+ case ehframe_dwarf_fpu_xmm10:
+ return fpu_xmm10;
+ case ehframe_dwarf_fpu_xmm11:
+ return fpu_xmm11;
+ case ehframe_dwarf_fpu_xmm12:
+ return fpu_xmm12;
+ case ehframe_dwarf_fpu_xmm13:
+ return fpu_xmm13;
+ case ehframe_dwarf_fpu_xmm14:
+ return fpu_xmm14;
+ case ehframe_dwarf_fpu_xmm15:
+ return fpu_xmm15;
+ case ehframe_dwarf_fpu_stmm0:
+ return fpu_stmm0;
+ case ehframe_dwarf_fpu_stmm1:
+ return fpu_stmm1;
+ case ehframe_dwarf_fpu_stmm2:
+ return fpu_stmm2;
+ case ehframe_dwarf_fpu_stmm3:
+ return fpu_stmm3;
+ case ehframe_dwarf_fpu_stmm4:
+ return fpu_stmm4;
+ case ehframe_dwarf_fpu_stmm5:
+ return fpu_stmm5;
+ case ehframe_dwarf_fpu_stmm6:
+ return fpu_stmm6;
+ case ehframe_dwarf_fpu_stmm7:
+ return fpu_stmm7;
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindLLDB) {
+ return reg;
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+bool RegisterContextDarwin_x86_64::HardwareSingleStep(bool enable) {
+ if (ReadGPR(true) != 0)
+ return false;
+
+ const uint64_t trace_bit = 0x100ull;
+ if (enable) {
+
+ if (gpr.rflags & trace_bit)
+ return true; // trace bit is already set, there is nothing to do
+ else
+ gpr.rflags |= trace_bit;
+ } else {
+ if (gpr.rflags & trace_bit)
+ gpr.rflags &= ~trace_bit;
+ else
+ return true; // trace bit is clear, there is nothing to do
+ }
+
+ return WriteGPR() == 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h
new file mode 100644
index 000000000000..1a65a4f28b33
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h
@@ -0,0 +1,213 @@
+//===-- RegisterContextDarwin_x86_64.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextDarwin_x86_64_h_
+#define liblldb_RegisterContextDarwin_x86_64_h_
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+class RegisterContextDarwin_x86_64 : public lldb_private::RegisterContext {
+public:
+ RegisterContextDarwin_x86_64(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ ~RegisterContextDarwin_x86_64() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+ struct GPR {
+ uint64_t rax;
+ uint64_t rbx;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rbp;
+ uint64_t rsp;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rip;
+ uint64_t rflags;
+ uint64_t cs;
+ uint64_t fs;
+ uint64_t gs;
+ };
+
+ struct MMSReg {
+ uint8_t bytes[10];
+ uint8_t pad[6];
+ };
+
+ struct XMMReg {
+ uint8_t bytes[16];
+ };
+
+ struct FPU {
+ uint32_t pad[2];
+ uint16_t fcw; // "fctrl"
+ uint16_t fsw; // "fstat"
+ uint8_t ftw; // "ftag"
+ uint8_t pad1;
+ uint16_t fop; // "fop"
+ uint32_t ip; // "fioff"
+ uint16_t cs; // "fiseg"
+ uint16_t pad2;
+ uint32_t dp; // "fooff"
+ uint16_t ds; // "foseg"
+ uint16_t pad3;
+ uint32_t mxcsr;
+ uint32_t mxcsrmask;
+ MMSReg stmm[8];
+ XMMReg xmm[16];
+ uint8_t pad4[6 * 16];
+ int pad5;
+ };
+
+ struct EXC {
+ uint32_t trapno;
+ uint32_t err;
+ uint64_t faultvaddr;
+ };
+
+protected:
+ enum { GPRRegSet = 4, FPURegSet = 5, EXCRegSet = 6 };
+
+ enum {
+ GPRWordCount = sizeof(GPR) / sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU) / sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC) / sizeof(uint32_t)
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ int gpr_errs[2]; // Read/Write errors
+ int fpu_errs[2]; // Read/Write errors
+ int exc_errs[2]; // Read/Write errors
+
+ void InvalidateAllRegisterStates() {
+ SetError(GPRRegSet, Read, -1);
+ SetError(FPURegSet, Read, -1);
+ SetError(EXCRegSet, Read, -1);
+ }
+
+ int GetError(int flavor, uint32_t err_idx) const {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case GPRRegSet:
+ return gpr_errs[err_idx];
+ case FPURegSet:
+ return fpu_errs[err_idx];
+ case EXCRegSet:
+ return exc_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+
+ bool SetError(int flavor, uint32_t err_idx, int err) {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+
+ bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; }
+
+ void LogGPR(lldb_private::Log *log, const char *format, ...);
+
+ int ReadGPR(bool force);
+
+ int ReadFPU(bool force);
+
+ int ReadEXC(bool force);
+
+ int WriteGPR();
+
+ int WriteFPU();
+
+ int WriteEXC();
+
+ // Subclasses override these to do the actual reading.
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) = 0;
+
+ virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0;
+
+ virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0;
+
+ virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0;
+
+ virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0;
+
+ virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0;
+
+ int ReadRegisterSet(uint32_t set, bool force);
+
+ int WriteRegisterSet(uint32_t set);
+
+ static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num);
+
+ static int GetSetForNativeRegNum(int reg_num);
+
+ static size_t GetRegisterInfosCount();
+
+ static const lldb_private::RegisterInfo *GetRegisterInfos();
+};
+
+#endif // liblldb_RegisterContextDarwin_x86_64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.cpp
new file mode 100644
index 000000000000..6832b6095931
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.cpp
@@ -0,0 +1,120 @@
+//===-- RegisterContextDummy.cpp ---------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/lldb-private.h"
+
+#include "RegisterContextDummy.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextDummy::RegisterContextDummy(Thread &thread,
+ uint32_t concrete_frame_idx,
+ uint32_t address_byte_size)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_reg_set0.name = "General Purpose Registers";
+ m_reg_set0.short_name = "GPR";
+ m_reg_set0.num_registers = 1;
+ m_reg_set0.registers = new uint32_t(0);
+
+ m_pc_reg_info.name = "pc";
+ m_pc_reg_info.alt_name = "pc";
+ m_pc_reg_info.byte_offset = 0;
+ m_pc_reg_info.byte_size = address_byte_size;
+ m_pc_reg_info.encoding = eEncodingUint;
+ m_pc_reg_info.format = eFormatPointer;
+ m_pc_reg_info.invalidate_regs = nullptr;
+ m_pc_reg_info.value_regs = nullptr;
+ m_pc_reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ m_pc_reg_info.kinds[eRegisterKindProcessPlugin] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindLLDB] = LLDB_INVALID_REGNUM;
+}
+
+RegisterContextDummy::~RegisterContextDummy() {
+ delete m_reg_set0.registers;
+ delete m_pc_reg_info.invalidate_regs;
+ delete m_pc_reg_info.value_regs;
+}
+
+void RegisterContextDummy::InvalidateAllRegisters() {}
+
+size_t RegisterContextDummy::GetRegisterCount() { return 1; }
+
+const lldb_private::RegisterInfo *
+RegisterContextDummy::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg)
+ return nullptr;
+ return &m_pc_reg_info;
+}
+
+size_t RegisterContextDummy::GetRegisterSetCount() { return 1; }
+
+const lldb_private::RegisterSet *
+RegisterContextDummy::GetRegisterSet(size_t reg_set) {
+ if (reg_set)
+ return nullptr;
+ return &m_reg_set0;
+}
+
+bool RegisterContextDummy::ReadRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) {
+ if (!reg_info)
+ return false;
+ uint32_t reg_number = reg_info->kinds[eRegisterKindGeneric];
+ if (reg_number == LLDB_REGNUM_GENERIC_PC) {
+ value.SetUInt(LLDB_INVALID_ADDRESS, reg_info->byte_size);
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextDummy::WriteRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextDummy::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextDummy::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+uint32_t RegisterContextDummy::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ if (kind == eRegisterKindGeneric && num == LLDB_REGNUM_GENERIC_PC)
+ return 0;
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.h
new file mode 100644
index 000000000000..bdaa2217d207
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.h
@@ -0,0 +1,64 @@
+//===-- RegisterContextDummy.h ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextDummy_h_
+#define lldb_RegisterContextDummy_h_
+
+#include <vector>
+
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class RegisterContextDummy : public lldb_private::RegisterContext {
+public:
+ typedef std::shared_ptr<RegisterContextDummy> SharedPtr;
+
+ RegisterContextDummy(Thread &thread, uint32_t concrete_frame_idx,
+ uint32_t address_byte_size);
+
+ ~RegisterContextDummy() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+private:
+ // For RegisterContextLLDB only
+
+ lldb_private::RegisterSet m_reg_set0; // register set 0 (PC only)
+ lldb_private::RegisterInfo m_pc_reg_info;
+
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextDummy);
+};
+
+} // namespace lldb_private
+
+#endif // lldb_RegisterContextDummy_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp
new file mode 100644
index 000000000000..b90b38108267
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp
@@ -0,0 +1,80 @@
+//===-- RegisterContextFreeBSD_i386.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextFreeBSD_i386.h"
+#include "RegisterContextPOSIX_x86.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// http://svnweb.freebsd.org/base/head/sys/x86/include/reg.h
+struct GPR {
+ uint32_t fs;
+ uint32_t es;
+ uint32_t ds;
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t isp;
+ uint32_t ebx;
+ uint32_t edx;
+ uint32_t ecx;
+ uint32_t eax;
+ uint32_t trapno;
+ uint32_t err;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+ uint32_t esp;
+ uint32_t ss;
+ uint32_t gs;
+};
+
+struct dbreg {
+ uint32_t dr[8]; /* debug registers */
+ /* Index 0-3: debug address registers */
+ /* Index 4-5: reserved */
+ /* Index 6: debug status */
+ /* Index 7: debug control */
+};
+
+using FPR_i386 = FXSAVE;
+
+struct UserArea {
+ GPR gpr;
+ FPR_i386 i387;
+};
+
+#define DR_SIZE sizeof(uint32_t)
+#define DR_OFFSET(reg_index) (LLVM_EXTENSION offsetof(dbreg, dr[reg_index]))
+
+// Include RegisterInfos_i386 to declare our g_register_infos_i386 structure.
+#define DECLARE_REGISTER_INFOS_I386_STRUCT
+#include "RegisterInfos_i386.h"
+#undef DECLARE_REGISTER_INFOS_I386_STRUCT
+
+RegisterContextFreeBSD_i386::RegisterContextFreeBSD_i386(
+ const ArchSpec &target_arch)
+ : RegisterInfoInterface(target_arch) {}
+
+size_t RegisterContextFreeBSD_i386::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextFreeBSD_i386::GetRegisterInfo() const {
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return g_register_infos_i386;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+uint32_t RegisterContextFreeBSD_i386::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_i386) /
+ sizeof(g_register_infos_i386[0]));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.h
new file mode 100644
index 000000000000..7aadf3a0a4c9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.h
@@ -0,0 +1,25 @@
+//===-- RegisterContextFreeBSD_i386.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextFreeBSD_i386_H_
+#define liblldb_RegisterContextFreeBSD_i386_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextFreeBSD_i386 : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextFreeBSD_i386(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp
new file mode 100644
index 000000000000..4331ef5ad14e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp
@@ -0,0 +1,117 @@
+//===-- RegisterContextFreeBSD_mips64.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextFreeBSD_mips64.h"
+#include "RegisterContextPOSIX_mips64.h"
+#include "lldb-mips-freebsd-register-enums.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb;
+
+static const uint32_t g_gpr_regnums[] = {
+ gpr_zero_mips64, gpr_r1_mips64, gpr_r2_mips64, gpr_r3_mips64,
+ gpr_r4_mips64, gpr_r5_mips64, gpr_r6_mips64, gpr_r7_mips64,
+ gpr_r8_mips64, gpr_r9_mips64, gpr_r10_mips64, gpr_r11_mips64,
+ gpr_r12_mips64, gpr_r13_mips64, gpr_r14_mips64, gpr_r15_mips64,
+ gpr_r16_mips64, gpr_r17_mips64, gpr_r18_mips64, gpr_r19_mips64,
+ gpr_r20_mips64, gpr_r21_mips64, gpr_r22_mips64, gpr_r23_mips64,
+ gpr_r24_mips64, gpr_r25_mips64, gpr_r26_mips64, gpr_r27_mips64,
+ gpr_gp_mips64, gpr_sp_mips64, gpr_r30_mips64, gpr_ra_mips64,
+ gpr_sr_mips64, gpr_mullo_mips64, gpr_mulhi_mips64, gpr_badvaddr_mips64,
+ gpr_cause_mips64, gpr_pc_mips64, gpr_ic_mips64, gpr_dummy_mips64};
+
+// Number of register sets provided by this context.
+constexpr size_t k_num_register_sets = 1;
+
+static const RegisterSet g_reg_sets_mips64[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_mips64,
+ g_gpr_regnums},
+};
+
+
+// http://svnweb.freebsd.org/base/head/sys/mips/include/regnum.h
+typedef struct _GPR {
+ uint64_t zero;
+ uint64_t r1;
+ uint64_t r2;
+ uint64_t r3;
+ uint64_t r4;
+ uint64_t r5;
+ uint64_t r6;
+ uint64_t r7;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t r16;
+ uint64_t r17;
+ uint64_t r18;
+ uint64_t r19;
+ uint64_t r20;
+ uint64_t r21;
+ uint64_t r22;
+ uint64_t r23;
+ uint64_t r24;
+ uint64_t r25;
+ uint64_t r26;
+ uint64_t r27;
+ uint64_t gp;
+ uint64_t sp;
+ uint64_t r30;
+ uint64_t ra;
+ uint64_t sr;
+ uint64_t mullo;
+ uint64_t mulhi;
+ uint64_t badvaddr;
+ uint64_t cause;
+ uint64_t pc;
+ uint64_t ic;
+ uint64_t dummy;
+} GPR_freebsd_mips;
+
+// Include RegisterInfos_mips64 to declare our g_register_infos_mips64
+// structure.
+#define DECLARE_REGISTER_INFOS_MIPS64_STRUCT
+#include "RegisterInfos_mips64.h"
+#undef DECLARE_REGISTER_INFOS_MIPS64_STRUCT
+
+RegisterContextFreeBSD_mips64::RegisterContextFreeBSD_mips64(
+ const ArchSpec &target_arch)
+ : RegisterInfoInterface(target_arch) {}
+
+size_t RegisterContextFreeBSD_mips64::GetGPRSize() const {
+ return sizeof(GPR_freebsd_mips);
+}
+
+const RegisterSet *
+RegisterContextFreeBSD_mips64::GetRegisterSet(size_t set) const {
+ // Check if RegisterSet is available
+ if (set < k_num_register_sets)
+ return &g_reg_sets_mips64[set];
+ return nullptr;
+}
+
+size_t
+RegisterContextFreeBSD_mips64::GetRegisterSetCount() const {
+ return k_num_register_sets;
+}
+
+const RegisterInfo *RegisterContextFreeBSD_mips64::GetRegisterInfo() const {
+ assert(m_target_arch.GetCore() == ArchSpec::eCore_mips64);
+ return g_register_infos_mips64;
+}
+
+uint32_t RegisterContextFreeBSD_mips64::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_mips64) /
+ sizeof(g_register_infos_mips64[0]));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h
new file mode 100644
index 000000000000..96f02b4440c5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h
@@ -0,0 +1,30 @@
+//===-- RegisterContextFreeBSD_mips64.h -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextFreeBSD_mips64_H_
+#define liblldb_RegisterContextFreeBSD_mips64_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextFreeBSD_mips64
+ : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextFreeBSD_mips64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) const;
+
+ size_t GetRegisterSetCount() const;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp
new file mode 100644
index 000000000000..4f869eb3b177
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp
@@ -0,0 +1,236 @@
+//===-- RegisterContextFreeBSD_powerpc.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextFreeBSD_powerpc.h"
+#include "RegisterContextPOSIX_powerpc.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb;
+
+// http://svnweb.freebsd.org/base/head/sys/powerpc/include/reg.h
+typedef struct _GPR64 {
+ uint64_t r0;
+ uint64_t r1;
+ uint64_t r2;
+ uint64_t r3;
+ uint64_t r4;
+ uint64_t r5;
+ uint64_t r6;
+ uint64_t r7;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t r16;
+ uint64_t r17;
+ uint64_t r18;
+ uint64_t r19;
+ uint64_t r20;
+ uint64_t r21;
+ uint64_t r22;
+ uint64_t r23;
+ uint64_t r24;
+ uint64_t r25;
+ uint64_t r26;
+ uint64_t r27;
+ uint64_t r28;
+ uint64_t r29;
+ uint64_t r30;
+ uint64_t r31;
+ uint64_t lr;
+ uint64_t cr;
+ uint64_t xer;
+ uint64_t ctr;
+ uint64_t pc;
+} GPR64;
+
+typedef struct _GPR32 {
+ uint32_t r0;
+ uint32_t r1;
+ uint32_t r2;
+ uint32_t r3;
+ uint32_t r4;
+ uint32_t r5;
+ uint32_t r6;
+ uint32_t r7;
+ uint32_t r8;
+ uint32_t r9;
+ uint32_t r10;
+ uint32_t r11;
+ uint32_t r12;
+ uint32_t r13;
+ uint32_t r14;
+ uint32_t r15;
+ uint32_t r16;
+ uint32_t r17;
+ uint32_t r18;
+ uint32_t r19;
+ uint32_t r20;
+ uint32_t r21;
+ uint32_t r22;
+ uint32_t r23;
+ uint32_t r24;
+ uint32_t r25;
+ uint32_t r26;
+ uint32_t r27;
+ uint32_t r28;
+ uint32_t r29;
+ uint32_t r30;
+ uint32_t r31;
+ uint32_t lr;
+ uint32_t cr;
+ uint32_t xer;
+ uint32_t ctr;
+ uint32_t pc;
+} GPR32;
+
+typedef struct _FPR {
+ uint64_t f0;
+ uint64_t f1;
+ uint64_t f2;
+ uint64_t f3;
+ uint64_t f4;
+ uint64_t f5;
+ uint64_t f6;
+ uint64_t f7;
+ uint64_t f8;
+ uint64_t f9;
+ uint64_t f10;
+ uint64_t f11;
+ uint64_t f12;
+ uint64_t f13;
+ uint64_t f14;
+ uint64_t f15;
+ uint64_t f16;
+ uint64_t f17;
+ uint64_t f18;
+ uint64_t f19;
+ uint64_t f20;
+ uint64_t f21;
+ uint64_t f22;
+ uint64_t f23;
+ uint64_t f24;
+ uint64_t f25;
+ uint64_t f26;
+ uint64_t f27;
+ uint64_t f28;
+ uint64_t f29;
+ uint64_t f30;
+ uint64_t f31;
+ uint64_t fpscr;
+} FPR;
+
+typedef struct _VMX {
+ uint32_t v0[4];
+ uint32_t v1[4];
+ uint32_t v2[4];
+ uint32_t v3[4];
+ uint32_t v4[4];
+ uint32_t v5[4];
+ uint32_t v6[4];
+ uint32_t v7[4];
+ uint32_t v8[4];
+ uint32_t v9[4];
+ uint32_t v10[4];
+ uint32_t v11[4];
+ uint32_t v12[4];
+ uint32_t v13[4];
+ uint32_t v14[4];
+ uint32_t v15[4];
+ uint32_t v16[4];
+ uint32_t v17[4];
+ uint32_t v18[4];
+ uint32_t v19[4];
+ uint32_t v20[4];
+ uint32_t v21[4];
+ uint32_t v22[4];
+ uint32_t v23[4];
+ uint32_t v24[4];
+ uint32_t v25[4];
+ uint32_t v26[4];
+ uint32_t v27[4];
+ uint32_t v28[4];
+ uint32_t v29[4];
+ uint32_t v30[4];
+ uint32_t v31[4];
+ uint32_t pad[2];
+ uint32_t vrsave;
+ uint32_t vscr;
+} VMX;
+
+// Include RegisterInfos_powerpc to declare our g_register_infos_powerpc
+// structure.
+#define DECLARE_REGISTER_INFOS_POWERPC_STRUCT
+#include "RegisterInfos_powerpc.h"
+#undef DECLARE_REGISTER_INFOS_POWERPC_STRUCT
+
+RegisterContextFreeBSD_powerpc::RegisterContextFreeBSD_powerpc(
+ const ArchSpec &target_arch)
+ : RegisterInfoInterface(target_arch) {}
+
+RegisterContextFreeBSD_powerpc::~RegisterContextFreeBSD_powerpc() {}
+
+size_t RegisterContextFreeBSD_powerpc::GetGPRSize() const {
+ // This is an 'abstract' base, so no GPR struct.
+ return 0;
+}
+
+const RegisterInfo *RegisterContextFreeBSD_powerpc::GetRegisterInfo() const {
+ // assert (m_target_arch.GetCore() == ArchSpec::eCore_powerpc);
+ llvm_unreachable("Abstract class!");
+ return nullptr;
+}
+
+uint32_t RegisterContextFreeBSD_powerpc::GetRegisterCount() const { return 0; }
+
+RegisterContextFreeBSD_powerpc32::RegisterContextFreeBSD_powerpc32(
+ const ArchSpec &target_arch)
+ : RegisterContextFreeBSD_powerpc(target_arch) {}
+
+RegisterContextFreeBSD_powerpc32::~RegisterContextFreeBSD_powerpc32() {}
+
+size_t RegisterContextFreeBSD_powerpc32::GetGPRSize() const {
+ return sizeof(GPR32);
+}
+
+const RegisterInfo *RegisterContextFreeBSD_powerpc32::GetRegisterInfo() const {
+ // assert (m_target_arch.GetCore() == ArchSpec::eCore_powerpc);
+ return g_register_infos_powerpc32;
+}
+
+uint32_t RegisterContextFreeBSD_powerpc32::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_powerpc32) /
+ sizeof(g_register_infos_powerpc32[0]));
+}
+
+RegisterContextFreeBSD_powerpc64::RegisterContextFreeBSD_powerpc64(
+ const ArchSpec &target_arch)
+ : RegisterContextFreeBSD_powerpc(target_arch) {}
+
+RegisterContextFreeBSD_powerpc64::~RegisterContextFreeBSD_powerpc64() {}
+
+size_t RegisterContextFreeBSD_powerpc64::GetGPRSize() const {
+ return sizeof(GPR64);
+}
+
+const RegisterInfo *RegisterContextFreeBSD_powerpc64::GetRegisterInfo() const {
+ // assert (m_target_arch.GetCore() == ArchSpec::eCore_powerpc);
+ if (m_target_arch.GetMachine() == llvm::Triple::ppc)
+ return g_register_infos_powerpc64_32;
+ return g_register_infos_powerpc64;
+}
+
+uint32_t RegisterContextFreeBSD_powerpc64::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_powerpc64) /
+ sizeof(g_register_infos_powerpc64[0]));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h
new file mode 100644
index 000000000000..ba2751194d16
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h
@@ -0,0 +1,52 @@
+//===-- RegisterContextFreeBSD_powerpc.h -------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextFreeBSD_powerpc_h_
+#define liblldb_RegisterContextFreeBSD_powerpc_h_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextFreeBSD_powerpc
+ : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextFreeBSD_powerpc(const lldb_private::ArchSpec &target_arch);
+ ~RegisterContextFreeBSD_powerpc() override;
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+class RegisterContextFreeBSD_powerpc32 : public RegisterContextFreeBSD_powerpc {
+public:
+ RegisterContextFreeBSD_powerpc32(const lldb_private::ArchSpec &target_arch);
+ ~RegisterContextFreeBSD_powerpc32() override;
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+class RegisterContextFreeBSD_powerpc64 : public RegisterContextFreeBSD_powerpc {
+public:
+ RegisterContextFreeBSD_powerpc64(const lldb_private::ArchSpec &target_arch);
+ ~RegisterContextFreeBSD_powerpc64() override;
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+#endif // liblldb_RegisterContextFreeBSD_powerpc_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp
new file mode 100644
index 000000000000..bcf3951ee077
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp
@@ -0,0 +1,143 @@
+//===-- RegisterContextFreeBSD_x86_64.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextFreeBSD_x86_64.h"
+#include "RegisterContextFreeBSD_i386.h"
+#include "RegisterContextPOSIX_x86.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb;
+
+// http://svnweb.freebsd.org/base/head/sys/x86/include/reg.h
+typedef struct _GPR {
+ uint64_t r15;
+ uint64_t r14;
+ uint64_t r13;
+ uint64_t r12;
+ uint64_t r11;
+ uint64_t r10;
+ uint64_t r9;
+ uint64_t r8;
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t rdx;
+ uint64_t rcx;
+ uint64_t rax;
+ uint32_t trapno;
+ uint16_t fs;
+ uint16_t gs;
+ uint32_t err;
+ uint16_t es;
+ uint16_t ds;
+ uint64_t rip;
+ uint64_t cs;
+ uint64_t rflags;
+ uint64_t rsp;
+ uint64_t ss;
+} GPR;
+
+struct DBG {
+ uint64_t dr[16]; /* debug registers */
+ /* Index 0-3: debug address registers */
+ /* Index 4-5: reserved */
+ /* Index 6: debug status */
+ /* Index 7: debug control */
+ /* Index 8-15: reserved */
+};
+
+struct UserArea {
+ GPR gpr;
+ FPR fpr;
+ DBG dbg;
+};
+
+#define DR_OFFSET(reg_index) (LLVM_EXTENSION offsetof(DBG, dr[reg_index]))
+
+// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64
+// structure.
+#define DECLARE_REGISTER_INFOS_X86_64_STRUCT
+#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]);
+
+// 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];
+}
+
+static const RegisterInfo *
+PrivateGetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return GetRegisterInfo_i386(target_arch);
+ case llvm::Triple::x86_64:
+ return g_register_infos_x86_64;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+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() &&
+ "i386 register info vector not filled.");
+ return static_cast<uint32_t>(GetSharedRegisterInfoVector().size());
+ case llvm::Triple::x86_64:
+ return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) /
+ sizeof(g_register_infos_x86_64[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextFreeBSD_x86_64::RegisterContextFreeBSD_x86_64(
+ const ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(PrivateGetRegisterInfoPtr(target_arch)),
+ m_register_count(PrivateGetRegisterCount(target_arch)) {}
+
+size_t RegisterContextFreeBSD_x86_64::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextFreeBSD_x86_64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextFreeBSD_x86_64::GetRegisterCount() const {
+ return m_register_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h
new file mode 100644
index 000000000000..c379e1a5cd75
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h
@@ -0,0 +1,30 @@
+//===-- RegisterContextFreeBSD_x86_64.h -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextFreeBSD_x86_64_H_
+#define liblldb_RegisterContextFreeBSD_x86_64_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextFreeBSD_x86_64
+ : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextFreeBSD_x86_64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ const uint32_t m_register_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.cpp
new file mode 100644
index 000000000000..c19a2bfae668
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.cpp
@@ -0,0 +1,122 @@
+//===-- RegisterContextHistory.cpp ---------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/lldb-private.h"
+
+#include "RegisterContextHistory.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextHistory::RegisterContextHistory(Thread &thread,
+ uint32_t concrete_frame_idx,
+ uint32_t address_byte_size,
+ addr_t pc_value)
+ : RegisterContext(thread, concrete_frame_idx), m_pc_value(pc_value) {
+ m_reg_set0.name = "General Purpose Registers";
+ m_reg_set0.short_name = "GPR";
+ m_reg_set0.num_registers = 1;
+ m_reg_set0.registers = new uint32_t(0);
+
+ m_pc_reg_info.name = "pc";
+ m_pc_reg_info.alt_name = "pc";
+ m_pc_reg_info.byte_offset = 0;
+ m_pc_reg_info.byte_size = address_byte_size;
+ m_pc_reg_info.encoding = eEncodingUint;
+ m_pc_reg_info.format = eFormatPointer;
+ m_pc_reg_info.invalidate_regs = nullptr;
+ m_pc_reg_info.value_regs = nullptr;
+ m_pc_reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ m_pc_reg_info.kinds[eRegisterKindProcessPlugin] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindLLDB] = LLDB_INVALID_REGNUM;
+}
+
+RegisterContextHistory::~RegisterContextHistory() {
+ delete m_reg_set0.registers;
+ delete m_pc_reg_info.invalidate_regs;
+ delete m_pc_reg_info.value_regs;
+}
+
+void RegisterContextHistory::InvalidateAllRegisters() {}
+
+size_t RegisterContextHistory::GetRegisterCount() { return 1; }
+
+const lldb_private::RegisterInfo *
+RegisterContextHistory::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg)
+ return nullptr;
+ return &m_pc_reg_info;
+}
+
+size_t RegisterContextHistory::GetRegisterSetCount() { return 1; }
+
+const lldb_private::RegisterSet *
+RegisterContextHistory::GetRegisterSet(size_t reg_set) {
+ if (reg_set)
+ return nullptr;
+ return &m_reg_set0;
+}
+
+bool RegisterContextHistory::ReadRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) {
+ if (!reg_info)
+ return false;
+ uint32_t reg_number = reg_info->kinds[eRegisterKindGeneric];
+ if (reg_number == LLDB_REGNUM_GENERIC_PC) {
+ value.SetUInt(m_pc_value, reg_info->byte_size);
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextHistory::WriteRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextHistory::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextHistory::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+uint32_t RegisterContextHistory::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ if (kind == eRegisterKindGeneric && num == LLDB_REGNUM_GENERIC_PC)
+ return 0;
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.h
new file mode 100644
index 000000000000..952e4263d955
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.h
@@ -0,0 +1,65 @@
+//===-- RegisterContextHistory.h ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextHistory_h_
+#define lldb_RegisterContextHistory_h_
+
+#include <vector>
+
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class RegisterContextHistory : public lldb_private::RegisterContext {
+public:
+ typedef std::shared_ptr<RegisterContextHistory> SharedPtr;
+
+ RegisterContextHistory(Thread &thread, uint32_t concrete_frame_idx,
+ uint32_t address_byte_size, lldb::addr_t pc_value);
+
+ ~RegisterContextHistory() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+private:
+ // For RegisterContextLLDB only
+
+ lldb_private::RegisterSet m_reg_set0; // register set 0 (PC only)
+ lldb_private::RegisterInfo m_pc_reg_info;
+
+ lldb::addr_t m_pc_value;
+
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextHistory);
+};
+} // namespace lldb_private
+
+#endif // lldb_RegisterContextHistory_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
new file mode 100644
index 000000000000..76646d8897d1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
@@ -0,0 +1,2105 @@
+//===-- RegisterContextLLDB.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/ArmUnwindInfo.h"
+#include "lldb/Symbol/DWARFCallFrameInfo.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/lldb-private.h"
+
+#include "RegisterContextLLDB.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static ConstString GetSymbolOrFunctionName(const SymbolContext &sym_ctx) {
+ if (sym_ctx.symbol)
+ return sym_ctx.symbol->GetName();
+ else if (sym_ctx.function)
+ return sym_ctx.function->GetName();
+ return ConstString();
+}
+
+RegisterContextLLDB::RegisterContextLLDB(Thread &thread,
+ const SharedPtr &next_frame,
+ SymbolContext &sym_ctx,
+ uint32_t frame_number,
+ UnwindLLDB &unwind_lldb)
+ : RegisterContext(thread, frame_number), m_thread(thread),
+ m_fast_unwind_plan_sp(), m_full_unwind_plan_sp(),
+ m_fallback_unwind_plan_sp(), m_all_registers_available(false),
+ m_frame_type(-1), m_cfa(LLDB_INVALID_ADDRESS),
+ m_afa(LLDB_INVALID_ADDRESS), m_start_pc(),
+ m_current_pc(), m_current_offset(0), m_current_offset_backed_up_one(0),
+ m_sym_ctx(sym_ctx), m_sym_ctx_valid(false), m_frame_number(frame_number),
+ m_registers(), m_parent_unwind(unwind_lldb) {
+ m_sym_ctx.Clear(false);
+ m_sym_ctx_valid = false;
+
+ if (IsFrameZero()) {
+ InitializeZerothFrame();
+ } else {
+ InitializeNonZerothFrame();
+ }
+
+ // This same code exists over in the GetFullUnwindPlanForFrame() but it may
+ // not have been executed yet
+ if (IsFrameZero() || next_frame->m_frame_type == eTrapHandlerFrame ||
+ next_frame->m_frame_type == eDebuggerFrame) {
+ m_all_registers_available = true;
+ }
+}
+
+bool RegisterContextLLDB::IsUnwindPlanValidForCurrentPC(
+ lldb::UnwindPlanSP unwind_plan_sp, int &valid_pc_offset) {
+ if (!unwind_plan_sp)
+ return false;
+
+ // check if m_current_pc is valid
+ if (unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ // yes - current offset can be used as is
+ valid_pc_offset = m_current_offset;
+ return true;
+ }
+
+ // if m_current_offset <= 0, we've got nothing else to try
+ if (m_current_offset <= 0)
+ return false;
+
+ // check pc - 1 to see if it's valid
+ Address pc_minus_one(m_current_pc);
+ pc_minus_one.SetOffset(m_current_pc.GetOffset() - 1);
+ if (unwind_plan_sp->PlanValidAtAddress(pc_minus_one)) {
+ // *valid_pc_offset = m_current_offset - 1;
+ valid_pc_offset = m_current_pc.GetOffset() - 1;
+ return true;
+ }
+
+ return false;
+}
+
+// Initialize a RegisterContextLLDB which is the first frame of a stack -- the
+// zeroth frame or currently executing frame.
+
+void RegisterContextLLDB::InitializeZerothFrame() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ RegisterContextSP reg_ctx_sp = m_thread.GetRegisterContext();
+
+ if (reg_ctx_sp.get() == nullptr) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("frame does not have a register context");
+ return;
+ }
+
+ addr_t current_pc = reg_ctx_sp->GetPC();
+
+ if (current_pc == LLDB_INVALID_ADDRESS) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("frame does not have a pc");
+ return;
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs
+ // this will strip bit zero in case we read a PC from memory or from the LR.
+ // (which would be a no-op in frame 0 where we get it from the register set,
+ // but still a good idea to make the call here for other ABIs that may
+ // exist.)
+ ABI *abi = process->GetABI().get();
+ if (abi)
+ current_pc = abi->FixCodeAddress(current_pc);
+
+ // Initialize m_current_pc, an Address object, based on current_pc, an
+ // addr_t.
+ m_current_pc.SetLoadAddress(current_pc, &process->GetTarget());
+
+ // If we don't have a Module for some reason, we're not going to find
+ // symbol/function information - just stick in some reasonable defaults and
+ // hope we can unwind past this frame.
+ ModuleSP pc_module_sp(m_current_pc.GetModule());
+ if (!m_current_pc.IsValid() || !pc_module_sp) {
+ UnwindLogMsg("using architectural default unwind method");
+ }
+
+ // We require either a symbol or function in the symbols context to be
+ // successfully filled in or this context is of no use to us.
+ const SymbolContextItem resolve_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ if (pc_module_sp.get() && (pc_module_sp->ResolveSymbolContextForAddress(
+ m_current_pc, resolve_scope, m_sym_ctx) &
+ resolve_scope)) {
+ m_sym_ctx_valid = true;
+ }
+
+ if (m_sym_ctx.symbol) {
+ UnwindLogMsg("with pc value of 0x%" PRIx64 ", symbol name is '%s'",
+ current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ } else if (m_sym_ctx.function) {
+ UnwindLogMsg("with pc value of 0x%" PRIx64 ", function name is '%s'",
+ current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ } else {
+ UnwindLogMsg("with pc value of 0x%" PRIx64
+ ", no symbol/function name is known.",
+ current_pc);
+ }
+
+ AddressRange addr_range;
+ m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range);
+
+ if (IsTrapHandlerSymbol(process, m_sym_ctx)) {
+ m_frame_type = eTrapHandlerFrame;
+ } else {
+ // FIXME: Detect eDebuggerFrame here.
+ m_frame_type = eNormalFrame;
+ }
+
+ // If we were able to find a symbol/function, set addr_range to the bounds of
+ // that symbol/function. else treat the current pc value as the start_pc and
+ // record no offset.
+ if (addr_range.GetBaseAddress().IsValid()) {
+ m_start_pc = addr_range.GetBaseAddress();
+ if (m_current_pc.GetSection() == m_start_pc.GetSection()) {
+ m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset();
+ } else if (m_current_pc.GetModule() == m_start_pc.GetModule()) {
+ // This means that whatever symbol we kicked up isn't really correct ---
+ // we should not cross section boundaries ... We really should NULL out
+ // the function/symbol in this case unless there is a bad assumption here
+ // due to inlined functions?
+ m_current_offset =
+ m_current_pc.GetFileAddress() - m_start_pc.GetFileAddress();
+ }
+ m_current_offset_backed_up_one = m_current_offset;
+ } else {
+ m_start_pc = m_current_pc;
+ m_current_offset = -1;
+ m_current_offset_backed_up_one = -1;
+ }
+
+ // We've set m_frame_type and m_sym_ctx before these calls.
+
+ m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame();
+ m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
+
+ UnwindPlan::RowSP active_row;
+ lldb::RegisterKind row_register_kind = eRegisterKindGeneric;
+ if (m_full_unwind_plan_sp &&
+ m_full_unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ active_row =
+ m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+ row_register_kind = m_full_unwind_plan_sp->GetRegisterKind();
+ if (active_row.get() && log) {
+ StreamString active_row_strm;
+ active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread,
+ m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
+ UnwindLogMsg("%s", active_row_strm.GetData());
+ }
+ }
+
+ if (!active_row.get()) {
+ UnwindLogMsg("could not find an unwindplan row for this frame's pc");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), m_cfa)) {
+ // Try the fall back unwind plan since the
+ // full unwind plan failed.
+ FuncUnwindersSP func_unwinders_sp;
+ UnwindPlanSP call_site_unwind_plan;
+ bool cfa_status = false;
+
+ if (m_sym_ctx_valid) {
+ func_unwinders_sp =
+ pc_module_sp->GetUnwindTable().GetFuncUnwindersContainingAddress(
+ m_current_pc, m_sym_ctx);
+ }
+
+ if (func_unwinders_sp.get() != nullptr)
+ call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(
+ process->GetTarget(), m_thread);
+
+ if (call_site_unwind_plan.get() != nullptr) {
+ m_fallback_unwind_plan_sp = call_site_unwind_plan;
+ if (TryFallbackUnwindPlan())
+ cfa_status = true;
+ }
+ if (!cfa_status) {
+ UnwindLogMsg("could not read CFA value for first frame.");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+ } else
+ ReadFrameAddress(row_register_kind, active_row->GetAFAValue(), m_afa);
+
+ UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64
+ " afa is 0x%" PRIx64 " using %s UnwindPlan",
+ (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
+ (uint64_t)m_cfa,
+ (uint64_t)m_afa,
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+}
+
+// Initialize a RegisterContextLLDB for the non-zeroth frame -- rely on the
+// RegisterContextLLDB "below" it to provide things like its current pc value.
+
+void RegisterContextLLDB::InitializeNonZerothFrame() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (IsFrameZero()) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("non-zeroth frame tests positive for IsFrameZero -- that "
+ "shouldn't happen.");
+ return;
+ }
+
+ if (!GetNextFrame().get() || !GetNextFrame()->IsValid()) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("Could not get next frame, marking this frame as invalid.");
+ return;
+ }
+ if (!m_thread.GetRegisterContext()) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("Could not get register context for this thread, marking this "
+ "frame as invalid.");
+ return;
+ }
+
+ addr_t pc;
+ if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) {
+ UnwindLogMsg("could not get pc value");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs
+ // this will strip bit zero in case we read a PC from memory or from the LR.
+ ABI *abi = process->GetABI().get();
+ if (abi)
+ pc = abi->FixCodeAddress(pc);
+
+ if (log) {
+ UnwindLogMsg("pc = 0x%" PRIx64, pc);
+ addr_t reg_val;
+ if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val))
+ UnwindLogMsg("fp = 0x%" PRIx64, reg_val);
+ if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val))
+ UnwindLogMsg("sp = 0x%" PRIx64, reg_val);
+ }
+
+ // A pc of 0x0 means it's the end of the stack crawl unless we're above a trap
+ // handler function
+ bool above_trap_handler = false;
+ if (GetNextFrame().get() && GetNextFrame()->IsValid() &&
+ GetNextFrame()->IsTrapHandlerFrame())
+ above_trap_handler = true;
+
+ if (pc == 0 || pc == 0x1) {
+ if (!above_trap_handler) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("this frame has a pc of 0x0");
+ return;
+ }
+ }
+
+ const bool allow_section_end = true;
+ m_current_pc.SetLoadAddress(pc, &process->GetTarget(), allow_section_end);
+
+ // If we don't have a Module for some reason, we're not going to find
+ // symbol/function information - just stick in some reasonable defaults and
+ // hope we can unwind past this frame.
+ ModuleSP pc_module_sp(m_current_pc.GetModule());
+ if (!m_current_pc.IsValid() || !pc_module_sp) {
+ UnwindLogMsg("using architectural default unwind method");
+
+ // Test the pc value to see if we know it's in an unmapped/non-executable
+ // region of memory.
+ uint32_t permissions;
+ if (process->GetLoadAddressPermissions(pc, permissions) &&
+ (permissions & ePermissionsExecutable) == 0) {
+ // If this is the second frame off the stack, we may have unwound the
+ // first frame incorrectly. But using the architecture default unwind
+ // plan may get us back on track -- albeit possibly skipping a real
+ // frame. Give this frame a clearly-invalid pc and see if we can get any
+ // further.
+ if (GetNextFrame().get() && GetNextFrame()->IsValid() &&
+ GetNextFrame()->IsFrameZero()) {
+ UnwindLogMsg("had a pc of 0x%" PRIx64 " which is not in executable "
+ "memory but on frame 1 -- "
+ "allowing it once.",
+ (uint64_t)pc);
+ m_frame_type = eSkipFrame;
+ } else {
+ // anywhere other than the second frame, a non-executable pc means
+ // we're off in the weeds -- stop now.
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("pc is in a non-executable section of memory and this "
+ "isn't the 2nd frame in the stack walk.");
+ return;
+ }
+ }
+
+ if (abi) {
+ m_fast_unwind_plan_sp.reset();
+ m_full_unwind_plan_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ abi->CreateDefaultUnwindPlan(*m_full_unwind_plan_sp);
+ if (m_frame_type != eSkipFrame) // don't override eSkipFrame
+ {
+ m_frame_type = eNormalFrame;
+ }
+ m_all_registers_available = false;
+ m_current_offset = -1;
+ m_current_offset_backed_up_one = -1;
+ RegisterKind row_register_kind = m_full_unwind_plan_sp->GetRegisterKind();
+ UnwindPlan::RowSP row = m_full_unwind_plan_sp->GetRowForFunctionOffset(0);
+ if (row.get()) {
+ if (!ReadFrameAddress(row_register_kind, row->GetCFAValue(), m_cfa)) {
+ UnwindLogMsg("failed to get cfa value");
+ if (m_frame_type != eSkipFrame) // don't override eSkipFrame
+ {
+ m_frame_type = eNotAValidFrame;
+ }
+ return;
+ }
+
+ 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) {
+ UnwindLogMsg("could not find a valid cfa address");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ // m_cfa should point into the stack memory; if we can query memory
+ // region permissions, see if the memory is allocated & readable.
+ if (process->GetLoadAddressPermissions(m_cfa, permissions) &&
+ (permissions & ePermissionsReadable) == 0) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg(
+ "the CFA points to a region of memory that is not readable");
+ return;
+ }
+ } else {
+ UnwindLogMsg("could not find a row for function offset zero");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ if (CheckIfLoopingStack()) {
+ TryFallbackUnwindPlan();
+ if (CheckIfLoopingStack()) {
+ UnwindLogMsg("same CFA address as next frame, assuming the unwind is "
+ "looping - stopping");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+ }
+
+ UnwindLogMsg("initialized frame cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
+ (uint64_t)m_cfa, (uint64_t)m_afa);
+ return;
+ }
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("could not find any symbol for this pc, or a default unwind "
+ "plan, to continue unwind.");
+ return;
+ }
+
+ bool resolve_tail_call_address = false; // m_current_pc can be one past the
+ // address range of the function...
+ // If the saved pc does not point to a function/symbol because it is beyond
+ // the bounds of the correct function and there's no symbol there, we do
+ // *not* want ResolveSymbolContextForAddress to back up the pc by 1, because
+ // then we might not find the correct unwind information later. Instead, let
+ // ResolveSymbolContextForAddress fail, and handle the case via
+ // decr_pc_and_recompute_addr_range below.
+ const SymbolContextItem resolve_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ uint32_t resolved_scope = pc_module_sp->ResolveSymbolContextForAddress(
+ m_current_pc, resolve_scope, m_sym_ctx, resolve_tail_call_address);
+
+ // We require either a symbol or function in the symbols context to be
+ // successfully filled in or this context is of no use to us.
+ if (resolve_scope & resolved_scope) {
+ m_sym_ctx_valid = true;
+ }
+
+ if (m_sym_ctx.symbol) {
+ UnwindLogMsg("with pc value of 0x%" PRIx64 ", symbol name is '%s'", pc,
+ GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ } else if (m_sym_ctx.function) {
+ UnwindLogMsg("with pc value of 0x%" PRIx64 ", function name is '%s'", pc,
+ GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ } else {
+ UnwindLogMsg("with pc value of 0x%" PRIx64
+ ", no symbol/function name is known.",
+ pc);
+ }
+
+ AddressRange addr_range;
+ if (!m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range)) {
+ m_sym_ctx_valid = false;
+ }
+
+ bool decr_pc_and_recompute_addr_range = false;
+
+ // If the symbol lookup failed...
+ if (!m_sym_ctx_valid)
+ decr_pc_and_recompute_addr_range = true;
+
+ // Or if we're in the middle of the stack (and not "above" an asynchronous
+ // event like sigtramp), and our "current" pc is the start of a function...
+ if (GetNextFrame()->m_frame_type != eTrapHandlerFrame &&
+ GetNextFrame()->m_frame_type != eDebuggerFrame &&
+ (!m_sym_ctx_valid ||
+ (addr_range.GetBaseAddress().IsValid() &&
+ addr_range.GetBaseAddress().GetSection() == m_current_pc.GetSection() &&
+ addr_range.GetBaseAddress().GetOffset() == m_current_pc.GetOffset()))) {
+ decr_pc_and_recompute_addr_range = true;
+ }
+
+ // We need to back up the pc by 1 byte and re-search for the Symbol to handle
+ // the case where the "saved pc" value is pointing to the next function, e.g.
+ // if a function ends with a CALL instruction.
+ // FIXME this may need to be an architectural-dependent behavior; if so we'll
+ // need to add a member function
+ // to the ABI plugin and consult that.
+ if (decr_pc_and_recompute_addr_range) {
+ UnwindLogMsg("Backing up the pc value of 0x%" PRIx64
+ " by 1 and re-doing symbol lookup; old symbol was %s",
+ pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ Address temporary_pc;
+ temporary_pc.SetLoadAddress(pc - 1, &process->GetTarget());
+ m_sym_ctx.Clear(false);
+ m_sym_ctx_valid = false;
+ SymbolContextItem resolve_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+
+ ModuleSP temporary_module_sp = temporary_pc.GetModule();
+ if (temporary_module_sp &&
+ temporary_module_sp->ResolveSymbolContextForAddress(
+ temporary_pc, resolve_scope, m_sym_ctx) &
+ resolve_scope) {
+ if (m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range))
+ m_sym_ctx_valid = true;
+ }
+ UnwindLogMsg("Symbol is now %s",
+ GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ }
+
+ // If we were able to find a symbol/function, set addr_range_ptr to the
+ // bounds of that symbol/function. else treat the current pc value as the
+ // start_pc and record no offset.
+ if (addr_range.GetBaseAddress().IsValid()) {
+ m_start_pc = addr_range.GetBaseAddress();
+ m_current_offset = pc - m_start_pc.GetLoadAddress(&process->GetTarget());
+ m_current_offset_backed_up_one = m_current_offset;
+ if (decr_pc_and_recompute_addr_range &&
+ m_current_offset_backed_up_one > 0) {
+ m_current_offset_backed_up_one--;
+ if (m_sym_ctx_valid) {
+ m_current_pc.SetLoadAddress(pc - 1, &process->GetTarget());
+ }
+ }
+ } else {
+ m_start_pc = m_current_pc;
+ m_current_offset = -1;
+ m_current_offset_backed_up_one = -1;
+ }
+
+ if (IsTrapHandlerSymbol(process, m_sym_ctx)) {
+ m_frame_type = eTrapHandlerFrame;
+ } else {
+ // FIXME: Detect eDebuggerFrame here.
+ if (m_frame_type != eSkipFrame) // don't override eSkipFrame
+ {
+ m_frame_type = eNormalFrame;
+ }
+ }
+
+ // We've set m_frame_type and m_sym_ctx before this call.
+ m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame();
+
+ UnwindPlan::RowSP active_row;
+ RegisterKind row_register_kind = eRegisterKindGeneric;
+
+ // Try to get by with just the fast UnwindPlan if possible - the full
+ // UnwindPlan may be expensive to get (e.g. if we have to parse the entire
+ // eh_frame section of an ObjectFile for the first time.)
+
+ if (m_fast_unwind_plan_sp &&
+ m_fast_unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ active_row =
+ m_fast_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+ row_register_kind = m_fast_unwind_plan_sp->GetRegisterKind();
+ if (active_row.get() && log) {
+ StreamString active_row_strm;
+ active_row->Dump(active_row_strm, m_fast_unwind_plan_sp.get(), &m_thread,
+ m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
+ UnwindLogMsg("active row: %s", active_row_strm.GetData());
+ }
+ } else {
+ m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
+ int valid_offset = -1;
+ if (IsUnwindPlanValidForCurrentPC(m_full_unwind_plan_sp, valid_offset)) {
+ active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(valid_offset);
+ row_register_kind = m_full_unwind_plan_sp->GetRegisterKind();
+ if (active_row.get() && log) {
+ StreamString active_row_strm;
+ active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(),
+ &m_thread,
+ m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
+ UnwindLogMsg("active row: %s", active_row_strm.GetData());
+ }
+ }
+ }
+
+ if (!active_row.get()) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("could not find unwind row for this pc");
+ return;
+ }
+
+ if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), m_cfa)) {
+ UnwindLogMsg("failed to get cfa");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ ReadFrameAddress(row_register_kind, active_row->GetAFAValue(), m_afa);
+
+ UnwindLogMsg("m_cfa = 0x%" PRIx64 " m_afa = 0x%" PRIx64, m_cfa, m_afa);
+
+ if (CheckIfLoopingStack()) {
+ TryFallbackUnwindPlan();
+ if (CheckIfLoopingStack()) {
+ UnwindLogMsg("same CFA address as next frame, assuming the unwind is "
+ "looping - stopping");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+ }
+
+ UnwindLogMsg("initialized frame current pc is 0x%" PRIx64
+ " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
+ (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
+ (uint64_t)m_cfa,
+ (uint64_t)m_afa);
+}
+
+bool RegisterContextLLDB::CheckIfLoopingStack() {
+ // If we have a bad stack setup, we can get the same CFA value multiple times
+ // -- or even more devious, we can actually oscillate between two CFA values.
+ // Detect that here and break out to avoid a possible infinite loop in lldb
+ // trying to unwind the stack. To detect when we have the same CFA value
+ // multiple times, we compare the
+ // CFA of the current
+ // frame with the 2nd next frame because in some specail case (e.g. signal
+ // hanlders, hand written assembly without ABI compiance) we can have 2
+ // frames with the same
+ // CFA (in theory we
+ // can have arbitrary number of frames with the same CFA, but more then 2 is
+ // very very unlikely)
+
+ RegisterContextLLDB::SharedPtr next_frame = GetNextFrame();
+ if (next_frame) {
+ RegisterContextLLDB::SharedPtr next_next_frame = next_frame->GetNextFrame();
+ addr_t next_next_frame_cfa = LLDB_INVALID_ADDRESS;
+ if (next_next_frame && next_next_frame->GetCFA(next_next_frame_cfa)) {
+ if (next_next_frame_cfa == m_cfa) {
+ // We have a loop in the stack unwind
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool RegisterContextLLDB::IsFrameZero() const { return m_frame_number == 0; }
+
+// Find a fast unwind plan for this frame, if possible.
+//
+// On entry to this method,
+//
+// 1. m_frame_type should already be set to eTrapHandlerFrame/eDebuggerFrame
+// if either of those are correct,
+// 2. m_sym_ctx should already be filled in, and
+// 3. m_current_pc should have the current pc value for this frame
+// 4. m_current_offset_backed_up_one should have the current byte offset into
+// the function, maybe backed up by 1, -1 if unknown
+
+UnwindPlanSP RegisterContextLLDB::GetFastUnwindPlanForFrame() {
+ UnwindPlanSP unwind_plan_sp;
+ ModuleSP pc_module_sp(m_current_pc.GetModule());
+
+ if (!m_current_pc.IsValid() || !pc_module_sp ||
+ pc_module_sp->GetObjectFile() == nullptr)
+ return unwind_plan_sp;
+
+ if (IsFrameZero())
+ return unwind_plan_sp;
+
+ FuncUnwindersSP func_unwinders_sp(
+ pc_module_sp->GetUnwindTable().GetFuncUnwindersContainingAddress(
+ m_current_pc, m_sym_ctx));
+ if (!func_unwinders_sp)
+ return unwind_plan_sp;
+
+ // If we're in _sigtramp(), unwinding past this frame requires special
+ // knowledge.
+ if (m_frame_type == eTrapHandlerFrame || m_frame_type == eDebuggerFrame)
+ return unwind_plan_sp;
+
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanFastUnwind(
+ *m_thread.CalculateTarget(), m_thread);
+ if (unwind_plan_sp) {
+ if (unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log && log->GetVerbose()) {
+ if (m_fast_unwind_plan_sp)
+ UnwindLogMsgVerbose("frame, and has a fast UnwindPlan");
+ else
+ UnwindLogMsgVerbose("frame");
+ }
+ m_frame_type = eNormalFrame;
+ return unwind_plan_sp;
+ } else {
+ unwind_plan_sp.reset();
+ }
+ }
+ return unwind_plan_sp;
+}
+
+// On entry to this method,
+//
+// 1. m_frame_type should already be set to eTrapHandlerFrame/eDebuggerFrame
+// if either of those are correct,
+// 2. m_sym_ctx should already be filled in, and
+// 3. m_current_pc should have the current pc value for this frame
+// 4. m_current_offset_backed_up_one should have the current byte offset into
+// the function, maybe backed up by 1, -1 if unknown
+
+UnwindPlanSP RegisterContextLLDB::GetFullUnwindPlanForFrame() {
+ UnwindPlanSP unwind_plan_sp;
+ UnwindPlanSP arch_default_unwind_plan_sp;
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ ABI *abi = process ? process->GetABI().get() : nullptr;
+ if (abi) {
+ arch_default_unwind_plan_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp);
+ } else {
+ UnwindLogMsg(
+ "unable to get architectural default UnwindPlan from ABI plugin");
+ }
+
+ bool behaves_like_zeroth_frame = false;
+ if (IsFrameZero() || GetNextFrame()->m_frame_type == eTrapHandlerFrame ||
+ GetNextFrame()->m_frame_type == eDebuggerFrame) {
+ behaves_like_zeroth_frame = true;
+ // If this frame behaves like a 0th frame (currently executing or
+ // interrupted asynchronously), all registers can be retrieved.
+ m_all_registers_available = true;
+ }
+
+ // If we've done a jmp 0x0 / bl 0x0 (called through a null function pointer)
+ // so the pc is 0x0 in the zeroth frame, we need to use the "unwind at first
+ // instruction" arch default UnwindPlan Also, if this Process can report on
+ // memory region attributes, any non-executable region means we jumped
+ // through a bad function pointer - handle the same way as 0x0. Note, if we
+ // have a symbol context & a symbol, we don't want to follow this code path.
+ // This is for jumping to memory regions without any information available.
+
+ if ((!m_sym_ctx_valid ||
+ (m_sym_ctx.function == nullptr && m_sym_ctx.symbol == nullptr)) &&
+ behaves_like_zeroth_frame && m_current_pc.IsValid()) {
+ uint32_t permissions;
+ addr_t current_pc_addr =
+ m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr());
+ if (current_pc_addr == 0 ||
+ (process &&
+ process->GetLoadAddressPermissions(current_pc_addr, permissions) &&
+ (permissions & ePermissionsExecutable) == 0)) {
+ if (abi) {
+ unwind_plan_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ abi->CreateFunctionEntryUnwindPlan(*unwind_plan_sp);
+ m_frame_type = eNormalFrame;
+ return unwind_plan_sp;
+ }
+ }
+ }
+
+ // No Module for the current pc, try using the architecture default unwind.
+ ModuleSP pc_module_sp(m_current_pc.GetModule());
+ if (!m_current_pc.IsValid() || !pc_module_sp ||
+ pc_module_sp->GetObjectFile() == nullptr) {
+ m_frame_type = eNormalFrame;
+ return arch_default_unwind_plan_sp;
+ }
+
+ FuncUnwindersSP func_unwinders_sp;
+ if (m_sym_ctx_valid) {
+ func_unwinders_sp =
+ pc_module_sp->GetUnwindTable().GetFuncUnwindersContainingAddress(
+ m_current_pc, m_sym_ctx);
+ }
+
+ // No FuncUnwinders available for this pc (stripped function symbols, lldb
+ // could not augment its function table with another source, like
+ // LC_FUNCTION_STARTS or eh_frame in ObjectFileMachO). See if eh_frame or the
+ // .ARM.exidx tables have unwind information for this address, else fall back
+ // to the architectural default unwind.
+ if (!func_unwinders_sp) {
+ m_frame_type = eNormalFrame;
+
+ if (!pc_module_sp || !pc_module_sp->GetObjectFile() ||
+ !m_current_pc.IsValid())
+ return arch_default_unwind_plan_sp;
+
+ // Even with -fomit-frame-pointer, we can try eh_frame to get back on
+ // track.
+ DWARFCallFrameInfo *eh_frame =
+ pc_module_sp->GetUnwindTable().GetEHFrameInfo();
+ if (eh_frame) {
+ unwind_plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (eh_frame->GetUnwindPlan(m_current_pc, *unwind_plan_sp))
+ return unwind_plan_sp;
+ else
+ unwind_plan_sp.reset();
+ }
+
+ ArmUnwindInfo *arm_exidx =
+ pc_module_sp->GetUnwindTable().GetArmUnwindInfo();
+ if (arm_exidx) {
+ unwind_plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (arm_exidx->GetUnwindPlan(exe_ctx.GetTargetRef(), m_current_pc,
+ *unwind_plan_sp))
+ return unwind_plan_sp;
+ else
+ unwind_plan_sp.reset();
+ }
+
+ return arch_default_unwind_plan_sp;
+ }
+
+ // If we're in _sigtramp(), unwinding past this frame requires special
+ // knowledge. On Mac OS X this knowledge is properly encoded in the eh_frame
+ // section, so prefer that if available. On other platforms we may need to
+ // provide a platform-specific UnwindPlan which encodes the details of how to
+ // unwind out of sigtramp.
+ if (m_frame_type == eTrapHandlerFrame && process) {
+ m_fast_unwind_plan_sp.reset();
+ unwind_plan_sp =
+ func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget());
+ if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc) &&
+ unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) {
+ return unwind_plan_sp;
+ }
+ }
+
+ // Ask the DynamicLoader if the eh_frame CFI should be trusted in this frame
+ // even when it's frame zero This comes up if we have hand-written functions
+ // in a Module and hand-written eh_frame. The assembly instruction
+ // inspection may fail and the eh_frame CFI were probably written with some
+ // care to do the right thing. It'd be nice if there was a way to ask the
+ // eh_frame directly if it is asynchronous (can be trusted at every
+ // instruction point) or synchronous (the normal case - only at call sites).
+ // But there is not.
+ if (process && process->GetDynamicLoader() &&
+ process->GetDynamicLoader()->AlwaysRelyOnEHUnwindInfo(m_sym_ctx)) {
+ // We must specifically call the GetEHFrameUnwindPlan() method here --
+ // normally we would call GetUnwindPlanAtCallSite() -- because CallSite may
+ // return an unwind plan sourced from either eh_frame (that's what we
+ // intend) or compact unwind (this won't work)
+ unwind_plan_sp =
+ func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget());
+ if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because the "
+ "DynamicLoader suggested we prefer it",
+ unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+ }
+
+ // Typically the NonCallSite UnwindPlan is the unwind created by inspecting
+ // the assembly language instructions
+ if (behaves_like_zeroth_frame && process) {
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite(
+ process->GetTarget(), m_thread);
+ if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ if (unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) {
+ // We probably have an UnwindPlan created by inspecting assembly
+ // instructions. The assembly profilers work really well with compiler-
+ // generated functions but hand- written assembly can be problematic.
+ // We set the eh_frame based unwind plan as our fallback unwind plan if
+ // instruction emulation doesn't work out even for non call sites if it
+ // is available and use the architecture default unwind plan if it is
+ // not available. The eh_frame unwind plan is more reliable even on non
+ // call sites then the architecture default plan and for hand written
+ // assembly code it is often written in a way that it valid at all
+ // location what helps in the most common cases when the instruction
+ // emulation fails.
+ UnwindPlanSP call_site_unwind_plan =
+ func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(),
+ m_thread);
+ if (call_site_unwind_plan &&
+ call_site_unwind_plan.get() != unwind_plan_sp.get() &&
+ call_site_unwind_plan->GetSourceName() !=
+ unwind_plan_sp->GetSourceName()) {
+ m_fallback_unwind_plan_sp = call_site_unwind_plan;
+ } else {
+ m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp;
+ }
+ }
+ UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because this "
+ "is the non-call site unwind plan and this is a "
+ "zeroth frame",
+ unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+
+ // If we're on the first instruction of a function, and we have an
+ // architectural default UnwindPlan for the initial instruction of a
+ // function, use that.
+ if (m_current_offset == 0) {
+ unwind_plan_sp =
+ func_unwinders_sp->GetUnwindPlanArchitectureDefaultAtFunctionEntry(
+ m_thread);
+ if (unwind_plan_sp) {
+ UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because we are at "
+ "the first instruction of a function",
+ unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+ }
+ }
+
+ // Typically this is unwind info from an eh_frame section intended for
+ // exception handling; only valid at call sites
+ if (process) {
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite(
+ process->GetTarget(), m_thread);
+ }
+ int valid_offset = -1;
+ if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset)) {
+ UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because this "
+ "is the call-site unwind plan",
+ unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+
+ // We'd prefer to use an UnwindPlan intended for call sites when we're at a
+ // call site but if we've struck out on that, fall back to using the non-
+ // call-site assembly inspection UnwindPlan if possible.
+ if (process) {
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite(
+ process->GetTarget(), m_thread);
+ }
+ if (unwind_plan_sp &&
+ unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) {
+ // We probably have an UnwindPlan created by inspecting assembly
+ // instructions. The assembly profilers work really well with compiler-
+ // generated functions but hand- written assembly can be problematic. We
+ // set the eh_frame based unwind plan as our fallback unwind plan if
+ // instruction emulation doesn't work out even for non call sites if it is
+ // available and use the architecture default unwind plan if it is not
+ // available. The eh_frame unwind plan is more reliable even on non call
+ // sites then the architecture default plan and for hand written assembly
+ // code it is often written in a way that it valid at all location what
+ // helps in the most common cases when the instruction emulation fails.
+ UnwindPlanSP call_site_unwind_plan =
+ func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(),
+ m_thread);
+ if (call_site_unwind_plan &&
+ call_site_unwind_plan.get() != unwind_plan_sp.get() &&
+ call_site_unwind_plan->GetSourceName() !=
+ unwind_plan_sp->GetSourceName()) {
+ m_fallback_unwind_plan_sp = call_site_unwind_plan;
+ } else {
+ m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp;
+ }
+ }
+
+ if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset)) {
+ UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because we "
+ "failed to find a call-site unwind plan that would work",
+ unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+
+ // If nothing else, use the architectural default UnwindPlan and hope that
+ // does the job.
+ if (arch_default_unwind_plan_sp)
+ UnwindLogMsgVerbose(
+ "frame uses %s for full UnwindPlan because we are falling back "
+ "to the arch default plan",
+ arch_default_unwind_plan_sp->GetSourceName().GetCString());
+ else
+ UnwindLogMsg(
+ "Unable to find any UnwindPlan for full unwind of this frame.");
+
+ return arch_default_unwind_plan_sp;
+}
+
+void RegisterContextLLDB::InvalidateAllRegisters() {
+ m_frame_type = eNotAValidFrame;
+}
+
+size_t RegisterContextLLDB::GetRegisterCount() {
+ return m_thread.GetRegisterContext()->GetRegisterCount();
+}
+
+const RegisterInfo *RegisterContextLLDB::GetRegisterInfoAtIndex(size_t reg) {
+ return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg);
+}
+
+size_t RegisterContextLLDB::GetRegisterSetCount() {
+ return m_thread.GetRegisterContext()->GetRegisterSetCount();
+}
+
+const RegisterSet *RegisterContextLLDB::GetRegisterSet(size_t reg_set) {
+ return m_thread.GetRegisterContext()->GetRegisterSet(reg_set);
+}
+
+uint32_t RegisterContextLLDB::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber(
+ kind, num);
+}
+
+bool RegisterContextLLDB::ReadRegisterValueFromRegisterLocation(
+ lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!IsValid())
+ return false;
+ bool success = false;
+
+ switch (regloc.type) {
+ case UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext: {
+ const RegisterInfo *other_reg_info =
+ GetRegisterInfoAtIndex(regloc.location.register_number);
+
+ if (!other_reg_info)
+ return false;
+
+ success =
+ m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value);
+ } break;
+ case UnwindLLDB::RegisterLocation::eRegisterInRegister: {
+ const RegisterInfo *other_reg_info =
+ GetRegisterInfoAtIndex(regloc.location.register_number);
+
+ if (!other_reg_info)
+ return false;
+
+ if (IsFrameZero()) {
+ success =
+ m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value);
+ } else {
+ success = GetNextFrame()->ReadRegister(other_reg_info, value);
+ }
+ } break;
+ case UnwindLLDB::RegisterLocation::eRegisterValueInferred:
+ success =
+ value.SetUInt(regloc.location.inferred_value, reg_info->byte_size);
+ break;
+
+ case UnwindLLDB::RegisterLocation::eRegisterNotSaved:
+ break;
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation:
+ llvm_unreachable("FIXME debugger inferior function call unwind");
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: {
+ Status error(ReadRegisterValueFromMemory(
+ reg_info, regloc.location.target_memory_location, reg_info->byte_size,
+ value));
+ success = error.Success();
+ } break;
+ default:
+ llvm_unreachable("Unknown RegisterLocation type.");
+ }
+ return success;
+}
+
+bool RegisterContextLLDB::WriteRegisterValueToRegisterLocation(
+ lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ if (!IsValid())
+ return false;
+
+ bool success = false;
+
+ switch (regloc.type) {
+ case UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext: {
+ const RegisterInfo *other_reg_info =
+ GetRegisterInfoAtIndex(regloc.location.register_number);
+ success =
+ m_thread.GetRegisterContext()->WriteRegister(other_reg_info, value);
+ } break;
+ case UnwindLLDB::RegisterLocation::eRegisterInRegister: {
+ const RegisterInfo *other_reg_info =
+ GetRegisterInfoAtIndex(regloc.location.register_number);
+ if (IsFrameZero()) {
+ success =
+ m_thread.GetRegisterContext()->WriteRegister(other_reg_info, value);
+ } else {
+ success = GetNextFrame()->WriteRegister(other_reg_info, value);
+ }
+ } break;
+ case UnwindLLDB::RegisterLocation::eRegisterValueInferred:
+ case UnwindLLDB::RegisterLocation::eRegisterNotSaved:
+ break;
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation:
+ llvm_unreachable("FIXME debugger inferior function call unwind");
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: {
+ Status error(WriteRegisterValueToMemory(
+ reg_info, regloc.location.target_memory_location, reg_info->byte_size,
+ value));
+ success = error.Success();
+ } break;
+ default:
+ llvm_unreachable("Unknown RegisterLocation type.");
+ }
+ return success;
+}
+
+bool RegisterContextLLDB::IsValid() const {
+ return m_frame_type != eNotAValidFrame;
+}
+
+// After the final stack frame in a stack walk we'll get one invalid
+// (eNotAValidFrame) stack frame -- one past the end of the stack walk. But
+// higher-level code will need to tell the differnece between "the unwind plan
+// below this frame failed" versus "we successfully completed the stack walk"
+// so this method helps to disambiguate that.
+
+bool RegisterContextLLDB::IsTrapHandlerFrame() const {
+ return m_frame_type == eTrapHandlerFrame;
+}
+
+// A skip frame is a bogus frame on the stack -- but one where we're likely to
+// find a real frame farther
+// up the stack if we keep looking. It's always the second frame in an unwind
+// (i.e. the first frame after frame zero) where unwinding can be the
+// trickiest. Ideally we'll mark up this frame in some way so the user knows
+// we're displaying bad data and we may have skipped one frame of their real
+// program in the process of getting back on track.
+
+bool RegisterContextLLDB::IsSkipFrame() const {
+ return m_frame_type == eSkipFrame;
+}
+
+bool RegisterContextLLDB::IsTrapHandlerSymbol(
+ lldb_private::Process *process,
+ const lldb_private::SymbolContext &m_sym_ctx) const {
+ PlatformSP platform_sp(process->GetTarget().GetPlatform());
+ if (platform_sp) {
+ const std::vector<ConstString> trap_handler_names(
+ platform_sp->GetTrapHandlerSymbolNames());
+ for (ConstString name : trap_handler_names) {
+ if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) ||
+ (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) {
+ return true;
+ }
+ }
+ }
+ const std::vector<ConstString> user_specified_trap_handler_names(
+ m_parent_unwind.GetUserSpecifiedTrapHandlerFunctionNames());
+ for (ConstString name : user_specified_trap_handler_names) {
+ if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) ||
+ (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Answer the question: Where did THIS frame save the CALLER frame ("previous"
+// frame)'s register value?
+
+enum UnwindLLDB::RegisterSearchResult
+RegisterContextLLDB::SavedLocationForRegister(
+ uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc) {
+ RegisterNumber regnum(m_thread, eRegisterKindLLDB, lldb_regnum);
+
+ // Have we already found this register location?
+ if (!m_registers.empty()) {
+ std::map<uint32_t,
+ lldb_private::UnwindLLDB::RegisterLocation>::const_iterator
+ iterator;
+ iterator = m_registers.find(regnum.GetAsKind(eRegisterKindLLDB));
+ if (iterator != m_registers.end()) {
+ regloc = iterator->second;
+ UnwindLogMsg("supplying caller's saved %s (%d)'s location, cached",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ }
+
+ // Look through the available UnwindPlans for the register location.
+
+ UnwindPlan::Row::RegisterLocation unwindplan_regloc;
+ bool have_unwindplan_regloc = false;
+ RegisterKind unwindplan_registerkind = kNumRegisterKinds;
+
+ if (m_fast_unwind_plan_sp) {
+ UnwindPlan::RowSP active_row =
+ m_fast_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+ unwindplan_registerkind = m_fast_unwind_plan_sp->GetRegisterKind();
+ if (regnum.GetAsKind(unwindplan_registerkind) == LLDB_INVALID_REGNUM) {
+ UnwindLogMsg("could not convert lldb regnum %s (%d) into %d RegisterKind "
+ "reg numbering scheme",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ (int)unwindplan_registerkind);
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+ if (active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind),
+ unwindplan_regloc)) {
+ UnwindLogMsg(
+ "supplying caller's saved %s (%d)'s location using FastUnwindPlan",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ have_unwindplan_regloc = true;
+ }
+ }
+
+ if (!have_unwindplan_regloc) {
+ // m_full_unwind_plan_sp being NULL means that we haven't tried to find a
+ // full UnwindPlan yet
+ if (!m_full_unwind_plan_sp)
+ m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
+
+ if (m_full_unwind_plan_sp) {
+ RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+
+ UnwindPlan::RowSP active_row =
+ m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+ unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind();
+
+ RegisterNumber return_address_reg;
+
+ // If we're fetching the saved pc and this UnwindPlan defines a
+ // ReturnAddress register (e.g. lr on arm), look for the return address
+ // register number in the UnwindPlan's row.
+ if (pc_regnum.IsValid() && pc_regnum == regnum &&
+ m_full_unwind_plan_sp->GetReturnAddressRegister() !=
+ LLDB_INVALID_REGNUM) {
+
+ return_address_reg.init(
+ m_thread, m_full_unwind_plan_sp->GetRegisterKind(),
+ m_full_unwind_plan_sp->GetReturnAddressRegister());
+ regnum = return_address_reg;
+ UnwindLogMsg("requested caller's saved PC but this UnwindPlan uses a "
+ "RA reg; getting %s (%d) instead",
+ return_address_reg.GetName(),
+ return_address_reg.GetAsKind(eRegisterKindLLDB));
+ } else {
+ if (regnum.GetAsKind(unwindplan_registerkind) == LLDB_INVALID_REGNUM) {
+ if (unwindplan_registerkind == eRegisterKindGeneric) {
+ UnwindLogMsg("could not convert lldb regnum %s (%d) into "
+ "eRegisterKindGeneric reg numbering scheme",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ } else {
+ UnwindLogMsg("could not convert lldb regnum %s (%d) into %d "
+ "RegisterKind reg numbering scheme",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ (int)unwindplan_registerkind);
+ }
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+ }
+
+ if (regnum.IsValid() &&
+ active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind),
+ unwindplan_regloc)) {
+ have_unwindplan_regloc = true;
+ UnwindLogMsg(
+ "supplying caller's saved %s (%d)'s location using %s UnwindPlan",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+ }
+
+ // This is frame 0 and we're retrieving the PC and it's saved in a Return
+ // Address register and it hasn't been saved anywhere yet -- that is,
+ // it's still live in the actual register. Handle this specially.
+
+ if (!have_unwindplan_regloc && return_address_reg.IsValid() &&
+ IsFrameZero()) {
+ if (return_address_reg.GetAsKind(eRegisterKindLLDB) !=
+ LLDB_INVALID_REGNUM) {
+ lldb_private::UnwindLLDB::RegisterLocation new_regloc;
+ new_regloc.type =
+ UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext;
+ new_regloc.location.register_number =
+ return_address_reg.GetAsKind(eRegisterKindLLDB);
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc;
+ regloc = new_regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) from the live "
+ "RegisterContext at frame 0, saved in %d",
+ return_address_reg.GetName(),
+ return_address_reg.GetAsKind(eRegisterKindLLDB),
+ return_address_reg.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ }
+
+ // If this architecture stores the return address in a register (it
+ // defines a Return Address register) and we're on a non-zero stack frame
+ // and the Full UnwindPlan says that the pc is stored in the
+ // RA registers (e.g. lr on arm), then we know that the full unwindplan is
+ // not trustworthy -- this
+ // is an impossible situation and the instruction emulation code has
+ // likely been misled. If this stack frame meets those criteria, we need
+ // to throw away the Full UnwindPlan that the instruction emulation came
+ // up with and fall back to the architecture's Default UnwindPlan so the
+ // stack walk can get past this point.
+
+ // Special note: If the Full UnwindPlan was generated from the compiler,
+ // don't second-guess it when we're at a call site location.
+
+ // arch_default_ra_regnum is the return address register # in the Full
+ // UnwindPlan register numbering
+ RegisterNumber arch_default_ra_regnum(m_thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA);
+
+ if (arch_default_ra_regnum.GetAsKind(unwindplan_registerkind) !=
+ LLDB_INVALID_REGNUM &&
+ pc_regnum == regnum && unwindplan_regloc.IsInOtherRegister() &&
+ unwindplan_regloc.GetRegisterNumber() ==
+ arch_default_ra_regnum.GetAsKind(unwindplan_registerkind) &&
+ m_full_unwind_plan_sp->GetSourcedFromCompiler() != eLazyBoolYes &&
+ !m_all_registers_available) {
+ UnwindLogMsg("%s UnwindPlan tried to restore the pc from the link "
+ "register but this is a non-zero frame",
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+
+ // Throw away the full unwindplan; install the arch default unwindplan
+ if (ForceSwitchToFallbackUnwindPlan()) {
+ // Update for the possibly new unwind plan
+ unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind();
+ UnwindPlan::RowSP active_row =
+ m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+
+ // Sanity check: Verify that we can fetch a pc value and CFA value
+ // with this unwind plan
+
+ RegisterNumber arch_default_pc_reg(m_thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+ bool can_fetch_pc_value = false;
+ bool can_fetch_cfa = false;
+ addr_t cfa_value;
+ if (active_row) {
+ if (arch_default_pc_reg.GetAsKind(unwindplan_registerkind) !=
+ LLDB_INVALID_REGNUM &&
+ active_row->GetRegisterInfo(
+ arch_default_pc_reg.GetAsKind(unwindplan_registerkind),
+ unwindplan_regloc)) {
+ can_fetch_pc_value = true;
+ }
+ if (ReadFrameAddress(unwindplan_registerkind,
+ active_row->GetCFAValue(), cfa_value)) {
+ can_fetch_cfa = true;
+ }
+ }
+
+ have_unwindplan_regloc = can_fetch_pc_value && can_fetch_cfa;
+ } else {
+ // We were unable to fall back to another unwind plan
+ have_unwindplan_regloc = false;
+ }
+ }
+ }
+ }
+
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (!have_unwindplan_regloc) {
+ // If the UnwindPlan failed to give us an unwind location for this
+ // register, we may be able to fall back to some ABI-defined default. For
+ // example, some ABIs allow to determine the caller's SP via the CFA. Also,
+ // the ABI may set volatile registers to the undefined state.
+ ABI *abi = process ? process->GetABI().get() : nullptr;
+ if (abi) {
+ const RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(regnum.GetAsKind(eRegisterKindLLDB));
+ if (reg_info &&
+ abi->GetFallbackRegisterLocation(reg_info, unwindplan_regloc)) {
+ UnwindLogMsg(
+ "supplying caller's saved %s (%d)'s location using ABI default",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ have_unwindplan_regloc = true;
+ }
+ }
+ }
+
+ if (!have_unwindplan_regloc) {
+ if (IsFrameZero()) {
+ // This is frame 0 - we should return the actual live register context
+ // value
+ lldb_private::UnwindLLDB::RegisterLocation new_regloc;
+ new_regloc.type =
+ UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext;
+ new_regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB);
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc;
+ regloc = new_regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) from the live "
+ "RegisterContext at frame 0",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ } else {
+ std::string unwindplan_name("");
+ if (m_full_unwind_plan_sp) {
+ unwindplan_name += "via '";
+ unwindplan_name += m_full_unwind_plan_sp->GetSourceName().AsCString();
+ unwindplan_name += "'";
+ }
+ UnwindLogMsg("no save location for %s (%d) %s", regnum.GetName(),
+ regnum.GetAsKind(eRegisterKindLLDB),
+ unwindplan_name.c_str());
+ }
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+
+ // unwindplan_regloc has valid contents about where to retrieve the register
+ if (unwindplan_regloc.IsUnspecified()) {
+ lldb_private::UnwindLLDB::RegisterLocation new_regloc;
+ new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterNotSaved;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc;
+ UnwindLogMsg("save location for %s (%d) is unspecified, continue searching",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+
+ if (unwindplan_regloc.IsUndefined()) {
+ UnwindLogMsg(
+ "did not supply reg location for %s (%d) because it is volatile",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile;
+ }
+
+ if (unwindplan_regloc.IsSame()) {
+ if (!IsFrameZero() &&
+ (regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_PC ||
+ regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_RA)) {
+ UnwindLogMsg("register %s (%d) is marked as 'IsSame' - it is a pc or "
+ "return address reg on a non-zero frame -- treat as if we "
+ "have no information",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ } else {
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister;
+ regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB);
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg(
+ "supplying caller's register %s (%d), saved in register %s (%d)",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ }
+
+ if (unwindplan_regloc.IsCFAPlusOffset()) {
+ int offset = unwindplan_regloc.GetOffset();
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
+ regloc.location.inferred_value = m_cfa + offset;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d), value is CFA plus "
+ "offset %d [value is 0x%" PRIx64 "]",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
+ regloc.location.inferred_value);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsAtCFAPlusOffset()) {
+ int offset = unwindplan_regloc.GetOffset();
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
+ regloc.location.target_memory_location = m_cfa + offset;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) from the stack, saved at "
+ "CFA plus offset %d [saved at 0x%" PRIx64 "]",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
+ regloc.location.target_memory_location);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsAFAPlusOffset()) {
+ if (m_afa == LLDB_INVALID_ADDRESS)
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+
+ int offset = unwindplan_regloc.GetOffset();
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
+ regloc.location.inferred_value = m_afa + offset;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d), value is AFA plus "
+ "offset %d [value is 0x%" PRIx64 "]",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
+ regloc.location.inferred_value);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsAtAFAPlusOffset()) {
+ if (m_afa == LLDB_INVALID_ADDRESS)
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+
+ int offset = unwindplan_regloc.GetOffset();
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
+ regloc.location.target_memory_location = m_afa + offset;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) from the stack, saved at "
+ "AFA plus offset %d [saved at 0x%" PRIx64 "]",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
+ regloc.location.target_memory_location);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsInOtherRegister()) {
+ uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber();
+ RegisterNumber row_regnum(m_thread, unwindplan_registerkind,
+ unwindplan_regnum);
+ if (row_regnum.GetAsKind(eRegisterKindLLDB) == LLDB_INVALID_REGNUM) {
+ UnwindLogMsg("could not supply caller's %s (%d) location - was saved in "
+ "another reg but couldn't convert that regnum",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister;
+ regloc.location.register_number = row_regnum.GetAsKind(eRegisterKindLLDB);
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg(
+ "supplying caller's register %s (%d), saved in register %s (%d)",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ row_regnum.GetName(), row_regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsDWARFExpression() ||
+ unwindplan_regloc.IsAtDWARFExpression()) {
+ DataExtractor dwarfdata(unwindplan_regloc.GetDWARFExpressionBytes(),
+ unwindplan_regloc.GetDWARFExpressionLength(),
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+ ModuleSP opcode_ctx;
+ DWARFExpression dwarfexpr(opcode_ctx, dwarfdata, nullptr, 0,
+ unwindplan_regloc.GetDWARFExpressionLength());
+ dwarfexpr.SetRegisterKind(unwindplan_registerkind);
+ Value cfa_val = Scalar(m_cfa);
+ cfa_val.SetValueType(Value::eValueTypeLoadAddress);
+ Value result;
+ Status error;
+ if (dwarfexpr.Evaluate(&exe_ctx, this, 0, &cfa_val, nullptr, result,
+ &error)) {
+ addr_t val;
+ val = result.GetScalar().ULongLong();
+ if (unwindplan_regloc.IsDWARFExpression()) {
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
+ regloc.location.inferred_value = val;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) via DWARF expression "
+ "(IsDWARFExpression)",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ } else {
+ regloc.type =
+ UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
+ regloc.location.target_memory_location = val;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) via DWARF expression "
+ "(IsAtDWARFExpression)",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ }
+ UnwindLogMsg("tried to use IsDWARFExpression or IsAtDWARFExpression for %s "
+ "(%d) but failed",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+
+ UnwindLogMsg("no save location for %s (%d) in this stack frame",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+
+ // FIXME UnwindPlan::Row types atDWARFExpression and isDWARFExpression are
+ // unsupported.
+
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+}
+
+// TryFallbackUnwindPlan() -- this method is a little tricky.
+//
+// When this is called, the frame above -- the caller frame, the "previous"
+// frame -- is invalid or bad.
+//
+// Instead of stopping the stack walk here, we'll try a different UnwindPlan
+// and see if we can get a valid frame above us.
+//
+// This most often happens when an unwind plan based on assembly instruction
+// inspection is not correct -- mostly with hand-written assembly functions or
+// functions where the stack frame is set up "out of band", e.g. the kernel
+// saved the register context and then called an asynchronous trap handler like
+// _sigtramp.
+//
+// Often in these cases, if we just do a dumb stack walk we'll get past this
+// tricky frame and our usual techniques can continue to be used.
+
+bool RegisterContextLLDB::TryFallbackUnwindPlan() {
+ if (m_fallback_unwind_plan_sp.get() == nullptr)
+ return false;
+
+ if (m_full_unwind_plan_sp.get() == nullptr)
+ return false;
+
+ if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() ||
+ m_full_unwind_plan_sp->GetSourceName() ==
+ m_fallback_unwind_plan_sp->GetSourceName()) {
+ return false;
+ }
+
+ // If a compiler generated unwind plan failed, trying the arch default
+ // unwindplan isn't going to do any better.
+ if (m_full_unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes)
+ return false;
+
+ // Get the caller's pc value and our own CFA value. Swap in the fallback
+ // unwind plan, re-fetch the caller's pc value and CFA value. If they're the
+ // same, then the fallback unwind plan provides no benefit.
+
+ RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+
+ addr_t old_caller_pc_value = LLDB_INVALID_ADDRESS;
+ addr_t new_caller_pc_value = LLDB_INVALID_ADDRESS;
+ UnwindLLDB::RegisterLocation regloc;
+ if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB),
+ regloc) ==
+ UnwindLLDB::RegisterSearchResult::eRegisterFound) {
+ const RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB));
+ if (reg_info) {
+ RegisterValue reg_value;
+ if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) {
+ old_caller_pc_value = reg_value.GetAsUInt64();
+ }
+ }
+ }
+
+ // This is a tricky wrinkle! If SavedLocationForRegister() detects a really
+ // impossible register location for the full unwind plan, it may call
+ // ForceSwitchToFallbackUnwindPlan() which in turn replaces the full
+ // unwindplan with the fallback... in short, we're done, we're using the
+ // fallback UnwindPlan. We checked if m_fallback_unwind_plan_sp was nullptr
+ // at the top -- the only way it became nullptr since then is via
+ // SavedLocationForRegister().
+ if (m_fallback_unwind_plan_sp.get() == nullptr)
+ return true;
+
+ // Switch the full UnwindPlan to be the fallback UnwindPlan. If we decide
+ // this isn't working, we need to restore. We'll also need to save & restore
+ // the value of the m_cfa ivar. Save is down below a bit in 'old_cfa'.
+ UnwindPlanSP original_full_unwind_plan_sp = m_full_unwind_plan_sp;
+ addr_t old_cfa = m_cfa;
+ addr_t old_afa = m_afa;
+
+ m_registers.clear();
+
+ m_full_unwind_plan_sp = m_fallback_unwind_plan_sp;
+
+ UnwindPlan::RowSP active_row =
+ m_fallback_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+
+ if (active_row &&
+ active_row->GetCFAValue().GetValueType() !=
+ UnwindPlan::Row::FAValue::unspecified) {
+ addr_t new_cfa;
+ if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
+ active_row->GetCFAValue(), new_cfa) ||
+ new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) {
+ UnwindLogMsg("failed to get cfa with fallback unwindplan");
+ m_fallback_unwind_plan_sp.reset();
+ m_full_unwind_plan_sp = original_full_unwind_plan_sp;
+ return false;
+ }
+ m_cfa = new_cfa;
+
+ ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
+ active_row->GetAFAValue(), m_afa);
+
+ if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB),
+ regloc) ==
+ UnwindLLDB::RegisterSearchResult::eRegisterFound) {
+ const RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB));
+ if (reg_info) {
+ RegisterValue reg_value;
+ if (ReadRegisterValueFromRegisterLocation(regloc, reg_info,
+ reg_value)) {
+ new_caller_pc_value = reg_value.GetAsUInt64();
+ }
+ }
+ }
+
+ if (new_caller_pc_value == LLDB_INVALID_ADDRESS) {
+ UnwindLogMsg("failed to get a pc value for the caller frame with the "
+ "fallback unwind plan");
+ m_fallback_unwind_plan_sp.reset();
+ m_full_unwind_plan_sp = original_full_unwind_plan_sp;
+ m_cfa = old_cfa;
+ m_afa = old_afa;
+ return false;
+ }
+
+ if (old_caller_pc_value == new_caller_pc_value &&
+ m_cfa == old_cfa &&
+ m_afa == old_afa) {
+ UnwindLogMsg("fallback unwind plan got the same values for this frame "
+ "CFA and caller frame pc, not using");
+ m_fallback_unwind_plan_sp.reset();
+ m_full_unwind_plan_sp = original_full_unwind_plan_sp;
+ return false;
+ }
+
+ UnwindLogMsg("trying to unwind from this function with the UnwindPlan '%s' "
+ "because UnwindPlan '%s' failed.",
+ m_fallback_unwind_plan_sp->GetSourceName().GetCString(),
+ original_full_unwind_plan_sp->GetSourceName().GetCString());
+
+ // We've copied the fallback unwind plan into the full - now clear the
+ // fallback.
+ m_fallback_unwind_plan_sp.reset();
+ }
+
+ return true;
+}
+
+bool RegisterContextLLDB::ForceSwitchToFallbackUnwindPlan() {
+ if (m_fallback_unwind_plan_sp.get() == nullptr)
+ return false;
+
+ if (m_full_unwind_plan_sp.get() == nullptr)
+ return false;
+
+ if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() ||
+ m_full_unwind_plan_sp->GetSourceName() ==
+ m_fallback_unwind_plan_sp->GetSourceName()) {
+ return false;
+ }
+
+ UnwindPlan::RowSP active_row =
+ m_fallback_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+
+ if (active_row &&
+ active_row->GetCFAValue().GetValueType() !=
+ UnwindPlan::Row::FAValue::unspecified) {
+ addr_t new_cfa;
+ if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
+ active_row->GetCFAValue(), new_cfa) ||
+ new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) {
+ UnwindLogMsg("failed to get cfa with fallback unwindplan");
+ m_fallback_unwind_plan_sp.reset();
+ return false;
+ }
+
+ ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
+ active_row->GetAFAValue(), m_afa);
+
+ m_full_unwind_plan_sp = m_fallback_unwind_plan_sp;
+ m_fallback_unwind_plan_sp.reset();
+
+ m_registers.clear();
+
+ m_cfa = new_cfa;
+
+ UnwindLogMsg("switched unconditionally to the fallback unwindplan %s",
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextLLDB::ReadFrameAddress(
+ lldb::RegisterKind row_register_kind, UnwindPlan::Row::FAValue &fa,
+ addr_t &address) {
+ RegisterValue reg_value;
+
+ address = LLDB_INVALID_ADDRESS;
+ addr_t cfa_reg_contents;
+
+ switch (fa.GetValueType()) {
+ case UnwindPlan::Row::FAValue::isRegisterDereferenced: {
+ RegisterNumber cfa_reg(m_thread, row_register_kind,
+ fa.GetRegisterNumber());
+ if (ReadGPRValue(cfa_reg, cfa_reg_contents)) {
+ const RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(cfa_reg.GetAsKind(eRegisterKindLLDB));
+ RegisterValue reg_value;
+ if (reg_info) {
+ Status error = ReadRegisterValueFromMemory(
+ reg_info, cfa_reg_contents, reg_info->byte_size, reg_value);
+ if (error.Success()) {
+ address = reg_value.GetAsUInt64();
+ UnwindLogMsg(
+ "CFA value via dereferencing reg %s (%d): reg has val 0x%" PRIx64
+ ", CFA value is 0x%" PRIx64,
+ cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
+ cfa_reg_contents, address);
+ return true;
+ } else {
+ UnwindLogMsg("Tried to deref reg %s (%d) [0x%" PRIx64
+ "] but memory read failed.",
+ cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
+ cfa_reg_contents);
+ }
+ }
+ }
+ break;
+ }
+ case UnwindPlan::Row::FAValue::isRegisterPlusOffset: {
+ 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) {
+ UnwindLogMsg(
+ "Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64,
+ cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
+ cfa_reg_contents);
+ cfa_reg_contents = LLDB_INVALID_ADDRESS;
+ return false;
+ }
+ address = cfa_reg_contents + fa.GetOffset();
+ UnwindLogMsg(
+ "CFA is 0x%" PRIx64 ": Register %s (%d) contents are 0x%" PRIx64
+ ", offset is %d",
+ address, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
+ cfa_reg_contents, fa.GetOffset());
+ return true;
+ }
+ break;
+ }
+ case UnwindPlan::Row::FAValue::isDWARFExpression: {
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ DataExtractor dwarfdata(fa.GetDWARFExpressionBytes(),
+ fa.GetDWARFExpressionLength(),
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+ ModuleSP opcode_ctx;
+ DWARFExpression dwarfexpr(opcode_ctx, dwarfdata, nullptr, 0,
+ fa.GetDWARFExpressionLength());
+ dwarfexpr.SetRegisterKind(row_register_kind);
+ Value result;
+ Status error;
+ if (dwarfexpr.Evaluate(&exe_ctx, this, 0, nullptr, nullptr, result,
+ &error)) {
+ address = result.GetScalar().ULongLong();
+
+ UnwindLogMsg("CFA value set by DWARF expression is 0x%" PRIx64,
+ address);
+ return true;
+ }
+ UnwindLogMsg("Failed to set CFA value via DWARF expression: %s",
+ error.AsCString());
+ break;
+ }
+ default:
+ return false;
+ }
+ return false;
+}
+
+// Retrieve a general purpose register value for THIS frame, as saved by the
+// NEXT frame, i.e. the frame that
+// this frame called. e.g.
+//
+// foo () { }
+// bar () { foo (); }
+// main () { bar (); }
+//
+// stopped in foo() so
+// frame 0 - foo
+// frame 1 - bar
+// frame 2 - main
+// and this RegisterContext is for frame 1 (bar) - if we want to get the pc
+// value for frame 1, we need to ask
+// where frame 0 (the "next" frame) saved that and retrieve the value.
+
+bool RegisterContextLLDB::ReadGPRValue(lldb::RegisterKind register_kind,
+ uint32_t regnum, addr_t &value) {
+ if (!IsValid())
+ return false;
+
+ uint32_t lldb_regnum;
+ if (register_kind == eRegisterKindLLDB) {
+ lldb_regnum = regnum;
+ } else if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds(
+ register_kind, regnum, eRegisterKindLLDB, lldb_regnum)) {
+ return false;
+ }
+
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum);
+ RegisterValue reg_value;
+ // if this is frame 0 (currently executing frame), get the requested reg
+ // contents from the actual thread registers
+ if (IsFrameZero()) {
+ if (m_thread.GetRegisterContext()->ReadRegister(reg_info, reg_value)) {
+ value = reg_value.GetAsUInt64();
+ return true;
+ }
+ return false;
+ }
+
+ bool pc_register = false;
+ uint32_t generic_regnum;
+ if (register_kind == eRegisterKindGeneric &&
+ (regnum == LLDB_REGNUM_GENERIC_PC || regnum == LLDB_REGNUM_GENERIC_RA)) {
+ pc_register = true;
+ } else if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds(
+ register_kind, regnum, eRegisterKindGeneric, generic_regnum) &&
+ (generic_regnum == LLDB_REGNUM_GENERIC_PC ||
+ generic_regnum == LLDB_REGNUM_GENERIC_RA)) {
+ pc_register = true;
+ }
+
+ lldb_private::UnwindLLDB::RegisterLocation regloc;
+ if (!m_parent_unwind.SearchForSavedLocationForRegister(
+ lldb_regnum, regloc, m_frame_number - 1, pc_register)) {
+ return false;
+ }
+ if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) {
+ value = reg_value.GetAsUInt64();
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextLLDB::ReadGPRValue(const RegisterNumber &regnum,
+ addr_t &value) {
+ return ReadGPRValue(regnum.GetRegisterKind(), regnum.GetRegisterNumber(),
+ value);
+}
+
+// Find the value of a register in THIS frame
+
+bool RegisterContextLLDB::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ if (!IsValid())
+ return false;
+
+ const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
+ UnwindLogMsgVerbose("looking for register saved location for reg %d",
+ lldb_regnum);
+
+ // If this is the 0th frame, hand this over to the live register context
+ if (IsFrameZero()) {
+ UnwindLogMsgVerbose("passing along to the live register context for reg %d",
+ lldb_regnum);
+ return m_thread.GetRegisterContext()->ReadRegister(reg_info, value);
+ }
+
+ bool is_pc_regnum = false;
+ if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC ||
+ reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_RA) {
+ is_pc_regnum = true;
+ }
+
+ lldb_private::UnwindLLDB::RegisterLocation regloc;
+ // Find out where the NEXT frame saved THIS frame's register contents
+ if (!m_parent_unwind.SearchForSavedLocationForRegister(
+ lldb_regnum, regloc, m_frame_number - 1, is_pc_regnum))
+ return false;
+
+ return ReadRegisterValueFromRegisterLocation(regloc, reg_info, value);
+}
+
+bool RegisterContextLLDB::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ if (!IsValid())
+ return false;
+
+ const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
+ UnwindLogMsgVerbose("looking for register saved location for reg %d",
+ lldb_regnum);
+
+ // If this is the 0th frame, hand this over to the live register context
+ if (IsFrameZero()) {
+ UnwindLogMsgVerbose("passing along to the live register context for reg %d",
+ lldb_regnum);
+ return m_thread.GetRegisterContext()->WriteRegister(reg_info, value);
+ }
+
+ lldb_private::UnwindLLDB::RegisterLocation regloc;
+ // Find out where the NEXT frame saved THIS frame's register contents
+ if (!m_parent_unwind.SearchForSavedLocationForRegister(
+ lldb_regnum, regloc, m_frame_number - 1, false))
+ return false;
+
+ return WriteRegisterValueToRegisterLocation(regloc, reg_info, value);
+}
+
+// Don't need to implement this one
+bool RegisterContextLLDB::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+// Don't need to implement this one
+bool RegisterContextLLDB::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+// Retrieve the pc value for THIS from
+
+bool RegisterContextLLDB::GetCFA(addr_t &cfa) {
+ if (!IsValid()) {
+ return false;
+ }
+ if (m_cfa == LLDB_INVALID_ADDRESS) {
+ return false;
+ }
+ cfa = m_cfa;
+ return true;
+}
+
+RegisterContextLLDB::SharedPtr RegisterContextLLDB::GetNextFrame() const {
+ RegisterContextLLDB::SharedPtr regctx;
+ if (m_frame_number == 0)
+ return regctx;
+ return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number - 1);
+}
+
+RegisterContextLLDB::SharedPtr RegisterContextLLDB::GetPrevFrame() const {
+ RegisterContextLLDB::SharedPtr regctx;
+ return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number + 1);
+}
+
+// Retrieve the address of the start of the function of THIS frame
+
+bool RegisterContextLLDB::GetStartPC(addr_t &start_pc) {
+ if (!IsValid())
+ return false;
+
+ if (!m_start_pc.IsValid()) {
+ bool read_successfully = ReadPC (start_pc);
+ if (read_successfully)
+ {
+ ProcessSP process_sp (m_thread.GetProcess());
+ if (process_sp)
+ {
+ ABI *abi = process_sp->GetABI().get();
+ if (abi)
+ start_pc = abi->FixCodeAddress(start_pc);
+ }
+ }
+ return read_successfully;
+ }
+ start_pc = m_start_pc.GetLoadAddress(CalculateTarget().get());
+ return true;
+}
+
+// Retrieve the current pc value for THIS frame, as saved by the NEXT frame.
+
+bool RegisterContextLLDB::ReadPC(addr_t &pc) {
+ if (!IsValid())
+ return false;
+
+ bool above_trap_handler = false;
+ if (GetNextFrame().get() && GetNextFrame()->IsValid() &&
+ GetNextFrame()->IsTrapHandlerFrame())
+ above_trap_handler = true;
+
+ if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) {
+ // A pc value of 0 or 1 is impossible in the middle of the stack -- it
+ // indicates the end of a stack walk.
+ // On the currently executing frame (or such a frame interrupted
+ // asynchronously by sigtramp et al) this may occur if code has jumped
+ // through a NULL pointer -- we want to be able to unwind past that frame
+ // to help find the bug.
+
+ ProcessSP process_sp (m_thread.GetProcess());
+ if (process_sp)
+ {
+ ABI *abi = process_sp->GetABI().get();
+ if (abi)
+ pc = abi->FixCodeAddress(pc);
+ }
+
+ return !(m_all_registers_available == false &&
+ above_trap_handler == false && (pc == 0 || pc == 1));
+ } else {
+ return false;
+ }
+}
+
+void RegisterContextLLDB::UnwindLogMsg(const char *fmt, ...) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log) {
+ va_list args;
+ va_start(args, fmt);
+
+ char *logmsg;
+ if (vasprintf(&logmsg, fmt, args) == -1 || logmsg == nullptr) {
+ if (logmsg)
+ free(logmsg);
+ va_end(args);
+ return;
+ }
+ va_end(args);
+
+ log->Printf("%*sth%d/fr%u %s", m_frame_number < 100 ? m_frame_number : 100,
+ "", m_thread.GetIndexID(), m_frame_number, logmsg);
+ free(logmsg);
+ }
+}
+
+void RegisterContextLLDB::UnwindLogMsgVerbose(const char *fmt, ...) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log && log->GetVerbose()) {
+ va_list args;
+ va_start(args, fmt);
+
+ char *logmsg;
+ if (vasprintf(&logmsg, fmt, args) == -1 || logmsg == nullptr) {
+ if (logmsg)
+ free(logmsg);
+ va_end(args);
+ return;
+ }
+ va_end(args);
+
+ log->Printf("%*sth%d/fr%u %s", m_frame_number < 100 ? m_frame_number : 100,
+ "", m_thread.GetIndexID(), m_frame_number, logmsg);
+ free(logmsg);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h
new file mode 100644
index 000000000000..64dd394d233b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h
@@ -0,0 +1,253 @@
+//===-- RegisterContextLLDB.h --------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextLLDB_h_
+#define lldb_RegisterContextLLDB_h_
+
+#include <vector>
+
+#include "UnwindLLDB.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/RegisterNumber.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class UnwindLLDB;
+
+class RegisterContextLLDB : public lldb_private::RegisterContext {
+public:
+ typedef std::shared_ptr<RegisterContextLLDB> SharedPtr;
+
+ RegisterContextLLDB(lldb_private::Thread &thread, const SharedPtr &next_frame,
+ lldb_private::SymbolContext &sym_ctx,
+ uint32_t frame_number,
+ lldb_private::UnwindLLDB &unwind_lldb);
+
+ ~RegisterContextLLDB() override = default;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ bool IsValid() const;
+
+ bool IsTrapHandlerFrame() const;
+
+ bool GetCFA(lldb::addr_t &cfa);
+
+ bool GetStartPC(lldb::addr_t &start_pc);
+
+ bool ReadPC(lldb::addr_t &start_pc);
+
+private:
+ enum FrameType {
+ eNormalFrame,
+ eTrapHandlerFrame,
+ eDebuggerFrame, // a debugger inferior function call frame; we get caller's
+ // registers from debugger
+ eSkipFrame, // The unwind resulted in a bogus frame but may get back on
+ // track so we don't want to give up yet
+ eNotAValidFrame // this frame is invalid for some reason - most likely it is
+ // past the top (end) of the stack
+ };
+
+ // UnwindLLDB needs to pass around references to RegisterLocations
+ friend class UnwindLLDB;
+
+ // Returns true if we have an unwind loop -- the same stack frame unwinding
+ // multiple times.
+ bool CheckIfLoopingStack();
+
+ // Indicates whether this frame is frame zero -- the currently
+ // executing frame -- or not.
+ bool IsFrameZero() const;
+
+ void InitializeZerothFrame();
+
+ void InitializeNonZerothFrame();
+
+ SharedPtr GetNextFrame() const;
+
+ SharedPtr GetPrevFrame() const;
+
+ // A SkipFrame occurs when the unwind out of frame 0 didn't go right -- we've
+ // got one bogus frame at frame #1.
+ // There is a good chance we'll get back on track if we follow the frame
+ // pointer chain (or whatever is appropriate
+ // on this ABI) so we allow one invalid frame to be in the stack. Ideally
+ // we'll mark this frame specially at some
+ // point and indicate to the user that the unwinder had a hiccup. Often when
+ // this happens we will miss a frame of
+ // the program's actual stack in the unwind and we want to flag that for the
+ // user somehow.
+ bool IsSkipFrame() const;
+
+ /// Determines if a SymbolContext is a trap handler or not
+ ///
+ /// Given a SymbolContext, determines if this is a trap handler function
+ /// aka asynchronous signal handler.
+ ///
+ /// \return
+ /// Returns true if the SymbolContext is a trap handler.
+ bool IsTrapHandlerSymbol(lldb_private::Process *process,
+ const lldb_private::SymbolContext &m_sym_ctx) const;
+
+ // Provide a location for where THIS function saved the CALLER's register
+ // value
+ // Or a frame "below" this one saved it, i.e. a function called by this one,
+ // preserved a register that this
+ // function didn't modify/use.
+ //
+ // The RegisterLocation type may be set to eRegisterNotAvailable -- this will
+ // happen for a volatile register
+ // being queried mid-stack. Instead of floating frame 0's contents of that
+ // register up the stack (which may
+ // or may not be the value of that reg when the function was executing), we
+ // won't return any value.
+ //
+ // If a non-volatile register (a "preserved" register) is requested mid-stack
+ // and no frames "below" the requested
+ // stack have saved the register anywhere, it is safe to assume that frame 0's
+ // register values are still the same
+ // as the requesting frame's.
+ lldb_private::UnwindLLDB::RegisterSearchResult
+ SavedLocationForRegister(uint32_t lldb_regnum,
+ lldb_private::UnwindLLDB::RegisterLocation &regloc);
+
+ bool ReadRegisterValueFromRegisterLocation(
+ lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegisterValueToRegisterLocation(
+ lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ /// If the unwind has to the caller frame has failed, try something else
+ ///
+ /// If lldb is using an assembly language based UnwindPlan for a frame and
+ /// the unwind to the caller frame fails, try falling back to a generic
+ /// UnwindPlan (architecture default unwindplan) to see if that might work
+ /// better. This is mostly helping to work around problems where the
+ /// assembly language inspection fails on hand-written assembly code.
+ ///
+ /// \return
+ /// Returns true if a fallback unwindplan was found & was installed.
+ bool TryFallbackUnwindPlan();
+
+ /// Switch to the fallback unwind plan unconditionally without any safety
+ /// checks that it is providing better results than the normal unwind plan.
+ ///
+ /// The only time it is valid to call this method is if the full unwindplan is
+ /// found to be fundamentally incorrect/impossible.
+ ///
+ /// Returns true if it was able to install the fallback unwind plan.
+ bool ForceSwitchToFallbackUnwindPlan();
+
+ // Get the contents of a general purpose (address-size) register for this
+ // frame
+ // (usually retrieved from the next frame)
+ bool ReadGPRValue(lldb::RegisterKind register_kind, uint32_t regnum,
+ lldb::addr_t &value);
+
+ bool ReadGPRValue(const RegisterNumber &reg_num, lldb::addr_t &value);
+
+ // Get the Frame Address register for a given frame.
+ bool ReadFrameAddress(lldb::RegisterKind register_kind,
+ UnwindPlan::Row::FAValue &fa, lldb::addr_t &address);
+
+ lldb::UnwindPlanSP GetFastUnwindPlanForFrame();
+
+ lldb::UnwindPlanSP GetFullUnwindPlanForFrame();
+
+ void UnwindLogMsg(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+
+ void UnwindLogMsgVerbose(const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+
+ bool IsUnwindPlanValidForCurrentPC(lldb::UnwindPlanSP unwind_plan_sp,
+ int &valid_pc_offset);
+
+ lldb_private::Thread &m_thread;
+
+ ///
+ // The following tell us how to retrieve the CALLER's register values (ie the
+ // "previous" frame, aka the frame above)
+ // i.e. where THIS frame saved them
+ ///
+
+ lldb::UnwindPlanSP m_fast_unwind_plan_sp; // may be NULL
+ lldb::UnwindPlanSP m_full_unwind_plan_sp;
+ lldb::UnwindPlanSP m_fallback_unwind_plan_sp; // may be NULL
+
+ bool m_all_registers_available; // Can we retrieve all regs or just
+ // nonvolatile regs?
+ int m_frame_type; // enum FrameType
+
+ lldb::addr_t m_cfa;
+ lldb::addr_t m_afa;
+ lldb_private::Address m_start_pc;
+ lldb_private::Address m_current_pc;
+
+ int m_current_offset; // how far into the function we've executed; -1 if
+ // unknown
+ // 0 if no instructions have been executed yet.
+
+ int m_current_offset_backed_up_one; // how far into the function we've
+ // executed; -1 if unknown
+ // 0 if no instructions have been executed yet.
+ // On architectures where the return address on the stack points
+ // to the instruction after the CALL, this value will have 1
+ // subtracted from it. Else a function that ends in a CALL will
+ // have an offset pointing into the next function's address range.
+ // m_current_pc has the actual address of the "current" pc.
+
+ lldb_private::SymbolContext &m_sym_ctx;
+ bool m_sym_ctx_valid; // if ResolveSymbolContextForAddress fails, don't try to
+ // use m_sym_ctx
+
+ uint32_t m_frame_number; // What stack frame this RegisterContext is
+
+ std::map<uint32_t, lldb_private::UnwindLLDB::RegisterLocation>
+ m_registers; // where to find reg values for this frame
+
+ lldb_private::UnwindLLDB &m_parent_unwind; // The UnwindLLDB that is creating
+ // this RegisterContextLLDB
+
+ // For RegisterContextLLDB only
+
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextLLDB);
+};
+
+} // namespace lldb_private
+
+#endif // lldb_RegisterContextLLDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp
new file mode 100644
index 000000000000..79979639dc7e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp
@@ -0,0 +1,133 @@
+//===-- RegisterContextLinux_i386.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextLinux_i386.h"
+#include "RegisterContextPOSIX_x86.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+struct GPR {
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebp;
+ uint32_t eax;
+ uint32_t ds;
+ uint32_t es;
+ uint32_t fs;
+ uint32_t gs;
+ uint32_t orig_eax;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+ uint32_t esp;
+ uint32_t ss;
+};
+
+struct FPR_i386 {
+ uint16_t fctrl; // FPU Control Word (fcw)
+ uint16_t fstat; // FPU Status Word (fsw)
+ uint16_t ftag; // FPU Tag Word (ftw)
+ uint16_t fop; // Last Instruction Opcode (fop)
+ union {
+ struct {
+ uint64_t fip; // Instruction Pointer
+ uint64_t fdp; // Data Pointer
+ } x86_64;
+ struct {
+ uint32_t fioff; // FPU IP Offset (fip)
+ uint32_t fiseg; // FPU IP Selector (fcs)
+ uint32_t fooff; // FPU Operand Pointer Offset (foo)
+ uint32_t foseg; // FPU Operand Pointer Selector (fos)
+ } i386_; // Added _ in the end to avoid error with gcc defining i386 in some
+ // cases
+ } ptr;
+ uint32_t mxcsr; // MXCSR Register State
+ uint32_t mxcsrmask; // MXCSR Mask
+ MMSReg stmm[8]; // 8*16 bytes for each FP-reg = 128 bytes
+ XMMReg xmm[8]; // 8*16 bytes for each XMM-reg = 128 bytes
+ uint32_t padding[56];
+};
+
+struct UserArea {
+ GPR regs; // General purpose registers.
+ int32_t fpvalid; // True if FPU is being used.
+ FPR_i386 i387; // FPU registers.
+ uint32_t tsize; // Text segment size.
+ uint32_t dsize; // Data segment size.
+ uint32_t ssize; // Stack segment size.
+ uint32_t start_code; // VM address of text.
+ uint32_t start_stack; // VM address of stack bottom (top in rsp).
+ int32_t signal; // Signal causing core dump.
+ int32_t reserved; // Unused.
+ uint32_t ar0; // Location of GPR's.
+ uint32_t fpstate; // Location of FPR's. Should be a FXSTATE *, but this
+ // has to be 32-bits even on 64-bit systems.
+ uint32_t magic; // Identifier for core dumps.
+ char u_comm[32]; // Command causing core dump.
+ uint32_t u_debugreg[8]; // Debug registers (DR0 - DR7).
+};
+
+#define DR_SIZE sizeof(((UserArea *)NULL)->u_debugreg[0])
+#define DR_0_OFFSET 0xFC
+#define DR_OFFSET(reg_index) (DR_0_OFFSET + (reg_index * 4))
+#define FPR_SIZE(reg) sizeof(((FPR_i386 *)NULL)->reg)
+
+// Include RegisterInfos_i386 to declare our g_register_infos_i386 structure.
+#define DECLARE_REGISTER_INFOS_I386_STRUCT
+#include "RegisterInfos_i386.h"
+#undef DECLARE_REGISTER_INFOS_I386_STRUCT
+
+RegisterContextLinux_i386::RegisterContextLinux_i386(
+ const ArchSpec &target_arch)
+ : RegisterInfoInterface(target_arch) {
+ RegisterInfo orig_ax = {"orig_eax",
+ nullptr,
+ sizeof(((GPR *)nullptr)->orig_eax),
+ (LLVM_EXTENSION offsetof(GPR, orig_eax)),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0};
+ d_register_infos.push_back(orig_ax);
+}
+
+size_t RegisterContextLinux_i386::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextLinux_i386::GetRegisterInfo() const {
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ return g_register_infos_i386;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+uint32_t RegisterContextLinux_i386::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_i386) /
+ sizeof(g_register_infos_i386[0]));
+}
+
+uint32_t RegisterContextLinux_i386::GetUserRegisterCount() const {
+ return static_cast<uint32_t>(k_num_user_registers_i386);
+}
+
+const std::vector<lldb_private::RegisterInfo> *
+RegisterContextLinux_i386::GetDynamicRegisterInfoP() const {
+ return &d_register_infos;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.h
new file mode 100644
index 000000000000..5567a1ac42e5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.h
@@ -0,0 +1,33 @@
+//===-- RegisterContextLinux_i386.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextLinux_i386_H_
+#define liblldb_RegisterContextLinux_i386_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextLinux_i386 : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextLinux_i386(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+ const std::vector<lldb_private::RegisterInfo> *
+ GetDynamicRegisterInfoP() const override;
+
+private:
+ std::vector<lldb_private::RegisterInfo> d_register_infos;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp
new file mode 100644
index 000000000000..fc60fea79176
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp
@@ -0,0 +1,149 @@
+//===-- RegisterContextLinux_mips.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include <stddef.h>
+#include <vector>
+
+// For eh_frame and DWARF Register numbers
+#include "RegisterContextLinux_mips.h"
+
+// Internal codes for mips registers
+#include "lldb-mips-linux-register-enums.h"
+
+// For GP and FP buffers
+#include "RegisterContext_mips.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// Include RegisterInfos_mips to declare our g_register_infos_mips structure.
+#define DECLARE_REGISTER_INFOS_MIPS_STRUCT
+#include "RegisterInfos_mips.h"
+#undef DECLARE_REGISTER_INFOS_MIPS_STRUCT
+
+// mips general purpose registers.
+const uint32_t g_gp_regnums_mips[] = {
+ gpr_zero_mips, gpr_r1_mips, gpr_r2_mips, gpr_r3_mips,
+ gpr_r4_mips, gpr_r5_mips, gpr_r6_mips, gpr_r7_mips,
+ gpr_r8_mips, gpr_r9_mips, gpr_r10_mips, gpr_r11_mips,
+ gpr_r12_mips, gpr_r13_mips, gpr_r14_mips, gpr_r15_mips,
+ gpr_r16_mips, gpr_r17_mips, gpr_r18_mips, gpr_r19_mips,
+ gpr_r20_mips, gpr_r21_mips, gpr_r22_mips, gpr_r23_mips,
+ gpr_r24_mips, gpr_r25_mips, gpr_r26_mips, gpr_r27_mips,
+ gpr_gp_mips, gpr_sp_mips, gpr_r30_mips, gpr_ra_mips,
+ gpr_sr_mips, gpr_mullo_mips, gpr_mulhi_mips, gpr_badvaddr_mips,
+ gpr_cause_mips, gpr_pc_mips, gpr_config5_mips,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_gp_regnums_mips) / sizeof(g_gp_regnums_mips[0])) - 1 ==
+ k_num_gpr_registers_mips,
+ "g_gp_regnums_mips has wrong number of register infos");
+// mips floating point registers.
+const uint32_t g_fp_regnums_mips[] = {
+ fpr_f0_mips, fpr_f1_mips, fpr_f2_mips, fpr_f3_mips,
+ fpr_f4_mips, fpr_f5_mips, fpr_f6_mips, fpr_f7_mips,
+ fpr_f8_mips, fpr_f9_mips, fpr_f10_mips, fpr_f11_mips,
+ fpr_f12_mips, fpr_f13_mips, fpr_f14_mips, fpr_f15_mips,
+ fpr_f16_mips, fpr_f17_mips, fpr_f18_mips, fpr_f19_mips,
+ fpr_f20_mips, fpr_f21_mips, fpr_f22_mips, fpr_f23_mips,
+ fpr_f24_mips, fpr_f25_mips, fpr_f26_mips, fpr_f27_mips,
+ fpr_f28_mips, fpr_f29_mips, fpr_f30_mips, fpr_f31_mips,
+ fpr_fcsr_mips, fpr_fir_mips, fpr_config5_mips,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_fp_regnums_mips) / sizeof(g_fp_regnums_mips[0])) - 1 ==
+ k_num_fpr_registers_mips,
+ "g_fp_regnums_mips has wrong number of register infos");
+
+// mips MSA registers.
+const uint32_t g_msa_regnums_mips[] = {
+ msa_w0_mips, msa_w1_mips, msa_w2_mips, msa_w3_mips,
+ msa_w4_mips, msa_w5_mips, msa_w6_mips, msa_w7_mips,
+ msa_w8_mips, msa_w9_mips, msa_w10_mips, msa_w11_mips,
+ msa_w12_mips, msa_w13_mips, msa_w14_mips, msa_w15_mips,
+ msa_w16_mips, msa_w17_mips, msa_w18_mips, msa_w19_mips,
+ msa_w20_mips, msa_w21_mips, msa_w22_mips, msa_w23_mips,
+ msa_w24_mips, msa_w25_mips, msa_w26_mips, msa_w27_mips,
+ msa_w28_mips, msa_w29_mips, msa_w30_mips, msa_w31_mips,
+ msa_fcsr_mips, msa_fir_mips, msa_mcsr_mips, msa_mir_mips,
+ msa_config5_mips,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_msa_regnums_mips) / sizeof(g_msa_regnums_mips[0])) -
+ 1 ==
+ k_num_msa_registers_mips,
+ "g_msa_regnums_mips has wrong number of register infos");
+
+// Number of register sets provided by this context.
+constexpr size_t k_num_register_sets = 3;
+
+// Register sets for mips.
+static const RegisterSet g_reg_sets_mips[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_mips,
+ g_gp_regnums_mips},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_mips,
+ g_fp_regnums_mips},
+ {"MSA Registers", "msa", k_num_msa_registers_mips, g_msa_regnums_mips}};
+
+uint32_t GetUserRegisterInfoCount(bool msa_present) {
+ if (msa_present)
+ return static_cast<uint32_t>(k_num_user_registers_mips);
+ return static_cast<uint32_t>(k_num_user_registers_mips -
+ k_num_msa_registers_mips);
+}
+
+RegisterContextLinux_mips::RegisterContextLinux_mips(
+ const ArchSpec &target_arch, bool msa_present)
+ : RegisterInfoInterface(target_arch),
+ m_user_register_count(GetUserRegisterInfoCount(msa_present)) {}
+
+size_t RegisterContextLinux_mips::GetGPRSize() const {
+ return sizeof(GPR_linux_mips);
+}
+
+const RegisterInfo *RegisterContextLinux_mips::GetRegisterInfo() const {
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ return g_register_infos_mips;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+const RegisterSet *
+RegisterContextLinux_mips::GetRegisterSet(size_t set) const {
+ if (set >= k_num_register_sets)
+ return nullptr;
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ return &g_reg_sets_mips[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+size_t
+RegisterContextLinux_mips::GetRegisterSetCount() const {
+ return k_num_register_sets;
+}
+
+uint32_t RegisterContextLinux_mips::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_mips) /
+ sizeof(g_register_infos_mips[0]));
+}
+
+uint32_t RegisterContextLinux_mips::GetUserRegisterCount() const {
+ return static_cast<uint32_t>(m_user_register_count);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.h
new file mode 100644
index 000000000000..e637dfc15e4d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.h
@@ -0,0 +1,36 @@
+//===-- RegisterContextLinux_mips.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextLinux_mips_H_
+#define liblldb_RegisterContextLinux_mips_H_
+
+#include "RegisterInfoInterface.h"
+#include "lldb/lldb-private.h"
+
+class RegisterContextLinux_mips : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextLinux_mips(const lldb_private::ArchSpec &target_arch,
+ bool msa_present = true);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) const;
+
+ size_t GetRegisterSetCount() const;
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+private:
+ uint32_t m_user_register_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp
new file mode 100644
index 000000000000..3927883c47a4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp
@@ -0,0 +1,207 @@
+//===-- RegisterContextLinux_mips64.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+
+#include <stddef.h>
+#include <vector>
+
+// For eh_frame and DWARF Register numbers
+#include "RegisterContextLinux_mips64.h"
+
+// For GP and FP buffers
+#include "RegisterContext_mips.h"
+
+// Internal codes for all mips32 and mips64 registers
+#include "lldb-mips-linux-register-enums.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Include RegisterInfos_mips64 to declare our g_register_infos_mips64
+// structure.
+#define DECLARE_REGISTER_INFOS_MIPS64_STRUCT
+#define LINUX_MIPS64
+#include "RegisterInfos_mips64.h"
+#undef LINUX_MIPS64
+#undef DECLARE_REGISTER_INFOS_MIPS64_STRUCT
+
+// Include RegisterInfos_mips to declare our g_register_infos_mips structure.
+#define DECLARE_REGISTER_INFOS_MIPS_STRUCT
+#include "RegisterInfos_mips.h"
+#undef DECLARE_REGISTER_INFOS_MIPS_STRUCT
+
+// mips64 general purpose registers.
+const uint32_t g_gp_regnums_mips64[] = {
+ gpr_zero_mips64, gpr_r1_mips64, gpr_r2_mips64,
+ gpr_r3_mips64, gpr_r4_mips64, gpr_r5_mips64,
+ gpr_r6_mips64, gpr_r7_mips64, gpr_r8_mips64,
+ gpr_r9_mips64, gpr_r10_mips64, gpr_r11_mips64,
+ gpr_r12_mips64, gpr_r13_mips64, gpr_r14_mips64,
+ gpr_r15_mips64, gpr_r16_mips64, gpr_r17_mips64,
+ gpr_r18_mips64, gpr_r19_mips64, gpr_r20_mips64,
+ gpr_r21_mips64, gpr_r22_mips64, gpr_r23_mips64,
+ gpr_r24_mips64, gpr_r25_mips64, gpr_r26_mips64,
+ gpr_r27_mips64, gpr_gp_mips64, gpr_sp_mips64,
+ gpr_r30_mips64, gpr_ra_mips64, gpr_sr_mips64,
+ gpr_mullo_mips64, gpr_mulhi_mips64, gpr_badvaddr_mips64,
+ gpr_cause_mips64, gpr_pc_mips64, gpr_config5_mips64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_gp_regnums_mips64) / sizeof(g_gp_regnums_mips64[0])) -
+ 1 ==
+ k_num_gpr_registers_mips64,
+ "g_gp_regnums_mips64 has wrong number of register infos");
+
+// mips64 floating point registers.
+const uint32_t g_fp_regnums_mips64[] = {
+ fpr_f0_mips64, fpr_f1_mips64, fpr_f2_mips64, fpr_f3_mips64,
+ fpr_f4_mips64, fpr_f5_mips64, fpr_f6_mips64, fpr_f7_mips64,
+ fpr_f8_mips64, fpr_f9_mips64, fpr_f10_mips64, fpr_f11_mips64,
+ fpr_f12_mips64, fpr_f13_mips64, fpr_f14_mips64, fpr_f15_mips64,
+ fpr_f16_mips64, fpr_f17_mips64, fpr_f18_mips64, fpr_f19_mips64,
+ fpr_f20_mips64, fpr_f21_mips64, fpr_f22_mips64, fpr_f23_mips64,
+ fpr_f24_mips64, fpr_f25_mips64, fpr_f26_mips64, fpr_f27_mips64,
+ fpr_f28_mips64, fpr_f29_mips64, fpr_f30_mips64, fpr_f31_mips64,
+ fpr_fcsr_mips64, fpr_fir_mips64, fpr_config5_mips64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_fp_regnums_mips64) / sizeof(g_fp_regnums_mips64[0])) -
+ 1 ==
+ k_num_fpr_registers_mips64,
+ "g_fp_regnums_mips64 has wrong number of register infos");
+
+// mips64 MSA registers.
+const uint32_t g_msa_regnums_mips64[] = {
+ msa_w0_mips64, msa_w1_mips64, msa_w2_mips64, msa_w3_mips64,
+ msa_w4_mips64, msa_w5_mips64, msa_w6_mips64, msa_w7_mips64,
+ msa_w8_mips64, msa_w9_mips64, msa_w10_mips64, msa_w11_mips64,
+ msa_w12_mips64, msa_w13_mips64, msa_w14_mips64, msa_w15_mips64,
+ msa_w16_mips64, msa_w17_mips64, msa_w18_mips64, msa_w19_mips64,
+ msa_w20_mips64, msa_w21_mips64, msa_w22_mips64, msa_w23_mips64,
+ msa_w24_mips64, msa_w25_mips64, msa_w26_mips64, msa_w27_mips64,
+ msa_w28_mips64, msa_w29_mips64, msa_w30_mips64, msa_w31_mips64,
+ msa_fcsr_mips64, msa_fir_mips64, msa_mcsr_mips64, msa_mir_mips64,
+ msa_config5_mips64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_msa_regnums_mips64) / sizeof(g_msa_regnums_mips64[0])) -
+ 1 ==
+ k_num_msa_registers_mips64,
+ "g_msa_regnums_mips64 has wrong number of register infos");
+
+// Number of register sets provided by this context.
+constexpr size_t k_num_register_sets = 3;
+
+// Register sets for mips64.
+static const RegisterSet g_reg_sets_mips64[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_mips64,
+ g_gp_regnums_mips64},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_mips64,
+ g_fp_regnums_mips64},
+ {"MSA Registers", "msa", k_num_msa_registers_mips64, g_msa_regnums_mips64},
+};
+
+const RegisterSet *
+RegisterContextLinux_mips64::GetRegisterSet(size_t set) const {
+ if (set >= k_num_register_sets)
+ return nullptr;
+
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ return &g_reg_sets_mips64[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+ return nullptr;
+}
+
+size_t
+RegisterContextLinux_mips64::GetRegisterSetCount() const {
+ return k_num_register_sets;
+}
+
+static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ return g_register_infos_mips64;
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ return g_register_infos_mips;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ return static_cast<uint32_t>(sizeof(g_register_infos_mips64) /
+ sizeof(g_register_infos_mips64[0]));
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ return static_cast<uint32_t>(sizeof(g_register_infos_mips) /
+ sizeof(g_register_infos_mips[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch,
+ bool msa_present) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ if (msa_present)
+ return static_cast<uint32_t>(k_num_user_registers_mips);
+ return static_cast<uint32_t>(k_num_user_registers_mips -
+ k_num_msa_registers_mips);
+ case llvm::Triple::mips64el:
+ case llvm::Triple::mips64:
+ if (msa_present)
+ return static_cast<uint32_t>(k_num_user_registers_mips64);
+ return static_cast<uint32_t>(k_num_user_registers_mips64 -
+ k_num_msa_registers_mips64);
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextLinux_mips64::RegisterContextLinux_mips64(
+ const ArchSpec &target_arch, bool msa_present)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)),
+ m_user_register_count(
+ GetUserRegisterInfoCount(target_arch, msa_present)) {}
+
+size_t RegisterContextLinux_mips64::GetGPRSize() const {
+ return sizeof(GPR_linux_mips);
+}
+
+const RegisterInfo *RegisterContextLinux_mips64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextLinux_mips64::GetRegisterCount() const {
+ return m_register_info_count;
+}
+
+uint32_t RegisterContextLinux_mips64::GetUserRegisterCount() const {
+ return m_user_register_count;
+}
+
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h
new file mode 100644
index 000000000000..ca0f0140a22d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h
@@ -0,0 +1,39 @@
+//===-- RegisterContextLinux_mips64.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextLinux_mips64_H_
+#define liblldb_RegisterContextLinux_mips64_H_
+
+#include "RegisterInfoInterface.h"
+#include "lldb/lldb-private.h"
+
+class RegisterContextLinux_mips64 : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextLinux_mips64(const lldb_private::ArchSpec &target_arch,
+ bool msa_present = true);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) const;
+
+ size_t GetRegisterSetCount() const;
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+ uint32_t m_user_register_count;
+};
+
+#endif
+
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp
new file mode 100644
index 000000000000..d6401d788ab2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp
@@ -0,0 +1,74 @@
+//===-- RegisterContextLinux_s390x.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextLinux_s390x.h"
+#include "RegisterContextPOSIX_s390x.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// Include RegisterInfos_s390x to declare our g_register_infos_s390x structure.
+#define DECLARE_REGISTER_INFOS_S390X_STRUCT
+#include "RegisterInfos_s390x.h"
+#undef DECLARE_REGISTER_INFOS_S390X_STRUCT
+
+static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::systemz:
+ return g_register_infos_s390x;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::systemz:
+ return k_num_registers_s390x;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+static uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::systemz:
+ return k_num_user_registers_s390x + k_num_linux_registers_s390x;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextLinux_s390x::RegisterContextLinux_s390x(
+ const ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)),
+ m_user_register_count(GetUserRegisterInfoCount(target_arch)) {}
+
+const std::vector<lldb_private::RegisterInfo> *
+RegisterContextLinux_s390x::GetDynamicRegisterInfoP() const {
+ return &d_register_infos;
+}
+
+const RegisterInfo *RegisterContextLinux_s390x::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextLinux_s390x::GetRegisterCount() const {
+ return m_register_info_count;
+}
+
+uint32_t RegisterContextLinux_s390x::GetUserRegisterCount() const {
+ return m_user_register_count;
+}
+
+size_t RegisterContextLinux_s390x::GetGPRSize() const { return 0; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h
new file mode 100644
index 000000000000..10810c97af80
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h
@@ -0,0 +1,36 @@
+//===-- RegisterContextLinux_s390x.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextLinux_s390x_h_
+#define liblldb_RegisterContextLinux_s390x_h_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextLinux_s390x : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextLinux_s390x(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+ const std::vector<lldb_private::RegisterInfo> *
+ GetDynamicRegisterInfoP() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+ uint32_t m_user_register_count;
+ std::vector<lldb_private::RegisterInfo> d_register_infos;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp
new file mode 100644
index 000000000000..640d5bc02256
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp
@@ -0,0 +1,192 @@
+//===-- RegisterContextLinux_x86_64.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextLinux_x86_64.h"
+#include "RegisterContextLinux_i386.h"
+#include "RegisterContextPOSIX_x86.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb;
+
+typedef struct _GPR {
+ uint64_t r15;
+ uint64_t r14;
+ uint64_t r13;
+ uint64_t r12;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t r11;
+ uint64_t r10;
+ uint64_t r9;
+ uint64_t r8;
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t orig_rax;
+ uint64_t rip;
+ uint64_t cs;
+ uint64_t rflags;
+ uint64_t rsp;
+ uint64_t ss;
+ uint64_t fs_base;
+ uint64_t gs_base;
+ uint64_t ds;
+ uint64_t es;
+ uint64_t fs;
+ uint64_t gs;
+} GPR;
+
+struct DBG {
+ uint64_t dr[8];
+};
+
+struct UserArea {
+ GPR gpr; // General purpose registers.
+ int32_t fpvalid; // True if FPU is being used.
+ int32_t pad0;
+ FXSAVE fpr; // General purpose floating point registers (see FPR for extended
+ // register sets).
+ uint64_t tsize; // Text segment size.
+ uint64_t dsize; // Data segment size.
+ uint64_t ssize; // Stack segment size.
+ uint64_t start_code; // VM address of text.
+ uint64_t start_stack; // VM address of stack bottom (top in rsp).
+ int64_t signal; // Signal causing core dump.
+ int32_t reserved; // Unused.
+ int32_t pad1;
+ uint64_t ar0; // Location of GPR's.
+ FXSAVE *fpstate; // Location of FPR's.
+ uint64_t magic; // Identifier for core dumps.
+ char u_comm[32]; // Command causing core dump.
+ DBG dbg; // Debug registers.
+ uint64_t error_code; // CPU error code.
+ uint64_t fault_address; // Control register CR3.
+};
+
+#define DR_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, dbg) + \
+ LLVM_EXTENSION offsetof(DBG, dr[reg_index]))
+
+// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64
+// structure.
+#define DECLARE_REGISTER_INFOS_X86_64_STRUCT
+#include "RegisterInfos_x86_64.h"
+#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT
+
+static std::vector<lldb_private::RegisterInfo> &GetPrivateRegisterInfoVector() {
+ static std::vector<lldb_private::RegisterInfo> g_register_infos;
+ return g_register_infos;
+}
+
+static const RegisterInfo *
+GetRegisterInfo_i386(const lldb_private::ArchSpec &arch) {
+ std::vector<lldb_private::RegisterInfo> &g_register_infos =
+ GetPrivateRegisterInfoVector();
+
+ // Allocate RegisterInfo only once
+ if (g_register_infos.empty()) {
+ // Copy the register information from base class
+ std::unique_ptr<RegisterContextLinux_i386> reg_interface(
+ new RegisterContextLinux_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];
+}
+
+static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return GetRegisterInfo_i386(target_arch);
+ case llvm::Triple::x86_64:
+ return g_register_infos_x86_64;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86: {
+ assert(!GetPrivateRegisterInfoVector().empty() &&
+ "i386 register info not yet filled.");
+ return static_cast<uint32_t>(GetPrivateRegisterInfoVector().size());
+ }
+ case llvm::Triple::x86_64:
+ return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) /
+ sizeof(g_register_infos_x86_64[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+static uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return static_cast<uint32_t>(k_num_user_registers_i386);
+ case llvm::Triple::x86_64:
+ return static_cast<uint32_t>(k_num_user_registers_x86_64);
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextLinux_x86_64::RegisterContextLinux_x86_64(
+ const ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)),
+ m_user_register_count(GetUserRegisterInfoCount(target_arch)) {
+ RegisterInfo orig_ax = {"orig_rax",
+ nullptr,
+ sizeof(((GPR *)nullptr)->orig_rax),
+ (LLVM_EXTENSION offsetof(GPR, orig_rax)),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0};
+ d_register_infos.push_back(orig_ax);
+}
+
+size_t RegisterContextLinux_x86_64::GetGPRSize() const { return sizeof(GPR); }
+
+const std::vector<lldb_private::RegisterInfo> *
+RegisterContextLinux_x86_64::GetDynamicRegisterInfoP() const {
+ return &d_register_infos;
+}
+
+const RegisterInfo *RegisterContextLinux_x86_64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextLinux_x86_64::GetRegisterCount() const {
+ return m_register_info_count;
+}
+
+uint32_t RegisterContextLinux_x86_64::GetUserRegisterCount() const {
+ return m_user_register_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.h
new file mode 100644
index 000000000000..02f273cb02c9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.h
@@ -0,0 +1,36 @@
+//===-- RegisterContextLinux_x86_64.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextLinux_x86_64_H_
+#define liblldb_RegisterContextLinux_x86_64_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextLinux_x86_64 : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextLinux_x86_64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+ const std::vector<lldb_private::RegisterInfo> *
+ GetDynamicRegisterInfoP() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+ uint32_t m_user_register_count;
+ std::vector<lldb_private::RegisterInfo> d_register_infos;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp
new file mode 100644
index 000000000000..bc78c1d6160c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp
@@ -0,0 +1,160 @@
+//===-- RegisterContextMacOSXFrameBackchain.cpp -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextMacOSXFrameBackchain.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// RegisterContextMacOSXFrameBackchain constructor
+RegisterContextMacOSXFrameBackchain::RegisterContextMacOSXFrameBackchain(
+ Thread &thread, uint32_t concrete_frame_idx,
+ const UnwindMacOSXFrameBackchain::Cursor &cursor)
+ : RegisterContext(thread, concrete_frame_idx), m_cursor(cursor),
+ m_cursor_is_valid(true) {}
+
+// Destructor
+RegisterContextMacOSXFrameBackchain::~RegisterContextMacOSXFrameBackchain() {}
+
+void RegisterContextMacOSXFrameBackchain::InvalidateAllRegisters() {
+ m_cursor_is_valid = false;
+}
+
+size_t RegisterContextMacOSXFrameBackchain::GetRegisterCount() {
+ return m_thread.GetRegisterContext()->GetRegisterCount();
+}
+
+const RegisterInfo *
+RegisterContextMacOSXFrameBackchain::GetRegisterInfoAtIndex(size_t reg) {
+ return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg);
+}
+
+size_t RegisterContextMacOSXFrameBackchain::GetRegisterSetCount() {
+ return m_thread.GetRegisterContext()->GetRegisterSetCount();
+}
+
+const RegisterSet *
+RegisterContextMacOSXFrameBackchain::GetRegisterSet(size_t reg_set) {
+ return m_thread.GetRegisterContext()->GetRegisterSet(reg_set);
+}
+
+bool RegisterContextMacOSXFrameBackchain::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!m_cursor_is_valid)
+ return false;
+
+ uint64_t reg_value = LLDB_INVALID_ADDRESS;
+
+ switch (reg_info->kinds[eRegisterKindGeneric]) {
+ case LLDB_REGNUM_GENERIC_PC:
+ if (m_cursor.pc == LLDB_INVALID_ADDRESS)
+ return false;
+ reg_value = m_cursor.pc;
+ break;
+
+ case LLDB_REGNUM_GENERIC_FP:
+ if (m_cursor.fp == LLDB_INVALID_ADDRESS)
+ return false;
+ reg_value = m_cursor.fp;
+ break;
+
+ default:
+ return false;
+ }
+
+ switch (reg_info->encoding) {
+ case eEncodingInvalid:
+ case eEncodingVector:
+ break;
+
+ case eEncodingUint:
+ case eEncodingSint:
+ value.SetUInt(reg_value, reg_info->byte_size);
+ return true;
+
+ case eEncodingIEEE754:
+ switch (reg_info->byte_size) {
+ case sizeof(float):
+ if (sizeof(float) == sizeof(uint32_t)) {
+ value.SetUInt32(reg_value, RegisterValue::eTypeFloat);
+ return true;
+ } else if (sizeof(float) == sizeof(uint64_t)) {
+ value.SetUInt64(reg_value, RegisterValue::eTypeFloat);
+ return true;
+ }
+ break;
+
+ case sizeof(double):
+ if (sizeof(double) == sizeof(uint32_t)) {
+ value.SetUInt32(reg_value, RegisterValue::eTypeDouble);
+ return true;
+ } else if (sizeof(double) == sizeof(uint64_t)) {
+ value.SetUInt64(reg_value, RegisterValue::eTypeDouble);
+ return true;
+ }
+ break;
+
+// TOOD: need a better way to detect when "long double" types are
+// the same bytes size as "double"
+#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) && \
+ !defined(_MSC_VER) && !defined(__mips__) && !defined(__powerpc__) && \
+ !defined(__ANDROID__)
+ case sizeof(long double):
+ if (sizeof(long double) == sizeof(uint32_t)) {
+ value.SetUInt32(reg_value, RegisterValue::eTypeLongDouble);
+ return true;
+ } else if (sizeof(long double) == sizeof(uint64_t)) {
+ value.SetUInt64(reg_value, RegisterValue::eTypeLongDouble);
+ return true;
+ }
+ break;
+#endif
+ }
+ break;
+ }
+ return false;
+}
+
+bool RegisterContextMacOSXFrameBackchain::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ // Not supported yet. We could easily add support for this by remembering the
+ // address of each entry (it would need to be part of the cursor)
+ return false;
+}
+
+bool RegisterContextMacOSXFrameBackchain::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ // libunwind frames can't handle this it doesn't always have all register
+ // values. This call should only be called on frame zero anyway so there
+ // shouldn't be any problem
+ return false;
+}
+
+bool RegisterContextMacOSXFrameBackchain::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ // Since this class doesn't respond to "ReadAllRegisterValues()", it must not
+ // have been the one that saved all the register values. So we just let the
+ // thread's register context (the register context for frame zero) do the
+ // writing.
+ return m_thread.GetRegisterContext()->WriteAllRegisterValues(data_sp);
+}
+
+uint32_t
+RegisterContextMacOSXFrameBackchain::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber(
+ kind, num);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h
new file mode 100644
index 000000000000..36e5538daa8a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h
@@ -0,0 +1,56 @@
+//===-- RegisterContextMacOSXFrameBackchain.h -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextMacOSXFrameBackchain_h_
+#define lldb_RegisterContextMacOSXFrameBackchain_h_
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+#include "UnwindMacOSXFrameBackchain.h"
+
+class RegisterContextMacOSXFrameBackchain
+ : public lldb_private::RegisterContext {
+public:
+ RegisterContextMacOSXFrameBackchain(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ const UnwindMacOSXFrameBackchain::Cursor &cursor);
+
+ ~RegisterContextMacOSXFrameBackchain() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+private:
+ UnwindMacOSXFrameBackchain::Cursor m_cursor;
+ bool m_cursor_is_valid;
+
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextMacOSXFrameBackchain);
+};
+
+#endif // lldb_RegisterContextMacOSXFrameBackchain_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp
new file mode 100644
index 000000000000..c7042ab5137a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp
@@ -0,0 +1,74 @@
+//===-- RegisterContextMach_arm.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+
+#include "RegisterContextMach_arm.h"
+
+#include <mach/mach_types.h>
+#include <mach/thread_act.h>
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextMach_arm::RegisterContextMach_arm(Thread &thread,
+ uint32_t concrete_frame_idx)
+ : RegisterContextDarwin_arm(thread, concrete_frame_idx) {}
+
+RegisterContextMach_arm::~RegisterContextMach_arm() {}
+
+int RegisterContextMach_arm::DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) {
+ mach_msg_type_number_t count = GPRWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count);
+}
+
+int RegisterContextMach_arm::DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) {
+ mach_msg_type_number_t count = FPUWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count);
+}
+
+int RegisterContextMach_arm::DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) {
+ mach_msg_type_number_t count = EXCWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count);
+}
+
+int RegisterContextMach_arm::DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) {
+ mach_msg_type_number_t count = DBGWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&dbg, &count);
+}
+
+int RegisterContextMach_arm::DoWriteGPR(lldb::tid_t tid, int flavor,
+ const GPR &gpr) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<GPR *>(&gpr)),
+ GPRWordCount);
+}
+
+int RegisterContextMach_arm::DoWriteFPU(lldb::tid_t tid, int flavor,
+ const FPU &fpu) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<FPU *>(&fpu)),
+ FPUWordCount);
+}
+
+int RegisterContextMach_arm::DoWriteEXC(lldb::tid_t tid, int flavor,
+ const EXC &exc) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<EXC *>(&exc)),
+ EXCWordCount);
+}
+
+int RegisterContextMach_arm::DoWriteDBG(lldb::tid_t tid, int flavor,
+ const DBG &dbg) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<DBG *>(&dbg)),
+ DBGWordCount);
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.h
new file mode 100644
index 000000000000..8b2425a193be
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.h
@@ -0,0 +1,40 @@
+//===-- RegisterContextMach_arm.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMach_arm_h_
+#define liblldb_RegisterContextMach_arm_h_
+
+
+#include "RegisterContextDarwin_arm.h"
+
+class RegisterContextMach_arm : public RegisterContextDarwin_arm {
+public:
+ RegisterContextMach_arm(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ virtual ~RegisterContextMach_arm();
+
+protected:
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr);
+
+ int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu);
+
+ int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc);
+
+ int DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg);
+
+ int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr);
+
+ int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu);
+
+ int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc);
+
+ int DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg);
+};
+
+#endif // liblldb_RegisterContextMach_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp
new file mode 100644
index 000000000000..e631ab9bb26c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp
@@ -0,0 +1,60 @@
+//===-- RegisterContextMach_i386.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+
+#include <mach/thread_act.h>
+
+#include "RegisterContextMach_i386.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextMach_i386::RegisterContextMach_i386(Thread &thread,
+ uint32_t concrete_frame_idx)
+ : RegisterContextDarwin_i386(thread, concrete_frame_idx) {}
+
+RegisterContextMach_i386::~RegisterContextMach_i386() {}
+
+int RegisterContextMach_i386::DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) {
+ mach_msg_type_number_t count = GPRWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count);
+}
+
+int RegisterContextMach_i386::DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) {
+ mach_msg_type_number_t count = FPUWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count);
+}
+
+int RegisterContextMach_i386::DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) {
+ mach_msg_type_number_t count = EXCWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count);
+}
+
+int RegisterContextMach_i386::DoWriteGPR(lldb::tid_t tid, int flavor,
+ const GPR &gpr) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<GPR *>(&gpr)),
+ GPRWordCount);
+}
+
+int RegisterContextMach_i386::DoWriteFPU(lldb::tid_t tid, int flavor,
+ const FPU &fpu) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<FPU *>(&fpu)),
+ FPUWordCount);
+}
+
+int RegisterContextMach_i386::DoWriteEXC(lldb::tid_t tid, int flavor,
+ const EXC &exc) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<EXC *>(&exc)),
+ EXCWordCount);
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.h
new file mode 100644
index 000000000000..b8835561e98c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.h
@@ -0,0 +1,35 @@
+//===-- RegisterContextMach_i386.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMach_i386_h_
+#define liblldb_RegisterContextMach_i386_h_
+
+#include "RegisterContextDarwin_i386.h"
+
+class RegisterContextMach_i386 : public RegisterContextDarwin_i386 {
+public:
+ RegisterContextMach_i386(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ virtual ~RegisterContextMach_i386();
+
+protected:
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr);
+
+ int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu);
+
+ int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc);
+
+ int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr);
+
+ int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu);
+
+ int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc);
+};
+
+#endif // liblldb_RegisterContextMach_i386_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp
new file mode 100644
index 000000000000..db17d7d88778
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp
@@ -0,0 +1,63 @@
+//===-- RegisterContextMach_x86_64.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+
+#include <mach/thread_act.h>
+
+#include "RegisterContextMach_x86_64.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextMach_x86_64::RegisterContextMach_x86_64(
+ Thread &thread, uint32_t concrete_frame_idx)
+ : RegisterContextDarwin_x86_64(thread, concrete_frame_idx) {}
+
+RegisterContextMach_x86_64::~RegisterContextMach_x86_64() {}
+
+int RegisterContextMach_x86_64::DoReadGPR(lldb::tid_t tid, int flavor,
+ GPR &gpr) {
+ mach_msg_type_number_t count = GPRWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count);
+}
+
+int RegisterContextMach_x86_64::DoReadFPU(lldb::tid_t tid, int flavor,
+ FPU &fpu) {
+ mach_msg_type_number_t count = FPUWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count);
+}
+
+int RegisterContextMach_x86_64::DoReadEXC(lldb::tid_t tid, int flavor,
+ EXC &exc) {
+ mach_msg_type_number_t count = EXCWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count);
+}
+
+int RegisterContextMach_x86_64::DoWriteGPR(lldb::tid_t tid, int flavor,
+ const GPR &gpr) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<GPR *>(&gpr)),
+ GPRWordCount);
+}
+
+int RegisterContextMach_x86_64::DoWriteFPU(lldb::tid_t tid, int flavor,
+ const FPU &fpu) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<FPU *>(&fpu)),
+ FPUWordCount);
+}
+
+int RegisterContextMach_x86_64::DoWriteEXC(lldb::tid_t tid, int flavor,
+ const EXC &exc) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<EXC *>(&exc)),
+ EXCWordCount);
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h
new file mode 100644
index 000000000000..688009aef8af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h
@@ -0,0 +1,36 @@
+//===-- RegisterContextMach_x86_64.h ------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMach_x86_64_h_
+#define liblldb_RegisterContextMach_x86_64_h_
+
+#include "RegisterContextDarwin_x86_64.h"
+
+class RegisterContextMach_x86_64 : public RegisterContextDarwin_x86_64 {
+public:
+ RegisterContextMach_x86_64(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ virtual ~RegisterContextMach_x86_64();
+
+protected:
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr);
+
+ int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu);
+
+ int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc);
+
+ int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr);
+
+ int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu);
+
+ int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc);
+};
+
+#endif // liblldb_RegisterContextMach_x86_64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.cpp
new file mode 100644
index 000000000000..946d4fa9f8e5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.cpp
@@ -0,0 +1,139 @@
+//===-- RegisterContextMemory.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextMemory.h"
+
+#include "DynamicRegisterInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// RegisterContextMemory constructor
+RegisterContextMemory::RegisterContextMemory(Thread &thread,
+ uint32_t concrete_frame_idx,
+ DynamicRegisterInfo &reg_infos,
+ addr_t reg_data_addr)
+ : RegisterContext(thread, concrete_frame_idx), m_reg_infos(reg_infos),
+ m_reg_valid(), m_reg_data(), m_reg_data_addr(reg_data_addr) {
+ // Resize our vector of bools to contain one bool for every register. We will
+ // use these boolean values to know when a register value is valid in
+ // m_reg_data.
+ const size_t num_regs = reg_infos.GetNumRegisters();
+ assert(num_regs > 0);
+ m_reg_valid.resize(num_regs);
+
+ // Make a heap based buffer that is big enough to store all registers
+ DataBufferSP reg_data_sp(
+ new DataBufferHeap(reg_infos.GetRegisterDataByteSize(), 0));
+ m_reg_data.SetData(reg_data_sp);
+}
+
+// Destructor
+RegisterContextMemory::~RegisterContextMemory() {}
+
+void RegisterContextMemory::InvalidateAllRegisters() {
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS)
+ SetAllRegisterValid(false);
+}
+
+void RegisterContextMemory::SetAllRegisterValid(bool b) {
+ std::vector<bool>::iterator pos, end = m_reg_valid.end();
+ for (pos = m_reg_valid.begin(); pos != end; ++pos)
+ *pos = b;
+}
+
+size_t RegisterContextMemory::GetRegisterCount() {
+ return m_reg_infos.GetNumRegisters();
+}
+
+const RegisterInfo *RegisterContextMemory::GetRegisterInfoAtIndex(size_t reg) {
+ return m_reg_infos.GetRegisterInfoAtIndex(reg);
+}
+
+size_t RegisterContextMemory::GetRegisterSetCount() {
+ return m_reg_infos.GetNumRegisterSets();
+}
+
+const RegisterSet *RegisterContextMemory::GetRegisterSet(size_t reg_set) {
+ return m_reg_infos.GetRegisterSet(reg_set);
+}
+
+uint32_t RegisterContextMemory::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ return m_reg_infos.ConvertRegisterKindToRegisterNumber(kind, num);
+}
+
+bool RegisterContextMemory::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB];
+ if (!m_reg_valid[reg_num]) {
+ if (!ReadAllRegisterValues(m_reg_data.GetSharedDataBuffer()))
+ return false;
+ }
+ const bool partial_data_ok = false;
+ return reg_value
+ .SetValueFromData(reg_info, m_reg_data, reg_info->byte_offset,
+ partial_data_ok)
+ .Success();
+}
+
+bool RegisterContextMemory::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS) {
+ const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB];
+ addr_t reg_addr = m_reg_data_addr + reg_info->byte_offset;
+ Status error(WriteRegisterValueToMemory(reg_info, reg_addr,
+ reg_info->byte_size, reg_value));
+ m_reg_valid[reg_num] = false;
+ return error.Success();
+ }
+ return false;
+}
+
+bool RegisterContextMemory::ReadAllRegisterValues(DataBufferSP &data_sp) {
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(CalculateProcess());
+ if (process_sp) {
+ Status error;
+ if (process_sp->ReadMemory(m_reg_data_addr, data_sp->GetBytes(),
+ data_sp->GetByteSize(),
+ error) == data_sp->GetByteSize()) {
+ SetAllRegisterValid(true);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool RegisterContextMemory::WriteAllRegisterValues(
+ const DataBufferSP &data_sp) {
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(CalculateProcess());
+ if (process_sp) {
+ Status error;
+ SetAllRegisterValid(false);
+ if (process_sp->WriteMemory(m_reg_data_addr, data_sp->GetBytes(),
+ data_sp->GetByteSize(),
+ error) == data_sp->GetByteSize())
+ return true;
+ }
+ }
+ return false;
+}
+
+void RegisterContextMemory::SetAllRegisterData(
+ const lldb::DataBufferSP &data_sp) {
+ m_reg_data.SetData(data_sp);
+ SetAllRegisterValid(true);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.h
new file mode 100644
index 000000000000..68223eaeffd7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.h
@@ -0,0 +1,73 @@
+//===-- RegisterContextMemory.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextMemory_h_
+#define lldb_RegisterContextMemory_h_
+
+#include <vector>
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/lldb-private.h"
+
+class DynamicRegisterInfo;
+
+class RegisterContextMemory : public lldb_private::RegisterContext {
+public:
+ RegisterContextMemory(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx,
+ DynamicRegisterInfo &reg_info,
+ lldb::addr_t reg_data_addr);
+
+ ~RegisterContextMemory() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ // If all of the thread register are in a contiguous buffer in
+ // memory, then the default ReadRegister/WriteRegister and
+ // ReadAllRegisterValues/WriteAllRegisterValues will work. If thread
+ // registers are not contiguous, clients will want to subclass this
+ // class and modify the read/write functions as needed.
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ void SetAllRegisterData(const lldb::DataBufferSP &data_sp);
+
+protected:
+ void SetAllRegisterValid(bool b);
+
+ DynamicRegisterInfo &m_reg_infos;
+ std::vector<bool> m_reg_valid;
+ lldb_private::DataExtractor m_reg_data;
+ lldb::addr_t m_reg_data_addr; // If this is valid, then we have a register
+ // context that is stored in memmory
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextMemory);
+};
+
+#endif // lldb_RegisterContextMemory_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp
new file mode 100644
index 000000000000..e620ff66c922
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp
@@ -0,0 +1,123 @@
+//===-- RegisterContextNetBSD_x86_64.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextNetBSD_x86_64.h"
+#include "RegisterContextPOSIX_x86.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Compiler.h"
+#include <cassert>
+#include <cstddef>
+
+using namespace lldb_private;
+using namespace lldb;
+
+// src/sys/arch/amd64/include/frame_regs.h
+typedef struct _GPR {
+ uint64_t rdi; /* 0 */
+ uint64_t rsi; /* 1 */
+ uint64_t rdx; /* 2 */
+ uint64_t rcx; /* 3 */
+ uint64_t r8; /* 4 */
+ uint64_t r9; /* 5 */
+ uint64_t r10; /* 6 */
+ uint64_t r11; /* 7 */
+ uint64_t r12; /* 8 */
+ uint64_t r13; /* 9 */
+ uint64_t r14; /* 10 */
+ uint64_t r15; /* 11 */
+ uint64_t rbp; /* 12 */
+ uint64_t rbx; /* 13 */
+ uint64_t rax; /* 14 */
+ uint64_t gs; /* 15 */
+ uint64_t fs; /* 16 */
+ uint64_t es; /* 17 */
+ uint64_t ds; /* 18 */
+ uint64_t trapno; /* 19 */
+ uint64_t err; /* 20 */
+ uint64_t rip; /* 21 */
+ uint64_t cs; /* 22 */
+ uint64_t rflags; /* 23 */
+ uint64_t rsp; /* 24 */
+ uint64_t ss; /* 25 */
+} GPR;
+
+struct DBG {
+ uint64_t dr[16]; /* debug registers */
+ /* Index 0-3: debug address registers */
+ /* Index 4-5: reserved */
+ /* Index 6: debug status */
+ /* Index 7: debug control */
+ /* Index 8-15: reserved */
+};
+
+/*
+ * src/sys/arch/amd64/include/mcontext.h
+ *
+ * typedef struct {
+ * __gregset_t __gregs;
+ * __greg_t _mc_tlsbase;
+ * __fpregset_t __fpregs;
+ * } mcontext_t;
+ */
+
+struct UserArea {
+ GPR gpr;
+ uint64_t mc_tlsbase;
+ FPR fpr;
+ DBG dbg;
+};
+
+#define DR_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, dbg) + \
+ LLVM_EXTENSION offsetof(DBG, dr[reg_index]))
+
+
+// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64
+// structure.
+#define DECLARE_REGISTER_INFOS_X86_64_STRUCT
+#include "RegisterInfos_x86_64.h"
+#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT
+
+static const RegisterInfo *
+PrivateGetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86_64:
+ return g_register_infos_x86_64;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+PrivateGetRegisterCount(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86_64:
+ return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) /
+ sizeof(g_register_infos_x86_64[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextNetBSD_x86_64::RegisterContextNetBSD_x86_64(
+ const ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(PrivateGetRegisterInfoPtr(target_arch)),
+ m_register_count(PrivateGetRegisterCount(target_arch)) {}
+
+size_t RegisterContextNetBSD_x86_64::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextNetBSD_x86_64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextNetBSD_x86_64::GetRegisterCount() const {
+ return m_register_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h
new file mode 100644
index 000000000000..4820ef8d17ba
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h
@@ -0,0 +1,30 @@
+//===-- RegisterContextNetBSD_x86_64.h -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextNetBSD_x86_64_H_
+#define liblldb_RegisterContextNetBSD_x86_64_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextNetBSD_x86_64
+ : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextNetBSD_x86_64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ const uint32_t m_register_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.cpp
new file mode 100644
index 000000000000..06eac6f7f991
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.cpp
@@ -0,0 +1,77 @@
+//===-- RegisterContextOpenBSD_i386.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextOpenBSD_i386.h"
+#include "RegisterContextPOSIX_x86.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// /usr/include/machine/reg.h
+struct GPR {
+ uint32_t eax;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t ebx;
+ uint32_t esp;
+ uint32_t ebp;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t eip;
+ uint32_t eflags;
+ uint32_t cs;
+ uint32_t ss;
+ uint32_t ds;
+ uint32_t es;
+ uint32_t fs;
+ uint32_t gs;
+};
+
+struct dbreg {
+ uint32_t dr[8]; /* debug registers */
+ /* Index 0-3: debug address registers */
+ /* Index 4-5: reserved */
+ /* Index 6: debug status */
+ /* Index 7: debug control */
+};
+
+using FPR_i386 = FXSAVE;
+
+struct UserArea {
+ GPR gpr;
+ FPR_i386 i387;
+};
+
+#define DR_SIZE sizeof(uint32_t)
+#define DR_OFFSET(reg_index) (LLVM_EXTENSION offsetof(dbreg, dr[reg_index]))
+
+// Include RegisterInfos_i386 to declare our g_register_infos_i386 structure.
+#define DECLARE_REGISTER_INFOS_I386_STRUCT
+#include "RegisterInfos_i386.h"
+#undef DECLARE_REGISTER_INFOS_I386_STRUCT
+
+RegisterContextOpenBSD_i386::RegisterContextOpenBSD_i386(
+ const ArchSpec &target_arch)
+ : RegisterInfoInterface(target_arch) {}
+
+size_t RegisterContextOpenBSD_i386::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextOpenBSD_i386::GetRegisterInfo() const {
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return g_register_infos_i386;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+uint32_t RegisterContextOpenBSD_i386::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_i386) /
+ sizeof(g_register_infos_i386[0]));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.h
new file mode 100644
index 000000000000..992ce0959fdf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.h
@@ -0,0 +1,25 @@
+//===-- RegisterContextOpenBSD_i386.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextOpenBSD_i386_H_
+#define liblldb_RegisterContextOpenBSD_i386_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextOpenBSD_i386 : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextOpenBSD_i386(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.cpp
new file mode 100644
index 000000000000..e210196d921d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.cpp
@@ -0,0 +1,104 @@
+//===-- RegisterContextOpenBSD_x86_64.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextOpenBSD_x86_64.h"
+#include "RegisterContextPOSIX_x86.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb;
+
+// /usr/include/machine/reg.h
+typedef struct _GPR {
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rdx;
+ uint64_t rcx;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t rax;
+ uint64_t rsp;
+ uint64_t rip;
+ uint64_t rflags;
+ uint64_t cs;
+ uint64_t ss;
+ uint64_t ds;
+ uint64_t es;
+ uint64_t fs;
+ uint64_t gs;
+} GPR;
+
+struct DBG {
+ uint64_t dr[16]; /* debug registers */
+ /* Index 0-3: debug address registers */
+ /* Index 4-5: reserved */
+ /* Index 6: debug status */
+ /* Index 7: debug control */
+ /* Index 8-15: reserved */
+};
+
+struct UserArea {
+ GPR gpr;
+ FPR fpr;
+ DBG dbg;
+};
+
+#define DR_OFFSET(reg_index) (LLVM_EXTENSION offsetof(DBG, dr[reg_index]))
+
+// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64
+// structure.
+#define DECLARE_REGISTER_INFOS_X86_64_STRUCT
+#include "RegisterInfos_x86_64.h"
+#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT
+
+static const RegisterInfo *
+PrivateGetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86_64:
+ return g_register_infos_x86_64;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+PrivateGetRegisterCount(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86_64:
+ return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) /
+ sizeof(g_register_infos_x86_64[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextOpenBSD_x86_64::RegisterContextOpenBSD_x86_64(
+ const ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(PrivateGetRegisterInfoPtr(target_arch)),
+ m_register_count(PrivateGetRegisterCount(target_arch)) {}
+
+size_t RegisterContextOpenBSD_x86_64::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextOpenBSD_x86_64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextOpenBSD_x86_64::GetRegisterCount() const {
+ return m_register_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h
new file mode 100644
index 000000000000..9c76e7211132
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h
@@ -0,0 +1,30 @@
+//===-- RegisterContextOpenBSD_x86_64.h -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextOpenBSD_x86_64_H_
+#define liblldb_RegisterContextOpenBSD_x86_64_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextOpenBSD_x86_64
+ : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextOpenBSD_x86_64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ const uint32_t m_register_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp
new file mode 100644
index 000000000000..821e2aa73b5b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp
@@ -0,0 +1,212 @@
+//===-- RegisterContextPOSIX_arm.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_arm.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// arm general purpose registers.
+const uint32_t g_gpr_regnums_arm[] = {
+ gpr_r0_arm, gpr_r1_arm, gpr_r2_arm, gpr_r3_arm, gpr_r4_arm,
+ gpr_r5_arm, gpr_r6_arm, gpr_r7_arm, gpr_r8_arm, gpr_r9_arm,
+ gpr_r10_arm, gpr_r11_arm, gpr_r12_arm, gpr_sp_arm, gpr_lr_arm,
+ gpr_pc_arm, gpr_cpsr_arm,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+
+};
+static_assert(((sizeof g_gpr_regnums_arm / sizeof g_gpr_regnums_arm[0]) - 1) ==
+ k_num_gpr_registers_arm,
+ "g_gpr_regnums_arm has wrong number of register infos");
+
+// arm floating point registers.
+static const uint32_t g_fpu_regnums_arm[] = {
+ fpu_s0_arm, fpu_s1_arm, fpu_s2_arm, fpu_s3_arm, fpu_s4_arm,
+ fpu_s5_arm, fpu_s6_arm, fpu_s7_arm, fpu_s8_arm, fpu_s9_arm,
+ fpu_s10_arm, fpu_s11_arm, fpu_s12_arm, fpu_s13_arm, fpu_s14_arm,
+ fpu_s15_arm, fpu_s16_arm, fpu_s17_arm, fpu_s18_arm, fpu_s19_arm,
+ fpu_s20_arm, fpu_s21_arm, fpu_s22_arm, fpu_s23_arm, fpu_s24_arm,
+ fpu_s25_arm, fpu_s26_arm, fpu_s27_arm, fpu_s28_arm, fpu_s29_arm,
+ fpu_s30_arm, fpu_s31_arm, fpu_fpscr_arm, fpu_d0_arm, fpu_d1_arm,
+ fpu_d2_arm, fpu_d3_arm, fpu_d4_arm, fpu_d5_arm, fpu_d6_arm,
+ fpu_d7_arm, fpu_d8_arm, fpu_d9_arm, fpu_d10_arm, fpu_d11_arm,
+ fpu_d12_arm, fpu_d13_arm, fpu_d14_arm, fpu_d15_arm, fpu_d16_arm,
+ fpu_d17_arm, fpu_d18_arm, fpu_d19_arm, fpu_d20_arm, fpu_d21_arm,
+ fpu_d22_arm, fpu_d23_arm, fpu_d24_arm, fpu_d25_arm, fpu_d26_arm,
+ fpu_d27_arm, fpu_d28_arm, fpu_d29_arm, fpu_d30_arm, fpu_d31_arm,
+ fpu_q0_arm, fpu_q1_arm, fpu_q2_arm, fpu_q3_arm, fpu_q4_arm,
+ fpu_q5_arm, fpu_q6_arm, fpu_q7_arm, fpu_q8_arm, fpu_q9_arm,
+ fpu_q10_arm, fpu_q11_arm, fpu_q12_arm, fpu_q13_arm, fpu_q14_arm,
+ fpu_q15_arm,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+
+};
+static_assert(((sizeof g_fpu_regnums_arm / sizeof g_fpu_regnums_arm[0]) - 1) ==
+ k_num_fpr_registers_arm,
+ "g_fpu_regnums_arm has wrong number of register infos");
+
+// Number of register sets provided by this context.
+enum { k_num_register_sets = 2 };
+
+// Register sets for arm.
+static const lldb_private::RegisterSet g_reg_sets_arm[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_arm,
+ g_gpr_regnums_arm},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_arm,
+ g_fpu_regnums_arm}};
+
+bool RegisterContextPOSIX_arm::IsGPR(unsigned reg) {
+ return reg <= m_reg_info.last_gpr; // GPR's come first.
+}
+
+bool RegisterContextPOSIX_arm::IsFPR(unsigned reg) {
+ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
+}
+
+RegisterContextPOSIX_arm::RegisterContextPOSIX_arm(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : lldb_private::RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+
+ switch (register_info->m_target_arch.GetMachine()) {
+ case llvm::Triple::arm:
+ m_reg_info.num_registers = k_num_registers_arm;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_arm;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_arm;
+ m_reg_info.last_gpr = k_last_gpr_arm;
+ m_reg_info.first_fpr = k_first_fpr_arm;
+ m_reg_info.last_fpr = k_last_fpr_arm;
+ m_reg_info.first_fpr_v = fpu_s0_arm;
+ m_reg_info.last_fpr_v = fpu_s31_arm;
+ m_reg_info.gpr_flags = gpr_cpsr_arm;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+
+ ::memset(&m_fpr, 0, sizeof m_fpr);
+}
+
+RegisterContextPOSIX_arm::~RegisterContextPOSIX_arm() {}
+
+void RegisterContextPOSIX_arm::Invalidate() {}
+
+void RegisterContextPOSIX_arm::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_arm::GetRegisterOffset(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_arm::GetRegisterSize(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_arm::GetRegisterCount() {
+ size_t num_registers =
+ m_reg_info.num_gpr_registers + m_reg_info.num_fpr_registers;
+ return num_registers;
+}
+
+size_t RegisterContextPOSIX_arm::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+const lldb_private::RegisterInfo *RegisterContextPOSIX_arm::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const lldb_private::RegisterInfo *
+RegisterContextPOSIX_arm::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < m_reg_info.num_registers)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_arm::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const lldb_private::RegisterSet *
+RegisterContextPOSIX_arm::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set)) {
+ switch (m_register_info_up->m_target_arch.GetMachine()) {
+ case llvm::Triple::arm:
+ return &g_reg_sets_arm[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+const char *RegisterContextPOSIX_arm::GetRegisterName(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_arm::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = lldb::eByteOrderInvalid;
+ lldb_private::Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+bool RegisterContextPOSIX_arm::IsRegisterSetAvailable(size_t set_index) {
+ return set_index < k_num_register_sets;
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_arm::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < lldb::kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const lldb_private::RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h
new file mode 100644
index 000000000000..603ba76430e6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h
@@ -0,0 +1,106 @@
+//===-- RegisterContextPOSIX_arm.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIX_arm_h_
+#define liblldb_RegisterContextPOSIX_arm_h_
+
+#include "RegisterInfoInterface.h"
+#include "lldb-arm-register-enums.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class ProcessMonitor;
+
+class RegisterContextPOSIX_arm : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_arm(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_arm() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ struct RegInfo {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+
+ uint32_t first_fpr_v;
+ uint32_t last_fpr_v;
+
+ uint32_t gpr_flags;
+ };
+
+ struct QReg {
+ uint8_t bytes[16];
+ };
+
+ struct FPU {
+ union {
+ uint32_t s[32];
+ uint64_t d[32];
+ QReg q[16]; // the 128-bit NEON registers
+ } floats;
+ uint32_t fpscr;
+ };
+
+ uint32_t m_gpr_arm[lldb_private::k_num_gpr_registers_arm]; // 32-bit general
+ // purpose
+ // registers.
+ RegInfo m_reg_info;
+ struct RegisterContextPOSIX_arm::FPU
+ m_fpr; // floating-point registers including extended register sets.
+ std::unique_ptr<lldb_private::RegisterInfoInterface>
+ m_register_info_up; // Register Info Interface (FreeBSD or Linux)
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp
new file mode 100644
index 000000000000..99b897d441b5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp
@@ -0,0 +1,232 @@
+//===-- RegisterContextPOSIX_arm64.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_arm64.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ARM64 general purpose registers.
+const uint32_t g_gpr_regnums_arm64[] = {
+ gpr_x0_arm64, gpr_x1_arm64, gpr_x2_arm64, gpr_x3_arm64,
+ gpr_x4_arm64, gpr_x5_arm64, gpr_x6_arm64, gpr_x7_arm64,
+ gpr_x8_arm64, gpr_x9_arm64, gpr_x10_arm64, gpr_x11_arm64,
+ gpr_x12_arm64, gpr_x13_arm64, gpr_x14_arm64, gpr_x15_arm64,
+ gpr_x16_arm64, gpr_x17_arm64, gpr_x18_arm64, gpr_x19_arm64,
+ gpr_x20_arm64, gpr_x21_arm64, gpr_x22_arm64, gpr_x23_arm64,
+ gpr_x24_arm64, gpr_x25_arm64, gpr_x26_arm64, gpr_x27_arm64,
+ gpr_x28_arm64, gpr_fp_arm64, gpr_lr_arm64, gpr_sp_arm64,
+ gpr_pc_arm64, gpr_cpsr_arm64, gpr_w0_arm64, gpr_w1_arm64,
+ gpr_w2_arm64, gpr_w3_arm64, gpr_w4_arm64, gpr_w5_arm64,
+ gpr_w6_arm64, gpr_w7_arm64, gpr_w8_arm64, gpr_w9_arm64,
+ gpr_w10_arm64, gpr_w11_arm64, gpr_w12_arm64, gpr_w13_arm64,
+ gpr_w14_arm64, gpr_w15_arm64, gpr_w16_arm64, gpr_w17_arm64,
+ gpr_w18_arm64, gpr_w19_arm64, gpr_w20_arm64, gpr_w21_arm64,
+ gpr_w22_arm64, gpr_w23_arm64, gpr_w24_arm64, gpr_w25_arm64,
+ gpr_w26_arm64, gpr_w27_arm64, gpr_w28_arm64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert(((sizeof g_gpr_regnums_arm64 / sizeof g_gpr_regnums_arm64[0]) -
+ 1) == k_num_gpr_registers_arm64,
+ "g_gpr_regnums_arm64 has wrong number of register infos");
+
+// ARM64 floating point registers.
+static const uint32_t g_fpu_regnums_arm64[] = {
+ fpu_v0_arm64, fpu_v1_arm64, fpu_v2_arm64, fpu_v3_arm64,
+ fpu_v4_arm64, fpu_v5_arm64, fpu_v6_arm64, fpu_v7_arm64,
+ fpu_v8_arm64, fpu_v9_arm64, fpu_v10_arm64, fpu_v11_arm64,
+ fpu_v12_arm64, fpu_v13_arm64, fpu_v14_arm64, fpu_v15_arm64,
+ fpu_v16_arm64, fpu_v17_arm64, fpu_v18_arm64, fpu_v19_arm64,
+ fpu_v20_arm64, fpu_v21_arm64, fpu_v22_arm64, fpu_v23_arm64,
+ fpu_v24_arm64, fpu_v25_arm64, fpu_v26_arm64, fpu_v27_arm64,
+ fpu_v28_arm64, fpu_v29_arm64, fpu_v30_arm64, fpu_v31_arm64,
+ fpu_s0_arm64, fpu_s1_arm64, fpu_s2_arm64, fpu_s3_arm64,
+ fpu_s4_arm64, fpu_s5_arm64, fpu_s6_arm64, fpu_s7_arm64,
+ fpu_s8_arm64, fpu_s9_arm64, fpu_s10_arm64, fpu_s11_arm64,
+ fpu_s12_arm64, fpu_s13_arm64, fpu_s14_arm64, fpu_s15_arm64,
+ fpu_s16_arm64, fpu_s17_arm64, fpu_s18_arm64, fpu_s19_arm64,
+ fpu_s20_arm64, fpu_s21_arm64, fpu_s22_arm64, fpu_s23_arm64,
+ fpu_s24_arm64, fpu_s25_arm64, fpu_s26_arm64, fpu_s27_arm64,
+ fpu_s28_arm64, fpu_s29_arm64, fpu_s30_arm64, fpu_s31_arm64,
+
+ fpu_d0_arm64, fpu_d1_arm64, fpu_d2_arm64, fpu_d3_arm64,
+ fpu_d4_arm64, fpu_d5_arm64, fpu_d6_arm64, fpu_d7_arm64,
+ fpu_d8_arm64, fpu_d9_arm64, fpu_d10_arm64, fpu_d11_arm64,
+ fpu_d12_arm64, fpu_d13_arm64, fpu_d14_arm64, fpu_d15_arm64,
+ fpu_d16_arm64, fpu_d17_arm64, fpu_d18_arm64, fpu_d19_arm64,
+ fpu_d20_arm64, fpu_d21_arm64, fpu_d22_arm64, fpu_d23_arm64,
+ fpu_d24_arm64, fpu_d25_arm64, fpu_d26_arm64, fpu_d27_arm64,
+ fpu_d28_arm64, fpu_d29_arm64, fpu_d30_arm64, fpu_d31_arm64,
+ fpu_fpsr_arm64, fpu_fpcr_arm64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert(((sizeof g_fpu_regnums_arm64 / sizeof g_fpu_regnums_arm64[0]) -
+ 1) == k_num_fpr_registers_arm64,
+ "g_fpu_regnums_arm64 has wrong number of register infos");
+
+// Number of register sets provided by this context.
+enum { k_num_register_sets = 2 };
+
+// Register sets for ARM64.
+static const lldb_private::RegisterSet g_reg_sets_arm64[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_arm64,
+ g_gpr_regnums_arm64},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_arm64,
+ g_fpu_regnums_arm64}};
+
+bool RegisterContextPOSIX_arm64::IsGPR(unsigned reg) {
+ return reg <= m_reg_info.last_gpr; // GPR's come first.
+}
+
+bool RegisterContextPOSIX_arm64::IsFPR(unsigned reg) {
+ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
+}
+
+RegisterContextPOSIX_arm64::RegisterContextPOSIX_arm64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : lldb_private::RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+
+ switch (register_info->m_target_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ m_reg_info.num_registers = k_num_registers_arm64;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_arm64;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_arm64;
+ m_reg_info.last_gpr = k_last_gpr_arm64;
+ m_reg_info.first_fpr = k_first_fpr_arm64;
+ m_reg_info.last_fpr = k_last_fpr_arm64;
+ m_reg_info.first_fpr_v = fpu_v0_arm64;
+ m_reg_info.last_fpr_v = fpu_v31_arm64;
+ m_reg_info.gpr_flags = gpr_cpsr_arm64;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+
+ ::memset(&m_fpr, 0, sizeof m_fpr);
+}
+
+RegisterContextPOSIX_arm64::~RegisterContextPOSIX_arm64() {}
+
+void RegisterContextPOSIX_arm64::Invalidate() {}
+
+void RegisterContextPOSIX_arm64::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_arm64::GetRegisterOffset(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_arm64::GetRegisterSize(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_arm64::GetRegisterCount() {
+ size_t num_registers =
+ m_reg_info.num_gpr_registers + m_reg_info.num_fpr_registers;
+ return num_registers;
+}
+
+size_t RegisterContextPOSIX_arm64::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+const lldb_private::RegisterInfo *
+RegisterContextPOSIX_arm64::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const lldb_private::RegisterInfo *
+RegisterContextPOSIX_arm64::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < m_reg_info.num_registers)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_arm64::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const lldb_private::RegisterSet *
+RegisterContextPOSIX_arm64::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set)) {
+ switch (m_register_info_up->m_target_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ return &g_reg_sets_arm64[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+const char *RegisterContextPOSIX_arm64::GetRegisterName(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_arm64::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = lldb::eByteOrderInvalid;
+ lldb_private::Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+bool RegisterContextPOSIX_arm64::IsRegisterSetAvailable(size_t set_index) {
+ return set_index < k_num_register_sets;
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_arm64::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < lldb::kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const lldb_private::RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h
new file mode 100644
index 000000000000..49a49b69da6b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h
@@ -0,0 +1,106 @@
+//===-- RegisterContextPOSIX_arm64.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIX_arm64_h_
+#define liblldb_RegisterContextPOSIX_arm64_h_
+
+#include "RegisterInfoInterface.h"
+#include "lldb-arm64-register-enums.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class ProcessMonitor;
+
+class RegisterContextPOSIX_arm64 : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_arm64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_arm64() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ struct RegInfo {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+
+ uint32_t first_fpr_v;
+ uint32_t last_fpr_v;
+
+ uint32_t gpr_flags;
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct VReg {
+ uint8_t bytes[16];
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct FPU {
+ VReg v[32];
+ uint32_t fpsr;
+ uint32_t fpcr;
+ };
+
+ uint64_t m_gpr_arm64[lldb_private::k_num_gpr_registers_arm64]; // 64-bit
+ // general
+ // purpose
+ // registers.
+ RegInfo m_reg_info;
+ struct RegisterContextPOSIX_arm64::FPU
+ m_fpr; // floating-point registers including extended register sets.
+ std::unique_ptr<lldb_private::RegisterInfoInterface>
+ m_register_info_up; // Register Info Interface (FreeBSD or Linux)
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_arm64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.cpp
new file mode 100644
index 000000000000..f1fa3035b2ef
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.cpp
@@ -0,0 +1,184 @@
+//===-- RegisterContextPOSIX_mips64.cpp -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_mips64.h"
+#include "RegisterContextFreeBSD_mips64.h"
+#include "RegisterContextLinux_mips64.h"
+#include "RegisterContextLinux_mips.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+bool RegisterContextPOSIX_mips64::IsGPR(unsigned reg) {
+ return reg < m_registers_count[gpr_registers_count]; // GPR's come first.
+}
+
+bool RegisterContextPOSIX_mips64::IsFPR(unsigned reg) {
+ int set = GetRegisterSetCount();
+ if (set > 1)
+ return reg < (m_registers_count[fpr_registers_count]
+ + m_registers_count[gpr_registers_count]);
+ return false;
+}
+
+RegisterContextPOSIX_mips64::RegisterContextPOSIX_mips64(
+ Thread &thread, uint32_t concrete_frame_idx,
+ RegisterInfoInterface *register_info)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+ m_num_registers = GetRegisterCount();
+ int set = GetRegisterSetCount();
+
+ const RegisterSet *reg_set_ptr;
+ for(int i = 0; i < set; ++i) {
+ reg_set_ptr = GetRegisterSet(i);
+ m_registers_count[i] = reg_set_ptr->num_registers;
+ }
+
+ assert(m_num_registers ==
+ static_cast<uint32_t>(m_registers_count[gpr_registers_count] +
+ m_registers_count[fpr_registers_count] +
+ m_registers_count[msa_registers_count]));
+}
+
+RegisterContextPOSIX_mips64::~RegisterContextPOSIX_mips64() {}
+
+void RegisterContextPOSIX_mips64::Invalidate() {}
+
+void RegisterContextPOSIX_mips64::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_mips64::GetRegisterOffset(unsigned reg) {
+ assert(reg < m_num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_mips64::GetRegisterSize(unsigned reg) {
+ assert(reg < m_num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_mips64::GetRegisterCount() {
+ return m_register_info_up->GetRegisterCount();
+}
+
+size_t RegisterContextPOSIX_mips64::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+const RegisterInfo *RegisterContextPOSIX_mips64::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const RegisterInfo *
+RegisterContextPOSIX_mips64::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < m_num_registers)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_mips64::GetRegisterSetCount() {
+ ArchSpec target_arch = m_register_info_up->GetTargetArchitecture();
+ switch (target_arch.GetTriple().getOS()) {
+ case llvm::Triple::Linux: {
+ if ((target_arch.GetMachine() == llvm::Triple::mipsel) ||
+ (target_arch.GetMachine() == llvm::Triple::mips)) {
+ const auto *context = static_cast<const RegisterContextLinux_mips *>(
+ m_register_info_up.get());
+ return context->GetRegisterSetCount();
+ }
+ const auto *context = static_cast<const RegisterContextLinux_mips64 *>(
+ m_register_info_up.get());
+ return context->GetRegisterSetCount();
+ }
+ default: {
+ const auto *context = static_cast<const RegisterContextFreeBSD_mips64 *>(
+ m_register_info_up.get());
+ return context->GetRegisterSetCount();
+ }
+
+ }
+}
+
+const RegisterSet *RegisterContextPOSIX_mips64::GetRegisterSet(size_t set) {
+ ArchSpec target_arch = m_register_info_up->GetTargetArchitecture();
+ switch (target_arch.GetTriple().getOS()) {
+ case llvm::Triple::Linux: {
+ if ((target_arch.GetMachine() == llvm::Triple::mipsel) ||
+ (target_arch.GetMachine() == llvm::Triple::mips)) {
+ const auto *context = static_cast<const RegisterContextLinux_mips *>(
+ m_register_info_up.get());
+ return context->GetRegisterSet(set);
+ }
+ const auto *context = static_cast<const RegisterContextLinux_mips64 *>(
+ m_register_info_up.get());
+ return context->GetRegisterSet(set);
+ }
+ default: {
+ const auto *context = static_cast<const RegisterContextFreeBSD_mips64 *>(
+ m_register_info_up.get());
+ return context->GetRegisterSet(set);
+ }
+ }
+}
+
+const char *RegisterContextPOSIX_mips64::GetRegisterName(unsigned reg) {
+ assert(reg < m_num_registers && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_mips64::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = eByteOrderInvalid;
+ Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+bool RegisterContextPOSIX_mips64::IsRegisterSetAvailable(size_t set_index) {
+ size_t num_sets = GetRegisterSetCount();
+
+ return (set_index < num_sets);
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_mips64::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = m_num_registers;
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h
new file mode 100644
index 000000000000..c507e14bd5b6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h
@@ -0,0 +1,84 @@
+//===-- RegisterContextPOSIX_mips64.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIX_mips64_h_
+#define liblldb_RegisterContextPOSIX_mips64_h_
+
+#include "RegisterContext_mips.h"
+#include "RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb_private;
+
+class ProcessMonitor;
+
+class RegisterContextPOSIX_mips64 : public lldb_private::RegisterContext {
+public:
+
+ enum Register_count{
+ gpr_registers_count = 0,
+ fpr_registers_count,
+ msa_registers_count,
+ register_set_count
+ };
+
+ RegisterContextPOSIX_mips64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_mips64() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ uint32_t m_num_registers;
+ uint8_t m_registers_count[register_set_count];
+ std::unique_ptr<lldb_private::RegisterInfoInterface>
+ m_register_info_up; // Register Info Interface (FreeBSD or Linux)
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_mips64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp
new file mode 100644
index 000000000000..a78e9ed37947
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp
@@ -0,0 +1,193 @@
+//===-- RegisterContextPOSIX_powerpc.cpp -------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_powerpc.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+static const uint32_t g_gpr_regnums[] = {
+ gpr_r0_powerpc, gpr_r1_powerpc, gpr_r2_powerpc, gpr_r3_powerpc,
+ gpr_r4_powerpc, gpr_r5_powerpc, gpr_r6_powerpc, gpr_r7_powerpc,
+ gpr_r8_powerpc, gpr_r9_powerpc, gpr_r10_powerpc, gpr_r11_powerpc,
+ gpr_r12_powerpc, gpr_r13_powerpc, gpr_r14_powerpc, gpr_r15_powerpc,
+ gpr_r16_powerpc, gpr_r17_powerpc, gpr_r18_powerpc, gpr_r19_powerpc,
+ gpr_r20_powerpc, gpr_r21_powerpc, gpr_r22_powerpc, gpr_r23_powerpc,
+ gpr_r24_powerpc, gpr_r25_powerpc, gpr_r26_powerpc, gpr_r27_powerpc,
+ gpr_r28_powerpc, gpr_r29_powerpc, gpr_r30_powerpc, gpr_r31_powerpc,
+ gpr_lr_powerpc, gpr_cr_powerpc, gpr_xer_powerpc, gpr_ctr_powerpc,
+ gpr_pc_powerpc,
+};
+
+static const uint32_t g_fpr_regnums[] = {
+ fpr_f0_powerpc, fpr_f1_powerpc, fpr_f2_powerpc, fpr_f3_powerpc,
+ fpr_f4_powerpc, fpr_f5_powerpc, fpr_f6_powerpc, fpr_f7_powerpc,
+ fpr_f8_powerpc, fpr_f9_powerpc, fpr_f10_powerpc, fpr_f11_powerpc,
+ fpr_f12_powerpc, fpr_f13_powerpc, fpr_f14_powerpc, fpr_f15_powerpc,
+ fpr_f16_powerpc, fpr_f17_powerpc, fpr_f18_powerpc, fpr_f19_powerpc,
+ fpr_f20_powerpc, fpr_f21_powerpc, fpr_f22_powerpc, fpr_f23_powerpc,
+ fpr_f24_powerpc, fpr_f25_powerpc, fpr_f26_powerpc, fpr_f27_powerpc,
+ fpr_f28_powerpc, fpr_f29_powerpc, fpr_f30_powerpc, fpr_f31_powerpc,
+ fpr_fpscr_powerpc,
+};
+
+static const uint32_t g_vmx_regnums[] = {
+ vmx_v0_powerpc, vmx_v1_powerpc, vmx_v2_powerpc, vmx_v3_powerpc,
+ vmx_v4_powerpc, vmx_v5_powerpc, vmx_v6_powerpc, vmx_v7_powerpc,
+ vmx_v8_powerpc, vmx_v9_powerpc, vmx_v10_powerpc, vmx_v11_powerpc,
+ vmx_v12_powerpc, vmx_v13_powerpc, vmx_v14_powerpc, vmx_v15_powerpc,
+ vmx_v16_powerpc, vmx_v17_powerpc, vmx_v18_powerpc, vmx_v19_powerpc,
+ vmx_v20_powerpc, vmx_v21_powerpc, vmx_v22_powerpc, vmx_v23_powerpc,
+ vmx_v24_powerpc, vmx_v25_powerpc, vmx_v26_powerpc, vmx_v27_powerpc,
+ vmx_v28_powerpc, vmx_v29_powerpc, vmx_v30_powerpc, vmx_v31_powerpc,
+ vmx_vrsave_powerpc, vmx_vscr_powerpc,
+};
+
+// Number of register sets provided by this context.
+enum { k_num_register_sets = 3 };
+
+static const RegisterSet g_reg_sets_powerpc[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_powerpc,
+ g_gpr_regnums},
+ {"Floating Point Registers", "fpr", k_num_fpr_registers_powerpc,
+ g_fpr_regnums},
+ {"Altivec/VMX Registers", "vmx", k_num_vmx_registers_powerpc,
+ g_vmx_regnums},
+};
+
+static_assert(k_first_gpr_powerpc == 0,
+ "GPRs must index starting at 0, or fix IsGPR()");
+bool RegisterContextPOSIX_powerpc::IsGPR(unsigned reg) {
+ return (reg <= k_last_gpr_powerpc); // GPR's come first.
+}
+
+bool RegisterContextPOSIX_powerpc::IsFPR(unsigned reg) {
+ return (reg >= k_first_fpr) && (reg <= k_last_fpr);
+}
+
+bool RegisterContextPOSIX_powerpc::IsVMX(unsigned reg) {
+ return (reg >= k_first_vmx) && (reg <= k_last_vmx);
+}
+
+RegisterContextPOSIX_powerpc::RegisterContextPOSIX_powerpc(
+ Thread &thread, uint32_t concrete_frame_idx,
+ RegisterInfoInterface *register_info)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+}
+
+RegisterContextPOSIX_powerpc::~RegisterContextPOSIX_powerpc() {}
+
+void RegisterContextPOSIX_powerpc::Invalidate() {}
+
+void RegisterContextPOSIX_powerpc::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_powerpc::GetRegisterOffset(unsigned reg) {
+ assert(reg < k_num_registers_powerpc && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_powerpc::GetRegisterSize(unsigned reg) {
+ assert(reg < k_num_registers_powerpc && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_powerpc::GetRegisterCount() {
+ size_t num_registers = k_num_registers_powerpc;
+ return num_registers;
+}
+
+size_t RegisterContextPOSIX_powerpc::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+const RegisterInfo *RegisterContextPOSIX_powerpc::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const RegisterInfo *
+RegisterContextPOSIX_powerpc::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < k_num_registers_powerpc)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_powerpc::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const RegisterSet *RegisterContextPOSIX_powerpc::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set))
+ return &g_reg_sets_powerpc[set];
+ else
+ return nullptr;
+}
+
+const char *RegisterContextPOSIX_powerpc::GetRegisterName(unsigned reg) {
+ assert(reg < k_num_registers_powerpc && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_powerpc::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = eByteOrderInvalid;
+ Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+bool RegisterContextPOSIX_powerpc::IsRegisterSetAvailable(size_t set_index) {
+ size_t num_sets = k_num_register_sets;
+
+ return (set_index < num_sets);
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_powerpc::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h
new file mode 100644
index 000000000000..1a21a717b22b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h
@@ -0,0 +1,202 @@
+//===-- RegisterContextPOSIX_powerpc.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIX_powerpc_h_
+#define liblldb_RegisterContextPOSIX_powerpc_h_
+
+#include "RegisterContext_powerpc.h"
+#include "RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class ProcessMonitor;
+
+// Internal codes for all powerpc registers.
+enum {
+ k_first_gpr_powerpc,
+ gpr_r0_powerpc = k_first_gpr_powerpc,
+ gpr_r1_powerpc,
+ gpr_r2_powerpc,
+ gpr_r3_powerpc,
+ gpr_r4_powerpc,
+ gpr_r5_powerpc,
+ gpr_r6_powerpc,
+ gpr_r7_powerpc,
+ gpr_r8_powerpc,
+ gpr_r9_powerpc,
+ gpr_r10_powerpc,
+ gpr_r11_powerpc,
+ gpr_r12_powerpc,
+ gpr_r13_powerpc,
+ gpr_r14_powerpc,
+ gpr_r15_powerpc,
+ gpr_r16_powerpc,
+ gpr_r17_powerpc,
+ gpr_r18_powerpc,
+ gpr_r19_powerpc,
+ gpr_r20_powerpc,
+ gpr_r21_powerpc,
+ gpr_r22_powerpc,
+ gpr_r23_powerpc,
+ gpr_r24_powerpc,
+ gpr_r25_powerpc,
+ gpr_r26_powerpc,
+ gpr_r27_powerpc,
+ gpr_r28_powerpc,
+ gpr_r29_powerpc,
+ gpr_r30_powerpc,
+ gpr_r31_powerpc,
+ gpr_lr_powerpc,
+ gpr_cr_powerpc,
+ gpr_xer_powerpc,
+ gpr_ctr_powerpc,
+ gpr_pc_powerpc,
+ k_last_gpr_powerpc = gpr_pc_powerpc,
+
+ k_first_fpr,
+ fpr_f0_powerpc = k_first_fpr,
+ fpr_f1_powerpc,
+ fpr_f2_powerpc,
+ fpr_f3_powerpc,
+ fpr_f4_powerpc,
+ fpr_f5_powerpc,
+ fpr_f6_powerpc,
+ fpr_f7_powerpc,
+ fpr_f8_powerpc,
+ fpr_f9_powerpc,
+ fpr_f10_powerpc,
+ fpr_f11_powerpc,
+ fpr_f12_powerpc,
+ fpr_f13_powerpc,
+ fpr_f14_powerpc,
+ fpr_f15_powerpc,
+ fpr_f16_powerpc,
+ fpr_f17_powerpc,
+ fpr_f18_powerpc,
+ fpr_f19_powerpc,
+ fpr_f20_powerpc,
+ fpr_f21_powerpc,
+ fpr_f22_powerpc,
+ fpr_f23_powerpc,
+ fpr_f24_powerpc,
+ fpr_f25_powerpc,
+ fpr_f26_powerpc,
+ fpr_f27_powerpc,
+ fpr_f28_powerpc,
+ fpr_f29_powerpc,
+ fpr_f30_powerpc,
+ fpr_f31_powerpc,
+ fpr_fpscr_powerpc,
+ k_last_fpr = fpr_fpscr_powerpc,
+
+ k_first_vmx,
+ vmx_v0_powerpc = k_first_vmx,
+ vmx_v1_powerpc,
+ vmx_v2_powerpc,
+ vmx_v3_powerpc,
+ vmx_v4_powerpc,
+ vmx_v5_powerpc,
+ vmx_v6_powerpc,
+ vmx_v7_powerpc,
+ vmx_v8_powerpc,
+ vmx_v9_powerpc,
+ vmx_v10_powerpc,
+ vmx_v11_powerpc,
+ vmx_v12_powerpc,
+ vmx_v13_powerpc,
+ vmx_v14_powerpc,
+ vmx_v15_powerpc,
+ vmx_v16_powerpc,
+ vmx_v17_powerpc,
+ vmx_v18_powerpc,
+ vmx_v19_powerpc,
+ vmx_v20_powerpc,
+ vmx_v21_powerpc,
+ vmx_v22_powerpc,
+ vmx_v23_powerpc,
+ vmx_v24_powerpc,
+ vmx_v25_powerpc,
+ vmx_v26_powerpc,
+ vmx_v27_powerpc,
+ vmx_v28_powerpc,
+ vmx_v29_powerpc,
+ vmx_v30_powerpc,
+ vmx_v31_powerpc,
+ vmx_vrsave_powerpc,
+ vmx_vscr_powerpc,
+ k_last_vmx = vmx_vscr_powerpc,
+
+ k_num_registers_powerpc,
+ k_num_gpr_registers_powerpc = k_last_gpr_powerpc - k_first_gpr_powerpc + 1,
+ k_num_fpr_registers_powerpc = k_last_fpr - k_first_fpr + 1,
+ k_num_vmx_registers_powerpc = k_last_vmx - k_first_vmx + 1,
+};
+
+class RegisterContextPOSIX_powerpc : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_powerpc(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_powerpc() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ uint64_t
+ m_gpr_powerpc[k_num_gpr_registers_powerpc]; // general purpose registers.
+ uint64_t
+ m_fpr_powerpc[k_num_fpr_registers_powerpc]; // floating point registers.
+ uint32_t m_vmx_powerpc[k_num_vmx_registers_powerpc][4];
+ std::unique_ptr<lldb_private::RegisterInfoInterface>
+ m_register_info_up; // Register Info Interface (FreeBSD or Linux)
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ bool IsVMX(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool ReadVMX() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+ virtual bool WriteVMX() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_powerpc_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.cpp
new file mode 100644
index 000000000000..02546c0ed16f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.cpp
@@ -0,0 +1,211 @@
+//===-- RegisterContextPOSIX_ppc64le.cpp -------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_ppc64le.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+static const uint32_t g_gpr_regnums[] = {
+ gpr_r0_ppc64le, gpr_r1_ppc64le, gpr_r2_ppc64le, gpr_r3_ppc64le,
+ gpr_r4_ppc64le, gpr_r5_ppc64le, gpr_r6_ppc64le, gpr_r7_ppc64le,
+ gpr_r8_ppc64le, gpr_r9_ppc64le, gpr_r10_ppc64le, gpr_r11_ppc64le,
+ gpr_r12_ppc64le, gpr_r13_ppc64le, gpr_r14_ppc64le, gpr_r15_ppc64le,
+ gpr_r16_ppc64le, gpr_r17_ppc64le, gpr_r18_ppc64le, gpr_r19_ppc64le,
+ gpr_r20_ppc64le, gpr_r21_ppc64le, gpr_r22_ppc64le, gpr_r23_ppc64le,
+ gpr_r24_ppc64le, gpr_r25_ppc64le, gpr_r26_ppc64le, gpr_r27_ppc64le,
+ gpr_r28_ppc64le, gpr_r29_ppc64le, gpr_r30_ppc64le, gpr_r31_ppc64le,
+ gpr_pc_ppc64le, gpr_msr_ppc64le, gpr_origr3_ppc64le, gpr_ctr_ppc64le,
+ gpr_lr_ppc64le, gpr_xer_ppc64le, gpr_cr_ppc64le, gpr_softe_ppc64le,
+ gpr_trap_ppc64le,
+};
+
+static const uint32_t g_fpr_regnums[] = {
+ fpr_f0_ppc64le, fpr_f1_ppc64le, fpr_f2_ppc64le, fpr_f3_ppc64le,
+ fpr_f4_ppc64le, fpr_f5_ppc64le, fpr_f6_ppc64le, fpr_f7_ppc64le,
+ fpr_f8_ppc64le, fpr_f9_ppc64le, fpr_f10_ppc64le, fpr_f11_ppc64le,
+ fpr_f12_ppc64le, fpr_f13_ppc64le, fpr_f14_ppc64le, fpr_f15_ppc64le,
+ fpr_f16_ppc64le, fpr_f17_ppc64le, fpr_f18_ppc64le, fpr_f19_ppc64le,
+ fpr_f20_ppc64le, fpr_f21_ppc64le, fpr_f22_ppc64le, fpr_f23_ppc64le,
+ fpr_f24_ppc64le, fpr_f25_ppc64le, fpr_f26_ppc64le, fpr_f27_ppc64le,
+ fpr_f28_ppc64le, fpr_f29_ppc64le, fpr_f30_ppc64le, fpr_f31_ppc64le,
+ fpr_fpscr_ppc64le,
+};
+
+static const uint32_t g_vmx_regnums[] = {
+ vmx_vr0_ppc64le, vmx_vr1_ppc64le, vmx_vr2_ppc64le, vmx_vr3_ppc64le,
+ vmx_vr4_ppc64le, vmx_vr5_ppc64le, vmx_vr6_ppc64le, vmx_vr7_ppc64le,
+ vmx_vr8_ppc64le, vmx_vr9_ppc64le, vmx_vr10_ppc64le, vmx_vr11_ppc64le,
+ vmx_vr12_ppc64le, vmx_vr13_ppc64le, vmx_vr14_ppc64le, vmx_vr15_ppc64le,
+ vmx_vr16_ppc64le, vmx_vr17_ppc64le, vmx_vr18_ppc64le, vmx_vr19_ppc64le,
+ vmx_vr20_ppc64le, vmx_vr21_ppc64le, vmx_vr22_ppc64le, vmx_vr23_ppc64le,
+ vmx_vr24_ppc64le, vmx_vr25_ppc64le, vmx_vr26_ppc64le, vmx_vr27_ppc64le,
+ vmx_vr28_ppc64le, vmx_vr29_ppc64le, vmx_vr30_ppc64le, vmx_vr31_ppc64le,
+ vmx_vscr_ppc64le, vmx_vrsave_ppc64le,
+};
+
+static const uint32_t g_vsx_regnums[] = {
+ vsx_vs0_ppc64le, vsx_vs1_ppc64le, vsx_vs2_ppc64le, vsx_vs3_ppc64le,
+ vsx_vs4_ppc64le, vsx_vs5_ppc64le, vsx_vs6_ppc64le, vsx_vs7_ppc64le,
+ vsx_vs8_ppc64le, vsx_vs9_ppc64le, vsx_vs10_ppc64le, vsx_vs11_ppc64le,
+ vsx_vs12_ppc64le, vsx_vs13_ppc64le, vsx_vs14_ppc64le, vsx_vs15_ppc64le,
+ vsx_vs16_ppc64le, vsx_vs17_ppc64le, vsx_vs18_ppc64le, vsx_vs19_ppc64le,
+ vsx_vs20_ppc64le, vsx_vs21_ppc64le, vsx_vs22_ppc64le, vsx_vs23_ppc64le,
+ vsx_vs24_ppc64le, vsx_vs25_ppc64le, vsx_vs26_ppc64le, vsx_vs27_ppc64le,
+ vsx_vs28_ppc64le, vsx_vs29_ppc64le, vsx_vs30_ppc64le, vsx_vs31_ppc64le,
+ vsx_vs32_ppc64le, vsx_vs33_ppc64le, vsx_vs34_ppc64le, vsx_vs35_ppc64le,
+ vsx_vs36_ppc64le, vsx_vs37_ppc64le, vsx_vs38_ppc64le, vsx_vs39_ppc64le,
+ vsx_vs40_ppc64le, vsx_vs41_ppc64le, vsx_vs42_ppc64le, vsx_vs43_ppc64le,
+ vsx_vs44_ppc64le, vsx_vs45_ppc64le, vsx_vs46_ppc64le, vsx_vs47_ppc64le,
+ vsx_vs48_ppc64le, vsx_vs49_ppc64le, vsx_vs50_ppc64le, vsx_vs51_ppc64le,
+ vsx_vs52_ppc64le, vsx_vs53_ppc64le, vsx_vs54_ppc64le, vsx_vs55_ppc64le,
+ vsx_vs56_ppc64le, vsx_vs57_ppc64le, vsx_vs58_ppc64le, vsx_vs59_ppc64le,
+ vsx_vs60_ppc64le, vsx_vs61_ppc64le, vsx_vs62_ppc64le, vsx_vs63_ppc64le,
+};
+
+// Number of register sets provided by this context.
+enum { k_num_register_sets = 4 };
+
+static const RegisterSet g_reg_sets_ppc64le[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_ppc64le,
+ g_gpr_regnums},
+ {"Floating Point Registers", "fpr", k_num_fpr_registers_ppc64le,
+ g_fpr_regnums},
+ {"Altivec/VMX Registers", "vmx", k_num_vmx_registers_ppc64le,
+ g_vmx_regnums},
+ {"VSX Registers", "vsx", k_num_vsx_registers_ppc64le, g_vsx_regnums},
+};
+
+bool RegisterContextPOSIX_ppc64le::IsGPR(unsigned reg) {
+ return (reg <= k_last_gpr_ppc64le); // GPR's come first.
+}
+
+bool RegisterContextPOSIX_ppc64le::IsFPR(unsigned reg) {
+ return (reg >= k_first_fpr_ppc64le) && (reg <= k_last_fpr_ppc64le);
+}
+
+bool RegisterContextPOSIX_ppc64le::IsVMX(unsigned reg) {
+ return (reg >= k_first_vmx_ppc64le) && (reg <= k_last_vmx_ppc64le);
+}
+
+bool RegisterContextPOSIX_ppc64le::IsVSX(unsigned reg) {
+ return (reg >= k_first_vsx_ppc64le) && (reg <= k_last_vsx_ppc64le);
+}
+
+RegisterContextPOSIX_ppc64le::RegisterContextPOSIX_ppc64le(
+ Thread &thread, uint32_t concrete_frame_idx,
+ RegisterInfoInterface *register_info)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+}
+
+void RegisterContextPOSIX_ppc64le::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_ppc64le::GetRegisterOffset(unsigned reg) {
+ assert(reg < k_num_registers_ppc64le && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_ppc64le::GetRegisterSize(unsigned reg) {
+ assert(reg < k_num_registers_ppc64le && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_ppc64le::GetRegisterCount() {
+ size_t num_registers = k_num_registers_ppc64le;
+ return num_registers;
+}
+
+size_t RegisterContextPOSIX_ppc64le::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+const RegisterInfo *RegisterContextPOSIX_ppc64le::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const RegisterInfo *
+RegisterContextPOSIX_ppc64le::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < k_num_registers_ppc64le)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_ppc64le::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const RegisterSet *RegisterContextPOSIX_ppc64le::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set))
+ return &g_reg_sets_ppc64le[set];
+ else
+ return nullptr;
+}
+
+const char *RegisterContextPOSIX_ppc64le::GetRegisterName(unsigned reg) {
+ assert(reg < k_num_registers_ppc64le && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_ppc64le::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = eByteOrderInvalid;
+ Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+bool RegisterContextPOSIX_ppc64le::IsRegisterSetAvailable(size_t set_index) {
+ size_t num_sets = k_num_register_sets;
+
+ return (set_index < num_sets);
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_ppc64le::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h
new file mode 100644
index 000000000000..37079775a3c7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h
@@ -0,0 +1,77 @@
+//===-- RegisterContextPOSIX_ppc64le.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIX_ppc64le_h_
+#define liblldb_RegisterContextPOSIX_ppc64le_h_
+
+#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h"
+#include "RegisterInfoInterface.h"
+#include "Utility/PPC64LE_DWARF_Registers.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class RegisterContextPOSIX_ppc64le : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_ppc64le(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ // 64-bit general purpose registers.
+ uint64_t m_gpr_ppc64le[k_num_gpr_registers_ppc64le];
+
+ // floating-point registers including extended register.
+ uint64_t m_fpr_ppc64le[k_num_fpr_registers_ppc64le];
+
+ // VMX registers.
+ uint64_t m_vmx_ppc64le[k_num_vmx_registers_ppc64le * 2];
+
+ // VSX registers.
+ uint64_t m_vsx_ppc64le[k_num_vsx_registers_ppc64le * 2];
+
+ std::unique_ptr<lldb_private::RegisterInfoInterface> m_register_info_up;
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ bool IsVMX(unsigned reg);
+
+ bool IsVSX(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+};
+
+#endif // liblldb_RegisterContextPOSIX_ppc64le_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp
new file mode 100644
index 000000000000..e040e5075721
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp
@@ -0,0 +1,191 @@
+//===-- RegisterContextPOSIX_s390x.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_s390x.h"
+#include "RegisterContext_s390x.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// s390x 64-bit general purpose registers.
+static const uint32_t g_gpr_regnums_s390x[] = {
+ lldb_r0_s390x, lldb_r1_s390x, lldb_r2_s390x, lldb_r3_s390x,
+ lldb_r4_s390x, lldb_r5_s390x, lldb_r6_s390x, lldb_r7_s390x,
+ lldb_r8_s390x, lldb_r9_s390x, lldb_r10_s390x, lldb_r11_s390x,
+ lldb_r12_s390x, lldb_r13_s390x, lldb_r14_s390x, lldb_r15_s390x,
+ lldb_acr0_s390x, lldb_acr1_s390x, lldb_acr2_s390x, lldb_acr3_s390x,
+ lldb_acr4_s390x, lldb_acr5_s390x, lldb_acr6_s390x, lldb_acr7_s390x,
+ lldb_acr8_s390x, lldb_acr9_s390x, lldb_acr10_s390x, lldb_acr11_s390x,
+ lldb_acr12_s390x, lldb_acr13_s390x, lldb_acr14_s390x, lldb_acr15_s390x,
+ lldb_pswm_s390x, lldb_pswa_s390x,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert((sizeof(g_gpr_regnums_s390x) / sizeof(g_gpr_regnums_s390x[0])) -
+ 1 ==
+ k_num_gpr_registers_s390x,
+ "g_gpr_regnums_s390x has wrong number of register infos");
+
+// s390x 64-bit floating point registers.
+static const uint32_t g_fpu_regnums_s390x[] = {
+ lldb_f0_s390x, lldb_f1_s390x, lldb_f2_s390x, lldb_f3_s390x,
+ lldb_f4_s390x, lldb_f5_s390x, lldb_f6_s390x, lldb_f7_s390x,
+ lldb_f8_s390x, lldb_f9_s390x, lldb_f10_s390x, lldb_f11_s390x,
+ lldb_f12_s390x, lldb_f13_s390x, lldb_f14_s390x, lldb_f15_s390x,
+ lldb_fpc_s390x,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert((sizeof(g_fpu_regnums_s390x) / sizeof(g_fpu_regnums_s390x[0])) -
+ 1 ==
+ k_num_fpr_registers_s390x,
+ "g_fpu_regnums_s390x has wrong number of register infos");
+
+// Number of register sets provided by this context.
+enum { k_num_register_sets = 2 };
+
+// Register sets for s390x 64-bit.
+static const RegisterSet g_reg_sets_s390x[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_s390x,
+ g_gpr_regnums_s390x},
+ {"Floating Point Registers", "fpr", k_num_fpr_registers_s390x,
+ g_fpu_regnums_s390x},
+};
+
+bool RegisterContextPOSIX_s390x::IsGPR(unsigned reg) {
+ return reg <= m_reg_info.last_gpr; // GPRs come first.
+}
+
+bool RegisterContextPOSIX_s390x::IsFPR(unsigned reg) {
+ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
+}
+
+RegisterContextPOSIX_s390x::RegisterContextPOSIX_s390x(
+ Thread &thread, uint32_t concrete_frame_idx,
+ RegisterInfoInterface *register_info)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+
+ switch (register_info->m_target_arch.GetMachine()) {
+ case llvm::Triple::systemz:
+ m_reg_info.num_registers = k_num_registers_s390x;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_s390x;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_s390x;
+ m_reg_info.last_gpr = k_last_gpr_s390x;
+ m_reg_info.first_fpr = k_first_fpr_s390x;
+ m_reg_info.last_fpr = k_last_fpr_s390x;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+}
+
+RegisterContextPOSIX_s390x::~RegisterContextPOSIX_s390x() {}
+
+void RegisterContextPOSIX_s390x::Invalidate() {}
+
+void RegisterContextPOSIX_s390x::InvalidateAllRegisters() {}
+
+const RegisterInfo *RegisterContextPOSIX_s390x::GetRegisterInfo() {
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const RegisterInfo *
+RegisterContextPOSIX_s390x::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < m_reg_info.num_registers)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_s390x::GetRegisterCount() {
+ return m_reg_info.num_registers;
+}
+
+unsigned RegisterContextPOSIX_s390x::GetRegisterOffset(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_s390x::GetRegisterSize(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+const char *RegisterContextPOSIX_s390x::GetRegisterName(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+bool RegisterContextPOSIX_s390x::IsRegisterSetAvailable(size_t set_index) {
+ return set_index < k_num_register_sets;
+}
+
+size_t RegisterContextPOSIX_s390x::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const RegisterSet *RegisterContextPOSIX_s390x::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set)) {
+ switch (m_register_info_up->m_target_arch.GetMachine()) {
+ case llvm::Triple::systemz:
+ return &g_reg_sets_s390x[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_s390x::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = eByteOrderInvalid;
+ Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_s390x::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h
new file mode 100644
index 000000000000..54993ce6c3ec
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h
@@ -0,0 +1,79 @@
+//===-- RegisterContextPOSIX_s390x.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIX_s390x_h_
+#define liblldb_RegisterContextPOSIX_s390x_h_
+
+#include "RegisterContext_s390x.h"
+#include "RegisterInfoInterface.h"
+#include "lldb-s390x-register-enums.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class ProcessMonitor;
+
+class RegisterContextPOSIX_s390x : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_s390x(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_s390x() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ struct RegInfo {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+ };
+
+ RegInfo m_reg_info;
+ std::unique_ptr<lldb_private::RegisterInfoInterface> m_register_info_up;
+
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_s390x_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp
new file mode 100644
index 000000000000..4d5991f08f1d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp
@@ -0,0 +1,528 @@
+//===-- RegisterContextPOSIX_x86.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_x86.h"
+#include "RegisterContext_x86.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+const uint32_t g_gpr_regnums_i386[] = {
+ lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, lldb_edx_i386,
+ lldb_edi_i386, lldb_esi_i386, lldb_ebp_i386, lldb_esp_i386,
+ lldb_eip_i386, lldb_eflags_i386, lldb_cs_i386, lldb_fs_i386,
+ lldb_gs_i386, lldb_ss_i386, lldb_ds_i386, lldb_es_i386,
+ lldb_ax_i386, lldb_bx_i386, lldb_cx_i386, lldb_dx_i386,
+ lldb_di_i386, lldb_si_i386, lldb_bp_i386, lldb_sp_i386,
+ lldb_ah_i386, lldb_bh_i386, lldb_ch_i386, lldb_dh_i386,
+ lldb_al_i386, lldb_bl_i386, lldb_cl_i386, lldb_dl_i386,
+ LLDB_INVALID_REGNUM, // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_gpr_regnums_i386) / sizeof(g_gpr_regnums_i386[0])) -
+ 1 ==
+ k_num_gpr_registers_i386,
+ "g_gpr_regnums_i386 has wrong number of register infos");
+
+const uint32_t g_lldb_regnums_i386[] = {
+ lldb_fctrl_i386, lldb_fstat_i386, lldb_ftag_i386, lldb_fop_i386,
+ lldb_fiseg_i386, lldb_fioff_i386, lldb_foseg_i386, lldb_fooff_i386,
+ lldb_mxcsr_i386, lldb_mxcsrmask_i386, lldb_st0_i386, lldb_st1_i386,
+ lldb_st2_i386, lldb_st3_i386, lldb_st4_i386, lldb_st5_i386,
+ lldb_st6_i386, lldb_st7_i386, lldb_mm0_i386, lldb_mm1_i386,
+ lldb_mm2_i386, lldb_mm3_i386, lldb_mm4_i386, lldb_mm5_i386,
+ lldb_mm6_i386, lldb_mm7_i386, lldb_xmm0_i386, lldb_xmm1_i386,
+ lldb_xmm2_i386, lldb_xmm3_i386, lldb_xmm4_i386, lldb_xmm5_i386,
+ lldb_xmm6_i386, lldb_xmm7_i386,
+ LLDB_INVALID_REGNUM // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_lldb_regnums_i386) / sizeof(g_lldb_regnums_i386[0])) -
+ 1 ==
+ k_num_fpr_registers_i386,
+ "g_lldb_regnums_i386 has wrong number of register infos");
+
+const uint32_t g_avx_regnums_i386[] = {
+ lldb_ymm0_i386, lldb_ymm1_i386, lldb_ymm2_i386, lldb_ymm3_i386,
+ lldb_ymm4_i386, lldb_ymm5_i386, lldb_ymm6_i386, lldb_ymm7_i386,
+ LLDB_INVALID_REGNUM // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_avx_regnums_i386) / sizeof(g_avx_regnums_i386[0])) -
+ 1 ==
+ k_num_avx_registers_i386,
+ " g_avx_regnums_i386 has wrong number of register infos");
+
+static const uint32_t g_gpr_regnums_x86_64[] = {
+ lldb_rax_x86_64, lldb_rbx_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64,
+ lldb_rdi_x86_64, lldb_rsi_x86_64, lldb_rbp_x86_64, lldb_rsp_x86_64,
+ lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64,
+ lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64,
+ lldb_rip_x86_64, lldb_rflags_x86_64, lldb_cs_x86_64, lldb_fs_x86_64,
+ lldb_gs_x86_64, lldb_ss_x86_64, lldb_ds_x86_64, lldb_es_x86_64,
+ lldb_eax_x86_64, lldb_ebx_x86_64, lldb_ecx_x86_64, lldb_edx_x86_64,
+ lldb_edi_x86_64, lldb_esi_x86_64, lldb_ebp_x86_64, lldb_esp_x86_64,
+ lldb_r8d_x86_64, // Low 32 bits or r8
+ lldb_r9d_x86_64, // Low 32 bits or r9
+ lldb_r10d_x86_64, // Low 32 bits or r10
+ lldb_r11d_x86_64, // Low 32 bits or r11
+ lldb_r12d_x86_64, // Low 32 bits or r12
+ lldb_r13d_x86_64, // Low 32 bits or r13
+ lldb_r14d_x86_64, // Low 32 bits or r14
+ lldb_r15d_x86_64, // Low 32 bits or r15
+ lldb_ax_x86_64, lldb_bx_x86_64, lldb_cx_x86_64, lldb_dx_x86_64,
+ lldb_di_x86_64, lldb_si_x86_64, lldb_bp_x86_64, lldb_sp_x86_64,
+ lldb_r8w_x86_64, // Low 16 bits or r8
+ lldb_r9w_x86_64, // Low 16 bits or r9
+ lldb_r10w_x86_64, // Low 16 bits or r10
+ lldb_r11w_x86_64, // Low 16 bits or r11
+ lldb_r12w_x86_64, // Low 16 bits or r12
+ lldb_r13w_x86_64, // Low 16 bits or r13
+ lldb_r14w_x86_64, // Low 16 bits or r14
+ lldb_r15w_x86_64, // Low 16 bits or r15
+ lldb_ah_x86_64, lldb_bh_x86_64, lldb_ch_x86_64, lldb_dh_x86_64,
+ lldb_al_x86_64, lldb_bl_x86_64, lldb_cl_x86_64, lldb_dl_x86_64,
+ lldb_dil_x86_64, lldb_sil_x86_64, lldb_bpl_x86_64, lldb_spl_x86_64,
+ lldb_r8l_x86_64, // Low 8 bits or r8
+ lldb_r9l_x86_64, // Low 8 bits or r9
+ lldb_r10l_x86_64, // Low 8 bits or r10
+ lldb_r11l_x86_64, // Low 8 bits or r11
+ lldb_r12l_x86_64, // Low 8 bits or r12
+ lldb_r13l_x86_64, // Low 8 bits or r13
+ lldb_r14l_x86_64, // Low 8 bits or r14
+ lldb_r15l_x86_64, // Low 8 bits or r15
+ LLDB_INVALID_REGNUM // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) -
+ 1 ==
+ k_num_gpr_registers_x86_64,
+ "g_gpr_regnums_x86_64 has wrong number of register infos");
+
+static const uint32_t g_lldb_regnums_x86_64[] = {
+ lldb_fctrl_x86_64, lldb_fstat_x86_64, lldb_ftag_x86_64,
+ lldb_fop_x86_64, lldb_fiseg_x86_64, lldb_fioff_x86_64,
+ lldb_foseg_x86_64, lldb_fooff_x86_64, lldb_mxcsr_x86_64,
+ lldb_mxcsrmask_x86_64, lldb_st0_x86_64, lldb_st1_x86_64,
+ lldb_st2_x86_64, lldb_st3_x86_64, lldb_st4_x86_64,
+ lldb_st5_x86_64, lldb_st6_x86_64, lldb_st7_x86_64,
+ lldb_mm0_x86_64, lldb_mm1_x86_64, lldb_mm2_x86_64,
+ lldb_mm3_x86_64, lldb_mm4_x86_64, lldb_mm5_x86_64,
+ lldb_mm6_x86_64, lldb_mm7_x86_64, lldb_xmm0_x86_64,
+ lldb_xmm1_x86_64, lldb_xmm2_x86_64, lldb_xmm3_x86_64,
+ lldb_xmm4_x86_64, lldb_xmm5_x86_64, lldb_xmm6_x86_64,
+ lldb_xmm7_x86_64, lldb_xmm8_x86_64, lldb_xmm9_x86_64,
+ lldb_xmm10_x86_64, lldb_xmm11_x86_64, lldb_xmm12_x86_64,
+ lldb_xmm13_x86_64, lldb_xmm14_x86_64, lldb_xmm15_x86_64,
+ LLDB_INVALID_REGNUM // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_lldb_regnums_x86_64) /
+ sizeof(g_lldb_regnums_x86_64[0])) -
+ 1 ==
+ k_num_fpr_registers_x86_64,
+ "g_lldb_regnums_x86_64 has wrong number of register infos");
+
+static const uint32_t g_avx_regnums_x86_64[] = {
+ lldb_ymm0_x86_64, lldb_ymm1_x86_64, lldb_ymm2_x86_64, lldb_ymm3_x86_64,
+ lldb_ymm4_x86_64, lldb_ymm5_x86_64, lldb_ymm6_x86_64, lldb_ymm7_x86_64,
+ lldb_ymm8_x86_64, lldb_ymm9_x86_64, lldb_ymm10_x86_64, lldb_ymm11_x86_64,
+ lldb_ymm12_x86_64, lldb_ymm13_x86_64, lldb_ymm14_x86_64, lldb_ymm15_x86_64,
+ LLDB_INVALID_REGNUM // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_avx_regnums_x86_64) / sizeof(g_avx_regnums_x86_64[0])) -
+ 1 ==
+ k_num_avx_registers_x86_64,
+ "g_avx_regnums_x86_64 has wrong number of register infos");
+
+uint32_t RegisterContextPOSIX_x86::g_contained_eax[] = {lldb_eax_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_ebx[] = {lldb_ebx_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_ecx[] = {lldb_ecx_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_edx[] = {lldb_edx_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_edi[] = {lldb_edi_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_esi[] = {lldb_esi_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_ebp[] = {lldb_ebp_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_esp[] = {lldb_esp_i386,
+ LLDB_INVALID_REGNUM};
+
+uint32_t RegisterContextPOSIX_x86::g_invalidate_eax[] = {
+ lldb_eax_i386, lldb_ax_i386, lldb_ah_i386, lldb_al_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_ebx[] = {
+ lldb_ebx_i386, lldb_bx_i386, lldb_bh_i386, lldb_bl_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_ecx[] = {
+ lldb_ecx_i386, lldb_cx_i386, lldb_ch_i386, lldb_cl_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_edx[] = {
+ lldb_edx_i386, lldb_dx_i386, lldb_dh_i386, lldb_dl_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_edi[] = {
+ lldb_edi_i386, lldb_di_i386, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_esi[] = {
+ lldb_esi_i386, lldb_si_i386, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_ebp[] = {
+ lldb_ebp_i386, lldb_bp_i386, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_esp[] = {
+ lldb_esp_i386, lldb_sp_i386, LLDB_INVALID_REGNUM};
+
+uint32_t RegisterContextPOSIX_x86::g_contained_rax[] = {lldb_rax_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rbx[] = {lldb_rbx_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rcx[] = {lldb_rcx_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rdx[] = {lldb_rdx_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rdi[] = {lldb_rdi_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rsi[] = {lldb_rsi_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rbp[] = {lldb_rbp_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rsp[] = {lldb_rsp_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r8[] = {lldb_r8_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r9[] = {lldb_r9_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r10[] = {lldb_r10_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r11[] = {lldb_r11_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r12[] = {lldb_r12_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r13[] = {lldb_r13_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r14[] = {lldb_r14_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r15[] = {lldb_r15_x86_64,
+ LLDB_INVALID_REGNUM};
+
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rax[] = {
+ lldb_rax_x86_64, lldb_eax_x86_64, lldb_ax_x86_64,
+ lldb_ah_x86_64, lldb_al_x86_64, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rbx[] = {
+ lldb_rbx_x86_64, lldb_ebx_x86_64, lldb_bx_x86_64,
+ lldb_bh_x86_64, lldb_bl_x86_64, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rcx[] = {
+ lldb_rcx_x86_64, lldb_ecx_x86_64, lldb_cx_x86_64,
+ lldb_ch_x86_64, lldb_cl_x86_64, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rdx[] = {
+ lldb_rdx_x86_64, lldb_edx_x86_64, lldb_dx_x86_64,
+ lldb_dh_x86_64, lldb_dl_x86_64, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rdi[] = {
+ lldb_rdi_x86_64, lldb_edi_x86_64, lldb_di_x86_64, lldb_dil_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rsi[] = {
+ lldb_rsi_x86_64, lldb_esi_x86_64, lldb_si_x86_64, lldb_sil_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rbp[] = {
+ lldb_rbp_x86_64, lldb_ebp_x86_64, lldb_bp_x86_64, lldb_bpl_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rsp[] = {
+ lldb_rsp_x86_64, lldb_esp_x86_64, lldb_sp_x86_64, lldb_spl_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r8[] = {
+ lldb_r8_x86_64, lldb_r8d_x86_64, lldb_r8w_x86_64, lldb_r8l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r9[] = {
+ lldb_r9_x86_64, lldb_r9d_x86_64, lldb_r9w_x86_64, lldb_r9l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r10[] = {
+ lldb_r10_x86_64, lldb_r10d_x86_64, lldb_r10w_x86_64, lldb_r10l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r11[] = {
+ lldb_r11_x86_64, lldb_r11d_x86_64, lldb_r11w_x86_64, lldb_r11l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r12[] = {
+ lldb_r12_x86_64, lldb_r12d_x86_64, lldb_r12w_x86_64, lldb_r12l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r13[] = {
+ lldb_r13_x86_64, lldb_r13d_x86_64, lldb_r13w_x86_64, lldb_r13l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r14[] = {
+ lldb_r14_x86_64, lldb_r14d_x86_64, lldb_r14w_x86_64, lldb_r14l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r15[] = {
+ lldb_r15_x86_64, lldb_r15d_x86_64, lldb_r15w_x86_64, lldb_r15l_x86_64,
+ LLDB_INVALID_REGNUM};
+
+// Number of register sets provided by this context.
+enum { k_num_extended_register_sets = 1, k_num_register_sets = 3 };
+
+static const RegisterSet g_reg_sets_i386[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_i386,
+ g_gpr_regnums_i386},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_i386,
+ g_lldb_regnums_i386},
+ {"Advanced Vector Extensions", "avx", k_num_avx_registers_i386,
+ g_avx_regnums_i386}};
+
+static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_x86_64,
+ g_gpr_regnums_x86_64},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_x86_64,
+ g_lldb_regnums_x86_64},
+ {"Advanced Vector Extensions", "avx", k_num_avx_registers_x86_64,
+ g_avx_regnums_x86_64}};
+
+bool RegisterContextPOSIX_x86::IsGPR(unsigned reg) {
+ return reg <= m_reg_info.last_gpr; // GPR's come first.
+}
+
+bool RegisterContextPOSIX_x86::IsFPR(unsigned reg) {
+ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
+}
+
+bool RegisterContextPOSIX_x86::IsAVX(unsigned reg) {
+ return (m_reg_info.first_ymm <= reg && reg <= m_reg_info.last_ymm);
+}
+
+bool RegisterContextPOSIX_x86::IsFPR(unsigned reg, FPRType fpr_type) {
+ bool generic_fpr = IsFPR(reg);
+
+ if (fpr_type == eXSAVE)
+ return generic_fpr || IsAVX(reg);
+ return generic_fpr;
+}
+
+RegisterContextPOSIX_x86::RegisterContextPOSIX_x86(
+ Thread &thread, uint32_t concrete_frame_idx,
+ RegisterInfoInterface *register_info)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+
+ switch (register_info->m_target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ m_reg_info.num_registers = k_num_registers_i386;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_i386;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_i386;
+ m_reg_info.num_avx_registers = k_num_avx_registers_i386;
+ m_reg_info.last_gpr = k_last_gpr_i386;
+ m_reg_info.first_fpr = k_first_fpr_i386;
+ m_reg_info.last_fpr = k_last_fpr_i386;
+ m_reg_info.first_st = lldb_st0_i386;
+ m_reg_info.last_st = lldb_st7_i386;
+ m_reg_info.first_mm = lldb_mm0_i386;
+ m_reg_info.last_mm = lldb_mm7_i386;
+ m_reg_info.first_xmm = lldb_xmm0_i386;
+ m_reg_info.last_xmm = lldb_xmm7_i386;
+ m_reg_info.first_ymm = lldb_ymm0_i386;
+ m_reg_info.last_ymm = lldb_ymm7_i386;
+ m_reg_info.first_dr = lldb_dr0_i386;
+ m_reg_info.gpr_flags = lldb_eflags_i386;
+ break;
+ case llvm::Triple::x86_64:
+ m_reg_info.num_registers = k_num_registers_x86_64;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_x86_64;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_x86_64;
+ m_reg_info.num_avx_registers = k_num_avx_registers_x86_64;
+ m_reg_info.last_gpr = k_last_gpr_x86_64;
+ m_reg_info.first_fpr = k_first_fpr_x86_64;
+ m_reg_info.last_fpr = k_last_fpr_x86_64;
+ m_reg_info.first_st = lldb_st0_x86_64;
+ m_reg_info.last_st = lldb_st7_x86_64;
+ m_reg_info.first_mm = lldb_mm0_x86_64;
+ m_reg_info.last_mm = lldb_mm7_x86_64;
+ m_reg_info.first_xmm = lldb_xmm0_x86_64;
+ m_reg_info.last_xmm = lldb_xmm15_x86_64;
+ m_reg_info.first_ymm = lldb_ymm0_x86_64;
+ m_reg_info.last_ymm = lldb_ymm15_x86_64;
+ m_reg_info.first_dr = lldb_dr0_x86_64;
+ m_reg_info.gpr_flags = lldb_rflags_x86_64;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+
+ ::memset(&m_fpr, 0, sizeof(FPR));
+
+ m_fpr_type = eNotValid;
+}
+
+RegisterContextPOSIX_x86::~RegisterContextPOSIX_x86() {}
+
+RegisterContextPOSIX_x86::FPRType RegisterContextPOSIX_x86::GetFPRType() {
+ if (m_fpr_type == eNotValid) {
+ // TODO: Use assembly to call cpuid on the inferior and query ebx or ecx
+ m_fpr_type = eXSAVE; // extended floating-point registers, if available
+ if (!ReadFPR())
+ m_fpr_type = eFXSAVE; // assume generic floating-point registers
+ }
+ return m_fpr_type;
+}
+
+void RegisterContextPOSIX_x86::Invalidate() {}
+
+void RegisterContextPOSIX_x86::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_x86::GetRegisterOffset(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_x86::GetRegisterSize(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_x86::GetRegisterCount() {
+ size_t num_registers =
+ m_reg_info.num_gpr_registers + m_reg_info.num_fpr_registers;
+ if (GetFPRType() == eXSAVE)
+ return num_registers + m_reg_info.num_avx_registers;
+ return num_registers;
+}
+
+size_t RegisterContextPOSIX_x86::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+size_t RegisterContextPOSIX_x86::GetFXSAVEOffset() {
+ return GetRegisterInfo()[m_reg_info.first_fpr].byte_offset;
+}
+
+const RegisterInfo *RegisterContextPOSIX_x86::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const RegisterInfo *
+RegisterContextPOSIX_x86::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < m_reg_info.num_registers)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_x86::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const RegisterSet *RegisterContextPOSIX_x86::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set)) {
+ switch (m_register_info_up->m_target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return &g_reg_sets_i386[set];
+ case llvm::Triple::x86_64:
+ return &g_reg_sets_x86_64[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+const char *RegisterContextPOSIX_x86::GetRegisterName(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_x86::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = eByteOrderInvalid;
+ Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+// Parse ymm registers and into xmm.bytes and ymmh.bytes.
+bool RegisterContextPOSIX_x86::CopyYMMtoXSTATE(uint32_t reg,
+ lldb::ByteOrder byte_order) {
+ if (!IsAVX(reg))
+ return false;
+
+ if (byte_order == eByteOrderLittle) {
+ uint32_t reg_no = reg - m_reg_info.first_ymm;
+ YMMToXState(m_ymm_set.ymm[reg_no],
+ m_fpr.fxsave.xmm[reg_no].bytes,
+ m_fpr.xsave.ymmh[reg_no].bytes);
+ return true;
+ }
+
+ return false; // unsupported or invalid byte order
+}
+
+// Concatenate xmm.bytes with ymmh.bytes
+bool RegisterContextPOSIX_x86::CopyXSTATEtoYMM(uint32_t reg,
+ lldb::ByteOrder byte_order) {
+ if (!IsAVX(reg))
+ return false;
+
+ if (byte_order == eByteOrderLittle) {
+ uint32_t reg_no = reg - m_reg_info.first_ymm;
+ m_ymm_set.ymm[reg_no] = XStateToYMM(
+ m_fpr.fxsave.xmm[reg_no].bytes,
+ m_fpr.xsave.ymmh[reg_no].bytes);
+ return true;
+ }
+
+ return false; // unsupported or invalid byte order
+}
+
+bool RegisterContextPOSIX_x86::IsRegisterSetAvailable(size_t set_index) {
+ // Note: Extended register sets are assumed to be at the end of g_reg_sets...
+ size_t num_sets = k_num_register_sets - k_num_extended_register_sets;
+
+ if (GetFPRType() == eXSAVE) // ...and to start with AVX registers.
+ ++num_sets;
+ return (set_index < num_sets);
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_x86::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h
new file mode 100644
index 000000000000..932f97bb567f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h
@@ -0,0 +1,176 @@
+//===-- RegisterContextPOSIX_x86.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextPOSIX_x86_h_
+#define liblldb_RegisterContextPOSIX_x86_h_
+
+#include "RegisterContext_x86.h"
+#include "RegisterInfoInterface.h"
+#include "lldb-x86-register-enums.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class ProcessMonitor;
+
+class RegisterContextPOSIX_x86 : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_x86(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_x86() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual size_t GetFXSAVEOffset();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ // Note: prefer kernel definitions over user-land
+ enum FPRType {
+ eNotValid = 0,
+ eFSAVE, // TODO
+ eFXSAVE,
+ eSOFT, // TODO
+ eXSAVE
+ };
+
+ static uint32_t g_contained_eax[];
+ static uint32_t g_contained_ebx[];
+ static uint32_t g_contained_ecx[];
+ static uint32_t g_contained_edx[];
+ static uint32_t g_contained_edi[];
+ static uint32_t g_contained_esi[];
+ static uint32_t g_contained_ebp[];
+ static uint32_t g_contained_esp[];
+
+ static uint32_t g_invalidate_eax[];
+ static uint32_t g_invalidate_ebx[];
+ static uint32_t g_invalidate_ecx[];
+ static uint32_t g_invalidate_edx[];
+ static uint32_t g_invalidate_edi[];
+ static uint32_t g_invalidate_esi[];
+ static uint32_t g_invalidate_ebp[];
+ static uint32_t g_invalidate_esp[];
+
+ static uint32_t g_contained_rax[];
+ static uint32_t g_contained_rbx[];
+ static uint32_t g_contained_rcx[];
+ static uint32_t g_contained_rdx[];
+ static uint32_t g_contained_rdi[];
+ static uint32_t g_contained_rsi[];
+ static uint32_t g_contained_rbp[];
+ static uint32_t g_contained_rsp[];
+ static uint32_t g_contained_r8[];
+ static uint32_t g_contained_r9[];
+ static uint32_t g_contained_r10[];
+ static uint32_t g_contained_r11[];
+ static uint32_t g_contained_r12[];
+ static uint32_t g_contained_r13[];
+ static uint32_t g_contained_r14[];
+ static uint32_t g_contained_r15[];
+
+ static uint32_t g_invalidate_rax[];
+ static uint32_t g_invalidate_rbx[];
+ static uint32_t g_invalidate_rcx[];
+ static uint32_t g_invalidate_rdx[];
+ static uint32_t g_invalidate_rdi[];
+ static uint32_t g_invalidate_rsi[];
+ static uint32_t g_invalidate_rbp[];
+ static uint32_t g_invalidate_rsp[];
+ static uint32_t g_invalidate_r8[];
+ static uint32_t g_invalidate_r9[];
+ static uint32_t g_invalidate_r10[];
+ static uint32_t g_invalidate_r11[];
+ static uint32_t g_invalidate_r12[];
+ static uint32_t g_invalidate_r13[];
+ static uint32_t g_invalidate_r14[];
+ static uint32_t g_invalidate_r15[];
+
+protected:
+ struct RegInfo {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+ uint32_t num_avx_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+
+ uint32_t first_st;
+ uint32_t last_st;
+ uint32_t first_mm;
+ uint32_t last_mm;
+ uint32_t first_xmm;
+ uint32_t last_xmm;
+ uint32_t first_ymm;
+ uint32_t last_ymm;
+
+ uint32_t first_dr;
+ uint32_t gpr_flags;
+ };
+
+ uint64_t m_gpr_x86_64[lldb_private::k_num_gpr_registers_x86_64]; // 64-bit
+ // general
+ // purpose
+ // registers.
+ RegInfo m_reg_info;
+ FPRType
+ m_fpr_type; // determines the type of data stored by union FPR, if any.
+ lldb_private::FPR m_fpr; // floating-point registers including extended
+ // register sets.
+ lldb_private::YMM m_ymm_set; // copy of ymmh and xmm register halves.
+ std::unique_ptr<lldb_private::RegisterInfoInterface>
+ m_register_info_up; // Register Info Interface (FreeBSD or Linux)
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ bool IsAVX(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ bool CopyXSTATEtoYMM(uint32_t reg, lldb::ByteOrder byte_order);
+ bool CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order);
+ bool IsFPR(unsigned reg, FPRType fpr_type);
+ FPRType GetFPRType();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_x86_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp
new file mode 100644
index 000000000000..bcf60cc7a338
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp
@@ -0,0 +1,216 @@
+//===-- RegisterContextThreadMemory.cpp -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/OperatingSystem.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-private.h"
+
+#include "RegisterContextThreadMemory.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextThreadMemory::RegisterContextThreadMemory(
+ Thread &thread, lldb::addr_t register_data_addr)
+ : RegisterContext(thread, 0), m_thread_wp(thread.shared_from_this()),
+ m_reg_ctx_sp(), m_register_data_addr(register_data_addr), m_stop_id(0) {}
+
+RegisterContextThreadMemory::~RegisterContextThreadMemory() {}
+
+void RegisterContextThreadMemory::UpdateRegisterContext() {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ ProcessSP process_sp(thread_sp->GetProcess());
+
+ if (process_sp) {
+ const uint32_t stop_id = process_sp->GetModID().GetStopID();
+ if (m_stop_id != stop_id) {
+ m_stop_id = stop_id;
+ m_reg_ctx_sp.reset();
+ }
+ if (!m_reg_ctx_sp) {
+ ThreadSP backing_thread_sp(thread_sp->GetBackingThread());
+ if (backing_thread_sp) {
+ m_reg_ctx_sp = backing_thread_sp->GetRegisterContext();
+ } else {
+ OperatingSystem *os = process_sp->GetOperatingSystem();
+ if (os->IsOperatingSystemPluginThread(thread_sp))
+ m_reg_ctx_sp = os->CreateRegisterContextForThread(
+ thread_sp.get(), m_register_data_addr);
+ }
+ }
+ } else {
+ m_reg_ctx_sp.reset();
+ }
+ } else {
+ m_reg_ctx_sp.reset();
+ }
+}
+
+// Subclasses must override these functions
+void RegisterContextThreadMemory::InvalidateAllRegisters() {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ m_reg_ctx_sp->InvalidateAllRegisters();
+}
+
+size_t RegisterContextThreadMemory::GetRegisterCount() {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterCount();
+ return 0;
+}
+
+const RegisterInfo *
+RegisterContextThreadMemory::GetRegisterInfoAtIndex(size_t reg) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterInfoAtIndex(reg);
+ return nullptr;
+}
+
+size_t RegisterContextThreadMemory::GetRegisterSetCount() {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterSetCount();
+ return 0;
+}
+
+const RegisterSet *RegisterContextThreadMemory::GetRegisterSet(size_t reg_set) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterSet(reg_set);
+ return nullptr;
+}
+
+bool RegisterContextThreadMemory::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ReadRegister(reg_info, reg_value);
+ return false;
+}
+
+bool RegisterContextThreadMemory::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &reg_value) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->WriteRegister(reg_info, reg_value);
+ return false;
+}
+
+bool RegisterContextThreadMemory::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ReadAllRegisterValues(data_sp);
+ return false;
+}
+
+bool RegisterContextThreadMemory::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->WriteAllRegisterValues(data_sp);
+ return false;
+}
+
+bool RegisterContextThreadMemory::CopyFromRegisterContext(
+ lldb::RegisterContextSP reg_ctx_sp) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->CopyFromRegisterContext(reg_ctx_sp);
+ return false;
+}
+
+uint32_t RegisterContextThreadMemory::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ConvertRegisterKindToRegisterNumber(kind, num);
+ return false;
+}
+
+uint32_t RegisterContextThreadMemory::NumSupportedHardwareBreakpoints() {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->NumSupportedHardwareBreakpoints();
+ return false;
+}
+
+uint32_t RegisterContextThreadMemory::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->SetHardwareBreakpoint(addr, size);
+ return 0;
+}
+
+bool RegisterContextThreadMemory::ClearHardwareBreakpoint(uint32_t hw_idx) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ClearHardwareBreakpoint(hw_idx);
+ return false;
+}
+
+uint32_t RegisterContextThreadMemory::NumSupportedHardwareWatchpoints() {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->NumSupportedHardwareWatchpoints();
+ return 0;
+}
+
+uint32_t RegisterContextThreadMemory::SetHardwareWatchpoint(lldb::addr_t addr,
+ size_t size,
+ bool read,
+ bool write) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->SetHardwareWatchpoint(addr, size, read, write);
+ return 0;
+}
+
+bool RegisterContextThreadMemory::ClearHardwareWatchpoint(uint32_t hw_index) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ClearHardwareWatchpoint(hw_index);
+ return false;
+}
+
+bool RegisterContextThreadMemory::HardwareSingleStep(bool enable) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->HardwareSingleStep(enable);
+ return false;
+}
+
+Status RegisterContextThreadMemory::ReadRegisterValueFromMemory(
+ const lldb_private::RegisterInfo *reg_info, lldb::addr_t src_addr,
+ uint32_t src_len, RegisterValue &reg_value) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ReadRegisterValueFromMemory(reg_info, src_addr,
+ src_len, reg_value);
+ Status error;
+ error.SetErrorString("invalid register context");
+ return error;
+}
+
+Status RegisterContextThreadMemory::WriteRegisterValueToMemory(
+ const lldb_private::RegisterInfo *reg_info, lldb::addr_t dst_addr,
+ uint32_t dst_len, const RegisterValue &reg_value) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->WriteRegisterValueToMemory(reg_info, dst_addr, dst_len,
+ reg_value);
+ Status error;
+ error.SetErrorString("invalid register context");
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.h
new file mode 100644
index 000000000000..09a679ab2c9f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.h
@@ -0,0 +1,100 @@
+//===-- RegisterContextThreadMemory.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextThreadMemory_h_
+#define lldb_RegisterContextThreadMemory_h_
+
+#include <vector>
+
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class RegisterContextThreadMemory : public lldb_private::RegisterContext {
+public:
+ RegisterContextThreadMemory(Thread &thread, lldb::addr_t register_data_addr);
+
+ ~RegisterContextThreadMemory() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) override;
+
+ bool WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) override;
+
+ // These two functions are used to implement "push" and "pop" of register
+ // states. They are used primarily
+ // for expression evaluation, where we need to push a new state (storing the
+ // old one in data_sp) and then
+ // restoring the original state by passing the data_sp we got from
+ // ReadAllRegisters to WriteAllRegisterValues.
+ // ReadAllRegisters will do what is necessary to return a coherent set of
+ // register values for this thread, which
+ // may mean e.g. interrupting a thread that is sitting in a kernel trap. That
+ // is a somewhat disruptive operation,
+ // so these API's should only be used when this behavior is needed.
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool CopyFromRegisterContext(lldb::RegisterContextSP context);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ uint32_t NumSupportedHardwareBreakpoints() override;
+
+ uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+
+ bool ClearHardwareBreakpoint(uint32_t hw_idx) override;
+
+ uint32_t NumSupportedHardwareWatchpoints() override;
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write) override;
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+ Status ReadRegisterValueFromMemory(const lldb_private::RegisterInfo *reg_info,
+ lldb::addr_t src_addr, uint32_t src_len,
+ RegisterValue &reg_value) override;
+
+ Status WriteRegisterValueToMemory(const lldb_private::RegisterInfo *reg_info,
+ lldb::addr_t dst_addr, uint32_t dst_len,
+ const RegisterValue &reg_value) override;
+
+protected:
+ void UpdateRegisterContext();
+
+ lldb::ThreadWP m_thread_wp;
+ lldb::RegisterContextSP m_reg_ctx_sp;
+ lldb::addr_t m_register_data_addr;
+ uint32_t m_stop_id;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextThreadMemory);
+};
+
+} // namespace lldb_private
+
+#endif // lldb_RegisterContextThreadMemory_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_mips.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_mips.h
new file mode 100644
index 000000000000..7780be51baad
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_mips.h
@@ -0,0 +1,374 @@
+//===-- RegisterContext_mips.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContext_mips64_H_
+#define liblldb_RegisterContext_mips64_H_
+
+#include <cstddef>
+#include <cstdint>
+
+// eh_frame and DWARF Register numbers (eRegisterKindEHFrame &
+// eRegisterKindDWARF)
+
+enum {
+ // GP Registers
+ dwarf_zero_mips = 0,
+ dwarf_r1_mips,
+ dwarf_r2_mips,
+ dwarf_r3_mips,
+ dwarf_r4_mips,
+ dwarf_r5_mips,
+ dwarf_r6_mips,
+ dwarf_r7_mips,
+ dwarf_r8_mips,
+ dwarf_r9_mips,
+ dwarf_r10_mips,
+ dwarf_r11_mips,
+ dwarf_r12_mips,
+ dwarf_r13_mips,
+ dwarf_r14_mips,
+ dwarf_r15_mips,
+ dwarf_r16_mips,
+ dwarf_r17_mips,
+ dwarf_r18_mips,
+ dwarf_r19_mips,
+ dwarf_r20_mips,
+ dwarf_r21_mips,
+ dwarf_r22_mips,
+ dwarf_r23_mips,
+ dwarf_r24_mips,
+ dwarf_r25_mips,
+ dwarf_r26_mips,
+ dwarf_r27_mips,
+ dwarf_gp_mips,
+ dwarf_sp_mips,
+ dwarf_r30_mips,
+ dwarf_ra_mips,
+ dwarf_sr_mips,
+ dwarf_lo_mips,
+ dwarf_hi_mips,
+ dwarf_bad_mips,
+ dwarf_cause_mips,
+ dwarf_pc_mips,
+ dwarf_f0_mips,
+ dwarf_f1_mips,
+ dwarf_f2_mips,
+ dwarf_f3_mips,
+ dwarf_f4_mips,
+ dwarf_f5_mips,
+ dwarf_f6_mips,
+ dwarf_f7_mips,
+ dwarf_f8_mips,
+ dwarf_f9_mips,
+ dwarf_f10_mips,
+ dwarf_f11_mips,
+ dwarf_f12_mips,
+ dwarf_f13_mips,
+ dwarf_f14_mips,
+ dwarf_f15_mips,
+ dwarf_f16_mips,
+ dwarf_f17_mips,
+ dwarf_f18_mips,
+ dwarf_f19_mips,
+ dwarf_f20_mips,
+ dwarf_f21_mips,
+ dwarf_f22_mips,
+ dwarf_f23_mips,
+ dwarf_f24_mips,
+ dwarf_f25_mips,
+ dwarf_f26_mips,
+ dwarf_f27_mips,
+ dwarf_f28_mips,
+ dwarf_f29_mips,
+ dwarf_f30_mips,
+ dwarf_f31_mips,
+ dwarf_fcsr_mips,
+ dwarf_fir_mips,
+ dwarf_w0_mips,
+ dwarf_w1_mips,
+ dwarf_w2_mips,
+ dwarf_w3_mips,
+ dwarf_w4_mips,
+ dwarf_w5_mips,
+ dwarf_w6_mips,
+ dwarf_w7_mips,
+ dwarf_w8_mips,
+ dwarf_w9_mips,
+ dwarf_w10_mips,
+ dwarf_w11_mips,
+ dwarf_w12_mips,
+ dwarf_w13_mips,
+ dwarf_w14_mips,
+ dwarf_w15_mips,
+ dwarf_w16_mips,
+ dwarf_w17_mips,
+ dwarf_w18_mips,
+ dwarf_w19_mips,
+ dwarf_w20_mips,
+ dwarf_w21_mips,
+ dwarf_w22_mips,
+ dwarf_w23_mips,
+ dwarf_w24_mips,
+ dwarf_w25_mips,
+ dwarf_w26_mips,
+ dwarf_w27_mips,
+ dwarf_w28_mips,
+ dwarf_w29_mips,
+ dwarf_w30_mips,
+ dwarf_w31_mips,
+ dwarf_mcsr_mips,
+ dwarf_mir_mips,
+ dwarf_config5_mips,
+ dwarf_ic_mips,
+ dwarf_dummy_mips
+};
+
+enum {
+ dwarf_zero_mips64 = 0,
+ dwarf_r1_mips64,
+ dwarf_r2_mips64,
+ dwarf_r3_mips64,
+ dwarf_r4_mips64,
+ dwarf_r5_mips64,
+ dwarf_r6_mips64,
+ dwarf_r7_mips64,
+ dwarf_r8_mips64,
+ dwarf_r9_mips64,
+ dwarf_r10_mips64,
+ dwarf_r11_mips64,
+ dwarf_r12_mips64,
+ dwarf_r13_mips64,
+ dwarf_r14_mips64,
+ dwarf_r15_mips64,
+ dwarf_r16_mips64,
+ dwarf_r17_mips64,
+ dwarf_r18_mips64,
+ dwarf_r19_mips64,
+ dwarf_r20_mips64,
+ dwarf_r21_mips64,
+ dwarf_r22_mips64,
+ dwarf_r23_mips64,
+ dwarf_r24_mips64,
+ dwarf_r25_mips64,
+ dwarf_r26_mips64,
+ dwarf_r27_mips64,
+ dwarf_gp_mips64,
+ dwarf_sp_mips64,
+ dwarf_r30_mips64,
+ dwarf_ra_mips64,
+ dwarf_sr_mips64,
+ dwarf_lo_mips64,
+ dwarf_hi_mips64,
+ dwarf_bad_mips64,
+ dwarf_cause_mips64,
+ dwarf_pc_mips64,
+ dwarf_f0_mips64,
+ dwarf_f1_mips64,
+ dwarf_f2_mips64,
+ dwarf_f3_mips64,
+ dwarf_f4_mips64,
+ dwarf_f5_mips64,
+ dwarf_f6_mips64,
+ dwarf_f7_mips64,
+ dwarf_f8_mips64,
+ dwarf_f9_mips64,
+ dwarf_f10_mips64,
+ dwarf_f11_mips64,
+ dwarf_f12_mips64,
+ dwarf_f13_mips64,
+ dwarf_f14_mips64,
+ dwarf_f15_mips64,
+ dwarf_f16_mips64,
+ dwarf_f17_mips64,
+ dwarf_f18_mips64,
+ dwarf_f19_mips64,
+ dwarf_f20_mips64,
+ dwarf_f21_mips64,
+ dwarf_f22_mips64,
+ dwarf_f23_mips64,
+ dwarf_f24_mips64,
+ dwarf_f25_mips64,
+ dwarf_f26_mips64,
+ dwarf_f27_mips64,
+ dwarf_f28_mips64,
+ dwarf_f29_mips64,
+ dwarf_f30_mips64,
+ dwarf_f31_mips64,
+ dwarf_fcsr_mips64,
+ dwarf_fir_mips64,
+ dwarf_ic_mips64,
+ dwarf_dummy_mips64,
+ dwarf_w0_mips64,
+ dwarf_w1_mips64,
+ dwarf_w2_mips64,
+ dwarf_w3_mips64,
+ dwarf_w4_mips64,
+ dwarf_w5_mips64,
+ dwarf_w6_mips64,
+ dwarf_w7_mips64,
+ dwarf_w8_mips64,
+ dwarf_w9_mips64,
+ dwarf_w10_mips64,
+ dwarf_w11_mips64,
+ dwarf_w12_mips64,
+ dwarf_w13_mips64,
+ dwarf_w14_mips64,
+ dwarf_w15_mips64,
+ dwarf_w16_mips64,
+ dwarf_w17_mips64,
+ dwarf_w18_mips64,
+ dwarf_w19_mips64,
+ dwarf_w20_mips64,
+ dwarf_w21_mips64,
+ dwarf_w22_mips64,
+ dwarf_w23_mips64,
+ dwarf_w24_mips64,
+ dwarf_w25_mips64,
+ dwarf_w26_mips64,
+ dwarf_w27_mips64,
+ dwarf_w28_mips64,
+ dwarf_w29_mips64,
+ dwarf_w30_mips64,
+ dwarf_w31_mips64,
+ dwarf_mcsr_mips64,
+ dwarf_mir_mips64,
+ dwarf_config5_mips64,
+};
+
+// GP registers
+struct GPR_linux_mips {
+ uint64_t zero;
+ uint64_t r1;
+ uint64_t r2;
+ uint64_t r3;
+ uint64_t r4;
+ uint64_t r5;
+ uint64_t r6;
+ uint64_t r7;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t r16;
+ uint64_t r17;
+ uint64_t r18;
+ uint64_t r19;
+ uint64_t r20;
+ uint64_t r21;
+ uint64_t r22;
+ uint64_t r23;
+ uint64_t r24;
+ uint64_t r25;
+ uint64_t r26;
+ uint64_t r27;
+ uint64_t gp;
+ uint64_t sp;
+ uint64_t r30;
+ uint64_t ra;
+ uint64_t mullo;
+ uint64_t mulhi;
+ uint64_t pc;
+ uint64_t badvaddr;
+ uint64_t sr;
+ uint64_t cause;
+ uint64_t config5;
+};
+
+struct FPR_linux_mips {
+ uint64_t f0;
+ uint64_t f1;
+ uint64_t f2;
+ uint64_t f3;
+ uint64_t f4;
+ uint64_t f5;
+ uint64_t f6;
+ uint64_t f7;
+ uint64_t f8;
+ uint64_t f9;
+ uint64_t f10;
+ uint64_t f11;
+ uint64_t f12;
+ uint64_t f13;
+ uint64_t f14;
+ uint64_t f15;
+ uint64_t f16;
+ uint64_t f17;
+ uint64_t f18;
+ uint64_t f19;
+ uint64_t f20;
+ uint64_t f21;
+ uint64_t f22;
+ uint64_t f23;
+ uint64_t f24;
+ uint64_t f25;
+ uint64_t f26;
+ uint64_t f27;
+ uint64_t f28;
+ uint64_t f29;
+ uint64_t f30;
+ uint64_t f31;
+ uint32_t fcsr;
+ uint32_t fir;
+ uint32_t config5;
+};
+
+struct MSAReg {
+ uint8_t byte[16];
+};
+
+struct MSA_linux_mips {
+ MSAReg w0;
+ MSAReg w1;
+ MSAReg w2;
+ MSAReg w3;
+ MSAReg w4;
+ MSAReg w5;
+ MSAReg w6;
+ MSAReg w7;
+ MSAReg w8;
+ MSAReg w9;
+ MSAReg w10;
+ MSAReg w11;
+ MSAReg w12;
+ MSAReg w13;
+ MSAReg w14;
+ MSAReg w15;
+ MSAReg w16;
+ MSAReg w17;
+ MSAReg w18;
+ MSAReg w19;
+ MSAReg w20;
+ MSAReg w21;
+ MSAReg w22;
+ MSAReg w23;
+ MSAReg w24;
+ MSAReg w25;
+ MSAReg w26;
+ MSAReg w27;
+ MSAReg w28;
+ MSAReg w29;
+ MSAReg w30;
+ MSAReg w31;
+ uint32_t fcsr; /* FPU control status register */
+ uint32_t fir; /* FPU implementaion revision */
+ uint32_t mcsr; /* MSA control status register */
+ uint32_t mir; /* MSA implementation revision */
+ uint32_t config5; /* Config5 register */
+};
+
+struct UserArea {
+ GPR_linux_mips gpr; // General purpose registers.
+ FPR_linux_mips fpr; // Floating point registers.
+ MSA_linux_mips msa; // MSA registers.
+};
+
+#endif // liblldb_RegisterContext_mips64_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_powerpc.h
new file mode 100644
index 000000000000..1ffcbeb5ec48
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_powerpc.h
@@ -0,0 +1,123 @@
+//===-- RegisterContext_powerpc.h --------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContext_powerpc_H_
+#define liblldb_RegisterContext_powerpc_H_
+
+// eh_frame and DWARF Register numbers (eRegisterKindEHFrame &
+// eRegisterKindDWARF)
+enum {
+ dwarf_r0_powerpc = 0,
+ dwarf_r1_powerpc,
+ dwarf_r2_powerpc,
+ dwarf_r3_powerpc,
+ dwarf_r4_powerpc,
+ dwarf_r5_powerpc,
+ dwarf_r6_powerpc,
+ dwarf_r7_powerpc,
+ dwarf_r8_powerpc,
+ dwarf_r9_powerpc,
+ dwarf_r10_powerpc,
+ dwarf_r11_powerpc,
+ dwarf_r12_powerpc,
+ dwarf_r13_powerpc,
+ dwarf_r14_powerpc,
+ dwarf_r15_powerpc,
+ dwarf_r16_powerpc,
+ dwarf_r17_powerpc,
+ dwarf_r18_powerpc,
+ dwarf_r19_powerpc,
+ dwarf_r20_powerpc,
+ dwarf_r21_powerpc,
+ dwarf_r22_powerpc,
+ dwarf_r23_powerpc,
+ dwarf_r24_powerpc,
+ dwarf_r25_powerpc,
+ dwarf_r26_powerpc,
+ dwarf_r27_powerpc,
+ dwarf_r28_powerpc,
+ dwarf_r29_powerpc,
+ dwarf_r30_powerpc,
+ dwarf_r31_powerpc,
+ dwarf_f0_powerpc,
+ dwarf_f1_powerpc,
+ dwarf_f2_powerpc,
+ dwarf_f3_powerpc,
+ dwarf_f4_powerpc,
+ dwarf_f5_powerpc,
+ dwarf_f6_powerpc,
+ dwarf_f7_powerpc,
+ dwarf_f8_powerpc,
+ dwarf_f9_powerpc,
+ dwarf_f10_powerpc,
+ dwarf_f11_powerpc,
+ dwarf_f12_powerpc,
+ dwarf_f13_powerpc,
+ dwarf_f14_powerpc,
+ dwarf_f15_powerpc,
+ dwarf_f16_powerpc,
+ dwarf_f17_powerpc,
+ dwarf_f18_powerpc,
+ dwarf_f19_powerpc,
+ dwarf_f20_powerpc,
+ dwarf_f21_powerpc,
+ dwarf_f22_powerpc,
+ dwarf_f23_powerpc,
+ dwarf_f24_powerpc,
+ dwarf_f25_powerpc,
+ dwarf_f26_powerpc,
+ dwarf_f27_powerpc,
+ dwarf_f28_powerpc,
+ dwarf_f29_powerpc,
+ dwarf_f30_powerpc,
+ dwarf_f31_powerpc,
+ dwarf_cr_powerpc,
+ dwarf_fpscr_powerpc,
+ dwarf_msr_powerpc,
+ dwarf_vscr_powerpc,
+ dwarf_xer_powerpc = 101,
+ dwarf_lr_powerpc = 108,
+ dwarf_ctr_powerpc,
+ dwarf_pc_powerpc,
+ dwarf_vrsave_powerpc = 356,
+ dwarf_v0_powerpc = 1124,
+ dwarf_v1_powerpc,
+ dwarf_v2_powerpc,
+ dwarf_v3_powerpc,
+ dwarf_v4_powerpc,
+ dwarf_v5_powerpc,
+ dwarf_v6_powerpc,
+ dwarf_v7_powerpc,
+ dwarf_v8_powerpc,
+ dwarf_v9_powerpc,
+ dwarf_v10_powerpc,
+ dwarf_v11_powerpc,
+ dwarf_v12_powerpc,
+ dwarf_v13_powerpc,
+ dwarf_v14_powerpc,
+ dwarf_v15_powerpc,
+ dwarf_v16_powerpc,
+ dwarf_v17_powerpc,
+ dwarf_v18_powerpc,
+ dwarf_v19_powerpc,
+ dwarf_v20_powerpc,
+ dwarf_v21_powerpc,
+ dwarf_v22_powerpc,
+ dwarf_v23_powerpc,
+ dwarf_v24_powerpc,
+ dwarf_v25_powerpc,
+ dwarf_v26_powerpc,
+ dwarf_v27_powerpc,
+ dwarf_v28_powerpc,
+ dwarf_v29_powerpc,
+ dwarf_v30_powerpc,
+ dwarf_v31_powerpc,
+};
+
+#endif // liblldb_RegisterContext_powerpc_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_s390x.h
new file mode 100644
index 000000000000..2cf39e9eb8e2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_s390x.h
@@ -0,0 +1,90 @@
+//===-- RegisterContext_s390x.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContext_s390x_h_
+#define liblldb_RegisterContext_s390x_h_
+
+// SystemZ ehframe, dwarf regnums
+
+// EHFrame and DWARF Register numbers (eRegisterKindEHFrame &
+// eRegisterKindDWARF)
+enum {
+ // General Purpose Registers
+ dwarf_r0_s390x = 0,
+ dwarf_r1_s390x,
+ dwarf_r2_s390x,
+ dwarf_r3_s390x,
+ dwarf_r4_s390x,
+ dwarf_r5_s390x,
+ dwarf_r6_s390x,
+ dwarf_r7_s390x,
+ dwarf_r8_s390x,
+ dwarf_r9_s390x,
+ dwarf_r10_s390x,
+ dwarf_r11_s390x,
+ dwarf_r12_s390x,
+ dwarf_r13_s390x,
+ dwarf_r14_s390x,
+ dwarf_r15_s390x,
+ // Floating Point Registers / Vector Registers 0-15
+ dwarf_f0_s390x = 16,
+ dwarf_f2_s390x,
+ dwarf_f4_s390x,
+ dwarf_f6_s390x,
+ dwarf_f1_s390x,
+ dwarf_f3_s390x,
+ dwarf_f5_s390x,
+ dwarf_f7_s390x,
+ dwarf_f8_s390x,
+ dwarf_f10_s390x,
+ dwarf_f12_s390x,
+ dwarf_f14_s390x,
+ dwarf_f9_s390x,
+ dwarf_f11_s390x,
+ dwarf_f13_s390x,
+ dwarf_f15_s390x,
+ // Access Registers
+ dwarf_acr0_s390x = 48,
+ dwarf_acr1_s390x,
+ dwarf_acr2_s390x,
+ dwarf_acr3_s390x,
+ dwarf_acr4_s390x,
+ dwarf_acr5_s390x,
+ dwarf_acr6_s390x,
+ dwarf_acr7_s390x,
+ dwarf_acr8_s390x,
+ dwarf_acr9_s390x,
+ dwarf_acr10_s390x,
+ dwarf_acr11_s390x,
+ dwarf_acr12_s390x,
+ dwarf_acr13_s390x,
+ dwarf_acr14_s390x,
+ dwarf_acr15_s390x,
+ // Program Status Word
+ dwarf_pswm_s390x = 64,
+ dwarf_pswa_s390x,
+ // Vector Registers 16-31
+ dwarf_v16_s390x = 68,
+ dwarf_v18_s390x,
+ dwarf_v20_s390x,
+ dwarf_v22_s390x,
+ dwarf_v17_s390x,
+ dwarf_v19_s390x,
+ dwarf_v21_s390x,
+ dwarf_v23_s390x,
+ dwarf_v24_s390x,
+ dwarf_v26_s390x,
+ dwarf_v28_s390x,
+ dwarf_v30_s390x,
+ dwarf_v25_s390x,
+ dwarf_v27_s390x,
+ dwarf_v29_s390x,
+ dwarf_v31_s390x,
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.h
new file mode 100644
index 000000000000..2b79f778aa56
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.h
@@ -0,0 +1,374 @@
+//===-- RegisterContext_x86.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContext_x86_H_
+#define liblldb_RegisterContext_x86_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/Support/Compiler.h"
+
+namespace lldb_private {
+// i386 ehframe, dwarf regnums
+
+// Register numbers seen in eh_frame (eRegisterKindEHFrame) on i386 systems
+// (non-Darwin)
+//
+enum {
+ ehframe_eax_i386 = 0,
+ ehframe_ecx_i386,
+ ehframe_edx_i386,
+ ehframe_ebx_i386,
+
+ // on Darwin esp & ebp are reversed in the eh_frame section for i386 (versus
+ // dwarf's reg numbering).
+ // To be specific:
+ // i386+darwin eh_frame: 4 is ebp, 5 is esp
+ // i386+everyone else eh_frame: 4 is esp, 5 is ebp
+ // i386 dwarf: 4 is esp, 5 is ebp
+ // lldb will get the darwin-specific eh_frame reg numberings from debugserver,
+ // or the ABI, so we
+ // only encode the generally correct 4 == esp, 5 == ebp numbers in this
+ // generic header.
+
+ ehframe_esp_i386,
+ ehframe_ebp_i386,
+ ehframe_esi_i386,
+ ehframe_edi_i386,
+ ehframe_eip_i386,
+ ehframe_eflags_i386,
+ ehframe_st0_i386 = 12,
+ ehframe_st1_i386,
+ ehframe_st2_i386,
+ ehframe_st3_i386,
+ ehframe_st4_i386,
+ ehframe_st5_i386,
+ ehframe_st6_i386,
+ ehframe_st7_i386,
+ ehframe_xmm0_i386 = 21,
+ ehframe_xmm1_i386,
+ ehframe_xmm2_i386,
+ ehframe_xmm3_i386,
+ ehframe_xmm4_i386,
+ ehframe_xmm5_i386,
+ ehframe_xmm6_i386,
+ ehframe_xmm7_i386,
+ ehframe_mm0_i386 = 29,
+ ehframe_mm1_i386,
+ ehframe_mm2_i386,
+ ehframe_mm3_i386,
+ ehframe_mm4_i386,
+ ehframe_mm5_i386,
+ ehframe_mm6_i386,
+ ehframe_mm7_i386,
+};
+
+// DWARF register numbers (eRegisterKindDWARF)
+// Intel's x86 or IA-32
+enum {
+ // General Purpose Registers.
+ dwarf_eax_i386 = 0,
+ dwarf_ecx_i386,
+ dwarf_edx_i386,
+ dwarf_ebx_i386,
+ dwarf_esp_i386,
+ dwarf_ebp_i386,
+ dwarf_esi_i386,
+ dwarf_edi_i386,
+ dwarf_eip_i386,
+ dwarf_eflags_i386,
+ // Floating Point Registers
+ dwarf_st0_i386 = 11,
+ dwarf_st1_i386,
+ dwarf_st2_i386,
+ dwarf_st3_i386,
+ dwarf_st4_i386,
+ dwarf_st5_i386,
+ dwarf_st6_i386,
+ dwarf_st7_i386,
+ // SSE Registers
+ dwarf_xmm0_i386 = 21,
+ dwarf_xmm1_i386,
+ dwarf_xmm2_i386,
+ dwarf_xmm3_i386,
+ dwarf_xmm4_i386,
+ dwarf_xmm5_i386,
+ dwarf_xmm6_i386,
+ dwarf_xmm7_i386,
+ // MMX Registers
+ dwarf_mm0_i386 = 29,
+ dwarf_mm1_i386,
+ dwarf_mm2_i386,
+ dwarf_mm3_i386,
+ dwarf_mm4_i386,
+ dwarf_mm5_i386,
+ dwarf_mm6_i386,
+ dwarf_mm7_i386,
+ dwarf_fctrl_i386 = 37, // x87 control word
+ dwarf_fstat_i386 = 38, // x87 status word
+ dwarf_mxcsr_i386 = 39,
+ dwarf_es_i386 = 40,
+ dwarf_cs_i386 = 41,
+ dwarf_ss_i386 = 42,
+ dwarf_ds_i386 = 43,
+ dwarf_fs_i386 = 44,
+ dwarf_gs_i386 = 45,
+
+ // I believe the ymm registers use the dwarf_xmm%_i386 register numbers and
+ // then differentiate based on size of the register.
+ dwarf_bnd0_i386 = 101,
+ dwarf_bnd1_i386,
+ dwarf_bnd2_i386,
+ dwarf_bnd3_i386,
+};
+
+// AMD x86_64, AMD64, Intel EM64T, or Intel 64 ehframe, dwarf regnums
+
+// EHFrame and DWARF Register numbers (eRegisterKindEHFrame &
+// eRegisterKindDWARF)
+// This is the spec I used (as opposed to x86-64-abi-0.99.pdf):
+// http://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf
+enum {
+ // GP Registers
+ dwarf_rax_x86_64 = 0,
+ dwarf_rdx_x86_64,
+ dwarf_rcx_x86_64,
+ dwarf_rbx_x86_64,
+ dwarf_rsi_x86_64,
+ dwarf_rdi_x86_64,
+ dwarf_rbp_x86_64,
+ dwarf_rsp_x86_64,
+ // Extended GP Registers
+ dwarf_r8_x86_64 = 8,
+ dwarf_r9_x86_64,
+ dwarf_r10_x86_64,
+ dwarf_r11_x86_64,
+ dwarf_r12_x86_64,
+ dwarf_r13_x86_64,
+ dwarf_r14_x86_64,
+ dwarf_r15_x86_64,
+ // Return Address (RA) mapped to RIP
+ dwarf_rip_x86_64 = 16,
+ // SSE Vector Registers
+ dwarf_xmm0_x86_64 = 17,
+ dwarf_xmm1_x86_64,
+ dwarf_xmm2_x86_64,
+ dwarf_xmm3_x86_64,
+ dwarf_xmm4_x86_64,
+ dwarf_xmm5_x86_64,
+ dwarf_xmm6_x86_64,
+ dwarf_xmm7_x86_64,
+ dwarf_xmm8_x86_64,
+ dwarf_xmm9_x86_64,
+ dwarf_xmm10_x86_64,
+ dwarf_xmm11_x86_64,
+ dwarf_xmm12_x86_64,
+ dwarf_xmm13_x86_64,
+ dwarf_xmm14_x86_64,
+ dwarf_xmm15_x86_64,
+ // Floating Point Registers
+ dwarf_st0_x86_64 = 33,
+ dwarf_st1_x86_64,
+ dwarf_st2_x86_64,
+ dwarf_st3_x86_64,
+ dwarf_st4_x86_64,
+ dwarf_st5_x86_64,
+ dwarf_st6_x86_64,
+ dwarf_st7_x86_64,
+ // MMX Registers
+ dwarf_mm0_x86_64 = 41,
+ dwarf_mm1_x86_64,
+ dwarf_mm2_x86_64,
+ dwarf_mm3_x86_64,
+ dwarf_mm4_x86_64,
+ dwarf_mm5_x86_64,
+ dwarf_mm6_x86_64,
+ dwarf_mm7_x86_64,
+ // Control and Status Flags Register
+ dwarf_rflags_x86_64 = 49,
+ // selector registers
+ dwarf_es_x86_64 = 50,
+ dwarf_cs_x86_64,
+ dwarf_ss_x86_64,
+ dwarf_ds_x86_64,
+ dwarf_fs_x86_64,
+ dwarf_gs_x86_64,
+ // Floating point control registers
+ dwarf_mxcsr_x86_64 = 64, // Media Control and Status
+ dwarf_fctrl_x86_64, // x87 control word
+ dwarf_fstat_x86_64, // x87 status word
+ // Upper Vector Registers
+ dwarf_ymm0h_x86_64 = 67,
+ dwarf_ymm1h_x86_64,
+ dwarf_ymm2h_x86_64,
+ dwarf_ymm3h_x86_64,
+ dwarf_ymm4h_x86_64,
+ dwarf_ymm5h_x86_64,
+ dwarf_ymm6h_x86_64,
+ dwarf_ymm7h_x86_64,
+ dwarf_ymm8h_x86_64,
+ dwarf_ymm9h_x86_64,
+ dwarf_ymm10h_x86_64,
+ dwarf_ymm11h_x86_64,
+ dwarf_ymm12h_x86_64,
+ dwarf_ymm13h_x86_64,
+ dwarf_ymm14h_x86_64,
+ dwarf_ymm15h_x86_64,
+ // MPX registers
+ dwarf_bnd0_x86_64 = 126,
+ dwarf_bnd1_x86_64,
+ dwarf_bnd2_x86_64,
+ dwarf_bnd3_x86_64,
+ // AVX2 Vector Mask Registers
+ // dwarf_k0_x86_64 = 118,
+ // dwarf_k1_x86_64,
+ // dwarf_k2_x86_64,
+ // dwarf_k3_x86_64,
+ // dwarf_k4_x86_64,
+ // dwarf_k5_x86_64,
+ // dwarf_k6_x86_64,
+ // dwarf_k7_x86_64,
+};
+
+// Generic floating-point registers
+
+struct MMSReg {
+ uint8_t bytes[10];
+ uint8_t pad[6];
+};
+
+struct XMMReg {
+ uint8_t bytes[16]; // 128-bits for each XMM register
+};
+
+// i387_fxsave_struct
+struct FXSAVE {
+ uint16_t fctrl; // FPU Control Word (fcw)
+ uint16_t fstat; // FPU Status Word (fsw)
+ uint16_t ftag; // FPU Tag Word (ftw)
+ uint16_t fop; // Last Instruction Opcode (fop)
+ union {
+ struct {
+ uint64_t fip; // Instruction Pointer
+ uint64_t fdp; // Data Pointer
+ } x86_64;
+ struct {
+ uint32_t fioff; // FPU IP Offset (fip)
+ uint32_t fiseg; // FPU IP Selector (fcs)
+ uint32_t fooff; // FPU Operand Pointer Offset (foo)
+ uint32_t foseg; // FPU Operand Pointer Selector (fos)
+ } i386_; // Added _ in the end to avoid error with gcc defining i386 in some
+ // cases
+ } ptr;
+ uint32_t mxcsr; // MXCSR Register State
+ uint32_t mxcsrmask; // MXCSR Mask
+ MMSReg stmm[8]; // 8*16 bytes for each FP-reg = 128 bytes
+ XMMReg xmm[16]; // 16*16 bytes for each XMM-reg = 256 bytes
+ uint8_t padding1[48];
+ uint64_t xcr0;
+ uint8_t padding2[40];
+};
+
+// Extended floating-point registers
+
+struct YMMHReg {
+ uint8_t bytes[16]; // 16 * 8 bits for the high bytes of each YMM register
+};
+
+struct YMMReg {
+ uint8_t bytes[32]; // 16 * 16 bits for each YMM register
+};
+
+struct YMM {
+ YMMReg ymm[16]; // assembled from ymmh and xmm registers
+};
+
+struct MPXReg {
+ uint8_t bytes[16]; // MPX 128 bit bound registers
+};
+
+struct MPXCsr {
+ uint8_t bytes[8]; // MPX 64 bit bndcfgu and bndstatus registers (collectively
+ // BNDCSR state)
+};
+
+struct MPX {
+ MPXReg mpxr[4];
+ MPXCsr mpxc[2];
+};
+
+LLVM_PACKED_START
+struct XSAVE_HDR {
+ enum class XFeature : uint64_t {
+ FP = 1,
+ SSE = FP << 1,
+ YMM = SSE << 1,
+ BNDREGS = YMM << 1,
+ BNDCSR = BNDREGS << 1,
+ OPMASK = BNDCSR << 1,
+ ZMM_Hi256 = OPMASK << 1,
+ Hi16_ZMM = ZMM_Hi256 << 1,
+ PT = Hi16_ZMM << 1,
+ PKRU = PT << 1,
+ LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue*/ PKRU)
+ };
+
+ XFeature xstate_bv; // OS enabled xstate mask to determine the extended states
+ // supported by the processor
+ XFeature xcomp_bv; // Mask to indicate the format of the XSAVE area and of
+ // the XRSTOR instruction
+ uint64_t reserved1[1];
+ uint64_t reserved2[5];
+};
+static_assert(sizeof(XSAVE_HDR) == 64, "XSAVE_HDR layout incorrect");
+LLVM_PACKED_END
+
+// x86 extensions to FXSAVE (i.e. for AVX and MPX processors)
+LLVM_PACKED_START
+struct XSAVE {
+ FXSAVE i387; // floating point registers typical in i387_fxsave_struct
+ XSAVE_HDR header; // The xsave_hdr_struct can be used to determine if the
+ // following extensions are usable
+ YMMHReg ymmh[16]; // High 16 bytes of each of 16 YMM registers (the low bytes
+ // are in FXSAVE.xmm for compatibility with SSE)
+ uint64_t reserved3[16];
+ MPXReg mpxr[4]; // MPX BNDREG state, containing 128-bit bound registers
+ MPXCsr mpxc[2]; // MPX BNDCSR state, containing 64-bit BNDCFGU and
+ // BNDSTATUS registers
+};
+LLVM_PACKED_END
+
+// Floating-point registers
+union FPR {
+ FXSAVE fxsave; // Generic floating-point registers.
+ XSAVE xsave; // x86 extended processor state.
+};
+
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+// Convenience function to combine YMM register data from XSAVE-style input.
+inline YMMReg XStateToYMM(const void* xmm_bytes, const void* ymmh_bytes) {
+ YMMReg ret;
+
+ ::memcpy(ret.bytes, xmm_bytes, sizeof(XMMReg));
+ ::memcpy(ret.bytes + sizeof(XMMReg), ymmh_bytes, sizeof(YMMHReg));
+
+ return ret;
+}
+
+// Convenience function to copy YMM register data into XSAVE-style output.
+inline void YMMToXState(const YMMReg& input, void* xmm_bytes, void* ymmh_bytes) {
+ ::memcpy(xmm_bytes, input.bytes, sizeof(XMMReg));
+ ::memcpy(ymmh_bytes, input.bytes + sizeof(XMMReg), sizeof(YMMHReg));
+}
+
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoInterface.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoInterface.h
new file mode 100644
index 000000000000..4b58e749adce
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoInterface.h
@@ -0,0 +1,70 @@
+//===-- RegisterInfoInterface.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterInfoInterface_h
+#define lldb_RegisterInfoInterface_h
+
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/lldb-private-types.h"
+#include <vector>
+
+namespace lldb_private {
+
+/// \class RegisterInfoInterface
+///
+/// RegisterInfo interface to patch RegisterInfo structure for archs.
+class RegisterInfoInterface {
+public:
+ RegisterInfoInterface(const lldb_private::ArchSpec &target_arch)
+ : m_target_arch(target_arch) {}
+ virtual ~RegisterInfoInterface() {}
+
+ virtual size_t GetGPRSize() const = 0;
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo() const = 0;
+
+ // Returns the number of registers including the user registers and the
+ // lldb internal registers also
+ virtual uint32_t GetRegisterCount() const = 0;
+
+ // Returns the number of the user registers (excluding the registers
+ // kept for lldb internal use only). Subclasses should override it if
+ // they belongs to an architecture with lldb internal registers.
+ virtual uint32_t GetUserRegisterCount() const { return GetRegisterCount(); }
+
+ const lldb_private::ArchSpec &GetTargetArchitecture() const {
+ return m_target_arch;
+ }
+
+ virtual const lldb_private::RegisterInfo *
+ GetDynamicRegisterInfo(const char *reg_name) const {
+ const std::vector<lldb_private::RegisterInfo> *d_register_infos =
+ GetDynamicRegisterInfoP();
+ if (d_register_infos != nullptr) {
+ std::vector<lldb_private::RegisterInfo>::const_iterator pos =
+ d_register_infos->begin();
+ for (; pos < d_register_infos->end(); pos++) {
+ if (::strcmp(reg_name, pos->name) == 0)
+ return (d_register_infos->data() + (pos - d_register_infos->begin()));
+ }
+ }
+ return nullptr;
+ }
+
+ virtual const std::vector<lldb_private::RegisterInfo> *
+ GetDynamicRegisterInfoP() const {
+ return nullptr;
+ }
+
+public:
+ // FIXME make private.
+ lldb_private::ArchSpec m_target_arch;
+};
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
new file mode 100644
index 000000000000..d392d3be1c41
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
@@ -0,0 +1,91 @@
+//===-- RegisterInfoPOSIX_arm.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include <cassert>
+#include <stddef.h>
+#include <vector>
+
+#include "lldb/lldb-defines.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterInfoPOSIX_arm.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Based on RegisterContextDarwin_arm.cpp
+#define GPR_OFFSET(idx) ((idx)*4)
+#define FPU_OFFSET(idx) ((idx)*4 + sizeof(RegisterInfoPOSIX_arm::GPR))
+#define FPSCR_OFFSET \
+ (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm::FPU, fpscr) + \
+ sizeof(RegisterInfoPOSIX_arm::GPR))
+#define EXC_OFFSET(idx) \
+ ((idx)*4 + sizeof(RegisterInfoPOSIX_arm::GPR) + \
+ sizeof(RegisterInfoPOSIX_arm::FPU))
+#define DBG_OFFSET(reg) \
+ ((LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm::DBG, reg) + \
+ sizeof(RegisterInfoPOSIX_arm::GPR) + sizeof(RegisterInfoPOSIX_arm::FPU) + \
+ sizeof(RegisterInfoPOSIX_arm::EXC)))
+
+#define DEFINE_DBG(reg, i) \
+ #reg, NULL, sizeof(((RegisterInfoPOSIX_arm::DBG *) NULL)->reg[i]), \
+ DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ dbg_##reg##i }, \
+ NULL, NULL, NULL, 0
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterInfoPOSIX_arm::GPR) + sizeof(RegisterInfoPOSIX_arm::FPU) + \
+ sizeof(RegisterInfoPOSIX_arm::EXC))
+
+// Include RegisterInfos_arm to declare our g_register_infos_arm structure.
+#define DECLARE_REGISTER_INFOS_ARM_STRUCT
+#include "RegisterInfos_arm.h"
+#undef DECLARE_REGISTER_INFOS_ARM_STRUCT
+
+static const lldb_private::RegisterInfo *
+GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::arm:
+ return g_register_infos_arm;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::arm:
+ return static_cast<uint32_t>(sizeof(g_register_infos_arm) /
+ sizeof(g_register_infos_arm[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterInfoPOSIX_arm::RegisterInfoPOSIX_arm(
+ const lldb_private::ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)) {}
+
+size_t RegisterInfoPOSIX_arm::GetGPRSize() const {
+ return sizeof(struct RegisterInfoPOSIX_arm::GPR);
+}
+
+const lldb_private::RegisterInfo *
+RegisterInfoPOSIX_arm::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterInfoPOSIX_arm::GetRegisterCount() const {
+ return m_register_info_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
new file mode 100644
index 000000000000..39c2047600aa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
@@ -0,0 +1,61 @@
+//===-- RegisterInfoPOSIX_arm.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterInfoPOSIX_arm_h_
+#define liblldb_RegisterInfoPOSIX_arm_h_
+
+#include "RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+class RegisterInfoPOSIX_arm : public lldb_private::RegisterInfoInterface {
+public:
+ struct GPR {
+ uint32_t r[16]; // R0-R15
+ uint32_t cpsr; // CPSR
+ };
+
+ struct QReg {
+ uint8_t bytes[16];
+ };
+
+ struct FPU {
+ union {
+ uint32_t s[32];
+ uint64_t d[32];
+ QReg q[16]; // the 128-bit NEON registers
+ } floats;
+ uint32_t fpscr;
+ };
+ struct EXC {
+ uint32_t exception;
+ uint32_t fsr; /* Fault status */
+ uint32_t far; /* Virtual Fault Address */
+ };
+
+ struct DBG {
+ uint32_t bvr[16];
+ uint32_t bcr[16];
+ uint32_t wvr[16];
+ uint32_t wcr[16];
+ };
+
+ RegisterInfoPOSIX_arm(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+};
+
+#endif // liblldb_RegisterInfoPOSIX_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
new file mode 100644
index 000000000000..f7471526d054
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
@@ -0,0 +1,96 @@
+//===-- RegisterInfoPOSIX_arm64.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include <cassert>
+#include <stddef.h>
+#include <vector>
+
+#include "lldb/lldb-defines.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterInfoPOSIX_arm64.h"
+
+// Based on RegisterContextDarwin_arm64.cpp
+#define GPR_OFFSET(idx) ((idx)*8)
+#define GPR_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::GPR, reg))
+
+#define FPU_OFFSET(idx) ((idx)*16 + sizeof(RegisterInfoPOSIX_arm64::GPR))
+#define FPU_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::FPU, reg) + \
+ sizeof(RegisterInfoPOSIX_arm64::GPR))
+
+#define EXC_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::EXC, reg) + \
+ sizeof(RegisterInfoPOSIX_arm64::GPR) + \
+ sizeof(RegisterInfoPOSIX_arm64::FPU))
+#define DBG_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::DBG, reg) + \
+ sizeof(RegisterInfoPOSIX_arm64::GPR) + \
+ sizeof(RegisterInfoPOSIX_arm64::FPU) + \
+ sizeof(RegisterInfoPOSIX_arm64::EXC))
+
+#define DEFINE_DBG(reg, i) \
+ #reg, NULL, \
+ sizeof(((RegisterInfoPOSIX_arm64::DBG *) NULL)->reg[i]), \
+ DBG_OFFSET_NAME(reg[i]), lldb::eEncodingUint, lldb::eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ dbg_##reg##i }, \
+ NULL, NULL, NULL, 0
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterInfoPOSIX_arm64::GPR) + \
+ sizeof(RegisterInfoPOSIX_arm64::FPU) + \
+ sizeof(RegisterInfoPOSIX_arm64::EXC))
+
+// Include RegisterInfos_arm64 to declare our g_register_infos_arm64 structure.
+#define DECLARE_REGISTER_INFOS_ARM64_STRUCT
+#include "RegisterInfos_arm64.h"
+#undef DECLARE_REGISTER_INFOS_ARM64_STRUCT
+
+static const lldb_private::RegisterInfo *
+GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ return g_register_infos_arm64_le;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ return static_cast<uint32_t>(sizeof(g_register_infos_arm64_le) /
+ sizeof(g_register_infos_arm64_le[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
+ const lldb_private::ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)) {}
+
+size_t RegisterInfoPOSIX_arm64::GetGPRSize() const {
+ return sizeof(struct RegisterInfoPOSIX_arm64::GPR);
+}
+
+const lldb_private::RegisterInfo *
+RegisterInfoPOSIX_arm64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterInfoPOSIX_arm64::GetRegisterCount() const {
+ return m_register_info_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
new file mode 100644
index 000000000000..ace179a81814
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
@@ -0,0 +1,69 @@
+//===-- RegisterInfoPOSIX_arm64.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextLinux_arm64_H_
+#define liblldb_RegisterContextLinux_arm64_H_
+
+#include "RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+class RegisterInfoPOSIX_arm64 : public lldb_private::RegisterInfoInterface {
+public:
+ // based on RegisterContextDarwin_arm64.h
+ struct GPR {
+ uint64_t x[29]; // x0-x28
+ uint64_t fp; // x29
+ uint64_t lr; // x30
+ uint64_t sp; // x31
+ uint64_t pc; // pc
+ uint32_t cpsr; // cpsr
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct VReg {
+ uint8_t bytes[16];
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct FPU {
+ VReg v[32];
+ uint32_t fpsr;
+ uint32_t fpcr;
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct EXC {
+ uint64_t far; // Virtual Fault Address
+ uint32_t esr; // Exception syndrome
+ uint32_t exception; // number of arm exception token
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct DBG {
+ uint64_t bvr[16];
+ uint64_t bcr[16];
+ uint64_t wvr[16];
+ uint64_t wcr[16];
+ uint64_t mdscr_el1;
+ };
+
+ RegisterInfoPOSIX_arm64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp
new file mode 100644
index 000000000000..35051a3ce095
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp
@@ -0,0 +1,63 @@
+//===-- RegisterInfoPOSIX_ppc64le.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include <cassert>
+#include <stddef.h>
+#include <vector>
+
+#include "lldb/lldb-defines.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterInfoPOSIX_ppc64le.h"
+
+// Include RegisterInfoPOSIX_ppc64le to declare our g_register_infos_ppc64le
+#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+#include "RegisterInfos_ppc64le.h"
+#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+
+static const lldb_private::RegisterInfo *
+GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::ppc64le:
+ return g_register_infos_ppc64le;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::ppc64le:
+ return static_cast<uint32_t>(sizeof(g_register_infos_ppc64le) /
+ sizeof(g_register_infos_ppc64le[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterInfoPOSIX_ppc64le::RegisterInfoPOSIX_ppc64le(
+ const lldb_private::ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)) {}
+
+size_t RegisterInfoPOSIX_ppc64le::GetGPRSize() const {
+ return sizeof(GPR);
+}
+
+const lldb_private::RegisterInfo *
+RegisterInfoPOSIX_ppc64le::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterInfoPOSIX_ppc64le::GetRegisterCount() const {
+ return m_register_info_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h
new file mode 100644
index 000000000000..c4d4d3b546e2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h
@@ -0,0 +1,31 @@
+//===-- RegisterInfoPOSIX_ppc64le.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextLinux_ppc64le_H_
+#define liblldb_RegisterContextLinux_ppc64le_H_
+
+#include "RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+class RegisterInfoPOSIX_ppc64le : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterInfoPOSIX_ppc64le(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
new file mode 100644
index 000000000000..74b9e3b2db32
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
@@ -0,0 +1,1619 @@
+//===-- RegisterInfos_arm.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef DECLARE_REGISTER_INFOS_ARM_STRUCT
+
+#include <stddef.h>
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private.h"
+
+#include "Utility/ARM_DWARF_Registers.h"
+#include "Utility/ARM_ehframe_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#ifndef GPR_OFFSET
+#error GPR_OFFSET must be defined before including this header file
+#endif
+
+#ifndef FPU_OFFSET
+#error FPU_OFFSET must be defined before including this header file
+#endif
+
+#ifndef FPSCR_OFFSET
+#error FPSCR_OFFSET must be defined before including this header file
+#endif
+
+#ifndef EXC_OFFSET
+#error EXC_OFFSET_NAME must be defined before including this header file
+#endif
+
+#ifndef DEFINE_DBG
+#error DEFINE_DBG must be defined before including this header file
+#endif
+
+enum {
+ gpr_r0 = 0,
+ gpr_r1,
+ gpr_r2,
+ gpr_r3,
+ gpr_r4,
+ gpr_r5,
+ gpr_r6,
+ gpr_r7,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13,
+ gpr_sp = gpr_r13,
+ gpr_r14,
+ gpr_lr = gpr_r14,
+ gpr_r15,
+ gpr_pc = gpr_r15,
+ gpr_cpsr,
+
+ fpu_s0,
+ fpu_s1,
+ fpu_s2,
+ fpu_s3,
+ fpu_s4,
+ fpu_s5,
+ fpu_s6,
+ fpu_s7,
+ fpu_s8,
+ fpu_s9,
+ fpu_s10,
+ fpu_s11,
+ fpu_s12,
+ fpu_s13,
+ fpu_s14,
+ fpu_s15,
+ fpu_s16,
+ fpu_s17,
+ fpu_s18,
+ fpu_s19,
+ fpu_s20,
+ fpu_s21,
+ fpu_s22,
+ fpu_s23,
+ fpu_s24,
+ fpu_s25,
+ fpu_s26,
+ fpu_s27,
+ fpu_s28,
+ fpu_s29,
+ fpu_s30,
+ fpu_s31,
+ fpu_fpscr,
+
+ fpu_d0,
+ fpu_d1,
+ fpu_d2,
+ fpu_d3,
+ fpu_d4,
+ fpu_d5,
+ fpu_d6,
+ fpu_d7,
+ fpu_d8,
+ fpu_d9,
+ fpu_d10,
+ fpu_d11,
+ fpu_d12,
+ fpu_d13,
+ fpu_d14,
+ fpu_d15,
+ fpu_d16,
+ fpu_d17,
+ fpu_d18,
+ fpu_d19,
+ fpu_d20,
+ fpu_d21,
+ fpu_d22,
+ fpu_d23,
+ fpu_d24,
+ fpu_d25,
+ fpu_d26,
+ fpu_d27,
+ fpu_d28,
+ fpu_d29,
+ fpu_d30,
+ fpu_d31,
+
+ fpu_q0,
+ fpu_q1,
+ fpu_q2,
+ fpu_q3,
+ fpu_q4,
+ fpu_q5,
+ fpu_q6,
+ fpu_q7,
+ fpu_q8,
+ fpu_q9,
+ fpu_q10,
+ fpu_q11,
+ fpu_q12,
+ fpu_q13,
+ fpu_q14,
+ fpu_q15,
+
+ exc_exception,
+ exc_fsr,
+ exc_far,
+
+ dbg_bvr0,
+ dbg_bvr1,
+ dbg_bvr2,
+ dbg_bvr3,
+ dbg_bvr4,
+ dbg_bvr5,
+ dbg_bvr6,
+ dbg_bvr7,
+ dbg_bvr8,
+ dbg_bvr9,
+ dbg_bvr10,
+ dbg_bvr11,
+ dbg_bvr12,
+ dbg_bvr13,
+ dbg_bvr14,
+ dbg_bvr15,
+
+ dbg_bcr0,
+ dbg_bcr1,
+ dbg_bcr2,
+ dbg_bcr3,
+ dbg_bcr4,
+ dbg_bcr5,
+ dbg_bcr6,
+ dbg_bcr7,
+ dbg_bcr8,
+ dbg_bcr9,
+ dbg_bcr10,
+ dbg_bcr11,
+ dbg_bcr12,
+ dbg_bcr13,
+ dbg_bcr14,
+ dbg_bcr15,
+
+ dbg_wvr0,
+ dbg_wvr1,
+ dbg_wvr2,
+ dbg_wvr3,
+ dbg_wvr4,
+ dbg_wvr5,
+ dbg_wvr6,
+ dbg_wvr7,
+ dbg_wvr8,
+ dbg_wvr9,
+ dbg_wvr10,
+ dbg_wvr11,
+ dbg_wvr12,
+ dbg_wvr13,
+ dbg_wvr14,
+ dbg_wvr15,
+
+ dbg_wcr0,
+ dbg_wcr1,
+ dbg_wcr2,
+ dbg_wcr3,
+ dbg_wcr4,
+ dbg_wcr5,
+ dbg_wcr6,
+ dbg_wcr7,
+ dbg_wcr8,
+ dbg_wcr9,
+ dbg_wcr10,
+ dbg_wcr11,
+ dbg_wcr12,
+ dbg_wcr13,
+ dbg_wcr14,
+ dbg_wcr15,
+
+ k_num_registers
+};
+
+static uint32_t g_s0_invalidates[] = {fpu_d0, fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_s1_invalidates[] = {fpu_d0, fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_s2_invalidates[] = {fpu_d1, fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_s3_invalidates[] = {fpu_d1, fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_s4_invalidates[] = {fpu_d2, fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_s5_invalidates[] = {fpu_d2, fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_s6_invalidates[] = {fpu_d3, fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_s7_invalidates[] = {fpu_d3, fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_s8_invalidates[] = {fpu_d4, fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_s9_invalidates[] = {fpu_d4, fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_s10_invalidates[] = {fpu_d5, fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_s11_invalidates[] = {fpu_d5, fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_s12_invalidates[] = {fpu_d6, fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_s13_invalidates[] = {fpu_d6, fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_s14_invalidates[] = {fpu_d7, fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_s15_invalidates[] = {fpu_d7, fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_s16_invalidates[] = {fpu_d8, fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_s17_invalidates[] = {fpu_d8, fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_s18_invalidates[] = {fpu_d9, fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_s19_invalidates[] = {fpu_d9, fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_s20_invalidates[] = {fpu_d10, fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_s21_invalidates[] = {fpu_d10, fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_s22_invalidates[] = {fpu_d11, fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_s23_invalidates[] = {fpu_d11, fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_s24_invalidates[] = {fpu_d12, fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_s25_invalidates[] = {fpu_d12, fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_s26_invalidates[] = {fpu_d13, fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_s27_invalidates[] = {fpu_d13, fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_s28_invalidates[] = {fpu_d14, fpu_q7, LLDB_INVALID_REGNUM};
+static uint32_t g_s29_invalidates[] = {fpu_d14, fpu_q7, LLDB_INVALID_REGNUM};
+static uint32_t g_s30_invalidates[] = {fpu_d15, fpu_q7, LLDB_INVALID_REGNUM};
+static uint32_t g_s31_invalidates[] = {fpu_d15, fpu_q7, LLDB_INVALID_REGNUM};
+
+static uint32_t g_d0_contains[] = {fpu_s0, fpu_s1, LLDB_INVALID_REGNUM};
+static uint32_t g_d1_contains[] = {fpu_s2, fpu_s3, LLDB_INVALID_REGNUM};
+static uint32_t g_d2_contains[] = {fpu_s4, fpu_s5, LLDB_INVALID_REGNUM};
+static uint32_t g_d3_contains[] = {fpu_s6, fpu_s7, LLDB_INVALID_REGNUM};
+static uint32_t g_d4_contains[] = {fpu_s8, fpu_s9, LLDB_INVALID_REGNUM};
+static uint32_t g_d5_contains[] = {fpu_s10, fpu_s11, LLDB_INVALID_REGNUM};
+static uint32_t g_d6_contains[] = {fpu_s12, fpu_s13, LLDB_INVALID_REGNUM};
+static uint32_t g_d7_contains[] = {fpu_s14, fpu_s15, LLDB_INVALID_REGNUM};
+static uint32_t g_d8_contains[] = {fpu_s16, fpu_s17, LLDB_INVALID_REGNUM};
+static uint32_t g_d9_contains[] = {fpu_s18, fpu_s19, LLDB_INVALID_REGNUM};
+static uint32_t g_d10_contains[] = {fpu_s20, fpu_s21, LLDB_INVALID_REGNUM};
+static uint32_t g_d11_contains[] = {fpu_s22, fpu_s23, LLDB_INVALID_REGNUM};
+static uint32_t g_d12_contains[] = {fpu_s24, fpu_s25, LLDB_INVALID_REGNUM};
+static uint32_t g_d13_contains[] = {fpu_s26, fpu_s27, LLDB_INVALID_REGNUM};
+static uint32_t g_d14_contains[] = {fpu_s28, fpu_s29, LLDB_INVALID_REGNUM};
+static uint32_t g_d15_contains[] = {fpu_s30, fpu_s31, LLDB_INVALID_REGNUM};
+
+static uint32_t g_d0_invalidates[] = {fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_d1_invalidates[] = {fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_d2_invalidates[] = {fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_d3_invalidates[] = {fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_d4_invalidates[] = {fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_d5_invalidates[] = {fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_d6_invalidates[] = {fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_d7_invalidates[] = {fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_d8_invalidates[] = {fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_d9_invalidates[] = {fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_d10_invalidates[] = {fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_d11_invalidates[] = {fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_d12_invalidates[] = {fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_d13_invalidates[] = {fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_d14_invalidates[] = {fpu_q7, LLDB_INVALID_REGNUM};
+static uint32_t g_d15_invalidates[] = {fpu_q7, LLDB_INVALID_REGNUM};
+static uint32_t g_d16_invalidates[] = {fpu_q8, LLDB_INVALID_REGNUM};
+static uint32_t g_d17_invalidates[] = {fpu_q8, LLDB_INVALID_REGNUM};
+static uint32_t g_d18_invalidates[] = {fpu_q9, LLDB_INVALID_REGNUM};
+static uint32_t g_d19_invalidates[] = {fpu_q9, LLDB_INVALID_REGNUM};
+static uint32_t g_d20_invalidates[] = {fpu_q10, LLDB_INVALID_REGNUM};
+static uint32_t g_d21_invalidates[] = {fpu_q10, LLDB_INVALID_REGNUM};
+static uint32_t g_d22_invalidates[] = {fpu_q11, LLDB_INVALID_REGNUM};
+static uint32_t g_d23_invalidates[] = {fpu_q11, LLDB_INVALID_REGNUM};
+static uint32_t g_d24_invalidates[] = {fpu_q12, LLDB_INVALID_REGNUM};
+static uint32_t g_d25_invalidates[] = {fpu_q12, LLDB_INVALID_REGNUM};
+static uint32_t g_d26_invalidates[] = {fpu_q13, LLDB_INVALID_REGNUM};
+static uint32_t g_d27_invalidates[] = {fpu_q13, LLDB_INVALID_REGNUM};
+static uint32_t g_d28_invalidates[] = {fpu_q14, LLDB_INVALID_REGNUM};
+static uint32_t g_d29_invalidates[] = {fpu_q14, LLDB_INVALID_REGNUM};
+static uint32_t g_d30_invalidates[] = {fpu_q15, LLDB_INVALID_REGNUM};
+static uint32_t g_d31_invalidates[] = {fpu_q15, LLDB_INVALID_REGNUM};
+
+static uint32_t g_q0_contains[] = {
+ fpu_d0, fpu_d1, fpu_s0, fpu_s1, fpu_s2, fpu_s3, LLDB_INVALID_REGNUM};
+static uint32_t g_q1_contains[] = {
+ fpu_d2, fpu_d3, fpu_s4, fpu_s5, fpu_s6, fpu_s7, LLDB_INVALID_REGNUM};
+static uint32_t g_q2_contains[] = {
+ fpu_d4, fpu_d5, fpu_s8, fpu_s9, fpu_s10, fpu_s11, LLDB_INVALID_REGNUM};
+static uint32_t g_q3_contains[] = {
+ fpu_d6, fpu_d7, fpu_s12, fpu_s13, fpu_s14, fpu_s15, LLDB_INVALID_REGNUM};
+static uint32_t g_q4_contains[] = {
+ fpu_d8, fpu_d9, fpu_s16, fpu_s17, fpu_s18, fpu_s19, LLDB_INVALID_REGNUM};
+static uint32_t g_q5_contains[] = {
+ fpu_d10, fpu_d11, fpu_s20, fpu_s21, fpu_s22, fpu_s23, LLDB_INVALID_REGNUM};
+static uint32_t g_q6_contains[] = {
+ fpu_d12, fpu_d13, fpu_s24, fpu_s25, fpu_s26, fpu_s27, LLDB_INVALID_REGNUM};
+static uint32_t g_q7_contains[] = {
+ fpu_d14, fpu_d15, fpu_s28, fpu_s29, fpu_s30, fpu_s31, LLDB_INVALID_REGNUM};
+static uint32_t g_q8_contains[] = {fpu_d16, fpu_d17, LLDB_INVALID_REGNUM};
+static uint32_t g_q9_contains[] = {fpu_d18, fpu_d19, LLDB_INVALID_REGNUM};
+static uint32_t g_q10_contains[] = {fpu_d20, fpu_d21, LLDB_INVALID_REGNUM};
+static uint32_t g_q11_contains[] = {fpu_d22, fpu_d23, LLDB_INVALID_REGNUM};
+static uint32_t g_q12_contains[] = {fpu_d24, fpu_d25, LLDB_INVALID_REGNUM};
+static uint32_t g_q13_contains[] = {fpu_d26, fpu_d27, LLDB_INVALID_REGNUM};
+static uint32_t g_q14_contains[] = {fpu_d28, fpu_d29, LLDB_INVALID_REGNUM};
+static uint32_t g_q15_contains[] = {fpu_d30, fpu_d31, LLDB_INVALID_REGNUM};
+
+static RegisterInfo g_register_infos_arm[] = {
+ // NAME ALT SZ OFFSET ENCODING FORMAT
+ // EH_FRAME DWARF GENERIC
+ // PROCESS PLUGIN LLDB NATIVE VALUE REGS INVALIDATE REGS
+ // =========== ======= == ============== ================
+ // ==================== =================== ===================
+ // ========================== =================== =============
+ // ============== =================
+ {"r0",
+ nullptr,
+ 4,
+ GPR_OFFSET(0),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ gpr_r0},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ nullptr,
+ 4,
+ GPR_OFFSET(1),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ gpr_r1},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ nullptr,
+ 4,
+ GPR_OFFSET(2),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ gpr_r2},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ nullptr,
+ 4,
+ GPR_OFFSET(3),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ gpr_r3},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ nullptr,
+ 4,
+ GPR_OFFSET(4),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r4},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ nullptr,
+ 4,
+ GPR_OFFSET(5),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r5},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ nullptr,
+ 4,
+ GPR_OFFSET(6),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r6},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ nullptr,
+ 4,
+ GPR_OFFSET(7),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r7, dwarf_r7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r7},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ nullptr,
+ 4,
+ GPR_OFFSET(8),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r8},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ nullptr,
+ 4,
+ GPR_OFFSET(9),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r9},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ nullptr,
+ 4,
+ GPR_OFFSET(10),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_r10},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ nullptr,
+ 4,
+ GPR_OFFSET(11),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r11, dwarf_r11, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ gpr_r11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 4,
+ GPR_OFFSET(12),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_r12},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "r13",
+ 4,
+ GPR_OFFSET(13),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ gpr_sp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r14",
+ 4,
+ GPR_OFFSET(14),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ gpr_lr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "r15",
+ 4,
+ GPR_OFFSET(15),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ gpr_pc},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ GPR_OFFSET(16),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ gpr_cpsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"s0",
+ nullptr,
+ 4,
+ FPU_OFFSET(0),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s0},
+ nullptr,
+ g_s0_invalidates,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ FPU_OFFSET(1),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s1},
+ nullptr,
+ g_s1_invalidates,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ FPU_OFFSET(2),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s2},
+ nullptr,
+ g_s2_invalidates,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ FPU_OFFSET(3),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s3},
+ nullptr,
+ g_s3_invalidates,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ FPU_OFFSET(4),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s4},
+ nullptr,
+ g_s4_invalidates,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ FPU_OFFSET(5),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s5},
+ nullptr,
+ g_s5_invalidates,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ FPU_OFFSET(6),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s6},
+ nullptr,
+ g_s6_invalidates,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ FPU_OFFSET(7),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s7},
+ nullptr,
+ g_s7_invalidates,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ FPU_OFFSET(8),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s8},
+ nullptr,
+ g_s8_invalidates,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ FPU_OFFSET(9),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s9},
+ nullptr,
+ g_s9_invalidates,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ FPU_OFFSET(10),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s10},
+ nullptr,
+ g_s10_invalidates,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ FPU_OFFSET(11),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s11},
+ nullptr,
+ g_s11_invalidates,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ FPU_OFFSET(12),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s12},
+ nullptr,
+ g_s12_invalidates,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ FPU_OFFSET(13),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s13},
+ nullptr,
+ g_s13_invalidates,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ FPU_OFFSET(14),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s14},
+ nullptr,
+ g_s14_invalidates,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ FPU_OFFSET(15),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s15},
+ nullptr,
+ g_s15_invalidates,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ FPU_OFFSET(16),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s16},
+ nullptr,
+ g_s16_invalidates,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ FPU_OFFSET(17),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s17},
+ nullptr,
+ g_s17_invalidates,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ FPU_OFFSET(18),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s18},
+ nullptr,
+ g_s18_invalidates,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ FPU_OFFSET(19),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s19},
+ nullptr,
+ g_s19_invalidates,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ FPU_OFFSET(20),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s20},
+ nullptr,
+ g_s20_invalidates,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ FPU_OFFSET(21),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s21},
+ nullptr,
+ g_s21_invalidates,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ FPU_OFFSET(22),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s22},
+ nullptr,
+ g_s22_invalidates,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ FPU_OFFSET(23),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s23},
+ nullptr,
+ g_s23_invalidates,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ FPU_OFFSET(24),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s24},
+ nullptr,
+ g_s24_invalidates,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ FPU_OFFSET(25),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s25},
+ nullptr,
+ g_s25_invalidates,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ FPU_OFFSET(26),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s26},
+ nullptr,
+ g_s26_invalidates,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ FPU_OFFSET(27),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s27},
+ nullptr,
+ g_s27_invalidates,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ FPU_OFFSET(28),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s28},
+ nullptr,
+ g_s28_invalidates,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ FPU_OFFSET(29),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s29},
+ nullptr,
+ g_s29_invalidates,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ FPU_OFFSET(30),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s30},
+ nullptr,
+ g_s30_invalidates,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ FPU_OFFSET(31),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s31},
+ nullptr,
+ g_s31_invalidates,
+ nullptr,
+ 0},
+ {"fpscr",
+ nullptr,
+ 4,
+ FPSCR_OFFSET,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fpscr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"d0",
+ nullptr,
+ 8,
+ FPU_OFFSET(0),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d0},
+ g_d0_contains,
+ g_d0_invalidates,
+ nullptr,
+ 0},
+ {"d1",
+ nullptr,
+ 8,
+ FPU_OFFSET(2),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d1},
+ g_d1_contains,
+ g_d1_invalidates,
+ nullptr,
+ 0},
+ {"d2",
+ nullptr,
+ 8,
+ FPU_OFFSET(4),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d2},
+ g_d2_contains,
+ g_d2_invalidates,
+ nullptr,
+ 0},
+ {"d3",
+ nullptr,
+ 8,
+ FPU_OFFSET(6),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d3},
+ g_d3_contains,
+ g_d3_invalidates,
+ nullptr,
+ 0},
+ {"d4",
+ nullptr,
+ 8,
+ FPU_OFFSET(8),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d4},
+ g_d4_contains,
+ g_d4_invalidates,
+ nullptr,
+ 0},
+ {"d5",
+ nullptr,
+ 8,
+ FPU_OFFSET(10),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d5},
+ g_d5_contains,
+ g_d5_invalidates,
+ nullptr,
+ 0},
+ {"d6",
+ nullptr,
+ 8,
+ FPU_OFFSET(12),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d6},
+ g_d6_contains,
+ g_d6_invalidates,
+ nullptr,
+ 0},
+ {"d7",
+ nullptr,
+ 8,
+ FPU_OFFSET(14),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d7},
+ g_d7_contains,
+ g_d7_invalidates,
+ nullptr,
+ 0},
+ {"d8",
+ nullptr,
+ 8,
+ FPU_OFFSET(16),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d8},
+ g_d8_contains,
+ g_d8_invalidates,
+ nullptr,
+ 0},
+ {"d9",
+ nullptr,
+ 8,
+ FPU_OFFSET(18),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d9},
+ g_d9_contains,
+ g_d9_invalidates,
+ nullptr,
+ 0},
+ {"d10",
+ nullptr,
+ 8,
+ FPU_OFFSET(20),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d10},
+ g_d10_contains,
+ g_d10_invalidates,
+ nullptr,
+ 0},
+ {"d11",
+ nullptr,
+ 8,
+ FPU_OFFSET(22),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d11},
+ g_d11_contains,
+ g_d11_invalidates,
+ nullptr,
+ 0},
+ {"d12",
+ nullptr,
+ 8,
+ FPU_OFFSET(24),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d12},
+ g_d12_contains,
+ g_d12_invalidates,
+ nullptr,
+ 0},
+ {"d13",
+ nullptr,
+ 8,
+ FPU_OFFSET(26),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d13},
+ g_d13_contains,
+ g_d13_invalidates,
+ nullptr,
+ 0},
+ {"d14",
+ nullptr,
+ 8,
+ FPU_OFFSET(28),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d14},
+ g_d14_contains,
+ g_d14_invalidates,
+ nullptr,
+ 0},
+ {"d15",
+ nullptr,
+ 8,
+ FPU_OFFSET(30),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d15},
+ g_d15_contains,
+ g_d15_invalidates,
+ nullptr,
+ 0},
+ {"d16",
+ nullptr,
+ 8,
+ FPU_OFFSET(32),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d16},
+ nullptr,
+ g_d16_invalidates,
+ nullptr,
+ 0},
+ {"d17",
+ nullptr,
+ 8,
+ FPU_OFFSET(34),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d17},
+ nullptr,
+ g_d17_invalidates,
+ nullptr,
+ 0},
+ {"d18",
+ nullptr,
+ 8,
+ FPU_OFFSET(36),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d18},
+ nullptr,
+ g_d18_invalidates,
+ nullptr,
+ 0},
+ {"d19",
+ nullptr,
+ 8,
+ FPU_OFFSET(38),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d19},
+ nullptr,
+ g_d19_invalidates,
+ nullptr,
+ 0},
+ {"d20",
+ nullptr,
+ 8,
+ FPU_OFFSET(40),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d20},
+ nullptr,
+ g_d20_invalidates,
+ nullptr,
+ 0},
+ {"d21",
+ nullptr,
+ 8,
+ FPU_OFFSET(42),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d21},
+ nullptr,
+ g_d21_invalidates,
+ nullptr,
+ 0},
+ {"d22",
+ nullptr,
+ 8,
+ FPU_OFFSET(44),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d22},
+ nullptr,
+ g_d22_invalidates,
+ nullptr,
+ 0},
+ {"d23",
+ nullptr,
+ 8,
+ FPU_OFFSET(46),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d23},
+ nullptr,
+ g_d23_invalidates,
+ nullptr,
+ 0},
+ {"d24",
+ nullptr,
+ 8,
+ FPU_OFFSET(48),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d24},
+ nullptr,
+ g_d24_invalidates,
+ nullptr,
+ 0},
+ {"d25",
+ nullptr,
+ 8,
+ FPU_OFFSET(50),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d25},
+ nullptr,
+ g_d25_invalidates,
+ nullptr,
+ 0},
+ {"d26",
+ nullptr,
+ 8,
+ FPU_OFFSET(52),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d26},
+ nullptr,
+ g_d26_invalidates,
+ nullptr,
+ 0},
+ {"d27",
+ nullptr,
+ 8,
+ FPU_OFFSET(54),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d27},
+ nullptr,
+ g_d27_invalidates,
+ nullptr,
+ 0},
+ {"d28",
+ nullptr,
+ 8,
+ FPU_OFFSET(56),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d28},
+ nullptr,
+ g_d28_invalidates,
+ nullptr,
+ 0},
+ {"d29",
+ nullptr,
+ 8,
+ FPU_OFFSET(58),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d29},
+ nullptr,
+ g_d29_invalidates,
+ nullptr,
+ 0},
+ {"d30",
+ nullptr,
+ 8,
+ FPU_OFFSET(60),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d30},
+ nullptr,
+ g_d30_invalidates,
+ nullptr,
+ 0},
+ {"d31",
+ nullptr,
+ 8,
+ FPU_OFFSET(62),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d31},
+ nullptr,
+ g_d31_invalidates,
+ nullptr,
+ 0},
+
+ {"q0",
+ nullptr,
+ 16,
+ FPU_OFFSET(0),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q0},
+ g_q0_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q1",
+ nullptr,
+ 16,
+ FPU_OFFSET(4),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q1},
+ g_q1_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q2",
+ nullptr,
+ 16,
+ FPU_OFFSET(8),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q2},
+ g_q2_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q3",
+ nullptr,
+ 16,
+ FPU_OFFSET(12),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q3},
+ g_q3_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q4",
+ nullptr,
+ 16,
+ FPU_OFFSET(16),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q4},
+ g_q4_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q5",
+ nullptr,
+ 16,
+ FPU_OFFSET(20),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q5},
+ g_q5_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q6",
+ nullptr,
+ 16,
+ FPU_OFFSET(24),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q6},
+ g_q6_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q7",
+ nullptr,
+ 16,
+ FPU_OFFSET(28),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q7},
+ g_q7_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q8",
+ nullptr,
+ 16,
+ FPU_OFFSET(32),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q8},
+ g_q8_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q9",
+ nullptr,
+ 16,
+ FPU_OFFSET(36),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q9},
+ g_q9_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q10",
+ nullptr,
+ 16,
+ FPU_OFFSET(40),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q10},
+ g_q10_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q11",
+ nullptr,
+ 16,
+ FPU_OFFSET(44),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q11},
+ g_q11_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q12",
+ nullptr,
+ 16,
+ FPU_OFFSET(48),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q12},
+ g_q12_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q13",
+ nullptr,
+ 16,
+ FPU_OFFSET(52),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q13},
+ g_q13_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q14",
+ nullptr,
+ 16,
+ FPU_OFFSET(56),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q14},
+ g_q14_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q15",
+ nullptr,
+ 16,
+ FPU_OFFSET(60),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q15},
+ g_q15_contains,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"exception",
+ nullptr,
+ 4,
+ EXC_OFFSET(0),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_exception},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fsr",
+ nullptr,
+ 4,
+ EXC_OFFSET(1),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_fsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"far",
+ nullptr,
+ 4,
+ EXC_OFFSET(2),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_far},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {DEFINE_DBG(bvr, 0)},
+ {DEFINE_DBG(bvr, 1)},
+ {DEFINE_DBG(bvr, 2)},
+ {DEFINE_DBG(bvr, 3)},
+ {DEFINE_DBG(bvr, 4)},
+ {DEFINE_DBG(bvr, 5)},
+ {DEFINE_DBG(bvr, 6)},
+ {DEFINE_DBG(bvr, 7)},
+ {DEFINE_DBG(bvr, 8)},
+ {DEFINE_DBG(bvr, 9)},
+ {DEFINE_DBG(bvr, 10)},
+ {DEFINE_DBG(bvr, 11)},
+ {DEFINE_DBG(bvr, 12)},
+ {DEFINE_DBG(bvr, 13)},
+ {DEFINE_DBG(bvr, 14)},
+ {DEFINE_DBG(bvr, 15)},
+
+ {DEFINE_DBG(bcr, 0)},
+ {DEFINE_DBG(bcr, 1)},
+ {DEFINE_DBG(bcr, 2)},
+ {DEFINE_DBG(bcr, 3)},
+ {DEFINE_DBG(bcr, 4)},
+ {DEFINE_DBG(bcr, 5)},
+ {DEFINE_DBG(bcr, 6)},
+ {DEFINE_DBG(bcr, 7)},
+ {DEFINE_DBG(bcr, 8)},
+ {DEFINE_DBG(bcr, 9)},
+ {DEFINE_DBG(bcr, 10)},
+ {DEFINE_DBG(bcr, 11)},
+ {DEFINE_DBG(bcr, 12)},
+ {DEFINE_DBG(bcr, 13)},
+ {DEFINE_DBG(bcr, 14)},
+ {DEFINE_DBG(bcr, 15)},
+
+ {DEFINE_DBG(wvr, 0)},
+ {DEFINE_DBG(wvr, 1)},
+ {DEFINE_DBG(wvr, 2)},
+ {DEFINE_DBG(wvr, 3)},
+ {DEFINE_DBG(wvr, 4)},
+ {DEFINE_DBG(wvr, 5)},
+ {DEFINE_DBG(wvr, 6)},
+ {DEFINE_DBG(wvr, 7)},
+ {DEFINE_DBG(wvr, 8)},
+ {DEFINE_DBG(wvr, 9)},
+ {DEFINE_DBG(wvr, 10)},
+ {DEFINE_DBG(wvr, 11)},
+ {DEFINE_DBG(wvr, 12)},
+ {DEFINE_DBG(wvr, 13)},
+ {DEFINE_DBG(wvr, 14)},
+ {DEFINE_DBG(wvr, 15)},
+
+ {DEFINE_DBG(wcr, 0)},
+ {DEFINE_DBG(wcr, 1)},
+ {DEFINE_DBG(wcr, 2)},
+ {DEFINE_DBG(wcr, 3)},
+ {DEFINE_DBG(wcr, 4)},
+ {DEFINE_DBG(wcr, 5)},
+ {DEFINE_DBG(wcr, 6)},
+ {DEFINE_DBG(wcr, 7)},
+ {DEFINE_DBG(wcr, 8)},
+ {DEFINE_DBG(wcr, 9)},
+ {DEFINE_DBG(wcr, 10)},
+ {DEFINE_DBG(wcr, 11)},
+ {DEFINE_DBG(wcr, 12)},
+ {DEFINE_DBG(wcr, 13)},
+ {DEFINE_DBG(wcr, 14)},
+ {DEFINE_DBG(wcr, 15)}};
+
+#endif // DECLARE_REGISTER_INFOS_ARM_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h
new file mode 100644
index 000000000000..4ee0b528f229
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h
@@ -0,0 +1,712 @@
+//===-- RegisterInfos_arm64.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef DECLARE_REGISTER_INFOS_ARM64_STRUCT
+
+#include <stddef.h>
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private.h"
+
+#include "Utility/ARM64_DWARF_Registers.h"
+#include "Utility/ARM64_ehframe_Registers.h"
+
+#ifndef GPR_OFFSET
+#error GPR_OFFSET must be defined before including this header file
+#endif
+
+#ifndef GPR_OFFSET_NAME
+#error GPR_OFFSET_NAME must be defined before including this header file
+#endif
+
+#ifndef FPU_OFFSET
+#error FPU_OFFSET must be defined before including this header file
+#endif
+
+#ifndef FPU_OFFSET_NAME
+#error FPU_OFFSET_NAME must be defined before including this header file
+#endif
+
+#ifndef EXC_OFFSET_NAME
+#error EXC_OFFSET_NAME must be defined before including this header file
+#endif
+
+#ifndef DBG_OFFSET_NAME
+#error DBG_OFFSET_NAME must be defined before including this header file
+#endif
+
+#ifndef DEFINE_DBG
+#error DEFINE_DBG must be defined before including this header file
+#endif
+
+// Offsets for a little-endian layout of the register context
+#define GPR_W_PSEUDO_REG_ENDIAN_OFFSET 0
+#define FPU_S_PSEUDO_REG_ENDIAN_OFFSET 0
+#define FPU_D_PSEUDO_REG_ENDIAN_OFFSET 0
+
+enum {
+ gpr_x0 = 0,
+ gpr_x1,
+ gpr_x2,
+ gpr_x3,
+ gpr_x4,
+ gpr_x5,
+ gpr_x6,
+ gpr_x7,
+ gpr_x8,
+ gpr_x9,
+ gpr_x10,
+ gpr_x11,
+ gpr_x12,
+ gpr_x13,
+ gpr_x14,
+ gpr_x15,
+ gpr_x16,
+ gpr_x17,
+ gpr_x18,
+ gpr_x19,
+ gpr_x20,
+ gpr_x21,
+ gpr_x22,
+ gpr_x23,
+ gpr_x24,
+ gpr_x25,
+ gpr_x26,
+ gpr_x27,
+ gpr_x28,
+ gpr_x29 = 29,
+ gpr_fp = gpr_x29,
+ gpr_x30 = 30,
+ gpr_lr = gpr_x30,
+ gpr_ra = gpr_x30,
+ gpr_x31 = 31,
+ gpr_sp = gpr_x31,
+ gpr_pc = 32,
+ gpr_cpsr,
+
+ gpr_w0,
+ gpr_w1,
+ gpr_w2,
+ gpr_w3,
+ gpr_w4,
+ gpr_w5,
+ gpr_w6,
+ gpr_w7,
+ gpr_w8,
+ gpr_w9,
+ gpr_w10,
+ gpr_w11,
+ gpr_w12,
+ gpr_w13,
+ gpr_w14,
+ gpr_w15,
+ gpr_w16,
+ gpr_w17,
+ gpr_w18,
+ gpr_w19,
+ gpr_w20,
+ gpr_w21,
+ gpr_w22,
+ gpr_w23,
+ gpr_w24,
+ gpr_w25,
+ gpr_w26,
+ gpr_w27,
+ gpr_w28,
+
+ fpu_v0,
+ fpu_v1,
+ fpu_v2,
+ fpu_v3,
+ fpu_v4,
+ fpu_v5,
+ fpu_v6,
+ fpu_v7,
+ fpu_v8,
+ fpu_v9,
+ fpu_v10,
+ fpu_v11,
+ fpu_v12,
+ fpu_v13,
+ fpu_v14,
+ fpu_v15,
+ fpu_v16,
+ fpu_v17,
+ fpu_v18,
+ fpu_v19,
+ fpu_v20,
+ fpu_v21,
+ fpu_v22,
+ fpu_v23,
+ fpu_v24,
+ fpu_v25,
+ fpu_v26,
+ fpu_v27,
+ fpu_v28,
+ fpu_v29,
+ fpu_v30,
+ fpu_v31,
+
+ fpu_s0,
+ fpu_s1,
+ fpu_s2,
+ fpu_s3,
+ fpu_s4,
+ fpu_s5,
+ fpu_s6,
+ fpu_s7,
+ fpu_s8,
+ fpu_s9,
+ fpu_s10,
+ fpu_s11,
+ fpu_s12,
+ fpu_s13,
+ fpu_s14,
+ fpu_s15,
+ fpu_s16,
+ fpu_s17,
+ fpu_s18,
+ fpu_s19,
+ fpu_s20,
+ fpu_s21,
+ fpu_s22,
+ fpu_s23,
+ fpu_s24,
+ fpu_s25,
+ fpu_s26,
+ fpu_s27,
+ fpu_s28,
+ fpu_s29,
+ fpu_s30,
+ fpu_s31,
+
+ fpu_d0,
+ fpu_d1,
+ fpu_d2,
+ fpu_d3,
+ fpu_d4,
+ fpu_d5,
+ fpu_d6,
+ fpu_d7,
+ fpu_d8,
+ fpu_d9,
+ fpu_d10,
+ fpu_d11,
+ fpu_d12,
+ fpu_d13,
+ fpu_d14,
+ fpu_d15,
+ fpu_d16,
+ fpu_d17,
+ fpu_d18,
+ fpu_d19,
+ fpu_d20,
+ fpu_d21,
+ fpu_d22,
+ fpu_d23,
+ fpu_d24,
+ fpu_d25,
+ fpu_d26,
+ fpu_d27,
+ fpu_d28,
+ fpu_d29,
+ fpu_d30,
+ fpu_d31,
+
+ fpu_fpsr,
+ fpu_fpcr,
+
+ exc_far,
+ exc_esr,
+ exc_exception,
+
+ dbg_bvr0,
+ dbg_bvr1,
+ dbg_bvr2,
+ dbg_bvr3,
+ dbg_bvr4,
+ dbg_bvr5,
+ dbg_bvr6,
+ dbg_bvr7,
+ dbg_bvr8,
+ dbg_bvr9,
+ dbg_bvr10,
+ dbg_bvr11,
+ dbg_bvr12,
+ dbg_bvr13,
+ dbg_bvr14,
+ dbg_bvr15,
+
+ dbg_bcr0,
+ dbg_bcr1,
+ dbg_bcr2,
+ dbg_bcr3,
+ dbg_bcr4,
+ dbg_bcr5,
+ dbg_bcr6,
+ dbg_bcr7,
+ dbg_bcr8,
+ dbg_bcr9,
+ dbg_bcr10,
+ dbg_bcr11,
+ dbg_bcr12,
+ dbg_bcr13,
+ dbg_bcr14,
+ dbg_bcr15,
+
+ dbg_wvr0,
+ dbg_wvr1,
+ dbg_wvr2,
+ dbg_wvr3,
+ dbg_wvr4,
+ dbg_wvr5,
+ dbg_wvr6,
+ dbg_wvr7,
+ dbg_wvr8,
+ dbg_wvr9,
+ dbg_wvr10,
+ dbg_wvr11,
+ dbg_wvr12,
+ dbg_wvr13,
+ dbg_wvr14,
+ dbg_wvr15,
+
+ dbg_wcr0,
+ dbg_wcr1,
+ dbg_wcr2,
+ dbg_wcr3,
+ dbg_wcr4,
+ dbg_wcr5,
+ dbg_wcr6,
+ dbg_wcr7,
+ dbg_wcr8,
+ dbg_wcr9,
+ dbg_wcr10,
+ dbg_wcr11,
+ dbg_wcr12,
+ dbg_wcr13,
+ dbg_wcr14,
+ dbg_wcr15,
+
+ k_num_registers
+};
+
+static uint32_t g_contained_x0[] = {gpr_x0, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x1[] = {gpr_x1, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x2[] = {gpr_x2, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x3[] = {gpr_x3, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x4[] = {gpr_x4, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x5[] = {gpr_x5, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x6[] = {gpr_x6, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x7[] = {gpr_x7, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x8[] = {gpr_x8, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x9[] = {gpr_x9, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x10[] = {gpr_x10, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x11[] = {gpr_x11, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x12[] = {gpr_x12, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x13[] = {gpr_x13, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x14[] = {gpr_x14, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x15[] = {gpr_x15, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x16[] = {gpr_x16, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x17[] = {gpr_x17, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x18[] = {gpr_x18, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x19[] = {gpr_x19, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x20[] = {gpr_x20, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x21[] = {gpr_x21, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x22[] = {gpr_x22, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x23[] = {gpr_x23, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x24[] = {gpr_x24, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x25[] = {gpr_x25, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x26[] = {gpr_x26, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x27[] = {gpr_x27, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x28[] = {gpr_x28, LLDB_INVALID_REGNUM};
+
+static uint32_t g_w0_invalidates[] = {gpr_x0, LLDB_INVALID_REGNUM};
+static uint32_t g_w1_invalidates[] = {gpr_x1, LLDB_INVALID_REGNUM};
+static uint32_t g_w2_invalidates[] = {gpr_x2, LLDB_INVALID_REGNUM};
+static uint32_t g_w3_invalidates[] = {gpr_x3, LLDB_INVALID_REGNUM};
+static uint32_t g_w4_invalidates[] = {gpr_x4, LLDB_INVALID_REGNUM};
+static uint32_t g_w5_invalidates[] = {gpr_x5, LLDB_INVALID_REGNUM};
+static uint32_t g_w6_invalidates[] = {gpr_x6, LLDB_INVALID_REGNUM};
+static uint32_t g_w7_invalidates[] = {gpr_x7, LLDB_INVALID_REGNUM};
+static uint32_t g_w8_invalidates[] = {gpr_x8, LLDB_INVALID_REGNUM};
+static uint32_t g_w9_invalidates[] = {gpr_x9, LLDB_INVALID_REGNUM};
+static uint32_t g_w10_invalidates[] = {gpr_x10, LLDB_INVALID_REGNUM};
+static uint32_t g_w11_invalidates[] = {gpr_x11, LLDB_INVALID_REGNUM};
+static uint32_t g_w12_invalidates[] = {gpr_x12, LLDB_INVALID_REGNUM};
+static uint32_t g_w13_invalidates[] = {gpr_x13, LLDB_INVALID_REGNUM};
+static uint32_t g_w14_invalidates[] = {gpr_x14, LLDB_INVALID_REGNUM};
+static uint32_t g_w15_invalidates[] = {gpr_x15, LLDB_INVALID_REGNUM};
+static uint32_t g_w16_invalidates[] = {gpr_x16, LLDB_INVALID_REGNUM};
+static uint32_t g_w17_invalidates[] = {gpr_x17, LLDB_INVALID_REGNUM};
+static uint32_t g_w18_invalidates[] = {gpr_x18, LLDB_INVALID_REGNUM};
+static uint32_t g_w19_invalidates[] = {gpr_x19, LLDB_INVALID_REGNUM};
+static uint32_t g_w20_invalidates[] = {gpr_x20, LLDB_INVALID_REGNUM};
+static uint32_t g_w21_invalidates[] = {gpr_x21, LLDB_INVALID_REGNUM};
+static uint32_t g_w22_invalidates[] = {gpr_x22, LLDB_INVALID_REGNUM};
+static uint32_t g_w23_invalidates[] = {gpr_x23, LLDB_INVALID_REGNUM};
+static uint32_t g_w24_invalidates[] = {gpr_x24, LLDB_INVALID_REGNUM};
+static uint32_t g_w25_invalidates[] = {gpr_x25, LLDB_INVALID_REGNUM};
+static uint32_t g_w26_invalidates[] = {gpr_x26, LLDB_INVALID_REGNUM};
+static uint32_t g_w27_invalidates[] = {gpr_x27, LLDB_INVALID_REGNUM};
+static uint32_t g_w28_invalidates[] = {gpr_x28, LLDB_INVALID_REGNUM};
+
+static uint32_t g_contained_v0[] = {fpu_v0, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v1[] = {fpu_v1, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v2[] = {fpu_v2, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v3[] = {fpu_v3, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v4[] = {fpu_v4, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v5[] = {fpu_v5, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v6[] = {fpu_v6, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v7[] = {fpu_v7, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v8[] = {fpu_v8, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v9[] = {fpu_v9, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v10[] = {fpu_v10, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v11[] = {fpu_v11, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v12[] = {fpu_v12, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v13[] = {fpu_v13, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v14[] = {fpu_v14, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v15[] = {fpu_v15, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v16[] = {fpu_v16, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v17[] = {fpu_v17, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v18[] = {fpu_v18, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v19[] = {fpu_v19, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v20[] = {fpu_v20, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v21[] = {fpu_v21, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v22[] = {fpu_v22, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v23[] = {fpu_v23, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v24[] = {fpu_v24, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v25[] = {fpu_v25, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v26[] = {fpu_v26, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v27[] = {fpu_v27, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v28[] = {fpu_v28, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v29[] = {fpu_v29, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v30[] = {fpu_v30, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v31[] = {fpu_v31, LLDB_INVALID_REGNUM};
+
+static uint32_t g_s0_invalidates[] = {fpu_v0, fpu_d0, LLDB_INVALID_REGNUM};
+static uint32_t g_s1_invalidates[] = {fpu_v1, fpu_d1, LLDB_INVALID_REGNUM};
+static uint32_t g_s2_invalidates[] = {fpu_v2, fpu_d2, LLDB_INVALID_REGNUM};
+static uint32_t g_s3_invalidates[] = {fpu_v3, fpu_d3, LLDB_INVALID_REGNUM};
+static uint32_t g_s4_invalidates[] = {fpu_v4, fpu_d4, LLDB_INVALID_REGNUM};
+static uint32_t g_s5_invalidates[] = {fpu_v5, fpu_d5, LLDB_INVALID_REGNUM};
+static uint32_t g_s6_invalidates[] = {fpu_v6, fpu_d6, LLDB_INVALID_REGNUM};
+static uint32_t g_s7_invalidates[] = {fpu_v7, fpu_d7, LLDB_INVALID_REGNUM};
+static uint32_t g_s8_invalidates[] = {fpu_v8, fpu_d8, LLDB_INVALID_REGNUM};
+static uint32_t g_s9_invalidates[] = {fpu_v9, fpu_d9, LLDB_INVALID_REGNUM};
+static uint32_t g_s10_invalidates[] = {fpu_v10, fpu_d10, LLDB_INVALID_REGNUM};
+static uint32_t g_s11_invalidates[] = {fpu_v11, fpu_d11, LLDB_INVALID_REGNUM};
+static uint32_t g_s12_invalidates[] = {fpu_v12, fpu_d12, LLDB_INVALID_REGNUM};
+static uint32_t g_s13_invalidates[] = {fpu_v13, fpu_d13, LLDB_INVALID_REGNUM};
+static uint32_t g_s14_invalidates[] = {fpu_v14, fpu_d14, LLDB_INVALID_REGNUM};
+static uint32_t g_s15_invalidates[] = {fpu_v15, fpu_d15, LLDB_INVALID_REGNUM};
+static uint32_t g_s16_invalidates[] = {fpu_v16, fpu_d16, LLDB_INVALID_REGNUM};
+static uint32_t g_s17_invalidates[] = {fpu_v17, fpu_d17, LLDB_INVALID_REGNUM};
+static uint32_t g_s18_invalidates[] = {fpu_v18, fpu_d18, LLDB_INVALID_REGNUM};
+static uint32_t g_s19_invalidates[] = {fpu_v19, fpu_d19, LLDB_INVALID_REGNUM};
+static uint32_t g_s20_invalidates[] = {fpu_v20, fpu_d20, LLDB_INVALID_REGNUM};
+static uint32_t g_s21_invalidates[] = {fpu_v21, fpu_d21, LLDB_INVALID_REGNUM};
+static uint32_t g_s22_invalidates[] = {fpu_v22, fpu_d22, LLDB_INVALID_REGNUM};
+static uint32_t g_s23_invalidates[] = {fpu_v23, fpu_d23, LLDB_INVALID_REGNUM};
+static uint32_t g_s24_invalidates[] = {fpu_v24, fpu_d24, LLDB_INVALID_REGNUM};
+static uint32_t g_s25_invalidates[] = {fpu_v25, fpu_d25, LLDB_INVALID_REGNUM};
+static uint32_t g_s26_invalidates[] = {fpu_v26, fpu_d26, LLDB_INVALID_REGNUM};
+static uint32_t g_s27_invalidates[] = {fpu_v27, fpu_d27, LLDB_INVALID_REGNUM};
+static uint32_t g_s28_invalidates[] = {fpu_v28, fpu_d28, LLDB_INVALID_REGNUM};
+static uint32_t g_s29_invalidates[] = {fpu_v29, fpu_d29, LLDB_INVALID_REGNUM};
+static uint32_t g_s30_invalidates[] = {fpu_v30, fpu_d30, LLDB_INVALID_REGNUM};
+static uint32_t g_s31_invalidates[] = {fpu_v31, fpu_d31, LLDB_INVALID_REGNUM};
+
+static uint32_t g_d0_invalidates[] = {fpu_v0, fpu_s0, LLDB_INVALID_REGNUM};
+static uint32_t g_d1_invalidates[] = {fpu_v1, fpu_s1, LLDB_INVALID_REGNUM};
+static uint32_t g_d2_invalidates[] = {fpu_v2, fpu_s2, LLDB_INVALID_REGNUM};
+static uint32_t g_d3_invalidates[] = {fpu_v3, fpu_s3, LLDB_INVALID_REGNUM};
+static uint32_t g_d4_invalidates[] = {fpu_v4, fpu_s4, LLDB_INVALID_REGNUM};
+static uint32_t g_d5_invalidates[] = {fpu_v5, fpu_s5, LLDB_INVALID_REGNUM};
+static uint32_t g_d6_invalidates[] = {fpu_v6, fpu_s6, LLDB_INVALID_REGNUM};
+static uint32_t g_d7_invalidates[] = {fpu_v7, fpu_s7, LLDB_INVALID_REGNUM};
+static uint32_t g_d8_invalidates[] = {fpu_v8, fpu_s8, LLDB_INVALID_REGNUM};
+static uint32_t g_d9_invalidates[] = {fpu_v9, fpu_s9, LLDB_INVALID_REGNUM};
+static uint32_t g_d10_invalidates[] = {fpu_v10, fpu_s10, LLDB_INVALID_REGNUM};
+static uint32_t g_d11_invalidates[] = {fpu_v11, fpu_s11, LLDB_INVALID_REGNUM};
+static uint32_t g_d12_invalidates[] = {fpu_v12, fpu_s12, LLDB_INVALID_REGNUM};
+static uint32_t g_d13_invalidates[] = {fpu_v13, fpu_s13, LLDB_INVALID_REGNUM};
+static uint32_t g_d14_invalidates[] = {fpu_v14, fpu_s14, LLDB_INVALID_REGNUM};
+static uint32_t g_d15_invalidates[] = {fpu_v15, fpu_s15, LLDB_INVALID_REGNUM};
+static uint32_t g_d16_invalidates[] = {fpu_v16, fpu_s16, LLDB_INVALID_REGNUM};
+static uint32_t g_d17_invalidates[] = {fpu_v17, fpu_s17, LLDB_INVALID_REGNUM};
+static uint32_t g_d18_invalidates[] = {fpu_v18, fpu_s18, LLDB_INVALID_REGNUM};
+static uint32_t g_d19_invalidates[] = {fpu_v19, fpu_s19, LLDB_INVALID_REGNUM};
+static uint32_t g_d20_invalidates[] = {fpu_v20, fpu_s20, LLDB_INVALID_REGNUM};
+static uint32_t g_d21_invalidates[] = {fpu_v21, fpu_s21, LLDB_INVALID_REGNUM};
+static uint32_t g_d22_invalidates[] = {fpu_v22, fpu_s22, LLDB_INVALID_REGNUM};
+static uint32_t g_d23_invalidates[] = {fpu_v23, fpu_s23, LLDB_INVALID_REGNUM};
+static uint32_t g_d24_invalidates[] = {fpu_v24, fpu_s24, LLDB_INVALID_REGNUM};
+static uint32_t g_d25_invalidates[] = {fpu_v25, fpu_s25, LLDB_INVALID_REGNUM};
+static uint32_t g_d26_invalidates[] = {fpu_v26, fpu_s26, LLDB_INVALID_REGNUM};
+static uint32_t g_d27_invalidates[] = {fpu_v27, fpu_s27, LLDB_INVALID_REGNUM};
+static uint32_t g_d28_invalidates[] = {fpu_v28, fpu_s28, LLDB_INVALID_REGNUM};
+static uint32_t g_d29_invalidates[] = {fpu_v29, fpu_s29, LLDB_INVALID_REGNUM};
+static uint32_t g_d30_invalidates[] = {fpu_v30, fpu_s30, LLDB_INVALID_REGNUM};
+static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM};
+
+static lldb_private::RegisterInfo g_register_infos_arm64_le[] = {
+ // clang-format off
+ // General purpose registers
+ // NAME ALT SZ OFFSET ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB VALUE REGS INVAL DYNEXPR SZ
+ // ===== ======= == ============= =================== ================ ================= =============== ======================== =================== ====== ============== ======= ======= ==
+ {"x0", nullptr, 8, GPR_OFFSET(0), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x0, arm64_dwarf::x0, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM, gpr_x0}, nullptr, nullptr, nullptr, 0},
+ {"x1", nullptr, 8, GPR_OFFSET(1), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x1, arm64_dwarf::x1, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM, gpr_x1}, nullptr, nullptr, nullptr, 0},
+ {"x2", nullptr, 8, GPR_OFFSET(2), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x2, arm64_dwarf::x2, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM, gpr_x2}, nullptr, nullptr, nullptr, 0},
+ {"x3", nullptr, 8, GPR_OFFSET(3), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x3, arm64_dwarf::x3, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM, gpr_x3}, nullptr, nullptr, nullptr, 0},
+ {"x4", nullptr, 8, GPR_OFFSET(4), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x4, arm64_dwarf::x4, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM, gpr_x4}, nullptr, nullptr, nullptr, 0},
+ {"x5", nullptr, 8, GPR_OFFSET(5), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x5, arm64_dwarf::x5, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM, gpr_x5}, nullptr, nullptr, nullptr, 0},
+ {"x6", nullptr, 8, GPR_OFFSET(6), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x6, arm64_dwarf::x6, LLDB_REGNUM_GENERIC_ARG7, LLDB_INVALID_REGNUM, gpr_x6}, nullptr, nullptr, nullptr, 0},
+ {"x7", nullptr, 8, GPR_OFFSET(7), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x7, arm64_dwarf::x7, LLDB_REGNUM_GENERIC_ARG8, LLDB_INVALID_REGNUM, gpr_x7}, nullptr, nullptr, nullptr, 0},
+ {"x8", nullptr, 8, GPR_OFFSET(8), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x8, arm64_dwarf::x8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x8}, nullptr, nullptr, nullptr, 0},
+ {"x9", nullptr, 8, GPR_OFFSET(9), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x9, arm64_dwarf::x9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x9}, nullptr, nullptr, nullptr, 0},
+ {"x10", nullptr, 8, GPR_OFFSET(10), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x10, arm64_dwarf::x10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x10}, nullptr, nullptr, nullptr, 0},
+ {"x11", nullptr, 8, GPR_OFFSET(11), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x11, arm64_dwarf::x11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x11}, nullptr, nullptr, nullptr, 0},
+ {"x12", nullptr, 8, GPR_OFFSET(12), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x12, arm64_dwarf::x12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x12}, nullptr, nullptr, nullptr, 0},
+ {"x13", nullptr, 8, GPR_OFFSET(13), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x13, arm64_dwarf::x13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x13}, nullptr, nullptr, nullptr, 0},
+ {"x14", nullptr, 8, GPR_OFFSET(14), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x14, arm64_dwarf::x14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x14}, nullptr, nullptr, nullptr, 0},
+ {"x15", nullptr, 8, GPR_OFFSET(15), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x15, arm64_dwarf::x15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x15}, nullptr, nullptr, nullptr, 0},
+ {"x16", nullptr, 8, GPR_OFFSET(16), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x16, arm64_dwarf::x16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x16}, nullptr, nullptr, nullptr, 0},
+ {"x17", nullptr, 8, GPR_OFFSET(17), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x17, arm64_dwarf::x17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x17}, nullptr, nullptr, nullptr, 0},
+ {"x18", nullptr, 8, GPR_OFFSET(18), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x18, arm64_dwarf::x18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x18}, nullptr, nullptr, nullptr, 0},
+ {"x19", nullptr, 8, GPR_OFFSET(19), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x19, arm64_dwarf::x19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x19}, nullptr, nullptr, nullptr, 0},
+ {"x20", nullptr, 8, GPR_OFFSET(20), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x20, arm64_dwarf::x20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x20}, nullptr, nullptr, nullptr, 0},
+ {"x21", nullptr, 8, GPR_OFFSET(21), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x21, arm64_dwarf::x21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x21}, nullptr, nullptr, nullptr, 0},
+ {"x22", nullptr, 8, GPR_OFFSET(22), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x22, arm64_dwarf::x22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x22}, nullptr, nullptr, nullptr, 0},
+ {"x23", nullptr, 8, GPR_OFFSET(23), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x23, arm64_dwarf::x23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x23}, nullptr, nullptr, nullptr, 0},
+ {"x24", nullptr, 8, GPR_OFFSET(24), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x24, arm64_dwarf::x24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x24}, nullptr, nullptr, nullptr, 0},
+ {"x25", nullptr, 8, GPR_OFFSET(25), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x25, arm64_dwarf::x25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x25}, nullptr, nullptr, nullptr, 0},
+ {"x26", nullptr, 8, GPR_OFFSET(26), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x26, arm64_dwarf::x26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x26}, nullptr, nullptr, nullptr, 0},
+ {"x27", nullptr, 8, GPR_OFFSET(27), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x27, arm64_dwarf::x27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x27}, nullptr, nullptr, nullptr, 0},
+ {"x28", nullptr, 8, GPR_OFFSET(28), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x28, arm64_dwarf::x28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x28}, nullptr, nullptr, nullptr, 0},
+ {"fp", "x29", 8, GPR_OFFSET(29), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::fp, arm64_dwarf::fp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, gpr_fp}, nullptr, nullptr, nullptr, 0},
+ {"lr", "x30", 8, GPR_OFFSET(30), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::lr, arm64_dwarf::lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM, gpr_lr}, nullptr, nullptr, nullptr, 0},
+ {"sp", "x31", 8, GPR_OFFSET(31), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::sp, arm64_dwarf::sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, gpr_sp}, nullptr, nullptr, nullptr, 0},
+ {"pc", nullptr, 8, GPR_OFFSET(32), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::pc, arm64_dwarf::pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, gpr_pc}, nullptr, nullptr, nullptr, 0},
+
+ {"cpsr",nullptr, 4, GPR_OFFSET_NAME(cpsr), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::cpsr, arm64_dwarf::cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, gpr_cpsr}, nullptr, nullptr, nullptr, 0},
+
+ // NAME ALT SZ OFFSET ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB VALUE INVALIDATES DYNEXPR SZ
+ // ===== ======= == ============================================== =================== ================ ================= =============== =================== =================== ====== =============== ================= ======= ==
+ {"w0", nullptr, 4, GPR_OFFSET(0) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w0}, g_contained_x0, g_w0_invalidates, nullptr, 0},
+ {"w1", nullptr, 4, GPR_OFFSET(1) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w1}, g_contained_x1, g_w1_invalidates, nullptr, 0},
+ {"w2", nullptr, 4, GPR_OFFSET(2) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w2}, g_contained_x2, g_w2_invalidates, nullptr, 0},
+ {"w3", nullptr, 4, GPR_OFFSET(3) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w3}, g_contained_x3, g_w3_invalidates, nullptr, 0},
+ {"w4", nullptr, 4, GPR_OFFSET(4) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w4}, g_contained_x4, g_w4_invalidates, nullptr, 0},
+ {"w5", nullptr, 4, GPR_OFFSET(5) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w5}, g_contained_x5, g_w5_invalidates, nullptr, 0},
+ {"w6", nullptr, 4, GPR_OFFSET(6) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w6}, g_contained_x6, g_w6_invalidates, nullptr, 0},
+ {"w7", nullptr, 4, GPR_OFFSET(7) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w7}, g_contained_x7, g_w7_invalidates, nullptr, 0},
+ {"w8", nullptr, 4, GPR_OFFSET(8) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w8}, g_contained_x8, g_w8_invalidates, nullptr, 0},
+ {"w9", nullptr, 4, GPR_OFFSET(9) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w9}, g_contained_x9, g_w9_invalidates, nullptr, 0},
+ {"w10", nullptr, 4, GPR_OFFSET(10) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w10}, g_contained_x10, g_w10_invalidates, nullptr, 0},
+ {"w11", nullptr, 4, GPR_OFFSET(11) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w11}, g_contained_x11, g_w11_invalidates, nullptr, 0},
+ {"w12", nullptr, 4, GPR_OFFSET(12) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w12}, g_contained_x12, g_w12_invalidates, nullptr, 0},
+ {"w13", nullptr, 4, GPR_OFFSET(13) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w13}, g_contained_x13, g_w13_invalidates, nullptr, 0},
+ {"w14", nullptr, 4, GPR_OFFSET(14) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w14}, g_contained_x14, g_w14_invalidates, nullptr, 0},
+ {"w15", nullptr, 4, GPR_OFFSET(15) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w15}, g_contained_x15, g_w15_invalidates, nullptr, 0},
+ {"w16", nullptr, 4, GPR_OFFSET(16) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w16}, g_contained_x16, g_w16_invalidates, nullptr, 0},
+ {"w17", nullptr, 4, GPR_OFFSET(17) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w17}, g_contained_x17, g_w17_invalidates, nullptr, 0},
+ {"w18", nullptr, 4, GPR_OFFSET(18) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w18}, g_contained_x18, g_w18_invalidates, nullptr, 0},
+ {"w19", nullptr, 4, GPR_OFFSET(19) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w19}, g_contained_x19, g_w19_invalidates, nullptr, 0},
+ {"w20", nullptr, 4, GPR_OFFSET(20) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w20}, g_contained_x20, g_w20_invalidates, nullptr, 0},
+ {"w21", nullptr, 4, GPR_OFFSET(21) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w21}, g_contained_x21, g_w21_invalidates, nullptr, 0},
+ {"w22", nullptr, 4, GPR_OFFSET(22) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w22}, g_contained_x22, g_w22_invalidates, nullptr, 0},
+ {"w23", nullptr, 4, GPR_OFFSET(23) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w23}, g_contained_x23, g_w23_invalidates, nullptr, 0},
+ {"w24", nullptr, 4, GPR_OFFSET(24) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w24}, g_contained_x24, g_w24_invalidates, nullptr, 0},
+ {"w25", nullptr, 4, GPR_OFFSET(25) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w25}, g_contained_x25, g_w25_invalidates, nullptr, 0},
+ {"w26", nullptr, 4, GPR_OFFSET(26) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w26}, g_contained_x26, g_w26_invalidates, nullptr, 0},
+ {"w27", nullptr, 4, GPR_OFFSET(27) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w27}, g_contained_x27, g_w27_invalidates, nullptr, 0},
+ {"w28", nullptr, 4, GPR_OFFSET(28) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w28}, g_contained_x28, g_w28_invalidates, nullptr, 0},
+
+ // NAME ALT SZ OFFSET ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB VALUE REGS INVAL DYNEXPR SZ
+ // ===== ======= == ============= =================== ================ ================= =============== =================== =================== ====== ============== ======= ======= ==
+ {"v0", nullptr, 16, FPU_OFFSET(0), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v0}, nullptr, nullptr, nullptr, 0},
+ {"v1", nullptr, 16, FPU_OFFSET(1), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v1}, nullptr, nullptr, nullptr, 0},
+ {"v2", nullptr, 16, FPU_OFFSET(2), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v2}, nullptr, nullptr, nullptr, 0},
+ {"v3", nullptr, 16, FPU_OFFSET(3), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v3}, nullptr, nullptr, nullptr, 0},
+ {"v4", nullptr, 16, FPU_OFFSET(4), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v4}, nullptr, nullptr, nullptr, 0},
+ {"v5", nullptr, 16, FPU_OFFSET(5), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v5}, nullptr, nullptr, nullptr, 0},
+ {"v6", nullptr, 16, FPU_OFFSET(6), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v6}, nullptr, nullptr, nullptr, 0},
+ {"v7", nullptr, 16, FPU_OFFSET(7), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v7}, nullptr, nullptr, nullptr, 0},
+ {"v8", nullptr, 16, FPU_OFFSET(8), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v8}, nullptr, nullptr, nullptr, 0},
+ {"v9", nullptr, 16, FPU_OFFSET(9), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v9}, nullptr, nullptr, nullptr, 0},
+ {"v10", nullptr, 16, FPU_OFFSET(10), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v10}, nullptr, nullptr, nullptr, 0},
+ {"v11", nullptr, 16, FPU_OFFSET(11), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v11}, nullptr, nullptr, nullptr, 0},
+ {"v12", nullptr, 16, FPU_OFFSET(12), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v12}, nullptr, nullptr, nullptr, 0},
+ {"v13", nullptr, 16, FPU_OFFSET(13), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v13}, nullptr, nullptr, nullptr, 0},
+ {"v14", nullptr, 16, FPU_OFFSET(14), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v14}, nullptr, nullptr, nullptr, 0},
+ {"v15", nullptr, 16, FPU_OFFSET(15), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v15}, nullptr, nullptr, nullptr, 0},
+ {"v16", nullptr, 16, FPU_OFFSET(16), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v16}, nullptr, nullptr, nullptr, 0},
+ {"v17", nullptr, 16, FPU_OFFSET(17), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v17}, nullptr, nullptr, nullptr, 0},
+ {"v18", nullptr, 16, FPU_OFFSET(18), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v18}, nullptr, nullptr, nullptr, 0},
+ {"v19", nullptr, 16, FPU_OFFSET(19), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v19}, nullptr, nullptr, nullptr, 0},
+ {"v20", nullptr, 16, FPU_OFFSET(20), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v20}, nullptr, nullptr, nullptr, 0},
+ {"v21", nullptr, 16, FPU_OFFSET(21), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v21}, nullptr, nullptr, nullptr, 0},
+ {"v22", nullptr, 16, FPU_OFFSET(22), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v22}, nullptr, nullptr, nullptr, 0},
+ {"v23", nullptr, 16, FPU_OFFSET(23), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v23}, nullptr, nullptr, nullptr, 0},
+ {"v24", nullptr, 16, FPU_OFFSET(24), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v24}, nullptr, nullptr, nullptr, 0},
+ {"v25", nullptr, 16, FPU_OFFSET(25), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v25}, nullptr, nullptr, nullptr, 0},
+ {"v26", nullptr, 16, FPU_OFFSET(26), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v26}, nullptr, nullptr, nullptr, 0},
+ {"v27", nullptr, 16, FPU_OFFSET(27), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v27}, nullptr, nullptr, nullptr, 0},
+ {"v28", nullptr, 16, FPU_OFFSET(28), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v28}, nullptr, nullptr, nullptr, 0},
+ {"v29", nullptr, 16, FPU_OFFSET(29), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v29}, nullptr, nullptr, nullptr, 0},
+ {"v30", nullptr, 16, FPU_OFFSET(30), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v30}, nullptr, nullptr, nullptr, 0},
+ {"v31", nullptr, 16, FPU_OFFSET(31), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v31}, nullptr, nullptr, nullptr, 0},
+
+ // NAME ALT SZ OFFSET ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB VALUE REGS INVALIDATES DYNEXPR SZ
+ // ===== ======= == ============================================== =================== ================ ================= =============== =================== =================== ====== =============== ================= ======= ==
+ {"s0", nullptr, 4, FPU_OFFSET(0) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s0}, g_contained_v0, g_s0_invalidates, nullptr, 0},
+ {"s1", nullptr, 4, FPU_OFFSET(1) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s1}, g_contained_v1, g_s1_invalidates, nullptr, 0},
+ {"s2", nullptr, 4, FPU_OFFSET(2) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s2}, g_contained_v2, g_s2_invalidates, nullptr, 0},
+ {"s3", nullptr, 4, FPU_OFFSET(3) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s3}, g_contained_v3, g_s3_invalidates, nullptr, 0},
+ {"s4", nullptr, 4, FPU_OFFSET(4) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s4}, g_contained_v4, g_s4_invalidates, nullptr, 0},
+ {"s5", nullptr, 4, FPU_OFFSET(5) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s5}, g_contained_v5, g_s5_invalidates, nullptr, 0},
+ {"s6", nullptr, 4, FPU_OFFSET(6) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s6}, g_contained_v6, g_s6_invalidates, nullptr, 0},
+ {"s7", nullptr, 4, FPU_OFFSET(7) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s7}, g_contained_v7, g_s7_invalidates, nullptr, 0},
+ {"s8", nullptr, 4, FPU_OFFSET(8) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s8}, g_contained_v8, g_s8_invalidates, nullptr, 0},
+ {"s9", nullptr, 4, FPU_OFFSET(9) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s9}, g_contained_v9, g_s9_invalidates, nullptr, 0},
+ {"s10", nullptr, 4, FPU_OFFSET(10) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s10}, g_contained_v10, g_s10_invalidates, nullptr, 0},
+ {"s11", nullptr, 4, FPU_OFFSET(11) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s11}, g_contained_v11, g_s11_invalidates, nullptr, 0},
+ {"s12", nullptr, 4, FPU_OFFSET(12) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s12}, g_contained_v12, g_s12_invalidates, nullptr, 0},
+ {"s13", nullptr, 4, FPU_OFFSET(13) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s13}, g_contained_v13, g_s13_invalidates, nullptr, 0},
+ {"s14", nullptr, 4, FPU_OFFSET(14) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s14}, g_contained_v14, g_s14_invalidates, nullptr, 0},
+ {"s15", nullptr, 4, FPU_OFFSET(15) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s15}, g_contained_v15, g_s15_invalidates, nullptr, 0},
+ {"s16", nullptr, 4, FPU_OFFSET(16) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s16}, g_contained_v16, g_s16_invalidates, nullptr, 0},
+ {"s17", nullptr, 4, FPU_OFFSET(17) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s17}, g_contained_v17, g_s17_invalidates, nullptr, 0},
+ {"s18", nullptr, 4, FPU_OFFSET(18) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s18}, g_contained_v18, g_s18_invalidates, nullptr, 0},
+ {"s19", nullptr, 4, FPU_OFFSET(19) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s19}, g_contained_v19, g_s19_invalidates, nullptr, 0},
+ {"s20", nullptr, 4, FPU_OFFSET(20) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s20}, g_contained_v20, g_s20_invalidates, nullptr, 0},
+ {"s21", nullptr, 4, FPU_OFFSET(21) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s21}, g_contained_v21, g_s21_invalidates, nullptr, 0},
+ {"s22", nullptr, 4, FPU_OFFSET(22) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s22}, g_contained_v22, g_s22_invalidates, nullptr, 0},
+ {"s23", nullptr, 4, FPU_OFFSET(23) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s23}, g_contained_v23, g_s23_invalidates, nullptr, 0},
+ {"s24", nullptr, 4, FPU_OFFSET(24) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s24}, g_contained_v24, g_s24_invalidates, nullptr, 0},
+ {"s25", nullptr, 4, FPU_OFFSET(25) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s25}, g_contained_v25, g_s25_invalidates, nullptr, 0},
+ {"s26", nullptr, 4, FPU_OFFSET(26) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s26}, g_contained_v26, g_s26_invalidates, nullptr, 0},
+ {"s27", nullptr, 4, FPU_OFFSET(27) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s27}, g_contained_v27, g_s27_invalidates, nullptr, 0},
+ {"s28", nullptr, 4, FPU_OFFSET(28) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s28}, g_contained_v28, g_s28_invalidates, nullptr, 0},
+ {"s29", nullptr, 4, FPU_OFFSET(29) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s29}, g_contained_v29, g_s29_invalidates, nullptr, 0},
+ {"s30", nullptr, 4, FPU_OFFSET(30) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s30}, g_contained_v30, g_s30_invalidates, nullptr, 0},
+ {"s31", nullptr, 4, FPU_OFFSET(31) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s31}, g_contained_v31, g_s31_invalidates, nullptr, 0},
+
+ {"d0", nullptr, 8, FPU_OFFSET(0) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d0}, g_contained_v0, g_d0_invalidates, nullptr, 0},
+ {"d1", nullptr, 8, FPU_OFFSET(1) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d1}, g_contained_v1, g_d1_invalidates, nullptr, 0},
+ {"d2", nullptr, 8, FPU_OFFSET(2) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d2}, g_contained_v2, g_d2_invalidates, nullptr, 0},
+ {"d3", nullptr, 8, FPU_OFFSET(3) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d3}, g_contained_v3, g_d3_invalidates, nullptr, 0},
+ {"d4", nullptr, 8, FPU_OFFSET(4) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d4}, g_contained_v4, g_d4_invalidates, nullptr, 0},
+ {"d5", nullptr, 8, FPU_OFFSET(5) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d5}, g_contained_v5, g_d5_invalidates, nullptr, 0},
+ {"d6", nullptr, 8, FPU_OFFSET(6) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d6}, g_contained_v6, g_d6_invalidates, nullptr, 0},
+ {"d7", nullptr, 8, FPU_OFFSET(7) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d7}, g_contained_v7, g_d7_invalidates, nullptr, 0},
+ {"d8", nullptr, 8, FPU_OFFSET(8) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d8}, g_contained_v8, g_d8_invalidates, nullptr, 0},
+ {"d9", nullptr, 8, FPU_OFFSET(9) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d9}, g_contained_v9, g_d9_invalidates, nullptr, 0},
+ {"d10", nullptr, 8, FPU_OFFSET(10) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d10}, g_contained_v10, g_d10_invalidates, nullptr, 0},
+ {"d11", nullptr, 8, FPU_OFFSET(11) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d11}, g_contained_v11, g_d11_invalidates, nullptr, 0},
+ {"d12", nullptr, 8, FPU_OFFSET(12) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d12}, g_contained_v12, g_d12_invalidates, nullptr, 0},
+ {"d13", nullptr, 8, FPU_OFFSET(13) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d13}, g_contained_v13, g_d13_invalidates, nullptr, 0},
+ {"d14", nullptr, 8, FPU_OFFSET(14) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d14}, g_contained_v14, g_d14_invalidates, nullptr, 0},
+ {"d15", nullptr, 8, FPU_OFFSET(15) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d15}, g_contained_v15, g_d15_invalidates, nullptr, 0},
+ {"d16", nullptr, 8, FPU_OFFSET(16) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d16}, g_contained_v16, g_d16_invalidates, nullptr, 0},
+ {"d17", nullptr, 8, FPU_OFFSET(17) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d17}, g_contained_v17, g_d17_invalidates, nullptr, 0},
+ {"d18", nullptr, 8, FPU_OFFSET(18) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d18}, g_contained_v18, g_d18_invalidates, nullptr, 0},
+ {"d19", nullptr, 8, FPU_OFFSET(19) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d19}, g_contained_v19, g_d19_invalidates, nullptr, 0},
+ {"d20", nullptr, 8, FPU_OFFSET(20) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d20}, g_contained_v20, g_d20_invalidates, nullptr, 0},
+ {"d21", nullptr, 8, FPU_OFFSET(21) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d21}, g_contained_v21, g_d21_invalidates, nullptr, 0},
+ {"d22", nullptr, 8, FPU_OFFSET(22) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d22}, g_contained_v22, g_d22_invalidates, nullptr, 0},
+ {"d23", nullptr, 8, FPU_OFFSET(23) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d23}, g_contained_v23, g_d23_invalidates, nullptr, 0},
+ {"d24", nullptr, 8, FPU_OFFSET(24) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d24}, g_contained_v24, g_d24_invalidates, nullptr, 0},
+ {"d25", nullptr, 8, FPU_OFFSET(25) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d25}, g_contained_v25, g_d25_invalidates, nullptr, 0},
+ {"d26", nullptr, 8, FPU_OFFSET(26) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d26}, g_contained_v26, g_d26_invalidates, nullptr, 0},
+ {"d27", nullptr, 8, FPU_OFFSET(27) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d27}, g_contained_v27, g_d27_invalidates, nullptr, 0},
+ {"d28", nullptr, 8, FPU_OFFSET(28) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d28}, g_contained_v28, g_d28_invalidates, nullptr, 0},
+ {"d29", nullptr, 8, FPU_OFFSET(29) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d29}, g_contained_v29, g_d29_invalidates, nullptr, 0},
+ {"d30", nullptr, 8, FPU_OFFSET(30) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d30}, g_contained_v30, g_d30_invalidates, nullptr, 0},
+ {"d31", nullptr, 8, FPU_OFFSET(31) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d31}, g_contained_v31, g_d31_invalidates, nullptr, 0},
+
+ {"fpsr", nullptr, 4, FPU_OFFSET_NAME(fpsr), lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_fpsr}, nullptr, nullptr, nullptr, 0},
+ {"fpcr", nullptr, 4, FPU_OFFSET_NAME(fpcr), lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_fpcr}, nullptr, nullptr, nullptr, 0},
+
+ {"far", nullptr, 8, EXC_OFFSET_NAME(far), lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_far}, nullptr, nullptr, nullptr, 0},
+ {"esr", nullptr, 4, EXC_OFFSET_NAME(esr), lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_esr}, nullptr, nullptr, nullptr, 0},
+ {"exception", nullptr, 4, EXC_OFFSET_NAME(exception), lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_exception}, nullptr, nullptr, nullptr, 0},
+
+ {DEFINE_DBG(bvr, 0)},
+ {DEFINE_DBG(bvr, 1)},
+ {DEFINE_DBG(bvr, 2)},
+ {DEFINE_DBG(bvr, 3)},
+ {DEFINE_DBG(bvr, 4)},
+ {DEFINE_DBG(bvr, 5)},
+ {DEFINE_DBG(bvr, 6)},
+ {DEFINE_DBG(bvr, 7)},
+ {DEFINE_DBG(bvr, 8)},
+ {DEFINE_DBG(bvr, 9)},
+ {DEFINE_DBG(bvr, 10)},
+ {DEFINE_DBG(bvr, 11)},
+ {DEFINE_DBG(bvr, 12)},
+ {DEFINE_DBG(bvr, 13)},
+ {DEFINE_DBG(bvr, 14)},
+ {DEFINE_DBG(bvr, 15)},
+
+ {DEFINE_DBG(bcr, 0)},
+ {DEFINE_DBG(bcr, 1)},
+ {DEFINE_DBG(bcr, 2)},
+ {DEFINE_DBG(bcr, 3)},
+ {DEFINE_DBG(bcr, 4)},
+ {DEFINE_DBG(bcr, 5)},
+ {DEFINE_DBG(bcr, 6)},
+ {DEFINE_DBG(bcr, 7)},
+ {DEFINE_DBG(bcr, 8)},
+ {DEFINE_DBG(bcr, 9)},
+ {DEFINE_DBG(bcr, 10)},
+ {DEFINE_DBG(bcr, 11)},
+ {DEFINE_DBG(bcr, 12)},
+ {DEFINE_DBG(bcr, 13)},
+ {DEFINE_DBG(bcr, 14)},
+ {DEFINE_DBG(bcr, 15)},
+
+ {DEFINE_DBG(wvr, 0)},
+ {DEFINE_DBG(wvr, 1)},
+ {DEFINE_DBG(wvr, 2)},
+ {DEFINE_DBG(wvr, 3)},
+ {DEFINE_DBG(wvr, 4)},
+ {DEFINE_DBG(wvr, 5)},
+ {DEFINE_DBG(wvr, 6)},
+ {DEFINE_DBG(wvr, 7)},
+ {DEFINE_DBG(wvr, 8)},
+ {DEFINE_DBG(wvr, 9)},
+ {DEFINE_DBG(wvr, 10)},
+ {DEFINE_DBG(wvr, 11)},
+ {DEFINE_DBG(wvr, 12)},
+ {DEFINE_DBG(wvr, 13)},
+ {DEFINE_DBG(wvr, 14)},
+ {DEFINE_DBG(wvr, 15)},
+
+ {DEFINE_DBG(wcr, 0)},
+ {DEFINE_DBG(wcr, 1)},
+ {DEFINE_DBG(wcr, 2)},
+ {DEFINE_DBG(wcr, 3)},
+ {DEFINE_DBG(wcr, 4)},
+ {DEFINE_DBG(wcr, 5)},
+ {DEFINE_DBG(wcr, 6)},
+ {DEFINE_DBG(wcr, 7)},
+ {DEFINE_DBG(wcr, 8)},
+ {DEFINE_DBG(wcr, 9)},
+ {DEFINE_DBG(wcr, 10)},
+ {DEFINE_DBG(wcr, 11)},
+ {DEFINE_DBG(wcr, 12)},
+ {DEFINE_DBG(wcr, 13)},
+ {DEFINE_DBG(wcr, 14)},
+ {DEFINE_DBG(wcr, 15)}
+ // clang-format on
+};
+
+#endif // DECLARE_REGISTER_INFOS_ARM64_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_i386.h
new file mode 100644
index 000000000000..72ff904520ad
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_i386.h
@@ -0,0 +1,305 @@
+//===-- RegisterInfos_i386.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Compiler.h"
+#include <cstddef>
+#include <cstdint>
+
+
+#ifdef DECLARE_REGISTER_INFOS_I386_STRUCT
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname))
+
+// Computes the offset of the given FPR in the extended data area.
+#define FPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, i387) + \
+ LLVM_EXTENSION offsetof(FPR_i386, regname))
+
+// Computes the offset of the YMM register assembled from register halves.
+// Based on DNBArchImplI386.cpp from debugserver
+#define YMM_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, i387) + \
+ LLVM_EXTENSION offsetof(FPR, fxsave) + \
+ LLVM_EXTENSION offsetof(FXSAVE, xmm[7]) + sizeof(XMMReg) + \
+ (32 * reg_index))
+
+#define BNDR_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, i387) + \
+ LLVM_EXTENSION offsetof(FPR, xsave) + \
+ LLVM_EXTENSION offsetof(XSAVE, mpxr[reg_index]))
+
+#define BNDC_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, i387) + \
+ LLVM_EXTENSION offsetof(FPR, xsave) + \
+ LLVM_EXTENSION offsetof(XSAVE, mpxc[reg_index]))
+
+// Number of bytes needed to represent a FPR.
+#if !defined(FPR_SIZE)
+#define FPR_SIZE(reg) sizeof(((FXSAVE *)nullptr)->reg)
+#endif
+
+// Number of bytes needed to represent the i'th FP register.
+#define FP_SIZE sizeof(((MMSReg *)nullptr)->bytes)
+
+// Number of bytes needed to represent an XMM register.
+#define XMM_SIZE sizeof(XMMReg)
+
+// Number of bytes needed to represent a YMM register.
+#define YMM_SIZE sizeof(YMMReg)
+
+// Number of bytes needed to represent MPX registers.
+#define BNDR_SIZE sizeof(MPXReg)
+#define BNDC_SIZE sizeof(MPXCsr)
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((GPR *)nullptr)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ lldb_##reg##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_FPR(name, reg, kind1, kind2, kind3, kind4) \
+ { \
+ #name, nullptr, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ lldb_##name##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB
+
+#define DEFINE_FP_ST(reg, i) \
+ { \
+ #reg #i, nullptr, FP_SIZE, \
+ LLVM_EXTENSION FPR_OFFSET( \
+ stmm[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {ehframe_st##i##_i386, dwarf_st##i##_i386, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_st##i##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_FP_MM(reg, i) \
+ { \
+ #reg #i, nullptr, sizeof(uint64_t), \
+ LLVM_EXTENSION FPR_OFFSET( \
+ stmm[i]), eEncodingUint, eFormatHex, \
+ {ehframe_mm##i##_i386, dwarf_mm##i##_i386, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_mm##i##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_XMM(reg, i) \
+ { \
+ #reg #i, nullptr, XMM_SIZE, \
+ LLVM_EXTENSION FPR_OFFSET( \
+ reg[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {ehframe_##reg##i##_i386, dwarf_##reg##i##_i386, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg##i##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+// I believe the YMM registers use dwarf_xmm_%_i386 register numbers and then
+// differentiate based on register size.
+#define DEFINE_YMM(reg, i) \
+ { \
+ #reg #i, nullptr, YMM_SIZE, \
+ LLVM_EXTENSION YMM_OFFSET(i), eEncodingVector, eFormatVectorOfUInt8, \
+ {LLDB_INVALID_REGNUM, dwarf_xmm##i##_i386, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg##i##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_BNDR(reg, i) \
+ { \
+ #reg #i, nullptr, BNDR_SIZE, \
+ LLVM_EXTENSION BNDR_OFFSET(i), eEncodingVector, eFormatVectorOfUInt64, \
+ {dwarf_##reg##i##_i386, dwarf_##reg##i##_i386, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_##reg##i##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_BNDC(name, i) \
+ { \
+ #name, nullptr, BNDC_SIZE, \
+ LLVM_EXTENSION BNDC_OFFSET(i), eEncodingVector, \
+ eFormatVectorOfUInt8, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_##name##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_DR(reg, i) \
+ { \
+ #reg #i, nullptr, DR_SIZE, \
+ DR_OFFSET(i), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_16(reg16, reg32) \
+ { \
+ #reg16, nullptr, 2, \
+ GPR_OFFSET(reg32), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg16##_i386 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg32, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg32, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_8H(reg8, reg32) \
+ { \
+ #reg8, nullptr, 1, \
+ GPR_OFFSET(reg32) + 1, eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg8##_i386 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg32, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg32, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_8L(reg8, reg32) \
+ { \
+ #reg8, nullptr, 1, \
+ GPR_OFFSET(reg32), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg8##_i386 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg32, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg32, nullptr, 0 \
+ }
+
+static RegisterInfo g_register_infos_i386[] = {
+ // General purpose registers.
+ DEFINE_GPR(eax, nullptr, ehframe_eax_i386, dwarf_eax_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ebx, nullptr, ehframe_ebx_i386, dwarf_ebx_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ecx, nullptr, ehframe_ecx_i386, dwarf_ecx_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(edx, nullptr, ehframe_edx_i386, dwarf_edx_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(edi, nullptr, ehframe_edi_i386, dwarf_edi_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(esi, nullptr, ehframe_esi_i386, dwarf_esi_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ebp, "fp", ehframe_ebp_i386, dwarf_ebp_i386,
+ LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(esp, "sp", ehframe_esp_i386, dwarf_esp_i386,
+ LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(eip, "pc", ehframe_eip_i386, dwarf_eip_i386,
+ LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(eflags, "flags", ehframe_eflags_i386, dwarf_eflags_i386,
+ LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(cs, nullptr, LLDB_INVALID_REGNUM, dwarf_cs_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(fs, nullptr, LLDB_INVALID_REGNUM, dwarf_fs_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(gs, nullptr, LLDB_INVALID_REGNUM, dwarf_gs_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ss, nullptr, LLDB_INVALID_REGNUM, dwarf_ss_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ds, nullptr, LLDB_INVALID_REGNUM, dwarf_ds_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(es, nullptr, LLDB_INVALID_REGNUM, dwarf_es_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+
+ DEFINE_GPR_PSEUDO_16(ax, eax), DEFINE_GPR_PSEUDO_16(bx, ebx),
+ DEFINE_GPR_PSEUDO_16(cx, ecx), DEFINE_GPR_PSEUDO_16(dx, edx),
+ DEFINE_GPR_PSEUDO_16(di, edi), DEFINE_GPR_PSEUDO_16(si, esi),
+ DEFINE_GPR_PSEUDO_16(bp, ebp), DEFINE_GPR_PSEUDO_16(sp, esp),
+ DEFINE_GPR_PSEUDO_8H(ah, eax), DEFINE_GPR_PSEUDO_8H(bh, ebx),
+ DEFINE_GPR_PSEUDO_8H(ch, ecx), DEFINE_GPR_PSEUDO_8H(dh, edx),
+ DEFINE_GPR_PSEUDO_8L(al, eax), DEFINE_GPR_PSEUDO_8L(bl, ebx),
+ DEFINE_GPR_PSEUDO_8L(cl, ecx), DEFINE_GPR_PSEUDO_8L(dl, edx),
+
+ // i387 Floating point registers.
+ DEFINE_FPR(fctrl, fctrl, LLDB_INVALID_REGNUM, dwarf_fctrl_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fstat, fstat, LLDB_INVALID_REGNUM, dwarf_fstat_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(ftag, ftag, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fop, fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fiseg, ptr.i386_.fiseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fioff, ptr.i386_.fioff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(foseg, ptr.i386_.foseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fooff, ptr.i386_.fooff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(mxcsr, mxcsr, LLDB_INVALID_REGNUM, dwarf_mxcsr_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(mxcsrmask, mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+
+ // FP registers.
+ DEFINE_FP_ST(st, 0), DEFINE_FP_ST(st, 1), DEFINE_FP_ST(st, 2),
+ DEFINE_FP_ST(st, 3), DEFINE_FP_ST(st, 4), DEFINE_FP_ST(st, 5),
+ DEFINE_FP_ST(st, 6), DEFINE_FP_ST(st, 7), DEFINE_FP_MM(mm, 0),
+ DEFINE_FP_MM(mm, 1), DEFINE_FP_MM(mm, 2), DEFINE_FP_MM(mm, 3),
+ DEFINE_FP_MM(mm, 4), DEFINE_FP_MM(mm, 5), DEFINE_FP_MM(mm, 6),
+ DEFINE_FP_MM(mm, 7),
+
+ // XMM registers
+ DEFINE_XMM(xmm, 0), DEFINE_XMM(xmm, 1), DEFINE_XMM(xmm, 2),
+ DEFINE_XMM(xmm, 3), DEFINE_XMM(xmm, 4), DEFINE_XMM(xmm, 5),
+ DEFINE_XMM(xmm, 6), DEFINE_XMM(xmm, 7),
+
+ // Copy of YMM registers assembled from xmm and ymmh
+ DEFINE_YMM(ymm, 0), DEFINE_YMM(ymm, 1), DEFINE_YMM(ymm, 2),
+ DEFINE_YMM(ymm, 3), DEFINE_YMM(ymm, 4), DEFINE_YMM(ymm, 5),
+ DEFINE_YMM(ymm, 6), DEFINE_YMM(ymm, 7),
+
+ // MPX registers
+ DEFINE_BNDR(bnd, 0),
+ DEFINE_BNDR(bnd, 1),
+ DEFINE_BNDR(bnd, 2),
+ DEFINE_BNDR(bnd, 3),
+
+ DEFINE_BNDC(bndcfgu, 0),
+ DEFINE_BNDC(bndstatus, 1),
+
+ // Debug registers for lldb internal use
+ DEFINE_DR(dr, 0), DEFINE_DR(dr, 1), DEFINE_DR(dr, 2), DEFINE_DR(dr, 3),
+ DEFINE_DR(dr, 4), DEFINE_DR(dr, 5), DEFINE_DR(dr, 6), DEFINE_DR(dr, 7)};
+
+static_assert((sizeof(g_register_infos_i386) /
+ sizeof(g_register_infos_i386[0])) == k_num_registers_i386,
+ "g_register_infos_x86_64 has wrong number of register infos");
+
+#undef GPR_OFFSET
+#undef FPR_OFFSET
+#undef YMM_OFFSET
+#undef FPR_SIZE
+#undef FP_SIZE
+#undef XMM_SIZE
+#undef YMM_SIZE
+#undef DEFINE_GPR
+#undef DEFINE_FPR
+#undef DEFINE_FP
+#undef DEFINE_XMM
+#undef DEFINE_YMM
+#undef DEFINE_BNDR
+#undef DEFINE_BNDC
+#undef DEFINE_DR
+#undef DEFINE_GPR_PSEUDO_16
+#undef DEFINE_GPR_PSEUDO_8H
+#undef DEFINE_GPR_PSEUDO_8L
+
+#endif // DECLARE_REGISTER_INFOS_I386_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips.h
new file mode 100644
index 000000000000..08201fd1191c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips.h
@@ -0,0 +1,305 @@
+//===-- RegisterInfos_mips.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include <stddef.h>
+
+#include "lldb/Core/dwarf.h"
+#include "llvm/Support/Compiler.h"
+
+
+#ifdef DECLARE_REGISTER_INFOS_MIPS_STRUCT
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, gpr) + \
+ LLVM_EXTENSION offsetof(GPR_linux_mips, regname))
+
+// Computes the offset of the given FPR in the extended data area.
+#define FPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR_linux_mips, regname))
+
+// Computes the offset of the given MSA in the extended data area.
+#define MSA_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, msa) + \
+ LLVM_EXTENSION offsetof(MSA_linux_mips, regname))
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((GPR_linux_mips *) NULL)->reg) / 2, \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ gpr_##reg##_mips }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+const uint8_t dwarf_opcode_mips[] = {
+ llvm::dwarf::DW_OP_regx, dwarf_sr_mips, llvm::dwarf::DW_OP_lit1,
+ llvm::dwarf::DW_OP_lit26, llvm::dwarf::DW_OP_shl, llvm::dwarf::DW_OP_and,
+ llvm::dwarf::DW_OP_lit26, llvm::dwarf::DW_OP_shr};
+
+#define DEFINE_FPR(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((FPR_linux_mips *) NULL)->reg), \
+ FPR_OFFSET(reg), eEncodingIEEE754, eFormatFloat, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ fpr_##reg##_mips }, \
+ NULL, NULL, dwarf_opcode_mips, \
+ sizeof(dwarf_opcode_mips) \
+ }
+
+#define DEFINE_FPR_INFO(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((FPR_linux_mips *) NULL)->reg), \
+ FPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ fpr_##reg##_mips }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_MSA(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((MSA_linux_mips *) 0)->reg), \
+ MSA_OFFSET(reg), eEncodingVector, eFormatVectorOfUInt8, \
+ {kind1, kind2, kind3, kind4, \
+ msa_##reg##_mips }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_MSA_INFO(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((MSA_linux_mips *) 0)->reg), \
+ MSA_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ msa_##reg##_mips }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+// RegisterKind: EH_Frame, DWARF, Generic, Procss Plugin, LLDB
+
+static RegisterInfo g_register_infos_mips[] = {
+ DEFINE_GPR(zero, "zero", dwarf_zero_mips, dwarf_zero_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r1, "at", dwarf_r1_mips, dwarf_r1_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r2, nullptr, dwarf_r2_mips, dwarf_r2_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r3, nullptr, dwarf_r3_mips, dwarf_r3_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r4, nullptr, dwarf_r4_mips, dwarf_r4_mips,
+ LLDB_REGNUM_GENERIC_ARG1),
+ DEFINE_GPR(r5, nullptr, dwarf_r5_mips, dwarf_r5_mips,
+ LLDB_REGNUM_GENERIC_ARG2),
+ DEFINE_GPR(r6, nullptr, dwarf_r6_mips, dwarf_r6_mips,
+ LLDB_REGNUM_GENERIC_ARG3),
+ DEFINE_GPR(r7, nullptr, dwarf_r7_mips, dwarf_r7_mips,
+ LLDB_REGNUM_GENERIC_ARG4),
+ DEFINE_GPR(r8, nullptr, dwarf_r8_mips, dwarf_r8_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r9, nullptr, dwarf_r9_mips, dwarf_r9_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r10, nullptr, dwarf_r10_mips, dwarf_r10_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r11, nullptr, dwarf_r11_mips, dwarf_r11_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r12, nullptr, dwarf_r12_mips, dwarf_r12_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, nullptr, dwarf_r13_mips, dwarf_r13_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, nullptr, dwarf_r14_mips, dwarf_r14_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, nullptr, dwarf_r15_mips, dwarf_r15_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r16, nullptr, dwarf_r16_mips, dwarf_r16_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r17, nullptr, dwarf_r17_mips, dwarf_r17_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r18, nullptr, dwarf_r18_mips, dwarf_r18_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r19, nullptr, dwarf_r19_mips, dwarf_r19_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r20, nullptr, dwarf_r20_mips, dwarf_r20_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r21, nullptr, dwarf_r21_mips, dwarf_r21_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r22, nullptr, dwarf_r22_mips, dwarf_r22_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r23, nullptr, dwarf_r23_mips, dwarf_r23_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r24, nullptr, dwarf_r24_mips, dwarf_r24_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r25, nullptr, dwarf_r25_mips, dwarf_r25_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r26, nullptr, dwarf_r26_mips, dwarf_r26_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r27, nullptr, dwarf_r27_mips, dwarf_r27_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(gp, "gp", dwarf_gp_mips, dwarf_gp_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(sp, "sp", dwarf_sp_mips, dwarf_sp_mips, LLDB_REGNUM_GENERIC_SP),
+ DEFINE_GPR(r30, "fp", dwarf_r30_mips, dwarf_r30_mips,
+ LLDB_REGNUM_GENERIC_FP),
+ DEFINE_GPR(ra, "ra", dwarf_ra_mips, dwarf_ra_mips, LLDB_REGNUM_GENERIC_RA),
+ DEFINE_GPR(sr, "status", dwarf_sr_mips, dwarf_sr_mips,
+ LLDB_REGNUM_GENERIC_FLAGS),
+ DEFINE_GPR(mullo, nullptr, dwarf_lo_mips, dwarf_lo_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(mulhi, nullptr, dwarf_hi_mips, dwarf_hi_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(badvaddr, nullptr, dwarf_bad_mips, dwarf_bad_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(cause, nullptr, dwarf_cause_mips, dwarf_cause_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(pc, nullptr, dwarf_pc_mips, dwarf_pc_mips,
+ LLDB_REGNUM_GENERIC_PC),
+ DEFINE_GPR(config5, nullptr, dwarf_config5_mips, dwarf_config5_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f0, nullptr, dwarf_f0_mips, dwarf_f0_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f1, nullptr, dwarf_f1_mips, dwarf_f1_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f2, nullptr, dwarf_f2_mips, dwarf_f2_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f3, nullptr, dwarf_f3_mips, dwarf_f3_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f4, nullptr, dwarf_f4_mips, dwarf_f4_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f5, nullptr, dwarf_f5_mips, dwarf_f5_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f6, nullptr, dwarf_f6_mips, dwarf_f6_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f7, nullptr, dwarf_f7_mips, dwarf_f7_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f8, nullptr, dwarf_f8_mips, dwarf_f8_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f9, nullptr, dwarf_f9_mips, dwarf_f9_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f10, nullptr, dwarf_f10_mips, dwarf_f10_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f11, nullptr, dwarf_f11_mips, dwarf_f11_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f12, nullptr, dwarf_f12_mips, dwarf_f12_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f13, nullptr, dwarf_f13_mips, dwarf_f13_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f14, nullptr, dwarf_f14_mips, dwarf_f14_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f15, nullptr, dwarf_f15_mips, dwarf_f15_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f16, nullptr, dwarf_f16_mips, dwarf_f16_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f17, nullptr, dwarf_f17_mips, dwarf_f17_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f18, nullptr, dwarf_f18_mips, dwarf_f18_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f19, nullptr, dwarf_f19_mips, dwarf_f19_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f20, nullptr, dwarf_f20_mips, dwarf_f20_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f21, nullptr, dwarf_f21_mips, dwarf_f21_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f22, nullptr, dwarf_f22_mips, dwarf_f22_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f23, nullptr, dwarf_f23_mips, dwarf_f23_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f24, nullptr, dwarf_f24_mips, dwarf_f24_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f25, nullptr, dwarf_f25_mips, dwarf_f25_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f26, nullptr, dwarf_f26_mips, dwarf_f26_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f27, nullptr, dwarf_f27_mips, dwarf_f27_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f28, nullptr, dwarf_f28_mips, dwarf_f28_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f29, nullptr, dwarf_f29_mips, dwarf_f29_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f30, nullptr, dwarf_f30_mips, dwarf_f30_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f31, nullptr, dwarf_f31_mips, dwarf_f31_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(fcsr, nullptr, dwarf_fcsr_mips, dwarf_fcsr_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(fir, nullptr, dwarf_fir_mips, dwarf_fir_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(config5, nullptr, dwarf_config5_mips, dwarf_config5_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w0, nullptr, dwarf_w0_mips, dwarf_w0_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w1, nullptr, dwarf_w1_mips, dwarf_w1_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w2, nullptr, dwarf_w2_mips, dwarf_w2_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w3, nullptr, dwarf_w3_mips, dwarf_w3_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w4, nullptr, dwarf_w4_mips, dwarf_w4_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w5, nullptr, dwarf_w5_mips, dwarf_w5_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w6, nullptr, dwarf_w6_mips, dwarf_w6_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w7, nullptr, dwarf_w7_mips, dwarf_w7_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w8, nullptr, dwarf_w8_mips, dwarf_w8_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w9, nullptr, dwarf_w9_mips, dwarf_w9_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w10, nullptr, dwarf_w10_mips, dwarf_w10_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w11, nullptr, dwarf_w11_mips, dwarf_w11_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w12, nullptr, dwarf_w12_mips, dwarf_w12_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w13, nullptr, dwarf_w13_mips, dwarf_w13_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w14, nullptr, dwarf_w14_mips, dwarf_w14_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w15, nullptr, dwarf_w15_mips, dwarf_w15_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w16, nullptr, dwarf_w16_mips, dwarf_w16_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w17, nullptr, dwarf_w17_mips, dwarf_w17_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w18, nullptr, dwarf_w18_mips, dwarf_w18_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w19, nullptr, dwarf_w19_mips, dwarf_w19_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w20, nullptr, dwarf_w10_mips, dwarf_w20_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w21, nullptr, dwarf_w21_mips, dwarf_w21_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w22, nullptr, dwarf_w22_mips, dwarf_w22_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w23, nullptr, dwarf_w23_mips, dwarf_w23_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w24, nullptr, dwarf_w24_mips, dwarf_w24_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w25, nullptr, dwarf_w25_mips, dwarf_w25_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w26, nullptr, dwarf_w26_mips, dwarf_w26_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w27, nullptr, dwarf_w27_mips, dwarf_w27_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w28, nullptr, dwarf_w28_mips, dwarf_w28_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w29, nullptr, dwarf_w29_mips, dwarf_w29_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w30, nullptr, dwarf_w30_mips, dwarf_w30_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w31, nullptr, dwarf_w31_mips, dwarf_w31_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(mcsr, nullptr, dwarf_mcsr_mips, dwarf_mcsr_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(mir, nullptr, dwarf_mir_mips, dwarf_mir_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(fcsr, nullptr, dwarf_fcsr_mips, dwarf_fcsr_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(fir, nullptr, dwarf_fir_mips, dwarf_fir_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(config5, nullptr, dwarf_config5_mips, dwarf_config5_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM)};
+
+static_assert((sizeof(g_register_infos_mips) /
+ sizeof(g_register_infos_mips[0])) == k_num_registers_mips,
+ "g_register_infos_mips has wrong number of register infos");
+
+#undef GPR_OFFSET
+#undef FPR_OFFSET
+#undef MSA_OFFSET
+#undef DEFINE_GPR
+#undef DEFINE_FPR
+#undef DEFINE_FPR_INFO
+#undef DEFINE_MSA
+#undef DEFINE_MSA_INFO
+
+#endif // DECLARE_REGISTER_INFOS_MIPS_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips64.h
new file mode 100644
index 000000000000..b6e218ea4414
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips64.h
@@ -0,0 +1,435 @@
+//===-- RegisterInfos_mips64.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stddef.h>
+
+#include "lldb/Core/dwarf.h"
+#include "llvm/Support/Compiler.h"
+
+
+#ifdef DECLARE_REGISTER_INFOS_MIPS64_STRUCT
+
+// Computes the offset of the given GPR in the user data area.
+#ifdef LINUX_MIPS64
+#define GPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, gpr) + \
+ LLVM_EXTENSION offsetof(GPR_linux_mips, regname))
+#else
+#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR_freebsd_mips, regname))
+#endif
+
+// Computes the offset of the given FPR in the extended data area.
+#define FPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR_linux_mips, regname))
+
+// Computes the offset of the given MSA in the extended data area.
+#define MSA_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, msa) + \
+ LLVM_EXTENSION offsetof(MSA_linux_mips, regname))
+
+// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB
+
+// Note that the size and offset will be updated by platform-specific classes.
+#ifdef LINUX_MIPS64
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((GPR_linux_mips *) 0)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ gpr_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+#else
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((GPR_freebsd_mips *) 0)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ gpr_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+#endif
+
+#define DEFINE_GPR_INFO(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((GPR_linux_mips *) 0)->reg) / 2, \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ gpr_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+const uint8_t dwarf_opcode_mips64[] = {
+ llvm::dwarf::DW_OP_regx, dwarf_sr_mips64, llvm::dwarf::DW_OP_lit1,
+ llvm::dwarf::DW_OP_lit26, llvm::dwarf::DW_OP_shl, llvm::dwarf::DW_OP_and,
+ llvm::dwarf::DW_OP_lit26, llvm::dwarf::DW_OP_shr};
+
+#define DEFINE_FPR(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((FPR_linux_mips *) 0)->reg), \
+ FPR_OFFSET(reg), eEncodingIEEE754, eFormatFloat, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ fpr_##reg##_mips64 }, \
+ NULL, NULL, dwarf_opcode_mips64, \
+ sizeof(dwarf_opcode_mips64) \
+ }
+
+#define DEFINE_FPR_INFO(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((FPR_linux_mips *) 0)->reg), \
+ FPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ fpr_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_MSA(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((MSA_linux_mips *) 0)->reg), \
+ MSA_OFFSET(reg), eEncodingVector, eFormatVectorOfUInt8, \
+ {kind1, kind2, kind3, kind4, \
+ msa_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_MSA_INFO(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((MSA_linux_mips *) 0)->reg), \
+ MSA_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ msa_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+static RegisterInfo g_register_infos_mips64[] = {
+// General purpose registers. EH_Frame, DWARF,
+// Generic, Process Plugin
+#ifndef LINUX_MIPS64
+ DEFINE_GPR(zero, "r0", dwarf_zero_mips64, dwarf_zero_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r1, nullptr, dwarf_r1_mips64, dwarf_r1_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r2, nullptr, dwarf_r2_mips64, dwarf_r2_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r3, nullptr, dwarf_r3_mips64, dwarf_r3_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r4, nullptr, dwarf_r4_mips64, dwarf_r4_mips64,
+ LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r5, nullptr, dwarf_r5_mips64, dwarf_r5_mips64,
+ LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r6, nullptr, dwarf_r6_mips64, dwarf_r6_mips64,
+ LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r7, nullptr, dwarf_r7_mips64, dwarf_r7_mips64,
+ LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r8, nullptr, dwarf_r8_mips64, dwarf_r8_mips64,
+ LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r9, nullptr, dwarf_r9_mips64, dwarf_r9_mips64,
+ LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r10, nullptr, dwarf_r10_mips64, dwarf_r10_mips64,
+ LLDB_REGNUM_GENERIC_ARG7, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r11, nullptr, dwarf_r11_mips64, dwarf_r11_mips64,
+ LLDB_REGNUM_GENERIC_ARG8, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r12, nullptr, dwarf_r12_mips64, dwarf_r12_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, nullptr, dwarf_r13_mips64, dwarf_r13_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, nullptr, dwarf_r14_mips64, dwarf_r14_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, nullptr, dwarf_r15_mips64, dwarf_r15_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r16, nullptr, dwarf_r16_mips64, dwarf_r16_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r17, nullptr, dwarf_r17_mips64, dwarf_r17_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r18, nullptr, dwarf_r18_mips64, dwarf_r18_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r19, nullptr, dwarf_r19_mips64, dwarf_r19_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r20, nullptr, dwarf_r20_mips64, dwarf_r20_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r21, nullptr, dwarf_r21_mips64, dwarf_r21_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r22, nullptr, dwarf_r22_mips64, dwarf_r22_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r23, nullptr, dwarf_r23_mips64, dwarf_r23_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r24, nullptr, dwarf_r24_mips64, dwarf_r24_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r25, nullptr, dwarf_r25_mips64, dwarf_r25_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r26, nullptr, dwarf_r26_mips64, dwarf_r26_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r27, nullptr, dwarf_r27_mips64, dwarf_r27_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(gp, "r28", dwarf_gp_mips64, dwarf_gp_mips64, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(sp, "r29", dwarf_sp_mips64, dwarf_sp_mips64,
+ LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r30, nullptr, dwarf_r30_mips64, dwarf_r30_mips64,
+ LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ra, "r31", dwarf_ra_mips64, dwarf_ra_mips64,
+ LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(sr, nullptr, dwarf_sr_mips64, dwarf_sr_mips64,
+ LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(mullo, nullptr, dwarf_lo_mips64, dwarf_lo_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(mulhi, nullptr, dwarf_hi_mips64, dwarf_hi_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(badvaddr, nullptr, dwarf_bad_mips64, dwarf_bad_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(cause, nullptr, dwarf_cause_mips64, dwarf_cause_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(pc, "pc", dwarf_pc_mips64, dwarf_pc_mips64,
+ LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ic, nullptr, dwarf_ic_mips64, dwarf_ic_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(dummy, nullptr, dwarf_dummy_mips64, dwarf_dummy_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+#else
+ DEFINE_GPR(zero, "r0", dwarf_zero_mips64, dwarf_zero_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r1, nullptr, dwarf_r1_mips64, dwarf_r1_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r2, nullptr, dwarf_r2_mips64, dwarf_r2_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r3, nullptr, dwarf_r3_mips64, dwarf_r3_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r4, nullptr, dwarf_r4_mips64, dwarf_r4_mips64,
+ LLDB_REGNUM_GENERIC_ARG1),
+ DEFINE_GPR(r5, nullptr, dwarf_r5_mips64, dwarf_r5_mips64,
+ LLDB_REGNUM_GENERIC_ARG2),
+ DEFINE_GPR(r6, nullptr, dwarf_r6_mips64, dwarf_r6_mips64,
+ LLDB_REGNUM_GENERIC_ARG3),
+ DEFINE_GPR(r7, nullptr, dwarf_r7_mips64, dwarf_r7_mips64,
+ LLDB_REGNUM_GENERIC_ARG4),
+ DEFINE_GPR(r8, nullptr, dwarf_r8_mips64, dwarf_r8_mips64,
+ LLDB_REGNUM_GENERIC_ARG5),
+ DEFINE_GPR(r9, nullptr, dwarf_r9_mips64, dwarf_r9_mips64,
+ LLDB_REGNUM_GENERIC_ARG6),
+ DEFINE_GPR(r10, nullptr, dwarf_r10_mips64, dwarf_r10_mips64,
+ LLDB_REGNUM_GENERIC_ARG7),
+ DEFINE_GPR(r11, nullptr, dwarf_r11_mips64, dwarf_r11_mips64,
+ LLDB_REGNUM_GENERIC_ARG8),
+ DEFINE_GPR(r12, nullptr, dwarf_r12_mips64, dwarf_r12_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, nullptr, dwarf_r13_mips64, dwarf_r13_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, nullptr, dwarf_r14_mips64, dwarf_r14_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, nullptr, dwarf_r15_mips64, dwarf_r15_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r16, nullptr, dwarf_r16_mips64, dwarf_r16_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r17, nullptr, dwarf_r17_mips64, dwarf_r17_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r18, nullptr, dwarf_r18_mips64, dwarf_r18_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r19, nullptr, dwarf_r19_mips64, dwarf_r19_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r20, nullptr, dwarf_r20_mips64, dwarf_r20_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r21, nullptr, dwarf_r21_mips64, dwarf_r21_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r22, nullptr, dwarf_r22_mips64, dwarf_r22_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r23, nullptr, dwarf_r23_mips64, dwarf_r23_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r24, nullptr, dwarf_r24_mips64, dwarf_r24_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r25, nullptr, dwarf_r25_mips64, dwarf_r25_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r26, nullptr, dwarf_r26_mips64, dwarf_r26_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r27, nullptr, dwarf_r27_mips64, dwarf_r27_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(gp, "r28", dwarf_gp_mips64, dwarf_gp_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(sp, "r29", dwarf_sp_mips64, dwarf_sp_mips64,
+ LLDB_REGNUM_GENERIC_SP),
+ DEFINE_GPR(r30, nullptr, dwarf_r30_mips64, dwarf_r30_mips64,
+ LLDB_REGNUM_GENERIC_FP),
+ DEFINE_GPR(ra, "r31", dwarf_ra_mips64, dwarf_ra_mips64,
+ LLDB_REGNUM_GENERIC_RA),
+ DEFINE_GPR_INFO(sr, nullptr, dwarf_sr_mips64, dwarf_sr_mips64,
+ LLDB_REGNUM_GENERIC_FLAGS),
+ DEFINE_GPR(mullo, nullptr, dwarf_lo_mips64, dwarf_lo_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(mulhi, nullptr, dwarf_hi_mips64, dwarf_hi_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(badvaddr, nullptr, dwarf_bad_mips64, dwarf_bad_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR_INFO(cause, nullptr, dwarf_cause_mips64, dwarf_cause_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(pc, "pc", dwarf_pc_mips64, dwarf_pc_mips64,
+ LLDB_REGNUM_GENERIC_PC),
+ DEFINE_GPR_INFO(config5, nullptr, dwarf_config5_mips64,
+ dwarf_config5_mips64, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f0, nullptr, dwarf_f0_mips64, dwarf_f0_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f1, nullptr, dwarf_f1_mips64, dwarf_f1_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f2, nullptr, dwarf_f2_mips64, dwarf_f2_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f3, nullptr, dwarf_f3_mips64, dwarf_f3_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f4, nullptr, dwarf_f4_mips64, dwarf_f4_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f5, nullptr, dwarf_f5_mips64, dwarf_f5_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f6, nullptr, dwarf_f6_mips64, dwarf_f6_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f7, nullptr, dwarf_f7_mips64, dwarf_f7_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f8, nullptr, dwarf_f8_mips64, dwarf_f8_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f9, nullptr, dwarf_f9_mips64, dwarf_f9_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f10, nullptr, dwarf_f10_mips64, dwarf_f10_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f11, nullptr, dwarf_f11_mips64, dwarf_f11_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f12, nullptr, dwarf_f12_mips64, dwarf_f12_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f13, nullptr, dwarf_f13_mips64, dwarf_f13_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f14, nullptr, dwarf_f14_mips64, dwarf_f14_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f15, nullptr, dwarf_f15_mips64, dwarf_f15_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f16, nullptr, dwarf_f16_mips64, dwarf_f16_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f17, nullptr, dwarf_f17_mips64, dwarf_f17_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f18, nullptr, dwarf_f18_mips64, dwarf_f18_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f19, nullptr, dwarf_f19_mips64, dwarf_f19_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f20, nullptr, dwarf_f20_mips64, dwarf_f20_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f21, nullptr, dwarf_f21_mips64, dwarf_f21_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f22, nullptr, dwarf_f22_mips64, dwarf_f22_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f23, nullptr, dwarf_f23_mips64, dwarf_f23_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f24, nullptr, dwarf_f24_mips64, dwarf_f24_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f25, nullptr, dwarf_f25_mips64, dwarf_f25_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f26, nullptr, dwarf_f26_mips64, dwarf_f26_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f27, nullptr, dwarf_f27_mips64, dwarf_f27_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f28, nullptr, dwarf_f28_mips64, dwarf_f28_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f29, nullptr, dwarf_f29_mips64, dwarf_f29_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f30, nullptr, dwarf_f30_mips64, dwarf_f30_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f31, nullptr, dwarf_f31_mips64, dwarf_f31_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(fcsr, nullptr, dwarf_fcsr_mips64, dwarf_fcsr_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(fir, nullptr, dwarf_fir_mips64, dwarf_fir_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(config5, nullptr, dwarf_config5_mips64,
+ dwarf_config5_mips64, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w0, nullptr, dwarf_w0_mips64, dwarf_w0_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w1, nullptr, dwarf_w1_mips64, dwarf_w1_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w2, nullptr, dwarf_w2_mips64, dwarf_w2_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w3, nullptr, dwarf_w3_mips64, dwarf_w3_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w4, nullptr, dwarf_w4_mips64, dwarf_w4_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w5, nullptr, dwarf_w5_mips64, dwarf_w5_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w6, nullptr, dwarf_w6_mips64, dwarf_w6_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w7, nullptr, dwarf_w7_mips64, dwarf_w7_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w8, nullptr, dwarf_w8_mips64, dwarf_w8_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w9, nullptr, dwarf_w9_mips64, dwarf_w9_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w10, nullptr, dwarf_w10_mips64, dwarf_w10_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w11, nullptr, dwarf_w11_mips64, dwarf_w11_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w12, nullptr, dwarf_w12_mips64, dwarf_w12_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w13, nullptr, dwarf_w13_mips64, dwarf_w13_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w14, nullptr, dwarf_w14_mips64, dwarf_w14_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w15, nullptr, dwarf_w15_mips64, dwarf_w15_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w16, nullptr, dwarf_w16_mips64, dwarf_w16_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w17, nullptr, dwarf_w17_mips64, dwarf_w17_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w18, nullptr, dwarf_w18_mips64, dwarf_w18_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w19, nullptr, dwarf_w19_mips64, dwarf_w19_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w20, nullptr, dwarf_w10_mips64, dwarf_w20_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w21, nullptr, dwarf_w21_mips64, dwarf_w21_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w22, nullptr, dwarf_w22_mips64, dwarf_w22_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w23, nullptr, dwarf_w23_mips64, dwarf_w23_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w24, nullptr, dwarf_w24_mips64, dwarf_w24_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w25, nullptr, dwarf_w25_mips64, dwarf_w25_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w26, nullptr, dwarf_w26_mips64, dwarf_w26_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w27, nullptr, dwarf_w27_mips64, dwarf_w27_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w28, nullptr, dwarf_w28_mips64, dwarf_w28_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w29, nullptr, dwarf_w29_mips64, dwarf_w29_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w30, nullptr, dwarf_w30_mips64, dwarf_w30_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w31, nullptr, dwarf_w31_mips64, dwarf_w31_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(mcsr, nullptr, dwarf_mcsr_mips64, dwarf_mcsr_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(mir, nullptr, dwarf_mir_mips64, dwarf_mir_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(fcsr, nullptr, dwarf_fcsr_mips64, dwarf_fcsr_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(fir, nullptr, dwarf_fir_mips64, dwarf_fir_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(config5, nullptr, dwarf_config5_mips64,
+ dwarf_config5_mips64, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM)
+#endif
+};
+
+static_assert((sizeof(g_register_infos_mips64) /
+ sizeof(g_register_infos_mips64[0])) == k_num_registers_mips64,
+ "g_register_infos_mips64 has wrong number of register infos");
+
+#undef DEFINE_GPR
+#undef DEFINE_GPR_INFO
+#undef DEFINE_FPR
+#undef DEFINE_FPR_INFO
+#undef DEFINE_MSA
+#undef DEFINE_MSA_INFO
+#undef GPR_OFFSET
+#undef FPR_OFFSET
+#undef MSA_OFFSET
+
+#endif // DECLARE_REGISTER_INFOS_MIPS64_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_powerpc.h
new file mode 100644
index 000000000000..51be31f8e028
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_powerpc.h
@@ -0,0 +1,228 @@
+//===-- RegisterInfos_powerpc.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include <stddef.h>
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) (offsetof(GPR, regname))
+#define FPR_OFFSET(regname) (offsetof(FPR, regname))
+#define VMX_OFFSET(regname) (offsetof(VMX, regname))
+#define GPR_SIZE(regname) (sizeof(((GPR *)NULL)->regname))
+
+#ifdef DECLARE_REGISTER_INFOS_POWERPC_STRUCT
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, lldb_kind) \
+ { \
+ #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {dwarf_##reg##_powerpc, \
+ dwarf_##reg##_powerpc, lldb_kind, \
+ LLDB_INVALID_REGNUM, \
+ gpr_##reg##_powerpc }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_FPR(reg, lldb_kind) \
+ { \
+ #reg, NULL, 8, FPR_OFFSET(reg), eEncodingIEEE754, eFormatFloat, \
+ {dwarf_##reg##_powerpc, dwarf_##reg##_powerpc, \
+ lldb_kind, LLDB_INVALID_REGNUM, \
+ fpr_##reg##_powerpc }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_VMX(reg, lldb_kind) \
+ { \
+ #reg, NULL, 16, VMX_OFFSET(reg), eEncodingVector, eFormatVectorOfUInt32, \
+ {dwarf_##reg##_powerpc, dwarf_##reg##_powerpc, \
+ lldb_kind, LLDB_INVALID_REGNUM, \
+ vmx_##reg##_powerpc }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+// General purpose registers. EH_Frame, DWARF,
+// Generic, Process Plugin
+#define POWERPC_REGS \
+ DEFINE_GPR(r0, NULL, LLDB_INVALID_REGNUM) \
+ , DEFINE_GPR(r1, "sp", LLDB_REGNUM_GENERIC_SP), \
+ DEFINE_GPR(r2, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r3, "arg1", LLDB_REGNUM_GENERIC_ARG1), \
+ DEFINE_GPR(r4, "arg2", LLDB_REGNUM_GENERIC_ARG2), \
+ DEFINE_GPR(r5, "arg3", LLDB_REGNUM_GENERIC_ARG3), \
+ DEFINE_GPR(r6, "arg4", LLDB_REGNUM_GENERIC_ARG4), \
+ DEFINE_GPR(r7, "arg5", LLDB_REGNUM_GENERIC_ARG5), \
+ DEFINE_GPR(r8, "arg6", LLDB_REGNUM_GENERIC_ARG6), \
+ DEFINE_GPR(r9, "arg7", LLDB_REGNUM_GENERIC_ARG7), \
+ DEFINE_GPR(r10, "arg8", LLDB_REGNUM_GENERIC_ARG8), \
+ DEFINE_GPR(r11, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r12, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r13, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r14, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r15, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r16, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r17, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r18, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r19, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r20, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r21, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r22, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r23, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r24, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r25, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r26, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r27, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r28, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r29, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r30, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r31, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(lr, "lr", LLDB_REGNUM_GENERIC_RA), \
+ DEFINE_GPR(cr, "cr", LLDB_REGNUM_GENERIC_FLAGS), \
+ DEFINE_GPR(xer, "xer", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(ctr, "ctr", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(pc, "pc", LLDB_REGNUM_GENERIC_PC), \
+ DEFINE_FPR(f0, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f1, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f2, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f3, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f4, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f5, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f6, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f7, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f8, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f9, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f10, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f11, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f12, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f13, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f14, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f15, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f16, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f17, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f18, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f19, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f20, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f21, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f22, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f23, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f24, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f25, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f26, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f27, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f28, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f29, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f30, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f31, LLDB_INVALID_REGNUM), \
+ {"fpscr", \
+ NULL, \
+ 8, \
+ FPR_OFFSET(fpscr), \
+ eEncodingUint, \
+ eFormatHex, \
+ {dwarf_fpscr_powerpc, dwarf_fpscr_powerpc, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, fpr_fpscr_powerpc}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ DEFINE_VMX(v0, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v1, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v2, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v3, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v4, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v5, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v6, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v7, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v8, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v9, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v10, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v11, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v12, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v13, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v14, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v15, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v16, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v17, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v18, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v19, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v20, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v21, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v22, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v23, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v24, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v25, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v26, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v27, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v28, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v29, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v30, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v31, LLDB_INVALID_REGNUM), \
+ {"vrsave", \
+ NULL, \
+ 4, \
+ VMX_OFFSET(vrsave), \
+ eEncodingUint, \
+ eFormatHex, \
+ {dwarf_vrsave_powerpc, dwarf_vrsave_powerpc, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, vmx_vrsave_powerpc}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ {"vscr", \
+ NULL, \
+ 4, \
+ VMX_OFFSET(vscr), \
+ eEncodingUint, \
+ eFormatHex, \
+ {dwarf_vscr_powerpc, dwarf_vscr_powerpc, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, vmx_vscr_powerpc}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0},
+
+static RegisterInfo g_register_infos_powerpc64[] = {
+#define GPR GPR64
+ POWERPC_REGS
+#undef GPR
+};
+
+static RegisterInfo g_register_infos_powerpc32[] = {
+#define GPR GPR32
+ POWERPC_REGS
+#undef GPR
+};
+
+static RegisterInfo g_register_infos_powerpc64_32[] = {
+#define GPR GPR64
+#undef GPR_SIZE
+#define GPR_SIZE(reg) (sizeof(uint32_t))
+#undef GPR_OFFSET
+#define GPR_OFFSET(regname) \
+ (offsetof(GPR, regname) + (sizeof(((GPR *)NULL)->regname) - GPR_SIZE(reg)))
+ POWERPC_REGS
+#undef GPR
+};
+
+static_assert((sizeof(g_register_infos_powerpc32) /
+ sizeof(g_register_infos_powerpc32[0])) ==
+ k_num_registers_powerpc,
+ "g_register_infos_powerpc32 has wrong number of register infos");
+static_assert((sizeof(g_register_infos_powerpc64) /
+ sizeof(g_register_infos_powerpc64[0])) ==
+ k_num_registers_powerpc,
+ "g_register_infos_powerpc64 has wrong number of register infos");
+static_assert(sizeof(g_register_infos_powerpc64_32) ==
+ sizeof(g_register_infos_powerpc64),
+ "g_register_infos_powerpc64_32 doesn't match size of "
+ "g_register_infos_powerpc64");
+
+#undef DEFINE_FPR
+#undef DEFINE_GPR
+
+#endif // DECLARE_REGISTER_INFOS_POWERPC_STRUCT
+
+#undef GPR_OFFSET
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64.h
new file mode 100644
index 000000000000..1086d3db0b06
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64.h
@@ -0,0 +1,329 @@
+//===-- RegisterInfos_ppc64.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef DECLARE_REGISTER_INFOS_PPC64_STRUCT
+
+#include <stddef.h>
+
+// Computes the offset of the given GPR_PPC64 in the user data area.
+#define GPR_PPC64_OFFSET(regname) (offsetof(GPR_PPC64, regname))
+#define FPR_PPC64_OFFSET(regname) (offsetof(FPR_PPC64, regname) \
+ + sizeof(GPR_PPC64))
+#define VMX_PPC64_OFFSET(regname) (offsetof(VMX_PPC64, regname) \
+ + sizeof(GPR_PPC64) + sizeof(FPR_PPC64))
+#define GPR_PPC64_SIZE(regname) (sizeof(((GPR_PPC64 *)NULL)->regname))
+
+#include "Utility/PPC64_DWARF_Registers.h"
+#include "lldb-ppc64-register-enums.h"
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR_PPC64(reg, alt, lldb_kind) \
+ { \
+ #reg, alt, GPR_PPC64_SIZE(reg), GPR_PPC64_OFFSET(reg), lldb::eEncodingUint,\
+ lldb::eFormatHex, \
+ {ppc64_dwarf::dwarf_##reg##_ppc64, \
+ ppc64_dwarf::dwarf_##reg##_ppc64, \
+ lldb_kind, \
+ LLDB_INVALID_REGNUM, \
+ gpr_##reg##_ppc64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_FPR_PPC64(reg, alt, lldb_kind) \
+ { \
+#reg, alt, 8, FPR_PPC64_OFFSET(reg), lldb::eEncodingIEEE754, \
+ lldb::eFormatFloat, \
+ {ppc64_dwarf::dwarf_##reg##_ppc64, \
+ ppc64_dwarf::dwarf_##reg##_ppc64, lldb_kind, LLDB_INVALID_REGNUM, \
+ fpr_##reg##_ppc64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_VMX_PPC64(reg, lldb_kind) \
+ { \
+#reg, NULL, 16, VMX_PPC64_OFFSET(reg), lldb::eEncodingVector, \
+ lldb::eFormatVectorOfUInt32, \
+ {ppc64_dwarf::dwarf_##reg##_ppc64, \
+ ppc64_dwarf::dwarf_##reg##_ppc64, lldb_kind, LLDB_INVALID_REGNUM, \
+ vmx_##reg##_ppc64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+// General purpose registers.
+// EH_Frame, Generic, Process Plugin
+#define PPC64_REGS \
+ DEFINE_GPR_PPC64(r0, NULL, LLDB_INVALID_REGNUM) \
+ , DEFINE_GPR_PPC64(r1, "sp", LLDB_REGNUM_GENERIC_SP), \
+ DEFINE_GPR_PPC64(r2, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r3, "arg1", LLDB_REGNUM_GENERIC_ARG1), \
+ DEFINE_GPR_PPC64(r4, "arg2", LLDB_REGNUM_GENERIC_ARG2), \
+ DEFINE_GPR_PPC64(r5, "arg3", LLDB_REGNUM_GENERIC_ARG3), \
+ DEFINE_GPR_PPC64(r6, "arg4", LLDB_REGNUM_GENERIC_ARG4), \
+ DEFINE_GPR_PPC64(r7, "arg5", LLDB_REGNUM_GENERIC_ARG5), \
+ DEFINE_GPR_PPC64(r8, "arg6", LLDB_REGNUM_GENERIC_ARG6), \
+ DEFINE_GPR_PPC64(r9, "arg7", LLDB_REGNUM_GENERIC_ARG7), \
+ DEFINE_GPR_PPC64(r10, "arg8", LLDB_REGNUM_GENERIC_ARG8), \
+ DEFINE_GPR_PPC64(r11, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r12, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r13, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r14, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r15, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r16, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r17, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r18, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r19, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r20, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r21, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r22, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r23, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r24, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r25, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r26, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r27, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r28, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r29, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r30, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r31, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(cr, "cr", LLDB_REGNUM_GENERIC_FLAGS), \
+ DEFINE_GPR_PPC64(msr, "msr", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(xer, "xer", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(lr, "lr", LLDB_REGNUM_GENERIC_RA), \
+ DEFINE_GPR_PPC64(ctr, "ctr", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(pc, "pc", LLDB_REGNUM_GENERIC_PC), \
+ DEFINE_FPR_PPC64(f0, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f1, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f2, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f3, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f4, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f5, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f6, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f7, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f8, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f9, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f10, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f11, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f12, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f13, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f14, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f15, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f16, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f17, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f18, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f19, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f20, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f21, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f22, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f23, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f24, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f25, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f26, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f27, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f28, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f29, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f30, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f31, NULL, LLDB_INVALID_REGNUM), \
+ {"fpscr", \
+ NULL, \
+ 8, \
+ FPR_PPC64_OFFSET(fpscr), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64_dwarf::dwarf_fpscr_ppc64, \
+ ppc64_dwarf::dwarf_fpscr_ppc64, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, fpr_fpscr_ppc64}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ DEFINE_VMX_PPC64(vr0, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr1, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr2, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr3, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr4, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr5, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr6, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr7, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr8, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr9, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr10, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr11, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr12, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr13, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr14, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr15, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr16, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr17, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr18, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr19, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr20, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr21, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr22, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr23, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr24, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr25, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr26, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr27, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr28, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr29, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr30, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr31, LLDB_INVALID_REGNUM), \
+ {"vscr", \
+ NULL, \
+ 4, \
+ VMX_PPC64_OFFSET(vscr), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64_dwarf::dwarf_vscr_ppc64, ppc64_dwarf::dwarf_vscr_ppc64, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, vmx_vscr_ppc64}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ {"vrsave", \
+ NULL, \
+ 4, \
+ VMX_PPC64_OFFSET(vrsave), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64_dwarf::dwarf_vrsave_ppc64, \
+ ppc64_dwarf::dwarf_vrsave_ppc64, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, vmx_vrsave_ppc64}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, /* */
+
+typedef struct _GPR_PPC64 {
+ uint64_t r0;
+ uint64_t r1;
+ uint64_t r2;
+ uint64_t r3;
+ uint64_t r4;
+ uint64_t r5;
+ uint64_t r6;
+ uint64_t r7;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t r16;
+ uint64_t r17;
+ uint64_t r18;
+ uint64_t r19;
+ uint64_t r20;
+ uint64_t r21;
+ uint64_t r22;
+ uint64_t r23;
+ uint64_t r24;
+ uint64_t r25;
+ uint64_t r26;
+ uint64_t r27;
+ uint64_t r28;
+ uint64_t r29;
+ uint64_t r30;
+ uint64_t r31;
+ uint64_t cr;
+ uint64_t msr;
+ uint64_t xer;
+ uint64_t lr;
+ uint64_t ctr;
+ uint64_t pc;
+ uint64_t pad[3];
+} GPR_PPC64;
+
+typedef struct _FPR_PPC64 {
+ uint64_t f0;
+ uint64_t f1;
+ uint64_t f2;
+ uint64_t f3;
+ uint64_t f4;
+ uint64_t f5;
+ uint64_t f6;
+ uint64_t f7;
+ uint64_t f8;
+ uint64_t f9;
+ uint64_t f10;
+ uint64_t f11;
+ uint64_t f12;
+ uint64_t f13;
+ uint64_t f14;
+ uint64_t f15;
+ uint64_t f16;
+ uint64_t f17;
+ uint64_t f18;
+ uint64_t f19;
+ uint64_t f20;
+ uint64_t f21;
+ uint64_t f22;
+ uint64_t f23;
+ uint64_t f24;
+ uint64_t f25;
+ uint64_t f26;
+ uint64_t f27;
+ uint64_t f28;
+ uint64_t f29;
+ uint64_t f30;
+ uint64_t f31;
+ uint64_t fpscr;
+} FPR_PPC64;
+
+typedef struct _VMX_PPC64 {
+ uint32_t vr0[4];
+ uint32_t vr1[4];
+ uint32_t vr2[4];
+ uint32_t vr3[4];
+ uint32_t vr4[4];
+ uint32_t vr5[4];
+ uint32_t vr6[4];
+ uint32_t vr7[4];
+ uint32_t vr8[4];
+ uint32_t vr9[4];
+ uint32_t vr10[4];
+ uint32_t vr11[4];
+ uint32_t vr12[4];
+ uint32_t vr13[4];
+ uint32_t vr14[4];
+ uint32_t vr15[4];
+ uint32_t vr16[4];
+ uint32_t vr17[4];
+ uint32_t vr18[4];
+ uint32_t vr19[4];
+ uint32_t vr20[4];
+ uint32_t vr21[4];
+ uint32_t vr22[4];
+ uint32_t vr23[4];
+ uint32_t vr24[4];
+ uint32_t vr25[4];
+ uint32_t vr26[4];
+ uint32_t vr27[4];
+ uint32_t vr28[4];
+ uint32_t vr29[4];
+ uint32_t vr30[4];
+ uint32_t vr31[4];
+ uint32_t pad[2];
+ uint32_t vscr[2];
+ uint32_t vrsave;
+} VMX_PPC64;
+
+
+static lldb_private::RegisterInfo g_register_infos_ppc64[] = {
+ PPC64_REGS
+};
+
+static_assert((sizeof(g_register_infos_ppc64) /
+ sizeof(g_register_infos_ppc64[0])) ==
+ k_num_registers_ppc64,
+ "g_register_infos_powerpc64 has wrong number of register infos");
+
+#undef DEFINE_FPR_PPC64
+#undef DEFINE_GPR_PPC64
+#undef DEFINE_VMX_PPC64
+
+#endif // DECLARE_REGISTER_INFOS_PPC64_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64le.h
new file mode 100644
index 000000000000..0b099a53d875
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64le.h
@@ -0,0 +1,474 @@
+//===-- RegisterInfos_ppc64le.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+
+#include <stddef.h>
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) (offsetof(GPR, regname))
+#define FPR_OFFSET(regname) (offsetof(FPR, regname) + sizeof(GPR))
+#define VMX_OFFSET(regname) (offsetof(VMX, regname) + sizeof(GPR) + sizeof(FPR))
+#define VSX_OFFSET(regname) \
+ (offsetof(VSX, regname) + sizeof(GPR) + sizeof(FPR) + sizeof(VMX))
+#define GPR_SIZE(regname) (sizeof(((GPR *)NULL)->regname))
+
+#include "Utility/PPC64LE_DWARF_Registers.h"
+#include "lldb-ppc64le-register-enums.h"
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, lldb_kind) \
+ { \
+ #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64le_dwarf::dwarf_##reg##_ppc64le,\
+ ppc64le_dwarf::dwarf_##reg##_ppc64le,\
+ lldb_kind, \
+ LLDB_INVALID_REGNUM, \
+ gpr_##reg##_ppc64le }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_FPR(reg, alt, lldb_kind) \
+ { \
+#reg, alt, 8, FPR_OFFSET(reg), lldb::eEncodingIEEE754, lldb::eFormatFloat, \
+ {ppc64le_dwarf::dwarf_##reg##_ppc64le, \
+ ppc64le_dwarf::dwarf_##reg##_ppc64le, lldb_kind, LLDB_INVALID_REGNUM, \
+ fpr_##reg##_ppc64le }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_VMX(reg, lldb_kind) \
+ { \
+#reg, NULL, 16, VMX_OFFSET(reg), lldb::eEncodingVector, \
+ lldb::eFormatVectorOfUInt32, \
+ {ppc64le_dwarf::dwarf_##reg##_ppc64le, \
+ ppc64le_dwarf::dwarf_##reg##_ppc64le, lldb_kind, LLDB_INVALID_REGNUM, \
+ vmx_##reg##_ppc64le }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_VSX(reg, lldb_kind) \
+ { \
+#reg, NULL, 16, VSX_OFFSET(reg), lldb::eEncodingVector, \
+ lldb::eFormatVectorOfUInt32, \
+ {ppc64le_dwarf::dwarf_##reg##_ppc64le, \
+ ppc64le_dwarf::dwarf_##reg##_ppc64le, lldb_kind, LLDB_INVALID_REGNUM, \
+ vsx_##reg##_ppc64le }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+// General purpose registers.
+// EH_Frame, Generic, Process Plugin
+#define POWERPC_REGS \
+ DEFINE_GPR(r0, NULL, LLDB_INVALID_REGNUM) \
+ , DEFINE_GPR(r1, "sp", LLDB_REGNUM_GENERIC_SP), \
+ DEFINE_GPR(r2, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r3, "arg1", LLDB_REGNUM_GENERIC_ARG1), \
+ DEFINE_GPR(r4, "arg2", LLDB_REGNUM_GENERIC_ARG2), \
+ DEFINE_GPR(r5, "arg3", LLDB_REGNUM_GENERIC_ARG3), \
+ DEFINE_GPR(r6, "arg4", LLDB_REGNUM_GENERIC_ARG4), \
+ DEFINE_GPR(r7, "arg5", LLDB_REGNUM_GENERIC_ARG5), \
+ DEFINE_GPR(r8, "arg6", LLDB_REGNUM_GENERIC_ARG6), \
+ DEFINE_GPR(r9, "arg7", LLDB_REGNUM_GENERIC_ARG7), \
+ DEFINE_GPR(r10, "arg8", LLDB_REGNUM_GENERIC_ARG8), \
+ DEFINE_GPR(r11, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r12, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r13, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r14, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r15, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r16, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r17, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r18, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r19, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r20, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r21, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r22, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r23, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r24, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r25, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r26, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r27, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r28, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r29, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r30, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r31, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(pc, "pc", LLDB_REGNUM_GENERIC_PC), \
+ DEFINE_GPR(msr, "msr", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(origr3, "orig_r3", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(ctr, "ctr", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(lr, "lr", LLDB_REGNUM_GENERIC_RA), \
+ DEFINE_GPR(xer, "xer", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(cr, "cr", LLDB_REGNUM_GENERIC_FLAGS), \
+ DEFINE_GPR(softe, "softe", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(trap, "trap", LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f0, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f1, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f2, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f3, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f4, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f5, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f6, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f7, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f8, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f9, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f10, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f11, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f12, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f13, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f14, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f15, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f16, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f17, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f18, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f19, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f20, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f21, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f22, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f23, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f24, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f25, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f26, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f27, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f28, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f29, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f30, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f31, NULL, LLDB_INVALID_REGNUM), \
+ {"fpscr", \
+ NULL, \
+ 8, \
+ FPR_OFFSET(fpscr), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64le_dwarf::dwarf_fpscr_ppc64le, \
+ ppc64le_dwarf::dwarf_fpscr_ppc64le, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, fpr_fpscr_ppc64le}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ DEFINE_VMX(vr0, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr1, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr2, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr3, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr4, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr5, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr6, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr7, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr8, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr9, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr10, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr11, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr12, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr13, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr14, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr15, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr16, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr17, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr18, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr19, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr20, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr21, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr22, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr23, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr24, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr25, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr26, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr27, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr28, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr29, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr30, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr31, LLDB_INVALID_REGNUM), \
+ {"vscr", \
+ NULL, \
+ 4, \
+ VMX_OFFSET(vscr), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64le_dwarf::dwarf_vscr_ppc64le, ppc64le_dwarf::dwarf_vscr_ppc64le, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, vmx_vscr_ppc64le}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ {"vrsave", \
+ NULL, \
+ 4, \
+ VMX_OFFSET(vrsave), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64le_dwarf::dwarf_vrsave_ppc64le, \
+ ppc64le_dwarf::dwarf_vrsave_ppc64le, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, vmx_vrsave_ppc64le}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ DEFINE_VSX(vs0, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs1, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs2, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs3, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs4, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs5, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs6, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs7, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs8, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs9, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs10, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs11, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs12, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs13, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs14, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs15, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs16, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs17, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs18, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs19, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs20, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs21, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs22, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs23, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs24, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs25, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs26, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs27, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs28, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs29, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs30, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs31, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs32, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs33, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs34, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs35, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs36, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs37, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs38, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs39, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs40, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs41, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs42, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs43, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs44, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs45, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs46, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs47, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs48, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs49, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs50, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs51, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs52, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs53, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs54, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs55, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs56, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs57, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs58, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs59, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs50, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs61, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs62, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs63, LLDB_INVALID_REGNUM), /* */
+
+typedef struct _GPR {
+ uint64_t r0;
+ uint64_t r1;
+ uint64_t r2;
+ uint64_t r3;
+ uint64_t r4;
+ uint64_t r5;
+ uint64_t r6;
+ uint64_t r7;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t r16;
+ uint64_t r17;
+ uint64_t r18;
+ uint64_t r19;
+ uint64_t r20;
+ uint64_t r21;
+ uint64_t r22;
+ uint64_t r23;
+ uint64_t r24;
+ uint64_t r25;
+ uint64_t r26;
+ uint64_t r27;
+ uint64_t r28;
+ uint64_t r29;
+ uint64_t r30;
+ uint64_t r31;
+ uint64_t pc;
+ uint64_t msr;
+ uint64_t origr3;
+ uint64_t ctr;
+ uint64_t lr;
+ uint64_t xer;
+ uint64_t cr;
+ uint64_t softe;
+ uint64_t trap;
+ uint64_t pad[3];
+} GPR;
+
+typedef struct _FPR {
+ uint64_t f0;
+ uint64_t f1;
+ uint64_t f2;
+ uint64_t f3;
+ uint64_t f4;
+ uint64_t f5;
+ uint64_t f6;
+ uint64_t f7;
+ uint64_t f8;
+ uint64_t f9;
+ uint64_t f10;
+ uint64_t f11;
+ uint64_t f12;
+ uint64_t f13;
+ uint64_t f14;
+ uint64_t f15;
+ uint64_t f16;
+ uint64_t f17;
+ uint64_t f18;
+ uint64_t f19;
+ uint64_t f20;
+ uint64_t f21;
+ uint64_t f22;
+ uint64_t f23;
+ uint64_t f24;
+ uint64_t f25;
+ uint64_t f26;
+ uint64_t f27;
+ uint64_t f28;
+ uint64_t f29;
+ uint64_t f30;
+ uint64_t f31;
+ uint64_t fpscr;
+} FPR;
+
+typedef struct _VMX {
+ uint32_t vr0[4];
+ uint32_t vr1[4];
+ uint32_t vr2[4];
+ uint32_t vr3[4];
+ uint32_t vr4[4];
+ uint32_t vr5[4];
+ uint32_t vr6[4];
+ uint32_t vr7[4];
+ uint32_t vr8[4];
+ uint32_t vr9[4];
+ uint32_t vr10[4];
+ uint32_t vr11[4];
+ uint32_t vr12[4];
+ uint32_t vr13[4];
+ uint32_t vr14[4];
+ uint32_t vr15[4];
+ uint32_t vr16[4];
+ uint32_t vr17[4];
+ uint32_t vr18[4];
+ uint32_t vr19[4];
+ uint32_t vr20[4];
+ uint32_t vr21[4];
+ uint32_t vr22[4];
+ uint32_t vr23[4];
+ uint32_t vr24[4];
+ uint32_t vr25[4];
+ uint32_t vr26[4];
+ uint32_t vr27[4];
+ uint32_t vr28[4];
+ uint32_t vr29[4];
+ uint32_t vr30[4];
+ uint32_t vr31[4];
+ uint32_t pad[2];
+ uint32_t vscr[2];
+ uint32_t vrsave;
+} VMX;
+
+typedef struct _VSX {
+ uint32_t vs0[4];
+ uint32_t vs1[4];
+ uint32_t vs2[4];
+ uint32_t vs3[4];
+ uint32_t vs4[4];
+ uint32_t vs5[4];
+ uint32_t vs6[4];
+ uint32_t vs7[4];
+ uint32_t vs8[4];
+ uint32_t vs9[4];
+ uint32_t vs10[4];
+ uint32_t vs11[4];
+ uint32_t vs12[4];
+ uint32_t vs13[4];
+ uint32_t vs14[4];
+ uint32_t vs15[4];
+ uint32_t vs16[4];
+ uint32_t vs17[4];
+ uint32_t vs18[4];
+ uint32_t vs19[4];
+ uint32_t vs20[4];
+ uint32_t vs21[4];
+ uint32_t vs22[4];
+ uint32_t vs23[4];
+ uint32_t vs24[4];
+ uint32_t vs25[4];
+ uint32_t vs26[4];
+ uint32_t vs27[4];
+ uint32_t vs28[4];
+ uint32_t vs29[4];
+ uint32_t vs30[4];
+ uint32_t vs31[4];
+ uint32_t vs32[4];
+ uint32_t vs33[4];
+ uint32_t vs34[4];
+ uint32_t vs35[4];
+ uint32_t vs36[4];
+ uint32_t vs37[4];
+ uint32_t vs38[4];
+ uint32_t vs39[4];
+ uint32_t vs40[4];
+ uint32_t vs41[4];
+ uint32_t vs42[4];
+ uint32_t vs43[4];
+ uint32_t vs44[4];
+ uint32_t vs45[4];
+ uint32_t vs46[4];
+ uint32_t vs47[4];
+ uint32_t vs48[4];
+ uint32_t vs49[4];
+ uint32_t vs50[4];
+ uint32_t vs51[4];
+ uint32_t vs52[4];
+ uint32_t vs53[4];
+ uint32_t vs54[4];
+ uint32_t vs55[4];
+ uint32_t vs56[4];
+ uint32_t vs57[4];
+ uint32_t vs58[4];
+ uint32_t vs59[4];
+ uint32_t vs60[4];
+ uint32_t vs61[4];
+ uint32_t vs62[4];
+ uint32_t vs63[4];
+} VSX;
+
+static lldb_private::RegisterInfo g_register_infos_ppc64le[] = {
+ POWERPC_REGS
+};
+
+static_assert((sizeof(g_register_infos_ppc64le) /
+ sizeof(g_register_infos_ppc64le[0])) ==
+ k_num_registers_ppc64le,
+ "g_register_infos_powerpc64 has wrong number of register infos");
+
+#undef DEFINE_FPR
+#undef DEFINE_GPR
+#undef DEFINE_VMX
+#undef DEFINE_VSX
+
+#endif // DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_s390x.h
new file mode 100644
index 000000000000..11344ff8ee79
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_s390x.h
@@ -0,0 +1,124 @@
+//===-- RegisterInfos_s390x.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stddef.h>
+
+#include "llvm/Support/Compiler.h"
+
+
+#ifdef DECLARE_REGISTER_INFOS_S390X_STRUCT
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(num) (16 + 8 * num)
+// Computes the offset of the given ACR in the user data area.
+#define ACR_OFFSET(num) (16 + 8 * 16 + 4 * num)
+// Computes the offset of the given FPR in the extended data area.
+#define FPR_OFFSET(num) (8 + 8 * num)
+
+// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB
+
+#define DEFINE_GPR(name, size, offset, alt, generic) \
+ { \
+ #name, alt, size, offset, eEncodingUint, eFormatHex, \
+ {dwarf_##name##_s390x, dwarf_##name##_s390x, generic, \
+ LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_GPR_NODWARF(name, size, offset, alt, generic) \
+ { \
+ #name, alt, size, offset, eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, generic, \
+ LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_FPR(name, size, offset) \
+ { \
+ #name, NULL, size, offset, eEncodingUint, eFormatHex, \
+ {dwarf_##name##_s390x, dwarf_##name##_s390x, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_FPR_NODWARF(name, size, offset) \
+ { \
+ #name, NULL, size, offset, eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+static RegisterInfo g_register_infos_s390x[] = {
+ // General purpose registers.
+ DEFINE_GPR(r0, 8, GPR_OFFSET(0), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r1, 8, GPR_OFFSET(1), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r2, 8, GPR_OFFSET(2), "arg1", LLDB_REGNUM_GENERIC_ARG1),
+ DEFINE_GPR(r3, 8, GPR_OFFSET(3), "arg2", LLDB_REGNUM_GENERIC_ARG2),
+ DEFINE_GPR(r4, 8, GPR_OFFSET(4), "arg3", LLDB_REGNUM_GENERIC_ARG3),
+ DEFINE_GPR(r5, 8, GPR_OFFSET(5), "arg4", LLDB_REGNUM_GENERIC_ARG4),
+ DEFINE_GPR(r6, 8, GPR_OFFSET(6), "arg5", LLDB_REGNUM_GENERIC_ARG5),
+ DEFINE_GPR(r7, 8, GPR_OFFSET(7), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r8, 8, GPR_OFFSET(8), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r9, 8, GPR_OFFSET(9), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r10, 8, GPR_OFFSET(10), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r11, 8, GPR_OFFSET(11), "fp", LLDB_REGNUM_GENERIC_FP),
+ DEFINE_GPR(r12, 8, GPR_OFFSET(12), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, 8, GPR_OFFSET(13), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, 8, GPR_OFFSET(14), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, 8, GPR_OFFSET(15), "sp", LLDB_REGNUM_GENERIC_SP),
+ DEFINE_GPR(acr0, 4, ACR_OFFSET(0), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr1, 4, ACR_OFFSET(1), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr2, 4, ACR_OFFSET(2), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr3, 4, ACR_OFFSET(3), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr4, 4, ACR_OFFSET(4), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr5, 4, ACR_OFFSET(5), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr6, 4, ACR_OFFSET(6), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr7, 4, ACR_OFFSET(7), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr8, 4, ACR_OFFSET(8), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr9, 4, ACR_OFFSET(9), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr10, 4, ACR_OFFSET(10), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr11, 4, ACR_OFFSET(11), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr12, 4, ACR_OFFSET(12), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr13, 4, ACR_OFFSET(13), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr14, 4, ACR_OFFSET(14), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr15, 4, ACR_OFFSET(15), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(pswm, 8, 0, "flags", LLDB_REGNUM_GENERIC_FLAGS),
+ DEFINE_GPR(pswa, 8, 8, "pc", LLDB_REGNUM_GENERIC_PC),
+
+ // Floating point registers.
+ DEFINE_FPR(f0, 8, FPR_OFFSET(0)), DEFINE_FPR(f1, 8, FPR_OFFSET(1)),
+ DEFINE_FPR(f2, 8, FPR_OFFSET(2)), DEFINE_FPR(f3, 8, FPR_OFFSET(3)),
+ DEFINE_FPR(f4, 8, FPR_OFFSET(4)), DEFINE_FPR(f5, 8, FPR_OFFSET(5)),
+ DEFINE_FPR(f6, 8, FPR_OFFSET(6)), DEFINE_FPR(f7, 8, FPR_OFFSET(7)),
+ DEFINE_FPR(f8, 8, FPR_OFFSET(8)), DEFINE_FPR(f9, 8, FPR_OFFSET(9)),
+ DEFINE_FPR(f10, 8, FPR_OFFSET(10)), DEFINE_FPR(f11, 8, FPR_OFFSET(11)),
+ DEFINE_FPR(f12, 8, FPR_OFFSET(12)), DEFINE_FPR(f13, 8, FPR_OFFSET(13)),
+ DEFINE_FPR(f14, 8, FPR_OFFSET(14)), DEFINE_FPR(f15, 8, FPR_OFFSET(15)),
+ DEFINE_FPR_NODWARF(fpc, 4, 0),
+
+ // Linux operating-specific info.
+ DEFINE_GPR_NODWARF(orig_r2, 8, 16 + 16 * 8 + 16 * 4, nullptr,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR_NODWARF(last_break, 8, 0, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR_NODWARF(system_call, 4, 0, nullptr, LLDB_INVALID_REGNUM),
+};
+
+static_assert((sizeof(g_register_infos_s390x) /
+ sizeof(g_register_infos_s390x[0])) == k_num_registers_s390x,
+ "g_register_infos_s390x has wrong number of register infos");
+
+#undef GPR_OFFSET
+#undef ACR_OFFSET
+#undef FPR_OFFSET
+#undef DEFINE_GPR
+#undef DEFINE_GPR_NODWARF
+#undef DEFINE_FPR
+#undef DEFINE_FPR_NODWARF
+
+#endif // DECLARE_REGISTER_INFOS_S390X_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64.h
new file mode 100644
index 000000000000..4a3b3c73fd6b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64.h
@@ -0,0 +1,466 @@
+//===-- RegisterInfos_x86_64.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// This file is meant to be textually included. Do not #include modular
+// headers here.
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname))
+
+// Computes the offset of the given FPR in the extended data area.
+#define FPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR, fxsave) + \
+ LLVM_EXTENSION offsetof(FXSAVE, regname))
+
+// Computes the offset of the YMM register assembled from register halves.
+// Based on DNBArchImplX86_64.cpp from debugserver
+#define YMM_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR, xsave) + \
+ LLVM_EXTENSION offsetof(XSAVE, ymmh[0]) + (32 * reg_index))
+
+#define BNDR_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR, xsave) + \
+ LLVM_EXTENSION offsetof(XSAVE, mpxr[reg_index]))
+
+#define BNDC_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR, xsave) + \
+ LLVM_EXTENSION offsetof(XSAVE, mpxc[reg_index]))
+
+#ifdef DECLARE_REGISTER_INFOS_X86_64_STRUCT
+
+// Number of bytes needed to represent a FPR.
+#define FPR_SIZE(reg) sizeof(((FXSAVE *)nullptr)->reg)
+
+// Number of bytes needed to represent the i'th FP register.
+#define FP_SIZE sizeof(((MMSReg *)nullptr)->bytes)
+
+// Number of bytes needed to represent an XMM register.
+#define XMM_SIZE sizeof(XMMReg)
+
+// Number of bytes needed to represent a YMM register.
+#define YMM_SIZE sizeof(YMMReg)
+
+// Number of bytes needed to represent MPX registers.
+#define BNDR_SIZE sizeof(MPXReg)
+#define BNDC_SIZE sizeof(MPXCsr)
+
+#define DR_SIZE sizeof(((DBG *)nullptr)->dr[0])
+
+// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((GPR *)nullptr)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ lldb_##reg##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_FPR(name, reg, kind1, kind2, kind3, kind4) \
+ { \
+ #name, nullptr, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ lldb_##name##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_FP_ST(reg, i) \
+ { \
+ #reg #i, nullptr, FP_SIZE, \
+ LLVM_EXTENSION FPR_OFFSET( \
+ stmm[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {dwarf_st##i##_x86_64, dwarf_st##i##_x86_64, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_st##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_FP_MM(reg, i) \
+ { \
+ #reg #i, nullptr, sizeof(uint64_t), \
+ LLVM_EXTENSION FPR_OFFSET( \
+ stmm[i]), eEncodingUint, eFormatHex, \
+ {dwarf_mm##i##_x86_64, dwarf_mm##i##_x86_64, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_mm##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_XMM(reg, i) \
+ { \
+ #reg #i, nullptr, XMM_SIZE, \
+ LLVM_EXTENSION FPR_OFFSET( \
+ reg[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {dwarf_##reg##i##_x86_64, dwarf_##reg##i##_x86_64, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_YMM(reg, i) \
+ { \
+ #reg #i, nullptr, YMM_SIZE, \
+ LLVM_EXTENSION YMM_OFFSET(i), eEncodingVector, eFormatVectorOfUInt8, \
+ {dwarf_##reg##i##h_x86_64, \
+ dwarf_##reg##i##h_x86_64, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_BNDR(reg, i) \
+ { \
+ #reg #i, nullptr, BNDR_SIZE, \
+ LLVM_EXTENSION BNDR_OFFSET(i), eEncodingVector, eFormatVectorOfUInt64, \
+ {dwarf_##reg##i##_x86_64, \
+ dwarf_##reg##i##_x86_64, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_BNDC(name, i) \
+ { \
+ #name, nullptr, BNDC_SIZE, \
+ LLVM_EXTENSION BNDC_OFFSET(i), eEncodingVector, eFormatVectorOfUInt8, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_##name##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_DR(reg, i) \
+ { \
+ #reg #i, nullptr, DR_SIZE, \
+ DR_OFFSET(i), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_32(reg32, reg64) \
+ { \
+ #reg32, nullptr, 4, \
+ GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg32##_x86_64 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg64, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg64, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_16(reg16, reg64) \
+ { \
+ #reg16, nullptr, 2, \
+ GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg16##_x86_64 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg64, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg64, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_8H(reg8, reg64) \
+ { \
+ #reg8, nullptr, 1, \
+ GPR_OFFSET(reg64) + 1, eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg8##_x86_64 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg64, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg64, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_8L(reg8, reg64) \
+ { \
+ #reg8, nullptr, 1, \
+ GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg8##_x86_64 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg64, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg64, nullptr, 0 \
+ }
+
+// clang-format off
+static RegisterInfo g_register_infos_x86_64[] = {
+// General purpose registers EH_Frame DWARF Generic Process Plugin
+// =========================== ================== ================ ========================= ====================
+ DEFINE_GPR(rax, nullptr, dwarf_rax_x86_64, dwarf_rax_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rbx, nullptr, dwarf_rbx_x86_64, dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rcx, "arg4", dwarf_rcx_x86_64, dwarf_rcx_x86_64, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rdx, "arg3", dwarf_rdx_x86_64, dwarf_rdx_x86_64, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rdi, "arg1", dwarf_rdi_x86_64, dwarf_rdi_x86_64, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rsi, "arg2", dwarf_rsi_x86_64, dwarf_rsi_x86_64, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rbp, "fp", dwarf_rbp_x86_64, dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rsp, "sp", dwarf_rsp_x86_64, dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r8, "arg5", dwarf_r8_x86_64, dwarf_r8_x86_64, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r9, "arg6", dwarf_r9_x86_64, dwarf_r9_x86_64, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r10, nullptr, dwarf_r10_x86_64, dwarf_r10_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r11, nullptr, dwarf_r11_x86_64, dwarf_r11_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r12, nullptr, dwarf_r12_x86_64, dwarf_r12_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, nullptr, dwarf_r13_x86_64, dwarf_r13_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, nullptr, dwarf_r14_x86_64, dwarf_r14_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, nullptr, dwarf_r15_x86_64, dwarf_r15_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rip, "pc", dwarf_rip_x86_64, dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rflags, "flags", dwarf_rflags_x86_64, dwarf_rflags_x86_64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(cs, nullptr, dwarf_cs_x86_64, dwarf_cs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(fs, nullptr, dwarf_fs_x86_64, dwarf_fs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(gs, nullptr, dwarf_gs_x86_64, dwarf_gs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ss, nullptr, dwarf_ss_x86_64, dwarf_ss_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ds, nullptr, dwarf_ds_x86_64, dwarf_ds_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(es, nullptr, dwarf_es_x86_64, dwarf_es_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+
+ DEFINE_GPR_PSEUDO_32(eax, rax), DEFINE_GPR_PSEUDO_32(ebx, rbx),
+ DEFINE_GPR_PSEUDO_32(ecx, rcx), DEFINE_GPR_PSEUDO_32(edx, rdx),
+ DEFINE_GPR_PSEUDO_32(edi, rdi), DEFINE_GPR_PSEUDO_32(esi, rsi),
+ DEFINE_GPR_PSEUDO_32(ebp, rbp), DEFINE_GPR_PSEUDO_32(esp, rsp),
+ DEFINE_GPR_PSEUDO_32(r8d, r8), DEFINE_GPR_PSEUDO_32(r9d, r9),
+ DEFINE_GPR_PSEUDO_32(r10d, r10), DEFINE_GPR_PSEUDO_32(r11d, r11),
+ DEFINE_GPR_PSEUDO_32(r12d, r12), DEFINE_GPR_PSEUDO_32(r13d, r13),
+ DEFINE_GPR_PSEUDO_32(r14d, r14), DEFINE_GPR_PSEUDO_32(r15d, r15),
+ DEFINE_GPR_PSEUDO_16(ax, rax), DEFINE_GPR_PSEUDO_16(bx, rbx),
+ DEFINE_GPR_PSEUDO_16(cx, rcx), DEFINE_GPR_PSEUDO_16(dx, rdx),
+ DEFINE_GPR_PSEUDO_16(di, rdi), DEFINE_GPR_PSEUDO_16(si, rsi),
+ DEFINE_GPR_PSEUDO_16(bp, rbp), DEFINE_GPR_PSEUDO_16(sp, rsp),
+ DEFINE_GPR_PSEUDO_16(r8w, r8), DEFINE_GPR_PSEUDO_16(r9w, r9),
+ DEFINE_GPR_PSEUDO_16(r10w, r10), DEFINE_GPR_PSEUDO_16(r11w, r11),
+ DEFINE_GPR_PSEUDO_16(r12w, r12), DEFINE_GPR_PSEUDO_16(r13w, r13),
+ DEFINE_GPR_PSEUDO_16(r14w, r14), DEFINE_GPR_PSEUDO_16(r15w, r15),
+ DEFINE_GPR_PSEUDO_8H(ah, rax), DEFINE_GPR_PSEUDO_8H(bh, rbx),
+ DEFINE_GPR_PSEUDO_8H(ch, rcx), DEFINE_GPR_PSEUDO_8H(dh, rdx),
+ DEFINE_GPR_PSEUDO_8L(al, rax), DEFINE_GPR_PSEUDO_8L(bl, rbx),
+ DEFINE_GPR_PSEUDO_8L(cl, rcx), DEFINE_GPR_PSEUDO_8L(dl, rdx),
+ DEFINE_GPR_PSEUDO_8L(dil, rdi), DEFINE_GPR_PSEUDO_8L(sil, rsi),
+ DEFINE_GPR_PSEUDO_8L(bpl, rbp), DEFINE_GPR_PSEUDO_8L(spl, rsp),
+ DEFINE_GPR_PSEUDO_8L(r8l, r8), DEFINE_GPR_PSEUDO_8L(r9l, r9),
+ DEFINE_GPR_PSEUDO_8L(r10l, r10), DEFINE_GPR_PSEUDO_8L(r11l, r11),
+ DEFINE_GPR_PSEUDO_8L(r12l, r12), DEFINE_GPR_PSEUDO_8L(r13l, r13),
+ DEFINE_GPR_PSEUDO_8L(r14l, r14), DEFINE_GPR_PSEUDO_8L(r15l, r15),
+
+// i387 Floating point registers. EH_frame DWARF Generic Process Plugin
+// ====================================== =============== ================== =================== ====================
+ DEFINE_FPR(fctrl, fctrl, dwarf_fctrl_x86_64, dwarf_fctrl_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fstat, fstat, dwarf_fstat_x86_64, dwarf_fstat_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(ftag, ftag, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fop, fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fiseg, ptr.i386_.fiseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fioff, ptr.i386_.fioff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(foseg, ptr.i386_.foseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fooff, ptr.i386_.fooff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(mxcsr, mxcsr, dwarf_mxcsr_x86_64, dwarf_mxcsr_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(mxcsrmask, mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+
+ // FP registers.
+ DEFINE_FP_ST(st, 0), DEFINE_FP_ST(st, 1), DEFINE_FP_ST(st, 2),
+ DEFINE_FP_ST(st, 3), DEFINE_FP_ST(st, 4), DEFINE_FP_ST(st, 5),
+ DEFINE_FP_ST(st, 6), DEFINE_FP_ST(st, 7), DEFINE_FP_MM(mm, 0),
+ DEFINE_FP_MM(mm, 1), DEFINE_FP_MM(mm, 2), DEFINE_FP_MM(mm, 3),
+ DEFINE_FP_MM(mm, 4), DEFINE_FP_MM(mm, 5), DEFINE_FP_MM(mm, 6),
+ DEFINE_FP_MM(mm, 7),
+
+ // XMM registers
+ DEFINE_XMM(xmm, 0), DEFINE_XMM(xmm, 1), DEFINE_XMM(xmm, 2),
+ DEFINE_XMM(xmm, 3), DEFINE_XMM(xmm, 4), DEFINE_XMM(xmm, 5),
+ DEFINE_XMM(xmm, 6), DEFINE_XMM(xmm, 7), DEFINE_XMM(xmm, 8),
+ DEFINE_XMM(xmm, 9), DEFINE_XMM(xmm, 10), DEFINE_XMM(xmm, 11),
+ DEFINE_XMM(xmm, 12), DEFINE_XMM(xmm, 13), DEFINE_XMM(xmm, 14),
+ DEFINE_XMM(xmm, 15),
+
+ // Copy of YMM registers assembled from xmm and ymmh
+ DEFINE_YMM(ymm, 0), DEFINE_YMM(ymm, 1), DEFINE_YMM(ymm, 2),
+ DEFINE_YMM(ymm, 3), DEFINE_YMM(ymm, 4), DEFINE_YMM(ymm, 5),
+ DEFINE_YMM(ymm, 6), DEFINE_YMM(ymm, 7), DEFINE_YMM(ymm, 8),
+ DEFINE_YMM(ymm, 9), DEFINE_YMM(ymm, 10), DEFINE_YMM(ymm, 11),
+ DEFINE_YMM(ymm, 12), DEFINE_YMM(ymm, 13), DEFINE_YMM(ymm, 14),
+ DEFINE_YMM(ymm, 15),
+
+ // MPX registers
+ DEFINE_BNDR(bnd, 0),
+ DEFINE_BNDR(bnd, 1),
+ DEFINE_BNDR(bnd, 2),
+ DEFINE_BNDR(bnd, 3),
+
+ DEFINE_BNDC(bndcfgu, 0),
+ DEFINE_BNDC(bndstatus, 1),
+
+ // Debug registers for lldb internal use
+ DEFINE_DR(dr, 0), DEFINE_DR(dr, 1), DEFINE_DR(dr, 2), DEFINE_DR(dr, 3),
+ DEFINE_DR(dr, 4), DEFINE_DR(dr, 5), DEFINE_DR(dr, 6), DEFINE_DR(dr, 7)};
+
+// clang-format on
+
+static_assert((sizeof(g_register_infos_x86_64) /
+ sizeof(g_register_infos_x86_64[0])) == k_num_registers_x86_64,
+ "g_register_infos_x86_64 has wrong number of register infos");
+
+#undef FPR_SIZE
+#undef FP_SIZE
+#undef XMM_SIZE
+#undef YMM_SIZE
+#undef DEFINE_GPR
+#undef DEFINE_FPR
+#undef DEFINE_FP
+#undef DEFINE_XMM
+#undef DEFINE_YMM
+#undef DEFINE_BNDR
+#undef DEFINE_BNDC
+#undef DEFINE_DR
+#undef DEFINE_GPR_PSEUDO_32
+#undef DEFINE_GPR_PSEUDO_16
+#undef DEFINE_GPR_PSEUDO_8H
+#undef DEFINE_GPR_PSEUDO_8L
+
+#endif // DECLARE_REGISTER_INFOS_X86_64_STRUCT
+
+#ifdef UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS
+
+#define UPDATE_GPR_INFO(reg, reg64) \
+ do { \
+ g_register_infos[lldb_##reg##_i386].byte_offset = GPR_OFFSET(reg64); \
+ } while (false);
+
+#define UPDATE_GPR_INFO_8H(reg, reg64) \
+ do { \
+ g_register_infos[lldb_##reg##_i386].byte_offset = GPR_OFFSET(reg64) + 1; \
+ } while (false);
+
+#define UPDATE_FPR_INFO(reg, reg64) \
+ do { \
+ g_register_infos[lldb_##reg##_i386].byte_offset = FPR_OFFSET(reg64); \
+ } while (false);
+
+#define UPDATE_FP_INFO(reg, i) \
+ do { \
+ g_register_infos[lldb_##reg##i##_i386].byte_offset = FPR_OFFSET(stmm[i]); \
+ } while (false);
+
+#define UPDATE_XMM_INFO(reg, i) \
+ do { \
+ g_register_infos[lldb_##reg##i##_i386].byte_offset = FPR_OFFSET(reg[i]); \
+ } while (false);
+
+#define UPDATE_YMM_INFO(reg, i) \
+ do { \
+ g_register_infos[lldb_##reg##i##_i386].byte_offset = YMM_OFFSET(i); \
+ } while (false);
+
+#define UPDATE_DR_INFO(reg_index) \
+ do { \
+ g_register_infos[lldb_dr##reg_index##_i386].byte_offset = \
+ DR_OFFSET(reg_index); \
+ } while (false);
+
+// Update the register offsets
+UPDATE_GPR_INFO(eax, rax);
+UPDATE_GPR_INFO(ebx, rbx);
+UPDATE_GPR_INFO(ecx, rcx);
+UPDATE_GPR_INFO(edx, rdx);
+UPDATE_GPR_INFO(edi, rdi);
+UPDATE_GPR_INFO(esi, rsi);
+UPDATE_GPR_INFO(ebp, rbp);
+UPDATE_GPR_INFO(esp, rsp);
+UPDATE_GPR_INFO(eip, rip);
+UPDATE_GPR_INFO(eflags, rflags);
+UPDATE_GPR_INFO(cs, cs);
+UPDATE_GPR_INFO(fs, fs);
+UPDATE_GPR_INFO(gs, gs);
+UPDATE_GPR_INFO(ss, ss);
+UPDATE_GPR_INFO(ds, ds);
+UPDATE_GPR_INFO(es, es);
+
+UPDATE_GPR_INFO(ax, rax);
+UPDATE_GPR_INFO(bx, rbx);
+UPDATE_GPR_INFO(cx, rcx);
+UPDATE_GPR_INFO(dx, rdx);
+UPDATE_GPR_INFO(di, rdi);
+UPDATE_GPR_INFO(si, rsi);
+UPDATE_GPR_INFO(bp, rbp);
+UPDATE_GPR_INFO(sp, rsp);
+UPDATE_GPR_INFO_8H(ah, rax);
+UPDATE_GPR_INFO_8H(bh, rbx);
+UPDATE_GPR_INFO_8H(ch, rcx);
+UPDATE_GPR_INFO_8H(dh, rdx);
+UPDATE_GPR_INFO(al, rax);
+UPDATE_GPR_INFO(bl, rbx);
+UPDATE_GPR_INFO(cl, rcx);
+UPDATE_GPR_INFO(dl, rdx);
+
+UPDATE_FPR_INFO(fctrl, fctrl);
+UPDATE_FPR_INFO(fstat, fstat);
+UPDATE_FPR_INFO(ftag, ftag);
+UPDATE_FPR_INFO(fop, fop);
+UPDATE_FPR_INFO(fiseg, ptr.i386_.fiseg);
+UPDATE_FPR_INFO(fioff, ptr.i386_.fioff);
+UPDATE_FPR_INFO(fooff, ptr.i386_.fooff);
+UPDATE_FPR_INFO(foseg, ptr.i386_.foseg);
+UPDATE_FPR_INFO(mxcsr, mxcsr);
+UPDATE_FPR_INFO(mxcsrmask, mxcsrmask);
+
+UPDATE_FP_INFO(st, 0);
+UPDATE_FP_INFO(st, 1);
+UPDATE_FP_INFO(st, 2);
+UPDATE_FP_INFO(st, 3);
+UPDATE_FP_INFO(st, 4);
+UPDATE_FP_INFO(st, 5);
+UPDATE_FP_INFO(st, 6);
+UPDATE_FP_INFO(st, 7);
+UPDATE_FP_INFO(mm, 0);
+UPDATE_FP_INFO(mm, 1);
+UPDATE_FP_INFO(mm, 2);
+UPDATE_FP_INFO(mm, 3);
+UPDATE_FP_INFO(mm, 4);
+UPDATE_FP_INFO(mm, 5);
+UPDATE_FP_INFO(mm, 6);
+UPDATE_FP_INFO(mm, 7);
+
+UPDATE_XMM_INFO(xmm, 0);
+UPDATE_XMM_INFO(xmm, 1);
+UPDATE_XMM_INFO(xmm, 2);
+UPDATE_XMM_INFO(xmm, 3);
+UPDATE_XMM_INFO(xmm, 4);
+UPDATE_XMM_INFO(xmm, 5);
+UPDATE_XMM_INFO(xmm, 6);
+UPDATE_XMM_INFO(xmm, 7);
+
+UPDATE_YMM_INFO(ymm, 0);
+UPDATE_YMM_INFO(ymm, 1);
+UPDATE_YMM_INFO(ymm, 2);
+UPDATE_YMM_INFO(ymm, 3);
+UPDATE_YMM_INFO(ymm, 4);
+UPDATE_YMM_INFO(ymm, 5);
+UPDATE_YMM_INFO(ymm, 6);
+UPDATE_YMM_INFO(ymm, 7);
+
+UPDATE_DR_INFO(0);
+UPDATE_DR_INFO(1);
+UPDATE_DR_INFO(2);
+UPDATE_DR_INFO(3);
+UPDATE_DR_INFO(4);
+UPDATE_DR_INFO(5);
+UPDATE_DR_INFO(6);
+UPDATE_DR_INFO(7);
+
+#undef UPDATE_GPR_INFO
+#undef UPDATE_GPR_INFO_8H
+#undef UPDATE_FPR_INFO
+#undef UPDATE_FP_INFO
+#undef UPDATE_XMM_INFO
+#undef UPDATE_YMM_INFO
+#undef UPDATE_DR_INFO
+
+#endif // UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS
+
+#undef GPR_OFFSET
+#undef FPR_OFFSET
+#undef YMM_OFFSET
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp
new file mode 100644
index 000000000000..588015a51ef1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp
@@ -0,0 +1,624 @@
+//===-- StopInfoMachException.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "StopInfoMachException.h"
+
+
+#if defined(__APPLE__)
+// Needed for the EXC_RESOURCE interpretation macros
+#include <kern/exc_resource.h>
+#endif
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *StopInfoMachException::GetDescription() {
+ if (m_description.empty() && m_value != 0) {
+ ExecutionContext exe_ctx(m_thread_wp.lock());
+ Target *target = exe_ctx.GetTargetPtr();
+ const llvm::Triple::ArchType cpu =
+ target ? target->GetArchitecture().GetMachine()
+ : llvm::Triple::UnknownArch;
+
+ const char *exc_desc = nullptr;
+ const char *code_label = "code";
+ const char *code_desc = nullptr;
+ const char *subcode_label = "subcode";
+ const char *subcode_desc = nullptr;
+
+#if defined(__APPLE__)
+ char code_desc_buf[32];
+ char subcode_desc_buf[32];
+#endif
+
+ switch (m_value) {
+ case 1: // EXC_BAD_ACCESS
+ exc_desc = "EXC_BAD_ACCESS";
+ subcode_label = "address";
+ switch (cpu) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ switch (m_exc_code) {
+ case 0xd:
+ code_desc = "EXC_I386_GPFLT";
+ m_exc_data_count = 1;
+ break;
+ }
+ break;
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ switch (m_exc_code) {
+ case 0x101:
+ code_desc = "EXC_ARM_DA_ALIGN";
+ break;
+ case 0x102:
+ code_desc = "EXC_ARM_DA_DEBUG";
+ break;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code) {
+ case 0x101:
+ code_desc = "EXC_PPC_VM_PROT_READ";
+ break;
+ case 0x102:
+ code_desc = "EXC_PPC_BADSPACE";
+ break;
+ case 0x103:
+ code_desc = "EXC_PPC_UNALIGNED";
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 2: // EXC_BAD_INSTRUCTION
+ exc_desc = "EXC_BAD_INSTRUCTION";
+ switch (cpu) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ if (m_exc_code == 1)
+ code_desc = "EXC_I386_INVOP";
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code) {
+ case 1:
+ code_desc = "EXC_PPC_INVALID_SYSCALL";
+ break;
+ case 2:
+ code_desc = "EXC_PPC_UNIPL_INST";
+ break;
+ case 3:
+ code_desc = "EXC_PPC_PRIVINST";
+ break;
+ case 4:
+ code_desc = "EXC_PPC_PRIVREG";
+ break;
+ case 5:
+ code_desc = "EXC_PPC_TRACE";
+ break;
+ case 6:
+ code_desc = "EXC_PPC_PERFMON";
+ break;
+ }
+ break;
+
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ if (m_exc_code == 1)
+ code_desc = "EXC_ARM_UNDEFINED";
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 3: // EXC_ARITHMETIC
+ exc_desc = "EXC_ARITHMETIC";
+ switch (cpu) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ switch (m_exc_code) {
+ case 1:
+ code_desc = "EXC_I386_DIV";
+ break;
+ case 2:
+ code_desc = "EXC_I386_INTO";
+ break;
+ case 3:
+ code_desc = "EXC_I386_NOEXT";
+ break;
+ case 4:
+ code_desc = "EXC_I386_EXTOVR";
+ break;
+ case 5:
+ code_desc = "EXC_I386_EXTERR";
+ break;
+ case 6:
+ code_desc = "EXC_I386_EMERR";
+ break;
+ case 7:
+ code_desc = "EXC_I386_BOUND";
+ break;
+ case 8:
+ code_desc = "EXC_I386_SSEEXTERR";
+ break;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code) {
+ case 1:
+ code_desc = "EXC_PPC_OVERFLOW";
+ break;
+ case 2:
+ code_desc = "EXC_PPC_ZERO_DIVIDE";
+ break;
+ case 3:
+ code_desc = "EXC_PPC_FLT_INEXACT";
+ break;
+ case 4:
+ code_desc = "EXC_PPC_FLT_ZERO_DIVIDE";
+ break;
+ case 5:
+ code_desc = "EXC_PPC_FLT_UNDERFLOW";
+ break;
+ case 6:
+ code_desc = "EXC_PPC_FLT_OVERFLOW";
+ break;
+ case 7:
+ code_desc = "EXC_PPC_FLT_NOT_A_NUMBER";
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 4: // EXC_EMULATION
+ exc_desc = "EXC_EMULATION";
+ break;
+
+ case 5: // EXC_SOFTWARE
+ exc_desc = "EXC_SOFTWARE";
+ if (m_exc_code == 0x10003) {
+ subcode_desc = "EXC_SOFT_SIGNAL";
+ subcode_label = "signo";
+ }
+ break;
+
+ case 6: // EXC_BREAKPOINT
+ {
+ exc_desc = "EXC_BREAKPOINT";
+ switch (cpu) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ switch (m_exc_code) {
+ case 1:
+ code_desc = "EXC_I386_SGL";
+ break;
+ case 2:
+ code_desc = "EXC_I386_BPT";
+ break;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code) {
+ case 1:
+ code_desc = "EXC_PPC_BREAKPOINT";
+ break;
+ }
+ break;
+
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ switch (m_exc_code) {
+ case 0x101:
+ code_desc = "EXC_ARM_DA_ALIGN";
+ break;
+ case 0x102:
+ code_desc = "EXC_ARM_DA_DEBUG";
+ break;
+ case 1:
+ code_desc = "EXC_ARM_BREAKPOINT";
+ break;
+ // FIXME temporary workaround, exc_code 0 does not really mean
+ // EXC_ARM_BREAKPOINT
+ case 0:
+ code_desc = "EXC_ARM_BREAKPOINT";
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } break;
+
+ case 7:
+ exc_desc = "EXC_SYSCALL";
+ break;
+
+ case 8:
+ exc_desc = "EXC_MACH_SYSCALL";
+ break;
+
+ case 9:
+ exc_desc = "EXC_RPC_ALERT";
+ break;
+
+ case 10:
+ exc_desc = "EXC_CRASH";
+ break;
+ case 11:
+ exc_desc = "EXC_RESOURCE";
+#if defined(__APPLE__)
+ {
+ int resource_type = EXC_RESOURCE_DECODE_RESOURCE_TYPE(m_exc_code);
+
+ code_label = "limit";
+ code_desc = code_desc_buf;
+ subcode_label = "observed";
+ subcode_desc = subcode_desc_buf;
+
+ switch (resource_type) {
+ case RESOURCE_TYPE_CPU:
+ exc_desc = "EXC_RESOURCE RESOURCE_TYPE_CPU";
+ snprintf(code_desc_buf, sizeof(code_desc_buf), "%d%%",
+ (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE(m_exc_code));
+ snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d%%",
+ (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE_OBSERVED(m_exc_subcode));
+ break;
+ case RESOURCE_TYPE_WAKEUPS:
+ exc_desc = "EXC_RESOURCE RESOURCE_TYPE_WAKEUPS";
+ snprintf(code_desc_buf, sizeof(code_desc_buf), "%d w/s",
+ (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_PERMITTED(m_exc_code));
+ snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d w/s",
+ (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_OBSERVED(m_exc_subcode));
+ break;
+ case RESOURCE_TYPE_MEMORY:
+ exc_desc = "EXC_RESOURCE RESOURCE_TYPE_MEMORY";
+ snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB",
+ (int)EXC_RESOURCE_HWM_DECODE_LIMIT(m_exc_code));
+ subcode_desc = nullptr;
+ subcode_label = "unused";
+ break;
+ case RESOURCE_TYPE_IO:
+ exc_desc = "EXC_RESOURCE RESOURCE_TYPE_IO";
+ snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB",
+ (int)EXC_RESOURCE_IO_DECODE_LIMIT(m_exc_code));
+ snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d MB",
+ (int)EXC_RESOURCE_IO_OBSERVED(m_exc_subcode));;
+ break;
+ }
+ }
+#endif
+ break;
+ case 12:
+ exc_desc = "EXC_GUARD";
+ break;
+ }
+
+ StreamString strm;
+
+ if (exc_desc)
+ strm.PutCString(exc_desc);
+ else
+ strm.Printf("EXC_??? (%" PRIu64 ")", m_value);
+
+ if (m_exc_data_count >= 1) {
+ if (code_desc)
+ strm.Printf(" (%s=%s", code_label, code_desc);
+ else
+ strm.Printf(" (%s=%" PRIu64, code_label, m_exc_code);
+ }
+
+ if (m_exc_data_count >= 2) {
+ if (subcode_desc)
+ strm.Printf(", %s=%s", subcode_label, subcode_desc);
+ else
+ strm.Printf(", %s=0x%" PRIx64, subcode_label, m_exc_subcode);
+ }
+
+ if (m_exc_data_count > 0)
+ strm.PutChar(')');
+
+ m_description = strm.GetString();
+ }
+ return m_description.c_str();
+}
+
+StopInfoSP StopInfoMachException::CreateStopReasonWithMachException(
+ Thread &thread, uint32_t exc_type, uint32_t exc_data_count,
+ uint64_t exc_code, uint64_t exc_sub_code, uint64_t exc_sub_sub_code,
+ bool pc_already_adjusted, bool adjust_pc_if_needed) {
+ if (exc_type != 0) {
+ uint32_t pc_decrement = 0;
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ const llvm::Triple::ArchType cpu =
+ target ? target->GetArchitecture().GetMachine()
+ : llvm::Triple::UnknownArch;
+
+ switch (exc_type) {
+ case 1: // EXC_BAD_ACCESS
+ break;
+
+ case 2: // EXC_BAD_INSTRUCTION
+ switch (cpu) {
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (exc_code) {
+ case 1: // EXC_PPC_INVALID_SYSCALL
+ case 2: // EXC_PPC_UNIPL_INST
+ case 3: // EXC_PPC_PRIVINST
+ case 4: // EXC_PPC_PRIVREG
+ break;
+ case 5: // EXC_PPC_TRACE
+ return StopInfo::CreateStopReasonToTrace(thread);
+ case 6: // EXC_PPC_PERFMON
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 3: // EXC_ARITHMETIC
+ case 4: // EXC_EMULATION
+ break;
+
+ case 5: // EXC_SOFTWARE
+ if (exc_code == 0x10003) // EXC_SOFT_SIGNAL
+ {
+ if (exc_sub_code == 5) {
+ // On MacOSX, a SIGTRAP can signify that a process has called exec,
+ // so we should check with our dynamic loader to verify.
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ DynamicLoader *dynamic_loader = process_sp->GetDynamicLoader();
+ if (dynamic_loader && dynamic_loader->ProcessDidExec()) {
+ // The program was re-exec'ed
+ return StopInfo::CreateStopReasonWithExec(thread);
+ }
+ // if (!process_did_exec)
+ // {
+ // // We have a SIGTRAP, make sure we
+ // didn't exec by checking
+ // // for the PC being at
+ // "_dyld_start"...
+ // lldb::StackFrameSP frame_sp
+ // (thread.GetStackFrameAtIndex(0));
+ // if (frame_sp)
+ // {
+ // const Symbol *symbol =
+ // frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol;
+ // if (symbol)
+ // {
+ // if (symbol->GetName() ==
+ // ConstString("_dyld_start"))
+ // process_did_exec = true;
+ // }
+ // }
+ // }
+ }
+ }
+ return StopInfo::CreateStopReasonWithSignal(thread, exc_sub_code);
+ }
+ break;
+
+ case 6: // EXC_BREAKPOINT
+ {
+ bool is_actual_breakpoint = false;
+ bool is_trace_if_actual_breakpoint_missing = false;
+ switch (cpu) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ if (exc_code == 1) // EXC_I386_SGL
+ {
+ if (!exc_sub_code) {
+ // This looks like a plain trap.
+ // Have to check if there is a breakpoint here as well. When you
+ // single-step onto a trap, the single step stops you not to trap.
+ // Since we also do that check below, let's just use that logic.
+ is_actual_breakpoint = true;
+ is_trace_if_actual_breakpoint_missing = true;
+ } else {
+
+ // It's a watchpoint, then.
+ // The exc_sub_code indicates the data break address.
+ lldb::WatchpointSP wp_sp;
+ if (target)
+ wp_sp = target->GetWatchpointList().FindByAddress(
+ (lldb::addr_t)exc_sub_code);
+ if (wp_sp && wp_sp->IsEnabled()) {
+ // Debugserver may piggyback the hardware index of the fired
+ // watchpoint in the exception data. Set the hardware index if
+ // that's the case.
+ if (exc_data_count >= 3)
+ wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
+ return StopInfo::CreateStopReasonWithWatchpointID(thread,
+ wp_sp->GetID());
+ }
+ }
+ } else if (exc_code == 2 || // EXC_I386_BPT
+ exc_code == 3) // EXC_I386_BPTFLT
+ {
+ // KDP returns EXC_I386_BPTFLT for trace breakpoints
+ if (exc_code == 3)
+ is_trace_if_actual_breakpoint_missing = true;
+
+ is_actual_breakpoint = true;
+ if (!pc_already_adjusted)
+ pc_decrement = 1;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ is_actual_breakpoint = exc_code == 1; // EXC_PPC_BREAKPOINT
+ break;
+
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ if (exc_code == 0x102) // EXC_ARM_DA_DEBUG
+ {
+ // It's a watchpoint, then, if the exc_sub_code indicates a
+ // known/enabled data break address from our watchpoint list.
+ lldb::WatchpointSP wp_sp;
+ if (target)
+ wp_sp = target->GetWatchpointList().FindByAddress(
+ (lldb::addr_t)exc_sub_code);
+ if (wp_sp && wp_sp->IsEnabled()) {
+ // Debugserver may piggyback the hardware index of the fired
+ // watchpoint in the exception data. Set the hardware index if
+ // that's the case.
+ if (exc_data_count >= 3)
+ wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
+ return StopInfo::CreateStopReasonWithWatchpointID(thread,
+ wp_sp->GetID());
+ } else {
+ is_actual_breakpoint = true;
+ is_trace_if_actual_breakpoint_missing = true;
+ }
+ } else if (exc_code == 1) // EXC_ARM_BREAKPOINT
+ {
+ is_actual_breakpoint = true;
+ is_trace_if_actual_breakpoint_missing = true;
+ } else if (exc_code == 0) // FIXME not EXC_ARM_BREAKPOINT but a kernel
+ // is currently returning this so accept it
+ // as indicating a breakpoint until the
+ // kernel is fixed
+ {
+ is_actual_breakpoint = true;
+ is_trace_if_actual_breakpoint_missing = true;
+ }
+ break;
+
+ case llvm::Triple::aarch64: {
+ if (exc_code == 1 && exc_sub_code == 0) // EXC_ARM_BREAKPOINT
+ {
+ // This is hit when we single instruction step aka MDSCR_EL1 SS bit 0
+ // is set
+ is_actual_breakpoint = false;
+ is_trace_if_actual_breakpoint_missing = true;
+ }
+ if (exc_code == 0x102) // EXC_ARM_DA_DEBUG
+ {
+ // It's a watchpoint, then, if the exc_sub_code indicates a
+ // known/enabled data break address from our watchpoint list.
+ lldb::WatchpointSP wp_sp;
+ if (target)
+ wp_sp = target->GetWatchpointList().FindByAddress(
+ (lldb::addr_t)exc_sub_code);
+ if (wp_sp && wp_sp->IsEnabled()) {
+ // Debugserver may piggyback the hardware index of the fired
+ // watchpoint in the exception data. Set the hardware index if
+ // that's the case.
+ if (exc_data_count >= 3)
+ wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
+ return StopInfo::CreateStopReasonWithWatchpointID(thread,
+ wp_sp->GetID());
+ }
+ // EXC_ARM_DA_DEBUG seems to be reused for EXC_BREAKPOINT as well as
+ // EXC_BAD_ACCESS
+ if (thread.GetTemporaryResumeState() == eStateStepping)
+ return StopInfo::CreateStopReasonToTrace(thread);
+ }
+ // It looks like exc_sub_code has the 4 bytes of the instruction that
+ // triggered the exception, i.e. our breakpoint opcode
+ is_actual_breakpoint = exc_code == 1;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (is_actual_breakpoint) {
+ RegisterContextSP reg_ctx_sp(thread.GetRegisterContext());
+ addr_t pc = reg_ctx_sp->GetPC() - pc_decrement;
+
+ ProcessSP process_sp(thread.CalculateProcess());
+
+ lldb::BreakpointSiteSP bp_site_sp;
+ if (process_sp)
+ bp_site_sp = process_sp->GetBreakpointSiteList().FindByAddress(pc);
+ if (bp_site_sp && bp_site_sp->IsEnabled()) {
+ // Update the PC if we were asked to do so, but only do so if we find
+ // a breakpoint that we know about cause this could be a trap
+ // instruction in the code
+ if (pc_decrement > 0 && adjust_pc_if_needed)
+ reg_ctx_sp->SetPC(pc);
+
+ // If the breakpoint is for this thread, then we'll report the hit,
+ // but if it is for another thread, we can just report no reason. We
+ // don't need to worry about stepping over the breakpoint here, that
+ // will be taken care of when the thread resumes and notices that
+ // there's a breakpoint under the pc. If we have an operating system
+ // plug-in, we might have set a thread specific breakpoint using the
+ // operating system thread ID, so we can't make any assumptions about
+ // the thread ID so we must always report the breakpoint regardless
+ // of the thread.
+ if (bp_site_sp->ValidForThisThread(&thread) ||
+ thread.GetProcess()->GetOperatingSystem() != nullptr)
+ return StopInfo::CreateStopReasonWithBreakpointSiteID(
+ thread, bp_site_sp->GetID());
+ else if (is_trace_if_actual_breakpoint_missing)
+ return StopInfo::CreateStopReasonToTrace(thread);
+ else
+ return StopInfoSP();
+ }
+
+ // Don't call this a trace if we weren't single stepping this thread.
+ if (is_trace_if_actual_breakpoint_missing &&
+ thread.GetTemporaryResumeState() == eStateStepping) {
+ return StopInfo::CreateStopReasonToTrace(thread);
+ }
+ }
+ } break;
+
+ case 7: // EXC_SYSCALL
+ case 8: // EXC_MACH_SYSCALL
+ case 9: // EXC_RPC_ALERT
+ case 10: // EXC_CRASH
+ break;
+ }
+
+ return StopInfoSP(new StopInfoMachException(
+ thread, exc_type, exc_data_count, exc_code, exc_sub_code));
+ }
+ return StopInfoSP();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.h
new file mode 100644
index 000000000000..74c05812ab00
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.h
@@ -0,0 +1,51 @@
+//===-- StopInfoMachException.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_StopInfoMachException_h_
+#define liblldb_StopInfoMachException_h_
+
+#include <string>
+
+#include "lldb/Target/StopInfo.h"
+
+namespace lldb_private {
+
+class StopInfoMachException : public StopInfo {
+public:
+ // Constructors and Destructors
+ StopInfoMachException(Thread &thread, uint32_t exc_type,
+ uint32_t exc_data_count, uint64_t exc_code,
+ uint64_t exc_subcode)
+ : StopInfo(thread, exc_type), m_exc_data_count(exc_data_count),
+ m_exc_code(exc_code), m_exc_subcode(exc_subcode) {}
+
+ ~StopInfoMachException() override = default;
+
+ lldb::StopReason GetStopReason() const override {
+ return lldb::eStopReasonException;
+ }
+
+ const char *GetDescription() override;
+
+ // Since some mach exceptions will be reported as breakpoints, signals,
+ // or trace, we use this static accessor which will translate the mach
+ // exception into the correct StopInfo.
+ static lldb::StopInfoSP CreateStopReasonWithMachException(
+ Thread &thread, uint32_t exc_type, uint32_t exc_data_count,
+ uint64_t exc_code, uint64_t exc_sub_code, uint64_t exc_sub_sub_code,
+ bool pc_already_adjusted = true, bool adjust_pc_if_needed = false);
+
+protected:
+ uint32_t m_exc_data_count;
+ uint64_t m_exc_code;
+ uint64_t m_exc_subcode;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_StopInfoMachException_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp
new file mode 100644
index 000000000000..80b04bb14f77
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp
@@ -0,0 +1,103 @@
+//===-- ThreadMemory.cpp ----------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/Process/Utility/ThreadMemory.h"
+
+#include "Plugins/Process/Utility/RegisterContextThreadMemory.h"
+#include "lldb/Target/OperatingSystem.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Unwind.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+ThreadMemory::ThreadMemory(Process &process, tid_t tid,
+ const ValueObjectSP &thread_info_valobj_sp)
+ : Thread(process, tid), m_backing_thread_sp(),
+ m_thread_info_valobj_sp(thread_info_valobj_sp), m_name(), m_queue() {}
+
+ThreadMemory::ThreadMemory(Process &process, lldb::tid_t tid,
+ llvm::StringRef name, llvm::StringRef queue,
+ lldb::addr_t register_data_addr)
+ : Thread(process, tid), m_backing_thread_sp(), m_thread_info_valobj_sp(),
+ m_name(name), m_queue(queue), m_register_data_addr(register_data_addr) {}
+
+ThreadMemory::~ThreadMemory() { DestroyThread(); }
+
+void ThreadMemory::WillResume(StateType resume_state) {
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->WillResume(resume_state);
+}
+
+void ThreadMemory::ClearStackFrames() {
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->ClearStackFrames();
+ Thread::ClearStackFrames();
+}
+
+RegisterContextSP ThreadMemory::GetRegisterContext() {
+ if (!m_reg_context_sp)
+ m_reg_context_sp = std::make_shared<RegisterContextThreadMemory>(
+ *this, m_register_data_addr);
+ return m_reg_context_sp;
+}
+
+RegisterContextSP
+ThreadMemory::CreateRegisterContextForFrame(StackFrame *frame) {
+ RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+ if (concrete_frame_idx == 0) {
+ reg_ctx_sp = GetRegisterContext();
+ } else {
+ Unwind *unwinder = GetUnwinder();
+ if (unwinder != nullptr)
+ reg_ctx_sp = unwinder->CreateRegisterContextForFrame(frame);
+ }
+ return reg_ctx_sp;
+}
+
+bool ThreadMemory::CalculateStopInfo() {
+ if (m_backing_thread_sp) {
+ lldb::StopInfoSP backing_stop_info_sp(
+ m_backing_thread_sp->GetPrivateStopInfo());
+ if (backing_stop_info_sp &&
+ backing_stop_info_sp->IsValidForOperatingSystemThread(*this)) {
+ backing_stop_info_sp->SetThread(shared_from_this());
+ SetStopInfo(backing_stop_info_sp);
+ return true;
+ }
+ } else {
+ ProcessSP process_sp(GetProcess());
+
+ if (process_sp) {
+ OperatingSystem *os = process_sp->GetOperatingSystem();
+ if (os) {
+ SetStopInfo(os->CreateThreadStopReason(this));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void ThreadMemory::RefreshStateAfterStop() {
+ if (m_backing_thread_sp)
+ return m_backing_thread_sp->RefreshStateAfterStop();
+
+ if (m_reg_context_sp)
+ m_reg_context_sp->InvalidateAllRegisters();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.h
new file mode 100644
index 000000000000..85bc1451e4a0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.h
@@ -0,0 +1,106 @@
+//===-- ThreadMemory.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadMemory_h_
+#define liblldb_ThreadMemory_h_
+
+#include <string>
+
+#include "lldb/Target/Thread.h"
+
+class ThreadMemory : public lldb_private::Thread {
+public:
+ ThreadMemory(lldb_private::Process &process, lldb::tid_t tid,
+ const lldb::ValueObjectSP &thread_info_valobj_sp);
+
+ ThreadMemory(lldb_private::Process &process, lldb::tid_t tid,
+ llvm::StringRef name, llvm::StringRef queue,
+ lldb::addr_t register_data_addr);
+
+ ~ThreadMemory() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+
+ bool CalculateStopInfo() override;
+
+ const char *GetInfo() override {
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->GetInfo();
+ return nullptr;
+ }
+
+ const char *GetName() override {
+ if (!m_name.empty())
+ return m_name.c_str();
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->GetName();
+ return nullptr;
+ }
+
+ const char *GetQueueName() override {
+ if (!m_queue.empty())
+ return m_queue.c_str();
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->GetQueueName();
+ return nullptr;
+ }
+
+ void WillResume(lldb::StateType resume_state) override;
+
+ void DidResume() override {
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->DidResume();
+ }
+
+ lldb::user_id_t GetProtocolID() const override {
+ if (m_backing_thread_sp)
+ return m_backing_thread_sp->GetProtocolID();
+ return Thread::GetProtocolID();
+ }
+
+ void RefreshStateAfterStop() override;
+
+ lldb::ValueObjectSP &GetValueObject() { return m_thread_info_valobj_sp; }
+
+ void ClearStackFrames() override;
+
+ void ClearBackingThread() override { m_backing_thread_sp.reset(); }
+
+ bool SetBackingThread(const lldb::ThreadSP &thread_sp) override {
+ // printf ("Thread 0x%llx is being backed by thread 0x%llx\n", GetID(),
+ // thread_sp->GetID());
+ m_backing_thread_sp = thread_sp;
+ return (bool)thread_sp;
+ }
+
+ lldb::ThreadSP GetBackingThread() const override {
+ return m_backing_thread_sp;
+ }
+
+protected:
+ bool IsOperatingSystemPluginThread() const override { return true; }
+
+ // If this memory thread is actually represented by a thread from the
+ // lldb_private::Process subclass, then fill in the thread here and
+ // all APIs will be routed through this thread object. If m_backing_thread_sp
+ // is empty, then this thread is simply in memory with no representation
+ // through the process plug-in.
+ lldb::ThreadSP m_backing_thread_sp;
+ lldb::ValueObjectSP m_thread_info_valobj_sp;
+ std::string m_name;
+ std::string m_queue;
+ lldb::addr_t m_register_data_addr;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ThreadMemory);
+};
+
+#endif // liblldb_ThreadMemory_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp
new file mode 100644
index 000000000000..38209fb24948
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp
@@ -0,0 +1,505 @@
+//===-- UnwindLLDB.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Log.h"
+
+#include "RegisterContextLLDB.h"
+#include "UnwindLLDB.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindLLDB::UnwindLLDB(Thread &thread)
+ : Unwind(thread), m_frames(), m_unwind_complete(false),
+ m_user_supplied_trap_handler_functions() {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ Args args;
+ process_sp->GetTarget().GetUserSpecifiedTrapHandlerNames(args);
+ size_t count = args.GetArgumentCount();
+ for (size_t i = 0; i < count; i++) {
+ const char *func_name = args.GetArgumentAtIndex(i);
+ m_user_supplied_trap_handler_functions.push_back(ConstString(func_name));
+ }
+ }
+}
+
+uint32_t UnwindLLDB::DoGetFrameCount() {
+ if (!m_unwind_complete) {
+//#define DEBUG_FRAME_SPEED 1
+#if DEBUG_FRAME_SPEED
+#define FRAME_COUNT 10000
+ using namespace std::chrono;
+ auto time_value = steady_clock::now();
+#endif
+ if (!AddFirstFrame())
+ return 0;
+
+ ProcessSP process_sp(m_thread.GetProcess());
+ ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr;
+
+ while (AddOneMoreFrame(abi)) {
+#if DEBUG_FRAME_SPEED
+ if ((m_frames.size() % FRAME_COUNT) == 0) {
+ const auto now = steady_clock::now();
+ const auto delta_t = now - time_value;
+ printf("%u frames in %.9f ms (%g frames/sec)\n", FRAME_COUNT,
+ duration<double, std::milli>(delta_t).count(),
+ (float)FRAME_COUNT / duration<double>(delta_t).count());
+ time_value = now;
+ }
+#endif
+ }
+ }
+ return m_frames.size();
+}
+
+bool UnwindLLDB::AddFirstFrame() {
+ if (m_frames.size() > 0)
+ return true;
+
+ ProcessSP process_sp(m_thread.GetProcess());
+ ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr;
+
+ // First, set up the 0th (initial) frame
+ CursorSP first_cursor_sp(new Cursor());
+ RegisterContextLLDBSP reg_ctx_sp(new RegisterContextLLDB(
+ m_thread, RegisterContextLLDBSP(), first_cursor_sp->sctx, 0, *this));
+ if (reg_ctx_sp.get() == nullptr)
+ goto unwind_done;
+
+ if (!reg_ctx_sp->IsValid())
+ goto unwind_done;
+
+ if (!reg_ctx_sp->GetCFA(first_cursor_sp->cfa))
+ goto unwind_done;
+
+ if (!reg_ctx_sp->ReadPC(first_cursor_sp->start_pc))
+ goto unwind_done;
+
+ // Everything checks out, so release the auto pointer value and let the
+ // cursor own it in its shared pointer
+ first_cursor_sp->reg_ctx_lldb_sp = reg_ctx_sp;
+ m_frames.push_back(first_cursor_sp);
+
+ // Update the Full Unwind Plan for this frame if not valid
+ UpdateUnwindPlanForFirstFrameIfInvalid(abi);
+
+ return true;
+
+unwind_done:
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log) {
+ log->Printf("th%d Unwind of this thread is complete.",
+ m_thread.GetIndexID());
+ }
+ m_unwind_complete = true;
+ return false;
+}
+
+UnwindLLDB::CursorSP UnwindLLDB::GetOneMoreFrame(ABI *abi) {
+ assert(m_frames.size() != 0 &&
+ "Get one more frame called with empty frame list");
+
+ // If we've already gotten to the end of the stack, don't bother to try
+ // again...
+ if (m_unwind_complete)
+ return nullptr;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ CursorSP prev_frame = m_frames.back();
+ uint32_t cur_idx = m_frames.size();
+
+ CursorSP cursor_sp(new Cursor());
+ RegisterContextLLDBSP reg_ctx_sp(new RegisterContextLLDB(
+ m_thread, prev_frame->reg_ctx_lldb_sp, cursor_sp->sctx, cur_idx, *this));
+
+ uint64_t max_stack_depth = m_thread.GetMaxBacktraceDepth();
+
+ // We want to detect an unwind that cycles erroneously and stop backtracing.
+ // Don't want this maximum unwind limit to be too low -- if you have a
+ // backtrace with an "infinitely recursing" bug, it will crash when the stack
+ // blows out and the first 35,000 frames are uninteresting - it's the top
+ // most 5 frames that you actually care about. So you can't just cap the
+ // unwind at 10,000 or something. Realistically anything over around 200,000
+ // is going to blow out the stack space. If we're still unwinding at that
+ // point, we're probably never going to finish.
+ if (cur_idx >= max_stack_depth) {
+ if (log)
+ log->Printf("%*sFrame %d unwound too many frames, assuming unwind has "
+ "gone astray, stopping.",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+
+ if (reg_ctx_sp.get() == nullptr) {
+ // If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to
+ // that and return true. Subsequent calls to TryFallbackUnwindPlan() will
+ // return false.
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of prev_frame
+ // still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf("%*sFrame %d did not get a RegisterContext, stopping.",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+
+ if (!reg_ctx_sp->IsValid()) {
+ // We failed to get a valid RegisterContext. See if the regctx below this
+ // on the stack has a fallback unwind plan it can use. Subsequent calls to
+ // TryFallbackUnwindPlan() will return false.
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of prev_frame
+ // still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf("%*sFrame %d invalid RegisterContext for this frame, "
+ "stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+ if (!reg_ctx_sp->GetCFA(cursor_sp->cfa)) {
+ // If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to
+ // that and return true. Subsequent calls to TryFallbackUnwindPlan() will
+ // return false.
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of prev_frame
+ // still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf(
+ "%*sFrame %d did not get CFA for this frame, stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+ if (abi && !abi->CallFrameAddressIsValid(cursor_sp->cfa)) {
+ // On Mac OS X, the _sigtramp asynchronous signal trampoline frame may not
+ // have its (constructed) CFA aligned correctly -- don't do the abi
+ // alignment check for these.
+ if (!reg_ctx_sp->IsTrapHandlerFrame()) {
+ // See if we can find a fallback unwind plan for THIS frame. It may be
+ // that the UnwindPlan we're using for THIS frame was bad and gave us a
+ // bad CFA. If that's not it, then see if we can change the UnwindPlan
+ // for the frame below us ("NEXT") -- see if using that other UnwindPlan
+ // gets us a better unwind state.
+ if (!reg_ctx_sp->TryFallbackUnwindPlan() ||
+ !reg_ctx_sp->GetCFA(cursor_sp->cfa) ||
+ !abi->CallFrameAddressIsValid(cursor_sp->cfa)) {
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of
+ // prev_frame still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf("%*sFrame %d did not get a valid CFA for this frame, "
+ "stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ } else {
+ if (log)
+ log->Printf("%*sFrame %d had a bad CFA value but we switched the "
+ "UnwindPlan being used and got one that looks more "
+ "realistic.",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ }
+ }
+ }
+ if (!reg_ctx_sp->ReadPC(cursor_sp->start_pc)) {
+ // If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to
+ // that and return true. Subsequent calls to TryFallbackUnwindPlan() will
+ // return false.
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of prev_frame
+ // still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf(
+ "%*sFrame %d did not get PC for this frame, stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+ if (abi && !abi->CodeAddressIsValid(cursor_sp->start_pc)) {
+ // If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to
+ // that and return true. Subsequent calls to TryFallbackUnwindPlan() will
+ // return false.
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of prev_frame
+ // still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf("%*sFrame %d did not get a valid PC, stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+ // Infinite loop where the current cursor is the same as the previous one...
+ if (prev_frame->start_pc == cursor_sp->start_pc &&
+ prev_frame->cfa == cursor_sp->cfa) {
+ if (log)
+ log->Printf("th%d pc of this frame is the same as the previous frame and "
+ "CFAs for both frames are identical -- stopping unwind",
+ m_thread.GetIndexID());
+ return nullptr;
+ }
+
+ cursor_sp->reg_ctx_lldb_sp = reg_ctx_sp;
+ return cursor_sp;
+}
+
+void UnwindLLDB::UpdateUnwindPlanForFirstFrameIfInvalid(ABI *abi) {
+ // This function is called for First Frame only.
+ assert(m_frames.size() == 1 && "No. of cursor frames are not 1");
+
+ bool old_m_unwind_complete = m_unwind_complete;
+ CursorSP old_m_candidate_frame = m_candidate_frame;
+
+ // Try to unwind 2 more frames using the Unwinder. It uses Full UnwindPlan
+ // and if Full UnwindPlan fails, then uses FallBack UnwindPlan. Also update
+ // the cfa of Frame 0 (if required).
+ AddOneMoreFrame(abi);
+
+ // Remove all the frames added by above function as the purpose of using
+ // above function was just to check whether Unwinder of Frame 0 works or not.
+ for (uint32_t i = 1; i < m_frames.size(); i++)
+ m_frames.pop_back();
+
+ // Restore status after calling AddOneMoreFrame
+ m_unwind_complete = old_m_unwind_complete;
+ m_candidate_frame = old_m_candidate_frame;
+ return;
+}
+
+bool UnwindLLDB::AddOneMoreFrame(ABI *abi) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ // Frame zero is a little different
+ if (m_frames.empty())
+ return false;
+
+ // If we've already gotten to the end of the stack, don't bother to try
+ // again...
+ if (m_unwind_complete)
+ return false;
+
+ CursorSP new_frame = m_candidate_frame;
+ if (new_frame == nullptr)
+ new_frame = GetOneMoreFrame(abi);
+
+ if (new_frame == nullptr) {
+ if (log)
+ log->Printf("th%d Unwind of this thread is complete.",
+ m_thread.GetIndexID());
+ m_unwind_complete = true;
+ return false;
+ }
+
+ m_frames.push_back(new_frame);
+
+ // If we can get one more frame further then accept that we get back a
+ // correct frame.
+ m_candidate_frame = GetOneMoreFrame(abi);
+ if (m_candidate_frame)
+ return true;
+
+ // We can't go further from the frame returned by GetOneMore frame. Lets try
+ // to get a different frame with using the fallback unwind plan.
+ if (!m_frames[m_frames.size() - 2]
+ ->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // We don't have a valid fallback unwind plan. Accept the frame as it is.
+ // This is a valid situation when we are at the bottom of the stack.
+ return true;
+ }
+
+ // Remove the possibly incorrect frame from the frame list and try to add a
+ // different one with the newly selected fallback unwind plan.
+ m_frames.pop_back();
+ CursorSP new_frame_v2 = GetOneMoreFrame(abi);
+ if (new_frame_v2 == nullptr) {
+ // We haven't got a new frame from the fallback unwind plan. Accept the
+ // frame from the original unwind plan. This is a valid situation when we
+ // are at the bottom of the stack.
+ m_frames.push_back(new_frame);
+ return true;
+ }
+
+ // Push the new frame to the list and try to continue from this frame. If we
+ // can get a new frame then accept it as the correct one.
+ m_frames.push_back(new_frame_v2);
+ m_candidate_frame = GetOneMoreFrame(abi);
+ if (m_candidate_frame) {
+ // If control reached here then TryFallbackUnwindPlan had succeeded for
+ // Cursor::m_frames[m_frames.size() - 2]. It also succeeded to Unwind next
+ // 2 frames i.e. m_frames[m_frames.size() - 1] and a frame after that. For
+ // Cursor::m_frames[m_frames.size() - 2], reg_ctx_lldb_sp field was already
+ // updated during TryFallbackUnwindPlan call above. However, cfa field
+ // still needs to be updated. Hence updating it here and then returning.
+ return m_frames[m_frames.size() - 2]->reg_ctx_lldb_sp->GetCFA(
+ m_frames[m_frames.size() - 2]->cfa);
+ }
+
+ // The new frame hasn't helped in unwinding. Fall back to the original one as
+ // the default unwind plan is usually more reliable then the fallback one.
+ m_frames.pop_back();
+ m_frames.push_back(new_frame);
+ return true;
+}
+
+bool UnwindLLDB::DoGetFrameInfoAtIndex(uint32_t idx, addr_t &cfa, addr_t &pc) {
+ if (m_frames.size() == 0) {
+ if (!AddFirstFrame())
+ return false;
+ }
+
+ ProcessSP process_sp(m_thread.GetProcess());
+ ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr;
+
+ while (idx >= m_frames.size() && AddOneMoreFrame(abi))
+ ;
+
+ if (idx < m_frames.size()) {
+ cfa = m_frames[idx]->cfa;
+ pc = m_frames[idx]->start_pc;
+ return true;
+ }
+ return false;
+}
+
+lldb::RegisterContextSP
+UnwindLLDB::DoCreateRegisterContextForFrame(StackFrame *frame) {
+ lldb::RegisterContextSP reg_ctx_sp;
+ uint32_t idx = frame->GetConcreteFrameIndex();
+
+ if (idx == 0) {
+ return m_thread.GetRegisterContext();
+ }
+
+ if (m_frames.size() == 0) {
+ if (!AddFirstFrame())
+ return reg_ctx_sp;
+ }
+
+ ProcessSP process_sp(m_thread.GetProcess());
+ ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr;
+
+ while (idx >= m_frames.size()) {
+ if (!AddOneMoreFrame(abi))
+ break;
+ }
+
+ const uint32_t num_frames = m_frames.size();
+ if (idx < num_frames) {
+ Cursor *frame_cursor = m_frames[idx].get();
+ reg_ctx_sp = frame_cursor->reg_ctx_lldb_sp;
+ }
+ return reg_ctx_sp;
+}
+
+UnwindLLDB::RegisterContextLLDBSP
+UnwindLLDB::GetRegisterContextForFrameNum(uint32_t frame_num) {
+ RegisterContextLLDBSP reg_ctx_sp;
+ if (frame_num < m_frames.size())
+ reg_ctx_sp = m_frames[frame_num]->reg_ctx_lldb_sp;
+ return reg_ctx_sp;
+}
+
+bool UnwindLLDB::SearchForSavedLocationForRegister(
+ uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc,
+ uint32_t starting_frame_num, bool pc_reg) {
+ int64_t frame_num = starting_frame_num;
+ if (static_cast<size_t>(frame_num) >= m_frames.size())
+ return false;
+
+ // Never interrogate more than one level while looking for the saved pc
+ // value. If the value isn't saved by frame_num, none of the frames lower on
+ // the stack will have a useful value.
+ if (pc_reg) {
+ UnwindLLDB::RegisterSearchResult result;
+ result = m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister(
+ lldb_regnum, regloc);
+ return result == UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ while (frame_num >= 0) {
+ UnwindLLDB::RegisterSearchResult result;
+ result = m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister(
+ lldb_regnum, regloc);
+
+ // We descended down to the live register context aka stack frame 0 and are
+ // reading the value out of a live register.
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound &&
+ regloc.type ==
+ UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext) {
+ return true;
+ }
+
+ // If we have unwind instructions saying that register N is saved in
+ // register M in the middle of the stack (and N can equal M here, meaning
+ // the register was not used in this function), then change the register
+ // number we're looking for to M and keep looking for a concrete location
+ // down the stack, or an actual value from a live RegisterContext at frame
+ // 0.
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound &&
+ regloc.type == UnwindLLDB::RegisterLocation::eRegisterInRegister &&
+ frame_num > 0) {
+ result = UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ lldb_regnum = regloc.location.register_number;
+ }
+
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound)
+ return true;
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile)
+ return false;
+ frame_num--;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.h
new file mode 100644
index 000000000000..c512929c185b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.h
@@ -0,0 +1,157 @@
+//===-- UnwindLLDB.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_UnwindLLDB_h_
+#define lldb_UnwindLLDB_h_
+
+#include <vector>
+
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-public.h"
+
+namespace lldb_private {
+
+class RegisterContextLLDB;
+
+class UnwindLLDB : public lldb_private::Unwind {
+public:
+ UnwindLLDB(lldb_private::Thread &thread);
+
+ ~UnwindLLDB() override = default;
+
+ enum RegisterSearchResult {
+ eRegisterFound = 0,
+ eRegisterNotFound,
+ eRegisterIsVolatile
+ };
+
+protected:
+ friend class lldb_private::RegisterContextLLDB;
+
+ struct RegisterLocation {
+ enum RegisterLocationTypes {
+ eRegisterNotSaved = 0, // register was not preserved by callee. If
+ // volatile reg, is unavailable
+ eRegisterSavedAtMemoryLocation, // register is saved at a specific word of
+ // target mem (target_memory_location)
+ eRegisterInRegister, // register is available in a (possible other)
+ // register (register_number)
+ eRegisterSavedAtHostMemoryLocation, // register is saved at a word in
+ // lldb's address space
+ eRegisterValueInferred, // register val was computed (and is in
+ // inferred_value)
+ eRegisterInLiveRegisterContext // register value is in a live (stack frame
+ // #0) register
+ };
+ int type;
+ union {
+ lldb::addr_t target_memory_location;
+ uint32_t
+ register_number; // in eRegisterKindLLDB register numbering system
+ void *host_memory_location;
+ uint64_t inferred_value; // eRegisterValueInferred - e.g. stack pointer ==
+ // cfa + offset
+ } location;
+ };
+
+ void DoClear() override {
+ m_frames.clear();
+ m_candidate_frame.reset();
+ m_unwind_complete = false;
+ }
+
+ uint32_t DoGetFrameCount() override;
+
+ bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
+ lldb::addr_t &start_pc) override;
+
+ lldb::RegisterContextSP
+ DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+
+ typedef std::shared_ptr<RegisterContextLLDB> RegisterContextLLDBSP;
+
+ // Needed to retrieve the "next" frame (e.g. frame 2 needs to retrieve frame
+ // 1's RegisterContextLLDB)
+ // The RegisterContext for frame_num must already exist or this returns an
+ // empty shared pointer.
+ RegisterContextLLDBSP GetRegisterContextForFrameNum(uint32_t frame_num);
+
+ // Iterate over the RegisterContextLLDB's in our m_frames vector, look for the
+ // first one that
+ // has a saved location for this reg.
+ bool SearchForSavedLocationForRegister(
+ uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc,
+ uint32_t starting_frame_num, bool pc_register);
+
+ /// Provide the list of user-specified trap handler functions
+ ///
+ /// The Platform is one source of trap handler function names; that
+ /// may be augmented via a setting. The setting needs to be converted
+ /// into an array of ConstStrings before it can be used - we only want
+ /// to do that once per thread so it's here in the UnwindLLDB object.
+ ///
+ /// \return
+ /// Vector of ConstStrings of trap handler function names. May be
+ /// empty.
+ const std::vector<ConstString> &GetUserSpecifiedTrapHandlerFunctionNames() {
+ return m_user_supplied_trap_handler_functions;
+ }
+
+private:
+ struct Cursor {
+ lldb::addr_t start_pc; // The start address of the function/symbol for this
+ // frame - current pc if unknown
+ lldb::addr_t cfa; // The canonical frame address for this stack frame
+ lldb_private::SymbolContext sctx; // A symbol context we'll contribute to &
+ // provide to the StackFrame creation
+ RegisterContextLLDBSP
+ reg_ctx_lldb_sp; // These are all RegisterContextLLDB's
+
+ Cursor()
+ : start_pc(LLDB_INVALID_ADDRESS), cfa(LLDB_INVALID_ADDRESS), sctx(),
+ reg_ctx_lldb_sp() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Cursor);
+ };
+
+ typedef std::shared_ptr<Cursor> CursorSP;
+ std::vector<CursorSP> m_frames;
+ CursorSP m_candidate_frame;
+ bool m_unwind_complete; // If this is true, we've enumerated all the frames in
+ // the stack, and m_frames.size() is the
+ // number of frames, etc. Otherwise we've only gone as far as directly asked,
+ // and m_frames.size()
+ // is how far we've currently gone.
+
+ std::vector<ConstString> m_user_supplied_trap_handler_functions;
+
+ // Check if Full UnwindPlan of First frame is valid or not.
+ // If not then try Fallback UnwindPlan of the frame. If Fallback
+ // UnwindPlan succeeds then update the Full UnwindPlan with the
+ // Fallback UnwindPlan.
+ void UpdateUnwindPlanForFirstFrameIfInvalid(ABI *abi);
+
+ CursorSP GetOneMoreFrame(ABI *abi);
+
+ bool AddOneMoreFrame(ABI *abi);
+
+ bool AddFirstFrame();
+
+ // For UnwindLLDB only
+ DISALLOW_COPY_AND_ASSIGN(UnwindLLDB);
+};
+
+} // namespace lldb_private
+
+#endif // lldb_UnwindLLDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp
new file mode 100644
index 000000000000..7dc5a5f5fdd1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp
@@ -0,0 +1,247 @@
+//===-- UnwindMacOSXFrameBackchain.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+
+#include "RegisterContextMacOSXFrameBackchain.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindMacOSXFrameBackchain::UnwindMacOSXFrameBackchain(Thread &thread)
+ : Unwind(thread), m_cursors() {}
+
+uint32_t UnwindMacOSXFrameBackchain::DoGetFrameCount() {
+ if (m_cursors.empty()) {
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ const ArchSpec &target_arch = target->GetArchitecture();
+ // Frame zero should always be supplied by the thread...
+ exe_ctx.SetFrameSP(m_thread.GetStackFrameAtIndex(0));
+
+ if (target_arch.GetAddressByteSize() == 8)
+ GetStackFrameData_x86_64(exe_ctx);
+ else
+ GetStackFrameData_i386(exe_ctx);
+ }
+ }
+ return m_cursors.size();
+}
+
+bool UnwindMacOSXFrameBackchain::DoGetFrameInfoAtIndex(uint32_t idx,
+ addr_t &cfa,
+ addr_t &pc) {
+ const uint32_t frame_count = GetFrameCount();
+ if (idx < frame_count) {
+ if (m_cursors[idx].pc == LLDB_INVALID_ADDRESS)
+ return false;
+ if (m_cursors[idx].fp == LLDB_INVALID_ADDRESS)
+ return false;
+
+ pc = m_cursors[idx].pc;
+ cfa = m_cursors[idx].fp;
+
+ return true;
+ }
+ return false;
+}
+
+lldb::RegisterContextSP
+UnwindMacOSXFrameBackchain::DoCreateRegisterContextForFrame(StackFrame *frame) {
+ lldb::RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_idx = frame->GetConcreteFrameIndex();
+ const uint32_t frame_count = GetFrameCount();
+ if (concrete_idx < frame_count)
+ reg_ctx_sp = std::make_shared<RegisterContextMacOSXFrameBackchain>(
+ m_thread, concrete_idx, m_cursors[concrete_idx]);
+ return reg_ctx_sp;
+}
+
+size_t UnwindMacOSXFrameBackchain::GetStackFrameData_i386(
+ const ExecutionContext &exe_ctx) {
+ m_cursors.clear();
+
+ StackFrame *first_frame = exe_ctx.GetFramePtr();
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process == nullptr)
+ return 0;
+
+ struct Frame_i386 {
+ uint32_t fp;
+ uint32_t pc;
+ };
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+ assert(reg_ctx);
+
+ Cursor cursor;
+ cursor.pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS);
+ cursor.fp = reg_ctx->GetFP(0);
+
+ Frame_i386 frame = {static_cast<uint32_t>(cursor.fp),
+ static_cast<uint32_t>(cursor.pc)};
+
+ m_cursors.push_back(cursor);
+
+ const size_t k_frame_size = sizeof(frame);
+ Status error;
+ while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) {
+ // Read both the FP and PC (8 bytes)
+ if (process->ReadMemory(frame.fp, &frame.fp, k_frame_size, error) !=
+ k_frame_size)
+ break;
+ if (frame.pc >= 0x1000) {
+ cursor.pc = frame.pc;
+ cursor.fp = frame.fp;
+ m_cursors.push_back(cursor);
+ }
+ }
+ if (!m_cursors.empty()) {
+ lldb::addr_t first_frame_pc = m_cursors.front().pc;
+ if (first_frame_pc != LLDB_INVALID_ADDRESS) {
+ const SymbolContextItem resolve_scope =
+ eSymbolContextModule | eSymbolContextCompUnit |
+ eSymbolContextFunction | eSymbolContextSymbol;
+
+ SymbolContext first_frame_sc(
+ first_frame->GetSymbolContext(resolve_scope));
+ const AddressRange *addr_range_ptr = nullptr;
+ AddressRange range;
+ if (first_frame_sc.function)
+ addr_range_ptr = &first_frame_sc.function->GetAddressRange();
+ else if (first_frame_sc.symbol) {
+ range.GetBaseAddress() = first_frame_sc.symbol->GetAddress();
+ range.SetByteSize(first_frame_sc.symbol->GetByteSize());
+ addr_range_ptr = &range;
+ }
+
+ if (addr_range_ptr) {
+ if (first_frame->GetFrameCodeAddress() ==
+ addr_range_ptr->GetBaseAddress()) {
+ // We are at the first instruction, so we can recover the previous PC
+ // by dereferencing the SP
+ lldb::addr_t first_frame_sp = reg_ctx->GetSP(0);
+ // Read the real second frame return address into frame.pc
+ if (first_frame_sp &&
+ process->ReadMemory(first_frame_sp, &frame.pc, sizeof(frame.pc),
+ error) == sizeof(frame.pc)) {
+ cursor.fp = m_cursors.front().fp;
+ cursor.pc = frame.pc; // Set the new second frame PC
+
+ // Insert the second frame
+ m_cursors.insert(m_cursors.begin() + 1, cursor);
+
+ m_cursors.front().fp = first_frame_sp;
+ }
+ }
+ }
+ }
+ }
+ // uint32_t i=0;
+ // printf(" PC FP\n");
+ // printf(" ------------------ ------------------ \n");
+ // for (i=0; i<m_cursors.size(); ++i)
+ // {
+ // printf("[%3u] 0x%16.16" PRIx64 " 0x%16.16" PRIx64 "\n", i,
+ // m_cursors[i].pc, m_cursors[i].fp);
+ // }
+ return m_cursors.size();
+}
+
+size_t UnwindMacOSXFrameBackchain::GetStackFrameData_x86_64(
+ const ExecutionContext &exe_ctx) {
+ m_cursors.clear();
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process == nullptr)
+ return 0;
+
+ StackFrame *first_frame = exe_ctx.GetFramePtr();
+
+ struct Frame_x86_64 {
+ uint64_t fp;
+ uint64_t pc;
+ };
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+ assert(reg_ctx);
+
+ Cursor cursor;
+ cursor.pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS);
+ cursor.fp = reg_ctx->GetFP(0);
+
+ Frame_x86_64 frame = {cursor.fp, cursor.pc};
+
+ m_cursors.push_back(cursor);
+ Status error;
+ const size_t k_frame_size = sizeof(frame);
+ while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) {
+ // Read both the FP and PC (16 bytes)
+ if (process->ReadMemory(frame.fp, &frame.fp, k_frame_size, error) !=
+ k_frame_size)
+ break;
+
+ if (frame.pc >= 0x1000) {
+ cursor.pc = frame.pc;
+ cursor.fp = frame.fp;
+ m_cursors.push_back(cursor);
+ }
+ }
+ if (!m_cursors.empty()) {
+ lldb::addr_t first_frame_pc = m_cursors.front().pc;
+ if (first_frame_pc != LLDB_INVALID_ADDRESS) {
+ const SymbolContextItem resolve_scope =
+ eSymbolContextModule | eSymbolContextCompUnit |
+ eSymbolContextFunction | eSymbolContextSymbol;
+
+ SymbolContext first_frame_sc(
+ first_frame->GetSymbolContext(resolve_scope));
+ const AddressRange *addr_range_ptr = nullptr;
+ AddressRange range;
+ if (first_frame_sc.function)
+ addr_range_ptr = &first_frame_sc.function->GetAddressRange();
+ else if (first_frame_sc.symbol) {
+ range.GetBaseAddress() = first_frame_sc.symbol->GetAddress();
+ range.SetByteSize(first_frame_sc.symbol->GetByteSize());
+ addr_range_ptr = &range;
+ }
+
+ if (addr_range_ptr) {
+ if (first_frame->GetFrameCodeAddress() ==
+ addr_range_ptr->GetBaseAddress()) {
+ // We are at the first instruction, so we can recover the previous PC
+ // by dereferencing the SP
+ lldb::addr_t first_frame_sp = reg_ctx->GetSP(0);
+ // Read the real second frame return address into frame.pc
+ if (process->ReadMemory(first_frame_sp, &frame.pc, sizeof(frame.pc),
+ error) == sizeof(frame.pc)) {
+ cursor.fp = m_cursors.front().fp;
+ cursor.pc = frame.pc; // Set the new second frame PC
+
+ // Insert the second frame
+ m_cursors.insert(m_cursors.begin() + 1, cursor);
+
+ m_cursors.front().fp = first_frame_sp;
+ }
+ }
+ }
+ }
+ }
+ return m_cursors.size();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h
new file mode 100644
index 000000000000..2208bcc2f2e4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h
@@ -0,0 +1,53 @@
+//===-- UnwindMacOSXFrameBackchain.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_UnwindMacOSXFrameBackchain_h_
+#define lldb_UnwindMacOSXFrameBackchain_h_
+
+#include <vector>
+
+#include "lldb/Target/Unwind.h"
+#include "lldb/lldb-private.h"
+
+class UnwindMacOSXFrameBackchain : public lldb_private::Unwind {
+public:
+ UnwindMacOSXFrameBackchain(lldb_private::Thread &thread);
+
+ ~UnwindMacOSXFrameBackchain() override = default;
+
+protected:
+ void DoClear() override { m_cursors.clear(); }
+
+ uint32_t DoGetFrameCount() override;
+
+ bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
+ lldb::addr_t &pc) override;
+
+ lldb::RegisterContextSP
+ DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+
+ friend class RegisterContextMacOSXFrameBackchain;
+
+ struct Cursor {
+ lldb::addr_t pc; // Program counter
+ lldb::addr_t fp; // Frame pointer for us with backchain
+ };
+
+private:
+ std::vector<Cursor> m_cursors;
+
+ size_t GetStackFrameData_i386(const lldb_private::ExecutionContext &exe_ctx);
+
+ size_t
+ GetStackFrameData_x86_64(const lldb_private::ExecutionContext &exe_ctx);
+
+ // For UnwindMacOSXFrameBackchain only
+ DISALLOW_COPY_AND_ASSIGN(UnwindMacOSXFrameBackchain);
+};
+
+#endif // lldb_UnwindMacOSXFrameBackchain_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm-register-enums.h
new file mode 100644
index 000000000000..39cbf01ea9d2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm-register-enums.h
@@ -0,0 +1,199 @@
+//===-- lldb-arm-register-enums.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_arm_register_enums_h
+#define lldb_arm_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all ARM registers.
+enum {
+ k_first_gpr_arm = 0,
+ gpr_r0_arm = k_first_gpr_arm,
+ gpr_r1_arm,
+ gpr_r2_arm,
+ gpr_r3_arm,
+ gpr_r4_arm,
+ gpr_r5_arm,
+ gpr_r6_arm,
+ gpr_r7_arm,
+ gpr_r8_arm,
+ gpr_r9_arm,
+ gpr_r10_arm,
+ gpr_r11_arm,
+ gpr_r12_arm,
+ gpr_r13_arm,
+ gpr_sp_arm = gpr_r13_arm,
+ gpr_r14_arm,
+ gpr_lr_arm = gpr_r14_arm,
+ gpr_r15_arm,
+ gpr_pc_arm = gpr_r15_arm,
+ gpr_cpsr_arm,
+
+ k_last_gpr_arm = gpr_cpsr_arm,
+
+ k_first_fpr_arm,
+ fpu_s0_arm = k_first_fpr_arm,
+ fpu_s1_arm,
+ fpu_s2_arm,
+ fpu_s3_arm,
+ fpu_s4_arm,
+ fpu_s5_arm,
+ fpu_s6_arm,
+ fpu_s7_arm,
+ fpu_s8_arm,
+ fpu_s9_arm,
+ fpu_s10_arm,
+ fpu_s11_arm,
+ fpu_s12_arm,
+ fpu_s13_arm,
+ fpu_s14_arm,
+ fpu_s15_arm,
+ fpu_s16_arm,
+ fpu_s17_arm,
+ fpu_s18_arm,
+ fpu_s19_arm,
+ fpu_s20_arm,
+ fpu_s21_arm,
+ fpu_s22_arm,
+ fpu_s23_arm,
+ fpu_s24_arm,
+ fpu_s25_arm,
+ fpu_s26_arm,
+ fpu_s27_arm,
+ fpu_s28_arm,
+ fpu_s29_arm,
+ fpu_s30_arm,
+ fpu_s31_arm,
+ fpu_fpscr_arm,
+ fpu_d0_arm,
+ fpu_d1_arm,
+ fpu_d2_arm,
+ fpu_d3_arm,
+ fpu_d4_arm,
+ fpu_d5_arm,
+ fpu_d6_arm,
+ fpu_d7_arm,
+ fpu_d8_arm,
+ fpu_d9_arm,
+ fpu_d10_arm,
+ fpu_d11_arm,
+ fpu_d12_arm,
+ fpu_d13_arm,
+ fpu_d14_arm,
+ fpu_d15_arm,
+ fpu_d16_arm,
+ fpu_d17_arm,
+ fpu_d18_arm,
+ fpu_d19_arm,
+ fpu_d20_arm,
+ fpu_d21_arm,
+ fpu_d22_arm,
+ fpu_d23_arm,
+ fpu_d24_arm,
+ fpu_d25_arm,
+ fpu_d26_arm,
+ fpu_d27_arm,
+ fpu_d28_arm,
+ fpu_d29_arm,
+ fpu_d30_arm,
+ fpu_d31_arm,
+ fpu_q0_arm,
+ fpu_q1_arm,
+ fpu_q2_arm,
+ fpu_q3_arm,
+ fpu_q4_arm,
+ fpu_q5_arm,
+ fpu_q6_arm,
+ fpu_q7_arm,
+ fpu_q8_arm,
+ fpu_q9_arm,
+ fpu_q10_arm,
+ fpu_q11_arm,
+ fpu_q12_arm,
+ fpu_q13_arm,
+ fpu_q14_arm,
+ fpu_q15_arm,
+ k_last_fpr_arm = fpu_q15_arm,
+ exc_exception_arm,
+ exc_fsr_arm,
+ exc_far_arm,
+
+ dbg_bvr0_arm,
+ dbg_bvr1_arm,
+ dbg_bvr2_arm,
+ dbg_bvr3_arm,
+ dbg_bvr4_arm,
+ dbg_bvr5_arm,
+ dbg_bvr6_arm,
+ dbg_bvr7_arm,
+ dbg_bvr8_arm,
+ dbg_bvr9_arm,
+ dbg_bvr10_arm,
+ dbg_bvr11_arm,
+ dbg_bvr12_arm,
+ dbg_bvr13_arm,
+ dbg_bvr14_arm,
+ dbg_bvr15_arm,
+ dbg_bcr0_arm,
+ dbg_bcr1_arm,
+ dbg_bcr2_arm,
+ dbg_bcr3_arm,
+ dbg_bcr4_arm,
+ dbg_bcr5_arm,
+ dbg_bcr6_arm,
+ dbg_bcr7_arm,
+ dbg_bcr8_arm,
+ dbg_bcr9_arm,
+ dbg_bcr10_arm,
+ dbg_bcr11_arm,
+ dbg_bcr12_arm,
+ dbg_bcr13_arm,
+ dbg_bcr14_arm,
+ dbg_bcr15_arm,
+ dbg_wvr0_arm,
+ dbg_wvr1_arm,
+ dbg_wvr2_arm,
+ dbg_wvr3_arm,
+ dbg_wvr4_arm,
+ dbg_wvr5_arm,
+ dbg_wvr6_arm,
+ dbg_wvr7_arm,
+ dbg_wvr8_arm,
+ dbg_wvr9_arm,
+ dbg_wvr10_arm,
+ dbg_wvr11_arm,
+ dbg_wvr12_arm,
+ dbg_wvr13_arm,
+ dbg_wvr14_arm,
+ dbg_wvr15_arm,
+ dbg_wcr0_arm,
+ dbg_wcr1_arm,
+ dbg_wcr2_arm,
+ dbg_wcr3_arm,
+ dbg_wcr4_arm,
+ dbg_wcr5_arm,
+ dbg_wcr6_arm,
+ dbg_wcr7_arm,
+ dbg_wcr8_arm,
+ dbg_wcr9_arm,
+ dbg_wcr10_arm,
+ dbg_wcr11_arm,
+ dbg_wcr12_arm,
+ dbg_wcr13_arm,
+ dbg_wcr14_arm,
+ dbg_wcr15_arm,
+
+ k_num_registers_arm,
+ k_num_gpr_registers_arm = k_last_gpr_arm - k_first_gpr_arm + 1,
+ k_num_fpr_registers_arm = k_last_fpr_arm - k_first_fpr_arm + 1
+};
+}
+
+#endif // #ifndef lldb_arm64_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm64-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm64-register-enums.h
new file mode 100644
index 000000000000..cc414dcde3cf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm64-register-enums.h
@@ -0,0 +1,264 @@
+//===-- lldb-arm64-register-enums.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_arm64_register_enums_h
+#define lldb_arm64_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all ARM64 registers.
+enum {
+ k_first_gpr_arm64,
+ gpr_x0_arm64 = k_first_gpr_arm64,
+ gpr_x1_arm64,
+ gpr_x2_arm64,
+ gpr_x3_arm64,
+ gpr_x4_arm64,
+ gpr_x5_arm64,
+ gpr_x6_arm64,
+ gpr_x7_arm64,
+ gpr_x8_arm64,
+ gpr_x9_arm64,
+ gpr_x10_arm64,
+ gpr_x11_arm64,
+ gpr_x12_arm64,
+ gpr_x13_arm64,
+ gpr_x14_arm64,
+ gpr_x15_arm64,
+ gpr_x16_arm64,
+ gpr_x17_arm64,
+ gpr_x18_arm64,
+ gpr_x19_arm64,
+ gpr_x20_arm64,
+ gpr_x21_arm64,
+ gpr_x22_arm64,
+ gpr_x23_arm64,
+ gpr_x24_arm64,
+ gpr_x25_arm64,
+ gpr_x26_arm64,
+ gpr_x27_arm64,
+ gpr_x28_arm64,
+ gpr_fp_arm64,
+ gpr_lr_arm64,
+ gpr_sp_arm64,
+ gpr_pc_arm64,
+ gpr_cpsr_arm64,
+
+ gpr_w0_arm64,
+ gpr_w1_arm64,
+ gpr_w2_arm64,
+ gpr_w3_arm64,
+ gpr_w4_arm64,
+ gpr_w5_arm64,
+ gpr_w6_arm64,
+ gpr_w7_arm64,
+ gpr_w8_arm64,
+ gpr_w9_arm64,
+ gpr_w10_arm64,
+ gpr_w11_arm64,
+ gpr_w12_arm64,
+ gpr_w13_arm64,
+ gpr_w14_arm64,
+ gpr_w15_arm64,
+ gpr_w16_arm64,
+ gpr_w17_arm64,
+ gpr_w18_arm64,
+ gpr_w19_arm64,
+ gpr_w20_arm64,
+ gpr_w21_arm64,
+ gpr_w22_arm64,
+ gpr_w23_arm64,
+ gpr_w24_arm64,
+ gpr_w25_arm64,
+ gpr_w26_arm64,
+ gpr_w27_arm64,
+ gpr_w28_arm64,
+
+ k_last_gpr_arm64 = gpr_w28_arm64,
+
+ k_first_fpr_arm64,
+ fpu_v0_arm64 = k_first_fpr_arm64,
+ fpu_v1_arm64,
+ fpu_v2_arm64,
+ fpu_v3_arm64,
+ fpu_v4_arm64,
+ fpu_v5_arm64,
+ fpu_v6_arm64,
+ fpu_v7_arm64,
+ fpu_v8_arm64,
+ fpu_v9_arm64,
+ fpu_v10_arm64,
+ fpu_v11_arm64,
+ fpu_v12_arm64,
+ fpu_v13_arm64,
+ fpu_v14_arm64,
+ fpu_v15_arm64,
+ fpu_v16_arm64,
+ fpu_v17_arm64,
+ fpu_v18_arm64,
+ fpu_v19_arm64,
+ fpu_v20_arm64,
+ fpu_v21_arm64,
+ fpu_v22_arm64,
+ fpu_v23_arm64,
+ fpu_v24_arm64,
+ fpu_v25_arm64,
+ fpu_v26_arm64,
+ fpu_v27_arm64,
+ fpu_v28_arm64,
+ fpu_v29_arm64,
+ fpu_v30_arm64,
+ fpu_v31_arm64,
+
+ fpu_s0_arm64,
+ fpu_s1_arm64,
+ fpu_s2_arm64,
+ fpu_s3_arm64,
+ fpu_s4_arm64,
+ fpu_s5_arm64,
+ fpu_s6_arm64,
+ fpu_s7_arm64,
+ fpu_s8_arm64,
+ fpu_s9_arm64,
+ fpu_s10_arm64,
+ fpu_s11_arm64,
+ fpu_s12_arm64,
+ fpu_s13_arm64,
+ fpu_s14_arm64,
+ fpu_s15_arm64,
+ fpu_s16_arm64,
+ fpu_s17_arm64,
+ fpu_s18_arm64,
+ fpu_s19_arm64,
+ fpu_s20_arm64,
+ fpu_s21_arm64,
+ fpu_s22_arm64,
+ fpu_s23_arm64,
+ fpu_s24_arm64,
+ fpu_s25_arm64,
+ fpu_s26_arm64,
+ fpu_s27_arm64,
+ fpu_s28_arm64,
+ fpu_s29_arm64,
+ fpu_s30_arm64,
+ fpu_s31_arm64,
+
+ fpu_d0_arm64,
+ fpu_d1_arm64,
+ fpu_d2_arm64,
+ fpu_d3_arm64,
+ fpu_d4_arm64,
+ fpu_d5_arm64,
+ fpu_d6_arm64,
+ fpu_d7_arm64,
+ fpu_d8_arm64,
+ fpu_d9_arm64,
+ fpu_d10_arm64,
+ fpu_d11_arm64,
+ fpu_d12_arm64,
+ fpu_d13_arm64,
+ fpu_d14_arm64,
+ fpu_d15_arm64,
+ fpu_d16_arm64,
+ fpu_d17_arm64,
+ fpu_d18_arm64,
+ fpu_d19_arm64,
+ fpu_d20_arm64,
+ fpu_d21_arm64,
+ fpu_d22_arm64,
+ fpu_d23_arm64,
+ fpu_d24_arm64,
+ fpu_d25_arm64,
+ fpu_d26_arm64,
+ fpu_d27_arm64,
+ fpu_d28_arm64,
+ fpu_d29_arm64,
+ fpu_d30_arm64,
+ fpu_d31_arm64,
+
+ fpu_fpsr_arm64,
+ fpu_fpcr_arm64,
+ k_last_fpr_arm64 = fpu_fpcr_arm64,
+
+ exc_far_arm64,
+ exc_esr_arm64,
+ exc_exception_arm64,
+
+ dbg_bvr0_arm64,
+ dbg_bvr1_arm64,
+ dbg_bvr2_arm64,
+ dbg_bvr3_arm64,
+ dbg_bvr4_arm64,
+ dbg_bvr5_arm64,
+ dbg_bvr6_arm64,
+ dbg_bvr7_arm64,
+ dbg_bvr8_arm64,
+ dbg_bvr9_arm64,
+ dbg_bvr10_arm64,
+ dbg_bvr11_arm64,
+ dbg_bvr12_arm64,
+ dbg_bvr13_arm64,
+ dbg_bvr14_arm64,
+ dbg_bvr15_arm64,
+ dbg_bcr0_arm64,
+ dbg_bcr1_arm64,
+ dbg_bcr2_arm64,
+ dbg_bcr3_arm64,
+ dbg_bcr4_arm64,
+ dbg_bcr5_arm64,
+ dbg_bcr6_arm64,
+ dbg_bcr7_arm64,
+ dbg_bcr8_arm64,
+ dbg_bcr9_arm64,
+ dbg_bcr10_arm64,
+ dbg_bcr11_arm64,
+ dbg_bcr12_arm64,
+ dbg_bcr13_arm64,
+ dbg_bcr14_arm64,
+ dbg_bcr15_arm64,
+ dbg_wvr0_arm64,
+ dbg_wvr1_arm64,
+ dbg_wvr2_arm64,
+ dbg_wvr3_arm64,
+ dbg_wvr4_arm64,
+ dbg_wvr5_arm64,
+ dbg_wvr6_arm64,
+ dbg_wvr7_arm64,
+ dbg_wvr8_arm64,
+ dbg_wvr9_arm64,
+ dbg_wvr10_arm64,
+ dbg_wvr11_arm64,
+ dbg_wvr12_arm64,
+ dbg_wvr13_arm64,
+ dbg_wvr14_arm64,
+ dbg_wvr15_arm64,
+ dbg_wcr0_arm64,
+ dbg_wcr1_arm64,
+ dbg_wcr2_arm64,
+ dbg_wcr3_arm64,
+ dbg_wcr4_arm64,
+ dbg_wcr5_arm64,
+ dbg_wcr6_arm64,
+ dbg_wcr7_arm64,
+ dbg_wcr8_arm64,
+ dbg_wcr9_arm64,
+ dbg_wcr10_arm64,
+ dbg_wcr11_arm64,
+ dbg_wcr12_arm64,
+ dbg_wcr13_arm64,
+ dbg_wcr14_arm64,
+ dbg_wcr15_arm64,
+
+ k_num_registers_arm64,
+ k_num_gpr_registers_arm64 = k_last_gpr_arm64 - k_first_gpr_arm64 + 1,
+ k_num_fpr_registers_arm64 = k_last_fpr_arm64 - k_first_fpr_arm64 + 1
+};
+}
+
+#endif // #ifndef lldb_arm64_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h
new file mode 100644
index 000000000000..d97f77122426
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h
@@ -0,0 +1,65 @@
+//===-- lldb-mips-freebsd-register-enums.h ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_mips_freebsd_register_enums_h
+#define lldb_mips_freebsd_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all mips registers.
+enum {
+ k_first_gpr_mips64,
+ gpr_zero_mips64 = k_first_gpr_mips64,
+ gpr_r1_mips64,
+ gpr_r2_mips64,
+ gpr_r3_mips64,
+ gpr_r4_mips64,
+ gpr_r5_mips64,
+ gpr_r6_mips64,
+ gpr_r7_mips64,
+ gpr_r8_mips64,
+ gpr_r9_mips64,
+ gpr_r10_mips64,
+ gpr_r11_mips64,
+ gpr_r12_mips64,
+ gpr_r13_mips64,
+ gpr_r14_mips64,
+ gpr_r15_mips64,
+ gpr_r16_mips64,
+ gpr_r17_mips64,
+ gpr_r18_mips64,
+ gpr_r19_mips64,
+ gpr_r20_mips64,
+ gpr_r21_mips64,
+ gpr_r22_mips64,
+ gpr_r23_mips64,
+ gpr_r24_mips64,
+ gpr_r25_mips64,
+ gpr_r26_mips64,
+ gpr_r27_mips64,
+ gpr_gp_mips64,
+ gpr_sp_mips64,
+ gpr_r30_mips64,
+ gpr_ra_mips64,
+ gpr_sr_mips64,
+ gpr_mullo_mips64,
+ gpr_mulhi_mips64,
+ gpr_badvaddr_mips64,
+ gpr_cause_mips64,
+ gpr_pc_mips64,
+ gpr_ic_mips64,
+ gpr_dummy_mips64,
+ k_last_gpr_mips64 = gpr_dummy_mips64,
+
+ k_num_registers_mips64,
+
+ k_num_gpr_registers_mips64 = k_last_gpr_mips64 - k_first_gpr_mips64 + 1
+};
+}
+#endif // #ifndef lldb_mips_freebsd_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.h
new file mode 100644
index 000000000000..2f68b8022c9a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.h
@@ -0,0 +1,360 @@
+//===-- lldb-mips-linux-register-enums.h -------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_mips_linux_register_enums_h
+#define lldb_mips_linux_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all mips registers.
+enum {
+ k_first_gpr_mips,
+ gpr_zero_mips = k_first_gpr_mips,
+ gpr_r1_mips,
+ gpr_r2_mips,
+ gpr_r3_mips,
+ gpr_r4_mips,
+ gpr_r5_mips,
+ gpr_r6_mips,
+ gpr_r7_mips,
+ gpr_r8_mips,
+ gpr_r9_mips,
+ gpr_r10_mips,
+ gpr_r11_mips,
+ gpr_r12_mips,
+ gpr_r13_mips,
+ gpr_r14_mips,
+ gpr_r15_mips,
+ gpr_r16_mips,
+ gpr_r17_mips,
+ gpr_r18_mips,
+ gpr_r19_mips,
+ gpr_r20_mips,
+ gpr_r21_mips,
+ gpr_r22_mips,
+ gpr_r23_mips,
+ gpr_r24_mips,
+ gpr_r25_mips,
+ gpr_r26_mips,
+ gpr_r27_mips,
+ gpr_gp_mips,
+ gpr_sp_mips,
+ gpr_r30_mips,
+ gpr_ra_mips,
+ gpr_sr_mips,
+ gpr_mullo_mips,
+ gpr_mulhi_mips,
+ gpr_badvaddr_mips,
+ gpr_cause_mips,
+ gpr_pc_mips,
+ gpr_config5_mips,
+
+ k_last_gpr_mips = gpr_config5_mips,
+
+ k_first_fpr_mips,
+ fpr_f0_mips = k_first_fpr_mips,
+ fpr_f1_mips,
+ fpr_f2_mips,
+ fpr_f3_mips,
+ fpr_f4_mips,
+ fpr_f5_mips,
+ fpr_f6_mips,
+ fpr_f7_mips,
+ fpr_f8_mips,
+ fpr_f9_mips,
+ fpr_f10_mips,
+ fpr_f11_mips,
+ fpr_f12_mips,
+ fpr_f13_mips,
+ fpr_f14_mips,
+ fpr_f15_mips,
+ fpr_f16_mips,
+ fpr_f17_mips,
+ fpr_f18_mips,
+ fpr_f19_mips,
+ fpr_f20_mips,
+ fpr_f21_mips,
+ fpr_f22_mips,
+ fpr_f23_mips,
+ fpr_f24_mips,
+ fpr_f25_mips,
+ fpr_f26_mips,
+ fpr_f27_mips,
+ fpr_f28_mips,
+ fpr_f29_mips,
+ fpr_f30_mips,
+ fpr_f31_mips,
+ fpr_fcsr_mips,
+ fpr_fir_mips,
+ fpr_config5_mips,
+ k_last_fpr_mips = fpr_config5_mips,
+
+ k_first_msa_mips,
+ msa_w0_mips = k_first_msa_mips,
+ msa_w1_mips,
+ msa_w2_mips,
+ msa_w3_mips,
+ msa_w4_mips,
+ msa_w5_mips,
+ msa_w6_mips,
+ msa_w7_mips,
+ msa_w8_mips,
+ msa_w9_mips,
+ msa_w10_mips,
+ msa_w11_mips,
+ msa_w12_mips,
+ msa_w13_mips,
+ msa_w14_mips,
+ msa_w15_mips,
+ msa_w16_mips,
+ msa_w17_mips,
+ msa_w18_mips,
+ msa_w19_mips,
+ msa_w20_mips,
+ msa_w21_mips,
+ msa_w22_mips,
+ msa_w23_mips,
+ msa_w24_mips,
+ msa_w25_mips,
+ msa_w26_mips,
+ msa_w27_mips,
+ msa_w28_mips,
+ msa_w29_mips,
+ msa_w30_mips,
+ msa_w31_mips,
+ msa_fcsr_mips,
+ msa_fir_mips,
+ msa_mcsr_mips,
+ msa_mir_mips,
+ msa_config5_mips,
+ k_last_msa_mips = msa_config5_mips,
+
+ k_num_registers_mips,
+
+ k_num_gpr_registers_mips = k_last_gpr_mips - k_first_gpr_mips + 1,
+ k_num_fpr_registers_mips = k_last_fpr_mips - k_first_fpr_mips + 1,
+ k_num_msa_registers_mips = k_last_msa_mips - k_first_msa_mips + 1,
+ k_num_user_registers_mips = k_num_gpr_registers_mips +
+ k_num_fpr_registers_mips +
+ k_num_msa_registers_mips
+};
+
+// Internal codes for all mips64 registers.
+enum {
+ k_first_gpr_mips64,
+ gpr_zero_mips64 = k_first_gpr_mips64,
+ gpr_r1_mips64,
+ gpr_r2_mips64,
+ gpr_r3_mips64,
+ gpr_r4_mips64,
+ gpr_r5_mips64,
+ gpr_r6_mips64,
+ gpr_r7_mips64,
+ gpr_r8_mips64,
+ gpr_r9_mips64,
+ gpr_r10_mips64,
+ gpr_r11_mips64,
+ gpr_r12_mips64,
+ gpr_r13_mips64,
+ gpr_r14_mips64,
+ gpr_r15_mips64,
+ gpr_r16_mips64,
+ gpr_r17_mips64,
+ gpr_r18_mips64,
+ gpr_r19_mips64,
+ gpr_r20_mips64,
+ gpr_r21_mips64,
+ gpr_r22_mips64,
+ gpr_r23_mips64,
+ gpr_r24_mips64,
+ gpr_r25_mips64,
+ gpr_r26_mips64,
+ gpr_r27_mips64,
+ gpr_gp_mips64,
+ gpr_sp_mips64,
+ gpr_r30_mips64,
+ gpr_ra_mips64,
+ gpr_sr_mips64,
+ gpr_mullo_mips64,
+ gpr_mulhi_mips64,
+ gpr_badvaddr_mips64,
+ gpr_cause_mips64,
+ gpr_pc_mips64,
+ gpr_config5_mips64,
+ k_last_gpr_mips64 = gpr_config5_mips64,
+
+ k_first_fpr_mips64,
+ fpr_f0_mips64 = k_first_fpr_mips64,
+ fpr_f1_mips64,
+ fpr_f2_mips64,
+ fpr_f3_mips64,
+ fpr_f4_mips64,
+ fpr_f5_mips64,
+ fpr_f6_mips64,
+ fpr_f7_mips64,
+ fpr_f8_mips64,
+ fpr_f9_mips64,
+ fpr_f10_mips64,
+ fpr_f11_mips64,
+ fpr_f12_mips64,
+ fpr_f13_mips64,
+ fpr_f14_mips64,
+ fpr_f15_mips64,
+ fpr_f16_mips64,
+ fpr_f17_mips64,
+ fpr_f18_mips64,
+ fpr_f19_mips64,
+ fpr_f20_mips64,
+ fpr_f21_mips64,
+ fpr_f22_mips64,
+ fpr_f23_mips64,
+ fpr_f24_mips64,
+ fpr_f25_mips64,
+ fpr_f26_mips64,
+ fpr_f27_mips64,
+ fpr_f28_mips64,
+ fpr_f29_mips64,
+ fpr_f30_mips64,
+ fpr_f31_mips64,
+ fpr_fcsr_mips64,
+ fpr_fir_mips64,
+ fpr_config5_mips64,
+ k_last_fpr_mips64 = fpr_config5_mips64,
+
+ k_first_msa_mips64,
+ msa_w0_mips64 = k_first_msa_mips64,
+ msa_w1_mips64,
+ msa_w2_mips64,
+ msa_w3_mips64,
+ msa_w4_mips64,
+ msa_w5_mips64,
+ msa_w6_mips64,
+ msa_w7_mips64,
+ msa_w8_mips64,
+ msa_w9_mips64,
+ msa_w10_mips64,
+ msa_w11_mips64,
+ msa_w12_mips64,
+ msa_w13_mips64,
+ msa_w14_mips64,
+ msa_w15_mips64,
+ msa_w16_mips64,
+ msa_w17_mips64,
+ msa_w18_mips64,
+ msa_w19_mips64,
+ msa_w20_mips64,
+ msa_w21_mips64,
+ msa_w22_mips64,
+ msa_w23_mips64,
+ msa_w24_mips64,
+ msa_w25_mips64,
+ msa_w26_mips64,
+ msa_w27_mips64,
+ msa_w28_mips64,
+ msa_w29_mips64,
+ msa_w30_mips64,
+ msa_w31_mips64,
+ msa_fcsr_mips64,
+ msa_fir_mips64,
+ msa_mcsr_mips64,
+ msa_mir_mips64,
+ msa_config5_mips64,
+ k_last_msa_mips64 = msa_config5_mips64,
+
+ k_num_registers_mips64,
+
+ k_num_gpr_registers_mips64 = k_last_gpr_mips64 - k_first_gpr_mips64 + 1,
+ k_num_fpr_registers_mips64 = k_last_fpr_mips64 - k_first_fpr_mips64 + 1,
+ k_num_msa_registers_mips64 = k_last_msa_mips64 - k_first_msa_mips64 + 1,
+ k_num_user_registers_mips64 = k_num_gpr_registers_mips64 +
+ k_num_fpr_registers_mips64 +
+ k_num_msa_registers_mips64
+};
+
+// Register no. for RegisterKind = eRegisterKindProcessPlugin
+// The ptrace request PTRACE_PEEKUSER/PTRACE_POKEUSER used this number
+enum {
+ ptrace_zero_mips,
+ ptrace_r1_mips,
+ ptrace_r2_mips,
+ ptrace_r3_mips,
+ ptrace_r4_mips,
+ ptrace_r5_mips,
+ ptrace_r6_mips,
+ ptrace_r7_mips,
+ ptrace_r8_mips,
+ ptrace_r9_mips,
+ ptrace_r10_mips,
+ ptrace_r11_mips,
+ ptrace_r12_mips,
+ ptrace_r13_mips,
+ ptrace_r14_mips,
+ ptrace_r15_mips,
+ ptrace_r16_mips,
+ ptrace_r17_mips,
+ ptrace_r18_mips,
+ ptrace_r19_mips,
+ ptrace_r20_mips,
+ ptrace_r21_mips,
+ ptrace_r22_mips,
+ ptrace_r23_mips,
+ ptrace_r24_mips,
+ ptrace_r25_mips,
+ ptrace_r26_mips,
+ ptrace_r27_mips,
+ ptrace_gp_mips,
+ ptrace_sp_mips,
+ ptrace_r30_mips,
+ ptrace_ra_mips,
+ ptrace_f0_mips,
+ ptrace_f1_mips,
+ ptrace_f2_mips,
+ ptrace_f3_mips,
+ ptrace_f4_mips,
+ ptrace_f5_mips,
+ ptrace_f6_mips,
+ ptrace_f7_mips,
+ ptrace_f8_mips,
+ ptrace_f9_mips,
+ ptrace_f10_mips,
+ ptrace_f11_mips,
+ ptrace_f12_mips,
+ ptrace_f13_mips,
+ ptrace_f14_mips,
+ ptrace_f15_mips,
+ ptrace_f16_mips,
+ ptrace_f17_mips,
+ ptrace_f18_mips,
+ ptrace_f19_mips,
+ ptrace_f20_mips,
+ ptrace_f21_mips,
+ ptrace_f22_mips,
+ ptrace_f23_mips,
+ ptrace_f24_mips,
+ ptrace_f25_mips,
+ ptrace_f26_mips,
+ ptrace_f27_mips,
+ ptrace_f28_mips,
+ ptrace_f29_mips,
+ ptrace_f30_mips,
+ ptrace_f31_mips,
+ ptrace_pc_mips,
+ ptrace_cause_mips,
+ ptrace_badvaddr_mips,
+ ptrace_mulhi_mips,
+ ptrace_mullo_mips,
+ ptrace_fcsr_mips,
+ ptrace_fir_mips,
+ ptrace_sr_mips,
+ ptrace_config5_mips
+};
+}
+
+#endif // #ifndef lldb_mips_linux_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64-register-enums.h
new file mode 100644
index 000000000000..6edf7ee3864d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64-register-enums.h
@@ -0,0 +1,136 @@
+//===-- lldb-ppc64-register-enums.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_ppc64_register_enums_h
+#define lldb_ppc64_register_enums_h
+
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all ppc64 registers.
+enum {
+ k_first_gpr_ppc64,
+ gpr_r0_ppc64 = k_first_gpr_ppc64,
+ gpr_r1_ppc64,
+ gpr_r2_ppc64,
+ gpr_r3_ppc64,
+ gpr_r4_ppc64,
+ gpr_r5_ppc64,
+ gpr_r6_ppc64,
+ gpr_r7_ppc64,
+ gpr_r8_ppc64,
+ gpr_r9_ppc64,
+ gpr_r10_ppc64,
+ gpr_r11_ppc64,
+ gpr_r12_ppc64,
+ gpr_r13_ppc64,
+ gpr_r14_ppc64,
+ gpr_r15_ppc64,
+ gpr_r16_ppc64,
+ gpr_r17_ppc64,
+ gpr_r18_ppc64,
+ gpr_r19_ppc64,
+ gpr_r20_ppc64,
+ gpr_r21_ppc64,
+ gpr_r22_ppc64,
+ gpr_r23_ppc64,
+ gpr_r24_ppc64,
+ gpr_r25_ppc64,
+ gpr_r26_ppc64,
+ gpr_r27_ppc64,
+ gpr_r28_ppc64,
+ gpr_r29_ppc64,
+ gpr_r30_ppc64,
+ gpr_r31_ppc64,
+ gpr_cr_ppc64,
+ gpr_msr_ppc64,
+ gpr_xer_ppc64,
+ gpr_lr_ppc64,
+ gpr_ctr_ppc64,
+ gpr_pc_ppc64,
+ k_last_gpr_ppc64 = gpr_pc_ppc64,
+
+ k_first_fpr_ppc64,
+ fpr_f0_ppc64 = k_first_fpr_ppc64,
+ fpr_f1_ppc64,
+ fpr_f2_ppc64,
+ fpr_f3_ppc64,
+ fpr_f4_ppc64,
+ fpr_f5_ppc64,
+ fpr_f6_ppc64,
+ fpr_f7_ppc64,
+ fpr_f8_ppc64,
+ fpr_f9_ppc64,
+ fpr_f10_ppc64,
+ fpr_f11_ppc64,
+ fpr_f12_ppc64,
+ fpr_f13_ppc64,
+ fpr_f14_ppc64,
+ fpr_f15_ppc64,
+ fpr_f16_ppc64,
+ fpr_f17_ppc64,
+ fpr_f18_ppc64,
+ fpr_f19_ppc64,
+ fpr_f20_ppc64,
+ fpr_f21_ppc64,
+ fpr_f22_ppc64,
+ fpr_f23_ppc64,
+ fpr_f24_ppc64,
+ fpr_f25_ppc64,
+ fpr_f26_ppc64,
+ fpr_f27_ppc64,
+ fpr_f28_ppc64,
+ fpr_f29_ppc64,
+ fpr_f30_ppc64,
+ fpr_f31_ppc64,
+ fpr_fpscr_ppc64,
+ k_last_fpr_ppc64 = fpr_fpscr_ppc64,
+
+ k_first_vmx_ppc64,
+ vmx_vr0_ppc64 = k_first_vmx_ppc64,
+ vmx_vr1_ppc64,
+ vmx_vr2_ppc64,
+ vmx_vr3_ppc64,
+ vmx_vr4_ppc64,
+ vmx_vr5_ppc64,
+ vmx_vr6_ppc64,
+ vmx_vr7_ppc64,
+ vmx_vr8_ppc64,
+ vmx_vr9_ppc64,
+ vmx_vr10_ppc64,
+ vmx_vr11_ppc64,
+ vmx_vr12_ppc64,
+ vmx_vr13_ppc64,
+ vmx_vr14_ppc64,
+ vmx_vr15_ppc64,
+ vmx_vr16_ppc64,
+ vmx_vr17_ppc64,
+ vmx_vr18_ppc64,
+ vmx_vr19_ppc64,
+ vmx_vr20_ppc64,
+ vmx_vr21_ppc64,
+ vmx_vr22_ppc64,
+ vmx_vr23_ppc64,
+ vmx_vr24_ppc64,
+ vmx_vr25_ppc64,
+ vmx_vr26_ppc64,
+ vmx_vr27_ppc64,
+ vmx_vr28_ppc64,
+ vmx_vr29_ppc64,
+ vmx_vr30_ppc64,
+ vmx_vr31_ppc64,
+ vmx_vscr_ppc64,
+ vmx_vrsave_ppc64,
+ k_last_vmx_ppc64 = vmx_vrsave_ppc64,
+
+ k_num_registers_ppc64,
+ k_num_gpr_registers_ppc64 = k_last_gpr_ppc64 - k_first_gpr_ppc64 + 1,
+ k_num_fpr_registers_ppc64 = k_last_fpr_ppc64 - k_first_fpr_ppc64 + 1,
+ k_num_vmx_registers_ppc64 = k_last_vmx_ppc64 - k_first_vmx_ppc64 + 1,
+};
+
+#endif // #ifndef lldb_ppc64_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64le-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64le-register-enums.h
new file mode 100644
index 000000000000..0c381a5f3918
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64le-register-enums.h
@@ -0,0 +1,207 @@
+//===-- lldb-ppc64le-register-enums.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_ppc64le_register_enums_h
+#define lldb_ppc64le_register_enums_h
+
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all ppc64le registers.
+enum {
+ k_first_gpr_ppc64le,
+ gpr_r0_ppc64le = k_first_gpr_ppc64le,
+ gpr_r1_ppc64le,
+ gpr_r2_ppc64le,
+ gpr_r3_ppc64le,
+ gpr_r4_ppc64le,
+ gpr_r5_ppc64le,
+ gpr_r6_ppc64le,
+ gpr_r7_ppc64le,
+ gpr_r8_ppc64le,
+ gpr_r9_ppc64le,
+ gpr_r10_ppc64le,
+ gpr_r11_ppc64le,
+ gpr_r12_ppc64le,
+ gpr_r13_ppc64le,
+ gpr_r14_ppc64le,
+ gpr_r15_ppc64le,
+ gpr_r16_ppc64le,
+ gpr_r17_ppc64le,
+ gpr_r18_ppc64le,
+ gpr_r19_ppc64le,
+ gpr_r20_ppc64le,
+ gpr_r21_ppc64le,
+ gpr_r22_ppc64le,
+ gpr_r23_ppc64le,
+ gpr_r24_ppc64le,
+ gpr_r25_ppc64le,
+ gpr_r26_ppc64le,
+ gpr_r27_ppc64le,
+ gpr_r28_ppc64le,
+ gpr_r29_ppc64le,
+ gpr_r30_ppc64le,
+ gpr_r31_ppc64le,
+ gpr_pc_ppc64le,
+ gpr_msr_ppc64le,
+ gpr_origr3_ppc64le,
+ gpr_ctr_ppc64le,
+ gpr_lr_ppc64le,
+ gpr_xer_ppc64le,
+ gpr_cr_ppc64le,
+ gpr_softe_ppc64le,
+ gpr_trap_ppc64le,
+ k_last_gpr_ppc64le = gpr_trap_ppc64le,
+
+ k_first_fpr_ppc64le,
+ fpr_f0_ppc64le = k_first_fpr_ppc64le,
+ fpr_f1_ppc64le,
+ fpr_f2_ppc64le,
+ fpr_f3_ppc64le,
+ fpr_f4_ppc64le,
+ fpr_f5_ppc64le,
+ fpr_f6_ppc64le,
+ fpr_f7_ppc64le,
+ fpr_f8_ppc64le,
+ fpr_f9_ppc64le,
+ fpr_f10_ppc64le,
+ fpr_f11_ppc64le,
+ fpr_f12_ppc64le,
+ fpr_f13_ppc64le,
+ fpr_f14_ppc64le,
+ fpr_f15_ppc64le,
+ fpr_f16_ppc64le,
+ fpr_f17_ppc64le,
+ fpr_f18_ppc64le,
+ fpr_f19_ppc64le,
+ fpr_f20_ppc64le,
+ fpr_f21_ppc64le,
+ fpr_f22_ppc64le,
+ fpr_f23_ppc64le,
+ fpr_f24_ppc64le,
+ fpr_f25_ppc64le,
+ fpr_f26_ppc64le,
+ fpr_f27_ppc64le,
+ fpr_f28_ppc64le,
+ fpr_f29_ppc64le,
+ fpr_f30_ppc64le,
+ fpr_f31_ppc64le,
+ fpr_fpscr_ppc64le,
+ k_last_fpr_ppc64le = fpr_fpscr_ppc64le,
+
+ k_first_vmx_ppc64le,
+ vmx_vr0_ppc64le = k_first_vmx_ppc64le,
+ vmx_vr1_ppc64le,
+ vmx_vr2_ppc64le,
+ vmx_vr3_ppc64le,
+ vmx_vr4_ppc64le,
+ vmx_vr5_ppc64le,
+ vmx_vr6_ppc64le,
+ vmx_vr7_ppc64le,
+ vmx_vr8_ppc64le,
+ vmx_vr9_ppc64le,
+ vmx_vr10_ppc64le,
+ vmx_vr11_ppc64le,
+ vmx_vr12_ppc64le,
+ vmx_vr13_ppc64le,
+ vmx_vr14_ppc64le,
+ vmx_vr15_ppc64le,
+ vmx_vr16_ppc64le,
+ vmx_vr17_ppc64le,
+ vmx_vr18_ppc64le,
+ vmx_vr19_ppc64le,
+ vmx_vr20_ppc64le,
+ vmx_vr21_ppc64le,
+ vmx_vr22_ppc64le,
+ vmx_vr23_ppc64le,
+ vmx_vr24_ppc64le,
+ vmx_vr25_ppc64le,
+ vmx_vr26_ppc64le,
+ vmx_vr27_ppc64le,
+ vmx_vr28_ppc64le,
+ vmx_vr29_ppc64le,
+ vmx_vr30_ppc64le,
+ vmx_vr31_ppc64le,
+ vmx_vscr_ppc64le,
+ vmx_vrsave_ppc64le,
+ k_last_vmx_ppc64le = vmx_vrsave_ppc64le,
+
+ k_first_vsx_ppc64le,
+ vsx_vs0_ppc64le = k_first_vsx_ppc64le,
+ vsx_vs1_ppc64le,
+ vsx_vs2_ppc64le,
+ vsx_vs3_ppc64le,
+ vsx_vs4_ppc64le,
+ vsx_vs5_ppc64le,
+ vsx_vs6_ppc64le,
+ vsx_vs7_ppc64le,
+ vsx_vs8_ppc64le,
+ vsx_vs9_ppc64le,
+ vsx_vs10_ppc64le,
+ vsx_vs11_ppc64le,
+ vsx_vs12_ppc64le,
+ vsx_vs13_ppc64le,
+ vsx_vs14_ppc64le,
+ vsx_vs15_ppc64le,
+ vsx_vs16_ppc64le,
+ vsx_vs17_ppc64le,
+ vsx_vs18_ppc64le,
+ vsx_vs19_ppc64le,
+ vsx_vs20_ppc64le,
+ vsx_vs21_ppc64le,
+ vsx_vs22_ppc64le,
+ vsx_vs23_ppc64le,
+ vsx_vs24_ppc64le,
+ vsx_vs25_ppc64le,
+ vsx_vs26_ppc64le,
+ vsx_vs27_ppc64le,
+ vsx_vs28_ppc64le,
+ vsx_vs29_ppc64le,
+ vsx_vs30_ppc64le,
+ vsx_vs31_ppc64le,
+ vsx_vs32_ppc64le,
+ vsx_vs33_ppc64le,
+ vsx_vs34_ppc64le,
+ vsx_vs35_ppc64le,
+ vsx_vs36_ppc64le,
+ vsx_vs37_ppc64le,
+ vsx_vs38_ppc64le,
+ vsx_vs39_ppc64le,
+ vsx_vs40_ppc64le,
+ vsx_vs41_ppc64le,
+ vsx_vs42_ppc64le,
+ vsx_vs43_ppc64le,
+ vsx_vs44_ppc64le,
+ vsx_vs45_ppc64le,
+ vsx_vs46_ppc64le,
+ vsx_vs47_ppc64le,
+ vsx_vs48_ppc64le,
+ vsx_vs49_ppc64le,
+ vsx_vs50_ppc64le,
+ vsx_vs51_ppc64le,
+ vsx_vs52_ppc64le,
+ vsx_vs53_ppc64le,
+ vsx_vs54_ppc64le,
+ vsx_vs55_ppc64le,
+ vsx_vs56_ppc64le,
+ vsx_vs57_ppc64le,
+ vsx_vs58_ppc64le,
+ vsx_vs59_ppc64le,
+ vsx_vs60_ppc64le,
+ vsx_vs61_ppc64le,
+ vsx_vs62_ppc64le,
+ vsx_vs63_ppc64le,
+ k_last_vsx_ppc64le = vsx_vs63_ppc64le,
+
+ k_num_registers_ppc64le,
+ k_num_gpr_registers_ppc64le = k_last_gpr_ppc64le - k_first_gpr_ppc64le + 1,
+ k_num_fpr_registers_ppc64le = k_last_fpr_ppc64le - k_first_fpr_ppc64le + 1,
+ k_num_vmx_registers_ppc64le = k_last_vmx_ppc64le - k_first_vmx_ppc64le + 1,
+ k_num_vsx_registers_ppc64le = k_last_vsx_ppc64le - k_first_vsx_ppc64le + 1,
+};
+
+#endif // #ifndef lldb_ppc64le_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-s390x-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-s390x-register-enums.h
new file mode 100644
index 000000000000..bd6626108290
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-s390x-register-enums.h
@@ -0,0 +1,90 @@
+//===-- lldb-s390x-register-enums.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_s390x_register_enums_h
+#define lldb_s390x_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all s390x registers.
+enum {
+ k_first_gpr_s390x,
+ lldb_r0_s390x = k_first_gpr_s390x,
+ lldb_r1_s390x,
+ lldb_r2_s390x,
+ lldb_r3_s390x,
+ lldb_r4_s390x,
+ lldb_r5_s390x,
+ lldb_r6_s390x,
+ lldb_r7_s390x,
+ lldb_r8_s390x,
+ lldb_r9_s390x,
+ lldb_r10_s390x,
+ lldb_r11_s390x,
+ lldb_r12_s390x,
+ lldb_r13_s390x,
+ lldb_r14_s390x,
+ lldb_r15_s390x,
+ lldb_acr0_s390x,
+ lldb_acr1_s390x,
+ lldb_acr2_s390x,
+ lldb_acr3_s390x,
+ lldb_acr4_s390x,
+ lldb_acr5_s390x,
+ lldb_acr6_s390x,
+ lldb_acr7_s390x,
+ lldb_acr8_s390x,
+ lldb_acr9_s390x,
+ lldb_acr10_s390x,
+ lldb_acr11_s390x,
+ lldb_acr12_s390x,
+ lldb_acr13_s390x,
+ lldb_acr14_s390x,
+ lldb_acr15_s390x,
+ lldb_pswm_s390x,
+ lldb_pswa_s390x,
+ k_last_gpr_s390x = lldb_pswa_s390x,
+
+ k_first_fpr_s390x,
+ lldb_f0_s390x = k_first_fpr_s390x,
+ lldb_f1_s390x,
+ lldb_f2_s390x,
+ lldb_f3_s390x,
+ lldb_f4_s390x,
+ lldb_f5_s390x,
+ lldb_f6_s390x,
+ lldb_f7_s390x,
+ lldb_f8_s390x,
+ lldb_f9_s390x,
+ lldb_f10_s390x,
+ lldb_f11_s390x,
+ lldb_f12_s390x,
+ lldb_f13_s390x,
+ lldb_f14_s390x,
+ lldb_f15_s390x,
+ lldb_fpc_s390x,
+ k_last_fpr_s390x = lldb_fpc_s390x,
+
+ // These are only available on Linux.
+ k_first_linux_s390x,
+ lldb_orig_r2_s390x = k_first_linux_s390x,
+ lldb_last_break_s390x,
+ lldb_system_call_s390x,
+ k_last_linux_s390x = lldb_system_call_s390x,
+
+ k_num_registers_s390x,
+ k_num_gpr_registers_s390x = k_last_gpr_s390x - k_first_gpr_s390x + 1,
+ k_num_fpr_registers_s390x = k_last_fpr_s390x - k_first_fpr_s390x + 1,
+ k_num_linux_registers_s390x = k_last_linux_s390x - k_first_linux_s390x + 1,
+ k_num_user_registers_s390x =
+ k_num_gpr_registers_s390x + k_num_fpr_registers_s390x,
+};
+}
+
+#endif // #ifndef lldb_s390x_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-x86-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-x86-register-enums.h
new file mode 100644
index 000000000000..0d2149c83573
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-x86-register-enums.h
@@ -0,0 +1,318 @@
+//===-- lldb-x86-register-enums.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_x86_register_enums_h
+#define lldb_x86_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all i386 registers.
+enum {
+ k_first_gpr_i386,
+ lldb_eax_i386 = k_first_gpr_i386,
+ lldb_ebx_i386,
+ lldb_ecx_i386,
+ lldb_edx_i386,
+ lldb_edi_i386,
+ lldb_esi_i386,
+ lldb_ebp_i386,
+ lldb_esp_i386,
+ lldb_eip_i386,
+ lldb_eflags_i386,
+ lldb_cs_i386,
+ lldb_fs_i386,
+ lldb_gs_i386,
+ lldb_ss_i386,
+ lldb_ds_i386,
+ lldb_es_i386,
+
+ k_first_alias_i386,
+ lldb_ax_i386 = k_first_alias_i386,
+ lldb_bx_i386,
+ lldb_cx_i386,
+ lldb_dx_i386,
+ lldb_di_i386,
+ lldb_si_i386,
+ lldb_bp_i386,
+ lldb_sp_i386,
+ lldb_ah_i386,
+ lldb_bh_i386,
+ lldb_ch_i386,
+ lldb_dh_i386,
+ lldb_al_i386,
+ lldb_bl_i386,
+ lldb_cl_i386,
+ lldb_dl_i386,
+ k_last_alias_i386 = lldb_dl_i386,
+
+ k_last_gpr_i386 = k_last_alias_i386,
+
+ k_first_fpr_i386,
+ lldb_fctrl_i386 = k_first_fpr_i386,
+ lldb_fstat_i386,
+ lldb_ftag_i386,
+ lldb_fop_i386,
+ lldb_fiseg_i386,
+ lldb_fioff_i386,
+ lldb_foseg_i386,
+ lldb_fooff_i386,
+ lldb_mxcsr_i386,
+ lldb_mxcsrmask_i386,
+ lldb_st0_i386,
+ lldb_st1_i386,
+ lldb_st2_i386,
+ lldb_st3_i386,
+ lldb_st4_i386,
+ lldb_st5_i386,
+ lldb_st6_i386,
+ lldb_st7_i386,
+ lldb_mm0_i386,
+ lldb_mm1_i386,
+ lldb_mm2_i386,
+ lldb_mm3_i386,
+ lldb_mm4_i386,
+ lldb_mm5_i386,
+ lldb_mm6_i386,
+ lldb_mm7_i386,
+ lldb_xmm0_i386,
+ lldb_xmm1_i386,
+ lldb_xmm2_i386,
+ lldb_xmm3_i386,
+ lldb_xmm4_i386,
+ lldb_xmm5_i386,
+ lldb_xmm6_i386,
+ lldb_xmm7_i386,
+ k_last_fpr_i386 = lldb_xmm7_i386,
+
+ k_first_avx_i386,
+ lldb_ymm0_i386 = k_first_avx_i386,
+ lldb_ymm1_i386,
+ lldb_ymm2_i386,
+ lldb_ymm3_i386,
+ lldb_ymm4_i386,
+ lldb_ymm5_i386,
+ lldb_ymm6_i386,
+ lldb_ymm7_i386,
+ k_last_avx_i386 = lldb_ymm7_i386,
+
+ k_first_mpxr_i386,
+ lldb_bnd0_i386 = k_first_mpxr_i386,
+ lldb_bnd1_i386,
+ lldb_bnd2_i386,
+ lldb_bnd3_i386,
+ k_last_mpxr = lldb_bnd3_i386,
+
+ k_first_mpxc_i386,
+ lldb_bndcfgu_i386 = k_first_mpxc_i386,
+ lldb_bndstatus_i386,
+ k_last_mpxc_i386 = lldb_bndstatus_i386,
+
+ lldb_dr0_i386,
+ lldb_dr1_i386,
+ lldb_dr2_i386,
+ lldb_dr3_i386,
+ lldb_dr4_i386,
+ lldb_dr5_i386,
+ lldb_dr6_i386,
+ lldb_dr7_i386,
+
+ k_num_registers_i386,
+ k_num_gpr_registers_i386 = k_last_gpr_i386 - k_first_gpr_i386 + 1,
+ k_num_fpr_registers_i386 = k_last_fpr_i386 - k_first_fpr_i386 + 1,
+ k_num_avx_registers_i386 = k_last_avx_i386 - k_first_avx_i386 + 1,
+ k_num_mpx_registers_i386 = k_last_mpxc_i386 - k_first_mpxr_i386 + 1,
+ k_num_user_registers_i386 = k_num_gpr_registers_i386 +
+ k_num_fpr_registers_i386 +
+ k_num_avx_registers_i386 +
+ k_num_mpx_registers_i386,
+};
+
+// Internal codes for all x86_64 registers.
+enum {
+ k_first_gpr_x86_64,
+ lldb_rax_x86_64 = k_first_gpr_x86_64,
+ lldb_rbx_x86_64,
+ lldb_rcx_x86_64,
+ lldb_rdx_x86_64,
+ lldb_rdi_x86_64,
+ lldb_rsi_x86_64,
+ lldb_rbp_x86_64,
+ lldb_rsp_x86_64,
+ lldb_r8_x86_64,
+ lldb_r9_x86_64,
+ lldb_r10_x86_64,
+ lldb_r11_x86_64,
+ lldb_r12_x86_64,
+ lldb_r13_x86_64,
+ lldb_r14_x86_64,
+ lldb_r15_x86_64,
+ lldb_rip_x86_64,
+ lldb_rflags_x86_64,
+ lldb_cs_x86_64,
+ lldb_fs_x86_64,
+ lldb_gs_x86_64,
+ lldb_ss_x86_64,
+ lldb_ds_x86_64,
+ lldb_es_x86_64,
+
+ k_first_alias_x86_64,
+ lldb_eax_x86_64 = k_first_alias_x86_64,
+ lldb_ebx_x86_64,
+ lldb_ecx_x86_64,
+ lldb_edx_x86_64,
+ lldb_edi_x86_64,
+ lldb_esi_x86_64,
+ lldb_ebp_x86_64,
+ lldb_esp_x86_64,
+ lldb_r8d_x86_64, // Low 32 bits of r8
+ lldb_r9d_x86_64, // Low 32 bits of r9
+ lldb_r10d_x86_64, // Low 32 bits of r10
+ lldb_r11d_x86_64, // Low 32 bits of r11
+ lldb_r12d_x86_64, // Low 32 bits of r12
+ lldb_r13d_x86_64, // Low 32 bits of r13
+ lldb_r14d_x86_64, // Low 32 bits of r14
+ lldb_r15d_x86_64, // Low 32 bits of r15
+ lldb_ax_x86_64,
+ lldb_bx_x86_64,
+ lldb_cx_x86_64,
+ lldb_dx_x86_64,
+ lldb_di_x86_64,
+ lldb_si_x86_64,
+ lldb_bp_x86_64,
+ lldb_sp_x86_64,
+ lldb_r8w_x86_64, // Low 16 bits of r8
+ lldb_r9w_x86_64, // Low 16 bits of r9
+ lldb_r10w_x86_64, // Low 16 bits of r10
+ lldb_r11w_x86_64, // Low 16 bits of r11
+ lldb_r12w_x86_64, // Low 16 bits of r12
+ lldb_r13w_x86_64, // Low 16 bits of r13
+ lldb_r14w_x86_64, // Low 16 bits of r14
+ lldb_r15w_x86_64, // Low 16 bits of r15
+ lldb_ah_x86_64,
+ lldb_bh_x86_64,
+ lldb_ch_x86_64,
+ lldb_dh_x86_64,
+ lldb_al_x86_64,
+ lldb_bl_x86_64,
+ lldb_cl_x86_64,
+ lldb_dl_x86_64,
+ lldb_dil_x86_64,
+ lldb_sil_x86_64,
+ lldb_bpl_x86_64,
+ lldb_spl_x86_64,
+ lldb_r8l_x86_64, // Low 8 bits of r8
+ lldb_r9l_x86_64, // Low 8 bits of r9
+ lldb_r10l_x86_64, // Low 8 bits of r10
+ lldb_r11l_x86_64, // Low 8 bits of r11
+ lldb_r12l_x86_64, // Low 8 bits of r12
+ lldb_r13l_x86_64, // Low 8 bits of r13
+ lldb_r14l_x86_64, // Low 8 bits of r14
+ lldb_r15l_x86_64, // Low 8 bits of r15
+ k_last_alias_x86_64 = lldb_r15l_x86_64,
+
+ k_last_gpr_x86_64 = k_last_alias_x86_64,
+
+ k_first_fpr_x86_64,
+ lldb_fctrl_x86_64 = k_first_fpr_x86_64,
+ lldb_fstat_x86_64,
+ lldb_ftag_x86_64,
+ lldb_fop_x86_64,
+ lldb_fiseg_x86_64,
+ lldb_fioff_x86_64,
+ lldb_foseg_x86_64,
+ lldb_fooff_x86_64,
+ lldb_mxcsr_x86_64,
+ lldb_mxcsrmask_x86_64,
+ lldb_st0_x86_64,
+ lldb_st1_x86_64,
+ lldb_st2_x86_64,
+ lldb_st3_x86_64,
+ lldb_st4_x86_64,
+ lldb_st5_x86_64,
+ lldb_st6_x86_64,
+ lldb_st7_x86_64,
+ lldb_mm0_x86_64,
+ lldb_mm1_x86_64,
+ lldb_mm2_x86_64,
+ lldb_mm3_x86_64,
+ lldb_mm4_x86_64,
+ lldb_mm5_x86_64,
+ lldb_mm6_x86_64,
+ lldb_mm7_x86_64,
+ lldb_xmm0_x86_64,
+ lldb_xmm1_x86_64,
+ lldb_xmm2_x86_64,
+ lldb_xmm3_x86_64,
+ lldb_xmm4_x86_64,
+ lldb_xmm5_x86_64,
+ lldb_xmm6_x86_64,
+ lldb_xmm7_x86_64,
+ lldb_xmm8_x86_64,
+ lldb_xmm9_x86_64,
+ lldb_xmm10_x86_64,
+ lldb_xmm11_x86_64,
+ lldb_xmm12_x86_64,
+ lldb_xmm13_x86_64,
+ lldb_xmm14_x86_64,
+ lldb_xmm15_x86_64,
+ k_last_fpr_x86_64 = lldb_xmm15_x86_64,
+
+ k_first_avx_x86_64,
+ lldb_ymm0_x86_64 = k_first_avx_x86_64,
+ lldb_ymm1_x86_64,
+ lldb_ymm2_x86_64,
+ lldb_ymm3_x86_64,
+ lldb_ymm4_x86_64,
+ lldb_ymm5_x86_64,
+ lldb_ymm6_x86_64,
+ lldb_ymm7_x86_64,
+ lldb_ymm8_x86_64,
+ lldb_ymm9_x86_64,
+ lldb_ymm10_x86_64,
+ lldb_ymm11_x86_64,
+ lldb_ymm12_x86_64,
+ lldb_ymm13_x86_64,
+ lldb_ymm14_x86_64,
+ lldb_ymm15_x86_64,
+ k_last_avx_x86_64 = lldb_ymm15_x86_64,
+
+ k_first_mpxr_x86_64,
+ lldb_bnd0_x86_64 = k_first_mpxr_x86_64,
+ lldb_bnd1_x86_64,
+ lldb_bnd2_x86_64,
+ lldb_bnd3_x86_64,
+ k_last_mpxr_x86_64 = lldb_bnd3_x86_64,
+
+ k_first_mpxc_x86_64,
+ lldb_bndcfgu_x86_64 = k_first_mpxc_x86_64,
+ lldb_bndstatus_x86_64,
+ k_last_mpxc_x86_64 = lldb_bndstatus_x86_64,
+
+ lldb_dr0_x86_64,
+ lldb_dr1_x86_64,
+ lldb_dr2_x86_64,
+ lldb_dr3_x86_64,
+ lldb_dr4_x86_64,
+ lldb_dr5_x86_64,
+ lldb_dr6_x86_64,
+ lldb_dr7_x86_64,
+
+ k_num_registers_x86_64,
+ k_num_gpr_registers_x86_64 = k_last_gpr_x86_64 - k_first_gpr_x86_64 + 1,
+ k_num_fpr_registers_x86_64 = k_last_fpr_x86_64 - k_first_fpr_x86_64 + 1,
+ k_num_avx_registers_x86_64 = k_last_avx_x86_64 - k_first_avx_x86_64 + 1,
+ k_num_mpx_registers_x86_64 = k_last_mpxc_x86_64 - k_first_mpxr_x86_64 + 1,
+ k_num_user_registers_x86_64 = k_num_gpr_registers_x86_64 +
+ k_num_fpr_registers_x86_64 +
+ k_num_avx_registers_x86_64 +
+ k_num_mpx_registers_x86_64,
+};
+}
+
+#endif // #ifndef lldb_x86_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
new file mode 100644
index 000000000000..980169e16c51
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
@@ -0,0 +1,915 @@
+//===-- ProcessElfCore.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdlib.h>
+
+#include <memory>
+#include <mutex>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Support/Threading.h"
+
+#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h"
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "ProcessElfCore.h"
+#include "ThreadElfCore.h"
+
+using namespace lldb_private;
+
+ConstString ProcessElfCore::GetPluginNameStatic() {
+ static ConstString g_name("elf-core");
+ return g_name;
+}
+
+const char *ProcessElfCore::GetPluginDescriptionStatic() {
+ return "ELF core dump plug-in.";
+}
+
+void ProcessElfCore::Terminate() {
+ PluginManager::UnregisterPlugin(ProcessElfCore::CreateInstance);
+}
+
+lldb::ProcessSP ProcessElfCore::CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file) {
+ lldb::ProcessSP process_sp;
+ if (crash_file) {
+ // Read enough data for a ELF32 header or ELF64 header Note: Here we care
+ // about e_type field only, so it is safe to ignore possible presence of
+ // the header extension.
+ const size_t header_size = sizeof(llvm::ELF::Elf64_Ehdr);
+
+ auto data_sp = FileSystem::Instance().CreateDataBuffer(
+ crash_file->GetPath(), header_size, 0);
+ if (data_sp && data_sp->GetByteSize() == header_size &&
+ elf::ELFHeader::MagicBytesMatch(data_sp->GetBytes())) {
+ elf::ELFHeader elf_header;
+ DataExtractor data(data_sp, lldb::eByteOrderLittle, 4);
+ lldb::offset_t data_offset = 0;
+ if (elf_header.Parse(data, &data_offset)) {
+ if (elf_header.e_type == llvm::ELF::ET_CORE)
+ process_sp = std::make_shared<ProcessElfCore>(target_sp, listener_sp,
+ *crash_file);
+ }
+ }
+ }
+ return process_sp;
+}
+
+bool ProcessElfCore::CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) {
+ // For now we are just making sure the file exists for a given module
+ if (!m_core_module_sp && FileSystem::Instance().Exists(m_core_file)) {
+ ModuleSpec core_module_spec(m_core_file, target_sp->GetArchitecture());
+ Status error(ModuleList::GetSharedModule(core_module_spec, m_core_module_sp,
+ nullptr, nullptr, nullptr));
+ if (m_core_module_sp) {
+ ObjectFile *core_objfile = m_core_module_sp->GetObjectFile();
+ if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile)
+ return true;
+ }
+ }
+ return false;
+}
+
+// ProcessElfCore constructor
+ProcessElfCore::ProcessElfCore(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec &core_file)
+ : Process(target_sp, listener_sp), m_core_file(core_file) {}
+
+// Destructor
+ProcessElfCore::~ProcessElfCore() {
+ Clear();
+ // We need to call finalize on the process before destroying ourselves to
+ // make sure all of the broadcaster cleanup goes as planned. If we destruct
+ // this class, then Process::~Process() might have problems trying to fully
+ // destroy the broadcaster.
+ Finalize();
+}
+
+// PluginInterface
+ConstString ProcessElfCore::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t ProcessElfCore::GetPluginVersion() { return 1; }
+
+lldb::addr_t ProcessElfCore::AddAddressRangeFromLoadSegment(
+ const elf::ELFProgramHeader &header) {
+ const lldb::addr_t addr = header.p_vaddr;
+ FileRange file_range(header.p_offset, header.p_filesz);
+ VMRangeToFileOffset::Entry range_entry(addr, header.p_memsz, file_range);
+
+ VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back();
+ if (last_entry && last_entry->GetRangeEnd() == range_entry.GetRangeBase() &&
+ last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase() &&
+ last_entry->GetByteSize() == last_entry->data.GetByteSize()) {
+ last_entry->SetRangeEnd(range_entry.GetRangeEnd());
+ last_entry->data.SetRangeEnd(range_entry.data.GetRangeEnd());
+ } else {
+ m_core_aranges.Append(range_entry);
+ }
+
+ // Keep a separate map of permissions that that isn't coalesced so all ranges
+ // are maintained.
+ const uint32_t permissions =
+ ((header.p_flags & llvm::ELF::PF_R) ? lldb::ePermissionsReadable : 0u) |
+ ((header.p_flags & llvm::ELF::PF_W) ? lldb::ePermissionsWritable : 0u) |
+ ((header.p_flags & llvm::ELF::PF_X) ? lldb::ePermissionsExecutable : 0u);
+
+ m_core_range_infos.Append(
+ VMRangeToPermissions::Entry(addr, header.p_memsz, permissions));
+
+ return addr;
+}
+
+// Process Control
+Status ProcessElfCore::DoLoadCore() {
+ Status error;
+ if (!m_core_module_sp) {
+ error.SetErrorString("invalid core module");
+ return error;
+ }
+
+ ObjectFileELF *core = (ObjectFileELF *)(m_core_module_sp->GetObjectFile());
+ if (core == nullptr) {
+ error.SetErrorString("invalid core object file");
+ return error;
+ }
+
+ llvm::ArrayRef<elf::ELFProgramHeader> segments = core->ProgramHeaders();
+ if (segments.size() == 0) {
+ error.SetErrorString("core file has no segments");
+ return error;
+ }
+
+ SetCanJIT(false);
+
+ m_thread_data_valid = true;
+
+ bool ranges_are_sorted = true;
+ lldb::addr_t vm_addr = 0;
+ /// Walk through segments and Thread and Address Map information.
+ /// PT_NOTE - Contains Thread and Register information
+ /// PT_LOAD - Contains a contiguous range of Process Address Space
+ for (const elf::ELFProgramHeader &H : segments) {
+ DataExtractor data = core->GetSegmentData(H);
+
+ // Parse thread contexts and auxv structure
+ if (H.p_type == llvm::ELF::PT_NOTE) {
+ if (llvm::Error error = ParseThreadContextsFromNoteSegment(H, data))
+ return Status(std::move(error));
+ }
+ // PT_LOAD segments contains address map
+ if (H.p_type == llvm::ELF::PT_LOAD) {
+ lldb::addr_t last_addr = AddAddressRangeFromLoadSegment(H);
+ if (vm_addr > last_addr)
+ ranges_are_sorted = false;
+ vm_addr = last_addr;
+ }
+ }
+
+ if (!ranges_are_sorted) {
+ m_core_aranges.Sort();
+ m_core_range_infos.Sort();
+ }
+
+ // Even if the architecture is set in the target, we need to override it to
+ // match the core file which is always single arch.
+ ArchSpec arch(m_core_module_sp->GetArchitecture());
+
+ ArchSpec target_arch = GetTarget().GetArchitecture();
+ ArchSpec core_arch(m_core_module_sp->GetArchitecture());
+ target_arch.MergeFrom(core_arch);
+ GetTarget().SetArchitecture(target_arch);
+
+ SetUnixSignals(UnixSignals::Create(GetArchitecture()));
+
+ // Ensure we found at least one thread that was stopped on a signal.
+ bool siginfo_signal_found = false;
+ bool prstatus_signal_found = false;
+ // Check we found a signal in a SIGINFO note.
+ for (const auto &thread_data : m_thread_data) {
+ if (thread_data.signo != 0)
+ siginfo_signal_found = true;
+ if (thread_data.prstatus_sig != 0)
+ prstatus_signal_found = true;
+ }
+ if (!siginfo_signal_found) {
+ // If we don't have signal from SIGINFO use the signal from each threads
+ // PRSTATUS note.
+ if (prstatus_signal_found) {
+ for (auto &thread_data : m_thread_data)
+ thread_data.signo = thread_data.prstatus_sig;
+ } else if (m_thread_data.size() > 0) {
+ // If all else fails force the first thread to be SIGSTOP
+ m_thread_data.begin()->signo =
+ GetUnixSignals()->GetSignalNumberFromName("SIGSTOP");
+ }
+ }
+
+ // Core files are useless without the main executable. See if we can locate
+ // the main executable using data we found in the core file notes.
+ lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
+ if (!exe_module_sp) {
+ // The first entry in the NT_FILE might be our executable
+ if (!m_nt_file_entries.empty()) {
+ ModuleSpec exe_module_spec;
+ exe_module_spec.GetArchitecture() = arch;
+ exe_module_spec.GetFileSpec().SetFile(
+ m_nt_file_entries[0].path.GetCString(), FileSpec::Style::native);
+ if (exe_module_spec.GetFileSpec()) {
+ exe_module_sp = GetTarget().GetOrCreateModule(exe_module_spec,
+ true /* notify */);
+ if (exe_module_sp)
+ GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsNo);
+ }
+ }
+ }
+ return error;
+}
+
+lldb_private::DynamicLoader *ProcessElfCore::GetDynamicLoader() {
+ if (m_dyld_up.get() == nullptr)
+ m_dyld_up.reset(DynamicLoader::FindPlugin(
+ this, DynamicLoaderPOSIXDYLD::GetPluginNameStatic().GetCString()));
+ return m_dyld_up.get();
+}
+
+bool ProcessElfCore::UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) {
+ const uint32_t num_threads = GetNumThreadContexts();
+ if (!m_thread_data_valid)
+ return false;
+
+ for (lldb::tid_t tid = 0; tid < num_threads; ++tid) {
+ const ThreadData &td = m_thread_data[tid];
+ lldb::ThreadSP thread_sp(new ThreadElfCore(*this, td));
+ new_thread_list.AddThread(thread_sp);
+ }
+ return new_thread_list.GetSize(false) > 0;
+}
+
+void ProcessElfCore::RefreshStateAfterStop() {}
+
+Status ProcessElfCore::DoDestroy() { return Status(); }
+
+// Process Queries
+
+bool ProcessElfCore::IsAlive() { return true; }
+
+// Process Memory
+size_t ProcessElfCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) {
+ // Don't allow the caching that lldb_private::Process::ReadMemory does since
+ // in core files we have it all cached our our core file anyway.
+ return DoReadMemory(addr, buf, size, error);
+}
+
+Status ProcessElfCore::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &region_info) {
+ region_info.Clear();
+ const VMRangeToPermissions::Entry *permission_entry =
+ m_core_range_infos.FindEntryThatContainsOrFollows(load_addr);
+ if (permission_entry) {
+ if (permission_entry->Contains(load_addr)) {
+ region_info.GetRange().SetRangeBase(permission_entry->GetRangeBase());
+ region_info.GetRange().SetRangeEnd(permission_entry->GetRangeEnd());
+ const Flags permissions(permission_entry->data);
+ region_info.SetReadable(permissions.Test(lldb::ePermissionsReadable)
+ ? MemoryRegionInfo::eYes
+ : MemoryRegionInfo::eNo);
+ region_info.SetWritable(permissions.Test(lldb::ePermissionsWritable)
+ ? MemoryRegionInfo::eYes
+ : MemoryRegionInfo::eNo);
+ region_info.SetExecutable(permissions.Test(lldb::ePermissionsExecutable)
+ ? MemoryRegionInfo::eYes
+ : MemoryRegionInfo::eNo);
+ region_info.SetMapped(MemoryRegionInfo::eYes);
+ } else if (load_addr < permission_entry->GetRangeBase()) {
+ region_info.GetRange().SetRangeBase(load_addr);
+ region_info.GetRange().SetRangeEnd(permission_entry->GetRangeBase());
+ region_info.SetReadable(MemoryRegionInfo::eNo);
+ region_info.SetWritable(MemoryRegionInfo::eNo);
+ region_info.SetExecutable(MemoryRegionInfo::eNo);
+ region_info.SetMapped(MemoryRegionInfo::eNo);
+ }
+ return Status();
+ }
+
+ region_info.GetRange().SetRangeBase(load_addr);
+ region_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS);
+ region_info.SetReadable(MemoryRegionInfo::eNo);
+ region_info.SetWritable(MemoryRegionInfo::eNo);
+ region_info.SetExecutable(MemoryRegionInfo::eNo);
+ region_info.SetMapped(MemoryRegionInfo::eNo);
+ return Status();
+}
+
+size_t ProcessElfCore::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) {
+ ObjectFile *core_objfile = m_core_module_sp->GetObjectFile();
+
+ if (core_objfile == nullptr)
+ return 0;
+
+ // Get the address range
+ const VMRangeToFileOffset::Entry *address_range =
+ m_core_aranges.FindEntryThatContains(addr);
+ if (address_range == nullptr || address_range->GetRangeEnd() < addr) {
+ error.SetErrorStringWithFormat("core file does not contain 0x%" PRIx64,
+ addr);
+ return 0;
+ }
+
+ // Convert the address into core file offset
+ const lldb::addr_t offset = addr - address_range->GetRangeBase();
+ const lldb::addr_t file_start = address_range->data.GetRangeBase();
+ const lldb::addr_t file_end = address_range->data.GetRangeEnd();
+ size_t bytes_to_read = size; // Number of bytes to read from the core file
+ size_t bytes_copied = 0; // Number of bytes actually read from the core file
+ size_t zero_fill_size = 0; // Padding
+ lldb::addr_t bytes_left =
+ 0; // Number of bytes available in the core file from the given address
+
+ // Don't proceed if core file doesn't contain the actual data for this
+ // address range.
+ if (file_start == file_end)
+ return 0;
+
+ // Figure out how many on-disk bytes remain in this segment starting at the
+ // given offset
+ if (file_end > file_start + offset)
+ bytes_left = file_end - (file_start + offset);
+
+ // Figure out how many bytes we need to zero-fill if we are reading more
+ // bytes than available in the on-disk segment
+ if (bytes_to_read > bytes_left) {
+ zero_fill_size = bytes_to_read - bytes_left;
+ bytes_to_read = bytes_left;
+ }
+
+ // If there is data available on the core file read it
+ if (bytes_to_read)
+ bytes_copied =
+ core_objfile->CopyData(offset + file_start, bytes_to_read, buf);
+
+ assert(zero_fill_size <= size);
+ // Pad remaining bytes
+ if (zero_fill_size)
+ memset(((char *)buf) + bytes_copied, 0, zero_fill_size);
+
+ return bytes_copied + zero_fill_size;
+}
+
+void ProcessElfCore::Clear() {
+ m_thread_list.Clear();
+
+ SetUnixSignals(std::make_shared<UnixSignals>());
+}
+
+void ProcessElfCore::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+ });
+}
+
+lldb::addr_t ProcessElfCore::GetImageInfoAddress() {
+ ObjectFile *obj_file = GetTarget().GetExecutableModule()->GetObjectFile();
+ Address addr = obj_file->GetImageInfoAddress(&GetTarget());
+
+ if (addr.IsValid())
+ return addr.GetLoadAddress(&GetTarget());
+ return LLDB_INVALID_ADDRESS;
+}
+
+// Parse a FreeBSD NT_PRSTATUS note - see FreeBSD sys/procfs.h for details.
+static void ParseFreeBSDPrStatus(ThreadData &thread_data,
+ const DataExtractor &data,
+ const ArchSpec &arch) {
+ lldb::offset_t offset = 0;
+ bool lp64 = (arch.GetMachine() == llvm::Triple::aarch64 ||
+ arch.GetMachine() == llvm::Triple::mips64 ||
+ arch.GetMachine() == llvm::Triple::ppc64 ||
+ arch.GetMachine() == llvm::Triple::x86_64);
+ int pr_version = data.GetU32(&offset);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log) {
+ if (pr_version > 1)
+ log->Printf("FreeBSD PRSTATUS unexpected version %d", pr_version);
+ }
+
+ // Skip padding, pr_statussz, pr_gregsetsz, pr_fpregsetsz, pr_osreldate
+ if (lp64)
+ offset += 32;
+ else
+ offset += 16;
+
+ thread_data.signo = data.GetU32(&offset); // pr_cursig
+ thread_data.tid = data.GetU32(&offset); // pr_pid
+ if (lp64)
+ offset += 4;
+
+ size_t len = data.GetByteSize() - offset;
+ thread_data.gpregset = DataExtractor(data, offset, len);
+}
+
+static llvm::Error ParseNetBSDProcInfo(const DataExtractor &data,
+ uint32_t &cpi_nlwps,
+ uint32_t &cpi_signo,
+ uint32_t &cpi_siglwp,
+ uint32_t &cpi_pid) {
+ lldb::offset_t offset = 0;
+
+ uint32_t version = data.GetU32(&offset);
+ if (version != 1)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Unsupported procinfo version",
+ llvm::inconvertibleErrorCode());
+
+ uint32_t cpisize = data.GetU32(&offset);
+ if (cpisize != NETBSD::NT_PROCINFO_SIZE)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Unsupported procinfo size",
+ llvm::inconvertibleErrorCode());
+
+ cpi_signo = data.GetU32(&offset); /* killing signal */
+
+ offset += NETBSD::NT_PROCINFO_CPI_SIGCODE_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SIGPEND_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SIGMASK_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SIGIGNORE_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SIGCATCH_SIZE;
+ cpi_pid = data.GetU32(&offset);
+ offset += NETBSD::NT_PROCINFO_CPI_PPID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_PGRP_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_RUID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_EUID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SVUID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_RGID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_EGID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SVGID_SIZE;
+ cpi_nlwps = data.GetU32(&offset); /* number of LWPs */
+
+ offset += NETBSD::NT_PROCINFO_CPI_NAME_SIZE;
+ cpi_siglwp = data.GetU32(&offset); /* LWP target of killing signal */
+
+ return llvm::Error::success();
+}
+
+static void ParseOpenBSDProcInfo(ThreadData &thread_data,
+ const DataExtractor &data) {
+ lldb::offset_t offset = 0;
+
+ int version = data.GetU32(&offset);
+ if (version != 1)
+ return;
+
+ offset += 4;
+ thread_data.signo = data.GetU32(&offset);
+}
+
+llvm::Expected<std::vector<CoreNote>>
+ProcessElfCore::parseSegment(const DataExtractor &segment) {
+ lldb::offset_t offset = 0;
+ std::vector<CoreNote> result;
+
+ while (offset < segment.GetByteSize()) {
+ ELFNote note = ELFNote();
+ if (!note.Parse(segment, &offset))
+ return llvm::make_error<llvm::StringError>(
+ "Unable to parse note segment", llvm::inconvertibleErrorCode());
+
+ size_t note_start = offset;
+ size_t note_size = llvm::alignTo(note.n_descsz, 4);
+ DataExtractor note_data(segment, note_start, note_size);
+
+ result.push_back({note, note_data});
+ offset += note_size;
+ }
+
+ return std::move(result);
+}
+
+llvm::Error ProcessElfCore::parseFreeBSDNotes(llvm::ArrayRef<CoreNote> notes) {
+ bool have_prstatus = false;
+ bool have_prpsinfo = false;
+ ThreadData thread_data;
+ for (const auto &note : notes) {
+ if (note.info.n_name != "FreeBSD")
+ continue;
+
+ if ((note.info.n_type == FREEBSD::NT_PRSTATUS && have_prstatus) ||
+ (note.info.n_type == FREEBSD::NT_PRPSINFO && have_prpsinfo)) {
+ assert(thread_data.gpregset.GetByteSize() > 0);
+ // Add the new thread to thread list
+ m_thread_data.push_back(thread_data);
+ thread_data = ThreadData();
+ have_prstatus = false;
+ have_prpsinfo = false;
+ }
+
+ switch (note.info.n_type) {
+ case FREEBSD::NT_PRSTATUS:
+ have_prstatus = true;
+ ParseFreeBSDPrStatus(thread_data, note.data, GetArchitecture());
+ break;
+ case FREEBSD::NT_PRPSINFO:
+ have_prpsinfo = true;
+ break;
+ case FREEBSD::NT_THRMISC: {
+ lldb::offset_t offset = 0;
+ thread_data.name = note.data.GetCStr(&offset, 20);
+ break;
+ }
+ case FREEBSD::NT_PROCSTAT_AUXV:
+ // FIXME: FreeBSD sticks an int at the beginning of the note
+ m_auxv = DataExtractor(note.data, 4, note.data.GetByteSize() - 4);
+ break;
+ default:
+ thread_data.notes.push_back(note);
+ break;
+ }
+ }
+ if (!have_prstatus) {
+ return llvm::make_error<llvm::StringError>(
+ "Could not find NT_PRSTATUS note in core file.",
+ llvm::inconvertibleErrorCode());
+ }
+ m_thread_data.push_back(thread_data);
+ return llvm::Error::success();
+}
+
+/// NetBSD specific Thread context from PT_NOTE segment
+///
+/// NetBSD ELF core files use notes to provide information about
+/// the process's state. The note name is "NetBSD-CORE" for
+/// information that is global to the process, and "NetBSD-CORE@nn",
+/// where "nn" is the lwpid of the LWP that the information belongs
+/// to (such as register state).
+///
+/// NetBSD uses the following note identifiers:
+///
+/// ELF_NOTE_NETBSD_CORE_PROCINFO (value 1)
+/// Note is a "netbsd_elfcore_procinfo" structure.
+/// ELF_NOTE_NETBSD_CORE_AUXV (value 2; since NetBSD 8.0)
+/// Note is an array of AuxInfo structures.
+///
+/// NetBSD also uses ptrace(2) request numbers (the ones that exist in
+/// machine-dependent space) to identify register info notes. The
+/// info in such notes is in the same format that ptrace(2) would
+/// export that information.
+///
+/// For more information see /usr/include/sys/exec_elf.h
+///
+llvm::Error ProcessElfCore::parseNetBSDNotes(llvm::ArrayRef<CoreNote> notes) {
+ ThreadData thread_data;
+ bool had_nt_regs = false;
+
+ // To be extracted from struct netbsd_elfcore_procinfo
+ // Used to sanity check of the LWPs of the process
+ uint32_t nlwps = 0;
+ uint32_t signo; // killing signal
+ uint32_t siglwp; // LWP target of killing signal
+ uint32_t pr_pid;
+
+ for (const auto &note : notes) {
+ llvm::StringRef name = note.info.n_name;
+
+ if (name == "NetBSD-CORE") {
+ if (note.info.n_type == NETBSD::NT_PROCINFO) {
+ llvm::Error error = ParseNetBSDProcInfo(note.data, nlwps, signo,
+ siglwp, pr_pid);
+ if (error)
+ return error;
+ SetID(pr_pid);
+ } else if (note.info.n_type == NETBSD::NT_AUXV) {
+ m_auxv = note.data;
+ }
+ } else if (name.consume_front("NetBSD-CORE@")) {
+ lldb::tid_t tid;
+ if (name.getAsInteger(10, tid))
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Cannot convert LWP ID "
+ "to integer",
+ llvm::inconvertibleErrorCode());
+
+ switch (GetArchitecture().GetMachine()) {
+ case llvm::Triple::aarch64: {
+ // Assume order PT_GETREGS, PT_GETFPREGS
+ if (note.info.n_type == NETBSD::AARCH64::NT_REGS) {
+ // If this is the next thread, push the previous one first.
+ if (had_nt_regs) {
+ m_thread_data.push_back(thread_data);
+ thread_data = ThreadData();
+ had_nt_regs = false;
+ }
+
+ thread_data.gpregset = note.data;
+ thread_data.tid = tid;
+ if (thread_data.gpregset.GetByteSize() == 0)
+ return llvm::make_error<llvm::StringError>(
+ "Could not find general purpose registers note in core file.",
+ llvm::inconvertibleErrorCode());
+ had_nt_regs = true;
+ } else if (note.info.n_type == NETBSD::AARCH64::NT_FPREGS) {
+ if (!had_nt_regs || tid != thread_data.tid)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Unexpected order "
+ "of NOTEs PT_GETFPREG before PT_GETREG",
+ llvm::inconvertibleErrorCode());
+ thread_data.notes.push_back(note);
+ }
+ } break;
+ case llvm::Triple::x86_64: {
+ // Assume order PT_GETREGS, PT_GETFPREGS
+ if (note.info.n_type == NETBSD::AMD64::NT_REGS) {
+ // If this is the next thread, push the previous one first.
+ if (had_nt_regs) {
+ m_thread_data.push_back(thread_data);
+ thread_data = ThreadData();
+ had_nt_regs = false;
+ }
+
+ thread_data.gpregset = note.data;
+ thread_data.tid = tid;
+ if (thread_data.gpregset.GetByteSize() == 0)
+ return llvm::make_error<llvm::StringError>(
+ "Could not find general purpose registers note in core file.",
+ llvm::inconvertibleErrorCode());
+ had_nt_regs = true;
+ } else if (note.info.n_type == NETBSD::AMD64::NT_FPREGS) {
+ if (!had_nt_regs || tid != thread_data.tid)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Unexpected order "
+ "of NOTEs PT_GETFPREG before PT_GETREG",
+ llvm::inconvertibleErrorCode());
+ thread_data.notes.push_back(note);
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Push the last thread.
+ if (had_nt_regs)
+ m_thread_data.push_back(thread_data);
+
+ if (m_thread_data.empty())
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: No threads information "
+ "specified in notes",
+ llvm::inconvertibleErrorCode());
+
+ if (m_thread_data.size() != nlwps)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Mismatch between the number "
+ "of LWPs in netbsd_elfcore_procinfo and the number of LWPs specified "
+ "by MD notes",
+ llvm::inconvertibleErrorCode());
+
+ // Signal targeted at the whole process.
+ if (siglwp == 0) {
+ for (auto &data : m_thread_data)
+ data.signo = signo;
+ }
+ // Signal destined for a particular LWP.
+ else {
+ bool passed = false;
+
+ for (auto &data : m_thread_data) {
+ if (data.tid == siglwp) {
+ data.signo = signo;
+ passed = true;
+ break;
+ }
+ }
+
+ if (!passed)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Signal passed to unknown LWP",
+ llvm::inconvertibleErrorCode());
+ }
+
+ return llvm::Error::success();
+}
+
+llvm::Error ProcessElfCore::parseOpenBSDNotes(llvm::ArrayRef<CoreNote> notes) {
+ ThreadData thread_data;
+ for (const auto &note : notes) {
+ // OpenBSD per-thread information is stored in notes named "OpenBSD@nnn" so
+ // match on the initial part of the string.
+ if (!llvm::StringRef(note.info.n_name).startswith("OpenBSD"))
+ continue;
+
+ switch (note.info.n_type) {
+ case OPENBSD::NT_PROCINFO:
+ ParseOpenBSDProcInfo(thread_data, note.data);
+ break;
+ case OPENBSD::NT_AUXV:
+ m_auxv = note.data;
+ break;
+ case OPENBSD::NT_REGS:
+ thread_data.gpregset = note.data;
+ break;
+ default:
+ thread_data.notes.push_back(note);
+ break;
+ }
+ }
+ if (thread_data.gpregset.GetByteSize() == 0) {
+ return llvm::make_error<llvm::StringError>(
+ "Could not find general purpose registers note in core file.",
+ llvm::inconvertibleErrorCode());
+ }
+ m_thread_data.push_back(thread_data);
+ return llvm::Error::success();
+}
+
+/// A description of a linux process usually contains the following NOTE
+/// entries:
+/// - NT_PRPSINFO - General process information like pid, uid, name, ...
+/// - NT_SIGINFO - Information about the signal that terminated the process
+/// - NT_AUXV - Process auxiliary vector
+/// - NT_FILE - Files mapped into memory
+///
+/// Additionally, for each thread in the process the core file will contain at
+/// least the NT_PRSTATUS note, containing the thread id and general purpose
+/// registers. It may include additional notes for other register sets (floating
+/// point and vector registers, ...). The tricky part here is that some of these
+/// notes have "CORE" in their owner fields, while other set it to "LINUX".
+llvm::Error ProcessElfCore::parseLinuxNotes(llvm::ArrayRef<CoreNote> notes) {
+ const ArchSpec &arch = GetArchitecture();
+ bool have_prstatus = false;
+ bool have_prpsinfo = false;
+ ThreadData thread_data;
+ for (const auto &note : notes) {
+ if (note.info.n_name != "CORE" && note.info.n_name != "LINUX")
+ continue;
+
+ if ((note.info.n_type == LINUX::NT_PRSTATUS && have_prstatus) ||
+ (note.info.n_type == LINUX::NT_PRPSINFO && have_prpsinfo)) {
+ assert(thread_data.gpregset.GetByteSize() > 0);
+ // Add the new thread to thread list
+ m_thread_data.push_back(thread_data);
+ thread_data = ThreadData();
+ have_prstatus = false;
+ have_prpsinfo = false;
+ }
+
+ switch (note.info.n_type) {
+ case LINUX::NT_PRSTATUS: {
+ have_prstatus = true;
+ ELFLinuxPrStatus prstatus;
+ Status status = prstatus.Parse(note.data, arch);
+ if (status.Fail())
+ return status.ToError();
+ thread_data.prstatus_sig = prstatus.pr_cursig;
+ thread_data.tid = prstatus.pr_pid;
+ uint32_t header_size = ELFLinuxPrStatus::GetSize(arch);
+ size_t len = note.data.GetByteSize() - header_size;
+ thread_data.gpregset = DataExtractor(note.data, header_size, len);
+ break;
+ }
+ case LINUX::NT_PRPSINFO: {
+ have_prpsinfo = true;
+ ELFLinuxPrPsInfo prpsinfo;
+ Status status = prpsinfo.Parse(note.data, arch);
+ if (status.Fail())
+ return status.ToError();
+ thread_data.name.assign (prpsinfo.pr_fname, strnlen (prpsinfo.pr_fname, sizeof (prpsinfo.pr_fname)));
+ SetID(prpsinfo.pr_pid);
+ break;
+ }
+ case LINUX::NT_SIGINFO: {
+ ELFLinuxSigInfo siginfo;
+ Status status = siginfo.Parse(note.data, arch);
+ if (status.Fail())
+ return status.ToError();
+ thread_data.signo = siginfo.si_signo;
+ break;
+ }
+ case LINUX::NT_FILE: {
+ m_nt_file_entries.clear();
+ lldb::offset_t offset = 0;
+ const uint64_t count = note.data.GetAddress(&offset);
+ note.data.GetAddress(&offset); // Skip page size
+ for (uint64_t i = 0; i < count; ++i) {
+ NT_FILE_Entry entry;
+ entry.start = note.data.GetAddress(&offset);
+ entry.end = note.data.GetAddress(&offset);
+ entry.file_ofs = note.data.GetAddress(&offset);
+ m_nt_file_entries.push_back(entry);
+ }
+ for (uint64_t i = 0; i < count; ++i) {
+ const char *path = note.data.GetCStr(&offset);
+ if (path && path[0])
+ m_nt_file_entries[i].path.SetCString(path);
+ }
+ break;
+ }
+ case LINUX::NT_AUXV:
+ m_auxv = note.data;
+ break;
+ default:
+ thread_data.notes.push_back(note);
+ break;
+ }
+ }
+ // Add last entry in the note section
+ if (have_prstatus)
+ m_thread_data.push_back(thread_data);
+ return llvm::Error::success();
+}
+
+/// Parse Thread context from PT_NOTE segment and store it in the thread list
+/// A note segment consists of one or more NOTE entries, but their types and
+/// meaning differ depending on the OS.
+llvm::Error ProcessElfCore::ParseThreadContextsFromNoteSegment(
+ const elf::ELFProgramHeader &segment_header, DataExtractor segment_data) {
+ assert(segment_header.p_type == llvm::ELF::PT_NOTE);
+
+ auto notes_or_error = parseSegment(segment_data);
+ if(!notes_or_error)
+ return notes_or_error.takeError();
+ switch (GetArchitecture().GetTriple().getOS()) {
+ case llvm::Triple::FreeBSD:
+ return parseFreeBSDNotes(*notes_or_error);
+ case llvm::Triple::Linux:
+ return parseLinuxNotes(*notes_or_error);
+ case llvm::Triple::NetBSD:
+ return parseNetBSDNotes(*notes_or_error);
+ case llvm::Triple::OpenBSD:
+ return parseOpenBSDNotes(*notes_or_error);
+ default:
+ return llvm::make_error<llvm::StringError>(
+ "Don't know how to parse core file. Unsupported OS.",
+ llvm::inconvertibleErrorCode());
+ }
+}
+
+uint32_t ProcessElfCore::GetNumThreadContexts() {
+ if (!m_thread_data_valid)
+ DoLoadCore();
+ return m_thread_data.size();
+}
+
+ArchSpec ProcessElfCore::GetArchitecture() {
+ ArchSpec arch = m_core_module_sp->GetObjectFile()->GetArchitecture();
+
+ ArchSpec target_arch = GetTarget().GetArchitecture();
+ arch.MergeFrom(target_arch);
+
+ // On MIPS there is no way to differentiate betwenn 32bit and 64bit core
+ // files and this information can't be merged in from the target arch so we
+ // fail back to unconditionally returning the target arch in this config.
+ if (target_arch.IsMIPS()) {
+ return target_arch;
+ }
+
+ return arch;
+}
+
+DataExtractor ProcessElfCore::GetAuxvData() {
+ const uint8_t *start = m_auxv.GetDataStart();
+ size_t len = m_auxv.GetByteSize();
+ lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(start, len));
+ return DataExtractor(buffer, GetByteOrder(), GetAddressByteSize());
+}
+
+bool ProcessElfCore::GetProcessInfo(ProcessInstanceInfo &info) {
+ info.Clear();
+ info.SetProcessID(GetID());
+ info.SetArchitecture(GetArchitecture());
+ lldb::ModuleSP module_sp = GetTarget().GetExecutableModule();
+ if (module_sp) {
+ const bool add_exe_file_as_first_arg = false;
+ info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),
+ add_exe_file_as_first_arg);
+ }
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
new file mode 100644
index 000000000000..829fe9ac7199
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
@@ -0,0 +1,168 @@
+//===-- ProcessElfCore.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// Notes about Linux Process core dumps:
+// 1) Linux core dump is stored as ELF file.
+// 2) The ELF file's PT_NOTE and PT_LOAD segments describes the program's
+// address space and thread contexts.
+// 3) PT_NOTE segment contains note entries which describes a thread context.
+// 4) PT_LOAD segment describes a valid contiguous range of process address
+// space.
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessElfCore_h_
+#define liblldb_ProcessElfCore_h_
+
+#include <list>
+#include <vector>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/ObjectFile/ELF/ELFHeader.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+
+struct ThreadData;
+
+class ProcessElfCore : public lldb_private::Process {
+public:
+ // Constructors and Destructors
+ static lldb::ProcessSP
+ CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+ const lldb_private::FileSpec *crash_file_path);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ // Constructors and Destructors
+ ProcessElfCore(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+ const lldb_private::FileSpec &core_file);
+
+ ~ProcessElfCore() override;
+
+ // Check if a given Process
+ bool CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) override;
+
+ // Creating a new process, or attaching to an existing one
+ lldb_private::Status DoLoadCore() override;
+
+ lldb_private::DynamicLoader *GetDynamicLoader() override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // Process Control
+ lldb_private::Status DoDestroy() override;
+
+ void RefreshStateAfterStop() override;
+
+ lldb_private::Status WillResume() override {
+ lldb_private::Status error;
+ error.SetErrorStringWithFormat(
+ "error: %s does not support resuming processes",
+ GetPluginName().GetCString());
+ return error;
+ }
+
+ // Process Queries
+ bool IsAlive() override;
+
+ bool WarnBeforeDetach() const override { return false; }
+
+ // Process Memory
+ size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ lldb_private::Status &error) override;
+
+ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ lldb_private::Status &error) override;
+
+ lldb_private::Status
+ GetMemoryRegionInfo(lldb::addr_t load_addr,
+ lldb_private::MemoryRegionInfo &region_info) override;
+
+ lldb::addr_t GetImageInfoAddress() override;
+
+ lldb_private::ArchSpec GetArchitecture();
+
+ // Returns AUXV structure found in the core file
+ lldb_private::DataExtractor GetAuxvData() override;
+
+ bool GetProcessInfo(lldb_private::ProcessInstanceInfo &info) override;
+
+protected:
+ void Clear();
+
+ bool UpdateThreadList(lldb_private::ThreadList &old_thread_list,
+ lldb_private::ThreadList &new_thread_list) override;
+
+private:
+ struct NT_FILE_Entry {
+ lldb::addr_t start;
+ lldb::addr_t end;
+ lldb::addr_t file_ofs;
+ lldb_private::ConstString path;
+ };
+
+ // For ProcessElfCore only
+ typedef lldb_private::Range<lldb::addr_t, lldb::addr_t> FileRange;
+ typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, FileRange>
+ VMRangeToFileOffset;
+ typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, uint32_t>
+ VMRangeToPermissions;
+
+ lldb::ModuleSP m_core_module_sp;
+ lldb_private::FileSpec m_core_file;
+ std::string m_dyld_plugin_name;
+ DISALLOW_COPY_AND_ASSIGN(ProcessElfCore);
+
+ // True if m_thread_contexts contains valid entries
+ bool m_thread_data_valid = false;
+
+ // Contain thread data read from NOTE segments
+ std::vector<ThreadData> m_thread_data;
+
+ // AUXV structure found from the NOTE segment
+ lldb_private::DataExtractor m_auxv;
+
+ // Address ranges found in the core
+ VMRangeToFileOffset m_core_aranges;
+
+ // Permissions for all ranges
+ VMRangeToPermissions m_core_range_infos;
+
+ // NT_FILE entries found from the NOTE segment
+ std::vector<NT_FILE_Entry> m_nt_file_entries;
+
+ // Parse thread(s) data structures(prstatus, prpsinfo) from given NOTE segment
+ llvm::Error ParseThreadContextsFromNoteSegment(
+ const elf::ELFProgramHeader &segment_header,
+ lldb_private::DataExtractor segment_data);
+
+ // Returns number of thread contexts stored in the core file
+ uint32_t GetNumThreadContexts();
+
+ // Parse a contiguous address range of the process from LOAD segment
+ lldb::addr_t
+ AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader &header);
+
+ llvm::Expected<std::vector<lldb_private::CoreNote>>
+ parseSegment(const lldb_private::DataExtractor &segment);
+ llvm::Error parseFreeBSDNotes(llvm::ArrayRef<lldb_private::CoreNote> notes);
+ llvm::Error parseNetBSDNotes(llvm::ArrayRef<lldb_private::CoreNote> notes);
+ llvm::Error parseOpenBSDNotes(llvm::ArrayRef<lldb_private::CoreNote> notes);
+ llvm::Error parseLinuxNotes(llvm::ArrayRef<lldb_private::CoreNote> notes);
+};
+
+#endif // liblldb_ProcessElfCore_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp
new file mode 100644
index 000000000000..fa05c457fc61
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp
@@ -0,0 +1,72 @@
+//===-- RegisterContextPOSIXCore_arm.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextPOSIXCore_arm.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_arm::RegisterContextCorePOSIX_arm(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_arm(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+}
+
+RegisterContextCorePOSIX_arm::~RegisterContextCorePOSIX_arm() {}
+
+bool RegisterContextCorePOSIX_arm::ReadGPR() { return true; }
+
+bool RegisterContextCorePOSIX_arm::ReadFPR() { return false; }
+
+bool RegisterContextCorePOSIX_arm::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ lldb::offset_t offset = reg_info->byte_offset;
+ uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ value = v;
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h
new file mode 100644
index 000000000000..adda43ebccbc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h
@@ -0,0 +1,53 @@
+//===-- RegisterContextPOSIXCore_arm.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextCorePOSIX_arm_h_
+#define liblldb_RegisterContextCorePOSIX_arm_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_arm : public RegisterContextPOSIX_arm {
+public:
+ RegisterContextCorePOSIX_arm(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ ~RegisterContextCorePOSIX_arm() override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb_private::DataExtractor m_gpr;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp
new file mode 100644
index 000000000000..e477c438ba12
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp
@@ -0,0 +1,73 @@
+//===-- RegisterContextPOSIXCore_arm64.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextPOSIXCore_arm64.h"
+
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_arm64(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+}
+
+RegisterContextCorePOSIX_arm64::~RegisterContextCorePOSIX_arm64() {}
+
+bool RegisterContextCorePOSIX_arm64::ReadGPR() { return true; }
+
+bool RegisterContextCorePOSIX_arm64::ReadFPR() { return false; }
+
+bool RegisterContextCorePOSIX_arm64::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ lldb::offset_t offset = reg_info->byte_offset;
+ uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ value = v;
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h
new file mode 100644
index 000000000000..de6d819c29ce
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h
@@ -0,0 +1,53 @@
+//===-- RegisterContextPOSIXCore_arm64.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextCorePOSIX_arm64_h_
+#define liblldb_RegisterContextCorePOSIX_arm64_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_arm64 : public RegisterContextPOSIX_arm64 {
+public:
+ RegisterContextCorePOSIX_arm64(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ ~RegisterContextCorePOSIX_arm64() override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb_private::DataExtractor m_gpr;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_arm64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp
new file mode 100644
index 000000000000..3601f3b3b216
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp
@@ -0,0 +1,91 @@
+//===-- RegisterContextPOSIXCore_mips64.cpp ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextPOSIXCore_mips64.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_mips64::RegisterContextCorePOSIX_mips64(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_mips64(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+
+ DataExtractor fpregset = getRegset(
+ notes, register_info->GetTargetArchitecture().GetTriple(), FPR_Desc);
+ m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(),
+ fpregset.GetByteSize());
+ m_fpr.SetData(m_fpr_buffer);
+ m_fpr.SetByteOrder(fpregset.GetByteOrder());
+}
+
+RegisterContextCorePOSIX_mips64::~RegisterContextCorePOSIX_mips64() {}
+
+bool RegisterContextCorePOSIX_mips64::ReadGPR() { return true; }
+
+bool RegisterContextCorePOSIX_mips64::ReadFPR() { return false; }
+
+bool RegisterContextCorePOSIX_mips64::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+
+ lldb::offset_t offset = reg_info->byte_offset;
+ lldb_private::ArchSpec arch = m_register_info_up->GetTargetArchitecture();
+ uint64_t v;
+ if (IsGPR(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ if (reg_info->byte_size == 4 && !(arch.GetMachine() == llvm::Triple::mips64el))
+ // In case of 32bit core file, the register data are placed at 4 byte
+ // offset.
+ offset = offset / 2;
+ v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+ value = v;
+ return true;
+ } else if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ offset = offset - sizeof(GPR_linux_mips);
+ v =m_fpr.GetMaxU64(&offset, reg_info->byte_size);
+ value = v;
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.h
new file mode 100644
index 000000000000..999c9451573b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.h
@@ -0,0 +1,55 @@
+//===-- RegisterContextPOSIXCore_mips64.h -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextCorePOSIX_mips64_h_
+#define liblldb_RegisterContextCorePOSIX_mips64_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_mips64.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_mips64 : public RegisterContextPOSIX_mips64 {
+public:
+ RegisterContextCorePOSIX_mips64(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ ~RegisterContextCorePOSIX_mips64() override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb::DataBufferSP m_fpr_buffer;
+ lldb_private::DataExtractor m_gpr;
+ lldb_private::DataExtractor m_fpr;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_mips64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp
new file mode 100644
index 000000000000..6984bf4ee9b8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp
@@ -0,0 +1,111 @@
+//===-- RegisterContextPOSIXCore_powerpc.cpp --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextPOSIXCore_powerpc.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_powerpc::RegisterContextCorePOSIX_powerpc(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_powerpc(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+
+ ArchSpec arch = register_info->GetTargetArchitecture();
+ DataExtractor fpregset = getRegset(notes, arch.GetTriple(), FPR_Desc);
+ m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(),
+ fpregset.GetByteSize());
+ m_fpr.SetData(m_fpr_buffer);
+ m_fpr.SetByteOrder(fpregset.GetByteOrder());
+
+ DataExtractor vregset = getRegset(notes, arch.GetTriple(), PPC_VMX_Desc);
+ m_vec_buffer = std::make_shared<DataBufferHeap>(vregset.GetDataStart(),
+ vregset.GetByteSize());
+ m_vec.SetData(m_vec_buffer);
+ m_vec.SetByteOrder(vregset.GetByteOrder());
+}
+
+RegisterContextCorePOSIX_powerpc::~RegisterContextCorePOSIX_powerpc() {}
+
+bool RegisterContextCorePOSIX_powerpc::ReadGPR() { return true; }
+
+bool RegisterContextCorePOSIX_powerpc::ReadFPR() { return true; }
+
+bool RegisterContextCorePOSIX_powerpc::ReadVMX() { return true; }
+
+bool RegisterContextCorePOSIX_powerpc::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::WriteVMX() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ lldb::offset_t offset = reg_info->byte_offset;
+ if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ uint64_t v = m_fpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ value = v;
+ return true;
+ }
+ } else if (IsVMX(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ uint32_t v[4];
+ offset = m_vec.CopyData(offset, reg_info->byte_size, &v);
+ if (offset == reg_info->byte_size) {
+ value.SetBytes(v, reg_info->byte_size, m_vec.GetByteOrder());
+ return true;
+ }
+ } else {
+ uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ if (reg_info->byte_size < sizeof(v))
+ value = (uint32_t)v;
+ else
+ value = v;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h
new file mode 100644
index 000000000000..7684c0b31837
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h
@@ -0,0 +1,60 @@
+//===-- RegisterContextPOSIXCore_powerpc.h ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextCorePOSIX_powerpc_h_
+#define liblldb_RegisterContextCorePOSIX_powerpc_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_powerpc : public RegisterContextPOSIX_powerpc {
+public:
+ RegisterContextCorePOSIX_powerpc(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ ~RegisterContextCorePOSIX_powerpc() override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool ReadVMX() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+ bool WriteVMX() override;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb::DataBufferSP m_fpr_buffer;
+ lldb::DataBufferSP m_vec_buffer;
+ lldb_private::DataExtractor m_gpr;
+ lldb_private::DataExtractor m_fpr;
+ lldb_private::DataExtractor m_vec;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_powerpc_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.cpp
new file mode 100644
index 000000000000..0eebf474f60e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.cpp
@@ -0,0 +1,133 @@
+//===-- RegisterContextPOSIXCore_ppc64le.cpp --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextPOSIXCore_ppc64le.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_ppc64le::RegisterContextCorePOSIX_ppc64le(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_ppc64le(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+
+ ArchSpec arch = register_info->GetTargetArchitecture();
+ DataExtractor fpregset = getRegset(notes, arch.GetTriple(), FPR_Desc);
+ m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(),
+ fpregset.GetByteSize());
+ m_fpr.SetData(m_fpr_buffer);
+ m_fpr.SetByteOrder(fpregset.GetByteOrder());
+
+ DataExtractor vmxregset = getRegset(notes, arch.GetTriple(), PPC_VMX_Desc);
+ m_vmx_buffer = std::make_shared<DataBufferHeap>(vmxregset.GetDataStart(),
+ vmxregset.GetByteSize());
+ m_vmx.SetData(m_vmx_buffer);
+ m_vmx.SetByteOrder(vmxregset.GetByteOrder());
+
+ DataExtractor vsxregset = getRegset(notes, arch.GetTriple(), PPC_VSX_Desc);
+ m_vsx_buffer = std::make_shared<DataBufferHeap>(vsxregset.GetDataStart(),
+ vsxregset.GetByteSize());
+ m_vsx.SetData(m_vsx_buffer);
+ m_vsx.SetByteOrder(vsxregset.GetByteOrder());
+}
+
+size_t RegisterContextCorePOSIX_ppc64le::GetFPRSize() const {
+ return k_num_fpr_registers_ppc64le * sizeof(uint64_t);
+}
+
+size_t RegisterContextCorePOSIX_ppc64le::GetVMXSize() const {
+ return (k_num_vmx_registers_ppc64le - 1) * sizeof(uint64_t) * 2 +
+ sizeof(uint32_t);
+}
+
+size_t RegisterContextCorePOSIX_ppc64le::GetVSXSize() const {
+ return k_num_vsx_registers_ppc64le * sizeof(uint64_t) * 2;
+}
+
+bool RegisterContextCorePOSIX_ppc64le::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ lldb::offset_t offset = reg_info->byte_offset;
+
+ if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ uint64_t v;
+ offset -= GetGPRSize();
+ offset = m_fpr.CopyData(offset, reg_info->byte_size, &v);
+
+ if (offset == reg_info->byte_size) {
+ value.SetBytes(&v, reg_info->byte_size, m_fpr.GetByteOrder());
+ return true;
+ }
+ } else if (IsVMX(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ uint32_t v[4];
+ offset -= GetGPRSize() + GetFPRSize();
+ offset = m_vmx.CopyData(offset, reg_info->byte_size, &v);
+
+ if (offset == reg_info->byte_size) {
+ value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder());
+ return true;
+ }
+ } else if (IsVSX(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ uint32_t v[4];
+ lldb::offset_t tmp_offset;
+ offset -= GetGPRSize() + GetFPRSize() + GetVMXSize();
+
+ if (offset < GetVSXSize() / 2) {
+ tmp_offset = m_vsx.CopyData(offset / 2, reg_info->byte_size / 2, &v);
+
+ if (tmp_offset != reg_info->byte_size / 2) {
+ return false;
+ }
+
+ uint8_t *dst = (uint8_t *)&v + sizeof(uint64_t);
+ tmp_offset = m_fpr.CopyData(offset / 2, reg_info->byte_size / 2, dst);
+
+ if (tmp_offset != reg_info->byte_size / 2) {
+ return false;
+ }
+
+ value.SetBytes(&v, reg_info->byte_size, m_vsx.GetByteOrder());
+ return true;
+ } else {
+ offset =
+ m_vmx.CopyData(offset - GetVSXSize() / 2, reg_info->byte_size, &v);
+ if (offset == reg_info->byte_size) {
+ value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder());
+ return true;
+ }
+ }
+ } else {
+ uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ if (reg_info->byte_size < sizeof(v))
+ value = (uint32_t)v;
+ else
+ value = v;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool RegisterContextCorePOSIX_ppc64le::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.h
new file mode 100644
index 000000000000..6e01d23dd656
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.h
@@ -0,0 +1,48 @@
+//===-- RegisterContextPOSIXCore_ppc64le.h ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextCorePOSIX_ppc64le_h_
+#define liblldb_RegisterContextCorePOSIX_ppc64le_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_ppc64le : public RegisterContextPOSIX_ppc64le {
+public:
+ RegisterContextCorePOSIX_ppc64le(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+protected:
+ size_t GetFPRSize() const;
+
+ size_t GetVMXSize() const;
+
+ size_t GetVSXSize() const;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb::DataBufferSP m_fpr_buffer;
+ lldb::DataBufferSP m_vmx_buffer;
+ lldb::DataBufferSP m_vsx_buffer;
+ lldb_private::DataExtractor m_gpr;
+ lldb_private::DataExtractor m_fpr;
+ lldb_private::DataExtractor m_vmx;
+ lldb_private::DataExtractor m_vsx;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_ppc64le_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp
new file mode 100644
index 000000000000..d84fc3e74395
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp
@@ -0,0 +1,96 @@
+//===-- RegisterContextPOSIXCore_s390x.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextPOSIXCore_s390x.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_s390x::RegisterContextCorePOSIX_s390x(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_s390x(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+
+ DataExtractor fpregset = getRegset(
+ notes, register_info->GetTargetArchitecture().GetTriple(), FPR_Desc);
+ m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(),
+ fpregset.GetByteSize());
+ m_fpr.SetData(m_fpr_buffer);
+ m_fpr.SetByteOrder(fpregset.GetByteOrder());
+}
+
+RegisterContextCorePOSIX_s390x::~RegisterContextCorePOSIX_s390x() {}
+
+bool RegisterContextCorePOSIX_s390x::ReadGPR() { return true; }
+
+bool RegisterContextCorePOSIX_s390x::ReadFPR() { return true; }
+
+bool RegisterContextCorePOSIX_s390x::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM)
+ return false;
+
+ if (IsGPR(reg)) {
+ lldb::offset_t offset = reg_info->byte_offset;
+ uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ value.SetUInt(v, reg_info->byte_size);
+ return true;
+ }
+ }
+
+ if (IsFPR(reg)) {
+ lldb::offset_t offset = reg_info->byte_offset;
+ uint64_t v = m_fpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ value.SetUInt(v, reg_info->byte_size);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h
new file mode 100644
index 000000000000..729617649c4f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h
@@ -0,0 +1,55 @@
+//===-- RegisterContextPOSIXCore_s390x.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextCorePOSIX_s390x_h_
+#define liblldb_RegisterContextCorePOSIX_s390x_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_s390x.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_s390x : public RegisterContextPOSIX_s390x {
+public:
+ RegisterContextCorePOSIX_s390x(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ ~RegisterContextCorePOSIX_s390x() override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb_private::DataExtractor m_gpr;
+
+ lldb::DataBufferSP m_fpr_buffer;
+ lldb_private::DataExtractor m_fpr;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_s390x_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp
new file mode 100644
index 000000000000..4454857e1799
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp
@@ -0,0 +1,99 @@
+//===-- RegisterContextPOSIXCore_x86_64.cpp ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextPOSIXCore_x86_64.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_x86_64::RegisterContextCorePOSIX_x86_64(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_x86(thread, 0, register_info) {
+ size_t size, len;
+
+ size = GetGPRSize();
+ m_gpregset.reset(new uint8_t[size]);
+ len =
+ gpregset.ExtractBytes(0, size, lldb::eByteOrderLittle, m_gpregset.get());
+ if (len != size)
+ m_gpregset.reset();
+
+ DataExtractor fpregset = getRegset(
+ notes, register_info->GetTargetArchitecture().GetTriple(), FPR_Desc);
+ size = sizeof(FXSAVE);
+ m_fpregset.reset(new uint8_t[size]);
+ len =
+ fpregset.ExtractBytes(0, size, lldb::eByteOrderLittle, m_fpregset.get());
+ if (len != size)
+ m_fpregset.reset();
+}
+
+bool RegisterContextCorePOSIX_x86_64::ReadGPR() {
+ return m_gpregset != nullptr;
+}
+
+bool RegisterContextCorePOSIX_x86_64::ReadFPR() {
+ return m_fpregset != nullptr;
+}
+
+bool RegisterContextCorePOSIX_x86_64::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_x86_64::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_x86_64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint8_t *src;
+ size_t offset;
+ const size_t fxsave_offset = reg_info->byte_offset - GetFXSAVEOffset();
+ // make the offset relative to the beginning of the FXSAVE structure because
+ // this is the data that we have (not the entire UserArea)
+
+ if (m_gpregset && reg_info->byte_offset < GetGPRSize()) {
+ src = m_gpregset.get();
+ offset = reg_info->byte_offset;
+ } else if (m_fpregset && fxsave_offset < sizeof(FXSAVE)) {
+ src = m_fpregset.get();
+ offset = fxsave_offset;
+ } else {
+ return false;
+ }
+
+ Status error;
+ value.SetFromMemoryData(reg_info, src + offset, reg_info->byte_size,
+ lldb::eByteOrderLittle, error);
+
+ return error.Success();
+}
+
+bool RegisterContextCorePOSIX_x86_64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_x86_64::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_x86_64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_x86_64::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h
new file mode 100644
index 000000000000..f41991c240d2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h
@@ -0,0 +1,49 @@
+//===-- RegisterContextPOSIXCore_x86_64.h -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextCorePOSIX_x86_64_h_
+#define liblldb_RegisterContextCorePOSIX_x86_64_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_x86.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+
+class RegisterContextCorePOSIX_x86_64 : public RegisterContextPOSIX_x86 {
+public:
+ RegisterContextCorePOSIX_x86_64(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+private:
+ std::unique_ptr<uint8_t[]> m_gpregset;
+ std::unique_ptr<uint8_t[]> m_fpregset;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_x86_64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.cpp
new file mode 100644
index 000000000000..c8829d612b31
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.cpp
@@ -0,0 +1,38 @@
+//===-- RegisterUtilities.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb_private;
+
+static llvm::Optional<uint32_t>
+getNoteType(const llvm::Triple &Triple,
+ llvm::ArrayRef<RegsetDesc> RegsetDescs) {
+ for (const auto &Entry : RegsetDescs) {
+ if (Entry.OS != Triple.getOS())
+ continue;
+ if (Entry.Arch != llvm::Triple::UnknownArch &&
+ Entry.Arch != Triple.getArch())
+ continue;
+ return Entry.Note;
+ }
+ return llvm::None;
+}
+
+DataExtractor lldb_private::getRegset(llvm::ArrayRef<CoreNote> Notes,
+ const llvm::Triple &Triple,
+ llvm::ArrayRef<RegsetDesc> RegsetDescs) {
+ auto TypeOr = getNoteType(Triple, RegsetDescs);
+ if (!TypeOr)
+ return DataExtractor();
+ uint32_t Type = *TypeOr;
+ auto Iter = llvm::find_if(
+ Notes, [Type](const CoreNote &Note) { return Note.info.n_type == Type; });
+ return Iter == Notes.end() ? DataExtractor() : Iter->data;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h
new file mode 100644
index 000000000000..d3b3373150f8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h
@@ -0,0 +1,147 @@
+//===-- RegisterUtilities.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_REGISTERUTILITIES_H
+#define LLDB_REGISTERUTILITIES_H
+
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "lldb/Utility/DataExtractor.h"
+
+namespace lldb_private {
+/// Core files PT_NOTE segment descriptor types
+
+namespace FREEBSD {
+enum {
+ NT_PRSTATUS = 1,
+ NT_FPREGSET,
+ NT_PRPSINFO,
+ NT_THRMISC = 7,
+ NT_PROCSTAT_AUXV = 16,
+ NT_PPC_VMX = 0x100
+};
+}
+
+namespace NETBSD {
+enum { NT_PROCINFO = 1, NT_AUXV = 2 };
+
+/* Size in bytes */
+enum { NT_PROCINFO_SIZE = 160 };
+
+/* Size in bytes */
+enum {
+ NT_PROCINFO_CPI_VERSION_SIZE = 4,
+ NT_PROCINFO_CPI_CPISIZE_SIZE = 4,
+ NT_PROCINFO_CPI_SIGNO_SIZE = 4,
+ NT_PROCINFO_CPI_SIGCODE_SIZE = 4,
+ NT_PROCINFO_CPI_SIGPEND_SIZE = 16,
+ NT_PROCINFO_CPI_SIGMASK_SIZE = 16,
+ NT_PROCINFO_CPI_SIGIGNORE_SIZE = 16,
+ NT_PROCINFO_CPI_SIGCATCH_SIZE = 16,
+ NT_PROCINFO_CPI_PID_SIZE = 4,
+ NT_PROCINFO_CPI_PPID_SIZE = 4,
+ NT_PROCINFO_CPI_PGRP_SIZE = 4,
+ NT_PROCINFO_CPI_SID_SIZE = 4,
+ NT_PROCINFO_CPI_RUID_SIZE = 4,
+ NT_PROCINFO_CPI_EUID_SIZE = 4,
+ NT_PROCINFO_CPI_SVUID_SIZE = 4,
+ NT_PROCINFO_CPI_RGID_SIZE = 4,
+ NT_PROCINFO_CPI_EGID_SIZE = 4,
+ NT_PROCINFO_CPI_SVGID_SIZE = 4,
+ NT_PROCINFO_CPI_NLWPS_SIZE = 4,
+ NT_PROCINFO_CPI_NAME_SIZE = 32,
+ NT_PROCINFO_CPI_SIGLWP_SIZE = 4,
+};
+
+namespace AARCH64 {
+enum { NT_REGS = 32, NT_FPREGS = 34 };
+}
+
+namespace AMD64 {
+enum { NT_REGS = 33, NT_FPREGS = 35 };
+}
+
+} // namespace NETBSD
+
+namespace OPENBSD {
+enum {
+ NT_PROCINFO = 10,
+ NT_AUXV = 11,
+ NT_REGS = 20,
+ NT_FPREGS = 21,
+};
+}
+
+namespace LINUX {
+enum {
+ NT_PRSTATUS = 1,
+ NT_FPREGSET,
+ NT_PRPSINFO,
+ NT_TASKSTRUCT,
+ NT_PLATFORM,
+ NT_AUXV,
+ NT_FILE = 0x46494c45,
+ NT_SIGINFO = 0x53494749,
+ NT_PPC_VMX = 0x100,
+ NT_PPC_VSX = 0x102,
+ NT_PRXFPREG = 0x46e62b7f,
+};
+}
+
+struct CoreNote {
+ ELFNote info;
+ DataExtractor data;
+};
+
+// A structure describing how to find a register set in a core file from a given
+// OS.
+struct RegsetDesc {
+ // OS to which this entry applies to. Must not be UnknownOS.
+ llvm::Triple::OSType OS;
+
+ // Architecture to which this entry applies to. Can be UnknownArch, in which
+ // case it applies to all architectures of a given OS.
+ llvm::Triple::ArchType Arch;
+
+ // The note type under which the register set can be found.
+ uint32_t Note;
+};
+
+// Returns the register set in Notes which corresponds to the specified Triple
+// according to the list of register set descriptions in RegsetDescs. The list
+// is scanned linearly, so you can use a more specific entry (e.g. linux-i386)
+// to override a more general entry (e.g. general linux), as long as you place
+// it earlier in the list. If a register set is not found, it returns an empty
+// DataExtractor.
+DataExtractor getRegset(llvm::ArrayRef<CoreNote> Notes,
+ const llvm::Triple &Triple,
+ llvm::ArrayRef<RegsetDesc> RegsetDescs);
+
+constexpr RegsetDesc FPR_Desc[] = {
+ {llvm::Triple::FreeBSD, llvm::Triple::UnknownArch, FREEBSD::NT_FPREGSET},
+ // In a i386 core file NT_FPREGSET is present, but it's not the result
+ // of the FXSAVE instruction like in 64 bit files.
+ // The result from FXSAVE is in NT_PRXFPREG for i386 core files
+ {llvm::Triple::Linux, llvm::Triple::x86, LINUX::NT_PRXFPREG},
+ {llvm::Triple::Linux, llvm::Triple::UnknownArch, LINUX::NT_FPREGSET},
+ {llvm::Triple::NetBSD, llvm::Triple::aarch64, NETBSD::AARCH64::NT_FPREGS},
+ {llvm::Triple::NetBSD, llvm::Triple::x86_64, NETBSD::AMD64::NT_FPREGS},
+ {llvm::Triple::OpenBSD, llvm::Triple::UnknownArch, OPENBSD::NT_FPREGS},
+};
+
+constexpr RegsetDesc PPC_VMX_Desc[] = {
+ {llvm::Triple::FreeBSD, llvm::Triple::UnknownArch, FREEBSD::NT_PPC_VMX},
+ {llvm::Triple::Linux, llvm::Triple::UnknownArch, LINUX::NT_PPC_VMX},
+};
+
+constexpr RegsetDesc PPC_VSX_Desc[] = {
+ {llvm::Triple::Linux, llvm::Triple::UnknownArch, LINUX::NT_PPC_VSX},
+};
+
+} // namespace lldb_private
+
+#endif // #ifndef LLDB_REGISTERUTILITIES_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
new file mode 100644
index 000000000000..6ae93fd1ec87
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
@@ -0,0 +1,442 @@
+//===-- ThreadElfCore.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_i386.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_mips.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_mips64.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/Process/Utility/RegisterContextLinux_s390x.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h"
+#include "Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h"
+#include "Plugins/Process/Utility/RegisterContextOpenBSD_i386.h"
+#include "Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h"
+#include "ProcessElfCore.h"
+#include "RegisterContextPOSIXCore_arm.h"
+#include "RegisterContextPOSIXCore_arm64.h"
+#include "RegisterContextPOSIXCore_mips64.h"
+#include "RegisterContextPOSIXCore_powerpc.h"
+#include "RegisterContextPOSIXCore_ppc64le.h"
+#ifdef LLDB_ENABLE_ALL
+#include "RegisterContextPOSIXCore_s390x.h"
+#endif // LLDB_ENABLE_ALL
+#include "RegisterContextPOSIXCore_x86_64.h"
+#include "ThreadElfCore.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Construct a Thread object with given data
+ThreadElfCore::ThreadElfCore(Process &process, const ThreadData &td)
+ : Thread(process, td.tid), m_thread_name(td.name), m_thread_reg_ctx_sp(),
+ m_signo(td.signo), m_gpregset_data(td.gpregset), m_notes(td.notes) {}
+
+ThreadElfCore::~ThreadElfCore() { DestroyThread(); }
+
+void ThreadElfCore::RefreshStateAfterStop() {
+ GetRegisterContext()->InvalidateIfNeeded(false);
+}
+
+RegisterContextSP ThreadElfCore::GetRegisterContext() {
+ if (!m_reg_context_sp) {
+ m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
+ }
+ return m_reg_context_sp;
+}
+
+RegisterContextSP
+ThreadElfCore::CreateRegisterContextForFrame(StackFrame *frame) {
+ RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+ if (concrete_frame_idx == 0) {
+ if (m_thread_reg_ctx_sp)
+ return m_thread_reg_ctx_sp;
+
+ ProcessElfCore *process = static_cast<ProcessElfCore *>(GetProcess().get());
+ ArchSpec arch = process->GetArchitecture();
+ RegisterInfoInterface *reg_interface = nullptr;
+
+ switch (arch.GetTriple().getOS()) {
+ case llvm::Triple::FreeBSD: {
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ reg_interface = new RegisterInfoPOSIX_arm64(arch);
+ break;
+ case llvm::Triple::arm:
+ reg_interface = new RegisterInfoPOSIX_arm(arch);
+ break;
+ case llvm::Triple::ppc:
+ reg_interface = new RegisterContextFreeBSD_powerpc32(arch);
+ break;
+ case llvm::Triple::ppc64:
+ reg_interface = new RegisterContextFreeBSD_powerpc64(arch);
+ break;
+ case llvm::Triple::mips64:
+ reg_interface = new RegisterContextFreeBSD_mips64(arch);
+ break;
+ case llvm::Triple::x86:
+ reg_interface = new RegisterContextFreeBSD_i386(arch);
+ break;
+ case llvm::Triple::x86_64:
+ reg_interface = new RegisterContextFreeBSD_x86_64(arch);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case llvm::Triple::NetBSD: {
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ reg_interface = new RegisterInfoPOSIX_arm64(arch);
+ break;
+ case llvm::Triple::x86_64:
+ reg_interface = new RegisterContextNetBSD_x86_64(arch);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case llvm::Triple::Linux: {
+ switch (arch.GetMachine()) {
+ case llvm::Triple::arm:
+ reg_interface = new RegisterInfoPOSIX_arm(arch);
+ break;
+ case llvm::Triple::aarch64:
+ reg_interface = new RegisterInfoPOSIX_arm64(arch);
+ break;
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips:
+ reg_interface = new RegisterContextLinux_mips(arch);
+ break;
+ case llvm::Triple::mips64el:
+ case llvm::Triple::mips64:
+ reg_interface = new RegisterContextLinux_mips64(arch);
+ break;
+ case llvm::Triple::ppc64le:
+ reg_interface = new RegisterInfoPOSIX_ppc64le(arch);
+ break;
+#ifdef LLDB_ENABLE_ALL
+ case llvm::Triple::systemz:
+ reg_interface = new RegisterContextLinux_s390x(arch);
+ break;
+#endif // LLDB_ENABLE_ALL
+ case llvm::Triple::x86:
+ reg_interface = new RegisterContextLinux_i386(arch);
+ break;
+ case llvm::Triple::x86_64:
+ reg_interface = new RegisterContextLinux_x86_64(arch);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case llvm::Triple::OpenBSD: {
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ reg_interface = new RegisterInfoPOSIX_arm64(arch);
+ break;
+ case llvm::Triple::arm:
+ reg_interface = new RegisterInfoPOSIX_arm(arch);
+ break;
+ case llvm::Triple::x86:
+ reg_interface = new RegisterContextOpenBSD_i386(arch);
+ break;
+ case llvm::Triple::x86_64:
+ reg_interface = new RegisterContextOpenBSD_x86_64(arch);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (!reg_interface) {
+ if (log)
+ log->Printf("elf-core::%s:: Architecture(%d) or OS(%d) not supported",
+ __FUNCTION__, arch.GetMachine(), arch.GetTriple().getOS());
+ assert(false && "Architecture or OS not supported");
+ }
+
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_arm64>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ case llvm::Triple::arm:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_arm>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_mips64>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_mips64>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_powerpc>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ case llvm::Triple::ppc64le:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_ppc64le>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+#ifdef LLDB_ENABLE_ALL
+ case llvm::Triple::systemz:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_s390x>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+#endif // LLDB_ENABLE_ALL
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ default:
+ break;
+ }
+
+ reg_ctx_sp = m_thread_reg_ctx_sp;
+ } else {
+ Unwind *unwinder = GetUnwinder();
+ if (unwinder != nullptr)
+ reg_ctx_sp = unwinder->CreateRegisterContextForFrame(frame);
+ }
+ return reg_ctx_sp;
+}
+
+bool ThreadElfCore::CalculateStopInfo() {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, m_signo));
+ return true;
+ }
+ return false;
+}
+
+// Parse PRSTATUS from NOTE entry
+ELFLinuxPrStatus::ELFLinuxPrStatus() {
+ memset(this, 0, sizeof(ELFLinuxPrStatus));
+}
+
+size_t ELFLinuxPrStatus::GetSize(const lldb_private::ArchSpec &arch) {
+ constexpr size_t mips_linux_pr_status_size_o32 = 96;
+ constexpr size_t mips_linux_pr_status_size_n32 = 72;
+ constexpr size_t num_ptr_size_members = 10;
+ if (arch.IsMIPS()) {
+ std::string abi = arch.GetTargetABI();
+ assert(!abi.empty() && "ABI is not set");
+ if (!abi.compare("n64"))
+ return sizeof(ELFLinuxPrStatus);
+ else if (!abi.compare("o32"))
+ return mips_linux_pr_status_size_o32;
+ // N32 ABI
+ return mips_linux_pr_status_size_n32;
+ }
+ switch (arch.GetCore()) {
+ case lldb_private::ArchSpec::eCore_x86_32_i386:
+ case lldb_private::ArchSpec::eCore_x86_32_i486:
+ return 72;
+ default:
+ if (arch.GetAddressByteSize() == 8)
+ return sizeof(ELFLinuxPrStatus);
+ else
+ return sizeof(ELFLinuxPrStatus) - num_ptr_size_members * 4;
+ }
+}
+
+Status ELFLinuxPrStatus::Parse(const DataExtractor &data,
+ const ArchSpec &arch) {
+ Status error;
+ if (GetSize(arch) > data.GetByteSize()) {
+ error.SetErrorStringWithFormat(
+ "NT_PRSTATUS size should be %zu, but the remaining bytes are: %" PRIu64,
+ GetSize(arch), data.GetByteSize());
+ return error;
+ }
+
+ // Read field by field to correctly account for endianess of both the core
+ // dump and the platform running lldb.
+ offset_t offset = 0;
+ si_signo = data.GetU32(&offset);
+ si_code = data.GetU32(&offset);
+ si_errno = data.GetU32(&offset);
+
+ pr_cursig = data.GetU16(&offset);
+ offset += 2; // pad
+
+ pr_sigpend = data.GetPointer(&offset);
+ pr_sighold = data.GetPointer(&offset);
+
+ pr_pid = data.GetU32(&offset);
+ pr_ppid = data.GetU32(&offset);
+ pr_pgrp = data.GetU32(&offset);
+ pr_sid = data.GetU32(&offset);
+
+ pr_utime.tv_sec = data.GetPointer(&offset);
+ pr_utime.tv_usec = data.GetPointer(&offset);
+
+ pr_stime.tv_sec = data.GetPointer(&offset);
+ pr_stime.tv_usec = data.GetPointer(&offset);
+
+ pr_cutime.tv_sec = data.GetPointer(&offset);
+ pr_cutime.tv_usec = data.GetPointer(&offset);
+
+ pr_cstime.tv_sec = data.GetPointer(&offset);
+ pr_cstime.tv_usec = data.GetPointer(&offset);
+
+ return error;
+}
+
+// Parse PRPSINFO from NOTE entry
+ELFLinuxPrPsInfo::ELFLinuxPrPsInfo() {
+ memset(this, 0, sizeof(ELFLinuxPrPsInfo));
+}
+
+size_t ELFLinuxPrPsInfo::GetSize(const lldb_private::ArchSpec &arch) {
+ constexpr size_t mips_linux_pr_psinfo_size_o32_n32 = 128;
+ if (arch.IsMIPS()) {
+ uint8_t address_byte_size = arch.GetAddressByteSize();
+ if (address_byte_size == 8)
+ return sizeof(ELFLinuxPrPsInfo);
+ return mips_linux_pr_psinfo_size_o32_n32;
+ }
+
+ switch (arch.GetCore()) {
+ case lldb_private::ArchSpec::eCore_s390x_generic:
+ case lldb_private::ArchSpec::eCore_x86_64_x86_64:
+ return sizeof(ELFLinuxPrPsInfo);
+ case lldb_private::ArchSpec::eCore_x86_32_i386:
+ case lldb_private::ArchSpec::eCore_x86_32_i486:
+ return 124;
+ default:
+ return 0;
+ }
+}
+
+Status ELFLinuxPrPsInfo::Parse(const DataExtractor &data,
+ const ArchSpec &arch) {
+ Status error;
+ ByteOrder byteorder = data.GetByteOrder();
+ if (GetSize(arch) > data.GetByteSize()) {
+ error.SetErrorStringWithFormat(
+ "NT_PRPSINFO size should be %zu, but the remaining bytes are: %" PRIu64,
+ GetSize(arch), data.GetByteSize());
+ return error;
+ }
+ size_t size = 0;
+ offset_t offset = 0;
+
+ pr_state = data.GetU8(&offset);
+ pr_sname = data.GetU8(&offset);
+ pr_zomb = data.GetU8(&offset);
+ pr_nice = data.GetU8(&offset);
+ if (data.GetAddressByteSize() == 8) {
+ // Word align the next field on 64 bit.
+ offset += 4;
+ }
+
+ pr_flag = data.GetPointer(&offset);
+
+ if (arch.IsMIPS()) {
+ // The pr_uid and pr_gid is always 32 bit irrespective of platforms
+ pr_uid = data.GetU32(&offset);
+ pr_gid = data.GetU32(&offset);
+ } else {
+ // 16 bit on 32 bit platforms, 32 bit on 64 bit platforms
+ pr_uid = data.GetMaxU64(&offset, data.GetAddressByteSize() >> 1);
+ pr_gid = data.GetMaxU64(&offset, data.GetAddressByteSize() >> 1);
+ }
+
+ pr_pid = data.GetU32(&offset);
+ pr_ppid = data.GetU32(&offset);
+ pr_pgrp = data.GetU32(&offset);
+ pr_sid = data.GetU32(&offset);
+
+ size = 16;
+ data.ExtractBytes(offset, size, byteorder, pr_fname);
+ offset += size;
+
+ size = 80;
+ data.ExtractBytes(offset, size, byteorder, pr_psargs);
+ offset += size;
+
+ return error;
+}
+
+// Parse SIGINFO from NOTE entry
+ELFLinuxSigInfo::ELFLinuxSigInfo() { memset(this, 0, sizeof(ELFLinuxSigInfo)); }
+
+size_t ELFLinuxSigInfo::GetSize(const lldb_private::ArchSpec &arch) {
+ if (arch.IsMIPS())
+ return sizeof(ELFLinuxSigInfo);
+ switch (arch.GetCore()) {
+ case lldb_private::ArchSpec::eCore_x86_64_x86_64:
+ return sizeof(ELFLinuxSigInfo);
+ case lldb_private::ArchSpec::eCore_s390x_generic:
+ case lldb_private::ArchSpec::eCore_x86_32_i386:
+ case lldb_private::ArchSpec::eCore_x86_32_i486:
+ return 12;
+ default:
+ return 0;
+ }
+}
+
+Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch) {
+ Status error;
+ if (GetSize(arch) > data.GetByteSize()) {
+ error.SetErrorStringWithFormat(
+ "NT_SIGINFO size should be %zu, but the remaining bytes are: %" PRIu64,
+ GetSize(arch), data.GetByteSize());
+ return error;
+ }
+
+ // Parsing from a 32 bit ELF core file, and populating/reusing the structure
+ // properly, because the struct is for the 64 bit version
+ offset_t offset = 0;
+ si_signo = data.GetU32(&offset);
+ si_code = data.GetU32(&offset);
+ si_errno = data.GetU32(&offset);
+
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
new file mode 100644
index 000000000000..ddcf35013b34
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
@@ -0,0 +1,176 @@
+//===-- ThreadElfCore.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadElfCore_h_
+#define liblldb_ThreadElfCore_h_
+
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "llvm/ADT/DenseMap.h"
+#include <string>
+
+struct compat_timeval {
+ alignas(8) uint64_t tv_sec;
+ alignas(8) uint64_t tv_usec;
+};
+
+// PRSTATUS structure's size differs based on architecture.
+// This is the layout in the x86-64 arch.
+// In the i386 case we parse it manually and fill it again
+// in the same structure
+// The gp registers are also a part of this struct, but they are handled
+// separately
+
+#undef si_signo
+#undef si_code
+#undef si_errno
+
+struct ELFLinuxPrStatus {
+ int32_t si_signo;
+ int32_t si_code;
+ int32_t si_errno;
+
+ int16_t pr_cursig;
+
+ alignas(8) uint64_t pr_sigpend;
+ alignas(8) uint64_t pr_sighold;
+
+ uint32_t pr_pid;
+ uint32_t pr_ppid;
+ uint32_t pr_pgrp;
+ uint32_t pr_sid;
+
+ compat_timeval pr_utime;
+ compat_timeval pr_stime;
+ compat_timeval pr_cutime;
+ compat_timeval pr_cstime;
+
+ ELFLinuxPrStatus();
+
+ lldb_private::Status Parse(const lldb_private::DataExtractor &data,
+ const lldb_private::ArchSpec &arch);
+
+ // Return the bytesize of the structure
+ // 64 bit - just sizeof
+ // 32 bit - hardcoded because we are reusing the struct, but some of the
+ // members are smaller -
+ // so the layout is not the same
+ static size_t GetSize(const lldb_private::ArchSpec &arch);
+};
+
+static_assert(sizeof(ELFLinuxPrStatus) == 112,
+ "sizeof ELFLinuxPrStatus is not correct!");
+
+struct ELFLinuxSigInfo {
+ int32_t si_signo;
+ int32_t si_code;
+ int32_t si_errno;
+
+ ELFLinuxSigInfo();
+
+ lldb_private::Status Parse(const lldb_private::DataExtractor &data,
+ const lldb_private::ArchSpec &arch);
+
+ // Return the bytesize of the structure
+ // 64 bit - just sizeof
+ // 32 bit - hardcoded because we are reusing the struct, but some of the
+ // members are smaller -
+ // so the layout is not the same
+ static size_t GetSize(const lldb_private::ArchSpec &arch);
+};
+
+static_assert(sizeof(ELFLinuxSigInfo) == 12,
+ "sizeof ELFLinuxSigInfo is not correct!");
+
+// PRPSINFO structure's size differs based on architecture.
+// This is the layout in the x86-64 arch case.
+// In the i386 case we parse it manually and fill it again
+// in the same structure
+struct ELFLinuxPrPsInfo {
+ char pr_state;
+ char pr_sname;
+ char pr_zomb;
+ char pr_nice;
+ alignas(8) uint64_t pr_flag;
+ uint32_t pr_uid;
+ uint32_t pr_gid;
+ int32_t pr_pid;
+ int32_t pr_ppid;
+ int32_t pr_pgrp;
+ int32_t pr_sid;
+ char pr_fname[16];
+ char pr_psargs[80];
+
+ ELFLinuxPrPsInfo();
+
+ lldb_private::Status Parse(const lldb_private::DataExtractor &data,
+ const lldb_private::ArchSpec &arch);
+
+ // Return the bytesize of the structure
+ // 64 bit - just sizeof
+ // 32 bit - hardcoded because we are reusing the struct, but some of the
+ // members are smaller -
+ // so the layout is not the same
+ static size_t GetSize(const lldb_private::ArchSpec &arch);
+};
+
+static_assert(sizeof(ELFLinuxPrPsInfo) == 136,
+ "sizeof ELFLinuxPrPsInfo is not correct!");
+
+struct ThreadData {
+ lldb_private::DataExtractor gpregset;
+ std::vector<lldb_private::CoreNote> notes;
+ lldb::tid_t tid;
+ int signo = 0;
+ int prstatus_sig = 0;
+ std::string name;
+};
+
+class ThreadElfCore : public lldb_private::Thread {
+public:
+ ThreadElfCore(lldb_private::Process &process, const ThreadData &td);
+
+ ~ThreadElfCore() override;
+
+ void RefreshStateAfterStop() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+
+ static bool ThreadIDIsValid(lldb::tid_t thread) { return thread != 0; }
+
+ const char *GetName() override {
+ if (m_thread_name.empty())
+ return nullptr;
+ return m_thread_name.c_str();
+ }
+
+ void SetName(const char *name) override {
+ if (name && name[0])
+ m_thread_name.assign(name);
+ else
+ m_thread_name.clear();
+ }
+
+protected:
+ // Member variables.
+ std::string m_thread_name;
+ lldb::RegisterContextSP m_thread_reg_ctx_sp;
+
+ int m_signo;
+
+ lldb_private::DataExtractor m_gpregset_data;
+ std::vector<lldb_private::CoreNote> m_notes;
+
+ bool CalculateStopInfo() override;
+};
+
+#endif // liblldb_ThreadElfCore_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
new file mode 100644
index 000000000000..fe7ef6b3acea
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
@@ -0,0 +1,394 @@
+//===-- GDBRemoteClientBase.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteClientBase.h"
+
+#include "llvm/ADT/StringExtras.h"
+
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/LLDBAssert.h"
+
+#include "ProcessGDBRemoteLog.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+using namespace std::chrono;
+
+static const seconds kInterruptTimeout(5);
+
+/////////////////////////
+// GDBRemoteClientBase //
+/////////////////////////
+
+GDBRemoteClientBase::ContinueDelegate::~ContinueDelegate() = default;
+
+GDBRemoteClientBase::GDBRemoteClientBase(const char *comm_name,
+ const char *listener_name)
+ : GDBRemoteCommunication(comm_name, listener_name), m_async_count(0),
+ m_is_running(false), m_should_stop(false) {}
+
+StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse(
+ ContinueDelegate &delegate, const UnixSignals &signals,
+ llvm::StringRef payload, StringExtractorGDBRemote &response) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ response.Clear();
+
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_continue_packet = payload;
+ m_should_stop = false;
+ }
+ ContinueLock cont_lock(*this);
+ if (!cont_lock)
+ return eStateInvalid;
+ OnRunPacketSent(true);
+
+ for (;;) {
+ PacketResult read_result = ReadPacket(response, kInterruptTimeout, false);
+ switch (read_result) {
+ case PacketResult::ErrorReplyTimeout: {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ if (m_async_count == 0)
+ continue;
+ if (steady_clock::now() >= m_interrupt_time + kInterruptTimeout)
+ return eStateInvalid;
+ break;
+ }
+ case PacketResult::Success:
+ break;
+ default:
+ if (log)
+ log->Printf("GDBRemoteClientBase::%s () ReadPacket(...) => false",
+ __FUNCTION__);
+ return eStateInvalid;
+ }
+ if (response.Empty())
+ return eStateInvalid;
+
+ const char stop_type = response.GetChar();
+ if (log)
+ log->Printf("GDBRemoteClientBase::%s () got packet: %s", __FUNCTION__,
+ response.GetStringRef().c_str());
+
+ switch (stop_type) {
+ case 'W':
+ case 'X':
+ return eStateExited;
+ case 'E':
+ // ERROR
+ return eStateInvalid;
+ default:
+ if (log)
+ log->Printf("GDBRemoteClientBase::%s () unrecognized async packet",
+ __FUNCTION__);
+ return eStateInvalid;
+ case 'O': {
+ std::string inferior_stdout;
+ response.GetHexByteString(inferior_stdout);
+ delegate.HandleAsyncStdout(inferior_stdout);
+ break;
+ }
+ case 'A':
+ delegate.HandleAsyncMisc(
+ llvm::StringRef(response.GetStringRef()).substr(1));
+ break;
+ case 'J':
+ delegate.HandleAsyncStructuredDataPacket(response.GetStringRef());
+ break;
+ case 'T':
+ case 'S':
+ // Do this with the continue lock held.
+ const bool should_stop = ShouldStop(signals, response);
+ response.SetFilePos(0);
+
+ // The packet we should resume with. In the future we should check our
+ // thread list and "do the right thing" for new threads that show up
+ // while we stop and run async packets. Setting the packet to 'c' to
+ // continue all threads is the right thing to do 99.99% of the time
+ // because if a thread was single stepping, and we sent an interrupt, we
+ // will notice above that we didn't stop due to an interrupt but stopped
+ // due to stepping and we would _not_ continue. This packet may get
+ // modified by the async actions (e.g. to send a signal).
+ m_continue_packet = 'c';
+ cont_lock.unlock();
+
+ delegate.HandleStopReply();
+ if (should_stop)
+ return eStateStopped;
+
+ switch (cont_lock.lock()) {
+ case ContinueLock::LockResult::Success:
+ break;
+ case ContinueLock::LockResult::Failed:
+ return eStateInvalid;
+ case ContinueLock::LockResult::Cancelled:
+ return eStateStopped;
+ }
+ OnRunPacketSent(false);
+ break;
+ }
+ }
+}
+
+bool GDBRemoteClientBase::SendAsyncSignal(int signo) {
+ Lock lock(*this, true);
+ if (!lock || !lock.DidInterrupt())
+ return false;
+
+ m_continue_packet = 'C';
+ m_continue_packet += llvm::hexdigit((signo / 16) % 16);
+ m_continue_packet += llvm::hexdigit(signo % 16);
+ return true;
+}
+
+bool GDBRemoteClientBase::Interrupt() {
+ Lock lock(*this, true);
+ if (!lock.DidInterrupt())
+ return false;
+ m_should_stop = true;
+ return true;
+}
+GDBRemoteCommunication::PacketResult
+GDBRemoteClientBase::SendPacketAndWaitForResponse(
+ llvm::StringRef payload, StringExtractorGDBRemote &response,
+ bool send_async) {
+ Lock lock(*this, send_async);
+ if (!lock) {
+ if (Log *log =
+ ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS))
+ log->Printf("GDBRemoteClientBase::%s failed to get mutex, not sending "
+ "packet '%.*s' (send_async=%d)",
+ __FUNCTION__, int(payload.size()), payload.data(),
+ send_async);
+ return PacketResult::ErrorSendFailed;
+ }
+
+ return SendPacketAndWaitForResponseNoLock(payload, response);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteClientBase::SendPacketAndReceiveResponseWithOutputSupport(
+ llvm::StringRef payload, StringExtractorGDBRemote &response,
+ bool send_async,
+ llvm::function_ref<void(llvm::StringRef)> output_callback) {
+ Lock lock(*this, send_async);
+ if (!lock) {
+ if (Log *log =
+ ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS))
+ log->Printf("GDBRemoteClientBase::%s failed to get mutex, not sending "
+ "packet '%.*s' (send_async=%d)",
+ __FUNCTION__, int(payload.size()), payload.data(),
+ send_async);
+ return PacketResult::ErrorSendFailed;
+ }
+
+ PacketResult packet_result = SendPacketNoLock(payload);
+ if (packet_result != PacketResult::Success)
+ return packet_result;
+
+ return ReadPacketWithOutputSupport(response, GetPacketTimeout(), true,
+ output_callback);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteClientBase::SendPacketAndWaitForResponseNoLock(
+ llvm::StringRef payload, StringExtractorGDBRemote &response) {
+ PacketResult packet_result = SendPacketNoLock(payload);
+ if (packet_result != PacketResult::Success)
+ return packet_result;
+
+ const size_t max_response_retries = 3;
+ for (size_t i = 0; i < max_response_retries; ++i) {
+ packet_result = ReadPacket(response, GetPacketTimeout(), true);
+ // Make sure we received a response
+ if (packet_result != PacketResult::Success)
+ return packet_result;
+ // Make sure our response is valid for the payload that was sent
+ if (response.ValidateResponse())
+ return packet_result;
+ // Response says it wasn't valid
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS);
+ if (log)
+ log->Printf(
+ "error: packet with payload \"%.*s\" got invalid response \"%s\": %s",
+ int(payload.size()), payload.data(), response.GetStringRef().c_str(),
+ (i == (max_response_retries - 1))
+ ? "using invalid response and giving up"
+ : "ignoring response and waiting for another");
+ }
+ return packet_result;
+}
+
+bool GDBRemoteClientBase::SendvContPacket(llvm::StringRef payload,
+ StringExtractorGDBRemote &response) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationClient::%s ()", __FUNCTION__);
+
+ // we want to lock down packet sending while we continue
+ Lock lock(*this, true);
+
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationClient::%s () sending vCont packet: %.*s",
+ __FUNCTION__, int(payload.size()), payload.data());
+
+ if (SendPacketNoLock(payload) != PacketResult::Success)
+ return false;
+
+ OnRunPacketSent(true);
+
+ // wait for the response to the vCont
+ if (ReadPacket(response, llvm::None, false) == PacketResult::Success) {
+ if (response.IsOKResponse())
+ return true;
+ }
+
+ return false;
+}
+bool GDBRemoteClientBase::ShouldStop(const UnixSignals &signals,
+ StringExtractorGDBRemote &response) {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ if (m_async_count == 0)
+ return true; // We were not interrupted. The process stopped on its own.
+
+ // Older debugserver stubs (before April 2016) can return two stop-reply
+ // packets in response to a ^C packet. Additionally, all debugservers still
+ // return two stop replies if the inferior stops due to some other reason
+ // before the remote stub manages to interrupt it. We need to wait for this
+ // additional packet to make sure the packet sequence does not get skewed.
+ StringExtractorGDBRemote extra_stop_reply_packet;
+ ReadPacket(extra_stop_reply_packet, milliseconds(100), false);
+
+ // Interrupting is typically done using SIGSTOP or SIGINT, so if the process
+ // stops with some other signal, we definitely want to stop.
+ const uint8_t signo = response.GetHexU8(UINT8_MAX);
+ if (signo != signals.GetSignalNumberFromName("SIGSTOP") &&
+ signo != signals.GetSignalNumberFromName("SIGINT"))
+ return true;
+
+ // We probably only stopped to perform some async processing, so continue
+ // after that is done.
+ // TODO: This is not 100% correct, as the process may have been stopped with
+ // SIGINT or SIGSTOP that was not caused by us (e.g. raise(SIGINT)). This will
+ // normally cause a stop, but if it's done concurrently with a async
+ // interrupt, that stop will get eaten (llvm.org/pr20231).
+ return false;
+}
+
+void GDBRemoteClientBase::OnRunPacketSent(bool first) {
+ if (first)
+ BroadcastEvent(eBroadcastBitRunPacketSent, nullptr);
+}
+
+///////////////////////////////////////
+// GDBRemoteClientBase::ContinueLock //
+///////////////////////////////////////
+
+GDBRemoteClientBase::ContinueLock::ContinueLock(GDBRemoteClientBase &comm)
+ : m_comm(comm), m_acquired(false) {
+ lock();
+}
+
+GDBRemoteClientBase::ContinueLock::~ContinueLock() {
+ if (m_acquired)
+ unlock();
+}
+
+void GDBRemoteClientBase::ContinueLock::unlock() {
+ lldbassert(m_acquired);
+ {
+ std::unique_lock<std::mutex> lock(m_comm.m_mutex);
+ m_comm.m_is_running = false;
+ }
+ m_comm.m_cv.notify_all();
+ m_acquired = false;
+}
+
+GDBRemoteClientBase::ContinueLock::LockResult
+GDBRemoteClientBase::ContinueLock::lock() {
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS);
+ if (log)
+ log->Printf("GDBRemoteClientBase::ContinueLock::%s() resuming with %s",
+ __FUNCTION__, m_comm.m_continue_packet.c_str());
+
+ lldbassert(!m_acquired);
+ std::unique_lock<std::mutex> lock(m_comm.m_mutex);
+ m_comm.m_cv.wait(lock, [this] { return m_comm.m_async_count == 0; });
+ if (m_comm.m_should_stop) {
+ m_comm.m_should_stop = false;
+ if (log)
+ log->Printf("GDBRemoteClientBase::ContinueLock::%s() cancelled",
+ __FUNCTION__);
+ return LockResult::Cancelled;
+ }
+ if (m_comm.SendPacketNoLock(m_comm.m_continue_packet) !=
+ PacketResult::Success)
+ return LockResult::Failed;
+
+ lldbassert(!m_comm.m_is_running);
+ m_comm.m_is_running = true;
+ m_acquired = true;
+ return LockResult::Success;
+}
+
+///////////////////////////////
+// GDBRemoteClientBase::Lock //
+///////////////////////////////
+
+GDBRemoteClientBase::Lock::Lock(GDBRemoteClientBase &comm, bool interrupt)
+ : m_async_lock(comm.m_async_mutex, std::defer_lock), m_comm(comm),
+ m_acquired(false), m_did_interrupt(false) {
+ SyncWithContinueThread(interrupt);
+ if (m_acquired)
+ m_async_lock.lock();
+}
+
+void GDBRemoteClientBase::Lock::SyncWithContinueThread(bool interrupt) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ std::unique_lock<std::mutex> lock(m_comm.m_mutex);
+ if (m_comm.m_is_running && !interrupt)
+ return; // We were asked to avoid interrupting the sender. Lock is not
+ // acquired.
+
+ ++m_comm.m_async_count;
+ if (m_comm.m_is_running) {
+ if (m_comm.m_async_count == 1) {
+ // The sender has sent the continue packet and we are the first async
+ // packet. Let's interrupt it.
+ const char ctrl_c = '\x03';
+ ConnectionStatus status = eConnectionStatusSuccess;
+ size_t bytes_written = m_comm.Write(&ctrl_c, 1, status, nullptr);
+ if (bytes_written == 0) {
+ --m_comm.m_async_count;
+ if (log)
+ log->Printf("GDBRemoteClientBase::Lock::Lock failed to send "
+ "interrupt packet");
+ return;
+ }
+ if (log)
+ log->PutCString("GDBRemoteClientBase::Lock::Lock sent packet: \\x03");
+ m_comm.m_interrupt_time = steady_clock::now();
+ }
+ m_comm.m_cv.wait(lock, [this] { return !m_comm.m_is_running; });
+ m_did_interrupt = true;
+ }
+ m_acquired = true;
+}
+
+GDBRemoteClientBase::Lock::~Lock() {
+ if (!m_acquired)
+ return;
+ {
+ std::unique_lock<std::mutex> lock(m_comm.m_mutex);
+ --m_comm.m_async_count;
+ }
+ m_comm.m_cv.notify_one();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
new file mode 100644
index 000000000000..54f69e8caac6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
@@ -0,0 +1,148 @@
+//===-- GDBRemoteClientBase.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteClientBase_h_
+#define liblldb_GDBRemoteClientBase_h_
+
+#include "GDBRemoteCommunication.h"
+
+#include <condition_variable>
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class GDBRemoteClientBase : public GDBRemoteCommunication {
+public:
+ struct ContinueDelegate {
+ virtual ~ContinueDelegate();
+ virtual void HandleAsyncStdout(llvm::StringRef out) = 0;
+ virtual void HandleAsyncMisc(llvm::StringRef data) = 0;
+ virtual void HandleStopReply() = 0;
+
+ // =========================================================================
+ /// Process asynchronously-received structured data.
+ ///
+ /// \param[in] data
+ /// The complete data packet, expected to start with JSON-async.
+ // =========================================================================
+ virtual void HandleAsyncStructuredDataPacket(llvm::StringRef data) = 0;
+ };
+
+ GDBRemoteClientBase(const char *comm_name, const char *listener_name);
+
+ bool SendAsyncSignal(int signo);
+
+ bool Interrupt();
+
+ lldb::StateType SendContinuePacketAndWaitForResponse(
+ ContinueDelegate &delegate, const UnixSignals &signals,
+ llvm::StringRef payload, StringExtractorGDBRemote &response);
+
+ PacketResult SendPacketAndWaitForResponse(llvm::StringRef payload,
+ StringExtractorGDBRemote &response,
+ bool send_async);
+
+ PacketResult SendPacketAndReceiveResponseWithOutputSupport(
+ llvm::StringRef payload, StringExtractorGDBRemote &response,
+ bool send_async,
+ llvm::function_ref<void(llvm::StringRef)> output_callback);
+
+ bool SendvContPacket(llvm::StringRef payload,
+ StringExtractorGDBRemote &response);
+
+ class Lock {
+ public:
+ Lock(GDBRemoteClientBase &comm, bool interrupt);
+ ~Lock();
+
+ explicit operator bool() { return m_acquired; }
+
+ // Whether we had to interrupt the continue thread to acquire the
+ // connection.
+ bool DidInterrupt() const { return m_did_interrupt; }
+
+ private:
+ std::unique_lock<std::recursive_mutex> m_async_lock;
+ GDBRemoteClientBase &m_comm;
+ bool m_acquired;
+ bool m_did_interrupt;
+
+ void SyncWithContinueThread(bool interrupt);
+ };
+
+protected:
+ PacketResult
+ SendPacketAndWaitForResponseNoLock(llvm::StringRef payload,
+ StringExtractorGDBRemote &response);
+
+ virtual void OnRunPacketSent(bool first);
+
+private:
+ // Variables handling synchronization between the Continue thread and any
+ // other threads
+ // wishing to send packets over the connection. Either the continue thread has
+ // control over
+ // the connection (m_is_running == true) or the connection is free for an
+ // arbitrary number of
+ // other senders to take which indicate their interest by incrementing
+ // m_async_count.
+ // Semantics of individual states:
+ // - m_continue_packet == false, m_async_count == 0: connection is free
+ // - m_continue_packet == true, m_async_count == 0: only continue thread is
+ // present
+ // - m_continue_packet == true, m_async_count > 0: continue thread has
+ // control, async threads
+ // should interrupt it and wait for it to set m_continue_packet to false
+ // - m_continue_packet == false, m_async_count > 0: async threads have
+ // control, continue
+ // thread needs to wait for them to finish (m_async_count goes down to 0).
+ std::mutex m_mutex;
+ std::condition_variable m_cv;
+ // Packet with which to resume after an async interrupt. Can be changed by an
+ // async thread
+ // e.g. to inject a signal.
+ std::string m_continue_packet;
+ // When was the interrupt packet sent. Used to make sure we time out if the
+ // stub does not
+ // respond to interrupt requests.
+ std::chrono::time_point<std::chrono::steady_clock> m_interrupt_time;
+ uint32_t m_async_count;
+ bool m_is_running;
+ bool m_should_stop; // Whether we should resume after a stop.
+ // end of continue thread synchronization block
+
+ // This handles the synchronization between individual async threads. For now
+ // they just use a
+ // simple mutex.
+ std::recursive_mutex m_async_mutex;
+
+ bool ShouldStop(const UnixSignals &signals,
+ StringExtractorGDBRemote &response);
+
+ class ContinueLock {
+ public:
+ enum class LockResult { Success, Cancelled, Failed };
+
+ explicit ContinueLock(GDBRemoteClientBase &comm);
+ ~ContinueLock();
+ explicit operator bool() { return m_acquired; }
+
+ LockResult lock();
+
+ void unlock();
+
+ private:
+ GDBRemoteClientBase &m_comm;
+ bool m_acquired;
+ };
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationClient_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
new file mode 100644
index 000000000000..11052eff948f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -0,0 +1,1398 @@
+//===-- GDBRemoteCommunication.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteCommunication.h"
+
+#include <future>
+#include <limits.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+#include "ProcessGDBRemoteLog.h"
+
+#if defined(__APPLE__)
+#define DEBUGSERVER_BASENAME "debugserver"
+#else
+#define DEBUGSERVER_BASENAME "lldb-server"
+#endif
+
+#if defined(HAVE_LIBCOMPRESSION)
+#include <compression.h>
+#endif
+
+#if defined(HAVE_LIBZ)
+#include <zlib.h>
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+// GDBRemoteCommunication constructor
+GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name,
+ const char *listener_name)
+ : Communication(comm_name),
+#ifdef LLDB_CONFIGURATION_DEBUG
+ m_packet_timeout(1000),
+#else
+ m_packet_timeout(1),
+#endif
+ m_echo_number(0), m_supports_qEcho(eLazyBoolCalculate), m_history(512),
+ m_send_acks(true), m_compression_type(CompressionType::None),
+ m_listen_url() {
+}
+
+// Destructor
+GDBRemoteCommunication::~GDBRemoteCommunication() {
+ if (IsConnected()) {
+ Disconnect();
+ }
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (m_decompression_scratch)
+ free (m_decompression_scratch);
+#endif
+
+ // Stop the communications read thread which is used to parse all incoming
+ // packets. This function will block until the read thread returns.
+ if (m_read_thread_enabled)
+ StopReadThread();
+}
+
+char GDBRemoteCommunication::CalculcateChecksum(llvm::StringRef payload) {
+ int checksum = 0;
+
+ for (char c : payload)
+ checksum += c;
+
+ return checksum & 255;
+}
+
+size_t GDBRemoteCommunication::SendAck() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+ ConnectionStatus status = eConnectionStatusSuccess;
+ char ch = '+';
+ const size_t bytes_written = Write(&ch, 1, status, nullptr);
+ if (log)
+ log->Printf("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch);
+ m_history.AddPacket(ch, GDBRemoteCommunicationHistory::ePacketTypeSend,
+ bytes_written);
+ return bytes_written;
+}
+
+size_t GDBRemoteCommunication::SendNack() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+ ConnectionStatus status = eConnectionStatusSuccess;
+ char ch = '-';
+ const size_t bytes_written = Write(&ch, 1, status, nullptr);
+ if (log)
+ log->Printf("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch);
+ m_history.AddPacket(ch, GDBRemoteCommunicationHistory::ePacketTypeSend,
+ bytes_written);
+ return bytes_written;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::SendPacketNoLock(llvm::StringRef payload) {
+ StreamString packet(0, 4, eByteOrderBig);
+ packet.PutChar('$');
+ packet.Write(payload.data(), payload.size());
+ packet.PutChar('#');
+ packet.PutHex8(CalculcateChecksum(payload));
+ std::string packet_str = packet.GetString();
+
+ return SendRawPacketNoLock(packet_str);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::SendRawPacketNoLock(llvm::StringRef packet,
+ bool skip_ack) {
+ if (IsConnected()) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+ ConnectionStatus status = eConnectionStatusSuccess;
+ const char *packet_data = packet.data();
+ const size_t packet_length = packet.size();
+ size_t bytes_written = Write(packet_data, packet_length, status, nullptr);
+ if (log) {
+ size_t binary_start_offset = 0;
+ if (strncmp(packet_data, "$vFile:pwrite:", strlen("$vFile:pwrite:")) ==
+ 0) {
+ const char *first_comma = strchr(packet_data, ',');
+ if (first_comma) {
+ const char *second_comma = strchr(first_comma + 1, ',');
+ if (second_comma)
+ binary_start_offset = second_comma - packet_data + 1;
+ }
+ }
+
+ // If logging was just enabled and we have history, then dump out what we
+ // have to the log so we get the historical context. The Dump() call that
+ // logs all of the packet will set a boolean so that we don't dump this
+ // more than once
+ if (!m_history.DidDumpToLog())
+ m_history.Dump(log);
+
+ if (binary_start_offset) {
+ StreamString strm;
+ // Print non binary data header
+ strm.Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written,
+ (int)binary_start_offset, packet_data);
+ const uint8_t *p;
+ // Print binary data exactly as sent
+ for (p = (const uint8_t *)packet_data + binary_start_offset; *p != '#';
+ ++p)
+ strm.Printf("\\x%2.2x", *p);
+ // Print the checksum
+ strm.Printf("%*s", (int)3, p);
+ log->PutString(strm.GetString());
+ } else
+ log->Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written,
+ (int)packet_length, packet_data);
+ }
+
+ m_history.AddPacket(packet.str(), packet_length,
+ GDBRemoteCommunicationHistory::ePacketTypeSend,
+ bytes_written);
+
+ if (bytes_written == packet_length) {
+ if (!skip_ack && GetSendAcks())
+ return GetAck();
+ else
+ return PacketResult::Success;
+ } else {
+ if (log)
+ log->Printf("error: failed to send packet: %.*s", (int)packet_length,
+ packet_data);
+ }
+ }
+ return PacketResult::ErrorSendFailed;
+}
+
+GDBRemoteCommunication::PacketResult GDBRemoteCommunication::GetAck() {
+ StringExtractorGDBRemote packet;
+ PacketResult result = ReadPacket(packet, GetPacketTimeout(), false);
+ if (result == PacketResult::Success) {
+ if (packet.GetResponseType() ==
+ StringExtractorGDBRemote::ResponseType::eAck)
+ return PacketResult::Success;
+ else
+ return PacketResult::ErrorSendAck;
+ }
+ return result;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::ReadPacketWithOutputSupport(
+ StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
+ bool sync_on_timeout,
+ llvm::function_ref<void(llvm::StringRef)> output_callback) {
+ auto result = ReadPacket(response, timeout, sync_on_timeout);
+ while (result == PacketResult::Success && response.IsNormalResponse() &&
+ response.PeekChar() == 'O') {
+ response.GetChar();
+ std::string output;
+ if (response.GetHexByteString(output))
+ output_callback(output);
+ result = ReadPacket(response, timeout, sync_on_timeout);
+ }
+ return result;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::ReadPacket(StringExtractorGDBRemote &response,
+ Timeout<std::micro> timeout,
+ bool sync_on_timeout) {
+ if (m_read_thread_enabled)
+ return PopPacketFromQueue(response, timeout);
+ else
+ return WaitForPacketNoLock(response, timeout, sync_on_timeout);
+}
+
+// This function is called when a packet is requested.
+// A whole packet is popped from the packet queue and returned to the caller.
+// Packets are placed into this queue from the communication read thread. See
+// GDBRemoteCommunication::AppendBytesToCache.
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::PopPacketFromQueue(StringExtractorGDBRemote &response,
+ Timeout<std::micro> timeout) {
+ auto pred = [&] { return !m_packet_queue.empty() && IsConnected(); };
+ // lock down the packet queue
+ std::unique_lock<std::mutex> lock(m_packet_queue_mutex);
+
+ if (!timeout)
+ m_condition_queue_not_empty.wait(lock, pred);
+ else {
+ if (!m_condition_queue_not_empty.wait_for(lock, *timeout, pred))
+ return PacketResult::ErrorReplyTimeout;
+ if (!IsConnected())
+ return PacketResult::ErrorDisconnected;
+ }
+
+ // get the front element of the queue
+ response = m_packet_queue.front();
+
+ // remove the front element
+ m_packet_queue.pop();
+
+ // we got a packet
+ return PacketResult::Success;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::WaitForPacketNoLock(StringExtractorGDBRemote &packet,
+ Timeout<std::micro> timeout,
+ bool sync_on_timeout) {
+ uint8_t buffer[8192];
+ Status error;
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+
+ // Check for a packet from our cache first without trying any reading...
+ if (CheckForPacket(nullptr, 0, packet) != PacketType::Invalid)
+ return PacketResult::Success;
+
+ bool timed_out = false;
+ bool disconnected = false;
+ while (IsConnected() && !timed_out) {
+ lldb::ConnectionStatus status = eConnectionStatusNoConnection;
+ size_t bytes_read = Read(buffer, sizeof(buffer), timeout, status, &error);
+
+ LLDB_LOGV(log,
+ "Read(buffer, sizeof(buffer), timeout = {0}, "
+ "status = {1}, error = {2}) => bytes_read = {3}",
+ timeout, Communication::ConnectionStatusAsCString(status), error,
+ bytes_read);
+
+ if (bytes_read > 0) {
+ if (CheckForPacket(buffer, bytes_read, packet) != PacketType::Invalid)
+ return PacketResult::Success;
+ } else {
+ switch (status) {
+ case eConnectionStatusTimedOut:
+ case eConnectionStatusInterrupted:
+ if (sync_on_timeout) {
+ /// Sync the remote GDB server and make sure we get a response that
+ /// corresponds to what we send.
+ ///
+ /// Sends a "qEcho" packet and makes sure it gets the exact packet
+ /// echoed back. If the qEcho packet isn't supported, we send a qC
+ /// packet and make sure we get a valid thread ID back. We use the
+ /// "qC" packet since its response if very unique: is responds with
+ /// "QC%x" where %x is the thread ID of the current thread. This
+ /// makes the response unique enough from other packet responses to
+ /// ensure we are back on track.
+ ///
+ /// This packet is needed after we time out sending a packet so we
+ /// can ensure that we are getting the response for the packet we
+ /// are sending. There are no sequence IDs in the GDB remote
+ /// protocol (there used to be, but they are not supported anymore)
+ /// so if you timeout sending packet "abc", you might then send
+ /// packet "cde" and get the response for the previous "abc" packet.
+ /// Many responses are "OK" or "" (unsupported) or "EXX" (error) so
+ /// many responses for packets can look like responses for other
+ /// packets. So if we timeout, we need to ensure that we can get
+ /// back on track. If we can't get back on track, we must
+ /// disconnect.
+ bool sync_success = false;
+ bool got_actual_response = false;
+ // We timed out, we need to sync back up with the
+ char echo_packet[32];
+ int echo_packet_len = 0;
+ RegularExpression response_regex;
+
+ if (m_supports_qEcho == eLazyBoolYes) {
+ echo_packet_len = ::snprintf(echo_packet, sizeof(echo_packet),
+ "qEcho:%u", ++m_echo_number);
+ std::string regex_str = "^";
+ regex_str += echo_packet;
+ regex_str += "$";
+ response_regex.Compile(regex_str);
+ } else {
+ echo_packet_len =
+ ::snprintf(echo_packet, sizeof(echo_packet), "qC");
+ response_regex.Compile(llvm::StringRef("^QC[0-9A-Fa-f]+$"));
+ }
+
+ PacketResult echo_packet_result =
+ SendPacketNoLock(llvm::StringRef(echo_packet, echo_packet_len));
+ if (echo_packet_result == PacketResult::Success) {
+ const uint32_t max_retries = 3;
+ uint32_t successful_responses = 0;
+ for (uint32_t i = 0; i < max_retries; ++i) {
+ StringExtractorGDBRemote echo_response;
+ echo_packet_result =
+ WaitForPacketNoLock(echo_response, timeout, false);
+ if (echo_packet_result == PacketResult::Success) {
+ ++successful_responses;
+ if (response_regex.Execute(echo_response.GetStringRef())) {
+ sync_success = true;
+ break;
+ } else if (successful_responses == 1) {
+ // We got something else back as the first successful
+ // response, it probably is the response to the packet we
+ // actually wanted, so copy it over if this is the first
+ // success and continue to try to get the qEcho response
+ packet = echo_response;
+ got_actual_response = true;
+ }
+ } else if (echo_packet_result == PacketResult::ErrorReplyTimeout)
+ continue; // Packet timed out, continue waiting for a response
+ else
+ break; // Something else went wrong getting the packet back, we
+ // failed and are done trying
+ }
+ }
+
+ // We weren't able to sync back up with the server, we must abort
+ // otherwise all responses might not be from the right packets...
+ if (sync_success) {
+ // We timed out, but were able to recover
+ if (got_actual_response) {
+ // We initially timed out, but we did get a response that came in
+ // before the successful reply to our qEcho packet, so lets say
+ // everything is fine...
+ return PacketResult::Success;
+ }
+ } else {
+ disconnected = true;
+ Disconnect();
+ }
+ }
+ timed_out = true;
+ break;
+ case eConnectionStatusSuccess:
+ // printf ("status = success but error = %s\n",
+ // error.AsCString("<invalid>"));
+ break;
+
+ case eConnectionStatusEndOfFile:
+ case eConnectionStatusNoConnection:
+ case eConnectionStatusLostConnection:
+ case eConnectionStatusError:
+ disconnected = true;
+ Disconnect();
+ break;
+ }
+ }
+ }
+ packet.Clear();
+ if (disconnected)
+ return PacketResult::ErrorDisconnected;
+ if (timed_out)
+ return PacketResult::ErrorReplyTimeout;
+ else
+ return PacketResult::ErrorReplyFailed;
+}
+
+bool GDBRemoteCommunication::DecompressPacket() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+
+ if (!CompressionIsEnabled())
+ return true;
+
+ size_t pkt_size = m_bytes.size();
+
+ // Smallest possible compressed packet is $N#00 - an uncompressed empty
+ // reply, most commonly indicating an unsupported packet. Anything less than
+ // 5 characters, it's definitely not a compressed packet.
+ if (pkt_size < 5)
+ return true;
+
+ if (m_bytes[0] != '$' && m_bytes[0] != '%')
+ return true;
+ if (m_bytes[1] != 'C' && m_bytes[1] != 'N')
+ return true;
+
+ size_t hash_mark_idx = m_bytes.find('#');
+ if (hash_mark_idx == std::string::npos)
+ return true;
+ if (hash_mark_idx + 2 >= m_bytes.size())
+ return true;
+
+ if (!::isxdigit(m_bytes[hash_mark_idx + 1]) ||
+ !::isxdigit(m_bytes[hash_mark_idx + 2]))
+ return true;
+
+ size_t content_length =
+ pkt_size -
+ 5; // not counting '$', 'C' | 'N', '#', & the two hex checksum chars
+ size_t content_start = 2; // The first character of the
+ // compressed/not-compressed text of the packet
+ size_t checksum_idx =
+ hash_mark_idx +
+ 1; // The first character of the two hex checksum characters
+
+ // Normally size_of_first_packet == m_bytes.size() but m_bytes may contain
+ // multiple packets. size_of_first_packet is the size of the initial packet
+ // which we'll replace with the decompressed version of, leaving the rest of
+ // m_bytes unmodified.
+ size_t size_of_first_packet = hash_mark_idx + 3;
+
+ // Compressed packets ("$C") start with a base10 number which is the size of
+ // the uncompressed payload, then a : and then the compressed data. e.g.
+ // $C1024:<binary>#00 Update content_start and content_length to only include
+ // the <binary> part of the packet.
+
+ uint64_t decompressed_bufsize = ULONG_MAX;
+ if (m_bytes[1] == 'C') {
+ size_t i = content_start;
+ while (i < hash_mark_idx && isdigit(m_bytes[i]))
+ i++;
+ if (i < hash_mark_idx && m_bytes[i] == ':') {
+ i++;
+ content_start = i;
+ content_length = hash_mark_idx - content_start;
+ std::string bufsize_str(m_bytes.data() + 2, i - 2 - 1);
+ errno = 0;
+ decompressed_bufsize = ::strtoul(bufsize_str.c_str(), nullptr, 10);
+ if (errno != 0 || decompressed_bufsize == ULONG_MAX) {
+ m_bytes.erase(0, size_of_first_packet);
+ return false;
+ }
+ }
+ }
+
+ if (GetSendAcks()) {
+ char packet_checksum_cstr[3];
+ packet_checksum_cstr[0] = m_bytes[checksum_idx];
+ packet_checksum_cstr[1] = m_bytes[checksum_idx + 1];
+ packet_checksum_cstr[2] = '\0';
+ long packet_checksum = strtol(packet_checksum_cstr, nullptr, 16);
+
+ long actual_checksum = CalculcateChecksum(
+ llvm::StringRef(m_bytes).substr(1, hash_mark_idx - 1));
+ bool success = packet_checksum == actual_checksum;
+ if (!success) {
+ if (log)
+ log->Printf(
+ "error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x",
+ (int)(pkt_size), m_bytes.c_str(), (uint8_t)packet_checksum,
+ (uint8_t)actual_checksum);
+ }
+ // Send the ack or nack if needed
+ if (!success) {
+ SendNack();
+ m_bytes.erase(0, size_of_first_packet);
+ return false;
+ } else {
+ SendAck();
+ }
+ }
+
+ if (m_bytes[1] == 'N') {
+ // This packet was not compressed -- delete the 'N' character at the start
+ // and the packet may be processed as-is.
+ m_bytes.erase(1, 1);
+ return true;
+ }
+
+ // Reverse the gdb-remote binary escaping that was done to the compressed
+ // text to guard characters like '$', '#', '}', etc.
+ std::vector<uint8_t> unescaped_content;
+ unescaped_content.reserve(content_length);
+ size_t i = content_start;
+ while (i < hash_mark_idx) {
+ if (m_bytes[i] == '}') {
+ i++;
+ unescaped_content.push_back(m_bytes[i] ^ 0x20);
+ } else {
+ unescaped_content.push_back(m_bytes[i]);
+ }
+ i++;
+ }
+
+ uint8_t *decompressed_buffer = nullptr;
+ size_t decompressed_bytes = 0;
+
+ if (decompressed_bufsize != ULONG_MAX) {
+ decompressed_buffer = (uint8_t *)malloc(decompressed_bufsize);
+ if (decompressed_buffer == nullptr) {
+ m_bytes.erase(0, size_of_first_packet);
+ return false;
+ }
+ }
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (m_compression_type == CompressionType::ZlibDeflate ||
+ m_compression_type == CompressionType::LZFSE ||
+ m_compression_type == CompressionType::LZ4 ||
+ m_compression_type == CompressionType::LZMA) {
+ compression_algorithm compression_type;
+ if (m_compression_type == CompressionType::LZFSE)
+ compression_type = COMPRESSION_LZFSE;
+ else if (m_compression_type == CompressionType::ZlibDeflate)
+ compression_type = COMPRESSION_ZLIB;
+ else if (m_compression_type == CompressionType::LZ4)
+ compression_type = COMPRESSION_LZ4_RAW;
+ else if (m_compression_type == CompressionType::LZMA)
+ compression_type = COMPRESSION_LZMA;
+
+ if (m_decompression_scratch_type != m_compression_type) {
+ if (m_decompression_scratch) {
+ free (m_decompression_scratch);
+ m_decompression_scratch = nullptr;
+ }
+ size_t scratchbuf_size = 0;
+ if (m_compression_type == CompressionType::LZFSE)
+ scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZFSE);
+ else if (m_compression_type == CompressionType::LZ4)
+ scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZ4_RAW);
+ else if (m_compression_type == CompressionType::ZlibDeflate)
+ scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_ZLIB);
+ else if (m_compression_type == CompressionType::LZMA)
+ scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZMA);
+ else if (m_compression_type == CompressionType::LZFSE)
+ scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZFSE);
+ if (scratchbuf_size > 0) {
+ m_decompression_scratch = (void*) malloc (scratchbuf_size);
+ m_decompression_scratch_type = m_compression_type;
+ }
+ }
+
+ if (decompressed_bufsize != ULONG_MAX && decompressed_buffer != nullptr) {
+ decompressed_bytes = compression_decode_buffer(
+ decompressed_buffer, decompressed_bufsize,
+ (uint8_t *)unescaped_content.data(), unescaped_content.size(),
+ m_decompression_scratch, compression_type);
+ }
+ }
+#endif
+
+#if defined(HAVE_LIBZ)
+ if (decompressed_bytes == 0 && decompressed_bufsize != ULONG_MAX &&
+ decompressed_buffer != nullptr &&
+ m_compression_type == CompressionType::ZlibDeflate) {
+ z_stream stream;
+ memset(&stream, 0, sizeof(z_stream));
+ stream.next_in = (Bytef *)unescaped_content.data();
+ stream.avail_in = (uInt)unescaped_content.size();
+ stream.total_in = 0;
+ stream.next_out = (Bytef *)decompressed_buffer;
+ stream.avail_out = decompressed_bufsize;
+ stream.total_out = 0;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ if (inflateInit2(&stream, -15) == Z_OK) {
+ int status = inflate(&stream, Z_NO_FLUSH);
+ inflateEnd(&stream);
+ if (status == Z_STREAM_END) {
+ decompressed_bytes = stream.total_out;
+ }
+ }
+ }
+#endif
+
+ if (decompressed_bytes == 0 || decompressed_buffer == nullptr) {
+ if (decompressed_buffer)
+ free(decompressed_buffer);
+ m_bytes.erase(0, size_of_first_packet);
+ return false;
+ }
+
+ std::string new_packet;
+ new_packet.reserve(decompressed_bytes + 6);
+ new_packet.push_back(m_bytes[0]);
+ new_packet.append((const char *)decompressed_buffer, decompressed_bytes);
+ new_packet.push_back('#');
+ if (GetSendAcks()) {
+ uint8_t decompressed_checksum = CalculcateChecksum(
+ llvm::StringRef((const char *)decompressed_buffer, decompressed_bytes));
+ char decompressed_checksum_str[3];
+ snprintf(decompressed_checksum_str, 3, "%02x", decompressed_checksum);
+ new_packet.append(decompressed_checksum_str);
+ } else {
+ new_packet.push_back('0');
+ new_packet.push_back('0');
+ }
+
+ m_bytes.replace(0, size_of_first_packet, new_packet.data(),
+ new_packet.size());
+
+ free(decompressed_buffer);
+ return true;
+}
+
+GDBRemoteCommunication::PacketType
+GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len,
+ StringExtractorGDBRemote &packet) {
+ // Put the packet data into the buffer in a thread safe fashion
+ std::lock_guard<std::recursive_mutex> guard(m_bytes_mutex);
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+
+ if (src && src_len > 0) {
+ if (log && log->GetVerbose()) {
+ StreamString s;
+ log->Printf("GDBRemoteCommunication::%s adding %u bytes: %.*s",
+ __FUNCTION__, (uint32_t)src_len, (uint32_t)src_len, src);
+ }
+ m_bytes.append((const char *)src, src_len);
+ }
+
+ bool isNotifyPacket = false;
+
+ // Parse up the packets into gdb remote packets
+ if (!m_bytes.empty()) {
+ // end_idx must be one past the last valid packet byte. Start it off with
+ // an invalid value that is the same as the current index.
+ size_t content_start = 0;
+ size_t content_length = 0;
+ size_t total_length = 0;
+ size_t checksum_idx = std::string::npos;
+
+ // Size of packet before it is decompressed, for logging purposes
+ size_t original_packet_size = m_bytes.size();
+ if (CompressionIsEnabled()) {
+ if (!DecompressPacket()) {
+ packet.Clear();
+ return GDBRemoteCommunication::PacketType::Standard;
+ }
+ }
+
+ switch (m_bytes[0]) {
+ case '+': // Look for ack
+ case '-': // Look for cancel
+ case '\x03': // ^C to halt target
+ content_length = total_length = 1; // The command is one byte long...
+ break;
+
+ case '%': // Async notify packet
+ isNotifyPacket = true;
+ LLVM_FALLTHROUGH;
+
+ case '$':
+ // Look for a standard gdb packet?
+ {
+ size_t hash_pos = m_bytes.find('#');
+ if (hash_pos != std::string::npos) {
+ if (hash_pos + 2 < m_bytes.size()) {
+ checksum_idx = hash_pos + 1;
+ // Skip the dollar sign
+ content_start = 1;
+ // Don't include the # in the content or the $ in the content
+ // length
+ content_length = hash_pos - 1;
+
+ total_length =
+ hash_pos + 3; // Skip the # and the two hex checksum bytes
+ } else {
+ // Checksum bytes aren't all here yet
+ content_length = std::string::npos;
+ }
+ }
+ }
+ break;
+
+ default: {
+ // We have an unexpected byte and we need to flush all bad data that is
+ // in m_bytes, so we need to find the first byte that is a '+' (ACK), '-'
+ // (NACK), \x03 (CTRL+C interrupt), or '$' character (start of packet
+ // header) or of course, the end of the data in m_bytes...
+ const size_t bytes_len = m_bytes.size();
+ bool done = false;
+ uint32_t idx;
+ for (idx = 1; !done && idx < bytes_len; ++idx) {
+ switch (m_bytes[idx]) {
+ case '+':
+ case '-':
+ case '\x03':
+ case '%':
+ case '$':
+ done = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s tossing %u junk bytes: '%.*s'",
+ __FUNCTION__, idx - 1, idx - 1, m_bytes.c_str());
+ m_bytes.erase(0, idx - 1);
+ } break;
+ }
+
+ if (content_length == std::string::npos) {
+ packet.Clear();
+ return GDBRemoteCommunication::PacketType::Invalid;
+ } else if (total_length > 0) {
+
+ // We have a valid packet...
+ assert(content_length <= m_bytes.size());
+ assert(total_length <= m_bytes.size());
+ assert(content_length <= total_length);
+ size_t content_end = content_start + content_length;
+
+ bool success = true;
+ std::string &packet_str = packet.GetStringRef();
+ if (log) {
+ // If logging was just enabled and we have history, then dump out what
+ // we have to the log so we get the historical context. The Dump() call
+ // that logs all of the packet will set a boolean so that we don't dump
+ // this more than once
+ if (!m_history.DidDumpToLog())
+ m_history.Dump(log);
+
+ bool binary = false;
+ // Only detect binary for packets that start with a '$' and have a
+ // '#CC' checksum
+ if (m_bytes[0] == '$' && total_length > 4) {
+ for (size_t i = 0; !binary && i < total_length; ++i) {
+ unsigned char c = m_bytes[i];
+ if (isprint(c) == 0 && isspace(c) == 0) {
+ binary = true;
+ }
+ }
+ }
+ if (binary) {
+ StreamString strm;
+ // Packet header...
+ if (CompressionIsEnabled())
+ strm.Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %c",
+ (uint64_t)original_packet_size, (uint64_t)total_length,
+ m_bytes[0]);
+ else
+ strm.Printf("<%4" PRIu64 "> read packet: %c",
+ (uint64_t)total_length, m_bytes[0]);
+ for (size_t i = content_start; i < content_end; ++i) {
+ // Remove binary escaped bytes when displaying the packet...
+ const char ch = m_bytes[i];
+ if (ch == 0x7d) {
+ // 0x7d is the escape character. The next character is to be
+ // XOR'd with 0x20.
+ const char escapee = m_bytes[++i] ^ 0x20;
+ strm.Printf("%2.2x", escapee);
+ } else {
+ strm.Printf("%2.2x", (uint8_t)ch);
+ }
+ }
+ // Packet footer...
+ strm.Printf("%c%c%c", m_bytes[total_length - 3],
+ m_bytes[total_length - 2], m_bytes[total_length - 1]);
+ log->PutString(strm.GetString());
+ } else {
+ if (CompressionIsEnabled())
+ log->Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %.*s",
+ (uint64_t)original_packet_size, (uint64_t)total_length,
+ (int)(total_length), m_bytes.c_str());
+ else
+ log->Printf("<%4" PRIu64 "> read packet: %.*s",
+ (uint64_t)total_length, (int)(total_length),
+ m_bytes.c_str());
+ }
+ }
+
+ m_history.AddPacket(m_bytes, total_length,
+ GDBRemoteCommunicationHistory::ePacketTypeRecv,
+ total_length);
+
+ // Clear packet_str in case there is some existing data in it.
+ packet_str.clear();
+ // Copy the packet from m_bytes to packet_str expanding the run-length
+ // encoding in the process. Reserve enough byte for the most common case
+ // (no RLE used)
+ packet_str.reserve(m_bytes.length());
+ for (std::string::const_iterator c = m_bytes.begin() + content_start;
+ c != m_bytes.begin() + content_end; ++c) {
+ if (*c == '*') {
+ // '*' indicates RLE. Next character will give us the repeat count
+ // and previous character is what is to be repeated.
+ char char_to_repeat = packet_str.back();
+ // Number of time the previous character is repeated
+ int repeat_count = *++c + 3 - ' ';
+ // We have the char_to_repeat and repeat_count. Now push it in the
+ // packet.
+ for (int i = 0; i < repeat_count; ++i)
+ packet_str.push_back(char_to_repeat);
+ } else if (*c == 0x7d) {
+ // 0x7d is the escape character. The next character is to be XOR'd
+ // with 0x20.
+ char escapee = *++c ^ 0x20;
+ packet_str.push_back(escapee);
+ } else {
+ packet_str.push_back(*c);
+ }
+ }
+
+ if (m_bytes[0] == '$' || m_bytes[0] == '%') {
+ assert(checksum_idx < m_bytes.size());
+ if (::isxdigit(m_bytes[checksum_idx + 0]) ||
+ ::isxdigit(m_bytes[checksum_idx + 1])) {
+ if (GetSendAcks()) {
+ const char *packet_checksum_cstr = &m_bytes[checksum_idx];
+ char packet_checksum = strtol(packet_checksum_cstr, nullptr, 16);
+ char actual_checksum = CalculcateChecksum(
+ llvm::StringRef(m_bytes).slice(content_start, content_end));
+ success = packet_checksum == actual_checksum;
+ if (!success) {
+ if (log)
+ log->Printf("error: checksum mismatch: %.*s expected 0x%2.2x, "
+ "got 0x%2.2x",
+ (int)(total_length), m_bytes.c_str(),
+ (uint8_t)packet_checksum, (uint8_t)actual_checksum);
+ }
+ // Send the ack or nack if needed
+ if (!success)
+ SendNack();
+ else
+ SendAck();
+ }
+ } else {
+ success = false;
+ if (log)
+ log->Printf("error: invalid checksum in packet: '%s'\n",
+ m_bytes.c_str());
+ }
+ }
+
+ m_bytes.erase(0, total_length);
+ packet.SetFilePos(0);
+
+ if (isNotifyPacket)
+ return GDBRemoteCommunication::PacketType::Notify;
+ else
+ return GDBRemoteCommunication::PacketType::Standard;
+ }
+ }
+ packet.Clear();
+ return GDBRemoteCommunication::PacketType::Invalid;
+}
+
+Status GDBRemoteCommunication::StartListenThread(const char *hostname,
+ uint16_t port) {
+ if (m_listen_thread.IsJoinable())
+ return Status("listen thread already running");
+
+ char listen_url[512];
+ if (hostname && hostname[0])
+ snprintf(listen_url, sizeof(listen_url), "listen://%s:%i", hostname, port);
+ else
+ snprintf(listen_url, sizeof(listen_url), "listen://%i", port);
+ m_listen_url = listen_url;
+ SetConnection(new ConnectionFileDescriptor());
+ llvm::Expected<HostThread> listen_thread = ThreadLauncher::LaunchThread(
+ listen_url, GDBRemoteCommunication::ListenThread, this);
+ if (!listen_thread)
+ return Status(listen_thread.takeError());
+ m_listen_thread = *listen_thread;
+
+ return Status();
+}
+
+bool GDBRemoteCommunication::JoinListenThread() {
+ if (m_listen_thread.IsJoinable())
+ m_listen_thread.Join(nullptr);
+ return true;
+}
+
+lldb::thread_result_t
+GDBRemoteCommunication::ListenThread(lldb::thread_arg_t arg) {
+ GDBRemoteCommunication *comm = (GDBRemoteCommunication *)arg;
+ Status error;
+ ConnectionFileDescriptor *connection =
+ (ConnectionFileDescriptor *)comm->GetConnection();
+
+ if (connection) {
+ // Do the listen on another thread so we can continue on...
+ if (connection->Connect(comm->m_listen_url.c_str(), &error) !=
+ eConnectionStatusSuccess)
+ comm->SetConnection(nullptr);
+ }
+ return {};
+}
+
+Status GDBRemoteCommunication::StartDebugserverProcess(
+ const char *url, Platform *platform, ProcessLaunchInfo &launch_info,
+ uint16_t *port, const Args *inferior_args, int pass_comm_fd) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s(url=%s, port=%" PRIu16 ")",
+ __FUNCTION__, url ? url : "<empty>",
+ port ? *port : uint16_t(0));
+
+ Status error;
+ // If we locate debugserver, keep that located version around
+ static FileSpec g_debugserver_file_spec;
+
+ char debugserver_path[PATH_MAX];
+ FileSpec &debugserver_file_spec = launch_info.GetExecutableFile();
+
+ Environment host_env = Host::GetEnvironment();
+
+ // Always check to see if we have an environment override for the path to the
+ // debugserver to use and use it if we do.
+ std::string env_debugserver_path = host_env.lookup("LLDB_DEBUGSERVER_PATH");
+ if (!env_debugserver_path.empty()) {
+ debugserver_file_spec.SetFile(env_debugserver_path,
+ FileSpec::Style::native);
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() gdb-remote stub exe path set "
+ "from environment variable: %s",
+ __FUNCTION__, env_debugserver_path.c_str());
+ } else
+ debugserver_file_spec = g_debugserver_file_spec;
+ bool debugserver_exists =
+ FileSystem::Instance().Exists(debugserver_file_spec);
+ if (!debugserver_exists) {
+ // The debugserver binary is in the LLDB.framework/Resources directory.
+ debugserver_file_spec = HostInfo::GetSupportExeDir();
+ if (debugserver_file_spec) {
+ debugserver_file_spec.AppendPathComponent(DEBUGSERVER_BASENAME);
+ debugserver_exists = FileSystem::Instance().Exists(debugserver_file_spec);
+ if (debugserver_exists) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunication::%s() found gdb-remote stub exe '%s'",
+ __FUNCTION__, debugserver_file_spec.GetPath().c_str());
+
+ g_debugserver_file_spec = debugserver_file_spec;
+ } else {
+ if (platform)
+ debugserver_file_spec =
+ platform->LocateExecutable(DEBUGSERVER_BASENAME);
+ else
+ debugserver_file_spec.Clear();
+ if (debugserver_file_spec) {
+ // Platform::LocateExecutable() wouldn't return a path if it doesn't
+ // exist
+ debugserver_exists = true;
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() could not find "
+ "gdb-remote stub exe '%s'",
+ __FUNCTION__, debugserver_file_spec.GetPath().c_str());
+ }
+ // Don't cache the platform specific GDB server binary as it could
+ // change from platform to platform
+ g_debugserver_file_spec.Clear();
+ }
+ }
+ }
+
+ if (debugserver_exists) {
+ debugserver_file_spec.GetPath(debugserver_path, sizeof(debugserver_path));
+
+ Args &debugserver_args = launch_info.GetArguments();
+ debugserver_args.Clear();
+
+ // Start args with "debugserver /file/path -r --"
+ debugserver_args.AppendArgument(llvm::StringRef(debugserver_path));
+
+#if !defined(__APPLE__)
+ // First argument to lldb-server must be mode in which to run.
+ debugserver_args.AppendArgument(llvm::StringRef("gdbserver"));
+#endif
+
+ // If a url is supplied then use it
+ if (url)
+ debugserver_args.AppendArgument(llvm::StringRef(url));
+
+ if (pass_comm_fd >= 0) {
+ StreamString fd_arg;
+ fd_arg.Printf("--fd=%i", pass_comm_fd);
+ debugserver_args.AppendArgument(fd_arg.GetString());
+ // Send "pass_comm_fd" down to the inferior so it can use it to
+ // communicate back with this process
+ launch_info.AppendDuplicateFileAction(pass_comm_fd, pass_comm_fd);
+ }
+
+ // use native registers, not the GDB registers
+ debugserver_args.AppendArgument(llvm::StringRef("--native-regs"));
+
+ if (launch_info.GetLaunchInSeparateProcessGroup()) {
+ debugserver_args.AppendArgument(llvm::StringRef("--setsid"));
+ }
+
+ llvm::SmallString<128> named_pipe_path;
+ // socket_pipe is used by debug server to communicate back either
+ // TCP port or domain socket name which it listens on.
+ // The second purpose of the pipe to serve as a synchronization point -
+ // once data is written to the pipe, debug server is up and running.
+ Pipe socket_pipe;
+
+ // port is null when debug server should listen on domain socket - we're
+ // not interested in port value but rather waiting for debug server to
+ // become available.
+ if (pass_comm_fd == -1) {
+ if (url) {
+// Create a temporary file to get the stdout/stderr and redirect the output of
+// the command into this file. We will later read this file if all goes well
+// and fill the data into "command_output_ptr"
+#if defined(__APPLE__)
+ // Binding to port zero, we need to figure out what port it ends up
+ // using using a named pipe...
+ error = socket_pipe.CreateWithUniqueName("debugserver-named-pipe",
+ false, named_pipe_path);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "named pipe creation failed: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ debugserver_args.AppendArgument(llvm::StringRef("--named-pipe"));
+ debugserver_args.AppendArgument(named_pipe_path);
+#else
+ // Binding to port zero, we need to figure out what port it ends up
+ // using using an unnamed pipe...
+ error = socket_pipe.CreateNew(true);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "unnamed pipe creation failed: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ pipe_t write = socket_pipe.GetWritePipe();
+ debugserver_args.AppendArgument(llvm::StringRef("--pipe"));
+ debugserver_args.AppendArgument(llvm::to_string(write));
+ launch_info.AppendCloseFileAction(socket_pipe.GetReadFileDescriptor());
+#endif
+ } else {
+ // No host and port given, so lets listen on our end and make the
+ // debugserver connect to us..
+ error = StartListenThread("127.0.0.1", 0);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() unable to start listen "
+ "thread: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ ConnectionFileDescriptor *connection =
+ (ConnectionFileDescriptor *)GetConnection();
+ // Wait for 10 seconds to resolve the bound port
+ uint16_t port_ = connection->GetListeningPort(std::chrono::seconds(10));
+ if (port_ > 0) {
+ char port_cstr[32];
+ snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%i", port_);
+ // Send the host and port down that debugserver and specify an option
+ // so that it connects back to the port we are listening to in this
+ // process
+ debugserver_args.AppendArgument(llvm::StringRef("--reverse-connect"));
+ debugserver_args.AppendArgument(llvm::StringRef(port_cstr));
+ if (port)
+ *port = port_;
+ } else {
+ error.SetErrorString("failed to bind to port 0 on 127.0.0.1");
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() failed: %s", __FUNCTION__,
+ error.AsCString());
+ return error;
+ }
+ }
+ }
+ std::string env_debugserver_log_file =
+ host_env.lookup("LLDB_DEBUGSERVER_LOG_FILE");
+ if (!env_debugserver_log_file.empty()) {
+ debugserver_args.AppendArgument(
+ llvm::formatv("--log-file={0}", env_debugserver_log_file).str());
+ }
+
+#if defined(__APPLE__)
+ const char *env_debugserver_log_flags =
+ getenv("LLDB_DEBUGSERVER_LOG_FLAGS");
+ if (env_debugserver_log_flags) {
+ debugserver_args.AppendArgument(
+ llvm::formatv("--log-flags={0}", env_debugserver_log_flags).str());
+ }
+#else
+ std::string env_debugserver_log_channels =
+ host_env.lookup("LLDB_SERVER_LOG_CHANNELS");
+ if (!env_debugserver_log_channels.empty()) {
+ debugserver_args.AppendArgument(
+ llvm::formatv("--log-channels={0}", env_debugserver_log_channels)
+ .str());
+ }
+#endif
+
+ // Add additional args, starting with LLDB_DEBUGSERVER_EXTRA_ARG_1 until an
+ // env var doesn't come back.
+ uint32_t env_var_index = 1;
+ bool has_env_var;
+ do {
+ char env_var_name[64];
+ snprintf(env_var_name, sizeof(env_var_name),
+ "LLDB_DEBUGSERVER_EXTRA_ARG_%" PRIu32, env_var_index++);
+ std::string extra_arg = host_env.lookup(env_var_name);
+ has_env_var = !extra_arg.empty();
+
+ if (has_env_var) {
+ debugserver_args.AppendArgument(llvm::StringRef(extra_arg));
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s adding env var %s contents "
+ "to stub command line (%s)",
+ __FUNCTION__, env_var_name, extra_arg.c_str());
+ }
+ } while (has_env_var);
+
+ if (inferior_args && inferior_args->GetArgumentCount() > 0) {
+ debugserver_args.AppendArgument(llvm::StringRef("--"));
+ debugserver_args.AppendArguments(*inferior_args);
+ }
+
+ // Copy the current environment to the gdbserver/debugserver instance
+ launch_info.GetEnvironment() = host_env;
+
+ // Close STDIN, STDOUT and STDERR.
+ launch_info.AppendCloseFileAction(STDIN_FILENO);
+ launch_info.AppendCloseFileAction(STDOUT_FILENO);
+ launch_info.AppendCloseFileAction(STDERR_FILENO);
+
+ // Redirect STDIN, STDOUT and STDERR to "/dev/null".
+ launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false);
+ launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true);
+ launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true);
+
+ if (log) {
+ StreamString string_stream;
+ Platform *const platform = nullptr;
+ launch_info.Dump(string_stream, platform);
+ log->Printf("launch info for gdb-remote stub:\n%s",
+ string_stream.GetData());
+ }
+ error = Host::LaunchProcess(launch_info);
+
+ if (error.Success() &&
+ (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) &&
+ pass_comm_fd == -1) {
+ if (named_pipe_path.size() > 0) {
+ error = socket_pipe.OpenAsReader(named_pipe_path, false);
+ if (error.Fail())
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "failed to open named pipe %s for reading: %s",
+ __FUNCTION__, named_pipe_path.c_str(),
+ error.AsCString());
+ }
+
+ if (socket_pipe.CanWrite())
+ socket_pipe.CloseWriteFileDescriptor();
+ if (socket_pipe.CanRead()) {
+ char port_cstr[PATH_MAX] = {0};
+ port_cstr[0] = '\0';
+ size_t num_bytes = sizeof(port_cstr);
+ // Read port from pipe with 10 second timeout.
+ error = socket_pipe.ReadWithTimeout(
+ port_cstr, num_bytes, std::chrono::seconds{10}, num_bytes);
+ if (error.Success() && (port != nullptr)) {
+ assert(num_bytes > 0 && port_cstr[num_bytes - 1] == '\0');
+ uint16_t child_port = StringConvert::ToUInt32(port_cstr, 0);
+ if (*port == 0 || *port == child_port) {
+ *port = child_port;
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "debugserver listens %u port",
+ __FUNCTION__, *port);
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "debugserver listening on port "
+ "%d but requested port was %d",
+ __FUNCTION__, (uint32_t)child_port,
+ (uint32_t)(*port));
+ }
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "failed to read a port value from pipe %s: %s",
+ __FUNCTION__, named_pipe_path.c_str(),
+ error.AsCString());
+ }
+ socket_pipe.Close();
+ }
+
+ if (named_pipe_path.size() > 0) {
+ const auto err = socket_pipe.Delete(named_pipe_path);
+ if (err.Fail()) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunication::%s failed to delete pipe %s: %s",
+ __FUNCTION__, named_pipe_path.c_str(), err.AsCString());
+ }
+ }
+
+ // Make sure we actually connect with the debugserver...
+ JoinListenThread();
+ }
+ } else {
+ error.SetErrorStringWithFormat("unable to locate " DEBUGSERVER_BASENAME);
+ }
+
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() failed: %s", __FUNCTION__,
+ error.AsCString());
+ }
+
+ return error;
+}
+
+void GDBRemoteCommunication::DumpHistory(Stream &strm) { m_history.Dump(strm); }
+
+void GDBRemoteCommunication::SetHistoryStream(llvm::raw_ostream *strm) {
+ m_history.SetStream(strm);
+}
+
+llvm::Error
+GDBRemoteCommunication::ConnectLocally(GDBRemoteCommunication &client,
+ GDBRemoteCommunication &server) {
+ const bool child_processes_inherit = false;
+ const int backlog = 5;
+ TCPSocket listen_socket(true, child_processes_inherit);
+ if (llvm::Error error =
+ listen_socket.Listen("127.0.0.1:0", backlog).ToError())
+ return error;
+
+ Socket *accept_socket;
+ std::future<Status> accept_status = std::async(
+ std::launch::async, [&] { return listen_socket.Accept(accept_socket); });
+
+ llvm::SmallString<32> remote_addr;
+ llvm::raw_svector_ostream(remote_addr)
+ << "connect://127.0.0.1:" << listen_socket.GetLocalPortNumber();
+
+ std::unique_ptr<ConnectionFileDescriptor> conn_up(
+ new ConnectionFileDescriptor());
+ Status status;
+ if (conn_up->Connect(remote_addr, &status) != lldb::eConnectionStatusSuccess)
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Unable to connect: %s", status.AsCString());
+
+ client.SetConnection(conn_up.release());
+ if (llvm::Error error = accept_status.get().ToError())
+ return error;
+
+ server.SetConnection(new ConnectionFileDescriptor(accept_socket));
+ return llvm::Error::success();
+}
+
+GDBRemoteCommunication::ScopedTimeout::ScopedTimeout(
+ GDBRemoteCommunication &gdb_comm, std::chrono::seconds timeout)
+ : m_gdb_comm(gdb_comm), m_timeout_modified(false) {
+ auto curr_timeout = gdb_comm.GetPacketTimeout();
+ // Only update the timeout if the timeout is greater than the current
+ // timeout. If the current timeout is larger, then just use that.
+ if (curr_timeout < timeout) {
+ m_timeout_modified = true;
+ m_saved_timeout = m_gdb_comm.SetPacketTimeout(timeout);
+ }
+}
+
+GDBRemoteCommunication::ScopedTimeout::~ScopedTimeout() {
+ // Only restore the timeout if we set it in the constructor.
+ if (m_timeout_modified)
+ m_gdb_comm.SetPacketTimeout(m_saved_timeout);
+}
+
+// This function is called via the Communications class read thread when bytes
+// become available for this connection. This function will consume all
+// incoming bytes and try to parse whole packets as they become available. Full
+// packets are placed in a queue, so that all packet requests can simply pop
+// from this queue. Async notification packets will be dispatched immediately
+// to the ProcessGDBRemote Async thread via an event.
+void GDBRemoteCommunication::AppendBytesToCache(const uint8_t *bytes,
+ size_t len, bool broadcast,
+ lldb::ConnectionStatus status) {
+ StringExtractorGDBRemote packet;
+
+ while (true) {
+ PacketType type = CheckForPacket(bytes, len, packet);
+
+ // scrub the data so we do not pass it back to CheckForPacket on future
+ // passes of the loop
+ bytes = nullptr;
+ len = 0;
+
+ // we may have received no packet so lets bail out
+ if (type == PacketType::Invalid)
+ break;
+
+ if (type == PacketType::Standard) {
+ // scope for the mutex
+ {
+ // lock down the packet queue
+ std::lock_guard<std::mutex> guard(m_packet_queue_mutex);
+ // push a new packet into the queue
+ m_packet_queue.push(packet);
+ // Signal condition variable that we have a packet
+ m_condition_queue_not_empty.notify_one();
+ }
+ }
+
+ if (type == PacketType::Notify) {
+ // put this packet into an event
+ const char *pdata = packet.GetStringRef().c_str();
+
+ // as the communication class, we are a broadcaster and the async thread
+ // is tuned to listen to us
+ BroadcastEvent(eBroadcastBitGdbReadThreadGotNotify,
+ new EventDataBytes(pdata));
+ }
+ }
+}
+
+void llvm::format_provider<GDBRemoteCommunication::PacketResult>::format(
+ const GDBRemoteCommunication::PacketResult &result, raw_ostream &Stream,
+ StringRef Style) {
+ using PacketResult = GDBRemoteCommunication::PacketResult;
+
+ switch (result) {
+ case PacketResult::Success:
+ Stream << "Success";
+ break;
+ case PacketResult::ErrorSendFailed:
+ Stream << "ErrorSendFailed";
+ break;
+ case PacketResult::ErrorSendAck:
+ Stream << "ErrorSendAck";
+ break;
+ case PacketResult::ErrorReplyFailed:
+ Stream << "ErrorReplyFailed";
+ break;
+ case PacketResult::ErrorReplyTimeout:
+ Stream << "ErrorReplyTimeout";
+ break;
+ case PacketResult::ErrorReplyInvalid:
+ Stream << "ErrorReplyInvalid";
+ break;
+ case PacketResult::ErrorReplyAck:
+ Stream << "ErrorReplyAck";
+ break;
+ case PacketResult::ErrorDisconnected:
+ Stream << "ErrorDisconnected";
+ break;
+ case PacketResult::ErrorNoSequenceLock:
+ Stream << "ErrorNoSequenceLock";
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
new file mode 100644
index 000000000000..bb777a5c26a7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
@@ -0,0 +1,238 @@
+//===-- GDBRemoteCommunication.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunication_h_
+#define liblldb_GDBRemoteCommunication_h_
+
+#include "GDBRemoteCommunicationHistory.h"
+
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "lldb/Core/Communication.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Listener.h"
+#include "lldb/Utility/Predicate.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+#include "lldb/lldb-public.h"
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+enum GDBStoppointType {
+ eStoppointInvalid = -1,
+ eBreakpointSoftware = 0,
+ eBreakpointHardware,
+ eWatchpointWrite,
+ eWatchpointRead,
+ eWatchpointReadWrite
+};
+
+enum class CompressionType {
+ None = 0, // no compression
+ ZlibDeflate, // zlib's deflate compression scheme, requires zlib or Apple's
+ // libcompression
+ LZFSE, // an Apple compression scheme, requires Apple's libcompression
+ LZ4, // lz compression - called "lz4 raw" in libcompression terms, compat with
+ // https://code.google.com/p/lz4/
+ LZMA, // Lempel–Ziv–Markov chain algorithm
+};
+
+class ProcessGDBRemote;
+
+class GDBRemoteCommunication : public Communication {
+public:
+ enum {
+ eBroadcastBitRunPacketSent = kLoUserBroadcastBit,
+ eBroadcastBitGdbReadThreadGotNotify =
+ kLoUserBroadcastBit << 1 // Sent when we received a notify packet.
+ };
+
+ enum class PacketType { Invalid = 0, Standard, Notify };
+
+ enum class PacketResult {
+ Success = 0, // Success
+ ErrorSendFailed, // Status sending the packet
+ ErrorSendAck, // Didn't get an ack back after sending a packet
+ ErrorReplyFailed, // Status getting the reply
+ ErrorReplyTimeout, // Timed out waiting for reply
+ ErrorReplyInvalid, // Got a reply but it wasn't valid for the packet that
+ // was sent
+ ErrorReplyAck, // Sending reply ack failed
+ ErrorDisconnected, // We were disconnected
+ ErrorNoSequenceLock // We couldn't get the sequence lock for a multi-packet
+ // request
+ };
+
+ // Class to change the timeout for a given scope and restore it to the
+ // original value when the
+ // created ScopedTimeout object got out of scope
+ class ScopedTimeout {
+ public:
+ ScopedTimeout(GDBRemoteCommunication &gdb_comm,
+ std::chrono::seconds timeout);
+ ~ScopedTimeout();
+
+ private:
+ GDBRemoteCommunication &m_gdb_comm;
+ std::chrono::seconds m_saved_timeout;
+ // Don't ever reduce the timeout for a packet, only increase it. If the
+ // requested timeout if less than the current timeout, we don't set it
+ // and won't need to restore it.
+ bool m_timeout_modified;
+ };
+
+ GDBRemoteCommunication(const char *comm_name, const char *listener_name);
+
+ ~GDBRemoteCommunication() override;
+
+ PacketResult GetAck();
+
+ size_t SendAck();
+
+ size_t SendNack();
+
+ char CalculcateChecksum(llvm::StringRef payload);
+
+ PacketType CheckForPacket(const uint8_t *src, size_t src_len,
+ StringExtractorGDBRemote &packet);
+
+ bool GetSendAcks() { return m_send_acks; }
+
+ // Set the global packet timeout.
+ //
+ // For clients, this is the timeout that gets used when sending
+ // packets and waiting for responses. For servers, this is used when waiting
+ // for ACKs.
+ std::chrono::seconds SetPacketTimeout(std::chrono::seconds packet_timeout) {
+ const auto old_packet_timeout = m_packet_timeout;
+ m_packet_timeout = packet_timeout;
+ return old_packet_timeout;
+ }
+
+ std::chrono::seconds GetPacketTimeout() const { return m_packet_timeout; }
+
+ // Start a debugserver instance on the current host using the
+ // supplied connection URL.
+ Status StartDebugserverProcess(
+ const char *url,
+ Platform *platform, // If non nullptr, then check with the platform for
+ // the GDB server binary if it can't be located
+ ProcessLaunchInfo &launch_info, uint16_t *port, const Args *inferior_args,
+ int pass_comm_fd); // Communication file descriptor to pass during
+ // fork/exec to avoid having to connect/accept
+
+ void DumpHistory(Stream &strm);
+ void SetHistoryStream(llvm::raw_ostream *strm);
+
+ static llvm::Error ConnectLocally(GDBRemoteCommunication &client,
+ GDBRemoteCommunication &server);
+
+protected:
+ std::chrono::seconds m_packet_timeout;
+ uint32_t m_echo_number;
+ LazyBool m_supports_qEcho;
+ GDBRemoteCommunicationHistory m_history;
+ bool m_send_acks;
+ bool m_is_platform; // Set to true if this class represents a platform,
+ // false if this class represents a debug session for
+ // a single process
+
+ CompressionType m_compression_type;
+
+ PacketResult SendPacketNoLock(llvm::StringRef payload);
+ PacketResult SendRawPacketNoLock(llvm::StringRef payload,
+ bool skip_ack = false);
+
+ PacketResult ReadPacket(StringExtractorGDBRemote &response,
+ Timeout<std::micro> timeout, bool sync_on_timeout);
+
+ PacketResult ReadPacketWithOutputSupport(
+ StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
+ bool sync_on_timeout,
+ llvm::function_ref<void(llvm::StringRef)> output_callback);
+
+ // Pop a packet from the queue in a thread safe manner
+ PacketResult PopPacketFromQueue(StringExtractorGDBRemote &response,
+ Timeout<std::micro> timeout);
+
+ PacketResult WaitForPacketNoLock(StringExtractorGDBRemote &response,
+ Timeout<std::micro> timeout,
+ bool sync_on_timeout);
+
+ bool CompressionIsEnabled() {
+ return m_compression_type != CompressionType::None;
+ }
+
+ // If compression is enabled, decompress the packet in m_bytes and update
+ // m_bytes with the uncompressed version.
+ // Returns 'true' packet was decompressed and m_bytes is the now-decompressed
+ // text.
+ // Returns 'false' if unable to decompress or if the checksum was invalid.
+ //
+ // NB: Once the packet has been decompressed, checksum cannot be computed
+ // based
+ // on m_bytes. The checksum was for the compressed packet.
+ bool DecompressPacket();
+
+ Status StartListenThread(const char *hostname = "127.0.0.1",
+ uint16_t port = 0);
+
+ bool JoinListenThread();
+
+ static lldb::thread_result_t ListenThread(lldb::thread_arg_t arg);
+
+ // GDB-Remote read thread
+ // . this thread constantly tries to read from the communication
+ // class and stores all packets received in a queue. The usual
+ // threads read requests simply pop packets off the queue in the
+ // usual order.
+ // This setup allows us to intercept and handle async packets, such
+ // as the notify packet.
+
+ // This method is defined as part of communication.h
+ // when the read thread gets any bytes it will pass them on to this function
+ void AppendBytesToCache(const uint8_t *bytes, size_t len, bool broadcast,
+ lldb::ConnectionStatus status) override;
+
+private:
+ std::queue<StringExtractorGDBRemote> m_packet_queue; // The packet queue
+ std::mutex m_packet_queue_mutex; // Mutex for accessing queue
+ std::condition_variable
+ m_condition_queue_not_empty; // Condition variable to wait for packets
+
+ HostThread m_listen_thread;
+ std::string m_listen_url;
+
+#if defined(HAVE_LIBCOMPRESSION)
+ CompressionType m_decompression_scratch_type = CompressionType::None;
+ void *m_decompression_scratch = nullptr;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunication);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+namespace llvm {
+template <>
+struct format_provider<
+ lldb_private::process_gdb_remote::GDBRemoteCommunication::PacketResult> {
+ static void format(const lldb_private::process_gdb_remote::
+ GDBRemoteCommunication::PacketResult &state,
+ raw_ostream &Stream, StringRef Style);
+};
+} // namespace llvm
+
+#endif // liblldb_GDBRemoteCommunication_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
new file mode 100644
index 000000000000..9797184026e0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -0,0 +1,4005 @@
+//===-- GDBRemoteCommunicationClient.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteCommunicationClient.h"
+
+#include <math.h>
+#include <sys/stat.h>
+
+#include <numeric>
+#include <sstream>
+
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/XML.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/JSON.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+#include "llvm/ADT/StringSwitch.h"
+
+#if defined(__APPLE__)
+#ifndef HAVE_LIBCOMPRESSION
+#define HAVE_LIBCOMPRESSION
+#endif
+#include <compression.h>
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+using namespace std::chrono;
+
+// GDBRemoteCommunicationClient constructor
+GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
+ : GDBRemoteClientBase("gdb-remote.client", "gdb-remote.client.rx_packet"),
+ m_supports_not_sending_acks(eLazyBoolCalculate),
+ m_supports_thread_suffix(eLazyBoolCalculate),
+ m_supports_threads_in_stop_reply(eLazyBoolCalculate),
+ m_supports_vCont_all(eLazyBoolCalculate),
+ m_supports_vCont_any(eLazyBoolCalculate),
+ m_supports_vCont_c(eLazyBoolCalculate),
+ m_supports_vCont_C(eLazyBoolCalculate),
+ m_supports_vCont_s(eLazyBoolCalculate),
+ m_supports_vCont_S(eLazyBoolCalculate),
+ m_qHostInfo_is_valid(eLazyBoolCalculate),
+ m_curr_pid_is_valid(eLazyBoolCalculate),
+ m_qProcessInfo_is_valid(eLazyBoolCalculate),
+ m_qGDBServerVersion_is_valid(eLazyBoolCalculate),
+ m_supports_alloc_dealloc_memory(eLazyBoolCalculate),
+ m_supports_memory_region_info(eLazyBoolCalculate),
+ m_supports_watchpoint_support_info(eLazyBoolCalculate),
+ m_supports_detach_stay_stopped(eLazyBoolCalculate),
+ m_watchpoints_trigger_after_instruction(eLazyBoolCalculate),
+ m_attach_or_wait_reply(eLazyBoolCalculate),
+ m_prepare_for_reg_writing_reply(eLazyBoolCalculate),
+ m_supports_p(eLazyBoolCalculate), m_supports_x(eLazyBoolCalculate),
+ m_avoid_g_packets(eLazyBoolCalculate),
+ m_supports_QSaveRegisterState(eLazyBoolCalculate),
+ m_supports_qXfer_auxv_read(eLazyBoolCalculate),
+ m_supports_qXfer_libraries_read(eLazyBoolCalculate),
+ m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate),
+ m_supports_qXfer_features_read(eLazyBoolCalculate),
+ m_supports_qXfer_memory_map_read(eLazyBoolCalculate),
+ m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate),
+ m_supports_jThreadExtendedInfo(eLazyBoolCalculate),
+ m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate),
+ m_supports_jGetSharedCacheInfo(eLazyBoolCalculate),
+ m_supports_QPassSignals(eLazyBoolCalculate),
+ m_supports_error_string_reply(eLazyBoolCalculate),
+ m_supports_qProcessInfoPID(true), m_supports_qfProcessInfo(true),
+ m_supports_qUserName(true), m_supports_qGroupName(true),
+ m_supports_qThreadStopInfo(true), m_supports_z0(true),
+ m_supports_z1(true), m_supports_z2(true), m_supports_z3(true),
+ m_supports_z4(true), m_supports_QEnvironment(true),
+ m_supports_QEnvironmentHexEncoded(true), m_supports_qSymbol(true),
+ m_qSymbol_requests_done(false), m_supports_qModuleInfo(true),
+ m_supports_jThreadsInfo(true), m_supports_jModulesInfo(true),
+ m_curr_pid(LLDB_INVALID_PROCESS_ID), m_curr_tid(LLDB_INVALID_THREAD_ID),
+ m_curr_tid_run(LLDB_INVALID_THREAD_ID),
+ m_num_supported_hardware_watchpoints(0), m_host_arch(), m_process_arch(),
+ m_os_build(), m_os_kernel(), m_hostname(), m_gdb_server_name(),
+ m_gdb_server_version(UINT32_MAX), m_default_packet_timeout(0),
+ m_max_packet_size(0), m_qSupported_response(),
+ m_supported_async_json_packets_is_valid(false),
+ m_supported_async_json_packets_sp(), m_qXfer_memory_map(),
+ m_qXfer_memory_map_loaded(false) {}
+
+// Destructor
+GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() {
+ if (IsConnected())
+ Disconnect();
+}
+
+bool GDBRemoteCommunicationClient::HandshakeWithServer(Status *error_ptr) {
+ ResetDiscoverableSettings(false);
+
+ // Start the read thread after we send the handshake ack since if we fail to
+ // send the handshake ack, there is no reason to continue...
+ if (SendAck()) {
+ // Wait for any responses that might have been queued up in the remote
+ // GDB server and flush them all
+ StringExtractorGDBRemote response;
+ PacketResult packet_result = PacketResult::Success;
+ while (packet_result == PacketResult::Success)
+ packet_result = ReadPacket(response, milliseconds(10), false);
+
+ // The return value from QueryNoAckModeSupported() is true if the packet
+ // was sent and _any_ response (including UNIMPLEMENTED) was received), or
+ // false if no response was received. This quickly tells us if we have a
+ // live connection to a remote GDB server...
+ if (QueryNoAckModeSupported()) {
+ return true;
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorString("failed to get reply to handshake packet");
+ }
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorString("failed to send the handshake ack");
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetEchoSupported() {
+ if (m_supports_qEcho == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qEcho == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQPassSignalsSupported() {
+ if (m_supports_QPassSignals == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_QPassSignals == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetAugmentedLibrariesSVR4ReadSupported() {
+ if (m_supports_augmented_libraries_svr4_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_augmented_libraries_svr4_read == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQXferLibrariesSVR4ReadSupported() {
+ if (m_supports_qXfer_libraries_svr4_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qXfer_libraries_svr4_read == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQXferLibrariesReadSupported() {
+ if (m_supports_qXfer_libraries_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qXfer_libraries_read == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQXferAuxvReadSupported() {
+ if (m_supports_qXfer_auxv_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qXfer_auxv_read == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported() {
+ if (m_supports_qXfer_features_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qXfer_features_read == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQXferMemoryMapReadSupported() {
+ if (m_supports_qXfer_memory_map_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qXfer_memory_map_read == eLazyBoolYes;
+}
+
+uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() {
+ if (m_max_packet_size == 0) {
+ GetRemoteQSupported();
+ }
+ return m_max_packet_size;
+}
+
+bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() {
+ if (m_supports_not_sending_acks == eLazyBoolCalculate) {
+ m_send_acks = true;
+ m_supports_not_sending_acks = eLazyBoolNo;
+
+ // This is the first real packet that we'll send in a debug session and it
+ // may take a little longer than normal to receive a reply. Wait at least
+ // 6 seconds for a reply to this packet.
+
+ ScopedTimeout timeout(*this, std::max(GetPacketTimeout(), seconds(6)));
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("QStartNoAckMode", response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_send_acks = false;
+ m_supports_not_sending_acks = eLazyBoolYes;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+void GDBRemoteCommunicationClient::GetListThreadsInStopReplySupported() {
+ if (m_supports_threads_in_stop_reply == eLazyBoolCalculate) {
+ m_supports_threads_in_stop_reply = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("QListThreadsInStopReply", response,
+ false) == PacketResult::Success) {
+ if (response.IsOKResponse())
+ m_supports_threads_in_stop_reply = eLazyBoolYes;
+ }
+ }
+}
+
+bool GDBRemoteCommunicationClient::GetVAttachOrWaitSupported() {
+ if (m_attach_or_wait_reply == eLazyBoolCalculate) {
+ m_attach_or_wait_reply = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qVAttachOrWaitSupported", response,
+ false) == PacketResult::Success) {
+ if (response.IsOKResponse())
+ m_attach_or_wait_reply = eLazyBoolYes;
+ }
+ }
+ return m_attach_or_wait_reply == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetSyncThreadStateSupported() {
+ if (m_prepare_for_reg_writing_reply == eLazyBoolCalculate) {
+ m_prepare_for_reg_writing_reply = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qSyncThreadStateSupported", response,
+ false) == PacketResult::Success) {
+ if (response.IsOKResponse())
+ m_prepare_for_reg_writing_reply = eLazyBoolYes;
+ }
+ }
+ return m_prepare_for_reg_writing_reply == eLazyBoolYes;
+}
+
+void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
+ if (!did_exec) {
+ // Hard reset everything, this is when we first connect to a GDB server
+ m_supports_not_sending_acks = eLazyBoolCalculate;
+ m_supports_thread_suffix = eLazyBoolCalculate;
+ m_supports_threads_in_stop_reply = eLazyBoolCalculate;
+ m_supports_vCont_c = eLazyBoolCalculate;
+ m_supports_vCont_C = eLazyBoolCalculate;
+ m_supports_vCont_s = eLazyBoolCalculate;
+ m_supports_vCont_S = eLazyBoolCalculate;
+ m_supports_p = eLazyBoolCalculate;
+ m_supports_x = eLazyBoolCalculate;
+ m_supports_QSaveRegisterState = eLazyBoolCalculate;
+ m_qHostInfo_is_valid = eLazyBoolCalculate;
+ m_curr_pid_is_valid = eLazyBoolCalculate;
+ m_qGDBServerVersion_is_valid = eLazyBoolCalculate;
+ m_supports_alloc_dealloc_memory = eLazyBoolCalculate;
+ m_supports_memory_region_info = eLazyBoolCalculate;
+ m_prepare_for_reg_writing_reply = eLazyBoolCalculate;
+ m_attach_or_wait_reply = eLazyBoolCalculate;
+ m_avoid_g_packets = eLazyBoolCalculate;
+ m_supports_qXfer_auxv_read = eLazyBoolCalculate;
+ m_supports_qXfer_libraries_read = eLazyBoolCalculate;
+ m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
+ m_supports_qXfer_features_read = eLazyBoolCalculate;
+ m_supports_qXfer_memory_map_read = eLazyBoolCalculate;
+ m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate;
+ m_supports_qProcessInfoPID = true;
+ m_supports_qfProcessInfo = true;
+ m_supports_qUserName = true;
+ m_supports_qGroupName = true;
+ m_supports_qThreadStopInfo = true;
+ m_supports_z0 = true;
+ m_supports_z1 = true;
+ m_supports_z2 = true;
+ m_supports_z3 = true;
+ m_supports_z4 = true;
+ m_supports_QEnvironment = true;
+ m_supports_QEnvironmentHexEncoded = true;
+ m_supports_qSymbol = true;
+ m_qSymbol_requests_done = false;
+ m_supports_qModuleInfo = true;
+ m_host_arch.Clear();
+ m_os_version = llvm::VersionTuple();
+ m_os_build.clear();
+ m_os_kernel.clear();
+ m_hostname.clear();
+ m_gdb_server_name.clear();
+ m_gdb_server_version = UINT32_MAX;
+ m_default_packet_timeout = seconds(0);
+ m_max_packet_size = 0;
+ m_qSupported_response.clear();
+ m_supported_async_json_packets_is_valid = false;
+ m_supported_async_json_packets_sp.reset();
+ m_supports_jModulesInfo = true;
+ }
+
+ // These flags should be reset when we first connect to a GDB server and when
+ // our inferior process execs
+ m_qProcessInfo_is_valid = eLazyBoolCalculate;
+ m_process_arch.Clear();
+}
+
+void GDBRemoteCommunicationClient::GetRemoteQSupported() {
+ // Clear out any capabilities we expect to see in the qSupported response
+ m_supports_qXfer_auxv_read = eLazyBoolNo;
+ m_supports_qXfer_libraries_read = eLazyBoolNo;
+ m_supports_qXfer_libraries_svr4_read = eLazyBoolNo;
+ m_supports_augmented_libraries_svr4_read = eLazyBoolNo;
+ m_supports_qXfer_features_read = eLazyBoolNo;
+ m_supports_qXfer_memory_map_read = eLazyBoolNo;
+ m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
+ // not, we assume no limit
+
+ // build the qSupported packet
+ std::vector<std::string> features = {"xmlRegisters=i386,arm,mips"};
+ StreamString packet;
+ packet.PutCString("qSupported");
+ for (uint32_t i = 0; i < features.size(); ++i) {
+ packet.PutCString(i == 0 ? ":" : ";");
+ packet.PutCString(features[i]);
+ }
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response,
+ /*send_async=*/false) ==
+ PacketResult::Success) {
+ const char *response_cstr = response.GetStringRef().c_str();
+
+ // Hang on to the qSupported packet, so that platforms can do custom
+ // configuration of the transport before attaching/launching the process.
+ m_qSupported_response = response_cstr;
+
+ if (::strstr(response_cstr, "qXfer:auxv:read+"))
+ m_supports_qXfer_auxv_read = eLazyBoolYes;
+ if (::strstr(response_cstr, "qXfer:libraries-svr4:read+"))
+ m_supports_qXfer_libraries_svr4_read = eLazyBoolYes;
+ if (::strstr(response_cstr, "augmented-libraries-svr4-read")) {
+ m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; // implied
+ m_supports_augmented_libraries_svr4_read = eLazyBoolYes;
+ }
+ if (::strstr(response_cstr, "qXfer:libraries:read+"))
+ m_supports_qXfer_libraries_read = eLazyBoolYes;
+ if (::strstr(response_cstr, "qXfer:features:read+"))
+ m_supports_qXfer_features_read = eLazyBoolYes;
+ if (::strstr(response_cstr, "qXfer:memory-map:read+"))
+ m_supports_qXfer_memory_map_read = eLazyBoolYes;
+
+ // Look for a list of compressions in the features list e.g.
+ // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-
+ // deflate,lzma
+ const char *features_list = ::strstr(response_cstr, "qXfer:features:");
+ if (features_list) {
+ const char *compressions =
+ ::strstr(features_list, "SupportedCompressions=");
+ if (compressions) {
+ std::vector<std::string> supported_compressions;
+ compressions += sizeof("SupportedCompressions=") - 1;
+ const char *end_of_compressions = strchr(compressions, ';');
+ if (end_of_compressions == nullptr) {
+ end_of_compressions = strchr(compressions, '\0');
+ }
+ const char *current_compression = compressions;
+ while (current_compression < end_of_compressions) {
+ const char *next_compression_name = strchr(current_compression, ',');
+ const char *end_of_this_word = next_compression_name;
+ if (next_compression_name == nullptr ||
+ end_of_compressions < next_compression_name) {
+ end_of_this_word = end_of_compressions;
+ }
+
+ if (end_of_this_word) {
+ if (end_of_this_word == current_compression) {
+ current_compression++;
+ } else {
+ std::string this_compression(
+ current_compression, end_of_this_word - current_compression);
+ supported_compressions.push_back(this_compression);
+ current_compression = end_of_this_word + 1;
+ }
+ } else {
+ supported_compressions.push_back(current_compression);
+ current_compression = end_of_compressions;
+ }
+ }
+
+ if (supported_compressions.size() > 0) {
+ MaybeEnableCompression(supported_compressions);
+ }
+ }
+ }
+
+ if (::strstr(response_cstr, "qEcho"))
+ m_supports_qEcho = eLazyBoolYes;
+ else
+ m_supports_qEcho = eLazyBoolNo;
+
+ if (::strstr(response_cstr, "QPassSignals+"))
+ m_supports_QPassSignals = eLazyBoolYes;
+ else
+ m_supports_QPassSignals = eLazyBoolNo;
+
+ const char *packet_size_str = ::strstr(response_cstr, "PacketSize=");
+ if (packet_size_str) {
+ StringExtractorGDBRemote packet_response(packet_size_str +
+ strlen("PacketSize="));
+ m_max_packet_size =
+ packet_response.GetHexMaxU64(/*little_endian=*/false, UINT64_MAX);
+ if (m_max_packet_size == 0) {
+ m_max_packet_size = UINT64_MAX; // Must have been a garbled response
+ Log *log(
+ ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("Garbled PacketSize spec in qSupported response");
+ }
+ }
+ }
+}
+
+bool GDBRemoteCommunicationClient::GetThreadSuffixSupported() {
+ if (m_supports_thread_suffix == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_thread_suffix = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("QThreadSuffixSupported", response,
+ false) == PacketResult::Success) {
+ if (response.IsOKResponse())
+ m_supports_thread_suffix = eLazyBoolYes;
+ }
+ }
+ return m_supports_thread_suffix;
+}
+bool GDBRemoteCommunicationClient::GetVContSupported(char flavor) {
+ if (m_supports_vCont_c == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_vCont_any = eLazyBoolNo;
+ m_supports_vCont_all = eLazyBoolNo;
+ m_supports_vCont_c = eLazyBoolNo;
+ m_supports_vCont_C = eLazyBoolNo;
+ m_supports_vCont_s = eLazyBoolNo;
+ m_supports_vCont_S = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("vCont?", response, false) ==
+ PacketResult::Success) {
+ const char *response_cstr = response.GetStringRef().c_str();
+ if (::strstr(response_cstr, ";c"))
+ m_supports_vCont_c = eLazyBoolYes;
+
+ if (::strstr(response_cstr, ";C"))
+ m_supports_vCont_C = eLazyBoolYes;
+
+ if (::strstr(response_cstr, ";s"))
+ m_supports_vCont_s = eLazyBoolYes;
+
+ if (::strstr(response_cstr, ";S"))
+ m_supports_vCont_S = eLazyBoolYes;
+
+ if (m_supports_vCont_c == eLazyBoolYes &&
+ m_supports_vCont_C == eLazyBoolYes &&
+ m_supports_vCont_s == eLazyBoolYes &&
+ m_supports_vCont_S == eLazyBoolYes) {
+ m_supports_vCont_all = eLazyBoolYes;
+ }
+
+ if (m_supports_vCont_c == eLazyBoolYes ||
+ m_supports_vCont_C == eLazyBoolYes ||
+ m_supports_vCont_s == eLazyBoolYes ||
+ m_supports_vCont_S == eLazyBoolYes) {
+ m_supports_vCont_any = eLazyBoolYes;
+ }
+ }
+ }
+
+ switch (flavor) {
+ case 'a':
+ return m_supports_vCont_any;
+ case 'A':
+ return m_supports_vCont_all;
+ case 'c':
+ return m_supports_vCont_c;
+ case 'C':
+ return m_supports_vCont_C;
+ case 's':
+ return m_supports_vCont_s;
+ case 'S':
+ return m_supports_vCont_S;
+ default:
+ break;
+ }
+ return false;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationClient::SendThreadSpecificPacketAndWaitForResponse(
+ lldb::tid_t tid, StreamString &&payload, StringExtractorGDBRemote &response,
+ bool send_async) {
+ Lock lock(*this, send_async);
+ if (!lock) {
+ if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(
+ GDBR_LOG_PROCESS | GDBR_LOG_PACKETS))
+ log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex "
+ "for %s packet.",
+ __FUNCTION__, payload.GetData());
+ return PacketResult::ErrorNoSequenceLock;
+ }
+
+ if (GetThreadSuffixSupported())
+ payload.Printf(";thread:%4.4" PRIx64 ";", tid);
+ else {
+ if (!SetCurrentThread(tid))
+ return PacketResult::ErrorSendFailed;
+ }
+
+ return SendPacketAndWaitForResponseNoLock(payload.GetString(), response);
+}
+
+// Check if the target supports 'p' packet. It sends out a 'p' packet and
+// checks the response. A normal packet will tell us that support is available.
+//
+// Takes a valid thread ID because p needs to apply to a thread.
+bool GDBRemoteCommunicationClient::GetpPacketSupported(lldb::tid_t tid) {
+ if (m_supports_p == eLazyBoolCalculate) {
+ m_supports_p = eLazyBoolNo;
+ StreamString payload;
+ payload.PutCString("p0");
+ StringExtractorGDBRemote response;
+ if (SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload),
+ response, false) ==
+ PacketResult::Success &&
+ response.IsNormalResponse()) {
+ m_supports_p = eLazyBoolYes;
+ }
+ }
+ return m_supports_p;
+}
+
+StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() {
+ // Get information on all threads at one using the "jThreadsInfo" packet
+ StructuredData::ObjectSP object_sp;
+
+ if (m_supports_jThreadsInfo) {
+ StringExtractorGDBRemote response;
+ response.SetResponseValidatorToJSON();
+ if (SendPacketAndWaitForResponse("jThreadsInfo", response, false) ==
+ PacketResult::Success) {
+ if (response.IsUnsupportedResponse()) {
+ m_supports_jThreadsInfo = false;
+ } else if (!response.Empty()) {
+ object_sp = StructuredData::ParseJSON(response.GetStringRef());
+ }
+ }
+ }
+ return object_sp;
+}
+
+bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() {
+ if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_jThreadExtendedInfo = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("jThreadExtendedInfo:", response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_supports_jThreadExtendedInfo = eLazyBoolYes;
+ }
+ }
+ }
+ return m_supports_jThreadExtendedInfo;
+}
+
+void GDBRemoteCommunicationClient::EnableErrorStringInPacket() {
+ if (m_supports_error_string_reply == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ // We try to enable error strings in remote packets but if we fail, we just
+ // work in the older way.
+ m_supports_error_string_reply = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("QEnableErrorStrings", response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_supports_error_string_reply = eLazyBoolYes;
+ }
+ }
+ }
+}
+
+bool GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported() {
+ if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("jGetLoadedDynamicLibrariesInfos:",
+ response,
+ false) == PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolYes;
+ }
+ }
+ }
+ return m_supports_jLoadedDynamicLibrariesInfos;
+}
+
+bool GDBRemoteCommunicationClient::GetSharedCacheInfoSupported() {
+ if (m_supports_jGetSharedCacheInfo == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_jGetSharedCacheInfo = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("jGetSharedCacheInfo:", response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_supports_jGetSharedCacheInfo = eLazyBoolYes;
+ }
+ }
+ }
+ return m_supports_jGetSharedCacheInfo;
+}
+
+bool GDBRemoteCommunicationClient::GetxPacketSupported() {
+ if (m_supports_x == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_x = eLazyBoolNo;
+ char packet[256];
+ snprintf(packet, sizeof(packet), "x0,0");
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ m_supports_x = eLazyBoolYes;
+ }
+ }
+ return m_supports_x;
+}
+
+GDBRemoteCommunicationClient::PacketResult
+GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses(
+ const char *payload_prefix, std::string &response_string) {
+ Lock lock(*this, false);
+ if (!lock) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS |
+ GDBR_LOG_PACKETS));
+ if (log)
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "packets with prefix '%s'",
+ payload_prefix);
+ return PacketResult::ErrorNoSequenceLock;
+ }
+
+ response_string = "";
+ std::string payload_prefix_str(payload_prefix);
+ unsigned int response_size = 0x1000;
+ if (response_size > GetRemoteMaxPacketSize()) { // May send qSupported packet
+ response_size = GetRemoteMaxPacketSize();
+ }
+
+ for (unsigned int offset = 0; true; offset += response_size) {
+ StringExtractorGDBRemote this_response;
+ // Construct payload
+ char sizeDescriptor[128];
+ snprintf(sizeDescriptor, sizeof(sizeDescriptor), "%x,%x", offset,
+ response_size);
+ PacketResult result = SendPacketAndWaitForResponseNoLock(
+ payload_prefix_str + sizeDescriptor, this_response);
+ if (result != PacketResult::Success)
+ return result;
+
+ const std::string &this_string = this_response.GetStringRef();
+
+ // Check for m or l as first character; l seems to mean this is the last
+ // chunk
+ char first_char = *this_string.c_str();
+ if (first_char != 'm' && first_char != 'l') {
+ return PacketResult::ErrorReplyInvalid;
+ }
+ // Concatenate the result so far (skipping 'm' or 'l')
+ response_string.append(this_string, 1, std::string::npos);
+ if (first_char == 'l')
+ // We're done
+ return PacketResult::Success;
+ }
+}
+
+lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) {
+ if (allow_lazy && m_curr_pid_is_valid == eLazyBoolYes)
+ return m_curr_pid;
+
+ // First try to retrieve the pid via the qProcessInfo request.
+ GetCurrentProcessInfo(allow_lazy);
+ if (m_curr_pid_is_valid == eLazyBoolYes) {
+ // We really got it.
+ return m_curr_pid;
+ } else {
+ // If we don't get a response for qProcessInfo, check if $qC gives us a
+ // result. $qC only returns a real process id on older debugserver and
+ // lldb-platform stubs. The gdb remote protocol documents $qC as returning
+ // the thread id, which newer debugserver and lldb-gdbserver stubs return
+ // correctly.
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qC", response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() == 'Q') {
+ if (response.GetChar() == 'C') {
+ m_curr_pid = response.GetHexMaxU32(false, LLDB_INVALID_PROCESS_ID);
+ if (m_curr_pid != LLDB_INVALID_PROCESS_ID) {
+ m_curr_pid_is_valid = eLazyBoolYes;
+ return m_curr_pid;
+ }
+ }
+ }
+ }
+
+ // If we don't get a response for $qC, check if $qfThreadID gives us a
+ // result.
+ if (m_curr_pid == LLDB_INVALID_PROCESS_ID) {
+ std::vector<lldb::tid_t> thread_ids;
+ bool sequence_mutex_unavailable;
+ size_t size;
+ size = GetCurrentThreadIDs(thread_ids, sequence_mutex_unavailable);
+ if (size && !sequence_mutex_unavailable) {
+ m_curr_pid = thread_ids.front();
+ m_curr_pid_is_valid = eLazyBoolYes;
+ return m_curr_pid;
+ }
+ }
+ }
+
+ return LLDB_INVALID_PROCESS_ID;
+}
+
+bool GDBRemoteCommunicationClient::GetLaunchSuccess(std::string &error_str) {
+ error_str.clear();
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qLaunchSuccess", response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return true;
+ if (response.GetChar() == 'E') {
+ // A string the describes what failed when launching...
+ error_str = response.GetStringRef().substr(1);
+ } else {
+ error_str.assign("unknown error occurred launching process");
+ }
+ } else {
+ error_str.assign("timed out waiting for app to launch");
+ }
+ return false;
+}
+
+int GDBRemoteCommunicationClient::SendArgumentsPacket(
+ const ProcessLaunchInfo &launch_info) {
+ // Since we don't get the send argv0 separate from the executable path, we
+ // need to make sure to use the actual executable path found in the
+ // launch_info...
+ std::vector<const char *> argv;
+ FileSpec exe_file = launch_info.GetExecutableFile();
+ std::string exe_path;
+ const char *arg = nullptr;
+ const Args &launch_args = launch_info.GetArguments();
+ if (exe_file)
+ exe_path = exe_file.GetPath(false);
+ else {
+ arg = launch_args.GetArgumentAtIndex(0);
+ if (arg)
+ exe_path = arg;
+ }
+ if (!exe_path.empty()) {
+ argv.push_back(exe_path.c_str());
+ for (uint32_t i = 1; (arg = launch_args.GetArgumentAtIndex(i)) != nullptr;
+ ++i) {
+ if (arg)
+ argv.push_back(arg);
+ }
+ }
+ if (!argv.empty()) {
+ StreamString packet;
+ packet.PutChar('A');
+ for (size_t i = 0, n = argv.size(); i < n; ++i) {
+ arg = argv[i];
+ const int arg_len = strlen(arg);
+ if (i > 0)
+ packet.PutChar(',');
+ packet.Printf("%i,%i,", arg_len * 2, (int)i);
+ packet.PutBytesAsRawHex8(arg, arg_len);
+ }
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SendEnvironment(const Environment &env) {
+ for (const auto &KV : env) {
+ int r = SendEnvironmentPacket(Environment::compose(KV).c_str());
+ if (r != 0)
+ return r;
+ }
+ return 0;
+}
+
+int GDBRemoteCommunicationClient::SendEnvironmentPacket(
+ char const *name_equal_value) {
+ if (name_equal_value && name_equal_value[0]) {
+ StreamString packet;
+ bool send_hex_encoding = false;
+ for (const char *p = name_equal_value; *p != '\0' && !send_hex_encoding;
+ ++p) {
+ if (isprint(*p)) {
+ switch (*p) {
+ case '$':
+ case '#':
+ case '*':
+ case '}':
+ send_hex_encoding = true;
+ break;
+ default:
+ break;
+ }
+ } else {
+ // We have non printable characters, lets hex encode this...
+ send_hex_encoding = true;
+ }
+ }
+
+ StringExtractorGDBRemote response;
+ if (send_hex_encoding) {
+ if (m_supports_QEnvironmentHexEncoded) {
+ packet.PutCString("QEnvironmentHexEncoded:");
+ packet.PutBytesAsRawHex8(name_equal_value, strlen(name_equal_value));
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ if (response.IsUnsupportedResponse())
+ m_supports_QEnvironmentHexEncoded = false;
+ }
+ }
+
+ } else if (m_supports_QEnvironment) {
+ packet.Printf("QEnvironment:%s", name_equal_value);
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ if (response.IsUnsupportedResponse())
+ m_supports_QEnvironment = false;
+ }
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SendLaunchArchPacket(char const *arch) {
+ if (arch && arch[0]) {
+ StreamString packet;
+ packet.Printf("QLaunchArch:%s", arch);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SendLaunchEventDataPacket(
+ char const *data, bool *was_supported) {
+ if (data && *data != '\0') {
+ StreamString packet;
+ packet.Printf("QSetProcessEvent:%s", data);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ if (was_supported)
+ *was_supported = true;
+ return 0;
+ } else if (response.IsUnsupportedResponse()) {
+ if (was_supported)
+ *was_supported = false;
+ return -1;
+ } else {
+ uint8_t error = response.GetError();
+ if (was_supported)
+ *was_supported = true;
+ if (error)
+ return error;
+ }
+ }
+ }
+ return -1;
+}
+
+llvm::VersionTuple GDBRemoteCommunicationClient::GetOSVersion() {
+ GetHostInfo();
+ return m_os_version;
+}
+
+bool GDBRemoteCommunicationClient::GetOSBuildString(std::string &s) {
+ if (GetHostInfo()) {
+ if (!m_os_build.empty()) {
+ s = m_os_build;
+ return true;
+ }
+ }
+ s.clear();
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetOSKernelDescription(std::string &s) {
+ if (GetHostInfo()) {
+ if (!m_os_kernel.empty()) {
+ s = m_os_kernel;
+ return true;
+ }
+ }
+ s.clear();
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetHostname(std::string &s) {
+ if (GetHostInfo()) {
+ if (!m_hostname.empty()) {
+ s = m_hostname;
+ return true;
+ }
+ }
+ s.clear();
+ return false;
+}
+
+ArchSpec GDBRemoteCommunicationClient::GetSystemArchitecture() {
+ if (GetHostInfo())
+ return m_host_arch;
+ return ArchSpec();
+}
+
+const lldb_private::ArchSpec &
+GDBRemoteCommunicationClient::GetProcessArchitecture() {
+ if (m_qProcessInfo_is_valid == eLazyBoolCalculate)
+ GetCurrentProcessInfo();
+ return m_process_arch;
+}
+
+bool GDBRemoteCommunicationClient::GetGDBServerVersion() {
+ if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) {
+ m_gdb_server_name.clear();
+ m_gdb_server_version = 0;
+ m_qGDBServerVersion_is_valid = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qGDBServerVersion", response, false) ==
+ PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ llvm::StringRef name, value;
+ bool success = false;
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("name")) {
+ success = true;
+ m_gdb_server_name = value;
+ } else if (name.equals("version")) {
+ llvm::StringRef major, minor;
+ std::tie(major, minor) = value.split('.');
+ if (!major.getAsInteger(0, m_gdb_server_version))
+ success = true;
+ }
+ }
+ if (success)
+ m_qGDBServerVersion_is_valid = eLazyBoolYes;
+ }
+ }
+ }
+ return m_qGDBServerVersion_is_valid == eLazyBoolYes;
+}
+
+void GDBRemoteCommunicationClient::MaybeEnableCompression(
+ std::vector<std::string> supported_compressions) {
+ CompressionType avail_type = CompressionType::None;
+ std::string avail_name;
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (avail_type == CompressionType::None) {
+ for (auto compression : supported_compressions) {
+ if (compression == "lzfse") {
+ avail_type = CompressionType::LZFSE;
+ avail_name = compression;
+ break;
+ }
+ }
+ }
+#endif
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (avail_type == CompressionType::None) {
+ for (auto compression : supported_compressions) {
+ if (compression == "zlib-deflate") {
+ avail_type = CompressionType::ZlibDeflate;
+ avail_name = compression;
+ break;
+ }
+ }
+ }
+#endif
+
+#if defined(HAVE_LIBZ)
+ if (avail_type == CompressionType::None) {
+ for (auto compression : supported_compressions) {
+ if (compression == "zlib-deflate") {
+ avail_type = CompressionType::ZlibDeflate;
+ avail_name = compression;
+ break;
+ }
+ }
+ }
+#endif
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (avail_type == CompressionType::None) {
+ for (auto compression : supported_compressions) {
+ if (compression == "lz4") {
+ avail_type = CompressionType::LZ4;
+ avail_name = compression;
+ break;
+ }
+ }
+ }
+#endif
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (avail_type == CompressionType::None) {
+ for (auto compression : supported_compressions) {
+ if (compression == "lzma") {
+ avail_type = CompressionType::LZMA;
+ avail_name = compression;
+ break;
+ }
+ }
+ }
+#endif
+
+ if (avail_type != CompressionType::None) {
+ StringExtractorGDBRemote response;
+ std::string packet = "QEnableCompression:type:" + avail_name + ";";
+ if (SendPacketAndWaitForResponse(packet, response, false) !=
+ PacketResult::Success)
+ return;
+
+ if (response.IsOKResponse()) {
+ m_compression_type = avail_type;
+ }
+ }
+}
+
+const char *GDBRemoteCommunicationClient::GetGDBServerProgramName() {
+ if (GetGDBServerVersion()) {
+ if (!m_gdb_server_name.empty())
+ return m_gdb_server_name.c_str();
+ }
+ return nullptr;
+}
+
+uint32_t GDBRemoteCommunicationClient::GetGDBServerProgramVersion() {
+ if (GetGDBServerVersion())
+ return m_gdb_server_version;
+ return 0;
+}
+
+bool GDBRemoteCommunicationClient::GetDefaultThreadId(lldb::tid_t &tid) {
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qC", response, false) !=
+ PacketResult::Success)
+ return false;
+
+ if (!response.IsNormalResponse())
+ return false;
+
+ if (response.GetChar() == 'Q' && response.GetChar() == 'C')
+ tid = response.GetHexMaxU32(true, -1);
+
+ return true;
+}
+
+bool GDBRemoteCommunicationClient::GetHostInfo(bool force) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS));
+
+ if (force || m_qHostInfo_is_valid == eLazyBoolCalculate) {
+ // host info computation can require DNS traffic and shelling out to external processes.
+ // Increase the timeout to account for that.
+ ScopedTimeout timeout(*this, seconds(10));
+ m_qHostInfo_is_valid = eLazyBoolNo;
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qHostInfo", response, false) ==
+ PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ uint32_t cpu = LLDB_INVALID_CPUTYPE;
+ uint32_t sub = 0;
+ std::string arch_name;
+ std::string os_name;
+ std::string vendor_name;
+ std::string triple;
+ std::string distribution_id;
+ uint32_t pointer_byte_size = 0;
+ ByteOrder byte_order = eByteOrderInvalid;
+ uint32_t num_keys_decoded = 0;
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("cputype")) {
+ // exception type in big endian hex
+ if (!value.getAsInteger(0, cpu))
+ ++num_keys_decoded;
+ } else if (name.equals("cpusubtype")) {
+ // exception count in big endian hex
+ if (!value.getAsInteger(0, sub))
+ ++num_keys_decoded;
+ } else if (name.equals("arch")) {
+ arch_name = value;
+ ++num_keys_decoded;
+ } else if (name.equals("triple")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(triple);
+ ++num_keys_decoded;
+ } else if (name.equals("distribution_id")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(distribution_id);
+ ++num_keys_decoded;
+ } else if (name.equals("os_build")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(m_os_build);
+ ++num_keys_decoded;
+ } else if (name.equals("hostname")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(m_hostname);
+ ++num_keys_decoded;
+ } else if (name.equals("os_kernel")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(m_os_kernel);
+ ++num_keys_decoded;
+ } else if (name.equals("ostype")) {
+ os_name = value;
+ ++num_keys_decoded;
+ } else if (name.equals("vendor")) {
+ vendor_name = value;
+ ++num_keys_decoded;
+ } else if (name.equals("endian")) {
+ byte_order = llvm::StringSwitch<lldb::ByteOrder>(value)
+ .Case("little", eByteOrderLittle)
+ .Case("big", eByteOrderBig)
+ .Case("pdp", eByteOrderPDP)
+ .Default(eByteOrderInvalid);
+ if (byte_order != eByteOrderInvalid)
+ ++num_keys_decoded;
+ } else if (name.equals("ptrsize")) {
+ if (!value.getAsInteger(0, pointer_byte_size))
+ ++num_keys_decoded;
+ } else if (name.equals("os_version") ||
+ name.equals(
+ "version")) // Older debugserver binaries used the
+ // "version" key instead of
+ // "os_version"...
+ {
+ if (!m_os_version.tryParse(value))
+ ++num_keys_decoded;
+ } else if (name.equals("watchpoint_exceptions_received")) {
+ m_watchpoints_trigger_after_instruction =
+ llvm::StringSwitch<LazyBool>(value)
+ .Case("before", eLazyBoolNo)
+ .Case("after", eLazyBoolYes)
+ .Default(eLazyBoolCalculate);
+ if (m_watchpoints_trigger_after_instruction != eLazyBoolCalculate)
+ ++num_keys_decoded;
+ } else if (name.equals("default_packet_timeout")) {
+ uint32_t timeout_seconds;
+ if (!value.getAsInteger(0, timeout_seconds)) {
+ m_default_packet_timeout = seconds(timeout_seconds);
+ SetPacketTimeout(m_default_packet_timeout);
+ ++num_keys_decoded;
+ }
+ }
+ }
+
+ if (num_keys_decoded > 0)
+ m_qHostInfo_is_valid = eLazyBoolYes;
+
+ if (triple.empty()) {
+ if (arch_name.empty()) {
+ if (cpu != LLDB_INVALID_CPUTYPE) {
+ m_host_arch.SetArchitecture(eArchTypeMachO, cpu, sub);
+ if (pointer_byte_size) {
+ assert(pointer_byte_size == m_host_arch.GetAddressByteSize());
+ }
+ if (byte_order != eByteOrderInvalid) {
+ assert(byte_order == m_host_arch.GetByteOrder());
+ }
+
+ if (!vendor_name.empty())
+ m_host_arch.GetTriple().setVendorName(
+ llvm::StringRef(vendor_name));
+ if (!os_name.empty())
+ m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name));
+ }
+ } else {
+ std::string triple;
+ triple += arch_name;
+ if (!vendor_name.empty() || !os_name.empty()) {
+ triple += '-';
+ if (vendor_name.empty())
+ triple += "unknown";
+ else
+ triple += vendor_name;
+ triple += '-';
+ if (os_name.empty())
+ triple += "unknown";
+ else
+ triple += os_name;
+ }
+ m_host_arch.SetTriple(triple.c_str());
+
+ llvm::Triple &host_triple = m_host_arch.GetTriple();
+ if (host_triple.getVendor() == llvm::Triple::Apple &&
+ host_triple.getOS() == llvm::Triple::Darwin) {
+ switch (m_host_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ host_triple.setOS(llvm::Triple::IOS);
+ break;
+ default:
+ host_triple.setOS(llvm::Triple::MacOSX);
+ break;
+ }
+ }
+ if (pointer_byte_size) {
+ assert(pointer_byte_size == m_host_arch.GetAddressByteSize());
+ }
+ if (byte_order != eByteOrderInvalid) {
+ assert(byte_order == m_host_arch.GetByteOrder());
+ }
+ }
+ } else {
+ m_host_arch.SetTriple(triple.c_str());
+ if (pointer_byte_size) {
+ assert(pointer_byte_size == m_host_arch.GetAddressByteSize());
+ }
+ if (byte_order != eByteOrderInvalid) {
+ assert(byte_order == m_host_arch.GetByteOrder());
+ }
+
+ if (log)
+ log->Printf("GDBRemoteCommunicationClient::%s parsed host "
+ "architecture as %s, triple as %s from triple text %s",
+ __FUNCTION__, m_host_arch.GetArchitectureName()
+ ? m_host_arch.GetArchitectureName()
+ : "<null-arch-name>",
+ m_host_arch.GetTriple().getTriple().c_str(),
+ triple.c_str());
+ }
+ if (!distribution_id.empty())
+ m_host_arch.SetDistributionId(distribution_id.c_str());
+ }
+ }
+ }
+ return m_qHostInfo_is_valid == eLazyBoolYes;
+}
+
+int GDBRemoteCommunicationClient::SendAttach(
+ lldb::pid_t pid, StringExtractorGDBRemote &response) {
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, pid);
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ assert(packet_len < (int)sizeof(packet));
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsErrorResponse())
+ return response.GetError();
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SendStdinNotification(const char *data,
+ size_t data_len) {
+ StreamString packet;
+ packet.PutCString("I");
+ packet.PutBytesAsRawHex8(data, data_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ return 0;
+ }
+ return response.GetError();
+}
+
+const lldb_private::ArchSpec &
+GDBRemoteCommunicationClient::GetHostArchitecture() {
+ if (m_qHostInfo_is_valid == eLazyBoolCalculate)
+ GetHostInfo();
+ return m_host_arch;
+}
+
+seconds GDBRemoteCommunicationClient::GetHostDefaultPacketTimeout() {
+ if (m_qHostInfo_is_valid == eLazyBoolCalculate)
+ GetHostInfo();
+ return m_default_packet_timeout;
+}
+
+addr_t GDBRemoteCommunicationClient::AllocateMemory(size_t size,
+ uint32_t permissions) {
+ if (m_supports_alloc_dealloc_memory != eLazyBoolNo) {
+ m_supports_alloc_dealloc_memory = eLazyBoolYes;
+ char packet[64];
+ const int packet_len = ::snprintf(
+ packet, sizeof(packet), "_M%" PRIx64 ",%s%s%s", (uint64_t)size,
+ permissions & lldb::ePermissionsReadable ? "r" : "",
+ permissions & lldb::ePermissionsWritable ? "w" : "",
+ permissions & lldb::ePermissionsExecutable ? "x" : "");
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsUnsupportedResponse())
+ m_supports_alloc_dealloc_memory = eLazyBoolNo;
+ else if (!response.IsErrorResponse())
+ return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
+ } else {
+ m_supports_alloc_dealloc_memory = eLazyBoolNo;
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool GDBRemoteCommunicationClient::DeallocateMemory(addr_t addr) {
+ if (m_supports_alloc_dealloc_memory != eLazyBoolNo) {
+ m_supports_alloc_dealloc_memory = eLazyBoolYes;
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "_m%" PRIx64, (uint64_t)addr);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsUnsupportedResponse())
+ m_supports_alloc_dealloc_memory = eLazyBoolNo;
+ else if (response.IsOKResponse())
+ return true;
+ } else {
+ m_supports_alloc_dealloc_memory = eLazyBoolNo;
+ }
+ }
+ return false;
+}
+
+Status GDBRemoteCommunicationClient::Detach(bool keep_stopped) {
+ Status error;
+
+ if (keep_stopped) {
+ if (m_supports_detach_stay_stopped == eLazyBoolCalculate) {
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:");
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success &&
+ response.IsOKResponse()) {
+ m_supports_detach_stay_stopped = eLazyBoolYes;
+ } else {
+ m_supports_detach_stay_stopped = eLazyBoolNo;
+ }
+ }
+
+ if (m_supports_detach_stay_stopped == eLazyBoolNo) {
+ error.SetErrorString("Stays stopped not supported by this target.");
+ return error;
+ } else {
+ StringExtractorGDBRemote response;
+ PacketResult packet_result =
+ SendPacketAndWaitForResponse("D1", response, false);
+ if (packet_result != PacketResult::Success)
+ error.SetErrorString("Sending extended disconnect packet failed.");
+ }
+ } else {
+ StringExtractorGDBRemote response;
+ PacketResult packet_result =
+ SendPacketAndWaitForResponse("D", response, false);
+ if (packet_result != PacketResult::Success)
+ error.SetErrorString("Sending disconnect packet failed.");
+ }
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
+ lldb::addr_t addr, lldb_private::MemoryRegionInfo &region_info) {
+ Status error;
+ region_info.Clear();
+
+ if (m_supports_memory_region_info != eLazyBoolNo) {
+ m_supports_memory_region_info = eLazyBoolYes;
+ char packet[64];
+ const int packet_len = ::snprintf(
+ packet, sizeof(packet), "qMemoryRegionInfo:%" PRIx64, (uint64_t)addr);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success &&
+ response.GetResponseType() == StringExtractorGDBRemote::eResponse) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ addr_t addr_value = LLDB_INVALID_ADDRESS;
+ bool success = true;
+ bool saw_permissions = false;
+ while (success && response.GetNameColonValue(name, value)) {
+ if (name.equals("start")) {
+ if (!value.getAsInteger(16, addr_value))
+ region_info.GetRange().SetRangeBase(addr_value);
+ } else if (name.equals("size")) {
+ if (!value.getAsInteger(16, addr_value))
+ region_info.GetRange().SetByteSize(addr_value);
+ } else if (name.equals("permissions") &&
+ region_info.GetRange().IsValid()) {
+ saw_permissions = true;
+ if (region_info.GetRange().Contains(addr)) {
+ if (value.find('r') != llvm::StringRef::npos)
+ region_info.SetReadable(MemoryRegionInfo::eYes);
+ else
+ region_info.SetReadable(MemoryRegionInfo::eNo);
+
+ if (value.find('w') != llvm::StringRef::npos)
+ region_info.SetWritable(MemoryRegionInfo::eYes);
+ else
+ region_info.SetWritable(MemoryRegionInfo::eNo);
+
+ if (value.find('x') != llvm::StringRef::npos)
+ region_info.SetExecutable(MemoryRegionInfo::eYes);
+ else
+ region_info.SetExecutable(MemoryRegionInfo::eNo);
+
+ region_info.SetMapped(MemoryRegionInfo::eYes);
+ } else {
+ // The reported region does not contain this address -- we're
+ // looking at an unmapped page
+ region_info.SetReadable(MemoryRegionInfo::eNo);
+ region_info.SetWritable(MemoryRegionInfo::eNo);
+ region_info.SetExecutable(MemoryRegionInfo::eNo);
+ region_info.SetMapped(MemoryRegionInfo::eNo);
+ }
+ } else if (name.equals("name")) {
+ StringExtractorGDBRemote name_extractor(value);
+ std::string name;
+ name_extractor.GetHexByteString(name);
+ region_info.SetName(name.c_str());
+ } else if (name.equals("error")) {
+ StringExtractorGDBRemote error_extractor(value);
+ std::string error_string;
+ // Now convert the HEX bytes into a string value
+ error_extractor.GetHexByteString(error_string);
+ error.SetErrorString(error_string.c_str());
+ }
+ }
+
+ if (region_info.GetRange().IsValid()) {
+ // We got a valid address range back but no permissions -- which means
+ // this is an unmapped page
+ if (!saw_permissions) {
+ region_info.SetReadable(MemoryRegionInfo::eNo);
+ region_info.SetWritable(MemoryRegionInfo::eNo);
+ region_info.SetExecutable(MemoryRegionInfo::eNo);
+ region_info.SetMapped(MemoryRegionInfo::eNo);
+ }
+ } else {
+ // We got an invalid address range back
+ error.SetErrorString("Server returned invalid range");
+ }
+ } else {
+ m_supports_memory_region_info = eLazyBoolNo;
+ }
+ }
+
+ if (m_supports_memory_region_info == eLazyBoolNo) {
+ error.SetErrorString("qMemoryRegionInfo is not supported");
+ }
+
+ // Try qXfer:memory-map:read to get region information not included in
+ // qMemoryRegionInfo
+ MemoryRegionInfo qXfer_region_info;
+ Status qXfer_error = GetQXferMemoryMapRegionInfo(addr, qXfer_region_info);
+
+ if (error.Fail()) {
+ // If qMemoryRegionInfo failed, but qXfer:memory-map:read succeeded, use
+ // the qXfer result as a fallback
+ if (qXfer_error.Success()) {
+ region_info = qXfer_region_info;
+ error.Clear();
+ } else {
+ region_info.Clear();
+ }
+ } else if (qXfer_error.Success()) {
+ // If both qMemoryRegionInfo and qXfer:memory-map:read succeeded, and if
+ // both regions are the same range, update the result to include the flash-
+ // memory information that is specific to the qXfer result.
+ if (region_info.GetRange() == qXfer_region_info.GetRange()) {
+ region_info.SetFlash(qXfer_region_info.GetFlash());
+ region_info.SetBlocksize(qXfer_region_info.GetBlocksize());
+ }
+ }
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::GetQXferMemoryMapRegionInfo(
+ lldb::addr_t addr, MemoryRegionInfo &region) {
+ Status error = LoadQXferMemoryMap();
+ if (!error.Success())
+ return error;
+ for (const auto &map_region : m_qXfer_memory_map) {
+ if (map_region.GetRange().Contains(addr)) {
+ region = map_region;
+ return error;
+ }
+ }
+ error.SetErrorString("Region not found");
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::LoadQXferMemoryMap() {
+
+ Status error;
+
+ if (m_qXfer_memory_map_loaded)
+ // Already loaded, return success
+ return error;
+
+ if (!XMLDocument::XMLEnabled()) {
+ error.SetErrorString("XML is not supported");
+ return error;
+ }
+
+ if (!GetQXferMemoryMapReadSupported()) {
+ error.SetErrorString("Memory map is not supported");
+ return error;
+ }
+
+ std::string xml;
+ lldb_private::Status lldberr;
+ if (!ReadExtFeature(ConstString("memory-map"), ConstString(""), xml,
+ lldberr)) {
+ error.SetErrorString("Failed to read memory map");
+ return error;
+ }
+
+ XMLDocument xml_document;
+
+ if (!xml_document.ParseMemory(xml.c_str(), xml.size())) {
+ error.SetErrorString("Failed to parse memory map xml");
+ return error;
+ }
+
+ XMLNode map_node = xml_document.GetRootElement("memory-map");
+ if (!map_node) {
+ error.SetErrorString("Invalid root node in memory map xml");
+ return error;
+ }
+
+ m_qXfer_memory_map.clear();
+
+ map_node.ForEachChildElement([this](const XMLNode &memory_node) -> bool {
+ if (!memory_node.IsElement())
+ return true;
+ if (memory_node.GetName() != "memory")
+ return true;
+ auto type = memory_node.GetAttributeValue("type", "");
+ uint64_t start;
+ uint64_t length;
+ if (!memory_node.GetAttributeValueAsUnsigned("start", start))
+ return true;
+ if (!memory_node.GetAttributeValueAsUnsigned("length", length))
+ return true;
+ MemoryRegionInfo region;
+ region.GetRange().SetRangeBase(start);
+ region.GetRange().SetByteSize(length);
+ if (type == "rom") {
+ region.SetReadable(MemoryRegionInfo::eYes);
+ this->m_qXfer_memory_map.push_back(region);
+ } else if (type == "ram") {
+ region.SetReadable(MemoryRegionInfo::eYes);
+ region.SetWritable(MemoryRegionInfo::eYes);
+ this->m_qXfer_memory_map.push_back(region);
+ } else if (type == "flash") {
+ region.SetFlash(MemoryRegionInfo::eYes);
+ memory_node.ForEachChildElement(
+ [&region](const XMLNode &prop_node) -> bool {
+ if (!prop_node.IsElement())
+ return true;
+ if (prop_node.GetName() != "property")
+ return true;
+ auto propname = prop_node.GetAttributeValue("name", "");
+ if (propname == "blocksize") {
+ uint64_t blocksize;
+ if (prop_node.GetElementTextAsUnsigned(blocksize))
+ region.SetBlocksize(blocksize);
+ }
+ return true;
+ });
+ this->m_qXfer_memory_map.push_back(region);
+ }
+ return true;
+ });
+
+ m_qXfer_memory_map_loaded = true;
+
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::GetWatchpointSupportInfo(uint32_t &num) {
+ Status error;
+
+ if (m_supports_watchpoint_support_info == eLazyBoolYes) {
+ num = m_num_supported_hardware_watchpoints;
+ return error;
+ }
+
+ // Set num to 0 first.
+ num = 0;
+ if (m_supports_watchpoint_support_info != eLazyBoolNo) {
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qWatchpointSupportInfo:");
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ m_supports_watchpoint_support_info = eLazyBoolYes;
+ llvm::StringRef name;
+ llvm::StringRef value;
+ bool found_num_field = false;
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("num")) {
+ value.getAsInteger(0, m_num_supported_hardware_watchpoints);
+ num = m_num_supported_hardware_watchpoints;
+ found_num_field = true;
+ }
+ }
+ if (!found_num_field) {
+ m_supports_watchpoint_support_info = eLazyBoolNo;
+ }
+ } else {
+ m_supports_watchpoint_support_info = eLazyBoolNo;
+ }
+ }
+
+ if (m_supports_watchpoint_support_info == eLazyBoolNo) {
+ error.SetErrorString("qWatchpointSupportInfo is not supported");
+ }
+ return error;
+}
+
+lldb_private::Status GDBRemoteCommunicationClient::GetWatchpointSupportInfo(
+ uint32_t &num, bool &after, const ArchSpec &arch) {
+ Status error(GetWatchpointSupportInfo(num));
+ if (error.Success())
+ error = GetWatchpointsTriggerAfterInstruction(after, arch);
+ return error;
+}
+
+lldb_private::Status
+GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction(
+ bool &after, const ArchSpec &arch) {
+ Status error;
+ llvm::Triple triple = arch.GetTriple();
+
+ // we assume watchpoints will happen after running the relevant opcode and we
+ // only want to override this behavior if we have explicitly received a
+ // qHostInfo telling us otherwise
+ if (m_qHostInfo_is_valid != eLazyBoolYes) {
+ // On targets like MIPS and ppc64, watchpoint exceptions are always
+ // generated before the instruction is executed. The connected target may
+ // not support qHostInfo or qWatchpointSupportInfo packets.
+ after = !(triple.isMIPS() || triple.isPPC64());
+ } else {
+ // For MIPS and ppc64, set m_watchpoints_trigger_after_instruction to
+ // eLazyBoolNo if it is not calculated before.
+ if (m_watchpoints_trigger_after_instruction == eLazyBoolCalculate &&
+ (triple.isMIPS() || triple.isPPC64()))
+ m_watchpoints_trigger_after_instruction = eLazyBoolNo;
+
+ after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo);
+ }
+ return error;
+}
+
+int GDBRemoteCommunicationClient::SetSTDIN(const FileSpec &file_spec) {
+ if (file_spec) {
+ std::string path{file_spec.GetPath(false)};
+ StreamString packet;
+ packet.PutCString("QSetSTDIN:");
+ packet.PutStringAsRawHex8(path);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SetSTDOUT(const FileSpec &file_spec) {
+ if (file_spec) {
+ std::string path{file_spec.GetPath(false)};
+ StreamString packet;
+ packet.PutCString("QSetSTDOUT:");
+ packet.PutStringAsRawHex8(path);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SetSTDERR(const FileSpec &file_spec) {
+ if (file_spec) {
+ std::string path{file_spec.GetPath(false)};
+ StreamString packet;
+ packet.PutCString("QSetSTDERR:");
+ packet.PutStringAsRawHex8(path);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+bool GDBRemoteCommunicationClient::GetWorkingDir(FileSpec &working_dir) {
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qGetWorkingDir", response, false) ==
+ PacketResult::Success) {
+ if (response.IsUnsupportedResponse())
+ return false;
+ if (response.IsErrorResponse())
+ return false;
+ std::string cwd;
+ response.GetHexByteString(cwd);
+ working_dir.SetFile(cwd, GetHostArchitecture().GetTriple());
+ return !cwd.empty();
+ }
+ return false;
+}
+
+int GDBRemoteCommunicationClient::SetWorkingDir(const FileSpec &working_dir) {
+ if (working_dir) {
+ std::string path{working_dir.GetPath(false)};
+ StreamString packet;
+ packet.PutCString("QSetWorkingDir:");
+ packet.PutStringAsRawHex8(path);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SetDisableASLR(bool enable) {
+ char packet[32];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "QSetDisableASLR:%i", enable ? 1 : 0);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SetDetachOnError(bool enable) {
+ char packet[32];
+ const int packet_len = ::snprintf(packet, sizeof(packet),
+ "QSetDetachOnError:%i", enable ? 1 : 0);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ return -1;
+}
+
+bool GDBRemoteCommunicationClient::DecodeProcessInfoResponse(
+ StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info) {
+ if (response.IsNormalResponse()) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ StringExtractor extractor;
+
+ uint32_t cpu = LLDB_INVALID_CPUTYPE;
+ uint32_t sub = 0;
+ std::string vendor;
+ std::string os_type;
+
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("pid")) {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ value.getAsInteger(0, pid);
+ process_info.SetProcessID(pid);
+ } else if (name.equals("ppid")) {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ value.getAsInteger(0, pid);
+ process_info.SetParentProcessID(pid);
+ } else if (name.equals("uid")) {
+ uint32_t uid = UINT32_MAX;
+ value.getAsInteger(0, uid);
+ process_info.SetUserID(uid);
+ } else if (name.equals("euid")) {
+ uint32_t uid = UINT32_MAX;
+ value.getAsInteger(0, uid);
+ process_info.SetEffectiveGroupID(uid);
+ } else if (name.equals("gid")) {
+ uint32_t gid = UINT32_MAX;
+ value.getAsInteger(0, gid);
+ process_info.SetGroupID(gid);
+ } else if (name.equals("egid")) {
+ uint32_t gid = UINT32_MAX;
+ value.getAsInteger(0, gid);
+ process_info.SetEffectiveGroupID(gid);
+ } else if (name.equals("triple")) {
+ StringExtractor extractor(value);
+ std::string triple;
+ extractor.GetHexByteString(triple);
+ process_info.GetArchitecture().SetTriple(triple.c_str());
+ } else if (name.equals("name")) {
+ StringExtractor extractor(value);
+ // The process name from ASCII hex bytes since we can't control the
+ // characters in a process name
+ std::string name;
+ extractor.GetHexByteString(name);
+ process_info.GetExecutableFile().SetFile(name, FileSpec::Style::native);
+ } else if (name.equals("cputype")) {
+ value.getAsInteger(0, cpu);
+ } else if (name.equals("cpusubtype")) {
+ value.getAsInteger(0, sub);
+ } else if (name.equals("vendor")) {
+ vendor = value;
+ } else if (name.equals("ostype")) {
+ os_type = value;
+ }
+ }
+
+ if (cpu != LLDB_INVALID_CPUTYPE && !vendor.empty() && !os_type.empty()) {
+ if (vendor == "apple") {
+ process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu,
+ sub);
+ process_info.GetArchitecture().GetTriple().setVendorName(
+ llvm::StringRef(vendor));
+ process_info.GetArchitecture().GetTriple().setOSName(
+ llvm::StringRef(os_type));
+ }
+ }
+
+ if (process_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
+ return true;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetProcessInfo(
+ lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ process_info.Clear();
+
+ if (m_supports_qProcessInfoPID) {
+ char packet[32];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qProcessInfoPID:%" PRIu64, pid);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ return DecodeProcessInfoResponse(response, process_info);
+ } else {
+ m_supports_qProcessInfoPID = false;
+ return false;
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS |
+ GDBR_LOG_PACKETS));
+
+ if (allow_lazy) {
+ if (m_qProcessInfo_is_valid == eLazyBoolYes)
+ return true;
+ if (m_qProcessInfo_is_valid == eLazyBoolNo)
+ return false;
+ }
+
+ GetHostInfo();
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qProcessInfo", response, false) ==
+ PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ uint32_t cpu = LLDB_INVALID_CPUTYPE;
+ uint32_t sub = 0;
+ std::string arch_name;
+ std::string os_name;
+ std::string vendor_name;
+ std::string triple;
+ std::string elf_abi;
+ uint32_t pointer_byte_size = 0;
+ StringExtractor extractor;
+ ByteOrder byte_order = eByteOrderInvalid;
+ uint32_t num_keys_decoded = 0;
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("cputype")) {
+ if (!value.getAsInteger(16, cpu))
+ ++num_keys_decoded;
+ } else if (name.equals("cpusubtype")) {
+ if (!value.getAsInteger(16, sub))
+ ++num_keys_decoded;
+ } else if (name.equals("triple")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(triple);
+ ++num_keys_decoded;
+ } else if (name.equals("ostype")) {
+ os_name = value;
+ ++num_keys_decoded;
+ } else if (name.equals("vendor")) {
+ vendor_name = value;
+ ++num_keys_decoded;
+ } else if (name.equals("endian")) {
+ byte_order = llvm::StringSwitch<lldb::ByteOrder>(value)
+ .Case("little", eByteOrderLittle)
+ .Case("big", eByteOrderBig)
+ .Case("pdp", eByteOrderPDP)
+ .Default(eByteOrderInvalid);
+ if (byte_order != eByteOrderInvalid)
+ ++num_keys_decoded;
+ } else if (name.equals("ptrsize")) {
+ if (!value.getAsInteger(16, pointer_byte_size))
+ ++num_keys_decoded;
+ } else if (name.equals("pid")) {
+ if (!value.getAsInteger(16, pid))
+ ++num_keys_decoded;
+ } else if (name.equals("elf_abi")) {
+ elf_abi = value;
+ ++num_keys_decoded;
+ }
+ }
+ if (num_keys_decoded > 0)
+ m_qProcessInfo_is_valid = eLazyBoolYes;
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ m_curr_pid_is_valid = eLazyBoolYes;
+ m_curr_pid = pid;
+ }
+
+ // Set the ArchSpec from the triple if we have it.
+ if (!triple.empty()) {
+ m_process_arch.SetTriple(triple.c_str());
+ m_process_arch.SetFlags(elf_abi);
+ if (pointer_byte_size) {
+ assert(pointer_byte_size == m_process_arch.GetAddressByteSize());
+ }
+ } else if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() &&
+ !vendor_name.empty()) {
+ llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name);
+
+ assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat);
+ assert(triple.getObjectFormat() != llvm::Triple::Wasm);
+ assert(triple.getObjectFormat() != llvm::Triple::XCOFF);
+ switch (triple.getObjectFormat()) {
+ case llvm::Triple::MachO:
+ m_process_arch.SetArchitecture(eArchTypeMachO, cpu, sub);
+ break;
+ case llvm::Triple::ELF:
+ m_process_arch.SetArchitecture(eArchTypeELF, cpu, sub);
+ break;
+ case llvm::Triple::COFF:
+ m_process_arch.SetArchitecture(eArchTypeCOFF, cpu, sub);
+ break;
+ case llvm::Triple::Wasm:
+ case llvm::Triple::XCOFF:
+ if (log)
+ log->Printf("error: not supported target architecture");
+ return false;
+ case llvm::Triple::UnknownObjectFormat:
+ if (log)
+ log->Printf("error: failed to determine target architecture");
+ return false;
+ }
+
+ if (pointer_byte_size) {
+ assert(pointer_byte_size == m_process_arch.GetAddressByteSize());
+ }
+ if (byte_order != eByteOrderInvalid) {
+ assert(byte_order == m_process_arch.GetByteOrder());
+ }
+ m_process_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name));
+ m_process_arch.GetTriple().setOSName(llvm::StringRef(os_name));
+ m_host_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name));
+ m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name));
+ }
+ return true;
+ }
+ } else {
+ m_qProcessInfo_is_valid = eLazyBoolNo;
+ }
+
+ return false;
+}
+
+uint32_t GDBRemoteCommunicationClient::FindProcesses(
+ const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ process_infos.Clear();
+
+ if (m_supports_qfProcessInfo) {
+ StreamString packet;
+ packet.PutCString("qfProcessInfo");
+ if (!match_info.MatchAllProcesses()) {
+ packet.PutChar(':');
+ const char *name = match_info.GetProcessInfo().GetName();
+ bool has_name_match = false;
+ if (name && name[0]) {
+ has_name_match = true;
+ NameMatch name_match_type = match_info.GetNameMatchType();
+ switch (name_match_type) {
+ case NameMatch::Ignore:
+ has_name_match = false;
+ break;
+
+ case NameMatch::Equals:
+ packet.PutCString("name_match:equals;");
+ break;
+
+ case NameMatch::Contains:
+ packet.PutCString("name_match:contains;");
+ break;
+
+ case NameMatch::StartsWith:
+ packet.PutCString("name_match:starts_with;");
+ break;
+
+ case NameMatch::EndsWith:
+ packet.PutCString("name_match:ends_with;");
+ break;
+
+ case NameMatch::RegularExpression:
+ packet.PutCString("name_match:regex;");
+ break;
+ }
+ if (has_name_match) {
+ packet.PutCString("name:");
+ packet.PutBytesAsRawHex8(name, ::strlen(name));
+ packet.PutChar(';');
+ }
+ }
+
+ if (match_info.GetProcessInfo().ProcessIDIsValid())
+ packet.Printf("pid:%" PRIu64 ";",
+ match_info.GetProcessInfo().GetProcessID());
+ if (match_info.GetProcessInfo().ParentProcessIDIsValid())
+ packet.Printf("parent_pid:%" PRIu64 ";",
+ match_info.GetProcessInfo().GetParentProcessID());
+ if (match_info.GetProcessInfo().UserIDIsValid())
+ packet.Printf("uid:%u;", match_info.GetProcessInfo().GetUserID());
+ if (match_info.GetProcessInfo().GroupIDIsValid())
+ packet.Printf("gid:%u;", match_info.GetProcessInfo().GetGroupID());
+ if (match_info.GetProcessInfo().EffectiveUserIDIsValid())
+ packet.Printf("euid:%u;",
+ match_info.GetProcessInfo().GetEffectiveUserID());
+ if (match_info.GetProcessInfo().EffectiveGroupIDIsValid())
+ packet.Printf("egid:%u;",
+ match_info.GetProcessInfo().GetEffectiveGroupID());
+ if (match_info.GetProcessInfo().EffectiveGroupIDIsValid())
+ packet.Printf("all_users:%u;", match_info.GetMatchAllUsers() ? 1 : 0);
+ if (match_info.GetProcessInfo().GetArchitecture().IsValid()) {
+ const ArchSpec &match_arch =
+ match_info.GetProcessInfo().GetArchitecture();
+ const llvm::Triple &triple = match_arch.GetTriple();
+ packet.PutCString("triple:");
+ packet.PutCString(triple.getTriple());
+ packet.PutChar(';');
+ }
+ }
+ StringExtractorGDBRemote response;
+ // Increase timeout as the first qfProcessInfo packet takes a long time on
+ // Android. The value of 1min was arrived at empirically.
+ ScopedTimeout timeout(*this, minutes(1));
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ do {
+ ProcessInstanceInfo process_info;
+ if (!DecodeProcessInfoResponse(response, process_info))
+ break;
+ process_infos.Append(process_info);
+ response.GetStringRef().clear();
+ response.SetFilePos(0);
+ } while (SendPacketAndWaitForResponse("qsProcessInfo", response, false) ==
+ PacketResult::Success);
+ } else {
+ m_supports_qfProcessInfo = false;
+ return 0;
+ }
+ }
+ return process_infos.GetSize();
+}
+
+bool GDBRemoteCommunicationClient::GetUserName(uint32_t uid,
+ std::string &name) {
+ if (m_supports_qUserName) {
+ char packet[32];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qUserName:%i", uid);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ // Make sure we parsed the right number of characters. The response is
+ // the hex encoded user name and should make up the entire packet. If
+ // there are any non-hex ASCII bytes, the length won't match below..
+ if (response.GetHexByteString(name) * 2 ==
+ response.GetStringRef().size())
+ return true;
+ }
+ } else {
+ m_supports_qUserName = false;
+ return false;
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetGroupName(uint32_t gid,
+ std::string &name) {
+ if (m_supports_qGroupName) {
+ char packet[32];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qGroupName:%i", gid);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ // Make sure we parsed the right number of characters. The response is
+ // the hex encoded group name and should make up the entire packet. If
+ // there are any non-hex ASCII bytes, the length won't match below..
+ if (response.GetHexByteString(name) * 2 ==
+ response.GetStringRef().size())
+ return true;
+ }
+ } else {
+ m_supports_qGroupName = false;
+ return false;
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::SetNonStopMode(const bool enable) {
+ // Form non-stop packet request
+ char packet[32];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "QNonStop:%1d", (int)enable);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+
+ StringExtractorGDBRemote response;
+ // Send to target
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success)
+ if (response.IsOKResponse())
+ return true;
+
+ // Failed or not supported
+ return false;
+}
+
+static void MakeSpeedTestPacket(StreamString &packet, uint32_t send_size,
+ uint32_t recv_size) {
+ packet.Clear();
+ packet.Printf("qSpeedTest:response_size:%i;data:", recv_size);
+ uint32_t bytes_left = send_size;
+ while (bytes_left > 0) {
+ if (bytes_left >= 26) {
+ packet.PutCString("abcdefghijklmnopqrstuvwxyz");
+ bytes_left -= 26;
+ } else {
+ packet.Printf("%*.*s;", bytes_left, bytes_left,
+ "abcdefghijklmnopqrstuvwxyz");
+ bytes_left = 0;
+ }
+ }
+}
+
+duration<float>
+calculate_standard_deviation(const std::vector<duration<float>> &v) {
+ using Dur = duration<float>;
+ Dur sum = std::accumulate(std::begin(v), std::end(v), Dur());
+ Dur mean = sum / v.size();
+ float accum = 0;
+ for (auto d : v) {
+ float delta = (d - mean).count();
+ accum += delta * delta;
+ };
+
+ return Dur(sqrtf(accum / (v.size() - 1)));
+}
+
+void GDBRemoteCommunicationClient::TestPacketSpeed(const uint32_t num_packets,
+ uint32_t max_send,
+ uint32_t max_recv,
+ uint64_t recv_amount,
+ bool json, Stream &strm) {
+ uint32_t i;
+ if (SendSpeedTestPacket(0, 0)) {
+ StreamString packet;
+ if (json)
+ strm.Printf("{ \"packet_speeds\" : {\n \"num_packets\" : %u,\n "
+ "\"results\" : [",
+ num_packets);
+ else
+ strm.Printf("Testing sending %u packets of various sizes:\n",
+ num_packets);
+ strm.Flush();
+
+ uint32_t result_idx = 0;
+ uint32_t send_size;
+ std::vector<duration<float>> packet_times;
+
+ for (send_size = 0; send_size <= max_send;
+ send_size ? send_size *= 2 : send_size = 4) {
+ for (uint32_t recv_size = 0; recv_size <= max_recv;
+ recv_size ? recv_size *= 2 : recv_size = 4) {
+ MakeSpeedTestPacket(packet, send_size, recv_size);
+
+ packet_times.clear();
+ // Test how long it takes to send 'num_packets' packets
+ const auto start_time = steady_clock::now();
+ for (i = 0; i < num_packets; ++i) {
+ const auto packet_start_time = steady_clock::now();
+ StringExtractorGDBRemote response;
+ SendPacketAndWaitForResponse(packet.GetString(), response, false);
+ const auto packet_end_time = steady_clock::now();
+ packet_times.push_back(packet_end_time - packet_start_time);
+ }
+ const auto end_time = steady_clock::now();
+ const auto total_time = end_time - start_time;
+
+ float packets_per_second =
+ ((float)num_packets) / duration<float>(total_time).count();
+ auto average_per_packet = total_time / num_packets;
+ const duration<float> standard_deviation =
+ calculate_standard_deviation(packet_times);
+ if (json) {
+ strm.Format("{0}\n {{\"send_size\" : {1,6}, \"recv_size\" : "
+ "{2,6}, \"total_time_nsec\" : {3,12:ns-}, "
+ "\"standard_deviation_nsec\" : {4,9:ns-f0}}",
+ result_idx > 0 ? "," : "", send_size, recv_size,
+ total_time, standard_deviation);
+ ++result_idx;
+ } else {
+ strm.Format("qSpeedTest(send={0,7}, recv={1,7}) in {2:s+f9} for "
+ "{3,9:f2} packets/s ({4,10:ms+f6} per packet) with "
+ "standard deviation of {5,10:ms+f6}\n",
+ send_size, recv_size, duration<float>(total_time),
+ packets_per_second, duration<float>(average_per_packet),
+ standard_deviation);
+ }
+ strm.Flush();
+ }
+ }
+
+ const float k_recv_amount_mb = (float)recv_amount / (1024.0f * 1024.0f);
+ if (json)
+ strm.Printf("\n ]\n },\n \"download_speed\" : {\n \"byte_size\" "
+ ": %" PRIu64 ",\n \"results\" : [",
+ recv_amount);
+ else
+ strm.Printf("Testing receiving %2.1fMB of data using varying receive "
+ "packet sizes:\n",
+ k_recv_amount_mb);
+ strm.Flush();
+ send_size = 0;
+ result_idx = 0;
+ for (uint32_t recv_size = 32; recv_size <= max_recv; recv_size *= 2) {
+ MakeSpeedTestPacket(packet, send_size, recv_size);
+
+ // If we have a receive size, test how long it takes to receive 4MB of
+ // data
+ if (recv_size > 0) {
+ const auto start_time = steady_clock::now();
+ uint32_t bytes_read = 0;
+ uint32_t packet_count = 0;
+ while (bytes_read < recv_amount) {
+ StringExtractorGDBRemote response;
+ SendPacketAndWaitForResponse(packet.GetString(), response, false);
+ bytes_read += recv_size;
+ ++packet_count;
+ }
+ const auto end_time = steady_clock::now();
+ const auto total_time = end_time - start_time;
+ float mb_second = ((float)recv_amount) /
+ duration<float>(total_time).count() /
+ (1024.0 * 1024.0);
+ float packets_per_second =
+ ((float)packet_count) / duration<float>(total_time).count();
+ const auto average_per_packet = total_time / packet_count;
+
+ if (json) {
+ strm.Format("{0}\n {{\"send_size\" : {1,6}, \"recv_size\" : "
+ "{2,6}, \"total_time_nsec\" : {3,12:ns-}}",
+ result_idx > 0 ? "," : "", send_size, recv_size,
+ total_time);
+ ++result_idx;
+ } else {
+ strm.Format("qSpeedTest(send={0,7}, recv={1,7}) {2,6} packets needed "
+ "to receive {3:f1}MB in {4:s+f9} for {5} MB/sec for "
+ "{6,9:f2} packets/sec ({7,10:ms+f6} per packet)\n",
+ send_size, recv_size, packet_count, k_recv_amount_mb,
+ duration<float>(total_time), mb_second,
+ packets_per_second, duration<float>(average_per_packet));
+ }
+ strm.Flush();
+ }
+ }
+ if (json)
+ strm.Printf("\n ]\n }\n}\n");
+ else
+ strm.EOL();
+ }
+}
+
+bool GDBRemoteCommunicationClient::SendSpeedTestPacket(uint32_t send_size,
+ uint32_t recv_size) {
+ StreamString packet;
+ packet.Printf("qSpeedTest:response_size:%i;data:", recv_size);
+ uint32_t bytes_left = send_size;
+ while (bytes_left > 0) {
+ if (bytes_left >= 26) {
+ packet.PutCString("abcdefghijklmnopqrstuvwxyz");
+ bytes_left -= 26;
+ } else {
+ packet.Printf("%*.*s;", bytes_left, bytes_left,
+ "abcdefghijklmnopqrstuvwxyz");
+ bytes_left = 0;
+ }
+ }
+
+ StringExtractorGDBRemote response;
+ return SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success;
+}
+
+bool GDBRemoteCommunicationClient::LaunchGDBServer(
+ const char *remote_accept_hostname, lldb::pid_t &pid, uint16_t &port,
+ std::string &socket_name) {
+ pid = LLDB_INVALID_PROCESS_ID;
+ port = 0;
+ socket_name.clear();
+
+ StringExtractorGDBRemote response;
+ StreamString stream;
+ stream.PutCString("qLaunchGDBServer;");
+ std::string hostname;
+ if (remote_accept_hostname && remote_accept_hostname[0])
+ hostname = remote_accept_hostname;
+ else {
+ if (HostInfo::GetHostname(hostname)) {
+ // Make the GDB server we launch only accept connections from this host
+ stream.Printf("host:%s;", hostname.c_str());
+ } else {
+ // Make the GDB server we launch accept connections from any host since
+ // we can't figure out the hostname
+ stream.Printf("host:*;");
+ }
+ }
+ // give the process a few seconds to startup
+ ScopedTimeout timeout(*this, seconds(10));
+
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("port"))
+ value.getAsInteger(0, port);
+ else if (name.equals("pid"))
+ value.getAsInteger(0, pid);
+ else if (name.compare("socket_name") == 0) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(socket_name);
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+size_t GDBRemoteCommunicationClient::QueryGDBServer(
+ std::vector<std::pair<uint16_t, std::string>> &connection_urls) {
+ connection_urls.clear();
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qQueryGDBServer", response, false) !=
+ PacketResult::Success)
+ return 0;
+
+ StructuredData::ObjectSP data =
+ StructuredData::ParseJSON(response.GetStringRef());
+ if (!data)
+ return 0;
+
+ StructuredData::Array *array = data->GetAsArray();
+ if (!array)
+ return 0;
+
+ for (size_t i = 0, count = array->GetSize(); i < count; ++i) {
+ StructuredData::Dictionary *element = nullptr;
+ if (!array->GetItemAtIndexAsDictionary(i, element))
+ continue;
+
+ uint16_t port = 0;
+ if (StructuredData::ObjectSP port_osp =
+ element->GetValueForKey(llvm::StringRef("port")))
+ port = port_osp->GetIntegerValue(0);
+
+ std::string socket_name;
+ if (StructuredData::ObjectSP socket_name_osp =
+ element->GetValueForKey(llvm::StringRef("socket_name")))
+ socket_name = socket_name_osp->GetStringValue();
+
+ if (port != 0 || !socket_name.empty())
+ connection_urls.emplace_back(port, socket_name);
+ }
+ return connection_urls.size();
+}
+
+bool GDBRemoteCommunicationClient::KillSpawnedProcess(lldb::pid_t pid) {
+ StreamString stream;
+ stream.Printf("qKillSpawnedProcess:%" PRId64, pid);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return true;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::SetCurrentThread(uint64_t tid) {
+ if (m_curr_tid == tid)
+ return true;
+
+ char packet[32];
+ int packet_len;
+ if (tid == UINT64_MAX)
+ packet_len = ::snprintf(packet, sizeof(packet), "Hg-1");
+ else
+ packet_len = ::snprintf(packet, sizeof(packet), "Hg%" PRIx64, tid);
+ assert(packet_len + 1 < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_curr_tid = tid;
+ return true;
+ }
+
+ /*
+ * Connected bare-iron target (like YAMON gdb-stub) may not have support for
+ * Hg packet.
+ * The reply from '?' packet could be as simple as 'S05'. There is no packet
+ * which can
+ * give us pid and/or tid. Assume pid=tid=1 in such cases.
+ */
+ if (response.IsUnsupportedResponse() && IsConnected()) {
+ m_curr_tid = 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::SetCurrentThreadForRun(uint64_t tid) {
+ if (m_curr_tid_run == tid)
+ return true;
+
+ char packet[32];
+ int packet_len;
+ if (tid == UINT64_MAX)
+ packet_len = ::snprintf(packet, sizeof(packet), "Hc-1");
+ else
+ packet_len = ::snprintf(packet, sizeof(packet), "Hc%" PRIx64, tid);
+
+ assert(packet_len + 1 < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_curr_tid_run = tid;
+ return true;
+ }
+
+ /*
+ * Connected bare-iron target (like YAMON gdb-stub) may not have support for
+ * Hc packet.
+ * The reply from '?' packet could be as simple as 'S05'. There is no packet
+ * which can
+ * give us pid and/or tid. Assume pid=tid=1 in such cases.
+ */
+ if (response.IsUnsupportedResponse() && IsConnected()) {
+ m_curr_tid_run = 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetStopReply(
+ StringExtractorGDBRemote &response) {
+ if (SendPacketAndWaitForResponse("?", response, false) ==
+ PacketResult::Success)
+ return response.IsNormalResponse();
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetThreadStopInfo(
+ lldb::tid_t tid, StringExtractorGDBRemote &response) {
+ if (m_supports_qThreadStopInfo) {
+ char packet[256];
+ int packet_len =
+ ::snprintf(packet, sizeof(packet), "qThreadStopInfo%" PRIx64, tid);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsUnsupportedResponse())
+ m_supports_qThreadStopInfo = false;
+ else if (response.IsNormalResponse())
+ return true;
+ else
+ return false;
+ } else {
+ m_supports_qThreadStopInfo = false;
+ }
+ }
+ return false;
+}
+
+uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket(
+ GDBStoppointType type, bool insert, addr_t addr, uint32_t length) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationClient::%s() %s at addr = 0x%" PRIx64,
+ __FUNCTION__, insert ? "add" : "remove", addr);
+
+ // Check if the stub is known not to support this breakpoint type
+ if (!SupportsGDBStoppointPacket(type))
+ return UINT8_MAX;
+ // Construct the breakpoint packet
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "%c%i,%" PRIx64 ",%x",
+ insert ? 'Z' : 'z', type, addr, length);
+ // Check we haven't overwritten the end of the packet buffer
+ assert(packet_len + 1 < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ // Make sure the response is either "OK", "EXX" where XX are two hex digits,
+ // or "" (unsupported)
+ response.SetResponseValidatorToOKErrorNotSupported();
+ // Try to send the breakpoint packet, and check that it was correctly sent
+ if (SendPacketAndWaitForResponse(packet, response, true) ==
+ PacketResult::Success) {
+ // Receive and OK packet when the breakpoint successfully placed
+ if (response.IsOKResponse())
+ return 0;
+
+ // Status while setting breakpoint, send back specific error
+ if (response.IsErrorResponse())
+ return response.GetError();
+
+ // Empty packet informs us that breakpoint is not supported
+ if (response.IsUnsupportedResponse()) {
+ // Disable this breakpoint type since it is unsupported
+ switch (type) {
+ case eBreakpointSoftware:
+ m_supports_z0 = false;
+ break;
+ case eBreakpointHardware:
+ m_supports_z1 = false;
+ break;
+ case eWatchpointWrite:
+ m_supports_z2 = false;
+ break;
+ case eWatchpointRead:
+ m_supports_z3 = false;
+ break;
+ case eWatchpointReadWrite:
+ m_supports_z4 = false;
+ break;
+ case eStoppointInvalid:
+ return UINT8_MAX;
+ }
+ }
+ }
+ // Signal generic failure
+ return UINT8_MAX;
+}
+
+size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs(
+ std::vector<lldb::tid_t> &thread_ids, bool &sequence_mutex_unavailable) {
+ thread_ids.clear();
+
+ Lock lock(*this, false);
+ if (lock) {
+ sequence_mutex_unavailable = false;
+ StringExtractorGDBRemote response;
+
+ PacketResult packet_result;
+ for (packet_result =
+ SendPacketAndWaitForResponseNoLock("qfThreadInfo", response);
+ packet_result == PacketResult::Success && response.IsNormalResponse();
+ packet_result =
+ SendPacketAndWaitForResponseNoLock("qsThreadInfo", response)) {
+ char ch = response.GetChar();
+ if (ch == 'l')
+ break;
+ if (ch == 'm') {
+ do {
+ tid_t tid = response.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID);
+
+ if (tid != LLDB_INVALID_THREAD_ID) {
+ thread_ids.push_back(tid);
+ }
+ ch = response.GetChar(); // Skip the command separator
+ } while (ch == ','); // Make sure we got a comma separator
+ }
+ }
+
+ /*
+ * Connected bare-iron target (like YAMON gdb-stub) may not have support for
+ * qProcessInfo, qC and qfThreadInfo packets. The reply from '?' packet
+ * could
+ * be as simple as 'S05'. There is no packet which can give us pid and/or
+ * tid.
+ * Assume pid=tid=1 in such cases.
+ */
+ if ((response.IsUnsupportedResponse() || response.IsNormalResponse()) &&
+ thread_ids.size() == 0 && IsConnected()) {
+ thread_ids.push_back(1);
+ }
+ } else {
+#if !defined(LLDB_CONFIGURATION_DEBUG)
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS |
+ GDBR_LOG_PACKETS));
+ if (log)
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "packet 'qfThreadInfo'");
+#endif
+ sequence_mutex_unavailable = true;
+ }
+ return thread_ids.size();
+}
+
+lldb::addr_t GDBRemoteCommunicationClient::GetShlibInfoAddr() {
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qShlibInfoAddr", response, false) !=
+ PacketResult::Success ||
+ !response.IsNormalResponse())
+ return LLDB_INVALID_ADDRESS;
+ return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
+}
+
+lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand(
+ const char *command, // Shouldn't be NULL
+ const FileSpec &
+ working_dir, // Pass empty FileSpec to use the current working directory
+ int *status_ptr, // Pass NULL if you don't want the process exit status
+ int *signo_ptr, // Pass NULL if you don't want the signal that caused the
+ // process to exit
+ std::string
+ *command_output, // Pass NULL if you don't want the command output
+ const Timeout<std::micro> &timeout) {
+ lldb_private::StreamString stream;
+ stream.PutCString("qPlatform_shell:");
+ stream.PutBytesAsRawHex8(command, strlen(command));
+ stream.PutChar(',');
+ uint32_t timeout_sec = UINT32_MAX;
+ if (timeout) {
+ // TODO: Use chrono version of std::ceil once c++17 is available.
+ timeout_sec = std::ceil(std::chrono::duration<double>(*timeout).count());
+ }
+ stream.PutHex32(timeout_sec);
+ if (working_dir) {
+ std::string path{working_dir.GetPath(false)};
+ stream.PutChar(',');
+ stream.PutStringAsRawHex8(path);
+ }
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F')
+ return Status("malformed reply");
+ if (response.GetChar() != ',')
+ return Status("malformed reply");
+ uint32_t exitcode = response.GetHexMaxU32(false, UINT32_MAX);
+ if (exitcode == UINT32_MAX)
+ return Status("unable to run remote process");
+ else if (status_ptr)
+ *status_ptr = exitcode;
+ if (response.GetChar() != ',')
+ return Status("malformed reply");
+ uint32_t signo = response.GetHexMaxU32(false, UINT32_MAX);
+ if (signo_ptr)
+ *signo_ptr = signo;
+ if (response.GetChar() != ',')
+ return Status("malformed reply");
+ std::string output;
+ response.GetEscapedBinaryData(output);
+ if (command_output)
+ command_output->assign(output);
+ return Status();
+ }
+ return Status("unable to send packet");
+}
+
+Status GDBRemoteCommunicationClient::MakeDirectory(const FileSpec &file_spec,
+ uint32_t file_permissions) {
+ std::string path{file_spec.GetPath(false)};
+ lldb_private::StreamString stream;
+ stream.PutCString("qPlatform_mkdir:");
+ stream.PutHex32(file_permissions);
+ stream.PutChar(',');
+ stream.PutStringAsRawHex8(path);
+ llvm::StringRef packet = stream.GetString();
+ StringExtractorGDBRemote response;
+
+ if (SendPacketAndWaitForResponse(packet, response, false) !=
+ PacketResult::Success)
+ return Status("failed to send '%s' packet", packet.str().c_str());
+
+ if (response.GetChar() != 'F')
+ return Status("invalid response to '%s' packet", packet.str().c_str());
+
+ return Status(response.GetU32(UINT32_MAX), eErrorTypePOSIX);
+}
+
+Status
+GDBRemoteCommunicationClient::SetFilePermissions(const FileSpec &file_spec,
+ uint32_t file_permissions) {
+ std::string path{file_spec.GetPath(false)};
+ lldb_private::StreamString stream;
+ stream.PutCString("qPlatform_chmod:");
+ stream.PutHex32(file_permissions);
+ stream.PutChar(',');
+ stream.PutStringAsRawHex8(path);
+ llvm::StringRef packet = stream.GetString();
+ StringExtractorGDBRemote response;
+
+ if (SendPacketAndWaitForResponse(packet, response, false) !=
+ PacketResult::Success)
+ return Status("failed to send '%s' packet", stream.GetData());
+
+ if (response.GetChar() != 'F')
+ return Status("invalid response to '%s' packet", stream.GetData());
+
+ return Status(response.GetU32(UINT32_MAX), eErrorTypePOSIX);
+}
+
+static uint64_t ParseHostIOPacketResponse(StringExtractorGDBRemote &response,
+ uint64_t fail_result, Status &error) {
+ response.SetFilePos(0);
+ if (response.GetChar() != 'F')
+ return fail_result;
+ int32_t result = response.GetS32(-2);
+ if (result == -2)
+ return fail_result;
+ if (response.GetChar() == ',') {
+ int result_errno = response.GetS32(-2);
+ if (result_errno != -2)
+ error.SetError(result_errno, eErrorTypePOSIX);
+ else
+ error.SetError(-1, eErrorTypeGeneric);
+ } else
+ error.Clear();
+ return result;
+}
+lldb::user_id_t
+GDBRemoteCommunicationClient::OpenFile(const lldb_private::FileSpec &file_spec,
+ uint32_t flags, mode_t mode,
+ Status &error) {
+ std::string path(file_spec.GetPath(false));
+ lldb_private::StreamString stream;
+ stream.PutCString("vFile:open:");
+ if (path.empty())
+ return UINT64_MAX;
+ stream.PutStringAsRawHex8(path);
+ stream.PutChar(',');
+ stream.PutHex32(flags);
+ stream.PutChar(',');
+ stream.PutHex32(mode);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ return ParseHostIOPacketResponse(response, UINT64_MAX, error);
+ }
+ return UINT64_MAX;
+}
+
+bool GDBRemoteCommunicationClient::CloseFile(lldb::user_id_t fd,
+ Status &error) {
+ lldb_private::StreamString stream;
+ stream.Printf("vFile:close:%i", (int)fd);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ return ParseHostIOPacketResponse(response, -1, error) == 0;
+ }
+ return false;
+}
+
+// Extension of host I/O packets to get the file size.
+lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize(
+ const lldb_private::FileSpec &file_spec) {
+ std::string path(file_spec.GetPath(false));
+ lldb_private::StreamString stream;
+ stream.PutCString("vFile:size:");
+ stream.PutStringAsRawHex8(path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F')
+ return UINT64_MAX;
+ uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX);
+ return retcode;
+ }
+ return UINT64_MAX;
+}
+
+Status
+GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec,
+ uint32_t &file_permissions) {
+ std::string path{file_spec.GetPath(false)};
+ Status error;
+ lldb_private::StreamString stream;
+ stream.PutCString("vFile:mode:");
+ stream.PutStringAsRawHex8(path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F') {
+ error.SetErrorStringWithFormat("invalid response to '%s' packet",
+ stream.GetData());
+ } else {
+ const uint32_t mode = response.GetS32(-1);
+ if (static_cast<int32_t>(mode) == -1) {
+ if (response.GetChar() == ',') {
+ int response_errno = response.GetS32(-1);
+ if (response_errno > 0)
+ error.SetError(response_errno, lldb::eErrorTypePOSIX);
+ else
+ error.SetErrorToGenericError();
+ } else
+ error.SetErrorToGenericError();
+ } else {
+ file_permissions = mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat("failed to send '%s' packet",
+ stream.GetData());
+ }
+ return error;
+}
+
+uint64_t GDBRemoteCommunicationClient::ReadFile(lldb::user_id_t fd,
+ uint64_t offset, void *dst,
+ uint64_t dst_len,
+ Status &error) {
+ lldb_private::StreamString stream;
+ stream.Printf("vFile:pread:%i,%" PRId64 ",%" PRId64, (int)fd, dst_len,
+ offset);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F')
+ return 0;
+ uint32_t retcode = response.GetHexMaxU32(false, UINT32_MAX);
+ if (retcode == UINT32_MAX)
+ return retcode;
+ const char next = (response.Peek() ? *response.Peek() : 0);
+ if (next == ',')
+ return 0;
+ if (next == ';') {
+ response.GetChar(); // skip the semicolon
+ std::string buffer;
+ if (response.GetEscapedBinaryData(buffer)) {
+ const uint64_t data_to_write =
+ std::min<uint64_t>(dst_len, buffer.size());
+ if (data_to_write > 0)
+ memcpy(dst, &buffer[0], data_to_write);
+ return data_to_write;
+ }
+ }
+ }
+ return 0;
+}
+
+uint64_t GDBRemoteCommunicationClient::WriteFile(lldb::user_id_t fd,
+ uint64_t offset,
+ const void *src,
+ uint64_t src_len,
+ Status &error) {
+ lldb_private::StreamGDBRemote stream;
+ stream.Printf("vFile:pwrite:%i,%" PRId64 ",", (int)fd, offset);
+ stream.PutEscapedBytes(src, src_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F') {
+ error.SetErrorStringWithFormat("write file failed");
+ return 0;
+ }
+ uint64_t bytes_written = response.GetU64(UINT64_MAX);
+ if (bytes_written == UINT64_MAX) {
+ error.SetErrorToGenericError();
+ if (response.GetChar() == ',') {
+ int response_errno = response.GetS32(-1);
+ if (response_errno > 0)
+ error.SetError(response_errno, lldb::eErrorTypePOSIX);
+ }
+ return 0;
+ }
+ return bytes_written;
+ } else {
+ error.SetErrorString("failed to send vFile:pwrite packet");
+ }
+ return 0;
+}
+
+Status GDBRemoteCommunicationClient::CreateSymlink(const FileSpec &src,
+ const FileSpec &dst) {
+ std::string src_path{src.GetPath(false)}, dst_path{dst.GetPath(false)};
+ Status error;
+ lldb_private::StreamGDBRemote stream;
+ stream.PutCString("vFile:symlink:");
+ // the unix symlink() command reverses its parameters where the dst if first,
+ // so we follow suit here
+ stream.PutStringAsRawHex8(dst_path);
+ stream.PutChar(',');
+ stream.PutStringAsRawHex8(src_path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() == 'F') {
+ uint32_t result = response.GetU32(UINT32_MAX);
+ if (result != 0) {
+ error.SetErrorToGenericError();
+ if (response.GetChar() == ',') {
+ int response_errno = response.GetS32(-1);
+ if (response_errno > 0)
+ error.SetError(response_errno, lldb::eErrorTypePOSIX);
+ }
+ }
+ } else {
+ // Should have returned with 'F<result>[,<errno>]'
+ error.SetErrorStringWithFormat("symlink failed");
+ }
+ } else {
+ error.SetErrorString("failed to send vFile:symlink packet");
+ }
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) {
+ std::string path{file_spec.GetPath(false)};
+ Status error;
+ lldb_private::StreamGDBRemote stream;
+ stream.PutCString("vFile:unlink:");
+ // the unix symlink() command reverses its parameters where the dst if first,
+ // so we follow suit here
+ stream.PutStringAsRawHex8(path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() == 'F') {
+ uint32_t result = response.GetU32(UINT32_MAX);
+ if (result != 0) {
+ error.SetErrorToGenericError();
+ if (response.GetChar() == ',') {
+ int response_errno = response.GetS32(-1);
+ if (response_errno > 0)
+ error.SetError(response_errno, lldb::eErrorTypePOSIX);
+ }
+ }
+ } else {
+ // Should have returned with 'F<result>[,<errno>]'
+ error.SetErrorStringWithFormat("unlink failed");
+ }
+ } else {
+ error.SetErrorString("failed to send vFile:unlink packet");
+ }
+ return error;
+}
+
+// Extension of host I/O packets to get whether a file exists.
+bool GDBRemoteCommunicationClient::GetFileExists(
+ const lldb_private::FileSpec &file_spec) {
+ std::string path(file_spec.GetPath(false));
+ lldb_private::StreamString stream;
+ stream.PutCString("vFile:exists:");
+ stream.PutStringAsRawHex8(path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F')
+ return false;
+ if (response.GetChar() != ',')
+ return false;
+ bool retcode = (response.GetChar() != '0');
+ return retcode;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::CalculateMD5(
+ const lldb_private::FileSpec &file_spec, uint64_t &high, uint64_t &low) {
+ std::string path(file_spec.GetPath(false));
+ lldb_private::StreamString stream;
+ stream.PutCString("vFile:MD5:");
+ stream.PutStringAsRawHex8(path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F')
+ return false;
+ if (response.GetChar() != ',')
+ return false;
+ if (response.Peek() && *response.Peek() == 'x')
+ return false;
+ low = response.GetHexMaxU64(false, UINT64_MAX);
+ high = response.GetHexMaxU64(false, UINT64_MAX);
+ return true;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::AvoidGPackets(ProcessGDBRemote *process) {
+ // Some targets have issues with g/G packets and we need to avoid using them
+ if (m_avoid_g_packets == eLazyBoolCalculate) {
+ if (process) {
+ m_avoid_g_packets = eLazyBoolNo;
+ const ArchSpec &arch = process->GetTarget().GetArchitecture();
+ if (arch.IsValid() &&
+ arch.GetTriple().getVendor() == llvm::Triple::Apple &&
+ arch.GetTriple().getOS() == llvm::Triple::IOS &&
+ arch.GetTriple().getArch() == llvm::Triple::aarch64) {
+ m_avoid_g_packets = eLazyBoolYes;
+ uint32_t gdb_server_version = GetGDBServerProgramVersion();
+ if (gdb_server_version != 0) {
+ const char *gdb_server_name = GetGDBServerProgramName();
+ if (gdb_server_name && strcmp(gdb_server_name, "debugserver") == 0) {
+ if (gdb_server_version >= 310)
+ m_avoid_g_packets = eLazyBoolNo;
+ }
+ }
+ }
+ }
+ }
+ return m_avoid_g_packets == eLazyBoolYes;
+}
+
+DataBufferSP GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid,
+ uint32_t reg) {
+ StreamString payload;
+ payload.Printf("p%x", reg);
+ StringExtractorGDBRemote response;
+ if (SendThreadSpecificPacketAndWaitForResponse(
+ tid, std::move(payload), response, false) != PacketResult::Success ||
+ !response.IsNormalResponse())
+ return nullptr;
+
+ DataBufferSP buffer_sp(
+ new DataBufferHeap(response.GetStringRef().size() / 2, 0));
+ response.GetHexBytes(buffer_sp->GetData(), '\xcc');
+ return buffer_sp;
+}
+
+DataBufferSP GDBRemoteCommunicationClient::ReadAllRegisters(lldb::tid_t tid) {
+ StreamString payload;
+ payload.PutChar('g');
+ StringExtractorGDBRemote response;
+ if (SendThreadSpecificPacketAndWaitForResponse(
+ tid, std::move(payload), response, false) != PacketResult::Success ||
+ !response.IsNormalResponse())
+ return nullptr;
+
+ DataBufferSP buffer_sp(
+ new DataBufferHeap(response.GetStringRef().size() / 2, 0));
+ response.GetHexBytes(buffer_sp->GetData(), '\xcc');
+ return buffer_sp;
+}
+
+bool GDBRemoteCommunicationClient::WriteRegister(lldb::tid_t tid,
+ uint32_t reg_num,
+ llvm::ArrayRef<uint8_t> data) {
+ StreamString payload;
+ payload.Printf("P%x=", reg_num);
+ payload.PutBytesAsRawHex8(data.data(), data.size(),
+ endian::InlHostByteOrder(),
+ endian::InlHostByteOrder());
+ StringExtractorGDBRemote response;
+ return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload),
+ response, false) ==
+ PacketResult::Success &&
+ response.IsOKResponse();
+}
+
+bool GDBRemoteCommunicationClient::WriteAllRegisters(
+ lldb::tid_t tid, llvm::ArrayRef<uint8_t> data) {
+ StreamString payload;
+ payload.PutChar('G');
+ payload.PutBytesAsRawHex8(data.data(), data.size(),
+ endian::InlHostByteOrder(),
+ endian::InlHostByteOrder());
+ StringExtractorGDBRemote response;
+ return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload),
+ response, false) ==
+ PacketResult::Success &&
+ response.IsOKResponse();
+}
+
+bool GDBRemoteCommunicationClient::SaveRegisterState(lldb::tid_t tid,
+ uint32_t &save_id) {
+ save_id = 0; // Set to invalid save ID
+ if (m_supports_QSaveRegisterState == eLazyBoolNo)
+ return false;
+
+ m_supports_QSaveRegisterState = eLazyBoolYes;
+ StreamString payload;
+ payload.PutCString("QSaveRegisterState");
+ StringExtractorGDBRemote response;
+ if (SendThreadSpecificPacketAndWaitForResponse(
+ tid, std::move(payload), response, false) != PacketResult::Success)
+ return false;
+
+ if (response.IsUnsupportedResponse())
+ m_supports_QSaveRegisterState = eLazyBoolNo;
+
+ const uint32_t response_save_id = response.GetU32(0);
+ if (response_save_id == 0)
+ return false;
+
+ save_id = response_save_id;
+ return true;
+}
+
+bool GDBRemoteCommunicationClient::RestoreRegisterState(lldb::tid_t tid,
+ uint32_t save_id) {
+ // We use the "m_supports_QSaveRegisterState" variable here because the
+ // QSaveRegisterState and QRestoreRegisterState packets must both be
+ // supported in order to be useful
+ if (m_supports_QSaveRegisterState == eLazyBoolNo)
+ return false;
+
+ StreamString payload;
+ payload.Printf("QRestoreRegisterState:%u", save_id);
+ StringExtractorGDBRemote response;
+ if (SendThreadSpecificPacketAndWaitForResponse(
+ tid, std::move(payload), response, false) != PacketResult::Success)
+ return false;
+
+ if (response.IsOKResponse())
+ return true;
+
+ if (response.IsUnsupportedResponse())
+ m_supports_QSaveRegisterState = eLazyBoolNo;
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::SyncThreadState(lldb::tid_t tid) {
+ if (!GetSyncThreadStateSupported())
+ return false;
+
+ StreamString packet;
+ StringExtractorGDBRemote response;
+ packet.Printf("QSyncThreadState:%4.4" PRIx64 ";", tid);
+ return SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ GDBRemoteCommunication::PacketResult::Success &&
+ response.IsOKResponse();
+}
+
+lldb::user_id_t
+GDBRemoteCommunicationClient::SendStartTracePacket(const TraceOptions &options,
+ Status &error) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ lldb::user_id_t ret_uid = LLDB_INVALID_UID;
+
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jTraceStart:");
+
+ StructuredData::Dictionary json_packet;
+ json_packet.AddIntegerItem("type", options.getType());
+ json_packet.AddIntegerItem("buffersize", options.getTraceBufferSize());
+ json_packet.AddIntegerItem("metabuffersize", options.getMetaDataBufferSize());
+
+ if (options.getThreadID() != LLDB_INVALID_THREAD_ID)
+ json_packet.AddIntegerItem("threadid", options.getThreadID());
+
+ StructuredData::DictionarySP custom_params = options.getTraceParams();
+ if (custom_params)
+ json_packet.AddItem("params", custom_params);
+
+ StreamString json_string;
+ json_packet.Dump(json_string, false);
+ escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (!response.IsNormalResponse()) {
+ error = response.GetStatus();
+ LLDB_LOG(log, "Target does not support Tracing , error {0}", error);
+ } else {
+ ret_uid = response.GetHexMaxU64(false, LLDB_INVALID_UID);
+ }
+ } else {
+ LLDB_LOG(log, "failed to send packet");
+ error.SetErrorStringWithFormat("failed to send packet: '%s'",
+ escaped_packet.GetData());
+ }
+ return ret_uid;
+}
+
+Status
+GDBRemoteCommunicationClient::SendStopTracePacket(lldb::user_id_t uid,
+ lldb::tid_t thread_id) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ StringExtractorGDBRemote response;
+ Status error;
+
+ StructuredData::Dictionary json_packet;
+ StreamGDBRemote escaped_packet;
+ StreamString json_string;
+ escaped_packet.PutCString("jTraceStop:");
+
+ json_packet.AddIntegerItem("traceid", uid);
+
+ if (thread_id != LLDB_INVALID_THREAD_ID)
+ json_packet.AddIntegerItem("threadid", thread_id);
+
+ json_packet.Dump(json_string, false);
+
+ escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
+
+ if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (!response.IsOKResponse()) {
+ error = response.GetStatus();
+ LLDB_LOG(log, "stop tracing failed");
+ }
+ } else {
+ LLDB_LOG(log, "failed to send packet");
+ error.SetErrorStringWithFormat(
+ "failed to send packet: '%s' with error '%d'", escaped_packet.GetData(),
+ response.GetError());
+ }
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::SendGetDataPacket(
+ lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) {
+
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jTraceBufferRead:");
+ return SendGetTraceDataPacket(escaped_packet, uid, thread_id, buffer, offset);
+}
+
+Status GDBRemoteCommunicationClient::SendGetMetaDataPacket(
+ lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) {
+
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jTraceMetaRead:");
+ return SendGetTraceDataPacket(escaped_packet, uid, thread_id, buffer, offset);
+}
+
+Status
+GDBRemoteCommunicationClient::SendGetTraceConfigPacket(lldb::user_id_t uid,
+ TraceOptions &options) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ StringExtractorGDBRemote response;
+ Status error;
+
+ StreamString json_string;
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jTraceConfigRead:");
+
+ StructuredData::Dictionary json_packet;
+ json_packet.AddIntegerItem("traceid", uid);
+
+ if (options.getThreadID() != LLDB_INVALID_THREAD_ID)
+ json_packet.AddIntegerItem("threadid", options.getThreadID());
+
+ json_packet.Dump(json_string, false);
+ escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
+
+ if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ uint64_t type = std::numeric_limits<uint64_t>::max();
+ uint64_t buffersize = std::numeric_limits<uint64_t>::max();
+ uint64_t metabuffersize = std::numeric_limits<uint64_t>::max();
+
+ auto json_object = StructuredData::ParseJSON(response.Peek());
+
+ if (!json_object ||
+ json_object->GetType() != lldb::eStructuredDataTypeDictionary) {
+ error.SetErrorString("Invalid Configuration obtained");
+ return error;
+ }
+
+ auto json_dict = json_object->GetAsDictionary();
+
+ json_dict->GetValueForKeyAsInteger<uint64_t>("metabuffersize",
+ metabuffersize);
+ options.setMetaDataBufferSize(metabuffersize);
+
+ json_dict->GetValueForKeyAsInteger<uint64_t>("buffersize", buffersize);
+ options.setTraceBufferSize(buffersize);
+
+ json_dict->GetValueForKeyAsInteger<uint64_t>("type", type);
+ options.setType(static_cast<lldb::TraceType>(type));
+
+ StructuredData::ObjectSP custom_params_sp =
+ json_dict->GetValueForKey("params");
+ if (custom_params_sp) {
+ if (custom_params_sp->GetType() !=
+ lldb::eStructuredDataTypeDictionary) {
+ error.SetErrorString("Invalid Configuration obtained");
+ return error;
+ } else
+ options.setTraceParams(
+ static_pointer_cast<StructuredData::Dictionary>(
+ custom_params_sp));
+ }
+ } else {
+ error = response.GetStatus();
+ }
+ } else {
+ LLDB_LOG(log, "failed to send packet");
+ error.SetErrorStringWithFormat("failed to send packet: '%s'",
+ escaped_packet.GetData());
+ }
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::SendGetTraceDataPacket(
+ StreamGDBRemote &packet, lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ Status error;
+
+ StructuredData::Dictionary json_packet;
+
+ json_packet.AddIntegerItem("traceid", uid);
+ json_packet.AddIntegerItem("offset", offset);
+ json_packet.AddIntegerItem("buffersize", buffer.size());
+
+ if (thread_id != LLDB_INVALID_THREAD_ID)
+ json_packet.AddIntegerItem("threadid", thread_id);
+
+ StreamString json_string;
+ json_packet.Dump(json_string, false);
+
+ packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ size_t filled_size = response.GetHexBytesAvail(buffer);
+ buffer = llvm::MutableArrayRef<uint8_t>(buffer.data(), filled_size);
+ } else {
+ error = response.GetStatus();
+ buffer = buffer.slice(buffer.size());
+ }
+ } else {
+ LLDB_LOG(log, "failed to send packet");
+ error.SetErrorStringWithFormat("failed to send packet: '%s'",
+ packet.GetData());
+ buffer = buffer.slice(buffer.size());
+ }
+ return error;
+}
+
+bool GDBRemoteCommunicationClient::GetModuleInfo(
+ const FileSpec &module_file_spec, const lldb_private::ArchSpec &arch_spec,
+ ModuleSpec &module_spec) {
+ if (!m_supports_qModuleInfo)
+ return false;
+
+ std::string module_path = module_file_spec.GetPath(false);
+ if (module_path.empty())
+ return false;
+
+ StreamString packet;
+ packet.PutCString("qModuleInfo:");
+ packet.PutStringAsRawHex8(module_path);
+ packet.PutCString(";");
+ const auto &triple = arch_spec.GetTriple().getTriple();
+ packet.PutStringAsRawHex8(triple);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) !=
+ PacketResult::Success)
+ return false;
+
+ if (response.IsErrorResponse())
+ return false;
+
+ if (response.IsUnsupportedResponse()) {
+ m_supports_qModuleInfo = false;
+ return false;
+ }
+
+ llvm::StringRef name;
+ llvm::StringRef value;
+
+ module_spec.Clear();
+ module_spec.GetFileSpec() = module_file_spec;
+
+ while (response.GetNameColonValue(name, value)) {
+ if (name == "uuid" || name == "md5") {
+ StringExtractor extractor(value);
+ std::string uuid;
+ extractor.GetHexByteString(uuid);
+ module_spec.GetUUID().SetFromStringRef(uuid, uuid.size() / 2);
+ } else if (name == "triple") {
+ StringExtractor extractor(value);
+ std::string triple;
+ extractor.GetHexByteString(triple);
+ module_spec.GetArchitecture().SetTriple(triple.c_str());
+ } else if (name == "file_offset") {
+ uint64_t ival = 0;
+ if (!value.getAsInteger(16, ival))
+ module_spec.SetObjectOffset(ival);
+ } else if (name == "file_size") {
+ uint64_t ival = 0;
+ if (!value.getAsInteger(16, ival))
+ module_spec.SetObjectSize(ival);
+ } else if (name == "file_path") {
+ StringExtractor extractor(value);
+ std::string path;
+ extractor.GetHexByteString(path);
+ module_spec.GetFileSpec() = FileSpec(path, arch_spec.GetTriple());
+ }
+ }
+
+ return true;
+}
+
+static llvm::Optional<ModuleSpec>
+ParseModuleSpec(StructuredData::Dictionary *dict) {
+ ModuleSpec result;
+ if (!dict)
+ return llvm::None;
+
+ llvm::StringRef string;
+ uint64_t integer;
+
+ if (!dict->GetValueForKeyAsString("uuid", string))
+ return llvm::None;
+ if (result.GetUUID().SetFromStringRef(string, string.size() / 2) !=
+ string.size())
+ return llvm::None;
+
+ if (!dict->GetValueForKeyAsInteger("file_offset", integer))
+ return llvm::None;
+ result.SetObjectOffset(integer);
+
+ if (!dict->GetValueForKeyAsInteger("file_size", integer))
+ return llvm::None;
+ result.SetObjectSize(integer);
+
+ if (!dict->GetValueForKeyAsString("triple", string))
+ return llvm::None;
+ result.GetArchitecture().SetTriple(string);
+
+ if (!dict->GetValueForKeyAsString("file_path", string))
+ return llvm::None;
+ result.GetFileSpec() = FileSpec(string, result.GetArchitecture().GetTriple());
+
+ return result;
+}
+
+llvm::Optional<std::vector<ModuleSpec>>
+GDBRemoteCommunicationClient::GetModulesInfo(
+ llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple) {
+ if (!m_supports_jModulesInfo)
+ return llvm::None;
+
+ JSONArray::SP module_array_sp = std::make_shared<JSONArray>();
+ for (const FileSpec &module_file_spec : module_file_specs) {
+ JSONObject::SP module_sp = std::make_shared<JSONObject>();
+ module_array_sp->AppendObject(module_sp);
+ module_sp->SetObject(
+ "file", std::make_shared<JSONString>(module_file_spec.GetPath(false)));
+ module_sp->SetObject("triple",
+ std::make_shared<JSONString>(triple.getTriple()));
+ }
+ StreamString unescaped_payload;
+ unescaped_payload.PutCString("jModulesInfo:");
+ module_array_sp->Write(unescaped_payload);
+ StreamGDBRemote payload;
+ payload.PutEscapedBytes(unescaped_payload.GetString().data(),
+ unescaped_payload.GetSize());
+
+ // Increase the timeout for jModulesInfo since this packet can take longer.
+ ScopedTimeout timeout(*this, std::chrono::seconds(10));
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(payload.GetString(), response, false) !=
+ PacketResult::Success ||
+ response.IsErrorResponse())
+ return llvm::None;
+
+ if (response.IsUnsupportedResponse()) {
+ m_supports_jModulesInfo = false;
+ return llvm::None;
+ }
+
+ StructuredData::ObjectSP response_object_sp =
+ StructuredData::ParseJSON(response.GetStringRef());
+ if (!response_object_sp)
+ return llvm::None;
+
+ StructuredData::Array *response_array = response_object_sp->GetAsArray();
+ if (!response_array)
+ return llvm::None;
+
+ std::vector<ModuleSpec> result;
+ for (size_t i = 0; i < response_array->GetSize(); ++i) {
+ if (llvm::Optional<ModuleSpec> module_spec = ParseModuleSpec(
+ response_array->GetItemAtIndex(i)->GetAsDictionary()))
+ result.push_back(*module_spec);
+ }
+
+ return result;
+}
+
+// query the target remote for extended information using the qXfer packet
+//
+// example: object='features', annex='target.xml', out=<xml output> return:
+// 'true' on success
+// 'false' on failure (err set)
+bool GDBRemoteCommunicationClient::ReadExtFeature(
+ const lldb_private::ConstString object,
+ const lldb_private::ConstString annex, std::string &out,
+ lldb_private::Status &err) {
+
+ std::stringstream output;
+ StringExtractorGDBRemote chunk;
+
+ uint64_t size = GetRemoteMaxPacketSize();
+ if (size == 0)
+ size = 0x1000;
+ size = size - 1; // Leave space for the 'm' or 'l' character in the response
+ int offset = 0;
+ bool active = true;
+
+ // loop until all data has been read
+ while (active) {
+
+ // send query extended feature packet
+ std::stringstream packet;
+ packet << "qXfer:" << object.AsCString("")
+ << ":read:" << annex.AsCString("") << ":" << std::hex << offset
+ << "," << std::hex << size;
+
+ GDBRemoteCommunication::PacketResult res =
+ SendPacketAndWaitForResponse(packet.str(), chunk, false);
+
+ if (res != GDBRemoteCommunication::PacketResult::Success) {
+ err.SetErrorString("Error sending $qXfer packet");
+ return false;
+ }
+
+ const std::string &str = chunk.GetStringRef();
+ if (str.length() == 0) {
+ // should have some data in chunk
+ err.SetErrorString("Empty response from $qXfer packet");
+ return false;
+ }
+
+ // check packet code
+ switch (str[0]) {
+ // last chunk
+ case ('l'):
+ active = false;
+ LLVM_FALLTHROUGH;
+
+ // more chunks
+ case ('m'):
+ if (str.length() > 1)
+ output << &str[1];
+ offset += size;
+ break;
+
+ // unknown chunk
+ default:
+ err.SetErrorString("Invalid continuation code from $qXfer packet");
+ return false;
+ }
+ }
+
+ out = output.str();
+ err.Success();
+ return true;
+}
+
+// Notify the target that gdb is prepared to serve symbol lookup requests.
+// packet: "qSymbol::"
+// reply:
+// OK The target does not need to look up any (more) symbols.
+// qSymbol:<sym_name> The target requests the value of symbol sym_name (hex
+// encoded).
+// LLDB may provide the value by sending another qSymbol
+// packet
+// in the form of"qSymbol:<sym_value>:<sym_name>".
+//
+// Three examples:
+//
+// lldb sends: qSymbol::
+// lldb receives: OK
+// Remote gdb stub does not need to know the addresses of any symbols, lldb
+// does not
+// need to ask again in this session.
+//
+// lldb sends: qSymbol::
+// lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473
+// lldb sends: qSymbol::64697370617463685f71756575655f6f666673657473
+// lldb receives: OK
+// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb does
+// not know
+// the address at this time. lldb needs to send qSymbol:: again when it has
+// more
+// solibs loaded.
+//
+// lldb sends: qSymbol::
+// lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473
+// lldb sends: qSymbol:2bc97554:64697370617463685f71756575655f6f666673657473
+// lldb receives: OK
+// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb says
+// that it
+// is at address 0x2bc97554. Remote gdb stub sends 'OK' indicating that it
+// does not
+// need any more symbols. lldb does not need to ask again in this session.
+
+void GDBRemoteCommunicationClient::ServeSymbolLookups(
+ lldb_private::Process *process) {
+ // Set to true once we've resolved a symbol to an address for the remote
+ // stub. If we get an 'OK' response after this, the remote stub doesn't need
+ // any more symbols and we can stop asking.
+ bool symbol_response_provided = false;
+
+ // Is this the initial qSymbol:: packet?
+ bool first_qsymbol_query = true;
+
+ if (m_supports_qSymbol && !m_qSymbol_requests_done) {
+ Lock lock(*this, false);
+ if (lock) {
+ StreamString packet;
+ packet.PutCString("qSymbol::");
+ StringExtractorGDBRemote response;
+ while (SendPacketAndWaitForResponseNoLock(packet.GetString(), response) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ if (symbol_response_provided || first_qsymbol_query) {
+ m_qSymbol_requests_done = true;
+ }
+
+ // We are done serving symbols requests
+ return;
+ }
+ first_qsymbol_query = false;
+
+ if (response.IsUnsupportedResponse()) {
+ // qSymbol is not supported by the current GDB server we are
+ // connected to
+ m_supports_qSymbol = false;
+ return;
+ } else {
+ llvm::StringRef response_str(response.GetStringRef());
+ if (response_str.startswith("qSymbol:")) {
+ response.SetFilePos(strlen("qSymbol:"));
+ std::string symbol_name;
+ if (response.GetHexByteString(symbol_name)) {
+ if (symbol_name.empty())
+ return;
+
+ addr_t symbol_load_addr = LLDB_INVALID_ADDRESS;
+ lldb_private::SymbolContextList sc_list;
+ if (process->GetTarget().GetImages().FindSymbolsWithNameAndType(
+ ConstString(symbol_name), eSymbolTypeAny, sc_list)) {
+ const size_t num_scs = sc_list.GetSize();
+ for (size_t sc_idx = 0;
+ sc_idx < num_scs &&
+ symbol_load_addr == LLDB_INVALID_ADDRESS;
+ ++sc_idx) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(sc_idx, sc)) {
+ if (sc.symbol) {
+ switch (sc.symbol->GetType()) {
+ case eSymbolTypeInvalid:
+ case eSymbolTypeAbsolute:
+ case eSymbolTypeUndefined:
+ case eSymbolTypeSourceFile:
+ case eSymbolTypeHeaderFile:
+ case eSymbolTypeObjectFile:
+ case eSymbolTypeCommonBlock:
+ case eSymbolTypeBlock:
+ case eSymbolTypeLocal:
+ case eSymbolTypeParam:
+ case eSymbolTypeVariable:
+ case eSymbolTypeVariableType:
+ case eSymbolTypeLineEntry:
+ case eSymbolTypeLineHeader:
+ case eSymbolTypeScopeBegin:
+ case eSymbolTypeScopeEnd:
+ case eSymbolTypeAdditional:
+ case eSymbolTypeCompiler:
+ case eSymbolTypeInstrumentation:
+ case eSymbolTypeTrampoline:
+ break;
+
+ case eSymbolTypeCode:
+ case eSymbolTypeResolver:
+ case eSymbolTypeData:
+ case eSymbolTypeRuntime:
+ case eSymbolTypeException:
+ case eSymbolTypeObjCClass:
+ case eSymbolTypeObjCMetaClass:
+ case eSymbolTypeObjCIVar:
+ case eSymbolTypeReExported:
+ symbol_load_addr =
+ sc.symbol->GetLoadAddress(&process->GetTarget());
+ break;
+ }
+ }
+ }
+ }
+ }
+ // This is the normal path where our symbol lookup was successful
+ // and we want to send a packet with the new symbol value and see
+ // if another lookup needs to be done.
+
+ // Change "packet" to contain the requested symbol value and name
+ packet.Clear();
+ packet.PutCString("qSymbol:");
+ if (symbol_load_addr != LLDB_INVALID_ADDRESS) {
+ packet.Printf("%" PRIx64, symbol_load_addr);
+ symbol_response_provided = true;
+ } else {
+ symbol_response_provided = false;
+ }
+ packet.PutCString(":");
+ packet.PutBytesAsRawHex8(symbol_name.data(), symbol_name.size());
+ continue; // go back to the while loop and send "packet" and wait
+ // for another response
+ }
+ }
+ }
+ }
+ // If we make it here, the symbol request packet response wasn't valid or
+ // our symbol lookup failed so we must abort
+ return;
+
+ } else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(
+ GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)) {
+ log->Printf(
+ "GDBRemoteCommunicationClient::%s: Didn't get sequence mutex.",
+ __FUNCTION__);
+ }
+ }
+}
+
+StructuredData::Array *
+GDBRemoteCommunicationClient::GetSupportedStructuredDataPlugins() {
+ if (!m_supported_async_json_packets_is_valid) {
+ // Query the server for the array of supported asynchronous JSON packets.
+ m_supported_async_json_packets_is_valid = true;
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ // Poll it now.
+ StringExtractorGDBRemote response;
+ const bool send_async = false;
+ if (SendPacketAndWaitForResponse("qStructuredDataPlugins", response,
+ send_async) == PacketResult::Success) {
+ m_supported_async_json_packets_sp =
+ StructuredData::ParseJSON(response.GetStringRef());
+ if (m_supported_async_json_packets_sp &&
+ !m_supported_async_json_packets_sp->GetAsArray()) {
+ // We were returned something other than a JSON array. This is
+ // invalid. Clear it out.
+ if (log)
+ log->Printf("GDBRemoteCommunicationClient::%s(): "
+ "QSupportedAsyncJSONPackets returned invalid "
+ "result: %s",
+ __FUNCTION__, response.GetStringRef().c_str());
+ m_supported_async_json_packets_sp.reset();
+ }
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunicationClient::%s(): "
+ "QSupportedAsyncJSONPackets unsupported",
+ __FUNCTION__);
+ }
+
+ if (log && m_supported_async_json_packets_sp) {
+ StreamString stream;
+ m_supported_async_json_packets_sp->Dump(stream);
+ log->Printf("GDBRemoteCommunicationClient::%s(): supported async "
+ "JSON packets: %s",
+ __FUNCTION__, stream.GetData());
+ }
+ }
+
+ return m_supported_async_json_packets_sp
+ ? m_supported_async_json_packets_sp->GetAsArray()
+ : nullptr;
+}
+
+Status GDBRemoteCommunicationClient::SendSignalsToIgnore(
+ llvm::ArrayRef<int32_t> signals) {
+ // Format packet:
+ // QPassSignals:<hex_sig1>;<hex_sig2>...;<hex_sigN>
+ auto range = llvm::make_range(signals.begin(), signals.end());
+ std::string packet = formatv("QPassSignals:{0:$[;]@(x-2)}", range).str();
+
+ StringExtractorGDBRemote response;
+ auto send_status = SendPacketAndWaitForResponse(packet, response, false);
+
+ if (send_status != GDBRemoteCommunication::PacketResult::Success)
+ return Status("Sending QPassSignals packet failed");
+
+ if (response.IsOKResponse()) {
+ return Status();
+ } else {
+ return Status("Unknown error happened during sending QPassSignals packet.");
+ }
+}
+
+Status GDBRemoteCommunicationClient::ConfigureRemoteStructuredData(
+ ConstString type_name, const StructuredData::ObjectSP &config_sp) {
+ Status error;
+
+ if (type_name.GetLength() == 0) {
+ error.SetErrorString("invalid type_name argument");
+ return error;
+ }
+
+ // Build command: Configure{type_name}: serialized config data.
+ StreamGDBRemote stream;
+ stream.PutCString("QConfigure");
+ stream.PutCString(type_name.AsCString());
+ stream.PutChar(':');
+ if (config_sp) {
+ // Gather the plain-text version of the configuration data.
+ StreamString unescaped_stream;
+ config_sp->Dump(unescaped_stream);
+ unescaped_stream.Flush();
+
+ // Add it to the stream in escaped fashion.
+ stream.PutEscapedBytes(unescaped_stream.GetString().data(),
+ unescaped_stream.GetSize());
+ }
+ stream.Flush();
+
+ // Send the packet.
+ const bool send_async = false;
+ StringExtractorGDBRemote response;
+ auto result =
+ SendPacketAndWaitForResponse(stream.GetString(), response, send_async);
+ if (result == PacketResult::Success) {
+ // We failed if the config result comes back other than OK.
+ if (strcmp(response.GetStringRef().c_str(), "OK") == 0) {
+ // Okay!
+ error.Clear();
+ } else {
+ error.SetErrorStringWithFormat("configuring StructuredData feature "
+ "%s failed with error %s",
+ type_name.AsCString(),
+ response.GetStringRef().c_str());
+ }
+ } else {
+ // Can we get more data here on the failure?
+ error.SetErrorStringWithFormat("configuring StructuredData feature %s "
+ "failed when sending packet: "
+ "PacketResult=%d",
+ type_name.AsCString(), (int)result);
+ }
+ return error;
+}
+
+void GDBRemoteCommunicationClient::OnRunPacketSent(bool first) {
+ GDBRemoteClientBase::OnRunPacketSent(first);
+ m_curr_tid = LLDB_INVALID_THREAD_ID;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
new file mode 100644
index 000000000000..de85c9f8b67b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -0,0 +1,602 @@
+//===-- GDBRemoteCommunicationClient.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunicationClient_h_
+#define liblldb_GDBRemoteCommunicationClient_h_
+
+#include "GDBRemoteClientBase.h"
+
+#include <chrono>
+#include <map>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/StreamGDBRemote.h"
+#include "lldb/Utility/StructuredData.h"
+#if defined(_WIN32)
+#include "lldb/Host/windows/PosixApi.h"
+#endif
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/VersionTuple.h"
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
+public:
+ GDBRemoteCommunicationClient();
+
+ ~GDBRemoteCommunicationClient() override;
+
+ // After connecting, send the handshake to the server to make sure
+ // we are communicating with it.
+ bool HandshakeWithServer(Status *error_ptr);
+
+ // For packets which specify a range of output to be returned,
+ // return all of the output via a series of request packets of the form
+ // <prefix>0,<size>
+ // <prefix><size>,<size>
+ // <prefix><size>*2,<size>
+ // <prefix><size>*3,<size>
+ // ...
+ // until a "$l..." packet is received, indicating the end.
+ // (size is in hex; this format is used by a standard gdbserver to
+ // return the given portion of the output specified by <prefix>;
+ // for example, "qXfer:libraries-svr4:read::fff,1000" means
+ // "return a chunk of the xml description file for shared
+ // library load addresses, where the chunk starts at offset 0xfff
+ // and continues for 0x1000 bytes").
+ // Concatenate the resulting server response packets together and
+ // return in response_string. If any packet fails, the return value
+ // indicates that failure and the returned string value is undefined.
+ PacketResult
+ SendPacketsAndConcatenateResponses(const char *send_payload_prefix,
+ std::string &response_string);
+
+ bool GetThreadSuffixSupported();
+
+ // This packet is usually sent first and the boolean return value
+ // indicates if the packet was send and any response was received
+ // even in the response is UNIMPLEMENTED. If the packet failed to
+ // get a response, then false is returned. This quickly tells us
+ // if we were able to connect and communicate with the remote GDB
+ // server
+ bool QueryNoAckModeSupported();
+
+ void GetListThreadsInStopReplySupported();
+
+ lldb::pid_t GetCurrentProcessID(bool allow_lazy = true);
+
+ bool GetLaunchSuccess(std::string &error_str);
+
+ bool LaunchGDBServer(const char *remote_accept_hostname, lldb::pid_t &pid,
+ uint16_t &port, std::string &socket_name);
+
+ size_t QueryGDBServer(
+ std::vector<std::pair<uint16_t, std::string>> &connection_urls);
+
+ bool KillSpawnedProcess(lldb::pid_t pid);
+
+ /// Sends a GDB remote protocol 'A' packet that delivers program
+ /// arguments to the remote server.
+ ///
+ /// \param[in] argv
+ /// A NULL terminated array of const C strings to use as the
+ /// arguments.
+ ///
+ /// \return
+ /// Zero if the response was "OK", a positive value if the
+ /// the response was "Exx" where xx are two hex digits, or
+ /// -1 if the call is unsupported or any other unexpected
+ /// response was received.
+ int SendArgumentsPacket(const ProcessLaunchInfo &launch_info);
+
+ /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the
+ /// environment that will get used when launching an application
+ /// in conjunction with the 'A' packet. This function can be called
+ /// multiple times in a row in order to pass on the desired
+ /// environment that the inferior should be launched with.
+ ///
+ /// \param[in] name_equal_value
+ /// A NULL terminated C string that contains a single environment
+ /// in the format "NAME=VALUE".
+ ///
+ /// \return
+ /// Zero if the response was "OK", a positive value if the
+ /// the response was "Exx" where xx are two hex digits, or
+ /// -1 if the call is unsupported or any other unexpected
+ /// response was received.
+ int SendEnvironmentPacket(char const *name_equal_value);
+ int SendEnvironment(const Environment &env);
+
+ int SendLaunchArchPacket(const char *arch);
+
+ int SendLaunchEventDataPacket(const char *data,
+ bool *was_supported = nullptr);
+
+ /// Sends a "vAttach:PID" where PID is in hex.
+ ///
+ /// \param[in] pid
+ /// A process ID for the remote gdb server to attach to.
+ ///
+ /// \param[out] response
+ /// The response received from the gdb server. If the return
+ /// value is zero, \a response will contain a stop reply
+ /// packet.
+ ///
+ /// \return
+ /// Zero if the attach was successful, or an error indicating
+ /// an error code.
+ int SendAttach(lldb::pid_t pid, StringExtractorGDBRemote &response);
+
+ /// Sends a GDB remote protocol 'I' packet that delivers stdin
+ /// data to the remote process.
+ ///
+ /// \param[in] data
+ /// A pointer to stdin data.
+ ///
+ /// \param[in] data_len
+ /// The number of bytes available at \a data.
+ ///
+ /// \return
+ /// Zero if the attach was successful, or an error indicating
+ /// an error code.
+ int SendStdinNotification(const char *data, size_t data_len);
+
+ /// Sets the path to use for stdin/out/err for a process
+ /// that will be launched with the 'A' packet.
+ ///
+ /// \param[in] path
+ /// The path to use for stdin/out/err
+ ///
+ /// \return
+ /// Zero if the for success, or an error code for failure.
+ int SetSTDIN(const FileSpec &file_spec);
+ int SetSTDOUT(const FileSpec &file_spec);
+ int SetSTDERR(const FileSpec &file_spec);
+
+ /// Sets the disable ASLR flag to \a enable for a process that will
+ /// be launched with the 'A' packet.
+ ///
+ /// \param[in] enable
+ /// A boolean value indicating whether to disable ASLR or not.
+ ///
+ /// \return
+ /// Zero if the for success, or an error code for failure.
+ int SetDisableASLR(bool enable);
+
+ /// Sets the DetachOnError flag to \a enable for the process controlled by the
+ /// stub.
+ ///
+ /// \param[in] enable
+ /// A boolean value indicating whether to detach on error or not.
+ ///
+ /// \return
+ /// Zero if the for success, or an error code for failure.
+ int SetDetachOnError(bool enable);
+
+ /// Sets the working directory to \a path for a process that will
+ /// be launched with the 'A' packet for non platform based
+ /// connections. If this packet is sent to a GDB server that
+ /// implements the platform, it will change the current working
+ /// directory for the platform process.
+ ///
+ /// \param[in] working_dir
+ /// The path to a directory to use when launching our process
+ ///
+ /// \return
+ /// Zero if the for success, or an error code for failure.
+ int SetWorkingDir(const FileSpec &working_dir);
+
+ /// Gets the current working directory of a remote platform GDB
+ /// server.
+ ///
+ /// \param[out] working_dir
+ /// The current working directory on the remote platform.
+ ///
+ /// \return
+ /// Boolean for success
+ bool GetWorkingDir(FileSpec &working_dir);
+
+ lldb::addr_t AllocateMemory(size_t size, uint32_t permissions);
+
+ bool DeallocateMemory(lldb::addr_t addr);
+
+ Status Detach(bool keep_stopped);
+
+ Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info);
+
+ Status GetWatchpointSupportInfo(uint32_t &num);
+
+ Status GetWatchpointSupportInfo(uint32_t &num, bool &after,
+ const ArchSpec &arch);
+
+ Status GetWatchpointsTriggerAfterInstruction(bool &after,
+ const ArchSpec &arch);
+
+ const ArchSpec &GetHostArchitecture();
+
+ std::chrono::seconds GetHostDefaultPacketTimeout();
+
+ const ArchSpec &GetProcessArchitecture();
+
+ void GetRemoteQSupported();
+
+ bool GetVContSupported(char flavor);
+
+ bool GetpPacketSupported(lldb::tid_t tid);
+
+ bool GetxPacketSupported();
+
+ bool GetVAttachOrWaitSupported();
+
+ bool GetSyncThreadStateSupported();
+
+ void ResetDiscoverableSettings(bool did_exec);
+
+ bool GetHostInfo(bool force = false);
+
+ bool GetDefaultThreadId(lldb::tid_t &tid);
+
+ llvm::VersionTuple GetOSVersion();
+
+ bool GetOSBuildString(std::string &s);
+
+ bool GetOSKernelDescription(std::string &s);
+
+ ArchSpec GetSystemArchitecture();
+
+ bool GetHostname(std::string &s);
+
+ lldb::addr_t GetShlibInfoAddr();
+
+ bool GetSupportsThreadSuffix();
+
+ bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info);
+
+ uint32_t FindProcesses(const ProcessInstanceInfoMatch &process_match_info,
+ ProcessInstanceInfoList &process_infos);
+
+ bool GetUserName(uint32_t uid, std::string &name);
+
+ bool GetGroupName(uint32_t gid, std::string &name);
+
+ bool HasFullVContSupport() { return GetVContSupported('A'); }
+
+ bool HasAnyVContSupport() { return GetVContSupported('a'); }
+
+ bool GetStopReply(StringExtractorGDBRemote &response);
+
+ bool GetThreadStopInfo(lldb::tid_t tid, StringExtractorGDBRemote &response);
+
+ bool SupportsGDBStoppointPacket(GDBStoppointType type) {
+ switch (type) {
+ case eBreakpointSoftware:
+ return m_supports_z0;
+ case eBreakpointHardware:
+ return m_supports_z1;
+ case eWatchpointWrite:
+ return m_supports_z2;
+ case eWatchpointRead:
+ return m_supports_z3;
+ case eWatchpointReadWrite:
+ return m_supports_z4;
+ default:
+ return false;
+ }
+ }
+
+ uint8_t SendGDBStoppointTypePacket(
+ GDBStoppointType type, // Type of breakpoint or watchpoint
+ bool insert, // Insert or remove?
+ lldb::addr_t addr, // Address of breakpoint or watchpoint
+ uint32_t length); // Byte Size of breakpoint or watchpoint
+
+ bool SetNonStopMode(const bool enable);
+
+ void TestPacketSpeed(const uint32_t num_packets, uint32_t max_send,
+ uint32_t max_recv, uint64_t recv_amount, bool json,
+ Stream &strm);
+
+ // This packet is for testing the speed of the interface only. Both
+ // the client and server need to support it, but this allows us to
+ // measure the packet speed without any other work being done on the
+ // other end and avoids any of that work affecting the packet send
+ // and response times.
+ bool SendSpeedTestPacket(uint32_t send_size, uint32_t recv_size);
+
+ bool SetCurrentThread(uint64_t tid);
+
+ bool SetCurrentThreadForRun(uint64_t tid);
+
+ bool GetQXferAuxvReadSupported();
+
+ void EnableErrorStringInPacket();
+
+ bool GetQXferLibrariesReadSupported();
+
+ bool GetQXferLibrariesSVR4ReadSupported();
+
+ uint64_t GetRemoteMaxPacketSize();
+
+ bool GetEchoSupported();
+
+ bool GetQPassSignalsSupported();
+
+ bool GetAugmentedLibrariesSVR4ReadSupported();
+
+ bool GetQXferFeaturesReadSupported();
+
+ bool GetQXferMemoryMapReadSupported();
+
+ LazyBool SupportsAllocDeallocMemory() // const
+ {
+ // Uncomment this to have lldb pretend the debug server doesn't respond to
+ // alloc/dealloc memory packets.
+ // m_supports_alloc_dealloc_memory = lldb_private::eLazyBoolNo;
+ return m_supports_alloc_dealloc_memory;
+ }
+
+ size_t GetCurrentThreadIDs(std::vector<lldb::tid_t> &thread_ids,
+ bool &sequence_mutex_unavailable);
+
+ lldb::user_id_t OpenFile(const FileSpec &file_spec, uint32_t flags,
+ mode_t mode, Status &error);
+
+ bool CloseFile(lldb::user_id_t fd, Status &error);
+
+ lldb::user_id_t GetFileSize(const FileSpec &file_spec);
+
+ Status GetFilePermissions(const FileSpec &file_spec,
+ uint32_t &file_permissions);
+
+ Status SetFilePermissions(const FileSpec &file_spec,
+ uint32_t file_permissions);
+
+ uint64_t ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst,
+ uint64_t dst_len, Status &error);
+
+ uint64_t WriteFile(lldb::user_id_t fd, uint64_t offset, const void *src,
+ uint64_t src_len, Status &error);
+
+ Status CreateSymlink(const FileSpec &src, const FileSpec &dst);
+
+ Status Unlink(const FileSpec &file_spec);
+
+ Status MakeDirectory(const FileSpec &file_spec, uint32_t mode);
+
+ bool GetFileExists(const FileSpec &file_spec);
+
+ Status RunShellCommand(
+ const char *command, // Shouldn't be nullptr
+ const FileSpec &working_dir, // Pass empty FileSpec to use the current
+ // working directory
+ int *status_ptr, // Pass nullptr if you don't want the process exit status
+ int *signo_ptr, // Pass nullptr if you don't want the signal that caused
+ // the process to exit
+ std::string
+ *command_output, // Pass nullptr if you don't want the command output
+ const Timeout<std::micro> &timeout);
+
+ bool CalculateMD5(const FileSpec &file_spec, uint64_t &high, uint64_t &low);
+
+ lldb::DataBufferSP ReadRegister(
+ lldb::tid_t tid,
+ uint32_t
+ reg_num); // Must be the eRegisterKindProcessPlugin register number
+
+ lldb::DataBufferSP ReadAllRegisters(lldb::tid_t tid);
+
+ bool
+ WriteRegister(lldb::tid_t tid,
+ uint32_t reg_num, // eRegisterKindProcessPlugin register number
+ llvm::ArrayRef<uint8_t> data);
+
+ bool WriteAllRegisters(lldb::tid_t tid, llvm::ArrayRef<uint8_t> data);
+
+ bool SaveRegisterState(lldb::tid_t tid, uint32_t &save_id);
+
+ bool RestoreRegisterState(lldb::tid_t tid, uint32_t save_id);
+
+ bool SyncThreadState(lldb::tid_t tid);
+
+ const char *GetGDBServerProgramName();
+
+ uint32_t GetGDBServerProgramVersion();
+
+ bool AvoidGPackets(ProcessGDBRemote *process);
+
+ StructuredData::ObjectSP GetThreadsInfo();
+
+ bool GetThreadExtendedInfoSupported();
+
+ bool GetLoadedDynamicLibrariesInfosSupported();
+
+ bool GetSharedCacheInfoSupported();
+
+ bool GetModuleInfo(const FileSpec &module_file_spec,
+ const ArchSpec &arch_spec, ModuleSpec &module_spec);
+
+ llvm::Optional<std::vector<ModuleSpec>>
+ GetModulesInfo(llvm::ArrayRef<FileSpec> module_file_specs,
+ const llvm::Triple &triple);
+
+ bool ReadExtFeature(const lldb_private::ConstString object,
+ const lldb_private::ConstString annex, std::string &out,
+ lldb_private::Status &err);
+
+ void ServeSymbolLookups(lldb_private::Process *process);
+
+ // Sends QPassSignals packet to the server with given signals to ignore.
+ Status SendSignalsToIgnore(llvm::ArrayRef<int32_t> signals);
+
+ /// Return the feature set supported by the gdb-remote server.
+ ///
+ /// This method returns the remote side's response to the qSupported
+ /// packet. The response is the complete string payload returned
+ /// to the client.
+ ///
+ /// \return
+ /// The string returned by the server to the qSupported query.
+ const std::string &GetServerSupportedFeatures() const {
+ return m_qSupported_response;
+ }
+
+ /// Return the array of async JSON packet types supported by the remote.
+ ///
+ /// This method returns the remote side's array of supported JSON
+ /// packet types as a list of type names. Each of the results are
+ /// expected to have an Enable{type_name} command to enable and configure
+ /// the related feature. Each type_name for an enabled feature will
+ /// possibly send async-style packets that contain a payload of a
+ /// binhex-encoded JSON dictionary. The dictionary will have a
+ /// string field named 'type', that contains the type_name of the
+ /// supported packet type.
+ ///
+ /// There is a Plugin category called structured-data plugins.
+ /// A plugin indicates whether it knows how to handle a type_name.
+ /// If so, it can be used to process the async JSON packet.
+ ///
+ /// \return
+ /// The string returned by the server to the qSupported query.
+ lldb_private::StructuredData::Array *GetSupportedStructuredDataPlugins();
+
+ /// Configure a StructuredData feature on the remote end.
+ ///
+ /// \see \b Process::ConfigureStructuredData(...) for details.
+ Status
+ ConfigureRemoteStructuredData(ConstString type_name,
+ const StructuredData::ObjectSP &config_sp);
+
+ lldb::user_id_t SendStartTracePacket(const TraceOptions &options,
+ Status &error);
+
+ Status SendStopTracePacket(lldb::user_id_t uid, lldb::tid_t thread_id);
+
+ Status SendGetDataPacket(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0);
+
+ Status SendGetMetaDataPacket(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0);
+
+ Status SendGetTraceConfigPacket(lldb::user_id_t uid, TraceOptions &options);
+
+protected:
+ LazyBool m_supports_not_sending_acks;
+ LazyBool m_supports_thread_suffix;
+ LazyBool m_supports_threads_in_stop_reply;
+ LazyBool m_supports_vCont_all;
+ LazyBool m_supports_vCont_any;
+ LazyBool m_supports_vCont_c;
+ LazyBool m_supports_vCont_C;
+ LazyBool m_supports_vCont_s;
+ LazyBool m_supports_vCont_S;
+ LazyBool m_qHostInfo_is_valid;
+ LazyBool m_curr_pid_is_valid;
+ LazyBool m_qProcessInfo_is_valid;
+ LazyBool m_qGDBServerVersion_is_valid;
+ LazyBool m_supports_alloc_dealloc_memory;
+ LazyBool m_supports_memory_region_info;
+ LazyBool m_supports_watchpoint_support_info;
+ LazyBool m_supports_detach_stay_stopped;
+ LazyBool m_watchpoints_trigger_after_instruction;
+ LazyBool m_attach_or_wait_reply;
+ LazyBool m_prepare_for_reg_writing_reply;
+ LazyBool m_supports_p;
+ LazyBool m_supports_x;
+ LazyBool m_avoid_g_packets;
+ LazyBool m_supports_QSaveRegisterState;
+ LazyBool m_supports_qXfer_auxv_read;
+ LazyBool m_supports_qXfer_libraries_read;
+ LazyBool m_supports_qXfer_libraries_svr4_read;
+ LazyBool m_supports_qXfer_features_read;
+ LazyBool m_supports_qXfer_memory_map_read;
+ LazyBool m_supports_augmented_libraries_svr4_read;
+ LazyBool m_supports_jThreadExtendedInfo;
+ LazyBool m_supports_jLoadedDynamicLibrariesInfos;
+ LazyBool m_supports_jGetSharedCacheInfo;
+ LazyBool m_supports_QPassSignals;
+ LazyBool m_supports_error_string_reply;
+
+ bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
+ m_supports_qUserName : 1, m_supports_qGroupName : 1,
+ m_supports_qThreadStopInfo : 1, m_supports_z0 : 1, m_supports_z1 : 1,
+ m_supports_z2 : 1, m_supports_z3 : 1, m_supports_z4 : 1,
+ m_supports_QEnvironment : 1, m_supports_QEnvironmentHexEncoded : 1,
+ m_supports_qSymbol : 1, m_qSymbol_requests_done : 1,
+ m_supports_qModuleInfo : 1, m_supports_jThreadsInfo : 1,
+ m_supports_jModulesInfo : 1;
+
+ lldb::pid_t m_curr_pid;
+ lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all
+ // other operations
+ lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for
+ // continue, step, etc
+
+ uint32_t m_num_supported_hardware_watchpoints;
+
+ ArchSpec m_host_arch;
+ ArchSpec m_process_arch;
+ llvm::VersionTuple m_os_version;
+ std::string m_os_build;
+ std::string m_os_kernel;
+ std::string m_hostname;
+ std::string m_gdb_server_name; // from reply to qGDBServerVersion, empty if
+ // qGDBServerVersion is not supported
+ uint32_t m_gdb_server_version; // from reply to qGDBServerVersion, zero if
+ // qGDBServerVersion is not supported
+ std::chrono::seconds m_default_packet_timeout;
+ uint64_t m_max_packet_size; // as returned by qSupported
+ std::string m_qSupported_response; // the complete response to qSupported
+
+ bool m_supported_async_json_packets_is_valid;
+ lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp;
+
+ std::vector<MemoryRegionInfo> m_qXfer_memory_map;
+ bool m_qXfer_memory_map_loaded;
+
+ bool GetCurrentProcessInfo(bool allow_lazy_pid = true);
+
+ bool GetGDBServerVersion();
+
+ // Given the list of compression types that the remote debug stub can support,
+ // possibly enable compression if we find an encoding we can handle.
+ void MaybeEnableCompression(std::vector<std::string> supported_compressions);
+
+ bool DecodeProcessInfoResponse(StringExtractorGDBRemote &response,
+ ProcessInstanceInfo &process_info);
+
+ void OnRunPacketSent(bool first) override;
+
+ PacketResult SendThreadSpecificPacketAndWaitForResponse(
+ lldb::tid_t tid, StreamString &&payload,
+ StringExtractorGDBRemote &response, bool send_async);
+
+ Status SendGetTraceDataPacket(StreamGDBRemote &packet, lldb::user_id_t uid,
+ lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset);
+
+ Status LoadQXferMemoryMap();
+
+ Status GetQXferMemoryMapRegionInfo(lldb::addr_t addr,
+ MemoryRegionInfo &region);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationClient_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp
new file mode 100644
index 000000000000..bcddb4faf863
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp
@@ -0,0 +1,142 @@
+//===-- GDBRemoteCommunicationHistory.cpp -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteCommunicationHistory.h"
+
+// Other libraries and framework includes
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+void GDBRemoteCommunicationHistory::Entry::Serialize(raw_ostream &strm) const {
+ yaml::Output yout(strm);
+ yout << const_cast<GDBRemoteCommunicationHistory::Entry &>(*this);
+ strm.flush();
+}
+
+GDBRemoteCommunicationHistory::GDBRemoteCommunicationHistory(uint32_t size)
+ : m_packets(), m_curr_idx(0), m_total_packet_count(0),
+ m_dumped_to_log(false) {
+ if (size)
+ m_packets.resize(size);
+}
+
+GDBRemoteCommunicationHistory::~GDBRemoteCommunicationHistory() {}
+
+void GDBRemoteCommunicationHistory::AddPacket(char packet_char, PacketType type,
+ uint32_t bytes_transmitted) {
+ const size_t size = m_packets.size();
+ if (size == 0)
+ return;
+
+ const uint32_t idx = GetNextIndex();
+ m_packets[idx].packet.data.assign(1, packet_char);
+ m_packets[idx].type = type;
+ m_packets[idx].bytes_transmitted = bytes_transmitted;
+ m_packets[idx].packet_idx = m_total_packet_count;
+ m_packets[idx].tid = llvm::get_threadid();
+ if (m_stream)
+ m_packets[idx].Serialize(*m_stream);
+}
+
+void GDBRemoteCommunicationHistory::AddPacket(const std::string &src,
+ uint32_t src_len, PacketType type,
+ uint32_t bytes_transmitted) {
+ const size_t size = m_packets.size();
+ if (size == 0)
+ return;
+
+ const uint32_t idx = GetNextIndex();
+ m_packets[idx].packet.data.assign(src, 0, src_len);
+ m_packets[idx].type = type;
+ m_packets[idx].bytes_transmitted = bytes_transmitted;
+ m_packets[idx].packet_idx = m_total_packet_count;
+ m_packets[idx].tid = llvm::get_threadid();
+ if (m_stream)
+ m_packets[idx].Serialize(*m_stream);
+}
+
+void GDBRemoteCommunicationHistory::Dump(Stream &strm) const {
+ const uint32_t size = GetNumPacketsInHistory();
+ const uint32_t first_idx = GetFirstSavedPacketIndex();
+ const uint32_t stop_idx = m_curr_idx + size;
+ for (uint32_t i = first_idx; i < stop_idx; ++i) {
+ const uint32_t idx = NormalizeIndex(i);
+ const Entry &entry = m_packets[idx];
+ if (entry.type == ePacketTypeInvalid || entry.packet.data.empty())
+ break;
+ strm.Printf("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s\n",
+ entry.packet_idx, entry.tid, entry.bytes_transmitted,
+ (entry.type == ePacketTypeSend) ? "send" : "read",
+ entry.packet.data.c_str());
+ }
+}
+
+void GDBRemoteCommunicationHistory::Dump(Log *log) const {
+ if (!log || m_dumped_to_log)
+ return;
+
+ m_dumped_to_log = true;
+ const uint32_t size = GetNumPacketsInHistory();
+ const uint32_t first_idx = GetFirstSavedPacketIndex();
+ const uint32_t stop_idx = m_curr_idx + size;
+ for (uint32_t i = first_idx; i < stop_idx; ++i) {
+ const uint32_t idx = NormalizeIndex(i);
+ const Entry &entry = m_packets[idx];
+ if (entry.type == ePacketTypeInvalid || entry.packet.data.empty())
+ break;
+ log->Printf("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s",
+ entry.packet_idx, entry.tid, entry.bytes_transmitted,
+ (entry.type == ePacketTypeSend) ? "send" : "read",
+ entry.packet.data.c_str());
+ }
+}
+
+void yaml::ScalarEnumerationTraits<GDBRemoteCommunicationHistory::PacketType>::
+ enumeration(IO &io, GDBRemoteCommunicationHistory::PacketType &value) {
+ io.enumCase(value, "Invalid",
+ GDBRemoteCommunicationHistory::ePacketTypeInvalid);
+ io.enumCase(value, "Send", GDBRemoteCommunicationHistory::ePacketTypeSend);
+ io.enumCase(value, "Recv", GDBRemoteCommunicationHistory::ePacketTypeRecv);
+}
+
+void yaml::ScalarTraits<GDBRemoteCommunicationHistory::Entry::BinaryData>::
+ output(const GDBRemoteCommunicationHistory::Entry::BinaryData &Val, void *,
+ raw_ostream &Out) {
+ Out << toHex(Val.data);
+}
+
+StringRef
+yaml::ScalarTraits<GDBRemoteCommunicationHistory::Entry::BinaryData>::input(
+ StringRef Scalar, void *,
+ GDBRemoteCommunicationHistory::Entry::BinaryData &Val) {
+ Val.data = fromHex(Scalar);
+ return {};
+}
+
+void yaml::MappingTraits<GDBRemoteCommunicationHistory::Entry>::mapping(
+ IO &io, GDBRemoteCommunicationHistory::Entry &Entry) {
+ io.mapRequired("packet", Entry.packet);
+ io.mapRequired("type", Entry.type);
+ io.mapRequired("bytes", Entry.bytes_transmitted);
+ io.mapRequired("index", Entry.packet_idx);
+ io.mapRequired("tid", Entry.tid);
+}
+
+StringRef yaml::MappingTraits<GDBRemoteCommunicationHistory::Entry>::validate(
+ IO &io, GDBRemoteCommunicationHistory::Entry &Entry) {
+ if (Entry.bytes_transmitted != Entry.packet.data.size())
+ return "BinaryData size doesn't match bytes transmitted";
+
+ return {};
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h
new file mode 100644
index 000000000000..85f112b50623
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h
@@ -0,0 +1,155 @@
+//===-- GDBRemoteCommunicationHistory.h--------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunicationHistory_h_
+#define liblldb_GDBRemoteCommunicationHistory_h_
+
+#include <string>
+#include <vector>
+
+#include "lldb/lldb-public.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+/// The history keeps a circular buffer of GDB remote packets. The history is
+/// used for logging and replaying GDB remote packets.
+class GDBRemoteCommunicationHistory {
+public:
+ friend llvm::yaml::MappingTraits<GDBRemoteCommunicationHistory>;
+
+ enum PacketType { ePacketTypeInvalid = 0, ePacketTypeSend, ePacketTypeRecv };
+
+ /// Entry in the ring buffer containing the packet data, its type, size and
+ /// index. Entries can be serialized to file.
+ struct Entry {
+ Entry()
+ : packet(), type(ePacketTypeInvalid), bytes_transmitted(0),
+ packet_idx(0), tid(LLDB_INVALID_THREAD_ID) {}
+
+ void Clear() {
+ packet.data.clear();
+ type = ePacketTypeInvalid;
+ bytes_transmitted = 0;
+ packet_idx = 0;
+ tid = LLDB_INVALID_THREAD_ID;
+ }
+
+ struct BinaryData {
+ std::string data;
+ };
+
+ void Serialize(llvm::raw_ostream &strm) const;
+
+ BinaryData packet;
+ PacketType type;
+ uint32_t bytes_transmitted;
+ uint32_t packet_idx;
+ lldb::tid_t tid;
+ };
+
+ GDBRemoteCommunicationHistory(uint32_t size = 0);
+
+ ~GDBRemoteCommunicationHistory();
+
+ // For single char packets for ack, nack and /x03
+ void AddPacket(char packet_char, PacketType type, uint32_t bytes_transmitted);
+
+ void AddPacket(const std::string &src, uint32_t src_len, PacketType type,
+ uint32_t bytes_transmitted);
+
+ void Dump(Stream &strm) const;
+ void Dump(Log *log) const;
+ bool DidDumpToLog() const { return m_dumped_to_log; }
+
+ void SetStream(llvm::raw_ostream *strm) { m_stream = strm; }
+
+private:
+ uint32_t GetFirstSavedPacketIndex() const {
+ if (m_total_packet_count < m_packets.size())
+ return 0;
+ else
+ return m_curr_idx + 1;
+ }
+
+ uint32_t GetNumPacketsInHistory() const {
+ if (m_total_packet_count < m_packets.size())
+ return m_total_packet_count;
+ else
+ return (uint32_t)m_packets.size();
+ }
+
+ uint32_t GetNextIndex() {
+ ++m_total_packet_count;
+ const uint32_t idx = m_curr_idx;
+ m_curr_idx = NormalizeIndex(idx + 1);
+ return idx;
+ }
+
+ uint32_t NormalizeIndex(uint32_t i) const {
+ return m_packets.empty() ? 0 : i % m_packets.size();
+ }
+
+ std::vector<Entry> m_packets;
+ uint32_t m_curr_idx;
+ uint32_t m_total_packet_count;
+ mutable bool m_dumped_to_log;
+ llvm::raw_ostream *m_stream = nullptr;
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry)
+
+namespace llvm {
+namespace yaml {
+
+template <>
+struct ScalarEnumerationTraits<lldb_private::process_gdb_remote::
+ GDBRemoteCommunicationHistory::PacketType> {
+ static void enumeration(IO &io,
+ lldb_private::process_gdb_remote::
+ GDBRemoteCommunicationHistory::PacketType &value);
+};
+
+template <>
+struct ScalarTraits<lldb_private::process_gdb_remote::
+ GDBRemoteCommunicationHistory::Entry::BinaryData> {
+ static void output(const lldb_private::process_gdb_remote::
+ GDBRemoteCommunicationHistory::Entry::BinaryData &,
+ void *, raw_ostream &);
+
+ static StringRef
+ input(StringRef, void *,
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry::
+ BinaryData &);
+
+ static QuotingType mustQuote(StringRef S) { return QuotingType::None; }
+};
+
+template <>
+struct MappingTraits<
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry> {
+ static void
+ mapping(IO &io,
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry
+ &Entry);
+
+ static StringRef validate(
+ IO &io,
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry &);
+};
+
+} // namespace yaml
+} // namespace llvm
+
+#endif // liblldb_GDBRemoteCommunicationHistory_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp
new file mode 100644
index 000000000000..417f5737a30f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp
@@ -0,0 +1,282 @@
+//===-- GDBRemoteCommunicationReplayServer.cpp ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+
+#include "lldb/Host/Config.h"
+
+#include "GDBRemoteCommunicationReplayServer.h"
+#include "ProcessGDBRemoteLog.h"
+
+// C Includes
+// C++ Includes
+#include <cstring>
+
+// Project includes
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+/// Check if the given expected packet matches the actual packet.
+static bool unexpected(llvm::StringRef expected, llvm::StringRef actual) {
+ // The 'expected' string contains the raw data, including the leading $ and
+ // trailing checksum. The 'actual' string contains only the packet's content.
+ if (expected.contains(actual))
+ return false;
+ // Contains a PID which might be different.
+ if (expected.contains("vAttach"))
+ return false;
+ // Contains a ascii-hex-path.
+ if (expected.contains("QSetSTD"))
+ return false;
+ // Contains environment values.
+ if (expected.contains("QEnvironment"))
+ return false;
+
+ return true;
+}
+
+/// Check if we should reply to the given packet.
+static bool skip(llvm::StringRef data) {
+ assert(!data.empty() && "Empty packet?");
+
+ // We've already acknowledge the '+' packet so we're done here.
+ if (data == "+")
+ return true;
+
+ /// Don't 't reply to ^C. We need this because of stop reply packets, which
+ /// are only returned when the target halts. Reproducers synchronize these
+ /// 'asynchronous' replies, by recording them as a regular replies to the
+ /// previous packet (e.g. vCont). As a result, we should ignore real
+ /// asynchronous requests.
+ if (data.data()[0] == 0x03)
+ return true;
+
+ return false;
+}
+
+GDBRemoteCommunicationReplayServer::GDBRemoteCommunicationReplayServer()
+ : GDBRemoteCommunication("gdb-replay", "gdb-replay.rx_packet"),
+ m_async_broadcaster(nullptr, "lldb.gdb-replay.async-broadcaster"),
+ m_async_listener_sp(
+ Listener::MakeListener("lldb.gdb-replay.async-listener")),
+ m_async_thread_state_mutex(), m_skip_acks(false) {
+ m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
+ "async thread continue");
+ m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
+ "async thread should exit");
+
+ const uint32_t async_event_mask =
+ eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit;
+ m_async_listener_sp->StartListeningForEvents(&m_async_broadcaster,
+ async_event_mask);
+}
+
+GDBRemoteCommunicationReplayServer::~GDBRemoteCommunicationReplayServer() {
+ StopAsyncThread();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationReplayServer::GetPacketAndSendResponse(
+ Timeout<std::micro> timeout, Status &error, bool &interrupt, bool &quit) {
+ std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+
+ StringExtractorGDBRemote packet;
+ PacketResult packet_result = WaitForPacketNoLock(packet, timeout, false);
+
+ if (packet_result != PacketResult::Success) {
+ if (!IsConnected()) {
+ error.SetErrorString("lost connection");
+ quit = true;
+ } else {
+ error.SetErrorString("timeout");
+ }
+ return packet_result;
+ }
+
+ m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue);
+
+ // Check if we should reply to this packet.
+ if (skip(packet.GetStringRef()))
+ return PacketResult::Success;
+
+ // This completes the handshake. Since m_send_acks was true, we can unset it
+ // already.
+ if (packet.GetStringRef() == "QStartNoAckMode")
+ m_send_acks = false;
+
+ // A QEnvironment packet is sent for every environment variable. If the
+ // number of environment variables is different during replay, the replies
+ // become out of sync.
+ if (packet.GetStringRef().find("QEnvironment") == 0)
+ return SendRawPacketNoLock("$OK#9a");
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ while (!m_packet_history.empty()) {
+ // Pop last packet from the history.
+ GDBRemoteCommunicationHistory::Entry entry = m_packet_history.back();
+ m_packet_history.pop_back();
+
+ // We've handled the handshake implicitly before. Skip the packet and move
+ // on.
+ if (entry.packet.data == "+")
+ continue;
+
+ if (entry.type == GDBRemoteCommunicationHistory::ePacketTypeSend) {
+ if (unexpected(entry.packet.data, packet.GetStringRef())) {
+ LLDB_LOG(log,
+ "GDBRemoteCommunicationReplayServer expected packet: '{0}'",
+ entry.packet.data);
+ LLDB_LOG(log, "GDBRemoteCommunicationReplayServer actual packet: '{0}'",
+ packet.GetStringRef());
+ assert(false && "Encountered unexpected packet during replay");
+ return PacketResult::ErrorSendFailed;
+ }
+
+ // Ignore QEnvironment packets as they're handled earlier.
+ if (entry.packet.data.find("QEnvironment") == 1) {
+ assert(m_packet_history.back().type ==
+ GDBRemoteCommunicationHistory::ePacketTypeRecv);
+ m_packet_history.pop_back();
+ }
+
+ continue;
+ }
+
+ if (entry.type == GDBRemoteCommunicationHistory::ePacketTypeInvalid) {
+ LLDB_LOG(
+ log,
+ "GDBRemoteCommunicationReplayServer skipped invalid packet: '{0}'",
+ packet.GetStringRef());
+ continue;
+ }
+
+ LLDB_LOG(log,
+ "GDBRemoteCommunicationReplayServer replied to '{0}' with '{1}'",
+ packet.GetStringRef(), entry.packet.data);
+ return SendRawPacketNoLock(entry.packet.data);
+ }
+
+ quit = true;
+
+ return packet_result;
+}
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(
+ std::vector<
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry>)
+
+llvm::Error
+GDBRemoteCommunicationReplayServer::LoadReplayHistory(const FileSpec &path) {
+ auto error_or_file = MemoryBuffer::getFile(path.GetPath());
+ if (auto err = error_or_file.getError())
+ return errorCodeToError(err);
+
+ yaml::Input yin((*error_or_file)->getBuffer());
+ yin >> m_packet_history;
+
+ if (auto err = yin.error())
+ return errorCodeToError(err);
+
+ // We want to manipulate the vector like a stack so we need to reverse the
+ // order of the packets to have the oldest on at the back.
+ std::reverse(m_packet_history.begin(), m_packet_history.end());
+
+ return Error::success();
+}
+
+bool GDBRemoteCommunicationReplayServer::StartAsyncThread() {
+ std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+ if (!m_async_thread.IsJoinable()) {
+ // Create a thread that watches our internal state and controls which
+ // events make it to clients (into the DCProcess event queue).
+ llvm::Expected<HostThread> async_thread = ThreadLauncher::LaunchThread(
+ "<lldb.gdb-replay.async>",
+ GDBRemoteCommunicationReplayServer::AsyncThread, this);
+ if (!async_thread) {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(async_thread.takeError()));
+ return false;
+ }
+ m_async_thread = *async_thread;
+ }
+
+ // Wait for handshake.
+ m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue);
+
+ return m_async_thread.IsJoinable();
+}
+
+void GDBRemoteCommunicationReplayServer::StopAsyncThread() {
+ std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+
+ if (!m_async_thread.IsJoinable())
+ return;
+
+ // Request thread to stop.
+ m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit);
+
+ // Disconnect client.
+ Disconnect();
+
+ // Stop the thread.
+ m_async_thread.Join(nullptr);
+ m_async_thread.Reset();
+}
+
+void GDBRemoteCommunicationReplayServer::ReceivePacket(
+ GDBRemoteCommunicationReplayServer &server, bool &done) {
+ Status error;
+ bool interrupt;
+ auto packet_result = server.GetPacketAndSendResponse(std::chrono::seconds(1),
+ error, interrupt, done);
+ if (packet_result != GDBRemoteCommunication::PacketResult::Success &&
+ packet_result !=
+ GDBRemoteCommunication::PacketResult::ErrorReplyTimeout) {
+ done = true;
+ } else {
+ server.m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue);
+ }
+}
+
+thread_result_t GDBRemoteCommunicationReplayServer::AsyncThread(void *arg) {
+ GDBRemoteCommunicationReplayServer *server =
+ (GDBRemoteCommunicationReplayServer *)arg;
+
+ EventSP event_sp;
+ bool done = false;
+
+ while (true) {
+ if (server->m_async_listener_sp->GetEvent(event_sp, llvm::None)) {
+ const uint32_t event_type = event_sp->GetType();
+ if (event_sp->BroadcasterIs(&server->m_async_broadcaster)) {
+ switch (event_type) {
+ case eBroadcastBitAsyncContinue:
+ ReceivePacket(*server, done);
+ if (done)
+ return {};
+ break;
+ case eBroadcastBitAsyncThreadShouldExit:
+ default:
+ return {};
+ }
+ }
+ }
+ }
+
+ return {};
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h
new file mode 100644
index 000000000000..26d65e265463
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h
@@ -0,0 +1,82 @@
+//===-- GDBRemoteCommunicationReplayServer.h --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunicationReplayServer_h_
+#define liblldb_GDBRemoteCommunicationReplayServer_h_
+
+// Other libraries and framework includes
+#include "GDBRemoteCommunication.h"
+#include "GDBRemoteCommunicationHistory.h"
+
+// Project includes
+#include "lldb/Host/HostThread.h"
+#include "lldb/Utility/Broadcaster.h"
+#include "lldb/lldb-private-forward.h"
+#include "llvm/Support/Error.h"
+
+// C Includes
+// C++ Includes
+#include <functional>
+#include <map>
+#include <thread>
+
+class StringExtractorGDBRemote;
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class ProcessGDBRemote;
+
+/// Dummy GDB server that replays packets from the GDB Remote Communication
+/// history. This is used to replay GDB packets.
+class GDBRemoteCommunicationReplayServer : public GDBRemoteCommunication {
+public:
+ GDBRemoteCommunicationReplayServer();
+
+ ~GDBRemoteCommunicationReplayServer() override;
+
+ PacketResult GetPacketAndSendResponse(Timeout<std::micro> timeout,
+ Status &error, bool &interrupt,
+ bool &quit);
+
+ bool HandshakeWithClient() { return GetAck() == PacketResult::Success; }
+
+ llvm::Error LoadReplayHistory(const FileSpec &path);
+
+ bool StartAsyncThread();
+ void StopAsyncThread();
+
+protected:
+ enum {
+ eBroadcastBitAsyncContinue = (1 << 0),
+ eBroadcastBitAsyncThreadShouldExit = (1 << 1),
+ };
+
+ static void ReceivePacket(GDBRemoteCommunicationReplayServer &server,
+ bool &done);
+ static lldb::thread_result_t AsyncThread(void *arg);
+
+ /// Replay history with the oldest packet at the end.
+ std::vector<GDBRemoteCommunicationHistory::Entry> m_packet_history;
+
+ /// Server thread.
+ Broadcaster m_async_broadcaster;
+ lldb::ListenerSP m_async_listener_sp;
+ HostThread m_async_thread;
+ std::recursive_mutex m_async_thread_state_mutex;
+
+ bool m_skip_acks;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationReplayServer);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationReplayServer_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
new file mode 100644
index 000000000000..49cbeb023fd5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
@@ -0,0 +1,158 @@
+//===-- GDBRemoteCommunicationServer.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+
+#include "lldb/Host/Config.h"
+
+#include "GDBRemoteCommunicationServer.h"
+
+#include <cstring>
+
+#include "ProcessGDBRemoteLog.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(
+ const char *comm_name, const char *listener_name)
+ : GDBRemoteCommunication(comm_name, listener_name), m_exit_now(false) {
+ RegisterPacketHandler(
+ StringExtractorGDBRemote::eServerPacketType_QEnableErrorStrings,
+ [this](StringExtractorGDBRemote packet, Status &error, bool &interrupt,
+ bool &quit) { return this->Handle_QErrorStringEnable(packet); });
+}
+
+GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer() {}
+
+void GDBRemoteCommunicationServer::RegisterPacketHandler(
+ StringExtractorGDBRemote::ServerPacketType packet_type,
+ PacketHandler handler) {
+ m_packet_handlers[packet_type] = std::move(handler);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::GetPacketAndSendResponse(
+ Timeout<std::micro> timeout, Status &error, bool &interrupt, bool &quit) {
+ StringExtractorGDBRemote packet;
+
+ PacketResult packet_result = WaitForPacketNoLock(packet, timeout, false);
+ if (packet_result == PacketResult::Success) {
+ const StringExtractorGDBRemote::ServerPacketType packet_type =
+ packet.GetServerPacketType();
+ switch (packet_type) {
+ case StringExtractorGDBRemote::eServerPacketType_nack:
+ case StringExtractorGDBRemote::eServerPacketType_ack:
+ break;
+
+ case StringExtractorGDBRemote::eServerPacketType_invalid:
+ error.SetErrorString("invalid packet");
+ quit = true;
+ break;
+
+ case StringExtractorGDBRemote::eServerPacketType_unimplemented:
+ packet_result = SendUnimplementedResponse(packet.GetStringRef().c_str());
+ break;
+
+ default:
+ auto handler_it = m_packet_handlers.find(packet_type);
+ if (handler_it == m_packet_handlers.end())
+ packet_result =
+ SendUnimplementedResponse(packet.GetStringRef().c_str());
+ else
+ packet_result = handler_it->second(packet, error, interrupt, quit);
+ break;
+ }
+ } else {
+ if (!IsConnected()) {
+ error.SetErrorString("lost connection");
+ quit = true;
+ } else {
+ error.SetErrorString("timeout");
+ }
+ }
+
+ // Check if anything occurred that would force us to want to exit.
+ if (m_exit_now)
+ quit = true;
+
+ return packet_result;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendUnimplementedResponse(const char *) {
+ // TODO: Log the packet we aren't handling...
+ return SendPacketNoLock("");
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendErrorResponse(uint8_t err) {
+ char packet[16];
+ int packet_len = ::snprintf(packet, sizeof(packet), "E%2.2x", err);
+ assert(packet_len < (int)sizeof(packet));
+ return SendPacketNoLock(llvm::StringRef(packet, packet_len));
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendErrorResponse(const Status &error) {
+ if (m_send_error_strings) {
+ lldb_private::StreamString packet;
+ packet.Printf("E%2.2x;", static_cast<uint8_t>(error.GetError()));
+ packet.PutStringAsRawHex8(error.AsCString());
+ return SendPacketNoLock(packet.GetString());
+ } else
+ return SendErrorResponse(error.GetError());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendErrorResponse(llvm::Error error) {
+ std::unique_ptr<llvm::ErrorInfoBase> EIB;
+ std::unique_ptr<PacketUnimplementedError> PUE;
+ llvm::handleAllErrors(
+ std::move(error),
+ [&](std::unique_ptr<PacketUnimplementedError> E) { PUE = std::move(E); },
+ [&](std::unique_ptr<llvm::ErrorInfoBase> E) { EIB = std::move(E); });
+
+ if (EIB)
+ return SendErrorResponse(Status(llvm::Error(std::move(EIB))));
+ if (PUE)
+ return SendUnimplementedResponse(PUE->message().c_str());
+ return SendErrorResponse(Status("Unknown Error"));
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::Handle_QErrorStringEnable(
+ StringExtractorGDBRemote &packet) {
+ m_send_error_strings = true;
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendIllFormedResponse(
+ const StringExtractorGDBRemote &failed_packet, const char *message) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServer::%s: ILLFORMED: '%s' (%s)",
+ __FUNCTION__, failed_packet.GetStringRef().c_str(),
+ message ? message : "");
+ return SendErrorResponse(0x03);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendOKResponse() {
+ return SendPacketNoLock("OK");
+}
+
+bool GDBRemoteCommunicationServer::HandshakeWithClient() {
+ return GetAck() == PacketResult::Success;
+}
+
+char PacketUnimplementedError::ID;
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
new file mode 100644
index 000000000000..86f0abf45e06
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
@@ -0,0 +1,95 @@
+//===-- GDBRemoteCommunicationServer.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunicationServer_h_
+#define liblldb_GDBRemoteCommunicationServer_h_
+
+#include <functional>
+#include <map>
+
+#include "GDBRemoteCommunication.h"
+#include "lldb/lldb-private-forward.h"
+
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+
+class StringExtractorGDBRemote;
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class ProcessGDBRemote;
+
+class GDBRemoteCommunicationServer : public GDBRemoteCommunication {
+public:
+ using PortMap = std::map<uint16_t, lldb::pid_t>;
+ using PacketHandler =
+ std::function<PacketResult(StringExtractorGDBRemote &packet,
+ Status &error, bool &interrupt, bool &quit)>;
+
+ GDBRemoteCommunicationServer(const char *comm_name,
+ const char *listener_name);
+
+ ~GDBRemoteCommunicationServer() override;
+
+ void
+ RegisterPacketHandler(StringExtractorGDBRemote::ServerPacketType packet_type,
+ PacketHandler handler);
+
+ PacketResult GetPacketAndSendResponse(Timeout<std::micro> timeout,
+ Status &error, bool &interrupt,
+ bool &quit);
+
+ // After connecting, do a little handshake with the client to make sure
+ // we are at least communicating
+ bool HandshakeWithClient();
+
+protected:
+ std::map<StringExtractorGDBRemote::ServerPacketType, PacketHandler>
+ m_packet_handlers;
+ bool m_exit_now; // use in asynchronous handling to indicate process should
+ // exit.
+
+ bool m_send_error_strings = false; // If the client enables this then
+ // we will send error strings as well.
+
+ PacketResult Handle_QErrorStringEnable(StringExtractorGDBRemote &packet);
+
+ PacketResult SendErrorResponse(const Status &error);
+
+ PacketResult SendErrorResponse(llvm::Error error);
+
+ PacketResult SendUnimplementedResponse(const char *packet);
+
+ PacketResult SendErrorResponse(uint8_t error);
+
+ PacketResult SendIllFormedResponse(const StringExtractorGDBRemote &packet,
+ const char *error_message);
+
+ PacketResult SendOKResponse();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationServer);
+};
+
+class PacketUnimplementedError
+ : public llvm::ErrorInfo<PacketUnimplementedError, llvm::StringError> {
+public:
+ static char ID;
+ using llvm::ErrorInfo<PacketUnimplementedError,
+ llvm::StringError>::ErrorInfo; // inherit constructors
+ PacketUnimplementedError(const llvm::Twine &S)
+ : ErrorInfo(S, llvm::errc::not_supported) {}
+
+ PacketUnimplementedError() : ErrorInfo(llvm::errc::not_supported) {}
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationServer_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
new file mode 100644
index 000000000000..d095c7a057ad
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -0,0 +1,1285 @@
+//===-- GDBRemoteCommunicationServerCommon.cpp ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteCommunicationServerCommon.h"
+
+#include <errno.h>
+
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
+#include <chrono>
+#include <cstring>
+
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/SafeMachO.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/JSON.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamGDBRemote.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StructuredData.h"
+#include "llvm/ADT/Triple.h"
+
+#include "ProcessGDBRemoteLog.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+#ifdef __ANDROID__
+#include "lldb/Host/android/HostInfoAndroid.h"
+#endif
+
+#include "llvm/ADT/StringSwitch.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+#ifdef __ANDROID__
+const static uint32_t g_default_packet_timeout_sec = 20; // seconds
+#else
+const static uint32_t g_default_packet_timeout_sec = 0; // not specified
+#endif
+
+// GDBRemoteCommunicationServerCommon constructor
+GDBRemoteCommunicationServerCommon::GDBRemoteCommunicationServerCommon(
+ const char *comm_name, const char *listener_name)
+ : GDBRemoteCommunicationServer(comm_name, listener_name),
+ m_process_launch_info(), m_process_launch_error(), m_proc_infos(),
+ m_proc_infos_index(0), m_thread_suffix_supported(false),
+ m_list_threads_in_stop_reply(false) {
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_A,
+ &GDBRemoteCommunicationServerCommon::Handle_A);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QEnvironment,
+ &GDBRemoteCommunicationServerCommon::Handle_QEnvironment);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QEnvironmentHexEncoded,
+ &GDBRemoteCommunicationServerCommon::Handle_QEnvironmentHexEncoded);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qfProcessInfo,
+ &GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qGroupName,
+ &GDBRemoteCommunicationServerCommon::Handle_qGroupName);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qHostInfo,
+ &GDBRemoteCommunicationServerCommon::Handle_qHostInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QLaunchArch,
+ &GDBRemoteCommunicationServerCommon::Handle_QLaunchArch);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess,
+ &GDBRemoteCommunicationServerCommon::Handle_qLaunchSuccess);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QListThreadsInStopReply,
+ &GDBRemoteCommunicationServerCommon::Handle_QListThreadsInStopReply);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qEcho,
+ &GDBRemoteCommunicationServerCommon::Handle_qEcho);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qModuleInfo,
+ &GDBRemoteCommunicationServerCommon::Handle_qModuleInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jModulesInfo,
+ &GDBRemoteCommunicationServerCommon::Handle_jModulesInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qPlatform_chmod,
+ &GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qPlatform_mkdir,
+ &GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qPlatform_shell,
+ &GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qProcessInfoPID,
+ &GDBRemoteCommunicationServerCommon::Handle_qProcessInfoPID);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetDetachOnError,
+ &GDBRemoteCommunicationServerCommon::Handle_QSetDetachOnError);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetSTDERR,
+ &GDBRemoteCommunicationServerCommon::Handle_QSetSTDERR);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetSTDIN,
+ &GDBRemoteCommunicationServerCommon::Handle_QSetSTDIN);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetSTDOUT,
+ &GDBRemoteCommunicationServerCommon::Handle_QSetSTDOUT);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qSpeedTest,
+ &GDBRemoteCommunicationServerCommon::Handle_qSpeedTest);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qsProcessInfo,
+ &GDBRemoteCommunicationServerCommon::Handle_qsProcessInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode,
+ &GDBRemoteCommunicationServerCommon::Handle_QStartNoAckMode);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qSupported,
+ &GDBRemoteCommunicationServerCommon::Handle_qSupported);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QThreadSuffixSupported,
+ &GDBRemoteCommunicationServerCommon::Handle_QThreadSuffixSupported);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qUserName,
+ &GDBRemoteCommunicationServerCommon::Handle_qUserName);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_close,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Close);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_exists,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Exists);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_md5,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_MD5);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_mode,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Mode);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_open,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Open);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_pread,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_pRead);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_pwrite,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_size,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Size);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_stat,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Stat);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_symlink,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_symlink);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_unlink,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_unlink);
+}
+
+// Destructor
+GDBRemoteCommunicationServerCommon::~GDBRemoteCommunicationServerCommon() {}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qHostInfo(
+ StringExtractorGDBRemote &packet) {
+ StreamString response;
+
+ // $cputype:16777223;cpusubtype:3;ostype:Darwin;vendor:apple;endian:little;ptrsize:8;#00
+
+ ArchSpec host_arch(HostInfo::GetArchitecture());
+ const llvm::Triple &host_triple = host_arch.GetTriple();
+ response.PutCString("triple:");
+ response.PutStringAsRawHex8(host_triple.getTriple());
+ response.Printf(";ptrsize:%u;", host_arch.GetAddressByteSize());
+
+ const char *distribution_id = host_arch.GetDistributionId().AsCString();
+ if (distribution_id) {
+ response.PutCString("distribution_id:");
+ response.PutStringAsRawHex8(distribution_id);
+ response.PutCString(";");
+ }
+
+#if defined(__APPLE__)
+ // For parity with debugserver, we'll include the vendor key.
+ response.PutCString("vendor:apple;");
+
+ // Send out MachO info.
+ uint32_t cpu = host_arch.GetMachOCPUType();
+ uint32_t sub = host_arch.GetMachOCPUSubType();
+ if (cpu != LLDB_INVALID_CPUTYPE)
+ response.Printf("cputype:%u;", cpu);
+ if (sub != LLDB_INVALID_CPUTYPE)
+ response.Printf("cpusubtype:%u;", sub);
+
+ if (cpu == llvm::MachO::CPU_TYPE_ARM || cpu == llvm::MachO::CPU_TYPE_ARM64) {
+// Indicate the OS type.
+#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
+ response.PutCString("ostype:tvos;");
+#elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ response.PutCString("ostype:watchos;");
+#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1
+ response.PutCString("ostype:bridgeos;");
+#else
+ response.PutCString("ostype:ios;");
+#endif
+
+ // On arm, we use "synchronous" watchpoints which means the exception is
+ // delivered before the instruction executes.
+ response.PutCString("watchpoint_exceptions_received:before;");
+ } else {
+ response.PutCString("ostype:macosx;");
+ response.Printf("watchpoint_exceptions_received:after;");
+ }
+
+#else
+ if (host_arch.GetMachine() == llvm::Triple::aarch64 ||
+ host_arch.GetMachine() == llvm::Triple::aarch64_be ||
+ host_arch.GetMachine() == llvm::Triple::arm ||
+ host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS())
+ response.Printf("watchpoint_exceptions_received:before;");
+ else
+ response.Printf("watchpoint_exceptions_received:after;");
+#endif
+
+ switch (endian::InlHostByteOrder()) {
+ case eByteOrderBig:
+ response.PutCString("endian:big;");
+ break;
+ case eByteOrderLittle:
+ response.PutCString("endian:little;");
+ break;
+ case eByteOrderPDP:
+ response.PutCString("endian:pdp;");
+ break;
+ default:
+ response.PutCString("endian:unknown;");
+ break;
+ }
+
+ llvm::VersionTuple version = HostInfo::GetOSVersion();
+ if (!version.empty()) {
+ response.Format("os_version:{0}", version.getAsString());
+ response.PutChar(';');
+ }
+
+ std::string s;
+ if (HostInfo::GetOSBuildString(s)) {
+ response.PutCString("os_build:");
+ response.PutStringAsRawHex8(s);
+ response.PutChar(';');
+ }
+ if (HostInfo::GetOSKernelDescription(s)) {
+ response.PutCString("os_kernel:");
+ response.PutStringAsRawHex8(s);
+ response.PutChar(';');
+ }
+
+#if defined(__APPLE__)
+
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ // For iOS devices, we are connected through a USB Mux so we never pretend to
+ // actually have a hostname as far as the remote lldb that is connecting to
+ // this lldb-platform is concerned
+ response.PutCString("hostname:");
+ response.PutStringAsRawHex8("127.0.0.1");
+ response.PutChar(';');
+#else // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ if (HostInfo::GetHostname(s)) {
+ response.PutCString("hostname:");
+ response.PutStringAsRawHex8(s);
+ response.PutChar(';');
+ }
+#endif // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+
+#else // #if defined(__APPLE__)
+ if (HostInfo::GetHostname(s)) {
+ response.PutCString("hostname:");
+ response.PutStringAsRawHex8(s);
+ response.PutChar(';');
+ }
+#endif // #if defined(__APPLE__)
+
+ if (g_default_packet_timeout_sec > 0)
+ response.Printf("default_packet_timeout:%u;", g_default_packet_timeout_sec);
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qProcessInfoPID(
+ StringExtractorGDBRemote &packet) {
+ // Packet format: "qProcessInfoPID:%i" where %i is the pid
+ packet.SetFilePos(::strlen("qProcessInfoPID:"));
+ lldb::pid_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID);
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ ProcessInstanceInfo proc_info;
+ if (Host::GetProcessInfo(pid, proc_info)) {
+ StreamString response;
+ CreateProcessInfoResponse(proc_info, response);
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ return SendErrorResponse(1);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo(
+ StringExtractorGDBRemote &packet) {
+ m_proc_infos_index = 0;
+ m_proc_infos.Clear();
+
+ ProcessInstanceInfoMatch match_info;
+ packet.SetFilePos(::strlen("qfProcessInfo"));
+ if (packet.GetChar() == ':') {
+ llvm::StringRef key;
+ llvm::StringRef value;
+ while (packet.GetNameColonValue(key, value)) {
+ bool success = true;
+ if (key.equals("name")) {
+ StringExtractor extractor(value);
+ std::string file;
+ extractor.GetHexByteString(file);
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ file, FileSpec::Style::native);
+ } else if (key.equals("name_match")) {
+ NameMatch name_match = llvm::StringSwitch<NameMatch>(value)
+ .Case("equals", NameMatch::Equals)
+ .Case("starts_with", NameMatch::StartsWith)
+ .Case("ends_with", NameMatch::EndsWith)
+ .Case("contains", NameMatch::Contains)
+ .Case("regex", NameMatch::RegularExpression)
+ .Default(NameMatch::Ignore);
+ match_info.SetNameMatchType(name_match);
+ if (name_match == NameMatch::Ignore)
+ return SendErrorResponse(2);
+ } else if (key.equals("pid")) {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ if (value.getAsInteger(0, pid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetProcessID(pid);
+ } else if (key.equals("parent_pid")) {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ if (value.getAsInteger(0, pid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetParentProcessID(pid);
+ } else if (key.equals("uid")) {
+ uint32_t uid = UINT32_MAX;
+ if (value.getAsInteger(0, uid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetUserID(uid);
+ } else if (key.equals("gid")) {
+ uint32_t gid = UINT32_MAX;
+ if (value.getAsInteger(0, gid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetGroupID(gid);
+ } else if (key.equals("euid")) {
+ uint32_t uid = UINT32_MAX;
+ if (value.getAsInteger(0, uid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetEffectiveUserID(uid);
+ } else if (key.equals("egid")) {
+ uint32_t gid = UINT32_MAX;
+ if (value.getAsInteger(0, gid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetEffectiveGroupID(gid);
+ } else if (key.equals("all_users")) {
+ match_info.SetMatchAllUsers(
+ OptionArgParser::ToBoolean(value, false, &success));
+ } else if (key.equals("triple")) {
+ match_info.GetProcessInfo().GetArchitecture() =
+ HostInfo::GetAugmentedArchSpec(value);
+ } else {
+ success = false;
+ }
+
+ if (!success)
+ return SendErrorResponse(2);
+ }
+ }
+
+ if (Host::FindProcesses(match_info, m_proc_infos)) {
+ // We found something, return the first item by calling the get subsequent
+ // process info packet handler...
+ return Handle_qsProcessInfo(packet);
+ }
+ return SendErrorResponse(3);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qsProcessInfo(
+ StringExtractorGDBRemote &packet) {
+ if (m_proc_infos_index < m_proc_infos.GetSize()) {
+ StreamString response;
+ CreateProcessInfoResponse(
+ m_proc_infos.GetProcessInfoAtIndex(m_proc_infos_index), response);
+ ++m_proc_infos_index;
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(4);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qUserName(
+ StringExtractorGDBRemote &packet) {
+#if !defined(LLDB_DISABLE_POSIX)
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerCommon::%s begin", __FUNCTION__);
+
+ // Packet format: "qUserName:%i" where %i is the uid
+ packet.SetFilePos(::strlen("qUserName:"));
+ uint32_t uid = packet.GetU32(UINT32_MAX);
+ if (uid != UINT32_MAX) {
+ if (llvm::Optional<llvm::StringRef> name =
+ HostInfo::GetUserIDResolver().GetUserName(uid)) {
+ StreamString response;
+ response.PutStringAsRawHex8(*name);
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerCommon::%s end", __FUNCTION__);
+#endif
+ return SendErrorResponse(5);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qGroupName(
+ StringExtractorGDBRemote &packet) {
+#if !defined(LLDB_DISABLE_POSIX)
+ // Packet format: "qGroupName:%i" where %i is the gid
+ packet.SetFilePos(::strlen("qGroupName:"));
+ uint32_t gid = packet.GetU32(UINT32_MAX);
+ if (gid != UINT32_MAX) {
+ if (llvm::Optional<llvm::StringRef> name =
+ HostInfo::GetUserIDResolver().GetGroupName(gid)) {
+ StreamString response;
+ response.PutStringAsRawHex8(*name);
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+#endif
+ return SendErrorResponse(6);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qSpeedTest(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qSpeedTest:"));
+
+ llvm::StringRef key;
+ llvm::StringRef value;
+ bool success = packet.GetNameColonValue(key, value);
+ if (success && key.equals("response_size")) {
+ uint32_t response_size = 0;
+ if (!value.getAsInteger(0, response_size)) {
+ if (response_size == 0)
+ return SendOKResponse();
+ StreamString response;
+ uint32_t bytes_left = response_size;
+ response.PutCString("data:");
+ while (bytes_left > 0) {
+ if (bytes_left >= 26) {
+ response.PutCString("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ bytes_left -= 26;
+ } else {
+ response.Printf("%*.*s;", bytes_left, bytes_left,
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ bytes_left = 0;
+ }
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ return SendErrorResponse(7);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Open(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:open:"));
+ std::string path;
+ packet.GetHexByteStringTerminatedBy(path, ',');
+ if (!path.empty()) {
+ if (packet.GetChar() == ',') {
+ uint32_t flags = packet.GetHexMaxU32(false, 0);
+ if (packet.GetChar() == ',') {
+ mode_t mode = packet.GetHexMaxU32(false, 0600);
+ FileSpec path_spec(path);
+ FileSystem::Instance().Resolve(path_spec);
+ File file;
+ // Do not close fd.
+ Status error =
+ FileSystem::Instance().Open(file, path_spec, flags, mode, false);
+ const int save_errno = error.GetError();
+ StreamString response;
+ response.PutChar('F');
+ response.Printf("%i", file.GetDescriptor());
+ if (save_errno)
+ response.Printf(",%i", save_errno);
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ }
+ return SendErrorResponse(18);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Close(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:close:"));
+ int fd = packet.GetS32(-1);
+ int err = -1;
+ int save_errno = 0;
+ if (fd >= 0) {
+ File file(fd, true);
+ Status error = file.Close();
+ err = 0;
+ save_errno = error.GetError();
+ } else {
+ save_errno = EINVAL;
+ }
+ StreamString response;
+ response.PutChar('F');
+ response.Printf("%i", err);
+ if (save_errno)
+ response.Printf(",%i", save_errno);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_pRead(
+ StringExtractorGDBRemote &packet) {
+ StreamGDBRemote response;
+ packet.SetFilePos(::strlen("vFile:pread:"));
+ int fd = packet.GetS32(-1);
+ if (packet.GetChar() == ',') {
+ size_t count = packet.GetU64(UINT64_MAX);
+ if (packet.GetChar() == ',') {
+ off_t offset = packet.GetU64(UINT32_MAX);
+ if (count == UINT64_MAX) {
+ response.Printf("F-1:%i", EINVAL);
+ return SendPacketNoLock(response.GetString());
+ }
+
+ std::string buffer(count, 0);
+ File file(fd, false);
+ Status error = file.Read(static_cast<void *>(&buffer[0]), count, offset);
+ const ssize_t bytes_read = error.Success() ? count : -1;
+ const int save_errno = error.GetError();
+ response.PutChar('F');
+ response.Printf("%zi", bytes_read);
+ if (save_errno)
+ response.Printf(",%i", save_errno);
+ else {
+ response.PutChar(';');
+ response.PutEscapedBytes(&buffer[0], bytes_read);
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ return SendErrorResponse(21);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:pwrite:"));
+
+ StreamGDBRemote response;
+ response.PutChar('F');
+
+ int fd = packet.GetU32(UINT32_MAX);
+ if (packet.GetChar() == ',') {
+ off_t offset = packet.GetU64(UINT32_MAX);
+ if (packet.GetChar() == ',') {
+ std::string buffer;
+ if (packet.GetEscapedBinaryData(buffer)) {
+ File file(fd, false);
+ size_t count = buffer.size();
+ Status error =
+ file.Write(static_cast<const void *>(&buffer[0]), count, offset);
+ const ssize_t bytes_written = error.Success() ? count : -1;
+ const int save_errno = error.GetError();
+ response.Printf("%zi", bytes_written);
+ if (save_errno)
+ response.Printf(",%i", save_errno);
+ } else {
+ response.Printf("-1,%i", EINVAL);
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ return SendErrorResponse(27);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Size(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:size:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ if (!path.empty()) {
+ uint64_t Size;
+ if (llvm::sys::fs::file_size(path, Size))
+ return SendErrorResponse(5);
+ StreamString response;
+ response.PutChar('F');
+ response.PutHex64(Size);
+ if (Size == UINT64_MAX) {
+ response.PutChar(',');
+ response.PutHex64(Size); // TODO: replace with Host::GetSyswideErrorCode()
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(22);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Mode(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:mode:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ if (!path.empty()) {
+ FileSpec file_spec(path);
+ FileSystem::Instance().Resolve(file_spec);
+ std::error_code ec;
+ const uint32_t mode = FileSystem::Instance().GetPermissions(file_spec, ec);
+ StreamString response;
+ response.Printf("F%u", mode);
+ if (mode == 0 || ec)
+ response.Printf(",%i", (int)Status(ec).GetError());
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(23);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Exists(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:exists:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ if (!path.empty()) {
+ bool retcode = llvm::sys::fs::exists(path);
+ StreamString response;
+ response.PutChar('F');
+ response.PutChar(',');
+ if (retcode)
+ response.PutChar('1');
+ else
+ response.PutChar('0');
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(24);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_symlink(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:symlink:"));
+ std::string dst, src;
+ packet.GetHexByteStringTerminatedBy(dst, ',');
+ packet.GetChar(); // Skip ',' char
+ packet.GetHexByteString(src);
+
+ FileSpec src_spec(src);
+ FileSystem::Instance().Resolve(src_spec);
+ Status error = FileSystem::Instance().Symlink(src_spec, FileSpec(dst));
+
+ StreamString response;
+ response.Printf("F%u,%u", error.GetError(), error.GetError());
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_unlink(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:unlink:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ Status error(llvm::sys::fs::remove(path));
+ StreamString response;
+ response.Printf("F%u,%u", error.GetError(), error.GetError());
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qPlatform_shell:"));
+ std::string path;
+ std::string working_dir;
+ packet.GetHexByteStringTerminatedBy(path, ',');
+ if (!path.empty()) {
+ if (packet.GetChar() == ',') {
+ // FIXME: add timeout to qPlatform_shell packet
+ // uint32_t timeout = packet.GetHexMaxU32(false, 32);
+ if (packet.GetChar() == ',')
+ packet.GetHexByteString(working_dir);
+ int status, signo;
+ std::string output;
+ FileSpec working_spec(working_dir);
+ FileSystem::Instance().Resolve(working_spec);
+ Status err =
+ Host::RunShellCommand(path.c_str(), working_spec, &status, &signo,
+ &output, std::chrono::seconds(10));
+ StreamGDBRemote response;
+ if (err.Fail()) {
+ response.PutCString("F,");
+ response.PutHex32(UINT32_MAX);
+ } else {
+ response.PutCString("F,");
+ response.PutHex32(status);
+ response.PutChar(',');
+ response.PutHex32(signo);
+ response.PutChar(',');
+ response.PutEscapedBytes(output.c_str(), output.size());
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ return SendErrorResponse(24);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Stat(
+ StringExtractorGDBRemote &packet) {
+ return SendUnimplementedResponse(
+ "GDBRemoteCommunicationServerCommon::Handle_vFile_Stat() unimplemented");
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_MD5(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:MD5:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ if (!path.empty()) {
+ StreamGDBRemote response;
+ auto Result = llvm::sys::fs::md5_contents(path);
+ if (!Result) {
+ response.PutCString("F,");
+ response.PutCString("x");
+ } else {
+ response.PutCString("F,");
+ response.PutHex64(Result->low());
+ response.PutHex64(Result->high());
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(25);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qPlatform_mkdir:"));
+ mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX);
+ if (packet.GetChar() == ',') {
+ std::string path;
+ packet.GetHexByteString(path);
+ Status error(llvm::sys::fs::create_directory(path, mode));
+
+ StreamGDBRemote response;
+ response.Printf("F%u", error.GetError());
+
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(20);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qPlatform_chmod:"));
+
+ auto perms =
+ static_cast<llvm::sys::fs::perms>(packet.GetHexMaxU32(false, UINT32_MAX));
+ if (packet.GetChar() == ',') {
+ std::string path;
+ packet.GetHexByteString(path);
+ Status error(llvm::sys::fs::setPermissions(path, perms));
+
+ StreamGDBRemote response;
+ response.Printf("F%u", error.GetError());
+
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(19);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qSupported(
+ StringExtractorGDBRemote &packet) {
+ StreamGDBRemote response;
+
+ // Features common to lldb-platform and llgs.
+ uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet
+ // size--debugger can always use less
+ response.Printf("PacketSize=%x", max_packet_size);
+
+ response.PutCString(";QStartNoAckMode+");
+ response.PutCString(";QThreadSuffixSupported+");
+ response.PutCString(";QListThreadsInStopReply+");
+ response.PutCString(";qEcho+");
+#if defined(__linux__) || defined(__NetBSD__)
+ response.PutCString(";QPassSignals+");
+ response.PutCString(";qXfer:auxv:read+");
+#endif
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QThreadSuffixSupported(
+ StringExtractorGDBRemote &packet) {
+ m_thread_suffix_supported = true;
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QListThreadsInStopReply(
+ StringExtractorGDBRemote &packet) {
+ m_list_threads_in_stop_reply = true;
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QSetDetachOnError(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetDetachOnError:"));
+ if (packet.GetU32(0))
+ m_process_launch_info.GetFlags().Set(eLaunchFlagDetachOnError);
+ else
+ m_process_launch_info.GetFlags().Clear(eLaunchFlagDetachOnError);
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QStartNoAckMode(
+ StringExtractorGDBRemote &packet) {
+ // Send response first before changing m_send_acks to we ack this packet
+ PacketResult packet_result = SendOKResponse();
+ m_send_acks = false;
+ return packet_result;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QSetSTDIN(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetSTDIN:"));
+ FileAction file_action;
+ std::string path;
+ packet.GetHexByteString(path);
+ const bool read = true;
+ const bool write = false;
+ if (file_action.Open(STDIN_FILENO, FileSpec(path), read, write)) {
+ m_process_launch_info.AppendFileAction(file_action);
+ return SendOKResponse();
+ }
+ return SendErrorResponse(15);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QSetSTDOUT(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetSTDOUT:"));
+ FileAction file_action;
+ std::string path;
+ packet.GetHexByteString(path);
+ const bool read = false;
+ const bool write = true;
+ if (file_action.Open(STDOUT_FILENO, FileSpec(path), read, write)) {
+ m_process_launch_info.AppendFileAction(file_action);
+ return SendOKResponse();
+ }
+ return SendErrorResponse(16);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QSetSTDERR(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetSTDERR:"));
+ FileAction file_action;
+ std::string path;
+ packet.GetHexByteString(path);
+ const bool read = false;
+ const bool write = true;
+ if (file_action.Open(STDERR_FILENO, FileSpec(path), read, write)) {
+ m_process_launch_info.AppendFileAction(file_action);
+ return SendOKResponse();
+ }
+ return SendErrorResponse(17);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qLaunchSuccess(
+ StringExtractorGDBRemote &packet) {
+ if (m_process_launch_error.Success())
+ return SendOKResponse();
+ StreamString response;
+ response.PutChar('E');
+ response.PutCString(m_process_launch_error.AsCString("<unknown error>"));
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QEnvironment(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QEnvironment:"));
+ const uint32_t bytes_left = packet.GetBytesLeft();
+ if (bytes_left > 0) {
+ m_process_launch_info.GetEnvironment().insert(packet.Peek());
+ return SendOKResponse();
+ }
+ return SendErrorResponse(12);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QEnvironmentHexEncoded(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QEnvironmentHexEncoded:"));
+ const uint32_t bytes_left = packet.GetBytesLeft();
+ if (bytes_left > 0) {
+ std::string str;
+ packet.GetHexByteString(str);
+ m_process_launch_info.GetEnvironment().insert(str);
+ return SendOKResponse();
+ }
+ return SendErrorResponse(12);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QLaunchArch(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QLaunchArch:"));
+ const uint32_t bytes_left = packet.GetBytesLeft();
+ if (bytes_left > 0) {
+ const char *arch_triple = packet.Peek();
+ m_process_launch_info.SetArchitecture(
+ HostInfo::GetAugmentedArchSpec(arch_triple));
+ return SendOKResponse();
+ }
+ return SendErrorResponse(13);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_A(StringExtractorGDBRemote &packet) {
+ // The 'A' packet is the most over designed packet ever here with redundant
+ // argument indexes, redundant argument lengths and needed hex encoded
+ // argument string values. Really all that is needed is a comma separated hex
+ // encoded argument value list, but we will stay true to the documented
+ // version of the 'A' packet here...
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ int actual_arg_index = 0;
+
+ packet.SetFilePos(1); // Skip the 'A'
+ bool success = true;
+ while (success && packet.GetBytesLeft() > 0) {
+ // Decode the decimal argument string length. This length is the number of
+ // hex nibbles in the argument string value.
+ const uint32_t arg_len = packet.GetU32(UINT32_MAX);
+ if (arg_len == UINT32_MAX)
+ success = false;
+ else {
+ // Make sure the argument hex string length is followed by a comma
+ if (packet.GetChar() != ',')
+ success = false;
+ else {
+ // Decode the argument index. We ignore this really because who would
+ // really send down the arguments in a random order???
+ const uint32_t arg_idx = packet.GetU32(UINT32_MAX);
+ if (arg_idx == UINT32_MAX)
+ success = false;
+ else {
+ // Make sure the argument index is followed by a comma
+ if (packet.GetChar() != ',')
+ success = false;
+ else {
+ // Decode the argument string value from hex bytes back into a UTF8
+ // string and make sure the length matches the one supplied in the
+ // packet
+ std::string arg;
+ if (packet.GetHexByteStringFixedLength(arg, arg_len) !=
+ (arg_len / 2))
+ success = false;
+ else {
+ // If there are any bytes left
+ if (packet.GetBytesLeft()) {
+ if (packet.GetChar() != ',')
+ success = false;
+ }
+
+ if (success) {
+ if (arg_idx == 0)
+ m_process_launch_info.GetExecutableFile().SetFile(
+ arg, FileSpec::Style::native);
+ m_process_launch_info.GetArguments().AppendArgument(arg);
+ if (log)
+ log->Printf("LLGSPacketHandler::%s added arg %d: \"%s\"",
+ __FUNCTION__, actual_arg_index, arg.c_str());
+ ++actual_arg_index;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success) {
+ m_process_launch_error = LaunchProcess();
+ if (m_process_launch_error.Success())
+ return SendOKResponse();
+ LLDB_LOG(log, "failed to launch exe: {0}", m_process_launch_error);
+ }
+ return SendErrorResponse(8);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qEcho(
+ StringExtractorGDBRemote &packet) {
+ // Just echo back the exact same packet for qEcho...
+ return SendPacketNoLock(packet.GetStringRef());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qModuleInfo(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qModuleInfo:"));
+
+ std::string module_path;
+ packet.GetHexByteStringTerminatedBy(module_path, ';');
+ if (module_path.empty())
+ return SendErrorResponse(1);
+
+ if (packet.GetChar() != ';')
+ return SendErrorResponse(2);
+
+ std::string triple;
+ packet.GetHexByteString(triple);
+
+ ModuleSpec matched_module_spec = GetModuleInfo(module_path, triple);
+ if (!matched_module_spec.GetFileSpec())
+ return SendErrorResponse(3);
+
+ const auto file_offset = matched_module_spec.GetObjectOffset();
+ const auto file_size = matched_module_spec.GetObjectSize();
+ const auto uuid_str = matched_module_spec.GetUUID().GetAsString("");
+
+ StreamGDBRemote response;
+
+ if (uuid_str.empty()) {
+ auto Result = llvm::sys::fs::md5_contents(
+ matched_module_spec.GetFileSpec().GetPath());
+ if (!Result)
+ return SendErrorResponse(5);
+ response.PutCString("md5:");
+ response.PutStringAsRawHex8(Result->digest());
+ } else {
+ response.PutCString("uuid:");
+ response.PutStringAsRawHex8(uuid_str);
+ }
+ response.PutChar(';');
+
+ const auto &module_arch = matched_module_spec.GetArchitecture();
+ response.PutCString("triple:");
+ response.PutStringAsRawHex8(module_arch.GetTriple().getTriple());
+ response.PutChar(';');
+
+ response.PutCString("file_path:");
+ response.PutStringAsRawHex8(matched_module_spec.GetFileSpec().GetCString());
+ response.PutChar(';');
+ response.PutCString("file_offset:");
+ response.PutHex64(file_offset);
+ response.PutChar(';');
+ response.PutCString("file_size:");
+ response.PutHex64(file_size);
+ response.PutChar(';');
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_jModulesInfo(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("jModulesInfo:"));
+
+ StructuredData::ObjectSP object_sp = StructuredData::ParseJSON(packet.Peek());
+ if (!object_sp)
+ return SendErrorResponse(1);
+
+ StructuredData::Array *packet_array = object_sp->GetAsArray();
+ if (!packet_array)
+ return SendErrorResponse(2);
+
+ JSONArray::SP response_array_sp = std::make_shared<JSONArray>();
+ for (size_t i = 0; i < packet_array->GetSize(); ++i) {
+ StructuredData::Dictionary *query =
+ packet_array->GetItemAtIndex(i)->GetAsDictionary();
+ if (!query)
+ continue;
+ llvm::StringRef file, triple;
+ if (!query->GetValueForKeyAsString("file", file) ||
+ !query->GetValueForKeyAsString("triple", triple))
+ continue;
+
+ ModuleSpec matched_module_spec = GetModuleInfo(file, triple);
+ if (!matched_module_spec.GetFileSpec())
+ continue;
+
+ const auto file_offset = matched_module_spec.GetObjectOffset();
+ const auto file_size = matched_module_spec.GetObjectSize();
+ const auto uuid_str = matched_module_spec.GetUUID().GetAsString("");
+
+ if (uuid_str.empty())
+ continue;
+
+ JSONObject::SP response = std::make_shared<JSONObject>();
+ response_array_sp->AppendObject(response);
+ response->SetObject("uuid", std::make_shared<JSONString>(uuid_str));
+ response->SetObject(
+ "triple",
+ std::make_shared<JSONString>(
+ matched_module_spec.GetArchitecture().GetTriple().getTriple()));
+ response->SetObject("file_path",
+ std::make_shared<JSONString>(
+ matched_module_spec.GetFileSpec().GetPath()));
+ response->SetObject("file_offset",
+ std::make_shared<JSONNumber>(file_offset));
+ response->SetObject("file_size", std::make_shared<JSONNumber>(file_size));
+ }
+
+ StreamString response;
+ response_array_sp->Write(response);
+ StreamGDBRemote escaped_response;
+ escaped_response.PutEscapedBytes(response.GetString().data(),
+ response.GetSize());
+ return SendPacketNoLock(escaped_response.GetString());
+}
+
+void GDBRemoteCommunicationServerCommon::CreateProcessInfoResponse(
+ const ProcessInstanceInfo &proc_info, StreamString &response) {
+ response.Printf(
+ "pid:%" PRIu64 ";ppid:%" PRIu64 ";uid:%i;gid:%i;euid:%i;egid:%i;",
+ proc_info.GetProcessID(), proc_info.GetParentProcessID(),
+ proc_info.GetUserID(), proc_info.GetGroupID(),
+ proc_info.GetEffectiveUserID(), proc_info.GetEffectiveGroupID());
+ response.PutCString("name:");
+ response.PutStringAsRawHex8(proc_info.GetExecutableFile().GetCString());
+ response.PutChar(';');
+ const ArchSpec &proc_arch = proc_info.GetArchitecture();
+ if (proc_arch.IsValid()) {
+ const llvm::Triple &proc_triple = proc_arch.GetTriple();
+ response.PutCString("triple:");
+ response.PutStringAsRawHex8(proc_triple.getTriple());
+ response.PutChar(';');
+ }
+}
+
+void GDBRemoteCommunicationServerCommon::
+ CreateProcessInfoResponse_DebugServerStyle(
+ const ProcessInstanceInfo &proc_info, StreamString &response) {
+ response.Printf("pid:%" PRIx64 ";parent-pid:%" PRIx64
+ ";real-uid:%x;real-gid:%x;effective-uid:%x;effective-gid:%x;",
+ proc_info.GetProcessID(), proc_info.GetParentProcessID(),
+ proc_info.GetUserID(), proc_info.GetGroupID(),
+ proc_info.GetEffectiveUserID(),
+ proc_info.GetEffectiveGroupID());
+
+ const ArchSpec &proc_arch = proc_info.GetArchitecture();
+ if (proc_arch.IsValid()) {
+ const llvm::Triple &proc_triple = proc_arch.GetTriple();
+#if defined(__APPLE__)
+ // We'll send cputype/cpusubtype.
+ const uint32_t cpu_type = proc_arch.GetMachOCPUType();
+ if (cpu_type != 0)
+ response.Printf("cputype:%" PRIx32 ";", cpu_type);
+
+ const uint32_t cpu_subtype = proc_arch.GetMachOCPUSubType();
+ if (cpu_subtype != 0)
+ response.Printf("cpusubtype:%" PRIx32 ";", cpu_subtype);
+
+ const std::string vendor = proc_triple.getVendorName();
+ if (!vendor.empty())
+ response.Printf("vendor:%s;", vendor.c_str());
+#else
+ // We'll send the triple.
+ response.PutCString("triple:");
+ response.PutStringAsRawHex8(proc_triple.getTriple());
+ response.PutChar(';');
+#endif
+ std::string ostype = proc_triple.getOSName();
+ // Adjust so ostype reports ios for Apple/ARM and Apple/ARM64.
+ if (proc_triple.getVendor() == llvm::Triple::Apple) {
+ switch (proc_triple.getArch()) {
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ case llvm::Triple::aarch64:
+ ostype = "ios";
+ break;
+ default:
+ // No change.
+ break;
+ }
+ }
+ response.Printf("ostype:%s;", ostype.c_str());
+
+ switch (proc_arch.GetByteOrder()) {
+ case lldb::eByteOrderLittle:
+ response.PutCString("endian:little;");
+ break;
+ case lldb::eByteOrderBig:
+ response.PutCString("endian:big;");
+ break;
+ case lldb::eByteOrderPDP:
+ response.PutCString("endian:pdp;");
+ break;
+ default:
+ // Nothing.
+ break;
+ }
+ // In case of MIPS64, pointer size is depend on ELF ABI For N32 the pointer
+ // size is 4 and for N64 it is 8
+ std::string abi = proc_arch.GetTargetABI();
+ if (!abi.empty())
+ response.Printf("elf_abi:%s;", abi.c_str());
+ response.Printf("ptrsize:%d;", proc_arch.GetAddressByteSize());
+ }
+}
+
+FileSpec GDBRemoteCommunicationServerCommon::FindModuleFile(
+ const std::string &module_path, const ArchSpec &arch) {
+#ifdef __ANDROID__
+ return HostInfoAndroid::ResolveLibraryPath(module_path, arch);
+#else
+ FileSpec file_spec(module_path);
+ FileSystem::Instance().Resolve(file_spec);
+ return file_spec;
+#endif
+}
+
+ModuleSpec
+GDBRemoteCommunicationServerCommon::GetModuleInfo(llvm::StringRef module_path,
+ llvm::StringRef triple) {
+ ArchSpec arch(triple);
+
+ FileSpec req_module_path_spec(module_path);
+ FileSystem::Instance().Resolve(req_module_path_spec);
+
+ const FileSpec module_path_spec =
+ FindModuleFile(req_module_path_spec.GetPath(), arch);
+ const ModuleSpec module_spec(module_path_spec, arch);
+
+ ModuleSpecList module_specs;
+ if (!ObjectFile::GetModuleSpecifications(module_path_spec, 0, 0,
+ module_specs))
+ return ModuleSpec();
+
+ ModuleSpec matched_module_spec;
+ if (!module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec))
+ return ModuleSpec();
+
+ return matched_module_spec;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
new file mode 100644
index 000000000000..525546312470
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
@@ -0,0 +1,155 @@
+//===-- GDBRemoteCommunicationServerCommon.h --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunicationServerCommon_h_
+#define liblldb_GDBRemoteCommunicationServerCommon_h_
+
+#include <string>
+
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/lldb-private-forward.h"
+
+#include "GDBRemoteCommunicationServer.h"
+#include "GDBRemoteCommunicationServerCommon.h"
+
+class StringExtractorGDBRemote;
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class ProcessGDBRemote;
+
+class GDBRemoteCommunicationServerCommon : public GDBRemoteCommunicationServer {
+public:
+ GDBRemoteCommunicationServerCommon(const char *comm_name,
+ const char *listener_name);
+
+ ~GDBRemoteCommunicationServerCommon() override;
+
+protected:
+ ProcessLaunchInfo m_process_launch_info;
+ Status m_process_launch_error;
+ ProcessInstanceInfoList m_proc_infos;
+ uint32_t m_proc_infos_index;
+ bool m_thread_suffix_supported;
+ bool m_list_threads_in_stop_reply;
+
+ PacketResult Handle_A(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qHostInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qProcessInfoPID(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qfProcessInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qsProcessInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qUserName(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qGroupName(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qSpeedTest(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Open(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Close(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_pRead(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_pWrite(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Size(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Mode(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Exists(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_symlink(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_unlink(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Stat(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_MD5(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qEcho(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qModuleInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jModulesInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qPlatform_shell(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qPlatform_mkdir(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qPlatform_chmod(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qSupported(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QThreadSuffixSupported(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QListThreadsInStopReply(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetDetachOnError(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QStartNoAckMode(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetSTDIN(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetSTDOUT(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetSTDERR(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qLaunchSuccess(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QEnvironment(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QEnvironmentHexEncoded(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QLaunchArch(StringExtractorGDBRemote &packet);
+
+ static void CreateProcessInfoResponse(const ProcessInstanceInfo &proc_info,
+ StreamString &response);
+
+ static void CreateProcessInfoResponse_DebugServerStyle(
+ const ProcessInstanceInfo &proc_info, StreamString &response);
+
+ template <typename T>
+ void RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::ServerPacketType packet_type,
+ PacketResult (T::*handler)(StringExtractorGDBRemote &packet)) {
+ RegisterPacketHandler(packet_type,
+ [this, handler](StringExtractorGDBRemote packet,
+ Status &error, bool &interrupt,
+ bool &quit) {
+ return (static_cast<T *>(this)->*handler)(packet);
+ });
+ }
+
+ /// Launch a process with the current launch settings.
+ ///
+ /// This method supports running an lldb-gdbserver or similar
+ /// server in a situation where the startup code has been provided
+ /// with all the information for a child process to be launched.
+ ///
+ /// \return
+ /// An Status object indicating the success or failure of the
+ /// launch.
+ virtual Status LaunchProcess() = 0;
+
+ virtual FileSpec FindModuleFile(const std::string &module_path,
+ const ArchSpec &arch);
+
+private:
+ ModuleSpec GetModuleInfo(llvm::StringRef module_path, llvm::StringRef triple);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationServerCommon_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
new file mode 100644
index 000000000000..196607665bba
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -0,0 +1,3285 @@
+//===-- GDBRemoteCommunicationServerLLGS.cpp --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+
+#include "lldb/Host/Config.h"
+
+#include "GDBRemoteCommunicationServerLLGS.h"
+#include "lldb/Utility/StreamGDBRemote.h"
+
+#include <chrono>
+#include <cstring>
+#include <thread>
+
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/Debug.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/JSON.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/UriParser.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+using namespace llvm;
+
+// GDBRemote Errors
+
+namespace {
+enum GDBRemoteServerError {
+ // Set to the first unused error number in literal form below
+ eErrorFirst = 29,
+ eErrorNoProcess = eErrorFirst,
+ eErrorResume,
+ eErrorExitStatus
+};
+}
+
+// GDBRemoteCommunicationServerLLGS constructor
+GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS(
+ MainLoop &mainloop, const NativeProcessProtocol::Factory &process_factory)
+ : GDBRemoteCommunicationServerCommon("gdb-remote.server",
+ "gdb-remote.server.rx_packet"),
+ m_mainloop(mainloop), m_process_factory(process_factory),
+ m_stdio_communication("process.stdio") {
+ RegisterPacketHandlers();
+}
+
+void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_C,
+ &GDBRemoteCommunicationServerLLGS::Handle_C);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_c,
+ &GDBRemoteCommunicationServerLLGS::Handle_c);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_D,
+ &GDBRemoteCommunicationServerLLGS::Handle_D);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_H,
+ &GDBRemoteCommunicationServerLLGS::Handle_H);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_I,
+ &GDBRemoteCommunicationServerLLGS::Handle_I);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_interrupt,
+ &GDBRemoteCommunicationServerLLGS::Handle_interrupt);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_m,
+ &GDBRemoteCommunicationServerLLGS::Handle_memory_read);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_M,
+ &GDBRemoteCommunicationServerLLGS::Handle_M);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_p,
+ &GDBRemoteCommunicationServerLLGS::Handle_p);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_P,
+ &GDBRemoteCommunicationServerLLGS::Handle_P);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC,
+ &GDBRemoteCommunicationServerLLGS::Handle_qC);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qfThreadInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qFileLoadAddress,
+ &GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir,
+ &GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfoSupported,
+ &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qProcessInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qRegisterInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QRestoreRegisterState,
+ &GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSaveRegisterState,
+ &GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR,
+ &GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir,
+ &GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qsThreadInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jThreadsInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qXfer,
+ &GDBRemoteCommunicationServerLLGS::Handle_qXfer);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_s,
+ &GDBRemoteCommunicationServerLLGS::Handle_s);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_stop_reason,
+ &GDBRemoteCommunicationServerLLGS::Handle_stop_reason); // ?
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vAttach,
+ &GDBRemoteCommunicationServerLLGS::Handle_vAttach);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vCont,
+ &GDBRemoteCommunicationServerLLGS::Handle_vCont);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vCont_actions,
+ &GDBRemoteCommunicationServerLLGS::Handle_vCont_actions);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_x,
+ &GDBRemoteCommunicationServerLLGS::Handle_memory_read);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_Z,
+ &GDBRemoteCommunicationServerLLGS::Handle_Z);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z,
+ &GDBRemoteCommunicationServerLLGS::Handle_z);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QPassSignals,
+ &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals);
+
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jTraceStart,
+ &GDBRemoteCommunicationServerLLGS::Handle_jTraceStart);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jTraceBufferRead,
+ &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jTraceMetaRead,
+ &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jTraceStop,
+ &GDBRemoteCommunicationServerLLGS::Handle_jTraceStop);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jTraceConfigRead,
+ &GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead);
+
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
+ &GDBRemoteCommunicationServerLLGS::Handle_g);
+
+ RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k,
+ [this](StringExtractorGDBRemote packet, Status &error,
+ bool &interrupt, bool &quit) {
+ quit = true;
+ return this->Handle_k(packet);
+ });
+}
+
+void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(const ProcessLaunchInfo &info) {
+ m_process_launch_info = info;
+}
+
+Status GDBRemoteCommunicationServerLLGS::LaunchProcess() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (!m_process_launch_info.GetArguments().GetArgumentCount())
+ return Status("%s: no process command line specified to launch",
+ __FUNCTION__);
+
+ const bool should_forward_stdio =
+ m_process_launch_info.GetFileActionForFD(STDIN_FILENO) == nullptr ||
+ m_process_launch_info.GetFileActionForFD(STDOUT_FILENO) == nullptr ||
+ m_process_launch_info.GetFileActionForFD(STDERR_FILENO) == nullptr;
+ m_process_launch_info.SetLaunchInSeparateProcessGroup(true);
+ m_process_launch_info.GetFlags().Set(eLaunchFlagDebug);
+
+ if (should_forward_stdio) {
+ if (llvm::Error Err = m_process_launch_info.SetUpPtyRedirection())
+ return Status(std::move(Err));
+ }
+
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_debugged_process_mutex);
+ assert(!m_debugged_process_up && "lldb-server creating debugged "
+ "process but one already exists");
+ auto process_or =
+ m_process_factory.Launch(m_process_launch_info, *this, m_mainloop);
+ if (!process_or)
+ return Status(process_or.takeError());
+ m_debugged_process_up = std::move(*process_or);
+ }
+
+ // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol as
+ // needed. llgs local-process debugging may specify PTY paths, which will
+ // make these file actions non-null process launch -i/e/o will also make
+ // these file actions non-null nullptr means that the traffic is expected to
+ // flow over gdb-remote protocol
+ if (should_forward_stdio) {
+ // nullptr means it's not redirected to file or pty (in case of LLGS local)
+ // at least one of stdio will be transferred pty<->gdb-remote we need to
+ // give the pty master handle to this object to read and/or write
+ LLDB_LOG(log,
+ "pid = {0}: setting up stdout/stderr redirection via $O "
+ "gdb-remote commands",
+ m_debugged_process_up->GetID());
+
+ // Setup stdout/stderr mapping from inferior to $O
+ auto terminal_fd = m_debugged_process_up->GetTerminalFileDescriptor();
+ if (terminal_fd >= 0) {
+ if (log)
+ log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s setting "
+ "inferior STDIO fd to %d",
+ __FUNCTION__, terminal_fd);
+ Status status = SetSTDIOFileDescriptor(terminal_fd);
+ if (status.Fail())
+ return status;
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring "
+ "inferior STDIO since terminal fd reported as %d",
+ __FUNCTION__, terminal_fd);
+ }
+ } else {
+ LLDB_LOG(log,
+ "pid = {0} skipping stdout/stderr redirection via $O: inferior "
+ "will communicate over client-provided file descriptors",
+ m_debugged_process_up->GetID());
+ }
+
+ printf("Launched '%s' as process %" PRIu64 "...\n",
+ m_process_launch_info.GetArguments().GetArgumentAtIndex(0),
+ m_debugged_process_up->GetID());
+
+ return Status();
+}
+
+Status GDBRemoteCommunicationServerLLGS::AttachToProcess(lldb::pid_t pid) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64,
+ __FUNCTION__, pid);
+
+ // Before we try to attach, make sure we aren't already monitoring something
+ // else.
+ if (m_debugged_process_up &&
+ m_debugged_process_up->GetID() != LLDB_INVALID_PROCESS_ID)
+ return Status("cannot attach to process %" PRIu64
+ " when another process with pid %" PRIu64
+ " is being debugged.",
+ pid, m_debugged_process_up->GetID());
+
+ // Try to attach.
+ auto process_or = m_process_factory.Attach(pid, *this, m_mainloop);
+ if (!process_or) {
+ Status status(process_or.takeError());
+ llvm::errs() << llvm::formatv("failed to attach to process {0}: {1}", pid,
+ status);
+ return status;
+ }
+ m_debugged_process_up = std::move(*process_or);
+
+ // Setup stdout/stderr mapping from inferior.
+ auto terminal_fd = m_debugged_process_up->GetTerminalFileDescriptor();
+ if (terminal_fd >= 0) {
+ if (log)
+ log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s setting "
+ "inferior STDIO fd to %d",
+ __FUNCTION__, terminal_fd);
+ Status status = SetSTDIOFileDescriptor(terminal_fd);
+ if (status.Fail())
+ return status;
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring "
+ "inferior STDIO since terminal fd reported as %d",
+ __FUNCTION__, terminal_fd);
+ }
+
+ printf("Attached to process %" PRIu64 "...\n", pid);
+ return Status();
+}
+
+void GDBRemoteCommunicationServerLLGS::InitializeDelegate(
+ NativeProcessProtocol *process) {
+ assert(process && "process cannot be NULL");
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log) {
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called with "
+ "NativeProcessProtocol pid %" PRIu64 ", current state: %s",
+ __FUNCTION__, process->GetID(),
+ StateAsCString(process->GetState()));
+ }
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendWResponse(
+ NativeProcessProtocol *process) {
+ assert(process && "process cannot be NULL");
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // send W notification
+ auto wait_status = process->GetExitStatus();
+ if (!wait_status) {
+ LLDB_LOG(log, "pid = {0}, failed to retrieve process exit status",
+ process->GetID());
+
+ StreamGDBRemote response;
+ response.PutChar('E');
+ response.PutHex8(GDBRemoteServerError::eErrorExitStatus);
+ return SendPacketNoLock(response.GetString());
+ }
+
+ LLDB_LOG(log, "pid = {0}, returning exit type {1}", process->GetID(),
+ *wait_status);
+
+ StreamGDBRemote response;
+ response.Format("{0:g}", *wait_status);
+ return SendPacketNoLock(response.GetString());
+}
+
+static void AppendHexValue(StreamString &response, const uint8_t *buf,
+ uint32_t buf_size, bool swap) {
+ int64_t i;
+ if (swap) {
+ for (i = buf_size - 1; i >= 0; i--)
+ response.PutHex8(buf[i]);
+ } else {
+ for (i = 0; i < buf_size; i++)
+ response.PutHex8(buf[i]);
+ }
+}
+
+static void WriteRegisterValueInHexFixedWidth(
+ StreamString &response, NativeRegisterContext &reg_ctx,
+ const RegisterInfo &reg_info, const RegisterValue *reg_value_p,
+ lldb::ByteOrder byte_order) {
+ RegisterValue reg_value;
+ if (!reg_value_p) {
+ Status error = reg_ctx.ReadRegister(&reg_info, reg_value);
+ if (error.Success())
+ reg_value_p = &reg_value;
+ // else log.
+ }
+
+ if (reg_value_p) {
+ AppendHexValue(response, (const uint8_t *)reg_value_p->GetBytes(),
+ reg_value_p->GetByteSize(),
+ byte_order == lldb::eByteOrderLittle);
+ } else {
+ // Zero-out any unreadable values.
+ if (reg_info.byte_size > 0) {
+ std::basic_string<uint8_t> zeros(reg_info.byte_size, '\0');
+ AppendHexValue(response, zeros.data(), zeros.size(), false);
+ }
+ }
+}
+
+static JSONObject::SP GetRegistersAsJSON(NativeThreadProtocol &thread) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ NativeRegisterContext& reg_ctx = thread.GetRegisterContext();
+
+ JSONObject::SP register_object_sp = std::make_shared<JSONObject>();
+
+#ifdef LLDB_JTHREADSINFO_FULL_REGISTER_SET
+ // Expedite all registers in the first register set (i.e. should be GPRs)
+ // that are not contained in other registers.
+ const RegisterSet *reg_set_p = reg_ctx_sp->GetRegisterSet(0);
+ if (!reg_set_p)
+ return nullptr;
+ for (const uint32_t *reg_num_p = reg_set_p->registers;
+ *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) {
+ uint32_t reg_num = *reg_num_p;
+#else
+ // Expedite only a couple of registers until we figure out why sending
+ // registers is expensive.
+ static const uint32_t k_expedited_registers[] = {
+ LLDB_REGNUM_GENERIC_PC, LLDB_REGNUM_GENERIC_SP, LLDB_REGNUM_GENERIC_FP,
+ LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM};
+
+ for (const uint32_t *generic_reg_p = k_expedited_registers;
+ *generic_reg_p != LLDB_INVALID_REGNUM; ++generic_reg_p) {
+ uint32_t reg_num = reg_ctx.ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, *generic_reg_p);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ continue; // Target does not support the given register.
+#endif
+
+ const RegisterInfo *const reg_info_p =
+ reg_ctx.GetRegisterInfoAtIndex(reg_num);
+ if (reg_info_p == nullptr) {
+ if (log)
+ log->Printf(
+ "%s failed to get register info for register index %" PRIu32,
+ __FUNCTION__, reg_num);
+ continue;
+ }
+
+ if (reg_info_p->value_regs != nullptr)
+ continue; // Only expedite registers that are not contained in other
+ // registers.
+
+ RegisterValue reg_value;
+ Status error = reg_ctx.ReadRegister(reg_info_p, reg_value);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s",
+ __FUNCTION__,
+ reg_info_p->name ? reg_info_p->name : "<unnamed-register>",
+ reg_num, error.AsCString());
+ continue;
+ }
+
+ StreamString stream;
+ WriteRegisterValueInHexFixedWidth(stream, reg_ctx, *reg_info_p,
+ &reg_value, lldb::eByteOrderBig);
+
+ register_object_sp->SetObject(
+ llvm::to_string(reg_num),
+ std::make_shared<JSONString>(stream.GetString()));
+ }
+
+ return register_object_sp;
+}
+
+static const char *GetStopReasonString(StopReason stop_reason) {
+ switch (stop_reason) {
+ case eStopReasonTrace:
+ return "trace";
+ case eStopReasonBreakpoint:
+ return "breakpoint";
+ case eStopReasonWatchpoint:
+ return "watchpoint";
+ case eStopReasonSignal:
+ return "signal";
+ case eStopReasonException:
+ return "exception";
+ case eStopReasonExec:
+ return "exec";
+ case eStopReasonInstrumentation:
+ case eStopReasonInvalid:
+ case eStopReasonPlanComplete:
+ case eStopReasonThreadExiting:
+ case eStopReasonNone:
+ break; // ignored
+ }
+ return nullptr;
+}
+
+static JSONArray::SP GetJSONThreadsInfo(NativeProcessProtocol &process,
+ bool abridged) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ JSONArray::SP threads_array_sp = std::make_shared<JSONArray>();
+
+ // Ensure we can get info on the given thread.
+ uint32_t thread_idx = 0;
+ for (NativeThreadProtocol *thread;
+ (thread = process.GetThreadAtIndex(thread_idx)) != nullptr;
+ ++thread_idx) {
+
+ lldb::tid_t tid = thread->GetID();
+
+ // Grab the reason this thread stopped.
+ struct ThreadStopInfo tid_stop_info;
+ std::string description;
+ if (!thread->GetStopReason(tid_stop_info, description))
+ return nullptr;
+
+ const int signum = tid_stop_info.details.signal.signo;
+ if (log) {
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64
+ " tid %" PRIu64
+ " got signal signo = %d, reason = %d, exc_type = %" PRIu64,
+ __FUNCTION__, process.GetID(), tid, signum,
+ tid_stop_info.reason, tid_stop_info.details.exception.type);
+ }
+
+ JSONObject::SP thread_obj_sp = std::make_shared<JSONObject>();
+ threads_array_sp->AppendObject(thread_obj_sp);
+
+ if (!abridged) {
+ if (JSONObject::SP registers_sp = GetRegistersAsJSON(*thread))
+ thread_obj_sp->SetObject("registers", registers_sp);
+ }
+
+ thread_obj_sp->SetObject("tid", std::make_shared<JSONNumber>(tid));
+ if (signum != 0)
+ thread_obj_sp->SetObject("signal", std::make_shared<JSONNumber>(signum));
+
+ const std::string thread_name = thread->GetName();
+ if (!thread_name.empty())
+ thread_obj_sp->SetObject("name",
+ std::make_shared<JSONString>(thread_name));
+
+ if (const char *stop_reason_str = GetStopReasonString(tid_stop_info.reason))
+ thread_obj_sp->SetObject("reason",
+ std::make_shared<JSONString>(stop_reason_str));
+
+ if (!description.empty())
+ thread_obj_sp->SetObject("description",
+ std::make_shared<JSONString>(description));
+
+ if ((tid_stop_info.reason == eStopReasonException) &&
+ tid_stop_info.details.exception.type) {
+ thread_obj_sp->SetObject(
+ "metype",
+ std::make_shared<JSONNumber>(tid_stop_info.details.exception.type));
+
+ JSONArray::SP medata_array_sp = std::make_shared<JSONArray>();
+ for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count;
+ ++i) {
+ medata_array_sp->AppendObject(std::make_shared<JSONNumber>(
+ tid_stop_info.details.exception.data[i]));
+ }
+ thread_obj_sp->SetObject("medata", medata_array_sp);
+ }
+
+ // TODO: Expedite interesting regions of inferior memory
+ }
+
+ return threads_array_sp;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread(
+ lldb::tid_t tid) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ // Ensure we have a debugged process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(50);
+
+ LLDB_LOG(log, "preparing packet for pid {0} tid {1}",
+ m_debugged_process_up->GetID(), tid);
+
+ // Ensure we can get info on the given thread.
+ NativeThreadProtocol *thread = m_debugged_process_up->GetThreadByID(tid);
+ if (!thread)
+ return SendErrorResponse(51);
+
+ // Grab the reason this thread stopped.
+ struct ThreadStopInfo tid_stop_info;
+ std::string description;
+ if (!thread->GetStopReason(tid_stop_info, description))
+ return SendErrorResponse(52);
+
+ // FIXME implement register handling for exec'd inferiors.
+ // if (tid_stop_info.reason == eStopReasonExec) {
+ // const bool force = true;
+ // InitializeRegisters(force);
+ // }
+
+ StreamString response;
+ // Output the T packet with the thread
+ response.PutChar('T');
+ int signum = tid_stop_info.details.signal.signo;
+ LLDB_LOG(
+ log,
+ "pid {0}, tid {1}, got signal signo = {2}, reason = {3}, exc_type = {4}",
+ m_debugged_process_up->GetID(), tid, signum, int(tid_stop_info.reason),
+ tid_stop_info.details.exception.type);
+
+ // Print the signal number.
+ response.PutHex8(signum & 0xff);
+
+ // Include the tid.
+ response.Printf("thread:%" PRIx64 ";", tid);
+
+ // Include the thread name if there is one.
+ const std::string thread_name = thread->GetName();
+ if (!thread_name.empty()) {
+ size_t thread_name_len = thread_name.length();
+
+ if (::strcspn(thread_name.c_str(), "$#+-;:") == thread_name_len) {
+ response.PutCString("name:");
+ response.PutCString(thread_name);
+ } else {
+ // The thread name contains special chars, send as hex bytes.
+ response.PutCString("hexname:");
+ response.PutStringAsRawHex8(thread_name);
+ }
+ response.PutChar(';');
+ }
+
+ // If a 'QListThreadsInStopReply' was sent to enable this feature, we will
+ // send all thread IDs back in the "threads" key whose value is a list of hex
+ // thread IDs separated by commas:
+ // "threads:10a,10b,10c;"
+ // This will save the debugger from having to send a pair of qfThreadInfo and
+ // qsThreadInfo packets, but it also might take a lot of room in the stop
+ // reply packet, so it must be enabled only on systems where there are no
+ // limits on packet lengths.
+ if (m_list_threads_in_stop_reply) {
+ response.PutCString("threads:");
+
+ uint32_t thread_index = 0;
+ NativeThreadProtocol *listed_thread;
+ for (listed_thread = m_debugged_process_up->GetThreadAtIndex(thread_index);
+ listed_thread; ++thread_index,
+ listed_thread = m_debugged_process_up->GetThreadAtIndex(thread_index)) {
+ if (thread_index > 0)
+ response.PutChar(',');
+ response.Printf("%" PRIx64, listed_thread->GetID());
+ }
+ response.PutChar(';');
+
+ // Include JSON info that describes the stop reason for any threads that
+ // actually have stop reasons. We use the new "jstopinfo" key whose values
+ // is hex ascii JSON that contains the thread IDs thread stop info only for
+ // threads that have stop reasons. Only send this if we have more than one
+ // thread otherwise this packet has all the info it needs.
+ if (thread_index > 0) {
+ const bool threads_with_valid_stop_info_only = true;
+ JSONArray::SP threads_info_sp = GetJSONThreadsInfo(
+ *m_debugged_process_up, threads_with_valid_stop_info_only);
+ if (threads_info_sp) {
+ response.PutCString("jstopinfo:");
+ StreamString unescaped_response;
+ threads_info_sp->Write(unescaped_response);
+ response.PutStringAsRawHex8(unescaped_response.GetData());
+ response.PutChar(';');
+ } else
+ LLDB_LOG(log, "failed to prepare a jstopinfo field for pid {0}",
+ m_debugged_process_up->GetID());
+ }
+
+ uint32_t i = 0;
+ response.PutCString("thread-pcs");
+ char delimiter = ':';
+ for (NativeThreadProtocol *thread;
+ (thread = m_debugged_process_up->GetThreadAtIndex(i)) != nullptr;
+ ++i) {
+ NativeRegisterContext& reg_ctx = thread->GetRegisterContext();
+
+ uint32_t reg_to_read = reg_ctx.ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *const reg_info_p =
+ reg_ctx.GetRegisterInfoAtIndex(reg_to_read);
+
+ RegisterValue reg_value;
+ Status error = reg_ctx.ReadRegister(reg_info_p, reg_value);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s",
+ __FUNCTION__,
+ reg_info_p->name ? reg_info_p->name
+ : "<unnamed-register>",
+ reg_to_read, error.AsCString());
+ continue;
+ }
+
+ response.PutChar(delimiter);
+ delimiter = ',';
+ WriteRegisterValueInHexFixedWidth(response, reg_ctx, *reg_info_p,
+ &reg_value, endian::InlHostByteOrder());
+ }
+
+ response.PutChar(';');
+ }
+
+ //
+ // Expedite registers.
+ //
+
+ // Grab the register context.
+ NativeRegisterContext& reg_ctx = thread->GetRegisterContext();
+ // Expedite all registers in the first register set (i.e. should be GPRs)
+ // that are not contained in other registers.
+ const RegisterSet *reg_set_p;
+ if (reg_ctx.GetRegisterSetCount() > 0 &&
+ ((reg_set_p = reg_ctx.GetRegisterSet(0)) != nullptr)) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s expediting registers "
+ "from set '%s' (registers set count: %zu)",
+ __FUNCTION__,
+ reg_set_p->name ? reg_set_p->name : "<unnamed-set>",
+ reg_set_p->num_registers);
+
+ for (const uint32_t *reg_num_p = reg_set_p->registers;
+ *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) {
+ const RegisterInfo *const reg_info_p =
+ reg_ctx.GetRegisterInfoAtIndex(*reg_num_p);
+ if (reg_info_p == nullptr) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to get "
+ "register info for register set '%s', register index "
+ "%" PRIu32,
+ __FUNCTION__,
+ reg_set_p->name ? reg_set_p->name : "<unnamed-set>",
+ *reg_num_p);
+ } else if (reg_info_p->value_regs == nullptr) {
+ // Only expediate registers that are not contained in other registers.
+ RegisterValue reg_value;
+ Status error = reg_ctx.ReadRegister(reg_info_p, reg_value);
+ if (error.Success()) {
+ response.Printf("%.02x:", *reg_num_p);
+ WriteRegisterValueInHexFixedWidth(response, reg_ctx, *reg_info_p,
+ &reg_value, lldb::eByteOrderBig);
+ response.PutChar(';');
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to read "
+ "register '%s' index %" PRIu32 ": %s",
+ __FUNCTION__,
+ reg_info_p->name ? reg_info_p->name
+ : "<unnamed-register>",
+ *reg_num_p, error.AsCString());
+ }
+ }
+ }
+ }
+
+ const char *reason_str = GetStopReasonString(tid_stop_info.reason);
+ if (reason_str != nullptr) {
+ response.Printf("reason:%s;", reason_str);
+ }
+
+ if (!description.empty()) {
+ // Description may contains special chars, send as hex bytes.
+ response.PutCString("description:");
+ response.PutStringAsRawHex8(description);
+ response.PutChar(';');
+ } else if ((tid_stop_info.reason == eStopReasonException) &&
+ tid_stop_info.details.exception.type) {
+ response.PutCString("metype:");
+ response.PutHex64(tid_stop_info.details.exception.type);
+ response.PutCString(";mecount:");
+ response.PutHex32(tid_stop_info.details.exception.data_count);
+ response.PutChar(';');
+
+ for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) {
+ response.PutCString("medata:");
+ response.PutHex64(tid_stop_info.details.exception.data[i]);
+ response.PutChar(';');
+ }
+ }
+
+ return SendPacketNoLock(response.GetString());
+}
+
+void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited(
+ NativeProcessProtocol *process) {
+ assert(process && "process cannot be NULL");
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
+
+ PacketResult result = SendStopReasonForState(StateType::eStateExited);
+ if (result != PacketResult::Success) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send stop "
+ "notification for PID %" PRIu64 ", state: eStateExited",
+ __FUNCTION__, process->GetID());
+ }
+
+ // Close the pipe to the inferior terminal i/o if we launched it and set one
+ // up.
+ MaybeCloseInferiorTerminalConnection();
+
+ // We are ready to exit the debug monitor.
+ m_exit_now = true;
+ m_mainloop.RequestTermination();
+}
+
+void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped(
+ NativeProcessProtocol *process) {
+ assert(process && "process cannot be NULL");
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
+
+ // Send the stop reason unless this is the stop after the launch or attach.
+ switch (m_inferior_prev_state) {
+ case eStateLaunching:
+ case eStateAttaching:
+ // Don't send anything per debugserver behavior.
+ break;
+ default:
+ // In all other cases, send the stop reason.
+ PacketResult result = SendStopReasonForState(StateType::eStateStopped);
+ if (result != PacketResult::Success) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send stop "
+ "notification for PID %" PRIu64 ", state: eStateExited",
+ __FUNCTION__, process->GetID());
+ }
+ break;
+ }
+}
+
+void GDBRemoteCommunicationServerLLGS::ProcessStateChanged(
+ NativeProcessProtocol *process, lldb::StateType state) {
+ assert(process && "process cannot be NULL");
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log) {
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called with "
+ "NativeProcessProtocol pid %" PRIu64 ", state: %s",
+ __FUNCTION__, process->GetID(), StateAsCString(state));
+ }
+
+ switch (state) {
+ case StateType::eStateRunning:
+ StartSTDIOForwarding();
+ break;
+
+ case StateType::eStateStopped:
+ // Make sure we get all of the pending stdout/stderr from the inferior and
+ // send it to the lldb host before we send the state change notification
+ SendProcessOutput();
+ // Then stop the forwarding, so that any late output (see llvm.org/pr25652)
+ // does not interfere with our protocol.
+ StopSTDIOForwarding();
+ HandleInferiorState_Stopped(process);
+ break;
+
+ case StateType::eStateExited:
+ // Same as above
+ SendProcessOutput();
+ StopSTDIOForwarding();
+ HandleInferiorState_Exited(process);
+ break;
+
+ default:
+ if (log) {
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s didn't handle state "
+ "change for pid %" PRIu64 ", new state: %s",
+ __FUNCTION__, process->GetID(), StateAsCString(state));
+ }
+ break;
+ }
+
+ // Remember the previous state reported to us.
+ m_inferior_prev_state = state;
+}
+
+void GDBRemoteCommunicationServerLLGS::DidExec(NativeProcessProtocol *process) {
+ ClearProcessSpecificData();
+}
+
+void GDBRemoteCommunicationServerLLGS::DataAvailableCallback() {
+ Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_COMM));
+
+ if (!m_handshake_completed) {
+ if (!HandshakeWithClient()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s handshake with "
+ "client failed, exiting",
+ __FUNCTION__);
+ m_mainloop.RequestTermination();
+ return;
+ }
+ m_handshake_completed = true;
+ }
+
+ bool interrupt = false;
+ bool done = false;
+ Status error;
+ while (true) {
+ const PacketResult result = GetPacketAndSendResponse(
+ std::chrono::microseconds(0), error, interrupt, done);
+ if (result == PacketResult::ErrorReplyTimeout)
+ break; // No more packets in the queue
+
+ if ((result != PacketResult::Success)) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s processing a packet "
+ "failed: %s",
+ __FUNCTION__, error.AsCString());
+ m_mainloop.RequestTermination();
+ break;
+ }
+ }
+}
+
+Status GDBRemoteCommunicationServerLLGS::InitializeConnection(
+ std::unique_ptr<Connection> &&connection) {
+ IOObjectSP read_object_sp = connection->GetReadObject();
+ GDBRemoteCommunicationServer::SetConnection(connection.release());
+
+ Status error;
+ m_network_handle_up = m_mainloop.RegisterReadObject(
+ read_object_sp, [this](MainLoopBase &) { DataAvailableCallback(); },
+ error);
+ return error;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendONotification(const char *buffer,
+ uint32_t len) {
+ if ((buffer == nullptr) || (len == 0)) {
+ // Nothing to send.
+ return PacketResult::Success;
+ }
+
+ StreamString response;
+ response.PutChar('O');
+ response.PutBytesAsRawHex8(buffer, len);
+
+ return SendPacketNoLock(response.GetString());
+}
+
+Status GDBRemoteCommunicationServerLLGS::SetSTDIOFileDescriptor(int fd) {
+ Status error;
+
+ // Set up the reading/handling of process I/O
+ std::unique_ptr<ConnectionFileDescriptor> conn_up(
+ new ConnectionFileDescriptor(fd, true));
+ if (!conn_up) {
+ error.SetErrorString("failed to create ConnectionFileDescriptor");
+ return error;
+ }
+
+ m_stdio_communication.SetCloseOnEOF(false);
+ m_stdio_communication.SetConnection(conn_up.release());
+ if (!m_stdio_communication.IsConnected()) {
+ error.SetErrorString(
+ "failed to set connection for inferior I/O communication");
+ return error;
+ }
+
+ return Status();
+}
+
+void GDBRemoteCommunicationServerLLGS::StartSTDIOForwarding() {
+ // Don't forward if not connected (e.g. when attaching).
+ if (!m_stdio_communication.IsConnected())
+ return;
+
+ Status error;
+ lldbassert(!m_stdio_handle_up);
+ m_stdio_handle_up = m_mainloop.RegisterReadObject(
+ m_stdio_communication.GetConnection()->GetReadObject(),
+ [this](MainLoopBase &) { SendProcessOutput(); }, error);
+
+ if (!m_stdio_handle_up) {
+ // Not much we can do about the failure. Log it and continue without
+ // forwarding.
+ if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS))
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s Failed to set up stdio "
+ "forwarding: %s",
+ __FUNCTION__, error.AsCString());
+ }
+}
+
+void GDBRemoteCommunicationServerLLGS::StopSTDIOForwarding() {
+ m_stdio_handle_up.reset();
+}
+
+void GDBRemoteCommunicationServerLLGS::SendProcessOutput() {
+ char buffer[1024];
+ ConnectionStatus status;
+ Status error;
+ while (true) {
+ size_t bytes_read = m_stdio_communication.Read(
+ buffer, sizeof buffer, std::chrono::microseconds(0), status, &error);
+ switch (status) {
+ case eConnectionStatusSuccess:
+ SendONotification(buffer, bytes_read);
+ break;
+ case eConnectionStatusLostConnection:
+ case eConnectionStatusEndOfFile:
+ case eConnectionStatusError:
+ case eConnectionStatusNoConnection:
+ if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS))
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s Stopping stdio "
+ "forwarding as communication returned status %d (error: "
+ "%s)",
+ __FUNCTION__, status, error.AsCString());
+ m_stdio_handle_up.reset();
+ return;
+
+ case eConnectionStatusInterrupted:
+ case eConnectionStatusTimedOut:
+ return;
+ }
+ }
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jTraceStart(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ if (!packet.ConsumeFront("jTraceStart:"))
+ return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet ");
+
+ TraceOptions options;
+ uint64_t type = std::numeric_limits<uint64_t>::max();
+ uint64_t buffersize = std::numeric_limits<uint64_t>::max();
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ uint64_t metabuffersize = std::numeric_limits<uint64_t>::max();
+
+ auto json_object = StructuredData::ParseJSON(packet.Peek());
+
+ if (!json_object ||
+ json_object->GetType() != lldb::eStructuredDataTypeDictionary)
+ return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet ");
+
+ auto json_dict = json_object->GetAsDictionary();
+
+ json_dict->GetValueForKeyAsInteger("metabuffersize", metabuffersize);
+ options.setMetaDataBufferSize(metabuffersize);
+
+ json_dict->GetValueForKeyAsInteger("buffersize", buffersize);
+ options.setTraceBufferSize(buffersize);
+
+ json_dict->GetValueForKeyAsInteger("type", type);
+ options.setType(static_cast<lldb::TraceType>(type));
+
+ json_dict->GetValueForKeyAsInteger("threadid", tid);
+ options.setThreadID(tid);
+
+ StructuredData::ObjectSP custom_params_sp =
+ json_dict->GetValueForKey("params");
+ if (custom_params_sp &&
+ custom_params_sp->GetType() != lldb::eStructuredDataTypeDictionary)
+ return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet ");
+
+ options.setTraceParams(
+ static_pointer_cast<StructuredData::Dictionary>(custom_params_sp));
+
+ if (buffersize == std::numeric_limits<uint64_t>::max() ||
+ type != lldb::TraceType::eTraceTypeProcessorTrace) {
+ LLDB_LOG(log, "Ill formed packet buffersize = {0} type = {1}", buffersize,
+ type);
+ return SendIllFormedResponse(packet, "JTrace:start: Ill formed packet ");
+ }
+
+ Status error;
+ lldb::user_id_t uid = LLDB_INVALID_UID;
+ uid = m_debugged_process_up->StartTrace(options, error);
+ LLDB_LOG(log, "uid is {0} , error is {1}", uid, error.GetError());
+ if (error.Fail())
+ return SendErrorResponse(error);
+
+ StreamGDBRemote response;
+ response.Printf("%" PRIx64, uid);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jTraceStop(
+ StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ if (!packet.ConsumeFront("jTraceStop:"))
+ return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet ");
+
+ lldb::user_id_t uid = LLDB_INVALID_UID;
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+
+ auto json_object = StructuredData::ParseJSON(packet.Peek());
+
+ if (!json_object ||
+ json_object->GetType() != lldb::eStructuredDataTypeDictionary)
+ return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet ");
+
+ auto json_dict = json_object->GetAsDictionary();
+
+ if (!json_dict->GetValueForKeyAsInteger("traceid", uid))
+ return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet ");
+
+ json_dict->GetValueForKeyAsInteger("threadid", tid);
+
+ Status error = m_debugged_process_up->StopTrace(uid, tid);
+
+ if (error.Fail())
+ return SendErrorResponse(error);
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead(
+ StringExtractorGDBRemote &packet) {
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ if (!packet.ConsumeFront("jTraceConfigRead:"))
+ return SendIllFormedResponse(packet,
+ "jTraceConfigRead: Ill formed packet ");
+
+ lldb::user_id_t uid = LLDB_INVALID_UID;
+ lldb::tid_t threadid = LLDB_INVALID_THREAD_ID;
+
+ auto json_object = StructuredData::ParseJSON(packet.Peek());
+
+ if (!json_object ||
+ json_object->GetType() != lldb::eStructuredDataTypeDictionary)
+ return SendIllFormedResponse(packet,
+ "jTraceConfigRead: Ill formed packet ");
+
+ auto json_dict = json_object->GetAsDictionary();
+
+ if (!json_dict->GetValueForKeyAsInteger("traceid", uid))
+ return SendIllFormedResponse(packet,
+ "jTraceConfigRead: Ill formed packet ");
+
+ json_dict->GetValueForKeyAsInteger("threadid", threadid);
+
+ TraceOptions options;
+ StreamGDBRemote response;
+
+ options.setThreadID(threadid);
+ Status error = m_debugged_process_up->GetTraceConfig(uid, options);
+
+ if (error.Fail())
+ return SendErrorResponse(error);
+
+ StreamGDBRemote escaped_response;
+ StructuredData::Dictionary json_packet;
+
+ json_packet.AddIntegerItem("type", options.getType());
+ json_packet.AddIntegerItem("buffersize", options.getTraceBufferSize());
+ json_packet.AddIntegerItem("metabuffersize", options.getMetaDataBufferSize());
+
+ StructuredData::DictionarySP custom_params = options.getTraceParams();
+ if (custom_params)
+ json_packet.AddItem("params", custom_params);
+
+ StreamString json_string;
+ json_packet.Dump(json_string, false);
+ escaped_response.PutEscapedBytes(json_string.GetData(),
+ json_string.GetSize());
+ return SendPacketNoLock(escaped_response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jTraceRead(
+ StringExtractorGDBRemote &packet) {
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ enum PacketType { MetaData, BufferData };
+ PacketType tracetype = MetaData;
+
+ if (packet.ConsumeFront("jTraceBufferRead:"))
+ tracetype = BufferData;
+ else if (packet.ConsumeFront("jTraceMetaRead:"))
+ tracetype = MetaData;
+ else {
+ return SendIllFormedResponse(packet, "jTrace: Ill formed packet ");
+ }
+
+ lldb::user_id_t uid = LLDB_INVALID_UID;
+
+ uint64_t byte_count = std::numeric_limits<uint64_t>::max();
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ uint64_t offset = std::numeric_limits<uint64_t>::max();
+
+ auto json_object = StructuredData::ParseJSON(packet.Peek());
+
+ if (!json_object ||
+ json_object->GetType() != lldb::eStructuredDataTypeDictionary)
+ return SendIllFormedResponse(packet, "jTrace: Ill formed packet ");
+
+ auto json_dict = json_object->GetAsDictionary();
+
+ if (!json_dict->GetValueForKeyAsInteger("traceid", uid) ||
+ !json_dict->GetValueForKeyAsInteger("offset", offset) ||
+ !json_dict->GetValueForKeyAsInteger("buffersize", byte_count))
+ return SendIllFormedResponse(packet, "jTrace: Ill formed packet ");
+
+ json_dict->GetValueForKeyAsInteger("threadid", tid);
+
+ // Allocate the response buffer.
+ std::unique_ptr<uint8_t[]> buffer (new (std::nothrow) uint8_t[byte_count]);
+ if (!buffer)
+ return SendErrorResponse(0x78);
+
+ StreamGDBRemote response;
+ Status error;
+ llvm::MutableArrayRef<uint8_t> buf(buffer.get(), byte_count);
+
+ if (tracetype == BufferData)
+ error = m_debugged_process_up->GetData(uid, tid, buf, offset);
+ else if (tracetype == MetaData)
+ error = m_debugged_process_up->GetMetaData(uid, tid, buf, offset);
+
+ if (error.Fail())
+ return SendErrorResponse(error);
+
+ for (auto i : buf)
+ response.PutHex8(i);
+
+ StreamGDBRemote escaped_response;
+ escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
+ return SendPacketNoLock(escaped_response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo(
+ StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ lldb::pid_t pid = m_debugged_process_up->GetID();
+
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse(1);
+
+ ProcessInstanceInfo proc_info;
+ if (!Host::GetProcessInfo(pid, proc_info))
+ return SendErrorResponse(1);
+
+ StreamString response;
+ CreateProcessInfoResponse_DebugServerStyle(proc_info, response);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qC(StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ // Make sure we set the current thread so g and p packets return the data the
+ // gdb will expect.
+ lldb::tid_t tid = m_debugged_process_up->GetCurrentThreadID();
+ SetCurrentThreadID(tid);
+
+ NativeThreadProtocol *thread = m_debugged_process_up->GetCurrentThread();
+ if (!thread)
+ return SendErrorResponse(69);
+
+ StreamString response;
+ response.Printf("QC%" PRIx64, thread->GetID());
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_k(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ StopSTDIOForwarding();
+
+ if (!m_debugged_process_up) {
+ LLDB_LOG(log, "No debugged process found.");
+ return PacketResult::Success;
+ }
+
+ Status error = m_debugged_process_up->Kill();
+ if (error.Fail())
+ LLDB_LOG(log, "Failed to kill debugged process {0}: {1}",
+ m_debugged_process_up->GetID(), error);
+
+ // No OK response for kill packet.
+ // return SendOKResponse ();
+ return PacketResult::Success;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetDisableASLR:"));
+ if (packet.GetU32(0))
+ m_process_launch_info.GetFlags().Set(eLaunchFlagDisableASLR);
+ else
+ m_process_launch_info.GetFlags().Clear(eLaunchFlagDisableASLR);
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetWorkingDir:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ m_process_launch_info.SetWorkingDirectory(FileSpec(path));
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir(
+ StringExtractorGDBRemote &packet) {
+ FileSpec working_dir{m_process_launch_info.GetWorkingDirectory()};
+ if (working_dir) {
+ StreamString response;
+ response.PutStringAsRawHex8(working_dir.GetCString());
+ return SendPacketNoLock(response.GetString());
+ }
+
+ return SendErrorResponse(14);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_C(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
+
+ // Ensure we have a native process.
+ if (!m_debugged_process_up) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process "
+ "shared pointer",
+ __FUNCTION__);
+ return SendErrorResponse(0x36);
+ }
+
+ // Pull out the signal number.
+ packet.SetFilePos(::strlen("C"));
+ if (packet.GetBytesLeft() < 1) {
+ // Shouldn't be using a C without a signal.
+ return SendIllFormedResponse(packet, "C packet specified without signal.");
+ }
+ const uint32_t signo =
+ packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max());
+ if (signo == std::numeric_limits<uint32_t>::max())
+ return SendIllFormedResponse(packet, "failed to parse signal number");
+
+ // Handle optional continue address.
+ if (packet.GetBytesLeft() > 0) {
+ // FIXME add continue at address support for $C{signo}[;{continue-address}].
+ if (*packet.Peek() == ';')
+ return SendUnimplementedResponse(packet.GetStringRef().c_str());
+ else
+ return SendIllFormedResponse(
+ packet, "unexpected content after $C{signal-number}");
+ }
+
+ ResumeActionList resume_actions(StateType::eStateRunning, 0);
+ Status error;
+
+ // We have two branches: what to do if a continue thread is specified (in
+ // which case we target sending the signal to that thread), or when we don't
+ // have a continue thread set (in which case we send a signal to the
+ // process).
+
+ // TODO discuss with Greg Clayton, make sure this makes sense.
+
+ lldb::tid_t signal_tid = GetContinueThreadID();
+ if (signal_tid != LLDB_INVALID_THREAD_ID) {
+ // The resume action for the continue thread (or all threads if a continue
+ // thread is not set).
+ ResumeAction action = {GetContinueThreadID(), StateType::eStateRunning,
+ static_cast<int>(signo)};
+
+ // Add the action for the continue thread (or all threads when the continue
+ // thread isn't present).
+ resume_actions.Append(action);
+ } else {
+ // Send the signal to the process since we weren't targeting a specific
+ // continue thread with the signal.
+ error = m_debugged_process_up->Signal(signo);
+ if (error.Fail()) {
+ LLDB_LOG(log, "failed to send signal for process {0}: {1}",
+ m_debugged_process_up->GetID(), error);
+
+ return SendErrorResponse(0x52);
+ }
+ }
+
+ // Resume the threads.
+ error = m_debugged_process_up->Resume(resume_actions);
+ if (error.Fail()) {
+ LLDB_LOG(log, "failed to resume threads for process {0}: {1}",
+ m_debugged_process_up->GetID(), error);
+
+ return SendErrorResponse(0x38);
+ }
+
+ // Don't send an "OK" packet; response is the stopped/exited message.
+ return PacketResult::Success;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_c(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
+
+ packet.SetFilePos(packet.GetFilePos() + ::strlen("c"));
+
+ // For now just support all continue.
+ const bool has_continue_address = (packet.GetBytesLeft() > 0);
+ if (has_continue_address) {
+ LLDB_LOG(log, "not implemented for c[address] variant [{0} remains]",
+ packet.Peek());
+ return SendUnimplementedResponse(packet.GetStringRef().c_str());
+ }
+
+ // Ensure we have a native process.
+ if (!m_debugged_process_up) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process "
+ "shared pointer",
+ __FUNCTION__);
+ return SendErrorResponse(0x36);
+ }
+
+ // Build the ResumeActionList
+ ResumeActionList actions(StateType::eStateRunning, 0);
+
+ Status error = m_debugged_process_up->Resume(actions);
+ if (error.Fail()) {
+ LLDB_LOG(log, "c failed for process {0}: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(GDBRemoteServerError::eErrorResume);
+ }
+
+ LLDB_LOG(log, "continued process {0}", m_debugged_process_up->GetID());
+ // No response required from continue.
+ return PacketResult::Success;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_vCont_actions(
+ StringExtractorGDBRemote &packet) {
+ StreamString response;
+ response.Printf("vCont;c;C;s;S");
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_vCont(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s handling vCont packet",
+ __FUNCTION__);
+
+ packet.SetFilePos(::strlen("vCont"));
+
+ if (packet.GetBytesLeft() == 0) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s missing action from "
+ "vCont package",
+ __FUNCTION__);
+ return SendIllFormedResponse(packet, "Missing action from vCont package");
+ }
+
+ // Check if this is all continue (no options or ";c").
+ if (::strcmp(packet.Peek(), ";c") == 0) {
+ // Move past the ';', then do a simple 'c'.
+ packet.SetFilePos(packet.GetFilePos() + 1);
+ return Handle_c(packet);
+ } else if (::strcmp(packet.Peek(), ";s") == 0) {
+ // Move past the ';', then do a simple 's'.
+ packet.SetFilePos(packet.GetFilePos() + 1);
+ return Handle_s(packet);
+ }
+
+ // Ensure we have a native process.
+ if (!m_debugged_process_up) {
+ LLDB_LOG(log, "no debugged process");
+ return SendErrorResponse(0x36);
+ }
+
+ ResumeActionList thread_actions;
+
+ while (packet.GetBytesLeft() && *packet.Peek() == ';') {
+ // Skip the semi-colon.
+ packet.GetChar();
+
+ // Build up the thread action.
+ ResumeAction thread_action;
+ thread_action.tid = LLDB_INVALID_THREAD_ID;
+ thread_action.state = eStateInvalid;
+ thread_action.signal = 0;
+
+ const char action = packet.GetChar();
+ switch (action) {
+ case 'C':
+ thread_action.signal = packet.GetHexMaxU32(false, 0);
+ if (thread_action.signal == 0)
+ return SendIllFormedResponse(
+ packet, "Could not parse signal in vCont packet C action");
+ LLVM_FALLTHROUGH;
+
+ case 'c':
+ // Continue
+ thread_action.state = eStateRunning;
+ break;
+
+ case 'S':
+ thread_action.signal = packet.GetHexMaxU32(false, 0);
+ if (thread_action.signal == 0)
+ return SendIllFormedResponse(
+ packet, "Could not parse signal in vCont packet S action");
+ LLVM_FALLTHROUGH;
+
+ case 's':
+ // Step
+ thread_action.state = eStateStepping;
+ break;
+
+ default:
+ return SendIllFormedResponse(packet, "Unsupported vCont action");
+ break;
+ }
+
+ // Parse out optional :{thread-id} value.
+ if (packet.GetBytesLeft() && (*packet.Peek() == ':')) {
+ // Consume the separator.
+ packet.GetChar();
+
+ thread_action.tid = packet.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID);
+ if (thread_action.tid == LLDB_INVALID_THREAD_ID)
+ return SendIllFormedResponse(
+ packet, "Could not parse thread number in vCont packet");
+ }
+
+ thread_actions.Append(thread_action);
+ }
+
+ Status error = m_debugged_process_up->Resume(thread_actions);
+ if (error.Fail()) {
+ LLDB_LOG(log, "vCont failed for process {0}: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(GDBRemoteServerError::eErrorResume);
+ }
+
+ LLDB_LOG(log, "continued process {0}", m_debugged_process_up->GetID());
+ // No response required from vCont.
+ return PacketResult::Success;
+}
+
+void GDBRemoteCommunicationServerLLGS::SetCurrentThreadID(lldb::tid_t tid) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+ LLDB_LOG(log, "setting current thread id to {0}", tid);
+
+ m_current_tid = tid;
+ if (m_debugged_process_up)
+ m_debugged_process_up->SetCurrentThreadID(m_current_tid);
+}
+
+void GDBRemoteCommunicationServerLLGS::SetContinueThreadID(lldb::tid_t tid) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+ LLDB_LOG(log, "setting continue thread id to {0}", tid);
+
+ m_continue_tid = tid;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_stop_reason(
+ StringExtractorGDBRemote &packet) {
+ // Handle the $? gdbremote command.
+
+ // If no process, indicate error
+ if (!m_debugged_process_up)
+ return SendErrorResponse(02);
+
+ return SendStopReasonForState(m_debugged_process_up->GetState());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendStopReasonForState(
+ lldb::StateType process_state) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ switch (process_state) {
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateDetached:
+ // NOTE: gdb protocol doc looks like it should return $OK
+ // when everything is running (i.e. no stopped result).
+ return PacketResult::Success; // Ignore
+
+ case eStateSuspended:
+ case eStateStopped:
+ case eStateCrashed: {
+ lldb::tid_t tid = m_debugged_process_up->GetCurrentThreadID();
+ // Make sure we set the current thread so g and p packets return the data
+ // the gdb will expect.
+ SetCurrentThreadID(tid);
+ return SendStopReplyPacketForThread(tid);
+ }
+
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateExited:
+ return SendWResponse(m_debugged_process_up.get());
+
+ default:
+ LLDB_LOG(log, "pid {0}, current state reporting not handled: {1}",
+ m_debugged_process_up->GetID(), process_state);
+ break;
+ }
+
+ return SendErrorResponse(0);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo(
+ StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ // Ensure we have a thread.
+ NativeThreadProtocol *thread = m_debugged_process_up->GetThreadAtIndex(0);
+ if (!thread)
+ return SendErrorResponse(69);
+
+ // Get the register context for the first thread.
+ NativeRegisterContext &reg_context = thread->GetRegisterContext();
+
+ // Parse out the register number from the request.
+ packet.SetFilePos(strlen("qRegisterInfo"));
+ const uint32_t reg_index =
+ packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max());
+ if (reg_index == std::numeric_limits<uint32_t>::max())
+ return SendErrorResponse(69);
+
+ // Return the end of registers response if we've iterated one past the end of
+ // the register set.
+ if (reg_index >= reg_context.GetUserRegisterCount())
+ return SendErrorResponse(69);
+
+ const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index);
+ if (!reg_info)
+ return SendErrorResponse(69);
+
+ // Build the reginfos response.
+ StreamGDBRemote response;
+
+ response.PutCString("name:");
+ response.PutCString(reg_info->name);
+ response.PutChar(';');
+
+ if (reg_info->alt_name && reg_info->alt_name[0]) {
+ response.PutCString("alt-name:");
+ response.PutCString(reg_info->alt_name);
+ response.PutChar(';');
+ }
+
+ response.Printf("bitsize:%" PRIu32 ";offset:%" PRIu32 ";",
+ reg_info->byte_size * 8, reg_info->byte_offset);
+
+ switch (reg_info->encoding) {
+ case eEncodingUint:
+ response.PutCString("encoding:uint;");
+ break;
+ case eEncodingSint:
+ response.PutCString("encoding:sint;");
+ break;
+ case eEncodingIEEE754:
+ response.PutCString("encoding:ieee754;");
+ break;
+ case eEncodingVector:
+ response.PutCString("encoding:vector;");
+ break;
+ default:
+ break;
+ }
+
+ switch (reg_info->format) {
+ case eFormatBinary:
+ response.PutCString("format:binary;");
+ break;
+ case eFormatDecimal:
+ response.PutCString("format:decimal;");
+ break;
+ case eFormatHex:
+ response.PutCString("format:hex;");
+ break;
+ case eFormatFloat:
+ response.PutCString("format:float;");
+ break;
+ case eFormatVectorOfSInt8:
+ response.PutCString("format:vector-sint8;");
+ break;
+ case eFormatVectorOfUInt8:
+ response.PutCString("format:vector-uint8;");
+ break;
+ case eFormatVectorOfSInt16:
+ response.PutCString("format:vector-sint16;");
+ break;
+ case eFormatVectorOfUInt16:
+ response.PutCString("format:vector-uint16;");
+ break;
+ case eFormatVectorOfSInt32:
+ response.PutCString("format:vector-sint32;");
+ break;
+ case eFormatVectorOfUInt32:
+ response.PutCString("format:vector-uint32;");
+ break;
+ case eFormatVectorOfFloat32:
+ response.PutCString("format:vector-float32;");
+ break;
+ case eFormatVectorOfUInt64:
+ response.PutCString("format:vector-uint64;");
+ break;
+ case eFormatVectorOfUInt128:
+ response.PutCString("format:vector-uint128;");
+ break;
+ default:
+ break;
+ };
+
+ const char *const register_set_name =
+ reg_context.GetRegisterSetNameForRegisterAtIndex(reg_index);
+ if (register_set_name) {
+ response.PutCString("set:");
+ response.PutCString(register_set_name);
+ response.PutChar(';');
+ }
+
+ if (reg_info->kinds[RegisterKind::eRegisterKindEHFrame] !=
+ LLDB_INVALID_REGNUM)
+ response.Printf("ehframe:%" PRIu32 ";",
+ reg_info->kinds[RegisterKind::eRegisterKindEHFrame]);
+
+ if (reg_info->kinds[RegisterKind::eRegisterKindDWARF] != LLDB_INVALID_REGNUM)
+ response.Printf("dwarf:%" PRIu32 ";",
+ reg_info->kinds[RegisterKind::eRegisterKindDWARF]);
+
+ switch (reg_info->kinds[RegisterKind::eRegisterKindGeneric]) {
+ case LLDB_REGNUM_GENERIC_PC:
+ response.PutCString("generic:pc;");
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ response.PutCString("generic:sp;");
+ break;
+ case LLDB_REGNUM_GENERIC_FP:
+ response.PutCString("generic:fp;");
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ response.PutCString("generic:ra;");
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ response.PutCString("generic:flags;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG1:
+ response.PutCString("generic:arg1;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG2:
+ response.PutCString("generic:arg2;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG3:
+ response.PutCString("generic:arg3;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG4:
+ response.PutCString("generic:arg4;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG5:
+ response.PutCString("generic:arg5;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG6:
+ response.PutCString("generic:arg6;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG7:
+ response.PutCString("generic:arg7;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG8:
+ response.PutCString("generic:arg8;");
+ break;
+ default:
+ break;
+ }
+
+ if (reg_info->value_regs && reg_info->value_regs[0] != LLDB_INVALID_REGNUM) {
+ response.PutCString("container-regs:");
+ int i = 0;
+ for (const uint32_t *reg_num = reg_info->value_regs;
+ *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) {
+ if (i > 0)
+ response.PutChar(',');
+ response.Printf("%" PRIx32, *reg_num);
+ }
+ response.PutChar(';');
+ }
+
+ if (reg_info->invalidate_regs && reg_info->invalidate_regs[0]) {
+ response.PutCString("invalidate-regs:");
+ int i = 0;
+ for (const uint32_t *reg_num = reg_info->invalidate_regs;
+ *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) {
+ if (i > 0)
+ response.PutChar(',');
+ response.Printf("%" PRIx32, *reg_num);
+ }
+ response.PutChar(';');
+ }
+
+ if (reg_info->dynamic_size_dwarf_expr_bytes) {
+ const size_t dwarf_opcode_len = reg_info->dynamic_size_dwarf_len;
+ response.PutCString("dynamic_size_dwarf_expr_bytes:");
+ for (uint32_t i = 0; i < dwarf_opcode_len; ++i)
+ response.PutHex8(reg_info->dynamic_size_dwarf_expr_bytes[i]);
+ response.PutChar(';');
+ }
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ LLDB_LOG(log, "no process ({0}), returning OK",
+ m_debugged_process_up ? "invalid process id"
+ : "null m_debugged_process_up");
+ return SendOKResponse();
+ }
+
+ StreamGDBRemote response;
+ response.PutChar('m');
+
+ LLDB_LOG(log, "starting thread iteration");
+ NativeThreadProtocol *thread;
+ uint32_t thread_index;
+ for (thread_index = 0,
+ thread = m_debugged_process_up->GetThreadAtIndex(thread_index);
+ thread; ++thread_index,
+ thread = m_debugged_process_up->GetThreadAtIndex(thread_index)) {
+ LLDB_LOG(log, "iterated thread {0}(tid={2})", thread_index,
+ thread->GetID());
+ if (thread_index > 0)
+ response.PutChar(',');
+ response.Printf("%" PRIx64, thread->GetID());
+ }
+
+ LLDB_LOG(log, "finished thread iteration");
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo(
+ StringExtractorGDBRemote &packet) {
+ // FIXME for now we return the full thread list in the initial packet and
+ // always do nothing here.
+ return SendPacketNoLock("l");
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_g(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Move past packet name.
+ packet.SetFilePos(strlen("g"));
+
+ // Get the thread to use.
+ NativeThreadProtocol *thread = GetThreadFromSuffix(packet);
+ if (!thread) {
+ LLDB_LOG(log, "failed, no thread available");
+ return SendErrorResponse(0x15);
+ }
+
+ // Get the thread's register context.
+ NativeRegisterContext &reg_ctx = thread->GetRegisterContext();
+
+ std::vector<uint8_t> regs_buffer;
+ for (uint32_t reg_num = 0; reg_num < reg_ctx.GetUserRegisterCount();
+ ++reg_num) {
+ const RegisterInfo *reg_info = reg_ctx.GetRegisterInfoAtIndex(reg_num);
+
+ if (reg_info == nullptr) {
+ LLDB_LOG(log, "failed to get register info for register index {0}",
+ reg_num);
+ return SendErrorResponse(0x15);
+ }
+
+ if (reg_info->value_regs != nullptr)
+ continue; // skip registers that are contained in other registers
+
+ RegisterValue reg_value;
+ Status error = reg_ctx.ReadRegister(reg_info, reg_value);
+ if (error.Fail()) {
+ LLDB_LOG(log, "failed to read register at index {0}", reg_num);
+ return SendErrorResponse(0x15);
+ }
+
+ if (reg_info->byte_offset + reg_info->byte_size >= regs_buffer.size())
+ // Resize the buffer to guarantee it can store the register offsetted
+ // data.
+ regs_buffer.resize(reg_info->byte_offset + reg_info->byte_size);
+
+ // Copy the register offsetted data to the buffer.
+ memcpy(regs_buffer.data() + reg_info->byte_offset, reg_value.GetBytes(),
+ reg_info->byte_size);
+ }
+
+ // Write the response.
+ StreamGDBRemote response;
+ response.PutBytesAsRawHex8(regs_buffer.data(), regs_buffer.size());
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_p(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Parse out the register number from the request.
+ packet.SetFilePos(strlen("p"));
+ const uint32_t reg_index =
+ packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max());
+ if (reg_index == std::numeric_limits<uint32_t>::max()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not "
+ "parse register number from request \"%s\"",
+ __FUNCTION__, packet.GetStringRef().c_str());
+ return SendErrorResponse(0x15);
+ }
+
+ // Get the thread to use.
+ NativeThreadProtocol *thread = GetThreadFromSuffix(packet);
+ if (!thread) {
+ LLDB_LOG(log, "failed, no thread available");
+ return SendErrorResponse(0x15);
+ }
+
+ // Get the thread's register context.
+ NativeRegisterContext &reg_context = thread->GetRegisterContext();
+
+ // Return the end of registers response if we've iterated one past the end of
+ // the register set.
+ if (reg_index >= reg_context.GetUserRegisterCount()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested "
+ "register %" PRIu32 " beyond register count %" PRIu32,
+ __FUNCTION__, reg_index,
+ reg_context.GetUserRegisterCount());
+ return SendErrorResponse(0x15);
+ }
+
+ const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index);
+ if (!reg_info) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested "
+ "register %" PRIu32 " returned NULL",
+ __FUNCTION__, reg_index);
+ return SendErrorResponse(0x15);
+ }
+
+ // Build the reginfos response.
+ StreamGDBRemote response;
+
+ // Retrieve the value
+ RegisterValue reg_value;
+ Status error = reg_context.ReadRegister(reg_info, reg_value);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, read of "
+ "requested register %" PRIu32 " (%s) failed: %s",
+ __FUNCTION__, reg_index, reg_info->name, error.AsCString());
+ return SendErrorResponse(0x15);
+ }
+
+ const uint8_t *const data =
+ reinterpret_cast<const uint8_t *>(reg_value.GetBytes());
+ if (!data) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to get data "
+ "bytes from requested register %" PRIu32,
+ __FUNCTION__, reg_index);
+ return SendErrorResponse(0x15);
+ }
+
+ // FIXME flip as needed to get data in big/little endian format for this host.
+ for (uint32_t i = 0; i < reg_value.GetByteSize(); ++i)
+ response.PutHex8(data[i]);
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_P(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Ensure there is more content.
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Empty P packet");
+
+ // Parse out the register number from the request.
+ packet.SetFilePos(strlen("P"));
+ const uint32_t reg_index =
+ packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max());
+ if (reg_index == std::numeric_limits<uint32_t>::max()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not "
+ "parse register number from request \"%s\"",
+ __FUNCTION__, packet.GetStringRef().c_str());
+ return SendErrorResponse(0x29);
+ }
+
+ // Note debugserver would send an E30 here.
+ if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != '='))
+ return SendIllFormedResponse(
+ packet, "P packet missing '=' char after register number");
+
+ // Parse out the value.
+ uint8_t reg_bytes[32]; // big enough to support up to 256 bit ymmN register
+ size_t reg_size = packet.GetHexBytesAvail(reg_bytes);
+
+ // Get the thread to use.
+ NativeThreadProtocol *thread = GetThreadFromSuffix(packet);
+ if (!thread) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, no thread "
+ "available (thread index 0)",
+ __FUNCTION__);
+ return SendErrorResponse(0x28);
+ }
+
+ // Get the thread's register context.
+ NativeRegisterContext &reg_context = thread->GetRegisterContext();
+ const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index);
+ if (!reg_info) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested "
+ "register %" PRIu32 " returned NULL",
+ __FUNCTION__, reg_index);
+ return SendErrorResponse(0x48);
+ }
+
+ // Return the end of registers response if we've iterated one past the end of
+ // the register set.
+ if (reg_index >= reg_context.GetUserRegisterCount()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested "
+ "register %" PRIu32 " beyond register count %" PRIu32,
+ __FUNCTION__, reg_index, reg_context.GetUserRegisterCount());
+ return SendErrorResponse(0x47);
+ }
+
+ // The dwarf expression are evaluate on host site which may cause register
+ // size to change Hence the reg_size may not be same as reg_info->bytes_size
+ if ((reg_size != reg_info->byte_size) &&
+ !(reg_info->dynamic_size_dwarf_expr_bytes)) {
+ return SendIllFormedResponse(packet, "P packet register size is incorrect");
+ }
+
+ // Build the reginfos response.
+ StreamGDBRemote response;
+
+ RegisterValue reg_value(
+ reg_bytes, reg_size,
+ m_debugged_process_up->GetArchitecture().GetByteOrder());
+ Status error = reg_context.WriteRegister(reg_info, reg_value);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, write of "
+ "requested register %" PRIu32 " (%s) failed: %s",
+ __FUNCTION__, reg_index, reg_info->name, error.AsCString());
+ return SendErrorResponse(0x32);
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_H(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out which variant of $H is requested.
+ packet.SetFilePos(strlen("H"));
+ if (packet.GetBytesLeft() < 1) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, H command "
+ "missing {g,c} variant",
+ __FUNCTION__);
+ return SendIllFormedResponse(packet, "H command missing {g,c} variant");
+ }
+
+ const char h_variant = packet.GetChar();
+ switch (h_variant) {
+ case 'g':
+ break;
+
+ case 'c':
+ break;
+
+ default:
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, invalid $H variant %c",
+ __FUNCTION__, h_variant);
+ return SendIllFormedResponse(packet,
+ "H variant unsupported, should be c or g");
+ }
+
+ // Parse out the thread number.
+ // FIXME return a parse success/fail value. All values are valid here.
+ const lldb::tid_t tid =
+ packet.GetHexMaxU64(false, std::numeric_limits<lldb::tid_t>::max());
+
+ // Ensure we have the given thread when not specifying -1 (all threads) or 0
+ // (any thread).
+ if (tid != LLDB_INVALID_THREAD_ID && tid != 0) {
+ NativeThreadProtocol *thread = m_debugged_process_up->GetThreadByID(tid);
+ if (!thread) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, tid %" PRIu64
+ " not found",
+ __FUNCTION__, tid);
+ return SendErrorResponse(0x15);
+ }
+ }
+
+ // Now switch the given thread type.
+ switch (h_variant) {
+ case 'g':
+ SetCurrentThreadID(tid);
+ break;
+
+ case 'c':
+ SetContinueThreadID(tid);
+ break;
+
+ default:
+ assert(false && "unsupported $H variant - shouldn't get here");
+ return SendIllFormedResponse(packet,
+ "H variant unsupported, should be c or g");
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_I(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ packet.SetFilePos(::strlen("I"));
+ uint8_t tmp[4096];
+ for (;;) {
+ size_t read = packet.GetHexBytesAvail(tmp);
+ if (read == 0) {
+ break;
+ }
+ // write directly to stdin *this might block if stdin buffer is full*
+ // TODO: enqueue this block in circular buffer and send window size to
+ // remote host
+ ConnectionStatus status;
+ Status error;
+ m_stdio_communication.Write(tmp, read, status, &error);
+ if (error.Fail()) {
+ return SendErrorResponse(0x15);
+ }
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_interrupt(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ LLDB_LOG(log, "failed, no process available");
+ return SendErrorResponse(0x15);
+ }
+
+ // Interrupt the process.
+ Status error = m_debugged_process_up->Interrupt();
+ if (error.Fail()) {
+ LLDB_LOG(log, "failed for process {0}: {1}", m_debugged_process_up->GetID(),
+ error);
+ return SendErrorResponse(GDBRemoteServerError::eErrorResume);
+ }
+
+ LLDB_LOG(log, "stopped process {0}", m_debugged_process_up->GetID());
+
+ // No response required from stop all.
+ return PacketResult::Success;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_memory_read(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out the memory address.
+ packet.SetFilePos(strlen("m"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Too short m packet");
+
+ // Read the address. Punting on validation.
+ // FIXME replace with Hex U64 read with no default value that fails on failed
+ // read.
+ const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0);
+
+ // Validate comma.
+ if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ','))
+ return SendIllFormedResponse(packet, "Comma sep missing in m packet");
+
+ // Get # bytes to read.
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Length missing in m packet");
+
+ const uint64_t byte_count = packet.GetHexMaxU64(false, 0);
+ if (byte_count == 0) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s nothing to read: "
+ "zero-length packet",
+ __FUNCTION__);
+ return SendOKResponse();
+ }
+
+ // Allocate the response buffer.
+ std::string buf(byte_count, '\0');
+ if (buf.empty())
+ return SendErrorResponse(0x78);
+
+ // Retrieve the process memory.
+ size_t bytes_read = 0;
+ Status error = m_debugged_process_up->ReadMemoryWithoutTrap(
+ read_addr, &buf[0], byte_count, bytes_read);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64
+ " mem 0x%" PRIx64 ": failed to read. Error: %s",
+ __FUNCTION__, m_debugged_process_up->GetID(), read_addr,
+ error.AsCString());
+ return SendErrorResponse(0x08);
+ }
+
+ if (bytes_read == 0) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64
+ " mem 0x%" PRIx64 ": read 0 of %" PRIu64 " requested bytes",
+ __FUNCTION__, m_debugged_process_up->GetID(), read_addr,
+ byte_count);
+ return SendErrorResponse(0x08);
+ }
+
+ StreamGDBRemote response;
+ packet.SetFilePos(0);
+ char kind = packet.GetChar('?');
+ if (kind == 'x')
+ response.PutEscapedBytes(buf.data(), byte_count);
+ else {
+ assert(kind == 'm');
+ for (size_t i = 0; i < bytes_read; ++i)
+ response.PutHex8(buf[i]);
+ }
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_M(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out the memory address.
+ packet.SetFilePos(strlen("M"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Too short M packet");
+
+ // Read the address. Punting on validation.
+ // FIXME replace with Hex U64 read with no default value that fails on failed
+ // read.
+ const lldb::addr_t write_addr = packet.GetHexMaxU64(false, 0);
+
+ // Validate comma.
+ if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ','))
+ return SendIllFormedResponse(packet, "Comma sep missing in M packet");
+
+ // Get # bytes to read.
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Length missing in M packet");
+
+ const uint64_t byte_count = packet.GetHexMaxU64(false, 0);
+ if (byte_count == 0) {
+ LLDB_LOG(log, "nothing to write: zero-length packet");
+ return PacketResult::Success;
+ }
+
+ // Validate colon.
+ if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ':'))
+ return SendIllFormedResponse(
+ packet, "Comma sep missing in M packet after byte length");
+
+ // Allocate the conversion buffer.
+ std::vector<uint8_t> buf(byte_count, 0);
+ if (buf.empty())
+ return SendErrorResponse(0x78);
+
+ // Convert the hex memory write contents to bytes.
+ StreamGDBRemote response;
+ const uint64_t convert_count = packet.GetHexBytes(buf, 0);
+ if (convert_count != byte_count) {
+ LLDB_LOG(log,
+ "pid {0} mem {1:x}: asked to write {2} bytes, but only found {3} "
+ "to convert.",
+ m_debugged_process_up->GetID(), write_addr, byte_count,
+ convert_count);
+ return SendIllFormedResponse(packet, "M content byte length specified did "
+ "not match hex-encoded content "
+ "length");
+ }
+
+ // Write the process memory.
+ size_t bytes_written = 0;
+ Status error = m_debugged_process_up->WriteMemory(write_addr, &buf[0],
+ byte_count, bytes_written);
+ if (error.Fail()) {
+ LLDB_LOG(log, "pid {0} mem {1:x}: failed to write. Error: {2}",
+ m_debugged_process_up->GetID(), write_addr, error);
+ return SendErrorResponse(0x09);
+ }
+
+ if (bytes_written == 0) {
+ LLDB_LOG(log, "pid {0} mem {1:x}: wrote 0 of {2} requested bytes",
+ m_debugged_process_up->GetID(), write_addr, byte_count);
+ return SendErrorResponse(0x09);
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Currently only the NativeProcessProtocol knows if it can handle a
+ // qMemoryRegionInfoSupported request, but we're not guaranteed to be
+ // attached to a process. For now we'll assume the client only asks this
+ // when a process is being debugged.
+
+ // Ensure we have a process running; otherwise, we can't figure this out
+ // since we won't have a NativeProcessProtocol.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ // Test if we can get any region back when asking for the region around NULL.
+ MemoryRegionInfo region_info;
+ const Status error =
+ m_debugged_process_up->GetMemoryRegionInfo(0, region_info);
+ if (error.Fail()) {
+ // We don't support memory region info collection for this
+ // NativeProcessProtocol.
+ return SendUnimplementedResponse("");
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Ensure we have a process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out the memory address.
+ packet.SetFilePos(strlen("qMemoryRegionInfo:"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Too short qMemoryRegionInfo: packet");
+
+ // Read the address. Punting on validation.
+ const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0);
+
+ StreamGDBRemote response;
+
+ // Get the memory region info for the target address.
+ MemoryRegionInfo region_info;
+ const Status error =
+ m_debugged_process_up->GetMemoryRegionInfo(read_addr, region_info);
+ if (error.Fail()) {
+ // Return the error message.
+
+ response.PutCString("error:");
+ response.PutStringAsRawHex8(error.AsCString());
+ response.PutChar(';');
+ } else {
+ // Range start and size.
+ response.Printf("start:%" PRIx64 ";size:%" PRIx64 ";",
+ region_info.GetRange().GetRangeBase(),
+ region_info.GetRange().GetByteSize());
+
+ // Permissions.
+ if (region_info.GetReadable() || region_info.GetWritable() ||
+ region_info.GetExecutable()) {
+ // Write permissions info.
+ response.PutCString("permissions:");
+
+ if (region_info.GetReadable())
+ response.PutChar('r');
+ if (region_info.GetWritable())
+ response.PutChar('w');
+ if (region_info.GetExecutable())
+ response.PutChar('x');
+
+ response.PutChar(';');
+ }
+
+ // Name
+ ConstString name = region_info.GetName();
+ if (name) {
+ response.PutCString("name:");
+ response.PutStringAsRawHex8(name.AsCString());
+ response.PutChar(';');
+ }
+ }
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_Z(StringExtractorGDBRemote &packet) {
+ // Ensure we have a process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ LLDB_LOG(log, "failed, no process available");
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out software or hardware breakpoint or watchpoint requested.
+ packet.SetFilePos(strlen("Z"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(
+ packet, "Too short Z packet, missing software/hardware specifier");
+
+ bool want_breakpoint = true;
+ bool want_hardware = false;
+ uint32_t watch_flags = 0;
+
+ const GDBStoppointType stoppoint_type =
+ GDBStoppointType(packet.GetS32(eStoppointInvalid));
+ switch (stoppoint_type) {
+ case eBreakpointSoftware:
+ want_hardware = false;
+ want_breakpoint = true;
+ break;
+ case eBreakpointHardware:
+ want_hardware = true;
+ want_breakpoint = true;
+ break;
+ case eWatchpointWrite:
+ watch_flags = 1;
+ want_hardware = true;
+ want_breakpoint = false;
+ break;
+ case eWatchpointRead:
+ watch_flags = 2;
+ want_hardware = true;
+ want_breakpoint = false;
+ break;
+ case eWatchpointReadWrite:
+ watch_flags = 3;
+ want_hardware = true;
+ want_breakpoint = false;
+ break;
+ case eStoppointInvalid:
+ return SendIllFormedResponse(
+ packet, "Z packet had invalid software/hardware specifier");
+ }
+
+ if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',')
+ return SendIllFormedResponse(
+ packet, "Malformed Z packet, expecting comma after stoppoint type");
+
+ // Parse out the stoppoint address.
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Too short Z packet, missing address");
+ const lldb::addr_t addr = packet.GetHexMaxU64(false, 0);
+
+ if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',')
+ return SendIllFormedResponse(
+ packet, "Malformed Z packet, expecting comma after address");
+
+ // Parse out the stoppoint size (i.e. size hint for opcode size).
+ const uint32_t size =
+ packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max());
+ if (size == std::numeric_limits<uint32_t>::max())
+ return SendIllFormedResponse(
+ packet, "Malformed Z packet, failed to parse size argument");
+
+ if (want_breakpoint) {
+ // Try to set the breakpoint.
+ const Status error =
+ m_debugged_process_up->SetBreakpoint(addr, size, want_hardware);
+ if (error.Success())
+ return SendOKResponse();
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ LLDB_LOG(log, "pid {0} failed to set breakpoint: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x09);
+ } else {
+ // Try to set the watchpoint.
+ const Status error = m_debugged_process_up->SetWatchpoint(
+ addr, size, watch_flags, want_hardware);
+ if (error.Success())
+ return SendOKResponse();
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ LLDB_LOG(log, "pid {0} failed to set watchpoint: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x09);
+ }
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_z(StringExtractorGDBRemote &packet) {
+ // Ensure we have a process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ LLDB_LOG(log, "failed, no process available");
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out software or hardware breakpoint or watchpoint requested.
+ packet.SetFilePos(strlen("z"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(
+ packet, "Too short z packet, missing software/hardware specifier");
+
+ bool want_breakpoint = true;
+ bool want_hardware = false;
+
+ const GDBStoppointType stoppoint_type =
+ GDBStoppointType(packet.GetS32(eStoppointInvalid));
+ switch (stoppoint_type) {
+ case eBreakpointHardware:
+ want_breakpoint = true;
+ want_hardware = true;
+ break;
+ case eBreakpointSoftware:
+ want_breakpoint = true;
+ break;
+ case eWatchpointWrite:
+ want_breakpoint = false;
+ break;
+ case eWatchpointRead:
+ want_breakpoint = false;
+ break;
+ case eWatchpointReadWrite:
+ want_breakpoint = false;
+ break;
+ default:
+ return SendIllFormedResponse(
+ packet, "z packet had invalid software/hardware specifier");
+ }
+
+ if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',')
+ return SendIllFormedResponse(
+ packet, "Malformed z packet, expecting comma after stoppoint type");
+
+ // Parse out the stoppoint address.
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Too short z packet, missing address");
+ const lldb::addr_t addr = packet.GetHexMaxU64(false, 0);
+
+ if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',')
+ return SendIllFormedResponse(
+ packet, "Malformed z packet, expecting comma after address");
+
+ /*
+ // Parse out the stoppoint size (i.e. size hint for opcode size).
+ const uint32_t size = packet.GetHexMaxU32 (false,
+ std::numeric_limits<uint32_t>::max ());
+ if (size == std::numeric_limits<uint32_t>::max ())
+ return SendIllFormedResponse(packet, "Malformed z packet, failed to parse
+ size argument");
+ */
+
+ if (want_breakpoint) {
+ // Try to clear the breakpoint.
+ const Status error =
+ m_debugged_process_up->RemoveBreakpoint(addr, want_hardware);
+ if (error.Success())
+ return SendOKResponse();
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ LLDB_LOG(log, "pid {0} failed to remove breakpoint: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x09);
+ } else {
+ // Try to clear the watchpoint.
+ const Status error = m_debugged_process_up->RemoveWatchpoint(addr);
+ if (error.Success())
+ return SendOKResponse();
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ LLDB_LOG(log, "pid {0} failed to remove watchpoint: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x09);
+ }
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_s(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ // Ensure we have a process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x32);
+ }
+
+ // We first try to use a continue thread id. If any one or any all set, use
+ // the current thread. Bail out if we don't have a thread id.
+ lldb::tid_t tid = GetContinueThreadID();
+ if (tid == 0 || tid == LLDB_INVALID_THREAD_ID)
+ tid = GetCurrentThreadID();
+ if (tid == LLDB_INVALID_THREAD_ID)
+ return SendErrorResponse(0x33);
+
+ // Double check that we have such a thread.
+ // TODO investigate: on MacOSX we might need to do an UpdateThreads () here.
+ NativeThreadProtocol *thread = m_debugged_process_up->GetThreadByID(tid);
+ if (!thread)
+ return SendErrorResponse(0x33);
+
+ // Create the step action for the given thread.
+ ResumeAction action = {tid, eStateStepping, 0};
+
+ // Setup the actions list.
+ ResumeActionList actions;
+ actions.Append(action);
+
+ // All other threads stop while we're single stepping a thread.
+ actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
+ Status error = m_debugged_process_up->Resume(actions);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64
+ " tid %" PRIu64 " Resume() failed with error: %s",
+ __FUNCTION__, m_debugged_process_up->GetID(), tid,
+ error.AsCString());
+ return SendErrorResponse(0x49);
+ }
+
+ // No response here - the stop or exit will come from the resulting action.
+ return PacketResult::Success;
+}
+
+llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+GDBRemoteCommunicationServerLLGS::ReadXferObject(llvm::StringRef object,
+ llvm::StringRef annex) {
+ if (object == "auxv") {
+ // Make sure we have a valid process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "No process available");
+ }
+
+ // Grab the auxv data.
+ auto buffer_or_error = m_debugged_process_up->GetAuxvData();
+ if (!buffer_or_error)
+ return llvm::errorCodeToError(buffer_or_error.getError());
+ return std::move(*buffer_or_error);
+ }
+
+ return llvm::make_error<PacketUnimplementedError>(
+ "Xfer object not supported");
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qXfer(
+ StringExtractorGDBRemote &packet) {
+ SmallVector<StringRef, 5> fields;
+ // The packet format is "qXfer:<object>:<action>:<annex>:offset,length"
+ StringRef(packet.GetStringRef()).split(fields, ':', 4);
+ if (fields.size() != 5)
+ return SendIllFormedResponse(packet, "malformed qXfer packet");
+ StringRef &xfer_object = fields[1];
+ StringRef &xfer_action = fields[2];
+ StringRef &xfer_annex = fields[3];
+ StringExtractor offset_data(fields[4]);
+ if (xfer_action != "read")
+ return SendUnimplementedResponse("qXfer action not supported");
+ // Parse offset.
+ const uint64_t xfer_offset =
+ offset_data.GetHexMaxU64(false, std::numeric_limits<uint64_t>::max());
+ if (xfer_offset == std::numeric_limits<uint64_t>::max())
+ return SendIllFormedResponse(packet, "qXfer packet missing offset");
+ // Parse out comma.
+ if (offset_data.GetChar() != ',')
+ return SendIllFormedResponse(packet,
+ "qXfer packet missing comma after offset");
+ // Parse out the length.
+ const uint64_t xfer_length =
+ offset_data.GetHexMaxU64(false, std::numeric_limits<uint64_t>::max());
+ if (xfer_length == std::numeric_limits<uint64_t>::max())
+ return SendIllFormedResponse(packet, "qXfer packet missing length");
+
+ // Get a previously constructed buffer if it exists or create it now.
+ std::string buffer_key = (xfer_object + xfer_action + xfer_annex).str();
+ auto buffer_it = m_xfer_buffer_map.find(buffer_key);
+ if (buffer_it == m_xfer_buffer_map.end()) {
+ auto buffer_up = ReadXferObject(xfer_object, xfer_annex);
+ if (!buffer_up)
+ return SendErrorResponse(buffer_up.takeError());
+ buffer_it = m_xfer_buffer_map
+ .insert(std::make_pair(buffer_key, std::move(*buffer_up)))
+ .first;
+ }
+
+ // Send back the response
+ StreamGDBRemote response;
+ bool done_with_buffer = false;
+ llvm::StringRef buffer = buffer_it->second->getBuffer();
+ if (xfer_offset >= buffer.size()) {
+ // We have nothing left to send. Mark the buffer as complete.
+ response.PutChar('l');
+ done_with_buffer = true;
+ } else {
+ // Figure out how many bytes are available starting at the given offset.
+ buffer = buffer.drop_front(xfer_offset);
+ // Mark the response type according to whether we're reading the remainder
+ // of the data.
+ if (xfer_length >= buffer.size()) {
+ // There will be nothing left to read after this
+ response.PutChar('l');
+ done_with_buffer = true;
+ } else {
+ // There will still be bytes to read after this request.
+ response.PutChar('m');
+ buffer = buffer.take_front(xfer_length);
+ }
+ // Now write the data in encoded binary form.
+ response.PutEscapedBytes(buffer.data(), buffer.size());
+ }
+
+ if (done_with_buffer)
+ m_xfer_buffer_map.erase(buffer_it);
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Move past packet name.
+ packet.SetFilePos(strlen("QSaveRegisterState"));
+
+ // Get the thread to use.
+ NativeThreadProtocol *thread = GetThreadFromSuffix(packet);
+ if (!thread) {
+ if (m_thread_suffix_supported)
+ return SendIllFormedResponse(
+ packet, "No thread specified in QSaveRegisterState packet");
+ else
+ return SendIllFormedResponse(packet,
+ "No thread was is set with the Hg packet");
+ }
+
+ // Grab the register context for the thread.
+ NativeRegisterContext& reg_context = thread->GetRegisterContext();
+
+ // Save registers to a buffer.
+ DataBufferSP register_data_sp;
+ Status error = reg_context.ReadAllRegisterValues(register_data_sp);
+ if (error.Fail()) {
+ LLDB_LOG(log, "pid {0} failed to save all register values: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x75);
+ }
+
+ // Allocate a new save id.
+ const uint32_t save_id = GetNextSavedRegistersID();
+ assert((m_saved_registers_map.find(save_id) == m_saved_registers_map.end()) &&
+ "GetNextRegisterSaveID() returned an existing register save id");
+
+ // Save the register data buffer under the save id.
+ {
+ std::lock_guard<std::mutex> guard(m_saved_registers_mutex);
+ m_saved_registers_map[save_id] = register_data_sp;
+ }
+
+ // Write the response.
+ StreamGDBRemote response;
+ response.Printf("%" PRIu32, save_id);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Parse out save id.
+ packet.SetFilePos(strlen("QRestoreRegisterState:"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(
+ packet, "QRestoreRegisterState packet missing register save id");
+
+ const uint32_t save_id = packet.GetU32(0);
+ if (save_id == 0) {
+ LLDB_LOG(log, "QRestoreRegisterState packet has malformed save id, "
+ "expecting decimal uint32_t");
+ return SendErrorResponse(0x76);
+ }
+
+ // Get the thread to use.
+ NativeThreadProtocol *thread = GetThreadFromSuffix(packet);
+ if (!thread) {
+ if (m_thread_suffix_supported)
+ return SendIllFormedResponse(
+ packet, "No thread specified in QRestoreRegisterState packet");
+ else
+ return SendIllFormedResponse(packet,
+ "No thread was is set with the Hg packet");
+ }
+
+ // Grab the register context for the thread.
+ NativeRegisterContext &reg_context = thread->GetRegisterContext();
+
+ // Retrieve register state buffer, then remove from the list.
+ DataBufferSP register_data_sp;
+ {
+ std::lock_guard<std::mutex> guard(m_saved_registers_mutex);
+
+ // Find the register set buffer for the given save id.
+ auto it = m_saved_registers_map.find(save_id);
+ if (it == m_saved_registers_map.end()) {
+ LLDB_LOG(log,
+ "pid {0} does not have a register set save buffer for id {1}",
+ m_debugged_process_up->GetID(), save_id);
+ return SendErrorResponse(0x77);
+ }
+ register_data_sp = it->second;
+
+ // Remove it from the map.
+ m_saved_registers_map.erase(it);
+ }
+
+ Status error = reg_context.WriteAllRegisterValues(register_data_sp);
+ if (error.Fail()) {
+ LLDB_LOG(log, "pid {0} failed to restore all register values: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x77);
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_vAttach(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Consume the ';' after vAttach.
+ packet.SetFilePos(strlen("vAttach"));
+ if (!packet.GetBytesLeft() || packet.GetChar() != ';')
+ return SendIllFormedResponse(packet, "vAttach missing expected ';'");
+
+ // Grab the PID to which we will attach (assume hex encoding).
+ lldb::pid_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16);
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ return SendIllFormedResponse(packet,
+ "vAttach failed to parse the process id");
+
+ // Attempt to attach.
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s attempting to attach to "
+ "pid %" PRIu64,
+ __FUNCTION__, pid);
+
+ Status error = AttachToProcess(pid);
+
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to attach to "
+ "pid %" PRIu64 ": %s\n",
+ __FUNCTION__, pid, error.AsCString());
+ return SendErrorResponse(error);
+ }
+
+ // Notify we attached by sending a stop packet.
+ return SendStopReasonForState(m_debugged_process_up->GetState());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_D(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ StopSTDIOForwarding();
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+ // Consume the ';' after D.
+ packet.SetFilePos(1);
+ if (packet.GetBytesLeft()) {
+ if (packet.GetChar() != ';')
+ return SendIllFormedResponse(packet, "D missing expected ';'");
+
+ // Grab the PID from which we will detach (assume hex encoding).
+ pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16);
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ return SendIllFormedResponse(packet, "D failed to parse the process id");
+ }
+
+ if (pid != LLDB_INVALID_PROCESS_ID && m_debugged_process_up->GetID() != pid) {
+ return SendIllFormedResponse(packet, "Invalid pid");
+ }
+
+ const Status error = m_debugged_process_up->Detach();
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to detach from "
+ "pid %" PRIu64 ": %s\n",
+ __FUNCTION__, m_debugged_process_up->GetID(),
+ error.AsCString());
+ return SendErrorResponse(0x01);
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ packet.SetFilePos(strlen("qThreadStopInfo"));
+ const lldb::tid_t tid = packet.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID);
+ if (tid == LLDB_INVALID_THREAD_ID) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not "
+ "parse thread id from request \"%s\"",
+ __FUNCTION__, packet.GetStringRef().c_str());
+ return SendErrorResponse(0x15);
+ }
+ return SendStopReplyPacketForThread(tid);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo(
+ StringExtractorGDBRemote &) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ // Ensure we have a debugged process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(50);
+ LLDB_LOG(log, "preparing packet for pid {0}", m_debugged_process_up->GetID());
+
+ StreamString response;
+ const bool threads_with_valid_stop_info_only = false;
+ JSONArray::SP threads_array_sp = GetJSONThreadsInfo(
+ *m_debugged_process_up, threads_with_valid_stop_info_only);
+ if (!threads_array_sp) {
+ LLDB_LOG(log, "failed to prepare a packet for pid {0}",
+ m_debugged_process_up->GetID());
+ return SendErrorResponse(52);
+ }
+
+ threads_array_sp->Write(response);
+ StreamGDBRemote escaped_response;
+ escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
+ return SendPacketNoLock(escaped_response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo(
+ StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse(68);
+
+ packet.SetFilePos(strlen("qWatchpointSupportInfo"));
+ if (packet.GetBytesLeft() == 0)
+ return SendOKResponse();
+ if (packet.GetChar() != ':')
+ return SendErrorResponse(67);
+
+ auto hw_debug_cap = m_debugged_process_up->GetHardwareDebugSupportInfo();
+
+ StreamGDBRemote response;
+ if (hw_debug_cap == llvm::None)
+ response.Printf("num:0;");
+ else
+ response.Printf("num:%d;", hw_debug_cap->second);
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress(
+ StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse(67);
+
+ packet.SetFilePos(strlen("qFileLoadAddress:"));
+ if (packet.GetBytesLeft() == 0)
+ return SendErrorResponse(68);
+
+ std::string file_name;
+ packet.GetHexByteString(file_name);
+
+ lldb::addr_t file_load_address = LLDB_INVALID_ADDRESS;
+ Status error =
+ m_debugged_process_up->GetFileLoadAddress(file_name, file_load_address);
+ if (error.Fail())
+ return SendErrorResponse(69);
+
+ if (file_load_address == LLDB_INVALID_ADDRESS)
+ return SendErrorResponse(1); // File not loaded
+
+ StreamGDBRemote response;
+ response.PutHex64(file_load_address);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QPassSignals(
+ StringExtractorGDBRemote &packet) {
+ std::vector<int> signals;
+ packet.SetFilePos(strlen("QPassSignals:"));
+
+ // Read sequence of hex signal numbers divided by a semicolon and optionally
+ // spaces.
+ while (packet.GetBytesLeft() > 0) {
+ int signal = packet.GetS32(-1, 16);
+ if (signal < 0)
+ return SendIllFormedResponse(packet, "Failed to parse signal number.");
+ signals.push_back(signal);
+
+ packet.SkipSpaces();
+ char separator = packet.GetChar();
+ if (separator == '\0')
+ break; // End of string
+ if (separator != ';')
+ return SendIllFormedResponse(packet, "Invalid separator,"
+ " expected semicolon.");
+ }
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up)
+ return SendErrorResponse(68);
+
+ Status error = m_debugged_process_up->IgnoreSignals(signals);
+ if (error.Fail())
+ return SendErrorResponse(69);
+
+ return SendOKResponse();
+}
+
+void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Tell the stdio connection to shut down.
+ if (m_stdio_communication.IsConnected()) {
+ auto connection = m_stdio_communication.GetConnection();
+ if (connection) {
+ Status error;
+ connection->Disconnect(&error);
+
+ if (error.Success()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s disconnect process "
+ "terminal stdio - SUCCESS",
+ __FUNCTION__);
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s disconnect process "
+ "terminal stdio - FAIL: %s",
+ __FUNCTION__, error.AsCString());
+ }
+ }
+ }
+}
+
+NativeThreadProtocol *GDBRemoteCommunicationServerLLGS::GetThreadFromSuffix(
+ StringExtractorGDBRemote &packet) {
+ // We have no thread if we don't have a process.
+ if (!m_debugged_process_up ||
+ m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)
+ return nullptr;
+
+ // If the client hasn't asked for thread suffix support, there will not be a
+ // thread suffix. Use the current thread in that case.
+ if (!m_thread_suffix_supported) {
+ const lldb::tid_t current_tid = GetCurrentThreadID();
+ if (current_tid == LLDB_INVALID_THREAD_ID)
+ return nullptr;
+ else if (current_tid == 0) {
+ // Pick a thread.
+ return m_debugged_process_up->GetThreadAtIndex(0);
+ } else
+ return m_debugged_process_up->GetThreadByID(current_tid);
+ }
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Parse out the ';'.
+ if (packet.GetBytesLeft() < 1 || packet.GetChar() != ';') {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse "
+ "error: expected ';' prior to start of thread suffix: packet "
+ "contents = '%s'",
+ __FUNCTION__, packet.GetStringRef().c_str());
+ return nullptr;
+ }
+
+ if (!packet.GetBytesLeft())
+ return nullptr;
+
+ // Parse out thread: portion.
+ if (strncmp(packet.Peek(), "thread:", strlen("thread:")) != 0) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse "
+ "error: expected 'thread:' but not found, packet contents = "
+ "'%s'",
+ __FUNCTION__, packet.GetStringRef().c_str());
+ return nullptr;
+ }
+ packet.SetFilePos(packet.GetFilePos() + strlen("thread:"));
+ const lldb::tid_t tid = packet.GetHexMaxU64(false, 0);
+ if (tid != 0)
+ return m_debugged_process_up->GetThreadByID(tid);
+
+ return nullptr;
+}
+
+lldb::tid_t GDBRemoteCommunicationServerLLGS::GetCurrentThreadID() const {
+ if (m_current_tid == 0 || m_current_tid == LLDB_INVALID_THREAD_ID) {
+ // Use whatever the debug process says is the current thread id since the
+ // protocol either didn't specify or specified we want any/all threads
+ // marked as the current thread.
+ if (!m_debugged_process_up)
+ return LLDB_INVALID_THREAD_ID;
+ return m_debugged_process_up->GetCurrentThreadID();
+ }
+ // Use the specific current thread id set by the gdb remote protocol.
+ return m_current_tid;
+}
+
+uint32_t GDBRemoteCommunicationServerLLGS::GetNextSavedRegistersID() {
+ std::lock_guard<std::mutex> guard(m_saved_registers_mutex);
+ return m_next_saved_registers_id++;
+}
+
+void GDBRemoteCommunicationServerLLGS::ClearProcessSpecificData() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ LLDB_LOG(log, "clearing {0} xfer buffers", m_xfer_buffer_map.size());
+ m_xfer_buffer_map.clear();
+}
+
+FileSpec
+GDBRemoteCommunicationServerLLGS::FindModuleFile(const std::string &module_path,
+ const ArchSpec &arch) {
+ if (m_debugged_process_up) {
+ FileSpec file_spec;
+ if (m_debugged_process_up
+ ->GetLoadedModuleFileSpec(module_path.c_str(), file_spec)
+ .Success()) {
+ if (FileSystem::Instance().Exists(file_spec))
+ return file_spec;
+ }
+ }
+
+ return GDBRemoteCommunicationServerCommon::FindModuleFile(module_path, arch);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
new file mode 100644
index 000000000000..068ea52caaaf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -0,0 +1,229 @@
+//===-- GDBRemoteCommunicationServerLLGS.h ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunicationServerLLGS_h_
+#define liblldb_GDBRemoteCommunicationServerLLGS_h_
+
+#include <mutex>
+#include <unordered_map>
+
+#include "lldb/Core/Communication.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/lldb-private-forward.h"
+
+#include "GDBRemoteCommunicationServerCommon.h"
+
+class StringExtractorGDBRemote;
+
+namespace lldb_private {
+
+namespace process_gdb_remote {
+
+class ProcessGDBRemote;
+
+class GDBRemoteCommunicationServerLLGS
+ : public GDBRemoteCommunicationServerCommon,
+ public NativeProcessProtocol::NativeDelegate {
+public:
+ // Constructors and Destructors
+ GDBRemoteCommunicationServerLLGS(
+ MainLoop &mainloop,
+ const NativeProcessProtocol::Factory &process_factory);
+
+ void SetLaunchInfo(const ProcessLaunchInfo &info);
+
+ /// Launch a process with the current launch settings.
+ ///
+ /// This method supports running an lldb-gdbserver or similar
+ /// server in a situation where the startup code has been provided
+ /// with all the information for a child process to be launched.
+ ///
+ /// \return
+ /// An Status object indicating the success or failure of the
+ /// launch.
+ Status LaunchProcess() override;
+
+ /// Attach to a process.
+ ///
+ /// This method supports attaching llgs to a process accessible via the
+ /// configured Platform.
+ ///
+ /// \return
+ /// An Status object indicating the success or failure of the
+ /// attach operation.
+ Status AttachToProcess(lldb::pid_t pid);
+
+ // NativeProcessProtocol::NativeDelegate overrides
+ void InitializeDelegate(NativeProcessProtocol *process) override;
+
+ void ProcessStateChanged(NativeProcessProtocol *process,
+ lldb::StateType state) override;
+
+ void DidExec(NativeProcessProtocol *process) override;
+
+ Status InitializeConnection(std::unique_ptr<Connection> &&connection);
+
+protected:
+ MainLoop &m_mainloop;
+ MainLoop::ReadHandleUP m_network_handle_up;
+ const NativeProcessProtocol::Factory &m_process_factory;
+ lldb::tid_t m_current_tid = LLDB_INVALID_THREAD_ID;
+ lldb::tid_t m_continue_tid = LLDB_INVALID_THREAD_ID;
+ std::recursive_mutex m_debugged_process_mutex;
+ std::unique_ptr<NativeProcessProtocol> m_debugged_process_up;
+
+ Communication m_stdio_communication;
+ MainLoop::ReadHandleUP m_stdio_handle_up;
+
+ lldb::StateType m_inferior_prev_state = lldb::StateType::eStateInvalid;
+ llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> m_xfer_buffer_map;
+ std::mutex m_saved_registers_mutex;
+ std::unordered_map<uint32_t, lldb::DataBufferSP> m_saved_registers_map;
+ uint32_t m_next_saved_registers_id = 1;
+ bool m_handshake_completed = false;
+
+ PacketResult SendONotification(const char *buffer, uint32_t len);
+
+ PacketResult SendWResponse(NativeProcessProtocol *process);
+
+ PacketResult SendStopReplyPacketForThread(lldb::tid_t tid);
+
+ PacketResult SendStopReasonForState(lldb::StateType process_state);
+
+ PacketResult Handle_k(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qC(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetDisableASLR(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetWorkingDir(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_C(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_c(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vCont(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vCont_actions(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_stop_reason(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qRegisterInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qfThreadInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qsThreadInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_p(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_P(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_H(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_I(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_interrupt(StringExtractorGDBRemote &packet);
+
+ // Handles $m and $x packets.
+ PacketResult Handle_memory_read(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_M(StringExtractorGDBRemote &packet);
+
+ PacketResult
+ Handle_qMemoryRegionInfoSupported(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qMemoryRegionInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_Z(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_z(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_s(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qXfer(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSaveRegisterState(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jTraceStart(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jTraceRead(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jTraceStop(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jTraceConfigRead(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QRestoreRegisterState(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vAttach(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_D(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qThreadStopInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jThreadsInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qWatchpointSupportInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_g(StringExtractorGDBRemote &packet);
+
+ void SetCurrentThreadID(lldb::tid_t tid);
+
+ lldb::tid_t GetCurrentThreadID() const;
+
+ void SetContinueThreadID(lldb::tid_t tid);
+
+ lldb::tid_t GetContinueThreadID() const { return m_continue_tid; }
+
+ Status SetSTDIOFileDescriptor(int fd);
+
+ FileSpec FindModuleFile(const std::string &module_path,
+ const ArchSpec &arch) override;
+
+ llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+ ReadXferObject(llvm::StringRef object, llvm::StringRef annex);
+
+private:
+ void HandleInferiorState_Exited(NativeProcessProtocol *process);
+
+ void HandleInferiorState_Stopped(NativeProcessProtocol *process);
+
+ NativeThreadProtocol *GetThreadFromSuffix(StringExtractorGDBRemote &packet);
+
+ uint32_t GetNextSavedRegistersID();
+
+ void MaybeCloseInferiorTerminalConnection();
+
+ void ClearProcessSpecificData();
+
+ void RegisterPacketHandlers();
+
+ void DataAvailableCallback();
+
+ void SendProcessOutput();
+
+ void StartSTDIOForwarding();
+
+ void StopSTDIOForwarding();
+
+ // For GDBRemoteCommunicationServerLLGS only
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationServerLLGS);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationServerLLGS_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
new file mode 100644
index 000000000000..6deb75f2f021
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
@@ -0,0 +1,550 @@
+//===-- GDBRemoteCommunicationServerPlatform.cpp ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteCommunicationServerPlatform.h"
+
+#include <errno.h>
+
+#include <chrono>
+#include <csignal>
+#include <cstring>
+#include <mutex>
+#include <sstream>
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/JSON.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamGDBRemote.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/Utility/UriParser.h"
+
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+// GDBRemoteCommunicationServerPlatform constructor
+GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform(
+ const Socket::SocketProtocol socket_protocol, const char *socket_scheme)
+ : GDBRemoteCommunicationServerCommon("gdb-remote.server",
+ "gdb-remote.server.rx_packet"),
+ m_socket_protocol(socket_protocol), m_socket_scheme(socket_scheme),
+ m_spawned_pids_mutex(), m_port_map(), m_port_offset(0) {
+ m_pending_gdb_server.pid = LLDB_INVALID_PROCESS_ID;
+ m_pending_gdb_server.port = 0;
+
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qC,
+ &GDBRemoteCommunicationServerPlatform::Handle_qC);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir,
+ &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer,
+ &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer,
+ &GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess,
+ &GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qProcessInfo,
+ &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir,
+ &GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jSignalsInfo,
+ &GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo);
+
+ RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_interrupt,
+ [](StringExtractorGDBRemote packet, Status &error,
+ bool &interrupt, bool &quit) {
+ error.SetErrorString("interrupt received");
+ interrupt = true;
+ return PacketResult::Success;
+ });
+}
+
+// Destructor
+GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() {}
+
+Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer(
+ const lldb_private::Args &args, std::string hostname, lldb::pid_t &pid,
+ uint16_t &port, std::string &socket_name) {
+ if (port == UINT16_MAX)
+ port = GetNextAvailablePort();
+
+ // Spawn a new thread to accept the port that gets bound after binding to
+ // port 0 (zero).
+
+ // ignore the hostname send from the remote end, just use the ip address that
+ // we're currently communicating with as the hostname
+
+ // Spawn a debugserver and try to get the port it listens to.
+ ProcessLaunchInfo debugserver_launch_info;
+ if (hostname.empty())
+ hostname = "127.0.0.1";
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ if (log)
+ log->Printf("Launching debugserver with: %s:%u...", hostname.c_str(), port);
+
+ // Do not run in a new session so that it can not linger after the platform
+ // closes.
+ debugserver_launch_info.SetLaunchInSeparateProcessGroup(false);
+ debugserver_launch_info.SetMonitorProcessCallback(
+ std::bind(&GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped,
+ this, std::placeholders::_1),
+ false);
+
+ std::ostringstream url;
+// debugserver does not accept the URL scheme prefix.
+#if !defined(__APPLE__)
+ url << m_socket_scheme << "://";
+#endif
+ uint16_t *port_ptr = &port;
+ if (m_socket_protocol == Socket::ProtocolTcp) {
+ llvm::StringRef platform_scheme;
+ llvm::StringRef platform_ip;
+ int platform_port;
+ llvm::StringRef platform_path;
+ std::string platform_uri = GetConnection()->GetURI();
+ bool ok = UriParser::Parse(platform_uri, platform_scheme, platform_ip,
+ platform_port, platform_path);
+ UNUSED_IF_ASSERT_DISABLED(ok);
+ assert(ok);
+ url << platform_ip.str() << ":" << port;
+ } else {
+ socket_name = GetDomainSocketPath("gdbserver").GetPath();
+ url << socket_name;
+ port_ptr = nullptr;
+ }
+
+ Status error = StartDebugserverProcess(
+ url.str().c_str(), nullptr, debugserver_launch_info, port_ptr, &args, -1);
+
+ pid = debugserver_launch_info.GetProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ m_spawned_pids.insert(pid);
+ if (port > 0)
+ AssociatePortWithProcess(port, pid);
+ } else {
+ if (port > 0)
+ FreePort(port);
+ }
+ return error;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer(
+ StringExtractorGDBRemote &packet) {
+ // Spawn a local debugserver as a platform so we can then attach or launch a
+ // process...
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerPlatform::%s() called",
+ __FUNCTION__);
+
+ ConnectionFileDescriptor file_conn;
+ std::string hostname;
+ packet.SetFilePos(::strlen("qLaunchGDBServer;"));
+ llvm::StringRef name;
+ llvm::StringRef value;
+ uint16_t port = UINT16_MAX;
+ while (packet.GetNameColonValue(name, value)) {
+ if (name.equals("host"))
+ hostname = value;
+ else if (name.equals("port"))
+ value.getAsInteger(0, port);
+ }
+
+ lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ std::string socket_name;
+ Status error =
+ LaunchGDBServer(Args(), hostname, debugserver_pid, port, socket_name);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerPlatform::%s() debugserver "
+ "launch failed: %s",
+ __FUNCTION__, error.AsCString());
+ return SendErrorResponse(9);
+ }
+
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerPlatform::%s() debugserver "
+ "launched successfully as pid %" PRIu64,
+ __FUNCTION__, debugserver_pid);
+
+ StreamGDBRemote response;
+ response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid,
+ port + m_port_offset);
+ if (!socket_name.empty()) {
+ response.PutCString("socket_name:");
+ response.PutStringAsRawHex8(socket_name);
+ response.PutChar(';');
+ }
+
+ PacketResult packet_result = SendPacketNoLock(response.GetString());
+ if (packet_result != PacketResult::Success) {
+ if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
+ Host::Kill(debugserver_pid, SIGINT);
+ }
+ return packet_result;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer(
+ StringExtractorGDBRemote &packet) {
+ if (m_pending_gdb_server.pid == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse(4);
+
+ JSONObject::SP server_sp = std::make_shared<JSONObject>();
+ server_sp->SetObject("port",
+ std::make_shared<JSONNumber>(m_pending_gdb_server.port));
+ if (!m_pending_gdb_server.socket_name.empty())
+ server_sp->SetObject(
+ "socket_name",
+ std::make_shared<JSONString>(m_pending_gdb_server.socket_name.c_str()));
+
+ JSONArray server_list;
+ server_list.AppendObject(server_sp);
+
+ StreamGDBRemote response;
+ server_list.Write(response);
+
+ StreamGDBRemote escaped_response;
+ escaped_response.PutEscapedBytes(response.GetString().data(),
+ response.GetSize());
+ return SendPacketNoLock(escaped_response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qKillSpawnedProcess:"));
+
+ lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID);
+
+ // verify that we know anything about this pid. Scope for locker
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end()) {
+ // not a pid we know about
+ return SendErrorResponse(10);
+ }
+ }
+
+ // go ahead and attempt to kill the spawned process
+ if (KillSpawnedProcess(pid))
+ return SendOKResponse();
+ else
+ return SendErrorResponse(11);
+}
+
+bool GDBRemoteCommunicationServerPlatform::KillSpawnedProcess(lldb::pid_t pid) {
+ // make sure we know about this process
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end())
+ return false;
+ }
+
+ // first try a SIGTERM (standard kill)
+ Host::Kill(pid, SIGTERM);
+
+ // check if that worked
+ for (size_t i = 0; i < 10; ++i) {
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end()) {
+ // it is now killed
+ return true;
+ }
+ }
+ usleep(10000);
+ }
+
+ // check one more time after the final usleep
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end())
+ return true;
+ }
+
+ // the launched process still lives. Now try killing it again, this time
+ // with an unblockable signal.
+ Host::Kill(pid, SIGKILL);
+
+ for (size_t i = 0; i < 10; ++i) {
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end()) {
+ // it is now killed
+ return true;
+ }
+ }
+ usleep(10000);
+ }
+
+ // check one more time after the final usleep Scope for locker
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end())
+ return true;
+ }
+
+ // no luck - the process still lives
+ return false;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo(
+ StringExtractorGDBRemote &packet) {
+ lldb::pid_t pid = m_process_launch_info.GetProcessID();
+ m_process_launch_info.Clear();
+
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse(1);
+
+ ProcessInstanceInfo proc_info;
+ if (!Host::GetProcessInfo(pid, proc_info))
+ return SendErrorResponse(1);
+
+ StreamString response;
+ CreateProcessInfoResponse_DebugServerStyle(proc_info, response);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir(
+ StringExtractorGDBRemote &packet) {
+
+ llvm::SmallString<64> cwd;
+ if (std::error_code ec = llvm::sys::fs::current_path(cwd))
+ return SendErrorResponse(ec.value());
+
+ StreamString response;
+ response.PutBytesAsRawHex8(cwd.data(), cwd.size());
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetWorkingDir:"));
+ std::string path;
+ packet.GetHexByteString(path);
+
+ if (std::error_code ec = llvm::sys::fs::set_current_path(path))
+ return SendErrorResponse(ec.value());
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qC(
+ StringExtractorGDBRemote &packet) {
+ // NOTE: lldb should now be using qProcessInfo for process IDs. This path
+ // here
+ // should not be used. It is reporting process id instead of thread id. The
+ // correct answer doesn't seem to make much sense for lldb-platform.
+ // CONSIDER: flip to "unsupported".
+ lldb::pid_t pid = m_process_launch_info.GetProcessID();
+
+ StreamString response;
+ response.Printf("QC%" PRIx64, pid);
+
+ // If we launch a process and this GDB server is acting as a platform, then
+ // we need to clear the process launch state so we can start launching
+ // another process. In order to launch a process a bunch or packets need to
+ // be sent: environment packets, working directory, disable ASLR, and many
+ // more settings. When we launch a process we then need to know when to clear
+ // this information. Currently we are selecting the 'qC' packet as that
+ // packet which seems to make the most sense.
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ m_process_launch_info.Clear();
+ }
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo(
+ StringExtractorGDBRemote &packet) {
+ StructuredData::Array signal_array;
+
+ lldb::UnixSignalsSP signals = UnixSignals::CreateForHost();
+ for (auto signo = signals->GetFirstSignalNumber();
+ signo != LLDB_INVALID_SIGNAL_NUMBER;
+ signo = signals->GetNextSignalNumber(signo)) {
+ auto dictionary = std::make_shared<StructuredData::Dictionary>();
+
+ dictionary->AddIntegerItem("signo", signo);
+ dictionary->AddStringItem("name", signals->GetSignalAsCString(signo));
+
+ bool suppress, stop, notify;
+ signals->GetSignalInfo(signo, suppress, stop, notify);
+ dictionary->AddBooleanItem("suppress", suppress);
+ dictionary->AddBooleanItem("stop", stop);
+ dictionary->AddBooleanItem("notify", notify);
+
+ signal_array.Push(dictionary);
+ }
+
+ StreamString response;
+ signal_array.Dump(response);
+ return SendPacketNoLock(response.GetString());
+}
+
+bool GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped(
+ lldb::pid_t pid) {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ FreePortForProcess(pid);
+ m_spawned_pids.erase(pid);
+ return true;
+}
+
+Status GDBRemoteCommunicationServerPlatform::LaunchProcess() {
+ if (!m_process_launch_info.GetArguments().GetArgumentCount())
+ return Status("%s: no process command line specified to launch",
+ __FUNCTION__);
+
+ // specify the process monitor if not already set. This should generally be
+ // what happens since we need to reap started processes.
+ if (!m_process_launch_info.GetMonitorProcessCallback())
+ m_process_launch_info.SetMonitorProcessCallback(
+ std::bind(
+ &GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped,
+ this, std::placeholders::_1),
+ false);
+
+ Status error = Host::LaunchProcess(m_process_launch_info);
+ if (!error.Success()) {
+ fprintf(stderr, "%s: failed to launch executable %s", __FUNCTION__,
+ m_process_launch_info.GetArguments().GetArgumentAtIndex(0));
+ return error;
+ }
+
+ printf("Launched '%s' as process %" PRIu64 "...\n",
+ m_process_launch_info.GetArguments().GetArgumentAtIndex(0),
+ m_process_launch_info.GetProcessID());
+
+ // add to list of spawned processes. On an lldb-gdbserver, we would expect
+ // there to be only one.
+ const auto pid = m_process_launch_info.GetProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ // add to spawned pids
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ m_spawned_pids.insert(pid);
+ }
+
+ return error;
+}
+
+void GDBRemoteCommunicationServerPlatform::SetPortMap(PortMap &&port_map) {
+ m_port_map = port_map;
+}
+
+uint16_t GDBRemoteCommunicationServerPlatform::GetNextAvailablePort() {
+ if (m_port_map.empty())
+ return 0; // Bind to port zero and get a port, we didn't have any
+ // limitations
+
+ for (auto &pair : m_port_map) {
+ if (pair.second == LLDB_INVALID_PROCESS_ID) {
+ pair.second = ~(lldb::pid_t)LLDB_INVALID_PROCESS_ID;
+ return pair.first;
+ }
+ }
+ return UINT16_MAX;
+}
+
+bool GDBRemoteCommunicationServerPlatform::AssociatePortWithProcess(
+ uint16_t port, lldb::pid_t pid) {
+ PortMap::iterator pos = m_port_map.find(port);
+ if (pos != m_port_map.end()) {
+ pos->second = pid;
+ return true;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationServerPlatform::FreePort(uint16_t port) {
+ PortMap::iterator pos = m_port_map.find(port);
+ if (pos != m_port_map.end()) {
+ pos->second = LLDB_INVALID_PROCESS_ID;
+ return true;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationServerPlatform::FreePortForProcess(lldb::pid_t pid) {
+ if (!m_port_map.empty()) {
+ for (auto &pair : m_port_map) {
+ if (pair.second == pid) {
+ pair.second = LLDB_INVALID_PROCESS_ID;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+const FileSpec &GDBRemoteCommunicationServerPlatform::GetDomainSocketDir() {
+ static FileSpec g_domainsocket_dir;
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ const char *domainsocket_dir_env =
+ ::getenv("LLDB_DEBUGSERVER_DOMAINSOCKET_DIR");
+ if (domainsocket_dir_env != nullptr)
+ g_domainsocket_dir = FileSpec(domainsocket_dir_env);
+ else
+ g_domainsocket_dir = HostInfo::GetProcessTempDir();
+ });
+
+ return g_domainsocket_dir;
+}
+
+FileSpec
+GDBRemoteCommunicationServerPlatform::GetDomainSocketPath(const char *prefix) {
+ llvm::SmallString<128> socket_path;
+ llvm::SmallString<128> socket_name(
+ (llvm::StringRef(prefix) + ".%%%%%%").str());
+
+ FileSpec socket_path_spec(GetDomainSocketDir());
+ socket_path_spec.AppendPathComponent(socket_name.c_str());
+
+ llvm::sys::fs::createUniqueFile(socket_path_spec.GetCString(), socket_path);
+ return FileSpec(socket_path.c_str());
+}
+
+void GDBRemoteCommunicationServerPlatform::SetPortOffset(uint16_t port_offset) {
+ m_port_offset = port_offset;
+}
+
+void GDBRemoteCommunicationServerPlatform::SetPendingGdbServer(
+ lldb::pid_t pid, uint16_t port, const std::string &socket_name) {
+ m_pending_gdb_server.pid = pid;
+ m_pending_gdb_server.port = port;
+ m_pending_gdb_server.socket_name = socket_name;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h
new file mode 100644
index 000000000000..eacc99a012db
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h
@@ -0,0 +1,109 @@
+//===-- GDBRemoteCommunicationServerPlatform.h ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunicationServerPlatform_h_
+#define liblldb_GDBRemoteCommunicationServerPlatform_h_
+
+#include <map>
+#include <mutex>
+#include <set>
+
+#include "GDBRemoteCommunicationServerCommon.h"
+#include "lldb/Host/Socket.h"
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class GDBRemoteCommunicationServerPlatform
+ : public GDBRemoteCommunicationServerCommon {
+public:
+ typedef std::map<uint16_t, lldb::pid_t> PortMap;
+
+ GDBRemoteCommunicationServerPlatform(
+ const Socket::SocketProtocol socket_protocol, const char *socket_scheme);
+
+ ~GDBRemoteCommunicationServerPlatform() override;
+
+ Status LaunchProcess() override;
+
+ // Set both ports to zero to let the platform automatically bind to
+ // a port chosen by the OS.
+ void SetPortMap(PortMap &&port_map);
+
+ // If we are using a port map where we can only use certain ports,
+ // get the next available port.
+ //
+ // If we are using a port map and we are out of ports, return UINT16_MAX
+ //
+ // If we aren't using a port map, return 0 to indicate we should bind to
+ // port 0 and then figure out which port we used.
+ uint16_t GetNextAvailablePort();
+
+ bool AssociatePortWithProcess(uint16_t port, lldb::pid_t pid);
+
+ bool FreePort(uint16_t port);
+
+ bool FreePortForProcess(lldb::pid_t pid);
+
+ void SetPortOffset(uint16_t port_offset);
+
+ void SetInferiorArguments(const lldb_private::Args &args);
+
+ Status LaunchGDBServer(const lldb_private::Args &args, std::string hostname,
+ lldb::pid_t &pid, uint16_t &port,
+ std::string &socket_name);
+
+ void SetPendingGdbServer(lldb::pid_t pid, uint16_t port,
+ const std::string &socket_name);
+
+protected:
+ const Socket::SocketProtocol m_socket_protocol;
+ const std::string m_socket_scheme;
+ std::recursive_mutex m_spawned_pids_mutex;
+ std::set<lldb::pid_t> m_spawned_pids;
+
+ PortMap m_port_map;
+ uint16_t m_port_offset;
+ struct {
+ lldb::pid_t pid;
+ uint16_t port;
+ std::string socket_name;
+ } m_pending_gdb_server;
+
+ PacketResult Handle_qLaunchGDBServer(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qQueryGDBServer(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qKillSpawnedProcess(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetWorkingDir(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qC(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jSignalsInfo(StringExtractorGDBRemote &packet);
+
+private:
+ bool KillSpawnedProcess(lldb::pid_t pid);
+
+ bool DebugserverProcessReaped(lldb::pid_t pid);
+
+ static const FileSpec &GetDomainSocketDir();
+
+ static FileSpec GetDomainSocketPath(const char *prefix);
+
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationServerPlatform);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationServerPlatform_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
new file mode 100644
index 000000000000..a77e659a55fa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
@@ -0,0 +1,958 @@
+//===-- GDBRemoteRegisterContext.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteRegisterContext.h"
+
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/StreamString.h"
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "ThreadGDBRemote.h"
+#include "Utility/ARM_DWARF_Registers.h"
+#include "Utility/ARM_ehframe_Registers.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+// GDBRemoteRegisterContext constructor
+GDBRemoteRegisterContext::GDBRemoteRegisterContext(
+ ThreadGDBRemote &thread, uint32_t concrete_frame_idx,
+ GDBRemoteDynamicRegisterInfo &reg_info, bool read_all_at_once)
+ : RegisterContext(thread, concrete_frame_idx), m_reg_info(reg_info),
+ m_reg_valid(), m_reg_data(), m_read_all_at_once(read_all_at_once) {
+ // Resize our vector of bools to contain one bool for every register. We will
+ // use these boolean values to know when a register value is valid in
+ // m_reg_data.
+ m_reg_valid.resize(reg_info.GetNumRegisters());
+
+ // Make a heap based buffer that is big enough to store all registers
+ DataBufferSP reg_data_sp(
+ new DataBufferHeap(reg_info.GetRegisterDataByteSize(), 0));
+ m_reg_data.SetData(reg_data_sp);
+ m_reg_data.SetByteOrder(thread.GetProcess()->GetByteOrder());
+}
+
+// Destructor
+GDBRemoteRegisterContext::~GDBRemoteRegisterContext() {}
+
+void GDBRemoteRegisterContext::InvalidateAllRegisters() {
+ SetAllRegisterValid(false);
+}
+
+void GDBRemoteRegisterContext::SetAllRegisterValid(bool b) {
+ std::vector<bool>::iterator pos, end = m_reg_valid.end();
+ for (pos = m_reg_valid.begin(); pos != end; ++pos)
+ *pos = b;
+}
+
+size_t GDBRemoteRegisterContext::GetRegisterCount() {
+ return m_reg_info.GetNumRegisters();
+}
+
+const RegisterInfo *
+GDBRemoteRegisterContext::GetRegisterInfoAtIndex(size_t reg) {
+ RegisterInfo *reg_info = m_reg_info.GetRegisterInfoAtIndex(reg);
+
+ if (reg_info && reg_info->dynamic_size_dwarf_expr_bytes) {
+ const ArchSpec &arch = m_thread.GetProcess()->GetTarget().GetArchitecture();
+ uint8_t reg_size = UpdateDynamicRegisterSize(arch, reg_info);
+ reg_info->byte_size = reg_size;
+ }
+ return reg_info;
+}
+
+size_t GDBRemoteRegisterContext::GetRegisterSetCount() {
+ return m_reg_info.GetNumRegisterSets();
+}
+
+const RegisterSet *GDBRemoteRegisterContext::GetRegisterSet(size_t reg_set) {
+ return m_reg_info.GetRegisterSet(reg_set);
+}
+
+bool GDBRemoteRegisterContext::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ // Read the register
+ if (ReadRegisterBytes(reg_info, m_reg_data)) {
+ const bool partial_data_ok = false;
+ Status error(value.SetValueFromData(
+ reg_info, m_reg_data, reg_info->byte_offset, partial_data_ok));
+ return error.Success();
+ }
+ return false;
+}
+
+bool GDBRemoteRegisterContext::PrivateSetRegisterValue(
+ uint32_t reg, llvm::ArrayRef<uint8_t> data) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info == nullptr)
+ return false;
+
+ // Invalidate if needed
+ InvalidateIfNeeded(false);
+
+ const size_t reg_byte_size = reg_info->byte_size;
+ memcpy(const_cast<uint8_t *>(
+ m_reg_data.PeekData(reg_info->byte_offset, reg_byte_size)),
+ data.data(), std::min(data.size(), reg_byte_size));
+ bool success = data.size() >= reg_byte_size;
+ if (success) {
+ SetRegisterIsValid(reg, true);
+ } else if (data.size() > 0) {
+ // Only set register is valid to false if we copied some bytes, else leave
+ // it as it was.
+ SetRegisterIsValid(reg, false);
+ }
+ return success;
+}
+
+bool GDBRemoteRegisterContext::PrivateSetRegisterValue(uint32_t reg,
+ uint64_t new_reg_val) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info == nullptr)
+ return false;
+
+ // Early in process startup, we can get a thread that has an invalid byte
+ // order because the process hasn't been completely set up yet (see the ctor
+ // where the byte order is setfrom the process). If that's the case, we
+ // can't set the value here.
+ if (m_reg_data.GetByteOrder() == eByteOrderInvalid) {
+ return false;
+ }
+
+ // Invalidate if needed
+ InvalidateIfNeeded(false);
+
+ DataBufferSP buffer_sp(new DataBufferHeap(&new_reg_val, sizeof(new_reg_val)));
+ DataExtractor data(buffer_sp, endian::InlHostByteOrder(), sizeof(void *));
+
+ // If our register context and our register info disagree, which should never
+ // happen, don't overwrite past the end of the buffer.
+ if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size)
+ return false;
+
+ // Grab a pointer to where we are going to put this register
+ uint8_t *dst = const_cast<uint8_t *>(
+ m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size));
+
+ if (dst == nullptr)
+ return false;
+
+ if (data.CopyByteOrderedData(0, // src offset
+ reg_info->byte_size, // src length
+ dst, // dst
+ reg_info->byte_size, // dst length
+ m_reg_data.GetByteOrder())) // dst byte order
+ {
+ SetRegisterIsValid(reg, true);
+ return true;
+ }
+ return false;
+}
+
+// Helper function for GDBRemoteRegisterContext::ReadRegisterBytes().
+bool GDBRemoteRegisterContext::GetPrimordialRegister(
+ const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) {
+ const uint32_t lldb_reg = reg_info->kinds[eRegisterKindLLDB];
+ const uint32_t remote_reg = reg_info->kinds[eRegisterKindProcessPlugin];
+
+ if (DataBufferSP buffer_sp =
+ gdb_comm.ReadRegister(m_thread.GetProtocolID(), remote_reg))
+ return PrivateSetRegisterValue(
+ lldb_reg, llvm::ArrayRef<uint8_t>(buffer_sp->GetBytes(),
+ buffer_sp->GetByteSize()));
+ return false;
+}
+
+bool GDBRemoteRegisterContext::ReadRegisterBytes(const RegisterInfo *reg_info,
+ DataExtractor &data) {
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ InvalidateIfNeeded(false);
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (!GetRegisterIsValid(reg)) {
+ if (m_read_all_at_once) {
+ if (DataBufferSP buffer_sp =
+ gdb_comm.ReadAllRegisters(m_thread.GetProtocolID())) {
+ memcpy(const_cast<uint8_t *>(m_reg_data.GetDataStart()),
+ buffer_sp->GetBytes(),
+ std::min(buffer_sp->GetByteSize(), m_reg_data.GetByteSize()));
+ if (buffer_sp->GetByteSize() >= m_reg_data.GetByteSize()) {
+ SetAllRegisterValid(true);
+ return true;
+ } else {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD |
+ GDBR_LOG_PACKETS));
+ if (log)
+ log->Printf ("error: GDBRemoteRegisterContext::ReadRegisterBytes tried to read the "
+ "entire register context at once, expected at least %" PRId64 " bytes "
+ "but only got %" PRId64 " bytes.", m_reg_data.GetByteSize(),
+ buffer_sp->GetByteSize());
+ }
+ }
+ return false;
+ }
+ if (reg_info->value_regs) {
+ // Process this composite register request by delegating to the
+ // constituent primordial registers.
+
+ // Index of the primordial register.
+ bool success = true;
+ for (uint32_t idx = 0; success; ++idx) {
+ const uint32_t prim_reg = reg_info->value_regs[idx];
+ if (prim_reg == LLDB_INVALID_REGNUM)
+ break;
+ // We have a valid primordial register as our constituent. Grab the
+ // corresponding register info.
+ const RegisterInfo *prim_reg_info = GetRegisterInfoAtIndex(prim_reg);
+ if (prim_reg_info == nullptr)
+ success = false;
+ else {
+ // Read the containing register if it hasn't already been read
+ if (!GetRegisterIsValid(prim_reg))
+ success = GetPrimordialRegister(prim_reg_info, gdb_comm);
+ }
+ }
+
+ if (success) {
+ // If we reach this point, all primordial register requests have
+ // succeeded. Validate this composite register.
+ SetRegisterIsValid(reg_info, true);
+ }
+ } else {
+ // Get each register individually
+ GetPrimordialRegister(reg_info, gdb_comm);
+ }
+
+ // Make sure we got a valid register value after reading it
+ if (!GetRegisterIsValid(reg))
+ return false;
+ }
+
+ if (&data != &m_reg_data) {
+ assert(m_reg_data.GetByteSize() >=
+ reg_info->byte_offset + reg_info->byte_size);
+ // If our register context and our register info disagree, which should
+ // never happen, don't read past the end of the buffer.
+ if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size)
+ return false;
+
+ // If we aren't extracting into our own buffer (which only happens when
+ // this function is called from ReadRegisterValue(uint32_t, Scalar&)) then
+ // we transfer bytes from our buffer into the data buffer that was passed
+ // in
+
+ data.SetByteOrder(m_reg_data.GetByteOrder());
+ data.SetData(m_reg_data, reg_info->byte_offset, reg_info->byte_size);
+ }
+ return true;
+}
+
+bool GDBRemoteRegisterContext::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ DataExtractor data;
+ if (value.GetData(data))
+ return WriteRegisterBytes(reg_info, data, 0);
+ return false;
+}
+
+// Helper function for GDBRemoteRegisterContext::WriteRegisterBytes().
+bool GDBRemoteRegisterContext::SetPrimordialRegister(
+ const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) {
+ StreamString packet;
+ StringExtractorGDBRemote response;
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ // Invalidate just this register
+ SetRegisterIsValid(reg, false);
+
+ return gdb_comm.WriteRegister(
+ m_thread.GetProtocolID(), reg_info->kinds[eRegisterKindProcessPlugin],
+ {m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size),
+ reg_info->byte_size});
+}
+
+bool GDBRemoteRegisterContext::WriteRegisterBytes(const RegisterInfo *reg_info,
+ DataExtractor &data,
+ uint32_t data_offset) {
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ assert(m_reg_data.GetByteSize() >=
+ reg_info->byte_offset + reg_info->byte_size);
+
+ // If our register context and our register info disagree, which should never
+ // happen, don't overwrite past the end of the buffer.
+ if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size)
+ return false;
+
+ // Grab a pointer to where we are going to put this register
+ uint8_t *dst = const_cast<uint8_t *>(
+ m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size));
+
+ if (dst == nullptr)
+ return false;
+
+ if (data.CopyByteOrderedData(data_offset, // src offset
+ reg_info->byte_size, // src length
+ dst, // dst
+ reg_info->byte_size, // dst length
+ m_reg_data.GetByteOrder())) // dst byte order
+ {
+ GDBRemoteClientBase::Lock lock(gdb_comm, false);
+ if (lock) {
+ if (m_read_all_at_once) {
+ // Invalidate all register values
+ InvalidateIfNeeded(true);
+
+ // Set all registers in one packet
+ if (gdb_comm.WriteAllRegisters(
+ m_thread.GetProtocolID(),
+ {m_reg_data.GetDataStart(), size_t(m_reg_data.GetByteSize())}))
+
+ {
+ SetAllRegisterValid(false);
+ return true;
+ }
+ } else {
+ bool success = true;
+
+ if (reg_info->value_regs) {
+ // This register is part of another register. In this case we read
+ // the actual register data for any "value_regs", and once all that
+ // data is read, we will have enough data in our register context
+ // bytes for the value of this register
+
+ // Invalidate this composite register first.
+
+ for (uint32_t idx = 0; success; ++idx) {
+ const uint32_t reg = reg_info->value_regs[idx];
+ if (reg == LLDB_INVALID_REGNUM)
+ break;
+ // We have a valid primordial register as our constituent. Grab the
+ // corresponding register info.
+ const RegisterInfo *value_reg_info = GetRegisterInfoAtIndex(reg);
+ if (value_reg_info == nullptr)
+ success = false;
+ else
+ success = SetPrimordialRegister(value_reg_info, gdb_comm);
+ }
+ } else {
+ // This is an actual register, write it
+ success = SetPrimordialRegister(reg_info, gdb_comm);
+ }
+
+ // Check if writing this register will invalidate any other register
+ // values? If so, invalidate them
+ if (reg_info->invalidate_regs) {
+ for (uint32_t idx = 0, reg = reg_info->invalidate_regs[0];
+ reg != LLDB_INVALID_REGNUM;
+ reg = reg_info->invalidate_regs[++idx]) {
+ SetRegisterIsValid(reg, false);
+ }
+ }
+
+ return success;
+ }
+ } else {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD |
+ GDBR_LOG_PACKETS));
+ if (log) {
+ if (log->GetVerbose()) {
+ StreamString strm;
+ gdb_comm.DumpHistory(strm);
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "write register for \"%s\":\n%s",
+ reg_info->name, strm.GetData());
+ } else
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "write register for \"%s\"",
+ reg_info->name);
+ }
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteRegisterContext::ReadAllRegisterValues(
+ RegisterCheckpoint &reg_checkpoint) {
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ uint32_t save_id = 0;
+ if (gdb_comm.SaveRegisterState(thread->GetProtocolID(), save_id)) {
+ reg_checkpoint.SetID(save_id);
+ reg_checkpoint.GetData().reset();
+ return true;
+ } else {
+ reg_checkpoint.SetID(0); // Invalid save ID is zero
+ return ReadAllRegisterValues(reg_checkpoint.GetData());
+ }
+}
+
+bool GDBRemoteRegisterContext::WriteAllRegisterValues(
+ const RegisterCheckpoint &reg_checkpoint) {
+ uint32_t save_id = reg_checkpoint.GetID();
+ if (save_id != 0) {
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ return gdb_comm.RestoreRegisterState(m_thread.GetProtocolID(), save_id);
+ } else {
+ return WriteAllRegisterValues(reg_checkpoint.GetData());
+ }
+}
+
+bool GDBRemoteRegisterContext::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ const bool use_g_packet =
+ !gdb_comm.AvoidGPackets((ProcessGDBRemote *)process);
+
+ GDBRemoteClientBase::Lock lock(gdb_comm, false);
+ if (lock) {
+ if (gdb_comm.SyncThreadState(m_thread.GetProtocolID()))
+ InvalidateAllRegisters();
+
+ if (use_g_packet &&
+ (data_sp = gdb_comm.ReadAllRegisters(m_thread.GetProtocolID())))
+ return true;
+
+ // We're going to read each register
+ // individually and store them as binary data in a buffer.
+ const RegisterInfo *reg_info;
+
+ for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex(i)) != nullptr;
+ i++) {
+ if (reg_info
+ ->value_regs) // skip registers that are slices of real registers
+ continue;
+ ReadRegisterBytes(reg_info, m_reg_data);
+ // ReadRegisterBytes saves the contents of the register in to the
+ // m_reg_data buffer
+ }
+ data_sp = std::make_shared<DataBufferHeap>(
+ m_reg_data.GetDataStart(), m_reg_info.GetRegisterDataByteSize());
+ return true;
+ } else {
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD |
+ GDBR_LOG_PACKETS));
+ if (log) {
+ if (log->GetVerbose()) {
+ StreamString strm;
+ gdb_comm.DumpHistory(strm);
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "read all registers:\n%s",
+ strm.GetData());
+ } else
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "read all registers");
+ }
+ }
+
+ data_sp.reset();
+ return false;
+}
+
+bool GDBRemoteRegisterContext::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (!data_sp || data_sp->GetBytes() == nullptr || data_sp->GetByteSize() == 0)
+ return false;
+
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ const bool use_g_packet =
+ !gdb_comm.AvoidGPackets((ProcessGDBRemote *)process);
+
+ GDBRemoteClientBase::Lock lock(gdb_comm, false);
+ if (lock) {
+ // The data_sp contains the G response packet.
+ if (use_g_packet) {
+ if (gdb_comm.WriteAllRegisters(
+ m_thread.GetProtocolID(),
+ {data_sp->GetBytes(), size_t(data_sp->GetByteSize())}))
+ return true;
+
+ uint32_t num_restored = 0;
+ // We need to manually go through all of the registers and restore them
+ // manually
+ DataExtractor restore_data(data_sp, m_reg_data.GetByteOrder(),
+ m_reg_data.GetAddressByteSize());
+
+ const RegisterInfo *reg_info;
+
+ // The g packet contents may either include the slice registers
+ // (registers defined in terms of other registers, e.g. eax is a subset
+ // of rax) or not. The slice registers should NOT be in the g packet,
+ // but some implementations may incorrectly include them.
+ //
+ // If the slice registers are included in the packet, we must step over
+ // the slice registers when parsing the packet -- relying on the
+ // RegisterInfo byte_offset field would be incorrect. If the slice
+ // registers are not included, then using the byte_offset values into the
+ // data buffer is the best way to find individual register values.
+
+ uint64_t size_including_slice_registers = 0;
+ uint64_t size_not_including_slice_registers = 0;
+ uint64_t size_by_highest_offset = 0;
+
+ for (uint32_t reg_idx = 0;
+ (reg_info = GetRegisterInfoAtIndex(reg_idx)) != nullptr; ++reg_idx) {
+ size_including_slice_registers += reg_info->byte_size;
+ if (reg_info->value_regs == nullptr)
+ size_not_including_slice_registers += reg_info->byte_size;
+ if (reg_info->byte_offset >= size_by_highest_offset)
+ size_by_highest_offset = reg_info->byte_offset + reg_info->byte_size;
+ }
+
+ bool use_byte_offset_into_buffer;
+ if (size_by_highest_offset == restore_data.GetByteSize()) {
+ // The size of the packet agrees with the highest offset: + size in the
+ // register file
+ use_byte_offset_into_buffer = true;
+ } else if (size_not_including_slice_registers ==
+ restore_data.GetByteSize()) {
+ // The size of the packet is the same as concatenating all of the
+ // registers sequentially, skipping the slice registers
+ use_byte_offset_into_buffer = true;
+ } else if (size_including_slice_registers == restore_data.GetByteSize()) {
+ // The slice registers are present in the packet (when they shouldn't
+ // be). Don't try to use the RegisterInfo byte_offset into the
+ // restore_data, it will point to the wrong place.
+ use_byte_offset_into_buffer = false;
+ } else {
+ // None of our expected sizes match the actual g packet data we're
+ // looking at. The most conservative approach here is to use the
+ // running total byte offset.
+ use_byte_offset_into_buffer = false;
+ }
+
+ // In case our register definitions don't include the correct offsets,
+ // keep track of the size of each reg & compute offset based on that.
+ uint32_t running_byte_offset = 0;
+ for (uint32_t reg_idx = 0;
+ (reg_info = GetRegisterInfoAtIndex(reg_idx)) != nullptr;
+ ++reg_idx, running_byte_offset += reg_info->byte_size) {
+ // Skip composite aka slice registers (e.g. eax is a slice of rax).
+ if (reg_info->value_regs)
+ continue;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ uint32_t register_offset;
+ if (use_byte_offset_into_buffer) {
+ register_offset = reg_info->byte_offset;
+ } else {
+ register_offset = running_byte_offset;
+ }
+
+ const uint32_t reg_byte_size = reg_info->byte_size;
+
+ const uint8_t *restore_src =
+ restore_data.PeekData(register_offset, reg_byte_size);
+ if (restore_src) {
+ SetRegisterIsValid(reg, false);
+ if (gdb_comm.WriteRegister(
+ m_thread.GetProtocolID(),
+ reg_info->kinds[eRegisterKindProcessPlugin],
+ {restore_src, reg_byte_size}))
+ ++num_restored;
+ }
+ }
+ return num_restored > 0;
+ } else {
+ // For the use_g_packet == false case, we're going to write each register
+ // individually. The data buffer is binary data in this case, instead of
+ // ascii characters.
+
+ bool arm64_debugserver = false;
+ if (m_thread.GetProcess().get()) {
+ const ArchSpec &arch =
+ m_thread.GetProcess()->GetTarget().GetArchitecture();
+ if (arch.IsValid() && arch.GetMachine() == llvm::Triple::aarch64 &&
+ arch.GetTriple().getVendor() == llvm::Triple::Apple &&
+ arch.GetTriple().getOS() == llvm::Triple::IOS) {
+ arm64_debugserver = true;
+ }
+ }
+ uint32_t num_restored = 0;
+ const RegisterInfo *reg_info;
+ for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex(i)) != nullptr;
+ i++) {
+ if (reg_info->value_regs) // skip registers that are slices of real
+ // registers
+ continue;
+ // Skip the fpsr and fpcr floating point status/control register
+ // writing to work around a bug in an older version of debugserver that
+ // would lead to register context corruption when writing fpsr/fpcr.
+ if (arm64_debugserver && (strcmp(reg_info->name, "fpsr") == 0 ||
+ strcmp(reg_info->name, "fpcr") == 0)) {
+ continue;
+ }
+
+ SetRegisterIsValid(reg_info, false);
+ if (gdb_comm.WriteRegister(m_thread.GetProtocolID(),
+ reg_info->kinds[eRegisterKindProcessPlugin],
+ {data_sp->GetBytes() + reg_info->byte_offset,
+ reg_info->byte_size}))
+ ++num_restored;
+ }
+ return num_restored > 0;
+ }
+ } else {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD |
+ GDBR_LOG_PACKETS));
+ if (log) {
+ if (log->GetVerbose()) {
+ StreamString strm;
+ gdb_comm.DumpHistory(strm);
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "write all registers:\n%s",
+ strm.GetData());
+ } else
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "write all registers");
+ }
+ }
+ return false;
+}
+
+uint32_t GDBRemoteRegisterContext::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ return m_reg_info.ConvertRegisterKindToRegisterNumber(kind, num);
+}
+
+void GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters(bool from_scratch) {
+ // For Advanced SIMD and VFP register mapping.
+ static uint32_t g_d0_regs[] = {26, 27, LLDB_INVALID_REGNUM}; // (s0, s1)
+ static uint32_t g_d1_regs[] = {28, 29, LLDB_INVALID_REGNUM}; // (s2, s3)
+ static uint32_t g_d2_regs[] = {30, 31, LLDB_INVALID_REGNUM}; // (s4, s5)
+ static uint32_t g_d3_regs[] = {32, 33, LLDB_INVALID_REGNUM}; // (s6, s7)
+ static uint32_t g_d4_regs[] = {34, 35, LLDB_INVALID_REGNUM}; // (s8, s9)
+ static uint32_t g_d5_regs[] = {36, 37, LLDB_INVALID_REGNUM}; // (s10, s11)
+ static uint32_t g_d6_regs[] = {38, 39, LLDB_INVALID_REGNUM}; // (s12, s13)
+ static uint32_t g_d7_regs[] = {40, 41, LLDB_INVALID_REGNUM}; // (s14, s15)
+ static uint32_t g_d8_regs[] = {42, 43, LLDB_INVALID_REGNUM}; // (s16, s17)
+ static uint32_t g_d9_regs[] = {44, 45, LLDB_INVALID_REGNUM}; // (s18, s19)
+ static uint32_t g_d10_regs[] = {46, 47, LLDB_INVALID_REGNUM}; // (s20, s21)
+ static uint32_t g_d11_regs[] = {48, 49, LLDB_INVALID_REGNUM}; // (s22, s23)
+ static uint32_t g_d12_regs[] = {50, 51, LLDB_INVALID_REGNUM}; // (s24, s25)
+ static uint32_t g_d13_regs[] = {52, 53, LLDB_INVALID_REGNUM}; // (s26, s27)
+ static uint32_t g_d14_regs[] = {54, 55, LLDB_INVALID_REGNUM}; // (s28, s29)
+ static uint32_t g_d15_regs[] = {56, 57, LLDB_INVALID_REGNUM}; // (s30, s31)
+ static uint32_t g_q0_regs[] = {
+ 26, 27, 28, 29, LLDB_INVALID_REGNUM}; // (d0, d1) -> (s0, s1, s2, s3)
+ static uint32_t g_q1_regs[] = {
+ 30, 31, 32, 33, LLDB_INVALID_REGNUM}; // (d2, d3) -> (s4, s5, s6, s7)
+ static uint32_t g_q2_regs[] = {
+ 34, 35, 36, 37, LLDB_INVALID_REGNUM}; // (d4, d5) -> (s8, s9, s10, s11)
+ static uint32_t g_q3_regs[] = {
+ 38, 39, 40, 41, LLDB_INVALID_REGNUM}; // (d6, d7) -> (s12, s13, s14, s15)
+ static uint32_t g_q4_regs[] = {
+ 42, 43, 44, 45, LLDB_INVALID_REGNUM}; // (d8, d9) -> (s16, s17, s18, s19)
+ static uint32_t g_q5_regs[] = {
+ 46, 47, 48, 49,
+ LLDB_INVALID_REGNUM}; // (d10, d11) -> (s20, s21, s22, s23)
+ static uint32_t g_q6_regs[] = {
+ 50, 51, 52, 53,
+ LLDB_INVALID_REGNUM}; // (d12, d13) -> (s24, s25, s26, s27)
+ static uint32_t g_q7_regs[] = {
+ 54, 55, 56, 57,
+ LLDB_INVALID_REGNUM}; // (d14, d15) -> (s28, s29, s30, s31)
+ static uint32_t g_q8_regs[] = {59, 60, LLDB_INVALID_REGNUM}; // (d16, d17)
+ static uint32_t g_q9_regs[] = {61, 62, LLDB_INVALID_REGNUM}; // (d18, d19)
+ static uint32_t g_q10_regs[] = {63, 64, LLDB_INVALID_REGNUM}; // (d20, d21)
+ static uint32_t g_q11_regs[] = {65, 66, LLDB_INVALID_REGNUM}; // (d22, d23)
+ static uint32_t g_q12_regs[] = {67, 68, LLDB_INVALID_REGNUM}; // (d24, d25)
+ static uint32_t g_q13_regs[] = {69, 70, LLDB_INVALID_REGNUM}; // (d26, d27)
+ static uint32_t g_q14_regs[] = {71, 72, LLDB_INVALID_REGNUM}; // (d28, d29)
+ static uint32_t g_q15_regs[] = {73, 74, LLDB_INVALID_REGNUM}; // (d30, d31)
+
+ // This is our array of composite registers, with each element coming from
+ // the above register mappings.
+ static uint32_t *g_composites[] = {
+ g_d0_regs, g_d1_regs, g_d2_regs, g_d3_regs, g_d4_regs, g_d5_regs,
+ g_d6_regs, g_d7_regs, g_d8_regs, g_d9_regs, g_d10_regs, g_d11_regs,
+ g_d12_regs, g_d13_regs, g_d14_regs, g_d15_regs, g_q0_regs, g_q1_regs,
+ g_q2_regs, g_q3_regs, g_q4_regs, g_q5_regs, g_q6_regs, g_q7_regs,
+ g_q8_regs, g_q9_regs, g_q10_regs, g_q11_regs, g_q12_regs, g_q13_regs,
+ g_q14_regs, g_q15_regs};
+
+ // clang-format off
+ static RegisterInfo g_register_infos[] = {
+// NAME ALT SZ OFF ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB VALUE REGS INVALIDATE REGS SIZE EXPR SIZE LEN
+// ====== ====== === === ============= ========== =================== =================== ====================== ============= ==== ========== =============== ========= ========
+ { "r0", "arg1", 4, 0, eEncodingUint, eFormatHex, { ehframe_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1,0, 0 }, nullptr, nullptr, nullptr, 0 },
+ { "r1", "arg2", 4, 0, eEncodingUint, eFormatHex, { ehframe_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2,1, 1 }, nullptr, nullptr, nullptr, 0 },
+ { "r2", "arg3", 4, 0, eEncodingUint, eFormatHex, { ehframe_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3,2, 2 }, nullptr, nullptr, nullptr, 0 },
+ { "r3", "arg4", 4, 0, eEncodingUint, eFormatHex, { ehframe_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4,3, 3 }, nullptr, nullptr, nullptr, 0 },
+ { "r4", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, 4, 4 }, nullptr, nullptr, nullptr, 0 },
+ { "r5", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, 5, 5 }, nullptr, nullptr, nullptr, 0 },
+ { "r6", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, 6, 6 }, nullptr, nullptr, nullptr, 0 },
+ { "r7", "fp", 4, 0, eEncodingUint, eFormatHex, { ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, 7, 7 }, nullptr, nullptr, nullptr, 0 },
+ { "r8", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, 8, 8 }, nullptr, nullptr, nullptr, 0 },
+ { "r9", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, 9, 9 }, nullptr, nullptr, nullptr, 0 },
+ { "r10", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, 10, 10 }, nullptr, nullptr, nullptr, 0 },
+ { "r11", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r11, dwarf_r11, LLDB_INVALID_REGNUM, 11, 11 }, nullptr, nullptr, nullptr, 0 },
+ { "r12", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, 12, 12 }, nullptr, nullptr, nullptr, 0 },
+ { "sp", "r13", 4, 0, eEncodingUint, eFormatHex, { ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, 13, 13 }, nullptr, nullptr, nullptr, 0 },
+ { "lr", "r14", 4, 0, eEncodingUint, eFormatHex, { ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, 14, 14 }, nullptr, nullptr, nullptr, 0 },
+ { "pc", "r15", 4, 0, eEncodingUint, eFormatHex, { ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, 15, 15 }, nullptr, nullptr, nullptr, 0 },
+ { "f0", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 16, 16 }, nullptr, nullptr, nullptr, 0 },
+ { "f1", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 17, 17 }, nullptr, nullptr, nullptr, 0 },
+ { "f2", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 18, 18 }, nullptr, nullptr, nullptr, 0 },
+ { "f3", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 19, 19 }, nullptr, nullptr, nullptr, 0 },
+ { "f4", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 20, 20 }, nullptr, nullptr, nullptr, 0 },
+ { "f5", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 21, 21 }, nullptr, nullptr, nullptr, 0 },
+ { "f6", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 22, 22 }, nullptr, nullptr, nullptr, 0 },
+ { "f7", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 23, 23 }, nullptr, nullptr, nullptr, 0 },
+ { "fps", nullptr, 4, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 24, 24 }, nullptr, nullptr, nullptr, 0 },
+ { "cpsr","flags", 4, 0, eEncodingUint, eFormatHex, { ehframe_cpsr, dwarf_cpsr, LLDB_INVALID_REGNUM, 25, 25 }, nullptr, nullptr, nullptr, 0 },
+ { "s0", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, 26, 26 }, nullptr, nullptr, nullptr, 0 },
+ { "s1", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, 27, 27 }, nullptr, nullptr, nullptr, 0 },
+ { "s2", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, 28, 28 }, nullptr, nullptr, nullptr, 0 },
+ { "s3", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, 29, 29 }, nullptr, nullptr, nullptr, 0 },
+ { "s4", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, 30, 30 }, nullptr, nullptr, nullptr, 0 },
+ { "s5", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, 31, 31 }, nullptr, nullptr, nullptr, 0 },
+ { "s6", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, 32, 32 }, nullptr, nullptr, nullptr, 0 },
+ { "s7", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, 33, 33 }, nullptr, nullptr, nullptr, 0 },
+ { "s8", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, 34, 34 }, nullptr, nullptr, nullptr, 0 },
+ { "s9", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, 35, 35 }, nullptr, nullptr, nullptr, 0 },
+ { "s10", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, 36, 36 }, nullptr, nullptr, nullptr, 0 },
+ { "s11", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, 37, 37 }, nullptr, nullptr, nullptr, 0 },
+ { "s12", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, 38, 38 }, nullptr, nullptr, nullptr, 0 },
+ { "s13", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, 39, 39 }, nullptr, nullptr, nullptr, 0 },
+ { "s14", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, 40, 40 }, nullptr, nullptr, nullptr, 0 },
+ { "s15", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, 41, 41 }, nullptr, nullptr, nullptr, 0 },
+ { "s16", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, 42, 42 }, nullptr, nullptr, nullptr, 0 },
+ { "s17", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, 43, 43 }, nullptr, nullptr, nullptr, 0 },
+ { "s18", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, 44, 44 }, nullptr, nullptr, nullptr, 0 },
+ { "s19", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, 45, 45 }, nullptr, nullptr, nullptr, 0 },
+ { "s20", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, 46, 46 }, nullptr, nullptr, nullptr, 0 },
+ { "s21", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, 47, 47 }, nullptr, nullptr, nullptr, 0 },
+ { "s22", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, 48, 48 }, nullptr, nullptr, nullptr, 0 },
+ { "s23", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, 49, 49 }, nullptr, nullptr, nullptr, 0 },
+ { "s24", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, 50, 50 }, nullptr, nullptr, nullptr, 0 },
+ { "s25", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, 51, 51 }, nullptr, nullptr, nullptr, 0 },
+ { "s26", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, 52, 52 }, nullptr, nullptr, nullptr, 0 },
+ { "s27", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, 53, 53 }, nullptr, nullptr, nullptr, 0 },
+ { "s28", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, 54, 54 }, nullptr, nullptr, nullptr, 0 },
+ { "s29", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, 55, 55 }, nullptr, nullptr, nullptr, 0 },
+ { "s30", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, 56, 56 }, nullptr, nullptr, nullptr, 0 },
+ { "s31", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, 57, 57 }, nullptr, nullptr, nullptr, 0 },
+ { "fpscr",nullptr, 4, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 58, 58 }, nullptr, nullptr, nullptr, 0 },
+ { "d16", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, 59, 59 }, nullptr, nullptr, nullptr, 0 },
+ { "d17", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, 60, 60 }, nullptr, nullptr, nullptr, 0 },
+ { "d18", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, 61, 61 }, nullptr, nullptr, nullptr, 0 },
+ { "d19", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, 62, 62 }, nullptr, nullptr, nullptr, 0 },
+ { "d20", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, 63, 63 }, nullptr, nullptr, nullptr, 0 },
+ { "d21", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, 64, 64 }, nullptr, nullptr, nullptr, 0 },
+ { "d22", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, 65, 65 }, nullptr, nullptr, nullptr, 0 },
+ { "d23", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, 66, 66 }, nullptr, nullptr, nullptr, 0 },
+ { "d24", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, 67, 67 }, nullptr, nullptr, nullptr, 0 },
+ { "d25", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, 68, 68 }, nullptr, nullptr, nullptr, 0 },
+ { "d26", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, 69, 69 }, nullptr, nullptr, nullptr, 0 },
+ { "d27", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, 70, 70 }, nullptr, nullptr, nullptr, 0 },
+ { "d28", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, 71, 71 }, nullptr, nullptr, nullptr, 0 },
+ { "d29", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, 72, 72 }, nullptr, nullptr, nullptr, 0 },
+ { "d30", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, 73, 73 }, nullptr, nullptr, nullptr, 0 },
+ { "d31", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, 74, 74 }, nullptr, nullptr, nullptr, 0 },
+ { "d0", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, 75, 75 }, g_d0_regs, nullptr, nullptr, 0 },
+ { "d1", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, 76, 76 }, g_d1_regs, nullptr, nullptr, 0 },
+ { "d2", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, 77, 77 }, g_d2_regs, nullptr, nullptr, 0 },
+ { "d3", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, 78, 78 }, g_d3_regs, nullptr, nullptr, 0 },
+ { "d4", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, 79, 79 }, g_d4_regs, nullptr, nullptr, 0 },
+ { "d5", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, 80, 80 }, g_d5_regs, nullptr, nullptr, 0 },
+ { "d6", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, 81, 81 }, g_d6_regs, nullptr, nullptr, 0 },
+ { "d7", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, 82, 82 }, g_d7_regs, nullptr, nullptr, 0 },
+ { "d8", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, 83, 83 }, g_d8_regs, nullptr, nullptr, 0 },
+ { "d9", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, 84, 84 }, g_d9_regs, nullptr, nullptr, 0 },
+ { "d10", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, 85, 85 }, g_d10_regs, nullptr, nullptr, 0 },
+ { "d11", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, 86, 86 }, g_d11_regs, nullptr, nullptr, 0 },
+ { "d12", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, 87, 87 }, g_d12_regs, nullptr, nullptr, 0 },
+ { "d13", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, 88, 88 }, g_d13_regs, nullptr, nullptr, 0 },
+ { "d14", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, 89, 89 }, g_d14_regs, nullptr, nullptr, 0 },
+ { "d15", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, 90, 90 }, g_d15_regs, nullptr, nullptr, 0 },
+ { "q0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q0, LLDB_INVALID_REGNUM, 91, 91 }, g_q0_regs, nullptr, nullptr, 0 },
+ { "q1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q1, LLDB_INVALID_REGNUM, 92, 92 }, g_q1_regs, nullptr, nullptr, 0 },
+ { "q2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q2, LLDB_INVALID_REGNUM, 93, 93 }, g_q2_regs, nullptr, nullptr, 0 },
+ { "q3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q3, LLDB_INVALID_REGNUM, 94, 94 }, g_q3_regs, nullptr, nullptr, 0 },
+ { "q4", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q4, LLDB_INVALID_REGNUM, 95, 95 }, g_q4_regs, nullptr, nullptr, 0 },
+ { "q5", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q5, LLDB_INVALID_REGNUM, 96, 96 }, g_q5_regs, nullptr, nullptr, 0 },
+ { "q6", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q6, LLDB_INVALID_REGNUM, 97, 97 }, g_q6_regs, nullptr, nullptr, 0 },
+ { "q7", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q7, LLDB_INVALID_REGNUM, 98, 98 }, g_q7_regs, nullptr, nullptr, 0 },
+ { "q8", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q8, LLDB_INVALID_REGNUM, 99, 99 }, g_q8_regs, nullptr, nullptr, 0 },
+ { "q9", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q9, LLDB_INVALID_REGNUM, 100, 100 }, g_q9_regs, nullptr, nullptr, 0 },
+ { "q10", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q10, LLDB_INVALID_REGNUM, 101, 101 }, g_q10_regs, nullptr, nullptr, 0 },
+ { "q11", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q11, LLDB_INVALID_REGNUM, 102, 102 }, g_q11_regs, nullptr, nullptr, 0 },
+ { "q12", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q12, LLDB_INVALID_REGNUM, 103, 103 }, g_q12_regs, nullptr, nullptr, 0 },
+ { "q13", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q13, LLDB_INVALID_REGNUM, 104, 104 }, g_q13_regs, nullptr, nullptr, 0 },
+ { "q14", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q14, LLDB_INVALID_REGNUM, 105, 105 }, g_q14_regs, nullptr, nullptr, 0 },
+ { "q15", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q15, LLDB_INVALID_REGNUM, 106, 106 }, g_q15_regs, nullptr, nullptr, 0 }
+ };
+ // clang-format on
+
+ static const uint32_t num_registers = llvm::array_lengthof(g_register_infos);
+ static ConstString gpr_reg_set("General Purpose Registers");
+ static ConstString sfp_reg_set("Software Floating Point Registers");
+ static ConstString vfp_reg_set("Floating Point Registers");
+ size_t i;
+ if (from_scratch) {
+ // Calculate the offsets of the registers
+ // Note that the layout of the "composite" registers (d0-d15 and q0-q15)
+ // which comes after the "primordial" registers is important. This enables
+ // us to calculate the offset of the composite register by using the offset
+ // of its first primordial register. For example, to calculate the offset
+ // of q0, use s0's offset.
+ if (g_register_infos[2].byte_offset == 0) {
+ uint32_t byte_offset = 0;
+ for (i = 0; i < num_registers; ++i) {
+ // For primordial registers, increment the byte_offset by the byte_size
+ // to arrive at the byte_offset for the next register. Otherwise, we
+ // have a composite register whose offset can be calculated by
+ // consulting the offset of its first primordial register.
+ if (!g_register_infos[i].value_regs) {
+ g_register_infos[i].byte_offset = byte_offset;
+ byte_offset += g_register_infos[i].byte_size;
+ } else {
+ const uint32_t first_primordial_reg =
+ g_register_infos[i].value_regs[0];
+ g_register_infos[i].byte_offset =
+ g_register_infos[first_primordial_reg].byte_offset;
+ }
+ }
+ }
+ for (i = 0; i < num_registers; ++i) {
+ ConstString name;
+ ConstString alt_name;
+ if (g_register_infos[i].name && g_register_infos[i].name[0])
+ name.SetCString(g_register_infos[i].name);
+ if (g_register_infos[i].alt_name && g_register_infos[i].alt_name[0])
+ alt_name.SetCString(g_register_infos[i].alt_name);
+
+ if (i <= 15 || i == 25)
+ AddRegister(g_register_infos[i], name, alt_name, gpr_reg_set);
+ else if (i <= 24)
+ AddRegister(g_register_infos[i], name, alt_name, sfp_reg_set);
+ else
+ AddRegister(g_register_infos[i], name, alt_name, vfp_reg_set);
+ }
+ } else {
+ // Add composite registers to our primordial registers, then.
+ const size_t num_composites = llvm::array_lengthof(g_composites);
+ const size_t num_dynamic_regs = GetNumRegisters();
+ const size_t num_common_regs = num_registers - num_composites;
+ RegisterInfo *g_comp_register_infos = g_register_infos + num_common_regs;
+
+ // First we need to validate that all registers that we already have match
+ // the non composite regs. If so, then we can add the registers, else we
+ // need to bail
+ bool match = true;
+ if (num_dynamic_regs == num_common_regs) {
+ for (i = 0; match && i < num_dynamic_regs; ++i) {
+ // Make sure all register names match
+ if (m_regs[i].name && g_register_infos[i].name) {
+ if (strcmp(m_regs[i].name, g_register_infos[i].name)) {
+ match = false;
+ break;
+ }
+ }
+
+ // Make sure all register byte sizes match
+ if (m_regs[i].byte_size != g_register_infos[i].byte_size) {
+ match = false;
+ break;
+ }
+ }
+ } else {
+ // Wrong number of registers.
+ match = false;
+ }
+ // If "match" is true, then we can add extra registers.
+ if (match) {
+ for (i = 0; i < num_composites; ++i) {
+ ConstString name;
+ ConstString alt_name;
+ const uint32_t first_primordial_reg =
+ g_comp_register_infos[i].value_regs[0];
+ const char *reg_name = g_register_infos[first_primordial_reg].name;
+ if (reg_name && reg_name[0]) {
+ for (uint32_t j = 0; j < num_dynamic_regs; ++j) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(j);
+ // Find a matching primordial register info entry.
+ if (reg_info && reg_info->name &&
+ ::strcasecmp(reg_info->name, reg_name) == 0) {
+ // The name matches the existing primordial entry. Find and
+ // assign the offset, and then add this composite register entry.
+ g_comp_register_infos[i].byte_offset = reg_info->byte_offset;
+ name.SetCString(g_comp_register_infos[i].name);
+ AddRegister(g_comp_register_infos[i], name, alt_name,
+ vfp_reg_set);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
new file mode 100644
index 000000000000..25e9b716f8cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
@@ -0,0 +1,132 @@
+//===-- GDBRemoteRegisterContext.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_GDBRemoteRegisterContext_h_
+#define lldb_GDBRemoteRegisterContext_h_
+
+#include <vector>
+
+#include "Plugins/Process/Utility/DynamicRegisterInfo.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private.h"
+
+#include "GDBRemoteCommunicationClient.h"
+
+class StringExtractor;
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class ThreadGDBRemote;
+class ProcessGDBRemote;
+
+class GDBRemoteDynamicRegisterInfo : public DynamicRegisterInfo {
+public:
+ GDBRemoteDynamicRegisterInfo() : DynamicRegisterInfo() {}
+
+ ~GDBRemoteDynamicRegisterInfo() override = default;
+
+ void HardcodeARMRegisters(bool from_scratch);
+};
+
+class GDBRemoteRegisterContext : public RegisterContext {
+public:
+ GDBRemoteRegisterContext(ThreadGDBRemote &thread, uint32_t concrete_frame_idx,
+ GDBRemoteDynamicRegisterInfo &reg_info,
+ bool read_all_at_once);
+
+ ~GDBRemoteRegisterContext() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) override;
+
+ bool WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool ReadAllRegisterValues(RegisterCheckpoint &reg_checkpoint) override;
+
+ bool
+ WriteAllRegisterValues(const RegisterCheckpoint &reg_checkpoint) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ friend class ThreadGDBRemote;
+
+ bool ReadRegisterBytes(const RegisterInfo *reg_info, DataExtractor &data);
+
+ bool WriteRegisterBytes(const RegisterInfo *reg_info, DataExtractor &data,
+ uint32_t data_offset);
+
+ bool PrivateSetRegisterValue(uint32_t reg, llvm::ArrayRef<uint8_t> data);
+
+ bool PrivateSetRegisterValue(uint32_t reg, uint64_t val);
+
+ void SetAllRegisterValid(bool b);
+
+ bool GetRegisterIsValid(uint32_t reg) const {
+#if defined(LLDB_CONFIGURATION_DEBUG)
+ assert(reg < m_reg_valid.size());
+#endif
+ if (reg < m_reg_valid.size())
+ return m_reg_valid[reg];
+ return false;
+ }
+
+ void SetRegisterIsValid(const RegisterInfo *reg_info, bool valid) {
+ if (reg_info)
+ return SetRegisterIsValid(reg_info->kinds[lldb::eRegisterKindLLDB],
+ valid);
+ }
+
+ void SetRegisterIsValid(uint32_t reg, bool valid) {
+#if defined(LLDB_CONFIGURATION_DEBUG)
+ assert(reg < m_reg_valid.size());
+#endif
+ if (reg < m_reg_valid.size())
+ m_reg_valid[reg] = valid;
+ }
+
+ GDBRemoteDynamicRegisterInfo &m_reg_info;
+ std::vector<bool> m_reg_valid;
+ DataExtractor m_reg_data;
+ bool m_read_all_at_once;
+
+private:
+ // Helper function for ReadRegisterBytes().
+ bool GetPrimordialRegister(const RegisterInfo *reg_info,
+ GDBRemoteCommunicationClient &gdb_comm);
+ // Helper function for WriteRegisterBytes().
+ bool SetPrimordialRegister(const RegisterInfo *reg_info,
+ GDBRemoteCommunicationClient &gdb_comm);
+
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteRegisterContext);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // lldb_GDBRemoteRegisterContext_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
new file mode 100644
index 000000000000..915e301ecc30
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -0,0 +1,5486 @@
+//===-- ProcessGDBRemote.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#ifndef LLDB_DISABLE_POSIX
+#include <netinet/in.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <algorithm>
+#include <csignal>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <sstream>
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/Value.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Host/XML.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+#include "lldb/Interpreter/OptionGroupUInt64.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Symbol/LocateSymbolFile.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetList.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/CleanUp.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+#include "GDBRemoteRegisterContext.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/Process/Utility/GDBRemoteSignals.h"
+#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
+#include "Plugins/Process/Utility/StopInfoMachException.h"
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "ThreadGDBRemote.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUGSERVER_BASENAME "debugserver"
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+namespace lldb {
+// Provide a function that can easily dump the packet history if we know a
+// ProcessGDBRemote * value (which we can get from logs or from debugging). We
+// need the function in the lldb namespace so it makes it into the final
+// executable since the LLDB shared library only exports stuff in the lldb
+// namespace. This allows you to attach with a debugger and call this function
+// and get the packet history dumped to a file.
+void DumpProcessGDBRemotePacketHistory(void *p, const char *path) {
+ StreamFile strm;
+ Status error = FileSystem::Instance().Open(strm.GetFile(), FileSpec(path),
+ File::eOpenOptionWrite |
+ File::eOpenOptionCanCreate);
+ if (error.Success())
+ ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory(strm);
+}
+} // namespace lldb
+
+namespace {
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"packet-timeout",
+ OptionValue::eTypeUInt64,
+ true,
+ 5
+#if defined(__has_feature)
+#if __has_feature(address_sanitizer)
+ * 2
+#endif
+#endif
+ ,
+ nullptr,
+ {},
+ "Specify the default packet timeout in seconds."},
+ {"target-definition-file",
+ OptionValue::eTypeFileSpec,
+ true,
+ 0,
+ nullptr,
+ {},
+ "The file that provides the description for remote target registers."},
+ {"use-libraries-svr4",
+ OptionValue::eTypeBoolean,
+ true,
+ false,
+ nullptr,
+ {},
+ "If true, the libraries-svr4 feature will be used to get a hold of the "
+ "process's loaded modules."}};
+
+enum {
+ ePropertyPacketTimeout,
+ ePropertyTargetDefinitionFile,
+ ePropertyUseSVR4
+};
+
+class PluginProperties : public Properties {
+public:
+ static ConstString GetSettingName() {
+ return ProcessGDBRemote::GetPluginNameStatic();
+ }
+
+ PluginProperties() : Properties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_properties);
+ }
+
+ ~PluginProperties() override {}
+
+ uint64_t GetPacketTimeout() {
+ const uint32_t idx = ePropertyPacketTimeout;
+ return m_collection_sp->GetPropertyAtIndexAsUInt64(
+ nullptr, idx, g_properties[idx].default_uint_value);
+ }
+
+ bool SetPacketTimeout(uint64_t timeout) {
+ const uint32_t idx = ePropertyPacketTimeout;
+ return m_collection_sp->SetPropertyAtIndexAsUInt64(nullptr, idx, timeout);
+ }
+
+ FileSpec GetTargetDefinitionFile() const {
+ const uint32_t idx = ePropertyTargetDefinitionFile;
+ return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx);
+ }
+
+ bool GetUseSVR4() const {
+ const uint32_t idx = ePropertyUseSVR4;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+ }
+};
+
+typedef std::shared_ptr<PluginProperties> ProcessKDPPropertiesSP;
+
+static const ProcessKDPPropertiesSP &GetGlobalPluginProperties() {
+ static ProcessKDPPropertiesSP g_settings_sp;
+ if (!g_settings_sp)
+ g_settings_sp = std::make_shared<PluginProperties>();
+ return g_settings_sp;
+}
+
+class ProcessGDBRemoteProvider
+ : public repro::Provider<ProcessGDBRemoteProvider> {
+public:
+ struct Info {
+ static const char *name;
+ static const char *file;
+ };
+
+ ProcessGDBRemoteProvider(const FileSpec &directory) : Provider(directory) {
+ }
+
+ raw_ostream *GetHistoryStream() {
+ FileSpec history_file = GetRoot().CopyByAppendingPathComponent(Info::file);
+
+ std::error_code EC;
+ m_stream_up = llvm::make_unique<raw_fd_ostream>(history_file.GetPath(), EC,
+ sys::fs::OpenFlags::F_Text);
+ return m_stream_up.get();
+ }
+
+ void SetCallback(std::function<void()> callback) {
+ m_callback = std::move(callback);
+ }
+
+ void Keep() override { m_callback(); }
+
+ void Discard() override { m_callback(); }
+
+ static char ID;
+
+private:
+ std::function<void()> m_callback;
+ std::unique_ptr<raw_fd_ostream> m_stream_up;
+};
+
+char ProcessGDBRemoteProvider::ID = 0;
+const char *ProcessGDBRemoteProvider::Info::name = "gdb-remote";
+const char *ProcessGDBRemoteProvider::Info::file = "gdb-remote.yaml";
+
+} // namespace
+
+// TODO Randomly assigning a port is unsafe. We should get an unused
+// ephemeral port from the kernel and make sure we reserve it before passing it
+// to debugserver.
+
+#if defined(__APPLE__)
+#define LOW_PORT (IPPORT_RESERVED)
+#define HIGH_PORT (IPPORT_HIFIRSTAUTO)
+#else
+#define LOW_PORT (1024u)
+#define HIGH_PORT (49151u)
+#endif
+
+#if defined(__APPLE__) && \
+ (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
+static bool rand_initialized = false;
+
+static inline uint16_t get_random_port() {
+ if (!rand_initialized) {
+ time_t seed = time(NULL);
+
+ rand_initialized = true;
+ srand(seed);
+ }
+ return (rand() % (HIGH_PORT - LOW_PORT)) + LOW_PORT;
+}
+#endif
+
+ConstString ProcessGDBRemote::GetPluginNameStatic() {
+ static ConstString g_name("gdb-remote");
+ return g_name;
+}
+
+const char *ProcessGDBRemote::GetPluginDescriptionStatic() {
+ return "GDB Remote protocol based debugging plug-in.";
+}
+
+void ProcessGDBRemote::Terminate() {
+ PluginManager::UnregisterPlugin(ProcessGDBRemote::CreateInstance);
+}
+
+lldb::ProcessSP
+ProcessGDBRemote::CreateInstance(lldb::TargetSP target_sp,
+ ListenerSP listener_sp,
+ const FileSpec *crash_file_path) {
+ lldb::ProcessSP process_sp;
+ if (crash_file_path == nullptr)
+ process_sp = std::make_shared<ProcessGDBRemote>(target_sp, listener_sp);
+ return process_sp;
+}
+
+bool ProcessGDBRemote::CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) {
+ if (plugin_specified_by_name)
+ return true;
+
+ // For now we are just making sure the file exists for a given module
+ Module *exe_module = target_sp->GetExecutableModulePointer();
+ if (exe_module) {
+ ObjectFile *exe_objfile = exe_module->GetObjectFile();
+ // We can't debug core files...
+ switch (exe_objfile->GetType()) {
+ case ObjectFile::eTypeInvalid:
+ case ObjectFile::eTypeCoreFile:
+ case ObjectFile::eTypeDebugInfo:
+ case ObjectFile::eTypeObjectFile:
+ case ObjectFile::eTypeSharedLibrary:
+ case ObjectFile::eTypeStubLibrary:
+ case ObjectFile::eTypeJIT:
+ return false;
+ case ObjectFile::eTypeExecutable:
+ case ObjectFile::eTypeDynamicLinker:
+ case ObjectFile::eTypeUnknown:
+ break;
+ }
+ return FileSystem::Instance().Exists(exe_module->GetFileSpec());
+ }
+ // However, if there is no executable module, we return true since we might
+ // be preparing to attach.
+ return true;
+}
+
+// ProcessGDBRemote constructor
+ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp,
+ ListenerSP listener_sp)
+ : Process(target_sp, listener_sp),
+ m_debugserver_pid(LLDB_INVALID_PROCESS_ID), m_last_stop_packet_mutex(),
+ m_register_info(),
+ m_async_broadcaster(nullptr, "lldb.process.gdb-remote.async-broadcaster"),
+ m_async_listener_sp(
+ Listener::MakeListener("lldb.process.gdb-remote.async-listener")),
+ m_async_thread_state_mutex(), m_thread_ids(), m_thread_pcs(),
+ m_jstopinfo_sp(), m_jthreadsinfo_sp(), m_continue_c_tids(),
+ m_continue_C_tids(), m_continue_s_tids(), m_continue_S_tids(),
+ m_max_memory_size(0), m_remote_stub_max_memory_size(0),
+ m_addr_to_mmap_size(), m_thread_create_bp_sp(),
+ m_waiting_for_attach(false), m_destroy_tried_resuming(false),
+ m_command_sp(), m_breakpoint_pc_offset(0),
+ m_initial_tid(LLDB_INVALID_THREAD_ID), m_replay_mode(false),
+ m_allow_flash_writes(false), m_erased_flash_ranges() {
+ m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
+ "async thread should exit");
+ m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
+ "async thread continue");
+ m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadDidExit,
+ "async thread did exit");
+
+ if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
+ ProcessGDBRemoteProvider &provider =
+ g->GetOrCreate<ProcessGDBRemoteProvider>();
+ // Set the history stream to the stream owned by the provider.
+ m_gdb_comm.SetHistoryStream(provider.GetHistoryStream());
+ // Make sure to clear the stream again when we're finished.
+ provider.SetCallback([&]() { m_gdb_comm.SetHistoryStream(nullptr); });
+ }
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_ASYNC));
+
+ const uint32_t async_event_mask =
+ eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit;
+
+ if (m_async_listener_sp->StartListeningForEvents(
+ &m_async_broadcaster, async_event_mask) != async_event_mask) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s failed to listen for "
+ "m_async_broadcaster events",
+ __FUNCTION__);
+ }
+
+ const uint32_t gdb_event_mask =
+ Communication::eBroadcastBitReadThreadDidExit |
+ GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify;
+ if (m_async_listener_sp->StartListeningForEvents(
+ &m_gdb_comm, gdb_event_mask) != gdb_event_mask) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s failed to listen for m_gdb_comm events",
+ __FUNCTION__);
+ }
+
+ const uint64_t timeout_seconds =
+ GetGlobalPluginProperties()->GetPacketTimeout();
+ if (timeout_seconds > 0)
+ m_gdb_comm.SetPacketTimeout(std::chrono::seconds(timeout_seconds));
+}
+
+// Destructor
+ProcessGDBRemote::~ProcessGDBRemote() {
+ // m_mach_process.UnregisterNotificationCallbacks (this);
+ Clear();
+ // We need to call finalize on the process before destroying ourselves to
+ // make sure all of the broadcaster cleanup goes as planned. If we destruct
+ // this class, then Process::~Process() might have problems trying to fully
+ // destroy the broadcaster.
+ Finalize();
+
+ // The general Finalize is going to try to destroy the process and that
+ // SHOULD shut down the async thread. However, if we don't kill it it will
+ // get stranded and its connection will go away so when it wakes up it will
+ // crash. So kill it for sure here.
+ StopAsyncThread();
+ KillDebugserverProcess();
+}
+
+// PluginInterface
+ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; }
+
+bool ProcessGDBRemote::ParsePythonTargetDefinition(
+ const FileSpec &target_definition_fspec) {
+ ScriptInterpreter *interpreter =
+ GetTarget().GetDebugger().GetScriptInterpreter();
+ Status error;
+ StructuredData::ObjectSP module_object_sp(
+ interpreter->LoadPluginModule(target_definition_fspec, error));
+ if (module_object_sp) {
+ StructuredData::DictionarySP target_definition_sp(
+ interpreter->GetDynamicSettings(module_object_sp, &GetTarget(),
+ "gdb-server-target-definition", error));
+
+ if (target_definition_sp) {
+ StructuredData::ObjectSP target_object(
+ target_definition_sp->GetValueForKey("host-info"));
+ if (target_object) {
+ if (auto host_info_dict = target_object->GetAsDictionary()) {
+ StructuredData::ObjectSP triple_value =
+ host_info_dict->GetValueForKey("triple");
+ if (auto triple_string_value = triple_value->GetAsString()) {
+ std::string triple_string = triple_string_value->GetValue();
+ ArchSpec host_arch(triple_string.c_str());
+ if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) {
+ GetTarget().SetArchitecture(host_arch);
+ }
+ }
+ }
+ }
+ m_breakpoint_pc_offset = 0;
+ StructuredData::ObjectSP breakpoint_pc_offset_value =
+ target_definition_sp->GetValueForKey("breakpoint-pc-offset");
+ if (breakpoint_pc_offset_value) {
+ if (auto breakpoint_pc_int_value =
+ breakpoint_pc_offset_value->GetAsInteger())
+ m_breakpoint_pc_offset = breakpoint_pc_int_value->GetValue();
+ }
+
+ if (m_register_info.SetRegisterInfo(*target_definition_sp,
+ GetTarget().GetArchitecture()) > 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// If the remote stub didn't give us eh_frame or DWARF register numbers for a
+// register, see if the ABI can provide them.
+// DWARF and eh_frame register numbers are defined as a part of the ABI.
+static void AugmentRegisterInfoViaABI(RegisterInfo &reg_info,
+ ConstString reg_name, ABISP abi_sp) {
+ if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM ||
+ reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM) {
+ if (abi_sp) {
+ RegisterInfo abi_reg_info;
+ if (abi_sp->GetRegisterInfoByName(reg_name, abi_reg_info)) {
+ if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM &&
+ abi_reg_info.kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM) {
+ reg_info.kinds[eRegisterKindEHFrame] =
+ abi_reg_info.kinds[eRegisterKindEHFrame];
+ }
+ if (reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM &&
+ abi_reg_info.kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM) {
+ reg_info.kinds[eRegisterKindDWARF] =
+ abi_reg_info.kinds[eRegisterKindDWARF];
+ }
+ if (reg_info.kinds[eRegisterKindGeneric] == LLDB_INVALID_REGNUM &&
+ abi_reg_info.kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) {
+ reg_info.kinds[eRegisterKindGeneric] =
+ abi_reg_info.kinds[eRegisterKindGeneric];
+ }
+ }
+ }
+ }
+}
+
+static size_t SplitCommaSeparatedRegisterNumberString(
+ const llvm::StringRef &comma_separated_regiter_numbers,
+ std::vector<uint32_t> &regnums, int base) {
+ regnums.clear();
+ std::pair<llvm::StringRef, llvm::StringRef> value_pair;
+ value_pair.second = comma_separated_regiter_numbers;
+ do {
+ value_pair = value_pair.second.split(',');
+ if (!value_pair.first.empty()) {
+ uint32_t reg = StringConvert::ToUInt32(value_pair.first.str().c_str(),
+ LLDB_INVALID_REGNUM, base);
+ if (reg != LLDB_INVALID_REGNUM)
+ regnums.push_back(reg);
+ }
+ } while (!value_pair.second.empty());
+ return regnums.size();
+}
+
+void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) {
+ if (!force && m_register_info.GetNumRegisters() > 0)
+ return;
+
+ m_register_info.Clear();
+
+ // Check if qHostInfo specified a specific packet timeout for this
+ // connection. If so then lets update our setting so the user knows what the
+ // timeout is and can see it.
+ const auto host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout();
+ if (host_packet_timeout > std::chrono::seconds(0)) {
+ GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout.count());
+ }
+
+ // Register info search order:
+ // 1 - Use the target definition python file if one is specified.
+ // 2 - If the target definition doesn't have any of the info from the
+ // target.xml (registers) then proceed to read the target.xml.
+ // 3 - Fall back on the qRegisterInfo packets.
+
+ FileSpec target_definition_fspec =
+ GetGlobalPluginProperties()->GetTargetDefinitionFile();
+ if (!FileSystem::Instance().Exists(target_definition_fspec)) {
+ // If the filename doesn't exist, it may be a ~ not having been expanded -
+ // try to resolve it.
+ FileSystem::Instance().Resolve(target_definition_fspec);
+ }
+ if (target_definition_fspec) {
+ // See if we can get register definitions from a python file
+ if (ParsePythonTargetDefinition(target_definition_fspec)) {
+ return;
+ } else {
+ StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream();
+ stream_sp->Printf("ERROR: target description file %s failed to parse.\n",
+ target_definition_fspec.GetPath().c_str());
+ }
+ }
+
+ const ArchSpec &target_arch = GetTarget().GetArchitecture();
+ const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture();
+ const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture();
+
+ // Use the process' architecture instead of the host arch, if available
+ ArchSpec arch_to_use;
+ if (remote_process_arch.IsValid())
+ arch_to_use = remote_process_arch;
+ else
+ arch_to_use = remote_host_arch;
+
+ if (!arch_to_use.IsValid())
+ arch_to_use = target_arch;
+
+ if (GetGDBServerRegisterInfo(arch_to_use))
+ return;
+
+ char packet[128];
+ uint32_t reg_offset = 0;
+ uint32_t reg_num = 0;
+ for (StringExtractorGDBRemote::ResponseType response_type =
+ StringExtractorGDBRemote::eResponse;
+ response_type == StringExtractorGDBRemote::eResponse; ++reg_num) {
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qRegisterInfo%x", reg_num);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, false) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ response_type = response.GetResponseType();
+ if (response_type == StringExtractorGDBRemote::eResponse) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ ConstString reg_name;
+ ConstString alt_name;
+ ConstString set_name;
+ std::vector<uint32_t> value_regs;
+ std::vector<uint32_t> invalidate_regs;
+ std::vector<uint8_t> dwarf_opcode_bytes;
+ RegisterInfo reg_info = {
+ nullptr, // Name
+ nullptr, // Alt name
+ 0, // byte size
+ reg_offset, // offset
+ eEncodingUint, // encoding
+ eFormatHex, // format
+ {
+ LLDB_INVALID_REGNUM, // eh_frame reg num
+ LLDB_INVALID_REGNUM, // DWARF reg num
+ LLDB_INVALID_REGNUM, // generic reg num
+ reg_num, // process plugin reg num
+ reg_num // native register number
+ },
+ nullptr,
+ nullptr,
+ nullptr, // Dwarf expression opcode bytes pointer
+ 0 // Dwarf expression opcode bytes length
+ };
+
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("name")) {
+ reg_name.SetString(value);
+ } else if (name.equals("alt-name")) {
+ alt_name.SetString(value);
+ } else if (name.equals("bitsize")) {
+ value.getAsInteger(0, reg_info.byte_size);
+ reg_info.byte_size /= CHAR_BIT;
+ } else if (name.equals("offset")) {
+ if (value.getAsInteger(0, reg_offset))
+ reg_offset = UINT32_MAX;
+ } else if (name.equals("encoding")) {
+ const Encoding encoding = Args::StringToEncoding(value);
+ if (encoding != eEncodingInvalid)
+ reg_info.encoding = encoding;
+ } else if (name.equals("format")) {
+ Format format = eFormatInvalid;
+ if (OptionArgParser::ToFormat(value.str().c_str(), format, nullptr)
+ .Success())
+ reg_info.format = format;
+ else {
+ reg_info.format =
+ llvm::StringSwitch<Format>(value)
+ .Case("binary", eFormatBinary)
+ .Case("decimal", eFormatDecimal)
+ .Case("hex", eFormatHex)
+ .Case("float", eFormatFloat)
+ .Case("vector-sint8", eFormatVectorOfSInt8)
+ .Case("vector-uint8", eFormatVectorOfUInt8)
+ .Case("vector-sint16", eFormatVectorOfSInt16)
+ .Case("vector-uint16", eFormatVectorOfUInt16)
+ .Case("vector-sint32", eFormatVectorOfSInt32)
+ .Case("vector-uint32", eFormatVectorOfUInt32)
+ .Case("vector-float32", eFormatVectorOfFloat32)
+ .Case("vector-uint64", eFormatVectorOfUInt64)
+ .Case("vector-uint128", eFormatVectorOfUInt128)
+ .Default(eFormatInvalid);
+ }
+ } else if (name.equals("set")) {
+ set_name.SetString(value);
+ } else if (name.equals("gcc") || name.equals("ehframe")) {
+ if (value.getAsInteger(0, reg_info.kinds[eRegisterKindEHFrame]))
+ reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM;
+ } else if (name.equals("dwarf")) {
+ if (value.getAsInteger(0, reg_info.kinds[eRegisterKindDWARF]))
+ reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM;
+ } else if (name.equals("generic")) {
+ reg_info.kinds[eRegisterKindGeneric] =
+ Args::StringToGenericRegister(value);
+ } else if (name.equals("container-regs")) {
+ SplitCommaSeparatedRegisterNumberString(value, value_regs, 16);
+ } else if (name.equals("invalidate-regs")) {
+ SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 16);
+ } else if (name.equals("dynamic_size_dwarf_expr_bytes")) {
+ size_t dwarf_opcode_len = value.size() / 2;
+ assert(dwarf_opcode_len > 0);
+
+ dwarf_opcode_bytes.resize(dwarf_opcode_len);
+ reg_info.dynamic_size_dwarf_len = dwarf_opcode_len;
+
+ StringExtractor opcode_extractor(value);
+ uint32_t ret_val =
+ opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes);
+ assert(dwarf_opcode_len == ret_val);
+ UNUSED_IF_ASSERT_DISABLED(ret_val);
+ reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data();
+ }
+ }
+
+ reg_info.byte_offset = reg_offset;
+ assert(reg_info.byte_size != 0);
+ reg_offset += reg_info.byte_size;
+ if (!value_regs.empty()) {
+ value_regs.push_back(LLDB_INVALID_REGNUM);
+ reg_info.value_regs = value_regs.data();
+ }
+ if (!invalidate_regs.empty()) {
+ invalidate_regs.push_back(LLDB_INVALID_REGNUM);
+ reg_info.invalidate_regs = invalidate_regs.data();
+ }
+
+ // We have to make a temporary ABI here, and not use the GetABI because
+ // this code gets called in DidAttach, when the target architecture
+ // (and consequently the ABI we'll get from the process) may be wrong.
+ ABISP abi_to_use = ABI::FindPlugin(shared_from_this(), arch_to_use);
+
+ AugmentRegisterInfoViaABI(reg_info, reg_name, abi_to_use);
+
+ m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name);
+ } else {
+ break; // ensure exit before reg_num is incremented
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (m_register_info.GetNumRegisters() > 0) {
+ m_register_info.Finalize(GetTarget().GetArchitecture());
+ return;
+ }
+
+ // We didn't get anything if the accumulated reg_num is zero. See if we are
+ // debugging ARM and fill with a hard coded register set until we can get an
+ // updated debugserver down on the devices. On the other hand, if the
+ // accumulated reg_num is positive, see if we can add composite registers to
+ // the existing primordial ones.
+ bool from_scratch = (m_register_info.GetNumRegisters() == 0);
+
+ if (!target_arch.IsValid()) {
+ if (arch_to_use.IsValid() &&
+ (arch_to_use.GetMachine() == llvm::Triple::arm ||
+ arch_to_use.GetMachine() == llvm::Triple::thumb) &&
+ arch_to_use.GetTriple().getVendor() == llvm::Triple::Apple)
+ m_register_info.HardcodeARMRegisters(from_scratch);
+ } else if (target_arch.GetMachine() == llvm::Triple::arm ||
+ target_arch.GetMachine() == llvm::Triple::thumb) {
+ m_register_info.HardcodeARMRegisters(from_scratch);
+ }
+
+ // At this point, we can finalize our register info.
+ m_register_info.Finalize(GetTarget().GetArchitecture());
+}
+
+Status ProcessGDBRemote::WillLaunch(lldb_private::Module *module) {
+ return WillLaunchOrAttach();
+}
+
+Status ProcessGDBRemote::WillAttachToProcessWithID(lldb::pid_t pid) {
+ return WillLaunchOrAttach();
+}
+
+Status ProcessGDBRemote::WillAttachToProcessWithName(const char *process_name,
+ bool wait_for_launch) {
+ return WillLaunchOrAttach();
+}
+
+Status ProcessGDBRemote::DoConnectRemote(Stream *strm,
+ llvm::StringRef remote_url) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ Status error(WillLaunchOrAttach());
+
+ if (error.Fail())
+ return error;
+
+ error = ConnectToDebugserver(remote_url);
+
+ if (error.Fail())
+ return error;
+ StartAsyncThread();
+
+ lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID();
+ if (pid == LLDB_INVALID_PROCESS_ID) {
+ // We don't have a valid process ID, so note that we are connected and
+ // could now request to launch or attach, or get remote process listings...
+ SetPrivateState(eStateConnected);
+ } else {
+ // We have a valid process
+ SetID(pid);
+ GetThreadList();
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.GetStopReply(response)) {
+ SetLastStopPacket(response);
+
+ // '?' Packets must be handled differently in non-stop mode
+ if (GetTarget().GetNonStopModeEnabled())
+ HandleStopReplySequence();
+
+ Target &target = GetTarget();
+ if (!target.GetArchitecture().IsValid()) {
+ if (m_gdb_comm.GetProcessArchitecture().IsValid()) {
+ target.SetArchitecture(m_gdb_comm.GetProcessArchitecture());
+ } else {
+ if (m_gdb_comm.GetHostArchitecture().IsValid()) {
+ target.SetArchitecture(m_gdb_comm.GetHostArchitecture());
+ }
+ }
+ }
+
+ const StateType state = SetThreadStopInfo(response);
+ if (state != eStateInvalid) {
+ SetPrivateState(state);
+ } else
+ error.SetErrorStringWithFormat(
+ "Process %" PRIu64 " was reported after connecting to "
+ "'%s', but state was not stopped: %s",
+ pid, remote_url.str().c_str(), StateAsCString(state));
+ } else
+ error.SetErrorStringWithFormat("Process %" PRIu64
+ " was reported after connecting to '%s', "
+ "but no stop reply packet was received",
+ pid, remote_url.str().c_str());
+ }
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s pid %" PRIu64
+ ": normalizing target architecture initial triple: %s "
+ "(GetTarget().GetArchitecture().IsValid() %s, "
+ "m_gdb_comm.GetHostArchitecture().IsValid(): %s)",
+ __FUNCTION__, GetID(),
+ GetTarget().GetArchitecture().GetTriple().getTriple().c_str(),
+ GetTarget().GetArchitecture().IsValid() ? "true" : "false",
+ m_gdb_comm.GetHostArchitecture().IsValid() ? "true" : "false");
+
+ if (error.Success() && !GetTarget().GetArchitecture().IsValid() &&
+ m_gdb_comm.GetHostArchitecture().IsValid()) {
+ // Prefer the *process'* architecture over that of the *host*, if
+ // available.
+ if (m_gdb_comm.GetProcessArchitecture().IsValid())
+ GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture());
+ else
+ GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture());
+ }
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s pid %" PRIu64
+ ": normalized target architecture triple: %s",
+ __FUNCTION__, GetID(),
+ GetTarget().GetArchitecture().GetTriple().getTriple().c_str());
+
+ if (error.Success()) {
+ PlatformSP platform_sp = GetTarget().GetPlatform();
+ if (platform_sp && platform_sp->IsConnected())
+ SetUnixSignals(platform_sp->GetUnixSignals());
+ else
+ SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture()));
+ }
+
+ return error;
+}
+
+Status ProcessGDBRemote::WillLaunchOrAttach() {
+ Status error;
+ m_stdio_communication.Clear();
+ return error;
+}
+
+// Process Control
+Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module,
+ ProcessLaunchInfo &launch_info) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ Status error;
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s() entered", __FUNCTION__);
+
+ uint32_t launch_flags = launch_info.GetFlags().Get();
+ FileSpec stdin_file_spec{};
+ FileSpec stdout_file_spec{};
+ FileSpec stderr_file_spec{};
+ FileSpec working_dir = launch_info.GetWorkingDirectory();
+
+ const FileAction *file_action;
+ file_action = launch_info.GetFileActionForFD(STDIN_FILENO);
+ if (file_action) {
+ if (file_action->GetAction() == FileAction::eFileActionOpen)
+ stdin_file_spec = file_action->GetFileSpec();
+ }
+ file_action = launch_info.GetFileActionForFD(STDOUT_FILENO);
+ if (file_action) {
+ if (file_action->GetAction() == FileAction::eFileActionOpen)
+ stdout_file_spec = file_action->GetFileSpec();
+ }
+ file_action = launch_info.GetFileActionForFD(STDERR_FILENO);
+ if (file_action) {
+ if (file_action->GetAction() == FileAction::eFileActionOpen)
+ stderr_file_spec = file_action->GetFileSpec();
+ }
+
+ if (log) {
+ if (stdin_file_spec || stdout_file_spec || stderr_file_spec)
+ log->Printf("ProcessGDBRemote::%s provided with STDIO paths via "
+ "launch_info: stdin=%s, stdout=%s, stderr=%s",
+ __FUNCTION__,
+ stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
+ stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
+ stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
+ else
+ log->Printf("ProcessGDBRemote::%s no STDIO paths given via launch_info",
+ __FUNCTION__);
+ }
+
+ const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0;
+ if (stdin_file_spec || disable_stdio) {
+ // the inferior will be reading stdin from the specified file or stdio is
+ // completely disabled
+ m_stdin_forward = false;
+ } else {
+ m_stdin_forward = true;
+ }
+
+ // ::LogSetBitMask (GDBR_LOG_DEFAULT);
+ // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE |
+ // LLDB_LOG_OPTION_PREPEND_TIMESTAMP |
+ // LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD);
+ // ::LogSetLogFile ("/dev/stdout");
+
+ ObjectFile *object_file = exe_module->GetObjectFile();
+ if (object_file) {
+ error = EstablishConnectionIfNeeded(launch_info);
+ if (error.Success()) {
+ PseudoTerminal pty;
+ const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0;
+
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ if (disable_stdio) {
+ // set to /dev/null unless redirected to a file above
+ if (!stdin_file_spec)
+ stdin_file_spec.SetFile(FileSystem::DEV_NULL,
+ FileSpec::Style::native);
+ if (!stdout_file_spec)
+ stdout_file_spec.SetFile(FileSystem::DEV_NULL,
+ FileSpec::Style::native);
+ if (!stderr_file_spec)
+ stderr_file_spec.SetFile(FileSystem::DEV_NULL,
+ FileSpec::Style::native);
+ } else if (platform_sp && platform_sp->IsHost()) {
+ // If the debugserver is local and we aren't disabling STDIO, lets use
+ // a pseudo terminal to instead of relying on the 'O' packets for stdio
+ // since 'O' packets can really slow down debugging if the inferior
+ // does a lot of output.
+ if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) &&
+ pty.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY, nullptr, 0)) {
+ FileSpec slave_name{pty.GetSlaveName(nullptr, 0)};
+
+ if (!stdin_file_spec)
+ stdin_file_spec = slave_name;
+
+ if (!stdout_file_spec)
+ stdout_file_spec = slave_name;
+
+ if (!stderr_file_spec)
+ stderr_file_spec = slave_name;
+ }
+ if (log)
+ log->Printf(
+ "ProcessGDBRemote::%s adjusted STDIO paths for local platform "
+ "(IsHost() is true) using slave: stdin=%s, stdout=%s, stderr=%s",
+ __FUNCTION__,
+ stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
+ stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
+ stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
+ }
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s final STDIO paths after all "
+ "adjustments: stdin=%s, stdout=%s, stderr=%s",
+ __FUNCTION__,
+ stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
+ stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
+ stderr_file_spec ? stderr_file_spec.GetCString()
+ : "<null>");
+
+ if (stdin_file_spec)
+ m_gdb_comm.SetSTDIN(stdin_file_spec);
+ if (stdout_file_spec)
+ m_gdb_comm.SetSTDOUT(stdout_file_spec);
+ if (stderr_file_spec)
+ m_gdb_comm.SetSTDERR(stderr_file_spec);
+
+ m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR);
+ m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError);
+
+ m_gdb_comm.SendLaunchArchPacket(
+ GetTarget().GetArchitecture().GetArchitectureName());
+
+ const char *launch_event_data = launch_info.GetLaunchEventData();
+ if (launch_event_data != nullptr && *launch_event_data != '\0')
+ m_gdb_comm.SendLaunchEventDataPacket(launch_event_data);
+
+ if (working_dir) {
+ m_gdb_comm.SetWorkingDir(working_dir);
+ }
+
+ // Send the environment and the program + arguments after we connect
+ m_gdb_comm.SendEnvironment(launch_info.GetEnvironment());
+
+ {
+ // Scope for the scoped timeout object
+ GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm,
+ std::chrono::seconds(10));
+
+ int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info);
+ if (arg_packet_err == 0) {
+ std::string error_str;
+ if (m_gdb_comm.GetLaunchSuccess(error_str)) {
+ SetID(m_gdb_comm.GetCurrentProcessID());
+ } else {
+ error.SetErrorString(error_str.c_str());
+ }
+ } else {
+ error.SetErrorStringWithFormat("'A' packet returned an error: %i",
+ arg_packet_err);
+ }
+ }
+
+ if (GetID() == LLDB_INVALID_PROCESS_ID) {
+ if (log)
+ log->Printf("failed to connect to debugserver: %s",
+ error.AsCString());
+ KillDebugserverProcess();
+ return error;
+ }
+
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.GetStopReply(response)) {
+ SetLastStopPacket(response);
+ // '?' Packets must be handled differently in non-stop mode
+ if (GetTarget().GetNonStopModeEnabled())
+ HandleStopReplySequence();
+
+ const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture();
+
+ if (process_arch.IsValid()) {
+ GetTarget().MergeArchitecture(process_arch);
+ } else {
+ const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture();
+ if (host_arch.IsValid())
+ GetTarget().MergeArchitecture(host_arch);
+ }
+
+ SetPrivateState(SetThreadStopInfo(response));
+
+ if (!disable_stdio) {
+ if (pty.GetMasterFileDescriptor() != PseudoTerminal::invalid_fd)
+ SetSTDIOFileDescriptor(pty.ReleaseMasterFileDescriptor());
+ }
+ }
+ } else {
+ if (log)
+ log->Printf("failed to connect to debugserver: %s", error.AsCString());
+ }
+ } else {
+ // Set our user ID to an invalid process ID.
+ SetID(LLDB_INVALID_PROCESS_ID);
+ error.SetErrorStringWithFormat(
+ "failed to get object file from '%s' for arch %s",
+ exe_module->GetFileSpec().GetFilename().AsCString(),
+ exe_module->GetArchitecture().GetArchitectureName());
+ }
+ return error;
+}
+
+Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) {
+ Status error;
+ // Only connect if we have a valid connect URL
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (!connect_url.empty()) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s Connecting to %s", __FUNCTION__,
+ connect_url.str().c_str());
+ std::unique_ptr<ConnectionFileDescriptor> conn_up(
+ new ConnectionFileDescriptor());
+ if (conn_up) {
+ const uint32_t max_retry_count = 50;
+ uint32_t retry_count = 0;
+ while (!m_gdb_comm.IsConnected()) {
+ if (conn_up->Connect(connect_url, &error) == eConnectionStatusSuccess) {
+ m_gdb_comm.SetConnection(conn_up.release());
+ break;
+ } else if (error.WasInterrupted()) {
+ // If we were interrupted, don't keep retrying.
+ break;
+ }
+
+ retry_count++;
+
+ if (retry_count >= max_retry_count)
+ break;
+
+ usleep(100000);
+ }
+ }
+ }
+
+ if (!m_gdb_comm.IsConnected()) {
+ if (error.Success())
+ error.SetErrorString("not connected to remote gdb server");
+ return error;
+ }
+
+ // Start the communications read thread so all incoming data can be parsed
+ // into packets and queued as they arrive.
+ if (GetTarget().GetNonStopModeEnabled())
+ m_gdb_comm.StartReadThread();
+
+ // We always seem to be able to open a connection to a local port so we need
+ // to make sure we can then send data to it. If we can't then we aren't
+ // actually connected to anything, so try and do the handshake with the
+ // remote GDB server and make sure that goes alright.
+ if (!m_gdb_comm.HandshakeWithServer(&error)) {
+ m_gdb_comm.Disconnect();
+ if (error.Success())
+ error.SetErrorString("not connected to remote gdb server");
+ return error;
+ }
+
+ // Send $QNonStop:1 packet on startup if required
+ if (GetTarget().GetNonStopModeEnabled())
+ GetTarget().SetNonStopModeEnabled(m_gdb_comm.SetNonStopMode(true));
+
+ m_gdb_comm.GetEchoSupported();
+ m_gdb_comm.GetThreadSuffixSupported();
+ m_gdb_comm.GetListThreadsInStopReplySupported();
+ m_gdb_comm.GetHostInfo();
+ m_gdb_comm.GetVContSupported('c');
+ m_gdb_comm.GetVAttachOrWaitSupported();
+ m_gdb_comm.EnableErrorStringInPacket();
+
+ // Ask the remote server for the default thread id
+ if (GetTarget().GetNonStopModeEnabled())
+ m_gdb_comm.GetDefaultThreadId(m_initial_tid);
+
+ size_t num_cmds = GetExtraStartupCommands().GetArgumentCount();
+ for (size_t idx = 0; idx < num_cmds; idx++) {
+ StringExtractorGDBRemote response;
+ m_gdb_comm.SendPacketAndWaitForResponse(
+ GetExtraStartupCommands().GetArgumentAtIndex(idx), response, false);
+ }
+ return error;
+}
+
+void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::%s()", __FUNCTION__);
+ if (GetID() != LLDB_INVALID_PROCESS_ID) {
+ BuildDynamicRegisterInfo(false);
+
+ // See if the GDB server supports the qHostInfo information
+
+ // See if the GDB server supports the qProcessInfo packet, if so prefer
+ // that over the Host information as it will be more specific to our
+ // process.
+
+ const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture();
+ if (remote_process_arch.IsValid()) {
+ process_arch = remote_process_arch;
+ if (log)
+ log->Printf("ProcessGDBRemote::%s gdb-remote had process architecture, "
+ "using %s %s",
+ __FUNCTION__,
+ process_arch.GetArchitectureName()
+ ? process_arch.GetArchitectureName()
+ : "<null>",
+ process_arch.GetTriple().getTriple().c_str()
+ ? process_arch.GetTriple().getTriple().c_str()
+ : "<null>");
+ } else {
+ process_arch = m_gdb_comm.GetHostArchitecture();
+ if (log)
+ log->Printf("ProcessGDBRemote::%s gdb-remote did not have process "
+ "architecture, using gdb-remote host architecture %s %s",
+ __FUNCTION__,
+ process_arch.GetArchitectureName()
+ ? process_arch.GetArchitectureName()
+ : "<null>",
+ process_arch.GetTriple().getTriple().c_str()
+ ? process_arch.GetTriple().getTriple().c_str()
+ : "<null>");
+ }
+
+ if (process_arch.IsValid()) {
+ const ArchSpec &target_arch = GetTarget().GetArchitecture();
+ if (target_arch.IsValid()) {
+ if (log)
+ log->Printf(
+ "ProcessGDBRemote::%s analyzing target arch, currently %s %s",
+ __FUNCTION__,
+ target_arch.GetArchitectureName()
+ ? target_arch.GetArchitectureName()
+ : "<null>",
+ target_arch.GetTriple().getTriple().c_str()
+ ? target_arch.GetTriple().getTriple().c_str()
+ : "<null>");
+
+ // If the remote host is ARM and we have apple as the vendor, then
+ // ARM executables and shared libraries can have mixed ARM
+ // architectures.
+ // You can have an armv6 executable, and if the host is armv7, then the
+ // system will load the best possible architecture for all shared
+ // libraries it has, so we really need to take the remote host
+ // architecture as our defacto architecture in this case.
+
+ if ((process_arch.GetMachine() == llvm::Triple::arm ||
+ process_arch.GetMachine() == llvm::Triple::thumb) &&
+ process_arch.GetTriple().getVendor() == llvm::Triple::Apple) {
+ GetTarget().SetArchitecture(process_arch);
+ if (log)
+ log->Printf("ProcessGDBRemote::%s remote process is ARM/Apple, "
+ "setting target arch to %s %s",
+ __FUNCTION__,
+ process_arch.GetArchitectureName()
+ ? process_arch.GetArchitectureName()
+ : "<null>",
+ process_arch.GetTriple().getTriple().c_str()
+ ? process_arch.GetTriple().getTriple().c_str()
+ : "<null>");
+ } else {
+ // Fill in what is missing in the triple
+ const llvm::Triple &remote_triple = process_arch.GetTriple();
+ llvm::Triple new_target_triple = target_arch.GetTriple();
+ if (new_target_triple.getVendorName().size() == 0) {
+ new_target_triple.setVendor(remote_triple.getVendor());
+
+ if (new_target_triple.getOSName().size() == 0) {
+ new_target_triple.setOS(remote_triple.getOS());
+
+ if (new_target_triple.getEnvironmentName().size() == 0)
+ new_target_triple.setEnvironment(
+ remote_triple.getEnvironment());
+ }
+
+ ArchSpec new_target_arch = target_arch;
+ new_target_arch.SetTriple(new_target_triple);
+ GetTarget().SetArchitecture(new_target_arch);
+ }
+ }
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s final target arch after "
+ "adjustments for remote architecture: %s %s",
+ __FUNCTION__,
+ target_arch.GetArchitectureName()
+ ? target_arch.GetArchitectureName()
+ : "<null>",
+ target_arch.GetTriple().getTriple().c_str()
+ ? target_arch.GetTriple().getTriple().c_str()
+ : "<null>");
+ } else {
+ // The target doesn't have a valid architecture yet, set it from the
+ // architecture we got from the remote GDB server
+ GetTarget().SetArchitecture(process_arch);
+ }
+ }
+
+ // Find out which StructuredDataPlugins are supported by the debug monitor.
+ // These plugins transmit data over async $J packets.
+ auto supported_packets_array =
+ m_gdb_comm.GetSupportedStructuredDataPlugins();
+ if (supported_packets_array)
+ MapSupportedStructuredDataPlugins(*supported_packets_array);
+ }
+}
+
+void ProcessGDBRemote::DidLaunch() {
+ ArchSpec process_arch;
+ DidLaunchOrAttach(process_arch);
+}
+
+Status ProcessGDBRemote::DoAttachToProcessWithID(
+ lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ Status error;
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s()", __FUNCTION__);
+
+ // Clear out and clean up from any current state
+ Clear();
+ if (attach_pid != LLDB_INVALID_PROCESS_ID) {
+ error = EstablishConnectionIfNeeded(attach_info);
+ if (error.Success()) {
+ m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError());
+
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid);
+ SetID(attach_pid);
+ m_async_broadcaster.BroadcastEvent(
+ eBroadcastBitAsyncContinue, new EventDataBytes(packet, packet_len));
+ } else
+ SetExitStatus(-1, error.AsCString());
+ }
+
+ return error;
+}
+
+Status ProcessGDBRemote::DoAttachToProcessWithName(
+ const char *process_name, const ProcessAttachInfo &attach_info) {
+ Status error;
+ // Clear out and clean up from any current state
+ Clear();
+
+ if (process_name && process_name[0]) {
+ error = EstablishConnectionIfNeeded(attach_info);
+ if (error.Success()) {
+ StreamString packet;
+
+ m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError());
+
+ if (attach_info.GetWaitForLaunch()) {
+ if (!m_gdb_comm.GetVAttachOrWaitSupported()) {
+ packet.PutCString("vAttachWait");
+ } else {
+ if (attach_info.GetIgnoreExisting())
+ packet.PutCString("vAttachWait");
+ else
+ packet.PutCString("vAttachOrWait");
+ }
+ } else
+ packet.PutCString("vAttachName");
+ packet.PutChar(';');
+ packet.PutBytesAsRawHex8(process_name, strlen(process_name),
+ endian::InlHostByteOrder(),
+ endian::InlHostByteOrder());
+
+ m_async_broadcaster.BroadcastEvent(
+ eBroadcastBitAsyncContinue,
+ new EventDataBytes(packet.GetString().data(), packet.GetSize()));
+
+ } else
+ SetExitStatus(-1, error.AsCString());
+ }
+ return error;
+}
+
+lldb::user_id_t ProcessGDBRemote::StartTrace(const TraceOptions &options,
+ Status &error) {
+ return m_gdb_comm.SendStartTracePacket(options, error);
+}
+
+Status ProcessGDBRemote::StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) {
+ return m_gdb_comm.SendStopTracePacket(uid, thread_id);
+}
+
+Status ProcessGDBRemote::GetData(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) {
+ return m_gdb_comm.SendGetDataPacket(uid, thread_id, buffer, offset);
+}
+
+Status ProcessGDBRemote::GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) {
+ return m_gdb_comm.SendGetMetaDataPacket(uid, thread_id, buffer, offset);
+}
+
+Status ProcessGDBRemote::GetTraceConfig(lldb::user_id_t uid,
+ TraceOptions &options) {
+ return m_gdb_comm.SendGetTraceConfigPacket(uid, options);
+}
+
+void ProcessGDBRemote::DidExit() {
+ // When we exit, disconnect from the GDB server communications
+ m_gdb_comm.Disconnect();
+}
+
+void ProcessGDBRemote::DidAttach(ArchSpec &process_arch) {
+ // If you can figure out what the architecture is, fill it in here.
+ process_arch.Clear();
+ DidLaunchOrAttach(process_arch);
+}
+
+Status ProcessGDBRemote::WillResume() {
+ m_continue_c_tids.clear();
+ m_continue_C_tids.clear();
+ m_continue_s_tids.clear();
+ m_continue_S_tids.clear();
+ m_jstopinfo_sp.reset();
+ m_jthreadsinfo_sp.reset();
+ return Status();
+}
+
+Status ProcessGDBRemote::DoResume() {
+ Status error;
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::Resume()");
+
+ ListenerSP listener_sp(
+ Listener::MakeListener("gdb-remote.resume-packet-sent"));
+ if (listener_sp->StartListeningForEvents(
+ &m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) {
+ listener_sp->StartListeningForEvents(
+ &m_async_broadcaster,
+ ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit);
+
+ const size_t num_threads = GetThreadList().GetSize();
+
+ StreamString continue_packet;
+ bool continue_packet_error = false;
+ if (m_gdb_comm.HasAnyVContSupport()) {
+ if (!GetTarget().GetNonStopModeEnabled() &&
+ (m_continue_c_tids.size() == num_threads ||
+ (m_continue_c_tids.empty() && m_continue_C_tids.empty() &&
+ m_continue_s_tids.empty() && m_continue_S_tids.empty()))) {
+ // All threads are continuing, just send a "c" packet
+ continue_packet.PutCString("c");
+ } else {
+ continue_packet.PutCString("vCont");
+
+ if (!m_continue_c_tids.empty()) {
+ if (m_gdb_comm.GetVContSupported('c')) {
+ for (tid_collection::const_iterator
+ t_pos = m_continue_c_tids.begin(),
+ t_end = m_continue_c_tids.end();
+ t_pos != t_end; ++t_pos)
+ continue_packet.Printf(";c:%4.4" PRIx64, *t_pos);
+ } else
+ continue_packet_error = true;
+ }
+
+ if (!continue_packet_error && !m_continue_C_tids.empty()) {
+ if (m_gdb_comm.GetVContSupported('C')) {
+ for (tid_sig_collection::const_iterator
+ s_pos = m_continue_C_tids.begin(),
+ s_end = m_continue_C_tids.end();
+ s_pos != s_end; ++s_pos)
+ continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second,
+ s_pos->first);
+ } else
+ continue_packet_error = true;
+ }
+
+ if (!continue_packet_error && !m_continue_s_tids.empty()) {
+ if (m_gdb_comm.GetVContSupported('s')) {
+ for (tid_collection::const_iterator
+ t_pos = m_continue_s_tids.begin(),
+ t_end = m_continue_s_tids.end();
+ t_pos != t_end; ++t_pos)
+ continue_packet.Printf(";s:%4.4" PRIx64, *t_pos);
+ } else
+ continue_packet_error = true;
+ }
+
+ if (!continue_packet_error && !m_continue_S_tids.empty()) {
+ if (m_gdb_comm.GetVContSupported('S')) {
+ for (tid_sig_collection::const_iterator
+ s_pos = m_continue_S_tids.begin(),
+ s_end = m_continue_S_tids.end();
+ s_pos != s_end; ++s_pos)
+ continue_packet.Printf(";S%2.2x:%4.4" PRIx64, s_pos->second,
+ s_pos->first);
+ } else
+ continue_packet_error = true;
+ }
+
+ if (continue_packet_error)
+ continue_packet.Clear();
+ }
+ } else
+ continue_packet_error = true;
+
+ if (continue_packet_error) {
+ // Either no vCont support, or we tried to use part of the vCont packet
+ // that wasn't supported by the remote GDB server. We need to try and
+ // make a simple packet that can do our continue
+ const size_t num_continue_c_tids = m_continue_c_tids.size();
+ const size_t num_continue_C_tids = m_continue_C_tids.size();
+ const size_t num_continue_s_tids = m_continue_s_tids.size();
+ const size_t num_continue_S_tids = m_continue_S_tids.size();
+ if (num_continue_c_tids > 0) {
+ if (num_continue_c_tids == num_threads) {
+ // All threads are resuming...
+ m_gdb_comm.SetCurrentThreadForRun(-1);
+ continue_packet.PutChar('c');
+ continue_packet_error = false;
+ } else if (num_continue_c_tids == 1 && num_continue_C_tids == 0 &&
+ num_continue_s_tids == 0 && num_continue_S_tids == 0) {
+ // Only one thread is continuing
+ m_gdb_comm.SetCurrentThreadForRun(m_continue_c_tids.front());
+ continue_packet.PutChar('c');
+ continue_packet_error = false;
+ }
+ }
+
+ if (continue_packet_error && num_continue_C_tids > 0) {
+ if ((num_continue_C_tids + num_continue_c_tids) == num_threads &&
+ num_continue_C_tids > 0 && num_continue_s_tids == 0 &&
+ num_continue_S_tids == 0) {
+ const int continue_signo = m_continue_C_tids.front().second;
+ // Only one thread is continuing
+ if (num_continue_C_tids > 1) {
+ // More that one thread with a signal, yet we don't have vCont
+ // support and we are being asked to resume each thread with a
+ // signal, we need to make sure they are all the same signal, or we
+ // can't issue the continue accurately with the current support...
+ if (num_continue_C_tids > 1) {
+ continue_packet_error = false;
+ for (size_t i = 1; i < m_continue_C_tids.size(); ++i) {
+ if (m_continue_C_tids[i].second != continue_signo)
+ continue_packet_error = true;
+ }
+ }
+ if (!continue_packet_error)
+ m_gdb_comm.SetCurrentThreadForRun(-1);
+ } else {
+ // Set the continue thread ID
+ continue_packet_error = false;
+ m_gdb_comm.SetCurrentThreadForRun(m_continue_C_tids.front().first);
+ }
+ if (!continue_packet_error) {
+ // Add threads continuing with the same signo...
+ continue_packet.Printf("C%2.2x", continue_signo);
+ }
+ }
+ }
+
+ if (continue_packet_error && num_continue_s_tids > 0) {
+ if (num_continue_s_tids == num_threads) {
+ // All threads are resuming...
+ m_gdb_comm.SetCurrentThreadForRun(-1);
+
+ // If in Non-Stop-Mode use vCont when stepping
+ if (GetTarget().GetNonStopModeEnabled()) {
+ if (m_gdb_comm.GetVContSupported('s'))
+ continue_packet.PutCString("vCont;s");
+ else
+ continue_packet.PutChar('s');
+ } else
+ continue_packet.PutChar('s');
+
+ continue_packet_error = false;
+ } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 &&
+ num_continue_s_tids == 1 && num_continue_S_tids == 0) {
+ // Only one thread is stepping
+ m_gdb_comm.SetCurrentThreadForRun(m_continue_s_tids.front());
+ continue_packet.PutChar('s');
+ continue_packet_error = false;
+ }
+ }
+
+ if (!continue_packet_error && num_continue_S_tids > 0) {
+ if (num_continue_S_tids == num_threads) {
+ const int step_signo = m_continue_S_tids.front().second;
+ // Are all threads trying to step with the same signal?
+ continue_packet_error = false;
+ if (num_continue_S_tids > 1) {
+ for (size_t i = 1; i < num_threads; ++i) {
+ if (m_continue_S_tids[i].second != step_signo)
+ continue_packet_error = true;
+ }
+ }
+ if (!continue_packet_error) {
+ // Add threads stepping with the same signo...
+ m_gdb_comm.SetCurrentThreadForRun(-1);
+ continue_packet.Printf("S%2.2x", step_signo);
+ }
+ } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 &&
+ num_continue_s_tids == 0 && num_continue_S_tids == 1) {
+ // Only one thread is stepping with signal
+ m_gdb_comm.SetCurrentThreadForRun(m_continue_S_tids.front().first);
+ continue_packet.Printf("S%2.2x", m_continue_S_tids.front().second);
+ continue_packet_error = false;
+ }
+ }
+ }
+
+ if (continue_packet_error) {
+ error.SetErrorString("can't make continue packet for this resume");
+ } else {
+ EventSP event_sp;
+ if (!m_async_thread.IsJoinable()) {
+ error.SetErrorString("Trying to resume but the async thread is dead.");
+ if (log)
+ log->Printf("ProcessGDBRemote::DoResume: Trying to resume but the "
+ "async thread is dead.");
+ return error;
+ }
+
+ m_async_broadcaster.BroadcastEvent(
+ eBroadcastBitAsyncContinue,
+ new EventDataBytes(continue_packet.GetString().data(),
+ continue_packet.GetSize()));
+
+ if (!listener_sp->GetEvent(event_sp, std::chrono::seconds(5))) {
+ error.SetErrorString("Resume timed out.");
+ if (log)
+ log->Printf("ProcessGDBRemote::DoResume: Resume timed out.");
+ } else if (event_sp->BroadcasterIs(&m_async_broadcaster)) {
+ error.SetErrorString("Broadcast continue, but the async thread was "
+ "killed before we got an ack back.");
+ if (log)
+ log->Printf("ProcessGDBRemote::DoResume: Broadcast continue, but the "
+ "async thread was killed before we got an ack back.");
+ return error;
+ }
+ }
+ }
+
+ return error;
+}
+
+void ProcessGDBRemote::HandleStopReplySequence() {
+ while (true) {
+ // Send vStopped
+ StringExtractorGDBRemote response;
+ m_gdb_comm.SendPacketAndWaitForResponse("vStopped", response, false);
+
+ // OK represents end of signal list
+ if (response.IsOKResponse())
+ break;
+
+ // If not OK or a normal packet we have a problem
+ if (!response.IsNormalResponse())
+ break;
+
+ SetLastStopPacket(response);
+ }
+}
+
+void ProcessGDBRemote::ClearThreadIDList() {
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex());
+ m_thread_ids.clear();
+ m_thread_pcs.clear();
+}
+
+size_t
+ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue(std::string &value) {
+ m_thread_ids.clear();
+ size_t comma_pos;
+ lldb::tid_t tid;
+ while ((comma_pos = value.find(',')) != std::string::npos) {
+ value[comma_pos] = '\0';
+ // thread in big endian hex
+ tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16);
+ if (tid != LLDB_INVALID_THREAD_ID)
+ m_thread_ids.push_back(tid);
+ value.erase(0, comma_pos + 1);
+ }
+ tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16);
+ if (tid != LLDB_INVALID_THREAD_ID)
+ m_thread_ids.push_back(tid);
+ return m_thread_ids.size();
+}
+
+size_t
+ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue(std::string &value) {
+ m_thread_pcs.clear();
+ size_t comma_pos;
+ lldb::addr_t pc;
+ while ((comma_pos = value.find(',')) != std::string::npos) {
+ value[comma_pos] = '\0';
+ pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16);
+ if (pc != LLDB_INVALID_ADDRESS)
+ m_thread_pcs.push_back(pc);
+ value.erase(0, comma_pos + 1);
+ }
+ pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16);
+ if (pc != LLDB_INVALID_THREAD_ID)
+ m_thread_pcs.push_back(pc);
+ return m_thread_pcs.size();
+}
+
+bool ProcessGDBRemote::UpdateThreadIDList() {
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex());
+
+ if (m_jthreadsinfo_sp) {
+ // If we have the JSON threads info, we can get the thread list from that
+ StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray();
+ if (thread_infos && thread_infos->GetSize() > 0) {
+ m_thread_ids.clear();
+ m_thread_pcs.clear();
+ thread_infos->ForEach([this](StructuredData::Object *object) -> bool {
+ StructuredData::Dictionary *thread_dict = object->GetAsDictionary();
+ if (thread_dict) {
+ // Set the thread stop info from the JSON dictionary
+ SetThreadStopInfo(thread_dict);
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>("tid", tid))
+ m_thread_ids.push_back(tid);
+ }
+ return true; // Keep iterating through all thread_info objects
+ });
+ }
+ if (!m_thread_ids.empty())
+ return true;
+ } else {
+ // See if we can get the thread IDs from the current stop reply packets
+ // that might contain a "threads" key/value pair
+
+ // Lock the thread stack while we access it
+ // Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex);
+ std::unique_lock<std::recursive_mutex> stop_stack_lock(
+ m_last_stop_packet_mutex, std::defer_lock);
+ if (stop_stack_lock.try_lock()) {
+ // Get the number of stop packets on the stack
+ int nItems = m_stop_packet_stack.size();
+ // Iterate over them
+ for (int i = 0; i < nItems; i++) {
+ // Get the thread stop info
+ StringExtractorGDBRemote &stop_info = m_stop_packet_stack[i];
+ const std::string &stop_info_str = stop_info.GetStringRef();
+
+ m_thread_pcs.clear();
+ const size_t thread_pcs_pos = stop_info_str.find(";thread-pcs:");
+ if (thread_pcs_pos != std::string::npos) {
+ const size_t start = thread_pcs_pos + strlen(";thread-pcs:");
+ const size_t end = stop_info_str.find(';', start);
+ if (end != std::string::npos) {
+ std::string value = stop_info_str.substr(start, end - start);
+ UpdateThreadPCsFromStopReplyThreadsValue(value);
+ }
+ }
+
+ const size_t threads_pos = stop_info_str.find(";threads:");
+ if (threads_pos != std::string::npos) {
+ const size_t start = threads_pos + strlen(";threads:");
+ const size_t end = stop_info_str.find(';', start);
+ if (end != std::string::npos) {
+ std::string value = stop_info_str.substr(start, end - start);
+ if (UpdateThreadIDsFromStopReplyThreadsValue(value))
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ bool sequence_mutex_unavailable = false;
+ m_gdb_comm.GetCurrentThreadIDs(m_thread_ids, sequence_mutex_unavailable);
+ if (sequence_mutex_unavailable) {
+ return false; // We just didn't get the list
+ }
+ return true;
+}
+
+bool ProcessGDBRemote::UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) {
+ // locker will keep a mutex locked until it goes out of scope
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_THREAD));
+ LLDB_LOGV(log, "pid = {0}", GetID());
+
+ size_t num_thread_ids = m_thread_ids.size();
+ // The "m_thread_ids" thread ID list should always be updated after each stop
+ // reply packet, but in case it isn't, update it here.
+ if (num_thread_ids == 0) {
+ if (!UpdateThreadIDList())
+ return false;
+ num_thread_ids = m_thread_ids.size();
+ }
+
+ ThreadList old_thread_list_copy(old_thread_list);
+ if (num_thread_ids > 0) {
+ for (size_t i = 0; i < num_thread_ids; ++i) {
+ tid_t tid = m_thread_ids[i];
+ ThreadSP thread_sp(
+ old_thread_list_copy.RemoveThreadByProtocolID(tid, false));
+ if (!thread_sp) {
+ thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid);
+ LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.",
+ thread_sp.get(), thread_sp->GetID());
+ } else {
+ LLDB_LOGV(log, "Found old thread: {0} for thread ID: {1:x}.",
+ thread_sp.get(), thread_sp->GetID());
+ }
+
+ SetThreadPc(thread_sp, i);
+ new_thread_list.AddThreadSortedByIndexID(thread_sp);
+ }
+ }
+
+ // Whatever that is left in old_thread_list_copy are not present in
+ // new_thread_list. Remove non-existent threads from internal id table.
+ size_t old_num_thread_ids = old_thread_list_copy.GetSize(false);
+ for (size_t i = 0; i < old_num_thread_ids; i++) {
+ ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false));
+ if (old_thread_sp) {
+ lldb::tid_t old_thread_id = old_thread_sp->GetProtocolID();
+ m_thread_id_to_index_id_map.erase(old_thread_id);
+ }
+ }
+
+ return true;
+}
+
+void ProcessGDBRemote::SetThreadPc(const ThreadSP &thread_sp, uint64_t index) {
+ if (m_thread_ids.size() == m_thread_pcs.size() && thread_sp.get() &&
+ GetByteOrder() != eByteOrderInvalid) {
+ ThreadGDBRemote *gdb_thread =
+ static_cast<ThreadGDBRemote *>(thread_sp.get());
+ RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
+ if (reg_ctx_sp) {
+ uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ if (pc_regnum != LLDB_INVALID_REGNUM) {
+ gdb_thread->PrivateSetRegisterValue(pc_regnum, m_thread_pcs[index]);
+ }
+ }
+ }
+}
+
+bool ProcessGDBRemote::GetThreadStopInfoFromJSON(
+ ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp) {
+ // See if we got thread stop infos for all threads via the "jThreadsInfo"
+ // packet
+ if (thread_infos_sp) {
+ StructuredData::Array *thread_infos = thread_infos_sp->GetAsArray();
+ if (thread_infos) {
+ lldb::tid_t tid;
+ const size_t n = thread_infos->GetSize();
+ for (size_t i = 0; i < n; ++i) {
+ StructuredData::Dictionary *thread_dict =
+ thread_infos->GetItemAtIndex(i)->GetAsDictionary();
+ if (thread_dict) {
+ if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>(
+ "tid", tid, LLDB_INVALID_THREAD_ID)) {
+ if (tid == thread->GetID())
+ return (bool)SetThreadStopInfo(thread_dict);
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool ProcessGDBRemote::CalculateThreadStopInfo(ThreadGDBRemote *thread) {
+ // See if we got thread stop infos for all threads via the "jThreadsInfo"
+ // packet
+ if (GetThreadStopInfoFromJSON(thread, m_jthreadsinfo_sp))
+ return true;
+
+ // See if we got thread stop info for any threads valid stop info reasons
+ // threads via the "jstopinfo" packet stop reply packet key/value pair?
+ if (m_jstopinfo_sp) {
+ // If we have "jstopinfo" then we have stop descriptions for all threads
+ // that have stop reasons, and if there is no entry for a thread, then it
+ // has no stop reason.
+ thread->GetRegisterContext()->InvalidateIfNeeded(true);
+ if (!GetThreadStopInfoFromJSON(thread, m_jstopinfo_sp)) {
+ thread->SetStopInfo(StopInfoSP());
+ }
+ return true;
+ }
+
+ // Fall back to using the qThreadStopInfo packet
+ StringExtractorGDBRemote stop_packet;
+ if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet))
+ return SetThreadStopInfo(stop_packet) == eStateStopped;
+ return false;
+}
+
+ThreadSP ProcessGDBRemote::SetThreadStopInfo(
+ lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map,
+ uint8_t signo, const std::string &thread_name, const std::string &reason,
+ const std::string &description, uint32_t exc_type,
+ const std::vector<addr_t> &exc_data, addr_t thread_dispatch_qaddr,
+ bool queue_vars_valid, // Set to true if queue_name, queue_kind and
+ // queue_serial are valid
+ LazyBool associated_with_dispatch_queue, addr_t dispatch_queue_t,
+ std::string &queue_name, QueueKind queue_kind, uint64_t queue_serial) {
+ ThreadSP thread_sp;
+ if (tid != LLDB_INVALID_THREAD_ID) {
+ // Scope for "locker" below
+ {
+ // m_thread_list_real does have its own mutex, but we need to hold onto
+ // the mutex between the call to m_thread_list_real.FindThreadByID(...)
+ // and the m_thread_list_real.AddThread(...) so it doesn't change on us
+ std::lock_guard<std::recursive_mutex> guard(
+ m_thread_list_real.GetMutex());
+ thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false);
+
+ if (!thread_sp) {
+ // Create the thread if we need to
+ thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid);
+ m_thread_list_real.AddThread(thread_sp);
+ }
+ }
+
+ if (thread_sp) {
+ ThreadGDBRemote *gdb_thread =
+ static_cast<ThreadGDBRemote *>(thread_sp.get());
+ gdb_thread->GetRegisterContext()->InvalidateIfNeeded(true);
+
+ auto iter = std::find(m_thread_ids.begin(), m_thread_ids.end(), tid);
+ if (iter != m_thread_ids.end()) {
+ SetThreadPc(thread_sp, iter - m_thread_ids.begin());
+ }
+
+ for (const auto &pair : expedited_register_map) {
+ StringExtractor reg_value_extractor;
+ reg_value_extractor.GetStringRef() = pair.second;
+ DataBufferSP buffer_sp(new DataBufferHeap(
+ reg_value_extractor.GetStringRef().size() / 2, 0));
+ reg_value_extractor.GetHexBytes(buffer_sp->GetData(), '\xcc');
+ gdb_thread->PrivateSetRegisterValue(pair.first, buffer_sp->GetData());
+ }
+
+ thread_sp->SetName(thread_name.empty() ? nullptr : thread_name.c_str());
+
+ gdb_thread->SetThreadDispatchQAddr(thread_dispatch_qaddr);
+ // Check if the GDB server was able to provide the queue name, kind and
+ // serial number
+ if (queue_vars_valid)
+ gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind,
+ queue_serial, dispatch_queue_t,
+ associated_with_dispatch_queue);
+ else
+ gdb_thread->ClearQueueInfo();
+
+ gdb_thread->SetAssociatedWithLibdispatchQueue(
+ associated_with_dispatch_queue);
+
+ if (dispatch_queue_t != LLDB_INVALID_ADDRESS)
+ gdb_thread->SetQueueLibdispatchQueueAddress(dispatch_queue_t);
+
+ // Make sure we update our thread stop reason just once
+ if (!thread_sp->StopInfoIsUpToDate()) {
+ thread_sp->SetStopInfo(StopInfoSP());
+ // If there's a memory thread backed by this thread, we need to use it
+ // to calculate StopInfo.
+ if (ThreadSP memory_thread_sp =
+ m_thread_list.GetBackingThread(thread_sp))
+ thread_sp = memory_thread_sp;
+
+ if (exc_type != 0) {
+ const size_t exc_data_size = exc_data.size();
+
+ thread_sp->SetStopInfo(
+ StopInfoMachException::CreateStopReasonWithMachException(
+ *thread_sp, exc_type, exc_data_size,
+ exc_data_size >= 1 ? exc_data[0] : 0,
+ exc_data_size >= 2 ? exc_data[1] : 0,
+ exc_data_size >= 3 ? exc_data[2] : 0));
+ } else {
+ bool handled = false;
+ bool did_exec = false;
+ if (!reason.empty()) {
+ if (reason == "trace") {
+ addr_t pc = thread_sp->GetRegisterContext()->GetPC();
+ lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()
+ ->GetBreakpointSiteList()
+ .FindByAddress(pc);
+
+ // If the current pc is a breakpoint site then the StopInfo
+ // should be set to Breakpoint Otherwise, it will be set to
+ // Trace.
+ if (bp_site_sp &&
+ bp_site_sp->ValidForThisThread(thread_sp.get())) {
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonWithBreakpointSiteID(
+ *thread_sp, bp_site_sp->GetID()));
+ } else
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonToTrace(*thread_sp));
+ handled = true;
+ } else if (reason == "breakpoint") {
+ addr_t pc = thread_sp->GetRegisterContext()->GetPC();
+ lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()
+ ->GetBreakpointSiteList()
+ .FindByAddress(pc);
+ if (bp_site_sp) {
+ // If the breakpoint is for this thread, then we'll report the
+ // hit, but if it is for another thread, we can just report no
+ // reason. We don't need to worry about stepping over the
+ // breakpoint here, that will be taken care of when the thread
+ // resumes and notices that there's a breakpoint under the pc.
+ handled = true;
+ if (bp_site_sp->ValidForThisThread(thread_sp.get())) {
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonWithBreakpointSiteID(
+ *thread_sp, bp_site_sp->GetID()));
+ } else {
+ StopInfoSP invalid_stop_info_sp;
+ thread_sp->SetStopInfo(invalid_stop_info_sp);
+ }
+ }
+ } else if (reason == "trap") {
+ // Let the trap just use the standard signal stop reason below...
+ } else if (reason == "watchpoint") {
+ StringExtractor desc_extractor(description.c_str());
+ addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS);
+ uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32);
+ addr_t wp_hit_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS);
+ watch_id_t watch_id = LLDB_INVALID_WATCH_ID;
+ if (wp_addr != LLDB_INVALID_ADDRESS) {
+ WatchpointSP wp_sp;
+ ArchSpec::Core core = GetTarget().GetArchitecture().GetCore();
+ if ((core >= ArchSpec::kCore_mips_first &&
+ core <= ArchSpec::kCore_mips_last) ||
+ (core >= ArchSpec::eCore_arm_generic &&
+ core <= ArchSpec::eCore_arm_aarch64))
+ wp_sp = GetTarget().GetWatchpointList().FindByAddress(
+ wp_hit_addr);
+ if (!wp_sp)
+ wp_sp =
+ GetTarget().GetWatchpointList().FindByAddress(wp_addr);
+ if (wp_sp) {
+ wp_sp->SetHardwareIndex(wp_index);
+ watch_id = wp_sp->GetID();
+ }
+ }
+ if (watch_id == LLDB_INVALID_WATCH_ID) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(
+ GDBR_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("failed to find watchpoint");
+ }
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithWatchpointID(
+ *thread_sp, watch_id, wp_hit_addr));
+ handled = true;
+ } else if (reason == "exception") {
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException(
+ *thread_sp, description.c_str()));
+ handled = true;
+ } else if (reason == "exec") {
+ did_exec = true;
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonWithExec(*thread_sp));
+ handled = true;
+ }
+ } else if (!signo) {
+ addr_t pc = thread_sp->GetRegisterContext()->GetPC();
+ lldb::BreakpointSiteSP bp_site_sp =
+ thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(
+ pc);
+
+ // If the current pc is a breakpoint site then the StopInfo should
+ // be set to Breakpoint even though the remote stub did not set it
+ // as such. This can happen when the thread is involuntarily
+ // interrupted (e.g. due to stops on other threads) just as it is
+ // about to execute the breakpoint instruction.
+ if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) {
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonWithBreakpointSiteID(
+ *thread_sp, bp_site_sp->GetID()));
+ handled = true;
+ }
+ }
+
+ if (!handled && signo && !did_exec) {
+ if (signo == SIGTRAP) {
+ // Currently we are going to assume SIGTRAP means we are either
+ // hitting a breakpoint or hardware single stepping.
+ handled = true;
+ addr_t pc = thread_sp->GetRegisterContext()->GetPC() +
+ m_breakpoint_pc_offset;
+ lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()
+ ->GetBreakpointSiteList()
+ .FindByAddress(pc);
+
+ if (bp_site_sp) {
+ // If the breakpoint is for this thread, then we'll report the
+ // hit, but if it is for another thread, we can just report no
+ // reason. We don't need to worry about stepping over the
+ // breakpoint here, that will be taken care of when the thread
+ // resumes and notices that there's a breakpoint under the pc.
+ if (bp_site_sp->ValidForThisThread(thread_sp.get())) {
+ if (m_breakpoint_pc_offset != 0)
+ thread_sp->GetRegisterContext()->SetPC(pc);
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonWithBreakpointSiteID(
+ *thread_sp, bp_site_sp->GetID()));
+ } else {
+ StopInfoSP invalid_stop_info_sp;
+ thread_sp->SetStopInfo(invalid_stop_info_sp);
+ }
+ } else {
+ // If we were stepping then assume the stop was the result of
+ // the trace. If we were not stepping then report the SIGTRAP.
+ // FIXME: We are still missing the case where we single step
+ // over a trap instruction.
+ if (thread_sp->GetTemporaryResumeState() == eStateStepping)
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonToTrace(*thread_sp));
+ else
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal(
+ *thread_sp, signo, description.c_str()));
+ }
+ }
+ if (!handled)
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal(
+ *thread_sp, signo, description.c_str()));
+ }
+
+ if (!description.empty()) {
+ lldb::StopInfoSP stop_info_sp(thread_sp->GetStopInfo());
+ if (stop_info_sp) {
+ const char *stop_info_desc = stop_info_sp->GetDescription();
+ if (!stop_info_desc || !stop_info_desc[0])
+ stop_info_sp->SetDescription(description.c_str());
+ } else {
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException(
+ *thread_sp, description.c_str()));
+ }
+ }
+ }
+ }
+ }
+ }
+ return thread_sp;
+}
+
+lldb::ThreadSP
+ProcessGDBRemote::SetThreadStopInfo(StructuredData::Dictionary *thread_dict) {
+ static ConstString g_key_tid("tid");
+ static ConstString g_key_name("name");
+ static ConstString g_key_reason("reason");
+ static ConstString g_key_metype("metype");
+ static ConstString g_key_medata("medata");
+ static ConstString g_key_qaddr("qaddr");
+ static ConstString g_key_dispatch_queue_t("dispatch_queue_t");
+ static ConstString g_key_associated_with_dispatch_queue(
+ "associated_with_dispatch_queue");
+ static ConstString g_key_queue_name("qname");
+ static ConstString g_key_queue_kind("qkind");
+ static ConstString g_key_queue_serial_number("qserialnum");
+ static ConstString g_key_registers("registers");
+ static ConstString g_key_memory("memory");
+ static ConstString g_key_address("address");
+ static ConstString g_key_bytes("bytes");
+ static ConstString g_key_description("description");
+ static ConstString g_key_signal("signal");
+
+ // Stop with signal and thread info
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ uint8_t signo = 0;
+ std::string value;
+ std::string thread_name;
+ std::string reason;
+ std::string description;
+ uint32_t exc_type = 0;
+ std::vector<addr_t> exc_data;
+ addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
+ ExpeditedRegisterMap expedited_register_map;
+ bool queue_vars_valid = false;
+ addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS;
+ LazyBool associated_with_dispatch_queue = eLazyBoolCalculate;
+ std::string queue_name;
+ QueueKind queue_kind = eQueueKindUnknown;
+ uint64_t queue_serial_number = 0;
+ // Iterate through all of the thread dictionary key/value pairs from the
+ // structured data dictionary
+
+ thread_dict->ForEach([this, &tid, &expedited_register_map, &thread_name,
+ &signo, &reason, &description, &exc_type, &exc_data,
+ &thread_dispatch_qaddr, &queue_vars_valid,
+ &associated_with_dispatch_queue, &dispatch_queue_t,
+ &queue_name, &queue_kind, &queue_serial_number](
+ ConstString key,
+ StructuredData::Object *object) -> bool {
+ if (key == g_key_tid) {
+ // thread in big endian hex
+ tid = object->GetIntegerValue(LLDB_INVALID_THREAD_ID);
+ } else if (key == g_key_metype) {
+ // exception type in big endian hex
+ exc_type = object->GetIntegerValue(0);
+ } else if (key == g_key_medata) {
+ // exception data in big endian hex
+ StructuredData::Array *array = object->GetAsArray();
+ if (array) {
+ array->ForEach([&exc_data](StructuredData::Object *object) -> bool {
+ exc_data.push_back(object->GetIntegerValue());
+ return true; // Keep iterating through all array items
+ });
+ }
+ } else if (key == g_key_name) {
+ thread_name = object->GetStringValue();
+ } else if (key == g_key_qaddr) {
+ thread_dispatch_qaddr = object->GetIntegerValue(LLDB_INVALID_ADDRESS);
+ } else if (key == g_key_queue_name) {
+ queue_vars_valid = true;
+ queue_name = object->GetStringValue();
+ } else if (key == g_key_queue_kind) {
+ std::string queue_kind_str = object->GetStringValue();
+ if (queue_kind_str == "serial") {
+ queue_vars_valid = true;
+ queue_kind = eQueueKindSerial;
+ } else if (queue_kind_str == "concurrent") {
+ queue_vars_valid = true;
+ queue_kind = eQueueKindConcurrent;
+ }
+ } else if (key == g_key_queue_serial_number) {
+ queue_serial_number = object->GetIntegerValue(0);
+ if (queue_serial_number != 0)
+ queue_vars_valid = true;
+ } else if (key == g_key_dispatch_queue_t) {
+ dispatch_queue_t = object->GetIntegerValue(0);
+ if (dispatch_queue_t != 0 && dispatch_queue_t != LLDB_INVALID_ADDRESS)
+ queue_vars_valid = true;
+ } else if (key == g_key_associated_with_dispatch_queue) {
+ queue_vars_valid = true;
+ bool associated = object->GetBooleanValue();
+ if (associated)
+ associated_with_dispatch_queue = eLazyBoolYes;
+ else
+ associated_with_dispatch_queue = eLazyBoolNo;
+ } else if (key == g_key_reason) {
+ reason = object->GetStringValue();
+ } else if (key == g_key_description) {
+ description = object->GetStringValue();
+ } else if (key == g_key_registers) {
+ StructuredData::Dictionary *registers_dict = object->GetAsDictionary();
+
+ if (registers_dict) {
+ registers_dict->ForEach(
+ [&expedited_register_map](ConstString key,
+ StructuredData::Object *object) -> bool {
+ const uint32_t reg =
+ StringConvert::ToUInt32(key.GetCString(), UINT32_MAX, 10);
+ if (reg != UINT32_MAX)
+ expedited_register_map[reg] = object->GetStringValue();
+ return true; // Keep iterating through all array items
+ });
+ }
+ } else if (key == g_key_memory) {
+ StructuredData::Array *array = object->GetAsArray();
+ if (array) {
+ array->ForEach([this](StructuredData::Object *object) -> bool {
+ StructuredData::Dictionary *mem_cache_dict =
+ object->GetAsDictionary();
+ if (mem_cache_dict) {
+ lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS;
+ if (mem_cache_dict->GetValueForKeyAsInteger<lldb::addr_t>(
+ "address", mem_cache_addr)) {
+ if (mem_cache_addr != LLDB_INVALID_ADDRESS) {
+ llvm::StringRef str;
+ if (mem_cache_dict->GetValueForKeyAsString("bytes", str)) {
+ StringExtractor bytes(str);
+ bytes.SetFilePos(0);
+
+ const size_t byte_size = bytes.GetStringRef().size() / 2;
+ DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0));
+ const size_t bytes_copied =
+ bytes.GetHexBytes(data_buffer_sp->GetData(), 0);
+ if (bytes_copied == byte_size)
+ m_memory_cache.AddL1CacheData(mem_cache_addr,
+ data_buffer_sp);
+ }
+ }
+ }
+ }
+ return true; // Keep iterating through all array items
+ });
+ }
+
+ } else if (key == g_key_signal)
+ signo = object->GetIntegerValue(LLDB_INVALID_SIGNAL_NUMBER);
+ return true; // Keep iterating through all dictionary key/value pairs
+ });
+
+ return SetThreadStopInfo(tid, expedited_register_map, signo, thread_name,
+ reason, description, exc_type, exc_data,
+ thread_dispatch_qaddr, queue_vars_valid,
+ associated_with_dispatch_queue, dispatch_queue_t,
+ queue_name, queue_kind, queue_serial_number);
+}
+
+StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
+ stop_packet.SetFilePos(0);
+ const char stop_type = stop_packet.GetChar();
+ switch (stop_type) {
+ case 'T':
+ case 'S': {
+ // This is a bit of a hack, but is is required. If we did exec, we need to
+ // clear our thread lists and also know to rebuild our dynamic register
+ // info before we lookup and threads and populate the expedited register
+ // values so we need to know this right away so we can cleanup and update
+ // our registers.
+ const uint32_t stop_id = GetStopID();
+ if (stop_id == 0) {
+ // Our first stop, make sure we have a process ID, and also make sure we
+ // know about our registers
+ if (GetID() == LLDB_INVALID_PROCESS_ID) {
+ lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ SetID(pid);
+ }
+ BuildDynamicRegisterInfo(true);
+ }
+ // Stop with signal and thread info
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ const uint8_t signo = stop_packet.GetHexU8();
+ llvm::StringRef key;
+ llvm::StringRef value;
+ std::string thread_name;
+ std::string reason;
+ std::string description;
+ uint32_t exc_type = 0;
+ std::vector<addr_t> exc_data;
+ addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
+ bool queue_vars_valid =
+ false; // says if locals below that start with "queue_" are valid
+ addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS;
+ LazyBool associated_with_dispatch_queue = eLazyBoolCalculate;
+ std::string queue_name;
+ QueueKind queue_kind = eQueueKindUnknown;
+ uint64_t queue_serial_number = 0;
+ ExpeditedRegisterMap expedited_register_map;
+ while (stop_packet.GetNameColonValue(key, value)) {
+ if (key.compare("metype") == 0) {
+ // exception type in big endian hex
+ value.getAsInteger(16, exc_type);
+ } else if (key.compare("medata") == 0) {
+ // exception data in big endian hex
+ uint64_t x;
+ value.getAsInteger(16, x);
+ exc_data.push_back(x);
+ } else if (key.compare("thread") == 0) {
+ // thread in big endian hex
+ if (value.getAsInteger(16, tid))
+ tid = LLDB_INVALID_THREAD_ID;
+ } else if (key.compare("threads") == 0) {
+ std::lock_guard<std::recursive_mutex> guard(
+ m_thread_list_real.GetMutex());
+
+ m_thread_ids.clear();
+ // A comma separated list of all threads in the current
+ // process that includes the thread for this stop reply packet
+ lldb::tid_t tid;
+ while (!value.empty()) {
+ llvm::StringRef tid_str;
+ std::tie(tid_str, value) = value.split(',');
+ if (tid_str.getAsInteger(16, tid))
+ tid = LLDB_INVALID_THREAD_ID;
+ m_thread_ids.push_back(tid);
+ }
+ } else if (key.compare("thread-pcs") == 0) {
+ m_thread_pcs.clear();
+ // A comma separated list of all threads in the current
+ // process that includes the thread for this stop reply packet
+ lldb::addr_t pc;
+ while (!value.empty()) {
+ llvm::StringRef pc_str;
+ std::tie(pc_str, value) = value.split(',');
+ if (pc_str.getAsInteger(16, pc))
+ pc = LLDB_INVALID_ADDRESS;
+ m_thread_pcs.push_back(pc);
+ }
+ } else if (key.compare("jstopinfo") == 0) {
+ StringExtractor json_extractor(value);
+ std::string json;
+ // Now convert the HEX bytes into a string value
+ json_extractor.GetHexByteString(json);
+
+ // This JSON contains thread IDs and thread stop info for all threads.
+ // It doesn't contain expedited registers, memory or queue info.
+ m_jstopinfo_sp = StructuredData::ParseJSON(json);
+ } else if (key.compare("hexname") == 0) {
+ StringExtractor name_extractor(value);
+ std::string name;
+ // Now convert the HEX bytes into a string value
+ name_extractor.GetHexByteString(thread_name);
+ } else if (key.compare("name") == 0) {
+ thread_name = value;
+ } else if (key.compare("qaddr") == 0) {
+ value.getAsInteger(16, thread_dispatch_qaddr);
+ } else if (key.compare("dispatch_queue_t") == 0) {
+ queue_vars_valid = true;
+ value.getAsInteger(16, dispatch_queue_t);
+ } else if (key.compare("qname") == 0) {
+ queue_vars_valid = true;
+ StringExtractor name_extractor(value);
+ // Now convert the HEX bytes into a string value
+ name_extractor.GetHexByteString(queue_name);
+ } else if (key.compare("qkind") == 0) {
+ queue_kind = llvm::StringSwitch<QueueKind>(value)
+ .Case("serial", eQueueKindSerial)
+ .Case("concurrent", eQueueKindConcurrent)
+ .Default(eQueueKindUnknown);
+ queue_vars_valid = queue_kind != eQueueKindUnknown;
+ } else if (key.compare("qserialnum") == 0) {
+ if (!value.getAsInteger(0, queue_serial_number))
+ queue_vars_valid = true;
+ } else if (key.compare("reason") == 0) {
+ reason = value;
+ } else if (key.compare("description") == 0) {
+ StringExtractor desc_extractor(value);
+ // Now convert the HEX bytes into a string value
+ desc_extractor.GetHexByteString(description);
+ } else if (key.compare("memory") == 0) {
+ // Expedited memory. GDB servers can choose to send back expedited
+ // memory that can populate the L1 memory cache in the process so that
+ // things like the frame pointer backchain can be expedited. This will
+ // help stack backtracing be more efficient by not having to send as
+ // many memory read requests down the remote GDB server.
+
+ // Key/value pair format: memory:<addr>=<bytes>;
+ // <addr> is a number whose base will be interpreted by the prefix:
+ // "0x[0-9a-fA-F]+" for hex
+ // "0[0-7]+" for octal
+ // "[1-9]+" for decimal
+ // <bytes> is native endian ASCII hex bytes just like the register
+ // values
+ llvm::StringRef addr_str, bytes_str;
+ std::tie(addr_str, bytes_str) = value.split('=');
+ if (!addr_str.empty() && !bytes_str.empty()) {
+ lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS;
+ if (!addr_str.getAsInteger(0, mem_cache_addr)) {
+ StringExtractor bytes(bytes_str);
+ const size_t byte_size = bytes.GetBytesLeft() / 2;
+ DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0));
+ const size_t bytes_copied =
+ bytes.GetHexBytes(data_buffer_sp->GetData(), 0);
+ if (bytes_copied == byte_size)
+ m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp);
+ }
+ }
+ } else if (key.compare("watch") == 0 || key.compare("rwatch") == 0 ||
+ key.compare("awatch") == 0) {
+ // Support standard GDB remote stop reply packet 'TAAwatch:addr'
+ lldb::addr_t wp_addr = LLDB_INVALID_ADDRESS;
+ value.getAsInteger(16, wp_addr);
+
+ WatchpointSP wp_sp =
+ GetTarget().GetWatchpointList().FindByAddress(wp_addr);
+ uint32_t wp_index = LLDB_INVALID_INDEX32;
+
+ if (wp_sp)
+ wp_index = wp_sp->GetHardwareIndex();
+
+ reason = "watchpoint";
+ StreamString ostr;
+ ostr.Printf("%" PRIu64 " %" PRIu32, wp_addr, wp_index);
+ description = ostr.GetString();
+ } else if (key.compare("library") == 0) {
+ LoadModules();
+ } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) {
+ uint32_t reg = UINT32_MAX;
+ if (!key.getAsInteger(16, reg))
+ expedited_register_map[reg] = std::move(value);
+ }
+ }
+
+ if (tid == LLDB_INVALID_THREAD_ID) {
+ // A thread id may be invalid if the response is old style 'S' packet
+ // which does not provide the
+ // thread information. So update the thread list and choose the first
+ // one.
+ UpdateThreadIDList();
+
+ if (!m_thread_ids.empty()) {
+ tid = m_thread_ids.front();
+ }
+ }
+
+ ThreadSP thread_sp = SetThreadStopInfo(
+ tid, expedited_register_map, signo, thread_name, reason, description,
+ exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid,
+ associated_with_dispatch_queue, dispatch_queue_t, queue_name,
+ queue_kind, queue_serial_number);
+
+ return eStateStopped;
+ } break;
+
+ case 'W':
+ case 'X':
+ // process exited
+ return eStateExited;
+
+ default:
+ break;
+ }
+ return eStateInvalid;
+}
+
+void ProcessGDBRemote::RefreshStateAfterStop() {
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex());
+
+ m_thread_ids.clear();
+ m_thread_pcs.clear();
+ // Set the thread stop info. It might have a "threads" key whose value is a
+ // list of all thread IDs in the current process, so m_thread_ids might get
+ // set.
+
+ // Scope for the lock
+ {
+ // Check to see if SetThreadStopInfo() filled in m_thread_ids?
+ if (m_thread_ids.empty()) {
+ // No, we need to fetch the thread list manually
+ UpdateThreadIDList();
+ }
+ // We might set some stop info's so make sure the thread list is up to
+ // date before we do that or we might overwrite what was computed here.
+ UpdateThreadListIfNeeded();
+
+ // Lock the thread stack while we access it
+ std::lock_guard<std::recursive_mutex> guard(m_last_stop_packet_mutex);
+ // Get the number of stop packets on the stack
+ int nItems = m_stop_packet_stack.size();
+ // Iterate over them
+ for (int i = 0; i < nItems; i++) {
+ // Get the thread stop info
+ StringExtractorGDBRemote stop_info = m_stop_packet_stack[i];
+ // Process thread stop info
+ SetThreadStopInfo(stop_info);
+ }
+ // Clear the thread stop stack
+ m_stop_packet_stack.clear();
+ }
+
+ // If we have queried for a default thread id
+ if (m_initial_tid != LLDB_INVALID_THREAD_ID) {
+ m_thread_list.SetSelectedThreadByID(m_initial_tid);
+ m_initial_tid = LLDB_INVALID_THREAD_ID;
+ }
+
+ // Let all threads recover from stopping and do any clean up based on the
+ // previous thread state (if any).
+ m_thread_list_real.RefreshStateAfterStop();
+}
+
+Status ProcessGDBRemote::DoHalt(bool &caused_stop) {
+ Status error;
+
+ if (m_public_state.GetValue() == eStateAttaching) {
+ // We are being asked to halt during an attach. We need to just close our
+ // file handle and debugserver will go away, and we can be done...
+ m_gdb_comm.Disconnect();
+ } else
+ caused_stop = m_gdb_comm.Interrupt();
+ return error;
+}
+
+Status ProcessGDBRemote::DoDetach(bool keep_stopped) {
+ Status error;
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped);
+
+ error = m_gdb_comm.Detach(keep_stopped);
+ if (log) {
+ if (error.Success())
+ log->PutCString(
+ "ProcessGDBRemote::DoDetach() detach packet sent successfully");
+ else
+ log->Printf("ProcessGDBRemote::DoDetach() detach packet send failed: %s",
+ error.AsCString() ? error.AsCString() : "<unknown error>");
+ }
+
+ if (!error.Success())
+ return error;
+
+ // Sleep for one second to let the process get all detached...
+ StopAsyncThread();
+
+ SetPrivateState(eStateDetached);
+ ResumePrivateStateThread();
+
+ // KillDebugserverProcess ();
+ return error;
+}
+
+Status ProcessGDBRemote::DoDestroy() {
+ Status error;
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDestroy()");
+
+#ifdef LLDB_ENABLE_ALL // XXX Currently no iOS target support on FreeBSD
+ // There is a bug in older iOS debugservers where they don't shut down the
+ // process they are debugging properly. If the process is sitting at a
+ // breakpoint or an exception, this can cause problems with restarting. So
+ // we check to see if any of our threads are stopped at a breakpoint, and if
+ // so we remove all the breakpoints, resume the process, and THEN destroy it
+ // again.
+ //
+ // Note, we don't have a good way to test the version of debugserver, but I
+ // happen to know that the set of all the iOS debugservers which don't
+ // support GetThreadSuffixSupported() and that of the debugservers with this
+ // bug are equal. There really should be a better way to test this!
+ //
+ // We also use m_destroy_tried_resuming to make sure we only do this once, if
+ // we resume and then halt and get called here to destroy again and we're
+ // still at a breakpoint or exception, then we should just do the straight-
+ // forward kill.
+ //
+ // And of course, if we weren't able to stop the process by the time we get
+ // here, it isn't necessary (or helpful) to do any of this.
+
+ if (!m_gdb_comm.GetThreadSuffixSupported() &&
+ m_public_state.GetValue() != eStateRunning) {
+ PlatformSP platform_sp = GetTarget().GetPlatform();
+
+ // FIXME: These should be ConstStrings so we aren't doing strcmp'ing.
+ if (platform_sp && platform_sp->GetName() &&
+ platform_sp->GetName() == PlatformRemoteiOS::GetPluginNameStatic()) {
+ if (m_destroy_tried_resuming) {
+ if (log)
+ log->PutCString("ProcessGDBRemote::DoDestroy() - Tried resuming to "
+ "destroy once already, not doing it again.");
+ } else {
+ // At present, the plans are discarded and the breakpoints disabled
+ // Process::Destroy, but we really need it to happen here and it
+ // doesn't matter if we do it twice.
+ m_thread_list.DiscardThreadPlans();
+ DisableAllBreakpointSites();
+
+ bool stop_looks_like_crash = false;
+ ThreadList &threads = GetThreadList();
+
+ {
+ std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
+
+ size_t num_threads = threads.GetSize();
+ for (size_t i = 0; i < num_threads; i++) {
+ ThreadSP thread_sp = threads.GetThreadAtIndex(i);
+ StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo();
+ StopReason reason = eStopReasonInvalid;
+ if (stop_info_sp)
+ reason = stop_info_sp->GetStopReason();
+ if (reason == eStopReasonBreakpoint ||
+ reason == eStopReasonException) {
+ if (log)
+ log->Printf(
+ "ProcessGDBRemote::DoDestroy() - thread: 0x%4.4" PRIx64
+ " stopped with reason: %s.",
+ thread_sp->GetProtocolID(), stop_info_sp->GetDescription());
+ stop_looks_like_crash = true;
+ break;
+ }
+ }
+ }
+
+ if (stop_looks_like_crash) {
+ if (log)
+ log->PutCString("ProcessGDBRemote::DoDestroy() - Stopped at a "
+ "breakpoint, continue and then kill.");
+ m_destroy_tried_resuming = true;
+
+ // If we are going to run again before killing, it would be good to
+ // suspend all the threads before resuming so they won't get into
+ // more trouble. Sadly, for the threads stopped with the breakpoint
+ // or exception, the exception doesn't get cleared if it is
+ // suspended, so we do have to run the risk of letting those threads
+ // proceed a bit.
+
+ {
+ std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
+
+ size_t num_threads = threads.GetSize();
+ for (size_t i = 0; i < num_threads; i++) {
+ ThreadSP thread_sp = threads.GetThreadAtIndex(i);
+ StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo();
+ StopReason reason = eStopReasonInvalid;
+ if (stop_info_sp)
+ reason = stop_info_sp->GetStopReason();
+ if (reason != eStopReasonBreakpoint &&
+ reason != eStopReasonException) {
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDestroy() - Suspending "
+ "thread: 0x%4.4" PRIx64 " before running.",
+ thread_sp->GetProtocolID());
+ thread_sp->SetResumeState(eStateSuspended);
+ }
+ }
+ }
+ Resume();
+ return Destroy(false);
+ }
+ }
+ }
+ }
+#endif // LLDB_ENABLE_ALL
+
+ // Interrupt if our inferior is running...
+ int exit_status = SIGABRT;
+ std::string exit_string;
+
+ if (m_gdb_comm.IsConnected()) {
+ if (m_public_state.GetValue() != eStateAttaching) {
+ StringExtractorGDBRemote response;
+ bool send_async = true;
+ GDBRemoteCommunication::ScopedTimeout(m_gdb_comm,
+ std::chrono::seconds(3));
+
+ if (m_gdb_comm.SendPacketAndWaitForResponse("k", response, send_async) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ char packet_cmd = response.GetChar(0);
+
+ if (packet_cmd == 'W' || packet_cmd == 'X') {
+#if defined(__APPLE__)
+ // For Native processes on Mac OS X, we launch through the Host
+ // Platform, then hand the process off to debugserver, which becomes
+ // the parent process through "PT_ATTACH". Then when we go to kill
+ // the process on Mac OS X we call ptrace(PT_KILL) to kill it, then
+ // we call waitpid which returns with no error and the correct
+ // status. But amusingly enough that doesn't seem to actually reap
+ // the process, but instead it is left around as a Zombie. Probably
+ // the kernel is in the process of switching ownership back to lldb
+ // which was the original parent, and gets confused in the handoff.
+ // Anyway, so call waitpid here to finally reap it.
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ if (platform_sp && platform_sp->IsHost()) {
+ int status;
+ ::pid_t reap_pid;
+ reap_pid = waitpid(GetID(), &status, WNOHANG);
+ if (log)
+ log->Printf("Reaped pid: %d, status: %d.\n", reap_pid, status);
+ }
+#endif
+ SetLastStopPacket(response);
+ ClearThreadIDList();
+ exit_status = response.GetHexU8();
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDestroy - got unexpected response "
+ "to k packet: %s",
+ response.GetStringRef().c_str());
+ exit_string.assign("got unexpected response to k packet: ");
+ exit_string.append(response.GetStringRef());
+ }
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDestroy - failed to send k packet");
+ exit_string.assign("failed to send the k packet");
+ }
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDestroy - killed or interrupted while "
+ "attaching");
+ exit_string.assign("killed or interrupted while attaching.");
+ }
+ } else {
+ // If we missed setting the exit status on the way out, do it here.
+ // NB set exit status can be called multiple times, the first one sets the
+ // status.
+ exit_string.assign("destroying when not connected to debugserver");
+ }
+
+ SetExitStatus(exit_status, exit_string.c_str());
+
+ StopAsyncThread();
+ KillDebugserverProcess();
+ return error;
+}
+
+void ProcessGDBRemote::SetLastStopPacket(
+ const StringExtractorGDBRemote &response) {
+ const bool did_exec =
+ response.GetStringRef().find(";reason:exec;") != std::string::npos;
+ if (did_exec) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::SetLastStopPacket () - detected exec");
+
+ m_thread_list_real.Clear();
+ m_thread_list.Clear();
+ BuildDynamicRegisterInfo(true);
+ m_gdb_comm.ResetDiscoverableSettings(did_exec);
+ }
+
+ // Scope the lock
+ {
+ // Lock the thread stack while we access it
+ std::lock_guard<std::recursive_mutex> guard(m_last_stop_packet_mutex);
+
+ // We are are not using non-stop mode, there can only be one last stop
+ // reply packet, so clear the list.
+ if (!GetTarget().GetNonStopModeEnabled())
+ m_stop_packet_stack.clear();
+
+ // Add this stop packet to the stop packet stack This stack will get popped
+ // and examined when we switch to the Stopped state
+ m_stop_packet_stack.push_back(response);
+ }
+}
+
+void ProcessGDBRemote::SetUnixSignals(const UnixSignalsSP &signals_sp) {
+ Process::SetUnixSignals(std::make_shared<GDBRemoteSignals>(signals_sp));
+}
+
+// Process Queries
+
+bool ProcessGDBRemote::IsAlive() {
+ return m_gdb_comm.IsConnected() && Process::IsAlive();
+}
+
+addr_t ProcessGDBRemote::GetImageInfoAddress() {
+ // request the link map address via the $qShlibInfoAddr packet
+ lldb::addr_t addr = m_gdb_comm.GetShlibInfoAddr();
+
+ // the loaded module list can also provides a link map address
+ if (addr == LLDB_INVALID_ADDRESS) {
+ LoadedModuleInfoList list;
+ if (GetLoadedModuleList(list).Success())
+ addr = list.m_link_map;
+ }
+
+ return addr;
+}
+
+void ProcessGDBRemote::WillPublicStop() {
+ // See if the GDB remote client supports the JSON threads info. If so, we
+ // gather stop info for all threads, expedited registers, expedited memory,
+ // runtime queue information (iOS and MacOSX only), and more. Expediting
+ // memory will help stack backtracing be much faster. Expediting registers
+ // will make sure we don't have to read the thread registers for GPRs.
+ m_jthreadsinfo_sp = m_gdb_comm.GetThreadsInfo();
+
+ if (m_jthreadsinfo_sp) {
+ // Now set the stop info for each thread and also expedite any registers
+ // and memory that was in the jThreadsInfo response.
+ StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray();
+ if (thread_infos) {
+ const size_t n = thread_infos->GetSize();
+ for (size_t i = 0; i < n; ++i) {
+ StructuredData::Dictionary *thread_dict =
+ thread_infos->GetItemAtIndex(i)->GetAsDictionary();
+ if (thread_dict)
+ SetThreadStopInfo(thread_dict);
+ }
+ }
+ }
+}
+
+// Process Memory
+size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size,
+ Status &error) {
+ GetMaxMemorySize();
+ bool binary_memory_read = m_gdb_comm.GetxPacketSupported();
+ // M and m packets take 2 bytes for 1 byte of memory
+ size_t max_memory_size =
+ binary_memory_read ? m_max_memory_size : m_max_memory_size / 2;
+ if (size > max_memory_size) {
+ // Keep memory read sizes down to a sane limit. This function will be
+ // called multiple times in order to complete the task by
+ // lldb_private::Process so it is ok to do this.
+ size = max_memory_size;
+ }
+
+ char packet[64];
+ int packet_len;
+ packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64,
+ binary_memory_read ? 'x' : 'm', (uint64_t)addr,
+ (uint64_t)size);
+ assert(packet_len + 1 < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ error.Clear();
+ if (binary_memory_read) {
+ // The lower level GDBRemoteCommunication packet receive layer has
+ // already de-quoted any 0x7d character escaping that was present in
+ // the packet
+
+ size_t data_received_size = response.GetBytesLeft();
+ if (data_received_size > size) {
+ // Don't write past the end of BUF if the remote debug server gave us
+ // too much data for some reason.
+ data_received_size = size;
+ }
+ memcpy(buf, response.GetStringRef().data(), data_received_size);
+ return data_received_size;
+ } else {
+ return response.GetHexBytes(
+ llvm::MutableArrayRef<uint8_t>((uint8_t *)buf, size), '\xdd');
+ }
+ } else if (response.IsErrorResponse())
+ error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr);
+ else if (response.IsUnsupportedResponse())
+ error.SetErrorStringWithFormat(
+ "GDB server does not support reading memory");
+ else
+ error.SetErrorStringWithFormat(
+ "unexpected response to GDB server memory read packet '%s': '%s'",
+ packet, response.GetStringRef().c_str());
+ } else {
+ error.SetErrorStringWithFormat("failed to send packet: '%s'", packet);
+ }
+ return 0;
+}
+
+Status ProcessGDBRemote::WriteObjectFile(
+ std::vector<ObjectFile::LoadableData> entries) {
+ Status error;
+ // Sort the entries by address because some writes, like those to flash
+ // memory, must happen in order of increasing address.
+ std::stable_sort(
+ std::begin(entries), std::end(entries),
+ [](const ObjectFile::LoadableData a, const ObjectFile::LoadableData b) {
+ return a.Dest < b.Dest;
+ });
+ m_allow_flash_writes = true;
+ error = Process::WriteObjectFile(entries);
+ if (error.Success())
+ error = FlashDone();
+ else
+ // Even though some of the writing failed, try to send a flash done if some
+ // of the writing succeeded so the flash state is reset to normal, but
+ // don't stomp on the error status that was set in the write failure since
+ // that's the one we want to report back.
+ FlashDone();
+ m_allow_flash_writes = false;
+ return error;
+}
+
+bool ProcessGDBRemote::HasErased(FlashRange range) {
+ auto size = m_erased_flash_ranges.GetSize();
+ for (size_t i = 0; i < size; ++i)
+ if (m_erased_flash_ranges.GetEntryAtIndex(i)->Contains(range))
+ return true;
+ return false;
+}
+
+Status ProcessGDBRemote::FlashErase(lldb::addr_t addr, size_t size) {
+ Status status;
+
+ MemoryRegionInfo region;
+ status = GetMemoryRegionInfo(addr, region);
+ if (!status.Success())
+ return status;
+
+ // The gdb spec doesn't say if erasures are allowed across multiple regions,
+ // but we'll disallow it to be safe and to keep the logic simple by worring
+ // about only one region's block size. DoMemoryWrite is this function's
+ // primary user, and it can easily keep writes within a single memory region
+ if (addr + size > region.GetRange().GetRangeEnd()) {
+ status.SetErrorString("Unable to erase flash in multiple regions");
+ return status;
+ }
+
+ uint64_t blocksize = region.GetBlocksize();
+ if (blocksize == 0) {
+ status.SetErrorString("Unable to erase flash because blocksize is 0");
+ return status;
+ }
+
+ // Erasures can only be done on block boundary adresses, so round down addr
+ // and round up size
+ lldb::addr_t block_start_addr = addr - (addr % blocksize);
+ size += (addr - block_start_addr);
+ if ((size % blocksize) != 0)
+ size += (blocksize - size % blocksize);
+
+ FlashRange range(block_start_addr, size);
+
+ if (HasErased(range))
+ return status;
+
+ // We haven't erased the entire range, but we may have erased part of it.
+ // (e.g., block A is already erased and range starts in A and ends in B). So,
+ // adjust range if necessary to exclude already erased blocks.
+ if (!m_erased_flash_ranges.IsEmpty()) {
+ // Assuming that writes and erasures are done in increasing addr order,
+ // because that is a requirement of the vFlashWrite command. Therefore, we
+ // only need to look at the last range in the list for overlap.
+ const auto &last_range = *m_erased_flash_ranges.Back();
+ if (range.GetRangeBase() < last_range.GetRangeEnd()) {
+ auto overlap = last_range.GetRangeEnd() - range.GetRangeBase();
+ // overlap will be less than range.GetByteSize() or else HasErased()
+ // would have been true
+ range.SetByteSize(range.GetByteSize() - overlap);
+ range.SetRangeBase(range.GetRangeBase() + overlap);
+ }
+ }
+
+ StreamString packet;
+ packet.Printf("vFlashErase:%" PRIx64 ",%" PRIx64, range.GetRangeBase(),
+ (uint64_t)range.GetByteSize());
+
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_erased_flash_ranges.Insert(range, true);
+ } else {
+ if (response.IsErrorResponse())
+ status.SetErrorStringWithFormat("flash erase failed for 0x%" PRIx64,
+ addr);
+ else if (response.IsUnsupportedResponse())
+ status.SetErrorStringWithFormat("GDB server does not support flashing");
+ else
+ status.SetErrorStringWithFormat(
+ "unexpected response to GDB server flash erase packet '%s': '%s'",
+ packet.GetData(), response.GetStringRef().c_str());
+ }
+ } else {
+ status.SetErrorStringWithFormat("failed to send packet: '%s'",
+ packet.GetData());
+ }
+ return status;
+}
+
+Status ProcessGDBRemote::FlashDone() {
+ Status status;
+ // If we haven't erased any blocks, then we must not have written anything
+ // either, so there is no need to actually send a vFlashDone command
+ if (m_erased_flash_ranges.IsEmpty())
+ return status;
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse("vFlashDone", response, true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_erased_flash_ranges.Clear();
+ } else {
+ if (response.IsErrorResponse())
+ status.SetErrorStringWithFormat("flash done failed");
+ else if (response.IsUnsupportedResponse())
+ status.SetErrorStringWithFormat("GDB server does not support flashing");
+ else
+ status.SetErrorStringWithFormat(
+ "unexpected response to GDB server flash done packet: '%s'",
+ response.GetStringRef().c_str());
+ }
+ } else {
+ status.SetErrorStringWithFormat("failed to send flash done packet");
+ }
+ return status;
+}
+
+size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf,
+ size_t size, Status &error) {
+ GetMaxMemorySize();
+ // M and m packets take 2 bytes for 1 byte of memory
+ size_t max_memory_size = m_max_memory_size / 2;
+ if (size > max_memory_size) {
+ // Keep memory read sizes down to a sane limit. This function will be
+ // called multiple times in order to complete the task by
+ // lldb_private::Process so it is ok to do this.
+ size = max_memory_size;
+ }
+
+ StreamGDBRemote packet;
+
+ MemoryRegionInfo region;
+ Status region_status = GetMemoryRegionInfo(addr, region);
+
+ bool is_flash =
+ region_status.Success() && region.GetFlash() == MemoryRegionInfo::eYes;
+
+ if (is_flash) {
+ if (!m_allow_flash_writes) {
+ error.SetErrorString("Writing to flash memory is not allowed");
+ return 0;
+ }
+ // Keep the write within a flash memory region
+ if (addr + size > region.GetRange().GetRangeEnd())
+ size = region.GetRange().GetRangeEnd() - addr;
+ // Flash memory must be erased before it can be written
+ error = FlashErase(addr, size);
+ if (!error.Success())
+ return 0;
+ packet.Printf("vFlashWrite:%" PRIx64 ":", addr);
+ packet.PutEscapedBytes(buf, size);
+ } else {
+ packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size);
+ packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(),
+ endian::InlHostByteOrder());
+ }
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ error.Clear();
+ return size;
+ } else if (response.IsErrorResponse())
+ error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64,
+ addr);
+ else if (response.IsUnsupportedResponse())
+ error.SetErrorStringWithFormat(
+ "GDB server does not support writing memory");
+ else
+ error.SetErrorStringWithFormat(
+ "unexpected response to GDB server memory write packet '%s': '%s'",
+ packet.GetData(), response.GetStringRef().c_str());
+ } else {
+ error.SetErrorStringWithFormat("failed to send packet: '%s'",
+ packet.GetData());
+ }
+ return 0;
+}
+
+lldb::addr_t ProcessGDBRemote::DoAllocateMemory(size_t size,
+ uint32_t permissions,
+ Status &error) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_EXPRESSIONS));
+ addr_t allocated_addr = LLDB_INVALID_ADDRESS;
+
+ if (m_gdb_comm.SupportsAllocDeallocMemory() != eLazyBoolNo) {
+ allocated_addr = m_gdb_comm.AllocateMemory(size, permissions);
+ if (allocated_addr != LLDB_INVALID_ADDRESS ||
+ m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes)
+ return allocated_addr;
+ }
+
+ if (m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolNo) {
+ // Call mmap() to create memory in the inferior..
+ unsigned prot = 0;
+ if (permissions & lldb::ePermissionsReadable)
+ prot |= eMmapProtRead;
+ if (permissions & lldb::ePermissionsWritable)
+ prot |= eMmapProtWrite;
+ if (permissions & lldb::ePermissionsExecutable)
+ prot |= eMmapProtExec;
+
+ if (InferiorCallMmap(this, allocated_addr, 0, size, prot,
+ eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0))
+ m_addr_to_mmap_size[allocated_addr] = size;
+ else {
+ allocated_addr = LLDB_INVALID_ADDRESS;
+ if (log)
+ log->Printf("ProcessGDBRemote::%s no direct stub support for memory "
+ "allocation, and InferiorCallMmap also failed - is stub "
+ "missing register context save/restore capability?",
+ __FUNCTION__);
+ }
+ }
+
+ if (allocated_addr == LLDB_INVALID_ADDRESS)
+ error.SetErrorStringWithFormat(
+ "unable to allocate %" PRIu64 " bytes of memory with permissions %s",
+ (uint64_t)size, GetPermissionsAsCString(permissions));
+ else
+ error.Clear();
+ return allocated_addr;
+}
+
+Status ProcessGDBRemote::GetMemoryRegionInfo(addr_t load_addr,
+ MemoryRegionInfo &region_info) {
+
+ Status error(m_gdb_comm.GetMemoryRegionInfo(load_addr, region_info));
+ return error;
+}
+
+Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num) {
+
+ Status error(m_gdb_comm.GetWatchpointSupportInfo(num));
+ return error;
+}
+
+Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num, bool &after) {
+ Status error(m_gdb_comm.GetWatchpointSupportInfo(
+ num, after, GetTarget().GetArchitecture()));
+ return error;
+}
+
+Status ProcessGDBRemote::DoDeallocateMemory(lldb::addr_t addr) {
+ Status error;
+ LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory();
+
+ switch (supported) {
+ case eLazyBoolCalculate:
+ // We should never be deallocating memory without allocating memory first
+ // so we should never get eLazyBoolCalculate
+ error.SetErrorString(
+ "tried to deallocate memory without ever allocating memory");
+ break;
+
+ case eLazyBoolYes:
+ if (!m_gdb_comm.DeallocateMemory(addr))
+ error.SetErrorStringWithFormat(
+ "unable to deallocate memory at 0x%" PRIx64, addr);
+ break;
+
+ case eLazyBoolNo:
+ // Call munmap() to deallocate memory in the inferior..
+ {
+ MMapMap::iterator pos = m_addr_to_mmap_size.find(addr);
+ if (pos != m_addr_to_mmap_size.end() &&
+ InferiorCallMunmap(this, addr, pos->second))
+ m_addr_to_mmap_size.erase(pos);
+ else
+ error.SetErrorStringWithFormat(
+ "unable to deallocate memory at 0x%" PRIx64, addr);
+ }
+ break;
+ }
+
+ return error;
+}
+
+// Process STDIO
+size_t ProcessGDBRemote::PutSTDIN(const char *src, size_t src_len,
+ Status &error) {
+ if (m_stdio_communication.IsConnected()) {
+ ConnectionStatus status;
+ m_stdio_communication.Write(src, src_len, status, nullptr);
+ } else if (m_stdin_forward) {
+ m_gdb_comm.SendStdinNotification(src, src_len);
+ }
+ return 0;
+}
+
+Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) {
+ Status error;
+ assert(bp_site != nullptr);
+
+ // Get logging info
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS));
+ user_id_t site_id = bp_site->GetID();
+
+ // Get the breakpoint address
+ const addr_t addr = bp_site->GetLoadAddress();
+
+ // Log that a breakpoint was requested
+ if (log)
+ log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64
+ ") address = 0x%" PRIx64,
+ site_id, (uint64_t)addr);
+
+ // Breakpoint already exists and is enabled
+ if (bp_site->IsEnabled()) {
+ if (log)
+ log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64
+ ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)",
+ site_id, (uint64_t)addr);
+ return error;
+ }
+
+ // Get the software breakpoint trap opcode size
+ const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site);
+
+ // SupportsGDBStoppointPacket() simply checks a boolean, indicating if this
+ // breakpoint type is supported by the remote stub. These are set to true by
+ // default, and later set to false only after we receive an unimplemented
+ // response when sending a breakpoint packet. This means initially that
+ // unless we were specifically instructed to use a hardware breakpoint, LLDB
+ // will attempt to set a software breakpoint. HardwareRequired() also queries
+ // a boolean variable which indicates if the user specifically asked for
+ // hardware breakpoints. If true then we will skip over software
+ // breakpoints.
+ if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) &&
+ (!bp_site->HardwareRequired())) {
+ // Try to send off a software breakpoint packet ($Z0)
+ uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket(
+ eBreakpointSoftware, true, addr, bp_op_size);
+ if (error_no == 0) {
+ // The breakpoint was placed successfully
+ bp_site->SetEnabled(true);
+ bp_site->SetType(BreakpointSite::eExternal);
+ return error;
+ }
+
+ // SendGDBStoppointTypePacket() will return an error if it was unable to
+ // set this breakpoint. We need to differentiate between a error specific
+ // to placing this breakpoint or if we have learned that this breakpoint
+ // type is unsupported. To do this, we must test the support boolean for
+ // this breakpoint type to see if it now indicates that this breakpoint
+ // type is unsupported. If they are still supported then we should return
+ // with the error code. If they are now unsupported, then we would like to
+ // fall through and try another form of breakpoint.
+ if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) {
+ if (error_no != UINT8_MAX)
+ error.SetErrorStringWithFormat(
+ "error: %d sending the breakpoint request", errno);
+ else
+ error.SetErrorString("error sending the breakpoint request");
+ return error;
+ }
+
+ // We reach here when software breakpoints have been found to be
+ // unsupported. For future calls to set a breakpoint, we will not attempt
+ // to set a breakpoint with a type that is known not to be supported.
+ if (log)
+ log->Printf("Software breakpoints are unsupported");
+
+ // So we will fall through and try a hardware breakpoint
+ }
+
+ // The process of setting a hardware breakpoint is much the same as above.
+ // We check the supported boolean for this breakpoint type, and if it is
+ // thought to be supported then we will try to set this breakpoint with a
+ // hardware breakpoint.
+ if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) {
+ // Try to send off a hardware breakpoint packet ($Z1)
+ uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket(
+ eBreakpointHardware, true, addr, bp_op_size);
+ if (error_no == 0) {
+ // The breakpoint was placed successfully
+ bp_site->SetEnabled(true);
+ bp_site->SetType(BreakpointSite::eHardware);
+ return error;
+ }
+
+ // Check if the error was something other then an unsupported breakpoint
+ // type
+ if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) {
+ // Unable to set this hardware breakpoint
+ if (error_no != UINT8_MAX)
+ error.SetErrorStringWithFormat(
+ "error: %d sending the hardware breakpoint request "
+ "(hardware breakpoint resources might be exhausted or unavailable)",
+ error_no);
+ else
+ error.SetErrorString("error sending the hardware breakpoint request "
+ "(hardware breakpoint resources "
+ "might be exhausted or unavailable)");
+ return error;
+ }
+
+ // We will reach here when the stub gives an unsupported response to a
+ // hardware breakpoint
+ if (log)
+ log->Printf("Hardware breakpoints are unsupported");
+
+ // Finally we will falling through to a #trap style breakpoint
+ }
+
+ // Don't fall through when hardware breakpoints were specifically requested
+ if (bp_site->HardwareRequired()) {
+ error.SetErrorString("hardware breakpoints are not supported");
+ return error;
+ }
+
+ // As a last resort we want to place a manual breakpoint. An instruction is
+ // placed into the process memory using memory write packets.
+ return EnableSoftwareBreakpoint(bp_site);
+}
+
+Status ProcessGDBRemote::DisableBreakpointSite(BreakpointSite *bp_site) {
+ Status error;
+ assert(bp_site != nullptr);
+ addr_t addr = bp_site->GetLoadAddress();
+ user_id_t site_id = bp_site->GetID();
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64
+ ") addr = 0x%8.8" PRIx64,
+ site_id, (uint64_t)addr);
+
+ if (bp_site->IsEnabled()) {
+ const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site);
+
+ BreakpointSite::Type bp_type = bp_site->GetType();
+ switch (bp_type) {
+ case BreakpointSite::eSoftware:
+ error = DisableSoftwareBreakpoint(bp_site);
+ break;
+
+ case BreakpointSite::eHardware:
+ if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, false,
+ addr, bp_op_size))
+ error.SetErrorToGenericError();
+ break;
+
+ case BreakpointSite::eExternal: {
+ GDBStoppointType stoppoint_type;
+ if (bp_site->IsHardware())
+ stoppoint_type = eBreakpointHardware;
+ else
+ stoppoint_type = eBreakpointSoftware;
+
+ if (m_gdb_comm.SendGDBStoppointTypePacket(stoppoint_type, false, addr,
+ bp_op_size))
+ error.SetErrorToGenericError();
+ } break;
+ }
+ if (error.Success())
+ bp_site->SetEnabled(false);
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)",
+ site_id, (uint64_t)addr);
+ return error;
+ }
+
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+// Pre-requisite: wp != NULL.
+static GDBStoppointType GetGDBStoppointType(Watchpoint *wp) {
+ assert(wp);
+ bool watch_read = wp->WatchpointRead();
+ bool watch_write = wp->WatchpointWrite();
+
+ // watch_read and watch_write cannot both be false.
+ assert(watch_read || watch_write);
+ if (watch_read && watch_write)
+ return eWatchpointReadWrite;
+ else if (watch_read)
+ return eWatchpointRead;
+ else // Must be watch_write, then.
+ return eWatchpointWrite;
+}
+
+Status ProcessGDBRemote::EnableWatchpoint(Watchpoint *wp, bool notify) {
+ Status error;
+ if (wp) {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log(
+ ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")",
+ watchID);
+ if (wp->IsEnabled()) {
+ if (log)
+ log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.",
+ watchID, (uint64_t)addr);
+ return error;
+ }
+
+ GDBStoppointType type = GetGDBStoppointType(wp);
+ // Pass down an appropriate z/Z packet...
+ if (m_gdb_comm.SupportsGDBStoppointPacket(type)) {
+ if (m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr,
+ wp->GetByteSize()) == 0) {
+ wp->SetEnabled(true, notify);
+ return error;
+ } else
+ error.SetErrorString("sending gdb watchpoint packet failed");
+ } else
+ error.SetErrorString("watchpoints not supported");
+ } else {
+ error.SetErrorString("Watchpoint argument was NULL.");
+ }
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+Status ProcessGDBRemote::DisableWatchpoint(Watchpoint *wp, bool notify) {
+ Status error;
+ if (wp) {
+ user_id_t watchID = wp->GetID();
+
+ Log *log(
+ ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS));
+
+ addr_t addr = wp->GetLoadAddress();
+
+ if (log)
+ log->Printf("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64,
+ watchID, (uint64_t)addr);
+
+ if (!wp->IsEnabled()) {
+ if (log)
+ log->Printf("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)",
+ watchID, (uint64_t)addr);
+ // See also 'class WatchpointSentry' within StopInfo.cpp. This disabling
+ // attempt might come from the user-supplied actions, we'll route it in
+ // order for the watchpoint object to intelligently process this action.
+ wp->SetEnabled(false, notify);
+ return error;
+ }
+
+ if (wp->IsHardware()) {
+ GDBStoppointType type = GetGDBStoppointType(wp);
+ // Pass down an appropriate z/Z packet...
+ if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr,
+ wp->GetByteSize()) == 0) {
+ wp->SetEnabled(false, notify);
+ return error;
+ } else
+ error.SetErrorString("sending gdb watchpoint packet failed");
+ }
+ // TODO: clear software watchpoints if we implement them
+ } else {
+ error.SetErrorString("Watchpoint argument was NULL.");
+ }
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+void ProcessGDBRemote::Clear() {
+ m_thread_list_real.Clear();
+ m_thread_list.Clear();
+}
+
+Status ProcessGDBRemote::DoSignal(int signo) {
+ Status error;
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::DoSignal (signal = %d)", signo);
+
+ if (!m_gdb_comm.SendAsyncSignal(signo))
+ error.SetErrorStringWithFormat("failed to send signal %i", signo);
+ return error;
+}
+
+Status ProcessGDBRemote::ConnectToReplayServer(repro::Loader *loader) {
+ if (!loader)
+ return Status("No loader provided.");
+
+ // Construct replay history path.
+ FileSpec history_file = loader->GetFile<ProcessGDBRemoteProvider::Info>();
+ if (!history_file)
+ return Status("No provider for gdb-remote.");
+
+ // Enable replay mode.
+ m_replay_mode = true;
+
+ // Load replay history.
+ if (auto error = m_gdb_replay_server.LoadReplayHistory(history_file))
+ return Status("Unable to load replay history");
+
+ // Make a local connection.
+ if (auto error = GDBRemoteCommunication::ConnectLocally(m_gdb_comm,
+ m_gdb_replay_server))
+ return Status("Unable to connect to replay server");
+
+ // Start server thread.
+ m_gdb_replay_server.StartAsyncThread();
+
+ // Start client thread.
+ StartAsyncThread();
+
+ // Do the usual setup.
+ return ConnectToDebugserver("");
+}
+
+Status
+ProcessGDBRemote::EstablishConnectionIfNeeded(const ProcessInfo &process_info) {
+ // Make sure we aren't already connected?
+ if (m_gdb_comm.IsConnected())
+ return Status();
+
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ if (platform_sp && !platform_sp->IsHost())
+ return Status("Lost debug server connection");
+
+ if (repro::Loader *loader = repro::Reproducer::Instance().GetLoader())
+ return ConnectToReplayServer(loader);
+
+ auto error = LaunchAndConnectToDebugserver(process_info);
+ if (error.Fail()) {
+ const char *error_string = error.AsCString();
+ if (error_string == nullptr)
+ error_string = "unable to launch " DEBUGSERVER_BASENAME;
+ }
+ return error;
+}
+#if !defined(_WIN32)
+#define USE_SOCKETPAIR_FOR_LOCAL_CONNECTION 1
+#endif
+
+#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION
+static bool SetCloexecFlag(int fd) {
+#if defined(FD_CLOEXEC)
+ int flags = ::fcntl(fd, F_GETFD);
+ if (flags == -1)
+ return false;
+ return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
+#else
+ return false;
+#endif
+}
+#endif
+
+Status ProcessGDBRemote::LaunchAndConnectToDebugserver(
+ const ProcessInfo &process_info) {
+ using namespace std::placeholders; // For _1, _2, etc.
+
+ Status error;
+ if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) {
+ // If we locate debugserver, keep that located version around
+ static FileSpec g_debugserver_file_spec;
+
+ ProcessLaunchInfo debugserver_launch_info;
+ // Make debugserver run in its own session so signals generated by special
+ // terminal key sequences (^C) don't affect debugserver.
+ debugserver_launch_info.SetLaunchInSeparateProcessGroup(true);
+
+ const std::weak_ptr<ProcessGDBRemote> this_wp =
+ std::static_pointer_cast<ProcessGDBRemote>(shared_from_this());
+ debugserver_launch_info.SetMonitorProcessCallback(
+ std::bind(MonitorDebugserverProcess, this_wp, _1, _2, _3, _4), false);
+ debugserver_launch_info.SetUserID(process_info.GetUserID());
+
+ int communication_fd = -1;
+#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION
+ // Use a socketpair on non-Windows systems for security and performance
+ // reasons.
+ int sockets[2]; /* the pair of socket descriptors */
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ int our_socket = sockets[0];
+ int gdb_socket = sockets[1];
+ CleanUp cleanup_our(close, our_socket);
+ CleanUp cleanup_gdb(close, gdb_socket);
+
+ // Don't let any child processes inherit our communication socket
+ SetCloexecFlag(our_socket);
+ communication_fd = gdb_socket;
+#endif
+
+ error = m_gdb_comm.StartDebugserverProcess(
+ nullptr, GetTarget().GetPlatform().get(), debugserver_launch_info,
+ nullptr, nullptr, communication_fd);
+
+ if (error.Success())
+ m_debugserver_pid = debugserver_launch_info.GetProcessID();
+ else
+ m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+
+ if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) {
+#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION
+ // Our process spawned correctly, we can now set our connection to use
+ // our end of the socket pair
+ cleanup_our.disable();
+ m_gdb_comm.SetConnection(new ConnectionFileDescriptor(our_socket, true));
+#endif
+ StartAsyncThread();
+ }
+
+ if (error.Fail()) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (log)
+ log->Printf("failed to start debugserver process: %s",
+ error.AsCString());
+ return error;
+ }
+
+ if (m_gdb_comm.IsConnected()) {
+ // Finish the connection process by doing the handshake without
+ // connecting (send NULL URL)
+ error = ConnectToDebugserver("");
+ } else {
+ error.SetErrorString("connection failed");
+ }
+ }
+ return error;
+}
+
+bool ProcessGDBRemote::MonitorDebugserverProcess(
+ std::weak_ptr<ProcessGDBRemote> process_wp, lldb::pid_t debugserver_pid,
+ bool exited, // True if the process did exit
+ int signo, // Zero for no signal
+ int exit_status // Exit value of process if signal is zero
+) {
+ // "debugserver_pid" argument passed in is the process ID for debugserver
+ // that we are tracking...
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ const bool handled = true;
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s(process_wp, pid=%" PRIu64
+ ", signo=%i (0x%x), exit_status=%i)",
+ __FUNCTION__, debugserver_pid, signo, signo, exit_status);
+
+ std::shared_ptr<ProcessGDBRemote> process_sp = process_wp.lock();
+ if (log)
+ log->Printf("ProcessGDBRemote::%s(process = %p)", __FUNCTION__,
+ static_cast<void *>(process_sp.get()));
+ if (!process_sp || process_sp->m_debugserver_pid != debugserver_pid)
+ return handled;
+
+ // Sleep for a half a second to make sure our inferior process has time to
+ // set its exit status before we set it incorrectly when both the debugserver
+ // and the inferior process shut down.
+ usleep(500000);
+ // If our process hasn't yet exited, debugserver might have died. If the
+ // process did exit, then we are reaping it.
+ const StateType state = process_sp->GetState();
+
+ if (state != eStateInvalid && state != eStateUnloaded &&
+ state != eStateExited && state != eStateDetached) {
+ char error_str[1024];
+ if (signo) {
+ const char *signal_cstr =
+ process_sp->GetUnixSignals()->GetSignalAsCString(signo);
+ if (signal_cstr)
+ ::snprintf(error_str, sizeof(error_str),
+ DEBUGSERVER_BASENAME " died with signal %s", signal_cstr);
+ else
+ ::snprintf(error_str, sizeof(error_str),
+ DEBUGSERVER_BASENAME " died with signal %i", signo);
+ } else {
+ ::snprintf(error_str, sizeof(error_str),
+ DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x",
+ exit_status);
+ }
+
+ process_sp->SetExitStatus(-1, error_str);
+ }
+ // Debugserver has exited we need to let our ProcessGDBRemote know that it no
+ // longer has a debugserver instance
+ process_sp->m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ return handled;
+}
+
+void ProcessGDBRemote::KillDebugserverProcess() {
+ m_gdb_comm.Disconnect();
+ if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) {
+ Host::Kill(m_debugserver_pid, SIGINT);
+ m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ }
+}
+
+void ProcessGDBRemote::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+ });
+}
+
+void ProcessGDBRemote::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForProcessPlugin(
+ debugger, PluginProperties::GetSettingName())) {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForProcessPlugin(
+ debugger, GetGlobalPluginProperties()->GetValueProperties(),
+ ConstString("Properties for the gdb-remote process plug-in."),
+ is_global_setting);
+ }
+}
+
+bool ProcessGDBRemote::StartAsyncThread() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s ()", __FUNCTION__);
+
+ std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+ if (!m_async_thread.IsJoinable()) {
+ // Create a thread that watches our internal state and controls which
+ // events make it to clients (into the DCProcess event queue).
+
+ llvm::Expected<HostThread> async_thread = ThreadLauncher::LaunchThread(
+ "<lldb.process.gdb-remote.async>", ProcessGDBRemote::AsyncThread, this);
+ if (!async_thread) {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(async_thread.takeError()));
+ return false;
+ }
+ m_async_thread = *async_thread;
+ } else if (log)
+ log->Printf("ProcessGDBRemote::%s () - Called when Async thread was "
+ "already running.",
+ __FUNCTION__);
+
+ return m_async_thread.IsJoinable();
+}
+
+void ProcessGDBRemote::StopAsyncThread() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s ()", __FUNCTION__);
+
+ std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+ if (m_async_thread.IsJoinable()) {
+ m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit);
+
+ // This will shut down the async thread.
+ m_gdb_comm.Disconnect(); // Disconnect from the debug server.
+
+ // Stop the stdio thread
+ m_async_thread.Join(nullptr);
+ m_async_thread.Reset();
+ } else if (log)
+ log->Printf(
+ "ProcessGDBRemote::%s () - Called when Async thread was not running.",
+ __FUNCTION__);
+}
+
+bool ProcessGDBRemote::HandleNotifyPacket(StringExtractorGDBRemote &packet) {
+ // get the packet at a string
+ const std::string &pkt = packet.GetStringRef();
+ // skip %stop:
+ StringExtractorGDBRemote stop_info(pkt.c_str() + 5);
+
+ // pass as a thread stop info packet
+ SetLastStopPacket(stop_info);
+
+ // check for more stop reasons
+ HandleStopReplySequence();
+
+ // if the process is stopped then we need to fake a resume so that we can
+ // stop properly with the new break. This is possible due to
+ // SetPrivateState() broadcasting the state change as a side effect.
+ if (GetPrivateState() == lldb::StateType::eStateStopped) {
+ SetPrivateState(lldb::StateType::eStateRunning);
+ }
+
+ // since we have some stopped packets we can halt the process
+ SetPrivateState(lldb::StateType::eStateStopped);
+
+ return true;
+}
+
+thread_result_t ProcessGDBRemote::AsyncThread(void *arg) {
+ ProcessGDBRemote *process = (ProcessGDBRemote *)arg;
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") thread starting...",
+ __FUNCTION__, arg, process->GetID());
+
+ EventSP event_sp;
+ bool done = false;
+ while (!done) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") listener.WaitForEvent (NULL, event_sp)...",
+ __FUNCTION__, arg, process->GetID());
+ if (process->m_async_listener_sp->GetEvent(event_sp, llvm::None)) {
+ const uint32_t event_type = event_sp->GetType();
+ if (event_sp->BroadcasterIs(&process->m_async_broadcaster)) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") Got an event of type: %d...",
+ __FUNCTION__, arg, process->GetID(), event_type);
+
+ switch (event_type) {
+ case eBroadcastBitAsyncContinue: {
+ const EventDataBytes *continue_packet =
+ EventDataBytes::GetEventDataFromEvent(event_sp.get());
+
+ if (continue_packet) {
+ const char *continue_cstr =
+ (const char *)continue_packet->GetBytes();
+ const size_t continue_cstr_len = continue_packet->GetByteSize();
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") got eBroadcastBitAsyncContinue: %s",
+ __FUNCTION__, arg, process->GetID(), continue_cstr);
+
+ if (::strstr(continue_cstr, "vAttach") == nullptr)
+ process->SetPrivateState(eStateRunning);
+ StringExtractorGDBRemote response;
+
+ // If in Non-Stop-Mode
+ if (process->GetTarget().GetNonStopModeEnabled()) {
+ // send the vCont packet
+ if (!process->GetGDBRemote().SendvContPacket(
+ llvm::StringRef(continue_cstr, continue_cstr_len),
+ response)) {
+ // Something went wrong
+ done = true;
+ break;
+ }
+ }
+ // If in All-Stop-Mode
+ else {
+ StateType stop_state =
+ process->GetGDBRemote().SendContinuePacketAndWaitForResponse(
+ *process, *process->GetUnixSignals(),
+ llvm::StringRef(continue_cstr, continue_cstr_len),
+ response);
+
+ // We need to immediately clear the thread ID list so we are sure
+ // to get a valid list of threads. The thread ID list might be
+ // contained within the "response", or the stop reply packet that
+ // caused the stop. So clear it now before we give the stop reply
+ // packet to the process using the
+ // process->SetLastStopPacket()...
+ process->ClearThreadIDList();
+
+ switch (stop_state) {
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ process->SetLastStopPacket(response);
+ process->SetPrivateState(stop_state);
+ break;
+
+ case eStateExited: {
+ process->SetLastStopPacket(response);
+ process->ClearThreadIDList();
+ response.SetFilePos(1);
+
+ int exit_status = response.GetHexU8();
+ std::string desc_string;
+ if (response.GetBytesLeft() > 0 &&
+ response.GetChar('-') == ';') {
+ llvm::StringRef desc_str;
+ llvm::StringRef desc_token;
+ while (response.GetNameColonValue(desc_token, desc_str)) {
+ if (desc_token != "description")
+ continue;
+ StringExtractor extractor(desc_str);
+ extractor.GetHexByteString(desc_string);
+ }
+ }
+ process->SetExitStatus(exit_status, desc_string.c_str());
+ done = true;
+ break;
+ }
+ case eStateInvalid: {
+ // Check to see if we were trying to attach and if we got back
+ // the "E87" error code from debugserver -- this indicates that
+ // the process is not debuggable. Return a slightly more
+ // helpful error message about why the attach failed.
+ if (::strstr(continue_cstr, "vAttach") != nullptr &&
+ response.GetError() == 0x87) {
+ process->SetExitStatus(-1, "cannot attach to process due to "
+ "System Integrity Protection");
+ } else if (::strstr(continue_cstr, "vAttach") != nullptr &&
+ response.GetStatus().Fail()) {
+ process->SetExitStatus(-1, response.GetStatus().AsCString());
+ } else {
+ process->SetExitStatus(-1, "lost connection");
+ }
+ break;
+ }
+
+ default:
+ process->SetPrivateState(stop_state);
+ break;
+ } // switch(stop_state)
+ } // else // if in All-stop-mode
+ } // if (continue_packet)
+ } // case eBroadcastBitAysncContinue
+ break;
+
+ case eBroadcastBitAsyncThreadShouldExit:
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") got eBroadcastBitAsyncThreadShouldExit...",
+ __FUNCTION__, arg, process->GetID());
+ done = true;
+ break;
+
+ default:
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") got unknown event 0x%8.8x",
+ __FUNCTION__, arg, process->GetID(), event_type);
+ done = true;
+ break;
+ }
+ } else if (event_sp->BroadcasterIs(&process->m_gdb_comm)) {
+ switch (event_type) {
+ case Communication::eBroadcastBitReadThreadDidExit:
+ process->SetExitStatus(-1, "lost connection");
+ done = true;
+ break;
+
+ case GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify: {
+ lldb_private::Event *event = event_sp.get();
+ const EventDataBytes *continue_packet =
+ EventDataBytes::GetEventDataFromEvent(event);
+ StringExtractorGDBRemote notify(
+ (const char *)continue_packet->GetBytes());
+ // Hand this over to the process to handle
+ process->HandleNotifyPacket(notify);
+ break;
+ }
+
+ default:
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") got unknown event 0x%8.8x",
+ __FUNCTION__, arg, process->GetID(), event_type);
+ done = true;
+ break;
+ }
+ }
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") listener.WaitForEvent (NULL, event_sp) => false",
+ __FUNCTION__, arg, process->GetID());
+ done = true;
+ }
+ }
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") thread exiting...",
+ __FUNCTION__, arg, process->GetID());
+
+ return {};
+}
+
+// uint32_t
+// ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList
+// &matches, std::vector<lldb::pid_t> &pids)
+//{
+// // If we are planning to launch the debugserver remotely, then we need to
+// fire up a debugserver
+// // process and ask it for the list of processes. But if we are local, we
+// can let the Host do it.
+// if (m_local_debugserver)
+// {
+// return Host::ListProcessesMatchingName (name, matches, pids);
+// }
+// else
+// {
+// // FIXME: Implement talking to the remote debugserver.
+// return 0;
+// }
+//
+//}
+//
+bool ProcessGDBRemote::NewThreadNotifyBreakpointHit(
+ void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ // I don't think I have to do anything here, just make sure I notice the new
+ // thread when it starts to
+ // run so I can stop it if that's what I want to do.
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Hit New Thread Notification breakpoint.");
+ return false;
+}
+
+Status ProcessGDBRemote::UpdateAutomaticSignalFiltering() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ LLDB_LOG(log, "Check if need to update ignored signals");
+
+ // QPassSignals package is not supported by the server, there is no way we
+ // can ignore any signals on server side.
+ if (!m_gdb_comm.GetQPassSignalsSupported())
+ return Status();
+
+ // No signals, nothing to send.
+ if (m_unix_signals_sp == nullptr)
+ return Status();
+
+ // Signals' version hasn't changed, no need to send anything.
+ uint64_t new_signals_version = m_unix_signals_sp->GetVersion();
+ if (new_signals_version == m_last_signals_version) {
+ LLDB_LOG(log, "Signals' version hasn't changed. version={0}",
+ m_last_signals_version);
+ return Status();
+ }
+
+ auto signals_to_ignore =
+ m_unix_signals_sp->GetFilteredSignals(false, false, false);
+ Status error = m_gdb_comm.SendSignalsToIgnore(signals_to_ignore);
+
+ LLDB_LOG(log,
+ "Signals' version changed. old version={0}, new version={1}, "
+ "signals ignored={2}, update result={3}",
+ m_last_signals_version, new_signals_version,
+ signals_to_ignore.size(), error);
+
+ if (error.Success())
+ m_last_signals_version = new_signals_version;
+
+ return error;
+}
+
+bool ProcessGDBRemote::StartNoticingNewThreads() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (m_thread_create_bp_sp) {
+ if (log && log->GetVerbose())
+ log->Printf("Enabled noticing new thread breakpoint.");
+ m_thread_create_bp_sp->SetEnabled(true);
+ } else {
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ if (platform_sp) {
+ m_thread_create_bp_sp =
+ platform_sp->SetThreadCreationBreakpoint(GetTarget());
+ if (m_thread_create_bp_sp) {
+ if (log && log->GetVerbose())
+ log->Printf(
+ "Successfully created new thread notification breakpoint %i",
+ m_thread_create_bp_sp->GetID());
+ m_thread_create_bp_sp->SetCallback(
+ ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true);
+ } else {
+ if (log)
+ log->Printf("Failed to create new thread notification breakpoint.");
+ }
+ }
+ }
+ return m_thread_create_bp_sp.get() != nullptr;
+}
+
+bool ProcessGDBRemote::StopNoticingNewThreads() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf("Disabling new thread notification breakpoint.");
+
+ if (m_thread_create_bp_sp)
+ m_thread_create_bp_sp->SetEnabled(false);
+
+ return true;
+}
+
+DynamicLoader *ProcessGDBRemote::GetDynamicLoader() {
+ if (m_dyld_up.get() == nullptr)
+ m_dyld_up.reset(DynamicLoader::FindPlugin(this, nullptr));
+ return m_dyld_up.get();
+}
+
+Status ProcessGDBRemote::SendEventData(const char *data) {
+ int return_value;
+ bool was_supported;
+
+ Status error;
+
+ return_value = m_gdb_comm.SendLaunchEventDataPacket(data, &was_supported);
+ if (return_value != 0) {
+ if (!was_supported)
+ error.SetErrorString("Sending events is not supported for this process.");
+ else
+ error.SetErrorStringWithFormat("Error sending event data: %d.",
+ return_value);
+ }
+ return error;
+}
+
+DataExtractor ProcessGDBRemote::GetAuxvData() {
+ DataBufferSP buf;
+ if (m_gdb_comm.GetQXferAuxvReadSupported()) {
+ std::string response_string;
+ if (m_gdb_comm.SendPacketsAndConcatenateResponses("qXfer:auxv:read::",
+ response_string) ==
+ GDBRemoteCommunication::PacketResult::Success)
+ buf = std::make_shared<DataBufferHeap>(response_string.c_str(),
+ response_string.length());
+ }
+ return DataExtractor(buf, GetByteOrder(), GetAddressByteSize());
+}
+
+StructuredData::ObjectSP
+ProcessGDBRemote::GetExtendedInfoForThread(lldb::tid_t tid) {
+ StructuredData::ObjectSP object_sp;
+
+ if (m_gdb_comm.GetThreadExtendedInfoSupported()) {
+ StructuredData::ObjectSP args_dict(new StructuredData::Dictionary());
+ SystemRuntime *runtime = GetSystemRuntime();
+ if (runtime) {
+ runtime->AddThreadExtendedInfoPacketHints(args_dict);
+ }
+ args_dict->GetAsDictionary()->AddIntegerItem("thread", tid);
+
+ StreamString packet;
+ packet << "jThreadExtendedInfo:";
+ args_dict->Dump(packet, false);
+
+ // FIXME the final character of a JSON dictionary, '}', is the escape
+ // character in gdb-remote binary mode. lldb currently doesn't escape
+ // these characters in its packet output -- so we add the quoted version of
+ // the } character here manually in case we talk to a debugserver which un-
+ // escapes the characters at packet read time.
+ packet << (char)(0x7d ^ 0x20);
+
+ StringExtractorGDBRemote response;
+ response.SetResponseValidatorToJSON();
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ false) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ StringExtractorGDBRemote::ResponseType response_type =
+ response.GetResponseType();
+ if (response_type == StringExtractorGDBRemote::eResponse) {
+ if (!response.Empty()) {
+ object_sp = StructuredData::ParseJSON(response.GetStringRef());
+ }
+ }
+ }
+ }
+ return object_sp;
+}
+
+StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos(
+ lldb::addr_t image_list_address, lldb::addr_t image_count) {
+
+ StructuredData::ObjectSP args_dict(new StructuredData::Dictionary());
+ args_dict->GetAsDictionary()->AddIntegerItem("image_list_address",
+ image_list_address);
+ args_dict->GetAsDictionary()->AddIntegerItem("image_count", image_count);
+
+ return GetLoadedDynamicLibrariesInfos_sender(args_dict);
+}
+
+StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos() {
+ StructuredData::ObjectSP args_dict(new StructuredData::Dictionary());
+
+ args_dict->GetAsDictionary()->AddBooleanItem("fetch_all_solibs", true);
+
+ return GetLoadedDynamicLibrariesInfos_sender(args_dict);
+}
+
+StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos(
+ const std::vector<lldb::addr_t> &load_addresses) {
+ StructuredData::ObjectSP args_dict(new StructuredData::Dictionary());
+ StructuredData::ArraySP addresses(new StructuredData::Array);
+
+ for (auto addr : load_addresses) {
+ StructuredData::ObjectSP addr_sp(new StructuredData::Integer(addr));
+ addresses->AddItem(addr_sp);
+ }
+
+ args_dict->GetAsDictionary()->AddItem("solib_addresses", addresses);
+
+ return GetLoadedDynamicLibrariesInfos_sender(args_dict);
+}
+
+StructuredData::ObjectSP
+ProcessGDBRemote::GetLoadedDynamicLibrariesInfos_sender(
+ StructuredData::ObjectSP args_dict) {
+ StructuredData::ObjectSP object_sp;
+
+ if (m_gdb_comm.GetLoadedDynamicLibrariesInfosSupported()) {
+ // Scope for the scoped timeout object
+ GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm,
+ std::chrono::seconds(10));
+
+ StreamString packet;
+ packet << "jGetLoadedDynamicLibrariesInfos:";
+ args_dict->Dump(packet, false);
+
+ // FIXME the final character of a JSON dictionary, '}', is the escape
+ // character in gdb-remote binary mode. lldb currently doesn't escape
+ // these characters in its packet output -- so we add the quoted version of
+ // the } character here manually in case we talk to a debugserver which un-
+ // escapes the characters at packet read time.
+ packet << (char)(0x7d ^ 0x20);
+
+ StringExtractorGDBRemote response;
+ response.SetResponseValidatorToJSON();
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ false) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ StringExtractorGDBRemote::ResponseType response_type =
+ response.GetResponseType();
+ if (response_type == StringExtractorGDBRemote::eResponse) {
+ if (!response.Empty()) {
+ object_sp = StructuredData::ParseJSON(response.GetStringRef());
+ }
+ }
+ }
+ }
+ return object_sp;
+}
+
+StructuredData::ObjectSP ProcessGDBRemote::GetSharedCacheInfo() {
+ StructuredData::ObjectSP object_sp;
+ StructuredData::ObjectSP args_dict(new StructuredData::Dictionary());
+
+ if (m_gdb_comm.GetSharedCacheInfoSupported()) {
+ StreamString packet;
+ packet << "jGetSharedCacheInfo:";
+ args_dict->Dump(packet, false);
+
+ // FIXME the final character of a JSON dictionary, '}', is the escape
+ // character in gdb-remote binary mode. lldb currently doesn't escape
+ // these characters in its packet output -- so we add the quoted version of
+ // the } character here manually in case we talk to a debugserver which un-
+ // escapes the characters at packet read time.
+ packet << (char)(0x7d ^ 0x20);
+
+ StringExtractorGDBRemote response;
+ response.SetResponseValidatorToJSON();
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ false) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ StringExtractorGDBRemote::ResponseType response_type =
+ response.GetResponseType();
+ if (response_type == StringExtractorGDBRemote::eResponse) {
+ if (!response.Empty()) {
+ object_sp = StructuredData::ParseJSON(response.GetStringRef());
+ }
+ }
+ }
+ }
+ return object_sp;
+}
+
+Status ProcessGDBRemote::ConfigureStructuredData(
+ ConstString type_name, const StructuredData::ObjectSP &config_sp) {
+ return m_gdb_comm.ConfigureRemoteStructuredData(type_name, config_sp);
+}
+
+// Establish the largest memory read/write payloads we should use. If the
+// remote stub has a max packet size, stay under that size.
+//
+// If the remote stub's max packet size is crazy large, use a reasonable
+// largeish default.
+//
+// If the remote stub doesn't advertise a max packet size, use a conservative
+// default.
+
+void ProcessGDBRemote::GetMaxMemorySize() {
+ const uint64_t reasonable_largeish_default = 128 * 1024;
+ const uint64_t conservative_default = 512;
+
+ if (m_max_memory_size == 0) {
+ uint64_t stub_max_size = m_gdb_comm.GetRemoteMaxPacketSize();
+ if (stub_max_size != UINT64_MAX && stub_max_size != 0) {
+ // Save the stub's claimed maximum packet size
+ m_remote_stub_max_memory_size = stub_max_size;
+
+ // Even if the stub says it can support ginormous packets, don't exceed
+ // our reasonable largeish default packet size.
+ if (stub_max_size > reasonable_largeish_default) {
+ stub_max_size = reasonable_largeish_default;
+ }
+
+ // Memory packet have other overheads too like Maddr,size:#NN Instead of
+ // calculating the bytes taken by size and addr every time, we take a
+ // maximum guess here.
+ if (stub_max_size > 70)
+ stub_max_size -= 32 + 32 + 6;
+ else {
+ // In unlikely scenario that max packet size is less then 70, we will
+ // hope that data being written is small enough to fit.
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(
+ GDBR_LOG_COMM | GDBR_LOG_MEMORY));
+ if (log)
+ log->Warning("Packet size is too small. "
+ "LLDB may face problems while writing memory");
+ }
+
+ m_max_memory_size = stub_max_size;
+ } else {
+ m_max_memory_size = conservative_default;
+ }
+ }
+}
+
+void ProcessGDBRemote::SetUserSpecifiedMaxMemoryTransferSize(
+ uint64_t user_specified_max) {
+ if (user_specified_max != 0) {
+ GetMaxMemorySize();
+
+ if (m_remote_stub_max_memory_size != 0) {
+ if (m_remote_stub_max_memory_size < user_specified_max) {
+ m_max_memory_size = m_remote_stub_max_memory_size; // user specified a
+ // packet size too
+ // big, go as big
+ // as the remote stub says we can go.
+ } else {
+ m_max_memory_size = user_specified_max; // user's packet size is good
+ }
+ } else {
+ m_max_memory_size =
+ user_specified_max; // user's packet size is probably fine
+ }
+ }
+}
+
+bool ProcessGDBRemote::GetModuleSpec(const FileSpec &module_file_spec,
+ const ArchSpec &arch,
+ ModuleSpec &module_spec) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+
+ const ModuleCacheKey key(module_file_spec.GetPath(),
+ arch.GetTriple().getTriple());
+ auto cached = m_cached_module_specs.find(key);
+ if (cached != m_cached_module_specs.end()) {
+ module_spec = cached->second;
+ return bool(module_spec);
+ }
+
+ if (!m_gdb_comm.GetModuleInfo(module_file_spec, arch, module_spec)) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s - failed to get module info for %s:%s",
+ __FUNCTION__, module_file_spec.GetPath().c_str(),
+ arch.GetTriple().getTriple().c_str());
+ return false;
+ }
+
+ if (log) {
+ StreamString stream;
+ module_spec.Dump(stream);
+ log->Printf("ProcessGDBRemote::%s - got module info for (%s:%s) : %s",
+ __FUNCTION__, module_file_spec.GetPath().c_str(),
+ arch.GetTriple().getTriple().c_str(), stream.GetData());
+ }
+
+ m_cached_module_specs[key] = module_spec;
+ return true;
+}
+
+void ProcessGDBRemote::PrefetchModuleSpecs(
+ llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple) {
+ auto module_specs = m_gdb_comm.GetModulesInfo(module_file_specs, triple);
+ if (module_specs) {
+ for (const FileSpec &spec : module_file_specs)
+ m_cached_module_specs[ModuleCacheKey(spec.GetPath(),
+ triple.getTriple())] = ModuleSpec();
+ for (const ModuleSpec &spec : *module_specs)
+ m_cached_module_specs[ModuleCacheKey(spec.GetFileSpec().GetPath(),
+ triple.getTriple())] = spec;
+ }
+}
+
+llvm::VersionTuple ProcessGDBRemote::GetHostOSVersion() {
+ return m_gdb_comm.GetOSVersion();
+}
+
+namespace {
+
+typedef std::vector<std::string> stringVec;
+
+typedef std::vector<struct GdbServerRegisterInfo> GDBServerRegisterVec;
+struct RegisterSetInfo {
+ ConstString name;
+};
+
+typedef std::map<uint32_t, RegisterSetInfo> RegisterSetMap;
+
+struct GdbServerTargetInfo {
+ std::string arch;
+ std::string osabi;
+ stringVec includes;
+ RegisterSetMap reg_set_map;
+};
+
+bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info,
+ GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp,
+ uint32_t &cur_reg_num, uint32_t &reg_offset) {
+ if (!feature_node)
+ return false;
+
+ feature_node.ForEachChildElementWithName(
+ "reg",
+ [&target_info, &dyn_reg_info, &cur_reg_num, &reg_offset,
+ &abi_sp](const XMLNode &reg_node) -> bool {
+ std::string gdb_group;
+ std::string gdb_type;
+ ConstString reg_name;
+ ConstString alt_name;
+ ConstString set_name;
+ std::vector<uint32_t> value_regs;
+ std::vector<uint32_t> invalidate_regs;
+ std::vector<uint8_t> dwarf_opcode_bytes;
+ bool encoding_set = false;
+ bool format_set = false;
+ RegisterInfo reg_info = {
+ nullptr, // Name
+ nullptr, // Alt name
+ 0, // byte size
+ reg_offset, // offset
+ eEncodingUint, // encoding
+ eFormatHex, // format
+ {
+ LLDB_INVALID_REGNUM, // eh_frame reg num
+ LLDB_INVALID_REGNUM, // DWARF reg num
+ LLDB_INVALID_REGNUM, // generic reg num
+ cur_reg_num, // process plugin reg num
+ cur_reg_num // native register number
+ },
+ nullptr,
+ nullptr,
+ nullptr, // Dwarf Expression opcode bytes pointer
+ 0 // Dwarf Expression opcode bytes length
+ };
+
+ reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type,
+ &reg_name, &alt_name, &set_name, &value_regs,
+ &invalidate_regs, &encoding_set, &format_set,
+ &reg_info, &reg_offset, &dwarf_opcode_bytes](
+ const llvm::StringRef &name,
+ const llvm::StringRef &value) -> bool {
+ if (name == "name") {
+ reg_name.SetString(value);
+ } else if (name == "bitsize") {
+ reg_info.byte_size =
+ StringConvert::ToUInt32(value.data(), 0, 0) / CHAR_BIT;
+ } else if (name == "type") {
+ gdb_type = value.str();
+ } else if (name == "group") {
+ gdb_group = value.str();
+ } else if (name == "regnum") {
+ const uint32_t regnum =
+ StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0);
+ if (regnum != LLDB_INVALID_REGNUM) {
+ reg_info.kinds[eRegisterKindProcessPlugin] = regnum;
+ }
+ } else if (name == "offset") {
+ reg_offset = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0);
+ } else if (name == "altname") {
+ alt_name.SetString(value);
+ } else if (name == "encoding") {
+ encoding_set = true;
+ reg_info.encoding = Args::StringToEncoding(value, eEncodingUint);
+ } else if (name == "format") {
+ format_set = true;
+ Format format = eFormatInvalid;
+ if (OptionArgParser::ToFormat(value.data(), format, nullptr)
+ .Success())
+ reg_info.format = format;
+ else if (value == "vector-sint8")
+ reg_info.format = eFormatVectorOfSInt8;
+ else if (value == "vector-uint8")
+ reg_info.format = eFormatVectorOfUInt8;
+ else if (value == "vector-sint16")
+ reg_info.format = eFormatVectorOfSInt16;
+ else if (value == "vector-uint16")
+ reg_info.format = eFormatVectorOfUInt16;
+ else if (value == "vector-sint32")
+ reg_info.format = eFormatVectorOfSInt32;
+ else if (value == "vector-uint32")
+ reg_info.format = eFormatVectorOfUInt32;
+ else if (value == "vector-float32")
+ reg_info.format = eFormatVectorOfFloat32;
+ else if (value == "vector-uint64")
+ reg_info.format = eFormatVectorOfUInt64;
+ else if (value == "vector-uint128")
+ reg_info.format = eFormatVectorOfUInt128;
+ } else if (name == "group_id") {
+ const uint32_t set_id =
+ StringConvert::ToUInt32(value.data(), UINT32_MAX, 0);
+ RegisterSetMap::const_iterator pos =
+ target_info.reg_set_map.find(set_id);
+ if (pos != target_info.reg_set_map.end())
+ set_name = pos->second.name;
+ } else if (name == "gcc_regnum" || name == "ehframe_regnum") {
+ reg_info.kinds[eRegisterKindEHFrame] =
+ StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0);
+ } else if (name == "dwarf_regnum") {
+ reg_info.kinds[eRegisterKindDWARF] =
+ StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0);
+ } else if (name == "generic") {
+ reg_info.kinds[eRegisterKindGeneric] =
+ Args::StringToGenericRegister(value);
+ } else if (name == "value_regnums") {
+ SplitCommaSeparatedRegisterNumberString(value, value_regs, 0);
+ } else if (name == "invalidate_regnums") {
+ SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 0);
+ } else if (name == "dynamic_size_dwarf_expr_bytes") {
+ StringExtractor opcode_extractor;
+ std::string opcode_string = value.str();
+ size_t dwarf_opcode_len = opcode_string.length() / 2;
+ assert(dwarf_opcode_len > 0);
+
+ dwarf_opcode_bytes.resize(dwarf_opcode_len);
+ reg_info.dynamic_size_dwarf_len = dwarf_opcode_len;
+ opcode_extractor.GetStringRef().swap(opcode_string);
+ uint32_t ret_val =
+ opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes);
+ assert(dwarf_opcode_len == ret_val);
+ UNUSED_IF_ASSERT_DISABLED(ret_val);
+ reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data();
+ } else {
+ printf("unhandled attribute %s = %s\n", name.data(), value.data());
+ }
+ return true; // Keep iterating through all attributes
+ });
+
+ if (!gdb_type.empty() && !(encoding_set || format_set)) {
+ if (gdb_type.find("int") == 0) {
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ } else if (gdb_type == "data_ptr" || gdb_type == "code_ptr") {
+ reg_info.format = eFormatAddressInfo;
+ reg_info.encoding = eEncodingUint;
+ } else if (gdb_type == "i387_ext" || gdb_type == "float") {
+ reg_info.format = eFormatFloat;
+ reg_info.encoding = eEncodingIEEE754;
+ }
+ }
+
+ // Only update the register set name if we didn't get a "reg_set"
+ // attribute. "set_name" will be empty if we didn't have a "reg_set"
+ // attribute.
+ if (!set_name) {
+ if (!gdb_group.empty()) {
+ set_name.SetCString(gdb_group.c_str());
+ } else {
+ // If no register group name provided anywhere,
+ // we'll create a 'general' register set
+ set_name.SetCString("general");
+ }
+ }
+
+ reg_info.byte_offset = reg_offset;
+ assert(reg_info.byte_size != 0);
+ reg_offset += reg_info.byte_size;
+ if (!value_regs.empty()) {
+ value_regs.push_back(LLDB_INVALID_REGNUM);
+ reg_info.value_regs = value_regs.data();
+ }
+ if (!invalidate_regs.empty()) {
+ invalidate_regs.push_back(LLDB_INVALID_REGNUM);
+ reg_info.invalidate_regs = invalidate_regs.data();
+ }
+
+ ++cur_reg_num;
+ AugmentRegisterInfoViaABI(reg_info, reg_name, abi_sp);
+ dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name);
+
+ return true; // Keep iterating through all "reg" elements
+ });
+ return true;
+}
+
+} // namespace
+
+// This method fetches a register description feature xml file from
+// the remote stub and adds registers/register groupsets/architecture
+// information to the current process. It will call itself recursively
+// for nested register definition files. It returns true if it was able
+// to fetch and parse an xml file.
+bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess(ArchSpec &arch_to_use,
+ std::string xml_filename,
+ uint32_t &cur_reg_num,
+ uint32_t &reg_offset) {
+ // request the target xml file
+ std::string raw;
+ lldb_private::Status lldberr;
+ if (!m_gdb_comm.ReadExtFeature(ConstString("features"),
+ ConstString(xml_filename.c_str()),
+ raw, lldberr)) {
+ return false;
+ }
+
+ XMLDocument xml_document;
+
+ if (xml_document.ParseMemory(raw.c_str(), raw.size(), xml_filename.c_str())) {
+ GdbServerTargetInfo target_info;
+ std::vector<XMLNode> feature_nodes;
+
+ // The top level feature XML file will start with a <target> tag.
+ XMLNode target_node = xml_document.GetRootElement("target");
+ if (target_node) {
+ target_node.ForEachChildElement([&target_info, &feature_nodes](
+ const XMLNode &node) -> bool {
+ llvm::StringRef name = node.GetName();
+ if (name == "architecture") {
+ node.GetElementText(target_info.arch);
+ } else if (name == "osabi") {
+ node.GetElementText(target_info.osabi);
+ } else if (name == "xi:include" || name == "include") {
+ llvm::StringRef href = node.GetAttributeValue("href");
+ if (!href.empty())
+ target_info.includes.push_back(href.str());
+ } else if (name == "feature") {
+ feature_nodes.push_back(node);
+ } else if (name == "groups") {
+ node.ForEachChildElementWithName(
+ "group", [&target_info](const XMLNode &node) -> bool {
+ uint32_t set_id = UINT32_MAX;
+ RegisterSetInfo set_info;
+
+ node.ForEachAttribute(
+ [&set_id, &set_info](const llvm::StringRef &name,
+ const llvm::StringRef &value) -> bool {
+ if (name == "id")
+ set_id = StringConvert::ToUInt32(value.data(),
+ UINT32_MAX, 0);
+ if (name == "name")
+ set_info.name = ConstString(value);
+ return true; // Keep iterating through all attributes
+ });
+
+ if (set_id != UINT32_MAX)
+ target_info.reg_set_map[set_id] = set_info;
+ return true; // Keep iterating through all "group" elements
+ });
+ }
+ return true; // Keep iterating through all children of the target_node
+ });
+ } else {
+ // In an included XML feature file, we're already "inside" the <target>
+ // tag of the initial XML file; this included file will likely only have
+ // a <feature> tag. Need to check for any more included files in this
+ // <feature> element.
+ XMLNode feature_node = xml_document.GetRootElement("feature");
+ if (feature_node) {
+ feature_nodes.push_back(feature_node);
+ feature_node.ForEachChildElement([&target_info](
+ const XMLNode &node) -> bool {
+ llvm::StringRef name = node.GetName();
+ if (name == "xi:include" || name == "include") {
+ llvm::StringRef href = node.GetAttributeValue("href");
+ if (!href.empty())
+ target_info.includes.push_back(href.str());
+ }
+ return true;
+ });
+ }
+ }
+
+ // If the target.xml includes an architecture entry like
+ // <architecture>i386:x86-64</architecture> (seen from VMWare ESXi)
+ // <architecture>arm</architecture> (seen from Segger JLink on unspecified arm board)
+ // use that if we don't have anything better.
+ if (!arch_to_use.IsValid() && !target_info.arch.empty()) {
+ if (target_info.arch == "i386:x86-64") {
+ // We don't have any information about vendor or OS.
+ arch_to_use.SetTriple("x86_64--");
+ GetTarget().MergeArchitecture(arch_to_use);
+ }
+
+ // SEGGER J-Link jtag boards send this very-generic arch name,
+ // we'll need to use this if we have absolutely nothing better
+ // to work with or the register definitions won't be accepted.
+ if (target_info.arch == "arm") {
+ arch_to_use.SetTriple("arm--");
+ GetTarget().MergeArchitecture(arch_to_use);
+ }
+ }
+
+ if (arch_to_use.IsValid()) {
+ // Don't use Process::GetABI, this code gets called from DidAttach, and
+ // in that context we haven't set the Target's architecture yet, so the
+ // ABI is also potentially incorrect.
+ ABISP abi_to_use_sp = ABI::FindPlugin(shared_from_this(), arch_to_use);
+ for (auto &feature_node : feature_nodes) {
+ ParseRegisters(feature_node, target_info, this->m_register_info,
+ abi_to_use_sp, cur_reg_num, reg_offset);
+ }
+
+ for (const auto &include : target_info.includes) {
+ GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, include,
+ cur_reg_num, reg_offset);
+ }
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+// query the target of gdb-remote for extended target information returns
+// true on success (got register definitions), false on failure (did not).
+bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) {
+ // Make sure LLDB has an XML parser it can use first
+ if (!XMLDocument::XMLEnabled())
+ return false;
+
+ // check that we have extended feature read support
+ if (!m_gdb_comm.GetQXferFeaturesReadSupported())
+ return false;
+
+ uint32_t cur_reg_num = 0;
+ uint32_t reg_offset = 0;
+ if (GetGDBServerRegisterInfoXMLAndProcess (arch_to_use, "target.xml", cur_reg_num, reg_offset))
+ this->m_register_info.Finalize(arch_to_use);
+
+ return m_register_info.GetNumRegisters() > 0;
+}
+
+Status ProcessGDBRemote::GetLoadedModuleList(LoadedModuleInfoList &list) {
+ // Make sure LLDB has an XML parser it can use first
+ if (!XMLDocument::XMLEnabled())
+ return Status(0, ErrorType::eErrorTypeGeneric);
+
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS);
+ if (log)
+ log->Printf("ProcessGDBRemote::%s", __FUNCTION__);
+
+ GDBRemoteCommunicationClient &comm = m_gdb_comm;
+ bool can_use_svr4 = GetGlobalPluginProperties()->GetUseSVR4();
+
+ // check that we have extended feature read support
+ if (can_use_svr4 && comm.GetQXferLibrariesSVR4ReadSupported()) {
+ list.clear();
+
+ // request the loaded library list
+ std::string raw;
+ lldb_private::Status lldberr;
+
+ if (!comm.ReadExtFeature(ConstString("libraries-svr4"), ConstString(""),
+ raw, lldberr))
+ return Status(0, ErrorType::eErrorTypeGeneric);
+
+ // parse the xml file in memory
+ if (log)
+ log->Printf("parsing: %s", raw.c_str());
+ XMLDocument doc;
+
+ if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml"))
+ return Status(0, ErrorType::eErrorTypeGeneric);
+
+ XMLNode root_element = doc.GetRootElement("library-list-svr4");
+ if (!root_element)
+ return Status();
+
+ // main link map structure
+ llvm::StringRef main_lm = root_element.GetAttributeValue("main-lm");
+ if (!main_lm.empty()) {
+ list.m_link_map =
+ StringConvert::ToUInt64(main_lm.data(), LLDB_INVALID_ADDRESS, 0);
+ }
+
+ root_element.ForEachChildElementWithName(
+ "library", [log, &list](const XMLNode &library) -> bool {
+
+ LoadedModuleInfoList::LoadedModuleInfo module;
+
+ library.ForEachAttribute(
+ [&module](const llvm::StringRef &name,
+ const llvm::StringRef &value) -> bool {
+
+ if (name == "name")
+ module.set_name(value.str());
+ else if (name == "lm") {
+ // the address of the link_map struct.
+ module.set_link_map(StringConvert::ToUInt64(
+ value.data(), LLDB_INVALID_ADDRESS, 0));
+ } else if (name == "l_addr") {
+ // the displacement as read from the field 'l_addr' of the
+ // link_map struct.
+ module.set_base(StringConvert::ToUInt64(
+ value.data(), LLDB_INVALID_ADDRESS, 0));
+ // base address is always a displacement, not an absolute
+ // value.
+ module.set_base_is_offset(true);
+ } else if (name == "l_ld") {
+ // the memory address of the libraries PT_DYAMIC section.
+ module.set_dynamic(StringConvert::ToUInt64(
+ value.data(), LLDB_INVALID_ADDRESS, 0));
+ }
+
+ return true; // Keep iterating over all properties of "library"
+ });
+
+ if (log) {
+ std::string name;
+ lldb::addr_t lm = 0, base = 0, ld = 0;
+ bool base_is_offset;
+
+ module.get_name(name);
+ module.get_link_map(lm);
+ module.get_base(base);
+ module.get_base_is_offset(base_is_offset);
+ module.get_dynamic(ld);
+
+ log->Printf("found (link_map:0x%08" PRIx64 ", base:0x%08" PRIx64
+ "[%s], ld:0x%08" PRIx64 ", name:'%s')",
+ lm, base, (base_is_offset ? "offset" : "absolute"), ld,
+ name.c_str());
+ }
+
+ list.add(module);
+ return true; // Keep iterating over all "library" elements in the root
+ // node
+ });
+
+ if (log)
+ log->Printf("found %" PRId32 " modules in total",
+ (int)list.m_list.size());
+ } else if (comm.GetQXferLibrariesReadSupported()) {
+ list.clear();
+
+ // request the loaded library list
+ std::string raw;
+ lldb_private::Status lldberr;
+
+ if (!comm.ReadExtFeature(ConstString("libraries"), ConstString(""), raw,
+ lldberr))
+ return Status(0, ErrorType::eErrorTypeGeneric);
+
+ if (log)
+ log->Printf("parsing: %s", raw.c_str());
+ XMLDocument doc;
+
+ if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml"))
+ return Status(0, ErrorType::eErrorTypeGeneric);
+
+ XMLNode root_element = doc.GetRootElement("library-list");
+ if (!root_element)
+ return Status();
+
+ root_element.ForEachChildElementWithName(
+ "library", [log, &list](const XMLNode &library) -> bool {
+ LoadedModuleInfoList::LoadedModuleInfo module;
+
+ llvm::StringRef name = library.GetAttributeValue("name");
+ module.set_name(name.str());
+
+ // The base address of a given library will be the address of its
+ // first section. Most remotes send only one section for Windows
+ // targets for example.
+ const XMLNode &section =
+ library.FindFirstChildElementWithName("section");
+ llvm::StringRef address = section.GetAttributeValue("address");
+ module.set_base(
+ StringConvert::ToUInt64(address.data(), LLDB_INVALID_ADDRESS, 0));
+ // These addresses are absolute values.
+ module.set_base_is_offset(false);
+
+ if (log) {
+ std::string name;
+ lldb::addr_t base = 0;
+ bool base_is_offset;
+ module.get_name(name);
+ module.get_base(base);
+ module.get_base_is_offset(base_is_offset);
+
+ log->Printf("found (base:0x%08" PRIx64 "[%s], name:'%s')", base,
+ (base_is_offset ? "offset" : "absolute"), name.c_str());
+ }
+
+ list.add(module);
+ return true; // Keep iterating over all "library" elements in the root
+ // node
+ });
+
+ if (log)
+ log->Printf("found %" PRId32 " modules in total",
+ (int)list.m_list.size());
+ } else {
+ return Status(0, ErrorType::eErrorTypeGeneric);
+ }
+
+ return Status();
+}
+
+lldb::ModuleSP ProcessGDBRemote::LoadModuleAtAddress(const FileSpec &file,
+ lldb::addr_t link_map,
+ lldb::addr_t base_addr,
+ bool value_is_offset) {
+ DynamicLoader *loader = GetDynamicLoader();
+ if (!loader)
+ return nullptr;
+
+ return loader->LoadModuleAtAddress(file, link_map, base_addr,
+ value_is_offset);
+}
+
+size_t ProcessGDBRemote::LoadModules(LoadedModuleInfoList &module_list) {
+ using lldb_private::process_gdb_remote::ProcessGDBRemote;
+
+ // request a list of loaded libraries from GDBServer
+ if (GetLoadedModuleList(module_list).Fail())
+ return 0;
+
+ // get a list of all the modules
+ ModuleList new_modules;
+
+ for (LoadedModuleInfoList::LoadedModuleInfo &modInfo : module_list.m_list) {
+ std::string mod_name;
+ lldb::addr_t mod_base;
+ lldb::addr_t link_map;
+ bool mod_base_is_offset;
+
+ bool valid = true;
+ valid &= modInfo.get_name(mod_name);
+ valid &= modInfo.get_base(mod_base);
+ valid &= modInfo.get_base_is_offset(mod_base_is_offset);
+ if (!valid)
+ continue;
+
+ if (!modInfo.get_link_map(link_map))
+ link_map = LLDB_INVALID_ADDRESS;
+
+ FileSpec file(mod_name);
+ FileSystem::Instance().Resolve(file);
+ lldb::ModuleSP module_sp =
+ LoadModuleAtAddress(file, link_map, mod_base, mod_base_is_offset);
+
+ if (module_sp.get())
+ new_modules.Append(module_sp);
+ }
+
+ if (new_modules.GetSize() > 0) {
+ ModuleList removed_modules;
+ Target &target = GetTarget();
+ ModuleList &loaded_modules = m_process->GetTarget().GetImages();
+
+ for (size_t i = 0; i < loaded_modules.GetSize(); ++i) {
+ const lldb::ModuleSP loaded_module = loaded_modules.GetModuleAtIndex(i);
+
+ bool found = false;
+ for (size_t j = 0; j < new_modules.GetSize(); ++j) {
+ if (new_modules.GetModuleAtIndex(j).get() == loaded_module.get())
+ found = true;
+ }
+
+ // The main executable will never be included in libraries-svr4, don't
+ // remove it
+ if (!found &&
+ loaded_module.get() != target.GetExecutableModulePointer()) {
+ removed_modules.Append(loaded_module);
+ }
+ }
+
+ loaded_modules.Remove(removed_modules);
+ m_process->GetTarget().ModulesDidUnload(removed_modules, false);
+
+ new_modules.ForEach([&target](const lldb::ModuleSP module_sp) -> bool {
+ lldb_private::ObjectFile *obj = module_sp->GetObjectFile();
+ if (!obj)
+ return true;
+
+ if (obj->GetType() != ObjectFile::Type::eTypeExecutable)
+ return true;
+
+ lldb::ModuleSP module_copy_sp = module_sp;
+ target.SetExecutableModule(module_copy_sp, eLoadDependentsNo);
+ return false;
+ });
+
+ loaded_modules.AppendIfNeeded(new_modules);
+ m_process->GetTarget().ModulesDidLoad(new_modules);
+ }
+
+ return new_modules.GetSize();
+}
+
+size_t ProcessGDBRemote::LoadModules() {
+ LoadedModuleInfoList module_list;
+ return LoadModules(module_list);
+}
+
+Status ProcessGDBRemote::GetFileLoadAddress(const FileSpec &file,
+ bool &is_loaded,
+ lldb::addr_t &load_addr) {
+ is_loaded = false;
+ load_addr = LLDB_INVALID_ADDRESS;
+
+ std::string file_path = file.GetPath(false);
+ if (file_path.empty())
+ return Status("Empty file name specified");
+
+ StreamString packet;
+ packet.PutCString("qFileLoadAddress:");
+ packet.PutStringAsRawHex8(file_path);
+
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ false) !=
+ GDBRemoteCommunication::PacketResult::Success)
+ return Status("Sending qFileLoadAddress packet failed");
+
+ if (response.IsErrorResponse()) {
+ if (response.GetError() == 1) {
+ // The file is not loaded into the inferior
+ is_loaded = false;
+ load_addr = LLDB_INVALID_ADDRESS;
+ return Status();
+ }
+
+ return Status(
+ "Fetching file load address from remote server returned an error");
+ }
+
+ if (response.IsNormalResponse()) {
+ is_loaded = true;
+ load_addr = response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
+ return Status();
+ }
+
+ return Status(
+ "Unknown error happened during sending the load address packet");
+}
+
+void ProcessGDBRemote::ModulesDidLoad(ModuleList &module_list) {
+ // We must call the lldb_private::Process::ModulesDidLoad () first before we
+ // do anything
+ Process::ModulesDidLoad(module_list);
+
+ // After loading shared libraries, we can ask our remote GDB server if it
+ // needs any symbols.
+ m_gdb_comm.ServeSymbolLookups(this);
+}
+
+void ProcessGDBRemote::HandleAsyncStdout(llvm::StringRef out) {
+ AppendSTDOUT(out.data(), out.size());
+}
+
+static const char *end_delimiter = "--end--;";
+static const int end_delimiter_len = 8;
+
+void ProcessGDBRemote::HandleAsyncMisc(llvm::StringRef data) {
+ std::string input = data.str(); // '1' to move beyond 'A'
+ if (m_partial_profile_data.length() > 0) {
+ m_partial_profile_data.append(input);
+ input = m_partial_profile_data;
+ m_partial_profile_data.clear();
+ }
+
+ size_t found, pos = 0, len = input.length();
+ while ((found = input.find(end_delimiter, pos)) != std::string::npos) {
+ StringExtractorGDBRemote profileDataExtractor(
+ input.substr(pos, found).c_str());
+ std::string profile_data =
+ HarmonizeThreadIdsForProfileData(profileDataExtractor);
+ BroadcastAsyncProfileData(profile_data);
+
+ pos = found + end_delimiter_len;
+ }
+
+ if (pos < len) {
+ // Last incomplete chunk.
+ m_partial_profile_data = input.substr(pos);
+ }
+}
+
+std::string ProcessGDBRemote::HarmonizeThreadIdsForProfileData(
+ StringExtractorGDBRemote &profileDataExtractor) {
+ std::map<uint64_t, uint32_t> new_thread_id_to_used_usec_map;
+ std::string output;
+ llvm::raw_string_ostream output_stream(output);
+ llvm::StringRef name, value;
+
+ // Going to assuming thread_used_usec comes first, else bail out.
+ while (profileDataExtractor.GetNameColonValue(name, value)) {
+ if (name.compare("thread_used_id") == 0) {
+ StringExtractor threadIDHexExtractor(value);
+ uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0);
+
+ bool has_used_usec = false;
+ uint32_t curr_used_usec = 0;
+ llvm::StringRef usec_name, usec_value;
+ uint32_t input_file_pos = profileDataExtractor.GetFilePos();
+ if (profileDataExtractor.GetNameColonValue(usec_name, usec_value)) {
+ if (usec_name.equals("thread_used_usec")) {
+ has_used_usec = true;
+ usec_value.getAsInteger(0, curr_used_usec);
+ } else {
+ // We didn't find what we want, it is probably an older version. Bail
+ // out.
+ profileDataExtractor.SetFilePos(input_file_pos);
+ }
+ }
+
+ if (has_used_usec) {
+ uint32_t prev_used_usec = 0;
+ std::map<uint64_t, uint32_t>::iterator iterator =
+ m_thread_id_to_used_usec_map.find(thread_id);
+ if (iterator != m_thread_id_to_used_usec_map.end()) {
+ prev_used_usec = m_thread_id_to_used_usec_map[thread_id];
+ }
+
+ uint32_t real_used_usec = curr_used_usec - prev_used_usec;
+ // A good first time record is one that runs for at least 0.25 sec
+ bool good_first_time =
+ (prev_used_usec == 0) && (real_used_usec > 250000);
+ bool good_subsequent_time =
+ (prev_used_usec > 0) &&
+ ((real_used_usec > 0) || (HasAssignedIndexIDToThread(thread_id)));
+
+ if (good_first_time || good_subsequent_time) {
+ // We try to avoid doing too many index id reservation, resulting in
+ // fast increase of index ids.
+
+ output_stream << name << ":";
+ int32_t index_id = AssignIndexIDToThread(thread_id);
+ output_stream << index_id << ";";
+
+ output_stream << usec_name << ":" << usec_value << ";";
+ } else {
+ // Skip past 'thread_used_name'.
+ llvm::StringRef local_name, local_value;
+ profileDataExtractor.GetNameColonValue(local_name, local_value);
+ }
+
+ // Store current time as previous time so that they can be compared
+ // later.
+ new_thread_id_to_used_usec_map[thread_id] = curr_used_usec;
+ } else {
+ // Bail out and use old string.
+ output_stream << name << ":" << value << ";";
+ }
+ } else {
+ output_stream << name << ":" << value << ";";
+ }
+ }
+ output_stream << end_delimiter;
+ m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map;
+
+ return output_stream.str();
+}
+
+void ProcessGDBRemote::HandleStopReply() {
+ if (GetStopID() != 0)
+ return;
+
+ if (GetID() == LLDB_INVALID_PROCESS_ID) {
+ lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ SetID(pid);
+ }
+ BuildDynamicRegisterInfo(true);
+}
+
+static const char *const s_async_json_packet_prefix = "JSON-async:";
+
+static StructuredData::ObjectSP
+ParseStructuredDataPacket(llvm::StringRef packet) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (!packet.consume_front(s_async_json_packet_prefix)) {
+ if (log) {
+ log->Printf(
+ "GDBRemoteCommunicationClientBase::%s() received $J packet "
+ "but was not a StructuredData packet: packet starts with "
+ "%s",
+ __FUNCTION__,
+ packet.slice(0, strlen(s_async_json_packet_prefix)).str().c_str());
+ }
+ return StructuredData::ObjectSP();
+ }
+
+ // This is an asynchronous JSON packet, destined for a StructuredDataPlugin.
+ StructuredData::ObjectSP json_sp = StructuredData::ParseJSON(packet);
+ if (log) {
+ if (json_sp) {
+ StreamString json_str;
+ json_sp->Dump(json_str);
+ json_str.Flush();
+ log->Printf("ProcessGDBRemote::%s() "
+ "received Async StructuredData packet: %s",
+ __FUNCTION__, json_str.GetData());
+ } else {
+ log->Printf("ProcessGDBRemote::%s"
+ "() received StructuredData packet:"
+ " parse failure",
+ __FUNCTION__);
+ }
+ }
+ return json_sp;
+}
+
+void ProcessGDBRemote::HandleAsyncStructuredDataPacket(llvm::StringRef data) {
+ auto structured_data_sp = ParseStructuredDataPacket(data);
+ if (structured_data_sp)
+ RouteAsyncStructuredData(structured_data_sp);
+}
+
+class CommandObjectProcessGDBRemoteSpeedTest : public CommandObjectParsed {
+public:
+ CommandObjectProcessGDBRemoteSpeedTest(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process plugin packet speed-test",
+ "Tests packet speeds of various sizes to determine "
+ "the performance characteristics of the GDB remote "
+ "connection. ",
+ nullptr),
+ m_option_group(),
+ m_num_packets(LLDB_OPT_SET_1, false, "count", 'c', 0, eArgTypeCount,
+ "The number of packets to send of each varying size "
+ "(default is 1000).",
+ 1000),
+ m_max_send(LLDB_OPT_SET_1, false, "max-send", 's', 0, eArgTypeCount,
+ "The maximum number of bytes to send in a packet. Sizes "
+ "increase in powers of 2 while the size is less than or "
+ "equal to this option value. (default 1024).",
+ 1024),
+ m_max_recv(LLDB_OPT_SET_1, false, "max-receive", 'r', 0, eArgTypeCount,
+ "The maximum number of bytes to receive in a packet. Sizes "
+ "increase in powers of 2 while the size is less than or "
+ "equal to this option value. (default 1024).",
+ 1024),
+ m_json(LLDB_OPT_SET_1, false, "json", 'j',
+ "Print the output as JSON data for easy parsing.", false, true) {
+ m_option_group.Append(&m_num_packets, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_max_send, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_max_recv, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_json, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectProcessGDBRemoteSpeedTest() override {}
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ ProcessGDBRemote *process =
+ (ProcessGDBRemote *)m_interpreter.GetExecutionContext()
+ .GetProcessPtr();
+ if (process) {
+ StreamSP output_stream_sp(
+ m_interpreter.GetDebugger().GetAsyncOutputStream());
+ result.SetImmediateOutputStream(output_stream_sp);
+
+ const uint32_t num_packets =
+ (uint32_t)m_num_packets.GetOptionValue().GetCurrentValue();
+ const uint64_t max_send = m_max_send.GetOptionValue().GetCurrentValue();
+ const uint64_t max_recv = m_max_recv.GetOptionValue().GetCurrentValue();
+ const bool json = m_json.GetOptionValue().GetCurrentValue();
+ const uint64_t k_recv_amount =
+ 4 * 1024 * 1024; // Receive amount in bytes
+ process->GetGDBRemote().TestPacketSpeed(
+ num_packets, max_send, max_recv, k_recv_amount, json,
+ output_stream_sp ? *output_stream_sp : result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ } else {
+ result.AppendErrorWithFormat("'%s' takes no arguments",
+ m_cmd_name.c_str());
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+protected:
+ OptionGroupOptions m_option_group;
+ OptionGroupUInt64 m_num_packets;
+ OptionGroupUInt64 m_max_send;
+ OptionGroupUInt64 m_max_recv;
+ OptionGroupBoolean m_json;
+};
+
+class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed {
+private:
+public:
+ CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process plugin packet history",
+ "Dumps the packet history buffer. ", nullptr) {}
+
+ ~CommandObjectProcessGDBRemotePacketHistory() override {}
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ ProcessGDBRemote *process =
+ (ProcessGDBRemote *)m_interpreter.GetExecutionContext()
+ .GetProcessPtr();
+ if (process) {
+ process->GetGDBRemote().DumpHistory(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ } else {
+ result.AppendErrorWithFormat("'%s' takes no arguments",
+ m_cmd_name.c_str());
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+};
+
+class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed {
+private:
+public:
+ CommandObjectProcessGDBRemotePacketXferSize(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process plugin packet xfer-size",
+ "Maximum size that lldb will try to read/write one one chunk.",
+ nullptr) {}
+
+ ~CommandObjectProcessGDBRemotePacketXferSize() override {}
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ result.AppendErrorWithFormat("'%s' takes an argument to specify the max "
+ "amount to be transferred when "
+ "reading/writing",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ProcessGDBRemote *process =
+ (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process) {
+ const char *packet_size = command.GetArgumentAtIndex(0);
+ errno = 0;
+ uint64_t user_specified_max = strtoul(packet_size, nullptr, 10);
+ if (errno == 0 && user_specified_max != 0) {
+ process->SetUserSpecifiedMaxMemoryTransferSize(user_specified_max);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+};
+
+class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed {
+private:
+public:
+ CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process plugin packet send",
+ "Send a custom packet through the GDB remote "
+ "protocol and print the answer. "
+ "The packet header and footer will automatically "
+ "be added to the packet prior to sending and "
+ "stripped from the result.",
+ nullptr) {}
+
+ ~CommandObjectProcessGDBRemotePacketSend() override {}
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ result.AppendErrorWithFormat(
+ "'%s' takes a one or more packet content arguments",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ProcessGDBRemote *process =
+ (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process) {
+ for (size_t i = 0; i < argc; ++i) {
+ const char *packet_cstr = command.GetArgumentAtIndex(0);
+ bool send_async = true;
+ StringExtractorGDBRemote response;
+ process->GetGDBRemote().SendPacketAndWaitForResponse(
+ packet_cstr, response, send_async);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ Stream &output_strm = result.GetOutputStream();
+ output_strm.Printf(" packet: %s\n", packet_cstr);
+ std::string &response_str = response.GetStringRef();
+
+ if (strstr(packet_cstr, "qGetProfileData") != nullptr) {
+ response_str = process->HarmonizeThreadIdsForProfileData(response);
+ }
+
+ if (response_str.empty())
+ output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n");
+ else
+ output_strm.Printf("response: %s\n", response.GetStringRef().c_str());
+ }
+ }
+ return true;
+ }
+};
+
+class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw {
+private:
+public:
+ CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "process plugin packet monitor",
+ "Send a qRcmd packet through the GDB remote protocol "
+ "and print the response."
+ "The argument passed to this command will be hex "
+ "encoded into a valid 'qRcmd' packet, sent and the "
+ "response will be printed.") {}
+
+ ~CommandObjectProcessGDBRemotePacketMonitor() override {}
+
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ if (command.empty()) {
+ result.AppendErrorWithFormat("'%s' takes a command string argument",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ProcessGDBRemote *process =
+ (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process) {
+ StreamString packet;
+ packet.PutCString("qRcmd,");
+ packet.PutBytesAsRawHex8(command.data(), command.size());
+
+ bool send_async = true;
+ StringExtractorGDBRemote response;
+ Stream &output_strm = result.GetOutputStream();
+ process->GetGDBRemote().SendPacketAndReceiveResponseWithOutputSupport(
+ packet.GetString(), response, send_async,
+ [&output_strm](llvm::StringRef output) { output_strm << output; });
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ output_strm.Printf(" packet: %s\n", packet.GetData());
+ const std::string &response_str = response.GetStringRef();
+
+ if (response_str.empty())
+ output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n");
+ else
+ output_strm.Printf("response: %s\n", response.GetStringRef().c_str());
+ }
+ return true;
+ }
+};
+
+class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword {
+private:
+public:
+ CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "process plugin packet",
+ "Commands that deal with GDB remote packets.",
+ nullptr) {
+ LoadSubCommand(
+ "history",
+ CommandObjectSP(
+ new CommandObjectProcessGDBRemotePacketHistory(interpreter)));
+ LoadSubCommand(
+ "send", CommandObjectSP(
+ new CommandObjectProcessGDBRemotePacketSend(interpreter)));
+ LoadSubCommand(
+ "monitor",
+ CommandObjectSP(
+ new CommandObjectProcessGDBRemotePacketMonitor(interpreter)));
+ LoadSubCommand(
+ "xfer-size",
+ CommandObjectSP(
+ new CommandObjectProcessGDBRemotePacketXferSize(interpreter)));
+ LoadSubCommand("speed-test",
+ CommandObjectSP(new CommandObjectProcessGDBRemoteSpeedTest(
+ interpreter)));
+ }
+
+ ~CommandObjectProcessGDBRemotePacket() override {}
+};
+
+class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordProcessGDBRemote(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "process plugin",
+ "Commands for operating on a ProcessGDBRemote process.",
+ "process plugin <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "packet",
+ CommandObjectSP(new CommandObjectProcessGDBRemotePacket(interpreter)));
+ }
+
+ ~CommandObjectMultiwordProcessGDBRemote() override {}
+};
+
+CommandObject *ProcessGDBRemote::GetPluginCommandObject() {
+ if (!m_command_sp)
+ m_command_sp = std::make_shared<CommandObjectMultiwordProcessGDBRemote>(
+ GetTarget().GetDebugger().GetCommandInterpreter());
+ return m_command_sp.get();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
new file mode 100644
index 000000000000..9c41fc2e5e98
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -0,0 +1,459 @@
+//===-- ProcessGDBRemote.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessGDBRemote_h_
+#define liblldb_ProcessGDBRemote_h_
+
+#include <atomic>
+#include <map>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include "lldb/Core/LoadedModuleInfoList.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/ThreadSafeValue.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Broadcaster.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamGDBRemote.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractor.h"
+#include "lldb/Utility/StringList.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private-forward.h"
+
+#include "GDBRemoteCommunicationClient.h"
+#include "GDBRemoteCommunicationReplayServer.h"
+#include "GDBRemoteRegisterContext.h"
+
+#include "llvm/ADT/DenseMap.h"
+
+namespace lldb_private {
+namespace repro {
+class Loader;
+}
+namespace process_gdb_remote {
+
+class ThreadGDBRemote;
+
+class ProcessGDBRemote : public Process,
+ private GDBRemoteClientBase::ContinueDelegate {
+public:
+ ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp);
+
+ ~ProcessGDBRemote() override;
+
+ static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file_path);
+
+ static void Initialize();
+
+ static void DebuggerInitialize(Debugger &debugger);
+
+ static void Terminate();
+
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ // Check if a given Process
+ bool CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) override;
+
+ CommandObject *GetPluginCommandObject() override;
+
+ // Creating a new process, or attaching to an existing one
+ Status WillLaunch(Module *module) override;
+
+ Status DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) override;
+
+ void DidLaunch() override;
+
+ Status WillAttachToProcessWithID(lldb::pid_t pid) override;
+
+ Status WillAttachToProcessWithName(const char *process_name,
+ bool wait_for_launch) override;
+
+ Status DoConnectRemote(Stream *strm, llvm::StringRef remote_url) override;
+
+ Status WillLaunchOrAttach();
+
+ Status DoAttachToProcessWithID(lldb::pid_t pid,
+ const ProcessAttachInfo &attach_info) override;
+
+ Status
+ DoAttachToProcessWithName(const char *process_name,
+ const ProcessAttachInfo &attach_info) override;
+
+ void DidAttach(ArchSpec &process_arch) override;
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // Process Control
+ Status WillResume() override;
+
+ Status DoResume() override;
+
+ Status DoHalt(bool &caused_stop) override;
+
+ Status DoDetach(bool keep_stopped) override;
+
+ bool DetachRequiresHalt() override { return true; }
+
+ Status DoSignal(int signal) override;
+
+ Status DoDestroy() override;
+
+ void RefreshStateAfterStop() override;
+
+ void SetUnixSignals(const lldb::UnixSignalsSP &signals_sp);
+
+ // Process Queries
+ bool IsAlive() override;
+
+ lldb::addr_t GetImageInfoAddress() override;
+
+ void WillPublicStop() override;
+
+ // Process Memory
+ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) override;
+
+ Status
+ WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) override;
+
+ size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size,
+ Status &error) override;
+
+ lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions,
+ Status &error) override;
+
+ Status GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &region_info) override;
+
+ Status DoDeallocateMemory(lldb::addr_t ptr) override;
+
+ // Process STDIO
+ size_t PutSTDIN(const char *buf, size_t buf_size, Status &error) override;
+
+ // Process Breakpoints
+ Status EnableBreakpointSite(BreakpointSite *bp_site) override;
+
+ Status DisableBreakpointSite(BreakpointSite *bp_site) override;
+
+ // Process Watchpoints
+ Status EnableWatchpoint(Watchpoint *wp, bool notify = true) override;
+
+ Status DisableWatchpoint(Watchpoint *wp, bool notify = true) override;
+
+ Status GetWatchpointSupportInfo(uint32_t &num) override;
+
+ lldb::user_id_t StartTrace(const TraceOptions &options,
+ Status &error) override;
+
+ Status StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) override;
+
+ Status GetData(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0) override;
+
+ Status GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0) override;
+
+ Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) override;
+
+ Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override;
+
+ bool StartNoticingNewThreads() override;
+
+ bool StopNoticingNewThreads() override;
+
+ GDBRemoteCommunicationClient &GetGDBRemote() { return m_gdb_comm; }
+
+ Status SendEventData(const char *data) override;
+
+ // Override DidExit so we can disconnect from the remote GDB server
+ void DidExit() override;
+
+ void SetUserSpecifiedMaxMemoryTransferSize(uint64_t user_specified_max);
+
+ bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch,
+ ModuleSpec &module_spec) override;
+
+ void PrefetchModuleSpecs(llvm::ArrayRef<FileSpec> module_file_specs,
+ const llvm::Triple &triple) override;
+
+ llvm::VersionTuple GetHostOSVersion() override;
+
+ size_t LoadModules(LoadedModuleInfoList &module_list) override;
+
+ size_t LoadModules() override;
+
+ Status GetFileLoadAddress(const FileSpec &file, bool &is_loaded,
+ lldb::addr_t &load_addr) override;
+
+ void ModulesDidLoad(ModuleList &module_list) override;
+
+ StructuredData::ObjectSP
+ GetLoadedDynamicLibrariesInfos(lldb::addr_t image_list_address,
+ lldb::addr_t image_count) override;
+
+ Status
+ ConfigureStructuredData(ConstString type_name,
+ const StructuredData::ObjectSP &config_sp) override;
+
+ StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos() override;
+
+ StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos(
+ const std::vector<lldb::addr_t> &load_addresses) override;
+
+ StructuredData::ObjectSP
+ GetLoadedDynamicLibrariesInfos_sender(StructuredData::ObjectSP args);
+
+ StructuredData::ObjectSP GetSharedCacheInfo() override;
+
+ std::string HarmonizeThreadIdsForProfileData(
+ StringExtractorGDBRemote &inputStringExtractor);
+
+protected:
+ friend class ThreadGDBRemote;
+ friend class GDBRemoteCommunicationClient;
+ friend class GDBRemoteRegisterContext;
+
+ /// Broadcaster event bits definitions.
+ enum {
+ eBroadcastBitAsyncContinue = (1 << 0),
+ eBroadcastBitAsyncThreadShouldExit = (1 << 1),
+ eBroadcastBitAsyncThreadDidExit = (1 << 2)
+ };
+
+ GDBRemoteCommunicationClient m_gdb_comm;
+ GDBRemoteCommunicationReplayServer m_gdb_replay_server;
+ std::atomic<lldb::pid_t> m_debugserver_pid;
+ std::vector<StringExtractorGDBRemote> m_stop_packet_stack; // The stop packet
+ // stack replaces
+ // the last stop
+ // packet variable
+ std::recursive_mutex m_last_stop_packet_mutex;
+ GDBRemoteDynamicRegisterInfo m_register_info;
+ Broadcaster m_async_broadcaster;
+ lldb::ListenerSP m_async_listener_sp;
+ HostThread m_async_thread;
+ std::recursive_mutex m_async_thread_state_mutex;
+ typedef std::vector<lldb::tid_t> tid_collection;
+ typedef std::vector<std::pair<lldb::tid_t, int>> tid_sig_collection;
+ typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap;
+ typedef std::map<uint32_t, std::string> ExpeditedRegisterMap;
+ tid_collection m_thread_ids; // Thread IDs for all threads. This list gets
+ // updated after stopping
+ std::vector<lldb::addr_t> m_thread_pcs; // PC values for all the threads.
+ StructuredData::ObjectSP m_jstopinfo_sp; // Stop info only for any threads
+ // that have valid stop infos
+ StructuredData::ObjectSP m_jthreadsinfo_sp; // Full stop info, expedited
+ // registers and memory for all
+ // threads if "jThreadsInfo"
+ // packet is supported
+ tid_collection m_continue_c_tids; // 'c' for continue
+ tid_sig_collection m_continue_C_tids; // 'C' for continue with signal
+ tid_collection m_continue_s_tids; // 's' for step
+ tid_sig_collection m_continue_S_tids; // 'S' for step with signal
+ uint64_t m_max_memory_size; // The maximum number of bytes to read/write when
+ // reading and writing memory
+ uint64_t m_remote_stub_max_memory_size; // The maximum memory size the remote
+ // gdb stub can handle
+ MMapMap m_addr_to_mmap_size;
+ lldb::BreakpointSP m_thread_create_bp_sp;
+ bool m_waiting_for_attach;
+ bool m_destroy_tried_resuming;
+ lldb::CommandObjectSP m_command_sp;
+ int64_t m_breakpoint_pc_offset;
+ lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach
+
+ bool m_replay_mode;
+ bool m_allow_flash_writes;
+ using FlashRangeVector = lldb_private::RangeVector<lldb::addr_t, size_t>;
+ using FlashRange = FlashRangeVector::Entry;
+ FlashRangeVector m_erased_flash_ranges;
+
+ // Accessors
+ bool IsRunning(lldb::StateType state) {
+ return state == lldb::eStateRunning || IsStepping(state);
+ }
+
+ bool IsStepping(lldb::StateType state) {
+ return state == lldb::eStateStepping;
+ }
+
+ bool CanResume(lldb::StateType state) { return state == lldb::eStateStopped; }
+
+ bool HasExited(lldb::StateType state) { return state == lldb::eStateExited; }
+
+ bool ProcessIDIsValid() const;
+
+ void Clear();
+
+ bool UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) override;
+
+ Status ConnectToReplayServer(repro::Loader *loader);
+
+ Status EstablishConnectionIfNeeded(const ProcessInfo &process_info);
+
+ Status LaunchAndConnectToDebugserver(const ProcessInfo &process_info);
+
+ void KillDebugserverProcess();
+
+ void BuildDynamicRegisterInfo(bool force);
+
+ void SetLastStopPacket(const StringExtractorGDBRemote &response);
+
+ bool ParsePythonTargetDefinition(const FileSpec &target_definition_fspec);
+
+ DataExtractor GetAuxvData() override;
+
+ StructuredData::ObjectSP GetExtendedInfoForThread(lldb::tid_t tid);
+
+ void GetMaxMemorySize();
+
+ bool CalculateThreadStopInfo(ThreadGDBRemote *thread);
+
+ size_t UpdateThreadPCsFromStopReplyThreadsValue(std::string &value);
+
+ size_t UpdateThreadIDsFromStopReplyThreadsValue(std::string &value);
+
+ bool HandleNotifyPacket(StringExtractorGDBRemote &packet);
+
+ bool StartAsyncThread();
+
+ void StopAsyncThread();
+
+ static lldb::thread_result_t AsyncThread(void *arg);
+
+ static bool
+ MonitorDebugserverProcess(std::weak_ptr<ProcessGDBRemote> process_wp,
+ lldb::pid_t pid, bool exited, int signo,
+ int exit_status);
+
+ lldb::StateType SetThreadStopInfo(StringExtractor &stop_packet);
+
+ bool
+ GetThreadStopInfoFromJSON(ThreadGDBRemote *thread,
+ const StructuredData::ObjectSP &thread_infos_sp);
+
+ lldb::ThreadSP SetThreadStopInfo(StructuredData::Dictionary *thread_dict);
+
+ lldb::ThreadSP
+ SetThreadStopInfo(lldb::tid_t tid,
+ ExpeditedRegisterMap &expedited_register_map, uint8_t signo,
+ const std::string &thread_name, const std::string &reason,
+ const std::string &description, uint32_t exc_type,
+ const std::vector<lldb::addr_t> &exc_data,
+ lldb::addr_t thread_dispatch_qaddr, bool queue_vars_valid,
+ lldb_private::LazyBool associated_with_libdispatch_queue,
+ lldb::addr_t dispatch_queue_t, std::string &queue_name,
+ lldb::QueueKind queue_kind, uint64_t queue_serial);
+
+ void HandleStopReplySequence();
+
+ void ClearThreadIDList();
+
+ bool UpdateThreadIDList();
+
+ void DidLaunchOrAttach(ArchSpec &process_arch);
+
+ Status ConnectToDebugserver(llvm::StringRef host_port);
+
+ const char *GetDispatchQueueNameForThread(lldb::addr_t thread_dispatch_qaddr,
+ std::string &dispatch_queue_name);
+
+ DynamicLoader *GetDynamicLoader() override;
+
+ bool GetGDBServerRegisterInfoXMLAndProcess(ArchSpec &arch_to_use,
+ std::string xml_filename,
+ uint32_t &cur_reg_num,
+ uint32_t &reg_offset);
+
+ // Query remote GDBServer for register information
+ bool GetGDBServerRegisterInfo(ArchSpec &arch);
+
+ // Query remote GDBServer for a detailed loaded library list
+ Status GetLoadedModuleList(LoadedModuleInfoList &);
+
+ lldb::ModuleSP LoadModuleAtAddress(const FileSpec &file,
+ lldb::addr_t link_map,
+ lldb::addr_t base_addr,
+ bool value_is_offset);
+
+ Status UpdateAutomaticSignalFiltering() override;
+
+ Status FlashErase(lldb::addr_t addr, size_t size);
+
+ Status FlashDone();
+
+ bool HasErased(FlashRange range);
+
+private:
+ // For ProcessGDBRemote only
+ std::string m_partial_profile_data;
+ std::map<uint64_t, uint32_t> m_thread_id_to_used_usec_map;
+ uint64_t m_last_signals_version = 0;
+
+ static bool NewThreadNotifyBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ // ContinueDelegate interface
+ void HandleAsyncStdout(llvm::StringRef out) override;
+ void HandleAsyncMisc(llvm::StringRef data) override;
+ void HandleStopReply() override;
+ void HandleAsyncStructuredDataPacket(llvm::StringRef data) override;
+
+ void SetThreadPc(const lldb::ThreadSP &thread_sp, uint64_t index);
+ using ModuleCacheKey = std::pair<std::string, std::string>;
+ // KeyInfo for the cached module spec DenseMap.
+ // The invariant is that all real keys will have the file and architecture
+ // set.
+ // The empty key has an empty file and an empty arch.
+ // The tombstone key has an invalid arch and an empty file.
+ // The comparison and hash functions take the file name and architecture
+ // triple into account.
+ struct ModuleCacheInfo {
+ static ModuleCacheKey getEmptyKey() { return ModuleCacheKey(); }
+
+ static ModuleCacheKey getTombstoneKey() { return ModuleCacheKey("", "T"); }
+
+ static unsigned getHashValue(const ModuleCacheKey &key) {
+ return llvm::hash_combine(key.first, key.second);
+ }
+
+ static bool isEqual(const ModuleCacheKey &LHS, const ModuleCacheKey &RHS) {
+ return LHS == RHS;
+ }
+ };
+
+ llvm::DenseMap<ModuleCacheKey, ModuleSpec, ModuleCacheInfo>
+ m_cached_module_specs;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessGDBRemote);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_ProcessGDBRemote_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
new file mode 100644
index 000000000000..8cadc45824b3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
@@ -0,0 +1,43 @@
+//===-- ProcessGDBRemoteLog.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProcessGDBRemoteLog.h"
+#include "ProcessGDBRemote.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+static constexpr Log::Category g_categories[] = {
+ {{"async"}, {"log asynchronous activity"}, GDBR_LOG_ASYNC},
+ {{"break"}, {"log breakpoints"}, GDBR_LOG_BREAKPOINTS},
+ {{"comm"}, {"log communication activity"}, GDBR_LOG_COMM},
+ {{"packets"}, {"log gdb remote packets"}, GDBR_LOG_PACKETS},
+ {{"memory"}, {"log memory reads and writes"}, GDBR_LOG_MEMORY},
+ {{"data-short"},
+ {"log memory bytes for memory reads and writes for short transactions "
+ "only"},
+ GDBR_LOG_MEMORY_DATA_SHORT},
+ {{"data-long"},
+ {"log memory bytes for memory reads and writes for all transactions"},
+ GDBR_LOG_MEMORY_DATA_LONG},
+ {{"process"}, {"log process events and activities"}, GDBR_LOG_PROCESS},
+ {{"step"}, {"log step related activities"}, GDBR_LOG_STEP},
+ {{"thread"}, {"log thread events and activities"}, GDBR_LOG_THREAD},
+ {{"watch"}, {"log watchpoint related activities"}, GDBR_LOG_WATCHPOINTS},
+};
+
+Log::Channel ProcessGDBRemoteLog::g_channel(g_categories, GDBR_LOG_DEFAULT);
+
+void ProcessGDBRemoteLog::Initialize() {
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ Log::Register("gdb-remote", g_channel);
+ });
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
new file mode 100644
index 000000000000..d9b8d4536afe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
@@ -0,0 +1,46 @@
+//===-- ProcessGDBRemoteLog.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessGDBRemoteLog_h_
+#define liblldb_ProcessGDBRemoteLog_h_
+
+
+#include "lldb/Utility/Log.h"
+
+#define GDBR_LOG_PROCESS (1u << 1)
+#define GDBR_LOG_THREAD (1u << 2)
+#define GDBR_LOG_PACKETS (1u << 3)
+#define GDBR_LOG_MEMORY (1u << 4) // Log memory reads/writes calls
+#define GDBR_LOG_MEMORY_DATA_SHORT \
+ (1u << 5) // Log short memory reads/writes bytes
+#define GDBR_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes
+#define GDBR_LOG_BREAKPOINTS (1u << 7)
+#define GDBR_LOG_WATCHPOINTS (1u << 8)
+#define GDBR_LOG_STEP (1u << 9)
+#define GDBR_LOG_COMM (1u << 10)
+#define GDBR_LOG_ASYNC (1u << 11)
+#define GDBR_LOG_ALL (UINT32_MAX)
+#define GDBR_LOG_DEFAULT GDBR_LOG_PACKETS
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class ProcessGDBRemoteLog {
+ static Log::Channel g_channel;
+
+public:
+ static void Initialize();
+
+ static Log *GetLogIfAllCategoriesSet(uint32_t mask) { return g_channel.GetLogIfAll(mask); }
+ static Log *GetLogIfAnyCategoryIsSet(uint32_t mask) { return g_channel.GetLogIfAny(mask); }
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_ProcessGDBRemoteLog_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
new file mode 100644
index 000000000000..6607bce4488b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
@@ -0,0 +1,343 @@
+//===-- ThreadGDBRemote.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ThreadGDBRemote.h"
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+// Thread Registers
+
+ThreadGDBRemote::ThreadGDBRemote(Process &process, lldb::tid_t tid)
+ : Thread(process, tid), m_thread_name(), m_dispatch_queue_name(),
+ m_thread_dispatch_qaddr(LLDB_INVALID_ADDRESS),
+ m_dispatch_queue_t(LLDB_INVALID_ADDRESS), m_queue_kind(eQueueKindUnknown),
+ m_queue_serial_number(LLDB_INVALID_QUEUE_ID),
+ m_associated_with_libdispatch_queue(eLazyBoolCalculate) {
+ Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD));
+ LLDB_LOG(log, "this = {0}, pid = {1}, tid = {2}", this, process.GetID(),
+ GetID());
+}
+
+ThreadGDBRemote::~ThreadGDBRemote() {
+ ProcessSP process_sp(GetProcess());
+ Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD));
+ LLDB_LOG(log, "this = {0}, pid = {1}, tid = {2}", this,
+ process_sp ? process_sp->GetID() : LLDB_INVALID_PROCESS_ID, GetID());
+ DestroyThread();
+}
+
+const char *ThreadGDBRemote::GetName() {
+ if (m_thread_name.empty())
+ return nullptr;
+ return m_thread_name.c_str();
+}
+
+void ThreadGDBRemote::ClearQueueInfo() {
+ m_dispatch_queue_name.clear();
+ m_queue_kind = eQueueKindUnknown;
+ m_queue_serial_number = 0;
+ m_dispatch_queue_t = LLDB_INVALID_ADDRESS;
+ m_associated_with_libdispatch_queue = eLazyBoolCalculate;
+}
+
+void ThreadGDBRemote::SetQueueInfo(std::string &&queue_name,
+ QueueKind queue_kind, uint64_t queue_serial,
+ addr_t dispatch_queue_t,
+ LazyBool associated_with_libdispatch_queue) {
+ m_dispatch_queue_name = queue_name;
+ m_queue_kind = queue_kind;
+ m_queue_serial_number = queue_serial;
+ m_dispatch_queue_t = dispatch_queue_t;
+ m_associated_with_libdispatch_queue = associated_with_libdispatch_queue;
+}
+
+const char *ThreadGDBRemote::GetQueueName() {
+ // If our cached queue info is valid, then someone called
+ // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned
+ // from the stop reply packet. In this case we trust that the info is valid
+ // in m_dispatch_queue_name without refetching it
+ if (CachedQueueInfoIsValid()) {
+ if (m_dispatch_queue_name.empty())
+ return nullptr;
+ else
+ return m_dispatch_queue_name.c_str();
+ }
+ // Always re-fetch the dispatch queue name since it can change
+
+ if (m_associated_with_libdispatch_queue == eLazyBoolNo)
+ return nullptr;
+
+ if (m_thread_dispatch_qaddr != 0 &&
+ m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ SystemRuntime *runtime = process_sp->GetSystemRuntime();
+ if (runtime)
+ m_dispatch_queue_name =
+ runtime->GetQueueNameFromThreadQAddress(m_thread_dispatch_qaddr);
+ else
+ m_dispatch_queue_name.clear();
+
+ if (!m_dispatch_queue_name.empty())
+ return m_dispatch_queue_name.c_str();
+ }
+ }
+ return nullptr;
+}
+
+QueueKind ThreadGDBRemote::GetQueueKind() {
+ // If our cached queue info is valid, then someone called
+ // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned
+ // from the stop reply packet. In this case we trust that the info is valid
+ // in m_dispatch_queue_name without refetching it
+ if (CachedQueueInfoIsValid()) {
+ return m_queue_kind;
+ }
+
+ if (m_associated_with_libdispatch_queue == eLazyBoolNo)
+ return eQueueKindUnknown;
+
+ if (m_thread_dispatch_qaddr != 0 &&
+ m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ SystemRuntime *runtime = process_sp->GetSystemRuntime();
+ if (runtime)
+ m_queue_kind = runtime->GetQueueKind(m_thread_dispatch_qaddr);
+ return m_queue_kind;
+ }
+ }
+ return eQueueKindUnknown;
+}
+
+queue_id_t ThreadGDBRemote::GetQueueID() {
+ // If our cached queue info is valid, then someone called
+ // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned
+ // from the stop reply packet. In this case we trust that the info is valid
+ // in m_dispatch_queue_name without refetching it
+ if (CachedQueueInfoIsValid())
+ return m_queue_serial_number;
+
+ if (m_associated_with_libdispatch_queue == eLazyBoolNo)
+ return LLDB_INVALID_QUEUE_ID;
+
+ if (m_thread_dispatch_qaddr != 0 &&
+ m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ SystemRuntime *runtime = process_sp->GetSystemRuntime();
+ if (runtime) {
+ return runtime->GetQueueIDFromThreadQAddress(m_thread_dispatch_qaddr);
+ }
+ }
+ }
+ return LLDB_INVALID_QUEUE_ID;
+}
+
+QueueSP ThreadGDBRemote::GetQueue() {
+ queue_id_t queue_id = GetQueueID();
+ QueueSP queue;
+ if (queue_id != LLDB_INVALID_QUEUE_ID) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ queue = process_sp->GetQueueList().FindQueueByID(queue_id);
+ }
+ }
+ return queue;
+}
+
+addr_t ThreadGDBRemote::GetQueueLibdispatchQueueAddress() {
+ if (m_dispatch_queue_t == LLDB_INVALID_ADDRESS) {
+ if (m_thread_dispatch_qaddr != 0 &&
+ m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ SystemRuntime *runtime = process_sp->GetSystemRuntime();
+ if (runtime) {
+ m_dispatch_queue_t =
+ runtime->GetLibdispatchQueueAddressFromThreadQAddress(
+ m_thread_dispatch_qaddr);
+ }
+ }
+ }
+ }
+ return m_dispatch_queue_t;
+}
+
+void ThreadGDBRemote::SetQueueLibdispatchQueueAddress(
+ lldb::addr_t dispatch_queue_t) {
+ m_dispatch_queue_t = dispatch_queue_t;
+}
+
+bool ThreadGDBRemote::ThreadHasQueueInformation() const {
+ return m_thread_dispatch_qaddr != 0 &&
+ m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS &&
+ m_dispatch_queue_t != LLDB_INVALID_ADDRESS &&
+ m_queue_kind != eQueueKindUnknown && m_queue_serial_number != 0;
+}
+
+LazyBool ThreadGDBRemote::GetAssociatedWithLibdispatchQueue() {
+ return m_associated_with_libdispatch_queue;
+}
+
+void ThreadGDBRemote::SetAssociatedWithLibdispatchQueue(
+ LazyBool associated_with_libdispatch_queue) {
+ m_associated_with_libdispatch_queue = associated_with_libdispatch_queue;
+}
+
+StructuredData::ObjectSP ThreadGDBRemote::FetchThreadExtendedInfo() {
+ StructuredData::ObjectSP object_sp;
+ const lldb::user_id_t tid = GetProtocolID();
+ Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD));
+ if (log)
+ log->Printf("Fetching extended information for thread %4.4" PRIx64, tid);
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ ProcessGDBRemote *gdb_process =
+ static_cast<ProcessGDBRemote *>(process_sp.get());
+ object_sp = gdb_process->GetExtendedInfoForThread(tid);
+ }
+ return object_sp;
+}
+
+void ThreadGDBRemote::WillResume(StateType resume_state) {
+ int signo = GetResumeSignal();
+ const lldb::user_id_t tid = GetProtocolID();
+ Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD));
+ if (log)
+ log->Printf("Resuming thread: %4.4" PRIx64 " with state: %s.", tid,
+ StateAsCString(resume_state));
+
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ ProcessGDBRemote *gdb_process =
+ static_cast<ProcessGDBRemote *>(process_sp.get());
+ switch (resume_state) {
+ case eStateSuspended:
+ case eStateStopped:
+ // Don't append anything for threads that should stay stopped.
+ break;
+
+ case eStateRunning:
+ if (gdb_process->GetUnixSignals()->SignalIsValid(signo))
+ gdb_process->m_continue_C_tids.push_back(std::make_pair(tid, signo));
+ else
+ gdb_process->m_continue_c_tids.push_back(tid);
+ break;
+
+ case eStateStepping:
+ if (gdb_process->GetUnixSignals()->SignalIsValid(signo))
+ gdb_process->m_continue_S_tids.push_back(std::make_pair(tid, signo));
+ else
+ gdb_process->m_continue_s_tids.push_back(tid);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void ThreadGDBRemote::RefreshStateAfterStop() {
+ // Invalidate all registers in our register context. We don't set "force" to
+ // true because the stop reply packet might have had some register values
+ // that were expedited and these will already be copied into the register
+ // context by the time this function gets called. The
+ // GDBRemoteRegisterContext class has been made smart enough to detect when
+ // it needs to invalidate which registers are valid by putting hooks in the
+ // register read and register supply functions where they check the process
+ // stop ID and do the right thing.
+ const bool force = false;
+ GetRegisterContext()->InvalidateIfNeeded(force);
+}
+
+bool ThreadGDBRemote::ThreadIDIsValid(lldb::tid_t thread) {
+ return thread != 0;
+}
+
+void ThreadGDBRemote::Dump(Log *log, uint32_t index) {}
+
+bool ThreadGDBRemote::ShouldStop(bool &step_more) { return true; }
+lldb::RegisterContextSP ThreadGDBRemote::GetRegisterContext() {
+ if (!m_reg_context_sp)
+ m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
+ return m_reg_context_sp;
+}
+
+lldb::RegisterContextSP
+ThreadGDBRemote::CreateRegisterContextForFrame(StackFrame *frame) {
+ lldb::RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+ if (concrete_frame_idx == 0) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ ProcessGDBRemote *gdb_process =
+ static_cast<ProcessGDBRemote *>(process_sp.get());
+ // read_all_registers_at_once will be true if 'p' packet is not
+ // supported.
+ bool read_all_registers_at_once =
+ !gdb_process->GetGDBRemote().GetpPacketSupported(GetID());
+ reg_ctx_sp = std::make_shared<GDBRemoteRegisterContext>(
+ *this, concrete_frame_idx, gdb_process->m_register_info,
+ read_all_registers_at_once);
+ }
+ } else {
+ Unwind *unwinder = GetUnwinder();
+ if (unwinder != nullptr)
+ reg_ctx_sp = unwinder->CreateRegisterContextForFrame(frame);
+ }
+ return reg_ctx_sp;
+}
+
+bool ThreadGDBRemote::PrivateSetRegisterValue(uint32_t reg,
+ llvm::ArrayRef<uint8_t> data) {
+ GDBRemoteRegisterContext *gdb_reg_ctx =
+ static_cast<GDBRemoteRegisterContext *>(GetRegisterContext().get());
+ assert(gdb_reg_ctx);
+ return gdb_reg_ctx->PrivateSetRegisterValue(reg, data);
+}
+
+bool ThreadGDBRemote::PrivateSetRegisterValue(uint32_t reg, uint64_t regval) {
+ GDBRemoteRegisterContext *gdb_reg_ctx =
+ static_cast<GDBRemoteRegisterContext *>(GetRegisterContext().get());
+ assert(gdb_reg_ctx);
+ return gdb_reg_ctx->PrivateSetRegisterValue(reg, regval);
+}
+
+bool ThreadGDBRemote::CalculateStopInfo() {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp)
+ return static_cast<ProcessGDBRemote *>(process_sp.get())
+ ->CalculateThreadStopInfo(this);
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h
new file mode 100644
index 000000000000..c74be169abaf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h
@@ -0,0 +1,119 @@
+//===-- ThreadGDBRemote.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadGDBRemote_h_
+#define liblldb_ThreadGDBRemote_h_
+
+#include <string>
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/StructuredData.h"
+
+class StringExtractor;
+
+namespace lldb_private {
+class Process;
+
+namespace process_gdb_remote {
+
+class ProcessGDBRemote;
+
+class ThreadGDBRemote : public Thread {
+public:
+ ThreadGDBRemote(Process &process, lldb::tid_t tid);
+
+ ~ThreadGDBRemote() override;
+
+ void WillResume(lldb::StateType resume_state) override;
+
+ void RefreshStateAfterStop() override;
+
+ const char *GetName() override;
+
+ const char *GetQueueName() override;
+
+ lldb::QueueKind GetQueueKind() override;
+
+ lldb::queue_id_t GetQueueID() override;
+
+ lldb::QueueSP GetQueue() override;
+
+ lldb::addr_t GetQueueLibdispatchQueueAddress() override;
+
+ void SetQueueLibdispatchQueueAddress(lldb::addr_t dispatch_queue_t) override;
+
+ bool ThreadHasQueueInformation() const override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(StackFrame *frame) override;
+
+ void Dump(Log *log, uint32_t index);
+
+ static bool ThreadIDIsValid(lldb::tid_t thread);
+
+ bool ShouldStop(bool &step_more);
+
+ const char *GetBasicInfoAsString();
+
+ void SetName(const char *name) override {
+ if (name && name[0])
+ m_thread_name.assign(name);
+ else
+ m_thread_name.clear();
+ }
+
+ lldb::addr_t GetThreadDispatchQAddr() { return m_thread_dispatch_qaddr; }
+
+ void SetThreadDispatchQAddr(lldb::addr_t thread_dispatch_qaddr) {
+ m_thread_dispatch_qaddr = thread_dispatch_qaddr;
+ }
+
+ void ClearQueueInfo();
+
+ void SetQueueInfo(std::string &&queue_name, lldb::QueueKind queue_kind,
+ uint64_t queue_serial, lldb::addr_t dispatch_queue_t,
+ lldb_private::LazyBool associated_with_libdispatch_queue);
+
+ lldb_private::LazyBool GetAssociatedWithLibdispatchQueue() override;
+
+ void SetAssociatedWithLibdispatchQueue(
+ lldb_private::LazyBool associated_with_libdispatch_queue) override;
+
+ StructuredData::ObjectSP FetchThreadExtendedInfo() override;
+
+protected:
+ friend class ProcessGDBRemote;
+
+ std::string m_thread_name;
+ std::string m_dispatch_queue_name;
+ lldb::addr_t m_thread_dispatch_qaddr;
+ lldb::addr_t m_dispatch_queue_t;
+ lldb::QueueKind
+ m_queue_kind; // Queue info from stop reply/stop info for thread
+ uint64_t
+ m_queue_serial_number; // Queue info from stop reply/stop info for thread
+ lldb_private::LazyBool m_associated_with_libdispatch_queue;
+
+ bool PrivateSetRegisterValue(uint32_t reg, llvm::ArrayRef<uint8_t> data);
+
+ bool PrivateSetRegisterValue(uint32_t reg, uint64_t regval);
+
+ bool CachedQueueInfoIsValid() const {
+ return m_queue_kind != lldb::eQueueKindUnknown;
+ }
+ void SetStopInfoFromPacket(StringExtractor &stop_packet, uint32_t stop_id);
+
+ bool CalculateStopInfo() override;
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_ThreadGDBRemote_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
new file mode 100644
index 000000000000..ff015aa54b76
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
@@ -0,0 +1,615 @@
+//===-- MinidumpParser.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MinidumpParser.h"
+#include "NtStructures.h"
+#include "RegisterContextMinidump_x86_32.h"
+
+#include "Plugins/Process/Utility/LinuxProcMaps.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+
+// C includes
+// C++ includes
+#include <algorithm>
+#include <map>
+#include <vector>
+#include <utility>
+
+using namespace lldb_private;
+using namespace minidump;
+
+llvm::Expected<MinidumpParser>
+MinidumpParser::Create(const lldb::DataBufferSP &data_sp) {
+ auto ExpectedFile = llvm::object::MinidumpFile::create(
+ llvm::MemoryBufferRef(toStringRef(data_sp->GetData()), "minidump"));
+ if (!ExpectedFile)
+ return ExpectedFile.takeError();
+
+ return MinidumpParser(data_sp, std::move(*ExpectedFile));
+}
+
+MinidumpParser::MinidumpParser(lldb::DataBufferSP data_sp,
+ std::unique_ptr<llvm::object::MinidumpFile> file)
+ : m_data_sp(std::move(data_sp)), m_file(std::move(file)) {}
+
+llvm::ArrayRef<uint8_t> MinidumpParser::GetData() {
+ return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes(),
+ m_data_sp->GetByteSize());
+}
+
+llvm::ArrayRef<uint8_t> MinidumpParser::GetStream(StreamType stream_type) {
+ return m_file->getRawStream(stream_type)
+ .getValueOr(llvm::ArrayRef<uint8_t>());
+}
+
+UUID MinidumpParser::GetModuleUUID(const minidump::Module *module) {
+ auto cv_record =
+ GetData().slice(module->CvRecord.RVA, module->CvRecord.DataSize);
+
+ // Read the CV record signature
+ const llvm::support::ulittle32_t *signature = nullptr;
+ Status error = consumeObject(cv_record, signature);
+ if (error.Fail())
+ return UUID();
+
+ const CvSignature cv_signature =
+ static_cast<CvSignature>(static_cast<uint32_t>(*signature));
+
+ if (cv_signature == CvSignature::Pdb70) {
+ const CvRecordPdb70 *pdb70_uuid = nullptr;
+ Status error = consumeObject(cv_record, pdb70_uuid);
+ if (error.Fail())
+ return UUID();
+
+ CvRecordPdb70 swapped;
+ if (!GetArchitecture().GetTriple().isOSBinFormatELF()) {
+ // LLDB's UUID class treats the data as a sequence of bytes, but breakpad
+ // interprets it as a sequence of little-endian fields, which it converts
+ // to big-endian when converting to text. Swap the bytes to big endian so
+ // that the string representation comes out right.
+ swapped = *pdb70_uuid;
+ llvm::sys::swapByteOrder(swapped.Uuid.Data1);
+ llvm::sys::swapByteOrder(swapped.Uuid.Data2);
+ llvm::sys::swapByteOrder(swapped.Uuid.Data3);
+ llvm::sys::swapByteOrder(swapped.Age);
+ pdb70_uuid = &swapped;
+ }
+ if (pdb70_uuid->Age != 0)
+ return UUID::fromOptionalData(pdb70_uuid, sizeof(*pdb70_uuid));
+ return UUID::fromOptionalData(&pdb70_uuid->Uuid, sizeof(pdb70_uuid->Uuid));
+ } else if (cv_signature == CvSignature::ElfBuildId)
+ return UUID::fromOptionalData(cv_record);
+
+ return UUID();
+}
+
+llvm::ArrayRef<minidump::Thread> MinidumpParser::GetThreads() {
+ auto ExpectedThreads = GetMinidumpFile().getThreadList();
+ if (ExpectedThreads)
+ return *ExpectedThreads;
+
+ LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD),
+ ExpectedThreads.takeError(),
+ "Failed to read thread list: {0}");
+ return {};
+}
+
+llvm::ArrayRef<uint8_t>
+MinidumpParser::GetThreadContext(const LocationDescriptor &location) {
+ if (location.RVA + location.DataSize > GetData().size())
+ return {};
+ return GetData().slice(location.RVA, location.DataSize);
+}
+
+llvm::ArrayRef<uint8_t>
+MinidumpParser::GetThreadContext(const minidump::Thread &td) {
+ return GetThreadContext(td.Context);
+}
+
+llvm::ArrayRef<uint8_t>
+MinidumpParser::GetThreadContextWow64(const minidump::Thread &td) {
+ // On Windows, a 32-bit process can run on a 64-bit machine under WOW64. If
+ // the minidump was captured with a 64-bit debugger, then the CONTEXT we just
+ // grabbed from the mini_dump_thread is the one for the 64-bit "native"
+ // process rather than the 32-bit "guest" process we care about. In this
+ // case, we can get the 32-bit CONTEXT from the TEB (Thread Environment
+ // Block) of the 64-bit process.
+ auto teb_mem = GetMemory(td.EnvironmentBlock, sizeof(TEB64));
+ if (teb_mem.empty())
+ return {};
+
+ const TEB64 *wow64teb;
+ Status error = consumeObject(teb_mem, wow64teb);
+ if (error.Fail())
+ return {};
+
+ // Slot 1 of the thread-local storage in the 64-bit TEB points to a structure
+ // that includes the 32-bit CONTEXT (after a ULONG). See:
+ // https://msdn.microsoft.com/en-us/library/ms681670.aspx
+ auto context =
+ GetMemory(wow64teb->tls_slots[1] + 4, sizeof(MinidumpContext_x86_32));
+ if (context.size() < sizeof(MinidumpContext_x86_32))
+ return {};
+
+ return context;
+ // NOTE: We don't currently use the TEB for anything else. If we
+ // need it in the future, the 32-bit TEB is located according to the address
+ // stored in the first slot of the 64-bit TEB (wow64teb.Reserved1[0]).
+}
+
+ArchSpec MinidumpParser::GetArchitecture() {
+ if (m_arch.IsValid())
+ return m_arch;
+
+ // Set the architecture in m_arch
+ llvm::Expected<const SystemInfo &> system_info = m_file->getSystemInfo();
+
+ if (!system_info) {
+ LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS),
+ system_info.takeError(),
+ "Failed to read SystemInfo stream: {0}");
+ return m_arch;
+ }
+
+ // TODO what to do about big endiand flavors of arm ?
+ // TODO set the arm subarch stuff if the minidump has info about it
+
+ llvm::Triple triple;
+ triple.setVendor(llvm::Triple::VendorType::UnknownVendor);
+
+ switch (system_info->ProcessorArch) {
+ case ProcessorArchitecture::X86:
+ triple.setArch(llvm::Triple::ArchType::x86);
+ break;
+ case ProcessorArchitecture::AMD64:
+ triple.setArch(llvm::Triple::ArchType::x86_64);
+ break;
+ case ProcessorArchitecture::ARM:
+ triple.setArch(llvm::Triple::ArchType::arm);
+ break;
+ case ProcessorArchitecture::ARM64:
+ triple.setArch(llvm::Triple::ArchType::aarch64);
+ break;
+ default:
+ triple.setArch(llvm::Triple::ArchType::UnknownArch);
+ break;
+ }
+
+ // TODO add all of the OSes that Minidump/breakpad distinguishes?
+ switch (system_info->PlatformId) {
+ case OSPlatform::Win32S:
+ case OSPlatform::Win32Windows:
+ case OSPlatform::Win32NT:
+ case OSPlatform::Win32CE:
+ triple.setOS(llvm::Triple::OSType::Win32);
+ break;
+ case OSPlatform::Linux:
+ triple.setOS(llvm::Triple::OSType::Linux);
+ break;
+ case OSPlatform::MacOSX:
+ triple.setOS(llvm::Triple::OSType::MacOSX);
+ triple.setVendor(llvm::Triple::Apple);
+ break;
+ case OSPlatform::IOS:
+ triple.setOS(llvm::Triple::OSType::IOS);
+ triple.setVendor(llvm::Triple::Apple);
+ break;
+ case OSPlatform::Android:
+ triple.setOS(llvm::Triple::OSType::Linux);
+ triple.setEnvironment(llvm::Triple::EnvironmentType::Android);
+ break;
+ default: {
+ triple.setOS(llvm::Triple::OSType::UnknownOS);
+ auto ExpectedCSD = m_file->getString(system_info->CSDVersionRVA);
+ if (!ExpectedCSD) {
+ LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS),
+ ExpectedCSD.takeError(),
+ "Failed to CSD Version string: {0}");
+ } else {
+ if (ExpectedCSD->find("Linux") != std::string::npos)
+ triple.setOS(llvm::Triple::OSType::Linux);
+ }
+ break;
+ }
+ }
+ m_arch.SetTriple(triple);
+ return m_arch;
+}
+
+const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() {
+ llvm::ArrayRef<uint8_t> data = GetStream(StreamType::MiscInfo);
+
+ if (data.size() == 0)
+ return nullptr;
+
+ return MinidumpMiscInfo::Parse(data);
+}
+
+llvm::Optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() {
+ llvm::ArrayRef<uint8_t> data = GetStream(StreamType::LinuxProcStatus);
+
+ if (data.size() == 0)
+ return llvm::None;
+
+ return LinuxProcStatus::Parse(data);
+}
+
+llvm::Optional<lldb::pid_t> MinidumpParser::GetPid() {
+ const MinidumpMiscInfo *misc_info = GetMiscInfo();
+ if (misc_info != nullptr) {
+ return misc_info->GetPid();
+ }
+
+ llvm::Optional<LinuxProcStatus> proc_status = GetLinuxProcStatus();
+ if (proc_status.hasValue()) {
+ return proc_status->GetPid();
+ }
+
+ return llvm::None;
+}
+
+llvm::ArrayRef<minidump::Module> MinidumpParser::GetModuleList() {
+ auto ExpectedModules = GetMinidumpFile().getModuleList();
+ if (ExpectedModules)
+ return *ExpectedModules;
+
+ LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES),
+ ExpectedModules.takeError(),
+ "Failed to read module list: {0}");
+ return {};
+}
+
+std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES);
+ auto ExpectedModules = GetMinidumpFile().getModuleList();
+ if (!ExpectedModules) {
+ LLDB_LOG_ERROR(log, ExpectedModules.takeError(),
+ "Failed to read module list: {0}");
+ return {};
+ }
+
+ // map module_name -> filtered_modules index
+ typedef llvm::StringMap<size_t> MapType;
+ MapType module_name_to_filtered_index;
+
+ std::vector<const minidump::Module *> filtered_modules;
+
+ for (const auto &module : *ExpectedModules) {
+ auto ExpectedName = m_file->getString(module.ModuleNameRVA);
+ if (!ExpectedName) {
+ LLDB_LOG_ERROR(log, ExpectedName.takeError(),
+ "Failed to get module name: {0}");
+ continue;
+ }
+
+ MapType::iterator iter;
+ bool inserted;
+ // See if we have inserted this module aready into filtered_modules. If we
+ // haven't insert an entry into module_name_to_filtered_index with the
+ // index where we will insert it if it isn't in the vector already.
+ std::tie(iter, inserted) = module_name_to_filtered_index.try_emplace(
+ *ExpectedName, filtered_modules.size());
+
+ if (inserted) {
+ // This module has not been seen yet, insert it into filtered_modules at
+ // the index that was inserted into module_name_to_filtered_index using
+ // "filtered_modules.size()" above.
+ filtered_modules.push_back(&module);
+ } else {
+ // This module has been seen. Modules are sometimes mentioned multiple
+ // times when they are mapped discontiguously, so find the module with
+ // the lowest "base_of_image" and use that as the filtered module.
+ auto dup_module = filtered_modules[iter->second];
+ if (module.BaseOfImage < dup_module->BaseOfImage)
+ filtered_modules[iter->second] = &module;
+ }
+ }
+ return filtered_modules;
+}
+
+const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() {
+ llvm::ArrayRef<uint8_t> data = GetStream(StreamType::Exception);
+
+ if (data.size() == 0)
+ return nullptr;
+
+ return MinidumpExceptionStream::Parse(data);
+}
+
+llvm::Optional<minidump::Range>
+MinidumpParser::FindMemoryRange(lldb::addr_t addr) {
+ llvm::ArrayRef<uint8_t> data64 = GetStream(StreamType::Memory64List);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES);
+
+ auto ExpectedMemory = GetMinidumpFile().getMemoryList();
+ if (!ExpectedMemory) {
+ LLDB_LOG_ERROR(log, ExpectedMemory.takeError(),
+ "Failed to read memory list: {0}");
+ } else {
+ for (const auto &memory_desc : *ExpectedMemory) {
+ const LocationDescriptor &loc_desc = memory_desc.Memory;
+ const lldb::addr_t range_start = memory_desc.StartOfMemoryRange;
+ const size_t range_size = loc_desc.DataSize;
+
+ if (loc_desc.RVA + loc_desc.DataSize > GetData().size())
+ return llvm::None;
+
+ if (range_start <= addr && addr < range_start + range_size) {
+ auto ExpectedSlice = GetMinidumpFile().getRawData(loc_desc);
+ if (!ExpectedSlice) {
+ LLDB_LOG_ERROR(log, ExpectedSlice.takeError(),
+ "Failed to get memory slice: {0}");
+ return llvm::None;
+ }
+ return minidump::Range(range_start, *ExpectedSlice);
+ }
+ }
+ }
+
+ // Some Minidumps have a Memory64ListStream that captures all the heap memory
+ // (full-memory Minidumps). We can't exactly use the same loop as above,
+ // because the Minidump uses slightly different data structures to describe
+ // those
+
+ if (!data64.empty()) {
+ llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list;
+ uint64_t base_rva;
+ std::tie(memory64_list, base_rva) =
+ MinidumpMemoryDescriptor64::ParseMemory64List(data64);
+
+ if (memory64_list.empty())
+ return llvm::None;
+
+ for (const auto &memory_desc64 : memory64_list) {
+ const lldb::addr_t range_start = memory_desc64.start_of_memory_range;
+ const size_t range_size = memory_desc64.data_size;
+
+ if (base_rva + range_size > GetData().size())
+ return llvm::None;
+
+ if (range_start <= addr && addr < range_start + range_size) {
+ return minidump::Range(range_start,
+ GetData().slice(base_rva, range_size));
+ }
+ base_rva += range_size;
+ }
+ }
+
+ return llvm::None;
+}
+
+llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr,
+ size_t size) {
+ // I don't have a sense of how frequently this is called or how many memory
+ // ranges a Minidump typically has, so I'm not sure if searching for the
+ // appropriate range linearly each time is stupid. Perhaps we should build
+ // an index for faster lookups.
+ llvm::Optional<minidump::Range> range = FindMemoryRange(addr);
+ if (!range)
+ return {};
+
+ // There's at least some overlap between the beginning of the desired range
+ // (addr) and the current range. Figure out where the overlap begins and how
+ // much overlap there is.
+
+ const size_t offset = addr - range->start;
+
+ if (addr < range->start || offset >= range->range_ref.size())
+ return {};
+
+ const size_t overlap = std::min(size, range->range_ref.size() - offset);
+ return range->range_ref.slice(offset, overlap);
+}
+
+static bool
+CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser,
+ std::vector<MemoryRegionInfo> &regions) {
+ auto data = parser.GetStream(StreamType::LinuxMaps);
+ if (data.empty())
+ return false;
+ ParseLinuxMapRegions(llvm::toStringRef(data),
+ [&](const lldb_private::MemoryRegionInfo &region,
+ const lldb_private::Status &status) -> bool {
+ if (status.Success())
+ regions.push_back(region);
+ return true;
+ });
+ return !regions.empty();
+}
+
+static bool
+CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser,
+ std::vector<MemoryRegionInfo> &regions) {
+ auto data = parser.GetStream(StreamType::MemoryInfoList);
+ if (data.empty())
+ return false;
+ auto mem_info_list = MinidumpMemoryInfo::ParseMemoryInfoList(data);
+ if (mem_info_list.empty())
+ return false;
+ constexpr auto yes = MemoryRegionInfo::eYes;
+ constexpr auto no = MemoryRegionInfo::eNo;
+ regions.reserve(mem_info_list.size());
+ for (const auto &entry : mem_info_list) {
+ MemoryRegionInfo region;
+ region.GetRange().SetRangeBase(entry->base_address);
+ region.GetRange().SetByteSize(entry->region_size);
+ region.SetReadable(entry->isReadable() ? yes : no);
+ region.SetWritable(entry->isWritable() ? yes : no);
+ region.SetExecutable(entry->isExecutable() ? yes : no);
+ region.SetMapped(entry->isMapped() ? yes : no);
+ regions.push_back(region);
+ }
+ return !regions.empty();
+}
+
+static bool
+CreateRegionsCacheFromMemoryList(MinidumpParser &parser,
+ std::vector<MemoryRegionInfo> &regions) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES);
+ auto ExpectedMemory = parser.GetMinidumpFile().getMemoryList();
+ if (!ExpectedMemory) {
+ LLDB_LOG_ERROR(log, ExpectedMemory.takeError(),
+ "Failed to read memory list: {0}");
+ return false;
+ }
+ regions.reserve(ExpectedMemory->size());
+ for (const MemoryDescriptor &memory_desc : *ExpectedMemory) {
+ if (memory_desc.Memory.DataSize == 0)
+ continue;
+ MemoryRegionInfo region;
+ region.GetRange().SetRangeBase(memory_desc.StartOfMemoryRange);
+ region.GetRange().SetByteSize(memory_desc.Memory.DataSize);
+ region.SetReadable(MemoryRegionInfo::eYes);
+ region.SetMapped(MemoryRegionInfo::eYes);
+ regions.push_back(region);
+ }
+ regions.shrink_to_fit();
+ return !regions.empty();
+}
+
+static bool
+CreateRegionsCacheFromMemory64List(MinidumpParser &parser,
+ std::vector<MemoryRegionInfo> &regions) {
+ llvm::ArrayRef<uint8_t> data =
+ parser.GetStream(StreamType::Memory64List);
+ if (data.empty())
+ return false;
+ llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list;
+ uint64_t base_rva;
+ std::tie(memory64_list, base_rva) =
+ MinidumpMemoryDescriptor64::ParseMemory64List(data);
+
+ if (memory64_list.empty())
+ return false;
+
+ regions.reserve(memory64_list.size());
+ for (const auto &memory_desc : memory64_list) {
+ if (memory_desc.data_size == 0)
+ continue;
+ MemoryRegionInfo region;
+ region.GetRange().SetRangeBase(memory_desc.start_of_memory_range);
+ region.GetRange().SetByteSize(memory_desc.data_size);
+ region.SetReadable(MemoryRegionInfo::eYes);
+ region.SetMapped(MemoryRegionInfo::eYes);
+ regions.push_back(region);
+ }
+ regions.shrink_to_fit();
+ return !regions.empty();
+}
+
+MemoryRegionInfo
+MinidumpParser::FindMemoryRegion(lldb::addr_t load_addr) const {
+ auto begin = m_regions.begin();
+ auto end = m_regions.end();
+ auto pos = std::lower_bound(begin, end, load_addr);
+ if (pos != end && pos->GetRange().Contains(load_addr))
+ return *pos;
+
+ MemoryRegionInfo region;
+ if (pos == begin)
+ region.GetRange().SetRangeBase(0);
+ else {
+ auto prev = pos - 1;
+ if (prev->GetRange().Contains(load_addr))
+ return *prev;
+ region.GetRange().SetRangeBase(prev->GetRange().GetRangeEnd());
+ }
+ if (pos == end)
+ region.GetRange().SetRangeEnd(UINT64_MAX);
+ else
+ region.GetRange().SetRangeEnd(pos->GetRange().GetRangeBase());
+ region.SetReadable(MemoryRegionInfo::eNo);
+ region.SetWritable(MemoryRegionInfo::eNo);
+ region.SetExecutable(MemoryRegionInfo::eNo);
+ region.SetMapped(MemoryRegionInfo::eNo);
+ return region;
+}
+
+MemoryRegionInfo
+MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) {
+ if (!m_parsed_regions)
+ GetMemoryRegions();
+ return FindMemoryRegion(load_addr);
+}
+
+const MemoryRegionInfos &MinidumpParser::GetMemoryRegions() {
+ if (!m_parsed_regions) {
+ m_parsed_regions = true;
+ // We haven't cached our memory regions yet we will create the region cache
+ // once. We create the region cache using the best source. We start with
+ // the linux maps since they are the most complete and have names for the
+ // regions. Next we try the MemoryInfoList since it has
+ // read/write/execute/map data, and then fall back to the MemoryList and
+ // Memory64List to just get a list of the memory that is mapped in this
+ // core file
+ if (!CreateRegionsCacheFromLinuxMaps(*this, m_regions))
+ if (!CreateRegionsCacheFromMemoryInfoList(*this, m_regions))
+ if (!CreateRegionsCacheFromMemoryList(*this, m_regions))
+ CreateRegionsCacheFromMemory64List(*this, m_regions);
+ llvm::sort(m_regions.begin(), m_regions.end());
+ }
+ return m_regions;
+}
+
+#define ENUM_TO_CSTR(ST) \
+ case StreamType::ST: \
+ return #ST
+
+llvm::StringRef
+MinidumpParser::GetStreamTypeAsString(StreamType stream_type) {
+ switch (stream_type) {
+ ENUM_TO_CSTR(Unused);
+ ENUM_TO_CSTR(ThreadList);
+ ENUM_TO_CSTR(ModuleList);
+ ENUM_TO_CSTR(MemoryList);
+ ENUM_TO_CSTR(Exception);
+ ENUM_TO_CSTR(SystemInfo);
+ ENUM_TO_CSTR(ThreadExList);
+ ENUM_TO_CSTR(Memory64List);
+ ENUM_TO_CSTR(CommentA);
+ ENUM_TO_CSTR(CommentW);
+ ENUM_TO_CSTR(HandleData);
+ ENUM_TO_CSTR(FunctionTable);
+ ENUM_TO_CSTR(UnloadedModuleList);
+ ENUM_TO_CSTR(MiscInfo);
+ ENUM_TO_CSTR(MemoryInfoList);
+ ENUM_TO_CSTR(ThreadInfoList);
+ ENUM_TO_CSTR(HandleOperationList);
+ ENUM_TO_CSTR(Token);
+ ENUM_TO_CSTR(JavascriptData);
+ ENUM_TO_CSTR(SystemMemoryInfo);
+ ENUM_TO_CSTR(ProcessVMCounters);
+ ENUM_TO_CSTR(LastReserved);
+ ENUM_TO_CSTR(BreakpadInfo);
+ ENUM_TO_CSTR(AssertionInfo);
+ ENUM_TO_CSTR(LinuxCPUInfo);
+ ENUM_TO_CSTR(LinuxProcStatus);
+ ENUM_TO_CSTR(LinuxLSBRelease);
+ ENUM_TO_CSTR(LinuxCMDLine);
+ ENUM_TO_CSTR(LinuxEnviron);
+ ENUM_TO_CSTR(LinuxAuxv);
+ ENUM_TO_CSTR(LinuxMaps);
+ ENUM_TO_CSTR(LinuxDSODebug);
+ ENUM_TO_CSTR(LinuxProcStat);
+ ENUM_TO_CSTR(LinuxProcUptime);
+ ENUM_TO_CSTR(LinuxProcFD);
+ ENUM_TO_CSTR(FacebookAppCustomData);
+ ENUM_TO_CSTR(FacebookBuildID);
+ ENUM_TO_CSTR(FacebookAppVersionName);
+ ENUM_TO_CSTR(FacebookJavaStack);
+ ENUM_TO_CSTR(FacebookDalvikInfo);
+ ENUM_TO_CSTR(FacebookUnwindSymbols);
+ ENUM_TO_CSTR(FacebookDumpErrorLog);
+ ENUM_TO_CSTR(FacebookAppStateLog);
+ ENUM_TO_CSTR(FacebookAbortReason);
+ ENUM_TO_CSTR(FacebookThreadName);
+ ENUM_TO_CSTR(FacebookLogcat);
+ }
+ return "unknown stream type";
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h
new file mode 100644
index 000000000000..fce64f0ed5fc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h
@@ -0,0 +1,115 @@
+//===-- MinidumpParser.h -----------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MinidumpParser_h_
+#define liblldb_MinidumpParser_h_
+
+#include "MinidumpTypes.h"
+
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/UUID.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Minidump.h"
+
+// C includes
+
+// C++ includes
+#include <cstring>
+#include <unordered_map>
+
+namespace lldb_private {
+
+namespace minidump {
+
+// Describes a range of memory captured in the Minidump
+struct Range {
+ lldb::addr_t start; // virtual address of the beginning of the range
+ // range_ref - absolute pointer to the first byte of the range and size
+ llvm::ArrayRef<uint8_t> range_ref;
+
+ Range(lldb::addr_t start, llvm::ArrayRef<uint8_t> range_ref)
+ : start(start), range_ref(range_ref) {}
+
+ friend bool operator==(const Range &lhs, const Range &rhs) {
+ return lhs.start == rhs.start && lhs.range_ref == rhs.range_ref;
+ }
+};
+
+class MinidumpParser {
+public:
+ static llvm::Expected<MinidumpParser>
+ Create(const lldb::DataBufferSP &data_buf_sp);
+
+ llvm::ArrayRef<uint8_t> GetData();
+
+ llvm::ArrayRef<uint8_t> GetStream(StreamType stream_type);
+
+ UUID GetModuleUUID(const minidump::Module *module);
+
+ llvm::ArrayRef<minidump::Thread> GetThreads();
+
+ llvm::ArrayRef<uint8_t> GetThreadContext(const LocationDescriptor &location);
+
+ llvm::ArrayRef<uint8_t> GetThreadContext(const minidump::Thread &td);
+
+ llvm::ArrayRef<uint8_t> GetThreadContextWow64(const minidump::Thread &td);
+
+ ArchSpec GetArchitecture();
+
+ const MinidumpMiscInfo *GetMiscInfo();
+
+ llvm::Optional<LinuxProcStatus> GetLinuxProcStatus();
+
+ llvm::Optional<lldb::pid_t> GetPid();
+
+ llvm::ArrayRef<minidump::Module> GetModuleList();
+
+ // There are cases in which there is more than one record in the ModuleList
+ // for the same module name.(e.g. when the binary has non contiguous segments)
+ // So this function returns a filtered module list - if it finds records that
+ // have the same name, it keeps the copy with the lowest load address.
+ std::vector<const minidump::Module *> GetFilteredModuleList();
+
+ const MinidumpExceptionStream *GetExceptionStream();
+
+ llvm::Optional<Range> FindMemoryRange(lldb::addr_t addr);
+
+ llvm::ArrayRef<uint8_t> GetMemory(lldb::addr_t addr, size_t size);
+
+ MemoryRegionInfo GetMemoryRegionInfo(lldb::addr_t load_addr);
+
+ const MemoryRegionInfos &GetMemoryRegions();
+
+ static llvm::StringRef GetStreamTypeAsString(StreamType stream_type);
+
+ llvm::object::MinidumpFile &GetMinidumpFile() { return *m_file; }
+
+private:
+ MinidumpParser(lldb::DataBufferSP data_sp,
+ std::unique_ptr<llvm::object::MinidumpFile> file);
+
+ MemoryRegionInfo FindMemoryRegion(lldb::addr_t load_addr) const;
+
+private:
+ lldb::DataBufferSP m_data_sp;
+ std::unique_ptr<llvm::object::MinidumpFile> m_file;
+ ArchSpec m_arch;
+ MemoryRegionInfos m_regions;
+ bool m_parsed_regions = false;
+};
+
+} // end namespace minidump
+} // end namespace lldb_private
+#endif // liblldb_MinidumpParser_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp
new file mode 100644
index 000000000000..d7fc6e43d090
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp
@@ -0,0 +1,115 @@
+//===-- MinidumpTypes.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MinidumpTypes.h"
+
+// C includes
+// C++ includes
+
+using namespace lldb_private;
+using namespace minidump;
+
+// MinidumpMiscInfo
+const MinidumpMiscInfo *MinidumpMiscInfo::Parse(llvm::ArrayRef<uint8_t> &data) {
+ const MinidumpMiscInfo *misc_info;
+ Status error = consumeObject(data, misc_info);
+ if (error.Fail())
+ return nullptr;
+
+ return misc_info;
+}
+
+llvm::Optional<lldb::pid_t> MinidumpMiscInfo::GetPid() const {
+ uint32_t pid_flag = static_cast<uint32_t>(MinidumpMiscInfoFlags::ProcessID);
+ if (flags1 & pid_flag)
+ return llvm::Optional<lldb::pid_t>(process_id);
+
+ return llvm::None;
+}
+
+// Linux Proc Status
+// it's stored as an ascii string in the file
+llvm::Optional<LinuxProcStatus>
+LinuxProcStatus::Parse(llvm::ArrayRef<uint8_t> &data) {
+ LinuxProcStatus result;
+ result.proc_status =
+ llvm::StringRef(reinterpret_cast<const char *>(data.data()), data.size());
+ data = data.drop_front(data.size());
+
+ llvm::SmallVector<llvm::StringRef, 0> lines;
+ result.proc_status.split(lines, '\n', 42);
+ // /proc/$pid/status has 41 lines, but why not use 42?
+ for (auto line : lines) {
+ if (line.consume_front("Pid:")) {
+ line = line.trim();
+ if (!line.getAsInteger(10, result.pid))
+ return result;
+ }
+ }
+
+ return llvm::None;
+}
+
+lldb::pid_t LinuxProcStatus::GetPid() const { return pid; }
+
+// Exception stuff
+const MinidumpExceptionStream *
+MinidumpExceptionStream::Parse(llvm::ArrayRef<uint8_t> &data) {
+ const MinidumpExceptionStream *exception_stream = nullptr;
+ Status error = consumeObject(data, exception_stream);
+ if (error.Fail())
+ return nullptr;
+
+ return exception_stream;
+}
+
+std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t>
+MinidumpMemoryDescriptor64::ParseMemory64List(llvm::ArrayRef<uint8_t> &data) {
+ const llvm::support::ulittle64_t *mem_ranges_count;
+ Status error = consumeObject(data, mem_ranges_count);
+ if (error.Fail() ||
+ *mem_ranges_count * sizeof(MinidumpMemoryDescriptor64) > data.size())
+ return {};
+
+ const llvm::support::ulittle64_t *base_rva;
+ error = consumeObject(data, base_rva);
+ if (error.Fail())
+ return {};
+
+ return std::make_pair(
+ llvm::makeArrayRef(
+ reinterpret_cast<const MinidumpMemoryDescriptor64 *>(data.data()),
+ *mem_ranges_count),
+ *base_rva);
+}
+
+std::vector<const MinidumpMemoryInfo *>
+MinidumpMemoryInfo::ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data) {
+ const MinidumpMemoryInfoListHeader *header;
+ Status error = consumeObject(data, header);
+ if (error.Fail() ||
+ header->size_of_header < sizeof(MinidumpMemoryInfoListHeader) ||
+ header->size_of_entry < sizeof(MinidumpMemoryInfo))
+ return {};
+
+ data = data.drop_front(header->size_of_header -
+ sizeof(MinidumpMemoryInfoListHeader));
+
+ if (header->size_of_entry * header->num_of_entries > data.size())
+ return {};
+
+ std::vector<const MinidumpMemoryInfo *> result;
+ result.reserve(header->num_of_entries);
+
+ for (uint64_t i = 0; i < header->num_of_entries; ++i) {
+ result.push_back(reinterpret_cast<const MinidumpMemoryInfo *>(
+ data.data() + i * header->size_of_entry));
+ }
+
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h
new file mode 100644
index 000000000000..b4878e82de5d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h
@@ -0,0 +1,236 @@
+//===-- MinidumpTypes.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MinidumpTypes_h_
+#define liblldb_MinidumpTypes_h_
+
+
+#include "lldb/Utility/Status.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/Minidump.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Endian.h"
+
+// C includes
+// C++ includes
+
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms679293(v=vs.85).aspx
+// https://chromium.googlesource.com/breakpad/breakpad/
+
+namespace lldb_private {
+
+namespace minidump {
+
+using namespace llvm::minidump;
+
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+enum class CvSignature : uint32_t {
+ Pdb70 = 0x53445352, // RSDS
+ ElfBuildId = 0x4270454c, // BpEL (Breakpad/Crashpad minidumps)
+};
+
+// Reference:
+// https://crashpad.chromium.org/doxygen/structcrashpad_1_1CodeViewRecordPDB70.html
+struct CvRecordPdb70 {
+ struct {
+ llvm::support::ulittle32_t Data1;
+ llvm::support::ulittle16_t Data2;
+ llvm::support::ulittle16_t Data3;
+ uint8_t Data4[8];
+ } Uuid;
+ llvm::support::ulittle32_t Age;
+ // char PDBFileName[];
+};
+static_assert(sizeof(CvRecordPdb70) == 20,
+ "sizeof CvRecordPdb70 is not correct!");
+
+enum class MinidumpMiscInfoFlags : uint32_t {
+ ProcessID = (1 << 0),
+ ProcessTimes = (1 << 1),
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ ProcessTimes)
+};
+
+template <typename T>
+Status consumeObject(llvm::ArrayRef<uint8_t> &Buffer, const T *&Object) {
+ Status error;
+ if (Buffer.size() < sizeof(T)) {
+ error.SetErrorString("Insufficient buffer!");
+ return error;
+ }
+
+ Object = reinterpret_cast<const T *>(Buffer.data());
+ Buffer = Buffer.drop_front(sizeof(T));
+ return error;
+}
+
+struct MinidumpMemoryDescriptor64 {
+ llvm::support::ulittle64_t start_of_memory_range;
+ llvm::support::ulittle64_t data_size;
+
+ static std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t>
+ ParseMemory64List(llvm::ArrayRef<uint8_t> &data);
+};
+static_assert(sizeof(MinidumpMemoryDescriptor64) == 16,
+ "sizeof MinidumpMemoryDescriptor64 is not correct!");
+
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680385(v=vs.85).aspx
+struct MinidumpMemoryInfoListHeader {
+ llvm::support::ulittle32_t size_of_header;
+ llvm::support::ulittle32_t size_of_entry;
+ llvm::support::ulittle64_t num_of_entries;
+};
+static_assert(sizeof(MinidumpMemoryInfoListHeader) == 16,
+ "sizeof MinidumpMemoryInfoListHeader is not correct!");
+
+enum class MinidumpMemoryInfoState : uint32_t {
+ MemCommit = 0x1000,
+ MemFree = 0x10000,
+ MemReserve = 0x2000,
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MemFree)
+};
+
+enum class MinidumpMemoryInfoType : uint32_t {
+ MemImage = 0x1000000,
+ MemMapped = 0x40000,
+ MemPrivate = 0x20000,
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MemImage)
+};
+
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx
+enum class MinidumpMemoryProtectionContants : uint32_t {
+ PageExecute = 0x10,
+ PageExecuteRead = 0x20,
+ PageExecuteReadWrite = 0x40,
+ PageExecuteWriteCopy = 0x80,
+ PageNoAccess = 0x01,
+ PageReadOnly = 0x02,
+ PageReadWrite = 0x04,
+ PageWriteCopy = 0x08,
+ PageTargetsInvalid = 0x40000000,
+ PageTargetsNoUpdate = 0x40000000,
+
+ PageWritable = PageExecuteReadWrite | PageExecuteWriteCopy | PageReadWrite |
+ PageWriteCopy,
+ PageExecutable = PageExecute | PageExecuteRead | PageExecuteReadWrite |
+ PageExecuteWriteCopy,
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ PageTargetsInvalid)
+};
+
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680386(v=vs.85).aspx
+struct MinidumpMemoryInfo {
+ llvm::support::ulittle64_t base_address;
+ llvm::support::ulittle64_t allocation_base;
+ llvm::support::ulittle32_t allocation_protect;
+ llvm::support::ulittle32_t alignment1;
+ llvm::support::ulittle64_t region_size;
+ llvm::support::ulittle32_t state;
+ llvm::support::ulittle32_t protect;
+ llvm::support::ulittle32_t type;
+ llvm::support::ulittle32_t alignment2;
+
+ static std::vector<const MinidumpMemoryInfo *>
+ ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data);
+
+ bool isReadable() const {
+ const auto mask = MinidumpMemoryProtectionContants::PageNoAccess;
+ return (static_cast<uint32_t>(mask) & protect) == 0;
+ }
+
+ bool isWritable() const {
+ const auto mask = MinidumpMemoryProtectionContants::PageWritable;
+ return (static_cast<uint32_t>(mask) & protect) != 0;
+ }
+
+ bool isExecutable() const {
+ const auto mask = MinidumpMemoryProtectionContants::PageExecutable;
+ return (static_cast<uint32_t>(mask) & protect) != 0;
+ }
+
+ bool isMapped() const {
+ return state != static_cast<uint32_t>(MinidumpMemoryInfoState::MemFree);
+ }
+};
+
+static_assert(sizeof(MinidumpMemoryInfo) == 48,
+ "sizeof MinidumpMemoryInfo is not correct!");
+
+// TODO misc2, misc3 ?
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680389(v=vs.85).aspx
+struct MinidumpMiscInfo {
+ llvm::support::ulittle32_t size;
+ // flags1 represents what info in the struct is valid
+ llvm::support::ulittle32_t flags1;
+ llvm::support::ulittle32_t process_id;
+ llvm::support::ulittle32_t process_create_time;
+ llvm::support::ulittle32_t process_user_time;
+ llvm::support::ulittle32_t process_kernel_time;
+
+ static const MinidumpMiscInfo *Parse(llvm::ArrayRef<uint8_t> &data);
+
+ llvm::Optional<lldb::pid_t> GetPid() const;
+};
+static_assert(sizeof(MinidumpMiscInfo) == 24,
+ "sizeof MinidumpMiscInfo is not correct!");
+
+// The /proc/pid/status is saved as an ascii string in the file
+class LinuxProcStatus {
+public:
+ llvm::StringRef proc_status;
+ lldb::pid_t pid;
+
+ static llvm::Optional<LinuxProcStatus> Parse(llvm::ArrayRef<uint8_t> &data);
+
+ lldb::pid_t GetPid() const;
+
+private:
+ LinuxProcStatus() = default;
+};
+
+// Exception stuff
+struct MinidumpException {
+ enum : unsigned {
+ ExceptonInfoMaxParams = 15,
+ DumpRequested = 0xFFFFFFFF,
+ };
+
+ llvm::support::ulittle32_t exception_code;
+ llvm::support::ulittle32_t exception_flags;
+ llvm::support::ulittle64_t exception_record;
+ llvm::support::ulittle64_t exception_address;
+ llvm::support::ulittle32_t number_parameters;
+ llvm::support::ulittle32_t unused_alignment;
+ llvm::support::ulittle64_t exception_information[ExceptonInfoMaxParams];
+};
+static_assert(sizeof(MinidumpException) == 152,
+ "sizeof MinidumpException is not correct!");
+
+struct MinidumpExceptionStream {
+ llvm::support::ulittle32_t thread_id;
+ llvm::support::ulittle32_t alignment;
+ MinidumpException exception_record;
+ LocationDescriptor thread_context;
+
+ static const MinidumpExceptionStream *Parse(llvm::ArrayRef<uint8_t> &data);
+};
+static_assert(sizeof(MinidumpExceptionStream) == 168,
+ "sizeof MinidumpExceptionStream is not correct!");
+
+} // namespace minidump
+} // namespace lldb_private
+#endif // liblldb_MinidumpTypes_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h
new file mode 100644
index 000000000000..fdb0cfb7981e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h
@@ -0,0 +1,36 @@
+//===-- NtStructures.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_Plugins_Process_Minidump_NtStructures_h_
+#define liblldb_Plugins_Process_Minidump_NtStructures_h_
+
+#include "llvm/Support/Endian.h"
+
+namespace lldb_private {
+
+namespace minidump {
+
+// This describes the layout of a TEB (Thread Environment Block) for a 64-bit
+// process. It's adapted from the 32-bit TEB in winternl.h. Currently, we care
+// only about the position of the tls_slots.
+struct TEB64 {
+ llvm::support::ulittle64_t reserved1[12];
+ llvm::support::ulittle64_t process_environment_block;
+ llvm::support::ulittle64_t reserved2[399];
+ uint8_t reserved3[1952];
+ llvm::support::ulittle64_t tls_slots[64];
+ uint8_t reserved4[8];
+ llvm::support::ulittle64_t reserved5[26];
+ llvm::support::ulittle64_t reserved_for_ole; // Windows 2000 only
+ llvm::support::ulittle64_t reserved6[4];
+ llvm::support::ulittle64_t tls_expansion_slots;
+};
+
+#endif // liblldb_Plugins_Process_Minidump_NtStructures_h_
+} // namespace minidump
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
new file mode 100644
index 000000000000..a7fc42cad16c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
@@ -0,0 +1,822 @@
+//===-- ProcessMinidump.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProcessMinidump.h"
+
+#include "ThreadMinidump.h"
+
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+#include "lldb/Target/JITLoaderList.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Threading.h"
+
+#include "Plugins/Process/Utility/StopInfoMachException.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace minidump;
+
+namespace {
+
+/// A minimal ObjectFile implementation providing a dummy object file for the
+/// cases when the real module binary is not available. This allows the module
+/// to show up in "image list" and symbols to be added to it.
+class PlaceholderObjectFile : public ObjectFile {
+public:
+ PlaceholderObjectFile(const lldb::ModuleSP &module_sp,
+ const ModuleSpec &module_spec, lldb::offset_t base,
+ lldb::offset_t size)
+ : ObjectFile(module_sp, &module_spec.GetFileSpec(), /*file_offset*/ 0,
+ /*length*/ 0, /*data_sp*/ nullptr, /*data_offset*/ 0),
+ m_arch(module_spec.GetArchitecture()), m_uuid(module_spec.GetUUID()),
+ m_base(base), m_size(size) {
+ m_symtab_up = llvm::make_unique<Symtab>(this);
+ }
+
+ ConstString GetPluginName() override { return ConstString("placeholder"); }
+ uint32_t GetPluginVersion() override { return 1; }
+ bool ParseHeader() override { return true; }
+ Type CalculateType() override { return eTypeUnknown; }
+ Strata CalculateStrata() override { return eStrataUnknown; }
+ uint32_t GetDependentModules(FileSpecList &file_list) override { return 0; }
+ bool IsExecutable() const override { return false; }
+ ArchSpec GetArchitecture() override { return m_arch; }
+ UUID GetUUID() override { return m_uuid; }
+ Symtab *GetSymtab() override { return m_symtab_up.get(); }
+ bool IsStripped() override { return true; }
+ ByteOrder GetByteOrder() const override { return m_arch.GetByteOrder(); }
+
+ uint32_t GetAddressByteSize() const override {
+ return m_arch.GetAddressByteSize();
+ }
+
+ Address GetBaseAddress() override {
+ return Address(m_sections_up->GetSectionAtIndex(0), 0);
+ }
+
+ void CreateSections(SectionList &unified_section_list) override {
+ m_sections_up = llvm::make_unique<SectionList>();
+ auto section_sp = std::make_shared<Section>(
+ GetModule(), this, /*sect_id*/ 0, ConstString(".module_image"),
+ eSectionTypeOther, m_base, m_size, /*file_offset*/ 0, /*file_size*/ 0,
+ /*log2align*/ 0, /*flags*/ 0);
+ section_sp->SetPermissions(ePermissionsReadable | ePermissionsExecutable);
+ m_sections_up->AddSection(section_sp);
+ unified_section_list.AddSection(std::move(section_sp));
+ }
+
+ bool SetLoadAddress(Target &target, addr_t value,
+ bool value_is_offset) override {
+ assert(!value_is_offset);
+ assert(value == m_base);
+
+ // Create sections if they haven't been created already.
+ GetModule()->GetSectionList();
+ assert(m_sections_up->GetNumSections(0) == 1);
+
+ target.GetSectionLoadList().SetSectionLoadAddress(
+ m_sections_up->GetSectionAtIndex(0), m_base);
+ return true;
+ }
+
+ void Dump(Stream *s) override {
+ s->Format("Placeholder object file for {0} loaded at [{1:x}-{2:x})\n",
+ GetFileSpec(), m_base, m_base + m_size);
+ }
+
+private:
+ ArchSpec m_arch;
+ UUID m_uuid;
+ lldb::offset_t m_base;
+ lldb::offset_t m_size;
+};
+} // namespace
+
+ConstString ProcessMinidump::GetPluginNameStatic() {
+ static ConstString g_name("minidump");
+ return g_name;
+}
+
+const char *ProcessMinidump::GetPluginDescriptionStatic() {
+ return "Minidump plug-in.";
+}
+
+lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file) {
+ if (!crash_file)
+ return nullptr;
+
+ lldb::ProcessSP process_sp;
+ // Read enough data for the Minidump header
+ constexpr size_t header_size = sizeof(Header);
+ auto DataPtr = FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(),
+ header_size, 0);
+ if (!DataPtr)
+ return nullptr;
+
+ lldbassert(DataPtr->GetByteSize() == header_size);
+ if (identify_magic(toStringRef(DataPtr->GetData())) != llvm::file_magic::minidump)
+ return nullptr;
+
+ auto AllData =
+ FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), -1, 0);
+ if (!AllData)
+ return nullptr;
+
+ return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file,
+ std::move(AllData));
+}
+
+bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) {
+ return true;
+}
+
+ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec &core_file,
+ DataBufferSP core_data)
+ : Process(target_sp, listener_sp), m_core_file(core_file),
+ m_core_data(std::move(core_data)), m_is_wow64(false) {}
+
+ProcessMinidump::~ProcessMinidump() {
+ Clear();
+ // We need to call finalize on the process before destroying ourselves to
+ // make sure all of the broadcaster cleanup goes as planned. If we destruct
+ // this class, then Process::~Process() might have problems trying to fully
+ // destroy the broadcaster.
+ Finalize();
+}
+
+void ProcessMinidump::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(),
+ ProcessMinidump::CreateInstance);
+ });
+}
+
+void ProcessMinidump::Terminate() {
+ PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance);
+}
+
+Status ProcessMinidump::DoLoadCore() {
+ auto expected_parser = MinidumpParser::Create(m_core_data);
+ if (!expected_parser)
+ return Status(expected_parser.takeError());
+ m_minidump_parser = std::move(*expected_parser);
+
+ Status error;
+
+ // Do we support the minidump's architecture?
+ ArchSpec arch = GetArchitecture();
+ switch (arch.GetMachine()) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ case llvm::Triple::arm:
+ case llvm::Triple::aarch64:
+ // Any supported architectures must be listed here and also supported in
+ // ThreadMinidump::CreateRegisterContextForFrame().
+ break;
+ default:
+ error.SetErrorStringWithFormat("unsupported minidump architecture: %s",
+ arch.GetArchitectureName());
+ return error;
+ }
+ GetTarget().SetArchitecture(arch, true /*set_platform*/);
+
+ m_thread_list = m_minidump_parser->GetThreads();
+ m_active_exception = m_minidump_parser->GetExceptionStream();
+ ReadModuleList();
+
+ llvm::Optional<lldb::pid_t> pid = m_minidump_parser->GetPid();
+ if (!pid) {
+ error.SetErrorString("failed to parse PID");
+ return error;
+ }
+ SetID(pid.getValue());
+
+ return error;
+}
+
+ConstString ProcessMinidump::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t ProcessMinidump::GetPluginVersion() { return 1; }
+
+Status ProcessMinidump::DoDestroy() { return Status(); }
+
+void ProcessMinidump::RefreshStateAfterStop() {
+ if (!m_active_exception)
+ return;
+
+ if (m_active_exception->exception_record.exception_code ==
+ MinidumpException::DumpRequested) {
+ return;
+ }
+
+ lldb::StopInfoSP stop_info;
+ lldb::ThreadSP stop_thread;
+
+ Process::m_thread_list.SetSelectedThreadByID(m_active_exception->thread_id);
+ stop_thread = Process::m_thread_list.GetSelectedThread();
+ ArchSpec arch = GetArchitecture();
+
+ if (arch.GetTriple().getOS() == llvm::Triple::Linux) {
+ stop_info = StopInfo::CreateStopReasonWithSignal(
+ *stop_thread, m_active_exception->exception_record.exception_code);
+ } else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) {
+ stop_info = StopInfoMachException::CreateStopReasonWithMachException(
+ *stop_thread, m_active_exception->exception_record.exception_code, 2,
+ m_active_exception->exception_record.exception_flags,
+ m_active_exception->exception_record.exception_address, 0);
+ } else {
+ std::string desc;
+ llvm::raw_string_ostream desc_stream(desc);
+ desc_stream << "Exception "
+ << llvm::format_hex(
+ m_active_exception->exception_record.exception_code, 8)
+ << " encountered at address "
+ << llvm::format_hex(
+ m_active_exception->exception_record.exception_address,
+ 8);
+ stop_info = StopInfo::CreateStopReasonWithException(
+ *stop_thread, desc_stream.str().c_str());
+ }
+
+ stop_thread->SetStopInfo(stop_info);
+}
+
+bool ProcessMinidump::IsAlive() { return true; }
+
+bool ProcessMinidump::WarnBeforeDetach() const { return false; }
+
+size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) {
+ // Don't allow the caching that lldb_private::Process::ReadMemory does since
+ // we have it all cached in our dump file anyway.
+ return DoReadMemory(addr, buf, size, error);
+}
+
+size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) {
+
+ llvm::ArrayRef<uint8_t> mem = m_minidump_parser->GetMemory(addr, size);
+ if (mem.empty()) {
+ error.SetErrorString("could not parse memory info");
+ return 0;
+ }
+
+ std::memcpy(buf, mem.data(), mem.size());
+ return mem.size();
+}
+
+ArchSpec ProcessMinidump::GetArchitecture() {
+ if (!m_is_wow64) {
+ return m_minidump_parser->GetArchitecture();
+ }
+
+ llvm::Triple triple;
+ triple.setVendor(llvm::Triple::VendorType::UnknownVendor);
+ triple.setArch(llvm::Triple::ArchType::x86);
+ triple.setOS(llvm::Triple::OSType::Win32);
+ return ArchSpec(triple);
+}
+
+Status ProcessMinidump::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) {
+ range_info = m_minidump_parser->GetMemoryRegionInfo(load_addr);
+ return Status();
+}
+
+Status ProcessMinidump::GetMemoryRegions(
+ lldb_private::MemoryRegionInfos &region_list) {
+ region_list = m_minidump_parser->GetMemoryRegions();
+ return Status();
+}
+
+void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); }
+
+bool ProcessMinidump::UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) {
+ for (const minidump::Thread &thread : m_thread_list) {
+ LocationDescriptor context_location = thread.Context;
+
+ // If the minidump contains an exception context, use it
+ if (m_active_exception != nullptr &&
+ m_active_exception->thread_id == thread.ThreadId) {
+ context_location = m_active_exception->thread_context;
+ }
+
+ llvm::ArrayRef<uint8_t> context;
+ if (!m_is_wow64)
+ context = m_minidump_parser->GetThreadContext(context_location);
+ else
+ context = m_minidump_parser->GetThreadContextWow64(thread);
+
+ lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context));
+ new_thread_list.AddThread(thread_sp);
+ }
+ return new_thread_list.GetSize(false) > 0;
+}
+
+void ProcessMinidump::ReadModuleList() {
+ std::vector<const minidump::Module *> filtered_modules =
+ m_minidump_parser->GetFilteredModuleList();
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
+
+ for (auto module : filtered_modules) {
+ std::string name = cantFail(m_minidump_parser->GetMinidumpFile().getString(
+ module->ModuleNameRVA));
+ LLDB_LOG(log, "found module: name: {0} {1:x10}-{2:x10} size: {3}", name,
+ module->BaseOfImage, module->BaseOfImage + module->SizeOfImage,
+ module->SizeOfImage);
+
+ // check if the process is wow64 - a 32 bit windows process running on a
+ // 64 bit windows
+ if (llvm::StringRef(name).endswith_lower("wow64.dll")) {
+ m_is_wow64 = true;
+ }
+
+ const auto uuid = m_minidump_parser->GetModuleUUID(module);
+ auto file_spec = FileSpec(name, GetArchitecture().GetTriple());
+ ModuleSpec module_spec(file_spec, uuid);
+ module_spec.GetArchitecture() = GetArchitecture();
+ Status error;
+ // Try and find a module with a full UUID that matches. This function will
+ // add the module to the target if it finds one.
+ lldb::ModuleSP module_sp = GetTarget().GetOrCreateModule(module_spec,
+ true /* notify */, &error);
+ if (!module_sp) {
+ // Try and find a module without specifying the UUID and only looking for
+ // the file given a basename. We then will look for a partial UUID match
+ // if we find any matches. This function will add the module to the
+ // target if it finds one, so we need to remove the module from the target
+ // if the UUID doesn't match during our manual UUID verification. This
+ // allows the "target.exec-search-paths" setting to specify one or more
+ // directories that contain executables that can be searched for matches.
+ ModuleSpec basename_module_spec(module_spec);
+ basename_module_spec.GetUUID().Clear();
+ basename_module_spec.GetFileSpec().GetDirectory().Clear();
+ module_sp = GetTarget().GetOrCreateModule(basename_module_spec,
+ true /* notify */, &error);
+ if (module_sp) {
+ // We consider the module to be a match if the minidump UUID is a
+ // prefix of the actual UUID, or if either of the UUIDs are empty.
+ const auto dmp_bytes = uuid.GetBytes();
+ const auto mod_bytes = module_sp->GetUUID().GetBytes();
+ const bool match = dmp_bytes.empty() || mod_bytes.empty() ||
+ mod_bytes.take_front(dmp_bytes.size()) == dmp_bytes;
+ if (!match) {
+ GetTarget().GetImages().Remove(module_sp);
+ module_sp.reset();
+ }
+ }
+ }
+ if (!module_sp) {
+ // We failed to locate a matching local object file. Fortunately, the
+ // minidump format encodes enough information about each module's memory
+ // range to allow us to create placeholder modules.
+ //
+ // This enables most LLDB functionality involving address-to-module
+ // translations (ex. identifing the module for a stack frame PC) and
+ // modules/sections commands (ex. target modules list, ...)
+ LLDB_LOG(log,
+ "Unable to locate the matching object file, creating a "
+ "placeholder module for: {0}",
+ name);
+
+ module_sp = Module::CreateModuleFromObjectFile<PlaceholderObjectFile>(
+ module_spec, module->BaseOfImage, module->SizeOfImage);
+ GetTarget().GetImages().Append(module_sp, true /* notify */);
+ }
+
+ bool load_addr_changed = false;
+ module_sp->SetLoadAddress(GetTarget(), module->BaseOfImage, false,
+ load_addr_changed);
+ }
+}
+
+bool ProcessMinidump::GetProcessInfo(ProcessInstanceInfo &info) {
+ info.Clear();
+ info.SetProcessID(GetID());
+ info.SetArchitecture(GetArchitecture());
+ lldb::ModuleSP module_sp = GetTarget().GetExecutableModule();
+ if (module_sp) {
+ const bool add_exe_file_as_first_arg = false;
+ info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),
+ add_exe_file_as_first_arg);
+ }
+ return true;
+}
+
+// For minidumps there's no runtime generated code so we don't need JITLoader(s)
+// Avoiding them will also speed up minidump loading since JITLoaders normally
+// try to set up symbolic breakpoints, which in turn may force loading more
+// debug information than needed.
+JITLoaderList &ProcessMinidump::GetJITLoaders() {
+ if (!m_jit_loaders_up) {
+ m_jit_loaders_up = llvm::make_unique<JITLoaderList>();
+ }
+ return *m_jit_loaders_up;
+}
+
+#define INIT_BOOL(VAR, LONG, SHORT, DESC) \
+ VAR(LLDB_OPT_SET_1, false, LONG, SHORT, DESC, false, true)
+#define APPEND_OPT(VAR) \
+ m_option_group.Append(&VAR, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1)
+
+class CommandObjectProcessMinidumpDump : public CommandObjectParsed {
+private:
+ OptionGroupOptions m_option_group;
+ OptionGroupBoolean m_dump_all;
+ OptionGroupBoolean m_dump_directory;
+ OptionGroupBoolean m_dump_linux_cpuinfo;
+ OptionGroupBoolean m_dump_linux_proc_status;
+ OptionGroupBoolean m_dump_linux_lsb_release;
+ OptionGroupBoolean m_dump_linux_cmdline;
+ OptionGroupBoolean m_dump_linux_environ;
+ OptionGroupBoolean m_dump_linux_auxv;
+ OptionGroupBoolean m_dump_linux_maps;
+ OptionGroupBoolean m_dump_linux_proc_stat;
+ OptionGroupBoolean m_dump_linux_proc_uptime;
+ OptionGroupBoolean m_dump_linux_proc_fd;
+ OptionGroupBoolean m_dump_linux_all;
+ OptionGroupBoolean m_fb_app_data;
+ OptionGroupBoolean m_fb_build_id;
+ OptionGroupBoolean m_fb_version;
+ OptionGroupBoolean m_fb_java_stack;
+ OptionGroupBoolean m_fb_dalvik;
+ OptionGroupBoolean m_fb_unwind;
+ OptionGroupBoolean m_fb_error_log;
+ OptionGroupBoolean m_fb_app_state;
+ OptionGroupBoolean m_fb_abort;
+ OptionGroupBoolean m_fb_thread;
+ OptionGroupBoolean m_fb_logcat;
+ OptionGroupBoolean m_fb_all;
+
+ void SetDefaultOptionsIfNoneAreSet() {
+ if (m_dump_all.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_all.GetOptionValue().GetCurrentValue() ||
+ m_fb_all.GetOptionValue().GetCurrentValue() ||
+ m_dump_directory.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_proc_status.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_cmdline.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_environ.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_auxv.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_maps.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue() ||
+ m_fb_app_data.GetOptionValue().GetCurrentValue() ||
+ m_fb_build_id.GetOptionValue().GetCurrentValue() ||
+ m_fb_version.GetOptionValue().GetCurrentValue() ||
+ m_fb_java_stack.GetOptionValue().GetCurrentValue() ||
+ m_fb_dalvik.GetOptionValue().GetCurrentValue() ||
+ m_fb_unwind.GetOptionValue().GetCurrentValue() ||
+ m_fb_error_log.GetOptionValue().GetCurrentValue() ||
+ m_fb_app_state.GetOptionValue().GetCurrentValue() ||
+ m_fb_abort.GetOptionValue().GetCurrentValue() ||
+ m_fb_thread.GetOptionValue().GetCurrentValue() ||
+ m_fb_logcat.GetOptionValue().GetCurrentValue())
+ return;
+ // If no options were set, then dump everything
+ m_dump_all.GetOptionValue().SetCurrentValue(true);
+ }
+ bool DumpAll() const {
+ return m_dump_all.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpDirectory() const {
+ return DumpAll() ||
+ m_dump_directory.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinux() const {
+ return DumpAll() || m_dump_linux_all.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxCPUInfo() const {
+ return DumpLinux() ||
+ m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxProcStatus() const {
+ return DumpLinux() ||
+ m_dump_linux_proc_status.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxProcStat() const {
+ return DumpLinux() ||
+ m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxLSBRelease() const {
+ return DumpLinux() ||
+ m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxCMDLine() const {
+ return DumpLinux() ||
+ m_dump_linux_cmdline.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxEnviron() const {
+ return DumpLinux() ||
+ m_dump_linux_environ.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxAuxv() const {
+ return DumpLinux() ||
+ m_dump_linux_auxv.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxMaps() const {
+ return DumpLinux() ||
+ m_dump_linux_maps.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxProcUptime() const {
+ return DumpLinux() ||
+ m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxProcFD() const {
+ return DumpLinux() ||
+ m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebook() const {
+ return DumpAll() || m_fb_all.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookAppData() const {
+ return DumpFacebook() || m_fb_app_data.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookBuildID() const {
+ return DumpFacebook() || m_fb_build_id.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookVersionName() const {
+ return DumpFacebook() || m_fb_version.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookJavaStack() const {
+ return DumpFacebook() || m_fb_java_stack.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookDalvikInfo() const {
+ return DumpFacebook() || m_fb_dalvik.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookUnwindSymbols() const {
+ return DumpFacebook() || m_fb_unwind.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookErrorLog() const {
+ return DumpFacebook() || m_fb_error_log.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookAppStateLog() const {
+ return DumpFacebook() || m_fb_app_state.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookAbortReason() const {
+ return DumpFacebook() || m_fb_abort.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookThreadName() const {
+ return DumpFacebook() || m_fb_thread.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookLogcat() const {
+ return DumpFacebook() || m_fb_logcat.GetOptionValue().GetCurrentValue();
+ }
+public:
+ CommandObjectProcessMinidumpDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process plugin dump",
+ "Dump information from the minidump file.", nullptr),
+ m_option_group(),
+ INIT_BOOL(m_dump_all, "all", 'a',
+ "Dump the everything in the minidump."),
+ INIT_BOOL(m_dump_directory, "directory", 'd',
+ "Dump the minidump directory map."),
+ INIT_BOOL(m_dump_linux_cpuinfo, "cpuinfo", 'C',
+ "Dump linux /proc/cpuinfo."),
+ INIT_BOOL(m_dump_linux_proc_status, "status", 's',
+ "Dump linux /proc/<pid>/status."),
+ INIT_BOOL(m_dump_linux_lsb_release, "lsb-release", 'r',
+ "Dump linux /etc/lsb-release."),
+ INIT_BOOL(m_dump_linux_cmdline, "cmdline", 'c',
+ "Dump linux /proc/<pid>/cmdline."),
+ INIT_BOOL(m_dump_linux_environ, "environ", 'e',
+ "Dump linux /proc/<pid>/environ."),
+ INIT_BOOL(m_dump_linux_auxv, "auxv", 'x',
+ "Dump linux /proc/<pid>/auxv."),
+ INIT_BOOL(m_dump_linux_maps, "maps", 'm',
+ "Dump linux /proc/<pid>/maps."),
+ INIT_BOOL(m_dump_linux_proc_stat, "stat", 'S',
+ "Dump linux /proc/<pid>/stat."),
+ INIT_BOOL(m_dump_linux_proc_uptime, "uptime", 'u',
+ "Dump linux process uptime."),
+ INIT_BOOL(m_dump_linux_proc_fd, "fd", 'f',
+ "Dump linux /proc/<pid>/fd."),
+ INIT_BOOL(m_dump_linux_all, "linux", 'l',
+ "Dump all linux streams."),
+ INIT_BOOL(m_fb_app_data, "fb-app-data", 1,
+ "Dump Facebook application custom data."),
+ INIT_BOOL(m_fb_build_id, "fb-build-id", 2,
+ "Dump the Facebook build ID."),
+ INIT_BOOL(m_fb_version, "fb-version", 3,
+ "Dump Facebook application version string."),
+ INIT_BOOL(m_fb_java_stack, "fb-java-stack", 4,
+ "Dump Facebook java stack."),
+ INIT_BOOL(m_fb_dalvik, "fb-dalvik-info", 5,
+ "Dump Facebook Dalvik info."),
+ INIT_BOOL(m_fb_unwind, "fb-unwind-symbols", 6,
+ "Dump Facebook unwind symbols."),
+ INIT_BOOL(m_fb_error_log, "fb-error-log", 7,
+ "Dump Facebook error log."),
+ INIT_BOOL(m_fb_app_state, "fb-app-state-log", 8,
+ "Dump Facebook java stack."),
+ INIT_BOOL(m_fb_abort, "fb-abort-reason", 9,
+ "Dump Facebook abort reason."),
+ INIT_BOOL(m_fb_thread, "fb-thread-name", 10,
+ "Dump Facebook thread name."),
+ INIT_BOOL(m_fb_logcat, "fb-logcat", 11,
+ "Dump Facebook logcat."),
+ INIT_BOOL(m_fb_all, "facebook", 12, "Dump all Facebook streams.") {
+ APPEND_OPT(m_dump_all);
+ APPEND_OPT(m_dump_directory);
+ APPEND_OPT(m_dump_linux_cpuinfo);
+ APPEND_OPT(m_dump_linux_proc_status);
+ APPEND_OPT(m_dump_linux_lsb_release);
+ APPEND_OPT(m_dump_linux_cmdline);
+ APPEND_OPT(m_dump_linux_environ);
+ APPEND_OPT(m_dump_linux_auxv);
+ APPEND_OPT(m_dump_linux_maps);
+ APPEND_OPT(m_dump_linux_proc_stat);
+ APPEND_OPT(m_dump_linux_proc_uptime);
+ APPEND_OPT(m_dump_linux_proc_fd);
+ APPEND_OPT(m_dump_linux_all);
+ APPEND_OPT(m_fb_app_data);
+ APPEND_OPT(m_fb_build_id);
+ APPEND_OPT(m_fb_version);
+ APPEND_OPT(m_fb_java_stack);
+ APPEND_OPT(m_fb_dalvik);
+ APPEND_OPT(m_fb_unwind);
+ APPEND_OPT(m_fb_error_log);
+ APPEND_OPT(m_fb_app_state);
+ APPEND_OPT(m_fb_abort);
+ APPEND_OPT(m_fb_thread);
+ APPEND_OPT(m_fb_logcat);
+ APPEND_OPT(m_fb_all);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectProcessMinidumpDump() override {}
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc > 0) {
+ result.AppendErrorWithFormat("'%s' take no arguments, only options",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ SetDefaultOptionsIfNoneAreSet();
+
+ ProcessMinidump *process = static_cast<ProcessMinidump *>(
+ m_interpreter.GetExecutionContext().GetProcessPtr());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ Stream &s = result.GetOutputStream();
+ MinidumpParser &minidump = *process->m_minidump_parser;
+ if (DumpDirectory()) {
+ s.Printf("RVA SIZE TYPE StreamType\n");
+ s.Printf("---------- ---------- ---------- --------------------------\n");
+ for (const auto &stream_desc : minidump.GetMinidumpFile().streams())
+ s.Printf(
+ "0x%8.8x 0x%8.8x 0x%8.8x %s\n", (uint32_t)stream_desc.Location.RVA,
+ (uint32_t)stream_desc.Location.DataSize,
+ (unsigned)(StreamType)stream_desc.Type,
+ MinidumpParser::GetStreamTypeAsString(stream_desc.Type).data());
+ s.Printf("\n");
+ }
+ auto DumpTextStream = [&](StreamType stream_type,
+ llvm::StringRef label) -> void {
+ auto bytes = minidump.GetStream(stream_type);
+ if (!bytes.empty()) {
+ if (label.empty())
+ label = MinidumpParser::GetStreamTypeAsString(stream_type);
+ s.Printf("%s:\n%s\n\n", label.data(), bytes.data());
+ }
+ };
+ auto DumpBinaryStream = [&](StreamType stream_type,
+ llvm::StringRef label) -> void {
+ auto bytes = minidump.GetStream(stream_type);
+ if (!bytes.empty()) {
+ if (label.empty())
+ label = MinidumpParser::GetStreamTypeAsString(stream_type);
+ s.Printf("%s:\n", label.data());
+ DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,
+ process->GetAddressByteSize());
+ DumpDataExtractor(data, &s, 0, lldb::eFormatBytesWithASCII, 1,
+ bytes.size(), 16, 0, 0, 0);
+ s.Printf("\n\n");
+ }
+ };
+
+ if (DumpLinuxCPUInfo())
+ DumpTextStream(StreamType::LinuxCPUInfo, "/proc/cpuinfo");
+ if (DumpLinuxProcStatus())
+ DumpTextStream(StreamType::LinuxProcStatus, "/proc/PID/status");
+ if (DumpLinuxLSBRelease())
+ DumpTextStream(StreamType::LinuxLSBRelease, "/etc/lsb-release");
+ if (DumpLinuxCMDLine())
+ DumpTextStream(StreamType::LinuxCMDLine, "/proc/PID/cmdline");
+ if (DumpLinuxEnviron())
+ DumpTextStream(StreamType::LinuxEnviron, "/proc/PID/environ");
+ if (DumpLinuxAuxv())
+ DumpBinaryStream(StreamType::LinuxAuxv, "/proc/PID/auxv");
+ if (DumpLinuxMaps())
+ DumpTextStream(StreamType::LinuxMaps, "/proc/PID/maps");
+ if (DumpLinuxProcStat())
+ DumpTextStream(StreamType::LinuxProcStat, "/proc/PID/stat");
+ if (DumpLinuxProcUptime())
+ DumpTextStream(StreamType::LinuxProcUptime, "uptime");
+ if (DumpLinuxProcFD())
+ DumpTextStream(StreamType::LinuxProcFD, "/proc/PID/fd");
+ if (DumpFacebookAppData())
+ DumpTextStream(StreamType::FacebookAppCustomData,
+ "Facebook App Data");
+ if (DumpFacebookBuildID()) {
+ auto bytes = minidump.GetStream(StreamType::FacebookBuildID);
+ if (bytes.size() >= 4) {
+ DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,
+ process->GetAddressByteSize());
+ lldb::offset_t offset = 0;
+ uint32_t build_id = data.GetU32(&offset);
+ s.Printf("Facebook Build ID:\n");
+ s.Printf("%u\n", build_id);
+ s.Printf("\n");
+ }
+ }
+ if (DumpFacebookVersionName())
+ DumpTextStream(StreamType::FacebookAppVersionName,
+ "Facebook Version String");
+ if (DumpFacebookJavaStack())
+ DumpTextStream(StreamType::FacebookJavaStack,
+ "Facebook Java Stack");
+ if (DumpFacebookDalvikInfo())
+ DumpTextStream(StreamType::FacebookDalvikInfo,
+ "Facebook Dalvik Info");
+ if (DumpFacebookUnwindSymbols())
+ DumpBinaryStream(StreamType::FacebookUnwindSymbols,
+ "Facebook Unwind Symbols Bytes");
+ if (DumpFacebookErrorLog())
+ DumpTextStream(StreamType::FacebookDumpErrorLog,
+ "Facebook Error Log");
+ if (DumpFacebookAppStateLog())
+ DumpTextStream(StreamType::FacebookAppStateLog,
+ "Faceook Application State Log");
+ if (DumpFacebookAbortReason())
+ DumpTextStream(StreamType::FacebookAbortReason,
+ "Facebook Abort Reason");
+ if (DumpFacebookThreadName())
+ DumpTextStream(StreamType::FacebookThreadName,
+ "Facebook Thread Name");
+ if (DumpFacebookLogcat())
+ DumpTextStream(StreamType::FacebookLogcat,
+ "Facebook Logcat");
+ return true;
+ }
+};
+
+class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordProcessMinidump(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "process plugin",
+ "Commands for operating on a ProcessMinidump process.",
+ "process plugin <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("dump",
+ CommandObjectSP(new CommandObjectProcessMinidumpDump(interpreter)));
+ }
+
+ ~CommandObjectMultiwordProcessMinidump() override {}
+};
+
+CommandObject *ProcessMinidump::GetPluginCommandObject() {
+ if (!m_command_sp)
+ m_command_sp = std::make_shared<CommandObjectMultiwordProcessMinidump>(
+ GetTarget().GetDebugger().GetCommandInterpreter());
+ return m_command_sp.get();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
new file mode 100644
index 000000000000..c39040f61dc5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
@@ -0,0 +1,119 @@
+//===-- ProcessMinidump.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessMinidump_h_
+#define liblldb_ProcessMinidump_h_
+
+#include "MinidumpParser.h"
+#include "MinidumpTypes.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+
+
+namespace lldb_private {
+
+namespace minidump {
+
+class ProcessMinidump : public Process {
+public:
+ static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file_path);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ ProcessMinidump(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+ const FileSpec &core_file, lldb::DataBufferSP code_data);
+
+ ~ProcessMinidump() override;
+
+ bool CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) override;
+
+ CommandObject *GetPluginCommandObject() override;
+
+ Status DoLoadCore() override;
+
+ DynamicLoader *GetDynamicLoader() override { return nullptr; }
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ SystemRuntime *GetSystemRuntime() override { return nullptr; }
+
+ Status DoDestroy() override;
+
+ void RefreshStateAfterStop() override;
+
+ bool IsAlive() override;
+
+ bool WarnBeforeDetach() const override;
+
+ size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) override;
+
+ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) override;
+
+ ArchSpec GetArchitecture();
+
+ Status GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) override;
+
+ Status GetMemoryRegions(
+ lldb_private::MemoryRegionInfos &region_list) override;
+
+ bool GetProcessInfo(ProcessInstanceInfo &info) override;
+
+ Status WillResume() override {
+ Status error;
+ error.SetErrorStringWithFormat(
+ "error: %s does not support resuming processes",
+ GetPluginName().GetCString());
+ return error;
+ }
+
+ llvm::Optional<MinidumpParser> m_minidump_parser;
+
+protected:
+ void Clear();
+
+ bool UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) override;
+
+ void ReadModuleList();
+
+ JITLoaderList &GetJITLoaders() override;
+
+private:
+ FileSpec m_core_file;
+ lldb::DataBufferSP m_core_data;
+ llvm::ArrayRef<minidump::Thread> m_thread_list;
+ const MinidumpExceptionStream *m_active_exception;
+ lldb::CommandObjectSP m_command_sp;
+ bool m_is_wow64;
+};
+
+} // namespace minidump
+} // namespace lldb_private
+
+#endif // liblldb_ProcessMinidump_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp
new file mode 100644
index 000000000000..f2e456097dfc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp
@@ -0,0 +1,533 @@
+//===-- RegisterContextMinidump_ARM.cpp -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextMinidump_ARM.h"
+
+#include "Utility/ARM_DWARF_Registers.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/lldb-enumerations.h"
+
+// C includes
+#include <assert.h>
+
+// C++ includes
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace minidump;
+
+#define INV LLDB_INVALID_REGNUM
+#define OFFSET(r) (offsetof(RegisterContextMinidump_ARM::Context, r))
+
+#define DEF_R(i) \
+ { \
+ "r" #i, nullptr, 4, OFFSET(r) + i * 4, eEncodingUint, eFormatHex, \
+ {dwarf_r##i, dwarf_r##i, INV, INV, reg_r##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_R_ARG(i, n) \
+ { \
+ "r" #i, "arg" #n, 4, OFFSET(r) + i * 4, eEncodingUint, eFormatHex, \
+ {dwarf_r##i, dwarf_r##i, LLDB_REGNUM_GENERIC_ARG1 + i, INV, reg_r##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_D(i) \
+ { \
+ "d" #i, nullptr, 8, OFFSET(d) + i * 8, eEncodingVector, \
+ eFormatVectorOfUInt8, {dwarf_d##i, dwarf_d##i, INV, INV, reg_d##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_S(i) \
+ { \
+ "s" #i, nullptr, 4, OFFSET(s) + i * 4, eEncodingIEEE754, eFormatFloat, \
+ {dwarf_s##i, dwarf_s##i, INV, INV, reg_s##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_Q(i) \
+ { \
+ "q" #i, nullptr, 16, OFFSET(q) + i * 16, eEncodingVector, \
+ eFormatVectorOfUInt8, {dwarf_q##i, dwarf_q##i, INV, INV, reg_q##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+// Zero based LLDB register numbers for this register context
+enum {
+ // General Purpose Registers
+ reg_r0,
+ reg_r1,
+ reg_r2,
+ reg_r3,
+ reg_r4,
+ reg_r5,
+ reg_r6,
+ reg_r7,
+ reg_r8,
+ reg_r9,
+ reg_r10,
+ reg_r11,
+ reg_r12,
+ reg_sp,
+ reg_lr,
+ reg_pc,
+ reg_cpsr,
+ // Floating Point Registers
+ reg_fpscr,
+ reg_d0,
+ reg_d1,
+ reg_d2,
+ reg_d3,
+ reg_d4,
+ reg_d5,
+ reg_d6,
+ reg_d7,
+ reg_d8,
+ reg_d9,
+ reg_d10,
+ reg_d11,
+ reg_d12,
+ reg_d13,
+ reg_d14,
+ reg_d15,
+ reg_d16,
+ reg_d17,
+ reg_d18,
+ reg_d19,
+ reg_d20,
+ reg_d21,
+ reg_d22,
+ reg_d23,
+ reg_d24,
+ reg_d25,
+ reg_d26,
+ reg_d27,
+ reg_d28,
+ reg_d29,
+ reg_d30,
+ reg_d31,
+ reg_s0,
+ reg_s1,
+ reg_s2,
+ reg_s3,
+ reg_s4,
+ reg_s5,
+ reg_s6,
+ reg_s7,
+ reg_s8,
+ reg_s9,
+ reg_s10,
+ reg_s11,
+ reg_s12,
+ reg_s13,
+ reg_s14,
+ reg_s15,
+ reg_s16,
+ reg_s17,
+ reg_s18,
+ reg_s19,
+ reg_s20,
+ reg_s21,
+ reg_s22,
+ reg_s23,
+ reg_s24,
+ reg_s25,
+ reg_s26,
+ reg_s27,
+ reg_s28,
+ reg_s29,
+ reg_s30,
+ reg_s31,
+ reg_q0,
+ reg_q1,
+ reg_q2,
+ reg_q3,
+ reg_q4,
+ reg_q5,
+ reg_q6,
+ reg_q7,
+ reg_q8,
+ reg_q9,
+ reg_q10,
+ reg_q11,
+ reg_q12,
+ reg_q13,
+ reg_q14,
+ reg_q15,
+ k_num_regs
+};
+
+static RegisterInfo g_reg_info_apple_fp = {
+ "fp",
+ "r7",
+ 4,
+ OFFSET(r) + 7 * 4,
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_r7, LLDB_REGNUM_GENERIC_FP, INV, reg_r7},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0};
+
+static RegisterInfo g_reg_info_fp = {
+ "fp",
+ "r11",
+ 4,
+ OFFSET(r) + 11 * 4,
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_r11, LLDB_REGNUM_GENERIC_FP, INV, reg_r11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0};
+
+// Register info definitions for this register context
+static RegisterInfo g_reg_infos[] = {
+ DEF_R_ARG(0, 1),
+ DEF_R_ARG(1, 2),
+ DEF_R_ARG(2, 3),
+ DEF_R_ARG(3, 4),
+ DEF_R(4),
+ DEF_R(5),
+ DEF_R(6),
+ DEF_R(7),
+ DEF_R(8),
+ DEF_R(9),
+ DEF_R(10),
+ DEF_R(11),
+ DEF_R(12),
+ {"sp",
+ "r13",
+ 4,
+ OFFSET(r) + 13 * 4,
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_sp, LLDB_REGNUM_GENERIC_SP, INV, reg_sp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r14",
+ 4,
+ OFFSET(r) + 14 * 4,
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_lr, LLDB_REGNUM_GENERIC_RA, INV, reg_lr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "r15",
+ 4,
+ OFFSET(r) + 15 * 4,
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_pc, LLDB_REGNUM_GENERIC_PC, INV, reg_pc},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ OFFSET(cpsr),
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, INV, reg_cpsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpscr",
+ nullptr,
+ 8,
+ OFFSET(fpscr),
+ eEncodingUint,
+ eFormatHex,
+ {INV, INV, INV, INV, reg_fpscr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ DEF_D(0),
+ DEF_D(1),
+ DEF_D(2),
+ DEF_D(3),
+ DEF_D(4),
+ DEF_D(5),
+ DEF_D(6),
+ DEF_D(7),
+ DEF_D(8),
+ DEF_D(9),
+ DEF_D(10),
+ DEF_D(11),
+ DEF_D(12),
+ DEF_D(13),
+ DEF_D(14),
+ DEF_D(15),
+ DEF_D(16),
+ DEF_D(17),
+ DEF_D(18),
+ DEF_D(19),
+ DEF_D(20),
+ DEF_D(21),
+ DEF_D(22),
+ DEF_D(23),
+ DEF_D(24),
+ DEF_D(25),
+ DEF_D(26),
+ DEF_D(27),
+ DEF_D(28),
+ DEF_D(29),
+ DEF_D(30),
+ DEF_D(31),
+ DEF_S(0),
+ DEF_S(1),
+ DEF_S(2),
+ DEF_S(3),
+ DEF_S(4),
+ DEF_S(5),
+ DEF_S(6),
+ DEF_S(7),
+ DEF_S(8),
+ DEF_S(9),
+ DEF_S(10),
+ DEF_S(11),
+ DEF_S(12),
+ DEF_S(13),
+ DEF_S(14),
+ DEF_S(15),
+ DEF_S(16),
+ DEF_S(17),
+ DEF_S(18),
+ DEF_S(19),
+ DEF_S(20),
+ DEF_S(21),
+ DEF_S(22),
+ DEF_S(23),
+ DEF_S(24),
+ DEF_S(25),
+ DEF_S(26),
+ DEF_S(27),
+ DEF_S(28),
+ DEF_S(29),
+ DEF_S(30),
+ DEF_S(31),
+ DEF_Q(0),
+ DEF_Q(1),
+ DEF_Q(2),
+ DEF_Q(3),
+ DEF_Q(4),
+ DEF_Q(5),
+ DEF_Q(6),
+ DEF_Q(7),
+ DEF_Q(8),
+ DEF_Q(9),
+ DEF_Q(10),
+ DEF_Q(11),
+ DEF_Q(12),
+ DEF_Q(13),
+ DEF_Q(14),
+ DEF_Q(15)};
+
+constexpr size_t k_num_reg_infos = llvm::array_lengthof(g_reg_infos);
+
+// ARM general purpose registers.
+const uint32_t g_gpr_regnums[] = {
+ reg_r0,
+ reg_r1,
+ reg_r2,
+ reg_r3,
+ reg_r4,
+ reg_r5,
+ reg_r6,
+ reg_r7,
+ reg_r8,
+ reg_r9,
+ reg_r10,
+ reg_r11,
+ reg_r12,
+ reg_sp,
+ reg_lr,
+ reg_pc,
+ reg_cpsr,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+const uint32_t g_fpu_regnums[] = {
+ reg_fpscr,
+ reg_d0,
+ reg_d1,
+ reg_d2,
+ reg_d3,
+ reg_d4,
+ reg_d5,
+ reg_d6,
+ reg_d7,
+ reg_d8,
+ reg_d9,
+ reg_d10,
+ reg_d11,
+ reg_d12,
+ reg_d13,
+ reg_d14,
+ reg_d15,
+ reg_d16,
+ reg_d17,
+ reg_d18,
+ reg_d19,
+ reg_d20,
+ reg_d21,
+ reg_d22,
+ reg_d23,
+ reg_d24,
+ reg_d25,
+ reg_d26,
+ reg_d27,
+ reg_d28,
+ reg_d29,
+ reg_d30,
+ reg_d31,
+ reg_s0,
+ reg_s1,
+ reg_s2,
+ reg_s3,
+ reg_s4,
+ reg_s5,
+ reg_s6,
+ reg_s7,
+ reg_s8,
+ reg_s9,
+ reg_s10,
+ reg_s11,
+ reg_s12,
+ reg_s13,
+ reg_s14,
+ reg_s15,
+ reg_s16,
+ reg_s17,
+ reg_s18,
+ reg_s19,
+ reg_s20,
+ reg_s21,
+ reg_s22,
+ reg_s23,
+ reg_s24,
+ reg_s25,
+ reg_s26,
+ reg_s27,
+ reg_s28,
+ reg_s29,
+ reg_s30,
+ reg_s31,
+ reg_q0,
+ reg_q1,
+ reg_q2,
+ reg_q3,
+ reg_q4,
+ reg_q5,
+ reg_q6,
+ reg_q7,
+ reg_q8,
+ reg_q9,
+ reg_q10,
+ reg_q11,
+ reg_q12,
+ reg_q13,
+ reg_q14,
+ reg_q15,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+// Skip the last LLDB_INVALID_REGNUM in each count below by subtracting 1
+constexpr size_t k_num_gpr_regs = llvm::array_lengthof(g_gpr_regnums) - 1;
+constexpr size_t k_num_fpu_regs = llvm::array_lengthof(g_fpu_regnums) - 1;
+
+static RegisterSet g_reg_sets[] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_regs, g_gpr_regnums},
+ {"Floating Point Registers", "fpu", k_num_fpu_regs, g_fpu_regnums},
+};
+
+constexpr size_t k_num_reg_sets = llvm::array_lengthof(g_reg_sets);
+
+RegisterContextMinidump_ARM::RegisterContextMinidump_ARM(
+ lldb_private::Thread &thread, const DataExtractor &data, bool apple)
+ : RegisterContext(thread, 0), m_apple(apple) {
+ lldb::offset_t offset = 0;
+ m_regs.context_flags = data.GetU32(&offset);
+ for (unsigned i = 0; i < llvm::array_lengthof(m_regs.r); ++i)
+ m_regs.r[i] = data.GetU32(&offset);
+ m_regs.cpsr = data.GetU32(&offset);
+ m_regs.fpscr = data.GetU64(&offset);
+ for (unsigned i = 0; i < llvm::array_lengthof(m_regs.d); ++i)
+ m_regs.d[i] = data.GetU64(&offset);
+ lldbassert(k_num_regs == k_num_reg_infos);
+}
+
+size_t RegisterContextMinidump_ARM::GetRegisterCount() { return k_num_regs; }
+
+const RegisterInfo *
+RegisterContextMinidump_ARM::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < k_num_reg_infos) {
+ if (m_apple) {
+ if (reg == reg_r7)
+ return &g_reg_info_apple_fp;
+ } else {
+ if (reg == reg_r11)
+ return &g_reg_info_fp;
+ }
+ return &g_reg_infos[reg];
+ }
+ return nullptr;
+}
+
+size_t RegisterContextMinidump_ARM::GetRegisterSetCount() {
+ return k_num_reg_sets;
+}
+
+const RegisterSet *RegisterContextMinidump_ARM::GetRegisterSet(size_t set) {
+ if (set < k_num_reg_sets)
+ return &g_reg_sets[set];
+ return nullptr;
+}
+
+const char *RegisterContextMinidump_ARM::GetRegisterName(unsigned reg) {
+ if (reg < k_num_reg_infos)
+ return g_reg_infos[reg].name;
+ return nullptr;
+}
+
+bool RegisterContextMinidump_ARM::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ Status error;
+ reg_value.SetFromMemoryData(
+ reg_info, (const uint8_t *)&m_regs + reg_info->byte_offset,
+ reg_info->byte_size, lldb::eByteOrderLittle, error);
+ return error.Success();
+}
+
+bool RegisterContextMinidump_ARM::WriteRegister(const RegisterInfo *,
+ const RegisterValue &) {
+ return false;
+}
+
+uint32_t RegisterContextMinidump_ARM::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ for (size_t i = 0; i < k_num_regs; ++i) {
+ if (g_reg_infos[i].kinds[kind] == num)
+ return i;
+ }
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h
new file mode 100644
index 000000000000..eff8cdfef00a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h
@@ -0,0 +1,92 @@
+//===-- RegisterContextMinidump_ARM.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMinidump_ARM_h_
+#define liblldb_RegisterContextMinidump_ARM_h_
+
+#include "MinidumpTypes.h"
+
+#include "Plugins/Process/Utility/RegisterInfoInterface.h"
+
+#include "lldb/Target/RegisterContext.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitmaskEnum.h"
+
+// C includes
+// C++ includes
+
+namespace lldb_private {
+
+namespace minidump {
+
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+class RegisterContextMinidump_ARM : public lldb_private::RegisterContext {
+public:
+ RegisterContextMinidump_ARM(lldb_private::Thread &thread,
+ const DataExtractor &data, bool apple);
+
+ ~RegisterContextMinidump_ARM() override = default;
+
+ void InvalidateAllRegisters() override {
+ // Do nothing... registers are always valid...
+ }
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ bool ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) override;
+
+ bool WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ // Reference: see breakpad/crashpad source
+ struct QRegValue {
+ uint64_t lo;
+ uint64_t hi;
+ };
+
+ struct Context {
+ uint32_t context_flags;
+ uint32_t r[16];
+ uint32_t cpsr;
+ uint64_t fpscr;
+ union {
+ uint64_t d[32];
+ uint32_t s[32];
+ QRegValue q[16];
+ };
+ uint32_t extra[8];
+ };
+
+protected:
+ enum class Flags : uint32_t {
+ ARM_Flag = 0x40000000,
+ Integer = ARM_Flag | 0x00000002,
+ FloatingPoint = ARM_Flag | 0x00000004,
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ FloatingPoint)
+ };
+ Context m_regs;
+ const bool m_apple; // True if this is an Apple ARM where FP is R7
+};
+
+} // end namespace minidump
+} // end namespace lldb_private
+#endif // liblldb_RegisterContextMinidump_ARM_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp
new file mode 100644
index 000000000000..bbd0e14a3267
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp
@@ -0,0 +1,833 @@
+//===-- RegisterContextMinidump_ARM64.cpp -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextMinidump_ARM64.h"
+
+#include "Utility/ARM64_DWARF_Registers.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/lldb-enumerations.h"
+
+// C includes
+#include <assert.h>
+
+// C++ includes
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace minidump;
+
+#define INV LLDB_INVALID_REGNUM
+#define OFFSET(r) (offsetof(RegisterContextMinidump_ARM64::Context, r))
+
+#define DEF_X(i) \
+ { \
+ "x" #i, nullptr, 8, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \
+ {arm64_dwarf::x##i, arm64_dwarf::x##i, INV, INV, reg_x##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_W(i) \
+ { \
+ "w" #i, nullptr, 4, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \
+ {INV, INV, INV, INV, reg_w##i}, nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_X_ARG(i, n) \
+ { \
+ "x" #i, "arg" #n, 8, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \
+ {arm64_dwarf::x##i, arm64_dwarf::x##i, LLDB_REGNUM_GENERIC_ARG1 + i, \
+ INV, reg_x##i}, nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_V(i) \
+ { \
+ "v" #i, nullptr, 16, OFFSET(v) + i * 16, eEncodingVector, \
+ eFormatVectorOfUInt8, {arm64_dwarf::v##i, arm64_dwarf::v##i, INV, INV, \
+ reg_v##i}, nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_D(i) \
+ { \
+ "d" #i, nullptr, 8, OFFSET(v) + i * 16, eEncodingVector, \
+ eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_d##i}, nullptr, \
+ nullptr, nullptr, 0 \
+ }
+
+#define DEF_S(i) \
+ { \
+ "s" #i, nullptr, 4, OFFSET(v) + i * 16, eEncodingVector, \
+ eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_s##i}, nullptr, \
+ nullptr, nullptr, 0 \
+ }
+
+#define DEF_H(i) \
+ { \
+ "h" #i, nullptr, 2, OFFSET(v) + i * 16, eEncodingVector, \
+ eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_h##i}, nullptr, \
+ nullptr, nullptr, 0 \
+ }
+
+// Zero based LLDB register numbers for this register context
+enum {
+ // General Purpose Registers
+ reg_x0 = 0,
+ reg_x1,
+ reg_x2,
+ reg_x3,
+ reg_x4,
+ reg_x5,
+ reg_x6,
+ reg_x7,
+ reg_x8,
+ reg_x9,
+ reg_x10,
+ reg_x11,
+ reg_x12,
+ reg_x13,
+ reg_x14,
+ reg_x15,
+ reg_x16,
+ reg_x17,
+ reg_x18,
+ reg_x19,
+ reg_x20,
+ reg_x21,
+ reg_x22,
+ reg_x23,
+ reg_x24,
+ reg_x25,
+ reg_x26,
+ reg_x27,
+ reg_x28,
+ reg_fp,
+ reg_lr,
+ reg_sp,
+ reg_pc,
+ reg_w0,
+ reg_w1,
+ reg_w2,
+ reg_w3,
+ reg_w4,
+ reg_w5,
+ reg_w6,
+ reg_w7,
+ reg_w8,
+ reg_w9,
+ reg_w10,
+ reg_w11,
+ reg_w12,
+ reg_w13,
+ reg_w14,
+ reg_w15,
+ reg_w16,
+ reg_w17,
+ reg_w18,
+ reg_w19,
+ reg_w20,
+ reg_w21,
+ reg_w22,
+ reg_w23,
+ reg_w24,
+ reg_w25,
+ reg_w26,
+ reg_w27,
+ reg_w28,
+ reg_w29,
+ reg_w30,
+ reg_w31,
+ reg_cpsr,
+ // Floating Point Registers
+ reg_fpsr,
+ reg_fpcr,
+ reg_v0,
+ reg_v1,
+ reg_v2,
+ reg_v3,
+ reg_v4,
+ reg_v5,
+ reg_v6,
+ reg_v7,
+ reg_v8,
+ reg_v9,
+ reg_v10,
+ reg_v11,
+ reg_v12,
+ reg_v13,
+ reg_v14,
+ reg_v15,
+ reg_v16,
+ reg_v17,
+ reg_v18,
+ reg_v19,
+ reg_v20,
+ reg_v21,
+ reg_v22,
+ reg_v23,
+ reg_v24,
+ reg_v25,
+ reg_v26,
+ reg_v27,
+ reg_v28,
+ reg_v29,
+ reg_v30,
+ reg_v31,
+ reg_d0,
+ reg_d1,
+ reg_d2,
+ reg_d3,
+ reg_d4,
+ reg_d5,
+ reg_d6,
+ reg_d7,
+ reg_d8,
+ reg_d9,
+ reg_d10,
+ reg_d11,
+ reg_d12,
+ reg_d13,
+ reg_d14,
+ reg_d15,
+ reg_d16,
+ reg_d17,
+ reg_d18,
+ reg_d19,
+ reg_d20,
+ reg_d21,
+ reg_d22,
+ reg_d23,
+ reg_d24,
+ reg_d25,
+ reg_d26,
+ reg_d27,
+ reg_d28,
+ reg_d29,
+ reg_d30,
+ reg_d31,
+ reg_s0,
+ reg_s1,
+ reg_s2,
+ reg_s3,
+ reg_s4,
+ reg_s5,
+ reg_s6,
+ reg_s7,
+ reg_s8,
+ reg_s9,
+ reg_s10,
+ reg_s11,
+ reg_s12,
+ reg_s13,
+ reg_s14,
+ reg_s15,
+ reg_s16,
+ reg_s17,
+ reg_s18,
+ reg_s19,
+ reg_s20,
+ reg_s21,
+ reg_s22,
+ reg_s23,
+ reg_s24,
+ reg_s25,
+ reg_s26,
+ reg_s27,
+ reg_s28,
+ reg_s29,
+ reg_s30,
+ reg_s31,
+ reg_h0,
+ reg_h1,
+ reg_h2,
+ reg_h3,
+ reg_h4,
+ reg_h5,
+ reg_h6,
+ reg_h7,
+ reg_h8,
+ reg_h9,
+ reg_h10,
+ reg_h11,
+ reg_h12,
+ reg_h13,
+ reg_h14,
+ reg_h15,
+ reg_h16,
+ reg_h17,
+ reg_h18,
+ reg_h19,
+ reg_h20,
+ reg_h21,
+ reg_h22,
+ reg_h23,
+ reg_h24,
+ reg_h25,
+ reg_h26,
+ reg_h27,
+ reg_h28,
+ reg_h29,
+ reg_h30,
+ reg_h31,
+ k_num_regs
+};
+
+// Register info definitions for this register context
+static RegisterInfo g_reg_infos[] = {
+ DEF_X_ARG(0, 1),
+ DEF_X_ARG(1, 2),
+ DEF_X_ARG(2, 3),
+ DEF_X_ARG(3, 4),
+ DEF_X_ARG(4, 5),
+ DEF_X_ARG(5, 6),
+ DEF_X_ARG(6, 7),
+ DEF_X_ARG(7, 8),
+ DEF_X(8),
+ DEF_X(9),
+ DEF_X(10),
+ DEF_X(11),
+ DEF_X(12),
+ DEF_X(13),
+ DEF_X(14),
+ DEF_X(15),
+ DEF_X(16),
+ DEF_X(17),
+ DEF_X(18),
+ DEF_X(19),
+ DEF_X(20),
+ DEF_X(21),
+ DEF_X(22),
+ DEF_X(23),
+ DEF_X(24),
+ DEF_X(25),
+ DEF_X(26),
+ DEF_X(27),
+ DEF_X(28),
+ {"fp",
+ "x29",
+ 8,
+ OFFSET(x) + 29 * 8,
+ eEncodingUint,
+ eFormatHex,
+ {arm64_dwarf::x29, arm64_dwarf::x29, LLDB_REGNUM_GENERIC_FP, INV, reg_fp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "x30",
+ 8,
+ OFFSET(x) + 30 * 8,
+ eEncodingUint,
+ eFormatHex,
+ {arm64_dwarf::x30, arm64_dwarf::x30, LLDB_REGNUM_GENERIC_RA, INV, reg_lr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "x31",
+ 8,
+ OFFSET(x) + 31 * 8,
+ eEncodingUint,
+ eFormatHex,
+ {arm64_dwarf::x31, arm64_dwarf::x31, LLDB_REGNUM_GENERIC_SP, INV, reg_sp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ nullptr,
+ 8,
+ OFFSET(pc),
+ eEncodingUint,
+ eFormatHex,
+ {arm64_dwarf::pc, arm64_dwarf::pc, LLDB_REGNUM_GENERIC_PC, INV, reg_pc},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // w0 - w31
+ DEF_W(0),
+ DEF_W(1),
+ DEF_W(2),
+ DEF_W(3),
+ DEF_W(4),
+ DEF_W(5),
+ DEF_W(6),
+ DEF_W(7),
+ DEF_W(8),
+ DEF_W(9),
+ DEF_W(10),
+ DEF_W(11),
+ DEF_W(12),
+ DEF_W(13),
+ DEF_W(14),
+ DEF_W(15),
+ DEF_W(16),
+ DEF_W(17),
+ DEF_W(18),
+ DEF_W(19),
+ DEF_W(20),
+ DEF_W(21),
+ DEF_W(22),
+ DEF_W(23),
+ DEF_W(24),
+ DEF_W(25),
+ DEF_W(26),
+ DEF_W(27),
+ DEF_W(28),
+ DEF_W(29),
+ DEF_W(30),
+ DEF_W(31),
+ {"cpsr",
+ "psr",
+ 4,
+ OFFSET(cpsr),
+ eEncodingUint,
+ eFormatHex,
+ {INV, arm64_dwarf::cpsr, LLDB_REGNUM_GENERIC_FLAGS, INV, reg_cpsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpsr",
+ nullptr,
+ 4,
+ OFFSET(fpsr),
+ eEncodingUint,
+ eFormatHex,
+ {INV, INV, INV, INV, reg_fpsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpcr",
+ nullptr,
+ 4,
+ OFFSET(fpcr),
+ eEncodingUint,
+ eFormatHex,
+ {INV, INV, INV, INV, reg_fpcr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // v0 - v31
+ DEF_V(0),
+ DEF_V(1),
+ DEF_V(2),
+ DEF_V(3),
+ DEF_V(4),
+ DEF_V(5),
+ DEF_V(6),
+ DEF_V(7),
+ DEF_V(8),
+ DEF_V(9),
+ DEF_V(10),
+ DEF_V(11),
+ DEF_V(12),
+ DEF_V(13),
+ DEF_V(14),
+ DEF_V(15),
+ DEF_V(16),
+ DEF_V(17),
+ DEF_V(18),
+ DEF_V(19),
+ DEF_V(20),
+ DEF_V(21),
+ DEF_V(22),
+ DEF_V(23),
+ DEF_V(24),
+ DEF_V(25),
+ DEF_V(26),
+ DEF_V(27),
+ DEF_V(28),
+ DEF_V(29),
+ DEF_V(30),
+ DEF_V(31),
+ // d0 - d31
+ DEF_D(0),
+ DEF_D(1),
+ DEF_D(2),
+ DEF_D(3),
+ DEF_D(4),
+ DEF_D(5),
+ DEF_D(6),
+ DEF_D(7),
+ DEF_D(8),
+ DEF_D(9),
+ DEF_D(10),
+ DEF_D(11),
+ DEF_D(12),
+ DEF_D(13),
+ DEF_D(14),
+ DEF_D(15),
+ DEF_D(16),
+ DEF_D(17),
+ DEF_D(18),
+ DEF_D(19),
+ DEF_D(20),
+ DEF_D(21),
+ DEF_D(22),
+ DEF_D(23),
+ DEF_D(24),
+ DEF_D(25),
+ DEF_D(26),
+ DEF_D(27),
+ DEF_D(28),
+ DEF_D(29),
+ DEF_D(30),
+ DEF_D(31),
+ // s0 - s31
+ DEF_S(0),
+ DEF_S(1),
+ DEF_S(2),
+ DEF_S(3),
+ DEF_S(4),
+ DEF_S(5),
+ DEF_S(6),
+ DEF_S(7),
+ DEF_S(8),
+ DEF_S(9),
+ DEF_S(10),
+ DEF_S(11),
+ DEF_S(12),
+ DEF_S(13),
+ DEF_S(14),
+ DEF_S(15),
+ DEF_S(16),
+ DEF_S(17),
+ DEF_S(18),
+ DEF_S(19),
+ DEF_S(20),
+ DEF_S(21),
+ DEF_S(22),
+ DEF_S(23),
+ DEF_S(24),
+ DEF_S(25),
+ DEF_S(26),
+ DEF_S(27),
+ DEF_S(28),
+ DEF_S(29),
+ DEF_S(30),
+ DEF_S(31),
+ // h0 - h31
+ DEF_H(0),
+ DEF_H(1),
+ DEF_H(2),
+ DEF_H(3),
+ DEF_H(4),
+ DEF_H(5),
+ DEF_H(6),
+ DEF_H(7),
+ DEF_H(8),
+ DEF_H(9),
+ DEF_H(10),
+ DEF_H(11),
+ DEF_H(12),
+ DEF_H(13),
+ DEF_H(14),
+ DEF_H(15),
+ DEF_H(16),
+ DEF_H(17),
+ DEF_H(18),
+ DEF_H(19),
+ DEF_H(20),
+ DEF_H(21),
+ DEF_H(22),
+ DEF_H(23),
+ DEF_H(24),
+ DEF_H(25),
+ DEF_H(26),
+ DEF_H(27),
+ DEF_H(28),
+ DEF_H(29),
+ DEF_H(30),
+ DEF_H(31),
+};
+
+constexpr size_t k_num_reg_infos = llvm::array_lengthof(g_reg_infos);
+
+// ARM64 general purpose registers.
+const uint32_t g_gpr_regnums[] = {
+ reg_x0,
+ reg_x1,
+ reg_x2,
+ reg_x3,
+ reg_x4,
+ reg_x5,
+ reg_x6,
+ reg_x7,
+ reg_x8,
+ reg_x9,
+ reg_x10,
+ reg_x11,
+ reg_x12,
+ reg_x13,
+ reg_x14,
+ reg_x15,
+ reg_x16,
+ reg_x17,
+ reg_x18,
+ reg_x19,
+ reg_x20,
+ reg_x21,
+ reg_x22,
+ reg_x23,
+ reg_x24,
+ reg_x25,
+ reg_x26,
+ reg_x27,
+ reg_x28,
+ reg_fp,
+ reg_lr,
+ reg_sp,
+ reg_w0,
+ reg_w1,
+ reg_w2,
+ reg_w3,
+ reg_w4,
+ reg_w5,
+ reg_w6,
+ reg_w7,
+ reg_w8,
+ reg_w9,
+ reg_w10,
+ reg_w11,
+ reg_w12,
+ reg_w13,
+ reg_w14,
+ reg_w15,
+ reg_w16,
+ reg_w17,
+ reg_w18,
+ reg_w19,
+ reg_w20,
+ reg_w21,
+ reg_w22,
+ reg_w23,
+ reg_w24,
+ reg_w25,
+ reg_w26,
+ reg_w27,
+ reg_w28,
+ reg_w29,
+ reg_w30,
+ reg_w31,
+ reg_pc,
+ reg_cpsr,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+const uint32_t g_fpu_regnums[] = {
+ reg_v0,
+ reg_v1,
+ reg_v2,
+ reg_v3,
+ reg_v4,
+ reg_v5,
+ reg_v6,
+ reg_v7,
+ reg_v8,
+ reg_v9,
+ reg_v10,
+ reg_v11,
+ reg_v12,
+ reg_v13,
+ reg_v14,
+ reg_v15,
+ reg_v16,
+ reg_v17,
+ reg_v18,
+ reg_v19,
+ reg_v20,
+ reg_v21,
+ reg_v22,
+ reg_v23,
+ reg_v24,
+ reg_v25,
+ reg_v26,
+ reg_v27,
+ reg_v28,
+ reg_v29,
+ reg_v30,
+ reg_v31,
+ reg_d0,
+ reg_d1,
+ reg_d2,
+ reg_d3,
+ reg_d4,
+ reg_d5,
+ reg_d6,
+ reg_d7,
+ reg_d8,
+ reg_d9,
+ reg_d10,
+ reg_d11,
+ reg_d12,
+ reg_d13,
+ reg_d14,
+ reg_d15,
+ reg_d16,
+ reg_d17,
+ reg_d18,
+ reg_d19,
+ reg_d20,
+ reg_d21,
+ reg_d22,
+ reg_d23,
+ reg_d24,
+ reg_d25,
+ reg_d26,
+ reg_d27,
+ reg_d28,
+ reg_d29,
+ reg_d30,
+ reg_d31,
+ reg_s0,
+ reg_s1,
+ reg_s2,
+ reg_s3,
+ reg_s4,
+ reg_s5,
+ reg_s6,
+ reg_s7,
+ reg_s8,
+ reg_s9,
+ reg_s10,
+ reg_s11,
+ reg_s12,
+ reg_s13,
+ reg_s14,
+ reg_s15,
+ reg_s16,
+ reg_s17,
+ reg_s18,
+ reg_s19,
+ reg_s20,
+ reg_s21,
+ reg_s22,
+ reg_s23,
+ reg_s24,
+ reg_s25,
+ reg_s26,
+ reg_s27,
+ reg_s28,
+ reg_s29,
+ reg_s30,
+ reg_s31,
+ reg_h0,
+ reg_h1,
+ reg_h2,
+ reg_h3,
+ reg_h4,
+ reg_h5,
+ reg_h6,
+ reg_h7,
+ reg_h8,
+ reg_h9,
+ reg_h10,
+ reg_h11,
+ reg_h12,
+ reg_h13,
+ reg_h14,
+ reg_h15,
+ reg_h16,
+ reg_h17,
+ reg_h18,
+ reg_h19,
+ reg_h20,
+ reg_h21,
+ reg_h22,
+ reg_h23,
+ reg_h24,
+ reg_h25,
+ reg_h26,
+ reg_h27,
+ reg_h28,
+ reg_h29,
+ reg_h30,
+ reg_h31,
+ reg_fpsr,
+ reg_fpcr,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+// Skip the last LLDB_INVALID_REGNUM in each count below by subtracting 1
+constexpr size_t k_num_gpr_regs = llvm::array_lengthof(g_gpr_regnums) - 1;
+constexpr size_t k_num_fpu_regs = llvm::array_lengthof(g_fpu_regnums) - 1;
+
+static RegisterSet g_reg_sets[] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_regs, g_gpr_regnums},
+ {"Floating Point Registers", "fpu", k_num_fpu_regs, g_fpu_regnums},
+};
+
+constexpr size_t k_num_reg_sets = llvm::array_lengthof(g_reg_sets);
+
+RegisterContextMinidump_ARM64::RegisterContextMinidump_ARM64(
+ lldb_private::Thread &thread, const DataExtractor &data)
+ : RegisterContext(thread, 0) {
+ lldb::offset_t offset = 0;
+ m_regs.context_flags = data.GetU64(&offset);
+ for (unsigned i = 0; i < 32; ++i)
+ m_regs.x[i] = data.GetU64(&offset);
+ m_regs.pc = data.GetU64(&offset);
+ m_regs.cpsr = data.GetU32(&offset);
+ m_regs.fpsr = data.GetU32(&offset);
+ m_regs.fpcr = data.GetU32(&offset);
+ auto regs_data = data.GetData(&offset, sizeof(m_regs.v));
+ if (regs_data)
+ memcpy(m_regs.v, regs_data, sizeof(m_regs.v));
+ static_assert(k_num_regs == k_num_reg_infos, "");
+}
+size_t RegisterContextMinidump_ARM64::GetRegisterCount() { return k_num_regs; }
+
+const RegisterInfo *
+RegisterContextMinidump_ARM64::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < k_num_reg_infos)
+ return &g_reg_infos[reg];
+ return nullptr;
+}
+
+size_t RegisterContextMinidump_ARM64::GetRegisterSetCount() {
+ return k_num_reg_sets;
+}
+
+const RegisterSet *RegisterContextMinidump_ARM64::GetRegisterSet(size_t set) {
+ if (set < k_num_reg_sets)
+ return &g_reg_sets[set];
+ return nullptr;
+}
+
+const char *RegisterContextMinidump_ARM64::GetRegisterName(unsigned reg) {
+ if (reg < k_num_reg_infos)
+ return g_reg_infos[reg].name;
+ return nullptr;
+}
+
+bool RegisterContextMinidump_ARM64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ Status error;
+ reg_value.SetFromMemoryData(
+ reg_info, (const uint8_t *)&m_regs + reg_info->byte_offset,
+ reg_info->byte_size, lldb::eByteOrderLittle, error);
+ return error.Success();
+}
+
+bool RegisterContextMinidump_ARM64::WriteRegister(const RegisterInfo *,
+ const RegisterValue &) {
+ return false;
+}
+
+uint32_t RegisterContextMinidump_ARM64::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ for (size_t i = 0; i < k_num_regs; ++i) {
+ if (g_reg_infos[i].kinds[kind] == num)
+ return i;
+ }
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h
new file mode 100644
index 000000000000..f9e7f39eea60
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h
@@ -0,0 +1,82 @@
+//===-- RegisterContextMinidump_ARM64.h -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMinidump_ARM64_h_
+#define liblldb_RegisterContextMinidump_ARM64_h_
+
+#include "MinidumpTypes.h"
+
+#include "Plugins/Process/Utility/RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitmaskEnum.h"
+
+// C includes
+// C++ includes
+
+namespace lldb_private {
+
+namespace minidump {
+
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+class RegisterContextMinidump_ARM64 : public lldb_private::RegisterContext {
+public:
+ RegisterContextMinidump_ARM64(lldb_private::Thread &thread,
+ const DataExtractor &data);
+
+ ~RegisterContextMinidump_ARM64() override = default;
+
+ void InvalidateAllRegisters() override {
+ // Do nothing... registers are always valid...
+ }
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ bool ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) override;
+
+ bool WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ // Reference: see breakpad/crashpad source
+ struct Context {
+ uint64_t context_flags;
+ uint64_t x[32];
+ uint64_t pc;
+ uint32_t cpsr;
+ uint32_t fpsr;
+ uint32_t fpcr;
+ uint8_t v[32 * 16]; // 32 128-bit floating point registers
+ };
+
+protected:
+ enum class Flags : uint32_t {
+ ARM64_Flag = 0x80000000,
+ Integer = ARM64_Flag | 0x00000002,
+ FloatingPoint = ARM64_Flag | 0x00000004,
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ FloatingPoint)
+ };
+ Context m_regs;
+};
+
+} // end namespace minidump
+} // end namespace lldb_private
+#endif // liblldb_RegisterContextMinidump_ARM64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp
new file mode 100644
index 000000000000..8ac2abb22093
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp
@@ -0,0 +1,96 @@
+//===-- RegisterContextMinidump_x86_32.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextMinidump_x86_32.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+
+// C includes
+// C++ includes
+
+using namespace lldb_private;
+using namespace minidump;
+
+static void writeRegister(const void *reg_src,
+ llvm::MutableArrayRef<uint8_t> reg_dest) {
+ memcpy(reg_dest.data(), reg_src, reg_dest.size());
+}
+
+lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_32(
+ llvm::ArrayRef<uint8_t> source_data,
+ RegisterInfoInterface *target_reg_interface) {
+
+ const RegisterInfo *reg_info = target_reg_interface->GetRegisterInfo();
+
+ lldb::DataBufferSP result_context_buf(
+ new DataBufferHeap(target_reg_interface->GetGPRSize(), 0));
+ uint8_t *result_base = result_context_buf->GetBytes();
+
+ if (source_data.size() < sizeof(MinidumpContext_x86_32))
+ return nullptr;
+
+ const MinidumpContext_x86_32 *context;
+ consumeObject(source_data, context);
+
+ const MinidumpContext_x86_32_Flags context_flags =
+ static_cast<MinidumpContext_x86_32_Flags>(
+ static_cast<uint32_t>(context->context_flags));
+ auto x86_32_Flag = MinidumpContext_x86_32_Flags::x86_32_Flag;
+ auto ControlFlag = MinidumpContext_x86_32_Flags::Control;
+ auto IntegerFlag = MinidumpContext_x86_32_Flags::Integer;
+ auto SegmentsFlag = MinidumpContext_x86_32_Flags::Segments;
+
+ if ((context_flags & x86_32_Flag) != x86_32_Flag) {
+ return nullptr;
+ }
+
+ if ((context_flags & ControlFlag) == ControlFlag) {
+ writeRegister(&context->ebp,
+ reg_info[lldb_ebp_i386].mutable_data(result_base));
+ writeRegister(&context->eip,
+ reg_info[lldb_eip_i386].mutable_data(result_base));
+ writeRegister(&context->cs,
+ reg_info[lldb_cs_i386].mutable_data(result_base));
+ writeRegister(&context->eflags,
+ reg_info[lldb_eflags_i386].mutable_data(result_base));
+ writeRegister(&context->esp,
+ reg_info[lldb_esp_i386].mutable_data(result_base));
+ writeRegister(&context->ss,
+ reg_info[lldb_ss_i386].mutable_data(result_base));
+ }
+
+ if ((context_flags & SegmentsFlag) == SegmentsFlag) {
+ writeRegister(&context->ds,
+ reg_info[lldb_ds_i386].mutable_data(result_base));
+ writeRegister(&context->es,
+ reg_info[lldb_es_i386].mutable_data(result_base));
+ writeRegister(&context->fs,
+ reg_info[lldb_fs_i386].mutable_data(result_base));
+ writeRegister(&context->gs,
+ reg_info[lldb_gs_i386].mutable_data(result_base));
+ }
+
+ if ((context_flags & IntegerFlag) == IntegerFlag) {
+ writeRegister(&context->eax,
+ reg_info[lldb_eax_i386].mutable_data(result_base));
+ writeRegister(&context->ecx,
+ reg_info[lldb_ecx_i386].mutable_data(result_base));
+ writeRegister(&context->edx,
+ reg_info[lldb_edx_i386].mutable_data(result_base));
+ writeRegister(&context->ebx,
+ reg_info[lldb_ebx_i386].mutable_data(result_base));
+ writeRegister(&context->esi,
+ reg_info[lldb_esi_i386].mutable_data(result_base));
+ writeRegister(&context->edi,
+ reg_info[lldb_edi_i386].mutable_data(result_base));
+ }
+
+ // TODO parse the floating point registers
+
+ return result_context_buf;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h
new file mode 100644
index 000000000000..d787f78ec7d3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h
@@ -0,0 +1,135 @@
+//===-- RegisterContextMinidump_x86_32.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMinidump_x86_32_h_
+#define liblldb_RegisterContextMinidump_x86_32_h_
+
+#include "MinidumpTypes.h"
+
+#include "Plugins/Process/Utility/RegisterInfoInterface.h"
+#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
+
+#include "lldb/Target/RegisterContext.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/Support/Endian.h"
+
+// C includes
+// C++ includes
+
+namespace lldb_private {
+
+namespace minidump {
+
+// This function receives an ArrayRef pointing to the bytes of the Minidump
+// register context and returns a DataBuffer that's ordered by the offsets
+// specified in the RegisterInfoInterface argument
+// This way we can reuse the already existing register contexts
+lldb::DataBufferSP
+ConvertMinidumpContext_x86_32(llvm::ArrayRef<uint8_t> source_data,
+ RegisterInfoInterface *target_reg_interface);
+
+// Reference: see breakpad/crashpad source or WinNT.h
+struct MinidumpFloatingSaveAreaX86 {
+ llvm::support::ulittle32_t control_word;
+ llvm::support::ulittle32_t status_word;
+ llvm::support::ulittle32_t tag_word;
+ llvm::support::ulittle32_t error_offset;
+ llvm::support::ulittle32_t error_selector;
+ llvm::support::ulittle32_t data_offset;
+ llvm::support::ulittle32_t data_selector;
+
+ enum {
+ RegisterAreaSize = 80,
+ };
+ // register_area contains eight 80-bit (x87 "long double") quantities for
+ // floating-point registers %st0 (%mm0) through %st7 (%mm7).
+ uint8_t register_area[RegisterAreaSize];
+ llvm::support::ulittle32_t cr0_npx_state;
+};
+
+struct MinidumpContext_x86_32 {
+ // The context_flags field determines which parts
+ // of the structure are populated (have valid values)
+ llvm::support::ulittle32_t context_flags;
+
+ // The next 6 registers are included with
+ // MinidumpContext_x86_32_Flags::DebugRegisters
+ llvm::support::ulittle32_t dr0;
+ llvm::support::ulittle32_t dr1;
+ llvm::support::ulittle32_t dr2;
+ llvm::support::ulittle32_t dr3;
+ llvm::support::ulittle32_t dr6;
+ llvm::support::ulittle32_t dr7;
+
+ // The next field is included with
+ // MinidumpContext_x86_32_Flags::FloatingPoint
+ MinidumpFloatingSaveAreaX86 float_save;
+
+ // The next 4 registers are included with
+ // MinidumpContext_x86_32_Flags::Segments
+ llvm::support::ulittle32_t gs;
+ llvm::support::ulittle32_t fs;
+ llvm::support::ulittle32_t es;
+ llvm::support::ulittle32_t ds;
+
+ // The next 6 registers are included with
+ // MinidumpContext_x86_32_Flags::Integer
+ llvm::support::ulittle32_t edi;
+ llvm::support::ulittle32_t esi;
+ llvm::support::ulittle32_t ebx;
+ llvm::support::ulittle32_t edx;
+ llvm::support::ulittle32_t ecx;
+ llvm::support::ulittle32_t eax;
+
+ // The next 6 registers are included with
+ // MinidumpContext_x86_32_Flags::Control
+ llvm::support::ulittle32_t ebp;
+ llvm::support::ulittle32_t eip;
+ llvm::support::ulittle32_t cs; // WinNT.h says "must be sanitized"
+ llvm::support::ulittle32_t eflags; // WinNT.h says "must be sanitized"
+ llvm::support::ulittle32_t esp;
+ llvm::support::ulittle32_t ss;
+
+ // The next field is included with
+ // MinidumpContext_x86_32_Flags::ExtendedRegisters
+ // It contains vector (MMX/SSE) registers. It it laid out in the
+ // format used by the fxsave and fsrstor instructions, so it includes
+ // a copy of the x87 floating-point registers as well. See FXSAVE in
+ // "Intel Architecture Software Developer's Manual, Volume 2."
+ enum {
+ ExtendedRegistersSize = 512,
+ };
+ uint8_t extended_registers[ExtendedRegistersSize];
+};
+
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+// For context_flags. These values indicate the type of
+// context stored in the structure. The high 24 bits identify the CPU, the
+// low 8 bits identify the type of context saved.
+enum class MinidumpContext_x86_32_Flags : uint32_t {
+ x86_32_Flag = 0x00010000, // CONTEXT_i386, CONTEXT_i486
+ Control = x86_32_Flag | 0x00000001,
+ Integer = x86_32_Flag | 0x00000002,
+ Segments = x86_32_Flag | 0x00000004,
+ FloatingPoint = x86_32_Flag | 0x00000008,
+ DebugRegisters = x86_32_Flag | 0x00000010,
+ ExtendedRegisters = x86_32_Flag | 0x00000020,
+ XState = x86_32_Flag | 0x00000040,
+
+ Full = Control | Integer | Segments,
+ All = Full | FloatingPoint | DebugRegisters | ExtendedRegisters,
+
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ All)
+};
+
+} // end namespace minidump
+} // end namespace lldb_private
+#endif // liblldb_RegisterContextMinidump_x86_32_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp
new file mode 100644
index 000000000000..515ccf6b2c3c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp
@@ -0,0 +1,110 @@
+//===-- RegisterContextMinidump_x86_64.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextMinidump_x86_64.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+
+// C includes
+// C++ includes
+
+using namespace lldb_private;
+using namespace minidump;
+
+static llvm::MutableArrayRef<uint8_t> getDestRegister(uint8_t *context,
+ const RegisterInfo &reg) {
+ auto bytes = reg.mutable_data(context);
+
+ switch (reg.kinds[lldb::eRegisterKindLLDB]) {
+ case lldb_cs_x86_64:
+ case lldb_ds_x86_64:
+ case lldb_es_x86_64:
+ case lldb_fs_x86_64:
+ case lldb_gs_x86_64:
+ case lldb_ss_x86_64:
+ return bytes.take_front(2);
+ break;
+ case lldb_rflags_x86_64:
+ return bytes.take_front(4);
+ break;
+ default:
+ return bytes.take_front(8);
+ break;
+ }
+}
+
+static void writeRegister(const void *reg_src, uint8_t *context,
+ const RegisterInfo &reg) {
+ llvm::MutableArrayRef<uint8_t> reg_dest = getDestRegister(context, reg);
+ memcpy(reg_dest.data(), reg_src, reg_dest.size());
+}
+
+lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_64(
+ llvm::ArrayRef<uint8_t> source_data,
+ RegisterInfoInterface *target_reg_interface) {
+
+ const RegisterInfo *reg_info = target_reg_interface->GetRegisterInfo();
+
+ lldb::DataBufferSP result_context_buf(
+ new DataBufferHeap(target_reg_interface->GetGPRSize(), 0));
+ uint8_t *result_base = result_context_buf->GetBytes();
+
+ if (source_data.size() < sizeof(MinidumpContext_x86_64))
+ return nullptr;
+
+ const MinidumpContext_x86_64 *context;
+ consumeObject(source_data, context);
+
+ const MinidumpContext_x86_64_Flags context_flags =
+ static_cast<MinidumpContext_x86_64_Flags>(
+ static_cast<uint32_t>(context->context_flags));
+ auto x86_64_Flag = MinidumpContext_x86_64_Flags::x86_64_Flag;
+ auto ControlFlag = MinidumpContext_x86_64_Flags::Control;
+ auto IntegerFlag = MinidumpContext_x86_64_Flags::Integer;
+ auto SegmentsFlag = MinidumpContext_x86_64_Flags::Segments;
+
+ if ((context_flags & x86_64_Flag) != x86_64_Flag)
+ return nullptr;
+
+ if ((context_flags & ControlFlag) == ControlFlag) {
+ writeRegister(&context->cs, result_base, reg_info[lldb_cs_x86_64]);
+ writeRegister(&context->ss, result_base, reg_info[lldb_ss_x86_64]);
+ writeRegister(&context->eflags, result_base, reg_info[lldb_rflags_x86_64]);
+ writeRegister(&context->rsp, result_base, reg_info[lldb_rsp_x86_64]);
+ writeRegister(&context->rip, result_base, reg_info[lldb_rip_x86_64]);
+ }
+
+ if ((context_flags & SegmentsFlag) == SegmentsFlag) {
+ writeRegister(&context->ds, result_base, reg_info[lldb_ds_x86_64]);
+ writeRegister(&context->es, result_base, reg_info[lldb_es_x86_64]);
+ writeRegister(&context->fs, result_base, reg_info[lldb_fs_x86_64]);
+ writeRegister(&context->gs, result_base, reg_info[lldb_gs_x86_64]);
+ }
+
+ if ((context_flags & IntegerFlag) == IntegerFlag) {
+ writeRegister(&context->rax, result_base, reg_info[lldb_rax_x86_64]);
+ writeRegister(&context->rcx, result_base, reg_info[lldb_rcx_x86_64]);
+ writeRegister(&context->rdx, result_base, reg_info[lldb_rdx_x86_64]);
+ writeRegister(&context->rbx, result_base, reg_info[lldb_rbx_x86_64]);
+ writeRegister(&context->rbp, result_base, reg_info[lldb_rbp_x86_64]);
+ writeRegister(&context->rsi, result_base, reg_info[lldb_rsi_x86_64]);
+ writeRegister(&context->rdi, result_base, reg_info[lldb_rdi_x86_64]);
+ writeRegister(&context->r8, result_base, reg_info[lldb_r8_x86_64]);
+ writeRegister(&context->r9, result_base, reg_info[lldb_r9_x86_64]);
+ writeRegister(&context->r10, result_base, reg_info[lldb_r10_x86_64]);
+ writeRegister(&context->r11, result_base, reg_info[lldb_r11_x86_64]);
+ writeRegister(&context->r12, result_base, reg_info[lldb_r12_x86_64]);
+ writeRegister(&context->r13, result_base, reg_info[lldb_r13_x86_64]);
+ writeRegister(&context->r14, result_base, reg_info[lldb_r14_x86_64]);
+ writeRegister(&context->r15, result_base, reg_info[lldb_r15_x86_64]);
+ }
+
+ // TODO parse the floating point registers
+
+ return result_context_buf;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h
new file mode 100644
index 000000000000..34ddd477a9d1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h
@@ -0,0 +1,180 @@
+//===-- RegisterContextMinidump_x86_64.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMinidump_h_
+#define liblldb_RegisterContextMinidump_h_
+
+#include "MinidumpTypes.h"
+
+#include "Plugins/Process/Utility/RegisterInfoInterface.h"
+#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
+
+#include "lldb/Target/RegisterContext.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/Support/Endian.h"
+
+// C includes
+// C++ includes
+
+namespace lldb_private {
+
+namespace minidump {
+
+// This function receives an ArrayRef pointing to the bytes of the Minidump
+// register context and returns a DataBuffer that's ordered by the offsets
+// specified in the RegisterInfoInterface argument
+// This way we can reuse the already existing register contexts
+lldb::DataBufferSP
+ConvertMinidumpContext_x86_64(llvm::ArrayRef<uint8_t> source_data,
+ RegisterInfoInterface *target_reg_interface);
+
+struct Uint128 {
+ llvm::support::ulittle64_t high;
+ llvm::support::ulittle64_t low;
+};
+
+// Reference: see breakpad/crashpad source or WinNT.h
+struct MinidumpXMMSaveArea32AMD64 {
+ llvm::support::ulittle16_t control_word;
+ llvm::support::ulittle16_t status_word;
+ uint8_t tag_word;
+ uint8_t reserved1;
+ llvm::support::ulittle16_t error_opcode;
+ llvm::support::ulittle32_t error_offset;
+ llvm::support::ulittle16_t error_selector;
+ llvm::support::ulittle16_t reserved2;
+ llvm::support::ulittle32_t data_offset;
+ llvm::support::ulittle16_t data_selector;
+ llvm::support::ulittle16_t reserved3;
+ llvm::support::ulittle32_t mx_csr;
+ llvm::support::ulittle32_t mx_csr_mask;
+ Uint128 float_registers[8];
+ Uint128 xmm_registers[16];
+ uint8_t reserved4[96];
+};
+
+struct MinidumpContext_x86_64 {
+ // Register parameter home addresses.
+ llvm::support::ulittle64_t p1_home;
+ llvm::support::ulittle64_t p2_home;
+ llvm::support::ulittle64_t p3_home;
+ llvm::support::ulittle64_t p4_home;
+ llvm::support::ulittle64_t p5_home;
+ llvm::support::ulittle64_t p6_home;
+
+ // The context_flags field determines which parts
+ // of the structure are populated (have valid values)
+ llvm::support::ulittle32_t context_flags;
+ llvm::support::ulittle32_t mx_csr;
+
+ // The next register is included with
+ // MinidumpContext_x86_64_Flags::Control
+ llvm::support::ulittle16_t cs;
+
+ // The next 4 registers are included with
+ // MinidumpContext_x86_64_Flags::Segments
+ llvm::support::ulittle16_t ds;
+ llvm::support::ulittle16_t es;
+ llvm::support::ulittle16_t fs;
+ llvm::support::ulittle16_t gs;
+
+ // The next 2 registers are included with
+ // MinidumpContext_x86_64_Flags::Control
+ llvm::support::ulittle16_t ss;
+ llvm::support::ulittle32_t eflags;
+
+ // The next 6 registers are included with
+ // MinidumpContext_x86_64_Flags::DebugRegisters
+ llvm::support::ulittle64_t dr0;
+ llvm::support::ulittle64_t dr1;
+ llvm::support::ulittle64_t dr2;
+ llvm::support::ulittle64_t dr3;
+ llvm::support::ulittle64_t dr6;
+ llvm::support::ulittle64_t dr7;
+
+ // The next 4 registers are included with
+ // MinidumpContext_x86_64_Flags::Integer
+ llvm::support::ulittle64_t rax;
+ llvm::support::ulittle64_t rcx;
+ llvm::support::ulittle64_t rdx;
+ llvm::support::ulittle64_t rbx;
+
+ // The next register is included with
+ // MinidumpContext_x86_64_Flags::Control
+ llvm::support::ulittle64_t rsp;
+
+ // The next 11 registers are included with
+ // MinidumpContext_x86_64_Flags::Integer
+ llvm::support::ulittle64_t rbp;
+ llvm::support::ulittle64_t rsi;
+ llvm::support::ulittle64_t rdi;
+ llvm::support::ulittle64_t r8;
+ llvm::support::ulittle64_t r9;
+ llvm::support::ulittle64_t r10;
+ llvm::support::ulittle64_t r11;
+ llvm::support::ulittle64_t r12;
+ llvm::support::ulittle64_t r13;
+ llvm::support::ulittle64_t r14;
+ llvm::support::ulittle64_t r15;
+
+ // The next register is included with
+ // MinidumpContext_x86_64_Flags::Control
+ llvm::support::ulittle64_t rip;
+
+ // The next set of registers are included with
+ // MinidumpContext_x86_64_Flags:FloatingPoint
+ union FPR {
+ MinidumpXMMSaveArea32AMD64 flt_save;
+ struct {
+ Uint128 header[2];
+ Uint128 legacy[8];
+ Uint128 xmm[16];
+ } sse_registers;
+ };
+
+ enum {
+ VRCount = 26,
+ };
+
+ Uint128 vector_register[VRCount];
+ llvm::support::ulittle64_t vector_control;
+
+ // The next 5 registers are included with
+ // MinidumpContext_x86_64_Flags::DebugRegisters
+ llvm::support::ulittle64_t debug_control;
+ llvm::support::ulittle64_t last_branch_to_rip;
+ llvm::support::ulittle64_t last_branch_from_rip;
+ llvm::support::ulittle64_t last_exception_to_rip;
+ llvm::support::ulittle64_t last_exception_from_rip;
+};
+
+// For context_flags. These values indicate the type of
+// context stored in the structure. The high 24 bits identify the CPU, the
+// low 8 bits identify the type of context saved.
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+enum class MinidumpContext_x86_64_Flags : uint32_t {
+ x86_64_Flag = 0x00100000,
+ Control = x86_64_Flag | 0x00000001,
+ Integer = x86_64_Flag | 0x00000002,
+ Segments = x86_64_Flag | 0x00000004,
+ FloatingPoint = x86_64_Flag | 0x00000008,
+ DebugRegisters = x86_64_Flag | 0x00000010,
+ XState = x86_64_Flag | 0x00000040,
+
+ Full = Control | Integer | FloatingPoint,
+ All = Full | Segments | DebugRegisters,
+
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ All)
+};
+
+} // end namespace minidump
+} // end namespace lldb_private
+#endif // liblldb_RegisterContextMinidump_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp
new file mode 100644
index 000000000000..5262de5a94c4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp
@@ -0,0 +1,118 @@
+//===-- ThreadMinidump.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ThreadMinidump.h"
+
+#include "ProcessMinidump.h"
+
+#include "RegisterContextMinidump_ARM.h"
+#include "RegisterContextMinidump_ARM64.h"
+#include "RegisterContextMinidump_x86_32.h"
+#include "RegisterContextMinidump_x86_64.h"
+
+#include "Plugins/Process/Utility/RegisterContextLinux_i386.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h"
+#include "Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace minidump;
+
+ThreadMinidump::ThreadMinidump(Process &process, const minidump::Thread &td,
+ llvm::ArrayRef<uint8_t> gpregset_data)
+ : Thread(process, td.ThreadId), m_thread_reg_ctx_sp(),
+ m_gpregset_data(gpregset_data) {}
+
+ThreadMinidump::~ThreadMinidump() {}
+
+void ThreadMinidump::RefreshStateAfterStop() {}
+
+RegisterContextSP ThreadMinidump::GetRegisterContext() {
+ if (!m_reg_context_sp) {
+ m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
+ }
+ return m_reg_context_sp;
+}
+
+RegisterContextSP
+ThreadMinidump::CreateRegisterContextForFrame(StackFrame *frame) {
+ RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+ if (concrete_frame_idx == 0) {
+ if (m_thread_reg_ctx_sp)
+ return m_thread_reg_ctx_sp;
+
+ ProcessMinidump *process =
+ static_cast<ProcessMinidump *>(GetProcess().get());
+ ArchSpec arch = process->GetArchitecture();
+ RegisterInfoInterface *reg_interface = nullptr;
+
+ // TODO write other register contexts and add them here
+ switch (arch.GetMachine()) {
+ case llvm::Triple::x86: {
+ reg_interface = new RegisterContextLinux_i386(arch);
+ lldb::DataBufferSP buf =
+ ConvertMinidumpContext_x86_32(m_gpregset_data, reg_interface);
+ DataExtractor gpregset(buf, lldb::eByteOrderLittle, 4);
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>(
+ *this, reg_interface, gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote>());
+ break;
+ }
+ case llvm::Triple::x86_64: {
+ reg_interface = new RegisterContextLinux_x86_64(arch);
+ lldb::DataBufferSP buf =
+ ConvertMinidumpContext_x86_64(m_gpregset_data, reg_interface);
+ DataExtractor gpregset(buf, lldb::eByteOrderLittle, 8);
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>(
+ *this, reg_interface, gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote>());
+ break;
+ }
+ case llvm::Triple::aarch64: {
+ DataExtractor data(m_gpregset_data.data(), m_gpregset_data.size(),
+ lldb::eByteOrderLittle, 8);
+ m_thread_reg_ctx_sp =
+ std::make_shared<RegisterContextMinidump_ARM64>(*this, data);
+ break;
+ }
+ case llvm::Triple::arm: {
+ DataExtractor data(m_gpregset_data.data(), m_gpregset_data.size(),
+ lldb::eByteOrderLittle, 8);
+ const bool apple = arch.GetTriple().getVendor() == llvm::Triple::Apple;
+ m_thread_reg_ctx_sp =
+ std::make_shared<RegisterContextMinidump_ARM>(*this, data, apple);
+ break;
+ }
+ default:
+ break;
+ }
+
+ reg_ctx_sp = m_thread_reg_ctx_sp;
+ } else if (m_unwinder_up) {
+ reg_ctx_sp = m_unwinder_up->CreateRegisterContextForFrame(frame);
+ }
+
+ return reg_ctx_sp;
+}
+
+bool ThreadMinidump::CalculateStopInfo() { return false; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h
new file mode 100644
index 000000000000..44c41bc9f50e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h
@@ -0,0 +1,45 @@
+//===-- ThreadMinidump.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadMinidump_h_
+#define liblldb_ThreadMinidump_h_
+
+#include "MinidumpTypes.h"
+
+#include "lldb/Target/Thread.h"
+
+
+namespace lldb_private {
+
+namespace minidump {
+
+class ThreadMinidump : public Thread {
+public:
+ ThreadMinidump(Process &process, const minidump::Thread &td,
+ llvm::ArrayRef<uint8_t> gpregset_data);
+
+ ~ThreadMinidump() override;
+
+ void RefreshStateAfterStop() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(StackFrame *frame) override;
+
+protected:
+ lldb::RegisterContextSP m_thread_reg_ctx_sp;
+ llvm::ArrayRef<uint8_t> m_gpregset_data;
+
+ bool CalculateStopInfo() override;
+};
+
+} // namespace minidump
+} // namespace lldb_private
+
+#endif // liblldb_ThreadMinidump_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp
new file mode 100644
index 000000000000..7b9598379553
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp
@@ -0,0 +1,71 @@
+//===-- ScriptInterpreterNone.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ScriptInterpreterNone.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+
+#include "llvm/Support/Threading.h"
+
+#include <mutex>
+
+using namespace lldb;
+using namespace lldb_private;
+
+ScriptInterpreterNone::ScriptInterpreterNone(Debugger &debugger)
+ : ScriptInterpreter(debugger, eScriptLanguageNone) {}
+
+ScriptInterpreterNone::~ScriptInterpreterNone() {}
+
+bool ScriptInterpreterNone::ExecuteOneLine(llvm::StringRef command,
+ CommandReturnObject *,
+ const ExecuteScriptOptions &) {
+ m_debugger.GetErrorFile()->PutCString(
+ "error: there is no embedded script interpreter in this mode.\n");
+ return false;
+}
+
+void ScriptInterpreterNone::ExecuteInterpreterLoop() {
+ m_debugger.GetErrorFile()->PutCString(
+ "error: there is no embedded script interpreter in this mode.\n");
+}
+
+void ScriptInterpreterNone::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(),
+ lldb::eScriptLanguageNone, CreateInstance);
+ });
+}
+
+void ScriptInterpreterNone::Terminate() {}
+
+lldb::ScriptInterpreterSP
+ScriptInterpreterNone::CreateInstance(Debugger &debugger) {
+ return std::make_shared<ScriptInterpreterNone>(debugger);
+}
+
+lldb_private::ConstString ScriptInterpreterNone::GetPluginNameStatic() {
+ static ConstString g_name("script-none");
+ return g_name;
+}
+
+const char *ScriptInterpreterNone::GetPluginDescriptionStatic() {
+ return "Null script interpreter";
+}
+
+lldb_private::ConstString ScriptInterpreterNone::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ScriptInterpreterNone::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h
new file mode 100644
index 000000000000..242065cc23e8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h
@@ -0,0 +1,47 @@
+//===-- ScriptInterpreterNone.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ScriptInterpreterNone_h_
+#define liblldb_ScriptInterpreterNone_h_
+
+#include "lldb/Interpreter/ScriptInterpreter.h"
+
+namespace lldb_private {
+
+class ScriptInterpreterNone : public ScriptInterpreter {
+public:
+ ScriptInterpreterNone(Debugger &debugger);
+
+ ~ScriptInterpreterNone() override;
+
+ bool ExecuteOneLine(
+ llvm::StringRef command, CommandReturnObject *result,
+ const ExecuteScriptOptions &options = ExecuteScriptOptions()) override;
+
+ void ExecuteInterpreterLoop() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ScriptInterpreterSP CreateInstance(Debugger &debugger);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ScriptInterpreterNone_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
new file mode 100644
index 000000000000..29dd037efd86
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
@@ -0,0 +1,1052 @@
+//===-- PythonDataObjects.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef LLDB_DISABLE_PYTHON
+
+// Python is disabled in this build
+
+#else
+
+#include "PythonDataObjects.h"
+#include "ScriptInterpreterPython.h"
+
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Errno.h"
+
+#include <stdio.h>
+
+using namespace lldb_private;
+using namespace lldb;
+
+void StructuredPythonObject::Dump(Stream &s, bool pretty_print) const {
+ s << "Python Obj: 0x" << GetValue();
+}
+
+// PythonObject
+
+void PythonObject::Dump(Stream &strm) const {
+ if (m_py_obj) {
+ FILE *file = llvm::sys::RetryAfterSignal(nullptr, ::tmpfile);
+ if (file) {
+ ::PyObject_Print(m_py_obj, file, 0);
+ const long length = ftell(file);
+ if (length) {
+ ::rewind(file);
+ std::vector<char> file_contents(length, '\0');
+ const size_t length_read =
+ ::fread(file_contents.data(), 1, file_contents.size(), file);
+ if (length_read > 0)
+ strm.Write(file_contents.data(), length_read);
+ }
+ ::fclose(file);
+ }
+ } else
+ strm.PutCString("NULL");
+}
+
+PyObjectType PythonObject::GetObjectType() const {
+ if (!IsAllocated())
+ return PyObjectType::None;
+
+ if (PythonModule::Check(m_py_obj))
+ return PyObjectType::Module;
+ if (PythonList::Check(m_py_obj))
+ return PyObjectType::List;
+ if (PythonTuple::Check(m_py_obj))
+ return PyObjectType::Tuple;
+ if (PythonDictionary::Check(m_py_obj))
+ return PyObjectType::Dictionary;
+ if (PythonString::Check(m_py_obj))
+ return PyObjectType::String;
+#if PY_MAJOR_VERSION >= 3
+ if (PythonBytes::Check(m_py_obj))
+ return PyObjectType::Bytes;
+#endif
+ if (PythonByteArray::Check(m_py_obj))
+ return PyObjectType::ByteArray;
+ if (PythonBoolean::Check(m_py_obj))
+ return PyObjectType::Boolean;
+ if (PythonInteger::Check(m_py_obj))
+ return PyObjectType::Integer;
+ if (PythonFile::Check(m_py_obj))
+ return PyObjectType::File;
+ if (PythonCallable::Check(m_py_obj))
+ return PyObjectType::Callable;
+ return PyObjectType::Unknown;
+}
+
+PythonString PythonObject::Repr() const {
+ if (!m_py_obj)
+ return PythonString();
+ PyObject *repr = PyObject_Repr(m_py_obj);
+ if (!repr)
+ return PythonString();
+ return PythonString(PyRefType::Owned, repr);
+}
+
+PythonString PythonObject::Str() const {
+ if (!m_py_obj)
+ return PythonString();
+ PyObject *str = PyObject_Str(m_py_obj);
+ if (!str)
+ return PythonString();
+ return PythonString(PyRefType::Owned, str);
+}
+
+PythonObject
+PythonObject::ResolveNameWithDictionary(llvm::StringRef name,
+ const PythonDictionary &dict) {
+ size_t dot_pos = name.find('.');
+ llvm::StringRef piece = name.substr(0, dot_pos);
+ PythonObject result = dict.GetItemForKey(PythonString(piece));
+ if (dot_pos == llvm::StringRef::npos) {
+ // There was no dot, we're done.
+ return result;
+ }
+
+ // There was a dot. The remaining portion of the name should be looked up in
+ // the context of the object that was found in the dictionary.
+ return result.ResolveName(name.substr(dot_pos + 1));
+}
+
+PythonObject PythonObject::ResolveName(llvm::StringRef name) const {
+ // Resolve the name in the context of the specified object. If, for example,
+ // `this` refers to a PyModule, then this will look for `name` in this
+ // module. If `this` refers to a PyType, then it will resolve `name` as an
+ // attribute of that type. If `this` refers to an instance of an object,
+ // then it will resolve `name` as the value of the specified field.
+ //
+ // This function handles dotted names so that, for example, if `m_py_obj`
+ // refers to the `sys` module, and `name` == "path.append", then it will find
+ // the function `sys.path.append`.
+
+ size_t dot_pos = name.find('.');
+ if (dot_pos == llvm::StringRef::npos) {
+ // No dots in the name, we should be able to find the value immediately as
+ // an attribute of `m_py_obj`.
+ return GetAttributeValue(name);
+ }
+
+ // Look up the first piece of the name, and resolve the rest as a child of
+ // that.
+ PythonObject parent = ResolveName(name.substr(0, dot_pos));
+ if (!parent.IsAllocated())
+ return PythonObject();
+
+ // Tail recursion.. should be optimized by the compiler
+ return parent.ResolveName(name.substr(dot_pos + 1));
+}
+
+bool PythonObject::HasAttribute(llvm::StringRef attr) const {
+ if (!IsValid())
+ return false;
+ PythonString py_attr(attr);
+ return !!PyObject_HasAttr(m_py_obj, py_attr.get());
+}
+
+PythonObject PythonObject::GetAttributeValue(llvm::StringRef attr) const {
+ if (!IsValid())
+ return PythonObject();
+
+ PythonString py_attr(attr);
+ if (!PyObject_HasAttr(m_py_obj, py_attr.get()))
+ return PythonObject();
+
+ return PythonObject(PyRefType::Owned,
+ PyObject_GetAttr(m_py_obj, py_attr.get()));
+}
+
+bool PythonObject::IsNone() const { return m_py_obj == Py_None; }
+
+bool PythonObject::IsValid() const { return m_py_obj != nullptr; }
+
+bool PythonObject::IsAllocated() const { return IsValid() && !IsNone(); }
+
+StructuredData::ObjectSP PythonObject::CreateStructuredObject() const {
+ switch (GetObjectType()) {
+ case PyObjectType::Dictionary:
+ return PythonDictionary(PyRefType::Borrowed, m_py_obj)
+ .CreateStructuredDictionary();
+ case PyObjectType::Boolean:
+ return PythonBoolean(PyRefType::Borrowed, m_py_obj)
+ .CreateStructuredBoolean();
+ case PyObjectType::Integer:
+ return PythonInteger(PyRefType::Borrowed, m_py_obj)
+ .CreateStructuredInteger();
+ case PyObjectType::List:
+ return PythonList(PyRefType::Borrowed, m_py_obj).CreateStructuredArray();
+ case PyObjectType::String:
+ return PythonString(PyRefType::Borrowed, m_py_obj).CreateStructuredString();
+ case PyObjectType::Bytes:
+ return PythonBytes(PyRefType::Borrowed, m_py_obj).CreateStructuredString();
+ case PyObjectType::ByteArray:
+ return PythonByteArray(PyRefType::Borrowed, m_py_obj)
+ .CreateStructuredString();
+ case PyObjectType::None:
+ return StructuredData::ObjectSP();
+ default:
+ return StructuredData::ObjectSP(new StructuredPythonObject(m_py_obj));
+ }
+}
+
+// PythonString
+PythonBytes::PythonBytes() : PythonObject() {}
+
+PythonBytes::PythonBytes(llvm::ArrayRef<uint8_t> bytes) : PythonObject() {
+ SetBytes(bytes);
+}
+
+PythonBytes::PythonBytes(const uint8_t *bytes, size_t length) : PythonObject() {
+ SetBytes(llvm::ArrayRef<uint8_t>(bytes, length));
+}
+
+PythonBytes::PythonBytes(PyRefType type, PyObject *py_obj) : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a string
+}
+
+PythonBytes::~PythonBytes() {}
+
+bool PythonBytes::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+ return PyBytes_Check(py_obj);
+}
+
+void PythonBytes::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonBytes::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+llvm::ArrayRef<uint8_t> PythonBytes::GetBytes() const {
+ if (!IsValid())
+ return llvm::ArrayRef<uint8_t>();
+
+ Py_ssize_t size;
+ char *c;
+
+ PyBytes_AsStringAndSize(m_py_obj, &c, &size);
+ return llvm::ArrayRef<uint8_t>(reinterpret_cast<uint8_t *>(c), size);
+}
+
+size_t PythonBytes::GetSize() const {
+ if (!IsValid())
+ return 0;
+ return PyBytes_Size(m_py_obj);
+}
+
+void PythonBytes::SetBytes(llvm::ArrayRef<uint8_t> bytes) {
+ const char *data = reinterpret_cast<const char *>(bytes.data());
+ PyObject *py_bytes = PyBytes_FromStringAndSize(data, bytes.size());
+ PythonObject::Reset(PyRefType::Owned, py_bytes);
+}
+
+StructuredData::StringSP PythonBytes::CreateStructuredString() const {
+ StructuredData::StringSP result(new StructuredData::String);
+ Py_ssize_t size;
+ char *c;
+ PyBytes_AsStringAndSize(m_py_obj, &c, &size);
+ result->SetValue(std::string(c, size));
+ return result;
+}
+
+PythonByteArray::PythonByteArray(llvm::ArrayRef<uint8_t> bytes)
+ : PythonByteArray(bytes.data(), bytes.size()) {}
+
+PythonByteArray::PythonByteArray(const uint8_t *bytes, size_t length) {
+ const char *str = reinterpret_cast<const char *>(bytes);
+ Reset(PyRefType::Owned, PyByteArray_FromStringAndSize(str, length));
+}
+
+PythonByteArray::PythonByteArray(PyRefType type, PyObject *o) {
+ Reset(type, o);
+}
+
+PythonByteArray::PythonByteArray(const PythonBytes &object)
+ : PythonObject(object) {}
+
+PythonByteArray::~PythonByteArray() {}
+
+bool PythonByteArray::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+ return PyByteArray_Check(py_obj);
+}
+
+void PythonByteArray::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonByteArray::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+llvm::ArrayRef<uint8_t> PythonByteArray::GetBytes() const {
+ if (!IsValid())
+ return llvm::ArrayRef<uint8_t>();
+
+ char *c = PyByteArray_AsString(m_py_obj);
+ size_t size = GetSize();
+ return llvm::ArrayRef<uint8_t>(reinterpret_cast<uint8_t *>(c), size);
+}
+
+size_t PythonByteArray::GetSize() const {
+ if (!IsValid())
+ return 0;
+
+ return PyByteArray_Size(m_py_obj);
+}
+
+StructuredData::StringSP PythonByteArray::CreateStructuredString() const {
+ StructuredData::StringSP result(new StructuredData::String);
+ llvm::ArrayRef<uint8_t> bytes = GetBytes();
+ const char *str = reinterpret_cast<const char *>(bytes.data());
+ result->SetValue(std::string(str, bytes.size()));
+ return result;
+}
+
+// PythonString
+
+PythonString::PythonString(PyRefType type, PyObject *py_obj) : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a string
+}
+
+PythonString::PythonString(llvm::StringRef string) : PythonObject() {
+ SetString(string);
+}
+
+PythonString::PythonString(const char *string) : PythonObject() {
+ SetString(llvm::StringRef(string));
+}
+
+PythonString::PythonString() : PythonObject() {}
+
+PythonString::~PythonString() {}
+
+bool PythonString::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+
+ if (PyUnicode_Check(py_obj))
+ return true;
+#if PY_MAJOR_VERSION < 3
+ if (PyString_Check(py_obj))
+ return true;
+#endif
+ return false;
+}
+
+void PythonString::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonString::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+#if PY_MAJOR_VERSION < 3
+ // In Python 2, Don't store PyUnicode objects directly, because we need
+ // access to their underlying character buffers which Python 2 doesn't
+ // provide.
+ if (PyUnicode_Check(py_obj))
+ result.Reset(PyRefType::Owned, PyUnicode_AsUTF8String(result.get()));
+#endif
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+llvm::StringRef PythonString::GetString() const {
+ if (!IsValid())
+ return llvm::StringRef();
+
+ Py_ssize_t size;
+ const char *data;
+
+#if PY_MAJOR_VERSION >= 3
+ data = PyUnicode_AsUTF8AndSize(m_py_obj, &size);
+#else
+ char *c;
+ PyString_AsStringAndSize(m_py_obj, &c, &size);
+ data = c;
+#endif
+ return llvm::StringRef(data, size);
+}
+
+size_t PythonString::GetSize() const {
+ if (IsValid()) {
+#if PY_MAJOR_VERSION >= 3
+ return PyUnicode_GetSize(m_py_obj);
+#else
+ return PyString_Size(m_py_obj);
+#endif
+ }
+ return 0;
+}
+
+void PythonString::SetString(llvm::StringRef string) {
+#if PY_MAJOR_VERSION >= 3
+ PyObject *unicode = PyUnicode_FromStringAndSize(string.data(), string.size());
+ PythonObject::Reset(PyRefType::Owned, unicode);
+#else
+ PyObject *str = PyString_FromStringAndSize(string.data(), string.size());
+ PythonObject::Reset(PyRefType::Owned, str);
+#endif
+}
+
+StructuredData::StringSP PythonString::CreateStructuredString() const {
+ StructuredData::StringSP result(new StructuredData::String);
+ result->SetValue(GetString());
+ return result;
+}
+
+// PythonInteger
+
+PythonInteger::PythonInteger() : PythonObject() {}
+
+PythonInteger::PythonInteger(PyRefType type, PyObject *py_obj)
+ : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a integer type
+}
+
+PythonInteger::PythonInteger(int64_t value) : PythonObject() {
+ SetInteger(value);
+}
+
+PythonInteger::~PythonInteger() {}
+
+bool PythonInteger::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+
+#if PY_MAJOR_VERSION >= 3
+ // Python 3 does not have PyInt_Check. There is only one type of integral
+ // value, long.
+ return PyLong_Check(py_obj);
+#else
+ return PyLong_Check(py_obj) || PyInt_Check(py_obj);
+#endif
+}
+
+void PythonInteger::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonInteger::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+#if PY_MAJOR_VERSION < 3
+ // Always store this as a PyLong, which makes interoperability between Python
+ // 2.x and Python 3.x easier. This is only necessary in 2.x, since 3.x
+ // doesn't even have a PyInt.
+ if (PyInt_Check(py_obj)) {
+ // Since we converted the original object to a different type, the new
+ // object is an owned object regardless of the ownership semantics
+ // requested by the user.
+ result.Reset(PyRefType::Owned, PyLong_FromLongLong(PyInt_AsLong(py_obj)));
+ }
+#endif
+
+ assert(PyLong_Check(result.get()) &&
+ "Couldn't get a PyLong from this PyObject");
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+int64_t PythonInteger::GetInteger() const {
+ if (m_py_obj) {
+ assert(PyLong_Check(m_py_obj) &&
+ "PythonInteger::GetInteger has a PyObject that isn't a PyLong");
+
+ int overflow = 0;
+ int64_t result = PyLong_AsLongLongAndOverflow(m_py_obj, &overflow);
+ if (overflow != 0) {
+ // We got an integer that overflows, like 18446744072853913392L we can't
+ // use PyLong_AsLongLong() as it will return 0xffffffffffffffff. If we
+ // use the unsigned long long it will work as expected.
+ const uint64_t uval = PyLong_AsUnsignedLongLong(m_py_obj);
+ result = static_cast<int64_t>(uval);
+ }
+ return result;
+ }
+ return UINT64_MAX;
+}
+
+void PythonInteger::SetInteger(int64_t value) {
+ PythonObject::Reset(PyRefType::Owned, PyLong_FromLongLong(value));
+}
+
+StructuredData::IntegerSP PythonInteger::CreateStructuredInteger() const {
+ StructuredData::IntegerSP result(new StructuredData::Integer);
+ result->SetValue(GetInteger());
+ return result;
+}
+
+// PythonBoolean
+
+PythonBoolean::PythonBoolean(PyRefType type, PyObject *py_obj)
+ : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a boolean type
+}
+
+PythonBoolean::PythonBoolean(bool value) {
+ SetValue(value);
+}
+
+bool PythonBoolean::Check(PyObject *py_obj) {
+ return py_obj ? PyBool_Check(py_obj) : false;
+}
+
+void PythonBoolean::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonBoolean::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+bool PythonBoolean::GetValue() const {
+ return m_py_obj ? PyObject_IsTrue(m_py_obj) : false;
+}
+
+void PythonBoolean::SetValue(bool value) {
+ PythonObject::Reset(PyRefType::Owned, PyBool_FromLong(value));
+}
+
+StructuredData::BooleanSP PythonBoolean::CreateStructuredBoolean() const {
+ StructuredData::BooleanSP result(new StructuredData::Boolean);
+ result->SetValue(GetValue());
+ return result;
+}
+
+// PythonList
+
+PythonList::PythonList(PyInitialValue value) : PythonObject() {
+ if (value == PyInitialValue::Empty)
+ Reset(PyRefType::Owned, PyList_New(0));
+}
+
+PythonList::PythonList(int list_size) : PythonObject() {
+ Reset(PyRefType::Owned, PyList_New(list_size));
+}
+
+PythonList::PythonList(PyRefType type, PyObject *py_obj) : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a list
+}
+
+PythonList::~PythonList() {}
+
+bool PythonList::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+ return PyList_Check(py_obj);
+}
+
+void PythonList::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonList::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+uint32_t PythonList::GetSize() const {
+ if (IsValid())
+ return PyList_GET_SIZE(m_py_obj);
+ return 0;
+}
+
+PythonObject PythonList::GetItemAtIndex(uint32_t index) const {
+ if (IsValid())
+ return PythonObject(PyRefType::Borrowed, PyList_GetItem(m_py_obj, index));
+ return PythonObject();
+}
+
+void PythonList::SetItemAtIndex(uint32_t index, const PythonObject &object) {
+ if (IsAllocated() && object.IsValid()) {
+ // PyList_SetItem is documented to "steal" a reference, so we need to
+ // convert it to an owned reference by incrementing it.
+ Py_INCREF(object.get());
+ PyList_SetItem(m_py_obj, index, object.get());
+ }
+}
+
+void PythonList::AppendItem(const PythonObject &object) {
+ if (IsAllocated() && object.IsValid()) {
+ // `PyList_Append` does *not* steal a reference, so do not call `Py_INCREF`
+ // here like we do with `PyList_SetItem`.
+ PyList_Append(m_py_obj, object.get());
+ }
+}
+
+StructuredData::ArraySP PythonList::CreateStructuredArray() const {
+ StructuredData::ArraySP result(new StructuredData::Array);
+ uint32_t count = GetSize();
+ for (uint32_t i = 0; i < count; ++i) {
+ PythonObject obj = GetItemAtIndex(i);
+ result->AddItem(obj.CreateStructuredObject());
+ }
+ return result;
+}
+
+// PythonTuple
+
+PythonTuple::PythonTuple(PyInitialValue value) : PythonObject() {
+ if (value == PyInitialValue::Empty)
+ Reset(PyRefType::Owned, PyTuple_New(0));
+}
+
+PythonTuple::PythonTuple(int tuple_size) : PythonObject() {
+ Reset(PyRefType::Owned, PyTuple_New(tuple_size));
+}
+
+PythonTuple::PythonTuple(PyRefType type, PyObject *py_obj) : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a tuple
+}
+
+PythonTuple::PythonTuple(std::initializer_list<PythonObject> objects) {
+ m_py_obj = PyTuple_New(objects.size());
+
+ uint32_t idx = 0;
+ for (auto object : objects) {
+ if (object.IsValid())
+ SetItemAtIndex(idx, object);
+ idx++;
+ }
+}
+
+PythonTuple::PythonTuple(std::initializer_list<PyObject *> objects) {
+ m_py_obj = PyTuple_New(objects.size());
+
+ uint32_t idx = 0;
+ for (auto py_object : objects) {
+ PythonObject object(PyRefType::Borrowed, py_object);
+ if (object.IsValid())
+ SetItemAtIndex(idx, object);
+ idx++;
+ }
+}
+
+PythonTuple::~PythonTuple() {}
+
+bool PythonTuple::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+ return PyTuple_Check(py_obj);
+}
+
+void PythonTuple::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonTuple::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+uint32_t PythonTuple::GetSize() const {
+ if (IsValid())
+ return PyTuple_GET_SIZE(m_py_obj);
+ return 0;
+}
+
+PythonObject PythonTuple::GetItemAtIndex(uint32_t index) const {
+ if (IsValid())
+ return PythonObject(PyRefType::Borrowed, PyTuple_GetItem(m_py_obj, index));
+ return PythonObject();
+}
+
+void PythonTuple::SetItemAtIndex(uint32_t index, const PythonObject &object) {
+ if (IsAllocated() && object.IsValid()) {
+ // PyTuple_SetItem is documented to "steal" a reference, so we need to
+ // convert it to an owned reference by incrementing it.
+ Py_INCREF(object.get());
+ PyTuple_SetItem(m_py_obj, index, object.get());
+ }
+}
+
+StructuredData::ArraySP PythonTuple::CreateStructuredArray() const {
+ StructuredData::ArraySP result(new StructuredData::Array);
+ uint32_t count = GetSize();
+ for (uint32_t i = 0; i < count; ++i) {
+ PythonObject obj = GetItemAtIndex(i);
+ result->AddItem(obj.CreateStructuredObject());
+ }
+ return result;
+}
+
+// PythonDictionary
+
+PythonDictionary::PythonDictionary(PyInitialValue value) : PythonObject() {
+ if (value == PyInitialValue::Empty)
+ Reset(PyRefType::Owned, PyDict_New());
+}
+
+PythonDictionary::PythonDictionary(PyRefType type, PyObject *py_obj)
+ : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a dictionary
+}
+
+PythonDictionary::~PythonDictionary() {}
+
+bool PythonDictionary::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+
+ return PyDict_Check(py_obj);
+}
+
+void PythonDictionary::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonDictionary::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+uint32_t PythonDictionary::GetSize() const {
+ if (IsValid())
+ return PyDict_Size(m_py_obj);
+ return 0;
+}
+
+PythonList PythonDictionary::GetKeys() const {
+ if (IsValid())
+ return PythonList(PyRefType::Owned, PyDict_Keys(m_py_obj));
+ return PythonList(PyInitialValue::Invalid);
+}
+
+PythonObject PythonDictionary::GetItemForKey(const PythonObject &key) const {
+ if (IsAllocated() && key.IsValid())
+ return PythonObject(PyRefType::Borrowed,
+ PyDict_GetItem(m_py_obj, key.get()));
+ return PythonObject();
+}
+
+void PythonDictionary::SetItemForKey(const PythonObject &key,
+ const PythonObject &value) {
+ if (IsAllocated() && key.IsValid() && value.IsValid())
+ PyDict_SetItem(m_py_obj, key.get(), value.get());
+}
+
+StructuredData::DictionarySP
+PythonDictionary::CreateStructuredDictionary() const {
+ StructuredData::DictionarySP result(new StructuredData::Dictionary);
+ PythonList keys(GetKeys());
+ uint32_t num_keys = keys.GetSize();
+ for (uint32_t i = 0; i < num_keys; ++i) {
+ PythonObject key = keys.GetItemAtIndex(i);
+ PythonObject value = GetItemForKey(key);
+ StructuredData::ObjectSP structured_value = value.CreateStructuredObject();
+ result->AddItem(key.Str().GetString(), structured_value);
+ }
+ return result;
+}
+
+PythonModule::PythonModule() : PythonObject() {}
+
+PythonModule::PythonModule(PyRefType type, PyObject *py_obj) {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a module
+}
+
+PythonModule::~PythonModule() {}
+
+PythonModule PythonModule::BuiltinsModule() {
+#if PY_MAJOR_VERSION >= 3
+ return AddModule("builtins");
+#else
+ return AddModule("__builtin__");
+#endif
+}
+
+PythonModule PythonModule::MainModule() { return AddModule("__main__"); }
+
+PythonModule PythonModule::AddModule(llvm::StringRef module) {
+ std::string str = module.str();
+ return PythonModule(PyRefType::Borrowed, PyImport_AddModule(str.c_str()));
+}
+
+PythonModule PythonModule::ImportModule(llvm::StringRef module) {
+ std::string str = module.str();
+ return PythonModule(PyRefType::Owned, PyImport_ImportModule(str.c_str()));
+}
+
+bool PythonModule::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+
+ return PyModule_Check(py_obj);
+}
+
+void PythonModule::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonModule::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+PythonDictionary PythonModule::GetDictionary() const {
+ return PythonDictionary(PyRefType::Borrowed, PyModule_GetDict(m_py_obj));
+}
+
+PythonCallable::PythonCallable() : PythonObject() {}
+
+PythonCallable::PythonCallable(PyRefType type, PyObject *py_obj) {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a callable
+}
+
+PythonCallable::~PythonCallable() {}
+
+bool PythonCallable::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+
+ return PyCallable_Check(py_obj);
+}
+
+void PythonCallable::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonCallable::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+PythonCallable::ArgInfo PythonCallable::GetNumArguments() const {
+ ArgInfo result = {0, false, false, false};
+ if (!IsValid())
+ return result;
+
+ PyObject *py_func_obj = m_py_obj;
+ if (PyMethod_Check(py_func_obj)) {
+ py_func_obj = PyMethod_GET_FUNCTION(py_func_obj);
+ PythonObject im_self = GetAttributeValue("im_self");
+ if (im_self.IsValid() && !im_self.IsNone())
+ result.is_bound_method = true;
+ } else {
+ // see if this is a callable object with an __call__ method
+ if (!PyFunction_Check(py_func_obj)) {
+ PythonObject __call__ = GetAttributeValue("__call__");
+ if (__call__.IsValid()) {
+ auto __callable__ = __call__.AsType<PythonCallable>();
+ if (__callable__.IsValid()) {
+ py_func_obj = PyMethod_GET_FUNCTION(__callable__.get());
+ PythonObject im_self = GetAttributeValue("im_self");
+ if (im_self.IsValid() && !im_self.IsNone())
+ result.is_bound_method = true;
+ }
+ }
+ }
+ }
+
+ if (!py_func_obj)
+ return result;
+
+ PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(py_func_obj);
+ if (!code)
+ return result;
+
+ result.count = code->co_argcount;
+ result.has_varargs = !!(code->co_flags & CO_VARARGS);
+ result.has_kwargs = !!(code->co_flags & CO_VARKEYWORDS);
+ return result;
+}
+
+PythonObject PythonCallable::operator()() {
+ return PythonObject(PyRefType::Owned, PyObject_CallObject(m_py_obj, nullptr));
+}
+
+PythonObject PythonCallable::
+operator()(std::initializer_list<PyObject *> args) {
+ PythonTuple arg_tuple(args);
+ return PythonObject(PyRefType::Owned,
+ PyObject_CallObject(m_py_obj, arg_tuple.get()));
+}
+
+PythonObject PythonCallable::
+operator()(std::initializer_list<PythonObject> args) {
+ PythonTuple arg_tuple(args);
+ return PythonObject(PyRefType::Owned,
+ PyObject_CallObject(m_py_obj, arg_tuple.get()));
+}
+
+PythonFile::PythonFile() : PythonObject() {}
+
+PythonFile::PythonFile(File &file, const char *mode) { Reset(file, mode); }
+
+PythonFile::PythonFile(const char *path, const char *mode) {
+ lldb_private::File file;
+ FileSystem::Instance().Open(file, FileSpec(path), GetOptionsFromMode(mode));
+ Reset(file, mode);
+}
+
+PythonFile::PythonFile(PyRefType type, PyObject *o) { Reset(type, o); }
+
+PythonFile::~PythonFile() {}
+
+bool PythonFile::Check(PyObject *py_obj) {
+#if PY_MAJOR_VERSION < 3
+ return PyFile_Check(py_obj);
+#else
+ // In Python 3, there is no `PyFile_Check`, and in fact PyFile is not even a
+ // first-class object type anymore. `PyFile_FromFd` is just a thin wrapper
+ // over `io.open()`, which returns some object derived from `io.IOBase`. As a
+ // result, the only way to detect a file in Python 3 is to check whether it
+ // inherits from `io.IOBase`. Since it is possible for non-files to also
+ // inherit from `io.IOBase`, we additionally verify that it has the `fileno`
+ // attribute, which should guarantee that it is backed by the file system.
+ PythonObject io_module(PyRefType::Owned, PyImport_ImportModule("io"));
+ PythonDictionary io_dict(PyRefType::Borrowed,
+ PyModule_GetDict(io_module.get()));
+ PythonObject io_base_class = io_dict.GetItemForKey(PythonString("IOBase"));
+
+ PythonObject object_type(PyRefType::Owned, PyObject_Type(py_obj));
+
+ if (1 != PyObject_IsSubclass(object_type.get(), io_base_class.get()))
+ return false;
+ if (!object_type.HasAttribute("fileno"))
+ return false;
+
+ return true;
+#endif
+}
+
+void PythonFile::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonFile::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+void PythonFile::Reset(File &file, const char *mode) {
+ if (!file.IsValid()) {
+ Reset();
+ return;
+ }
+
+ char *cmode = const_cast<char *>(mode);
+#if PY_MAJOR_VERSION >= 3
+ Reset(PyRefType::Owned, PyFile_FromFd(file.GetDescriptor(), nullptr, cmode,
+ -1, nullptr, "ignore", nullptr, 0));
+#else
+ // Read through the Python source, doesn't seem to modify these strings
+ Reset(PyRefType::Owned,
+ PyFile_FromFile(file.GetStream(), const_cast<char *>(""), cmode,
+ nullptr));
+#endif
+}
+
+uint32_t PythonFile::GetOptionsFromMode(llvm::StringRef mode) {
+ if (mode.empty())
+ return 0;
+
+ return llvm::StringSwitch<uint32_t>(mode.str())
+ .Case("r", File::eOpenOptionRead)
+ .Case("w", File::eOpenOptionWrite)
+ .Case("a", File::eOpenOptionWrite | File::eOpenOptionAppend |
+ File::eOpenOptionCanCreate)
+ .Case("r+", File::eOpenOptionRead | File::eOpenOptionWrite)
+ .Case("w+", File::eOpenOptionRead | File::eOpenOptionWrite |
+ File::eOpenOptionCanCreate | File::eOpenOptionTruncate)
+ .Case("a+", File::eOpenOptionRead | File::eOpenOptionWrite |
+ File::eOpenOptionAppend | File::eOpenOptionCanCreate)
+ .Default(0);
+}
+
+bool PythonFile::GetUnderlyingFile(File &file) const {
+ if (!IsValid())
+ return false;
+
+ file.Close();
+ // We don't own the file descriptor returned by this function, make sure the
+ // File object knows about that.
+ file.SetDescriptor(PyObject_AsFileDescriptor(m_py_obj), false);
+ PythonString py_mode = GetAttributeValue("mode").AsType<PythonString>();
+ file.SetOptions(PythonFile::GetOptionsFromMode(py_mode.GetString()));
+ return file.IsValid();
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
new file mode 100644
index 000000000000..049ce90bb1b1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
@@ -0,0 +1,479 @@
+//===-- PythonDataObjects.h--------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H
+
+#ifndef LLDB_DISABLE_PYTHON
+
+// LLDB Python header must be included first
+#include "lldb-python.h"
+
+#include "lldb/Host/File.h"
+#include "lldb/Utility/StructuredData.h"
+
+#include "llvm/ADT/ArrayRef.h"
+
+namespace lldb_private {
+
+class PythonBytes;
+class PythonString;
+class PythonList;
+class PythonDictionary;
+class PythonInteger;
+
+class StructuredPythonObject : public StructuredData::Generic {
+public:
+ StructuredPythonObject() : StructuredData::Generic() {}
+
+ StructuredPythonObject(void *obj) : StructuredData::Generic(obj) {
+ Py_XINCREF(GetValue());
+ }
+
+ ~StructuredPythonObject() override {
+ if (Py_IsInitialized())
+ Py_XDECREF(GetValue());
+ SetValue(nullptr);
+ }
+
+ bool IsValid() const override { return GetValue() && GetValue() != Py_None; }
+
+ void Dump(Stream &s, bool pretty_print = true) const override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(StructuredPythonObject);
+};
+
+enum class PyObjectType {
+ Unknown,
+ None,
+ Boolean,
+ Integer,
+ Dictionary,
+ List,
+ String,
+ Bytes,
+ ByteArray,
+ Module,
+ Callable,
+ Tuple,
+ File
+};
+
+enum class PyRefType {
+ Borrowed, // We are not given ownership of the incoming PyObject.
+ // We cannot safely hold it without calling Py_INCREF.
+ Owned // We have ownership of the incoming PyObject. We should
+ // not call Py_INCREF.
+};
+
+enum class PyInitialValue { Invalid, Empty };
+
+class PythonObject {
+public:
+ PythonObject() : m_py_obj(nullptr) {}
+
+ PythonObject(PyRefType type, PyObject *py_obj) : m_py_obj(nullptr) {
+ Reset(type, py_obj);
+ }
+
+ PythonObject(const PythonObject &rhs) : m_py_obj(nullptr) { Reset(rhs); }
+
+ virtual ~PythonObject() { Reset(); }
+
+ void Reset() {
+ // Avoid calling the virtual method since it's not necessary
+ // to actually validate the type of the PyObject if we're
+ // just setting to null.
+ if (Py_IsInitialized())
+ Py_XDECREF(m_py_obj);
+ m_py_obj = nullptr;
+ }
+
+ void Reset(const PythonObject &rhs) {
+ // Avoid calling the virtual method if it's not necessary
+ // to actually validate the type of the PyObject.
+ if (!rhs.IsValid())
+ Reset();
+ else
+ Reset(PyRefType::Borrowed, rhs.m_py_obj);
+ }
+
+ // PythonObject is implicitly convertible to PyObject *, which will call the
+ // wrong overload. We want to explicitly disallow this, since a PyObject
+ // *always* owns its reference. Therefore the overload which takes a
+ // PyRefType doesn't make sense, and the copy constructor should be used.
+ void Reset(PyRefType type, const PythonObject &ref) = delete;
+
+ virtual void Reset(PyRefType type, PyObject *py_obj) {
+ if (py_obj == m_py_obj)
+ return;
+
+ if (Py_IsInitialized())
+ Py_XDECREF(m_py_obj);
+
+ m_py_obj = py_obj;
+
+ // If this is a borrowed reference, we need to convert it to
+ // an owned reference by incrementing it. If it is an owned
+ // reference (for example the caller allocated it with PyDict_New()
+ // then we must *not* increment it.
+ if (Py_IsInitialized() && type == PyRefType::Borrowed)
+ Py_XINCREF(m_py_obj);
+ }
+
+ void Dump() const {
+ if (m_py_obj)
+ _PyObject_Dump(m_py_obj);
+ else
+ puts("NULL");
+ }
+
+ void Dump(Stream &strm) const;
+
+ PyObject *get() const { return m_py_obj; }
+
+ PyObject *release() {
+ PyObject *result = m_py_obj;
+ m_py_obj = nullptr;
+ return result;
+ }
+
+ PythonObject &operator=(const PythonObject &other) {
+ Reset(PyRefType::Borrowed, other.get());
+ return *this;
+ }
+
+ PyObjectType GetObjectType() const;
+
+ PythonString Repr() const;
+
+ PythonString Str() const;
+
+ static PythonObject ResolveNameWithDictionary(llvm::StringRef name,
+ const PythonDictionary &dict);
+
+ template <typename T>
+ static T ResolveNameWithDictionary(llvm::StringRef name,
+ const PythonDictionary &dict) {
+ return ResolveNameWithDictionary(name, dict).AsType<T>();
+ }
+
+ PythonObject ResolveName(llvm::StringRef name) const;
+
+ template <typename T> T ResolveName(llvm::StringRef name) const {
+ return ResolveName(name).AsType<T>();
+ }
+
+ bool HasAttribute(llvm::StringRef attribute) const;
+
+ PythonObject GetAttributeValue(llvm::StringRef attribute) const;
+
+ bool IsValid() const;
+
+ bool IsAllocated() const;
+
+ bool IsNone() const;
+
+ template <typename T> T AsType() const {
+ if (!T::Check(m_py_obj))
+ return T();
+ return T(PyRefType::Borrowed, m_py_obj);
+ }
+
+ StructuredData::ObjectSP CreateStructuredObject() const;
+
+protected:
+ PyObject *m_py_obj;
+};
+
+class PythonBytes : public PythonObject {
+public:
+ PythonBytes();
+ explicit PythonBytes(llvm::ArrayRef<uint8_t> bytes);
+ PythonBytes(const uint8_t *bytes, size_t length);
+ PythonBytes(PyRefType type, PyObject *o);
+
+ ~PythonBytes() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ llvm::ArrayRef<uint8_t> GetBytes() const;
+
+ size_t GetSize() const;
+
+ void SetBytes(llvm::ArrayRef<uint8_t> stringbytes);
+
+ StructuredData::StringSP CreateStructuredString() const;
+};
+
+class PythonByteArray : public PythonObject {
+public:
+ PythonByteArray();
+ explicit PythonByteArray(llvm::ArrayRef<uint8_t> bytes);
+ PythonByteArray(const uint8_t *bytes, size_t length);
+ PythonByteArray(PyRefType type, PyObject *o);
+ PythonByteArray(const PythonBytes &object);
+
+ ~PythonByteArray() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ llvm::ArrayRef<uint8_t> GetBytes() const;
+
+ size_t GetSize() const;
+
+ void SetBytes(llvm::ArrayRef<uint8_t> stringbytes);
+
+ StructuredData::StringSP CreateStructuredString() const;
+};
+
+class PythonString : public PythonObject {
+public:
+ PythonString();
+ explicit PythonString(llvm::StringRef string);
+ explicit PythonString(const char *string);
+ PythonString(PyRefType type, PyObject *o);
+
+ ~PythonString() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ llvm::StringRef GetString() const;
+
+ size_t GetSize() const;
+
+ void SetString(llvm::StringRef string);
+
+ StructuredData::StringSP CreateStructuredString() const;
+};
+
+class PythonInteger : public PythonObject {
+public:
+ PythonInteger();
+ explicit PythonInteger(int64_t value);
+ PythonInteger(PyRefType type, PyObject *o);
+
+ ~PythonInteger() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ int64_t GetInteger() const;
+
+ void SetInteger(int64_t value);
+
+ StructuredData::IntegerSP CreateStructuredInteger() const;
+};
+
+class PythonBoolean : public PythonObject {
+public:
+ PythonBoolean() = default;
+ explicit PythonBoolean(bool value);
+ PythonBoolean(PyRefType type, PyObject *o);
+
+ ~PythonBoolean() override = default;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ bool GetValue() const;
+
+ void SetValue(bool value);
+
+ StructuredData::BooleanSP CreateStructuredBoolean() const;
+};
+
+class PythonList : public PythonObject {
+public:
+ PythonList() {}
+ explicit PythonList(PyInitialValue value);
+ explicit PythonList(int list_size);
+ PythonList(PyRefType type, PyObject *o);
+
+ ~PythonList() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ uint32_t GetSize() const;
+
+ PythonObject GetItemAtIndex(uint32_t index) const;
+
+ void SetItemAtIndex(uint32_t index, const PythonObject &object);
+
+ void AppendItem(const PythonObject &object);
+
+ StructuredData::ArraySP CreateStructuredArray() const;
+};
+
+class PythonTuple : public PythonObject {
+public:
+ PythonTuple() {}
+ explicit PythonTuple(PyInitialValue value);
+ explicit PythonTuple(int tuple_size);
+ PythonTuple(PyRefType type, PyObject *o);
+ PythonTuple(std::initializer_list<PythonObject> objects);
+ PythonTuple(std::initializer_list<PyObject *> objects);
+
+ ~PythonTuple() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ uint32_t GetSize() const;
+
+ PythonObject GetItemAtIndex(uint32_t index) const;
+
+ void SetItemAtIndex(uint32_t index, const PythonObject &object);
+
+ StructuredData::ArraySP CreateStructuredArray() const;
+};
+
+class PythonDictionary : public PythonObject {
+public:
+ PythonDictionary() {}
+ explicit PythonDictionary(PyInitialValue value);
+ PythonDictionary(PyRefType type, PyObject *o);
+
+ ~PythonDictionary() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ uint32_t GetSize() const;
+
+ PythonList GetKeys() const;
+
+ PythonObject GetItemForKey(const PythonObject &key) const;
+ void SetItemForKey(const PythonObject &key, const PythonObject &value);
+
+ StructuredData::DictionarySP CreateStructuredDictionary() const;
+};
+
+class PythonModule : public PythonObject {
+public:
+ PythonModule();
+ PythonModule(PyRefType type, PyObject *o);
+
+ ~PythonModule() override;
+
+ static bool Check(PyObject *py_obj);
+
+ static PythonModule BuiltinsModule();
+
+ static PythonModule MainModule();
+
+ static PythonModule AddModule(llvm::StringRef module);
+
+ static PythonModule ImportModule(llvm::StringRef module);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ PythonDictionary GetDictionary() const;
+};
+
+class PythonCallable : public PythonObject {
+public:
+ struct ArgInfo {
+ size_t count;
+ bool is_bound_method : 1;
+ bool has_varargs : 1;
+ bool has_kwargs : 1;
+ };
+
+ PythonCallable();
+ PythonCallable(PyRefType type, PyObject *o);
+
+ ~PythonCallable() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ ArgInfo GetNumArguments() const;
+
+ PythonObject operator()();
+
+ PythonObject operator()(std::initializer_list<PyObject *> args);
+
+ PythonObject operator()(std::initializer_list<PythonObject> args);
+
+ template <typename Arg, typename... Args>
+ PythonObject operator()(const Arg &arg, Args... args) {
+ return operator()({arg, args...});
+ }
+};
+
+class PythonFile : public PythonObject {
+public:
+ PythonFile();
+ PythonFile(File &file, const char *mode);
+ PythonFile(const char *path, const char *mode);
+ PythonFile(PyRefType type, PyObject *o);
+
+ ~PythonFile() override;
+
+ static bool Check(PyObject *py_obj);
+
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+ void Reset(File &file, const char *mode);
+
+ static uint32_t GetOptionsFromMode(llvm::StringRef mode);
+
+ bool GetUnderlyingFile(File &file) const;
+};
+
+} // namespace lldb_private
+
+#endif
+
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp
new file mode 100644
index 000000000000..c9d834ce6868
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp
@@ -0,0 +1,169 @@
+//===-- PythonExceptionState.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_DISABLE_PYTHON
+
+// LLDB Python header must be included first
+#include "lldb-python.h"
+
+#include "PythonExceptionState.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace lldb_private;
+
+PythonExceptionState::PythonExceptionState(bool restore_on_exit)
+ : m_restore_on_exit(restore_on_exit) {
+ Acquire(restore_on_exit);
+}
+
+PythonExceptionState::~PythonExceptionState() {
+ if (m_restore_on_exit)
+ Restore();
+}
+
+void PythonExceptionState::Acquire(bool restore_on_exit) {
+ // If a state is already acquired, the user needs to decide whether they want
+ // to discard or restore it. Don't allow the potential silent loss of a
+ // valid state.
+ assert(!IsError());
+
+ if (!HasErrorOccurred())
+ return;
+
+ PyObject *py_type = nullptr;
+ PyObject *py_value = nullptr;
+ PyObject *py_traceback = nullptr;
+ PyErr_Fetch(&py_type, &py_value, &py_traceback);
+ // PyErr_Fetch clears the error flag.
+ assert(!HasErrorOccurred());
+
+ // Ownership of the objects returned by `PyErr_Fetch` is transferred to us.
+ m_type.Reset(PyRefType::Owned, py_type);
+ m_value.Reset(PyRefType::Owned, py_value);
+ m_traceback.Reset(PyRefType::Owned, py_traceback);
+ m_restore_on_exit = restore_on_exit;
+}
+
+void PythonExceptionState::Restore() {
+ if (m_type.IsValid()) {
+ // The documentation for PyErr_Restore says "Do not pass a null type and
+ // non-null value or traceback. So only restore if type was non-null to
+ // begin with. In this case we're passing ownership back to Python so
+ // release them all.
+ PyErr_Restore(m_type.release(), m_value.release(), m_traceback.release());
+ }
+
+ // After we restore, we should not hold onto the exception state. Demand
+ // that it be re-acquired.
+ Discard();
+}
+
+void PythonExceptionState::Discard() {
+ m_type.Reset();
+ m_value.Reset();
+ m_traceback.Reset();
+}
+
+void PythonExceptionState::Reset() {
+ if (m_restore_on_exit)
+ Restore();
+ else
+ Discard();
+}
+
+bool PythonExceptionState::HasErrorOccurred() { return PyErr_Occurred(); }
+
+bool PythonExceptionState::IsError() const {
+ return m_type.IsValid() || m_value.IsValid() || m_traceback.IsValid();
+}
+
+PythonObject PythonExceptionState::GetType() const { return m_type; }
+
+PythonObject PythonExceptionState::GetValue() const { return m_value; }
+
+PythonObject PythonExceptionState::GetTraceback() const { return m_traceback; }
+
+std::string PythonExceptionState::Format() const {
+ // Don't allow this function to modify the error state.
+ PythonExceptionState state(true);
+
+ std::string backtrace = ReadBacktrace();
+ if (!IsError())
+ return std::string();
+
+ // It's possible that ReadPythonBacktrace generated another exception. If
+ // this happens we have to clear the exception, because otherwise
+ // PyObject_Str() will assert below. That's why we needed to do the save /
+ // restore at the beginning of this function.
+ PythonExceptionState bt_error_state(false);
+
+ std::string error_string;
+ llvm::raw_string_ostream error_stream(error_string);
+ error_stream << m_value.Str().GetString() << "\n";
+
+ if (!bt_error_state.IsError()) {
+ // If we were able to read the backtrace, just append it.
+ error_stream << backtrace << "\n";
+ } else {
+ // Otherwise, append some information about why we were unable to obtain
+ // the backtrace.
+ PythonString bt_error = bt_error_state.GetValue().Str();
+ error_stream << "An error occurred while retrieving the backtrace: "
+ << bt_error.GetString() << "\n";
+ }
+ return error_stream.str();
+}
+
+std::string PythonExceptionState::ReadBacktrace() const {
+ std::string retval("backtrace unavailable");
+
+ auto traceback_module = PythonModule::ImportModule("traceback");
+#if PY_MAJOR_VERSION >= 3
+ auto stringIO_module = PythonModule::ImportModule("io");
+#else
+ auto stringIO_module = PythonModule::ImportModule("StringIO");
+#endif
+ if (!m_traceback.IsAllocated())
+ return retval;
+
+ if (!traceback_module.IsAllocated() || !stringIO_module.IsAllocated())
+ return retval;
+
+ auto stringIO_builder =
+ stringIO_module.ResolveName<PythonCallable>("StringIO");
+ if (!stringIO_builder.IsAllocated())
+ return retval;
+
+ auto stringIO_buffer = stringIO_builder();
+ if (!stringIO_buffer.IsAllocated())
+ return retval;
+
+ auto printTB = traceback_module.ResolveName<PythonCallable>("print_tb");
+ if (!printTB.IsAllocated())
+ return retval;
+
+ auto printTB_result =
+ printTB(m_traceback.get(), Py_None, stringIO_buffer.get());
+ auto stringIO_getvalue =
+ stringIO_buffer.ResolveName<PythonCallable>("getvalue");
+ if (!stringIO_getvalue.IsAllocated())
+ return retval;
+
+ auto printTB_string = stringIO_getvalue().AsType<PythonString>();
+ if (!printTB_string.IsAllocated())
+ return retval;
+
+ llvm::StringRef string_data(printTB_string.GetString());
+ retval.assign(string_data.data(), string_data.size());
+
+ return retval;
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h
new file mode 100644
index 000000000000..3a88aa037776
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h
@@ -0,0 +1,56 @@
+//===-- PythonExceptionState.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONEXCEPTIONSTATE_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONEXCEPTIONSTATE_H
+
+#ifndef LLDB_DISABLE_PYTHON
+
+#include "PythonDataObjects.h"
+
+namespace lldb_private {
+
+class PythonExceptionState {
+public:
+ explicit PythonExceptionState(bool restore_on_exit);
+ ~PythonExceptionState();
+
+ void Acquire(bool restore_on_exit);
+
+ void Restore();
+
+ void Discard();
+
+ void Reset();
+
+ static bool HasErrorOccurred();
+
+ bool IsError() const;
+
+ PythonObject GetType() const;
+
+ PythonObject GetValue() const;
+
+ PythonObject GetTraceback() const;
+
+ std::string Format() const;
+
+private:
+ std::string ReadBacktrace() const;
+
+ bool m_restore_on_exit;
+
+ PythonObject m_type;
+ PythonObject m_value;
+ PythonObject m_traceback;
+};
+}
+
+#endif
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
new file mode 100644
index 000000000000..2d2b68ceaaa6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -0,0 +1,3259 @@
+//===-- ScriptInterpreterPython.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef LLDB_DISABLE_PYTHON
+
+// Python is disabled in this build
+
+#else
+
+// LLDB Python header must be included first
+#include "lldb-python.h"
+
+#include "PythonDataObjects.h"
+#include "PythonExceptionState.h"
+#include "ScriptInterpreterPythonImpl.h"
+
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBValue.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Breakpoint/WatchpointOptions.h"
+#include "lldb/Core/Communication.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Utility/Timer.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/ConnectionGenericFileWindows.h"
+#endif
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+
+#include <memory>
+#include <mutex>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Defined in the SWIG source file
+#if PY_MAJOR_VERSION >= 3
+extern "C" PyObject *PyInit__lldb(void);
+
+#define LLDBSwigPyInit PyInit__lldb
+
+#else
+extern "C" void init_lldb(void);
+
+#define LLDBSwigPyInit init_lldb
+#endif
+
+// These prototypes are the Pythonic implementations of the required callbacks.
+// Although these are scripting-language specific, their definition depends on
+// the public API.
+extern "C" bool LLDBSwigPythonBreakpointCallbackFunction(
+ const char *python_function_name, const char *session_dictionary_name,
+ const lldb::StackFrameSP &sb_frame,
+ const lldb::BreakpointLocationSP &sb_bp_loc);
+
+extern "C" bool LLDBSwigPythonWatchpointCallbackFunction(
+ const char *python_function_name, const char *session_dictionary_name,
+ const lldb::StackFrameSP &sb_frame, const lldb::WatchpointSP &sb_wp);
+
+extern "C" bool LLDBSwigPythonCallTypeScript(
+ const char *python_function_name, void *session_dictionary,
+ const lldb::ValueObjectSP &valobj_sp, void **pyfunct_wrapper,
+ const lldb::TypeSummaryOptionsSP &options_sp, std::string &retval);
+
+extern "C" void *
+LLDBSwigPythonCreateSyntheticProvider(const char *python_class_name,
+ const char *session_dictionary_name,
+ const lldb::ValueObjectSP &valobj_sp);
+
+extern "C" void *
+LLDBSwigPythonCreateCommandObject(const char *python_class_name,
+ const char *session_dictionary_name,
+ const lldb::DebuggerSP debugger_sp);
+
+extern "C" void *LLDBSwigPythonCreateScriptedThreadPlan(
+ const char *python_class_name, const char *session_dictionary_name,
+ const lldb::ThreadPlanSP &thread_plan_sp);
+
+extern "C" bool LLDBSWIGPythonCallThreadPlan(void *implementor,
+ const char *method_name,
+ Event *event_sp, bool &got_error);
+
+extern "C" void *LLDBSwigPythonCreateScriptedBreakpointResolver(
+ const char *python_class_name, const char *session_dictionary_name,
+ lldb_private::StructuredDataImpl *args, lldb::BreakpointSP &bkpt_sp);
+
+extern "C" unsigned int
+LLDBSwigPythonCallBreakpointResolver(void *implementor, const char *method_name,
+ lldb_private::SymbolContext *sym_ctx);
+
+extern "C" size_t LLDBSwigPython_CalculateNumChildren(void *implementor,
+ uint32_t max);
+
+extern "C" void *LLDBSwigPython_GetChildAtIndex(void *implementor,
+ uint32_t idx);
+
+extern "C" int LLDBSwigPython_GetIndexOfChildWithName(void *implementor,
+ const char *child_name);
+
+extern "C" void *LLDBSWIGPython_CastPyObjectToSBValue(void *data);
+
+extern lldb::ValueObjectSP
+LLDBSWIGPython_GetValueObjectSPFromSBValue(void *data);
+
+extern "C" bool LLDBSwigPython_UpdateSynthProviderInstance(void *implementor);
+
+extern "C" bool
+LLDBSwigPython_MightHaveChildrenSynthProviderInstance(void *implementor);
+
+extern "C" void *
+LLDBSwigPython_GetValueSynthProviderInstance(void *implementor);
+
+extern "C" bool
+LLDBSwigPythonCallCommand(const char *python_function_name,
+ const char *session_dictionary_name,
+ lldb::DebuggerSP &debugger, const char *args,
+ lldb_private::CommandReturnObject &cmd_retobj,
+ lldb::ExecutionContextRefSP exe_ctx_ref_sp);
+
+extern "C" bool
+LLDBSwigPythonCallCommandObject(void *implementor, lldb::DebuggerSP &debugger,
+ const char *args,
+ lldb_private::CommandReturnObject &cmd_retobj,
+ lldb::ExecutionContextRefSP exe_ctx_ref_sp);
+
+extern "C" bool
+LLDBSwigPythonCallModuleInit(const char *python_module_name,
+ const char *session_dictionary_name,
+ lldb::DebuggerSP &debugger);
+
+extern "C" void *
+LLDBSWIGPythonCreateOSPlugin(const char *python_class_name,
+ const char *session_dictionary_name,
+ const lldb::ProcessSP &process_sp);
+
+extern "C" void *
+LLDBSWIGPython_CreateFrameRecognizer(const char *python_class_name,
+ const char *session_dictionary_name);
+
+extern "C" void *
+LLDBSwigPython_GetRecognizedArguments(void *implementor,
+ const lldb::StackFrameSP &frame_sp);
+
+extern "C" bool LLDBSWIGPythonRunScriptKeywordProcess(
+ const char *python_function_name, const char *session_dictionary_name,
+ lldb::ProcessSP &process, std::string &output);
+
+extern "C" bool LLDBSWIGPythonRunScriptKeywordThread(
+ const char *python_function_name, const char *session_dictionary_name,
+ lldb::ThreadSP &thread, std::string &output);
+
+extern "C" bool LLDBSWIGPythonRunScriptKeywordTarget(
+ const char *python_function_name, const char *session_dictionary_name,
+ lldb::TargetSP &target, std::string &output);
+
+extern "C" bool LLDBSWIGPythonRunScriptKeywordFrame(
+ const char *python_function_name, const char *session_dictionary_name,
+ lldb::StackFrameSP &frame, std::string &output);
+
+extern "C" bool LLDBSWIGPythonRunScriptKeywordValue(
+ const char *python_function_name, const char *session_dictionary_name,
+ lldb::ValueObjectSP &value, std::string &output);
+
+extern "C" void *
+LLDBSWIGPython_GetDynamicSetting(void *module, const char *setting,
+ const lldb::TargetSP &target_sp);
+
+static bool g_initialized = false;
+
+namespace {
+
+// Initializing Python is not a straightforward process. We cannot control
+// what external code may have done before getting to this point in LLDB,
+// including potentially having already initialized Python, so we need to do a
+// lot of work to ensure that the existing state of the system is maintained
+// across our initialization. We do this by using an RAII pattern where we
+// save off initial state at the beginning, and restore it at the end
+struct InitializePythonRAII {
+public:
+ InitializePythonRAII()
+ : m_gil_state(PyGILState_UNLOCKED), m_was_already_initialized(false) {
+ // Python will muck with STDIN terminal state, so save off any current TTY
+ // settings so we can restore them.
+ m_stdin_tty_state.Save(STDIN_FILENO, false);
+
+ InitializePythonHome();
+
+ // Register _lldb as a built-in module.
+ PyImport_AppendInittab("_lldb", LLDBSwigPyInit);
+
+// Python < 3.2 and Python >= 3.2 reversed the ordering requirements for
+// calling `Py_Initialize` and `PyEval_InitThreads`. < 3.2 requires that you
+// call `PyEval_InitThreads` first, and >= 3.2 requires that you call it last.
+#if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || (PY_MAJOR_VERSION > 3)
+ Py_InitializeEx(0);
+ InitializeThreadsPrivate();
+#else
+ InitializeThreadsPrivate();
+ Py_InitializeEx(0);
+#endif
+ }
+
+ ~InitializePythonRAII() {
+ if (m_was_already_initialized) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+ LLDB_LOGV(log, "Releasing PyGILState. Returning to state = {0}locked",
+ m_gil_state == PyGILState_UNLOCKED ? "un" : "");
+ PyGILState_Release(m_gil_state);
+ } else {
+ // We initialized the threads in this function, just unlock the GIL.
+ PyEval_SaveThread();
+ }
+
+ m_stdin_tty_state.Restore();
+ }
+
+private:
+ void InitializePythonHome() {
+#if defined(LLDB_PYTHON_HOME)
+#if PY_MAJOR_VERSION >= 3
+ size_t size = 0;
+ static wchar_t *g_python_home = Py_DecodeLocale(LLDB_PYTHON_HOME, &size);
+#else
+ static char g_python_home[] = LLDB_PYTHON_HOME;
+#endif
+ Py_SetPythonHome(g_python_home);
+#else
+#if defined(__APPLE__) && PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 7
+ // For Darwin, the only Python version supported is the one shipped in the
+ // OS OS and linked with lldb. Other installation of Python may have higher
+ // priorities in the path, overriding PYTHONHOME and causing
+ // problems/incompatibilities. In order to avoid confusion, always hardcode
+ // the PythonHome to be right, as it's not going to change.
+ static char path[] =
+ "/System/Library/Frameworks/Python.framework/Versions/2.7";
+ Py_SetPythonHome(path);
+#endif
+#endif
+ }
+
+ void InitializeThreadsPrivate() {
+// Since Python 3.7 `Py_Initialize` calls `PyEval_InitThreads` inside itself,
+// so there is no way to determine whether the embedded interpreter
+// was already initialized by some external code. `PyEval_ThreadsInitialized`
+// would always return `true` and `PyGILState_Ensure/Release` flow would be
+// executed instead of unlocking GIL with `PyEval_SaveThread`. When
+// an another thread calls `PyGILState_Ensure` it would get stuck in deadlock.
+#if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 7) || (PY_MAJOR_VERSION > 3)
+ // The only case we should go further and acquire the GIL: it is unlocked.
+ if (PyGILState_Check())
+ return;
+#endif
+
+ if (PyEval_ThreadsInitialized()) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+
+ m_was_already_initialized = true;
+ m_gil_state = PyGILState_Ensure();
+ LLDB_LOGV(log, "Ensured PyGILState. Previous state = {0}locked\n",
+ m_gil_state == PyGILState_UNLOCKED ? "un" : "");
+ return;
+ }
+
+ // InitThreads acquires the GIL if it hasn't been called before.
+ PyEval_InitThreads();
+ }
+
+ TerminalState m_stdin_tty_state;
+ PyGILState_STATE m_gil_state;
+ bool m_was_already_initialized;
+};
+} // namespace
+
+void ScriptInterpreterPython::ComputePythonDirForApple(
+ llvm::SmallVectorImpl<char> &path) {
+ auto style = llvm::sys::path::Style::posix;
+
+ llvm::StringRef path_ref(path.begin(), path.size());
+ auto rbegin = llvm::sys::path::rbegin(path_ref, style);
+ auto rend = llvm::sys::path::rend(path_ref);
+ auto framework = std::find(rbegin, rend, "LLDB.framework");
+ if (framework == rend) {
+ ComputePythonDirForPosix(path);
+ return;
+ }
+ path.resize(framework - rend);
+ llvm::sys::path::append(path, style, "LLDB.framework", "Resources", "Python");
+}
+
+void ScriptInterpreterPython::ComputePythonDirForPosix(
+ llvm::SmallVectorImpl<char> &path) {
+ auto style = llvm::sys::path::Style::posix;
+#if defined(LLDB_PYTHON_RELATIVE_LIBDIR)
+ // Build the path by backing out of the lib dir, then building with whatever
+ // the real python interpreter uses. (e.g. lib for most, lib64 on RHEL
+ // x86_64).
+ llvm::sys::path::remove_filename(path, style);
+ llvm::sys::path::append(path, style, LLDB_PYTHON_RELATIVE_LIBDIR);
+#else
+ llvm::sys::path::append(path, style,
+ "python" + llvm::Twine(PY_MAJOR_VERSION) + "." +
+ llvm::Twine(PY_MINOR_VERSION),
+ "site-packages");
+#endif
+}
+
+void ScriptInterpreterPython::ComputePythonDirForWindows(
+ llvm::SmallVectorImpl<char> &path) {
+ auto style = llvm::sys::path::Style::windows;
+ llvm::sys::path::remove_filename(path, style);
+ llvm::sys::path::append(path, style, "lib", "site-packages");
+
+ // This will be injected directly through FileSpec.GetDirectory().SetString(),
+ // so we need to normalize manually.
+ std::replace(path.begin(), path.end(), '\\', '/');
+}
+
+FileSpec ScriptInterpreterPython::GetPythonDir() {
+ static FileSpec g_spec = []() {
+ FileSpec spec = HostInfo::GetShlibDir();
+ if (!spec)
+ return FileSpec();
+ llvm::SmallString<64> path;
+ spec.GetPath(path);
+
+#if defined(__APPLE__)
+ ComputePythonDirForApple(path);
+#elif defined(_WIN32)
+ ComputePythonDirForWindows(path);
+#else
+ ComputePythonDirForPosix(path);
+#endif
+ spec.GetDirectory().SetString(path);
+ return spec;
+ }();
+ return g_spec;
+}
+
+lldb_private::ConstString ScriptInterpreterPython::GetPluginNameStatic() {
+ static ConstString g_name("script-python");
+ return g_name;
+}
+
+const char *ScriptInterpreterPython::GetPluginDescriptionStatic() {
+ return "Embedded Python interpreter";
+}
+
+void ScriptInterpreterPython::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(),
+ lldb::eScriptLanguagePython,
+ ScriptInterpreterPythonImpl::CreateInstance);
+ });
+}
+
+void ScriptInterpreterPython::Terminate() {}
+
+ScriptInterpreterPythonImpl::Locker::Locker(
+ ScriptInterpreterPythonImpl *py_interpreter, uint16_t on_entry,
+ uint16_t on_leave, FILE *in, FILE *out, FILE *err)
+ : ScriptInterpreterLocker(),
+ m_teardown_session((on_leave & TearDownSession) == TearDownSession),
+ m_python_interpreter(py_interpreter) {
+ DoAcquireLock();
+ if ((on_entry & InitSession) == InitSession) {
+ if (!DoInitSession(on_entry, in, out, err)) {
+ // Don't teardown the session if we didn't init it.
+ m_teardown_session = false;
+ }
+ }
+}
+
+bool ScriptInterpreterPythonImpl::Locker::DoAcquireLock() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+ m_GILState = PyGILState_Ensure();
+ LLDB_LOGV(log, "Ensured PyGILState. Previous state = {0}locked",
+ m_GILState == PyGILState_UNLOCKED ? "un" : "");
+
+ // we need to save the thread state when we first start the command because
+ // we might decide to interrupt it while some action is taking place outside
+ // of Python (e.g. printing to screen, waiting for the network, ...) in that
+ // case, _PyThreadState_Current will be NULL - and we would be unable to set
+ // the asynchronous exception - not a desirable situation
+ m_python_interpreter->SetThreadState(PyThreadState_Get());
+ m_python_interpreter->IncrementLockCount();
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::Locker::DoInitSession(uint16_t on_entry_flags,
+ FILE *in, FILE *out,
+ FILE *err) {
+ if (!m_python_interpreter)
+ return false;
+ return m_python_interpreter->EnterSession(on_entry_flags, in, out, err);
+}
+
+bool ScriptInterpreterPythonImpl::Locker::DoFreeLock() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+ LLDB_LOGV(log, "Releasing PyGILState. Returning to state = {0}locked",
+ m_GILState == PyGILState_UNLOCKED ? "un" : "");
+ PyGILState_Release(m_GILState);
+ m_python_interpreter->DecrementLockCount();
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::Locker::DoTearDownSession() {
+ if (!m_python_interpreter)
+ return false;
+ m_python_interpreter->LeaveSession();
+ return true;
+}
+
+ScriptInterpreterPythonImpl::Locker::~Locker() {
+ if (m_teardown_session)
+ DoTearDownSession();
+ DoFreeLock();
+}
+
+ScriptInterpreterPythonImpl::ScriptInterpreterPythonImpl(Debugger &debugger)
+ : ScriptInterpreterPython(debugger), m_saved_stdin(), m_saved_stdout(),
+ m_saved_stderr(), m_main_module(),
+ m_session_dict(PyInitialValue::Invalid),
+ m_sys_module_dict(PyInitialValue::Invalid), m_run_one_line_function(),
+ m_run_one_line_str_global(),
+ m_dictionary_name(m_debugger.GetInstanceName().AsCString()),
+ m_terminal_state(), m_active_io_handler(eIOHandlerNone),
+ m_session_is_active(false), m_pty_slave_is_open(false),
+ m_valid_session(true), m_lock_count(0), m_command_thread_state(nullptr) {
+ InitializePrivate();
+
+ m_dictionary_name.append("_dict");
+ StreamString run_string;
+ run_string.Printf("%s = dict()", m_dictionary_name.c_str());
+
+ Locker locker(this, Locker::AcquireLock, Locker::FreeAcquiredLock);
+ PyRun_SimpleString(run_string.GetData());
+
+ run_string.Clear();
+ run_string.Printf(
+ "run_one_line (%s, 'import copy, keyword, os, re, sys, uuid, lldb')",
+ m_dictionary_name.c_str());
+ PyRun_SimpleString(run_string.GetData());
+
+ // Reloading modules requires a different syntax in Python 2 and Python 3.
+ // This provides a consistent syntax no matter what version of Python.
+ run_string.Clear();
+ run_string.Printf("run_one_line (%s, 'from six.moves import reload_module')",
+ m_dictionary_name.c_str());
+ PyRun_SimpleString(run_string.GetData());
+
+ // WARNING: temporary code that loads Cocoa formatters - this should be done
+ // on a per-platform basis rather than loading the whole set and letting the
+ // individual formatter classes exploit APIs to check whether they can/cannot
+ // do their task
+ run_string.Clear();
+ run_string.Printf(
+ "run_one_line (%s, 'import lldb.formatters, lldb.formatters.cpp, pydoc')",
+ m_dictionary_name.c_str());
+ PyRun_SimpleString(run_string.GetData());
+ run_string.Clear();
+
+ run_string.Printf("run_one_line (%s, 'import lldb.embedded_interpreter; from "
+ "lldb.embedded_interpreter import run_python_interpreter; "
+ "from lldb.embedded_interpreter import run_one_line')",
+ m_dictionary_name.c_str());
+ PyRun_SimpleString(run_string.GetData());
+ run_string.Clear();
+
+ run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64
+ "; pydoc.pager = pydoc.plainpager')",
+ m_dictionary_name.c_str(), m_debugger.GetID());
+ PyRun_SimpleString(run_string.GetData());
+}
+
+ScriptInterpreterPythonImpl::~ScriptInterpreterPythonImpl() {
+ // the session dictionary may hold objects with complex state which means
+ // that they may need to be torn down with some level of smarts and that, in
+ // turn, requires a valid thread state force Python to procure itself such a
+ // thread state, nuke the session dictionary and then release it for others
+ // to use and proceed with the rest of the shutdown
+ auto gil_state = PyGILState_Ensure();
+ m_session_dict.Reset();
+ PyGILState_Release(gil_state);
+}
+
+lldb_private::ConstString ScriptInterpreterPythonImpl::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ScriptInterpreterPythonImpl::GetPluginVersion() { return 1; }
+
+void ScriptInterpreterPythonImpl::IOHandlerActivated(IOHandler &io_handler,
+ bool interactive) {
+ const char *instructions = nullptr;
+
+ switch (m_active_io_handler) {
+ case eIOHandlerNone:
+ break;
+ case eIOHandlerBreakpoint:
+ instructions = R"(Enter your Python command(s). Type 'DONE' to end.
+def function (frame, bp_loc, internal_dict):
+ """frame: the lldb.SBFrame for the location at which you stopped
+ bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information
+ internal_dict: an LLDB support object not to be used"""
+)";
+ break;
+ case eIOHandlerWatchpoint:
+ instructions = "Enter your Python command(s). Type 'DONE' to end.\n";
+ break;
+ }
+
+ if (instructions) {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ if (output_sp && interactive) {
+ output_sp->PutCString(instructions);
+ output_sp->Flush();
+ }
+ }
+}
+
+void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) {
+ io_handler.SetIsDone(true);
+ bool batch_mode = m_debugger.GetCommandInterpreter().GetBatchCommandMode();
+
+ switch (m_active_io_handler) {
+ case eIOHandlerNone:
+ break;
+ case eIOHandlerBreakpoint: {
+ std::vector<BreakpointOptions *> *bp_options_vec =
+ (std::vector<BreakpointOptions *> *)io_handler.GetUserData();
+ for (auto bp_options : *bp_options_vec) {
+ if (!bp_options)
+ continue;
+
+ auto data_up = llvm::make_unique<CommandDataPython>();
+ if (!data_up)
+ break;
+ data_up->user_source.SplitIntoLines(data);
+
+ if (GenerateBreakpointCommandCallbackData(data_up->user_source,
+ data_up->script_source)
+ .Success()) {
+ auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>(
+ std::move(data_up));
+ bp_options->SetCallback(
+ ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp);
+ } else if (!batch_mode) {
+ StreamFileSP error_sp = io_handler.GetErrorStreamFile();
+ if (error_sp) {
+ error_sp->Printf("Warning: No command attached to breakpoint.\n");
+ error_sp->Flush();
+ }
+ }
+ }
+ m_active_io_handler = eIOHandlerNone;
+ } break;
+ case eIOHandlerWatchpoint: {
+ WatchpointOptions *wp_options =
+ (WatchpointOptions *)io_handler.GetUserData();
+ auto data_up = llvm::make_unique<WatchpointOptions::CommandData>();
+ data_up->user_source.SplitIntoLines(data);
+
+ if (GenerateWatchpointCommandCallbackData(data_up->user_source,
+ data_up->script_source)) {
+ auto baton_sp =
+ std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up));
+ wp_options->SetCallback(
+ ScriptInterpreterPythonImpl::WatchpointCallbackFunction, baton_sp);
+ } else if (!batch_mode) {
+ StreamFileSP error_sp = io_handler.GetErrorStreamFile();
+ if (error_sp) {
+ error_sp->Printf("Warning: No command attached to breakpoint.\n");
+ error_sp->Flush();
+ }
+ }
+ m_active_io_handler = eIOHandlerNone;
+ } break;
+ }
+}
+
+lldb::ScriptInterpreterSP
+ScriptInterpreterPythonImpl::CreateInstance(Debugger &debugger) {
+ return std::make_shared<ScriptInterpreterPythonImpl>(debugger);
+}
+
+void ScriptInterpreterPythonImpl::ResetOutputFileHandle(FILE *fh) {}
+
+void ScriptInterpreterPythonImpl::SaveTerminalState(int fd) {
+ // Python mucks with the terminal state of STDIN. If we can possibly avoid
+ // this by setting the file handles up correctly prior to entering the
+ // interpreter we should. For now we save and restore the terminal state on
+ // the input file handle.
+ m_terminal_state.Save(fd, false);
+}
+
+void ScriptInterpreterPythonImpl::RestoreTerminalState() {
+ // Python mucks with the terminal state of STDIN. If we can possibly avoid
+ // this by setting the file handles up correctly prior to entering the
+ // interpreter we should. For now we save and restore the terminal state on
+ // the input file handle.
+ m_terminal_state.Restore();
+}
+
+void ScriptInterpreterPythonImpl::LeaveSession() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+ if (log)
+ log->PutCString("ScriptInterpreterPythonImpl::LeaveSession()");
+
+ // checking that we have a valid thread state - since we use our own
+ // threading and locking in some (rare) cases during cleanup Python may end
+ // up believing we have no thread state and PyImport_AddModule will crash if
+ // that is the case - since that seems to only happen when destroying the
+ // SBDebugger, we can make do without clearing up stdout and stderr
+
+ // rdar://problem/11292882
+ // When the current thread state is NULL, PyThreadState_Get() issues a fatal
+ // error.
+ if (PyThreadState_GetDict()) {
+ PythonDictionary &sys_module_dict = GetSysModuleDictionary();
+ if (sys_module_dict.IsValid()) {
+ if (m_saved_stdin.IsValid()) {
+ sys_module_dict.SetItemForKey(PythonString("stdin"), m_saved_stdin);
+ m_saved_stdin.Reset();
+ }
+ if (m_saved_stdout.IsValid()) {
+ sys_module_dict.SetItemForKey(PythonString("stdout"), m_saved_stdout);
+ m_saved_stdout.Reset();
+ }
+ if (m_saved_stderr.IsValid()) {
+ sys_module_dict.SetItemForKey(PythonString("stderr"), m_saved_stderr);
+ m_saved_stderr.Reset();
+ }
+ }
+ }
+
+ m_session_is_active = false;
+}
+
+bool ScriptInterpreterPythonImpl::SetStdHandle(File &file, const char *py_name,
+ PythonFile &save_file,
+ const char *mode) {
+ if (file.IsValid()) {
+ // Flush the file before giving it to python to avoid interleaved output.
+ file.Flush();
+
+ PythonDictionary &sys_module_dict = GetSysModuleDictionary();
+
+ save_file = sys_module_dict.GetItemForKey(PythonString(py_name))
+ .AsType<PythonFile>();
+
+ PythonFile new_file(file, mode);
+ sys_module_dict.SetItemForKey(PythonString(py_name), new_file);
+ return true;
+ } else
+ save_file.Reset();
+ return false;
+}
+
+bool ScriptInterpreterPythonImpl::EnterSession(uint16_t on_entry_flags,
+ FILE *in, FILE *out, FILE *err) {
+ // If we have already entered the session, without having officially 'left'
+ // it, then there is no need to 'enter' it again.
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+ if (m_session_is_active) {
+ if (log)
+ log->Printf(
+ "ScriptInterpreterPythonImpl::EnterSession(on_entry_flags=0x%" PRIx16
+ ") session is already active, returning without doing anything",
+ on_entry_flags);
+ return false;
+ }
+
+ if (log)
+ log->Printf(
+ "ScriptInterpreterPythonImpl::EnterSession(on_entry_flags=0x%" PRIx16
+ ")",
+ on_entry_flags);
+
+ m_session_is_active = true;
+
+ StreamString run_string;
+
+ if (on_entry_flags & Locker::InitGlobals) {
+ run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64,
+ m_dictionary_name.c_str(), m_debugger.GetID());
+ run_string.Printf(
+ "; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")",
+ m_debugger.GetID());
+ run_string.PutCString("; lldb.target = lldb.debugger.GetSelectedTarget()");
+ run_string.PutCString("; lldb.process = lldb.target.GetProcess()");
+ run_string.PutCString("; lldb.thread = lldb.process.GetSelectedThread ()");
+ run_string.PutCString("; lldb.frame = lldb.thread.GetSelectedFrame ()");
+ run_string.PutCString("')");
+ } else {
+ // If we aren't initing the globals, we should still always set the
+ // debugger (since that is always unique.)
+ run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64,
+ m_dictionary_name.c_str(), m_debugger.GetID());
+ run_string.Printf(
+ "; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")",
+ m_debugger.GetID());
+ run_string.PutCString("')");
+ }
+
+ PyRun_SimpleString(run_string.GetData());
+ run_string.Clear();
+
+ PythonDictionary &sys_module_dict = GetSysModuleDictionary();
+ if (sys_module_dict.IsValid()) {
+ File in_file(in, false);
+ File out_file(out, false);
+ File err_file(err, false);
+
+ lldb::StreamFileSP in_sp;
+ lldb::StreamFileSP out_sp;
+ lldb::StreamFileSP err_sp;
+ if (!in_file.IsValid() || !out_file.IsValid() || !err_file.IsValid())
+ m_debugger.AdoptTopIOHandlerFilesIfInvalid(in_sp, out_sp, err_sp);
+
+ if (on_entry_flags & Locker::NoSTDIN) {
+ m_saved_stdin.Reset();
+ } else {
+ if (!SetStdHandle(in_file, "stdin", m_saved_stdin, "r")) {
+ if (in_sp)
+ SetStdHandle(in_sp->GetFile(), "stdin", m_saved_stdin, "r");
+ }
+ }
+
+ if (!SetStdHandle(out_file, "stdout", m_saved_stdout, "w")) {
+ if (out_sp)
+ SetStdHandle(out_sp->GetFile(), "stdout", m_saved_stdout, "w");
+ }
+
+ if (!SetStdHandle(err_file, "stderr", m_saved_stderr, "w")) {
+ if (err_sp)
+ SetStdHandle(err_sp->GetFile(), "stderr", m_saved_stderr, "w");
+ }
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ return true;
+}
+
+PythonObject &ScriptInterpreterPythonImpl::GetMainModule() {
+ if (!m_main_module.IsValid())
+ m_main_module.Reset(PyRefType::Borrowed, PyImport_AddModule("__main__"));
+ return m_main_module;
+}
+
+PythonDictionary &ScriptInterpreterPythonImpl::GetSessionDictionary() {
+ if (m_session_dict.IsValid())
+ return m_session_dict;
+
+ PythonObject &main_module = GetMainModule();
+ if (!main_module.IsValid())
+ return m_session_dict;
+
+ PythonDictionary main_dict(PyRefType::Borrowed,
+ PyModule_GetDict(main_module.get()));
+ if (!main_dict.IsValid())
+ return m_session_dict;
+
+ PythonObject item = main_dict.GetItemForKey(PythonString(m_dictionary_name));
+ m_session_dict.Reset(PyRefType::Borrowed, item.get());
+ return m_session_dict;
+}
+
+PythonDictionary &ScriptInterpreterPythonImpl::GetSysModuleDictionary() {
+ if (m_sys_module_dict.IsValid())
+ return m_sys_module_dict;
+
+ PythonObject sys_module(PyRefType::Borrowed, PyImport_AddModule("sys"));
+ if (sys_module.IsValid())
+ m_sys_module_dict.Reset(PyRefType::Borrowed,
+ PyModule_GetDict(sys_module.get()));
+ return m_sys_module_dict;
+}
+
+static std::string GenerateUniqueName(const char *base_name_wanted,
+ uint32_t &functions_counter,
+ const void *name_token = nullptr) {
+ StreamString sstr;
+
+ if (!base_name_wanted)
+ return std::string();
+
+ if (!name_token)
+ sstr.Printf("%s_%d", base_name_wanted, functions_counter++);
+ else
+ sstr.Printf("%s_%p", base_name_wanted, name_token);
+
+ return sstr.GetString();
+}
+
+bool ScriptInterpreterPythonImpl::GetEmbeddedInterpreterModuleObjects() {
+ if (m_run_one_line_function.IsValid())
+ return true;
+
+ PythonObject module(PyRefType::Borrowed,
+ PyImport_AddModule("lldb.embedded_interpreter"));
+ if (!module.IsValid())
+ return false;
+
+ PythonDictionary module_dict(PyRefType::Borrowed,
+ PyModule_GetDict(module.get()));
+ if (!module_dict.IsValid())
+ return false;
+
+ m_run_one_line_function =
+ module_dict.GetItemForKey(PythonString("run_one_line"));
+ m_run_one_line_str_global =
+ module_dict.GetItemForKey(PythonString("g_run_one_line_str"));
+ return m_run_one_line_function.IsValid();
+}
+
+static void ReadThreadBytesReceived(void *baton, const void *src,
+ size_t src_len) {
+ if (src && src_len) {
+ Stream *strm = (Stream *)baton;
+ strm->Write(src, src_len);
+ strm->Flush();
+ }
+}
+
+bool ScriptInterpreterPythonImpl::ExecuteOneLine(
+ llvm::StringRef command, CommandReturnObject *result,
+ const ExecuteScriptOptions &options) {
+ std::string command_str = command.str();
+
+ if (!m_valid_session)
+ return false;
+
+ if (!command.empty()) {
+ // We want to call run_one_line, passing in the dictionary and the command
+ // string. We cannot do this through PyRun_SimpleString here because the
+ // command string may contain escaped characters, and putting it inside
+ // another string to pass to PyRun_SimpleString messes up the escaping. So
+ // we use the following more complicated method to pass the command string
+ // directly down to Python.
+ Debugger &debugger = m_debugger;
+
+ StreamFileSP input_file_sp;
+ StreamFileSP output_file_sp;
+ StreamFileSP error_file_sp;
+ Communication output_comm(
+ "lldb.ScriptInterpreterPythonImpl.ExecuteOneLine.comm");
+ bool join_read_thread = false;
+ if (options.GetEnableIO()) {
+ if (result) {
+ input_file_sp = debugger.GetInputFile();
+ // Set output to a temporary file so we can forward the results on to
+ // the result object
+
+ Pipe pipe;
+ Status pipe_result = pipe.CreateNew(false);
+ if (pipe_result.Success()) {
+#if defined(_WIN32)
+ lldb::file_t read_file = pipe.GetReadNativeHandle();
+ pipe.ReleaseReadFileDescriptor();
+ std::unique_ptr<ConnectionGenericFile> conn_up(
+ new ConnectionGenericFile(read_file, true));
+#else
+ std::unique_ptr<ConnectionFileDescriptor> conn_up(
+ new ConnectionFileDescriptor(pipe.ReleaseReadFileDescriptor(),
+ true));
+#endif
+ if (conn_up->IsConnected()) {
+ output_comm.SetConnection(conn_up.release());
+ output_comm.SetReadThreadBytesReceivedCallback(
+ ReadThreadBytesReceived, &result->GetOutputStream());
+ output_comm.StartReadThread();
+ join_read_thread = true;
+ FILE *outfile_handle =
+ fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
+ output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
+ error_file_sp = output_file_sp;
+ if (outfile_handle)
+ ::setbuf(outfile_handle, nullptr);
+
+ result->SetImmediateOutputFile(
+ debugger.GetOutputFile()->GetFile().GetStream());
+ result->SetImmediateErrorFile(
+ debugger.GetErrorFile()->GetFile().GetStream());
+ }
+ }
+ }
+ if (!input_file_sp || !output_file_sp || !error_file_sp)
+ debugger.AdoptTopIOHandlerFilesIfInvalid(input_file_sp, output_file_sp,
+ error_file_sp);
+ } else {
+ input_file_sp = std::make_shared<StreamFile>();
+ FileSystem::Instance().Open(input_file_sp->GetFile(),
+ FileSpec(FileSystem::DEV_NULL),
+ File::eOpenOptionRead);
+
+ output_file_sp = std::make_shared<StreamFile>();
+ FileSystem::Instance().Open(output_file_sp->GetFile(),
+ FileSpec(FileSystem::DEV_NULL),
+ File::eOpenOptionWrite);
+
+ error_file_sp = output_file_sp;
+ }
+
+ FILE *in_file = input_file_sp->GetFile().GetStream();
+ FILE *out_file = output_file_sp->GetFile().GetStream();
+ FILE *err_file = error_file_sp->GetFile().GetStream();
+ bool success = false;
+ {
+ // WARNING! It's imperative that this RAII scope be as tight as
+ // possible. In particular, the scope must end *before* we try to join
+ // the read thread. The reason for this is that a pre-requisite for
+ // joining the read thread is that we close the write handle (to break
+ // the pipe and cause it to wake up and exit). But acquiring the GIL as
+ // below will redirect Python's stdio to use this same handle. If we
+ // close the handle while Python is still using it, bad things will
+ // happen.
+ Locker locker(
+ this,
+ Locker::AcquireLock | Locker::InitSession |
+ (options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) |
+ ((result && result->GetInteractive()) ? 0 : Locker::NoSTDIN),
+ Locker::FreeAcquiredLock | Locker::TearDownSession, in_file, out_file,
+ err_file);
+
+ // Find the correct script interpreter dictionary in the main module.
+ PythonDictionary &session_dict = GetSessionDictionary();
+ if (session_dict.IsValid()) {
+ if (GetEmbeddedInterpreterModuleObjects()) {
+ if (PyCallable_Check(m_run_one_line_function.get())) {
+ PythonObject pargs(
+ PyRefType::Owned,
+ Py_BuildValue("(Os)", session_dict.get(), command_str.c_str()));
+ if (pargs.IsValid()) {
+ PythonObject return_value(
+ PyRefType::Owned,
+ PyObject_CallObject(m_run_one_line_function.get(),
+ pargs.get()));
+ if (return_value.IsValid())
+ success = true;
+ else if (options.GetMaskoutErrors() && PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ }
+ }
+ }
+ }
+
+ // Flush our output and error file handles
+ ::fflush(out_file);
+ if (out_file != err_file)
+ ::fflush(err_file);
+ }
+
+ if (join_read_thread) {
+ // Close the write end of the pipe since we are done with our one line
+ // script. This should cause the read thread that output_comm is using to
+ // exit
+ output_file_sp->GetFile().Close();
+ // The close above should cause this thread to exit when it gets to the
+ // end of file, so let it get all its data
+ output_comm.JoinReadThread();
+ // Now we can close the read end of the pipe
+ output_comm.Disconnect();
+ }
+
+ if (success)
+ return true;
+
+ // The one-liner failed. Append the error message.
+ if (result) {
+ result->AppendErrorWithFormat(
+ "python failed attempting to evaluate '%s'\n", command_str.c_str());
+ }
+ return false;
+ }
+
+ if (result)
+ result->AppendError("empty command passed to python\n");
+ return false;
+}
+
+void ScriptInterpreterPythonImpl::ExecuteInterpreterLoop() {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ Debugger &debugger = m_debugger;
+
+ // At the moment, the only time the debugger does not have an input file
+ // handle is when this is called directly from Python, in which case it is
+ // both dangerous and unnecessary (not to mention confusing) to try to embed
+ // a running interpreter loop inside the already running Python interpreter
+ // loop, so we won't do it.
+
+ if (!debugger.GetInputFile()->GetFile().IsValid())
+ return;
+
+ IOHandlerSP io_handler_sp(new IOHandlerPythonInterpreter(debugger, this));
+ if (io_handler_sp) {
+ debugger.PushIOHandler(io_handler_sp);
+ }
+}
+
+bool ScriptInterpreterPythonImpl::Interrupt() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+
+ if (IsExecutingPython()) {
+ PyThreadState *state = PyThreadState_GET();
+ if (!state)
+ state = GetThreadState();
+ if (state) {
+ long tid = state->thread_id;
+ PyThreadState_Swap(state);
+ int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt);
+ if (log)
+ log->Printf("ScriptInterpreterPythonImpl::Interrupt() sending "
+ "PyExc_KeyboardInterrupt (tid = %li, num_threads = %i)...",
+ tid, num_threads);
+ return true;
+ }
+ }
+ if (log)
+ log->Printf(
+ "ScriptInterpreterPythonImpl::Interrupt() python code not running, "
+ "can't interrupt");
+ return false;
+}
+bool ScriptInterpreterPythonImpl::ExecuteOneLineWithReturn(
+ llvm::StringRef in_string, ScriptInterpreter::ScriptReturnType return_type,
+ void *ret_value, const ExecuteScriptOptions &options) {
+
+ Locker locker(this,
+ Locker::AcquireLock | Locker::InitSession |
+ (options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) |
+ Locker::NoSTDIN,
+ Locker::FreeAcquiredLock | Locker::TearDownSession);
+
+ PythonObject py_return;
+ PythonObject &main_module = GetMainModule();
+ PythonDictionary globals(PyRefType::Borrowed,
+ PyModule_GetDict(main_module.get()));
+ PythonObject py_error;
+ bool ret_success = false;
+ int success;
+
+ PythonDictionary locals = GetSessionDictionary();
+
+ if (!locals.IsValid()) {
+ locals.Reset(
+ PyRefType::Owned,
+ PyObject_GetAttrString(globals.get(), m_dictionary_name.c_str()));
+ }
+
+ if (!locals.IsValid())
+ locals = globals;
+
+ py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
+ if (py_error.IsValid())
+ PyErr_Clear();
+
+ std::string as_string = in_string.str();
+ { // scope for PythonInputReaderManager
+ // PythonInputReaderManager py_input(options.GetEnableIO() ? this : NULL);
+ py_return.Reset(PyRefType::Owned,
+ PyRun_String(as_string.c_str(), Py_eval_input,
+ globals.get(), locals.get()));
+ if (!py_return.IsValid()) {
+ py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
+ if (py_error.IsValid())
+ PyErr_Clear();
+
+ py_return.Reset(PyRefType::Owned,
+ PyRun_String(as_string.c_str(), Py_single_input,
+ globals.get(), locals.get()));
+ }
+ }
+
+ if (py_return.IsValid()) {
+ switch (return_type) {
+ case eScriptReturnTypeCharPtr: // "char *"
+ {
+ const char format[3] = "s#";
+ success = PyArg_Parse(py_return.get(), format, (char **)ret_value);
+ break;
+ }
+ case eScriptReturnTypeCharStrOrNone: // char* or NULL if py_return ==
+ // Py_None
+ {
+ const char format[3] = "z";
+ success = PyArg_Parse(py_return.get(), format, (char **)ret_value);
+ break;
+ }
+ case eScriptReturnTypeBool: {
+ const char format[2] = "b";
+ success = PyArg_Parse(py_return.get(), format, (bool *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeShortInt: {
+ const char format[2] = "h";
+ success = PyArg_Parse(py_return.get(), format, (short *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeShortIntUnsigned: {
+ const char format[2] = "H";
+ success =
+ PyArg_Parse(py_return.get(), format, (unsigned short *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeInt: {
+ const char format[2] = "i";
+ success = PyArg_Parse(py_return.get(), format, (int *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeIntUnsigned: {
+ const char format[2] = "I";
+ success = PyArg_Parse(py_return.get(), format, (unsigned int *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeLongInt: {
+ const char format[2] = "l";
+ success = PyArg_Parse(py_return.get(), format, (long *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeLongIntUnsigned: {
+ const char format[2] = "k";
+ success =
+ PyArg_Parse(py_return.get(), format, (unsigned long *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeLongLong: {
+ const char format[2] = "L";
+ success = PyArg_Parse(py_return.get(), format, (long long *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeLongLongUnsigned: {
+ const char format[2] = "K";
+ success =
+ PyArg_Parse(py_return.get(), format, (unsigned long long *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeFloat: {
+ const char format[2] = "f";
+ success = PyArg_Parse(py_return.get(), format, (float *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeDouble: {
+ const char format[2] = "d";
+ success = PyArg_Parse(py_return.get(), format, (double *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeChar: {
+ const char format[2] = "c";
+ success = PyArg_Parse(py_return.get(), format, (char *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeOpaqueObject: {
+ success = true;
+ PyObject *saved_value = py_return.get();
+ Py_XINCREF(saved_value);
+ *((PyObject **)ret_value) = saved_value;
+ break;
+ }
+ }
+
+ ret_success = success;
+ }
+
+ py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
+ if (py_error.IsValid()) {
+ ret_success = false;
+ if (options.GetMaskoutErrors()) {
+ if (PyErr_GivenExceptionMatches(py_error.get(), PyExc_SyntaxError))
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ }
+
+ return ret_success;
+}
+
+Status ScriptInterpreterPythonImpl::ExecuteMultipleLines(
+ const char *in_string, const ExecuteScriptOptions &options) {
+ Status error;
+
+ Locker locker(this,
+ Locker::AcquireLock | Locker::InitSession |
+ (options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) |
+ Locker::NoSTDIN,
+ Locker::FreeAcquiredLock | Locker::TearDownSession);
+
+ PythonObject return_value;
+ PythonObject &main_module = GetMainModule();
+ PythonDictionary globals(PyRefType::Borrowed,
+ PyModule_GetDict(main_module.get()));
+ PythonObject py_error;
+
+ PythonDictionary locals = GetSessionDictionary();
+
+ if (!locals.IsValid())
+ locals.Reset(
+ PyRefType::Owned,
+ PyObject_GetAttrString(globals.get(), m_dictionary_name.c_str()));
+
+ if (!locals.IsValid())
+ locals = globals;
+
+ py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
+ if (py_error.IsValid())
+ PyErr_Clear();
+
+ if (in_string != nullptr) {
+ PythonObject code_object;
+ code_object.Reset(PyRefType::Owned,
+ Py_CompileString(in_string, "temp.py", Py_file_input));
+
+ if (code_object.IsValid()) {
+// In Python 2.x, PyEval_EvalCode takes a PyCodeObject, but in Python 3.x, it
+// takes a PyObject. They are convertible (hence the function
+// PyCode_Check(PyObject*), so we have to do the cast for Python 2.x
+#if PY_MAJOR_VERSION >= 3
+ PyObject *py_code_obj = code_object.get();
+#else
+ PyCodeObject *py_code_obj =
+ reinterpret_cast<PyCodeObject *>(code_object.get());
+#endif
+ return_value.Reset(
+ PyRefType::Owned,
+ PyEval_EvalCode(py_code_obj, globals.get(), locals.get()));
+ }
+ }
+
+ PythonExceptionState exception_state(!options.GetMaskoutErrors());
+ if (exception_state.IsError())
+ error.SetErrorString(exception_state.Format().c_str());
+
+ return error;
+}
+
+void ScriptInterpreterPythonImpl::CollectDataForBreakpointCommandCallback(
+ std::vector<BreakpointOptions *> &bp_options_vec,
+ CommandReturnObject &result) {
+ m_active_io_handler = eIOHandlerBreakpoint;
+ m_debugger.GetCommandInterpreter().GetPythonCommandsFromIOHandler(
+ " ", *this, true, &bp_options_vec);
+}
+
+void ScriptInterpreterPythonImpl::CollectDataForWatchpointCommandCallback(
+ WatchpointOptions *wp_options, CommandReturnObject &result) {
+ m_active_io_handler = eIOHandlerWatchpoint;
+ m_debugger.GetCommandInterpreter().GetPythonCommandsFromIOHandler(
+ " ", *this, true, wp_options);
+}
+
+void ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction(
+ BreakpointOptions *bp_options, const char *function_name) {
+ // For now just cons up a oneliner that calls the provided function.
+ std::string oneliner("return ");
+ oneliner += function_name;
+ oneliner += "(frame, bp_loc, internal_dict)";
+ m_debugger.GetScriptInterpreter()->SetBreakpointCommandCallback(
+ bp_options, oneliner.c_str());
+}
+
+Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
+ BreakpointOptions *bp_options,
+ std::unique_ptr<BreakpointOptions::CommandData> &cmd_data_up) {
+ Status error;
+ error = GenerateBreakpointCommandCallbackData(cmd_data_up->user_source,
+ cmd_data_up->script_source);
+ if (error.Fail()) {
+ return error;
+ }
+ auto baton_sp =
+ std::make_shared<BreakpointOptions::CommandBaton>(std::move(cmd_data_up));
+ bp_options->SetCallback(
+ ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp);
+ return error;
+}
+
+// Set a Python one-liner as the callback for the breakpoint.
+Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
+ BreakpointOptions *bp_options, const char *command_body_text) {
+ auto data_up = llvm::make_unique<CommandDataPython>();
+
+ // Split the command_body_text into lines, and pass that to
+ // GenerateBreakpointCommandCallbackData. That will wrap the body in an
+ // auto-generated function, and return the function name in script_source.
+ // That is what the callback will actually invoke.
+
+ data_up->user_source.SplitIntoLines(command_body_text);
+ Status error = GenerateBreakpointCommandCallbackData(data_up->user_source,
+ data_up->script_source);
+ if (error.Success()) {
+ auto baton_sp =
+ std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up));
+ bp_options->SetCallback(
+ ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp);
+ return error;
+ } else
+ return error;
+}
+
+// Set a Python one-liner as the callback for the watchpoint.
+void ScriptInterpreterPythonImpl::SetWatchpointCommandCallback(
+ WatchpointOptions *wp_options, const char *oneliner) {
+ auto data_up = llvm::make_unique<WatchpointOptions::CommandData>();
+
+ // It's necessary to set both user_source and script_source to the oneliner.
+ // The former is used to generate callback description (as in watchpoint
+ // command list) while the latter is used for Python to interpret during the
+ // actual callback.
+
+ data_up->user_source.AppendString(oneliner);
+ data_up->script_source.assign(oneliner);
+
+ if (GenerateWatchpointCommandCallbackData(data_up->user_source,
+ data_up->script_source)) {
+ auto baton_sp =
+ std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up));
+ wp_options->SetCallback(
+ ScriptInterpreterPythonImpl::WatchpointCallbackFunction, baton_sp);
+ }
+
+ return;
+}
+
+Status ScriptInterpreterPythonImpl::ExportFunctionDefinitionToInterpreter(
+ StringList &function_def) {
+ // Convert StringList to one long, newline delimited, const char *.
+ std::string function_def_string(function_def.CopyList());
+
+ Status error = ExecuteMultipleLines(
+ function_def_string.c_str(),
+ ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false));
+ return error;
+}
+
+Status ScriptInterpreterPythonImpl::GenerateFunction(const char *signature,
+ const StringList &input) {
+ Status error;
+ int num_lines = input.GetSize();
+ if (num_lines == 0) {
+ error.SetErrorString("No input data.");
+ return error;
+ }
+
+ if (!signature || *signature == 0) {
+ error.SetErrorString("No output function name.");
+ return error;
+ }
+
+ StreamString sstr;
+ StringList auto_generated_function;
+ auto_generated_function.AppendString(signature);
+ auto_generated_function.AppendString(
+ " global_dict = globals()"); // Grab the global dictionary
+ auto_generated_function.AppendString(
+ " new_keys = internal_dict.keys()"); // Make a list of keys in the
+ // session dict
+ auto_generated_function.AppendString(
+ " old_keys = global_dict.keys()"); // Save list of keys in global dict
+ auto_generated_function.AppendString(
+ " global_dict.update (internal_dict)"); // Add the session dictionary
+ // to the
+ // global dictionary.
+
+ // Wrap everything up inside the function, increasing the indentation.
+
+ auto_generated_function.AppendString(" if True:");
+ for (int i = 0; i < num_lines; ++i) {
+ sstr.Clear();
+ sstr.Printf(" %s", input.GetStringAtIndex(i));
+ auto_generated_function.AppendString(sstr.GetData());
+ }
+ auto_generated_function.AppendString(
+ " for key in new_keys:"); // Iterate over all the keys from session
+ // dict
+ auto_generated_function.AppendString(
+ " internal_dict[key] = global_dict[key]"); // Update session dict
+ // values
+ auto_generated_function.AppendString(
+ " if key not in old_keys:"); // If key was not originally in
+ // global dict
+ auto_generated_function.AppendString(
+ " del global_dict[key]"); // ...then remove key/value from
+ // global dict
+
+ // Verify that the results are valid Python.
+
+ error = ExportFunctionDefinitionToInterpreter(auto_generated_function);
+
+ return error;
+}
+
+bool ScriptInterpreterPythonImpl::GenerateTypeScriptFunction(
+ StringList &user_input, std::string &output, const void *name_token) {
+ static uint32_t num_created_functions = 0;
+ user_input.RemoveBlankLines();
+ StreamString sstr;
+
+ // Check to see if we have any data; if not, just return.
+ if (user_input.GetSize() == 0)
+ return false;
+
+ // Take what the user wrote, wrap it all up inside one big auto-generated
+ // Python function, passing in the ValueObject as parameter to the function.
+
+ std::string auto_generated_function_name(
+ GenerateUniqueName("lldb_autogen_python_type_print_func",
+ num_created_functions, name_token));
+ sstr.Printf("def %s (valobj, internal_dict):",
+ auto_generated_function_name.c_str());
+
+ if (!GenerateFunction(sstr.GetData(), user_input).Success())
+ return false;
+
+ // Store the name of the auto-generated function to be called.
+ output.assign(auto_generated_function_name);
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::GenerateScriptAliasFunction(
+ StringList &user_input, std::string &output) {
+ static uint32_t num_created_functions = 0;
+ user_input.RemoveBlankLines();
+ StreamString sstr;
+
+ // Check to see if we have any data; if not, just return.
+ if (user_input.GetSize() == 0)
+ return false;
+
+ std::string auto_generated_function_name(GenerateUniqueName(
+ "lldb_autogen_python_cmd_alias_func", num_created_functions));
+
+ sstr.Printf("def %s (debugger, args, result, internal_dict):",
+ auto_generated_function_name.c_str());
+
+ if (!GenerateFunction(sstr.GetData(), user_input).Success())
+ return false;
+
+ // Store the name of the auto-generated function to be called.
+ output.assign(auto_generated_function_name);
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::GenerateTypeSynthClass(
+ StringList &user_input, std::string &output, const void *name_token) {
+ static uint32_t num_created_classes = 0;
+ user_input.RemoveBlankLines();
+ int num_lines = user_input.GetSize();
+ StreamString sstr;
+
+ // Check to see if we have any data; if not, just return.
+ if (user_input.GetSize() == 0)
+ return false;
+
+ // Wrap all user input into a Python class
+
+ std::string auto_generated_class_name(GenerateUniqueName(
+ "lldb_autogen_python_type_synth_class", num_created_classes, name_token));
+
+ StringList auto_generated_class;
+
+ // Create the function name & definition string.
+
+ sstr.Printf("class %s:", auto_generated_class_name.c_str());
+ auto_generated_class.AppendString(sstr.GetString());
+
+ // Wrap everything up inside the class, increasing the indentation. we don't
+ // need to play any fancy indentation tricks here because there is no
+ // surrounding code whose indentation we need to honor
+ for (int i = 0; i < num_lines; ++i) {
+ sstr.Clear();
+ sstr.Printf(" %s", user_input.GetStringAtIndex(i));
+ auto_generated_class.AppendString(sstr.GetString());
+ }
+
+ // Verify that the results are valid Python. (even though the method is
+ // ExportFunctionDefinitionToInterpreter, a class will actually be exported)
+ // (TODO: rename that method to ExportDefinitionToInterpreter)
+ if (!ExportFunctionDefinitionToInterpreter(auto_generated_class).Success())
+ return false;
+
+ // Store the name of the auto-generated class
+
+ output.assign(auto_generated_class_name);
+ return true;
+}
+
+StructuredData::GenericSP
+ScriptInterpreterPythonImpl::CreateFrameRecognizer(const char *class_name) {
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::GenericSP();
+
+ void *ret_val;
+
+ {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+ ret_val = LLDBSWIGPython_CreateFrameRecognizer(class_name,
+ m_dictionary_name.c_str());
+ }
+
+ return StructuredData::GenericSP(new StructuredPythonObject(ret_val));
+}
+
+lldb::ValueObjectListSP ScriptInterpreterPythonImpl::GetRecognizedArguments(
+ const StructuredData::ObjectSP &os_plugin_object_sp,
+ lldb::StackFrameSP frame_sp) {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ if (!os_plugin_object_sp)
+ return ValueObjectListSP();
+
+ StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
+ if (!generic)
+ return nullptr;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+
+ if (!implementor.IsAllocated())
+ return ValueObjectListSP();
+
+ PythonObject py_return(PyRefType::Owned,
+ (PyObject *)LLDBSwigPython_GetRecognizedArguments(
+ implementor.get(), frame_sp));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ if (py_return.get()) {
+ PythonList result_list(PyRefType::Borrowed, py_return.get());
+ ValueObjectListSP result = ValueObjectListSP(new ValueObjectList());
+ for (size_t i = 0; i < result_list.GetSize(); i++) {
+ PyObject *item = result_list.GetItemAtIndex(i).get();
+ lldb::SBValue *sb_value_ptr =
+ (lldb::SBValue *)LLDBSWIGPython_CastPyObjectToSBValue(item);
+ auto valobj_sp = LLDBSWIGPython_GetValueObjectSPFromSBValue(sb_value_ptr);
+ if (valobj_sp)
+ result->Append(valobj_sp);
+ }
+ return result;
+ }
+ return ValueObjectListSP();
+}
+
+StructuredData::GenericSP
+ScriptInterpreterPythonImpl::OSPlugin_CreatePluginObject(
+ const char *class_name, lldb::ProcessSP process_sp) {
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::GenericSP();
+
+ if (!process_sp)
+ return StructuredData::GenericSP();
+
+ void *ret_val;
+
+ {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+ ret_val = LLDBSWIGPythonCreateOSPlugin(
+ class_name, m_dictionary_name.c_str(), process_sp);
+ }
+
+ return StructuredData::GenericSP(new StructuredPythonObject(ret_val));
+}
+
+StructuredData::DictionarySP ScriptInterpreterPythonImpl::OSPlugin_RegisterInfo(
+ StructuredData::ObjectSP os_plugin_object_sp) {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_register_info";
+
+ if (!os_plugin_object_sp)
+ return StructuredData::DictionarySP();
+
+ StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
+ if (!generic)
+ return nullptr;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+
+ if (!implementor.IsAllocated())
+ return StructuredData::DictionarySP();
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return StructuredData::DictionarySP();
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ return StructuredData::DictionarySP();
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ if (py_return.get()) {
+ PythonDictionary result_dict(PyRefType::Borrowed, py_return.get());
+ return result_dict.CreateStructuredDictionary();
+ }
+ return StructuredData::DictionarySP();
+}
+
+StructuredData::ArraySP ScriptInterpreterPythonImpl::OSPlugin_ThreadsInfo(
+ StructuredData::ObjectSP os_plugin_object_sp) {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_thread_info";
+
+ if (!os_plugin_object_sp)
+ return StructuredData::ArraySP();
+
+ StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
+ if (!generic)
+ return nullptr;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+
+ if (!implementor.IsAllocated())
+ return StructuredData::ArraySP();
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return StructuredData::ArraySP();
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ return StructuredData::ArraySP();
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.get()) {
+ PythonList result_list(PyRefType::Borrowed, py_return.get());
+ return result_list.CreateStructuredArray();
+ }
+ return StructuredData::ArraySP();
+}
+
+// GetPythonValueFormatString provides a system independent type safe way to
+// convert a variable's type into a python value format. Python value formats
+// are defined in terms of builtin C types and could change from system to as
+// the underlying typedef for uint* types, size_t, off_t and other values
+// change.
+
+template <typename T> const char *GetPythonValueFormatString(T t);
+template <> const char *GetPythonValueFormatString(char *) { return "s"; }
+template <> const char *GetPythonValueFormatString(char) { return "b"; }
+template <> const char *GetPythonValueFormatString(unsigned char) {
+ return "B";
+}
+template <> const char *GetPythonValueFormatString(short) { return "h"; }
+template <> const char *GetPythonValueFormatString(unsigned short) {
+ return "H";
+}
+template <> const char *GetPythonValueFormatString(int) { return "i"; }
+template <> const char *GetPythonValueFormatString(unsigned int) { return "I"; }
+template <> const char *GetPythonValueFormatString(long) { return "l"; }
+template <> const char *GetPythonValueFormatString(unsigned long) {
+ return "k";
+}
+template <> const char *GetPythonValueFormatString(long long) { return "L"; }
+template <> const char *GetPythonValueFormatString(unsigned long long) {
+ return "K";
+}
+template <> const char *GetPythonValueFormatString(float t) { return "f"; }
+template <> const char *GetPythonValueFormatString(double t) { return "d"; }
+
+StructuredData::StringSP
+ScriptInterpreterPythonImpl::OSPlugin_RegisterContextData(
+ StructuredData::ObjectSP os_plugin_object_sp, lldb::tid_t tid) {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_register_data";
+ static char *param_format =
+ const_cast<char *>(GetPythonValueFormatString(tid));
+
+ if (!os_plugin_object_sp)
+ return StructuredData::StringSP();
+
+ StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
+ if (!generic)
+ return nullptr;
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+
+ if (!implementor.IsAllocated())
+ return StructuredData::StringSP();
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return StructuredData::StringSP();
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ return StructuredData::StringSP();
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, param_format, tid));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.get()) {
+ PythonBytes result(PyRefType::Borrowed, py_return.get());
+ return result.CreateStructuredString();
+ }
+ return StructuredData::StringSP();
+}
+
+StructuredData::DictionarySP ScriptInterpreterPythonImpl::OSPlugin_CreateThread(
+ StructuredData::ObjectSP os_plugin_object_sp, lldb::tid_t tid,
+ lldb::addr_t context) {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "create_thread";
+ std::string param_format;
+ param_format += GetPythonValueFormatString(tid);
+ param_format += GetPythonValueFormatString(context);
+
+ if (!os_plugin_object_sp)
+ return StructuredData::DictionarySP();
+
+ StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
+ if (!generic)
+ return nullptr;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+
+ if (!implementor.IsAllocated())
+ return StructuredData::DictionarySP();
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return StructuredData::DictionarySP();
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ return StructuredData::DictionarySP();
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name,
+ &param_format[0], tid, context));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.get()) {
+ PythonDictionary result_dict(PyRefType::Borrowed, py_return.get());
+ return result_dict.CreateStructuredDictionary();
+ }
+ return StructuredData::DictionarySP();
+}
+
+StructuredData::ObjectSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlan(
+ const char *class_name, lldb::ThreadPlanSP thread_plan_sp) {
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::ObjectSP();
+
+ if (!thread_plan_sp.get())
+ return StructuredData::ObjectSP();
+
+ Debugger &debugger = thread_plan_sp->GetTarget().GetDebugger();
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ ScriptInterpreterPythonImpl *python_interpreter =
+ static_cast<ScriptInterpreterPythonImpl *>(script_interpreter);
+
+ if (!script_interpreter)
+ return StructuredData::ObjectSP();
+
+ void *ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+
+ ret_val = LLDBSwigPythonCreateScriptedThreadPlan(
+ class_name, python_interpreter->m_dictionary_name.c_str(),
+ thread_plan_sp);
+ }
+
+ return StructuredData::ObjectSP(new StructuredPythonObject(ret_val));
+}
+
+bool ScriptInterpreterPythonImpl::ScriptedThreadPlanExplainsStop(
+ StructuredData::ObjectSP implementor_sp, Event *event, bool &script_error) {
+ bool explains_stop = true;
+ StructuredData::Generic *generic = nullptr;
+ if (implementor_sp)
+ generic = implementor_sp->GetAsGeneric();
+ if (generic) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ explains_stop = LLDBSWIGPythonCallThreadPlan(
+ generic->GetValue(), "explains_stop", event, script_error);
+ if (script_error)
+ return true;
+ }
+ return explains_stop;
+}
+
+bool ScriptInterpreterPythonImpl::ScriptedThreadPlanShouldStop(
+ StructuredData::ObjectSP implementor_sp, Event *event, bool &script_error) {
+ bool should_stop = true;
+ StructuredData::Generic *generic = nullptr;
+ if (implementor_sp)
+ generic = implementor_sp->GetAsGeneric();
+ if (generic) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ should_stop = LLDBSWIGPythonCallThreadPlan(
+ generic->GetValue(), "should_stop", event, script_error);
+ if (script_error)
+ return true;
+ }
+ return should_stop;
+}
+
+bool ScriptInterpreterPythonImpl::ScriptedThreadPlanIsStale(
+ StructuredData::ObjectSP implementor_sp, bool &script_error) {
+ bool is_stale = true;
+ StructuredData::Generic *generic = nullptr;
+ if (implementor_sp)
+ generic = implementor_sp->GetAsGeneric();
+ if (generic) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ is_stale = LLDBSWIGPythonCallThreadPlan(generic->GetValue(), "is_stale",
+ nullptr, script_error);
+ if (script_error)
+ return true;
+ }
+ return is_stale;
+}
+
+lldb::StateType ScriptInterpreterPythonImpl::ScriptedThreadPlanGetRunState(
+ StructuredData::ObjectSP implementor_sp, bool &script_error) {
+ bool should_step = false;
+ StructuredData::Generic *generic = nullptr;
+ if (implementor_sp)
+ generic = implementor_sp->GetAsGeneric();
+ if (generic) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ should_step = LLDBSWIGPythonCallThreadPlan(
+ generic->GetValue(), "should_step", nullptr, script_error);
+ if (script_error)
+ should_step = true;
+ }
+ if (should_step)
+ return lldb::eStateStepping;
+ else
+ return lldb::eStateRunning;
+}
+
+StructuredData::GenericSP
+ScriptInterpreterPythonImpl::CreateScriptedBreakpointResolver(
+ const char *class_name, StructuredDataImpl *args_data,
+ lldb::BreakpointSP &bkpt_sp) {
+
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::GenericSP();
+
+ if (!bkpt_sp.get())
+ return StructuredData::GenericSP();
+
+ Debugger &debugger = bkpt_sp->GetTarget().GetDebugger();
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ ScriptInterpreterPythonImpl *python_interpreter =
+ static_cast<ScriptInterpreterPythonImpl *>(script_interpreter);
+
+ if (!script_interpreter)
+ return StructuredData::GenericSP();
+
+ void *ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+
+ ret_val = LLDBSwigPythonCreateScriptedBreakpointResolver(
+ class_name, python_interpreter->m_dictionary_name.c_str(), args_data,
+ bkpt_sp);
+ }
+
+ return StructuredData::GenericSP(new StructuredPythonObject(ret_val));
+}
+
+bool ScriptInterpreterPythonImpl::ScriptedBreakpointResolverSearchCallback(
+ StructuredData::GenericSP implementor_sp, SymbolContext *sym_ctx) {
+ bool should_continue = false;
+
+ if (implementor_sp) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ should_continue = LLDBSwigPythonCallBreakpointResolver(
+ implementor_sp->GetValue(), "__callback__", sym_ctx);
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ }
+ return should_continue;
+}
+
+lldb::SearchDepth
+ScriptInterpreterPythonImpl::ScriptedBreakpointResolverSearchDepth(
+ StructuredData::GenericSP implementor_sp) {
+ int depth_as_int = lldb::eSearchDepthModule;
+ if (implementor_sp) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ depth_as_int = LLDBSwigPythonCallBreakpointResolver(
+ implementor_sp->GetValue(), "__get_depth__", nullptr);
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ }
+ if (depth_as_int == lldb::eSearchDepthInvalid)
+ return lldb::eSearchDepthModule;
+
+ if (depth_as_int <= lldb::kLastSearchDepthKind)
+ return (lldb::SearchDepth)depth_as_int;
+ else
+ return lldb::eSearchDepthModule;
+}
+
+StructuredData::ObjectSP
+ScriptInterpreterPythonImpl::LoadPluginModule(const FileSpec &file_spec,
+ lldb_private::Status &error) {
+ if (!FileSystem::Instance().Exists(file_spec)) {
+ error.SetErrorString("no such file");
+ return StructuredData::ObjectSP();
+ }
+
+ StructuredData::ObjectSP module_sp;
+
+ if (LoadScriptingModule(file_spec.GetPath().c_str(), true, true, error,
+ &module_sp))
+ return module_sp;
+
+ return StructuredData::ObjectSP();
+}
+
+StructuredData::DictionarySP ScriptInterpreterPythonImpl::GetDynamicSettings(
+ StructuredData::ObjectSP plugin_module_sp, Target *target,
+ const char *setting_name, lldb_private::Status &error) {
+ if (!plugin_module_sp || !target || !setting_name || !setting_name[0])
+ return StructuredData::DictionarySP();
+ StructuredData::Generic *generic = plugin_module_sp->GetAsGeneric();
+ if (!generic)
+ return StructuredData::DictionarySP();
+
+ PythonObject reply_pyobj;
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ TargetSP target_sp(target->shared_from_this());
+ reply_pyobj.Reset(PyRefType::Owned,
+ (PyObject *)LLDBSWIGPython_GetDynamicSetting(
+ generic->GetValue(), setting_name, target_sp));
+
+ PythonDictionary py_dict(PyRefType::Borrowed, reply_pyobj.get());
+ return py_dict.CreateStructuredDictionary();
+}
+
+StructuredData::ObjectSP
+ScriptInterpreterPythonImpl::CreateSyntheticScriptedProvider(
+ const char *class_name, lldb::ValueObjectSP valobj) {
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::ObjectSP();
+
+ if (!valobj.get())
+ return StructuredData::ObjectSP();
+
+ ExecutionContext exe_ctx(valobj->GetExecutionContextRef());
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target)
+ return StructuredData::ObjectSP();
+
+ Debugger &debugger = target->GetDebugger();
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ ScriptInterpreterPythonImpl *python_interpreter =
+ (ScriptInterpreterPythonImpl *)script_interpreter;
+
+ if (!script_interpreter)
+ return StructuredData::ObjectSP();
+
+ void *ret_val = nullptr;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSwigPythonCreateSyntheticProvider(
+ class_name, python_interpreter->m_dictionary_name.c_str(), valobj);
+ }
+
+ return StructuredData::ObjectSP(new StructuredPythonObject(ret_val));
+}
+
+StructuredData::GenericSP
+ScriptInterpreterPythonImpl::CreateScriptCommandObject(const char *class_name) {
+ DebuggerSP debugger_sp(m_debugger.shared_from_this());
+
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::GenericSP();
+
+ if (!debugger_sp.get())
+ return StructuredData::GenericSP();
+
+ void *ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSwigPythonCreateCommandObject(
+ class_name, m_dictionary_name.c_str(), debugger_sp);
+ }
+
+ return StructuredData::GenericSP(new StructuredPythonObject(ret_val));
+}
+
+bool ScriptInterpreterPythonImpl::GenerateTypeScriptFunction(
+ const char *oneliner, std::string &output, const void *name_token) {
+ StringList input;
+ input.SplitIntoLines(oneliner, strlen(oneliner));
+ return GenerateTypeScriptFunction(input, output, name_token);
+}
+
+bool ScriptInterpreterPythonImpl::GenerateTypeSynthClass(
+ const char *oneliner, std::string &output, const void *name_token) {
+ StringList input;
+ input.SplitIntoLines(oneliner, strlen(oneliner));
+ return GenerateTypeSynthClass(input, output, name_token);
+}
+
+Status ScriptInterpreterPythonImpl::GenerateBreakpointCommandCallbackData(
+ StringList &user_input, std::string &output) {
+ static uint32_t num_created_functions = 0;
+ user_input.RemoveBlankLines();
+ StreamString sstr;
+ Status error;
+ if (user_input.GetSize() == 0) {
+ error.SetErrorString("No input data.");
+ return error;
+ }
+
+ std::string auto_generated_function_name(GenerateUniqueName(
+ "lldb_autogen_python_bp_callback_func_", num_created_functions));
+ sstr.Printf("def %s (frame, bp_loc, internal_dict):",
+ auto_generated_function_name.c_str());
+
+ error = GenerateFunction(sstr.GetData(), user_input);
+ if (!error.Success())
+ return error;
+
+ // Store the name of the auto-generated function to be called.
+ output.assign(auto_generated_function_name);
+ return error;
+}
+
+bool ScriptInterpreterPythonImpl::GenerateWatchpointCommandCallbackData(
+ StringList &user_input, std::string &output) {
+ static uint32_t num_created_functions = 0;
+ user_input.RemoveBlankLines();
+ StreamString sstr;
+
+ if (user_input.GetSize() == 0)
+ return false;
+
+ std::string auto_generated_function_name(GenerateUniqueName(
+ "lldb_autogen_python_wp_callback_func_", num_created_functions));
+ sstr.Printf("def %s (frame, wp, internal_dict):",
+ auto_generated_function_name.c_str());
+
+ if (!GenerateFunction(sstr.GetData(), user_input).Success())
+ return false;
+
+ // Store the name of the auto-generated function to be called.
+ output.assign(auto_generated_function_name);
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::GetScriptedSummary(
+ const char *python_function_name, lldb::ValueObjectSP valobj,
+ StructuredData::ObjectSP &callee_wrapper_sp,
+ const TypeSummaryOptions &options, std::string &retval) {
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ if (!valobj.get()) {
+ retval.assign("<no object>");
+ return false;
+ }
+
+ void *old_callee = nullptr;
+ StructuredData::Generic *generic = nullptr;
+ if (callee_wrapper_sp) {
+ generic = callee_wrapper_sp->GetAsGeneric();
+ if (generic)
+ old_callee = generic->GetValue();
+ }
+ void *new_callee = old_callee;
+
+ bool ret_val;
+ if (python_function_name && *python_function_name) {
+ {
+ Locker py_lock(this, Locker::AcquireLock | Locker::InitSession |
+ Locker::NoSTDIN);
+ {
+ TypeSummaryOptionsSP options_sp(new TypeSummaryOptions(options));
+
+ static Timer::Category func_cat("LLDBSwigPythonCallTypeScript");
+ Timer scoped_timer(func_cat, "LLDBSwigPythonCallTypeScript");
+ ret_val = LLDBSwigPythonCallTypeScript(
+ python_function_name, GetSessionDictionary().get(), valobj,
+ &new_callee, options_sp, retval);
+ }
+ }
+ } else {
+ retval.assign("<no function name>");
+ return false;
+ }
+
+ if (new_callee && old_callee != new_callee)
+ callee_wrapper_sp = std::make_shared<StructuredPythonObject>(new_callee);
+
+ return ret_val;
+}
+
+void ScriptInterpreterPythonImpl::Clear() {
+ // Release any global variables that might have strong references to
+ // LLDB objects when clearing the python script interpreter.
+ Locker locker(this, Locker::AcquireLock, Locker::FreeAcquiredLock);
+
+ // This may be called as part of Py_Finalize. In that case the modules are
+ // destroyed in random order and we can't guarantee that we can access these.
+ if (Py_IsInitialized())
+ PyRun_SimpleString("lldb.debugger = None; lldb.target = None; lldb.process "
+ "= None; lldb.thread = None; lldb.frame = None");
+}
+
+bool ScriptInterpreterPythonImpl::BreakpointCallbackFunction(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ CommandDataPython *bp_option_data = (CommandDataPython *)baton;
+ const char *python_function_name = bp_option_data->script_source.c_str();
+
+ if (!context)
+ return true;
+
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target)
+ return true;
+
+ Debugger &debugger = target->GetDebugger();
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ ScriptInterpreterPythonImpl *python_interpreter =
+ (ScriptInterpreterPythonImpl *)script_interpreter;
+
+ if (!script_interpreter)
+ return true;
+
+ if (python_function_name && python_function_name[0]) {
+ const StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP());
+ BreakpointSP breakpoint_sp = target->GetBreakpointByID(break_id);
+ if (breakpoint_sp) {
+ const BreakpointLocationSP bp_loc_sp(
+ breakpoint_sp->FindLocationByID(break_loc_id));
+
+ if (stop_frame_sp && bp_loc_sp) {
+ bool ret_val = true;
+ {
+ Locker py_lock(python_interpreter, Locker::AcquireLock |
+ Locker::InitSession |
+ Locker::NoSTDIN);
+ ret_val = LLDBSwigPythonBreakpointCallbackFunction(
+ python_function_name,
+ python_interpreter->m_dictionary_name.c_str(), stop_frame_sp,
+ bp_loc_sp);
+ }
+ return ret_val;
+ }
+ }
+ }
+ // We currently always true so we stop in case anything goes wrong when
+ // trying to call the script function
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::WatchpointCallbackFunction(
+ void *baton, StoppointCallbackContext *context, user_id_t watch_id) {
+ WatchpointOptions::CommandData *wp_option_data =
+ (WatchpointOptions::CommandData *)baton;
+ const char *python_function_name = wp_option_data->script_source.c_str();
+
+ if (!context)
+ return true;
+
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target)
+ return true;
+
+ Debugger &debugger = target->GetDebugger();
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ ScriptInterpreterPythonImpl *python_interpreter =
+ (ScriptInterpreterPythonImpl *)script_interpreter;
+
+ if (!script_interpreter)
+ return true;
+
+ if (python_function_name && python_function_name[0]) {
+ const StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP());
+ WatchpointSP wp_sp = target->GetWatchpointList().FindByID(watch_id);
+ if (wp_sp) {
+ if (stop_frame_sp && wp_sp) {
+ bool ret_val = true;
+ {
+ Locker py_lock(python_interpreter, Locker::AcquireLock |
+ Locker::InitSession |
+ Locker::NoSTDIN);
+ ret_val = LLDBSwigPythonWatchpointCallbackFunction(
+ python_function_name,
+ python_interpreter->m_dictionary_name.c_str(), stop_frame_sp,
+ wp_sp);
+ }
+ return ret_val;
+ }
+ }
+ }
+ // We currently always true so we stop in case anything goes wrong when
+ // trying to call the script function
+ return true;
+}
+
+size_t ScriptInterpreterPythonImpl::CalculateNumChildren(
+ const StructuredData::ObjectSP &implementor_sp, uint32_t max) {
+ if (!implementor_sp)
+ return 0;
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return 0;
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return 0;
+
+ size_t ret_val = 0;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSwigPython_CalculateNumChildren(implementor, max);
+ }
+
+ return ret_val;
+}
+
+lldb::ValueObjectSP ScriptInterpreterPythonImpl::GetChildAtIndex(
+ const StructuredData::ObjectSP &implementor_sp, uint32_t idx) {
+ if (!implementor_sp)
+ return lldb::ValueObjectSP();
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return lldb::ValueObjectSP();
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return lldb::ValueObjectSP();
+
+ lldb::ValueObjectSP ret_val;
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ void *child_ptr = LLDBSwigPython_GetChildAtIndex(implementor, idx);
+ if (child_ptr != nullptr && child_ptr != Py_None) {
+ lldb::SBValue *sb_value_ptr =
+ (lldb::SBValue *)LLDBSWIGPython_CastPyObjectToSBValue(child_ptr);
+ if (sb_value_ptr == nullptr)
+ Py_XDECREF(child_ptr);
+ else
+ ret_val = LLDBSWIGPython_GetValueObjectSPFromSBValue(sb_value_ptr);
+ } else {
+ Py_XDECREF(child_ptr);
+ }
+ }
+
+ return ret_val;
+}
+
+int ScriptInterpreterPythonImpl::GetIndexOfChildWithName(
+ const StructuredData::ObjectSP &implementor_sp, const char *child_name) {
+ if (!implementor_sp)
+ return UINT32_MAX;
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return UINT32_MAX;
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return UINT32_MAX;
+
+ int ret_val = UINT32_MAX;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSwigPython_GetIndexOfChildWithName(implementor, child_name);
+ }
+
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::UpdateSynthProviderInstance(
+ const StructuredData::ObjectSP &implementor_sp) {
+ bool ret_val = false;
+
+ if (!implementor_sp)
+ return ret_val;
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return ret_val;
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSwigPython_UpdateSynthProviderInstance(implementor);
+ }
+
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::MightHaveChildrenSynthProviderInstance(
+ const StructuredData::ObjectSP &implementor_sp) {
+ bool ret_val = false;
+
+ if (!implementor_sp)
+ return ret_val;
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return ret_val;
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val =
+ LLDBSwigPython_MightHaveChildrenSynthProviderInstance(implementor);
+ }
+
+ return ret_val;
+}
+
+lldb::ValueObjectSP ScriptInterpreterPythonImpl::GetSyntheticValue(
+ const StructuredData::ObjectSP &implementor_sp) {
+ lldb::ValueObjectSP ret_val(nullptr);
+
+ if (!implementor_sp)
+ return ret_val;
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return ret_val;
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ void *child_ptr = LLDBSwigPython_GetValueSynthProviderInstance(implementor);
+ if (child_ptr != nullptr && child_ptr != Py_None) {
+ lldb::SBValue *sb_value_ptr =
+ (lldb::SBValue *)LLDBSWIGPython_CastPyObjectToSBValue(child_ptr);
+ if (sb_value_ptr == nullptr)
+ Py_XDECREF(child_ptr);
+ else
+ ret_val = LLDBSWIGPython_GetValueObjectSPFromSBValue(sb_value_ptr);
+ } else {
+ Py_XDECREF(child_ptr);
+ }
+ }
+
+ return ret_val;
+}
+
+ConstString ScriptInterpreterPythonImpl::GetSyntheticTypeName(
+ const StructuredData::ObjectSP &implementor_sp) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+
+ static char callee_name[] = "get_type_name";
+
+ ConstString ret_val;
+ bool got_string = false;
+ std::string buffer;
+
+ if (!implementor_sp)
+ return ret_val;
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return ret_val;
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+ if (!implementor.IsAllocated())
+ return ret_val;
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return ret_val;
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ return ret_val;
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.IsAllocated() && PythonString::Check(py_return.get())) {
+ PythonString py_string(PyRefType::Borrowed, py_return.get());
+ llvm::StringRef return_data(py_string.GetString());
+ if (!return_data.empty()) {
+ buffer.assign(return_data.data(), return_data.size());
+ got_string = true;
+ }
+ }
+
+ if (got_string)
+ ret_val.SetCStringWithLength(buffer.c_str(), buffer.size());
+
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
+ const char *impl_function, Process *process, std::string &output,
+ Status &error) {
+ bool ret_val;
+ if (!process) {
+ error.SetErrorString("no process");
+ return false;
+ }
+ if (!impl_function || !impl_function[0]) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ {
+ ProcessSP process_sp(process->shared_from_this());
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSWIGPythonRunScriptKeywordProcess(
+ impl_function, m_dictionary_name.c_str(), process_sp, output);
+ if (!ret_val)
+ error.SetErrorString("python script evaluation failed");
+ }
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
+ const char *impl_function, Thread *thread, std::string &output,
+ Status &error) {
+ bool ret_val;
+ if (!thread) {
+ error.SetErrorString("no thread");
+ return false;
+ }
+ if (!impl_function || !impl_function[0]) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ {
+ ThreadSP thread_sp(thread->shared_from_this());
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSWIGPythonRunScriptKeywordThread(
+ impl_function, m_dictionary_name.c_str(), thread_sp, output);
+ if (!ret_val)
+ error.SetErrorString("python script evaluation failed");
+ }
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
+ const char *impl_function, Target *target, std::string &output,
+ Status &error) {
+ bool ret_val;
+ if (!target) {
+ error.SetErrorString("no thread");
+ return false;
+ }
+ if (!impl_function || !impl_function[0]) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ {
+ TargetSP target_sp(target->shared_from_this());
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSWIGPythonRunScriptKeywordTarget(
+ impl_function, m_dictionary_name.c_str(), target_sp, output);
+ if (!ret_val)
+ error.SetErrorString("python script evaluation failed");
+ }
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
+ const char *impl_function, StackFrame *frame, std::string &output,
+ Status &error) {
+ bool ret_val;
+ if (!frame) {
+ error.SetErrorString("no frame");
+ return false;
+ }
+ if (!impl_function || !impl_function[0]) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ {
+ StackFrameSP frame_sp(frame->shared_from_this());
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSWIGPythonRunScriptKeywordFrame(
+ impl_function, m_dictionary_name.c_str(), frame_sp, output);
+ if (!ret_val)
+ error.SetErrorString("python script evaluation failed");
+ }
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
+ const char *impl_function, ValueObject *value, std::string &output,
+ Status &error) {
+ bool ret_val;
+ if (!value) {
+ error.SetErrorString("no value");
+ return false;
+ }
+ if (!impl_function || !impl_function[0]) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ {
+ ValueObjectSP value_sp(value->GetSP());
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSWIGPythonRunScriptKeywordValue(
+ impl_function, m_dictionary_name.c_str(), value_sp, output);
+ if (!ret_val)
+ error.SetErrorString("python script evaluation failed");
+ }
+ return ret_val;
+}
+
+uint64_t replace_all(std::string &str, const std::string &oldStr,
+ const std::string &newStr) {
+ size_t pos = 0;
+ uint64_t matches = 0;
+ while ((pos = str.find(oldStr, pos)) != std::string::npos) {
+ matches++;
+ str.replace(pos, oldStr.length(), newStr);
+ pos += newStr.length();
+ }
+ return matches;
+}
+
+bool ScriptInterpreterPythonImpl::LoadScriptingModule(
+ const char *pathname, bool can_reload, bool init_session,
+ lldb_private::Status &error, StructuredData::ObjectSP *module_sp) {
+ if (!pathname || !pathname[0]) {
+ error.SetErrorString("invalid pathname");
+ return false;
+ }
+
+ lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this();
+
+ {
+ FileSpec target_file(pathname);
+ FileSystem::Instance().Resolve(target_file);
+ std::string basename(target_file.GetFilename().GetCString());
+
+ StreamString command_stream;
+
+ // Before executing Python code, lock the GIL.
+ Locker py_lock(this,
+ Locker::AcquireLock |
+ (init_session ? Locker::InitSession : 0) |
+ Locker::NoSTDIN,
+ Locker::FreeAcquiredLock |
+ (init_session ? Locker::TearDownSession : 0));
+ namespace fs = llvm::sys::fs;
+ fs::file_status st;
+ std::error_code ec = status(target_file.GetPath(), st);
+
+ if (ec || st.type() == fs::file_type::status_error ||
+ st.type() == fs::file_type::type_unknown ||
+ st.type() == fs::file_type::file_not_found) {
+ // if not a valid file of any sort, check if it might be a filename still
+ // dot can't be used but / and \ can, and if either is found, reject
+ if (strchr(pathname, '\\') || strchr(pathname, '/')) {
+ error.SetErrorString("invalid pathname");
+ return false;
+ }
+ basename = pathname; // not a filename, probably a package of some sort,
+ // let it go through
+ } else if (is_directory(st) || is_regular_file(st)) {
+ if (target_file.GetDirectory().IsEmpty()) {
+ error.SetErrorString("invalid directory name");
+ return false;
+ }
+
+ std::string directory = target_file.GetDirectory().GetCString();
+ replace_all(directory, "\\", "\\\\");
+ replace_all(directory, "'", "\\'");
+
+ // now make sure that Python has "directory" in the search path
+ StreamString command_stream;
+ command_stream.Printf("if not (sys.path.__contains__('%s')):\n "
+ "sys.path.insert(1,'%s');\n\n",
+ directory.c_str(), directory.c_str());
+ bool syspath_retval =
+ ExecuteMultipleLines(command_stream.GetData(),
+ ScriptInterpreter::ExecuteScriptOptions()
+ .SetEnableIO(false)
+ .SetSetLLDBGlobals(false))
+ .Success();
+ if (!syspath_retval) {
+ error.SetErrorString("Python sys.path handling failed");
+ return false;
+ }
+
+ // strip .py or .pyc extension
+ ConstString extension = target_file.GetFileNameExtension();
+ if (extension) {
+ if (llvm::StringRef(extension.GetCString()) == ".py")
+ basename.resize(basename.length() - 3);
+ else if (llvm::StringRef(extension.GetCString()) == ".pyc")
+ basename.resize(basename.length() - 4);
+ }
+ } else {
+ error.SetErrorString("no known way to import this module specification");
+ return false;
+ }
+
+ // check if the module is already import-ed
+ command_stream.Clear();
+ command_stream.Printf("sys.modules.__contains__('%s')", basename.c_str());
+ bool does_contain = false;
+ // this call will succeed if the module was ever imported in any Debugger
+ // in the lifetime of the process in which this LLDB framework is living
+ bool was_imported_globally =
+ (ExecuteOneLineWithReturn(
+ command_stream.GetData(),
+ ScriptInterpreterPythonImpl::eScriptReturnTypeBool, &does_contain,
+ ScriptInterpreter::ExecuteScriptOptions()
+ .SetEnableIO(false)
+ .SetSetLLDBGlobals(false)) &&
+ does_contain);
+ // this call will fail if the module was not imported in this Debugger
+ // before
+ command_stream.Clear();
+ command_stream.Printf("sys.getrefcount(%s)", basename.c_str());
+ bool was_imported_locally = GetSessionDictionary()
+ .GetItemForKey(PythonString(basename))
+ .IsAllocated();
+
+ bool was_imported = (was_imported_globally || was_imported_locally);
+
+ if (was_imported && !can_reload) {
+ error.SetErrorString("module already imported");
+ return false;
+ }
+
+ // now actually do the import
+ command_stream.Clear();
+
+ if (was_imported) {
+ if (!was_imported_locally)
+ command_stream.Printf("import %s ; reload_module(%s)", basename.c_str(),
+ basename.c_str());
+ else
+ command_stream.Printf("reload_module(%s)", basename.c_str());
+ } else
+ command_stream.Printf("import %s", basename.c_str());
+
+ error = ExecuteMultipleLines(command_stream.GetData(),
+ ScriptInterpreter::ExecuteScriptOptions()
+ .SetEnableIO(false)
+ .SetSetLLDBGlobals(false));
+ if (error.Fail())
+ return false;
+
+ // if we are here, everything worked
+ // call __lldb_init_module(debugger,dict)
+ if (!LLDBSwigPythonCallModuleInit(basename.c_str(),
+ m_dictionary_name.c_str(), debugger_sp)) {
+ error.SetErrorString("calling __lldb_init_module failed");
+ return false;
+ }
+
+ if (module_sp) {
+ // everything went just great, now set the module object
+ command_stream.Clear();
+ command_stream.Printf("%s", basename.c_str());
+ void *module_pyobj = nullptr;
+ if (ExecuteOneLineWithReturn(
+ command_stream.GetData(),
+ ScriptInterpreter::eScriptReturnTypeOpaqueObject,
+ &module_pyobj) &&
+ module_pyobj)
+ *module_sp = std::make_shared<StructuredPythonObject>(module_pyobj);
+ }
+
+ return true;
+ }
+}
+
+bool ScriptInterpreterPythonImpl::IsReservedWord(const char *word) {
+ if (!word || !word[0])
+ return false;
+
+ llvm::StringRef word_sr(word);
+
+ // filter out a few characters that would just confuse us and that are
+ // clearly not keyword material anyway
+ if (word_sr.find('"') != llvm::StringRef::npos ||
+ word_sr.find('\'') != llvm::StringRef::npos)
+ return false;
+
+ StreamString command_stream;
+ command_stream.Printf("keyword.iskeyword('%s')", word);
+ bool result;
+ ExecuteScriptOptions options;
+ options.SetEnableIO(false);
+ options.SetMaskoutErrors(true);
+ options.SetSetLLDBGlobals(false);
+ if (ExecuteOneLineWithReturn(command_stream.GetData(),
+ ScriptInterpreter::eScriptReturnTypeBool,
+ &result, options))
+ return result;
+ return false;
+}
+
+ScriptInterpreterPythonImpl::SynchronicityHandler::SynchronicityHandler(
+ lldb::DebuggerSP debugger_sp, ScriptedCommandSynchronicity synchro)
+ : m_debugger_sp(debugger_sp), m_synch_wanted(synchro),
+ m_old_asynch(debugger_sp->GetAsyncExecution()) {
+ if (m_synch_wanted == eScriptedCommandSynchronicitySynchronous)
+ m_debugger_sp->SetAsyncExecution(false);
+ else if (m_synch_wanted == eScriptedCommandSynchronicityAsynchronous)
+ m_debugger_sp->SetAsyncExecution(true);
+}
+
+ScriptInterpreterPythonImpl::SynchronicityHandler::~SynchronicityHandler() {
+ if (m_synch_wanted != eScriptedCommandSynchronicityCurrentValue)
+ m_debugger_sp->SetAsyncExecution(m_old_asynch);
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptBasedCommand(
+ const char *impl_function, llvm::StringRef args,
+ ScriptedCommandSynchronicity synchronicity,
+ lldb_private::CommandReturnObject &cmd_retobj, Status &error,
+ const lldb_private::ExecutionContext &exe_ctx) {
+ if (!impl_function) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this();
+ lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx));
+
+ if (!debugger_sp.get()) {
+ error.SetErrorString("invalid Debugger pointer");
+ return false;
+ }
+
+ bool ret_val = false;
+
+ std::string err_msg;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession |
+ (cmd_retobj.GetInteractive() ? 0 : Locker::NoSTDIN),
+ Locker::FreeLock | Locker::TearDownSession);
+
+ SynchronicityHandler synch_handler(debugger_sp, synchronicity);
+
+ std::string args_str = args.str();
+ ret_val = LLDBSwigPythonCallCommand(
+ impl_function, m_dictionary_name.c_str(), debugger_sp, args_str.c_str(),
+ cmd_retobj, exe_ctx_ref_sp);
+ }
+
+ if (!ret_val)
+ error.SetErrorString("unable to execute script function");
+ else
+ error.Clear();
+
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptBasedCommand(
+ StructuredData::GenericSP impl_obj_sp, llvm::StringRef args,
+ ScriptedCommandSynchronicity synchronicity,
+ lldb_private::CommandReturnObject &cmd_retobj, Status &error,
+ const lldb_private::ExecutionContext &exe_ctx) {
+ if (!impl_obj_sp || !impl_obj_sp->IsValid()) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this();
+ lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx));
+
+ if (!debugger_sp.get()) {
+ error.SetErrorString("invalid Debugger pointer");
+ return false;
+ }
+
+ bool ret_val = false;
+
+ std::string err_msg;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession |
+ (cmd_retobj.GetInteractive() ? 0 : Locker::NoSTDIN),
+ Locker::FreeLock | Locker::TearDownSession);
+
+ SynchronicityHandler synch_handler(debugger_sp, synchronicity);
+
+ std::string args_str = args.str();
+ ret_val = LLDBSwigPythonCallCommandObject(impl_obj_sp->GetValue(),
+ debugger_sp, args_str.c_str(),
+ cmd_retobj, exe_ctx_ref_sp);
+ }
+
+ if (!ret_val)
+ error.SetErrorString("unable to execute script function");
+ else
+ error.Clear();
+
+ return ret_val;
+}
+
+// in Python, a special attribute __doc__ contains the docstring for an object
+// (function, method, class, ...) if any is defined Otherwise, the attribute's
+// value is None
+bool ScriptInterpreterPythonImpl::GetDocumentationForItem(const char *item,
+ std::string &dest) {
+ dest.clear();
+ if (!item || !*item)
+ return false;
+ std::string command(item);
+ command += ".__doc__";
+
+ char *result_ptr = nullptr; // Python is going to point this to valid data if
+ // ExecuteOneLineWithReturn returns successfully
+
+ if (ExecuteOneLineWithReturn(
+ command.c_str(), ScriptInterpreter::eScriptReturnTypeCharStrOrNone,
+ &result_ptr,
+ ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false))) {
+ if (result_ptr)
+ dest.assign(result_ptr);
+ return true;
+ } else {
+ StreamString str_stream;
+ str_stream.Printf(
+ "Function %s was not found. Containing module might be missing.", item);
+ dest = str_stream.GetString();
+ return false;
+ }
+}
+
+bool ScriptInterpreterPythonImpl::GetShortHelpForCommandObject(
+ StructuredData::GenericSP cmd_obj_sp, std::string &dest) {
+ bool got_string = false;
+ dest.clear();
+
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_short_help";
+
+ if (!cmd_obj_sp)
+ return false;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)cmd_obj_sp->GetValue());
+
+ if (!implementor.IsAllocated())
+ return false;
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return false;
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ return false;
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.IsAllocated() && PythonString::Check(py_return.get())) {
+ PythonString py_string(PyRefType::Borrowed, py_return.get());
+ llvm::StringRef return_data(py_string.GetString());
+ dest.assign(return_data.data(), return_data.size());
+ got_string = true;
+ }
+ return got_string;
+}
+
+uint32_t ScriptInterpreterPythonImpl::GetFlagsForCommandObject(
+ StructuredData::GenericSP cmd_obj_sp) {
+ uint32_t result = 0;
+
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_flags";
+
+ if (!cmd_obj_sp)
+ return result;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)cmd_obj_sp->GetValue());
+
+ if (!implementor.IsAllocated())
+ return result;
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return result;
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ return result;
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.IsAllocated() && PythonInteger::Check(py_return.get())) {
+ PythonInteger int_value(PyRefType::Borrowed, py_return.get());
+ result = int_value.GetInteger();
+ }
+
+ return result;
+}
+
+bool ScriptInterpreterPythonImpl::GetLongHelpForCommandObject(
+ StructuredData::GenericSP cmd_obj_sp, std::string &dest) {
+ bool got_string = false;
+ dest.clear();
+
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_long_help";
+
+ if (!cmd_obj_sp)
+ return false;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)cmd_obj_sp->GetValue());
+
+ if (!implementor.IsAllocated())
+ return false;
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return false;
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ return false;
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.IsAllocated() && PythonString::Check(py_return.get())) {
+ PythonString str(PyRefType::Borrowed, py_return.get());
+ llvm::StringRef str_data(str.GetString());
+ dest.assign(str_data.data(), str_data.size());
+ got_string = true;
+ }
+
+ return got_string;
+}
+
+std::unique_ptr<ScriptInterpreterLocker>
+ScriptInterpreterPythonImpl::AcquireInterpreterLock() {
+ std::unique_ptr<ScriptInterpreterLocker> py_lock(new Locker(
+ this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN,
+ Locker::FreeLock | Locker::TearDownSession));
+ return py_lock;
+}
+
+void ScriptInterpreterPythonImpl::InitializePrivate() {
+ if (g_initialized)
+ return;
+
+ g_initialized = true;
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ // RAII-based initialization which correctly handles multiple-initialization,
+ // version- specific differences among Python 2 and Python 3, and saving and
+ // restoring various other pieces of state that can get mucked with during
+ // initialization.
+ InitializePythonRAII initialize_guard;
+
+ LLDBSwigPyInit();
+
+ // Update the path python uses to search for modules to include the current
+ // directory.
+
+ PyRun_SimpleString("import sys");
+ AddToSysPath(AddLocation::End, ".");
+
+ // Don't denormalize paths when calling file_spec.GetPath(). On platforms
+ // that use a backslash as the path separator, this will result in executing
+ // python code containing paths with unescaped backslashes. But Python also
+ // accepts forward slashes, so to make life easier we just use that.
+ if (FileSpec file_spec = GetPythonDir())
+ AddToSysPath(AddLocation::Beginning, file_spec.GetPath(false));
+ if (FileSpec file_spec = HostInfo::GetShlibDir())
+ AddToSysPath(AddLocation::Beginning, file_spec.GetPath(false));
+
+ PyRun_SimpleString("sys.dont_write_bytecode = 1; import "
+ "lldb.embedded_interpreter; from "
+ "lldb.embedded_interpreter import run_python_interpreter; "
+ "from lldb.embedded_interpreter import run_one_line");
+}
+
+void ScriptInterpreterPythonImpl::AddToSysPath(AddLocation location,
+ std::string path) {
+ std::string path_copy;
+
+ std::string statement;
+ if (location == AddLocation::Beginning) {
+ statement.assign("sys.path.insert(0,\"");
+ statement.append(path);
+ statement.append("\")");
+ } else {
+ statement.assign("sys.path.append(\"");
+ statement.append(path);
+ statement.append("\")");
+ }
+ PyRun_SimpleString(statement.c_str());
+}
+
+// We are intentionally NOT calling Py_Finalize here (this would be the logical
+// place to call it). Calling Py_Finalize here causes test suite runs to seg
+// fault: The test suite runs in Python. It registers SBDebugger::Terminate to
+// be called 'at_exit'. When the test suite Python harness finishes up, it
+// calls Py_Finalize, which calls all the 'at_exit' registered functions.
+// SBDebugger::Terminate calls Debugger::Terminate, which calls lldb::Terminate,
+// which calls ScriptInterpreter::Terminate, which calls
+// ScriptInterpreterPythonImpl::Terminate. So if we call Py_Finalize here, we
+// end up with Py_Finalize being called from within Py_Finalize, which results
+// in a seg fault. Since this function only gets called when lldb is shutting
+// down and going away anyway, the fact that we don't actually call Py_Finalize
+// should not cause any problems (everything should shut down/go away anyway
+// when the process exits).
+//
+// void ScriptInterpreterPythonImpl::Terminate() { Py_Finalize (); }
+
+#endif // LLDB_DISABLE_PYTHON
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
new file mode 100644
index 000000000000..24941ec77452
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
@@ -0,0 +1,57 @@
+//===-- ScriptInterpreterPython.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHON_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHON_H
+
+#ifdef LLDB_DISABLE_PYTHON
+
+// Python is disabled in this build
+
+#else
+
+#include "lldb/Breakpoint/BreakpointOptions.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/lldb-private.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace lldb_private {
+/// Abstract interface for the Python script interpreter.
+class ScriptInterpreterPython : public ScriptInterpreter,
+ public IOHandlerDelegateMultiline {
+public:
+ class CommandDataPython : public BreakpointOptions::CommandData {
+ public:
+ CommandDataPython() : BreakpointOptions::CommandData() {
+ interpreter = lldb::eScriptLanguagePython;
+ }
+ };
+
+ ScriptInterpreterPython(Debugger &debugger)
+ : ScriptInterpreter(debugger, lldb::eScriptLanguagePython),
+ IOHandlerDelegateMultiline("DONE") {}
+
+ static void Initialize();
+ static void Terminate();
+ static lldb_private::ConstString GetPluginNameStatic();
+ static const char *GetPluginDescriptionStatic();
+ static FileSpec GetPythonDir();
+
+protected:
+ static void ComputePythonDirForApple(llvm::SmallVectorImpl<char> &path);
+ static void ComputePythonDirForPosix(llvm::SmallVectorImpl<char> &path);
+ static void ComputePythonDirForWindows(llvm::SmallVectorImpl<char> &path);
+};
+} // namespace lldb_private
+
+#endif // LLDB_DISABLE_PYTHON
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHON_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
new file mode 100644
index 000000000000..a9993c4068a2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -0,0 +1,473 @@
+//===-- ScriptInterpreterPythonImpl.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef LLDB_DISABLE_PYTHON
+
+// Python is disabled in this build
+
+#else
+
+#include "lldb-python.h"
+
+#include "PythonDataObjects.h"
+#include "ScriptInterpreterPython.h"
+
+#include "lldb/Host/Terminal.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_private {
+class IOHandlerPythonInterpreter;
+class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
+public:
+ friend class IOHandlerPythonInterpreter;
+
+ ScriptInterpreterPythonImpl(Debugger &debugger);
+
+ ~ScriptInterpreterPythonImpl() override;
+
+ bool Interrupt() override;
+
+ bool ExecuteOneLine(
+ llvm::StringRef command, CommandReturnObject *result,
+ const ExecuteScriptOptions &options = ExecuteScriptOptions()) override;
+
+ void ExecuteInterpreterLoop() override;
+
+ bool ExecuteOneLineWithReturn(
+ llvm::StringRef in_string,
+ ScriptInterpreter::ScriptReturnType return_type, void *ret_value,
+ const ExecuteScriptOptions &options = ExecuteScriptOptions()) override;
+
+ lldb_private::Status ExecuteMultipleLines(
+ const char *in_string,
+ const ExecuteScriptOptions &options = ExecuteScriptOptions()) override;
+
+ Status
+ ExportFunctionDefinitionToInterpreter(StringList &function_def) override;
+
+ bool GenerateTypeScriptFunction(StringList &input, std::string &output,
+ const void *name_token = nullptr) override;
+
+ bool GenerateTypeSynthClass(StringList &input, std::string &output,
+ const void *name_token = nullptr) override;
+
+ bool GenerateTypeSynthClass(const char *oneliner, std::string &output,
+ const void *name_token = nullptr) override;
+
+ // use this if the function code is just a one-liner script
+ bool GenerateTypeScriptFunction(const char *oneliner, std::string &output,
+ const void *name_token = nullptr) override;
+
+ bool GenerateScriptAliasFunction(StringList &input,
+ std::string &output) override;
+
+ StructuredData::ObjectSP
+ CreateSyntheticScriptedProvider(const char *class_name,
+ lldb::ValueObjectSP valobj) override;
+
+ StructuredData::GenericSP
+ CreateScriptCommandObject(const char *class_name) override;
+
+ StructuredData::ObjectSP
+ CreateScriptedThreadPlan(const char *class_name,
+ lldb::ThreadPlanSP thread_plan) override;
+
+ bool ScriptedThreadPlanExplainsStop(StructuredData::ObjectSP implementor_sp,
+ Event *event,
+ bool &script_error) override;
+
+ bool ScriptedThreadPlanShouldStop(StructuredData::ObjectSP implementor_sp,
+ Event *event, bool &script_error) override;
+
+ bool ScriptedThreadPlanIsStale(StructuredData::ObjectSP implementor_sp,
+ bool &script_error) override;
+
+ lldb::StateType
+ ScriptedThreadPlanGetRunState(StructuredData::ObjectSP implementor_sp,
+ bool &script_error) override;
+
+ StructuredData::GenericSP
+ CreateScriptedBreakpointResolver(const char *class_name,
+ StructuredDataImpl *args_data,
+ lldb::BreakpointSP &bkpt_sp) override;
+ bool ScriptedBreakpointResolverSearchCallback(
+ StructuredData::GenericSP implementor_sp,
+ SymbolContext *sym_ctx) override;
+
+ lldb::SearchDepth ScriptedBreakpointResolverSearchDepth(
+ StructuredData::GenericSP implementor_sp) override;
+
+ StructuredData::GenericSP
+ CreateFrameRecognizer(const char *class_name) override;
+
+ lldb::ValueObjectListSP
+ GetRecognizedArguments(const StructuredData::ObjectSP &implementor,
+ lldb::StackFrameSP frame_sp) override;
+
+ StructuredData::GenericSP
+ OSPlugin_CreatePluginObject(const char *class_name,
+ lldb::ProcessSP process_sp) override;
+
+ StructuredData::DictionarySP
+ OSPlugin_RegisterInfo(StructuredData::ObjectSP os_plugin_object_sp) override;
+
+ StructuredData::ArraySP
+ OSPlugin_ThreadsInfo(StructuredData::ObjectSP os_plugin_object_sp) override;
+
+ StructuredData::StringSP
+ OSPlugin_RegisterContextData(StructuredData::ObjectSP os_plugin_object_sp,
+ lldb::tid_t thread_id) override;
+
+ StructuredData::DictionarySP
+ OSPlugin_CreateThread(StructuredData::ObjectSP os_plugin_object_sp,
+ lldb::tid_t tid, lldb::addr_t context) override;
+
+ StructuredData::ObjectSP
+ LoadPluginModule(const FileSpec &file_spec,
+ lldb_private::Status &error) override;
+
+ StructuredData::DictionarySP
+ GetDynamicSettings(StructuredData::ObjectSP plugin_module_sp, Target *target,
+ const char *setting_name,
+ lldb_private::Status &error) override;
+
+ size_t CalculateNumChildren(const StructuredData::ObjectSP &implementor,
+ uint32_t max) override;
+
+ lldb::ValueObjectSP
+ GetChildAtIndex(const StructuredData::ObjectSP &implementor,
+ uint32_t idx) override;
+
+ int GetIndexOfChildWithName(const StructuredData::ObjectSP &implementor,
+ const char *child_name) override;
+
+ bool UpdateSynthProviderInstance(
+ const StructuredData::ObjectSP &implementor) override;
+
+ bool MightHaveChildrenSynthProviderInstance(
+ const StructuredData::ObjectSP &implementor) override;
+
+ lldb::ValueObjectSP
+ GetSyntheticValue(const StructuredData::ObjectSP &implementor) override;
+
+ ConstString
+ GetSyntheticTypeName(const StructuredData::ObjectSP &implementor) override;
+
+ bool
+ RunScriptBasedCommand(const char *impl_function, llvm::StringRef args,
+ ScriptedCommandSynchronicity synchronicity,
+ lldb_private::CommandReturnObject &cmd_retobj,
+ Status &error,
+ const lldb_private::ExecutionContext &exe_ctx) override;
+
+ bool RunScriptBasedCommand(
+ StructuredData::GenericSP impl_obj_sp, llvm::StringRef args,
+ ScriptedCommandSynchronicity synchronicity,
+ lldb_private::CommandReturnObject &cmd_retobj, Status &error,
+ const lldb_private::ExecutionContext &exe_ctx) override;
+
+ Status GenerateFunction(const char *signature,
+ const StringList &input) override;
+
+ Status GenerateBreakpointCommandCallbackData(StringList &input,
+ std::string &output) override;
+
+ bool GenerateWatchpointCommandCallbackData(StringList &input,
+ std::string &output) override;
+
+ bool GetScriptedSummary(const char *function_name, lldb::ValueObjectSP valobj,
+ StructuredData::ObjectSP &callee_wrapper_sp,
+ const TypeSummaryOptions &options,
+ std::string &retval) override;
+
+ void Clear() override;
+
+ bool GetDocumentationForItem(const char *item, std::string &dest) override;
+
+ bool GetShortHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp,
+ std::string &dest) override;
+
+ uint32_t
+ GetFlagsForCommandObject(StructuredData::GenericSP cmd_obj_sp) override;
+
+ bool GetLongHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp,
+ std::string &dest) override;
+
+ bool CheckObjectExists(const char *name) override {
+ if (!name || !name[0])
+ return false;
+ std::string temp;
+ return GetDocumentationForItem(name, temp);
+ }
+
+ bool RunScriptFormatKeyword(const char *impl_function, Process *process,
+ std::string &output, Status &error) override;
+
+ bool RunScriptFormatKeyword(const char *impl_function, Thread *thread,
+ std::string &output, Status &error) override;
+
+ bool RunScriptFormatKeyword(const char *impl_function, Target *target,
+ std::string &output, Status &error) override;
+
+ bool RunScriptFormatKeyword(const char *impl_function, StackFrame *frame,
+ std::string &output, Status &error) override;
+
+ bool RunScriptFormatKeyword(const char *impl_function, ValueObject *value,
+ std::string &output, Status &error) override;
+
+ bool
+ LoadScriptingModule(const char *filename, bool can_reload, bool init_session,
+ lldb_private::Status &error,
+ StructuredData::ObjectSP *module_sp = nullptr) override;
+
+ bool IsReservedWord(const char *word) override;
+
+ std::unique_ptr<ScriptInterpreterLocker> AcquireInterpreterLock() override;
+
+ void CollectDataForBreakpointCommandCallback(
+ std::vector<BreakpointOptions *> &bp_options_vec,
+ CommandReturnObject &result) override;
+
+ void
+ CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options,
+ CommandReturnObject &result) override;
+
+ /// Set the callback body text into the callback for the breakpoint.
+ Status SetBreakpointCommandCallback(BreakpointOptions *bp_options,
+ const char *callback_body) override;
+
+ void SetBreakpointCommandCallbackFunction(BreakpointOptions *bp_options,
+ const char *function_name) override;
+
+ /// This one is for deserialization:
+ Status SetBreakpointCommandCallback(
+ BreakpointOptions *bp_options,
+ std::unique_ptr<BreakpointOptions::CommandData> &data_up) override;
+
+ /// Set a one-liner as the callback for the watchpoint.
+ void SetWatchpointCommandCallback(WatchpointOptions *wp_options,
+ const char *oneliner) override;
+
+ void ResetOutputFileHandle(FILE *new_fh) override;
+
+ const char *GetDictionaryName() { return m_dictionary_name.c_str(); }
+
+ PyThreadState *GetThreadState() { return m_command_thread_state; }
+
+ void SetThreadState(PyThreadState *s) {
+ if (s)
+ m_command_thread_state = s;
+ }
+
+ // IOHandlerDelegate
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override;
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override;
+
+ static lldb::ScriptInterpreterSP CreateInstance(Debugger &debugger);
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ class Locker : public ScriptInterpreterLocker {
+ public:
+ enum OnEntry {
+ AcquireLock = 0x0001,
+ InitSession = 0x0002,
+ InitGlobals = 0x0004,
+ NoSTDIN = 0x0008
+ };
+
+ enum OnLeave {
+ FreeLock = 0x0001,
+ FreeAcquiredLock = 0x0002, // do not free the lock if we already held it
+ // when calling constructor
+ TearDownSession = 0x0004
+ };
+
+ Locker(ScriptInterpreterPythonImpl *py_interpreter = nullptr,
+ uint16_t on_entry = AcquireLock | InitSession,
+ uint16_t on_leave = FreeLock | TearDownSession, FILE *in = nullptr,
+ FILE *out = nullptr, FILE *err = nullptr);
+
+ ~Locker() override;
+
+ private:
+ bool DoAcquireLock();
+
+ bool DoInitSession(uint16_t on_entry_flags, FILE *in, FILE *out, FILE *err);
+
+ bool DoFreeLock();
+
+ bool DoTearDownSession();
+
+ bool m_teardown_session;
+ ScriptInterpreterPythonImpl *m_python_interpreter;
+ // FILE* m_tmp_fh;
+ PyGILState_STATE m_GILState;
+ };
+
+ static bool BreakpointCallbackFunction(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+ static bool WatchpointCallbackFunction(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t watch_id);
+ static void InitializePrivate();
+
+ class SynchronicityHandler {
+ private:
+ lldb::DebuggerSP m_debugger_sp;
+ ScriptedCommandSynchronicity m_synch_wanted;
+ bool m_old_asynch;
+
+ public:
+ SynchronicityHandler(lldb::DebuggerSP, ScriptedCommandSynchronicity);
+
+ ~SynchronicityHandler();
+ };
+
+ enum class AddLocation { Beginning, End };
+
+ static void AddToSysPath(AddLocation location, std::string path);
+
+ bool EnterSession(uint16_t on_entry_flags, FILE *in, FILE *out, FILE *err);
+
+ void LeaveSession();
+
+ void SaveTerminalState(int fd);
+
+ void RestoreTerminalState();
+
+ uint32_t IsExecutingPython() const { return m_lock_count > 0; }
+
+ uint32_t IncrementLockCount() { return ++m_lock_count; }
+
+ uint32_t DecrementLockCount() {
+ if (m_lock_count > 0)
+ --m_lock_count;
+ return m_lock_count;
+ }
+
+ enum ActiveIOHandler {
+ eIOHandlerNone,
+ eIOHandlerBreakpoint,
+ eIOHandlerWatchpoint
+ };
+
+ PythonObject &GetMainModule();
+
+ PythonDictionary &GetSessionDictionary();
+
+ PythonDictionary &GetSysModuleDictionary();
+
+ bool GetEmbeddedInterpreterModuleObjects();
+
+ bool SetStdHandle(File &file, const char *py_name, PythonFile &save_file,
+ const char *mode);
+
+ PythonFile m_saved_stdin;
+ PythonFile m_saved_stdout;
+ PythonFile m_saved_stderr;
+ PythonObject m_main_module;
+ PythonDictionary m_session_dict;
+ PythonDictionary m_sys_module_dict;
+ PythonObject m_run_one_line_function;
+ PythonObject m_run_one_line_str_global;
+ std::string m_dictionary_name;
+ TerminalState m_terminal_state;
+ ActiveIOHandler m_active_io_handler;
+ bool m_session_is_active;
+ bool m_pty_slave_is_open;
+ bool m_valid_session;
+ uint32_t m_lock_count;
+ PyThreadState *m_command_thread_state;
+};
+
+class IOHandlerPythonInterpreter : public IOHandler {
+public:
+ IOHandlerPythonInterpreter(Debugger &debugger,
+ ScriptInterpreterPythonImpl *python)
+ : IOHandler(debugger, IOHandler::Type::PythonInterpreter),
+ m_python(python) {}
+
+ ~IOHandlerPythonInterpreter() override {}
+
+ ConstString GetControlSequence(char ch) override {
+ if (ch == 'd')
+ return ConstString("quit()\n");
+ return ConstString();
+ }
+
+ void Run() override {
+ if (m_python) {
+ int stdin_fd = GetInputFD();
+ if (stdin_fd >= 0) {
+ Terminal terminal(stdin_fd);
+ TerminalState terminal_state;
+ const bool is_a_tty = terminal.IsATerminal();
+
+ if (is_a_tty) {
+ terminal_state.Save(stdin_fd, false);
+ terminal.SetCanonical(false);
+ terminal.SetEcho(true);
+ }
+
+ ScriptInterpreterPythonImpl::Locker locker(
+ m_python,
+ ScriptInterpreterPythonImpl::Locker::AcquireLock |
+ ScriptInterpreterPythonImpl::Locker::InitSession |
+ ScriptInterpreterPythonImpl::Locker::InitGlobals,
+ ScriptInterpreterPythonImpl::Locker::FreeAcquiredLock |
+ ScriptInterpreterPythonImpl::Locker::TearDownSession);
+
+ // The following call drops into the embedded interpreter loop and
+ // stays there until the user chooses to exit from the Python
+ // interpreter. This embedded interpreter will, as any Python code that
+ // performs I/O, unlock the GIL before a system call that can hang, and
+ // lock it when the syscall has returned.
+
+ // We need to surround the call to the embedded interpreter with calls
+ // to PyGILState_Ensure and PyGILState_Release (using the Locker
+ // above). This is because Python has a global lock which must be held
+ // whenever we want to touch any Python objects. Otherwise, if the user
+ // calls Python code, the interpreter state will be off, and things
+ // could hang (it's happened before).
+
+ StreamString run_string;
+ run_string.Printf("run_python_interpreter (%s)",
+ m_python->GetDictionaryName());
+ PyRun_SimpleString(run_string.GetData());
+
+ if (is_a_tty)
+ terminal_state.Restore();
+ }
+ }
+ SetIsDone(true);
+ }
+
+ void Cancel() override {}
+
+ bool Interrupt() override { return m_python->Interrupt(); }
+
+ void GotEOF() override {}
+
+protected:
+ ScriptInterpreterPythonImpl *m_python;
+};
+
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h
new file mode 100644
index 000000000000..884514da9924
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h
@@ -0,0 +1,45 @@
+//===-- lldb-python.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_LLDB_PYTHON_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_LLDB_PYTHON_H
+
+// Python.h needs to be included before any system headers in order to avoid
+// redefinition of macros
+
+#ifdef LLDB_DISABLE_PYTHON
+// Python is disabled in this build
+#else
+#include "llvm/Support/Compiler.h"
+#if defined(_WIN32)
+// If anyone #includes Host/PosixApi.h later, it will try to typedef pid_t. We
+// need to ensure this doesn't happen. At the same time, Python.h will also try
+// to redefine a bunch of stuff that PosixApi.h defines. So define it all now
+// so that PosixApi.h doesn't redefine it.
+#define NO_PID_T
+#endif
+#if defined(__linux__)
+// features.h will define _POSIX_C_SOURCE if _GNU_SOURCE is defined. This value
+// may be different from the value that Python defines it to be which results
+// in a warning. Undefine _POSIX_C_SOURCE before including Python.h The same
+// holds for _XOPEN_SOURCE.
+#undef _POSIX_C_SOURCE
+#undef _XOPEN_SOURCE
+#endif
+
+// Include locale before Python so _PY_PORT_CTYPE_UTF8_ISSUE doesn't cause
+// macro redefinitions.
+#if defined(__APPLE__)
+#include <locale>
+#endif
+
+// Include python for non windows machines
+#include <Python.h>
+#endif // LLDB_DISABLE_PYTHON
+
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_LLDB_PYTHON_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp b/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
new file mode 100644
index 000000000000..81a27125c036
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
@@ -0,0 +1,1924 @@
+//===-- StructuredDataDarwinLog.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "StructuredDataDarwinLog.h"
+
+#include <string.h>
+
+#include <memory>
+#include <sstream>
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlanCallOnFunctionExit.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+
+#define DARWIN_LOG_TYPE_VALUE "DarwinLog"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark -
+#pragma mark Anonymous Namespace
+
+// Anonymous namespace
+
+namespace sddarwinlog_private {
+const uint64_t NANOS_PER_MICRO = 1000;
+const uint64_t NANOS_PER_MILLI = NANOS_PER_MICRO * 1000;
+const uint64_t NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
+const uint64_t NANOS_PER_MINUTE = NANOS_PER_SECOND * 60;
+const uint64_t NANOS_PER_HOUR = NANOS_PER_MINUTE * 60;
+
+static bool DEFAULT_FILTER_FALLTHROUGH_ACCEPTS = true;
+
+/// Global, sticky enable switch. If true, the user has explicitly
+/// run the enable command. When a process launches or is attached to,
+/// we will enable DarwinLog if either the settings for auto-enable is
+/// on, or if the user had explicitly run enable at some point prior
+/// to the launch/attach.
+static bool s_is_explicitly_enabled;
+
+class EnableOptions;
+using EnableOptionsSP = std::shared_ptr<EnableOptions>;
+
+using OptionsMap =
+ std::map<DebuggerWP, EnableOptionsSP, std::owner_less<DebuggerWP>>;
+
+static OptionsMap &GetGlobalOptionsMap() {
+ static OptionsMap s_options_map;
+ return s_options_map;
+}
+
+static std::mutex &GetGlobalOptionsMapLock() {
+ static std::mutex s_options_map_lock;
+ return s_options_map_lock;
+}
+
+EnableOptionsSP GetGlobalEnableOptions(const DebuggerSP &debugger_sp) {
+ if (!debugger_sp)
+ return EnableOptionsSP();
+
+ std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock());
+ OptionsMap &options_map = GetGlobalOptionsMap();
+ DebuggerWP debugger_wp(debugger_sp);
+ auto find_it = options_map.find(debugger_wp);
+ if (find_it != options_map.end())
+ return find_it->second;
+ else
+ return EnableOptionsSP();
+}
+
+void SetGlobalEnableOptions(const DebuggerSP &debugger_sp,
+ const EnableOptionsSP &options_sp) {
+ std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock());
+ OptionsMap &options_map = GetGlobalOptionsMap();
+ DebuggerWP debugger_wp(debugger_sp);
+ auto find_it = options_map.find(debugger_wp);
+ if (find_it != options_map.end())
+ find_it->second = options_sp;
+ else
+ options_map.insert(std::make_pair(debugger_wp, options_sp));
+}
+
+#pragma mark -
+#pragma mark Settings Handling
+
+/// Code to handle the StructuredDataDarwinLog settings
+
+static constexpr PropertyDefinition g_properties[] = {
+ {
+ "enable-on-startup", // name
+ OptionValue::eTypeBoolean, // type
+ true, // global
+ false, // default uint value
+ nullptr, // default cstring value
+ {}, // enum values
+ "Enable Darwin os_log collection when debugged process is launched "
+ "or attached." // description
+ },
+ {
+ "auto-enable-options", // name
+ OptionValue::eTypeString, // type
+ true, // global
+ 0, // default uint value
+ "", // default cstring value
+ {}, // enum values
+ "Specify the options to 'plugin structured-data darwin-log enable' "
+ "that should be applied when automatically enabling logging on "
+ "startup/attach." // description
+ }};
+
+enum { ePropertyEnableOnStartup = 0, ePropertyAutoEnableOptions = 1 };
+
+class StructuredDataDarwinLogProperties : public Properties {
+public:
+ static ConstString &GetSettingName() {
+ static ConstString g_setting_name("darwin-log");
+ return g_setting_name;
+ }
+
+ StructuredDataDarwinLogProperties() : Properties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_properties);
+ }
+
+ ~StructuredDataDarwinLogProperties() override {}
+
+ bool GetEnableOnStartup() const {
+ const uint32_t idx = ePropertyEnableOnStartup;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+ }
+
+ llvm::StringRef GetAutoEnableOptions() const {
+ const uint32_t idx = ePropertyAutoEnableOptions;
+ return m_collection_sp->GetPropertyAtIndexAsString(
+ nullptr, idx, g_properties[idx].default_cstr_value);
+ }
+
+ const char *GetLoggingModuleName() const { return "libsystem_trace.dylib"; }
+};
+
+using StructuredDataDarwinLogPropertiesSP =
+ std::shared_ptr<StructuredDataDarwinLogProperties>;
+
+static const StructuredDataDarwinLogPropertiesSP &GetGlobalProperties() {
+ static StructuredDataDarwinLogPropertiesSP g_settings_sp;
+ if (!g_settings_sp)
+ g_settings_sp = std::make_shared<StructuredDataDarwinLogProperties>();
+ return g_settings_sp;
+}
+
+const char *const s_filter_attributes[] = {
+ "activity", // current activity
+ "activity-chain", // entire activity chain, each level separated by ':'
+ "category", // category of the log message
+ "message", // message contents, fully expanded
+ "subsystem" // subsystem of the log message
+
+ // Consider implementing this action as it would be cheaper to filter.
+ // "message" requires always formatting the message, which is a waste of
+ // cycles if it ends up being rejected. "format", // format string
+ // used to format message text
+};
+
+static ConstString GetDarwinLogTypeName() {
+ static const ConstString s_key_name("DarwinLog");
+ return s_key_name;
+}
+
+static ConstString GetLogEventType() {
+ static const ConstString s_event_type("log");
+ return s_event_type;
+}
+
+class FilterRule;
+using FilterRuleSP = std::shared_ptr<FilterRule>;
+
+class FilterRule {
+public:
+ virtual ~FilterRule() {}
+
+ using OperationCreationFunc =
+ std::function<FilterRuleSP(bool accept, size_t attribute_index,
+ const std::string &op_arg, Status &error)>;
+
+ static void RegisterOperation(ConstString operation,
+ const OperationCreationFunc &creation_func) {
+ GetCreationFuncMap().insert(std::make_pair(operation, creation_func));
+ }
+
+ static FilterRuleSP CreateRule(bool match_accepts, size_t attribute,
+ ConstString operation,
+ const std::string &op_arg, Status &error) {
+ // Find the creation func for this type of filter rule.
+ auto map = GetCreationFuncMap();
+ auto find_it = map.find(operation);
+ if (find_it == map.end()) {
+ error.SetErrorStringWithFormat("unknown filter operation \""
+ "%s\"",
+ operation.GetCString());
+ return FilterRuleSP();
+ }
+
+ return find_it->second(match_accepts, attribute, op_arg, error);
+ }
+
+ StructuredData::ObjectSP Serialize() const {
+ StructuredData::Dictionary *dict_p = new StructuredData::Dictionary();
+
+ // Indicate whether this is an accept or reject rule.
+ dict_p->AddBooleanItem("accept", m_accept);
+
+ // Indicate which attribute of the message this filter references. This can
+ // drop into the rule-specific DoSerialization if we get to the point where
+ // not all FilterRule derived classes work on an attribute. (e.g. logical
+ // and/or and other compound operations).
+ dict_p->AddStringItem("attribute", s_filter_attributes[m_attribute_index]);
+
+ // Indicate the type of the rule.
+ dict_p->AddStringItem("type", GetOperationType().GetCString());
+
+ // Let the rule add its own specific details here.
+ DoSerialization(*dict_p);
+
+ return StructuredData::ObjectSP(dict_p);
+ }
+
+ virtual void Dump(Stream &stream) const = 0;
+
+ ConstString GetOperationType() const { return m_operation; }
+
+protected:
+ FilterRule(bool accept, size_t attribute_index, ConstString operation)
+ : m_accept(accept), m_attribute_index(attribute_index),
+ m_operation(operation) {}
+
+ virtual void DoSerialization(StructuredData::Dictionary &dict) const = 0;
+
+ bool GetMatchAccepts() const { return m_accept; }
+
+ const char *GetFilterAttribute() const {
+ return s_filter_attributes[m_attribute_index];
+ }
+
+private:
+ using CreationFuncMap = std::map<ConstString, OperationCreationFunc>;
+
+ static CreationFuncMap &GetCreationFuncMap() {
+ static CreationFuncMap s_map;
+ return s_map;
+ }
+
+ const bool m_accept;
+ const size_t m_attribute_index;
+ const ConstString m_operation;
+};
+
+using FilterRules = std::vector<FilterRuleSP>;
+
+class RegexFilterRule : public FilterRule {
+public:
+ static void RegisterOperation() {
+ FilterRule::RegisterOperation(StaticGetOperation(), CreateOperation);
+ }
+
+ void Dump(Stream &stream) const override {
+ stream.Printf("%s %s regex %s", GetMatchAccepts() ? "accept" : "reject",
+ GetFilterAttribute(), m_regex_text.c_str());
+ }
+
+protected:
+ void DoSerialization(StructuredData::Dictionary &dict) const override {
+ dict.AddStringItem("regex", m_regex_text);
+ }
+
+private:
+ static FilterRuleSP CreateOperation(bool accept, size_t attribute_index,
+ const std::string &op_arg,
+ Status &error) {
+ // We treat the op_arg as a regex. Validate it.
+ if (op_arg.empty()) {
+ error.SetErrorString("regex filter type requires a regex "
+ "argument");
+ return FilterRuleSP();
+ }
+
+ // Instantiate the regex so we can report any errors.
+ auto regex = RegularExpression(op_arg);
+ if (!regex.IsValid()) {
+ char error_text[256];
+ error_text[0] = '\0';
+ regex.GetErrorAsCString(error_text, sizeof(error_text));
+ error.SetErrorString(error_text);
+ return FilterRuleSP();
+ }
+
+ // We passed all our checks, this appears fine.
+ error.Clear();
+ return FilterRuleSP(new RegexFilterRule(accept, attribute_index, op_arg));
+ }
+
+ static ConstString StaticGetOperation() {
+ static ConstString s_operation("regex");
+ return s_operation;
+ }
+
+ RegexFilterRule(bool accept, size_t attribute_index,
+ const std::string &regex_text)
+ : FilterRule(accept, attribute_index, StaticGetOperation()),
+ m_regex_text(regex_text) {}
+
+ const std::string m_regex_text;
+};
+
+class ExactMatchFilterRule : public FilterRule {
+public:
+ static void RegisterOperation() {
+ FilterRule::RegisterOperation(StaticGetOperation(), CreateOperation);
+ }
+
+ void Dump(Stream &stream) const override {
+ stream.Printf("%s %s match %s", GetMatchAccepts() ? "accept" : "reject",
+ GetFilterAttribute(), m_match_text.c_str());
+ }
+
+protected:
+ void DoSerialization(StructuredData::Dictionary &dict) const override {
+ dict.AddStringItem("exact_text", m_match_text);
+ }
+
+private:
+ static FilterRuleSP CreateOperation(bool accept, size_t attribute_index,
+ const std::string &op_arg,
+ Status &error) {
+ if (op_arg.empty()) {
+ error.SetErrorString("exact match filter type requires an "
+ "argument containing the text that must "
+ "match the specified message attribute.");
+ return FilterRuleSP();
+ }
+
+ error.Clear();
+ return FilterRuleSP(
+ new ExactMatchFilterRule(accept, attribute_index, op_arg));
+ }
+
+ static ConstString StaticGetOperation() {
+ static ConstString s_operation("match");
+ return s_operation;
+ }
+
+ ExactMatchFilterRule(bool accept, size_t attribute_index,
+ const std::string &match_text)
+ : FilterRule(accept, attribute_index, StaticGetOperation()),
+ m_match_text(match_text) {}
+
+ const std::string m_match_text;
+};
+
+static void RegisterFilterOperations() {
+ ExactMatchFilterRule::RegisterOperation();
+ RegexFilterRule::RegisterOperation();
+}
+
+// =========================================================================
+// Commands
+// =========================================================================
+
+/// Provides the main on-off switch for enabling darwin logging.
+///
+/// It is valid to run the enable command when logging is already enabled.
+/// This resets the logging with whatever settings are currently set.
+
+static constexpr OptionDefinition g_enable_option_table[] = {
+ // Source stream include/exclude options (the first-level filter). This one
+ // should be made as small as possible as everything that goes through here
+ // must be processed by the process monitor.
+ {LLDB_OPT_SET_ALL, false, "any-process", 'a', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Specifies log messages from other related processes should be "
+ "included."},
+ {LLDB_OPT_SET_ALL, false, "debug", 'd', OptionParser::eNoArgument, nullptr,
+ {}, 0, eArgTypeNone,
+ "Specifies debug-level log messages should be included. Specifying"
+ " --debug implies --info."},
+ {LLDB_OPT_SET_ALL, false, "info", 'i', OptionParser::eNoArgument, nullptr,
+ {}, 0, eArgTypeNone,
+ "Specifies info-level log messages should be included."},
+ {LLDB_OPT_SET_ALL, false, "filter", 'f', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgRawInput,
+ // There doesn't appear to be a great way for me to have these multi-line,
+ // formatted tables in help. This looks mostly right but there are extra
+ // linefeeds added at seemingly random spots, and indentation isn't
+ // handled properly on those lines.
+ "Appends a filter rule to the log message filter chain. Multiple "
+ "rules may be added by specifying this option multiple times, "
+ "once per filter rule. Filter rules are processed in the order "
+ "they are specified, with the --no-match-accepts setting used "
+ "for any message that doesn't match one of the rules.\n"
+ "\n"
+ " Filter spec format:\n"
+ "\n"
+ " --filter \"{action} {attribute} {op}\"\n"
+ "\n"
+ " {action} :=\n"
+ " accept |\n"
+ " reject\n"
+ "\n"
+ " {attribute} :=\n"
+ " activity | // message's most-derived activity\n"
+ " activity-chain | // message's {parent}:{child} activity\n"
+ " category | // message's category\n"
+ " message | // message's expanded contents\n"
+ " subsystem | // message's subsystem\n"
+ "\n"
+ " {op} :=\n"
+ " match {exact-match-text} |\n"
+ " regex {search-regex}\n"
+ "\n"
+ "The regex flavor used is the C++ std::regex ECMAScript format. "
+ "Prefer character classes like [[:digit:]] to \\d and the like, as "
+ "getting the backslashes escaped through properly is error-prone."},
+ {LLDB_OPT_SET_ALL, false, "live-stream", 'l',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
+ "Specify whether logging events are live-streamed or buffered. "
+ "True indicates live streaming, false indicates buffered. The "
+ "default is true (live streaming). Live streaming will deliver "
+ "log messages with less delay, but buffered capture mode has less "
+ "of an observer effect."},
+ {LLDB_OPT_SET_ALL, false, "no-match-accepts", 'n',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
+ "Specify whether a log message that doesn't match any filter rule "
+ "is accepted or rejected, where true indicates accept. The "
+ "default is true."},
+ {LLDB_OPT_SET_ALL, false, "echo-to-stderr", 'e',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
+ "Specify whether os_log()/NSLog() messages are echoed to the "
+ "target program's stderr. When DarwinLog is enabled, we shut off "
+ "the mirroring of os_log()/NSLog() to the program's stderr. "
+ "Setting this flag to true will restore the stderr mirroring."
+ "The default is false."},
+ {LLDB_OPT_SET_ALL, false, "broadcast-events", 'b',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
+ "Specify if the plugin should broadcast events. Broadcasting "
+ "log events is a requirement for displaying the log entries in "
+ "LLDB command-line. It is also required if LLDB clients want to "
+ "process log events. The default is true."},
+ // Message formatting options
+ {LLDB_OPT_SET_ALL, false, "timestamp-relative", 'r',
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
+ "Include timestamp in the message header when printing a log "
+ "message. The timestamp is relative to the first displayed "
+ "message."},
+ {LLDB_OPT_SET_ALL, false, "subsystem", 's', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Include the subsystem in the message header when displaying "
+ "a log message."},
+ {LLDB_OPT_SET_ALL, false, "category", 'c', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Include the category in the message header when displaying "
+ "a log message."},
+ {LLDB_OPT_SET_ALL, false, "activity-chain", 'C', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Include the activity parent-child chain in the message header "
+ "when displaying a log message. The activity hierarchy is "
+ "displayed as {grandparent-activity}:"
+ "{parent-activity}:{activity}[:...]."},
+ {LLDB_OPT_SET_ALL, false, "all-fields", 'A', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Shortcut to specify that all header fields should be displayed."}};
+
+class EnableOptions : public Options {
+public:
+ EnableOptions()
+ : Options(), m_include_debug_level(false), m_include_info_level(false),
+ m_include_any_process(false),
+ m_filter_fall_through_accepts(DEFAULT_FILTER_FALLTHROUGH_ACCEPTS),
+ m_echo_to_stderr(false), m_display_timestamp_relative(false),
+ m_display_subsystem(false), m_display_category(false),
+ m_display_activity_chain(false), m_broadcast_events(true),
+ m_live_stream(true), m_filter_rules() {}
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_include_debug_level = false;
+ m_include_info_level = false;
+ m_include_any_process = false;
+ m_filter_fall_through_accepts = DEFAULT_FILTER_FALLTHROUGH_ACCEPTS;
+ m_echo_to_stderr = false;
+ m_display_timestamp_relative = false;
+ m_display_subsystem = false;
+ m_display_category = false;
+ m_display_activity_chain = false;
+ m_broadcast_events = true;
+ m_live_stream = true;
+ m_filter_rules.clear();
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'a':
+ m_include_any_process = true;
+ break;
+
+ case 'A':
+ m_display_timestamp_relative = true;
+ m_display_category = true;
+ m_display_subsystem = true;
+ m_display_activity_chain = true;
+ break;
+
+ case 'b':
+ m_broadcast_events =
+ OptionArgParser::ToBoolean(option_arg, true, nullptr);
+ break;
+
+ case 'c':
+ m_display_category = true;
+ break;
+
+ case 'C':
+ m_display_activity_chain = true;
+ break;
+
+ case 'd':
+ m_include_debug_level = true;
+ break;
+
+ case 'e':
+ m_echo_to_stderr = OptionArgParser::ToBoolean(option_arg, false, nullptr);
+ break;
+
+ case 'f':
+ return ParseFilterRule(option_arg);
+
+ case 'i':
+ m_include_info_level = true;
+ break;
+
+ case 'l':
+ m_live_stream = OptionArgParser::ToBoolean(option_arg, false, nullptr);
+ break;
+
+ case 'n':
+ m_filter_fall_through_accepts =
+ OptionArgParser::ToBoolean(option_arg, true, nullptr);
+ break;
+
+ case 'r':
+ m_display_timestamp_relative = true;
+ break;
+
+ case 's':
+ m_display_subsystem = true;
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unsupported option '%c'", short_option);
+ }
+ return error;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_enable_option_table);
+ }
+
+ StructuredData::DictionarySP BuildConfigurationData(bool enabled) {
+ StructuredData::DictionarySP config_sp(new StructuredData::Dictionary());
+
+ // Set the basic enabled state.
+ config_sp->AddBooleanItem("enabled", enabled);
+
+ // If we're disabled, there's nothing more to add.
+ if (!enabled)
+ return config_sp;
+
+ // Handle source stream flags.
+ auto source_flags_sp =
+ StructuredData::DictionarySP(new StructuredData::Dictionary());
+ config_sp->AddItem("source-flags", source_flags_sp);
+
+ source_flags_sp->AddBooleanItem("any-process", m_include_any_process);
+ source_flags_sp->AddBooleanItem("debug-level", m_include_debug_level);
+ // The debug-level flag, if set, implies info-level.
+ source_flags_sp->AddBooleanItem("info-level", m_include_info_level ||
+ m_include_debug_level);
+ source_flags_sp->AddBooleanItem("live-stream", m_live_stream);
+
+ // Specify default filter rule (the fall-through)
+ config_sp->AddBooleanItem("filter-fall-through-accepts",
+ m_filter_fall_through_accepts);
+
+ // Handle filter rules
+ if (!m_filter_rules.empty()) {
+ auto json_filter_rules_sp =
+ StructuredData::ArraySP(new StructuredData::Array);
+ config_sp->AddItem("filter-rules", json_filter_rules_sp);
+ for (auto &rule_sp : m_filter_rules) {
+ if (!rule_sp)
+ continue;
+ json_filter_rules_sp->AddItem(rule_sp->Serialize());
+ }
+ }
+ return config_sp;
+ }
+
+ bool GetIncludeDebugLevel() const { return m_include_debug_level; }
+
+ bool GetIncludeInfoLevel() const {
+ // Specifying debug level implies info level.
+ return m_include_info_level || m_include_debug_level;
+ }
+
+ const FilterRules &GetFilterRules() const { return m_filter_rules; }
+
+ bool GetFallthroughAccepts() const { return m_filter_fall_through_accepts; }
+
+ bool GetEchoToStdErr() const { return m_echo_to_stderr; }
+
+ bool GetDisplayTimestampRelative() const {
+ return m_display_timestamp_relative;
+ }
+
+ bool GetDisplaySubsystem() const { return m_display_subsystem; }
+ bool GetDisplayCategory() const { return m_display_category; }
+ bool GetDisplayActivityChain() const { return m_display_activity_chain; }
+
+ bool GetDisplayAnyHeaderFields() const {
+ return m_display_timestamp_relative || m_display_activity_chain ||
+ m_display_subsystem || m_display_category;
+ }
+
+ bool GetBroadcastEvents() const { return m_broadcast_events; }
+
+private:
+ Status ParseFilterRule(llvm::StringRef rule_text) {
+ Status error;
+
+ if (rule_text.empty()) {
+ error.SetErrorString("invalid rule_text");
+ return error;
+ }
+
+ // filter spec format:
+ //
+ // {action} {attribute} {op}
+ //
+ // {action} :=
+ // accept |
+ // reject
+ //
+ // {attribute} :=
+ // category |
+ // subsystem |
+ // activity |
+ // activity-chain |
+ // message |
+ // format
+ //
+ // {op} :=
+ // match {exact-match-text} |
+ // regex {search-regex}
+
+ // Parse action.
+ auto action_end_pos = rule_text.find(" ");
+ if (action_end_pos == std::string::npos) {
+ error.SetErrorStringWithFormat("could not parse filter rule "
+ "action from \"%s\"",
+ rule_text.str().c_str());
+ return error;
+ }
+ auto action = rule_text.substr(0, action_end_pos);
+ bool accept;
+ if (action == "accept")
+ accept = true;
+ else if (action == "reject")
+ accept = false;
+ else {
+ error.SetErrorString("filter action must be \"accept\" or \"deny\"");
+ return error;
+ }
+
+ // parse attribute
+ auto attribute_end_pos = rule_text.find(" ", action_end_pos + 1);
+ if (attribute_end_pos == std::string::npos) {
+ error.SetErrorStringWithFormat("could not parse filter rule "
+ "attribute from \"%s\"",
+ rule_text.str().c_str());
+ return error;
+ }
+ auto attribute = rule_text.substr(action_end_pos + 1,
+ attribute_end_pos - (action_end_pos + 1));
+ auto attribute_index = MatchAttributeIndex(attribute);
+ if (attribute_index < 0) {
+ error.SetErrorStringWithFormat("filter rule attribute unknown: "
+ "%s",
+ attribute.str().c_str());
+ return error;
+ }
+
+ // parse operation
+ auto operation_end_pos = rule_text.find(" ", attribute_end_pos + 1);
+ auto operation = rule_text.substr(
+ attribute_end_pos + 1, operation_end_pos - (attribute_end_pos + 1));
+
+ // add filter spec
+ auto rule_sp =
+ FilterRule::CreateRule(accept, attribute_index, ConstString(operation),
+ rule_text.substr(operation_end_pos + 1), error);
+
+ if (rule_sp && error.Success())
+ m_filter_rules.push_back(rule_sp);
+
+ return error;
+ }
+
+ int MatchAttributeIndex(llvm::StringRef attribute_name) const {
+ for (const auto &Item : llvm::enumerate(s_filter_attributes)) {
+ if (attribute_name == Item.value())
+ return Item.index();
+ }
+
+ // We didn't match anything.
+ return -1;
+ }
+
+ bool m_include_debug_level;
+ bool m_include_info_level;
+ bool m_include_any_process;
+ bool m_filter_fall_through_accepts;
+ bool m_echo_to_stderr;
+ bool m_display_timestamp_relative;
+ bool m_display_subsystem;
+ bool m_display_category;
+ bool m_display_activity_chain;
+ bool m_broadcast_events;
+ bool m_live_stream;
+ FilterRules m_filter_rules;
+};
+
+class EnableCommand : public CommandObjectParsed {
+public:
+ EnableCommand(CommandInterpreter &interpreter, bool enable, const char *name,
+ const char *help, const char *syntax)
+ : CommandObjectParsed(interpreter, name, help, syntax), m_enable(enable),
+ m_options_sp(enable ? new EnableOptions() : nullptr) {}
+
+protected:
+ void AppendStrictSourcesWarning(CommandReturnObject &result,
+ const char *source_name) {
+ if (!source_name)
+ return;
+
+ // Check if we're *not* using strict sources. If not, then the user is
+ // going to get debug-level info anyways, probably not what they're
+ // expecting. Unfortunately we can only fix this by adding an env var,
+ // which would have had to have happened already. Thus, a warning is the
+ // best we can do here.
+ StreamString stream;
+ stream.Printf("darwin-log source settings specify to exclude "
+ "%s messages, but setting "
+ "'plugin.structured-data.darwin-log."
+ "strict-sources' is disabled. This process will "
+ "automatically have %s messages included. Enable"
+ " the property and relaunch the target binary to have"
+ " these messages excluded.",
+ source_name, source_name);
+ result.AppendWarning(stream.GetString());
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // First off, set the global sticky state of enable/disable based on this
+ // command execution.
+ s_is_explicitly_enabled = m_enable;
+
+ // Next, if this is an enable, save off the option data. We will need it
+ // later if a process hasn't been launched or attached yet.
+ if (m_enable) {
+ // Save off enabled configuration so we can apply these parsed options
+ // the next time an attach or launch occurs.
+ DebuggerSP debugger_sp =
+ GetCommandInterpreter().GetDebugger().shared_from_this();
+ SetGlobalEnableOptions(debugger_sp, m_options_sp);
+ }
+
+ // Now check if we have a running process. If so, we should instruct the
+ // process monitor to enable/disable DarwinLog support now.
+ Target *target = GetSelectedOrDummyTarget();
+ if (!target) {
+ // No target, so there is nothing more to do right now.
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ // Grab the active process.
+ auto process_sp = target->GetProcessSP();
+ if (!process_sp) {
+ // No active process, so there is nothing more to do right now.
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ // If the process is no longer alive, we can't do this now. We'll catch it
+ // the next time the process is started up.
+ if (!process_sp->IsAlive()) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ // Get the plugin for the process.
+ auto plugin_sp =
+ process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
+ if (!plugin_sp || (plugin_sp->GetPluginName() !=
+ StructuredDataDarwinLog::GetStaticPluginName())) {
+ result.AppendError("failed to get StructuredDataPlugin for "
+ "the process");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ StructuredDataDarwinLog &plugin =
+ *static_cast<StructuredDataDarwinLog *>(plugin_sp.get());
+
+ if (m_enable) {
+ // Hook up the breakpoint for the process that detects when libtrace has
+ // been sufficiently initialized to really start the os_log stream. This
+ // is insurance to assure us that logging is really enabled. Requesting
+ // that logging be enabled for a process before libtrace is initialized
+ // results in a scenario where no errors occur, but no logging is
+ // captured, either. This step is to eliminate that possibility.
+ plugin.AddInitCompletionHook(*process_sp);
+ }
+
+ // Send configuration to the feature by way of the process. Construct the
+ // options we will use.
+ auto config_sp = m_options_sp->BuildConfigurationData(m_enable);
+ const Status error =
+ process_sp->ConfigureStructuredData(GetDarwinLogTypeName(), config_sp);
+
+ // Report results.
+ if (!error.Success()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ // Our configuration failed, so we're definitely disabled.
+ plugin.SetEnabled(false);
+ } else {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ // Our configuration succeeded, so we're enabled/disabled per whichever
+ // one this command is setup to do.
+ plugin.SetEnabled(m_enable);
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override {
+ // We don't have options when this represents disable.
+ return m_enable ? m_options_sp.get() : nullptr;
+ }
+
+private:
+ const bool m_enable;
+ EnableOptionsSP m_options_sp;
+};
+
+/// Provides the status command.
+class StatusCommand : public CommandObjectParsed {
+public:
+ StatusCommand(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "status",
+ "Show whether Darwin log supported is available"
+ " and enabled.",
+ "plugin structured-data darwin-log status") {}
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ auto &stream = result.GetOutputStream();
+
+ // Figure out if we've got a process. If so, we can tell if DarwinLog is
+ // available for that process.
+ Target *target = GetSelectedOrDummyTarget();
+ auto process_sp = target ? target->GetProcessSP() : ProcessSP();
+ if (!target || !process_sp) {
+ stream.PutCString("Availability: unknown (requires process)\n");
+ stream.PutCString("Enabled: not applicable "
+ "(requires process)\n");
+ } else {
+ auto plugin_sp =
+ process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
+ stream.Printf("Availability: %s\n",
+ plugin_sp ? "available" : "unavailable");
+ ConstString plugin_name = StructuredDataDarwinLog::GetStaticPluginName();
+ const bool enabled =
+ plugin_sp ? plugin_sp->GetEnabled(plugin_name) : false;
+ stream.Printf("Enabled: %s\n", enabled ? "true" : "false");
+ }
+
+ // Display filter settings.
+ DebuggerSP debugger_sp =
+ GetCommandInterpreter().GetDebugger().shared_from_this();
+ auto options_sp = GetGlobalEnableOptions(debugger_sp);
+ if (!options_sp) {
+ // Nothing more to do.
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ // Print filter rules
+ stream.PutCString("DarwinLog filter rules:\n");
+
+ stream.IndentMore();
+
+ if (options_sp->GetFilterRules().empty()) {
+ stream.Indent();
+ stream.PutCString("none\n");
+ } else {
+ // Print each of the filter rules.
+ int rule_number = 0;
+ for (auto rule_sp : options_sp->GetFilterRules()) {
+ ++rule_number;
+ if (!rule_sp)
+ continue;
+
+ stream.Indent();
+ stream.Printf("%02d: ", rule_number);
+ rule_sp->Dump(stream);
+ stream.PutChar('\n');
+ }
+ }
+ stream.IndentLess();
+
+ // Print no-match handling.
+ stream.Indent();
+ stream.Printf("no-match behavior: %s\n",
+ options_sp->GetFallthroughAccepts() ? "accept" : "reject");
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+/// Provides the darwin-log base command
+class BaseCommand : public CommandObjectMultiword {
+public:
+ BaseCommand(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "plugin structured-data darwin-log",
+ "Commands for configuring Darwin os_log "
+ "support.",
+ "") {
+ // enable
+ auto enable_help = "Enable Darwin log collection, or re-enable "
+ "with modified configuration.";
+ auto enable_syntax = "plugin structured-data darwin-log enable";
+ auto enable_cmd_sp = CommandObjectSP(
+ new EnableCommand(interpreter,
+ true, // enable
+ "enable", enable_help, enable_syntax));
+ LoadSubCommand("enable", enable_cmd_sp);
+
+ // disable
+ auto disable_help = "Disable Darwin log collection.";
+ auto disable_syntax = "plugin structured-data darwin-log disable";
+ auto disable_cmd_sp = CommandObjectSP(
+ new EnableCommand(interpreter,
+ false, // disable
+ "disable", disable_help, disable_syntax));
+ LoadSubCommand("disable", disable_cmd_sp);
+
+ // status
+ auto status_cmd_sp = CommandObjectSP(new StatusCommand(interpreter));
+ LoadSubCommand("status", status_cmd_sp);
+ }
+};
+
+EnableOptionsSP ParseAutoEnableOptions(Status &error, Debugger &debugger) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS);
+ // We are abusing the options data model here so that we can parse options
+ // without requiring the Debugger instance.
+
+ // We have an empty execution context at this point. We only want to parse
+ // options, and we don't need any context to do this here. In fact, we want
+ // to be able to parse the enable options before having any context.
+ ExecutionContext exe_ctx;
+
+ EnableOptionsSP options_sp(new EnableOptions());
+ options_sp->NotifyOptionParsingStarting(&exe_ctx);
+
+ CommandReturnObject result;
+
+ // Parse the arguments.
+ auto options_property_sp =
+ debugger.GetPropertyValue(nullptr, "plugin.structured-data.darwin-log."
+ "auto-enable-options",
+ false, error);
+ if (!error.Success())
+ return EnableOptionsSP();
+ if (!options_property_sp) {
+ error.SetErrorString("failed to find option setting for "
+ "plugin.structured-data.darwin-log.");
+ return EnableOptionsSP();
+ }
+
+ const char *enable_options =
+ options_property_sp->GetAsString()->GetCurrentValue();
+ Args args(enable_options);
+ if (args.GetArgumentCount() > 0) {
+ // Eliminate the initial '--' that would be required to set the settings
+ // that themselves include '-' and/or '--'.
+ const char *first_arg = args.GetArgumentAtIndex(0);
+ if (first_arg && (strcmp(first_arg, "--") == 0))
+ args.Shift();
+ }
+
+ bool require_validation = false;
+ llvm::Expected<Args> args_or =
+ options_sp->Parse(args, &exe_ctx, PlatformSP(), require_validation);
+ if (!args_or) {
+ LLDB_LOG_ERROR(
+ log, args_or.takeError(),
+ "Parsing plugin.structured-data.darwin-log.auto-enable-options value "
+ "failed: {0}");
+ return EnableOptionsSP();
+ }
+
+ if (!options_sp->VerifyOptions(result))
+ return EnableOptionsSP();
+
+ // We successfully parsed and validated the options.
+ return options_sp;
+}
+
+bool RunEnableCommand(CommandInterpreter &interpreter) {
+ StreamString command_stream;
+
+ command_stream << "plugin structured-data darwin-log enable";
+ auto enable_options = GetGlobalProperties()->GetAutoEnableOptions();
+ if (!enable_options.empty()) {
+ command_stream << ' ';
+ command_stream << enable_options;
+ }
+
+ // Run the command.
+ CommandReturnObject return_object;
+ interpreter.HandleCommand(command_stream.GetData(), eLazyBoolNo,
+ return_object);
+ return return_object.Succeeded();
+}
+}
+using namespace sddarwinlog_private;
+
+#pragma mark -
+#pragma mark Public static API
+
+// Public static API
+
+void StructuredDataDarwinLog::Initialize() {
+ RegisterFilterOperations();
+ PluginManager::RegisterPlugin(
+ GetStaticPluginName(), "Darwin os_log() and os_activity() support",
+ &CreateInstance, &DebuggerInitialize, &FilterLaunchInfo);
+}
+
+void StructuredDataDarwinLog::Terminate() {
+ PluginManager::UnregisterPlugin(&CreateInstance);
+}
+
+ConstString StructuredDataDarwinLog::GetStaticPluginName() {
+ static ConstString s_plugin_name("darwin-log");
+ return s_plugin_name;
+}
+
+#pragma mark -
+#pragma mark PluginInterface API
+
+// PluginInterface API
+
+ConstString StructuredDataDarwinLog::GetPluginName() {
+ return GetStaticPluginName();
+}
+
+uint32_t StructuredDataDarwinLog::GetPluginVersion() { return 1; }
+
+#pragma mark -
+#pragma mark StructuredDataPlugin API
+
+// StructuredDataPlugin API
+
+bool StructuredDataDarwinLog::SupportsStructuredDataType(
+ ConstString type_name) {
+ return type_name == GetDarwinLogTypeName();
+}
+
+void StructuredDataDarwinLog::HandleArrivalOfStructuredData(
+ Process &process, ConstString type_name,
+ const StructuredData::ObjectSP &object_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log) {
+ StreamString json_stream;
+ if (object_sp)
+ object_sp->Dump(json_stream);
+ else
+ json_stream.PutCString("<null>");
+ log->Printf("StructuredDataDarwinLog::%s() called with json: %s",
+ __FUNCTION__, json_stream.GetData());
+ }
+
+ // Ignore empty structured data.
+ if (!object_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() StructuredData object "
+ "is null",
+ __FUNCTION__);
+ return;
+ }
+
+ // Ignore any data that isn't for us.
+ if (type_name != GetDarwinLogTypeName()) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() StructuredData type "
+ "expected to be %s but was %s, ignoring",
+ __FUNCTION__, GetDarwinLogTypeName().AsCString(),
+ type_name.AsCString());
+ return;
+ }
+
+ // Broadcast the structured data event if we have that enabled. This is the
+ // way that the outside world (all clients) get access to this data. This
+ // plugin sets policy as to whether we do that.
+ DebuggerSP debugger_sp = process.GetTarget().GetDebugger().shared_from_this();
+ auto options_sp = GetGlobalEnableOptions(debugger_sp);
+ if (options_sp && options_sp->GetBroadcastEvents()) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() broadcasting event",
+ __FUNCTION__);
+ process.BroadcastStructuredData(object_sp, shared_from_this());
+ }
+
+ // Later, hang on to a configurable amount of these and allow commands to
+ // inspect, including showing backtraces.
+}
+
+static void SetErrorWithJSON(Status &error, const char *message,
+ StructuredData::Object &object) {
+ if (!message) {
+ error.SetErrorString("Internal error: message not set.");
+ return;
+ }
+
+ StreamString object_stream;
+ object.Dump(object_stream);
+ object_stream.Flush();
+
+ error.SetErrorStringWithFormat("%s: %s", message, object_stream.GetData());
+}
+
+Status StructuredDataDarwinLog::GetDescription(
+ const StructuredData::ObjectSP &object_sp, lldb_private::Stream &stream) {
+ Status error;
+
+ if (!object_sp) {
+ error.SetErrorString("No structured data.");
+ return error;
+ }
+
+ // Log message payload objects will be dictionaries.
+ const StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary();
+ if (!dictionary) {
+ SetErrorWithJSON(error, "Structured data should have been a dictionary "
+ "but wasn't",
+ *object_sp);
+ return error;
+ }
+
+ // Validate this is really a message for our plugin.
+ ConstString type_name;
+ if (!dictionary->GetValueForKeyAsString("type", type_name)) {
+ SetErrorWithJSON(error, "Structured data doesn't contain mandatory "
+ "type field",
+ *object_sp);
+ return error;
+ }
+
+ if (type_name != GetDarwinLogTypeName()) {
+ // This is okay - it simply means the data we received is not a log
+ // message. We'll just format it as is.
+ object_sp->Dump(stream);
+ return error;
+ }
+
+ // DarwinLog dictionaries store their data
+ // in an array with key name "events".
+ StructuredData::Array *events = nullptr;
+ if (!dictionary->GetValueForKeyAsArray("events", events) || !events) {
+ SetErrorWithJSON(error, "Log structured data is missing mandatory "
+ "'events' field, expected to be an array",
+ *object_sp);
+ return error;
+ }
+
+ events->ForEach(
+ [&stream, &error, &object_sp, this](StructuredData::Object *object) {
+ if (!object) {
+ // Invalid. Stop iterating.
+ SetErrorWithJSON(error, "Log event entry is null", *object_sp);
+ return false;
+ }
+
+ auto event = object->GetAsDictionary();
+ if (!event) {
+ // Invalid, stop iterating.
+ SetErrorWithJSON(error, "Log event is not a dictionary", *object_sp);
+ return false;
+ }
+
+ // If we haven't already grabbed the first timestamp value, do that
+ // now.
+ if (!m_recorded_first_timestamp) {
+ uint64_t timestamp = 0;
+ if (event->GetValueForKeyAsInteger("timestamp", timestamp)) {
+ m_first_timestamp_seen = timestamp;
+ m_recorded_first_timestamp = true;
+ }
+ }
+
+ HandleDisplayOfEvent(*event, stream);
+ return true;
+ });
+
+ stream.Flush();
+ return error;
+}
+
+bool StructuredDataDarwinLog::GetEnabled(ConstString type_name) const {
+ if (type_name == GetStaticPluginName())
+ return m_is_enabled;
+ else
+ return false;
+}
+
+void StructuredDataDarwinLog::SetEnabled(bool enabled) {
+ m_is_enabled = enabled;
+}
+
+void StructuredDataDarwinLog::ModulesDidLoad(Process &process,
+ ModuleList &module_list) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s called (process uid %u)",
+ __FUNCTION__, process.GetUniqueID());
+
+ // Check if we should enable the darwin log support on startup/attach.
+ if (!GetGlobalProperties()->GetEnableOnStartup() &&
+ !s_is_explicitly_enabled) {
+ // We're neither auto-enabled or explicitly enabled, so we shouldn't try to
+ // enable here.
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s not applicable, we're not "
+ "enabled (process uid %u)",
+ __FUNCTION__, process.GetUniqueID());
+ return;
+ }
+
+ // If we already added the breakpoint, we've got nothing left to do.
+ {
+ std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex);
+ if (m_added_breakpoint) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s process uid %u's "
+ "post-libtrace-init breakpoint is already set",
+ __FUNCTION__, process.GetUniqueID());
+ return;
+ }
+ }
+
+ // The logging support module name, specifies the name of the image name that
+ // must be loaded into the debugged process before we can try to enable
+ // logging.
+ const char *logging_module_cstr =
+ GetGlobalProperties()->GetLoggingModuleName();
+ if (!logging_module_cstr || (logging_module_cstr[0] == 0)) {
+ // We need this. Bail.
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s no logging module name "
+ "specified, we don't know where to set a breakpoint "
+ "(process uid %u)",
+ __FUNCTION__, process.GetUniqueID());
+ return;
+ }
+
+ const ConstString logging_module_name = ConstString(logging_module_cstr);
+
+ // We need to see libtrace in the list of modules before we can enable
+ // tracing for the target process.
+ bool found_logging_support_module = false;
+ for (size_t i = 0; i < module_list.GetSize(); ++i) {
+ auto module_sp = module_list.GetModuleAtIndex(i);
+ if (!module_sp)
+ continue;
+
+ auto &file_spec = module_sp->GetFileSpec();
+ found_logging_support_module =
+ (file_spec.GetLastPathComponent() == logging_module_name);
+ if (found_logging_support_module)
+ break;
+ }
+
+ if (!found_logging_support_module) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s logging module %s "
+ "has not yet been loaded, can't set a breakpoint "
+ "yet (process uid %u)",
+ __FUNCTION__, logging_module_name.AsCString(),
+ process.GetUniqueID());
+ return;
+ }
+
+ // Time to enqueue the breakpoint so we can wait for logging support to be
+ // initialized before we try to tap the libtrace stream.
+ AddInitCompletionHook(process);
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s post-init hook breakpoint "
+ "set for logging module %s (process uid %u)",
+ __FUNCTION__, logging_module_name.AsCString(),
+ process.GetUniqueID());
+
+ // We need to try the enable here as well, which will succeed in the event
+ // that we're attaching to (rather than launching) the process and the
+ // process is already past initialization time. In that case, the completion
+ // breakpoint will never get hit and therefore won't start that way. It
+ // doesn't hurt much beyond a bit of bandwidth if we end up doing this twice.
+ // It hurts much more if we don't get the logging enabled when the user
+ // expects it.
+ EnableNow();
+}
+
+// public destructor
+
+StructuredDataDarwinLog::~StructuredDataDarwinLog() {
+ if (m_breakpoint_id != LLDB_INVALID_BREAK_ID) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ process_sp->GetTarget().RemoveBreakpointByID(m_breakpoint_id);
+ m_breakpoint_id = LLDB_INVALID_BREAK_ID;
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Private instance methods
+
+// Private constructors
+
+StructuredDataDarwinLog::StructuredDataDarwinLog(const ProcessWP &process_wp)
+ : StructuredDataPlugin(process_wp), m_recorded_first_timestamp(false),
+ m_first_timestamp_seen(0), m_is_enabled(false),
+ m_added_breakpoint_mutex(), m_added_breakpoint(),
+ m_breakpoint_id(LLDB_INVALID_BREAK_ID) {}
+
+// Private static methods
+
+StructuredDataPluginSP
+StructuredDataDarwinLog::CreateInstance(Process &process) {
+ // Currently only Apple targets support the os_log/os_activity protocol.
+ if (process.GetTarget().GetArchitecture().GetTriple().getVendor() ==
+ llvm::Triple::VendorType::Apple) {
+ auto process_wp = ProcessWP(process.shared_from_this());
+ return StructuredDataPluginSP(new StructuredDataDarwinLog(process_wp));
+ } else {
+ return StructuredDataPluginSP();
+ }
+}
+
+void StructuredDataDarwinLog::DebuggerInitialize(Debugger &debugger) {
+ // Setup parent class first.
+ StructuredDataPlugin::InitializeBasePluginForDebugger(debugger);
+
+ // Get parent command.
+ auto &interpreter = debugger.GetCommandInterpreter();
+ llvm::StringRef parent_command_text = "plugin structured-data";
+ auto parent_command =
+ interpreter.GetCommandObjectForCommand(parent_command_text);
+ if (!parent_command) {
+ // Ut oh, parent failed to create parent command.
+ // TODO log
+ return;
+ }
+
+ auto command_name = "darwin-log";
+ auto command_sp = CommandObjectSP(new BaseCommand(interpreter));
+ bool result = parent_command->LoadSubCommand(command_name, command_sp);
+ if (!result) {
+ // TODO log it once we setup structured data logging
+ }
+
+ if (!PluginManager::GetSettingForPlatformPlugin(
+ debugger, StructuredDataDarwinLogProperties::GetSettingName())) {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForStructuredDataPlugin(
+ debugger, GetGlobalProperties()->GetValueProperties(),
+ ConstString("Properties for the darwin-log"
+ " plug-in."),
+ is_global_setting);
+ }
+}
+
+Status StructuredDataDarwinLog::FilterLaunchInfo(ProcessLaunchInfo &launch_info,
+ Target *target) {
+ Status error;
+
+ // If we're not debugging this launched process, there's nothing for us to do
+ // here.
+ if (!launch_info.GetFlags().AnySet(eLaunchFlagDebug))
+ return error;
+
+ // Darwin os_log() support automatically adds debug-level and info-level
+ // messages when a debugger is attached to a process. However, with
+ // integrated support for debugging built into the command-line LLDB, the
+ // user may specifically set to *not* include debug-level and info-level
+ // content. When the user is using the integrated log support, we want to
+ // put the kabosh on that automatic adding of info and debug level. This is
+ // done by adding an environment variable to the process on launch. (This
+ // also means it is not possible to suppress this behavior if attaching to an
+ // already-running app).
+ // Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+
+ // If the target architecture is not one that supports DarwinLog, we have
+ // nothing to do here.
+ auto &triple = target ? target->GetArchitecture().GetTriple()
+ : launch_info.GetArchitecture().GetTriple();
+ if (triple.getVendor() != llvm::Triple::Apple) {
+ return error;
+ }
+
+ // If DarwinLog is not enabled (either by explicit user command or via the
+ // auto-enable option), then we have nothing to do.
+ if (!GetGlobalProperties()->GetEnableOnStartup() &&
+ !s_is_explicitly_enabled) {
+ // Nothing to do, DarwinLog is not enabled.
+ return error;
+ }
+
+ // If we don't have parsed configuration info, that implies we have enable-
+ // on-startup set up, but we haven't yet attempted to run the enable command.
+ if (!target) {
+ // We really can't do this without a target. We need to be able to get to
+ // the debugger to get the proper options to do this right.
+ // TODO log.
+ error.SetErrorString("requires a target to auto-enable DarwinLog.");
+ return error;
+ }
+
+ DebuggerSP debugger_sp = target->GetDebugger().shared_from_this();
+ auto options_sp = GetGlobalEnableOptions(debugger_sp);
+ if (!options_sp && debugger_sp) {
+ options_sp = ParseAutoEnableOptions(error, *debugger_sp.get());
+ if (!options_sp || !error.Success())
+ return error;
+
+ // We already parsed the options, save them now so we don't generate them
+ // again until the user runs the command manually.
+ SetGlobalEnableOptions(debugger_sp, options_sp);
+ }
+
+ if (!options_sp->GetEchoToStdErr()) {
+ // The user doesn't want to see os_log/NSLog messages echo to stderr. That
+ // mechanism is entirely separate from the DarwinLog support. By default we
+ // don't want to get it via stderr, because that would be in duplicate of
+ // the explicit log support here.
+
+ // Here we need to strip out any OS_ACTIVITY_DT_MODE setting to prevent
+ // echoing of os_log()/NSLog() to stderr in the target program.
+ launch_info.GetEnvironment().erase("OS_ACTIVITY_DT_MODE");
+
+ // We will also set the env var that tells any downstream launcher from
+ // adding OS_ACTIVITY_DT_MODE.
+ launch_info.GetEnvironment()["IDE_DISABLED_OS_ACTIVITY_DT_MODE"] = "1";
+ }
+
+ // Set the OS_ACTIVITY_MODE env var appropriately to enable/disable debug and
+ // info level messages.
+ const char *env_var_value;
+ if (options_sp->GetIncludeDebugLevel())
+ env_var_value = "debug";
+ else if (options_sp->GetIncludeInfoLevel())
+ env_var_value = "info";
+ else
+ env_var_value = "default";
+
+ launch_info.GetEnvironment()["OS_ACTIVITY_MODE"] = env_var_value;
+
+ return error;
+}
+
+bool StructuredDataDarwinLog::InitCompletionHookCallback(
+ void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ // We hit the init function. We now want to enqueue our new thread plan,
+ // which will in turn enqueue a StepOut thread plan. When the StepOut
+ // finishes and control returns to our new thread plan, that is the time when
+ // we can execute our logic to enable the logging support.
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() called", __FUNCTION__);
+
+ // Get the current thread.
+ if (!context) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: no context, "
+ "ignoring",
+ __FUNCTION__);
+ return false;
+ }
+
+ // Get the plugin from the process.
+ auto process_sp = context->exe_ctx_ref.GetProcessSP();
+ if (!process_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: invalid "
+ "process in context, ignoring",
+ __FUNCTION__);
+ return false;
+ }
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() call is for process uid %d",
+ __FUNCTION__, process_sp->GetUniqueID());
+
+ auto plugin_sp = process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
+ if (!plugin_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: no plugin for "
+ "feature %s in process uid %u",
+ __FUNCTION__, GetDarwinLogTypeName().AsCString(),
+ process_sp->GetUniqueID());
+ return false;
+ }
+
+ // Create the callback for when the thread plan completes.
+ bool called_enable_method = false;
+ const auto process_uid = process_sp->GetUniqueID();
+
+ std::weak_ptr<StructuredDataPlugin> plugin_wp(plugin_sp);
+ ThreadPlanCallOnFunctionExit::Callback callback =
+ [plugin_wp, &called_enable_method, log, process_uid]() {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::post-init callback: "
+ "called (process uid %u)",
+ process_uid);
+
+ auto strong_plugin_sp = plugin_wp.lock();
+ if (!strong_plugin_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::post-init callback: "
+ "plugin no longer exists, ignoring (process "
+ "uid %u)",
+ process_uid);
+ return;
+ }
+ // Make sure we only call it once, just in case the thread plan hits
+ // the breakpoint twice.
+ if (!called_enable_method) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::post-init callback: "
+ "calling EnableNow() (process uid %u)",
+ process_uid);
+ static_cast<StructuredDataDarwinLog *>(strong_plugin_sp.get())
+ ->EnableNow();
+ called_enable_method = true;
+ } else {
+ // Our breakpoint was hit more than once. Unexpected but no harm
+ // done. Log it.
+ if (log)
+ log->Printf("StructuredDataDarwinLog::post-init callback: "
+ "skipping EnableNow(), already called by "
+ "callback [we hit this more than once] "
+ "(process uid %u)",
+ process_uid);
+ }
+ };
+
+ // Grab the current thread.
+ auto thread_sp = context->exe_ctx_ref.GetThreadSP();
+ if (!thread_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: failed to "
+ "retrieve the current thread from the execution "
+ "context, nowhere to run the thread plan (process uid "
+ "%u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ return false;
+ }
+
+ // Queue the thread plan.
+ auto thread_plan_sp =
+ ThreadPlanSP(new ThreadPlanCallOnFunctionExit(*thread_sp, callback));
+ const bool abort_other_plans = false;
+ thread_sp->QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() queuing thread plan on "
+ "trace library init method entry (process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+
+ // We return false here to indicate that it isn't a public stop.
+ return false;
+}
+
+void StructuredDataDarwinLog::AddInitCompletionHook(Process &process) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() called (process uid %u)",
+ __FUNCTION__, process.GetUniqueID());
+
+ // Make sure we haven't already done this.
+ {
+ std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex);
+ if (m_added_breakpoint) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() ignoring request, "
+ "breakpoint already set (process uid %u)",
+ __FUNCTION__, process.GetUniqueID());
+ return;
+ }
+
+ // We're about to do this, don't let anybody else try to do it.
+ m_added_breakpoint = true;
+ }
+
+ // Set a breakpoint for the process that will kick in when libtrace has
+ // finished its initialization.
+ Target &target = process.GetTarget();
+
+ // Build up the module list.
+ FileSpecList module_spec_list;
+ auto module_file_spec =
+ FileSpec(GetGlobalProperties()->GetLoggingModuleName());
+ module_spec_list.Append(module_file_spec);
+
+ // We aren't specifying a source file set.
+ FileSpecList *source_spec_list = nullptr;
+
+ const char *func_name = "_libtrace_init";
+ const lldb::addr_t offset = 0;
+ const LazyBool skip_prologue = eLazyBoolCalculate;
+ // This is an internal breakpoint - the user shouldn't see it.
+ const bool internal = true;
+ const bool hardware = false;
+
+ auto breakpoint_sp = target.CreateBreakpoint(
+ &module_spec_list, source_spec_list, func_name, eFunctionNameTypeFull,
+ eLanguageTypeC, offset, skip_prologue, internal, hardware);
+ if (!breakpoint_sp) {
+ // Huh? Bail here.
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() failed to set "
+ "breakpoint in module %s, function %s (process uid %u)",
+ __FUNCTION__, GetGlobalProperties()->GetLoggingModuleName(),
+ func_name, process.GetUniqueID());
+ return;
+ }
+
+ // Set our callback.
+ breakpoint_sp->SetCallback(InitCompletionHookCallback, nullptr);
+ m_breakpoint_id = breakpoint_sp->GetID();
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() breakpoint set in module %s,"
+ "function %s (process uid %u)",
+ __FUNCTION__, GetGlobalProperties()->GetLoggingModuleName(),
+ func_name, process.GetUniqueID());
+}
+
+void StructuredDataDarwinLog::DumpTimestamp(Stream &stream,
+ uint64_t timestamp) {
+ const uint64_t delta_nanos = timestamp - m_first_timestamp_seen;
+
+ const uint64_t hours = delta_nanos / NANOS_PER_HOUR;
+ uint64_t nanos_remaining = delta_nanos % NANOS_PER_HOUR;
+
+ const uint64_t minutes = nanos_remaining / NANOS_PER_MINUTE;
+ nanos_remaining = nanos_remaining % NANOS_PER_MINUTE;
+
+ const uint64_t seconds = nanos_remaining / NANOS_PER_SECOND;
+ nanos_remaining = nanos_remaining % NANOS_PER_SECOND;
+
+ stream.Printf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%09" PRIu64, hours,
+ minutes, seconds, nanos_remaining);
+}
+
+size_t
+StructuredDataDarwinLog::DumpHeader(Stream &output_stream,
+ const StructuredData::Dictionary &event) {
+ StreamString stream;
+
+ ProcessSP process_sp = GetProcess();
+ if (!process_sp) {
+ // TODO log
+ return 0;
+ }
+
+ DebuggerSP debugger_sp =
+ process_sp->GetTarget().GetDebugger().shared_from_this();
+ if (!debugger_sp) {
+ // TODO log
+ return 0;
+ }
+
+ auto options_sp = GetGlobalEnableOptions(debugger_sp);
+ if (!options_sp) {
+ // TODO log
+ return 0;
+ }
+
+ // Check if we should even display a header.
+ if (!options_sp->GetDisplayAnyHeaderFields())
+ return 0;
+
+ stream.PutChar('[');
+
+ int header_count = 0;
+ if (options_sp->GetDisplayTimestampRelative()) {
+ uint64_t timestamp = 0;
+ if (event.GetValueForKeyAsInteger("timestamp", timestamp)) {
+ DumpTimestamp(stream, timestamp);
+ ++header_count;
+ }
+ }
+
+ if (options_sp->GetDisplayActivityChain()) {
+ llvm::StringRef activity_chain;
+ if (event.GetValueForKeyAsString("activity-chain", activity_chain) &&
+ !activity_chain.empty()) {
+ if (header_count > 0)
+ stream.PutChar(',');
+
+ // Display the activity chain, from parent-most to child-most activity,
+ // separated by a colon (:).
+ stream.PutCString("activity-chain=");
+ stream.PutCString(activity_chain);
+ ++header_count;
+ }
+ }
+
+ if (options_sp->GetDisplaySubsystem()) {
+ llvm::StringRef subsystem;
+ if (event.GetValueForKeyAsString("subsystem", subsystem) &&
+ !subsystem.empty()) {
+ if (header_count > 0)
+ stream.PutChar(',');
+ stream.PutCString("subsystem=");
+ stream.PutCString(subsystem);
+ ++header_count;
+ }
+ }
+
+ if (options_sp->GetDisplayCategory()) {
+ llvm::StringRef category;
+ if (event.GetValueForKeyAsString("category", category) &&
+ !category.empty()) {
+ if (header_count > 0)
+ stream.PutChar(',');
+ stream.PutCString("category=");
+ stream.PutCString(category);
+ ++header_count;
+ }
+ }
+ stream.PutCString("] ");
+
+ output_stream.PutCString(stream.GetString());
+
+ return stream.GetSize();
+}
+
+size_t StructuredDataDarwinLog::HandleDisplayOfEvent(
+ const StructuredData::Dictionary &event, Stream &stream) {
+ // Check the type of the event.
+ ConstString event_type;
+ if (!event.GetValueForKeyAsString("type", event_type)) {
+ // Hmm, we expected to get events that describe what they are. Continue
+ // anyway.
+ return 0;
+ }
+
+ if (event_type != GetLogEventType())
+ return 0;
+
+ size_t total_bytes = 0;
+
+ // Grab the message content.
+ llvm::StringRef message;
+ if (!event.GetValueForKeyAsString("message", message))
+ return true;
+
+ // Display the log entry.
+ const auto len = message.size();
+
+ total_bytes += DumpHeader(stream, event);
+
+ stream.Write(message.data(), len);
+ total_bytes += len;
+
+ // Add an end of line.
+ stream.PutChar('\n');
+ total_bytes += sizeof(char);
+
+ return total_bytes;
+}
+
+void StructuredDataDarwinLog::EnableNow() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() called", __FUNCTION__);
+
+ // Run the enable command.
+ auto process_sp = GetProcess();
+ if (!process_sp) {
+ // Nothing to do.
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: failed to get "
+ "valid process, skipping",
+ __FUNCTION__);
+ return;
+ }
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() call is for process uid %u",
+ __FUNCTION__, process_sp->GetUniqueID());
+
+ // If we have configuration data, we can directly enable it now. Otherwise,
+ // we need to run through the command interpreter to parse the auto-run
+ // options (which is the only way we get here without having already-parsed
+ // configuration data).
+ DebuggerSP debugger_sp =
+ process_sp->GetTarget().GetDebugger().shared_from_this();
+ if (!debugger_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: failed to get "
+ "debugger shared pointer, skipping (process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ return;
+ }
+
+ auto options_sp = GetGlobalEnableOptions(debugger_sp);
+ if (!options_sp) {
+ // We haven't run the enable command yet. Just do that now, it'll take
+ // care of the rest.
+ auto &interpreter = debugger_sp->GetCommandInterpreter();
+ const bool success = RunEnableCommand(interpreter);
+ if (log) {
+ if (success)
+ log->Printf("StructuredDataDarwinLog::%s() ran enable command "
+ "successfully for (process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ else
+ log->Printf("StructuredDataDarwinLog::%s() error: running "
+ "enable command failed (process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ }
+ // Report failures to the debugger error stream.
+ auto error_stream_sp = debugger_sp->GetAsyncErrorStream();
+ if (error_stream_sp) {
+ error_stream_sp->Printf("failed to configure DarwinLog "
+ "support\n");
+ error_stream_sp->Flush();
+ }
+ return;
+ }
+
+ // We've previously been enabled. We will re-enable now with the previously
+ // specified options.
+ auto config_sp = options_sp->BuildConfigurationData(true);
+ if (!config_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: failed to "
+ "build configuration data for enable options, skipping "
+ "(process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ return;
+ }
+
+ // We can run it directly.
+ // Send configuration to the feature by way of the process.
+ const Status error =
+ process_sp->ConfigureStructuredData(GetDarwinLogTypeName(), config_sp);
+
+ // Report results.
+ if (!error.Success()) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() "
+ "ConfigureStructuredData() call failed "
+ "(process uid %u): %s",
+ __FUNCTION__, process_sp->GetUniqueID(), error.AsCString());
+ auto error_stream_sp = debugger_sp->GetAsyncErrorStream();
+ if (error_stream_sp) {
+ error_stream_sp->Printf("failed to configure DarwinLog "
+ "support: %s\n",
+ error.AsCString());
+ error_stream_sp->Flush();
+ }
+ m_is_enabled = false;
+ } else {
+ m_is_enabled = true;
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() success via direct "
+ "configuration (process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h b/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h
new file mode 100644
index 000000000000..8fa1eed66efb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h
@@ -0,0 +1,118 @@
+//===-- StructuredDataDarwinLog.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef StructuredDataDarwinLog_h
+#define StructuredDataDarwinLog_h
+
+#include "lldb/Target/StructuredDataPlugin.h"
+
+#include <mutex>
+
+// Forward declarations
+namespace sddarwinlog_private {
+class EnableCommand;
+}
+
+namespace lldb_private {
+
+class StructuredDataDarwinLog : public StructuredDataPlugin {
+ friend sddarwinlog_private::EnableCommand;
+
+public:
+ // Public static API
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static ConstString GetStaticPluginName();
+
+ /// Return whether the DarwinLog functionality is enabled.
+ ///
+ /// The DarwinLog functionality is enabled if the user expicitly enabled
+ /// it with the enable command, or if the user has the setting set
+ /// that controls if we always enable it for newly created/attached
+ /// processes.
+ ///
+ /// \return
+ /// True if DarwinLog support is/will be enabled for existing or
+ /// newly launched/attached processes.
+ static bool IsEnabled();
+
+ // PluginInterface API
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // StructuredDataPlugin API
+
+ bool SupportsStructuredDataType(ConstString type_name) override;
+
+ void HandleArrivalOfStructuredData(
+ Process &process, ConstString type_name,
+ const StructuredData::ObjectSP &object_sp) override;
+
+ Status GetDescription(const StructuredData::ObjectSP &object_sp,
+ lldb_private::Stream &stream) override;
+
+ bool GetEnabled(ConstString type_name) const override;
+
+ void ModulesDidLoad(Process &process, ModuleList &module_list) override;
+
+ ~StructuredDataDarwinLog() override;
+
+private:
+ // Private constructors
+
+ StructuredDataDarwinLog(const lldb::ProcessWP &process_wp);
+
+ // Private static methods
+
+ static lldb::StructuredDataPluginSP CreateInstance(Process &process);
+
+ static void DebuggerInitialize(Debugger &debugger);
+
+ static bool InitCompletionHookCallback(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ static Status FilterLaunchInfo(ProcessLaunchInfo &launch_info,
+ Target *target);
+
+ // Internal helper methods used by friend classes
+ void SetEnabled(bool enabled);
+
+ void AddInitCompletionHook(Process &process);
+
+ // Private methods
+
+ void DumpTimestamp(Stream &stream, uint64_t timestamp);
+
+ size_t DumpHeader(Stream &stream, const StructuredData::Dictionary &event);
+
+ size_t HandleDisplayOfEvent(const StructuredData::Dictionary &event,
+ Stream &stream);
+
+ /// Call the enable command again, using whatever settings were initially
+ /// made.
+
+ void EnableNow();
+
+ // Private data
+ bool m_recorded_first_timestamp;
+ uint64_t m_first_timestamp_seen;
+ bool m_is_enabled;
+ std::mutex m_added_breakpoint_mutex;
+ bool m_added_breakpoint;
+ lldb::user_id_t m_breakpoint_id;
+};
+}
+
+#endif /* StructuredDataPluginDarwinLog_hpp */
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
new file mode 100644
index 000000000000..d4258274a38c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
@@ -0,0 +1,653 @@
+//===-- SymbolFileBreakpad.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h"
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/PostfixExpression.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::breakpad;
+
+class SymbolFileBreakpad::LineIterator {
+public:
+ // begin iterator for sections of given type
+ LineIterator(ObjectFile &obj, Record::Kind section_type)
+ : m_obj(&obj), m_section_type(toString(section_type)),
+ m_next_section_idx(0), m_next_line(llvm::StringRef::npos) {
+ ++*this;
+ }
+
+ // An iterator starting at the position given by the bookmark.
+ LineIterator(ObjectFile &obj, Record::Kind section_type, Bookmark bookmark);
+
+ // end iterator
+ explicit LineIterator(ObjectFile &obj)
+ : m_obj(&obj),
+ m_next_section_idx(m_obj->GetSectionList()->GetNumSections(0)),
+ m_current_line(llvm::StringRef::npos),
+ m_next_line(llvm::StringRef::npos) {}
+
+ friend bool operator!=(const LineIterator &lhs, const LineIterator &rhs) {
+ assert(lhs.m_obj == rhs.m_obj);
+ if (lhs.m_next_section_idx != rhs.m_next_section_idx)
+ return true;
+ if (lhs.m_current_line != rhs.m_current_line)
+ return true;
+ assert(lhs.m_next_line == rhs.m_next_line);
+ return false;
+ }
+
+ const LineIterator &operator++();
+ llvm::StringRef operator*() const {
+ return m_section_text.slice(m_current_line, m_next_line);
+ }
+
+ Bookmark GetBookmark() const {
+ return Bookmark{m_next_section_idx, m_current_line};
+ }
+
+private:
+ ObjectFile *m_obj;
+ ConstString m_section_type;
+ uint32_t m_next_section_idx;
+ llvm::StringRef m_section_text;
+ size_t m_current_line;
+ size_t m_next_line;
+
+ void FindNextLine() {
+ m_next_line = m_section_text.find('\n', m_current_line);
+ if (m_next_line != llvm::StringRef::npos) {
+ ++m_next_line;
+ if (m_next_line >= m_section_text.size())
+ m_next_line = llvm::StringRef::npos;
+ }
+ }
+};
+
+SymbolFileBreakpad::LineIterator::LineIterator(ObjectFile &obj,
+ Record::Kind section_type,
+ Bookmark bookmark)
+ : m_obj(&obj), m_section_type(toString(section_type)),
+ m_next_section_idx(bookmark.section), m_current_line(bookmark.offset) {
+ Section &sect =
+ *obj.GetSectionList()->GetSectionAtIndex(m_next_section_idx - 1);
+ assert(sect.GetName() == m_section_type);
+
+ DataExtractor data;
+ obj.ReadSectionData(&sect, data);
+ m_section_text = toStringRef(data.GetData());
+
+ assert(m_current_line < m_section_text.size());
+ FindNextLine();
+}
+
+const SymbolFileBreakpad::LineIterator &
+SymbolFileBreakpad::LineIterator::operator++() {
+ const SectionList &list = *m_obj->GetSectionList();
+ size_t num_sections = list.GetNumSections(0);
+ while (m_next_line != llvm::StringRef::npos ||
+ m_next_section_idx < num_sections) {
+ if (m_next_line != llvm::StringRef::npos) {
+ m_current_line = m_next_line;
+ FindNextLine();
+ return *this;
+ }
+
+ Section &sect = *list.GetSectionAtIndex(m_next_section_idx++);
+ if (sect.GetName() != m_section_type)
+ continue;
+ DataExtractor data;
+ m_obj->ReadSectionData(&sect, data);
+ m_section_text = toStringRef(data.GetData());
+ m_next_line = 0;
+ }
+ // We've reached the end.
+ m_current_line = m_next_line;
+ return *this;
+}
+
+llvm::iterator_range<SymbolFileBreakpad::LineIterator>
+SymbolFileBreakpad::lines(Record::Kind section_type) {
+ return llvm::make_range(LineIterator(*m_obj_file, section_type),
+ LineIterator(*m_obj_file));
+}
+
+namespace {
+// A helper class for constructing the list of support files for a given compile
+// unit.
+class SupportFileMap {
+public:
+ // Given a breakpad file ID, return a file ID to be used in the support files
+ // for this compile unit.
+ size_t operator[](size_t file) {
+ return m_map.try_emplace(file, m_map.size() + 1).first->second;
+ }
+
+ // Construct a FileSpecList containing only the support files relevant for
+ // this compile unit (in the correct order).
+ FileSpecList translate(const FileSpec &cu_spec,
+ llvm::ArrayRef<FileSpec> all_files);
+
+private:
+ llvm::DenseMap<size_t, size_t> m_map;
+};
+} // namespace
+
+FileSpecList SupportFileMap::translate(const FileSpec &cu_spec,
+ llvm::ArrayRef<FileSpec> all_files) {
+ std::vector<FileSpec> result;
+ result.resize(m_map.size() + 1);
+ result[0] = cu_spec;
+ for (const auto &KV : m_map) {
+ if (KV.first < all_files.size())
+ result[KV.second] = all_files[KV.first];
+ }
+ return FileSpecList(std::move(result));
+}
+
+void SymbolFileBreakpad::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+}
+
+void SymbolFileBreakpad::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString SymbolFileBreakpad::GetPluginNameStatic() {
+ static ConstString g_name("breakpad");
+ return g_name;
+}
+
+uint32_t SymbolFileBreakpad::CalculateAbilities() {
+ if (!m_obj_file)
+ return 0;
+ if (m_obj_file->GetPluginName() != ObjectFileBreakpad::GetPluginNameStatic())
+ return 0;
+
+ return CompileUnits | Functions | LineTables;
+}
+
+uint32_t SymbolFileBreakpad::GetNumCompileUnits() {
+ ParseCUData();
+ return m_cu_data->GetSize();
+}
+
+CompUnitSP SymbolFileBreakpad::ParseCompileUnitAtIndex(uint32_t index) {
+ if (index >= m_cu_data->GetSize())
+ return nullptr;
+
+ CompUnitData &data = m_cu_data->GetEntryRef(index).data;
+
+ ParseFileRecords();
+
+ FileSpec spec;
+
+ // The FileSpec of the compile unit will be the file corresponding to the
+ // first LINE record.
+ LineIterator It(*m_obj_file, Record::Func, data.bookmark), End(*m_obj_file);
+ assert(Record::classify(*It) == Record::Func);
+ ++It; // Skip FUNC record.
+ if (It != End) {
+ auto record = LineRecord::parse(*It);
+ if (record && record->FileNum < m_files->size())
+ spec = (*m_files)[record->FileNum];
+ }
+
+ auto cu_sp = std::make_shared<CompileUnit>(m_obj_file->GetModule(),
+ /*user_data*/ nullptr, spec, index,
+ eLanguageTypeUnknown,
+ /*is_optimized*/ eLazyBoolNo);
+
+ GetSymbolVendor().SetCompileUnitAtIndex(index, cu_sp);
+ return cu_sp;
+}
+
+size_t SymbolFileBreakpad::ParseFunctions(CompileUnit &comp_unit) {
+ // TODO
+ return 0;
+}
+
+bool SymbolFileBreakpad::ParseLineTable(CompileUnit &comp_unit) {
+ CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data;
+
+ if (!data.line_table_up)
+ ParseLineTableAndSupportFiles(comp_unit, data);
+
+ comp_unit.SetLineTable(data.line_table_up.release());
+ return true;
+}
+
+bool SymbolFileBreakpad::ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) {
+ CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data;
+ if (!data.support_files)
+ ParseLineTableAndSupportFiles(comp_unit, data);
+
+ support_files = std::move(*data.support_files);
+ return true;
+}
+
+uint32_t
+SymbolFileBreakpad::ResolveSymbolContext(const Address &so_addr,
+ SymbolContextItem resolve_scope,
+ SymbolContext &sc) {
+ if (!(resolve_scope & (eSymbolContextCompUnit | eSymbolContextLineEntry)))
+ return 0;
+
+ ParseCUData();
+ uint32_t idx =
+ m_cu_data->FindEntryIndexThatContains(so_addr.GetFileAddress());
+ if (idx == UINT32_MAX)
+ return 0;
+
+ sc.comp_unit = GetSymbolVendor().GetCompileUnitAtIndex(idx).get();
+ SymbolContextItem result = eSymbolContextCompUnit;
+ if (resolve_scope & eSymbolContextLineEntry) {
+ if (sc.comp_unit->GetLineTable()->FindLineEntryByAddress(so_addr,
+ sc.line_entry)) {
+ result |= eSymbolContextLineEntry;
+ }
+ }
+
+ return result;
+}
+
+uint32_t SymbolFileBreakpad::ResolveSymbolContext(
+ const FileSpec &file_spec, uint32_t line, bool check_inlines,
+ lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
+ if (!(resolve_scope & eSymbolContextCompUnit))
+ return 0;
+
+ uint32_t old_size = sc_list.GetSize();
+ for (size_t i = 0, size = GetNumCompileUnits(); i < size; ++i) {
+ CompileUnit &cu = *GetSymbolVendor().GetCompileUnitAtIndex(i);
+ cu.ResolveSymbolContext(file_spec, line, check_inlines,
+ /*exact*/ false, resolve_scope, sc_list);
+ }
+ return sc_list.GetSize() - old_size;
+}
+
+uint32_t SymbolFileBreakpad::FindFunctions(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask, bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ // TODO
+ if (!append)
+ sc_list.Clear();
+ return sc_list.GetSize();
+}
+
+uint32_t SymbolFileBreakpad::FindFunctions(const RegularExpression &regex,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ // TODO
+ if (!append)
+ sc_list.Clear();
+ return sc_list.GetSize();
+}
+
+uint32_t SymbolFileBreakpad::FindTypes(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<SymbolFile *> &searched_symbol_files, TypeMap &types) {
+ if (!append)
+ types.Clear();
+ return types.GetSize();
+}
+
+size_t
+SymbolFileBreakpad::FindTypes(const std::vector<CompilerContext> &context,
+ bool append, TypeMap &types) {
+ if (!append)
+ types.Clear();
+ return types.GetSize();
+}
+
+void SymbolFileBreakpad::AddSymbols(Symtab &symtab) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+ Module &module = *m_obj_file->GetModule();
+ addr_t base = GetBaseFileAddress();
+ if (base == LLDB_INVALID_ADDRESS) {
+ LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping "
+ "symtab population.");
+ return;
+ }
+
+ const SectionList &list = *module.GetSectionList();
+ llvm::DenseMap<addr_t, Symbol> symbols;
+ auto add_symbol = [&](addr_t address, llvm::Optional<addr_t> size,
+ llvm::StringRef name) {
+ address += base;
+ SectionSP section_sp = list.FindSectionContainingFileAddress(address);
+ if (!section_sp) {
+ LLDB_LOG(log,
+ "Ignoring symbol {0}, whose address ({1}) is outside of the "
+ "object file. Mismatched symbol file?",
+ name, address);
+ return;
+ }
+ symbols.try_emplace(
+ address, /*symID*/ 0, Mangled(name, /*is_mangled*/ false),
+ eSymbolTypeCode, /*is_global*/ true, /*is_debug*/ false,
+ /*is_trampoline*/ false, /*is_artificial*/ false,
+ AddressRange(section_sp, address - section_sp->GetFileAddress(),
+ size.getValueOr(0)),
+ size.hasValue(), /*contains_linker_annotations*/ false, /*flags*/ 0);
+ };
+
+ for (llvm::StringRef line : lines(Record::Func)) {
+ if (auto record = FuncRecord::parse(line))
+ add_symbol(record->Address, record->Size, record->Name);
+ }
+
+ for (llvm::StringRef line : lines(Record::Public)) {
+ if (auto record = PublicRecord::parse(line))
+ add_symbol(record->Address, llvm::None, record->Name);
+ else
+ LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line);
+ }
+
+ for (auto &KV : symbols)
+ symtab.AddSymbol(std::move(KV.second));
+ symtab.CalculateSymbolSizes();
+}
+
+static llvm::Optional<std::pair<llvm::StringRef, llvm::StringRef>>
+GetRule(llvm::StringRef &unwind_rules) {
+ // Unwind rules are of the form
+ // register1: expression1 register2: expression2 ...
+ // We assume none of the tokens in expression<n> end with a colon.
+
+ llvm::StringRef lhs, rest;
+ std::tie(lhs, rest) = getToken(unwind_rules);
+ if (!lhs.consume_back(":"))
+ return llvm::None;
+
+ // Seek forward to the next register: expression pair
+ llvm::StringRef::size_type pos = rest.find(": ");
+ if (pos == llvm::StringRef::npos) {
+ // No pair found, this means the rest of the string is a single expression.
+ unwind_rules = llvm::StringRef();
+ return std::make_pair(lhs, rest);
+ }
+
+ // Go back one token to find the end of the current rule.
+ pos = rest.rfind(' ', pos);
+ if (pos == llvm::StringRef::npos)
+ return llvm::None;
+
+ llvm::StringRef rhs = rest.take_front(pos);
+ unwind_rules = rest.drop_front(pos);
+ return std::make_pair(lhs, rhs);
+}
+
+static const RegisterInfo *
+ResolveRegister(const SymbolFile::RegisterInfoResolver &resolver,
+ llvm::StringRef name) {
+ if (name.consume_front("$"))
+ return resolver.ResolveName(name);
+
+ return nullptr;
+}
+
+static const RegisterInfo *
+ResolveRegisterOrRA(const SymbolFile::RegisterInfoResolver &resolver,
+ llvm::StringRef name) {
+ if (name == ".ra")
+ return resolver.ResolveNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ return ResolveRegister(resolver, name);
+}
+
+bool SymbolFileBreakpad::ParseUnwindRow(llvm::StringRef unwind_rules,
+ const RegisterInfoResolver &resolver,
+ UnwindPlan::Row &row) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+
+ llvm::BumpPtrAllocator node_alloc;
+ while (auto rule = GetRule(unwind_rules)) {
+ node_alloc.Reset();
+ llvm::StringRef lhs = rule->first;
+ postfix::Node *rhs = postfix::Parse(rule->second, node_alloc);
+ if (!rhs) {
+ LLDB_LOG(log, "Could not parse `{0}` as unwind rhs.", rule->second);
+ return false;
+ }
+
+ bool success = postfix::ResolveSymbols(
+ rhs, [&](postfix::SymbolNode &symbol) -> postfix::Node * {
+ llvm::StringRef name = symbol.GetName();
+ if (name == ".cfa" && lhs != ".cfa")
+ return postfix::MakeNode<postfix::InitialValueNode>(node_alloc);
+
+ if (const RegisterInfo *info = ResolveRegister(resolver, name)) {
+ return postfix::MakeNode<postfix::RegisterNode>(
+ node_alloc, info->kinds[eRegisterKindLLDB]);
+ }
+ return nullptr;
+ });
+
+ if (!success) {
+ LLDB_LOG(log, "Resolving symbols in `{0}` failed.", rule->second);
+ return false;
+ }
+
+ ArchSpec arch = m_obj_file->GetArchitecture();
+ StreamString dwarf(Stream::eBinary, arch.GetAddressByteSize(),
+ arch.GetByteOrder());
+ ToDWARF(*rhs, dwarf);
+ uint8_t *saved = m_allocator.Allocate<uint8_t>(dwarf.GetSize());
+ std::memcpy(saved, dwarf.GetData(), dwarf.GetSize());
+
+ if (lhs == ".cfa") {
+ row.GetCFAValue().SetIsDWARFExpression(saved, dwarf.GetSize());
+ } else if (const RegisterInfo *info = ResolveRegisterOrRA(resolver, lhs)) {
+ UnwindPlan::Row::RegisterLocation loc;
+ loc.SetIsDWARFExpression(saved, dwarf.GetSize());
+ row.SetRegisterInfo(info->kinds[eRegisterKindLLDB], loc);
+ } else
+ LLDB_LOG(log, "Invalid register `{0}` in unwind rule.", lhs);
+ }
+ if (unwind_rules.empty())
+ return true;
+
+ LLDB_LOG(log, "Could not parse `{0}` as an unwind rule.", unwind_rules);
+ return false;
+}
+
+UnwindPlanSP
+SymbolFileBreakpad::GetUnwindPlan(const Address &address,
+ const RegisterInfoResolver &resolver) {
+ ParseUnwindData();
+ const UnwindMap::Entry *entry =
+ m_unwind_data->FindEntryThatContains(address.GetFileAddress());
+ if (!entry)
+ return nullptr;
+
+ addr_t base = GetBaseFileAddress();
+ if (base == LLDB_INVALID_ADDRESS)
+ return nullptr;
+
+ LineIterator It(*m_obj_file, Record::StackCFI, entry->data), End(*m_obj_file);
+ llvm::Optional<StackCFIRecord> init_record = StackCFIRecord::parse(*It);
+ assert(init_record.hasValue());
+ assert(init_record->Size.hasValue());
+
+ auto plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindLLDB);
+ plan_sp->SetSourceName("breakpad STACK CFI");
+ plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ plan_sp->SetSourcedFromCompiler(eLazyBoolYes);
+ plan_sp->SetPlanValidAddressRange(
+ AddressRange(base + init_record->Address, *init_record->Size,
+ m_obj_file->GetModule()->GetSectionList()));
+
+ auto row_sp = std::make_shared<UnwindPlan::Row>();
+ row_sp->SetOffset(0);
+ if (!ParseUnwindRow(init_record->UnwindRules, resolver, *row_sp))
+ return nullptr;
+ plan_sp->AppendRow(row_sp);
+ for (++It; It != End; ++It) {
+ llvm::Optional<StackCFIRecord> record = StackCFIRecord::parse(*It);
+ if (!record.hasValue())
+ return nullptr;
+ if (record->Size.hasValue())
+ break;
+
+ row_sp = std::make_shared<UnwindPlan::Row>(*row_sp);
+ row_sp->SetOffset(record->Address - init_record->Address);
+ if (!ParseUnwindRow(record->UnwindRules, resolver, *row_sp))
+ return nullptr;
+ plan_sp->AppendRow(row_sp);
+ }
+ return plan_sp;
+}
+
+SymbolVendor &SymbolFileBreakpad::GetSymbolVendor() {
+ return *m_obj_file->GetModule()->GetSymbolVendor();
+}
+
+addr_t SymbolFileBreakpad::GetBaseFileAddress() {
+ return m_obj_file->GetModule()
+ ->GetObjectFile()
+ ->GetBaseAddress()
+ .GetFileAddress();
+}
+
+// Parse out all the FILE records from the breakpad file. These will be needed
+// when constructing the support file lists for individual compile units.
+void SymbolFileBreakpad::ParseFileRecords() {
+ if (m_files)
+ return;
+ m_files.emplace();
+
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+ for (llvm::StringRef line : lines(Record::File)) {
+ auto record = FileRecord::parse(line);
+ if (!record) {
+ LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line);
+ continue;
+ }
+
+ if (record->Number >= m_files->size())
+ m_files->resize(record->Number + 1);
+ FileSpec::Style style = FileSpec::GuessPathStyle(record->Name)
+ .getValueOr(FileSpec::Style::native);
+ (*m_files)[record->Number] = FileSpec(record->Name, style);
+ }
+}
+
+void SymbolFileBreakpad::ParseCUData() {
+ if (m_cu_data)
+ return;
+
+ m_cu_data.emplace();
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+ addr_t base = GetBaseFileAddress();
+ if (base == LLDB_INVALID_ADDRESS) {
+ LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address "
+ "of object file.");
+ }
+
+ // We shall create one compile unit for each FUNC record. So, count the number
+ // of FUNC records, and store them in m_cu_data, together with their ranges.
+ for (LineIterator It(*m_obj_file, Record::Func), End(*m_obj_file); It != End;
+ ++It) {
+ if (auto record = FuncRecord::parse(*It)) {
+ m_cu_data->Append(CompUnitMap::Entry(base + record->Address, record->Size,
+ CompUnitData(It.GetBookmark())));
+ } else
+ LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It);
+ }
+ m_cu_data->Sort();
+}
+
+// Construct the list of support files and line table entries for the given
+// compile unit.
+void SymbolFileBreakpad::ParseLineTableAndSupportFiles(CompileUnit &cu,
+ CompUnitData &data) {
+ addr_t base = GetBaseFileAddress();
+ assert(base != LLDB_INVALID_ADDRESS &&
+ "How did we create compile units without a base address?");
+
+ SupportFileMap map;
+ data.line_table_up = llvm::make_unique<LineTable>(&cu);
+ std::unique_ptr<LineSequence> line_seq_up(
+ data.line_table_up->CreateLineSequenceContainer());
+ llvm::Optional<addr_t> next_addr;
+ auto finish_sequence = [&]() {
+ data.line_table_up->AppendLineEntryToSequence(
+ line_seq_up.get(), *next_addr, /*line*/ 0, /*column*/ 0,
+ /*file_idx*/ 0, /*is_start_of_statement*/ false,
+ /*is_start_of_basic_block*/ false, /*is_prologue_end*/ false,
+ /*is_epilogue_begin*/ false, /*is_terminal_entry*/ true);
+ data.line_table_up->InsertSequence(line_seq_up.get());
+ line_seq_up->Clear();
+ };
+
+ LineIterator It(*m_obj_file, Record::Func, data.bookmark), End(*m_obj_file);
+ assert(Record::classify(*It) == Record::Func);
+ for (++It; It != End; ++It) {
+ auto record = LineRecord::parse(*It);
+ if (!record)
+ break;
+
+ record->Address += base;
+
+ if (next_addr && *next_addr != record->Address) {
+ // Discontiguous entries. Finish off the previous sequence and reset.
+ finish_sequence();
+ }
+ data.line_table_up->AppendLineEntryToSequence(
+ line_seq_up.get(), record->Address, record->LineNum, /*column*/ 0,
+ map[record->FileNum], /*is_start_of_statement*/ true,
+ /*is_start_of_basic_block*/ false, /*is_prologue_end*/ false,
+ /*is_epilogue_begin*/ false, /*is_terminal_entry*/ false);
+ next_addr = record->Address + record->Size;
+ }
+ if (next_addr)
+ finish_sequence();
+ data.support_files = map.translate(cu, *m_files);
+}
+
+void SymbolFileBreakpad::ParseUnwindData() {
+ if (m_unwind_data)
+ return;
+
+ m_unwind_data.emplace();
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+ addr_t base = GetBaseFileAddress();
+ if (base == LLDB_INVALID_ADDRESS) {
+ LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address "
+ "of object file.");
+ }
+
+ for (LineIterator It(*m_obj_file, Record::StackCFI), End(*m_obj_file);
+ It != End; ++It) {
+ if (auto record = StackCFIRecord::parse(*It)) {
+ if (record->Size)
+ m_unwind_data->Append(UnwindMap::Entry(
+ base + record->Address, *record->Size, It.GetBookmark()));
+ } else
+ LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It);
+ }
+ m_unwind_data->Sort();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
new file mode 100644
index 000000000000..8a0b7645fd0a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
@@ -0,0 +1,222 @@
+//===-- SymbolFileBreakpad.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILE_BREAKPAD_SYMBOLFILEBREAKPAD_H
+#define LLDB_PLUGINS_SYMBOLFILE_BREAKPAD_SYMBOLFILEBREAKPAD_H
+
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/UnwindPlan.h"
+
+namespace lldb_private {
+
+namespace breakpad {
+
+class SymbolFileBreakpad : public SymbolFile {
+public:
+ // Static Functions
+ static void Initialize();
+ static void Terminate();
+ static void DebuggerInitialize(Debugger &debugger) {}
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic() {
+ return "Breakpad debug symbol file reader.";
+ }
+
+ static SymbolFile *CreateInstance(ObjectFile *obj_file) {
+ return new SymbolFileBreakpad(obj_file);
+ }
+
+ // Constructors and Destructors
+ SymbolFileBreakpad(ObjectFile *object_file) : SymbolFile(object_file) {}
+
+ ~SymbolFileBreakpad() override {}
+
+ uint32_t CalculateAbilities() override;
+
+ void InitializeObject() override {}
+
+ // Compile Unit function calls
+
+ uint32_t GetNumCompileUnits() override;
+
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType ParseLanguage(CompileUnit &comp_unit) override {
+ return lldb::eLanguageTypeUnknown;
+ }
+
+ size_t ParseFunctions(CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(CompileUnit &comp_unit) override { return false; }
+
+ bool ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) override;
+ size_t ParseTypes(CompileUnit &cu) override { return 0; }
+
+ bool ParseImportedModules(
+ const SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override {
+ return false;
+ }
+
+ size_t ParseBlocksRecursive(Function &func) override { return 0; }
+
+ uint32_t FindGlobalVariables(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches,
+ VariableList &variables) override {
+ return 0;
+ }
+
+ size_t ParseVariablesForContext(const SymbolContext &sc) override {
+ return 0;
+ }
+ Type *ResolveTypeUID(lldb::user_id_t type_uid) override { return nullptr; }
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override {
+ return llvm::None;
+ }
+
+ bool CompleteType(CompilerType &compiler_type) override { return false; }
+ uint32_t ResolveSymbolContext(const Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ SymbolContext &sc) override;
+
+ uint32_t ResolveSymbolContext(const FileSpec &file_spec, uint32_t line,
+ bool check_inlines,
+ lldb::SymbolContextItem resolve_scope,
+ SymbolContextList &sc_list) override;
+
+ size_t GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask,
+ TypeList &type_list) override {
+ return 0;
+ }
+
+ uint32_t FindFunctions(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ lldb::FunctionNameType name_type_mask,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) override;
+
+ uint32_t FindFunctions(const RegularExpression &regex, bool include_inlines,
+ bool append, SymbolContextList &sc_list) override;
+
+ uint32_t FindTypes(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx, bool append,
+ uint32_t max_matches,
+ llvm::DenseSet<SymbolFile *> &searched_symbol_files,
+ TypeMap &types) override;
+
+ size_t FindTypes(const std::vector<CompilerContext> &context, bool append,
+ TypeMap &types) override;
+
+ TypeSystem *GetTypeSystemForLanguage(lldb::LanguageType language) override {
+ return nullptr;
+ }
+
+ CompilerDeclContext
+ FindNamespace(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx) override {
+ return CompilerDeclContext();
+ }
+
+ void AddSymbols(Symtab &symtab) override;
+
+ lldb::UnwindPlanSP
+ GetUnwindPlan(const Address &address,
+ const RegisterInfoResolver &resolver) override;
+
+ ConstString GetPluginName() override { return GetPluginNameStatic(); }
+ uint32_t GetPluginVersion() override { return 1; }
+
+private:
+ // A class representing a position in the breakpad file. Useful for
+ // remembering the position so we can go back to it later and parse more data.
+ // Can be converted to/from a LineIterator, but it has a much smaller memory
+ // footprint.
+ struct Bookmark {
+ uint32_t section;
+ size_t offset;
+
+ friend bool operator<(const Bookmark &lhs, const Bookmark &rhs) {
+ return std::tie(lhs.section, lhs.offset) <
+ std::tie(rhs.section, rhs.offset);
+ }
+ };
+
+ // At iterator class for simplifying algorithms reading data from the breakpad
+ // file. It iterates over all records (lines) in the sections of a given type.
+ // It also supports saving a specific position (via the GetBookmark() method)
+ // and then resuming from it afterwards.
+ class LineIterator;
+
+ // Return an iterator range for all records in the given object file of the
+ // given type.
+ llvm::iterator_range<LineIterator> lines(Record::Kind section_type);
+
+ // Breakpad files do not contain sufficient information to correctly
+ // reconstruct compile units. The approach chosen here is to treat each
+ // function as a compile unit. The compile unit name is the name if the first
+ // line entry belonging to this function.
+ // This class is our internal representation of a compile unit. It stores the
+ // CompileUnit object and a bookmark pointing to the FUNC record of the
+ // compile unit function. It also lazily construct the list of support files
+ // and line table entries for the compile unit, when these are needed.
+ class CompUnitData {
+ public:
+ CompUnitData(Bookmark bookmark) : bookmark(bookmark) {}
+
+ CompUnitData() = default;
+ CompUnitData(const CompUnitData &rhs) : bookmark(rhs.bookmark) {}
+ CompUnitData &operator=(const CompUnitData &rhs) {
+ bookmark = rhs.bookmark;
+ support_files.reset();
+ line_table_up.reset();
+ return *this;
+ }
+ friend bool operator<(const CompUnitData &lhs, const CompUnitData &rhs) {
+ return lhs.bookmark < rhs.bookmark;
+ }
+
+ Bookmark bookmark;
+ llvm::Optional<FileSpecList> support_files;
+ std::unique_ptr<LineTable> line_table_up;
+
+ };
+
+ SymbolVendor &GetSymbolVendor();
+ lldb::addr_t GetBaseFileAddress();
+ void ParseFileRecords();
+ void ParseCUData();
+ void ParseLineTableAndSupportFiles(CompileUnit &cu, CompUnitData &data);
+ void ParseUnwindData();
+ bool ParseUnwindRow(llvm::StringRef unwind_rules,
+ const RegisterInfoResolver &resolver,
+ UnwindPlan::Row &row);
+
+ using CompUnitMap = RangeDataVector<lldb::addr_t, lldb::addr_t, CompUnitData>;
+
+ llvm::Optional<std::vector<FileSpec>> m_files;
+ llvm::Optional<CompUnitMap> m_cu_data;
+
+ using UnwindMap = RangeDataVector<lldb::addr_t, lldb::addr_t, Bookmark>;
+ llvm::Optional<UnwindMap> m_unwind_data;
+ llvm::BumpPtrAllocator m_allocator;
+};
+
+} // namespace breakpad
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp
new file mode 100644
index 000000000000..9ae047da1325
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp
@@ -0,0 +1,175 @@
+//===-- AppleDWARFIndex.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/SymbolFile/DWARF/AppleDWARFIndex.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
+#include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
+#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Function.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+std::unique_ptr<AppleDWARFIndex> AppleDWARFIndex::Create(
+ Module &module, DWARFDataExtractor apple_names,
+ DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types,
+ DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str) {
+ auto apple_names_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>(
+ apple_names, debug_str, ".apple_names");
+ if (!apple_names_table_up->IsValid())
+ apple_names_table_up.reset();
+
+ auto apple_namespaces_table_up =
+ llvm::make_unique<DWARFMappedHash::MemoryTable>(
+ apple_namespaces, debug_str, ".apple_namespaces");
+ if (!apple_namespaces_table_up->IsValid())
+ apple_namespaces_table_up.reset();
+
+ auto apple_types_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>(
+ apple_types, debug_str, ".apple_types");
+ if (!apple_types_table_up->IsValid())
+ apple_types_table_up.reset();
+
+ auto apple_objc_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>(
+ apple_objc, debug_str, ".apple_objc");
+ if (!apple_objc_table_up->IsValid())
+ apple_objc_table_up.reset();
+
+ if (apple_names_table_up || apple_names_table_up || apple_types_table_up ||
+ apple_objc_table_up)
+ return llvm::make_unique<AppleDWARFIndex>(
+ module, std::move(apple_names_table_up),
+ std::move(apple_namespaces_table_up), std::move(apple_types_table_up),
+ std::move(apple_objc_table_up));
+
+ return nullptr;
+}
+
+void AppleDWARFIndex::GetGlobalVariables(ConstString basename, DIEArray &offsets) {
+ if (m_apple_names_up)
+ m_apple_names_up->FindByName(basename.GetStringRef(), offsets);
+}
+
+void AppleDWARFIndex::GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) {
+ if (!m_apple_names_up)
+ return;
+
+ DWARFMappedHash::DIEInfoArray hash_data;
+ if (m_apple_names_up->AppendAllDIEsThatMatchingRegex(regex, hash_data))
+ DWARFMappedHash::ExtractDIEArray(hash_data, offsets);
+}
+
+void AppleDWARFIndex::GetGlobalVariables(const DWARFUnit &cu,
+ DIEArray &offsets) {
+ if (!m_apple_names_up)
+ return;
+
+ DWARFMappedHash::DIEInfoArray hash_data;
+ if (m_apple_names_up->AppendAllDIEsInRange(cu.GetOffset(),
+ cu.GetNextUnitOffset(), hash_data))
+ DWARFMappedHash::ExtractDIEArray(hash_data, offsets);
+}
+
+void AppleDWARFIndex::GetObjCMethods(ConstString class_name,
+ DIEArray &offsets) {
+ if (m_apple_objc_up)
+ m_apple_objc_up->FindByName(class_name.GetStringRef(), offsets);
+}
+
+void AppleDWARFIndex::GetCompleteObjCClass(ConstString class_name,
+ bool must_be_implementation,
+ DIEArray &offsets) {
+ if (m_apple_types_up) {
+ m_apple_types_up->FindCompleteObjCClassByName(
+ class_name.GetStringRef(), offsets, must_be_implementation);
+ }
+}
+
+void AppleDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) {
+ if (m_apple_types_up)
+ m_apple_types_up->FindByName(name.GetStringRef(), offsets);
+}
+
+void AppleDWARFIndex::GetTypes(const DWARFDeclContext &context,
+ DIEArray &offsets) {
+ if (!m_apple_types_up)
+ return;
+
+ Log *log = LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION |
+ DWARF_LOG_LOOKUPS);
+ const bool has_tag = m_apple_types_up->GetHeader().header_data.ContainsAtom(
+ DWARFMappedHash::eAtomTypeTag);
+ const bool has_qualified_name_hash =
+ m_apple_types_up->GetHeader().header_data.ContainsAtom(
+ DWARFMappedHash::eAtomTypeQualNameHash);
+ const ConstString type_name(context[0].name);
+ const dw_tag_t tag = context[0].tag;
+ if (has_tag && has_qualified_name_hash) {
+ const char *qualified_name = context.GetQualifiedName();
+ const uint32_t qualified_name_hash = llvm::djbHash(qualified_name);
+ if (log)
+ m_module.LogMessage(log, "FindByNameAndTagAndQualifiedNameHash()");
+ m_apple_types_up->FindByNameAndTagAndQualifiedNameHash(
+ type_name.GetStringRef(), tag, qualified_name_hash, offsets);
+ } else if (has_tag) {
+ if (log)
+ m_module.LogMessage(log, "FindByNameAndTag()");
+ m_apple_types_up->FindByNameAndTag(type_name.GetStringRef(), tag, offsets);
+ } else
+ m_apple_types_up->FindByName(type_name.GetStringRef(), offsets);
+}
+
+void AppleDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {
+ if (m_apple_namespaces_up)
+ m_apple_namespaces_up->FindByName(name.GetStringRef(), offsets);
+}
+
+void AppleDWARFIndex::GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) {
+ DIEArray offsets;
+ m_apple_names_up->FindByName(name.GetStringRef(), offsets);
+ for (const DIERef &die_ref : offsets) {
+ ProcessFunctionDIE(name.GetStringRef(), die_ref, dwarf, parent_decl_ctx,
+ name_type_mask, dies);
+ }
+}
+
+void AppleDWARFIndex::GetFunctions(const RegularExpression &regex,
+ DIEArray &offsets) {
+ if (!m_apple_names_up)
+ return;
+
+ DWARFMappedHash::DIEInfoArray hash_data;
+ if (m_apple_names_up->AppendAllDIEsThatMatchingRegex(regex, hash_data))
+ DWARFMappedHash::ExtractDIEArray(hash_data, offsets);
+}
+
+void AppleDWARFIndex::ReportInvalidDIERef(const DIERef &ref,
+ llvm::StringRef name) {
+ m_module.ReportErrorIfModifyDetected(
+ "the DWARF debug information has been modified (accelerator table had "
+ "bad die 0x%8.8x for '%s')\n",
+ ref.die_offset(), name.str().c_str());
+}
+
+void AppleDWARFIndex::Dump(Stream &s) {
+ if (m_apple_names_up)
+ s.PutCString(".apple_names index present\n");
+ if (m_apple_namespaces_up)
+ s.PutCString(".apple_namespaces index present\n");
+ if (m_apple_types_up)
+ s.PutCString(".apple_types index present\n");
+ if (m_apple_objc_up)
+ s.PutCString(".apple_objc index present\n");
+ // TODO: Dump index contents
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h
new file mode 100644
index 000000000000..d15d61e270b3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h
@@ -0,0 +1,62 @@
+//===-- AppleDWARFIndex.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_APPLEDWARFINDEX_H
+#define LLDB_APPLEDWARFINDEX_H
+
+#include "Plugins/SymbolFile/DWARF/DWARFIndex.h"
+#include "Plugins/SymbolFile/DWARF/HashedNameToDIE.h"
+
+namespace lldb_private {
+class AppleDWARFIndex : public DWARFIndex {
+public:
+ static std::unique_ptr<AppleDWARFIndex>
+ Create(Module &module, DWARFDataExtractor apple_names,
+ DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types,
+ DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str);
+
+ AppleDWARFIndex(
+ Module &module, std::unique_ptr<DWARFMappedHash::MemoryTable> apple_names,
+ std::unique_ptr<DWARFMappedHash::MemoryTable> apple_namespaces,
+ std::unique_ptr<DWARFMappedHash::MemoryTable> apple_types,
+ std::unique_ptr<DWARFMappedHash::MemoryTable> apple_objc)
+ : DWARFIndex(module), m_apple_names_up(std::move(apple_names)),
+ m_apple_namespaces_up(std::move(apple_namespaces)),
+ m_apple_types_up(std::move(apple_types)),
+ m_apple_objc_up(std::move(apple_objc)) {}
+
+ void Preload() override {}
+
+ void GetGlobalVariables(ConstString basename, DIEArray &offsets) override;
+ void GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) override;
+ void GetGlobalVariables(const DWARFUnit &cu, DIEArray &offsets) override;
+ void GetObjCMethods(ConstString class_name, DIEArray &offsets) override;
+ void GetCompleteObjCClass(ConstString class_name, bool must_be_implementation,
+ DIEArray &offsets) override;
+ void GetTypes(ConstString name, DIEArray &offsets) override;
+ void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override;
+ void GetNamespaces(ConstString name, DIEArray &offsets) override;
+ void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) override;
+ void GetFunctions(const RegularExpression &regex, DIEArray &offsets) override;
+
+ void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) override;
+ void Dump(Stream &s) override;
+
+private:
+ std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_names_up;
+ std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_namespaces_up;
+ std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_types_up;
+ std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_objc_up;
+};
+} // namespace lldb_private
+
+#endif // LLDB_APPLEDWARFINDEX_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp
new file mode 100644
index 000000000000..f7f2a5bf006b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp
@@ -0,0 +1,18 @@
+//===-- DIERef.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DIERef.h"
+#include "llvm/Support/Format.h"
+
+void llvm::format_provider<DIERef>::format(const DIERef &ref, raw_ostream &OS,
+ StringRef Style) {
+ if (ref.dwo_num())
+ OS << format_hex_no_prefix(*ref.dwo_num(), 8) << "/";
+ OS << (ref.section() == DIERef::DebugInfo ? "INFO" : "TYPE");
+ OS << "/" << format_hex_no_prefix(ref.die_offset(), 8);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h
new file mode 100644
index 000000000000..5546bb7e8b86
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h
@@ -0,0 +1,63 @@
+//===-- DIERef.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DIERef_h_
+#define SymbolFileDWARF_DIERef_h_
+
+#include "lldb/Core/dwarf.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/FormatProviders.h"
+#include <cassert>
+#include <vector>
+
+/// Identifies a DWARF debug info entry within a given Module. It contains three
+/// "coordinates":
+/// - dwo_num: identifies the dwo file in the Module. If this field is not set,
+/// the DIERef references the main file.
+/// - section: identifies the section of the debug info entry in the given file:
+/// debug_info or debug_types.
+/// - die_offset: The offset of the debug info entry as an absolute offset from
+/// the beginning of the section specified in the section field.
+class DIERef {
+public:
+ enum Section : uint8_t { DebugInfo, DebugTypes };
+
+ DIERef(llvm::Optional<uint32_t> dwo_num, Section section,
+ dw_offset_t die_offset)
+ : m_dwo_num(dwo_num.getValueOr(0)), m_dwo_num_valid(bool(dwo_num)),
+ m_section(section), m_die_offset(die_offset) {
+ assert(this->dwo_num() == dwo_num && "Dwo number out of range?");
+ }
+
+ llvm::Optional<uint32_t> dwo_num() const {
+ if (m_dwo_num_valid)
+ return m_dwo_num;
+ return llvm::None;
+ }
+
+ Section section() const { return static_cast<Section>(m_section); }
+
+ dw_offset_t die_offset() const { return m_die_offset; }
+
+private:
+ uint32_t m_dwo_num : 30;
+ uint32_t m_dwo_num_valid : 1;
+ uint32_t m_section : 1;
+ dw_offset_t m_die_offset;
+};
+static_assert(sizeof(DIERef) == 8, "");
+
+typedef std::vector<DIERef> DIEArray;
+
+namespace llvm {
+template<> struct format_provider<DIERef> {
+ static void format(const DIERef &ref, raw_ostream &OS, StringRef Style);
+};
+} // namespace llvm
+
+#endif // SymbolFileDWARF_DIERef_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h
new file mode 100644
index 000000000000..e7927b31b9c3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h
@@ -0,0 +1,59 @@
+//===-- DWARFASTParser.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFASTParser_h_
+#define SymbolFileDWARF_DWARFASTParser_h_
+
+#include "DWARFDefines.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/CompilerDecl.h"
+#include "lldb/Symbol/CompilerDeclContext.h"
+
+class DWARFDIE;
+namespace lldb_private {
+class CompileUnit;
+class ExecutionContext;
+}
+class SymbolFileDWARF;
+
+class DWARFASTParser {
+public:
+ virtual ~DWARFASTParser() {}
+
+ virtual lldb::TypeSP ParseTypeFromDWARF(const lldb_private::SymbolContext &sc,
+ const DWARFDIE &die,
+ lldb_private::Log *log,
+ bool *type_is_new_ptr) = 0;
+
+ virtual lldb_private::Function *
+ ParseFunctionFromDWARF(lldb_private::CompileUnit &comp_unit,
+ const DWARFDIE &die) = 0;
+
+ virtual bool
+ CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type,
+ lldb_private::CompilerType &compiler_type) = 0;
+
+ virtual lldb_private::CompilerDecl
+ GetDeclForUIDFromDWARF(const DWARFDIE &die) = 0;
+
+ virtual lldb_private::CompilerDeclContext
+ GetDeclContextForUIDFromDWARF(const DWARFDIE &die) = 0;
+
+ virtual lldb_private::CompilerDeclContext
+ GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) = 0;
+
+ virtual std::vector<DWARFDIE>
+ GetDIEForDeclContext(lldb_private::CompilerDeclContext decl_context) = 0;
+
+ static llvm::Optional<lldb_private::SymbolFile::ArrayInfo>
+ ParseChildArrayInfo(const DWARFDIE &parent_die,
+ const lldb_private::ExecutionContext *exe_ctx = nullptr);
+};
+
+#endif // SymbolFileDWARF_DWARFASTParser_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
new file mode 100644
index 000000000000..b85ab54a10d3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -0,0 +1,3885 @@
+//===-- DWARFASTParserClang.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdlib.h>
+
+#include "DWARFASTParserClang.h"
+#include "DWARFDIE.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFDeclContext.h"
+#include "DWARFDefines.h"
+#include "SymbolFileDWARF.h"
+#include "SymbolFileDWARFDwo.h"
+#include "SymbolFileDWARFDebugMap.h"
+#include "UniqueDWARFASTType.h"
+
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN
+
+#ifdef ENABLE_DEBUG_PRINTF
+#include <stdio.h>
+#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
+#else
+#define DEBUG_PRINTF(fmt, ...)
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+DWARFASTParserClang::DWARFASTParserClang(ClangASTContext &ast)
+ : m_ast(ast), m_die_to_decl_ctx(), m_decl_ctx_to_die() {}
+
+DWARFASTParserClang::~DWARFASTParserClang() {}
+
+static AccessType DW_ACCESS_to_AccessType(uint32_t dwarf_accessibility) {
+ switch (dwarf_accessibility) {
+ case DW_ACCESS_public:
+ return eAccessPublic;
+ case DW_ACCESS_private:
+ return eAccessPrivate;
+ case DW_ACCESS_protected:
+ return eAccessProtected;
+ default:
+ break;
+ }
+ return eAccessNone;
+}
+
+static bool DeclKindIsCXXClass(clang::Decl::Kind decl_kind) {
+ switch (decl_kind) {
+ case clang::Decl::CXXRecord:
+ case clang::Decl::ClassTemplateSpecialization:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+struct BitfieldInfo {
+ uint64_t bit_size;
+ uint64_t bit_offset;
+
+ BitfieldInfo()
+ : bit_size(LLDB_INVALID_ADDRESS), bit_offset(LLDB_INVALID_ADDRESS) {}
+
+ void Clear() {
+ bit_size = LLDB_INVALID_ADDRESS;
+ bit_offset = LLDB_INVALID_ADDRESS;
+ }
+
+ bool IsValid() const {
+ return (bit_size != LLDB_INVALID_ADDRESS) &&
+ (bit_offset != LLDB_INVALID_ADDRESS);
+ }
+
+ bool NextBitfieldOffsetIsValid(const uint64_t next_bit_offset) const {
+ if (IsValid()) {
+ // This bitfield info is valid, so any subsequent bitfields must not
+ // overlap and must be at a higher bit offset than any previous bitfield
+ // + size.
+ return (bit_size + bit_offset) <= next_bit_offset;
+ } else {
+ // If the this BitfieldInfo is not valid, then any offset isOK
+ return true;
+ }
+ }
+};
+
+ClangASTImporter &DWARFASTParserClang::GetClangASTImporter() {
+ if (!m_clang_ast_importer_up) {
+ m_clang_ast_importer_up.reset(new ClangASTImporter);
+ }
+ return *m_clang_ast_importer_up;
+}
+
+/// Detect a forward declaration that is nested in a DW_TAG_module.
+static bool IsClangModuleFwdDecl(const DWARFDIE &Die) {
+ if (!Die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0))
+ return false;
+ auto Parent = Die.GetParent();
+ while (Parent.IsValid()) {
+ if (Parent.Tag() == DW_TAG_module)
+ return true;
+ Parent = Parent.GetParent();
+ }
+ return false;
+}
+
+TypeSP DWARFASTParserClang::ParseTypeFromDWO(const DWARFDIE &die, Log *log) {
+ ModuleSP dwo_module_sp = die.GetContainingDWOModule();
+ if (!dwo_module_sp)
+ return TypeSP();
+
+ // If this type comes from a Clang module, look in the DWARF section
+ // of the pcm file in the module cache. Clang generates DWO skeleton
+ // units as breadcrumbs to find them.
+ std::vector<CompilerContext> decl_context;
+ die.GetDeclContext(decl_context);
+ TypeMap dwo_types;
+
+ if (!dwo_module_sp->GetSymbolVendor()->FindTypes(decl_context, true,
+ dwo_types)) {
+ if (!IsClangModuleFwdDecl(die))
+ return TypeSP();
+
+ // Since this this type is defined in one of the Clang modules imported by
+ // this symbol file, search all of them.
+ auto &sym_file = die.GetCU()->GetSymbolFileDWARF();
+ for (const auto &name_module : sym_file.getExternalTypeModules()) {
+ if (!name_module.second)
+ continue;
+ SymbolVendor *sym_vendor = name_module.second->GetSymbolVendor();
+ if (sym_vendor->FindTypes(decl_context, true, dwo_types))
+ break;
+ }
+ }
+
+ if (dwo_types.GetSize() != 1)
+ return TypeSP();
+
+ // We found a real definition for this type in the Clang module, so lets use
+ // it and cache the fact that we found a complete type for this die.
+ TypeSP dwo_type_sp = dwo_types.GetTypeAtIndex(0);
+ if (!dwo_type_sp)
+ return TypeSP();
+
+ lldb_private::CompilerType dwo_type = dwo_type_sp->GetForwardCompilerType();
+
+ lldb_private::CompilerType type =
+ GetClangASTImporter().CopyType(m_ast, dwo_type);
+
+ if (!type)
+ return TypeSP();
+
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ TypeSP type_sp(new Type(
+ die.GetID(), dwarf, dwo_type_sp->GetName(), dwo_type_sp->GetByteSize(),
+ nullptr, LLDB_INVALID_UID, Type::eEncodingInvalid,
+ &dwo_type_sp->GetDeclaration(), type, Type::eResolveStateForward));
+
+ dwarf->GetTypeList()->Insert(type_sp);
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ clang::TagDecl *tag_decl = ClangASTContext::GetAsTagDecl(type);
+ if (tag_decl)
+ LinkDeclContextToDIE(tag_decl, die);
+ else {
+ clang::DeclContext *defn_decl_ctx = GetCachedClangDeclContextForDIE(die);
+ if (defn_decl_ctx)
+ LinkDeclContextToDIE(defn_decl_ctx, die);
+ }
+
+ return type_sp;
+}
+
+static void CompleteExternalTagDeclType(ClangASTImporter &ast_importer,
+ clang::DeclContext *decl_ctx,
+ DWARFDIE die,
+ const char *type_name_cstr) {
+ auto *tag_decl_ctx = clang::dyn_cast<clang::TagDecl>(decl_ctx);
+ if (!tag_decl_ctx)
+ return;
+
+ // If this type was not imported from an external AST, there's nothing to do.
+ CompilerType type = ClangASTContext::GetTypeForDecl(tag_decl_ctx);
+ if (!type || !ast_importer.CanImport(type))
+ return;
+
+ auto qual_type = ClangUtil::GetQualType(type);
+ if (!ast_importer.RequireCompleteType(qual_type)) {
+ die.GetDWARF()->GetObjectFile()->GetModule()->ReportError(
+ "Unable to complete the Decl context for DIE '%s' at offset "
+ "0x%8.8x.\nPlease file a bug report.",
+ type_name_cstr ? type_name_cstr : "", die.GetOffset());
+ // We need to make the type look complete otherwise, we might crash in
+ // Clang when adding children.
+ if (ClangASTContext::StartTagDeclarationDefinition(type))
+ ClangASTContext::CompleteTagDeclarationDefinition(type);
+ }
+}
+
+namespace {
+/// Parsed form of all attributes that are relevant for type reconstruction.
+/// Some attributes are relevant for all kinds of types (declaration), while
+/// others are only meaningful to a specific type (is_virtual)
+struct ParsedTypeAttributes {
+ explicit ParsedTypeAttributes(const DWARFDIE &die);
+
+ AccessType accessibility = eAccessNone;
+ bool is_artificial = false;
+ bool is_complete_objc_class = false;
+ bool is_explicit = false;
+ bool is_forward_declaration = false;
+ bool is_inline = false;
+ bool is_scoped_enum = false;
+ bool is_vector = false;
+ bool is_virtual = false;
+ clang::StorageClass storage = clang::SC_None;
+ const char *mangled_name = nullptr;
+ ConstString name;
+ Declaration decl;
+ DWARFDIE object_pointer;
+ DWARFFormValue abstract_origin;
+ DWARFFormValue containing_type;
+ DWARFFormValue signature;
+ DWARFFormValue specification;
+ DWARFFormValue type;
+ LanguageType class_language = eLanguageTypeUnknown;
+ llvm::Optional<uint64_t> byte_size;
+ size_t calling_convention = llvm::dwarf::DW_CC_normal;
+ uint32_t bit_stride = 0;
+ uint32_t byte_stride = 0;
+ uint32_t encoding = 0;
+};
+} // namespace
+
+ParsedTypeAttributes::ParsedTypeAttributes(const DWARFDIE &die) {
+ DWARFAttributes attributes;
+ size_t num_attributes = die.GetAttributes(attributes);
+ for (size_t i = 0; i < num_attributes; ++i) {
+ dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (!attributes.ExtractFormValueAtIndex(i, form_value))
+ continue;
+ switch (attr) {
+ case DW_AT_abstract_origin:
+ abstract_origin = form_value;
+ break;
+
+ case DW_AT_accessibility:
+ accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned());
+ break;
+
+ case DW_AT_artificial:
+ is_artificial = form_value.Boolean();
+ break;
+
+ case DW_AT_bit_stride:
+ bit_stride = form_value.Unsigned();
+ break;
+
+ case DW_AT_byte_size:
+ byte_size = form_value.Unsigned();
+ break;
+
+ case DW_AT_byte_stride:
+ byte_stride = form_value.Unsigned();
+ break;
+
+ case DW_AT_calling_convention:
+ calling_convention = form_value.Unsigned();
+ break;
+
+ case DW_AT_containing_type:
+ containing_type = form_value;
+ break;
+
+ case DW_AT_decl_file:
+ decl.SetFile(die.GetCU()->GetFile(form_value.Unsigned()));
+ break;
+ case DW_AT_decl_line:
+ decl.SetLine(form_value.Unsigned());
+ break;
+ case DW_AT_decl_column:
+ decl.SetColumn(form_value.Unsigned());
+ break;
+
+ case DW_AT_declaration:
+ is_forward_declaration = form_value.Boolean();
+ break;
+
+ case DW_AT_encoding:
+ encoding = form_value.Unsigned();
+ break;
+
+ case DW_AT_enum_class:
+ is_scoped_enum = form_value.Boolean();
+ break;
+
+ case DW_AT_explicit:
+ is_explicit = form_value.Boolean();
+ break;
+
+ case DW_AT_external:
+ if (form_value.Unsigned())
+ storage = clang::SC_Extern;
+ break;
+
+ case DW_AT_inline:
+ is_inline = form_value.Boolean();
+ break;
+
+ case DW_AT_linkage_name:
+ case DW_AT_MIPS_linkage_name:
+ mangled_name = form_value.AsCString();
+ break;
+
+ case DW_AT_name:
+ name.SetCString(form_value.AsCString());
+ break;
+
+ case DW_AT_object_pointer:
+ object_pointer = form_value.Reference();
+ break;
+
+ case DW_AT_signature:
+ signature = form_value;
+ break;
+
+ case DW_AT_specification:
+ specification = form_value;
+ break;
+
+ case DW_AT_type:
+ type = form_value;
+ break;
+
+ case DW_AT_virtuality:
+ is_virtual = form_value.Boolean();
+ break;
+
+ case DW_AT_APPLE_objc_complete_type:
+ is_complete_objc_class = form_value.Signed();
+ break;
+
+ case DW_AT_APPLE_runtime_class:
+ class_language = (LanguageType)form_value.Signed();
+ break;
+
+ case DW_AT_GNU_vector:
+ is_vector = form_value.Boolean();
+ break;
+ }
+ }
+}
+
+static std::string GetUnitName(const DWARFDIE &die) {
+ if (DWARFUnit *unit = die.GetCU())
+ return unit->GetAbsolutePath().GetPath();
+ return "<missing DWARF unit path>";
+}
+
+TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc,
+ const DWARFDIE &die, Log *log,
+ bool *type_is_new_ptr) {
+ if (type_is_new_ptr)
+ *type_is_new_ptr = false;
+
+ if (!die)
+ return nullptr;
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ if (log) {
+ DWARFDIE context_die;
+ clang::DeclContext *context =
+ GetClangDeclContextContainingDIE(die, &context_die);
+
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::ParseType (die = 0x%8.8x, decl_ctx = %p (die "
+ "0x%8.8x)) %s name = '%s')",
+ die.GetOffset(), static_cast<void *>(context), context_die.GetOffset(),
+ die.GetTagAsCString(), die.GetName());
+ }
+
+
+ Type *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE());
+ if (type_ptr == DIE_IS_BEING_PARSED)
+ return nullptr;
+ if (type_ptr)
+ return type_ptr->shared_from_this();
+ // Set a bit that lets us know that we are currently parsing this
+ dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED;
+
+ ParsedTypeAttributes attrs(die);
+
+ if (DWARFDIE signature_die = attrs.signature.Reference()) {
+ if (TypeSP type_sp =
+ ParseTypeFromDWARF(sc, signature_die, log, type_is_new_ptr)) {
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ if (clang::DeclContext *decl_ctx =
+ GetCachedClangDeclContextForDIE(signature_die))
+ LinkDeclContextToDIE(decl_ctx, die);
+ return type_sp;
+ }
+ return nullptr;
+ }
+
+ TypeList *type_list = dwarf->GetTypeList();
+ if (type_is_new_ptr)
+ *type_is_new_ptr = true;
+
+ const dw_tag_t tag = die.Tag();
+
+ Type::ResolveState resolve_state = Type::eResolveStateUnresolved;
+
+ Type::EncodingDataType encoding_data_type = Type::eEncodingIsUID;
+ CompilerType clang_type;
+
+ TypeSP type_sp;
+ LanguageType cu_language = die.GetLanguage();
+ switch (tag) {
+ case DW_TAG_typedef:
+ case DW_TAG_base_type:
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type:
+ case DW_TAG_rvalue_reference_type:
+ case DW_TAG_const_type:
+ case DW_TAG_restrict_type:
+ case DW_TAG_volatile_type:
+ case DW_TAG_unspecified_type: {
+ if (tag == DW_TAG_typedef && attrs.type.IsValid()) {
+ // Try to parse a typedef from the DWO file first as modules can
+ // contain typedef'ed structures that have no names like:
+ //
+ // typedef struct { int a; } Foo;
+ //
+ // In this case we will have a structure with no name and a typedef
+ // named "Foo" that points to this unnamed structure. The name in the
+ // typedef is the only identifier for the struct, so always try to
+ // get typedefs from DWO files if possible.
+ //
+ // The type_sp returned will be empty if the typedef doesn't exist in
+ // a DWO file, so it is cheap to call this function just to check.
+ //
+ // If we don't do this we end up creating a TypeSP that says this is
+ // a typedef to type 0x123 (the DW_AT_type value would be 0x123 in
+ // the DW_TAG_typedef), and this is the unnamed structure type. We
+ // will have a hard time tracking down an unnammed structure type in
+ // the module DWO file, so we make sure we don't get into this
+ // situation by always resolving typedefs from the DWO file.
+ const DWARFDIE encoding_die = attrs.type.Reference();
+
+ // First make sure that the die that this is typedef'ed to _is_ just
+ // a declaration (DW_AT_declaration == 1), not a full definition
+ // since template types can't be represented in modules since only
+ // concrete instances of templates are ever emitted and modules won't
+ // contain those
+ if (encoding_die &&
+ encoding_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) {
+ type_sp = ParseTypeFromDWO(die, log);
+ if (type_sp)
+ return type_sp;
+ }
+ }
+
+ DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\") type => 0x%8.8lx\n",
+ die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr,
+ encoding_uid.Reference());
+
+ switch (tag) {
+ default:
+ break;
+
+ case DW_TAG_unspecified_type:
+ if (attrs.name == "nullptr_t" || attrs.name == "decltype(nullptr)") {
+ resolve_state = Type::eResolveStateFull;
+ clang_type = m_ast.GetBasicType(eBasicTypeNullPtr);
+ break;
+ }
+ // Fall through to base type below in case we can handle the type
+ // there...
+ LLVM_FALLTHROUGH;
+
+ case DW_TAG_base_type:
+ resolve_state = Type::eResolveStateFull;
+ clang_type = m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize(
+ attrs.name.GetCString(), attrs.encoding,
+ attrs.byte_size.getValueOr(0) * 8);
+ break;
+
+ case DW_TAG_pointer_type:
+ encoding_data_type = Type::eEncodingIsPointerUID;
+ break;
+ case DW_TAG_reference_type:
+ encoding_data_type = Type::eEncodingIsLValueReferenceUID;
+ break;
+ case DW_TAG_rvalue_reference_type:
+ encoding_data_type = Type::eEncodingIsRValueReferenceUID;
+ break;
+ case DW_TAG_typedef:
+ encoding_data_type = Type::eEncodingIsTypedefUID;
+ break;
+ case DW_TAG_const_type:
+ encoding_data_type = Type::eEncodingIsConstUID;
+ break;
+ case DW_TAG_restrict_type:
+ encoding_data_type = Type::eEncodingIsRestrictUID;
+ break;
+ case DW_TAG_volatile_type:
+ encoding_data_type = Type::eEncodingIsVolatileUID;
+ break;
+ }
+
+ if (!clang_type && (encoding_data_type == Type::eEncodingIsPointerUID ||
+ encoding_data_type == Type::eEncodingIsTypedefUID)) {
+ if (tag == DW_TAG_pointer_type) {
+ DWARFDIE target_die = die.GetReferencedDIE(DW_AT_type);
+
+ if (target_die.GetAttributeValueAsUnsigned(DW_AT_APPLE_block, 0)) {
+ // Blocks have a __FuncPtr inside them which is a pointer to a
+ // function of the proper type.
+
+ for (DWARFDIE child_die = target_die.GetFirstChild();
+ child_die.IsValid(); child_die = child_die.GetSibling()) {
+ if (!strcmp(child_die.GetAttributeValueAsString(DW_AT_name, ""),
+ "__FuncPtr")) {
+ DWARFDIE function_pointer_type =
+ child_die.GetReferencedDIE(DW_AT_type);
+
+ if (function_pointer_type) {
+ DWARFDIE function_type =
+ function_pointer_type.GetReferencedDIE(DW_AT_type);
+
+ bool function_type_is_new_pointer;
+ TypeSP lldb_function_type_sp = ParseTypeFromDWARF(
+ sc, function_type, log, &function_type_is_new_pointer);
+
+ if (lldb_function_type_sp) {
+ clang_type = m_ast.CreateBlockPointerType(
+ lldb_function_type_sp->GetForwardCompilerType());
+ encoding_data_type = Type::eEncodingIsUID;
+ attrs.type.Clear();
+ resolve_state = Type::eResolveStateFull;
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ if (cu_language == eLanguageTypeObjC ||
+ cu_language == eLanguageTypeObjC_plus_plus) {
+ if (attrs.name) {
+ static ConstString g_objc_type_name_id("id");
+ static ConstString g_objc_type_name_Class("Class");
+ static ConstString g_objc_type_name_selector("SEL");
+
+ if (attrs.name == g_objc_type_name_id) {
+ if (log)
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' "
+ "is Objective-C 'id' built-in type.",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+ clang_type = m_ast.GetBasicType(eBasicTypeObjCID);
+ encoding_data_type = Type::eEncodingIsUID;
+ attrs.type.Clear();
+ resolve_state = Type::eResolveStateFull;
+
+ } else if (attrs.name == g_objc_type_name_Class) {
+ if (log)
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' "
+ "is Objective-C 'Class' built-in type.",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+ clang_type = m_ast.GetBasicType(eBasicTypeObjCClass);
+ encoding_data_type = Type::eEncodingIsUID;
+ attrs.type.Clear();
+ resolve_state = Type::eResolveStateFull;
+ } else if (attrs.name == g_objc_type_name_selector) {
+ if (log)
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' "
+ "is Objective-C 'selector' built-in type.",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+ clang_type = m_ast.GetBasicType(eBasicTypeObjCSel);
+ encoding_data_type = Type::eEncodingIsUID;
+ attrs.type.Clear();
+ resolve_state = Type::eResolveStateFull;
+ }
+ } else if (encoding_data_type == Type::eEncodingIsPointerUID &&
+ attrs.type.IsValid()) {
+ // Clang sometimes erroneously emits id as objc_object*. In that
+ // case we fix up the type to "id".
+
+ const DWARFDIE encoding_die = attrs.type.Reference();
+
+ if (encoding_die && encoding_die.Tag() == DW_TAG_structure_type) {
+ if (const char *struct_name = encoding_die.GetName()) {
+ if (!strcmp(struct_name, "objc_object")) {
+ if (log)
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s "
+ "'%s' is 'objc_object*', which we overrode to "
+ "'id'.",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+ clang_type = m_ast.GetBasicType(eBasicTypeObjCID);
+ encoding_data_type = Type::eEncodingIsUID;
+ attrs.type.Clear();
+ resolve_state = Type::eResolveStateFull;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ type_sp = std::make_shared<Type>(
+ die.GetID(), dwarf, attrs.name, attrs.byte_size, nullptr,
+ dwarf->GetUID(attrs.type.Reference()), encoding_data_type, &attrs.decl,
+ clang_type, resolve_state);
+
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ } break;
+
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type: {
+ // UniqueDWARFASTType is large, so don't create a local variables on
+ // the stack, put it on the heap. This function is often called
+ // recursively and clang isn't good and sharing the stack space for
+ // variables in different blocks.
+ std::unique_ptr<UniqueDWARFASTType> unique_ast_entry_up(
+ new UniqueDWARFASTType());
+
+ ConstString unique_typename(attrs.name);
+ Declaration unique_decl(attrs.decl);
+
+ if (attrs.name) {
+ if (Language::LanguageIsCPlusPlus(cu_language)) {
+ // For C++, we rely solely upon the one definition rule that says
+ // only one thing can exist at a given decl context. We ignore the
+ // file and line that things are declared on.
+ std::string qualified_name;
+ if (die.GetQualifiedName(qualified_name))
+ unique_typename = ConstString(qualified_name);
+ unique_decl.Clear();
+ }
+
+ if (dwarf->GetUniqueDWARFASTTypeMap().Find(
+ unique_typename, die, unique_decl, attrs.byte_size.getValueOr(-1),
+ *unique_ast_entry_up)) {
+ type_sp = unique_ast_entry_up->m_type_sp;
+ if (type_sp) {
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ return type_sp;
+ }
+ }
+ }
+
+ DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
+ DW_TAG_value_to_name(tag), type_name_cstr);
+
+ int tag_decl_kind = -1;
+ AccessType default_accessibility = eAccessNone;
+ if (tag == DW_TAG_structure_type) {
+ tag_decl_kind = clang::TTK_Struct;
+ default_accessibility = eAccessPublic;
+ } else if (tag == DW_TAG_union_type) {
+ tag_decl_kind = clang::TTK_Union;
+ default_accessibility = eAccessPublic;
+ } else if (tag == DW_TAG_class_type) {
+ tag_decl_kind = clang::TTK_Class;
+ default_accessibility = eAccessPrivate;
+ }
+
+ if (attrs.byte_size && *attrs.byte_size == 0 && attrs.name &&
+ !die.HasChildren() && cu_language == eLanguageTypeObjC) {
+ // Work around an issue with clang at the moment where forward
+ // declarations for objective C classes are emitted as:
+ // DW_TAG_structure_type [2]
+ // DW_AT_name( "ForwardObjcClass" )
+ // DW_AT_byte_size( 0x00 )
+ // DW_AT_decl_file( "..." )
+ // DW_AT_decl_line( 1 )
+ //
+ // Note that there is no DW_AT_declaration and there are no children,
+ // and the byte size is zero.
+ attrs.is_forward_declaration = true;
+ }
+
+ if (attrs.class_language == eLanguageTypeObjC ||
+ attrs.class_language == eLanguageTypeObjC_plus_plus) {
+ if (!attrs.is_complete_objc_class &&
+ die.Supports_DW_AT_APPLE_objc_complete_type()) {
+ // We have a valid eSymbolTypeObjCClass class symbol whose name
+ // matches the current objective C class that we are trying to find
+ // and this DIE isn't the complete definition (we checked
+ // is_complete_objc_class above and know it is false), so the real
+ // definition is in here somewhere
+ type_sp =
+ dwarf->FindCompleteObjCDefinitionTypeForDIE(die, attrs.name, true);
+
+ if (!type_sp) {
+ SymbolFileDWARFDebugMap *debug_map_symfile =
+ dwarf->GetDebugMapSymfile();
+ if (debug_map_symfile) {
+ // We weren't able to find a full declaration in this DWARF,
+ // see if we have a declaration anywhere else...
+ type_sp = debug_map_symfile->FindCompleteObjCDefinitionTypeForDIE(
+ die, attrs.name, true);
+ }
+ }
+
+ if (type_sp) {
+ if (log) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is an "
+ "incomplete objc type, complete type is 0x%8.8" PRIx64,
+ static_cast<void *>(this), die.GetOffset(),
+ DW_TAG_value_to_name(tag), attrs.name.GetCString(),
+ type_sp->GetID());
+ }
+
+ // We found a real definition for this type elsewhere so lets use
+ // it and cache the fact that we found a complete type for this
+ // die
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ return type_sp;
+ }
+ }
+ }
+
+ if (attrs.is_forward_declaration) {
+ // We have a forward declaration to a type and we need to try and
+ // find a full declaration. We look in the current type index just in
+ // case we have a forward declaration followed by an actual
+ // declarations in the DWARF. If this fails, we need to look
+ // elsewhere...
+ if (log) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a "
+ "forward declaration, trying to find complete type",
+ static_cast<void *>(this), die.GetOffset(),
+ DW_TAG_value_to_name(tag), attrs.name.GetCString());
+ }
+
+ // See if the type comes from a DWO module and if so, track down that
+ // type.
+ type_sp = ParseTypeFromDWO(die, log);
+ if (type_sp)
+ return type_sp;
+
+ DWARFDeclContext die_decl_ctx;
+ die.GetDWARFDeclContext(die_decl_ctx);
+
+ // type_sp = FindDefinitionTypeForDIE (dwarf_cu, die,
+ // type_name_const_str);
+ type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx);
+
+ if (!type_sp) {
+ SymbolFileDWARFDebugMap *debug_map_symfile =
+ dwarf->GetDebugMapSymfile();
+ if (debug_map_symfile) {
+ // We weren't able to find a full declaration in this DWARF, see
+ // if we have a declaration anywhere else...
+ type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext(
+ die_decl_ctx);
+ }
+ }
+
+ if (type_sp) {
+ if (log) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a "
+ "forward declaration, complete type is 0x%8.8" PRIx64,
+ static_cast<void *>(this), die.GetOffset(),
+ DW_TAG_value_to_name(tag), attrs.name.GetCString(),
+ type_sp->GetID());
+ }
+
+ // We found a real definition for this type elsewhere so lets use
+ // it and cache the fact that we found a complete type for this die
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ clang::DeclContext *defn_decl_ctx =
+ GetCachedClangDeclContextForDIE(dwarf->GetDIE(type_sp->GetID()));
+ if (defn_decl_ctx)
+ LinkDeclContextToDIE(defn_decl_ctx, die);
+ return type_sp;
+ }
+ }
+ assert(tag_decl_kind != -1);
+ bool clang_type_was_created = false;
+ clang_type.SetCompilerType(
+ &m_ast, dwarf->GetForwardDeclDieToClangType().lookup(die.GetDIE()));
+ if (!clang_type) {
+ clang::DeclContext *decl_ctx =
+ GetClangDeclContextContainingDIE(die, nullptr);
+
+ // If your decl context is a record that was imported from another
+ // AST context (in the gmodules case), we need to make sure the type
+ // backing the Decl is complete before adding children to it. This is
+ // not an issue in the non-gmodules case because the debug info will
+ // always contain a full definition of parent types in that case.
+ CompleteExternalTagDeclType(GetClangASTImporter(), decl_ctx, die,
+ attrs.name.GetCString());
+
+ if (attrs.accessibility == eAccessNone && decl_ctx) {
+ // Check the decl context that contains this class/struct/union. If
+ // it is a class we must give it an accessibility.
+ const clang::Decl::Kind containing_decl_kind = decl_ctx->getDeclKind();
+ if (DeclKindIsCXXClass(containing_decl_kind))
+ attrs.accessibility = default_accessibility;
+ }
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(die.GetID());
+ metadata.SetIsDynamicCXXType(dwarf->ClassOrStructIsVirtual(die));
+
+ if (attrs.name.GetStringRef().contains('<')) {
+ ClangASTContext::TemplateParameterInfos template_param_infos;
+ if (ParseTemplateParameterInfos(die, template_param_infos)) {
+ clang::ClassTemplateDecl *class_template_decl =
+ m_ast.ParseClassTemplateDecl(decl_ctx, attrs.accessibility,
+ attrs.name.GetCString(),
+ tag_decl_kind, template_param_infos);
+ if (!class_template_decl) {
+ if (log) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" "
+ "clang::ClassTemplateDecl failed to return a decl.",
+ static_cast<void *>(this), die.GetOffset(),
+ DW_TAG_value_to_name(tag), attrs.name.GetCString());
+ }
+ return TypeSP();
+ }
+
+ clang::ClassTemplateSpecializationDecl *class_specialization_decl =
+ m_ast.CreateClassTemplateSpecializationDecl(
+ decl_ctx, class_template_decl, tag_decl_kind,
+ template_param_infos);
+ clang_type = m_ast.CreateClassTemplateSpecializationType(
+ class_specialization_decl);
+ clang_type_was_created = true;
+
+ m_ast.SetMetadata(class_template_decl, metadata);
+ m_ast.SetMetadata(class_specialization_decl, metadata);
+ }
+ }
+
+ if (!clang_type_was_created) {
+ clang_type_was_created = true;
+ clang_type = m_ast.CreateRecordType(
+ decl_ctx, attrs.accessibility, attrs.name.GetCString(),
+ tag_decl_kind, attrs.class_language, &metadata);
+ }
+ }
+
+ // Store a forward declaration to this class type in case any
+ // parameters in any class methods need it for the clang types for
+ // function prototypes.
+ LinkDeclContextToDIE(m_ast.GetDeclContextForType(clang_type), die);
+ type_sp = std::make_shared<Type>(die.GetID(), dwarf, attrs.name,
+ attrs.byte_size, nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, &attrs.decl, clang_type,
+ Type::eResolveStateForward);
+
+ type_sp->SetIsCompleteObjCClass(attrs.is_complete_objc_class);
+
+ // Add our type to the unique type map so we don't end up creating many
+ // copies of the same type over and over in the ASTContext for our
+ // module
+ unique_ast_entry_up->m_type_sp = type_sp;
+ unique_ast_entry_up->m_die = die;
+ unique_ast_entry_up->m_declaration = unique_decl;
+ unique_ast_entry_up->m_byte_size = attrs.byte_size.getValueOr(0);
+ dwarf->GetUniqueDWARFASTTypeMap().Insert(unique_typename,
+ *unique_ast_entry_up);
+
+ if (attrs.is_forward_declaration && die.HasChildren()) {
+ // Check to see if the DIE actually has a definition, some version of
+ // GCC will
+ // emit DIEs with DW_AT_declaration set to true, but yet still have
+ // subprogram, members, or inheritance, so we can't trust it
+ DWARFDIE child_die = die.GetFirstChild();
+ while (child_die) {
+ switch (child_die.Tag()) {
+ case DW_TAG_inheritance:
+ case DW_TAG_subprogram:
+ case DW_TAG_member:
+ case DW_TAG_APPLE_property:
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_typedef:
+ case DW_TAG_union_type:
+ child_die.Clear();
+ attrs.is_forward_declaration = false;
+ break;
+ default:
+ child_die = child_die.GetSibling();
+ break;
+ }
+ }
+ }
+
+ if (!attrs.is_forward_declaration) {
+ // Always start the definition for a class type so that if the class
+ // has child classes or types that require the class to be created
+ // for use as their decl contexts the class will be ready to accept
+ // these child definitions.
+ if (!die.HasChildren()) {
+ // No children for this struct/union/class, lets finish it
+ if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) {
+ ClangASTContext::CompleteTagDeclarationDefinition(clang_type);
+ } else {
+ dwarf->GetObjectFile()->GetModule()->ReportError(
+ "DWARF DIE at 0x%8.8x named \"%s\" was not able to start its "
+ "definition.\nPlease file a bug and attach the file at the "
+ "start of this error message",
+ die.GetOffset(), attrs.name.GetCString());
+ }
+
+ if (tag == DW_TAG_structure_type) // this only applies in C
+ {
+ clang::RecordDecl *record_decl =
+ ClangASTContext::GetAsRecordDecl(clang_type);
+
+ if (record_decl) {
+ GetClangASTImporter().InsertRecordDecl(
+ record_decl, ClangASTImporter::LayoutInfo());
+ }
+ }
+ } else if (clang_type_was_created) {
+ // Start the definition if the class is not objective C since the
+ // underlying decls respond to isCompleteDefinition(). Objective
+ // C decls don't respond to isCompleteDefinition() so we can't
+ // start the declaration definition right away. For C++
+ // class/union/structs we want to start the definition in case the
+ // class is needed as the declaration context for a contained class
+ // or type without the need to complete that type..
+
+ if (attrs.class_language != eLanguageTypeObjC &&
+ attrs.class_language != eLanguageTypeObjC_plus_plus)
+ ClangASTContext::StartTagDeclarationDefinition(clang_type);
+
+ // Leave this as a forward declaration until we need to know the
+ // details of the type. lldb_private::Type will automatically call
+ // the SymbolFile virtual function
+ // "SymbolFileDWARF::CompleteType(Type *)" When the definition
+ // needs to be defined.
+ assert(!dwarf->GetForwardDeclClangTypeToDie().count(
+ ClangUtil::RemoveFastQualifiers(clang_type)
+ .GetOpaqueQualType()) &&
+ "Type already in the forward declaration map!");
+ // Can't assume m_ast.GetSymbolFile() is actually a
+ // SymbolFileDWARF, it can be a SymbolFileDWARFDebugMap for Apple
+ // binaries.
+ dwarf->GetForwardDeclDieToClangType()[die.GetDIE()] =
+ clang_type.GetOpaqueQualType();
+ dwarf->GetForwardDeclClangTypeToDie()
+ [ClangUtil::RemoveFastQualifiers(clang_type).GetOpaqueQualType()] =
+ die.GetID();
+ m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true);
+ }
+ }
+
+ // If we made a clang type, set the trivial abi if applicable: We only
+ // do this for pass by value - which implies the Trivial ABI. There
+ // isn't a way to assert that something that would normally be pass by
+ // value is pass by reference, so we ignore that attribute if set.
+ if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_value) {
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
+ if (record_decl) {
+ record_decl->setHasTrivialSpecialMemberForCall();
+ }
+ }
+
+ if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_reference) {
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
+ if (record_decl)
+ record_decl->setArgPassingRestrictions(
+ clang::RecordDecl::APK_CannotPassInRegs);
+ }
+
+ } break;
+
+ case DW_TAG_enumeration_type: {
+ if (attrs.is_forward_declaration) {
+ type_sp = ParseTypeFromDWO(die, log);
+ if (type_sp)
+ return type_sp;
+
+ DWARFDeclContext die_decl_ctx;
+ die.GetDWARFDeclContext(die_decl_ctx);
+
+ type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx);
+
+ if (!type_sp) {
+ SymbolFileDWARFDebugMap *debug_map_symfile =
+ dwarf->GetDebugMapSymfile();
+ if (debug_map_symfile) {
+ // We weren't able to find a full declaration in this DWARF,
+ // see if we have a declaration anywhere else...
+ type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext(
+ die_decl_ctx);
+ }
+ }
+
+ if (type_sp) {
+ if (log) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a "
+ "forward declaration, complete type is 0x%8.8" PRIx64,
+ static_cast<void *>(this), die.GetOffset(),
+ DW_TAG_value_to_name(tag), attrs.name.GetCString(),
+ type_sp->GetID());
+ }
+
+ // We found a real definition for this type elsewhere so lets use
+ // it and cache the fact that we found a complete type for this
+ // die
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ clang::DeclContext *defn_decl_ctx =
+ GetCachedClangDeclContextForDIE(dwarf->GetDIE(type_sp->GetID()));
+ if (defn_decl_ctx)
+ LinkDeclContextToDIE(defn_decl_ctx, die);
+ return type_sp;
+ }
+ }
+ DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
+ DW_TAG_value_to_name(tag), type_name_cstr);
+
+ CompilerType enumerator_clang_type;
+ clang_type.SetCompilerType(
+ &m_ast, dwarf->GetForwardDeclDieToClangType().lookup(die.GetDIE()));
+ if (!clang_type) {
+ if (attrs.type.IsValid()) {
+ Type *enumerator_type =
+ dwarf->ResolveTypeUID(attrs.type.Reference(), true);
+ if (enumerator_type)
+ enumerator_clang_type = enumerator_type->GetFullCompilerType();
+ }
+
+ if (!enumerator_clang_type) {
+ if (attrs.byte_size) {
+ enumerator_clang_type =
+ m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize(
+ NULL, DW_ATE_signed, *attrs.byte_size * 8);
+ } else {
+ enumerator_clang_type = m_ast.GetBasicType(eBasicTypeInt);
+ }
+ }
+
+ clang_type = m_ast.CreateEnumerationType(
+ attrs.name.GetCString(),
+ GetClangDeclContextContainingDIE(die, nullptr), attrs.decl,
+ enumerator_clang_type, attrs.is_scoped_enum);
+ } else {
+ enumerator_clang_type =
+ m_ast.GetEnumerationIntegerType(clang_type.GetOpaqueQualType());
+ }
+
+ LinkDeclContextToDIE(ClangASTContext::GetDeclContextForType(clang_type),
+ die);
+
+ type_sp = std::make_shared<Type>(
+ die.GetID(), dwarf, attrs.name, attrs.byte_size, nullptr,
+ dwarf->GetUID(attrs.type.Reference()), Type::eEncodingIsUID,
+ &attrs.decl, clang_type, Type::eResolveStateForward);
+
+ if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) {
+ if (die.HasChildren()) {
+ bool is_signed = false;
+ enumerator_clang_type.IsIntegerType(is_signed);
+ ParseChildEnumerators(clang_type, is_signed,
+ type_sp->GetByteSize().getValueOr(0), die);
+ }
+ ClangASTContext::CompleteTagDeclarationDefinition(clang_type);
+ } else {
+ dwarf->GetObjectFile()->GetModule()->ReportError(
+ "DWARF DIE at 0x%8.8x named \"%s\" was not able to start its "
+ "definition.\nPlease file a bug and attach the file at the "
+ "start of this error message",
+ die.GetOffset(), attrs.name.GetCString());
+ }
+ } break;
+
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_subprogram:
+ case DW_TAG_subroutine_type: {
+ bool is_variadic = false;
+ bool is_static = false;
+ bool has_template_params = false;
+
+ unsigned type_quals = 0;
+
+ std::string object_pointer_name;
+ if (attrs.object_pointer) {
+ const char *object_pointer_name_cstr = attrs.object_pointer.GetName();
+ if (object_pointer_name_cstr)
+ object_pointer_name = object_pointer_name_cstr;
+ }
+
+ DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
+ DW_TAG_value_to_name(tag), type_name_cstr);
+
+ CompilerType return_clang_type;
+ Type *func_type = NULL;
+
+ if (attrs.type.IsValid())
+ func_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true);
+
+ if (func_type)
+ return_clang_type = func_type->GetForwardCompilerType();
+ else
+ return_clang_type = m_ast.GetBasicType(eBasicTypeVoid);
+
+ std::vector<CompilerType> function_param_types;
+ std::vector<clang::ParmVarDecl *> function_param_decls;
+
+ // Parse the function children for the parameters
+
+ DWARFDIE decl_ctx_die;
+ clang::DeclContext *containing_decl_ctx =
+ GetClangDeclContextContainingDIE(die, &decl_ctx_die);
+ const clang::Decl::Kind containing_decl_kind =
+ containing_decl_ctx->getDeclKind();
+
+ bool is_cxx_method = DeclKindIsCXXClass(containing_decl_kind);
+ // Start off static. This will be set to false in
+ // ParseChildParameters(...) if we find a "this" parameters as the
+ // first parameter
+ if (is_cxx_method) {
+ is_static = true;
+ }
+
+ if (die.HasChildren()) {
+ bool skip_artificial = true;
+ ParseChildParameters(containing_decl_ctx, die, skip_artificial, is_static,
+ is_variadic, has_template_params,
+ function_param_types, function_param_decls,
+ type_quals);
+ }
+
+ bool ignore_containing_context = false;
+ // Check for templatized class member functions. If we had any
+ // DW_TAG_template_type_parameter or DW_TAG_template_value_parameter
+ // the DW_TAG_subprogram DIE, then we can't let this become a method in
+ // a class. Why? Because templatized functions are only emitted if one
+ // of the templatized methods is used in the current compile unit and
+ // we will end up with classes that may or may not include these member
+ // functions and this means one class won't match another class
+ // definition and it affects our ability to use a class in the clang
+ // expression parser. So for the greater good, we currently must not
+ // allow any template member functions in a class definition.
+ if (is_cxx_method && has_template_params) {
+ ignore_containing_context = true;
+ is_cxx_method = false;
+ }
+
+ // clang_type will get the function prototype clang type after this
+ // call
+ clang_type = m_ast.CreateFunctionType(
+ return_clang_type, function_param_types.data(),
+ function_param_types.size(), is_variadic, type_quals);
+
+ if (attrs.name) {
+ bool type_handled = false;
+ if (tag == DW_TAG_subprogram || tag == DW_TAG_inlined_subroutine) {
+ ObjCLanguage::MethodName objc_method(attrs.name.GetStringRef(), true);
+ if (objc_method.IsValid(true)) {
+ CompilerType class_opaque_type;
+ ConstString class_name(objc_method.GetClassName());
+ if (class_name) {
+ TypeSP complete_objc_class_type_sp(
+ dwarf->FindCompleteObjCDefinitionTypeForDIE(DWARFDIE(),
+ class_name, false));
+
+ if (complete_objc_class_type_sp) {
+ CompilerType type_clang_forward_type =
+ complete_objc_class_type_sp->GetForwardCompilerType();
+ if (ClangASTContext::IsObjCObjectOrInterfaceType(
+ type_clang_forward_type))
+ class_opaque_type = type_clang_forward_type;
+ }
+ }
+
+ if (class_opaque_type) {
+ // If accessibility isn't set to anything valid, assume public
+ // for now...
+ if (attrs.accessibility == eAccessNone)
+ attrs.accessibility = eAccessPublic;
+
+ clang::ObjCMethodDecl *objc_method_decl =
+ m_ast.AddMethodToObjCObjectType(
+ class_opaque_type, attrs.name.GetCString(), clang_type,
+ attrs.accessibility, attrs.is_artificial, is_variadic);
+ type_handled = objc_method_decl != NULL;
+ if (type_handled) {
+ LinkDeclContextToDIE(
+ ClangASTContext::GetAsDeclContext(objc_method_decl), die);
+ m_ast.SetMetadataAsUserID(objc_method_decl, die.GetID());
+ } else {
+ dwarf->GetObjectFile()->GetModule()->ReportError(
+ "{0x%8.8x}: invalid Objective-C method 0x%4.4x (%s), "
+ "please file a bug and attach the file at the start of "
+ "this error message",
+ die.GetOffset(), tag, DW_TAG_value_to_name(tag));
+ }
+ }
+ } else if (is_cxx_method) {
+ // Look at the parent of this DIE and see if is is a class or
+ // struct and see if this is actually a C++ method
+ Type *class_type = dwarf->ResolveType(decl_ctx_die);
+ if (class_type) {
+ bool alternate_defn = false;
+ if (class_type->GetID() != decl_ctx_die.GetID() ||
+ decl_ctx_die.GetContainingDWOModuleDIE()) {
+ alternate_defn = true;
+
+ // We uniqued the parent class of this function to another
+ // class so we now need to associate all dies under
+ // "decl_ctx_die" to DIEs in the DIE for "class_type"...
+ DWARFDIE class_type_die = dwarf->GetDIE(class_type->GetID());
+
+ if (class_type_die) {
+ std::vector<DWARFDIE> failures;
+
+ CopyUniqueClassMethodTypes(decl_ctx_die, class_type_die,
+ class_type, failures);
+
+ // FIXME do something with these failures that's smarter
+ // than
+ // just dropping them on the ground. Unfortunately classes
+ // don't like having stuff added to them after their
+ // definitions are complete...
+
+ type_ptr = dwarf->GetDIEToType()[die.GetDIE()];
+ if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) {
+ type_sp = type_ptr->shared_from_this();
+ break;
+ }
+ }
+ }
+
+ if (attrs.specification.IsValid()) {
+ // We have a specification which we are going to base our
+ // function prototype off of, so we need this type to be
+ // completed so that the m_die_to_decl_ctx for the method in
+ // the specification has a valid clang decl context.
+ class_type->GetForwardCompilerType();
+ // If we have a specification, then the function type should
+ // have been made with the specification and not with this
+ // die.
+ DWARFDIE spec_die = attrs.specification.Reference();
+ clang::DeclContext *spec_clang_decl_ctx =
+ GetClangDeclContextForDIE(spec_die);
+ if (spec_clang_decl_ctx) {
+ LinkDeclContextToDIE(spec_clang_decl_ctx, die);
+ } else {
+ dwarf->GetObjectFile()->GetModule()->ReportWarning(
+ "0x%8.8" PRIx64 ": DW_AT_specification(0x%8.8x"
+ ") has no decl\n",
+ die.GetID(), spec_die.GetOffset());
+ }
+ type_handled = true;
+ } else if (attrs.abstract_origin.IsValid()) {
+ // We have a specification which we are going to base our
+ // function prototype off of, so we need this type to be
+ // completed so that the m_die_to_decl_ctx for the method in
+ // the abstract origin has a valid clang decl context.
+ class_type->GetForwardCompilerType();
+
+ DWARFDIE abs_die = attrs.abstract_origin.Reference();
+ clang::DeclContext *abs_clang_decl_ctx =
+ GetClangDeclContextForDIE(abs_die);
+ if (abs_clang_decl_ctx) {
+ LinkDeclContextToDIE(abs_clang_decl_ctx, die);
+ } else {
+ dwarf->GetObjectFile()->GetModule()->ReportWarning(
+ "0x%8.8" PRIx64 ": DW_AT_abstract_origin(0x%8.8x"
+ ") has no decl\n",
+ die.GetID(), abs_die.GetOffset());
+ }
+ type_handled = true;
+ } else {
+ CompilerType class_opaque_type =
+ class_type->GetForwardCompilerType();
+ if (ClangASTContext::IsCXXClassType(class_opaque_type)) {
+ if (class_opaque_type.IsBeingDefined() || alternate_defn) {
+ if (!is_static && !die.HasChildren()) {
+ // We have a C++ member function with no children (this
+ // pointer!) and clang will get mad if we try and make
+ // a function that isn't well formed in the DWARF, so
+ // we will just skip it...
+ type_handled = true;
+ } else {
+ bool add_method = true;
+ if (alternate_defn) {
+ // If an alternate definition for the class exists,
+ // then add the method only if an equivalent is not
+ // already present.
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(
+ class_opaque_type.GetOpaqueQualType());
+ if (record_decl) {
+ for (auto method_iter = record_decl->method_begin();
+ method_iter != record_decl->method_end();
+ method_iter++) {
+ clang::CXXMethodDecl *method_decl = *method_iter;
+ if (method_decl->getNameInfo().getAsString() ==
+ attrs.name.GetStringRef()) {
+ if (method_decl->getType() ==
+ ClangUtil::GetQualType(clang_type)) {
+ add_method = false;
+ LinkDeclContextToDIE(
+ ClangASTContext::GetAsDeclContext(
+ method_decl),
+ die);
+ type_handled = true;
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (add_method) {
+ llvm::PrettyStackTraceFormat stack_trace(
+ "SymbolFileDWARF::ParseType() is adding a method "
+ "%s to class %s in DIE 0x%8.8" PRIx64 " from %s",
+ attrs.name.GetCString(),
+ class_type->GetName().GetCString(), die.GetID(),
+ dwarf->GetObjectFile()
+ ->GetFileSpec()
+ .GetPath()
+ .c_str());
+
+ const bool is_attr_used = false;
+ // Neither GCC 4.2 nor clang++ currently set a valid
+ // accessibility in the DWARF for C++ methods...
+ // Default to public for now...
+ if (attrs.accessibility == eAccessNone)
+ attrs.accessibility = eAccessPublic;
+
+ clang::CXXMethodDecl *cxx_method_decl =
+ m_ast.AddMethodToCXXRecordType(
+ class_opaque_type.GetOpaqueQualType(),
+ attrs.name.GetCString(), attrs.mangled_name,
+ clang_type, attrs.accessibility, attrs.is_virtual,
+ is_static, attrs.is_inline, attrs.is_explicit,
+ is_attr_used, attrs.is_artificial);
+
+ type_handled = cxx_method_decl != NULL;
+
+ if (type_handled) {
+ LinkDeclContextToDIE(
+ ClangASTContext::GetAsDeclContext(cxx_method_decl),
+ die);
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(die.GetID());
+
+ if (!object_pointer_name.empty()) {
+ metadata.SetObjectPtrName(
+ object_pointer_name.c_str());
+ if (log)
+ log->Printf(
+ "Setting object pointer name: %s on method "
+ "object %p.\n",
+ object_pointer_name.c_str(),
+ static_cast<void *>(cxx_method_decl));
+ }
+ m_ast.SetMetadata(cxx_method_decl, metadata);
+ } else {
+ ignore_containing_context = true;
+ }
+ }
+ }
+ } else {
+ // We were asked to parse the type for a method in a
+ // class, yet the class hasn't been asked to complete
+ // itself through the clang::ExternalASTSource protocol,
+ // so we need to just have the class complete itself and
+ // do things the right way, then our
+ // DIE should then have an entry in the
+ // dwarf->GetDIEToType() map. First
+ // we need to modify the dwarf->GetDIEToType() so it
+ // doesn't think we are trying to parse this DIE
+ // anymore...
+ dwarf->GetDIEToType()[die.GetDIE()] = NULL;
+
+ // Now we get the full type to force our class type to
+ // complete itself using the clang::ExternalASTSource
+ // protocol which will parse all base classes and all
+ // methods (including the method for this DIE).
+ class_type->GetFullCompilerType();
+
+ // The type for this DIE should have been filled in the
+ // function call above
+ type_ptr = dwarf->GetDIEToType()[die.GetDIE()];
+ if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) {
+ type_sp = type_ptr->shared_from_this();
+ break;
+ }
+
+ // FIXME This is fixing some even uglier behavior but we
+ // really need to
+ // uniq the methods of each class as well as the class
+ // itself. <rdar://problem/11240464>
+ type_handled = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!type_handled) {
+ clang::FunctionDecl *function_decl = nullptr;
+ clang::FunctionDecl *template_function_decl = nullptr;
+
+ if (attrs.abstract_origin.IsValid()) {
+ DWARFDIE abs_die = attrs.abstract_origin.Reference();
+
+ if (dwarf->ResolveType(abs_die)) {
+ function_decl = llvm::dyn_cast_or_null<clang::FunctionDecl>(
+ GetCachedClangDeclContextForDIE(abs_die));
+
+ if (function_decl) {
+ LinkDeclContextToDIE(function_decl, die);
+ }
+ }
+ }
+
+ if (!function_decl) {
+ // We just have a function that isn't part of a class
+ function_decl = m_ast.CreateFunctionDeclaration(
+ ignore_containing_context ? m_ast.GetTranslationUnitDecl()
+ : containing_decl_ctx,
+ attrs.name.GetCString(), clang_type, attrs.storage,
+ attrs.is_inline);
+
+ if (has_template_params) {
+ ClangASTContext::TemplateParameterInfos template_param_infos;
+ ParseTemplateParameterInfos(die, template_param_infos);
+ template_function_decl = m_ast.CreateFunctionDeclaration(
+ ignore_containing_context ? m_ast.GetTranslationUnitDecl()
+ : containing_decl_ctx,
+ attrs.name.GetCString(), clang_type, attrs.storage,
+ attrs.is_inline);
+ clang::FunctionTemplateDecl *func_template_decl =
+ m_ast.CreateFunctionTemplateDecl(
+ containing_decl_ctx, template_function_decl,
+ attrs.name.GetCString(), template_param_infos);
+ m_ast.CreateFunctionTemplateSpecializationInfo(
+ function_decl, func_template_decl, template_param_infos);
+ }
+
+ lldbassert(function_decl);
+
+ if (function_decl) {
+ LinkDeclContextToDIE(function_decl, die);
+
+ if (!function_param_decls.empty()) {
+ m_ast.SetFunctionParameters(function_decl,
+ &function_param_decls.front(),
+ function_param_decls.size());
+ if (template_function_decl)
+ m_ast.SetFunctionParameters(template_function_decl,
+ &function_param_decls.front(),
+ function_param_decls.size());
+ }
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(die.GetID());
+
+ if (!object_pointer_name.empty()) {
+ metadata.SetObjectPtrName(object_pointer_name.c_str());
+ if (log)
+ log->Printf("Setting object pointer name: %s on function "
+ "object %p.",
+ object_pointer_name.c_str(),
+ static_cast<void *>(function_decl));
+ }
+ m_ast.SetMetadata(function_decl, metadata);
+ }
+ }
+ }
+ }
+ type_sp = std::make_shared<Type>(
+ die.GetID(), dwarf, attrs.name, llvm::None, nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, &attrs.decl, clang_type, Type::eResolveStateFull);
+ assert(type_sp.get());
+ } break;
+
+ case DW_TAG_array_type: {
+ DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
+ DW_TAG_value_to_name(tag), type_name_cstr);
+
+ DWARFDIE type_die = attrs.type.Reference();
+ Type *element_type = dwarf->ResolveTypeUID(type_die, true);
+
+ if (element_type) {
+ auto array_info = ParseChildArrayInfo(die);
+ if (array_info) {
+ attrs.byte_stride = array_info->byte_stride;
+ attrs.bit_stride = array_info->bit_stride;
+ }
+ if (attrs.byte_stride == 0 && attrs.bit_stride == 0)
+ attrs.byte_stride = element_type->GetByteSize().getValueOr(0);
+ CompilerType array_element_type = element_type->GetForwardCompilerType();
+
+ if (ClangASTContext::IsCXXClassType(array_element_type) &&
+ !array_element_type.GetCompleteType()) {
+ ModuleSP module_sp = die.GetModule();
+ if (module_sp) {
+ if (die.GetCU()->GetProducer() == eProducerClang)
+ module_sp->ReportError(
+ "DWARF DW_TAG_array_type DIE at 0x%8.8x has a "
+ "class/union/struct element type DIE 0x%8.8x that is a "
+ "forward declaration, not a complete definition.\nTry "
+ "compiling the source file with -fstandalone-debug or "
+ "disable -gmodules",
+ die.GetOffset(), type_die.GetOffset());
+ else
+ module_sp->ReportError(
+ "DWARF DW_TAG_array_type DIE at 0x%8.8x has a "
+ "class/union/struct element type DIE 0x%8.8x that is a "
+ "forward declaration, not a complete definition.\nPlease "
+ "file a bug against the compiler and include the "
+ "preprocessed output for %s",
+ die.GetOffset(), type_die.GetOffset(),
+ GetUnitName(die).c_str());
+ }
+
+ // We have no choice other than to pretend that the element class
+ // type is complete. If we don't do this, clang will crash when
+ // trying to layout the class. Since we provide layout
+ // assistance, all ivars in this class and other classes will be
+ // fine, this is the best we can do short of crashing.
+ if (ClangASTContext::StartTagDeclarationDefinition(
+ array_element_type)) {
+ ClangASTContext::CompleteTagDeclarationDefinition(array_element_type);
+ } else {
+ module_sp->ReportError("DWARF DIE at 0x%8.8x was not able to "
+ "start its definition.\nPlease file a "
+ "bug and attach the file at the start "
+ "of this error message",
+ type_die.GetOffset());
+ }
+ }
+
+ uint64_t array_element_bit_stride =
+ attrs.byte_stride * 8 + attrs.bit_stride;
+ if (array_info && array_info->element_orders.size() > 0) {
+ uint64_t num_elements = 0;
+ auto end = array_info->element_orders.rend();
+ for (auto pos = array_info->element_orders.rbegin(); pos != end;
+ ++pos) {
+ num_elements = *pos;
+ clang_type = m_ast.CreateArrayType(array_element_type, num_elements,
+ attrs.is_vector);
+ array_element_type = clang_type;
+ array_element_bit_stride =
+ num_elements ? array_element_bit_stride * num_elements
+ : array_element_bit_stride;
+ }
+ } else {
+ clang_type = m_ast.CreateArrayType(array_element_type, 0, attrs.is_vector);
+ }
+ ConstString empty_name;
+ type_sp = std::make_shared<Type>(
+ die.GetID(), dwarf, empty_name, array_element_bit_stride / 8, nullptr,
+ dwarf->GetUID(type_die), Type::eEncodingIsUID, &attrs.decl,
+ clang_type, Type::eResolveStateFull);
+ type_sp->SetEncodingType(element_type);
+ m_ast.SetMetadataAsUserID(clang_type.GetOpaqueQualType(), die.GetID());
+ }
+ } break;
+
+ case DW_TAG_ptr_to_member_type: {
+ Type *pointee_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true);
+ Type *class_type =
+ dwarf->ResolveTypeUID(attrs.containing_type.Reference(), true);
+
+ CompilerType pointee_clang_type = pointee_type->GetForwardCompilerType();
+ CompilerType class_clang_type = class_type->GetLayoutCompilerType();
+
+ clang_type = ClangASTContext::CreateMemberPointerType(class_clang_type,
+ pointee_clang_type);
+
+ if (llvm::Optional<uint64_t> clang_type_size =
+ clang_type.GetByteSize(nullptr)) {
+ type_sp = std::make_shared<Type>(
+ die.GetID(), dwarf, attrs.name, *clang_type_size, nullptr,
+ LLDB_INVALID_UID, Type::eEncodingIsUID, nullptr, clang_type,
+ Type::eResolveStateForward);
+ }
+
+ break;
+ }
+ default:
+ dwarf->GetObjectFile()->GetModule()->ReportError(
+ "{0x%8.8x}: unhandled type tag 0x%4.4x (%s), please file a bug and "
+ "attach the file at the start of this error message",
+ die.GetOffset(), tag, DW_TAG_value_to_name(tag));
+ break;
+ }
+
+ if (type_sp.get()) {
+ DWARFDIE sc_parent_die = SymbolFileDWARF::GetParentSymbolContextDIE(die);
+ dw_tag_t sc_parent_tag = sc_parent_die.Tag();
+
+ SymbolContextScope *symbol_context_scope = NULL;
+ if (sc_parent_tag == DW_TAG_compile_unit ||
+ sc_parent_tag == DW_TAG_partial_unit) {
+ symbol_context_scope = sc.comp_unit;
+ } else if (sc.function != NULL && sc_parent_die) {
+ symbol_context_scope =
+ sc.function->GetBlock(true).FindBlockByID(sc_parent_die.GetID());
+ if (symbol_context_scope == NULL)
+ symbol_context_scope = sc.function;
+ } else
+ symbol_context_scope = sc.module_sp.get();
+
+ if (symbol_context_scope != NULL) {
+ type_sp->SetSymbolContextScope(symbol_context_scope);
+ }
+
+ // We are ready to put this type into the uniqued list up at the module
+ // level
+ type_list->Insert(type_sp);
+
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ }
+ return type_sp;
+}
+
+// DWARF parsing functions
+
+class DWARFASTParserClang::DelayedAddObjCClassProperty {
+public:
+ DelayedAddObjCClassProperty(
+ const CompilerType &class_opaque_type, const char *property_name,
+ const CompilerType &property_opaque_type, // The property type is only
+ // required if you don't have an
+ // ivar decl
+ clang::ObjCIvarDecl *ivar_decl, const char *property_setter_name,
+ const char *property_getter_name, uint32_t property_attributes,
+ const ClangASTMetadata *metadata)
+ : m_class_opaque_type(class_opaque_type), m_property_name(property_name),
+ m_property_opaque_type(property_opaque_type), m_ivar_decl(ivar_decl),
+ m_property_setter_name(property_setter_name),
+ m_property_getter_name(property_getter_name),
+ m_property_attributes(property_attributes) {
+ if (metadata != nullptr) {
+ m_metadata_up.reset(new ClangASTMetadata());
+ *m_metadata_up = *metadata;
+ }
+ }
+
+ DelayedAddObjCClassProperty(const DelayedAddObjCClassProperty &rhs) {
+ *this = rhs;
+ }
+
+ DelayedAddObjCClassProperty &
+ operator=(const DelayedAddObjCClassProperty &rhs) {
+ m_class_opaque_type = rhs.m_class_opaque_type;
+ m_property_name = rhs.m_property_name;
+ m_property_opaque_type = rhs.m_property_opaque_type;
+ m_ivar_decl = rhs.m_ivar_decl;
+ m_property_setter_name = rhs.m_property_setter_name;
+ m_property_getter_name = rhs.m_property_getter_name;
+ m_property_attributes = rhs.m_property_attributes;
+
+ if (rhs.m_metadata_up) {
+ m_metadata_up.reset(new ClangASTMetadata());
+ *m_metadata_up = *rhs.m_metadata_up;
+ }
+ return *this;
+ }
+
+ bool Finalize() {
+ return ClangASTContext::AddObjCClassProperty(
+ m_class_opaque_type, m_property_name, m_property_opaque_type,
+ m_ivar_decl, m_property_setter_name, m_property_getter_name,
+ m_property_attributes, m_metadata_up.get());
+ }
+
+private:
+ CompilerType m_class_opaque_type;
+ const char *m_property_name;
+ CompilerType m_property_opaque_type;
+ clang::ObjCIvarDecl *m_ivar_decl;
+ const char *m_property_setter_name;
+ const char *m_property_getter_name;
+ uint32_t m_property_attributes;
+ std::unique_ptr<ClangASTMetadata> m_metadata_up;
+};
+
+bool DWARFASTParserClang::ParseTemplateDIE(
+ const DWARFDIE &die,
+ ClangASTContext::TemplateParameterInfos &template_param_infos) {
+ const dw_tag_t tag = die.Tag();
+ bool is_template_template_argument = false;
+
+ switch (tag) {
+ case DW_TAG_GNU_template_parameter_pack: {
+ template_param_infos.packed_args.reset(
+ new ClangASTContext::TemplateParameterInfos);
+ for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid();
+ child_die = child_die.GetSibling()) {
+ if (!ParseTemplateDIE(child_die, *template_param_infos.packed_args))
+ return false;
+ }
+ if (const char *name = die.GetName()) {
+ template_param_infos.pack_name = name;
+ }
+ return true;
+ }
+ case DW_TAG_GNU_template_template_param:
+ is_template_template_argument = true;
+ LLVM_FALLTHROUGH;
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_template_value_parameter: {
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ const char *name = nullptr;
+ const char *template_name = nullptr;
+ CompilerType clang_type;
+ uint64_t uval64 = 0;
+ bool uval64_valid = false;
+ if (num_attributes > 0) {
+ DWARFFormValue form_value;
+ for (size_t i = 0; i < num_attributes; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+
+ switch (attr) {
+ case DW_AT_name:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ name = form_value.AsCString();
+ break;
+
+ case DW_AT_GNU_template_name:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ template_name = form_value.AsCString();
+ break;
+
+ case DW_AT_type:
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ Type *lldb_type = die.ResolveTypeUID(form_value.Reference());
+ if (lldb_type)
+ clang_type = lldb_type->GetForwardCompilerType();
+ }
+ break;
+
+ case DW_AT_const_value:
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ uval64_valid = true;
+ uval64 = form_value.Unsigned();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ clang::ASTContext *ast = m_ast.getASTContext();
+ if (!clang_type)
+ clang_type = m_ast.GetBasicType(eBasicTypeVoid);
+
+ if (!is_template_template_argument) {
+ bool is_signed = false;
+ if (name && name[0])
+ template_param_infos.names.push_back(name);
+ else
+ template_param_infos.names.push_back(NULL);
+
+ // Get the signed value for any integer or enumeration if available
+ clang_type.IsIntegerOrEnumerationType(is_signed);
+
+ if (tag == DW_TAG_template_value_parameter && uval64_valid) {
+ llvm::Optional<uint64_t> size = clang_type.GetBitSize(nullptr);
+ if (!size)
+ return false;
+ llvm::APInt apint(*size, uval64, is_signed);
+ template_param_infos.args.push_back(
+ clang::TemplateArgument(*ast, llvm::APSInt(apint, !is_signed),
+ ClangUtil::GetQualType(clang_type)));
+ } else {
+ template_param_infos.args.push_back(
+ clang::TemplateArgument(ClangUtil::GetQualType(clang_type)));
+ }
+ } else {
+ auto *tplt_type = m_ast.CreateTemplateTemplateParmDecl(template_name);
+ template_param_infos.names.push_back(name);
+ template_param_infos.args.push_back(
+ clang::TemplateArgument(clang::TemplateName(tplt_type)));
+ }
+ }
+ }
+ return true;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+bool DWARFASTParserClang::ParseTemplateParameterInfos(
+ const DWARFDIE &parent_die,
+ ClangASTContext::TemplateParameterInfos &template_param_infos) {
+
+ if (!parent_die)
+ return false;
+
+ for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
+ die = die.GetSibling()) {
+ const dw_tag_t tag = die.Tag();
+
+ switch (tag) {
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_template_value_parameter:
+ case DW_TAG_GNU_template_parameter_pack:
+ case DW_TAG_GNU_template_template_param:
+ ParseTemplateDIE(die, template_param_infos);
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (template_param_infos.args.empty())
+ return false;
+ return template_param_infos.args.size() == template_param_infos.names.size();
+}
+
+bool DWARFASTParserClang::CompleteTypeFromDWARF(const DWARFDIE &die,
+ lldb_private::Type *type,
+ CompilerType &clang_type) {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+
+ std::lock_guard<std::recursive_mutex> guard(
+ dwarf->GetObjectFile()->GetModule()->GetMutex());
+
+ // Disable external storage for this type so we don't get anymore
+ // clang::ExternalASTSource queries for this type.
+ m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), false);
+
+ if (!die)
+ return false;
+
+#if defined LLDB_CONFIGURATION_DEBUG
+ // For debugging purposes, the LLDB_DWARF_DONT_COMPLETE_TYPENAMES environment
+ // variable can be set with one or more typenames separated by ';'
+ // characters. This will cause this function to not complete any types whose
+ // names match.
+ //
+ // Examples of setting this environment variable:
+ //
+ // LLDB_DWARF_DONT_COMPLETE_TYPENAMES=Foo
+ // LLDB_DWARF_DONT_COMPLETE_TYPENAMES=Foo;Bar;Baz
+ const char *dont_complete_typenames_cstr =
+ getenv("LLDB_DWARF_DONT_COMPLETE_TYPENAMES");
+ if (dont_complete_typenames_cstr && dont_complete_typenames_cstr[0]) {
+ const char *die_name = die.GetName();
+ if (die_name && die_name[0]) {
+ const char *match = strstr(dont_complete_typenames_cstr, die_name);
+ if (match) {
+ size_t die_name_length = strlen(die_name);
+ while (match) {
+ const char separator_char = ';';
+ const char next_char = match[die_name_length];
+ if (next_char == '\0' || next_char == separator_char) {
+ if (match == dont_complete_typenames_cstr ||
+ match[-1] == separator_char)
+ return false;
+ }
+ match = strstr(match + 1, die_name);
+ }
+ }
+ }
+ }
+#endif
+
+ const dw_tag_t tag = die.Tag();
+
+ Log *log =
+ nullptr; // (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO|DWARF_LOG_TYPE_COMPLETION));
+ if (log)
+ dwarf->GetObjectFile()->GetModule()->LogMessageVerboseBacktrace(
+ log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...",
+ die.GetID(), die.GetTagAsCString(), type->GetName().AsCString());
+ assert(clang_type);
+ DWARFAttributes attributes;
+ switch (tag) {
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type: {
+ ClangASTImporter::LayoutInfo layout_info;
+
+ {
+ if (die.HasChildren()) {
+ LanguageType class_language = eLanguageTypeUnknown;
+ if (ClangASTContext::IsObjCObjectOrInterfaceType(clang_type)) {
+ class_language = eLanguageTypeObjC;
+ // For objective C we don't start the definition when the class is
+ // created.
+ ClangASTContext::StartTagDeclarationDefinition(clang_type);
+ }
+
+ int tag_decl_kind = -1;
+ AccessType default_accessibility = eAccessNone;
+ if (tag == DW_TAG_structure_type) {
+ tag_decl_kind = clang::TTK_Struct;
+ default_accessibility = eAccessPublic;
+ } else if (tag == DW_TAG_union_type) {
+ tag_decl_kind = clang::TTK_Union;
+ default_accessibility = eAccessPublic;
+ } else if (tag == DW_TAG_class_type) {
+ tag_decl_kind = clang::TTK_Class;
+ default_accessibility = eAccessPrivate;
+ }
+
+ std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases;
+ std::vector<int> member_accessibilities;
+ bool is_a_class = false;
+ // Parse members and base classes first
+ std::vector<DWARFDIE> member_function_dies;
+
+ DelayedPropertyList delayed_properties;
+ ParseChildMembers(die, clang_type, class_language, bases,
+ member_accessibilities, member_function_dies,
+ delayed_properties, default_accessibility, is_a_class,
+ layout_info);
+
+ // Now parse any methods if there were any...
+ for (const DWARFDIE &die : member_function_dies)
+ dwarf->ResolveType(die);
+
+ if (class_language == eLanguageTypeObjC) {
+ ConstString class_name(clang_type.GetTypeName());
+ if (class_name) {
+ DIEArray method_die_offsets;
+ dwarf->GetObjCMethodDIEOffsets(class_name, method_die_offsets);
+
+ if (!method_die_offsets.empty()) {
+ DWARFDebugInfo *debug_info = dwarf->DebugInfo();
+
+ const size_t num_matches = method_die_offsets.size();
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = method_die_offsets[i];
+ DWARFDIE method_die = debug_info->GetDIE(die_ref);
+
+ if (method_die)
+ method_die.ResolveType();
+ }
+ }
+
+ for (DelayedPropertyList::iterator pi = delayed_properties.begin(),
+ pe = delayed_properties.end();
+ pi != pe; ++pi)
+ pi->Finalize();
+ }
+ }
+
+ // If we have a DW_TAG_structure_type instead of a DW_TAG_class_type we
+ // need to tell the clang type it is actually a class.
+ if (class_language != eLanguageTypeObjC) {
+ if (is_a_class && tag_decl_kind != clang::TTK_Class)
+ m_ast.SetTagTypeKind(ClangUtil::GetQualType(clang_type),
+ clang::TTK_Class);
+ }
+
+ // Since DW_TAG_structure_type gets used for both classes and
+ // structures, we may need to set any DW_TAG_member fields to have a
+ // "private" access if none was specified. When we parsed the child
+ // members we tracked that actual accessibility value for each
+ // DW_TAG_member in the "member_accessibilities" array. If the value
+ // for the member is zero, then it was set to the
+ // "default_accessibility" which for structs was "public". Below we
+ // correct this by setting any fields to "private" that weren't
+ // correctly set.
+ if (is_a_class && !member_accessibilities.empty()) {
+ // This is a class and all members that didn't have their access
+ // specified are private.
+ m_ast.SetDefaultAccessForRecordFields(
+ m_ast.GetAsRecordDecl(clang_type), eAccessPrivate,
+ &member_accessibilities.front(), member_accessibilities.size());
+ }
+
+ if (!bases.empty()) {
+ // Make sure all base classes refer to complete types and not forward
+ // declarations. If we don't do this, clang will crash with an
+ // assertion in the call to clang_type.TransferBaseClasses()
+ for (const auto &base_class : bases) {
+ clang::TypeSourceInfo *type_source_info =
+ base_class->getTypeSourceInfo();
+ if (type_source_info) {
+ CompilerType base_class_type(
+ &m_ast, type_source_info->getType().getAsOpaquePtr());
+ if (!base_class_type.GetCompleteType()) {
+ auto module = dwarf->GetObjectFile()->GetModule();
+ module->ReportError(":: Class '%s' has a base class '%s' which "
+ "does not have a complete definition.",
+ die.GetName(),
+ base_class_type.GetTypeName().GetCString());
+ if (die.GetCU()->GetProducer() == eProducerClang)
+ module->ReportError(":: Try compiling the source file with "
+ "-fstandalone-debug.");
+
+ // We have no choice other than to pretend that the base class
+ // is complete. If we don't do this, clang will crash when we
+ // call setBases() inside of
+ // "clang_type.TransferBaseClasses()" below. Since we
+ // provide layout assistance, all ivars in this class and other
+ // classes will be fine, this is the best we can do short of
+ // crashing.
+ if (ClangASTContext::StartTagDeclarationDefinition(
+ base_class_type)) {
+ ClangASTContext::CompleteTagDeclarationDefinition(
+ base_class_type);
+ }
+ }
+ }
+ }
+
+ m_ast.TransferBaseClasses(clang_type.GetOpaqueQualType(),
+ std::move(bases));
+ }
+ }
+ }
+
+ m_ast.AddMethodOverridesForCXXRecordType(clang_type.GetOpaqueQualType());
+ ClangASTContext::BuildIndirectFields(clang_type);
+ ClangASTContext::CompleteTagDeclarationDefinition(clang_type);
+
+ if (!layout_info.field_offsets.empty() ||
+ !layout_info.base_offsets.empty() ||
+ !layout_info.vbase_offsets.empty()) {
+ if (type)
+ layout_info.bit_size = type->GetByteSize().getValueOr(0) * 8;
+ if (layout_info.bit_size == 0)
+ layout_info.bit_size =
+ die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
+
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
+ if (record_decl) {
+ if (log) {
+ ModuleSP module_sp = dwarf->GetObjectFile()->GetModule();
+
+ if (module_sp) {
+ module_sp->LogMessage(
+ log,
+ "ClangASTContext::CompleteTypeFromDWARF (clang_type = %p) "
+ "caching layout info for record_decl = %p, bit_size = %" PRIu64
+ ", alignment = %" PRIu64
+ ", field_offsets[%u], base_offsets[%u], vbase_offsets[%u])",
+ static_cast<void *>(clang_type.GetOpaqueQualType()),
+ static_cast<void *>(record_decl), layout_info.bit_size,
+ layout_info.alignment,
+ static_cast<uint32_t>(layout_info.field_offsets.size()),
+ static_cast<uint32_t>(layout_info.base_offsets.size()),
+ static_cast<uint32_t>(layout_info.vbase_offsets.size()));
+
+ uint32_t idx;
+ {
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t>::const_iterator
+ pos,
+ end = layout_info.field_offsets.end();
+ for (idx = 0, pos = layout_info.field_offsets.begin(); pos != end;
+ ++pos, ++idx) {
+ module_sp->LogMessage(
+ log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = "
+ "%p) field[%u] = { bit_offset=%u, name='%s' }",
+ static_cast<void *>(clang_type.GetOpaqueQualType()), idx,
+ static_cast<uint32_t>(pos->second),
+ pos->first->getNameAsString().c_str());
+ }
+ }
+
+ {
+ llvm::DenseMap<const clang::CXXRecordDecl *,
+ clang::CharUnits>::const_iterator base_pos,
+ base_end = layout_info.base_offsets.end();
+ for (idx = 0, base_pos = layout_info.base_offsets.begin();
+ base_pos != base_end; ++base_pos, ++idx) {
+ module_sp->LogMessage(
+ log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = "
+ "%p) base[%u] = { byte_offset=%u, name='%s' }",
+ clang_type.GetOpaqueQualType(), idx,
+ (uint32_t)base_pos->second.getQuantity(),
+ base_pos->first->getNameAsString().c_str());
+ }
+ }
+ {
+ llvm::DenseMap<const clang::CXXRecordDecl *,
+ clang::CharUnits>::const_iterator vbase_pos,
+ vbase_end = layout_info.vbase_offsets.end();
+ for (idx = 0, vbase_pos = layout_info.vbase_offsets.begin();
+ vbase_pos != vbase_end; ++vbase_pos, ++idx) {
+ module_sp->LogMessage(
+ log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = "
+ "%p) vbase[%u] = { byte_offset=%u, name='%s' }",
+ static_cast<void *>(clang_type.GetOpaqueQualType()), idx,
+ static_cast<uint32_t>(vbase_pos->second.getQuantity()),
+ vbase_pos->first->getNameAsString().c_str());
+ }
+ }
+ }
+ }
+ GetClangASTImporter().InsertRecordDecl(record_decl, layout_info);
+ }
+ }
+ }
+
+ return (bool)clang_type;
+
+ case DW_TAG_enumeration_type:
+ if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) {
+ if (die.HasChildren()) {
+ bool is_signed = false;
+ clang_type.IsIntegerType(is_signed);
+ ParseChildEnumerators(clang_type, is_signed,
+ type->GetByteSize().getValueOr(0), die);
+ }
+ ClangASTContext::CompleteTagDeclarationDefinition(clang_type);
+ }
+ return (bool)clang_type;
+
+ default:
+ assert(false && "not a forward clang type decl!");
+ break;
+ }
+
+ return false;
+}
+
+std::vector<DWARFDIE> DWARFASTParserClang::GetDIEForDeclContext(
+ lldb_private::CompilerDeclContext decl_context) {
+ std::vector<DWARFDIE> result;
+ for (auto it = m_decl_ctx_to_die.find(
+ (clang::DeclContext *)decl_context.GetOpaqueDeclContext());
+ it != m_decl_ctx_to_die.end(); it++)
+ result.push_back(it->second);
+ return result;
+}
+
+CompilerDecl DWARFASTParserClang::GetDeclForUIDFromDWARF(const DWARFDIE &die) {
+ clang::Decl *clang_decl = GetClangDeclForDIE(die);
+ if (clang_decl != nullptr)
+ return CompilerDecl(&m_ast, clang_decl);
+ return CompilerDecl();
+}
+
+CompilerDeclContext
+DWARFASTParserClang::GetDeclContextForUIDFromDWARF(const DWARFDIE &die) {
+ clang::DeclContext *clang_decl_ctx = GetClangDeclContextForDIE(die);
+ if (clang_decl_ctx)
+ return CompilerDeclContext(&m_ast, clang_decl_ctx);
+ return CompilerDeclContext();
+}
+
+CompilerDeclContext
+DWARFASTParserClang::GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) {
+ clang::DeclContext *clang_decl_ctx =
+ GetClangDeclContextContainingDIE(die, nullptr);
+ if (clang_decl_ctx)
+ return CompilerDeclContext(&m_ast, clang_decl_ctx);
+ return CompilerDeclContext();
+}
+
+size_t DWARFASTParserClang::ParseChildEnumerators(
+ lldb_private::CompilerType &clang_type, bool is_signed,
+ uint32_t enumerator_byte_size, const DWARFDIE &parent_die) {
+ if (!parent_die)
+ return 0;
+
+ size_t enumerators_added = 0;
+
+ for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
+ die = die.GetSibling()) {
+ const dw_tag_t tag = die.Tag();
+ if (tag == DW_TAG_enumerator) {
+ DWARFAttributes attributes;
+ const size_t num_child_attributes = die.GetAttributes(attributes);
+ if (num_child_attributes > 0) {
+ const char *name = nullptr;
+ bool got_value = false;
+ int64_t enum_value = 0;
+ Declaration decl;
+
+ uint32_t i;
+ for (i = 0; i < num_child_attributes; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_const_value:
+ got_value = true;
+ if (is_signed)
+ enum_value = form_value.Signed();
+ else
+ enum_value = form_value.Unsigned();
+ break;
+
+ case DW_AT_name:
+ name = form_value.AsCString();
+ break;
+
+ case DW_AT_description:
+ default:
+ case DW_AT_decl_file:
+ decl.SetFile(die.GetCU()->GetFile(form_value.Unsigned()));
+ break;
+ case DW_AT_decl_line:
+ decl.SetLine(form_value.Unsigned());
+ break;
+ case DW_AT_decl_column:
+ decl.SetColumn(form_value.Unsigned());
+ break;
+ case DW_AT_sibling:
+ break;
+ }
+ }
+ }
+
+ if (name && name[0] && got_value) {
+ m_ast.AddEnumerationValueToEnumerationType(
+ clang_type, decl, name, enum_value, enumerator_byte_size * 8);
+ ++enumerators_added;
+ }
+ }
+ }
+ }
+ return enumerators_added;
+}
+
+#if defined(LLDB_CONFIGURATION_DEBUG) || defined(LLDB_CONFIGURATION_RELEASE)
+
+class DIEStack {
+public:
+ void Push(const DWARFDIE &die) { m_dies.push_back(die); }
+
+ void LogDIEs(Log *log) {
+ StreamString log_strm;
+ const size_t n = m_dies.size();
+ log_strm.Printf("DIEStack[%" PRIu64 "]:\n", (uint64_t)n);
+ for (size_t i = 0; i < n; i++) {
+ std::string qualified_name;
+ const DWARFDIE &die = m_dies[i];
+ die.GetQualifiedName(qualified_name);
+ log_strm.Printf("[%" PRIu64 "] 0x%8.8x: %s name='%s'\n", (uint64_t)i,
+ die.GetOffset(), die.GetTagAsCString(),
+ qualified_name.c_str());
+ }
+ log->PutCString(log_strm.GetData());
+ }
+ void Pop() { m_dies.pop_back(); }
+
+ class ScopedPopper {
+ public:
+ ScopedPopper(DIEStack &die_stack)
+ : m_die_stack(die_stack), m_valid(false) {}
+
+ void Push(const DWARFDIE &die) {
+ m_valid = true;
+ m_die_stack.Push(die);
+ }
+
+ ~ScopedPopper() {
+ if (m_valid)
+ m_die_stack.Pop();
+ }
+
+ protected:
+ DIEStack &m_die_stack;
+ bool m_valid;
+ };
+
+protected:
+ typedef std::vector<DWARFDIE> Stack;
+ Stack m_dies;
+};
+#endif
+
+Function *DWARFASTParserClang::ParseFunctionFromDWARF(CompileUnit &comp_unit,
+ const DWARFDIE &die) {
+ DWARFRangeList func_ranges;
+ const char *name = nullptr;
+ const char *mangled = nullptr;
+ int decl_file = 0;
+ int decl_line = 0;
+ int decl_column = 0;
+ int call_file = 0;
+ int call_line = 0;
+ int call_column = 0;
+ DWARFExpression frame_base;
+
+ const dw_tag_t tag = die.Tag();
+
+ if (tag != DW_TAG_subprogram)
+ return nullptr;
+
+ if (die.GetDIENamesAndRanges(name, mangled, func_ranges, decl_file, decl_line,
+ decl_column, call_file, call_line, call_column,
+ &frame_base)) {
+
+ // Union of all ranges in the function DIE (if the function is
+ // discontiguous)
+ AddressRange func_range;
+ lldb::addr_t lowest_func_addr = func_ranges.GetMinRangeBase(0);
+ lldb::addr_t highest_func_addr = func_ranges.GetMaxRangeEnd(0);
+ if (lowest_func_addr != LLDB_INVALID_ADDRESS &&
+ lowest_func_addr <= highest_func_addr) {
+ ModuleSP module_sp(die.GetModule());
+ func_range.GetBaseAddress().ResolveAddressUsingFileSections(
+ lowest_func_addr, module_sp->GetSectionList());
+ if (func_range.GetBaseAddress().IsValid())
+ func_range.SetByteSize(highest_func_addr - lowest_func_addr);
+ }
+
+ if (func_range.GetBaseAddress().IsValid()) {
+ Mangled func_name;
+ if (mangled)
+ func_name.SetValue(ConstString(mangled), true);
+ else if ((die.GetParent().Tag() == DW_TAG_compile_unit ||
+ die.GetParent().Tag() == DW_TAG_partial_unit) &&
+ Language::LanguageIsCPlusPlus(die.GetLanguage()) &&
+ !Language::LanguageIsObjC(die.GetLanguage()) && name &&
+ strcmp(name, "main") != 0) {
+ // If the mangled name is not present in the DWARF, generate the
+ // demangled name using the decl context. We skip if the function is
+ // "main" as its name is never mangled.
+ bool is_static = false;
+ bool is_variadic = false;
+ bool has_template_params = false;
+ unsigned type_quals = 0;
+ std::vector<CompilerType> param_types;
+ std::vector<clang::ParmVarDecl *> param_decls;
+ DWARFDeclContext decl_ctx;
+ StreamString sstr;
+
+ die.GetDWARFDeclContext(decl_ctx);
+ sstr << decl_ctx.GetQualifiedName();
+
+ clang::DeclContext *containing_decl_ctx =
+ GetClangDeclContextContainingDIE(die, nullptr);
+ ParseChildParameters(containing_decl_ctx, die, true, is_static,
+ is_variadic, has_template_params, param_types,
+ param_decls, type_quals);
+ sstr << "(";
+ for (size_t i = 0; i < param_types.size(); i++) {
+ if (i > 0)
+ sstr << ", ";
+ sstr << param_types[i].GetTypeName();
+ }
+ if (is_variadic)
+ sstr << ", ...";
+ sstr << ")";
+ if (type_quals & clang::Qualifiers::Const)
+ sstr << " const";
+
+ func_name.SetValue(ConstString(sstr.GetString()), false);
+ } else
+ func_name.SetValue(ConstString(name), false);
+
+ FunctionSP func_sp;
+ std::unique_ptr<Declaration> decl_up;
+ if (decl_file != 0 || decl_line != 0 || decl_column != 0)
+ decl_up.reset(new Declaration(die.GetCU()->GetFile(decl_file),
+ decl_line, decl_column));
+
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ // Supply the type _only_ if it has already been parsed
+ Type *func_type = dwarf->GetDIEToType().lookup(die.GetDIE());
+
+ assert(func_type == nullptr || func_type != DIE_IS_BEING_PARSED);
+
+ if (dwarf->FixupAddress(func_range.GetBaseAddress())) {
+ const user_id_t func_user_id = die.GetID();
+ func_sp =
+ std::make_shared<Function>(&comp_unit,
+ func_user_id, // UserID is the DIE offset
+ func_user_id, func_name, func_type,
+ func_range); // first address range
+
+ if (func_sp.get() != nullptr) {
+ if (frame_base.IsValid())
+ func_sp->GetFrameBaseExpression() = frame_base;
+ comp_unit.AddFunction(func_sp);
+ return func_sp.get();
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool DWARFASTParserClang::ParseChildMembers(
+ const DWARFDIE &parent_die, CompilerType &class_clang_type,
+ const LanguageType class_language,
+ std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes,
+ std::vector<int> &member_accessibilities,
+ std::vector<DWARFDIE> &member_function_dies,
+ DelayedPropertyList &delayed_properties, AccessType &default_accessibility,
+ bool &is_a_class, ClangASTImporter::LayoutInfo &layout_info) {
+ if (!parent_die)
+ return false;
+
+ // Get the parent byte size so we can verify any members will fit
+ const uint64_t parent_byte_size =
+ parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX);
+ const uint64_t parent_bit_size =
+ parent_byte_size == UINT64_MAX ? UINT64_MAX : parent_byte_size * 8;
+
+ uint32_t member_idx = 0;
+ BitfieldInfo last_field_info;
+
+ ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule();
+ ClangASTContext *ast =
+ llvm::dyn_cast_or_null<ClangASTContext>(class_clang_type.GetTypeSystem());
+ if (ast == nullptr)
+ return false;
+
+ for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
+ die = die.GetSibling()) {
+ dw_tag_t tag = die.Tag();
+
+ switch (tag) {
+ case DW_TAG_member:
+ case DW_TAG_APPLE_property: {
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ if (num_attributes > 0) {
+ const char *name = nullptr;
+ const char *prop_name = nullptr;
+ const char *prop_getter_name = nullptr;
+ const char *prop_setter_name = nullptr;
+ uint32_t prop_attributes = 0;
+
+ bool is_artificial = false;
+ DWARFFormValue encoding_form;
+ AccessType accessibility = eAccessNone;
+ uint32_t member_byte_offset =
+ (parent_die.Tag() == DW_TAG_union_type) ? 0 : UINT32_MAX;
+ llvm::Optional<uint64_t> byte_size;
+ int64_t bit_offset = 0;
+ uint64_t data_bit_offset = UINT64_MAX;
+ size_t bit_size = 0;
+ bool is_external =
+ false; // On DW_TAG_members, this means the member is static
+ uint32_t i;
+ for (i = 0; i < num_attributes && !is_artificial; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_name:
+ name = form_value.AsCString();
+ break;
+ case DW_AT_type:
+ encoding_form = form_value;
+ break;
+ case DW_AT_bit_offset:
+ bit_offset = form_value.Signed();
+ break;
+ case DW_AT_bit_size:
+ bit_size = form_value.Unsigned();
+ break;
+ case DW_AT_byte_size:
+ byte_size = form_value.Unsigned();
+ break;
+ case DW_AT_data_bit_offset:
+ data_bit_offset = form_value.Unsigned();
+ break;
+ case DW_AT_data_member_location:
+ if (form_value.BlockData()) {
+ Value initialValue(0);
+ Value memberOffset(0);
+ const DWARFDataExtractor &debug_info_data = die.GetData();
+ uint32_t block_length = form_value.Unsigned();
+ uint32_t block_offset =
+ form_value.BlockData() - debug_info_data.GetDataStart();
+ if (DWARFExpression::Evaluate(
+ nullptr, // ExecutionContext *
+ nullptr, // RegisterContext *
+ module_sp, debug_info_data, die.GetCU(), block_offset,
+ block_length, eRegisterKindDWARF, &initialValue,
+ nullptr, memberOffset, nullptr)) {
+ member_byte_offset =
+ memberOffset.ResolveValue(nullptr).UInt();
+ }
+ } else {
+ // With DWARF 3 and later, if the value is an integer constant,
+ // this form value is the offset in bytes from the beginning of
+ // the containing entity.
+ member_byte_offset = form_value.Unsigned();
+ }
+ break;
+
+ case DW_AT_accessibility:
+ accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned());
+ break;
+ case DW_AT_artificial:
+ is_artificial = form_value.Boolean();
+ break;
+ case DW_AT_APPLE_property_name:
+ prop_name = form_value.AsCString();
+ break;
+ case DW_AT_APPLE_property_getter:
+ prop_getter_name = form_value.AsCString();
+ break;
+ case DW_AT_APPLE_property_setter:
+ prop_setter_name = form_value.AsCString();
+ break;
+ case DW_AT_APPLE_property_attribute:
+ prop_attributes = form_value.Unsigned();
+ break;
+ case DW_AT_external:
+ is_external = form_value.Boolean();
+ break;
+
+ default:
+ case DW_AT_declaration:
+ case DW_AT_description:
+ case DW_AT_mutable:
+ case DW_AT_visibility:
+ case DW_AT_sibling:
+ break;
+ }
+ }
+ }
+
+ if (prop_name) {
+ ConstString fixed_getter;
+ ConstString fixed_setter;
+
+ // Check if the property getter/setter were provided as full names.
+ // We want basenames, so we extract them.
+
+ if (prop_getter_name && prop_getter_name[0] == '-') {
+ ObjCLanguage::MethodName prop_getter_method(prop_getter_name, true);
+ prop_getter_name = prop_getter_method.GetSelector().GetCString();
+ }
+
+ if (prop_setter_name && prop_setter_name[0] == '-') {
+ ObjCLanguage::MethodName prop_setter_method(prop_setter_name, true);
+ prop_setter_name = prop_setter_method.GetSelector().GetCString();
+ }
+
+ // If the names haven't been provided, they need to be filled in.
+
+ if (!prop_getter_name) {
+ prop_getter_name = prop_name;
+ }
+ if (!prop_setter_name && prop_name[0] &&
+ !(prop_attributes & DW_APPLE_PROPERTY_readonly)) {
+ StreamString ss;
+
+ ss.Printf("set%c%s:", toupper(prop_name[0]), &prop_name[1]);
+
+ fixed_setter.SetString(ss.GetString());
+ prop_setter_name = fixed_setter.GetCString();
+ }
+ }
+
+ // Clang has a DWARF generation bug where sometimes it represents
+ // fields that are references with bad byte size and bit size/offset
+ // information such as:
+ //
+ // DW_AT_byte_size( 0x00 )
+ // DW_AT_bit_size( 0x40 )
+ // DW_AT_bit_offset( 0xffffffffffffffc0 )
+ //
+ // So check the bit offset to make sure it is sane, and if the values
+ // are not sane, remove them. If we don't do this then we will end up
+ // with a crash if we try to use this type in an expression when clang
+ // becomes unhappy with its recycled debug info.
+
+ if (byte_size.getValueOr(0) == 0 && bit_offset < 0) {
+ bit_size = 0;
+ bit_offset = 0;
+ }
+
+ // FIXME: Make Clang ignore Objective-C accessibility for expressions
+ if (class_language == eLanguageTypeObjC ||
+ class_language == eLanguageTypeObjC_plus_plus)
+ accessibility = eAccessNone;
+
+ // Handle static members
+ if (is_external && member_byte_offset == UINT32_MAX) {
+ Type *var_type = die.ResolveTypeUID(encoding_form.Reference());
+
+ if (var_type) {
+ if (accessibility == eAccessNone)
+ accessibility = eAccessPublic;
+ ClangASTContext::AddVariableToRecordType(
+ class_clang_type, name, var_type->GetLayoutCompilerType(),
+ accessibility);
+ }
+ break;
+ }
+
+ if (!is_artificial) {
+ Type *member_type = die.ResolveTypeUID(encoding_form.Reference());
+
+ clang::FieldDecl *field_decl = nullptr;
+ if (tag == DW_TAG_member) {
+ if (member_type) {
+ if (accessibility == eAccessNone)
+ accessibility = default_accessibility;
+ member_accessibilities.push_back(accessibility);
+
+ uint64_t field_bit_offset =
+ (member_byte_offset == UINT32_MAX ? 0
+ : (member_byte_offset * 8));
+ if (bit_size > 0) {
+
+ BitfieldInfo this_field_info;
+ this_field_info.bit_offset = field_bit_offset;
+ this_field_info.bit_size = bit_size;
+
+ /////////////////////////////////////////////////////////////
+ // How to locate a field given the DWARF debug information
+ //
+ // AT_byte_size indicates the size of the word in which the bit
+ // offset must be interpreted.
+ //
+ // AT_data_member_location indicates the byte offset of the
+ // word from the base address of the structure.
+ //
+ // AT_bit_offset indicates how many bits into the word
+ // (according to the host endianness) the low-order bit of the
+ // field starts. AT_bit_offset can be negative.
+ //
+ // AT_bit_size indicates the size of the field in bits.
+ /////////////////////////////////////////////////////////////
+
+ if (data_bit_offset != UINT64_MAX) {
+ this_field_info.bit_offset = data_bit_offset;
+ } else {
+ if (!byte_size)
+ byte_size = member_type->GetByteSize();
+
+ ObjectFile *objfile = die.GetDWARF()->GetObjectFile();
+ if (objfile->GetByteOrder() == eByteOrderLittle) {
+ this_field_info.bit_offset += byte_size.getValueOr(0) * 8;
+ this_field_info.bit_offset -= (bit_offset + bit_size);
+ } else {
+ this_field_info.bit_offset += bit_offset;
+ }
+ }
+
+ if ((this_field_info.bit_offset >= parent_bit_size) ||
+ !last_field_info.NextBitfieldOffsetIsValid(
+ this_field_info.bit_offset)) {
+ ObjectFile *objfile = die.GetDWARF()->GetObjectFile();
+ objfile->GetModule()->ReportWarning(
+ "0x%8.8" PRIx64 ": %s bitfield named \"%s\" has invalid "
+ "bit offset (0x%8.8" PRIx64
+ ") member will be ignored. Please file a bug against the "
+ "compiler and include the preprocessed output for %s\n",
+ die.GetID(), DW_TAG_value_to_name(tag), name,
+ this_field_info.bit_offset,
+ GetUnitName(parent_die).c_str());
+ this_field_info.Clear();
+ continue;
+ }
+
+ // Update the field bit offset we will report for layout
+ field_bit_offset = this_field_info.bit_offset;
+
+ // If the member to be emitted did not start on a character
+ // boundary and there is empty space between the last field and
+ // this one, then we need to emit an anonymous member filling
+ // up the space up to its start. There are three cases here:
+ //
+ // 1 If the previous member ended on a character boundary, then
+ // we can emit an
+ // anonymous member starting at the most recent character
+ // boundary.
+ //
+ // 2 If the previous member did not end on a character boundary
+ // and the distance
+ // from the end of the previous member to the current member
+ // is less than a
+ // word width, then we can emit an anonymous member starting
+ // right after the
+ // previous member and right before this member.
+ //
+ // 3 If the previous member did not end on a character boundary
+ // and the distance
+ // from the end of the previous member to the current member
+ // is greater than
+ // or equal a word width, then we act as in Case 1.
+
+ const uint64_t character_width = 8;
+ const uint64_t word_width = 32;
+
+ // Objective-C has invalid DW_AT_bit_offset values in older
+ // versions of clang, so we have to be careful and only insert
+ // unnamed bitfields if we have a new enough clang.
+ bool detect_unnamed_bitfields = true;
+
+ if (class_language == eLanguageTypeObjC ||
+ class_language == eLanguageTypeObjC_plus_plus)
+ detect_unnamed_bitfields =
+ die.GetCU()->Supports_unnamed_objc_bitfields();
+
+ if (detect_unnamed_bitfields) {
+ BitfieldInfo anon_field_info;
+
+ if ((this_field_info.bit_offset % character_width) !=
+ 0) // not char aligned
+ {
+ uint64_t last_field_end = 0;
+
+ if (last_field_info.IsValid())
+ last_field_end =
+ last_field_info.bit_offset + last_field_info.bit_size;
+
+ if (this_field_info.bit_offset != last_field_end) {
+ if (((last_field_end % character_width) == 0) || // case 1
+ (this_field_info.bit_offset - last_field_end >=
+ word_width)) // case 3
+ {
+ anon_field_info.bit_size =
+ this_field_info.bit_offset % character_width;
+ anon_field_info.bit_offset =
+ this_field_info.bit_offset -
+ anon_field_info.bit_size;
+ } else // case 2
+ {
+ anon_field_info.bit_size =
+ this_field_info.bit_offset - last_field_end;
+ anon_field_info.bit_offset = last_field_end;
+ }
+ }
+ }
+
+ if (anon_field_info.IsValid()) {
+ clang::FieldDecl *unnamed_bitfield_decl =
+ ClangASTContext::AddFieldToRecordType(
+ class_clang_type, llvm::StringRef(),
+ m_ast.GetBuiltinTypeForEncodingAndBitSize(
+ eEncodingSint, word_width),
+ accessibility, anon_field_info.bit_size);
+
+ layout_info.field_offsets.insert(std::make_pair(
+ unnamed_bitfield_decl, anon_field_info.bit_offset));
+ }
+ }
+ last_field_info = this_field_info;
+ } else {
+ last_field_info.Clear();
+ }
+
+ CompilerType member_clang_type =
+ member_type->GetLayoutCompilerType();
+ if (!member_clang_type.IsCompleteType())
+ member_clang_type.GetCompleteType();
+
+ {
+ // Older versions of clang emit array[0] and array[1] in the
+ // same way (<rdar://problem/12566646>). 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].
+
+ 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 (member_byte_offset >= parent_byte_size) {
+ if (member_array_size != 1 &&
+ (member_array_size != 0 ||
+ member_byte_offset > parent_byte_size)) {
+ module_sp->ReportError(
+ "0x%8.8" PRIx64
+ ": DW_TAG_member '%s' refers to type 0x%8.8x"
+ " which extends beyond the bounds of 0x%8.8" PRIx64,
+ die.GetID(), name,
+ encoding_form.Reference().GetOffset(),
+ parent_die.GetID());
+ }
+
+ member_clang_type = m_ast.CreateArrayType(
+ member_array_element_type, 0, false);
+ }
+ }
+ }
+
+ if (ClangASTContext::IsCXXClassType(member_clang_type) &&
+ !member_clang_type.GetCompleteType()) {
+ if (die.GetCU()->GetProducer() == eProducerClang)
+ module_sp->ReportError(
+ "DWARF DIE at 0x%8.8x (class %s) has a member variable "
+ "0x%8.8x (%s) whose type is a forward declaration, not a "
+ "complete definition.\nTry compiling the source file "
+ "with -fstandalone-debug",
+ parent_die.GetOffset(), parent_die.GetName(),
+ die.GetOffset(), name);
+ else
+ module_sp->ReportError(
+ "DWARF DIE at 0x%8.8x (class %s) has a member variable "
+ "0x%8.8x (%s) whose type is a forward declaration, not a "
+ "complete definition.\nPlease file a bug against the "
+ "compiler and include the preprocessed output for %s",
+ parent_die.GetOffset(), parent_die.GetName(),
+ die.GetOffset(), name, GetUnitName(parent_die).c_str());
+ // We have no choice other than to pretend that the member
+ // class is complete. If we don't do this, clang will crash
+ // when trying to layout the class. Since we provide layout
+ // assistance, all ivars in this class and other classes will
+ // be fine, this is the best we can do short of crashing.
+ if (ClangASTContext::StartTagDeclarationDefinition(
+ member_clang_type)) {
+ ClangASTContext::CompleteTagDeclarationDefinition(
+ member_clang_type);
+ } else {
+ module_sp->ReportError(
+ "DWARF DIE at 0x%8.8x (class %s) has a member variable "
+ "0x%8.8x (%s) whose type claims to be a C++ class but we "
+ "were not able to start its definition.\nPlease file a "
+ "bug and attach the file at the start of this error "
+ "message",
+ parent_die.GetOffset(), parent_die.GetName(),
+ die.GetOffset(), name);
+ }
+ }
+
+ field_decl = ClangASTContext::AddFieldToRecordType(
+ class_clang_type, name, member_clang_type, accessibility,
+ bit_size);
+
+ m_ast.SetMetadataAsUserID(field_decl, die.GetID());
+
+ layout_info.field_offsets.insert(
+ std::make_pair(field_decl, field_bit_offset));
+ } else {
+ if (name)
+ module_sp->ReportError(
+ "0x%8.8" PRIx64
+ ": DW_TAG_member '%s' refers to type 0x%8.8x"
+ " which was unable to be parsed",
+ die.GetID(), name, encoding_form.Reference().GetOffset());
+ else
+ module_sp->ReportError(
+ "0x%8.8" PRIx64 ": DW_TAG_member refers to type 0x%8.8x"
+ " which was unable to be parsed",
+ die.GetID(), encoding_form.Reference().GetOffset());
+ }
+ }
+
+ if (prop_name != nullptr && member_type) {
+ clang::ObjCIvarDecl *ivar_decl = nullptr;
+
+ if (field_decl) {
+ ivar_decl = clang::dyn_cast<clang::ObjCIvarDecl>(field_decl);
+ assert(ivar_decl != nullptr);
+ }
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(die.GetID());
+ delayed_properties.push_back(DelayedAddObjCClassProperty(
+ class_clang_type, prop_name,
+ member_type->GetLayoutCompilerType(), ivar_decl,
+ prop_setter_name, prop_getter_name, prop_attributes,
+ &metadata));
+
+ if (ivar_decl)
+ m_ast.SetMetadataAsUserID(ivar_decl, die.GetID());
+ }
+ }
+ }
+ ++member_idx;
+ } break;
+
+ case DW_TAG_subprogram:
+ // Let the type parsing code handle this one for us.
+ member_function_dies.push_back(die);
+ break;
+
+ case DW_TAG_inheritance: {
+ is_a_class = true;
+ if (default_accessibility == eAccessNone)
+ default_accessibility = eAccessPrivate;
+ // TODO: implement DW_TAG_inheritance type parsing
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ if (num_attributes > 0) {
+ DWARFFormValue encoding_form;
+ AccessType accessibility = default_accessibility;
+ bool is_virtual = false;
+ bool is_base_of_class = true;
+ off_t member_byte_offset = 0;
+ uint32_t i;
+ for (i = 0; i < num_attributes; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_type:
+ encoding_form = form_value;
+ break;
+ case DW_AT_data_member_location:
+ if (form_value.BlockData()) {
+ Value initialValue(0);
+ Value memberOffset(0);
+ const DWARFDataExtractor &debug_info_data = die.GetData();
+ uint32_t block_length = form_value.Unsigned();
+ uint32_t block_offset =
+ form_value.BlockData() - debug_info_data.GetDataStart();
+ if (DWARFExpression::Evaluate(nullptr, nullptr, module_sp,
+ debug_info_data, die.GetCU(),
+ block_offset, block_length,
+ eRegisterKindDWARF, &initialValue,
+ nullptr, memberOffset, nullptr)) {
+ member_byte_offset =
+ memberOffset.ResolveValue(nullptr).UInt();
+ }
+ } else {
+ // With DWARF 3 and later, if the value is an integer constant,
+ // this form value is the offset in bytes from the beginning of
+ // the containing entity.
+ member_byte_offset = form_value.Unsigned();
+ }
+ break;
+
+ case DW_AT_accessibility:
+ accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned());
+ break;
+
+ case DW_AT_virtuality:
+ is_virtual = form_value.Boolean();
+ break;
+
+ case DW_AT_sibling:
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ Type *base_class_type = die.ResolveTypeUID(encoding_form.Reference());
+ if (base_class_type == nullptr) {
+ module_sp->ReportError("0x%8.8x: DW_TAG_inheritance failed to "
+ "resolve the base class at 0x%8.8x"
+ " from enclosing type 0x%8.8x. \nPlease file "
+ "a bug and attach the file at the start of "
+ "this error message",
+ die.GetOffset(),
+ encoding_form.Reference().GetOffset(),
+ parent_die.GetOffset());
+ break;
+ }
+
+ CompilerType base_class_clang_type =
+ base_class_type->GetFullCompilerType();
+ assert(base_class_clang_type);
+ if (class_language == eLanguageTypeObjC) {
+ ast->SetObjCSuperClass(class_clang_type, base_class_clang_type);
+ } else {
+ std::unique_ptr<clang::CXXBaseSpecifier> result =
+ ast->CreateBaseClassSpecifier(
+ base_class_clang_type.GetOpaqueQualType(), accessibility,
+ is_virtual, is_base_of_class);
+ if (!result)
+ break;
+
+ base_classes.push_back(std::move(result));
+
+ if (is_virtual) {
+ // Do not specify any offset for virtual inheritance. The DWARF
+ // produced by clang doesn't give us a constant offset, but gives
+ // us a DWARF expressions that requires an actual object in memory.
+ // the DW_AT_data_member_location for a virtual base class looks
+ // like:
+ // DW_AT_data_member_location( DW_OP_dup, DW_OP_deref,
+ // DW_OP_constu(0x00000018), DW_OP_minus, DW_OP_deref,
+ // DW_OP_plus )
+ // Given this, there is really no valid response we can give to
+ // clang for virtual base class offsets, and this should eventually
+ // be removed from LayoutRecordType() in the external
+ // AST source in clang.
+ } else {
+ layout_info.base_offsets.insert(std::make_pair(
+ ast->GetAsCXXRecordDecl(
+ base_class_clang_type.GetOpaqueQualType()),
+ clang::CharUnits::fromQuantity(member_byte_offset)));
+ }
+ }
+ }
+ } break;
+
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+size_t DWARFASTParserClang::ParseChildParameters(
+ clang::DeclContext *containing_decl_ctx, const DWARFDIE &parent_die,
+ bool skip_artificial, bool &is_static, bool &is_variadic,
+ bool &has_template_params, std::vector<CompilerType> &function_param_types,
+ std::vector<clang::ParmVarDecl *> &function_param_decls,
+ unsigned &type_quals) {
+ if (!parent_die)
+ return 0;
+
+ size_t arg_idx = 0;
+ for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
+ die = die.GetSibling()) {
+ const dw_tag_t tag = die.Tag();
+ switch (tag) {
+ case DW_TAG_formal_parameter: {
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ if (num_attributes > 0) {
+ const char *name = nullptr;
+ DWARFFormValue param_type_die_form;
+ bool is_artificial = false;
+ // one of None, Auto, Register, Extern, Static, PrivateExtern
+
+ clang::StorageClass storage = clang::SC_None;
+ uint32_t i;
+ for (i = 0; i < num_attributes; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_name:
+ name = form_value.AsCString();
+ break;
+ case DW_AT_type:
+ param_type_die_form = form_value;
+ break;
+ case DW_AT_artificial:
+ is_artificial = form_value.Boolean();
+ break;
+ case DW_AT_location:
+ case DW_AT_const_value:
+ case DW_AT_default_value:
+ case DW_AT_description:
+ case DW_AT_endianity:
+ case DW_AT_is_optional:
+ case DW_AT_segment:
+ case DW_AT_variable_parameter:
+ default:
+ case DW_AT_abstract_origin:
+ case DW_AT_sibling:
+ break;
+ }
+ }
+ }
+
+ bool skip = false;
+ if (skip_artificial && is_artificial) {
+ // In order to determine if a C++ member function is "const" we
+ // have to look at the const-ness of "this"...
+ if (arg_idx == 0 &&
+ DeclKindIsCXXClass(containing_decl_ctx->getDeclKind()) &&
+ // Often times compilers omit the "this" name for the
+ // specification DIEs, so we can't rely upon the name being in
+ // the formal parameter DIE...
+ (name == nullptr || ::strcmp(name, "this") == 0)) {
+ Type *this_type =
+ die.ResolveTypeUID(param_type_die_form.Reference());
+ if (this_type) {
+ uint32_t encoding_mask = this_type->GetEncodingMask();
+ if (encoding_mask & Type::eEncodingIsPointerUID) {
+ is_static = false;
+
+ if (encoding_mask & (1u << Type::eEncodingIsConstUID))
+ type_quals |= clang::Qualifiers::Const;
+ if (encoding_mask & (1u << Type::eEncodingIsVolatileUID))
+ type_quals |= clang::Qualifiers::Volatile;
+ }
+ }
+ }
+ skip = true;
+ }
+
+ if (!skip) {
+ Type *type = die.ResolveTypeUID(param_type_die_form.Reference());
+ if (type) {
+ function_param_types.push_back(type->GetForwardCompilerType());
+
+ clang::ParmVarDecl *param_var_decl =
+ m_ast.CreateParameterDeclaration(containing_decl_ctx, name,
+ type->GetForwardCompilerType(),
+ storage);
+ assert(param_var_decl);
+ function_param_decls.push_back(param_var_decl);
+
+ m_ast.SetMetadataAsUserID(param_var_decl, die.GetID());
+ }
+ }
+ }
+ arg_idx++;
+ } break;
+
+ case DW_TAG_unspecified_parameters:
+ is_variadic = true;
+ break;
+
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_template_value_parameter:
+ case DW_TAG_GNU_template_parameter_pack:
+ // The one caller of this was never using the template_param_infos, and
+ // the local variable was taking up a large amount of stack space in
+ // SymbolFileDWARF::ParseType() so this was removed. If we ever need the
+ // template params back, we can add them back.
+ // ParseTemplateDIE (dwarf_cu, die, template_param_infos);
+ has_template_params = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return arg_idx;
+}
+
+llvm::Optional<SymbolFile::ArrayInfo>
+DWARFASTParser::ParseChildArrayInfo(const DWARFDIE &parent_die,
+ const ExecutionContext *exe_ctx) {
+ SymbolFile::ArrayInfo array_info;
+ if (!parent_die)
+ return llvm::None;
+
+ for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
+ die = die.GetSibling()) {
+ const dw_tag_t tag = die.Tag();
+ switch (tag) {
+ case DW_TAG_subrange_type: {
+ DWARFAttributes attributes;
+ const size_t num_child_attributes = die.GetAttributes(attributes);
+ if (num_child_attributes > 0) {
+ uint64_t num_elements = 0;
+ uint64_t lower_bound = 0;
+ uint64_t upper_bound = 0;
+ bool upper_bound_valid = false;
+ uint32_t i;
+ for (i = 0; i < num_child_attributes; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_name:
+ break;
+
+ case DW_AT_count:
+ if (DWARFDIE var_die = die.GetReferencedDIE(DW_AT_count)) {
+ if (var_die.Tag() == DW_TAG_variable)
+ if (exe_ctx) {
+ if (auto frame = exe_ctx->GetFrameSP()) {
+ Status error;
+ lldb::VariableSP var_sp;
+ auto valobj_sp = frame->GetValueForVariableExpressionPath(
+ var_die.GetName(), eNoDynamicValues, 0, var_sp,
+ error);
+ if (valobj_sp) {
+ num_elements = valobj_sp->GetValueAsUnsigned(0);
+ break;
+ }
+ }
+ }
+ } else
+ num_elements = form_value.Unsigned();
+ break;
+
+ case DW_AT_bit_stride:
+ array_info.bit_stride = form_value.Unsigned();
+ break;
+
+ case DW_AT_byte_stride:
+ array_info.byte_stride = form_value.Unsigned();
+ break;
+
+ case DW_AT_lower_bound:
+ lower_bound = form_value.Unsigned();
+ break;
+
+ case DW_AT_upper_bound:
+ upper_bound_valid = true;
+ upper_bound = form_value.Unsigned();
+ break;
+
+ default:
+ case DW_AT_abstract_origin:
+ case DW_AT_accessibility:
+ case DW_AT_allocated:
+ case DW_AT_associated:
+ case DW_AT_data_location:
+ case DW_AT_declaration:
+ case DW_AT_description:
+ case DW_AT_sibling:
+ case DW_AT_threads_scaled:
+ case DW_AT_type:
+ case DW_AT_visibility:
+ break;
+ }
+ }
+ }
+
+ if (num_elements == 0) {
+ if (upper_bound_valid && upper_bound >= lower_bound)
+ num_elements = upper_bound - lower_bound + 1;
+ }
+
+ array_info.element_orders.push_back(num_elements);
+ }
+ } break;
+ }
+ }
+ return array_info;
+}
+
+Type *DWARFASTParserClang::GetTypeForDIE(const DWARFDIE &die) {
+ if (die) {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ if (num_attributes > 0) {
+ DWARFFormValue type_die_form;
+ for (size_t i = 0; i < num_attributes; ++i) {
+ dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+
+ if (attr == DW_AT_type &&
+ attributes.ExtractFormValueAtIndex(i, form_value))
+ return dwarf->ResolveTypeUID(form_value.Reference(), true);
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+clang::Decl *DWARFASTParserClang::GetClangDeclForDIE(const DWARFDIE &die) {
+ if (!die)
+ return nullptr;
+
+ switch (die.Tag()) {
+ case DW_TAG_variable:
+ case DW_TAG_constant:
+ case DW_TAG_formal_parameter:
+ case DW_TAG_imported_declaration:
+ case DW_TAG_imported_module:
+ break;
+ default:
+ return nullptr;
+ }
+
+ DIEToDeclMap::iterator cache_pos = m_die_to_decl.find(die.GetDIE());
+ if (cache_pos != m_die_to_decl.end())
+ return cache_pos->second;
+
+ if (DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification)) {
+ clang::Decl *decl = GetClangDeclForDIE(spec_die);
+ m_die_to_decl[die.GetDIE()] = decl;
+ m_decl_to_die[decl].insert(die.GetDIE());
+ return decl;
+ }
+
+ if (DWARFDIE abstract_origin_die =
+ die.GetReferencedDIE(DW_AT_abstract_origin)) {
+ clang::Decl *decl = GetClangDeclForDIE(abstract_origin_die);
+ m_die_to_decl[die.GetDIE()] = decl;
+ m_decl_to_die[decl].insert(die.GetDIE());
+ return decl;
+ }
+
+ clang::Decl *decl = nullptr;
+ switch (die.Tag()) {
+ case DW_TAG_variable:
+ case DW_TAG_constant:
+ case DW_TAG_formal_parameter: {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ Type *type = GetTypeForDIE(die);
+ if (dwarf && type) {
+ const char *name = die.GetName();
+ clang::DeclContext *decl_context =
+ ClangASTContext::DeclContextGetAsDeclContext(
+ dwarf->GetDeclContextContainingUID(die.GetID()));
+ decl = m_ast.CreateVariableDeclaration(
+ decl_context, name,
+ ClangUtil::GetQualType(type->GetForwardCompilerType()));
+ }
+ break;
+ }
+ case DW_TAG_imported_declaration: {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import);
+ if (imported_uid) {
+ CompilerDecl imported_decl = imported_uid.GetDecl();
+ if (imported_decl) {
+ clang::DeclContext *decl_context =
+ ClangASTContext::DeclContextGetAsDeclContext(
+ dwarf->GetDeclContextContainingUID(die.GetID()));
+ if (clang::NamedDecl *clang_imported_decl =
+ llvm::dyn_cast<clang::NamedDecl>(
+ (clang::Decl *)imported_decl.GetOpaqueDecl()))
+ decl =
+ m_ast.CreateUsingDeclaration(decl_context, clang_imported_decl);
+ }
+ }
+ break;
+ }
+ case DW_TAG_imported_module: {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import);
+
+ if (imported_uid) {
+ CompilerDeclContext imported_decl_ctx = imported_uid.GetDeclContext();
+ if (imported_decl_ctx) {
+ clang::DeclContext *decl_context =
+ ClangASTContext::DeclContextGetAsDeclContext(
+ dwarf->GetDeclContextContainingUID(die.GetID()));
+ if (clang::NamespaceDecl *ns_decl =
+ ClangASTContext::DeclContextGetAsNamespaceDecl(
+ imported_decl_ctx))
+ decl = m_ast.CreateUsingDirectiveDeclaration(decl_context, ns_decl);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ m_die_to_decl[die.GetDIE()] = decl;
+ m_decl_to_die[decl].insert(die.GetDIE());
+
+ return decl;
+}
+
+clang::DeclContext *
+DWARFASTParserClang::GetClangDeclContextForDIE(const DWARFDIE &die) {
+ if (die) {
+ clang::DeclContext *decl_ctx = GetCachedClangDeclContextForDIE(die);
+ if (decl_ctx)
+ return decl_ctx;
+
+ bool try_parsing_type = true;
+ switch (die.Tag()) {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ decl_ctx = m_ast.GetTranslationUnitDecl();
+ try_parsing_type = false;
+ break;
+
+ case DW_TAG_namespace:
+ decl_ctx = ResolveNamespaceDIE(die);
+ try_parsing_type = false;
+ break;
+
+ case DW_TAG_lexical_block:
+ decl_ctx = GetDeclContextForBlock(die);
+ try_parsing_type = false;
+ break;
+
+ default:
+ break;
+ }
+
+ if (decl_ctx == nullptr && try_parsing_type) {
+ Type *type = die.GetDWARF()->ResolveType(die);
+ if (type)
+ decl_ctx = GetCachedClangDeclContextForDIE(die);
+ }
+
+ if (decl_ctx) {
+ LinkDeclContextToDIE(decl_ctx, die);
+ return decl_ctx;
+ }
+ }
+ return nullptr;
+}
+
+static bool IsSubroutine(const DWARFDIE &die) {
+ switch (die.Tag()) {
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DWARFDIE GetContainingFunctionWithAbstractOrigin(const DWARFDIE &die) {
+ for (DWARFDIE candidate = die; candidate; candidate = candidate.GetParent()) {
+ if (IsSubroutine(candidate)) {
+ if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) {
+ return candidate;
+ } else {
+ return DWARFDIE();
+ }
+ }
+ }
+ assert(0 && "Shouldn't call GetContainingFunctionWithAbstractOrigin on "
+ "something not in a function");
+ return DWARFDIE();
+}
+
+static DWARFDIE FindAnyChildWithAbstractOrigin(const DWARFDIE &context) {
+ for (DWARFDIE candidate = context.GetFirstChild(); candidate.IsValid();
+ candidate = candidate.GetSibling()) {
+ if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) {
+ return candidate;
+ }
+ }
+ return DWARFDIE();
+}
+
+static DWARFDIE FindFirstChildWithAbstractOrigin(const DWARFDIE &block,
+ const DWARFDIE &function) {
+ assert(IsSubroutine(function));
+ for (DWARFDIE context = block; context != function.GetParent();
+ context = context.GetParent()) {
+ assert(!IsSubroutine(context) || context == function);
+ if (DWARFDIE child = FindAnyChildWithAbstractOrigin(context)) {
+ return child;
+ }
+ }
+ return DWARFDIE();
+}
+
+clang::DeclContext *
+DWARFASTParserClang::GetDeclContextForBlock(const DWARFDIE &die) {
+ assert(die.Tag() == DW_TAG_lexical_block);
+ DWARFDIE containing_function_with_abstract_origin =
+ GetContainingFunctionWithAbstractOrigin(die);
+ if (!containing_function_with_abstract_origin) {
+ return (clang::DeclContext *)ResolveBlockDIE(die);
+ }
+ DWARFDIE child = FindFirstChildWithAbstractOrigin(
+ die, containing_function_with_abstract_origin);
+ CompilerDeclContext decl_context =
+ GetDeclContextContainingUIDFromDWARF(child);
+ return (clang::DeclContext *)decl_context.GetOpaqueDeclContext();
+}
+
+clang::BlockDecl *DWARFASTParserClang::ResolveBlockDIE(const DWARFDIE &die) {
+ if (die && die.Tag() == DW_TAG_lexical_block) {
+ clang::BlockDecl *decl =
+ llvm::cast_or_null<clang::BlockDecl>(m_die_to_decl_ctx[die.GetDIE()]);
+
+ if (!decl) {
+ DWARFDIE decl_context_die;
+ clang::DeclContext *decl_context =
+ GetClangDeclContextContainingDIE(die, &decl_context_die);
+ decl = m_ast.CreateBlockDeclaration(decl_context);
+
+ if (decl)
+ LinkDeclContextToDIE((clang::DeclContext *)decl, die);
+ }
+
+ return decl;
+ }
+ return nullptr;
+}
+
+clang::NamespaceDecl *
+DWARFASTParserClang::ResolveNamespaceDIE(const DWARFDIE &die) {
+ if (die && die.Tag() == DW_TAG_namespace) {
+ // See if we already parsed this namespace DIE and associated it with a
+ // uniqued namespace declaration
+ clang::NamespaceDecl *namespace_decl =
+ static_cast<clang::NamespaceDecl *>(m_die_to_decl_ctx[die.GetDIE()]);
+ if (namespace_decl)
+ return namespace_decl;
+ else {
+ const char *namespace_name = die.GetName();
+ clang::DeclContext *containing_decl_ctx =
+ GetClangDeclContextContainingDIE(die, nullptr);
+ bool is_inline =
+ die.GetAttributeValueAsUnsigned(DW_AT_export_symbols, 0) != 0;
+
+ namespace_decl = m_ast.GetUniqueNamespaceDeclaration(
+ namespace_name, containing_decl_ctx, is_inline);
+ Log *log =
+ nullptr; // (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO));
+ if (log) {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ if (namespace_name) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log, "ASTContext => %p: 0x%8.8" PRIx64
+ ": DW_TAG_namespace with DW_AT_name(\"%s\") => "
+ "clang::NamespaceDecl *%p (original = %p)",
+ static_cast<void *>(m_ast.getASTContext()), die.GetID(),
+ namespace_name, static_cast<void *>(namespace_decl),
+ static_cast<void *>(namespace_decl->getOriginalNamespace()));
+ } else {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log, "ASTContext => %p: 0x%8.8" PRIx64
+ ": DW_TAG_namespace (anonymous) => clang::NamespaceDecl *%p "
+ "(original = %p)",
+ static_cast<void *>(m_ast.getASTContext()), die.GetID(),
+ static_cast<void *>(namespace_decl),
+ static_cast<void *>(namespace_decl->getOriginalNamespace()));
+ }
+ }
+
+ if (namespace_decl)
+ LinkDeclContextToDIE((clang::DeclContext *)namespace_decl, die);
+ return namespace_decl;
+ }
+ }
+ return nullptr;
+}
+
+clang::DeclContext *DWARFASTParserClang::GetClangDeclContextContainingDIE(
+ const DWARFDIE &die, DWARFDIE *decl_ctx_die_copy) {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+
+ DWARFDIE decl_ctx_die = dwarf->GetDeclContextDIEContainingDIE(die);
+
+ if (decl_ctx_die_copy)
+ *decl_ctx_die_copy = decl_ctx_die;
+
+ if (decl_ctx_die) {
+ clang::DeclContext *clang_decl_ctx =
+ GetClangDeclContextForDIE(decl_ctx_die);
+ if (clang_decl_ctx)
+ return clang_decl_ctx;
+ }
+ return m_ast.GetTranslationUnitDecl();
+}
+
+clang::DeclContext *
+DWARFASTParserClang::GetCachedClangDeclContextForDIE(const DWARFDIE &die) {
+ if (die) {
+ DIEToDeclContextMap::iterator pos = m_die_to_decl_ctx.find(die.GetDIE());
+ if (pos != m_die_to_decl_ctx.end())
+ return pos->second;
+ }
+ return nullptr;
+}
+
+void DWARFASTParserClang::LinkDeclContextToDIE(clang::DeclContext *decl_ctx,
+ const DWARFDIE &die) {
+ m_die_to_decl_ctx[die.GetDIE()] = decl_ctx;
+ // There can be many DIEs for a single decl context
+ // m_decl_ctx_to_die[decl_ctx].insert(die.GetDIE());
+ m_decl_ctx_to_die.insert(std::make_pair(decl_ctx, die));
+}
+
+bool DWARFASTParserClang::CopyUniqueClassMethodTypes(
+ const DWARFDIE &src_class_die, const DWARFDIE &dst_class_die,
+ lldb_private::Type *class_type, std::vector<DWARFDIE> &failures) {
+ if (!class_type || !src_class_die || !dst_class_die)
+ return false;
+ if (src_class_die.Tag() != dst_class_die.Tag())
+ return false;
+
+ // We need to complete the class type so we can get all of the method types
+ // parsed so we can then unique those types to their equivalent counterparts
+ // in "dst_cu" and "dst_class_die"
+ class_type->GetFullCompilerType();
+
+ DWARFDIE src_die;
+ DWARFDIE dst_die;
+ UniqueCStringMap<DWARFDIE> src_name_to_die;
+ UniqueCStringMap<DWARFDIE> dst_name_to_die;
+ UniqueCStringMap<DWARFDIE> src_name_to_die_artificial;
+ UniqueCStringMap<DWARFDIE> dst_name_to_die_artificial;
+ for (src_die = src_class_die.GetFirstChild(); src_die.IsValid();
+ src_die = src_die.GetSibling()) {
+ if (src_die.Tag() == DW_TAG_subprogram) {
+ // Make sure this is a declaration and not a concrete instance by looking
+ // for DW_AT_declaration set to 1. Sometimes concrete function instances
+ // are placed inside the class definitions and shouldn't be included in
+ // the list of things are are tracking here.
+ if (src_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) {
+ const char *src_name = src_die.GetMangledName();
+ if (src_name) {
+ ConstString src_const_name(src_name);
+ if (src_die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0))
+ src_name_to_die_artificial.Append(src_const_name, src_die);
+ else
+ src_name_to_die.Append(src_const_name, src_die);
+ }
+ }
+ }
+ }
+ for (dst_die = dst_class_die.GetFirstChild(); dst_die.IsValid();
+ dst_die = dst_die.GetSibling()) {
+ if (dst_die.Tag() == DW_TAG_subprogram) {
+ // Make sure this is a declaration and not a concrete instance by looking
+ // for DW_AT_declaration set to 1. Sometimes concrete function instances
+ // are placed inside the class definitions and shouldn't be included in
+ // the list of things are are tracking here.
+ if (dst_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) {
+ const char *dst_name = dst_die.GetMangledName();
+ if (dst_name) {
+ ConstString dst_const_name(dst_name);
+ if (dst_die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0))
+ dst_name_to_die_artificial.Append(dst_const_name, dst_die);
+ else
+ dst_name_to_die.Append(dst_const_name, dst_die);
+ }
+ }
+ }
+ }
+ const uint32_t src_size = src_name_to_die.GetSize();
+ const uint32_t dst_size = dst_name_to_die.GetSize();
+ Log *log = nullptr; // (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO |
+ // DWARF_LOG_TYPE_COMPLETION));
+
+ // Is everything kosher so we can go through the members at top speed?
+ bool fast_path = true;
+
+ if (src_size != dst_size) {
+ if (src_size != 0 && dst_size != 0) {
+ if (log)
+ log->Printf("warning: trying to unique class DIE 0x%8.8x to 0x%8.8x, "
+ "but they didn't have the same size (src=%d, dst=%d)",
+ src_class_die.GetOffset(), dst_class_die.GetOffset(),
+ src_size, dst_size);
+ }
+
+ fast_path = false;
+ }
+
+ uint32_t idx;
+
+ if (fast_path) {
+ for (idx = 0; idx < src_size; ++idx) {
+ src_die = src_name_to_die.GetValueAtIndexUnchecked(idx);
+ dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx);
+
+ if (src_die.Tag() != dst_die.Tag()) {
+ if (log)
+ log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, "
+ "but 0x%8.8x (%s) tags didn't match 0x%8.8x (%s)",
+ src_class_die.GetOffset(), dst_class_die.GetOffset(),
+ src_die.GetOffset(), src_die.GetTagAsCString(),
+ dst_die.GetOffset(), dst_die.GetTagAsCString());
+ fast_path = false;
+ }
+
+ const char *src_name = src_die.GetMangledName();
+ const char *dst_name = dst_die.GetMangledName();
+
+ // Make sure the names match
+ if (src_name == dst_name || (strcmp(src_name, dst_name) == 0))
+ continue;
+
+ if (log)
+ log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, "
+ "but 0x%8.8x (%s) names didn't match 0x%8.8x (%s)",
+ src_class_die.GetOffset(), dst_class_die.GetOffset(),
+ src_die.GetOffset(), src_name, dst_die.GetOffset(),
+ dst_name);
+
+ fast_path = false;
+ }
+ }
+
+ DWARFASTParserClang *src_dwarf_ast_parser =
+ (DWARFASTParserClang *)src_die.GetDWARFParser();
+ DWARFASTParserClang *dst_dwarf_ast_parser =
+ (DWARFASTParserClang *)dst_die.GetDWARFParser();
+
+ // Now do the work of linking the DeclContexts and Types.
+ if (fast_path) {
+ // We can do this quickly. Just run across the tables index-for-index
+ // since we know each node has matching names and tags.
+ for (idx = 0; idx < src_size; ++idx) {
+ src_die = src_name_to_die.GetValueAtIndexUnchecked(idx);
+ dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx);
+
+ clang::DeclContext *src_decl_ctx =
+ src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()];
+ if (src_decl_ctx) {
+ if (log)
+ log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_decl_ctx), src_die.GetOffset(),
+ dst_die.GetOffset());
+ dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die);
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique decl context from 0x%8.8x for "
+ "0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+
+ Type *src_child_type =
+ dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()];
+ if (src_child_type) {
+ if (log)
+ log->Printf(
+ "uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_child_type), src_child_type->GetID(),
+ src_die.GetOffset(), dst_die.GetOffset());
+ dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type;
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique lldb_private::Type from "
+ "0x%8.8x for 0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+ }
+ } else {
+ // We must do this slowly. For each member of the destination, look up a
+ // member in the source with the same name, check its tag, and unique them
+ // if everything matches up. Report failures.
+
+ if (!src_name_to_die.IsEmpty() && !dst_name_to_die.IsEmpty()) {
+ src_name_to_die.Sort();
+
+ for (idx = 0; idx < dst_size; ++idx) {
+ ConstString dst_name = dst_name_to_die.GetCStringAtIndex(idx);
+ dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx);
+ src_die = src_name_to_die.Find(dst_name, DWARFDIE());
+
+ if (src_die && (src_die.Tag() == dst_die.Tag())) {
+ clang::DeclContext *src_decl_ctx =
+ src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()];
+ if (src_decl_ctx) {
+ if (log)
+ log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_decl_ctx),
+ src_die.GetOffset(), dst_die.GetOffset());
+ dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die);
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique decl context from 0x%8.8x "
+ "for 0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+
+ Type *src_child_type =
+ dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()];
+ if (src_child_type) {
+ if (log)
+ log->Printf("uniquing type %p (uid=0x%" PRIx64
+ ") from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_child_type),
+ src_child_type->GetID(), src_die.GetOffset(),
+ dst_die.GetOffset());
+ dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] =
+ src_child_type;
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique lldb_private::Type from "
+ "0x%8.8x for 0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+ } else {
+ if (log)
+ log->Printf("warning: couldn't find a match for 0x%8.8x",
+ dst_die.GetOffset());
+
+ failures.push_back(dst_die);
+ }
+ }
+ }
+ }
+
+ const uint32_t src_size_artificial = src_name_to_die_artificial.GetSize();
+ const uint32_t dst_size_artificial = dst_name_to_die_artificial.GetSize();
+
+ if (src_size_artificial && dst_size_artificial) {
+ dst_name_to_die_artificial.Sort();
+
+ for (idx = 0; idx < src_size_artificial; ++idx) {
+ ConstString src_name_artificial =
+ src_name_to_die_artificial.GetCStringAtIndex(idx);
+ src_die = src_name_to_die_artificial.GetValueAtIndexUnchecked(idx);
+ dst_die =
+ dst_name_to_die_artificial.Find(src_name_artificial, DWARFDIE());
+
+ if (dst_die) {
+ // Both classes have the artificial types, link them
+ clang::DeclContext *src_decl_ctx =
+ src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()];
+ if (src_decl_ctx) {
+ if (log)
+ log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_decl_ctx), src_die.GetOffset(),
+ dst_die.GetOffset());
+ dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die);
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique decl context from 0x%8.8x "
+ "for 0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+
+ Type *src_child_type =
+ dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()];
+ if (src_child_type) {
+ if (log)
+ log->Printf(
+ "uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_child_type), src_child_type->GetID(),
+ src_die.GetOffset(), dst_die.GetOffset());
+ dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type;
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique lldb_private::Type from "
+ "0x%8.8x for 0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+ }
+ }
+ }
+
+ if (dst_size_artificial) {
+ for (idx = 0; idx < dst_size_artificial; ++idx) {
+ ConstString dst_name_artificial =
+ dst_name_to_die_artificial.GetCStringAtIndex(idx);
+ dst_die = dst_name_to_die_artificial.GetValueAtIndexUnchecked(idx);
+ if (log)
+ log->Printf("warning: need to create artificial method for 0x%8.8x for "
+ "method '%s'",
+ dst_die.GetOffset(), dst_name_artificial.GetCString());
+
+ failures.push_back(dst_die);
+ }
+ }
+
+ return !failures.empty();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
new file mode 100644
index 000000000000..5b5d83d65932
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
@@ -0,0 +1,151 @@
+//===-- DWARFASTParserClang.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFASTParserClang_h_
+#define SymbolFileDWARF_DWARFASTParserClang_h_
+
+#include "clang/AST/CharUnits.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include "DWARFASTParser.h"
+#include "DWARFDefines.h"
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+
+#include <vector>
+
+namespace lldb_private {
+class CompileUnit;
+}
+class DWARFDebugInfoEntry;
+class SymbolFileDWARF;
+
+class DWARFASTParserClang : public DWARFASTParser {
+public:
+ DWARFASTParserClang(lldb_private::ClangASTContext &ast);
+
+ ~DWARFASTParserClang() override;
+
+ // DWARFASTParser interface.
+ lldb::TypeSP ParseTypeFromDWARF(const lldb_private::SymbolContext &sc,
+ const DWARFDIE &die, lldb_private::Log *log,
+ bool *type_is_new_ptr) override;
+
+ lldb_private::Function *
+ ParseFunctionFromDWARF(lldb_private::CompileUnit &comp_unit,
+ const DWARFDIE &die) override;
+
+ bool
+ CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type,
+ lldb_private::CompilerType &compiler_type) override;
+
+ lldb_private::CompilerDecl
+ GetDeclForUIDFromDWARF(const DWARFDIE &die) override;
+
+ std::vector<DWARFDIE>
+ GetDIEForDeclContext(lldb_private::CompilerDeclContext decl_context) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextForUIDFromDWARF(const DWARFDIE &die) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) override;
+
+ lldb_private::ClangASTImporter &GetClangASTImporter();
+
+protected:
+ class DelayedAddObjCClassProperty;
+ typedef std::vector<DelayedAddObjCClassProperty> DelayedPropertyList;
+
+ clang::DeclContext *GetDeclContextForBlock(const DWARFDIE &die);
+
+ clang::BlockDecl *ResolveBlockDIE(const DWARFDIE &die);
+
+ clang::NamespaceDecl *ResolveNamespaceDIE(const DWARFDIE &die);
+
+ bool ParseTemplateDIE(const DWARFDIE &die,
+ lldb_private::ClangASTContext::TemplateParameterInfos
+ &template_param_infos);
+ bool ParseTemplateParameterInfos(
+ const DWARFDIE &parent_die,
+ lldb_private::ClangASTContext::TemplateParameterInfos
+ &template_param_infos);
+
+ bool ParseChildMembers(
+ const DWARFDIE &die, lldb_private::CompilerType &class_compiler_type,
+ const lldb::LanguageType class_language,
+ std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes,
+ std::vector<int> &member_accessibilities,
+ std::vector<DWARFDIE> &member_function_dies,
+ DelayedPropertyList &delayed_properties,
+ lldb::AccessType &default_accessibility, bool &is_a_class,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info);
+
+ size_t
+ ParseChildParameters(clang::DeclContext *containing_decl_ctx,
+ const DWARFDIE &parent_die, bool skip_artificial,
+ bool &is_static, bool &is_variadic,
+ bool &has_template_params,
+ std::vector<lldb_private::CompilerType> &function_args,
+ std::vector<clang::ParmVarDecl *> &function_param_decls,
+ unsigned &type_quals);
+
+ size_t ParseChildEnumerators(lldb_private::CompilerType &compiler_type,
+ bool is_signed, uint32_t enumerator_byte_size,
+ const DWARFDIE &parent_die);
+
+ lldb_private::Type *GetTypeForDIE(const DWARFDIE &die);
+
+ clang::Decl *GetClangDeclForDIE(const DWARFDIE &die);
+
+ clang::DeclContext *GetClangDeclContextForDIE(const DWARFDIE &die);
+
+ clang::DeclContext *GetClangDeclContextContainingDIE(const DWARFDIE &die,
+ DWARFDIE *decl_ctx_die);
+
+ bool CopyUniqueClassMethodTypes(const DWARFDIE &src_class_die,
+ const DWARFDIE &dst_class_die,
+ lldb_private::Type *class_type,
+ std::vector<DWARFDIE> &failures);
+
+ clang::DeclContext *GetCachedClangDeclContextForDIE(const DWARFDIE &die);
+
+ void LinkDeclContextToDIE(clang::DeclContext *decl_ctx, const DWARFDIE &die);
+
+ void LinkDeclToDIE(clang::Decl *decl, const DWARFDIE &die);
+
+ lldb::TypeSP ParseTypeFromDWO(const DWARFDIE &die, lldb_private::Log *log);
+
+ // Return true if this type is a declaration to a type in an external
+ // module.
+ lldb::ModuleSP GetModuleForType(const DWARFDIE &die);
+
+ typedef llvm::SmallPtrSet<const DWARFDebugInfoEntry *, 4> DIEPointerSet;
+ typedef llvm::DenseMap<const DWARFDebugInfoEntry *, clang::DeclContext *>
+ DIEToDeclContextMap;
+ // typedef llvm::DenseMap<const clang::DeclContext *, DIEPointerSet>
+ // DeclContextToDIEMap;
+ typedef std::multimap<const clang::DeclContext *, const DWARFDIE>
+ DeclContextToDIEMap;
+ typedef llvm::DenseMap<const DWARFDebugInfoEntry *, clang::Decl *>
+ DIEToDeclMap;
+ typedef llvm::DenseMap<const clang::Decl *, DIEPointerSet> DeclToDIEMap;
+
+ lldb_private::ClangASTContext &m_ast;
+ DIEToDeclMap m_die_to_decl;
+ DeclToDIEMap m_decl_to_die;
+ DIEToDeclContextMap m_die_to_decl_ctx;
+ DeclContextToDIEMap m_decl_ctx_to_die;
+ std::unique_ptr<lldb_private::ClangASTImporter> m_clang_ast_importer_up;
+};
+
+#endif // SymbolFileDWARF_DWARFASTParserClang_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp
new file mode 100644
index 000000000000..6128163a2926
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp
@@ -0,0 +1,89 @@
+//===-- DWARFAbbreviationDeclaration.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFAbbreviationDeclaration.h"
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/Object/Error.h"
+
+#include "DWARFFormValue.h"
+
+using namespace lldb_private;
+
+DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration()
+ : m_code(InvalidCode), m_tag(0), m_has_children(0), m_attributes() {}
+
+DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration(dw_tag_t tag,
+ uint8_t has_children)
+ : m_code(InvalidCode), m_tag(tag), m_has_children(has_children),
+ m_attributes() {}
+
+llvm::Expected<DWARFEnumState>
+DWARFAbbreviationDeclaration::extract(const DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr) {
+ m_code = data.GetULEB128(offset_ptr);
+ if (m_code == 0)
+ return DWARFEnumState::Complete;
+
+ m_attributes.clear();
+ m_tag = data.GetULEB128(offset_ptr);
+ if (m_tag == DW_TAG_null)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "abbrev decl requires non-null tag.");
+
+ m_has_children = data.GetU8(offset_ptr);
+
+ while (data.ValidOffset(*offset_ptr)) {
+ dw_attr_t attr = data.GetULEB128(offset_ptr);
+ dw_form_t form = data.GetULEB128(offset_ptr);
+
+ // This is the last attribute for this abbrev decl, but there may still be
+ // more abbrev decls, so return MoreItems to indicate to the caller that
+ // they should call this function again.
+ if (!attr && !form)
+ return DWARFEnumState::MoreItems;
+
+ if (!attr || !form)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "malformed abbreviation declaration attribute");
+
+ DWARFFormValue::ValueType val;
+
+ if (form == DW_FORM_implicit_const)
+ val.value.sval = data.GetULEB128(offset_ptr);
+
+ m_attributes.push_back(DWARFAttribute(attr, form, val));
+ }
+
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "abbreviation declaration attribute list not terminated with a null "
+ "entry");
+}
+
+bool DWARFAbbreviationDeclaration::IsValid() {
+ return m_code != 0 && m_tag != 0;
+}
+
+uint32_t
+DWARFAbbreviationDeclaration::FindAttributeIndex(dw_attr_t attr) const {
+ uint32_t i;
+ const uint32_t kNumAttributes = m_attributes.size();
+ for (i = 0; i < kNumAttributes; ++i) {
+ if (m_attributes[i].get_attr() == attr)
+ return i;
+ }
+ return DW_INVALID_INDEX;
+}
+
+bool DWARFAbbreviationDeclaration::
+operator==(const DWARFAbbreviationDeclaration &rhs) const {
+ return Tag() == rhs.Tag() && HasChildren() == rhs.HasChildren() &&
+ m_attributes == rhs.m_attributes;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h
new file mode 100644
index 000000000000..c0cf8823a368
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h
@@ -0,0 +1,65 @@
+//===-- DWARFAbbreviationDeclaration.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_DWARFAbbreviationDeclaration_h_
+#define liblldb_DWARFAbbreviationDeclaration_h_
+
+#include "DWARFAttribute.h"
+#include "DWARFDefines.h"
+#include "SymbolFileDWARF.h"
+#include "llvm/Support/Error.h"
+
+class DWARFAbbreviationDeclaration {
+public:
+ enum { InvalidCode = 0 };
+ DWARFAbbreviationDeclaration();
+
+ // For hand crafting an abbreviation declaration
+ DWARFAbbreviationDeclaration(dw_tag_t tag, uint8_t has_children);
+
+ dw_uleb128_t Code() const { return m_code; }
+ void SetCode(dw_uleb128_t code) { m_code = code; }
+ dw_tag_t Tag() const { return m_tag; }
+ bool HasChildren() const { return m_has_children; }
+ size_t NumAttributes() const { return m_attributes.size(); }
+ dw_form_t GetFormByIndex(uint32_t idx) const {
+ return m_attributes.size() > idx ? m_attributes[idx].get_form() : 0;
+ }
+
+ // idx is assumed to be valid when calling GetAttrAndFormByIndex()
+ void GetAttrAndFormValueByIndex(uint32_t idx, dw_attr_t &attr,
+ DWARFFormValue &form_value) const {
+ m_attributes[idx].get(attr, form_value.FormRef(), form_value.ValueRef());
+ }
+ dw_form_t GetFormByIndexUnchecked(uint32_t idx) const {
+ return m_attributes[idx].get_form();
+ }
+ uint32_t FindAttributeIndex(dw_attr_t attr) const;
+
+ /// Extract one abbreviation declaration and all of its associated attributes.
+ /// Possible return values:
+ /// DWARFEnumState::Complete - the extraction completed successfully. This
+ /// was the last abbrev decl in a sequence, and the user should not call
+ /// this function again.
+ /// DWARFEnumState::MoreItems - the extraction completed successfully. The
+ /// user should call this function again to retrieve the next decl.
+ /// llvm::Error - A parsing error occurred. The debug info is malformed.
+ llvm::Expected<lldb_private::DWARFEnumState>
+ extract(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr);
+ bool IsValid();
+ bool operator==(const DWARFAbbreviationDeclaration &rhs) const;
+
+protected:
+ dw_uleb128_t m_code;
+ dw_tag_t m_tag;
+ uint8_t m_has_children;
+ DWARFAttribute::collection m_attributes;
+};
+
+#endif // liblldb_DWARFAbbreviationDeclaration_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp
new file mode 100644
index 000000000000..b3594a455680
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp
@@ -0,0 +1,58 @@
+//===-- DWARFAttribute.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFAttribute.h"
+#include "DWARFUnit.h"
+#include "DWARFDebugInfo.h"
+
+DWARFAttributes::DWARFAttributes() : m_infos() {}
+
+DWARFAttributes::~DWARFAttributes() {}
+
+uint32_t DWARFAttributes::FindAttributeIndex(dw_attr_t attr) const {
+ collection::const_iterator end = m_infos.end();
+ collection::const_iterator beg = m_infos.begin();
+ collection::const_iterator pos;
+ for (pos = beg; pos != end; ++pos) {
+ if (pos->attr.get_attr() == attr)
+ return std::distance(beg, pos);
+ }
+ return UINT32_MAX;
+}
+
+void DWARFAttributes::Append(const DWARFUnit *cu, dw_offset_t attr_die_offset,
+ dw_attr_t attr, dw_form_t form) {
+ AttributeValue attr_value = {
+ cu, attr_die_offset, {attr, form, DWARFFormValue::ValueType()}};
+ m_infos.push_back(attr_value);
+}
+
+bool DWARFAttributes::ExtractFormValueAtIndex(
+ uint32_t i, DWARFFormValue &form_value) const {
+ const DWARFUnit *cu = CompileUnitAtIndex(i);
+ form_value.SetUnit(cu);
+ form_value.SetForm(FormAtIndex(i));
+ lldb::offset_t offset = DIEOffsetAtIndex(i);
+ return form_value.ExtractValue(cu->GetData(), &offset);
+}
+
+DWARFDIE
+DWARFAttributes::FormValueAsReference(dw_attr_t attr) const {
+ const uint32_t attr_idx = FindAttributeIndex(attr);
+ if (attr_idx != UINT32_MAX)
+ return FormValueAsReferenceAtIndex(attr_idx);
+ return {};
+}
+
+DWARFDIE
+DWARFAttributes::FormValueAsReferenceAtIndex(uint32_t i) const {
+ DWARFFormValue form_value;
+ if (ExtractFormValueAtIndex(i, form_value))
+ return form_value.Reference();
+ return {};
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h
new file mode 100644
index 000000000000..58427b19100a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h
@@ -0,0 +1,85 @@
+//===-- DWARFAttribute.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFAttribute_h_
+#define SymbolFileDWARF_DWARFAttribute_h_
+
+#include "DWARFDefines.h"
+#include "DWARFFormValue.h"
+#include "llvm/ADT/SmallVector.h"
+#include <vector>
+
+class DWARFUnit;
+
+class DWARFAttribute {
+public:
+ DWARFAttribute(dw_attr_t attr, dw_form_t form,
+ DWARFFormValue::ValueType value)
+ : m_attr(attr), m_form(form), m_value(value) {}
+
+ void set(dw_attr_t attr, dw_form_t form) {
+ m_attr = attr;
+ m_form = form;
+ }
+ dw_attr_t get_attr() const { return m_attr; }
+ dw_form_t get_form() const { return m_form; }
+ void get(dw_attr_t &attr, dw_form_t &form,
+ DWARFFormValue::ValueType &val) const {
+ attr = m_attr;
+ form = m_form;
+ val = m_value;
+ }
+ bool operator==(const DWARFAttribute &rhs) const {
+ return m_attr == rhs.m_attr && m_form == rhs.m_form;
+ }
+ typedef std::vector<DWARFAttribute> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+protected:
+ dw_attr_t m_attr;
+ dw_form_t m_form;
+ DWARFFormValue::ValueType m_value;
+};
+
+class DWARFAttributes {
+public:
+ DWARFAttributes();
+ ~DWARFAttributes();
+
+ void Append(const DWARFUnit *cu, dw_offset_t attr_die_offset,
+ dw_attr_t attr, dw_form_t form);
+ const DWARFUnit *CompileUnitAtIndex(uint32_t i) const {
+ return m_infos[i].cu;
+ }
+ dw_offset_t DIEOffsetAtIndex(uint32_t i) const {
+ return m_infos[i].die_offset;
+ }
+ dw_attr_t AttributeAtIndex(uint32_t i) const {
+ return m_infos[i].attr.get_attr();
+ }
+ dw_attr_t FormAtIndex(uint32_t i) const { return m_infos[i].attr.get_form(); }
+ bool ExtractFormValueAtIndex(uint32_t i, DWARFFormValue &form_value) const;
+ DWARFDIE FormValueAsReferenceAtIndex(uint32_t i) const;
+ DWARFDIE FormValueAsReference(dw_attr_t attr) const;
+ uint32_t FindAttributeIndex(dw_attr_t attr) const;
+ void Clear() { m_infos.clear(); }
+ size_t Size() const { return m_infos.size(); }
+
+protected:
+ struct AttributeValue {
+ const DWARFUnit *cu; // Keep the compile unit with each attribute in
+ // case we have DW_FORM_ref_addr values
+ dw_offset_t die_offset;
+ DWARFAttribute attr;
+ };
+ typedef llvm::SmallVector<AttributeValue, 8> collection;
+ collection m_infos;
+};
+
+#endif // SymbolFileDWARF_DWARFAttribute_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp
new file mode 100644
index 000000000000..96adb72c9532
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp
@@ -0,0 +1,149 @@
+//===-- DWARFBaseDIE.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFBaseDIE.h"
+
+#include "DWARFUnit.h"
+#include "DWARFDebugInfoEntry.h"
+#include "SymbolFileDWARF.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ObjectFile.h"
+
+using namespace lldb_private;
+
+llvm::Optional<DIERef> DWARFBaseDIE::GetDIERef() const {
+ if (!IsValid())
+ return llvm::None;
+
+ return DIERef(m_cu->GetSymbolFileDWARF().GetDwoNum(), m_cu->GetDebugSection(),
+ m_die->GetOffset());
+}
+
+dw_tag_t DWARFBaseDIE::Tag() const {
+ if (m_die)
+ return m_die->Tag();
+ else
+ return 0;
+}
+
+const char *DWARFBaseDIE::GetTagAsCString() const {
+ return lldb_private::DW_TAG_value_to_name(Tag());
+}
+
+const char *DWARFBaseDIE::GetAttributeValueAsString(const dw_attr_t attr,
+ const char *fail_value) const {
+ if (IsValid())
+ return m_die->GetAttributeValueAsString(GetCU(), attr, fail_value);
+ else
+ return fail_value;
+}
+
+uint64_t DWARFBaseDIE::GetAttributeValueAsUnsigned(const dw_attr_t attr,
+ uint64_t fail_value) const {
+ if (IsValid())
+ return m_die->GetAttributeValueAsUnsigned(GetCU(), attr, fail_value);
+ else
+ return fail_value;
+}
+
+uint64_t DWARFBaseDIE::GetAttributeValueAsAddress(const dw_attr_t attr,
+ uint64_t fail_value) const {
+ if (IsValid())
+ return m_die->GetAttributeValueAsAddress(GetCU(), attr, fail_value);
+ else
+ return fail_value;
+}
+
+lldb::user_id_t DWARFBaseDIE::GetID() const {
+ if (IsValid())
+ return GetDWARF()->GetUID(*this);
+ return LLDB_INVALID_UID;
+}
+
+const char *DWARFBaseDIE::GetName() const {
+ if (IsValid())
+ return m_die->GetName(m_cu);
+ else
+ return nullptr;
+}
+
+lldb::LanguageType DWARFBaseDIE::GetLanguage() const {
+ if (IsValid())
+ return m_cu->GetLanguageType();
+ else
+ return lldb::eLanguageTypeUnknown;
+}
+
+lldb::ModuleSP DWARFBaseDIE::GetModule() const {
+ SymbolFileDWARF *dwarf = GetDWARF();
+ if (dwarf)
+ return dwarf->GetObjectFile()->GetModule();
+ else
+ return lldb::ModuleSP();
+}
+
+dw_offset_t DWARFBaseDIE::GetOffset() const {
+ if (IsValid())
+ return m_die->GetOffset();
+ else
+ return DW_INVALID_OFFSET;
+}
+
+SymbolFileDWARF *DWARFBaseDIE::GetDWARF() const {
+ if (m_cu)
+ return &m_cu->GetSymbolFileDWARF();
+ else
+ return nullptr;
+}
+
+lldb_private::TypeSystem *DWARFBaseDIE::GetTypeSystem() const {
+ if (m_cu)
+ return m_cu->GetTypeSystem();
+ else
+ return nullptr;
+}
+
+DWARFASTParser *DWARFBaseDIE::GetDWARFParser() const {
+ lldb_private::TypeSystem *type_system = GetTypeSystem();
+ if (type_system)
+ return type_system->GetDWARFParser();
+ else
+ return nullptr;
+}
+
+bool DWARFBaseDIE::HasChildren() const {
+ return m_die && m_die->HasChildren();
+}
+
+bool DWARFBaseDIE::Supports_DW_AT_APPLE_objc_complete_type() const {
+ return IsValid() && GetDWARF()->Supports_DW_AT_APPLE_objc_complete_type(m_cu);
+}
+
+size_t DWARFBaseDIE::GetAttributes(DWARFAttributes &attributes,
+ uint32_t depth) const {
+ if (IsValid())
+ return m_die->GetAttributes(m_cu, attributes, depth);
+ if (depth == 0)
+ attributes.Clear();
+ return 0;
+}
+
+bool operator==(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs) {
+ return lhs.GetDIE() == rhs.GetDIE() && lhs.GetCU() == rhs.GetCU();
+}
+
+bool operator!=(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs) {
+ return !(lhs == rhs);
+}
+
+const DWARFDataExtractor &DWARFBaseDIE::GetData() const {
+ // Clients must check if this DIE is valid before calling this function.
+ assert(IsValid());
+ return m_cu->GetData();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h
new file mode 100644
index 000000000000..0058043017cd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h
@@ -0,0 +1,127 @@
+//===-- DWARFBaseDIE.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFBaseDIE_h_
+#define SymbolFileDWARF_DWARFBaseDIE_h_
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/lldb-types.h"
+
+class DIERef;
+class DWARFASTParser;
+class DWARFAttributes;
+class DWARFUnit;
+class DWARFDebugInfoEntry;
+class DWARFDeclContext;
+class SymbolFileDWARF;
+
+class DWARFBaseDIE {
+public:
+ DWARFBaseDIE() : m_cu(nullptr), m_die(nullptr) {}
+
+ DWARFBaseDIE(DWARFUnit *cu, DWARFDebugInfoEntry *die)
+ : m_cu(cu), m_die(die) {}
+
+ DWARFBaseDIE(const DWARFUnit *cu, DWARFDebugInfoEntry *die)
+ : m_cu(const_cast<DWARFUnit *>(cu)), m_die(die) {}
+
+ DWARFBaseDIE(DWARFUnit *cu, const DWARFDebugInfoEntry *die)
+ : m_cu(cu), m_die(const_cast<DWARFDebugInfoEntry *>(die)) {}
+
+ DWARFBaseDIE(const DWARFUnit *cu, const DWARFDebugInfoEntry *die)
+ : m_cu(const_cast<DWARFUnit *>(cu)),
+ m_die(const_cast<DWARFDebugInfoEntry *>(die)) {}
+
+ // Tests
+ explicit operator bool() const { return IsValid(); }
+
+ bool IsValid() const { return m_cu && m_die; }
+
+ bool HasChildren() const;
+
+ bool Supports_DW_AT_APPLE_objc_complete_type() const;
+
+ // Accessors
+ SymbolFileDWARF *GetDWARF() const;
+
+ DWARFUnit *GetCU() const { return m_cu; }
+
+ DWARFDebugInfoEntry *GetDIE() const { return m_die; }
+
+ llvm::Optional<DIERef> GetDIERef() const;
+
+ lldb_private::TypeSystem *GetTypeSystem() const;
+
+ DWARFASTParser *GetDWARFParser() const;
+
+ void Set(DWARFUnit *cu, DWARFDebugInfoEntry *die) {
+ if (cu && die) {
+ m_cu = cu;
+ m_die = die;
+ } else {
+ Clear();
+ }
+ }
+
+ void Clear() {
+ m_cu = nullptr;
+ m_die = nullptr;
+ }
+
+ // Get the data that contains the attribute values for this DIE. Support
+ // for .debug_types means that any DIE can have its data either in the
+ // .debug_info or the .debug_types section; this method will return the
+ // correct section data.
+ //
+ // Clients must validate that this object is valid before calling this.
+ const lldb_private::DWARFDataExtractor &GetData() const;
+
+ // Accessing information about a DIE
+ dw_tag_t Tag() const;
+
+ const char *GetTagAsCString() const;
+
+ dw_offset_t GetOffset() const;
+
+ // Get the LLDB user ID for this DIE. This is often just the DIE offset,
+ // but it might have a SymbolFileDWARF::GetID() in the high 32 bits if
+ // we are doing Darwin DWARF in .o file, or DWARF stand alone debug
+ // info.
+ lldb::user_id_t GetID() const;
+
+ const char *GetName() const;
+
+ lldb::LanguageType GetLanguage() const;
+
+ lldb::ModuleSP GetModule() const;
+
+ // Getting attribute values from the DIE.
+ //
+ // GetAttributeValueAsXXX() functions should only be used if you are
+ // looking for one or two attributes on a DIE. If you are trying to
+ // parse all attributes, use GetAttributes (...) instead
+ const char *GetAttributeValueAsString(const dw_attr_t attr,
+ const char *fail_value) const;
+
+ uint64_t GetAttributeValueAsUnsigned(const dw_attr_t attr,
+ uint64_t fail_value) const;
+
+ uint64_t GetAttributeValueAsAddress(const dw_attr_t attr,
+ uint64_t fail_value) const;
+
+ size_t GetAttributes(DWARFAttributes &attributes, uint32_t depth = 0) const;
+
+protected:
+ DWARFUnit *m_cu;
+ DWARFDebugInfoEntry *m_die;
+};
+
+bool operator==(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs);
+bool operator!=(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs);
+
+#endif // SymbolFileDWARF_DWARFBaseDIE_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp
new file mode 100644
index 000000000000..718f0537d6ed
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp
@@ -0,0 +1,115 @@
+//===-- DWARFCompileUnit.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFCompileUnit.h"
+#include "DWARFDebugAranges.h"
+#include "SymbolFileDWARFDebugMap.h"
+
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void DWARFCompileUnit::Dump(Stream *s) const {
+ s->Printf("0x%8.8x: Compile Unit: length = 0x%8.8x, version = 0x%4.4x, "
+ "abbr_offset = 0x%8.8x, addr_size = 0x%2.2x (next CU at "
+ "{0x%8.8x})\n",
+ GetOffset(), GetLength(), GetVersion(), GetAbbrevOffset(),
+ GetAddressByteSize(), GetNextUnitOffset());
+}
+
+void DWARFCompileUnit::BuildAddressRangeTable(
+ DWARFDebugAranges *debug_aranges) {
+ // This function is usually called if there in no .debug_aranges section in
+ // order to produce a compile unit level set of address ranges that is
+ // accurate.
+
+ size_t num_debug_aranges = debug_aranges->GetNumRanges();
+
+ // First get the compile unit DIE only and check if it has a DW_AT_ranges
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+
+ const dw_offset_t cu_offset = GetOffset();
+ if (die) {
+ DWARFRangeList ranges;
+ const size_t num_ranges =
+ die->GetAttributeAddressRanges(this, ranges, false);
+ if (num_ranges > 0) {
+ // This compile unit has DW_AT_ranges, assume this is correct if it is
+ // present since clang no longer makes .debug_aranges by default and it
+ // emits DW_AT_ranges for DW_TAG_compile_units. GCC also does this with
+ // recent GCC builds.
+ for (size_t i = 0; i < num_ranges; ++i) {
+ const DWARFRangeList::Entry &range = ranges.GetEntryRef(i);
+ debug_aranges->AppendRange(cu_offset, range.GetRangeBase(),
+ range.GetRangeEnd());
+ }
+
+ return; // We got all of our ranges from the DW_AT_ranges attribute
+ }
+ }
+ // We don't have a DW_AT_ranges attribute, so we need to parse the DWARF
+
+ // If the DIEs weren't parsed, then we don't want all dies for all compile
+ // units to stay loaded when they weren't needed. So we can end up parsing
+ // the DWARF and then throwing them all away to keep memory usage down.
+ ScopedExtractDIEs clear_dies(ExtractDIEsScoped());
+
+ die = DIEPtr();
+ if (die)
+ die->BuildAddressRangeTable(this, debug_aranges);
+
+ if (debug_aranges->GetNumRanges() == num_debug_aranges) {
+ // We got nothing from the functions, maybe we have a line tables only
+ // situation. Check the line tables and build the arange table from this.
+ SymbolContext sc;
+ sc.comp_unit = m_dwarf.GetCompUnitForDWARFCompUnit(*this);
+ if (sc.comp_unit) {
+ SymbolFileDWARFDebugMap *debug_map_sym_file =
+ m_dwarf.GetDebugMapSymfile();
+ if (debug_map_sym_file == nullptr) {
+ if (LineTable *line_table = sc.comp_unit->GetLineTable()) {
+ LineTable::FileAddressRanges file_ranges;
+ const bool append = true;
+ const size_t num_ranges =
+ line_table->GetContiguousFileAddressRanges(file_ranges, append);
+ for (uint32_t idx = 0; idx < num_ranges; ++idx) {
+ const LineTable::FileAddressRanges::Entry &range =
+ file_ranges.GetEntryRef(idx);
+ debug_aranges->AppendRange(cu_offset, range.GetRangeBase(),
+ range.GetRangeEnd());
+ }
+ }
+ } else
+ debug_map_sym_file->AddOSOARanges(&m_dwarf, debug_aranges);
+ }
+ }
+
+ if (debug_aranges->GetNumRanges() == num_debug_aranges) {
+ // We got nothing from the functions, maybe we have a line tables only
+ // situation. Check the line tables and build the arange table from this.
+ SymbolContext sc;
+ sc.comp_unit = m_dwarf.GetCompUnitForDWARFCompUnit(*this);
+ if (sc.comp_unit) {
+ if (LineTable *line_table = sc.comp_unit->GetLineTable()) {
+ LineTable::FileAddressRanges file_ranges;
+ const bool append = true;
+ const size_t num_ranges =
+ line_table->GetContiguousFileAddressRanges(file_ranges, append);
+ for (uint32_t idx = 0; idx < num_ranges; ++idx) {
+ const LineTable::FileAddressRanges::Entry &range =
+ file_ranges.GetEntryRef(idx);
+ debug_aranges->AppendRange(GetOffset(), range.GetRangeBase(),
+ range.GetRangeEnd());
+ }
+ }
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h
new file mode 100644
index 000000000000..75647dbb082f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h
@@ -0,0 +1,35 @@
+//===-- DWARFCompileUnit.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFCompileUnit_h_
+#define SymbolFileDWARF_DWARFCompileUnit_h_
+
+#include "DWARFUnit.h"
+#include "llvm/Support/Error.h"
+
+class DWARFCompileUnit : public DWARFUnit {
+public:
+ void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) override;
+
+ void Dump(lldb_private::Stream *s) const override;
+
+ static bool classof(const DWARFUnit *unit) { return !unit->IsTypeUnit(); }
+
+private:
+ DWARFCompileUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid,
+ const DWARFUnitHeader &header,
+ const DWARFAbbreviationDeclarationSet &abbrevs,
+ DIERef::Section section)
+ : DWARFUnit(dwarf, uid, header, abbrevs, section) {}
+
+ DISALLOW_COPY_AND_ASSIGN(DWARFCompileUnit);
+
+ friend class DWARFUnit;
+};
+
+#endif // SymbolFileDWARF_DWARFCompileUnit_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp
new file mode 100644
index 000000000000..eb307ce1cce1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp
@@ -0,0 +1,138 @@
+//===-- DWARFContext.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFContext.h"
+
+#include "lldb/Core/Section.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static DWARFDataExtractor LoadSection(SectionList *section_list,
+ SectionType section_type) {
+ if (!section_list)
+ return DWARFDataExtractor();
+
+ auto section_sp = section_list->FindSectionByType(section_type, true);
+ if (!section_sp)
+ return DWARFDataExtractor();
+
+ DWARFDataExtractor data;
+ section_sp->GetSectionData(data);
+ return data;
+}
+
+const DWARFDataExtractor &
+DWARFContext::LoadOrGetSection(SectionType main_section_type,
+ llvm::Optional<SectionType> dwo_section_type,
+ SectionData &data) {
+ llvm::call_once(data.flag, [&] {
+ if (dwo_section_type && isDwo())
+ data.data = LoadSection(m_dwo_section_list, *dwo_section_type);
+ else
+ data.data = LoadSection(m_main_section_list, main_section_type);
+ });
+ return data.data;
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadAbbrevData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugAbbrev,
+ eSectionTypeDWARFDebugAbbrevDwo, m_data_debug_abbrev);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadArangesData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugAranges, llvm::None,
+ m_data_debug_aranges);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadAddrData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugAddr, llvm::None,
+ m_data_debug_addr);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadDebugInfoData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugInfo,
+ eSectionTypeDWARFDebugInfoDwo, m_data_debug_info);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadLineData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugLine, llvm::None,
+ m_data_debug_line);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadLineStrData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugLineStr, llvm::None,
+ m_data_debug_line_str);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadMacroData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugMacro, llvm::None,
+ m_data_debug_macro);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadRangesData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugRanges, llvm::None,
+ m_data_debug_ranges);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadRngListsData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugRngLists, llvm::None,
+ m_data_debug_rnglists);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadStrData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugStr,
+ eSectionTypeDWARFDebugStrDwo, m_data_debug_str);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadStrOffsetsData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugStrOffsets,
+ eSectionTypeDWARFDebugStrOffsetsDwo,
+ m_data_debug_str_offsets);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadDebugTypesData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugTypes,
+ eSectionTypeDWARFDebugTypesDwo, m_data_debug_types);
+}
+
+llvm::DWARFContext &DWARFContext::GetAsLLVM() {
+ if (!m_llvm_context) {
+ llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> section_map;
+ uint8_t addr_size = 0;
+
+ auto AddSection = [&](Section &section) {
+ DataExtractor section_data;
+ section.GetSectionData(section_data);
+
+ // Set the address size the first time we see it.
+ if (addr_size == 0)
+ addr_size = section_data.GetByteSize();
+
+ llvm::StringRef data = llvm::toStringRef(section_data.GetData());
+ llvm::StringRef name = section.GetName().GetStringRef();
+ if (name.startswith("."))
+ name = name.drop_front();
+ section_map.try_emplace(
+ name, llvm::MemoryBuffer::getMemBuffer(data, name, false));
+ };
+
+ if (m_main_section_list) {
+ for (auto &section : *m_main_section_list)
+ AddSection(*section);
+ }
+
+ if (m_dwo_section_list) {
+ for (auto &section : *m_dwo_section_list)
+ AddSection(*section);
+ }
+
+ m_llvm_context = llvm::DWARFContext::create(section_map, addr_size);
+ }
+ return *m_llvm_context;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h
new file mode 100644
index 000000000000..add042384039
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h
@@ -0,0 +1,74 @@
+//===-- DWARFContext.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILE_DWARF_DWARFCONTEXT_H
+#define LLDB_PLUGINS_SYMBOLFILE_DWARF_DWARFCONTEXT_H
+
+#include "DWARFDataExtractor.h"
+#include "lldb/Core/Section.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Support/Threading.h"
+#include <memory>
+
+namespace lldb_private {
+class DWARFContext {
+private:
+ SectionList *m_main_section_list;
+ SectionList *m_dwo_section_list;
+ mutable std::unique_ptr<llvm::DWARFContext> m_llvm_context;
+
+ struct SectionData {
+ llvm::once_flag flag;
+ DWARFDataExtractor data;
+ };
+
+ SectionData m_data_debug_abbrev;
+ SectionData m_data_debug_addr;
+ SectionData m_data_debug_aranges;
+ SectionData m_data_debug_info;
+ SectionData m_data_debug_line;
+ SectionData m_data_debug_line_str;
+ SectionData m_data_debug_macro;
+ SectionData m_data_debug_ranges;
+ SectionData m_data_debug_rnglists;
+ SectionData m_data_debug_str;
+ SectionData m_data_debug_str_offsets;
+ SectionData m_data_debug_types;
+
+ bool isDwo() { return m_dwo_section_list != nullptr; }
+
+ const DWARFDataExtractor &
+ LoadOrGetSection(lldb::SectionType main_section_type,
+ llvm::Optional<lldb::SectionType> dwo_section_type,
+ SectionData &data);
+
+public:
+ explicit DWARFContext(SectionList *main_section_list,
+ SectionList *dwo_section_list)
+ : m_main_section_list(main_section_list),
+ m_dwo_section_list(dwo_section_list) {}
+
+ const DWARFDataExtractor &getOrLoadAbbrevData();
+ const DWARFDataExtractor &getOrLoadAddrData();
+ const DWARFDataExtractor &getOrLoadArangesData();
+ const DWARFDataExtractor &getOrLoadDebugInfoData();
+ const DWARFDataExtractor &getOrLoadLineData();
+ const DWARFDataExtractor &getOrLoadLineStrData();
+ const DWARFDataExtractor &getOrLoadMacroData();
+ const DWARFDataExtractor &getOrLoadRangesData();
+ const DWARFDataExtractor &getOrLoadRngListsData();
+ const DWARFDataExtractor &getOrLoadStrData();
+ const DWARFDataExtractor &getOrLoadStrOffsetsData();
+ const DWARFDataExtractor &getOrLoadDebugTypesData();
+
+ llvm::DWARFContext &GetAsLLVM();
+};
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
new file mode 100644
index 000000000000..9d97ca15a252
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
@@ -0,0 +1,483 @@
+//===-- DWARFDIE.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFDIE.h"
+
+#include "DWARFASTParser.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFDebugInfoEntry.h"
+#include "DWARFDeclContext.h"
+#include "DWARFUnit.h"
+
+using namespace lldb_private;
+
+namespace {
+
+/// Iterate through all DIEs elaborating (i.e. reachable by a chain of
+/// DW_AT_specification and DW_AT_abstract_origin attributes) a given DIE. For
+/// convenience, the starting die is included in the sequence as the first
+/// item.
+class ElaboratingDIEIterator
+ : public std::iterator<std::input_iterator_tag, DWARFDIE> {
+
+ // The operating invariant is: top of m_worklist contains the "current" item
+ // and the rest of the list are items yet to be visited. An empty worklist
+ // means we've reached the end.
+ // Infinite recursion is prevented by maintaining a list of seen DIEs.
+ // Container sizes are optimized for the case of following DW_AT_specification
+ // and DW_AT_abstract_origin just once.
+ llvm::SmallVector<DWARFDIE, 2> m_worklist;
+ llvm::SmallSet<lldb::user_id_t, 3> m_seen;
+
+ void Next() {
+ assert(!m_worklist.empty() && "Incrementing end iterator?");
+
+ // Pop the current item from the list.
+ DWARFDIE die = m_worklist.back();
+ m_worklist.pop_back();
+
+ // And add back any items that elaborate it.
+ for (dw_attr_t attr : {DW_AT_specification, DW_AT_abstract_origin}) {
+ if (DWARFDIE d = die.GetReferencedDIE(attr))
+ if (m_seen.insert(die.GetID()).second)
+ m_worklist.push_back(d);
+ }
+ }
+
+public:
+ /// An iterator starting at die d.
+ explicit ElaboratingDIEIterator(DWARFDIE d) : m_worklist(1, d) {}
+
+ /// End marker
+ ElaboratingDIEIterator() {}
+
+ const DWARFDIE &operator*() const { return m_worklist.back(); }
+ ElaboratingDIEIterator &operator++() {
+ Next();
+ return *this;
+ }
+ ElaboratingDIEIterator operator++(int) {
+ ElaboratingDIEIterator I = *this;
+ Next();
+ return I;
+ }
+
+ friend bool operator==(const ElaboratingDIEIterator &a,
+ const ElaboratingDIEIterator &b) {
+ if (a.m_worklist.empty() || b.m_worklist.empty())
+ return a.m_worklist.empty() == b.m_worklist.empty();
+ return a.m_worklist.back() == b.m_worklist.back();
+ }
+ friend bool operator!=(const ElaboratingDIEIterator &a,
+ const ElaboratingDIEIterator &b) {
+ return !(a == b);
+ }
+};
+
+llvm::iterator_range<ElaboratingDIEIterator>
+elaborating_dies(const DWARFDIE &die) {
+ return llvm::make_range(ElaboratingDIEIterator(die),
+ ElaboratingDIEIterator());
+}
+} // namespace
+
+DWARFDIE
+DWARFDIE::GetParent() const {
+ if (IsValid())
+ return DWARFDIE(m_cu, m_die->GetParent());
+ else
+ return DWARFDIE();
+}
+
+DWARFDIE
+DWARFDIE::GetFirstChild() const {
+ if (IsValid())
+ return DWARFDIE(m_cu, m_die->GetFirstChild());
+ else
+ return DWARFDIE();
+}
+
+DWARFDIE
+DWARFDIE::GetSibling() const {
+ if (IsValid())
+ return DWARFDIE(m_cu, m_die->GetSibling());
+ else
+ return DWARFDIE();
+}
+
+DWARFDIE
+DWARFDIE::GetReferencedDIE(const dw_attr_t attr) const {
+ if (IsValid())
+ return m_die->GetAttributeValueAsReference(GetCU(), attr);
+ else
+ return {};
+}
+
+DWARFDIE
+DWARFDIE::GetDIE(dw_offset_t die_offset) const {
+ if (IsValid())
+ return m_cu->GetDIE(die_offset);
+ else
+ return DWARFDIE();
+}
+
+DWARFDIE
+DWARFDIE::GetAttributeValueAsReferenceDIE(const dw_attr_t attr) const {
+ if (IsValid()) {
+ DWARFUnit *cu = GetCU();
+ const bool check_specification_or_abstract_origin = true;
+ DWARFFormValue form_value;
+ if (m_die->GetAttributeValue(cu, attr, form_value, nullptr,
+ check_specification_or_abstract_origin))
+ return form_value.Reference();
+ }
+ return DWARFDIE();
+}
+
+DWARFDIE
+DWARFDIE::LookupDeepestBlock(lldb::addr_t file_addr) const {
+ if (IsValid()) {
+ SymbolFileDWARF *dwarf = GetDWARF();
+ DWARFUnit *cu = GetCU();
+ DWARFDebugInfoEntry *function_die = nullptr;
+ DWARFDebugInfoEntry *block_die = nullptr;
+ if (m_die->LookupAddress(file_addr, cu, &function_die, &block_die)) {
+ if (block_die && block_die != function_die) {
+ if (cu->ContainsDIEOffset(block_die->GetOffset()))
+ return DWARFDIE(cu, block_die);
+ else
+ return DWARFDIE(dwarf->DebugInfo()->GetUnit(DIERef(
+ cu->GetSymbolFileDWARF().GetDwoNum(),
+ cu->GetDebugSection(), block_die->GetOffset())),
+ block_die);
+ }
+ }
+ }
+ return DWARFDIE();
+}
+
+const char *DWARFDIE::GetMangledName() const {
+ if (IsValid())
+ return m_die->GetMangledName(m_cu);
+ else
+ return nullptr;
+}
+
+const char *DWARFDIE::GetPubname() const {
+ if (IsValid())
+ return m_die->GetPubname(m_cu);
+ else
+ return nullptr;
+}
+
+const char *DWARFDIE::GetQualifiedName(std::string &storage) const {
+ if (IsValid())
+ return m_die->GetQualifiedName(m_cu, storage);
+ else
+ return nullptr;
+}
+
+// GetName
+//
+// Get value of the DW_AT_name attribute and place that value into the supplied
+// stream object. If the DIE is a NULL object "NULL" is placed into the stream,
+// and if no DW_AT_name attribute exists for the DIE then nothing is printed.
+void DWARFDIE::GetName(Stream &s) const {
+ if (!IsValid())
+ return;
+ if (GetDIE()->IsNULL()) {
+ s.PutCString("NULL");
+ return;
+ }
+ const char *name = GetDIE()->GetAttributeValueAsString(GetCU(), DW_AT_name, nullptr, true);
+ if (!name)
+ return;
+ s.PutCString(name);
+}
+
+// AppendTypeName
+//
+// Follows the type name definition down through all needed tags to end up with
+// a fully qualified type name and dump the results to the supplied stream.
+// This is used to show the name of types given a type identifier.
+void DWARFDIE::AppendTypeName(Stream &s) const {
+ if (!IsValid())
+ return;
+ if (GetDIE()->IsNULL()) {
+ s.PutCString("NULL");
+ return;
+ }
+ if (const char *name = GetPubname()) {
+ s.PutCString(name);
+ return;
+ }
+ switch (Tag()) {
+ case DW_TAG_array_type:
+ break; // print out a "[]" after printing the full type of the element
+ // below
+ case DW_TAG_base_type:
+ s.PutCString("base ");
+ break;
+ case DW_TAG_class_type:
+ s.PutCString("class ");
+ break;
+ case DW_TAG_const_type:
+ s.PutCString("const ");
+ break;
+ case DW_TAG_enumeration_type:
+ s.PutCString("enum ");
+ break;
+ case DW_TAG_file_type:
+ s.PutCString("file ");
+ break;
+ case DW_TAG_interface_type:
+ s.PutCString("interface ");
+ break;
+ case DW_TAG_packed_type:
+ s.PutCString("packed ");
+ break;
+ case DW_TAG_pointer_type:
+ break; // print out a '*' after printing the full type below
+ case DW_TAG_ptr_to_member_type:
+ break; // print out a '*' after printing the full type below
+ case DW_TAG_reference_type:
+ break; // print out a '&' after printing the full type below
+ case DW_TAG_restrict_type:
+ s.PutCString("restrict ");
+ break;
+ case DW_TAG_set_type:
+ s.PutCString("set ");
+ break;
+ case DW_TAG_shared_type:
+ s.PutCString("shared ");
+ break;
+ case DW_TAG_string_type:
+ s.PutCString("string ");
+ break;
+ case DW_TAG_structure_type:
+ s.PutCString("struct ");
+ break;
+ case DW_TAG_subrange_type:
+ s.PutCString("subrange ");
+ break;
+ case DW_TAG_subroutine_type:
+ s.PutCString("function ");
+ break;
+ case DW_TAG_thrown_type:
+ s.PutCString("thrown ");
+ break;
+ case DW_TAG_union_type:
+ s.PutCString("union ");
+ break;
+ case DW_TAG_unspecified_type:
+ s.PutCString("unspecified ");
+ break;
+ case DW_TAG_volatile_type:
+ s.PutCString("volatile ");
+ break;
+ default:
+ return;
+ }
+
+ // Follow the DW_AT_type if possible
+ if (DWARFDIE next_die = GetAttributeValueAsReferenceDIE(DW_AT_type))
+ next_die.AppendTypeName(s);
+
+ switch (Tag()) {
+ case DW_TAG_array_type:
+ s.PutCString("[]");
+ break;
+ case DW_TAG_pointer_type:
+ s.PutChar('*');
+ break;
+ case DW_TAG_ptr_to_member_type:
+ s.PutChar('*');
+ break;
+ case DW_TAG_reference_type:
+ s.PutChar('&');
+ break;
+ default:
+ break;
+ }
+}
+
+lldb_private::Type *DWARFDIE::ResolveType() const {
+ if (IsValid())
+ return GetDWARF()->ResolveType(*this, true);
+ else
+ return nullptr;
+}
+
+lldb_private::Type *DWARFDIE::ResolveTypeUID(const DWARFDIE &die) const {
+ if (SymbolFileDWARF *dwarf = GetDWARF())
+ return dwarf->ResolveTypeUID(die, true);
+ return nullptr;
+}
+
+std::vector<DWARFDIE> DWARFDIE::GetDeclContextDIEs() const {
+ if (!IsValid())
+ return {};
+
+ std::vector<DWARFDIE> result;
+ DWARFDIE parent = GetParentDeclContextDIE();
+ while (parent.IsValid() && parent.GetDIE() != GetDIE()) {
+ result.push_back(std::move(parent));
+ parent = parent.GetParentDeclContextDIE();
+ }
+
+ return result;
+}
+
+void DWARFDIE::GetDWARFDeclContext(DWARFDeclContext &dwarf_decl_ctx) const {
+ if (IsValid()) {
+ dwarf_decl_ctx.SetLanguage(GetLanguage());
+ m_die->GetDWARFDeclContext(GetCU(), dwarf_decl_ctx);
+ } else {
+ dwarf_decl_ctx.Clear();
+ }
+}
+
+void DWARFDIE::GetDeclContext(std::vector<CompilerContext> &context) const {
+ const dw_tag_t tag = Tag();
+ if (tag == DW_TAG_compile_unit || tag == DW_TAG_partial_unit)
+ return;
+ DWARFDIE parent = GetParent();
+ if (parent)
+ parent.GetDeclContext(context);
+ switch (tag) {
+ case DW_TAG_module:
+ context.push_back(
+ CompilerContext(CompilerContextKind::Module, ConstString(GetName())));
+ break;
+ case DW_TAG_namespace:
+ context.push_back(CompilerContext(CompilerContextKind::Namespace,
+ ConstString(GetName())));
+ break;
+ case DW_TAG_structure_type:
+ context.push_back(CompilerContext(CompilerContextKind::Structure,
+ ConstString(GetName())));
+ break;
+ case DW_TAG_union_type:
+ context.push_back(
+ CompilerContext(CompilerContextKind::Union, ConstString(GetName())));
+ break;
+ case DW_TAG_class_type:
+ context.push_back(
+ CompilerContext(CompilerContextKind::Class, ConstString(GetName())));
+ break;
+ case DW_TAG_enumeration_type:
+ context.push_back(CompilerContext(CompilerContextKind::Enumeration,
+ ConstString(GetName())));
+ break;
+ case DW_TAG_subprogram:
+ context.push_back(CompilerContext(CompilerContextKind::Function,
+ ConstString(GetPubname())));
+ break;
+ case DW_TAG_variable:
+ context.push_back(CompilerContext(CompilerContextKind::Variable,
+ ConstString(GetPubname())));
+ break;
+ case DW_TAG_typedef:
+ context.push_back(
+ CompilerContext(CompilerContextKind::Typedef, ConstString(GetName())));
+ break;
+ default:
+ break;
+ }
+}
+
+DWARFDIE
+DWARFDIE::GetParentDeclContextDIE() const {
+ if (IsValid())
+ return m_die->GetParentDeclContextDIE(m_cu);
+ else
+ return DWARFDIE();
+}
+
+bool DWARFDIE::IsStructUnionOrClass() const {
+ const dw_tag_t tag = Tag();
+ return tag == DW_TAG_class_type || tag == DW_TAG_structure_type ||
+ tag == DW_TAG_union_type;
+}
+
+bool DWARFDIE::IsMethod() const {
+ for (DWARFDIE d : elaborating_dies(*this))
+ if (d.GetParent().IsStructUnionOrClass())
+ return true;
+ return false;
+}
+
+DWARFDIE
+DWARFDIE::GetContainingDWOModuleDIE() const {
+ if (IsValid()) {
+ DWARFDIE top_module_die;
+ // Now make sure this DIE is scoped in a DW_TAG_module tag and return true
+ // if so
+ for (DWARFDIE parent = GetParent(); parent.IsValid();
+ parent = parent.GetParent()) {
+ const dw_tag_t tag = parent.Tag();
+ if (tag == DW_TAG_module)
+ top_module_die = parent;
+ else if (tag == DW_TAG_compile_unit || tag == DW_TAG_partial_unit)
+ break;
+ }
+
+ return top_module_die;
+ }
+ return DWARFDIE();
+}
+
+lldb::ModuleSP DWARFDIE::GetContainingDWOModule() const {
+ if (IsValid()) {
+ DWARFDIE dwo_module_die = GetContainingDWOModuleDIE();
+
+ if (dwo_module_die) {
+ const char *module_name = dwo_module_die.GetName();
+ if (module_name)
+ return GetDWARF()->GetDWOModule(lldb_private::ConstString(module_name));
+ }
+ }
+ return lldb::ModuleSP();
+}
+
+bool DWARFDIE::GetDIENamesAndRanges(
+ const char *&name, const char *&mangled, DWARFRangeList &ranges,
+ int &decl_file, int &decl_line, int &decl_column, int &call_file,
+ int &call_line, int &call_column,
+ lldb_private::DWARFExpression *frame_base) const {
+ if (IsValid()) {
+ return m_die->GetDIENamesAndRanges(
+ GetCU(), name, mangled, ranges, decl_file, decl_line, decl_column,
+ call_file, call_line, call_column, frame_base);
+ } else
+ return false;
+}
+
+CompilerDecl DWARFDIE::GetDecl() const {
+ DWARFASTParser *dwarf_ast = GetDWARFParser();
+ if (dwarf_ast)
+ return dwarf_ast->GetDeclForUIDFromDWARF(*this);
+ else
+ return CompilerDecl();
+}
+
+CompilerDeclContext DWARFDIE::GetDeclContext() const {
+ DWARFASTParser *dwarf_ast = GetDWARFParser();
+ if (dwarf_ast)
+ return dwarf_ast->GetDeclContextForUIDFromDWARF(*this);
+ else
+ return CompilerDeclContext();
+}
+
+CompilerDeclContext DWARFDIE::GetContainingDeclContext() const {
+ DWARFASTParser *dwarf_ast = GetDWARFParser();
+ if (dwarf_ast)
+ return dwarf_ast->GetDeclContextContainingUIDFromDWARF(*this);
+ else
+ return CompilerDeclContext();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h
new file mode 100644
index 000000000000..7753ec9008cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h
@@ -0,0 +1,107 @@
+//===-- DWARFDIE.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFDIE_h_
+#define SymbolFileDWARF_DWARFDIE_h_
+
+#include "DWARFBaseDIE.h"
+#include "llvm/ADT/SmallSet.h"
+
+class DWARFDIE : public DWARFBaseDIE {
+public:
+ using DWARFBaseDIE::DWARFBaseDIE;
+
+ // Tests
+ bool IsStructUnionOrClass() const;
+
+ bool IsMethod() const;
+
+ // Accessors
+ lldb::ModuleSP GetContainingDWOModule() const;
+
+ DWARFDIE
+ GetContainingDWOModuleDIE() const;
+
+ // Accessing information about a DIE
+ const char *GetMangledName() const;
+
+ const char *GetPubname() const;
+
+ const char *GetQualifiedName(std::string &storage) const;
+
+ using DWARFBaseDIE::GetName;
+ void GetName(lldb_private::Stream &s) const;
+
+ void AppendTypeName(lldb_private::Stream &s) const;
+
+ lldb_private::Type *ResolveType() const;
+
+ // Resolve a type by UID using this DIE's DWARF file
+ lldb_private::Type *ResolveTypeUID(const DWARFDIE &die) const;
+
+ // Functions for obtaining DIE relations and references
+
+ DWARFDIE
+ GetParent() const;
+
+ DWARFDIE
+ GetFirstChild() const;
+
+ DWARFDIE
+ GetSibling() const;
+
+ DWARFDIE
+ GetReferencedDIE(const dw_attr_t attr) const;
+
+ // Get a another DIE from the same DWARF file as this DIE. This will
+ // check the current DIE's compile unit first to see if "die_offset" is
+ // in the same compile unit, and fall back to checking the DWARF file.
+ DWARFDIE
+ GetDIE(dw_offset_t die_offset) const;
+ using DWARFBaseDIE::GetDIE;
+
+ DWARFDIE
+ LookupDeepestBlock(lldb::addr_t file_addr) const;
+
+ DWARFDIE
+ GetParentDeclContextDIE() const;
+
+ // DeclContext related functions
+ std::vector<DWARFDIE> GetDeclContextDIEs() const;
+
+ void GetDWARFDeclContext(DWARFDeclContext &dwarf_decl_ctx) const;
+
+ /// Return this DIE's decl context as it is needed to look up types
+ /// in Clang's -gmodules debug info format.
+ void
+ GetDeclContext(std::vector<lldb_private::CompilerContext> &context) const;
+
+ // Getting attribute values from the DIE.
+ //
+ // GetAttributeValueAsXXX() functions should only be used if you are
+ // looking for one or two attributes on a DIE. If you are trying to
+ // parse all attributes, use GetAttributes (...) instead
+ DWARFDIE
+ GetAttributeValueAsReferenceDIE(const dw_attr_t attr) const;
+
+ bool GetDIENamesAndRanges(const char *&name, const char *&mangled,
+ DWARFRangeList &ranges, int &decl_file,
+ int &decl_line, int &decl_column, int &call_file,
+ int &call_line, int &call_column,
+ lldb_private::DWARFExpression *frame_base) const;
+
+ // CompilerDecl related functions
+
+ lldb_private::CompilerDecl GetDecl() const;
+
+ lldb_private::CompilerDeclContext GetDeclContext() const;
+
+ lldb_private::CompilerDeclContext GetContainingDeclContext() const;
+};
+
+#endif // SymbolFileDWARF_DWARFDIE_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp
new file mode 100644
index 000000000000..1678b228137f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp
@@ -0,0 +1,30 @@
+//===-- DWARFDataExtractor.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFDataExtractor.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_private {
+
+uint64_t
+DWARFDataExtractor::GetDWARFInitialLength(lldb::offset_t *offset_ptr) const {
+ return GetU32(offset_ptr);
+}
+
+dw_offset_t
+DWARFDataExtractor::GetDWARFOffset(lldb::offset_t *offset_ptr) const {
+ return GetMaxU64(offset_ptr, GetDWARFSizeOfOffset());
+}
+
+llvm::DWARFDataExtractor DWARFDataExtractor::GetAsLLVM() const {
+ return llvm::DWARFDataExtractor(
+ llvm::StringRef(reinterpret_cast<const char *>(GetDataStart()),
+ GetByteSize()),
+ GetByteOrder() == lldb::eByteOrderLittle, GetAddressByteSize());
+}
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h
new file mode 100644
index 000000000000..22db5e8c0b79
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h
@@ -0,0 +1,37 @@
+//===-- DWARFDataExtractor.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_DWARFDataExtractor_h_
+#define liblldb_DWARFDataExtractor_h_
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
+
+namespace lldb_private {
+
+class DWARFDataExtractor : public DataExtractor {
+public:
+ DWARFDataExtractor() = default;
+
+ DWARFDataExtractor(const DWARFDataExtractor &data, lldb::offset_t offset,
+ lldb::offset_t length)
+ : DataExtractor(data, offset, length) {}
+
+ uint64_t GetDWARFInitialLength(lldb::offset_t *offset_ptr) const;
+
+ dw_offset_t GetDWARFOffset(lldb::offset_t *offset_ptr) const;
+
+ size_t GetDWARFSizeofInitialLength() const { return 4; }
+ size_t GetDWARFSizeOfOffset() const { return 4; }
+
+ llvm::DWARFDataExtractor GetAsLLVM() const;
+};
+}
+
+#endif // liblldb_DWARFDataExtractor_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp
new file mode 100644
index 000000000000..26301566a8e1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp
@@ -0,0 +1,146 @@
+//===-- DWARFDebugAbbrev.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFDebugAbbrev.h"
+#include "DWARFDataExtractor.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace std;
+
+// DWARFAbbreviationDeclarationSet::Clear()
+void DWARFAbbreviationDeclarationSet::Clear() {
+ m_idx_offset = 0;
+ m_decls.clear();
+}
+
+// DWARFAbbreviationDeclarationSet::Extract()
+llvm::Error
+DWARFAbbreviationDeclarationSet::extract(const DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr) {
+ const lldb::offset_t begin_offset = *offset_ptr;
+ m_offset = begin_offset;
+ Clear();
+ DWARFAbbreviationDeclaration abbrevDeclaration;
+ dw_uleb128_t prev_abbr_code = 0;
+ while (true) {
+ llvm::Expected<DWARFEnumState> es =
+ abbrevDeclaration.extract(data, offset_ptr);
+ if (!es)
+ return es.takeError();
+ if (*es == DWARFEnumState::Complete)
+ break;
+ m_decls.push_back(abbrevDeclaration);
+ if (m_idx_offset == 0)
+ m_idx_offset = abbrevDeclaration.Code();
+ else if (prev_abbr_code + 1 != abbrevDeclaration.Code()) {
+ // Out of order indexes, we can't do O(1) lookups...
+ m_idx_offset = UINT32_MAX;
+ }
+ prev_abbr_code = abbrevDeclaration.Code();
+ }
+ return llvm::ErrorSuccess();
+}
+
+// DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration()
+const DWARFAbbreviationDeclaration *
+DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration(
+ dw_uleb128_t abbrCode) const {
+ if (m_idx_offset == UINT32_MAX) {
+ DWARFAbbreviationDeclarationCollConstIter pos;
+ DWARFAbbreviationDeclarationCollConstIter end = m_decls.end();
+ for (pos = m_decls.begin(); pos != end; ++pos) {
+ if (pos->Code() == abbrCode)
+ return &(*pos);
+ }
+ } else {
+ uint32_t idx = abbrCode - m_idx_offset;
+ if (idx < m_decls.size())
+ return &m_decls[idx];
+ }
+ return nullptr;
+}
+
+
+// DWARFAbbreviationDeclarationSet::GetUnsupportedForms()
+void DWARFAbbreviationDeclarationSet::GetUnsupportedForms(
+ std::set<dw_form_t> &invalid_forms) const {
+ for (const auto &abbr_decl : m_decls) {
+ const size_t num_attrs = abbr_decl.NumAttributes();
+ for (size_t i=0; i<num_attrs; ++i) {
+ dw_form_t form = abbr_decl.GetFormByIndex(i);
+ if (!DWARFFormValue::FormIsSupported(form))
+ invalid_forms.insert(form);
+ }
+ }
+}
+
+// Encode
+//
+// Encode the abbreviation table onto the end of the buffer provided into a
+// byte representation as would be found in a ".debug_abbrev" debug information
+// section.
+// void
+// DWARFAbbreviationDeclarationSet::Encode(BinaryStreamBuf& debug_abbrev_buf)
+// const
+//{
+// DWARFAbbreviationDeclarationCollConstIter pos;
+// DWARFAbbreviationDeclarationCollConstIter end = m_decls.end();
+// for (pos = m_decls.begin(); pos != end; ++pos)
+// pos->Append(debug_abbrev_buf);
+// debug_abbrev_buf.Append8(0);
+//}
+
+// DWARFDebugAbbrev constructor
+DWARFDebugAbbrev::DWARFDebugAbbrev()
+ : m_abbrevCollMap(), m_prev_abbr_offset_pos(m_abbrevCollMap.end()) {}
+
+// DWARFDebugAbbrev::Parse()
+llvm::Error DWARFDebugAbbrev::parse(const DWARFDataExtractor &data) {
+ lldb::offset_t offset = 0;
+
+ while (data.ValidOffset(offset)) {
+ uint32_t initial_cu_offset = offset;
+ DWARFAbbreviationDeclarationSet abbrevDeclSet;
+
+ llvm::Error error = abbrevDeclSet.extract(data, &offset);
+ if (error)
+ return error;
+
+ m_abbrevCollMap[initial_cu_offset] = abbrevDeclSet;
+ }
+ m_prev_abbr_offset_pos = m_abbrevCollMap.end();
+ return llvm::ErrorSuccess();
+}
+
+// DWARFDebugAbbrev::GetAbbreviationDeclarationSet()
+const DWARFAbbreviationDeclarationSet *
+DWARFDebugAbbrev::GetAbbreviationDeclarationSet(
+ dw_offset_t cu_abbr_offset) const {
+ DWARFAbbreviationDeclarationCollMapConstIter end = m_abbrevCollMap.end();
+ DWARFAbbreviationDeclarationCollMapConstIter pos;
+ if (m_prev_abbr_offset_pos != end &&
+ m_prev_abbr_offset_pos->first == cu_abbr_offset)
+ return &(m_prev_abbr_offset_pos->second);
+ else {
+ pos = m_abbrevCollMap.find(cu_abbr_offset);
+ m_prev_abbr_offset_pos = pos;
+ }
+
+ if (pos != m_abbrevCollMap.end())
+ return &(pos->second);
+ return nullptr;
+}
+
+// DWARFDebugAbbrev::GetUnsupportedForms()
+void DWARFDebugAbbrev::GetUnsupportedForms(
+ std::set<dw_form_t> &invalid_forms) const {
+ for (const auto &pair : m_abbrevCollMap)
+ pair.second.GetUnsupportedForms(invalid_forms);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h
new file mode 100644
index 000000000000..9c4729326081
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h
@@ -0,0 +1,81 @@
+//===-- DWARFDebugAbbrev.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFDebugAbbrev_h_
+#define SymbolFileDWARF_DWARFDebugAbbrev_h_
+
+#include <list>
+#include <map>
+
+#include "lldb/lldb-private.h"
+
+#include "DWARFAbbreviationDeclaration.h"
+#include "DWARFDefines.h"
+
+typedef std::vector<DWARFAbbreviationDeclaration>
+ DWARFAbbreviationDeclarationColl;
+typedef DWARFAbbreviationDeclarationColl::iterator
+ DWARFAbbreviationDeclarationCollIter;
+typedef DWARFAbbreviationDeclarationColl::const_iterator
+ DWARFAbbreviationDeclarationCollConstIter;
+
+class DWARFAbbreviationDeclarationSet {
+public:
+ DWARFAbbreviationDeclarationSet()
+ : m_offset(DW_INVALID_OFFSET), m_idx_offset(0), m_decls() {}
+
+ DWARFAbbreviationDeclarationSet(dw_offset_t offset, uint32_t idx_offset)
+ : m_offset(offset), m_idx_offset(idx_offset), m_decls() {}
+
+ void Clear();
+ dw_offset_t GetOffset() const { return m_offset; }
+
+ /// Extract all abbrev decls in a set. Returns llvm::ErrorSuccess() on
+ /// success, and an appropriate llvm::Error object otherwise.
+ llvm::Error extract(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr);
+ // void Encode(BinaryStreamBuf& debug_abbrev_buf) const;
+ void GetUnsupportedForms(std::set<dw_form_t> &invalid_forms) const;
+
+ const DWARFAbbreviationDeclaration *
+ GetAbbreviationDeclaration(dw_uleb128_t abbrCode) const;
+
+ /// Unit test accessor functions.
+ /// @{
+ uint32_t GetIndexOffset() const { return m_idx_offset; }
+ /// @}
+private:
+ dw_offset_t m_offset;
+ uint32_t m_idx_offset;
+ std::vector<DWARFAbbreviationDeclaration> m_decls;
+};
+
+typedef std::map<dw_offset_t, DWARFAbbreviationDeclarationSet>
+ DWARFAbbreviationDeclarationCollMap;
+typedef DWARFAbbreviationDeclarationCollMap::iterator
+ DWARFAbbreviationDeclarationCollMapIter;
+typedef DWARFAbbreviationDeclarationCollMap::const_iterator
+ DWARFAbbreviationDeclarationCollMapConstIter;
+
+class DWARFDebugAbbrev {
+public:
+ DWARFDebugAbbrev();
+ const DWARFAbbreviationDeclarationSet *
+ GetAbbreviationDeclarationSet(dw_offset_t cu_abbr_offset) const;
+ /// Extract all abbreviations for a particular compile unit. Returns
+ /// llvm::ErrorSuccess() on success, and an appropriate llvm::Error object
+ /// otherwise.
+ llvm::Error parse(const lldb_private::DWARFDataExtractor &data);
+ void GetUnsupportedForms(std::set<dw_form_t> &invalid_forms) const;
+
+protected:
+ DWARFAbbreviationDeclarationCollMap m_abbrevCollMap;
+ mutable DWARFAbbreviationDeclarationCollMapConstIter m_prev_abbr_offset_pos;
+};
+
+#endif // SymbolFileDWARF_DWARFDebugAbbrev_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp
new file mode 100644
index 000000000000..86ce3b329b25
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp
@@ -0,0 +1,140 @@
+//===-- DWARFDebugArangeSet.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFDebugArangeSet.h"
+#include "DWARFDataExtractor.h"
+#include "llvm/Object/Error.h"
+#include <cassert>
+
+using namespace lldb_private;
+
+DWARFDebugArangeSet::DWARFDebugArangeSet()
+ : m_offset(DW_INVALID_OFFSET), m_header(), m_arange_descriptors() {
+ m_header.length = 0;
+ m_header.version = 0;
+ m_header.cu_offset = 0;
+ m_header.addr_size = 0;
+ m_header.seg_size = 0;
+}
+
+void DWARFDebugArangeSet::Clear() {
+ m_offset = DW_INVALID_OFFSET;
+ m_header.length = 0;
+ m_header.version = 0;
+ m_header.cu_offset = 0;
+ m_header.addr_size = 0;
+ m_header.seg_size = 0;
+ m_arange_descriptors.clear();
+}
+
+llvm::Error DWARFDebugArangeSet::extract(const DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr) {
+ assert(data.ValidOffset(*offset_ptr));
+
+ m_arange_descriptors.clear();
+ m_offset = *offset_ptr;
+
+ // 7.20 Address Range Table
+ //
+ // Each set of entries in the table of address ranges contained in the
+ // .debug_aranges section begins with a header consisting of: a 4-byte
+ // length containing the length of the set of entries for this compilation
+ // unit, not including the length field itself; a 2-byte version identifier
+ // containing the value 2 for DWARF Version 2; a 4-byte offset into
+ // the.debug_infosection; a 1-byte unsigned integer containing the size in
+ // bytes of an address (or the offset portion of an address for segmented
+ // addressing) on the target system; and a 1-byte unsigned integer
+ // containing the size in bytes of a segment descriptor on the target
+ // system. This header is followed by a series of tuples. Each tuple
+ // consists of an address and a length, each in the size appropriate for an
+ // address on the target architecture.
+ m_header.length = data.GetDWARFInitialLength(offset_ptr);
+ m_header.version = data.GetU16(offset_ptr);
+ m_header.cu_offset = data.GetDWARFOffset(offset_ptr);
+ m_header.addr_size = data.GetU8(offset_ptr);
+ m_header.seg_size = data.GetU8(offset_ptr);
+
+ // Try to avoid reading invalid arange sets by making sure:
+ // 1 - the version looks good
+ // 2 - the address byte size looks plausible
+ // 3 - the length seems to make sense
+ // size looks plausible
+ if (m_header.version < 2 || m_header.version > 5)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid arange header version");
+
+ if (m_header.addr_size != 4 && m_header.addr_size != 8)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid arange header address size");
+
+ if (m_header.length == 0)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid arange header length");
+
+ if (!data.ValidOffset(m_offset + sizeof(m_header.length) + m_header.length -
+ 1))
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid arange header length");
+
+ // The first tuple following the header in each set begins at an offset
+ // that is a multiple of the size of a single tuple (that is, twice the
+ // size of an address). The header is padded, if necessary, to the
+ // appropriate boundary.
+ const uint32_t header_size = *offset_ptr - m_offset;
+ const uint32_t tuple_size = m_header.addr_size << 1;
+ uint32_t first_tuple_offset = 0;
+ while (first_tuple_offset < header_size)
+ first_tuple_offset += tuple_size;
+
+ *offset_ptr = m_offset + first_tuple_offset;
+
+ Descriptor arangeDescriptor;
+
+ static_assert(sizeof(arangeDescriptor.address) ==
+ sizeof(arangeDescriptor.length),
+ "DWARFDebugArangeSet::Descriptor.address and "
+ "DWARFDebugArangeSet::Descriptor.length must have same size");
+
+ while (data.ValidOffset(*offset_ptr)) {
+ arangeDescriptor.address = data.GetMaxU64(offset_ptr, m_header.addr_size);
+ arangeDescriptor.length = data.GetMaxU64(offset_ptr, m_header.addr_size);
+
+ // Each set of tuples is terminated by a 0 for the address and 0 for
+ // the length.
+ if (!arangeDescriptor.address && !arangeDescriptor.length)
+ return llvm::ErrorSuccess();
+
+ m_arange_descriptors.push_back(arangeDescriptor);
+ }
+
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "arange descriptors not terminated by null entry");
+}
+
+class DescriptorContainsAddress {
+public:
+ DescriptorContainsAddress(dw_addr_t address) : m_address(address) {}
+ bool operator()(const DWARFDebugArangeSet::Descriptor &desc) const {
+ return (m_address >= desc.address) &&
+ (m_address < (desc.address + desc.length));
+ }
+
+private:
+ const dw_addr_t m_address;
+};
+
+dw_offset_t DWARFDebugArangeSet::FindAddress(dw_addr_t address) const {
+ DescriptorConstIter end = m_arange_descriptors.end();
+ DescriptorConstIter pos =
+ std::find_if(m_arange_descriptors.begin(), end, // Range
+ DescriptorContainsAddress(address)); // Predicate
+ if (pos != end)
+ return m_header.cu_offset;
+
+ return DW_INVALID_OFFSET;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h
new file mode 100644
index 000000000000..db0cf22a3f45
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h
@@ -0,0 +1,62 @@
+//===-- DWARFDebugArangeSet.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFDebugArangeSet_h_
+#define SymbolFileDWARF_DWARFDebugArangeSet_h_
+
+#include "lldb/Core/dwarf.h"
+#include <cstdint>
+#include <vector>
+
+class DWARFDebugArangeSet {
+public:
+ struct Header {
+ uint32_t length; // The total length of the entries for that set, not
+ // including the length field itself.
+ uint16_t version; // The DWARF version number
+ uint32_t cu_offset; // The offset from the beginning of the .debug_info
+ // section of the compilation unit entry referenced by
+ // the table.
+ uint8_t addr_size; // The size in bytes of an address on the target
+ // architecture. For segmented addressing, this is the
+ // size of the offset portion of the address
+ uint8_t seg_size; // The size in bytes of a segment descriptor on the target
+ // architecture. If the target system uses a flat address
+ // space, this value is 0.
+ };
+
+ struct Descriptor {
+ dw_addr_t address;
+ dw_addr_t length;
+ dw_addr_t end_address() const { return address + length; }
+ };
+
+ DWARFDebugArangeSet();
+ void Clear();
+ void SetOffset(uint32_t offset) { m_offset = offset; }
+ llvm::Error extract(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr);
+ dw_offset_t FindAddress(dw_addr_t address) const;
+ size_t NumDescriptors() const { return m_arange_descriptors.size(); }
+ const Header &GetHeader() const { return m_header; }
+
+ const Descriptor &GetDescriptorRef(uint32_t i) const {
+ return m_arange_descriptors[i];
+ }
+
+protected:
+ typedef std::vector<Descriptor> DescriptorColl;
+ typedef DescriptorColl::iterator DescriptorIter;
+ typedef DescriptorColl::const_iterator DescriptorConstIter;
+
+ uint32_t m_offset;
+ Header m_header;
+ DescriptorColl m_arange_descriptors;
+};
+
+#endif // SymbolFileDWARF_DWARFDebugArangeSet_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp
new file mode 100644
index 000000000000..ccf33e6dc341
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp
@@ -0,0 +1,95 @@
+//===-- DWARFDebugAranges.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFDebugAranges.h"
+#include "DWARFDebugArangeSet.h"
+#include "DWARFUnit.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Timer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Constructor
+DWARFDebugAranges::DWARFDebugAranges() : m_aranges() {}
+
+// CountArangeDescriptors
+class CountArangeDescriptors {
+public:
+ CountArangeDescriptors(uint32_t &count_ref) : count(count_ref) {
+ // printf("constructor CountArangeDescriptors()\n");
+ }
+ void operator()(const DWARFDebugArangeSet &set) {
+ count += set.NumDescriptors();
+ }
+ uint32_t &count;
+};
+
+// Extract
+llvm::Error
+DWARFDebugAranges::extract(const DWARFDataExtractor &debug_aranges_data) {
+ lldb::offset_t offset = 0;
+
+ DWARFDebugArangeSet set;
+ Range range;
+ while (debug_aranges_data.ValidOffset(offset)) {
+ llvm::Error error = set.extract(debug_aranges_data, &offset);
+ if (!error)
+ return error;
+
+ const uint32_t num_descriptors = set.NumDescriptors();
+ if (num_descriptors > 0) {
+ const dw_offset_t cu_offset = set.GetHeader().cu_offset;
+
+ for (uint32_t i = 0; i < num_descriptors; ++i) {
+ const DWARFDebugArangeSet::Descriptor &descriptor =
+ set.GetDescriptorRef(i);
+ m_aranges.Append(RangeToDIE::Entry(descriptor.address,
+ descriptor.length, cu_offset));
+ }
+ }
+ set.Clear();
+ }
+ return llvm::ErrorSuccess();
+}
+
+void DWARFDebugAranges::Dump(Log *log) const {
+ if (log == nullptr)
+ return;
+
+ const size_t num_entries = m_aranges.GetSize();
+ for (size_t i = 0; i < num_entries; ++i) {
+ const RangeToDIE::Entry *entry = m_aranges.GetEntryAtIndex(i);
+ if (entry)
+ log->Printf("0x%8.8x: [0x%" PRIx64 " - 0x%" PRIx64 ")", entry->data,
+ entry->GetRangeBase(), entry->GetRangeEnd());
+ }
+}
+
+void DWARFDebugAranges::AppendRange(dw_offset_t offset, dw_addr_t low_pc,
+ dw_addr_t high_pc) {
+ if (high_pc > low_pc)
+ m_aranges.Append(RangeToDIE::Entry(low_pc, high_pc - low_pc, offset));
+}
+
+void DWARFDebugAranges::Sort(bool minimize) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
+ static_cast<void *>(this));
+
+ m_aranges.Sort();
+ m_aranges.CombineConsecutiveEntriesWithEqualData();
+}
+
+// FindAddress
+dw_offset_t DWARFDebugAranges::FindAddress(dw_addr_t address) const {
+ const RangeToDIE::Entry *entry = m_aranges.FindEntryThatContains(address);
+ if (entry)
+ return entry->data;
+ return DW_INVALID_OFFSET;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h
new file mode 100644
index 000000000000..74ba011b27af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h
@@ -0,0 +1,55 @@
+//===-- DWARFDebugAranges.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFDebugAranges_h_
+#define SymbolFileDWARF_DWARFDebugAranges_h_
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/Utility/RangeMap.h"
+#include "llvm/Support/Error.h"
+
+class DWARFDebugAranges {
+protected:
+ typedef lldb_private::RangeDataVector<dw_addr_t, uint32_t, dw_offset_t>
+ RangeToDIE;
+
+public:
+ typedef RangeToDIE::Entry Range;
+ typedef std::vector<RangeToDIE::Entry> RangeColl;
+
+ DWARFDebugAranges();
+
+ void Clear() { m_aranges.Clear(); }
+
+ llvm::Error
+ extract(const lldb_private::DWARFDataExtractor &debug_aranges_data);
+
+ // Use append range multiple times and then call sort
+ void AppendRange(dw_offset_t cu_offset, dw_addr_t low_pc, dw_addr_t high_pc);
+
+ void Sort(bool minimize);
+
+ void Dump(lldb_private::Log *log) const;
+
+ dw_offset_t FindAddress(dw_addr_t address) const;
+
+ bool IsEmpty() const { return m_aranges.IsEmpty(); }
+ size_t GetNumRanges() const { return m_aranges.GetSize(); }
+
+ dw_offset_t OffsetAtIndex(uint32_t idx) const {
+ const Range *range = m_aranges.GetEntryAtIndex(idx);
+ if (range)
+ return range->data;
+ return DW_INVALID_OFFSET;
+ }
+
+protected:
+ RangeToDIE m_aranges;
+};
+
+#endif // SymbolFileDWARF_DWARFDebugAranges_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp
new file mode 100644
index 000000000000..100f35f8c6b0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp
@@ -0,0 +1,197 @@
+//===-- DWARFDebugInfo.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolFileDWARF.h"
+
+#include <algorithm>
+#include <set>
+
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include "llvm/Support/Casting.h"
+
+#include "DWARFCompileUnit.h"
+#include "DWARFContext.h"
+#include "DWARFDebugAranges.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFDebugInfoEntry.h"
+#include "DWARFFormValue.h"
+#include "DWARFTypeUnit.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace std;
+
+// Constructor
+DWARFDebugInfo::DWARFDebugInfo(SymbolFileDWARF &dwarf,
+ lldb_private::DWARFContext &context)
+ : m_dwarf(dwarf), m_context(context), m_units(), m_cu_aranges_up() {}
+
+llvm::Expected<DWARFDebugAranges &> DWARFDebugInfo::GetCompileUnitAranges() {
+ if (m_cu_aranges_up)
+ return *m_cu_aranges_up;
+
+ m_cu_aranges_up = llvm::make_unique<DWARFDebugAranges>();
+ const DWARFDataExtractor &debug_aranges_data =
+ m_context.getOrLoadArangesData();
+ if (llvm::Error error = m_cu_aranges_up->extract(debug_aranges_data))
+ return std::move(error);
+
+ // Make a list of all CUs represented by the arange data in the file.
+ std::set<dw_offset_t> cus_with_data;
+ for (size_t n = 0; n < m_cu_aranges_up->GetNumRanges(); n++) {
+ dw_offset_t offset = m_cu_aranges_up->OffsetAtIndex(n);
+ if (offset != DW_INVALID_OFFSET)
+ cus_with_data.insert(offset);
+ }
+
+ // Manually build arange data for everything that wasn't in the
+ // .debug_aranges table.
+ const size_t num_units = GetNumUnits();
+ for (size_t idx = 0; idx < num_units; ++idx) {
+ DWARFUnit *cu = GetUnitAtIndex(idx);
+
+ dw_offset_t offset = cu->GetOffset();
+ if (cus_with_data.find(offset) == cus_with_data.end())
+ cu->BuildAddressRangeTable(m_cu_aranges_up.get());
+ }
+
+ const bool minimize = true;
+ m_cu_aranges_up->Sort(minimize);
+ return *m_cu_aranges_up;
+}
+
+void DWARFDebugInfo::ParseUnitsFor(DIERef::Section section) {
+ DWARFDataExtractor data = section == DIERef::Section::DebugTypes
+ ? m_context.getOrLoadDebugTypesData()
+ : m_context.getOrLoadDebugInfoData();
+ lldb::offset_t offset = 0;
+ while (data.ValidOffset(offset)) {
+ llvm::Expected<DWARFUnitSP> unit_sp =
+ DWARFUnit::extract(m_dwarf, m_units.size(), data, section, &offset);
+
+ if (!unit_sp) {
+ // FIXME: Propagate this error up.
+ llvm::consumeError(unit_sp.takeError());
+ return;
+ }
+
+ // If it didn't return an error, then it should be returning a valid Unit.
+ assert(*unit_sp);
+ m_units.push_back(*unit_sp);
+ offset = (*unit_sp)->GetNextUnitOffset();
+
+ if (auto *type_unit = llvm::dyn_cast<DWARFTypeUnit>(unit_sp->get())) {
+ m_type_hash_to_unit_index.emplace_back(type_unit->GetTypeHash(),
+ unit_sp.get()->GetID());
+ }
+ }
+}
+
+void DWARFDebugInfo::ParseUnitHeadersIfNeeded() {
+ if (!m_units.empty())
+ return;
+
+ ParseUnitsFor(DIERef::Section::DebugInfo);
+ ParseUnitsFor(DIERef::Section::DebugTypes);
+ llvm::sort(m_type_hash_to_unit_index, llvm::less_first());
+}
+
+size_t DWARFDebugInfo::GetNumUnits() {
+ ParseUnitHeadersIfNeeded();
+ return m_units.size();
+}
+
+DWARFUnit *DWARFDebugInfo::GetUnitAtIndex(user_id_t idx) {
+ DWARFUnit *cu = nullptr;
+ if (idx < GetNumUnits())
+ cu = m_units[idx].get();
+ return cu;
+}
+
+uint32_t DWARFDebugInfo::FindUnitIndex(DIERef::Section section,
+ dw_offset_t offset) {
+ ParseUnitHeadersIfNeeded();
+
+ // llvm::lower_bound is not used as for DIE offsets it would still return
+ // index +1 and GetOffset() returning index itself would be a special case.
+ auto pos = llvm::upper_bound(
+ m_units, std::make_pair(section, offset),
+ [](const std::pair<DIERef::Section, dw_offset_t> &lhs,
+ const DWARFUnitSP &rhs) {
+ return lhs < std::make_pair(rhs->GetDebugSection(), rhs->GetOffset());
+ });
+ uint32_t idx = std::distance(m_units.begin(), pos);
+ if (idx == 0)
+ return DW_INVALID_OFFSET;
+ return idx - 1;
+}
+
+DWARFUnit *DWARFDebugInfo::GetUnitAtOffset(DIERef::Section section,
+ dw_offset_t cu_offset,
+ uint32_t *idx_ptr) {
+ uint32_t idx = FindUnitIndex(section, cu_offset);
+ DWARFUnit *result = GetUnitAtIndex(idx);
+ if (result && result->GetOffset() != cu_offset) {
+ result = nullptr;
+ idx = DW_INVALID_INDEX;
+ }
+ if (idx_ptr)
+ *idx_ptr = idx;
+ return result;
+}
+
+DWARFUnit *DWARFDebugInfo::GetUnit(const DIERef &die_ref) {
+ return GetUnitContainingDIEOffset(die_ref.section(), die_ref.die_offset());
+}
+
+DWARFUnit *
+DWARFDebugInfo::GetUnitContainingDIEOffset(DIERef::Section section,
+ dw_offset_t die_offset) {
+ uint32_t idx = FindUnitIndex(section, die_offset);
+ DWARFUnit *result = GetUnitAtIndex(idx);
+ if (result && !result->ContainsDIEOffset(die_offset))
+ return nullptr;
+ return result;
+}
+
+DWARFTypeUnit *DWARFDebugInfo::GetTypeUnitForHash(uint64_t hash) {
+ auto pos = llvm::lower_bound(m_type_hash_to_unit_index,
+ std::make_pair(hash, 0u), llvm::less_first());
+ if (pos == m_type_hash_to_unit_index.end() || pos->first != hash)
+ return nullptr;
+ return llvm::cast<DWARFTypeUnit>(GetUnitAtIndex(pos->second));
+}
+
+bool DWARFDebugInfo::ContainsTypeUnits() {
+ ParseUnitHeadersIfNeeded();
+ return !m_type_hash_to_unit_index.empty();
+}
+
+DWARFDIE
+DWARFDebugInfo::GetDIEForDIEOffset(DIERef::Section section,
+ dw_offset_t die_offset) {
+ DWARFUnit *cu = GetUnitContainingDIEOffset(section, die_offset);
+ if (cu)
+ return cu->GetDIE(die_offset);
+ return DWARFDIE();
+}
+
+// GetDIE()
+//
+// Get the DIE (Debug Information Entry) with the specified offset.
+DWARFDIE
+DWARFDebugInfo::GetDIE(const DIERef &die_ref) {
+ DWARFUnit *cu = GetUnit(die_ref);
+ if (cu)
+ return cu->GetDIE(die_ref.die_offset());
+ return DWARFDIE(); // Not found
+}
+
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h
new file mode 100644
index 000000000000..d1b066ffe80c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h
@@ -0,0 +1,88 @@
+//===-- DWARFDebugInfo.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFDebugInfo_h_
+#define SymbolFileDWARF_DWARFDebugInfo_h_
+
+#include <map>
+#include <vector>
+
+#include "DWARFDIE.h"
+#include "DWARFTypeUnit.h"
+#include "DWARFUnit.h"
+#include "SymbolFileDWARF.h"
+#include "lldb/Core/STLUtils.h"
+#include "lldb/lldb-private.h"
+#include "llvm/Support/Error.h"
+
+namespace lldb_private {
+class DWARFContext;
+}
+
+typedef std::multimap<const char *, dw_offset_t, CStringCompareFunctionObject>
+ CStringToDIEMap;
+typedef CStringToDIEMap::iterator CStringToDIEMapIter;
+typedef CStringToDIEMap::const_iterator CStringToDIEMapConstIter;
+
+class DWARFDebugInfo {
+public:
+ typedef dw_offset_t (*Callback)(SymbolFileDWARF *dwarf2Data,
+ DWARFUnit *cu,
+ DWARFDebugInfoEntry *die,
+ const dw_offset_t next_offset,
+ const uint32_t depth, void *userData);
+
+ explicit DWARFDebugInfo(SymbolFileDWARF &dwarf,
+ lldb_private::DWARFContext &context);
+
+ size_t GetNumUnits();
+ DWARFUnit *GetUnitAtIndex(lldb::user_id_t idx);
+ DWARFUnit *GetUnitAtOffset(DIERef::Section section, dw_offset_t cu_offset,
+ uint32_t *idx_ptr = nullptr);
+ DWARFUnit *GetUnitContainingDIEOffset(DIERef::Section section,
+ dw_offset_t die_offset);
+ DWARFUnit *GetUnit(const DIERef &die_ref);
+ DWARFTypeUnit *GetTypeUnitForHash(uint64_t hash);
+ bool ContainsTypeUnits();
+ DWARFDIE GetDIEForDIEOffset(DIERef::Section section,
+ dw_offset_t die_offset);
+ DWARFDIE GetDIE(const DIERef &die_ref);
+
+ enum {
+ eDumpFlag_Verbose = (1 << 0), // Verbose dumping
+ eDumpFlag_ShowForm = (1 << 1), // Show the DW_form type
+ eDumpFlag_ShowAncestors =
+ (1 << 2) // Show all parent DIEs when dumping single DIEs
+ };
+
+ llvm::Expected<DWARFDebugAranges &> GetCompileUnitAranges();
+
+protected:
+ typedef std::vector<DWARFUnitSP> UnitColl;
+
+ SymbolFileDWARF &m_dwarf;
+ lldb_private::DWARFContext &m_context;
+ UnitColl m_units;
+ std::unique_ptr<DWARFDebugAranges>
+ m_cu_aranges_up; // A quick address to compile unit table
+
+ std::vector<std::pair<uint64_t, uint32_t>> m_type_hash_to_unit_index;
+
+private:
+ // All parsing needs to be done partially any managed by this class as
+ // accessors are called.
+ void ParseUnitHeadersIfNeeded();
+
+ void ParseUnitsFor(DIERef::Section section);
+
+ uint32_t FindUnitIndex(DIERef::Section section, dw_offset_t offset);
+
+ DISALLOW_COPY_AND_ASSIGN(DWARFDebugInfo);
+};
+
+#endif // SymbolFileDWARF_DWARFDebugInfo_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
new file mode 100644
index 000000000000..2f55b7d40ed9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
@@ -0,0 +1,1246 @@
+//===-- DWARFDebugInfoEntry.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFDebugInfoEntry.h"
+
+#include <assert.h>
+
+#include <algorithm>
+
+#include "llvm/Support/LEB128.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/Stream.h"
+
+#include "DWARFCompileUnit.h"
+#include "DWARFDebugAbbrev.h"
+#include "DWARFDebugAranges.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFDebugRanges.h"
+#include "DWARFDeclContext.h"
+#include "DWARFFormValue.h"
+#include "DWARFUnit.h"
+#include "SymbolFileDWARF.h"
+#include "SymbolFileDWARFDwo.h"
+
+using namespace lldb_private;
+using namespace std;
+extern int g_verbose;
+
+// Extract a debug info entry for a given DWARFUnit from the data
+// starting at the offset in offset_ptr
+bool DWARFDebugInfoEntry::Extract(const DWARFDataExtractor &data,
+ const DWARFUnit *cu,
+ lldb::offset_t *offset_ptr) {
+ m_offset = *offset_ptr;
+ m_parent_idx = 0;
+ m_sibling_idx = 0;
+ const uint64_t abbr_idx = data.GetULEB128(offset_ptr);
+ lldbassert(abbr_idx <= UINT16_MAX);
+ m_abbr_idx = abbr_idx;
+
+ // assert (fixed_form_sizes); // For best performance this should be
+ // specified!
+
+ if (m_abbr_idx) {
+ lldb::offset_t offset = *offset_ptr;
+ const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu);
+ if (abbrevDecl == nullptr) {
+ cu->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError(
+ "{0x%8.8x}: invalid abbreviation code %u, please file a bug and "
+ "attach the file at the start of this error message",
+ m_offset, (unsigned)abbr_idx);
+ // WE can't parse anymore if the DWARF is borked...
+ *offset_ptr = UINT32_MAX;
+ return false;
+ }
+ m_tag = abbrevDecl->Tag();
+ m_has_children = abbrevDecl->HasChildren();
+ // Skip all data in the .debug_info or .debug_types for the attributes
+ const uint32_t numAttributes = abbrevDecl->NumAttributes();
+ uint32_t i;
+ dw_form_t form;
+ for (i = 0; i < numAttributes; ++i) {
+ form = abbrevDecl->GetFormByIndexUnchecked(i);
+ llvm::Optional<uint8_t> fixed_skip_size =
+ DWARFFormValue::GetFixedSize(form, cu);
+ if (fixed_skip_size)
+ offset += *fixed_skip_size;
+ else {
+ bool form_is_indirect = false;
+ do {
+ form_is_indirect = false;
+ uint32_t form_size = 0;
+ switch (form) {
+ // Blocks if inlined data that have a length field and the data bytes
+ // inlined in the .debug_info/.debug_types
+ case DW_FORM_exprloc:
+ case DW_FORM_block:
+ form_size = data.GetULEB128(&offset);
+ break;
+ case DW_FORM_block1:
+ form_size = data.GetU8_unchecked(&offset);
+ break;
+ case DW_FORM_block2:
+ form_size = data.GetU16_unchecked(&offset);
+ break;
+ case DW_FORM_block4:
+ form_size = data.GetU32_unchecked(&offset);
+ break;
+
+ // Inlined NULL terminated C-strings
+ case DW_FORM_string:
+ data.GetCStr(&offset);
+ break;
+
+ // Compile unit address sized values
+ case DW_FORM_addr:
+ form_size = cu->GetAddressByteSize();
+ break;
+ case DW_FORM_ref_addr:
+ if (cu->GetVersion() <= 2)
+ form_size = cu->GetAddressByteSize();
+ else
+ form_size = 4;
+ break;
+
+ // 0 sized form
+ case DW_FORM_flag_present:
+ form_size = 0;
+ break;
+
+ // 1 byte values
+ case DW_FORM_addrx1:
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ case DW_FORM_ref1:
+ case DW_FORM_strx1:
+ form_size = 1;
+ break;
+
+ // 2 byte values
+ case DW_FORM_addrx2:
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ case DW_FORM_strx2:
+ form_size = 2;
+ break;
+
+ // 3 byte values
+ case DW_FORM_addrx3:
+ case DW_FORM_strx3:
+ form_size = 3;
+ break;
+
+ // 4 byte values
+ case DW_FORM_addrx4:
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ case DW_FORM_strx4:
+ form_size = 4;
+ break;
+
+ // 8 byte values
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_sig8:
+ form_size = 8;
+ break;
+
+ // signed or unsigned LEB 128 values
+ case DW_FORM_addrx:
+ case DW_FORM_rnglistx:
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ case DW_FORM_ref_udata:
+ case DW_FORM_GNU_addr_index:
+ case DW_FORM_GNU_str_index:
+ case DW_FORM_strx:
+ data.Skip_LEB128(&offset);
+ break;
+
+ case DW_FORM_indirect:
+ form_is_indirect = true;
+ form = data.GetULEB128(&offset);
+ break;
+
+ case DW_FORM_strp:
+ case DW_FORM_sec_offset:
+ data.GetU32(&offset);
+ break;
+
+ case DW_FORM_implicit_const:
+ form_size = 0;
+ break;
+
+ default:
+ *offset_ptr = m_offset;
+ return false;
+ }
+ offset += form_size;
+
+ } while (form_is_indirect);
+ }
+ }
+ *offset_ptr = offset;
+ return true;
+ } else {
+ m_tag = 0;
+ m_has_children = false;
+ return true; // NULL debug tag entry
+ }
+
+ return false;
+}
+
+static DWARFRangeList GetRangesOrReportError(const DWARFUnit &unit,
+ const DWARFDebugInfoEntry &die,
+ const DWARFFormValue &value) {
+ llvm::Expected<DWARFRangeList> expected_ranges =
+ (value.Form() == DW_FORM_rnglistx)
+ ? unit.FindRnglistFromIndex(value.Unsigned())
+ : unit.FindRnglistFromOffset(value.Unsigned());
+ if (expected_ranges)
+ return std::move(*expected_ranges);
+ unit.GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError(
+ "{0x%8.8x}: DIE has DW_AT_ranges(0x%" PRIx64 ") attribute, but "
+ "range extraction failed (%s), please file a bug "
+ "and attach the file at the start of this error message",
+ die.GetOffset(), value.Unsigned(),
+ toString(expected_ranges.takeError()).c_str());
+ return DWARFRangeList();
+}
+
+// GetDIENamesAndRanges
+//
+// Gets the valid address ranges for a given DIE by looking for a
+// DW_AT_low_pc/DW_AT_high_pc pair, DW_AT_entry_pc, or DW_AT_ranges attributes.
+bool DWARFDebugInfoEntry::GetDIENamesAndRanges(
+ const DWARFUnit *cu, const char *&name, const char *&mangled,
+ DWARFRangeList &ranges, int &decl_file, int &decl_line, int &decl_column,
+ int &call_file, int &call_line, int &call_column,
+ DWARFExpression *frame_base) const {
+ dw_addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ dw_addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ std::vector<DWARFDIE> dies;
+ bool set_frame_base_loclist_addr = false;
+
+ const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu);
+
+ SymbolFileDWARF &dwarf = cu->GetSymbolFileDWARF();
+ lldb::ModuleSP module = dwarf.GetObjectFile()->GetModule();
+
+ if (abbrevDecl) {
+ const DWARFDataExtractor &data = cu->GetData();
+ lldb::offset_t offset = GetFirstAttributeOffset();
+
+ if (!data.ValidOffset(offset))
+ return false;
+
+ const uint32_t numAttributes = abbrevDecl->NumAttributes();
+ bool do_offset = false;
+
+ for (uint32_t i = 0; i < numAttributes; ++i) {
+ DWARFFormValue form_value(cu);
+ dw_attr_t attr;
+ abbrevDecl->GetAttrAndFormValueByIndex(i, attr, form_value);
+
+ if (form_value.ExtractValue(data, &offset)) {
+ switch (attr) {
+ case DW_AT_low_pc:
+ lo_pc = form_value.Address();
+
+ if (do_offset)
+ hi_pc += lo_pc;
+ do_offset = false;
+ break;
+
+ case DW_AT_entry_pc:
+ lo_pc = form_value.Address();
+ break;
+
+ case DW_AT_high_pc:
+ if (form_value.Form() == DW_FORM_addr ||
+ form_value.Form() == DW_FORM_addrx ||
+ form_value.Form() == DW_FORM_GNU_addr_index) {
+ hi_pc = form_value.Address();
+ } else {
+ hi_pc = form_value.Unsigned();
+ if (lo_pc == LLDB_INVALID_ADDRESS)
+ do_offset = hi_pc != LLDB_INVALID_ADDRESS;
+ else
+ hi_pc += lo_pc; // DWARF 4 introduces <offset-from-lo-pc> to save
+ // on relocations
+ }
+ break;
+
+ case DW_AT_ranges:
+ ranges = GetRangesOrReportError(*cu, *this, form_value);
+ break;
+
+ case DW_AT_name:
+ if (name == nullptr)
+ name = form_value.AsCString();
+ break;
+
+ case DW_AT_MIPS_linkage_name:
+ case DW_AT_linkage_name:
+ if (mangled == nullptr)
+ mangled = form_value.AsCString();
+ break;
+
+ case DW_AT_abstract_origin:
+ dies.push_back(form_value.Reference());
+ break;
+
+ case DW_AT_specification:
+ dies.push_back(form_value.Reference());
+ break;
+
+ case DW_AT_decl_file:
+ if (decl_file == 0)
+ decl_file = form_value.Unsigned();
+ break;
+
+ case DW_AT_decl_line:
+ if (decl_line == 0)
+ decl_line = form_value.Unsigned();
+ break;
+
+ case DW_AT_decl_column:
+ if (decl_column == 0)
+ decl_column = form_value.Unsigned();
+ break;
+
+ case DW_AT_call_file:
+ if (call_file == 0)
+ call_file = form_value.Unsigned();
+ break;
+
+ case DW_AT_call_line:
+ if (call_line == 0)
+ call_line = form_value.Unsigned();
+ break;
+
+ case DW_AT_call_column:
+ if (call_column == 0)
+ call_column = form_value.Unsigned();
+ break;
+
+ case DW_AT_frame_base:
+ if (frame_base) {
+ if (form_value.BlockData()) {
+ uint32_t block_offset =
+ form_value.BlockData() - data.GetDataStart();
+ uint32_t block_length = form_value.Unsigned();
+ *frame_base = DWARFExpression(module, data, cu,
+ block_offset, block_length);
+ } else {
+ const DWARFDataExtractor &debug_loc_data = dwarf.DebugLocData();
+ const dw_offset_t debug_loc_offset = form_value.Unsigned();
+
+ size_t loc_list_length = DWARFExpression::LocationListSize(
+ cu, debug_loc_data, debug_loc_offset);
+ if (loc_list_length > 0) {
+ *frame_base =
+ DWARFExpression(module, debug_loc_data, cu,
+ debug_loc_offset, loc_list_length);
+ if (lo_pc != LLDB_INVALID_ADDRESS) {
+ assert(lo_pc >= cu->GetBaseAddress());
+ frame_base->SetLocationListSlide(lo_pc -
+ cu->GetBaseAddress());
+ } else {
+ set_frame_base_loclist_addr = true;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if (ranges.IsEmpty()) {
+ if (lo_pc != LLDB_INVALID_ADDRESS) {
+ if (hi_pc != LLDB_INVALID_ADDRESS && hi_pc > lo_pc)
+ ranges.Append(DWARFRangeList::Entry(lo_pc, hi_pc - lo_pc));
+ else
+ ranges.Append(DWARFRangeList::Entry(lo_pc, 0));
+ }
+ }
+
+ if (set_frame_base_loclist_addr) {
+ dw_addr_t lowest_range_pc = ranges.GetMinRangeBase(0);
+ assert(lowest_range_pc >= cu->GetBaseAddress());
+ frame_base->SetLocationListSlide(lowest_range_pc - cu->GetBaseAddress());
+ }
+
+ if (ranges.IsEmpty() || name == nullptr || mangled == nullptr) {
+ for (const DWARFDIE &die : dies) {
+ if (die) {
+ die.GetDIE()->GetDIENamesAndRanges(die.GetCU(), name, mangled, ranges,
+ decl_file, decl_line, decl_column,
+ call_file, call_line, call_column);
+ }
+ }
+ }
+ return !ranges.IsEmpty();
+}
+
+// Dump
+//
+// Dumps a debug information entry and all of it's attributes to the specified
+// stream.
+void DWARFDebugInfoEntry::Dump(const DWARFUnit *cu, Stream &s,
+ uint32_t recurse_depth) const {
+ const DWARFDataExtractor &data = cu->GetData();
+ lldb::offset_t offset = m_offset;
+
+ if (data.ValidOffset(offset)) {
+ dw_uleb128_t abbrCode = data.GetULEB128(&offset);
+
+ s.Printf("\n0x%8.8x: ", m_offset);
+ s.Indent();
+ if (abbrCode != m_abbr_idx) {
+ s.Printf("error: DWARF has been modified\n");
+ } else if (abbrCode) {
+ const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu);
+ if (abbrevDecl) {
+ s.PutCString(DW_TAG_value_to_name(abbrevDecl->Tag()));
+ s.Printf(" [%u] %c\n", abbrCode, abbrevDecl->HasChildren() ? '*' : ' ');
+
+ // Dump all data in the .debug_info/.debug_types for the attributes
+ const uint32_t numAttributes = abbrevDecl->NumAttributes();
+ for (uint32_t i = 0; i < numAttributes; ++i) {
+ DWARFFormValue form_value(cu);
+ dw_attr_t attr;
+ abbrevDecl->GetAttrAndFormValueByIndex(i, attr, form_value);
+
+ DumpAttribute(cu, data, &offset, s, attr, form_value);
+ }
+
+ const DWARFDebugInfoEntry *child = GetFirstChild();
+ if (recurse_depth > 0 && child) {
+ s.IndentMore();
+
+ while (child) {
+ child->Dump(cu, s, recurse_depth - 1);
+ child = child->GetSibling();
+ }
+ s.IndentLess();
+ }
+ } else
+ s.Printf("Abbreviation code note found in 'debug_abbrev' class for "
+ "code: %u\n",
+ abbrCode);
+ } else {
+ s.Printf("NULL\n");
+ }
+ }
+}
+
+// DumpAttribute
+//
+// Dumps a debug information entry attribute along with it's form. Any special
+// display of attributes is done (disassemble location lists, show enumeration
+// values for attributes, etc).
+void DWARFDebugInfoEntry::DumpAttribute(
+ const DWARFUnit *cu, const DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr, Stream &s, dw_attr_t attr,
+ DWARFFormValue &form_value) {
+ bool show_form = s.GetFlags().Test(DWARFDebugInfo::eDumpFlag_ShowForm);
+
+ s.Printf(" ");
+ s.Indent(DW_AT_value_to_name(attr));
+
+ if (show_form) {
+ s.Printf("[%s", DW_FORM_value_to_name(form_value.Form()));
+ }
+
+ if (!form_value.ExtractValue(data, offset_ptr))
+ return;
+
+ if (show_form) {
+ if (form_value.Form() == DW_FORM_indirect) {
+ s.Printf(" [%s]", DW_FORM_value_to_name(form_value.Form()));
+ }
+
+ s.PutCString("] ");
+ }
+
+ s.PutCString("( ");
+
+ SymbolFileDWARF &dwarf = cu->GetSymbolFileDWARF();
+
+ // Check to see if we have any special attribute formatters
+ switch (attr) {
+ case DW_AT_stmt_list:
+ s.Printf("0x%8.8" PRIx64, form_value.Unsigned());
+ break;
+
+ case DW_AT_language:
+ s.PutCString(DW_LANG_value_to_name(form_value.Unsigned()));
+ break;
+
+ case DW_AT_encoding:
+ s.PutCString(DW_ATE_value_to_name(form_value.Unsigned()));
+ break;
+
+ case DW_AT_frame_base:
+ case DW_AT_location:
+ case DW_AT_data_member_location: {
+ const uint8_t *blockData = form_value.BlockData();
+ if (blockData) {
+ // Location description is inlined in data in the form value
+ DWARFDataExtractor locationData(data,
+ (*offset_ptr) - form_value.Unsigned(),
+ form_value.Unsigned());
+ DWARFExpression::PrintDWARFExpression(
+ s, locationData, DWARFUnit::GetAddressByteSize(cu), 4, false);
+ } else {
+ // We have a location list offset as the value that is the offset into
+ // the .debug_loc section that describes the value over it's lifetime
+ uint64_t debug_loc_offset = form_value.Unsigned();
+ DWARFExpression::PrintDWARFLocationList(s, cu, dwarf.DebugLocData(),
+ debug_loc_offset);
+ }
+ } break;
+
+ case DW_AT_abstract_origin:
+ case DW_AT_specification: {
+ DWARFDIE abstract_die = form_value.Reference();
+ form_value.Dump(s);
+ // *ostrm_ptr << HEX32 << abstract_die.GetOffset() << " ( ";
+ abstract_die.GetName(s);
+ } break;
+
+ case DW_AT_type: {
+ DWARFDIE type_die = form_value.Reference();
+ s.PutCString(" ( ");
+ type_die.AppendTypeName(s);
+ s.PutCString(" )");
+ } break;
+
+ default:
+ break;
+ }
+
+ s.PutCString(" )\n");
+}
+
+// Get all attribute values for a given DIE, including following any
+// specification or abstract origin attributes and including those in the
+// results. Any duplicate attributes will have the first instance take
+// precedence (this can happen for declaration attributes).
+size_t DWARFDebugInfoEntry::GetAttributes(
+ const DWARFUnit *cu, DWARFAttributes &attributes,
+ uint32_t curr_depth) const {
+ const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu);
+ if (abbrevDecl) {
+ const DWARFDataExtractor &data = cu->GetData();
+ lldb::offset_t offset = GetFirstAttributeOffset();
+
+ const uint32_t num_attributes = abbrevDecl->NumAttributes();
+ for (uint32_t i = 0; i < num_attributes; ++i) {
+ DWARFFormValue form_value(cu);
+ dw_attr_t attr;
+ abbrevDecl->GetAttrAndFormValueByIndex(i, attr, form_value);
+ const dw_form_t form = form_value.Form();
+
+ // If we are tracking down DW_AT_specification or DW_AT_abstract_origin
+ // attributes, the depth will be non-zero. We need to omit certain
+ // attributes that don't make sense.
+ switch (attr) {
+ case DW_AT_sibling:
+ case DW_AT_declaration:
+ if (curr_depth > 0) {
+ // This attribute doesn't make sense when combined with the DIE that
+ // references this DIE. We know a DIE is referencing this DIE because
+ // curr_depth is not zero
+ break;
+ }
+ LLVM_FALLTHROUGH;
+ default:
+ attributes.Append(cu, offset, attr, form);
+ break;
+ }
+
+ if ((attr == DW_AT_specification) || (attr == DW_AT_abstract_origin)) {
+ if (form_value.ExtractValue(data, &offset)) {
+ DWARFDIE spec_die = form_value.Reference();
+ if (spec_die)
+ spec_die.GetAttributes(attributes, curr_depth + 1);
+ }
+ } else {
+ llvm::Optional<uint8_t> fixed_skip_size = DWARFFormValue::GetFixedSize(form, cu);
+ if (fixed_skip_size)
+ offset += *fixed_skip_size;
+ else
+ DWARFFormValue::SkipValue(form, data, &offset, cu);
+ }
+ }
+ } else {
+ attributes.Clear();
+ }
+ return attributes.Size();
+}
+
+// GetAttributeValue
+//
+// Get the value of an attribute and return the .debug_info or .debug_types
+// offset of the attribute if it was properly extracted into form_value,
+// or zero if we fail since an offset of zero is invalid for an attribute (it
+// would be a compile unit header).
+dw_offset_t DWARFDebugInfoEntry::GetAttributeValue(
+ const DWARFUnit *cu, const dw_attr_t attr, DWARFFormValue &form_value,
+ dw_offset_t *end_attr_offset_ptr,
+ bool check_specification_or_abstract_origin) const {
+ if (const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu)) {
+ uint32_t attr_idx = abbrevDecl->FindAttributeIndex(attr);
+
+ if (attr_idx != DW_INVALID_INDEX) {
+ const DWARFDataExtractor &data = cu->GetData();
+ lldb::offset_t offset = GetFirstAttributeOffset();
+
+ uint32_t idx = 0;
+ while (idx < attr_idx)
+ DWARFFormValue::SkipValue(abbrevDecl->GetFormByIndex(idx++),
+ data, &offset, cu);
+
+ const dw_offset_t attr_offset = offset;
+ form_value.SetUnit(cu);
+ form_value.SetForm(abbrevDecl->GetFormByIndex(idx));
+ if (form_value.ExtractValue(data, &offset)) {
+ if (end_attr_offset_ptr)
+ *end_attr_offset_ptr = offset;
+ return attr_offset;
+ }
+ }
+ }
+
+ if (check_specification_or_abstract_origin) {
+ if (GetAttributeValue(cu, DW_AT_specification, form_value)) {
+ DWARFDIE die = form_value.Reference();
+ if (die) {
+ dw_offset_t die_offset = die.GetDIE()->GetAttributeValue(
+ die.GetCU(), attr, form_value, end_attr_offset_ptr, false);
+ if (die_offset)
+ return die_offset;
+ }
+ }
+
+ if (GetAttributeValue(cu, DW_AT_abstract_origin, form_value)) {
+ DWARFDIE die = form_value.Reference();
+ if (die) {
+ dw_offset_t die_offset = die.GetDIE()->GetAttributeValue(
+ die.GetCU(), attr, form_value, end_attr_offset_ptr, false);
+ if (die_offset)
+ return die_offset;
+ }
+ }
+ }
+
+ // If we're a unit DIE, also check the attributes of the dwo unit (if any).
+ if (GetParent())
+ return 0;
+ SymbolFileDWARFDwo *dwo_symbol_file = cu->GetDwoSymbolFile();
+ if (!dwo_symbol_file)
+ return 0;
+
+ DWARFCompileUnit *dwo_cu = dwo_symbol_file->GetCompileUnit();
+ if (!dwo_cu)
+ return 0;
+
+ DWARFBaseDIE dwo_cu_die = dwo_cu->GetUnitDIEOnly();
+ if (!dwo_cu_die.IsValid())
+ return 0;
+
+ return dwo_cu_die.GetDIE()->GetAttributeValue(
+ dwo_cu, attr, form_value, end_attr_offset_ptr,
+ check_specification_or_abstract_origin);
+}
+
+// GetAttributeValueAsString
+//
+// Get the value of an attribute as a string return it. The resulting pointer
+// to the string data exists within the supplied SymbolFileDWARF and will only
+// be available as long as the SymbolFileDWARF is still around and it's content
+// doesn't change.
+const char *DWARFDebugInfoEntry::GetAttributeValueAsString(
+ const DWARFUnit *cu, const dw_attr_t attr, const char *fail_value,
+ bool check_specification_or_abstract_origin) const {
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, attr, form_value, nullptr,
+ check_specification_or_abstract_origin))
+ return form_value.AsCString();
+ return fail_value;
+}
+
+// GetAttributeValueAsUnsigned
+//
+// Get the value of an attribute as unsigned and return it.
+uint64_t DWARFDebugInfoEntry::GetAttributeValueAsUnsigned(
+ const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value,
+ bool check_specification_or_abstract_origin) const {
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, attr, form_value, nullptr,
+ check_specification_or_abstract_origin))
+ return form_value.Unsigned();
+ return fail_value;
+}
+
+// GetAttributeValueAsReference
+//
+// Get the value of an attribute as reference and fix up and compile unit
+// relative offsets as needed.
+DWARFDIE DWARFDebugInfoEntry::GetAttributeValueAsReference(
+ const DWARFUnit *cu, const dw_attr_t attr,
+ bool check_specification_or_abstract_origin) const {
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, attr, form_value, nullptr,
+ check_specification_or_abstract_origin))
+ return form_value.Reference();
+ return {};
+}
+
+uint64_t DWARFDebugInfoEntry::GetAttributeValueAsAddress(
+ const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value,
+ bool check_specification_or_abstract_origin) const {
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, attr, form_value, nullptr,
+ check_specification_or_abstract_origin))
+ return form_value.Address();
+ return fail_value;
+}
+
+// GetAttributeHighPC
+//
+// Get the hi_pc, adding hi_pc to lo_pc when specified as an <offset-from-low-
+// pc>.
+//
+// Returns the hi_pc or fail_value.
+dw_addr_t DWARFDebugInfoEntry::GetAttributeHighPC(
+ const DWARFUnit *cu, dw_addr_t lo_pc, uint64_t fail_value,
+ bool check_specification_or_abstract_origin) const {
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, DW_AT_high_pc, form_value, nullptr,
+ check_specification_or_abstract_origin)) {
+ dw_form_t form = form_value.Form();
+ if (form == DW_FORM_addr || form == DW_FORM_addrx ||
+ form == DW_FORM_GNU_addr_index)
+ return form_value.Address();
+
+ // DWARF4 can specify the hi_pc as an <offset-from-lowpc>
+ return lo_pc + form_value.Unsigned();
+ }
+ return fail_value;
+}
+
+// GetAttributeAddressRange
+//
+// Get the lo_pc and hi_pc, adding hi_pc to lo_pc when specified as an <offset-
+// from-low-pc>.
+//
+// Returns true or sets lo_pc and hi_pc to fail_value.
+bool DWARFDebugInfoEntry::GetAttributeAddressRange(
+ const DWARFUnit *cu, dw_addr_t &lo_pc, dw_addr_t &hi_pc,
+ uint64_t fail_value, bool check_specification_or_abstract_origin) const {
+ lo_pc = GetAttributeValueAsAddress(cu, DW_AT_low_pc, fail_value,
+ check_specification_or_abstract_origin);
+ if (lo_pc != fail_value) {
+ hi_pc = GetAttributeHighPC(cu, lo_pc, fail_value,
+ check_specification_or_abstract_origin);
+ if (hi_pc != fail_value)
+ return true;
+ }
+ lo_pc = fail_value;
+ hi_pc = fail_value;
+ return false;
+}
+
+size_t DWARFDebugInfoEntry::GetAttributeAddressRanges(
+ const DWARFUnit *cu, DWARFRangeList &ranges, bool check_hi_lo_pc,
+ bool check_specification_or_abstract_origin) const {
+ ranges.Clear();
+
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, DW_AT_ranges, form_value)) {
+ ranges = GetRangesOrReportError(*cu, *this, form_value);
+ } else if (check_hi_lo_pc) {
+ dw_addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ dw_addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ if (GetAttributeAddressRange(cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS,
+ check_specification_or_abstract_origin)) {
+ if (lo_pc < hi_pc)
+ ranges.Append(DWARFRangeList::Entry(lo_pc, hi_pc - lo_pc));
+ }
+ }
+ return ranges.GetSize();
+}
+
+// GetName
+//
+// Get value of the DW_AT_name attribute and return it if one exists, else
+// return NULL.
+const char *DWARFDebugInfoEntry::GetName(const DWARFUnit *cu) const {
+ return GetAttributeValueAsString(cu, DW_AT_name, nullptr, true);
+}
+
+// GetMangledName
+//
+// Get value of the DW_AT_MIPS_linkage_name attribute and return it if one
+// exists, else return the value of the DW_AT_name attribute
+const char *
+DWARFDebugInfoEntry::GetMangledName(const DWARFUnit *cu,
+ bool substitute_name_allowed) const {
+ const char *name = nullptr;
+
+ name = GetAttributeValueAsString(cu, DW_AT_MIPS_linkage_name, nullptr, true);
+ if (name)
+ return name;
+
+ name = GetAttributeValueAsString(cu, DW_AT_linkage_name, nullptr, true);
+ if (name)
+ return name;
+
+ if (!substitute_name_allowed)
+ return nullptr;
+
+ name = GetAttributeValueAsString(cu, DW_AT_name, nullptr, true);
+ return name;
+}
+
+// GetPubname
+//
+// Get value the name for a DIE as it should appear for a .debug_pubnames or
+// .debug_pubtypes section.
+const char *DWARFDebugInfoEntry::GetPubname(const DWARFUnit *cu) const {
+ const char *name = nullptr;
+ if (!cu)
+ return name;
+
+ name = GetAttributeValueAsString(cu, DW_AT_MIPS_linkage_name, nullptr, true);
+ if (name)
+ return name;
+
+ name = GetAttributeValueAsString(cu, DW_AT_linkage_name, nullptr, true);
+ if (name)
+ return name;
+
+ name = GetAttributeValueAsString(cu, DW_AT_name, nullptr, true);
+ return name;
+}
+
+// BuildAddressRangeTable
+void DWARFDebugInfoEntry::BuildAddressRangeTable(
+ const DWARFUnit *cu, DWARFDebugAranges *debug_aranges) const {
+ if (m_tag) {
+ if (m_tag == DW_TAG_subprogram) {
+ dw_addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ dw_addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ if (GetAttributeAddressRange(cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS)) {
+ /// printf("BuildAddressRangeTable() 0x%8.8x: %30s: [0x%8.8x -
+ /// 0x%8.8x)\n", m_offset, DW_TAG_value_to_name(tag), lo_pc, hi_pc);
+ debug_aranges->AppendRange(cu->GetOffset(), lo_pc, hi_pc);
+ }
+ }
+
+ const DWARFDebugInfoEntry *child = GetFirstChild();
+ while (child) {
+ child->BuildAddressRangeTable(cu, debug_aranges);
+ child = child->GetSibling();
+ }
+ }
+}
+
+// BuildFunctionAddressRangeTable
+//
+// This function is very similar to the BuildAddressRangeTable function except
+// that the actual DIE offset for the function is placed in the table instead
+// of the compile unit offset (which is the way the standard .debug_aranges
+// section does it).
+void DWARFDebugInfoEntry::BuildFunctionAddressRangeTable(
+ const DWARFUnit *cu, DWARFDebugAranges *debug_aranges) const {
+ if (m_tag) {
+ if (m_tag == DW_TAG_subprogram) {
+ dw_addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ dw_addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ if (GetAttributeAddressRange(cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS)) {
+ // printf("BuildAddressRangeTable() 0x%8.8x: [0x%16.16" PRIx64 " -
+ // 0x%16.16" PRIx64 ")\n", m_offset, lo_pc, hi_pc); // DEBUG ONLY
+ debug_aranges->AppendRange(GetOffset(), lo_pc, hi_pc);
+ }
+ }
+
+ const DWARFDebugInfoEntry *child = GetFirstChild();
+ while (child) {
+ child->BuildFunctionAddressRangeTable(cu, debug_aranges);
+ child = child->GetSibling();
+ }
+ }
+}
+
+void DWARFDebugInfoEntry::GetDWARFDeclContext(
+ DWARFUnit *cu, DWARFDeclContext &dwarf_decl_ctx) const {
+ const dw_tag_t tag = Tag();
+ if (tag != DW_TAG_compile_unit && tag != DW_TAG_partial_unit) {
+ dwarf_decl_ctx.AppendDeclContext(tag, GetName(cu));
+ DWARFDIE parent_decl_ctx_die = GetParentDeclContextDIE(cu);
+ if (parent_decl_ctx_die && parent_decl_ctx_die.GetDIE() != this) {
+ if (parent_decl_ctx_die.Tag() != DW_TAG_compile_unit &&
+ parent_decl_ctx_die.Tag() != DW_TAG_partial_unit)
+ parent_decl_ctx_die.GetDIE()->GetDWARFDeclContext(
+ parent_decl_ctx_die.GetCU(), dwarf_decl_ctx);
+ }
+ }
+}
+
+DWARFDIE
+DWARFDebugInfoEntry::GetParentDeclContextDIE(DWARFUnit *cu) const {
+ DWARFAttributes attributes;
+ GetAttributes(cu, attributes);
+ return GetParentDeclContextDIE(cu, attributes);
+}
+
+DWARFDIE
+DWARFDebugInfoEntry::GetParentDeclContextDIE(
+ DWARFUnit *cu, const DWARFAttributes &attributes) const {
+ DWARFDIE die(cu, const_cast<DWARFDebugInfoEntry *>(this));
+
+ while (die) {
+ // If this is the original DIE that we are searching for a declaration for,
+ // then don't look in the cache as we don't want our own decl context to be
+ // our decl context...
+ if (die.GetDIE() != this) {
+ switch (die.Tag()) {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ case DW_TAG_namespace:
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type:
+ return die;
+
+ default:
+ break;
+ }
+ }
+
+ DWARFDIE spec_die = attributes.FormValueAsReference(DW_AT_specification);
+ if (spec_die) {
+ DWARFDIE decl_ctx_die = spec_die.GetParentDeclContextDIE();
+ if (decl_ctx_die)
+ return decl_ctx_die;
+ }
+
+ DWARFDIE abs_die = attributes.FormValueAsReference(DW_AT_abstract_origin);
+ if (abs_die) {
+ DWARFDIE decl_ctx_die = abs_die.GetParentDeclContextDIE();
+ if (decl_ctx_die)
+ return decl_ctx_die;
+ }
+
+ die = die.GetParent();
+ }
+ return DWARFDIE();
+}
+
+const char *DWARFDebugInfoEntry::GetQualifiedName(DWARFUnit *cu,
+ std::string &storage) const {
+ DWARFAttributes attributes;
+ GetAttributes(cu, attributes);
+ return GetQualifiedName(cu, attributes, storage);
+}
+
+const char *
+DWARFDebugInfoEntry::GetQualifiedName(DWARFUnit *cu,
+ const DWARFAttributes &attributes,
+ std::string &storage) const {
+
+ const char *name = GetName(cu);
+
+ if (name) {
+ DWARFDIE parent_decl_ctx_die = GetParentDeclContextDIE(cu);
+ storage.clear();
+ // TODO: change this to get the correct decl context parent....
+ while (parent_decl_ctx_die) {
+ const dw_tag_t parent_tag = parent_decl_ctx_die.Tag();
+ switch (parent_tag) {
+ case DW_TAG_namespace: {
+ const char *namespace_name = parent_decl_ctx_die.GetName();
+ if (namespace_name) {
+ storage.insert(0, "::");
+ storage.insert(0, namespace_name);
+ } else {
+ storage.insert(0, "(anonymous namespace)::");
+ }
+ parent_decl_ctx_die = parent_decl_ctx_die.GetParentDeclContextDIE();
+ } break;
+
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type: {
+ const char *class_union_struct_name = parent_decl_ctx_die.GetName();
+
+ if (class_union_struct_name) {
+ storage.insert(0, "::");
+ storage.insert(0, class_union_struct_name);
+ }
+ parent_decl_ctx_die = parent_decl_ctx_die.GetParentDeclContextDIE();
+ } break;
+
+ default:
+ parent_decl_ctx_die.Clear();
+ break;
+ }
+ }
+
+ if (storage.empty())
+ storage.append("::");
+
+ storage.append(name);
+ }
+ if (storage.empty())
+ return nullptr;
+ return storage.c_str();
+}
+
+bool DWARFDebugInfoEntry::LookupAddress(const dw_addr_t address,
+ const DWARFUnit *cu,
+ DWARFDebugInfoEntry **function_die,
+ DWARFDebugInfoEntry **block_die) {
+ bool found_address = false;
+ if (m_tag) {
+ bool check_children = false;
+ bool match_addr_range = false;
+ // printf("0x%8.8x: %30s: address = 0x%8.8x - ", m_offset,
+ // DW_TAG_value_to_name(tag), address);
+ switch (m_tag) {
+ case DW_TAG_array_type:
+ break;
+ case DW_TAG_class_type:
+ check_children = true;
+ break;
+ case DW_TAG_entry_point:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_formal_parameter:
+ case DW_TAG_imported_declaration:
+ case DW_TAG_label:
+ break;
+ case DW_TAG_lexical_block:
+ check_children = true;
+ match_addr_range = true;
+ break;
+ case DW_TAG_member:
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type:
+ break;
+ case DW_TAG_compile_unit:
+ match_addr_range = true;
+ break;
+ case DW_TAG_string_type:
+ break;
+ case DW_TAG_structure_type:
+ check_children = true;
+ break;
+ case DW_TAG_subroutine_type:
+ case DW_TAG_typedef:
+ case DW_TAG_union_type:
+ case DW_TAG_unspecified_parameters:
+ case DW_TAG_variant:
+ break;
+ case DW_TAG_common_block:
+ check_children = true;
+ break;
+ case DW_TAG_common_inclusion:
+ case DW_TAG_inheritance:
+ break;
+ case DW_TAG_inlined_subroutine:
+ check_children = true;
+ match_addr_range = true;
+ break;
+ case DW_TAG_module:
+ match_addr_range = true;
+ break;
+ case DW_TAG_ptr_to_member_type:
+ case DW_TAG_set_type:
+ case DW_TAG_subrange_type:
+ case DW_TAG_with_stmt:
+ case DW_TAG_access_declaration:
+ case DW_TAG_base_type:
+ break;
+ case DW_TAG_catch_block:
+ match_addr_range = true;
+ break;
+ case DW_TAG_const_type:
+ case DW_TAG_constant:
+ case DW_TAG_enumerator:
+ case DW_TAG_file_type:
+ case DW_TAG_friend:
+ case DW_TAG_namelist:
+ case DW_TAG_namelist_item:
+ case DW_TAG_packed_type:
+ break;
+ case DW_TAG_subprogram:
+ match_addr_range = true;
+ break;
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_template_value_parameter:
+ case DW_TAG_GNU_template_parameter_pack:
+ case DW_TAG_thrown_type:
+ break;
+ case DW_TAG_try_block:
+ match_addr_range = true;
+ break;
+ case DW_TAG_variant_part:
+ case DW_TAG_variable:
+ case DW_TAG_volatile_type:
+ case DW_TAG_dwarf_procedure:
+ case DW_TAG_restrict_type:
+ case DW_TAG_interface_type:
+ break;
+ case DW_TAG_namespace:
+ check_children = true;
+ break;
+ case DW_TAG_imported_module:
+ case DW_TAG_unspecified_type:
+ break;
+ case DW_TAG_partial_unit:
+ match_addr_range = true;
+ break;
+ case DW_TAG_imported_unit:
+ case DW_TAG_shared_type:
+ default:
+ break;
+ }
+
+ if (match_addr_range) {
+ dw_addr_t lo_pc =
+ GetAttributeValueAsAddress(cu, DW_AT_low_pc, LLDB_INVALID_ADDRESS);
+ if (lo_pc != LLDB_INVALID_ADDRESS) {
+ dw_addr_t hi_pc = GetAttributeHighPC(cu, lo_pc, LLDB_INVALID_ADDRESS);
+ if (hi_pc != LLDB_INVALID_ADDRESS) {
+ // printf("\n0x%8.8x: %30s: address = 0x%8.8x [0x%8.8x - 0x%8.8x) ",
+ // m_offset, DW_TAG_value_to_name(tag), address, lo_pc, hi_pc);
+ if ((lo_pc <= address) && (address < hi_pc)) {
+ found_address = true;
+ // puts("***MATCH***");
+ switch (m_tag) {
+ case DW_TAG_compile_unit: // File
+ case DW_TAG_partial_unit: // File
+ check_children =
+ ((function_die != nullptr) || (block_die != nullptr));
+ break;
+
+ case DW_TAG_subprogram: // Function
+ if (function_die)
+ *function_die = this;
+ check_children = (block_die != nullptr);
+ break;
+
+ case DW_TAG_inlined_subroutine: // Inlined Function
+ case DW_TAG_lexical_block: // Block { } in code
+ if (block_die) {
+ *block_die = this;
+ check_children = true;
+ }
+ break;
+
+ default:
+ check_children = true;
+ break;
+ }
+ }
+ } else {
+ // Compile units may not have a valid high/low pc when there
+ // are address gaps in subroutines so we must always search
+ // if there is no valid high and low PC.
+ check_children =
+ (m_tag == DW_TAG_compile_unit || m_tag == DW_TAG_partial_unit) &&
+ ((function_die != nullptr) || (block_die != nullptr));
+ }
+ } else {
+ DWARFRangeList ranges;
+ if (GetAttributeAddressRanges(cu, ranges, /*check_hi_lo_pc*/ false) &&
+ ranges.FindEntryThatContains(address)) {
+ found_address = true;
+ // puts("***MATCH***");
+ switch (m_tag) {
+ case DW_TAG_compile_unit: // File
+ case DW_TAG_partial_unit: // File
+ check_children =
+ ((function_die != nullptr) || (block_die != nullptr));
+ break;
+
+ case DW_TAG_subprogram: // Function
+ if (function_die)
+ *function_die = this;
+ check_children = (block_die != nullptr);
+ break;
+
+ case DW_TAG_inlined_subroutine: // Inlined Function
+ case DW_TAG_lexical_block: // Block { } in code
+ if (block_die) {
+ *block_die = this;
+ check_children = true;
+ }
+ break;
+
+ default:
+ check_children = true;
+ break;
+ }
+ } else {
+ check_children = false;
+ }
+ }
+ }
+
+ if (check_children) {
+ // printf("checking children\n");
+ DWARFDebugInfoEntry *child = GetFirstChild();
+ while (child) {
+ if (child->LookupAddress(address, cu, function_die, block_die))
+ return true;
+ child = child->GetSibling();
+ }
+ }
+ }
+ return found_address;
+}
+
+lldb::offset_t DWARFDebugInfoEntry::GetFirstAttributeOffset() const {
+ return GetOffset() + llvm::getULEB128Size(m_abbr_idx);
+}
+
+const DWARFAbbreviationDeclaration *
+DWARFDebugInfoEntry::GetAbbreviationDeclarationPtr(const DWARFUnit *cu) const {
+ if (cu) {
+ const DWARFAbbreviationDeclarationSet *abbrev_set = cu->GetAbbreviations();
+ if (abbrev_set)
+ return abbrev_set->GetAbbreviationDeclaration(m_abbr_idx);
+ }
+ return nullptr;
+}
+
+bool DWARFDebugInfoEntry::operator==(const DWARFDebugInfoEntry &rhs) const {
+ return m_offset == rhs.m_offset && m_parent_idx == rhs.m_parent_idx &&
+ m_sibling_idx == rhs.m_sibling_idx &&
+ m_abbr_idx == rhs.m_abbr_idx && m_has_children == rhs.m_has_children &&
+ m_tag == rhs.m_tag;
+}
+
+bool DWARFDebugInfoEntry::operator!=(const DWARFDebugInfoEntry &rhs) const {
+ return !(*this == rhs);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
new file mode 100644
index 000000000000..1e7b5f27642d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
@@ -0,0 +1,185 @@
+//===-- DWARFDebugInfoEntry.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFDebugInfoEntry_h_
+#define SymbolFileDWARF_DWARFDebugInfoEntry_h_
+
+#include "SymbolFileDWARF.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include "DWARFAbbreviationDeclaration.h"
+#include "DWARFDebugAbbrev.h"
+#include "DWARFDebugRanges.h"
+#include <map>
+#include <set>
+#include <vector>
+
+class DWARFDeclContext;
+
+#define DIE_SIBLING_IDX_BITSIZE 31
+
+class DWARFDebugInfoEntry {
+public:
+ typedef std::vector<DWARFDebugInfoEntry> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+ DWARFDebugInfoEntry()
+ : m_offset(DW_INVALID_OFFSET), m_parent_idx(0), m_sibling_idx(0),
+ m_has_children(false), m_abbr_idx(0), m_tag(0) {}
+
+ explicit operator bool() const { return m_offset != DW_INVALID_OFFSET; }
+ bool operator==(const DWARFDebugInfoEntry &rhs) const;
+ bool operator!=(const DWARFDebugInfoEntry &rhs) const;
+
+ void BuildAddressRangeTable(const DWARFUnit *cu,
+ DWARFDebugAranges *debug_aranges) const;
+
+ void BuildFunctionAddressRangeTable(const DWARFUnit *cu,
+ DWARFDebugAranges *debug_aranges) const;
+
+ bool Extract(const lldb_private::DWARFDataExtractor &data,
+ const DWARFUnit *cu, lldb::offset_t *offset_ptr);
+
+ bool LookupAddress(const dw_addr_t address, const DWARFUnit *cu,
+ DWARFDebugInfoEntry **function_die,
+ DWARFDebugInfoEntry **block_die);
+
+ size_t GetAttributes(const DWARFUnit *cu,
+ DWARFAttributes &attrs,
+ uint32_t curr_depth = 0)
+ const; // "curr_depth" for internal use only, don't set this yourself!!!
+
+ dw_offset_t
+ GetAttributeValue(const DWARFUnit *cu, const dw_attr_t attr,
+ DWARFFormValue &formValue,
+ dw_offset_t *end_attr_offset_ptr = nullptr,
+ bool check_specification_or_abstract_origin = false) const;
+
+ const char *GetAttributeValueAsString(
+ const DWARFUnit *cu, const dw_attr_t attr, const char *fail_value,
+ bool check_specification_or_abstract_origin = false) const;
+
+ uint64_t GetAttributeValueAsUnsigned(
+ const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value,
+ bool check_specification_or_abstract_origin = false) const;
+
+ DWARFDIE GetAttributeValueAsReference(
+ const DWARFUnit *cu, const dw_attr_t attr,
+ bool check_specification_or_abstract_origin = false) const;
+
+ uint64_t GetAttributeValueAsAddress(
+ const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value,
+ bool check_specification_or_abstract_origin = false) const;
+
+ dw_addr_t
+ GetAttributeHighPC(const DWARFUnit *cu, dw_addr_t lo_pc, uint64_t fail_value,
+ bool check_specification_or_abstract_origin = false) const;
+
+ bool GetAttributeAddressRange(
+ const DWARFUnit *cu, dw_addr_t &lo_pc, dw_addr_t &hi_pc,
+ uint64_t fail_value,
+ bool check_specification_or_abstract_origin = false) const;
+
+ size_t GetAttributeAddressRanges(
+ const DWARFUnit *cu, DWARFRangeList &ranges, bool check_hi_lo_pc,
+ bool check_specification_or_abstract_origin = false) const;
+
+ const char *GetName(const DWARFUnit *cu) const;
+
+ const char *GetMangledName(const DWARFUnit *cu,
+ bool substitute_name_allowed = true) const;
+
+ const char *GetPubname(const DWARFUnit *cu) const;
+
+ const char *GetQualifiedName(DWARFUnit *cu, std::string &storage) const;
+
+ const char *GetQualifiedName(DWARFUnit *cu, const DWARFAttributes &attributes,
+ std::string &storage) const;
+
+ void Dump(const DWARFUnit *cu, lldb_private::Stream &s,
+ uint32_t recurse_depth) const;
+
+ static void
+ DumpAttribute(const DWARFUnit *cu,
+ const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr, lldb_private::Stream &s,
+ dw_attr_t attr, DWARFFormValue &form_value);
+
+ bool GetDIENamesAndRanges(
+ const DWARFUnit *cu, const char *&name, const char *&mangled,
+ DWARFRangeList &rangeList, int &decl_file, int &decl_line,
+ int &decl_column, int &call_file, int &call_line, int &call_column,
+ lldb_private::DWARFExpression *frame_base = nullptr) const;
+
+ const DWARFAbbreviationDeclaration *
+ GetAbbreviationDeclarationPtr(const DWARFUnit *cu) const;
+
+ lldb::offset_t GetFirstAttributeOffset() const;
+
+ dw_tag_t Tag() const { return m_tag; }
+
+ bool IsNULL() const { return m_abbr_idx == 0; }
+
+ dw_offset_t GetOffset() const { return m_offset; }
+
+ bool HasChildren() const { return m_has_children; }
+
+ void SetHasChildren(bool b) { m_has_children = b; }
+
+ // We know we are kept in a vector of contiguous entries, so we know
+ // our parent will be some index behind "this".
+ DWARFDebugInfoEntry *GetParent() {
+ return m_parent_idx > 0 ? this - m_parent_idx : nullptr;
+ }
+ const DWARFDebugInfoEntry *GetParent() const {
+ return m_parent_idx > 0 ? this - m_parent_idx : nullptr;
+ }
+ // We know we are kept in a vector of contiguous entries, so we know
+ // our sibling will be some index after "this".
+ DWARFDebugInfoEntry *GetSibling() {
+ return m_sibling_idx > 0 ? this + m_sibling_idx : nullptr;
+ }
+ const DWARFDebugInfoEntry *GetSibling() const {
+ return m_sibling_idx > 0 ? this + m_sibling_idx : nullptr;
+ }
+ // We know we are kept in a vector of contiguous entries, so we know
+ // we don't need to store our child pointer, if we have a child it will
+ // be the next entry in the list...
+ DWARFDebugInfoEntry *GetFirstChild() {
+ return HasChildren() ? this + 1 : nullptr;
+ }
+ const DWARFDebugInfoEntry *GetFirstChild() const {
+ return HasChildren() ? this + 1 : nullptr;
+ }
+
+ void GetDWARFDeclContext(DWARFUnit *cu,
+ DWARFDeclContext &dwarf_decl_ctx) const;
+
+ DWARFDIE GetParentDeclContextDIE(DWARFUnit *cu) const;
+ DWARFDIE GetParentDeclContextDIE(DWARFUnit *cu,
+ const DWARFAttributes &attributes) const;
+
+ void SetSiblingIndex(uint32_t idx) { m_sibling_idx = idx; }
+ void SetParentIndex(uint32_t idx) { m_parent_idx = idx; }
+
+protected:
+ dw_offset_t m_offset; // Offset within the .debug_info/.debug_types
+ uint32_t m_parent_idx; // How many to subtract from "this" to get the parent.
+ // If zero this die has no parent
+ uint32_t m_sibling_idx : 31, // How many to add to "this" to get the sibling.
+ // If it is zero, then the DIE doesn't have children, or the
+ // DWARF claimed it had children but the DIE only contained
+ // a single NULL terminating child.
+ m_has_children : 1;
+ uint16_t m_abbr_idx;
+ uint16_t m_tag; // A copy of the DW_TAG value so we don't have to go through
+ // the compile unit abbrev table
+};
+
+#endif // SymbolFileDWARF_DWARFDebugInfoEntry_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp
new file mode 100644
index 000000000000..953089fee22b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp
@@ -0,0 +1,1038 @@
+//===-- DWARFDebugLine.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFDebugLine.h"
+
+//#define ENABLE_DEBUG_PRINTF // DO NOT LEAVE THIS DEFINED: DEBUG ONLY!!!
+#include <assert.h>
+
+#include <memory>
+
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Timer.h"
+
+#include "DWARFUnit.h"
+#include "LogChannelDWARF.h"
+#include "SymbolFileDWARF.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace std;
+
+// Parse
+//
+// Parse all information in the debug_line_data into an internal
+// representation.
+void DWARFDebugLine::Parse(const DWARFDataExtractor &debug_line_data) {
+ m_lineTableMap.clear();
+ lldb::offset_t offset = 0;
+ LineTable::shared_ptr line_table_sp(new LineTable);
+ while (debug_line_data.ValidOffset(offset)) {
+ const lldb::offset_t debug_line_offset = offset;
+
+ if (line_table_sp.get() == nullptr)
+ break;
+
+ if (ParseStatementTable(debug_line_data, &offset, line_table_sp.get(), nullptr)) {
+ // Make sure we don't don't loop infinitely
+ if (offset <= debug_line_offset)
+ break;
+ // DEBUG_PRINTF("m_lineTableMap[0x%8.8x] = line_table_sp\n",
+ // debug_line_offset);
+ m_lineTableMap[debug_line_offset] = line_table_sp;
+ line_table_sp = std::make_shared<LineTable>();
+ } else
+ ++offset; // Try next byte in line table
+ }
+}
+
+void DWARFDebugLine::ParseIfNeeded(const DWARFDataExtractor &debug_line_data) {
+ if (m_lineTableMap.empty())
+ Parse(debug_line_data);
+}
+
+// DWARFDebugLine::GetLineTable
+DWARFDebugLine::LineTable::shared_ptr
+DWARFDebugLine::GetLineTable(const dw_offset_t offset) const {
+ DWARFDebugLine::LineTable::shared_ptr line_table_shared_ptr;
+ LineTableConstIter pos = m_lineTableMap.find(offset);
+ if (pos != m_lineTableMap.end())
+ line_table_shared_ptr = pos->second;
+ return line_table_shared_ptr;
+}
+
+// Parse
+//
+// Parse the entire line table contents calling callback each time a new
+// prologue is parsed and every time a new row is to be added to the line
+// table.
+void DWARFDebugLine::Parse(const DWARFDataExtractor &debug_line_data,
+ DWARFDebugLine::State::Callback callback,
+ void *userData) {
+ lldb::offset_t offset = 0;
+ if (debug_line_data.ValidOffset(offset)) {
+ if (!ParseStatementTable(debug_line_data, &offset, callback, userData, nullptr))
+ ++offset; // Skip to next byte in .debug_line section
+ }
+}
+
+namespace {
+struct EntryDescriptor {
+ dw_sleb128_t code;
+ dw_sleb128_t form;
+};
+
+static std::vector<EntryDescriptor>
+ReadDescriptors(const DWARFDataExtractor &debug_line_data,
+ lldb::offset_t *offset_ptr) {
+ std::vector<EntryDescriptor> ret;
+ uint8_t n = debug_line_data.GetU8(offset_ptr);
+ for (uint8_t i = 0; i < n; ++i) {
+ EntryDescriptor ent;
+ ent.code = debug_line_data.GetULEB128(offset_ptr);
+ ent.form = debug_line_data.GetULEB128(offset_ptr);
+ ret.push_back(ent);
+ }
+ return ret;
+}
+} // namespace
+
+// DWARFDebugLine::ParsePrologue
+bool DWARFDebugLine::ParsePrologue(const DWARFDataExtractor &debug_line_data,
+ lldb::offset_t *offset_ptr,
+ Prologue *prologue, DWARFUnit *dwarf_cu) {
+ const lldb::offset_t prologue_offset = *offset_ptr;
+
+ // DEBUG_PRINTF("0x%8.8x: ParsePrologue()\n", *offset_ptr);
+
+ prologue->Clear();
+ uint32_t i;
+ const char *s;
+ prologue->total_length = debug_line_data.GetDWARFInitialLength(offset_ptr);
+ prologue->version = debug_line_data.GetU16(offset_ptr);
+ if (prologue->version < 2 || prologue->version > 5)
+ return false;
+
+ if (prologue->version >= 5) {
+ prologue->address_size = debug_line_data.GetU8(offset_ptr);
+ prologue->segment_selector_size = debug_line_data.GetU8(offset_ptr);
+ }
+
+ prologue->prologue_length = debug_line_data.GetDWARFOffset(offset_ptr);
+ const lldb::offset_t end_prologue_offset =
+ prologue->prologue_length + *offset_ptr;
+ prologue->min_inst_length = debug_line_data.GetU8(offset_ptr);
+ if (prologue->version >= 4)
+ prologue->maximum_operations_per_instruction =
+ debug_line_data.GetU8(offset_ptr);
+ else
+ prologue->maximum_operations_per_instruction = 1;
+ prologue->default_is_stmt = debug_line_data.GetU8(offset_ptr);
+ prologue->line_base = debug_line_data.GetU8(offset_ptr);
+ prologue->line_range = debug_line_data.GetU8(offset_ptr);
+ prologue->opcode_base = debug_line_data.GetU8(offset_ptr);
+
+ prologue->standard_opcode_lengths.reserve(prologue->opcode_base - 1);
+
+ for (i = 1; i < prologue->opcode_base; ++i) {
+ uint8_t op_len = debug_line_data.GetU8(offset_ptr);
+ prologue->standard_opcode_lengths.push_back(op_len);
+ }
+
+ if (prologue->version >= 5) {
+ std::vector<EntryDescriptor> dirEntryFormatV =
+ ReadDescriptors(debug_line_data, offset_ptr);
+ uint8_t dirCount = debug_line_data.GetULEB128(offset_ptr);
+ for (int i = 0; i < dirCount; ++i) {
+ for (EntryDescriptor &ent : dirEntryFormatV) {
+ DWARFFormValue value(dwarf_cu, ent.form);
+ if (ent.code != DW_LNCT_path) {
+ if (!value.SkipValue(debug_line_data, offset_ptr))
+ return false;
+ continue;
+ }
+
+ if (!value.ExtractValue(debug_line_data, offset_ptr))
+ return false;
+ prologue->include_directories.push_back(value.AsCString());
+ }
+ }
+
+ std::vector<EntryDescriptor> filesEntryFormatV =
+ ReadDescriptors(debug_line_data, offset_ptr);
+ llvm::DenseSet<std::pair<uint64_t, uint64_t>> seen;
+ uint8_t n = debug_line_data.GetULEB128(offset_ptr);
+ for (int i = 0; i < n; ++i) {
+ FileNameEntry entry;
+ for (EntryDescriptor &ent : filesEntryFormatV) {
+ DWARFFormValue value(dwarf_cu, ent.form);
+ if (!value.ExtractValue(debug_line_data, offset_ptr))
+ return false;
+
+ switch (ent.code) {
+ case DW_LNCT_path:
+ entry.name = value.AsCString();
+ break;
+ case DW_LNCT_directory_index:
+ entry.dir_idx = value.Unsigned();
+ break;
+ case DW_LNCT_timestamp:
+ entry.mod_time = value.Unsigned();
+ break;
+ case DW_LNCT_size:
+ entry.length = value.Unsigned();
+ break;
+ case DW_LNCT_MD5:
+ assert(value.Unsigned() == 16);
+ std::uninitialized_copy_n(value.BlockData(), 16,
+ entry.checksum.Bytes.begin());
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (seen.insert(entry.checksum.words()).second)
+ prologue->file_names.push_back(entry);
+ }
+ } else {
+ while (*offset_ptr < end_prologue_offset) {
+ s = debug_line_data.GetCStr(offset_ptr);
+ if (s && s[0])
+ prologue->include_directories.push_back(s);
+ else
+ break;
+ }
+
+ while (*offset_ptr < end_prologue_offset) {
+ const char *name = debug_line_data.GetCStr(offset_ptr);
+ if (name && name[0]) {
+ FileNameEntry fileEntry;
+ fileEntry.name = name;
+ fileEntry.dir_idx = debug_line_data.GetULEB128(offset_ptr);
+ fileEntry.mod_time = debug_line_data.GetULEB128(offset_ptr);
+ fileEntry.length = debug_line_data.GetULEB128(offset_ptr);
+ prologue->file_names.push_back(fileEntry);
+ } else
+ break;
+ }
+ }
+
+ // XXX GNU as is broken for 64-Bit DWARF
+ if (*offset_ptr != end_prologue_offset) {
+ Host::SystemLog(Host::eSystemLogWarning,
+ "warning: parsing line table prologue at 0x%8.8" PRIx64
+ " should have ended at 0x%8.8" PRIx64
+ " but it ended at 0x%8.8" PRIx64 "\n",
+ prologue_offset, end_prologue_offset, *offset_ptr);
+ }
+ return end_prologue_offset;
+}
+
+bool DWARFDebugLine::ParseSupportFiles(
+ const lldb::ModuleSP &module_sp, const DWARFDataExtractor &debug_line_data,
+ dw_offset_t stmt_list, FileSpecList &support_files, DWARFUnit *dwarf_cu) {
+ lldb::offset_t offset = stmt_list;
+
+ Prologue prologue;
+ if (!ParsePrologue(debug_line_data, &offset, &prologue, dwarf_cu)) {
+ Host::SystemLog(Host::eSystemLogError, "error: parsing line table prologue "
+ "at 0x%8.8x (parsing ended around "
+ "0x%8.8" PRIx64 "\n",
+ stmt_list, offset);
+ return false;
+ }
+
+ FileSpec file_spec;
+ std::string remapped_file;
+
+ for (uint32_t file_idx = 1;
+ prologue.GetFile(file_idx, dwarf_cu->GetCompilationDirectory(),
+ dwarf_cu->GetPathStyle(), file_spec);
+ ++file_idx) {
+ if (module_sp->RemapSourceFile(file_spec.GetPath(), remapped_file))
+ file_spec.SetFile(remapped_file, FileSpec::Style::native);
+ support_files.Append(file_spec);
+ }
+ return true;
+}
+
+// ParseStatementTable
+//
+// Parse a single line table (prologue and all rows) and call the callback
+// function once for the prologue (row in state will be zero) and each time a
+// row is to be added to the line table.
+bool DWARFDebugLine::ParseStatementTable(
+ const DWARFDataExtractor &debug_line_data, lldb::offset_t *offset_ptr,
+ DWARFDebugLine::State::Callback callback, void *userData, DWARFUnit *dwarf_cu) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_LINE));
+ Prologue::shared_ptr prologue(new Prologue());
+
+ const dw_offset_t debug_line_offset = *offset_ptr;
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat, "DWARFDebugLine::ParseStatementTable (.debug_line[0x%8.8x])",
+ debug_line_offset);
+
+ if (!ParsePrologue(debug_line_data, offset_ptr, prologue.get(), dwarf_cu)) {
+ if (log)
+ log->Error("failed to parse DWARF line table prologue");
+ // Restore our offset and return false to indicate failure!
+ *offset_ptr = debug_line_offset;
+ return false;
+ }
+
+ if (log)
+ prologue->Dump(log);
+
+ const dw_offset_t end_offset =
+ debug_line_offset + prologue->total_length +
+ (debug_line_data.GetDWARFSizeofInitialLength());
+
+ State state(prologue, log, callback, userData);
+
+ while (*offset_ptr < end_offset) {
+ // DEBUG_PRINTF("0x%8.8x: ", *offset_ptr);
+ uint8_t opcode = debug_line_data.GetU8(offset_ptr);
+
+ if (opcode == 0) {
+ // Extended Opcodes always start with a zero opcode followed by a uleb128
+ // length so you can skip ones you don't know about
+ lldb::offset_t ext_offset = *offset_ptr;
+ dw_uleb128_t len = debug_line_data.GetULEB128(offset_ptr);
+ dw_offset_t arg_size = len - (*offset_ptr - ext_offset);
+
+ // DEBUG_PRINTF("Extended: <%2u> ", len);
+ uint8_t sub_opcode = debug_line_data.GetU8(offset_ptr);
+ switch (sub_opcode) {
+ case DW_LNE_end_sequence:
+ // Set the end_sequence register of the state machine to true and
+ // append a row to the matrix using the current values of the state-
+ // machine registers. Then reset the registers to the initial values
+ // specified above. Every statement program sequence must end with a
+ // DW_LNE_end_sequence instruction which creates a row whose address is
+ // that of the byte after the last target machine instruction of the
+ // sequence.
+ state.end_sequence = true;
+ state.AppendRowToMatrix(*offset_ptr);
+ state.Reset();
+ break;
+
+ case DW_LNE_set_address:
+ // Takes a single relocatable address as an operand. The size of the
+ // operand is the size appropriate to hold an address on the target
+ // machine. Set the address register to the value given by the
+ // relocatable address. All of the other statement program opcodes that
+ // affect the address register add a delta to it. This instruction
+ // stores a relocatable value into it instead.
+ if (arg_size == 4)
+ state.address = debug_line_data.GetU32(offset_ptr);
+ else // arg_size == 8
+ state.address = debug_line_data.GetU64(offset_ptr);
+ break;
+
+ case DW_LNE_define_file:
+ // Takes 4 arguments. The first is a null terminated string containing
+ // a source file name. The second is an unsigned LEB128 number
+ // representing the directory index of the directory in which the file
+ // was found. The third is an unsigned LEB128 number representing the
+ // time of last modification of the file. The fourth is an unsigned
+ // LEB128 number representing the length in bytes of the file. The time
+ // and length fields may contain LEB128(0) if the information is not
+ // available.
+ //
+ // The directory index represents an entry in the include_directories
+ // section of the statement program prologue. The index is LEB128(0) if
+ // the file was found in the current directory of the compilation,
+ // LEB128(1) if it was found in the first directory in the
+ // include_directories section, and so on. The directory index is
+ // ignored for file names that represent full path names.
+ //
+ // The files are numbered, starting at 1, in the order in which they
+ // appear; the names in the prologue come before names defined by the
+ // DW_LNE_define_file instruction. These numbers are used in the file
+ // register of the state machine.
+ {
+ FileNameEntry fileEntry;
+ fileEntry.name = debug_line_data.GetCStr(offset_ptr);
+ fileEntry.dir_idx = debug_line_data.GetULEB128(offset_ptr);
+ fileEntry.mod_time = debug_line_data.GetULEB128(offset_ptr);
+ fileEntry.length = debug_line_data.GetULEB128(offset_ptr);
+ state.prologue->file_names.push_back(fileEntry);
+ }
+ break;
+
+ default:
+ // Length doesn't include the zero opcode byte or the length itself,
+ // but it does include the sub_opcode, so we have to adjust for that
+ // below
+ (*offset_ptr) += arg_size;
+ break;
+ }
+ } else if (opcode < prologue->opcode_base) {
+ switch (opcode) {
+ // Standard Opcodes
+ case DW_LNS_copy:
+ // Takes no arguments. Append a row to the matrix using the current
+ // values of the state-machine registers. Then set the basic_block
+ // register to false.
+ state.AppendRowToMatrix(*offset_ptr);
+ break;
+
+ case DW_LNS_advance_pc:
+ // Takes a single unsigned LEB128 operand, multiplies it by the
+ // min_inst_length field of the prologue, and adds the result to the
+ // address register of the state machine.
+ state.address +=
+ debug_line_data.GetULEB128(offset_ptr) * prologue->min_inst_length;
+ break;
+
+ case DW_LNS_advance_line:
+ // Takes a single signed LEB128 operand and adds that value to the line
+ // register of the state machine.
+ state.line += debug_line_data.GetSLEB128(offset_ptr);
+ break;
+
+ case DW_LNS_set_file:
+ // Takes a single unsigned LEB128 operand and stores it in the file
+ // register of the state machine.
+ state.file = debug_line_data.GetULEB128(offset_ptr);
+ break;
+
+ case DW_LNS_set_column:
+ // Takes a single unsigned LEB128 operand and stores it in the column
+ // register of the state machine.
+ state.column = debug_line_data.GetULEB128(offset_ptr);
+ break;
+
+ case DW_LNS_negate_stmt:
+ // Takes no arguments. Set the is_stmt register of the state machine to
+ // the logical negation of its current value.
+ state.is_stmt = !state.is_stmt;
+ break;
+
+ case DW_LNS_set_basic_block:
+ // Takes no arguments. Set the basic_block register of the state
+ // machine to true
+ state.basic_block = true;
+ break;
+
+ case DW_LNS_const_add_pc:
+ // Takes no arguments. Add to the address register of the state machine
+ // the address increment value corresponding to special opcode 255. The
+ // motivation for DW_LNS_const_add_pc is this: when the statement
+ // program needs to advance the address by a small amount, it can use a
+ // single special opcode, which occupies a single byte. When it needs
+ // to advance the address by up to twice the range of the last special
+ // opcode, it can use DW_LNS_const_add_pc followed by a special opcode,
+ // for a total of two bytes. Only if it needs to advance the address by
+ // more than twice that range will it need to use both
+ // DW_LNS_advance_pc and a special opcode, requiring three or more
+ // bytes.
+ {
+ uint8_t adjust_opcode = 255 - prologue->opcode_base;
+ dw_addr_t addr_offset = (adjust_opcode / prologue->line_range) *
+ prologue->min_inst_length;
+ state.address += addr_offset;
+ }
+ break;
+
+ case DW_LNS_fixed_advance_pc:
+ // Takes a single uhalf operand. Add to the address register of the
+ // state machine the value of the (unencoded) operand. This is the only
+ // extended opcode that takes an argument that is not a variable length
+ // number. The motivation for DW_LNS_fixed_advance_pc is this: existing
+ // assemblers cannot emit DW_LNS_advance_pc or special opcodes because
+ // they cannot encode LEB128 numbers or judge when the computation of a
+ // special opcode overflows and requires the use of DW_LNS_advance_pc.
+ // Such assemblers, however, can use DW_LNS_fixed_advance_pc instead,
+ // sacrificing compression.
+ state.address += debug_line_data.GetU16(offset_ptr);
+ break;
+
+ case DW_LNS_set_prologue_end:
+ // Takes no arguments. Set the prologue_end register of the state
+ // machine to true
+ state.prologue_end = true;
+ break;
+
+ case DW_LNS_set_epilogue_begin:
+ // Takes no arguments. Set the basic_block register of the state
+ // machine to true
+ state.epilogue_begin = true;
+ break;
+
+ case DW_LNS_set_isa:
+ // Takes a single unsigned LEB128 operand and stores it in the column
+ // register of the state machine.
+ state.isa = debug_line_data.GetULEB128(offset_ptr);
+ break;
+
+ default:
+ // Handle any unknown standard opcodes here. We know the lengths of
+ // such opcodes because they are specified in the prologue as a
+ // multiple of LEB128 operands for each opcode.
+ {
+ uint8_t i;
+ assert(static_cast<size_t>(opcode - 1) <
+ prologue->standard_opcode_lengths.size());
+ const uint8_t opcode_length =
+ prologue->standard_opcode_lengths[opcode - 1];
+ for (i = 0; i < opcode_length; ++i)
+ debug_line_data.Skip_LEB128(offset_ptr);
+ }
+ break;
+ }
+ } else {
+ // Special Opcodes
+
+ // A special opcode value is chosen based on the amount that needs
+ // to be added to the line and address registers. The maximum line
+ // increment for a special opcode is the value of the line_base field in
+ // the header, plus the value of the line_range field, minus 1 (line base
+ // + line range - 1). If the desired line increment is greater than the
+ // maximum line increment, a standard opcode must be used instead of a
+ // special opcode. The "address advance" is calculated by dividing the
+ // desired address increment by the minimum_instruction_length field from
+ // the header. The special opcode is then calculated using the following
+ // formula:
+ //
+ // opcode = (desired line increment - line_base) + (line_range * address
+ // advance) + opcode_base
+ //
+ // If the resulting opcode is greater than 255, a standard opcode must be
+ // used instead.
+ //
+ // To decode a special opcode, subtract the opcode_base from the opcode
+ // itself to give the adjusted opcode. The amount to increment the
+ // address register is the result of the adjusted opcode divided by the
+ // line_range multiplied by the minimum_instruction_length field from the
+ // header. That is:
+ //
+ // address increment = (adjusted opcode / line_range) *
+ // minimum_instruction_length
+ //
+ // The amount to increment the line register is the line_base plus the
+ // result of the adjusted opcode modulo the line_range. That is:
+ //
+ // line increment = line_base + (adjusted opcode % line_range)
+
+ uint8_t adjust_opcode = opcode - prologue->opcode_base;
+ dw_addr_t addr_offset =
+ (adjust_opcode / prologue->line_range) * prologue->min_inst_length;
+ int32_t line_offset =
+ prologue->line_base + (adjust_opcode % prologue->line_range);
+ state.line += line_offset;
+ state.address += addr_offset;
+ state.AppendRowToMatrix(*offset_ptr);
+ }
+ }
+
+ state.Finalize(*offset_ptr);
+
+ return end_offset;
+}
+
+// ParseStatementTableCallback
+static void ParseStatementTableCallback(dw_offset_t offset,
+ const DWARFDebugLine::State &state,
+ void *userData) {
+ DWARFDebugLine::LineTable *line_table = (DWARFDebugLine::LineTable *)userData;
+ if (state.row == DWARFDebugLine::State::StartParsingLineTable) {
+ // Just started parsing the line table, so lets keep a reference to the
+ // prologue using the supplied shared pointer
+ line_table->prologue = state.prologue;
+ } else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) {
+ // Done parsing line table, nothing to do for the cleanup
+ } else {
+ // We have a new row, lets append it
+ line_table->AppendRow(state);
+ }
+}
+
+// ParseStatementTable
+//
+// Parse a line table at offset and populate the LineTable class with the
+// prologue and all rows.
+bool DWARFDebugLine::ParseStatementTable(
+ const DWARFDataExtractor &debug_line_data, lldb::offset_t *offset_ptr,
+ LineTable *line_table, DWARFUnit *dwarf_cu) {
+ return ParseStatementTable(debug_line_data, offset_ptr,
+ ParseStatementTableCallback, line_table, dwarf_cu);
+}
+
+inline bool DWARFDebugLine::Prologue::IsValid() const {
+ return SymbolFileDWARF::SupportedVersion(version);
+}
+
+// DWARFDebugLine::Prologue::Dump
+void DWARFDebugLine::Prologue::Dump(Log *log) {
+ uint32_t i;
+
+ log->Printf("Line table prologue:");
+ log->Printf(" total_length: 0x%8.8x", total_length);
+ log->Printf(" version: %u", version);
+ log->Printf("prologue_length: 0x%8.8x", prologue_length);
+ log->Printf("min_inst_length: %u", min_inst_length);
+ log->Printf("default_is_stmt: %u", default_is_stmt);
+ log->Printf(" line_base: %i", line_base);
+ log->Printf(" line_range: %u", line_range);
+ log->Printf(" opcode_base: %u", opcode_base);
+
+ for (i = 0; i < standard_opcode_lengths.size(); ++i) {
+ log->Printf("standard_opcode_lengths[%s] = %u", DW_LNS_value_to_name(i + 1),
+ standard_opcode_lengths[i]);
+ }
+
+ if (!include_directories.empty()) {
+ for (i = 0; i < include_directories.size(); ++i) {
+ log->Printf("include_directories[%3u] = '%s'", i + 1,
+ include_directories[i]);
+ }
+ }
+
+ if (!file_names.empty()) {
+ log->PutCString(" Dir Mod Time File Len File Name");
+ log->PutCString(" ---- ---------- ---------- "
+ "---------------------------");
+ for (i = 0; i < file_names.size(); ++i) {
+ const FileNameEntry &fileEntry = file_names[i];
+ log->Printf("file_names[%3u] %4u 0x%8.8x 0x%8.8x %s", i + 1,
+ fileEntry.dir_idx, fileEntry.mod_time, fileEntry.length,
+ fileEntry.name);
+ }
+ }
+}
+
+// DWARFDebugLine::ParsePrologue::Append
+//
+// Append the contents of the prologue to the binary stream buffer
+// void
+// DWARFDebugLine::Prologue::Append(BinaryStreamBuf& buff) const
+//{
+// uint32_t i;
+//
+// buff.Append32(total_length);
+// buff.Append16(version);
+// buff.Append32(prologue_length);
+// buff.Append8(min_inst_length);
+// buff.Append8(default_is_stmt);
+// buff.Append8(line_base);
+// buff.Append8(line_range);
+// buff.Append8(opcode_base);
+//
+// for (i=0; i<standard_opcode_lengths.size(); ++i)
+// buff.Append8(standard_opcode_lengths[i]);
+//
+// for (i=0; i<include_directories.size(); ++i)
+// buff.AppendCStr(include_directories[i].c_str());
+// buff.Append8(0); // Terminate the include directory section with empty
+// string
+//
+// for (i=0; i<file_names.size(); ++i)
+// {
+// buff.AppendCStr(file_names[i].name.c_str());
+// buff.Append32_as_ULEB128(file_names[i].dir_idx);
+// buff.Append32_as_ULEB128(file_names[i].mod_time);
+// buff.Append32_as_ULEB128(file_names[i].length);
+// }
+// buff.Append8(0); // Terminate the file names section with empty string
+//}
+
+bool DWARFDebugLine::Prologue::GetFile(uint32_t file_idx,
+ const FileSpec &comp_dir,
+ FileSpec::Style style,
+ FileSpec &file) const {
+ uint32_t idx = file_idx - 1; // File indexes are 1 based...
+ if (idx < file_names.size()) {
+ file.SetFile(file_names[idx].name, style);
+ if (file.IsRelative()) {
+ if (file_names[idx].dir_idx > 0) {
+ const uint32_t dir_idx = file_names[idx].dir_idx - 1;
+ if (dir_idx < include_directories.size()) {
+ file.PrependPathComponent(include_directories[dir_idx]);
+ if (!file.IsRelative())
+ return true;
+ }
+ }
+
+ if (comp_dir)
+ file.PrependPathComponent(comp_dir);
+ }
+ return true;
+ }
+ return false;
+}
+
+void DWARFDebugLine::LineTable::AppendRow(const DWARFDebugLine::Row &state) {
+ rows.push_back(state);
+}
+
+// Compare function for the binary search in
+// DWARFDebugLine::LineTable::LookupAddress()
+static bool FindMatchingAddress(const DWARFDebugLine::Row &row1,
+ const DWARFDebugLine::Row &row2) {
+ return row1.address < row2.address;
+}
+
+// DWARFDebugLine::LineTable::LookupAddress
+uint32_t DWARFDebugLine::LineTable::LookupAddress(dw_addr_t address,
+ dw_addr_t cu_high_pc) const {
+ uint32_t index = UINT32_MAX;
+ if (!rows.empty()) {
+ // Use the lower_bound algorithm to perform a binary search since we know
+ // that our line table data is ordered by address.
+ DWARFDebugLine::Row row;
+ row.address = address;
+ Row::const_iterator begin_pos = rows.begin();
+ Row::const_iterator end_pos = rows.end();
+ Row::const_iterator pos =
+ lower_bound(begin_pos, end_pos, row, FindMatchingAddress);
+ if (pos == end_pos) {
+ if (address < cu_high_pc)
+ return rows.size() - 1;
+ } else {
+ // Rely on fact that we are using a std::vector and we can do pointer
+ // arithmetic to find the row index (which will be one less that what we
+ // found since it will find the first position after the current address)
+ // since std::vector iterators are just pointers to the container type.
+ index = pos - begin_pos;
+ if (pos->address > address) {
+ if (index > 0)
+ --index;
+ else
+ index = UINT32_MAX;
+ }
+ }
+ }
+ return index; // Failed to find address
+}
+
+// DWARFDebugLine::Row::Row
+DWARFDebugLine::Row::Row(bool default_is_stmt)
+ : address(0), line(1), column(0), file(1), is_stmt(default_is_stmt),
+ basic_block(false), end_sequence(false), prologue_end(false),
+ epilogue_begin(false), isa(0) {}
+
+// Called after a row is appended to the matrix
+void DWARFDebugLine::Row::PostAppend() {
+ basic_block = false;
+ prologue_end = false;
+ epilogue_begin = false;
+}
+
+// DWARFDebugLine::Row::Reset
+void DWARFDebugLine::Row::Reset(bool default_is_stmt) {
+ address = 0;
+ line = 1;
+ column = 0;
+ file = 1;
+ is_stmt = default_is_stmt;
+ basic_block = false;
+ end_sequence = false;
+ prologue_end = false;
+ epilogue_begin = false;
+ isa = 0;
+}
+// DWARFDebugLine::Row::Dump
+void DWARFDebugLine::Row::Dump(Log *log) const {
+ log->Printf("0x%16.16" PRIx64 " %6u %6u %6u %3u %s%s%s%s%s", address, line,
+ column, file, isa, is_stmt ? " is_stmt" : "",
+ basic_block ? " basic_block" : "",
+ prologue_end ? " prologue_end" : "",
+ epilogue_begin ? " epilogue_begin" : "",
+ end_sequence ? " end_sequence" : "");
+}
+
+// Compare function LineTable structures
+static bool AddressLessThan(const DWARFDebugLine::Row &a,
+ const DWARFDebugLine::Row &b) {
+ return a.address < b.address;
+}
+
+// Insert a row at the correct address if the addresses can be out of order
+// which can only happen when we are linking a line table that may have had
+// it's contents rearranged.
+void DWARFDebugLine::Row::Insert(Row::collection &state_coll,
+ const Row &state) {
+ // If we don't have anything yet, or if the address of the last state in our
+ // line table is less than the current one, just append the current state
+ if (state_coll.empty() || AddressLessThan(state_coll.back(), state)) {
+ state_coll.push_back(state);
+ } else {
+ // Do a binary search for the correct entry
+ pair<Row::iterator, Row::iterator> range(equal_range(
+ state_coll.begin(), state_coll.end(), state, AddressLessThan));
+
+ // If the addresses are equal, we can safely replace the previous entry
+ // with the current one if the one it is replacing is an end_sequence
+ // entry. We currently always place an extra end sequence when ever we exit
+ // a valid address range for a function in case the functions get
+ // rearranged by optimizations or by order specifications. These extra end
+ // sequences will disappear by getting replaced with valid consecutive
+ // entries within a compile unit if there are no gaps.
+ if (range.first == range.second) {
+ state_coll.insert(range.first, state);
+ } else {
+ if ((distance(range.first, range.second) == 1) &&
+ range.first->end_sequence == true) {
+ *range.first = state;
+ } else {
+ state_coll.insert(range.second, state);
+ }
+ }
+ }
+}
+
+// DWARFDebugLine::State::State
+DWARFDebugLine::State::State(Prologue::shared_ptr &p, Log *l,
+ DWARFDebugLine::State::Callback cb, void *userData)
+ : Row(p->default_is_stmt), prologue(p), log(l), callback(cb),
+ callbackUserData(userData), row(StartParsingLineTable) {
+ // Call the callback with the initial row state of zero for the prologue
+ if (callback)
+ callback(0, *this, callbackUserData);
+}
+
+// DWARFDebugLine::State::Reset
+void DWARFDebugLine::State::Reset() { Row::Reset(prologue->default_is_stmt); }
+
+// DWARFDebugLine::State::AppendRowToMatrix
+void DWARFDebugLine::State::AppendRowToMatrix(dw_offset_t offset) {
+ // Each time we are to add an entry into the line table matrix call the
+ // callback function so that someone can do something with the current state
+ // of the state machine (like build a line table or dump the line table!)
+ if (log) {
+ if (row == 0) {
+ log->PutCString("Address Line Column File ISA Flags");
+ log->PutCString(
+ "------------------ ------ ------ ------ --- -------------");
+ }
+ Dump(log);
+ }
+
+ ++row; // Increase the row number before we call our callback for a real row
+ if (callback)
+ callback(offset, *this, callbackUserData);
+ PostAppend();
+}
+
+// DWARFDebugLine::State::Finalize
+void DWARFDebugLine::State::Finalize(dw_offset_t offset) {
+ // Call the callback with a special row state when we are done parsing a line
+ // table
+ row = DoneParsingLineTable;
+ if (callback)
+ callback(offset, *this, callbackUserData);
+}
+
+// void
+// DWARFDebugLine::AppendLineTableData
+//(
+// const DWARFDebugLine::Prologue* prologue,
+// const DWARFDebugLine::Row::collection& state_coll,
+// const uint32_t addr_size,
+// BinaryStreamBuf &debug_line_data
+//)
+//{
+// if (state_coll.empty())
+// {
+// // We have no entries, just make an empty line table
+// debug_line_data.Append8(0);
+// debug_line_data.Append8(1);
+// debug_line_data.Append8(DW_LNE_end_sequence);
+// }
+// else
+// {
+// DWARFDebugLine::Row::const_iterator pos;
+// Row::const_iterator end = state_coll.end();
+// bool default_is_stmt = prologue->default_is_stmt;
+// const DWARFDebugLine::Row reset_state(default_is_stmt);
+// const DWARFDebugLine::Row* prev_state = &reset_state;
+// const int32_t max_line_increment_for_special_opcode =
+// prologue->MaxLineIncrementForSpecialOpcode();
+// for (pos = state_coll.begin(); pos != end; ++pos)
+// {
+// const DWARFDebugLine::Row& curr_state = *pos;
+// int32_t line_increment = 0;
+// dw_addr_t addr_offset = curr_state.address - prev_state->address;
+// dw_addr_t addr_advance = (addr_offset) / prologue->min_inst_length;
+// line_increment = (int32_t)(curr_state.line - prev_state->line);
+//
+// // If our previous state was the reset state, then let's emit the
+// // address to keep GDB's DWARF parser happy. If we don't start each
+// // sequence with a DW_LNE_set_address opcode, the line table won't
+// // get slid properly in GDB.
+//
+// if (prev_state == &reset_state)
+// {
+// debug_line_data.Append8(0); // Extended opcode
+// debug_line_data.Append32_as_ULEB128(addr_size + 1); // Length of
+// opcode bytes
+// debug_line_data.Append8(DW_LNE_set_address);
+// debug_line_data.AppendMax64(curr_state.address, addr_size);
+// addr_advance = 0;
+// }
+//
+// if (prev_state->file != curr_state.file)
+// {
+// debug_line_data.Append8(DW_LNS_set_file);
+// debug_line_data.Append32_as_ULEB128(curr_state.file);
+// }
+//
+// if (prev_state->column != curr_state.column)
+// {
+// debug_line_data.Append8(DW_LNS_set_column);
+// debug_line_data.Append32_as_ULEB128(curr_state.column);
+// }
+//
+// // Don't do anything fancy if we are at the end of a sequence
+// // as we don't want to push any extra rows since the
+// DW_LNE_end_sequence
+// // will push a row itself!
+// if (curr_state.end_sequence)
+// {
+// if (line_increment != 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_line);
+// debug_line_data.Append32_as_SLEB128(line_increment);
+// }
+//
+// if (addr_advance > 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_pc);
+// debug_line_data.Append32_as_ULEB128(addr_advance);
+// }
+//
+// // Now push the end sequence on!
+// debug_line_data.Append8(0);
+// debug_line_data.Append8(1);
+// debug_line_data.Append8(DW_LNE_end_sequence);
+//
+// prev_state = &reset_state;
+// }
+// else
+// {
+// if (line_increment || addr_advance)
+// {
+// if (line_increment > max_line_increment_for_special_opcode)
+// {
+// debug_line_data.Append8(DW_LNS_advance_line);
+// debug_line_data.Append32_as_SLEB128(line_increment);
+// line_increment = 0;
+// }
+//
+// uint32_t special_opcode = (line_increment >=
+// prologue->line_base) ? ((line_increment -
+// prologue->line_base) + (prologue->line_range * addr_advance)
+// + prologue->opcode_base) : 256;
+// if (special_opcode > 255)
+// {
+// // Both the address and line won't fit in one special
+// opcode
+// // check to see if just the line advance will?
+// uint32_t special_opcode_line = ((line_increment >=
+// prologue->line_base) && (line_increment != 0)) ?
+// ((line_increment - prologue->line_base) +
+// prologue->opcode_base) : 256;
+//
+//
+// if (special_opcode_line > 255)
+// {
+// // Nope, the line advance won't fit by itself, check
+// the address increment by itself
+// uint32_t special_opcode_addr = addr_advance ?
+// ((0 - prologue->line_base) +
+// (prologue->line_range * addr_advance) +
+// prologue->opcode_base) : 256;
+//
+// if (special_opcode_addr > 255)
+// {
+// // Neither the address nor the line will fit in
+// a
+// // special opcode, we must manually enter both
+// then
+// // do a DW_LNS_copy to push a row (special
+// opcode
+// // automatically imply a new row is pushed)
+// if (line_increment != 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_line);
+// debug_line_data.Append32_as_SLEB128(line_increment);
+// }
+//
+// if (addr_advance > 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_pc);
+// debug_line_data.Append32_as_ULEB128(addr_advance);
+// }
+//
+// // Now push a row onto the line table manually
+// debug_line_data.Append8(DW_LNS_copy);
+//
+// }
+// else
+// {
+// // The address increment alone will fit into a
+// special opcode
+// // so modify our line change, then issue a
+// special opcode
+// // for the address increment and it will push a
+// row into the
+// // line table
+// if (line_increment != 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_line);
+// debug_line_data.Append32_as_SLEB128(line_increment);
+// }
+//
+// // Advance of line and address will fit into a
+// single byte special opcode
+// // and this will also push a row onto the line
+// table
+// debug_line_data.Append8(special_opcode_addr);
+// }
+// }
+// else
+// {
+// // The line change alone will fit into a special
+// opcode
+// // so modify our address increment first, then issue
+// a
+// // special opcode for the line change and it will
+// push
+// // a row into the line table
+// if (addr_advance > 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_pc);
+// debug_line_data.Append32_as_ULEB128(addr_advance);
+// }
+//
+// // Advance of line and address will fit into a
+// single byte special opcode
+// // and this will also push a row onto the line table
+// debug_line_data.Append8(special_opcode_line);
+// }
+// }
+// else
+// {
+// // Advance of line and address will fit into a single
+// byte special opcode
+// // and this will also push a row onto the line table
+// debug_line_data.Append8(special_opcode);
+// }
+// }
+// prev_state = &curr_state;
+// }
+// }
+// }
+//}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h
new file mode 100644
index 000000000000..0d236ca686b5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h
@@ -0,0 +1,227 @@
+//===-- DWARFDebugLine.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFDebugLine_h_
+#define SymbolFileDWARF_DWARFDebugLine_h_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/lldb-private.h"
+
+#include "DWARFDataExtractor.h"
+#include "DWARFDefines.h"
+
+#include "llvm/Support/MD5.h"
+
+class DWARFUnit;
+class SymbolFileDWARF;
+
+// DWARFDebugLine
+class DWARFDebugLine {
+public:
+ // FileNameEntry
+ struct FileNameEntry {
+ FileNameEntry()
+ : name(nullptr), dir_idx(0), mod_time(0), length(0), checksum() {}
+
+ const char *name;
+ dw_sleb128_t dir_idx;
+ dw_sleb128_t mod_time;
+ dw_sleb128_t length;
+ llvm::MD5::MD5Result checksum;
+ };
+
+ // Prologue
+ struct Prologue {
+
+ Prologue()
+ : total_length(0), version(0), prologue_length(0), min_inst_length(0),
+ default_is_stmt(0), line_base(0), line_range(0), opcode_base(0),
+ standard_opcode_lengths(), include_directories(), file_names() {}
+
+ typedef std::shared_ptr<Prologue> shared_ptr;
+
+ uint32_t total_length; // The size in bytes of the statement information for
+ // this compilation unit (not including the
+ // total_length field itself).
+ uint16_t
+ version; // Version identifier for the statement information format.
+
+ uint8_t address_size;
+ uint8_t segment_selector_size;
+
+ uint32_t prologue_length; // The number of bytes following the
+ // prologue_length field to the beginning of the
+ // first byte of the statement program itself.
+ uint8_t min_inst_length; // The size in bytes of the smallest target machine
+ // instruction. Statement program opcodes that
+ // alter the address register first multiply their
+ // operands by this value.
+ uint8_t maximum_operations_per_instruction; // New in DWARF4. The maximum
+ // number of individual
+ // operations that may be
+ // encoded in an instruction.
+ uint8_t default_is_stmt; // The initial value of theis_stmtregister.
+ int8_t line_base; // This parameter affects the meaning of the special
+ // opcodes. See below.
+ uint8_t line_range; // This parameter affects the meaning of the special
+ // opcodes. See below.
+ uint8_t opcode_base; // The number assigned to the first special opcode.
+ std::vector<uint8_t> standard_opcode_lengths;
+ std::vector<const char *> include_directories;
+ std::vector<FileNameEntry> file_names;
+
+ int32_t MaxLineIncrementForSpecialOpcode() const {
+ return line_base + (int8_t)line_range - 1;
+ }
+ bool IsValid() const;
+ // void Append(BinaryStreamBuf& buff) const;
+ void Dump(lldb_private::Log *log);
+ void Clear() {
+ total_length = version = prologue_length = min_inst_length = line_base =
+ line_range = opcode_base = 0;
+ line_base = 0;
+ standard_opcode_lengths.clear();
+ include_directories.clear();
+ file_names.clear();
+ }
+ bool GetFile(uint32_t file_idx, const lldb_private::FileSpec &cu_comp_dir,
+ lldb_private::FileSpec::Style style,
+ lldb_private::FileSpec &file) const;
+ };
+
+ // Standard .debug_line state machine structure
+ struct Row {
+ typedef std::vector<Row> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+ Row(bool default_is_stmt = false);
+ virtual ~Row() {}
+ void PostAppend();
+ void Reset(bool default_is_stmt);
+ void Dump(lldb_private::Log *log) const;
+ static void Insert(Row::collection &state_coll, const Row &state);
+
+ dw_addr_t address; // The program-counter value corresponding to a machine
+ // instruction generated by the compiler.
+ uint32_t line; // An unsigned integer indicating a source line number. Lines
+ // are numbered beginning at 1. The compiler may emit the
+ // value 0 in cases where an instruction cannot be attributed
+ // to any source line.
+ uint16_t column; // An unsigned integer indicating a column number within a
+ // source line. Columns are numbered beginning at 1. The
+ // value 0 is reserved to indicate that a statement begins
+ // at the 'left edge' of the line.
+ uint16_t file; // An unsigned integer indicating the identity of the source
+ // file corresponding to a machine instruction.
+ uint8_t is_stmt : 1, // A boolean indicating that the current instruction is
+ // the beginning of a statement.
+ basic_block : 1, // A boolean indicating that the current instruction is
+ // the beginning of a basic block.
+ end_sequence : 1, // A boolean indicating that the current address is
+ // that of the first byte after the end of a sequence
+ // of target machine instructions.
+ prologue_end : 1, // A boolean indicating that the current address is
+ // one (of possibly many) where execution should be
+ // suspended for an entry breakpoint of a function.
+ epilogue_begin : 1; // A boolean indicating that the current address is
+ // one (of possibly many) where execution should be
+ // suspended for an exit breakpoint of a function.
+ uint32_t isa; // An unsigned integer whose value encodes the applicable
+ // instruction set architecture for the current instruction.
+ };
+
+ // LineTable
+ struct LineTable {
+ typedef std::shared_ptr<LineTable> shared_ptr;
+
+ LineTable() : prologue(), rows() {}
+
+ void AppendRow(const DWARFDebugLine::Row &state);
+ void Clear() {
+ prologue.reset();
+ rows.clear();
+ }
+
+ uint32_t LookupAddress(dw_addr_t address, dw_addr_t cu_high_pc) const;
+
+ Prologue::shared_ptr prologue;
+ Row::collection rows;
+ };
+
+ // State
+ struct State : public Row {
+ typedef void (*Callback)(dw_offset_t offset, const State &state,
+ void *userData);
+
+ // Special row codes used when calling the callback
+ enum { StartParsingLineTable = 0, DoneParsingLineTable = -1 };
+
+ State(Prologue::shared_ptr &prologue_sp, lldb_private::Log *log,
+ Callback callback, void *userData);
+
+ void AppendRowToMatrix(dw_offset_t offset);
+
+ void Finalize(dw_offset_t offset);
+
+ void Reset();
+
+ Prologue::shared_ptr prologue;
+ lldb_private::Log *log;
+ Callback callback; // Callback function that gets called each time an entry
+ // is to be added to the matrix
+ void *callbackUserData;
+ int row; // The row number that starts at zero for the prologue, and
+ // increases for each row added to the matrix
+ private:
+ DISALLOW_COPY_AND_ASSIGN(State);
+ };
+
+ static bool
+ ParseSupportFiles(const lldb::ModuleSP &module_sp,
+ const lldb_private::DWARFDataExtractor &debug_line_data,
+ dw_offset_t stmt_list,
+ lldb_private::FileSpecList &support_files,
+ DWARFUnit *dwarf_cu);
+ static bool
+ ParsePrologue(const lldb_private::DWARFDataExtractor &debug_line_data,
+ lldb::offset_t *offset_ptr, Prologue *prologue,
+ DWARFUnit *dwarf_cu = nullptr);
+ static bool
+ ParseStatementTable(const lldb_private::DWARFDataExtractor &debug_line_data,
+ lldb::offset_t *offset_ptr, State::Callback callback,
+ void *userData, DWARFUnit *dwarf_cu);
+ static bool
+ ParseStatementTable(const lldb_private::DWARFDataExtractor &debug_line_data,
+ lldb::offset_t *offset_ptr, LineTable *line_table,
+ DWARFUnit *dwarf_cu);
+ static void Parse(const lldb_private::DWARFDataExtractor &debug_line_data,
+ DWARFDebugLine::State::Callback callback, void *userData);
+ // static void AppendLineTableData(const DWARFDebugLine::Prologue* prologue,
+ // const DWARFDebugLine::Row::collection& state_coll, const uint32_t
+ // addr_size, BinaryStreamBuf &debug_line_data);
+
+ DWARFDebugLine() : m_lineTableMap() {}
+
+ void Parse(const lldb_private::DWARFDataExtractor &debug_line_data);
+ void ParseIfNeeded(const lldb_private::DWARFDataExtractor &debug_line_data);
+ LineTable::shared_ptr GetLineTable(const dw_offset_t offset) const;
+
+protected:
+ typedef std::map<dw_offset_t, LineTable::shared_ptr> LineTableMap;
+ typedef LineTableMap::iterator LineTableIter;
+ typedef LineTableMap::const_iterator LineTableConstIter;
+
+ LineTableMap m_lineTableMap;
+};
+
+#endif // SymbolFileDWARF_DWARFDebugLine_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp
new file mode 100644
index 000000000000..4238be7ec1c3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp
@@ -0,0 +1,126 @@
+//===-- DWARFDebugMacro.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFDebugMacro.h"
+#include "SymbolFileDWARF.h"
+
+#include "lldb/Symbol/DebugMacros.h"
+
+#include "DWARFDataExtractor.h"
+
+using namespace lldb_private;
+
+DWARFDebugMacroHeader
+DWARFDebugMacroHeader::ParseHeader(const DWARFDataExtractor &debug_macro_data,
+ lldb::offset_t *offset) {
+ DWARFDebugMacroHeader header;
+
+ // Skip over the version field in header.
+ header.m_version = debug_macro_data.GetU16(offset);
+
+ uint8_t flags = debug_macro_data.GetU8(offset);
+ header.m_offset_is_64_bit = (flags & OFFSET_SIZE_MASK) != 0;
+
+ if (flags & DEBUG_LINE_OFFSET_MASK) {
+ if (header.m_offset_is_64_bit)
+ header.m_debug_line_offset = debug_macro_data.GetU64(offset);
+ else
+ header.m_debug_line_offset = debug_macro_data.GetU32(offset);
+ }
+
+ // Skip over the operands table if it is present.
+ if (flags & OPCODE_OPERANDS_TABLE_MASK)
+ SkipOperandTable(debug_macro_data, offset);
+
+ return header;
+}
+
+void DWARFDebugMacroHeader::SkipOperandTable(
+ const DWARFDataExtractor &debug_macro_data, lldb::offset_t *offset) {
+ uint8_t entry_count = debug_macro_data.GetU8(offset);
+ for (uint8_t i = 0; i < entry_count; i++) {
+ // Skip over the opcode number.
+ debug_macro_data.GetU8(offset);
+
+ uint64_t operand_count = debug_macro_data.GetULEB128(offset);
+
+ for (uint64_t j = 0; j < operand_count; j++) {
+ // Skip over the operand form
+ debug_macro_data.GetU8(offset);
+ }
+ }
+}
+
+void DWARFDebugMacroEntry::ReadMacroEntries(
+ const DWARFDataExtractor &debug_macro_data,
+ const DWARFDataExtractor &debug_str_data, const bool offset_is_64_bit,
+ lldb::offset_t *offset, SymbolFileDWARF *sym_file_dwarf,
+ DebugMacrosSP &debug_macros_sp) {
+ llvm::dwarf::MacroEntryType type =
+ static_cast<llvm::dwarf::MacroEntryType>(debug_macro_data.GetU8(offset));
+ while (type != 0) {
+ lldb::offset_t new_offset = 0, str_offset = 0;
+ uint32_t line = 0;
+ const char *macro_str = nullptr;
+ uint32_t debug_line_file_idx = 0;
+
+ switch (type) {
+ case DW_MACRO_define:
+ case DW_MACRO_undef:
+ line = debug_macro_data.GetULEB128(offset);
+ macro_str = debug_macro_data.GetCStr(offset);
+ if (type == DW_MACRO_define)
+ debug_macros_sp->AddMacroEntry(
+ DebugMacroEntry::CreateDefineEntry(line, macro_str));
+ else
+ debug_macros_sp->AddMacroEntry(
+ DebugMacroEntry::CreateUndefEntry(line, macro_str));
+ break;
+ case DW_MACRO_define_strp:
+ case DW_MACRO_undef_strp:
+ line = debug_macro_data.GetULEB128(offset);
+ if (offset_is_64_bit)
+ str_offset = debug_macro_data.GetU64(offset);
+ else
+ str_offset = debug_macro_data.GetU32(offset);
+ macro_str = debug_str_data.GetCStr(&str_offset);
+ if (type == DW_MACRO_define_strp)
+ debug_macros_sp->AddMacroEntry(
+ DebugMacroEntry::CreateDefineEntry(line, macro_str));
+ else
+ debug_macros_sp->AddMacroEntry(
+ DebugMacroEntry::CreateUndefEntry(line, macro_str));
+ break;
+ case DW_MACRO_start_file:
+ line = debug_macro_data.GetULEB128(offset);
+ debug_line_file_idx = debug_macro_data.GetULEB128(offset);
+ debug_macros_sp->AddMacroEntry(
+ DebugMacroEntry::CreateStartFileEntry(line, debug_line_file_idx));
+ break;
+ case DW_MACRO_end_file:
+ // This operation has no operands.
+ debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateEndFileEntry());
+ break;
+ case DW_MACRO_import:
+ if (offset_is_64_bit)
+ new_offset = debug_macro_data.GetU64(offset);
+ else
+ new_offset = debug_macro_data.GetU32(offset);
+ debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateIndirectEntry(
+ sym_file_dwarf->ParseDebugMacros(&new_offset)));
+ break;
+ default:
+ // TODO: Add support for other standard operations.
+ // TODO: Provide mechanism to hook handling of non-standard/extension
+ // operands.
+ return;
+ }
+ type = static_cast<llvm::dwarf::MacroEntryType>(
+ debug_macro_data.GetU8(offset));
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h
new file mode 100644
index 000000000000..c3a93a9f4d14
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h
@@ -0,0 +1,61 @@
+//===-- DWARFDebugMacro.h ----------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFDebugMacro_h_
+#define SymbolFileDWARF_DWARFDebugMacro_h_
+
+#include <map>
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/Symbol/DebugMacros.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+class DWARFDataExtractor;
+
+} // namespace lldb_private
+
+class SymbolFileDWARF;
+
+class DWARFDebugMacroHeader {
+public:
+ enum HeaderFlagMask {
+ OFFSET_SIZE_MASK = 0x1,
+ DEBUG_LINE_OFFSET_MASK = 0x2,
+ OPCODE_OPERANDS_TABLE_MASK = 0x4
+ };
+
+ static DWARFDebugMacroHeader
+ ParseHeader(const lldb_private::DWARFDataExtractor &debug_macro_data,
+ lldb::offset_t *offset);
+
+ bool OffsetIs64Bit() const { return m_offset_is_64_bit; }
+
+private:
+ static void
+ SkipOperandTable(const lldb_private::DWARFDataExtractor &debug_macro_data,
+ lldb::offset_t *offset);
+
+ uint16_t m_version;
+ bool m_offset_is_64_bit;
+ uint64_t m_debug_line_offset;
+};
+
+class DWARFDebugMacroEntry {
+public:
+ static void
+ ReadMacroEntries(const lldb_private::DWARFDataExtractor &debug_macro_data,
+ const lldb_private::DWARFDataExtractor &debug_str_data,
+ const bool offset_is_64_bit, lldb::offset_t *sect_offset,
+ SymbolFileDWARF *sym_file_dwarf,
+ lldb_private::DebugMacrosSP &debug_macros_sp);
+};
+
+#endif // SymbolFileDWARF_DWARFDebugMacro_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp
new file mode 100644
index 000000000000..207c71211c9a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp
@@ -0,0 +1,290 @@
+//===-- DWARFDebugRanges.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFDebugRanges.h"
+#include "DWARFUnit.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+
+static dw_addr_t GetBaseAddressMarker(uint32_t addr_size) {
+ switch(addr_size) {
+ case 2:
+ return 0xffff;
+ case 4:
+ return 0xffffffff;
+ case 8:
+ return 0xffffffffffffffff;
+ }
+ llvm_unreachable("GetBaseAddressMarker unsupported address size.");
+}
+
+DWARFDebugRanges::DWARFDebugRanges() : m_range_map() {}
+
+void DWARFDebugRanges::Extract(DWARFContext &context) {
+ DWARFRangeList range_list;
+ lldb::offset_t offset = 0;
+ dw_offset_t debug_ranges_offset = offset;
+ while (Extract(context, &offset, range_list)) {
+ range_list.Sort();
+ m_range_map[debug_ranges_offset] = range_list;
+ debug_ranges_offset = offset;
+ }
+}
+
+bool DWARFDebugRanges::Extract(DWARFContext &context,
+ lldb::offset_t *offset_ptr,
+ DWARFRangeList &range_list) {
+ range_list.Clear();
+
+ lldb::offset_t range_offset = *offset_ptr;
+ const DWARFDataExtractor &debug_ranges_data = context.getOrLoadRangesData();
+ uint32_t addr_size = debug_ranges_data.GetAddressByteSize();
+ dw_addr_t base_addr = 0;
+ dw_addr_t base_addr_marker = GetBaseAddressMarker(addr_size);
+
+ while (
+ debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) {
+ dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
+ dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
+
+ if (!begin && !end) {
+ // End of range list
+ break;
+ }
+
+ if (begin == base_addr_marker) {
+ base_addr = end;
+ continue;
+ }
+
+ // Filter out empty ranges
+ if (begin < end)
+ range_list.Append(DWARFRangeList::Entry(begin + base_addr, end - begin));
+ }
+
+ // Make sure we consumed at least something
+ return range_offset != *offset_ptr;
+}
+
+void DWARFDebugRanges::Dump(Stream &s,
+ const DWARFDataExtractor &debug_ranges_data,
+ lldb::offset_t *offset_ptr,
+ dw_addr_t cu_base_addr) {
+ uint32_t addr_size = s.GetAddressByteSize();
+
+ dw_addr_t base_addr = cu_base_addr;
+ while (
+ debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) {
+ dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
+ dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
+ // Extend 4 byte addresses that consists of 32 bits of 1's to be 64 bits of
+ // ones
+ if (begin == 0xFFFFFFFFull && addr_size == 4)
+ begin = LLDB_INVALID_ADDRESS;
+
+ s.Indent();
+ if (begin == 0 && end == 0) {
+ s.PutCString(" End");
+ break;
+ } else if (begin == LLDB_INVALID_ADDRESS) {
+ // A base address selection entry
+ base_addr = end;
+ s.Address(base_addr, sizeof(dw_addr_t), " Base address = ");
+ } else {
+ // Convert from offset to an address
+ dw_addr_t begin_addr = begin + base_addr;
+ dw_addr_t end_addr = end + base_addr;
+
+ s.AddressRange(begin_addr, end_addr, sizeof(dw_addr_t), nullptr);
+ }
+ }
+}
+
+bool DWARFDebugRanges::FindRanges(const DWARFUnit *cu,
+ dw_offset_t debug_ranges_offset,
+ DWARFRangeList &range_list) const {
+ dw_addr_t debug_ranges_address = cu->GetRangesBase() + debug_ranges_offset;
+ range_map_const_iterator pos = m_range_map.find(debug_ranges_address);
+ if (pos != m_range_map.end()) {
+ range_list = pos->second;
+
+ // All DW_AT_ranges are relative to the base address of the compile
+ // unit. We add the compile unit base address to make sure all the
+ // addresses are properly fixed up.
+ range_list.Slide(cu->GetBaseAddress());
+ return true;
+ }
+ return false;
+}
+
+uint64_t DWARFDebugRanges::GetOffset(size_t Index) const {
+ lldbassert(false && "DW_FORM_rnglistx is not present before DWARF5");
+ return 0;
+}
+
+bool DWARFDebugRngLists::ExtractRangeList(
+ const DWARFDataExtractor &data, uint8_t addrSize,
+ lldb::offset_t *offset_ptr, std::vector<RngListEntry> &rangeList) {
+ rangeList.clear();
+
+ bool error = false;
+ while (!error) {
+ switch (data.GetU8(offset_ptr)) {
+ case DW_RLE_end_of_list:
+ return true;
+
+ case DW_RLE_start_length: {
+ dw_addr_t begin = data.GetMaxU64(offset_ptr, addrSize);
+ dw_addr_t len = data.GetULEB128(offset_ptr);
+ rangeList.push_back({DW_RLE_start_length, begin, len});
+ break;
+ }
+
+ case DW_RLE_start_end: {
+ dw_addr_t begin = data.GetMaxU64(offset_ptr, addrSize);
+ dw_addr_t end = data.GetMaxU64(offset_ptr, addrSize);
+ rangeList.push_back({DW_RLE_start_end, begin, end});
+ break;
+ }
+
+ case DW_RLE_base_address: {
+ dw_addr_t base = data.GetMaxU64(offset_ptr, addrSize);
+ rangeList.push_back({DW_RLE_base_address, base, 0});
+ break;
+ }
+
+ case DW_RLE_offset_pair: {
+ dw_addr_t begin = data.GetULEB128(offset_ptr);
+ dw_addr_t end = data.GetULEB128(offset_ptr);
+ rangeList.push_back({DW_RLE_offset_pair, begin, end});
+ break;
+ }
+
+ case DW_RLE_base_addressx: {
+ dw_addr_t base = data.GetULEB128(offset_ptr);
+ rangeList.push_back({DW_RLE_base_addressx, base, 0});
+ break;
+ }
+
+ case DW_RLE_startx_endx: {
+ dw_addr_t start = data.GetULEB128(offset_ptr);
+ dw_addr_t end = data.GetULEB128(offset_ptr);
+ rangeList.push_back({DW_RLE_startx_endx, start, end});
+ break;
+ }
+
+ case DW_RLE_startx_length: {
+ dw_addr_t start = data.GetULEB128(offset_ptr);
+ dw_addr_t length = data.GetULEB128(offset_ptr);
+ rangeList.push_back({DW_RLE_startx_length, start, length});
+ break;
+ }
+
+ default:
+ lldbassert(0 && "unknown range list entry encoding");
+ error = true;
+ }
+ }
+
+ return false;
+}
+
+static uint64_t ReadAddressFromDebugAddrSection(const DWARFUnit *cu,
+ uint32_t index) {
+ uint32_t index_size = cu->GetAddressByteSize();
+ dw_offset_t addr_base = cu->GetAddrBase();
+ lldb::offset_t offset = addr_base + index * index_size;
+ return cu->GetSymbolFileDWARF()
+ .GetDWARFContext()
+ .getOrLoadAddrData()
+ .GetMaxU64(&offset, index_size);
+}
+
+bool DWARFDebugRngLists::FindRanges(const DWARFUnit *cu,
+ dw_offset_t debug_ranges_offset,
+ DWARFRangeList &range_list) const {
+ range_list.Clear();
+ dw_addr_t debug_ranges_address = cu->GetRangesBase() + debug_ranges_offset;
+ auto pos = m_range_map.find(debug_ranges_address);
+ if (pos != m_range_map.end()) {
+ dw_addr_t BaseAddr = cu->GetBaseAddress();
+ for (const RngListEntry &E : pos->second) {
+ switch (E.encoding) {
+ case DW_RLE_start_length:
+ range_list.Append(DWARFRangeList::Entry(E.value0, E.value1));
+ break;
+ case DW_RLE_base_address:
+ BaseAddr = E.value0;
+ break;
+ case DW_RLE_start_end:
+ range_list.Append(DWARFRangeList::Entry(E.value0, E.value1 - E.value0));
+ break;
+ case DW_RLE_offset_pair:
+ range_list.Append(
+ DWARFRangeList::Entry(BaseAddr + E.value0, E.value1 - E.value0));
+ break;
+ case DW_RLE_base_addressx: {
+ BaseAddr = ReadAddressFromDebugAddrSection(cu, E.value0);
+ break;
+ }
+ case DW_RLE_startx_endx: {
+ dw_addr_t start = ReadAddressFromDebugAddrSection(cu, E.value0);
+ dw_addr_t end = ReadAddressFromDebugAddrSection(cu, E.value1);
+ range_list.Append(DWARFRangeList::Entry(start, end - start));
+ break;
+ }
+ case DW_RLE_startx_length: {
+ dw_addr_t start = ReadAddressFromDebugAddrSection(cu, E.value0);
+ range_list.Append(DWARFRangeList::Entry(start, E.value1));
+ break;
+ }
+ default:
+ llvm_unreachable("unexpected encoding");
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+void DWARFDebugRngLists::Extract(DWARFContext &context) {
+ const DWARFDataExtractor &data = context.getOrLoadRngListsData();
+ lldb::offset_t offset = 0;
+
+ uint64_t length = data.GetU32(&offset);
+ // FIXME: Handle DWARF64.
+ lldb::offset_t end = offset + length;
+
+ // Check version.
+ if (data.GetU16(&offset) < 5)
+ return;
+
+ uint8_t addrSize = data.GetU8(&offset);
+
+ // We do not support non-zero segment selector size.
+ if (data.GetU8(&offset) != 0) {
+ lldbassert(0 && "not implemented");
+ return;
+ }
+
+ uint32_t offsetsAmount = data.GetU32(&offset);
+ for (uint32_t i = 0; i < offsetsAmount; ++i)
+ Offsets.push_back(data.GetMaxU64(&offset, 4));
+
+ lldb::offset_t listOffset = offset;
+ std::vector<RngListEntry> rangeList;
+ while (offset < end && ExtractRangeList(data, addrSize, &offset, rangeList)) {
+ m_range_map[listOffset] = rangeList;
+ listOffset = offset;
+ }
+}
+
+uint64_t DWARFDebugRngLists::GetOffset(size_t Index) const {
+ return Offsets[Index];
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h
new file mode 100644
index 000000000000..baf2667f0afe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h
@@ -0,0 +1,76 @@
+//===-- DWARFDebugRanges.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFDebugRanges_h_
+#define SymbolFileDWARF_DWARFDebugRanges_h_
+
+#include "lldb/Core/dwarf.h"
+#include <map>
+
+class DWARFUnit;
+namespace lldb_private {
+class DWARFContext;
+}
+
+class DWARFDebugRangesBase {
+public:
+ virtual ~DWARFDebugRangesBase(){};
+
+ virtual void Extract(lldb_private::DWARFContext &context) = 0;
+ virtual bool FindRanges(const DWARFUnit *cu, dw_offset_t debug_ranges_offset,
+ DWARFRangeList &range_list) const = 0;
+ virtual uint64_t GetOffset(size_t Index) const = 0;
+};
+
+class DWARFDebugRanges final : public DWARFDebugRangesBase {
+public:
+ DWARFDebugRanges();
+
+ void Extract(lldb_private::DWARFContext &context) override;
+ bool FindRanges(const DWARFUnit *cu, dw_offset_t debug_ranges_offset,
+ DWARFRangeList &range_list) const override;
+ uint64_t GetOffset(size_t Index) const override;
+
+ static void Dump(lldb_private::Stream &s,
+ const lldb_private::DWARFDataExtractor &debug_ranges_data,
+ lldb::offset_t *offset_ptr, dw_addr_t cu_base_addr);
+
+protected:
+ bool Extract(lldb_private::DWARFContext &context, lldb::offset_t *offset_ptr,
+ DWARFRangeList &range_list);
+
+ typedef std::map<dw_offset_t, DWARFRangeList> range_map;
+ typedef range_map::iterator range_map_iterator;
+ typedef range_map::const_iterator range_map_const_iterator;
+ range_map m_range_map;
+};
+
+// DWARF v5 .debug_rnglists section.
+class DWARFDebugRngLists final : public DWARFDebugRangesBase {
+ struct RngListEntry {
+ uint8_t encoding;
+ uint64_t value0;
+ uint64_t value1;
+ };
+
+public:
+ void Extract(lldb_private::DWARFContext &context) override;
+ bool FindRanges(const DWARFUnit *cu, dw_offset_t debug_ranges_offset,
+ DWARFRangeList &range_list) const override;
+ uint64_t GetOffset(size_t Index) const override;
+
+protected:
+ bool ExtractRangeList(const lldb_private::DWARFDataExtractor &data,
+ uint8_t addrSize, lldb::offset_t *offset_ptr,
+ std::vector<RngListEntry> &list);
+
+ std::vector<uint64_t> Offsets;
+ std::map<dw_offset_t, std::vector<RngListEntry>> m_range_map;
+};
+
+#endif // SymbolFileDWARF_DWARFDebugRanges_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp
new file mode 100644
index 000000000000..a664314035e4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp
@@ -0,0 +1,87 @@
+//===-- DWARFDeclContext.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFDeclContext.h"
+
+const char *DWARFDeclContext::GetQualifiedName() const {
+ if (m_qualified_name.empty()) {
+ // The declaration context array for a class named "foo" in namespace
+ // "a::b::c" will be something like:
+ // [0] DW_TAG_class_type "foo"
+ // [1] DW_TAG_namespace "c"
+ // [2] DW_TAG_namespace "b"
+ // [3] DW_TAG_namespace "a"
+ if (!m_entries.empty()) {
+ if (m_entries.size() == 1) {
+ if (m_entries[0].name) {
+ m_qualified_name.append("::");
+ m_qualified_name.append(m_entries[0].name);
+ }
+ } else {
+ collection::const_reverse_iterator pos;
+ collection::const_reverse_iterator begin = m_entries.rbegin();
+ collection::const_reverse_iterator end = m_entries.rend();
+ for (pos = begin; pos != end; ++pos) {
+ if (pos != begin)
+ m_qualified_name.append("::");
+ if (pos->name == nullptr) {
+ if (pos->tag == DW_TAG_namespace)
+ m_qualified_name.append("(anonymous namespace)");
+ else if (pos->tag == DW_TAG_class_type)
+ m_qualified_name.append("(anonymous class)");
+ else if (pos->tag == DW_TAG_structure_type)
+ m_qualified_name.append("(anonymous struct)");
+ else if (pos->tag == DW_TAG_union_type)
+ m_qualified_name.append("(anonymous union)");
+ else
+ m_qualified_name.append("(anonymous)");
+ } else
+ m_qualified_name.append(pos->name);
+ }
+ }
+ }
+ }
+ if (m_qualified_name.empty())
+ return nullptr;
+ return m_qualified_name.c_str();
+}
+
+bool DWARFDeclContext::operator==(const DWARFDeclContext &rhs) const {
+ if (m_entries.size() != rhs.m_entries.size())
+ return false;
+
+ collection::const_iterator pos;
+ collection::const_iterator begin = m_entries.begin();
+ collection::const_iterator end = m_entries.end();
+
+ collection::const_iterator rhs_pos;
+ collection::const_iterator rhs_begin = rhs.m_entries.begin();
+ // The two entry arrays have the same size
+
+ // First compare the tags before we do expensive name compares
+ for (pos = begin, rhs_pos = rhs_begin; pos != end; ++pos, ++rhs_pos) {
+ if (pos->tag != rhs_pos->tag) {
+ // Check for DW_TAG_structure_type and DW_TAG_class_type as they are
+ // often used interchangeably in GCC
+ if (pos->tag == DW_TAG_structure_type &&
+ rhs_pos->tag == DW_TAG_class_type)
+ continue;
+ if (pos->tag == DW_TAG_class_type &&
+ rhs_pos->tag == DW_TAG_structure_type)
+ continue;
+ return false;
+ }
+ }
+ // The tags all match, now compare the names
+ for (pos = begin, rhs_pos = rhs_begin; pos != end; ++pos, ++rhs_pos) {
+ if (!pos->NameMatches(*rhs_pos))
+ return false;
+ }
+ // All tags and names match
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h
new file mode 100644
index 000000000000..d0d70dd5123e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h
@@ -0,0 +1,88 @@
+//===-- DWARFDeclContext.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFDeclContext_h_
+#define SymbolFileDWARF_DWARFDeclContext_h_
+
+#include <string>
+#include <vector>
+#include "lldb/Utility/ConstString.h"
+#include "DWARFDefines.h"
+
+// DWARFDeclContext
+//
+// A class that represents a declaration context all the way down to a
+// DIE. This is useful when trying to find a DIE in one DWARF to a DIE
+// in another DWARF file.
+
+class DWARFDeclContext {
+public:
+ struct Entry {
+ Entry() : tag(0), name(nullptr) {}
+ Entry(dw_tag_t t, const char *n) : tag(t), name(n) {}
+
+ bool NameMatches(const Entry &rhs) const {
+ if (name == rhs.name)
+ return true;
+ else if (name && rhs.name)
+ return strcmp(name, rhs.name) == 0;
+ return false;
+ }
+
+ // Test operator
+ explicit operator bool() const { return tag != 0; }
+
+ dw_tag_t tag;
+ const char *name;
+ };
+
+ DWARFDeclContext() : m_entries(), m_language(lldb::eLanguageTypeUnknown) {}
+
+ void AppendDeclContext(dw_tag_t tag, const char *name) {
+ m_entries.push_back(Entry(tag, name));
+ }
+
+ bool operator==(const DWARFDeclContext &rhs) const;
+
+ uint32_t GetSize() const { return m_entries.size(); }
+
+ Entry &operator[](uint32_t idx) {
+ // "idx" must be valid
+ return m_entries[idx];
+ }
+
+ const Entry &operator[](uint32_t idx) const {
+ // "idx" must be valid
+ return m_entries[idx];
+ }
+
+ const char *GetQualifiedName() const;
+
+ // Same as GetQaulifiedName, but the life time of the returned string will
+ // be that of the LLDB session.
+ lldb_private::ConstString GetQualifiedNameAsConstString() const {
+ return lldb_private::ConstString(GetQualifiedName());
+ }
+
+ void Clear() {
+ m_entries.clear();
+ m_qualified_name.clear();
+ }
+
+ lldb::LanguageType GetLanguage() const { return m_language; }
+
+ void SetLanguage(lldb::LanguageType language) { m_language = language; }
+
+protected:
+ typedef std::vector<Entry> collection;
+ collection m_entries;
+ mutable std::string m_qualified_name;
+ lldb::LanguageType m_language;
+};
+
+#endif // SymbolFileDWARF_DWARFDeclContext_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
new file mode 100644
index 000000000000..3bf0bb088227
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
@@ -0,0 +1,402 @@
+//===-- DWARFDefines.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFDefines.h"
+#include "lldb/Utility/ConstString.h"
+#include <cstdio>
+#include <cstring>
+#include <string>
+
+namespace lldb_private {
+
+const char *DW_TAG_value_to_name(uint32_t val) {
+ static char invalid[100];
+
+ if (val == 0)
+ return "NULL";
+
+ llvm::StringRef llvmstr = llvm::dwarf::TagString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_TAG constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+const char *DW_AT_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::AttributeString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_AT constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+const char *DW_FORM_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::FormEncodingString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_FORM constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+const char *DW_OP_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::OperationEncodingString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+DRC_class DW_OP_value_to_class(uint32_t val) {
+ switch (val) {
+ case 0x03:
+ return DRC_ONEOPERAND;
+ case 0x06:
+ return DRC_ZEROOPERANDS;
+ case 0x08:
+ return DRC_ONEOPERAND;
+ case 0x09:
+ return DRC_ONEOPERAND;
+ case 0x0a:
+ return DRC_ONEOPERAND;
+ case 0x0b:
+ return DRC_ONEOPERAND;
+ case 0x0c:
+ return DRC_ONEOPERAND;
+ case 0x0d:
+ return DRC_ONEOPERAND;
+ case 0x0e:
+ return DRC_ONEOPERAND;
+ case 0x0f:
+ return DRC_ONEOPERAND;
+ case 0x10:
+ return DRC_ONEOPERAND;
+ case 0x11:
+ return DRC_ONEOPERAND;
+ case 0x12:
+ return DRC_ZEROOPERANDS;
+ case 0x13:
+ return DRC_ZEROOPERANDS;
+ case 0x14:
+ return DRC_ZEROOPERANDS;
+ case 0x15:
+ return DRC_ONEOPERAND;
+ case 0x16:
+ return DRC_ZEROOPERANDS;
+ case 0x17:
+ return DRC_ZEROOPERANDS;
+ case 0x18:
+ return DRC_ZEROOPERANDS;
+ case 0x19:
+ return DRC_ZEROOPERANDS;
+ case 0x1a:
+ return DRC_ZEROOPERANDS;
+ case 0x1b:
+ return DRC_ZEROOPERANDS;
+ case 0x1c:
+ return DRC_ZEROOPERANDS;
+ case 0x1d:
+ return DRC_ZEROOPERANDS;
+ case 0x1e:
+ return DRC_ZEROOPERANDS;
+ case 0x1f:
+ return DRC_ZEROOPERANDS;
+ case 0x20:
+ return DRC_ZEROOPERANDS;
+ case 0x21:
+ return DRC_ZEROOPERANDS;
+ case 0x22:
+ return DRC_ZEROOPERANDS;
+ case 0x23:
+ return DRC_ONEOPERAND;
+ case 0x24:
+ return DRC_ZEROOPERANDS;
+ case 0x25:
+ return DRC_ZEROOPERANDS;
+ case 0x26:
+ return DRC_ZEROOPERANDS;
+ case 0x27:
+ return DRC_ZEROOPERANDS;
+ case 0x2f:
+ return DRC_ONEOPERAND;
+ case 0x28:
+ return DRC_ONEOPERAND;
+ case 0x29:
+ return DRC_ZEROOPERANDS;
+ case 0x2a:
+ return DRC_ZEROOPERANDS;
+ case 0x2b:
+ return DRC_ZEROOPERANDS;
+ case 0x2c:
+ return DRC_ZEROOPERANDS;
+ case 0x2d:
+ return DRC_ZEROOPERANDS;
+ case 0x2e:
+ return DRC_ZEROOPERANDS;
+ case 0x30:
+ return DRC_ZEROOPERANDS;
+ case 0x31:
+ return DRC_ZEROOPERANDS;
+ case 0x32:
+ return DRC_ZEROOPERANDS;
+ case 0x33:
+ return DRC_ZEROOPERANDS;
+ case 0x34:
+ return DRC_ZEROOPERANDS;
+ case 0x35:
+ return DRC_ZEROOPERANDS;
+ case 0x36:
+ return DRC_ZEROOPERANDS;
+ case 0x37:
+ return DRC_ZEROOPERANDS;
+ case 0x38:
+ return DRC_ZEROOPERANDS;
+ case 0x39:
+ return DRC_ZEROOPERANDS;
+ case 0x3a:
+ return DRC_ZEROOPERANDS;
+ case 0x3b:
+ return DRC_ZEROOPERANDS;
+ case 0x3c:
+ return DRC_ZEROOPERANDS;
+ case 0x3d:
+ return DRC_ZEROOPERANDS;
+ case 0x3e:
+ return DRC_ZEROOPERANDS;
+ case 0x3f:
+ return DRC_ZEROOPERANDS;
+ case 0x40:
+ return DRC_ZEROOPERANDS;
+ case 0x41:
+ return DRC_ZEROOPERANDS;
+ case 0x42:
+ return DRC_ZEROOPERANDS;
+ case 0x43:
+ return DRC_ZEROOPERANDS;
+ case 0x44:
+ return DRC_ZEROOPERANDS;
+ case 0x45:
+ return DRC_ZEROOPERANDS;
+ case 0x46:
+ return DRC_ZEROOPERANDS;
+ case 0x47:
+ return DRC_ZEROOPERANDS;
+ case 0x48:
+ return DRC_ZEROOPERANDS;
+ case 0x49:
+ return DRC_ZEROOPERANDS;
+ case 0x4a:
+ return DRC_ZEROOPERANDS;
+ case 0x4b:
+ return DRC_ZEROOPERANDS;
+ case 0x4c:
+ return DRC_ZEROOPERANDS;
+ case 0x4d:
+ return DRC_ZEROOPERANDS;
+ case 0x4e:
+ return DRC_ZEROOPERANDS;
+ case 0x4f:
+ return DRC_ZEROOPERANDS;
+ case 0x50:
+ return DRC_ZEROOPERANDS;
+ case 0x51:
+ return DRC_ZEROOPERANDS;
+ case 0x52:
+ return DRC_ZEROOPERANDS;
+ case 0x53:
+ return DRC_ZEROOPERANDS;
+ case 0x54:
+ return DRC_ZEROOPERANDS;
+ case 0x55:
+ return DRC_ZEROOPERANDS;
+ case 0x56:
+ return DRC_ZEROOPERANDS;
+ case 0x57:
+ return DRC_ZEROOPERANDS;
+ case 0x58:
+ return DRC_ZEROOPERANDS;
+ case 0x59:
+ return DRC_ZEROOPERANDS;
+ case 0x5a:
+ return DRC_ZEROOPERANDS;
+ case 0x5b:
+ return DRC_ZEROOPERANDS;
+ case 0x5c:
+ return DRC_ZEROOPERANDS;
+ case 0x5d:
+ return DRC_ZEROOPERANDS;
+ case 0x5e:
+ return DRC_ZEROOPERANDS;
+ case 0x5f:
+ return DRC_ZEROOPERANDS;
+ case 0x60:
+ return DRC_ZEROOPERANDS;
+ case 0x61:
+ return DRC_ZEROOPERANDS;
+ case 0x62:
+ return DRC_ZEROOPERANDS;
+ case 0x63:
+ return DRC_ZEROOPERANDS;
+ case 0x64:
+ return DRC_ZEROOPERANDS;
+ case 0x65:
+ return DRC_ZEROOPERANDS;
+ case 0x66:
+ return DRC_ZEROOPERANDS;
+ case 0x67:
+ return DRC_ZEROOPERANDS;
+ case 0x68:
+ return DRC_ZEROOPERANDS;
+ case 0x69:
+ return DRC_ZEROOPERANDS;
+ case 0x6a:
+ return DRC_ZEROOPERANDS;
+ case 0x6b:
+ return DRC_ZEROOPERANDS;
+ case 0x6c:
+ return DRC_ZEROOPERANDS;
+ case 0x6d:
+ return DRC_ZEROOPERANDS;
+ case 0x6e:
+ return DRC_ZEROOPERANDS;
+ case 0x6f:
+ return DRC_ZEROOPERANDS;
+ case 0x70:
+ return DRC_ONEOPERAND;
+ case 0x71:
+ return DRC_ONEOPERAND;
+ case 0x72:
+ return DRC_ONEOPERAND;
+ case 0x73:
+ return DRC_ONEOPERAND;
+ case 0x74:
+ return DRC_ONEOPERAND;
+ case 0x75:
+ return DRC_ONEOPERAND;
+ case 0x76:
+ return DRC_ONEOPERAND;
+ case 0x77:
+ return DRC_ONEOPERAND;
+ case 0x78:
+ return DRC_ONEOPERAND;
+ case 0x79:
+ return DRC_ONEOPERAND;
+ case 0x7a:
+ return DRC_ONEOPERAND;
+ case 0x7b:
+ return DRC_ONEOPERAND;
+ case 0x7c:
+ return DRC_ONEOPERAND;
+ case 0x7d:
+ return DRC_ONEOPERAND;
+ case 0x7e:
+ return DRC_ONEOPERAND;
+ case 0x7f:
+ return DRC_ONEOPERAND;
+ case 0x80:
+ return DRC_ONEOPERAND;
+ case 0x81:
+ return DRC_ONEOPERAND;
+ case 0x82:
+ return DRC_ONEOPERAND;
+ case 0x83:
+ return DRC_ONEOPERAND;
+ case 0x84:
+ return DRC_ONEOPERAND;
+ case 0x85:
+ return DRC_ONEOPERAND;
+ case 0x86:
+ return DRC_ONEOPERAND;
+ case 0x87:
+ return DRC_ONEOPERAND;
+ case 0x88:
+ return DRC_ONEOPERAND;
+ case 0x89:
+ return DRC_ONEOPERAND;
+ case 0x8a:
+ return DRC_ONEOPERAND;
+ case 0x8b:
+ return DRC_ONEOPERAND;
+ case 0x8c:
+ return DRC_ONEOPERAND;
+ case 0x8d:
+ return DRC_ONEOPERAND;
+ case 0x8e:
+ return DRC_ONEOPERAND;
+ case 0x8f:
+ return DRC_ONEOPERAND;
+ case 0x90:
+ return DRC_ONEOPERAND;
+ case 0x91:
+ return DRC_ONEOPERAND;
+ case 0x92:
+ return DRC_TWOOPERANDS;
+ case 0x93:
+ return DRC_ONEOPERAND;
+ case 0x94:
+ return DRC_ONEOPERAND;
+ case 0x95:
+ return DRC_ONEOPERAND;
+ case 0x96:
+ return DRC_ZEROOPERANDS;
+ case 0x97:
+ return DRC_DWARFv3 | DRC_ZEROOPERANDS;
+ case 0x98:
+ return DRC_DWARFv3 | DRC_ONEOPERAND;
+ case 0x99:
+ return DRC_DWARFv3 | DRC_ONEOPERAND;
+ case 0x9a:
+ return DRC_DWARFv3 | DRC_ONEOPERAND;
+ case 0xf0:
+ return DRC_ZEROOPERANDS; /* DW_OP_APPLE_uninit */
+ case 0xe0:
+ return 0;
+ case 0xff:
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+const char *DW_ATE_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::AttributeEncodingString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_ATE constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+const char *DW_LANG_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::LanguageString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_LANG constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+const char *DW_LNS_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::LNStandardString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_LNS constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h
new file mode 100644
index 000000000000..d16cb07baf88
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h
@@ -0,0 +1,77 @@
+//===-- DWARFDefines.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFDefines_h_
+#define SymbolFileDWARF_DWARFDefines_h_
+
+#include "lldb/Core/dwarf.h"
+#include <stdint.h>
+
+namespace lldb_private {
+
+enum class DWARFEnumState { MoreItems, Complete };
+
+typedef uint32_t DRC_class; // Holds DRC_* class bitfields
+
+const char *DW_TAG_value_to_name(uint32_t val);
+
+const char *DW_AT_value_to_name(uint32_t val);
+
+const char *DW_FORM_value_to_name(uint32_t val);
+
+const char *DW_OP_value_to_name(uint32_t val);
+
+DRC_class DW_OP_value_to_class(uint32_t val);
+
+const char *DW_ATE_value_to_name(uint32_t val);
+
+const char *DW_LANG_value_to_name(uint32_t val);
+
+const char *DW_LNS_value_to_name(uint32_t val);
+
+/* These DRC are entirely our own construction,
+ although they are derived from various comments in the DWARF standard.
+ Most of these are not useful to the parser, but the DW_AT and DW_FORM
+ classes should prove to be usable in some fashion. */
+
+#define DRC_0x65 0x1
+#define DRC_ADDRESS 0x2
+#define DRC_BLOCK 0x4
+#define DRC_CONSTANT 0x8
+#define DRC_DWARFv3 0x10
+#define DRC_FLAG 0x20
+#define DRC_INDIRECT_SPECIAL 0x40
+#define DRC_LINEPTR 0x80
+#define DRC_LOCEXPR 0x100
+#define DRC_LOCLISTPTR 0x200
+#define DRC_MACPTR 0x400
+#define DRC_ONEOPERAND 0x800
+#define DRC_OPERANDONE_1BYTE_DELTA 0x1000
+#define DRC_OPERANDONE_2BYTE_DELTA 0x2000
+#define DRC_OPERANDONE_4BYTE_DELTA 0x4000
+#define DRC_OPERANDONE_ADDRESS 0x8000
+#define DRC_OPERANDONE_BLOCK 0x10000
+#define DRC_OPERANDONE_SLEB128_OFFSET 0x20000
+#define DRC_OPERANDONE_ULEB128_OFFSET 0x40000
+#define DRC_OPERANDONE_ULEB128_REGISTER 0x80000
+#define DRC_OPERANDTWO_BLOCK 0x100000
+#define DRC_OPERANDTWO_SLEB128_OFFSET 0x200000
+#define DRC_OPERANDTWO_ULEB128_OFFSET 0x400000
+#define DRC_OPERANDTWO_ULEB128_REGISTER 0x800000
+#define DRC_OPERNADONE_ULEB128_REGISTER 0x1000000
+#define DRC_RANGELISTPTR 0x2000000
+#define DRC_REFERENCE 0x4000000
+#define DRC_STRING 0x8000000
+#define DRC_TWOOPERANDS 0x10000000
+#define DRC_VENDOR_GNU 0x20000000
+#define DRC_VENDOR_MIPS 0x40000000
+#define DRC_ZEROOPERANDS 0x80000000
+
+} // namespace lldb_private
+
+#endif // SymbolFileDWARF_DWARFDefines_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp
new file mode 100644
index 000000000000..046ae67446af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp
@@ -0,0 +1,739 @@
+//===-- DWARFFormValue.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <assert.h>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/Stream.h"
+
+#include "DWARFDebugInfo.h"
+#include "DWARFFormValue.h"
+#include "DWARFUnit.h"
+
+class DWARFUnit;
+
+using namespace lldb_private;
+
+void DWARFFormValue::Clear() {
+ m_unit = nullptr;
+ m_form = 0;
+ m_value = ValueTypeTag();
+}
+
+bool DWARFFormValue::ExtractValue(const DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr) {
+ if (m_form == DW_FORM_implicit_const)
+ return true;
+
+ bool indirect = false;
+ bool is_block = false;
+ m_value.data = nullptr;
+ uint8_t ref_addr_size;
+ // Read the value for the form into value and follow and DW_FORM_indirect
+ // instances we run into
+ do {
+ indirect = false;
+ switch (m_form) {
+ case DW_FORM_addr:
+ assert(m_unit);
+ m_value.value.uval =
+ data.GetMaxU64(offset_ptr, DWARFUnit::GetAddressByteSize(m_unit));
+ break;
+ case DW_FORM_block1:
+ m_value.value.uval = data.GetU8(offset_ptr);
+ is_block = true;
+ break;
+ case DW_FORM_block2:
+ m_value.value.uval = data.GetU16(offset_ptr);
+ is_block = true;
+ break;
+ case DW_FORM_block4:
+ m_value.value.uval = data.GetU32(offset_ptr);
+ is_block = true;
+ break;
+ case DW_FORM_data16:
+ m_value.value.uval = 16;
+ is_block = true;
+ break;
+ case DW_FORM_exprloc:
+ case DW_FORM_block:
+ m_value.value.uval = data.GetULEB128(offset_ptr);
+ is_block = true;
+ break;
+ case DW_FORM_string:
+ m_value.value.cstr = data.GetCStr(offset_ptr);
+ break;
+ case DW_FORM_sdata:
+ m_value.value.sval = data.GetSLEB128(offset_ptr);
+ break;
+ case DW_FORM_strp:
+ case DW_FORM_line_strp:
+ case DW_FORM_sec_offset:
+ m_value.value.uval = data.GetMaxU64(offset_ptr, 4);
+ break;
+ case DW_FORM_addrx1:
+ case DW_FORM_strx1:
+ case DW_FORM_ref1:
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ m_value.value.uval = data.GetU8(offset_ptr);
+ break;
+ case DW_FORM_addrx2:
+ case DW_FORM_strx2:
+ case DW_FORM_ref2:
+ case DW_FORM_data2:
+ m_value.value.uval = data.GetU16(offset_ptr);
+ break;
+ case DW_FORM_addrx3:
+ case DW_FORM_strx3:
+ m_value.value.uval = data.GetMaxU64(offset_ptr, 3);
+ break;
+ case DW_FORM_addrx4:
+ case DW_FORM_strx4:
+ case DW_FORM_ref4:
+ case DW_FORM_data4:
+ m_value.value.uval = data.GetU32(offset_ptr);
+ break;
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_sig8:
+ m_value.value.uval = data.GetU64(offset_ptr);
+ break;
+ case DW_FORM_addrx:
+ case DW_FORM_rnglistx:
+ case DW_FORM_strx:
+ case DW_FORM_udata:
+ case DW_FORM_ref_udata:
+ case DW_FORM_GNU_str_index:
+ case DW_FORM_GNU_addr_index:
+ m_value.value.uval = data.GetULEB128(offset_ptr);
+ break;
+ case DW_FORM_ref_addr:
+ assert(m_unit);
+ if (m_unit->GetVersion() <= 2)
+ ref_addr_size = m_unit->GetAddressByteSize();
+ else
+ ref_addr_size = 4;
+ m_value.value.uval = data.GetMaxU64(offset_ptr, ref_addr_size);
+ break;
+ case DW_FORM_indirect:
+ m_form = data.GetULEB128(offset_ptr);
+ indirect = true;
+ break;
+ case DW_FORM_flag_present:
+ m_value.value.uval = 1;
+ break;
+ default:
+ return false;
+ }
+ } while (indirect);
+
+ if (is_block) {
+ m_value.data = data.PeekData(*offset_ptr, m_value.value.uval);
+ if (m_value.data != nullptr) {
+ *offset_ptr += m_value.value.uval;
+ }
+ }
+
+ return true;
+}
+
+struct FormSize {
+ uint8_t valid:1, size:7;
+};
+static FormSize g_form_sizes[] = {
+ {0,0}, // 0x00 unused
+ {0,0}, // 0x01 DW_FORM_addr
+ {0,0}, // 0x02 unused
+ {0,0}, // 0x03 DW_FORM_block2
+ {0,0}, // 0x04 DW_FORM_block4
+ {1,2}, // 0x05 DW_FORM_data2
+ {1,4}, // 0x06 DW_FORM_data4
+ {1,8}, // 0x07 DW_FORM_data8
+ {0,0}, // 0x08 DW_FORM_string
+ {0,0}, // 0x09 DW_FORM_block
+ {0,0}, // 0x0a DW_FORM_block1
+ {1,1}, // 0x0b DW_FORM_data1
+ {1,1}, // 0x0c DW_FORM_flag
+ {0,0}, // 0x0d DW_FORM_sdata
+ {1,4}, // 0x0e DW_FORM_strp
+ {0,0}, // 0x0f DW_FORM_udata
+ {0,0}, // 0x10 DW_FORM_ref_addr (addr size for DWARF2 and earlier, 4 bytes for
+ // DWARF32, 8 bytes for DWARF32 in DWARF 3 and later
+ {1,1}, // 0x11 DW_FORM_ref1
+ {1,2}, // 0x12 DW_FORM_ref2
+ {1,4}, // 0x13 DW_FORM_ref4
+ {1,8}, // 0x14 DW_FORM_ref8
+ {0,0}, // 0x15 DW_FORM_ref_udata
+ {0,0}, // 0x16 DW_FORM_indirect
+ {1,4}, // 0x17 DW_FORM_sec_offset
+ {0,0}, // 0x18 DW_FORM_exprloc
+ {1,0}, // 0x19 DW_FORM_flag_present
+ {0,0}, // 0x1a
+ {0,0}, // 0x1b
+ {0,0}, // 0x1c
+ {0,0}, // 0x1d
+ {0,0}, // 0x1e
+ {0,0}, // 0x1f
+ {1,8}, // 0x20 DW_FORM_ref_sig8
+};
+
+llvm::Optional<uint8_t>
+DWARFFormValue::GetFixedSize(dw_form_t form, const DWARFUnit *u) {
+ if (form <= DW_FORM_ref_sig8 && g_form_sizes[form].valid)
+ return g_form_sizes[form].size;
+ if (form == DW_FORM_addr && u)
+ return u->GetAddressByteSize();
+ return llvm::None;
+}
+
+llvm::Optional<uint8_t> DWARFFormValue::GetFixedSize() const {
+ return GetFixedSize(m_form, m_unit);
+}
+
+bool DWARFFormValue::SkipValue(const DWARFDataExtractor &debug_info_data,
+ lldb::offset_t *offset_ptr) const {
+ return DWARFFormValue::SkipValue(m_form, debug_info_data, offset_ptr, m_unit);
+}
+
+bool DWARFFormValue::SkipValue(dw_form_t form,
+ const DWARFDataExtractor &debug_info_data,
+ lldb::offset_t *offset_ptr,
+ const DWARFUnit *unit) {
+ uint8_t ref_addr_size;
+ switch (form) {
+ // Blocks if inlined data that have a length field and the data bytes inlined
+ // in the .debug_info
+ case DW_FORM_exprloc:
+ case DW_FORM_block: {
+ dw_uleb128_t size = debug_info_data.GetULEB128(offset_ptr);
+ *offset_ptr += size;
+ }
+ return true;
+ case DW_FORM_block1: {
+ dw_uleb128_t size = debug_info_data.GetU8(offset_ptr);
+ *offset_ptr += size;
+ }
+ return true;
+ case DW_FORM_block2: {
+ dw_uleb128_t size = debug_info_data.GetU16(offset_ptr);
+ *offset_ptr += size;
+ }
+ return true;
+ case DW_FORM_block4: {
+ dw_uleb128_t size = debug_info_data.GetU32(offset_ptr);
+ *offset_ptr += size;
+ }
+ return true;
+
+ // Inlined NULL terminated C-strings
+ case DW_FORM_string:
+ debug_info_data.GetCStr(offset_ptr);
+ return true;
+
+ // Compile unit address sized values
+ case DW_FORM_addr:
+ *offset_ptr += DWARFUnit::GetAddressByteSize(unit);
+ return true;
+
+ case DW_FORM_ref_addr:
+ ref_addr_size = 4;
+ assert(unit); // Unit must be valid for DW_FORM_ref_addr objects or we will
+ // get this wrong
+ if (unit->GetVersion() <= 2)
+ ref_addr_size = unit->GetAddressByteSize();
+ else
+ ref_addr_size = 4;
+ *offset_ptr += ref_addr_size;
+ return true;
+
+ // 0 bytes values (implied from DW_FORM)
+ case DW_FORM_flag_present:
+ case DW_FORM_implicit_const:
+ return true;
+
+ // 1 byte values
+ case DW_FORM_addrx1:
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ case DW_FORM_ref1:
+ case DW_FORM_strx1:
+ *offset_ptr += 1;
+ return true;
+
+ // 2 byte values
+ case DW_FORM_addrx2:
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ case DW_FORM_strx2:
+ *offset_ptr += 2;
+ return true;
+
+ // 3 byte values
+ case DW_FORM_addrx3:
+ case DW_FORM_strx3:
+ *offset_ptr += 3;
+ return true;
+
+ // 32 bit for DWARF 32, 64 for DWARF 64
+ case DW_FORM_sec_offset:
+ case DW_FORM_strp:
+ *offset_ptr += 4;
+ return true;
+
+ // 4 byte values
+ case DW_FORM_addrx4:
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ case DW_FORM_strx4:
+ *offset_ptr += 4;
+ return true;
+
+ // 8 byte values
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_sig8:
+ *offset_ptr += 8;
+ return true;
+
+ // signed or unsigned LEB 128 values
+ case DW_FORM_addrx:
+ case DW_FORM_rnglistx:
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ case DW_FORM_ref_udata:
+ case DW_FORM_GNU_addr_index:
+ case DW_FORM_GNU_str_index:
+ case DW_FORM_strx:
+ debug_info_data.Skip_LEB128(offset_ptr);
+ return true;
+
+ case DW_FORM_indirect: {
+ dw_form_t indirect_form = debug_info_data.GetULEB128(offset_ptr);
+ return DWARFFormValue::SkipValue(indirect_form, debug_info_data, offset_ptr,
+ unit);
+ }
+
+ default:
+ break;
+ }
+ return false;
+}
+
+void DWARFFormValue::Dump(Stream &s) const {
+ uint64_t uvalue = Unsigned();
+ bool unit_relative_offset = false;
+
+ switch (m_form) {
+ case DW_FORM_addr:
+ s.Address(uvalue, sizeof(uint64_t));
+ break;
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ s.PutHex8(uvalue);
+ break;
+ case DW_FORM_data2:
+ s.PutHex16(uvalue);
+ break;
+ case DW_FORM_sec_offset:
+ case DW_FORM_data4:
+ s.PutHex32(uvalue);
+ break;
+ case DW_FORM_ref_sig8:
+ case DW_FORM_data8:
+ s.PutHex64(uvalue);
+ break;
+ case DW_FORM_string:
+ s.QuotedCString(AsCString());
+ break;
+ case DW_FORM_exprloc:
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ if (uvalue > 0) {
+ switch (m_form) {
+ case DW_FORM_exprloc:
+ case DW_FORM_block:
+ s.Printf("<0x%" PRIx64 "> ", uvalue);
+ break;
+ case DW_FORM_block1:
+ s.Printf("<0x%2.2x> ", (uint8_t)uvalue);
+ break;
+ case DW_FORM_block2:
+ s.Printf("<0x%4.4x> ", (uint16_t)uvalue);
+ break;
+ case DW_FORM_block4:
+ s.Printf("<0x%8.8x> ", (uint32_t)uvalue);
+ break;
+ default:
+ break;
+ }
+
+ const uint8_t *data_ptr = m_value.data;
+ if (data_ptr) {
+ const uint8_t *end_data_ptr =
+ data_ptr + uvalue; // uvalue contains size of block
+ while (data_ptr < end_data_ptr) {
+ s.Printf("%2.2x ", *data_ptr);
+ ++data_ptr;
+ }
+ } else
+ s.PutCString("NULL");
+ }
+ break;
+
+ case DW_FORM_sdata:
+ s.PutSLEB128(uvalue);
+ break;
+ case DW_FORM_udata:
+ s.PutULEB128(uvalue);
+ break;
+ case DW_FORM_strp: {
+ const char *dbg_str = AsCString();
+ if (dbg_str) {
+ s.QuotedCString(dbg_str);
+ } else {
+ s.PutHex32(uvalue);
+ }
+ } break;
+
+ case DW_FORM_ref_addr: {
+ assert(m_unit); // Unit must be valid for DW_FORM_ref_addr objects or we
+ // will get this wrong
+ if (m_unit->GetVersion() <= 2)
+ s.Address(uvalue, sizeof(uint64_t) * 2);
+ else
+ s.Address(uvalue, 4 * 2); // 4 for DWARF32, 8 for DWARF64, but we don't
+ // support DWARF64 yet
+ break;
+ }
+ case DW_FORM_ref1:
+ unit_relative_offset = true;
+ break;
+ case DW_FORM_ref2:
+ unit_relative_offset = true;
+ break;
+ case DW_FORM_ref4:
+ unit_relative_offset = true;
+ break;
+ case DW_FORM_ref8:
+ unit_relative_offset = true;
+ break;
+ case DW_FORM_ref_udata:
+ unit_relative_offset = true;
+ break;
+
+ // All DW_FORM_indirect attributes should be resolved prior to calling this
+ // function
+ case DW_FORM_indirect:
+ s.PutCString("DW_FORM_indirect");
+ break;
+ case DW_FORM_flag_present:
+ break;
+ default:
+ s.Printf("DW_FORM(0x%4.4x)", m_form);
+ break;
+ }
+
+ if (unit_relative_offset) {
+ assert(m_unit); // Unit must be valid for DW_FORM_ref forms that are compile
+ // unit relative or we will get this wrong
+ s.Printf("{0x%8.8" PRIx64 "}", uvalue + m_unit->GetOffset());
+ }
+}
+
+const char *DWARFFormValue::AsCString() const {
+ SymbolFileDWARF &symbol_file = m_unit->GetSymbolFileDWARF();
+
+ if (m_form == DW_FORM_string) {
+ return m_value.value.cstr;
+ } else if (m_form == DW_FORM_strp) {
+ return symbol_file.GetDWARFContext().getOrLoadStrData().PeekCStr(
+ m_value.value.uval);
+ } else if (m_form == DW_FORM_GNU_str_index) {
+ uint32_t index_size = 4;
+ lldb::offset_t offset = m_value.value.uval * index_size;
+ dw_offset_t str_offset =
+ symbol_file.GetDWARFContext().getOrLoadStrOffsetsData().GetMaxU64(
+ &offset, index_size);
+ return symbol_file.GetDWARFContext().getOrLoadStrData().PeekCStr(
+ str_offset);
+ }
+
+ if (m_form == DW_FORM_strx || m_form == DW_FORM_strx1 ||
+ m_form == DW_FORM_strx2 || m_form == DW_FORM_strx3 ||
+ m_form == DW_FORM_strx4) {
+
+ // The same code as above.
+ uint32_t indexSize = 4;
+ lldb::offset_t offset =
+ m_unit->GetStrOffsetsBase() + m_value.value.uval * indexSize;
+ dw_offset_t strOffset =
+ symbol_file.GetDWARFContext().getOrLoadStrOffsetsData().GetMaxU64(
+ &offset, indexSize);
+ return symbol_file.GetDWARFContext().getOrLoadStrData().PeekCStr(strOffset);
+ }
+
+ if (m_form == DW_FORM_line_strp)
+ return symbol_file.GetDWARFContext().getOrLoadLineStrData().PeekCStr(
+ m_value.value.uval);
+
+ return nullptr;
+}
+
+dw_addr_t DWARFFormValue::Address() const {
+ SymbolFileDWARF &symbol_file = m_unit->GetSymbolFileDWARF();
+
+ if (m_form == DW_FORM_addr)
+ return Unsigned();
+
+ assert(m_unit);
+ assert(m_form == DW_FORM_GNU_addr_index || m_form == DW_FORM_addrx ||
+ m_form == DW_FORM_addrx1 || m_form == DW_FORM_addrx2 ||
+ m_form == DW_FORM_addrx3 || m_form == DW_FORM_addrx4);
+
+ uint32_t index_size = m_unit->GetAddressByteSize();
+ dw_offset_t addr_base = m_unit->GetAddrBase();
+ lldb::offset_t offset = addr_base + m_value.value.uval * index_size;
+ return symbol_file.GetDWARFContext().getOrLoadAddrData().GetMaxU64(
+ &offset, index_size);
+}
+
+DWARFDIE DWARFFormValue::Reference() const {
+ uint64_t value = m_value.value.uval;
+ switch (m_form) {
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_udata:
+ assert(m_unit); // Unit must be valid for DW_FORM_ref forms that are compile
+ // unit relative or we will get this wrong
+ value += m_unit->GetOffset();
+ if (!m_unit->ContainsDIEOffset(value)) {
+ m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError(
+ "DW_FORM_ref* DIE reference 0x%" PRIx64 " is outside of its CU",
+ value);
+ return {};
+ }
+ return const_cast<DWARFUnit *>(m_unit)->GetDIE(value);
+
+ case DW_FORM_ref_addr: {
+ DWARFUnit *ref_cu =
+ m_unit->GetSymbolFileDWARF().DebugInfo()->GetUnitContainingDIEOffset(
+ DIERef::Section::DebugInfo, value);
+ if (!ref_cu) {
+ m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError(
+ "DW_FORM_ref_addr DIE reference 0x%" PRIx64 " has no matching CU",
+ value);
+ return {};
+ }
+ return ref_cu->GetDIE(value);
+ }
+
+ case DW_FORM_ref_sig8: {
+ DWARFTypeUnit *tu =
+ m_unit->GetSymbolFileDWARF().DebugInfo()->GetTypeUnitForHash(value);
+ if (!tu)
+ return {};
+ return tu->GetDIE(tu->GetTypeOffset());
+ }
+
+ default:
+ return {};
+ }
+}
+
+uint64_t DWARFFormValue::Reference(dw_offset_t base_offset) const {
+ uint64_t value = m_value.value.uval;
+ switch (m_form) {
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_udata:
+ return value + base_offset;
+
+ case DW_FORM_ref_addr:
+ case DW_FORM_ref_sig8:
+ case DW_FORM_GNU_ref_alt:
+ return value;
+
+ default:
+ return DW_INVALID_OFFSET;
+ }
+}
+
+const uint8_t *DWARFFormValue::BlockData() const { return m_value.data; }
+
+bool DWARFFormValue::IsBlockForm(const dw_form_t form) {
+ switch (form) {
+ case DW_FORM_exprloc:
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ return true;
+ }
+ return false;
+}
+
+bool DWARFFormValue::IsDataForm(const dw_form_t form) {
+ switch (form) {
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ return true;
+ }
+ return false;
+}
+
+int DWARFFormValue::Compare(const DWARFFormValue &a_value,
+ const DWARFFormValue &b_value) {
+ dw_form_t a_form = a_value.Form();
+ dw_form_t b_form = b_value.Form();
+ if (a_form < b_form)
+ return -1;
+ if (a_form > b_form)
+ return 1;
+ switch (a_form) {
+ case DW_FORM_addr:
+ case DW_FORM_addrx:
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ case DW_FORM_udata:
+ case DW_FORM_ref_addr:
+ case DW_FORM_sec_offset:
+ case DW_FORM_flag_present:
+ case DW_FORM_ref_sig8:
+ case DW_FORM_GNU_addr_index: {
+ uint64_t a = a_value.Unsigned();
+ uint64_t b = b_value.Unsigned();
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+ return 0;
+ }
+
+ case DW_FORM_sdata: {
+ int64_t a = a_value.Signed();
+ int64_t b = b_value.Signed();
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+ return 0;
+ }
+
+ case DW_FORM_string:
+ case DW_FORM_strp:
+ case DW_FORM_GNU_str_index: {
+ const char *a_string = a_value.AsCString();
+ const char *b_string = b_value.AsCString();
+ if (a_string == b_string)
+ return 0;
+ else if (a_string && b_string)
+ return strcmp(a_string, b_string);
+ else if (a_string == nullptr)
+ return -1; // A string is NULL, and B is valid
+ else
+ return 1; // A string valid, and B is NULL
+ }
+
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ case DW_FORM_exprloc: {
+ uint64_t a_len = a_value.Unsigned();
+ uint64_t b_len = b_value.Unsigned();
+ if (a_len < b_len)
+ return -1;
+ if (a_len > b_len)
+ return 1;
+ // The block lengths are the same
+ return memcmp(a_value.BlockData(), b_value.BlockData(), a_value.Unsigned());
+ } break;
+
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_udata: {
+ uint64_t a = a_value.m_value.value.uval;
+ uint64_t b = b_value.m_value.value.uval;
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+ return 0;
+ }
+
+ case DW_FORM_indirect:
+ llvm_unreachable(
+ "This shouldn't happen after the form has been extracted...");
+
+ default:
+ llvm_unreachable("Unhandled DW_FORM");
+ }
+ return -1;
+}
+
+bool DWARFFormValue::FormIsSupported(dw_form_t form) {
+ switch (form) {
+ case DW_FORM_addr:
+ case DW_FORM_addrx:
+ case DW_FORM_rnglistx:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ case DW_FORM_string:
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ case DW_FORM_sdata:
+ case DW_FORM_strp:
+ case DW_FORM_strx:
+ case DW_FORM_strx1:
+ case DW_FORM_strx2:
+ case DW_FORM_strx3:
+ case DW_FORM_strx4:
+ case DW_FORM_udata:
+ case DW_FORM_ref_addr:
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_udata:
+ case DW_FORM_indirect:
+ case DW_FORM_sec_offset:
+ case DW_FORM_exprloc:
+ case DW_FORM_flag_present:
+ case DW_FORM_ref_sig8:
+ case DW_FORM_GNU_str_index:
+ case DW_FORM_GNU_addr_index:
+ case DW_FORM_implicit_const:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h
new file mode 100644
index 000000000000..848db2990ded
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h
@@ -0,0 +1,89 @@
+//===-- DWARFFormValue.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFFormValue_h_
+#define SymbolFileDWARF_DWARFFormValue_h_
+
+#include "DWARFDataExtractor.h"
+#include <stddef.h>
+#include "llvm/ADT/Optional.h"
+
+class DWARFUnit;
+class SymbolFileDWARF;
+class DWARFDIE;
+
+class DWARFFormValue {
+public:
+ typedef struct ValueTypeTag {
+ ValueTypeTag() : value(), data(nullptr) { value.uval = 0; }
+
+ union {
+ uint64_t uval;
+ int64_t sval;
+ const char *cstr;
+ } value;
+ const uint8_t *data;
+ } ValueType;
+
+ enum {
+ eValueTypeInvalid = 0,
+ eValueTypeUnsigned,
+ eValueTypeSigned,
+ eValueTypeCStr,
+ eValueTypeBlock
+ };
+
+ DWARFFormValue() = default;
+ DWARFFormValue(const DWARFUnit *unit) : m_unit(unit) {}
+ DWARFFormValue(const DWARFUnit *unit, dw_form_t form)
+ : m_unit(unit), m_form(form) {}
+ void SetUnit(const DWARFUnit *unit) { m_unit = unit; }
+ dw_form_t Form() const { return m_form; }
+ dw_form_t& FormRef() { return m_form; }
+ void SetForm(dw_form_t form) { m_form = form; }
+ const ValueType &Value() const { return m_value; }
+ ValueType &ValueRef() { return m_value; }
+ void SetValue(const ValueType &val) { m_value = val; }
+
+ void Dump(lldb_private::Stream &s) const;
+ bool ExtractValue(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr);
+ const uint8_t *BlockData() const;
+ static llvm::Optional<uint8_t> GetFixedSize(dw_form_t form,
+ const DWARFUnit *u);
+ llvm::Optional<uint8_t> GetFixedSize() const;
+ DWARFDIE Reference() const;
+ uint64_t Reference(dw_offset_t offset) const;
+ bool Boolean() const { return m_value.value.uval != 0; }
+ uint64_t Unsigned() const { return m_value.value.uval; }
+ void SetUnsigned(uint64_t uval) { m_value.value.uval = uval; }
+ int64_t Signed() const { return m_value.value.sval; }
+ void SetSigned(int64_t sval) { m_value.value.sval = sval; }
+ const char *AsCString() const;
+ dw_addr_t Address() const;
+ bool IsValid() const { return m_form != 0; }
+ bool SkipValue(const lldb_private::DWARFDataExtractor &debug_info_data,
+ lldb::offset_t *offset_ptr) const;
+ static bool SkipValue(const dw_form_t form,
+ const lldb_private::DWARFDataExtractor &debug_info_data,
+ lldb::offset_t *offset_ptr, const DWARFUnit *unit);
+ static bool IsBlockForm(const dw_form_t form);
+ static bool IsDataForm(const dw_form_t form);
+ static int Compare(const DWARFFormValue &a, const DWARFFormValue &b);
+ void Clear();
+ static bool FormIsSupported(dw_form_t form);
+
+protected:
+ // Compile unit where m_value was located.
+ // It may be different from compile unit where m_value refers to.
+ const DWARFUnit *m_unit = nullptr; // Unit for this form
+ dw_form_t m_form = 0; // Form for this value
+ ValueType m_value; // Contains all data for the form
+};
+
+#endif // SymbolFileDWARF_DWARFFormValue_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp
new file mode 100644
index 000000000000..c122f756e81a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp
@@ -0,0 +1,66 @@
+//===-- DWARFIndex.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/SymbolFile/DWARF/DWARFIndex.h"
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDIE.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+DWARFIndex::~DWARFIndex() = default;
+
+void DWARFIndex::ProcessFunctionDIE(llvm::StringRef name, DIERef ref,
+ SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) {
+ DWARFDIE die = dwarf.GetDIE(ref);
+ if (!die) {
+ ReportInvalidDIERef(ref, name);
+ return;
+ }
+
+ // Exit early if we're searching exclusively for methods or selectors and
+ // we have a context specified (no methods in namespaces).
+ uint32_t looking_for_nonmethods =
+ name_type_mask & ~(eFunctionNameTypeMethod | eFunctionNameTypeSelector);
+ if (!looking_for_nonmethods && parent_decl_ctx.IsValid())
+ return;
+
+ // Otherwise, we need to also check that the context matches. If it does not
+ // match, we do nothing.
+ if (!SymbolFileDWARF::DIEInDeclContext(&parent_decl_ctx, die))
+ return;
+
+ // In case of a full match, we just insert everything we find.
+ if (name_type_mask & eFunctionNameTypeFull) {
+ dies.push_back(die);
+ return;
+ }
+
+ // If looking for ObjC selectors, we need to also check if the name is a
+ // possible selector.
+ if (name_type_mask & eFunctionNameTypeSelector &&
+ ObjCLanguage::IsPossibleObjCMethodName(die.GetName())) {
+ dies.push_back(die);
+ return;
+ }
+
+ bool looking_for_methods = name_type_mask & lldb::eFunctionNameTypeMethod;
+ bool looking_for_functions = name_type_mask & lldb::eFunctionNameTypeBase;
+ if (looking_for_methods || looking_for_functions) {
+ // If we're looking for either methods or functions, we definitely want this
+ // die. Otherwise, only keep it if the die type matches what we are
+ // searching for.
+ if ((looking_for_methods && looking_for_functions) ||
+ looking_for_methods == die.IsMethod())
+ dies.push_back(die);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
new file mode 100644
index 000000000000..e3c7c5e63ac8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
@@ -0,0 +1,66 @@
+//===-- DWARFIndex.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_DWARFINDEX_H
+#define LLDB_DWARFINDEX_H
+
+#include "Plugins/SymbolFile/DWARF/DIERef.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDIE.h"
+#include "Plugins/SymbolFile/DWARF/DWARFFormValue.h"
+
+class DWARFDeclContext;
+class DWARFDIE;
+
+namespace lldb_private {
+class DWARFIndex {
+public:
+ DWARFIndex(Module &module) : m_module(module) {}
+ virtual ~DWARFIndex();
+
+ virtual void Preload() = 0;
+
+ /// Finds global variables with the given base name. Any additional filtering
+ /// (e.g., to only retrieve variables from a given context) should be done by
+ /// the consumer.
+ virtual void GetGlobalVariables(ConstString basename, DIEArray &offsets) = 0;
+
+ virtual void GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) = 0;
+ virtual void GetGlobalVariables(const DWARFUnit &cu, DIEArray &offsets) = 0;
+ virtual void GetObjCMethods(ConstString class_name, DIEArray &offsets) = 0;
+ virtual void GetCompleteObjCClass(ConstString class_name,
+ bool must_be_implementation,
+ DIEArray &offsets) = 0;
+ virtual void GetTypes(ConstString name, DIEArray &offsets) = 0;
+ virtual void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) = 0;
+ virtual void GetNamespaces(ConstString name, DIEArray &offsets) = 0;
+ virtual void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) = 0;
+ virtual void GetFunctions(const RegularExpression &regex,
+ DIEArray &offsets) = 0;
+
+ virtual void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) = 0;
+ virtual void Dump(Stream &s) = 0;
+
+protected:
+ Module &m_module;
+
+ /// Helper function implementing common logic for processing function dies. If
+ /// the function given by "ref" matches search criteria given by
+ /// "parent_decl_ctx" and "name_type_mask", it is inserted into the "dies"
+ /// vector.
+ void ProcessFunctionDIE(llvm::StringRef name, DIERef ref,
+ SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask, std::vector<DWARFDIE> &dies);
+};
+} // namespace lldb_private
+
+#endif // LLDB_DWARFINDEX_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp
new file mode 100644
index 000000000000..fcc031bf1ea0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp
@@ -0,0 +1,23 @@
+//===-- DWARFTypeUnit.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFTypeUnit.h"
+
+#include "SymbolFileDWARF.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void DWARFTypeUnit::Dump(Stream *s) const {
+ s->Printf("0x%8.8x: Type Unit: length = 0x%8.8x, version = 0x%4.4x, "
+ "abbr_offset = 0x%8.8x, addr_size = 0x%2.2x (next CU at "
+ "{0x%8.8x})\n",
+ GetOffset(), GetLength(), GetVersion(), GetAbbrevOffset(),
+ GetAddressByteSize(), GetNextUnitOffset());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h
new file mode 100644
index 000000000000..6ff73ecd8efa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h
@@ -0,0 +1,37 @@
+//===-- DWARFTypeUnit.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFTypeUnit_h_
+#define SymbolFileDWARF_DWARFTypeUnit_h_
+
+#include "DWARFUnit.h"
+#include "llvm/Support/Error.h"
+
+class DWARFTypeUnit : public DWARFUnit {
+public:
+ void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) override {}
+
+ void Dump(lldb_private::Stream *s) const override;
+
+ uint64_t GetTypeHash() { return m_header.GetTypeHash(); }
+
+ dw_offset_t GetTypeOffset() { return GetOffset() + m_header.GetTypeOffset(); }
+
+ static bool classof(const DWARFUnit *unit) { return unit->IsTypeUnit(); }
+
+private:
+ DWARFTypeUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid,
+ const DWARFUnitHeader &header,
+ const DWARFAbbreviationDeclarationSet &abbrevs,
+ DIERef::Section section)
+ : DWARFUnit(dwarf, uid, header, abbrevs, section) {}
+
+ friend class DWARFUnit;
+};
+
+#endif // SymbolFileDWARF_DWARFTypeUnit_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
new file mode 100644
index 000000000000..33e83d1fe57f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
@@ -0,0 +1,878 @@
+//===-- DWARFUnit.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFUnit.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+#include "llvm/Object/Error.h"
+
+#include "DWARFCompileUnit.h"
+#include "DWARFDebugAranges.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFTypeUnit.h"
+#include "LogChannelDWARF.h"
+#include "SymbolFileDWARFDwo.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace std;
+
+extern int g_verbose;
+
+DWARFUnit::DWARFUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid,
+ const DWARFUnitHeader &header,
+ const DWARFAbbreviationDeclarationSet &abbrevs,
+ DIERef::Section section)
+ : UserID(uid), m_dwarf(dwarf), m_header(header), m_abbrevs(&abbrevs),
+ m_cancel_scopes(false), m_section(section) {}
+
+DWARFUnit::~DWARFUnit() = default;
+
+// Parses first DIE of a compile unit.
+void DWARFUnit::ExtractUnitDIEIfNeeded() {
+ {
+ llvm::sys::ScopedReader lock(m_first_die_mutex);
+ if (m_first_die)
+ return; // Already parsed
+ }
+ llvm::sys::ScopedWriter lock(m_first_die_mutex);
+ if (m_first_die)
+ return; // Already parsed
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%8.8x: DWARFUnit::ExtractUnitDIEIfNeeded()",
+ GetOffset());
+
+ // Set the offset to that of the first DIE and calculate the start of the
+ // next compilation unit header.
+ lldb::offset_t offset = GetFirstDIEOffset();
+
+ // We are in our compile unit, parse starting at the offset we were told to
+ // parse
+ const DWARFDataExtractor &data = GetData();
+ if (offset < GetNextUnitOffset() &&
+ m_first_die.Extract(data, this, &offset)) {
+ AddUnitDIE(m_first_die);
+ return;
+ }
+}
+
+// Parses a compile unit and indexes its DIEs if it hasn't already been done.
+// It will leave this compile unit extracted forever.
+void DWARFUnit::ExtractDIEsIfNeeded() {
+ m_cancel_scopes = true;
+
+ {
+ llvm::sys::ScopedReader lock(m_die_array_mutex);
+ if (!m_die_array.empty())
+ return; // Already parsed
+ }
+ llvm::sys::ScopedWriter lock(m_die_array_mutex);
+ if (!m_die_array.empty())
+ return; // Already parsed
+
+ ExtractDIEsRWLocked();
+}
+
+// Parses a compile unit and indexes its DIEs if it hasn't already been done.
+// It will clear this compile unit after returned instance gets out of scope,
+// no other ScopedExtractDIEs instance is running for this compile unit
+// and no ExtractDIEsIfNeeded() has been executed during this ScopedExtractDIEs
+// lifetime.
+DWARFUnit::ScopedExtractDIEs DWARFUnit::ExtractDIEsScoped() {
+ ScopedExtractDIEs scoped(*this);
+
+ {
+ llvm::sys::ScopedReader lock(m_die_array_mutex);
+ if (!m_die_array.empty())
+ return scoped; // Already parsed
+ }
+ llvm::sys::ScopedWriter lock(m_die_array_mutex);
+ if (!m_die_array.empty())
+ return scoped; // Already parsed
+
+ // Otherwise m_die_array would be already populated.
+ lldbassert(!m_cancel_scopes);
+
+ ExtractDIEsRWLocked();
+ scoped.m_clear_dies = true;
+ return scoped;
+}
+
+DWARFUnit::ScopedExtractDIEs::ScopedExtractDIEs(DWARFUnit &cu) : m_cu(&cu) {
+ m_cu->m_die_array_scoped_mutex.lock_shared();
+}
+
+DWARFUnit::ScopedExtractDIEs::~ScopedExtractDIEs() {
+ if (!m_cu)
+ return;
+ m_cu->m_die_array_scoped_mutex.unlock_shared();
+ if (!m_clear_dies || m_cu->m_cancel_scopes)
+ return;
+ // Be sure no other ScopedExtractDIEs is running anymore.
+ llvm::sys::ScopedWriter lock_scoped(m_cu->m_die_array_scoped_mutex);
+ llvm::sys::ScopedWriter lock(m_cu->m_die_array_mutex);
+ if (m_cu->m_cancel_scopes)
+ return;
+ m_cu->ClearDIEsRWLocked();
+}
+
+DWARFUnit::ScopedExtractDIEs::ScopedExtractDIEs(ScopedExtractDIEs &&rhs)
+ : m_cu(rhs.m_cu), m_clear_dies(rhs.m_clear_dies) {
+ rhs.m_cu = nullptr;
+}
+
+DWARFUnit::ScopedExtractDIEs &DWARFUnit::ScopedExtractDIEs::operator=(
+ DWARFUnit::ScopedExtractDIEs &&rhs) {
+ m_cu = rhs.m_cu;
+ rhs.m_cu = nullptr;
+ m_clear_dies = rhs.m_clear_dies;
+ return *this;
+}
+
+// Parses a compile unit and indexes its DIEs, m_die_array_mutex must be
+// held R/W and m_die_array must be empty.
+void DWARFUnit::ExtractDIEsRWLocked() {
+ llvm::sys::ScopedWriter first_die_lock(m_first_die_mutex);
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%8.8x: DWARFUnit::ExtractDIEsIfNeeded()",
+ GetOffset());
+
+ // Set the offset to that of the first DIE and calculate the start of the
+ // next compilation unit header.
+ lldb::offset_t offset = GetFirstDIEOffset();
+ lldb::offset_t next_cu_offset = GetNextUnitOffset();
+
+ DWARFDebugInfoEntry die;
+
+ uint32_t depth = 0;
+ // We are in our compile unit, parse starting at the offset we were told to
+ // parse
+ const DWARFDataExtractor &data = GetData();
+ std::vector<uint32_t> die_index_stack;
+ die_index_stack.reserve(32);
+ die_index_stack.push_back(0);
+ bool prev_die_had_children = false;
+ while (offset < next_cu_offset && die.Extract(data, this, &offset)) {
+ const bool null_die = die.IsNULL();
+ if (depth == 0) {
+ assert(m_die_array.empty() && "Compile unit DIE already added");
+
+ // The average bytes per DIE entry has been seen to be around 14-20 so
+ // lets pre-reserve half of that since we are now stripping the NULL
+ // tags.
+
+ // Only reserve the memory if we are adding children of the main
+ // compile unit DIE. The compile unit DIE is always the first entry, so
+ // if our size is 1, then we are adding the first compile unit child
+ // DIE and should reserve the memory.
+ m_die_array.reserve(GetDebugInfoSize() / 24);
+ m_die_array.push_back(die);
+
+ if (!m_first_die)
+ AddUnitDIE(m_die_array.front());
+
+ // With -fsplit-dwarf-inlining, clang will emit non-empty skeleton compile
+ // units. We are not able to access these DIE *and* the dwo file
+ // simultaneously. We also don't need to do that as the dwo file will
+ // contain a superset of information. So, we don't even attempt to parse
+ // any remaining DIEs.
+ if (m_dwo_symbol_file) {
+ m_die_array.front().SetHasChildren(false);
+ break;
+ }
+
+ } else {
+ if (null_die) {
+ if (prev_die_had_children) {
+ // This will only happen if a DIE says is has children but all it
+ // contains is a NULL tag. Since we are removing the NULL DIEs from
+ // the list (saves up to 25% in C++ code), we need a way to let the
+ // DIE know that it actually doesn't have children.
+ if (!m_die_array.empty())
+ m_die_array.back().SetHasChildren(false);
+ }
+ } else {
+ die.SetParentIndex(m_die_array.size() - die_index_stack[depth - 1]);
+
+ if (die_index_stack.back())
+ m_die_array[die_index_stack.back()].SetSiblingIndex(
+ m_die_array.size() - die_index_stack.back());
+
+ // Only push the DIE if it isn't a NULL DIE
+ m_die_array.push_back(die);
+ }
+ }
+
+ if (null_die) {
+ // NULL DIE.
+ if (!die_index_stack.empty())
+ die_index_stack.pop_back();
+
+ if (depth > 0)
+ --depth;
+ prev_die_had_children = false;
+ } else {
+ die_index_stack.back() = m_die_array.size() - 1;
+ // Normal DIE
+ const bool die_has_children = die.HasChildren();
+ if (die_has_children) {
+ die_index_stack.push_back(0);
+ ++depth;
+ }
+ prev_die_had_children = die_has_children;
+ }
+
+ if (depth == 0)
+ break; // We are done with this compile unit!
+ }
+
+ if (!m_die_array.empty()) {
+ if (m_first_die) {
+ // Only needed for the assertion.
+ m_first_die.SetHasChildren(m_die_array.front().HasChildren());
+ lldbassert(m_first_die == m_die_array.front());
+ }
+ m_first_die = m_die_array.front();
+ }
+
+ m_die_array.shrink_to_fit();
+
+ if (m_dwo_symbol_file) {
+ DWARFUnit *dwo_cu = m_dwo_symbol_file->GetCompileUnit();
+ dwo_cu->ExtractDIEsIfNeeded();
+ }
+}
+
+// This is used when a split dwarf is enabled.
+// A skeleton compilation unit may contain the DW_AT_str_offsets_base attribute
+// that points to the first string offset of the CU contribution to the
+// .debug_str_offsets. At the same time, the corresponding split debug unit also
+// may use DW_FORM_strx* forms pointing to its own .debug_str_offsets.dwo and
+// for that case, we should find the offset (skip the section header).
+static void SetDwoStrOffsetsBase(DWARFUnit *dwo_cu) {
+ lldb::offset_t baseOffset = 0;
+
+ const DWARFDataExtractor &strOffsets =
+ dwo_cu->GetSymbolFileDWARF().GetDWARFContext().getOrLoadStrOffsetsData();
+ uint64_t length = strOffsets.GetU32(&baseOffset);
+ if (length == 0xffffffff)
+ length = strOffsets.GetU64(&baseOffset);
+
+ // Check version.
+ if (strOffsets.GetU16(&baseOffset) < 5)
+ return;
+
+ // Skip padding.
+ baseOffset += 2;
+
+ dwo_cu->SetStrOffsetsBase(baseOffset);
+}
+
+// m_die_array_mutex must be already held as read/write.
+void DWARFUnit::AddUnitDIE(const DWARFDebugInfoEntry &cu_die) {
+ llvm::Optional<uint64_t> addr_base, gnu_addr_base, ranges_base,
+ gnu_ranges_base;
+
+ DWARFAttributes attributes;
+ size_t num_attributes = cu_die.GetAttributes(this, attributes);
+ for (size_t i = 0; i < num_attributes; ++i) {
+ dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (!attributes.ExtractFormValueAtIndex(i, form_value))
+ continue;
+ switch (attr) {
+ case DW_AT_addr_base:
+ addr_base = form_value.Unsigned();
+ SetAddrBase(*addr_base);
+ break;
+ case DW_AT_rnglists_base:
+ ranges_base = form_value.Unsigned();
+ SetRangesBase(*ranges_base);
+ break;
+ case DW_AT_str_offsets_base:
+ SetStrOffsetsBase(form_value.Unsigned());
+ break;
+ case DW_AT_low_pc:
+ SetBaseAddress(form_value.Address());
+ break;
+ case DW_AT_entry_pc:
+ // If the value was already set by DW_AT_low_pc, don't update it.
+ if (m_base_addr == LLDB_INVALID_ADDRESS)
+ SetBaseAddress(form_value.Address());
+ break;
+ case DW_AT_stmt_list:
+ m_line_table_offset = form_value.Unsigned();
+ break;
+ case DW_AT_GNU_addr_base:
+ gnu_addr_base = form_value.Unsigned();
+ break;
+ case DW_AT_GNU_ranges_base:
+ gnu_ranges_base = form_value.Unsigned();
+ break;
+ }
+ }
+
+ std::unique_ptr<SymbolFileDWARFDwo> dwo_symbol_file =
+ m_dwarf.GetDwoSymbolFileForCompileUnit(*this, cu_die);
+ if (!dwo_symbol_file)
+ return;
+
+ DWARFUnit *dwo_cu = dwo_symbol_file->GetCompileUnit();
+ if (!dwo_cu)
+ return; // Can't fetch the compile unit from the dwo file.
+
+ DWARFBaseDIE dwo_cu_die = dwo_cu->GetUnitDIEOnly();
+ if (!dwo_cu_die.IsValid())
+ return; // Can't fetch the compile unit DIE from the dwo file.
+
+ uint64_t main_dwo_id =
+ cu_die.GetAttributeValueAsUnsigned(this, DW_AT_GNU_dwo_id, 0);
+ uint64_t sub_dwo_id =
+ dwo_cu_die.GetAttributeValueAsUnsigned(DW_AT_GNU_dwo_id, 0);
+ if (main_dwo_id != sub_dwo_id)
+ return; // The 2 dwo ID isn't match. Don't use the dwo file as it belongs to
+ // a differectn compilation.
+
+ m_dwo_symbol_file = std::move(dwo_symbol_file);
+
+ // Here for DWO CU we want to use the address base set in the skeleton unit
+ // (DW_AT_addr_base) if it is available and use the DW_AT_GNU_addr_base
+ // otherwise. We do that because pre-DWARF v5 could use the DW_AT_GNU_*
+ // attributes which were applicable to the DWO units. The corresponding
+ // DW_AT_* attributes standardized in DWARF v5 are also applicable to the main
+ // unit in contrast.
+ if (addr_base)
+ dwo_cu->SetAddrBase(*addr_base);
+ else if (gnu_addr_base)
+ dwo_cu->SetAddrBase(*gnu_addr_base);
+
+ if (ranges_base)
+ dwo_cu->SetRangesBase(*ranges_base);
+ else if (gnu_ranges_base)
+ dwo_cu->SetRangesBase(*gnu_ranges_base);
+
+ for (size_t i = 0; i < m_dwo_symbol_file->DebugInfo()->GetNumUnits(); ++i) {
+ DWARFUnit *unit = m_dwo_symbol_file->DebugInfo()->GetUnitAtIndex(i);
+ SetDwoStrOffsetsBase(unit);
+ }
+}
+
+DWARFDIE DWARFUnit::LookupAddress(const dw_addr_t address) {
+ if (DIE()) {
+ const DWARFDebugAranges &func_aranges = GetFunctionAranges();
+
+ // Re-check the aranges auto pointer contents in case it was created above
+ if (!func_aranges.IsEmpty())
+ return GetDIE(func_aranges.FindAddress(address));
+ }
+ return DWARFDIE();
+}
+
+size_t DWARFUnit::AppendDIEsWithTag(const dw_tag_t tag,
+ std::vector<DWARFDIE> &dies,
+ uint32_t depth) const {
+ size_t old_size = dies.size();
+ {
+ llvm::sys::ScopedReader lock(m_die_array_mutex);
+ DWARFDebugInfoEntry::const_iterator pos;
+ DWARFDebugInfoEntry::const_iterator end = m_die_array.end();
+ for (pos = m_die_array.begin(); pos != end; ++pos) {
+ if (pos->Tag() == tag)
+ dies.emplace_back(this, &(*pos));
+ }
+ }
+
+ // Return the number of DIEs added to the collection
+ return dies.size() - old_size;
+}
+
+size_t DWARFUnit::GetDebugInfoSize() const {
+ return GetLengthByteSize() + GetLength() - GetHeaderByteSize();
+}
+
+const DWARFAbbreviationDeclarationSet *DWARFUnit::GetAbbreviations() const {
+ return m_abbrevs;
+}
+
+dw_offset_t DWARFUnit::GetAbbrevOffset() const {
+ return m_abbrevs ? m_abbrevs->GetOffset() : DW_INVALID_OFFSET;
+}
+
+dw_offset_t DWARFUnit::GetLineTableOffset() {
+ ExtractUnitDIEIfNeeded();
+ return m_line_table_offset;
+}
+
+void DWARFUnit::SetAddrBase(dw_addr_t addr_base) { m_addr_base = addr_base; }
+
+void DWARFUnit::SetRangesBase(dw_addr_t ranges_base) {
+ m_ranges_base = ranges_base;
+}
+
+void DWARFUnit::SetStrOffsetsBase(dw_offset_t str_offsets_base) {
+ m_str_offsets_base = str_offsets_base;
+}
+
+// It may be called only with m_die_array_mutex held R/W.
+void DWARFUnit::ClearDIEsRWLocked() {
+ m_die_array.clear();
+ m_die_array.shrink_to_fit();
+
+ if (m_dwo_symbol_file)
+ m_dwo_symbol_file->GetCompileUnit()->ClearDIEsRWLocked();
+}
+
+lldb::ByteOrder DWARFUnit::GetByteOrder() const {
+ return m_dwarf.GetObjectFile()->GetByteOrder();
+}
+
+TypeSystem *DWARFUnit::GetTypeSystem() {
+ return m_dwarf.GetTypeSystemForLanguage(GetLanguageType());
+}
+
+void DWARFUnit::SetBaseAddress(dw_addr_t base_addr) { m_base_addr = base_addr; }
+
+// Compare function DWARFDebugAranges::Range structures
+static bool CompareDIEOffset(const DWARFDebugInfoEntry &die,
+ const dw_offset_t die_offset) {
+ return die.GetOffset() < die_offset;
+}
+
+// GetDIE()
+//
+// Get the DIE (Debug Information Entry) with the specified offset by first
+// checking if the DIE is contained within this compile unit and grabbing the
+// DIE from this compile unit. Otherwise we grab the DIE from the DWARF file.
+DWARFDIE
+DWARFUnit::GetDIE(dw_offset_t die_offset) {
+ if (die_offset != DW_INVALID_OFFSET) {
+ if (GetDwoSymbolFile())
+ return GetDwoSymbolFile()->GetCompileUnit()->GetDIE(die_offset);
+
+ if (ContainsDIEOffset(die_offset)) {
+ ExtractDIEsIfNeeded();
+ DWARFDebugInfoEntry::const_iterator end = m_die_array.cend();
+ DWARFDebugInfoEntry::const_iterator pos =
+ lower_bound(m_die_array.cbegin(), end, die_offset, CompareDIEOffset);
+ if (pos != end) {
+ if (die_offset == (*pos).GetOffset())
+ return DWARFDIE(this, &(*pos));
+ }
+ } else
+ GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError(
+ "GetDIE for DIE 0x%" PRIx32 " is outside of its CU 0x%" PRIx32,
+ die_offset, GetOffset());
+ }
+ return DWARFDIE(); // Not found
+}
+
+DWARFUnit &DWARFUnit::GetNonSkeletonUnit() {
+ if (SymbolFileDWARFDwo *dwo = GetDwoSymbolFile())
+ return *dwo->GetCompileUnit();
+ return *this;
+}
+
+uint8_t DWARFUnit::GetAddressByteSize(const DWARFUnit *cu) {
+ if (cu)
+ return cu->GetAddressByteSize();
+ return DWARFUnit::GetDefaultAddressSize();
+}
+
+uint8_t DWARFUnit::GetDefaultAddressSize() { return 4; }
+
+void *DWARFUnit::GetUserData() const { return m_user_data; }
+
+void DWARFUnit::SetUserData(void *d) {
+ m_user_data = d;
+ if (m_dwo_symbol_file)
+ m_dwo_symbol_file->GetCompileUnit()->SetUserData(d);
+}
+
+bool DWARFUnit::Supports_DW_AT_APPLE_objc_complete_type() {
+ return GetProducer() != eProducerLLVMGCC;
+}
+
+bool DWARFUnit::DW_AT_decl_file_attributes_are_invalid() {
+ // llvm-gcc makes completely invalid decl file attributes and won't ever be
+ // fixed, so we need to know to ignore these.
+ return GetProducer() == eProducerLLVMGCC;
+}
+
+bool DWARFUnit::Supports_unnamed_objc_bitfields() {
+ if (GetProducer() == eProducerClang) {
+ const uint32_t major_version = GetProducerVersionMajor();
+ return major_version > 425 ||
+ (major_version == 425 && GetProducerVersionUpdate() >= 13);
+ }
+ return true; // Assume all other compilers didn't have incorrect ObjC bitfield
+ // info
+}
+
+void DWARFUnit::ParseProducerInfo() {
+ m_producer_version_major = UINT32_MAX;
+ m_producer_version_minor = UINT32_MAX;
+ m_producer_version_update = UINT32_MAX;
+
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+ if (die) {
+
+ const char *producer_cstr =
+ die->GetAttributeValueAsString(this, DW_AT_producer, nullptr);
+ if (producer_cstr) {
+ RegularExpression llvm_gcc_regex(
+ llvm::StringRef("^4\\.[012]\\.[01] \\(Based on Apple "
+ "Inc\\. build [0-9]+\\) \\(LLVM build "
+ "[\\.0-9]+\\)$"));
+ if (llvm_gcc_regex.Execute(llvm::StringRef(producer_cstr))) {
+ m_producer = eProducerLLVMGCC;
+ } else if (strstr(producer_cstr, "clang")) {
+ static RegularExpression g_clang_version_regex(
+ llvm::StringRef("clang-([0-9]+)\\.([0-9]+)\\.([0-9]+)"));
+ RegularExpression::Match regex_match(3);
+ if (g_clang_version_regex.Execute(llvm::StringRef(producer_cstr),
+ &regex_match)) {
+ std::string str;
+ if (regex_match.GetMatchAtIndex(producer_cstr, 1, str))
+ m_producer_version_major =
+ StringConvert::ToUInt32(str.c_str(), UINT32_MAX, 10);
+ if (regex_match.GetMatchAtIndex(producer_cstr, 2, str))
+ m_producer_version_minor =
+ StringConvert::ToUInt32(str.c_str(), UINT32_MAX, 10);
+ if (regex_match.GetMatchAtIndex(producer_cstr, 3, str))
+ m_producer_version_update =
+ StringConvert::ToUInt32(str.c_str(), UINT32_MAX, 10);
+ }
+ m_producer = eProducerClang;
+ } else if (strstr(producer_cstr, "GNU"))
+ m_producer = eProducerGCC;
+ }
+ }
+ if (m_producer == eProducerInvalid)
+ m_producer = eProcucerOther;
+}
+
+DWARFProducer DWARFUnit::GetProducer() {
+ if (m_producer == eProducerInvalid)
+ ParseProducerInfo();
+ return m_producer;
+}
+
+uint32_t DWARFUnit::GetProducerVersionMajor() {
+ if (m_producer_version_major == 0)
+ ParseProducerInfo();
+ return m_producer_version_major;
+}
+
+uint32_t DWARFUnit::GetProducerVersionMinor() {
+ if (m_producer_version_minor == 0)
+ ParseProducerInfo();
+ return m_producer_version_minor;
+}
+
+uint32_t DWARFUnit::GetProducerVersionUpdate() {
+ if (m_producer_version_update == 0)
+ ParseProducerInfo();
+ return m_producer_version_update;
+}
+LanguageType DWARFUnit::LanguageTypeFromDWARF(uint64_t val) {
+ // Note: user languages between lo_user and hi_user must be handled
+ // explicitly here.
+ switch (val) {
+ case DW_LANG_Mips_Assembler:
+ return eLanguageTypeMipsAssembler;
+ case DW_LANG_GOOGLE_RenderScript:
+ return eLanguageTypeExtRenderScript;
+ default:
+ return static_cast<LanguageType>(val);
+ }
+}
+
+LanguageType DWARFUnit::GetLanguageType() {
+ if (m_language_type != eLanguageTypeUnknown)
+ return m_language_type;
+
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+ if (die)
+ m_language_type = LanguageTypeFromDWARF(
+ die->GetAttributeValueAsUnsigned(this, DW_AT_language, 0));
+ return m_language_type;
+}
+
+bool DWARFUnit::GetIsOptimized() {
+ if (m_is_optimized == eLazyBoolCalculate) {
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+ if (die) {
+ m_is_optimized = eLazyBoolNo;
+ if (die->GetAttributeValueAsUnsigned(this, DW_AT_APPLE_optimized, 0) ==
+ 1) {
+ m_is_optimized = eLazyBoolYes;
+ }
+ }
+ }
+ return m_is_optimized == eLazyBoolYes;
+}
+
+FileSpec::Style DWARFUnit::GetPathStyle() {
+ if (!m_comp_dir)
+ ComputeCompDirAndGuessPathStyle();
+ return m_comp_dir->GetPathStyle();
+}
+
+const FileSpec &DWARFUnit::GetCompilationDirectory() {
+ if (!m_comp_dir)
+ ComputeCompDirAndGuessPathStyle();
+ return *m_comp_dir;
+}
+
+const FileSpec &DWARFUnit::GetAbsolutePath() {
+ if (!m_file_spec)
+ ComputeAbsolutePath();
+ return *m_file_spec;
+}
+
+FileSpec DWARFUnit::GetFile(size_t file_idx) {
+ return m_dwarf.GetFile(*this, file_idx);
+}
+
+// DWARF2/3 suggests the form hostname:pathname for compilation directory.
+// Remove the host part if present.
+static llvm::StringRef
+removeHostnameFromPathname(llvm::StringRef path_from_dwarf) {
+ llvm::StringRef host, path;
+ std::tie(host, path) = path_from_dwarf.split(':');
+
+ if (host.contains('/'))
+ return path_from_dwarf;
+
+ // check whether we have a windows path, and so the first character is a
+ // drive-letter not a hostname.
+ if (host.size() == 1 && llvm::isAlpha(host[0]) && path.startswith("\\"))
+ return path_from_dwarf;
+
+ return path;
+}
+
+static FileSpec resolveCompDir(const FileSpec &path) {
+ bool is_symlink = SymbolFileDWARF::GetSymlinkPaths().FindFileIndex(
+ 0, path, /*full*/ true) != UINT32_MAX;
+
+ if (!is_symlink)
+ return path;
+
+ namespace fs = llvm::sys::fs;
+ if (fs::get_file_type(path.GetPath(), false) != fs::file_type::symlink_file)
+ return path;
+
+ FileSpec resolved_symlink;
+ const auto error = FileSystem::Instance().Readlink(path, resolved_symlink);
+ if (error.Success())
+ return resolved_symlink;
+
+ return path;
+}
+
+void DWARFUnit::ComputeCompDirAndGuessPathStyle() {
+ m_comp_dir = FileSpec();
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+ if (!die)
+ return;
+
+ llvm::StringRef comp_dir = removeHostnameFromPathname(
+ die->GetAttributeValueAsString(this, DW_AT_comp_dir, nullptr));
+ if (!comp_dir.empty()) {
+ FileSpec::Style comp_dir_style =
+ FileSpec::GuessPathStyle(comp_dir).getValueOr(FileSpec::Style::native);
+ m_comp_dir = resolveCompDir(FileSpec(comp_dir, comp_dir_style));
+ } else {
+ // Try to detect the style based on the DW_AT_name attribute, but just store
+ // the detected style in the m_comp_dir field.
+ const char *name =
+ die->GetAttributeValueAsString(this, DW_AT_name, nullptr);
+ m_comp_dir = FileSpec(
+ "", FileSpec::GuessPathStyle(name).getValueOr(FileSpec::Style::native));
+ }
+}
+
+void DWARFUnit::ComputeAbsolutePath() {
+ m_file_spec = FileSpec();
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+ if (!die)
+ return;
+
+ m_file_spec =
+ FileSpec(die->GetAttributeValueAsString(this, DW_AT_name, nullptr),
+ GetPathStyle());
+
+ if (m_file_spec->IsRelative())
+ m_file_spec->MakeAbsolute(GetCompilationDirectory());
+}
+
+SymbolFileDWARFDwo *DWARFUnit::GetDwoSymbolFile() const {
+ return m_dwo_symbol_file.get();
+}
+
+const DWARFDebugAranges &DWARFUnit::GetFunctionAranges() {
+ if (m_func_aranges_up == nullptr) {
+ m_func_aranges_up.reset(new DWARFDebugAranges());
+ const DWARFDebugInfoEntry *die = DIEPtr();
+ if (die)
+ die->BuildFunctionAddressRangeTable(this, m_func_aranges_up.get());
+
+ if (m_dwo_symbol_file) {
+ DWARFUnit *dwo_cu = m_dwo_symbol_file->GetCompileUnit();
+ const DWARFDebugInfoEntry *dwo_die = dwo_cu->DIEPtr();
+ if (dwo_die)
+ dwo_die->BuildFunctionAddressRangeTable(dwo_cu,
+ m_func_aranges_up.get());
+ }
+
+ const bool minimize = false;
+ m_func_aranges_up->Sort(minimize);
+ }
+ return *m_func_aranges_up;
+}
+
+llvm::Expected<DWARFUnitHeader>
+DWARFUnitHeader::extract(const DWARFDataExtractor &data, DIERef::Section section,
+ lldb::offset_t *offset_ptr) {
+ DWARFUnitHeader header;
+ header.m_offset = *offset_ptr;
+ header.m_length = data.GetDWARFInitialLength(offset_ptr);
+ header.m_version = data.GetU16(offset_ptr);
+ if (header.m_version == 5) {
+ header.m_unit_type = data.GetU8(offset_ptr);
+ header.m_addr_size = data.GetU8(offset_ptr);
+ header.m_abbr_offset = data.GetDWARFOffset(offset_ptr);
+ if (header.m_unit_type == llvm::dwarf::DW_UT_skeleton)
+ header.m_dwo_id = data.GetU64(offset_ptr);
+ } else {
+ header.m_abbr_offset = data.GetDWARFOffset(offset_ptr);
+ header.m_addr_size = data.GetU8(offset_ptr);
+ header.m_unit_type =
+ section == DIERef::Section::DebugTypes ? DW_UT_type : DW_UT_compile;
+ }
+
+ if (header.IsTypeUnit()) {
+ header.m_type_hash = data.GetU64(offset_ptr);
+ header.m_type_offset = data.GetDWARFOffset(offset_ptr);
+ }
+
+ bool length_OK = data.ValidOffset(header.GetNextUnitOffset() - 1);
+ bool version_OK = SymbolFileDWARF::SupportedVersion(header.m_version);
+ bool addr_size_OK = (header.m_addr_size == 4) || (header.m_addr_size == 8);
+ bool type_offset_OK =
+ !header.IsTypeUnit() || (header.m_type_offset <= header.GetLength());
+
+ if (!length_OK)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid unit length");
+ if (!version_OK)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Unsupported unit version");
+ if (!addr_size_OK)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid unit address size");
+ if (!type_offset_OK)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Type offset out of range");
+
+ return header;
+}
+
+llvm::Expected<DWARFUnitSP>
+DWARFUnit::extract(SymbolFileDWARF &dwarf, user_id_t uid,
+ const DWARFDataExtractor &debug_info,
+ DIERef::Section section, lldb::offset_t *offset_ptr) {
+ assert(debug_info.ValidOffset(*offset_ptr));
+
+ auto expected_header =
+ DWARFUnitHeader::extract(debug_info, section, offset_ptr);
+ if (!expected_header)
+ return expected_header.takeError();
+
+ const DWARFDebugAbbrev *abbr = dwarf.DebugAbbrev();
+ if (!abbr)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "No debug_abbrev data");
+
+ bool abbr_offset_OK =
+ dwarf.GetDWARFContext().getOrLoadAbbrevData().ValidOffset(
+ expected_header->GetAbbrOffset());
+ if (!abbr_offset_OK)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Abbreviation offset for unit is not valid");
+
+ const DWARFAbbreviationDeclarationSet *abbrevs =
+ abbr->GetAbbreviationDeclarationSet(expected_header->GetAbbrOffset());
+ if (!abbrevs)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "No abbrev exists at the specified offset.");
+
+ if (expected_header->IsTypeUnit())
+ return DWARFUnitSP(
+ new DWARFTypeUnit(dwarf, uid, *expected_header, *abbrevs, section));
+ return DWARFUnitSP(
+ new DWARFCompileUnit(dwarf, uid, *expected_header, *abbrevs, section));
+}
+
+const lldb_private::DWARFDataExtractor &DWARFUnit::GetData() const {
+ return m_section == DIERef::Section::DebugTypes
+ ? m_dwarf.GetDWARFContext().getOrLoadDebugTypesData()
+ : m_dwarf.GetDWARFContext().getOrLoadDebugInfoData();
+}
+
+uint32_t DWARFUnit::GetHeaderByteSize() const {
+ switch (m_header.GetUnitType()) {
+ case llvm::dwarf::DW_UT_compile:
+ case llvm::dwarf::DW_UT_partial:
+ return GetVersion() < 5 ? 11 : 12;
+ case llvm::dwarf::DW_UT_skeleton:
+ case llvm::dwarf::DW_UT_split_compile:
+ return 20;
+ case llvm::dwarf::DW_UT_type:
+ case llvm::dwarf::DW_UT_split_type:
+ return GetVersion() < 5 ? 23 : 24;
+ }
+ llvm_unreachable("invalid UnitType.");
+}
+
+llvm::Expected<DWARFRangeList>
+DWARFUnit::FindRnglistFromOffset(dw_offset_t offset) const {
+ const DWARFDebugRangesBase *debug_ranges;
+ llvm::StringRef section;
+ if (GetVersion() <= 4) {
+ debug_ranges = m_dwarf.GetDebugRanges();
+ section = "debug_ranges";
+ } else {
+ debug_ranges = m_dwarf.GetDebugRngLists();
+ section = "debug_rnglists";
+ }
+ if (!debug_ranges)
+ return llvm::make_error<llvm::object::GenericBinaryError>("No " + section +
+ " section");
+
+ DWARFRangeList ranges;
+ debug_ranges->FindRanges(this, offset, ranges);
+ return ranges;
+}
+
+llvm::Expected<DWARFRangeList>
+DWARFUnit::FindRnglistFromIndex(uint32_t index) const {
+ const DWARFDebugRangesBase *debug_rnglists = m_dwarf.GetDebugRngLists();
+ if (!debug_rnglists)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "No debug_rnglists section");
+ return FindRnglistFromOffset(debug_rnglists->GetOffset(index));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
new file mode 100644
index 000000000000..8aa1e449f3ed
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
@@ -0,0 +1,306 @@
+//===-- DWARFUnit.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_DWARFUnit_h_
+#define SymbolFileDWARF_DWARFUnit_h_
+
+#include "DWARFDIE.h"
+#include "DWARFDebugInfoEntry.h"
+#include "lldb/lldb-enumerations.h"
+#include "llvm/Support/RWMutex.h"
+#include <atomic>
+
+class DWARFUnit;
+class DWARFCompileUnit;
+class NameToDIE;
+class SymbolFileDWARF;
+class SymbolFileDWARFDwo;
+
+typedef std::shared_ptr<DWARFUnit> DWARFUnitSP;
+
+enum DWARFProducer {
+ eProducerInvalid = 0,
+ eProducerClang,
+ eProducerGCC,
+ eProducerLLVMGCC,
+ eProcucerOther
+};
+
+/// Base class describing the header of any kind of "unit." Some information
+/// is specific to certain unit types. We separate this class out so we can
+/// parse the header before deciding what specific kind of unit to construct.
+class DWARFUnitHeader {
+ dw_offset_t m_offset = 0;
+ dw_offset_t m_length = 0;
+ uint16_t m_version = 0;
+ dw_offset_t m_abbr_offset = 0;
+ uint8_t m_unit_type = 0;
+ uint8_t m_addr_size = 0;
+
+ uint64_t m_type_hash = 0;
+ uint32_t m_type_offset = 0;
+
+ uint64_t m_dwo_id = 0;
+
+ DWARFUnitHeader() = default;
+
+public:
+ dw_offset_t GetOffset() const { return m_offset; }
+ uint16_t GetVersion() const { return m_version; }
+ uint16_t GetAddressByteSize() const { return m_addr_size; }
+ dw_offset_t GetLength() const { return m_length; }
+ dw_offset_t GetAbbrOffset() const { return m_abbr_offset; }
+ uint8_t GetUnitType() const { return m_unit_type; }
+ uint64_t GetTypeHash() const { return m_type_hash; }
+ dw_offset_t GetTypeOffset() const { return m_type_offset; }
+ bool IsTypeUnit() const {
+ return m_unit_type == DW_UT_type || m_unit_type == DW_UT_split_type;
+ }
+ uint32_t GetNextUnitOffset() const { return m_offset + m_length + 4; }
+
+ static llvm::Expected<DWARFUnitHeader>
+ extract(const lldb_private::DWARFDataExtractor &data, DIERef::Section section,
+ lldb::offset_t *offset_ptr);
+};
+
+class DWARFUnit : public lldb_private::UserID {
+ using die_iterator_range =
+ llvm::iterator_range<DWARFDebugInfoEntry::collection::iterator>;
+
+public:
+ static llvm::Expected<DWARFUnitSP>
+ extract(SymbolFileDWARF &dwarf2Data, lldb::user_id_t uid,
+ const lldb_private::DWARFDataExtractor &debug_info,
+ DIERef::Section section, lldb::offset_t *offset_ptr);
+ virtual ~DWARFUnit();
+
+ void ExtractUnitDIEIfNeeded();
+ void ExtractDIEsIfNeeded();
+
+ class ScopedExtractDIEs {
+ DWARFUnit *m_cu;
+ public:
+ bool m_clear_dies = false;
+ ScopedExtractDIEs(DWARFUnit &cu);
+ ~ScopedExtractDIEs();
+ DISALLOW_COPY_AND_ASSIGN(ScopedExtractDIEs);
+ ScopedExtractDIEs(ScopedExtractDIEs &&rhs);
+ ScopedExtractDIEs &operator=(ScopedExtractDIEs &&rhs);
+ };
+ ScopedExtractDIEs ExtractDIEsScoped();
+
+ DWARFDIE LookupAddress(const dw_addr_t address);
+ size_t AppendDIEsWithTag(const dw_tag_t tag, std::vector<DWARFDIE> &dies,
+ uint32_t depth = UINT32_MAX) const;
+ bool Verify(lldb_private::Stream *s) const;
+ virtual void Dump(lldb_private::Stream *s) const = 0;
+ /// Get the data that contains the DIE information for this unit.
+ ///
+ /// This will return the correct bytes that contain the data for
+ /// this DWARFUnit. It could be .debug_info or .debug_types
+ /// depending on where the data for this unit originates.
+ ///
+ /// \return
+ /// The correct data for the DIE information in this unit.
+ const lldb_private::DWARFDataExtractor &GetData() const;
+
+ /// Get the size in bytes of the unit header.
+ ///
+ /// \return
+ /// Byte size of the unit header
+ uint32_t GetHeaderByteSize() const;
+
+ // Offset of the initial length field.
+ dw_offset_t GetOffset() const { return m_header.GetOffset(); }
+ /// Get the size in bytes of the length field in the header.
+ ///
+ /// In DWARF32 this is just 4 bytes
+ ///
+ /// \return
+ /// Byte size of the compile unit header length field
+ size_t GetLengthByteSize() const { return 4; }
+
+ bool ContainsDIEOffset(dw_offset_t die_offset) const {
+ return die_offset >= GetFirstDIEOffset() &&
+ die_offset < GetNextUnitOffset();
+ }
+ dw_offset_t GetFirstDIEOffset() const {
+ return GetOffset() + GetHeaderByteSize();
+ }
+ dw_offset_t GetNextUnitOffset() const { return m_header.GetNextUnitOffset(); }
+ // Size of the CU data (without initial length and without header).
+ size_t GetDebugInfoSize() const;
+ // Size of the CU data incl. header but without initial length.
+ uint32_t GetLength() const { return m_header.GetLength(); }
+ uint16_t GetVersion() const { return m_header.GetVersion(); }
+ const DWARFAbbreviationDeclarationSet *GetAbbreviations() const;
+ dw_offset_t GetAbbrevOffset() const;
+ uint8_t GetAddressByteSize() const { return m_header.GetAddressByteSize(); }
+ dw_addr_t GetAddrBase() const { return m_addr_base; }
+ dw_addr_t GetBaseAddress() const { return m_base_addr; }
+ dw_offset_t GetLineTableOffset();
+ dw_addr_t GetRangesBase() const { return m_ranges_base; }
+ dw_addr_t GetStrOffsetsBase() const { return m_str_offsets_base; }
+ void SetAddrBase(dw_addr_t addr_base);
+ void SetRangesBase(dw_addr_t ranges_base);
+ void SetStrOffsetsBase(dw_offset_t str_offsets_base);
+ virtual void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) = 0;
+
+ lldb::ByteOrder GetByteOrder() const;
+
+ lldb_private::TypeSystem *GetTypeSystem();
+
+ const DWARFDebugAranges &GetFunctionAranges();
+
+ void SetBaseAddress(dw_addr_t base_addr);
+
+ DWARFBaseDIE GetUnitDIEOnly() { return DWARFDIE(this, GetUnitDIEPtrOnly()); }
+
+ DWARFDIE DIE() { return DWARFDIE(this, DIEPtr()); }
+
+ DWARFDIE GetDIE(dw_offset_t die_offset);
+
+ DWARFUnit &GetNonSkeletonUnit();
+
+ static uint8_t GetAddressByteSize(const DWARFUnit *cu);
+
+ static uint8_t GetDefaultAddressSize();
+
+ void *GetUserData() const;
+
+ void SetUserData(void *d);
+
+ bool Supports_DW_AT_APPLE_objc_complete_type();
+
+ bool DW_AT_decl_file_attributes_are_invalid();
+
+ bool Supports_unnamed_objc_bitfields();
+
+ SymbolFileDWARF &GetSymbolFileDWARF() const { return m_dwarf; }
+
+ DWARFProducer GetProducer();
+
+ uint32_t GetProducerVersionMajor();
+
+ uint32_t GetProducerVersionMinor();
+
+ uint32_t GetProducerVersionUpdate();
+
+ static lldb::LanguageType LanguageTypeFromDWARF(uint64_t val);
+
+ lldb::LanguageType GetLanguageType();
+
+ bool GetIsOptimized();
+
+ const lldb_private::FileSpec &GetCompilationDirectory();
+ const lldb_private::FileSpec &GetAbsolutePath();
+ lldb_private::FileSpec GetFile(size_t file_idx);
+ lldb_private::FileSpec::Style GetPathStyle();
+
+ SymbolFileDWARFDwo *GetDwoSymbolFile() const;
+
+ die_iterator_range dies() {
+ ExtractDIEsIfNeeded();
+ return die_iterator_range(m_die_array.begin(), m_die_array.end());
+ }
+
+ DIERef::Section GetDebugSection() const { return m_section; }
+
+ uint8_t GetUnitType() const { return m_header.GetUnitType(); }
+ bool IsTypeUnit() const { return m_header.IsTypeUnit(); }
+
+ /// Return a list of address ranges resulting from a (possibly encoded)
+ /// range list starting at a given offset in the appropriate ranges section.
+ llvm::Expected<DWARFRangeList> FindRnglistFromOffset(dw_offset_t offset) const;
+
+ /// Return a list of address ranges retrieved from an encoded range
+ /// list whose offset is found via a table lookup given an index (DWARF v5
+ /// and later).
+ llvm::Expected<DWARFRangeList> FindRnglistFromIndex(uint32_t index) const;
+
+protected:
+ DWARFUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid,
+ const DWARFUnitHeader &header,
+ const DWARFAbbreviationDeclarationSet &abbrevs,
+ DIERef::Section section);
+
+ llvm::Error ExtractHeader(SymbolFileDWARF &dwarf,
+ const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr);
+
+ // Get the DWARF unit DWARF debug information entry. Parse the single DIE
+ // if needed.
+ const DWARFDebugInfoEntry *GetUnitDIEPtrOnly() {
+ ExtractUnitDIEIfNeeded();
+ // m_first_die_mutex is not required as m_first_die is never cleared.
+ if (!m_first_die)
+ return NULL;
+ return &m_first_die;
+ }
+
+ // Get all DWARF debug informration entries. Parse all DIEs if needed.
+ const DWARFDebugInfoEntry *DIEPtr() {
+ ExtractDIEsIfNeeded();
+ if (m_die_array.empty())
+ return NULL;
+ return &m_die_array[0];
+ }
+
+ SymbolFileDWARF &m_dwarf;
+ std::unique_ptr<SymbolFileDWARFDwo> m_dwo_symbol_file;
+ DWARFUnitHeader m_header;
+ const DWARFAbbreviationDeclarationSet *m_abbrevs = nullptr;
+ void *m_user_data = nullptr;
+ // The compile unit debug information entry item
+ DWARFDebugInfoEntry::collection m_die_array;
+ mutable llvm::sys::RWMutex m_die_array_mutex;
+ // It is used for tracking of ScopedExtractDIEs instances.
+ mutable llvm::sys::RWMutex m_die_array_scoped_mutex;
+ // ScopedExtractDIEs instances should not call ClearDIEsRWLocked()
+ // as someone called ExtractDIEsIfNeeded().
+ std::atomic<bool> m_cancel_scopes;
+ // GetUnitDIEPtrOnly() needs to return pointer to the first DIE.
+ // But the first element of m_die_array after ExtractUnitDIEIfNeeded()
+ // would possibly move in memory after later ExtractDIEsIfNeeded().
+ DWARFDebugInfoEntry m_first_die;
+ llvm::sys::RWMutex m_first_die_mutex;
+ // A table similar to the .debug_aranges table, but this one points to the
+ // exact DW_TAG_subprogram DIEs
+ std::unique_ptr<DWARFDebugAranges> m_func_aranges_up;
+ dw_addr_t m_base_addr = 0;
+ DWARFProducer m_producer = eProducerInvalid;
+ uint32_t m_producer_version_major = 0;
+ uint32_t m_producer_version_minor = 0;
+ uint32_t m_producer_version_update = 0;
+ lldb::LanguageType m_language_type = lldb::eLanguageTypeUnknown;
+ lldb_private::LazyBool m_is_optimized = lldb_private::eLazyBoolCalculate;
+ llvm::Optional<lldb_private::FileSpec> m_comp_dir;
+ llvm::Optional<lldb_private::FileSpec> m_file_spec;
+ dw_addr_t m_addr_base = 0; // Value of DW_AT_addr_base
+ dw_addr_t m_ranges_base = 0; // Value of DW_AT_ranges_base
+
+ /// Value of DW_AT_stmt_list.
+ dw_offset_t m_line_table_offset = DW_INVALID_OFFSET;
+
+ dw_offset_t m_str_offsets_base = 0; // Value of DW_AT_str_offsets_base.
+ const DIERef::Section m_section;
+
+private:
+ void ParseProducerInfo();
+ void ExtractDIEsRWLocked();
+ void ClearDIEsRWLocked();
+
+ void AddUnitDIE(const DWARFDebugInfoEntry &cu_die);
+
+ void ComputeCompDirAndGuessPathStyle();
+ void ComputeAbsolutePath();
+
+ DISALLOW_COPY_AND_ASSIGN(DWARFUnit);
+};
+
+#endif // SymbolFileDWARF_DWARFUnit_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
new file mode 100644
index 000000000000..9746ad76c930
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
@@ -0,0 +1,272 @@
+//===-- DebugNamesDWARFIndex.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>>
+DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names,
+ DWARFDataExtractor debug_str,
+ DWARFDebugInfo *debug_info) {
+ if (!debug_info) {
+ return llvm::make_error<llvm::StringError>("debug info null",
+ llvm::inconvertibleErrorCode());
+ }
+ auto index_up = llvm::make_unique<DebugNames>(debug_names.GetAsLLVM(),
+ debug_str.GetAsLLVM());
+ if (llvm::Error E = index_up->extract())
+ return std::move(E);
+
+ return std::unique_ptr<DebugNamesDWARFIndex>(new DebugNamesDWARFIndex(
+ module, std::move(index_up), debug_names, debug_str, *debug_info));
+}
+
+llvm::DenseSet<dw_offset_t>
+DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) {
+ llvm::DenseSet<dw_offset_t> result;
+ for (const DebugNames::NameIndex &ni : debug_names) {
+ for (uint32_t cu = 0; cu < ni.getCUCount(); ++cu)
+ result.insert(ni.getCUOffset(cu));
+ }
+ return result;
+}
+
+llvm::Optional<DIERef>
+DebugNamesDWARFIndex::ToDIERef(const DebugNames::Entry &entry) {
+ llvm::Optional<uint64_t> cu_offset = entry.getCUOffset();
+ if (!cu_offset)
+ return llvm::None;
+
+ DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset);
+ if (!cu)
+ return llvm::None;
+
+ // This initializes the DWO symbol file. It's not possible for
+ // GetDwoSymbolFile to call this automatically because of mutual recursion
+ // between this and DWARFDebugInfoEntry::GetAttributeValue.
+ cu->ExtractUnitDIEIfNeeded();
+ cu = &cu->GetNonSkeletonUnit();
+
+ if (llvm::Optional<uint64_t> die_offset = entry.getDIEUnitOffset())
+ return DIERef(cu->GetSymbolFileDWARF().GetDwoNum(),
+ DIERef::Section::DebugInfo, cu->GetOffset() + *die_offset);
+
+ return llvm::None;
+}
+
+void DebugNamesDWARFIndex::Append(const DebugNames::Entry &entry,
+ DIEArray &offsets) {
+ if (llvm::Optional<DIERef> ref = ToDIERef(entry))
+ offsets.push_back(*ref);
+}
+
+void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error,
+ const DebugNames::NameIndex &ni,
+ llvm::StringRef name) {
+ // Ignore SentinelErrors, log everything else.
+ LLDB_LOG_ERROR(
+ LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS),
+ handleErrors(std::move(error), [](const DebugNames::SentinelError &) {}),
+ "Failed to parse index entries for index at {1:x}, name {2}: {0}",
+ ni.getUnitOffset(), name);
+}
+
+void DebugNamesDWARFIndex::GetGlobalVariables(ConstString basename,
+ DIEArray &offsets) {
+ m_fallback.GetGlobalVariables(basename, offsets);
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(basename.GetStringRef())) {
+ if (entry.tag() != DW_TAG_variable)
+ continue;
+
+ Append(entry, offsets);
+ }
+}
+
+void DebugNamesDWARFIndex::GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) {
+ m_fallback.GetGlobalVariables(regex, offsets);
+
+ for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
+ for (DebugNames::NameTableEntry nte: ni) {
+ if (!regex.Execute(nte.getString()))
+ continue;
+
+ uint32_t entry_offset = nte.getEntryOffset();
+ llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
+ for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
+ if (entry_or->tag() != DW_TAG_variable)
+ continue;
+
+ Append(*entry_or, offsets);
+ }
+ MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
+ }
+ }
+}
+
+void DebugNamesDWARFIndex::GetGlobalVariables(const DWARFUnit &cu,
+ DIEArray &offsets) {
+ m_fallback.GetGlobalVariables(cu, offsets);
+
+ uint64_t cu_offset = cu.GetOffset();
+ for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
+ for (DebugNames::NameTableEntry nte: ni) {
+ uint32_t entry_offset = nte.getEntryOffset();
+ llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
+ for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
+ if (entry_or->tag() != DW_TAG_variable)
+ continue;
+ if (entry_or->getCUOffset() != cu_offset)
+ continue;
+
+ Append(*entry_or, offsets);
+ }
+ MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
+ }
+ }
+}
+
+void DebugNamesDWARFIndex::GetCompleteObjCClass(ConstString class_name,
+ bool must_be_implementation,
+ DIEArray &offsets) {
+ m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, offsets);
+
+ // Keep a list of incomplete types as fallback for when we don't find the
+ // complete type.
+ DIEArray incomplete_types;
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(class_name.GetStringRef())) {
+ if (entry.tag() != DW_TAG_structure_type &&
+ entry.tag() != DW_TAG_class_type)
+ continue;
+
+ llvm::Optional<DIERef> ref = ToDIERef(entry);
+ if (!ref)
+ continue;
+
+ DWARFUnit *cu = m_debug_info.GetUnit(*ref);
+ if (!cu || !cu->Supports_DW_AT_APPLE_objc_complete_type()) {
+ incomplete_types.push_back(*ref);
+ continue;
+ }
+
+ // FIXME: We should return DWARFDIEs so we don't have to resolve it twice.
+ DWARFDIE die = m_debug_info.GetDIE(*ref);
+ if (!die)
+ continue;
+
+ if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) {
+ // If we find the complete version we're done.
+ offsets.push_back(*ref);
+ return;
+ } else {
+ incomplete_types.push_back(*ref);
+ }
+ }
+
+ offsets.insert(offsets.end(), incomplete_types.begin(),
+ incomplete_types.end());
+}
+
+void DebugNamesDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) {
+ m_fallback.GetTypes(name, offsets);
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(name.GetStringRef())) {
+ if (isType(entry.tag()))
+ Append(entry, offsets);
+ }
+}
+
+void DebugNamesDWARFIndex::GetTypes(const DWARFDeclContext &context,
+ DIEArray &offsets) {
+ m_fallback.GetTypes(context, offsets);
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(context[0].name)) {
+ if (entry.tag() == context[0].tag)
+ Append(entry, offsets);
+ }
+}
+
+void DebugNamesDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {
+ m_fallback.GetNamespaces(name, offsets);
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(name.GetStringRef())) {
+ if (entry.tag() == DW_TAG_namespace)
+ Append(entry, offsets);
+ }
+}
+
+void DebugNamesDWARFIndex::GetFunctions(
+ ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx, uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) {
+
+ std::vector<DWARFDIE> v;
+ m_fallback.GetFunctions(name, dwarf, parent_decl_ctx, name_type_mask, v);
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(name.GetStringRef())) {
+ Tag tag = entry.tag();
+ if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
+ continue;
+
+ if (llvm::Optional<DIERef> ref = ToDIERef(entry))
+ ProcessFunctionDIE(name.GetStringRef(), *ref, dwarf, parent_decl_ctx,
+ name_type_mask, v);
+ }
+
+ std::set<DWARFDebugInfoEntry *> seen;
+ for (DWARFDIE die : v)
+ if (seen.insert(die.GetDIE()).second)
+ dies.push_back(die);
+}
+
+void DebugNamesDWARFIndex::GetFunctions(const RegularExpression &regex,
+ DIEArray &offsets) {
+ m_fallback.GetFunctions(regex, offsets);
+
+ for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
+ for (DebugNames::NameTableEntry nte: ni) {
+ if (!regex.Execute(nte.getString()))
+ continue;
+
+ uint32_t entry_offset = nte.getEntryOffset();
+ llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
+ for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
+ Tag tag = entry_or->tag();
+ if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
+ continue;
+
+ Append(*entry_or, offsets);
+ }
+ MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
+ }
+ }
+}
+
+void DebugNamesDWARFIndex::Dump(Stream &s) {
+ m_fallback.Dump(s);
+
+ std::string data;
+ llvm::raw_string_ostream os(data);
+ m_debug_names_up->dump(os);
+ s.PutCString(os.str());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h
new file mode 100644
index 000000000000..dca25496373f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h
@@ -0,0 +1,81 @@
+//===-- DebugNamesDWARFIndex.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_DEBUGNAMESDWARFINDEX_H
+#define LLDB_DEBUGNAMESDWARFINDEX_H
+
+#include "Plugins/SymbolFile/DWARF/DWARFIndex.h"
+#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
+#include "Plugins/SymbolFile/DWARF/ManualDWARFIndex.h"
+#include "lldb/Utility/ConstString.h"
+#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
+
+namespace lldb_private {
+class DebugNamesDWARFIndex : public DWARFIndex {
+public:
+ static llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>>
+ Create(Module &module, DWARFDataExtractor debug_names,
+ DWARFDataExtractor debug_str, DWARFDebugInfo *debug_info);
+
+ void Preload() override { m_fallback.Preload(); }
+
+ void GetGlobalVariables(ConstString basename, DIEArray &offsets) override;
+ void GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) override;
+ void GetGlobalVariables(const DWARFUnit &cu, DIEArray &offsets) override;
+ void GetObjCMethods(ConstString class_name, DIEArray &offsets) override {}
+ void GetCompleteObjCClass(ConstString class_name, bool must_be_implementation,
+ DIEArray &offsets) override;
+ void GetTypes(ConstString name, DIEArray &offsets) override;
+ void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override;
+ void GetNamespaces(ConstString name, DIEArray &offsets) override;
+ void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) override;
+ void GetFunctions(const RegularExpression &regex,
+ DIEArray &offsets) override;
+
+ void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) override {}
+ void Dump(Stream &s) override;
+
+private:
+ DebugNamesDWARFIndex(Module &module,
+ std::unique_ptr<llvm::DWARFDebugNames> debug_names_up,
+ DWARFDataExtractor debug_names_data,
+ DWARFDataExtractor debug_str_data,
+ DWARFDebugInfo &debug_info)
+ : DWARFIndex(module), m_debug_info(debug_info),
+ m_debug_names_data(debug_names_data), m_debug_str_data(debug_str_data),
+ m_debug_names_up(std::move(debug_names_up)),
+ m_fallback(module, &debug_info, GetUnits(*m_debug_names_up)) {}
+
+ DWARFDebugInfo &m_debug_info;
+
+ // LLVM DWARFDebugNames will hold a non-owning reference to this data, so keep
+ // track of the ownership here.
+ DWARFDataExtractor m_debug_names_data;
+ DWARFDataExtractor m_debug_str_data;
+
+ using DebugNames = llvm::DWARFDebugNames;
+ std::unique_ptr<DebugNames> m_debug_names_up;
+ ManualDWARFIndex m_fallback;
+
+ llvm::Optional<DIERef> ToDIERef(const DebugNames::Entry &entry);
+ void Append(const DebugNames::Entry &entry, DIEArray &offsets);
+
+ static void MaybeLogLookupError(llvm::Error error,
+ const DebugNames::NameIndex &ni,
+ llvm::StringRef name);
+
+ static llvm::DenseSet<dw_offset_t> GetUnits(const DebugNames &debug_names);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_DEBUGNAMESDWARFINDEX_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp
new file mode 100644
index 000000000000..62b0ad37a9fc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp
@@ -0,0 +1,583 @@
+//===-- HashedNameToDIE.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "HashedNameToDIE.h"
+#include "llvm/ADT/StringRef.h"
+
+void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array,
+ DIEArray &die_offsets) {
+ const size_t count = die_info_array.size();
+ for (size_t i = 0; i < count; ++i)
+ die_offsets.emplace_back(die_info_array[i]);
+}
+
+void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array,
+ const dw_tag_t tag,
+ DIEArray &die_offsets) {
+ if (tag == 0) {
+ ExtractDIEArray(die_info_array, die_offsets);
+ } else {
+ const size_t count = die_info_array.size();
+ for (size_t i = 0; i < count; ++i) {
+ const dw_tag_t die_tag = die_info_array[i].tag;
+ bool tag_matches = die_tag == 0 || tag == die_tag;
+ if (!tag_matches) {
+ if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type)
+ tag_matches =
+ tag == DW_TAG_structure_type || tag == DW_TAG_class_type;
+ }
+ if (tag_matches)
+ die_offsets.emplace_back(die_info_array[i]);
+ }
+ }
+}
+
+void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array,
+ const dw_tag_t tag,
+ const uint32_t qualified_name_hash,
+ DIEArray &die_offsets) {
+ if (tag == 0) {
+ ExtractDIEArray(die_info_array, die_offsets);
+ } else {
+ const size_t count = die_info_array.size();
+ for (size_t i = 0; i < count; ++i) {
+ if (qualified_name_hash != die_info_array[i].qualified_name_hash)
+ continue;
+ const dw_tag_t die_tag = die_info_array[i].tag;
+ bool tag_matches = die_tag == 0 || tag == die_tag;
+ if (!tag_matches) {
+ if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type)
+ tag_matches =
+ tag == DW_TAG_structure_type || tag == DW_TAG_class_type;
+ }
+ if (tag_matches)
+ die_offsets.emplace_back(die_info_array[i]);
+ }
+ }
+}
+
+void DWARFMappedHash::ExtractClassOrStructDIEArray(
+ const DIEInfoArray &die_info_array,
+ bool return_implementation_only_if_available, DIEArray &die_offsets) {
+ const size_t count = die_info_array.size();
+ for (size_t i = 0; i < count; ++i) {
+ const dw_tag_t die_tag = die_info_array[i].tag;
+ if (die_tag == 0 || die_tag == DW_TAG_class_type ||
+ die_tag == DW_TAG_structure_type) {
+ if (die_info_array[i].type_flags & eTypeFlagClassIsImplementation) {
+ if (return_implementation_only_if_available) {
+ // We found the one true definition for this class, so only return
+ // that
+ die_offsets.clear();
+ die_offsets.emplace_back(die_info_array[i]);
+ return;
+ } else {
+ // Put the one true definition as the first entry so it matches first
+ die_offsets.emplace(die_offsets.begin(), die_info_array[i]);
+ }
+ } else {
+ die_offsets.emplace_back(die_info_array[i]);
+ }
+ }
+ }
+}
+
+void DWARFMappedHash::ExtractTypesFromDIEArray(
+ const DIEInfoArray &die_info_array, uint32_t type_flag_mask,
+ uint32_t type_flag_value, DIEArray &die_offsets) {
+ const size_t count = die_info_array.size();
+ for (size_t i = 0; i < count; ++i) {
+ if ((die_info_array[i].type_flags & type_flag_mask) == type_flag_value)
+ die_offsets.emplace_back(die_info_array[i]);
+ }
+}
+
+const char *DWARFMappedHash::GetAtomTypeName(uint16_t atom) {
+ switch (atom) {
+ case eAtomTypeNULL:
+ return "NULL";
+ case eAtomTypeDIEOffset:
+ return "die-offset";
+ case eAtomTypeCUOffset:
+ return "cu-offset";
+ case eAtomTypeTag:
+ return "die-tag";
+ case eAtomTypeNameFlags:
+ return "name-flags";
+ case eAtomTypeTypeFlags:
+ return "type-flags";
+ case eAtomTypeQualNameHash:
+ return "qualified-name-hash";
+ }
+ return "<invalid>";
+}
+
+DWARFMappedHash::DIEInfo::DIEInfo(dw_offset_t o, dw_tag_t t, uint32_t f,
+ uint32_t h)
+ : die_offset(o), tag(t), type_flags(f), qualified_name_hash(h) {}
+
+DWARFMappedHash::Prologue::Prologue(dw_offset_t _die_base_offset)
+ : die_base_offset(_die_base_offset), atoms(), atom_mask(0),
+ min_hash_data_byte_size(0), hash_data_has_fixed_byte_size(true) {
+ // Define an array of DIE offsets by first defining an array, and then define
+ // the atom type for the array, in this case we have an array of DIE offsets
+ AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4);
+}
+
+void DWARFMappedHash::Prologue::ClearAtoms() {
+ hash_data_has_fixed_byte_size = true;
+ min_hash_data_byte_size = 0;
+ atom_mask = 0;
+ atoms.clear();
+}
+
+bool DWARFMappedHash::Prologue::ContainsAtom(AtomType atom_type) const {
+ return (atom_mask & (1u << atom_type)) != 0;
+}
+
+void DWARFMappedHash::Prologue::Clear() {
+ die_base_offset = 0;
+ ClearAtoms();
+}
+
+void DWARFMappedHash::Prologue::AppendAtom(AtomType type, dw_form_t form) {
+ atoms.push_back({type, form});
+ atom_mask |= 1u << type;
+ switch (form) {
+ case DW_FORM_indirect:
+ case DW_FORM_exprloc:
+ case DW_FORM_flag_present:
+ case DW_FORM_ref_sig8:
+ llvm_unreachable("Unhandled atom form");
+
+ case DW_FORM_addrx:
+ case DW_FORM_string:
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ case DW_FORM_ref_udata:
+ case DW_FORM_GNU_addr_index:
+ case DW_FORM_GNU_str_index:
+ hash_data_has_fixed_byte_size = false;
+ LLVM_FALLTHROUGH;
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ case DW_FORM_ref1:
+ case DW_FORM_sec_offset:
+ min_hash_data_byte_size += 1;
+ break;
+
+ case DW_FORM_block2:
+ hash_data_has_fixed_byte_size = false;
+ LLVM_FALLTHROUGH;
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ min_hash_data_byte_size += 2;
+ break;
+
+ case DW_FORM_block4:
+ hash_data_has_fixed_byte_size = false;
+ LLVM_FALLTHROUGH;
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ case DW_FORM_addr:
+ case DW_FORM_ref_addr:
+ case DW_FORM_strp:
+ min_hash_data_byte_size += 4;
+ break;
+
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ min_hash_data_byte_size += 8;
+ break;
+ }
+}
+
+lldb::offset_t
+DWARFMappedHash::Prologue::Read(const lldb_private::DataExtractor &data,
+ lldb::offset_t offset) {
+ ClearAtoms();
+
+ die_base_offset = data.GetU32(&offset);
+
+ const uint32_t atom_count = data.GetU32(&offset);
+ if (atom_count == 0x00060003u) {
+ // Old format, deal with contents of old pre-release format
+ while (data.GetU32(&offset))
+ /* do nothing */;
+
+ // Hardcode to the only known value for now.
+ AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4);
+ } else {
+ for (uint32_t i = 0; i < atom_count; ++i) {
+ AtomType type = (AtomType)data.GetU16(&offset);
+ dw_form_t form = (dw_form_t)data.GetU16(&offset);
+ AppendAtom(type, form);
+ }
+ }
+ return offset;
+}
+
+size_t DWARFMappedHash::Prologue::GetByteSize() const {
+ // Add an extra count to the atoms size for the zero termination Atom that
+ // gets written to disk
+ return sizeof(die_base_offset) + sizeof(uint32_t) +
+ atoms.size() * sizeof(Atom);
+}
+
+size_t DWARFMappedHash::Prologue::GetMinimumHashDataByteSize() const {
+ return min_hash_data_byte_size;
+}
+
+bool DWARFMappedHash::Prologue::HashDataHasFixedByteSize() const {
+ return hash_data_has_fixed_byte_size;
+}
+
+size_t DWARFMappedHash::Header::GetByteSize(const HeaderData &header_data) {
+ return header_data.GetByteSize();
+}
+
+lldb::offset_t DWARFMappedHash::Header::Read(lldb_private::DataExtractor &data,
+ lldb::offset_t offset) {
+ offset = MappedHash::Header<Prologue>::Read(data, offset);
+ if (offset != UINT32_MAX) {
+ offset = header_data.Read(data, offset);
+ }
+ return offset;
+}
+
+bool DWARFMappedHash::Header::Read(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr,
+ DIEInfo &hash_data) const {
+ const size_t num_atoms = header_data.atoms.size();
+ if (num_atoms == 0)
+ return false;
+
+ for (size_t i = 0; i < num_atoms; ++i) {
+ DWARFFormValue form_value(nullptr, header_data.atoms[i].form);
+
+ if (!form_value.ExtractValue(data, offset_ptr))
+ return false;
+
+ switch (header_data.atoms[i].type) {
+ case eAtomTypeDIEOffset: // DIE offset, check form for encoding
+ hash_data.die_offset =
+ DWARFFormValue::IsDataForm(form_value.Form())
+ ? form_value.Unsigned()
+ : form_value.Reference(header_data.die_base_offset);
+ break;
+
+ case eAtomTypeTag: // DW_TAG value for the DIE
+ hash_data.tag = (dw_tag_t)form_value.Unsigned();
+ break;
+
+ case eAtomTypeTypeFlags: // Flags from enum TypeFlags
+ hash_data.type_flags = (uint32_t)form_value.Unsigned();
+ break;
+
+ case eAtomTypeQualNameHash: // Flags from enum TypeFlags
+ hash_data.qualified_name_hash = form_value.Unsigned();
+ break;
+
+ default:
+ // We can always skip atoms we don't know about
+ break;
+ }
+ }
+ return hash_data.die_offset != DW_INVALID_OFFSET;
+}
+
+DWARFMappedHash::MemoryTable::MemoryTable(
+ lldb_private::DWARFDataExtractor &table_data,
+ const lldb_private::DWARFDataExtractor &string_table, const char *name)
+ : MappedHash::MemoryTable<uint32_t, Header, DIEInfoArray>(table_data),
+ m_data(table_data), m_string_table(string_table), m_name(name) {}
+
+const char *
+DWARFMappedHash::MemoryTable::GetStringForKeyType(KeyType key) const {
+ // The key in the DWARF table is the .debug_str offset for the string
+ return m_string_table.PeekCStr(key);
+}
+
+bool DWARFMappedHash::MemoryTable::ReadHashData(uint32_t hash_data_offset,
+ HashData &hash_data) const {
+ lldb::offset_t offset = hash_data_offset;
+ offset += 4; // Skip string table offset that contains offset of hash name in
+ // .debug_str
+ const uint32_t count = m_data.GetU32(&offset);
+ if (count > 0) {
+ hash_data.resize(count);
+ for (uint32_t i = 0; i < count; ++i) {
+ if (!m_header.Read(m_data, &offset, hash_data[i]))
+ return false;
+ }
+ } else
+ hash_data.clear();
+ return true;
+}
+
+DWARFMappedHash::MemoryTable::Result
+DWARFMappedHash::MemoryTable::GetHashDataForName(
+ llvm::StringRef name, lldb::offset_t *hash_data_offset_ptr,
+ Pair &pair) const {
+ pair.key = m_data.GetU32(hash_data_offset_ptr);
+ pair.value.clear();
+
+ // If the key is zero, this terminates our chain of HashData objects for this
+ // hash value.
+ if (pair.key == 0)
+ return eResultEndOfHashData;
+
+ // There definitely should be a string for this string offset, if there
+ // isn't, there is something wrong, return and error
+ const char *strp_cstr = m_string_table.PeekCStr(pair.key);
+ if (strp_cstr == nullptr) {
+ *hash_data_offset_ptr = UINT32_MAX;
+ return eResultError;
+ }
+
+ const uint32_t count = m_data.GetU32(hash_data_offset_ptr);
+ const size_t min_total_hash_data_size =
+ count * m_header.header_data.GetMinimumHashDataByteSize();
+ if (count > 0 &&
+ m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr,
+ min_total_hash_data_size)) {
+ // We have at least one HashData entry, and we have enough data to parse at
+ // least "count" HashData entries.
+
+ // First make sure the entire C string matches...
+ const bool match = name == strp_cstr;
+
+ if (!match && m_header.header_data.HashDataHasFixedByteSize()) {
+ // If the string doesn't match and we have fixed size data, we can just
+ // add the total byte size of all HashData objects to the hash data
+ // offset and be done...
+ *hash_data_offset_ptr += min_total_hash_data_size;
+ } else {
+ // If the string does match, or we don't have fixed size data then we
+ // need to read the hash data as a stream. If the string matches we also
+ // append all HashData objects to the value array.
+ for (uint32_t i = 0; i < count; ++i) {
+ DIEInfo die_info;
+ if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) {
+ // Only happened if the HashData of the string matched...
+ if (match)
+ pair.value.push_back(die_info);
+ } else {
+ // Something went wrong while reading the data
+ *hash_data_offset_ptr = UINT32_MAX;
+ return eResultError;
+ }
+ }
+ }
+ // Return the correct response depending on if the string matched or not...
+ if (match)
+ return eResultKeyMatch; // The key (cstring) matches and we have lookup
+ // results!
+ else
+ return eResultKeyMismatch; // The key doesn't match, this function will
+ // get called
+ // again for the next key/value or the key terminator which in our case is
+ // a zero .debug_str offset.
+ } else {
+ *hash_data_offset_ptr = UINT32_MAX;
+ return eResultError;
+ }
+}
+
+DWARFMappedHash::MemoryTable::Result
+DWARFMappedHash::MemoryTable::AppendHashDataForRegularExpression(
+ const lldb_private::RegularExpression &regex,
+ lldb::offset_t *hash_data_offset_ptr, Pair &pair) const {
+ pair.key = m_data.GetU32(hash_data_offset_ptr);
+ // If the key is zero, this terminates our chain of HashData objects for this
+ // hash value.
+ if (pair.key == 0)
+ return eResultEndOfHashData;
+
+ // There definitely should be a string for this string offset, if there
+ // isn't, there is something wrong, return and error
+ const char *strp_cstr = m_string_table.PeekCStr(pair.key);
+ if (strp_cstr == nullptr)
+ return eResultError;
+
+ const uint32_t count = m_data.GetU32(hash_data_offset_ptr);
+ const size_t min_total_hash_data_size =
+ count * m_header.header_data.GetMinimumHashDataByteSize();
+ if (count > 0 &&
+ m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr,
+ min_total_hash_data_size)) {
+ const bool match = regex.Execute(llvm::StringRef(strp_cstr));
+
+ if (!match && m_header.header_data.HashDataHasFixedByteSize()) {
+ // If the regex doesn't match and we have fixed size data, we can just
+ // add the total byte size of all HashData objects to the hash data
+ // offset and be done...
+ *hash_data_offset_ptr += min_total_hash_data_size;
+ } else {
+ // If the string does match, or we don't have fixed size data then we
+ // need to read the hash data as a stream. If the string matches we also
+ // append all HashData objects to the value array.
+ for (uint32_t i = 0; i < count; ++i) {
+ DIEInfo die_info;
+ if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) {
+ // Only happened if the HashData of the string matched...
+ if (match)
+ pair.value.push_back(die_info);
+ } else {
+ // Something went wrong while reading the data
+ *hash_data_offset_ptr = UINT32_MAX;
+ return eResultError;
+ }
+ }
+ }
+ // Return the correct response depending on if the string matched or not...
+ if (match)
+ return eResultKeyMatch; // The key (cstring) matches and we have lookup
+ // results!
+ else
+ return eResultKeyMismatch; // The key doesn't match, this function will
+ // get called
+ // again for the next key/value or the key terminator which in our case is
+ // a zero .debug_str offset.
+ } else {
+ *hash_data_offset_ptr = UINT32_MAX;
+ return eResultError;
+ }
+}
+
+size_t DWARFMappedHash::MemoryTable::AppendAllDIEsThatMatchingRegex(
+ const lldb_private::RegularExpression &regex,
+ DIEInfoArray &die_info_array) const {
+ const uint32_t hash_count = m_header.hashes_count;
+ Pair pair;
+ for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) {
+ lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx);
+ while (hash_data_offset != UINT32_MAX) {
+ const lldb::offset_t prev_hash_data_offset = hash_data_offset;
+ Result hash_result =
+ AppendHashDataForRegularExpression(regex, &hash_data_offset, pair);
+ if (prev_hash_data_offset == hash_data_offset)
+ break;
+
+ // Check the result of getting our hash data
+ switch (hash_result) {
+ case eResultKeyMatch:
+ case eResultKeyMismatch:
+ // Whether we matches or not, it doesn't matter, we keep looking.
+ break;
+
+ case eResultEndOfHashData:
+ case eResultError:
+ hash_data_offset = UINT32_MAX;
+ break;
+ }
+ }
+ }
+ die_info_array.swap(pair.value);
+ return die_info_array.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::AppendAllDIEsInRange(
+ const uint32_t die_offset_start, const uint32_t die_offset_end,
+ DIEInfoArray &die_info_array) const {
+ const uint32_t hash_count = m_header.hashes_count;
+ for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) {
+ bool done = false;
+ lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx);
+ while (!done && hash_data_offset != UINT32_MAX) {
+ KeyType key = m_data.GetU32(&hash_data_offset);
+ // If the key is zero, this terminates our chain of HashData objects for
+ // this hash value.
+ if (key == 0)
+ break;
+
+ const uint32_t count = m_data.GetU32(&hash_data_offset);
+ for (uint32_t i = 0; i < count; ++i) {
+ DIEInfo die_info;
+ if (m_header.Read(m_data, &hash_data_offset, die_info)) {
+ if (die_info.die_offset == 0)
+ done = true;
+ if (die_offset_start <= die_info.die_offset &&
+ die_info.die_offset < die_offset_end)
+ die_info_array.push_back(die_info);
+ }
+ }
+ }
+ }
+ return die_info_array.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name,
+ DIEArray &die_offsets) {
+ if (name.empty())
+ return 0;
+
+ DIEInfoArray die_info_array;
+ if (FindByName(name, die_info_array))
+ DWARFMappedHash::ExtractDIEArray(die_info_array, die_offsets);
+ return die_info_array.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::FindByNameAndTag(llvm::StringRef name,
+ const dw_tag_t tag,
+ DIEArray &die_offsets) {
+ DIEInfoArray die_info_array;
+ if (FindByName(name, die_info_array))
+ DWARFMappedHash::ExtractDIEArray(die_info_array, tag, die_offsets);
+ return die_info_array.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::FindByNameAndTagAndQualifiedNameHash(
+ llvm::StringRef name, const dw_tag_t tag,
+ const uint32_t qualified_name_hash, DIEArray &die_offsets) {
+ DIEInfoArray die_info_array;
+ if (FindByName(name, die_info_array))
+ DWARFMappedHash::ExtractDIEArray(die_info_array, tag, qualified_name_hash,
+ die_offsets);
+ return die_info_array.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::FindCompleteObjCClassByName(
+ llvm::StringRef name, DIEArray &die_offsets, bool must_be_implementation) {
+ DIEInfoArray die_info_array;
+ if (FindByName(name, die_info_array)) {
+ if (must_be_implementation &&
+ GetHeader().header_data.ContainsAtom(eAtomTypeTypeFlags)) {
+ // If we have two atoms, then we have the DIE offset and the type flags
+ // so we can find the objective C class efficiently.
+ DWARFMappedHash::ExtractTypesFromDIEArray(die_info_array, UINT32_MAX,
+ eTypeFlagClassIsImplementation,
+ die_offsets);
+ } else {
+ // We don't only want the one true definition, so try and see what we can
+ // find, and only return class or struct DIEs. If we do have the full
+ // implementation, then return it alone, else return all possible
+ // matches.
+ const bool return_implementation_only_if_available = true;
+ DWARFMappedHash::ExtractClassOrStructDIEArray(
+ die_info_array, return_implementation_only_if_available, die_offsets);
+ }
+ }
+ return die_offsets.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name,
+ DIEInfoArray &die_info_array) {
+ if (name.empty())
+ return 0;
+
+ Pair kv_pair;
+ size_t old_size = die_info_array.size();
+ if (Find(name, kv_pair)) {
+ die_info_array.swap(kv_pair.value);
+ return die_info_array.size() - old_size;
+ }
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h
new file mode 100644
index 000000000000..a01612b59528
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h
@@ -0,0 +1,193 @@
+//===-- HashedNameToDIE.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_HashedNameToDIE_h_
+#define SymbolFileDWARF_HashedNameToDIE_h_
+
+#include <vector>
+
+#include "lldb/Core/MappedHash.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/lldb-defines.h"
+
+#include "DWARFDefines.h"
+#include "DWARFFormValue.h"
+#include "NameToDIE.h"
+
+class DWARFMappedHash {
+public:
+ enum AtomType : uint16_t {
+ eAtomTypeNULL = 0u,
+ eAtomTypeDIEOffset = 1u, // DIE offset, check form for encoding
+ eAtomTypeCUOffset = 2u, // DIE offset of the compiler unit header that
+ // contains the item in question
+ eAtomTypeTag = 3u, // DW_TAG_xxx value, should be encoded as DW_FORM_data1
+ // (if no tags exceed 255) or DW_FORM_data2
+ eAtomTypeNameFlags = 4u, // Flags from enum NameFlags
+ eAtomTypeTypeFlags = 5u, // Flags from enum TypeFlags,
+ eAtomTypeQualNameHash = 6u // A 32 bit hash of the full qualified name
+ // (since all hash entries are basename only)
+ // For example a type like "std::vector<int>::iterator" would have a name of
+ // "iterator"
+ // and a 32 bit hash for "std::vector<int>::iterator" to allow us to not
+ // have to pull
+ // in debug info for a type when we know the fully qualified name.
+ };
+
+ // Bit definitions for the eAtomTypeTypeFlags flags
+ enum TypeFlags {
+ // Always set for C++, only set for ObjC if this is the
+ // @implementation for class
+ eTypeFlagClassIsImplementation = (1u << 1)
+ };
+
+ struct DIEInfo {
+ dw_offset_t die_offset = DW_INVALID_OFFSET;
+ dw_tag_t tag = 0;
+
+ /// Any flags for this DIEInfo
+ uint32_t type_flags = 0;
+
+ /// A 32 bit hash of the fully qualified name
+ uint32_t qualified_name_hash = 0;
+
+ DIEInfo() = default;
+ DIEInfo(dw_offset_t o, dw_tag_t t, uint32_t f, uint32_t h);
+
+ explicit operator DIERef() const {
+ return DIERef(llvm::None, DIERef::Section::DebugInfo, die_offset);
+ }
+ };
+
+ struct Atom {
+ AtomType type;
+ dw_form_t form;
+ };
+
+ typedef std::vector<DIEInfo> DIEInfoArray;
+ typedef std::vector<Atom> AtomArray;
+
+ class Prologue {
+ public:
+ Prologue(dw_offset_t _die_base_offset = 0);
+
+ void ClearAtoms();
+
+ bool ContainsAtom(AtomType atom_type) const;
+
+ void Clear();
+
+ void AppendAtom(AtomType type, dw_form_t form);
+
+ lldb::offset_t Read(const lldb_private::DataExtractor &data,
+ lldb::offset_t offset);
+
+ size_t GetByteSize() const;
+
+ size_t GetMinimumHashDataByteSize() const;
+
+ bool HashDataHasFixedByteSize() const;
+
+ // DIE offset base so die offsets in hash_data can be CU relative
+ dw_offset_t die_base_offset;
+ AtomArray atoms;
+ uint32_t atom_mask;
+ size_t min_hash_data_byte_size;
+ bool hash_data_has_fixed_byte_size;
+ };
+
+ class Header : public MappedHash::Header<Prologue> {
+ public:
+ size_t GetByteSize(const HeaderData &header_data) override;
+
+ lldb::offset_t Read(lldb_private::DataExtractor &data,
+ lldb::offset_t offset) override;
+
+ bool Read(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr, DIEInfo &hash_data) const;
+ };
+
+ // A class for reading and using a saved hash table from a block of data
+ // in memory
+ class MemoryTable
+ : public MappedHash::MemoryTable<uint32_t, DWARFMappedHash::Header,
+ DIEInfoArray> {
+ public:
+ MemoryTable(lldb_private::DWARFDataExtractor &table_data,
+ const lldb_private::DWARFDataExtractor &string_table,
+ const char *name);
+
+ const char *GetStringForKeyType(KeyType key) const override;
+
+ bool ReadHashData(uint32_t hash_data_offset,
+ HashData &hash_data) const override;
+
+ size_t
+ AppendAllDIEsThatMatchingRegex(const lldb_private::RegularExpression &regex,
+ DIEInfoArray &die_info_array) const;
+
+ size_t AppendAllDIEsInRange(const uint32_t die_offset_start,
+ const uint32_t die_offset_end,
+ DIEInfoArray &die_info_array) const;
+
+ size_t FindByName(llvm::StringRef name, DIEArray &die_offsets);
+
+ size_t FindByNameAndTag(llvm::StringRef name, const dw_tag_t tag,
+ DIEArray &die_offsets);
+
+ size_t FindByNameAndTagAndQualifiedNameHash(
+ llvm::StringRef name, const dw_tag_t tag,
+ const uint32_t qualified_name_hash, DIEArray &die_offsets);
+
+ size_t FindCompleteObjCClassByName(llvm::StringRef name,
+ DIEArray &die_offsets,
+ bool must_be_implementation);
+
+ protected:
+ Result AppendHashDataForRegularExpression(
+ const lldb_private::RegularExpression &regex,
+ lldb::offset_t *hash_data_offset_ptr, Pair &pair) const;
+
+ size_t FindByName(llvm::StringRef name, DIEInfoArray &die_info_array);
+
+ Result GetHashDataForName(llvm::StringRef name,
+ lldb::offset_t *hash_data_offset_ptr,
+ Pair &pair) const override;
+
+ lldb_private::DWARFDataExtractor m_data;
+ lldb_private::DWARFDataExtractor m_string_table;
+ std::string m_name;
+ };
+
+ static void ExtractDIEArray(const DIEInfoArray &die_info_array,
+ DIEArray &die_offsets);
+
+protected:
+ static void ExtractDIEArray(const DIEInfoArray &die_info_array,
+ const dw_tag_t tag, DIEArray &die_offsets);
+
+ static void ExtractDIEArray(const DIEInfoArray &die_info_array,
+ const dw_tag_t tag,
+ const uint32_t qualified_name_hash,
+ DIEArray &die_offsets);
+
+ static void
+ ExtractClassOrStructDIEArray(const DIEInfoArray &die_info_array,
+ bool return_implementation_only_if_available,
+ DIEArray &die_offsets);
+
+ static void ExtractTypesFromDIEArray(const DIEInfoArray &die_info_array,
+ uint32_t type_flag_mask,
+ uint32_t type_flag_value,
+ DIEArray &die_offsets);
+
+ static const char *GetAtomTypeName(uint16_t atom);
+};
+
+#endif // SymbolFileDWARF_HashedNameToDIE_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp
new file mode 100644
index 000000000000..8495016d4280
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp
@@ -0,0 +1,33 @@
+//===-- LogChannelDWARF.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LogChannelDWARF.h"
+
+using namespace lldb_private;
+
+static constexpr Log::Category g_categories[] = {
+ {{"comp"},
+ {"log insertions of object files into DWARF debug maps"},
+ DWARF_LOG_TYPE_COMPLETION},
+ {{"info"}, {"log the parsing of .debug_info"}, DWARF_LOG_DEBUG_INFO},
+ {{"line"}, {"log the parsing of .debug_line"}, DWARF_LOG_DEBUG_LINE},
+ {{"lookups"},
+ {"log any lookups that happen by name, regex, or address"},
+ DWARF_LOG_LOOKUPS},
+ {{"map"},
+ {"log struct/unions/class type completions"},
+ DWARF_LOG_DEBUG_MAP},
+};
+
+Log::Channel LogChannelDWARF::g_channel(g_categories, DWARF_LOG_DEFAULT);
+
+void LogChannelDWARF::Initialize() {
+ Log::Register("dwarf", g_channel);
+}
+
+void LogChannelDWARF::Terminate() { Log::Unregister("dwarf"); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h
new file mode 100644
index 000000000000..a89c686735d2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h
@@ -0,0 +1,35 @@
+//===-- LogChannelDWARF.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_LogChannelDWARF_h_
+#define SymbolFileDWARF_LogChannelDWARF_h_
+
+#include "lldb/Utility/Log.h"
+
+#define DWARF_LOG_DEBUG_INFO (1u << 1)
+#define DWARF_LOG_DEBUG_LINE (1u << 2)
+#define DWARF_LOG_LOOKUPS (1u << 3)
+#define DWARF_LOG_TYPE_COMPLETION (1u << 4)
+#define DWARF_LOG_DEBUG_MAP (1u << 5)
+#define DWARF_LOG_ALL (UINT32_MAX)
+#define DWARF_LOG_DEFAULT (DWARF_LOG_DEBUG_INFO)
+
+namespace lldb_private {
+class LogChannelDWARF {
+ static Log::Channel g_channel;
+
+public:
+ static void Initialize();
+ static void Terminate();
+
+ static Log *GetLogIfAll(uint32_t mask) { return g_channel.GetLogIfAll(mask); }
+ static Log *GetLogIfAny(uint32_t mask) { return g_channel.GetLogIfAny(mask); }
+};
+}
+
+#endif // SymbolFileDWARF_LogChannelDWARF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
new file mode 100644
index 000000000000..aff8b5d8c15f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
@@ -0,0 +1,476 @@
+//===-- ManualDWARFIndex.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/SymbolFile/DWARF/ManualDWARFIndex.h"
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
+#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Host/TaskPool.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+void ManualDWARFIndex::Index() {
+ if (!m_debug_info)
+ return;
+
+ DWARFDebugInfo &debug_info = *m_debug_info;
+ m_debug_info = nullptr;
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%p", static_cast<void *>(&debug_info));
+
+ std::vector<DWARFUnit *> units_to_index;
+ units_to_index.reserve(debug_info.GetNumUnits());
+ for (size_t U = 0; U < debug_info.GetNumUnits(); ++U) {
+ DWARFUnit *unit = debug_info.GetUnitAtIndex(U);
+ if (unit && m_units_to_avoid.count(unit->GetOffset()) == 0)
+ units_to_index.push_back(unit);
+ }
+ if (units_to_index.empty())
+ return;
+
+ std::vector<IndexSet> sets(units_to_index.size());
+
+ // Keep memory down by clearing DIEs for any units if indexing
+ // caused us to load the unit's DIEs.
+ std::vector<llvm::Optional<DWARFUnit::ScopedExtractDIEs>> clear_cu_dies(
+ units_to_index.size());
+ auto parser_fn = [&](size_t cu_idx) {
+ IndexUnit(*units_to_index[cu_idx], sets[cu_idx]);
+ };
+
+ auto extract_fn = [&units_to_index, &clear_cu_dies](size_t cu_idx) {
+ clear_cu_dies[cu_idx] = units_to_index[cu_idx]->ExtractDIEsScoped();
+ };
+
+ // Create a task runner that extracts dies for each DWARF unit in a
+ // separate thread
+ // First figure out which units didn't have their DIEs already
+ // parsed and remember this. If no DIEs were parsed prior to this index
+ // function call, we are going to want to clear the CU dies after we are
+ // done indexing to make sure we don't pull in all DWARF dies, but we need
+ // to wait until all units have been indexed in case a DIE in one
+ // unit refers to another and the indexes accesses those DIEs.
+ TaskMapOverInt(0, units_to_index.size(), extract_fn);
+
+ // Now create a task runner that can index each DWARF unit in a
+ // separate thread so we can index quickly.
+
+ TaskMapOverInt(0, units_to_index.size(), parser_fn);
+
+ auto finalize_fn = [this, &sets](NameToDIE(IndexSet::*index)) {
+ NameToDIE &result = m_set.*index;
+ for (auto &set : sets)
+ result.Append(set.*index);
+ result.Finalize();
+ };
+
+ TaskPool::RunTasks([&]() { finalize_fn(&IndexSet::function_basenames); },
+ [&]() { finalize_fn(&IndexSet::function_fullnames); },
+ [&]() { finalize_fn(&IndexSet::function_methods); },
+ [&]() { finalize_fn(&IndexSet::function_selectors); },
+ [&]() { finalize_fn(&IndexSet::objc_class_selectors); },
+ [&]() { finalize_fn(&IndexSet::globals); },
+ [&]() { finalize_fn(&IndexSet::types); },
+ [&]() { finalize_fn(&IndexSet::namespaces); });
+}
+
+void ManualDWARFIndex::IndexUnit(DWARFUnit &unit, IndexSet &set) {
+ assert(
+ !unit.GetSymbolFileDWARF().GetBaseCompileUnit() &&
+ "DWARFUnit associated with .dwo or .dwp should not be indexed directly");
+
+ Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS);
+
+ if (log) {
+ m_module.LogMessage(
+ log, "ManualDWARFIndex::IndexUnit for unit at .debug_info[0x%8.8x]",
+ unit.GetOffset());
+ }
+
+ const LanguageType cu_language = unit.GetLanguageType();
+
+ IndexUnitImpl(unit, cu_language, set);
+
+ if (SymbolFileDWARFDwo *dwo_symbol_file = unit.GetDwoSymbolFile()) {
+ DWARFDebugInfo &dwo_info = *dwo_symbol_file->DebugInfo();
+ for (size_t i = 0; i < dwo_info.GetNumUnits(); ++i)
+ IndexUnitImpl(*dwo_info.GetUnitAtIndex(i), cu_language, set);
+ }
+}
+
+void ManualDWARFIndex::IndexUnitImpl(DWARFUnit &unit,
+ const LanguageType cu_language,
+ IndexSet &set) {
+ for (const DWARFDebugInfoEntry &die : unit.dies()) {
+ const dw_tag_t tag = die.Tag();
+
+ switch (tag) {
+ case DW_TAG_array_type:
+ case DW_TAG_base_type:
+ case DW_TAG_class_type:
+ case DW_TAG_constant:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_namespace:
+ case DW_TAG_string_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_subprogram:
+ case DW_TAG_subroutine_type:
+ case DW_TAG_typedef:
+ case DW_TAG_union_type:
+ case DW_TAG_unspecified_type:
+ case DW_TAG_variable:
+ break;
+
+ default:
+ continue;
+ }
+
+ DWARFAttributes attributes;
+ const char *name = nullptr;
+ const char *mangled_cstr = nullptr;
+ bool is_declaration = false;
+ // bool is_artificial = false;
+ bool has_address = false;
+ bool has_location_or_const_value = false;
+ bool is_global_or_static_variable = false;
+
+ DWARFFormValue specification_die_form;
+ const size_t num_attributes = die.GetAttributes(&unit, attributes);
+ if (num_attributes > 0) {
+ for (uint32_t i = 0; i < num_attributes; ++i) {
+ dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ switch (attr) {
+ case DW_AT_name:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ name = form_value.AsCString();
+ break;
+
+ case DW_AT_declaration:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ is_declaration = form_value.Unsigned() != 0;
+ break;
+
+ // case DW_AT_artificial:
+ // if (attributes.ExtractFormValueAtIndex(i,
+ // form_value))
+ // is_artificial = form_value.Unsigned() != 0;
+ // break;
+
+ case DW_AT_MIPS_linkage_name:
+ case DW_AT_linkage_name:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ mangled_cstr = form_value.AsCString();
+ break;
+
+ case DW_AT_low_pc:
+ case DW_AT_high_pc:
+ case DW_AT_ranges:
+ has_address = true;
+ break;
+
+ case DW_AT_entry_pc:
+ has_address = true;
+ break;
+
+ case DW_AT_location:
+ case DW_AT_const_value:
+ has_location_or_const_value = true;
+ if (tag == DW_TAG_variable) {
+ const DWARFDebugInfoEntry *parent_die = die.GetParent();
+ while (parent_die != nullptr) {
+ switch (parent_die->Tag()) {
+ case DW_TAG_subprogram:
+ case DW_TAG_lexical_block:
+ case DW_TAG_inlined_subroutine:
+ // Even if this is a function level static, we don't add it. We
+ // could theoretically add these if we wanted to by
+ // introspecting into the DW_AT_location and seeing if the
+ // location describes a hard coded address, but we don't want
+ // the performance penalty of that right now.
+ is_global_or_static_variable = false;
+ // if (attributes.ExtractFormValueAtIndex(dwarf, i,
+ // form_value)) {
+ // // If we have valid block data, then we have location
+ // // expression bytesthat are fixed (not a location list).
+ // const uint8_t *block_data = form_value.BlockData();
+ // if (block_data) {
+ // uint32_t block_length = form_value.Unsigned();
+ // if (block_length == 1 +
+ // attributes.UnitAtIndex(i)->GetAddressByteSize()) {
+ // if (block_data[0] == DW_OP_addr)
+ // add_die = true;
+ // }
+ // }
+ // }
+ parent_die = nullptr; // Terminate the while loop.
+ break;
+
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ is_global_or_static_variable = true;
+ parent_die = nullptr; // Terminate the while loop.
+ break;
+
+ default:
+ parent_die =
+ parent_die->GetParent(); // Keep going in the while loop.
+ break;
+ }
+ }
+ }
+ break;
+
+ case DW_AT_specification:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ specification_die_form = form_value;
+ break;
+ }
+ }
+ }
+
+ DIERef ref = *DWARFDIE(&unit, &die).GetDIERef();
+ switch (tag) {
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_subprogram:
+ if (has_address) {
+ if (name) {
+ bool is_objc_method = false;
+ if (cu_language == eLanguageTypeObjC ||
+ cu_language == eLanguageTypeObjC_plus_plus) {
+ ObjCLanguage::MethodName objc_method(name, true);
+ if (objc_method.IsValid(true)) {
+ is_objc_method = true;
+ ConstString class_name_with_category(
+ objc_method.GetClassNameWithCategory());
+ ConstString objc_selector_name(objc_method.GetSelector());
+ ConstString objc_fullname_no_category_name(
+ objc_method.GetFullNameWithoutCategory(true));
+ ConstString class_name_no_category(objc_method.GetClassName());
+ set.function_fullnames.Insert(ConstString(name), ref);
+ if (class_name_with_category)
+ set.objc_class_selectors.Insert(class_name_with_category, ref);
+ if (class_name_no_category &&
+ class_name_no_category != class_name_with_category)
+ set.objc_class_selectors.Insert(class_name_no_category, ref);
+ if (objc_selector_name)
+ set.function_selectors.Insert(objc_selector_name, ref);
+ if (objc_fullname_no_category_name)
+ set.function_fullnames.Insert(objc_fullname_no_category_name,
+ ref);
+ }
+ }
+ // If we have a mangled name, then the DW_AT_name attribute is
+ // usually the method name without the class or any parameters
+ bool is_method = DWARFDIE(&unit, &die).IsMethod();
+
+ if (is_method)
+ set.function_methods.Insert(ConstString(name), ref);
+ else
+ set.function_basenames.Insert(ConstString(name), ref);
+
+ if (!is_method && !mangled_cstr && !is_objc_method)
+ set.function_fullnames.Insert(ConstString(name), ref);
+ }
+ if (mangled_cstr) {
+ // Make sure our mangled name isn't the same string table entry as
+ // our name. If it starts with '_', then it is ok, else compare the
+ // string to make sure it isn't the same and we don't end up with
+ // duplicate entries
+ if (name && name != mangled_cstr &&
+ ((mangled_cstr[0] == '_') ||
+ (::strcmp(name, mangled_cstr) != 0))) {
+ set.function_fullnames.Insert(ConstString(mangled_cstr), ref);
+ }
+ }
+ }
+ break;
+
+ case DW_TAG_array_type:
+ case DW_TAG_base_type:
+ case DW_TAG_class_type:
+ case DW_TAG_constant:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_string_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_subroutine_type:
+ case DW_TAG_typedef:
+ case DW_TAG_union_type:
+ case DW_TAG_unspecified_type:
+ if (name && !is_declaration)
+ set.types.Insert(ConstString(name), ref);
+ if (mangled_cstr && !is_declaration)
+ set.types.Insert(ConstString(mangled_cstr), ref);
+ break;
+
+ case DW_TAG_namespace:
+ if (name)
+ set.namespaces.Insert(ConstString(name), ref);
+ break;
+
+ case DW_TAG_variable:
+ if (name && has_location_or_const_value && is_global_or_static_variable) {
+ set.globals.Insert(ConstString(name), ref);
+ // Be sure to include variables by their mangled and demangled names if
+ // they have any since a variable can have a basename "i", a mangled
+ // named "_ZN12_GLOBAL__N_11iE" and a demangled mangled name
+ // "(anonymous namespace)::i"...
+
+ // Make sure our mangled name isn't the same string table entry as our
+ // name. If it starts with '_', then it is ok, else compare the string
+ // to make sure it isn't the same and we don't end up with duplicate
+ // entries
+ if (mangled_cstr && name != mangled_cstr &&
+ ((mangled_cstr[0] == '_') || (::strcmp(name, mangled_cstr) != 0))) {
+ set.globals.Insert(ConstString(mangled_cstr), ref);
+ }
+ }
+ break;
+
+ default:
+ continue;
+ }
+ }
+}
+
+void ManualDWARFIndex::GetGlobalVariables(ConstString basename, DIEArray &offsets) {
+ Index();
+ m_set.globals.Find(basename, offsets);
+}
+
+void ManualDWARFIndex::GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) {
+ Index();
+ m_set.globals.Find(regex, offsets);
+}
+
+void ManualDWARFIndex::GetGlobalVariables(const DWARFUnit &unit,
+ DIEArray &offsets) {
+ Index();
+ m_set.globals.FindAllEntriesForUnit(unit, offsets);
+}
+
+void ManualDWARFIndex::GetObjCMethods(ConstString class_name,
+ DIEArray &offsets) {
+ Index();
+ m_set.objc_class_selectors.Find(class_name, offsets);
+}
+
+void ManualDWARFIndex::GetCompleteObjCClass(ConstString class_name,
+ bool must_be_implementation,
+ DIEArray &offsets) {
+ Index();
+ m_set.types.Find(class_name, offsets);
+}
+
+void ManualDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) {
+ Index();
+ m_set.types.Find(name, offsets);
+}
+
+void ManualDWARFIndex::GetTypes(const DWARFDeclContext &context,
+ DIEArray &offsets) {
+ Index();
+ m_set.types.Find(ConstString(context[0].name), offsets);
+}
+
+void ManualDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {
+ Index();
+ m_set.namespaces.Find(name, offsets);
+}
+
+void ManualDWARFIndex::GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) {
+ Index();
+
+ if (name_type_mask & eFunctionNameTypeFull) {
+ DIEArray offsets;
+ m_set.function_basenames.Find(name, offsets);
+ m_set.function_methods.Find(name, offsets);
+ m_set.function_fullnames.Find(name, offsets);
+ for (const DIERef &die_ref: offsets) {
+ DWARFDIE die = dwarf.GetDIE(die_ref);
+ if (!die)
+ continue;
+ if (SymbolFileDWARF::DIEInDeclContext(&parent_decl_ctx, die))
+ dies.push_back(die);
+ }
+ }
+ if (name_type_mask & eFunctionNameTypeBase) {
+ DIEArray offsets;
+ m_set.function_basenames.Find(name, offsets);
+ for (const DIERef &die_ref: offsets) {
+ DWARFDIE die = dwarf.GetDIE(die_ref);
+ if (!die)
+ continue;
+ if (SymbolFileDWARF::DIEInDeclContext(&parent_decl_ctx, die))
+ dies.push_back(die);
+ }
+ offsets.clear();
+ }
+
+ if (name_type_mask & eFunctionNameTypeMethod && !parent_decl_ctx.IsValid()) {
+ DIEArray offsets;
+ m_set.function_methods.Find(name, offsets);
+ for (const DIERef &die_ref: offsets) {
+ if (DWARFDIE die = dwarf.GetDIE(die_ref))
+ dies.push_back(die);
+ }
+ }
+
+ if (name_type_mask & eFunctionNameTypeSelector &&
+ !parent_decl_ctx.IsValid()) {
+ DIEArray offsets;
+ m_set.function_selectors.Find(name, offsets);
+ for (const DIERef &die_ref: offsets) {
+ if (DWARFDIE die = dwarf.GetDIE(die_ref))
+ dies.push_back(die);
+ }
+ }
+}
+
+void ManualDWARFIndex::GetFunctions(const RegularExpression &regex,
+ DIEArray &offsets) {
+ Index();
+
+ m_set.function_basenames.Find(regex, offsets);
+ m_set.function_fullnames.Find(regex, offsets);
+}
+
+void ManualDWARFIndex::Dump(Stream &s) {
+ s.Format("Manual DWARF index for ({0}) '{1:F}':",
+ m_module.GetArchitecture().GetArchitectureName(),
+ m_module.GetObjectFile()->GetFileSpec());
+ s.Printf("\nFunction basenames:\n");
+ m_set.function_basenames.Dump(&s);
+ s.Printf("\nFunction fullnames:\n");
+ m_set.function_fullnames.Dump(&s);
+ s.Printf("\nFunction methods:\n");
+ m_set.function_methods.Dump(&s);
+ s.Printf("\nFunction selectors:\n");
+ m_set.function_selectors.Dump(&s);
+ s.Printf("\nObjective-C class selectors:\n");
+ m_set.objc_class_selectors.Dump(&s);
+ s.Printf("\nGlobals and statics:\n");
+ m_set.globals.Dump(&s);
+ s.Printf("\nTypes:\n");
+ m_set.types.Dump(&s);
+ s.Printf("\nNamespaces:\n");
+ m_set.namespaces.Dump(&s);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
new file mode 100644
index 000000000000..dd03b103c936
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
@@ -0,0 +1,74 @@
+//===-- ManualDWARFIndex.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_MANUALDWARFINDEX_H
+#define LLDB_MANUALDWARFINDEX_H
+
+#include "Plugins/SymbolFile/DWARF/DWARFIndex.h"
+#include "Plugins/SymbolFile/DWARF/NameToDIE.h"
+#include "llvm/ADT/DenseSet.h"
+
+class DWARFDebugInfo;
+
+namespace lldb_private {
+class ManualDWARFIndex : public DWARFIndex {
+public:
+ ManualDWARFIndex(Module &module, DWARFDebugInfo *debug_info,
+ llvm::DenseSet<dw_offset_t> units_to_avoid = {})
+ : DWARFIndex(module), m_debug_info(debug_info),
+ m_units_to_avoid(std::move(units_to_avoid)) {}
+
+ void Preload() override { Index(); }
+
+ void GetGlobalVariables(ConstString basename, DIEArray &offsets) override;
+ void GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) override;
+ void GetGlobalVariables(const DWARFUnit &unit, DIEArray &offsets) override;
+ void GetObjCMethods(ConstString class_name, DIEArray &offsets) override;
+ void GetCompleteObjCClass(ConstString class_name, bool must_be_implementation,
+ DIEArray &offsets) override;
+ void GetTypes(ConstString name, DIEArray &offsets) override;
+ void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override;
+ void GetNamespaces(ConstString name, DIEArray &offsets) override;
+ void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) override;
+ void GetFunctions(const RegularExpression &regex, DIEArray &offsets) override;
+
+ void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) override {}
+ void Dump(Stream &s) override;
+
+private:
+ struct IndexSet {
+ NameToDIE function_basenames;
+ NameToDIE function_fullnames;
+ NameToDIE function_methods;
+ NameToDIE function_selectors;
+ NameToDIE objc_class_selectors;
+ NameToDIE globals;
+ NameToDIE types;
+ NameToDIE namespaces;
+ };
+ void Index();
+ void IndexUnit(DWARFUnit &unit, IndexSet &set);
+
+ static void IndexUnitImpl(DWARFUnit &unit,
+ const lldb::LanguageType cu_language,
+ IndexSet &set);
+
+ /// Non-null value means we haven't built the index yet.
+ DWARFDebugInfo *m_debug_info;
+ /// Which dwarf units should we skip while building the index.
+ llvm::DenseSet<dw_offset_t> m_units_to_avoid;
+
+ IndexSet m_set;
+};
+} // namespace lldb_private
+
+#endif // LLDB_MANUALDWARFINDEX_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp
new file mode 100644
index 000000000000..7d81afb1b226
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp
@@ -0,0 +1,78 @@
+//===-- NameToDIE.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NameToDIE.h"
+#include "DWARFUnit.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void NameToDIE::Finalize() {
+ m_map.Sort();
+ m_map.SizeToFit();
+}
+
+void NameToDIE::Insert(ConstString name, const DIERef &die_ref) {
+ m_map.Append(name, die_ref);
+}
+
+size_t NameToDIE::Find(ConstString name, DIEArray &info_array) const {
+ return m_map.GetValues(name, info_array);
+}
+
+size_t NameToDIE::Find(const RegularExpression &regex,
+ DIEArray &info_array) const {
+ return m_map.GetValues(regex, info_array);
+}
+
+size_t NameToDIE::FindAllEntriesForUnit(const DWARFUnit &unit,
+ DIEArray &info_array) const {
+ const size_t initial_size = info_array.size();
+ const uint32_t size = m_map.GetSize();
+ for (uint32_t i = 0; i < size; ++i) {
+ const DIERef &die_ref = m_map.GetValueAtIndexUnchecked(i);
+ if (unit.GetSymbolFileDWARF().GetDwoNum() == die_ref.dwo_num() &&
+ unit.GetDebugSection() == die_ref.section() &&
+ unit.GetOffset() <= die_ref.die_offset() &&
+ die_ref.die_offset() < unit.GetNextUnitOffset())
+ info_array.push_back(die_ref);
+ }
+ return info_array.size() - initial_size;
+}
+
+void NameToDIE::Dump(Stream *s) {
+ const uint32_t size = m_map.GetSize();
+ for (uint32_t i = 0; i < size; ++i) {
+ s->Format("{0} \"{1}\"\n", m_map.GetValueAtIndexUnchecked(i),
+ m_map.GetCStringAtIndexUnchecked(i));
+ }
+}
+
+void NameToDIE::ForEach(
+ std::function<bool(ConstString name, const DIERef &die_ref)> const
+ &callback) const {
+ const uint32_t size = m_map.GetSize();
+ for (uint32_t i = 0; i < size; ++i) {
+ if (!callback(m_map.GetCStringAtIndexUnchecked(i),
+ m_map.GetValueAtIndexUnchecked(i)))
+ break;
+ }
+}
+
+void NameToDIE::Append(const NameToDIE &other) {
+ const uint32_t size = other.m_map.GetSize();
+ for (uint32_t i = 0; i < size; ++i) {
+ m_map.Append(other.m_map.GetCStringAtIndexUnchecked(i),
+ other.m_map.GetValueAtIndexUnchecked(i));
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h
new file mode 100644
index 000000000000..b504f45e81b5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h
@@ -0,0 +1,53 @@
+//===-- NameToDIE.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_NameToDIE_h_
+#define SymbolFileDWARF_NameToDIE_h_
+
+#include <functional>
+
+#include "DIERef.h"
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/lldb-defines.h"
+
+class DWARFUnit;
+
+class NameToDIE {
+public:
+ NameToDIE() : m_map() {}
+
+ ~NameToDIE() {}
+
+ void Dump(lldb_private::Stream *s);
+
+ void Insert(lldb_private::ConstString name, const DIERef &die_ref);
+
+ void Append(const NameToDIE &other);
+
+ void Finalize();
+
+ size_t Find(lldb_private::ConstString name,
+ DIEArray &info_array) const;
+
+ size_t Find(const lldb_private::RegularExpression &regex,
+ DIEArray &info_array) const;
+
+ size_t FindAllEntriesForUnit(const DWARFUnit &unit,
+ DIEArray &info_array) const;
+
+ void
+ ForEach(std::function<bool(lldb_private::ConstString name,
+ const DIERef &die_ref)> const
+ &callback) const;
+
+protected:
+ lldb_private::UniqueCStringMap<DIERef> m_map;
+};
+
+#endif // SymbolFileDWARF_NameToDIE_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
new file mode 100644
index 000000000000..e2ddcfc5d64b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -0,0 +1,3762 @@
+//===-- SymbolFileDWARF.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolFileDWARF.h"
+
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Threading.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+
+#include "lldb/Interpreter/OptionValueFileSpecList.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/CompilerDecl.h"
+#include "lldb/Symbol/CompilerDeclContext.h"
+#include "lldb/Symbol/DebugMacros.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/LocateSymbolFile.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Symbol/VariableList.h"
+
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Target.h"
+
+#include "AppleDWARFIndex.h"
+#include "DWARFASTParser.h"
+#include "DWARFASTParserClang.h"
+#include "DWARFCompileUnit.h"
+#include "DWARFDebugAbbrev.h"
+#include "DWARFDebugAranges.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFDebugLine.h"
+#include "DWARFDebugMacro.h"
+#include "DWARFDebugRanges.h"
+#include "DWARFDeclContext.h"
+#include "DWARFFormValue.h"
+#include "DWARFTypeUnit.h"
+#include "DWARFUnit.h"
+#include "DebugNamesDWARFIndex.h"
+#include "LogChannelDWARF.h"
+#include "ManualDWARFIndex.h"
+#include "SymbolFileDWARFDebugMap.h"
+#include "SymbolFileDWARFDwo.h"
+#include "SymbolFileDWARFDwp.h"
+
+#include "llvm/Support/FileSystem.h"
+
+#include <algorithm>
+#include <map>
+#include <memory>
+
+#include <ctype.h>
+#include <string.h>
+
+//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN
+
+#ifdef ENABLE_DEBUG_PRINTF
+#include <stdio.h>
+#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
+#else
+#define DEBUG_PRINTF(fmt, ...)
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+// static inline bool
+// child_requires_parent_class_union_or_struct_to_be_completed (dw_tag_t tag)
+//{
+// switch (tag)
+// {
+// default:
+// break;
+// case DW_TAG_subprogram:
+// case DW_TAG_inlined_subroutine:
+// case DW_TAG_class_type:
+// case DW_TAG_structure_type:
+// case DW_TAG_union_type:
+// return true;
+// }
+// return false;
+//}
+//
+
+namespace {
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"comp-dir-symlink-paths", OptionValue::eTypeFileSpecList, true, 0, nullptr,
+ {},
+ "If the DW_AT_comp_dir matches any of these paths the symbolic "
+ "links will be resolved at DWARF parse time."},
+ {"ignore-file-indexes", OptionValue::eTypeBoolean, true, 0, nullptr, {},
+ "Ignore indexes present in the object files and always index DWARF "
+ "manually."}};
+
+enum {
+ ePropertySymLinkPaths,
+ ePropertyIgnoreIndexes,
+};
+
+class PluginProperties : public Properties {
+public:
+ static ConstString GetSettingName() {
+ return SymbolFileDWARF::GetPluginNameStatic();
+ }
+
+ PluginProperties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_properties);
+ }
+
+ FileSpecList GetSymLinkPaths() {
+ const OptionValueFileSpecList *option_value =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(
+ nullptr, true, ePropertySymLinkPaths);
+ assert(option_value);
+ return option_value->GetCurrentValue();
+ }
+
+ bool IgnoreFileIndexes() const {
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, ePropertyIgnoreIndexes, false);
+ }
+};
+
+typedef std::shared_ptr<PluginProperties> SymbolFileDWARFPropertiesSP;
+
+static const SymbolFileDWARFPropertiesSP &GetGlobalPluginProperties() {
+ static const auto g_settings_sp(std::make_shared<PluginProperties>());
+ return g_settings_sp;
+}
+
+} // anonymous namespace end
+
+FileSpecList SymbolFileDWARF::GetSymlinkPaths() {
+ return GetGlobalPluginProperties()->GetSymLinkPaths();
+}
+
+void SymbolFileDWARF::Initialize() {
+ LogChannelDWARF::Initialize();
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+}
+
+void SymbolFileDWARF::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForSymbolFilePlugin(
+ debugger, PluginProperties::GetSettingName())) {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForSymbolFilePlugin(
+ debugger, GetGlobalPluginProperties()->GetValueProperties(),
+ ConstString("Properties for the dwarf symbol-file plug-in."),
+ is_global_setting);
+ }
+}
+
+void SymbolFileDWARF::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+ LogChannelDWARF::Terminate();
+}
+
+lldb_private::ConstString SymbolFileDWARF::GetPluginNameStatic() {
+ static ConstString g_name("dwarf");
+ return g_name;
+}
+
+const char *SymbolFileDWARF::GetPluginDescriptionStatic() {
+ return "DWARF and DWARF3 debug symbol file reader.";
+}
+
+SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFile *obj_file) {
+ return new SymbolFileDWARF(obj_file,
+ /*dwo_section_list*/ nullptr);
+}
+
+TypeList *SymbolFileDWARF::GetTypeList() {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ if (debug_map_symfile)
+ return debug_map_symfile->GetTypeList();
+ else
+ return m_obj_file->GetModule()->GetTypeList();
+}
+void SymbolFileDWARF::GetTypes(const DWARFDIE &die, dw_offset_t min_die_offset,
+ dw_offset_t max_die_offset, uint32_t type_mask,
+ TypeSet &type_set) {
+ if (die) {
+ const dw_offset_t die_offset = die.GetOffset();
+
+ if (die_offset >= max_die_offset)
+ return;
+
+ if (die_offset >= min_die_offset) {
+ const dw_tag_t tag = die.Tag();
+
+ bool add_type = false;
+
+ switch (tag) {
+ case DW_TAG_array_type:
+ add_type = (type_mask & eTypeClassArray) != 0;
+ break;
+ case DW_TAG_unspecified_type:
+ case DW_TAG_base_type:
+ add_type = (type_mask & eTypeClassBuiltin) != 0;
+ break;
+ case DW_TAG_class_type:
+ add_type = (type_mask & eTypeClassClass) != 0;
+ break;
+ case DW_TAG_structure_type:
+ add_type = (type_mask & eTypeClassStruct) != 0;
+ break;
+ case DW_TAG_union_type:
+ add_type = (type_mask & eTypeClassUnion) != 0;
+ break;
+ case DW_TAG_enumeration_type:
+ add_type = (type_mask & eTypeClassEnumeration) != 0;
+ break;
+ case DW_TAG_subroutine_type:
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ add_type = (type_mask & eTypeClassFunction) != 0;
+ break;
+ case DW_TAG_pointer_type:
+ add_type = (type_mask & eTypeClassPointer) != 0;
+ break;
+ case DW_TAG_rvalue_reference_type:
+ case DW_TAG_reference_type:
+ add_type = (type_mask & eTypeClassReference) != 0;
+ break;
+ case DW_TAG_typedef:
+ add_type = (type_mask & eTypeClassTypedef) != 0;
+ break;
+ case DW_TAG_ptr_to_member_type:
+ add_type = (type_mask & eTypeClassMemberPointer) != 0;
+ break;
+ }
+
+ if (add_type) {
+ const bool assert_not_being_parsed = true;
+ Type *type = ResolveTypeUID(die, assert_not_being_parsed);
+ if (type) {
+ if (type_set.find(type) == type_set.end())
+ type_set.insert(type);
+ }
+ }
+ }
+
+ for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid();
+ child_die = child_die.GetSibling()) {
+ GetTypes(child_die, min_die_offset, max_die_offset, type_mask, type_set);
+ }
+ }
+}
+
+size_t SymbolFileDWARF::GetTypes(SymbolContextScope *sc_scope,
+ TypeClass type_mask, TypeList &type_list)
+
+{
+ ASSERT_MODULE_LOCK(this);
+ TypeSet type_set;
+
+ CompileUnit *comp_unit = nullptr;
+ DWARFUnit *dwarf_cu = nullptr;
+ if (sc_scope)
+ comp_unit = sc_scope->CalculateSymbolContextCompileUnit();
+
+ if (comp_unit) {
+ dwarf_cu = GetDWARFCompileUnit(comp_unit);
+ if (dwarf_cu == nullptr)
+ return 0;
+ GetTypes(dwarf_cu->DIE(), dwarf_cu->GetOffset(),
+ dwarf_cu->GetNextUnitOffset(), type_mask, type_set);
+ } else {
+ DWARFDebugInfo *info = DebugInfo();
+ if (info) {
+ const size_t num_cus = info->GetNumUnits();
+ for (size_t cu_idx = 0; cu_idx < num_cus; ++cu_idx) {
+ dwarf_cu = info->GetUnitAtIndex(cu_idx);
+ if (dwarf_cu) {
+ GetTypes(dwarf_cu->DIE(), 0, UINT32_MAX, type_mask, type_set);
+ }
+ }
+ }
+ }
+
+ std::set<CompilerType> compiler_type_set;
+ size_t num_types_added = 0;
+ for (Type *type : type_set) {
+ CompilerType compiler_type = type->GetForwardCompilerType();
+ if (compiler_type_set.find(compiler_type) == compiler_type_set.end()) {
+ compiler_type_set.insert(compiler_type);
+ type_list.Insert(type->shared_from_this());
+ ++num_types_added;
+ }
+ }
+ return num_types_added;
+}
+
+// Gets the first parent that is a lexical block, function or inlined
+// subroutine, or compile unit.
+DWARFDIE
+SymbolFileDWARF::GetParentSymbolContextDIE(const DWARFDIE &child_die) {
+ DWARFDIE die;
+ for (die = child_die.GetParent(); die; die = die.GetParent()) {
+ dw_tag_t tag = die.Tag();
+
+ switch (tag) {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_lexical_block:
+ return die;
+ }
+ }
+ return DWARFDIE();
+}
+
+SymbolFileDWARF::SymbolFileDWARF(ObjectFile *objfile,
+ SectionList *dwo_section_list)
+ : SymbolFile(objfile),
+ UserID(0x7fffffff00000000), // Used by SymbolFileDWARFDebugMap to
+ // when this class parses .o files to
+ // contain the .o file index/ID
+ m_debug_map_module_wp(), m_debug_map_symfile(nullptr),
+ m_context(objfile->GetModule()->GetSectionList(), dwo_section_list),
+ m_data_debug_loc(), m_abbr(), m_info(), m_fetched_external_modules(false),
+ m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate),
+ m_unique_ast_type_map() {}
+
+SymbolFileDWARF::~SymbolFileDWARF() {}
+
+static ConstString GetDWARFMachOSegmentName() {
+ static ConstString g_dwarf_section_name("__DWARF");
+ return g_dwarf_section_name;
+}
+
+UniqueDWARFASTTypeMap &SymbolFileDWARF::GetUniqueDWARFASTTypeMap() {
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ if (debug_map_symfile)
+ return debug_map_symfile->GetUniqueDWARFASTTypeMap();
+ else
+ return m_unique_ast_type_map;
+}
+
+TypeSystem *SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) {
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ TypeSystem *type_system;
+ if (debug_map_symfile) {
+ type_system = debug_map_symfile->GetTypeSystemForLanguage(language);
+ } else {
+ type_system = m_obj_file->GetModule()->GetTypeSystemForLanguage(language);
+ if (type_system)
+ type_system->SetSymbolFile(this);
+ }
+ return type_system;
+}
+
+void SymbolFileDWARF::InitializeObject() {
+ Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
+
+ if (!GetGlobalPluginProperties()->IgnoreFileIndexes()) {
+ DWARFDataExtractor apple_names, apple_namespaces, apple_types, apple_objc;
+ LoadSectionData(eSectionTypeDWARFAppleNames, apple_names);
+ LoadSectionData(eSectionTypeDWARFAppleNamespaces, apple_namespaces);
+ LoadSectionData(eSectionTypeDWARFAppleTypes, apple_types);
+ LoadSectionData(eSectionTypeDWARFAppleObjC, apple_objc);
+
+ m_index = AppleDWARFIndex::Create(
+ *GetObjectFile()->GetModule(), apple_names, apple_namespaces,
+ apple_types, apple_objc, m_context.getOrLoadStrData());
+
+ if (m_index)
+ return;
+
+ DWARFDataExtractor debug_names;
+ LoadSectionData(eSectionTypeDWARFDebugNames, debug_names);
+ if (debug_names.GetByteSize() > 0) {
+ llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> index_or =
+ DebugNamesDWARFIndex::Create(
+ *GetObjectFile()->GetModule(), debug_names,
+ m_context.getOrLoadStrData(), DebugInfo());
+ if (index_or) {
+ m_index = std::move(*index_or);
+ return;
+ }
+ LLDB_LOG_ERROR(log, index_or.takeError(),
+ "Unable to read .debug_names data: {0}");
+ }
+ }
+
+ m_index = llvm::make_unique<ManualDWARFIndex>(*GetObjectFile()->GetModule(),
+ DebugInfo());
+}
+
+bool SymbolFileDWARF::SupportedVersion(uint16_t version) {
+ return version >= 2 && version <= 5;
+}
+
+uint32_t SymbolFileDWARF::CalculateAbilities() {
+ uint32_t abilities = 0;
+ if (m_obj_file != nullptr) {
+ const Section *section = nullptr;
+ const SectionList *section_list = m_obj_file->GetSectionList();
+ if (section_list == nullptr)
+ return 0;
+
+ uint64_t debug_abbrev_file_size = 0;
+ uint64_t debug_info_file_size = 0;
+ uint64_t debug_line_file_size = 0;
+
+ section = section_list->FindSectionByName(GetDWARFMachOSegmentName()).get();
+
+ if (section)
+ section_list = &section->GetChildren();
+
+ section =
+ section_list->FindSectionByType(eSectionTypeDWARFDebugInfo, true).get();
+ if (section != nullptr) {
+ debug_info_file_size = section->GetFileSize();
+
+ section =
+ section_list->FindSectionByType(eSectionTypeDWARFDebugAbbrev, true)
+ .get();
+ if (section)
+ debug_abbrev_file_size = section->GetFileSize();
+
+ DWARFDebugAbbrev *abbrev = DebugAbbrev();
+ if (abbrev) {
+ std::set<dw_form_t> invalid_forms;
+ abbrev->GetUnsupportedForms(invalid_forms);
+ if (!invalid_forms.empty()) {
+ StreamString error;
+ error.Printf("unsupported DW_FORM value%s:", invalid_forms.size() > 1 ? "s" : "");
+ for (auto form : invalid_forms)
+ error.Printf(" %#x", form);
+ m_obj_file->GetModule()->ReportWarning("%s", error.GetString().str().c_str());
+ return 0;
+ }
+ }
+
+ section =
+ section_list->FindSectionByType(eSectionTypeDWARFDebugLine, true)
+ .get();
+ if (section)
+ debug_line_file_size = section->GetFileSize();
+ } else {
+ const char *symfile_dir_cstr =
+ m_obj_file->GetFileSpec().GetDirectory().GetCString();
+ if (symfile_dir_cstr) {
+ if (strcasestr(symfile_dir_cstr, ".dsym")) {
+ if (m_obj_file->GetType() == ObjectFile::eTypeDebugInfo) {
+ // We have a dSYM file that didn't have a any debug info. If the
+ // string table has a size of 1, then it was made from an
+ // executable with no debug info, or from an executable that was
+ // stripped.
+ section =
+ section_list->FindSectionByType(eSectionTypeDWARFDebugStr, true)
+ .get();
+ if (section && section->GetFileSize() == 1) {
+ m_obj_file->GetModule()->ReportWarning(
+ "empty dSYM file detected, dSYM was created with an "
+ "executable with no debug info.");
+ }
+ }
+ }
+ }
+ }
+
+ if (debug_abbrev_file_size > 0 && debug_info_file_size > 0)
+ abilities |= CompileUnits | Functions | Blocks | GlobalVariables |
+ LocalVariables | VariableTypes;
+
+ if (debug_line_file_size > 0)
+ abilities |= LineTables;
+ }
+ return abilities;
+}
+
+const DWARFDataExtractor &
+SymbolFileDWARF::GetCachedSectionData(lldb::SectionType sect_type,
+ DWARFDataSegment &data_segment) {
+ llvm::call_once(data_segment.m_flag, [this, sect_type, &data_segment] {
+ this->LoadSectionData(sect_type, std::ref(data_segment.m_data));
+ });
+ return data_segment.m_data;
+}
+
+void SymbolFileDWARF::LoadSectionData(lldb::SectionType sect_type,
+ DWARFDataExtractor &data) {
+ ModuleSP module_sp(m_obj_file->GetModule());
+ const SectionList *section_list = module_sp->GetSectionList();
+ if (!section_list)
+ return;
+
+ SectionSP section_sp(section_list->FindSectionByType(sect_type, true));
+ if (!section_sp)
+ return;
+
+ data.Clear();
+ m_obj_file->ReadSectionData(section_sp.get(), data);
+}
+
+const DWARFDataExtractor &SymbolFileDWARF::DebugLocData() {
+ const DWARFDataExtractor &debugLocData = get_debug_loc_data();
+ if (debugLocData.GetByteSize() > 0)
+ return debugLocData;
+ return get_debug_loclists_data();
+}
+
+const DWARFDataExtractor &SymbolFileDWARF::get_debug_loc_data() {
+ return GetCachedSectionData(eSectionTypeDWARFDebugLoc, m_data_debug_loc);
+}
+
+const DWARFDataExtractor &SymbolFileDWARF::get_debug_loclists_data() {
+ return GetCachedSectionData(eSectionTypeDWARFDebugLocLists,
+ m_data_debug_loclists);
+}
+
+DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() {
+ if (m_abbr)
+ return m_abbr.get();
+
+ const DWARFDataExtractor &debug_abbrev_data = m_context.getOrLoadAbbrevData();
+ if (debug_abbrev_data.GetByteSize() == 0)
+ return nullptr;
+
+ auto abbr = llvm::make_unique<DWARFDebugAbbrev>();
+ llvm::Error error = abbr->parse(debug_abbrev_data);
+ if (error) {
+ Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
+ LLDB_LOG_ERROR(log, std::move(error),
+ "Unable to read .debug_abbrev section: {0}");
+ return nullptr;
+ }
+
+ m_abbr = std::move(abbr);
+ return m_abbr.get();
+}
+
+const DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() const {
+ return m_abbr.get();
+}
+
+DWARFDebugInfo *SymbolFileDWARF::DebugInfo() {
+ if (m_info == nullptr) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
+ static_cast<void *>(this));
+ if (m_context.getOrLoadDebugInfoData().GetByteSize() > 0)
+ m_info = llvm::make_unique<DWARFDebugInfo>(*this, m_context);
+ }
+ return m_info.get();
+}
+
+const DWARFDebugInfo *SymbolFileDWARF::DebugInfo() const {
+ return m_info.get();
+}
+
+DWARFUnit *
+SymbolFileDWARF::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) {
+ if (!comp_unit)
+ return nullptr;
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (info) {
+ // The compile unit ID is the index of the DWARF unit.
+ DWARFUnit *dwarf_cu = info->GetUnitAtIndex(comp_unit->GetID());
+ if (dwarf_cu && dwarf_cu->GetUserData() == nullptr)
+ dwarf_cu->SetUserData(comp_unit);
+ return dwarf_cu;
+ }
+ return nullptr;
+}
+
+DWARFDebugRangesBase *SymbolFileDWARF::GetDebugRanges() {
+ if (!m_ranges) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
+ static_cast<void *>(this));
+
+ if (m_context.getOrLoadRangesData().GetByteSize() > 0)
+ m_ranges.reset(new DWARFDebugRanges());
+
+ if (m_ranges)
+ m_ranges->Extract(m_context);
+ }
+ return m_ranges.get();
+}
+
+DWARFDebugRangesBase *SymbolFileDWARF::GetDebugRngLists() {
+ if (!m_rnglists) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
+ static_cast<void *>(this));
+
+ if (m_context.getOrLoadRngListsData().GetByteSize() > 0)
+ m_rnglists.reset(new DWARFDebugRngLists());
+
+ if (m_rnglists)
+ m_rnglists->Extract(m_context);
+ }
+ return m_rnglists.get();
+}
+
+lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) {
+ CompUnitSP cu_sp;
+ CompileUnit *comp_unit = (CompileUnit *)dwarf_cu.GetUserData();
+ if (comp_unit) {
+ // We already parsed this compile unit, had out a shared pointer to it
+ cu_sp = comp_unit->shared_from_this();
+ } else {
+ if (&dwarf_cu.GetSymbolFileDWARF() != this) {
+ return dwarf_cu.GetSymbolFileDWARF().ParseCompileUnit(dwarf_cu);
+ } else if (dwarf_cu.GetOffset() == 0 && GetDebugMapSymfile()) {
+ // Let the debug map create the compile unit
+ cu_sp = m_debug_map_symfile->GetCompileUnit(this);
+ dwarf_cu.SetUserData(cu_sp.get());
+ } else {
+ ModuleSP module_sp(m_obj_file->GetModule());
+ if (module_sp) {
+ const DWARFDIE cu_die = dwarf_cu.DIE();
+ if (cu_die) {
+ FileSpec cu_file_spec(cu_die.GetName(), dwarf_cu.GetPathStyle());
+ if (cu_file_spec) {
+ // If we have a full path to the compile unit, we don't need to
+ // resolve the file. This can be expensive e.g. when the source
+ // files are NFS mounted.
+ cu_file_spec.MakeAbsolute(dwarf_cu.GetCompilationDirectory());
+
+ std::string remapped_file;
+ if (module_sp->RemapSourceFile(cu_file_spec.GetPath(),
+ remapped_file))
+ cu_file_spec.SetFile(remapped_file, FileSpec::Style::native);
+ }
+
+ LanguageType cu_language = DWARFUnit::LanguageTypeFromDWARF(
+ cu_die.GetAttributeValueAsUnsigned(DW_AT_language, 0));
+
+ bool is_optimized = dwarf_cu.GetIsOptimized();
+ BuildCuTranslationTable();
+ cu_sp = std::make_shared<CompileUnit>(
+ module_sp, &dwarf_cu, cu_file_spec,
+ *GetDWARFUnitIndex(dwarf_cu.GetID()), cu_language,
+ is_optimized ? eLazyBoolYes : eLazyBoolNo);
+
+ dwarf_cu.SetUserData(cu_sp.get());
+
+ m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(
+ dwarf_cu.GetID(), cu_sp);
+ }
+ }
+ }
+ }
+ return cu_sp;
+}
+
+void SymbolFileDWARF::BuildCuTranslationTable() {
+ if (!m_lldb_cu_to_dwarf_unit.empty())
+ return;
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (!info)
+ return;
+
+ if (!info->ContainsTypeUnits()) {
+ // We can use a 1-to-1 mapping. No need to build a translation table.
+ return;
+ }
+ for (uint32_t i = 0, num = info->GetNumUnits(); i < num; ++i) {
+ if (auto *cu = llvm::dyn_cast<DWARFCompileUnit>(info->GetUnitAtIndex(i))) {
+ cu->SetID(m_lldb_cu_to_dwarf_unit.size());
+ m_lldb_cu_to_dwarf_unit.push_back(i);
+ }
+ }
+}
+
+llvm::Optional<uint32_t> SymbolFileDWARF::GetDWARFUnitIndex(uint32_t cu_idx) {
+ BuildCuTranslationTable();
+ if (m_lldb_cu_to_dwarf_unit.empty())
+ return cu_idx;
+ if (cu_idx >= m_lldb_cu_to_dwarf_unit.size())
+ return llvm::None;
+ return m_lldb_cu_to_dwarf_unit[cu_idx];
+}
+
+uint32_t SymbolFileDWARF::GetNumCompileUnits() {
+ DWARFDebugInfo *info = DebugInfo();
+ if (!info)
+ return 0;
+ BuildCuTranslationTable();
+ return m_lldb_cu_to_dwarf_unit.empty() ? info->GetNumUnits()
+ : m_lldb_cu_to_dwarf_unit.size();
+}
+
+CompUnitSP SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) {
+ ASSERT_MODULE_LOCK(this);
+ DWARFDebugInfo *info = DebugInfo();
+ if (!info)
+ return {};
+
+ if (llvm::Optional<uint32_t> dwarf_idx = GetDWARFUnitIndex(cu_idx)) {
+ if (auto *dwarf_cu = llvm::cast_or_null<DWARFCompileUnit>(
+ info->GetUnitAtIndex(*dwarf_idx)))
+ return ParseCompileUnit(*dwarf_cu);
+ }
+ return {};
+}
+
+Function *SymbolFileDWARF::ParseFunction(CompileUnit &comp_unit,
+ const DWARFDIE &die) {
+ ASSERT_MODULE_LOCK(this);
+ if (die.IsValid()) {
+ TypeSystem *type_system =
+ GetTypeSystemForLanguage(die.GetCU()->GetLanguageType());
+
+ if (type_system) {
+ DWARFASTParser *dwarf_ast = type_system->GetDWARFParser();
+ if (dwarf_ast)
+ return dwarf_ast->ParseFunctionFromDWARF(comp_unit, die);
+ }
+ }
+ return nullptr;
+}
+
+bool SymbolFileDWARF::FixupAddress(Address &addr) {
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ if (debug_map_symfile) {
+ return debug_map_symfile->LinkOSOAddress(addr);
+ }
+ // This is a normal DWARF file, no address fixups need to happen
+ return true;
+}
+lldb::LanguageType SymbolFileDWARF::ParseLanguage(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (dwarf_cu)
+ return dwarf_cu->GetLanguageType();
+ else
+ return eLanguageTypeUnknown;
+}
+
+size_t SymbolFileDWARF::ParseFunctions(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (!dwarf_cu)
+ return 0;
+
+ size_t functions_added = 0;
+ std::vector<DWARFDIE> function_dies;
+ dwarf_cu->AppendDIEsWithTag(DW_TAG_subprogram, function_dies);
+ for (const DWARFDIE &die : function_dies) {
+ if (comp_unit.FindFunctionByUID(die.GetID()))
+ continue;
+ if (ParseFunction(comp_unit, die))
+ ++functions_added;
+ }
+ // FixupTypes();
+ return functions_added;
+}
+
+bool SymbolFileDWARF::ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) {
+ ASSERT_MODULE_LOCK(this);
+ if (DWARFUnit *unit = GetDWARFCompileUnit(&comp_unit)) {
+ const dw_offset_t stmt_list = unit->GetLineTableOffset();
+ if (stmt_list != DW_INVALID_OFFSET) {
+ // All file indexes in DWARF are one based and a file of index zero is
+ // supposed to be the compile unit itself.
+ support_files.Append(comp_unit);
+ return DWARFDebugLine::ParseSupportFiles(comp_unit.GetModule(),
+ m_context.getOrLoadLineData(),
+ stmt_list, support_files, unit);
+ }
+ }
+ return false;
+}
+
+FileSpec SymbolFileDWARF::GetFile(DWARFUnit &unit, size_t file_idx) {
+ if (auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(&unit)) {
+ if (CompileUnit *lldb_cu = GetCompUnitForDWARFCompUnit(*dwarf_cu))
+ return lldb_cu->GetSupportFiles().GetFileSpecAtIndex(file_idx);
+ return FileSpec();
+ }
+
+ auto &tu = llvm::cast<DWARFTypeUnit>(unit);
+ return GetTypeUnitSupportFiles(tu).GetFileSpecAtIndex(file_idx);
+}
+
+const FileSpecList &
+SymbolFileDWARF::GetTypeUnitSupportFiles(DWARFTypeUnit &tu) {
+ static FileSpecList empty_list;
+
+ dw_offset_t offset = tu.GetLineTableOffset();
+ if (offset == DW_INVALID_OFFSET ||
+ offset == llvm::DenseMapInfo<dw_offset_t>::getEmptyKey() ||
+ offset == llvm::DenseMapInfo<dw_offset_t>::getTombstoneKey())
+ return empty_list;
+
+ // Many type units can share a line table, so parse the support file list
+ // once, and cache it based on the offset field.
+ auto iter_bool = m_type_unit_support_files.try_emplace(offset);
+ FileSpecList &list = iter_bool.first->second;
+ if (iter_bool.second) {
+ list.Append(FileSpec());
+ DWARFDebugLine::ParseSupportFiles(GetObjectFile()->GetModule(),
+ m_context.getOrLoadLineData(), offset,
+ list, &tu);
+ }
+ return list;
+}
+
+bool SymbolFileDWARF::ParseIsOptimized(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (dwarf_cu)
+ return dwarf_cu->GetIsOptimized();
+ return false;
+}
+
+bool SymbolFileDWARF::ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<SourceModule> &imported_modules) {
+ ASSERT_MODULE_LOCK(this);
+ assert(sc.comp_unit);
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit);
+ if (!dwarf_cu)
+ return false;
+ if (!ClangModulesDeclVendor::LanguageSupportsClangModules(
+ sc.comp_unit->GetLanguage()))
+ return false;
+ UpdateExternalModuleListIfNeeded();
+
+ const DWARFDIE die = dwarf_cu->DIE();
+ if (!die)
+ return false;
+
+ for (DWARFDIE child_die = die.GetFirstChild(); child_die;
+ child_die = child_die.GetSibling()) {
+ if (child_die.Tag() != DW_TAG_imported_declaration)
+ continue;
+
+ DWARFDIE module_die = child_die.GetReferencedDIE(DW_AT_import);
+ if (module_die.Tag() != DW_TAG_module)
+ continue;
+
+ if (const char *name =
+ module_die.GetAttributeValueAsString(DW_AT_name, nullptr)) {
+ SourceModule module;
+ module.path.push_back(ConstString(name));
+
+ DWARFDIE parent_die = module_die;
+ while ((parent_die = parent_die.GetParent())) {
+ if (parent_die.Tag() != DW_TAG_module)
+ break;
+ if (const char *name =
+ parent_die.GetAttributeValueAsString(DW_AT_name, nullptr))
+ module.path.push_back(ConstString(name));
+ }
+ std::reverse(module.path.begin(), module.path.end());
+ if (const char *include_path = module_die.GetAttributeValueAsString(
+ DW_AT_LLVM_include_path, nullptr))
+ module.search_path = ConstString(include_path);
+ if (const char *sysroot = module_die.GetAttributeValueAsString(
+ DW_AT_LLVM_isysroot, nullptr))
+ module.sysroot = ConstString(sysroot);
+ imported_modules.push_back(module);
+ }
+ }
+ return true;
+}
+
+struct ParseDWARFLineTableCallbackInfo {
+ LineTable *line_table;
+ std::unique_ptr<LineSequence> sequence_up;
+ lldb::addr_t addr_mask;
+};
+
+// ParseStatementTableCallback
+static void ParseDWARFLineTableCallback(dw_offset_t offset,
+ const DWARFDebugLine::State &state,
+ void *userData) {
+ if (state.row == DWARFDebugLine::State::StartParsingLineTable) {
+ // Just started parsing the line table
+ } else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) {
+ // Done parsing line table, nothing to do for the cleanup
+ } else {
+ ParseDWARFLineTableCallbackInfo *info =
+ (ParseDWARFLineTableCallbackInfo *)userData;
+ LineTable *line_table = info->line_table;
+
+ // If this is our first time here, we need to create a sequence container.
+ if (!info->sequence_up) {
+ info->sequence_up.reset(line_table->CreateLineSequenceContainer());
+ assert(info->sequence_up.get());
+ }
+ line_table->AppendLineEntryToSequence(
+ info->sequence_up.get(), state.address & info->addr_mask, state.line,
+ state.column, state.file, state.is_stmt, state.basic_block,
+ state.prologue_end, state.epilogue_begin, state.end_sequence);
+ if (state.end_sequence) {
+ // First, put the current sequence into the line table.
+ line_table->InsertSequence(info->sequence_up.get());
+ // Then, empty it to prepare for the next sequence.
+ info->sequence_up->Clear();
+ }
+ }
+}
+
+bool SymbolFileDWARF::ParseLineTable(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+ if (comp_unit.GetLineTable() != nullptr)
+ return true;
+
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (dwarf_cu) {
+ const DWARFBaseDIE dwarf_cu_die = dwarf_cu->GetUnitDIEOnly();
+ if (dwarf_cu_die) {
+ const dw_offset_t cu_line_offset =
+ dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_stmt_list,
+ DW_INVALID_OFFSET);
+ if (cu_line_offset != DW_INVALID_OFFSET) {
+ std::unique_ptr<LineTable> line_table_up(new LineTable(&comp_unit));
+ if (line_table_up) {
+ ParseDWARFLineTableCallbackInfo info;
+ info.line_table = line_table_up.get();
+
+ /*
+ * MIPS:
+ * The SymbolContext may not have a valid target, thus we may not be
+ * able
+ * to call Address::GetOpcodeLoadAddress() which would clear the bit
+ * #0
+ * for MIPS. Use ArchSpec to clear the bit #0.
+ */
+ switch (GetObjectFile()->GetArchitecture().GetMachine()) {
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ info.addr_mask = ~((lldb::addr_t)1);
+ break;
+ default:
+ info.addr_mask = ~((lldb::addr_t)0);
+ break;
+ }
+
+ lldb::offset_t offset = cu_line_offset;
+ DWARFDebugLine::ParseStatementTable(
+ m_context.getOrLoadLineData(), &offset,
+ ParseDWARFLineTableCallback, &info, dwarf_cu);
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ if (debug_map_symfile) {
+ // We have an object file that has a line table with addresses that
+ // are not linked. We need to link the line table and convert the
+ // addresses that are relative to the .o file into addresses for
+ // the main executable.
+ comp_unit.SetLineTable(
+ debug_map_symfile->LinkOSOLineTable(this, line_table_up.get()));
+ } else {
+ comp_unit.SetLineTable(line_table_up.release());
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+lldb_private::DebugMacrosSP
+SymbolFileDWARF::ParseDebugMacros(lldb::offset_t *offset) {
+ auto iter = m_debug_macros_map.find(*offset);
+ if (iter != m_debug_macros_map.end())
+ return iter->second;
+
+ const DWARFDataExtractor &debug_macro_data = m_context.getOrLoadMacroData();
+ if (debug_macro_data.GetByteSize() == 0)
+ return DebugMacrosSP();
+
+ lldb_private::DebugMacrosSP debug_macros_sp(new lldb_private::DebugMacros());
+ m_debug_macros_map[*offset] = debug_macros_sp;
+
+ const DWARFDebugMacroHeader &header =
+ DWARFDebugMacroHeader::ParseHeader(debug_macro_data, offset);
+ DWARFDebugMacroEntry::ReadMacroEntries(
+ debug_macro_data, m_context.getOrLoadStrData(), header.OffsetIs64Bit(),
+ offset, this, debug_macros_sp);
+
+ return debug_macros_sp;
+}
+
+bool SymbolFileDWARF::ParseDebugMacros(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (dwarf_cu == nullptr)
+ return false;
+
+ const DWARFBaseDIE dwarf_cu_die = dwarf_cu->GetUnitDIEOnly();
+ if (!dwarf_cu_die)
+ return false;
+
+ lldb::offset_t sect_offset =
+ dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_macros, DW_INVALID_OFFSET);
+ if (sect_offset == DW_INVALID_OFFSET)
+ sect_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_GNU_macros,
+ DW_INVALID_OFFSET);
+ if (sect_offset == DW_INVALID_OFFSET)
+ return false;
+
+ comp_unit.SetDebugMacros(ParseDebugMacros(&sect_offset));
+
+ return true;
+}
+
+size_t SymbolFileDWARF::ParseBlocksRecursive(
+ lldb_private::CompileUnit &comp_unit, Block *parent_block,
+ const DWARFDIE &orig_die, addr_t subprogram_low_pc, uint32_t depth) {
+ size_t blocks_added = 0;
+ DWARFDIE die = orig_die;
+ while (die) {
+ dw_tag_t tag = die.Tag();
+
+ switch (tag) {
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_subprogram:
+ case DW_TAG_lexical_block: {
+ Block *block = nullptr;
+ if (tag == DW_TAG_subprogram) {
+ // Skip any DW_TAG_subprogram DIEs that are inside of a normal or
+ // inlined functions. These will be parsed on their own as separate
+ // entities.
+
+ if (depth > 0)
+ break;
+
+ block = parent_block;
+ } else {
+ BlockSP block_sp(new Block(die.GetID()));
+ parent_block->AddChild(block_sp);
+ block = block_sp.get();
+ }
+ DWARFRangeList ranges;
+ const char *name = nullptr;
+ const char *mangled_name = nullptr;
+
+ int decl_file = 0;
+ int decl_line = 0;
+ int decl_column = 0;
+ int call_file = 0;
+ int call_line = 0;
+ int call_column = 0;
+ if (die.GetDIENamesAndRanges(name, mangled_name, ranges, decl_file,
+ decl_line, decl_column, call_file, call_line,
+ call_column, nullptr)) {
+ if (tag == DW_TAG_subprogram) {
+ assert(subprogram_low_pc == LLDB_INVALID_ADDRESS);
+ subprogram_low_pc = ranges.GetMinRangeBase(0);
+ } else if (tag == DW_TAG_inlined_subroutine) {
+ // We get called here for inlined subroutines in two ways. The first
+ // time is when we are making the Function object for this inlined
+ // concrete instance. Since we're creating a top level block at
+ // here, the subprogram_low_pc will be LLDB_INVALID_ADDRESS. So we
+ // need to adjust the containing address. The second time is when we
+ // are parsing the blocks inside the function that contains the
+ // inlined concrete instance. Since these will be blocks inside the
+ // containing "real" function the offset will be for that function.
+ if (subprogram_low_pc == LLDB_INVALID_ADDRESS) {
+ subprogram_low_pc = ranges.GetMinRangeBase(0);
+ }
+ }
+
+ const size_t num_ranges = ranges.GetSize();
+ for (size_t i = 0; i < num_ranges; ++i) {
+ const DWARFRangeList::Entry &range = ranges.GetEntryRef(i);
+ const addr_t range_base = range.GetRangeBase();
+ if (range_base >= subprogram_low_pc)
+ block->AddRange(Block::Range(range_base - subprogram_low_pc,
+ range.GetByteSize()));
+ else {
+ GetObjectFile()->GetModule()->ReportError(
+ "0x%8.8" PRIx64 ": adding range [0x%" PRIx64 "-0x%" PRIx64
+ ") which has a base that is less than the function's low PC "
+ "0x%" PRIx64 ". Please file a bug and attach the file at the "
+ "start of this error message",
+ block->GetID(), range_base, range.GetRangeEnd(),
+ subprogram_low_pc);
+ }
+ }
+ block->FinalizeRanges();
+
+ if (tag != DW_TAG_subprogram &&
+ (name != nullptr || mangled_name != nullptr)) {
+ std::unique_ptr<Declaration> decl_up;
+ if (decl_file != 0 || decl_line != 0 || decl_column != 0)
+ decl_up.reset(new Declaration(
+ comp_unit.GetSupportFiles().GetFileSpecAtIndex(decl_file),
+ decl_line, decl_column));
+
+ std::unique_ptr<Declaration> call_up;
+ if (call_file != 0 || call_line != 0 || call_column != 0)
+ call_up.reset(new Declaration(
+ comp_unit.GetSupportFiles().GetFileSpecAtIndex(call_file),
+ call_line, call_column));
+
+ block->SetInlinedFunctionInfo(name, mangled_name, decl_up.get(),
+ call_up.get());
+ }
+
+ ++blocks_added;
+
+ if (die.HasChildren()) {
+ blocks_added +=
+ ParseBlocksRecursive(comp_unit, block, die.GetFirstChild(),
+ subprogram_low_pc, depth + 1);
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+
+ // Only parse siblings of the block if we are not at depth zero. A depth of
+ // zero indicates we are currently parsing the top level DW_TAG_subprogram
+ // DIE
+
+ if (depth == 0)
+ die.Clear();
+ else
+ die = die.GetSibling();
+ }
+ return blocks_added;
+}
+
+bool SymbolFileDWARF::ClassOrStructIsVirtual(const DWARFDIE &parent_die) {
+ if (parent_die) {
+ for (DWARFDIE die = parent_die.GetFirstChild(); die;
+ die = die.GetSibling()) {
+ dw_tag_t tag = die.Tag();
+ bool check_virtuality = false;
+ switch (tag) {
+ case DW_TAG_inheritance:
+ case DW_TAG_subprogram:
+ check_virtuality = true;
+ break;
+ default:
+ break;
+ }
+ if (check_virtuality) {
+ if (die.GetAttributeValueAsUnsigned(DW_AT_virtuality, 0) != 0)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void SymbolFileDWARF::ParseDeclsForContext(CompilerDeclContext decl_ctx) {
+ TypeSystem *type_system = decl_ctx.GetTypeSystem();
+ DWARFASTParser *ast_parser = type_system->GetDWARFParser();
+ std::vector<DWARFDIE> decl_ctx_die_list =
+ ast_parser->GetDIEForDeclContext(decl_ctx);
+
+ for (DWARFDIE decl_ctx_die : decl_ctx_die_list)
+ for (DWARFDIE decl = decl_ctx_die.GetFirstChild(); decl;
+ decl = decl.GetSibling())
+ ast_parser->GetDeclForUIDFromDWARF(decl);
+}
+
+user_id_t SymbolFileDWARF::GetUID(DIERef ref) {
+ if (GetDebugMapSymfile())
+ return GetID() | ref.die_offset();
+
+ return user_id_t(GetDwoNum().getValueOr(0x7fffffff)) << 32 |
+ ref.die_offset() |
+ (lldb::user_id_t(ref.section() == DIERef::Section::DebugTypes) << 63);
+}
+
+llvm::Optional<SymbolFileDWARF::DecodedUID>
+SymbolFileDWARF::DecodeUID(lldb::user_id_t uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ // Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API we
+ // must make sure we use the correct DWARF file when resolving things. On
+ // MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple
+ // SymbolFileDWARF classes, one for each .o file. We can often end up with
+ // references to other DWARF objects and we must be ready to receive a
+ // "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF
+ // instance.
+ if (SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile()) {
+ SymbolFileDWARF *dwarf = debug_map->GetSymbolFileByOSOIndex(
+ debug_map->GetOSOIndexFromUserID(uid));
+ return DecodedUID{
+ *dwarf, {llvm::None, DIERef::Section::DebugInfo, dw_offset_t(uid)}};
+ }
+ dw_offset_t die_offset = uid;
+ if (die_offset == DW_INVALID_OFFSET)
+ return llvm::None;
+
+ DIERef::Section section =
+ uid >> 63 ? DIERef::Section::DebugTypes : DIERef::Section::DebugInfo;
+
+ llvm::Optional<uint32_t> dwo_num = uid >> 32 & 0x7fffffff;
+ if (*dwo_num == 0x7fffffff)
+ dwo_num = llvm::None;
+
+ return DecodedUID{*this, {dwo_num, section, die_offset}};
+}
+
+DWARFDIE
+SymbolFileDWARF::GetDIE(lldb::user_id_t uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+
+ llvm::Optional<DecodedUID> decoded = DecodeUID(uid);
+
+ if (decoded)
+ return decoded->dwarf.GetDIE(decoded->ref);
+
+ return DWARFDIE();
+}
+
+CompilerDecl SymbolFileDWARF::GetDeclForUID(lldb::user_id_t type_uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ // Anytime we have a lldb::user_id_t, we must get the DIE by calling
+ // SymbolFileDWARF::GetDIE(). See comments inside the
+ // SymbolFileDWARF::GetDIE() for details.
+ if (DWARFDIE die = GetDIE(type_uid))
+ return die.GetDecl();
+ return CompilerDecl();
+}
+
+CompilerDeclContext
+SymbolFileDWARF::GetDeclContextForUID(lldb::user_id_t type_uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ // Anytime we have a lldb::user_id_t, we must get the DIE by calling
+ // SymbolFileDWARF::GetDIE(). See comments inside the
+ // SymbolFileDWARF::GetDIE() for details.
+ if (DWARFDIE die = GetDIE(type_uid))
+ return die.GetDeclContext();
+ return CompilerDeclContext();
+}
+
+CompilerDeclContext
+SymbolFileDWARF::GetDeclContextContainingUID(lldb::user_id_t type_uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ // Anytime we have a lldb::user_id_t, we must get the DIE by calling
+ // SymbolFileDWARF::GetDIE(). See comments inside the
+ // SymbolFileDWARF::GetDIE() for details.
+ if (DWARFDIE die = GetDIE(type_uid))
+ return die.GetContainingDeclContext();
+ return CompilerDeclContext();
+}
+
+Type *SymbolFileDWARF::ResolveTypeUID(lldb::user_id_t type_uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ // Anytime we have a lldb::user_id_t, we must get the DIE by calling
+ // SymbolFileDWARF::GetDIE(). See comments inside the
+ // SymbolFileDWARF::GetDIE() for details.
+ if (DWARFDIE type_die = GetDIE(type_uid))
+ return type_die.ResolveType();
+ else
+ return nullptr;
+}
+
+llvm::Optional<SymbolFile::ArrayInfo>
+SymbolFileDWARF::GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ if (DWARFDIE type_die = GetDIE(type_uid))
+ return DWARFASTParser::ParseChildArrayInfo(type_die, exe_ctx);
+ else
+ return llvm::None;
+}
+
+Type *SymbolFileDWARF::ResolveTypeUID(const DIERef &die_ref) {
+ return ResolveType(GetDIE(die_ref), true);
+}
+
+Type *SymbolFileDWARF::ResolveTypeUID(const DWARFDIE &die,
+ bool assert_not_being_parsed) {
+ if (die) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO));
+ if (log)
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s'",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+
+ // We might be coming in in the middle of a type tree (a class within a
+ // class, an enum within a class), so parse any needed parent DIEs before
+ // we get to this one...
+ DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(die);
+ if (decl_ctx_die) {
+ if (log) {
+ switch (decl_ctx_die.Tag()) {
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type: {
+ // Get the type, which could be a forward declaration
+ if (log)
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s' "
+ "resolve parent forward type for 0x%8.8x",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName(),
+ decl_ctx_die.GetOffset());
+ } break;
+
+ default:
+ break;
+ }
+ }
+ }
+ return ResolveType(die);
+ }
+ return nullptr;
+}
+
+// This function is used when SymbolFileDWARFDebugMap owns a bunch of
+// SymbolFileDWARF objects to detect if this DWARF file is the one that can
+// resolve a compiler_type.
+bool SymbolFileDWARF::HasForwardDeclForClangType(
+ const CompilerType &compiler_type) {
+ CompilerType compiler_type_no_qualifiers =
+ ClangUtil::RemoveFastQualifiers(compiler_type);
+ if (GetForwardDeclClangTypeToDie().count(
+ compiler_type_no_qualifiers.GetOpaqueQualType())) {
+ return true;
+ }
+ TypeSystem *type_system = compiler_type.GetTypeSystem();
+
+ ClangASTContext *clang_type_system =
+ llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang_type_system)
+ return false;
+ DWARFASTParserClang *ast_parser =
+ static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser());
+ return ast_parser->GetClangASTImporter().CanImport(compiler_type);
+}
+
+bool SymbolFileDWARF::CompleteType(CompilerType &compiler_type) {
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+
+ ClangASTContext *clang_type_system =
+ llvm::dyn_cast_or_null<ClangASTContext>(compiler_type.GetTypeSystem());
+ if (clang_type_system) {
+ DWARFASTParserClang *ast_parser =
+ static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser());
+ if (ast_parser &&
+ ast_parser->GetClangASTImporter().CanImport(compiler_type))
+ return ast_parser->GetClangASTImporter().CompleteType(compiler_type);
+ }
+
+ // We have a struct/union/class/enum that needs to be fully resolved.
+ CompilerType compiler_type_no_qualifiers =
+ ClangUtil::RemoveFastQualifiers(compiler_type);
+ auto die_it = GetForwardDeclClangTypeToDie().find(
+ compiler_type_no_qualifiers.GetOpaqueQualType());
+ if (die_it == GetForwardDeclClangTypeToDie().end()) {
+ // We have already resolved this type...
+ return true;
+ }
+
+ DWARFDIE dwarf_die = GetDIE(die_it->getSecond());
+ if (dwarf_die) {
+ // Once we start resolving this type, remove it from the forward
+ // declaration map in case anyone child members or other types require this
+ // type to get resolved. The type will get resolved when all of the calls
+ // to SymbolFileDWARF::ResolveClangOpaqueTypeDefinition are done.
+ GetForwardDeclClangTypeToDie().erase(die_it);
+
+ Type *type = GetDIEToType().lookup(dwarf_die.GetDIE());
+
+ Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO |
+ DWARF_LOG_TYPE_COMPLETION));
+ if (log)
+ GetObjectFile()->GetModule()->LogMessageVerboseBacktrace(
+ log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...",
+ dwarf_die.GetID(), dwarf_die.GetTagAsCString(),
+ type->GetName().AsCString());
+ assert(compiler_type);
+ DWARFASTParser *dwarf_ast = dwarf_die.GetDWARFParser();
+ if (dwarf_ast)
+ return dwarf_ast->CompleteTypeFromDWARF(dwarf_die, type, compiler_type);
+ }
+ return false;
+}
+
+Type *SymbolFileDWARF::ResolveType(const DWARFDIE &die,
+ bool assert_not_being_parsed,
+ bool resolve_function_context) {
+ if (die) {
+ Type *type = GetTypeForDIE(die, resolve_function_context).get();
+
+ if (assert_not_being_parsed) {
+ if (type != DIE_IS_BEING_PARSED)
+ return type;
+
+ GetObjectFile()->GetModule()->ReportError(
+ "Parsing a die that is being parsed die: 0x%8.8x: %s %s",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+
+ } else
+ return type;
+ }
+ return nullptr;
+}
+
+CompileUnit *
+SymbolFileDWARF::GetCompUnitForDWARFCompUnit(DWARFCompileUnit &dwarf_cu) {
+ // Check if the symbol vendor already knows about this compile unit?
+ if (dwarf_cu.GetUserData() == nullptr) {
+ // The symbol vendor doesn't know about this compile unit, we need to parse
+ // and add it to the symbol vendor object.
+ return ParseCompileUnit(dwarf_cu).get();
+ }
+ return (CompileUnit *)dwarf_cu.GetUserData();
+}
+
+size_t SymbolFileDWARF::GetObjCMethodDIEOffsets(ConstString class_name,
+ DIEArray &method_die_offsets) {
+ method_die_offsets.clear();
+ m_index->GetObjCMethods(class_name, method_die_offsets);
+ return method_die_offsets.size();
+}
+
+bool SymbolFileDWARF::GetFunction(const DWARFDIE &die, SymbolContext &sc) {
+ sc.Clear(false);
+
+ if (die && llvm::isa<DWARFCompileUnit>(die.GetCU())) {
+ // Check if the symbol vendor already knows about this compile unit?
+ sc.comp_unit =
+ GetCompUnitForDWARFCompUnit(llvm::cast<DWARFCompileUnit>(*die.GetCU()));
+
+ sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get();
+ if (sc.function == nullptr)
+ sc.function = ParseFunction(*sc.comp_unit, die);
+
+ if (sc.function) {
+ sc.module_sp = sc.function->CalculateSymbolContextModule();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+lldb::ModuleSP SymbolFileDWARF::GetDWOModule(ConstString name) {
+ UpdateExternalModuleListIfNeeded();
+ const auto &pos = m_external_type_modules.find(name);
+ if (pos != m_external_type_modules.end())
+ return pos->second;
+ else
+ return lldb::ModuleSP();
+}
+
+DWARFDIE
+SymbolFileDWARF::GetDIE(const DIERef &die_ref) {
+ if (die_ref.dwo_num()) {
+ return DebugInfo()
+ ->GetUnitAtIndex(*die_ref.dwo_num())
+ ->GetDwoSymbolFile()
+ ->GetDIE(die_ref);
+ }
+
+
+ DWARFDebugInfo *debug_info = DebugInfo();
+ if (debug_info)
+ return debug_info->GetDIE(die_ref);
+ else
+ return DWARFDIE();
+}
+
+std::unique_ptr<SymbolFileDWARFDwo>
+SymbolFileDWARF::GetDwoSymbolFileForCompileUnit(
+ DWARFUnit &unit, const DWARFDebugInfoEntry &cu_die) {
+ // If we are using a dSYM file, we never want the standard DWO files since
+ // the -gmodules support uses the same DWO machanism to specify full debug
+ // info files for modules.
+ if (GetDebugMapSymfile())
+ return nullptr;
+
+ DWARFCompileUnit *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(&unit);
+ // Only compile units can be split into two parts.
+ if (!dwarf_cu)
+ return nullptr;
+
+ const char *dwo_name =
+ cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_GNU_dwo_name, nullptr);
+ if (!dwo_name)
+ return nullptr;
+
+ SymbolFileDWARFDwp *dwp_symfile = GetDwpSymbolFile();
+ if (dwp_symfile) {
+ uint64_t dwo_id =
+ cu_die.GetAttributeValueAsUnsigned(dwarf_cu, DW_AT_GNU_dwo_id, 0);
+ std::unique_ptr<SymbolFileDWARFDwo> dwo_symfile =
+ dwp_symfile->GetSymbolFileForDwoId(*dwarf_cu, dwo_id);
+ if (dwo_symfile)
+ return dwo_symfile;
+ }
+
+ FileSpec dwo_file(dwo_name);
+ FileSystem::Instance().Resolve(dwo_file);
+ if (dwo_file.IsRelative()) {
+ const char *comp_dir =
+ cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr);
+ if (!comp_dir)
+ return nullptr;
+
+ dwo_file.SetFile(comp_dir, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(dwo_file);
+ dwo_file.AppendPathComponent(dwo_name);
+ }
+
+ if (!FileSystem::Instance().Exists(dwo_file))
+ return nullptr;
+
+ const lldb::offset_t file_offset = 0;
+ DataBufferSP dwo_file_data_sp;
+ lldb::offset_t dwo_file_data_offset = 0;
+ ObjectFileSP dwo_obj_file = ObjectFile::FindPlugin(
+ GetObjectFile()->GetModule(), &dwo_file, file_offset,
+ FileSystem::Instance().GetByteSize(dwo_file), dwo_file_data_sp,
+ dwo_file_data_offset);
+ if (dwo_obj_file == nullptr)
+ return nullptr;
+
+ return llvm::make_unique<SymbolFileDWARFDwo>(dwo_obj_file, *dwarf_cu);
+}
+
+void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() {
+ if (m_fetched_external_modules)
+ return;
+ m_fetched_external_modules = true;
+
+ DWARFDebugInfo *debug_info = DebugInfo();
+
+ const uint32_t num_compile_units = GetNumCompileUnits();
+ for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) {
+ DWARFUnit *dwarf_cu = debug_info->GetUnitAtIndex(cu_idx);
+
+ const DWARFBaseDIE die = dwarf_cu->GetUnitDIEOnly();
+ if (die && !die.HasChildren()) {
+ const char *name = die.GetAttributeValueAsString(DW_AT_name, nullptr);
+
+ if (name) {
+ ConstString const_name(name);
+ if (m_external_type_modules.find(const_name) ==
+ m_external_type_modules.end()) {
+ ModuleSP module_sp;
+ const char *dwo_path =
+ die.GetAttributeValueAsString(DW_AT_GNU_dwo_name, nullptr);
+ if (dwo_path) {
+ ModuleSpec dwo_module_spec;
+ dwo_module_spec.GetFileSpec().SetFile(dwo_path,
+ FileSpec::Style::native);
+ if (dwo_module_spec.GetFileSpec().IsRelative()) {
+ const char *comp_dir =
+ die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr);
+ if (comp_dir) {
+ dwo_module_spec.GetFileSpec().SetFile(comp_dir,
+ FileSpec::Style::native);
+ FileSystem::Instance().Resolve(dwo_module_spec.GetFileSpec());
+ dwo_module_spec.GetFileSpec().AppendPathComponent(dwo_path);
+ }
+ }
+ dwo_module_spec.GetArchitecture() =
+ m_obj_file->GetModule()->GetArchitecture();
+
+ // When LLDB loads "external" modules it looks at the presence of
+ // DW_AT_GNU_dwo_name. However, when the already created module
+ // (corresponding to .dwo itself) is being processed, it will see
+ // the presence of DW_AT_GNU_dwo_name (which contains the name of
+ // dwo file) and will try to call ModuleList::GetSharedModule
+ // again. In some cases (i.e. for empty files) Clang 4.0 generates
+ // a *.dwo file which has DW_AT_GNU_dwo_name, but no
+ // DW_AT_comp_dir. In this case the method
+ // ModuleList::GetSharedModule will fail and the warning will be
+ // printed. However, as one can notice in this case we don't
+ // actually need to try to load the already loaded module
+ // (corresponding to .dwo) so we simply skip it.
+ if (m_obj_file->GetFileSpec().GetFileNameExtension() == ".dwo" &&
+ llvm::StringRef(m_obj_file->GetFileSpec().GetPath())
+ .endswith(dwo_module_spec.GetFileSpec().GetPath())) {
+ continue;
+ }
+
+ Status error = ModuleList::GetSharedModule(
+ dwo_module_spec, module_sp, nullptr, nullptr, nullptr);
+ if (!module_sp) {
+ GetObjectFile()->GetModule()->ReportWarning(
+ "0x%8.8x: unable to locate module needed for external types: "
+ "%s\nerror: %s\nDebugging will be degraded due to missing "
+ "types. Rebuilding your project will regenerate the needed "
+ "module files.",
+ die.GetOffset(),
+ dwo_module_spec.GetFileSpec().GetPath().c_str(),
+ error.AsCString("unknown error"));
+ }
+ }
+ m_external_type_modules[const_name] = module_sp;
+ }
+ }
+ }
+ }
+}
+
+SymbolFileDWARF::GlobalVariableMap &SymbolFileDWARF::GetGlobalAranges() {
+ if (!m_global_aranges_up) {
+ m_global_aranges_up.reset(new GlobalVariableMap());
+
+ ModuleSP module_sp = GetObjectFile()->GetModule();
+ if (module_sp) {
+ const size_t num_cus = module_sp->GetNumCompileUnits();
+ for (size_t i = 0; i < num_cus; ++i) {
+ CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(i);
+ if (cu_sp) {
+ VariableListSP globals_sp = cu_sp->GetVariableList(true);
+ if (globals_sp) {
+ const size_t num_globals = globals_sp->GetSize();
+ for (size_t g = 0; g < num_globals; ++g) {
+ VariableSP var_sp = globals_sp->GetVariableAtIndex(g);
+ if (var_sp && !var_sp->GetLocationIsConstantValueData()) {
+ const DWARFExpression &location = var_sp->LocationExpression();
+ Value location_result;
+ Status error;
+ if (location.Evaluate(nullptr, LLDB_INVALID_ADDRESS, nullptr,
+ nullptr, location_result, &error)) {
+ if (location_result.GetValueType() ==
+ Value::eValueTypeFileAddress) {
+ lldb::addr_t file_addr =
+ location_result.GetScalar().ULongLong();
+ lldb::addr_t byte_size = 1;
+ if (var_sp->GetType())
+ byte_size =
+ var_sp->GetType()->GetByteSize().getValueOr(0);
+ m_global_aranges_up->Append(GlobalVariableMap::Entry(
+ file_addr, byte_size, var_sp.get()));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ m_global_aranges_up->Sort();
+ }
+ return *m_global_aranges_up;
+}
+
+uint32_t SymbolFileDWARF::ResolveSymbolContext(const Address &so_addr,
+ SymbolContextItem resolve_scope,
+ SymbolContext &sc) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "SymbolFileDWARF::"
+ "ResolveSymbolContext (so_addr = { "
+ "section = %p, offset = 0x%" PRIx64
+ " }, resolve_scope = 0x%8.8x)",
+ static_cast<void *>(so_addr.GetSection().get()),
+ so_addr.GetOffset(), resolve_scope);
+ uint32_t resolved = 0;
+ if (resolve_scope &
+ (eSymbolContextCompUnit | eSymbolContextFunction | eSymbolContextBlock |
+ eSymbolContextLineEntry | eSymbolContextVariable)) {
+ lldb::addr_t file_vm_addr = so_addr.GetFileAddress();
+
+ DWARFDebugInfo *debug_info = DebugInfo();
+ if (debug_info) {
+ llvm::Expected<DWARFDebugAranges &> aranges =
+ debug_info->GetCompileUnitAranges();
+ if (!aranges) {
+ Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
+ LLDB_LOG_ERROR(log, aranges.takeError(),
+ "SymbolFileDWARF::ResolveSymbolContext failed to get cu "
+ "aranges. {0}");
+ return 0;
+ }
+
+ const dw_offset_t cu_offset = aranges->FindAddress(file_vm_addr);
+ if (cu_offset == DW_INVALID_OFFSET) {
+ // Global variables are not in the compile unit address ranges. The
+ // only way to currently find global variables is to iterate over the
+ // .debug_pubnames or the __apple_names table and find all items in
+ // there that point to DW_TAG_variable DIEs and then find the address
+ // that matches.
+ if (resolve_scope & eSymbolContextVariable) {
+ GlobalVariableMap &map = GetGlobalAranges();
+ const GlobalVariableMap::Entry *entry =
+ map.FindEntryThatContains(file_vm_addr);
+ if (entry && entry->data) {
+ Variable *variable = entry->data;
+ SymbolContextScope *scc = variable->GetSymbolContextScope();
+ if (scc) {
+ scc->CalculateSymbolContext(&sc);
+ sc.variable = variable;
+ }
+ return sc.GetResolvedMask();
+ }
+ }
+ } else {
+ uint32_t cu_idx = DW_INVALID_INDEX;
+ if (auto *dwarf_cu = llvm::dyn_cast_or_null<DWARFCompileUnit>(
+ debug_info->GetUnitAtOffset(DIERef::Section::DebugInfo,
+ cu_offset, &cu_idx))) {
+ sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu);
+ if (sc.comp_unit) {
+ resolved |= eSymbolContextCompUnit;
+
+ bool force_check_line_table = false;
+ if (resolve_scope &
+ (eSymbolContextFunction | eSymbolContextBlock)) {
+ DWARFDIE function_die = dwarf_cu->LookupAddress(file_vm_addr);
+ DWARFDIE block_die;
+ if (function_die) {
+ sc.function =
+ sc.comp_unit->FindFunctionByUID(function_die.GetID()).get();
+ if (sc.function == nullptr)
+ sc.function = ParseFunction(*sc.comp_unit, function_die);
+
+ if (sc.function && (resolve_scope & eSymbolContextBlock))
+ block_die = function_die.LookupDeepestBlock(file_vm_addr);
+ } else {
+ // We might have had a compile unit that had discontiguous
+ // address ranges where the gaps are symbols that don't have
+ // any debug info. Discontiguous compile unit address ranges
+ // should only happen when there aren't other functions from
+ // other compile units in these gaps. This helps keep the size
+ // of the aranges down.
+ force_check_line_table = true;
+ }
+
+ if (sc.function != nullptr) {
+ resolved |= eSymbolContextFunction;
+
+ if (resolve_scope & eSymbolContextBlock) {
+ Block &block = sc.function->GetBlock(true);
+
+ if (block_die)
+ sc.block = block.FindBlockByID(block_die.GetID());
+ else
+ sc.block = block.FindBlockByID(function_die.GetID());
+ if (sc.block)
+ resolved |= eSymbolContextBlock;
+ }
+ }
+ }
+
+ if ((resolve_scope & eSymbolContextLineEntry) ||
+ force_check_line_table) {
+ LineTable *line_table = sc.comp_unit->GetLineTable();
+ if (line_table != nullptr) {
+ // And address that makes it into this function should be in
+ // terms of this debug file if there is no debug map, or it
+ // will be an address in the .o file which needs to be fixed up
+ // to be in terms of the debug map executable. Either way,
+ // calling FixupAddress() will work for us.
+ Address exe_so_addr(so_addr);
+ if (FixupAddress(exe_so_addr)) {
+ if (line_table->FindLineEntryByAddress(exe_so_addr,
+ sc.line_entry)) {
+ resolved |= eSymbolContextLineEntry;
+ }
+ }
+ }
+ }
+
+ if (force_check_line_table &&
+ !(resolved & eSymbolContextLineEntry)) {
+ // We might have had a compile unit that had discontiguous
+ // address ranges where the gaps are symbols that don't have any
+ // debug info. Discontiguous compile unit address ranges should
+ // only happen when there aren't other functions from other
+ // compile units in these gaps. This helps keep the size of the
+ // aranges down.
+ sc.comp_unit = nullptr;
+ resolved &= ~eSymbolContextCompUnit;
+ }
+ } else {
+ GetObjectFile()->GetModule()->ReportWarning(
+ "0x%8.8x: compile unit %u failed to create a valid "
+ "lldb_private::CompileUnit class.",
+ cu_offset, cu_idx);
+ }
+ }
+ }
+ }
+ }
+ return resolved;
+}
+
+uint32_t SymbolFileDWARF::ResolveSymbolContext(const FileSpec &file_spec,
+ uint32_t line,
+ bool check_inlines,
+ SymbolContextItem resolve_scope,
+ SymbolContextList &sc_list) {
+ const uint32_t prev_size = sc_list.GetSize();
+ if (resolve_scope & eSymbolContextCompUnit) {
+ for (uint32_t cu_idx = 0, num_cus = GetNumCompileUnits(); cu_idx < num_cus;
+ ++cu_idx) {
+ CompileUnit *dc_cu = ParseCompileUnitAtIndex(cu_idx).get();
+ if (!dc_cu)
+ continue;
+
+ const bool full_match = (bool)file_spec.GetDirectory();
+ bool file_spec_matches_cu_file_spec =
+ FileSpec::Equal(file_spec, *dc_cu, full_match);
+ if (check_inlines || file_spec_matches_cu_file_spec) {
+ SymbolContext sc(m_obj_file->GetModule());
+ sc.comp_unit = dc_cu;
+ uint32_t file_idx = UINT32_MAX;
+
+ // If we are looking for inline functions only and we don't find it
+ // in the support files, we are done.
+ if (check_inlines) {
+ file_idx =
+ sc.comp_unit->GetSupportFiles().FindFileIndex(1, file_spec, true);
+ if (file_idx == UINT32_MAX)
+ continue;
+ }
+
+ if (line != 0) {
+ LineTable *line_table = sc.comp_unit->GetLineTable();
+
+ if (line_table != nullptr && line != 0) {
+ // We will have already looked up the file index if we are
+ // searching for inline entries.
+ if (!check_inlines)
+ file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex(
+ 1, file_spec, true);
+
+ if (file_idx != UINT32_MAX) {
+ uint32_t found_line;
+ uint32_t line_idx = line_table->FindLineEntryIndexByFileIndex(
+ 0, file_idx, line, false, &sc.line_entry);
+ found_line = sc.line_entry.line;
+
+ while (line_idx != UINT32_MAX) {
+ sc.function = nullptr;
+ sc.block = nullptr;
+ if (resolve_scope &
+ (eSymbolContextFunction | eSymbolContextBlock)) {
+ const lldb::addr_t file_vm_addr =
+ sc.line_entry.range.GetBaseAddress().GetFileAddress();
+ if (file_vm_addr != LLDB_INVALID_ADDRESS) {
+ DWARFDIE function_die =
+ GetDWARFCompileUnit(dc_cu)->LookupAddress(file_vm_addr);
+ DWARFDIE block_die;
+ if (function_die) {
+ sc.function =
+ sc.comp_unit->FindFunctionByUID(function_die.GetID())
+ .get();
+ if (sc.function == nullptr)
+ sc.function =
+ ParseFunction(*sc.comp_unit, function_die);
+
+ if (sc.function && (resolve_scope & eSymbolContextBlock))
+ block_die =
+ function_die.LookupDeepestBlock(file_vm_addr);
+ }
+
+ if (sc.function != nullptr) {
+ Block &block = sc.function->GetBlock(true);
+
+ if (block_die)
+ sc.block = block.FindBlockByID(block_die.GetID());
+ else if (function_die)
+ sc.block = block.FindBlockByID(function_die.GetID());
+ }
+ }
+ }
+
+ sc_list.Append(sc);
+ line_idx = line_table->FindLineEntryIndexByFileIndex(
+ line_idx + 1, file_idx, found_line, true, &sc.line_entry);
+ }
+ }
+ } else if (file_spec_matches_cu_file_spec && !check_inlines) {
+ // only append the context if we aren't looking for inline call
+ // sites by file and line and if the file spec matches that of
+ // the compile unit
+ sc_list.Append(sc);
+ }
+ } else if (file_spec_matches_cu_file_spec && !check_inlines) {
+ // only append the context if we aren't looking for inline call
+ // sites by file and line and if the file spec matches that of
+ // the compile unit
+ sc_list.Append(sc);
+ }
+
+ if (!check_inlines)
+ break;
+ }
+ }
+ }
+ return sc_list.GetSize() - prev_size;
+}
+
+void SymbolFileDWARF::PreloadSymbols() {
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ m_index->Preload();
+}
+
+std::recursive_mutex &SymbolFileDWARF::GetModuleMutex() const {
+ lldb::ModuleSP module_sp(m_debug_map_module_wp.lock());
+ if (module_sp)
+ return module_sp->GetMutex();
+ return GetObjectFile()->GetModule()->GetMutex();
+}
+
+bool SymbolFileDWARF::DeclContextMatchesThisSymbolFile(
+ const lldb_private::CompilerDeclContext *decl_ctx) {
+ if (decl_ctx == nullptr || !decl_ctx->IsValid()) {
+ // Invalid namespace decl which means we aren't matching only things in
+ // this symbol file, so return true to indicate it matches this symbol
+ // file.
+ return true;
+ }
+
+ TypeSystem *decl_ctx_type_system = decl_ctx->GetTypeSystem();
+ TypeSystem *type_system = GetTypeSystemForLanguage(
+ decl_ctx_type_system->GetMinimumLanguage(nullptr));
+ if (decl_ctx_type_system == type_system)
+ return true; // The type systems match, return true
+
+ // The namespace AST was valid, and it does not match...
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log)
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "Valid namespace does not match symbol file");
+
+ return false;
+}
+
+uint32_t SymbolFileDWARF::FindGlobalVariables(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, VariableList &variables) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log)
+ GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", "
+ "parent_decl_ctx=%p, max_matches=%u, variables)",
+ name.GetCString(), static_cast<const void *>(parent_decl_ctx),
+ max_matches);
+
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (info == nullptr)
+ return 0;
+
+ // Remember how many variables are in the list before we search.
+ const uint32_t original_size = variables.GetSize();
+
+ llvm::StringRef basename;
+ llvm::StringRef context;
+ bool name_is_mangled = (bool)Mangled(name);
+
+ if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name.GetCString(),
+ context, basename))
+ basename = name.GetStringRef();
+
+ DIEArray die_offsets;
+ m_index->GetGlobalVariables(ConstString(basename), die_offsets);
+ const size_t num_die_matches = die_offsets.size();
+ if (num_die_matches) {
+ SymbolContext sc;
+ sc.module_sp = m_obj_file->GetModule();
+ assert(sc.module_sp);
+
+ // Loop invariant: Variables up to this index have been checked for context
+ // matches.
+ uint32_t pruned_idx = original_size;
+
+ bool done = false;
+ for (size_t i = 0; i < num_die_matches && !done; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+
+ if (die) {
+ switch (die.Tag()) {
+ default:
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_try_block:
+ case DW_TAG_catch_block:
+ break;
+
+ case DW_TAG_variable: {
+ auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(die.GetCU());
+ if (!dwarf_cu)
+ continue;
+ sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu);
+
+ if (parent_decl_ctx) {
+ DWARFASTParser *dwarf_ast = die.GetDWARFParser();
+ if (dwarf_ast) {
+ CompilerDeclContext actual_parent_decl_ctx =
+ dwarf_ast->GetDeclContextContainingUIDFromDWARF(die);
+ if (!actual_parent_decl_ctx ||
+ actual_parent_decl_ctx != *parent_decl_ctx)
+ continue;
+ }
+ }
+
+ ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false,
+ &variables);
+ while (pruned_idx < variables.GetSize()) {
+ VariableSP var_sp = variables.GetVariableAtIndex(pruned_idx);
+ if (name_is_mangled ||
+ var_sp->GetName().GetStringRef().contains(name.GetStringRef()))
+ ++pruned_idx;
+ else
+ variables.RemoveVariableAtIndex(pruned_idx);
+ }
+
+ if (variables.GetSize() - original_size >= max_matches)
+ done = true;
+ } break;
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
+ }
+ }
+ }
+
+ // Return the number of variable that were appended to the list
+ const uint32_t num_matches = variables.GetSize() - original_size;
+ if (log && num_matches > 0) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", "
+ "parent_decl_ctx=%p, max_matches=%u, variables) => %u",
+ name.GetCString(), static_cast<const void *>(parent_decl_ctx),
+ max_matches, num_matches);
+ }
+ return num_matches;
+}
+
+uint32_t SymbolFileDWARF::FindGlobalVariables(const RegularExpression &regex,
+ uint32_t max_matches,
+ VariableList &variables) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::FindGlobalVariables (regex=\"%s\", "
+ "max_matches=%u, variables)",
+ regex.GetText().str().c_str(), max_matches);
+ }
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (info == nullptr)
+ return 0;
+
+ // Remember how many variables are in the list before we search.
+ const uint32_t original_size = variables.GetSize();
+
+ DIEArray die_offsets;
+ m_index->GetGlobalVariables(regex, die_offsets);
+
+ SymbolContext sc;
+ sc.module_sp = m_obj_file->GetModule();
+ assert(sc.module_sp);
+
+ const size_t num_matches = die_offsets.size();
+ if (num_matches) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+
+ if (die) {
+ DWARFCompileUnit *dwarf_cu =
+ llvm::dyn_cast<DWARFCompileUnit>(die.GetCU());
+ if (!dwarf_cu)
+ continue;
+ sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu);
+
+ ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false, &variables);
+
+ if (variables.GetSize() - original_size >= max_matches)
+ break;
+ } else
+ m_index->ReportInvalidDIERef(die_ref, regex.GetText());
+ }
+ }
+
+ // Return the number of variable that were appended to the list
+ return variables.GetSize() - original_size;
+}
+
+bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die,
+ bool include_inlines,
+ SymbolContextList &sc_list) {
+ SymbolContext sc;
+
+ if (!orig_die)
+ return false;
+
+ // If we were passed a die that is not a function, just return false...
+ if (!(orig_die.Tag() == DW_TAG_subprogram ||
+ (include_inlines && orig_die.Tag() == DW_TAG_inlined_subroutine)))
+ return false;
+
+ DWARFDIE die = orig_die;
+ DWARFDIE inlined_die;
+ if (die.Tag() == DW_TAG_inlined_subroutine) {
+ inlined_die = die;
+
+ while (true) {
+ die = die.GetParent();
+
+ if (die) {
+ if (die.Tag() == DW_TAG_subprogram)
+ break;
+ } else
+ break;
+ }
+ }
+ assert(die && die.Tag() == DW_TAG_subprogram);
+ if (GetFunction(die, sc)) {
+ Address addr;
+ // Parse all blocks if needed
+ if (inlined_die) {
+ Block &function_block = sc.function->GetBlock(true);
+ sc.block = function_block.FindBlockByID(inlined_die.GetID());
+ if (sc.block == nullptr)
+ sc.block = function_block.FindBlockByID(inlined_die.GetOffset());
+ if (sc.block == nullptr || !sc.block->GetStartAddress(addr))
+ addr.Clear();
+ } else {
+ sc.block = nullptr;
+ addr = sc.function->GetAddressRange().GetBaseAddress();
+ }
+
+ if (addr.IsValid()) {
+ sc_list.Append(sc);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool SymbolFileDWARF::DIEInDeclContext(const CompilerDeclContext *decl_ctx,
+ const DWARFDIE &die) {
+ // If we have no parent decl context to match this DIE matches, and if the
+ // parent decl context isn't valid, we aren't trying to look for any
+ // particular decl context so any die matches.
+ if (decl_ctx == nullptr || !decl_ctx->IsValid())
+ return true;
+
+ if (die) {
+ DWARFASTParser *dwarf_ast = die.GetDWARFParser();
+ if (dwarf_ast) {
+ CompilerDeclContext actual_decl_ctx =
+ dwarf_ast->GetDeclContextContainingUIDFromDWARF(die);
+ if (actual_decl_ctx)
+ return decl_ctx->IsContainedInLookup(actual_decl_ctx);
+ }
+ }
+ return false;
+}
+
+uint32_t SymbolFileDWARF::FindFunctions(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask, bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "SymbolFileDWARF::FindFunctions (name = '%s')",
+ name.AsCString());
+
+ // eFunctionNameTypeAuto should be pre-resolved by a call to
+ // Module::LookupInfo::LookupInfo()
+ assert((name_type_mask & eFunctionNameTypeAuto) == 0);
+
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindFunctions (name=\"%s\", "
+ "name_type_mask=0x%x, append=%u, sc_list)",
+ name.GetCString(), name_type_mask, append);
+ }
+
+ // If we aren't appending the results to this list, then clear the list
+ if (!append)
+ sc_list.Clear();
+
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+
+ // If name is empty then we won't find anything.
+ if (name.IsEmpty())
+ return 0;
+
+ // Remember how many sc_list are in the list before we search in case we are
+ // appending the results to a variable list.
+
+ const uint32_t original_size = sc_list.GetSize();
+
+ llvm::DenseSet<const DWARFDebugInfoEntry *> resolved_dies;
+ DIEArray offsets;
+ CompilerDeclContext empty_decl_ctx;
+ if (!parent_decl_ctx)
+ parent_decl_ctx = &empty_decl_ctx;
+
+ std::vector<DWARFDIE> dies;
+ m_index->GetFunctions(name, *this, *parent_decl_ctx, name_type_mask, dies);
+ for (const DWARFDIE &die: dies) {
+ if (resolved_dies.insert(die.GetDIE()).second)
+ ResolveFunction(die, include_inlines, sc_list);
+ }
+
+ // Return the number of variable that were appended to the list
+ const uint32_t num_matches = sc_list.GetSize() - original_size;
+
+ if (log && num_matches > 0) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindFunctions (name=\"%s\", "
+ "name_type_mask=0x%x, include_inlines=%d, append=%u, sc_list) => "
+ "%u",
+ name.GetCString(), name_type_mask, include_inlines, append,
+ num_matches);
+ }
+ return num_matches;
+}
+
+uint32_t SymbolFileDWARF::FindFunctions(const RegularExpression &regex,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "SymbolFileDWARF::FindFunctions (regex = '%s')",
+ regex.GetText().str().c_str());
+
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::FindFunctions (regex=\"%s\", append=%u, sc_list)",
+ regex.GetText().str().c_str(), append);
+ }
+
+ // If we aren't appending the results to this list, then clear the list
+ if (!append)
+ sc_list.Clear();
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (!info)
+ return 0;
+
+ // Remember how many sc_list are in the list before we search in case we are
+ // appending the results to a variable list.
+ uint32_t original_size = sc_list.GetSize();
+
+ DIEArray offsets;
+ m_index->GetFunctions(regex, offsets);
+
+ llvm::DenseSet<const DWARFDebugInfoEntry *> resolved_dies;
+ for (DIERef ref : offsets) {
+ DWARFDIE die = info->GetDIE(ref);
+ if (!die) {
+ m_index->ReportInvalidDIERef(ref, regex.GetText());
+ continue;
+ }
+ if (resolved_dies.insert(die.GetDIE()).second)
+ ResolveFunction(die, include_inlines, sc_list);
+ }
+
+ // Return the number of variable that were appended to the list
+ return sc_list.GetSize() - original_size;
+}
+
+void SymbolFileDWARF::GetMangledNamesForFunction(
+ const std::string &scope_qualified_name,
+ std::vector<ConstString> &mangled_names) {
+ DWARFDebugInfo *info = DebugInfo();
+ uint32_t num_comp_units = 0;
+ if (info)
+ num_comp_units = info->GetNumUnits();
+
+ for (uint32_t i = 0; i < num_comp_units; i++) {
+ DWARFUnit *cu = info->GetUnitAtIndex(i);
+ if (cu == nullptr)
+ continue;
+
+ SymbolFileDWARFDwo *dwo = cu->GetDwoSymbolFile();
+ if (dwo)
+ dwo->GetMangledNamesForFunction(scope_qualified_name, mangled_names);
+ }
+
+ for (lldb::user_id_t uid :
+ m_function_scope_qualified_name_map.lookup(scope_qualified_name)) {
+ DWARFDIE die = GetDIE(uid);
+ mangled_names.push_back(ConstString(die.GetMangledName()));
+ }
+}
+
+uint32_t SymbolFileDWARF::FindTypes(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ TypeMap &types) {
+ // If we aren't appending the results to this list, then clear the list
+ if (!append)
+ types.Clear();
+
+ // Make sure we haven't already searched this SymbolFile before...
+ if (searched_symbol_files.count(this))
+ return 0;
+ else
+ searched_symbol_files.insert(this);
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (info == nullptr)
+ return 0;
+
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log) {
+ if (parent_decl_ctx)
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = "
+ "%p (\"%s\"), append=%u, max_matches=%u, type_list)",
+ name.GetCString(), static_cast<const void *>(parent_decl_ctx),
+ parent_decl_ctx->GetName().AsCString("<NULL>"), append, max_matches);
+ else
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = "
+ "NULL, append=%u, max_matches=%u, type_list)",
+ name.GetCString(), append, max_matches);
+ }
+
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+
+ DIEArray die_offsets;
+ m_index->GetTypes(name, die_offsets);
+ const size_t num_die_matches = die_offsets.size();
+
+ if (num_die_matches) {
+ const uint32_t initial_types_size = types.GetSize();
+ for (size_t i = 0; i < num_die_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+
+ if (die) {
+ if (!DIEInDeclContext(parent_decl_ctx, die))
+ continue; // The containing decl contexts don't match
+
+ Type *matching_type = ResolveType(die, true, true);
+ if (matching_type) {
+ // We found a type pointer, now find the shared pointer form our type
+ // list
+ types.InsertUnique(matching_type->shared_from_this());
+ if (types.GetSize() >= max_matches)
+ break;
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
+ }
+ }
+ const uint32_t num_matches = types.GetSize() - initial_types_size;
+ if (log && num_matches) {
+ if (parent_decl_ctx) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx "
+ "= %p (\"%s\"), append=%u, max_matches=%u, type_list) => %u",
+ name.GetCString(), static_cast<const void *>(parent_decl_ctx),
+ parent_decl_ctx->GetName().AsCString("<NULL>"), append, max_matches,
+ num_matches);
+ } else {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx "
+ "= NULL, append=%u, max_matches=%u, type_list) => %u",
+ name.GetCString(), append, max_matches, num_matches);
+ }
+ }
+ return num_matches;
+ } else {
+ UpdateExternalModuleListIfNeeded();
+
+ for (const auto &pair : m_external_type_modules) {
+ ModuleSP external_module_sp = pair.second;
+ if (external_module_sp) {
+ SymbolVendor *sym_vendor = external_module_sp->GetSymbolVendor();
+ if (sym_vendor) {
+ const uint32_t num_external_matches =
+ sym_vendor->FindTypes(name, parent_decl_ctx, append, max_matches,
+ searched_symbol_files, types);
+ if (num_external_matches)
+ return num_external_matches;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+size_t SymbolFileDWARF::FindTypes(const std::vector<CompilerContext> &context,
+ bool append, TypeMap &types) {
+ if (!append)
+ types.Clear();
+
+ if (context.empty())
+ return 0;
+
+ ConstString name = context.back().name;
+
+ if (!name)
+ return 0;
+
+ DIEArray die_offsets;
+ m_index->GetTypes(name, die_offsets);
+ const size_t num_die_matches = die_offsets.size();
+
+ if (num_die_matches) {
+ size_t num_matches = 0;
+ for (size_t i = 0; i < num_die_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+
+ if (die) {
+ std::vector<CompilerContext> die_context;
+ die.GetDeclContext(die_context);
+ if (die_context != context)
+ continue;
+
+ Type *matching_type = ResolveType(die, true, true);
+ if (matching_type) {
+ // We found a type pointer, now find the shared pointer form our type
+ // list
+ types.InsertUnique(matching_type->shared_from_this());
+ ++num_matches;
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
+ }
+ }
+ return num_matches;
+ }
+ return 0;
+}
+
+CompilerDeclContext
+SymbolFileDWARF::FindNamespace(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\")",
+ name.GetCString());
+ }
+
+ CompilerDeclContext namespace_decl_ctx;
+
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return namespace_decl_ctx;
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (info) {
+ DIEArray die_offsets;
+ m_index->GetNamespaces(name, die_offsets);
+ const size_t num_matches = die_offsets.size();
+ if (num_matches) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+
+ if (die) {
+ if (!DIEInDeclContext(parent_decl_ctx, die))
+ continue; // The containing decl contexts don't match
+
+ DWARFASTParser *dwarf_ast = die.GetDWARFParser();
+ if (dwarf_ast) {
+ namespace_decl_ctx = dwarf_ast->GetDeclContextForUIDFromDWARF(die);
+ if (namespace_decl_ctx)
+ break;
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
+ }
+ }
+ }
+ }
+ if (log && namespace_decl_ctx) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\") => "
+ "CompilerDeclContext(%p/%p) \"%s\"",
+ name.GetCString(),
+ static_cast<const void *>(namespace_decl_ctx.GetTypeSystem()),
+ static_cast<const void *>(namespace_decl_ctx.GetOpaqueDeclContext()),
+ namespace_decl_ctx.GetName().AsCString("<NULL>"));
+ }
+
+ return namespace_decl_ctx;
+}
+
+TypeSP SymbolFileDWARF::GetTypeForDIE(const DWARFDIE &die,
+ bool resolve_function_context) {
+ TypeSP type_sp;
+ if (die) {
+ Type *type_ptr = GetDIEToType().lookup(die.GetDIE());
+ if (type_ptr == nullptr) {
+ SymbolContextScope *scope;
+ if (auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(die.GetCU()))
+ scope = GetCompUnitForDWARFCompUnit(*dwarf_cu);
+ else
+ scope = GetObjectFile()->GetModule().get();
+ assert(scope);
+ SymbolContext sc(scope);
+ const DWARFDebugInfoEntry *parent_die = die.GetParent().GetDIE();
+ while (parent_die != nullptr) {
+ if (parent_die->Tag() == DW_TAG_subprogram)
+ break;
+ parent_die = parent_die->GetParent();
+ }
+ SymbolContext sc_backup = sc;
+ if (resolve_function_context && parent_die != nullptr &&
+ !GetFunction(DWARFDIE(die.GetCU(), parent_die), sc))
+ sc = sc_backup;
+
+ type_sp = ParseType(sc, die, nullptr);
+ } else if (type_ptr != DIE_IS_BEING_PARSED) {
+ // Grab the existing type from the master types lists
+ type_sp = type_ptr->shared_from_this();
+ }
+ }
+ return type_sp;
+}
+
+DWARFDIE
+SymbolFileDWARF::GetDeclContextDIEContainingDIE(const DWARFDIE &orig_die) {
+ if (orig_die) {
+ DWARFDIE die = orig_die;
+
+ while (die) {
+ // If this is the original DIE that we are searching for a declaration
+ // for, then don't look in the cache as we don't want our own decl
+ // context to be our decl context...
+ if (orig_die != die) {
+ switch (die.Tag()) {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ case DW_TAG_namespace:
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type:
+ case DW_TAG_lexical_block:
+ case DW_TAG_subprogram:
+ return die;
+ case DW_TAG_inlined_subroutine: {
+ DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin);
+ if (abs_die) {
+ return abs_die;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification);
+ if (spec_die) {
+ DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(spec_die);
+ if (decl_ctx_die)
+ return decl_ctx_die;
+ }
+
+ DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin);
+ if (abs_die) {
+ DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(abs_die);
+ if (decl_ctx_die)
+ return decl_ctx_die;
+ }
+
+ die = die.GetParent();
+ }
+ }
+ return DWARFDIE();
+}
+
+Symbol *
+SymbolFileDWARF::GetObjCClassSymbol(ConstString objc_class_name) {
+ Symbol *objc_class_symbol = nullptr;
+ if (m_obj_file) {
+ Symtab *symtab = m_obj_file->GetSymtab();
+ if (symtab) {
+ objc_class_symbol = symtab->FindFirstSymbolWithNameAndType(
+ objc_class_name, eSymbolTypeObjCClass, Symtab::eDebugNo,
+ Symtab::eVisibilityAny);
+ }
+ }
+ return objc_class_symbol;
+}
+
+// Some compilers don't emit the DW_AT_APPLE_objc_complete_type attribute. If
+// they don't then we can end up looking through all class types for a complete
+// type and never find the full definition. We need to know if this attribute
+// is supported, so we determine this here and cache th result. We also need to
+// worry about the debug map
+// DWARF file
+// if we are doing darwin DWARF in .o file debugging.
+bool SymbolFileDWARF::Supports_DW_AT_APPLE_objc_complete_type(
+ DWARFUnit *cu) {
+ if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) {
+ m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo;
+ if (cu && cu->Supports_DW_AT_APPLE_objc_complete_type())
+ m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes;
+ else {
+ DWARFDebugInfo *debug_info = DebugInfo();
+ const uint32_t num_compile_units = GetNumCompileUnits();
+ for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) {
+ DWARFUnit *dwarf_cu = debug_info->GetUnitAtIndex(cu_idx);
+ if (dwarf_cu != cu &&
+ dwarf_cu->Supports_DW_AT_APPLE_objc_complete_type()) {
+ m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes;
+ break;
+ }
+ }
+ }
+ if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolNo &&
+ GetDebugMapSymfile())
+ return m_debug_map_symfile->Supports_DW_AT_APPLE_objc_complete_type(this);
+ }
+ return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes;
+}
+
+// This function can be used when a DIE is found that is a forward declaration
+// DIE and we want to try and find a type that has the complete definition.
+TypeSP SymbolFileDWARF::FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, ConstString type_name,
+ bool must_be_implementation) {
+
+ TypeSP type_sp;
+
+ if (!type_name || (must_be_implementation && !GetObjCClassSymbol(type_name)))
+ return type_sp;
+
+ DIEArray die_offsets;
+ m_index->GetCompleteObjCClass(type_name, must_be_implementation, die_offsets);
+
+ const size_t num_matches = die_offsets.size();
+
+ if (num_matches) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE type_die = GetDIE(die_ref);
+
+ if (type_die) {
+ bool try_resolving_type = false;
+
+ // Don't try and resolve the DIE we are looking for with the DIE
+ // itself!
+ if (type_die != die) {
+ switch (type_die.Tag()) {
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ try_resolving_type = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (try_resolving_type) {
+ if (must_be_implementation &&
+ type_die.Supports_DW_AT_APPLE_objc_complete_type())
+ try_resolving_type = type_die.GetAttributeValueAsUnsigned(
+ DW_AT_APPLE_objc_complete_type, 0);
+
+ if (try_resolving_type) {
+ Type *resolved_type = ResolveType(type_die, false, true);
+ if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) {
+ DEBUG_PRINTF("resolved 0x%8.8" PRIx64 " from %s to 0x%8.8" PRIx64
+ " (cu 0x%8.8" PRIx64 ")\n",
+ die.GetID(),
+ m_obj_file->GetFileSpec().GetFilename().AsCString(
+ "<Unknown>"),
+ type_die.GetID(), type_cu->GetID());
+
+ if (die)
+ GetDIEToType()[die.GetDIE()] = resolved_type;
+ type_sp = resolved_type->shared_from_this();
+ break;
+ }
+ }
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, type_name.GetStringRef());
+ }
+ }
+ }
+ return type_sp;
+}
+
+// This function helps to ensure that the declaration contexts match for two
+// different DIEs. Often times debug information will refer to a forward
+// declaration of a type (the equivalent of "struct my_struct;". There will
+// often be a declaration of that type elsewhere that has the full definition.
+// When we go looking for the full type "my_struct", we will find one or more
+// matches in the accelerator tables and we will then need to make sure the
+// type was in the same declaration context as the original DIE. This function
+// can efficiently compare two DIEs and will return true when the declaration
+// context matches, and false when they don't.
+bool SymbolFileDWARF::DIEDeclContextsMatch(const DWARFDIE &die1,
+ const DWARFDIE &die2) {
+ if (die1 == die2)
+ return true;
+
+ std::vector<DWARFDIE> decl_ctx_1;
+ std::vector<DWARFDIE> decl_ctx_2;
+ // The declaration DIE stack is a stack of the declaration context DIEs all
+ // the way back to the compile unit. If a type "T" is declared inside a class
+ // "B", and class "B" is declared inside a class "A" and class "A" is in a
+ // namespace "lldb", and the namespace is in a compile unit, there will be a
+ // stack of DIEs:
+ //
+ // [0] DW_TAG_class_type for "B"
+ // [1] DW_TAG_class_type for "A"
+ // [2] DW_TAG_namespace for "lldb"
+ // [3] DW_TAG_compile_unit or DW_TAG_partial_unit for the source file.
+ //
+ // We grab both contexts and make sure that everything matches all the way
+ // back to the compiler unit.
+
+ // First lets grab the decl contexts for both DIEs
+ decl_ctx_1 = die1.GetDeclContextDIEs();
+ decl_ctx_2 = die2.GetDeclContextDIEs();
+ // Make sure the context arrays have the same size, otherwise we are done
+ const size_t count1 = decl_ctx_1.size();
+ const size_t count2 = decl_ctx_2.size();
+ if (count1 != count2)
+ return false;
+
+ // Make sure the DW_TAG values match all the way back up the compile unit. If
+ // they don't, then we are done.
+ DWARFDIE decl_ctx_die1;
+ DWARFDIE decl_ctx_die2;
+ size_t i;
+ for (i = 0; i < count1; i++) {
+ decl_ctx_die1 = decl_ctx_1[i];
+ decl_ctx_die2 = decl_ctx_2[i];
+ if (decl_ctx_die1.Tag() != decl_ctx_die2.Tag())
+ return false;
+ }
+#ifndef NDEBUG
+
+ // Make sure the top item in the decl context die array is always
+ // DW_TAG_compile_unit or DW_TAG_partial_unit. If it isn't then
+ // something went wrong in the DWARFDIE::GetDeclContextDIEs()
+ // function.
+ dw_tag_t cu_tag = decl_ctx_1[count1 - 1].Tag();
+ UNUSED_IF_ASSERT_DISABLED(cu_tag);
+ assert(cu_tag == DW_TAG_compile_unit || cu_tag == DW_TAG_partial_unit);
+
+#endif
+ // Always skip the compile unit when comparing by only iterating up to "count
+ // - 1". Here we compare the names as we go.
+ for (i = 0; i < count1 - 1; i++) {
+ decl_ctx_die1 = decl_ctx_1[i];
+ decl_ctx_die2 = decl_ctx_2[i];
+ const char *name1 = decl_ctx_die1.GetName();
+ const char *name2 = decl_ctx_die2.GetName();
+ // If the string was from a DW_FORM_strp, then the pointer will often be
+ // the same!
+ if (name1 == name2)
+ continue;
+
+ // Name pointers are not equal, so only compare the strings if both are not
+ // NULL.
+ if (name1 && name2) {
+ // If the strings don't compare, we are done...
+ if (strcmp(name1, name2) != 0)
+ return false;
+ } else {
+ // One name was NULL while the other wasn't
+ return false;
+ }
+ }
+ // We made it through all of the checks and the declaration contexts are
+ // equal.
+ return true;
+}
+
+TypeSP SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(
+ const DWARFDeclContext &dwarf_decl_ctx) {
+ TypeSP type_sp;
+
+ const uint32_t dwarf_decl_ctx_count = dwarf_decl_ctx.GetSize();
+ if (dwarf_decl_ctx_count > 0) {
+ const ConstString type_name(dwarf_decl_ctx[0].name);
+ const dw_tag_t tag = dwarf_decl_ctx[0].tag;
+
+ if (type_name) {
+ Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION |
+ DWARF_LOG_LOOKUPS));
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(tag=%"
+ "s, qualified-name='%s')",
+ DW_TAG_value_to_name(dwarf_decl_ctx[0].tag),
+ dwarf_decl_ctx.GetQualifiedName());
+ }
+
+ DIEArray die_offsets;
+ m_index->GetTypes(dwarf_decl_ctx, die_offsets);
+ const size_t num_matches = die_offsets.size();
+
+ // Get the type system that we are looking to find a type for. We will
+ // use this to ensure any matches we find are in a language that this
+ // type system supports
+ const LanguageType language = dwarf_decl_ctx.GetLanguage();
+ TypeSystem *type_system = (language == eLanguageTypeUnknown)
+ ? nullptr
+ : GetTypeSystemForLanguage(language);
+
+ if (num_matches) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE type_die = GetDIE(die_ref);
+
+ if (type_die) {
+ // Make sure type_die's langauge matches the type system we are
+ // looking for. We don't want to find a "Foo" type from Java if we
+ // are looking for a "Foo" type for C, C++, ObjC, or ObjC++.
+ if (type_system &&
+ !type_system->SupportsLanguage(type_die.GetLanguage()))
+ continue;
+ bool try_resolving_type = false;
+
+ // Don't try and resolve the DIE we are looking for with the DIE
+ // itself!
+ const dw_tag_t type_tag = type_die.Tag();
+ // Make sure the tags match
+ if (type_tag == tag) {
+ // The tags match, lets try resolving this type
+ try_resolving_type = true;
+ } else {
+ // The tags don't match, but we need to watch our for a forward
+ // declaration for a struct and ("struct foo") ends up being a
+ // class ("class foo { ... };") or vice versa.
+ switch (type_tag) {
+ case DW_TAG_class_type:
+ // We had a "class foo", see if we ended up with a "struct foo
+ // { ... };"
+ try_resolving_type = (tag == DW_TAG_structure_type);
+ break;
+ case DW_TAG_structure_type:
+ // We had a "struct foo", see if we ended up with a "class foo
+ // { ... };"
+ try_resolving_type = (tag == DW_TAG_class_type);
+ break;
+ default:
+ // Tags don't match, don't event try to resolve using this type
+ // whose name matches....
+ break;
+ }
+ }
+
+ if (try_resolving_type) {
+ DWARFDeclContext type_dwarf_decl_ctx;
+ type_die.GetDWARFDeclContext(type_dwarf_decl_ctx);
+
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::"
+ "FindDefinitionTypeForDWARFDeclContext(tag=%s, "
+ "qualified-name='%s') trying die=0x%8.8x (%s)",
+ DW_TAG_value_to_name(dwarf_decl_ctx[0].tag),
+ dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(),
+ type_dwarf_decl_ctx.GetQualifiedName());
+ }
+
+ // Make sure the decl contexts match all the way up
+ if (dwarf_decl_ctx == type_dwarf_decl_ctx) {
+ Type *resolved_type = ResolveType(type_die, false);
+ if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) {
+ type_sp = resolved_type->shared_from_this();
+ break;
+ }
+ }
+ } else {
+ if (log) {
+ std::string qualified_name;
+ type_die.GetQualifiedName(qualified_name);
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::"
+ "FindDefinitionTypeForDWARFDeclContext(tag=%s, "
+ "qualified-name='%s') ignoring die=0x%8.8x (%s)",
+ DW_TAG_value_to_name(dwarf_decl_ctx[0].tag),
+ dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(),
+ qualified_name.c_str());
+ }
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, type_name.GetStringRef());
+ }
+ }
+ }
+ }
+ }
+ return type_sp;
+}
+
+TypeSP SymbolFileDWARF::ParseType(const SymbolContext &sc, const DWARFDIE &die,
+ bool *type_is_new_ptr) {
+ if (!die)
+ return {};
+
+ TypeSystem *type_system =
+ GetTypeSystemForLanguage(die.GetCU()->GetLanguageType());
+ if (!type_system)
+ return {};
+
+ DWARFASTParser *dwarf_ast = type_system->GetDWARFParser();
+ if (!dwarf_ast)
+ return {};
+
+ Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
+ TypeSP type_sp = dwarf_ast->ParseTypeFromDWARF(sc, die, log, type_is_new_ptr);
+ if (type_sp) {
+ TypeList *type_list = GetTypeList();
+ if (type_list)
+ type_list->Insert(type_sp);
+
+ if (die.Tag() == DW_TAG_subprogram) {
+ std::string scope_qualified_name(GetDeclContextForUID(die.GetID())
+ .GetScopeQualifiedName()
+ .AsCString(""));
+ if (scope_qualified_name.size()) {
+ m_function_scope_qualified_name_map[scope_qualified_name].insert(
+ die.GetID());
+ }
+ }
+ }
+
+ return type_sp;
+}
+
+size_t SymbolFileDWARF::ParseTypes(const SymbolContext &sc,
+ const DWARFDIE &orig_die,
+ bool parse_siblings, bool parse_children) {
+ size_t types_added = 0;
+ DWARFDIE die = orig_die;
+ while (die) {
+ bool type_is_new = false;
+ if (ParseType(sc, die, &type_is_new).get()) {
+ if (type_is_new)
+ ++types_added;
+ }
+
+ if (parse_children && die.HasChildren()) {
+ if (die.Tag() == DW_TAG_subprogram) {
+ SymbolContext child_sc(sc);
+ child_sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get();
+ types_added += ParseTypes(child_sc, die.GetFirstChild(), true, true);
+ } else
+ types_added += ParseTypes(sc, die.GetFirstChild(), true, true);
+ }
+
+ if (parse_siblings)
+ die = die.GetSibling();
+ else
+ die.Clear();
+ }
+ return types_added;
+}
+
+size_t SymbolFileDWARF::ParseBlocksRecursive(Function &func) {
+ ASSERT_MODULE_LOCK(this);
+ CompileUnit *comp_unit = func.GetCompileUnit();
+ lldbassert(comp_unit);
+
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(comp_unit);
+ if (!dwarf_cu)
+ return 0;
+
+ size_t functions_added = 0;
+ const dw_offset_t function_die_offset = func.GetID();
+ DWARFDIE function_die = dwarf_cu->GetDIE(function_die_offset);
+ if (function_die) {
+ ParseBlocksRecursive(*comp_unit, &func.GetBlock(false), function_die,
+ LLDB_INVALID_ADDRESS, 0);
+ }
+
+ return functions_added;
+}
+
+size_t SymbolFileDWARF::ParseTypes(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+ size_t types_added = 0;
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (dwarf_cu) {
+ DWARFDIE dwarf_cu_die = dwarf_cu->DIE();
+ if (dwarf_cu_die && dwarf_cu_die.HasChildren()) {
+ SymbolContext sc;
+ sc.comp_unit = &comp_unit;
+ types_added = ParseTypes(sc, dwarf_cu_die.GetFirstChild(), true, true);
+ }
+ }
+
+ return types_added;
+}
+
+size_t SymbolFileDWARF::ParseVariablesForContext(const SymbolContext &sc) {
+ ASSERT_MODULE_LOCK(this);
+ if (sc.comp_unit != nullptr) {
+ DWARFDebugInfo *info = DebugInfo();
+ if (info == nullptr)
+ return 0;
+
+ if (sc.function) {
+ DWARFDIE function_die = GetDIE(sc.function->GetID());
+
+ const dw_addr_t func_lo_pc = function_die.GetAttributeValueAsAddress(
+ DW_AT_low_pc, LLDB_INVALID_ADDRESS);
+ if (func_lo_pc != LLDB_INVALID_ADDRESS) {
+ const size_t num_variables = ParseVariables(
+ sc, function_die.GetFirstChild(), func_lo_pc, true, true);
+
+ // Let all blocks know they have parse all their variables
+ sc.function->GetBlock(false).SetDidParseVariables(true, true);
+ return num_variables;
+ }
+ } else if (sc.comp_unit) {
+ DWARFUnit *dwarf_cu = info->GetUnitAtIndex(sc.comp_unit->GetID());
+
+ if (dwarf_cu == nullptr)
+ return 0;
+
+ uint32_t vars_added = 0;
+ VariableListSP variables(sc.comp_unit->GetVariableList(false));
+
+ if (variables.get() == nullptr) {
+ variables = std::make_shared<VariableList>();
+ sc.comp_unit->SetVariableList(variables);
+
+ DIEArray die_offsets;
+ m_index->GetGlobalVariables(dwarf_cu->GetNonSkeletonUnit(),
+ die_offsets);
+ const size_t num_matches = die_offsets.size();
+ if (num_matches) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+ if (die) {
+ VariableSP var_sp(
+ ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS));
+ if (var_sp) {
+ variables->AddVariableIfUnique(var_sp);
+ ++vars_added;
+ }
+ } else
+ m_index->ReportInvalidDIERef(die_ref, "");
+ }
+ }
+ }
+ return vars_added;
+ }
+ }
+ return 0;
+}
+
+VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
+ const DWARFDIE &die,
+ const lldb::addr_t func_low_pc) {
+ if (die.GetDWARF() != this)
+ return die.GetDWARF()->ParseVariableDIE(sc, die, func_low_pc);
+
+ VariableSP var_sp;
+ if (!die)
+ return var_sp;
+
+ var_sp = GetDIEToVariable()[die.GetDIE()];
+ if (var_sp)
+ return var_sp; // Already been parsed!
+
+ const dw_tag_t tag = die.Tag();
+ ModuleSP module = GetObjectFile()->GetModule();
+
+ if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) ||
+ (tag == DW_TAG_formal_parameter && sc.function)) {
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ DWARFDIE spec_die;
+ if (num_attributes > 0) {
+ const char *name = nullptr;
+ const char *mangled = nullptr;
+ Declaration decl;
+ uint32_t i;
+ DWARFFormValue type_die_form;
+ DWARFExpression location;
+ bool is_external = false;
+ bool is_artificial = false;
+ bool location_is_const_value_data = false;
+ bool has_explicit_location = false;
+ DWARFFormValue const_value;
+ Variable::RangeList scope_ranges;
+ // AccessType accessibility = eAccessNone;
+
+ for (i = 0; i < num_attributes; ++i) {
+ dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_decl_file:
+ decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(
+ form_value.Unsigned()));
+ break;
+ case DW_AT_decl_line:
+ decl.SetLine(form_value.Unsigned());
+ break;
+ case DW_AT_decl_column:
+ decl.SetColumn(form_value.Unsigned());
+ break;
+ case DW_AT_name:
+ name = form_value.AsCString();
+ break;
+ case DW_AT_linkage_name:
+ case DW_AT_MIPS_linkage_name:
+ mangled = form_value.AsCString();
+ break;
+ case DW_AT_type:
+ type_die_form = form_value;
+ break;
+ case DW_AT_external:
+ is_external = form_value.Boolean();
+ break;
+ case DW_AT_const_value:
+ // If we have already found a DW_AT_location attribute, ignore this
+ // attribute.
+ if (!has_explicit_location) {
+ location_is_const_value_data = true;
+ // The constant value will be either a block, a data value or a
+ // string.
+ auto debug_info_data = die.GetData();
+ if (DWARFFormValue::IsBlockForm(form_value.Form())) {
+ // Retrieve the value as a block expression.
+ uint32_t block_offset =
+ form_value.BlockData() - debug_info_data.GetDataStart();
+ uint32_t block_length = form_value.Unsigned();
+ location = DWARFExpression(module, debug_info_data, die.GetCU(),
+ block_offset, block_length);
+ } else if (DWARFFormValue::IsDataForm(form_value.Form())) {
+ // Retrieve the value as a data expression.
+ uint32_t data_offset = attributes.DIEOffsetAtIndex(i);
+ if (auto data_length = form_value.GetFixedSize())
+ location =
+ DWARFExpression(module, debug_info_data, die.GetCU(),
+ data_offset, *data_length);
+ else {
+ const uint8_t *data_pointer = form_value.BlockData();
+ if (data_pointer) {
+ form_value.Unsigned();
+ } else if (DWARFFormValue::IsDataForm(form_value.Form())) {
+ // we need to get the byte size of the type later after we
+ // create the variable
+ const_value = form_value;
+ }
+ }
+ } else {
+ // Retrieve the value as a string expression.
+ if (form_value.Form() == DW_FORM_strp) {
+ uint32_t data_offset = attributes.DIEOffsetAtIndex(i);
+ if (auto data_length = form_value.GetFixedSize())
+ location =
+ DWARFExpression(module, debug_info_data, die.GetCU(),
+ data_offset, *data_length);
+ } else {
+ const char *str = form_value.AsCString();
+ uint32_t string_offset =
+ str - (const char *)debug_info_data.GetDataStart();
+ uint32_t string_length = strlen(str) + 1;
+ location =
+ DWARFExpression(module, debug_info_data, die.GetCU(),
+ string_offset, string_length);
+ }
+ }
+ }
+ break;
+ case DW_AT_location: {
+ location_is_const_value_data = false;
+ has_explicit_location = true;
+ if (DWARFFormValue::IsBlockForm(form_value.Form())) {
+ auto data = die.GetData();
+
+ uint32_t block_offset =
+ form_value.BlockData() - data.GetDataStart();
+ uint32_t block_length = form_value.Unsigned();
+ location = DWARFExpression(module, data, die.GetCU(),
+ block_offset, block_length);
+ } else {
+ const DWARFDataExtractor &debug_loc_data = DebugLocData();
+ const dw_offset_t debug_loc_offset = form_value.Unsigned();
+
+ size_t loc_list_length = DWARFExpression::LocationListSize(
+ die.GetCU(), debug_loc_data, debug_loc_offset);
+ if (loc_list_length > 0) {
+ location = DWARFExpression(module, debug_loc_data, die.GetCU(),
+ debug_loc_offset, loc_list_length);
+ assert(func_low_pc != LLDB_INVALID_ADDRESS);
+ location.SetLocationListSlide(
+ func_low_pc -
+ attributes.CompileUnitAtIndex(i)->GetBaseAddress());
+ }
+ }
+ } break;
+ case DW_AT_specification:
+ spec_die = form_value.Reference();
+ break;
+ case DW_AT_start_scope:
+ // TODO: Implement this.
+ break;
+ case DW_AT_artificial:
+ is_artificial = form_value.Boolean();
+ break;
+ case DW_AT_accessibility:
+ break; // accessibility =
+ // DW_ACCESS_to_AccessType(form_value.Unsigned()); break;
+ case DW_AT_declaration:
+ case DW_AT_description:
+ case DW_AT_endianity:
+ case DW_AT_segment:
+ case DW_AT_visibility:
+ default:
+ case DW_AT_abstract_origin:
+ case DW_AT_sibling:
+ break;
+ }
+ }
+ }
+
+ const DWARFDIE parent_context_die = GetDeclContextDIEContainingDIE(die);
+ const dw_tag_t parent_tag = die.GetParent().Tag();
+ bool is_static_member =
+ (parent_tag == DW_TAG_compile_unit ||
+ parent_tag == DW_TAG_partial_unit) &&
+ (parent_context_die.Tag() == DW_TAG_class_type ||
+ parent_context_die.Tag() == DW_TAG_structure_type);
+
+ ValueType scope = eValueTypeInvalid;
+
+ const DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die);
+ SymbolContextScope *symbol_context_scope = nullptr;
+
+ bool has_explicit_mangled = mangled != nullptr;
+ if (!mangled) {
+ // LLDB relies on the mangled name (DW_TAG_linkage_name or
+ // DW_AT_MIPS_linkage_name) to generate fully qualified names
+ // of global variables with commands like "frame var j". For
+ // example, if j were an int variable holding a value 4 and
+ // declared in a namespace B which in turn is contained in a
+ // namespace A, the command "frame var j" returns
+ // "(int) A::B::j = 4".
+ // If the compiler does not emit a linkage name, we should be
+ // able to generate a fully qualified name from the
+ // declaration context.
+ if ((parent_tag == DW_TAG_compile_unit ||
+ parent_tag == DW_TAG_partial_unit) &&
+ Language::LanguageIsCPlusPlus(die.GetLanguage())) {
+ DWARFDeclContext decl_ctx;
+
+ die.GetDWARFDeclContext(decl_ctx);
+ mangled = decl_ctx.GetQualifiedNameAsConstString().GetCString();
+ }
+ }
+
+ if (tag == DW_TAG_formal_parameter)
+ scope = eValueTypeVariableArgument;
+ else {
+ // DWARF doesn't specify if a DW_TAG_variable is a local, global
+ // or static variable, so we have to do a little digging:
+ // 1) DW_AT_linkage_name implies static lifetime (but may be missing)
+ // 2) An empty DW_AT_location is an (optimized-out) static lifetime var.
+ // 3) DW_AT_location containing a DW_OP_addr implies static lifetime.
+ // Clang likes to combine small global variables into the same symbol
+ // with locations like: DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus
+ // so we need to look through the whole expression.
+ bool is_static_lifetime =
+ has_explicit_mangled ||
+ (has_explicit_location && !location.IsValid());
+ // Check if the location has a DW_OP_addr with any address value...
+ lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS;
+ if (!location_is_const_value_data) {
+ bool op_error = false;
+ location_DW_OP_addr = location.GetLocation_DW_OP_addr(0, op_error);
+ if (op_error) {
+ StreamString strm;
+ location.DumpLocationForAddress(&strm, eDescriptionLevelFull, 0, 0,
+ nullptr);
+ GetObjectFile()->GetModule()->ReportError(
+ "0x%8.8x: %s has an invalid location: %s", die.GetOffset(),
+ die.GetTagAsCString(), strm.GetData());
+ }
+ if (location_DW_OP_addr != LLDB_INVALID_ADDRESS)
+ is_static_lifetime = true;
+ }
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ if (debug_map_symfile)
+ // Set the module of the expression to the linked module
+ // instead of the oject file so the relocated address can be
+ // found there.
+ location.SetModule(debug_map_symfile->GetObjectFile()->GetModule());
+
+ if (is_static_lifetime) {
+ if (is_external)
+ scope = eValueTypeVariableGlobal;
+ else
+ scope = eValueTypeVariableStatic;
+
+ if (debug_map_symfile) {
+ // When leaving the DWARF in the .o files on darwin, when we have a
+ // global variable that wasn't initialized, the .o file might not
+ // have allocated a virtual address for the global variable. In
+ // this case it will have created a symbol for the global variable
+ // that is undefined/data and external and the value will be the
+ // byte size of the variable. When we do the address map in
+ // SymbolFileDWARFDebugMap we rely on having an address, we need to
+ // do some magic here so we can get the correct address for our
+ // global variable. The address for all of these entries will be
+ // zero, and there will be an undefined symbol in this object file,
+ // and the executable will have a matching symbol with a good
+ // address. So here we dig up the correct address and replace it in
+ // the location for the variable, and set the variable's symbol
+ // context scope to be that of the main executable so the file
+ // address will resolve correctly.
+ bool linked_oso_file_addr = false;
+ if (is_external && location_DW_OP_addr == 0) {
+ // we have a possible uninitialized extern global
+ ConstString const_name(mangled ? mangled : name);
+ ObjectFile *debug_map_objfile =
+ debug_map_symfile->GetObjectFile();
+ if (debug_map_objfile) {
+ Symtab *debug_map_symtab = debug_map_objfile->GetSymtab();
+ if (debug_map_symtab) {
+ Symbol *exe_symbol =
+ debug_map_symtab->FindFirstSymbolWithNameAndType(
+ const_name, eSymbolTypeData, Symtab::eDebugYes,
+ Symtab::eVisibilityExtern);
+ if (exe_symbol) {
+ if (exe_symbol->ValueIsAddress()) {
+ const addr_t exe_file_addr =
+ exe_symbol->GetAddressRef().GetFileAddress();
+ if (exe_file_addr != LLDB_INVALID_ADDRESS) {
+ if (location.Update_DW_OP_addr(exe_file_addr)) {
+ linked_oso_file_addr = true;
+ symbol_context_scope = exe_symbol;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!linked_oso_file_addr) {
+ // The DW_OP_addr is not zero, but it contains a .o file address
+ // which needs to be linked up correctly.
+ const lldb::addr_t exe_file_addr =
+ debug_map_symfile->LinkOSOFileAddress(this,
+ location_DW_OP_addr);
+ if (exe_file_addr != LLDB_INVALID_ADDRESS) {
+ // Update the file address for this variable
+ location.Update_DW_OP_addr(exe_file_addr);
+ } else {
+ // Variable didn't make it into the final executable
+ return var_sp;
+ }
+ }
+ }
+ } else {
+ if (location_is_const_value_data)
+ scope = eValueTypeVariableStatic;
+ else {
+ scope = eValueTypeVariableLocal;
+ if (debug_map_symfile) {
+ // We need to check for TLS addresses that we need to fixup
+ if (location.ContainsThreadLocalStorage()) {
+ location.LinkThreadLocalStorage(
+ debug_map_symfile->GetObjectFile()->GetModule(),
+ [this, debug_map_symfile](
+ lldb::addr_t unlinked_file_addr) -> lldb::addr_t {
+ return debug_map_symfile->LinkOSOFileAddress(
+ this, unlinked_file_addr);
+ });
+ scope = eValueTypeVariableThreadLocal;
+ }
+ }
+ }
+ }
+ }
+
+ if (symbol_context_scope == nullptr) {
+ switch (parent_tag) {
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_lexical_block:
+ if (sc.function) {
+ symbol_context_scope = sc.function->GetBlock(true).FindBlockByID(
+ sc_parent_die.GetID());
+ if (symbol_context_scope == nullptr)
+ symbol_context_scope = sc.function;
+ }
+ break;
+
+ default:
+ symbol_context_scope = sc.comp_unit;
+ break;
+ }
+ }
+
+ if (symbol_context_scope) {
+ SymbolFileTypeSP type_sp(
+ new SymbolFileType(*this, GetUID(type_die_form.Reference())));
+
+ if (const_value.Form() && type_sp && type_sp->GetType())
+ location.UpdateValue(const_value.Unsigned(),
+ type_sp->GetType()->GetByteSize().getValueOr(0),
+ die.GetCU()->GetAddressByteSize());
+
+ var_sp = std::make_shared<Variable>(
+ die.GetID(), name, mangled, type_sp, scope, symbol_context_scope,
+ scope_ranges, &decl, location, is_external, is_artificial,
+ is_static_member);
+
+ var_sp->SetLocationIsConstantValueData(location_is_const_value_data);
+ } else {
+ // Not ready to parse this variable yet. It might be a global or static
+ // variable that is in a function scope and the function in the symbol
+ // context wasn't filled in yet
+ return var_sp;
+ }
+ }
+ // Cache var_sp even if NULL (the variable was just a specification or was
+ // missing vital information to be able to be displayed in the debugger
+ // (missing location due to optimization, etc)) so we don't re-parse this
+ // DIE over and over later...
+ GetDIEToVariable()[die.GetDIE()] = var_sp;
+ if (spec_die)
+ GetDIEToVariable()[spec_die.GetDIE()] = var_sp;
+ }
+ return var_sp;
+}
+
+DWARFDIE
+SymbolFileDWARF::FindBlockContainingSpecification(
+ const DIERef &func_die_ref, dw_offset_t spec_block_die_offset) {
+ // Give the concrete function die specified by "func_die_offset", find the
+ // concrete block whose DW_AT_specification or DW_AT_abstract_origin points
+ // to "spec_block_die_offset"
+ return FindBlockContainingSpecification(DebugInfo()->GetDIE(func_die_ref),
+ spec_block_die_offset);
+}
+
+DWARFDIE
+SymbolFileDWARF::FindBlockContainingSpecification(
+ const DWARFDIE &die, dw_offset_t spec_block_die_offset) {
+ if (die) {
+ switch (die.Tag()) {
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_lexical_block: {
+ if (die.GetReferencedDIE(DW_AT_specification).GetOffset() ==
+ spec_block_die_offset)
+ return die;
+
+ if (die.GetReferencedDIE(DW_AT_abstract_origin).GetOffset() ==
+ spec_block_die_offset)
+ return die;
+ } break;
+ }
+
+ // Give the concrete function die specified by "func_die_offset", find the
+ // concrete block whose DW_AT_specification or DW_AT_abstract_origin points
+ // to "spec_block_die_offset"
+ for (DWARFDIE child_die = die.GetFirstChild(); child_die;
+ child_die = child_die.GetSibling()) {
+ DWARFDIE result_die =
+ FindBlockContainingSpecification(child_die, spec_block_die_offset);
+ if (result_die)
+ return result_die;
+ }
+ }
+
+ return DWARFDIE();
+}
+
+size_t SymbolFileDWARF::ParseVariables(const SymbolContext &sc,
+ const DWARFDIE &orig_die,
+ const lldb::addr_t func_low_pc,
+ bool parse_siblings, bool parse_children,
+ VariableList *cc_variable_list) {
+ if (!orig_die)
+ return 0;
+
+ VariableListSP variable_list_sp;
+
+ size_t vars_added = 0;
+ DWARFDIE die = orig_die;
+ while (die) {
+ dw_tag_t tag = die.Tag();
+
+ // Check to see if we have already parsed this variable or constant?
+ VariableSP var_sp = GetDIEToVariable()[die.GetDIE()];
+ if (var_sp) {
+ if (cc_variable_list)
+ cc_variable_list->AddVariableIfUnique(var_sp);
+ } else {
+ // We haven't already parsed it, lets do that now.
+ if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) ||
+ (tag == DW_TAG_formal_parameter && sc.function)) {
+ if (variable_list_sp.get() == nullptr) {
+ DWARFDIE sc_parent_die = GetParentSymbolContextDIE(orig_die);
+ dw_tag_t parent_tag = sc_parent_die.Tag();
+ switch (parent_tag) {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ if (sc.comp_unit != nullptr) {
+ variable_list_sp = sc.comp_unit->GetVariableList(false);
+ if (variable_list_sp.get() == nullptr) {
+ variable_list_sp = std::make_shared<VariableList>();
+ }
+ } else {
+ GetObjectFile()->GetModule()->ReportError(
+ "parent 0x%8.8" PRIx64 " %s with no valid compile unit in "
+ "symbol context for 0x%8.8" PRIx64
+ " %s.\n",
+ sc_parent_die.GetID(), sc_parent_die.GetTagAsCString(),
+ orig_die.GetID(), orig_die.GetTagAsCString());
+ }
+ break;
+
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_lexical_block:
+ if (sc.function != nullptr) {
+ // Check to see if we already have parsed the variables for the
+ // given scope
+
+ Block *block = sc.function->GetBlock(true).FindBlockByID(
+ sc_parent_die.GetID());
+ if (block == nullptr) {
+ // This must be a specification or abstract origin with a
+ // concrete block counterpart in the current function. We need
+ // to find the concrete block so we can correctly add the
+ // variable to it
+ const DWARFDIE concrete_block_die =
+ FindBlockContainingSpecification(
+ GetDIE(sc.function->GetID()),
+ sc_parent_die.GetOffset());
+ if (concrete_block_die)
+ block = sc.function->GetBlock(true).FindBlockByID(
+ concrete_block_die.GetID());
+ }
+
+ if (block != nullptr) {
+ const bool can_create = false;
+ variable_list_sp = block->GetBlockVariableList(can_create);
+ if (variable_list_sp.get() == nullptr) {
+ variable_list_sp = std::make_shared<VariableList>();
+ block->SetVariableList(variable_list_sp);
+ }
+ }
+ }
+ break;
+
+ default:
+ GetObjectFile()->GetModule()->ReportError(
+ "didn't find appropriate parent DIE for variable list for "
+ "0x%8.8" PRIx64 " %s.\n",
+ orig_die.GetID(), orig_die.GetTagAsCString());
+ break;
+ }
+ }
+
+ if (variable_list_sp) {
+ VariableSP var_sp(ParseVariableDIE(sc, die, func_low_pc));
+ if (var_sp) {
+ variable_list_sp->AddVariableIfUnique(var_sp);
+ if (cc_variable_list)
+ cc_variable_list->AddVariableIfUnique(var_sp);
+ ++vars_added;
+ }
+ }
+ }
+ }
+
+ bool skip_children = (sc.function == nullptr && tag == DW_TAG_subprogram);
+
+ if (!skip_children && parse_children && die.HasChildren()) {
+ vars_added += ParseVariables(sc, die.GetFirstChild(), func_low_pc, true,
+ true, cc_variable_list);
+ }
+
+ if (parse_siblings)
+ die = die.GetSibling();
+ else
+ die.Clear();
+ }
+ return vars_added;
+}
+
+/// Collect call graph edges present in a function DIE.
+static std::vector<lldb_private::CallEdge>
+CollectCallEdges(DWARFDIE function_die) {
+ // Check if the function has a supported call site-related attribute.
+ // TODO: In the future it may be worthwhile to support call_all_source_calls.
+ uint64_t has_call_edges =
+ function_die.GetAttributeValueAsUnsigned(DW_AT_call_all_calls, 0);
+ if (!has_call_edges)
+ return {};
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ LLDB_LOG(log, "CollectCallEdges: Found call site info in {0}",
+ function_die.GetPubname());
+
+ // Scan the DIE for TAG_call_site entries.
+ // TODO: A recursive scan of all blocks in the subprogram is needed in order
+ // to be DWARF5-compliant. This may need to be done lazily to be performant.
+ // For now, assume that all entries are nested directly under the subprogram
+ // (this is the kind of DWARF LLVM produces) and parse them eagerly.
+ std::vector<CallEdge> call_edges;
+ for (DWARFDIE child = function_die.GetFirstChild(); child.IsValid();
+ child = child.GetSibling()) {
+ if (child.Tag() != DW_TAG_call_site)
+ continue;
+
+ // Extract DW_AT_call_origin (the call target's DIE).
+ DWARFDIE call_origin = child.GetReferencedDIE(DW_AT_call_origin);
+ if (!call_origin.IsValid()) {
+ LLDB_LOG(log, "CollectCallEdges: Invalid call origin in {0}",
+ function_die.GetPubname());
+ continue;
+ }
+
+ // Extract DW_AT_call_return_pc (the PC the call returns to) if it's
+ // available. It should only ever be unavailable for tail call edges, in
+ // which case use LLDB_INVALID_ADDRESS.
+ addr_t return_pc = child.GetAttributeValueAsAddress(DW_AT_call_return_pc,
+ LLDB_INVALID_ADDRESS);
+
+ LLDB_LOG(log, "CollectCallEdges: Found call origin: {0} (retn-PC: {1:x})",
+ call_origin.GetPubname(), return_pc);
+ call_edges.emplace_back(call_origin.GetMangledName(), return_pc);
+ }
+ return call_edges;
+}
+
+std::vector<lldb_private::CallEdge>
+SymbolFileDWARF::ParseCallEdgesInFunction(UserID func_id) {
+ DWARFDIE func_die = GetDIE(func_id.GetID());
+ if (func_die.IsValid())
+ return CollectCallEdges(func_die);
+ return {};
+}
+
+// PluginInterface protocol
+ConstString SymbolFileDWARF::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t SymbolFileDWARF::GetPluginVersion() { return 1; }
+
+void SymbolFileDWARF::Dump(lldb_private::Stream &s) { m_index->Dump(s); }
+
+void SymbolFileDWARF::DumpClangAST(Stream &s) {
+ TypeSystem *ts = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus);
+ ClangASTContext *clang = llvm::dyn_cast_or_null<ClangASTContext>(ts);
+ if (!clang)
+ return;
+ clang->Dump(s);
+}
+
+SymbolFileDWARFDebugMap *SymbolFileDWARF::GetDebugMapSymfile() {
+ if (m_debug_map_symfile == nullptr && !m_debug_map_module_wp.expired()) {
+ lldb::ModuleSP module_sp(m_debug_map_module_wp.lock());
+ if (module_sp) {
+ SymbolVendor *sym_vendor = module_sp->GetSymbolVendor();
+ if (sym_vendor)
+ m_debug_map_symfile =
+ (SymbolFileDWARFDebugMap *)sym_vendor->GetSymbolFile();
+ }
+ }
+ return m_debug_map_symfile;
+}
+
+DWARFExpression::LocationListFormat
+SymbolFileDWARF::GetLocationListFormat() const {
+ if (m_data_debug_loclists.m_data.GetByteSize() > 0)
+ return DWARFExpression::LocLists;
+ return DWARFExpression::RegularLocationList;
+}
+
+SymbolFileDWARFDwp *SymbolFileDWARF::GetDwpSymbolFile() {
+ llvm::call_once(m_dwp_symfile_once_flag, [this]() {
+ ModuleSpec module_spec;
+ module_spec.GetFileSpec() = m_obj_file->GetFileSpec();
+ module_spec.GetSymbolFileSpec() =
+ FileSpec(m_obj_file->GetFileSpec().GetPath() + ".dwp");
+
+ FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
+ FileSpec dwp_filespec =
+ Symbols::LocateExecutableSymbolFile(module_spec, search_paths);
+ if (FileSystem::Instance().Exists(dwp_filespec)) {
+ m_dwp_symfile = SymbolFileDWARFDwp::Create(GetObjectFile()->GetModule(),
+ dwp_filespec);
+ }
+ });
+ return m_dwp_symfile.get();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
new file mode 100644
index 000000000000..018af47305f4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -0,0 +1,492 @@
+//===-- SymbolFileDWARF.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_SymbolFileDWARF_h_
+#define SymbolFileDWARF_SymbolFileDWARF_h_
+
+#include <list>
+#include <map>
+#include <mutex>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/Threading.h"
+
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/DebugMacros.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Flags.h"
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/lldb-private.h"
+
+#include "DWARFContext.h"
+#include "DWARFDataExtractor.h"
+#include "DWARFDefines.h"
+#include "DWARFIndex.h"
+#include "UniqueDWARFASTType.h"
+
+// Forward Declarations for this DWARF plugin
+class DebugMapModule;
+class DWARFAbbreviationDeclaration;
+class DWARFAbbreviationDeclarationSet;
+class DWARFCompileUnit;
+class DWARFDebugAbbrev;
+class DWARFDebugAranges;
+class DWARFDebugInfo;
+class DWARFDebugInfoEntry;
+class DWARFDebugLine;
+class DWARFDebugRangesBase;
+class DWARFDeclContext;
+class DWARFFormValue;
+class DWARFTypeUnit;
+class SymbolFileDWARFDebugMap;
+class SymbolFileDWARFDwo;
+class SymbolFileDWARFDwp;
+
+#define DIE_IS_BEING_PARSED ((lldb_private::Type *)1)
+
+class SymbolFileDWARF : public lldb_private::SymbolFile,
+ public lldb_private::UserID {
+public:
+ friend class SymbolFileDWARFDebugMap;
+ friend class SymbolFileDWARFDwo;
+ friend class DebugMapModule;
+ friend class DWARFCompileUnit;
+ friend class DWARFDIE;
+ friend class DWARFASTParserClang;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static void DebuggerInitialize(lldb_private::Debugger &debugger);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolFile *
+ CreateInstance(lldb_private::ObjectFile *obj_file);
+
+ static lldb_private::FileSpecList GetSymlinkPaths();
+
+ // Constructors and Destructors
+
+ SymbolFileDWARF(lldb_private::ObjectFile *ofile,
+ lldb_private::SectionList *dwo_section_list);
+
+ ~SymbolFileDWARF() override;
+
+ uint32_t CalculateAbilities() override;
+
+ void InitializeObject() override;
+
+ // Compile Unit function calls
+
+ uint32_t GetNumCompileUnits() override;
+
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType
+ ParseLanguage(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
+ lldb_private::FileSpecList &support_files) override;
+
+ bool ParseIsOptimized(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override;
+
+ size_t ParseBlocksRecursive(lldb_private::Function &func) override;
+
+ size_t
+ ParseVariablesForContext(const lldb_private::SymbolContext &sc) override;
+
+ lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override;
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override;
+
+ bool CompleteType(lldb_private::CompilerType &compiler_type) override;
+
+ lldb_private::Type *ResolveType(const DWARFDIE &die,
+ bool assert_not_being_parsed = true,
+ bool resolve_function_context = false);
+
+ lldb_private::CompilerDecl GetDeclForUID(lldb::user_id_t uid) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextForUID(lldb::user_id_t uid) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextContainingUID(lldb::user_id_t uid) override;
+
+ void
+ ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override;
+
+ uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContext &sc) override;
+
+ uint32_t
+ ResolveSymbolContext(const lldb_private::FileSpec &file_spec, uint32_t line,
+ bool check_inlines,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContextList &sc_list) override;
+
+ uint32_t
+ FindGlobalVariables(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+
+ uint32_t FindGlobalVariables(const lldb_private::RegularExpression &regex,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+
+ uint32_t
+ FindFunctions(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ lldb::FunctionNameType name_type_mask, bool include_inlines,
+ bool append, lldb_private::SymbolContextList &sc_list) override;
+
+ uint32_t FindFunctions(const lldb_private::RegularExpression &regex,
+ bool include_inlines, bool append,
+ lldb_private::SymbolContextList &sc_list) override;
+
+ void GetMangledNamesForFunction(
+ const std::string &scope_qualified_name,
+ std::vector<lldb_private::ConstString> &mangled_names) override;
+
+ uint32_t
+ FindTypes(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ lldb_private::TypeMap &types) override;
+
+ size_t FindTypes(const std::vector<lldb_private::CompilerContext> &context,
+ bool append, lldb_private::TypeMap &types) override;
+
+ lldb_private::TypeList *GetTypeList() override;
+
+ size_t GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ lldb::TypeClass type_mask,
+ lldb_private::TypeList &type_list) override;
+
+ lldb_private::TypeSystem *
+ GetTypeSystemForLanguage(lldb::LanguageType language) override;
+
+ lldb_private::CompilerDeclContext FindNamespace(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx) override;
+
+ void PreloadSymbols() override;
+
+ std::recursive_mutex &GetModuleMutex() const override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ const lldb_private::DWARFDataExtractor &get_debug_loc_data();
+ const lldb_private::DWARFDataExtractor &get_debug_loclists_data();
+
+ DWARFDebugAbbrev *DebugAbbrev();
+
+ const DWARFDebugAbbrev *DebugAbbrev() const;
+
+ DWARFDebugInfo *DebugInfo();
+
+ const DWARFDebugInfo *DebugInfo() const;
+
+ DWARFDebugRangesBase *GetDebugRanges();
+ DWARFDebugRangesBase *GetDebugRngLists();
+
+ const lldb_private::DWARFDataExtractor &DebugLocData();
+
+ static bool SupportedVersion(uint16_t version);
+
+ DWARFDIE
+ GetDeclContextDIEContainingDIE(const DWARFDIE &die);
+
+ bool
+ HasForwardDeclForClangType(const lldb_private::CompilerType &compiler_type);
+
+ lldb_private::CompileUnit *
+ GetCompUnitForDWARFCompUnit(DWARFCompileUnit &dwarf_cu);
+
+ virtual size_t GetObjCMethodDIEOffsets(lldb_private::ConstString class_name,
+ DIEArray &method_die_offsets);
+
+ bool Supports_DW_AT_APPLE_objc_complete_type(DWARFUnit *cu);
+
+ lldb_private::DebugMacrosSP ParseDebugMacros(lldb::offset_t *offset);
+
+ static DWARFDIE GetParentSymbolContextDIE(const DWARFDIE &die);
+
+ virtual lldb::CompUnitSP ParseCompileUnit(DWARFCompileUnit &dwarf_cu);
+
+ virtual lldb_private::DWARFExpression::LocationListFormat
+ GetLocationListFormat() const;
+
+ lldb::ModuleSP GetDWOModule(lldb_private::ConstString name);
+
+ typedef std::map<lldb_private::ConstString, lldb::ModuleSP>
+ ExternalTypeModuleMap;
+
+ /// Return the list of Clang modules imported by this SymbolFile.
+ const ExternalTypeModuleMap& getExternalTypeModules() const {
+ return m_external_type_modules;
+ }
+
+ virtual DWARFDIE GetDIE(const DIERef &die_ref);
+
+ DWARFDIE GetDIE(lldb::user_id_t uid);
+
+ lldb::user_id_t GetUID(const DWARFBaseDIE &die) {
+ return GetUID(die.GetDIERef());
+ }
+
+ lldb::user_id_t GetUID(const llvm::Optional<DIERef> &ref) {
+ return ref ? GetUID(*ref) : LLDB_INVALID_UID;
+ }
+
+ lldb::user_id_t GetUID(DIERef ref);
+
+ virtual std::unique_ptr<SymbolFileDWARFDwo>
+ GetDwoSymbolFileForCompileUnit(DWARFUnit &dwarf_cu,
+ const DWARFDebugInfoEntry &cu_die);
+
+ // For regular SymbolFileDWARF instances the method returns nullptr,
+ // for the instances of the subclass SymbolFileDWARFDwo
+ // the method returns a pointer to the base compile unit.
+ virtual DWARFCompileUnit *GetBaseCompileUnit() { return nullptr; }
+
+ virtual llvm::Optional<uint32_t> GetDwoNum() { return llvm::None; }
+
+ static bool
+ DIEInDeclContext(const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ const DWARFDIE &die);
+
+ std::vector<lldb_private::CallEdge>
+ ParseCallEdgesInFunction(UserID func_id) override;
+
+ void Dump(lldb_private::Stream &s) override;
+
+ void DumpClangAST(lldb_private::Stream &s) override;
+
+ lldb_private::DWARFContext &GetDWARFContext() { return m_context; }
+
+ lldb_private::FileSpec GetFile(DWARFUnit &unit, size_t file_idx);
+
+protected:
+ typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb_private::Type *>
+ DIEToTypePtr;
+ typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb::VariableSP>
+ DIEToVariableSP;
+ typedef llvm::DenseMap<const DWARFDebugInfoEntry *,
+ lldb::opaque_compiler_type_t>
+ DIEToClangType;
+ typedef llvm::DenseMap<lldb::opaque_compiler_type_t, lldb::user_id_t>
+ ClangTypeToDIE;
+
+ struct DWARFDataSegment {
+ llvm::once_flag m_flag;
+ lldb_private::DWARFDataExtractor m_data;
+ };
+
+ DISALLOW_COPY_AND_ASSIGN(SymbolFileDWARF);
+
+ const lldb_private::DWARFDataExtractor &
+ GetCachedSectionData(lldb::SectionType sect_type,
+ DWARFDataSegment &data_segment);
+
+ virtual void LoadSectionData(lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data);
+
+ bool DeclContextMatchesThisSymbolFile(
+ const lldb_private::CompilerDeclContext *decl_ctx);
+
+ virtual DWARFUnit *
+ GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit);
+
+ DWARFUnit *GetNextUnparsedDWARFCompileUnit(DWARFUnit *prev_cu);
+
+ bool GetFunction(const DWARFDIE &die, lldb_private::SymbolContext &sc);
+
+ lldb_private::Function *ParseFunction(lldb_private::CompileUnit &comp_unit,
+ const DWARFDIE &die);
+
+ size_t ParseBlocksRecursive(lldb_private::CompileUnit &comp_unit,
+ lldb_private::Block *parent_block,
+ const DWARFDIE &die,
+ lldb::addr_t subprogram_low_pc, uint32_t depth);
+
+ size_t ParseTypes(const lldb_private::SymbolContext &sc, const DWARFDIE &die,
+ bool parse_siblings, bool parse_children);
+
+ lldb::TypeSP ParseType(const lldb_private::SymbolContext &sc,
+ const DWARFDIE &die, bool *type_is_new);
+
+ lldb_private::Type *ResolveTypeUID(const DWARFDIE &die,
+ bool assert_not_being_parsed);
+
+ lldb_private::Type *ResolveTypeUID(const DIERef &die_ref);
+
+ lldb::VariableSP ParseVariableDIE(const lldb_private::SymbolContext &sc,
+ const DWARFDIE &die,
+ const lldb::addr_t func_low_pc);
+
+ size_t ParseVariables(const lldb_private::SymbolContext &sc,
+ const DWARFDIE &orig_die,
+ const lldb::addr_t func_low_pc, bool parse_siblings,
+ bool parse_children,
+ lldb_private::VariableList *cc_variable_list = nullptr);
+
+ bool ClassOrStructIsVirtual(const DWARFDIE &die);
+
+ // Given a die_offset, figure out the symbol context representing that die.
+ bool ResolveFunction(const DWARFDIE &die, bool include_inlines,
+ lldb_private::SymbolContextList &sc_list);
+
+ virtual lldb::TypeSP
+ FindDefinitionTypeForDWARFDeclContext(const DWARFDeclContext &die_decl_ctx);
+
+ virtual lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, lldb_private::ConstString type_name,
+ bool must_be_implementation);
+
+ lldb_private::Symbol *
+ GetObjCClassSymbol(lldb_private::ConstString objc_class_name);
+
+ lldb::TypeSP GetTypeForDIE(const DWARFDIE &die,
+ bool resolve_function_context = false);
+
+ void SetDebugMapModule(const lldb::ModuleSP &module_sp) {
+ m_debug_map_module_wp = module_sp;
+ }
+
+ SymbolFileDWARFDebugMap *GetDebugMapSymfile();
+
+ DWARFDIE
+ FindBlockContainingSpecification(const DIERef &func_die_ref,
+ dw_offset_t spec_block_die_offset);
+
+ DWARFDIE
+ FindBlockContainingSpecification(const DWARFDIE &die,
+ dw_offset_t spec_block_die_offset);
+
+ virtual UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap();
+
+ bool DIEDeclContextsMatch(const DWARFDIE &die1, const DWARFDIE &die2);
+
+ bool ClassContainsSelector(const DWARFDIE &class_die,
+ lldb_private::ConstString selector);
+
+ bool FixupAddress(lldb_private::Address &addr);
+
+ typedef std::set<lldb_private::Type *> TypeSet;
+
+ void GetTypes(const DWARFDIE &die, dw_offset_t min_die_offset,
+ dw_offset_t max_die_offset, uint32_t type_mask,
+ TypeSet &type_set);
+
+ typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t,
+ lldb_private::Variable *>
+ GlobalVariableMap;
+
+ GlobalVariableMap &GetGlobalAranges();
+
+ void UpdateExternalModuleListIfNeeded();
+
+ virtual DIEToTypePtr &GetDIEToType() { return m_die_to_type; }
+
+ virtual DIEToVariableSP &GetDIEToVariable() { return m_die_to_variable_sp; }
+
+ virtual DIEToClangType &GetForwardDeclDieToClangType() {
+ return m_forward_decl_die_to_clang_type;
+ }
+
+ virtual ClangTypeToDIE &GetForwardDeclClangTypeToDie() {
+ return m_forward_decl_clang_type_to_die;
+ }
+
+ void BuildCuTranslationTable();
+ llvm::Optional<uint32_t> GetDWARFUnitIndex(uint32_t cu_idx);
+
+ struct DecodedUID {
+ SymbolFileDWARF &dwarf;
+ DIERef ref;
+ };
+ llvm::Optional<DecodedUID> DecodeUID(lldb::user_id_t uid);
+
+ SymbolFileDWARFDwp *GetDwpSymbolFile();
+
+ const lldb_private::FileSpecList &GetTypeUnitSupportFiles(DWARFTypeUnit &tu);
+
+ lldb::ModuleWP m_debug_map_module_wp;
+ SymbolFileDWARFDebugMap *m_debug_map_symfile;
+
+ llvm::once_flag m_dwp_symfile_once_flag;
+ std::unique_ptr<SymbolFileDWARFDwp> m_dwp_symfile;
+
+ lldb_private::DWARFContext m_context;
+
+ DWARFDataSegment m_data_debug_loc;
+ DWARFDataSegment m_data_debug_loclists;
+
+ // The unique pointer items below are generated on demand if and when someone
+ // accesses them through a non const version of this class.
+ std::unique_ptr<DWARFDebugAbbrev> m_abbr;
+ std::unique_ptr<DWARFDebugInfo> m_info;
+ std::unique_ptr<GlobalVariableMap> m_global_aranges_up;
+
+ typedef std::unordered_map<lldb::offset_t, lldb_private::DebugMacrosSP>
+ DebugMacrosMap;
+ DebugMacrosMap m_debug_macros_map;
+
+ ExternalTypeModuleMap m_external_type_modules;
+ std::unique_ptr<lldb_private::DWARFIndex> m_index;
+ bool m_fetched_external_modules : 1;
+ lldb_private::LazyBool m_supports_DW_AT_APPLE_objc_complete_type;
+
+ typedef std::set<lldb::user_id_t> DIERefSet;
+ typedef llvm::StringMap<DIERefSet> NameToOffsetMap;
+ NameToOffsetMap m_function_scope_qualified_name_map;
+ std::unique_ptr<DWARFDebugRangesBase> m_ranges;
+ std::unique_ptr<DWARFDebugRangesBase> m_rnglists;
+ UniqueDWARFASTTypeMap m_unique_ast_type_map;
+ DIEToTypePtr m_die_to_type;
+ DIEToVariableSP m_die_to_variable_sp;
+ DIEToClangType m_forward_decl_die_to_clang_type;
+ ClangTypeToDIE m_forward_decl_clang_type_to_die;
+ llvm::DenseMap<dw_offset_t, lldb_private::FileSpecList>
+ m_type_unit_support_files;
+ std::vector<uint32_t> m_lldb_cu_to_dwarf_unit;
+};
+
+#endif // SymbolFileDWARF_SymbolFileDWARF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
new file mode 100644
index 000000000000..8ec64dbaf764
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -0,0 +1,1440 @@
+//===-- SymbolFileDWARFDebugMap.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolFileDWARFDebugMap.h"
+#include "DWARFDebugAranges.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Timer.h"
+
+//#define DEBUG_OSO_DMAP // DO NOT CHECKIN WITH THIS NOT COMMENTED OUT
+#if defined(DEBUG_OSO_DMAP)
+#include "lldb/Core/StreamFile.h"
+#endif
+
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/VariableList.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+#include "LogChannelDWARF.h"
+#include "SymbolFileDWARF.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Subclass lldb_private::Module so we can intercept the
+// "Module::GetObjectFile()" (so we can fixup the object file sections) and
+// also for "Module::GetSymbolVendor()" (so we can fixup the symbol file id.
+
+const SymbolFileDWARFDebugMap::FileRangeMap &
+SymbolFileDWARFDebugMap::CompileUnitInfo::GetFileRangeMap(
+ SymbolFileDWARFDebugMap *exe_symfile) {
+ if (file_range_map_valid)
+ return file_range_map;
+
+ file_range_map_valid = true;
+
+ Module *oso_module = exe_symfile->GetModuleByCompUnitInfo(this);
+ if (!oso_module)
+ return file_range_map;
+
+ ObjectFile *oso_objfile = oso_module->GetObjectFile();
+ if (!oso_objfile)
+ return file_range_map;
+
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP));
+ if (log)
+ log->Printf(
+ "%p: SymbolFileDWARFDebugMap::CompileUnitInfo::GetFileRangeMap ('%s')",
+ static_cast<void *>(this),
+ oso_module->GetSpecificationDescription().c_str());
+
+ std::vector<SymbolFileDWARFDebugMap::CompileUnitInfo *> cu_infos;
+ if (exe_symfile->GetCompUnitInfosForModule(oso_module, cu_infos)) {
+ for (auto comp_unit_info : cu_infos) {
+ Symtab *exe_symtab = exe_symfile->GetObjectFile()->GetSymtab();
+ ModuleSP oso_module_sp(oso_objfile->GetModule());
+ Symtab *oso_symtab = oso_objfile->GetSymtab();
+
+ /// const uint32_t fun_resolve_flags = SymbolContext::Module |
+ /// eSymbolContextCompUnit | eSymbolContextFunction;
+ // SectionList *oso_sections = oso_objfile->Sections();
+ // Now we need to make sections that map from zero based object file
+ // addresses to where things ended up in the main executable.
+
+ assert(comp_unit_info->first_symbol_index != UINT32_MAX);
+ // End index is one past the last valid symbol index
+ const uint32_t oso_end_idx = comp_unit_info->last_symbol_index + 1;
+ for (uint32_t idx = comp_unit_info->first_symbol_index +
+ 2; // Skip the N_SO and N_OSO
+ idx < oso_end_idx; ++idx) {
+ Symbol *exe_symbol = exe_symtab->SymbolAtIndex(idx);
+ if (exe_symbol) {
+ if (!exe_symbol->IsDebug())
+ continue;
+
+ switch (exe_symbol->GetType()) {
+ default:
+ break;
+
+ case eSymbolTypeCode: {
+ // For each N_FUN, or function that we run into in the debug map we
+ // make a new section that we add to the sections found in the .o
+ // file. This new section has the file address set to what the
+ // addresses are in the .o file, and the load address is adjusted
+ // to match where it ended up in the final executable! We do this
+ // before we parse any dwarf info so that when it goes get parsed
+ // all section/offset addresses that get registered will resolve
+ // correctly to the new addresses in the main executable.
+
+ // First we find the original symbol in the .o file's symbol table
+ Symbol *oso_fun_symbol = oso_symtab->FindFirstSymbolWithNameAndType(
+ exe_symbol->GetMangled().GetName(lldb::eLanguageTypeUnknown,
+ Mangled::ePreferMangled),
+ eSymbolTypeCode, Symtab::eDebugNo, Symtab::eVisibilityAny);
+ if (oso_fun_symbol) {
+ // Add the inverse OSO file address to debug map entry mapping
+ exe_symfile->AddOSOFileRange(
+ this, exe_symbol->GetAddressRef().GetFileAddress(),
+ exe_symbol->GetByteSize(),
+ oso_fun_symbol->GetAddressRef().GetFileAddress(),
+ oso_fun_symbol->GetByteSize());
+ }
+ } break;
+
+ case eSymbolTypeData: {
+ // For each N_GSYM we remap the address for the global by making a
+ // new section that we add to the sections found in the .o file.
+ // This new section has the file address set to what the addresses
+ // are in the .o file, and the load address is adjusted to match
+ // where it ended up in the final executable! We do this before we
+ // parse any dwarf info so that when it goes get parsed all
+ // section/offset addresses that get registered will resolve
+ // correctly to the new addresses in the main executable. We
+ // initially set the section size to be 1 byte, but will need to
+ // fix up these addresses further after all globals have been
+ // parsed to span the gaps, or we can find the global variable
+ // sizes from the DWARF info as we are parsing.
+
+ // Next we find the non-stab entry that corresponds to the N_GSYM
+ // in the .o file
+ Symbol *oso_gsym_symbol =
+ oso_symtab->FindFirstSymbolWithNameAndType(
+ exe_symbol->GetMangled().GetName(lldb::eLanguageTypeUnknown,
+ Mangled::ePreferMangled),
+ eSymbolTypeData, Symtab::eDebugNo, Symtab::eVisibilityAny);
+ if (exe_symbol && oso_gsym_symbol && exe_symbol->ValueIsAddress() &&
+ oso_gsym_symbol->ValueIsAddress()) {
+ // Add the inverse OSO file address to debug map entry mapping
+ exe_symfile->AddOSOFileRange(
+ this, exe_symbol->GetAddressRef().GetFileAddress(),
+ exe_symbol->GetByteSize(),
+ oso_gsym_symbol->GetAddressRef().GetFileAddress(),
+ oso_gsym_symbol->GetByteSize());
+ }
+ } break;
+ }
+ }
+ }
+
+ exe_symfile->FinalizeOSOFileRanges(this);
+ // We don't need the symbols anymore for the .o files
+ oso_objfile->ClearSymtab();
+ }
+ }
+ return file_range_map;
+}
+
+class DebugMapModule : public Module {
+public:
+ DebugMapModule(const ModuleSP &exe_module_sp, uint32_t cu_idx,
+ const FileSpec &file_spec, const ArchSpec &arch,
+ const ConstString *object_name, off_t object_offset,
+ const llvm::sys::TimePoint<> object_mod_time)
+ : Module(file_spec, arch, object_name, object_offset, object_mod_time),
+ m_exe_module_wp(exe_module_sp), m_cu_idx(cu_idx) {}
+
+ ~DebugMapModule() override = default;
+
+ SymbolVendor *
+ GetSymbolVendor(bool can_create = true,
+ lldb_private::Stream *feedback_strm = nullptr) override {
+ // Scope for locker
+ if (m_symfile_up.get() || !can_create)
+ return m_symfile_up.get();
+
+ ModuleSP exe_module_sp(m_exe_module_wp.lock());
+ if (exe_module_sp) {
+ // Now get the object file outside of a locking scope
+ ObjectFile *oso_objfile = GetObjectFile();
+ if (oso_objfile) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ SymbolVendor *symbol_vendor =
+ Module::GetSymbolVendor(can_create, feedback_strm);
+ if (symbol_vendor) {
+ // Set a pointer to this class to set our OSO DWARF file know that
+ // the DWARF is being used along with a debug map and that it will
+ // have the remapped sections that we do below.
+ SymbolFileDWARF *oso_symfile =
+ SymbolFileDWARFDebugMap::GetSymbolFileAsSymbolFileDWARF(
+ symbol_vendor->GetSymbolFile());
+
+ if (!oso_symfile)
+ return nullptr;
+
+ ObjectFile *exe_objfile = exe_module_sp->GetObjectFile();
+ SymbolVendor *exe_sym_vendor = exe_module_sp->GetSymbolVendor();
+
+ if (exe_objfile && exe_sym_vendor) {
+ oso_symfile->SetDebugMapModule(exe_module_sp);
+ // Set the ID of the symbol file DWARF to the index of the OSO
+ // shifted left by 32 bits to provide a unique prefix for any
+ // UserID's that get created in the symbol file.
+ oso_symfile->SetID(((uint64_t)m_cu_idx + 1ull) << 32ull);
+ }
+ return symbol_vendor;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+protected:
+ ModuleWP m_exe_module_wp;
+ const uint32_t m_cu_idx;
+};
+
+void SymbolFileDWARFDebugMap::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void SymbolFileDWARFDebugMap::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString SymbolFileDWARFDebugMap::GetPluginNameStatic() {
+ static ConstString g_name("dwarf-debugmap");
+ return g_name;
+}
+
+const char *SymbolFileDWARFDebugMap::GetPluginDescriptionStatic() {
+ return "DWARF and DWARF3 debug symbol file reader (debug map).";
+}
+
+SymbolFile *SymbolFileDWARFDebugMap::CreateInstance(ObjectFile *obj_file) {
+ return new SymbolFileDWARFDebugMap(obj_file);
+}
+
+SymbolFileDWARFDebugMap::SymbolFileDWARFDebugMap(ObjectFile *ofile)
+ : SymbolFile(ofile), m_flags(), m_compile_unit_infos(), m_func_indexes(),
+ m_glob_indexes(),
+ m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate) {}
+
+SymbolFileDWARFDebugMap::~SymbolFileDWARFDebugMap() {}
+
+void SymbolFileDWARFDebugMap::InitializeObject() {}
+
+void SymbolFileDWARFDebugMap::InitOSO() {
+ if (m_flags.test(kHaveInitializedOSOs))
+ return;
+
+ m_flags.set(kHaveInitializedOSOs);
+
+ // If the object file has been stripped, there is no sense in looking further
+ // as all of the debug symbols for the debug map will not be available
+ if (m_obj_file->IsStripped())
+ return;
+
+ // Also make sure the file type is some sort of executable. Core files, debug
+ // info files (dSYM), object files (.o files), and stub libraries all can
+ switch (m_obj_file->GetType()) {
+ case ObjectFile::eTypeInvalid:
+ case ObjectFile::eTypeCoreFile:
+ case ObjectFile::eTypeDebugInfo:
+ case ObjectFile::eTypeObjectFile:
+ case ObjectFile::eTypeStubLibrary:
+ case ObjectFile::eTypeUnknown:
+ case ObjectFile::eTypeJIT:
+ return;
+
+ case ObjectFile::eTypeExecutable:
+ case ObjectFile::eTypeDynamicLinker:
+ case ObjectFile::eTypeSharedLibrary:
+ break;
+ }
+
+ // In order to get the abilities of this plug-in, we look at the list of
+ // N_OSO entries (object files) from the symbol table and make sure that
+ // these files exist and also contain valid DWARF. If we get any of that then
+ // we return the abilities of the first N_OSO's DWARF.
+
+ Symtab *symtab = m_obj_file->GetSymtab();
+ if (symtab) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP));
+
+ std::vector<uint32_t> oso_indexes;
+ // When a mach-o symbol is encoded, the n_type field is encoded in bits
+ // 23:16, and the n_desc field is encoded in bits 15:0.
+ //
+ // To find all N_OSO entries that are part of the DWARF + debug map we find
+ // only object file symbols with the flags value as follows: bits 23:16 ==
+ // 0x66 (N_OSO) bits 15: 0 == 0x0001 (specifies this is a debug map object
+ // file)
+ const uint32_t k_oso_symbol_flags_value = 0x660001u;
+
+ const uint32_t oso_index_count =
+ symtab->AppendSymbolIndexesWithTypeAndFlagsValue(
+ eSymbolTypeObjectFile, k_oso_symbol_flags_value, oso_indexes);
+
+ if (oso_index_count > 0) {
+ symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, Symtab::eDebugYes,
+ Symtab::eVisibilityAny,
+ m_func_indexes);
+ symtab->AppendSymbolIndexesWithType(eSymbolTypeData, Symtab::eDebugYes,
+ Symtab::eVisibilityAny,
+ m_glob_indexes);
+
+ symtab->SortSymbolIndexesByValue(m_func_indexes, true);
+ symtab->SortSymbolIndexesByValue(m_glob_indexes, true);
+
+ for (uint32_t sym_idx : m_func_indexes) {
+ const Symbol *symbol = symtab->SymbolAtIndex(sym_idx);
+ lldb::addr_t file_addr = symbol->GetAddressRef().GetFileAddress();
+ lldb::addr_t byte_size = symbol->GetByteSize();
+ DebugMap::Entry debug_map_entry(
+ file_addr, byte_size, OSOEntry(sym_idx, LLDB_INVALID_ADDRESS));
+ m_debug_map.Append(debug_map_entry);
+ }
+ for (uint32_t sym_idx : m_glob_indexes) {
+ const Symbol *symbol = symtab->SymbolAtIndex(sym_idx);
+ lldb::addr_t file_addr = symbol->GetAddressRef().GetFileAddress();
+ lldb::addr_t byte_size = symbol->GetByteSize();
+ DebugMap::Entry debug_map_entry(
+ file_addr, byte_size, OSOEntry(sym_idx, LLDB_INVALID_ADDRESS));
+ m_debug_map.Append(debug_map_entry);
+ }
+ m_debug_map.Sort();
+
+ m_compile_unit_infos.resize(oso_index_count);
+
+ for (uint32_t i = 0; i < oso_index_count; ++i) {
+ const uint32_t so_idx = oso_indexes[i] - 1;
+ const uint32_t oso_idx = oso_indexes[i];
+ const Symbol *so_symbol = symtab->SymbolAtIndex(so_idx);
+ const Symbol *oso_symbol = symtab->SymbolAtIndex(oso_idx);
+ if (so_symbol && oso_symbol &&
+ so_symbol->GetType() == eSymbolTypeSourceFile &&
+ oso_symbol->GetType() == eSymbolTypeObjectFile) {
+ m_compile_unit_infos[i].so_file.SetFile(
+ so_symbol->GetName().AsCString(), FileSpec::Style::native);
+ m_compile_unit_infos[i].oso_path = oso_symbol->GetName();
+ m_compile_unit_infos[i].oso_mod_time =
+ llvm::sys::toTimePoint(oso_symbol->GetIntegerValue(0));
+ uint32_t sibling_idx = so_symbol->GetSiblingIndex();
+ // The sibling index can't be less that or equal to the current index
+ // "i"
+ if (sibling_idx == UINT32_MAX) {
+ m_obj_file->GetModule()->ReportError(
+ "N_SO in symbol with UID %u has invalid sibling in debug map, "
+ "please file a bug and attach the binary listed in this error",
+ so_symbol->GetID());
+ } else {
+ const Symbol *last_symbol = symtab->SymbolAtIndex(sibling_idx - 1);
+ m_compile_unit_infos[i].first_symbol_index = so_idx;
+ m_compile_unit_infos[i].last_symbol_index = sibling_idx - 1;
+ m_compile_unit_infos[i].first_symbol_id = so_symbol->GetID();
+ m_compile_unit_infos[i].last_symbol_id = last_symbol->GetID();
+
+ if (log)
+ log->Printf("Initialized OSO 0x%8.8x: file=%s", i,
+ oso_symbol->GetName().GetCString());
+ }
+ } else {
+ if (oso_symbol == nullptr)
+ m_obj_file->GetModule()->ReportError(
+ "N_OSO symbol[%u] can't be found, please file a bug and attach "
+ "the binary listed in this error",
+ oso_idx);
+ else if (so_symbol == nullptr)
+ m_obj_file->GetModule()->ReportError(
+ "N_SO not found for N_OSO symbol[%u], please file a bug and "
+ "attach the binary listed in this error",
+ oso_idx);
+ else if (so_symbol->GetType() != eSymbolTypeSourceFile)
+ m_obj_file->GetModule()->ReportError(
+ "N_SO has incorrect symbol type (%u) for N_OSO symbol[%u], "
+ "please file a bug and attach the binary listed in this error",
+ so_symbol->GetType(), oso_idx);
+ else if (oso_symbol->GetType() != eSymbolTypeSourceFile)
+ m_obj_file->GetModule()->ReportError(
+ "N_OSO has incorrect symbol type (%u) for N_OSO symbol[%u], "
+ "please file a bug and attach the binary listed in this error",
+ oso_symbol->GetType(), oso_idx);
+ }
+ }
+ }
+ }
+}
+
+Module *SymbolFileDWARFDebugMap::GetModuleByOSOIndex(uint32_t oso_idx) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ if (oso_idx < cu_count)
+ return GetModuleByCompUnitInfo(&m_compile_unit_infos[oso_idx]);
+ return nullptr;
+}
+
+Module *SymbolFileDWARFDebugMap::GetModuleByCompUnitInfo(
+ CompileUnitInfo *comp_unit_info) {
+ if (!comp_unit_info->oso_sp) {
+ auto pos = m_oso_map.find(
+ {comp_unit_info->oso_path, comp_unit_info->oso_mod_time});
+ if (pos != m_oso_map.end()) {
+ comp_unit_info->oso_sp = pos->second;
+ } else {
+ ObjectFile *obj_file = GetObjectFile();
+ comp_unit_info->oso_sp = std::make_shared<OSOInfo>();
+ m_oso_map[{comp_unit_info->oso_path, comp_unit_info->oso_mod_time}] =
+ comp_unit_info->oso_sp;
+ const char *oso_path = comp_unit_info->oso_path.GetCString();
+ FileSpec oso_file(oso_path);
+ ConstString oso_object;
+ if (FileSystem::Instance().Exists(oso_file)) {
+ // The modification time returned by the FS can have a higher precision
+ // than the one from the CU.
+ auto oso_mod_time = std::chrono::time_point_cast<std::chrono::seconds>(
+ FileSystem::Instance().GetModificationTime(oso_file));
+ if (oso_mod_time != comp_unit_info->oso_mod_time) {
+ obj_file->GetModule()->ReportError(
+ "debug map object file '%s' has changed (actual time is "
+ "%s, debug map time is %s"
+ ") since this executable was linked, file will be ignored",
+ oso_file.GetPath().c_str(), llvm::to_string(oso_mod_time).c_str(),
+ llvm::to_string(comp_unit_info->oso_mod_time).c_str());
+ return nullptr;
+ }
+
+ } else {
+ const bool must_exist = true;
+
+ if (!ObjectFile::SplitArchivePathWithObject(oso_path, oso_file,
+ oso_object, must_exist)) {
+ return nullptr;
+ }
+ }
+ // Always create a new module for .o files. Why? Because we use the debug
+ // map, to add new sections to each .o file and even though a .o file
+ // might not have changed, the sections that get added to the .o file can
+ // change.
+ ArchSpec oso_arch;
+ // Only adopt the architecture from the module (not the vendor or OS)
+ // since .o files for "i386-apple-ios" will historically show up as "i386
+ // -apple-macosx" due to the lack of a LC_VERSION_MIN_MACOSX or
+ // LC_VERSION_MIN_IPHONEOS load command...
+ oso_arch.SetTriple(m_obj_file->GetModule()
+ ->GetArchitecture()
+ .GetTriple()
+ .getArchName()
+ .str()
+ .c_str());
+ comp_unit_info->oso_sp->module_sp = std::make_shared<DebugMapModule>(
+ obj_file->GetModule(), GetCompUnitInfoIndex(comp_unit_info), oso_file,
+ oso_arch, oso_object ? &oso_object : nullptr, 0,
+ oso_object ? comp_unit_info->oso_mod_time : llvm::sys::TimePoint<>());
+ }
+ }
+ if (comp_unit_info->oso_sp)
+ return comp_unit_info->oso_sp->module_sp.get();
+ return nullptr;
+}
+
+bool SymbolFileDWARFDebugMap::GetFileSpecForSO(uint32_t oso_idx,
+ FileSpec &file_spec) {
+ if (oso_idx < m_compile_unit_infos.size()) {
+ if (m_compile_unit_infos[oso_idx].so_file) {
+ file_spec = m_compile_unit_infos[oso_idx].so_file;
+ return true;
+ }
+ }
+ return false;
+}
+
+ObjectFile *SymbolFileDWARFDebugMap::GetObjectFileByOSOIndex(uint32_t oso_idx) {
+ Module *oso_module = GetModuleByOSOIndex(oso_idx);
+ if (oso_module)
+ return oso_module->GetObjectFile();
+ return nullptr;
+}
+
+SymbolFileDWARF *
+SymbolFileDWARFDebugMap::GetSymbolFile(const SymbolContext &sc) {
+ return GetSymbolFile(*sc.comp_unit);
+}
+
+SymbolFileDWARF *
+SymbolFileDWARFDebugMap::GetSymbolFile(const CompileUnit &comp_unit) {
+ CompileUnitInfo *comp_unit_info = GetCompUnitInfo(comp_unit);
+ if (comp_unit_info)
+ return GetSymbolFileByCompUnitInfo(comp_unit_info);
+ return nullptr;
+}
+
+ObjectFile *SymbolFileDWARFDebugMap::GetObjectFileByCompUnitInfo(
+ CompileUnitInfo *comp_unit_info) {
+ Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info);
+ if (oso_module)
+ return oso_module->GetObjectFile();
+ return nullptr;
+}
+
+uint32_t SymbolFileDWARFDebugMap::GetCompUnitInfoIndex(
+ const CompileUnitInfo *comp_unit_info) {
+ if (!m_compile_unit_infos.empty()) {
+ const CompileUnitInfo *first_comp_unit_info = &m_compile_unit_infos.front();
+ const CompileUnitInfo *last_comp_unit_info = &m_compile_unit_infos.back();
+ if (first_comp_unit_info <= comp_unit_info &&
+ comp_unit_info <= last_comp_unit_info)
+ return comp_unit_info - first_comp_unit_info;
+ }
+ return UINT32_MAX;
+}
+
+SymbolFileDWARF *
+SymbolFileDWARFDebugMap::GetSymbolFileByOSOIndex(uint32_t oso_idx) {
+ unsigned size = m_compile_unit_infos.size();
+ if (oso_idx < size)
+ return GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[oso_idx]);
+ return nullptr;
+}
+
+SymbolFileDWARF *
+SymbolFileDWARFDebugMap::GetSymbolFileAsSymbolFileDWARF(SymbolFile *sym_file) {
+ if (sym_file &&
+ sym_file->GetPluginName() == SymbolFileDWARF::GetPluginNameStatic())
+ return (SymbolFileDWARF *)sym_file;
+ return nullptr;
+}
+
+SymbolFileDWARF *SymbolFileDWARFDebugMap::GetSymbolFileByCompUnitInfo(
+ CompileUnitInfo *comp_unit_info) {
+ Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info);
+ if (oso_module) {
+ SymbolVendor *sym_vendor = oso_module->GetSymbolVendor();
+ if (sym_vendor)
+ return GetSymbolFileAsSymbolFileDWARF(sym_vendor->GetSymbolFile());
+ }
+ return nullptr;
+}
+
+uint32_t SymbolFileDWARFDebugMap::CalculateAbilities() {
+ // In order to get the abilities of this plug-in, we look at the list of
+ // N_OSO entries (object files) from the symbol table and make sure that
+ // these files exist and also contain valid DWARF. If we get any of that then
+ // we return the abilities of the first N_OSO's DWARF.
+
+ const uint32_t oso_index_count = GetNumCompileUnits();
+ if (oso_index_count > 0) {
+ InitOSO();
+ if (!m_compile_unit_infos.empty()) {
+ return SymbolFile::CompileUnits | SymbolFile::Functions |
+ SymbolFile::Blocks | SymbolFile::GlobalVariables |
+ SymbolFile::LocalVariables | SymbolFile::VariableTypes |
+ SymbolFile::LineTables;
+ }
+ }
+ return 0;
+}
+
+uint32_t SymbolFileDWARFDebugMap::GetNumCompileUnits() {
+ InitOSO();
+ return m_compile_unit_infos.size();
+}
+
+CompUnitSP SymbolFileDWARFDebugMap::ParseCompileUnitAtIndex(uint32_t cu_idx) {
+ CompUnitSP comp_unit_sp;
+ const uint32_t cu_count = GetNumCompileUnits();
+
+ if (cu_idx < cu_count) {
+ Module *oso_module = GetModuleByCompUnitInfo(&m_compile_unit_infos[cu_idx]);
+ if (oso_module) {
+ FileSpec so_file_spec;
+ if (GetFileSpecForSO(cu_idx, so_file_spec)) {
+ // User zero as the ID to match the compile unit at offset zero in each
+ // .o file since each .o file can only have one compile unit for now.
+ lldb::user_id_t cu_id = 0;
+ m_compile_unit_infos[cu_idx].compile_unit_sp =
+ std::make_shared<CompileUnit>(
+ m_obj_file->GetModule(), nullptr, so_file_spec, cu_id,
+ eLanguageTypeUnknown, eLazyBoolCalculate);
+
+ if (m_compile_unit_infos[cu_idx].compile_unit_sp) {
+ // Let our symbol vendor know about this compile unit
+ m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(
+ cu_idx, m_compile_unit_infos[cu_idx].compile_unit_sp);
+ }
+ }
+ }
+ comp_unit_sp = m_compile_unit_infos[cu_idx].compile_unit_sp;
+ }
+
+ return comp_unit_sp;
+}
+
+SymbolFileDWARFDebugMap::CompileUnitInfo *
+SymbolFileDWARFDebugMap::GetCompUnitInfo(const SymbolContext &sc) {
+ return GetCompUnitInfo(*sc.comp_unit);
+}
+
+SymbolFileDWARFDebugMap::CompileUnitInfo *
+SymbolFileDWARFDebugMap::GetCompUnitInfo(const CompileUnit &comp_unit) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ for (uint32_t i = 0; i < cu_count; ++i) {
+ if (comp_unit == m_compile_unit_infos[i].compile_unit_sp.get())
+ return &m_compile_unit_infos[i];
+ }
+ return nullptr;
+}
+
+size_t SymbolFileDWARFDebugMap::GetCompUnitInfosForModule(
+ const lldb_private::Module *module,
+ std::vector<CompileUnitInfo *> &cu_infos) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ for (uint32_t i = 0; i < cu_count; ++i) {
+ if (module == GetModuleByCompUnitInfo(&m_compile_unit_infos[i]))
+ cu_infos.push_back(&m_compile_unit_infos[i]);
+ }
+ return cu_infos.size();
+}
+
+lldb::LanguageType
+SymbolFileDWARFDebugMap::ParseLanguage(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseLanguage(comp_unit);
+ return eLanguageTypeUnknown;
+}
+
+size_t SymbolFileDWARFDebugMap::ParseFunctions(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseFunctions(comp_unit);
+ return 0;
+}
+
+bool SymbolFileDWARFDebugMap::ParseLineTable(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseLineTable(comp_unit);
+ return false;
+}
+
+bool SymbolFileDWARFDebugMap::ParseDebugMacros(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseDebugMacros(comp_unit);
+ return false;
+}
+
+bool SymbolFileDWARFDebugMap::ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseSupportFiles(comp_unit, support_files);
+ return false;
+}
+
+bool SymbolFileDWARFDebugMap::ParseIsOptimized(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseIsOptimized(comp_unit);
+ return false;
+}
+
+bool SymbolFileDWARFDebugMap::ParseImportedModules(
+ const SymbolContext &sc, std::vector<SourceModule> &imported_modules) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc);
+ if (oso_dwarf)
+ return oso_dwarf->ParseImportedModules(sc, imported_modules);
+ return false;
+}
+
+size_t SymbolFileDWARFDebugMap::ParseBlocksRecursive(Function &func) {
+ CompileUnit *comp_unit = func.GetCompileUnit();
+ if (!comp_unit)
+ return 0;
+
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(*comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseBlocksRecursive(func);
+ return 0;
+}
+
+size_t SymbolFileDWARFDebugMap::ParseTypes(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseTypes(comp_unit);
+ return 0;
+}
+
+size_t
+SymbolFileDWARFDebugMap::ParseVariablesForContext(const SymbolContext &sc) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc);
+ if (oso_dwarf)
+ return oso_dwarf->ParseVariablesForContext(sc);
+ return 0;
+}
+
+Type *SymbolFileDWARFDebugMap::ResolveTypeUID(lldb::user_id_t type_uid) {
+ const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid);
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf)
+ return oso_dwarf->ResolveTypeUID(type_uid);
+ return nullptr;
+}
+
+llvm::Optional<SymbolFile::ArrayInfo>
+SymbolFileDWARFDebugMap::GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
+ const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid);
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf)
+ return oso_dwarf->GetDynamicArrayInfoForUID(type_uid, exe_ctx);
+ return llvm::None;
+}
+
+bool SymbolFileDWARFDebugMap::CompleteType(CompilerType &compiler_type) {
+ bool success = false;
+ if (compiler_type) {
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ if (oso_dwarf->HasForwardDeclForClangType(compiler_type)) {
+ oso_dwarf->CompleteType(compiler_type);
+ success = true;
+ return true;
+ }
+ return false;
+ });
+ }
+ return success;
+}
+
+uint32_t
+SymbolFileDWARFDebugMap::ResolveSymbolContext(const Address &exe_so_addr,
+ SymbolContextItem resolve_scope,
+ SymbolContext &sc) {
+ uint32_t resolved_flags = 0;
+ Symtab *symtab = m_obj_file->GetSymtab();
+ if (symtab) {
+ const addr_t exe_file_addr = exe_so_addr.GetFileAddress();
+
+ const DebugMap::Entry *debug_map_entry =
+ m_debug_map.FindEntryThatContains(exe_file_addr);
+ if (debug_map_entry) {
+
+ sc.symbol =
+ symtab->SymbolAtIndex(debug_map_entry->data.GetExeSymbolIndex());
+
+ if (sc.symbol != nullptr) {
+ resolved_flags |= eSymbolContextSymbol;
+
+ uint32_t oso_idx = 0;
+ CompileUnitInfo *comp_unit_info =
+ GetCompileUnitInfoForSymbolWithID(sc.symbol->GetID(), &oso_idx);
+ if (comp_unit_info) {
+ comp_unit_info->GetFileRangeMap(this);
+ Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info);
+ if (oso_module) {
+ lldb::addr_t oso_file_addr =
+ exe_file_addr - debug_map_entry->GetRangeBase() +
+ debug_map_entry->data.GetOSOFileAddress();
+ Address oso_so_addr;
+ if (oso_module->ResolveFileAddress(oso_file_addr, oso_so_addr)) {
+ resolved_flags |=
+ oso_module->GetSymbolVendor()->ResolveSymbolContext(
+ oso_so_addr, resolve_scope, sc);
+ }
+ }
+ }
+ }
+ }
+ }
+ return resolved_flags;
+}
+
+uint32_t SymbolFileDWARFDebugMap::ResolveSymbolContext(
+ const FileSpec &file_spec, uint32_t line, bool check_inlines,
+ SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
+ const uint32_t initial = sc_list.GetSize();
+ const uint32_t cu_count = GetNumCompileUnits();
+
+ for (uint32_t i = 0; i < cu_count; ++i) {
+ // If we are checking for inlines, then we need to look through all compile
+ // units no matter if "file_spec" matches.
+ bool resolve = check_inlines;
+
+ if (!resolve) {
+ FileSpec so_file_spec;
+ if (GetFileSpecForSO(i, so_file_spec)) {
+ // Match the full path if the incoming file_spec has a directory (not
+ // just a basename)
+ const bool full_match = (bool)file_spec.GetDirectory();
+ resolve = FileSpec::Equal(file_spec, so_file_spec, full_match);
+ }
+ }
+ if (resolve) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(i);
+ if (oso_dwarf)
+ oso_dwarf->ResolveSymbolContext(file_spec, line, check_inlines,
+ resolve_scope, sc_list);
+ }
+ }
+ return sc_list.GetSize() - initial;
+}
+
+uint32_t SymbolFileDWARFDebugMap::PrivateFindGlobalVariables(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ const std::vector<uint32_t>
+ &indexes, // Indexes into the symbol table that match "name"
+ uint32_t max_matches, VariableList &variables) {
+ const uint32_t original_size = variables.GetSize();
+ const size_t match_count = indexes.size();
+ for (size_t i = 0; i < match_count; ++i) {
+ uint32_t oso_idx;
+ CompileUnitInfo *comp_unit_info =
+ GetCompileUnitInfoForSymbolWithIndex(indexes[i], &oso_idx);
+ if (comp_unit_info) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf) {
+ if (oso_dwarf->FindGlobalVariables(name, parent_decl_ctx, max_matches,
+ variables))
+ if (variables.GetSize() > max_matches)
+ break;
+ }
+ }
+ }
+ return variables.GetSize() - original_size;
+}
+
+uint32_t SymbolFileDWARFDebugMap::FindGlobalVariables(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, VariableList &variables) {
+
+ // Remember how many variables are in the list before we search.
+ const uint32_t original_size = variables.GetSize();
+
+ uint32_t total_matches = 0;
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ const uint32_t oso_matches = oso_dwarf->FindGlobalVariables(
+ name, parent_decl_ctx, max_matches, variables);
+ if (oso_matches > 0) {
+ total_matches += oso_matches;
+
+ // Are we getting all matches?
+ if (max_matches == UINT32_MAX)
+ return false; // Yep, continue getting everything
+
+ // If we have found enough matches, lets get out
+ if (max_matches >= total_matches)
+ return true;
+
+ // Update the max matches for any subsequent calls to find globals in any
+ // other object files with DWARF
+ max_matches -= oso_matches;
+ }
+
+ return false;
+ });
+
+ // Return the number of variable that were appended to the list
+ return variables.GetSize() - original_size;
+}
+
+uint32_t
+SymbolFileDWARFDebugMap::FindGlobalVariables(const RegularExpression &regex,
+ uint32_t max_matches,
+ VariableList &variables) {
+ // Remember how many variables are in the list before we search.
+ const uint32_t original_size = variables.GetSize();
+
+ uint32_t total_matches = 0;
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ const uint32_t oso_matches =
+ oso_dwarf->FindGlobalVariables(regex, max_matches, variables);
+ if (oso_matches > 0) {
+ total_matches += oso_matches;
+
+ // Are we getting all matches?
+ if (max_matches == UINT32_MAX)
+ return false; // Yep, continue getting everything
+
+ // If we have found enough matches, lets get out
+ if (max_matches >= total_matches)
+ return true;
+
+ // Update the max matches for any subsequent calls to find globals in any
+ // other object files with DWARF
+ max_matches -= oso_matches;
+ }
+
+ return false;
+ });
+
+ // Return the number of variable that were appended to the list
+ return variables.GetSize() - original_size;
+}
+
+int SymbolFileDWARFDebugMap::SymbolContainsSymbolWithIndex(
+ uint32_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) {
+ const uint32_t symbol_idx = *symbol_idx_ptr;
+
+ if (symbol_idx < comp_unit_info->first_symbol_index)
+ return -1;
+
+ if (symbol_idx <= comp_unit_info->last_symbol_index)
+ return 0;
+
+ return 1;
+}
+
+int SymbolFileDWARFDebugMap::SymbolContainsSymbolWithID(
+ user_id_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) {
+ const user_id_t symbol_id = *symbol_idx_ptr;
+
+ if (symbol_id < comp_unit_info->first_symbol_id)
+ return -1;
+
+ if (symbol_id <= comp_unit_info->last_symbol_id)
+ return 0;
+
+ return 1;
+}
+
+SymbolFileDWARFDebugMap::CompileUnitInfo *
+SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithIndex(
+ uint32_t symbol_idx, uint32_t *oso_idx_ptr) {
+ const uint32_t oso_index_count = m_compile_unit_infos.size();
+ CompileUnitInfo *comp_unit_info = nullptr;
+ if (oso_index_count) {
+ comp_unit_info = (CompileUnitInfo *)bsearch(
+ &symbol_idx, &m_compile_unit_infos[0], m_compile_unit_infos.size(),
+ sizeof(CompileUnitInfo),
+ (ComparisonFunction)SymbolContainsSymbolWithIndex);
+ }
+
+ if (oso_idx_ptr) {
+ if (comp_unit_info != nullptr)
+ *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0];
+ else
+ *oso_idx_ptr = UINT32_MAX;
+ }
+ return comp_unit_info;
+}
+
+SymbolFileDWARFDebugMap::CompileUnitInfo *
+SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithID(
+ user_id_t symbol_id, uint32_t *oso_idx_ptr) {
+ const uint32_t oso_index_count = m_compile_unit_infos.size();
+ CompileUnitInfo *comp_unit_info = nullptr;
+ if (oso_index_count) {
+ comp_unit_info = (CompileUnitInfo *)::bsearch(
+ &symbol_id, &m_compile_unit_infos[0], m_compile_unit_infos.size(),
+ sizeof(CompileUnitInfo),
+ (ComparisonFunction)SymbolContainsSymbolWithID);
+ }
+
+ if (oso_idx_ptr) {
+ if (comp_unit_info != nullptr)
+ *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0];
+ else
+ *oso_idx_ptr = UINT32_MAX;
+ }
+ return comp_unit_info;
+}
+
+static void RemoveFunctionsWithModuleNotEqualTo(const ModuleSP &module_sp,
+ SymbolContextList &sc_list,
+ uint32_t start_idx) {
+ // We found functions in .o files. Not all functions in the .o files will
+ // have made it into the final output file. The ones that did make it into
+ // the final output file will have a section whose module matches the module
+ // from the ObjectFile for this SymbolFile. When the modules don't match,
+ // then we have something that was in a .o file, but doesn't map to anything
+ // in the final executable.
+ uint32_t i = start_idx;
+ while (i < sc_list.GetSize()) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ if (sc.function) {
+ const SectionSP section_sp(
+ sc.function->GetAddressRange().GetBaseAddress().GetSection());
+ if (section_sp->GetModule() != module_sp) {
+ sc_list.RemoveContextAtIndex(i);
+ continue;
+ }
+ }
+ ++i;
+ }
+}
+
+uint32_t SymbolFileDWARFDebugMap::FindFunctions(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask, bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "SymbolFileDWARFDebugMap::FindFunctions (name = %s)",
+ name.GetCString());
+
+ uint32_t initial_size = 0;
+ if (append)
+ initial_size = sc_list.GetSize();
+ else
+ sc_list.Clear();
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ uint32_t sc_idx = sc_list.GetSize();
+ if (oso_dwarf->FindFunctions(name, parent_decl_ctx, name_type_mask,
+ include_inlines, true, sc_list)) {
+ RemoveFunctionsWithModuleNotEqualTo(m_obj_file->GetModule(), sc_list,
+ sc_idx);
+ }
+ return false;
+ });
+
+ return sc_list.GetSize() - initial_size;
+}
+
+uint32_t SymbolFileDWARFDebugMap::FindFunctions(const RegularExpression &regex,
+ bool include_inlines,
+ bool append,
+ SymbolContextList &sc_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "SymbolFileDWARFDebugMap::FindFunctions (regex = '%s')",
+ regex.GetText().str().c_str());
+
+ uint32_t initial_size = 0;
+ if (append)
+ initial_size = sc_list.GetSize();
+ else
+ sc_list.Clear();
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ uint32_t sc_idx = sc_list.GetSize();
+
+ if (oso_dwarf->FindFunctions(regex, include_inlines, true, sc_list)) {
+ RemoveFunctionsWithModuleNotEqualTo(m_obj_file->GetModule(), sc_list,
+ sc_idx);
+ }
+ return false;
+ });
+
+ return sc_list.GetSize() - initial_size;
+}
+
+size_t SymbolFileDWARFDebugMap::GetTypes(SymbolContextScope *sc_scope,
+ lldb::TypeClass type_mask,
+ TypeList &type_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "SymbolFileDWARFDebugMap::GetTypes (type_mask = 0x%8.8x)",
+ type_mask);
+
+ uint32_t initial_size = type_list.GetSize();
+ SymbolFileDWARF *oso_dwarf = nullptr;
+ if (sc_scope) {
+ SymbolContext sc;
+ sc_scope->CalculateSymbolContext(&sc);
+
+ CompileUnitInfo *cu_info = GetCompUnitInfo(sc);
+ if (cu_info) {
+ oso_dwarf = GetSymbolFileByCompUnitInfo(cu_info);
+ if (oso_dwarf)
+ oso_dwarf->GetTypes(sc_scope, type_mask, type_list);
+ }
+ } else {
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ oso_dwarf->GetTypes(sc_scope, type_mask, type_list);
+ return false;
+ });
+ }
+ return type_list.GetSize() - initial_size;
+}
+
+std::vector<lldb_private::CallEdge>
+SymbolFileDWARFDebugMap::ParseCallEdgesInFunction(UserID func_id) {
+ uint32_t oso_idx = GetOSOIndexFromUserID(func_id.GetID());
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf)
+ return oso_dwarf->ParseCallEdgesInFunction(func_id);
+ return {};
+}
+
+TypeSP SymbolFileDWARFDebugMap::FindDefinitionTypeForDWARFDeclContext(
+ const DWARFDeclContext &die_decl_ctx) {
+ TypeSP type_sp;
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ type_sp = oso_dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx);
+ return ((bool)type_sp);
+ });
+ return type_sp;
+}
+
+bool SymbolFileDWARFDebugMap::Supports_DW_AT_APPLE_objc_complete_type(
+ SymbolFileDWARF *skip_dwarf_oso) {
+ if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) {
+ m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo;
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ if (skip_dwarf_oso != oso_dwarf &&
+ oso_dwarf->Supports_DW_AT_APPLE_objc_complete_type(nullptr)) {
+ m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes;
+ return true;
+ }
+ return false;
+ });
+ }
+ return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes;
+}
+
+TypeSP SymbolFileDWARFDebugMap::FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, ConstString type_name,
+ bool must_be_implementation) {
+ // If we have a debug map, we will have an Objective-C symbol whose name is
+ // the type name and whose type is eSymbolTypeObjCClass. If we can find that
+ // symbol and find its containing parent, we can locate the .o file that will
+ // contain the implementation definition since it will be scoped inside the
+ // N_SO and we can then locate the SymbolFileDWARF that corresponds to that
+ // N_SO.
+ SymbolFileDWARF *oso_dwarf = nullptr;
+ TypeSP type_sp;
+ ObjectFile *module_objfile = m_obj_file->GetModule()->GetObjectFile();
+ if (module_objfile) {
+ Symtab *symtab = module_objfile->GetSymtab();
+ if (symtab) {
+ Symbol *objc_class_symbol = symtab->FindFirstSymbolWithNameAndType(
+ type_name, eSymbolTypeObjCClass, Symtab::eDebugAny,
+ Symtab::eVisibilityAny);
+ if (objc_class_symbol) {
+ // Get the N_SO symbol that contains the objective C class symbol as
+ // this should be the .o file that contains the real definition...
+ const Symbol *source_file_symbol = symtab->GetParent(objc_class_symbol);
+
+ if (source_file_symbol &&
+ source_file_symbol->GetType() == eSymbolTypeSourceFile) {
+ const uint32_t source_file_symbol_idx =
+ symtab->GetIndexForSymbol(source_file_symbol);
+ if (source_file_symbol_idx != UINT32_MAX) {
+ CompileUnitInfo *compile_unit_info =
+ GetCompileUnitInfoForSymbolWithIndex(source_file_symbol_idx,
+ nullptr);
+ if (compile_unit_info) {
+ oso_dwarf = GetSymbolFileByCompUnitInfo(compile_unit_info);
+ if (oso_dwarf) {
+ TypeSP type_sp(oso_dwarf->FindCompleteObjCDefinitionTypeForDIE(
+ die, type_name, must_be_implementation));
+ if (type_sp) {
+ return type_sp;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Only search all .o files for the definition if we don't need the
+ // implementation because otherwise, with a valid debug map we should have
+ // the ObjC class symbol and the code above should have found it.
+ if (!must_be_implementation) {
+ TypeSP type_sp;
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ type_sp = oso_dwarf->FindCompleteObjCDefinitionTypeForDIE(
+ die, type_name, must_be_implementation);
+ return (bool)type_sp;
+ });
+
+ return type_sp;
+ }
+ return TypeSP();
+}
+
+uint32_t SymbolFileDWARFDebugMap::FindTypes(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ TypeMap &types) {
+ if (!append)
+ types.Clear();
+
+ const uint32_t initial_types_size = types.GetSize();
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ oso_dwarf->FindTypes(name, parent_decl_ctx, append, max_matches,
+ searched_symbol_files, types);
+ return types.GetSize() >= max_matches;
+ });
+
+ return types.GetSize() - initial_types_size;
+}
+
+//
+// uint32_t
+// SymbolFileDWARFDebugMap::FindTypes (const SymbolContext& sc, const
+// RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding
+// encoding, lldb::user_id_t udt_uid, TypeList& types)
+//{
+// SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc);
+// if (oso_dwarf)
+// return oso_dwarf->FindTypes (sc, regex, append, max_matches, encoding,
+// udt_uid, types);
+// return 0;
+//}
+
+CompilerDeclContext SymbolFileDWARFDebugMap::FindNamespace(
+ lldb_private::ConstString name,
+ const CompilerDeclContext *parent_decl_ctx) {
+ CompilerDeclContext matching_namespace;
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ matching_namespace = oso_dwarf->FindNamespace(name, parent_decl_ctx);
+
+ return (bool)matching_namespace;
+ });
+
+ return matching_namespace;
+}
+
+void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s) {
+ ForEachSymbolFile([&s](SymbolFileDWARF *oso_dwarf) -> bool {
+ oso_dwarf->DumpClangAST(s);
+ return true;
+ });
+}
+
+// PluginInterface protocol
+lldb_private::ConstString SymbolFileDWARFDebugMap::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t SymbolFileDWARFDebugMap::GetPluginVersion() { return 1; }
+
+lldb::CompUnitSP
+SymbolFileDWARFDebugMap::GetCompileUnit(SymbolFileDWARF *oso_dwarf) {
+ if (oso_dwarf) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) {
+ SymbolFileDWARF *oso_symfile =
+ GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]);
+ if (oso_symfile == oso_dwarf) {
+ if (!m_compile_unit_infos[cu_idx].compile_unit_sp)
+ m_compile_unit_infos[cu_idx].compile_unit_sp =
+ ParseCompileUnitAtIndex(cu_idx);
+
+ return m_compile_unit_infos[cu_idx].compile_unit_sp;
+ }
+ }
+ }
+ llvm_unreachable("this shouldn't happen");
+}
+
+SymbolFileDWARFDebugMap::CompileUnitInfo *
+SymbolFileDWARFDebugMap::GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf) {
+ if (oso_dwarf) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) {
+ SymbolFileDWARF *oso_symfile =
+ GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]);
+ if (oso_symfile == oso_dwarf) {
+ return &m_compile_unit_infos[cu_idx];
+ }
+ }
+ }
+ return nullptr;
+}
+
+void SymbolFileDWARFDebugMap::SetCompileUnit(SymbolFileDWARF *oso_dwarf,
+ const CompUnitSP &cu_sp) {
+ if (oso_dwarf) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) {
+ SymbolFileDWARF *oso_symfile =
+ GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]);
+ if (oso_symfile == oso_dwarf) {
+ if (m_compile_unit_infos[cu_idx].compile_unit_sp) {
+ assert(m_compile_unit_infos[cu_idx].compile_unit_sp.get() ==
+ cu_sp.get());
+ } else {
+ m_compile_unit_infos[cu_idx].compile_unit_sp = cu_sp;
+ m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(
+ cu_idx, cu_sp);
+ }
+ }
+ }
+ }
+}
+
+CompilerDeclContext
+SymbolFileDWARFDebugMap::GetDeclContextForUID(lldb::user_id_t type_uid) {
+ const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid);
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf)
+ return oso_dwarf->GetDeclContextForUID(type_uid);
+ return CompilerDeclContext();
+}
+
+CompilerDeclContext
+SymbolFileDWARFDebugMap::GetDeclContextContainingUID(lldb::user_id_t type_uid) {
+ const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid);
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf)
+ return oso_dwarf->GetDeclContextContainingUID(type_uid);
+ return CompilerDeclContext();
+}
+
+void SymbolFileDWARFDebugMap::ParseDeclsForContext(
+ lldb_private::CompilerDeclContext decl_ctx) {
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ oso_dwarf->ParseDeclsForContext(decl_ctx);
+ return true; // Keep iterating
+ });
+}
+
+bool SymbolFileDWARFDebugMap::AddOSOFileRange(CompileUnitInfo *cu_info,
+ lldb::addr_t exe_file_addr,
+ lldb::addr_t exe_byte_size,
+ lldb::addr_t oso_file_addr,
+ lldb::addr_t oso_byte_size) {
+ const uint32_t debug_map_idx =
+ m_debug_map.FindEntryIndexThatContains(exe_file_addr);
+ if (debug_map_idx != UINT32_MAX) {
+ DebugMap::Entry *debug_map_entry =
+ m_debug_map.FindEntryThatContains(exe_file_addr);
+ debug_map_entry->data.SetOSOFileAddress(oso_file_addr);
+ addr_t range_size = std::min<addr_t>(exe_byte_size, oso_byte_size);
+ if (range_size == 0) {
+ range_size = std::max<addr_t>(exe_byte_size, oso_byte_size);
+ if (range_size == 0)
+ range_size = 1;
+ }
+ cu_info->file_range_map.Append(
+ FileRangeMap::Entry(oso_file_addr, range_size, exe_file_addr));
+ return true;
+ }
+ return false;
+}
+
+void SymbolFileDWARFDebugMap::FinalizeOSOFileRanges(CompileUnitInfo *cu_info) {
+ cu_info->file_range_map.Sort();
+#if defined(DEBUG_OSO_DMAP)
+ const FileRangeMap &oso_file_range_map = cu_info->GetFileRangeMap(this);
+ const size_t n = oso_file_range_map.GetSize();
+ printf("SymbolFileDWARFDebugMap::FinalizeOSOFileRanges (cu_info = %p) %s\n",
+ cu_info, cu_info->oso_sp->module_sp->GetFileSpec().GetPath().c_str());
+ for (size_t i = 0; i < n; ++i) {
+ const FileRangeMap::Entry &entry = oso_file_range_map.GetEntryRef(i);
+ printf("oso [0x%16.16" PRIx64 " - 0x%16.16" PRIx64
+ ") ==> exe [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ")\n",
+ entry.GetRangeBase(), entry.GetRangeEnd(), entry.data,
+ entry.data + entry.GetByteSize());
+ }
+#endif
+}
+
+lldb::addr_t
+SymbolFileDWARFDebugMap::LinkOSOFileAddress(SymbolFileDWARF *oso_symfile,
+ lldb::addr_t oso_file_addr) {
+ CompileUnitInfo *cu_info = GetCompileUnitInfo(oso_symfile);
+ if (cu_info) {
+ const FileRangeMap::Entry *oso_range_entry =
+ cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr);
+ if (oso_range_entry) {
+ const DebugMap::Entry *debug_map_entry =
+ m_debug_map.FindEntryThatContains(oso_range_entry->data);
+ if (debug_map_entry) {
+ const lldb::addr_t offset =
+ oso_file_addr - oso_range_entry->GetRangeBase();
+ const lldb::addr_t exe_file_addr =
+ debug_map_entry->GetRangeBase() + offset;
+ return exe_file_addr;
+ }
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool SymbolFileDWARFDebugMap::LinkOSOAddress(Address &addr) {
+ // Make sure this address hasn't been fixed already
+ Module *exe_module = GetObjectFile()->GetModule().get();
+ Module *addr_module = addr.GetModule().get();
+ if (addr_module == exe_module)
+ return true; // Address is already in terms of the main executable module
+
+ CompileUnitInfo *cu_info = GetCompileUnitInfo(GetSymbolFileAsSymbolFileDWARF(
+ addr_module->GetSymbolVendor()->GetSymbolFile()));
+ if (cu_info) {
+ const lldb::addr_t oso_file_addr = addr.GetFileAddress();
+ const FileRangeMap::Entry *oso_range_entry =
+ cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr);
+ if (oso_range_entry) {
+ const DebugMap::Entry *debug_map_entry =
+ m_debug_map.FindEntryThatContains(oso_range_entry->data);
+ if (debug_map_entry) {
+ const lldb::addr_t offset =
+ oso_file_addr - oso_range_entry->GetRangeBase();
+ const lldb::addr_t exe_file_addr =
+ debug_map_entry->GetRangeBase() + offset;
+ return exe_module->ResolveFileAddress(exe_file_addr, addr);
+ }
+ }
+ }
+ return true;
+}
+
+LineTable *SymbolFileDWARFDebugMap::LinkOSOLineTable(SymbolFileDWARF *oso_dwarf,
+ LineTable *line_table) {
+ CompileUnitInfo *cu_info = GetCompileUnitInfo(oso_dwarf);
+ if (cu_info)
+ return line_table->LinkLineTable(cu_info->GetFileRangeMap(this));
+ return nullptr;
+}
+
+size_t
+SymbolFileDWARFDebugMap::AddOSOARanges(SymbolFileDWARF *dwarf2Data,
+ DWARFDebugAranges *debug_aranges) {
+ size_t num_line_entries_added = 0;
+ if (debug_aranges && dwarf2Data) {
+ CompileUnitInfo *compile_unit_info = GetCompileUnitInfo(dwarf2Data);
+ if (compile_unit_info) {
+ const FileRangeMap &file_range_map =
+ compile_unit_info->GetFileRangeMap(this);
+ for (size_t idx = 0; idx < file_range_map.GetSize(); idx++) {
+ const FileRangeMap::Entry *entry = file_range_map.GetEntryAtIndex(idx);
+ if (entry) {
+ debug_aranges->AppendRange(dwarf2Data->GetID(), entry->GetRangeBase(),
+ entry->GetRangeEnd());
+ num_line_entries_added++;
+ }
+ }
+ }
+ }
+ return num_line_entries_added;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
new file mode 100644
index 000000000000..afc6142e8231
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -0,0 +1,362 @@
+//===-- SymbolFileDWARFDebugMap.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARF_SymbolFileDWARFDebugMap_h_
+#define SymbolFileDWARF_SymbolFileDWARFDebugMap_h_
+
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Utility/RangeMap.h"
+#include "llvm/Support/Chrono.h"
+#include <bitset>
+#include <map>
+#include <vector>
+
+#include "UniqueDWARFASTType.h"
+
+class SymbolFileDWARF;
+class DWARFDebugAranges;
+class DWARFDeclContext;
+
+class SymbolFileDWARFDebugMap : public lldb_private::SymbolFile {
+public:
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolFile *
+ CreateInstance(lldb_private::ObjectFile *obj_file);
+
+ // Constructors and Destructors
+ SymbolFileDWARFDebugMap(lldb_private::ObjectFile *ofile);
+ ~SymbolFileDWARFDebugMap() override;
+
+ uint32_t CalculateAbilities() override;
+ void InitializeObject() override;
+
+ // Compile Unit function calls
+ uint32_t GetNumCompileUnits() override;
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType
+ ParseLanguage(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
+ lldb_private::FileSpecList &support_files) override;
+
+ bool ParseIsOptimized(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override;
+ size_t ParseBlocksRecursive(lldb_private::Function &func) override;
+ size_t
+ ParseVariablesForContext(const lldb_private::SymbolContext &sc) override;
+
+ lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override;
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextForUID(lldb::user_id_t uid) override;
+ lldb_private::CompilerDeclContext
+ GetDeclContextContainingUID(lldb::user_id_t uid) override;
+ void
+ ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override;
+
+ bool CompleteType(lldb_private::CompilerType &compiler_type) override;
+ uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContext &sc) override;
+ uint32_t
+ ResolveSymbolContext(const lldb_private::FileSpec &file_spec, uint32_t line,
+ bool check_inlines,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContextList &sc_list) override;
+ uint32_t
+ FindGlobalVariables(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+ uint32_t FindGlobalVariables(const lldb_private::RegularExpression &regex,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+ uint32_t
+ FindFunctions(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ lldb::FunctionNameType name_type_mask, bool include_inlines,
+ bool append, lldb_private::SymbolContextList &sc_list) override;
+ uint32_t FindFunctions(const lldb_private::RegularExpression &regex,
+ bool include_inlines, bool append,
+ lldb_private::SymbolContextList &sc_list) override;
+ uint32_t
+ FindTypes(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ lldb_private::TypeMap &types) override;
+ lldb_private::CompilerDeclContext FindNamespace(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx) override;
+ size_t GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ lldb::TypeClass type_mask,
+ lldb_private::TypeList &type_list) override;
+ std::vector<lldb_private::CallEdge>
+ ParseCallEdgesInFunction(lldb_private::UserID func_id) override;
+
+ void DumpClangAST(lldb_private::Stream &s) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ enum { kHaveInitializedOSOs = (1 << 0), kNumFlags };
+
+ friend class DebugMapModule;
+ friend class DWARFASTParserClang;
+ friend class DWARFCompileUnit;
+ friend class SymbolFileDWARF;
+ struct OSOInfo {
+ lldb::ModuleSP module_sp;
+
+ OSOInfo() : module_sp() {}
+ };
+
+ typedef std::shared_ptr<OSOInfo> OSOInfoSP;
+
+ typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t,
+ lldb::addr_t>
+ FileRangeMap;
+
+ // Class specific types
+ struct CompileUnitInfo {
+ lldb_private::FileSpec so_file;
+ lldb_private::ConstString oso_path;
+ llvm::sys::TimePoint<> oso_mod_time;
+ OSOInfoSP oso_sp;
+ lldb::CompUnitSP compile_unit_sp;
+ uint32_t first_symbol_index;
+ uint32_t last_symbol_index;
+ uint32_t first_symbol_id;
+ uint32_t last_symbol_id;
+ FileRangeMap file_range_map;
+ bool file_range_map_valid;
+
+ CompileUnitInfo()
+ : so_file(), oso_path(), oso_mod_time(), oso_sp(), compile_unit_sp(),
+ first_symbol_index(UINT32_MAX), last_symbol_index(UINT32_MAX),
+ first_symbol_id(UINT32_MAX), last_symbol_id(UINT32_MAX),
+ file_range_map(), file_range_map_valid(false) {}
+
+ const FileRangeMap &GetFileRangeMap(SymbolFileDWARFDebugMap *exe_symfile);
+ };
+
+ // Protected Member Functions
+ void InitOSO();
+
+ static uint32_t GetOSOIndexFromUserID(lldb::user_id_t uid) {
+ return (uint32_t)((uid >> 32ull) - 1ull);
+ }
+
+ static SymbolFileDWARF *GetSymbolFileAsSymbolFileDWARF(SymbolFile *sym_file);
+
+ bool GetFileSpecForSO(uint32_t oso_idx, lldb_private::FileSpec &file_spec);
+
+ CompileUnitInfo *GetCompUnitInfo(const lldb_private::SymbolContext &sc);
+ CompileUnitInfo *GetCompUnitInfo(const lldb_private::CompileUnit &comp_unit);
+
+ size_t GetCompUnitInfosForModule(const lldb_private::Module *oso_module,
+ std::vector<CompileUnitInfo *> &cu_infos);
+
+ lldb_private::Module *
+ GetModuleByCompUnitInfo(CompileUnitInfo *comp_unit_info);
+
+ lldb_private::Module *GetModuleByOSOIndex(uint32_t oso_idx);
+
+ lldb_private::ObjectFile *
+ GetObjectFileByCompUnitInfo(CompileUnitInfo *comp_unit_info);
+
+ lldb_private::ObjectFile *GetObjectFileByOSOIndex(uint32_t oso_idx);
+
+ uint32_t GetCompUnitInfoIndex(const CompileUnitInfo *comp_unit_info);
+
+ SymbolFileDWARF *GetSymbolFile(const lldb_private::SymbolContext &sc);
+ SymbolFileDWARF *GetSymbolFile(const lldb_private::CompileUnit &comp_unit);
+
+ SymbolFileDWARF *GetSymbolFileByCompUnitInfo(CompileUnitInfo *comp_unit_info);
+
+ SymbolFileDWARF *GetSymbolFileByOSOIndex(uint32_t oso_idx);
+
+ // If closure returns "false", iteration continues. If it returns
+ // "true", iteration terminates.
+ void ForEachSymbolFile(std::function<bool(SymbolFileDWARF *)> closure) {
+ for (uint32_t oso_idx = 0, num_oso_idxs = m_compile_unit_infos.size();
+ oso_idx < num_oso_idxs; ++oso_idx) {
+ if (SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx)) {
+ if (closure(oso_dwarf))
+ return;
+ }
+ }
+ }
+
+ CompileUnitInfo *GetCompileUnitInfoForSymbolWithIndex(uint32_t symbol_idx,
+ uint32_t *oso_idx_ptr);
+
+ CompileUnitInfo *GetCompileUnitInfoForSymbolWithID(lldb::user_id_t symbol_id,
+ uint32_t *oso_idx_ptr);
+
+ static int
+ SymbolContainsSymbolWithIndex(uint32_t *symbol_idx_ptr,
+ const CompileUnitInfo *comp_unit_info);
+
+ static int SymbolContainsSymbolWithID(lldb::user_id_t *symbol_idx_ptr,
+ const CompileUnitInfo *comp_unit_info);
+
+ uint32_t PrivateFindGlobalVariables(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ const std::vector<uint32_t> &name_symbol_indexes, uint32_t max_matches,
+ lldb_private::VariableList &variables);
+
+ void SetCompileUnit(SymbolFileDWARF *oso_dwarf,
+ const lldb::CompUnitSP &cu_sp);
+
+ lldb::CompUnitSP GetCompileUnit(SymbolFileDWARF *oso_dwarf);
+
+ CompileUnitInfo *GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf);
+
+ lldb::TypeSP
+ FindDefinitionTypeForDWARFDeclContext(const DWARFDeclContext &die_decl_ctx);
+
+ bool Supports_DW_AT_APPLE_objc_complete_type(SymbolFileDWARF *skip_dwarf_oso);
+
+ lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, lldb_private::ConstString type_name,
+ bool must_be_implementation);
+
+ UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap() {
+ return m_unique_ast_type_map;
+ }
+
+ // OSOEntry
+ class OSOEntry {
+ public:
+ OSOEntry()
+ : m_exe_sym_idx(UINT32_MAX), m_oso_file_addr(LLDB_INVALID_ADDRESS) {}
+
+ OSOEntry(uint32_t exe_sym_idx, lldb::addr_t oso_file_addr)
+ : m_exe_sym_idx(exe_sym_idx), m_oso_file_addr(oso_file_addr) {}
+
+ uint32_t GetExeSymbolIndex() const { return m_exe_sym_idx; }
+
+ bool operator<(const OSOEntry &rhs) const {
+ return m_exe_sym_idx < rhs.m_exe_sym_idx;
+ }
+
+ lldb::addr_t GetOSOFileAddress() const { return m_oso_file_addr; }
+
+ void SetOSOFileAddress(lldb::addr_t oso_file_addr) {
+ m_oso_file_addr = oso_file_addr;
+ }
+
+ protected:
+ uint32_t m_exe_sym_idx;
+ lldb::addr_t m_oso_file_addr;
+ };
+
+ typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, OSOEntry>
+ DebugMap;
+
+ // Member Variables
+ std::bitset<kNumFlags> m_flags;
+ std::vector<CompileUnitInfo> m_compile_unit_infos;
+ std::vector<uint32_t> m_func_indexes; // Sorted by address
+ std::vector<uint32_t> m_glob_indexes;
+ std::map<std::pair<lldb_private::ConstString, llvm::sys::TimePoint<>>,
+ OSOInfoSP>
+ m_oso_map;
+ UniqueDWARFASTTypeMap m_unique_ast_type_map;
+ lldb_private::LazyBool m_supports_DW_AT_APPLE_objc_complete_type;
+ DebugMap m_debug_map;
+
+ // When an object file from the debug map gets parsed in
+ // SymbolFileDWARF, it needs to tell the debug map about the object
+ // files addresses by calling this function once for each N_FUN,
+ // N_GSYM and N_STSYM and after all entries in the debug map have
+ // been matched up, FinalizeOSOFileRanges() should be called.
+ bool AddOSOFileRange(CompileUnitInfo *cu_info, lldb::addr_t exe_file_addr,
+ lldb::addr_t exe_byte_size, lldb::addr_t oso_file_addr,
+ lldb::addr_t oso_byte_size);
+
+ // Called after calling AddOSOFileRange() for each object file debug
+ // map entry to finalize the info for the unlinked compile unit.
+ void FinalizeOSOFileRanges(CompileUnitInfo *cu_info);
+
+ /// Convert \a addr from a .o file address, to an executable address.
+ ///
+ /// \param[in] addr
+ /// A section offset address from a .o file
+ ///
+ /// \return
+ /// Returns true if \a addr was converted to be an executable
+ /// section/offset address, false otherwise.
+ bool LinkOSOAddress(lldb_private::Address &addr);
+
+ /// Convert a .o file "file address" to an executable "file address".
+ ///
+ /// \param[in] oso_symfile
+ /// The DWARF symbol file that contains \a oso_file_addr
+ ///
+ /// \param[in] oso_file_addr
+ /// A .o file "file address" to convert.
+ ///
+ /// \return
+ /// LLDB_INVALID_ADDRESS if \a oso_file_addr is not in the
+ /// linked executable, otherwise a valid "file address" from the
+ /// linked executable that contains the debug map.
+ lldb::addr_t LinkOSOFileAddress(SymbolFileDWARF *oso_symfile,
+ lldb::addr_t oso_file_addr);
+
+ /// Given a line table full of lines with "file addresses" that are
+ /// for a .o file represented by \a oso_symfile, link a new line table
+ /// and return it.
+ ///
+ /// \param[in] oso_symfile
+ /// The DWARF symbol file that produced the \a line_table
+ ///
+ /// \param[in] addr
+ /// A section offset address from a .o file
+ ///
+ /// \return
+ /// Returns a valid line table full of linked addresses, or NULL
+ /// if none of the line table addresses exist in the main
+ /// executable.
+ lldb_private::LineTable *
+ LinkOSOLineTable(SymbolFileDWARF *oso_symfile,
+ lldb_private::LineTable *line_table);
+
+ size_t AddOSOARanges(SymbolFileDWARF *dwarf2Data,
+ DWARFDebugAranges *debug_aranges);
+};
+
+#endif // #ifndef SymbolFileDWARF_SymbolFileDWARFDebugMap_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
new file mode 100644
index 000000000000..c5b54b65ea29
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
@@ -0,0 +1,153 @@
+//===-- SymbolFileDWARFDwo.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolFileDWARFDwo.h"
+
+#include "lldb/Core/Section.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "llvm/Support/Casting.h"
+
+#include "DWARFCompileUnit.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFUnit.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SymbolFileDWARFDwo::SymbolFileDWARFDwo(ObjectFileSP objfile,
+ DWARFCompileUnit &dwarf_cu)
+ : SymbolFileDWARF(objfile.get(), objfile->GetSectionList(
+ /*update_module_section_list*/ false)),
+ m_obj_file_sp(objfile), m_base_dwarf_cu(dwarf_cu) {
+ SetID(((lldb::user_id_t)dwarf_cu.GetID()) << 32);
+}
+
+void SymbolFileDWARFDwo::LoadSectionData(lldb::SectionType sect_type,
+ DWARFDataExtractor &data) {
+ const SectionList *section_list =
+ m_obj_file->GetSectionList(false /* update_module_section_list */);
+ if (section_list) {
+ SectionSP section_sp(section_list->FindSectionByType(sect_type, true));
+ if (section_sp) {
+
+ if (m_obj_file->ReadSectionData(section_sp.get(), data) != 0)
+ return;
+
+ data.Clear();
+ }
+ }
+
+ SymbolFileDWARF::LoadSectionData(sect_type, data);
+}
+
+lldb::CompUnitSP
+SymbolFileDWARFDwo::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) {
+ assert(GetCompileUnit() == &dwarf_cu &&
+ "SymbolFileDWARFDwo::ParseCompileUnit called with incompatible "
+ "compile unit");
+ return GetBaseSymbolFile().ParseCompileUnit(m_base_dwarf_cu);
+}
+
+DWARFCompileUnit *SymbolFileDWARFDwo::GetCompileUnit() {
+ if (!m_cu)
+ m_cu = ComputeCompileUnit();
+ return m_cu;
+}
+
+DWARFCompileUnit *SymbolFileDWARFDwo::ComputeCompileUnit() {
+ DWARFDebugInfo *debug_info = DebugInfo();
+ if (!debug_info)
+ return nullptr;
+
+ // Right now we only support dwo files with one compile unit. If we don't have
+ // type units, we can just check for the unit count.
+ if (!debug_info->ContainsTypeUnits() && debug_info->GetNumUnits() == 1)
+ return llvm::cast<DWARFCompileUnit>(debug_info->GetUnitAtIndex(0));
+
+ // Otherwise, we have to run through all units, and find the compile unit that
+ // way.
+ DWARFCompileUnit *cu = nullptr;
+ for (size_t i = 0; i < debug_info->GetNumUnits(); ++i) {
+ if (auto *candidate =
+ llvm::dyn_cast<DWARFCompileUnit>(debug_info->GetUnitAtIndex(i))) {
+ if (cu)
+ return nullptr; // More that one CU found.
+ cu = candidate;
+ }
+ }
+ return cu;
+}
+
+DWARFUnit *
+SymbolFileDWARFDwo::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) {
+ return GetCompileUnit();
+}
+
+SymbolFileDWARF::DIEToTypePtr &SymbolFileDWARFDwo::GetDIEToType() {
+ return GetBaseSymbolFile().GetDIEToType();
+}
+
+SymbolFileDWARF::DIEToVariableSP &SymbolFileDWARFDwo::GetDIEToVariable() {
+ return GetBaseSymbolFile().GetDIEToVariable();
+}
+
+SymbolFileDWARF::DIEToClangType &
+SymbolFileDWARFDwo::GetForwardDeclDieToClangType() {
+ return GetBaseSymbolFile().GetForwardDeclDieToClangType();
+}
+
+SymbolFileDWARF::ClangTypeToDIE &
+SymbolFileDWARFDwo::GetForwardDeclClangTypeToDie() {
+ return GetBaseSymbolFile().GetForwardDeclClangTypeToDie();
+}
+
+size_t SymbolFileDWARFDwo::GetObjCMethodDIEOffsets(
+ lldb_private::ConstString class_name, DIEArray &method_die_offsets) {
+ return GetBaseSymbolFile().GetObjCMethodDIEOffsets(class_name,
+ method_die_offsets);
+}
+
+UniqueDWARFASTTypeMap &SymbolFileDWARFDwo::GetUniqueDWARFASTTypeMap() {
+ return GetBaseSymbolFile().GetUniqueDWARFASTTypeMap();
+}
+
+lldb::TypeSP SymbolFileDWARFDwo::FindDefinitionTypeForDWARFDeclContext(
+ const DWARFDeclContext &die_decl_ctx) {
+ return GetBaseSymbolFile().FindDefinitionTypeForDWARFDeclContext(
+ die_decl_ctx);
+}
+
+lldb::TypeSP SymbolFileDWARFDwo::FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, lldb_private::ConstString type_name,
+ bool must_be_implementation) {
+ return GetBaseSymbolFile().FindCompleteObjCDefinitionTypeForDIE(
+ die, type_name, must_be_implementation);
+}
+
+SymbolFileDWARF &SymbolFileDWARFDwo::GetBaseSymbolFile() {
+ return m_base_dwarf_cu.GetSymbolFileDWARF();
+}
+
+DWARFExpression::LocationListFormat
+SymbolFileDWARFDwo::GetLocationListFormat() const {
+ return DWARFExpression::SplitDwarfLocationList;
+}
+
+TypeSystem *
+SymbolFileDWARFDwo::GetTypeSystemForLanguage(LanguageType language) {
+ return GetBaseSymbolFile().GetTypeSystemForLanguage(language);
+}
+
+DWARFDIE
+SymbolFileDWARFDwo::GetDIE(const DIERef &die_ref) {
+ if (*die_ref.dwo_num() == GetDwoNum())
+ return DebugInfo()->GetDIE(die_ref);
+ return GetBaseSymbolFile().GetDIE(die_ref);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h
new file mode 100644
index 000000000000..9b2f3bb84c4f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h
@@ -0,0 +1,79 @@
+//===-- SymbolFileDWARFDwo.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARFDwo_SymbolFileDWARFDwo_h_
+#define SymbolFileDWARFDwo_SymbolFileDWARFDwo_h_
+
+#include "SymbolFileDWARF.h"
+
+class SymbolFileDWARFDwo : public SymbolFileDWARF {
+public:
+ SymbolFileDWARFDwo(lldb::ObjectFileSP objfile, DWARFCompileUnit &dwarf_cu);
+
+ ~SymbolFileDWARFDwo() override = default;
+
+ lldb::CompUnitSP ParseCompileUnit(DWARFCompileUnit &dwarf_cu) override;
+
+ DWARFCompileUnit *GetCompileUnit();
+
+ DWARFUnit *
+ GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) override;
+
+ lldb_private::DWARFExpression::LocationListFormat
+ GetLocationListFormat() const override;
+
+ size_t GetObjCMethodDIEOffsets(lldb_private::ConstString class_name,
+ DIEArray &method_die_offsets) override;
+
+ lldb_private::TypeSystem *
+ GetTypeSystemForLanguage(lldb::LanguageType language) override;
+
+ DWARFDIE
+ GetDIE(const DIERef &die_ref) override;
+
+ std::unique_ptr<SymbolFileDWARFDwo>
+ GetDwoSymbolFileForCompileUnit(DWARFUnit &dwarf_cu,
+ const DWARFDebugInfoEntry &cu_die) override {
+ return nullptr;
+ }
+
+ DWARFCompileUnit *GetBaseCompileUnit() override { return &m_base_dwarf_cu; }
+
+ llvm::Optional<uint32_t> GetDwoNum() override { return GetID() >> 32; }
+
+protected:
+ void LoadSectionData(lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data) override;
+
+ DIEToTypePtr &GetDIEToType() override;
+
+ DIEToVariableSP &GetDIEToVariable() override;
+
+ DIEToClangType &GetForwardDeclDieToClangType() override;
+
+ ClangTypeToDIE &GetForwardDeclClangTypeToDie() override;
+
+ UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap() override;
+
+ lldb::TypeSP FindDefinitionTypeForDWARFDeclContext(
+ const DWARFDeclContext &die_decl_ctx) override;
+
+ lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, lldb_private::ConstString type_name,
+ bool must_be_implementation) override;
+
+ SymbolFileDWARF &GetBaseSymbolFile();
+
+ DWARFCompileUnit *ComputeCompileUnit();
+
+ lldb::ObjectFileSP m_obj_file_sp;
+ DWARFCompileUnit &m_base_dwarf_cu;
+ DWARFCompileUnit *m_cu = nullptr;
+};
+
+#endif // SymbolFileDWARFDwo_SymbolFileDWARFDwo_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.cpp
new file mode 100644
index 000000000000..efea192b17ce
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.cpp
@@ -0,0 +1,35 @@
+//===-- SymbolFileDWARFDwoDwp.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolFileDWARFDwoDwp.h"
+
+#include "lldb/Core/Section.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/LLDBAssert.h"
+
+#include "DWARFUnit.h"
+#include "DWARFDebugInfo.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SymbolFileDWARFDwoDwp::SymbolFileDWARFDwoDwp(SymbolFileDWARFDwp *dwp_symfile,
+ ObjectFileSP objfile,
+ DWARFCompileUnit &dwarf_cu,
+ uint64_t dwo_id)
+ : SymbolFileDWARFDwo(objfile, dwarf_cu), m_dwp_symfile(dwp_symfile),
+ m_dwo_id(dwo_id) {}
+
+void SymbolFileDWARFDwoDwp::LoadSectionData(lldb::SectionType sect_type,
+ DWARFDataExtractor &data) {
+ if (m_dwp_symfile->LoadSectionData(m_dwo_id, sect_type, data))
+ return;
+
+ SymbolFileDWARF::LoadSectionData(sect_type, data);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.h
new file mode 100644
index 000000000000..2105e1a8f6cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.h
@@ -0,0 +1,29 @@
+//===-- SymbolFileDWARFDwoDwp.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARFDwoDwp_SymbolFileDWARFDwoDwp_h_
+#define SymbolFileDWARFDwoDwp_SymbolFileDWARFDwoDwp_h_
+
+#include "SymbolFileDWARFDwo.h"
+#include "SymbolFileDWARFDwp.h"
+
+class SymbolFileDWARFDwoDwp : public SymbolFileDWARFDwo {
+public:
+ SymbolFileDWARFDwoDwp(SymbolFileDWARFDwp *dwp_symfile,
+ lldb::ObjectFileSP objfile, DWARFCompileUnit &dwarf_cu,
+ uint64_t dwo_id);
+
+protected:
+ void LoadSectionData(lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data) override;
+
+ SymbolFileDWARFDwp *m_dwp_symfile;
+ uint64_t m_dwo_id;
+};
+
+#endif // SymbolFileDWARFDwoDwp_SymbolFileDWARFDwoDwp_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.cpp
new file mode 100644
index 000000000000..08e6e1c8c2f3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.cpp
@@ -0,0 +1,138 @@
+//===-- SymbolFileDWARFDwp.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolFileDWARFDwp.h"
+
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+
+#include "SymbolFileDWARFDwoDwp.h"
+
+static llvm::DWARFSectionKind
+lldbSectTypeToLlvmSectionKind(lldb::SectionType type) {
+ switch (type) {
+ case lldb::eSectionTypeDWARFDebugInfo:
+ return llvm::DW_SECT_INFO;
+ // case lldb::eSectionTypeDWARFDebugTypes:
+ // return llvm::DW_SECT_TYPES;
+ case lldb::eSectionTypeDWARFDebugAbbrev:
+ return llvm::DW_SECT_ABBREV;
+ case lldb::eSectionTypeDWARFDebugLine:
+ return llvm::DW_SECT_LINE;
+ case lldb::eSectionTypeDWARFDebugLoc:
+ return llvm::DW_SECT_LOC;
+ case lldb::eSectionTypeDWARFDebugStrOffsets:
+ return llvm::DW_SECT_STR_OFFSETS;
+ // case lldb::eSectionTypeDWARFDebugMacinfo:
+ // return llvm::DW_SECT_MACINFO;
+ case lldb::eSectionTypeDWARFDebugMacro:
+ return llvm::DW_SECT_MACRO;
+ default:
+ // Note: 0 is an invalid dwarf section kind.
+ return llvm::DWARFSectionKind(0);
+ }
+}
+
+std::unique_ptr<SymbolFileDWARFDwp>
+SymbolFileDWARFDwp::Create(lldb::ModuleSP module_sp,
+ const lldb_private::FileSpec &file_spec) {
+ const lldb::offset_t file_offset = 0;
+ lldb::DataBufferSP file_data_sp;
+ lldb::offset_t file_data_offset = 0;
+ lldb::ObjectFileSP obj_file = lldb_private::ObjectFile::FindPlugin(
+ module_sp, &file_spec, file_offset,
+ lldb_private::FileSystem::Instance().GetByteSize(file_spec), file_data_sp,
+ file_data_offset);
+ if (obj_file == nullptr)
+ return nullptr;
+
+ std::unique_ptr<SymbolFileDWARFDwp> dwp_symfile(
+ new SymbolFileDWARFDwp(module_sp, obj_file));
+
+ lldb_private::DWARFDataExtractor debug_cu_index;
+ if (!dwp_symfile->LoadRawSectionData(lldb::eSectionTypeDWARFDebugCuIndex,
+ debug_cu_index))
+ return nullptr;
+
+ llvm::DataExtractor llvm_debug_cu_index(
+ llvm::StringRef(debug_cu_index.PeekCStr(0), debug_cu_index.GetByteSize()),
+ debug_cu_index.GetByteOrder() == lldb::eByteOrderLittle,
+ debug_cu_index.GetAddressByteSize());
+ if (!dwp_symfile->m_debug_cu_index.parse(llvm_debug_cu_index))
+ return nullptr;
+ dwp_symfile->InitDebugCUIndexMap();
+ return dwp_symfile;
+}
+
+void SymbolFileDWARFDwp::InitDebugCUIndexMap() {
+ m_debug_cu_index_map.clear();
+ for (const auto &entry : m_debug_cu_index.getRows())
+ m_debug_cu_index_map.emplace(entry.getSignature(), &entry);
+}
+
+SymbolFileDWARFDwp::SymbolFileDWARFDwp(lldb::ModuleSP module_sp,
+ lldb::ObjectFileSP obj_file)
+ : m_obj_file(std::move(obj_file)), m_debug_cu_index(llvm::DW_SECT_INFO)
+{}
+
+std::unique_ptr<SymbolFileDWARFDwo>
+SymbolFileDWARFDwp::GetSymbolFileForDwoId(DWARFCompileUnit &dwarf_cu,
+ uint64_t dwo_id) {
+ return std::unique_ptr<SymbolFileDWARFDwo>(
+ new SymbolFileDWARFDwoDwp(this, m_obj_file, dwarf_cu, dwo_id));
+}
+
+bool SymbolFileDWARFDwp::LoadSectionData(
+ uint64_t dwo_id, lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data) {
+ lldb_private::DWARFDataExtractor section_data;
+ if (!LoadRawSectionData(sect_type, section_data))
+ return false;
+
+ auto it = m_debug_cu_index_map.find(dwo_id);
+ if (it == m_debug_cu_index_map.end())
+ return false;
+
+ auto *offsets =
+ it->second->getOffset(lldbSectTypeToLlvmSectionKind(sect_type));
+ if (offsets) {
+ data.SetData(section_data, offsets->Offset, offsets->Length);
+ } else {
+ data.SetData(section_data, 0, section_data.GetByteSize());
+ }
+ return true;
+}
+
+bool SymbolFileDWARFDwp::LoadRawSectionData(
+ lldb::SectionType sect_type, lldb_private::DWARFDataExtractor &data) {
+ std::lock_guard<std::mutex> lock(m_sections_mutex);
+
+ auto it = m_sections.find(sect_type);
+ if (it != m_sections.end()) {
+ if (it->second.GetByteSize() == 0)
+ return false;
+
+ data = it->second;
+ return true;
+ }
+
+ const lldb_private::SectionList *section_list =
+ m_obj_file->GetSectionList(false /* update_module_section_list */);
+ if (section_list) {
+ lldb::SectionSP section_sp(
+ section_list->FindSectionByType(sect_type, true));
+ if (section_sp) {
+ if (m_obj_file->ReadSectionData(section_sp.get(), data) != 0) {
+ m_sections[sect_type] = data;
+ return true;
+ }
+ }
+ }
+ m_sections[sect_type].Clear();
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.h
new file mode 100644
index 000000000000..ef06b9dca8bb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.h
@@ -0,0 +1,50 @@
+//===-- SymbolFileDWARFDwp.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SymbolFileDWARFDwp_SymbolFileDWARFDwp_h_
+#define SymbolFileDWARFDwp_SymbolFileDWARFDwp_h_
+
+#include <memory>
+
+#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
+
+#include "lldb/Core/Module.h"
+
+#include "DWARFDataExtractor.h"
+#include "SymbolFileDWARFDwo.h"
+
+class SymbolFileDWARFDwp {
+public:
+ static std::unique_ptr<SymbolFileDWARFDwp>
+ Create(lldb::ModuleSP module_sp, const lldb_private::FileSpec &file_spec);
+
+ std::unique_ptr<SymbolFileDWARFDwo>
+ GetSymbolFileForDwoId(DWARFCompileUnit &dwarf_cu, uint64_t dwo_id);
+
+ bool LoadSectionData(uint64_t dwo_id, lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data);
+
+private:
+ explicit SymbolFileDWARFDwp(lldb::ModuleSP module_sp,
+ lldb::ObjectFileSP obj_file);
+
+ bool LoadRawSectionData(lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data);
+
+ void InitDebugCUIndexMap();
+
+ lldb::ObjectFileSP m_obj_file;
+
+ std::mutex m_sections_mutex;
+ std::map<lldb::SectionType, lldb_private::DWARFDataExtractor> m_sections;
+
+ llvm::DWARFUnitIndex m_debug_cu_index;
+ std::map<uint64_t, const llvm::DWARFUnitIndex::Entry *> m_debug_cu_index_map;
+};
+
+#endif // SymbolFileDWARFDwp_SymbolFileDWARFDwp_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp
new file mode 100644
index 000000000000..8da7e2226266
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp
@@ -0,0 +1,73 @@
+//===-- UniqueDWARFASTType.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UniqueDWARFASTType.h"
+
+#include "lldb/Symbol/Declaration.h"
+
+bool UniqueDWARFASTTypeList::Find(const DWARFDIE &die,
+ const lldb_private::Declaration &decl,
+ const int32_t byte_size,
+ UniqueDWARFASTType &entry) const {
+ for (const UniqueDWARFASTType &udt : m_collection) {
+ // Make sure the tags match
+ if (udt.m_die.Tag() == die.Tag()) {
+ // Validate byte sizes of both types only if both are valid.
+ if (udt.m_byte_size < 0 || byte_size < 0 ||
+ udt.m_byte_size == byte_size) {
+ // Make sure the file and line match
+ if (udt.m_declaration == decl) {
+ // The type has the same name, and was defined on the same file and
+ // line. Now verify all of the parent DIEs match.
+ DWARFDIE parent_arg_die = die.GetParent();
+ DWARFDIE parent_pos_die = udt.m_die.GetParent();
+ bool match = true;
+ bool done = false;
+ while (!done && match && parent_arg_die && parent_pos_die) {
+ const dw_tag_t parent_arg_tag = parent_arg_die.Tag();
+ const dw_tag_t parent_pos_tag = parent_pos_die.Tag();
+ if (parent_arg_tag == parent_pos_tag) {
+ switch (parent_arg_tag) {
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_namespace: {
+ const char *parent_arg_die_name = parent_arg_die.GetName();
+ if (parent_arg_die_name ==
+ nullptr) // Anonymous (i.e. no-name) struct
+ {
+ match = false;
+ } else {
+ const char *parent_pos_die_name = parent_pos_die.GetName();
+ if (parent_pos_die_name == nullptr ||
+ ((parent_arg_die_name != parent_pos_die_name) &&
+ strcmp(parent_arg_die_name, parent_pos_die_name)))
+ match = false;
+ }
+ } break;
+
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ done = true;
+ break;
+ }
+ }
+ parent_arg_die = parent_arg_die.GetParent();
+ parent_pos_die = parent_pos_die.GetParent();
+ }
+
+ if (match) {
+ entry = udt;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h
new file mode 100644
index 000000000000..1269dbac7126
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h
@@ -0,0 +1,103 @@
+//===-- UniqueDWARFASTType.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_UniqueDWARFASTType_h_
+#define lldb_UniqueDWARFASTType_h_
+
+#include <vector>
+
+#include "llvm/ADT/DenseMap.h"
+
+#include "DWARFDIE.h"
+#include "lldb/Symbol/Declaration.h"
+
+class UniqueDWARFASTType {
+public:
+ // Constructors and Destructors
+ UniqueDWARFASTType()
+ : m_type_sp(), m_die(), m_declaration(),
+ m_byte_size(
+ -1) // Set to negative value to make sure we have a valid value
+ {}
+
+ UniqueDWARFASTType(lldb::TypeSP &type_sp, const DWARFDIE &die,
+ const lldb_private::Declaration &decl, int32_t byte_size)
+ : m_type_sp(type_sp), m_die(die), m_declaration(decl),
+ m_byte_size(byte_size) {}
+
+ UniqueDWARFASTType(const UniqueDWARFASTType &rhs)
+ : m_type_sp(rhs.m_type_sp), m_die(rhs.m_die),
+ m_declaration(rhs.m_declaration), m_byte_size(rhs.m_byte_size) {}
+
+ ~UniqueDWARFASTType() {}
+
+ UniqueDWARFASTType &operator=(const UniqueDWARFASTType &rhs) {
+ if (this != &rhs) {
+ m_type_sp = rhs.m_type_sp;
+ m_die = rhs.m_die;
+ m_declaration = rhs.m_declaration;
+ m_byte_size = rhs.m_byte_size;
+ }
+ return *this;
+ }
+
+ lldb::TypeSP m_type_sp;
+ DWARFDIE m_die;
+ lldb_private::Declaration m_declaration;
+ int32_t m_byte_size;
+};
+
+class UniqueDWARFASTTypeList {
+public:
+ UniqueDWARFASTTypeList() : m_collection() {}
+
+ ~UniqueDWARFASTTypeList() {}
+
+ uint32_t GetSize() { return (uint32_t)m_collection.size(); }
+
+ void Append(const UniqueDWARFASTType &entry) {
+ m_collection.push_back(entry);
+ }
+
+ bool Find(const DWARFDIE &die, const lldb_private::Declaration &decl,
+ const int32_t byte_size, UniqueDWARFASTType &entry) const;
+
+protected:
+ typedef std::vector<UniqueDWARFASTType> collection;
+ collection m_collection;
+};
+
+class UniqueDWARFASTTypeMap {
+public:
+ UniqueDWARFASTTypeMap() : m_collection() {}
+
+ ~UniqueDWARFASTTypeMap() {}
+
+ void Insert(lldb_private::ConstString name,
+ const UniqueDWARFASTType &entry) {
+ m_collection[name.GetCString()].Append(entry);
+ }
+
+ bool Find(lldb_private::ConstString name, const DWARFDIE &die,
+ const lldb_private::Declaration &decl, const int32_t byte_size,
+ UniqueDWARFASTType &entry) const {
+ const char *unique_name_cstr = name.GetCString();
+ collection::const_iterator pos = m_collection.find(unique_name_cstr);
+ if (pos != m_collection.end()) {
+ return pos->second.Find(die, decl, byte_size, entry);
+ }
+ return false;
+ }
+
+protected:
+ // A unique name string should be used
+ typedef llvm::DenseMap<const char *, UniqueDWARFASTTypeList> collection;
+ collection m_collection;
+};
+
+#endif // lldb_UniqueDWARFASTType_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp
new file mode 100644
index 000000000000..3834165c71c0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp
@@ -0,0 +1,457 @@
+//===-- CodeViewRegisterMapping.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CodeViewRegisterMapping.h"
+
+#include "lldb/lldb-defines.h"
+
+#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
+
+using namespace lldb_private;
+
+static const uint32_t g_code_view_to_lldb_registers_x86[] = {
+ LLDB_INVALID_REGNUM, // NONE
+ lldb_al_i386, // AL
+ lldb_cl_i386, // CL
+ lldb_dl_i386, // DL
+ lldb_bl_i386, // BL
+ lldb_ah_i386, // AH
+ lldb_ch_i386, // CH
+ lldb_dh_i386, // DH
+ lldb_bh_i386, // BH
+ lldb_ax_i386, // AX
+ lldb_cx_i386, // CX
+ lldb_dx_i386, // DX
+ lldb_bx_i386, // BX
+ lldb_sp_i386, // SP
+ lldb_bp_i386, // BP
+ lldb_si_i386, // SI
+ lldb_di_i386, // DI
+ lldb_eax_i386, // EAX
+ lldb_ecx_i386, // ECX
+ lldb_edx_i386, // EDX
+ lldb_ebx_i386, // EBX
+ lldb_esp_i386, // ESP
+ lldb_ebp_i386, // EBP
+ lldb_esi_i386, // ESI
+ lldb_edi_i386, // EDI
+ lldb_es_i386, // ES
+ lldb_cs_i386, // CS
+ lldb_ss_i386, // SS
+ lldb_ds_i386, // DS
+ lldb_fs_i386, // FS
+ lldb_gs_i386, // GS
+ LLDB_INVALID_REGNUM, // IP
+ LLDB_INVALID_REGNUM, // FLAGS
+ lldb_eip_i386, // EIP
+ lldb_eflags_i386, // EFLAGS
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // TEMP
+ LLDB_INVALID_REGNUM, // TEMPH
+ LLDB_INVALID_REGNUM, // QUOTE
+ LLDB_INVALID_REGNUM, // PCDR3
+ LLDB_INVALID_REGNUM, // PCDR4
+ LLDB_INVALID_REGNUM, // PCDR5
+ LLDB_INVALID_REGNUM, // PCDR6
+ LLDB_INVALID_REGNUM, // PCDR7
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // CR0
+ LLDB_INVALID_REGNUM, // CR1
+ LLDB_INVALID_REGNUM, // CR2
+ LLDB_INVALID_REGNUM, // CR3
+ LLDB_INVALID_REGNUM, // CR4
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ lldb_dr0_i386, // DR0
+ lldb_dr1_i386, // DR1
+ lldb_dr2_i386, // DR2
+ lldb_dr3_i386, // DR3
+ lldb_dr4_i386, // DR4
+ lldb_dr5_i386, // DR5
+ lldb_dr6_i386, // DR6
+ lldb_dr7_i386, // DR7
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // GDTR
+ LLDB_INVALID_REGNUM, // GDTL
+ LLDB_INVALID_REGNUM, // IDTR
+ LLDB_INVALID_REGNUM, // IDTL
+ LLDB_INVALID_REGNUM, // LDTR
+ LLDB_INVALID_REGNUM, // TR
+ LLDB_INVALID_REGNUM, // PSEUDO1
+ LLDB_INVALID_REGNUM, // PSEUDO2
+ LLDB_INVALID_REGNUM, // PSEUDO3
+ LLDB_INVALID_REGNUM, // PSEUDO4
+ LLDB_INVALID_REGNUM, // PSEUDO5
+ LLDB_INVALID_REGNUM, // PSEUDO6
+ LLDB_INVALID_REGNUM, // PSEUDO7
+ LLDB_INVALID_REGNUM, // PSEUDO8
+ LLDB_INVALID_REGNUM, // PSEUDO9
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ lldb_st0_i386, // ST0
+ lldb_st1_i386, // ST1
+ lldb_st2_i386, // ST2
+ lldb_st3_i386, // ST3
+ lldb_st4_i386, // ST4
+ lldb_st5_i386, // ST5
+ lldb_st6_i386, // ST6
+ lldb_st7_i386, // ST7
+ LLDB_INVALID_REGNUM, // CTRL
+ LLDB_INVALID_REGNUM, // STAT
+ LLDB_INVALID_REGNUM, // TAG
+ LLDB_INVALID_REGNUM, // FPIP
+ LLDB_INVALID_REGNUM, // FPCS
+ LLDB_INVALID_REGNUM, // FPDO
+ LLDB_INVALID_REGNUM, // FPDS
+ LLDB_INVALID_REGNUM, // ISEM
+ LLDB_INVALID_REGNUM, // FPEIP
+ LLDB_INVALID_REGNUM, // FPEDO
+ lldb_mm0_i386, // MM0
+ lldb_mm1_i386, // MM1
+ lldb_mm2_i386, // MM2
+ lldb_mm3_i386, // MM3
+ lldb_mm4_i386, // MM4
+ lldb_mm5_i386, // MM5
+ lldb_mm6_i386, // MM6
+ lldb_mm7_i386, // MM7
+ lldb_xmm0_i386, // XMM0
+ lldb_xmm1_i386, // XMM1
+ lldb_xmm2_i386, // XMM2
+ lldb_xmm3_i386, // XMM3
+ lldb_xmm4_i386, // XMM4
+ lldb_xmm5_i386, // XMM5
+ lldb_xmm6_i386, // XMM6
+ lldb_xmm7_i386 // XMM7
+};
+
+static const uint32_t g_code_view_to_lldb_registers_x86_64[] = {
+ LLDB_INVALID_REGNUM, // NONE
+ lldb_al_x86_64, // AL
+ lldb_cl_x86_64, // CL
+ lldb_dl_x86_64, // DL
+ lldb_bl_x86_64, // BL
+ lldb_ah_x86_64, // AH
+ lldb_ch_x86_64, // CH
+ lldb_dh_x86_64, // DH
+ lldb_bh_x86_64, // BH
+ lldb_ax_x86_64, // AX
+ lldb_cx_x86_64, // CX
+ lldb_dx_x86_64, // DX
+ lldb_bx_x86_64, // BX
+ lldb_sp_x86_64, // SP
+ lldb_bp_x86_64, // BP
+ lldb_si_x86_64, // SI
+ lldb_di_x86_64, // DI
+ lldb_eax_x86_64, // EAX
+ lldb_ecx_x86_64, // ECX
+ lldb_edx_x86_64, // EDX
+ lldb_ebx_x86_64, // EBX
+ lldb_esp_x86_64, // ESP
+ lldb_ebp_x86_64, // EBP
+ lldb_esi_x86_64, // ESI
+ lldb_edi_x86_64, // EDI
+ lldb_es_x86_64, // ES
+ lldb_cs_x86_64, // CS
+ lldb_ss_x86_64, // SS
+ lldb_ds_x86_64, // DS
+ lldb_fs_x86_64, // FS
+ lldb_gs_x86_64, // GS
+ LLDB_INVALID_REGNUM, // IP
+ LLDB_INVALID_REGNUM, // FLAGS
+ LLDB_INVALID_REGNUM, // EIP
+ LLDB_INVALID_REGNUM, // EFLAGS
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // TEMP
+ LLDB_INVALID_REGNUM, // TEMPH
+ LLDB_INVALID_REGNUM, // QUOTE
+ LLDB_INVALID_REGNUM, // PCDR3
+ LLDB_INVALID_REGNUM, // PCDR4
+ LLDB_INVALID_REGNUM, // PCDR5
+ LLDB_INVALID_REGNUM, // PCDR6
+ LLDB_INVALID_REGNUM, // PCDR7
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // CR0
+ LLDB_INVALID_REGNUM, // CR1
+ LLDB_INVALID_REGNUM, // CR2
+ LLDB_INVALID_REGNUM, // CR3
+ LLDB_INVALID_REGNUM, // CR4
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ lldb_dr0_x86_64, // DR0
+ lldb_dr1_x86_64, // DR1
+ lldb_dr2_x86_64, // DR2
+ lldb_dr3_x86_64, // DR3
+ lldb_dr4_x86_64, // DR4
+ lldb_dr5_x86_64, // DR5
+ lldb_dr6_x86_64, // DR6
+ lldb_dr7_x86_64, // DR7
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // GDTR
+ LLDB_INVALID_REGNUM, // GDTL
+ LLDB_INVALID_REGNUM, // IDTR
+ LLDB_INVALID_REGNUM, // IDTL
+ LLDB_INVALID_REGNUM, // LDTR
+ LLDB_INVALID_REGNUM, // TR
+ LLDB_INVALID_REGNUM, // PSEUDO1
+ LLDB_INVALID_REGNUM, // PSEUDO2
+ LLDB_INVALID_REGNUM, // PSEUDO3
+ LLDB_INVALID_REGNUM, // PSEUDO4
+ LLDB_INVALID_REGNUM, // PSEUDO5
+ LLDB_INVALID_REGNUM, // PSEUDO6
+ LLDB_INVALID_REGNUM, // PSEUDO7
+ LLDB_INVALID_REGNUM, // PSEUDO8
+ LLDB_INVALID_REGNUM, // PSEUDO9
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ lldb_st0_x86_64, // ST0
+ lldb_st1_x86_64, // ST1
+ lldb_st2_x86_64, // ST2
+ lldb_st3_x86_64, // ST3
+ lldb_st4_x86_64, // ST4
+ lldb_st5_x86_64, // ST5
+ lldb_st6_x86_64, // ST6
+ lldb_st7_x86_64, // ST7
+ LLDB_INVALID_REGNUM, // CTRL
+ LLDB_INVALID_REGNUM, // STAT
+ LLDB_INVALID_REGNUM, // TAG
+ LLDB_INVALID_REGNUM, // FPIP
+ LLDB_INVALID_REGNUM, // FPCS
+ LLDB_INVALID_REGNUM, // FPDO
+ LLDB_INVALID_REGNUM, // FPDS
+ LLDB_INVALID_REGNUM, // ISEM
+ LLDB_INVALID_REGNUM, // FPEIP
+ LLDB_INVALID_REGNUM, // FPEDO
+ lldb_mm0_x86_64, // MM0
+ lldb_mm1_x86_64, // MM1
+ lldb_mm2_x86_64, // MM2
+ lldb_mm3_x86_64, // MM3
+ lldb_mm4_x86_64, // MM4
+ lldb_mm5_x86_64, // MM5
+ lldb_mm6_x86_64, // MM6
+ lldb_mm7_x86_64, // MM7
+ lldb_xmm0_x86_64, // XMM0
+ lldb_xmm1_x86_64, // XMM1
+ lldb_xmm2_x86_64, // XMM2
+ lldb_xmm3_x86_64, // XMM3
+ lldb_xmm4_x86_64, // XMM4
+ lldb_xmm5_x86_64, // XMM5
+ lldb_xmm6_x86_64, // XMM6
+ lldb_xmm7_x86_64, // XMM7
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM,
+ lldb_mxcsr_x86_64, // MXCSR
+ LLDB_INVALID_REGNUM, // EDXEAX
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // EMM0L
+ LLDB_INVALID_REGNUM, // EMM1L
+ LLDB_INVALID_REGNUM, // EMM2L
+ LLDB_INVALID_REGNUM, // EMM3L
+ LLDB_INVALID_REGNUM, // EMM4L
+ LLDB_INVALID_REGNUM, // EMM5L
+ LLDB_INVALID_REGNUM, // EMM6L
+ LLDB_INVALID_REGNUM, // EMM7L
+ LLDB_INVALID_REGNUM, // EMM0H
+ LLDB_INVALID_REGNUM, // EMM1H
+ LLDB_INVALID_REGNUM, // EMM2H
+ LLDB_INVALID_REGNUM, // EMM3H
+ LLDB_INVALID_REGNUM, // EMM4H
+ LLDB_INVALID_REGNUM, // EMM5H
+ LLDB_INVALID_REGNUM, // EMM6H
+ LLDB_INVALID_REGNUM, // EMM7H
+ LLDB_INVALID_REGNUM, // MM00
+ LLDB_INVALID_REGNUM, // MM01
+ LLDB_INVALID_REGNUM, // MM10
+ LLDB_INVALID_REGNUM, // MM11
+ LLDB_INVALID_REGNUM, // MM20
+ LLDB_INVALID_REGNUM, // MM21
+ LLDB_INVALID_REGNUM, // MM30
+ LLDB_INVALID_REGNUM, // MM31
+ LLDB_INVALID_REGNUM, // MM40
+ LLDB_INVALID_REGNUM, // MM41
+ LLDB_INVALID_REGNUM, // MM50
+ LLDB_INVALID_REGNUM, // MM51
+ LLDB_INVALID_REGNUM, // MM60
+ LLDB_INVALID_REGNUM, // MM61
+ LLDB_INVALID_REGNUM, // MM70
+ LLDB_INVALID_REGNUM, // MM71
+ lldb_xmm8_x86_64, // XMM8
+ lldb_xmm9_x86_64, // XMM9
+ lldb_xmm10_x86_64, // XMM10
+ lldb_xmm11_x86_64, // XMM11
+ lldb_xmm12_x86_64, // XMM12
+ lldb_xmm13_x86_64, // XMM13
+ lldb_xmm14_x86_64, // XMM14
+ lldb_xmm15_x86_64, // XMM15
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM,
+ lldb_sil_x86_64, // SIL
+ lldb_dil_x86_64, // DIL
+ lldb_bpl_x86_64, // BPL
+ lldb_spl_x86_64, // SPL
+ lldb_rax_x86_64, // RAX
+ lldb_rbx_x86_64, // RBX
+ lldb_rcx_x86_64, // RCX
+ lldb_rdx_x86_64, // RDX
+ lldb_rsi_x86_64, // RSI
+ lldb_rdi_x86_64, // RDI
+ lldb_rbp_x86_64, // RBP
+ lldb_rsp_x86_64, // RSP
+ lldb_r8_x86_64, // R8
+ lldb_r9_x86_64, // R9
+ lldb_r10_x86_64, // R10
+ lldb_r11_x86_64, // R11
+ lldb_r12_x86_64, // R12
+ lldb_r13_x86_64, // R13
+ lldb_r14_x86_64, // R14
+ lldb_r15_x86_64, // R15
+ lldb_r8l_x86_64, // R8B
+ lldb_r9l_x86_64, // R9B
+ lldb_r10l_x86_64, // R10B
+ lldb_r11l_x86_64, // R11B
+ lldb_r12l_x86_64, // R12B
+ lldb_r13l_x86_64, // R13B
+ lldb_r14l_x86_64, // R14B
+ lldb_r15l_x86_64, // R15B
+ lldb_r8w_x86_64, // R8W
+ lldb_r9w_x86_64, // R9W
+ lldb_r10w_x86_64, // R10W
+ lldb_r11w_x86_64, // R11W
+ lldb_r12w_x86_64, // R12W
+ lldb_r13w_x86_64, // R13W
+ lldb_r14w_x86_64, // R14W
+ lldb_r15w_x86_64, // R15W
+ lldb_r8d_x86_64, // R8D
+ lldb_r9d_x86_64, // R9D
+ lldb_r10d_x86_64, // R10D
+ lldb_r11d_x86_64, // R11D
+ lldb_r12d_x86_64, // R12D
+ lldb_r13d_x86_64, // R13D
+ lldb_r14d_x86_64, // R14D
+ lldb_r15d_x86_64, // R15D
+ lldb_ymm0_x86_64, // AMD64_YMM0
+ lldb_ymm1_x86_64, // AMD64_YMM1
+ lldb_ymm2_x86_64, // AMD64_YMM2
+ lldb_ymm3_x86_64, // AMD64_YMM3
+ lldb_ymm4_x86_64, // AMD64_YMM4
+ lldb_ymm5_x86_64, // AMD64_YMM5
+ lldb_ymm6_x86_64, // AMD64_YMM6
+ lldb_ymm7_x86_64, // AMD64_YMM7
+ lldb_ymm8_x86_64, // AMD64_YMM8
+ lldb_ymm9_x86_64, // AMD64_YMM9
+ lldb_ymm10_x86_64, // AMD64_YMM10
+ lldb_ymm11_x86_64, // AMD64_YMM11
+ lldb_ymm12_x86_64, // AMD64_YMM12
+ lldb_ymm13_x86_64, // AMD64_YMM13
+ lldb_ymm14_x86_64, // AMD64_YMM14
+ lldb_ymm15_x86_64, // AMD64_YMM15
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ lldb_bnd0_x86_64, // BND0
+ lldb_bnd1_x86_64, // BND1
+ lldb_bnd2_x86_64 // BND2
+};
+
+uint32_t lldb_private::npdb::GetLLDBRegisterNumber(
+ llvm::Triple::ArchType arch_type, llvm::codeview::RegisterId register_id) {
+ switch (arch_type) {
+ case llvm::Triple::x86:
+ if (static_cast<uint16_t>(register_id) <
+ sizeof(g_code_view_to_lldb_registers_x86) /
+ sizeof(g_code_view_to_lldb_registers_x86[0]))
+ return g_code_view_to_lldb_registers_x86[static_cast<uint16_t>(
+ register_id)];
+
+ switch (register_id) {
+ case llvm::codeview::RegisterId::MXCSR:
+ return lldb_mxcsr_i386;
+ case llvm::codeview::RegisterId::BND0:
+ return lldb_bnd0_i386;
+ case llvm::codeview::RegisterId::BND1:
+ return lldb_bnd1_i386;
+ case llvm::codeview::RegisterId::BND2:
+ return lldb_bnd2_i386;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+ case llvm::Triple::x86_64:
+ if (static_cast<uint16_t>(register_id) <
+ sizeof(g_code_view_to_lldb_registers_x86_64) /
+ sizeof(g_code_view_to_lldb_registers_x86_64[0]))
+ return g_code_view_to_lldb_registers_x86_64[static_cast<uint16_t>(
+ register_id)];
+
+ return LLDB_INVALID_REGNUM;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h
new file mode 100644
index 000000000000..b1c31e0c1785
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h
@@ -0,0 +1,24 @@
+//===-- CodeViewRegisterMapping.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_Plugins_SymbolFile_PDB_CodeViewRegisterMapping_h_
+#define lldb_Plugins_SymbolFile_PDB_CodeViewRegisterMapping_h_
+
+#include "llvm/ADT/Triple.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+
+namespace lldb_private {
+namespace npdb {
+
+uint32_t GetLLDBRegisterNumber(llvm::Triple::ArchType arch_type,
+ llvm::codeview::RegisterId register_id);
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp
new file mode 100644
index 000000000000..1838204e4ca6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp
@@ -0,0 +1,225 @@
+//===-- CompileUnitIndex.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CompileUnitIndex.h"
+
+#include "PdbIndex.h"
+#include "PdbUtil.h"
+
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
+#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
+#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/Support/Path.h"
+
+#include "lldb/Utility/LLDBAssert.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static bool IsMainFile(llvm::StringRef main, llvm::StringRef other) {
+ if (main == other)
+ return true;
+
+ // If the files refer to the local file system, we can just ask the file
+ // system if they're equivalent. But if the source isn't present on disk
+ // then we still want to try.
+ if (llvm::sys::fs::equivalent(main, other))
+ return true;
+
+ llvm::SmallString<64> normalized(other);
+ llvm::sys::path::native(normalized);
+ return main.equals_lower(normalized);
+}
+
+static void ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) {
+ cci.m_compile_opts.emplace();
+ llvm::cantFail(
+ SymbolDeserializer::deserializeAs<Compile3Sym>(sym, *cci.m_compile_opts));
+}
+
+static void ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) {
+ cci.m_obj_name.emplace();
+ llvm::cantFail(
+ SymbolDeserializer::deserializeAs<ObjNameSym>(sym, *cci.m_obj_name));
+}
+
+static void ParseBuildInfo(PdbIndex &index, const CVSymbol &sym,
+ CompilandIndexItem &cci) {
+ BuildInfoSym bis(SymbolRecordKind::BuildInfoSym);
+ llvm::cantFail(SymbolDeserializer::deserializeAs<BuildInfoSym>(sym, bis));
+
+ // S_BUILDINFO just points to an LF_BUILDINFO in the IPI stream. Let's do
+ // a little extra work to pull out the LF_BUILDINFO.
+ LazyRandomTypeCollection &types = index.ipi().typeCollection();
+ llvm::Optional<CVType> cvt = types.tryGetType(bis.BuildId);
+
+ if (!cvt || cvt->kind() != LF_BUILDINFO)
+ return;
+
+ BuildInfoRecord bir;
+ llvm::cantFail(TypeDeserializer::deserializeAs<BuildInfoRecord>(*cvt, bir));
+ cci.m_build_info.assign(bir.ArgIndices.begin(), bir.ArgIndices.end());
+}
+
+static void ParseExtendedInfo(PdbIndex &index, CompilandIndexItem &item) {
+ const CVSymbolArray &syms = item.m_debug_stream.getSymbolArray();
+
+ // This is a private function, it shouldn't be called if the information
+ // has already been parsed.
+ lldbassert(!item.m_obj_name);
+ lldbassert(!item.m_compile_opts);
+ lldbassert(item.m_build_info.empty());
+
+ // We're looking for 3 things. S_COMPILE3, S_OBJNAME, and S_BUILDINFO.
+ int found = 0;
+ for (const CVSymbol &sym : syms) {
+ switch (sym.kind()) {
+ case S_COMPILE3:
+ ParseCompile3(sym, item);
+ break;
+ case S_OBJNAME:
+ ParseObjname(sym, item);
+ break;
+ case S_BUILDINFO:
+ ParseBuildInfo(index, sym, item);
+ break;
+ default:
+ continue;
+ }
+ if (++found >= 3)
+ break;
+ }
+}
+
+CompilandIndexItem::CompilandIndexItem(
+ PdbCompilandId id, llvm::pdb::ModuleDebugStreamRef debug_stream,
+ llvm::pdb::DbiModuleDescriptor descriptor)
+ : m_id(id), m_debug_stream(std::move(debug_stream)),
+ m_module_descriptor(std::move(descriptor)) {}
+
+CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) {
+ auto result = m_comp_units.try_emplace(modi, nullptr);
+ if (!result.second)
+ return *result.first->second;
+
+ // Find the module list and load its debug information stream and cache it
+ // since we need to use it for almost all interesting operations.
+ const DbiModuleList &modules = m_index.dbi().modules();
+ llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi);
+ uint16_t stream = descriptor.getModuleStreamIndex();
+ std::unique_ptr<llvm::msf::MappedBlockStream> stream_data =
+ m_index.pdb().createIndexedStream(stream);
+
+
+ std::unique_ptr<CompilandIndexItem>& cci = result.first->second;
+
+ if (!stream_data) {
+ llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, nullptr);
+ cci = llvm::make_unique<CompilandIndexItem>(PdbCompilandId{ modi }, debug_stream, std::move(descriptor));
+ return *cci;
+ }
+
+ llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor,
+ std::move(stream_data));
+
+ cantFail(debug_stream.reload());
+
+ cci = llvm::make_unique<CompilandIndexItem>(
+ PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor));
+ ParseExtendedInfo(m_index, *cci);
+
+ cci->m_strings.initialize(debug_stream.getSubsectionsArray());
+ PDBStringTable &strings = cantFail(m_index.pdb().getStringTable());
+ cci->m_strings.setStrings(strings.getStringTable());
+
+ // We want the main source file to always comes first. Note that we can't
+ // just push_back the main file onto the front because `GetMainSourceFile`
+ // computes it in such a way that it doesn't own the resulting memory. So we
+ // have to iterate the module file list comparing each one to the main file
+ // name until we find it, and we can cache that one since the memory is backed
+ // by a contiguous chunk inside the mapped PDB.
+ llvm::SmallString<64> main_file = GetMainSourceFile(*cci);
+ std::string s = main_file.str();
+ llvm::sys::path::native(main_file);
+
+ uint32_t file_count = modules.getSourceFileCount(modi);
+ cci->m_file_list.reserve(file_count);
+ bool found_main_file = false;
+ for (llvm::StringRef file : modules.source_files(modi)) {
+ if (!found_main_file && IsMainFile(main_file, file)) {
+ cci->m_file_list.insert(cci->m_file_list.begin(), file);
+ found_main_file = true;
+ continue;
+ }
+ cci->m_file_list.push_back(file);
+ }
+
+ return *cci;
+}
+
+const CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) const {
+ auto iter = m_comp_units.find(modi);
+ if (iter == m_comp_units.end())
+ return nullptr;
+ return iter->second.get();
+}
+
+CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) {
+ auto iter = m_comp_units.find(modi);
+ if (iter == m_comp_units.end())
+ return nullptr;
+ return iter->second.get();
+}
+
+llvm::SmallString<64>
+CompileUnitIndex::GetMainSourceFile(const CompilandIndexItem &item) const {
+ // LF_BUILDINFO contains a list of arg indices which point to LF_STRING_ID
+ // records in the IPI stream. The order of the arg indices is as follows:
+ // [0] - working directory where compiler was invoked.
+ // [1] - absolute path to compiler binary
+ // [2] - source file name
+ // [3] - path to compiler generated PDB (the /Zi PDB, although this entry gets
+ // added even when using /Z7)
+ // [4] - full command line invocation.
+ //
+ // We need to form the path [0]\[2] to generate the full path to the main
+ // file.source
+ if (item.m_build_info.size() < 3)
+ return {""};
+
+ LazyRandomTypeCollection &types = m_index.ipi().typeCollection();
+
+ StringIdRecord working_dir;
+ StringIdRecord file_name;
+ CVType dir_cvt = types.getType(item.m_build_info[0]);
+ CVType file_cvt = types.getType(item.m_build_info[2]);
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<StringIdRecord>(dir_cvt, working_dir));
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<StringIdRecord>(file_cvt, file_name));
+
+ llvm::sys::path::Style style = working_dir.String.startswith("/")
+ ? llvm::sys::path::Style::posix
+ : llvm::sys::path::Style::windows;
+ if (llvm::sys::path::is_absolute(file_name.String, style))
+ return file_name.String;
+
+ llvm::SmallString<64> absolute_path = working_dir.String;
+ llvm::sys::path::append(absolute_path, file_name.String);
+ return absolute_path;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h
new file mode 100644
index 000000000000..44a1c8cdd9c2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h
@@ -0,0 +1,94 @@
+//===-- CompileUnitIndex.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILENATIVEPDB_COMPILEUNITINDEX_H
+#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_COMPILEUNITINDEX_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/IntervalMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
+#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+
+#include "PdbSymUid.h"
+
+#include <map>
+#include <memory>
+
+namespace lldb_private {
+
+namespace npdb {
+class PdbIndex;
+
+/// Represents a single compile unit. This class is useful for collecting the
+/// important accessors and information about a compile unit from disparate
+/// parts of the PDB into a single place, simplifying acess to compile unit
+/// information for the callers.
+struct CompilandIndexItem {
+ CompilandIndexItem(PdbCompilandId m_id,
+ llvm::pdb::ModuleDebugStreamRef debug_stream,
+ llvm::pdb::DbiModuleDescriptor descriptor);
+
+ // index of this compile unit.
+ PdbCompilandId m_id;
+
+ // debug stream.
+ llvm::pdb::ModuleDebugStreamRef m_debug_stream;
+
+ // dbi module descriptor.
+ llvm::pdb::DbiModuleDescriptor m_module_descriptor;
+
+ llvm::codeview::StringsAndChecksumsRef m_strings;
+
+ // List of files which contribute to this compiland.
+ std::vector<llvm::StringRef> m_file_list;
+
+ // Maps virtual address to global symbol id, which can then be used to
+ // locate the exact compile unit and offset of the symbol. Note that this
+ // is intentionally an ordered map so that we can find all symbols up to a
+ // given starting address.
+ std::map<lldb::addr_t, PdbSymUid> m_symbols_by_va;
+
+ // S_COMPILE3 sym describing compilation settings for the module.
+ llvm::Optional<llvm::codeview::Compile3Sym> m_compile_opts;
+
+ // S_OBJNAME sym describing object name.
+ llvm::Optional<llvm::codeview::ObjNameSym> m_obj_name;
+
+ // LF_BUILDINFO sym describing source file name, working directory,
+ // command line, etc. This usually contains exactly 5 items which
+ // are references to other strings.
+ llvm::SmallVector<llvm::codeview::TypeIndex, 5> m_build_info;
+};
+
+/// Indexes information about all compile units. This is really just a map of
+/// global compile unit index to |CompilandIndexItem| structures.
+class CompileUnitIndex {
+ PdbIndex &m_index;
+ llvm::DenseMap<uint16_t, std::unique_ptr<CompilandIndexItem>> m_comp_units;
+
+public:
+ explicit CompileUnitIndex(PdbIndex &index) : m_index(index) {}
+
+ CompilandIndexItem &GetOrCreateCompiland(uint16_t modi);
+
+ const CompilandIndexItem *GetCompiland(uint16_t modi) const;
+
+ CompilandIndexItem *GetCompiland(uint16_t modi);
+
+ llvm::SmallString<64> GetMainSourceFile(const CompilandIndexItem &item) const;
+};
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp
new file mode 100644
index 000000000000..3d8bfb058721
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp
@@ -0,0 +1,252 @@
+//===-- DWARFLocationExpression.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DWARFLocationExpression.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StreamBuffer.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBufferHeap.h"
+
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/Support/Endian.h"
+
+#include "PdbUtil.h"
+#include "CodeViewRegisterMapping.h"
+#include "PdbFPOProgramToDWARFExpression.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+uint32_t GetGenericRegisterNumber(llvm::codeview::RegisterId register_id) {
+ if (register_id == llvm::codeview::RegisterId::VFRAME)
+ return LLDB_REGNUM_GENERIC_FP;
+
+ return LLDB_INVALID_REGNUM;
+}
+
+static uint32_t GetRegisterNumber(llvm::Triple::ArchType arch_type,
+ llvm::codeview::RegisterId register_id,
+ RegisterKind &register_kind) {
+ register_kind = eRegisterKindLLDB;
+ uint32_t reg_num = GetLLDBRegisterNumber(arch_type, register_id);
+ if (reg_num != LLDB_INVALID_REGNUM)
+ return reg_num;
+
+ register_kind = eRegisterKindGeneric;
+ return GetGenericRegisterNumber(register_id);
+}
+
+static bool IsSimpleTypeSignedInteger(SimpleTypeKind kind) {
+ switch (kind) {
+ case SimpleTypeKind::Int128:
+ case SimpleTypeKind::Int64:
+ case SimpleTypeKind::Int64Quad:
+ case SimpleTypeKind::Int32:
+ case SimpleTypeKind::Int32Long:
+ case SimpleTypeKind::Int16:
+ case SimpleTypeKind::Int16Short:
+ case SimpleTypeKind::Float128:
+ case SimpleTypeKind::Float80:
+ case SimpleTypeKind::Float64:
+ case SimpleTypeKind::Float32:
+ case SimpleTypeKind::Float16:
+ case SimpleTypeKind::NarrowCharacter:
+ case SimpleTypeKind::SignedCharacter:
+ case SimpleTypeKind::SByte:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static std::pair<size_t, bool> GetIntegralTypeInfo(TypeIndex ti,
+ TpiStream &tpi) {
+ if (ti.isSimple()) {
+ SimpleTypeKind stk = ti.getSimpleKind();
+ return {GetTypeSizeForSimpleKind(stk), IsSimpleTypeSignedInteger(stk)};
+ }
+
+ CVType cvt = tpi.getType(ti);
+ switch (cvt.kind()) {
+ case LF_MODIFIER: {
+ ModifierRecord mfr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ModifierRecord>(cvt, mfr));
+ return GetIntegralTypeInfo(mfr.ModifiedType, tpi);
+ }
+ case LF_POINTER: {
+ PointerRecord pr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<PointerRecord>(cvt, pr));
+ return GetIntegralTypeInfo(pr.ReferentType, tpi);
+ }
+ case LF_ENUM: {
+ EnumRecord er;
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
+ return GetIntegralTypeInfo(er.UnderlyingType, tpi);
+ }
+ default:
+ assert(false && "Type is not integral!");
+ return {0, false};
+ }
+}
+
+template <typename StreamWriter>
+static DWARFExpression MakeLocationExpressionInternal(lldb::ModuleSP module,
+ StreamWriter &&writer) {
+ const ArchSpec &architecture = module->GetArchitecture();
+ ByteOrder byte_order = architecture.GetByteOrder();
+ uint32_t address_size = architecture.GetAddressByteSize();
+ uint32_t byte_size = architecture.GetDataByteSize();
+ if (byte_order == eByteOrderInvalid || address_size == 0)
+ return DWARFExpression();
+
+ RegisterKind register_kind = eRegisterKindDWARF;
+ StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order);
+
+ if (!writer(stream, register_kind))
+ return DWARFExpression();
+
+ DataBufferSP buffer =
+ std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize());
+ DataExtractor extractor(buffer, byte_order, address_size, byte_size);
+ DWARFExpression result(module, extractor, nullptr, 0, buffer->GetByteSize());
+ result.SetRegisterKind(register_kind);
+
+ return result;
+}
+
+static DWARFExpression MakeRegisterBasedLocationExpressionInternal(
+ llvm::codeview::RegisterId reg, llvm::Optional<int32_t> relative_offset,
+ lldb::ModuleSP module) {
+ return MakeLocationExpressionInternal(
+ module, [&](Stream &stream, RegisterKind &register_kind) -> bool {
+ uint32_t reg_num = GetRegisterNumber(
+ module->GetArchitecture().GetMachine(), reg, register_kind);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return false;
+
+ if (reg_num > 31) {
+ llvm::dwarf::LocationAtom base = relative_offset
+ ? llvm::dwarf::DW_OP_bregx
+ : llvm::dwarf::DW_OP_regx;
+ stream.PutHex8(base);
+ stream.PutULEB128(reg_num);
+ } else {
+ llvm::dwarf::LocationAtom base = relative_offset
+ ? llvm::dwarf::DW_OP_breg0
+ : llvm::dwarf::DW_OP_reg0;
+ stream.PutHex8(base + reg_num);
+ }
+
+ if (relative_offset)
+ stream.PutSLEB128(*relative_offset);
+
+ return true;
+ });
+}
+
+DWARFExpression lldb_private::npdb::MakeEnregisteredLocationExpression(
+ llvm::codeview::RegisterId reg, lldb::ModuleSP module) {
+ return MakeRegisterBasedLocationExpressionInternal(reg, llvm::None, module);
+}
+
+DWARFExpression lldb_private::npdb::MakeRegRelLocationExpression(
+ llvm::codeview::RegisterId reg, int32_t offset, lldb::ModuleSP module) {
+ return MakeRegisterBasedLocationExpressionInternal(reg, offset, module);
+}
+
+static bool EmitVFrameEvaluationDWARFExpression(
+ llvm::StringRef program, llvm::Triple::ArchType arch_type, Stream &stream) {
+ // VFrame value always stored in $TO pseudo-register
+ return TranslateFPOProgramToDWARFExpression(program, "$T0", arch_type,
+ stream);
+}
+
+DWARFExpression lldb_private::npdb::MakeVFrameRelLocationExpression(
+ llvm::StringRef fpo_program, int32_t offset, lldb::ModuleSP module) {
+ return MakeLocationExpressionInternal(
+ module, [&](Stream &stream, RegisterKind &register_kind) -> bool {
+ const ArchSpec &architecture = module->GetArchitecture();
+
+ if (!EmitVFrameEvaluationDWARFExpression(fpo_program, architecture.GetMachine(),
+ stream))
+ return false;
+
+ stream.PutHex8(llvm::dwarf::DW_OP_consts);
+ stream.PutSLEB128(offset);
+ stream.PutHex8(llvm::dwarf::DW_OP_plus);
+
+ register_kind = eRegisterKindLLDB;
+
+ return true;
+ });
+}
+
+DWARFExpression lldb_private::npdb::MakeGlobalLocationExpression(
+ uint16_t section, uint32_t offset, ModuleSP module) {
+ assert(section > 0);
+ assert(module);
+
+ return MakeLocationExpressionInternal(
+ module, [&](Stream &stream, RegisterKind &register_kind) -> bool {
+ stream.PutHex8(llvm::dwarf::DW_OP_addr);
+
+ SectionList *section_list = module->GetSectionList();
+ assert(section_list);
+
+ auto section_ptr = section_list->FindSectionByID(section);
+ if (!section_ptr)
+ return false;
+
+ stream.PutMaxHex64(section_ptr->GetFileAddress() + offset,
+ stream.GetAddressByteSize(), stream.GetByteOrder());
+
+ return true;
+ });
+}
+
+DWARFExpression lldb_private::npdb::MakeConstantLocationExpression(
+ TypeIndex underlying_ti, TpiStream &tpi, const llvm::APSInt &constant,
+ ModuleSP module) {
+ const ArchSpec &architecture = module->GetArchitecture();
+ uint32_t address_size = architecture.GetAddressByteSize();
+
+ size_t size = 0;
+ bool is_signed = false;
+ std::tie(size, is_signed) = GetIntegralTypeInfo(underlying_ti, tpi);
+
+ union {
+ llvm::support::little64_t I;
+ llvm::support::ulittle64_t U;
+ } Value;
+
+ std::shared_ptr<DataBufferHeap> buffer = std::make_shared<DataBufferHeap>();
+ buffer->SetByteSize(size);
+
+ llvm::ArrayRef<uint8_t> bytes;
+ if (is_signed) {
+ Value.I = constant.getSExtValue();
+ } else {
+ Value.U = constant.getZExtValue();
+ }
+
+ bytes = llvm::makeArrayRef(reinterpret_cast<const uint8_t *>(&Value), 8)
+ .take_front(size);
+ buffer->CopyData(bytes.data(), size);
+ DataExtractor extractor(buffer, lldb::eByteOrderLittle, address_size);
+ DWARFExpression result(nullptr, extractor, nullptr, 0, size);
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h
new file mode 100644
index 000000000000..c37d715babdc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h
@@ -0,0 +1,44 @@
+//===-- DWARFLocationExpression.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_DWARFLOCATIONEXPRESSION_H
+#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_DWARFLOCATIONEXPRESSION_H
+
+#include "lldb/lldb-forward.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+
+namespace llvm {
+class APSInt;
+namespace codeview {
+class TypeIndex;
+}
+namespace pdb {
+class TpiStream;
+}
+} // namespace llvm
+namespace lldb_private {
+namespace npdb {
+DWARFExpression
+MakeEnregisteredLocationExpression(llvm::codeview::RegisterId reg,
+ lldb::ModuleSP module);
+
+DWARFExpression MakeRegRelLocationExpression(llvm::codeview::RegisterId reg,
+ int32_t offset,
+ lldb::ModuleSP module);
+DWARFExpression MakeVFrameRelLocationExpression(llvm::StringRef fpo_program,
+ int32_t offset,
+ lldb::ModuleSP module);
+DWARFExpression MakeGlobalLocationExpression(uint16_t section, uint32_t offset,
+ lldb::ModuleSP module);
+DWARFExpression MakeConstantLocationExpression(
+ llvm::codeview::TypeIndex underlying_ti, llvm::pdb::TpiStream &tpi,
+ const llvm::APSInt &constant, lldb::ModuleSP module);
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
new file mode 100644
index 000000000000..4991be8e70ce
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
@@ -0,0 +1,1366 @@
+#include "PdbAstBuilder.h"
+
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/RecordName.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
+#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/Demangle/MicrosoftDemangle.h"
+
+#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/LLDBAssert.h"
+
+#include "PdbUtil.h"
+#include "UdtRecordCompleter.h"
+
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static llvm::Optional<PdbCompilandSymId> FindSymbolScope(PdbIndex &index,
+ PdbCompilandSymId id) {
+ CVSymbol sym = index.ReadSymbolRecord(id);
+ if (symbolOpensScope(sym.kind())) {
+ // If this exact symbol opens a scope, we can just directly access its
+ // parent.
+ id.offset = getScopeParentOffset(sym);
+ // Global symbols have parent offset of 0. Return llvm::None to indicate
+ // this.
+ if (id.offset == 0)
+ return llvm::None;
+ return id;
+ }
+
+ // Otherwise we need to start at the beginning and iterate forward until we
+ // reach (or pass) this particular symbol
+ CompilandIndexItem &cii = index.compilands().GetOrCreateCompiland(id.modi);
+ const CVSymbolArray &syms = cii.m_debug_stream.getSymbolArray();
+
+ auto begin = syms.begin();
+ auto end = syms.at(id.offset);
+ std::vector<PdbCompilandSymId> scope_stack;
+
+ while (begin != end) {
+ if (id.offset == begin.offset()) {
+ // We have a match! Return the top of the stack
+ if (scope_stack.empty())
+ return llvm::None;
+ return scope_stack.back();
+ }
+ if (begin.offset() > id.offset) {
+ // We passed it. We couldn't even find this symbol record.
+ lldbassert(false && "Invalid compiland symbol id!");
+ return llvm::None;
+ }
+
+ // We haven't found the symbol yet. Check if we need to open or close the
+ // scope stack.
+ if (symbolOpensScope(begin->kind())) {
+ // We can use the end offset of the scope to determine whether or not
+ // we can just outright skip this entire scope.
+ uint32_t scope_end = getScopeEndOffset(*begin);
+ if (scope_end < id.modi) {
+ begin = syms.at(scope_end);
+ } else {
+ // The symbol we're looking for is somewhere in this scope.
+ scope_stack.emplace_back(id.modi, begin.offset());
+ }
+ } else if (symbolEndsScope(begin->kind())) {
+ scope_stack.pop_back();
+ }
+ ++begin;
+ }
+
+ return llvm::None;
+}
+
+static clang::TagTypeKind TranslateUdtKind(const TagRecord &cr) {
+ switch (cr.Kind) {
+ case TypeRecordKind::Class:
+ return clang::TTK_Class;
+ case TypeRecordKind::Struct:
+ return clang::TTK_Struct;
+ case TypeRecordKind::Union:
+ return clang::TTK_Union;
+ case TypeRecordKind::Interface:
+ return clang::TTK_Interface;
+ case TypeRecordKind::Enum:
+ return clang::TTK_Enum;
+ default:
+ lldbassert(false && "Invalid tag record kind!");
+ return clang::TTK_Struct;
+ }
+}
+
+static bool IsCVarArgsFunction(llvm::ArrayRef<TypeIndex> args) {
+ if (args.empty())
+ return false;
+ return args.back() == TypeIndex::None();
+}
+
+static bool
+AnyScopesHaveTemplateParams(llvm::ArrayRef<llvm::ms_demangle::Node *> scopes) {
+ for (llvm::ms_demangle::Node *n : scopes) {
+ auto *idn = static_cast<llvm::ms_demangle::IdentifierNode *>(n);
+ if (idn->TemplateParams)
+ return true;
+ }
+ return false;
+}
+
+static ClangASTContext &GetClangASTContext(ObjectFile &obj) {
+ TypeSystem *ts =
+ obj.GetModule()->GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus);
+ lldbassert(ts);
+ return static_cast<ClangASTContext &>(*ts);
+}
+
+static llvm::Optional<clang::CallingConv>
+TranslateCallingConvention(llvm::codeview::CallingConvention conv) {
+ using CC = llvm::codeview::CallingConvention;
+ switch (conv) {
+
+ case CC::NearC:
+ case CC::FarC:
+ return clang::CallingConv::CC_C;
+ case CC::NearPascal:
+ case CC::FarPascal:
+ return clang::CallingConv::CC_X86Pascal;
+ case CC::NearFast:
+ case CC::FarFast:
+ return clang::CallingConv::CC_X86FastCall;
+ case CC::NearStdCall:
+ case CC::FarStdCall:
+ return clang::CallingConv::CC_X86StdCall;
+ case CC::ThisCall:
+ return clang::CallingConv::CC_X86ThisCall;
+ case CC::NearVector:
+ return clang::CallingConv::CC_X86VectorCall;
+ default:
+ return llvm::None;
+ }
+}
+
+static llvm::Optional<CVTagRecord>
+GetNestedTagDefinition(const NestedTypeRecord &Record,
+ const CVTagRecord &parent, TpiStream &tpi) {
+ // An LF_NESTTYPE is essentially a nested typedef / using declaration, but it
+ // is also used to indicate the primary definition of a nested class. That is
+ // to say, if you have:
+ // struct A {
+ // struct B {};
+ // using C = B;
+ // };
+ // Then in the debug info, this will appear as:
+ // LF_STRUCTURE `A::B` [type index = N]
+ // LF_STRUCTURE `A`
+ // LF_NESTTYPE [name = `B`, index = N]
+ // LF_NESTTYPE [name = `C`, index = N]
+ // In order to accurately reconstruct the decl context hierarchy, we need to
+ // know which ones are actual definitions and which ones are just aliases.
+
+ // If it's a simple type, then this is something like `using foo = int`.
+ if (Record.Type.isSimple())
+ return llvm::None;
+
+ CVType cvt = tpi.getType(Record.Type);
+
+ if (!IsTagRecord(cvt))
+ return llvm::None;
+
+ // If it's an inner definition, then treat whatever name we have here as a
+ // single component of a mangled name. So we can inject it into the parent's
+ // mangled name to see if it matches.
+ CVTagRecord child = CVTagRecord::create(cvt);
+ std::string qname = parent.asTag().getUniqueName();
+ if (qname.size() < 4 || child.asTag().getUniqueName().size() < 4)
+ return llvm::None;
+
+ // qname[3] is the tag type identifier (struct, class, union, etc). Since the
+ // inner tag type is not necessarily the same as the outer tag type, re-write
+ // it to match the inner tag type.
+ qname[3] = child.asTag().getUniqueName()[3];
+ std::string piece;
+ if (qname[3] == 'W')
+ piece = "4";
+ piece += Record.Name;
+ piece.push_back('@');
+ qname.insert(4, std::move(piece));
+ if (qname != child.asTag().UniqueName)
+ return llvm::None;
+
+ return std::move(child);
+}
+
+static bool IsAnonymousNamespaceName(llvm::StringRef name) {
+ return name == "`anonymous namespace'" || name == "`anonymous-namespace'";
+}
+
+PdbAstBuilder::PdbAstBuilder(ObjectFile &obj, PdbIndex &index)
+ : m_index(index), m_clang(GetClangASTContext(obj)) {
+ BuildParentMap();
+}
+
+lldb_private::CompilerDeclContext PdbAstBuilder::GetTranslationUnitDecl() {
+ return ToCompilerDeclContext(*m_clang.GetTranslationUnitDecl());
+}
+
+std::pair<clang::DeclContext *, std::string>
+PdbAstBuilder::CreateDeclInfoForType(const TagRecord &record, TypeIndex ti) {
+ // FIXME: Move this to GetDeclContextContainingUID.
+ if (!record.hasUniqueName())
+ return CreateDeclInfoForUndecoratedName(record.Name);
+
+ llvm::ms_demangle::Demangler demangler;
+ StringView sv(record.UniqueName.begin(), record.UniqueName.size());
+ llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv);
+ if (demangler.Error)
+ return {m_clang.GetTranslationUnitDecl(), record.UniqueName};
+
+ llvm::ms_demangle::IdentifierNode *idn =
+ ttn->QualifiedName->getUnqualifiedIdentifier();
+ std::string uname = idn->toString(llvm::ms_demangle::OF_NoTagSpecifier);
+
+ llvm::ms_demangle::NodeArrayNode *name_components =
+ ttn->QualifiedName->Components;
+ llvm::ArrayRef<llvm::ms_demangle::Node *> scopes(name_components->Nodes,
+ name_components->Count - 1);
+
+ clang::DeclContext *context = m_clang.GetTranslationUnitDecl();
+
+ // If this type doesn't have a parent type in the debug info, then the best we
+ // can do is to say that it's either a series of namespaces (if the scope is
+ // non-empty), or the translation unit (if the scope is empty).
+ auto parent_iter = m_parent_types.find(ti);
+ if (parent_iter == m_parent_types.end()) {
+ if (scopes.empty())
+ return {context, uname};
+
+ // If there is no parent in the debug info, but some of the scopes have
+ // template params, then this is a case of bad debug info. See, for
+ // example, llvm.org/pr39607. We don't want to create an ambiguity between
+ // a NamespaceDecl and a CXXRecordDecl, so instead we create a class at
+ // global scope with the fully qualified name.
+ if (AnyScopesHaveTemplateParams(scopes))
+ return {context, record.Name};
+
+ for (llvm::ms_demangle::Node *scope : scopes) {
+ auto *nii = static_cast<llvm::ms_demangle::NamedIdentifierNode *>(scope);
+ std::string str = nii->toString();
+ context = GetOrCreateNamespaceDecl(str.c_str(), *context);
+ }
+ return {context, uname};
+ }
+
+ // Otherwise, all we need to do is get the parent type of this type and
+ // recurse into our lazy type creation / AST reconstruction logic to get an
+ // LLDB TypeSP for the parent. This will cause the AST to automatically get
+ // the right DeclContext created for any parent.
+ clang::QualType parent_qt = GetOrCreateType(parent_iter->second);
+
+ context = clang::TagDecl::castToDeclContext(parent_qt->getAsTagDecl());
+ return {context, uname};
+}
+
+void PdbAstBuilder::BuildParentMap() {
+ LazyRandomTypeCollection &types = m_index.tpi().typeCollection();
+
+ llvm::DenseMap<TypeIndex, TypeIndex> forward_to_full;
+ llvm::DenseMap<TypeIndex, TypeIndex> full_to_forward;
+
+ struct RecordIndices {
+ TypeIndex forward;
+ TypeIndex full;
+ };
+
+ llvm::StringMap<RecordIndices> record_indices;
+
+ for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) {
+ CVType type = types.getType(*ti);
+ if (!IsTagRecord(type))
+ continue;
+
+ CVTagRecord tag = CVTagRecord::create(type);
+
+ RecordIndices &indices = record_indices[tag.asTag().getUniqueName()];
+ if (tag.asTag().isForwardRef())
+ indices.forward = *ti;
+ else
+ indices.full = *ti;
+
+ if (indices.full != TypeIndex::None() &&
+ indices.forward != TypeIndex::None()) {
+ forward_to_full[indices.forward] = indices.full;
+ full_to_forward[indices.full] = indices.forward;
+ }
+
+ // We're looking for LF_NESTTYPE records in the field list, so ignore
+ // forward references (no field list), and anything without a nested class
+ // (since there won't be any LF_NESTTYPE records).
+ if (tag.asTag().isForwardRef() || !tag.asTag().containsNestedClass())
+ continue;
+
+ struct ProcessTpiStream : public TypeVisitorCallbacks {
+ ProcessTpiStream(PdbIndex &index, TypeIndex parent,
+ const CVTagRecord &parent_cvt,
+ llvm::DenseMap<TypeIndex, TypeIndex> &parents)
+ : index(index), parents(parents), parent(parent),
+ parent_cvt(parent_cvt) {}
+
+ PdbIndex &index;
+ llvm::DenseMap<TypeIndex, TypeIndex> &parents;
+
+ unsigned unnamed_type_index = 1;
+ TypeIndex parent;
+ const CVTagRecord &parent_cvt;
+
+ llvm::Error visitKnownMember(CVMemberRecord &CVR,
+ NestedTypeRecord &Record) override {
+ std::string unnamed_type_name;
+ if (Record.Name.empty()) {
+ unnamed_type_name =
+ llvm::formatv("<unnamed-type-$S{0}>", unnamed_type_index).str();
+ Record.Name = unnamed_type_name;
+ ++unnamed_type_index;
+ }
+ llvm::Optional<CVTagRecord> tag =
+ GetNestedTagDefinition(Record, parent_cvt, index.tpi());
+ if (!tag)
+ return llvm::ErrorSuccess();
+
+ parents[Record.Type] = parent;
+ return llvm::ErrorSuccess();
+ }
+ };
+
+ CVType field_list = m_index.tpi().getType(tag.asTag().FieldList);
+ ProcessTpiStream process(m_index, *ti, tag, m_parent_types);
+ llvm::Error error = visitMemberRecordStream(field_list.data(), process);
+ if (error)
+ llvm::consumeError(std::move(error));
+ }
+
+ // Now that we know the forward -> full mapping of all type indices, we can
+ // re-write all the indices. At the end of this process, we want a mapping
+ // consisting of fwd -> full and full -> full for all child -> parent indices.
+ // We can re-write the values in place, but for the keys, we must save them
+ // off so that we don't modify the map in place while also iterating it.
+ std::vector<TypeIndex> full_keys;
+ std::vector<TypeIndex> fwd_keys;
+ for (auto &entry : m_parent_types) {
+ TypeIndex key = entry.first;
+ TypeIndex value = entry.second;
+
+ auto iter = forward_to_full.find(value);
+ if (iter != forward_to_full.end())
+ entry.second = iter->second;
+
+ iter = forward_to_full.find(key);
+ if (iter != forward_to_full.end())
+ fwd_keys.push_back(key);
+ else
+ full_keys.push_back(key);
+ }
+ for (TypeIndex fwd : fwd_keys) {
+ TypeIndex full = forward_to_full[fwd];
+ m_parent_types[full] = m_parent_types[fwd];
+ }
+ for (TypeIndex full : full_keys) {
+ TypeIndex fwd = full_to_forward[full];
+ m_parent_types[fwd] = m_parent_types[full];
+ }
+
+ // Now that
+}
+
+static bool isLocalVariableType(SymbolKind K) {
+ switch (K) {
+ case S_REGISTER:
+ case S_REGREL32:
+ case S_LOCAL:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static std::string
+RenderScopeList(llvm::ArrayRef<llvm::ms_demangle::Node *> nodes) {
+ lldbassert(!nodes.empty());
+
+ std::string result = nodes.front()->toString();
+ nodes = nodes.drop_front();
+ while (!nodes.empty()) {
+ result += "::";
+ result += nodes.front()->toString(llvm::ms_demangle::OF_NoTagSpecifier);
+ nodes = nodes.drop_front();
+ }
+ return result;
+}
+
+static llvm::Optional<PublicSym32> FindPublicSym(const SegmentOffset &addr,
+ SymbolStream &syms,
+ PublicsStream &publics) {
+ llvm::FixedStreamArray<ulittle32_t> addr_map = publics.getAddressMap();
+ auto iter = std::lower_bound(
+ addr_map.begin(), addr_map.end(), addr,
+ [&](const ulittle32_t &x, const SegmentOffset &y) {
+ CVSymbol s1 = syms.readRecord(x);
+ lldbassert(s1.kind() == S_PUB32);
+ PublicSym32 p1;
+ llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(s1, p1));
+ if (p1.Segment < y.segment)
+ return true;
+ return p1.Offset < y.offset;
+ });
+ if (iter == addr_map.end())
+ return llvm::None;
+ CVSymbol sym = syms.readRecord(*iter);
+ lldbassert(sym.kind() == S_PUB32);
+ PublicSym32 p;
+ llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(sym, p));
+ if (p.Segment == addr.segment && p.Offset == addr.offset)
+ return p;
+ return llvm::None;
+}
+
+clang::Decl *PdbAstBuilder::GetOrCreateSymbolForId(PdbCompilandSymId id) {
+ CVSymbol cvs = m_index.ReadSymbolRecord(id);
+
+ if (isLocalVariableType(cvs.kind())) {
+ clang::DeclContext *scope = GetParentDeclContext(id);
+ clang::Decl *scope_decl = clang::Decl::castFromDeclContext(scope);
+ PdbCompilandSymId scope_id(id.modi, m_decl_to_status[scope_decl].uid);
+ return GetOrCreateVariableDecl(scope_id, id);
+ }
+
+ switch (cvs.kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ return GetOrCreateFunctionDecl(id);
+ case S_GDATA32:
+ case S_LDATA32:
+ case S_GTHREAD32:
+ case S_CONSTANT:
+ // global variable
+ return nullptr;
+ case S_BLOCK32:
+ return GetOrCreateBlockDecl(id);
+ default:
+ return nullptr;
+ }
+}
+
+clang::Decl *PdbAstBuilder::GetOrCreateDeclForUid(PdbSymUid uid) {
+ if (clang::Decl *result = TryGetDecl(uid))
+ return result;
+
+ clang::Decl *result = nullptr;
+ switch (uid.kind()) {
+ case PdbSymUidKind::CompilandSym:
+ result = GetOrCreateSymbolForId(uid.asCompilandSym());
+ break;
+ case PdbSymUidKind::Type: {
+ clang::QualType qt = GetOrCreateType(uid.asTypeSym());
+ if (auto *tag = qt->getAsTagDecl()) {
+ result = tag;
+ break;
+ }
+ return nullptr;
+ }
+ default:
+ return nullptr;
+ }
+ m_uid_to_decl[toOpaqueUid(uid)] = result;
+ return result;
+}
+
+clang::DeclContext *PdbAstBuilder::GetOrCreateDeclContextForUid(PdbSymUid uid) {
+ if (uid.kind() == PdbSymUidKind::CompilandSym) {
+ if (uid.asCompilandSym().offset == 0)
+ return FromCompilerDeclContext(GetTranslationUnitDecl());
+ }
+
+ clang::Decl *decl = GetOrCreateDeclForUid(uid);
+ if (!decl)
+ return nullptr;
+
+ return clang::Decl::castToDeclContext(decl);
+}
+
+std::pair<clang::DeclContext *, std::string>
+PdbAstBuilder::CreateDeclInfoForUndecoratedName(llvm::StringRef name) {
+ MSVCUndecoratedNameParser parser(name);
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers();
+
+ auto context = FromCompilerDeclContext(GetTranslationUnitDecl());
+
+ llvm::StringRef uname = specs.back().GetBaseName();
+ specs = specs.drop_back();
+ if (specs.empty())
+ return {context, name};
+
+ llvm::StringRef scope_name = specs.back().GetFullName();
+
+ // It might be a class name, try that first.
+ std::vector<TypeIndex> types = m_index.tpi().findRecordsByName(scope_name);
+ while (!types.empty()) {
+ clang::QualType qt = GetOrCreateType(types.back());
+ clang::TagDecl *tag = qt->getAsTagDecl();
+ if (tag)
+ return {clang::TagDecl::castToDeclContext(tag), uname};
+ types.pop_back();
+ }
+
+ // If that fails, treat it as a series of namespaces.
+ for (const MSVCUndecoratedNameSpecifier &spec : specs) {
+ std::string ns_name = spec.GetBaseName().str();
+ context = GetOrCreateNamespaceDecl(ns_name.c_str(), *context);
+ }
+ return {context, uname};
+}
+
+clang::DeclContext *
+PdbAstBuilder::GetParentDeclContextForSymbol(const CVSymbol &sym) {
+ if (!SymbolHasAddress(sym))
+ return CreateDeclInfoForUndecoratedName(getSymbolName(sym)).first;
+ SegmentOffset addr = GetSegmentAndOffset(sym);
+ llvm::Optional<PublicSym32> pub =
+ FindPublicSym(addr, m_index.symrecords(), m_index.publics());
+ if (!pub)
+ return CreateDeclInfoForUndecoratedName(getSymbolName(sym)).first;
+
+ llvm::ms_demangle::Demangler demangler;
+ StringView name{pub->Name.begin(), pub->Name.size()};
+ llvm::ms_demangle::SymbolNode *node = demangler.parse(name);
+ if (!node)
+ return FromCompilerDeclContext(GetTranslationUnitDecl());
+ llvm::ArrayRef<llvm::ms_demangle::Node *> name_components{
+ node->Name->Components->Nodes, node->Name->Components->Count - 1};
+
+ if (!name_components.empty()) {
+ // Render the current list of scope nodes as a fully qualified name, and
+ // look it up in the debug info as a type name. If we find something,
+ // this is a type (which may itself be prefixed by a namespace). If we
+ // don't, this is a list of namespaces.
+ std::string qname = RenderScopeList(name_components);
+ std::vector<TypeIndex> matches = m_index.tpi().findRecordsByName(qname);
+ while (!matches.empty()) {
+ clang::QualType qt = GetOrCreateType(matches.back());
+ clang::TagDecl *tag = qt->getAsTagDecl();
+ if (tag)
+ return clang::TagDecl::castToDeclContext(tag);
+ matches.pop_back();
+ }
+ }
+
+ // It's not a type. It must be a series of namespaces.
+ auto context = FromCompilerDeclContext(GetTranslationUnitDecl());
+ while (!name_components.empty()) {
+ std::string ns = name_components.front()->toString();
+ context = GetOrCreateNamespaceDecl(ns.c_str(), *context);
+ name_components = name_components.drop_front();
+ }
+ return context;
+}
+
+clang::DeclContext *PdbAstBuilder::GetParentDeclContext(PdbSymUid uid) {
+ // We must do this *without* calling GetOrCreate on the current uid, as
+ // that would be an infinite recursion.
+ switch (uid.kind()) {
+ case PdbSymUidKind::CompilandSym: {
+ llvm::Optional<PdbCompilandSymId> scope =
+ FindSymbolScope(m_index, uid.asCompilandSym());
+ if (scope)
+ return GetOrCreateDeclContextForUid(*scope);
+
+ CVSymbol sym = m_index.ReadSymbolRecord(uid.asCompilandSym());
+ return GetParentDeclContextForSymbol(sym);
+ }
+ case PdbSymUidKind::Type: {
+ // It could be a namespace, class, or global. We don't support nested
+ // functions yet. Anyway, we just need to consult the parent type map.
+ PdbTypeSymId type_id = uid.asTypeSym();
+ auto iter = m_parent_types.find(type_id.index);
+ if (iter == m_parent_types.end())
+ return FromCompilerDeclContext(GetTranslationUnitDecl());
+ return GetOrCreateDeclContextForUid(PdbTypeSymId(iter->second));
+ }
+ case PdbSymUidKind::FieldListMember:
+ // In this case the parent DeclContext is the one for the class that this
+ // member is inside of.
+ break;
+ case PdbSymUidKind::GlobalSym: {
+ // If this refers to a compiland symbol, just recurse in with that symbol.
+ // The only other possibilities are S_CONSTANT and S_UDT, in which case we
+ // need to parse the undecorated name to figure out the scope, then look
+ // that up in the TPI stream. If it's found, it's a type, othewrise it's
+ // a series of namespaces.
+ // FIXME: do this.
+ CVSymbol global = m_index.ReadSymbolRecord(uid.asGlobalSym());
+ switch (global.kind()) {
+ case SymbolKind::S_GDATA32:
+ case SymbolKind::S_LDATA32:
+ return GetParentDeclContextForSymbol(global);
+ case SymbolKind::S_PROCREF:
+ case SymbolKind::S_LPROCREF: {
+ ProcRefSym ref{global.kind()};
+ llvm::cantFail(
+ SymbolDeserializer::deserializeAs<ProcRefSym>(global, ref));
+ PdbCompilandSymId cu_sym_id{ref.modi(), ref.SymOffset};
+ return GetParentDeclContext(cu_sym_id);
+ }
+ case SymbolKind::S_CONSTANT:
+ case SymbolKind::S_UDT:
+ return CreateDeclInfoForUndecoratedName(getSymbolName(global)).first;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return FromCompilerDeclContext(GetTranslationUnitDecl());
+}
+
+bool PdbAstBuilder::CompleteType(clang::QualType qt) {
+ clang::TagDecl *tag = qt->getAsTagDecl();
+ if (!tag)
+ return false;
+
+ return CompleteTagDecl(*tag);
+}
+
+bool PdbAstBuilder::CompleteTagDecl(clang::TagDecl &tag) {
+ // If this is not in our map, it's an error.
+ auto status_iter = m_decl_to_status.find(&tag);
+ lldbassert(status_iter != m_decl_to_status.end());
+
+ // If it's already complete, just return.
+ DeclStatus &status = status_iter->second;
+ if (status.resolved)
+ return true;
+
+ PdbTypeSymId type_id = PdbSymUid(status.uid).asTypeSym();
+
+ lldbassert(IsTagRecord(type_id, m_index.tpi()));
+
+ clang::QualType tag_qt = m_clang.getASTContext()->getTypeDeclType(&tag);
+ ClangASTContext::SetHasExternalStorage(tag_qt.getAsOpaquePtr(), false);
+
+ TypeIndex tag_ti = type_id.index;
+ CVType cvt = m_index.tpi().getType(tag_ti);
+ if (cvt.kind() == LF_MODIFIER)
+ tag_ti = LookThroughModifierRecord(cvt);
+
+ PdbTypeSymId best_ti = GetBestPossibleDecl(tag_ti, m_index.tpi());
+ cvt = m_index.tpi().getType(best_ti.index);
+ lldbassert(IsTagRecord(cvt));
+
+ if (IsForwardRefUdt(cvt)) {
+ // If we can't find a full decl for this forward ref anywhere in the debug
+ // info, then we have no way to complete it.
+ return false;
+ }
+
+ TypeIndex field_list_ti = GetFieldListIndex(cvt);
+ CVType field_list_cvt = m_index.tpi().getType(field_list_ti);
+ if (field_list_cvt.kind() != LF_FIELDLIST)
+ return false;
+
+ // Visit all members of this class, then perform any finalization necessary
+ // to complete the class.
+ CompilerType ct = ToCompilerType(tag_qt);
+ UdtRecordCompleter completer(best_ti, ct, tag, *this, m_index.tpi());
+ auto error =
+ llvm::codeview::visitMemberRecordStream(field_list_cvt.data(), completer);
+ completer.complete();
+
+ status.resolved = true;
+ if (!error)
+ return true;
+
+ llvm::consumeError(std::move(error));
+ return false;
+}
+
+clang::QualType PdbAstBuilder::CreateSimpleType(TypeIndex ti) {
+ if (ti == TypeIndex::NullptrT())
+ return GetBasicType(lldb::eBasicTypeNullPtr);
+
+ if (ti.getSimpleMode() != SimpleTypeMode::Direct) {
+ clang::QualType direct_type = GetOrCreateType(ti.makeDirect());
+ return m_clang.getASTContext()->getPointerType(direct_type);
+ }
+
+ if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated)
+ return {};
+
+ lldb::BasicType bt = GetCompilerTypeForSimpleKind(ti.getSimpleKind());
+ if (bt == lldb::eBasicTypeInvalid)
+ return {};
+
+ return GetBasicType(bt);
+}
+
+clang::QualType PdbAstBuilder::CreatePointerType(const PointerRecord &pointer) {
+ clang::QualType pointee_type = GetOrCreateType(pointer.ReferentType);
+
+ // This can happen for pointers to LF_VTSHAPE records, which we shouldn't
+ // create in the AST.
+ if (pointee_type.isNull())
+ return {};
+
+ if (pointer.isPointerToMember()) {
+ MemberPointerInfo mpi = pointer.getMemberInfo();
+ clang::QualType class_type = GetOrCreateType(mpi.ContainingType);
+
+ return m_clang.getASTContext()->getMemberPointerType(
+ pointee_type, class_type.getTypePtr());
+ }
+
+ clang::QualType pointer_type;
+ if (pointer.getMode() == PointerMode::LValueReference)
+ pointer_type =
+ m_clang.getASTContext()->getLValueReferenceType(pointee_type);
+ else if (pointer.getMode() == PointerMode::RValueReference)
+ pointer_type =
+ m_clang.getASTContext()->getRValueReferenceType(pointee_type);
+ else
+ pointer_type = m_clang.getASTContext()->getPointerType(pointee_type);
+
+ if ((pointer.getOptions() & PointerOptions::Const) != PointerOptions::None)
+ pointer_type.addConst();
+
+ if ((pointer.getOptions() & PointerOptions::Volatile) != PointerOptions::None)
+ pointer_type.addVolatile();
+
+ if ((pointer.getOptions() & PointerOptions::Restrict) != PointerOptions::None)
+ pointer_type.addRestrict();
+
+ return pointer_type;
+}
+
+clang::QualType
+PdbAstBuilder::CreateModifierType(const ModifierRecord &modifier) {
+ clang::QualType unmodified_type = GetOrCreateType(modifier.ModifiedType);
+ if (unmodified_type.isNull())
+ return {};
+
+ if ((modifier.Modifiers & ModifierOptions::Const) != ModifierOptions::None)
+ unmodified_type.addConst();
+ if ((modifier.Modifiers & ModifierOptions::Volatile) != ModifierOptions::None)
+ unmodified_type.addVolatile();
+
+ return unmodified_type;
+}
+
+clang::QualType PdbAstBuilder::CreateRecordType(PdbTypeSymId id,
+ const TagRecord &record) {
+ clang::DeclContext *context = nullptr;
+ std::string uname;
+ std::tie(context, uname) = CreateDeclInfoForType(record, id.index);
+ clang::TagTypeKind ttk = TranslateUdtKind(record);
+ lldb::AccessType access =
+ (ttk == clang::TTK_Class) ? lldb::eAccessPrivate : lldb::eAccessPublic;
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(toOpaqueUid(id));
+ metadata.SetIsDynamicCXXType(false);
+
+ CompilerType ct =
+ m_clang.CreateRecordType(context, access, uname.c_str(), ttk,
+ lldb::eLanguageTypeC_plus_plus, &metadata);
+
+ lldbassert(ct.IsValid());
+
+ ClangASTContext::StartTagDeclarationDefinition(ct);
+
+ // Even if it's possible, don't complete it at this point. Just mark it
+ // forward resolved, and if/when LLDB needs the full definition, it can
+ // ask us.
+ clang::QualType result =
+ clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType());
+
+ ClangASTContext::SetHasExternalStorage(result.getAsOpaquePtr(), true);
+ return result;
+}
+
+clang::Decl *PdbAstBuilder::TryGetDecl(PdbSymUid uid) const {
+ auto iter = m_uid_to_decl.find(toOpaqueUid(uid));
+ if (iter != m_uid_to_decl.end())
+ return iter->second;
+ return nullptr;
+}
+
+clang::NamespaceDecl *
+PdbAstBuilder::GetOrCreateNamespaceDecl(const char *name,
+ clang::DeclContext &context) {
+ return m_clang.GetUniqueNamespaceDeclaration(
+ IsAnonymousNamespaceName(name) ? nullptr : name, &context);
+}
+
+clang::BlockDecl *
+PdbAstBuilder::GetOrCreateBlockDecl(PdbCompilandSymId block_id) {
+ if (clang::Decl *decl = TryGetDecl(block_id))
+ return llvm::dyn_cast<clang::BlockDecl>(decl);
+
+ clang::DeclContext *scope = GetParentDeclContext(block_id);
+
+ clang::BlockDecl *block_decl = m_clang.CreateBlockDeclaration(scope);
+ m_uid_to_decl.insert({toOpaqueUid(block_id), block_decl});
+
+ DeclStatus status;
+ status.resolved = true;
+ status.uid = toOpaqueUid(block_id);
+ m_decl_to_status.insert({block_decl, status});
+
+ return block_decl;
+}
+
+clang::VarDecl *PdbAstBuilder::CreateVariableDecl(PdbSymUid uid, CVSymbol sym,
+ clang::DeclContext &scope) {
+ VariableInfo var_info = GetVariableNameInfo(sym);
+ clang::QualType qt = GetOrCreateType(var_info.type);
+
+ clang::VarDecl *var_decl = m_clang.CreateVariableDeclaration(
+ &scope, var_info.name.str().c_str(), qt);
+
+ m_uid_to_decl[toOpaqueUid(uid)] = var_decl;
+ DeclStatus status;
+ status.resolved = true;
+ status.uid = toOpaqueUid(uid);
+ m_decl_to_status.insert({var_decl, status});
+ return var_decl;
+}
+
+clang::VarDecl *
+PdbAstBuilder::GetOrCreateVariableDecl(PdbCompilandSymId scope_id,
+ PdbCompilandSymId var_id) {
+ if (clang::Decl *decl = TryGetDecl(var_id))
+ return llvm::dyn_cast<clang::VarDecl>(decl);
+
+ clang::DeclContext *scope = GetOrCreateDeclContextForUid(scope_id);
+
+ CVSymbol sym = m_index.ReadSymbolRecord(var_id);
+ return CreateVariableDecl(PdbSymUid(var_id), sym, *scope);
+}
+
+clang::VarDecl *PdbAstBuilder::GetOrCreateVariableDecl(PdbGlobalSymId var_id) {
+ if (clang::Decl *decl = TryGetDecl(var_id))
+ return llvm::dyn_cast<clang::VarDecl>(decl);
+
+ CVSymbol sym = m_index.ReadSymbolRecord(var_id);
+ auto context = FromCompilerDeclContext(GetTranslationUnitDecl());
+ return CreateVariableDecl(PdbSymUid(var_id), sym, *context);
+}
+
+clang::TypedefNameDecl *
+PdbAstBuilder::GetOrCreateTypedefDecl(PdbGlobalSymId id) {
+ if (clang::Decl *decl = TryGetDecl(id))
+ return llvm::dyn_cast<clang::TypedefNameDecl>(decl);
+
+ CVSymbol sym = m_index.ReadSymbolRecord(id);
+ lldbassert(sym.kind() == S_UDT);
+ UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym));
+
+ clang::DeclContext *scope = GetParentDeclContext(id);
+
+ PdbTypeSymId real_type_id{udt.Type, false};
+ clang::QualType qt = GetOrCreateType(real_type_id);
+
+ std::string uname = DropNameScope(udt.Name);
+
+ CompilerType ct = m_clang.CreateTypedefType(ToCompilerType(qt), uname.c_str(),
+ ToCompilerDeclContext(*scope));
+ clang::TypedefNameDecl *tnd = m_clang.GetAsTypedefDecl(ct);
+ DeclStatus status;
+ status.resolved = true;
+ status.uid = toOpaqueUid(id);
+ m_decl_to_status.insert({tnd, status});
+ return tnd;
+}
+
+clang::QualType PdbAstBuilder::GetBasicType(lldb::BasicType type) {
+ CompilerType ct = m_clang.GetBasicType(type);
+ return clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType());
+}
+
+clang::QualType PdbAstBuilder::CreateType(PdbTypeSymId type) {
+ if (type.index.isSimple())
+ return CreateSimpleType(type.index);
+
+ CVType cvt = m_index.tpi().getType(type.index);
+
+ if (cvt.kind() == LF_MODIFIER) {
+ ModifierRecord modifier;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<ModifierRecord>(cvt, modifier));
+ return CreateModifierType(modifier);
+ }
+
+ if (cvt.kind() == LF_POINTER) {
+ PointerRecord pointer;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<PointerRecord>(cvt, pointer));
+ return CreatePointerType(pointer);
+ }
+
+ if (IsTagRecord(cvt)) {
+ CVTagRecord tag = CVTagRecord::create(cvt);
+ if (tag.kind() == CVTagRecord::Union)
+ return CreateRecordType(type.index, tag.asUnion());
+ if (tag.kind() == CVTagRecord::Enum)
+ return CreateEnumType(type.index, tag.asEnum());
+ return CreateRecordType(type.index, tag.asClass());
+ }
+
+ if (cvt.kind() == LF_ARRAY) {
+ ArrayRecord ar;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ArrayRecord>(cvt, ar));
+ return CreateArrayType(ar);
+ }
+
+ if (cvt.kind() == LF_PROCEDURE) {
+ ProcedureRecord pr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, pr));
+ return CreateFunctionType(pr.ArgumentList, pr.ReturnType, pr.CallConv);
+ }
+
+ if (cvt.kind() == LF_MFUNCTION) {
+ MemberFunctionRecord mfr;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<MemberFunctionRecord>(cvt, mfr));
+ return CreateFunctionType(mfr.ArgumentList, mfr.ReturnType, mfr.CallConv);
+ }
+
+ return {};
+}
+
+clang::QualType PdbAstBuilder::GetOrCreateType(PdbTypeSymId type) {
+ lldb::user_id_t uid = toOpaqueUid(type);
+ auto iter = m_uid_to_type.find(uid);
+ if (iter != m_uid_to_type.end())
+ return iter->second;
+
+ PdbTypeSymId best_type = GetBestPossibleDecl(type, m_index.tpi());
+
+ clang::QualType qt;
+ if (best_type.index != type.index) {
+ // This is a forward decl. Call GetOrCreate on the full decl, then map the
+ // forward decl id to the full decl QualType.
+ clang::QualType qt = GetOrCreateType(best_type);
+ m_uid_to_type[toOpaqueUid(type)] = qt;
+ return qt;
+ }
+
+ // This is either a full decl, or a forward decl with no matching full decl
+ // in the debug info.
+ qt = CreateType(type);
+ m_uid_to_type[toOpaqueUid(type)] = qt;
+ if (IsTagRecord(type, m_index.tpi())) {
+ clang::TagDecl *tag = qt->getAsTagDecl();
+ lldbassert(m_decl_to_status.count(tag) == 0);
+
+ DeclStatus &status = m_decl_to_status[tag];
+ status.uid = uid;
+ status.resolved = false;
+ }
+ return qt;
+}
+
+clang::FunctionDecl *
+PdbAstBuilder::GetOrCreateFunctionDecl(PdbCompilandSymId func_id) {
+ if (clang::Decl *decl = TryGetDecl(func_id))
+ return llvm::dyn_cast<clang::FunctionDecl>(decl);
+
+ clang::DeclContext *parent = GetParentDeclContext(PdbSymUid(func_id));
+ std::string context_name;
+ if (clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(parent)) {
+ context_name = ns->getQualifiedNameAsString();
+ } else if (clang::TagDecl *tag = llvm::dyn_cast<clang::TagDecl>(parent)) {
+ context_name = tag->getQualifiedNameAsString();
+ }
+
+ CVSymbol cvs = m_index.ReadSymbolRecord(func_id);
+ ProcSym proc(static_cast<SymbolRecordKind>(cvs.kind()));
+ llvm::cantFail(SymbolDeserializer::deserializeAs<ProcSym>(cvs, proc));
+
+ PdbTypeSymId type_id(proc.FunctionType);
+ clang::QualType qt = GetOrCreateType(type_id);
+ if (qt.isNull())
+ return nullptr;
+
+ clang::StorageClass storage = clang::SC_None;
+ if (proc.Kind == SymbolRecordKind::ProcSym)
+ storage = clang::SC_Static;
+
+ const clang::FunctionProtoType *func_type =
+ llvm::dyn_cast<clang::FunctionProtoType>(qt);
+
+ CompilerType func_ct = ToCompilerType(qt);
+
+ llvm::StringRef proc_name = proc.Name;
+ proc_name.consume_front(context_name);
+ proc_name.consume_front("::");
+
+ clang::FunctionDecl *function_decl = m_clang.CreateFunctionDeclaration(
+ parent, proc_name.str().c_str(), func_ct, storage, false);
+
+ lldbassert(m_uid_to_decl.count(toOpaqueUid(func_id)) == 0);
+ m_uid_to_decl[toOpaqueUid(func_id)] = function_decl;
+ DeclStatus status;
+ status.resolved = true;
+ status.uid = toOpaqueUid(func_id);
+ m_decl_to_status.insert({function_decl, status});
+
+ CreateFunctionParameters(func_id, *function_decl, func_type->getNumParams());
+
+ return function_decl;
+}
+
+void PdbAstBuilder::CreateFunctionParameters(PdbCompilandSymId func_id,
+ clang::FunctionDecl &function_decl,
+ uint32_t param_count) {
+ CompilandIndexItem *cii = m_index.compilands().GetCompiland(func_id.modi);
+ CVSymbolArray scope =
+ cii->m_debug_stream.getSymbolArrayForScope(func_id.offset);
+
+ auto begin = scope.begin();
+ auto end = scope.end();
+ std::vector<clang::ParmVarDecl *> params;
+ while (begin != end && param_count > 0) {
+ uint32_t record_offset = begin.offset();
+ CVSymbol sym = *begin++;
+
+ TypeIndex param_type;
+ llvm::StringRef param_name;
+ switch (sym.kind()) {
+ case S_REGREL32: {
+ RegRelativeSym reg(SymbolRecordKind::RegRelativeSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg));
+ param_type = reg.Type;
+ param_name = reg.Name;
+ break;
+ }
+ case S_REGISTER: {
+ RegisterSym reg(SymbolRecordKind::RegisterSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
+ param_type = reg.Index;
+ param_name = reg.Name;
+ break;
+ }
+ case S_LOCAL: {
+ LocalSym local(SymbolRecordKind::LocalSym);
+ cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local));
+ if ((local.Flags & LocalSymFlags::IsParameter) == LocalSymFlags::None)
+ continue;
+ param_type = local.Type;
+ param_name = local.Name;
+ break;
+ }
+ case S_BLOCK32:
+ // All parameters should come before the first block. If that isn't the
+ // case, then perhaps this is bad debug info that doesn't contain
+ // information about all parameters.
+ return;
+ default:
+ continue;
+ }
+
+ PdbCompilandSymId param_uid(func_id.modi, record_offset);
+ clang::QualType qt = GetOrCreateType(param_type);
+
+ CompilerType param_type_ct(&m_clang, qt.getAsOpaquePtr());
+ clang::ParmVarDecl *param = m_clang.CreateParameterDeclaration(
+ &function_decl, param_name.str().c_str(), param_type_ct,
+ clang::SC_None);
+ lldbassert(m_uid_to_decl.count(toOpaqueUid(param_uid)) == 0);
+
+ m_uid_to_decl[toOpaqueUid(param_uid)] = param;
+ params.push_back(param);
+ --param_count;
+ }
+
+ if (!params.empty())
+ m_clang.SetFunctionParameters(&function_decl, params.data(), params.size());
+}
+
+clang::QualType PdbAstBuilder::CreateEnumType(PdbTypeSymId id,
+ const EnumRecord &er) {
+ clang::DeclContext *decl_context = nullptr;
+ std::string uname;
+ std::tie(decl_context, uname) = CreateDeclInfoForType(er, id.index);
+ clang::QualType underlying_type = GetOrCreateType(er.UnderlyingType);
+
+ Declaration declaration;
+ CompilerType enum_ct = m_clang.CreateEnumerationType(
+ uname.c_str(), decl_context, declaration, ToCompilerType(underlying_type),
+ er.isScoped());
+
+ ClangASTContext::StartTagDeclarationDefinition(enum_ct);
+ ClangASTContext::SetHasExternalStorage(enum_ct.GetOpaqueQualType(), true);
+
+ return clang::QualType::getFromOpaquePtr(enum_ct.GetOpaqueQualType());
+}
+
+clang::QualType PdbAstBuilder::CreateArrayType(const ArrayRecord &ar) {
+ clang::QualType element_type = GetOrCreateType(ar.ElementType);
+
+ uint64_t element_count =
+ ar.Size / GetSizeOfType({ar.ElementType}, m_index.tpi());
+
+ CompilerType array_ct = m_clang.CreateArrayType(ToCompilerType(element_type),
+ element_count, false);
+ return clang::QualType::getFromOpaquePtr(array_ct.GetOpaqueQualType());
+}
+
+clang::QualType PdbAstBuilder::CreateFunctionType(
+ TypeIndex args_type_idx, TypeIndex return_type_idx,
+ llvm::codeview::CallingConvention calling_convention) {
+ TpiStream &stream = m_index.tpi();
+ CVType args_cvt = stream.getType(args_type_idx);
+ ArgListRecord args;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<ArgListRecord>(args_cvt, args));
+
+ llvm::ArrayRef<TypeIndex> arg_indices = llvm::makeArrayRef(args.ArgIndices);
+ bool is_variadic = IsCVarArgsFunction(arg_indices);
+ if (is_variadic)
+ arg_indices = arg_indices.drop_back();
+
+ std::vector<CompilerType> arg_types;
+ arg_types.reserve(arg_indices.size());
+
+ for (TypeIndex arg_index : arg_indices) {
+ clang::QualType arg_type = GetOrCreateType(arg_index);
+ arg_types.push_back(ToCompilerType(arg_type));
+ }
+
+ clang::QualType return_type = GetOrCreateType(return_type_idx);
+
+ llvm::Optional<clang::CallingConv> cc =
+ TranslateCallingConvention(calling_convention);
+ if (!cc)
+ return {};
+
+ CompilerType return_ct = ToCompilerType(return_type);
+ CompilerType func_sig_ast_type = m_clang.CreateFunctionType(
+ return_ct, arg_types.data(), arg_types.size(), is_variadic, 0, *cc);
+
+ return clang::QualType::getFromOpaquePtr(
+ func_sig_ast_type.GetOpaqueQualType());
+}
+
+static bool isTagDecl(clang::DeclContext &context) {
+ return !!llvm::dyn_cast<clang::TagDecl>(&context);
+}
+
+static bool isFunctionDecl(clang::DeclContext &context) {
+ return !!llvm::dyn_cast<clang::FunctionDecl>(&context);
+}
+
+static bool isBlockDecl(clang::DeclContext &context) {
+ return !!llvm::dyn_cast<clang::BlockDecl>(&context);
+}
+
+void PdbAstBuilder::ParseAllNamespacesPlusChildrenOf(
+ llvm::Optional<llvm::StringRef> parent) {
+ TypeIndex ti{m_index.tpi().TypeIndexBegin()};
+ for (const CVType &cvt : m_index.tpi().typeArray()) {
+ PdbTypeSymId tid{ti};
+ ++ti;
+
+ if (!IsTagRecord(cvt))
+ continue;
+
+ CVTagRecord tag = CVTagRecord::create(cvt);
+
+ if (!parent.hasValue()) {
+ clang::QualType qt = GetOrCreateType(tid);
+ CompleteType(qt);
+ continue;
+ }
+
+ // Call CreateDeclInfoForType unconditionally so that the namespace info
+ // gets created. But only call CreateRecordType if the namespace name
+ // matches.
+ clang::DeclContext *context = nullptr;
+ std::string uname;
+ std::tie(context, uname) = CreateDeclInfoForType(tag.asTag(), tid.index);
+ if (!context->isNamespace())
+ continue;
+
+ clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(context);
+ std::string actual_ns = ns->getQualifiedNameAsString();
+ if (llvm::StringRef(actual_ns).startswith(*parent)) {
+ clang::QualType qt = GetOrCreateType(tid);
+ CompleteType(qt);
+ continue;
+ }
+ }
+
+ uint32_t module_count = m_index.dbi().modules().getModuleCount();
+ for (uint16_t modi = 0; modi < module_count; ++modi) {
+ CompilandIndexItem &cii = m_index.compilands().GetOrCreateCompiland(modi);
+ const CVSymbolArray &symbols = cii.m_debug_stream.getSymbolArray();
+ auto iter = symbols.begin();
+ while (iter != symbols.end()) {
+ PdbCompilandSymId sym_id{modi, iter.offset()};
+
+ switch (iter->kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ GetOrCreateFunctionDecl(sym_id);
+ iter = symbols.at(getScopeEndOffset(*iter));
+ break;
+ case S_GDATA32:
+ case S_GTHREAD32:
+ case S_LDATA32:
+ case S_LTHREAD32:
+ GetOrCreateVariableDecl(PdbCompilandSymId(modi, 0), sym_id);
+ ++iter;
+ break;
+ default:
+ ++iter;
+ continue;
+ }
+ }
+ }
+}
+
+static CVSymbolArray skipFunctionParameters(clang::Decl &decl,
+ const CVSymbolArray &symbols) {
+ clang::FunctionDecl *func_decl = llvm::dyn_cast<clang::FunctionDecl>(&decl);
+ if (!func_decl)
+ return symbols;
+ unsigned int params = func_decl->getNumParams();
+ if (params == 0)
+ return symbols;
+
+ CVSymbolArray result = symbols;
+
+ while (!result.empty()) {
+ if (params == 0)
+ return result;
+
+ CVSymbol sym = *result.begin();
+ result.drop_front();
+
+ if (!isLocalVariableType(sym.kind()))
+ continue;
+
+ --params;
+ }
+ return result;
+}
+
+void PdbAstBuilder::ParseBlockChildren(PdbCompilandSymId block_id) {
+ CVSymbol sym = m_index.ReadSymbolRecord(block_id);
+ lldbassert(sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32 ||
+ sym.kind() == S_BLOCK32);
+ CompilandIndexItem &cii =
+ m_index.compilands().GetOrCreateCompiland(block_id.modi);
+ CVSymbolArray symbols =
+ cii.m_debug_stream.getSymbolArrayForScope(block_id.offset);
+
+ // Function parameters should already have been created when the function was
+ // parsed.
+ if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32)
+ symbols =
+ skipFunctionParameters(*m_uid_to_decl[toOpaqueUid(block_id)], symbols);
+
+ auto begin = symbols.begin();
+ while (begin != symbols.end()) {
+ PdbCompilandSymId child_sym_id(block_id.modi, begin.offset());
+ GetOrCreateSymbolForId(child_sym_id);
+ if (begin->kind() == S_BLOCK32) {
+ ParseBlockChildren(child_sym_id);
+ begin = symbols.at(getScopeEndOffset(*begin));
+ }
+ ++begin;
+ }
+}
+
+void PdbAstBuilder::ParseDeclsForSimpleContext(clang::DeclContext &context) {
+
+ clang::Decl *decl = clang::Decl::castFromDeclContext(&context);
+ lldbassert(decl);
+
+ auto iter = m_decl_to_status.find(decl);
+ lldbassert(iter != m_decl_to_status.end());
+
+ if (auto *tag = llvm::dyn_cast<clang::TagDecl>(&context)) {
+ CompleteTagDecl(*tag);
+ return;
+ }
+
+ if (isFunctionDecl(context) || isBlockDecl(context)) {
+ PdbCompilandSymId block_id = PdbSymUid(iter->second.uid).asCompilandSym();
+ ParseBlockChildren(block_id);
+ }
+}
+
+void PdbAstBuilder::ParseDeclsForContext(clang::DeclContext &context) {
+ // Namespaces aren't explicitly represented in the debug info, and the only
+ // way to parse them is to parse all type info, demangling every single type
+ // and trying to reconstruct the DeclContext hierarchy this way. Since this
+ // is an expensive operation, we have to special case it so that we do other
+ // work (such as parsing the items that appear within the namespaces) at the
+ // same time.
+ if (context.isTranslationUnit()) {
+ ParseAllNamespacesPlusChildrenOf(llvm::None);
+ return;
+ }
+
+ if (context.isNamespace()) {
+ clang::NamespaceDecl &ns = *llvm::dyn_cast<clang::NamespaceDecl>(&context);
+ std::string qname = ns.getQualifiedNameAsString();
+ ParseAllNamespacesPlusChildrenOf(llvm::StringRef{qname});
+ return;
+ }
+
+ if (isTagDecl(context) || isFunctionDecl(context) || isBlockDecl(context)) {
+ ParseDeclsForSimpleContext(context);
+ return;
+ }
+}
+
+CompilerDecl PdbAstBuilder::ToCompilerDecl(clang::Decl &decl) {
+ return {&m_clang, &decl};
+}
+
+CompilerType PdbAstBuilder::ToCompilerType(clang::QualType qt) {
+ return {&m_clang, qt.getAsOpaquePtr()};
+}
+
+CompilerDeclContext
+PdbAstBuilder::ToCompilerDeclContext(clang::DeclContext &context) {
+ return {&m_clang, &context};
+}
+
+clang::Decl * PdbAstBuilder::FromCompilerDecl(CompilerDecl decl) {
+ return static_cast<clang::Decl *>(decl.GetOpaqueDecl());
+}
+
+clang::DeclContext *
+PdbAstBuilder::FromCompilerDeclContext(CompilerDeclContext context) {
+ return static_cast<clang::DeclContext *>(context.GetOpaqueDeclContext());
+}
+
+void PdbAstBuilder::Dump(Stream &stream) { m_clang.Dump(stream); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h
new file mode 100644
index 000000000000..67d024741e0d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h
@@ -0,0 +1,144 @@
+//===-- PdbAstBuilder.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H
+#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+
+#include "lldb/Symbol/ClangASTImporter.h"
+
+#include "PdbIndex.h"
+#include "PdbSymUid.h"
+
+namespace clang {
+class TagDecl;
+class DeclContext;
+class Decl;
+class QualType;
+class FunctionDecl;
+class NamespaceDecl;
+} // namespace clang
+
+namespace llvm {
+namespace codeview {
+class ProcSym;
+}
+} // namespace llvm
+
+namespace lldb_private {
+class ClangASTImporter;
+class ObjectFile;
+
+namespace npdb {
+class PdbIndex;
+struct VariableInfo;
+
+struct DeclStatus {
+ DeclStatus() = default;
+ DeclStatus(lldb::user_id_t uid, bool resolved)
+ : uid(uid), resolved(resolved) {}
+ lldb::user_id_t uid = 0;
+ bool resolved = false;
+};
+
+class PdbAstBuilder {
+public:
+ // Constructors and Destructors
+ PdbAstBuilder(ObjectFile &obj, PdbIndex &index);
+
+ lldb_private::CompilerDeclContext GetTranslationUnitDecl();
+
+ clang::Decl *GetOrCreateDeclForUid(PdbSymUid uid);
+ clang::DeclContext *GetOrCreateDeclContextForUid(PdbSymUid uid);
+ clang::DeclContext *GetParentDeclContext(PdbSymUid uid);
+
+ clang::FunctionDecl *GetOrCreateFunctionDecl(PdbCompilandSymId func_id);
+ clang::BlockDecl *GetOrCreateBlockDecl(PdbCompilandSymId block_id);
+ clang::VarDecl *GetOrCreateVariableDecl(PdbCompilandSymId scope_id,
+ PdbCompilandSymId var_id);
+ clang::VarDecl *GetOrCreateVariableDecl(PdbGlobalSymId var_id);
+ clang::TypedefNameDecl *GetOrCreateTypedefDecl(PdbGlobalSymId id);
+ void ParseDeclsForContext(clang::DeclContext &context);
+
+ clang::QualType GetBasicType(lldb::BasicType type);
+ clang::QualType GetOrCreateType(PdbTypeSymId type);
+
+ bool CompleteTagDecl(clang::TagDecl &tag);
+ bool CompleteType(clang::QualType qt);
+
+ CompilerDecl ToCompilerDecl(clang::Decl &decl);
+ CompilerType ToCompilerType(clang::QualType qt);
+ CompilerDeclContext ToCompilerDeclContext(clang::DeclContext &context);
+ clang::Decl * FromCompilerDecl(CompilerDecl decl);
+ clang::DeclContext *FromCompilerDeclContext(CompilerDeclContext context);
+
+ ClangASTContext &clang() { return m_clang; }
+ ClangASTImporter &importer() { return m_importer; }
+
+ void Dump(Stream &stream);
+
+private:
+ clang::Decl *TryGetDecl(PdbSymUid uid) const;
+
+ using TypeIndex = llvm::codeview::TypeIndex;
+
+ clang::QualType
+ CreatePointerType(const llvm::codeview::PointerRecord &pointer);
+ clang::QualType
+ CreateModifierType(const llvm::codeview::ModifierRecord &modifier);
+ clang::QualType CreateArrayType(const llvm::codeview::ArrayRecord &array);
+ clang::QualType CreateRecordType(PdbTypeSymId id,
+ const llvm::codeview::TagRecord &record);
+ clang::QualType CreateEnumType(PdbTypeSymId id,
+ const llvm::codeview::EnumRecord &record);
+ clang::QualType
+ CreateFunctionType(TypeIndex args_type_idx, TypeIndex return_type_idx,
+ llvm::codeview::CallingConvention calling_convention);
+ clang::QualType CreateType(PdbTypeSymId type);
+
+ void CreateFunctionParameters(PdbCompilandSymId func_id,
+ clang::FunctionDecl &function_decl,
+ uint32_t param_count);
+ clang::Decl *GetOrCreateSymbolForId(PdbCompilandSymId id);
+ clang::VarDecl *CreateVariableDecl(PdbSymUid uid,
+ llvm::codeview::CVSymbol sym,
+ clang::DeclContext &scope);
+ clang::DeclContext *
+ GetParentDeclContextForSymbol(const llvm::codeview::CVSymbol &sym);
+
+ clang::NamespaceDecl *GetOrCreateNamespaceDecl(const char *name,
+ clang::DeclContext &context);
+
+ void ParseAllNamespacesPlusChildrenOf(llvm::Optional<llvm::StringRef> parent);
+ void ParseDeclsForSimpleContext(clang::DeclContext &context);
+ void ParseBlockChildren(PdbCompilandSymId block_id);
+
+ void BuildParentMap();
+ std::pair<clang::DeclContext *, std::string>
+ CreateDeclInfoForType(const llvm::codeview::TagRecord &record, TypeIndex ti);
+ std::pair<clang::DeclContext *, std::string>
+ CreateDeclInfoForUndecoratedName(llvm::StringRef uname);
+ clang::QualType CreateSimpleType(TypeIndex ti);
+
+ PdbIndex &m_index;
+ ClangASTContext &m_clang;
+
+ ClangASTImporter m_importer;
+
+ llvm::DenseMap<TypeIndex, TypeIndex> m_parent_types;
+ llvm::DenseMap<clang::Decl *, DeclStatus> m_decl_to_status;
+ llvm::DenseMap<lldb::user_id_t, clang::Decl *> m_uid_to_decl;
+ llvm::DenseMap<lldb::user_id_t, clang::QualType> m_uid_to_type;
+};
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp
new file mode 100644
index 000000000000..79dd010ff311
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp
@@ -0,0 +1,137 @@
+//===-- PdbFPOProgramToDWARFExpression.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PdbFPOProgramToDWARFExpression.h"
+#include "CodeViewRegisterMapping.h"
+
+#include "lldb/Core/StreamBuffer.h"
+#include "lldb/Symbol/PostfixExpression.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Stream.h"
+#include "llvm/ADT/DenseMap.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/EnumTables.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::postfix;
+
+static uint32_t ResolveLLDBRegisterNum(llvm::StringRef reg_name, llvm::Triple::ArchType arch_type) {
+ // lookup register name to get lldb register number
+ llvm::codeview::CPUType cpu_type;
+ switch (arch_type) {
+ case llvm::Triple::ArchType::aarch64:
+ cpu_type = llvm::codeview::CPUType::ARM64;
+ break;
+
+ default:
+ cpu_type = llvm::codeview::CPUType::X64;
+ break;
+ }
+
+ llvm::ArrayRef<llvm::EnumEntry<uint16_t>> register_names =
+ llvm::codeview::getRegisterNames(cpu_type);
+ auto it = llvm::find_if(
+ register_names,
+ [&reg_name](const llvm::EnumEntry<uint16_t> &register_entry) {
+ return reg_name.compare_lower(register_entry.Name) == 0;
+ });
+
+ if (it == register_names.end())
+ return LLDB_INVALID_REGNUM;
+
+ auto reg_id = static_cast<llvm::codeview::RegisterId>(it->Value);
+ return npdb::GetLLDBRegisterNumber(arch_type, reg_id);
+}
+
+static bool ParseFPOSingleAssignmentProgram(llvm::StringRef program,
+ llvm::BumpPtrAllocator &alloc,
+ llvm::StringRef &register_name,
+ Node *&ast) {
+ // lvalue of assignment is always first token
+ // rvalue program goes next
+ std::tie(register_name, program) = getToken(program);
+ if (register_name.empty())
+ return false;
+
+ ast = Parse(program, alloc);
+ return ast != nullptr;
+}
+
+static Node *ParseFPOProgram(llvm::StringRef program,
+ llvm::StringRef register_name,
+ llvm::Triple::ArchType arch_type,
+ llvm::BumpPtrAllocator &alloc) {
+ llvm::DenseMap<llvm::StringRef, Node *> dependent_programs;
+
+ size_t cur = 0;
+ while (true) {
+ size_t assign_index = program.find('=', cur);
+ if (assign_index == llvm::StringRef::npos) {
+ llvm::StringRef tail = program.slice(cur, llvm::StringRef::npos);
+ if (!tail.trim().empty()) {
+ // missing assign operator
+ return nullptr;
+ }
+ break;
+ }
+ llvm::StringRef assignment_program = program.slice(cur, assign_index);
+
+ llvm::StringRef lvalue_name;
+ Node *rvalue_ast = nullptr;
+ if (!ParseFPOSingleAssignmentProgram(assignment_program, alloc, lvalue_name,
+ rvalue_ast)) {
+ return nullptr;
+ }
+
+ lldbassert(rvalue_ast);
+
+ // Emplace valid dependent subtrees to make target assignment independent
+ // from predecessors. Resolve all other SymbolNodes as registers.
+ bool success =
+ ResolveSymbols(rvalue_ast, [&](SymbolNode &symbol) -> Node * {
+ if (Node *node = dependent_programs.lookup(symbol.GetName()))
+ return node;
+ uint32_t reg_num =
+ ResolveLLDBRegisterNum(symbol.GetName().drop_front(1), arch_type);
+
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return nullptr;
+
+ return MakeNode<RegisterNode>(alloc, reg_num);
+ });
+ if (!success)
+ return nullptr;
+
+ if (lvalue_name == register_name) {
+ // found target assignment program - no need to parse further
+ return rvalue_ast;
+ }
+
+ dependent_programs[lvalue_name] = rvalue_ast;
+ cur = assign_index + 1;
+ }
+
+ return nullptr;
+}
+
+bool lldb_private::npdb::TranslateFPOProgramToDWARFExpression(
+ llvm::StringRef program, llvm::StringRef register_name,
+ llvm::Triple::ArchType arch_type, Stream &stream) {
+ llvm::BumpPtrAllocator node_alloc;
+ Node *target_program =
+ ParseFPOProgram(program, register_name, arch_type, node_alloc);
+ if (target_program == nullptr) {
+ return false;
+ }
+
+ ToDWARF(*target_program, stream);
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h
new file mode 100644
index 000000000000..107e26fb04cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h
@@ -0,0 +1,28 @@
+//===-- PdbFPOProgramToDWARFExpression.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_Plugins_SymbolFile_PDB_PDBFPOProgramToDWARFExpression_h_
+#define lldb_Plugins_SymbolFile_PDB_PDBFPOProgramToDWARFExpression_h_
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+
+namespace lldb_private {
+class Stream;
+
+namespace npdb {
+
+bool TranslateFPOProgramToDWARFExpression(llvm::StringRef program,
+ llvm::StringRef register_name,
+ llvm::Triple::ArchType arch_type,
+ lldb_private::Stream &stream);
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp
new file mode 100644
index 000000000000..ba9a95b16e18
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp
@@ -0,0 +1,196 @@
+//===-- PdbIndex.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PdbIndex.h"
+#include "PdbUtil.h"
+
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
+#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
+#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/Error.h"
+
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/lldb-defines.h"
+
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+PdbIndex::PdbIndex() : m_cus(*this), m_va_to_modi(m_allocator) {}
+
+#define ASSIGN_PTR_OR_RETURN(result_ptr, expr) \
+ { \
+ auto expected_result = expr; \
+ if (!expected_result) \
+ return expected_result.takeError(); \
+ result_ptr = &expected_result.get(); \
+ }
+
+llvm::Expected<std::unique_ptr<PdbIndex>>
+PdbIndex::create(std::unique_ptr<llvm::pdb::PDBFile> file) {
+ lldbassert(file);
+
+ std::unique_ptr<PdbIndex> result(new PdbIndex());
+ ASSIGN_PTR_OR_RETURN(result->m_dbi, file->getPDBDbiStream());
+ ASSIGN_PTR_OR_RETURN(result->m_tpi, file->getPDBTpiStream());
+ ASSIGN_PTR_OR_RETURN(result->m_ipi, file->getPDBIpiStream());
+ ASSIGN_PTR_OR_RETURN(result->m_info, file->getPDBInfoStream());
+ ASSIGN_PTR_OR_RETURN(result->m_publics, file->getPDBPublicsStream());
+ ASSIGN_PTR_OR_RETURN(result->m_globals, file->getPDBGlobalsStream());
+ ASSIGN_PTR_OR_RETURN(result->m_symrecords, file->getPDBSymbolStream());
+
+ result->m_tpi->buildHashMap();
+
+ result->m_file = std::move(file);
+
+ return std::move(result);
+}
+
+lldb::addr_t PdbIndex::MakeVirtualAddress(uint16_t segment,
+ uint32_t offset) const {
+ // Segment indices are 1-based.
+ lldbassert(segment > 0);
+
+ uint32_t max_section = dbi().getSectionHeaders().size();
+ lldbassert(segment <= max_section + 1);
+
+ // If this is an absolute symbol, it's indicated by the magic section index
+ // |max_section+1|. In this case, the offset is meaningless, so just return.
+ if (segment == max_section + 1)
+ return LLDB_INVALID_ADDRESS;
+
+ const llvm::object::coff_section &cs = dbi().getSectionHeaders()[segment - 1];
+ return m_load_address + static_cast<lldb::addr_t>(cs.VirtualAddress) +
+ static_cast<lldb::addr_t>(offset);
+}
+
+lldb::addr_t PdbIndex::MakeVirtualAddress(const SegmentOffset &so) const {
+ return MakeVirtualAddress(so.segment, so.offset);
+}
+
+llvm::Optional<uint16_t>
+PdbIndex::GetModuleIndexForAddr(uint16_t segment, uint32_t offset) const {
+ return GetModuleIndexForVa(MakeVirtualAddress(segment, offset));
+}
+
+llvm::Optional<uint16_t> PdbIndex::GetModuleIndexForVa(lldb::addr_t va) const {
+ auto iter = m_va_to_modi.find(va);
+ if (iter == m_va_to_modi.end())
+ return llvm::None;
+
+ return iter.value();
+}
+
+void PdbIndex::ParseSectionContribs() {
+ class Visitor : public ISectionContribVisitor {
+ PdbIndex &m_ctx;
+ llvm::IntervalMap<uint64_t, uint16_t> &m_imap;
+
+ public:
+ Visitor(PdbIndex &ctx, llvm::IntervalMap<uint64_t, uint16_t> &imap)
+ : m_ctx(ctx), m_imap(imap) {}
+
+ void visit(const SectionContrib &C) override {
+ if (C.Size == 0)
+ return;
+
+ uint64_t va = m_ctx.MakeVirtualAddress(C.ISect, C.Off);
+ uint64_t end = va + C.Size;
+ // IntervalMap's start and end represent a closed range, not a half-open
+ // range, so we have to subtract 1.
+ m_imap.insert(va, end - 1, C.Imod);
+ }
+ void visit(const SectionContrib2 &C) override { visit(C.Base); }
+ };
+ Visitor v(*this, m_va_to_modi);
+ dbi().visitSectionContributions(v);
+}
+
+void PdbIndex::BuildAddrToSymbolMap(CompilandIndexItem &cci) {
+ lldbassert(cci.m_symbols_by_va.empty() &&
+ "Addr to symbol map is already built!");
+ uint16_t modi = cci.m_id.modi;
+ const CVSymbolArray &syms = cci.m_debug_stream.getSymbolArray();
+ for (auto iter = syms.begin(); iter != syms.end(); ++iter) {
+ if (!SymbolHasAddress(*iter))
+ continue;
+
+ SegmentOffset so = GetSegmentAndOffset(*iter);
+ lldb::addr_t va = MakeVirtualAddress(so);
+
+ PdbCompilandSymId cu_sym_id(modi, iter.offset());
+
+ // It's rare, but we could have multiple symbols with the same address
+ // because of identical comdat folding. Right now, the first one will win.
+ cci.m_symbols_by_va.insert(std::make_pair(va, PdbSymUid(cu_sym_id)));
+ }
+}
+
+std::vector<SymbolAndUid> PdbIndex::FindSymbolsByVa(lldb::addr_t va) {
+ std::vector<SymbolAndUid> result;
+
+ llvm::Optional<uint16_t> modi = GetModuleIndexForVa(va);
+ if (!modi)
+ return result;
+
+ CompilandIndexItem &cci = compilands().GetOrCreateCompiland(*modi);
+ if (cci.m_symbols_by_va.empty())
+ BuildAddrToSymbolMap(cci);
+
+ // The map is sorted by starting address of the symbol. So for example
+ // we could (in theory) have this situation
+ //
+ // [------------------]
+ // [----------]
+ // [-----------]
+ // [-------------]
+ // [----]
+ // [-----]
+ // ^ Address we're searching for
+ // In order to find this, we use the upper_bound of the key value which would
+ // be the first symbol whose starting address is higher than the element we're
+ // searching for.
+
+ auto ub = cci.m_symbols_by_va.upper_bound(va);
+
+ for (auto iter = cci.m_symbols_by_va.begin(); iter != ub; ++iter) {
+ PdbCompilandSymId cu_sym_id = iter->second.asCompilandSym();
+ CVSymbol sym = ReadSymbolRecord(cu_sym_id);
+
+ SegmentOffsetLength sol;
+ if (SymbolIsCode(sym))
+ sol = GetSegmentOffsetAndLength(sym);
+ else
+ sol.so = GetSegmentAndOffset(sym);
+
+ lldb::addr_t start = MakeVirtualAddress(sol.so);
+ lldb::addr_t end = start + sol.length;
+ if (va >= start && va < end)
+ result.push_back({std::move(sym), iter->second});
+ }
+
+ return result;
+}
+
+CVSymbol PdbIndex::ReadSymbolRecord(PdbCompilandSymId cu_sym) const {
+ const CompilandIndexItem *cci = compilands().GetCompiland(cu_sym.modi);
+ auto iter = cci->m_debug_stream.getSymbolArray().at(cu_sym.offset);
+ lldbassert(iter != cci->m_debug_stream.getSymbolArray().end());
+ return *iter;
+}
+
+CVSymbol PdbIndex::ReadSymbolRecord(PdbGlobalSymId global) const {
+ return symrecords().readRecord(global.offset);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h
new file mode 100644
index 000000000000..b30e7870bbdb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h
@@ -0,0 +1,162 @@
+//===-- PdbIndex.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBINDEX_H
+#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBINDEX_H
+
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/IntervalMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+
+#include "CompileUnitIndex.h"
+#include "PdbSymUid.h"
+
+#include <map>
+#include <memory>
+
+namespace llvm {
+namespace pdb {
+class DbiStream;
+class TpiStream;
+class TpiStream;
+class InfoStream;
+class PublicsStream;
+class GlobalsStream;
+class SymbolStream;
+} // namespace pdb
+} // namespace llvm
+
+namespace lldb_private {
+namespace npdb {
+struct SegmentOffset;
+
+/// PdbIndex - Lazy access to the important parts of a PDB file.
+///
+/// This is a layer on top of LLVM's native PDB support libraries which cache
+/// certain data when it is accessed the first time. The entire PDB file is
+/// mapped into memory, and the underlying support libraries vend out memory
+/// that is always backed by the file, so it is safe to hold StringRefs and
+/// ArrayRefs into the backing memory as long as the PdbIndex instance is
+/// alive.
+class PdbIndex {
+
+ /// The underlying PDB file.
+ std::unique_ptr<llvm::pdb::PDBFile> m_file;
+
+ /// The DBI stream. This contains general high level information about the
+ /// features present in the PDB file, compile units (such as the information
+ /// necessary to locate full symbol information for each compile unit),
+ /// section contributions, and other data which is not specifically symbol or
+ /// type records.
+ llvm::pdb::DbiStream *m_dbi = nullptr;
+
+ /// TPI (types) and IPI (indices) streams. These are both in the exact same
+ /// format with different data. Most type records are stored in the TPI
+ /// stream but certain specific types of records are stored in the IPI stream.
+ /// The IPI stream records can refer to the records in the TPI stream, but not
+ /// the other way around.
+ llvm::pdb::TpiStream *m_tpi = nullptr;
+ llvm::pdb::TpiStream *m_ipi = nullptr;
+
+ /// This is called the "PDB Stream" in the Microsoft reference implementation.
+ /// It contains information about the structure of the file, as well as fields
+ /// used to match EXE and PDB.
+ llvm::pdb::InfoStream *m_info = nullptr;
+
+ /// Publics stream. Is actually a serialized hash table where the keys are
+ /// addresses of symbols in the executable, and values are a record containing
+ /// mangled names and an index which can be used to locate more detailed info
+ /// about the symbol in the Symbol Records stream. The publics stream only
+ /// contains info about externally visible symbols.
+ llvm::pdb::PublicsStream *m_publics = nullptr;
+
+ /// Globals stream. Contrary to its name, this does not contain information
+ /// about all "global variables" or "global functions". Rather, it is the
+ /// "global symbol table", i.e. it contains information about *every* symbol
+ /// in the executable. It is a hash table keyed on name, whose values are
+ /// indices into the symbol records stream to find the full record.
+ llvm::pdb::GlobalsStream *m_globals = nullptr;
+
+ /// Symbol records stream. The publics and globals stream refer to records
+ /// in this stream. For some records, like constants and typedefs, the
+ /// complete record lives in this stream. For other symbol types, such as
+ /// functions, data, and other things that have been materialied into a
+ /// specific compile unit, the records here simply provide a reference
+ /// necessary to locate the full information.
+ llvm::pdb::SymbolStream *m_symrecords = nullptr;
+
+ /// Index of all compile units, mapping identifier to |CompilandIndexItem|
+ /// instance.
+ CompileUnitIndex m_cus;
+
+ /// An allocator for the interval maps
+ llvm::IntervalMap<lldb::addr_t, uint32_t>::Allocator m_allocator;
+
+ /// Maps virtual address to module index
+ llvm::IntervalMap<lldb::addr_t, uint16_t> m_va_to_modi;
+
+ /// The address at which the program has been loaded into memory.
+ lldb::addr_t m_load_address = 0;
+
+ PdbIndex();
+
+ void BuildAddrToSymbolMap(CompilandIndexItem &cci);
+
+public:
+ static llvm::Expected<std::unique_ptr<PdbIndex>>
+ create(std::unique_ptr<llvm::pdb::PDBFile>);
+
+ void SetLoadAddress(lldb::addr_t addr) { m_load_address = addr; }
+ lldb::addr_t GetLoadAddress() const { return m_load_address; }
+ void ParseSectionContribs();
+
+ llvm::pdb::PDBFile &pdb() { return *m_file; }
+ const llvm::pdb::PDBFile &pdb() const { return *m_file; }
+
+ llvm::pdb::DbiStream &dbi() { return *m_dbi; }
+ const llvm::pdb::DbiStream &dbi() const { return *m_dbi; }
+
+ llvm::pdb::TpiStream &tpi() { return *m_tpi; }
+ const llvm::pdb::TpiStream &tpi() const { return *m_tpi; }
+
+ llvm::pdb::TpiStream &ipi() { return *m_ipi; }
+ const llvm::pdb::TpiStream &ipi() const { return *m_ipi; }
+
+ llvm::pdb::InfoStream &info() { return *m_info; }
+ const llvm::pdb::InfoStream &info() const { return *m_info; }
+
+ llvm::pdb::PublicsStream &publics() { return *m_publics; }
+ const llvm::pdb::PublicsStream &publics() const { return *m_publics; }
+
+ llvm::pdb::GlobalsStream &globals() { return *m_globals; }
+ const llvm::pdb::GlobalsStream &globals() const { return *m_globals; }
+
+ llvm::pdb::SymbolStream &symrecords() { return *m_symrecords; }
+ const llvm::pdb::SymbolStream &symrecords() const { return *m_symrecords; }
+
+ CompileUnitIndex &compilands() { return m_cus; }
+ const CompileUnitIndex &compilands() const { return m_cus; }
+
+ lldb::addr_t MakeVirtualAddress(uint16_t segment, uint32_t offset) const;
+ lldb::addr_t MakeVirtualAddress(const SegmentOffset &so) const;
+
+ std::vector<SymbolAndUid> FindSymbolsByVa(lldb::addr_t va);
+
+ llvm::codeview::CVSymbol ReadSymbolRecord(PdbCompilandSymId cu_sym) const;
+ llvm::codeview::CVSymbol ReadSymbolRecord(PdbGlobalSymId global) const;
+
+ llvm::Optional<uint16_t> GetModuleIndexForAddr(uint16_t segment,
+ uint32_t offset) const;
+ llvm::Optional<uint16_t> GetModuleIndexForVa(lldb::addr_t va) const;
+};
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp
new file mode 100644
index 000000000000..e5ad23f813eb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp
@@ -0,0 +1,160 @@
+//===-- PdbSymUid.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PdbSymUid.h"
+
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+
+namespace {
+struct GenericIdRepr {
+ uint64_t tag : 4;
+ uint64_t data : 60;
+};
+
+struct CompilandIdRepr {
+ uint64_t tag : 4;
+ uint64_t modi : 16;
+ uint64_t unused : 44;
+};
+
+struct CompilandSymIdRepr {
+ uint64_t tag : 4;
+ uint64_t modi : 16;
+ uint64_t offset : 32;
+ uint64_t unused : 12;
+};
+
+struct GlobalSymIdRepr {
+ uint64_t tag : 4;
+ uint64_t offset : 32;
+ uint64_t pub : 1;
+ uint64_t unused : 27;
+};
+
+struct TypeSymIdRepr {
+ uint64_t tag : 4;
+ uint64_t index : 32;
+ uint64_t ipi : 1;
+ uint64_t unused : 27;
+};
+
+struct FieldListMemberIdRepr {
+ uint64_t tag : 4;
+ uint64_t index : 32;
+ uint64_t offset : 16;
+ uint64_t unused : 12;
+};
+
+static_assert(sizeof(CompilandIdRepr) == 8, "Invalid structure size!");
+static_assert(sizeof(CompilandSymIdRepr) == 8, "Invalid structure size!");
+static_assert(sizeof(GlobalSymIdRepr) == 8, "Invalid structure size!");
+static_assert(sizeof(TypeSymIdRepr) == 8, "Invalid structure size!");
+static_assert(sizeof(FieldListMemberIdRepr) == 8, "Invalid structure size!");
+} // namespace
+
+template <typename OutT, typename InT> static OutT repr_cast(const InT &value) {
+ OutT result;
+ ::memcpy(&result, &value, sizeof(value));
+ return result;
+}
+
+PdbSymUid::PdbSymUid(const PdbCompilandId &cid) {
+ CompilandIdRepr repr;
+ ::memset(&repr, 0, sizeof(repr));
+ repr.modi = cid.modi;
+ repr.tag = static_cast<uint64_t>(PdbSymUidKind::Compiland);
+ m_repr = repr_cast<uint64_t>(repr);
+}
+
+PdbSymUid::PdbSymUid(const PdbCompilandSymId &csid) {
+ CompilandSymIdRepr repr;
+ ::memset(&repr, 0, sizeof(repr));
+ repr.modi = csid.modi;
+ repr.offset = csid.offset;
+ repr.tag = static_cast<uint64_t>(PdbSymUidKind::CompilandSym);
+ m_repr = repr_cast<uint64_t>(repr);
+}
+
+PdbSymUid::PdbSymUid(const PdbGlobalSymId &gsid) {
+ GlobalSymIdRepr repr;
+ ::memset(&repr, 0, sizeof(repr));
+ repr.pub = gsid.is_public;
+ repr.offset = gsid.offset;
+ repr.tag = static_cast<uint64_t>(PdbSymUidKind::GlobalSym);
+ m_repr = repr_cast<uint64_t>(repr);
+}
+
+PdbSymUid::PdbSymUid(const PdbTypeSymId &tsid) {
+ TypeSymIdRepr repr;
+ ::memset(&repr, 0, sizeof(repr));
+ repr.index = tsid.index.getIndex();
+ repr.ipi = tsid.is_ipi;
+ repr.tag = static_cast<uint64_t>(PdbSymUidKind::Type);
+ m_repr = repr_cast<uint64_t>(repr);
+}
+
+PdbSymUid::PdbSymUid(const PdbFieldListMemberId &flmid) {
+ FieldListMemberIdRepr repr;
+ ::memset(&repr, 0, sizeof(repr));
+ repr.index = flmid.index.getIndex();
+ repr.offset = flmid.offset;
+ repr.tag = static_cast<uint64_t>(PdbSymUidKind::FieldListMember);
+ m_repr = repr_cast<uint64_t>(repr);
+}
+
+PdbSymUidKind PdbSymUid::kind() const {
+ GenericIdRepr generic = repr_cast<GenericIdRepr>(m_repr);
+ return static_cast<PdbSymUidKind>(generic.tag);
+}
+
+PdbCompilandId PdbSymUid::asCompiland() const {
+ assert(kind() == PdbSymUidKind::Compiland);
+ auto repr = repr_cast<CompilandIdRepr>(m_repr);
+ PdbCompilandId result;
+ result.modi = repr.modi;
+ return result;
+}
+
+PdbCompilandSymId PdbSymUid::asCompilandSym() const {
+ assert(kind() == PdbSymUidKind::CompilandSym);
+ auto repr = repr_cast<CompilandSymIdRepr>(m_repr);
+ PdbCompilandSymId result;
+ result.modi = repr.modi;
+ result.offset = repr.offset;
+ return result;
+}
+
+PdbGlobalSymId PdbSymUid::asGlobalSym() const {
+ assert(kind() == PdbSymUidKind::GlobalSym ||
+ kind() == PdbSymUidKind::PublicSym);
+ auto repr = repr_cast<GlobalSymIdRepr>(m_repr);
+ PdbGlobalSymId result;
+ result.is_public = repr.pub;
+ result.offset = repr.offset;
+ return result;
+}
+
+PdbTypeSymId PdbSymUid::asTypeSym() const {
+ assert(kind() == PdbSymUidKind::Type);
+ auto repr = repr_cast<TypeSymIdRepr>(m_repr);
+ PdbTypeSymId result;
+ result.index.setIndex(repr.index);
+ result.is_ipi = repr.ipi;
+ return result;
+}
+
+PdbFieldListMemberId PdbSymUid::asFieldListMember() const {
+ assert(kind() == PdbSymUidKind::FieldListMember);
+ auto repr = repr_cast<FieldListMemberIdRepr>(m_repr);
+ PdbFieldListMemberId result;
+ result.index.setIndex(repr.index);
+ result.offset = repr.offset;
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h
new file mode 100644
index 000000000000..7252d63c1ab4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h
@@ -0,0 +1,125 @@
+//===-- PdbSymUid.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// A unique identification scheme for Pdb records.
+// The scheme is to partition a 64-bit integer into an 8-bit tag field, which
+// will contain some value from the PDB_SymType enumeration. The format of the
+// other 48-bits depend on the tag, but must be sufficient to locate the
+// corresponding entry in the underlying PDB file quickly. For example, for
+// a compile unit, we use 2 bytes to represent the index, which allows fast
+// access to the compile unit's information.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBSYMUID_H
+#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBSYMUID_H
+
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+#include "llvm/Support/Compiler.h"
+
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+namespace npdb {
+
+enum class PdbSymUidKind : uint8_t {
+ Compiland,
+ CompilandSym,
+ PublicSym,
+ GlobalSym,
+ Type,
+ FieldListMember
+};
+
+struct PdbCompilandId {
+ // 0-based index of module in PDB
+ uint16_t modi;
+};
+
+struct PdbCompilandSymId {
+ PdbCompilandSymId() = default;
+ PdbCompilandSymId(uint16_t modi, uint32_t offset)
+ : modi(modi), offset(offset) {}
+ // 0-based index of module in PDB
+ uint16_t modi = 0;
+
+ // Offset of symbol's record in module stream. This is
+ // offset by 4 from the CVSymbolArray's notion of offset
+ // due to the debug magic at the beginning of the stream.
+ uint32_t offset = 0;
+};
+
+struct PdbGlobalSymId {
+ PdbGlobalSymId() = default;
+ PdbGlobalSymId(uint32_t offset, bool is_public)
+ : offset(offset), is_public(is_public) {}
+
+ // Offset of symbol's record in globals or publics stream.
+ uint32_t offset = 0;
+
+ // True if this symbol is in the public stream, false if it's in the globals
+ // stream.
+ bool is_public = false;
+};
+
+struct PdbTypeSymId {
+ PdbTypeSymId() = default;
+ PdbTypeSymId(llvm::codeview::TypeIndex index, bool is_ipi = false)
+ : index(index), is_ipi(is_ipi) {}
+
+ // The index of the of the type in the TPI or IPI stream.
+ llvm::codeview::TypeIndex index;
+
+ // True if this symbol comes from the IPI stream, false if it's from the TPI
+ // stream.
+ bool is_ipi = false;
+};
+
+struct PdbFieldListMemberId {
+ // The TypeIndex of the LF_FIELDLIST record.
+ llvm::codeview::TypeIndex index;
+
+ // The offset from the beginning of the LF_FIELDLIST record to this record.
+ uint16_t offset = 0;
+};
+
+class PdbSymUid {
+ uint64_t m_repr = 0;
+
+public:
+ PdbSymUid() = default;
+ PdbSymUid(uint64_t repr) : m_repr(repr) {}
+ PdbSymUid(const PdbCompilandId &cid);
+ PdbSymUid(const PdbCompilandSymId &csid);
+ PdbSymUid(const PdbGlobalSymId &gsid);
+ PdbSymUid(const PdbTypeSymId &tsid);
+ PdbSymUid(const PdbFieldListMemberId &flmid);
+
+ uint64_t toOpaqueId() const { return m_repr; }
+
+ PdbSymUidKind kind() const;
+
+ PdbCompilandId asCompiland() const;
+ PdbCompilandSymId asCompilandSym() const;
+ PdbGlobalSymId asGlobalSym() const;
+ PdbTypeSymId asTypeSym() const;
+ PdbFieldListMemberId asFieldListMember() const;
+};
+
+template <typename T> uint64_t toOpaqueUid(const T &cid) {
+ return PdbSymUid(cid).toOpaqueId();
+}
+
+struct SymbolAndUid {
+ llvm::codeview::CVSymbol sym;
+ PdbSymUid uid;
+};
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
new file mode 100644
index 000000000000..1f5c97da81cf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
@@ -0,0 +1,877 @@
+//===-- PdbUtil.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PdbUtil.h"
+
+#include "DWARFLocationExpression.h"
+#include "PdbIndex.h"
+#include "PdbSymUid.h"
+
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+
+#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/lldb-enumerations.h"
+
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static Variable::RangeList
+MakeRangeList(const PdbIndex &index, const LocalVariableAddrRange &range,
+ llvm::ArrayRef<LocalVariableAddrGap> gaps) {
+ lldb::addr_t start =
+ index.MakeVirtualAddress(range.ISectStart, range.OffsetStart);
+ lldb::addr_t end = start + range.Range;
+
+ Variable::RangeList result;
+ while (!gaps.empty()) {
+ const LocalVariableAddrGap &gap = gaps.front();
+
+ lldb::addr_t size = gap.GapStartOffset - start;
+ result.Append(start, size);
+ start += gap.Range;
+ gaps = gaps.drop_front();
+ }
+
+ result.Append(start, end - start);
+ return result;
+}
+
+CVTagRecord CVTagRecord::create(CVType type) {
+ assert(IsTagRecord(type) && "type is not a tag record!");
+ switch (type.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE: {
+ ClassRecord cr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(type, cr));
+ return CVTagRecord(std::move(cr));
+ }
+ case LF_UNION: {
+ UnionRecord ur;
+ llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(type, ur));
+ return CVTagRecord(std::move(ur));
+ }
+ case LF_ENUM: {
+ EnumRecord er;
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(type, er));
+ return CVTagRecord(std::move(er));
+ }
+ default:
+ llvm_unreachable("Unreachable!");
+ }
+}
+
+CVTagRecord::CVTagRecord(ClassRecord &&c)
+ : cvclass(std::move(c)),
+ m_kind(cvclass.Kind == TypeRecordKind::Struct ? Struct : Class) {}
+CVTagRecord::CVTagRecord(UnionRecord &&u)
+ : cvunion(std::move(u)), m_kind(Union) {}
+CVTagRecord::CVTagRecord(EnumRecord &&e) : cvenum(std::move(e)), m_kind(Enum) {}
+
+PDB_SymType lldb_private::npdb::CVSymToPDBSym(SymbolKind kind) {
+ switch (kind) {
+ case S_COMPILE3:
+ case S_OBJNAME:
+ return PDB_SymType::CompilandDetails;
+ case S_ENVBLOCK:
+ return PDB_SymType::CompilandEnv;
+ case S_THUNK32:
+ case S_TRAMPOLINE:
+ return PDB_SymType::Thunk;
+ case S_COFFGROUP:
+ return PDB_SymType::CoffGroup;
+ case S_EXPORT:
+ return PDB_SymType::Export;
+ case S_LPROC32:
+ case S_GPROC32:
+ case S_LPROC32_DPC:
+ return PDB_SymType::Function;
+ case S_PUB32:
+ return PDB_SymType::PublicSymbol;
+ case S_INLINESITE:
+ return PDB_SymType::InlineSite;
+ case S_LOCAL:
+ case S_BPREL32:
+ case S_REGREL32:
+ case S_MANCONSTANT:
+ case S_CONSTANT:
+ case S_LDATA32:
+ case S_GDATA32:
+ case S_LMANDATA:
+ case S_GMANDATA:
+ case S_LTHREAD32:
+ case S_GTHREAD32:
+ return PDB_SymType::Data;
+ case S_BLOCK32:
+ return PDB_SymType::Block;
+ case S_LABEL32:
+ return PDB_SymType::Label;
+ case S_CALLSITEINFO:
+ return PDB_SymType::CallSite;
+ case S_HEAPALLOCSITE:
+ return PDB_SymType::HeapAllocationSite;
+ case S_CALLEES:
+ return PDB_SymType::Callee;
+ case S_CALLERS:
+ return PDB_SymType::Caller;
+ default:
+ lldbassert(false && "Invalid symbol record kind!");
+ }
+ return PDB_SymType::None;
+}
+
+PDB_SymType lldb_private::npdb::CVTypeToPDBType(TypeLeafKind kind) {
+ switch (kind) {
+ case LF_ARRAY:
+ return PDB_SymType::ArrayType;
+ case LF_ARGLIST:
+ return PDB_SymType::FunctionSig;
+ case LF_BCLASS:
+ return PDB_SymType::BaseClass;
+ case LF_BINTERFACE:
+ return PDB_SymType::BaseInterface;
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE:
+ case LF_UNION:
+ return PDB_SymType::UDT;
+ case LF_POINTER:
+ return PDB_SymType::PointerType;
+ case LF_ENUM:
+ return PDB_SymType::Enum;
+ case LF_PROCEDURE:
+ return PDB_SymType::FunctionSig;
+ case LF_BITFIELD:
+ return PDB_SymType::BuiltinType;
+ default:
+ lldbassert(false && "Invalid type record kind!");
+ }
+ return PDB_SymType::None;
+}
+
+bool lldb_private::npdb::SymbolHasAddress(const CVSymbol &sym) {
+ switch (sym.kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ case S_GPROC32_ID:
+ case S_LPROC32_ID:
+ case S_LPROC32_DPC:
+ case S_LPROC32_DPC_ID:
+ case S_THUNK32:
+ case S_TRAMPOLINE:
+ case S_COFFGROUP:
+ case S_BLOCK32:
+ case S_LABEL32:
+ case S_CALLSITEINFO:
+ case S_HEAPALLOCSITE:
+ case S_LDATA32:
+ case S_GDATA32:
+ case S_LMANDATA:
+ case S_GMANDATA:
+ case S_LTHREAD32:
+ case S_GTHREAD32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool lldb_private::npdb::SymbolIsCode(const CVSymbol &sym) {
+ switch (sym.kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ case S_GPROC32_ID:
+ case S_LPROC32_ID:
+ case S_LPROC32_DPC:
+ case S_LPROC32_DPC_ID:
+ case S_THUNK32:
+ case S_TRAMPOLINE:
+ case S_COFFGROUP:
+ case S_BLOCK32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+template <typename RecordT> RecordT createRecord(const CVSymbol &sym) {
+ RecordT record(static_cast<SymbolRecordKind>(sym.kind()));
+ cantFail(SymbolDeserializer::deserializeAs<RecordT>(sym, record));
+ return record;
+}
+
+template <typename RecordT>
+static SegmentOffset GetSegmentAndOffset(const CVSymbol &sym) {
+ RecordT record = createRecord<RecordT>(sym);
+ return {record.Segment, record.CodeOffset};
+}
+
+template <>
+SegmentOffset GetSegmentAndOffset<TrampolineSym>(const CVSymbol &sym) {
+ TrampolineSym record = createRecord<TrampolineSym>(sym);
+ return {record.ThunkSection, record.ThunkOffset};
+}
+
+template <> SegmentOffset GetSegmentAndOffset<Thunk32Sym>(const CVSymbol &sym) {
+ Thunk32Sym record = createRecord<Thunk32Sym>(sym);
+ return {record.Segment, record.Offset};
+}
+
+template <>
+SegmentOffset GetSegmentAndOffset<CoffGroupSym>(const CVSymbol &sym) {
+ CoffGroupSym record = createRecord<CoffGroupSym>(sym);
+ return {record.Segment, record.Offset};
+}
+
+template <> SegmentOffset GetSegmentAndOffset<DataSym>(const CVSymbol &sym) {
+ DataSym record = createRecord<DataSym>(sym);
+ return {record.Segment, record.DataOffset};
+}
+
+template <>
+SegmentOffset GetSegmentAndOffset<ThreadLocalDataSym>(const CVSymbol &sym) {
+ ThreadLocalDataSym record = createRecord<ThreadLocalDataSym>(sym);
+ return {record.Segment, record.DataOffset};
+}
+
+SegmentOffset lldb_private::npdb::GetSegmentAndOffset(const CVSymbol &sym) {
+ switch (sym.kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ case S_GPROC32_ID:
+ case S_LPROC32_ID:
+ case S_LPROC32_DPC:
+ case S_LPROC32_DPC_ID:
+ return ::GetSegmentAndOffset<ProcSym>(sym);
+ case S_THUNK32:
+ return ::GetSegmentAndOffset<Thunk32Sym>(sym);
+ break;
+ case S_TRAMPOLINE:
+ return ::GetSegmentAndOffset<TrampolineSym>(sym);
+ break;
+ case S_COFFGROUP:
+ return ::GetSegmentAndOffset<CoffGroupSym>(sym);
+ break;
+ case S_BLOCK32:
+ return ::GetSegmentAndOffset<BlockSym>(sym);
+ break;
+ case S_LABEL32:
+ return ::GetSegmentAndOffset<LabelSym>(sym);
+ break;
+ case S_CALLSITEINFO:
+ return ::GetSegmentAndOffset<CallSiteInfoSym>(sym);
+ break;
+ case S_HEAPALLOCSITE:
+ return ::GetSegmentAndOffset<HeapAllocationSiteSym>(sym);
+ break;
+ case S_LDATA32:
+ case S_GDATA32:
+ case S_LMANDATA:
+ case S_GMANDATA:
+ return ::GetSegmentAndOffset<DataSym>(sym);
+ break;
+ case S_LTHREAD32:
+ case S_GTHREAD32:
+ return ::GetSegmentAndOffset<ThreadLocalDataSym>(sym);
+ break;
+ default:
+ lldbassert(false && "Record does not have a segment/offset!");
+ }
+ return {0, 0};
+}
+
+template <typename RecordT>
+SegmentOffsetLength GetSegmentOffsetAndLength(const CVSymbol &sym) {
+ RecordT record = createRecord<RecordT>(sym);
+ return {record.Segment, record.CodeOffset, record.CodeSize};
+}
+
+template <>
+SegmentOffsetLength
+GetSegmentOffsetAndLength<TrampolineSym>(const CVSymbol &sym) {
+ TrampolineSym record = createRecord<TrampolineSym>(sym);
+ return {record.ThunkSection, record.ThunkOffset, record.Size};
+}
+
+template <>
+SegmentOffsetLength GetSegmentOffsetAndLength<Thunk32Sym>(const CVSymbol &sym) {
+ Thunk32Sym record = createRecord<Thunk32Sym>(sym);
+ return SegmentOffsetLength{record.Segment, record.Offset, record.Length};
+}
+
+template <>
+SegmentOffsetLength
+GetSegmentOffsetAndLength<CoffGroupSym>(const CVSymbol &sym) {
+ CoffGroupSym record = createRecord<CoffGroupSym>(sym);
+ return SegmentOffsetLength{record.Segment, record.Offset, record.Size};
+}
+
+SegmentOffsetLength
+lldb_private::npdb::GetSegmentOffsetAndLength(const CVSymbol &sym) {
+ switch (sym.kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ case S_GPROC32_ID:
+ case S_LPROC32_ID:
+ case S_LPROC32_DPC:
+ case S_LPROC32_DPC_ID:
+ return ::GetSegmentOffsetAndLength<ProcSym>(sym);
+ case S_THUNK32:
+ return ::GetSegmentOffsetAndLength<Thunk32Sym>(sym);
+ break;
+ case S_TRAMPOLINE:
+ return ::GetSegmentOffsetAndLength<TrampolineSym>(sym);
+ break;
+ case S_COFFGROUP:
+ return ::GetSegmentOffsetAndLength<CoffGroupSym>(sym);
+ break;
+ case S_BLOCK32:
+ return ::GetSegmentOffsetAndLength<BlockSym>(sym);
+ break;
+ default:
+ lldbassert(false && "Record does not have a segment/offset/length triple!");
+ }
+ return {0, 0, 0};
+}
+
+bool lldb_private::npdb::IsForwardRefUdt(CVType cvt) {
+ ClassRecord cr;
+ UnionRecord ur;
+ EnumRecord er;
+ switch (cvt.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE:
+ llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
+ return cr.isForwardRef();
+ case LF_UNION:
+ llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
+ return ur.isForwardRef();
+ case LF_ENUM:
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
+ return er.isForwardRef();
+ default:
+ return false;
+ }
+}
+
+bool lldb_private::npdb::IsTagRecord(llvm::codeview::CVType cvt) {
+ switch (cvt.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_UNION:
+ case LF_ENUM:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool lldb_private::npdb::IsClassStructUnion(llvm::codeview::CVType cvt) {
+ switch (cvt.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_UNION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool lldb_private::npdb::IsForwardRefUdt(const PdbTypeSymId &id,
+ TpiStream &tpi) {
+ if (id.is_ipi || id.index.isSimple())
+ return false;
+ return IsForwardRefUdt(tpi.getType(id.index));
+}
+
+bool lldb_private::npdb::IsTagRecord(const PdbTypeSymId &id, TpiStream &tpi) {
+ if (id.is_ipi || id.index.isSimple())
+ return false;
+ return IsTagRecord(tpi.getType(id.index));
+}
+
+lldb::AccessType
+lldb_private::npdb::TranslateMemberAccess(MemberAccess access) {
+ switch (access) {
+ case MemberAccess::Private:
+ return lldb::eAccessPrivate;
+ case MemberAccess::Protected:
+ return lldb::eAccessProtected;
+ case MemberAccess::Public:
+ return lldb::eAccessPublic;
+ case MemberAccess::None:
+ return lldb::eAccessNone;
+ }
+ llvm_unreachable("unreachable");
+}
+
+TypeIndex lldb_private::npdb::GetFieldListIndex(CVType cvt) {
+ switch (cvt.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE: {
+ ClassRecord cr;
+ cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
+ return cr.FieldList;
+ }
+ case LF_UNION: {
+ UnionRecord ur;
+ cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
+ return ur.FieldList;
+ }
+ case LF_ENUM: {
+ EnumRecord er;
+ cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
+ return er.FieldList;
+ }
+ default:
+ llvm_unreachable("Unreachable!");
+ }
+}
+
+TypeIndex lldb_private::npdb::LookThroughModifierRecord(CVType modifier) {
+ lldbassert(modifier.kind() == LF_MODIFIER);
+ ModifierRecord mr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ModifierRecord>(modifier, mr));
+ return mr.ModifiedType;
+}
+
+llvm::StringRef lldb_private::npdb::DropNameScope(llvm::StringRef name) {
+ return MSVCUndecoratedNameParser::DropScope(name);
+}
+
+VariableInfo lldb_private::npdb::GetVariableNameInfo(CVSymbol sym) {
+ VariableInfo result;
+
+ if (sym.kind() == S_REGREL32) {
+ RegRelativeSym reg(SymbolRecordKind::RegRelativeSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg));
+ result.type = reg.Type;
+ result.name = reg.Name;
+ return result;
+ }
+
+ if (sym.kind() == S_REGISTER) {
+ RegisterSym reg(SymbolRecordKind::RegisterSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
+ result.type = reg.Index;
+ result.name = reg.Name;
+ return result;
+ }
+
+ if (sym.kind() == S_LOCAL) {
+ LocalSym local(SymbolRecordKind::LocalSym);
+ cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local));
+ result.type = local.Type;
+ result.name = local.Name;
+ return result;
+ }
+
+ if (sym.kind() == S_GDATA32 || sym.kind() == S_LDATA32) {
+ DataSym data(SymbolRecordKind::DataSym);
+ cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym, data));
+ result.type = data.Type;
+ result.name = data.Name;
+ return result;
+ }
+
+ if (sym.kind() == S_GTHREAD32 || sym.kind() == S_LTHREAD32) {
+ ThreadLocalDataSym data(SymbolRecordKind::ThreadLocalDataSym);
+ cantFail(SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym, data));
+ result.type = data.Type;
+ result.name = data.Name;
+ return result;
+ }
+
+ if (sym.kind() == S_CONSTANT) {
+ ConstantSym constant(SymbolRecordKind::ConstantSym);
+ cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(sym, constant));
+ result.type = constant.Type;
+ result.name = constant.Name;
+ return result;
+ }
+
+ lldbassert(false && "Invalid variable record kind!");
+ return {};
+}
+
+static llvm::FixedStreamArray<FrameData>::Iterator
+GetCorrespondingFrameData(lldb::addr_t load_addr,
+ const DebugFrameDataSubsectionRef &fpo_data,
+ const Variable::RangeList &ranges) {
+ lldbassert(!ranges.IsEmpty());
+
+ // assume that all variable ranges correspond to one frame data
+ using RangeListEntry = Variable::RangeList::Entry;
+ const RangeListEntry &range = ranges.GetEntryRef(0);
+
+ auto it = fpo_data.begin();
+
+ // start by searching first frame data range containing variable range
+ for (; it != fpo_data.end(); ++it) {
+ RangeListEntry fd_range(load_addr + it->RvaStart, it->CodeSize);
+
+ if (fd_range.Contains(range)) {
+ break;
+ }
+ }
+
+ // then first most nested entry that still contains variable range
+ auto found = it;
+ for (; it != fpo_data.end(); ++it) {
+ RangeListEntry fd_range(load_addr + it->RvaStart, it->CodeSize);
+
+ if (!fd_range.Contains(range)) {
+ break;
+ }
+ found = it;
+ }
+
+ return found;
+}
+
+static bool GetFrameDataProgram(PdbIndex &index,
+ const Variable::RangeList &ranges,
+ llvm::StringRef &out_program) {
+ const DebugFrameDataSubsectionRef &new_fpo_data =
+ index.dbi().getNewFpoRecords();
+
+ auto frame_data_it =
+ GetCorrespondingFrameData(index.GetLoadAddress(), new_fpo_data, ranges);
+ if (frame_data_it == new_fpo_data.end())
+ return false;
+
+ PDBStringTable &strings = cantFail(index.pdb().getStringTable());
+ out_program = cantFail(strings.getStringForID(frame_data_it->FrameFunc));
+ return true;
+}
+
+static RegisterId GetBaseFrameRegister(PdbIndex &index,
+ PdbCompilandSymId frame_proc_id,
+ bool is_parameter) {
+ CVSymbol frame_proc_cvs = index.ReadSymbolRecord(frame_proc_id);
+ lldbassert(frame_proc_cvs.kind() == S_FRAMEPROC);
+
+ FrameProcSym frame_proc(SymbolRecordKind::FrameProcSym);
+ cantFail(SymbolDeserializer::deserializeAs<FrameProcSym>(frame_proc_cvs,
+ frame_proc));
+
+ CPUType cpu_type = index.compilands()
+ .GetCompiland(frame_proc_id.modi)
+ ->m_compile_opts->Machine;
+
+ return is_parameter ? frame_proc.getParamFramePtrReg(cpu_type)
+ : frame_proc.getLocalFramePtrReg(cpu_type);
+}
+
+VariableInfo lldb_private::npdb::GetVariableLocationInfo(
+ PdbIndex &index, PdbCompilandSymId var_id, Block &block,
+ lldb::ModuleSP module) {
+
+ CVSymbol sym = index.ReadSymbolRecord(var_id);
+
+ VariableInfo result = GetVariableNameInfo(sym);
+
+ if (sym.kind() == S_REGREL32) {
+ RegRelativeSym reg(SymbolRecordKind::RegRelativeSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg));
+ result.location =
+ MakeRegRelLocationExpression(reg.Register, reg.Offset, module);
+ result.ranges.emplace();
+ return result;
+ }
+
+ if (sym.kind() == S_REGISTER) {
+ RegisterSym reg(SymbolRecordKind::RegisterSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
+ result.location = MakeEnregisteredLocationExpression(reg.Register, module);
+ result.ranges.emplace();
+ return result;
+ }
+
+ if (sym.kind() == S_LOCAL) {
+ LocalSym local(SymbolRecordKind::LocalSym);
+ cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local));
+
+ PdbCompilandSymId loc_specifier_id(var_id.modi,
+ var_id.offset + sym.RecordData.size());
+ CVSymbol loc_specifier_cvs = index.ReadSymbolRecord(loc_specifier_id);
+ if (loc_specifier_cvs.kind() == S_DEFRANGE_FRAMEPOINTER_REL) {
+ DefRangeFramePointerRelSym loc(
+ SymbolRecordKind::DefRangeFramePointerRelSym);
+ cantFail(SymbolDeserializer::deserializeAs<DefRangeFramePointerRelSym>(
+ loc_specifier_cvs, loc));
+
+ Variable::RangeList ranges = MakeRangeList(index, loc.Range, loc.Gaps);
+
+ // TODO: may be better to pass function scope and not lookup it every
+ // time? find nearest parent function block
+ Block *cur = &block;
+ while (cur->GetParent()) {
+ cur = cur->GetParent();
+ }
+ PdbCompilandSymId func_scope_id =
+ PdbSymUid(cur->GetID()).asCompilandSym();
+ CVSymbol func_block_cvs = index.ReadSymbolRecord(func_scope_id);
+ lldbassert(func_block_cvs.kind() == S_GPROC32 ||
+ func_block_cvs.kind() == S_LPROC32);
+
+ PdbCompilandSymId frame_proc_id(
+ func_scope_id.modi, func_scope_id.offset + func_block_cvs.length());
+
+ bool is_parameter =
+ ((local.Flags & LocalSymFlags::IsParameter) != LocalSymFlags::None);
+ RegisterId base_reg =
+ GetBaseFrameRegister(index, frame_proc_id, is_parameter);
+
+ if (base_reg == RegisterId::VFRAME) {
+ llvm::StringRef program;
+ if (GetFrameDataProgram(index, ranges, program)) {
+ result.location =
+ MakeVFrameRelLocationExpression(program, loc.Offset, module);
+ result.ranges = std::move(ranges);
+ } else {
+ // invalid variable
+ }
+ } else {
+ result.location =
+ MakeRegRelLocationExpression(base_reg, loc.Offset, module);
+ result.ranges = std::move(ranges);
+ }
+ } else if (loc_specifier_cvs.kind() == S_DEFRANGE_REGISTER_REL) {
+ DefRangeRegisterRelSym loc(SymbolRecordKind::DefRangeRegisterRelSym);
+ cantFail(SymbolDeserializer::deserializeAs<DefRangeRegisterRelSym>(
+ loc_specifier_cvs, loc));
+
+ Variable::RangeList ranges = MakeRangeList(index, loc.Range, loc.Gaps);
+
+ RegisterId base_reg = (RegisterId)(uint16_t)loc.Hdr.Register;
+
+ if (base_reg == RegisterId::VFRAME) {
+ llvm::StringRef program;
+ if (GetFrameDataProgram(index, ranges, program)) {
+ result.location = MakeVFrameRelLocationExpression(
+ program, loc.Hdr.BasePointerOffset, module);
+ result.ranges = std::move(ranges);
+ } else {
+ // invalid variable
+ }
+ } else {
+ result.location = MakeRegRelLocationExpression(
+ base_reg, loc.Hdr.BasePointerOffset, module);
+ result.ranges = std::move(ranges);
+ }
+ }
+
+ // FIXME: Handle other kinds
+ return result;
+ }
+ llvm_unreachable("Symbol is not a local variable!");
+ return result;
+}
+
+lldb::BasicType
+lldb_private::npdb::GetCompilerTypeForSimpleKind(SimpleTypeKind kind) {
+ switch (kind) {
+ case SimpleTypeKind::Boolean128:
+ case SimpleTypeKind::Boolean16:
+ case SimpleTypeKind::Boolean32:
+ case SimpleTypeKind::Boolean64:
+ case SimpleTypeKind::Boolean8:
+ return lldb::eBasicTypeBool;
+ case SimpleTypeKind::Byte:
+ case SimpleTypeKind::UnsignedCharacter:
+ return lldb::eBasicTypeUnsignedChar;
+ case SimpleTypeKind::NarrowCharacter:
+ return lldb::eBasicTypeChar;
+ case SimpleTypeKind::SignedCharacter:
+ case SimpleTypeKind::SByte:
+ return lldb::eBasicTypeSignedChar;
+ case SimpleTypeKind::Character16:
+ return lldb::eBasicTypeChar16;
+ case SimpleTypeKind::Character32:
+ return lldb::eBasicTypeChar32;
+ case SimpleTypeKind::Complex80:
+ return lldb::eBasicTypeLongDoubleComplex;
+ case SimpleTypeKind::Complex64:
+ return lldb::eBasicTypeDoubleComplex;
+ case SimpleTypeKind::Complex32:
+ return lldb::eBasicTypeFloatComplex;
+ case SimpleTypeKind::Float128:
+ case SimpleTypeKind::Float80:
+ return lldb::eBasicTypeLongDouble;
+ case SimpleTypeKind::Float64:
+ return lldb::eBasicTypeDouble;
+ case SimpleTypeKind::Float32:
+ return lldb::eBasicTypeFloat;
+ case SimpleTypeKind::Float16:
+ return lldb::eBasicTypeHalf;
+ case SimpleTypeKind::Int128:
+ return lldb::eBasicTypeInt128;
+ case SimpleTypeKind::Int64:
+ case SimpleTypeKind::Int64Quad:
+ return lldb::eBasicTypeLongLong;
+ case SimpleTypeKind::Int32:
+ return lldb::eBasicTypeInt;
+ case SimpleTypeKind::Int16:
+ case SimpleTypeKind::Int16Short:
+ return lldb::eBasicTypeShort;
+ case SimpleTypeKind::UInt128:
+ return lldb::eBasicTypeUnsignedInt128;
+ case SimpleTypeKind::UInt64:
+ case SimpleTypeKind::UInt64Quad:
+ return lldb::eBasicTypeUnsignedLongLong;
+ case SimpleTypeKind::HResult:
+ case SimpleTypeKind::UInt32:
+ return lldb::eBasicTypeUnsignedInt;
+ case SimpleTypeKind::UInt16:
+ case SimpleTypeKind::UInt16Short:
+ return lldb::eBasicTypeUnsignedShort;
+ case SimpleTypeKind::Int32Long:
+ return lldb::eBasicTypeLong;
+ case SimpleTypeKind::UInt32Long:
+ return lldb::eBasicTypeUnsignedLong;
+ case SimpleTypeKind::Void:
+ return lldb::eBasicTypeVoid;
+ case SimpleTypeKind::WideCharacter:
+ return lldb::eBasicTypeWChar;
+ default:
+ return lldb::eBasicTypeInvalid;
+ }
+}
+
+size_t lldb_private::npdb::GetTypeSizeForSimpleKind(SimpleTypeKind kind) {
+ switch (kind) {
+ case SimpleTypeKind::Boolean128:
+ case SimpleTypeKind::Int128:
+ case SimpleTypeKind::UInt128:
+ case SimpleTypeKind::Float128:
+ return 16;
+ case SimpleTypeKind::Complex80:
+ case SimpleTypeKind::Float80:
+ return 10;
+ case SimpleTypeKind::Boolean64:
+ case SimpleTypeKind::Complex64:
+ case SimpleTypeKind::UInt64:
+ case SimpleTypeKind::UInt64Quad:
+ case SimpleTypeKind::Float64:
+ case SimpleTypeKind::Int64:
+ case SimpleTypeKind::Int64Quad:
+ return 8;
+ case SimpleTypeKind::Boolean32:
+ case SimpleTypeKind::Character32:
+ case SimpleTypeKind::Complex32:
+ case SimpleTypeKind::Float32:
+ case SimpleTypeKind::Int32:
+ case SimpleTypeKind::Int32Long:
+ case SimpleTypeKind::UInt32Long:
+ case SimpleTypeKind::HResult:
+ case SimpleTypeKind::UInt32:
+ return 4;
+ case SimpleTypeKind::Boolean16:
+ case SimpleTypeKind::Character16:
+ case SimpleTypeKind::Float16:
+ case SimpleTypeKind::Int16:
+ case SimpleTypeKind::Int16Short:
+ case SimpleTypeKind::UInt16:
+ case SimpleTypeKind::UInt16Short:
+ case SimpleTypeKind::WideCharacter:
+ return 2;
+ case SimpleTypeKind::Boolean8:
+ case SimpleTypeKind::Byte:
+ case SimpleTypeKind::UnsignedCharacter:
+ case SimpleTypeKind::NarrowCharacter:
+ case SimpleTypeKind::SignedCharacter:
+ case SimpleTypeKind::SByte:
+ return 1;
+ case SimpleTypeKind::Void:
+ default:
+ return 0;
+ }
+}
+
+PdbTypeSymId lldb_private::npdb::GetBestPossibleDecl(PdbTypeSymId id,
+ TpiStream &tpi) {
+ if (id.index.isSimple())
+ return id;
+
+ CVType cvt = tpi.getType(id.index);
+
+ // Only tag records have a best and a worst record.
+ if (!IsTagRecord(cvt))
+ return id;
+
+ // Tag records that are not forward decls are full decls, hence they are the
+ // best.
+ if (!IsForwardRefUdt(cvt))
+ return id;
+
+ return llvm::cantFail(tpi.findFullDeclForForwardRef(id.index));
+}
+
+template <typename RecordType> static size_t GetSizeOfTypeInternal(CVType cvt) {
+ RecordType record;
+ llvm::cantFail(TypeDeserializer::deserializeAs<RecordType>(cvt, record));
+ return record.getSize();
+}
+
+size_t lldb_private::npdb::GetSizeOfType(PdbTypeSymId id,
+ llvm::pdb::TpiStream &tpi) {
+ if (id.index.isSimple()) {
+ switch (id.index.getSimpleMode()) {
+ case SimpleTypeMode::Direct:
+ return GetTypeSizeForSimpleKind(id.index.getSimpleKind());
+ case SimpleTypeMode::NearPointer32:
+ case SimpleTypeMode::FarPointer32:
+ return 4;
+ case SimpleTypeMode::NearPointer64:
+ return 8;
+ case SimpleTypeMode::NearPointer128:
+ return 16;
+ default:
+ break;
+ }
+ return 0;
+ }
+
+ TypeIndex index = id.index;
+ if (IsForwardRefUdt(index, tpi))
+ index = llvm::cantFail(tpi.findFullDeclForForwardRef(index));
+
+ CVType cvt = tpi.getType(index);
+ switch (cvt.kind()) {
+ case LF_MODIFIER:
+ return GetSizeOfType({LookThroughModifierRecord(cvt)}, tpi);
+ case LF_ENUM: {
+ EnumRecord record;
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, record));
+ return GetSizeOfType({record.UnderlyingType}, tpi);
+ }
+ case LF_POINTER:
+ return GetSizeOfTypeInternal<PointerRecord>(cvt);
+ case LF_ARRAY:
+ return GetSizeOfTypeInternal<ArrayRecord>(cvt);
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE:
+ return GetSizeOfTypeInternal<ClassRecord>(cvt);
+ case LF_UNION:
+ return GetSizeOfTypeInternal<UnionRecord>(cvt);
+ default:
+ break;
+ }
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
new file mode 100644
index 000000000000..6f675b56dca4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
@@ -0,0 +1,158 @@
+//===-- PdbUtil.h -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBUTIL_H
+#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBUTIL_H
+
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+
+#include "PdbSymUid.h"
+
+#include <tuple>
+#include <utility>
+
+namespace llvm {
+namespace pdb {
+class TpiStream;
+}
+} // namespace llvm
+
+namespace lldb_private {
+namespace npdb {
+
+class PdbIndex;
+
+struct CVTagRecord {
+ enum Kind { Class, Struct, Union, Enum };
+
+ static CVTagRecord create(llvm::codeview::CVType type);
+
+ Kind kind() const { return m_kind; }
+
+ const llvm::codeview::TagRecord &asTag() const {
+ if (m_kind == Struct || m_kind == Class)
+ return cvclass;
+ if (m_kind == Enum)
+ return cvenum;
+ return cvunion;
+ }
+
+ const llvm::codeview::ClassRecord &asClass() const {
+ assert(m_kind == Struct || m_kind == Class);
+ return cvclass;
+ }
+
+ const llvm::codeview::EnumRecord &asEnum() const {
+ assert(m_kind == Enum);
+ return cvenum;
+ }
+
+ const llvm::codeview::UnionRecord &asUnion() const {
+ assert(m_kind == Union);
+ return cvunion;
+ }
+
+ llvm::StringRef name() const {
+ if (m_kind == Struct || m_kind == Union)
+ return cvclass.Name;
+ if (m_kind == Enum)
+ return cvenum.Name;
+ return cvunion.Name;
+ }
+
+private:
+ CVTagRecord(llvm::codeview::ClassRecord &&c);
+ CVTagRecord(llvm::codeview::UnionRecord &&u);
+ CVTagRecord(llvm::codeview::EnumRecord &&e);
+ union {
+ llvm::codeview::ClassRecord cvclass;
+ llvm::codeview::EnumRecord cvenum;
+ llvm::codeview::UnionRecord cvunion;
+ };
+ Kind m_kind;
+};
+
+struct SegmentOffset {
+ SegmentOffset() = default;
+ SegmentOffset(uint16_t s, uint32_t o) : segment(s), offset(o) {}
+ uint16_t segment = 0;
+ uint32_t offset = 0;
+};
+
+struct SegmentOffsetLength {
+ SegmentOffsetLength() = default;
+ SegmentOffsetLength(uint16_t s, uint32_t o, uint32_t l)
+ : so(s, o), length(l) {}
+ SegmentOffset so;
+ uint32_t length = 0;
+};
+
+struct VariableInfo {
+ llvm::StringRef name;
+ llvm::codeview::TypeIndex type;
+ llvm::Optional<DWARFExpression> location;
+ llvm::Optional<Variable::RangeList> ranges;
+};
+
+llvm::pdb::PDB_SymType CVSymToPDBSym(llvm::codeview::SymbolKind kind);
+llvm::pdb::PDB_SymType CVTypeToPDBType(llvm::codeview::TypeLeafKind kind);
+
+bool SymbolHasAddress(const llvm::codeview::CVSymbol &sym);
+bool SymbolIsCode(const llvm::codeview::CVSymbol &sym);
+
+SegmentOffset GetSegmentAndOffset(const llvm::codeview::CVSymbol &sym);
+SegmentOffsetLength
+GetSegmentOffsetAndLength(const llvm::codeview::CVSymbol &sym);
+
+template <typename RecordT> bool IsValidRecord(const RecordT &sym) {
+ return true;
+}
+
+inline bool IsValidRecord(const llvm::codeview::ProcRefSym &sym) {
+ // S_PROCREF symbols have 1-based module indices.
+ return sym.Module > 0;
+}
+
+bool IsForwardRefUdt(llvm::codeview::CVType cvt);
+bool IsTagRecord(llvm::codeview::CVType cvt);
+bool IsClassStructUnion(llvm::codeview::CVType cvt);
+
+bool IsForwardRefUdt(const PdbTypeSymId &id, llvm::pdb::TpiStream &tpi);
+bool IsTagRecord(const PdbTypeSymId &id, llvm::pdb::TpiStream &tpi);
+
+lldb::AccessType TranslateMemberAccess(llvm::codeview::MemberAccess access);
+llvm::codeview::TypeIndex GetFieldListIndex(llvm::codeview::CVType cvt);
+llvm::codeview::TypeIndex
+LookThroughModifierRecord(llvm::codeview::CVType modifier);
+
+llvm::StringRef DropNameScope(llvm::StringRef name);
+
+VariableInfo GetVariableNameInfo(llvm::codeview::CVSymbol symbol);
+VariableInfo GetVariableLocationInfo(PdbIndex &index, PdbCompilandSymId var_id, Block& block,
+ lldb::ModuleSP module);
+
+size_t GetTypeSizeForSimpleKind(llvm::codeview::SimpleTypeKind kind);
+lldb::BasicType
+GetCompilerTypeForSimpleKind(llvm::codeview::SimpleTypeKind kind);
+
+PdbTypeSymId GetBestPossibleDecl(PdbTypeSymId id, llvm::pdb::TpiStream &tpi);
+
+size_t GetSizeOfType(PdbTypeSymId id, llvm::pdb::TpiStream &tpi);
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
new file mode 100644
index 000000000000..e27d4699ae2f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -0,0 +1,1590 @@
+//===-- SymbolFileNativePDB.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolFileNativePDB.h"
+
+#include "clang/AST/Attr.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Type.h"
+
+#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamBuffer.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+
+#include "llvm/DebugInfo/CodeView/CVRecord.h"
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/RecordName.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+#include "llvm/Demangle/MicrosoftDemangle.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/BinaryStreamReader.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include "DWARFLocationExpression.h"
+#include "PdbAstBuilder.h"
+#include "PdbSymUid.h"
+#include "PdbUtil.h"
+#include "UdtRecordCompleter.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static lldb::LanguageType TranslateLanguage(PDB_Lang lang) {
+ switch (lang) {
+ case PDB_Lang::Cpp:
+ return lldb::LanguageType::eLanguageTypeC_plus_plus;
+ case PDB_Lang::C:
+ return lldb::LanguageType::eLanguageTypeC;
+ case PDB_Lang::Swift:
+ return lldb::LanguageType::eLanguageTypeSwift;
+ default:
+ return lldb::LanguageType::eLanguageTypeUnknown;
+ }
+}
+
+static std::unique_ptr<PDBFile> loadPDBFile(std::string PdbPath,
+ llvm::BumpPtrAllocator &Allocator) {
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ErrorOrBuffer =
+ llvm::MemoryBuffer::getFile(PdbPath, /*FileSize=*/-1,
+ /*RequiresNullTerminator=*/false);
+ if (!ErrorOrBuffer)
+ return nullptr;
+ std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(*ErrorOrBuffer);
+
+ llvm::StringRef Path = Buffer->getBufferIdentifier();
+ auto Stream = llvm::make_unique<llvm::MemoryBufferByteStream>(
+ std::move(Buffer), llvm::support::little);
+
+ auto File = llvm::make_unique<PDBFile>(Path, std::move(Stream), Allocator);
+ if (auto EC = File->parseFileHeaders()) {
+ llvm::consumeError(std::move(EC));
+ return nullptr;
+ }
+ if (auto EC = File->parseStreamData()) {
+ llvm::consumeError(std::move(EC));
+ return nullptr;
+ }
+
+ return File;
+}
+
+static std::unique_ptr<PDBFile>
+loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
+ // Try to find a matching PDB for an EXE.
+ using namespace llvm::object;
+ auto expected_binary = createBinary(exe_path);
+
+ // If the file isn't a PE/COFF executable, fail.
+ if (!expected_binary) {
+ llvm::consumeError(expected_binary.takeError());
+ return nullptr;
+ }
+ OwningBinary<Binary> binary = std::move(*expected_binary);
+
+ auto *obj = llvm::dyn_cast<llvm::object::COFFObjectFile>(binary.getBinary());
+ if (!obj)
+ return nullptr;
+ const llvm::codeview::DebugInfo *pdb_info = nullptr;
+
+ // If it doesn't have a debug directory, fail.
+ llvm::StringRef pdb_file;
+ auto ec = obj->getDebugPDBInfo(pdb_info, pdb_file);
+ if (ec)
+ return nullptr;
+
+ // if the file doesn't exist, is not a pdb, or doesn't have a matching guid,
+ // fail.
+ llvm::file_magic magic;
+ ec = llvm::identify_magic(pdb_file, magic);
+ if (ec || magic != llvm::file_magic::pdb)
+ return nullptr;
+ std::unique_ptr<PDBFile> pdb = loadPDBFile(pdb_file, allocator);
+ if (!pdb)
+ return nullptr;
+
+ auto expected_info = pdb->getPDBInfoStream();
+ if (!expected_info) {
+ llvm::consumeError(expected_info.takeError());
+ return nullptr;
+ }
+ llvm::codeview::GUID guid;
+ memcpy(&guid, pdb_info->PDB70.Signature, 16);
+
+ if (expected_info->getGuid() != guid)
+ return nullptr;
+ return pdb;
+}
+
+static bool IsFunctionPrologue(const CompilandIndexItem &cci,
+ lldb::addr_t addr) {
+ // FIXME: Implement this.
+ return false;
+}
+
+static bool IsFunctionEpilogue(const CompilandIndexItem &cci,
+ lldb::addr_t addr) {
+ // FIXME: Implement this.
+ return false;
+}
+
+static llvm::StringRef GetSimpleTypeName(SimpleTypeKind kind) {
+ switch (kind) {
+ case SimpleTypeKind::Boolean128:
+ case SimpleTypeKind::Boolean16:
+ case SimpleTypeKind::Boolean32:
+ case SimpleTypeKind::Boolean64:
+ case SimpleTypeKind::Boolean8:
+ return "bool";
+ case SimpleTypeKind::Byte:
+ case SimpleTypeKind::UnsignedCharacter:
+ return "unsigned char";
+ case SimpleTypeKind::NarrowCharacter:
+ return "char";
+ case SimpleTypeKind::SignedCharacter:
+ case SimpleTypeKind::SByte:
+ return "signed char";
+ case SimpleTypeKind::Character16:
+ return "char16_t";
+ case SimpleTypeKind::Character32:
+ return "char32_t";
+ case SimpleTypeKind::Complex80:
+ case SimpleTypeKind::Complex64:
+ case SimpleTypeKind::Complex32:
+ return "complex";
+ case SimpleTypeKind::Float128:
+ case SimpleTypeKind::Float80:
+ return "long double";
+ case SimpleTypeKind::Float64:
+ return "double";
+ case SimpleTypeKind::Float32:
+ return "float";
+ case SimpleTypeKind::Float16:
+ return "single";
+ case SimpleTypeKind::Int128:
+ return "__int128";
+ case SimpleTypeKind::Int64:
+ case SimpleTypeKind::Int64Quad:
+ return "int64_t";
+ case SimpleTypeKind::Int32:
+ return "int";
+ case SimpleTypeKind::Int16:
+ return "short";
+ case SimpleTypeKind::UInt128:
+ return "unsigned __int128";
+ case SimpleTypeKind::UInt64:
+ case SimpleTypeKind::UInt64Quad:
+ return "uint64_t";
+ case SimpleTypeKind::HResult:
+ return "HRESULT";
+ 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::Void:
+ return "void";
+ case SimpleTypeKind::WideCharacter:
+ return "wchar_t";
+ default:
+ return "";
+ }
+}
+
+static bool IsClassRecord(TypeLeafKind kind) {
+ switch (kind) {
+ case LF_STRUCTURE:
+ case LF_CLASS:
+ case LF_INTERFACE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void SymbolFileNativePDB::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+}
+
+void SymbolFileNativePDB::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+void SymbolFileNativePDB::DebuggerInitialize(Debugger &debugger) {}
+
+ConstString SymbolFileNativePDB::GetPluginNameStatic() {
+ static ConstString g_name("native-pdb");
+ return g_name;
+}
+
+const char *SymbolFileNativePDB::GetPluginDescriptionStatic() {
+ return "Microsoft PDB debug symbol cross-platform file reader.";
+}
+
+SymbolFile *SymbolFileNativePDB::CreateInstance(ObjectFile *obj_file) {
+ return new SymbolFileNativePDB(obj_file);
+}
+
+SymbolFileNativePDB::SymbolFileNativePDB(ObjectFile *object_file)
+ : SymbolFile(object_file) {}
+
+SymbolFileNativePDB::~SymbolFileNativePDB() {}
+
+uint32_t SymbolFileNativePDB::CalculateAbilities() {
+ uint32_t abilities = 0;
+ if (!m_obj_file)
+ return 0;
+
+ if (!m_index) {
+ // Lazily load and match the PDB file, but only do this once.
+ std::unique_ptr<PDBFile> file_up =
+ loadMatchingPDBFile(m_obj_file->GetFileSpec().GetPath(), m_allocator);
+
+ if (!file_up) {
+ auto module_sp = m_obj_file->GetModule();
+ if (!module_sp)
+ return 0;
+ // See if any symbol file is specified through `--symfile` option.
+ FileSpec symfile = module_sp->GetSymbolFileFileSpec();
+ if (!symfile)
+ return 0;
+ file_up = loadPDBFile(symfile.GetPath(), m_allocator);
+ }
+
+ if (!file_up)
+ return 0;
+
+ auto expected_index = PdbIndex::create(std::move(file_up));
+ if (!expected_index) {
+ llvm::consumeError(expected_index.takeError());
+ return 0;
+ }
+ m_index = std::move(*expected_index);
+ }
+ if (!m_index)
+ return 0;
+
+ // We don't especially have to be precise here. We only distinguish between
+ // stripped and not stripped.
+ abilities = kAllAbilities;
+
+ if (m_index->dbi().isStripped())
+ abilities &= ~(Blocks | LocalVariables);
+ return abilities;
+}
+
+void SymbolFileNativePDB::InitializeObject() {
+ m_obj_load_address = m_obj_file->GetBaseAddress().GetFileAddress();
+ m_index->SetLoadAddress(m_obj_load_address);
+ m_index->ParseSectionContribs();
+
+ TypeSystem *ts = m_obj_file->GetModule()->GetTypeSystemForLanguage(
+ lldb::eLanguageTypeC_plus_plus);
+ if (ts)
+ ts->SetSymbolFile(this);
+
+ m_ast = llvm::make_unique<PdbAstBuilder>(*m_obj_file, *m_index);
+}
+
+uint32_t SymbolFileNativePDB::GetNumCompileUnits() {
+ const DbiModuleList &modules = m_index->dbi().modules();
+ uint32_t count = modules.getModuleCount();
+ if (count == 0)
+ return count;
+
+ // The linker can inject an additional "dummy" compilation unit into the
+ // PDB. Ignore this special compile unit for our purposes, if it is there.
+ // It is always the last one.
+ DbiModuleDescriptor last = modules.getModuleDescriptor(count - 1);
+ if (last.getModuleName() == "* Linker *")
+ --count;
+ return count;
+}
+
+Block &SymbolFileNativePDB::CreateBlock(PdbCompilandSymId block_id) {
+ CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi);
+ CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset);
+
+ if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32) {
+ // This is a function. It must be global. Creating the Function entry for
+ // it automatically creates a block for it.
+ CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii);
+ return GetOrCreateFunction(block_id, *comp_unit)->GetBlock(false);
+ }
+
+ lldbassert(sym.kind() == S_BLOCK32);
+
+ // This is a block. Its parent is either a function or another block. In
+ // either case, its parent can be viewed as a block (e.g. a function contains
+ // 1 big block. So just get the parent block and add this block to it.
+ BlockSym block(static_cast<SymbolRecordKind>(sym.kind()));
+ cantFail(SymbolDeserializer::deserializeAs<BlockSym>(sym, block));
+ lldbassert(block.Parent != 0);
+ PdbCompilandSymId parent_id(block_id.modi, block.Parent);
+ Block &parent_block = GetOrCreateBlock(parent_id);
+ lldb::user_id_t opaque_block_uid = toOpaqueUid(block_id);
+ BlockSP child_block = std::make_shared<Block>(opaque_block_uid);
+ parent_block.AddChild(child_block);
+
+ m_ast->GetOrCreateBlockDecl(block_id);
+
+ m_blocks.insert({opaque_block_uid, child_block});
+ return *child_block;
+}
+
+lldb::FunctionSP SymbolFileNativePDB::CreateFunction(PdbCompilandSymId func_id,
+ CompileUnit &comp_unit) {
+ const CompilandIndexItem *cci =
+ m_index->compilands().GetCompiland(func_id.modi);
+ lldbassert(cci);
+ CVSymbol sym_record = cci->m_debug_stream.readSymbolAtOffset(func_id.offset);
+
+ lldbassert(sym_record.kind() == S_LPROC32 || sym_record.kind() == S_GPROC32);
+ SegmentOffsetLength sol = GetSegmentOffsetAndLength(sym_record);
+
+ auto file_vm_addr = m_index->MakeVirtualAddress(sol.so);
+ if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0)
+ return nullptr;
+
+ AddressRange func_range(file_vm_addr, sol.length,
+ comp_unit.GetModule()->GetSectionList());
+ if (!func_range.GetBaseAddress().IsValid())
+ return nullptr;
+
+ ProcSym proc(static_cast<SymbolRecordKind>(sym_record.kind()));
+ cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym_record, proc));
+ if (proc.FunctionType == TypeIndex::None())
+ return nullptr;
+ TypeSP func_type = GetOrCreateType(proc.FunctionType);
+ if (!func_type)
+ return nullptr;
+
+ PdbTypeSymId sig_id(proc.FunctionType, false);
+ Mangled mangled(proc.Name);
+ FunctionSP func_sp = std::make_shared<Function>(
+ &comp_unit, toOpaqueUid(func_id), toOpaqueUid(sig_id), mangled,
+ func_type.get(), func_range);
+
+ comp_unit.AddFunction(func_sp);
+
+ m_ast->GetOrCreateFunctionDecl(func_id);
+
+ return func_sp;
+}
+
+CompUnitSP
+SymbolFileNativePDB::CreateCompileUnit(const CompilandIndexItem &cci) {
+ lldb::LanguageType lang =
+ cci.m_compile_opts ? TranslateLanguage(cci.m_compile_opts->getLanguage())
+ : lldb::eLanguageTypeUnknown;
+
+ LazyBool optimized = eLazyBoolNo;
+ if (cci.m_compile_opts && cci.m_compile_opts->hasOptimizations())
+ optimized = eLazyBoolYes;
+
+ llvm::SmallString<64> source_file_name =
+ m_index->compilands().GetMainSourceFile(cci);
+ FileSpec fs(source_file_name);
+
+ CompUnitSP cu_sp =
+ std::make_shared<CompileUnit>(m_obj_file->GetModule(), nullptr, fs,
+ toOpaqueUid(cci.m_id), lang, optimized);
+
+ m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(
+ cci.m_id.modi, cu_sp);
+ return cu_sp;
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateModifierType(PdbTypeSymId type_id,
+ const ModifierRecord &mr,
+ CompilerType ct) {
+ TpiStream &stream = m_index->tpi();
+
+ std::string name;
+ if (mr.ModifiedType.isSimple())
+ name = GetSimpleTypeName(mr.ModifiedType.getSimpleKind());
+ else
+ name = computeTypeName(stream.typeCollection(), mr.ModifiedType);
+ Declaration decl;
+ lldb::TypeSP modified_type = GetOrCreateType(mr.ModifiedType);
+
+ return std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(name),
+ modified_type->GetByteSize(), nullptr,
+ LLDB_INVALID_UID, Type::eEncodingIsUID, decl,
+ ct, Type::eResolveStateFull);
+}
+
+lldb::TypeSP
+SymbolFileNativePDB::CreatePointerType(PdbTypeSymId type_id,
+ const llvm::codeview::PointerRecord &pr,
+ CompilerType ct) {
+ TypeSP pointee = GetOrCreateType(pr.ReferentType);
+ if (!pointee)
+ return nullptr;
+
+ if (pr.isPointerToMember()) {
+ MemberPointerInfo mpi = pr.getMemberInfo();
+ GetOrCreateType(mpi.ContainingType);
+ }
+
+ Declaration decl;
+ return std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(),
+ pr.getSize(), nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, decl, ct,
+ Type::eResolveStateFull);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateSimpleType(TypeIndex ti,
+ CompilerType ct) {
+ uint64_t uid = toOpaqueUid(PdbTypeSymId(ti, false));
+ if (ti == TypeIndex::NullptrT()) {
+ Declaration decl;
+ return std::make_shared<Type>(
+ uid, this, ConstString("std::nullptr_t"), 0, nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, decl, ct, Type::eResolveStateFull);
+ }
+
+ if (ti.getSimpleMode() != SimpleTypeMode::Direct) {
+ TypeSP direct_sp = GetOrCreateType(ti.makeDirect());
+ uint32_t pointer_size = 0;
+ switch (ti.getSimpleMode()) {
+ case SimpleTypeMode::FarPointer32:
+ case SimpleTypeMode::NearPointer32:
+ pointer_size = 4;
+ break;
+ case SimpleTypeMode::NearPointer64:
+ pointer_size = 8;
+ break;
+ default:
+ // 128-bit and 16-bit pointers unsupported.
+ return nullptr;
+ }
+ Declaration decl;
+ return std::make_shared<Type>(
+ uid, this, ConstString(), pointer_size, nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, decl, ct, Type::eResolveStateFull);
+ }
+
+ if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated)
+ return nullptr;
+
+ size_t size = GetTypeSizeForSimpleKind(ti.getSimpleKind());
+ llvm::StringRef type_name = GetSimpleTypeName(ti.getSimpleKind());
+
+ Declaration decl;
+ return std::make_shared<Type>(uid, this, ConstString(type_name), size,
+ nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID,
+ decl, ct, Type::eResolveStateFull);
+}
+
+static std::string GetUnqualifiedTypeName(const TagRecord &record) {
+ if (!record.hasUniqueName()) {
+ MSVCUndecoratedNameParser parser(record.Name);
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers();
+
+ return specs.back().GetBaseName();
+ }
+
+ llvm::ms_demangle::Demangler demangler;
+ StringView sv(record.UniqueName.begin(), record.UniqueName.size());
+ llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv);
+ if (demangler.Error)
+ return record.Name;
+
+ llvm::ms_demangle::IdentifierNode *idn =
+ ttn->QualifiedName->getUnqualifiedIdentifier();
+ return idn->toString();
+}
+
+lldb::TypeSP
+SymbolFileNativePDB::CreateClassStructUnion(PdbTypeSymId type_id,
+ const TagRecord &record,
+ size_t size, CompilerType ct) {
+
+ std::string uname = GetUnqualifiedTypeName(record);
+
+ // FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE.
+ Declaration decl;
+ return std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(uname),
+ size, nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, decl, ct,
+ Type::eResolveStateForward);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id,
+ const ClassRecord &cr,
+ CompilerType ct) {
+ return CreateClassStructUnion(type_id, cr, cr.getSize(), ct);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id,
+ const UnionRecord &ur,
+ CompilerType ct) {
+ return CreateClassStructUnion(type_id, ur, ur.getSize(), ct);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id,
+ const EnumRecord &er,
+ CompilerType ct) {
+ std::string uname = GetUnqualifiedTypeName(er);
+
+ Declaration decl;
+ TypeSP underlying_type = GetOrCreateType(er.UnderlyingType);
+
+ return std::make_shared<lldb_private::Type>(
+ toOpaqueUid(type_id), this, ConstString(uname),
+ underlying_type->GetByteSize(), nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, ct,
+ lldb_private::Type::eResolveStateForward);
+}
+
+TypeSP SymbolFileNativePDB::CreateArrayType(PdbTypeSymId type_id,
+ const ArrayRecord &ar,
+ CompilerType ct) {
+ TypeSP element_type = GetOrCreateType(ar.ElementType);
+
+ Declaration decl;
+ TypeSP array_sp = std::make_shared<lldb_private::Type>(
+ toOpaqueUid(type_id), this, ConstString(), ar.Size, nullptr,
+ LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, ct,
+ lldb_private::Type::eResolveStateFull);
+ array_sp->SetEncodingType(element_type.get());
+ return array_sp;
+}
+
+
+TypeSP SymbolFileNativePDB::CreateFunctionType(PdbTypeSymId type_id,
+ const MemberFunctionRecord &mfr,
+ CompilerType ct) {
+ Declaration decl;
+ return std::make_shared<lldb_private::Type>(
+ toOpaqueUid(type_id), this, ConstString(), 0, nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, ct,
+ lldb_private::Type::eResolveStateFull);
+}
+
+TypeSP SymbolFileNativePDB::CreateProcedureType(PdbTypeSymId type_id,
+ const ProcedureRecord &pr,
+ CompilerType ct) {
+ Declaration decl;
+ return std::make_shared<lldb_private::Type>(
+ toOpaqueUid(type_id), this, ConstString(), 0, nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, ct,
+ lldb_private::Type::eResolveStateFull);
+}
+
+TypeSP SymbolFileNativePDB::CreateType(PdbTypeSymId type_id, CompilerType ct) {
+ if (type_id.index.isSimple())
+ return CreateSimpleType(type_id.index, ct);
+
+ TpiStream &stream = type_id.is_ipi ? m_index->ipi() : m_index->tpi();
+ CVType cvt = stream.getType(type_id.index);
+
+ if (cvt.kind() == LF_MODIFIER) {
+ ModifierRecord modifier;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<ModifierRecord>(cvt, modifier));
+ return CreateModifierType(type_id, modifier, ct);
+ }
+
+ if (cvt.kind() == LF_POINTER) {
+ PointerRecord pointer;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<PointerRecord>(cvt, pointer));
+ return CreatePointerType(type_id, pointer, ct);
+ }
+
+ if (IsClassRecord(cvt.kind())) {
+ ClassRecord cr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
+ return CreateTagType(type_id, cr, ct);
+ }
+
+ if (cvt.kind() == LF_ENUM) {
+ EnumRecord er;
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
+ return CreateTagType(type_id, er, ct);
+ }
+
+ if (cvt.kind() == LF_UNION) {
+ UnionRecord ur;
+ llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
+ return CreateTagType(type_id, ur, ct);
+ }
+
+ if (cvt.kind() == LF_ARRAY) {
+ ArrayRecord ar;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ArrayRecord>(cvt, ar));
+ return CreateArrayType(type_id, ar, ct);
+ }
+
+ if (cvt.kind() == LF_PROCEDURE) {
+ ProcedureRecord pr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, pr));
+ return CreateProcedureType(type_id, pr, ct);
+ }
+ if (cvt.kind() == LF_MFUNCTION) {
+ MemberFunctionRecord mfr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<MemberFunctionRecord>(cvt, mfr));
+ return CreateFunctionType(type_id, mfr, ct);
+ }
+
+ return nullptr;
+}
+
+TypeSP SymbolFileNativePDB::CreateAndCacheType(PdbTypeSymId type_id) {
+ // If they search for a UDT which is a forward ref, try and resolve the full
+ // decl and just map the forward ref uid to the full decl record.
+ llvm::Optional<PdbTypeSymId> full_decl_uid;
+ if (IsForwardRefUdt(type_id, m_index->tpi())) {
+ auto expected_full_ti =
+ m_index->tpi().findFullDeclForForwardRef(type_id.index);
+ if (!expected_full_ti)
+ llvm::consumeError(expected_full_ti.takeError());
+ else if (*expected_full_ti != type_id.index) {
+ full_decl_uid = PdbTypeSymId(*expected_full_ti, false);
+
+ // It's possible that a lookup would occur for the full decl causing it
+ // to be cached, then a second lookup would occur for the forward decl.
+ // We don't want to create a second full decl, so make sure the full
+ // decl hasn't already been cached.
+ auto full_iter = m_types.find(toOpaqueUid(*full_decl_uid));
+ if (full_iter != m_types.end()) {
+ TypeSP result = full_iter->second;
+ // Map the forward decl to the TypeSP for the full decl so we can take
+ // the fast path next time.
+ m_types[toOpaqueUid(type_id)] = result;
+ return result;
+ }
+ }
+ }
+
+ PdbTypeSymId best_decl_id = full_decl_uid ? *full_decl_uid : type_id;
+
+ clang::QualType qt = m_ast->GetOrCreateType(best_decl_id);
+
+ TypeSP result = CreateType(best_decl_id, m_ast->ToCompilerType(qt));
+ if (!result)
+ return nullptr;
+
+ uint64_t best_uid = toOpaqueUid(best_decl_id);
+ m_types[best_uid] = result;
+ // If we had both a forward decl and a full decl, make both point to the new
+ // type.
+ if (full_decl_uid)
+ m_types[toOpaqueUid(type_id)] = result;
+
+ return result;
+}
+
+TypeSP SymbolFileNativePDB::GetOrCreateType(PdbTypeSymId type_id) {
+ // We can't use try_emplace / overwrite here because the process of creating
+ // a type could create nested types, which could invalidate iterators. So
+ // we have to do a 2-phase lookup / insert.
+ auto iter = m_types.find(toOpaqueUid(type_id));
+ if (iter != m_types.end())
+ return iter->second;
+
+ TypeSP type = CreateAndCacheType(type_id);
+ if (type)
+ m_obj_file->GetModule()->GetTypeList()->Insert(type);
+ return type;
+}
+
+VariableSP SymbolFileNativePDB::CreateGlobalVariable(PdbGlobalSymId var_id) {
+ CVSymbol sym = m_index->symrecords().readRecord(var_id.offset);
+ if (sym.kind() == S_CONSTANT)
+ return CreateConstantSymbol(var_id, sym);
+
+ lldb::ValueType scope = eValueTypeInvalid;
+ TypeIndex ti;
+ llvm::StringRef name;
+ lldb::addr_t addr = 0;
+ uint16_t section = 0;
+ uint32_t offset = 0;
+ bool is_external = false;
+ switch (sym.kind()) {
+ case S_GDATA32:
+ is_external = true;
+ LLVM_FALLTHROUGH;
+ case S_LDATA32: {
+ DataSym ds(sym.kind());
+ llvm::cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym, ds));
+ ti = ds.Type;
+ scope = (sym.kind() == S_GDATA32) ? eValueTypeVariableGlobal
+ : eValueTypeVariableStatic;
+ name = ds.Name;
+ section = ds.Segment;
+ offset = ds.DataOffset;
+ addr = m_index->MakeVirtualAddress(ds.Segment, ds.DataOffset);
+ break;
+ }
+ case S_GTHREAD32:
+ is_external = true;
+ LLVM_FALLTHROUGH;
+ case S_LTHREAD32: {
+ ThreadLocalDataSym tlds(sym.kind());
+ llvm::cantFail(
+ SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym, tlds));
+ ti = tlds.Type;
+ name = tlds.Name;
+ section = tlds.Segment;
+ offset = tlds.DataOffset;
+ addr = m_index->MakeVirtualAddress(tlds.Segment, tlds.DataOffset);
+ scope = eValueTypeVariableThreadLocal;
+ break;
+ }
+ default:
+ llvm_unreachable("unreachable!");
+ }
+
+ CompUnitSP comp_unit;
+ llvm::Optional<uint16_t> modi = m_index->GetModuleIndexForVa(addr);
+ if (modi) {
+ CompilandIndexItem &cci = m_index->compilands().GetOrCreateCompiland(*modi);
+ comp_unit = GetOrCreateCompileUnit(cci);
+ }
+
+ Declaration decl;
+ PdbTypeSymId tid(ti, false);
+ SymbolFileTypeSP type_sp =
+ std::make_shared<SymbolFileType>(*this, toOpaqueUid(tid));
+ Variable::RangeList ranges;
+
+ m_ast->GetOrCreateVariableDecl(var_id);
+
+ DWARFExpression location = MakeGlobalLocationExpression(
+ section, offset, GetObjectFile()->GetModule());
+
+ std::string global_name("::");
+ global_name += name;
+ VariableSP var_sp = std::make_shared<Variable>(
+ toOpaqueUid(var_id), name.str().c_str(), global_name.c_str(), type_sp,
+ scope, comp_unit.get(), ranges, &decl, location, is_external, false,
+ false);
+ var_sp->SetLocationIsConstantValueData(false);
+
+ return var_sp;
+}
+
+lldb::VariableSP
+SymbolFileNativePDB::CreateConstantSymbol(PdbGlobalSymId var_id,
+ const CVSymbol &cvs) {
+ TpiStream &tpi = m_index->tpi();
+ ConstantSym constant(cvs.kind());
+
+ llvm::cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(cvs, constant));
+ std::string global_name("::");
+ global_name += constant.Name;
+ PdbTypeSymId tid(constant.Type, false);
+ SymbolFileTypeSP type_sp =
+ std::make_shared<SymbolFileType>(*this, toOpaqueUid(tid));
+
+ Declaration decl;
+ Variable::RangeList ranges;
+ ModuleSP module = GetObjectFile()->GetModule();
+ DWARFExpression location = MakeConstantLocationExpression(
+ constant.Type, tpi, constant.Value, module);
+
+ VariableSP var_sp = std::make_shared<Variable>(
+ toOpaqueUid(var_id), constant.Name.str().c_str(), global_name.c_str(),
+ type_sp, eValueTypeVariableGlobal, module.get(), ranges, &decl, location,
+ false, false, false);
+ var_sp->SetLocationIsConstantValueData(true);
+ return var_sp;
+}
+
+VariableSP
+SymbolFileNativePDB::GetOrCreateGlobalVariable(PdbGlobalSymId var_id) {
+ auto emplace_result = m_global_vars.try_emplace(toOpaqueUid(var_id), nullptr);
+ if (emplace_result.second)
+ emplace_result.first->second = CreateGlobalVariable(var_id);
+
+ return emplace_result.first->second;
+}
+
+lldb::TypeSP SymbolFileNativePDB::GetOrCreateType(TypeIndex ti) {
+ return GetOrCreateType(PdbTypeSymId(ti, false));
+}
+
+FunctionSP SymbolFileNativePDB::GetOrCreateFunction(PdbCompilandSymId func_id,
+ CompileUnit &comp_unit) {
+ auto emplace_result = m_functions.try_emplace(toOpaqueUid(func_id), nullptr);
+ if (emplace_result.second)
+ emplace_result.first->second = CreateFunction(func_id, comp_unit);
+
+ return emplace_result.first->second;
+}
+
+CompUnitSP
+SymbolFileNativePDB::GetOrCreateCompileUnit(const CompilandIndexItem &cci) {
+
+ auto emplace_result =
+ m_compilands.try_emplace(toOpaqueUid(cci.m_id), nullptr);
+ if (emplace_result.second)
+ emplace_result.first->second = CreateCompileUnit(cci);
+
+ lldbassert(emplace_result.first->second);
+ return emplace_result.first->second;
+}
+
+Block &SymbolFileNativePDB::GetOrCreateBlock(PdbCompilandSymId block_id) {
+ auto iter = m_blocks.find(toOpaqueUid(block_id));
+ if (iter != m_blocks.end())
+ return *iter->second;
+
+ return CreateBlock(block_id);
+}
+
+void SymbolFileNativePDB::ParseDeclsForContext(
+ lldb_private::CompilerDeclContext decl_ctx) {
+ clang::DeclContext *context = m_ast->FromCompilerDeclContext(decl_ctx);
+ if (!context)
+ return;
+ m_ast->ParseDeclsForContext(*context);
+}
+
+lldb::CompUnitSP SymbolFileNativePDB::ParseCompileUnitAtIndex(uint32_t index) {
+ if (index >= GetNumCompileUnits())
+ return CompUnitSP();
+ lldbassert(index < UINT16_MAX);
+ if (index >= UINT16_MAX)
+ return nullptr;
+
+ CompilandIndexItem &item = m_index->compilands().GetOrCreateCompiland(index);
+
+ return GetOrCreateCompileUnit(item);
+}
+
+lldb::LanguageType SymbolFileNativePDB::ParseLanguage(CompileUnit &comp_unit) {
+ PdbSymUid uid(comp_unit.GetID());
+ lldbassert(uid.kind() == PdbSymUidKind::Compiland);
+
+ CompilandIndexItem *item =
+ m_index->compilands().GetCompiland(uid.asCompiland().modi);
+ lldbassert(item);
+ if (!item->m_compile_opts)
+ return lldb::eLanguageTypeUnknown;
+
+ return TranslateLanguage(item->m_compile_opts->getLanguage());
+}
+
+void SymbolFileNativePDB::AddSymbols(Symtab &symtab) { return; }
+
+size_t SymbolFileNativePDB::ParseFunctions(CompileUnit &comp_unit) {
+ PdbSymUid uid{comp_unit.GetID()};
+ lldbassert(uid.kind() == PdbSymUidKind::Compiland);
+ uint16_t modi = uid.asCompiland().modi;
+ CompilandIndexItem &cii = m_index->compilands().GetOrCreateCompiland(modi);
+
+ size_t count = comp_unit.GetNumFunctions();
+ const CVSymbolArray &syms = cii.m_debug_stream.getSymbolArray();
+ for (auto iter = syms.begin(); iter != syms.end(); ++iter) {
+ if (iter->kind() != S_LPROC32 && iter->kind() != S_GPROC32)
+ continue;
+
+ PdbCompilandSymId sym_id{modi, iter.offset()};
+
+ FunctionSP func = GetOrCreateFunction(sym_id, comp_unit);
+ }
+
+ size_t new_count = comp_unit.GetNumFunctions();
+ lldbassert(new_count >= count);
+ return new_count - count;
+}
+
+static bool NeedsResolvedCompileUnit(uint32_t resolve_scope) {
+ // If any of these flags are set, we need to resolve the compile unit.
+ uint32_t flags = eSymbolContextCompUnit;
+ flags |= eSymbolContextVariable;
+ flags |= eSymbolContextFunction;
+ flags |= eSymbolContextBlock;
+ flags |= eSymbolContextLineEntry;
+ return (resolve_scope & flags) != 0;
+}
+
+uint32_t SymbolFileNativePDB::ResolveSymbolContext(
+ const Address &addr, SymbolContextItem resolve_scope, SymbolContext &sc) {
+ uint32_t resolved_flags = 0;
+ lldb::addr_t file_addr = addr.GetFileAddress();
+
+ if (NeedsResolvedCompileUnit(resolve_scope)) {
+ llvm::Optional<uint16_t> modi = m_index->GetModuleIndexForVa(file_addr);
+ if (!modi)
+ return 0;
+ CompilandIndexItem *cci = m_index->compilands().GetCompiland(*modi);
+ if (!cci)
+ return 0;
+
+ sc.comp_unit = GetOrCreateCompileUnit(*cci).get();
+ resolved_flags |= eSymbolContextCompUnit;
+ }
+
+ if (resolve_scope & eSymbolContextFunction ||
+ resolve_scope & eSymbolContextBlock) {
+ lldbassert(sc.comp_unit);
+ std::vector<SymbolAndUid> matches = m_index->FindSymbolsByVa(file_addr);
+ // Search the matches in reverse. This way if there are multiple matches
+ // (for example we are 3 levels deep in a nested scope) it will find the
+ // innermost one first.
+ for (const auto &match : llvm::reverse(matches)) {
+ if (match.uid.kind() != PdbSymUidKind::CompilandSym)
+ continue;
+
+ PdbCompilandSymId csid = match.uid.asCompilandSym();
+ CVSymbol cvs = m_index->ReadSymbolRecord(csid);
+ PDB_SymType type = CVSymToPDBSym(cvs.kind());
+ if (type != PDB_SymType::Function && type != PDB_SymType::Block)
+ continue;
+ if (type == PDB_SymType::Function) {
+ sc.function = GetOrCreateFunction(csid, *sc.comp_unit).get();
+ sc.block = sc.GetFunctionBlock();
+ }
+
+ if (type == PDB_SymType::Block) {
+ sc.block = &GetOrCreateBlock(csid);
+ sc.function = sc.block->CalculateSymbolContextFunction();
+ }
+ resolved_flags |= eSymbolContextFunction;
+ resolved_flags |= eSymbolContextBlock;
+ break;
+ }
+ }
+
+ if (resolve_scope & eSymbolContextLineEntry) {
+ lldbassert(sc.comp_unit);
+ if (auto *line_table = sc.comp_unit->GetLineTable()) {
+ if (line_table->FindLineEntryByAddress(addr, sc.line_entry))
+ resolved_flags |= eSymbolContextLineEntry;
+ }
+ }
+
+ return resolved_flags;
+}
+
+uint32_t SymbolFileNativePDB::ResolveSymbolContext(
+ const FileSpec &file_spec, uint32_t line, bool check_inlines,
+ lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
+ return 0;
+}
+
+static void AppendLineEntryToSequence(LineTable &table, LineSequence &sequence,
+ const CompilandIndexItem &cci,
+ lldb::addr_t base_addr,
+ uint32_t file_number,
+ const LineFragmentHeader &block,
+ const LineNumberEntry &cur) {
+ LineInfo cur_info(cur.Flags);
+
+ if (cur_info.isAlwaysStepInto() || cur_info.isNeverStepInto())
+ return;
+
+ uint64_t addr = base_addr + cur.Offset;
+
+ bool is_statement = cur_info.isStatement();
+ bool is_prologue = IsFunctionPrologue(cci, addr);
+ bool is_epilogue = IsFunctionEpilogue(cci, addr);
+
+ uint32_t lno = cur_info.getStartLine();
+
+ table.AppendLineEntryToSequence(&sequence, addr, lno, 0, file_number,
+ is_statement, false, is_prologue, is_epilogue,
+ false);
+}
+
+static void TerminateLineSequence(LineTable &table,
+ const LineFragmentHeader &block,
+ lldb::addr_t base_addr, uint32_t file_number,
+ uint32_t last_line,
+ std::unique_ptr<LineSequence> seq) {
+ // The end is always a terminal entry, so insert it regardless.
+ table.AppendLineEntryToSequence(seq.get(), base_addr + block.CodeSize,
+ last_line, 0, file_number, false, false,
+ false, false, true);
+ table.InsertSequence(seq.release());
+}
+
+bool SymbolFileNativePDB::ParseLineTable(CompileUnit &comp_unit) {
+ // Unfortunately LLDB is set up to parse the entire compile unit line table
+ // all at once, even if all it really needs is line info for a specific
+ // function. In the future it would be nice if it could set the sc.m_function
+ // member, and we could only get the line info for the function in question.
+ PdbSymUid cu_id(comp_unit.GetID());
+ lldbassert(cu_id.kind() == PdbSymUidKind::Compiland);
+ CompilandIndexItem *cci =
+ m_index->compilands().GetCompiland(cu_id.asCompiland().modi);
+ lldbassert(cci);
+ auto line_table = llvm::make_unique<LineTable>(&comp_unit);
+
+ // This is basically a copy of the .debug$S subsections from all original COFF
+ // object files merged together with address relocations applied. We are
+ // looking for all DEBUG_S_LINES subsections.
+ for (const DebugSubsectionRecord &dssr :
+ cci->m_debug_stream.getSubsectionsArray()) {
+ if (dssr.kind() != DebugSubsectionKind::Lines)
+ continue;
+
+ DebugLinesSubsectionRef lines;
+ llvm::BinaryStreamReader reader(dssr.getRecordData());
+ if (auto EC = lines.initialize(reader)) {
+ llvm::consumeError(std::move(EC));
+ return false;
+ }
+
+ const LineFragmentHeader *lfh = lines.header();
+ uint64_t virtual_addr =
+ m_index->MakeVirtualAddress(lfh->RelocSegment, lfh->RelocOffset);
+
+ const auto &checksums = cci->m_strings.checksums().getArray();
+ const auto &strings = cci->m_strings.strings();
+ for (const LineColumnEntry &group : lines) {
+ // Indices in this structure are actually offsets of records in the
+ // DEBUG_S_FILECHECKSUMS subsection. Those entries then have an index
+ // into the global PDB string table.
+ auto iter = checksums.at(group.NameIndex);
+ if (iter == checksums.end())
+ continue;
+
+ llvm::Expected<llvm::StringRef> efn =
+ strings.getString(iter->FileNameOffset);
+ if (!efn) {
+ llvm::consumeError(efn.takeError());
+ continue;
+ }
+
+ // LLDB wants the index of the file in the list of support files.
+ auto fn_iter = llvm::find(cci->m_file_list, *efn);
+ lldbassert(fn_iter != cci->m_file_list.end());
+ // LLDB support file indices are 1-based.
+ uint32_t file_index =
+ 1 + std::distance(cci->m_file_list.begin(), fn_iter);
+
+ std::unique_ptr<LineSequence> sequence(
+ line_table->CreateLineSequenceContainer());
+ lldbassert(!group.LineNumbers.empty());
+
+ for (const LineNumberEntry &entry : group.LineNumbers) {
+ AppendLineEntryToSequence(*line_table, *sequence, *cci, virtual_addr,
+ file_index, *lfh, entry);
+ }
+ LineInfo last_line(group.LineNumbers.back().Flags);
+ TerminateLineSequence(*line_table, *lfh, virtual_addr, file_index,
+ last_line.getEndLine(), std::move(sequence));
+ }
+ }
+
+ if (line_table->GetSize() == 0)
+ return false;
+
+ comp_unit.SetLineTable(line_table.release());
+ return true;
+}
+
+bool SymbolFileNativePDB::ParseDebugMacros(CompileUnit &comp_unit) {
+ // PDB doesn't contain information about macros
+ return false;
+}
+
+bool SymbolFileNativePDB::ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) {
+ PdbSymUid cu_id(comp_unit.GetID());
+ lldbassert(cu_id.kind() == PdbSymUidKind::Compiland);
+ CompilandIndexItem *cci =
+ m_index->compilands().GetCompiland(cu_id.asCompiland().modi);
+ lldbassert(cci);
+
+ for (llvm::StringRef f : cci->m_file_list) {
+ FileSpec::Style style =
+ f.startswith("/") ? FileSpec::Style::posix : FileSpec::Style::windows;
+ FileSpec spec(f, style);
+ support_files.Append(spec);
+ }
+
+ llvm::SmallString<64> main_source_file =
+ m_index->compilands().GetMainSourceFile(*cci);
+ FileSpec::Style style = main_source_file.startswith("/")
+ ? FileSpec::Style::posix
+ : FileSpec::Style::windows;
+ FileSpec spec(main_source_file, style);
+ support_files.Insert(0, spec);
+ return true;
+}
+
+bool SymbolFileNativePDB::ParseImportedModules(
+ const SymbolContext &sc, std::vector<SourceModule> &imported_modules) {
+ // PDB does not yet support module debug info
+ return false;
+}
+
+size_t SymbolFileNativePDB::ParseBlocksRecursive(Function &func) {
+ GetOrCreateBlock(PdbSymUid(func.GetID()).asCompilandSym());
+ // FIXME: Parse child blocks
+ return 1;
+}
+
+void SymbolFileNativePDB::DumpClangAST(Stream &s) { m_ast->Dump(s); }
+
+uint32_t SymbolFileNativePDB::FindGlobalVariables(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, VariableList &variables) {
+ using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
+
+ std::vector<SymbolAndOffset> results = m_index->globals().findRecordsByName(
+ name.GetStringRef(), m_index->symrecords());
+ for (const SymbolAndOffset &result : results) {
+ VariableSP var;
+ switch (result.second.kind()) {
+ case SymbolKind::S_GDATA32:
+ case SymbolKind::S_LDATA32:
+ case SymbolKind::S_GTHREAD32:
+ case SymbolKind::S_LTHREAD32:
+ case SymbolKind::S_CONSTANT: {
+ PdbGlobalSymId global(result.first, false);
+ var = GetOrCreateGlobalVariable(global);
+ variables.AddVariable(var);
+ break;
+ }
+ default:
+ continue;
+ }
+ }
+ return variables.GetSize();
+}
+
+uint32_t SymbolFileNativePDB::FindFunctions(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask, bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ // For now we only support lookup by method name.
+ if (!(name_type_mask & eFunctionNameTypeMethod))
+ return 0;
+
+ using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
+
+ std::vector<SymbolAndOffset> matches = m_index->globals().findRecordsByName(
+ name.GetStringRef(), m_index->symrecords());
+ for (const SymbolAndOffset &match : matches) {
+ if (match.second.kind() != S_PROCREF && match.second.kind() != S_LPROCREF)
+ continue;
+ ProcRefSym proc(match.second.kind());
+ cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(match.second, proc));
+
+ if (!IsValidRecord(proc))
+ continue;
+
+ CompilandIndexItem &cci =
+ m_index->compilands().GetOrCreateCompiland(proc.modi());
+ SymbolContext sc;
+
+ sc.comp_unit = GetOrCreateCompileUnit(cci).get();
+ PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
+ sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
+
+ sc_list.Append(sc);
+ }
+
+ return sc_list.GetSize();
+}
+
+uint32_t SymbolFileNativePDB::FindFunctions(const RegularExpression &regex,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ return 0;
+}
+
+uint32_t SymbolFileNativePDB::FindTypes(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<SymbolFile *> &searched_symbol_files, TypeMap &types) {
+ if (!append)
+ types.Clear();
+ if (!name)
+ return 0;
+
+ searched_symbol_files.clear();
+ searched_symbol_files.insert(this);
+
+ // There is an assumption 'name' is not a regex
+ size_t match_count = FindTypesByName(name.GetStringRef(), max_matches, types);
+
+ return match_count;
+}
+
+size_t
+SymbolFileNativePDB::FindTypes(const std::vector<CompilerContext> &context,
+ bool append, TypeMap &types) {
+ return 0;
+}
+
+size_t SymbolFileNativePDB::FindTypesByName(llvm::StringRef name,
+ uint32_t max_matches,
+ TypeMap &types) {
+
+ size_t match_count = 0;
+ std::vector<TypeIndex> matches = m_index->tpi().findRecordsByName(name);
+ if (max_matches > 0 && max_matches < matches.size())
+ matches.resize(max_matches);
+
+ for (TypeIndex ti : matches) {
+ TypeSP type = GetOrCreateType(ti);
+ if (!type)
+ continue;
+
+ types.Insert(type);
+ ++match_count;
+ }
+ return match_count;
+}
+
+size_t SymbolFileNativePDB::ParseTypes(CompileUnit &comp_unit) {
+ // Only do the full type scan the first time.
+ if (m_done_full_type_scan)
+ return 0;
+
+ size_t old_count = m_obj_file->GetModule()->GetTypeList()->GetSize();
+ LazyRandomTypeCollection &types = m_index->tpi().typeCollection();
+
+ // First process the entire TPI stream.
+ for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) {
+ TypeSP type = GetOrCreateType(*ti);
+ if (type)
+ (void)type->GetFullCompilerType();
+ }
+
+ // Next look for S_UDT records in the globals stream.
+ for (const uint32_t gid : m_index->globals().getGlobalsTable()) {
+ PdbGlobalSymId global{gid, false};
+ CVSymbol sym = m_index->ReadSymbolRecord(global);
+ if (sym.kind() != S_UDT)
+ continue;
+
+ UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym));
+ bool is_typedef = true;
+ if (IsTagRecord(PdbTypeSymId{udt.Type, false}, m_index->tpi())) {
+ CVType cvt = m_index->tpi().getType(udt.Type);
+ llvm::StringRef name = CVTagRecord::create(cvt).name();
+ if (name == udt.Name)
+ is_typedef = false;
+ }
+
+ if (is_typedef)
+ GetOrCreateTypedef(global);
+ }
+
+ size_t new_count = m_obj_file->GetModule()->GetTypeList()->GetSize();
+
+ m_done_full_type_scan = true;
+
+ return new_count - old_count;
+}
+
+size_t
+SymbolFileNativePDB::ParseVariablesForCompileUnit(CompileUnit &comp_unit,
+ VariableList &variables) {
+ PdbSymUid sym_uid(comp_unit.GetID());
+ lldbassert(sym_uid.kind() == PdbSymUidKind::Compiland);
+ return 0;
+}
+
+VariableSP SymbolFileNativePDB::CreateLocalVariable(PdbCompilandSymId scope_id,
+ PdbCompilandSymId var_id,
+ bool is_param) {
+ ModuleSP module = GetObjectFile()->GetModule();
+ Block &block = GetOrCreateBlock(scope_id);
+ VariableInfo var_info =
+ GetVariableLocationInfo(*m_index, var_id, block, module);
+ if (!var_info.location || !var_info.ranges)
+ return nullptr;
+
+ CompilandIndexItem *cii = m_index->compilands().GetCompiland(var_id.modi);
+ CompUnitSP comp_unit_sp = GetOrCreateCompileUnit(*cii);
+ TypeSP type_sp = GetOrCreateType(var_info.type);
+ std::string name = var_info.name.str();
+ Declaration decl;
+ SymbolFileTypeSP sftype =
+ std::make_shared<SymbolFileType>(*this, type_sp->GetID());
+
+ ValueType var_scope =
+ is_param ? eValueTypeVariableArgument : eValueTypeVariableLocal;
+ VariableSP var_sp = std::make_shared<Variable>(
+ toOpaqueUid(var_id), name.c_str(), name.c_str(), sftype, var_scope,
+ comp_unit_sp.get(), *var_info.ranges, &decl, *var_info.location, false,
+ false, false);
+
+ if (!is_param)
+ m_ast->GetOrCreateVariableDecl(scope_id, var_id);
+
+ m_local_variables[toOpaqueUid(var_id)] = var_sp;
+ return var_sp;
+}
+
+VariableSP SymbolFileNativePDB::GetOrCreateLocalVariable(
+ PdbCompilandSymId scope_id, PdbCompilandSymId var_id, bool is_param) {
+ auto iter = m_local_variables.find(toOpaqueUid(var_id));
+ if (iter != m_local_variables.end())
+ return iter->second;
+
+ return CreateLocalVariable(scope_id, var_id, is_param);
+}
+
+TypeSP SymbolFileNativePDB::CreateTypedef(PdbGlobalSymId id) {
+ CVSymbol sym = m_index->ReadSymbolRecord(id);
+ lldbassert(sym.kind() == SymbolKind::S_UDT);
+
+ UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym));
+
+ TypeSP target_type = GetOrCreateType(udt.Type);
+
+ (void)m_ast->GetOrCreateTypedefDecl(id);
+
+ Declaration decl;
+ return std::make_shared<lldb_private::Type>(
+ toOpaqueUid(id), this, ConstString(udt.Name), target_type->GetByteSize(),
+ nullptr, target_type->GetID(), lldb_private::Type::eEncodingIsTypedefUID,
+ decl, target_type->GetForwardCompilerType(),
+ lldb_private::Type::eResolveStateForward);
+}
+
+TypeSP SymbolFileNativePDB::GetOrCreateTypedef(PdbGlobalSymId id) {
+ auto iter = m_types.find(toOpaqueUid(id));
+ if (iter != m_types.end())
+ return iter->second;
+
+ return CreateTypedef(id);
+}
+
+size_t SymbolFileNativePDB::ParseVariablesForBlock(PdbCompilandSymId block_id) {
+ Block &block = GetOrCreateBlock(block_id);
+
+ size_t count = 0;
+
+ CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi);
+ CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset);
+ uint32_t params_remaining = 0;
+ switch (sym.kind()) {
+ case S_GPROC32:
+ case S_LPROC32: {
+ ProcSym proc(static_cast<SymbolRecordKind>(sym.kind()));
+ cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym, proc));
+ CVType signature = m_index->tpi().getType(proc.FunctionType);
+ ProcedureRecord sig;
+ cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(signature, sig));
+ params_remaining = sig.getParameterCount();
+ break;
+ }
+ case S_BLOCK32:
+ break;
+ default:
+ lldbassert(false && "Symbol is not a block!");
+ return 0;
+ }
+
+ VariableListSP variables = block.GetBlockVariableList(false);
+ if (!variables) {
+ variables = std::make_shared<VariableList>();
+ block.SetVariableList(variables);
+ }
+
+ CVSymbolArray syms = limitSymbolArrayToScope(
+ cii->m_debug_stream.getSymbolArray(), block_id.offset);
+
+ // Skip the first record since it's a PROC32 or BLOCK32, and there's
+ // no point examining it since we know it's not a local variable.
+ syms.drop_front();
+ auto iter = syms.begin();
+ auto end = syms.end();
+
+ while (iter != end) {
+ uint32_t record_offset = iter.offset();
+ CVSymbol variable_cvs = *iter;
+ PdbCompilandSymId child_sym_id(block_id.modi, record_offset);
+ ++iter;
+
+ // If this is a block, recurse into its children and then skip it.
+ if (variable_cvs.kind() == S_BLOCK32) {
+ uint32_t block_end = getScopeEndOffset(variable_cvs);
+ count += ParseVariablesForBlock(child_sym_id);
+ iter = syms.at(block_end);
+ continue;
+ }
+
+ bool is_param = params_remaining > 0;
+ VariableSP variable;
+ switch (variable_cvs.kind()) {
+ case S_REGREL32:
+ case S_REGISTER:
+ case S_LOCAL:
+ variable = GetOrCreateLocalVariable(block_id, child_sym_id, is_param);
+ if (is_param)
+ --params_remaining;
+ if (variable)
+ variables->AddVariableIfUnique(variable);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Pass false for set_children, since we call this recursively so that the
+ // children will call this for themselves.
+ block.SetDidParseVariables(true, false);
+
+ return count;
+}
+
+size_t SymbolFileNativePDB::ParseVariablesForContext(const SymbolContext &sc) {
+ lldbassert(sc.function || sc.comp_unit);
+
+ VariableListSP variables;
+ if (sc.block) {
+ PdbSymUid block_id(sc.block->GetID());
+
+ size_t count = ParseVariablesForBlock(block_id.asCompilandSym());
+ return count;
+ }
+
+ if (sc.function) {
+ PdbSymUid block_id(sc.function->GetID());
+
+ size_t count = ParseVariablesForBlock(block_id.asCompilandSym());
+ return count;
+ }
+
+ if (sc.comp_unit) {
+ variables = sc.comp_unit->GetVariableList(false);
+ if (!variables) {
+ variables = std::make_shared<VariableList>();
+ sc.comp_unit->SetVariableList(variables);
+ }
+ return ParseVariablesForCompileUnit(*sc.comp_unit, *variables);
+ }
+
+ llvm_unreachable("Unreachable!");
+}
+
+CompilerDecl SymbolFileNativePDB::GetDeclForUID(lldb::user_id_t uid) {
+ clang::Decl *decl = m_ast->GetOrCreateDeclForUid(PdbSymUid(uid));
+
+ return m_ast->ToCompilerDecl(*decl);
+}
+
+CompilerDeclContext
+SymbolFileNativePDB::GetDeclContextForUID(lldb::user_id_t uid) {
+ clang::DeclContext *context =
+ m_ast->GetOrCreateDeclContextForUid(PdbSymUid(uid));
+ if (!context)
+ return {};
+
+ return m_ast->ToCompilerDeclContext(*context);
+}
+
+CompilerDeclContext
+SymbolFileNativePDB::GetDeclContextContainingUID(lldb::user_id_t uid) {
+ clang::DeclContext *context = m_ast->GetParentDeclContext(PdbSymUid(uid));
+ return m_ast->ToCompilerDeclContext(*context);
+}
+
+Type *SymbolFileNativePDB::ResolveTypeUID(lldb::user_id_t type_uid) {
+ auto iter = m_types.find(type_uid);
+ // lldb should not be passing us non-sensical type uids. the only way it
+ // could have a type uid in the first place is if we handed it out, in which
+ // case we should know about the type. However, that doesn't mean we've
+ // instantiated it yet. We can vend out a UID for a future type. So if the
+ // type doesn't exist, let's instantiate it now.
+ if (iter != m_types.end())
+ return &*iter->second;
+
+ PdbSymUid uid(type_uid);
+ lldbassert(uid.kind() == PdbSymUidKind::Type);
+ PdbTypeSymId type_id = uid.asTypeSym();
+ if (type_id.index.isNoneType())
+ return nullptr;
+
+ TypeSP type_sp = CreateAndCacheType(type_id);
+ return &*type_sp;
+}
+
+llvm::Optional<SymbolFile::ArrayInfo>
+SymbolFileNativePDB::GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
+ return llvm::None;
+}
+
+
+bool SymbolFileNativePDB::CompleteType(CompilerType &compiler_type) {
+ clang::QualType qt =
+ clang::QualType::getFromOpaquePtr(compiler_type.GetOpaqueQualType());
+
+ return m_ast->CompleteType(qt);
+}
+
+size_t SymbolFileNativePDB::GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ TypeClass type_mask,
+ lldb_private::TypeList &type_list) {
+ return 0;
+}
+
+CompilerDeclContext
+SymbolFileNativePDB::FindNamespace(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx) {
+ return {};
+}
+
+TypeSystem *
+SymbolFileNativePDB::GetTypeSystemForLanguage(lldb::LanguageType language) {
+ auto type_system =
+ m_obj_file->GetModule()->GetTypeSystemForLanguage(language);
+ if (type_system)
+ type_system->SetSymbolFile(this);
+ return type_system;
+}
+
+ConstString SymbolFileNativePDB::GetPluginName() {
+ static ConstString g_name("pdb");
+ return g_name;
+}
+
+uint32_t SymbolFileNativePDB::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
new file mode 100644
index 000000000000..20daff219a0a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -0,0 +1,241 @@
+//===-- SymbolFileNativePDB.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H
+#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H
+
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/SymbolFile.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/DebugInfo/CodeView/CVRecord.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+
+#include "CompileUnitIndex.h"
+#include "PdbIndex.h"
+
+namespace clang {
+class TagDecl;
+}
+
+namespace llvm {
+namespace codeview {
+class ClassRecord;
+class EnumRecord;
+class ModifierRecord;
+class PointerRecord;
+struct UnionRecord;
+} // namespace codeview
+} // namespace llvm
+
+namespace lldb_private {
+class ClangASTImporter;
+
+namespace npdb {
+class PdbAstBuilder;
+
+class SymbolFileNativePDB : public SymbolFile {
+ friend class UdtRecordCompleter;
+
+public:
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static void DebuggerInitialize(Debugger &debugger);
+
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static SymbolFile *CreateInstance(ObjectFile *obj_file);
+
+ // Constructors and Destructors
+ SymbolFileNativePDB(ObjectFile *ofile);
+
+ ~SymbolFileNativePDB() override;
+
+ uint32_t CalculateAbilities() override;
+
+ void InitializeObject() override;
+
+ // Compile Unit function calls
+
+ uint32_t GetNumCompileUnits() override;
+
+ void
+ ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override;
+
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType
+ ParseLanguage(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
+ FileSpecList &support_files) override;
+ size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseImportedModules(
+ const SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override;
+
+ size_t ParseBlocksRecursive(Function &func) override;
+
+ uint32_t FindGlobalVariables(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches,
+ VariableList &variables) override;
+
+ size_t ParseVariablesForContext(const SymbolContext &sc) override;
+
+ void AddSymbols(Symtab &symtab) override;
+
+ CompilerDecl GetDeclForUID(lldb::user_id_t uid) override;
+ CompilerDeclContext GetDeclContextForUID(lldb::user_id_t uid) override;
+ CompilerDeclContext GetDeclContextContainingUID(lldb::user_id_t uid) override;
+ Type *ResolveTypeUID(lldb::user_id_t type_uid) override;
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override;
+
+ bool CompleteType(CompilerType &compiler_type) override;
+ uint32_t ResolveSymbolContext(const Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ SymbolContext &sc) override;
+ uint32_t ResolveSymbolContext(const FileSpec &file_spec, uint32_t line,
+ bool check_inlines,
+ lldb::SymbolContextItem resolve_scope,
+ SymbolContextList &sc_list) override;
+
+ size_t GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask,
+ TypeList &type_list) override;
+
+ uint32_t FindFunctions(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ lldb::FunctionNameType name_type_mask,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) override;
+
+ uint32_t FindFunctions(const RegularExpression &regex, bool include_inlines,
+ bool append, SymbolContextList &sc_list) override;
+
+ uint32_t FindTypes(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx, bool append,
+ uint32_t max_matches,
+ llvm::DenseSet<SymbolFile *> &searched_symbol_files,
+ TypeMap &types) override;
+
+ size_t FindTypes(const std::vector<CompilerContext> &context, bool append,
+ TypeMap &types) override;
+
+ TypeSystem *GetTypeSystemForLanguage(lldb::LanguageType language) override;
+
+ CompilerDeclContext
+ FindNamespace(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx) override;
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ llvm::pdb::PDBFile &GetPDBFile() { return m_index->pdb(); }
+ const llvm::pdb::PDBFile &GetPDBFile() const { return m_index->pdb(); }
+
+ void DumpClangAST(Stream &s) override;
+
+private:
+
+ size_t FindTypesByName(llvm::StringRef name, uint32_t max_matches,
+ TypeMap &types);
+
+ lldb::TypeSP CreateModifierType(PdbTypeSymId type_id,
+ const llvm::codeview::ModifierRecord &mr,
+ CompilerType ct);
+ lldb::TypeSP CreatePointerType(PdbTypeSymId type_id,
+ const llvm::codeview::PointerRecord &pr,
+ CompilerType ct);
+ lldb::TypeSP CreateSimpleType(llvm::codeview::TypeIndex ti, CompilerType ct);
+ lldb::TypeSP CreateTagType(PdbTypeSymId type_id,
+ const llvm::codeview::ClassRecord &cr,
+ CompilerType ct);
+ lldb::TypeSP CreateTagType(PdbTypeSymId type_id,
+ const llvm::codeview::EnumRecord &er,
+ CompilerType ct);
+ lldb::TypeSP CreateTagType(PdbTypeSymId type_id,
+ const llvm::codeview::UnionRecord &ur,
+ CompilerType ct);
+ lldb::TypeSP CreateArrayType(PdbTypeSymId type_id,
+ const llvm::codeview::ArrayRecord &ar,
+ CompilerType ct);
+ lldb::TypeSP CreateFunctionType(PdbTypeSymId type_id,
+ const llvm::codeview::MemberFunctionRecord &pr,
+ CompilerType ct);
+ lldb::TypeSP CreateProcedureType(PdbTypeSymId type_id,
+ const llvm::codeview::ProcedureRecord &pr,
+ CompilerType ct);
+ lldb::TypeSP CreateClassStructUnion(PdbTypeSymId type_id,
+ const llvm::codeview::TagRecord &record,
+ size_t size, CompilerType ct);
+
+ lldb::FunctionSP GetOrCreateFunction(PdbCompilandSymId func_id,
+ CompileUnit &comp_unit);
+ lldb::CompUnitSP GetOrCreateCompileUnit(const CompilandIndexItem &cci);
+ lldb::TypeSP GetOrCreateType(PdbTypeSymId type_id);
+ lldb::TypeSP GetOrCreateType(llvm::codeview::TypeIndex ti);
+ lldb::VariableSP GetOrCreateGlobalVariable(PdbGlobalSymId var_id);
+ Block &GetOrCreateBlock(PdbCompilandSymId block_id);
+ lldb::VariableSP GetOrCreateLocalVariable(PdbCompilandSymId scope_id,
+ PdbCompilandSymId var_id,
+ bool is_param);
+ lldb::TypeSP GetOrCreateTypedef(PdbGlobalSymId id);
+
+ lldb::FunctionSP CreateFunction(PdbCompilandSymId func_id,
+ CompileUnit &comp_unit);
+ Block &CreateBlock(PdbCompilandSymId block_id);
+ lldb::VariableSP CreateLocalVariable(PdbCompilandSymId scope_id,
+ PdbCompilandSymId var_id, bool is_param);
+ lldb::TypeSP CreateTypedef(PdbGlobalSymId id);
+ lldb::CompUnitSP CreateCompileUnit(const CompilandIndexItem &cci);
+ lldb::TypeSP CreateType(PdbTypeSymId type_id, CompilerType ct);
+ lldb::TypeSP CreateAndCacheType(PdbTypeSymId type_id);
+ lldb::VariableSP CreateGlobalVariable(PdbGlobalSymId var_id);
+ lldb::VariableSP CreateConstantSymbol(PdbGlobalSymId var_id,
+ const llvm::codeview::CVSymbol &cvs);
+ size_t ParseVariablesForCompileUnit(CompileUnit &comp_unit,
+ VariableList &variables);
+ size_t ParseVariablesForBlock(PdbCompilandSymId block_id);
+
+ llvm::BumpPtrAllocator m_allocator;
+
+ lldb::addr_t m_obj_load_address = 0;
+ bool m_done_full_type_scan = false;
+
+ std::unique_ptr<PdbIndex> m_index;
+
+ std::unique_ptr<PdbAstBuilder> m_ast;
+
+ llvm::DenseMap<lldb::user_id_t, lldb::VariableSP> m_global_vars;
+ llvm::DenseMap<lldb::user_id_t, lldb::VariableSP> m_local_variables;
+ llvm::DenseMap<lldb::user_id_t, lldb::BlockSP> m_blocks;
+ llvm::DenseMap<lldb::user_id_t, lldb::FunctionSP> m_functions;
+ llvm::DenseMap<lldb::user_id_t, lldb::CompUnitSP> m_compilands;
+ llvm::DenseMap<lldb::user_id_t, lldb::TypeSP> m_types;
+};
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
new file mode 100644
index 000000000000..3c494dc83986
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
@@ -0,0 +1,236 @@
+#include "UdtRecordCompleter.h"
+
+#include "PdbAstBuilder.h"
+#include "PdbIndex.h"
+#include "PdbSymUid.h"
+#include "PdbUtil.h"
+
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+
+using Error = llvm::Error;
+
+UdtRecordCompleter::UdtRecordCompleter(PdbTypeSymId id,
+ CompilerType &derived_ct,
+ clang::TagDecl &tag_decl,
+ PdbAstBuilder &ast_builder,
+ TpiStream &tpi)
+ : m_id(id), m_derived_ct(derived_ct), m_tag_decl(tag_decl),
+ m_ast_builder(ast_builder), m_tpi(tpi) {
+ CVType cvt = m_tpi.getType(m_id.index);
+ switch (cvt.kind()) {
+ case LF_ENUM:
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, m_cvr.er));
+ break;
+ case LF_UNION:
+ llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, m_cvr.ur));
+ break;
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, m_cvr.cr));
+ break;
+ default:
+ llvm_unreachable("unreachable!");
+ }
+}
+
+clang::QualType UdtRecordCompleter::AddBaseClassForTypeIndex(
+ llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access,
+ llvm::Optional<uint64_t> vtable_idx) {
+ PdbTypeSymId type_id(ti);
+ clang::QualType qt = m_ast_builder.GetOrCreateType(type_id);
+
+ CVType udt_cvt = m_tpi.getType(ti);
+
+ std::unique_ptr<clang::CXXBaseSpecifier> base_spec =
+ m_ast_builder.clang().CreateBaseClassSpecifier(
+ qt.getAsOpaquePtr(), TranslateMemberAccess(access),
+ vtable_idx.hasValue(), udt_cvt.kind() == LF_CLASS);
+ lldbassert(base_spec);
+
+ m_bases.push_back(
+ std::make_pair(vtable_idx.getValueOr(0), std::move(base_spec)));
+
+ return qt;
+}
+
+void UdtRecordCompleter::AddMethod(llvm::StringRef name, TypeIndex type_idx,
+ MemberAccess access, MethodOptions options,
+ MemberAttributes attrs) {
+ clang::QualType method_qt =
+ m_ast_builder.GetOrCreateType(PdbTypeSymId(type_idx));
+ m_ast_builder.CompleteType(method_qt);
+
+ lldb::AccessType access_type = TranslateMemberAccess(access);
+ bool is_artificial = (options & MethodOptions::CompilerGenerated) ==
+ MethodOptions::CompilerGenerated;
+ m_ast_builder.clang().AddMethodToCXXRecordType(
+ m_derived_ct.GetOpaqueQualType(), name.data(), nullptr,
+ m_ast_builder.ToCompilerType(method_qt), access_type, attrs.isVirtual(),
+ attrs.isStatic(), false, false, false, is_artificial);
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ BaseClassRecord &base) {
+ clang::QualType base_qt =
+ AddBaseClassForTypeIndex(base.Type, base.getAccess());
+
+ auto decl =
+ m_ast_builder.clang().GetAsCXXRecordDecl(base_qt.getAsOpaquePtr());
+ lldbassert(decl);
+
+ auto offset = clang::CharUnits::fromQuantity(base.getBaseOffset());
+ m_layout.base_offsets.insert(std::make_pair(decl, offset));
+
+ return llvm::Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ VirtualBaseClassRecord &base) {
+ AddBaseClassForTypeIndex(base.BaseType, base.getAccess(), base.VTableIndex);
+
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ ListContinuationRecord &cont) {
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ VFPtrRecord &vfptr) {
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(
+ CVMemberRecord &cvr, StaticDataMemberRecord &static_data_member) {
+ clang::QualType member_type =
+ m_ast_builder.GetOrCreateType(PdbTypeSymId(static_data_member.Type));
+
+ m_ast_builder.CompleteType(member_type);
+
+ CompilerType member_ct = m_ast_builder.ToCompilerType(member_type);
+
+ lldb::AccessType access =
+ TranslateMemberAccess(static_data_member.getAccess());
+ ClangASTContext::AddVariableToRecordType(
+ m_derived_ct, static_data_member.Name, member_ct, access);
+
+ // FIXME: Add a PdbSymUid namespace for field list members and update
+ // the m_uid_to_decl map with this decl.
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ NestedTypeRecord &nested) {
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ DataMemberRecord &data_member) {
+
+ uint64_t offset = data_member.FieldOffset * 8;
+ uint32_t bitfield_width = 0;
+
+ TypeIndex ti(data_member.Type);
+ if (!ti.isSimple()) {
+ CVType cvt = m_tpi.getType(ti);
+ if (cvt.kind() == LF_BITFIELD) {
+ BitFieldRecord bfr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<BitFieldRecord>(cvt, bfr));
+ offset += bfr.BitOffset;
+ bitfield_width = bfr.BitSize;
+ ti = bfr.Type;
+ }
+ }
+
+ clang::QualType member_qt = m_ast_builder.GetOrCreateType(PdbTypeSymId(ti));
+ m_ast_builder.CompleteType(member_qt);
+
+ lldb::AccessType access = TranslateMemberAccess(data_member.getAccess());
+
+ clang::FieldDecl *decl = ClangASTContext::AddFieldToRecordType(
+ m_derived_ct, data_member.Name, m_ast_builder.ToCompilerType(member_qt),
+ access, bitfield_width);
+ // FIXME: Add a PdbSymUid namespace for field list members and update
+ // the m_uid_to_decl map with this decl.
+
+ m_layout.field_offsets.insert(std::make_pair(decl, offset));
+
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ OneMethodRecord &one_method) {
+ AddMethod(one_method.Name, one_method.Type, one_method.getAccess(),
+ one_method.getOptions(), one_method.Attrs);
+
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ OverloadedMethodRecord &overloaded) {
+ TypeIndex method_list_idx = overloaded.MethodList;
+
+ CVType method_list_type = m_tpi.getType(method_list_idx);
+ assert(method_list_type.kind() == LF_METHODLIST);
+
+ MethodOverloadListRecord method_list;
+ llvm::cantFail(TypeDeserializer::deserializeAs<MethodOverloadListRecord>(
+ method_list_type, method_list));
+
+ for (const OneMethodRecord &method : method_list.Methods)
+ AddMethod(overloaded.Name, method.Type, method.getAccess(),
+ method.getOptions(), method.Attrs);
+
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ EnumeratorRecord &enumerator) {
+ Declaration decl;
+ llvm::StringRef name = DropNameScope(enumerator.getName());
+
+ m_ast_builder.clang().AddEnumerationValueToEnumerationType(
+ m_derived_ct, decl, name.str().c_str(), enumerator.Value);
+ return Error::success();
+}
+
+void UdtRecordCompleter::complete() {
+ // Ensure the correct order for virtual bases.
+ std::stable_sort(m_bases.begin(), m_bases.end(),
+ [](const IndexedBase &lhs, const IndexedBase &rhs) {
+ return lhs.first < rhs.first;
+ });
+
+ std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases;
+ bases.reserve(m_bases.size());
+ for (auto &ib : m_bases)
+ bases.push_back(std::move(ib.second));
+
+ ClangASTContext &clang = m_ast_builder.clang();
+ clang.TransferBaseClasses(m_derived_ct.GetOpaqueQualType(), std::move(bases));
+
+ clang.AddMethodOverridesForCXXRecordType(m_derived_ct.GetOpaqueQualType());
+ ClangASTContext::BuildIndirectFields(m_derived_ct);
+ ClangASTContext::CompleteTagDeclarationDefinition(m_derived_ct);
+
+ if (auto *record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(&m_tag_decl)) {
+ m_ast_builder.importer().InsertRecordDecl(record_decl, m_layout);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h
new file mode 100644
index 000000000000..55397582209b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h
@@ -0,0 +1,82 @@
+//===-- UdtRecordCompleter.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H
+#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H
+
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "llvm/DebugInfo/CodeView/CVRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
+
+#include "PdbSymUid.h"
+
+namespace clang {
+class CXXBaseSpecifier;
+class QualType;
+class TagDecl;
+} // namespace clang
+
+namespace llvm {
+namespace pdb {
+class TpiStream;
+}
+} // namespace llvm
+
+namespace lldb_private {
+class Type;
+class CompilerType;
+namespace npdb {
+class PdbAstBuilder;
+
+class UdtRecordCompleter : public llvm::codeview::TypeVisitorCallbacks {
+ using IndexedBase =
+ std::pair<uint64_t, std::unique_ptr<clang::CXXBaseSpecifier>>;
+
+ union UdtTagRecord {
+ UdtTagRecord() {}
+ llvm::codeview::UnionRecord ur;
+ llvm::codeview::ClassRecord cr;
+ llvm::codeview::EnumRecord er;
+ } m_cvr;
+
+ PdbTypeSymId m_id;
+ CompilerType &m_derived_ct;
+ clang::TagDecl &m_tag_decl;
+ PdbAstBuilder &m_ast_builder;
+ llvm::pdb::TpiStream &m_tpi;
+ std::vector<IndexedBase> m_bases;
+ ClangASTImporter::LayoutInfo m_layout;
+
+public:
+ UdtRecordCompleter(PdbTypeSymId id, CompilerType &derived_ct,
+ clang::TagDecl &tag_decl, PdbAstBuilder &ast_builder,
+ llvm::pdb::TpiStream &tpi);
+
+#define MEMBER_RECORD(EnumName, EnumVal, Name) \
+ llvm::Error visitKnownMember(llvm::codeview::CVMemberRecord &CVR, \
+ llvm::codeview::Name##Record &Record) override;
+#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
+#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
+
+ void complete();
+
+private:
+ clang::QualType AddBaseClassForTypeIndex(
+ llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access,
+ llvm::Optional<uint64_t> vtable_idx = llvm::Optional<uint64_t>());
+ void AddMethod(llvm::StringRef name, llvm::codeview::TypeIndex type_idx,
+ llvm::codeview::MemberAccess access,
+ llvm::codeview::MethodOptions options,
+ llvm::codeview::MemberAttributes attrs);
+};
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif // LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp
new file mode 100644
index 000000000000..82cfcfbb040f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp
@@ -0,0 +1,1367 @@
+//===-- PDBASTParser.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PDBASTParser.h"
+
+#include "SymbolFilePDB.h"
+
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/Declaration.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/TypeSystem.h"
+
+#include "llvm/DebugInfo/PDB/IPDBLineNumber.h"
+#include "llvm/DebugInfo/PDB/IPDBSourceFile.h"
+#include "llvm/DebugInfo/PDB/PDBSymbol.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
+
+#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm::pdb;
+
+static int TranslateUdtKind(PDB_UdtType pdb_kind) {
+ switch (pdb_kind) {
+ case PDB_UdtType::Class:
+ return clang::TTK_Class;
+ case PDB_UdtType::Struct:
+ return clang::TTK_Struct;
+ case PDB_UdtType::Union:
+ return clang::TTK_Union;
+ case PDB_UdtType::Interface:
+ return clang::TTK_Interface;
+ }
+ llvm_unreachable("unsuported PDB UDT type");
+}
+
+static lldb::Encoding TranslateBuiltinEncoding(PDB_BuiltinType type) {
+ switch (type) {
+ case PDB_BuiltinType::Float:
+ return lldb::eEncodingIEEE754;
+ case PDB_BuiltinType::Int:
+ case PDB_BuiltinType::Long:
+ case PDB_BuiltinType::Char:
+ return lldb::eEncodingSint;
+ case PDB_BuiltinType::Bool:
+ case PDB_BuiltinType::Char16:
+ case PDB_BuiltinType::Char32:
+ case PDB_BuiltinType::UInt:
+ case PDB_BuiltinType::ULong:
+ case PDB_BuiltinType::HResult:
+ case PDB_BuiltinType::WCharT:
+ return lldb::eEncodingUint;
+ default:
+ return lldb::eEncodingInvalid;
+ }
+}
+
+static lldb::Encoding TranslateEnumEncoding(PDB_VariantType type) {
+ switch (type) {
+ case PDB_VariantType::Int8:
+ case PDB_VariantType::Int16:
+ case PDB_VariantType::Int32:
+ case PDB_VariantType::Int64:
+ return lldb::eEncodingSint;
+
+ case PDB_VariantType::UInt8:
+ case PDB_VariantType::UInt16:
+ case PDB_VariantType::UInt32:
+ case PDB_VariantType::UInt64:
+ return lldb::eEncodingUint;
+
+ default:
+ break;
+ }
+
+ return lldb::eEncodingSint;
+}
+
+static CompilerType
+GetBuiltinTypeForPDBEncodingAndBitSize(ClangASTContext &clang_ast,
+ const PDBSymbolTypeBuiltin &pdb_type,
+ Encoding encoding, uint32_t width) {
+ auto *ast = clang_ast.getASTContext();
+ if (!ast)
+ return CompilerType();
+
+ switch (pdb_type.getBuiltinType()) {
+ default:
+ break;
+ case PDB_BuiltinType::None:
+ return CompilerType();
+ case PDB_BuiltinType::Void:
+ return clang_ast.GetBasicType(eBasicTypeVoid);
+ case PDB_BuiltinType::Char:
+ return clang_ast.GetBasicType(eBasicTypeChar);
+ case PDB_BuiltinType::Bool:
+ return clang_ast.GetBasicType(eBasicTypeBool);
+ case PDB_BuiltinType::Long:
+ if (width == ast->getTypeSize(ast->LongTy))
+ return CompilerType(ast, ast->LongTy);
+ if (width == ast->getTypeSize(ast->LongLongTy))
+ return CompilerType(ast, ast->LongLongTy);
+ break;
+ case PDB_BuiltinType::ULong:
+ if (width == ast->getTypeSize(ast->UnsignedLongTy))
+ return CompilerType(ast, ast->UnsignedLongTy);
+ if (width == ast->getTypeSize(ast->UnsignedLongLongTy))
+ return CompilerType(ast, ast->UnsignedLongLongTy);
+ break;
+ case PDB_BuiltinType::WCharT:
+ if (width == ast->getTypeSize(ast->WCharTy))
+ return CompilerType(ast, ast->WCharTy);
+ break;
+ case PDB_BuiltinType::Char16:
+ return CompilerType(ast, ast->Char16Ty);
+ case PDB_BuiltinType::Char32:
+ return CompilerType(ast, ast->Char32Ty);
+ case PDB_BuiltinType::Float:
+ // Note: types `long double` and `double` have same bit size in MSVC and
+ // there is no information in the PDB to distinguish them. So when falling
+ // back to default search, the compiler type of `long double` will be
+ // represented by the one generated for `double`.
+ break;
+ }
+ // If there is no match on PDB_BuiltinType, fall back to default search by
+ // encoding and width only
+ return clang_ast.GetBuiltinTypeForEncodingAndBitSize(encoding, width);
+}
+
+static ConstString GetPDBBuiltinTypeName(const PDBSymbolTypeBuiltin &pdb_type,
+ CompilerType &compiler_type) {
+ PDB_BuiltinType kind = pdb_type.getBuiltinType();
+ switch (kind) {
+ default:
+ break;
+ case PDB_BuiltinType::Currency:
+ return ConstString("CURRENCY");
+ case PDB_BuiltinType::Date:
+ return ConstString("DATE");
+ case PDB_BuiltinType::Variant:
+ return ConstString("VARIANT");
+ case PDB_BuiltinType::Complex:
+ return ConstString("complex");
+ case PDB_BuiltinType::Bitfield:
+ return ConstString("bitfield");
+ case PDB_BuiltinType::BSTR:
+ return ConstString("BSTR");
+ case PDB_BuiltinType::HResult:
+ return ConstString("HRESULT");
+ case PDB_BuiltinType::BCD:
+ return ConstString("BCD");
+ case PDB_BuiltinType::Char16:
+ return ConstString("char16_t");
+ case PDB_BuiltinType::Char32:
+ return ConstString("char32_t");
+ case PDB_BuiltinType::None:
+ return ConstString("...");
+ }
+ return compiler_type.GetTypeName();
+}
+
+static bool GetDeclarationForSymbol(const PDBSymbol &symbol,
+ Declaration &decl) {
+ auto &raw_sym = symbol.getRawSymbol();
+ auto first_line_up = raw_sym.getSrcLineOnTypeDefn();
+
+ if (!first_line_up) {
+ auto lines_up = symbol.getSession().findLineNumbersByAddress(
+ raw_sym.getVirtualAddress(), raw_sym.getLength());
+ if (!lines_up)
+ return false;
+ first_line_up = lines_up->getNext();
+ if (!first_line_up)
+ return false;
+ }
+ uint32_t src_file_id = first_line_up->getSourceFileId();
+ auto src_file_up = symbol.getSession().getSourceFileById(src_file_id);
+ if (!src_file_up)
+ return false;
+
+ FileSpec spec(src_file_up->getFileName());
+ decl.SetFile(spec);
+ decl.SetColumn(first_line_up->getColumnNumber());
+ decl.SetLine(first_line_up->getLineNumber());
+ return true;
+}
+
+static AccessType TranslateMemberAccess(PDB_MemberAccess access) {
+ switch (access) {
+ case PDB_MemberAccess::Private:
+ return eAccessPrivate;
+ case PDB_MemberAccess::Protected:
+ return eAccessProtected;
+ case PDB_MemberAccess::Public:
+ return eAccessPublic;
+ }
+ return eAccessNone;
+}
+
+static AccessType GetDefaultAccessibilityForUdtKind(PDB_UdtType udt_kind) {
+ switch (udt_kind) {
+ case PDB_UdtType::Struct:
+ case PDB_UdtType::Union:
+ return eAccessPublic;
+ case PDB_UdtType::Class:
+ case PDB_UdtType::Interface:
+ return eAccessPrivate;
+ }
+ llvm_unreachable("unsupported PDB UDT type");
+}
+
+static AccessType GetAccessibilityForUdt(const PDBSymbolTypeUDT &udt) {
+ AccessType access = TranslateMemberAccess(udt.getAccess());
+ if (access != lldb::eAccessNone || !udt.isNested())
+ return access;
+
+ auto parent = udt.getClassParent();
+ if (!parent)
+ return lldb::eAccessNone;
+
+ auto parent_udt = llvm::dyn_cast<PDBSymbolTypeUDT>(parent.get());
+ if (!parent_udt)
+ return lldb::eAccessNone;
+
+ return GetDefaultAccessibilityForUdtKind(parent_udt->getUdtKind());
+}
+
+static clang::MSInheritanceAttr::Spelling
+GetMSInheritance(const PDBSymbolTypeUDT &udt) {
+ int base_count = 0;
+ bool has_virtual = false;
+
+ auto bases_enum = udt.findAllChildren<PDBSymbolTypeBaseClass>();
+ if (bases_enum) {
+ while (auto base = bases_enum->getNext()) {
+ base_count++;
+ has_virtual |= base->isVirtualBaseClass();
+ }
+ }
+
+ if (has_virtual)
+ return clang::MSInheritanceAttr::Keyword_virtual_inheritance;
+ if (base_count > 1)
+ return clang::MSInheritanceAttr::Keyword_multiple_inheritance;
+ return clang::MSInheritanceAttr::Keyword_single_inheritance;
+}
+
+static std::unique_ptr<llvm::pdb::PDBSymbol>
+GetClassOrFunctionParent(const llvm::pdb::PDBSymbol &symbol) {
+ const IPDBSession &session = symbol.getSession();
+ const IPDBRawSymbol &raw = symbol.getRawSymbol();
+ auto tag = symbol.getSymTag();
+
+ // For items that are nested inside of a class, return the class that it is
+ // nested inside of.
+ // Note that only certain items can be nested inside of classes.
+ switch (tag) {
+ case PDB_SymType::Function:
+ case PDB_SymType::Data:
+ case PDB_SymType::UDT:
+ case PDB_SymType::Enum:
+ case PDB_SymType::FunctionSig:
+ case PDB_SymType::Typedef:
+ case PDB_SymType::BaseClass:
+ case PDB_SymType::VTable: {
+ auto class_parent_id = raw.getClassParentId();
+ if (auto class_parent = session.getSymbolById(class_parent_id))
+ return class_parent;
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Otherwise, if it is nested inside of a function, return the function.
+ // Note that only certain items can be nested inside of functions.
+ switch (tag) {
+ case PDB_SymType::Block:
+ case PDB_SymType::Data: {
+ auto lexical_parent_id = raw.getLexicalParentId();
+ auto lexical_parent = session.getSymbolById(lexical_parent_id);
+ if (!lexical_parent)
+ return nullptr;
+
+ auto lexical_parent_tag = lexical_parent->getSymTag();
+ if (lexical_parent_tag == PDB_SymType::Function)
+ return lexical_parent;
+ if (lexical_parent_tag == PDB_SymType::Exe)
+ return nullptr;
+
+ return GetClassOrFunctionParent(*lexical_parent);
+ }
+ default:
+ return nullptr;
+ }
+}
+
+static clang::NamedDecl *
+GetDeclFromContextByName(const clang::ASTContext &ast,
+ const clang::DeclContext &decl_context,
+ llvm::StringRef name) {
+ clang::IdentifierInfo &ident = ast.Idents.get(name);
+ clang::DeclarationName decl_name = ast.DeclarationNames.getIdentifier(&ident);
+ clang::DeclContext::lookup_result result = decl_context.lookup(decl_name);
+ if (result.empty())
+ return nullptr;
+
+ return result[0];
+}
+
+static bool IsAnonymousNamespaceName(llvm::StringRef name) {
+ return name == "`anonymous namespace'" || name == "`anonymous-namespace'";
+}
+
+static clang::CallingConv TranslateCallingConvention(PDB_CallingConv pdb_cc) {
+ switch (pdb_cc) {
+ case llvm::codeview::CallingConvention::NearC:
+ return clang::CC_C;
+ case llvm::codeview::CallingConvention::NearStdCall:
+ return clang::CC_X86StdCall;
+ case llvm::codeview::CallingConvention::NearFast:
+ return clang::CC_X86FastCall;
+ case llvm::codeview::CallingConvention::ThisCall:
+ return clang::CC_X86ThisCall;
+ case llvm::codeview::CallingConvention::NearVector:
+ return clang::CC_X86VectorCall;
+ case llvm::codeview::CallingConvention::NearPascal:
+ return clang::CC_X86Pascal;
+ default:
+ assert(false && "Unknown calling convention");
+ return clang::CC_C;
+ }
+}
+
+PDBASTParser::PDBASTParser(lldb_private::ClangASTContext &ast) : m_ast(ast) {}
+
+PDBASTParser::~PDBASTParser() {}
+
+// DebugInfoASTParser interface
+
+lldb::TypeSP PDBASTParser::CreateLLDBTypeFromPDBType(const PDBSymbol &type) {
+ Declaration decl;
+ switch (type.getSymTag()) {
+ case PDB_SymType::BaseClass: {
+ auto symbol_file = m_ast.GetSymbolFile();
+ if (!symbol_file)
+ return nullptr;
+
+ auto ty = symbol_file->ResolveTypeUID(type.getRawSymbol().getTypeId());
+ return ty ? ty->shared_from_this() : nullptr;
+ } break;
+ case PDB_SymType::UDT: {
+ auto udt = llvm::dyn_cast<PDBSymbolTypeUDT>(&type);
+ assert(udt);
+
+ // Note that, unnamed UDT being typedef-ed is generated as a UDT symbol
+ // other than a Typedef symbol in PDB. For example,
+ // typedef union { short Row; short Col; } Union;
+ // is generated as a named UDT in PDB:
+ // union Union { short Row; short Col; }
+ // Such symbols will be handled here.
+
+ // Some UDT with trival ctor has zero length. Just ignore.
+ if (udt->getLength() == 0)
+ return nullptr;
+
+ // Ignore unnamed-tag UDTs.
+ std::string name = MSVCUndecoratedNameParser::DropScope(udt->getName());
+ if (name.empty())
+ return nullptr;
+
+ auto decl_context = GetDeclContextContainingSymbol(type);
+
+ // Check if such an UDT already exists in the current context.
+ // This may occur with const or volatile types. There are separate type
+ // symbols in PDB for types with const or volatile modifiers, but we need
+ // to create only one declaration for them all.
+ Type::ResolveStateTag type_resolve_state_tag;
+ CompilerType clang_type = m_ast.GetTypeForIdentifier<clang::CXXRecordDecl>(
+ ConstString(name), decl_context);
+ if (!clang_type.IsValid()) {
+ auto access = GetAccessibilityForUdt(*udt);
+
+ auto tag_type_kind = TranslateUdtKind(udt->getUdtKind());
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(type.getSymIndexId());
+ metadata.SetIsDynamicCXXType(false);
+
+ clang_type = m_ast.CreateRecordType(
+ decl_context, access, name.c_str(), tag_type_kind,
+ lldb::eLanguageTypeC_plus_plus, &metadata);
+ assert(clang_type.IsValid());
+
+ auto record_decl =
+ m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
+ assert(record_decl);
+ m_uid_to_decl[type.getSymIndexId()] = record_decl;
+
+ auto inheritance_attr = clang::MSInheritanceAttr::CreateImplicit(
+ *m_ast.getASTContext(), GetMSInheritance(*udt));
+ record_decl->addAttr(inheritance_attr);
+
+ ClangASTContext::StartTagDeclarationDefinition(clang_type);
+
+ auto children = udt->findAllChildren();
+ if (!children || children->getChildCount() == 0) {
+ // PDB does not have symbol of forwarder. We assume we get an udt w/o
+ // any fields. Just complete it at this point.
+ ClangASTContext::CompleteTagDeclarationDefinition(clang_type);
+
+ ClangASTContext::SetHasExternalStorage(clang_type.GetOpaqueQualType(),
+ false);
+
+ type_resolve_state_tag = Type::eResolveStateFull;
+ } else {
+ // Add the type to the forward declarations. It will help us to avoid
+ // an endless recursion in CompleteTypeFromUdt function.
+ m_forward_decl_to_uid[record_decl] = type.getSymIndexId();
+
+ ClangASTContext::SetHasExternalStorage(clang_type.GetOpaqueQualType(),
+ true);
+
+ type_resolve_state_tag = Type::eResolveStateForward;
+ }
+ } else
+ type_resolve_state_tag = Type::eResolveStateForward;
+
+ if (udt->isConstType())
+ clang_type = clang_type.AddConstModifier();
+
+ if (udt->isVolatileType())
+ clang_type = clang_type.AddVolatileModifier();
+
+ GetDeclarationForSymbol(type, decl);
+ return std::make_shared<lldb_private::Type>(
+ type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name),
+ udt->getLength(), nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, clang_type,
+ type_resolve_state_tag);
+ } break;
+ case PDB_SymType::Enum: {
+ auto enum_type = llvm::dyn_cast<PDBSymbolTypeEnum>(&type);
+ assert(enum_type);
+
+ std::string name =
+ MSVCUndecoratedNameParser::DropScope(enum_type->getName());
+ auto decl_context = GetDeclContextContainingSymbol(type);
+ uint64_t bytes = enum_type->getLength();
+
+ // Check if such an enum already exists in the current context
+ CompilerType ast_enum = m_ast.GetTypeForIdentifier<clang::EnumDecl>(
+ ConstString(name), decl_context);
+ if (!ast_enum.IsValid()) {
+ auto underlying_type_up = enum_type->getUnderlyingType();
+ if (!underlying_type_up)
+ return nullptr;
+
+ lldb::Encoding encoding =
+ TranslateBuiltinEncoding(underlying_type_up->getBuiltinType());
+ // FIXME: Type of underlying builtin is always `Int`. We correct it with
+ // the very first enumerator's encoding if any.
+ auto first_child = enum_type->findOneChild<PDBSymbolData>();
+ if (first_child)
+ encoding = TranslateEnumEncoding(first_child->getValue().Type);
+
+ CompilerType builtin_type;
+ if (bytes > 0)
+ builtin_type = GetBuiltinTypeForPDBEncodingAndBitSize(
+ m_ast, *underlying_type_up, encoding, bytes * 8);
+ else
+ builtin_type = m_ast.GetBasicType(eBasicTypeInt);
+
+ // FIXME: PDB does not have information about scoped enumeration (Enum
+ // Class). Set it false for now.
+ bool isScoped = false;
+
+ ast_enum = m_ast.CreateEnumerationType(name.c_str(), decl_context, decl,
+ builtin_type, isScoped);
+
+ auto enum_decl = ClangASTContext::GetAsEnumDecl(ast_enum);
+ assert(enum_decl);
+ m_uid_to_decl[type.getSymIndexId()] = enum_decl;
+
+ auto enum_values = enum_type->findAllChildren<PDBSymbolData>();
+ if (enum_values) {
+ while (auto enum_value = enum_values->getNext()) {
+ if (enum_value->getDataKind() != PDB_DataKind::Constant)
+ continue;
+ AddEnumValue(ast_enum, *enum_value);
+ }
+ }
+
+ if (ClangASTContext::StartTagDeclarationDefinition(ast_enum))
+ ClangASTContext::CompleteTagDeclarationDefinition(ast_enum);
+ }
+
+ if (enum_type->isConstType())
+ ast_enum = ast_enum.AddConstModifier();
+
+ if (enum_type->isVolatileType())
+ ast_enum = ast_enum.AddVolatileModifier();
+
+ GetDeclarationForSymbol(type, decl);
+ return std::make_shared<lldb_private::Type>(
+ type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name), bytes,
+ nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl,
+ ast_enum, lldb_private::Type::eResolveStateFull);
+ } break;
+ case PDB_SymType::Typedef: {
+ auto type_def = llvm::dyn_cast<PDBSymbolTypeTypedef>(&type);
+ assert(type_def);
+
+ lldb_private::Type *target_type =
+ m_ast.GetSymbolFile()->ResolveTypeUID(type_def->getTypeId());
+ if (!target_type)
+ return nullptr;
+
+ std::string name =
+ MSVCUndecoratedNameParser::DropScope(type_def->getName());
+ auto decl_ctx = GetDeclContextContainingSymbol(type);
+
+ // Check if such a typedef already exists in the current context
+ CompilerType ast_typedef =
+ m_ast.GetTypeForIdentifier<clang::TypedefNameDecl>(ConstString(name),
+ decl_ctx);
+ if (!ast_typedef.IsValid()) {
+ CompilerType target_ast_type = target_type->GetFullCompilerType();
+
+ ast_typedef = m_ast.CreateTypedefType(
+ target_ast_type, name.c_str(), CompilerDeclContext(&m_ast, decl_ctx));
+ if (!ast_typedef)
+ return nullptr;
+
+ auto typedef_decl = ClangASTContext::GetAsTypedefDecl(ast_typedef);
+ assert(typedef_decl);
+ m_uid_to_decl[type.getSymIndexId()] = typedef_decl;
+ }
+
+ if (type_def->isConstType())
+ ast_typedef = ast_typedef.AddConstModifier();
+
+ if (type_def->isVolatileType())
+ ast_typedef = ast_typedef.AddVolatileModifier();
+
+ GetDeclarationForSymbol(type, decl);
+ llvm::Optional<uint64_t> size;
+ if (type_def->getLength())
+ size = type_def->getLength();
+ return std::make_shared<lldb_private::Type>(
+ type_def->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name),
+ size, nullptr, target_type->GetID(),
+ lldb_private::Type::eEncodingIsTypedefUID, decl, ast_typedef,
+ lldb_private::Type::eResolveStateFull);
+ } break;
+ case PDB_SymType::Function:
+ case PDB_SymType::FunctionSig: {
+ std::string name;
+ PDBSymbolTypeFunctionSig *func_sig = nullptr;
+ if (auto pdb_func = llvm::dyn_cast<PDBSymbolFunc>(&type)) {
+ if (pdb_func->isCompilerGenerated())
+ return nullptr;
+
+ auto sig = pdb_func->getSignature();
+ if (!sig)
+ return nullptr;
+ func_sig = sig.release();
+ // Function type is named.
+ name = MSVCUndecoratedNameParser::DropScope(pdb_func->getName());
+ } else if (auto pdb_func_sig =
+ llvm::dyn_cast<PDBSymbolTypeFunctionSig>(&type)) {
+ func_sig = const_cast<PDBSymbolTypeFunctionSig *>(pdb_func_sig);
+ } else
+ llvm_unreachable("Unexpected PDB symbol!");
+
+ auto arg_enum = func_sig->getArguments();
+ uint32_t num_args = arg_enum->getChildCount();
+ std::vector<CompilerType> arg_list;
+
+ bool is_variadic = func_sig->isCVarArgs();
+ // Drop last variadic argument.
+ if (is_variadic)
+ --num_args;
+ for (uint32_t arg_idx = 0; arg_idx < num_args; arg_idx++) {
+ auto arg = arg_enum->getChildAtIndex(arg_idx);
+ if (!arg)
+ break;
+ lldb_private::Type *arg_type =
+ m_ast.GetSymbolFile()->ResolveTypeUID(arg->getSymIndexId());
+ // If there's some error looking up one of the dependent types of this
+ // function signature, bail.
+ if (!arg_type)
+ return nullptr;
+ CompilerType arg_ast_type = arg_type->GetFullCompilerType();
+ arg_list.push_back(arg_ast_type);
+ }
+ lldbassert(arg_list.size() <= num_args);
+
+ auto pdb_return_type = func_sig->getReturnType();
+ lldb_private::Type *return_type =
+ m_ast.GetSymbolFile()->ResolveTypeUID(pdb_return_type->getSymIndexId());
+ // If there's some error looking up one of the dependent types of this
+ // function signature, bail.
+ if (!return_type)
+ return nullptr;
+ CompilerType return_ast_type = return_type->GetFullCompilerType();
+ uint32_t type_quals = 0;
+ if (func_sig->isConstType())
+ type_quals |= clang::Qualifiers::Const;
+ if (func_sig->isVolatileType())
+ type_quals |= clang::Qualifiers::Volatile;
+ auto cc = TranslateCallingConvention(func_sig->getCallingConvention());
+ CompilerType func_sig_ast_type =
+ m_ast.CreateFunctionType(return_ast_type, arg_list.data(),
+ arg_list.size(), is_variadic, type_quals, cc);
+
+ GetDeclarationForSymbol(type, decl);
+ return std::make_shared<lldb_private::Type>(
+ type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name),
+ llvm::None, nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, func_sig_ast_type,
+ lldb_private::Type::eResolveStateFull);
+ } break;
+ case PDB_SymType::ArrayType: {
+ auto array_type = llvm::dyn_cast<PDBSymbolTypeArray>(&type);
+ assert(array_type);
+ uint32_t num_elements = array_type->getCount();
+ uint32_t element_uid = array_type->getElementTypeId();
+ llvm::Optional<uint64_t> bytes;
+ if (uint64_t size = array_type->getLength())
+ bytes = size;
+
+ // If array rank > 0, PDB gives the element type at N=0. So element type
+ // will parsed in the order N=0, N=1,..., N=rank sequentially.
+ lldb_private::Type *element_type =
+ m_ast.GetSymbolFile()->ResolveTypeUID(element_uid);
+ if (!element_type)
+ return nullptr;
+
+ CompilerType element_ast_type = element_type->GetForwardCompilerType();
+ // If element type is UDT, it needs to be complete.
+ if (ClangASTContext::IsCXXClassType(element_ast_type) &&
+ !element_ast_type.GetCompleteType()) {
+ if (ClangASTContext::StartTagDeclarationDefinition(element_ast_type)) {
+ ClangASTContext::CompleteTagDeclarationDefinition(element_ast_type);
+ } else {
+ // We are not able to start defintion.
+ return nullptr;
+ }
+ }
+ CompilerType array_ast_type = m_ast.CreateArrayType(
+ element_ast_type, num_elements, /*is_gnu_vector*/ false);
+ TypeSP type_sp = std::make_shared<lldb_private::Type>(
+ array_type->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(),
+ bytes, nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID,
+ decl, array_ast_type, lldb_private::Type::eResolveStateFull);
+ type_sp->SetEncodingType(element_type);
+ return type_sp;
+ } break;
+ case PDB_SymType::BuiltinType: {
+ auto *builtin_type = llvm::dyn_cast<PDBSymbolTypeBuiltin>(&type);
+ assert(builtin_type);
+ PDB_BuiltinType builtin_kind = builtin_type->getBuiltinType();
+ if (builtin_kind == PDB_BuiltinType::None)
+ return nullptr;
+
+ llvm::Optional<uint64_t> bytes;
+ if (uint64_t size = builtin_type->getLength())
+ bytes = size;
+ Encoding encoding = TranslateBuiltinEncoding(builtin_kind);
+ CompilerType builtin_ast_type = GetBuiltinTypeForPDBEncodingAndBitSize(
+ m_ast, *builtin_type, encoding, bytes.getValueOr(0) * 8);
+
+ if (builtin_type->isConstType())
+ builtin_ast_type = builtin_ast_type.AddConstModifier();
+
+ if (builtin_type->isVolatileType())
+ builtin_ast_type = builtin_ast_type.AddVolatileModifier();
+
+ auto type_name = GetPDBBuiltinTypeName(*builtin_type, builtin_ast_type);
+
+ return std::make_shared<lldb_private::Type>(
+ builtin_type->getSymIndexId(), m_ast.GetSymbolFile(), type_name, bytes,
+ nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl,
+ builtin_ast_type, lldb_private::Type::eResolveStateFull);
+ } break;
+ case PDB_SymType::PointerType: {
+ auto *pointer_type = llvm::dyn_cast<PDBSymbolTypePointer>(&type);
+ assert(pointer_type);
+ Type *pointee_type = m_ast.GetSymbolFile()->ResolveTypeUID(
+ pointer_type->getPointeeType()->getSymIndexId());
+ if (!pointee_type)
+ return nullptr;
+
+ if (pointer_type->isPointerToDataMember() ||
+ pointer_type->isPointerToMemberFunction()) {
+ auto class_parent_uid = pointer_type->getRawSymbol().getClassParentId();
+ auto class_parent_type =
+ m_ast.GetSymbolFile()->ResolveTypeUID(class_parent_uid);
+ assert(class_parent_type);
+
+ CompilerType pointer_ast_type;
+ pointer_ast_type = ClangASTContext::CreateMemberPointerType(
+ class_parent_type->GetLayoutCompilerType(),
+ pointee_type->GetForwardCompilerType());
+ assert(pointer_ast_type);
+
+ return std::make_shared<lldb_private::Type>(
+ pointer_type->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(),
+ pointer_type->getLength(), nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, pointer_ast_type,
+ lldb_private::Type::eResolveStateForward);
+ }
+
+ CompilerType pointer_ast_type;
+ pointer_ast_type = pointee_type->GetFullCompilerType();
+ if (pointer_type->isReference())
+ pointer_ast_type = pointer_ast_type.GetLValueReferenceType();
+ else if (pointer_type->isRValueReference())
+ pointer_ast_type = pointer_ast_type.GetRValueReferenceType();
+ else
+ pointer_ast_type = pointer_ast_type.GetPointerType();
+
+ if (pointer_type->isConstType())
+ pointer_ast_type = pointer_ast_type.AddConstModifier();
+
+ if (pointer_type->isVolatileType())
+ pointer_ast_type = pointer_ast_type.AddVolatileModifier();
+
+ if (pointer_type->isRestrictedType())
+ pointer_ast_type = pointer_ast_type.AddRestrictModifier();
+
+ return std::make_shared<lldb_private::Type>(
+ pointer_type->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(),
+ pointer_type->getLength(), nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, pointer_ast_type,
+ lldb_private::Type::eResolveStateFull);
+ } break;
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+bool PDBASTParser::CompleteTypeFromPDB(
+ lldb_private::CompilerType &compiler_type) {
+ if (GetClangASTImporter().CanImport(compiler_type))
+ return GetClangASTImporter().CompleteType(compiler_type);
+
+ // Remove the type from the forward declarations to avoid
+ // an endless recursion for types like a linked list.
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(compiler_type.GetOpaqueQualType());
+ auto uid_it = m_forward_decl_to_uid.find(record_decl);
+ if (uid_it == m_forward_decl_to_uid.end())
+ return true;
+
+ auto symbol_file = static_cast<SymbolFilePDB *>(m_ast.GetSymbolFile());
+ if (!symbol_file)
+ return false;
+
+ std::unique_ptr<PDBSymbol> symbol =
+ symbol_file->GetPDBSession().getSymbolById(uid_it->getSecond());
+ if (!symbol)
+ return false;
+
+ m_forward_decl_to_uid.erase(uid_it);
+
+ ClangASTContext::SetHasExternalStorage(compiler_type.GetOpaqueQualType(),
+ false);
+
+ switch (symbol->getSymTag()) {
+ case PDB_SymType::UDT: {
+ auto udt = llvm::dyn_cast<PDBSymbolTypeUDT>(symbol.get());
+ if (!udt)
+ return false;
+
+ return CompleteTypeFromUDT(*symbol_file, compiler_type, *udt);
+ }
+ default:
+ llvm_unreachable("not a forward clang type decl!");
+ }
+}
+
+clang::Decl *
+PDBASTParser::GetDeclForSymbol(const llvm::pdb::PDBSymbol &symbol) {
+ uint32_t sym_id = symbol.getSymIndexId();
+ auto it = m_uid_to_decl.find(sym_id);
+ if (it != m_uid_to_decl.end())
+ return it->second;
+
+ auto symbol_file = static_cast<SymbolFilePDB *>(m_ast.GetSymbolFile());
+ if (!symbol_file)
+ return nullptr;
+
+ // First of all, check if the symbol is a member of a class. Resolve the full
+ // class type and return the declaration from the cache if so.
+ auto tag = symbol.getSymTag();
+ if (tag == PDB_SymType::Data || tag == PDB_SymType::Function) {
+ const IPDBSession &session = symbol.getSession();
+ const IPDBRawSymbol &raw = symbol.getRawSymbol();
+
+ auto class_parent_id = raw.getClassParentId();
+ if (std::unique_ptr<PDBSymbol> class_parent =
+ session.getSymbolById(class_parent_id)) {
+ auto class_parent_type = symbol_file->ResolveTypeUID(class_parent_id);
+ if (!class_parent_type)
+ return nullptr;
+
+ CompilerType class_parent_ct = class_parent_type->GetFullCompilerType();
+
+ // Look a declaration up in the cache after completing the class
+ clang::Decl *decl = m_uid_to_decl.lookup(sym_id);
+ if (decl)
+ return decl;
+
+ // A declaration was not found in the cache. It means that the symbol
+ // has the class parent, but the class doesn't have the symbol in its
+ // children list.
+ if (auto func = llvm::dyn_cast_or_null<PDBSymbolFunc>(&symbol)) {
+ // Try to find a class child method with the same RVA and use its
+ // declaration if found.
+ if (uint32_t rva = func->getRelativeVirtualAddress()) {
+ if (std::unique_ptr<ConcreteSymbolEnumerator<PDBSymbolFunc>>
+ methods_enum =
+ class_parent->findAllChildren<PDBSymbolFunc>()) {
+ while (std::unique_ptr<PDBSymbolFunc> method =
+ methods_enum->getNext()) {
+ if (method->getRelativeVirtualAddress() == rva) {
+ decl = m_uid_to_decl.lookup(method->getSymIndexId());
+ if (decl)
+ break;
+ }
+ }
+ }
+ }
+
+ // If no class methods with the same RVA were found, then create a new
+ // method. It is possible for template methods.
+ if (!decl)
+ decl = AddRecordMethod(*symbol_file, class_parent_ct, *func);
+ }
+
+ if (decl)
+ m_uid_to_decl[sym_id] = decl;
+
+ return decl;
+ }
+ }
+
+ // If we are here, then the symbol is not belonging to a class and is not
+ // contained in the cache. So create a declaration for it.
+ switch (symbol.getSymTag()) {
+ case PDB_SymType::Data: {
+ auto data = llvm::dyn_cast<PDBSymbolData>(&symbol);
+ assert(data);
+
+ auto decl_context = GetDeclContextContainingSymbol(symbol);
+ assert(decl_context);
+
+ // May be the current context is a class really, but we haven't found
+ // any class parent. This happens e.g. in the case of class static
+ // variables - they has two symbols, one is a child of the class when
+ // another is a child of the exe. So always complete the parent and use
+ // an existing declaration if possible.
+ if (auto parent_decl = llvm::dyn_cast_or_null<clang::TagDecl>(decl_context))
+ m_ast.GetCompleteDecl(parent_decl);
+
+ std::string name = MSVCUndecoratedNameParser::DropScope(data->getName());
+
+ // Check if the current context already contains the symbol with the name.
+ clang::Decl *decl =
+ GetDeclFromContextByName(*m_ast.getASTContext(), *decl_context, name);
+ if (!decl) {
+ auto type = symbol_file->ResolveTypeUID(data->getTypeId());
+ if (!type)
+ return nullptr;
+
+ decl = m_ast.CreateVariableDeclaration(
+ decl_context, name.c_str(),
+ ClangUtil::GetQualType(type->GetLayoutCompilerType()));
+ }
+
+ m_uid_to_decl[sym_id] = decl;
+
+ return decl;
+ }
+ case PDB_SymType::Function: {
+ auto func = llvm::dyn_cast<PDBSymbolFunc>(&symbol);
+ assert(func);
+
+ auto decl_context = GetDeclContextContainingSymbol(symbol);
+ assert(decl_context);
+
+ std::string name = MSVCUndecoratedNameParser::DropScope(func->getName());
+
+ Type *type = symbol_file->ResolveTypeUID(sym_id);
+ if (!type)
+ return nullptr;
+
+ auto storage = func->isStatic() ? clang::StorageClass::SC_Static
+ : clang::StorageClass::SC_None;
+
+ auto decl = m_ast.CreateFunctionDeclaration(
+ decl_context, name.c_str(), type->GetForwardCompilerType(), storage,
+ func->hasInlineAttribute());
+
+ std::vector<clang::ParmVarDecl *> params;
+ if (std::unique_ptr<PDBSymbolTypeFunctionSig> sig = func->getSignature()) {
+ if (std::unique_ptr<ConcreteSymbolEnumerator<PDBSymbolTypeFunctionArg>>
+ arg_enum = sig->findAllChildren<PDBSymbolTypeFunctionArg>()) {
+ while (std::unique_ptr<PDBSymbolTypeFunctionArg> arg =
+ arg_enum->getNext()) {
+ Type *arg_type = symbol_file->ResolveTypeUID(arg->getTypeId());
+ if (!arg_type)
+ continue;
+
+ clang::ParmVarDecl *param = m_ast.CreateParameterDeclaration(
+ decl, nullptr, arg_type->GetForwardCompilerType(),
+ clang::SC_None);
+ if (param)
+ params.push_back(param);
+ }
+ }
+ }
+ if (params.size())
+ m_ast.SetFunctionParameters(decl, params.data(), params.size());
+
+ m_uid_to_decl[sym_id] = decl;
+
+ return decl;
+ }
+ default: {
+ // It's not a variable and not a function, check if it's a type
+ Type *type = symbol_file->ResolveTypeUID(sym_id);
+ if (!type)
+ return nullptr;
+
+ return m_uid_to_decl.lookup(sym_id);
+ }
+ }
+}
+
+clang::DeclContext *
+PDBASTParser::GetDeclContextForSymbol(const llvm::pdb::PDBSymbol &symbol) {
+ if (symbol.getSymTag() == PDB_SymType::Function) {
+ clang::DeclContext *result =
+ llvm::dyn_cast_or_null<clang::FunctionDecl>(GetDeclForSymbol(symbol));
+
+ if (result)
+ m_decl_context_to_uid[result] = symbol.getSymIndexId();
+
+ return result;
+ }
+
+ auto symbol_file = static_cast<SymbolFilePDB *>(m_ast.GetSymbolFile());
+ if (!symbol_file)
+ return nullptr;
+
+ auto type = symbol_file->ResolveTypeUID(symbol.getSymIndexId());
+ if (!type)
+ return nullptr;
+
+ clang::DeclContext *result =
+ m_ast.GetDeclContextForType(type->GetForwardCompilerType());
+
+ if (result)
+ m_decl_context_to_uid[result] = symbol.getSymIndexId();
+
+ return result;
+}
+
+clang::DeclContext *PDBASTParser::GetDeclContextContainingSymbol(
+ const llvm::pdb::PDBSymbol &symbol) {
+ auto parent = GetClassOrFunctionParent(symbol);
+ while (parent) {
+ if (auto parent_context = GetDeclContextForSymbol(*parent))
+ return parent_context;
+
+ parent = GetClassOrFunctionParent(*parent);
+ }
+
+ // We can't find any class or function parent of the symbol. So analyze
+ // the full symbol name. The symbol may be belonging to a namespace
+ // or function (or even to a class if it's e.g. a static variable symbol).
+
+ // TODO: Make clang to emit full names for variables in namespaces
+ // (as MSVC does)
+
+ std::string name(symbol.getRawSymbol().getName());
+ MSVCUndecoratedNameParser parser(name);
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers();
+ if (specs.empty())
+ return m_ast.GetTranslationUnitDecl();
+
+ auto symbol_file = static_cast<SymbolFilePDB *>(m_ast.GetSymbolFile());
+ if (!symbol_file)
+ return m_ast.GetTranslationUnitDecl();
+
+ auto global = symbol_file->GetPDBSession().getGlobalScope();
+ if (!global)
+ return m_ast.GetTranslationUnitDecl();
+
+ bool has_type_or_function_parent = false;
+ clang::DeclContext *curr_context = m_ast.GetTranslationUnitDecl();
+ for (std::size_t i = 0; i < specs.size() - 1; i++) {
+ // Check if there is a function or a type with the current context's name.
+ if (std::unique_ptr<IPDBEnumSymbols> children_enum = global->findChildren(
+ PDB_SymType::None, specs[i].GetFullName(), NS_CaseSensitive)) {
+ while (IPDBEnumChildren<PDBSymbol>::ChildTypePtr child =
+ children_enum->getNext()) {
+ if (clang::DeclContext *child_context =
+ GetDeclContextForSymbol(*child)) {
+ // Note that `GetDeclContextForSymbol' retrieves
+ // a declaration context for functions and types only,
+ // so if we are here then `child_context' is guaranteed
+ // a function or a type declaration context.
+ has_type_or_function_parent = true;
+ curr_context = child_context;
+ }
+ }
+ }
+
+ // If there were no functions or types above then retrieve a namespace with
+ // the current context's name. There can be no namespaces inside a function
+ // or a type. We check it to avoid fake namespaces such as `__l2':
+ // `N0::N1::CClass::PrivateFunc::__l2::InnerFuncStruct'
+ if (!has_type_or_function_parent) {
+ std::string namespace_name = specs[i].GetBaseName();
+ const char *namespace_name_c_str =
+ IsAnonymousNamespaceName(namespace_name) ? nullptr
+ : namespace_name.data();
+ clang::NamespaceDecl *namespace_decl =
+ m_ast.GetUniqueNamespaceDeclaration(namespace_name_c_str,
+ curr_context);
+
+ m_parent_to_namespaces[curr_context].insert(namespace_decl);
+ m_namespaces.insert(namespace_decl);
+
+ curr_context = namespace_decl;
+ }
+ }
+
+ return curr_context;
+}
+
+void PDBASTParser::ParseDeclsForDeclContext(
+ const clang::DeclContext *decl_context) {
+ auto symbol_file = static_cast<SymbolFilePDB *>(m_ast.GetSymbolFile());
+ if (!symbol_file)
+ return;
+
+ IPDBSession &session = symbol_file->GetPDBSession();
+ auto symbol_up =
+ session.getSymbolById(m_decl_context_to_uid.lookup(decl_context));
+ auto global_up = session.getGlobalScope();
+
+ PDBSymbol *symbol;
+ if (symbol_up)
+ symbol = symbol_up.get();
+ else if (global_up)
+ symbol = global_up.get();
+ else
+ return;
+
+ if (auto children = symbol->findAllChildren())
+ while (auto child = children->getNext())
+ GetDeclForSymbol(*child);
+}
+
+clang::NamespaceDecl *
+PDBASTParser::FindNamespaceDecl(const clang::DeclContext *parent,
+ llvm::StringRef name) {
+ NamespacesSet *set;
+ if (parent) {
+ auto pit = m_parent_to_namespaces.find(parent);
+ if (pit == m_parent_to_namespaces.end())
+ return nullptr;
+
+ set = &pit->second;
+ } else {
+ set = &m_namespaces;
+ }
+ assert(set);
+
+ for (clang::NamespaceDecl *namespace_decl : *set)
+ if (namespace_decl->getName().equals(name))
+ return namespace_decl;
+
+ for (clang::NamespaceDecl *namespace_decl : *set)
+ if (namespace_decl->isAnonymousNamespace())
+ return FindNamespaceDecl(namespace_decl, name);
+
+ return nullptr;
+}
+
+bool PDBASTParser::AddEnumValue(CompilerType enum_type,
+ const PDBSymbolData &enum_value) {
+ Declaration decl;
+ Variant v = enum_value.getValue();
+ std::string name = MSVCUndecoratedNameParser::DropScope(enum_value.getName());
+ int64_t raw_value;
+ switch (v.Type) {
+ case PDB_VariantType::Int8:
+ raw_value = v.Value.Int8;
+ break;
+ case PDB_VariantType::Int16:
+ raw_value = v.Value.Int16;
+ break;
+ case PDB_VariantType::Int32:
+ raw_value = v.Value.Int32;
+ break;
+ case PDB_VariantType::Int64:
+ raw_value = v.Value.Int64;
+ break;
+ case PDB_VariantType::UInt8:
+ raw_value = v.Value.UInt8;
+ break;
+ case PDB_VariantType::UInt16:
+ raw_value = v.Value.UInt16;
+ break;
+ case PDB_VariantType::UInt32:
+ raw_value = v.Value.UInt32;
+ break;
+ case PDB_VariantType::UInt64:
+ raw_value = v.Value.UInt64;
+ break;
+ default:
+ return false;
+ }
+ CompilerType underlying_type =
+ m_ast.GetEnumerationIntegerType(enum_type.GetOpaqueQualType());
+ uint32_t byte_size = m_ast.getASTContext()->getTypeSize(
+ ClangUtil::GetQualType(underlying_type));
+ auto enum_constant_decl = m_ast.AddEnumerationValueToEnumerationType(
+ enum_type, decl, name.c_str(), raw_value, byte_size * 8);
+ if (!enum_constant_decl)
+ return false;
+
+ m_uid_to_decl[enum_value.getSymIndexId()] = enum_constant_decl;
+
+ return true;
+}
+
+bool PDBASTParser::CompleteTypeFromUDT(
+ lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &compiler_type,
+ llvm::pdb::PDBSymbolTypeUDT &udt) {
+ ClangASTImporter::LayoutInfo layout_info;
+ layout_info.bit_size = udt.getLength() * 8;
+
+ auto nested_enums = udt.findAllChildren<PDBSymbolTypeUDT>();
+ if (nested_enums)
+ while (auto nested = nested_enums->getNext())
+ symbol_file.ResolveTypeUID(nested->getSymIndexId());
+
+ auto bases_enum = udt.findAllChildren<PDBSymbolTypeBaseClass>();
+ if (bases_enum)
+ AddRecordBases(symbol_file, compiler_type,
+ TranslateUdtKind(udt.getUdtKind()), *bases_enum,
+ layout_info);
+
+ auto members_enum = udt.findAllChildren<PDBSymbolData>();
+ if (members_enum)
+ AddRecordMembers(symbol_file, compiler_type, *members_enum, layout_info);
+
+ auto methods_enum = udt.findAllChildren<PDBSymbolFunc>();
+ if (methods_enum)
+ AddRecordMethods(symbol_file, compiler_type, *methods_enum);
+
+ m_ast.AddMethodOverridesForCXXRecordType(compiler_type.GetOpaqueQualType());
+ ClangASTContext::BuildIndirectFields(compiler_type);
+ ClangASTContext::CompleteTagDeclarationDefinition(compiler_type);
+
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(compiler_type.GetOpaqueQualType());
+ if (!record_decl)
+ return static_cast<bool>(compiler_type);
+
+ GetClangASTImporter().InsertRecordDecl(record_decl, layout_info);
+
+ return static_cast<bool>(compiler_type);
+}
+
+void PDBASTParser::AddRecordMembers(
+ lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ PDBDataSymbolEnumerator &members_enum,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info) {
+ while (auto member = members_enum.getNext()) {
+ if (member->isCompilerGenerated())
+ continue;
+
+ auto member_name = member->getName();
+
+ auto member_type = symbol_file.ResolveTypeUID(member->getTypeId());
+ if (!member_type)
+ continue;
+
+ auto member_comp_type = member_type->GetLayoutCompilerType();
+ if (!member_comp_type.GetCompleteType()) {
+ symbol_file.GetObjectFile()->GetModule()->ReportError(
+ ":: Class '%s' has a member '%s' of type '%s' "
+ "which does not have a complete definition.",
+ record_type.GetTypeName().GetCString(), member_name.c_str(),
+ member_comp_type.GetTypeName().GetCString());
+ if (ClangASTContext::StartTagDeclarationDefinition(member_comp_type))
+ ClangASTContext::CompleteTagDeclarationDefinition(member_comp_type);
+ }
+
+ auto access = TranslateMemberAccess(member->getAccess());
+
+ switch (member->getDataKind()) {
+ case PDB_DataKind::Member: {
+ auto location_type = member->getLocationType();
+
+ auto bit_size = member->getLength();
+ if (location_type == PDB_LocType::ThisRel)
+ bit_size *= 8;
+
+ auto decl = ClangASTContext::AddFieldToRecordType(
+ record_type, member_name.c_str(), member_comp_type, access, bit_size);
+ if (!decl)
+ continue;
+
+ m_uid_to_decl[member->getSymIndexId()] = decl;
+
+ auto offset = member->getOffset() * 8;
+ if (location_type == PDB_LocType::BitField)
+ offset += member->getBitPosition();
+
+ layout_info.field_offsets.insert(std::make_pair(decl, offset));
+
+ break;
+ }
+ case PDB_DataKind::StaticMember: {
+ auto decl = ClangASTContext::AddVariableToRecordType(
+ record_type, member_name.c_str(), member_comp_type, access);
+ if (!decl)
+ continue;
+
+ m_uid_to_decl[member->getSymIndexId()] = decl;
+
+ break;
+ }
+ default:
+ llvm_unreachable("unsupported PDB data kind");
+ }
+ }
+}
+
+void PDBASTParser::AddRecordBases(
+ lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type, int record_kind,
+ PDBBaseClassSymbolEnumerator &bases_enum,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info) const {
+ std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> base_classes;
+
+ while (auto base = bases_enum.getNext()) {
+ auto base_type = symbol_file.ResolveTypeUID(base->getTypeId());
+ if (!base_type)
+ continue;
+
+ auto base_comp_type = base_type->GetFullCompilerType();
+ if (!base_comp_type.GetCompleteType()) {
+ symbol_file.GetObjectFile()->GetModule()->ReportError(
+ ":: Class '%s' has a base class '%s' "
+ "which does not have a complete definition.",
+ record_type.GetTypeName().GetCString(),
+ base_comp_type.GetTypeName().GetCString());
+ if (ClangASTContext::StartTagDeclarationDefinition(base_comp_type))
+ ClangASTContext::CompleteTagDeclarationDefinition(base_comp_type);
+ }
+
+ auto access = TranslateMemberAccess(base->getAccess());
+
+ auto is_virtual = base->isVirtualBaseClass();
+
+ std::unique_ptr<clang::CXXBaseSpecifier> base_spec =
+ m_ast.CreateBaseClassSpecifier(base_comp_type.GetOpaqueQualType(),
+ access, is_virtual,
+ record_kind == clang::TTK_Class);
+ lldbassert(base_spec);
+
+ base_classes.push_back(std::move(base_spec));
+
+ if (is_virtual)
+ continue;
+
+ auto decl = m_ast.GetAsCXXRecordDecl(base_comp_type.GetOpaqueQualType());
+ if (!decl)
+ continue;
+
+ auto offset = clang::CharUnits::fromQuantity(base->getOffset());
+ layout_info.base_offsets.insert(std::make_pair(decl, offset));
+ }
+
+ m_ast.TransferBaseClasses(record_type.GetOpaqueQualType(),
+ std::move(base_classes));
+}
+
+void PDBASTParser::AddRecordMethods(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ PDBFuncSymbolEnumerator &methods_enum) {
+ while (std::unique_ptr<PDBSymbolFunc> method = methods_enum.getNext())
+ if (clang::CXXMethodDecl *decl =
+ AddRecordMethod(symbol_file, record_type, *method))
+ m_uid_to_decl[method->getSymIndexId()] = decl;
+}
+
+clang::CXXMethodDecl *
+PDBASTParser::AddRecordMethod(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ const llvm::pdb::PDBSymbolFunc &method) const {
+ std::string name = MSVCUndecoratedNameParser::DropScope(method.getName());
+
+ Type *method_type = symbol_file.ResolveTypeUID(method.getSymIndexId());
+ // MSVC specific __vecDelDtor.
+ if (!method_type)
+ return nullptr;
+
+ CompilerType method_comp_type = method_type->GetFullCompilerType();
+ if (!method_comp_type.GetCompleteType()) {
+ symbol_file.GetObjectFile()->GetModule()->ReportError(
+ ":: Class '%s' has a method '%s' whose type cannot be completed.",
+ record_type.GetTypeName().GetCString(),
+ method_comp_type.GetTypeName().GetCString());
+ if (ClangASTContext::StartTagDeclarationDefinition(method_comp_type))
+ ClangASTContext::CompleteTagDeclarationDefinition(method_comp_type);
+ }
+
+ AccessType access = TranslateMemberAccess(method.getAccess());
+ if (access == eAccessNone)
+ access = eAccessPublic;
+
+ // TODO: get mangled name for the method.
+ return m_ast.AddMethodToCXXRecordType(
+ record_type.GetOpaqueQualType(), name.c_str(),
+ /*mangled_name*/ nullptr, method_comp_type, access, method.isVirtual(),
+ method.isStatic(), method.hasInlineAttribute(),
+ /*is_explicit*/ false, // FIXME: Need this field in CodeView.
+ /*is_attr_used*/ false,
+ /*is_artificial*/ method.isCompilerGenerated());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.h
new file mode 100644
index 000000000000..9221d42b2020
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.h
@@ -0,0 +1,116 @@
+//===-- PDBASTParser.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H
+#define LLDB_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H
+
+#include "lldb/lldb-forward.h"
+
+#include "lldb/Symbol/ClangASTImporter.h"
+
+class SymbolFilePDB;
+
+namespace clang {
+class CharUnits;
+class CXXRecordDecl;
+class FieldDecl;
+class RecordDecl;
+} // namespace clang
+
+namespace lldb_private {
+class ClangASTContext;
+class CompilerType;
+} // namespace lldb_private
+
+namespace llvm {
+namespace pdb {
+template <typename ChildType> class ConcreteSymbolEnumerator;
+
+class PDBSymbol;
+class PDBSymbolData;
+class PDBSymbolFunc;
+class PDBSymbolTypeBaseClass;
+class PDBSymbolTypeBuiltin;
+class PDBSymbolTypeUDT;
+} // namespace pdb
+} // namespace llvm
+
+class PDBASTParser {
+public:
+ PDBASTParser(lldb_private::ClangASTContext &ast);
+ ~PDBASTParser();
+
+ lldb::TypeSP CreateLLDBTypeFromPDBType(const llvm::pdb::PDBSymbol &type);
+ bool CompleteTypeFromPDB(lldb_private::CompilerType &compiler_type);
+
+ clang::Decl *GetDeclForSymbol(const llvm::pdb::PDBSymbol &symbol);
+
+ clang::DeclContext *
+ GetDeclContextForSymbol(const llvm::pdb::PDBSymbol &symbol);
+ clang::DeclContext *
+ GetDeclContextContainingSymbol(const llvm::pdb::PDBSymbol &symbol);
+
+ void ParseDeclsForDeclContext(const clang::DeclContext *decl_context);
+
+ clang::NamespaceDecl *FindNamespaceDecl(const clang::DeclContext *parent,
+ llvm::StringRef name);
+
+ lldb_private::ClangASTImporter &GetClangASTImporter() {
+ return m_ast_importer;
+ }
+
+private:
+ typedef llvm::DenseMap<clang::CXXRecordDecl *, lldb::user_id_t>
+ CXXRecordDeclToUidMap;
+ typedef llvm::DenseMap<lldb::user_id_t, clang::Decl *> UidToDeclMap;
+ typedef std::set<clang::NamespaceDecl *> NamespacesSet;
+ typedef llvm::DenseMap<clang::DeclContext *, NamespacesSet>
+ ParentToNamespacesMap;
+ typedef llvm::DenseMap<clang::DeclContext *, lldb::user_id_t>
+ DeclContextToUidMap;
+ typedef llvm::pdb::ConcreteSymbolEnumerator<llvm::pdb::PDBSymbolData>
+ PDBDataSymbolEnumerator;
+ typedef llvm::pdb::ConcreteSymbolEnumerator<llvm::pdb::PDBSymbolTypeBaseClass>
+ PDBBaseClassSymbolEnumerator;
+ typedef llvm::pdb::ConcreteSymbolEnumerator<llvm::pdb::PDBSymbolFunc>
+ PDBFuncSymbolEnumerator;
+
+ bool AddEnumValue(lldb_private::CompilerType enum_type,
+ const llvm::pdb::PDBSymbolData &data);
+ bool CompleteTypeFromUDT(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &compiler_type,
+ llvm::pdb::PDBSymbolTypeUDT &udt);
+ void
+ AddRecordMembers(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ PDBDataSymbolEnumerator &members_enum,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info);
+ void
+ AddRecordBases(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type, int record_kind,
+ PDBBaseClassSymbolEnumerator &bases_enum,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info) const;
+ void AddRecordMethods(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ PDBFuncSymbolEnumerator &methods_enum);
+ clang::CXXMethodDecl *
+ AddRecordMethod(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ const llvm::pdb::PDBSymbolFunc &method) const;
+
+ lldb_private::ClangASTContext &m_ast;
+ lldb_private::ClangASTImporter m_ast_importer;
+
+ CXXRecordDeclToUidMap m_forward_decl_to_uid;
+ UidToDeclMap m_uid_to_decl;
+ ParentToNamespacesMap m_parent_to_namespaces;
+ NamespacesSet m_namespaces;
+ DeclContextToUidMap m_decl_context_to_uid;
+};
+
+#endif // LLDB_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp
new file mode 100644
index 000000000000..1c17bf6563b3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp
@@ -0,0 +1,181 @@
+//===-- PDBLocationToDWARFExpression.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PDBLocationToDWARFExpression.h"
+
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StreamBuffer.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Utility/DataBufferHeap.h"
+
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+
+#include "Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h"
+#include "Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::pdb;
+
+static std::unique_ptr<IPDBFrameData>
+GetCorrespondingFrameData(const IPDBSession &session,
+ const Variable::RangeList &ranges) {
+ auto enumFrameData = session.getFrameData();
+ if (!enumFrameData)
+ return nullptr;
+
+ std::unique_ptr<IPDBFrameData> found;
+ while (auto fd = enumFrameData->getNext()) {
+ Range<lldb::addr_t, lldb::addr_t> fdRange(fd->getVirtualAddress(),
+ fd->getLengthBlock());
+
+ for (size_t i = 0; i < ranges.GetSize(); i++) {
+ auto range = ranges.GetEntryAtIndex(i);
+ if (!range)
+ continue;
+
+ if (!range->DoesIntersect(fdRange))
+ continue;
+
+ found = std::move(fd);
+
+ break;
+ }
+ }
+
+ return found;
+}
+
+static bool EmitVFrameEvaluationDWARFExpression(
+ llvm::StringRef program, llvm::Triple::ArchType arch_type, Stream &stream) {
+ // VFrame value always stored in $TO pseudo-register
+ return TranslateFPOProgramToDWARFExpression(program, "$T0", arch_type,
+ stream);
+}
+
+DWARFExpression ConvertPDBLocationToDWARFExpression(
+ ModuleSP module, const PDBSymbolData &symbol,
+ const Variable::RangeList &ranges, bool &is_constant) {
+ is_constant = true;
+
+ if (!module)
+ return DWARFExpression();
+
+ const ArchSpec &architecture = module->GetArchitecture();
+ llvm::Triple::ArchType arch_type = architecture.GetMachine();
+ ByteOrder byte_order = architecture.GetByteOrder();
+ uint32_t address_size = architecture.GetAddressByteSize();
+ uint32_t byte_size = architecture.GetDataByteSize();
+ if (byte_order == eByteOrderInvalid || address_size == 0)
+ return DWARFExpression();
+
+ RegisterKind register_kind = eRegisterKindDWARF;
+ StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order);
+ switch (symbol.getLocationType()) {
+ case PDB_LocType::Static:
+ case PDB_LocType::TLS: {
+ stream.PutHex8(DW_OP_addr);
+
+ SectionList *section_list = module->GetSectionList();
+ if (!section_list)
+ return DWARFExpression();
+
+ uint32_t section_id = symbol.getAddressSection();
+
+ auto section = section_list->FindSectionByID(section_id);
+ if (!section)
+ return DWARFExpression();
+
+ uint32_t offset = symbol.getAddressOffset();
+ stream.PutMaxHex64(section->GetFileAddress() + offset, address_size,
+ byte_order);
+
+ is_constant = false;
+
+ break;
+ }
+ case PDB_LocType::RegRel: {
+ uint32_t reg_num;
+ auto reg_id = symbol.getRegisterId();
+ if (reg_id == llvm::codeview::RegisterId::VFRAME) {
+ if (auto fd = GetCorrespondingFrameData(symbol.getSession(), ranges)) {
+ if (EmitVFrameEvaluationDWARFExpression(fd->getProgram(), arch_type,
+ stream)) {
+ int32_t offset = symbol.getOffset();
+ stream.PutHex8(DW_OP_consts);
+ stream.PutSLEB128(offset);
+ stream.PutHex8(DW_OP_plus);
+
+ register_kind = eRegisterKindLLDB;
+
+ is_constant = false;
+ break;
+ }
+ }
+
+ register_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_FP;
+ } else {
+ register_kind = eRegisterKindLLDB;
+ reg_num = GetLLDBRegisterNumber(arch_type, reg_id);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return DWARFExpression();
+ }
+
+ if (reg_num > 31) {
+ stream.PutHex8(DW_OP_bregx);
+ stream.PutULEB128(reg_num);
+ } else
+ stream.PutHex8(DW_OP_breg0 + reg_num);
+
+ int32_t offset = symbol.getOffset();
+ stream.PutSLEB128(offset);
+
+ is_constant = false;
+
+ break;
+ }
+ case PDB_LocType::Enregistered: {
+ register_kind = eRegisterKindLLDB;
+ uint32_t reg_num = GetLLDBRegisterNumber(arch_type, symbol.getRegisterId());
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return DWARFExpression();
+
+ if (reg_num > 31) {
+ stream.PutHex8(DW_OP_regx);
+ stream.PutULEB128(reg_num);
+ } else
+ stream.PutHex8(DW_OP_reg0 + reg_num);
+
+ is_constant = false;
+
+ break;
+ }
+ case PDB_LocType::Constant: {
+ Variant value = symbol.getValue();
+ stream.PutRawBytes(&value.Value, sizeof(value.Value),
+ endian::InlHostByteOrder());
+ break;
+ }
+ default:
+ return DWARFExpression();
+ }
+
+ DataBufferSP buffer =
+ std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize());
+ DataExtractor extractor(buffer, byte_order, address_size, byte_size);
+ DWARFExpression result(module, extractor, nullptr, 0, buffer->GetByteSize());
+ result.SetRegisterKind(register_kind);
+
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h
new file mode 100644
index 000000000000..2e9d1386d537
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h
@@ -0,0 +1,47 @@
+//===-- PDBLocationToDWARFExpression.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_Plugins_SymbolFile_PDB_PDBLocationToDWARFExpression_h_
+#define lldb_Plugins_SymbolFile_PDB_PDBLocationToDWARFExpression_h_
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Variable.h"
+
+namespace lldb_private {
+class DWARFExpression;
+}
+
+namespace llvm {
+namespace pdb {
+class PDBSymbolData;
+}
+} // namespace llvm
+
+/// Converts a location information from a PDB symbol to a DWARF expression
+///
+/// \param[in] module
+/// The module \a symbol belongs to.
+///
+/// \param[in] symbol
+/// The symbol with a location information to convert.
+///
+/// \param[in] ranges
+/// Ranges where this variable is valid.
+///
+/// \param[out] is_constant
+/// Set to \b true if the result expression is a constant value data,
+/// and \b false if it is a DWARF bytecode.
+///
+/// \return
+/// The DWARF expression corresponding to the location data of \a symbol.
+lldb_private::DWARFExpression
+ConvertPDBLocationToDWARFExpression(lldb::ModuleSP module,
+ const llvm::pdb::PDBSymbolData &symbol,
+ const lldb_private::Variable::RangeList &ranges,
+ bool &is_constant);
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
new file mode 100644
index 000000000000..17dfcdaceb9c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
@@ -0,0 +1,1999 @@
+//===-- SymbolFilePDB.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolFilePDB.h"
+
+#include "PDBASTParser.h"
+#include "PDBLocationToDWARFExpression.h"
+
+#include "clang/Lex/Lexer.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Utility/RegularExpression.h"
+
+#include "llvm/DebugInfo/PDB/GenericError.h"
+#include "llvm/DebugInfo/PDB/IPDBDataStream.h"
+#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
+#include "llvm/DebugInfo/PDB/IPDBLineNumber.h"
+#include "llvm/DebugInfo/PDB/IPDBSectionContrib.h"
+#include "llvm/DebugInfo/PDB/IPDBSourceFile.h"
+#include "llvm/DebugInfo/PDB/IPDBTable.h"
+#include "llvm/DebugInfo/PDB/PDBSymbol.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolBlock.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolCompilandDetails.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
+
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
+#include "Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h"
+
+#include <regex>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm::pdb;
+
+namespace {
+lldb::LanguageType TranslateLanguage(PDB_Lang lang) {
+ switch (lang) {
+ case PDB_Lang::Cpp:
+ return lldb::LanguageType::eLanguageTypeC_plus_plus;
+ case PDB_Lang::C:
+ return lldb::LanguageType::eLanguageTypeC;
+ case PDB_Lang::Swift:
+ return lldb::LanguageType::eLanguageTypeSwift;
+ default:
+ return lldb::LanguageType::eLanguageTypeUnknown;
+ }
+}
+
+bool ShouldAddLine(uint32_t requested_line, uint32_t actual_line,
+ uint32_t addr_length) {
+ return ((requested_line == 0 || actual_line == requested_line) &&
+ addr_length > 0);
+}
+} // namespace
+
+static bool ShouldUseNativeReader() {
+#if defined(_WIN32)
+ llvm::StringRef use_native = ::getenv("LLDB_USE_NATIVE_PDB_READER");
+ return use_native.equals_lower("on") || use_native.equals_lower("yes") ||
+ use_native.equals_lower("1") || use_native.equals_lower("true");
+#else
+ return true;
+#endif
+}
+
+void SymbolFilePDB::Initialize() {
+ if (ShouldUseNativeReader()) {
+ npdb::SymbolFileNativePDB::Initialize();
+ } else {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+ }
+}
+
+void SymbolFilePDB::Terminate() {
+ if (ShouldUseNativeReader()) {
+ npdb::SymbolFileNativePDB::Terminate();
+ } else {
+ PluginManager::UnregisterPlugin(CreateInstance);
+ }
+}
+
+void SymbolFilePDB::DebuggerInitialize(lldb_private::Debugger &debugger) {}
+
+lldb_private::ConstString SymbolFilePDB::GetPluginNameStatic() {
+ static ConstString g_name("pdb");
+ return g_name;
+}
+
+const char *SymbolFilePDB::GetPluginDescriptionStatic() {
+ return "Microsoft PDB debug symbol file reader.";
+}
+
+lldb_private::SymbolFile *
+SymbolFilePDB::CreateInstance(lldb_private::ObjectFile *obj_file) {
+ return new SymbolFilePDB(obj_file);
+}
+
+SymbolFilePDB::SymbolFilePDB(lldb_private::ObjectFile *object_file)
+ : SymbolFile(object_file), m_session_up(), m_global_scope_up(),
+ m_cached_compile_unit_count(0) {}
+
+SymbolFilePDB::~SymbolFilePDB() {}
+
+uint32_t SymbolFilePDB::CalculateAbilities() {
+ uint32_t abilities = 0;
+ if (!m_obj_file)
+ return 0;
+
+ if (!m_session_up) {
+ // Lazily load and match the PDB file, but only do this once.
+ std::string exePath = m_obj_file->GetFileSpec().GetPath();
+ auto error = loadDataForEXE(PDB_ReaderType::DIA, llvm::StringRef(exePath),
+ m_session_up);
+ if (error) {
+ llvm::consumeError(std::move(error));
+ auto module_sp = m_obj_file->GetModule();
+ if (!module_sp)
+ return 0;
+ // See if any symbol file is specified through `--symfile` option.
+ FileSpec symfile = module_sp->GetSymbolFileFileSpec();
+ if (!symfile)
+ return 0;
+ error = loadDataForPDB(PDB_ReaderType::DIA,
+ llvm::StringRef(symfile.GetPath()), m_session_up);
+ if (error) {
+ llvm::consumeError(std::move(error));
+ return 0;
+ }
+ }
+ }
+ if (!m_session_up)
+ return 0;
+
+ auto enum_tables_up = m_session_up->getEnumTables();
+ if (!enum_tables_up)
+ return 0;
+ while (auto table_up = enum_tables_up->getNext()) {
+ if (table_up->getItemCount() == 0)
+ continue;
+ auto type = table_up->getTableType();
+ switch (type) {
+ case PDB_TableType::Symbols:
+ // This table represents a store of symbols with types listed in
+ // PDBSym_Type
+ abilities |= (CompileUnits | Functions | Blocks | GlobalVariables |
+ LocalVariables | VariableTypes);
+ break;
+ case PDB_TableType::LineNumbers:
+ abilities |= LineTables;
+ break;
+ default:
+ break;
+ }
+ }
+ return abilities;
+}
+
+void SymbolFilePDB::InitializeObject() {
+ lldb::addr_t obj_load_address = m_obj_file->GetBaseAddress().GetFileAddress();
+ lldbassert(obj_load_address && obj_load_address != LLDB_INVALID_ADDRESS);
+ m_session_up->setLoadAddress(obj_load_address);
+ if (!m_global_scope_up)
+ m_global_scope_up = m_session_up->getGlobalScope();
+ lldbassert(m_global_scope_up.get());
+}
+
+uint32_t SymbolFilePDB::GetNumCompileUnits() {
+ if (m_cached_compile_unit_count == 0) {
+ auto compilands = m_global_scope_up->findAllChildren<PDBSymbolCompiland>();
+ if (!compilands)
+ return 0;
+
+ // The linker could link *.dll (compiland language = LINK), or import
+ // *.dll. For example, a compiland with name `Import:KERNEL32.dll` could be
+ // found as a child of the global scope (PDB executable). Usually, such
+ // compilands contain `thunk` symbols in which we are not interested for
+ // now. However we still count them in the compiland list. If we perform
+ // any compiland related activity, like finding symbols through
+ // llvm::pdb::IPDBSession methods, such compilands will all be searched
+ // automatically no matter whether we include them or not.
+ m_cached_compile_unit_count = compilands->getChildCount();
+
+ // The linker can inject an additional "dummy" compilation unit into the
+ // PDB. Ignore this special compile unit for our purposes, if it is there.
+ // It is always the last one.
+ auto last_compiland_up =
+ compilands->getChildAtIndex(m_cached_compile_unit_count - 1);
+ lldbassert(last_compiland_up.get());
+ std::string name = last_compiland_up->getName();
+ if (name == "* Linker *")
+ --m_cached_compile_unit_count;
+ }
+ return m_cached_compile_unit_count;
+}
+
+void SymbolFilePDB::GetCompileUnitIndex(
+ const llvm::pdb::PDBSymbolCompiland &pdb_compiland, uint32_t &index) {
+ auto results_up = m_global_scope_up->findAllChildren<PDBSymbolCompiland>();
+ if (!results_up)
+ return;
+ auto uid = pdb_compiland.getSymIndexId();
+ for (uint32_t cu_idx = 0; cu_idx < GetNumCompileUnits(); ++cu_idx) {
+ auto compiland_up = results_up->getChildAtIndex(cu_idx);
+ if (!compiland_up)
+ continue;
+ if (compiland_up->getSymIndexId() == uid) {
+ index = cu_idx;
+ return;
+ }
+ }
+ index = UINT32_MAX;
+ return;
+}
+
+std::unique_ptr<llvm::pdb::PDBSymbolCompiland>
+SymbolFilePDB::GetPDBCompilandByUID(uint32_t uid) {
+ return m_session_up->getConcreteSymbolById<PDBSymbolCompiland>(uid);
+}
+
+lldb::CompUnitSP SymbolFilePDB::ParseCompileUnitAtIndex(uint32_t index) {
+ if (index >= GetNumCompileUnits())
+ return CompUnitSP();
+
+ // Assuming we always retrieve same compilands listed in same order through
+ // `PDBSymbolExe::findAllChildren` method, otherwise using `index` to get a
+ // compile unit makes no sense.
+ auto results = m_global_scope_up->findAllChildren<PDBSymbolCompiland>();
+ if (!results)
+ return CompUnitSP();
+ auto compiland_up = results->getChildAtIndex(index);
+ if (!compiland_up)
+ return CompUnitSP();
+ return ParseCompileUnitForUID(compiland_up->getSymIndexId(), index);
+}
+
+lldb::LanguageType SymbolFilePDB::ParseLanguage(CompileUnit &comp_unit) {
+ auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID());
+ if (!compiland_up)
+ return lldb::eLanguageTypeUnknown;
+ auto details = compiland_up->findOneChild<PDBSymbolCompilandDetails>();
+ if (!details)
+ return lldb::eLanguageTypeUnknown;
+ return TranslateLanguage(details->getLanguage());
+}
+
+lldb_private::Function *
+SymbolFilePDB::ParseCompileUnitFunctionForPDBFunc(const PDBSymbolFunc &pdb_func,
+ CompileUnit &comp_unit) {
+ if (FunctionSP result = comp_unit.FindFunctionByUID(pdb_func.getSymIndexId()))
+ return result.get();
+
+ auto file_vm_addr = pdb_func.getVirtualAddress();
+ if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0)
+ return nullptr;
+
+ auto func_length = pdb_func.getLength();
+ AddressRange func_range =
+ AddressRange(file_vm_addr, func_length,
+ GetObjectFile()->GetModule()->GetSectionList());
+ if (!func_range.GetBaseAddress().IsValid())
+ return nullptr;
+
+ lldb_private::Type *func_type = ResolveTypeUID(pdb_func.getSymIndexId());
+ if (!func_type)
+ return nullptr;
+
+ user_id_t func_type_uid = pdb_func.getSignatureId();
+
+ Mangled mangled = GetMangledForPDBFunc(pdb_func);
+
+ FunctionSP func_sp =
+ std::make_shared<Function>(&comp_unit, pdb_func.getSymIndexId(),
+ func_type_uid, mangled, func_type, func_range);
+
+ comp_unit.AddFunction(func_sp);
+
+ LanguageType lang = ParseLanguage(comp_unit);
+ TypeSystem *type_system = GetTypeSystemForLanguage(lang);
+ if (!type_system)
+ return nullptr;
+ ClangASTContext *clang_type_system =
+ llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang_type_system)
+ return nullptr;
+ clang_type_system->GetPDBParser()->GetDeclForSymbol(pdb_func);
+
+ return func_sp.get();
+}
+
+size_t SymbolFilePDB::ParseFunctions(CompileUnit &comp_unit) {
+ size_t func_added = 0;
+ auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID());
+ if (!compiland_up)
+ return 0;
+ auto results_up = compiland_up->findAllChildren<PDBSymbolFunc>();
+ if (!results_up)
+ return 0;
+ while (auto pdb_func_up = results_up->getNext()) {
+ auto func_sp = comp_unit.FindFunctionByUID(pdb_func_up->getSymIndexId());
+ if (!func_sp) {
+ if (ParseCompileUnitFunctionForPDBFunc(*pdb_func_up, comp_unit))
+ ++func_added;
+ }
+ }
+ return func_added;
+}
+
+bool SymbolFilePDB::ParseLineTable(CompileUnit &comp_unit) {
+ if (comp_unit.GetLineTable())
+ return true;
+ return ParseCompileUnitLineTable(comp_unit, 0);
+}
+
+bool SymbolFilePDB::ParseDebugMacros(CompileUnit &comp_unit) {
+ // PDB doesn't contain information about macros
+ return false;
+}
+
+bool SymbolFilePDB::ParseSupportFiles(
+ CompileUnit &comp_unit, lldb_private::FileSpecList &support_files) {
+
+ // In theory this is unnecessary work for us, because all of this information
+ // is easily (and quickly) accessible from DebugInfoPDB, so caching it a
+ // second time seems like a waste. Unfortunately, there's no good way around
+ // this short of a moderate refactor since SymbolVendor depends on being able
+ // to cache this list.
+ auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID());
+ if (!compiland_up)
+ return false;
+ auto files = m_session_up->getSourceFilesForCompiland(*compiland_up);
+ if (!files || files->getChildCount() == 0)
+ return false;
+
+ while (auto file = files->getNext()) {
+ FileSpec spec(file->getFileName(), FileSpec::Style::windows);
+ support_files.AppendIfUnique(spec);
+ }
+
+ // LLDB uses the DWARF-like file numeration (one based),
+ // the zeroth file is the compile unit itself
+ support_files.Insert(0, comp_unit);
+
+ return true;
+}
+
+bool SymbolFilePDB::ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<SourceModule> &imported_modules) {
+ // PDB does not yet support module debug info
+ return false;
+}
+
+static size_t ParseFunctionBlocksForPDBSymbol(
+ uint64_t func_file_vm_addr, const llvm::pdb::PDBSymbol *pdb_symbol,
+ lldb_private::Block *parent_block, bool is_top_parent) {
+ assert(pdb_symbol && parent_block);
+
+ size_t num_added = 0;
+ switch (pdb_symbol->getSymTag()) {
+ case PDB_SymType::Block:
+ case PDB_SymType::Function: {
+ Block *block = nullptr;
+ auto &raw_sym = pdb_symbol->getRawSymbol();
+ if (auto *pdb_func = llvm::dyn_cast<PDBSymbolFunc>(pdb_symbol)) {
+ if (pdb_func->hasNoInlineAttribute())
+ break;
+ if (is_top_parent)
+ block = parent_block;
+ else
+ break;
+ } else if (llvm::dyn_cast<PDBSymbolBlock>(pdb_symbol)) {
+ auto uid = pdb_symbol->getSymIndexId();
+ if (parent_block->FindBlockByID(uid))
+ break;
+ if (raw_sym.getVirtualAddress() < func_file_vm_addr)
+ break;
+
+ auto block_sp = std::make_shared<Block>(pdb_symbol->getSymIndexId());
+ parent_block->AddChild(block_sp);
+ block = block_sp.get();
+ } else
+ llvm_unreachable("Unexpected PDB symbol!");
+
+ block->AddRange(Block::Range(
+ raw_sym.getVirtualAddress() - func_file_vm_addr, raw_sym.getLength()));
+ block->FinalizeRanges();
+ ++num_added;
+
+ auto results_up = pdb_symbol->findAllChildren();
+ if (!results_up)
+ break;
+ while (auto symbol_up = results_up->getNext()) {
+ num_added += ParseFunctionBlocksForPDBSymbol(
+ func_file_vm_addr, symbol_up.get(), block, false);
+ }
+ } break;
+ default:
+ break;
+ }
+ return num_added;
+}
+
+size_t SymbolFilePDB::ParseBlocksRecursive(Function &func) {
+ size_t num_added = 0;
+ auto uid = func.GetID();
+ auto pdb_func_up = m_session_up->getConcreteSymbolById<PDBSymbolFunc>(uid);
+ if (!pdb_func_up)
+ return 0;
+ Block &parent_block = func.GetBlock(false);
+ num_added = ParseFunctionBlocksForPDBSymbol(
+ pdb_func_up->getVirtualAddress(), pdb_func_up.get(), &parent_block, true);
+ return num_added;
+}
+
+size_t SymbolFilePDB::ParseTypes(CompileUnit &comp_unit) {
+
+ size_t num_added = 0;
+ auto compiland = GetPDBCompilandByUID(comp_unit.GetID());
+ if (!compiland)
+ return 0;
+
+ auto ParseTypesByTagFn = [&num_added, this](const PDBSymbol &raw_sym) {
+ std::unique_ptr<IPDBEnumSymbols> results;
+ PDB_SymType tags_to_search[] = {PDB_SymType::Enum, PDB_SymType::Typedef,
+ PDB_SymType::UDT};
+ for (auto tag : tags_to_search) {
+ results = raw_sym.findAllChildren(tag);
+ if (!results || results->getChildCount() == 0)
+ continue;
+ while (auto symbol = results->getNext()) {
+ switch (symbol->getSymTag()) {
+ case PDB_SymType::Enum:
+ case PDB_SymType::UDT:
+ case PDB_SymType::Typedef:
+ break;
+ default:
+ continue;
+ }
+
+ // This should cause the type to get cached and stored in the `m_types`
+ // lookup.
+ if (auto type = ResolveTypeUID(symbol->getSymIndexId())) {
+ // Resolve the type completely to avoid a completion
+ // (and so a list change, which causes an iterators invalidation)
+ // during a TypeList dumping
+ type->GetFullCompilerType();
+ ++num_added;
+ }
+ }
+ }
+ };
+
+ ParseTypesByTagFn(*compiland);
+
+ // Also parse global types particularly coming from this compiland.
+ // Unfortunately, PDB has no compiland information for each global type. We
+ // have to parse them all. But ensure we only do this once.
+ static bool parse_all_global_types = false;
+ if (!parse_all_global_types) {
+ ParseTypesByTagFn(*m_global_scope_up);
+ parse_all_global_types = true;
+ }
+ return num_added;
+}
+
+size_t
+SymbolFilePDB::ParseVariablesForContext(const lldb_private::SymbolContext &sc) {
+ if (!sc.comp_unit)
+ return 0;
+
+ size_t num_added = 0;
+ if (sc.function) {
+ auto pdb_func = m_session_up->getConcreteSymbolById<PDBSymbolFunc>(
+ sc.function->GetID());
+ if (!pdb_func)
+ return 0;
+
+ num_added += ParseVariables(sc, *pdb_func);
+ sc.function->GetBlock(false).SetDidParseVariables(true, true);
+ } else if (sc.comp_unit) {
+ auto compiland = GetPDBCompilandByUID(sc.comp_unit->GetID());
+ if (!compiland)
+ return 0;
+
+ if (sc.comp_unit->GetVariableList(false))
+ return 0;
+
+ auto results = m_global_scope_up->findAllChildren<PDBSymbolData>();
+ if (results && results->getChildCount()) {
+ while (auto result = results->getNext()) {
+ auto cu_id = GetCompilandId(*result);
+ // FIXME: We are not able to determine variable's compile unit.
+ if (cu_id == 0)
+ continue;
+
+ if (cu_id == sc.comp_unit->GetID())
+ num_added += ParseVariables(sc, *result);
+ }
+ }
+
+ // FIXME: A `file static` or `global constant` variable appears both in
+ // compiland's children and global scope's children with unexpectedly
+ // different symbol's Id making it ambiguous.
+
+ // FIXME: 'local constant', for example, const char var[] = "abc", declared
+ // in a function scope, can't be found in PDB.
+
+ // Parse variables in this compiland.
+ num_added += ParseVariables(sc, *compiland);
+ }
+
+ return num_added;
+}
+
+lldb_private::Type *SymbolFilePDB::ResolveTypeUID(lldb::user_id_t type_uid) {
+ auto find_result = m_types.find(type_uid);
+ if (find_result != m_types.end())
+ return find_result->second.get();
+
+ TypeSystem *type_system =
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus);
+ ClangASTContext *clang_type_system =
+ llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang_type_system)
+ return nullptr;
+ PDBASTParser *pdb = clang_type_system->GetPDBParser();
+ if (!pdb)
+ return nullptr;
+
+ auto pdb_type = m_session_up->getSymbolById(type_uid);
+ if (pdb_type == nullptr)
+ return nullptr;
+
+ lldb::TypeSP result = pdb->CreateLLDBTypeFromPDBType(*pdb_type);
+ if (result) {
+ m_types.insert(std::make_pair(type_uid, result));
+ auto type_list = GetTypeList();
+ if (type_list)
+ type_list->Insert(result);
+ }
+ return result.get();
+}
+
+llvm::Optional<SymbolFile::ArrayInfo> SymbolFilePDB::GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
+ return llvm::None;
+}
+
+bool SymbolFilePDB::CompleteType(lldb_private::CompilerType &compiler_type) {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetObjectFile()->GetModule()->GetMutex());
+
+ ClangASTContext *clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus));
+ if (!clang_ast_ctx)
+ return false;
+
+ PDBASTParser *pdb = clang_ast_ctx->GetPDBParser();
+ if (!pdb)
+ return false;
+
+ return pdb->CompleteTypeFromPDB(compiler_type);
+}
+
+lldb_private::CompilerDecl SymbolFilePDB::GetDeclForUID(lldb::user_id_t uid) {
+ ClangASTContext *clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus));
+ if (!clang_ast_ctx)
+ return CompilerDecl();
+
+ PDBASTParser *pdb = clang_ast_ctx->GetPDBParser();
+ if (!pdb)
+ return CompilerDecl();
+
+ auto symbol = m_session_up->getSymbolById(uid);
+ if (!symbol)
+ return CompilerDecl();
+
+ auto decl = pdb->GetDeclForSymbol(*symbol);
+ if (!decl)
+ return CompilerDecl();
+
+ return CompilerDecl(clang_ast_ctx, decl);
+}
+
+lldb_private::CompilerDeclContext
+SymbolFilePDB::GetDeclContextForUID(lldb::user_id_t uid) {
+ ClangASTContext *clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus));
+ if (!clang_ast_ctx)
+ return CompilerDeclContext();
+
+ PDBASTParser *pdb = clang_ast_ctx->GetPDBParser();
+ if (!pdb)
+ return CompilerDeclContext();
+
+ auto symbol = m_session_up->getSymbolById(uid);
+ if (!symbol)
+ return CompilerDeclContext();
+
+ auto decl_context = pdb->GetDeclContextForSymbol(*symbol);
+ if (!decl_context)
+ return GetDeclContextContainingUID(uid);
+
+ return CompilerDeclContext(clang_ast_ctx, decl_context);
+}
+
+lldb_private::CompilerDeclContext
+SymbolFilePDB::GetDeclContextContainingUID(lldb::user_id_t uid) {
+ ClangASTContext *clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus));
+ if (!clang_ast_ctx)
+ return CompilerDeclContext();
+
+ PDBASTParser *pdb = clang_ast_ctx->GetPDBParser();
+ if (!pdb)
+ return CompilerDeclContext();
+
+ auto symbol = m_session_up->getSymbolById(uid);
+ if (!symbol)
+ return CompilerDeclContext();
+
+ auto decl_context = pdb->GetDeclContextContainingSymbol(*symbol);
+ assert(decl_context);
+
+ return CompilerDeclContext(clang_ast_ctx, decl_context);
+}
+
+void SymbolFilePDB::ParseDeclsForContext(
+ lldb_private::CompilerDeclContext decl_ctx) {
+ ClangASTContext *clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus));
+ if (!clang_ast_ctx)
+ return;
+
+ PDBASTParser *pdb = clang_ast_ctx->GetPDBParser();
+ if (!pdb)
+ return;
+
+ pdb->ParseDeclsForDeclContext(
+ static_cast<clang::DeclContext *>(decl_ctx.GetOpaqueDeclContext()));
+}
+
+uint32_t
+SymbolFilePDB::ResolveSymbolContext(const lldb_private::Address &so_addr,
+ SymbolContextItem resolve_scope,
+ lldb_private::SymbolContext &sc) {
+ uint32_t resolved_flags = 0;
+ if (resolve_scope & eSymbolContextCompUnit ||
+ resolve_scope & eSymbolContextVariable ||
+ resolve_scope & eSymbolContextFunction ||
+ resolve_scope & eSymbolContextBlock ||
+ resolve_scope & eSymbolContextLineEntry) {
+ auto cu_sp = GetCompileUnitContainsAddress(so_addr);
+ if (!cu_sp) {
+ if (resolved_flags | eSymbolContextVariable) {
+ // TODO: Resolve variables
+ }
+ return 0;
+ }
+ sc.comp_unit = cu_sp.get();
+ resolved_flags |= eSymbolContextCompUnit;
+ lldbassert(sc.module_sp == cu_sp->GetModule());
+ }
+
+ if (resolve_scope & eSymbolContextFunction ||
+ resolve_scope & eSymbolContextBlock) {
+ addr_t file_vm_addr = so_addr.GetFileAddress();
+ auto symbol_up =
+ m_session_up->findSymbolByAddress(file_vm_addr, PDB_SymType::Function);
+ if (symbol_up) {
+ auto *pdb_func = llvm::dyn_cast<PDBSymbolFunc>(symbol_up.get());
+ assert(pdb_func);
+ auto func_uid = pdb_func->getSymIndexId();
+ sc.function = sc.comp_unit->FindFunctionByUID(func_uid).get();
+ if (sc.function == nullptr)
+ sc.function =
+ ParseCompileUnitFunctionForPDBFunc(*pdb_func, *sc.comp_unit);
+ if (sc.function) {
+ resolved_flags |= eSymbolContextFunction;
+ if (resolve_scope & eSymbolContextBlock) {
+ auto block_symbol = m_session_up->findSymbolByAddress(
+ file_vm_addr, PDB_SymType::Block);
+ auto block_id = block_symbol ? block_symbol->getSymIndexId()
+ : sc.function->GetID();
+ sc.block = sc.function->GetBlock(true).FindBlockByID(block_id);
+ if (sc.block)
+ resolved_flags |= eSymbolContextBlock;
+ }
+ }
+ }
+ }
+
+ if (resolve_scope & eSymbolContextLineEntry) {
+ if (auto *line_table = sc.comp_unit->GetLineTable()) {
+ Address addr(so_addr);
+ if (line_table->FindLineEntryByAddress(addr, sc.line_entry))
+ resolved_flags |= eSymbolContextLineEntry;
+ }
+ }
+
+ return resolved_flags;
+}
+
+uint32_t SymbolFilePDB::ResolveSymbolContext(
+ const lldb_private::FileSpec &file_spec, uint32_t line, bool check_inlines,
+ SymbolContextItem resolve_scope, lldb_private::SymbolContextList &sc_list) {
+ const size_t old_size = sc_list.GetSize();
+ if (resolve_scope & lldb::eSymbolContextCompUnit) {
+ // Locate all compilation units with line numbers referencing the specified
+ // file. For example, if `file_spec` is <vector>, then this should return
+ // all source files and header files that reference <vector>, either
+ // directly or indirectly.
+ auto compilands = m_session_up->findCompilandsForSourceFile(
+ file_spec.GetPath(), PDB_NameSearchFlags::NS_CaseInsensitive);
+
+ if (!compilands)
+ return 0;
+
+ // For each one, either find its previously parsed data or parse it afresh
+ // and add it to the symbol context list.
+ while (auto compiland = compilands->getNext()) {
+ // If we're not checking inlines, then don't add line information for
+ // this file unless the FileSpec matches. For inline functions, we don't
+ // have to match the FileSpec since they could be defined in headers
+ // other than file specified in FileSpec.
+ if (!check_inlines) {
+ std::string source_file = compiland->getSourceFileFullPath();
+ if (source_file.empty())
+ continue;
+ FileSpec this_spec(source_file, FileSpec::Style::windows);
+ bool need_full_match = !file_spec.GetDirectory().IsEmpty();
+ if (FileSpec::Compare(file_spec, this_spec, need_full_match) != 0)
+ continue;
+ }
+
+ SymbolContext sc;
+ auto cu = ParseCompileUnitForUID(compiland->getSymIndexId());
+ if (!cu)
+ continue;
+ sc.comp_unit = cu.get();
+ sc.module_sp = cu->GetModule();
+
+ // If we were asked to resolve line entries, add all entries to the line
+ // table that match the requested line (or all lines if `line` == 0).
+ if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock |
+ eSymbolContextLineEntry)) {
+ bool has_line_table = ParseCompileUnitLineTable(*sc.comp_unit, line);
+
+ if ((resolve_scope & eSymbolContextLineEntry) && !has_line_table) {
+ // The query asks for line entries, but we can't get them for the
+ // compile unit. This is not normal for `line` = 0. So just assert
+ // it.
+ assert(line && "Couldn't get all line entries!\n");
+
+ // Current compiland does not have the requested line. Search next.
+ continue;
+ }
+
+ if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) {
+ if (!has_line_table)
+ continue;
+
+ auto *line_table = sc.comp_unit->GetLineTable();
+ lldbassert(line_table);
+
+ uint32_t num_line_entries = line_table->GetSize();
+ // Skip the terminal line entry.
+ --num_line_entries;
+
+ // If `line `!= 0, see if we can resolve function for each line entry
+ // in the line table.
+ for (uint32_t line_idx = 0; line && line_idx < num_line_entries;
+ ++line_idx) {
+ if (!line_table->GetLineEntryAtIndex(line_idx, sc.line_entry))
+ continue;
+
+ auto file_vm_addr =
+ sc.line_entry.range.GetBaseAddress().GetFileAddress();
+ if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0)
+ continue;
+
+ auto symbol_up = m_session_up->findSymbolByAddress(
+ file_vm_addr, PDB_SymType::Function);
+ if (symbol_up) {
+ auto func_uid = symbol_up->getSymIndexId();
+ sc.function = sc.comp_unit->FindFunctionByUID(func_uid).get();
+ if (sc.function == nullptr) {
+ auto pdb_func = llvm::dyn_cast<PDBSymbolFunc>(symbol_up.get());
+ assert(pdb_func);
+ sc.function = ParseCompileUnitFunctionForPDBFunc(*pdb_func,
+ *sc.comp_unit);
+ }
+ if (sc.function && (resolve_scope & eSymbolContextBlock)) {
+ Block &block = sc.function->GetBlock(true);
+ sc.block = block.FindBlockByID(sc.function->GetID());
+ }
+ }
+ sc_list.Append(sc);
+ }
+ } else if (has_line_table) {
+ // We can parse line table for the compile unit. But no query to
+ // resolve function or block. We append `sc` to the list anyway.
+ sc_list.Append(sc);
+ }
+ } else {
+ // No query for line entry, function or block. But we have a valid
+ // compile unit, append `sc` to the list.
+ sc_list.Append(sc);
+ }
+ }
+ }
+ return sc_list.GetSize() - old_size;
+}
+
+std::string SymbolFilePDB::GetMangledForPDBData(const PDBSymbolData &pdb_data) {
+ // Cache public names at first
+ if (m_public_names.empty())
+ if (auto result_up =
+ m_global_scope_up->findAllChildren(PDB_SymType::PublicSymbol))
+ while (auto symbol_up = result_up->getNext())
+ if (auto addr = symbol_up->getRawSymbol().getVirtualAddress())
+ m_public_names[addr] = symbol_up->getRawSymbol().getName();
+
+ // Look up the name in the cache
+ return m_public_names.lookup(pdb_data.getVirtualAddress());
+}
+
+VariableSP SymbolFilePDB::ParseVariableForPDBData(
+ const lldb_private::SymbolContext &sc,
+ const llvm::pdb::PDBSymbolData &pdb_data) {
+ VariableSP var_sp;
+ uint32_t var_uid = pdb_data.getSymIndexId();
+ auto result = m_variables.find(var_uid);
+ if (result != m_variables.end())
+ return result->second;
+
+ ValueType scope = eValueTypeInvalid;
+ bool is_static_member = false;
+ bool is_external = false;
+ bool is_artificial = false;
+
+ switch (pdb_data.getDataKind()) {
+ case PDB_DataKind::Global:
+ scope = eValueTypeVariableGlobal;
+ is_external = true;
+ break;
+ case PDB_DataKind::Local:
+ scope = eValueTypeVariableLocal;
+ break;
+ case PDB_DataKind::FileStatic:
+ scope = eValueTypeVariableStatic;
+ break;
+ case PDB_DataKind::StaticMember:
+ is_static_member = true;
+ scope = eValueTypeVariableStatic;
+ break;
+ case PDB_DataKind::Member:
+ scope = eValueTypeVariableStatic;
+ break;
+ case PDB_DataKind::Param:
+ scope = eValueTypeVariableArgument;
+ break;
+ case PDB_DataKind::Constant:
+ scope = eValueTypeConstResult;
+ break;
+ default:
+ break;
+ }
+
+ switch (pdb_data.getLocationType()) {
+ case PDB_LocType::TLS:
+ scope = eValueTypeVariableThreadLocal;
+ break;
+ case PDB_LocType::RegRel: {
+ // It is a `this` pointer.
+ if (pdb_data.getDataKind() == PDB_DataKind::ObjectPtr) {
+ scope = eValueTypeVariableArgument;
+ is_artificial = true;
+ }
+ } break;
+ default:
+ break;
+ }
+
+ Declaration decl;
+ if (!is_artificial && !pdb_data.isCompilerGenerated()) {
+ if (auto lines = pdb_data.getLineNumbers()) {
+ if (auto first_line = lines->getNext()) {
+ uint32_t src_file_id = first_line->getSourceFileId();
+ auto src_file = m_session_up->getSourceFileById(src_file_id);
+ if (src_file) {
+ FileSpec spec(src_file->getFileName());
+ decl.SetFile(spec);
+ decl.SetColumn(first_line->getColumnNumber());
+ decl.SetLine(first_line->getLineNumber());
+ }
+ }
+ }
+ }
+
+ Variable::RangeList ranges;
+ SymbolContextScope *context_scope = sc.comp_unit;
+ if (scope == eValueTypeVariableLocal || scope == eValueTypeVariableArgument) {
+ if (sc.function) {
+ Block &function_block = sc.function->GetBlock(true);
+ Block *block =
+ function_block.FindBlockByID(pdb_data.getLexicalParentId());
+ if (!block)
+ block = &function_block;
+
+ context_scope = block;
+
+ for (size_t i = 0, num_ranges = block->GetNumRanges(); i < num_ranges;
+ ++i) {
+ AddressRange range;
+ if (!block->GetRangeAtIndex(i, range))
+ continue;
+
+ ranges.Append(range.GetBaseAddress().GetFileAddress(),
+ range.GetByteSize());
+ }
+ }
+ }
+
+ SymbolFileTypeSP type_sp =
+ std::make_shared<SymbolFileType>(*this, pdb_data.getTypeId());
+
+ auto var_name = pdb_data.getName();
+ auto mangled = GetMangledForPDBData(pdb_data);
+ auto mangled_cstr = mangled.empty() ? nullptr : mangled.c_str();
+
+ bool is_constant;
+ DWARFExpression location = ConvertPDBLocationToDWARFExpression(
+ GetObjectFile()->GetModule(), pdb_data, ranges, is_constant);
+
+ var_sp = std::make_shared<Variable>(
+ var_uid, var_name.c_str(), mangled_cstr, type_sp, scope, context_scope,
+ ranges, &decl, location, is_external, is_artificial, is_static_member);
+ var_sp->SetLocationIsConstantValueData(is_constant);
+
+ m_variables.insert(std::make_pair(var_uid, var_sp));
+ return var_sp;
+}
+
+size_t
+SymbolFilePDB::ParseVariables(const lldb_private::SymbolContext &sc,
+ const llvm::pdb::PDBSymbol &pdb_symbol,
+ lldb_private::VariableList *variable_list) {
+ size_t num_added = 0;
+
+ if (auto pdb_data = llvm::dyn_cast<PDBSymbolData>(&pdb_symbol)) {
+ VariableListSP local_variable_list_sp;
+
+ auto result = m_variables.find(pdb_data->getSymIndexId());
+ if (result != m_variables.end()) {
+ if (variable_list)
+ variable_list->AddVariableIfUnique(result->second);
+ } else {
+ // Prepare right VariableList for this variable.
+ if (auto lexical_parent = pdb_data->getLexicalParent()) {
+ switch (lexical_parent->getSymTag()) {
+ case PDB_SymType::Exe:
+ assert(sc.comp_unit);
+ LLVM_FALLTHROUGH;
+ case PDB_SymType::Compiland: {
+ if (sc.comp_unit) {
+ local_variable_list_sp = sc.comp_unit->GetVariableList(false);
+ if (!local_variable_list_sp) {
+ local_variable_list_sp = std::make_shared<VariableList>();
+ sc.comp_unit->SetVariableList(local_variable_list_sp);
+ }
+ }
+ } break;
+ case PDB_SymType::Block:
+ case PDB_SymType::Function: {
+ if (sc.function) {
+ Block *block = sc.function->GetBlock(true).FindBlockByID(
+ lexical_parent->getSymIndexId());
+ if (block) {
+ local_variable_list_sp = block->GetBlockVariableList(false);
+ if (!local_variable_list_sp) {
+ local_variable_list_sp = std::make_shared<VariableList>();
+ block->SetVariableList(local_variable_list_sp);
+ }
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+
+ if (local_variable_list_sp) {
+ if (auto var_sp = ParseVariableForPDBData(sc, *pdb_data)) {
+ local_variable_list_sp->AddVariableIfUnique(var_sp);
+ if (variable_list)
+ variable_list->AddVariableIfUnique(var_sp);
+ ++num_added;
+ PDBASTParser *ast = GetPDBAstParser();
+ if (ast)
+ ast->GetDeclForSymbol(*pdb_data);
+ }
+ }
+ }
+ }
+
+ if (auto results = pdb_symbol.findAllChildren()) {
+ while (auto result = results->getNext())
+ num_added += ParseVariables(sc, *result, variable_list);
+ }
+
+ return num_added;
+}
+
+uint32_t SymbolFilePDB::FindGlobalVariables(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, lldb_private::VariableList &variables) {
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+ if (name.IsEmpty())
+ return 0;
+
+ auto results = m_global_scope_up->findAllChildren<PDBSymbolData>();
+ if (!results)
+ return 0;
+
+ uint32_t matches = 0;
+ size_t old_size = variables.GetSize();
+ while (auto result = results->getNext()) {
+ auto pdb_data = llvm::dyn_cast<PDBSymbolData>(result.get());
+ if (max_matches > 0 && matches >= max_matches)
+ break;
+
+ SymbolContext sc;
+ sc.module_sp = m_obj_file->GetModule();
+ lldbassert(sc.module_sp.get());
+
+ if (!name.GetStringRef().equals(
+ MSVCUndecoratedNameParser::DropScope(pdb_data->getName())))
+ continue;
+
+ sc.comp_unit = ParseCompileUnitForUID(GetCompilandId(*pdb_data)).get();
+ // FIXME: We are not able to determine the compile unit.
+ if (sc.comp_unit == nullptr)
+ continue;
+
+ if (parent_decl_ctx && GetDeclContextContainingUID(
+ result->getSymIndexId()) != *parent_decl_ctx)
+ continue;
+
+ ParseVariables(sc, *pdb_data, &variables);
+ matches = variables.GetSize() - old_size;
+ }
+
+ return matches;
+}
+
+uint32_t
+SymbolFilePDB::FindGlobalVariables(const lldb_private::RegularExpression &regex,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) {
+ if (!regex.IsValid())
+ return 0;
+ auto results = m_global_scope_up->findAllChildren<PDBSymbolData>();
+ if (!results)
+ return 0;
+
+ uint32_t matches = 0;
+ size_t old_size = variables.GetSize();
+ while (auto pdb_data = results->getNext()) {
+ if (max_matches > 0 && matches >= max_matches)
+ break;
+
+ auto var_name = pdb_data->getName();
+ if (var_name.empty())
+ continue;
+ if (!regex.Execute(var_name))
+ continue;
+ SymbolContext sc;
+ sc.module_sp = m_obj_file->GetModule();
+ lldbassert(sc.module_sp.get());
+
+ sc.comp_unit = ParseCompileUnitForUID(GetCompilandId(*pdb_data)).get();
+ // FIXME: We are not able to determine the compile unit.
+ if (sc.comp_unit == nullptr)
+ continue;
+
+ ParseVariables(sc, *pdb_data, &variables);
+ matches = variables.GetSize() - old_size;
+ }
+
+ return matches;
+}
+
+bool SymbolFilePDB::ResolveFunction(const llvm::pdb::PDBSymbolFunc &pdb_func,
+ bool include_inlines,
+ lldb_private::SymbolContextList &sc_list) {
+ lldb_private::SymbolContext sc;
+ sc.comp_unit = ParseCompileUnitForUID(pdb_func.getCompilandId()).get();
+ if (!sc.comp_unit)
+ return false;
+ sc.module_sp = sc.comp_unit->GetModule();
+ sc.function = ParseCompileUnitFunctionForPDBFunc(pdb_func, *sc.comp_unit);
+ if (!sc.function)
+ return false;
+
+ sc_list.Append(sc);
+ return true;
+}
+
+bool SymbolFilePDB::ResolveFunction(uint32_t uid, bool include_inlines,
+ lldb_private::SymbolContextList &sc_list) {
+ auto pdb_func_up = m_session_up->getConcreteSymbolById<PDBSymbolFunc>(uid);
+ if (!pdb_func_up && !(include_inlines && pdb_func_up->hasInlineAttribute()))
+ return false;
+ return ResolveFunction(*pdb_func_up, include_inlines, sc_list);
+}
+
+void SymbolFilePDB::CacheFunctionNames() {
+ if (!m_func_full_names.IsEmpty())
+ return;
+
+ std::map<uint64_t, uint32_t> addr_ids;
+
+ if (auto results_up = m_global_scope_up->findAllChildren<PDBSymbolFunc>()) {
+ while (auto pdb_func_up = results_up->getNext()) {
+ if (pdb_func_up->isCompilerGenerated())
+ continue;
+
+ auto name = pdb_func_up->getName();
+ auto demangled_name = pdb_func_up->getUndecoratedName();
+ if (name.empty() && demangled_name.empty())
+ continue;
+
+ auto uid = pdb_func_up->getSymIndexId();
+ if (!demangled_name.empty() && pdb_func_up->getVirtualAddress())
+ addr_ids.insert(std::make_pair(pdb_func_up->getVirtualAddress(), uid));
+
+ if (auto parent = pdb_func_up->getClassParent()) {
+
+ // PDB have symbols for class/struct methods or static methods in Enum
+ // Class. We won't bother to check if the parent is UDT or Enum here.
+ m_func_method_names.Append(ConstString(name), uid);
+
+ // To search a method name, like NS::Class:MemberFunc, LLDB searches
+ // its base name, i.e. MemberFunc by default. Since PDBSymbolFunc does
+ // not have inforamtion of this, we extract base names and cache them
+ // by our own effort.
+ llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(name);
+ if (!basename.empty())
+ m_func_base_names.Append(ConstString(basename), uid);
+ else {
+ m_func_base_names.Append(ConstString(name), uid);
+ }
+
+ if (!demangled_name.empty())
+ m_func_full_names.Append(ConstString(demangled_name), uid);
+
+ } else {
+ // Handle not-method symbols.
+
+ // The function name might contain namespace, or its lexical scope.
+ llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(name);
+ if (!basename.empty())
+ m_func_base_names.Append(ConstString(basename), uid);
+ else
+ m_func_base_names.Append(ConstString(name), uid);
+
+ if (name == "main") {
+ m_func_full_names.Append(ConstString(name), uid);
+
+ if (!demangled_name.empty() && name != demangled_name) {
+ m_func_full_names.Append(ConstString(demangled_name), uid);
+ m_func_base_names.Append(ConstString(demangled_name), uid);
+ }
+ } else if (!demangled_name.empty()) {
+ m_func_full_names.Append(ConstString(demangled_name), uid);
+ } else {
+ m_func_full_names.Append(ConstString(name), uid);
+ }
+ }
+ }
+ }
+
+ if (auto results_up =
+ m_global_scope_up->findAllChildren<PDBSymbolPublicSymbol>()) {
+ while (auto pub_sym_up = results_up->getNext()) {
+ if (!pub_sym_up->isFunction())
+ continue;
+ auto name = pub_sym_up->getName();
+ if (name.empty())
+ continue;
+
+ if (CPlusPlusLanguage::IsCPPMangledName(name.c_str())) {
+ auto vm_addr = pub_sym_up->getVirtualAddress();
+
+ // PDB public symbol has mangled name for its associated function.
+ if (vm_addr && addr_ids.find(vm_addr) != addr_ids.end()) {
+ // Cache mangled name.
+ m_func_full_names.Append(ConstString(name), addr_ids[vm_addr]);
+ }
+ }
+ }
+ }
+ // Sort them before value searching is working properly
+ m_func_full_names.Sort();
+ m_func_full_names.SizeToFit();
+ m_func_method_names.Sort();
+ m_func_method_names.SizeToFit();
+ m_func_base_names.Sort();
+ m_func_base_names.SizeToFit();
+}
+
+uint32_t SymbolFilePDB::FindFunctions(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask, bool include_inlines, bool append,
+ lldb_private::SymbolContextList &sc_list) {
+ if (!append)
+ sc_list.Clear();
+ lldbassert((name_type_mask & eFunctionNameTypeAuto) == 0);
+
+ if (name_type_mask == eFunctionNameTypeNone)
+ return 0;
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+ if (name.IsEmpty())
+ return 0;
+
+ auto old_size = sc_list.GetSize();
+ if (name_type_mask & eFunctionNameTypeFull ||
+ name_type_mask & eFunctionNameTypeBase ||
+ name_type_mask & eFunctionNameTypeMethod) {
+ CacheFunctionNames();
+
+ std::set<uint32_t> resolved_ids;
+ auto ResolveFn = [this, &name, parent_decl_ctx, include_inlines, &sc_list,
+ &resolved_ids](UniqueCStringMap<uint32_t> &Names) {
+ std::vector<uint32_t> ids;
+ if (!Names.GetValues(name, ids))
+ return;
+
+ for (uint32_t id : ids) {
+ if (resolved_ids.find(id) != resolved_ids.end())
+ continue;
+
+ if (parent_decl_ctx &&
+ GetDeclContextContainingUID(id) != *parent_decl_ctx)
+ continue;
+
+ if (ResolveFunction(id, include_inlines, sc_list))
+ resolved_ids.insert(id);
+ }
+ };
+ if (name_type_mask & eFunctionNameTypeFull) {
+ ResolveFn(m_func_full_names);
+ ResolveFn(m_func_base_names);
+ ResolveFn(m_func_method_names);
+ }
+ if (name_type_mask & eFunctionNameTypeBase) {
+ ResolveFn(m_func_base_names);
+ }
+ if (name_type_mask & eFunctionNameTypeMethod) {
+ ResolveFn(m_func_method_names);
+ }
+ }
+ return sc_list.GetSize() - old_size;
+}
+
+uint32_t
+SymbolFilePDB::FindFunctions(const lldb_private::RegularExpression &regex,
+ bool include_inlines, bool append,
+ lldb_private::SymbolContextList &sc_list) {
+ if (!append)
+ sc_list.Clear();
+ if (!regex.IsValid())
+ return 0;
+
+ auto old_size = sc_list.GetSize();
+ CacheFunctionNames();
+
+ std::set<uint32_t> resolved_ids;
+ auto ResolveFn = [&regex, include_inlines, &sc_list, &resolved_ids,
+ this](UniqueCStringMap<uint32_t> &Names) {
+ std::vector<uint32_t> ids;
+ if (Names.GetValues(regex, ids)) {
+ for (auto id : ids) {
+ if (resolved_ids.find(id) == resolved_ids.end())
+ if (ResolveFunction(id, include_inlines, sc_list))
+ resolved_ids.insert(id);
+ }
+ }
+ };
+ ResolveFn(m_func_full_names);
+ ResolveFn(m_func_base_names);
+
+ return sc_list.GetSize() - old_size;
+}
+
+void SymbolFilePDB::GetMangledNamesForFunction(
+ const std::string &scope_qualified_name,
+ std::vector<lldb_private::ConstString> &mangled_names) {}
+
+void SymbolFilePDB::AddSymbols(lldb_private::Symtab &symtab) {
+ std::set<lldb::addr_t> sym_addresses;
+ for (size_t i = 0; i < symtab.GetNumSymbols(); i++)
+ sym_addresses.insert(symtab.SymbolAtIndex(i)->GetFileAddress());
+
+ auto results = m_global_scope_up->findAllChildren<PDBSymbolPublicSymbol>();
+ if (!results)
+ return;
+
+ auto section_list = m_obj_file->GetSectionList();
+ if (!section_list)
+ return;
+
+ while (auto pub_symbol = results->getNext()) {
+ auto section_id = pub_symbol->getAddressSection();
+
+ auto section = section_list->FindSectionByID(section_id);
+ if (!section)
+ continue;
+
+ auto offset = pub_symbol->getAddressOffset();
+
+ auto file_addr = section->GetFileAddress() + offset;
+ if (sym_addresses.find(file_addr) != sym_addresses.end())
+ continue;
+ sym_addresses.insert(file_addr);
+
+ auto size = pub_symbol->getLength();
+ symtab.AddSymbol(
+ Symbol(pub_symbol->getSymIndexId(), // symID
+ pub_symbol->getName().c_str(), // name
+ true, // name_is_mangled
+ pub_symbol->isCode() ? eSymbolTypeCode : eSymbolTypeData, // type
+ true, // external
+ false, // is_debug
+ false, // is_trampoline
+ false, // is_artificial
+ section, // section_sp
+ offset, // value
+ size, // size
+ size != 0, // size_is_valid
+ false, // contains_linker_annotations
+ 0 // flags
+ ));
+ }
+
+ symtab.CalculateSymbolSizes();
+ symtab.Finalize();
+}
+
+uint32_t SymbolFilePDB::FindTypes(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx, bool append,
+ uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ lldb_private::TypeMap &types) {
+ if (!append)
+ types.Clear();
+ if (!name)
+ return 0;
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+
+ searched_symbol_files.clear();
+ searched_symbol_files.insert(this);
+
+ // There is an assumption 'name' is not a regex
+ FindTypesByName(name.GetStringRef(), parent_decl_ctx, max_matches, types);
+
+ return types.GetSize();
+}
+
+void SymbolFilePDB::DumpClangAST(Stream &s) {
+ auto type_system = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus);
+ auto clang = llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang)
+ return;
+ clang->Dump(s);
+}
+
+void SymbolFilePDB::FindTypesByRegex(
+ const lldb_private::RegularExpression &regex, uint32_t max_matches,
+ lldb_private::TypeMap &types) {
+ // When searching by regex, we need to go out of our way to limit the search
+ // space as much as possible since this searches EVERYTHING in the PDB,
+ // manually doing regex comparisons. PDB library isn't optimized for regex
+ // searches or searches across multiple symbol types at the same time, so the
+ // best we can do is to search enums, then typedefs, then classes one by one,
+ // and do a regex comparison against each of them.
+ PDB_SymType tags_to_search[] = {PDB_SymType::Enum, PDB_SymType::Typedef,
+ PDB_SymType::UDT};
+ std::unique_ptr<IPDBEnumSymbols> results;
+
+ uint32_t matches = 0;
+
+ for (auto tag : tags_to_search) {
+ results = m_global_scope_up->findAllChildren(tag);
+ if (!results)
+ continue;
+
+ while (auto result = results->getNext()) {
+ if (max_matches > 0 && matches >= max_matches)
+ break;
+
+ std::string type_name;
+ if (auto enum_type = llvm::dyn_cast<PDBSymbolTypeEnum>(result.get()))
+ type_name = enum_type->getName();
+ else if (auto typedef_type =
+ llvm::dyn_cast<PDBSymbolTypeTypedef>(result.get()))
+ type_name = typedef_type->getName();
+ else if (auto class_type = llvm::dyn_cast<PDBSymbolTypeUDT>(result.get()))
+ type_name = class_type->getName();
+ else {
+ // We're looking only for types that have names. Skip symbols, as well
+ // as unnamed types such as arrays, pointers, etc.
+ continue;
+ }
+
+ if (!regex.Execute(type_name))
+ continue;
+
+ // This should cause the type to get cached and stored in the `m_types`
+ // lookup.
+ if (!ResolveTypeUID(result->getSymIndexId()))
+ continue;
+
+ auto iter = m_types.find(result->getSymIndexId());
+ if (iter == m_types.end())
+ continue;
+ types.Insert(iter->second);
+ ++matches;
+ }
+ }
+}
+
+void SymbolFilePDB::FindTypesByName(
+ llvm::StringRef name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, lldb_private::TypeMap &types) {
+ std::unique_ptr<IPDBEnumSymbols> results;
+ if (name.empty())
+ return;
+ results = m_global_scope_up->findAllChildren(PDB_SymType::None);
+ if (!results)
+ return;
+
+ uint32_t matches = 0;
+
+ while (auto result = results->getNext()) {
+ if (max_matches > 0 && matches >= max_matches)
+ break;
+
+ if (MSVCUndecoratedNameParser::DropScope(
+ result->getRawSymbol().getName()) != name)
+ continue;
+
+ switch (result->getSymTag()) {
+ case PDB_SymType::Enum:
+ case PDB_SymType::UDT:
+ case PDB_SymType::Typedef:
+ break;
+ default:
+ // We're looking only for types that have names. Skip symbols, as well
+ // as unnamed types such as arrays, pointers, etc.
+ continue;
+ }
+
+ // This should cause the type to get cached and stored in the `m_types`
+ // lookup.
+ if (!ResolveTypeUID(result->getSymIndexId()))
+ continue;
+
+ if (parent_decl_ctx && GetDeclContextContainingUID(
+ result->getSymIndexId()) != *parent_decl_ctx)
+ continue;
+
+ auto iter = m_types.find(result->getSymIndexId());
+ if (iter == m_types.end())
+ continue;
+ types.Insert(iter->second);
+ ++matches;
+ }
+}
+
+size_t SymbolFilePDB::FindTypes(
+ const std::vector<lldb_private::CompilerContext> &contexts, bool append,
+ lldb_private::TypeMap &types) {
+ return 0;
+}
+
+lldb_private::TypeList *SymbolFilePDB::GetTypeList() {
+ return m_obj_file->GetModule()->GetTypeList();
+}
+
+void SymbolFilePDB::GetTypesForPDBSymbol(const llvm::pdb::PDBSymbol &pdb_symbol,
+ uint32_t type_mask,
+ TypeCollection &type_collection) {
+ bool can_parse = false;
+ switch (pdb_symbol.getSymTag()) {
+ case PDB_SymType::ArrayType:
+ can_parse = ((type_mask & eTypeClassArray) != 0);
+ break;
+ case PDB_SymType::BuiltinType:
+ can_parse = ((type_mask & eTypeClassBuiltin) != 0);
+ break;
+ case PDB_SymType::Enum:
+ can_parse = ((type_mask & eTypeClassEnumeration) != 0);
+ break;
+ case PDB_SymType::Function:
+ case PDB_SymType::FunctionSig:
+ can_parse = ((type_mask & eTypeClassFunction) != 0);
+ break;
+ case PDB_SymType::PointerType:
+ can_parse = ((type_mask & (eTypeClassPointer | eTypeClassBlockPointer |
+ eTypeClassMemberPointer)) != 0);
+ break;
+ case PDB_SymType::Typedef:
+ can_parse = ((type_mask & eTypeClassTypedef) != 0);
+ break;
+ case PDB_SymType::UDT: {
+ auto *udt = llvm::dyn_cast<PDBSymbolTypeUDT>(&pdb_symbol);
+ assert(udt);
+ can_parse = (udt->getUdtKind() != PDB_UdtType::Interface &&
+ ((type_mask & (eTypeClassClass | eTypeClassStruct |
+ eTypeClassUnion)) != 0));
+ } break;
+ default:
+ break;
+ }
+
+ if (can_parse) {
+ if (auto *type = ResolveTypeUID(pdb_symbol.getSymIndexId())) {
+ auto result =
+ std::find(type_collection.begin(), type_collection.end(), type);
+ if (result == type_collection.end())
+ type_collection.push_back(type);
+ }
+ }
+
+ auto results_up = pdb_symbol.findAllChildren();
+ while (auto symbol_up = results_up->getNext())
+ GetTypesForPDBSymbol(*symbol_up, type_mask, type_collection);
+}
+
+size_t SymbolFilePDB::GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ TypeClass type_mask,
+ lldb_private::TypeList &type_list) {
+ TypeCollection type_collection;
+ uint32_t old_size = type_list.GetSize();
+ CompileUnit *cu =
+ sc_scope ? sc_scope->CalculateSymbolContextCompileUnit() : nullptr;
+ if (cu) {
+ auto compiland_up = GetPDBCompilandByUID(cu->GetID());
+ if (!compiland_up)
+ return 0;
+ GetTypesForPDBSymbol(*compiland_up, type_mask, type_collection);
+ } else {
+ for (uint32_t cu_idx = 0; cu_idx < GetNumCompileUnits(); ++cu_idx) {
+ auto cu_sp = ParseCompileUnitAtIndex(cu_idx);
+ if (cu_sp) {
+ if (auto compiland_up = GetPDBCompilandByUID(cu_sp->GetID()))
+ GetTypesForPDBSymbol(*compiland_up, type_mask, type_collection);
+ }
+ }
+ }
+
+ for (auto type : type_collection) {
+ type->GetForwardCompilerType();
+ type_list.Insert(type->shared_from_this());
+ }
+ return type_list.GetSize() - old_size;
+}
+
+lldb_private::TypeSystem *
+SymbolFilePDB::GetTypeSystemForLanguage(lldb::LanguageType language) {
+ auto type_system =
+ m_obj_file->GetModule()->GetTypeSystemForLanguage(language);
+ if (type_system)
+ type_system->SetSymbolFile(this);
+ return type_system;
+}
+
+PDBASTParser *SymbolFilePDB::GetPDBAstParser() {
+ auto type_system = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus);
+ auto clang_type_system = llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang_type_system)
+ return nullptr;
+
+ return clang_type_system->GetPDBParser();
+}
+
+
+lldb_private::CompilerDeclContext SymbolFilePDB::FindNamespace(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx) {
+ auto type_system = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus);
+ auto clang_type_system = llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang_type_system)
+ return CompilerDeclContext();
+
+ PDBASTParser *pdb = clang_type_system->GetPDBParser();
+ if (!pdb)
+ return CompilerDeclContext();
+
+ clang::DeclContext *decl_context = nullptr;
+ if (parent_decl_ctx)
+ decl_context = static_cast<clang::DeclContext *>(
+ parent_decl_ctx->GetOpaqueDeclContext());
+
+ auto namespace_decl =
+ pdb->FindNamespaceDecl(decl_context, name.GetStringRef());
+ if (!namespace_decl)
+ return CompilerDeclContext();
+
+ return CompilerDeclContext(type_system,
+ static_cast<clang::DeclContext *>(namespace_decl));
+}
+
+lldb_private::ConstString SymbolFilePDB::GetPluginName() {
+ static ConstString g_name("pdb");
+ return g_name;
+}
+
+uint32_t SymbolFilePDB::GetPluginVersion() { return 1; }
+
+IPDBSession &SymbolFilePDB::GetPDBSession() { return *m_session_up; }
+
+const IPDBSession &SymbolFilePDB::GetPDBSession() const {
+ return *m_session_up;
+}
+
+lldb::CompUnitSP SymbolFilePDB::ParseCompileUnitForUID(uint32_t id,
+ uint32_t index) {
+ auto found_cu = m_comp_units.find(id);
+ if (found_cu != m_comp_units.end())
+ return found_cu->second;
+
+ auto compiland_up = GetPDBCompilandByUID(id);
+ if (!compiland_up)
+ return CompUnitSP();
+
+ lldb::LanguageType lang;
+ auto details = compiland_up->findOneChild<PDBSymbolCompilandDetails>();
+ if (!details)
+ lang = lldb::eLanguageTypeC_plus_plus;
+ else
+ lang = TranslateLanguage(details->getLanguage());
+
+ if (lang == lldb::LanguageType::eLanguageTypeUnknown)
+ return CompUnitSP();
+
+ std::string path = compiland_up->getSourceFileFullPath();
+ if (path.empty())
+ return CompUnitSP();
+
+ // Don't support optimized code for now, DebugInfoPDB does not return this
+ // information.
+ LazyBool optimized = eLazyBoolNo;
+ auto cu_sp = std::make_shared<CompileUnit>(m_obj_file->GetModule(), nullptr,
+ path.c_str(), id, lang, optimized);
+
+ if (!cu_sp)
+ return CompUnitSP();
+
+ m_comp_units.insert(std::make_pair(id, cu_sp));
+ if (index == UINT32_MAX)
+ GetCompileUnitIndex(*compiland_up, index);
+ lldbassert(index != UINT32_MAX);
+ m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(index,
+ cu_sp);
+ return cu_sp;
+}
+
+bool SymbolFilePDB::ParseCompileUnitLineTable(CompileUnit &comp_unit,
+ uint32_t match_line) {
+ auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID());
+ if (!compiland_up)
+ return false;
+
+ // LineEntry needs the *index* of the file into the list of support files
+ // returned by ParseCompileUnitSupportFiles. But the underlying SDK gives us
+ // a globally unique idenfitifier in the namespace of the PDB. So, we have
+ // to do a mapping so that we can hand out indices.
+ llvm::DenseMap<uint32_t, uint32_t> index_map;
+ BuildSupportFileIdToSupportFileIndexMap(*compiland_up, index_map);
+ auto line_table = llvm::make_unique<LineTable>(&comp_unit);
+
+ // Find contributions to `compiland` from all source and header files.
+ std::string path = comp_unit.GetPath();
+ auto files = m_session_up->getSourceFilesForCompiland(*compiland_up);
+ if (!files)
+ return false;
+
+ // For each source and header file, create a LineSequence for contributions
+ // to the compiland from that file, and add the sequence.
+ while (auto file = files->getNext()) {
+ std::unique_ptr<LineSequence> sequence(
+ line_table->CreateLineSequenceContainer());
+ auto lines = m_session_up->findLineNumbers(*compiland_up, *file);
+ if (!lines)
+ continue;
+ int entry_count = lines->getChildCount();
+
+ uint64_t prev_addr;
+ uint32_t prev_length;
+ uint32_t prev_line;
+ uint32_t prev_source_idx;
+
+ for (int i = 0; i < entry_count; ++i) {
+ auto line = lines->getChildAtIndex(i);
+
+ uint64_t lno = line->getLineNumber();
+ uint64_t addr = line->getVirtualAddress();
+ uint32_t length = line->getLength();
+ uint32_t source_id = line->getSourceFileId();
+ uint32_t col = line->getColumnNumber();
+ uint32_t source_idx = index_map[source_id];
+
+ // There was a gap between the current entry and the previous entry if
+ // the addresses don't perfectly line up.
+ bool is_gap = (i > 0) && (prev_addr + prev_length < addr);
+
+ // Before inserting the current entry, insert a terminal entry at the end
+ // of the previous entry's address range if the current entry resulted in
+ // a gap from the previous entry.
+ if (is_gap && ShouldAddLine(match_line, prev_line, prev_length)) {
+ line_table->AppendLineEntryToSequence(
+ sequence.get(), prev_addr + prev_length, prev_line, 0,
+ prev_source_idx, false, false, false, false, true);
+
+ line_table->InsertSequence(sequence.release());
+ sequence.reset(line_table->CreateLineSequenceContainer());
+ }
+
+ if (ShouldAddLine(match_line, lno, length)) {
+ bool is_statement = line->isStatement();
+ bool is_prologue = false;
+ bool is_epilogue = false;
+ auto func =
+ m_session_up->findSymbolByAddress(addr, PDB_SymType::Function);
+ if (func) {
+ auto prologue = func->findOneChild<PDBSymbolFuncDebugStart>();
+ if (prologue)
+ is_prologue = (addr == prologue->getVirtualAddress());
+
+ auto epilogue = func->findOneChild<PDBSymbolFuncDebugEnd>();
+ if (epilogue)
+ is_epilogue = (addr == epilogue->getVirtualAddress());
+ }
+
+ line_table->AppendLineEntryToSequence(sequence.get(), addr, lno, col,
+ source_idx, is_statement, false,
+ is_prologue, is_epilogue, false);
+ }
+
+ prev_addr = addr;
+ prev_length = length;
+ prev_line = lno;
+ prev_source_idx = source_idx;
+ }
+
+ if (entry_count > 0 && ShouldAddLine(match_line, prev_line, prev_length)) {
+ // The end is always a terminal entry, so insert it regardless.
+ line_table->AppendLineEntryToSequence(
+ sequence.get(), prev_addr + prev_length, prev_line, 0,
+ prev_source_idx, false, false, false, false, true);
+ }
+
+ line_table->InsertSequence(sequence.release());
+ }
+
+ if (line_table->GetSize()) {
+ comp_unit.SetLineTable(line_table.release());
+ return true;
+ }
+ return false;
+}
+
+void SymbolFilePDB::BuildSupportFileIdToSupportFileIndexMap(
+ const PDBSymbolCompiland &compiland,
+ llvm::DenseMap<uint32_t, uint32_t> &index_map) const {
+ // This is a hack, but we need to convert the source id into an index into
+ // the support files array. We don't want to do path comparisons to avoid
+ // basename / full path issues that may or may not even be a problem, so we
+ // use the globally unique source file identifiers. Ideally we could use the
+ // global identifiers everywhere, but LineEntry currently assumes indices.
+ auto source_files = m_session_up->getSourceFilesForCompiland(compiland);
+ if (!source_files)
+ return;
+
+ // LLDB uses the DWARF-like file numeration (one based)
+ int index = 1;
+
+ while (auto file = source_files->getNext()) {
+ uint32_t source_id = file->getUniqueId();
+ index_map[source_id] = index++;
+ }
+}
+
+lldb::CompUnitSP SymbolFilePDB::GetCompileUnitContainsAddress(
+ const lldb_private::Address &so_addr) {
+ lldb::addr_t file_vm_addr = so_addr.GetFileAddress();
+ if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0)
+ return nullptr;
+
+ // If it is a PDB function's vm addr, this is the first sure bet.
+ if (auto lines =
+ m_session_up->findLineNumbersByAddress(file_vm_addr, /*Length=*/1)) {
+ if (auto first_line = lines->getNext())
+ return ParseCompileUnitForUID(first_line->getCompilandId());
+ }
+
+ // Otherwise we resort to section contributions.
+ if (auto sec_contribs = m_session_up->getSectionContribs()) {
+ while (auto section = sec_contribs->getNext()) {
+ auto va = section->getVirtualAddress();
+ if (file_vm_addr >= va && file_vm_addr < va + section->getLength())
+ return ParseCompileUnitForUID(section->getCompilandId());
+ }
+ }
+ return nullptr;
+}
+
+Mangled
+SymbolFilePDB::GetMangledForPDBFunc(const llvm::pdb::PDBSymbolFunc &pdb_func) {
+ Mangled mangled;
+ auto func_name = pdb_func.getName();
+ auto func_undecorated_name = pdb_func.getUndecoratedName();
+ std::string func_decorated_name;
+
+ // Seek from public symbols for non-static function's decorated name if any.
+ // For static functions, they don't have undecorated names and aren't exposed
+ // in Public Symbols either.
+ if (!func_undecorated_name.empty()) {
+ auto result_up = m_global_scope_up->findChildren(
+ PDB_SymType::PublicSymbol, func_undecorated_name,
+ PDB_NameSearchFlags::NS_UndecoratedName);
+ if (result_up) {
+ while (auto symbol_up = result_up->getNext()) {
+ // For a public symbol, it is unique.
+ lldbassert(result_up->getChildCount() == 1);
+ if (auto *pdb_public_sym =
+ llvm::dyn_cast_or_null<PDBSymbolPublicSymbol>(
+ symbol_up.get())) {
+ if (pdb_public_sym->isFunction()) {
+ func_decorated_name = pdb_public_sym->getName();
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!func_decorated_name.empty()) {
+ mangled.SetMangledName(ConstString(func_decorated_name));
+
+ // For MSVC, format of C funciton's decorated name depends on calling
+ // conventon. Unfortunately none of the format is recognized by current
+ // LLDB. For example, `_purecall` is a __cdecl C function. From PDB,
+ // `__purecall` is retrieved as both its decorated and undecorated name
+ // (using PDBSymbolFunc::getUndecoratedName method). However `__purecall`
+ // string is not treated as mangled in LLDB (neither `?` nor `_Z` prefix).
+ // Mangled::GetDemangledName method will fail internally and caches an
+ // empty string as its undecorated name. So we will face a contradition
+ // here for the same symbol:
+ // non-empty undecorated name from PDB
+ // empty undecorated name from LLDB
+ if (!func_undecorated_name.empty() &&
+ mangled.GetDemangledName(mangled.GuessLanguage()).IsEmpty())
+ mangled.SetDemangledName(ConstString(func_undecorated_name));
+
+ // LLDB uses several flags to control how a C++ decorated name is
+ // undecorated for MSVC. See `safeUndecorateName` in Class Mangled. So the
+ // yielded name could be different from what we retrieve from
+ // PDB source unless we also apply same flags in getting undecorated
+ // name through PDBSymbolFunc::getUndecoratedNameEx method.
+ if (!func_undecorated_name.empty() &&
+ mangled.GetDemangledName(mangled.GuessLanguage()) !=
+ ConstString(func_undecorated_name))
+ mangled.SetDemangledName(ConstString(func_undecorated_name));
+ } else if (!func_undecorated_name.empty()) {
+ mangled.SetDemangledName(ConstString(func_undecorated_name));
+ } else if (!func_name.empty())
+ mangled.SetValue(ConstString(func_name), false);
+
+ return mangled;
+}
+
+bool SymbolFilePDB::DeclContextMatchesThisSymbolFile(
+ const lldb_private::CompilerDeclContext *decl_ctx) {
+ if (decl_ctx == nullptr || !decl_ctx->IsValid())
+ return true;
+
+ TypeSystem *decl_ctx_type_system = decl_ctx->GetTypeSystem();
+ if (!decl_ctx_type_system)
+ return false;
+ TypeSystem *type_system = GetTypeSystemForLanguage(
+ decl_ctx_type_system->GetMinimumLanguage(nullptr));
+ if (decl_ctx_type_system == type_system)
+ return true; // The type systems match, return true
+
+ return false;
+}
+
+uint32_t SymbolFilePDB::GetCompilandId(const llvm::pdb::PDBSymbolData &data) {
+ static const auto pred_upper = [](uint32_t lhs, SecContribInfo rhs) {
+ return lhs < rhs.Offset;
+ };
+
+ // Cache section contributions
+ if (m_sec_contribs.empty()) {
+ if (auto SecContribs = m_session_up->getSectionContribs()) {
+ while (auto SectionContrib = SecContribs->getNext()) {
+ auto comp_id = SectionContrib->getCompilandId();
+ if (!comp_id)
+ continue;
+
+ auto sec = SectionContrib->getAddressSection();
+ auto &sec_cs = m_sec_contribs[sec];
+
+ auto offset = SectionContrib->getAddressOffset();
+ auto it =
+ std::upper_bound(sec_cs.begin(), sec_cs.end(), offset, pred_upper);
+
+ auto size = SectionContrib->getLength();
+ sec_cs.insert(it, {offset, size, comp_id});
+ }
+ }
+ }
+
+ // Check by line number
+ if (auto Lines = data.getLineNumbers()) {
+ if (auto FirstLine = Lines->getNext())
+ return FirstLine->getCompilandId();
+ }
+
+ // Retrieve section + offset
+ uint32_t DataSection = data.getAddressSection();
+ uint32_t DataOffset = data.getAddressOffset();
+ if (DataSection == 0) {
+ if (auto RVA = data.getRelativeVirtualAddress())
+ m_session_up->addressForRVA(RVA, DataSection, DataOffset);
+ }
+
+ if (DataSection) {
+ // Search by section contributions
+ auto &sec_cs = m_sec_contribs[DataSection];
+ auto it =
+ std::upper_bound(sec_cs.begin(), sec_cs.end(), DataOffset, pred_upper);
+ if (it != sec_cs.begin()) {
+ --it;
+ if (DataOffset < it->Offset + it->Size)
+ return it->CompilandId;
+ }
+ } else {
+ // Search in lexical tree
+ auto LexParentId = data.getLexicalParentId();
+ while (auto LexParent = m_session_up->getSymbolById(LexParentId)) {
+ if (LexParent->getSymTag() == PDB_SymType::Exe)
+ break;
+ if (LexParent->getSymTag() == PDB_SymType::Compiland)
+ return LexParentId;
+ LexParentId = LexParent->getRawSymbol().getLexicalParentId();
+ }
+ }
+
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
new file mode 100644
index 000000000000..ba3099aaec4d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
@@ -0,0 +1,255 @@
+//===-- SymbolFilePDB.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
+#define lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
+
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Utility/UserID.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/PDB.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
+
+class PDBASTParser;
+
+class SymbolFilePDB : public lldb_private::SymbolFile {
+public:
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static void DebuggerInitialize(lldb_private::Debugger &debugger);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolFile *
+ CreateInstance(lldb_private::ObjectFile *obj_file);
+
+ // Constructors and Destructors
+ SymbolFilePDB(lldb_private::ObjectFile *ofile);
+
+ ~SymbolFilePDB() override;
+
+ uint32_t CalculateAbilities() override;
+
+ void InitializeObject() override;
+
+ // Compile Unit function calls
+
+ uint32_t GetNumCompileUnits() override;
+
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType
+ ParseLanguage(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
+ lldb_private::FileSpecList &support_files) override;
+
+ size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override;
+
+ size_t ParseBlocksRecursive(lldb_private::Function &func) override;
+
+ size_t
+ ParseVariablesForContext(const lldb_private::SymbolContext &sc) override;
+
+ lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override;
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override;
+
+ bool CompleteType(lldb_private::CompilerType &compiler_type) override;
+
+ lldb_private::CompilerDecl GetDeclForUID(lldb::user_id_t uid) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextForUID(lldb::user_id_t uid) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextContainingUID(lldb::user_id_t uid) override;
+
+ void
+ ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override;
+
+ uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContext &sc) override;
+
+ uint32_t
+ ResolveSymbolContext(const lldb_private::FileSpec &file_spec, uint32_t line,
+ bool check_inlines,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContextList &sc_list) override;
+
+ uint32_t
+ FindGlobalVariables(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+
+ uint32_t FindGlobalVariables(const lldb_private::RegularExpression &regex,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+
+ uint32_t
+ FindFunctions(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ lldb::FunctionNameType name_type_mask, bool include_inlines,
+ bool append, lldb_private::SymbolContextList &sc_list) override;
+
+ uint32_t FindFunctions(const lldb_private::RegularExpression &regex,
+ bool include_inlines, bool append,
+ lldb_private::SymbolContextList &sc_list) override;
+
+ void GetMangledNamesForFunction(
+ const std::string &scope_qualified_name,
+ std::vector<lldb_private::ConstString> &mangled_names) override;
+
+ void AddSymbols(lldb_private::Symtab &symtab) override;
+
+ uint32_t
+ FindTypes(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ lldb_private::TypeMap &types) override;
+
+ size_t FindTypes(const std::vector<lldb_private::CompilerContext> &context,
+ bool append, lldb_private::TypeMap &types) override;
+
+ void FindTypesByRegex(const lldb_private::RegularExpression &regex,
+ uint32_t max_matches, lldb_private::TypeMap &types);
+
+ lldb_private::TypeList *GetTypeList() override;
+
+ size_t GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ lldb::TypeClass type_mask,
+ lldb_private::TypeList &type_list) override;
+
+ lldb_private::TypeSystem *
+ GetTypeSystemForLanguage(lldb::LanguageType language) override;
+
+ lldb_private::CompilerDeclContext FindNamespace(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx) override;
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ llvm::pdb::IPDBSession &GetPDBSession();
+
+ const llvm::pdb::IPDBSession &GetPDBSession() const;
+
+ void DumpClangAST(lldb_private::Stream &s) override;
+
+private:
+ struct SecContribInfo {
+ uint32_t Offset;
+ uint32_t Size;
+ uint32_t CompilandId;
+ };
+ using SecContribsMap = std::map<uint32_t, std::vector<SecContribInfo>>;
+
+ lldb::CompUnitSP ParseCompileUnitForUID(uint32_t id,
+ uint32_t index = UINT32_MAX);
+
+ bool ParseCompileUnitLineTable(lldb_private::CompileUnit &comp_unit,
+ uint32_t match_line);
+
+ void BuildSupportFileIdToSupportFileIndexMap(
+ const llvm::pdb::PDBSymbolCompiland &pdb_compiland,
+ llvm::DenseMap<uint32_t, uint32_t> &index_map) const;
+
+ void FindTypesByName(llvm::StringRef name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, lldb_private::TypeMap &types);
+
+ std::string GetMangledForPDBData(const llvm::pdb::PDBSymbolData &pdb_data);
+
+ lldb::VariableSP
+ ParseVariableForPDBData(const lldb_private::SymbolContext &sc,
+ const llvm::pdb::PDBSymbolData &pdb_data);
+
+ size_t ParseVariables(const lldb_private::SymbolContext &sc,
+ const llvm::pdb::PDBSymbol &pdb_data,
+ lldb_private::VariableList *variable_list = nullptr);
+
+ lldb::CompUnitSP
+ GetCompileUnitContainsAddress(const lldb_private::Address &so_addr);
+
+ typedef std::vector<lldb_private::Type *> TypeCollection;
+
+ void GetTypesForPDBSymbol(const llvm::pdb::PDBSymbol &pdb_symbol,
+ uint32_t type_mask,
+ TypeCollection &type_collection);
+
+ lldb_private::Function *
+ ParseCompileUnitFunctionForPDBFunc(const llvm::pdb::PDBSymbolFunc &pdb_func,
+ lldb_private::CompileUnit &comp_unit);
+
+ void GetCompileUnitIndex(const llvm::pdb::PDBSymbolCompiland &pdb_compiland,
+ uint32_t &index);
+
+ PDBASTParser *GetPDBAstParser();
+
+ std::unique_ptr<llvm::pdb::PDBSymbolCompiland>
+ GetPDBCompilandByUID(uint32_t uid);
+
+ lldb_private::Mangled
+ GetMangledForPDBFunc(const llvm::pdb::PDBSymbolFunc &pdb_func);
+
+ bool ResolveFunction(const llvm::pdb::PDBSymbolFunc &pdb_func,
+ bool include_inlines,
+ lldb_private::SymbolContextList &sc_list);
+
+ bool ResolveFunction(uint32_t uid, bool include_inlines,
+ lldb_private::SymbolContextList &sc_list);
+
+ void CacheFunctionNames();
+
+ bool DeclContextMatchesThisSymbolFile(
+ const lldb_private::CompilerDeclContext *decl_ctx);
+
+ uint32_t GetCompilandId(const llvm::pdb::PDBSymbolData &data);
+
+ llvm::DenseMap<uint32_t, lldb::CompUnitSP> m_comp_units;
+ llvm::DenseMap<uint32_t, lldb::TypeSP> m_types;
+ llvm::DenseMap<uint32_t, lldb::VariableSP> m_variables;
+ llvm::DenseMap<uint64_t, std::string> m_public_names;
+
+ SecContribsMap m_sec_contribs;
+
+ std::vector<lldb::TypeSP> m_builtin_types;
+ std::unique_ptr<llvm::pdb::IPDBSession> m_session_up;
+ std::unique_ptr<llvm::pdb::PDBSymbolExe> m_global_scope_up;
+ uint32_t m_cached_compile_unit_count;
+
+ lldb_private::UniqueCStringMap<uint32_t> m_func_full_names;
+ lldb_private::UniqueCStringMap<uint32_t> m_func_base_names;
+ lldb_private::UniqueCStringMap<uint32_t> m_func_method_names;
+};
+
+#endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp
new file mode 100644
index 000000000000..a1b21e51b0fe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp
@@ -0,0 +1,267 @@
+//===-- SymbolFileSymtab.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolFileSymtab.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Timer.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+void SymbolFileSymtab::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void SymbolFileSymtab::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString SymbolFileSymtab::GetPluginNameStatic() {
+ static ConstString g_name("symtab");
+ return g_name;
+}
+
+const char *SymbolFileSymtab::GetPluginDescriptionStatic() {
+ return "Reads debug symbols from an object file's symbol table.";
+}
+
+SymbolFile *SymbolFileSymtab::CreateInstance(ObjectFile *obj_file) {
+ return new SymbolFileSymtab(obj_file);
+}
+
+size_t SymbolFileSymtab::GetTypes(SymbolContextScope *sc_scope,
+ TypeClass type_mask,
+ lldb_private::TypeList &type_list) {
+ return 0;
+}
+
+SymbolFileSymtab::SymbolFileSymtab(ObjectFile *obj_file)
+ : SymbolFile(obj_file), m_source_indexes(), m_func_indexes(),
+ m_code_indexes(), m_objc_class_name_to_index() {}
+
+SymbolFileSymtab::~SymbolFileSymtab() {}
+
+uint32_t SymbolFileSymtab::CalculateAbilities() {
+ uint32_t abilities = 0;
+ if (m_obj_file) {
+ const Symtab *symtab = m_obj_file->GetSymtab();
+ if (symtab) {
+ // The snippet of code below will get the indexes the module symbol table
+ // entries that are code, data, or function related (debug info), sort
+ // them by value (address) and dump the sorted symbols.
+ if (symtab->AppendSymbolIndexesWithType(eSymbolTypeSourceFile,
+ m_source_indexes)) {
+ abilities |= CompileUnits;
+ }
+
+ if (symtab->AppendSymbolIndexesWithType(
+ eSymbolTypeCode, Symtab::eDebugYes, Symtab::eVisibilityAny,
+ m_func_indexes)) {
+ symtab->SortSymbolIndexesByValue(m_func_indexes, true);
+ abilities |= Functions;
+ }
+
+ if (symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, Symtab::eDebugNo,
+ Symtab::eVisibilityAny,
+ m_code_indexes)) {
+ symtab->SortSymbolIndexesByValue(m_code_indexes, true);
+ abilities |= Functions;
+ }
+
+ if (symtab->AppendSymbolIndexesWithType(eSymbolTypeData,
+ m_data_indexes)) {
+ symtab->SortSymbolIndexesByValue(m_data_indexes, true);
+ abilities |= GlobalVariables;
+ }
+
+ lldb_private::Symtab::IndexCollection objc_class_indexes;
+ if (symtab->AppendSymbolIndexesWithType(eSymbolTypeObjCClass,
+ objc_class_indexes)) {
+ symtab->AppendSymbolNamesToMap(objc_class_indexes, true, true,
+ m_objc_class_name_to_index);
+ m_objc_class_name_to_index.Sort();
+ }
+ }
+ }
+ return abilities;
+}
+
+uint32_t SymbolFileSymtab::GetNumCompileUnits() {
+ // If we don't have any source file symbols we will just have one compile
+ // unit for the entire object file
+ if (m_source_indexes.empty())
+ return 0;
+
+ // If we have any source file symbols we will logically organize the object
+ // symbols using these.
+ return m_source_indexes.size();
+}
+
+CompUnitSP SymbolFileSymtab::ParseCompileUnitAtIndex(uint32_t idx) {
+ CompUnitSP cu_sp;
+
+ // If we don't have any source file symbols we will just have one compile
+ // unit for the entire object file
+ if (idx < m_source_indexes.size()) {
+ const Symbol *cu_symbol =
+ m_obj_file->GetSymtab()->SymbolAtIndex(m_source_indexes[idx]);
+ if (cu_symbol)
+ cu_sp = std::make_shared<CompileUnit>(m_obj_file->GetModule(), nullptr,
+ cu_symbol->GetName().AsCString(), 0,
+ eLanguageTypeUnknown, eLazyBoolNo);
+ }
+ return cu_sp;
+}
+
+lldb::LanguageType SymbolFileSymtab::ParseLanguage(CompileUnit &comp_unit) {
+ return eLanguageTypeUnknown;
+}
+
+size_t SymbolFileSymtab::ParseFunctions(CompileUnit &comp_unit) {
+ size_t num_added = 0;
+ // We must at least have a valid compile unit
+ const Symtab *symtab = m_obj_file->GetSymtab();
+ const Symbol *curr_symbol = nullptr;
+ const Symbol *next_symbol = nullptr;
+ // const char *prefix = m_obj_file->SymbolPrefix();
+ // if (prefix == NULL)
+ // prefix == "";
+ //
+ // const uint32_t prefix_len = strlen(prefix);
+
+ // If we don't have any source file symbols we will just have one compile
+ // unit for the entire object file
+ if (m_source_indexes.empty()) {
+ // The only time we will have a user ID of zero is when we don't have and
+ // source file symbols and we declare one compile unit for the entire
+ // object file
+ if (!m_func_indexes.empty()) {
+ }
+
+ if (!m_code_indexes.empty()) {
+ // StreamFile s(stdout);
+ // symtab->Dump(&s, m_code_indexes);
+
+ uint32_t idx = 0; // Index into the indexes
+ const uint32_t num_indexes = m_code_indexes.size();
+ for (idx = 0; idx < num_indexes; ++idx) {
+ uint32_t symbol_idx = m_code_indexes[idx];
+ curr_symbol = symtab->SymbolAtIndex(symbol_idx);
+ if (curr_symbol) {
+ // Union of all ranges in the function DIE (if the function is
+ // discontiguous)
+ AddressRange func_range(curr_symbol->GetAddress(), 0);
+ if (func_range.GetBaseAddress().IsSectionOffset()) {
+ uint32_t symbol_size = curr_symbol->GetByteSize();
+ if (symbol_size != 0 && !curr_symbol->GetSizeIsSibling())
+ func_range.SetByteSize(symbol_size);
+ else if (idx + 1 < num_indexes) {
+ next_symbol = symtab->SymbolAtIndex(m_code_indexes[idx + 1]);
+ if (next_symbol) {
+ func_range.SetByteSize(
+ next_symbol->GetAddressRef().GetOffset() -
+ curr_symbol->GetAddressRef().GetOffset());
+ }
+ }
+
+ FunctionSP func_sp(
+ new Function(&comp_unit,
+ symbol_idx, // UserID is the DIE offset
+ LLDB_INVALID_UID, // We don't have any type info
+ // for this function
+ curr_symbol->GetMangled(), // Linker/mangled name
+ nullptr, // no return type for a code symbol...
+ func_range)); // first address range
+
+ if (func_sp.get() != nullptr) {
+ comp_unit.AddFunction(func_sp);
+ ++num_added;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // We assume we
+ }
+ return num_added;
+}
+
+size_t SymbolFileSymtab::ParseTypes(CompileUnit &comp_unit) { return 0; }
+
+bool SymbolFileSymtab::ParseLineTable(CompileUnit &comp_unit) { return false; }
+
+bool SymbolFileSymtab::ParseDebugMacros(CompileUnit &comp_unit) {
+ return false;
+}
+
+bool SymbolFileSymtab::ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) {
+ return false;
+}
+
+bool SymbolFileSymtab::ParseImportedModules(
+ const SymbolContext &sc, std::vector<SourceModule> &imported_modules) {
+ return false;
+}
+
+size_t SymbolFileSymtab::ParseBlocksRecursive(Function &func) { return 0; }
+
+size_t SymbolFileSymtab::ParseVariablesForContext(const SymbolContext &sc) {
+ return 0;
+}
+
+Type *SymbolFileSymtab::ResolveTypeUID(lldb::user_id_t type_uid) {
+ return nullptr;
+}
+
+llvm::Optional<SymbolFile::ArrayInfo>
+SymbolFileSymtab::GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
+ return llvm::None;
+}
+
+bool SymbolFileSymtab::CompleteType(lldb_private::CompilerType &compiler_type) {
+ return false;
+}
+
+uint32_t SymbolFileSymtab::ResolveSymbolContext(const Address &so_addr,
+ SymbolContextItem resolve_scope,
+ SymbolContext &sc) {
+ if (m_obj_file->GetSymtab() == nullptr)
+ return 0;
+
+ uint32_t resolved_flags = 0;
+ if (resolve_scope & eSymbolContextSymbol) {
+ sc.symbol = m_obj_file->GetSymtab()->FindSymbolContainingFileAddress(
+ so_addr.GetFileAddress());
+ if (sc.symbol)
+ resolved_flags |= eSymbolContextSymbol;
+ }
+ return resolved_flags;
+}
+
+// PluginInterface protocol
+lldb_private::ConstString SymbolFileSymtab::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t SymbolFileSymtab::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h
new file mode 100644
index 000000000000..bc9a531419ae
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h
@@ -0,0 +1,101 @@
+//===-- SymbolFileSymtab.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_SymbolFileSymtab_h_
+#define liblldb_SymbolFileSymtab_h_
+
+#include <map>
+#include <vector>
+
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/Symtab.h"
+
+class SymbolFileSymtab : public lldb_private::SymbolFile {
+public:
+ // Constructors and Destructors
+ SymbolFileSymtab(lldb_private::ObjectFile *obj_file);
+
+ ~SymbolFileSymtab() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolFile *
+ CreateInstance(lldb_private::ObjectFile *obj_file);
+
+ uint32_t CalculateAbilities() override;
+
+ // Compile Unit function calls
+ uint32_t GetNumCompileUnits() override;
+
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType
+ ParseLanguage(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
+ lldb_private::FileSpecList &support_files) override;
+
+ size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override;
+
+ size_t ParseBlocksRecursive(lldb_private::Function &func) override;
+
+ size_t
+ ParseVariablesForContext(const lldb_private::SymbolContext &sc) override;
+
+ lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override;
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override;
+
+ bool CompleteType(lldb_private::CompilerType &compiler_type) override;
+
+ uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContext &sc) override;
+
+ size_t GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ lldb::TypeClass type_mask,
+ lldb_private::TypeList &type_list) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ typedef std::map<lldb_private::ConstString, lldb::TypeSP> TypeMap;
+
+ lldb_private::Symtab::IndexCollection m_source_indexes;
+ lldb_private::Symtab::IndexCollection m_func_indexes;
+ lldb_private::Symtab::IndexCollection m_code_indexes;
+ lldb_private::Symtab::IndexCollection m_data_indexes;
+ lldb_private::Symtab::NameToIndexMap m_objc_class_name_to_index;
+ TypeMap m_objc_class_types;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(SymbolFileSymtab);
+};
+
+#endif // liblldb_SymbolFileSymtab_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp
new file mode 100644
index 000000000000..f279af61a131
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp
@@ -0,0 +1,162 @@
+//===-- SymbolVendorELF.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolVendorELF.h"
+
+#include <string.h>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/LocateSymbolFile.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// SymbolVendorELF constructor
+SymbolVendorELF::SymbolVendorELF(const lldb::ModuleSP &module_sp)
+ : SymbolVendor(module_sp) {}
+
+// Destructor
+SymbolVendorELF::~SymbolVendorELF() {}
+
+void SymbolVendorELF::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void SymbolVendorELF::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString SymbolVendorELF::GetPluginNameStatic() {
+ static ConstString g_name("ELF");
+ return g_name;
+}
+
+const char *SymbolVendorELF::GetPluginDescriptionStatic() {
+ return "Symbol vendor for ELF that looks for dSYM files that match "
+ "executables.";
+}
+
+// CreateInstance
+//
+// Platforms can register a callback to use when creating symbol vendors to
+// allow for complex debug information file setups, and to also allow for
+// finding separate debug information files.
+SymbolVendor *
+SymbolVendorELF::CreateInstance(const lldb::ModuleSP &module_sp,
+ lldb_private::Stream *feedback_strm) {
+ if (!module_sp)
+ return nullptr;
+
+ ObjectFile *obj_file = module_sp->GetObjectFile();
+ if (!obj_file)
+ return nullptr;
+
+ static ConstString obj_file_elf("elf");
+ ConstString obj_name = obj_file->GetPluginName();
+ if (obj_name != obj_file_elf)
+ return nullptr;
+
+ lldb_private::UUID uuid = obj_file->GetUUID();
+ if (!uuid)
+ return nullptr;
+
+ // Get the .gnu_debuglink file (if specified).
+ FileSpecList file_spec_list = obj_file->GetDebugSymbolFilePaths();
+
+ // If the module specified a filespec, use it first.
+ FileSpec debug_symbol_fspec(module_sp->GetSymbolFileFileSpec());
+ if (debug_symbol_fspec)
+ file_spec_list.Insert(0, debug_symbol_fspec);
+
+ // If we have no debug symbol files, then nothing to do.
+ if (file_spec_list.IsEmpty())
+ return nullptr;
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "SymbolVendorELF::CreateInstance (module = %s)",
+ module_sp->GetFileSpec().GetPath().c_str());
+
+ for (size_t idx = 0; idx < file_spec_list.GetSize(); ++idx) {
+ ModuleSpec module_spec;
+ const FileSpec fspec = file_spec_list.GetFileSpecAtIndex(idx);
+
+ module_spec.GetFileSpec() = obj_file->GetFileSpec();
+ FileSystem::Instance().Resolve(module_spec.GetFileSpec());
+ module_spec.GetSymbolFileSpec() = fspec;
+ module_spec.GetUUID() = uuid;
+ FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
+ FileSpec dsym_fspec =
+ Symbols::LocateExecutableSymbolFile(module_spec, search_paths);
+ if (dsym_fspec) {
+ DataBufferSP dsym_file_data_sp;
+ lldb::offset_t dsym_file_data_offset = 0;
+ ObjectFileSP dsym_objfile_sp =
+ ObjectFile::FindPlugin(module_sp, &dsym_fspec, 0,
+ FileSystem::Instance().GetByteSize(dsym_fspec),
+ dsym_file_data_sp, dsym_file_data_offset);
+ if (dsym_objfile_sp) {
+ // This objfile is for debugging purposes. Sadly, ObjectFileELF won't
+ // be able to figure this out consistently as the symbol file may not
+ // have stripped the code sections, etc.
+ dsym_objfile_sp->SetType(ObjectFile::eTypeDebugInfo);
+
+ SymbolVendorELF *symbol_vendor = new SymbolVendorELF(module_sp);
+ if (symbol_vendor) {
+ // Get the module unified section list and add our debug sections to
+ // that.
+ SectionList *module_section_list = module_sp->GetSectionList();
+ SectionList *objfile_section_list = dsym_objfile_sp->GetSectionList();
+
+ static const SectionType g_sections[] = {
+ eSectionTypeDWARFDebugAbbrev, eSectionTypeDWARFDebugAddr,
+ eSectionTypeDWARFDebugAranges, eSectionTypeDWARFDebugCuIndex,
+ eSectionTypeDWARFDebugFrame, eSectionTypeDWARFDebugInfo,
+ eSectionTypeDWARFDebugLine, eSectionTypeDWARFDebugLoc,
+ eSectionTypeDWARFDebugMacInfo, eSectionTypeDWARFDebugPubNames,
+ eSectionTypeDWARFDebugPubTypes, eSectionTypeDWARFDebugRanges,
+ eSectionTypeDWARFDebugStr, eSectionTypeDWARFDebugStrOffsets,
+ eSectionTypeELFSymbolTable, eSectionTypeDWARFGNUDebugAltLink,
+ };
+ for (size_t idx = 0; idx < sizeof(g_sections) / sizeof(g_sections[0]);
+ ++idx) {
+ SectionType section_type = g_sections[idx];
+ SectionSP section_sp(
+ objfile_section_list->FindSectionByType(section_type, true));
+ if (section_sp) {
+ SectionSP module_section_sp(
+ module_section_list->FindSectionByType(section_type, true));
+ if (module_section_sp)
+ module_section_list->ReplaceSection(module_section_sp->GetID(),
+ section_sp);
+ else
+ module_section_list->AddSection(section_sp);
+ }
+ }
+
+ symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp);
+ return symbol_vendor;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+// PluginInterface protocol
+ConstString SymbolVendorELF::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t SymbolVendorELF::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h b/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h
new file mode 100644
index 000000000000..0cd740da5ce3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h
@@ -0,0 +1,44 @@
+//===-- SymbolVendorELF.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_SymbolVendorELF_h_
+#define liblldb_SymbolVendorELF_h_
+
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/lldb-private.h"
+
+class SymbolVendorELF : public lldb_private::SymbolVendor {
+public:
+ // Constructors and Destructors
+ SymbolVendorELF(const lldb::ModuleSP &module_sp);
+
+ ~SymbolVendorELF() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolVendor *
+ CreateInstance(const lldb::ModuleSP &module_sp,
+ lldb_private::Stream *feedback_strm);
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(SymbolVendorELF);
+};
+
+#endif // liblldb_SymbolVendorELF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
new file mode 100644
index 000000000000..4aa9fb634b61
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
@@ -0,0 +1,656 @@
+//===-- UnwindAssemblyInstEmulation.cpp --------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnwindAssemblyInstEmulation.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Core/FormatEntity.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// UnwindAssemblyInstEmulation method definitions
+
+bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
+ AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) {
+ std::vector<uint8_t> function_text(range.GetByteSize());
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ Status error;
+ const bool prefer_file_cache = true;
+ if (process_sp->GetTarget().ReadMemory(
+ range.GetBaseAddress(), prefer_file_cache, function_text.data(),
+ range.GetByteSize(), error) != range.GetByteSize()) {
+ return false;
+ }
+ }
+ return GetNonCallSiteUnwindPlanFromAssembly(
+ range, function_text.data(), function_text.size(), unwind_plan);
+}
+
+bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
+ AddressRange &range, uint8_t *opcode_data, size_t opcode_size,
+ UnwindPlan &unwind_plan) {
+ if (opcode_data == nullptr || opcode_size == 0)
+ return false;
+
+ if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() &&
+ m_inst_emulator_up.get()) {
+
+ // The instruction emulation subclass setup the unwind plan for the first
+ // instruction.
+ m_inst_emulator_up->CreateFunctionEntryUnwind(unwind_plan);
+
+ // CreateFunctionEntryUnwind should have created the first row. If it
+ // doesn't, then we are done.
+ if (unwind_plan.GetRowCount() == 0)
+ return false;
+
+ const bool prefer_file_cache = true;
+ DisassemblerSP disasm_sp(Disassembler::DisassembleBytes(
+ m_arch, nullptr, nullptr, range.GetBaseAddress(), opcode_data,
+ opcode_size, 99999, prefer_file_cache));
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (disasm_sp) {
+
+ m_range_ptr = &range;
+ m_unwind_plan_ptr = &unwind_plan;
+
+ const uint32_t addr_byte_size = m_arch.GetAddressByteSize();
+ const bool show_address = true;
+ const bool show_bytes = true;
+ m_inst_emulator_up->GetRegisterInfo(unwind_plan.GetRegisterKind(),
+ unwind_plan.GetInitialCFARegister(),
+ m_cfa_reg_info);
+
+ m_fp_is_cfa = false;
+ m_register_values.clear();
+ m_pushed_regs.clear();
+
+ // Initialize the CFA with a known value. In the 32 bit case it will be
+ // 0x80000000, and in the 64 bit case 0x8000000000000000. We use the
+ // address byte size to be safe for any future address sizes
+ m_initial_sp = (1ull << ((addr_byte_size * 8) - 1));
+ RegisterValue cfa_reg_value;
+ cfa_reg_value.SetUInt(m_initial_sp, m_cfa_reg_info.byte_size);
+ SetRegisterValue(m_cfa_reg_info, cfa_reg_value);
+
+ const InstructionList &inst_list = disasm_sp->GetInstructionList();
+ const size_t num_instructions = inst_list.GetSize();
+
+ if (num_instructions > 0) {
+ Instruction *inst = inst_list.GetInstructionAtIndex(0).get();
+ const lldb::addr_t base_addr = inst->GetAddress().GetFileAddress();
+
+ // Map for storing the unwind plan row and the value of the registers
+ // at a given offset. When we see a forward branch we add a new entry
+ // to this map with the actual unwind plan row and register context for
+ // the target address of the branch as the current data have to be
+ // valid for the target address of the branch too if we are in the same
+ // function.
+ std::map<lldb::addr_t, std::pair<UnwindPlan::RowSP, RegisterValueMap>>
+ saved_unwind_states;
+
+ // Make a copy of the current instruction Row and save it in m_curr_row
+ // so we can add updates as we process the instructions.
+ UnwindPlan::RowSP last_row = unwind_plan.GetLastRow();
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ if (last_row.get())
+ *newrow = *last_row.get();
+ m_curr_row.reset(newrow);
+
+ // Add the initial state to the save list with offset 0.
+ saved_unwind_states.insert({0, {last_row, m_register_values}});
+
+ // cache the pc register number (in whatever register numbering this
+ // UnwindPlan uses) for quick reference during instruction parsing.
+ RegisterInfo pc_reg_info;
+ m_inst_emulator_up->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info);
+
+ // cache the return address register number (in whatever register
+ // numbering this UnwindPlan uses) for quick reference during
+ // instruction parsing.
+ RegisterInfo ra_reg_info;
+ m_inst_emulator_up->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, ra_reg_info);
+
+ // The architecture dependent condition code of the last processed
+ // instruction.
+ EmulateInstruction::InstructionCondition last_condition =
+ EmulateInstruction::UnconditionalCondition;
+ lldb::addr_t condition_block_start_offset = 0;
+
+ for (size_t idx = 0; idx < num_instructions; ++idx) {
+ m_curr_row_modified = false;
+ m_forward_branch_offset = 0;
+
+ inst = inst_list.GetInstructionAtIndex(idx).get();
+ if (inst) {
+ lldb::addr_t current_offset =
+ inst->GetAddress().GetFileAddress() - base_addr;
+ auto it = saved_unwind_states.upper_bound(current_offset);
+ assert(it != saved_unwind_states.begin() &&
+ "Unwind row for the function entry missing");
+ --it; // Move it to the row corresponding to the current offset
+
+ // If the offset of m_curr_row don't match with the offset we see
+ // in saved_unwind_states then we have to update m_curr_row and
+ // m_register_values based on the saved values. It is happening
+ // after we processed an epilogue and a return to caller
+ // instruction.
+ if (it->second.first->GetOffset() != m_curr_row->GetOffset()) {
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *it->second.first;
+ m_curr_row.reset(newrow);
+ m_register_values = it->second.second;
+ }
+
+ m_inst_emulator_up->SetInstruction(inst->GetOpcode(),
+ inst->GetAddress(), nullptr);
+
+ if (last_condition !=
+ m_inst_emulator_up->GetInstructionCondition()) {
+ if (m_inst_emulator_up->GetInstructionCondition() !=
+ EmulateInstruction::UnconditionalCondition &&
+ saved_unwind_states.count(current_offset) == 0) {
+ // If we don't have a saved row for the current offset then
+ // save our current state because we will have to restore it
+ // after the conditional block.
+ auto new_row =
+ std::make_shared<UnwindPlan::Row>(*m_curr_row.get());
+ saved_unwind_states.insert(
+ {current_offset, {new_row, m_register_values}});
+ }
+
+ // If the last instruction was conditional with a different
+ // condition then the then current condition then restore the
+ // condition.
+ if (last_condition !=
+ EmulateInstruction::UnconditionalCondition) {
+ const auto &saved_state =
+ saved_unwind_states.at(condition_block_start_offset);
+ m_curr_row =
+ std::make_shared<UnwindPlan::Row>(*saved_state.first);
+ m_curr_row->SetOffset(current_offset);
+ m_register_values = saved_state.second;
+ bool replace_existing =
+ true; // The last instruction might already
+ // created a row for this offset and
+ // we want to overwrite it.
+ unwind_plan.InsertRow(
+ std::make_shared<UnwindPlan::Row>(*m_curr_row),
+ replace_existing);
+ }
+
+ // We are starting a new conditional block at the actual offset
+ condition_block_start_offset = current_offset;
+ }
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+ lldb_private::FormatEntity::Entry format;
+ FormatEntity::Parse("${frame.pc}: ", format);
+ inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address,
+ show_bytes, nullptr, nullptr, nullptr, &format, 0);
+ log->PutString(strm.GetString());
+ }
+
+ last_condition = m_inst_emulator_up->GetInstructionCondition();
+
+ m_inst_emulator_up->EvaluateInstruction(
+ eEmulateInstructionOptionIgnoreConditions);
+
+ // If the current instruction is a branch forward then save the
+ // current CFI information for the offset where we are branching.
+ if (m_forward_branch_offset != 0 &&
+ range.ContainsFileAddress(inst->GetAddress().GetFileAddress() +
+ m_forward_branch_offset)) {
+ auto newrow =
+ std::make_shared<UnwindPlan::Row>(*m_curr_row.get());
+ newrow->SetOffset(current_offset + m_forward_branch_offset);
+ saved_unwind_states.insert(
+ {current_offset + m_forward_branch_offset,
+ {newrow, m_register_values}});
+ unwind_plan.InsertRow(newrow);
+ }
+
+ // Were there any changes to the CFI while evaluating this
+ // instruction?
+ if (m_curr_row_modified) {
+ // Save the modified row if we don't already have a CFI row in
+ // the current address
+ if (saved_unwind_states.count(
+ current_offset + inst->GetOpcode().GetByteSize()) == 0) {
+ m_curr_row->SetOffset(current_offset +
+ inst->GetOpcode().GetByteSize());
+ unwind_plan.InsertRow(m_curr_row);
+ saved_unwind_states.insert(
+ {current_offset + inst->GetOpcode().GetByteSize(),
+ {m_curr_row, m_register_values}});
+
+ // Allocate a new Row for m_curr_row, copy the current state
+ // into it
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *m_curr_row.get();
+ m_curr_row.reset(newrow);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+ lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress();
+ strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):",
+ base_addr, base_addr + range.GetByteSize());
+ unwind_plan.Dump(strm, nullptr, base_addr);
+ log->PutString(strm.GetString());
+ }
+ return unwind_plan.GetRowCount() > 0;
+ }
+ return false;
+}
+
+bool UnwindAssemblyInstEmulation::AugmentUnwindPlanFromCallSite(
+ AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
+ return false;
+}
+
+bool UnwindAssemblyInstEmulation::GetFastUnwindPlan(AddressRange &func,
+ Thread &thread,
+ UnwindPlan &unwind_plan) {
+ return false;
+}
+
+bool UnwindAssemblyInstEmulation::FirstNonPrologueInsn(
+ AddressRange &func, const ExecutionContext &exe_ctx,
+ Address &first_non_prologue_insn) {
+ return false;
+}
+
+UnwindAssembly *
+UnwindAssemblyInstEmulation::CreateInstance(const ArchSpec &arch) {
+ std::unique_ptr<EmulateInstruction> inst_emulator_up(
+ EmulateInstruction::FindPlugin(arch, eInstructionTypePrologueEpilogue,
+ nullptr));
+ // Make sure that all prologue instructions are handled
+ if (inst_emulator_up)
+ return new UnwindAssemblyInstEmulation(arch, inst_emulator_up.release());
+ return nullptr;
+}
+
+// PluginInterface protocol in UnwindAssemblyParser_x86
+ConstString UnwindAssemblyInstEmulation::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t UnwindAssemblyInstEmulation::GetPluginVersion() { return 1; }
+
+void UnwindAssemblyInstEmulation::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void UnwindAssemblyInstEmulation::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString UnwindAssemblyInstEmulation::GetPluginNameStatic() {
+ static ConstString g_name("inst-emulation");
+ return g_name;
+}
+
+const char *UnwindAssemblyInstEmulation::GetPluginDescriptionStatic() {
+ return "Instruction emulation based unwind information.";
+}
+
+uint64_t UnwindAssemblyInstEmulation::MakeRegisterKindValuePair(
+ const RegisterInfo &reg_info) {
+ lldb::RegisterKind reg_kind;
+ uint32_t reg_num;
+ if (EmulateInstruction::GetBestRegisterKindAndNumber(&reg_info, reg_kind,
+ reg_num))
+ return (uint64_t)reg_kind << 24 | reg_num;
+ return 0ull;
+}
+
+void UnwindAssemblyInstEmulation::SetRegisterValue(
+ const RegisterInfo &reg_info, const RegisterValue &reg_value) {
+ m_register_values[MakeRegisterKindValuePair(reg_info)] = reg_value;
+}
+
+bool UnwindAssemblyInstEmulation::GetRegisterValue(const RegisterInfo &reg_info,
+ RegisterValue &reg_value) {
+ const uint64_t reg_id = MakeRegisterKindValuePair(reg_info);
+ RegisterValueMap::const_iterator pos = m_register_values.find(reg_id);
+ if (pos != m_register_values.end()) {
+ reg_value = pos->second;
+ return true; // We had a real value that comes from an opcode that wrote
+ // to it...
+ }
+ // We are making up a value that is recognizable...
+ reg_value.SetUInt(reg_id, reg_info.byte_size);
+ return false;
+}
+
+size_t UnwindAssemblyInstEmulation::ReadMemory(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst,
+ size_t dst_len) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+ strm.Printf(
+ "UnwindAssemblyInstEmulation::ReadMemory (addr = 0x%16.16" PRIx64
+ ", dst = %p, dst_len = %" PRIu64 ", context = ",
+ addr, dst, (uint64_t)dst_len);
+ context.Dump(strm, instruction);
+ log->PutString(strm.GetString());
+ }
+ memset(dst, 0, dst_len);
+ return dst_len;
+}
+
+size_t UnwindAssemblyInstEmulation::WriteMemory(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr,
+ const void *dst, size_t dst_len) {
+ if (baton && dst && dst_len)
+ return ((UnwindAssemblyInstEmulation *)baton)
+ ->WriteMemory(instruction, context, addr, dst, dst_len);
+ return 0;
+}
+
+size_t UnwindAssemblyInstEmulation::WriteMemory(
+ EmulateInstruction *instruction, const EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst, size_t dst_len) {
+ DataExtractor data(dst, dst_len,
+ instruction->GetArchitecture().GetByteOrder(),
+ instruction->GetArchitecture().GetAddressByteSize());
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+
+ strm.PutCString("UnwindAssemblyInstEmulation::WriteMemory (");
+ DumpDataExtractor(data, &strm, 0, eFormatBytes, 1, dst_len, UINT32_MAX,
+ addr, 0, 0);
+ strm.PutCString(", context = ");
+ context.Dump(strm, instruction);
+ log->PutString(strm.GetString());
+ }
+
+ const bool cant_replace = false;
+
+ switch (context.type) {
+ default:
+ case EmulateInstruction::eContextInvalid:
+ case EmulateInstruction::eContextReadOpcode:
+ case EmulateInstruction::eContextImmediate:
+ case EmulateInstruction::eContextAdjustBaseRegister:
+ case EmulateInstruction::eContextRegisterPlusOffset:
+ case EmulateInstruction::eContextAdjustPC:
+ case EmulateInstruction::eContextRegisterStore:
+ case EmulateInstruction::eContextRegisterLoad:
+ case EmulateInstruction::eContextRelativeBranchImmediate:
+ case EmulateInstruction::eContextAbsoluteBranchRegister:
+ case EmulateInstruction::eContextSupervisorCall:
+ case EmulateInstruction::eContextTableBranchReadMemory:
+ case EmulateInstruction::eContextWriteRegisterRandomBits:
+ case EmulateInstruction::eContextWriteMemoryRandomBits:
+ case EmulateInstruction::eContextArithmetic:
+ case EmulateInstruction::eContextAdvancePC:
+ case EmulateInstruction::eContextReturnFromException:
+ case EmulateInstruction::eContextPopRegisterOffStack:
+ case EmulateInstruction::eContextAdjustStackPointer:
+ break;
+
+ case EmulateInstruction::eContextPushRegisterOnStack: {
+ uint32_t reg_num = LLDB_INVALID_REGNUM;
+ uint32_t generic_regnum = LLDB_INVALID_REGNUM;
+ assert(context.info_type ==
+ EmulateInstruction::eInfoTypeRegisterToRegisterPlusOffset &&
+ "unhandled case, add code to handle this!");
+ const uint32_t unwind_reg_kind = m_unwind_plan_ptr->GetRegisterKind();
+ reg_num = context.info.RegisterToRegisterPlusOffset.data_reg
+ .kinds[unwind_reg_kind];
+ generic_regnum = context.info.RegisterToRegisterPlusOffset.data_reg
+ .kinds[eRegisterKindGeneric];
+
+ if (reg_num != LLDB_INVALID_REGNUM &&
+ generic_regnum != LLDB_REGNUM_GENERIC_SP) {
+ if (m_pushed_regs.find(reg_num) == m_pushed_regs.end()) {
+ m_pushed_regs[reg_num] = addr;
+ const int32_t offset = addr - m_initial_sp;
+ m_curr_row->SetRegisterLocationToAtCFAPlusOffset(reg_num, offset,
+ cant_replace);
+ m_curr_row_modified = true;
+ }
+ }
+ } break;
+ }
+
+ return dst_len;
+}
+
+bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction,
+ void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+
+ if (baton && reg_info)
+ return ((UnwindAssemblyInstEmulation *)baton)
+ ->ReadRegister(instruction, reg_info, reg_value);
+ return false;
+}
+bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ bool synthetic = GetRegisterValue(*reg_info, reg_value);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+
+ StreamString strm;
+ strm.Printf("UnwindAssemblyInstEmulation::ReadRegister (name = \"%s\") => "
+ "synthetic_value = %i, value = ",
+ reg_info->name, synthetic);
+ DumpRegisterValue(reg_value, &strm, reg_info, false, false, eFormatDefault);
+ log->PutString(strm.GetString());
+ }
+ return true;
+}
+
+bool UnwindAssemblyInstEmulation::WriteRegister(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ if (baton && reg_info)
+ return ((UnwindAssemblyInstEmulation *)baton)
+ ->WriteRegister(instruction, context, reg_info, reg_value);
+ return false;
+}
+bool UnwindAssemblyInstEmulation::WriteRegister(
+ EmulateInstruction *instruction, const EmulateInstruction::Context &context,
+ const RegisterInfo *reg_info, const RegisterValue &reg_value) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+
+ StreamString strm;
+ strm.Printf(
+ "UnwindAssemblyInstEmulation::WriteRegister (name = \"%s\", value = ",
+ reg_info->name);
+ DumpRegisterValue(reg_value, &strm, reg_info, false, false, eFormatDefault);
+ strm.PutCString(", context = ");
+ context.Dump(strm, instruction);
+ log->PutString(strm.GetString());
+ }
+
+ SetRegisterValue(*reg_info, reg_value);
+
+ switch (context.type) {
+ case EmulateInstruction::eContextInvalid:
+ case EmulateInstruction::eContextReadOpcode:
+ case EmulateInstruction::eContextImmediate:
+ case EmulateInstruction::eContextAdjustBaseRegister:
+ case EmulateInstruction::eContextRegisterPlusOffset:
+ case EmulateInstruction::eContextAdjustPC:
+ case EmulateInstruction::eContextRegisterStore:
+ case EmulateInstruction::eContextSupervisorCall:
+ case EmulateInstruction::eContextTableBranchReadMemory:
+ case EmulateInstruction::eContextWriteRegisterRandomBits:
+ case EmulateInstruction::eContextWriteMemoryRandomBits:
+ case EmulateInstruction::eContextAdvancePC:
+ case EmulateInstruction::eContextReturnFromException:
+ case EmulateInstruction::eContextPushRegisterOnStack:
+ case EmulateInstruction::eContextRegisterLoad:
+ // {
+ // const uint32_t reg_num =
+ // reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ // if (reg_num != LLDB_INVALID_REGNUM)
+ // {
+ // const bool can_replace_only_if_unspecified = true;
+ //
+ // m_curr_row.SetRegisterLocationToUndefined (reg_num,
+ // can_replace_only_if_unspecified,
+ // can_replace_only_if_unspecified);
+ // m_curr_row_modified = true;
+ // }
+ // }
+ break;
+
+ case EmulateInstruction::eContextArithmetic: {
+ // If we adjusted the current frame pointer by a constant then adjust the
+ // CFA offset
+ // with the same amount.
+ lldb::RegisterKind kind = m_unwind_plan_ptr->GetRegisterKind();
+ if (m_fp_is_cfa && reg_info->kinds[kind] == m_cfa_reg_info.kinds[kind] &&
+ context.info_type == EmulateInstruction::eInfoTypeRegisterPlusOffset &&
+ context.info.RegisterPlusOffset.reg.kinds[kind] ==
+ m_cfa_reg_info.kinds[kind]) {
+ const int64_t offset = context.info.RegisterPlusOffset.signed_offset;
+ m_curr_row->GetCFAValue().IncOffset(-1 * offset);
+ m_curr_row_modified = true;
+ }
+ } break;
+
+ case EmulateInstruction::eContextAbsoluteBranchRegister:
+ case EmulateInstruction::eContextRelativeBranchImmediate: {
+ if (context.info_type == EmulateInstruction::eInfoTypeISAAndImmediate &&
+ context.info.ISAAndImmediate.unsigned_data32 > 0) {
+ m_forward_branch_offset =
+ context.info.ISAAndImmediateSigned.signed_data32;
+ } else if (context.info_type ==
+ EmulateInstruction::eInfoTypeISAAndImmediateSigned &&
+ context.info.ISAAndImmediateSigned.signed_data32 > 0) {
+ m_forward_branch_offset = context.info.ISAAndImmediate.unsigned_data32;
+ } else if (context.info_type == EmulateInstruction::eInfoTypeImmediate &&
+ context.info.unsigned_immediate > 0) {
+ m_forward_branch_offset = context.info.unsigned_immediate;
+ } else if (context.info_type ==
+ EmulateInstruction::eInfoTypeImmediateSigned &&
+ context.info.signed_immediate > 0) {
+ m_forward_branch_offset = context.info.signed_immediate;
+ }
+ } break;
+
+ case EmulateInstruction::eContextPopRegisterOffStack: {
+ const uint32_t reg_num =
+ reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ const uint32_t generic_regnum = reg_info->kinds[eRegisterKindGeneric];
+ if (reg_num != LLDB_INVALID_REGNUM &&
+ generic_regnum != LLDB_REGNUM_GENERIC_SP) {
+ switch (context.info_type) {
+ case EmulateInstruction::eInfoTypeAddress:
+ if (m_pushed_regs.find(reg_num) != m_pushed_regs.end() &&
+ context.info.address == m_pushed_regs[reg_num]) {
+ m_curr_row->SetRegisterLocationToSame(reg_num,
+ false /*must_replace*/);
+ m_curr_row_modified = true;
+ }
+ break;
+ case EmulateInstruction::eInfoTypeISA:
+ assert(
+ (generic_regnum == LLDB_REGNUM_GENERIC_PC ||
+ generic_regnum == LLDB_REGNUM_GENERIC_FLAGS) &&
+ "eInfoTypeISA used for popping a register other the PC/FLAGS");
+ if (generic_regnum != LLDB_REGNUM_GENERIC_FLAGS) {
+ m_curr_row->SetRegisterLocationToSame(reg_num,
+ false /*must_replace*/);
+ m_curr_row_modified = true;
+ }
+ break;
+ default:
+ assert(false && "unhandled case, add code to handle this!");
+ break;
+ }
+ }
+ } break;
+
+ case EmulateInstruction::eContextSetFramePointer:
+ if (!m_fp_is_cfa) {
+ m_fp_is_cfa = true;
+ m_cfa_reg_info = *reg_info;
+ const uint32_t cfa_reg_num =
+ reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ assert(cfa_reg_num != LLDB_INVALID_REGNUM);
+ m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
+ cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64());
+ m_curr_row_modified = true;
+ }
+ break;
+
+ case EmulateInstruction::eContextRestoreStackPointer:
+ if (m_fp_is_cfa) {
+ m_fp_is_cfa = false;
+ m_cfa_reg_info = *reg_info;
+ const uint32_t cfa_reg_num =
+ reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ assert(cfa_reg_num != LLDB_INVALID_REGNUM);
+ m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
+ cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64());
+ m_curr_row_modified = true;
+ }
+ break;
+
+ case EmulateInstruction::eContextAdjustStackPointer:
+ // If we have created a frame using the frame pointer, don't follow
+ // subsequent adjustments to the stack pointer.
+ if (!m_fp_is_cfa) {
+ m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
+ m_curr_row->GetCFAValue().GetRegisterNumber(),
+ m_initial_sp - reg_value.GetAsUInt64());
+ m_curr_row_modified = true;
+ }
+ break;
+ }
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h
new file mode 100644
index 000000000000..9125bd5b1fe3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h
@@ -0,0 +1,154 @@
+//===-- UnwindAssemblyInstEmulation.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_UnwindAssemblyInstEmulation_h_
+#define liblldb_UnwindAssemblyInstEmulation_h_
+
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/UnwindAssembly.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/lldb-private.h"
+
+class UnwindAssemblyInstEmulation : public lldb_private::UnwindAssembly {
+public:
+ ~UnwindAssemblyInstEmulation() override = default;
+
+ bool GetNonCallSiteUnwindPlanFromAssembly(
+ lldb_private::AddressRange &func, lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool
+ GetNonCallSiteUnwindPlanFromAssembly(lldb_private::AddressRange &func,
+ uint8_t *opcode_data, size_t opcode_size,
+ lldb_private::UnwindPlan &unwind_plan);
+
+ bool
+ AugmentUnwindPlanFromCallSite(lldb_private::AddressRange &func,
+ lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool GetFastUnwindPlan(lldb_private::AddressRange &func,
+ lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ // thread may be NULL in which case we only use the Target (e.g. if this is
+ // called pre-process-launch).
+ bool
+ FirstNonPrologueInsn(lldb_private::AddressRange &func,
+ const lldb_private::ExecutionContext &exe_ctx,
+ lldb_private::Address &first_non_prologue_insn) override;
+
+ static lldb_private::UnwindAssembly *
+ CreateInstance(const lldb_private::ArchSpec &arch);
+
+ // PluginInterface protocol
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+private:
+ // Call CreateInstance to get an instance of this class
+ UnwindAssemblyInstEmulation(const lldb_private::ArchSpec &arch,
+ lldb_private::EmulateInstruction *inst_emulator)
+ : UnwindAssembly(arch), m_inst_emulator_up(inst_emulator),
+ m_range_ptr(nullptr), m_unwind_plan_ptr(nullptr), m_curr_row(),
+ m_cfa_reg_info(), m_fp_is_cfa(false), m_register_values(),
+ m_pushed_regs(), m_curr_row_modified(false),
+ m_forward_branch_offset(0) {
+ if (m_inst_emulator_up.get()) {
+ m_inst_emulator_up->SetBaton(this);
+ m_inst_emulator_up->SetCallbacks(ReadMemory, WriteMemory, ReadRegister,
+ WriteRegister);
+ }
+ }
+
+ static size_t
+ ReadMemory(lldb_private::EmulateInstruction *instruction, void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ lldb::addr_t addr, void *dst, size_t length);
+
+ static size_t
+ WriteMemory(lldb_private::EmulateInstruction *instruction, void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst, size_t length);
+
+ static bool ReadRegister(lldb_private::EmulateInstruction *instruction,
+ void *baton,
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value);
+
+ static bool
+ WriteRegister(lldb_private::EmulateInstruction *instruction, void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+ // size_t
+ // ReadMemory (lldb_private::EmulateInstruction *instruction,
+ // const lldb_private::EmulateInstruction::Context &context,
+ // lldb::addr_t addr,
+ // void *dst,
+ // size_t length);
+
+ size_t WriteMemory(lldb_private::EmulateInstruction *instruction,
+ const lldb_private::EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst, size_t length);
+
+ bool ReadRegister(lldb_private::EmulateInstruction *instruction,
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value);
+
+ bool WriteRegister(lldb_private::EmulateInstruction *instruction,
+ const lldb_private::EmulateInstruction::Context &context,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+ static uint64_t
+ MakeRegisterKindValuePair(const lldb_private::RegisterInfo &reg_info);
+
+ void SetRegisterValue(const lldb_private::RegisterInfo &reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+ bool GetRegisterValue(const lldb_private::RegisterInfo &reg_info,
+ lldb_private::RegisterValue &reg_value);
+
+ std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_up;
+ lldb_private::AddressRange *m_range_ptr;
+ lldb_private::UnwindPlan *m_unwind_plan_ptr;
+ lldb_private::UnwindPlan::RowSP m_curr_row;
+ typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap;
+ uint64_t m_initial_sp;
+ lldb_private::RegisterInfo m_cfa_reg_info;
+ bool m_fp_is_cfa;
+ typedef std::map<uint64_t, lldb_private::RegisterValue> RegisterValueMap;
+ RegisterValueMap m_register_values;
+ PushedRegisterToAddrMap m_pushed_regs;
+
+ // While processing the instruction stream, we need to communicate some state
+ // change
+ // information up to the higher level loop that makes decisions about how to
+ // push
+ // the unwind instructions for the UnwindPlan we're constructing.
+
+ // The instruction we're processing updated the UnwindPlan::Row contents
+ bool m_curr_row_modified;
+ // The instruction is branching forward with the given offset. 0 value means
+ // no branching.
+ uint32_t m_forward_branch_offset;
+};
+
+#endif // liblldb_UnwindAssemblyInstEmulation_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
new file mode 100644
index 000000000000..ce168f021047
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
@@ -0,0 +1,269 @@
+//===-- UnwindAssembly-x86.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnwindAssembly-x86.h"
+#include "x86AssemblyInspectionEngine.h"
+
+#include "llvm-c/Disassembler.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/RegisterNumber.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/UnwindAssembly.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// UnwindAssemblyParser_x86 method definitions
+
+UnwindAssembly_x86::UnwindAssembly_x86(const ArchSpec &arch)
+ : lldb_private::UnwindAssembly(arch),
+ m_assembly_inspection_engine(new x86AssemblyInspectionEngine(arch)) {}
+
+UnwindAssembly_x86::~UnwindAssembly_x86() {
+ delete m_assembly_inspection_engine;
+}
+
+bool UnwindAssembly_x86::GetNonCallSiteUnwindPlanFromAssembly(
+ AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
+ if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0)
+ return false;
+ if (m_assembly_inspection_engine == nullptr)
+ return false;
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp.get() == nullptr)
+ return false;
+ const bool prefer_file_cache = true;
+ std::vector<uint8_t> function_text(func.GetByteSize());
+ Status error;
+ if (process_sp->GetTarget().ReadMemory(
+ func.GetBaseAddress(), prefer_file_cache, function_text.data(),
+ func.GetByteSize(), error) == func.GetByteSize()) {
+ RegisterContextSP reg_ctx(thread.GetRegisterContext());
+ m_assembly_inspection_engine->Initialize(reg_ctx);
+ return m_assembly_inspection_engine->GetNonCallSiteUnwindPlanFromAssembly(
+ function_text.data(), func.GetByteSize(), func, unwind_plan);
+ }
+ return false;
+}
+
+bool UnwindAssembly_x86::AugmentUnwindPlanFromCallSite(
+ AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
+ bool do_augment_unwindplan = true;
+
+ UnwindPlan::RowSP first_row = unwind_plan.GetRowForFunctionOffset(0);
+ UnwindPlan::RowSP last_row = unwind_plan.GetRowForFunctionOffset(-1);
+
+ int wordsize = 8;
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp.get() == nullptr)
+ return false;
+
+ wordsize = process_sp->GetTarget().GetArchitecture().GetAddressByteSize();
+
+ RegisterNumber sp_regnum(thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP);
+ RegisterNumber pc_regnum(thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+
+ // Does this UnwindPlan describe the prologue? I want to see that the CFA is
+ // set in terms of the stack pointer plus an offset, and I want to see that
+ // rip is retrieved at the CFA-wordsize. If there is no description of the
+ // prologue, don't try to augment this eh_frame unwinder code, fall back to
+ // assembly parsing instead.
+
+ if (first_row->GetCFAValue().GetValueType() !=
+ UnwindPlan::Row::FAValue::isRegisterPlusOffset ||
+ RegisterNumber(thread, unwind_plan.GetRegisterKind(),
+ first_row->GetCFAValue().GetRegisterNumber()) !=
+ sp_regnum ||
+ first_row->GetCFAValue().GetOffset() != wordsize) {
+ return false;
+ }
+ UnwindPlan::Row::RegisterLocation first_row_pc_loc;
+ if (!first_row->GetRegisterInfo(
+ pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()),
+ first_row_pc_loc) ||
+ !first_row_pc_loc.IsAtCFAPlusOffset() ||
+ first_row_pc_loc.GetOffset() != -wordsize) {
+ return false;
+ }
+
+ // It looks like the prologue is described. Is the epilogue described? If it
+ // is, no need to do any augmentation.
+
+ if (first_row != last_row &&
+ first_row->GetOffset() != last_row->GetOffset()) {
+ // The first & last row have the same CFA register and the same CFA offset
+ // value and the CFA register is esp/rsp (the stack pointer).
+
+ // We're checking that both of them have an unwind rule like "CFA=esp+4" or
+ // CFA+rsp+8".
+
+ if (first_row->GetCFAValue().GetValueType() ==
+ last_row->GetCFAValue().GetValueType() &&
+ first_row->GetCFAValue().GetRegisterNumber() ==
+ last_row->GetCFAValue().GetRegisterNumber() &&
+ first_row->GetCFAValue().GetOffset() ==
+ last_row->GetCFAValue().GetOffset()) {
+ // Get the register locations for eip/rip from the first & last rows. Are
+ // they both CFA plus an offset? Is it the same offset?
+
+ UnwindPlan::Row::RegisterLocation last_row_pc_loc;
+ if (last_row->GetRegisterInfo(
+ pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()),
+ last_row_pc_loc)) {
+ if (last_row_pc_loc.IsAtCFAPlusOffset() &&
+ first_row_pc_loc.GetOffset() == last_row_pc_loc.GetOffset()) {
+
+ // One last sanity check: Is the unwind rule for getting the caller
+ // pc value "deref the CFA-4" or "deref the CFA-8"?
+
+ // If so, we have an UnwindPlan that already describes the epilogue
+ // and we don't need to modify it at all.
+
+ if (first_row_pc_loc.GetOffset() == -wordsize) {
+ do_augment_unwindplan = false;
+ }
+ }
+ }
+ }
+ }
+
+ if (do_augment_unwindplan) {
+ if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0)
+ return false;
+ if (m_assembly_inspection_engine == nullptr)
+ return false;
+ const bool prefer_file_cache = true;
+ std::vector<uint8_t> function_text(func.GetByteSize());
+ Status error;
+ if (process_sp->GetTarget().ReadMemory(
+ func.GetBaseAddress(), prefer_file_cache, function_text.data(),
+ func.GetByteSize(), error) == func.GetByteSize()) {
+ RegisterContextSP reg_ctx(thread.GetRegisterContext());
+ m_assembly_inspection_engine->Initialize(reg_ctx);
+ return m_assembly_inspection_engine->AugmentUnwindPlanFromCallSite(
+ function_text.data(), func.GetByteSize(), func, unwind_plan, reg_ctx);
+ }
+ }
+
+ return false;
+}
+
+bool UnwindAssembly_x86::GetFastUnwindPlan(AddressRange &func, Thread &thread,
+ UnwindPlan &unwind_plan) {
+ // if prologue is
+ // 55 pushl %ebp
+ // 89 e5 movl %esp, %ebp
+ // or
+ // 55 pushq %rbp
+ // 48 89 e5 movq %rsp, %rbp
+
+ // We should pull in the ABI architecture default unwind plan and return that
+
+ llvm::SmallVector<uint8_t, 4> opcode_data;
+
+ ProcessSP process_sp = thread.GetProcess();
+ if (process_sp) {
+ Target &target(process_sp->GetTarget());
+ const bool prefer_file_cache = true;
+ Status error;
+ if (target.ReadMemory(func.GetBaseAddress(), prefer_file_cache,
+ opcode_data.data(), 4, error) == 4) {
+ uint8_t i386_push_mov[] = {0x55, 0x89, 0xe5};
+ uint8_t x86_64_push_mov[] = {0x55, 0x48, 0x89, 0xe5};
+
+ if (memcmp(opcode_data.data(), i386_push_mov, sizeof(i386_push_mov)) ==
+ 0 ||
+ memcmp(opcode_data.data(), x86_64_push_mov,
+ sizeof(x86_64_push_mov)) == 0) {
+ ABISP abi_sp = process_sp->GetABI();
+ if (abi_sp) {
+ return abi_sp->CreateDefaultUnwindPlan(unwind_plan);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool UnwindAssembly_x86::FirstNonPrologueInsn(
+ AddressRange &func, const ExecutionContext &exe_ctx,
+ Address &first_non_prologue_insn) {
+
+ if (!func.GetBaseAddress().IsValid())
+ return false;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target == nullptr)
+ return false;
+
+ if (m_assembly_inspection_engine == nullptr)
+ return false;
+
+ const bool prefer_file_cache = true;
+ std::vector<uint8_t> function_text(func.GetByteSize());
+ Status error;
+ if (target->ReadMemory(func.GetBaseAddress(), prefer_file_cache,
+ function_text.data(), func.GetByteSize(),
+ error) == func.GetByteSize()) {
+ size_t offset;
+ if (m_assembly_inspection_engine->FindFirstNonPrologueInstruction(
+ function_text.data(), func.GetByteSize(), offset)) {
+ first_non_prologue_insn = func.GetBaseAddress();
+ first_non_prologue_insn.Slide(offset);
+ }
+ return true;
+ }
+ return false;
+}
+
+UnwindAssembly *UnwindAssembly_x86::CreateInstance(const ArchSpec &arch) {
+ const llvm::Triple::ArchType cpu = arch.GetMachine();
+ if (cpu == llvm::Triple::x86 || cpu == llvm::Triple::x86_64)
+ return new UnwindAssembly_x86(arch);
+ return nullptr;
+}
+
+// PluginInterface protocol in UnwindAssemblyParser_x86
+
+ConstString UnwindAssembly_x86::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t UnwindAssembly_x86::GetPluginVersion() { return 1; }
+
+void UnwindAssembly_x86::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void UnwindAssembly_x86::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString UnwindAssembly_x86::GetPluginNameStatic() {
+ static ConstString g_name("x86");
+ return g_name;
+}
+
+const char *UnwindAssembly_x86::GetPluginDescriptionStatic() {
+ return "i386 and x86_64 assembly language profiler plugin.";
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h
new file mode 100644
index 000000000000..7c198bbc33af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h
@@ -0,0 +1,65 @@
+//===-- UnwindAssembly-x86.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_UnwindAssembly_x86_h_
+#define liblldb_UnwindAssembly_x86_h_
+
+#include "x86AssemblyInspectionEngine.h"
+
+#include "lldb/Target/UnwindAssembly.h"
+#include "lldb/lldb-private.h"
+
+class UnwindAssembly_x86 : public lldb_private::UnwindAssembly {
+public:
+ ~UnwindAssembly_x86() override;
+
+ bool GetNonCallSiteUnwindPlanFromAssembly(
+ lldb_private::AddressRange &func, lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool
+ AugmentUnwindPlanFromCallSite(lldb_private::AddressRange &func,
+ lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool GetFastUnwindPlan(lldb_private::AddressRange &func,
+ lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ // thread may be NULL in which case we only use the Target (e.g. if this is
+ // called pre-process-launch).
+ bool
+ FirstNonPrologueInsn(lldb_private::AddressRange &func,
+ const lldb_private::ExecutionContext &exe_ctx,
+ lldb_private::Address &first_non_prologue_insn) override;
+
+ static lldb_private::UnwindAssembly *
+ CreateInstance(const lldb_private::ArchSpec &arch);
+
+ // PluginInterface protocol
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+private:
+ UnwindAssembly_x86(const lldb_private::ArchSpec &arch);
+
+ lldb_private::ArchSpec m_arch;
+
+ lldb_private::x86AssemblyInspectionEngine *m_assembly_inspection_engine;
+};
+
+#endif // liblldb_UnwindAssembly_x86_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp
new file mode 100644
index 000000000000..43041ca1bb2f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp
@@ -0,0 +1,1614 @@
+//===-- x86AssemblyInspectionEngine.cpp -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "x86AssemblyInspectionEngine.h"
+
+#include <memory>
+
+#include "llvm-c/Disassembler.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/UnwindAssembly.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+x86AssemblyInspectionEngine::x86AssemblyInspectionEngine(const ArchSpec &arch)
+ : m_cur_insn(nullptr), m_machine_ip_regnum(LLDB_INVALID_REGNUM),
+ m_machine_sp_regnum(LLDB_INVALID_REGNUM),
+ m_machine_fp_regnum(LLDB_INVALID_REGNUM),
+ m_lldb_ip_regnum(LLDB_INVALID_REGNUM),
+ m_lldb_sp_regnum(LLDB_INVALID_REGNUM),
+ m_lldb_fp_regnum(LLDB_INVALID_REGNUM),
+
+ m_reg_map(), m_arch(arch), m_cpu(k_cpu_unspecified), m_wordsize(-1),
+ m_register_map_initialized(false), m_disasm_context() {
+ m_disasm_context =
+ ::LLVMCreateDisasm(arch.GetTriple().getTriple().c_str(), nullptr,
+ /*TagType=*/1, nullptr, nullptr);
+}
+
+x86AssemblyInspectionEngine::~x86AssemblyInspectionEngine() {
+ ::LLVMDisasmDispose(m_disasm_context);
+}
+
+void x86AssemblyInspectionEngine::Initialize(RegisterContextSP &reg_ctx) {
+ m_cpu = k_cpu_unspecified;
+ m_wordsize = -1;
+ m_register_map_initialized = false;
+
+ const llvm::Triple::ArchType cpu = m_arch.GetMachine();
+ if (cpu == llvm::Triple::x86)
+ m_cpu = k_i386;
+ else if (cpu == llvm::Triple::x86_64)
+ m_cpu = k_x86_64;
+
+ if (m_cpu == k_cpu_unspecified)
+ return;
+
+ if (reg_ctx.get() == nullptr)
+ return;
+
+ if (m_cpu == k_i386) {
+ m_machine_ip_regnum = k_machine_eip;
+ m_machine_sp_regnum = k_machine_esp;
+ m_machine_fp_regnum = k_machine_ebp;
+ m_machine_alt_fp_regnum = k_machine_ebx;
+ m_wordsize = 4;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "eax";
+ m_reg_map[k_machine_eax] = reginfo;
+ reginfo.name = "edx";
+ m_reg_map[k_machine_edx] = reginfo;
+ reginfo.name = "esp";
+ m_reg_map[k_machine_esp] = reginfo;
+ reginfo.name = "esi";
+ m_reg_map[k_machine_esi] = reginfo;
+ reginfo.name = "eip";
+ m_reg_map[k_machine_eip] = reginfo;
+ reginfo.name = "ecx";
+ m_reg_map[k_machine_ecx] = reginfo;
+ reginfo.name = "ebx";
+ m_reg_map[k_machine_ebx] = reginfo;
+ reginfo.name = "ebp";
+ m_reg_map[k_machine_ebp] = reginfo;
+ reginfo.name = "edi";
+ m_reg_map[k_machine_edi] = reginfo;
+ } else {
+ m_machine_ip_regnum = k_machine_rip;
+ m_machine_sp_regnum = k_machine_rsp;
+ m_machine_fp_regnum = k_machine_rbp;
+ m_machine_alt_fp_regnum = k_machine_rbx;
+ m_wordsize = 8;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "rax";
+ m_reg_map[k_machine_rax] = reginfo;
+ reginfo.name = "rdx";
+ m_reg_map[k_machine_rdx] = reginfo;
+ reginfo.name = "rsp";
+ m_reg_map[k_machine_rsp] = reginfo;
+ reginfo.name = "rsi";
+ m_reg_map[k_machine_rsi] = reginfo;
+ reginfo.name = "r8";
+ m_reg_map[k_machine_r8] = reginfo;
+ reginfo.name = "r10";
+ m_reg_map[k_machine_r10] = reginfo;
+ reginfo.name = "r12";
+ m_reg_map[k_machine_r12] = reginfo;
+ reginfo.name = "r14";
+ m_reg_map[k_machine_r14] = reginfo;
+ reginfo.name = "rip";
+ m_reg_map[k_machine_rip] = reginfo;
+ reginfo.name = "rcx";
+ m_reg_map[k_machine_rcx] = reginfo;
+ reginfo.name = "rbx";
+ m_reg_map[k_machine_rbx] = reginfo;
+ reginfo.name = "rbp";
+ m_reg_map[k_machine_rbp] = reginfo;
+ reginfo.name = "rdi";
+ m_reg_map[k_machine_rdi] = reginfo;
+ reginfo.name = "r9";
+ m_reg_map[k_machine_r9] = reginfo;
+ reginfo.name = "r11";
+ m_reg_map[k_machine_r11] = reginfo;
+ reginfo.name = "r13";
+ m_reg_map[k_machine_r13] = reginfo;
+ reginfo.name = "r15";
+ m_reg_map[k_machine_r15] = reginfo;
+ }
+
+ for (MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.begin();
+ it != m_reg_map.end(); ++it) {
+ const RegisterInfo *ri = reg_ctx->GetRegisterInfoByName(it->second.name);
+ if (ri)
+ it->second.lldb_regnum = ri->kinds[eRegisterKindLLDB];
+ }
+
+ uint32_t lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_sp_regnum, lldb_regno))
+ m_lldb_sp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_fp_regnum, lldb_regno))
+ m_lldb_fp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_alt_fp_regnum, lldb_regno))
+ m_lldb_alt_fp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_ip_regnum, lldb_regno))
+ m_lldb_ip_regnum = lldb_regno;
+
+ m_register_map_initialized = true;
+}
+
+void x86AssemblyInspectionEngine::Initialize(
+ std::vector<lldb_reg_info> &reg_info) {
+ m_cpu = k_cpu_unspecified;
+ m_wordsize = -1;
+ m_register_map_initialized = false;
+
+ const llvm::Triple::ArchType cpu = m_arch.GetMachine();
+ if (cpu == llvm::Triple::x86)
+ m_cpu = k_i386;
+ else if (cpu == llvm::Triple::x86_64)
+ m_cpu = k_x86_64;
+
+ if (m_cpu == k_cpu_unspecified)
+ return;
+
+ if (m_cpu == k_i386) {
+ m_machine_ip_regnum = k_machine_eip;
+ m_machine_sp_regnum = k_machine_esp;
+ m_machine_fp_regnum = k_machine_ebp;
+ m_machine_alt_fp_regnum = k_machine_ebx;
+ m_wordsize = 4;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "eax";
+ m_reg_map[k_machine_eax] = reginfo;
+ reginfo.name = "edx";
+ m_reg_map[k_machine_edx] = reginfo;
+ reginfo.name = "esp";
+ m_reg_map[k_machine_esp] = reginfo;
+ reginfo.name = "esi";
+ m_reg_map[k_machine_esi] = reginfo;
+ reginfo.name = "eip";
+ m_reg_map[k_machine_eip] = reginfo;
+ reginfo.name = "ecx";
+ m_reg_map[k_machine_ecx] = reginfo;
+ reginfo.name = "ebx";
+ m_reg_map[k_machine_ebx] = reginfo;
+ reginfo.name = "ebp";
+ m_reg_map[k_machine_ebp] = reginfo;
+ reginfo.name = "edi";
+ m_reg_map[k_machine_edi] = reginfo;
+ } else {
+ m_machine_ip_regnum = k_machine_rip;
+ m_machine_sp_regnum = k_machine_rsp;
+ m_machine_fp_regnum = k_machine_rbp;
+ m_machine_alt_fp_regnum = k_machine_rbx;
+ m_wordsize = 8;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "rax";
+ m_reg_map[k_machine_rax] = reginfo;
+ reginfo.name = "rdx";
+ m_reg_map[k_machine_rdx] = reginfo;
+ reginfo.name = "rsp";
+ m_reg_map[k_machine_rsp] = reginfo;
+ reginfo.name = "rsi";
+ m_reg_map[k_machine_rsi] = reginfo;
+ reginfo.name = "r8";
+ m_reg_map[k_machine_r8] = reginfo;
+ reginfo.name = "r10";
+ m_reg_map[k_machine_r10] = reginfo;
+ reginfo.name = "r12";
+ m_reg_map[k_machine_r12] = reginfo;
+ reginfo.name = "r14";
+ m_reg_map[k_machine_r14] = reginfo;
+ reginfo.name = "rip";
+ m_reg_map[k_machine_rip] = reginfo;
+ reginfo.name = "rcx";
+ m_reg_map[k_machine_rcx] = reginfo;
+ reginfo.name = "rbx";
+ m_reg_map[k_machine_rbx] = reginfo;
+ reginfo.name = "rbp";
+ m_reg_map[k_machine_rbp] = reginfo;
+ reginfo.name = "rdi";
+ m_reg_map[k_machine_rdi] = reginfo;
+ reginfo.name = "r9";
+ m_reg_map[k_machine_r9] = reginfo;
+ reginfo.name = "r11";
+ m_reg_map[k_machine_r11] = reginfo;
+ reginfo.name = "r13";
+ m_reg_map[k_machine_r13] = reginfo;
+ reginfo.name = "r15";
+ m_reg_map[k_machine_r15] = reginfo;
+ }
+
+ for (MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.begin();
+ it != m_reg_map.end(); ++it) {
+ for (size_t i = 0; i < reg_info.size(); ++i) {
+ if (::strcmp(reg_info[i].name, it->second.name) == 0) {
+ it->second.lldb_regnum = reg_info[i].lldb_regnum;
+ break;
+ }
+ }
+ }
+
+ uint32_t lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_sp_regnum, lldb_regno))
+ m_lldb_sp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_fp_regnum, lldb_regno))
+ m_lldb_fp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_alt_fp_regnum, lldb_regno))
+ m_lldb_alt_fp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_ip_regnum, lldb_regno))
+ m_lldb_ip_regnum = lldb_regno;
+
+ m_register_map_initialized = true;
+}
+
+// This function expects an x86 native register number (i.e. the bits stripped
+// out of the actual instruction), not an lldb register number.
+//
+// FIXME: This is ABI dependent, it shouldn't be hardcoded here.
+
+bool x86AssemblyInspectionEngine::nonvolatile_reg_p(int machine_regno) {
+ if (m_cpu == k_i386) {
+ switch (machine_regno) {
+ case k_machine_ebx:
+ case k_machine_ebp: // not actually a nonvolatile but often treated as such
+ // by convention
+ case k_machine_esi:
+ case k_machine_edi:
+ case k_machine_esp:
+ return true;
+ default:
+ return false;
+ }
+ }
+ if (m_cpu == k_x86_64) {
+ switch (machine_regno) {
+ case k_machine_rbx:
+ case k_machine_rsp:
+ case k_machine_rbp: // not actually a nonvolatile but often treated as such
+ // by convention
+ case k_machine_r12:
+ case k_machine_r13:
+ case k_machine_r14:
+ case k_machine_r15:
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+// Macro to detect if this is a REX mode prefix byte.
+#define REX_W_PREFIX_P(opcode) (((opcode) & (~0x5)) == 0x48)
+
+// The high bit which should be added to the source register number (the "R"
+// bit)
+#define REX_W_SRCREG(opcode) (((opcode)&0x4) >> 2)
+
+// The high bit which should be added to the destination register number (the
+// "B" bit)
+#define REX_W_DSTREG(opcode) ((opcode)&0x1)
+
+// pushq %rbp [0x55]
+bool x86AssemblyInspectionEngine::push_rbp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return *p == 0x55;
+}
+
+// pushq $0 ; the first instruction in start() [0x6a 0x00]
+bool x86AssemblyInspectionEngine::push_0_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return *p == 0x6a && *(p + 1) == 0x0;
+}
+
+// pushq $0
+// pushl $0
+bool x86AssemblyInspectionEngine::push_imm_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return *p == 0x68 || *p == 0x6a;
+}
+
+// pushl imm8(%esp)
+//
+// e.g. 0xff 0x74 0x24 0x20 - 'pushl 0x20(%esp)' (same byte pattern for 'pushq
+// 0x20(%rsp)' in an x86_64 program)
+//
+// 0xff (with opcode bits '6' in next byte, PUSH r/m32) 0x74 (ModR/M byte with
+// three bits used to specify the opcode)
+// mod == b01, opcode == b110, R/M == b100
+// "+disp8"
+// 0x24 (SIB byte - scaled index = 0, r32 == esp) 0x20 imm8 value
+
+bool x86AssemblyInspectionEngine::push_extended_pattern_p() {
+ if (*m_cur_insn == 0xff) {
+ // Get the 3 opcode bits from the ModR/M byte
+ uint8_t opcode = (*(m_cur_insn + 1) >> 3) & 7;
+ if (opcode == 6) {
+ // I'm only looking for 0xff /6 here - I
+ // don't really care what value is being pushed, just that we're pushing
+ // a 32/64 bit value on to the stack is enough.
+ return true;
+ }
+ }
+ return false;
+}
+
+// instructions only valid in 32-bit mode:
+// 0x0e - push cs
+// 0x16 - push ss
+// 0x1e - push ds
+// 0x06 - push es
+bool x86AssemblyInspectionEngine::push_misc_reg_p() {
+ uint8_t p = *m_cur_insn;
+ if (m_wordsize == 4) {
+ if (p == 0x0e || p == 0x16 || p == 0x1e || p == 0x06)
+ return true;
+ }
+ return false;
+}
+
+// pushq %rbx
+// pushl %ebx
+bool x86AssemblyInspectionEngine::push_reg_p(int &regno) {
+ uint8_t *p = m_cur_insn;
+ int regno_prefix_bit = 0;
+ // If we have a rex prefix byte, check to see if a B bit is set
+ if (m_wordsize == 8 && (*p & 0xfe) == 0x40) {
+ regno_prefix_bit = (*p & 1) << 3;
+ p++;
+ }
+ if (*p >= 0x50 && *p <= 0x57) {
+ regno = (*p - 0x50) | regno_prefix_bit;
+ return true;
+ }
+ return false;
+}
+
+// movq %rsp, %rbp [0x48 0x8b 0xec] or [0x48 0x89 0xe5] movl %esp, %ebp [0x8b
+// 0xec] or [0x89 0xe5]
+bool x86AssemblyInspectionEngine::mov_rsp_rbp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ if (*(p) == 0x8b && *(p + 1) == 0xec)
+ return true;
+ if (*(p) == 0x89 && *(p + 1) == 0xe5)
+ return true;
+ return false;
+}
+
+// movq %rsp, %rbx [0x48 0x8b 0xdc] or [0x48 0x89 0xe3]
+// movl %esp, %ebx [0x8b 0xdc] or [0x89 0xe3]
+bool x86AssemblyInspectionEngine::mov_rsp_rbx_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ if (*(p) == 0x8b && *(p + 1) == 0xdc)
+ return true;
+ if (*(p) == 0x89 && *(p + 1) == 0xe3)
+ return true;
+ return false;
+}
+
+// movq %rbp, %rsp [0x48 0x8b 0xe5] or [0x48 0x89 0xec]
+// movl %ebp, %esp [0x8b 0xe5] or [0x89 0xec]
+bool x86AssemblyInspectionEngine::mov_rbp_rsp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ if (*(p) == 0x8b && *(p + 1) == 0xe5)
+ return true;
+ if (*(p) == 0x89 && *(p + 1) == 0xec)
+ return true;
+ return false;
+}
+
+// movq %rbx, %rsp [0x48 0x8b 0xe3] or [0x48 0x89 0xdc]
+// movl %ebx, %esp [0x8b 0xe3] or [0x89 0xdc]
+bool x86AssemblyInspectionEngine::mov_rbx_rsp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ if (*(p) == 0x8b && *(p + 1) == 0xe3)
+ return true;
+ if (*(p) == 0x89 && *(p + 1) == 0xdc)
+ return true;
+ return false;
+}
+
+// subq $0x20, %rsp
+bool x86AssemblyInspectionEngine::sub_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ // 8-bit immediate operand
+ if (*p == 0x83 && *(p + 1) == 0xec) {
+ amount = (int8_t) * (p + 2);
+ return true;
+ }
+ // 32-bit immediate operand
+ if (*p == 0x81 && *(p + 1) == 0xec) {
+ amount = (int32_t)extract_4(p + 2);
+ return true;
+ }
+ return false;
+}
+
+// addq $0x20, %rsp
+bool x86AssemblyInspectionEngine::add_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ // 8-bit immediate operand
+ if (*p == 0x83 && *(p + 1) == 0xc4) {
+ amount = (int8_t) * (p + 2);
+ return true;
+ }
+ // 32-bit immediate operand
+ if (*p == 0x81 && *(p + 1) == 0xc4) {
+ amount = (int32_t)extract_4(p + 2);
+ return true;
+ }
+ return false;
+}
+
+// lea esp, [esp - 0x28]
+// lea esp, [esp + 0x28]
+bool x86AssemblyInspectionEngine::lea_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+
+ // Check opcode
+ if (*p != 0x8d)
+ return false;
+
+ // 8 bit displacement
+ if (*(p + 1) == 0x64 && (*(p + 2) & 0x3f) == 0x24) {
+ amount = (int8_t) * (p + 3);
+ return true;
+ }
+
+ // 32 bit displacement
+ if (*(p + 1) == 0xa4 && (*(p + 2) & 0x3f) == 0x24) {
+ amount = (int32_t)extract_4(p + 3);
+ return true;
+ }
+
+ return false;
+}
+
+// lea -0x28(%ebp), %esp
+// (32-bit and 64-bit variants, 8-bit and 32-bit displacement)
+bool x86AssemblyInspectionEngine::lea_rbp_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+
+ // Check opcode
+ if (*p != 0x8d)
+ return false;
+ ++p;
+
+ // 8 bit displacement
+ if (*p == 0x65) {
+ amount = (int8_t)p[1];
+ return true;
+ }
+
+ // 32 bit displacement
+ if (*p == 0xa5) {
+ amount = (int32_t)extract_4(p + 1);
+ return true;
+ }
+
+ return false;
+}
+
+// lea -0x28(%ebx), %esp
+// (32-bit and 64-bit variants, 8-bit and 32-bit displacement)
+bool x86AssemblyInspectionEngine::lea_rbx_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+
+ // Check opcode
+ if (*p != 0x8d)
+ return false;
+ ++p;
+
+ // 8 bit displacement
+ if (*p == 0x63) {
+ amount = (int8_t)p[1];
+ return true;
+ }
+
+ // 32 bit displacement
+ if (*p == 0xa3) {
+ amount = (int32_t)extract_4(p + 1);
+ return true;
+ }
+
+ return false;
+}
+
+// and -0xfffffff0, %esp
+// (32-bit and 64-bit variants, 8-bit and 32-bit displacement)
+bool x86AssemblyInspectionEngine::and_rsp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+
+ if (*p != 0x81 && *p != 0x83)
+ return false;
+
+ return *++p == 0xe4;
+}
+
+// popq %rbx
+// popl %ebx
+bool x86AssemblyInspectionEngine::pop_reg_p(int &regno) {
+ uint8_t *p = m_cur_insn;
+ int regno_prefix_bit = 0;
+ // If we have a rex prefix byte, check to see if a B bit is set
+ if (m_wordsize == 8 && (*p & 0xfe) == 0x40) {
+ regno_prefix_bit = (*p & 1) << 3;
+ p++;
+ }
+ if (*p >= 0x58 && *p <= 0x5f) {
+ regno = (*p - 0x58) | regno_prefix_bit;
+ return true;
+ }
+ return false;
+}
+
+// popq %rbp [0x5d]
+// popl %ebp [0x5d]
+bool x86AssemblyInspectionEngine::pop_rbp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return (*p == 0x5d);
+}
+
+// instructions valid only in 32-bit mode:
+// 0x1f - pop ds
+// 0x07 - pop es
+// 0x17 - pop ss
+bool x86AssemblyInspectionEngine::pop_misc_reg_p() {
+ uint8_t p = *m_cur_insn;
+ if (m_wordsize == 4) {
+ if (p == 0x1f || p == 0x07 || p == 0x17)
+ return true;
+ }
+ return false;
+}
+
+// leave [0xc9]
+bool x86AssemblyInspectionEngine::leave_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return (*p == 0xc9);
+}
+
+// call $0 [0xe8 0x0 0x0 0x0 0x0]
+bool x86AssemblyInspectionEngine::call_next_insn_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return (*p == 0xe8) && (*(p + 1) == 0x0) && (*(p + 2) == 0x0) &&
+ (*(p + 3) == 0x0) && (*(p + 4) == 0x0);
+}
+
+// Look for an instruction sequence storing a nonvolatile register on to the
+// stack frame.
+
+// movq %rax, -0x10(%rbp) [0x48 0x89 0x45 0xf0]
+// movl %eax, -0xc(%ebp) [0x89 0x45 0xf4]
+
+// The offset value returned in rbp_offset will be positive -- but it must be
+// subtraced from the frame base register to get the actual location. The
+// positive value returned for the offset is a convention used elsewhere for
+// CFA offsets et al.
+
+bool x86AssemblyInspectionEngine::mov_reg_to_local_stack_frame_p(
+ int &regno, int &rbp_offset) {
+ uint8_t *p = m_cur_insn;
+ int src_reg_prefix_bit = 0;
+ int target_reg_prefix_bit = 0;
+
+ if (m_wordsize == 8 && REX_W_PREFIX_P(*p)) {
+ src_reg_prefix_bit = REX_W_SRCREG(*p) << 3;
+ target_reg_prefix_bit = REX_W_DSTREG(*p) << 3;
+ if (target_reg_prefix_bit == 1) {
+ // rbp/ebp don't need a prefix bit - we know this isn't the reg we care
+ // about.
+ return false;
+ }
+ p++;
+ }
+
+ if (*p == 0x89) {
+ /* Mask off the 3-5 bits which indicate the destination register
+ if this is a ModR/M byte. */
+ int opcode_destreg_masked_out = *(p + 1) & (~0x38);
+
+ /* Is this a ModR/M byte with Mod bits 01 and R/M bits 101
+ and three bits between them, e.g. 01nnn101
+ We're looking for a destination of ebp-disp8 or ebp-disp32. */
+ int immsize;
+ if (opcode_destreg_masked_out == 0x45)
+ immsize = 2;
+ else if (opcode_destreg_masked_out == 0x85)
+ immsize = 4;
+ else
+ return false;
+
+ int offset = 0;
+ if (immsize == 2)
+ offset = (int8_t) * (p + 2);
+ if (immsize == 4)
+ offset = (uint32_t)extract_4(p + 2);
+ if (offset > 0)
+ return false;
+
+ regno = ((*(p + 1) >> 3) & 0x7) | src_reg_prefix_bit;
+ rbp_offset = offset > 0 ? offset : -offset;
+ return true;
+ }
+ return false;
+}
+
+// Returns true if this is a jmp instruction where we can't
+// know the destination address statically.
+//
+// ff e0 jmpq *%rax
+// ff e1 jmpq *%rcx
+// ff 60 28 jmpq *0x28(%rax)
+// ff 60 60 jmpq *0x60(%rax)
+bool x86AssemblyInspectionEngine::jmp_to_reg_p() {
+ if (*m_cur_insn != 0xff)
+ return false;
+
+ // The second byte is a ModR/M /4 byte, strip off the registers
+ uint8_t second_byte_sans_reg = *(m_cur_insn + 1) & ~7;
+
+ // Don't handle 0x24 disp32, because the target address is
+ // knowable statically - pc_rel_branch_or_jump_p() will
+ // return the target address.
+
+ // [reg]
+ if (second_byte_sans_reg == 0x20)
+ return true;
+
+ // [reg]+disp8
+ if (second_byte_sans_reg == 0x60)
+ return true;
+
+ // [reg]+disp32
+ if (second_byte_sans_reg == 0xa0)
+ return true;
+
+ // reg
+ if (second_byte_sans_reg == 0xe0)
+ return true;
+
+ // disp32
+ // jumps to an address stored in memory, the value can't be cached
+ // in an unwind plan.
+ if (second_byte_sans_reg == 0x24)
+ return true;
+
+ // use SIB byte
+ // ff 24 fe jmpq *(%rsi,%rdi,8)
+ if (second_byte_sans_reg == 0x24)
+ return true;
+
+ return false;
+}
+
+// Detect branches to fixed pc-relative offsets.
+// Returns the offset from the address of the next instruction
+// that may be branch/jumped to.
+//
+// Cannot determine the offset of a JMP that jumps to the address in
+// a register ("jmpq *%rax") or offset from a register value
+// ("jmpq *0x28(%rax)"), this method will return false on those
+// instructions.
+//
+// These instructions all end in either a relative 8/16/32 bit value
+// depending on the instruction and the current execution mode of the
+// inferior process. Once we know the size of the opcode instruction,
+// we can use the total instruction length to determine the size of
+// the relative offset without having to compute it correctly.
+
+bool x86AssemblyInspectionEngine::pc_rel_branch_or_jump_p (
+ const int instruction_length, int &offset)
+{
+ int opcode_size = 0;
+
+ uint8_t b1 = m_cur_insn[0];
+
+ switch (b1) {
+ case 0x77: // JA/JNBE rel8
+ case 0x73: // JAE/JNB/JNC rel8
+ case 0x72: // JB/JC/JNAE rel8
+ case 0x76: // JBE/JNA rel8
+ case 0xe3: // JCXZ/JECXZ/JRCXZ rel8
+ case 0x74: // JE/JZ rel8
+ case 0x7f: // JG/JNLE rel8
+ case 0x7d: // JGE/JNL rel8
+ case 0x7c: // JL/JNGE rel8
+ case 0x7e: // JNG/JLE rel8
+ case 0x71: // JNO rel8
+ case 0x7b: // JNP/JPO rel8
+ case 0x79: // JNS rel8
+ case 0x75: // JNE/JNZ rel8
+ case 0x70: // JO rel8
+ case 0x7a: // JP/JPE rel8
+ case 0x78: // JS rel8
+ case 0xeb: // JMP rel8
+ case 0xe9: // JMP rel16/rel32
+ opcode_size = 1;
+ break;
+ default:
+ break;
+ }
+ if (b1 == 0x0f && opcode_size == 0) {
+ uint8_t b2 = m_cur_insn[1];
+ switch (b2) {
+ case 0x87: // JA/JNBE rel16/rel32
+ case 0x86: // JBE/JNA rel16/rel32
+ case 0x84: // JE/JZ rel16/rel32
+ case 0x8f: // JG/JNLE rel16/rel32
+ case 0x8d: // JNL/JGE rel16/rel32
+ case 0x8e: // JLE rel16/rel32
+ case 0x82: // JB/JC/JNAE rel16/rel32
+ case 0x83: // JAE/JNB/JNC rel16/rel32
+ case 0x85: // JNE/JNZ rel16/rel32
+ case 0x8c: // JL/JNGE rel16/rel32
+ case 0x81: // JNO rel16/rel32
+ case 0x8b: // JNP/JPO rel16/rel32
+ case 0x89: // JNS rel16/rel32
+ case 0x80: // JO rel16/rel32
+ case 0x8a: // JP rel16/rel32
+ case 0x88: // JS rel16/rel32
+ opcode_size = 2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (opcode_size == 0)
+ return false;
+
+ offset = 0;
+ if (instruction_length - opcode_size == 1) {
+ int8_t rel8 = (int8_t) *(m_cur_insn + opcode_size);
+ offset = rel8;
+ } else if (instruction_length - opcode_size == 2) {
+ int16_t rel16 = extract_2_signed (m_cur_insn + opcode_size);
+ offset = rel16;
+ } else if (instruction_length - opcode_size == 4) {
+ int32_t rel32 = extract_4_signed (m_cur_insn + opcode_size);
+ offset = rel32;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+// Returns true if this instruction is a intra-function branch or jump -
+// a branch/jump within the bounds of this same function.
+// Cannot predict where a jump through a register value ("jmpq *%rax")
+// will go, so it will return false on that instruction.
+bool x86AssemblyInspectionEngine::local_branch_p (
+ const addr_t current_func_text_offset,
+ const AddressRange &func_range,
+ const int instruction_length,
+ addr_t &target_insn_offset) {
+ int offset;
+ if (pc_rel_branch_or_jump_p (instruction_length, offset) && offset != 0) {
+ addr_t next_pc_value = current_func_text_offset + instruction_length;
+ if (offset < 0 && addr_t(-offset) > current_func_text_offset) {
+ // Branch target is before the start of this function
+ return false;
+ }
+ if (offset + next_pc_value > func_range.GetByteSize()) {
+ // Branch targets outside this function's bounds
+ return false;
+ }
+ // This instruction branches to target_insn_offset (byte offset into the function)
+ target_insn_offset = next_pc_value + offset;
+ return true;
+ }
+ return false;
+}
+
+// Returns true if this instruction is a inter-function branch or jump - a
+// branch/jump to another function.
+// Cannot predict where a jump through a register value ("jmpq *%rax")
+// will go, so it will return false on that instruction.
+bool x86AssemblyInspectionEngine::non_local_branch_p (
+ const addr_t current_func_text_offset,
+ const AddressRange &func_range,
+ const int instruction_length) {
+ int offset;
+ addr_t target_insn_offset;
+ if (pc_rel_branch_or_jump_p (instruction_length, offset)) {
+ return !local_branch_p(current_func_text_offset,func_range,instruction_length,target_insn_offset);
+ }
+ return false;
+}
+
+// ret [0xc3] or [0xcb] or [0xc2 imm16] or [0xca imm16]
+bool x86AssemblyInspectionEngine::ret_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return *p == 0xc3 || *p == 0xc2 || *p == 0xca || *p == 0xcb;
+}
+
+uint16_t x86AssemblyInspectionEngine::extract_2(uint8_t *b) {
+ uint16_t v = 0;
+ for (int i = 1; i >= 0; i--)
+ v = (v << 8) | b[i];
+ return v;
+}
+
+int16_t x86AssemblyInspectionEngine::extract_2_signed(uint8_t *b) {
+ int16_t v = 0;
+ for (int i = 1; i >= 0; i--)
+ v = (v << 8) | b[i];
+ return v;
+}
+
+uint32_t x86AssemblyInspectionEngine::extract_4(uint8_t *b) {
+ uint32_t v = 0;
+ for (int i = 3; i >= 0; i--)
+ v = (v << 8) | b[i];
+ return v;
+}
+
+int32_t x86AssemblyInspectionEngine::extract_4_signed(uint8_t *b) {
+ int32_t v = 0;
+ for (int i = 3; i >= 0; i--)
+ v = (v << 8) | b[i];
+ return v;
+}
+
+
+bool x86AssemblyInspectionEngine::instruction_length(uint8_t *insn_p,
+ int &length,
+ uint32_t buffer_remaining_bytes) {
+
+ uint32_t max_op_byte_size = std::min(buffer_remaining_bytes, m_arch.GetMaximumOpcodeByteSize());
+ llvm::SmallVector<uint8_t, 32> opcode_data;
+ opcode_data.resize(max_op_byte_size);
+
+ char out_string[512];
+ const size_t inst_size =
+ ::LLVMDisasmInstruction(m_disasm_context, insn_p, max_op_byte_size, 0,
+ out_string, sizeof(out_string));
+
+ length = inst_size;
+ return true;
+}
+
+bool x86AssemblyInspectionEngine::machine_regno_to_lldb_regno(
+ int machine_regno, uint32_t &lldb_regno) {
+ MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.find(machine_regno);
+ if (it != m_reg_map.end()) {
+ lldb_regno = it->second.lldb_regnum;
+ return true;
+ }
+ return false;
+}
+
+bool x86AssemblyInspectionEngine::GetNonCallSiteUnwindPlanFromAssembly(
+ uint8_t *data, size_t size, AddressRange &func_range,
+ UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+
+ if (data == nullptr || size == 0)
+ return false;
+
+ if (!m_register_map_initialized)
+ return false;
+
+ addr_t current_func_text_offset = 0;
+ int current_sp_bytes_offset_from_fa = 0;
+ bool is_aligned = false;
+ UnwindPlan::Row::RegisterLocation initial_regloc;
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ unwind_plan.SetPlanValidAddressRange(func_range);
+ unwind_plan.SetRegisterKind(eRegisterKindLLDB);
+
+ // At the start of the function, find the CFA by adding wordsize to the SP
+ // register
+ row->SetOffset(current_func_text_offset);
+ row->GetCFAValue().SetIsRegisterPlusOffset(m_lldb_sp_regnum, m_wordsize);
+
+ // caller's stack pointer value before the call insn is the CFA address
+ initial_regloc.SetIsCFAPlusOffset(0);
+ row->SetRegisterInfo(m_lldb_sp_regnum, initial_regloc);
+
+ // saved instruction pointer can be found at CFA - wordsize.
+ current_sp_bytes_offset_from_fa = m_wordsize;
+ initial_regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_fa);
+ row->SetRegisterInfo(m_lldb_ip_regnum, initial_regloc);
+
+ unwind_plan.AppendRow(row);
+
+ // Allocate a new Row, populate it with the existing Row contents.
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+
+ // Track which registers have been saved so far in the prologue. If we see
+ // another push of that register, it's not part of the prologue. The register
+ // numbers used here are the machine register #'s (i386_register_numbers,
+ // x86_64_register_numbers).
+ std::vector<bool> saved_registers(32, false);
+
+ // Once the prologue has completed we'll save a copy of the unwind
+ // instructions If there is an epilogue in the middle of the function, after
+ // that epilogue we'll reinstate the unwind setup -- we assume that some code
+ // path jumps over the mid-function epilogue
+
+ UnwindPlan::RowSP prologue_completed_row; // copy of prologue row of CFI
+ int prologue_completed_sp_bytes_offset_from_cfa; // The sp value before the
+ // epilogue started executed
+ bool prologue_completed_is_aligned;
+ std::vector<bool> prologue_completed_saved_registers;
+
+ while (current_func_text_offset < size) {
+ int stack_offset, insn_len;
+ int machine_regno; // register numbers masked directly out of instructions
+ uint32_t lldb_regno; // register numbers in lldb's eRegisterKindLLDB
+ // numbering scheme
+
+ bool in_epilogue = false; // we're in the middle of an epilogue sequence
+ bool row_updated = false; // The UnwindPlan::Row 'row' has been updated
+
+ m_cur_insn = data + current_func_text_offset;
+ if (!instruction_length(m_cur_insn, insn_len, size - current_func_text_offset)
+ || insn_len == 0
+ || insn_len > kMaxInstructionByteSize) {
+ // An unrecognized/junk instruction
+ break;
+ }
+
+ auto &cfa_value = row->GetCFAValue();
+ auto &afa_value = row->GetAFAValue();
+ auto fa_value_ptr = is_aligned ? &afa_value : &cfa_value;
+
+ if (mov_rsp_rbp_pattern_p()) {
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_fp_regnum, fa_value_ptr->GetOffset());
+ row_updated = true;
+ }
+ }
+
+ else if (mov_rsp_rbx_pattern_p()) {
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_alt_fp_regnum, fa_value_ptr->GetOffset());
+ row_updated = true;
+ }
+ }
+
+ else if (and_rsp_pattern_p()) {
+ current_sp_bytes_offset_from_fa = 0;
+ afa_value.SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, current_sp_bytes_offset_from_fa);
+ fa_value_ptr = &afa_value;
+ is_aligned = true;
+ row_updated = true;
+ }
+
+ else if (mov_rbp_rsp_pattern_p()) {
+ if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_fp_regnum)
+ {
+ is_aligned = false;
+ fa_value_ptr = &cfa_value;
+ afa_value.SetUnspecified();
+ row_updated = true;
+ }
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum)
+ current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset();
+ }
+
+ else if (mov_rbx_rsp_pattern_p()) {
+ if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_alt_fp_regnum)
+ {
+ is_aligned = false;
+ fa_value_ptr = &cfa_value;
+ afa_value.SetUnspecified();
+ row_updated = true;
+ }
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_alt_fp_regnum)
+ current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset();
+ }
+
+ // This is the start() function (or a pthread equivalent), it starts with a
+ // pushl $0x0 which puts the saved pc value of 0 on the stack. In this
+ // case we want to pretend we didn't see a stack movement at all --
+ // normally the saved pc value is already on the stack by the time the
+ // function starts executing.
+ else if (push_0_pattern_p()) {
+ }
+
+ else if (push_reg_p(machine_regno)) {
+ current_sp_bytes_offset_from_fa += m_wordsize;
+ // the PUSH instruction has moved the stack pointer - if the FA is set
+ // in terms of the stack pointer, we need to add a new row of
+ // instructions.
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ // record where non-volatile (callee-saved, spilled) registers are saved
+ // on the stack
+ if (nonvolatile_reg_p(machine_regno) &&
+ machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
+ !saved_registers[machine_regno]) {
+ UnwindPlan::Row::RegisterLocation regloc;
+ if (is_aligned)
+ regloc.SetAtAFAPlusOffset(-current_sp_bytes_offset_from_fa);
+ else
+ regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_fa);
+ row->SetRegisterInfo(lldb_regno, regloc);
+ saved_registers[machine_regno] = true;
+ row_updated = true;
+ }
+ }
+
+ else if (pop_reg_p(machine_regno)) {
+ current_sp_bytes_offset_from_fa -= m_wordsize;
+
+ if (nonvolatile_reg_p(machine_regno) &&
+ machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
+ saved_registers[machine_regno]) {
+ saved_registers[machine_regno] = false;
+ row->RemoveRegisterInfo(lldb_regno);
+
+ if (lldb_regno == fa_value_ptr->GetRegisterNumber()) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, fa_value_ptr->GetOffset());
+ }
+
+ in_epilogue = true;
+ row_updated = true;
+ }
+
+ // the POP instruction has moved the stack pointer - if the FA is set in
+ // terms of the stack pointer, we need to add a new row of instructions.
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ }
+
+ else if (pop_misc_reg_p()) {
+ current_sp_bytes_offset_from_fa -= m_wordsize;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ }
+
+ // The LEAVE instruction moves the value from rbp into rsp and pops a value
+ // off the stack into rbp (restoring the caller's rbp value). It is the
+ // opposite of ENTER, or 'push rbp, mov rsp rbp'.
+ else if (leave_pattern_p()) {
+ if (saved_registers[m_machine_fp_regnum]) {
+ saved_registers[m_machine_fp_regnum] = false;
+ row->RemoveRegisterInfo(m_lldb_fp_regnum);
+
+ row_updated = true;
+ }
+
+ if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_fp_regnum)
+ {
+ is_aligned = false;
+ fa_value_ptr = &cfa_value;
+ afa_value.SetUnspecified();
+ row_updated = true;
+ }
+
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum)
+ {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, fa_value_ptr->GetOffset());
+
+ current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset();
+ }
+
+ current_sp_bytes_offset_from_fa -= m_wordsize;
+
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+
+ in_epilogue = true;
+ }
+
+ else if (mov_reg_to_local_stack_frame_p(machine_regno, stack_offset) &&
+ nonvolatile_reg_p(machine_regno) &&
+ machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
+ !saved_registers[machine_regno]) {
+ saved_registers[machine_regno] = true;
+
+ UnwindPlan::Row::RegisterLocation regloc;
+
+ // stack_offset for 'movq %r15, -80(%rbp)' will be 80. In the Row, we
+ // want to express this as the offset from the FA. If the frame base is
+ // rbp (like the above instruction), the FA offset for rbp is probably
+ // 16. So we want to say that the value is stored at the FA address -
+ // 96.
+ if (is_aligned)
+ regloc.SetAtAFAPlusOffset(-(stack_offset + fa_value_ptr->GetOffset()));
+ else
+ regloc.SetAtCFAPlusOffset(-(stack_offset + fa_value_ptr->GetOffset()));
+
+ row->SetRegisterInfo(lldb_regno, regloc);
+
+ row_updated = true;
+ }
+
+ else if (sub_rsp_pattern_p(stack_offset)) {
+ current_sp_bytes_offset_from_fa += stack_offset;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ }
+
+ else if (add_rsp_pattern_p(stack_offset)) {
+ current_sp_bytes_offset_from_fa -= stack_offset;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ in_epilogue = true;
+ }
+
+ else if (push_extended_pattern_p() || push_imm_pattern_p() ||
+ push_misc_reg_p()) {
+ current_sp_bytes_offset_from_fa += m_wordsize;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ }
+
+ else if (lea_rsp_pattern_p(stack_offset)) {
+ current_sp_bytes_offset_from_fa -= stack_offset;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ if (stack_offset > 0)
+ in_epilogue = true;
+ }
+
+ else if (lea_rbp_rsp_pattern_p(stack_offset)) {
+ if (is_aligned &&
+ cfa_value.GetRegisterNumber() == m_lldb_fp_regnum) {
+ is_aligned = false;
+ fa_value_ptr = &cfa_value;
+ afa_value.SetUnspecified();
+ row_updated = true;
+ }
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum) {
+ current_sp_bytes_offset_from_fa =
+ fa_value_ptr->GetOffset() - stack_offset;
+ }
+ }
+
+ else if (lea_rbx_rsp_pattern_p(stack_offset)) {
+ if (is_aligned &&
+ cfa_value.GetRegisterNumber() == m_lldb_alt_fp_regnum) {
+ is_aligned = false;
+ fa_value_ptr = &cfa_value;
+ afa_value.SetUnspecified();
+ row_updated = true;
+ }
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_alt_fp_regnum) {
+ current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset() - stack_offset;
+ }
+ }
+
+ else if (prologue_completed_row.get() &&
+ (ret_pattern_p() ||
+ non_local_branch_p (current_func_text_offset, func_range, insn_len) ||
+ jmp_to_reg_p())) {
+ // Check if the current instruction is the end of an epilogue sequence,
+ // and if so, re-instate the prologue-completed unwind state.
+
+ // The current instruction is a branch/jump outside this function,
+ // a ret, or a jump through a register value which we cannot
+ // determine the effcts of. Verify that the stack frame state
+ // has been unwound to the same as it was at function entry to avoid
+ // mis-identifying a JMP instruction as an epilogue.
+ UnwindPlan::Row::RegisterLocation sp, pc;
+ if (row->GetRegisterInfo(m_lldb_sp_regnum, sp) &&
+ row->GetRegisterInfo(m_lldb_ip_regnum, pc)) {
+ // Any ret instruction variant is definitely indicative of an
+ // epilogue; for other insn patterns verify that we're back to
+ // the original unwind state.
+ if (ret_pattern_p() ||
+ (sp.IsCFAPlusOffset() && sp.GetOffset() == 0 &&
+ pc.IsAtCFAPlusOffset() && pc.GetOffset() == -m_wordsize)) {
+ // Reinstate the saved prologue setup for any instructions that come
+ // after the epilogue
+
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *prologue_completed_row.get();
+ row.reset(newrow);
+ current_sp_bytes_offset_from_fa =
+ prologue_completed_sp_bytes_offset_from_cfa;
+ is_aligned = prologue_completed_is_aligned;
+
+ saved_registers.clear();
+ saved_registers.resize(prologue_completed_saved_registers.size(), false);
+ for (size_t i = 0; i < prologue_completed_saved_registers.size(); ++i) {
+ saved_registers[i] = prologue_completed_saved_registers[i];
+ }
+
+ in_epilogue = true;
+ row_updated = true;
+ }
+ }
+ }
+
+ // call next instruction
+ // call 0
+ // => pop %ebx
+ // This is used in i386 programs to get the PIC base address for finding
+ // global data
+ else if (call_next_insn_pattern_p()) {
+ current_sp_bytes_offset_from_fa += m_wordsize;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ }
+
+ if (row_updated) {
+ if (current_func_text_offset + insn_len < size) {
+ row->SetOffset(current_func_text_offset + insn_len);
+ unwind_plan.AppendRow(row);
+ // Allocate a new Row, populate it with the existing Row contents.
+ newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+ }
+ }
+
+ if (!in_epilogue && row_updated) {
+ // If we're not in an epilogue sequence, save the updated Row
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ prologue_completed_row.reset(newrow);
+
+ prologue_completed_saved_registers.clear();
+ prologue_completed_saved_registers.resize(saved_registers.size(), false);
+ for (size_t i = 0; i < saved_registers.size(); ++i) {
+ prologue_completed_saved_registers[i] = saved_registers[i];
+ }
+ }
+
+ // We may change the sp value without adding a new Row necessarily -- keep
+ // track of it either way.
+ if (!in_epilogue) {
+ prologue_completed_sp_bytes_offset_from_cfa =
+ current_sp_bytes_offset_from_fa;
+ prologue_completed_is_aligned = is_aligned;
+ }
+
+ m_cur_insn = m_cur_insn + insn_len;
+ current_func_text_offset += insn_len;
+ }
+
+ unwind_plan.SetSourceName("assembly insn profiling");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+
+ return true;
+}
+
+bool x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite(
+ uint8_t *data, size_t size, AddressRange &func_range,
+ UnwindPlan &unwind_plan, RegisterContextSP &reg_ctx) {
+ Address addr_start = func_range.GetBaseAddress();
+ if (!addr_start.IsValid())
+ return false;
+
+ // We either need a live RegisterContext, or we need the UnwindPlan to
+ // already be in the lldb register numbering scheme.
+ if (reg_ctx.get() == nullptr &&
+ unwind_plan.GetRegisterKind() != eRegisterKindLLDB)
+ return false;
+
+ // Is original unwind_plan valid?
+ // unwind_plan should have at least one row which is ABI-default (CFA
+ // register is sp), and another row in mid-function.
+ if (unwind_plan.GetRowCount() < 2)
+ return false;
+
+ UnwindPlan::RowSP first_row = unwind_plan.GetRowAtIndex(0);
+ if (first_row->GetOffset() != 0)
+ return false;
+ uint32_t cfa_reg = first_row->GetCFAValue().GetRegisterNumber();
+ if (unwind_plan.GetRegisterKind() != eRegisterKindLLDB) {
+ cfa_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ unwind_plan.GetRegisterKind(),
+ first_row->GetCFAValue().GetRegisterNumber());
+ }
+ if (cfa_reg != m_lldb_sp_regnum ||
+ first_row->GetCFAValue().GetOffset() != m_wordsize)
+ return false;
+
+ UnwindPlan::RowSP original_last_row = unwind_plan.GetRowForFunctionOffset(-1);
+
+ size_t offset = 0;
+ int row_id = 1;
+ bool unwind_plan_updated = false;
+ UnwindPlan::RowSP row(new UnwindPlan::Row(*first_row));
+ m_cur_insn = data + offset;
+
+ // After a mid-function epilogue we will need to re-insert the original
+ // unwind rules so unwinds work for the remainder of the function. These
+ // aren't common with clang/gcc on x86 but it is possible.
+ bool reinstate_unwind_state = false;
+
+ while (offset < size) {
+ m_cur_insn = data + offset;
+ int insn_len;
+ if (!instruction_length(m_cur_insn, insn_len, size - offset)
+ || insn_len == 0
+ || insn_len > kMaxInstructionByteSize) {
+ // An unrecognized/junk instruction.
+ break;
+ }
+
+ // Advance offsets.
+ offset += insn_len;
+ m_cur_insn = data + offset;
+
+ // offset is pointing beyond the bounds of the function; stop looping.
+ if (offset >= size)
+ continue;
+
+ if (reinstate_unwind_state) {
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row());
+ *new_row = *original_last_row;
+ new_row->SetOffset(offset);
+ unwind_plan.AppendRow(new_row);
+ row = std::make_shared<UnwindPlan::Row>();
+ *row = *new_row;
+ reinstate_unwind_state = false;
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // If we already have one row for this instruction, we can continue.
+ while (row_id < unwind_plan.GetRowCount() &&
+ unwind_plan.GetRowAtIndex(row_id)->GetOffset() <= offset) {
+ row_id++;
+ }
+ UnwindPlan::RowSP original_row = unwind_plan.GetRowAtIndex(row_id - 1);
+ if (original_row->GetOffset() == offset) {
+ *row = *original_row;
+ continue;
+ }
+
+ if (row_id == 0) {
+ // If we are here, compiler didn't generate CFI for prologue. This won't
+ // happen to GCC or clang. In this case, bail out directly.
+ return false;
+ }
+
+ // Inspect the instruction to check if we need a new row for it.
+ cfa_reg = row->GetCFAValue().GetRegisterNumber();
+ if (unwind_plan.GetRegisterKind() != eRegisterKindLLDB) {
+ cfa_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ unwind_plan.GetRegisterKind(),
+ row->GetCFAValue().GetRegisterNumber());
+ }
+ if (cfa_reg == m_lldb_sp_regnum) {
+ // CFA register is sp.
+
+ // call next instruction
+ // call 0
+ // => pop %ebx
+ if (call_next_insn_pattern_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // push/pop register
+ int regno;
+ if (push_reg_p(regno)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+ if (pop_reg_p(regno)) {
+ // Technically, this might be a nonvolatile register recover in
+ // epilogue. We should reset RegisterInfo for the register. But in
+ // practice, previous rule for the register is still valid... So we
+ // ignore this case.
+
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ if (pop_misc_reg_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // push imm
+ if (push_imm_pattern_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // push extended
+ if (push_extended_pattern_p() || push_misc_reg_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // add/sub %rsp/%esp
+ int amount;
+ if (add_rsp_pattern_p(amount)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-amount);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+ if (sub_rsp_pattern_p(amount)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(amount);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // lea %rsp, [%rsp + $offset]
+ if (lea_rsp_pattern_p(amount)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-amount);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ if (ret_pattern_p()) {
+ reinstate_unwind_state = true;
+ continue;
+ }
+ } else if (cfa_reg == m_lldb_fp_regnum) {
+ // CFA register is fp.
+
+ // The only case we care about is epilogue:
+ // [0x5d] pop %rbp/%ebp
+ // => [0xc3] ret
+ if (pop_rbp_pattern_p() || leave_pattern_p()) {
+ offset += 1;
+ row->SetOffset(offset);
+ row->GetCFAValue().SetIsRegisterPlusOffset(
+ first_row->GetCFAValue().GetRegisterNumber(), m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ reinstate_unwind_state = true;
+ continue;
+ }
+ } else {
+ // CFA register is not sp or fp.
+
+ // This must be hand-written assembly.
+ // Just trust eh_frame and assume we have finished.
+ break;
+ }
+ }
+
+ unwind_plan.SetPlanValidAddressRange(func_range);
+ if (unwind_plan_updated) {
+ std::string unwind_plan_source(unwind_plan.GetSourceName().AsCString());
+ unwind_plan_source += " plus augmentation from assembly parsing";
+ unwind_plan.SetSourceName(unwind_plan_source.c_str());
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ }
+ return true;
+}
+
+bool x86AssemblyInspectionEngine::FindFirstNonPrologueInstruction(
+ uint8_t *data, size_t size, size_t &offset) {
+ offset = 0;
+
+ if (!m_register_map_initialized)
+ return false;
+
+ while (offset < size) {
+ int regno;
+ int insn_len;
+ int scratch;
+
+ m_cur_insn = data + offset;
+ if (!instruction_length(m_cur_insn, insn_len, size - offset)
+ || insn_len > kMaxInstructionByteSize
+ || insn_len == 0) {
+ // An error parsing the instruction, i.e. probably data/garbage - stop
+ // scanning
+ break;
+ }
+
+ if (push_rbp_pattern_p() || mov_rsp_rbp_pattern_p() ||
+ sub_rsp_pattern_p(scratch) || push_reg_p(regno) ||
+ mov_reg_to_local_stack_frame_p(regno, scratch) ||
+ (lea_rsp_pattern_p(scratch) && offset == 0)) {
+ offset += insn_len;
+ continue;
+ }
+ //
+ // Unknown non-prologue instruction - stop scanning
+ break;
+ }
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h
new file mode 100644
index 000000000000..680598abdeff
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h
@@ -0,0 +1,199 @@
+//===-- x86AssemblyInspectionEngine.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_x86AssemblyInspectionEngine_h_
+#define liblldb_x86AssemblyInspectionEngine_h_
+
+#include "llvm-c/Disassembler.h"
+
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private.h"
+
+#include <map>
+#include <vector>
+
+namespace lldb_private {
+
+// x86AssemblyInspectionEngine - a class which will take a buffer of bytes
+// of i386/x86_64 instructions and create an UnwindPlan based on those
+// assembly instructions.
+class x86AssemblyInspectionEngine {
+
+public:
+ /// default ctor
+ x86AssemblyInspectionEngine(const lldb_private::ArchSpec &arch);
+
+ /// default dtor
+ ~x86AssemblyInspectionEngine();
+
+ /// One of the two initialize methods that can be called on this object;
+ /// they must be called before any of the assembly inspection methods
+ /// are called. This one should be used if the caller has access to a
+ /// valid RegisterContext.
+ void Initialize(lldb::RegisterContextSP &reg_ctx);
+
+ /// One of the two initialize methods that can be called on this object;
+ /// they must be called before any of the assembly inspection methods
+ /// are called. This one takes a vector of register name and lldb
+ /// register numbers.
+ struct lldb_reg_info {
+ const char *name;
+ uint32_t lldb_regnum;
+ lldb_reg_info() : name(nullptr), lldb_regnum(LLDB_INVALID_REGNUM) {}
+ };
+ void Initialize(std::vector<lldb_reg_info> &reg_info);
+
+ /// Create an UnwindPlan for a "non-call site" stack frame situation.
+ /// This is usually when this function/method is currently executing, and may
+ /// be at
+ /// a location where exception-handling style unwind information (eh_frame,
+ /// compact unwind info, arm unwind info)
+ /// are not valid.
+ /// \p data is a pointer to the instructions for the function
+ /// \p size is the size of the instruction buffer above
+ /// \p func_range is the start Address and size of the function, to be
+ /// included in the UnwindPlan
+ /// \p unwind_plan is the unwind plan that this method creates
+ /// \returns true if it was able to create an UnwindPlan; false if not.
+ bool
+ GetNonCallSiteUnwindPlanFromAssembly(uint8_t *data, size_t size,
+ lldb_private::AddressRange &func_range,
+ lldb_private::UnwindPlan &unwind_plan);
+
+ /// Take an existing UnwindPlan, probably from eh_frame which may be missing
+ /// description
+ /// of the epilogue instructions, and add the epilogue description to it based
+ /// on the
+ /// instructions in the function.
+ ///
+ /// The \p unwind_plan 's register numbers must be converted into the lldb
+ /// register numbering
+ /// scheme OR a RegisterContext must be provided in \p reg_ctx. If the \p
+ /// unwind_plan
+ /// register numbers are already in lldb register numbering, \p reg_ctx may be
+ /// null.
+ /// \returns true if the \p unwind_plan was updated, false if it was not.
+ bool AugmentUnwindPlanFromCallSite(uint8_t *data, size_t size,
+ lldb_private::AddressRange &func_range,
+ lldb_private::UnwindPlan &unwind_plan,
+ lldb::RegisterContextSP &reg_ctx);
+
+ bool FindFirstNonPrologueInstruction(uint8_t *data, size_t size,
+ size_t &offset);
+
+private:
+ bool nonvolatile_reg_p(int machine_regno);
+ bool push_rbp_pattern_p();
+ bool push_0_pattern_p();
+ bool push_imm_pattern_p();
+ bool push_extended_pattern_p();
+ bool push_misc_reg_p();
+ bool mov_rsp_rbp_pattern_p();
+ bool mov_rsp_rbx_pattern_p();
+ bool mov_rbp_rsp_pattern_p();
+ bool mov_rbx_rsp_pattern_p();
+ bool sub_rsp_pattern_p(int &amount);
+ bool add_rsp_pattern_p(int &amount);
+ bool lea_rsp_pattern_p(int &amount);
+ bool lea_rbp_rsp_pattern_p(int &amount);
+ bool lea_rbx_rsp_pattern_p(int &amount);
+ bool and_rsp_pattern_p();
+ bool push_reg_p(int &regno);
+ bool pop_reg_p(int &regno);
+ bool pop_rbp_pattern_p();
+ bool pop_misc_reg_p();
+ bool leave_pattern_p();
+ bool call_next_insn_pattern_p();
+ bool mov_reg_to_local_stack_frame_p(int &regno, int &rbp_offset);
+ bool ret_pattern_p();
+ bool jmp_to_reg_p();
+ bool pc_rel_branch_or_jump_p (const int instruction_length, int &offset);
+ bool non_local_branch_p (const lldb::addr_t current_func_text_offset,
+ const lldb_private::AddressRange &func_range,
+ const int instruction_length);
+ bool local_branch_p (const lldb::addr_t current_func_text_offset,
+ const lldb_private::AddressRange &func_range,
+ const int instruction_length,
+ lldb::addr_t &target_insn_offset);
+ uint16_t extract_2(uint8_t *b);
+ int16_t extract_2_signed(uint8_t *b);
+ uint32_t extract_4(uint8_t *b);
+ int32_t extract_4_signed(uint8_t *b);
+
+ bool instruction_length(uint8_t *insn, int &length, uint32_t buffer_remaining_bytes);
+
+ bool machine_regno_to_lldb_regno(int machine_regno, uint32_t &lldb_regno);
+
+ enum CPU { k_i386, k_x86_64, k_cpu_unspecified };
+
+ enum i386_register_numbers {
+ k_machine_eax = 0,
+ k_machine_ecx = 1,
+ k_machine_edx = 2,
+ k_machine_ebx = 3,
+ k_machine_esp = 4,
+ k_machine_ebp = 5,
+ k_machine_esi = 6,
+ k_machine_edi = 7,
+ k_machine_eip = 8
+ };
+
+ enum x86_64_register_numbers {
+ k_machine_rax = 0,
+ k_machine_rcx = 1,
+ k_machine_rdx = 2,
+ k_machine_rbx = 3,
+ k_machine_rsp = 4,
+ k_machine_rbp = 5,
+ k_machine_rsi = 6,
+ k_machine_rdi = 7,
+ k_machine_r8 = 8,
+ k_machine_r9 = 9,
+ k_machine_r10 = 10,
+ k_machine_r11 = 11,
+ k_machine_r12 = 12,
+ k_machine_r13 = 13,
+ k_machine_r14 = 14,
+ k_machine_r15 = 15,
+ k_machine_rip = 16
+ };
+
+ enum { kMaxInstructionByteSize = 32 };
+
+ uint8_t *m_cur_insn;
+
+ uint32_t m_machine_ip_regnum;
+ uint32_t m_machine_sp_regnum;
+ uint32_t m_machine_fp_regnum;
+ uint32_t m_machine_alt_fp_regnum;
+ uint32_t m_lldb_ip_regnum;
+ uint32_t m_lldb_sp_regnum;
+ uint32_t m_lldb_fp_regnum;
+ uint32_t m_lldb_alt_fp_regnum;
+
+ typedef std::map<uint32_t, lldb_reg_info> MachineRegnumToNameAndLLDBRegnum;
+
+ MachineRegnumToNameAndLLDBRegnum m_reg_map;
+
+ lldb_private::ArchSpec m_arch;
+ CPU m_cpu;
+ int m_wordsize;
+
+ bool m_register_map_initialized;
+
+ ::LLVMDisasmContextRef m_disasm_context;
+
+ DISALLOW_COPY_AND_ASSIGN(x86AssemblyInspectionEngine);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_x86AssemblyInspectionEngine_h_
diff --git a/contrib/llvm-project/lldb/source/Symbol/ArmUnwindInfo.cpp b/contrib/llvm-project/lldb/source/Symbol/ArmUnwindInfo.cpp
new file mode 100644
index 000000000000..b9fd84b1e706
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/ArmUnwindInfo.cpp
@@ -0,0 +1,369 @@
+//===-- ArmUnwindInfo.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <vector>
+
+#include "Utility/ARM_DWARF_Registers.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ArmUnwindInfo.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Utility/Endian.h"
+
+/*
+ * Unwind information reader and parser for the ARM exception handling ABI
+ *
+ * Implemented based on:
+ * Exception Handling ABI for the ARM Architecture
+ * Document number: ARM IHI 0038A (current through ABI r2.09)
+ * Date of Issue: 25th January 2007, reissued 30th November 2012
+ * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
+ */
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Converts a prel31 avlue to lldb::addr_t with sign extension
+static addr_t Prel31ToAddr(uint32_t prel31) {
+ addr_t res = prel31;
+ if (prel31 & (1 << 30))
+ res |= 0xffffffff80000000ULL;
+ return res;
+}
+
+ArmUnwindInfo::ArmExidxEntry::ArmExidxEntry(uint32_t f, lldb::addr_t a,
+ uint32_t d)
+ : file_address(f), address(a), data(d) {}
+
+bool ArmUnwindInfo::ArmExidxEntry::operator<(const ArmExidxEntry &other) const {
+ return address < other.address;
+}
+
+ArmUnwindInfo::ArmUnwindInfo(ObjectFile &objfile, SectionSP &arm_exidx,
+ SectionSP &arm_extab)
+ : m_byte_order(objfile.GetByteOrder()), m_arm_exidx_sp(arm_exidx),
+ m_arm_extab_sp(arm_extab) {
+ objfile.ReadSectionData(arm_exidx.get(), m_arm_exidx_data);
+ objfile.ReadSectionData(arm_extab.get(), m_arm_extab_data);
+
+ addr_t exidx_base_addr = m_arm_exidx_sp->GetFileAddress();
+
+ offset_t offset = 0;
+ while (m_arm_exidx_data.ValidOffset(offset)) {
+ lldb::addr_t file_addr = exidx_base_addr + offset;
+ lldb::addr_t addr = exidx_base_addr + (addr_t)offset +
+ Prel31ToAddr(m_arm_exidx_data.GetU32(&offset));
+ uint32_t data = m_arm_exidx_data.GetU32(&offset);
+ m_exidx_entries.emplace_back(file_addr, addr, data);
+ }
+
+ // Sort the entries in the exidx section. The entries should be sorted inside
+ // the section but some old compiler isn't sorted them.
+ llvm::sort(m_exidx_entries.begin(), m_exidx_entries.end());
+}
+
+ArmUnwindInfo::~ArmUnwindInfo() {}
+
+// Read a byte from the unwind instruction stream with the given offset. Custom
+// function is required because have to red in order of significance within
+// their containing word (most significant byte first) and in increasing word
+// address order.
+uint8_t ArmUnwindInfo::GetByteAtOffset(const uint32_t *data,
+ uint16_t offset) const {
+ uint32_t value = data[offset / 4];
+ if (m_byte_order != endian::InlHostByteOrder())
+ value = llvm::ByteSwap_32(value);
+ return (value >> ((3 - (offset % 4)) * 8)) & 0xff;
+}
+
+uint64_t ArmUnwindInfo::GetULEB128(const uint32_t *data, uint16_t &offset,
+ uint16_t max_offset) const {
+ uint64_t result = 0;
+ uint8_t shift = 0;
+ while (offset < max_offset) {
+ uint8_t byte = GetByteAtOffset(data, offset++);
+ result |= (uint64_t)(byte & 0x7f) << shift;
+ if ((byte & 0x80) == 0)
+ break;
+ shift += 7;
+ }
+ return result;
+}
+
+bool ArmUnwindInfo::GetUnwindPlan(Target &target, const Address &addr,
+ UnwindPlan &unwind_plan) {
+ const uint32_t *data = (const uint32_t *)GetExceptionHandlingTableEntry(addr);
+ if (data == nullptr)
+ return false; // No unwind information for the function
+
+ if (data[0] == 0x1)
+ return false; // EXIDX_CANTUNWIND
+
+ uint16_t byte_count = 0;
+ uint16_t byte_offset = 0;
+ if (data[0] & 0x80000000) {
+ switch ((data[0] >> 24) & 0x0f) {
+ case 0:
+ byte_count = 4;
+ byte_offset = 1;
+ break;
+ case 1:
+ case 2:
+ byte_count = 4 * ((data[0] >> 16) & 0xff) + 4;
+ byte_offset = 2;
+ break;
+ default:
+ // Unhandled personality routine index
+ return false;
+ }
+ } else {
+ byte_count = 4 * ((data[1] >> 24) & 0xff) + 8;
+ byte_offset = 5;
+ }
+
+ uint8_t vsp_reg = dwarf_sp;
+ int32_t vsp = 0;
+ std::vector<std::pair<uint32_t, int32_t>>
+ register_offsets; // register -> (offset from vsp_reg)
+
+ while (byte_offset < byte_count) {
+ uint8_t byte1 = GetByteAtOffset(data, byte_offset++);
+ if ((byte1 & 0xc0) == 0x00) {
+ // 00xxxxxx
+ // vsp = vsp + (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive
+ vsp += ((byte1 & 0x3f) << 2) + 4;
+ } else if ((byte1 & 0xc0) == 0x40) {
+ // 01xxxxxx
+ // vsp = vsp – (xxxxxx << 2) - 4. Covers range 0x04-0x100 inclusive
+ vsp -= ((byte1 & 0x3f) << 2) + 4;
+ } else if ((byte1 & 0xf0) == 0x80) {
+ if (byte_offset >= byte_count)
+ return false;
+
+ uint8_t byte2 = GetByteAtOffset(data, byte_offset++);
+ if (byte1 == 0x80 && byte2 == 0) {
+ // 10000000 00000000
+ // Refuse to unwind (for example, out of a cleanup) (see remark a)
+ return false;
+ } else {
+ // 1000iiii iiiiiiii (i not all 0)
+ // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4} (see
+ // remark b)
+ uint16_t regs = ((byte1 & 0x0f) << 8) | byte2;
+ for (uint8_t i = 0; i < 12; ++i) {
+ if (regs & (1 << i)) {
+ register_offsets.emplace_back(dwarf_r4 + i, vsp);
+ vsp += 4;
+ }
+ }
+ }
+ } else if ((byte1 & 0xff) == 0x9d) {
+ // 10011101
+ // Reserved as prefix for ARM register to register moves
+ return false;
+ } else if ((byte1 & 0xff) == 0x9f) {
+ // 10011111
+ // Reserved as prefix for Intel Wireless MMX register to register moves
+ return false;
+ } else if ((byte1 & 0xf0) == 0x90) {
+ // 1001nnnn (nnnn != 13,15)
+ // Set vsp = r[nnnn]
+ vsp_reg = dwarf_r0 + (byte1 & 0x0f);
+ } else if ((byte1 & 0xf8) == 0xa0) {
+ // 10100nnn
+ // Pop r4-r[4+nnn]
+ uint8_t n = byte1 & 0x7;
+ for (uint8_t i = 0; i <= n; ++i) {
+ register_offsets.emplace_back(dwarf_r4 + i, vsp);
+ vsp += 4;
+ }
+ } else if ((byte1 & 0xf8) == 0xa8) {
+ // 10101nnn
+ // Pop r4-r[4+nnn], r14
+ uint8_t n = byte1 & 0x7;
+ for (uint8_t i = 0; i <= n; ++i) {
+ register_offsets.emplace_back(dwarf_r4 + i, vsp);
+ vsp += 4;
+ }
+
+ register_offsets.emplace_back(dwarf_lr, vsp);
+ vsp += 4;
+ } else if ((byte1 & 0xff) == 0xb0) {
+ // 10110000
+ // Finish (see remark c)
+ break;
+ } else if ((byte1 & 0xff) == 0xb1) {
+ if (byte_offset >= byte_count)
+ return false;
+
+ uint8_t byte2 = GetByteAtOffset(data, byte_offset++);
+ if ((byte2 & 0xff) == 0x00) {
+ // 10110001 00000000
+ // Spare (see remark f)
+ return false;
+ } else if ((byte2 & 0xf0) == 0x00) {
+ // 10110001 0000iiii (i not all 0)
+ // Pop integer registers under mask {r3, r2, r1, r0}
+ for (uint8_t i = 0; i < 4; ++i) {
+ if (byte2 & (1 << i)) {
+ register_offsets.emplace_back(dwarf_r0 + i, vsp);
+ vsp += 4;
+ }
+ }
+ } else {
+ // 10110001 xxxxyyyy
+ // Spare (xxxx != 0000)
+ return false;
+ }
+ } else if ((byte1 & 0xff) == 0xb2) {
+ // 10110010 uleb128
+ // vsp = vsp + 0x204+ (uleb128 << 2)
+ uint64_t uleb128 = GetULEB128(data, byte_offset, byte_count);
+ vsp += 0x204 + (uleb128 << 2);
+ } else if ((byte1 & 0xff) == 0xb3) {
+ // 10110011 sssscccc
+ // Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if)
+ // by FSTMFDX (see remark d)
+ if (byte_offset >= byte_count)
+ return false;
+
+ uint8_t byte2 = GetByteAtOffset(data, byte_offset++);
+ uint8_t s = (byte2 & 0xf0) >> 4;
+ uint8_t c = (byte2 & 0x0f) >> 0;
+ for (uint8_t i = 0; i <= c; ++i) {
+ register_offsets.emplace_back(dwarf_d0 + s + i, vsp);
+ vsp += 8;
+ }
+ vsp += 4;
+ } else if ((byte1 & 0xfc) == 0xb4) {
+ // 101101nn
+ // Spare (was Pop FPA)
+ return false;
+ } else if ((byte1 & 0xf8) == 0xb8) {
+ // 10111nnn
+ // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by
+ // FSTMFDX (see remark d)
+ uint8_t n = byte1 & 0x07;
+ for (uint8_t i = 0; i <= n; ++i) {
+ register_offsets.emplace_back(dwarf_d8 + i, vsp);
+ vsp += 8;
+ }
+ vsp += 4;
+ } else if ((byte1 & 0xf8) == 0xc0) {
+ // 11000nnn (nnn != 6,7)
+ // Intel Wireless MMX pop wR[10]-wR[10+nnn]
+
+ // 11000110 sssscccc
+ // Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc] (see remark e)
+
+ // 11000111 00000000
+ // Spare
+
+ // 11000111 0000iiii
+ // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}
+
+ // 11000111 xxxxyyyy
+ // Spare (xxxx != 0000)
+
+ return false;
+ } else if ((byte1 & 0xff) == 0xc8) {
+ // 11001000 sssscccc
+ // Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] saved
+ // (as if) by FSTMFDD (see remarks d,e)
+ if (byte_offset >= byte_count)
+ return false;
+
+ uint8_t byte2 = GetByteAtOffset(data, byte_offset++);
+ uint8_t s = (byte2 & 0xf0) >> 4;
+ uint8_t c = (byte2 & 0x0f) >> 0;
+ for (uint8_t i = 0; i <= c; ++i) {
+ register_offsets.emplace_back(dwarf_d16 + s + i, vsp);
+ vsp += 8;
+ }
+ } else if ((byte1 & 0xff) == 0xc9) {
+ // 11001001 sssscccc
+ // Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if)
+ // by FSTMFDD (see remark d)
+ if (byte_offset >= byte_count)
+ return false;
+
+ uint8_t byte2 = GetByteAtOffset(data, byte_offset++);
+ uint8_t s = (byte2 & 0xf0) >> 4;
+ uint8_t c = (byte2 & 0x0f) >> 0;
+ for (uint8_t i = 0; i <= c; ++i) {
+ register_offsets.emplace_back(dwarf_d0 + s + i, vsp);
+ vsp += 8;
+ }
+ } else if ((byte1 & 0xf8) == 0xc8) {
+ // 11001yyy
+ // Spare (yyy != 000, 001)
+ return false;
+ } else if ((byte1 & 0xf8) == 0xd0) {
+ // 11010nnn
+ // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by
+ // FSTMFDD (see remark d)
+ uint8_t n = byte1 & 0x07;
+ for (uint8_t i = 0; i <= n; ++i) {
+ register_offsets.emplace_back(dwarf_d8 + i, vsp);
+ vsp += 8;
+ }
+ } else if ((byte1 & 0xc0) == 0xc0) {
+ // 11xxxyyy Spare (xxx != 000, 001, 010)
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ UnwindPlan::RowSP row = std::make_shared<UnwindPlan::Row>();
+ row->SetOffset(0);
+ row->GetCFAValue().SetIsRegisterPlusOffset(vsp_reg, vsp);
+
+ bool have_location_for_pc = false;
+ for (const auto &offset : register_offsets) {
+ have_location_for_pc |= offset.first == dwarf_pc;
+ row->SetRegisterLocationToAtCFAPlusOffset(offset.first, offset.second - vsp,
+ true);
+ }
+
+ if (!have_location_for_pc) {
+ UnwindPlan::Row::RegisterLocation lr_location;
+ if (row->GetRegisterInfo(dwarf_lr, lr_location))
+ row->SetRegisterInfo(dwarf_pc, lr_location);
+ else
+ row->SetRegisterLocationToRegister(dwarf_pc, dwarf_lr, false);
+ }
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("ARM.exidx unwind info");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolYes);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ return true;
+}
+
+const uint8_t *
+ArmUnwindInfo::GetExceptionHandlingTableEntry(const Address &addr) {
+ auto it = std::upper_bound(m_exidx_entries.begin(), m_exidx_entries.end(),
+ ArmExidxEntry{0, addr.GetFileAddress(), 0});
+ if (it == m_exidx_entries.begin())
+ return nullptr;
+ --it;
+
+ if (it->data == 0x1)
+ return nullptr; // EXIDX_CANTUNWIND
+
+ if (it->data & 0x80000000)
+ return (const uint8_t *)&it->data;
+
+ addr_t data_file_addr = it->file_address + 4 + Prel31ToAddr(it->data);
+ return m_arm_extab_data.GetDataStart() +
+ (data_file_addr - m_arm_extab_sp->GetFileAddress());
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/Block.cpp b/contrib/llvm-project/lldb/source/Symbol/Block.cpp
new file mode 100644
index 000000000000..6fe617080f96
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/Block.cpp
@@ -0,0 +1,517 @@
+//===-- Block.cpp -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/Block.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Utility/Log.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+Block::Block(lldb::user_id_t uid)
+ : UserID(uid), m_parent_scope(nullptr), m_children(), m_ranges(),
+ m_inlineInfoSP(), m_variable_list_sp(), m_parsed_block_info(false),
+ m_parsed_block_variables(false), m_parsed_child_blocks(false) {}
+
+Block::~Block() {}
+
+void Block::GetDescription(Stream *s, Function *function,
+ lldb::DescriptionLevel level, Target *target) const {
+ *s << "id = " << ((const UserID &)*this);
+
+ size_t num_ranges = m_ranges.GetSize();
+ if (num_ranges > 0) {
+
+ addr_t base_addr = LLDB_INVALID_ADDRESS;
+ if (target)
+ base_addr =
+ function->GetAddressRange().GetBaseAddress().GetLoadAddress(target);
+ if (base_addr == LLDB_INVALID_ADDRESS)
+ base_addr = function->GetAddressRange().GetBaseAddress().GetFileAddress();
+
+ s->Printf(", range%s = ", num_ranges > 1 ? "s" : "");
+ for (size_t i = 0; i < num_ranges; ++i) {
+ const Range &range = m_ranges.GetEntryRef(i);
+ s->AddressRange(base_addr + range.GetRangeBase(),
+ base_addr + range.GetRangeEnd(), 4);
+ }
+ }
+
+ if (m_inlineInfoSP.get() != nullptr) {
+ bool show_fullpaths = (level == eDescriptionLevelVerbose);
+ m_inlineInfoSP->Dump(s, show_fullpaths);
+ }
+}
+
+void Block::Dump(Stream *s, addr_t base_addr, int32_t depth,
+ bool show_context) const {
+ if (depth < 0) {
+ Block *parent = GetParent();
+ if (parent) {
+ // We have a depth that is less than zero, print our parent blocks first
+ parent->Dump(s, base_addr, depth + 1, show_context);
+ }
+ }
+
+ s->Printf("%p: ", static_cast<const void *>(this));
+ s->Indent();
+ *s << "Block" << static_cast<const UserID &>(*this);
+ const Block *parent_block = GetParent();
+ if (parent_block) {
+ s->Printf(", parent = {0x%8.8" PRIx64 "}", parent_block->GetID());
+ }
+ if (m_inlineInfoSP.get() != nullptr) {
+ bool show_fullpaths = false;
+ m_inlineInfoSP->Dump(s, show_fullpaths);
+ }
+
+ if (!m_ranges.IsEmpty()) {
+ *s << ", ranges =";
+
+ size_t num_ranges = m_ranges.GetSize();
+ for (size_t i = 0; i < num_ranges; ++i) {
+ const Range &range = m_ranges.GetEntryRef(i);
+ if (parent_block != nullptr && !parent_block->Contains(range))
+ *s << '!';
+ else
+ *s << ' ';
+ s->AddressRange(base_addr + range.GetRangeBase(),
+ base_addr + range.GetRangeEnd(), 4);
+ }
+ }
+ s->EOL();
+
+ if (depth > 0) {
+ s->IndentMore();
+
+ if (m_variable_list_sp.get()) {
+ m_variable_list_sp->Dump(s, show_context);
+ }
+
+ collection::const_iterator pos, end = m_children.end();
+ for (pos = m_children.begin(); pos != end; ++pos)
+ (*pos)->Dump(s, base_addr, depth - 1, show_context);
+
+ s->IndentLess();
+ }
+}
+
+Block *Block::FindBlockByID(user_id_t block_id) {
+ if (block_id == GetID())
+ return this;
+
+ Block *matching_block = nullptr;
+ collection::const_iterator pos, end = m_children.end();
+ for (pos = m_children.begin(); pos != end; ++pos) {
+ matching_block = (*pos)->FindBlockByID(block_id);
+ if (matching_block)
+ break;
+ }
+ return matching_block;
+}
+
+void Block::CalculateSymbolContext(SymbolContext *sc) {
+ if (m_parent_scope)
+ m_parent_scope->CalculateSymbolContext(sc);
+ sc->block = this;
+}
+
+lldb::ModuleSP Block::CalculateSymbolContextModule() {
+ if (m_parent_scope)
+ return m_parent_scope->CalculateSymbolContextModule();
+ return lldb::ModuleSP();
+}
+
+CompileUnit *Block::CalculateSymbolContextCompileUnit() {
+ if (m_parent_scope)
+ return m_parent_scope->CalculateSymbolContextCompileUnit();
+ return nullptr;
+}
+
+Function *Block::CalculateSymbolContextFunction() {
+ if (m_parent_scope)
+ return m_parent_scope->CalculateSymbolContextFunction();
+ return nullptr;
+}
+
+Block *Block::CalculateSymbolContextBlock() { return this; }
+
+void Block::DumpSymbolContext(Stream *s) {
+ Function *function = CalculateSymbolContextFunction();
+ if (function)
+ function->DumpSymbolContext(s);
+ s->Printf(", Block{0x%8.8" PRIx64 "}", GetID());
+}
+
+void Block::DumpAddressRanges(Stream *s, lldb::addr_t base_addr) {
+ if (!m_ranges.IsEmpty()) {
+ size_t num_ranges = m_ranges.GetSize();
+ for (size_t i = 0; i < num_ranges; ++i) {
+ const Range &range = m_ranges.GetEntryRef(i);
+ s->AddressRange(base_addr + range.GetRangeBase(),
+ base_addr + range.GetRangeEnd(), 4);
+ }
+ }
+}
+
+bool Block::Contains(addr_t range_offset) const {
+ return m_ranges.FindEntryThatContains(range_offset) != nullptr;
+}
+
+bool Block::Contains(const Block *block) const {
+ if (this == block)
+ return false; // This block doesn't contain itself...
+
+ // Walk the parent chain for "block" and see if any if them match this block
+ const Block *block_parent;
+ for (block_parent = block->GetParent(); block_parent != nullptr;
+ block_parent = block_parent->GetParent()) {
+ if (this == block_parent)
+ return true; // One of the parents of "block" is this object!
+ }
+ return false;
+}
+
+bool Block::Contains(const Range &range) const {
+ return m_ranges.FindEntryThatContains(range) != nullptr;
+}
+
+Block *Block::GetParent() const {
+ if (m_parent_scope)
+ return m_parent_scope->CalculateSymbolContextBlock();
+ return nullptr;
+}
+
+Block *Block::GetContainingInlinedBlock() {
+ if (GetInlinedFunctionInfo())
+ return this;
+ return GetInlinedParent();
+}
+
+Block *Block::GetInlinedParent() {
+ Block *parent_block = GetParent();
+ if (parent_block) {
+ if (parent_block->GetInlinedFunctionInfo())
+ return parent_block;
+ else
+ return parent_block->GetInlinedParent();
+ }
+ return nullptr;
+}
+
+Block *Block::GetContainingInlinedBlockWithCallSite(
+ const Declaration &find_call_site) {
+ Block *inlined_block = GetContainingInlinedBlock();
+
+ while (inlined_block) {
+ const auto *function_info = inlined_block->GetInlinedFunctionInfo();
+
+ if (function_info &&
+ function_info->GetCallSite().FileAndLineEqual(find_call_site))
+ return inlined_block;
+ inlined_block = inlined_block->GetInlinedParent();
+ }
+ return nullptr;
+}
+
+bool Block::GetRangeContainingOffset(const addr_t offset, Range &range) {
+ const Range *range_ptr = m_ranges.FindEntryThatContains(offset);
+ if (range_ptr) {
+ range = *range_ptr;
+ return true;
+ }
+ range.Clear();
+ return false;
+}
+
+bool Block::GetRangeContainingAddress(const Address &addr,
+ AddressRange &range) {
+ Function *function = CalculateSymbolContextFunction();
+ if (function) {
+ const AddressRange &func_range = function->GetAddressRange();
+ if (addr.GetSection() == func_range.GetBaseAddress().GetSection()) {
+ const addr_t addr_offset = addr.GetOffset();
+ const addr_t func_offset = func_range.GetBaseAddress().GetOffset();
+ if (addr_offset >= func_offset &&
+ addr_offset < func_offset + func_range.GetByteSize()) {
+ addr_t offset = addr_offset - func_offset;
+
+ const Range *range_ptr = m_ranges.FindEntryThatContains(offset);
+
+ if (range_ptr) {
+ range.GetBaseAddress() = func_range.GetBaseAddress();
+ range.GetBaseAddress().SetOffset(func_offset +
+ range_ptr->GetRangeBase());
+ range.SetByteSize(range_ptr->GetByteSize());
+ return true;
+ }
+ }
+ }
+ }
+ range.Clear();
+ return false;
+}
+
+bool Block::GetRangeContainingLoadAddress(lldb::addr_t load_addr,
+ Target &target, AddressRange &range) {
+ Address load_address;
+ load_address.SetLoadAddress(load_addr, &target);
+ AddressRange containing_range;
+ return GetRangeContainingAddress(load_address, containing_range);
+}
+
+uint32_t Block::GetRangeIndexContainingAddress(const Address &addr) {
+ Function *function = CalculateSymbolContextFunction();
+ if (function) {
+ const AddressRange &func_range = function->GetAddressRange();
+ if (addr.GetSection() == func_range.GetBaseAddress().GetSection()) {
+ const addr_t addr_offset = addr.GetOffset();
+ const addr_t func_offset = func_range.GetBaseAddress().GetOffset();
+ if (addr_offset >= func_offset &&
+ addr_offset < func_offset + func_range.GetByteSize()) {
+ addr_t offset = addr_offset - func_offset;
+ return m_ranges.FindEntryIndexThatContains(offset);
+ }
+ }
+ }
+ return UINT32_MAX;
+}
+
+bool Block::GetRangeAtIndex(uint32_t range_idx, AddressRange &range) {
+ if (range_idx < m_ranges.GetSize()) {
+ Function *function = CalculateSymbolContextFunction();
+ if (function) {
+ const Range &vm_range = m_ranges.GetEntryRef(range_idx);
+ range.GetBaseAddress() = function->GetAddressRange().GetBaseAddress();
+ range.GetBaseAddress().Slide(vm_range.GetRangeBase());
+ range.SetByteSize(vm_range.GetByteSize());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Block::GetStartAddress(Address &addr) {
+ if (m_ranges.IsEmpty())
+ return false;
+
+ Function *function = CalculateSymbolContextFunction();
+ if (function) {
+ addr = function->GetAddressRange().GetBaseAddress();
+ addr.Slide(m_ranges.GetEntryRef(0).GetRangeBase());
+ return true;
+ }
+ return false;
+}
+
+void Block::FinalizeRanges() {
+ m_ranges.Sort();
+ m_ranges.CombineConsecutiveRanges();
+}
+
+void Block::AddRange(const Range &range) {
+ Block *parent_block = GetParent();
+ if (parent_block && !parent_block->Contains(range)) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS));
+ if (log) {
+ ModuleSP module_sp(m_parent_scope->CalculateSymbolContextModule());
+ Function *function = m_parent_scope->CalculateSymbolContextFunction();
+ const addr_t function_file_addr =
+ function->GetAddressRange().GetBaseAddress().GetFileAddress();
+ const addr_t block_start_addr = function_file_addr + range.GetRangeBase();
+ const addr_t block_end_addr = function_file_addr + range.GetRangeEnd();
+ Type *func_type = function->GetType();
+
+ const Declaration &func_decl = func_type->GetDeclaration();
+ if (func_decl.GetLine()) {
+ log->Printf("warning: %s:%u block {0x%8.8" PRIx64
+ "} has range[%u] [0x%" PRIx64 " - 0x%" PRIx64
+ ") which is not contained in parent block {0x%8.8" PRIx64
+ "} in function {0x%8.8" PRIx64 "} from %s",
+ func_decl.GetFile().GetPath().c_str(), func_decl.GetLine(),
+ GetID(), (uint32_t)m_ranges.GetSize(), block_start_addr,
+ block_end_addr, parent_block->GetID(), function->GetID(),
+ module_sp->GetFileSpec().GetPath().c_str());
+ } else {
+ log->Printf("warning: block {0x%8.8" PRIx64
+ "} has range[%u] [0x%" PRIx64 " - 0x%" PRIx64
+ ") which is not contained in parent block {0x%8.8" PRIx64
+ "} in function {0x%8.8" PRIx64 "} from %s",
+ GetID(), (uint32_t)m_ranges.GetSize(), block_start_addr,
+ block_end_addr, parent_block->GetID(), function->GetID(),
+ module_sp->GetFileSpec().GetPath().c_str());
+ }
+ }
+ parent_block->AddRange(range);
+ }
+ m_ranges.Append(range);
+}
+
+// Return the current number of bytes that this object occupies in memory
+size_t Block::MemorySize() const {
+ size_t mem_size = sizeof(Block) + m_ranges.GetSize() * sizeof(Range);
+ if (m_inlineInfoSP.get())
+ mem_size += m_inlineInfoSP->MemorySize();
+ if (m_variable_list_sp.get())
+ mem_size += m_variable_list_sp->MemorySize();
+ return mem_size;
+}
+
+void Block::AddChild(const BlockSP &child_block_sp) {
+ if (child_block_sp) {
+ child_block_sp->SetParentScope(this);
+ m_children.push_back(child_block_sp);
+ }
+}
+
+void Block::SetInlinedFunctionInfo(const char *name, const char *mangled,
+ const Declaration *decl_ptr,
+ const Declaration *call_decl_ptr) {
+ m_inlineInfoSP = std::make_shared<InlineFunctionInfo>(name, mangled, decl_ptr,
+ call_decl_ptr);
+}
+
+VariableListSP Block::GetBlockVariableList(bool can_create) {
+ if (!m_parsed_block_variables) {
+ if (m_variable_list_sp.get() == nullptr && can_create) {
+ m_parsed_block_variables = true;
+ SymbolContext sc;
+ CalculateSymbolContext(&sc);
+ assert(sc.module_sp);
+ sc.module_sp->GetSymbolVendor()->ParseVariablesForContext(sc);
+ }
+ }
+ return m_variable_list_sp;
+}
+
+uint32_t
+Block::AppendBlockVariables(bool can_create, bool get_child_block_variables,
+ bool stop_if_child_block_is_inlined_function,
+ const std::function<bool(Variable *)> &filter,
+ VariableList *variable_list) {
+ uint32_t num_variables_added = 0;
+ VariableList *block_var_list = GetBlockVariableList(can_create).get();
+ if (block_var_list) {
+ for (size_t i = 0; i < block_var_list->GetSize(); ++i) {
+ VariableSP variable = block_var_list->GetVariableAtIndex(i);
+ if (filter(variable.get())) {
+ num_variables_added++;
+ variable_list->AddVariable(variable);
+ }
+ }
+ }
+
+ if (get_child_block_variables) {
+ collection::const_iterator pos, end = m_children.end();
+ for (pos = m_children.begin(); pos != end; ++pos) {
+ Block *child_block = pos->get();
+ if (!stop_if_child_block_is_inlined_function ||
+ child_block->GetInlinedFunctionInfo() == nullptr) {
+ num_variables_added += child_block->AppendBlockVariables(
+ can_create, get_child_block_variables,
+ stop_if_child_block_is_inlined_function, filter, variable_list);
+ }
+ }
+ }
+ return num_variables_added;
+}
+
+uint32_t Block::AppendVariables(bool can_create, bool get_parent_variables,
+ bool stop_if_block_is_inlined_function,
+ const std::function<bool(Variable *)> &filter,
+ VariableList *variable_list) {
+ uint32_t num_variables_added = 0;
+ VariableListSP variable_list_sp(GetBlockVariableList(can_create));
+
+ bool is_inlined_function = GetInlinedFunctionInfo() != nullptr;
+ if (variable_list_sp) {
+ for (size_t i = 0; i < variable_list_sp->GetSize(); ++i) {
+ VariableSP variable = variable_list_sp->GetVariableAtIndex(i);
+ if (filter(variable.get())) {
+ num_variables_added++;
+ variable_list->AddVariable(variable);
+ }
+ }
+ }
+
+ if (get_parent_variables) {
+ if (stop_if_block_is_inlined_function && is_inlined_function)
+ return num_variables_added;
+
+ Block *parent_block = GetParent();
+ if (parent_block)
+ num_variables_added += parent_block->AppendVariables(
+ can_create, get_parent_variables, stop_if_block_is_inlined_function,
+ filter, variable_list);
+ }
+ return num_variables_added;
+}
+
+SymbolFile *Block::GetSymbolFile() {
+ if (ModuleSP module_sp = CalculateSymbolContextModule())
+ if (SymbolVendor *sym_vendor = module_sp->GetSymbolVendor())
+ return sym_vendor->GetSymbolFile();
+ return nullptr;
+}
+
+CompilerDeclContext Block::GetDeclContext() {
+ if (SymbolFile *sym_file = GetSymbolFile())
+ return sym_file->GetDeclContextForUID(GetID());
+ return CompilerDeclContext();
+}
+
+void Block::SetBlockInfoHasBeenParsed(bool b, bool set_children) {
+ m_parsed_block_info = b;
+ if (set_children) {
+ m_parsed_child_blocks = true;
+ collection::const_iterator pos, end = m_children.end();
+ for (pos = m_children.begin(); pos != end; ++pos)
+ (*pos)->SetBlockInfoHasBeenParsed(b, true);
+ }
+}
+
+void Block::SetDidParseVariables(bool b, bool set_children) {
+ m_parsed_block_variables = b;
+ if (set_children) {
+ collection::const_iterator pos, end = m_children.end();
+ for (pos = m_children.begin(); pos != end; ++pos)
+ (*pos)->SetDidParseVariables(b, true);
+ }
+}
+
+Block *Block::GetSibling() const {
+ if (m_parent_scope) {
+ Block *parent_block = GetParent();
+ if (parent_block)
+ return parent_block->GetSiblingForChild(this);
+ }
+ return nullptr;
+}
+// A parent of child blocks can be asked to find a sibling block given
+// one of its child blocks
+Block *Block::GetSiblingForChild(const Block *child_block) const {
+ if (!m_children.empty()) {
+ collection::const_iterator pos, end = m_children.end();
+ for (pos = m_children.begin(); pos != end; ++pos) {
+ if (pos->get() == child_block) {
+ if (++pos != end)
+ return pos->get();
+ break;
+ }
+ }
+ }
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/ClangASTContext.cpp b/contrib/llvm-project/lldb/source/Symbol/ClangASTContext.cpp
new file mode 100644
index 000000000000..6e85ed02c985
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/ClangASTContext.cpp
@@ -0,0 +1,10409 @@
+//===-- ClangASTContext.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/ClangASTContext.h"
+
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+
+// Clang headers like to use NDEBUG inside of them to enable/disable debug
+// related features using "#ifndef NDEBUG" preprocessor blocks to do one thing
+// or another. This is bad because it means that if clang was built in release
+// mode, it assumes that you are building in release mode which is not always
+// the case. You can end up with functions that are defined as empty in header
+// files when NDEBUG is not defined, and this can cause link errors with the
+// clang .a files that you have since you might be missing functions in the .a
+// file. So we have to define NDEBUG when including clang headers to avoid any
+// mismatches. This is covered by rdar://problem/8691220
+
+#if !defined(NDEBUG) && !defined(LLVM_NDEBUG_OFF)
+#define LLDB_DEFINED_NDEBUG_FOR_CLANG
+#define NDEBUG
+// Need to include assert.h so it is as clang would expect it to be (disabled)
+#include <assert.h>
+#endif
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTImporter.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Mangle.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/VTableBuilder.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/FileSystemOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/Frontend/FrontendOptions.h"
+#include "clang/Frontend/LangStandard.h"
+#include "clang/Sema/Sema.h"
+
+#ifdef LLDB_DEFINED_NDEBUG_FOR_CLANG
+#undef NDEBUG
+#undef LLDB_DEFINED_NDEBUG_FOR_CLANG
+// Need to re-include assert.h so it is as _we_ would expect it to be (enabled)
+#include <assert.h>
+#endif
+
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/Threading.h"
+
+#include "Plugins/ExpressionParser/Clang/ClangFunctionCaller.h"
+#include "Plugins/ExpressionParser/Clang/ClangUserExpression.h"
+#include "Plugins/ExpressionParser/Clang/ClangUtilityFunction.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Flags.h"
+
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ThreadSafeDenseMap.h"
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/ClangExternalASTSourceCallbacks.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/VerifyDecl.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Scalar.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+#include "Plugins/SymbolFile/DWARF/DWARFASTParserClang.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/SymbolFile/PDB/PDBASTParser.h"
+#endif // LLDB_ENABLE_ALL
+
+#include <stdio.h>
+
+#include <mutex>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+using namespace clang;
+
+namespace {
+static inline bool
+ClangASTContextSupportsLanguage(lldb::LanguageType language) {
+ return language == eLanguageTypeUnknown || // Clang is the default type system
+ Language::LanguageIsC(language) ||
+ Language::LanguageIsCPlusPlus(language) ||
+ Language::LanguageIsObjC(language) ||
+ Language::LanguageIsPascal(language) ||
+ // Use Clang for Rust until there is a proper language plugin for it
+ language == eLanguageTypeRust ||
+ language == eLanguageTypeExtRenderScript ||
+ // Use Clang for D until there is a proper language plugin for it
+ language == eLanguageTypeD ||
+ // Open Dylan compiler debug info is designed to be Clang-compatible
+ language == eLanguageTypeDylan;
+}
+
+// Checks whether m1 is an overload of m2 (as opposed to an override). This is
+// called by addOverridesForMethod to distinguish overrides (which share a
+// vtable entry) from overloads (which require distinct entries).
+bool isOverload(clang::CXXMethodDecl *m1, clang::CXXMethodDecl *m2) {
+ // FIXME: This should detect covariant return types, but currently doesn't.
+ lldbassert(&m1->getASTContext() == &m2->getASTContext() &&
+ "Methods should have the same AST context");
+ clang::ASTContext &context = m1->getASTContext();
+
+ const auto *m1Type = llvm::cast<clang::FunctionProtoType>(
+ context.getCanonicalType(m1->getType()));
+
+ const auto *m2Type = llvm::cast<clang::FunctionProtoType>(
+ context.getCanonicalType(m2->getType()));
+
+ auto compareArgTypes = [&context](const clang::QualType &m1p,
+ const clang::QualType &m2p) {
+ return context.hasSameType(m1p.getUnqualifiedType(),
+ m2p.getUnqualifiedType());
+ };
+
+ // FIXME: In C++14 and later, we can just pass m2Type->param_type_end()
+ // as a fourth parameter to std::equal().
+ return (m1->getNumParams() != m2->getNumParams()) ||
+ !std::equal(m1Type->param_type_begin(), m1Type->param_type_end(),
+ m2Type->param_type_begin(), compareArgTypes);
+}
+
+// If decl is a virtual method, walk the base classes looking for methods that
+// decl overrides. This table of overridden methods is used by IRGen to
+// determine the vtable layout for decl's parent class.
+void addOverridesForMethod(clang::CXXMethodDecl *decl) {
+ if (!decl->isVirtual())
+ return;
+
+ clang::CXXBasePaths paths;
+
+ auto find_overridden_methods =
+ [decl](const clang::CXXBaseSpecifier *specifier,
+ clang::CXXBasePath &path) {
+ if (auto *base_record = llvm::dyn_cast<clang::CXXRecordDecl>(
+ specifier->getType()->getAs<clang::RecordType>()->getDecl())) {
+
+ clang::DeclarationName name = decl->getDeclName();
+
+ // If this is a destructor, check whether the base class destructor is
+ // virtual.
+ if (name.getNameKind() == clang::DeclarationName::CXXDestructorName)
+ if (auto *baseDtorDecl = base_record->getDestructor()) {
+ if (baseDtorDecl->isVirtual()) {
+ path.Decls = baseDtorDecl;
+ return true;
+ } else
+ return false;
+ }
+
+ // Otherwise, search for name in the base class.
+ for (path.Decls = base_record->lookup(name); !path.Decls.empty();
+ path.Decls = path.Decls.slice(1)) {
+ if (auto *method_decl =
+ llvm::dyn_cast<clang::CXXMethodDecl>(path.Decls.front()))
+ if (method_decl->isVirtual() && !isOverload(decl, method_decl)) {
+ path.Decls = method_decl;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ };
+
+ if (decl->getParent()->lookupInBases(find_overridden_methods, paths)) {
+ for (auto *overridden_decl : paths.found_decls())
+ decl->addOverriddenMethod(
+ llvm::cast<clang::CXXMethodDecl>(overridden_decl));
+ }
+}
+}
+
+static lldb::addr_t GetVTableAddress(Process &process,
+ VTableContextBase &vtable_ctx,
+ ValueObject &valobj,
+ const ASTRecordLayout &record_layout) {
+ // Retrieve type info
+ CompilerType pointee_type;
+ CompilerType this_type(valobj.GetCompilerType());
+ uint32_t type_info = this_type.GetTypeInfo(&pointee_type);
+ if (!type_info)
+ return LLDB_INVALID_ADDRESS;
+
+ // Check if it's a pointer or reference
+ bool ptr_or_ref = false;
+ if (type_info & (eTypeIsPointer | eTypeIsReference)) {
+ ptr_or_ref = true;
+ type_info = pointee_type.GetTypeInfo();
+ }
+
+ // We process only C++ classes
+ const uint32_t cpp_class = eTypeIsClass | eTypeIsCPlusPlus;
+ if ((type_info & cpp_class) != cpp_class)
+ return LLDB_INVALID_ADDRESS;
+
+ // Calculate offset to VTable pointer
+ lldb::offset_t vbtable_ptr_offset =
+ vtable_ctx.isMicrosoft() ? record_layout.getVBPtrOffset().getQuantity()
+ : 0;
+
+ if (ptr_or_ref) {
+ // We have a pointer / ref to object, so read
+ // VTable pointer from process memory
+
+ if (valobj.GetAddressTypeOfChildren() != eAddressTypeLoad)
+ return LLDB_INVALID_ADDRESS;
+
+ auto vbtable_ptr_addr = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ if (vbtable_ptr_addr == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ vbtable_ptr_addr += vbtable_ptr_offset;
+
+ Status err;
+ return process.ReadPointerFromMemory(vbtable_ptr_addr, err);
+ }
+
+ // We have an object already read from process memory,
+ // so just extract VTable pointer from it
+
+ DataExtractor data;
+ Status err;
+ auto size = valobj.GetData(data, err);
+ if (err.Fail() || vbtable_ptr_offset + data.GetAddressByteSize() > size)
+ return LLDB_INVALID_ADDRESS;
+
+ return data.GetPointer(&vbtable_ptr_offset);
+}
+
+static int64_t ReadVBaseOffsetFromVTable(Process &process,
+ VTableContextBase &vtable_ctx,
+ lldb::addr_t vtable_ptr,
+ const CXXRecordDecl *cxx_record_decl,
+ const CXXRecordDecl *base_class_decl) {
+ if (vtable_ctx.isMicrosoft()) {
+ clang::MicrosoftVTableContext &msoft_vtable_ctx =
+ static_cast<clang::MicrosoftVTableContext &>(vtable_ctx);
+
+ // Get the index into the virtual base table. The
+ // index is the index in uint32_t from vbtable_ptr
+ const unsigned vbtable_index =
+ msoft_vtable_ctx.getVBTableIndex(cxx_record_decl, base_class_decl);
+ const lldb::addr_t base_offset_addr = vtable_ptr + vbtable_index * 4;
+ Status err;
+ return process.ReadSignedIntegerFromMemory(base_offset_addr, 4, INT64_MAX,
+ err);
+ }
+
+ clang::ItaniumVTableContext &itanium_vtable_ctx =
+ static_cast<clang::ItaniumVTableContext &>(vtable_ctx);
+
+ clang::CharUnits base_offset_offset =
+ itanium_vtable_ctx.getVirtualBaseOffsetOffset(cxx_record_decl,
+ base_class_decl);
+ const lldb::addr_t base_offset_addr =
+ vtable_ptr + base_offset_offset.getQuantity();
+ const uint32_t base_offset_size = process.GetAddressByteSize();
+ Status err;
+ return process.ReadSignedIntegerFromMemory(base_offset_addr, base_offset_size,
+ INT64_MAX, err);
+}
+
+static bool GetVBaseBitOffset(VTableContextBase &vtable_ctx,
+ ValueObject &valobj,
+ const ASTRecordLayout &record_layout,
+ const CXXRecordDecl *cxx_record_decl,
+ const CXXRecordDecl *base_class_decl,
+ int32_t &bit_offset) {
+ ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (!process)
+ return false;
+
+ lldb::addr_t vtable_ptr =
+ GetVTableAddress(*process, vtable_ctx, valobj, record_layout);
+ if (vtable_ptr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ auto base_offset = ReadVBaseOffsetFromVTable(
+ *process, vtable_ctx, vtable_ptr, cxx_record_decl, base_class_decl);
+ if (base_offset == INT64_MAX)
+ return false;
+
+ bit_offset = base_offset * 8;
+
+ return true;
+}
+
+typedef lldb_private::ThreadSafeDenseMap<clang::ASTContext *, ClangASTContext *>
+ ClangASTMap;
+
+static ClangASTMap &GetASTMap() {
+ static ClangASTMap *g_map_ptr = nullptr;
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ g_map_ptr = new ClangASTMap(); // leaked on purpose to avoid spins
+ });
+ return *g_map_ptr;
+}
+
+bool ClangASTContext::IsOperator(const char *name,
+ clang::OverloadedOperatorKind &op_kind) {
+ if (name == nullptr || name[0] == '\0')
+ return false;
+
+#define OPERATOR_PREFIX "operator"
+#define OPERATOR_PREFIX_LENGTH (sizeof(OPERATOR_PREFIX) - 1)
+
+ const char *post_op_name = nullptr;
+
+ bool no_space = true;
+
+ if (::strncmp(name, OPERATOR_PREFIX, OPERATOR_PREFIX_LENGTH))
+ return false;
+
+ post_op_name = name + OPERATOR_PREFIX_LENGTH;
+
+ if (post_op_name[0] == ' ') {
+ post_op_name++;
+ no_space = false;
+ }
+
+#undef OPERATOR_PREFIX
+#undef OPERATOR_PREFIX_LENGTH
+
+ // This is an operator, set the overloaded operator kind to invalid in case
+ // this is a conversion operator...
+ op_kind = clang::NUM_OVERLOADED_OPERATORS;
+
+ switch (post_op_name[0]) {
+ default:
+ if (no_space)
+ return false;
+ break;
+ case 'n':
+ if (no_space)
+ return false;
+ if (strcmp(post_op_name, "new") == 0)
+ op_kind = clang::OO_New;
+ else if (strcmp(post_op_name, "new[]") == 0)
+ op_kind = clang::OO_Array_New;
+ break;
+
+ case 'd':
+ if (no_space)
+ return false;
+ if (strcmp(post_op_name, "delete") == 0)
+ op_kind = clang::OO_Delete;
+ else if (strcmp(post_op_name, "delete[]") == 0)
+ op_kind = clang::OO_Array_Delete;
+ break;
+
+ case '+':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Plus;
+ else if (post_op_name[2] == '\0') {
+ if (post_op_name[1] == '=')
+ op_kind = clang::OO_PlusEqual;
+ else if (post_op_name[1] == '+')
+ op_kind = clang::OO_PlusPlus;
+ }
+ break;
+
+ case '-':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Minus;
+ else if (post_op_name[2] == '\0') {
+ switch (post_op_name[1]) {
+ case '=':
+ op_kind = clang::OO_MinusEqual;
+ break;
+ case '-':
+ op_kind = clang::OO_MinusMinus;
+ break;
+ case '>':
+ op_kind = clang::OO_Arrow;
+ break;
+ }
+ } else if (post_op_name[3] == '\0') {
+ if (post_op_name[2] == '*')
+ op_kind = clang::OO_ArrowStar;
+ break;
+ }
+ break;
+
+ case '*':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Star;
+ else if (post_op_name[1] == '=' && post_op_name[2] == '\0')
+ op_kind = clang::OO_StarEqual;
+ break;
+
+ case '/':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Slash;
+ else if (post_op_name[1] == '=' && post_op_name[2] == '\0')
+ op_kind = clang::OO_SlashEqual;
+ break;
+
+ case '%':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Percent;
+ else if (post_op_name[1] == '=' && post_op_name[2] == '\0')
+ op_kind = clang::OO_PercentEqual;
+ break;
+
+ case '^':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Caret;
+ else if (post_op_name[1] == '=' && post_op_name[2] == '\0')
+ op_kind = clang::OO_CaretEqual;
+ break;
+
+ case '&':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Amp;
+ else if (post_op_name[2] == '\0') {
+ switch (post_op_name[1]) {
+ case '=':
+ op_kind = clang::OO_AmpEqual;
+ break;
+ case '&':
+ op_kind = clang::OO_AmpAmp;
+ break;
+ }
+ }
+ break;
+
+ case '|':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Pipe;
+ else if (post_op_name[2] == '\0') {
+ switch (post_op_name[1]) {
+ case '=':
+ op_kind = clang::OO_PipeEqual;
+ break;
+ case '|':
+ op_kind = clang::OO_PipePipe;
+ break;
+ }
+ }
+ break;
+
+ case '~':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Tilde;
+ break;
+
+ case '!':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Exclaim;
+ else if (post_op_name[1] == '=' && post_op_name[2] == '\0')
+ op_kind = clang::OO_ExclaimEqual;
+ break;
+
+ case '=':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Equal;
+ else if (post_op_name[1] == '=' && post_op_name[2] == '\0')
+ op_kind = clang::OO_EqualEqual;
+ break;
+
+ case '<':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Less;
+ else if (post_op_name[2] == '\0') {
+ switch (post_op_name[1]) {
+ case '<':
+ op_kind = clang::OO_LessLess;
+ break;
+ case '=':
+ op_kind = clang::OO_LessEqual;
+ break;
+ }
+ } else if (post_op_name[3] == '\0') {
+ if (post_op_name[2] == '=')
+ op_kind = clang::OO_LessLessEqual;
+ }
+ break;
+
+ case '>':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Greater;
+ else if (post_op_name[2] == '\0') {
+ switch (post_op_name[1]) {
+ case '>':
+ op_kind = clang::OO_GreaterGreater;
+ break;
+ case '=':
+ op_kind = clang::OO_GreaterEqual;
+ break;
+ }
+ } else if (post_op_name[1] == '>' && post_op_name[2] == '=' &&
+ post_op_name[3] == '\0') {
+ op_kind = clang::OO_GreaterGreaterEqual;
+ }
+ break;
+
+ case ',':
+ if (post_op_name[1] == '\0')
+ op_kind = clang::OO_Comma;
+ break;
+
+ case '(':
+ if (post_op_name[1] == ')' && post_op_name[2] == '\0')
+ op_kind = clang::OO_Call;
+ break;
+
+ case '[':
+ if (post_op_name[1] == ']' && post_op_name[2] == '\0')
+ op_kind = clang::OO_Subscript;
+ break;
+ }
+
+ return true;
+}
+
+clang::AccessSpecifier
+ClangASTContext::ConvertAccessTypeToAccessSpecifier(AccessType access) {
+ switch (access) {
+ default:
+ break;
+ case eAccessNone:
+ return AS_none;
+ case eAccessPublic:
+ return AS_public;
+ case eAccessPrivate:
+ return AS_private;
+ case eAccessProtected:
+ return AS_protected;
+ }
+ return AS_none;
+}
+
+static void ParseLangArgs(LangOptions &Opts, InputKind IK, const char *triple) {
+ // FIXME: Cleanup per-file based stuff.
+
+ // Set some properties which depend solely on the input kind; it would be
+ // nice to move these to the language standard, and have the driver resolve
+ // the input kind + language standard.
+ if (IK.getLanguage() == InputKind::Asm) {
+ Opts.AsmPreprocessor = 1;
+ } else if (IK.isObjectiveC()) {
+ Opts.ObjC = 1;
+ }
+
+ LangStandard::Kind LangStd = LangStandard::lang_unspecified;
+
+ if (LangStd == LangStandard::lang_unspecified) {
+ // Based on the base language, pick one.
+ switch (IK.getLanguage()) {
+ case InputKind::Unknown:
+ case InputKind::LLVM_IR:
+ case InputKind::RenderScript:
+ llvm_unreachable("Invalid input kind!");
+ case InputKind::OpenCL:
+ LangStd = LangStandard::lang_opencl10;
+ break;
+ case InputKind::CUDA:
+ LangStd = LangStandard::lang_cuda;
+ break;
+ case InputKind::Asm:
+ case InputKind::C:
+ case InputKind::ObjC:
+ LangStd = LangStandard::lang_gnu99;
+ break;
+ case InputKind::CXX:
+ case InputKind::ObjCXX:
+ LangStd = LangStandard::lang_gnucxx98;
+ break;
+ case InputKind::HIP:
+ LangStd = LangStandard::lang_hip;
+ break;
+ }
+ }
+
+ const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd);
+ Opts.LineComment = Std.hasLineComments();
+ Opts.C99 = Std.isC99();
+ Opts.CPlusPlus = Std.isCPlusPlus();
+ Opts.CPlusPlus11 = Std.isCPlusPlus11();
+ Opts.Digraphs = Std.hasDigraphs();
+ Opts.GNUMode = Std.isGNUMode();
+ Opts.GNUInline = !Std.isC99();
+ Opts.HexFloats = Std.hasHexFloats();
+ Opts.ImplicitInt = Std.hasImplicitInt();
+
+ Opts.WChar = true;
+
+ // OpenCL has some additional defaults.
+ if (LangStd == LangStandard::lang_opencl10) {
+ Opts.OpenCL = 1;
+ Opts.AltiVec = 1;
+ Opts.CXXOperatorNames = 1;
+ Opts.LaxVectorConversions = 1;
+ }
+
+ // OpenCL and C++ both have bool, true, false keywords.
+ Opts.Bool = Opts.OpenCL || Opts.CPlusPlus;
+
+ Opts.setValueVisibilityMode(DefaultVisibility);
+
+ // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs is
+ // specified, or -std is set to a conforming mode.
+ Opts.Trigraphs = !Opts.GNUMode;
+ Opts.CharIsSigned = ArchSpec(triple).CharIsSignedByDefault();
+ Opts.OptimizeSize = 0;
+
+ // FIXME: Eliminate this dependency.
+ // unsigned Opt =
+ // Args.hasArg(OPT_Os) ? 2 : getLastArgIntValue(Args, OPT_O, 0, Diags);
+ // Opts.Optimize = Opt != 0;
+ unsigned Opt = 0;
+
+ // This is the __NO_INLINE__ define, which just depends on things like the
+ // optimization level and -fno-inline, not actually whether the backend has
+ // inlining enabled.
+ //
+ // FIXME: This is affected by other options (-fno-inline).
+ Opts.NoInlineDefine = !Opt;
+}
+
+ClangASTContext::ClangASTContext(const char *target_triple)
+ : TypeSystem(TypeSystem::eKindClang), m_target_triple(), m_ast_up(),
+ m_language_options_up(), m_source_manager_up(), m_diagnostics_engine_up(),
+ m_target_options_rp(), m_target_info_up(), m_identifier_table_up(),
+ m_selector_table_up(), m_builtins_up(), m_callback_tag_decl(nullptr),
+ m_callback_objc_decl(nullptr), m_callback_baton(nullptr),
+ m_pointer_byte_size(0), m_ast_owned(false) {
+ if (target_triple && target_triple[0])
+ SetTargetTriple(target_triple);
+}
+
+// Destructor
+ClangASTContext::~ClangASTContext() { Finalize(); }
+
+ConstString ClangASTContext::GetPluginNameStatic() {
+ return ConstString("clang");
+}
+
+ConstString ClangASTContext::GetPluginName() {
+ return ClangASTContext::GetPluginNameStatic();
+}
+
+uint32_t ClangASTContext::GetPluginVersion() { return 1; }
+
+lldb::TypeSystemSP ClangASTContext::CreateInstance(lldb::LanguageType language,
+ lldb_private::Module *module,
+ Target *target) {
+ if (ClangASTContextSupportsLanguage(language)) {
+ ArchSpec arch;
+ if (module)
+ arch = module->GetArchitecture();
+ else if (target)
+ arch = target->GetArchitecture();
+
+ if (arch.IsValid()) {
+ ArchSpec fixed_arch = arch;
+ // LLVM wants this to be set to iOS or MacOSX; if we're working on
+ // a bare-boards type image, change the triple for llvm's benefit.
+ if (fixed_arch.GetTriple().getVendor() == llvm::Triple::Apple &&
+ fixed_arch.GetTriple().getOS() == llvm::Triple::UnknownOS) {
+ if (fixed_arch.GetTriple().getArch() == llvm::Triple::arm ||
+ fixed_arch.GetTriple().getArch() == llvm::Triple::aarch64 ||
+ fixed_arch.GetTriple().getArch() == llvm::Triple::thumb) {
+ fixed_arch.GetTriple().setOS(llvm::Triple::IOS);
+ } else {
+ fixed_arch.GetTriple().setOS(llvm::Triple::MacOSX);
+ }
+ }
+
+ if (module) {
+ std::shared_ptr<ClangASTContext> ast_sp(new ClangASTContext);
+ if (ast_sp) {
+ ast_sp->SetArchitecture(fixed_arch);
+ }
+ return ast_sp;
+ } else if (target && target->IsValid()) {
+ std::shared_ptr<ClangASTContextForExpressions> ast_sp(
+ new ClangASTContextForExpressions(*target));
+ if (ast_sp) {
+ ast_sp->SetArchitecture(fixed_arch);
+ ast_sp->m_scratch_ast_source_up.reset(
+ new ClangASTSource(target->shared_from_this()));
+ lldbassert(ast_sp->getFileManager());
+ ast_sp->m_scratch_ast_source_up->InstallASTContext(
+ *ast_sp->getASTContext(), *ast_sp->getFileManager(), true);
+ llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> proxy_ast_source(
+ ast_sp->m_scratch_ast_source_up->CreateProxy());
+ ast_sp->SetExternalSource(proxy_ast_source);
+ return ast_sp;
+ }
+ }
+ }
+ }
+ return lldb::TypeSystemSP();
+}
+
+void ClangASTContext::EnumerateSupportedLanguages(
+ std::set<lldb::LanguageType> &languages_for_types,
+ std::set<lldb::LanguageType> &languages_for_expressions) {
+ static std::vector<lldb::LanguageType> s_supported_languages_for_types(
+ {lldb::eLanguageTypeC89, lldb::eLanguageTypeC, lldb::eLanguageTypeC11,
+ lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeC99,
+ lldb::eLanguageTypeObjC, lldb::eLanguageTypeObjC_plus_plus,
+ lldb::eLanguageTypeC_plus_plus_03, lldb::eLanguageTypeC_plus_plus_11,
+ lldb::eLanguageTypeC11, lldb::eLanguageTypeC_plus_plus_14});
+
+ static std::vector<lldb::LanguageType> s_supported_languages_for_expressions(
+ {lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeObjC_plus_plus,
+ lldb::eLanguageTypeC_plus_plus_03, lldb::eLanguageTypeC_plus_plus_11,
+ lldb::eLanguageTypeC_plus_plus_14});
+
+ languages_for_types.insert(s_supported_languages_for_types.begin(),
+ s_supported_languages_for_types.end());
+ languages_for_expressions.insert(
+ s_supported_languages_for_expressions.begin(),
+ s_supported_languages_for_expressions.end());
+}
+
+void ClangASTContext::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "clang base AST context plug-in",
+ CreateInstance, EnumerateSupportedLanguages);
+}
+
+void ClangASTContext::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+void ClangASTContext::Finalize() {
+ if (m_ast_up) {
+ GetASTMap().Erase(m_ast_up.get());
+ if (!m_ast_owned)
+ m_ast_up.release();
+ }
+
+ m_builtins_up.reset();
+ m_selector_table_up.reset();
+ m_identifier_table_up.reset();
+ m_target_info_up.reset();
+ m_target_options_rp.reset();
+ m_diagnostics_engine_up.reset();
+ m_source_manager_up.reset();
+ m_language_options_up.reset();
+ m_ast_up.reset();
+ m_scratch_ast_source_up.reset();
+}
+
+void ClangASTContext::Clear() {
+ m_ast_up.reset();
+ m_language_options_up.reset();
+ m_source_manager_up.reset();
+ m_diagnostics_engine_up.reset();
+ m_target_options_rp.reset();
+ m_target_info_up.reset();
+ m_identifier_table_up.reset();
+ m_selector_table_up.reset();
+ m_builtins_up.reset();
+ m_pointer_byte_size = 0;
+}
+
+void ClangASTContext::setSema(Sema *s) {
+ // Ensure that the new sema actually belongs to our ASTContext.
+ assert(s == nullptr || &s->getASTContext() == m_ast_up.get());
+ m_sema = s;
+}
+
+const char *ClangASTContext::GetTargetTriple() {
+ return m_target_triple.c_str();
+}
+
+void ClangASTContext::SetTargetTriple(const char *target_triple) {
+ Clear();
+ m_target_triple.assign(target_triple);
+}
+
+void ClangASTContext::SetArchitecture(const ArchSpec &arch) {
+ SetTargetTriple(arch.GetTriple().str().c_str());
+}
+
+bool ClangASTContext::HasExternalSource() {
+ ASTContext *ast = getASTContext();
+ if (ast)
+ return ast->getExternalSource() != nullptr;
+ return false;
+}
+
+void ClangASTContext::SetExternalSource(
+ llvm::IntrusiveRefCntPtr<ExternalASTSource> &ast_source_up) {
+ ASTContext *ast = getASTContext();
+ if (ast) {
+ ast->setExternalSource(ast_source_up);
+ ast->getTranslationUnitDecl()->setHasExternalLexicalStorage(true);
+ }
+}
+
+void ClangASTContext::RemoveExternalSource() {
+ ASTContext *ast = getASTContext();
+
+ if (ast) {
+ llvm::IntrusiveRefCntPtr<ExternalASTSource> empty_ast_source_up;
+ ast->setExternalSource(empty_ast_source_up);
+ ast->getTranslationUnitDecl()->setHasExternalLexicalStorage(false);
+ }
+}
+
+void ClangASTContext::setASTContext(clang::ASTContext *ast_ctx) {
+ if (!m_ast_owned) {
+ m_ast_up.release();
+ }
+ m_ast_owned = false;
+ m_ast_up.reset(ast_ctx);
+ GetASTMap().Insert(ast_ctx, this);
+}
+
+ASTContext *ClangASTContext::getASTContext() {
+ if (m_ast_up == nullptr) {
+ m_ast_owned = true;
+ m_ast_up.reset(new ASTContext(*getLanguageOptions(), *getSourceManager(),
+ *getIdentifierTable(), *getSelectorTable(),
+ *getBuiltinContext()));
+
+ m_ast_up->getDiagnostics().setClient(getDiagnosticConsumer(), false);
+
+ // This can be NULL if we don't know anything about the architecture or if
+ // the target for an architecture isn't enabled in the llvm/clang that we
+ // built
+ TargetInfo *target_info = getTargetInfo();
+ if (target_info)
+ m_ast_up->InitBuiltinTypes(*target_info);
+
+ if ((m_callback_tag_decl || m_callback_objc_decl) && m_callback_baton) {
+ m_ast_up->getTranslationUnitDecl()->setHasExternalLexicalStorage();
+ // m_ast_up->getTranslationUnitDecl()->setHasExternalVisibleStorage();
+ }
+
+ GetASTMap().Insert(m_ast_up.get(), this);
+
+ llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> ast_source_up(
+ new ClangExternalASTSourceCallbacks(
+ ClangASTContext::CompleteTagDecl,
+ ClangASTContext::CompleteObjCInterfaceDecl, nullptr,
+ ClangASTContext::LayoutRecordType, this));
+ SetExternalSource(ast_source_up);
+ }
+ return m_ast_up.get();
+}
+
+ClangASTContext *ClangASTContext::GetASTContext(clang::ASTContext *ast) {
+ ClangASTContext *clang_ast = GetASTMap().Lookup(ast);
+ return clang_ast;
+}
+
+Builtin::Context *ClangASTContext::getBuiltinContext() {
+ if (m_builtins_up == nullptr)
+ m_builtins_up.reset(new Builtin::Context());
+ return m_builtins_up.get();
+}
+
+IdentifierTable *ClangASTContext::getIdentifierTable() {
+ if (m_identifier_table_up == nullptr)
+ m_identifier_table_up.reset(
+ new IdentifierTable(*ClangASTContext::getLanguageOptions(), nullptr));
+ return m_identifier_table_up.get();
+}
+
+LangOptions *ClangASTContext::getLanguageOptions() {
+ if (m_language_options_up == nullptr) {
+ m_language_options_up.reset(new LangOptions());
+ ParseLangArgs(*m_language_options_up, InputKind::ObjCXX, GetTargetTriple());
+ // InitializeLangOptions(*m_language_options_up, InputKind::ObjCXX);
+ }
+ return m_language_options_up.get();
+}
+
+SelectorTable *ClangASTContext::getSelectorTable() {
+ if (m_selector_table_up == nullptr)
+ m_selector_table_up.reset(new SelectorTable());
+ return m_selector_table_up.get();
+}
+
+clang::FileManager *ClangASTContext::getFileManager() {
+ if (m_file_manager_up == nullptr) {
+ clang::FileSystemOptions file_system_options;
+ m_file_manager_up.reset(new clang::FileManager(
+ file_system_options, FileSystem::Instance().GetVirtualFileSystem()));
+ }
+ return m_file_manager_up.get();
+}
+
+clang::SourceManager *ClangASTContext::getSourceManager() {
+ if (m_source_manager_up == nullptr)
+ m_source_manager_up.reset(
+ new clang::SourceManager(*getDiagnosticsEngine(), *getFileManager()));
+ return m_source_manager_up.get();
+}
+
+clang::DiagnosticsEngine *ClangASTContext::getDiagnosticsEngine() {
+ if (m_diagnostics_engine_up == nullptr) {
+ llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_id_sp(new DiagnosticIDs());
+ m_diagnostics_engine_up.reset(
+ new DiagnosticsEngine(diag_id_sp, new DiagnosticOptions()));
+ }
+ return m_diagnostics_engine_up.get();
+}
+
+clang::MangleContext *ClangASTContext::getMangleContext() {
+ if (m_mangle_ctx_up == nullptr)
+ m_mangle_ctx_up.reset(getASTContext()->createMangleContext());
+ return m_mangle_ctx_up.get();
+}
+
+class NullDiagnosticConsumer : public DiagnosticConsumer {
+public:
+ NullDiagnosticConsumer() {
+ m_log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ }
+
+ void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+ const clang::Diagnostic &info) override {
+ if (m_log) {
+ llvm::SmallVector<char, 32> diag_str(10);
+ info.FormatDiagnostic(diag_str);
+ diag_str.push_back('\0');
+ m_log->Printf("Compiler diagnostic: %s\n", diag_str.data());
+ }
+ }
+
+ DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
+ return new NullDiagnosticConsumer();
+ }
+
+private:
+ Log *m_log;
+};
+
+DiagnosticConsumer *ClangASTContext::getDiagnosticConsumer() {
+ if (m_diagnostic_consumer_up == nullptr)
+ m_diagnostic_consumer_up.reset(new NullDiagnosticConsumer);
+
+ return m_diagnostic_consumer_up.get();
+}
+
+std::shared_ptr<clang::TargetOptions> &ClangASTContext::getTargetOptions() {
+ if (m_target_options_rp == nullptr && !m_target_triple.empty()) {
+ m_target_options_rp = std::make_shared<clang::TargetOptions>();
+ if (m_target_options_rp != nullptr)
+ m_target_options_rp->Triple = m_target_triple;
+ }
+ return m_target_options_rp;
+}
+
+TargetInfo *ClangASTContext::getTargetInfo() {
+ // target_triple should be something like "x86_64-apple-macosx"
+ if (m_target_info_up == nullptr && !m_target_triple.empty())
+ m_target_info_up.reset(TargetInfo::CreateTargetInfo(*getDiagnosticsEngine(),
+ getTargetOptions()));
+ return m_target_info_up.get();
+}
+
+#pragma mark Basic Types
+
+static inline bool QualTypeMatchesBitSize(const uint64_t bit_size,
+ ASTContext *ast, QualType qual_type) {
+ uint64_t qual_type_bit_size = ast->getTypeSize(qual_type);
+ return qual_type_bit_size == bit_size;
+}
+
+CompilerType
+ClangASTContext::GetBuiltinTypeForEncodingAndBitSize(Encoding encoding,
+ size_t bit_size) {
+ return ClangASTContext::GetBuiltinTypeForEncodingAndBitSize(
+ getASTContext(), encoding, bit_size);
+}
+
+CompilerType ClangASTContext::GetBuiltinTypeForEncodingAndBitSize(
+ ASTContext *ast, Encoding encoding, uint32_t bit_size) {
+ if (!ast)
+ return CompilerType();
+ switch (encoding) {
+ case eEncodingInvalid:
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->VoidPtrTy))
+ return CompilerType(ast, ast->VoidPtrTy);
+ break;
+
+ case eEncodingUint:
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy))
+ return CompilerType(ast, ast->UnsignedCharTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy))
+ return CompilerType(ast, ast->UnsignedShortTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy))
+ return CompilerType(ast, ast->UnsignedIntTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongTy))
+ return CompilerType(ast, ast->UnsignedLongTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongLongTy))
+ return CompilerType(ast, ast->UnsignedLongLongTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedInt128Ty))
+ return CompilerType(ast, ast->UnsignedInt128Ty);
+ break;
+
+ case eEncodingSint:
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->SignedCharTy))
+ return CompilerType(ast, ast->SignedCharTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->ShortTy))
+ return CompilerType(ast, ast->ShortTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->IntTy))
+ return CompilerType(ast, ast->IntTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->LongTy))
+ return CompilerType(ast, ast->LongTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->LongLongTy))
+ return CompilerType(ast, ast->LongLongTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->Int128Ty))
+ return CompilerType(ast, ast->Int128Ty);
+ break;
+
+ case eEncodingIEEE754:
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->FloatTy))
+ return CompilerType(ast, ast->FloatTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->DoubleTy))
+ return CompilerType(ast, ast->DoubleTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleTy))
+ return CompilerType(ast, ast->LongDoubleTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->HalfTy))
+ return CompilerType(ast, ast->HalfTy);
+ break;
+
+ case eEncodingVector:
+ // Sanity check that bit_size is a multiple of 8's.
+ if (bit_size && !(bit_size & 0x7u))
+ return CompilerType(
+ ast, ast->getExtVectorType(ast->UnsignedCharTy, bit_size / 8));
+ break;
+ }
+
+ return CompilerType();
+}
+
+lldb::BasicType
+ClangASTContext::GetBasicTypeEnumeration(ConstString name) {
+ if (name) {
+ typedef UniqueCStringMap<lldb::BasicType> TypeNameToBasicTypeMap;
+ static TypeNameToBasicTypeMap g_type_map;
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ // "void"
+ g_type_map.Append(ConstString("void"), eBasicTypeVoid);
+
+ // "char"
+ g_type_map.Append(ConstString("char"), eBasicTypeChar);
+ g_type_map.Append(ConstString("signed char"), eBasicTypeSignedChar);
+ g_type_map.Append(ConstString("unsigned char"), eBasicTypeUnsignedChar);
+ g_type_map.Append(ConstString("wchar_t"), eBasicTypeWChar);
+ g_type_map.Append(ConstString("signed wchar_t"), eBasicTypeSignedWChar);
+ g_type_map.Append(ConstString("unsigned wchar_t"),
+ eBasicTypeUnsignedWChar);
+ // "short"
+ g_type_map.Append(ConstString("short"), eBasicTypeShort);
+ g_type_map.Append(ConstString("short int"), eBasicTypeShort);
+ g_type_map.Append(ConstString("unsigned short"), eBasicTypeUnsignedShort);
+ g_type_map.Append(ConstString("unsigned short int"),
+ eBasicTypeUnsignedShort);
+
+ // "int"
+ g_type_map.Append(ConstString("int"), eBasicTypeInt);
+ g_type_map.Append(ConstString("signed int"), eBasicTypeInt);
+ g_type_map.Append(ConstString("unsigned int"), eBasicTypeUnsignedInt);
+ g_type_map.Append(ConstString("unsigned"), eBasicTypeUnsignedInt);
+
+ // "long"
+ g_type_map.Append(ConstString("long"), eBasicTypeLong);
+ g_type_map.Append(ConstString("long int"), eBasicTypeLong);
+ g_type_map.Append(ConstString("unsigned long"), eBasicTypeUnsignedLong);
+ g_type_map.Append(ConstString("unsigned long int"),
+ eBasicTypeUnsignedLong);
+
+ // "long long"
+ g_type_map.Append(ConstString("long long"), eBasicTypeLongLong);
+ g_type_map.Append(ConstString("long long int"), eBasicTypeLongLong);
+ g_type_map.Append(ConstString("unsigned long long"),
+ eBasicTypeUnsignedLongLong);
+ g_type_map.Append(ConstString("unsigned long long int"),
+ eBasicTypeUnsignedLongLong);
+
+ // "int128"
+ g_type_map.Append(ConstString("__int128_t"), eBasicTypeInt128);
+ g_type_map.Append(ConstString("__uint128_t"), eBasicTypeUnsignedInt128);
+
+ // Miscellaneous
+ g_type_map.Append(ConstString("bool"), eBasicTypeBool);
+ g_type_map.Append(ConstString("float"), eBasicTypeFloat);
+ g_type_map.Append(ConstString("double"), eBasicTypeDouble);
+ g_type_map.Append(ConstString("long double"), eBasicTypeLongDouble);
+ g_type_map.Append(ConstString("id"), eBasicTypeObjCID);
+ g_type_map.Append(ConstString("SEL"), eBasicTypeObjCSel);
+ g_type_map.Append(ConstString("nullptr"), eBasicTypeNullPtr);
+ g_type_map.Sort();
+ });
+
+ return g_type_map.Find(name, eBasicTypeInvalid);
+ }
+ return eBasicTypeInvalid;
+}
+
+CompilerType ClangASTContext::GetBasicType(ASTContext *ast,
+ ConstString name) {
+ if (ast) {
+ lldb::BasicType basic_type = ClangASTContext::GetBasicTypeEnumeration(name);
+ return ClangASTContext::GetBasicType(ast, basic_type);
+ }
+ return CompilerType();
+}
+
+uint32_t ClangASTContext::GetPointerByteSize() {
+ if (m_pointer_byte_size == 0)
+ if (auto size = GetBasicType(lldb::eBasicTypeVoid)
+ .GetPointerType()
+ .GetByteSize(nullptr))
+ m_pointer_byte_size = *size;
+ return m_pointer_byte_size;
+}
+
+CompilerType ClangASTContext::GetBasicType(lldb::BasicType basic_type) {
+ return GetBasicType(getASTContext(), basic_type);
+}
+
+CompilerType ClangASTContext::GetBasicType(ASTContext *ast,
+ lldb::BasicType basic_type) {
+ if (!ast)
+ return CompilerType();
+ lldb::opaque_compiler_type_t clang_type =
+ GetOpaqueCompilerType(ast, basic_type);
+
+ if (clang_type)
+ return CompilerType(GetASTContext(ast), clang_type);
+ return CompilerType();
+}
+
+CompilerType ClangASTContext::GetBuiltinTypeForDWARFEncodingAndBitSize(
+ const char *type_name, uint32_t dw_ate, uint32_t bit_size) {
+ ASTContext *ast = getASTContext();
+
+#define streq(a, b) strcmp(a, b) == 0
+ assert(ast != nullptr);
+ if (ast) {
+ switch (dw_ate) {
+ default:
+ break;
+
+ case DW_ATE_address:
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->VoidPtrTy))
+ return CompilerType(ast, ast->VoidPtrTy);
+ break;
+
+ case DW_ATE_boolean:
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->BoolTy))
+ return CompilerType(ast, ast->BoolTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy))
+ return CompilerType(ast, ast->UnsignedCharTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy))
+ return CompilerType(ast, ast->UnsignedShortTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy))
+ return CompilerType(ast, ast->UnsignedIntTy);
+ break;
+
+ case DW_ATE_lo_user:
+ // This has been seen to mean DW_AT_complex_integer
+ if (type_name) {
+ if (::strstr(type_name, "complex")) {
+ CompilerType complex_int_clang_type =
+ GetBuiltinTypeForDWARFEncodingAndBitSize("int", DW_ATE_signed,
+ bit_size / 2);
+ return CompilerType(ast, ast->getComplexType(ClangUtil::GetQualType(
+ complex_int_clang_type)));
+ }
+ }
+ break;
+
+ case DW_ATE_complex_float:
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->FloatComplexTy))
+ return CompilerType(ast, ast->FloatComplexTy);
+ else if (QualTypeMatchesBitSize(bit_size, ast, ast->DoubleComplexTy))
+ return CompilerType(ast, ast->DoubleComplexTy);
+ else if (QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleComplexTy))
+ return CompilerType(ast, ast->LongDoubleComplexTy);
+ else {
+ CompilerType complex_float_clang_type =
+ GetBuiltinTypeForDWARFEncodingAndBitSize("float", DW_ATE_float,
+ bit_size / 2);
+ return CompilerType(ast, ast->getComplexType(ClangUtil::GetQualType(
+ complex_float_clang_type)));
+ }
+ break;
+
+ case DW_ATE_float:
+ if (streq(type_name, "float") &&
+ QualTypeMatchesBitSize(bit_size, ast, ast->FloatTy))
+ return CompilerType(ast, ast->FloatTy);
+ if (streq(type_name, "double") &&
+ QualTypeMatchesBitSize(bit_size, ast, ast->DoubleTy))
+ return CompilerType(ast, ast->DoubleTy);
+ if (streq(type_name, "long double") &&
+ QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleTy))
+ return CompilerType(ast, ast->LongDoubleTy);
+ // Fall back to not requiring a name match
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->FloatTy))
+ return CompilerType(ast, ast->FloatTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->DoubleTy))
+ return CompilerType(ast, ast->DoubleTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleTy))
+ return CompilerType(ast, ast->LongDoubleTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->HalfTy))
+ return CompilerType(ast, ast->HalfTy);
+ break;
+
+ case DW_ATE_signed:
+ if (type_name) {
+ if (streq(type_name, "wchar_t") &&
+ QualTypeMatchesBitSize(bit_size, ast, ast->WCharTy) &&
+ (getTargetInfo() &&
+ TargetInfo::isTypeSigned(getTargetInfo()->getWCharType())))
+ return CompilerType(ast, ast->WCharTy);
+ if (streq(type_name, "void") &&
+ QualTypeMatchesBitSize(bit_size, ast, ast->VoidTy))
+ return CompilerType(ast, ast->VoidTy);
+ if (strstr(type_name, "long long") &&
+ QualTypeMatchesBitSize(bit_size, ast, ast->LongLongTy))
+ return CompilerType(ast, ast->LongLongTy);
+ if (strstr(type_name, "long") &&
+ QualTypeMatchesBitSize(bit_size, ast, ast->LongTy))
+ return CompilerType(ast, ast->LongTy);
+ if (strstr(type_name, "short") &&
+ QualTypeMatchesBitSize(bit_size, ast, ast->ShortTy))
+ return CompilerType(ast, ast->ShortTy);
+ if (strstr(type_name, "char")) {
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy))
+ return CompilerType(ast, ast->CharTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->SignedCharTy))
+ return CompilerType(ast, ast->SignedCharTy);
+ }
+ if (strstr(type_name, "int")) {
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->IntTy))
+ return CompilerType(ast, ast->IntTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->Int128Ty))
+ return CompilerType(ast, ast->Int128Ty);
+ }
+ }
+ // We weren't able to match up a type name, just search by size
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy))
+ return CompilerType(ast, ast->CharTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->ShortTy))
+ return CompilerType(ast, ast->ShortTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->IntTy))
+ return CompilerType(ast, ast->IntTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->LongTy))
+ return CompilerType(ast, ast->LongTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->LongLongTy))
+ return CompilerType(ast, ast->LongLongTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->Int128Ty))
+ return CompilerType(ast, ast->Int128Ty);
+ break;
+
+ case DW_ATE_signed_char:
+ if (ast->getLangOpts().CharIsSigned && type_name &&
+ streq(type_name, "char")) {
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy))
+ return CompilerType(ast, ast->CharTy);
+ }
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->SignedCharTy))
+ return CompilerType(ast, ast->SignedCharTy);
+ break;
+
+ case DW_ATE_unsigned:
+ if (type_name) {
+ if (streq(type_name, "wchar_t")) {
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->WCharTy)) {
+ if (!(getTargetInfo() &&
+ TargetInfo::isTypeSigned(getTargetInfo()->getWCharType())))
+ return CompilerType(ast, ast->WCharTy);
+ }
+ }
+ if (strstr(type_name, "long long")) {
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongLongTy))
+ return CompilerType(ast, ast->UnsignedLongLongTy);
+ } else if (strstr(type_name, "long")) {
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongTy))
+ return CompilerType(ast, ast->UnsignedLongTy);
+ } else if (strstr(type_name, "short")) {
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy))
+ return CompilerType(ast, ast->UnsignedShortTy);
+ } else if (strstr(type_name, "char")) {
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy))
+ return CompilerType(ast, ast->UnsignedCharTy);
+ } else if (strstr(type_name, "int")) {
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy))
+ return CompilerType(ast, ast->UnsignedIntTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedInt128Ty))
+ return CompilerType(ast, ast->UnsignedInt128Ty);
+ }
+ }
+ // We weren't able to match up a type name, just search by size
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy))
+ return CompilerType(ast, ast->UnsignedCharTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy))
+ return CompilerType(ast, ast->UnsignedShortTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy))
+ return CompilerType(ast, ast->UnsignedIntTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongTy))
+ return CompilerType(ast, ast->UnsignedLongTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongLongTy))
+ return CompilerType(ast, ast->UnsignedLongLongTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedInt128Ty))
+ return CompilerType(ast, ast->UnsignedInt128Ty);
+ break;
+
+ case DW_ATE_unsigned_char:
+ if (!ast->getLangOpts().CharIsSigned && type_name &&
+ streq(type_name, "char")) {
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy))
+ return CompilerType(ast, ast->CharTy);
+ }
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy))
+ return CompilerType(ast, ast->UnsignedCharTy);
+ if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy))
+ return CompilerType(ast, ast->UnsignedShortTy);
+ break;
+
+ case DW_ATE_imaginary_float:
+ break;
+
+ case DW_ATE_UTF:
+ if (type_name) {
+ if (streq(type_name, "char16_t")) {
+ return CompilerType(ast, ast->Char16Ty);
+ } else if (streq(type_name, "char32_t")) {
+ return CompilerType(ast, ast->Char32Ty);
+ }
+ }
+ break;
+ }
+ }
+ // This assert should fire for anything that we don't catch above so we know
+ // to fix any issues we run into.
+ if (type_name) {
+ Host::SystemLog(Host::eSystemLogError, "error: need to add support for "
+ "DW_TAG_base_type '%s' encoded with "
+ "DW_ATE = 0x%x, bit_size = %u\n",
+ type_name, dw_ate, bit_size);
+ } else {
+ Host::SystemLog(Host::eSystemLogError, "error: need to add support for "
+ "DW_TAG_base_type encoded with "
+ "DW_ATE = 0x%x, bit_size = %u\n",
+ dw_ate, bit_size);
+ }
+ return CompilerType();
+}
+
+CompilerType ClangASTContext::GetUnknownAnyType(clang::ASTContext *ast) {
+ if (ast)
+ return CompilerType(ast, ast->UnknownAnyTy);
+ return CompilerType();
+}
+
+CompilerType ClangASTContext::GetCStringType(bool is_const) {
+ ASTContext *ast = getASTContext();
+ QualType char_type(ast->CharTy);
+
+ if (is_const)
+ char_type.addConst();
+
+ return CompilerType(ast, ast->getPointerType(char_type));
+}
+
+clang::DeclContext *
+ClangASTContext::GetTranslationUnitDecl(clang::ASTContext *ast) {
+ return ast->getTranslationUnitDecl();
+}
+
+clang::Decl *ClangASTContext::CopyDecl(ASTContext *dst_ast, ASTContext *src_ast,
+ clang::Decl *source_decl) {
+ FileSystemOptions file_system_options;
+ FileManager file_manager(file_system_options);
+ ASTImporter importer(*dst_ast, file_manager, *src_ast, file_manager, false);
+
+ if (llvm::Expected<clang::Decl *> ret_or_error =
+ importer.Import(source_decl)) {
+ return *ret_or_error;
+ } else {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ LLDB_LOG_ERROR(log, ret_or_error.takeError(), "Couldn't import decl: {0}");
+ return nullptr;
+ }
+}
+
+bool ClangASTContext::AreTypesSame(CompilerType type1, CompilerType type2,
+ bool ignore_qualifiers) {
+ ClangASTContext *ast =
+ llvm::dyn_cast_or_null<ClangASTContext>(type1.GetTypeSystem());
+ if (!ast || ast != type2.GetTypeSystem())
+ return false;
+
+ if (type1.GetOpaqueQualType() == type2.GetOpaqueQualType())
+ return true;
+
+ QualType type1_qual = ClangUtil::GetQualType(type1);
+ QualType type2_qual = ClangUtil::GetQualType(type2);
+
+ if (ignore_qualifiers) {
+ type1_qual = type1_qual.getUnqualifiedType();
+ type2_qual = type2_qual.getUnqualifiedType();
+ }
+
+ return ast->getASTContext()->hasSameType(type1_qual, type2_qual);
+}
+
+CompilerType ClangASTContext::GetTypeForDecl(clang::NamedDecl *decl) {
+ if (clang::ObjCInterfaceDecl *interface_decl =
+ llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl))
+ return GetTypeForDecl(interface_decl);
+ if (clang::TagDecl *tag_decl = llvm::dyn_cast<clang::TagDecl>(decl))
+ return GetTypeForDecl(tag_decl);
+ return CompilerType();
+}
+
+CompilerType ClangASTContext::GetTypeForDecl(TagDecl *decl) {
+ // No need to call the getASTContext() accessor (which can create the AST if
+ // it isn't created yet, because we can't have created a decl in this
+ // AST if our AST didn't already exist...
+ ASTContext *ast = &decl->getASTContext();
+ if (ast)
+ return CompilerType(ast, ast->getTagDeclType(decl));
+ return CompilerType();
+}
+
+CompilerType ClangASTContext::GetTypeForDecl(ObjCInterfaceDecl *decl) {
+ // No need to call the getASTContext() accessor (which can create the AST if
+ // it isn't created yet, because we can't have created a decl in this
+ // AST if our AST didn't already exist...
+ ASTContext *ast = &decl->getASTContext();
+ if (ast)
+ return CompilerType(ast, ast->getObjCInterfaceType(decl));
+ return CompilerType();
+}
+
+#pragma mark Structure, Unions, Classes
+
+CompilerType ClangASTContext::CreateRecordType(DeclContext *decl_ctx,
+ AccessType access_type,
+ const char *name, int kind,
+ LanguageType language,
+ ClangASTMetadata *metadata) {
+ ASTContext *ast = getASTContext();
+ assert(ast != nullptr);
+
+ if (decl_ctx == nullptr)
+ decl_ctx = ast->getTranslationUnitDecl();
+
+ if (language == eLanguageTypeObjC ||
+ language == eLanguageTypeObjC_plus_plus) {
+ bool isForwardDecl = true;
+ bool isInternal = false;
+ return CreateObjCClass(name, decl_ctx, isForwardDecl, isInternal, metadata);
+ }
+
+ // NOTE: Eventually CXXRecordDecl will be merged back into RecordDecl and
+ // we will need to update this code. I was told to currently always use the
+ // CXXRecordDecl class since we often don't know from debug information if
+ // something is struct or a class, so we default to always use the more
+ // complete definition just in case.
+
+ bool is_anonymous = (!name) || (!name[0]);
+
+ CXXRecordDecl *decl = CXXRecordDecl::Create(
+ *ast, (TagDecl::TagKind)kind, decl_ctx, SourceLocation(),
+ SourceLocation(), is_anonymous ? nullptr : &ast->Idents.get(name));
+
+ if (is_anonymous)
+ decl->setAnonymousStructOrUnion(true);
+
+ if (decl) {
+ if (metadata)
+ SetMetadata(ast, decl, *metadata);
+
+ if (access_type != eAccessNone)
+ decl->setAccess(ConvertAccessTypeToAccessSpecifier(access_type));
+
+ if (decl_ctx)
+ decl_ctx->addDecl(decl);
+
+ return CompilerType(ast, ast->getTagDeclType(decl));
+ }
+ return CompilerType();
+}
+
+namespace {
+ bool IsValueParam(const clang::TemplateArgument &argument) {
+ return argument.getKind() == TemplateArgument::Integral;
+ }
+}
+
+static TemplateParameterList *CreateTemplateParameterList(
+ ASTContext *ast,
+ const ClangASTContext::TemplateParameterInfos &template_param_infos,
+ llvm::SmallVector<NamedDecl *, 8> &template_param_decls) {
+ const bool parameter_pack = false;
+ const bool is_typename = false;
+ const unsigned depth = 0;
+ const size_t num_template_params = template_param_infos.args.size();
+ DeclContext *const decl_context =
+ ast->getTranslationUnitDecl(); // Is this the right decl context?,
+ for (size_t i = 0; i < num_template_params; ++i) {
+ const char *name = template_param_infos.names[i];
+
+ IdentifierInfo *identifier_info = nullptr;
+ if (name && name[0])
+ identifier_info = &ast->Idents.get(name);
+ if (IsValueParam(template_param_infos.args[i])) {
+ template_param_decls.push_back(NonTypeTemplateParmDecl::Create(
+ *ast, decl_context,
+ SourceLocation(), SourceLocation(), depth, i, identifier_info,
+ template_param_infos.args[i].getIntegralType(), parameter_pack,
+ nullptr));
+
+ } else {
+ template_param_decls.push_back(TemplateTypeParmDecl::Create(
+ *ast, decl_context,
+ SourceLocation(), SourceLocation(), depth, i, identifier_info,
+ is_typename, parameter_pack));
+ }
+ }
+
+ if (template_param_infos.packed_args) {
+ IdentifierInfo *identifier_info = nullptr;
+ if (template_param_infos.pack_name && template_param_infos.pack_name[0])
+ identifier_info = &ast->Idents.get(template_param_infos.pack_name);
+ const bool parameter_pack_true = true;
+
+ if (!template_param_infos.packed_args->args.empty() &&
+ IsValueParam(template_param_infos.packed_args->args[0])) {
+ template_param_decls.push_back(NonTypeTemplateParmDecl::Create(
+ *ast, decl_context, SourceLocation(), SourceLocation(), depth,
+ num_template_params, identifier_info,
+ template_param_infos.packed_args->args[0].getIntegralType(),
+ parameter_pack_true, nullptr));
+ } else {
+ template_param_decls.push_back(TemplateTypeParmDecl::Create(
+ *ast, decl_context, SourceLocation(), SourceLocation(), depth,
+ num_template_params, identifier_info, is_typename,
+ parameter_pack_true));
+ }
+ }
+ clang::Expr *const requires_clause = nullptr; // TODO: Concepts
+ TemplateParameterList *template_param_list = TemplateParameterList::Create(
+ *ast, SourceLocation(), SourceLocation(), template_param_decls,
+ SourceLocation(), requires_clause);
+ return template_param_list;
+}
+
+clang::FunctionTemplateDecl *ClangASTContext::CreateFunctionTemplateDecl(
+ clang::DeclContext *decl_ctx, clang::FunctionDecl *func_decl,
+ const char *name, const TemplateParameterInfos &template_param_infos) {
+ // /// Create a function template node.
+ ASTContext *ast = getASTContext();
+
+ llvm::SmallVector<NamedDecl *, 8> template_param_decls;
+
+ TemplateParameterList *template_param_list = CreateTemplateParameterList(
+ ast, template_param_infos, template_param_decls);
+ FunctionTemplateDecl *func_tmpl_decl = FunctionTemplateDecl::Create(
+ *ast, decl_ctx, func_decl->getLocation(), func_decl->getDeclName(),
+ template_param_list, func_decl);
+
+ for (size_t i = 0, template_param_decl_count = template_param_decls.size();
+ i < template_param_decl_count; ++i) {
+ // TODO: verify which decl context we should put template_param_decls into..
+ template_param_decls[i]->setDeclContext(func_decl);
+ }
+
+ return func_tmpl_decl;
+}
+
+void ClangASTContext::CreateFunctionTemplateSpecializationInfo(
+ FunctionDecl *func_decl, clang::FunctionTemplateDecl *func_tmpl_decl,
+ const TemplateParameterInfos &infos) {
+ TemplateArgumentList *template_args_ptr =
+ TemplateArgumentList::CreateCopy(func_decl->getASTContext(), infos.args);
+
+ func_decl->setFunctionTemplateSpecialization(func_tmpl_decl,
+ template_args_ptr, nullptr);
+}
+
+ClassTemplateDecl *ClangASTContext::CreateClassTemplateDecl(
+ DeclContext *decl_ctx, lldb::AccessType access_type, const char *class_name,
+ int kind, const TemplateParameterInfos &template_param_infos) {
+ ASTContext *ast = getASTContext();
+
+ ClassTemplateDecl *class_template_decl = nullptr;
+ if (decl_ctx == nullptr)
+ decl_ctx = ast->getTranslationUnitDecl();
+
+ IdentifierInfo &identifier_info = ast->Idents.get(class_name);
+ DeclarationName decl_name(&identifier_info);
+
+ clang::DeclContext::lookup_result result = decl_ctx->lookup(decl_name);
+
+ for (NamedDecl *decl : result) {
+ class_template_decl = dyn_cast<clang::ClassTemplateDecl>(decl);
+ if (class_template_decl)
+ return class_template_decl;
+ }
+
+ llvm::SmallVector<NamedDecl *, 8> template_param_decls;
+
+ TemplateParameterList *template_param_list = CreateTemplateParameterList(
+ ast, template_param_infos, template_param_decls);
+
+ CXXRecordDecl *template_cxx_decl = CXXRecordDecl::Create(
+ *ast, (TagDecl::TagKind)kind,
+ decl_ctx, // What decl context do we use here? TU? The actual decl
+ // context?
+ SourceLocation(), SourceLocation(), &identifier_info);
+
+ for (size_t i = 0, template_param_decl_count = template_param_decls.size();
+ i < template_param_decl_count; ++i) {
+ template_param_decls[i]->setDeclContext(template_cxx_decl);
+ }
+
+ // With templated classes, we say that a class is templated with
+ // specializations, but that the bare class has no functions.
+ // template_cxx_decl->startDefinition();
+ // template_cxx_decl->completeDefinition();
+
+ class_template_decl = ClassTemplateDecl::Create(
+ *ast,
+ decl_ctx, // What decl context do we use here? TU? The actual decl
+ // context?
+ SourceLocation(), decl_name, template_param_list, template_cxx_decl);
+ template_cxx_decl->setDescribedClassTemplate(class_template_decl);
+
+ if (class_template_decl) {
+ if (access_type != eAccessNone)
+ class_template_decl->setAccess(
+ ConvertAccessTypeToAccessSpecifier(access_type));
+
+ // if (TagDecl *ctx_tag_decl = dyn_cast<TagDecl>(decl_ctx))
+ // CompleteTagDeclarationDefinition(GetTypeForDecl(ctx_tag_decl));
+
+ decl_ctx->addDecl(class_template_decl);
+
+#ifdef LLDB_CONFIGURATION_DEBUG
+ VerifyDecl(class_template_decl);
+#endif
+ }
+
+ return class_template_decl;
+}
+
+TemplateTemplateParmDecl *
+ClangASTContext::CreateTemplateTemplateParmDecl(const char *template_name) {
+ ASTContext *ast = getASTContext();
+
+ auto *decl_ctx = ast->getTranslationUnitDecl();
+
+ IdentifierInfo &identifier_info = ast->Idents.get(template_name);
+ llvm::SmallVector<NamedDecl *, 8> template_param_decls;
+
+ ClangASTContext::TemplateParameterInfos template_param_infos;
+ TemplateParameterList *template_param_list = CreateTemplateParameterList(
+ ast, template_param_infos, template_param_decls);
+
+ // LLDB needs to create those decls only to be able to display a
+ // type that includes a template template argument. Only the name matters for
+ // this purpose, so we use dummy values for the other characterisitcs of the
+ // type.
+ return TemplateTemplateParmDecl::Create(
+ *ast, decl_ctx, SourceLocation(),
+ /*Depth*/ 0, /*Position*/ 0,
+ /*IsParameterPack*/ false, &identifier_info, template_param_list);
+}
+
+ClassTemplateSpecializationDecl *
+ClangASTContext::CreateClassTemplateSpecializationDecl(
+ DeclContext *decl_ctx, ClassTemplateDecl *class_template_decl, int kind,
+ const TemplateParameterInfos &template_param_infos) {
+ ASTContext *ast = getASTContext();
+ llvm::SmallVector<clang::TemplateArgument, 2> args(
+ template_param_infos.args.size() +
+ (template_param_infos.packed_args ? 1 : 0));
+ std::copy(template_param_infos.args.begin(), template_param_infos.args.end(),
+ args.begin());
+ if (template_param_infos.packed_args) {
+ args[args.size() - 1] = TemplateArgument::CreatePackCopy(
+ *ast, template_param_infos.packed_args->args);
+ }
+ ClassTemplateSpecializationDecl *class_template_specialization_decl =
+ ClassTemplateSpecializationDecl::Create(
+ *ast, (TagDecl::TagKind)kind, decl_ctx, SourceLocation(),
+ SourceLocation(), class_template_decl, args,
+ nullptr);
+
+ class_template_specialization_decl->setSpecializationKind(
+ TSK_ExplicitSpecialization);
+
+ return class_template_specialization_decl;
+}
+
+CompilerType ClangASTContext::CreateClassTemplateSpecializationType(
+ ClassTemplateSpecializationDecl *class_template_specialization_decl) {
+ if (class_template_specialization_decl) {
+ ASTContext *ast = getASTContext();
+ if (ast)
+ return CompilerType(
+ ast, ast->getTagDeclType(class_template_specialization_decl));
+ }
+ return CompilerType();
+}
+
+static inline bool check_op_param(bool is_method,
+ clang::OverloadedOperatorKind op_kind,
+ bool unary, bool binary,
+ uint32_t num_params) {
+ // Special-case call since it can take any number of operands
+ if (op_kind == OO_Call)
+ return true;
+
+ // The parameter count doesn't include "this"
+ if (is_method)
+ ++num_params;
+ if (num_params == 1)
+ return unary;
+ if (num_params == 2)
+ return binary;
+ else
+ return false;
+}
+
+bool ClangASTContext::CheckOverloadedOperatorKindParameterCount(
+ bool is_method, clang::OverloadedOperatorKind op_kind,
+ uint32_t num_params) {
+ switch (op_kind) {
+ default:
+ break;
+ // C++ standard allows any number of arguments to new/delete
+ case OO_New:
+ case OO_Array_New:
+ case OO_Delete:
+ case OO_Array_Delete:
+ return true;
+ }
+
+#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
+ case OO_##Name: \
+ return check_op_param(is_method, op_kind, Unary, Binary, num_params);
+ switch (op_kind) {
+#include "clang/Basic/OperatorKinds.def"
+ default:
+ break;
+ }
+ return false;
+}
+
+clang::AccessSpecifier
+ClangASTContext::UnifyAccessSpecifiers(clang::AccessSpecifier lhs,
+ clang::AccessSpecifier rhs) {
+ // Make the access equal to the stricter of the field and the nested field's
+ // access
+ if (lhs == AS_none || rhs == AS_none)
+ return AS_none;
+ if (lhs == AS_private || rhs == AS_private)
+ return AS_private;
+ if (lhs == AS_protected || rhs == AS_protected)
+ return AS_protected;
+ return AS_public;
+}
+
+bool ClangASTContext::FieldIsBitfield(FieldDecl *field,
+ uint32_t &bitfield_bit_size) {
+ return FieldIsBitfield(getASTContext(), field, bitfield_bit_size);
+}
+
+bool ClangASTContext::FieldIsBitfield(ASTContext *ast, FieldDecl *field,
+ uint32_t &bitfield_bit_size) {
+ if (ast == nullptr || field == nullptr)
+ return false;
+
+ if (field->isBitField()) {
+ Expr *bit_width_expr = field->getBitWidth();
+ if (bit_width_expr) {
+ llvm::APSInt bit_width_apsint;
+ if (bit_width_expr->isIntegerConstantExpr(bit_width_apsint, *ast)) {
+ bitfield_bit_size = bit_width_apsint.getLimitedValue(UINT32_MAX);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool ClangASTContext::RecordHasFields(const RecordDecl *record_decl) {
+ if (record_decl == nullptr)
+ return false;
+
+ if (!record_decl->field_empty())
+ return true;
+
+ // No fields, lets check this is a CXX record and check the base classes
+ const CXXRecordDecl *cxx_record_decl = dyn_cast<CXXRecordDecl>(record_decl);
+ if (cxx_record_decl) {
+ CXXRecordDecl::base_class_const_iterator base_class, base_class_end;
+ for (base_class = cxx_record_decl->bases_begin(),
+ base_class_end = cxx_record_decl->bases_end();
+ base_class != base_class_end; ++base_class) {
+ const CXXRecordDecl *base_class_decl = cast<CXXRecordDecl>(
+ base_class->getType()->getAs<RecordType>()->getDecl());
+ if (RecordHasFields(base_class_decl))
+ return true;
+ }
+ }
+ return false;
+}
+
+#pragma mark Objective-C Classes
+
+CompilerType ClangASTContext::CreateObjCClass(const char *name,
+ DeclContext *decl_ctx,
+ bool isForwardDecl,
+ bool isInternal,
+ ClangASTMetadata *metadata) {
+ ASTContext *ast = getASTContext();
+ assert(ast != nullptr);
+ assert(name && name[0]);
+ if (decl_ctx == nullptr)
+ decl_ctx = ast->getTranslationUnitDecl();
+
+ ObjCInterfaceDecl *decl = ObjCInterfaceDecl::Create(
+ *ast, decl_ctx, SourceLocation(), &ast->Idents.get(name), nullptr,
+ nullptr, SourceLocation(),
+ /*isForwardDecl,*/
+ isInternal);
+
+ if (decl && metadata)
+ SetMetadata(ast, decl, *metadata);
+
+ return CompilerType(ast, ast->getObjCInterfaceType(decl));
+}
+
+static inline bool BaseSpecifierIsEmpty(const CXXBaseSpecifier *b) {
+ return !ClangASTContext::RecordHasFields(b->getType()->getAsCXXRecordDecl());
+}
+
+uint32_t
+ClangASTContext::GetNumBaseClasses(const CXXRecordDecl *cxx_record_decl,
+ bool omit_empty_base_classes) {
+ uint32_t num_bases = 0;
+ if (cxx_record_decl) {
+ if (omit_empty_base_classes) {
+ CXXRecordDecl::base_class_const_iterator base_class, base_class_end;
+ for (base_class = cxx_record_decl->bases_begin(),
+ base_class_end = cxx_record_decl->bases_end();
+ base_class != base_class_end; ++base_class) {
+ // Skip empty base classes
+ if (omit_empty_base_classes) {
+ if (BaseSpecifierIsEmpty(base_class))
+ continue;
+ }
+ ++num_bases;
+ }
+ } else
+ num_bases = cxx_record_decl->getNumBases();
+ }
+ return num_bases;
+}
+
+#pragma mark Namespace Declarations
+
+NamespaceDecl *ClangASTContext::GetUniqueNamespaceDeclaration(
+ const char *name, DeclContext *decl_ctx, bool is_inline) {
+ NamespaceDecl *namespace_decl = nullptr;
+ ASTContext *ast = getASTContext();
+ TranslationUnitDecl *translation_unit_decl = ast->getTranslationUnitDecl();
+ if (decl_ctx == nullptr)
+ decl_ctx = translation_unit_decl;
+
+ if (name) {
+ IdentifierInfo &identifier_info = ast->Idents.get(name);
+ DeclarationName decl_name(&identifier_info);
+ clang::DeclContext::lookup_result result = decl_ctx->lookup(decl_name);
+ for (NamedDecl *decl : result) {
+ namespace_decl = dyn_cast<clang::NamespaceDecl>(decl);
+ if (namespace_decl)
+ return namespace_decl;
+ }
+
+ namespace_decl =
+ NamespaceDecl::Create(*ast, decl_ctx, is_inline, SourceLocation(),
+ SourceLocation(), &identifier_info, nullptr);
+
+ decl_ctx->addDecl(namespace_decl);
+ } else {
+ if (decl_ctx == translation_unit_decl) {
+ namespace_decl = translation_unit_decl->getAnonymousNamespace();
+ if (namespace_decl)
+ return namespace_decl;
+
+ namespace_decl =
+ NamespaceDecl::Create(*ast, decl_ctx, false, SourceLocation(),
+ SourceLocation(), nullptr, nullptr);
+ translation_unit_decl->setAnonymousNamespace(namespace_decl);
+ translation_unit_decl->addDecl(namespace_decl);
+ assert(namespace_decl == translation_unit_decl->getAnonymousNamespace());
+ } else {
+ NamespaceDecl *parent_namespace_decl = cast<NamespaceDecl>(decl_ctx);
+ if (parent_namespace_decl) {
+ namespace_decl = parent_namespace_decl->getAnonymousNamespace();
+ if (namespace_decl)
+ return namespace_decl;
+ namespace_decl =
+ NamespaceDecl::Create(*ast, decl_ctx, false, SourceLocation(),
+ SourceLocation(), nullptr, nullptr);
+ parent_namespace_decl->setAnonymousNamespace(namespace_decl);
+ parent_namespace_decl->addDecl(namespace_decl);
+ assert(namespace_decl ==
+ parent_namespace_decl->getAnonymousNamespace());
+ } else {
+ assert(false && "GetUniqueNamespaceDeclaration called with no name and "
+ "no namespace as decl_ctx");
+ }
+ }
+ }
+#ifdef LLDB_CONFIGURATION_DEBUG
+ VerifyDecl(namespace_decl);
+#endif
+ return namespace_decl;
+}
+
+NamespaceDecl *ClangASTContext::GetUniqueNamespaceDeclaration(
+ clang::ASTContext *ast, const char *name, clang::DeclContext *decl_ctx,
+ bool is_inline) {
+ ClangASTContext *ast_ctx = ClangASTContext::GetASTContext(ast);
+ if (ast_ctx == nullptr)
+ return nullptr;
+
+ return ast_ctx->GetUniqueNamespaceDeclaration(name, decl_ctx, is_inline);
+}
+
+clang::BlockDecl *
+ClangASTContext::CreateBlockDeclaration(clang::DeclContext *ctx) {
+ if (ctx != nullptr) {
+ clang::BlockDecl *decl = clang::BlockDecl::Create(*getASTContext(), ctx,
+ clang::SourceLocation());
+ ctx->addDecl(decl);
+ return decl;
+ }
+ return nullptr;
+}
+
+clang::DeclContext *FindLCABetweenDecls(clang::DeclContext *left,
+ clang::DeclContext *right,
+ clang::DeclContext *root) {
+ if (root == nullptr)
+ return nullptr;
+
+ std::set<clang::DeclContext *> path_left;
+ for (clang::DeclContext *d = left; d != nullptr; d = d->getParent())
+ path_left.insert(d);
+
+ for (clang::DeclContext *d = right; d != nullptr; d = d->getParent())
+ if (path_left.find(d) != path_left.end())
+ return d;
+
+ return nullptr;
+}
+
+clang::UsingDirectiveDecl *ClangASTContext::CreateUsingDirectiveDeclaration(
+ clang::DeclContext *decl_ctx, clang::NamespaceDecl *ns_decl) {
+ if (decl_ctx != nullptr && ns_decl != nullptr) {
+ clang::TranslationUnitDecl *translation_unit =
+ (clang::TranslationUnitDecl *)GetTranslationUnitDecl(getASTContext());
+ clang::UsingDirectiveDecl *using_decl = clang::UsingDirectiveDecl::Create(
+ *getASTContext(), decl_ctx, clang::SourceLocation(),
+ clang::SourceLocation(), clang::NestedNameSpecifierLoc(),
+ clang::SourceLocation(), ns_decl,
+ FindLCABetweenDecls(decl_ctx, ns_decl, translation_unit));
+ decl_ctx->addDecl(using_decl);
+ return using_decl;
+ }
+ return nullptr;
+}
+
+clang::UsingDecl *
+ClangASTContext::CreateUsingDeclaration(clang::DeclContext *current_decl_ctx,
+ clang::NamedDecl *target) {
+ if (current_decl_ctx != nullptr && target != nullptr) {
+ clang::UsingDecl *using_decl = clang::UsingDecl::Create(
+ *getASTContext(), current_decl_ctx, clang::SourceLocation(),
+ clang::NestedNameSpecifierLoc(), clang::DeclarationNameInfo(), false);
+ clang::UsingShadowDecl *shadow_decl = clang::UsingShadowDecl::Create(
+ *getASTContext(), current_decl_ctx, clang::SourceLocation(), using_decl,
+ target);
+ using_decl->addShadowDecl(shadow_decl);
+ current_decl_ctx->addDecl(using_decl);
+ return using_decl;
+ }
+ return nullptr;
+}
+
+clang::VarDecl *ClangASTContext::CreateVariableDeclaration(
+ clang::DeclContext *decl_context, const char *name, clang::QualType type) {
+ if (decl_context != nullptr) {
+ clang::VarDecl *var_decl = clang::VarDecl::Create(
+ *getASTContext(), decl_context, clang::SourceLocation(),
+ clang::SourceLocation(),
+ name && name[0] ? &getASTContext()->Idents.getOwn(name) : nullptr, type,
+ nullptr, clang::SC_None);
+ var_decl->setAccess(clang::AS_public);
+ decl_context->addDecl(var_decl);
+ return var_decl;
+ }
+ return nullptr;
+}
+
+lldb::opaque_compiler_type_t
+ClangASTContext::GetOpaqueCompilerType(clang::ASTContext *ast,
+ lldb::BasicType basic_type) {
+ switch (basic_type) {
+ case eBasicTypeVoid:
+ return ast->VoidTy.getAsOpaquePtr();
+ case eBasicTypeChar:
+ return ast->CharTy.getAsOpaquePtr();
+ case eBasicTypeSignedChar:
+ return ast->SignedCharTy.getAsOpaquePtr();
+ case eBasicTypeUnsignedChar:
+ return ast->UnsignedCharTy.getAsOpaquePtr();
+ case eBasicTypeWChar:
+ return ast->getWCharType().getAsOpaquePtr();
+ case eBasicTypeSignedWChar:
+ return ast->getSignedWCharType().getAsOpaquePtr();
+ case eBasicTypeUnsignedWChar:
+ return ast->getUnsignedWCharType().getAsOpaquePtr();
+ case eBasicTypeChar16:
+ return ast->Char16Ty.getAsOpaquePtr();
+ case eBasicTypeChar32:
+ return ast->Char32Ty.getAsOpaquePtr();
+ case eBasicTypeShort:
+ return ast->ShortTy.getAsOpaquePtr();
+ case eBasicTypeUnsignedShort:
+ return ast->UnsignedShortTy.getAsOpaquePtr();
+ case eBasicTypeInt:
+ return ast->IntTy.getAsOpaquePtr();
+ case eBasicTypeUnsignedInt:
+ return ast->UnsignedIntTy.getAsOpaquePtr();
+ case eBasicTypeLong:
+ return ast->LongTy.getAsOpaquePtr();
+ case eBasicTypeUnsignedLong:
+ return ast->UnsignedLongTy.getAsOpaquePtr();
+ case eBasicTypeLongLong:
+ return ast->LongLongTy.getAsOpaquePtr();
+ case eBasicTypeUnsignedLongLong:
+ return ast->UnsignedLongLongTy.getAsOpaquePtr();
+ case eBasicTypeInt128:
+ return ast->Int128Ty.getAsOpaquePtr();
+ case eBasicTypeUnsignedInt128:
+ return ast->UnsignedInt128Ty.getAsOpaquePtr();
+ case eBasicTypeBool:
+ return ast->BoolTy.getAsOpaquePtr();
+ case eBasicTypeHalf:
+ return ast->HalfTy.getAsOpaquePtr();
+ case eBasicTypeFloat:
+ return ast->FloatTy.getAsOpaquePtr();
+ case eBasicTypeDouble:
+ return ast->DoubleTy.getAsOpaquePtr();
+ case eBasicTypeLongDouble:
+ return ast->LongDoubleTy.getAsOpaquePtr();
+ case eBasicTypeFloatComplex:
+ return ast->FloatComplexTy.getAsOpaquePtr();
+ case eBasicTypeDoubleComplex:
+ return ast->DoubleComplexTy.getAsOpaquePtr();
+ case eBasicTypeLongDoubleComplex:
+ return ast->LongDoubleComplexTy.getAsOpaquePtr();
+ case eBasicTypeObjCID:
+ return ast->getObjCIdType().getAsOpaquePtr();
+ case eBasicTypeObjCClass:
+ return ast->getObjCClassType().getAsOpaquePtr();
+ case eBasicTypeObjCSel:
+ return ast->getObjCSelType().getAsOpaquePtr();
+ case eBasicTypeNullPtr:
+ return ast->NullPtrTy.getAsOpaquePtr();
+ default:
+ return nullptr;
+ }
+}
+
+#pragma mark Function Types
+
+clang::DeclarationName
+ClangASTContext::GetDeclarationName(const char *name,
+ const CompilerType &function_clang_type) {
+ if (!name || !name[0])
+ return clang::DeclarationName();
+
+ clang::OverloadedOperatorKind op_kind = clang::NUM_OVERLOADED_OPERATORS;
+ if (!IsOperator(name, op_kind) || op_kind == clang::NUM_OVERLOADED_OPERATORS)
+ return DeclarationName(&getASTContext()->Idents.get(
+ name)); // Not operator, but a regular function.
+
+ // Check the number of operator parameters. Sometimes we have seen bad DWARF
+ // that doesn't correctly describe operators and if we try to create a method
+ // and add it to the class, clang will assert and crash, so we need to make
+ // sure things are acceptable.
+ clang::QualType method_qual_type(ClangUtil::GetQualType(function_clang_type));
+ const clang::FunctionProtoType *function_type =
+ llvm::dyn_cast<clang::FunctionProtoType>(method_qual_type.getTypePtr());
+ if (function_type == nullptr)
+ return clang::DeclarationName();
+
+ const bool is_method = false;
+ const unsigned int num_params = function_type->getNumParams();
+ if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount(
+ is_method, op_kind, num_params))
+ return clang::DeclarationName();
+
+ return getASTContext()->DeclarationNames.getCXXOperatorName(op_kind);
+}
+
+FunctionDecl *ClangASTContext::CreateFunctionDeclaration(
+ DeclContext *decl_ctx, const char *name,
+ const CompilerType &function_clang_type, int storage, bool is_inline) {
+ FunctionDecl *func_decl = nullptr;
+ ASTContext *ast = getASTContext();
+ if (decl_ctx == nullptr)
+ decl_ctx = ast->getTranslationUnitDecl();
+
+ const bool hasWrittenPrototype = true;
+ const bool isConstexprSpecified = false;
+
+ clang::DeclarationName declarationName =
+ GetDeclarationName(name, function_clang_type);
+ func_decl = FunctionDecl::Create(
+ *ast, decl_ctx, SourceLocation(), SourceLocation(), declarationName,
+ ClangUtil::GetQualType(function_clang_type), nullptr,
+ (clang::StorageClass)storage, is_inline, hasWrittenPrototype,
+ isConstexprSpecified ? CSK_constexpr : CSK_unspecified);
+ if (func_decl)
+ decl_ctx->addDecl(func_decl);
+
+#ifdef LLDB_CONFIGURATION_DEBUG
+ VerifyDecl(func_decl);
+#endif
+
+ return func_decl;
+}
+
+CompilerType ClangASTContext::CreateFunctionType(
+ ASTContext *ast, const CompilerType &result_type, const CompilerType *args,
+ unsigned num_args, bool is_variadic, unsigned type_quals,
+ clang::CallingConv cc) {
+ if (ast == nullptr)
+ return CompilerType(); // invalid AST
+
+ if (!result_type || !ClangUtil::IsClangType(result_type))
+ return CompilerType(); // invalid return type
+
+ std::vector<QualType> qual_type_args;
+ if (num_args > 0 && args == nullptr)
+ return CompilerType(); // invalid argument array passed in
+
+ // Verify that all arguments are valid and the right type
+ for (unsigned i = 0; i < num_args; ++i) {
+ if (args[i]) {
+ // Make sure we have a clang type in args[i] and not a type from another
+ // language whose name might match
+ const bool is_clang_type = ClangUtil::IsClangType(args[i]);
+ lldbassert(is_clang_type);
+ if (is_clang_type)
+ qual_type_args.push_back(ClangUtil::GetQualType(args[i]));
+ else
+ return CompilerType(); // invalid argument type (must be a clang type)
+ } else
+ return CompilerType(); // invalid argument type (empty)
+ }
+
+ // TODO: Detect calling convention in DWARF?
+ FunctionProtoType::ExtProtoInfo proto_info;
+ proto_info.ExtInfo = cc;
+ proto_info.Variadic = is_variadic;
+ proto_info.ExceptionSpec = EST_None;
+ proto_info.TypeQuals = clang::Qualifiers::fromFastMask(type_quals);
+ proto_info.RefQualifier = RQ_None;
+
+ return CompilerType(ast,
+ ast->getFunctionType(ClangUtil::GetQualType(result_type),
+ qual_type_args, proto_info));
+}
+
+ParmVarDecl *ClangASTContext::CreateParameterDeclaration(
+ clang::DeclContext *decl_ctx, const char *name,
+ const CompilerType &param_type, int storage) {
+ ASTContext *ast = getASTContext();
+ assert(ast != nullptr);
+ auto *decl =
+ ParmVarDecl::Create(*ast, decl_ctx, SourceLocation(), SourceLocation(),
+ name && name[0] ? &ast->Idents.get(name) : nullptr,
+ ClangUtil::GetQualType(param_type), nullptr,
+ (clang::StorageClass)storage, nullptr);
+ decl_ctx->addDecl(decl);
+ return decl;
+}
+
+void ClangASTContext::SetFunctionParameters(FunctionDecl *function_decl,
+ ParmVarDecl **params,
+ unsigned num_params) {
+ if (function_decl)
+ function_decl->setParams(ArrayRef<ParmVarDecl *>(params, num_params));
+}
+
+CompilerType
+ClangASTContext::CreateBlockPointerType(const CompilerType &function_type) {
+ QualType block_type = m_ast_up->getBlockPointerType(
+ clang::QualType::getFromOpaquePtr(function_type.GetOpaqueQualType()));
+
+ return CompilerType(this, block_type.getAsOpaquePtr());
+}
+
+#pragma mark Array Types
+
+CompilerType ClangASTContext::CreateArrayType(const CompilerType &element_type,
+ size_t element_count,
+ bool is_vector) {
+ if (element_type.IsValid()) {
+ ASTContext *ast = getASTContext();
+ assert(ast != nullptr);
+
+ if (is_vector) {
+ return CompilerType(
+ ast, ast->getExtVectorType(ClangUtil::GetQualType(element_type),
+ element_count));
+ } else {
+
+ llvm::APInt ap_element_count(64, element_count);
+ if (element_count == 0) {
+ return CompilerType(ast, ast->getIncompleteArrayType(
+ ClangUtil::GetQualType(element_type),
+ clang::ArrayType::Normal, 0));
+ } else {
+ return CompilerType(
+ ast, ast->getConstantArrayType(ClangUtil::GetQualType(element_type),
+ ap_element_count,
+ clang::ArrayType::Normal, 0));
+ }
+ }
+ }
+ return CompilerType();
+}
+
+CompilerType ClangASTContext::CreateStructForIdentifier(
+ ConstString type_name,
+ const std::initializer_list<std::pair<const char *, CompilerType>>
+ &type_fields,
+ bool packed) {
+ CompilerType type;
+ if (!type_name.IsEmpty() &&
+ (type = GetTypeForIdentifier<clang::CXXRecordDecl>(type_name))
+ .IsValid()) {
+ lldbassert(0 && "Trying to create a type for an existing name");
+ return type;
+ }
+
+ type = CreateRecordType(nullptr, lldb::eAccessPublic, type_name.GetCString(),
+ clang::TTK_Struct, lldb::eLanguageTypeC);
+ StartTagDeclarationDefinition(type);
+ for (const auto &field : type_fields)
+ AddFieldToRecordType(type, field.first, field.second, lldb::eAccessPublic,
+ 0);
+ if (packed)
+ SetIsPacked(type);
+ CompleteTagDeclarationDefinition(type);
+ return type;
+}
+
+CompilerType ClangASTContext::GetOrCreateStructForIdentifier(
+ ConstString type_name,
+ const std::initializer_list<std::pair<const char *, CompilerType>>
+ &type_fields,
+ bool packed) {
+ CompilerType type;
+ if ((type = GetTypeForIdentifier<clang::CXXRecordDecl>(type_name)).IsValid())
+ return type;
+
+ return CreateStructForIdentifier(type_name, type_fields, packed);
+}
+
+#pragma mark Enumeration Types
+
+CompilerType
+ClangASTContext::CreateEnumerationType(const char *name, DeclContext *decl_ctx,
+ const Declaration &decl,
+ const CompilerType &integer_clang_type,
+ bool is_scoped) {
+ // TODO: Do something intelligent with the Declaration object passed in
+ // like maybe filling in the SourceLocation with it...
+ ASTContext *ast = getASTContext();
+
+ // TODO: ask about these...
+ // const bool IsFixed = false;
+
+ EnumDecl *enum_decl = EnumDecl::Create(
+ *ast, decl_ctx, SourceLocation(), SourceLocation(),
+ name && name[0] ? &ast->Idents.get(name) : nullptr, nullptr,
+ is_scoped, // IsScoped
+ is_scoped, // IsScopedUsingClassTag
+ false); // IsFixed
+
+ if (enum_decl) {
+ if (decl_ctx)
+ decl_ctx->addDecl(enum_decl);
+
+ // TODO: check if we should be setting the promotion type too?
+ enum_decl->setIntegerType(ClangUtil::GetQualType(integer_clang_type));
+
+ enum_decl->setAccess(AS_public); // TODO respect what's in the debug info
+
+ return CompilerType(ast, ast->getTagDeclType(enum_decl));
+ }
+ return CompilerType();
+}
+
+CompilerType ClangASTContext::GetIntTypeFromBitSize(clang::ASTContext *ast,
+ size_t bit_size,
+ bool is_signed) {
+ if (ast) {
+ if (is_signed) {
+ if (bit_size == ast->getTypeSize(ast->SignedCharTy))
+ return CompilerType(ast, ast->SignedCharTy);
+
+ if (bit_size == ast->getTypeSize(ast->ShortTy))
+ return CompilerType(ast, ast->ShortTy);
+
+ if (bit_size == ast->getTypeSize(ast->IntTy))
+ return CompilerType(ast, ast->IntTy);
+
+ if (bit_size == ast->getTypeSize(ast->LongTy))
+ return CompilerType(ast, ast->LongTy);
+
+ if (bit_size == ast->getTypeSize(ast->LongLongTy))
+ return CompilerType(ast, ast->LongLongTy);
+
+ if (bit_size == ast->getTypeSize(ast->Int128Ty))
+ return CompilerType(ast, ast->Int128Ty);
+ } else {
+ if (bit_size == ast->getTypeSize(ast->UnsignedCharTy))
+ return CompilerType(ast, ast->UnsignedCharTy);
+
+ if (bit_size == ast->getTypeSize(ast->UnsignedShortTy))
+ return CompilerType(ast, ast->UnsignedShortTy);
+
+ if (bit_size == ast->getTypeSize(ast->UnsignedIntTy))
+ return CompilerType(ast, ast->UnsignedIntTy);
+
+ if (bit_size == ast->getTypeSize(ast->UnsignedLongTy))
+ return CompilerType(ast, ast->UnsignedLongTy);
+
+ if (bit_size == ast->getTypeSize(ast->UnsignedLongLongTy))
+ return CompilerType(ast, ast->UnsignedLongLongTy);
+
+ if (bit_size == ast->getTypeSize(ast->UnsignedInt128Ty))
+ return CompilerType(ast, ast->UnsignedInt128Ty);
+ }
+ }
+ return CompilerType();
+}
+
+CompilerType ClangASTContext::GetPointerSizedIntType(clang::ASTContext *ast,
+ bool is_signed) {
+ if (ast)
+ return GetIntTypeFromBitSize(ast, ast->getTypeSize(ast->VoidPtrTy),
+ is_signed);
+ return CompilerType();
+}
+
+void ClangASTContext::DumpDeclContextHiearchy(clang::DeclContext *decl_ctx) {
+ if (decl_ctx) {
+ DumpDeclContextHiearchy(decl_ctx->getParent());
+
+ clang::NamedDecl *named_decl = llvm::dyn_cast<clang::NamedDecl>(decl_ctx);
+ if (named_decl) {
+ printf("%20s: %s\n", decl_ctx->getDeclKindName(),
+ named_decl->getDeclName().getAsString().c_str());
+ } else {
+ printf("%20s\n", decl_ctx->getDeclKindName());
+ }
+ }
+}
+
+void ClangASTContext::DumpDeclHiearchy(clang::Decl *decl) {
+ if (decl == nullptr)
+ return;
+ DumpDeclContextHiearchy(decl->getDeclContext());
+
+ clang::RecordDecl *record_decl = llvm::dyn_cast<clang::RecordDecl>(decl);
+ if (record_decl) {
+ printf("%20s: %s%s\n", decl->getDeclKindName(),
+ record_decl->getDeclName().getAsString().c_str(),
+ record_decl->isInjectedClassName() ? " (injected class name)" : "");
+
+ } else {
+ clang::NamedDecl *named_decl = llvm::dyn_cast<clang::NamedDecl>(decl);
+ if (named_decl) {
+ printf("%20s: %s\n", decl->getDeclKindName(),
+ named_decl->getDeclName().getAsString().c_str());
+ } else {
+ printf("%20s\n", decl->getDeclKindName());
+ }
+ }
+}
+
+bool ClangASTContext::DeclsAreEquivalent(clang::Decl *lhs_decl,
+ clang::Decl *rhs_decl) {
+ if (lhs_decl && rhs_decl) {
+ // Make sure the decl kinds match first
+ const clang::Decl::Kind lhs_decl_kind = lhs_decl->getKind();
+ const clang::Decl::Kind rhs_decl_kind = rhs_decl->getKind();
+
+ if (lhs_decl_kind == rhs_decl_kind) {
+ // Now check that the decl contexts kinds are all equivalent before we
+ // have to check any names of the decl contexts...
+ clang::DeclContext *lhs_decl_ctx = lhs_decl->getDeclContext();
+ clang::DeclContext *rhs_decl_ctx = rhs_decl->getDeclContext();
+ if (lhs_decl_ctx && rhs_decl_ctx) {
+ while (true) {
+ if (lhs_decl_ctx && rhs_decl_ctx) {
+ const clang::Decl::Kind lhs_decl_ctx_kind =
+ lhs_decl_ctx->getDeclKind();
+ const clang::Decl::Kind rhs_decl_ctx_kind =
+ rhs_decl_ctx->getDeclKind();
+ if (lhs_decl_ctx_kind == rhs_decl_ctx_kind) {
+ lhs_decl_ctx = lhs_decl_ctx->getParent();
+ rhs_decl_ctx = rhs_decl_ctx->getParent();
+
+ if (lhs_decl_ctx == nullptr && rhs_decl_ctx == nullptr)
+ break;
+ } else
+ return false;
+ } else
+ return false;
+ }
+
+ // Now make sure the name of the decls match
+ clang::NamedDecl *lhs_named_decl =
+ llvm::dyn_cast<clang::NamedDecl>(lhs_decl);
+ clang::NamedDecl *rhs_named_decl =
+ llvm::dyn_cast<clang::NamedDecl>(rhs_decl);
+ if (lhs_named_decl && rhs_named_decl) {
+ clang::DeclarationName lhs_decl_name = lhs_named_decl->getDeclName();
+ clang::DeclarationName rhs_decl_name = rhs_named_decl->getDeclName();
+ if (lhs_decl_name.getNameKind() == rhs_decl_name.getNameKind()) {
+ if (lhs_decl_name.getAsString() != rhs_decl_name.getAsString())
+ return false;
+ } else
+ return false;
+ } else
+ return false;
+
+ // We know that the decl context kinds all match, so now we need to
+ // make sure the names match as well
+ lhs_decl_ctx = lhs_decl->getDeclContext();
+ rhs_decl_ctx = rhs_decl->getDeclContext();
+ while (true) {
+ switch (lhs_decl_ctx->getDeclKind()) {
+ case clang::Decl::TranslationUnit:
+ // We don't care about the translation unit names
+ return true;
+ default: {
+ clang::NamedDecl *lhs_named_decl =
+ llvm::dyn_cast<clang::NamedDecl>(lhs_decl_ctx);
+ clang::NamedDecl *rhs_named_decl =
+ llvm::dyn_cast<clang::NamedDecl>(rhs_decl_ctx);
+ if (lhs_named_decl && rhs_named_decl) {
+ clang::DeclarationName lhs_decl_name =
+ lhs_named_decl->getDeclName();
+ clang::DeclarationName rhs_decl_name =
+ rhs_named_decl->getDeclName();
+ if (lhs_decl_name.getNameKind() == rhs_decl_name.getNameKind()) {
+ if (lhs_decl_name.getAsString() != rhs_decl_name.getAsString())
+ return false;
+ } else
+ return false;
+ } else
+ return false;
+ } break;
+ }
+ lhs_decl_ctx = lhs_decl_ctx->getParent();
+ rhs_decl_ctx = rhs_decl_ctx->getParent();
+ }
+ }
+ }
+ }
+ return false;
+}
+bool ClangASTContext::GetCompleteDecl(clang::ASTContext *ast,
+ clang::Decl *decl) {
+ if (!decl)
+ return false;
+
+ ExternalASTSource *ast_source = ast->getExternalSource();
+
+ if (!ast_source)
+ return false;
+
+ if (clang::TagDecl *tag_decl = llvm::dyn_cast<clang::TagDecl>(decl)) {
+ if (tag_decl->isCompleteDefinition())
+ return true;
+
+ if (!tag_decl->hasExternalLexicalStorage())
+ return false;
+
+ ast_source->CompleteType(tag_decl);
+
+ return !tag_decl->getTypeForDecl()->isIncompleteType();
+ } else if (clang::ObjCInterfaceDecl *objc_interface_decl =
+ llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl)) {
+ if (objc_interface_decl->getDefinition())
+ return true;
+
+ if (!objc_interface_decl->hasExternalLexicalStorage())
+ return false;
+
+ ast_source->CompleteType(objc_interface_decl);
+
+ return !objc_interface_decl->getTypeForDecl()->isIncompleteType();
+ } else {
+ return false;
+ }
+}
+
+void ClangASTContext::SetMetadataAsUserID(const void *object,
+ user_id_t user_id) {
+ ClangASTMetadata meta_data;
+ meta_data.SetUserID(user_id);
+ SetMetadata(object, meta_data);
+}
+
+void ClangASTContext::SetMetadata(clang::ASTContext *ast, const void *object,
+ ClangASTMetadata &metadata) {
+ ClangExternalASTSourceCommon *external_source =
+ ClangExternalASTSourceCommon::Lookup(ast->getExternalSource());
+
+ if (external_source)
+ external_source->SetMetadata(object, metadata);
+}
+
+ClangASTMetadata *ClangASTContext::GetMetadata(clang::ASTContext *ast,
+ const void *object) {
+ ClangExternalASTSourceCommon *external_source =
+ ClangExternalASTSourceCommon::Lookup(ast->getExternalSource());
+
+ if (external_source && external_source->HasMetadata(object))
+ return external_source->GetMetadata(object);
+ else
+ return nullptr;
+}
+
+clang::DeclContext *
+ClangASTContext::GetAsDeclContext(clang::CXXMethodDecl *cxx_method_decl) {
+ return llvm::dyn_cast<clang::DeclContext>(cxx_method_decl);
+}
+
+clang::DeclContext *
+ClangASTContext::GetAsDeclContext(clang::ObjCMethodDecl *objc_method_decl) {
+ return llvm::dyn_cast<clang::DeclContext>(objc_method_decl);
+}
+
+bool ClangASTContext::SetTagTypeKind(clang::QualType tag_qual_type,
+ int kind) const {
+ const clang::Type *clang_type = tag_qual_type.getTypePtr();
+ if (clang_type) {
+ const clang::TagType *tag_type = llvm::dyn_cast<clang::TagType>(clang_type);
+ if (tag_type) {
+ clang::TagDecl *tag_decl =
+ llvm::dyn_cast<clang::TagDecl>(tag_type->getDecl());
+ if (tag_decl) {
+ tag_decl->setTagKind((clang::TagDecl::TagKind)kind);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool ClangASTContext::SetDefaultAccessForRecordFields(
+ clang::RecordDecl *record_decl, int default_accessibility,
+ int *assigned_accessibilities, size_t num_assigned_accessibilities) {
+ if (record_decl) {
+ uint32_t field_idx;
+ clang::RecordDecl::field_iterator field, field_end;
+ for (field = record_decl->field_begin(),
+ field_end = record_decl->field_end(), field_idx = 0;
+ field != field_end; ++field, ++field_idx) {
+ // If no accessibility was assigned, assign the correct one
+ if (field_idx < num_assigned_accessibilities &&
+ assigned_accessibilities[field_idx] == clang::AS_none)
+ field->setAccess((clang::AccessSpecifier)default_accessibility);
+ }
+ return true;
+ }
+ return false;
+}
+
+clang::DeclContext *
+ClangASTContext::GetDeclContextForType(const CompilerType &type) {
+ return GetDeclContextForType(ClangUtil::GetQualType(type));
+}
+
+clang::DeclContext *
+ClangASTContext::GetDeclContextForType(clang::QualType type) {
+ if (type.isNull())
+ return nullptr;
+
+ clang::QualType qual_type = type.getCanonicalType();
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::ObjCInterface:
+ return llvm::cast<clang::ObjCObjectType>(qual_type.getTypePtr())
+ ->getInterface();
+ case clang::Type::ObjCObjectPointer:
+ return GetDeclContextForType(
+ llvm::cast<clang::ObjCObjectPointerType>(qual_type.getTypePtr())
+ ->getPointeeType());
+ case clang::Type::Record:
+ return llvm::cast<clang::RecordType>(qual_type)->getDecl();
+ case clang::Type::Enum:
+ return llvm::cast<clang::EnumType>(qual_type)->getDecl();
+ case clang::Type::Typedef:
+ return GetDeclContextForType(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType());
+ case clang::Type::Auto:
+ return GetDeclContextForType(
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType());
+ case clang::Type::Elaborated:
+ return GetDeclContextForType(
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType());
+ case clang::Type::Paren:
+ return GetDeclContextForType(
+ llvm::cast<clang::ParenType>(qual_type)->desugar());
+ default:
+ break;
+ }
+ // No DeclContext in this type...
+ return nullptr;
+}
+
+static bool GetCompleteQualType(clang::ASTContext *ast,
+ clang::QualType qual_type,
+ bool allow_completion = true) {
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::ConstantArray:
+ case clang::Type::IncompleteArray:
+ case clang::Type::VariableArray: {
+ const clang::ArrayType *array_type =
+ llvm::dyn_cast<clang::ArrayType>(qual_type.getTypePtr());
+
+ if (array_type)
+ return GetCompleteQualType(ast, array_type->getElementType(),
+ allow_completion);
+ } break;
+ case clang::Type::Record: {
+ clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl) {
+ if (cxx_record_decl->hasExternalLexicalStorage()) {
+ const bool is_complete = cxx_record_decl->isCompleteDefinition();
+ const bool fields_loaded =
+ cxx_record_decl->hasLoadedFieldsFromExternalStorage();
+ if (is_complete && fields_loaded)
+ return true;
+
+ if (!allow_completion)
+ return false;
+
+ // Call the field_begin() accessor to for it to use the external source
+ // to load the fields...
+ clang::ExternalASTSource *external_ast_source =
+ ast->getExternalSource();
+ if (external_ast_source) {
+ external_ast_source->CompleteType(cxx_record_decl);
+ if (cxx_record_decl->isCompleteDefinition()) {
+ cxx_record_decl->field_begin();
+ cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true);
+ }
+ }
+ }
+ }
+ const clang::TagType *tag_type =
+ llvm::cast<clang::TagType>(qual_type.getTypePtr());
+ return !tag_type->isIncompleteType();
+ } break;
+
+ case clang::Type::Enum: {
+ const clang::TagType *tag_type =
+ llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr());
+ if (tag_type) {
+ clang::TagDecl *tag_decl = tag_type->getDecl();
+ if (tag_decl) {
+ if (tag_decl->getDefinition())
+ return true;
+
+ if (!allow_completion)
+ return false;
+
+ if (tag_decl->hasExternalLexicalStorage()) {
+ if (ast) {
+ clang::ExternalASTSource *external_ast_source =
+ ast->getExternalSource();
+ if (external_ast_source) {
+ external_ast_source->CompleteType(tag_decl);
+ return !tag_type->isIncompleteType();
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ } break;
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface: {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type);
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+ // We currently can't complete objective C types through the newly added
+ // ASTContext because it only supports TagDecl objects right now...
+ if (class_interface_decl) {
+ if (class_interface_decl->getDefinition())
+ return true;
+
+ if (!allow_completion)
+ return false;
+
+ if (class_interface_decl->hasExternalLexicalStorage()) {
+ if (ast) {
+ clang::ExternalASTSource *external_ast_source =
+ ast->getExternalSource();
+ if (external_ast_source) {
+ external_ast_source->CompleteType(class_interface_decl);
+ return !objc_class_type->isIncompleteType();
+ }
+ }
+ }
+ return false;
+ }
+ }
+ } break;
+
+ case clang::Type::Typedef:
+ return GetCompleteQualType(ast, llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType(),
+ allow_completion);
+
+ case clang::Type::Auto:
+ return GetCompleteQualType(
+ ast, llvm::cast<clang::AutoType>(qual_type)->getDeducedType(),
+ allow_completion);
+
+ case clang::Type::Elaborated:
+ return GetCompleteQualType(
+ ast, llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType(),
+ allow_completion);
+
+ case clang::Type::Paren:
+ return GetCompleteQualType(
+ ast, llvm::cast<clang::ParenType>(qual_type)->desugar(),
+ allow_completion);
+
+ case clang::Type::Attributed:
+ return GetCompleteQualType(
+ ast, llvm::cast<clang::AttributedType>(qual_type)->getModifiedType(),
+ allow_completion);
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static clang::ObjCIvarDecl::AccessControl
+ConvertAccessTypeToObjCIvarAccessControl(AccessType access) {
+ switch (access) {
+ case eAccessNone:
+ return clang::ObjCIvarDecl::None;
+ case eAccessPublic:
+ return clang::ObjCIvarDecl::Public;
+ case eAccessPrivate:
+ return clang::ObjCIvarDecl::Private;
+ case eAccessProtected:
+ return clang::ObjCIvarDecl::Protected;
+ case eAccessPackage:
+ return clang::ObjCIvarDecl::Package;
+ }
+ return clang::ObjCIvarDecl::None;
+}
+
+// Tests
+
+bool ClangASTContext::IsAggregateType(lldb::opaque_compiler_type_t type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::IncompleteArray:
+ case clang::Type::VariableArray:
+ case clang::Type::ConstantArray:
+ case clang::Type::ExtVector:
+ case clang::Type::Vector:
+ case clang::Type::Record:
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface:
+ return true;
+ case clang::Type::Auto:
+ return IsAggregateType(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr());
+ case clang::Type::Elaborated:
+ return IsAggregateType(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr());
+ case clang::Type::Typedef:
+ return IsAggregateType(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr());
+ case clang::Type::Paren:
+ return IsAggregateType(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr());
+ default:
+ break;
+ }
+ // The clang type does have a value
+ return false;
+}
+
+bool ClangASTContext::IsAnonymousType(lldb::opaque_compiler_type_t type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record: {
+ if (const clang::RecordType *record_type =
+ llvm::dyn_cast_or_null<clang::RecordType>(
+ qual_type.getTypePtrOrNull())) {
+ if (const clang::RecordDecl *record_decl = record_type->getDecl()) {
+ return record_decl->isAnonymousStructOrUnion();
+ }
+ }
+ break;
+ }
+ case clang::Type::Auto:
+ return IsAnonymousType(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr());
+ case clang::Type::Elaborated:
+ return IsAnonymousType(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr());
+ case clang::Type::Typedef:
+ return IsAnonymousType(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr());
+ case clang::Type::Paren:
+ return IsAnonymousType(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr());
+ default:
+ break;
+ }
+ // The clang type does have a value
+ return false;
+}
+
+bool ClangASTContext::IsArrayType(lldb::opaque_compiler_type_t type,
+ CompilerType *element_type_ptr,
+ uint64_t *size, bool *is_incomplete) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ default:
+ break;
+
+ case clang::Type::ConstantArray:
+ if (element_type_ptr)
+ element_type_ptr->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::ConstantArrayType>(qual_type)->getElementType());
+ if (size)
+ *size = llvm::cast<clang::ConstantArrayType>(qual_type)
+ ->getSize()
+ .getLimitedValue(ULLONG_MAX);
+ if (is_incomplete)
+ *is_incomplete = false;
+ return true;
+
+ case clang::Type::IncompleteArray:
+ if (element_type_ptr)
+ element_type_ptr->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::IncompleteArrayType>(qual_type)->getElementType());
+ if (size)
+ *size = 0;
+ if (is_incomplete)
+ *is_incomplete = true;
+ return true;
+
+ case clang::Type::VariableArray:
+ if (element_type_ptr)
+ element_type_ptr->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::VariableArrayType>(qual_type)->getElementType());
+ if (size)
+ *size = 0;
+ if (is_incomplete)
+ *is_incomplete = false;
+ return true;
+
+ case clang::Type::DependentSizedArray:
+ if (element_type_ptr)
+ element_type_ptr->SetCompilerType(
+ getASTContext(), llvm::cast<clang::DependentSizedArrayType>(qual_type)
+ ->getElementType());
+ if (size)
+ *size = 0;
+ if (is_incomplete)
+ *is_incomplete = false;
+ return true;
+
+ case clang::Type::Typedef:
+ return IsArrayType(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ element_type_ptr, size, is_incomplete);
+ case clang::Type::Auto:
+ return IsArrayType(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ element_type_ptr, size, is_incomplete);
+ case clang::Type::Elaborated:
+ return IsArrayType(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ element_type_ptr, size, is_incomplete);
+ case clang::Type::Paren:
+ return IsArrayType(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr(),
+ element_type_ptr, size, is_incomplete);
+ }
+ if (element_type_ptr)
+ element_type_ptr->Clear();
+ if (size)
+ *size = 0;
+ if (is_incomplete)
+ *is_incomplete = false;
+ return false;
+}
+
+bool ClangASTContext::IsVectorType(lldb::opaque_compiler_type_t type,
+ CompilerType *element_type, uint64_t *size) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Vector: {
+ const clang::VectorType *vector_type =
+ qual_type->getAs<clang::VectorType>();
+ if (vector_type) {
+ if (size)
+ *size = vector_type->getNumElements();
+ if (element_type)
+ *element_type =
+ CompilerType(getASTContext(), vector_type->getElementType());
+ }
+ return true;
+ } break;
+ case clang::Type::ExtVector: {
+ const clang::ExtVectorType *ext_vector_type =
+ qual_type->getAs<clang::ExtVectorType>();
+ if (ext_vector_type) {
+ if (size)
+ *size = ext_vector_type->getNumElements();
+ if (element_type)
+ *element_type =
+ CompilerType(getASTContext(), ext_vector_type->getElementType());
+ }
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+bool ClangASTContext::IsRuntimeGeneratedType(
+ lldb::opaque_compiler_type_t type) {
+ clang::DeclContext *decl_ctx = ClangASTContext::GetASTContext(getASTContext())
+ ->GetDeclContextForType(GetQualType(type));
+ if (!decl_ctx)
+ return false;
+
+ if (!llvm::isa<clang::ObjCInterfaceDecl>(decl_ctx))
+ return false;
+
+ clang::ObjCInterfaceDecl *result_iface_decl =
+ llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl_ctx);
+
+ ClangASTMetadata *ast_metadata =
+ ClangASTContext::GetMetadata(getASTContext(), result_iface_decl);
+ if (!ast_metadata)
+ return false;
+ return (ast_metadata->GetISAPtr() != 0);
+}
+
+bool ClangASTContext::IsCharType(lldb::opaque_compiler_type_t type) {
+ return GetQualType(type).getUnqualifiedType()->isCharType();
+}
+
+bool ClangASTContext::IsCompleteType(lldb::opaque_compiler_type_t type) {
+ const bool allow_completion = false;
+ return GetCompleteQualType(getASTContext(), GetQualType(type),
+ allow_completion);
+}
+
+bool ClangASTContext::IsConst(lldb::opaque_compiler_type_t type) {
+ return GetQualType(type).isConstQualified();
+}
+
+bool ClangASTContext::IsCStringType(lldb::opaque_compiler_type_t type,
+ uint32_t &length) {
+ CompilerType pointee_or_element_clang_type;
+ length = 0;
+ Flags type_flags(GetTypeInfo(type, &pointee_or_element_clang_type));
+
+ if (!pointee_or_element_clang_type.IsValid())
+ return false;
+
+ if (type_flags.AnySet(eTypeIsArray | eTypeIsPointer)) {
+ if (pointee_or_element_clang_type.IsCharType()) {
+ if (type_flags.Test(eTypeIsArray)) {
+ // We know the size of the array and it could be a C string since it is
+ // an array of characters
+ length = llvm::cast<clang::ConstantArrayType>(
+ GetCanonicalQualType(type).getTypePtr())
+ ->getSize()
+ .getLimitedValue();
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ClangASTContext::IsFunctionType(lldb::opaque_compiler_type_t type,
+ bool *is_variadic_ptr) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ if (qual_type->isFunctionType()) {
+ if (is_variadic_ptr) {
+ const clang::FunctionProtoType *function_proto_type =
+ llvm::dyn_cast<clang::FunctionProtoType>(qual_type.getTypePtr());
+ if (function_proto_type)
+ *is_variadic_ptr = function_proto_type->isVariadic();
+ else
+ *is_variadic_ptr = false;
+ }
+ return true;
+ }
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ default:
+ break;
+ case clang::Type::Typedef:
+ return IsFunctionType(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ nullptr);
+ case clang::Type::Auto:
+ return IsFunctionType(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ nullptr);
+ case clang::Type::Elaborated:
+ return IsFunctionType(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ nullptr);
+ case clang::Type::Paren:
+ return IsFunctionType(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr(),
+ nullptr);
+ case clang::Type::LValueReference:
+ case clang::Type::RValueReference: {
+ const clang::ReferenceType *reference_type =
+ llvm::cast<clang::ReferenceType>(qual_type.getTypePtr());
+ if (reference_type)
+ return IsFunctionType(reference_type->getPointeeType().getAsOpaquePtr(),
+ nullptr);
+ } break;
+ }
+ }
+ return false;
+}
+
+// Used to detect "Homogeneous Floating-point Aggregates"
+uint32_t
+ClangASTContext::IsHomogeneousAggregate(lldb::opaque_compiler_type_t type,
+ CompilerType *base_type_ptr) {
+ if (!type)
+ return 0;
+
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::CXXRecordDecl *cxx_record_decl =
+ qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl) {
+ if (cxx_record_decl->getNumBases() || cxx_record_decl->isDynamicClass())
+ return 0;
+ }
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+ if (record_type) {
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+ if (record_decl) {
+ // We are looking for a structure that contains only floating point
+ // types
+ clang::RecordDecl::field_iterator field_pos,
+ field_end = record_decl->field_end();
+ uint32_t num_fields = 0;
+ bool is_hva = false;
+ bool is_hfa = false;
+ clang::QualType base_qual_type;
+ uint64_t base_bitwidth = 0;
+ for (field_pos = record_decl->field_begin(); field_pos != field_end;
+ ++field_pos) {
+ clang::QualType field_qual_type = field_pos->getType();
+ uint64_t field_bitwidth = getASTContext()->getTypeSize(qual_type);
+ if (field_qual_type->isFloatingType()) {
+ if (field_qual_type->isComplexType())
+ return 0;
+ else {
+ if (num_fields == 0)
+ base_qual_type = field_qual_type;
+ else {
+ if (is_hva)
+ return 0;
+ is_hfa = true;
+ if (field_qual_type.getTypePtr() !=
+ base_qual_type.getTypePtr())
+ return 0;
+ }
+ }
+ } else if (field_qual_type->isVectorType() ||
+ field_qual_type->isExtVectorType()) {
+ if (num_fields == 0) {
+ base_qual_type = field_qual_type;
+ base_bitwidth = field_bitwidth;
+ } else {
+ if (is_hfa)
+ return 0;
+ is_hva = true;
+ if (base_bitwidth != field_bitwidth)
+ return 0;
+ if (field_qual_type.getTypePtr() != base_qual_type.getTypePtr())
+ return 0;
+ }
+ } else
+ return 0;
+ ++num_fields;
+ }
+ if (base_type_ptr)
+ *base_type_ptr = CompilerType(getASTContext(), base_qual_type);
+ return num_fields;
+ }
+ }
+ }
+ break;
+
+ case clang::Type::Typedef:
+ return IsHomogeneousAggregate(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ base_type_ptr);
+
+ case clang::Type::Auto:
+ return IsHomogeneousAggregate(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ base_type_ptr);
+
+ case clang::Type::Elaborated:
+ return IsHomogeneousAggregate(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ base_type_ptr);
+ default:
+ break;
+ }
+ return 0;
+}
+
+size_t ClangASTContext::GetNumberOfFunctionArguments(
+ lldb::opaque_compiler_type_t type) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::FunctionProtoType *func =
+ llvm::dyn_cast<clang::FunctionProtoType>(qual_type.getTypePtr());
+ if (func)
+ return func->getNumParams();
+ }
+ return 0;
+}
+
+CompilerType
+ClangASTContext::GetFunctionArgumentAtIndex(lldb::opaque_compiler_type_t type,
+ const size_t index) {
+ if (type) {
+ clang::QualType qual_type(GetQualType(type));
+ const clang::FunctionProtoType *func =
+ llvm::dyn_cast<clang::FunctionProtoType>(qual_type.getTypePtr());
+ if (func) {
+ if (index < func->getNumParams())
+ return CompilerType(getASTContext(), func->getParamType(index));
+ }
+ }
+ return CompilerType();
+}
+
+bool ClangASTContext::IsFunctionPointerType(lldb::opaque_compiler_type_t type) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ if (qual_type->isFunctionPointerType())
+ return true;
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ default:
+ break;
+ case clang::Type::Typedef:
+ return IsFunctionPointerType(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr());
+ case clang::Type::Auto:
+ return IsFunctionPointerType(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr());
+ case clang::Type::Elaborated:
+ return IsFunctionPointerType(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr());
+ case clang::Type::Paren:
+ return IsFunctionPointerType(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr());
+
+ case clang::Type::LValueReference:
+ case clang::Type::RValueReference: {
+ const clang::ReferenceType *reference_type =
+ llvm::cast<clang::ReferenceType>(qual_type.getTypePtr());
+ if (reference_type)
+ return IsFunctionPointerType(
+ reference_type->getPointeeType().getAsOpaquePtr());
+ } break;
+ }
+ }
+ return false;
+}
+
+bool ClangASTContext::IsBlockPointerType(
+ lldb::opaque_compiler_type_t type,
+ CompilerType *function_pointer_type_ptr) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ if (qual_type->isBlockPointerType()) {
+ if (function_pointer_type_ptr) {
+ const clang::BlockPointerType *block_pointer_type =
+ qual_type->getAs<clang::BlockPointerType>();
+ QualType pointee_type = block_pointer_type->getPointeeType();
+ QualType function_pointer_type = m_ast_up->getPointerType(pointee_type);
+ *function_pointer_type_ptr =
+ CompilerType(getASTContext(), function_pointer_type);
+ }
+ return true;
+ }
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ default:
+ break;
+ case clang::Type::Typedef:
+ return IsBlockPointerType(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ function_pointer_type_ptr);
+ case clang::Type::Auto:
+ return IsBlockPointerType(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ function_pointer_type_ptr);
+ case clang::Type::Elaborated:
+ return IsBlockPointerType(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ function_pointer_type_ptr);
+ case clang::Type::Paren:
+ return IsBlockPointerType(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr(),
+ function_pointer_type_ptr);
+
+ case clang::Type::LValueReference:
+ case clang::Type::RValueReference: {
+ const clang::ReferenceType *reference_type =
+ llvm::cast<clang::ReferenceType>(qual_type.getTypePtr());
+ if (reference_type)
+ return IsBlockPointerType(
+ reference_type->getPointeeType().getAsOpaquePtr(),
+ function_pointer_type_ptr);
+ } break;
+ }
+ }
+ return false;
+}
+
+bool ClangASTContext::IsIntegerType(lldb::opaque_compiler_type_t type,
+ bool &is_signed) {
+ if (!type)
+ return false;
+
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::BuiltinType *builtin_type =
+ llvm::dyn_cast<clang::BuiltinType>(qual_type->getCanonicalTypeInternal());
+
+ if (builtin_type) {
+ if (builtin_type->isInteger()) {
+ is_signed = builtin_type->isSignedInteger();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ClangASTContext::IsEnumerationType(lldb::opaque_compiler_type_t type,
+ bool &is_signed) {
+ if (type) {
+ const clang::EnumType *enum_type = llvm::dyn_cast<clang::EnumType>(
+ GetCanonicalQualType(type)->getCanonicalTypeInternal());
+
+ if (enum_type) {
+ IsIntegerType(enum_type->getDecl()->getIntegerType().getAsOpaquePtr(),
+ is_signed);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ClangASTContext::IsPointerType(lldb::opaque_compiler_type_t type,
+ CompilerType *pointee_type) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Builtin:
+ switch (llvm::cast<clang::BuiltinType>(qual_type)->getKind()) {
+ default:
+ break;
+ case clang::BuiltinType::ObjCId:
+ case clang::BuiltinType::ObjCClass:
+ return true;
+ }
+ return false;
+ case clang::Type::ObjCObjectPointer:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(), llvm::cast<clang::ObjCObjectPointerType>(qual_type)
+ ->getPointeeType());
+ return true;
+ case clang::Type::BlockPointer:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::BlockPointerType>(qual_type)->getPointeeType());
+ return true;
+ case clang::Type::Pointer:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::PointerType>(qual_type)->getPointeeType());
+ return true;
+ case clang::Type::MemberPointer:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::MemberPointerType>(qual_type)->getPointeeType());
+ return true;
+ case clang::Type::Typedef:
+ return IsPointerType(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ pointee_type);
+ case clang::Type::Auto:
+ return IsPointerType(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ pointee_type);
+ case clang::Type::Elaborated:
+ return IsPointerType(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ pointee_type);
+ case clang::Type::Paren:
+ return IsPointerType(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr(),
+ pointee_type);
+ default:
+ break;
+ }
+ }
+ if (pointee_type)
+ pointee_type->Clear();
+ return false;
+}
+
+bool ClangASTContext::IsPointerOrReferenceType(
+ lldb::opaque_compiler_type_t type, CompilerType *pointee_type) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Builtin:
+ switch (llvm::cast<clang::BuiltinType>(qual_type)->getKind()) {
+ default:
+ break;
+ case clang::BuiltinType::ObjCId:
+ case clang::BuiltinType::ObjCClass:
+ return true;
+ }
+ return false;
+ case clang::Type::ObjCObjectPointer:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(), llvm::cast<clang::ObjCObjectPointerType>(qual_type)
+ ->getPointeeType());
+ return true;
+ case clang::Type::BlockPointer:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::BlockPointerType>(qual_type)->getPointeeType());
+ return true;
+ case clang::Type::Pointer:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::PointerType>(qual_type)->getPointeeType());
+ return true;
+ case clang::Type::MemberPointer:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::MemberPointerType>(qual_type)->getPointeeType());
+ return true;
+ case clang::Type::LValueReference:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::LValueReferenceType>(qual_type)->desugar());
+ return true;
+ case clang::Type::RValueReference:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::RValueReferenceType>(qual_type)->desugar());
+ return true;
+ case clang::Type::Typedef:
+ return IsPointerOrReferenceType(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ pointee_type);
+ case clang::Type::Auto:
+ return IsPointerOrReferenceType(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ pointee_type);
+ case clang::Type::Elaborated:
+ return IsPointerOrReferenceType(
+ llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ pointee_type);
+ case clang::Type::Paren:
+ return IsPointerOrReferenceType(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr(),
+ pointee_type);
+ default:
+ break;
+ }
+ }
+ if (pointee_type)
+ pointee_type->Clear();
+ return false;
+}
+
+bool ClangASTContext::IsReferenceType(lldb::opaque_compiler_type_t type,
+ CompilerType *pointee_type,
+ bool *is_rvalue) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+
+ switch (type_class) {
+ case clang::Type::LValueReference:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::LValueReferenceType>(qual_type)->desugar());
+ if (is_rvalue)
+ *is_rvalue = false;
+ return true;
+ case clang::Type::RValueReference:
+ if (pointee_type)
+ pointee_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::RValueReferenceType>(qual_type)->desugar());
+ if (is_rvalue)
+ *is_rvalue = true;
+ return true;
+ case clang::Type::Typedef:
+ return IsReferenceType(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ pointee_type, is_rvalue);
+ case clang::Type::Auto:
+ return IsReferenceType(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ pointee_type, is_rvalue);
+ case clang::Type::Elaborated:
+ return IsReferenceType(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ pointee_type, is_rvalue);
+ case clang::Type::Paren:
+ return IsReferenceType(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr(),
+ pointee_type, is_rvalue);
+
+ default:
+ break;
+ }
+ }
+ if (pointee_type)
+ pointee_type->Clear();
+ return false;
+}
+
+bool ClangASTContext::IsFloatingPointType(lldb::opaque_compiler_type_t type,
+ uint32_t &count, bool &is_complex) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ if (const clang::BuiltinType *BT = llvm::dyn_cast<clang::BuiltinType>(
+ qual_type->getCanonicalTypeInternal())) {
+ clang::BuiltinType::Kind kind = BT->getKind();
+ if (kind >= clang::BuiltinType::Float &&
+ kind <= clang::BuiltinType::LongDouble) {
+ count = 1;
+ is_complex = false;
+ return true;
+ }
+ } else if (const clang::ComplexType *CT =
+ llvm::dyn_cast<clang::ComplexType>(
+ qual_type->getCanonicalTypeInternal())) {
+ if (IsFloatingPointType(CT->getElementType().getAsOpaquePtr(), count,
+ is_complex)) {
+ count = 2;
+ is_complex = true;
+ return true;
+ }
+ } else if (const clang::VectorType *VT = llvm::dyn_cast<clang::VectorType>(
+ qual_type->getCanonicalTypeInternal())) {
+ if (IsFloatingPointType(VT->getElementType().getAsOpaquePtr(), count,
+ is_complex)) {
+ count = VT->getNumElements();
+ is_complex = false;
+ return true;
+ }
+ }
+ }
+ count = 0;
+ is_complex = false;
+ return false;
+}
+
+bool ClangASTContext::IsDefined(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return false;
+
+ clang::QualType qual_type(GetQualType(type));
+ const clang::TagType *tag_type =
+ llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr());
+ if (tag_type) {
+ clang::TagDecl *tag_decl = tag_type->getDecl();
+ if (tag_decl)
+ return tag_decl->isCompleteDefinition();
+ return false;
+ } else {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type);
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+ if (class_interface_decl)
+ return class_interface_decl->getDefinition() != nullptr;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ClangASTContext::IsObjCClassType(const CompilerType &type) {
+ if (type) {
+ clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type));
+
+ const clang::ObjCObjectPointerType *obj_pointer_type =
+ llvm::dyn_cast<clang::ObjCObjectPointerType>(qual_type);
+
+ if (obj_pointer_type)
+ return obj_pointer_type->isObjCClassType();
+ }
+ return false;
+}
+
+bool ClangASTContext::IsObjCObjectOrInterfaceType(const CompilerType &type) {
+ if (ClangUtil::IsClangType(type))
+ return ClangUtil::GetCanonicalQualType(type)->isObjCObjectOrInterfaceType();
+ return false;
+}
+
+bool ClangASTContext::IsClassType(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return false;
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ return (type_class == clang::Type::Record);
+}
+
+bool ClangASTContext::IsEnumType(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return false;
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ return (type_class == clang::Type::Enum);
+}
+
+bool ClangASTContext::IsPolymorphicClass(lldb::opaque_compiler_type_t type) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+ if (record_decl) {
+ const clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
+ if (cxx_record_decl)
+ return cxx_record_decl->isPolymorphic();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+bool ClangASTContext::IsPossibleDynamicType(lldb::opaque_compiler_type_t type,
+ CompilerType *dynamic_pointee_type,
+ bool check_cplusplus,
+ bool check_objc) {
+ clang::QualType pointee_qual_type;
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ bool success = false;
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Builtin:
+ if (check_objc &&
+ llvm::cast<clang::BuiltinType>(qual_type)->getKind() ==
+ clang::BuiltinType::ObjCId) {
+ if (dynamic_pointee_type)
+ dynamic_pointee_type->SetCompilerType(this, type);
+ return true;
+ }
+ break;
+
+ case clang::Type::ObjCObjectPointer:
+ if (check_objc) {
+ if (auto objc_pointee_type =
+ qual_type->getPointeeType().getTypePtrOrNull()) {
+ if (auto objc_object_type =
+ llvm::dyn_cast_or_null<clang::ObjCObjectType>(
+ objc_pointee_type)) {
+ if (objc_object_type->isObjCClass())
+ return false;
+ }
+ }
+ if (dynamic_pointee_type)
+ dynamic_pointee_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::ObjCObjectPointerType>(qual_type)
+ ->getPointeeType());
+ return true;
+ }
+ break;
+
+ case clang::Type::Pointer:
+ pointee_qual_type =
+ llvm::cast<clang::PointerType>(qual_type)->getPointeeType();
+ success = true;
+ break;
+
+ case clang::Type::LValueReference:
+ case clang::Type::RValueReference:
+ pointee_qual_type =
+ llvm::cast<clang::ReferenceType>(qual_type)->getPointeeType();
+ success = true;
+ break;
+
+ case clang::Type::Typedef:
+ return IsPossibleDynamicType(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ dynamic_pointee_type, check_cplusplus,
+ check_objc);
+
+ case clang::Type::Auto:
+ return IsPossibleDynamicType(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ dynamic_pointee_type, check_cplusplus,
+ check_objc);
+
+ case clang::Type::Elaborated:
+ return IsPossibleDynamicType(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ dynamic_pointee_type, check_cplusplus,
+ check_objc);
+
+ case clang::Type::Paren:
+ return IsPossibleDynamicType(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr(),
+ dynamic_pointee_type, check_cplusplus, check_objc);
+ default:
+ break;
+ }
+
+ if (success) {
+ // Check to make sure what we are pointing too is a possible dynamic C++
+ // type We currently accept any "void *" (in case we have a class that
+ // has been watered down to an opaque pointer) and virtual C++ classes.
+ const clang::Type::TypeClass pointee_type_class =
+ pointee_qual_type.getCanonicalType()->getTypeClass();
+ switch (pointee_type_class) {
+ case clang::Type::Builtin:
+ switch (llvm::cast<clang::BuiltinType>(pointee_qual_type)->getKind()) {
+ case clang::BuiltinType::UnknownAny:
+ case clang::BuiltinType::Void:
+ if (dynamic_pointee_type)
+ dynamic_pointee_type->SetCompilerType(getASTContext(),
+ pointee_qual_type);
+ return true;
+ default:
+ break;
+ }
+ break;
+
+ case clang::Type::Record:
+ if (check_cplusplus) {
+ clang::CXXRecordDecl *cxx_record_decl =
+ pointee_qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl) {
+ bool is_complete = cxx_record_decl->isCompleteDefinition();
+
+ if (is_complete)
+ success = cxx_record_decl->isDynamicClass();
+ else {
+ ClangASTMetadata *metadata = ClangASTContext::GetMetadata(
+ getASTContext(), cxx_record_decl);
+ if (metadata)
+ success = metadata->GetIsDynamicCXXType();
+ else {
+ is_complete = CompilerType(getASTContext(), pointee_qual_type)
+ .GetCompleteType();
+ if (is_complete)
+ success = cxx_record_decl->isDynamicClass();
+ else
+ success = false;
+ }
+ }
+
+ if (success) {
+ if (dynamic_pointee_type)
+ dynamic_pointee_type->SetCompilerType(getASTContext(),
+ pointee_qual_type);
+ return true;
+ }
+ }
+ }
+ break;
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface:
+ if (check_objc) {
+ if (dynamic_pointee_type)
+ dynamic_pointee_type->SetCompilerType(getASTContext(),
+ pointee_qual_type);
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ if (dynamic_pointee_type)
+ dynamic_pointee_type->Clear();
+ return false;
+}
+
+bool ClangASTContext::IsScalarType(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return false;
+
+ return (GetTypeInfo(type, nullptr) & eTypeIsScalar) != 0;
+}
+
+bool ClangASTContext::IsTypedefType(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return false;
+ return GetQualType(type)->getTypeClass() == clang::Type::Typedef;
+}
+
+bool ClangASTContext::IsVoidType(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return false;
+ return GetCanonicalQualType(type)->isVoidType();
+}
+
+bool ClangASTContext::CanPassInRegisters(const CompilerType &type) {
+ if (auto *record_decl =
+ ClangASTContext::GetAsRecordDecl(type)) {
+ return record_decl->canPassInRegisters();
+ }
+ return false;
+}
+
+bool ClangASTContext::SupportsLanguage(lldb::LanguageType language) {
+ return ClangASTContextSupportsLanguage(language);
+}
+
+bool ClangASTContext::GetCXXClassName(const CompilerType &type,
+ std::string &class_name) {
+ if (type) {
+ clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type));
+ if (!qual_type.isNull()) {
+ clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl) {
+ class_name.assign(cxx_record_decl->getIdentifier()->getNameStart());
+ return true;
+ }
+ }
+ }
+ class_name.clear();
+ return false;
+}
+
+bool ClangASTContext::IsCXXClassType(const CompilerType &type) {
+ if (!type)
+ return false;
+
+ clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type));
+ return !qual_type.isNull() && qual_type->getAsCXXRecordDecl() != nullptr;
+}
+
+bool ClangASTContext::IsBeingDefined(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return false;
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::TagType *tag_type = llvm::dyn_cast<clang::TagType>(qual_type);
+ if (tag_type)
+ return tag_type->isBeingDefined();
+ return false;
+}
+
+bool ClangASTContext::IsObjCObjectPointerType(const CompilerType &type,
+ CompilerType *class_type_ptr) {
+ if (!type)
+ return false;
+
+ clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type));
+
+ if (!qual_type.isNull() && qual_type->isObjCObjectPointerType()) {
+ if (class_type_ptr) {
+ if (!qual_type->isObjCClassType() && !qual_type->isObjCIdType()) {
+ const clang::ObjCObjectPointerType *obj_pointer_type =
+ llvm::dyn_cast<clang::ObjCObjectPointerType>(qual_type);
+ if (obj_pointer_type == nullptr)
+ class_type_ptr->Clear();
+ else
+ class_type_ptr->SetCompilerType(
+ type.GetTypeSystem(),
+ clang::QualType(obj_pointer_type->getInterfaceType(), 0)
+ .getAsOpaquePtr());
+ }
+ }
+ return true;
+ }
+ if (class_type_ptr)
+ class_type_ptr->Clear();
+ return false;
+}
+
+bool ClangASTContext::GetObjCClassName(const CompilerType &type,
+ std::string &class_name) {
+ if (!type)
+ return false;
+
+ clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type));
+
+ const clang::ObjCObjectType *object_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type);
+ if (object_type) {
+ const clang::ObjCInterfaceDecl *interface = object_type->getInterface();
+ if (interface) {
+ class_name = interface->getNameAsString();
+ return true;
+ }
+ }
+ return false;
+}
+
+// Type Completion
+
+bool ClangASTContext::GetCompleteType(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return false;
+ const bool allow_completion = true;
+ return GetCompleteQualType(getASTContext(), GetQualType(type),
+ allow_completion);
+}
+
+ConstString ClangASTContext::GetTypeName(lldb::opaque_compiler_type_t type) {
+ std::string type_name;
+ if (type) {
+ clang::PrintingPolicy printing_policy(getASTContext()->getPrintingPolicy());
+ clang::QualType qual_type(GetQualType(type));
+ printing_policy.SuppressTagKeyword = true;
+ const clang::TypedefType *typedef_type =
+ qual_type->getAs<clang::TypedefType>();
+ if (typedef_type) {
+ const clang::TypedefNameDecl *typedef_decl = typedef_type->getDecl();
+ type_name = typedef_decl->getQualifiedNameAsString();
+ } else {
+ type_name = qual_type.getAsString(printing_policy);
+ }
+ }
+ return ConstString(type_name);
+}
+
+uint32_t
+ClangASTContext::GetTypeInfo(lldb::opaque_compiler_type_t type,
+ CompilerType *pointee_or_element_clang_type) {
+ if (!type)
+ return 0;
+
+ if (pointee_or_element_clang_type)
+ pointee_or_element_clang_type->Clear();
+
+ clang::QualType qual_type(GetQualType(type));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Attributed:
+ return GetTypeInfo(
+ qual_type->getAs<clang::AttributedType>()
+ ->getModifiedType().getAsOpaquePtr(),
+ pointee_or_element_clang_type);
+ case clang::Type::Builtin: {
+ const clang::BuiltinType *builtin_type = llvm::dyn_cast<clang::BuiltinType>(
+ qual_type->getCanonicalTypeInternal());
+
+ uint32_t builtin_type_flags = eTypeIsBuiltIn | eTypeHasValue;
+ switch (builtin_type->getKind()) {
+ case clang::BuiltinType::ObjCId:
+ case clang::BuiltinType::ObjCClass:
+ if (pointee_or_element_clang_type)
+ pointee_or_element_clang_type->SetCompilerType(
+ getASTContext(), getASTContext()->ObjCBuiltinClassTy);
+ builtin_type_flags |= eTypeIsPointer | eTypeIsObjC;
+ break;
+
+ case clang::BuiltinType::ObjCSel:
+ if (pointee_or_element_clang_type)
+ pointee_or_element_clang_type->SetCompilerType(getASTContext(),
+ getASTContext()->CharTy);
+ builtin_type_flags |= eTypeIsPointer | eTypeIsObjC;
+ break;
+
+ case clang::BuiltinType::Bool:
+ case clang::BuiltinType::Char_U:
+ case clang::BuiltinType::UChar:
+ case clang::BuiltinType::WChar_U:
+ case clang::BuiltinType::Char16:
+ case clang::BuiltinType::Char32:
+ case clang::BuiltinType::UShort:
+ case clang::BuiltinType::UInt:
+ case clang::BuiltinType::ULong:
+ case clang::BuiltinType::ULongLong:
+ case clang::BuiltinType::UInt128:
+ case clang::BuiltinType::Char_S:
+ case clang::BuiltinType::SChar:
+ case clang::BuiltinType::WChar_S:
+ case clang::BuiltinType::Short:
+ case clang::BuiltinType::Int:
+ case clang::BuiltinType::Long:
+ case clang::BuiltinType::LongLong:
+ case clang::BuiltinType::Int128:
+ case clang::BuiltinType::Float:
+ case clang::BuiltinType::Double:
+ case clang::BuiltinType::LongDouble:
+ builtin_type_flags |= eTypeIsScalar;
+ if (builtin_type->isInteger()) {
+ builtin_type_flags |= eTypeIsInteger;
+ if (builtin_type->isSignedInteger())
+ builtin_type_flags |= eTypeIsSigned;
+ } else if (builtin_type->isFloatingPoint())
+ builtin_type_flags |= eTypeIsFloat;
+ break;
+ default:
+ break;
+ }
+ return builtin_type_flags;
+ }
+
+ case clang::Type::BlockPointer:
+ if (pointee_or_element_clang_type)
+ pointee_or_element_clang_type->SetCompilerType(
+ getASTContext(), qual_type->getPointeeType());
+ return eTypeIsPointer | eTypeHasChildren | eTypeIsBlock;
+
+ case clang::Type::Complex: {
+ uint32_t complex_type_flags =
+ eTypeIsBuiltIn | eTypeHasValue | eTypeIsComplex;
+ const clang::ComplexType *complex_type = llvm::dyn_cast<clang::ComplexType>(
+ qual_type->getCanonicalTypeInternal());
+ if (complex_type) {
+ clang::QualType complex_element_type(complex_type->getElementType());
+ if (complex_element_type->isIntegerType())
+ complex_type_flags |= eTypeIsFloat;
+ else if (complex_element_type->isFloatingType())
+ complex_type_flags |= eTypeIsInteger;
+ }
+ return complex_type_flags;
+ } break;
+
+ case clang::Type::ConstantArray:
+ case clang::Type::DependentSizedArray:
+ case clang::Type::IncompleteArray:
+ case clang::Type::VariableArray:
+ if (pointee_or_element_clang_type)
+ pointee_or_element_clang_type->SetCompilerType(
+ getASTContext(), llvm::cast<clang::ArrayType>(qual_type.getTypePtr())
+ ->getElementType());
+ return eTypeHasChildren | eTypeIsArray;
+
+ case clang::Type::DependentName:
+ return 0;
+ case clang::Type::DependentSizedExtVector:
+ return eTypeHasChildren | eTypeIsVector;
+ case clang::Type::DependentTemplateSpecialization:
+ return eTypeIsTemplate;
+ case clang::Type::Decltype:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::DecltypeType>(qual_type)->getUnderlyingType())
+ .GetTypeInfo(pointee_or_element_clang_type);
+
+ case clang::Type::Enum:
+ if (pointee_or_element_clang_type)
+ pointee_or_element_clang_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::EnumType>(qual_type)->getDecl()->getIntegerType());
+ return eTypeIsEnumeration | eTypeHasValue;
+
+ case clang::Type::Auto:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType())
+ .GetTypeInfo(pointee_or_element_clang_type);
+ case clang::Type::Elaborated:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType())
+ .GetTypeInfo(pointee_or_element_clang_type);
+ case clang::Type::Paren:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar())
+ .GetTypeInfo(pointee_or_element_clang_type);
+
+ case clang::Type::FunctionProto:
+ return eTypeIsFuncPrototype | eTypeHasValue;
+ case clang::Type::FunctionNoProto:
+ return eTypeIsFuncPrototype | eTypeHasValue;
+ case clang::Type::InjectedClassName:
+ return 0;
+
+ case clang::Type::LValueReference:
+ case clang::Type::RValueReference:
+ if (pointee_or_element_clang_type)
+ pointee_or_element_clang_type->SetCompilerType(
+ getASTContext(),
+ llvm::cast<clang::ReferenceType>(qual_type.getTypePtr())
+ ->getPointeeType());
+ return eTypeHasChildren | eTypeIsReference | eTypeHasValue;
+
+ case clang::Type::MemberPointer:
+ return eTypeIsPointer | eTypeIsMember | eTypeHasValue;
+
+ case clang::Type::ObjCObjectPointer:
+ if (pointee_or_element_clang_type)
+ pointee_or_element_clang_type->SetCompilerType(
+ getASTContext(), qual_type->getPointeeType());
+ return eTypeHasChildren | eTypeIsObjC | eTypeIsClass | eTypeIsPointer |
+ eTypeHasValue;
+
+ case clang::Type::ObjCObject:
+ return eTypeHasChildren | eTypeIsObjC | eTypeIsClass;
+ case clang::Type::ObjCInterface:
+ return eTypeHasChildren | eTypeIsObjC | eTypeIsClass;
+
+ case clang::Type::Pointer:
+ if (pointee_or_element_clang_type)
+ pointee_or_element_clang_type->SetCompilerType(
+ getASTContext(), qual_type->getPointeeType());
+ return eTypeHasChildren | eTypeIsPointer | eTypeHasValue;
+
+ case clang::Type::Record:
+ if (qual_type->getAsCXXRecordDecl())
+ return eTypeHasChildren | eTypeIsClass | eTypeIsCPlusPlus;
+ else
+ return eTypeHasChildren | eTypeIsStructUnion;
+ break;
+ case clang::Type::SubstTemplateTypeParm:
+ return eTypeIsTemplate;
+ case clang::Type::TemplateTypeParm:
+ return eTypeIsTemplate;
+ case clang::Type::TemplateSpecialization:
+ return eTypeIsTemplate;
+
+ case clang::Type::Typedef:
+ return eTypeIsTypedef |
+ CompilerType(getASTContext(),
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType())
+ .GetTypeInfo(pointee_or_element_clang_type);
+ case clang::Type::TypeOfExpr:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::TypeOfExprType>(qual_type)
+ ->getUnderlyingExpr()
+ ->getType())
+ .GetTypeInfo(pointee_or_element_clang_type);
+ case clang::Type::TypeOf:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::TypeOfType>(qual_type)->getUnderlyingType())
+ .GetTypeInfo(pointee_or_element_clang_type);
+ case clang::Type::UnresolvedUsing:
+ return 0;
+
+ case clang::Type::ExtVector:
+ case clang::Type::Vector: {
+ uint32_t vector_type_flags = eTypeHasChildren | eTypeIsVector;
+ const clang::VectorType *vector_type = llvm::dyn_cast<clang::VectorType>(
+ qual_type->getCanonicalTypeInternal());
+ if (vector_type) {
+ if (vector_type->isIntegerType())
+ vector_type_flags |= eTypeIsFloat;
+ else if (vector_type->isFloatingType())
+ vector_type_flags |= eTypeIsInteger;
+ }
+ return vector_type_flags;
+ }
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+lldb::LanguageType
+ClangASTContext::GetMinimumLanguage(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return lldb::eLanguageTypeC;
+
+ // If the type is a reference, then resolve it to what it refers to first:
+ clang::QualType qual_type(GetCanonicalQualType(type).getNonReferenceType());
+ if (qual_type->isAnyPointerType()) {
+ if (qual_type->isObjCObjectPointerType())
+ return lldb::eLanguageTypeObjC;
+ if (qual_type->getPointeeCXXRecordDecl())
+ return lldb::eLanguageTypeC_plus_plus;
+
+ clang::QualType pointee_type(qual_type->getPointeeType());
+ if (pointee_type->getPointeeCXXRecordDecl())
+ return lldb::eLanguageTypeC_plus_plus;
+ if (pointee_type->isObjCObjectOrInterfaceType())
+ return lldb::eLanguageTypeObjC;
+ if (pointee_type->isObjCClassType())
+ return lldb::eLanguageTypeObjC;
+ if (pointee_type.getTypePtr() ==
+ getASTContext()->ObjCBuiltinIdTy.getTypePtr())
+ return lldb::eLanguageTypeObjC;
+ } else {
+ if (qual_type->isObjCObjectOrInterfaceType())
+ return lldb::eLanguageTypeObjC;
+ if (qual_type->getAsCXXRecordDecl())
+ return lldb::eLanguageTypeC_plus_plus;
+ switch (qual_type->getTypeClass()) {
+ default:
+ break;
+ case clang::Type::Builtin:
+ switch (llvm::cast<clang::BuiltinType>(qual_type)->getKind()) {
+ default:
+ case clang::BuiltinType::Void:
+ case clang::BuiltinType::Bool:
+ case clang::BuiltinType::Char_U:
+ case clang::BuiltinType::UChar:
+ case clang::BuiltinType::WChar_U:
+ case clang::BuiltinType::Char16:
+ case clang::BuiltinType::Char32:
+ case clang::BuiltinType::UShort:
+ case clang::BuiltinType::UInt:
+ case clang::BuiltinType::ULong:
+ case clang::BuiltinType::ULongLong:
+ case clang::BuiltinType::UInt128:
+ case clang::BuiltinType::Char_S:
+ case clang::BuiltinType::SChar:
+ case clang::BuiltinType::WChar_S:
+ case clang::BuiltinType::Short:
+ case clang::BuiltinType::Int:
+ case clang::BuiltinType::Long:
+ case clang::BuiltinType::LongLong:
+ case clang::BuiltinType::Int128:
+ case clang::BuiltinType::Float:
+ case clang::BuiltinType::Double:
+ case clang::BuiltinType::LongDouble:
+ break;
+
+ case clang::BuiltinType::NullPtr:
+ return eLanguageTypeC_plus_plus;
+
+ case clang::BuiltinType::ObjCId:
+ case clang::BuiltinType::ObjCClass:
+ case clang::BuiltinType::ObjCSel:
+ return eLanguageTypeObjC;
+
+ case clang::BuiltinType::Dependent:
+ case clang::BuiltinType::Overload:
+ case clang::BuiltinType::BoundMember:
+ case clang::BuiltinType::UnknownAny:
+ break;
+ }
+ break;
+ case clang::Type::Typedef:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType())
+ .GetMinimumLanguage();
+ }
+ }
+ return lldb::eLanguageTypeC;
+}
+
+lldb::TypeClass
+ClangASTContext::GetTypeClass(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return lldb::eTypeClassInvalid;
+
+ clang::QualType qual_type(GetQualType(type));
+
+ switch (qual_type->getTypeClass()) {
+ case clang::Type::UnaryTransform:
+ break;
+ case clang::Type::FunctionNoProto:
+ return lldb::eTypeClassFunction;
+ case clang::Type::FunctionProto:
+ return lldb::eTypeClassFunction;
+ case clang::Type::IncompleteArray:
+ return lldb::eTypeClassArray;
+ case clang::Type::VariableArray:
+ return lldb::eTypeClassArray;
+ case clang::Type::ConstantArray:
+ return lldb::eTypeClassArray;
+ case clang::Type::DependentSizedArray:
+ return lldb::eTypeClassArray;
+ case clang::Type::DependentSizedExtVector:
+ return lldb::eTypeClassVector;
+ case clang::Type::DependentVector:
+ return lldb::eTypeClassVector;
+ case clang::Type::ExtVector:
+ return lldb::eTypeClassVector;
+ case clang::Type::Vector:
+ return lldb::eTypeClassVector;
+ case clang::Type::Builtin:
+ return lldb::eTypeClassBuiltin;
+ case clang::Type::ObjCObjectPointer:
+ return lldb::eTypeClassObjCObjectPointer;
+ case clang::Type::BlockPointer:
+ return lldb::eTypeClassBlockPointer;
+ case clang::Type::Pointer:
+ return lldb::eTypeClassPointer;
+ case clang::Type::LValueReference:
+ return lldb::eTypeClassReference;
+ case clang::Type::RValueReference:
+ return lldb::eTypeClassReference;
+ case clang::Type::MemberPointer:
+ return lldb::eTypeClassMemberPointer;
+ case clang::Type::Complex:
+ if (qual_type->isComplexType())
+ return lldb::eTypeClassComplexFloat;
+ else
+ return lldb::eTypeClassComplexInteger;
+ case clang::Type::ObjCObject:
+ return lldb::eTypeClassObjCObject;
+ case clang::Type::ObjCInterface:
+ return lldb::eTypeClassObjCInterface;
+ case clang::Type::Record: {
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+ if (record_decl->isUnion())
+ return lldb::eTypeClassUnion;
+ else if (record_decl->isStruct())
+ return lldb::eTypeClassStruct;
+ else
+ return lldb::eTypeClassClass;
+ } break;
+ case clang::Type::Enum:
+ return lldb::eTypeClassEnumeration;
+ case clang::Type::Typedef:
+ return lldb::eTypeClassTypedef;
+ case clang::Type::UnresolvedUsing:
+ break;
+ case clang::Type::Paren:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar())
+ .GetTypeClass();
+ case clang::Type::Auto:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType())
+ .GetTypeClass();
+ case clang::Type::Elaborated:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType())
+ .GetTypeClass();
+
+ case clang::Type::Attributed:
+ break;
+ case clang::Type::TemplateTypeParm:
+ break;
+ case clang::Type::SubstTemplateTypeParm:
+ break;
+ case clang::Type::SubstTemplateTypeParmPack:
+ break;
+ case clang::Type::InjectedClassName:
+ break;
+ case clang::Type::DependentName:
+ break;
+ case clang::Type::DependentTemplateSpecialization:
+ break;
+ case clang::Type::PackExpansion:
+ break;
+
+ case clang::Type::TypeOfExpr:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::TypeOfExprType>(qual_type)
+ ->getUnderlyingExpr()
+ ->getType())
+ .GetTypeClass();
+ case clang::Type::TypeOf:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::TypeOfType>(qual_type)->getUnderlyingType())
+ .GetTypeClass();
+ case clang::Type::Decltype:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::TypeOfType>(qual_type)->getUnderlyingType())
+ .GetTypeClass();
+ case clang::Type::TemplateSpecialization:
+ break;
+ case clang::Type::DeducedTemplateSpecialization:
+ break;
+ case clang::Type::Atomic:
+ break;
+ case clang::Type::Pipe:
+ break;
+
+ // pointer type decayed from an array or function type.
+ case clang::Type::Decayed:
+ break;
+ case clang::Type::Adjusted:
+ break;
+ case clang::Type::ObjCTypeParam:
+ break;
+
+ case clang::Type::DependentAddressSpace:
+ break;
+ case clang::Type::MacroQualified:
+ break;
+ }
+ // We don't know hot to display this type...
+ return lldb::eTypeClassOther;
+}
+
+unsigned ClangASTContext::GetTypeQualifiers(lldb::opaque_compiler_type_t type) {
+ if (type)
+ return GetQualType(type).getQualifiers().getCVRQualifiers();
+ return 0;
+}
+
+// Creating related types
+
+CompilerType
+ClangASTContext::GetArrayElementType(lldb::opaque_compiler_type_t type,
+ uint64_t *stride) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ const clang::Type *array_eletype =
+ qual_type.getTypePtr()->getArrayElementTypeNoTypeQual();
+
+ if (!array_eletype)
+ return CompilerType();
+
+ CompilerType element_type(getASTContext(),
+ array_eletype->getCanonicalTypeUnqualified());
+
+ // TODO: the real stride will be >= this value.. find the real one!
+ if (stride)
+ if (Optional<uint64_t> size = element_type.GetByteSize(nullptr))
+ *stride = *size;
+
+ return element_type;
+ }
+ return CompilerType();
+}
+
+CompilerType ClangASTContext::GetArrayType(lldb::opaque_compiler_type_t type,
+ uint64_t size) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ if (clang::ASTContext *ast_ctx = getASTContext()) {
+ if (size != 0)
+ return CompilerType(
+ ast_ctx, ast_ctx->getConstantArrayType(
+ qual_type, llvm::APInt(64, size),
+ clang::ArrayType::ArraySizeModifier::Normal, 0));
+ else
+ return CompilerType(
+ ast_ctx,
+ ast_ctx->getIncompleteArrayType(
+ qual_type, clang::ArrayType::ArraySizeModifier::Normal, 0));
+ }
+ }
+
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::GetCanonicalType(lldb::opaque_compiler_type_t type) {
+ if (type)
+ return CompilerType(getASTContext(), GetCanonicalQualType(type));
+ return CompilerType();
+}
+
+static clang::QualType GetFullyUnqualifiedType_Impl(clang::ASTContext *ast,
+ clang::QualType qual_type) {
+ if (qual_type->isPointerType())
+ qual_type = ast->getPointerType(
+ GetFullyUnqualifiedType_Impl(ast, qual_type->getPointeeType()));
+ else
+ qual_type = qual_type.getUnqualifiedType();
+ qual_type.removeLocalConst();
+ qual_type.removeLocalRestrict();
+ qual_type.removeLocalVolatile();
+ return qual_type;
+}
+
+CompilerType
+ClangASTContext::GetFullyUnqualifiedType(lldb::opaque_compiler_type_t type) {
+ if (type)
+ return CompilerType(
+ getASTContext(),
+ GetFullyUnqualifiedType_Impl(getASTContext(), GetQualType(type)));
+ return CompilerType();
+}
+
+int ClangASTContext::GetFunctionArgumentCount(
+ lldb::opaque_compiler_type_t type) {
+ if (type) {
+ const clang::FunctionProtoType *func =
+ llvm::dyn_cast<clang::FunctionProtoType>(GetCanonicalQualType(type));
+ if (func)
+ return func->getNumParams();
+ }
+ return -1;
+}
+
+CompilerType ClangASTContext::GetFunctionArgumentTypeAtIndex(
+ lldb::opaque_compiler_type_t type, size_t idx) {
+ if (type) {
+ const clang::FunctionProtoType *func =
+ llvm::dyn_cast<clang::FunctionProtoType>(GetQualType(type));
+ if (func) {
+ const uint32_t num_args = func->getNumParams();
+ if (idx < num_args)
+ return CompilerType(getASTContext(), func->getParamType(idx));
+ }
+ }
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::GetFunctionReturnType(lldb::opaque_compiler_type_t type) {
+ if (type) {
+ clang::QualType qual_type(GetQualType(type));
+ const clang::FunctionProtoType *func =
+ llvm::dyn_cast<clang::FunctionProtoType>(qual_type.getTypePtr());
+ if (func)
+ return CompilerType(getASTContext(), func->getReturnType());
+ }
+ return CompilerType();
+}
+
+size_t
+ClangASTContext::GetNumMemberFunctions(lldb::opaque_compiler_type_t type) {
+ size_t num_functions = 0;
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ switch (qual_type->getTypeClass()) {
+ case clang::Type::Record:
+ if (GetCompleteQualType(getASTContext(), qual_type)) {
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+ assert(record_decl);
+ const clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
+ if (cxx_record_decl)
+ num_functions = std::distance(cxx_record_decl->method_begin(),
+ cxx_record_decl->method_end());
+ }
+ break;
+
+ case clang::Type::ObjCObjectPointer: {
+ const clang::ObjCObjectPointerType *objc_class_type =
+ qual_type->getAs<clang::ObjCObjectPointerType>();
+ const clang::ObjCInterfaceType *objc_interface_type =
+ objc_class_type->getInterfaceType();
+ if (objc_interface_type &&
+ GetCompleteType(static_cast<lldb::opaque_compiler_type_t>(
+ const_cast<clang::ObjCInterfaceType *>(objc_interface_type)))) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_interface_type->getDecl();
+ if (class_interface_decl) {
+ num_functions = std::distance(class_interface_decl->meth_begin(),
+ class_interface_decl->meth_end());
+ }
+ }
+ break;
+ }
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface:
+ if (GetCompleteType(type)) {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr());
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+ if (class_interface_decl)
+ num_functions = std::distance(class_interface_decl->meth_begin(),
+ class_interface_decl->meth_end());
+ }
+ }
+ break;
+
+ case clang::Type::Typedef:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType())
+ .GetNumMemberFunctions();
+
+ case clang::Type::Auto:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType())
+ .GetNumMemberFunctions();
+
+ case clang::Type::Elaborated:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType())
+ .GetNumMemberFunctions();
+
+ case clang::Type::Paren:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar())
+ .GetNumMemberFunctions();
+
+ default:
+ break;
+ }
+ }
+ return num_functions;
+}
+
+TypeMemberFunctionImpl
+ClangASTContext::GetMemberFunctionAtIndex(lldb::opaque_compiler_type_t type,
+ size_t idx) {
+ std::string name;
+ MemberFunctionKind kind(MemberFunctionKind::eMemberFunctionKindUnknown);
+ CompilerType clang_type;
+ CompilerDecl clang_decl;
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ switch (qual_type->getTypeClass()) {
+ case clang::Type::Record:
+ if (GetCompleteQualType(getASTContext(), qual_type)) {
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+ assert(record_decl);
+ const clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
+ if (cxx_record_decl) {
+ auto method_iter = cxx_record_decl->method_begin();
+ auto method_end = cxx_record_decl->method_end();
+ if (idx <
+ static_cast<size_t>(std::distance(method_iter, method_end))) {
+ std::advance(method_iter, idx);
+ clang::CXXMethodDecl *cxx_method_decl =
+ method_iter->getCanonicalDecl();
+ if (cxx_method_decl) {
+ name = cxx_method_decl->getDeclName().getAsString();
+ if (cxx_method_decl->isStatic())
+ kind = lldb::eMemberFunctionKindStaticMethod;
+ else if (llvm::isa<clang::CXXConstructorDecl>(cxx_method_decl))
+ kind = lldb::eMemberFunctionKindConstructor;
+ else if (llvm::isa<clang::CXXDestructorDecl>(cxx_method_decl))
+ kind = lldb::eMemberFunctionKindDestructor;
+ else
+ kind = lldb::eMemberFunctionKindInstanceMethod;
+ clang_type = CompilerType(
+ this, cxx_method_decl->getType().getAsOpaquePtr());
+ clang_decl = CompilerDecl(this, cxx_method_decl);
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::ObjCObjectPointer: {
+ const clang::ObjCObjectPointerType *objc_class_type =
+ qual_type->getAs<clang::ObjCObjectPointerType>();
+ const clang::ObjCInterfaceType *objc_interface_type =
+ objc_class_type->getInterfaceType();
+ if (objc_interface_type &&
+ GetCompleteType(static_cast<lldb::opaque_compiler_type_t>(
+ const_cast<clang::ObjCInterfaceType *>(objc_interface_type)))) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_interface_type->getDecl();
+ if (class_interface_decl) {
+ auto method_iter = class_interface_decl->meth_begin();
+ auto method_end = class_interface_decl->meth_end();
+ if (idx <
+ static_cast<size_t>(std::distance(method_iter, method_end))) {
+ std::advance(method_iter, idx);
+ clang::ObjCMethodDecl *objc_method_decl =
+ method_iter->getCanonicalDecl();
+ if (objc_method_decl) {
+ clang_decl = CompilerDecl(this, objc_method_decl);
+ name = objc_method_decl->getSelector().getAsString();
+ if (objc_method_decl->isClassMethod())
+ kind = lldb::eMemberFunctionKindStaticMethod;
+ else
+ kind = lldb::eMemberFunctionKindInstanceMethod;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface:
+ if (GetCompleteType(type)) {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr());
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+ if (class_interface_decl) {
+ auto method_iter = class_interface_decl->meth_begin();
+ auto method_end = class_interface_decl->meth_end();
+ if (idx <
+ static_cast<size_t>(std::distance(method_iter, method_end))) {
+ std::advance(method_iter, idx);
+ clang::ObjCMethodDecl *objc_method_decl =
+ method_iter->getCanonicalDecl();
+ if (objc_method_decl) {
+ clang_decl = CompilerDecl(this, objc_method_decl);
+ name = objc_method_decl->getSelector().getAsString();
+ if (objc_method_decl->isClassMethod())
+ kind = lldb::eMemberFunctionKindStaticMethod;
+ else
+ kind = lldb::eMemberFunctionKindInstanceMethod;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::Typedef:
+ return GetMemberFunctionAtIndex(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ idx);
+
+ case clang::Type::Auto:
+ return GetMemberFunctionAtIndex(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ idx);
+
+ case clang::Type::Elaborated:
+ return GetMemberFunctionAtIndex(
+ llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ idx);
+
+ case clang::Type::Paren:
+ return GetMemberFunctionAtIndex(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr(),
+ idx);
+
+ default:
+ break;
+ }
+ }
+
+ if (kind == eMemberFunctionKindUnknown)
+ return TypeMemberFunctionImpl();
+ else
+ return TypeMemberFunctionImpl(clang_type, clang_decl, name, kind);
+}
+
+CompilerType
+ClangASTContext::GetNonReferenceType(lldb::opaque_compiler_type_t type) {
+ if (type)
+ return CompilerType(getASTContext(),
+ GetQualType(type).getNonReferenceType());
+ return CompilerType();
+}
+
+CompilerType ClangASTContext::CreateTypedefType(
+ const CompilerType &type, const char *typedef_name,
+ const CompilerDeclContext &compiler_decl_ctx) {
+ if (type && typedef_name && typedef_name[0]) {
+ ClangASTContext *ast =
+ llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (!ast)
+ return CompilerType();
+ clang::ASTContext *clang_ast = ast->getASTContext();
+ clang::QualType qual_type(ClangUtil::GetQualType(type));
+
+ clang::DeclContext *decl_ctx =
+ ClangASTContext::DeclContextGetAsDeclContext(compiler_decl_ctx);
+ if (decl_ctx == nullptr)
+ decl_ctx = ast->getASTContext()->getTranslationUnitDecl();
+
+ clang::TypedefDecl *decl = clang::TypedefDecl::Create(
+ *clang_ast, decl_ctx, clang::SourceLocation(), clang::SourceLocation(),
+ &clang_ast->Idents.get(typedef_name),
+ clang_ast->getTrivialTypeSourceInfo(qual_type));
+
+ decl->setAccess(clang::AS_public); // TODO respect proper access specifier
+
+ decl_ctx->addDecl(decl);
+
+ // Get a uniqued clang::QualType for the typedef decl type
+ return CompilerType(clang_ast, clang_ast->getTypedefType(decl));
+ }
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::GetPointeeType(lldb::opaque_compiler_type_t type) {
+ if (type) {
+ clang::QualType qual_type(GetQualType(type));
+ return CompilerType(getASTContext(),
+ qual_type.getTypePtr()->getPointeeType());
+ }
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::GetPointerType(lldb::opaque_compiler_type_t type) {
+ if (type) {
+ clang::QualType qual_type(GetQualType(type));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface:
+ return CompilerType(getASTContext(),
+ getASTContext()->getObjCObjectPointerType(qual_type));
+
+ default:
+ return CompilerType(getASTContext(),
+ getASTContext()->getPointerType(qual_type));
+ }
+ }
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::GetLValueReferenceType(lldb::opaque_compiler_type_t type) {
+ if (type)
+ return CompilerType(this, getASTContext()
+ ->getLValueReferenceType(GetQualType(type))
+ .getAsOpaquePtr());
+ else
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::GetRValueReferenceType(lldb::opaque_compiler_type_t type) {
+ if (type)
+ return CompilerType(this, getASTContext()
+ ->getRValueReferenceType(GetQualType(type))
+ .getAsOpaquePtr());
+ else
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::AddConstModifier(lldb::opaque_compiler_type_t type) {
+ if (type) {
+ clang::QualType result(GetQualType(type));
+ result.addConst();
+ return CompilerType(this, result.getAsOpaquePtr());
+ }
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::AddVolatileModifier(lldb::opaque_compiler_type_t type) {
+ if (type) {
+ clang::QualType result(GetQualType(type));
+ result.addVolatile();
+ return CompilerType(this, result.getAsOpaquePtr());
+ }
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::AddRestrictModifier(lldb::opaque_compiler_type_t type) {
+ if (type) {
+ clang::QualType result(GetQualType(type));
+ result.addRestrict();
+ return CompilerType(this, result.getAsOpaquePtr());
+ }
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::CreateTypedef(lldb::opaque_compiler_type_t type,
+ const char *typedef_name,
+ const CompilerDeclContext &compiler_decl_ctx) {
+ if (type) {
+ clang::ASTContext *clang_ast = getASTContext();
+ clang::QualType qual_type(GetQualType(type));
+
+ clang::DeclContext *decl_ctx =
+ ClangASTContext::DeclContextGetAsDeclContext(compiler_decl_ctx);
+ if (decl_ctx == nullptr)
+ decl_ctx = getASTContext()->getTranslationUnitDecl();
+
+ clang::TypedefDecl *decl = clang::TypedefDecl::Create(
+ *clang_ast, decl_ctx, clang::SourceLocation(), clang::SourceLocation(),
+ &clang_ast->Idents.get(typedef_name),
+ clang_ast->getTrivialTypeSourceInfo(qual_type));
+
+ clang::TagDecl *tdecl = nullptr;
+ if (!qual_type.isNull()) {
+ if (const clang::RecordType *rt = qual_type->getAs<clang::RecordType>())
+ tdecl = rt->getDecl();
+ if (const clang::EnumType *et = qual_type->getAs<clang::EnumType>())
+ tdecl = et->getDecl();
+ }
+
+ // Check whether this declaration is an anonymous struct, union, or enum,
+ // hidden behind a typedef. If so, we try to check whether we have a
+ // typedef tag to attach to the original record declaration
+ if (tdecl && !tdecl->getIdentifier() && !tdecl->getTypedefNameForAnonDecl())
+ tdecl->setTypedefNameForAnonDecl(decl);
+
+ decl->setAccess(clang::AS_public); // TODO respect proper access specifier
+
+ // Get a uniqued clang::QualType for the typedef decl type
+ return CompilerType(this, clang_ast->getTypedefType(decl).getAsOpaquePtr());
+ }
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::GetTypedefedType(lldb::opaque_compiler_type_t type) {
+ if (type) {
+ const clang::TypedefType *typedef_type =
+ llvm::dyn_cast<clang::TypedefType>(GetQualType(type));
+ if (typedef_type)
+ return CompilerType(getASTContext(),
+ typedef_type->getDecl()->getUnderlyingType());
+ }
+ return CompilerType();
+}
+
+// Create related types using the current type's AST
+
+CompilerType ClangASTContext::GetBasicTypeFromAST(lldb::BasicType basic_type) {
+ return ClangASTContext::GetBasicType(getASTContext(), basic_type);
+}
+// Exploring the type
+
+Optional<uint64_t>
+ClangASTContext::GetBitSize(lldb::opaque_compiler_type_t type,
+ ExecutionContextScope *exe_scope) {
+ if (GetCompleteType(type)) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type))
+ return getASTContext()->getTypeSize(qual_type);
+ else
+ return None;
+ break;
+
+ case clang::Type::ObjCInterface:
+ case clang::Type::ObjCObject: {
+ ExecutionContext exe_ctx(exe_scope);
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process);
+ if (objc_runtime) {
+ uint64_t bit_size = 0;
+ if (objc_runtime->GetTypeBitSize(
+ CompilerType(getASTContext(), qual_type), bit_size))
+ return bit_size;
+ }
+ } else {
+ static bool g_printed = false;
+ if (!g_printed) {
+ StreamString s;
+ DumpTypeDescription(type, &s);
+
+ llvm::outs() << "warning: trying to determine the size of type ";
+ llvm::outs() << s.GetString() << "\n";
+ llvm::outs() << "without a valid ExecutionContext. this is not "
+ "reliable. please file a bug against LLDB.\n";
+ llvm::outs() << "backtrace:\n";
+ llvm::sys::PrintStackTrace(llvm::outs());
+ llvm::outs() << "\n";
+ g_printed = true;
+ }
+ }
+ }
+ LLVM_FALLTHROUGH;
+ default:
+ const uint32_t bit_size = getASTContext()->getTypeSize(qual_type);
+ if (bit_size == 0) {
+ if (qual_type->isIncompleteArrayType())
+ return getASTContext()->getTypeSize(
+ qual_type->getArrayElementTypeNoTypeQual()
+ ->getCanonicalTypeUnqualified());
+ }
+ if (qual_type->isObjCObjectOrInterfaceType())
+ return bit_size +
+ getASTContext()->getTypeSize(
+ getASTContext()->ObjCBuiltinClassTy);
+ // Function types actually have a size of 0, that's not an error.
+ if (qual_type->isFunctionProtoType())
+ return bit_size;
+ if (bit_size)
+ return bit_size;
+ }
+ }
+ return None;
+}
+
+size_t ClangASTContext::GetTypeBitAlign(lldb::opaque_compiler_type_t type) {
+ if (GetCompleteType(type))
+ return getASTContext()->getTypeAlign(GetQualType(type));
+ return 0;
+}
+
+lldb::Encoding ClangASTContext::GetEncoding(lldb::opaque_compiler_type_t type,
+ uint64_t &count) {
+ if (!type)
+ return lldb::eEncodingInvalid;
+
+ count = 1;
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ switch (qual_type->getTypeClass()) {
+ case clang::Type::UnaryTransform:
+ break;
+
+ case clang::Type::FunctionNoProto:
+ case clang::Type::FunctionProto:
+ break;
+
+ case clang::Type::IncompleteArray:
+ case clang::Type::VariableArray:
+ break;
+
+ case clang::Type::ConstantArray:
+ break;
+
+ case clang::Type::DependentVector:
+ case clang::Type::ExtVector:
+ case clang::Type::Vector:
+ // TODO: Set this to more than one???
+ break;
+
+ case clang::Type::Builtin:
+ switch (llvm::cast<clang::BuiltinType>(qual_type)->getKind()) {
+ case clang::BuiltinType::Void:
+ break;
+
+ case clang::BuiltinType::Bool:
+ case clang::BuiltinType::Char_S:
+ case clang::BuiltinType::SChar:
+ case clang::BuiltinType::WChar_S:
+ case clang::BuiltinType::Short:
+ case clang::BuiltinType::Int:
+ case clang::BuiltinType::Long:
+ case clang::BuiltinType::LongLong:
+ case clang::BuiltinType::Int128:
+ return lldb::eEncodingSint;
+
+ case clang::BuiltinType::Char_U:
+ case clang::BuiltinType::UChar:
+ case clang::BuiltinType::WChar_U:
+ case clang::BuiltinType::Char8:
+ case clang::BuiltinType::Char16:
+ case clang::BuiltinType::Char32:
+ case clang::BuiltinType::UShort:
+ case clang::BuiltinType::UInt:
+ case clang::BuiltinType::ULong:
+ case clang::BuiltinType::ULongLong:
+ case clang::BuiltinType::UInt128:
+ return lldb::eEncodingUint;
+
+ // Fixed point types. Note that they are currently ignored.
+ case clang::BuiltinType::ShortAccum:
+ case clang::BuiltinType::Accum:
+ case clang::BuiltinType::LongAccum:
+ case clang::BuiltinType::UShortAccum:
+ case clang::BuiltinType::UAccum:
+ case clang::BuiltinType::ULongAccum:
+ case clang::BuiltinType::ShortFract:
+ case clang::BuiltinType::Fract:
+ case clang::BuiltinType::LongFract:
+ case clang::BuiltinType::UShortFract:
+ case clang::BuiltinType::UFract:
+ case clang::BuiltinType::ULongFract:
+ case clang::BuiltinType::SatShortAccum:
+ case clang::BuiltinType::SatAccum:
+ case clang::BuiltinType::SatLongAccum:
+ case clang::BuiltinType::SatUShortAccum:
+ case clang::BuiltinType::SatUAccum:
+ case clang::BuiltinType::SatULongAccum:
+ case clang::BuiltinType::SatShortFract:
+ case clang::BuiltinType::SatFract:
+ case clang::BuiltinType::SatLongFract:
+ case clang::BuiltinType::SatUShortFract:
+ case clang::BuiltinType::SatUFract:
+ case clang::BuiltinType::SatULongFract:
+ break;
+
+ case clang::BuiltinType::Half:
+ case clang::BuiltinType::Float:
+ case clang::BuiltinType::Float16:
+ case clang::BuiltinType::Float128:
+ case clang::BuiltinType::Double:
+ case clang::BuiltinType::LongDouble:
+ return lldb::eEncodingIEEE754;
+
+ case clang::BuiltinType::ObjCClass:
+ case clang::BuiltinType::ObjCId:
+ case clang::BuiltinType::ObjCSel:
+ return lldb::eEncodingUint;
+
+ case clang::BuiltinType::NullPtr:
+ return lldb::eEncodingUint;
+
+ case clang::BuiltinType::Kind::ARCUnbridgedCast:
+ case clang::BuiltinType::Kind::BoundMember:
+ case clang::BuiltinType::Kind::BuiltinFn:
+ case clang::BuiltinType::Kind::Dependent:
+ case clang::BuiltinType::Kind::OCLClkEvent:
+ case clang::BuiltinType::Kind::OCLEvent:
+ case clang::BuiltinType::Kind::OCLImage1dRO:
+ case clang::BuiltinType::Kind::OCLImage1dWO:
+ case clang::BuiltinType::Kind::OCLImage1dRW:
+ case clang::BuiltinType::Kind::OCLImage1dArrayRO:
+ case clang::BuiltinType::Kind::OCLImage1dArrayWO:
+ case clang::BuiltinType::Kind::OCLImage1dArrayRW:
+ case clang::BuiltinType::Kind::OCLImage1dBufferRO:
+ case clang::BuiltinType::Kind::OCLImage1dBufferWO:
+ case clang::BuiltinType::Kind::OCLImage1dBufferRW:
+ case clang::BuiltinType::Kind::OCLImage2dRO:
+ case clang::BuiltinType::Kind::OCLImage2dWO:
+ case clang::BuiltinType::Kind::OCLImage2dRW:
+ case clang::BuiltinType::Kind::OCLImage2dArrayRO:
+ case clang::BuiltinType::Kind::OCLImage2dArrayWO:
+ case clang::BuiltinType::Kind::OCLImage2dArrayRW:
+ case clang::BuiltinType::Kind::OCLImage2dArrayDepthRO:
+ case clang::BuiltinType::Kind::OCLImage2dArrayDepthWO:
+ case clang::BuiltinType::Kind::OCLImage2dArrayDepthRW:
+ case clang::BuiltinType::Kind::OCLImage2dArrayMSAARO:
+ case clang::BuiltinType::Kind::OCLImage2dArrayMSAAWO:
+ case clang::BuiltinType::Kind::OCLImage2dArrayMSAARW:
+ case clang::BuiltinType::Kind::OCLImage2dArrayMSAADepthRO:
+ case clang::BuiltinType::Kind::OCLImage2dArrayMSAADepthWO:
+ case clang::BuiltinType::Kind::OCLImage2dArrayMSAADepthRW:
+ case clang::BuiltinType::Kind::OCLImage2dDepthRO:
+ case clang::BuiltinType::Kind::OCLImage2dDepthWO:
+ case clang::BuiltinType::Kind::OCLImage2dDepthRW:
+ case clang::BuiltinType::Kind::OCLImage2dMSAARO:
+ case clang::BuiltinType::Kind::OCLImage2dMSAAWO:
+ case clang::BuiltinType::Kind::OCLImage2dMSAARW:
+ case clang::BuiltinType::Kind::OCLImage2dMSAADepthRO:
+ case clang::BuiltinType::Kind::OCLImage2dMSAADepthWO:
+ case clang::BuiltinType::Kind::OCLImage2dMSAADepthRW:
+ case clang::BuiltinType::Kind::OCLImage3dRO:
+ case clang::BuiltinType::Kind::OCLImage3dWO:
+ case clang::BuiltinType::Kind::OCLImage3dRW:
+ case clang::BuiltinType::Kind::OCLQueue:
+ case clang::BuiltinType::Kind::OCLReserveID:
+ case clang::BuiltinType::Kind::OCLSampler:
+ case clang::BuiltinType::Kind::OMPArraySection:
+ case clang::BuiltinType::Kind::Overload:
+ case clang::BuiltinType::Kind::PseudoObject:
+ case clang::BuiltinType::Kind::UnknownAny:
+ break;
+
+ case clang::BuiltinType::OCLIntelSubgroupAVCMcePayload:
+ case clang::BuiltinType::OCLIntelSubgroupAVCImePayload:
+ case clang::BuiltinType::OCLIntelSubgroupAVCRefPayload:
+ case clang::BuiltinType::OCLIntelSubgroupAVCSicPayload:
+ case clang::BuiltinType::OCLIntelSubgroupAVCMceResult:
+ case clang::BuiltinType::OCLIntelSubgroupAVCImeResult:
+ case clang::BuiltinType::OCLIntelSubgroupAVCRefResult:
+ case clang::BuiltinType::OCLIntelSubgroupAVCSicResult:
+ case clang::BuiltinType::OCLIntelSubgroupAVCImeResultSingleRefStreamout:
+ case clang::BuiltinType::OCLIntelSubgroupAVCImeResultDualRefStreamout:
+ case clang::BuiltinType::OCLIntelSubgroupAVCImeSingleRefStreamin:
+ case clang::BuiltinType::OCLIntelSubgroupAVCImeDualRefStreamin:
+ break;
+ }
+ break;
+ // All pointer types are represented as unsigned integer encodings. We may
+ // nee to add a eEncodingPointer if we ever need to know the difference
+ case clang::Type::ObjCObjectPointer:
+ case clang::Type::BlockPointer:
+ case clang::Type::Pointer:
+ case clang::Type::LValueReference:
+ case clang::Type::RValueReference:
+ case clang::Type::MemberPointer:
+ return lldb::eEncodingUint;
+ case clang::Type::Complex: {
+ lldb::Encoding encoding = lldb::eEncodingIEEE754;
+ if (qual_type->isComplexType())
+ encoding = lldb::eEncodingIEEE754;
+ else {
+ const clang::ComplexType *complex_type =
+ qual_type->getAsComplexIntegerType();
+ if (complex_type)
+ encoding = CompilerType(getASTContext(), complex_type->getElementType())
+ .GetEncoding(count);
+ else
+ encoding = lldb::eEncodingSint;
+ }
+ count = 2;
+ return encoding;
+ }
+
+ case clang::Type::ObjCInterface:
+ break;
+ case clang::Type::Record:
+ break;
+ case clang::Type::Enum:
+ return lldb::eEncodingSint;
+ case clang::Type::Typedef:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType())
+ .GetEncoding(count);
+
+ case clang::Type::Auto:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType())
+ .GetEncoding(count);
+
+ case clang::Type::Elaborated:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType())
+ .GetEncoding(count);
+
+ case clang::Type::Paren:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar())
+ .GetEncoding(count);
+ case clang::Type::TypeOfExpr:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::TypeOfExprType>(qual_type)
+ ->getUnderlyingExpr()
+ ->getType())
+ .GetEncoding(count);
+ case clang::Type::TypeOf:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::TypeOfType>(qual_type)->getUnderlyingType())
+ .GetEncoding(count);
+ case clang::Type::Decltype:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::DecltypeType>(qual_type)->getUnderlyingType())
+ .GetEncoding(count);
+ case clang::Type::DependentSizedArray:
+ case clang::Type::DependentSizedExtVector:
+ case clang::Type::UnresolvedUsing:
+ case clang::Type::Attributed:
+ case clang::Type::TemplateTypeParm:
+ case clang::Type::SubstTemplateTypeParm:
+ case clang::Type::SubstTemplateTypeParmPack:
+ case clang::Type::InjectedClassName:
+ case clang::Type::DependentName:
+ case clang::Type::DependentTemplateSpecialization:
+ case clang::Type::PackExpansion:
+ case clang::Type::ObjCObject:
+
+ case clang::Type::TemplateSpecialization:
+ case clang::Type::DeducedTemplateSpecialization:
+ case clang::Type::Atomic:
+ case clang::Type::Adjusted:
+ case clang::Type::Pipe:
+ break;
+
+ // pointer type decayed from an array or function type.
+ case clang::Type::Decayed:
+ break;
+ case clang::Type::ObjCTypeParam:
+ break;
+
+ case clang::Type::DependentAddressSpace:
+ break;
+ case clang::Type::MacroQualified:
+ break;
+ }
+ count = 0;
+ return lldb::eEncodingInvalid;
+}
+
+lldb::Format ClangASTContext::GetFormat(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return lldb::eFormatDefault;
+
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ switch (qual_type->getTypeClass()) {
+ case clang::Type::UnaryTransform:
+ break;
+
+ case clang::Type::FunctionNoProto:
+ case clang::Type::FunctionProto:
+ break;
+
+ case clang::Type::IncompleteArray:
+ case clang::Type::VariableArray:
+ break;
+
+ case clang::Type::ConstantArray:
+ return lldb::eFormatVoid; // no value
+
+ case clang::Type::DependentVector:
+ case clang::Type::ExtVector:
+ case clang::Type::Vector:
+ break;
+
+ case clang::Type::Builtin:
+ switch (llvm::cast<clang::BuiltinType>(qual_type)->getKind()) {
+ // default: assert(0 && "Unknown builtin type!");
+ case clang::BuiltinType::UnknownAny:
+ case clang::BuiltinType::Void:
+ case clang::BuiltinType::BoundMember:
+ break;
+
+ case clang::BuiltinType::Bool:
+ return lldb::eFormatBoolean;
+ case clang::BuiltinType::Char_S:
+ case clang::BuiltinType::SChar:
+ case clang::BuiltinType::WChar_S:
+ case clang::BuiltinType::Char_U:
+ case clang::BuiltinType::UChar:
+ case clang::BuiltinType::WChar_U:
+ return lldb::eFormatChar;
+ case clang::BuiltinType::Char16:
+ return lldb::eFormatUnicode16;
+ case clang::BuiltinType::Char32:
+ return lldb::eFormatUnicode32;
+ case clang::BuiltinType::UShort:
+ return lldb::eFormatUnsigned;
+ case clang::BuiltinType::Short:
+ return lldb::eFormatDecimal;
+ case clang::BuiltinType::UInt:
+ return lldb::eFormatUnsigned;
+ case clang::BuiltinType::Int:
+ return lldb::eFormatDecimal;
+ case clang::BuiltinType::ULong:
+ return lldb::eFormatUnsigned;
+ case clang::BuiltinType::Long:
+ return lldb::eFormatDecimal;
+ case clang::BuiltinType::ULongLong:
+ return lldb::eFormatUnsigned;
+ case clang::BuiltinType::LongLong:
+ return lldb::eFormatDecimal;
+ case clang::BuiltinType::UInt128:
+ return lldb::eFormatUnsigned;
+ case clang::BuiltinType::Int128:
+ return lldb::eFormatDecimal;
+ case clang::BuiltinType::Half:
+ case clang::BuiltinType::Float:
+ case clang::BuiltinType::Double:
+ case clang::BuiltinType::LongDouble:
+ return lldb::eFormatFloat;
+ default:
+ return lldb::eFormatHex;
+ }
+ break;
+ case clang::Type::ObjCObjectPointer:
+ return lldb::eFormatHex;
+ case clang::Type::BlockPointer:
+ return lldb::eFormatHex;
+ case clang::Type::Pointer:
+ return lldb::eFormatHex;
+ case clang::Type::LValueReference:
+ case clang::Type::RValueReference:
+ return lldb::eFormatHex;
+ case clang::Type::MemberPointer:
+ break;
+ case clang::Type::Complex: {
+ if (qual_type->isComplexType())
+ return lldb::eFormatComplex;
+ else
+ return lldb::eFormatComplexInteger;
+ }
+ case clang::Type::ObjCInterface:
+ break;
+ case clang::Type::Record:
+ break;
+ case clang::Type::Enum:
+ return lldb::eFormatEnum;
+ case clang::Type::Typedef:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType())
+ .GetFormat();
+ case clang::Type::Auto:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->desugar())
+ .GetFormat();
+ case clang::Type::Paren:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar())
+ .GetFormat();
+ case clang::Type::Elaborated:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType())
+ .GetFormat();
+ case clang::Type::TypeOfExpr:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::TypeOfExprType>(qual_type)
+ ->getUnderlyingExpr()
+ ->getType())
+ .GetFormat();
+ case clang::Type::TypeOf:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::TypeOfType>(qual_type)->getUnderlyingType())
+ .GetFormat();
+ case clang::Type::Decltype:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::DecltypeType>(qual_type)->getUnderlyingType())
+ .GetFormat();
+ case clang::Type::DependentSizedArray:
+ case clang::Type::DependentSizedExtVector:
+ case clang::Type::UnresolvedUsing:
+ case clang::Type::Attributed:
+ case clang::Type::TemplateTypeParm:
+ case clang::Type::SubstTemplateTypeParm:
+ case clang::Type::SubstTemplateTypeParmPack:
+ case clang::Type::InjectedClassName:
+ case clang::Type::DependentName:
+ case clang::Type::DependentTemplateSpecialization:
+ case clang::Type::PackExpansion:
+ case clang::Type::ObjCObject:
+
+ case clang::Type::TemplateSpecialization:
+ case clang::Type::DeducedTemplateSpecialization:
+ case clang::Type::Atomic:
+ case clang::Type::Adjusted:
+ case clang::Type::Pipe:
+ break;
+
+ // pointer type decayed from an array or function type.
+ case clang::Type::Decayed:
+ break;
+ case clang::Type::ObjCTypeParam:
+ break;
+
+ case clang::Type::DependentAddressSpace:
+ break;
+ case clang::Type::MacroQualified:
+ break;
+ }
+ // We don't know hot to display this type...
+ return lldb::eFormatBytes;
+}
+
+static bool ObjCDeclHasIVars(clang::ObjCInterfaceDecl *class_interface_decl,
+ bool check_superclass) {
+ while (class_interface_decl) {
+ if (class_interface_decl->ivar_size() > 0)
+ return true;
+
+ if (check_superclass)
+ class_interface_decl = class_interface_decl->getSuperClass();
+ else
+ break;
+ }
+ return false;
+}
+
+static Optional<SymbolFile::ArrayInfo>
+GetDynamicArrayInfo(ClangASTContext &ast, SymbolFile *sym_file,
+ clang::QualType qual_type,
+ const ExecutionContext *exe_ctx) {
+ if (qual_type->isIncompleteArrayType())
+ if (auto *metadata = ast.GetMetadata(qual_type.getAsOpaquePtr()))
+ return sym_file->GetDynamicArrayInfoForUID(metadata->GetUserID(),
+ exe_ctx);
+ return llvm::None;
+}
+
+uint32_t ClangASTContext::GetNumChildren(lldb::opaque_compiler_type_t type,
+ bool omit_empty_base_classes,
+ const ExecutionContext *exe_ctx) {
+ if (!type)
+ return 0;
+
+ uint32_t num_children = 0;
+ clang::QualType qual_type(GetQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Builtin:
+ switch (llvm::cast<clang::BuiltinType>(qual_type)->getKind()) {
+ case clang::BuiltinType::ObjCId: // child is Class
+ case clang::BuiltinType::ObjCClass: // child is Class
+ num_children = 1;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case clang::Type::Complex:
+ return 0;
+ case clang::Type::Record:
+ if (GetCompleteQualType(getASTContext(), qual_type)) {
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+ assert(record_decl);
+ const clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
+ if (cxx_record_decl) {
+ if (omit_empty_base_classes) {
+ // Check each base classes to see if it or any of its base classes
+ // contain any fields. This can help limit the noise in variable
+ // views by not having to show base classes that contain no members.
+ clang::CXXRecordDecl::base_class_const_iterator base_class,
+ base_class_end;
+ for (base_class = cxx_record_decl->bases_begin(),
+ base_class_end = cxx_record_decl->bases_end();
+ base_class != base_class_end; ++base_class) {
+ const clang::CXXRecordDecl *base_class_decl =
+ llvm::cast<clang::CXXRecordDecl>(
+ base_class->getType()
+ ->getAs<clang::RecordType>()
+ ->getDecl());
+
+ // Skip empty base classes
+ if (!ClangASTContext::RecordHasFields(base_class_decl))
+ continue;
+
+ num_children++;
+ }
+ } else {
+ // Include all base classes
+ num_children += cxx_record_decl->getNumBases();
+ }
+ }
+ clang::RecordDecl::field_iterator field, field_end;
+ for (field = record_decl->field_begin(),
+ field_end = record_decl->field_end();
+ field != field_end; ++field)
+ ++num_children;
+ }
+ break;
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface:
+ if (GetCompleteQualType(getASTContext(), qual_type)) {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr());
+ assert(objc_class_type);
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+
+ if (class_interface_decl) {
+
+ clang::ObjCInterfaceDecl *superclass_interface_decl =
+ class_interface_decl->getSuperClass();
+ if (superclass_interface_decl) {
+ if (omit_empty_base_classes) {
+ if (ObjCDeclHasIVars(superclass_interface_decl, true))
+ ++num_children;
+ } else
+ ++num_children;
+ }
+
+ num_children += class_interface_decl->ivar_size();
+ }
+ }
+ }
+ break;
+
+ case clang::Type::ObjCObjectPointer: {
+ const clang::ObjCObjectPointerType *pointer_type =
+ llvm::cast<clang::ObjCObjectPointerType>(qual_type.getTypePtr());
+ clang::QualType pointee_type = pointer_type->getPointeeType();
+ uint32_t num_pointee_children =
+ CompilerType(getASTContext(), pointee_type)
+ .GetNumChildren(omit_empty_base_classes, exe_ctx);
+ // If this type points to a simple type, then it has 1 child
+ if (num_pointee_children == 0)
+ num_children = 1;
+ else
+ num_children = num_pointee_children;
+ } break;
+
+ case clang::Type::Vector:
+ case clang::Type::ExtVector:
+ num_children =
+ llvm::cast<clang::VectorType>(qual_type.getTypePtr())->getNumElements();
+ break;
+
+ case clang::Type::ConstantArray:
+ num_children = llvm::cast<clang::ConstantArrayType>(qual_type.getTypePtr())
+ ->getSize()
+ .getLimitedValue();
+ break;
+ case clang::Type::IncompleteArray:
+ if (auto array_info =
+ GetDynamicArrayInfo(*this, GetSymbolFile(), qual_type, exe_ctx))
+ // Only 1-dimensional arrays are supported.
+ num_children = array_info->element_orders.size()
+ ? array_info->element_orders.back()
+ : 0;
+ break;
+
+ case clang::Type::Pointer: {
+ const clang::PointerType *pointer_type =
+ llvm::cast<clang::PointerType>(qual_type.getTypePtr());
+ clang::QualType pointee_type(pointer_type->getPointeeType());
+ uint32_t num_pointee_children =
+ CompilerType(getASTContext(), pointee_type)
+ .GetNumChildren(omit_empty_base_classes, exe_ctx);
+ if (num_pointee_children == 0) {
+ // We have a pointer to a pointee type that claims it has no children. We
+ // will want to look at
+ num_children = GetNumPointeeChildren(pointee_type);
+ } else
+ num_children = num_pointee_children;
+ } break;
+
+ case clang::Type::LValueReference:
+ case clang::Type::RValueReference: {
+ const clang::ReferenceType *reference_type =
+ llvm::cast<clang::ReferenceType>(qual_type.getTypePtr());
+ clang::QualType pointee_type = reference_type->getPointeeType();
+ uint32_t num_pointee_children =
+ CompilerType(getASTContext(), pointee_type)
+ .GetNumChildren(omit_empty_base_classes, exe_ctx);
+ // If this type points to a simple type, then it has 1 child
+ if (num_pointee_children == 0)
+ num_children = 1;
+ else
+ num_children = num_pointee_children;
+ } break;
+
+ case clang::Type::Typedef:
+ num_children =
+ CompilerType(getASTContext(), llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType())
+ .GetNumChildren(omit_empty_base_classes, exe_ctx);
+ break;
+
+ case clang::Type::Auto:
+ num_children =
+ CompilerType(getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType())
+ .GetNumChildren(omit_empty_base_classes, exe_ctx);
+ break;
+
+ case clang::Type::Elaborated:
+ num_children =
+ CompilerType(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType())
+ .GetNumChildren(omit_empty_base_classes, exe_ctx);
+ break;
+
+ case clang::Type::Paren:
+ num_children =
+ CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar())
+ .GetNumChildren(omit_empty_base_classes, exe_ctx);
+ break;
+ default:
+ break;
+ }
+ return num_children;
+}
+
+CompilerType ClangASTContext::GetBuiltinTypeByName(ConstString name) {
+ return GetBasicType(GetBasicTypeEnumeration(name));
+}
+
+lldb::BasicType
+ClangASTContext::GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) {
+ if (type) {
+ clang::QualType qual_type(GetQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ if (type_class == clang::Type::Builtin) {
+ switch (llvm::cast<clang::BuiltinType>(qual_type)->getKind()) {
+ case clang::BuiltinType::Void:
+ return eBasicTypeVoid;
+ case clang::BuiltinType::Bool:
+ return eBasicTypeBool;
+ case clang::BuiltinType::Char_S:
+ return eBasicTypeSignedChar;
+ case clang::BuiltinType::Char_U:
+ return eBasicTypeUnsignedChar;
+ case clang::BuiltinType::Char16:
+ return eBasicTypeChar16;
+ case clang::BuiltinType::Char32:
+ return eBasicTypeChar32;
+ case clang::BuiltinType::UChar:
+ return eBasicTypeUnsignedChar;
+ case clang::BuiltinType::SChar:
+ return eBasicTypeSignedChar;
+ case clang::BuiltinType::WChar_S:
+ return eBasicTypeSignedWChar;
+ case clang::BuiltinType::WChar_U:
+ return eBasicTypeUnsignedWChar;
+ case clang::BuiltinType::Short:
+ return eBasicTypeShort;
+ case clang::BuiltinType::UShort:
+ return eBasicTypeUnsignedShort;
+ case clang::BuiltinType::Int:
+ return eBasicTypeInt;
+ case clang::BuiltinType::UInt:
+ return eBasicTypeUnsignedInt;
+ case clang::BuiltinType::Long:
+ return eBasicTypeLong;
+ case clang::BuiltinType::ULong:
+ return eBasicTypeUnsignedLong;
+ case clang::BuiltinType::LongLong:
+ return eBasicTypeLongLong;
+ case clang::BuiltinType::ULongLong:
+ return eBasicTypeUnsignedLongLong;
+ case clang::BuiltinType::Int128:
+ return eBasicTypeInt128;
+ case clang::BuiltinType::UInt128:
+ return eBasicTypeUnsignedInt128;
+
+ case clang::BuiltinType::Half:
+ return eBasicTypeHalf;
+ case clang::BuiltinType::Float:
+ return eBasicTypeFloat;
+ case clang::BuiltinType::Double:
+ return eBasicTypeDouble;
+ case clang::BuiltinType::LongDouble:
+ return eBasicTypeLongDouble;
+
+ case clang::BuiltinType::NullPtr:
+ return eBasicTypeNullPtr;
+ case clang::BuiltinType::ObjCId:
+ return eBasicTypeObjCID;
+ case clang::BuiltinType::ObjCClass:
+ return eBasicTypeObjCClass;
+ case clang::BuiltinType::ObjCSel:
+ return eBasicTypeObjCSel;
+ default:
+ return eBasicTypeOther;
+ }
+ }
+ }
+ return eBasicTypeInvalid;
+}
+
+void ClangASTContext::ForEachEnumerator(
+ lldb::opaque_compiler_type_t type,
+ std::function<bool(const CompilerType &integer_type,
+ ConstString name,
+ const llvm::APSInt &value)> const &callback) {
+ const clang::EnumType *enum_type =
+ llvm::dyn_cast<clang::EnumType>(GetCanonicalQualType(type));
+ if (enum_type) {
+ const clang::EnumDecl *enum_decl = enum_type->getDecl();
+ if (enum_decl) {
+ CompilerType integer_type(this,
+ enum_decl->getIntegerType().getAsOpaquePtr());
+
+ clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos;
+ for (enum_pos = enum_decl->enumerator_begin(),
+ enum_end_pos = enum_decl->enumerator_end();
+ enum_pos != enum_end_pos; ++enum_pos) {
+ ConstString name(enum_pos->getNameAsString().c_str());
+ if (!callback(integer_type, name, enum_pos->getInitVal()))
+ break;
+ }
+ }
+ }
+}
+
+#pragma mark Aggregate Types
+
+uint32_t ClangASTContext::GetNumFields(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return 0;
+
+ uint32_t count = 0;
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::RecordType *record_type =
+ llvm::dyn_cast<clang::RecordType>(qual_type.getTypePtr());
+ if (record_type) {
+ clang::RecordDecl *record_decl = record_type->getDecl();
+ if (record_decl) {
+ uint32_t field_idx = 0;
+ clang::RecordDecl::field_iterator field, field_end;
+ for (field = record_decl->field_begin(),
+ field_end = record_decl->field_end();
+ field != field_end; ++field)
+ ++field_idx;
+ count = field_idx;
+ }
+ }
+ }
+ break;
+
+ case clang::Type::Typedef:
+ count =
+ CompilerType(getASTContext(), llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType())
+ .GetNumFields();
+ break;
+
+ case clang::Type::Auto:
+ count =
+ CompilerType(getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType())
+ .GetNumFields();
+ break;
+
+ case clang::Type::Elaborated:
+ count = CompilerType(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType())
+ .GetNumFields();
+ break;
+
+ case clang::Type::Paren:
+ count = CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar())
+ .GetNumFields();
+ break;
+
+ case clang::Type::ObjCObjectPointer: {
+ const clang::ObjCObjectPointerType *objc_class_type =
+ qual_type->getAs<clang::ObjCObjectPointerType>();
+ const clang::ObjCInterfaceType *objc_interface_type =
+ objc_class_type->getInterfaceType();
+ if (objc_interface_type &&
+ GetCompleteType(static_cast<lldb::opaque_compiler_type_t>(
+ const_cast<clang::ObjCInterfaceType *>(objc_interface_type)))) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_interface_type->getDecl();
+ if (class_interface_decl) {
+ count = class_interface_decl->ivar_size();
+ }
+ }
+ break;
+ }
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface:
+ if (GetCompleteType(type)) {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr());
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+
+ if (class_interface_decl)
+ count = class_interface_decl->ivar_size();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return count;
+}
+
+static lldb::opaque_compiler_type_t
+GetObjCFieldAtIndex(clang::ASTContext *ast,
+ clang::ObjCInterfaceDecl *class_interface_decl, size_t idx,
+ std::string &name, uint64_t *bit_offset_ptr,
+ uint32_t *bitfield_bit_size_ptr, bool *is_bitfield_ptr) {
+ if (class_interface_decl) {
+ if (idx < (class_interface_decl->ivar_size())) {
+ clang::ObjCInterfaceDecl::ivar_iterator ivar_pos,
+ ivar_end = class_interface_decl->ivar_end();
+ uint32_t ivar_idx = 0;
+
+ for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end;
+ ++ivar_pos, ++ivar_idx) {
+ if (ivar_idx == idx) {
+ const clang::ObjCIvarDecl *ivar_decl = *ivar_pos;
+
+ clang::QualType ivar_qual_type(ivar_decl->getType());
+
+ name.assign(ivar_decl->getNameAsString());
+
+ if (bit_offset_ptr) {
+ const clang::ASTRecordLayout &interface_layout =
+ ast->getASTObjCInterfaceLayout(class_interface_decl);
+ *bit_offset_ptr = interface_layout.getFieldOffset(ivar_idx);
+ }
+
+ const bool is_bitfield = ivar_pos->isBitField();
+
+ if (bitfield_bit_size_ptr) {
+ *bitfield_bit_size_ptr = 0;
+
+ if (is_bitfield && ast) {
+ clang::Expr *bitfield_bit_size_expr = ivar_pos->getBitWidth();
+ clang::Expr::EvalResult result;
+ if (bitfield_bit_size_expr &&
+ bitfield_bit_size_expr->EvaluateAsInt(result, *ast)) {
+ llvm::APSInt bitfield_apsint = result.Val.getInt();
+ *bitfield_bit_size_ptr = bitfield_apsint.getLimitedValue();
+ }
+ }
+ }
+ if (is_bitfield_ptr)
+ *is_bitfield_ptr = is_bitfield;
+
+ return ivar_qual_type.getAsOpaquePtr();
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+CompilerType ClangASTContext::GetFieldAtIndex(lldb::opaque_compiler_type_t type,
+ size_t idx, std::string &name,
+ uint64_t *bit_offset_ptr,
+ uint32_t *bitfield_bit_size_ptr,
+ bool *is_bitfield_ptr) {
+ if (!type)
+ return CompilerType();
+
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+ uint32_t field_idx = 0;
+ clang::RecordDecl::field_iterator field, field_end;
+ for (field = record_decl->field_begin(),
+ field_end = record_decl->field_end();
+ field != field_end; ++field, ++field_idx) {
+ if (idx == field_idx) {
+ // Print the member type if requested
+ // Print the member name and equal sign
+ name.assign(field->getNameAsString());
+
+ // Figure out the type byte size (field_type_info.first) and
+ // alignment (field_type_info.second) from the AST context.
+ if (bit_offset_ptr) {
+ const clang::ASTRecordLayout &record_layout =
+ getASTContext()->getASTRecordLayout(record_decl);
+ *bit_offset_ptr = record_layout.getFieldOffset(field_idx);
+ }
+
+ const bool is_bitfield = field->isBitField();
+
+ if (bitfield_bit_size_ptr) {
+ *bitfield_bit_size_ptr = 0;
+
+ if (is_bitfield) {
+ clang::Expr *bitfield_bit_size_expr = field->getBitWidth();
+ clang::Expr::EvalResult result;
+ if (bitfield_bit_size_expr &&
+ bitfield_bit_size_expr->EvaluateAsInt(result,
+ *getASTContext())) {
+ llvm::APSInt bitfield_apsint = result.Val.getInt();
+ *bitfield_bit_size_ptr = bitfield_apsint.getLimitedValue();
+ }
+ }
+ }
+ if (is_bitfield_ptr)
+ *is_bitfield_ptr = is_bitfield;
+
+ return CompilerType(getASTContext(), field->getType());
+ }
+ }
+ }
+ break;
+
+ case clang::Type::ObjCObjectPointer: {
+ const clang::ObjCObjectPointerType *objc_class_type =
+ qual_type->getAs<clang::ObjCObjectPointerType>();
+ const clang::ObjCInterfaceType *objc_interface_type =
+ objc_class_type->getInterfaceType();
+ if (objc_interface_type &&
+ GetCompleteType(static_cast<lldb::opaque_compiler_type_t>(
+ const_cast<clang::ObjCInterfaceType *>(objc_interface_type)))) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_interface_type->getDecl();
+ if (class_interface_decl) {
+ return CompilerType(
+ this, GetObjCFieldAtIndex(getASTContext(), class_interface_decl,
+ idx, name, bit_offset_ptr,
+ bitfield_bit_size_ptr, is_bitfield_ptr));
+ }
+ }
+ break;
+ }
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface:
+ if (GetCompleteType(type)) {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr());
+ assert(objc_class_type);
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+ return CompilerType(
+ this, GetObjCFieldAtIndex(getASTContext(), class_interface_decl,
+ idx, name, bit_offset_ptr,
+ bitfield_bit_size_ptr, is_bitfield_ptr));
+ }
+ }
+ break;
+
+ case clang::Type::Typedef:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType())
+ .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr,
+ is_bitfield_ptr);
+
+ case clang::Type::Auto:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType())
+ .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr,
+ is_bitfield_ptr);
+
+ case clang::Type::Elaborated:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType())
+ .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr,
+ is_bitfield_ptr);
+
+ case clang::Type::Paren:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar())
+ .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr,
+ is_bitfield_ptr);
+
+ default:
+ break;
+ }
+ return CompilerType();
+}
+
+uint32_t
+ClangASTContext::GetNumDirectBaseClasses(lldb::opaque_compiler_type_t type) {
+ uint32_t count = 0;
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::CXXRecordDecl *cxx_record_decl =
+ qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl)
+ count = cxx_record_decl->getNumBases();
+ }
+ break;
+
+ case clang::Type::ObjCObjectPointer:
+ count = GetPointeeType(type).GetNumDirectBaseClasses();
+ break;
+
+ case clang::Type::ObjCObject:
+ if (GetCompleteType(type)) {
+ const clang::ObjCObjectType *objc_class_type =
+ qual_type->getAsObjCQualifiedInterfaceType();
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+
+ if (class_interface_decl && class_interface_decl->getSuperClass())
+ count = 1;
+ }
+ }
+ break;
+ case clang::Type::ObjCInterface:
+ if (GetCompleteType(type)) {
+ const clang::ObjCInterfaceType *objc_interface_type =
+ qual_type->getAs<clang::ObjCInterfaceType>();
+ if (objc_interface_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_interface_type->getInterface();
+
+ if (class_interface_decl && class_interface_decl->getSuperClass())
+ count = 1;
+ }
+ }
+ break;
+
+ case clang::Type::Typedef:
+ count = GetNumDirectBaseClasses(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr());
+ break;
+
+ case clang::Type::Auto:
+ count = GetNumDirectBaseClasses(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr());
+ break;
+
+ case clang::Type::Elaborated:
+ count = GetNumDirectBaseClasses(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr());
+ break;
+
+ case clang::Type::Paren:
+ return GetNumDirectBaseClasses(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr());
+
+ default:
+ break;
+ }
+ return count;
+}
+
+uint32_t
+ClangASTContext::GetNumVirtualBaseClasses(lldb::opaque_compiler_type_t type) {
+ uint32_t count = 0;
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::CXXRecordDecl *cxx_record_decl =
+ qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl)
+ count = cxx_record_decl->getNumVBases();
+ }
+ break;
+
+ case clang::Type::Typedef:
+ count = GetNumVirtualBaseClasses(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr());
+ break;
+
+ case clang::Type::Auto:
+ count = GetNumVirtualBaseClasses(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr());
+ break;
+
+ case clang::Type::Elaborated:
+ count =
+ GetNumVirtualBaseClasses(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr());
+ break;
+
+ case clang::Type::Paren:
+ count = GetNumVirtualBaseClasses(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr());
+ break;
+
+ default:
+ break;
+ }
+ return count;
+}
+
+CompilerType ClangASTContext::GetDirectBaseClassAtIndex(
+ lldb::opaque_compiler_type_t type, size_t idx, uint32_t *bit_offset_ptr) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::CXXRecordDecl *cxx_record_decl =
+ qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl) {
+ uint32_t curr_idx = 0;
+ clang::CXXRecordDecl::base_class_const_iterator base_class,
+ base_class_end;
+ for (base_class = cxx_record_decl->bases_begin(),
+ base_class_end = cxx_record_decl->bases_end();
+ base_class != base_class_end; ++base_class, ++curr_idx) {
+ if (curr_idx == idx) {
+ if (bit_offset_ptr) {
+ const clang::ASTRecordLayout &record_layout =
+ getASTContext()->getASTRecordLayout(cxx_record_decl);
+ const clang::CXXRecordDecl *base_class_decl =
+ llvm::cast<clang::CXXRecordDecl>(
+ base_class->getType()
+ ->getAs<clang::RecordType>()
+ ->getDecl());
+ if (base_class->isVirtual())
+ *bit_offset_ptr =
+ record_layout.getVBaseClassOffset(base_class_decl)
+ .getQuantity() *
+ 8;
+ else
+ *bit_offset_ptr =
+ record_layout.getBaseClassOffset(base_class_decl)
+ .getQuantity() *
+ 8;
+ }
+ return CompilerType(this, base_class->getType().getAsOpaquePtr());
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::ObjCObjectPointer:
+ return GetPointeeType(type).GetDirectBaseClassAtIndex(idx, bit_offset_ptr);
+
+ case clang::Type::ObjCObject:
+ if (idx == 0 && GetCompleteType(type)) {
+ const clang::ObjCObjectType *objc_class_type =
+ qual_type->getAsObjCQualifiedInterfaceType();
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+
+ if (class_interface_decl) {
+ clang::ObjCInterfaceDecl *superclass_interface_decl =
+ class_interface_decl->getSuperClass();
+ if (superclass_interface_decl) {
+ if (bit_offset_ptr)
+ *bit_offset_ptr = 0;
+ return CompilerType(getASTContext(),
+ getASTContext()->getObjCInterfaceType(
+ superclass_interface_decl));
+ }
+ }
+ }
+ }
+ break;
+ case clang::Type::ObjCInterface:
+ if (idx == 0 && GetCompleteType(type)) {
+ const clang::ObjCObjectType *objc_interface_type =
+ qual_type->getAs<clang::ObjCInterfaceType>();
+ if (objc_interface_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_interface_type->getInterface();
+
+ if (class_interface_decl) {
+ clang::ObjCInterfaceDecl *superclass_interface_decl =
+ class_interface_decl->getSuperClass();
+ if (superclass_interface_decl) {
+ if (bit_offset_ptr)
+ *bit_offset_ptr = 0;
+ return CompilerType(getASTContext(),
+ getASTContext()->getObjCInterfaceType(
+ superclass_interface_decl));
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::Typedef:
+ return GetDirectBaseClassAtIndex(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ idx, bit_offset_ptr);
+
+ case clang::Type::Auto:
+ return GetDirectBaseClassAtIndex(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ idx, bit_offset_ptr);
+
+ case clang::Type::Elaborated:
+ return GetDirectBaseClassAtIndex(
+ llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ idx, bit_offset_ptr);
+
+ case clang::Type::Paren:
+ return GetDirectBaseClassAtIndex(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr(),
+ idx, bit_offset_ptr);
+
+ default:
+ break;
+ }
+ return CompilerType();
+}
+
+CompilerType ClangASTContext::GetVirtualBaseClassAtIndex(
+ lldb::opaque_compiler_type_t type, size_t idx, uint32_t *bit_offset_ptr) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::CXXRecordDecl *cxx_record_decl =
+ qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl) {
+ uint32_t curr_idx = 0;
+ clang::CXXRecordDecl::base_class_const_iterator base_class,
+ base_class_end;
+ for (base_class = cxx_record_decl->vbases_begin(),
+ base_class_end = cxx_record_decl->vbases_end();
+ base_class != base_class_end; ++base_class, ++curr_idx) {
+ if (curr_idx == idx) {
+ if (bit_offset_ptr) {
+ const clang::ASTRecordLayout &record_layout =
+ getASTContext()->getASTRecordLayout(cxx_record_decl);
+ const clang::CXXRecordDecl *base_class_decl =
+ llvm::cast<clang::CXXRecordDecl>(
+ base_class->getType()
+ ->getAs<clang::RecordType>()
+ ->getDecl());
+ *bit_offset_ptr =
+ record_layout.getVBaseClassOffset(base_class_decl)
+ .getQuantity() *
+ 8;
+ }
+ return CompilerType(this, base_class->getType().getAsOpaquePtr());
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::Typedef:
+ return GetVirtualBaseClassAtIndex(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ idx, bit_offset_ptr);
+
+ case clang::Type::Auto:
+ return GetVirtualBaseClassAtIndex(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ idx, bit_offset_ptr);
+
+ case clang::Type::Elaborated:
+ return GetVirtualBaseClassAtIndex(
+ llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ idx, bit_offset_ptr);
+
+ case clang::Type::Paren:
+ return GetVirtualBaseClassAtIndex(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr(),
+ idx, bit_offset_ptr);
+
+ default:
+ break;
+ }
+ return CompilerType();
+}
+
+// If a pointer to a pointee type (the clang_type arg) says that it has no
+// children, then we either need to trust it, or override it and return a
+// different result. For example, an "int *" has one child that is an integer,
+// but a function pointer doesn't have any children. Likewise if a Record type
+// claims it has no children, then there really is nothing to show.
+uint32_t ClangASTContext::GetNumPointeeChildren(clang::QualType type) {
+ if (type.isNull())
+ return 0;
+
+ clang::QualType qual_type(type.getCanonicalType());
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Builtin:
+ switch (llvm::cast<clang::BuiltinType>(qual_type)->getKind()) {
+ case clang::BuiltinType::UnknownAny:
+ case clang::BuiltinType::Void:
+ case clang::BuiltinType::NullPtr:
+ case clang::BuiltinType::OCLEvent:
+ case clang::BuiltinType::OCLImage1dRO:
+ case clang::BuiltinType::OCLImage1dWO:
+ case clang::BuiltinType::OCLImage1dRW:
+ case clang::BuiltinType::OCLImage1dArrayRO:
+ case clang::BuiltinType::OCLImage1dArrayWO:
+ case clang::BuiltinType::OCLImage1dArrayRW:
+ case clang::BuiltinType::OCLImage1dBufferRO:
+ case clang::BuiltinType::OCLImage1dBufferWO:
+ case clang::BuiltinType::OCLImage1dBufferRW:
+ case clang::BuiltinType::OCLImage2dRO:
+ case clang::BuiltinType::OCLImage2dWO:
+ case clang::BuiltinType::OCLImage2dRW:
+ case clang::BuiltinType::OCLImage2dArrayRO:
+ case clang::BuiltinType::OCLImage2dArrayWO:
+ case clang::BuiltinType::OCLImage2dArrayRW:
+ case clang::BuiltinType::OCLImage3dRO:
+ case clang::BuiltinType::OCLImage3dWO:
+ case clang::BuiltinType::OCLImage3dRW:
+ case clang::BuiltinType::OCLSampler:
+ return 0;
+ case clang::BuiltinType::Bool:
+ case clang::BuiltinType::Char_U:
+ case clang::BuiltinType::UChar:
+ case clang::BuiltinType::WChar_U:
+ case clang::BuiltinType::Char16:
+ case clang::BuiltinType::Char32:
+ case clang::BuiltinType::UShort:
+ case clang::BuiltinType::UInt:
+ case clang::BuiltinType::ULong:
+ case clang::BuiltinType::ULongLong:
+ case clang::BuiltinType::UInt128:
+ case clang::BuiltinType::Char_S:
+ case clang::BuiltinType::SChar:
+ case clang::BuiltinType::WChar_S:
+ case clang::BuiltinType::Short:
+ case clang::BuiltinType::Int:
+ case clang::BuiltinType::Long:
+ case clang::BuiltinType::LongLong:
+ case clang::BuiltinType::Int128:
+ case clang::BuiltinType::Float:
+ case clang::BuiltinType::Double:
+ case clang::BuiltinType::LongDouble:
+ case clang::BuiltinType::Dependent:
+ case clang::BuiltinType::Overload:
+ case clang::BuiltinType::ObjCId:
+ case clang::BuiltinType::ObjCClass:
+ case clang::BuiltinType::ObjCSel:
+ case clang::BuiltinType::BoundMember:
+ case clang::BuiltinType::Half:
+ case clang::BuiltinType::ARCUnbridgedCast:
+ case clang::BuiltinType::PseudoObject:
+ case clang::BuiltinType::BuiltinFn:
+ case clang::BuiltinType::OMPArraySection:
+ return 1;
+ default:
+ return 0;
+ }
+ break;
+
+ case clang::Type::Complex:
+ return 1;
+ case clang::Type::Pointer:
+ return 1;
+ case clang::Type::BlockPointer:
+ return 0; // If block pointers don't have debug info, then no children for
+ // them
+ case clang::Type::LValueReference:
+ return 1;
+ case clang::Type::RValueReference:
+ return 1;
+ case clang::Type::MemberPointer:
+ return 0;
+ case clang::Type::ConstantArray:
+ return 0;
+ case clang::Type::IncompleteArray:
+ return 0;
+ case clang::Type::VariableArray:
+ return 0;
+ case clang::Type::DependentSizedArray:
+ return 0;
+ case clang::Type::DependentSizedExtVector:
+ return 0;
+ case clang::Type::Vector:
+ return 0;
+ case clang::Type::ExtVector:
+ return 0;
+ case clang::Type::FunctionProto:
+ return 0; // When we function pointers, they have no children...
+ case clang::Type::FunctionNoProto:
+ return 0; // When we function pointers, they have no children...
+ case clang::Type::UnresolvedUsing:
+ return 0;
+ case clang::Type::Paren:
+ return GetNumPointeeChildren(
+ llvm::cast<clang::ParenType>(qual_type)->desugar());
+ case clang::Type::Typedef:
+ return GetNumPointeeChildren(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType());
+ case clang::Type::Auto:
+ return GetNumPointeeChildren(
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType());
+ case clang::Type::Elaborated:
+ return GetNumPointeeChildren(
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType());
+ case clang::Type::TypeOfExpr:
+ return GetNumPointeeChildren(llvm::cast<clang::TypeOfExprType>(qual_type)
+ ->getUnderlyingExpr()
+ ->getType());
+ case clang::Type::TypeOf:
+ return GetNumPointeeChildren(
+ llvm::cast<clang::TypeOfType>(qual_type)->getUnderlyingType());
+ case clang::Type::Decltype:
+ return GetNumPointeeChildren(
+ llvm::cast<clang::DecltypeType>(qual_type)->getUnderlyingType());
+ case clang::Type::Record:
+ return 0;
+ case clang::Type::Enum:
+ return 1;
+ case clang::Type::TemplateTypeParm:
+ return 1;
+ case clang::Type::SubstTemplateTypeParm:
+ return 1;
+ case clang::Type::TemplateSpecialization:
+ return 1;
+ case clang::Type::InjectedClassName:
+ return 0;
+ case clang::Type::DependentName:
+ return 1;
+ case clang::Type::DependentTemplateSpecialization:
+ return 1;
+ case clang::Type::ObjCObject:
+ return 0;
+ case clang::Type::ObjCInterface:
+ return 0;
+ case clang::Type::ObjCObjectPointer:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+CompilerType ClangASTContext::GetChildCompilerTypeAtIndex(
+ lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx,
+ bool transparent_pointers, bool omit_empty_base_classes,
+ bool ignore_array_bounds, std::string &child_name,
+ uint32_t &child_byte_size, int32_t &child_byte_offset,
+ uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset,
+ bool &child_is_base_class, bool &child_is_deref_of_parent,
+ ValueObject *valobj, uint64_t &language_flags) {
+ if (!type)
+ return CompilerType();
+
+ auto get_exe_scope = [&exe_ctx]() {
+ return exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
+ };
+
+ clang::QualType parent_qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass parent_type_class =
+ parent_qual_type->getTypeClass();
+ child_bitfield_bit_size = 0;
+ child_bitfield_bit_offset = 0;
+ child_is_base_class = false;
+ language_flags = 0;
+
+ const bool idx_is_valid =
+ idx < GetNumChildren(type, omit_empty_base_classes, exe_ctx);
+ int32_t bit_offset;
+ switch (parent_type_class) {
+ case clang::Type::Builtin:
+ if (idx_is_valid) {
+ switch (llvm::cast<clang::BuiltinType>(parent_qual_type)->getKind()) {
+ case clang::BuiltinType::ObjCId:
+ case clang::BuiltinType::ObjCClass:
+ child_name = "isa";
+ child_byte_size =
+ getASTContext()->getTypeSize(getASTContext()->ObjCBuiltinClassTy) /
+ CHAR_BIT;
+ return CompilerType(getASTContext(),
+ getASTContext()->ObjCBuiltinClassTy);
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ case clang::Type::Record:
+ if (idx_is_valid && GetCompleteType(type)) {
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(parent_qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+ assert(record_decl);
+ const clang::ASTRecordLayout &record_layout =
+ getASTContext()->getASTRecordLayout(record_decl);
+ uint32_t child_idx = 0;
+
+ const clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
+ if (cxx_record_decl) {
+ // We might have base classes to print out first
+ clang::CXXRecordDecl::base_class_const_iterator base_class,
+ base_class_end;
+ for (base_class = cxx_record_decl->bases_begin(),
+ base_class_end = cxx_record_decl->bases_end();
+ base_class != base_class_end; ++base_class) {
+ const clang::CXXRecordDecl *base_class_decl = nullptr;
+
+ // Skip empty base classes
+ if (omit_empty_base_classes) {
+ base_class_decl = llvm::cast<clang::CXXRecordDecl>(
+ base_class->getType()->getAs<clang::RecordType>()->getDecl());
+ if (!ClangASTContext::RecordHasFields(base_class_decl))
+ continue;
+ }
+
+ if (idx == child_idx) {
+ if (base_class_decl == nullptr)
+ base_class_decl = llvm::cast<clang::CXXRecordDecl>(
+ base_class->getType()->getAs<clang::RecordType>()->getDecl());
+
+ if (base_class->isVirtual()) {
+ bool handled = false;
+ if (valobj) {
+ clang::VTableContextBase *vtable_ctx =
+ getASTContext()->getVTableContext();
+ if (vtable_ctx)
+ handled = GetVBaseBitOffset(*vtable_ctx, *valobj,
+ record_layout, cxx_record_decl,
+ base_class_decl, bit_offset);
+ }
+ if (!handled)
+ bit_offset = record_layout.getVBaseClassOffset(base_class_decl)
+ .getQuantity() *
+ 8;
+ } else
+ bit_offset = record_layout.getBaseClassOffset(base_class_decl)
+ .getQuantity() *
+ 8;
+
+ // Base classes should be a multiple of 8 bits in size
+ child_byte_offset = bit_offset / 8;
+ CompilerType base_class_clang_type(getASTContext(),
+ base_class->getType());
+ child_name = base_class_clang_type.GetTypeName().AsCString("");
+ Optional<uint64_t> size =
+ base_class_clang_type.GetBitSize(get_exe_scope());
+ if (!size)
+ return {};
+ uint64_t base_class_clang_type_bit_size = *size;
+
+ // Base classes bit sizes should be a multiple of 8 bits in size
+ assert(base_class_clang_type_bit_size % 8 == 0);
+ child_byte_size = base_class_clang_type_bit_size / 8;
+ child_is_base_class = true;
+ return base_class_clang_type;
+ }
+ // We don't increment the child index in the for loop since we might
+ // be skipping empty base classes
+ ++child_idx;
+ }
+ }
+ // Make sure index is in range...
+ uint32_t field_idx = 0;
+ clang::RecordDecl::field_iterator field, field_end;
+ for (field = record_decl->field_begin(),
+ field_end = record_decl->field_end();
+ field != field_end; ++field, ++field_idx, ++child_idx) {
+ if (idx == child_idx) {
+ // Print the member type if requested
+ // Print the member name and equal sign
+ child_name.assign(field->getNameAsString());
+
+ // Figure out the type byte size (field_type_info.first) and
+ // alignment (field_type_info.second) from the AST context.
+ CompilerType field_clang_type(getASTContext(), field->getType());
+ assert(field_idx < record_layout.getFieldCount());
+ Optional<uint64_t> size =
+ field_clang_type.GetByteSize(get_exe_scope());
+ if (!size)
+ return {};
+ child_byte_size = *size;
+ const uint32_t child_bit_size = child_byte_size * 8;
+
+ // Figure out the field offset within the current struct/union/class
+ // type
+ bit_offset = record_layout.getFieldOffset(field_idx);
+ if (ClangASTContext::FieldIsBitfield(getASTContext(), *field,
+ child_bitfield_bit_size)) {
+ child_bitfield_bit_offset = bit_offset % child_bit_size;
+ const uint32_t child_bit_offset =
+ bit_offset - child_bitfield_bit_offset;
+ child_byte_offset = child_bit_offset / 8;
+ } else {
+ child_byte_offset = bit_offset / 8;
+ }
+
+ return field_clang_type;
+ }
+ }
+ }
+ break;
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface:
+ if (idx_is_valid && GetCompleteType(type)) {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(parent_qual_type.getTypePtr());
+ assert(objc_class_type);
+ if (objc_class_type) {
+ uint32_t child_idx = 0;
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+
+ if (class_interface_decl) {
+
+ const clang::ASTRecordLayout &interface_layout =
+ getASTContext()->getASTObjCInterfaceLayout(class_interface_decl);
+ clang::ObjCInterfaceDecl *superclass_interface_decl =
+ class_interface_decl->getSuperClass();
+ if (superclass_interface_decl) {
+ if (omit_empty_base_classes) {
+ CompilerType base_class_clang_type(
+ getASTContext(), getASTContext()->getObjCInterfaceType(
+ superclass_interface_decl));
+ if (base_class_clang_type.GetNumChildren(omit_empty_base_classes,
+ exe_ctx) > 0) {
+ if (idx == 0) {
+ clang::QualType ivar_qual_type(
+ getASTContext()->getObjCInterfaceType(
+ superclass_interface_decl));
+
+ child_name.assign(
+ superclass_interface_decl->getNameAsString());
+
+ clang::TypeInfo ivar_type_info =
+ getASTContext()->getTypeInfo(ivar_qual_type.getTypePtr());
+
+ child_byte_size = ivar_type_info.Width / 8;
+ child_byte_offset = 0;
+ child_is_base_class = true;
+
+ return CompilerType(getASTContext(), ivar_qual_type);
+ }
+
+ ++child_idx;
+ }
+ } else
+ ++child_idx;
+ }
+
+ const uint32_t superclass_idx = child_idx;
+
+ if (idx < (child_idx + class_interface_decl->ivar_size())) {
+ clang::ObjCInterfaceDecl::ivar_iterator ivar_pos,
+ ivar_end = class_interface_decl->ivar_end();
+
+ for (ivar_pos = class_interface_decl->ivar_begin();
+ ivar_pos != ivar_end; ++ivar_pos) {
+ if (child_idx == idx) {
+ clang::ObjCIvarDecl *ivar_decl = *ivar_pos;
+
+ clang::QualType ivar_qual_type(ivar_decl->getType());
+
+ child_name.assign(ivar_decl->getNameAsString());
+
+ clang::TypeInfo ivar_type_info =
+ getASTContext()->getTypeInfo(ivar_qual_type.getTypePtr());
+
+ child_byte_size = ivar_type_info.Width / 8;
+
+ // Figure out the field offset within the current
+ // struct/union/class type For ObjC objects, we can't trust the
+ // bit offset we get from the Clang AST, since that doesn't
+ // account for the space taken up by unbacked properties, or
+ // from the changing size of base classes that are newer than
+ // this class. So if we have a process around that we can ask
+ // about this object, do so.
+ child_byte_offset = LLDB_INVALID_IVAR_OFFSET;
+ Process *process = nullptr;
+ if (exe_ctx)
+ process = exe_ctx->GetProcessPtr();
+ if (process) {
+ ObjCLanguageRuntime *objc_runtime =
+ ObjCLanguageRuntime::Get(*process);
+ if (objc_runtime != nullptr) {
+ CompilerType parent_ast_type(getASTContext(),
+ parent_qual_type);
+ child_byte_offset = objc_runtime->GetByteOffsetForIvar(
+ parent_ast_type, ivar_decl->getNameAsString().c_str());
+ }
+ }
+
+ // Setting this to INT32_MAX to make sure we don't compute it
+ // twice...
+ bit_offset = INT32_MAX;
+
+ if (child_byte_offset ==
+ static_cast<int32_t>(LLDB_INVALID_IVAR_OFFSET)) {
+ bit_offset = interface_layout.getFieldOffset(child_idx -
+ superclass_idx);
+ child_byte_offset = bit_offset / 8;
+ }
+
+ // Note, the ObjC Ivar Byte offset is just that, it doesn't
+ // account for the bit offset of a bitfield within its
+ // containing object. So regardless of where we get the byte
+ // offset from, we still need to get the bit offset for
+ // bitfields from the layout.
+
+ if (ClangASTContext::FieldIsBitfield(getASTContext(), ivar_decl,
+ child_bitfield_bit_size)) {
+ if (bit_offset == INT32_MAX)
+ bit_offset = interface_layout.getFieldOffset(
+ child_idx - superclass_idx);
+
+ child_bitfield_bit_offset = bit_offset % 8;
+ }
+ return CompilerType(getASTContext(), ivar_qual_type);
+ }
+ ++child_idx;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::ObjCObjectPointer:
+ if (idx_is_valid) {
+ CompilerType pointee_clang_type(GetPointeeType(type));
+
+ if (transparent_pointers && pointee_clang_type.IsAggregateType()) {
+ child_is_deref_of_parent = false;
+ bool tmp_child_is_deref_of_parent = false;
+ return pointee_clang_type.GetChildCompilerTypeAtIndex(
+ exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, tmp_child_is_deref_of_parent, valobj,
+ language_flags);
+ } else {
+ child_is_deref_of_parent = true;
+ const char *parent_name =
+ valobj ? valobj->GetName().GetCString() : nullptr;
+ if (parent_name) {
+ child_name.assign(1, '*');
+ child_name += parent_name;
+ }
+
+ // We have a pointer to an simple type
+ if (idx == 0 && pointee_clang_type.GetCompleteType()) {
+ if (Optional<uint64_t> size =
+ pointee_clang_type.GetByteSize(get_exe_scope())) {
+ child_byte_size = *size;
+ child_byte_offset = 0;
+ return pointee_clang_type;
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::Vector:
+ case clang::Type::ExtVector:
+ if (idx_is_valid) {
+ const clang::VectorType *array =
+ llvm::cast<clang::VectorType>(parent_qual_type.getTypePtr());
+ if (array) {
+ CompilerType element_type(getASTContext(), array->getElementType());
+ if (element_type.GetCompleteType()) {
+ char element_name[64];
+ ::snprintf(element_name, sizeof(element_name), "[%" PRIu64 "]",
+ static_cast<uint64_t>(idx));
+ child_name.assign(element_name);
+ if (Optional<uint64_t> size =
+ element_type.GetByteSize(get_exe_scope())) {
+ child_byte_size = *size;
+ child_byte_offset = (int32_t)idx * (int32_t)child_byte_size;
+ return element_type;
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::ConstantArray:
+ case clang::Type::IncompleteArray:
+ if (ignore_array_bounds || idx_is_valid) {
+ const clang::ArrayType *array = GetQualType(type)->getAsArrayTypeUnsafe();
+ if (array) {
+ CompilerType element_type(getASTContext(), array->getElementType());
+ if (element_type.GetCompleteType()) {
+ child_name = llvm::formatv("[{0}]", idx);
+ if (Optional<uint64_t> size =
+ element_type.GetByteSize(get_exe_scope())) {
+ child_byte_size = *size;
+ child_byte_offset = (int32_t)idx * (int32_t)child_byte_size;
+ return element_type;
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::Pointer: {
+ CompilerType pointee_clang_type(GetPointeeType(type));
+
+ // Don't dereference "void *" pointers
+ if (pointee_clang_type.IsVoidType())
+ return CompilerType();
+
+ if (transparent_pointers && pointee_clang_type.IsAggregateType()) {
+ child_is_deref_of_parent = false;
+ bool tmp_child_is_deref_of_parent = false;
+ return pointee_clang_type.GetChildCompilerTypeAtIndex(
+ exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, tmp_child_is_deref_of_parent, valobj,
+ language_flags);
+ } else {
+ child_is_deref_of_parent = true;
+
+ const char *parent_name =
+ valobj ? valobj->GetName().GetCString() : nullptr;
+ if (parent_name) {
+ child_name.assign(1, '*');
+ child_name += parent_name;
+ }
+
+ // We have a pointer to an simple type
+ if (idx == 0) {
+ if (Optional<uint64_t> size =
+ pointee_clang_type.GetByteSize(get_exe_scope())) {
+ child_byte_size = *size;
+ child_byte_offset = 0;
+ return pointee_clang_type;
+ }
+ }
+ }
+ break;
+ }
+
+ case clang::Type::LValueReference:
+ case clang::Type::RValueReference:
+ if (idx_is_valid) {
+ const clang::ReferenceType *reference_type =
+ llvm::cast<clang::ReferenceType>(parent_qual_type.getTypePtr());
+ CompilerType pointee_clang_type(getASTContext(),
+ reference_type->getPointeeType());
+ if (transparent_pointers && pointee_clang_type.IsAggregateType()) {
+ child_is_deref_of_parent = false;
+ bool tmp_child_is_deref_of_parent = false;
+ return pointee_clang_type.GetChildCompilerTypeAtIndex(
+ exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, tmp_child_is_deref_of_parent, valobj,
+ language_flags);
+ } else {
+ const char *parent_name =
+ valobj ? valobj->GetName().GetCString() : nullptr;
+ if (parent_name) {
+ child_name.assign(1, '&');
+ child_name += parent_name;
+ }
+
+ // We have a pointer to an simple type
+ if (idx == 0) {
+ if (Optional<uint64_t> size =
+ pointee_clang_type.GetByteSize(get_exe_scope())) {
+ child_byte_size = *size;
+ child_byte_offset = 0;
+ return pointee_clang_type;
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::Typedef: {
+ CompilerType typedefed_clang_type(
+ getASTContext(), llvm::cast<clang::TypedefType>(parent_qual_type)
+ ->getDecl()
+ ->getUnderlyingType());
+ return typedefed_clang_type.GetChildCompilerTypeAtIndex(
+ exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, valobj, language_flags);
+ } break;
+
+ case clang::Type::Auto: {
+ CompilerType elaborated_clang_type(
+ getASTContext(),
+ llvm::cast<clang::AutoType>(parent_qual_type)->getDeducedType());
+ return elaborated_clang_type.GetChildCompilerTypeAtIndex(
+ exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, valobj, language_flags);
+ }
+
+ case clang::Type::Elaborated: {
+ CompilerType elaborated_clang_type(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(parent_qual_type)->getNamedType());
+ return elaborated_clang_type.GetChildCompilerTypeAtIndex(
+ exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, valobj, language_flags);
+ }
+
+ case clang::Type::Paren: {
+ CompilerType paren_clang_type(
+ getASTContext(),
+ llvm::cast<clang::ParenType>(parent_qual_type)->desugar());
+ return paren_clang_type.GetChildCompilerTypeAtIndex(
+ exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, valobj, language_flags);
+ }
+
+ default:
+ break;
+ }
+ return CompilerType();
+}
+
+static uint32_t GetIndexForRecordBase(const clang::RecordDecl *record_decl,
+ const clang::CXXBaseSpecifier *base_spec,
+ bool omit_empty_base_classes) {
+ uint32_t child_idx = 0;
+
+ const clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
+
+ // const char *super_name = record_decl->getNameAsCString();
+ // const char *base_name =
+ // base_spec->getType()->getAs<clang::RecordType>()->getDecl()->getNameAsCString();
+ // printf ("GetIndexForRecordChild (%s, %s)\n", super_name, base_name);
+ //
+ if (cxx_record_decl) {
+ clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end;
+ for (base_class = cxx_record_decl->bases_begin(),
+ base_class_end = cxx_record_decl->bases_end();
+ base_class != base_class_end; ++base_class) {
+ if (omit_empty_base_classes) {
+ if (BaseSpecifierIsEmpty(base_class))
+ continue;
+ }
+
+ // printf ("GetIndexForRecordChild (%s, %s) base[%u] = %s\n",
+ // super_name, base_name,
+ // child_idx,
+ // base_class->getType()->getAs<clang::RecordType>()->getDecl()->getNameAsCString());
+ //
+ //
+ if (base_class == base_spec)
+ return child_idx;
+ ++child_idx;
+ }
+ }
+
+ return UINT32_MAX;
+}
+
+static uint32_t GetIndexForRecordChild(const clang::RecordDecl *record_decl,
+ clang::NamedDecl *canonical_decl,
+ bool omit_empty_base_classes) {
+ uint32_t child_idx = ClangASTContext::GetNumBaseClasses(
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl),
+ omit_empty_base_classes);
+
+ clang::RecordDecl::field_iterator field, field_end;
+ for (field = record_decl->field_begin(), field_end = record_decl->field_end();
+ field != field_end; ++field, ++child_idx) {
+ if (field->getCanonicalDecl() == canonical_decl)
+ return child_idx;
+ }
+
+ return UINT32_MAX;
+}
+
+// Look for a child member (doesn't include base classes, but it does include
+// their members) in the type hierarchy. Returns an index path into
+// "clang_type" on how to reach the appropriate member.
+//
+// class A
+// {
+// public:
+// int m_a;
+// int m_b;
+// };
+//
+// class B
+// {
+// };
+//
+// class C :
+// public B,
+// public A
+// {
+// };
+//
+// If we have a clang type that describes "class C", and we wanted to looked
+// "m_b" in it:
+//
+// With omit_empty_base_classes == false we would get an integer array back
+// with: { 1, 1 } The first index 1 is the child index for "class A" within
+// class C The second index 1 is the child index for "m_b" within class A
+//
+// With omit_empty_base_classes == true we would get an integer array back
+// with: { 0, 1 } The first index 0 is the child index for "class A" within
+// class C (since class B doesn't have any members it doesn't count) The second
+// index 1 is the child index for "m_b" within class A
+
+size_t ClangASTContext::GetIndexOfChildMemberWithName(
+ lldb::opaque_compiler_type_t type, const char *name,
+ bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes) {
+ if (type && name && name[0]) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+
+ assert(record_decl);
+ uint32_t child_idx = 0;
+
+ const clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
+
+ // Try and find a field that matches NAME
+ clang::RecordDecl::field_iterator field, field_end;
+ llvm::StringRef name_sref(name);
+ for (field = record_decl->field_begin(),
+ field_end = record_decl->field_end();
+ field != field_end; ++field, ++child_idx) {
+ llvm::StringRef field_name = field->getName();
+ if (field_name.empty()) {
+ CompilerType field_type(getASTContext(), field->getType());
+ child_indexes.push_back(child_idx);
+ if (field_type.GetIndexOfChildMemberWithName(
+ name, omit_empty_base_classes, child_indexes))
+ return child_indexes.size();
+ child_indexes.pop_back();
+
+ } else if (field_name.equals(name_sref)) {
+ // We have to add on the number of base classes to this index!
+ child_indexes.push_back(
+ child_idx + ClangASTContext::GetNumBaseClasses(
+ cxx_record_decl, omit_empty_base_classes));
+ return child_indexes.size();
+ }
+ }
+
+ if (cxx_record_decl) {
+ const clang::RecordDecl *parent_record_decl = cxx_record_decl;
+
+ // printf ("parent = %s\n", parent_record_decl->getNameAsCString());
+
+ // const Decl *root_cdecl = cxx_record_decl->getCanonicalDecl();
+ // Didn't find things easily, lets let clang do its thang...
+ clang::IdentifierInfo &ident_ref =
+ getASTContext()->Idents.get(name_sref);
+ clang::DeclarationName decl_name(&ident_ref);
+
+ clang::CXXBasePaths paths;
+ if (cxx_record_decl->lookupInBases(
+ [decl_name](const clang::CXXBaseSpecifier *specifier,
+ clang::CXXBasePath &path) {
+ return clang::CXXRecordDecl::FindOrdinaryMember(
+ specifier, path, decl_name);
+ },
+ paths)) {
+ clang::CXXBasePaths::const_paths_iterator path,
+ path_end = paths.end();
+ for (path = paths.begin(); path != path_end; ++path) {
+ const size_t num_path_elements = path->size();
+ for (size_t e = 0; e < num_path_elements; ++e) {
+ clang::CXXBasePathElement elem = (*path)[e];
+
+ child_idx = GetIndexForRecordBase(parent_record_decl, elem.Base,
+ omit_empty_base_classes);
+ if (child_idx == UINT32_MAX) {
+ child_indexes.clear();
+ return 0;
+ } else {
+ child_indexes.push_back(child_idx);
+ parent_record_decl = llvm::cast<clang::RecordDecl>(
+ elem.Base->getType()
+ ->getAs<clang::RecordType>()
+ ->getDecl());
+ }
+ }
+ for (clang::NamedDecl *path_decl : path->Decls) {
+ child_idx = GetIndexForRecordChild(
+ parent_record_decl, path_decl, omit_empty_base_classes);
+ if (child_idx == UINT32_MAX) {
+ child_indexes.clear();
+ return 0;
+ } else {
+ child_indexes.push_back(child_idx);
+ }
+ }
+ }
+ return child_indexes.size();
+ }
+ }
+ }
+ break;
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface:
+ if (GetCompleteType(type)) {
+ llvm::StringRef name_sref(name);
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr());
+ assert(objc_class_type);
+ if (objc_class_type) {
+ uint32_t child_idx = 0;
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+
+ if (class_interface_decl) {
+ clang::ObjCInterfaceDecl::ivar_iterator ivar_pos,
+ ivar_end = class_interface_decl->ivar_end();
+ clang::ObjCInterfaceDecl *superclass_interface_decl =
+ class_interface_decl->getSuperClass();
+
+ for (ivar_pos = class_interface_decl->ivar_begin();
+ ivar_pos != ivar_end; ++ivar_pos, ++child_idx) {
+ const clang::ObjCIvarDecl *ivar_decl = *ivar_pos;
+
+ if (ivar_decl->getName().equals(name_sref)) {
+ if ((!omit_empty_base_classes && superclass_interface_decl) ||
+ (omit_empty_base_classes &&
+ ObjCDeclHasIVars(superclass_interface_decl, true)))
+ ++child_idx;
+
+ child_indexes.push_back(child_idx);
+ return child_indexes.size();
+ }
+ }
+
+ if (superclass_interface_decl) {
+ // The super class index is always zero for ObjC classes, so we
+ // push it onto the child indexes in case we find an ivar in our
+ // superclass...
+ child_indexes.push_back(0);
+
+ CompilerType superclass_clang_type(
+ getASTContext(), getASTContext()->getObjCInterfaceType(
+ superclass_interface_decl));
+ if (superclass_clang_type.GetIndexOfChildMemberWithName(
+ name, omit_empty_base_classes, child_indexes)) {
+ // We did find an ivar in a superclass so just return the
+ // results!
+ return child_indexes.size();
+ }
+
+ // We didn't find an ivar matching "name" in our superclass, pop
+ // the superclass zero index that we pushed on above.
+ child_indexes.pop_back();
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::ObjCObjectPointer: {
+ CompilerType objc_object_clang_type(
+ getASTContext(),
+ llvm::cast<clang::ObjCObjectPointerType>(qual_type.getTypePtr())
+ ->getPointeeType());
+ return objc_object_clang_type.GetIndexOfChildMemberWithName(
+ name, omit_empty_base_classes, child_indexes);
+ } break;
+
+ case clang::Type::ConstantArray: {
+ // const clang::ConstantArrayType *array =
+ // llvm::cast<clang::ConstantArrayType>(parent_qual_type.getTypePtr());
+ // const uint64_t element_count =
+ // array->getSize().getLimitedValue();
+ //
+ // if (idx < element_count)
+ // {
+ // std::pair<uint64_t, unsigned> field_type_info =
+ // ast->getTypeInfo(array->getElementType());
+ //
+ // char element_name[32];
+ // ::snprintf (element_name, sizeof (element_name),
+ // "%s[%u]", parent_name ? parent_name : "", idx);
+ //
+ // child_name.assign(element_name);
+ // assert(field_type_info.first % 8 == 0);
+ // child_byte_size = field_type_info.first / 8;
+ // child_byte_offset = idx * child_byte_size;
+ // return array->getElementType().getAsOpaquePtr();
+ // }
+ } break;
+
+ // case clang::Type::MemberPointerType:
+ // {
+ // MemberPointerType *mem_ptr_type =
+ // llvm::cast<MemberPointerType>(qual_type.getTypePtr());
+ // clang::QualType pointee_type =
+ // mem_ptr_type->getPointeeType();
+ //
+ // if (ClangASTContext::IsAggregateType
+ // (pointee_type.getAsOpaquePtr()))
+ // {
+ // return GetIndexOfChildWithName (ast,
+ // mem_ptr_type->getPointeeType().getAsOpaquePtr(),
+ // name);
+ // }
+ // }
+ // break;
+ //
+ case clang::Type::LValueReference:
+ case clang::Type::RValueReference: {
+ const clang::ReferenceType *reference_type =
+ llvm::cast<clang::ReferenceType>(qual_type.getTypePtr());
+ clang::QualType pointee_type(reference_type->getPointeeType());
+ CompilerType pointee_clang_type(getASTContext(), pointee_type);
+
+ if (pointee_clang_type.IsAggregateType()) {
+ return pointee_clang_type.GetIndexOfChildMemberWithName(
+ name, omit_empty_base_classes, child_indexes);
+ }
+ } break;
+
+ case clang::Type::Pointer: {
+ CompilerType pointee_clang_type(GetPointeeType(type));
+
+ if (pointee_clang_type.IsAggregateType()) {
+ return pointee_clang_type.GetIndexOfChildMemberWithName(
+ name, omit_empty_base_classes, child_indexes);
+ }
+ } break;
+
+ case clang::Type::Typedef:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType())
+ .GetIndexOfChildMemberWithName(name, omit_empty_base_classes,
+ child_indexes);
+
+ case clang::Type::Auto:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType())
+ .GetIndexOfChildMemberWithName(name, omit_empty_base_classes,
+ child_indexes);
+
+ case clang::Type::Elaborated:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType())
+ .GetIndexOfChildMemberWithName(name, omit_empty_base_classes,
+ child_indexes);
+
+ case clang::Type::Paren:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar())
+ .GetIndexOfChildMemberWithName(name, omit_empty_base_classes,
+ child_indexes);
+
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+// Get the index of the child of "clang_type" whose name matches. This function
+// doesn't descend into the children, but only looks one level deep and name
+// matches can include base class names.
+
+uint32_t
+ClangASTContext::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type,
+ const char *name,
+ bool omit_empty_base_classes) {
+ if (type && name && name[0]) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+
+ assert(record_decl);
+ uint32_t child_idx = 0;
+
+ const clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
+
+ if (cxx_record_decl) {
+ clang::CXXRecordDecl::base_class_const_iterator base_class,
+ base_class_end;
+ for (base_class = cxx_record_decl->bases_begin(),
+ base_class_end = cxx_record_decl->bases_end();
+ base_class != base_class_end; ++base_class) {
+ // Skip empty base classes
+ clang::CXXRecordDecl *base_class_decl =
+ llvm::cast<clang::CXXRecordDecl>(
+ base_class->getType()
+ ->getAs<clang::RecordType>()
+ ->getDecl());
+ if (omit_empty_base_classes &&
+ !ClangASTContext::RecordHasFields(base_class_decl))
+ continue;
+
+ CompilerType base_class_clang_type(getASTContext(),
+ base_class->getType());
+ std::string base_class_type_name(
+ base_class_clang_type.GetTypeName().AsCString(""));
+ if (base_class_type_name == name)
+ return child_idx;
+ ++child_idx;
+ }
+ }
+
+ // Try and find a field that matches NAME
+ clang::RecordDecl::field_iterator field, field_end;
+ llvm::StringRef name_sref(name);
+ for (field = record_decl->field_begin(),
+ field_end = record_decl->field_end();
+ field != field_end; ++field, ++child_idx) {
+ if (field->getName().equals(name_sref))
+ return child_idx;
+ }
+ }
+ break;
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface:
+ if (GetCompleteType(type)) {
+ llvm::StringRef name_sref(name);
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr());
+ assert(objc_class_type);
+ if (objc_class_type) {
+ uint32_t child_idx = 0;
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+
+ if (class_interface_decl) {
+ clang::ObjCInterfaceDecl::ivar_iterator ivar_pos,
+ ivar_end = class_interface_decl->ivar_end();
+ clang::ObjCInterfaceDecl *superclass_interface_decl =
+ class_interface_decl->getSuperClass();
+
+ for (ivar_pos = class_interface_decl->ivar_begin();
+ ivar_pos != ivar_end; ++ivar_pos, ++child_idx) {
+ const clang::ObjCIvarDecl *ivar_decl = *ivar_pos;
+
+ if (ivar_decl->getName().equals(name_sref)) {
+ if ((!omit_empty_base_classes && superclass_interface_decl) ||
+ (omit_empty_base_classes &&
+ ObjCDeclHasIVars(superclass_interface_decl, true)))
+ ++child_idx;
+
+ return child_idx;
+ }
+ }
+
+ if (superclass_interface_decl) {
+ if (superclass_interface_decl->getName().equals(name_sref))
+ return 0;
+ }
+ }
+ }
+ }
+ break;
+
+ case clang::Type::ObjCObjectPointer: {
+ CompilerType pointee_clang_type(
+ getASTContext(),
+ llvm::cast<clang::ObjCObjectPointerType>(qual_type.getTypePtr())
+ ->getPointeeType());
+ return pointee_clang_type.GetIndexOfChildWithName(
+ name, omit_empty_base_classes);
+ } break;
+
+ case clang::Type::ConstantArray: {
+ // const clang::ConstantArrayType *array =
+ // llvm::cast<clang::ConstantArrayType>(parent_qual_type.getTypePtr());
+ // const uint64_t element_count =
+ // array->getSize().getLimitedValue();
+ //
+ // if (idx < element_count)
+ // {
+ // std::pair<uint64_t, unsigned> field_type_info =
+ // ast->getTypeInfo(array->getElementType());
+ //
+ // char element_name[32];
+ // ::snprintf (element_name, sizeof (element_name),
+ // "%s[%u]", parent_name ? parent_name : "", idx);
+ //
+ // child_name.assign(element_name);
+ // assert(field_type_info.first % 8 == 0);
+ // child_byte_size = field_type_info.first / 8;
+ // child_byte_offset = idx * child_byte_size;
+ // return array->getElementType().getAsOpaquePtr();
+ // }
+ } break;
+
+ // case clang::Type::MemberPointerType:
+ // {
+ // MemberPointerType *mem_ptr_type =
+ // llvm::cast<MemberPointerType>(qual_type.getTypePtr());
+ // clang::QualType pointee_type =
+ // mem_ptr_type->getPointeeType();
+ //
+ // if (ClangASTContext::IsAggregateType
+ // (pointee_type.getAsOpaquePtr()))
+ // {
+ // return GetIndexOfChildWithName (ast,
+ // mem_ptr_type->getPointeeType().getAsOpaquePtr(),
+ // name);
+ // }
+ // }
+ // break;
+ //
+ case clang::Type::LValueReference:
+ case clang::Type::RValueReference: {
+ const clang::ReferenceType *reference_type =
+ llvm::cast<clang::ReferenceType>(qual_type.getTypePtr());
+ CompilerType pointee_type(getASTContext(),
+ reference_type->getPointeeType());
+
+ if (pointee_type.IsAggregateType()) {
+ return pointee_type.GetIndexOfChildWithName(name,
+ omit_empty_base_classes);
+ }
+ } break;
+
+ case clang::Type::Pointer: {
+ const clang::PointerType *pointer_type =
+ llvm::cast<clang::PointerType>(qual_type.getTypePtr());
+ CompilerType pointee_type(getASTContext(),
+ pointer_type->getPointeeType());
+
+ if (pointee_type.IsAggregateType()) {
+ return pointee_type.GetIndexOfChildWithName(name,
+ omit_empty_base_classes);
+ } else {
+ // if (parent_name)
+ // {
+ // child_name.assign(1, '*');
+ // child_name += parent_name;
+ // }
+ //
+ // // We have a pointer to an simple type
+ // if (idx == 0)
+ // {
+ // std::pair<uint64_t, unsigned> clang_type_info
+ // = ast->getTypeInfo(pointee_type);
+ // assert(clang_type_info.first % 8 == 0);
+ // child_byte_size = clang_type_info.first / 8;
+ // child_byte_offset = 0;
+ // return pointee_type.getAsOpaquePtr();
+ // }
+ }
+ } break;
+
+ case clang::Type::Auto:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType())
+ .GetIndexOfChildWithName(name, omit_empty_base_classes);
+
+ case clang::Type::Elaborated:
+ return CompilerType(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType())
+ .GetIndexOfChildWithName(name, omit_empty_base_classes);
+
+ case clang::Type::Paren:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar())
+ .GetIndexOfChildWithName(name, omit_empty_base_classes);
+
+ case clang::Type::Typedef:
+ return CompilerType(getASTContext(),
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType())
+ .GetIndexOfChildWithName(name, omit_empty_base_classes);
+
+ default:
+ break;
+ }
+ }
+ return UINT32_MAX;
+}
+
+size_t
+ClangASTContext::GetNumTemplateArguments(lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return 0;
+
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::CXXRecordDecl *cxx_record_decl =
+ qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl) {
+ const clang::ClassTemplateSpecializationDecl *template_decl =
+ llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(
+ cxx_record_decl);
+ if (template_decl)
+ return template_decl->getTemplateArgs().size();
+ }
+ }
+ break;
+
+ case clang::Type::Typedef:
+ return (CompilerType(getASTContext(),
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()))
+ .GetNumTemplateArguments();
+
+ case clang::Type::Auto:
+ return (CompilerType(
+ getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType()))
+ .GetNumTemplateArguments();
+
+ case clang::Type::Elaborated:
+ return (CompilerType(
+ getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType()))
+ .GetNumTemplateArguments();
+
+ case clang::Type::Paren:
+ return (CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar()))
+ .GetNumTemplateArguments();
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+const clang::ClassTemplateSpecializationDecl *
+ClangASTContext::GetAsTemplateSpecialization(
+ lldb::opaque_compiler_type_t type) {
+ if (!type)
+ return nullptr;
+
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record: {
+ if (! GetCompleteType(type))
+ return nullptr;
+ const clang::CXXRecordDecl *cxx_record_decl =
+ qual_type->getAsCXXRecordDecl();
+ if (!cxx_record_decl)
+ return nullptr;
+ return llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(
+ cxx_record_decl);
+ }
+
+ case clang::Type::Typedef:
+ return GetAsTemplateSpecialization(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr());
+
+ case clang::Type::Auto:
+ return GetAsTemplateSpecialization(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr());
+
+ case clang::Type::Elaborated:
+ return GetAsTemplateSpecialization(
+ llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr());
+
+ case clang::Type::Paren:
+ return GetAsTemplateSpecialization(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr());
+
+ default:
+ return nullptr;
+ }
+}
+
+lldb::TemplateArgumentKind
+ClangASTContext::GetTemplateArgumentKind(lldb::opaque_compiler_type_t type,
+ size_t arg_idx) {
+ const clang::ClassTemplateSpecializationDecl *template_decl =
+ GetAsTemplateSpecialization(type);
+ if (! template_decl || arg_idx >= template_decl->getTemplateArgs().size())
+ return eTemplateArgumentKindNull;
+
+ switch (template_decl->getTemplateArgs()[arg_idx].getKind()) {
+ case clang::TemplateArgument::Null:
+ return eTemplateArgumentKindNull;
+
+ case clang::TemplateArgument::NullPtr:
+ return eTemplateArgumentKindNullPtr;
+
+ case clang::TemplateArgument::Type:
+ return eTemplateArgumentKindType;
+
+ case clang::TemplateArgument::Declaration:
+ return eTemplateArgumentKindDeclaration;
+
+ case clang::TemplateArgument::Integral:
+ return eTemplateArgumentKindIntegral;
+
+ case clang::TemplateArgument::Template:
+ return eTemplateArgumentKindTemplate;
+
+ case clang::TemplateArgument::TemplateExpansion:
+ return eTemplateArgumentKindTemplateExpansion;
+
+ case clang::TemplateArgument::Expression:
+ return eTemplateArgumentKindExpression;
+
+ case clang::TemplateArgument::Pack:
+ return eTemplateArgumentKindPack;
+ }
+ llvm_unreachable("Unhandled clang::TemplateArgument::ArgKind");
+}
+
+CompilerType
+ClangASTContext::GetTypeTemplateArgument(lldb::opaque_compiler_type_t type,
+ size_t idx) {
+ const clang::ClassTemplateSpecializationDecl *template_decl =
+ GetAsTemplateSpecialization(type);
+ if (!template_decl || idx >= template_decl->getTemplateArgs().size())
+ return CompilerType();
+
+ const clang::TemplateArgument &template_arg =
+ template_decl->getTemplateArgs()[idx];
+ if (template_arg.getKind() != clang::TemplateArgument::Type)
+ return CompilerType();
+
+ return CompilerType(getASTContext(), template_arg.getAsType());
+}
+
+Optional<CompilerType::IntegralTemplateArgument>
+ClangASTContext::GetIntegralTemplateArgument(lldb::opaque_compiler_type_t type,
+ size_t idx) {
+ const clang::ClassTemplateSpecializationDecl *template_decl =
+ GetAsTemplateSpecialization(type);
+ if (! template_decl || idx >= template_decl->getTemplateArgs().size())
+ return llvm::None;
+
+ const clang::TemplateArgument &template_arg =
+ template_decl->getTemplateArgs()[idx];
+ if (template_arg.getKind() != clang::TemplateArgument::Integral)
+ return llvm::None;
+
+ return {{template_arg.getAsIntegral(),
+ CompilerType(getASTContext(), template_arg.getIntegralType())}};
+}
+
+CompilerType ClangASTContext::GetTypeForFormatters(void *type) {
+ if (type)
+ return ClangUtil::RemoveFastQualifiers(CompilerType(this, type));
+ return CompilerType();
+}
+
+clang::EnumDecl *ClangASTContext::GetAsEnumDecl(const CompilerType &type) {
+ const clang::EnumType *enutype =
+ llvm::dyn_cast<clang::EnumType>(ClangUtil::GetCanonicalQualType(type));
+ if (enutype)
+ return enutype->getDecl();
+ return nullptr;
+}
+
+clang::RecordDecl *ClangASTContext::GetAsRecordDecl(const CompilerType &type) {
+ const clang::RecordType *record_type =
+ llvm::dyn_cast<clang::RecordType>(ClangUtil::GetCanonicalQualType(type));
+ if (record_type)
+ return record_type->getDecl();
+ return nullptr;
+}
+
+clang::TagDecl *ClangASTContext::GetAsTagDecl(const CompilerType &type) {
+ return ClangUtil::GetAsTagDecl(type);
+}
+
+clang::TypedefNameDecl *
+ClangASTContext::GetAsTypedefDecl(const CompilerType &type) {
+ const clang::TypedefType *typedef_type =
+ llvm::dyn_cast<clang::TypedefType>(ClangUtil::GetQualType(type));
+ if (typedef_type)
+ return typedef_type->getDecl();
+ return nullptr;
+}
+
+clang::CXXRecordDecl *
+ClangASTContext::GetAsCXXRecordDecl(lldb::opaque_compiler_type_t type) {
+ return GetCanonicalQualType(type)->getAsCXXRecordDecl();
+}
+
+clang::ObjCInterfaceDecl *
+ClangASTContext::GetAsObjCInterfaceDecl(const CompilerType &type) {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(
+ ClangUtil::GetCanonicalQualType(type));
+ if (objc_class_type)
+ return objc_class_type->getInterface();
+ return nullptr;
+}
+
+clang::FieldDecl *ClangASTContext::AddFieldToRecordType(
+ const CompilerType &type, llvm::StringRef name,
+ const CompilerType &field_clang_type, AccessType access,
+ uint32_t bitfield_bit_size) {
+ if (!type.IsValid() || !field_clang_type.IsValid())
+ return nullptr;
+ ClangASTContext *ast =
+ llvm::dyn_cast_or_null<ClangASTContext>(type.GetTypeSystem());
+ if (!ast)
+ return nullptr;
+ clang::ASTContext *clang_ast = ast->getASTContext();
+ clang::IdentifierInfo *ident = nullptr;
+ if (!name.empty())
+ ident = &clang_ast->Idents.get(name);
+
+ clang::FieldDecl *field = nullptr;
+
+ clang::Expr *bit_width = nullptr;
+ if (bitfield_bit_size != 0) {
+ llvm::APInt bitfield_bit_size_apint(
+ clang_ast->getTypeSize(clang_ast->IntTy), bitfield_bit_size);
+ bit_width = new (*clang_ast)
+ clang::IntegerLiteral(*clang_ast, bitfield_bit_size_apint,
+ clang_ast->IntTy, clang::SourceLocation());
+ }
+
+ clang::RecordDecl *record_decl = ast->GetAsRecordDecl(type);
+ if (record_decl) {
+ field = clang::FieldDecl::Create(
+ *clang_ast, record_decl, clang::SourceLocation(),
+ clang::SourceLocation(),
+ ident, // Identifier
+ ClangUtil::GetQualType(field_clang_type), // Field type
+ nullptr, // TInfo *
+ bit_width, // BitWidth
+ false, // Mutable
+ clang::ICIS_NoInit); // HasInit
+
+ if (name.empty()) {
+ // Determine whether this field corresponds to an anonymous struct or
+ // union.
+ if (const clang::TagType *TagT =
+ field->getType()->getAs<clang::TagType>()) {
+ if (clang::RecordDecl *Rec =
+ llvm::dyn_cast<clang::RecordDecl>(TagT->getDecl()))
+ if (!Rec->getDeclName()) {
+ Rec->setAnonymousStructOrUnion(true);
+ field->setImplicit();
+ }
+ }
+ }
+
+ if (field) {
+ field->setAccess(
+ ClangASTContext::ConvertAccessTypeToAccessSpecifier(access));
+
+ record_decl->addDecl(field);
+
+#ifdef LLDB_CONFIGURATION_DEBUG
+ VerifyDecl(field);
+#endif
+ }
+ } else {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ ast->GetAsObjCInterfaceDecl(type);
+
+ if (class_interface_decl) {
+ const bool is_synthesized = false;
+
+ field_clang_type.GetCompleteType();
+
+ field = clang::ObjCIvarDecl::Create(
+ *clang_ast, class_interface_decl, clang::SourceLocation(),
+ clang::SourceLocation(),
+ ident, // Identifier
+ ClangUtil::GetQualType(field_clang_type), // Field type
+ nullptr, // TypeSourceInfo *
+ ConvertAccessTypeToObjCIvarAccessControl(access), bit_width,
+ is_synthesized);
+
+ if (field) {
+ class_interface_decl->addDecl(field);
+
+#ifdef LLDB_CONFIGURATION_DEBUG
+ VerifyDecl(field);
+#endif
+ }
+ }
+ }
+ return field;
+}
+
+void ClangASTContext::BuildIndirectFields(const CompilerType &type) {
+ if (!type)
+ return;
+
+ ClangASTContext *ast = llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (!ast)
+ return;
+
+ clang::RecordDecl *record_decl = ast->GetAsRecordDecl(type);
+
+ if (!record_decl)
+ return;
+
+ typedef llvm::SmallVector<clang::IndirectFieldDecl *, 1> IndirectFieldVector;
+
+ IndirectFieldVector indirect_fields;
+ clang::RecordDecl::field_iterator field_pos;
+ clang::RecordDecl::field_iterator field_end_pos = record_decl->field_end();
+ clang::RecordDecl::field_iterator last_field_pos = field_end_pos;
+ for (field_pos = record_decl->field_begin(); field_pos != field_end_pos;
+ last_field_pos = field_pos++) {
+ if (field_pos->isAnonymousStructOrUnion()) {
+ clang::QualType field_qual_type = field_pos->getType();
+
+ const clang::RecordType *field_record_type =
+ field_qual_type->getAs<clang::RecordType>();
+
+ if (!field_record_type)
+ continue;
+
+ clang::RecordDecl *field_record_decl = field_record_type->getDecl();
+
+ if (!field_record_decl)
+ continue;
+
+ for (clang::RecordDecl::decl_iterator
+ di = field_record_decl->decls_begin(),
+ de = field_record_decl->decls_end();
+ di != de; ++di) {
+ if (clang::FieldDecl *nested_field_decl =
+ llvm::dyn_cast<clang::FieldDecl>(*di)) {
+ clang::NamedDecl **chain =
+ new (*ast->getASTContext()) clang::NamedDecl *[2];
+ chain[0] = *field_pos;
+ chain[1] = nested_field_decl;
+ clang::IndirectFieldDecl *indirect_field =
+ clang::IndirectFieldDecl::Create(
+ *ast->getASTContext(), record_decl, clang::SourceLocation(),
+ nested_field_decl->getIdentifier(),
+ nested_field_decl->getType(), {chain, 2});
+
+ indirect_field->setImplicit();
+
+ indirect_field->setAccess(ClangASTContext::UnifyAccessSpecifiers(
+ field_pos->getAccess(), nested_field_decl->getAccess()));
+
+ indirect_fields.push_back(indirect_field);
+ } else if (clang::IndirectFieldDecl *nested_indirect_field_decl =
+ llvm::dyn_cast<clang::IndirectFieldDecl>(*di)) {
+ size_t nested_chain_size =
+ nested_indirect_field_decl->getChainingSize();
+ clang::NamedDecl **chain = new (*ast->getASTContext())
+ clang::NamedDecl *[nested_chain_size + 1];
+ chain[0] = *field_pos;
+
+ int chain_index = 1;
+ for (clang::IndirectFieldDecl::chain_iterator
+ nci = nested_indirect_field_decl->chain_begin(),
+ nce = nested_indirect_field_decl->chain_end();
+ nci < nce; ++nci) {
+ chain[chain_index] = *nci;
+ chain_index++;
+ }
+
+ clang::IndirectFieldDecl *indirect_field =
+ clang::IndirectFieldDecl::Create(
+ *ast->getASTContext(), record_decl, clang::SourceLocation(),
+ nested_indirect_field_decl->getIdentifier(),
+ nested_indirect_field_decl->getType(),
+ {chain, nested_chain_size + 1});
+
+ indirect_field->setImplicit();
+
+ indirect_field->setAccess(ClangASTContext::UnifyAccessSpecifiers(
+ field_pos->getAccess(), nested_indirect_field_decl->getAccess()));
+
+ indirect_fields.push_back(indirect_field);
+ }
+ }
+ }
+ }
+
+ // Check the last field to see if it has an incomplete array type as its last
+ // member and if it does, the tell the record decl about it
+ if (last_field_pos != field_end_pos) {
+ if (last_field_pos->getType()->isIncompleteArrayType())
+ record_decl->hasFlexibleArrayMember();
+ }
+
+ for (IndirectFieldVector::iterator ifi = indirect_fields.begin(),
+ ife = indirect_fields.end();
+ ifi < ife; ++ifi) {
+ record_decl->addDecl(*ifi);
+ }
+}
+
+void ClangASTContext::SetIsPacked(const CompilerType &type) {
+ if (type) {
+ ClangASTContext *ast =
+ llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (ast) {
+ clang::RecordDecl *record_decl = GetAsRecordDecl(type);
+
+ if (!record_decl)
+ return;
+
+ record_decl->addAttr(
+ clang::PackedAttr::CreateImplicit(*ast->getASTContext()));
+ }
+ }
+}
+
+clang::VarDecl *ClangASTContext::AddVariableToRecordType(
+ const CompilerType &type, llvm::StringRef name,
+ const CompilerType &var_type, AccessType access) {
+ if (!type.IsValid() || !var_type.IsValid())
+ return nullptr;
+
+ ClangASTContext *ast = llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (!ast)
+ return nullptr;
+
+ clang::RecordDecl *record_decl = ast->GetAsRecordDecl(type);
+ if (!record_decl)
+ return nullptr;
+
+ clang::VarDecl *var_decl = nullptr;
+ clang::IdentifierInfo *ident = nullptr;
+ if (!name.empty())
+ ident = &ast->getASTContext()->Idents.get(name);
+
+ var_decl = clang::VarDecl::Create(
+ *ast->getASTContext(), // ASTContext &
+ record_decl, // DeclContext *
+ clang::SourceLocation(), // clang::SourceLocation StartLoc
+ clang::SourceLocation(), // clang::SourceLocation IdLoc
+ ident, // clang::IdentifierInfo *
+ ClangUtil::GetQualType(var_type), // Variable clang::QualType
+ nullptr, // TypeSourceInfo *
+ clang::SC_Static); // StorageClass
+ if (!var_decl)
+ return nullptr;
+
+ var_decl->setAccess(
+ ClangASTContext::ConvertAccessTypeToAccessSpecifier(access));
+ record_decl->addDecl(var_decl);
+
+#ifdef LLDB_CONFIGURATION_DEBUG
+ VerifyDecl(var_decl);
+#endif
+
+ return var_decl;
+}
+
+clang::CXXMethodDecl *ClangASTContext::AddMethodToCXXRecordType(
+ lldb::opaque_compiler_type_t type, const char *name, const char *mangled_name,
+ const CompilerType &method_clang_type, lldb::AccessType access,
+ bool is_virtual, bool is_static, bool is_inline, bool is_explicit,
+ bool is_attr_used, bool is_artificial) {
+ if (!type || !method_clang_type.IsValid() || name == nullptr ||
+ name[0] == '\0')
+ return nullptr;
+
+ clang::QualType record_qual_type(GetCanonicalQualType(type));
+
+ clang::CXXRecordDecl *cxx_record_decl =
+ record_qual_type->getAsCXXRecordDecl();
+
+ if (cxx_record_decl == nullptr)
+ return nullptr;
+
+ clang::QualType method_qual_type(ClangUtil::GetQualType(method_clang_type));
+
+ clang::CXXMethodDecl *cxx_method_decl = nullptr;
+
+ clang::DeclarationName decl_name(&getASTContext()->Idents.get(name));
+
+ const clang::FunctionType *function_type =
+ llvm::dyn_cast<clang::FunctionType>(method_qual_type.getTypePtr());
+
+ if (function_type == nullptr)
+ return nullptr;
+
+ const clang::FunctionProtoType *method_function_prototype(
+ llvm::dyn_cast<clang::FunctionProtoType>(function_type));
+
+ if (!method_function_prototype)
+ return nullptr;
+
+ unsigned int num_params = method_function_prototype->getNumParams();
+
+ clang::CXXDestructorDecl *cxx_dtor_decl(nullptr);
+ clang::CXXConstructorDecl *cxx_ctor_decl(nullptr);
+
+ if (is_artificial)
+ return nullptr; // skip everything artificial
+
+ const clang::ExplicitSpecifier explicit_spec(
+ nullptr /*expr*/, is_explicit
+ ? clang::ExplicitSpecKind::ResolvedTrue
+ : clang::ExplicitSpecKind::ResolvedFalse);
+ if (name[0] == '~') {
+ cxx_dtor_decl = clang::CXXDestructorDecl::Create(
+ *getASTContext(), cxx_record_decl, clang::SourceLocation(),
+ clang::DeclarationNameInfo(
+ getASTContext()->DeclarationNames.getCXXDestructorName(
+ getASTContext()->getCanonicalType(record_qual_type)),
+ clang::SourceLocation()),
+ method_qual_type, nullptr, is_inline, is_artificial);
+ cxx_method_decl = cxx_dtor_decl;
+ } else if (decl_name == cxx_record_decl->getDeclName()) {
+ cxx_ctor_decl = clang::CXXConstructorDecl::Create(
+ *getASTContext(), cxx_record_decl, clang::SourceLocation(),
+ clang::DeclarationNameInfo(
+ getASTContext()->DeclarationNames.getCXXConstructorName(
+ getASTContext()->getCanonicalType(record_qual_type)),
+ clang::SourceLocation()),
+ method_qual_type,
+ nullptr, // TypeSourceInfo *
+ explicit_spec, is_inline, is_artificial, CSK_unspecified);
+ cxx_method_decl = cxx_ctor_decl;
+ } else {
+ clang::StorageClass SC = is_static ? clang::SC_Static : clang::SC_None;
+ clang::OverloadedOperatorKind op_kind = clang::NUM_OVERLOADED_OPERATORS;
+
+ if (IsOperator(name, op_kind)) {
+ if (op_kind != clang::NUM_OVERLOADED_OPERATORS) {
+ // Check the number of operator parameters. Sometimes we have seen bad
+ // DWARF that doesn't correctly describe operators and if we try to
+ // create a method and add it to the class, clang will assert and
+ // crash, so we need to make sure things are acceptable.
+ const bool is_method = true;
+ if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount(
+ is_method, op_kind, num_params))
+ return nullptr;
+ cxx_method_decl = clang::CXXMethodDecl::Create(
+ *getASTContext(), cxx_record_decl, clang::SourceLocation(),
+ clang::DeclarationNameInfo(
+ getASTContext()->DeclarationNames.getCXXOperatorName(op_kind),
+ clang::SourceLocation()),
+ method_qual_type,
+ nullptr, // TypeSourceInfo *
+ SC, is_inline, CSK_unspecified, clang::SourceLocation());
+ } else if (num_params == 0) {
+ // Conversion operators don't take params...
+ cxx_method_decl = clang::CXXConversionDecl::Create(
+ *getASTContext(), cxx_record_decl, clang::SourceLocation(),
+ clang::DeclarationNameInfo(
+ getASTContext()->DeclarationNames.getCXXConversionFunctionName(
+ getASTContext()->getCanonicalType(
+ function_type->getReturnType())),
+ clang::SourceLocation()),
+ method_qual_type,
+ nullptr, // TypeSourceInfo *
+ is_inline, explicit_spec, CSK_unspecified,
+ clang::SourceLocation());
+ }
+ }
+
+ if (cxx_method_decl == nullptr) {
+ cxx_method_decl = clang::CXXMethodDecl::Create(
+ *getASTContext(), cxx_record_decl, clang::SourceLocation(),
+ clang::DeclarationNameInfo(decl_name, clang::SourceLocation()),
+ method_qual_type,
+ nullptr, // TypeSourceInfo *
+ SC, is_inline, CSK_unspecified, clang::SourceLocation());
+ }
+ }
+
+ clang::AccessSpecifier access_specifier =
+ ClangASTContext::ConvertAccessTypeToAccessSpecifier(access);
+
+ cxx_method_decl->setAccess(access_specifier);
+ cxx_method_decl->setVirtualAsWritten(is_virtual);
+
+ if (is_attr_used)
+ cxx_method_decl->addAttr(clang::UsedAttr::CreateImplicit(*getASTContext()));
+
+ if (mangled_name != nullptr) {
+ cxx_method_decl->addAttr(
+ clang::AsmLabelAttr::CreateImplicit(*getASTContext(), mangled_name));
+ }
+
+ // Populate the method decl with parameter decls
+
+ llvm::SmallVector<clang::ParmVarDecl *, 12> params;
+
+ for (unsigned param_index = 0; param_index < num_params; ++param_index) {
+ params.push_back(clang::ParmVarDecl::Create(
+ *getASTContext(), cxx_method_decl, clang::SourceLocation(),
+ clang::SourceLocation(),
+ nullptr, // anonymous
+ method_function_prototype->getParamType(param_index), nullptr,
+ clang::SC_None, nullptr));
+ }
+
+ cxx_method_decl->setParams(llvm::ArrayRef<clang::ParmVarDecl *>(params));
+
+ cxx_record_decl->addDecl(cxx_method_decl);
+
+ // Sometimes the debug info will mention a constructor (default/copy/move),
+ // destructor, or assignment operator (copy/move) but there won't be any
+ // version of this in the code. So we check if the function was artificially
+ // generated and if it is trivial and this lets the compiler/backend know
+ // that it can inline the IR for these when it needs to and we can avoid a
+ // "missing function" error when running expressions.
+
+ if (is_artificial) {
+ if (cxx_ctor_decl && ((cxx_ctor_decl->isDefaultConstructor() &&
+ cxx_record_decl->hasTrivialDefaultConstructor()) ||
+ (cxx_ctor_decl->isCopyConstructor() &&
+ cxx_record_decl->hasTrivialCopyConstructor()) ||
+ (cxx_ctor_decl->isMoveConstructor() &&
+ cxx_record_decl->hasTrivialMoveConstructor()))) {
+ cxx_ctor_decl->setDefaulted();
+ cxx_ctor_decl->setTrivial(true);
+ } else if (cxx_dtor_decl) {
+ if (cxx_record_decl->hasTrivialDestructor()) {
+ cxx_dtor_decl->setDefaulted();
+ cxx_dtor_decl->setTrivial(true);
+ }
+ } else if ((cxx_method_decl->isCopyAssignmentOperator() &&
+ cxx_record_decl->hasTrivialCopyAssignment()) ||
+ (cxx_method_decl->isMoveAssignmentOperator() &&
+ cxx_record_decl->hasTrivialMoveAssignment())) {
+ cxx_method_decl->setDefaulted();
+ cxx_method_decl->setTrivial(true);
+ }
+ }
+
+#ifdef LLDB_CONFIGURATION_DEBUG
+ VerifyDecl(cxx_method_decl);
+#endif
+
+ // printf ("decl->isPolymorphic() = %i\n",
+ // cxx_record_decl->isPolymorphic());
+ // printf ("decl->isAggregate() = %i\n",
+ // cxx_record_decl->isAggregate());
+ // printf ("decl->isPOD() = %i\n",
+ // cxx_record_decl->isPOD());
+ // printf ("decl->isEmpty() = %i\n",
+ // cxx_record_decl->isEmpty());
+ // printf ("decl->isAbstract() = %i\n",
+ // cxx_record_decl->isAbstract());
+ // printf ("decl->hasTrivialConstructor() = %i\n",
+ // cxx_record_decl->hasTrivialConstructor());
+ // printf ("decl->hasTrivialCopyConstructor() = %i\n",
+ // cxx_record_decl->hasTrivialCopyConstructor());
+ // printf ("decl->hasTrivialCopyAssignment() = %i\n",
+ // cxx_record_decl->hasTrivialCopyAssignment());
+ // printf ("decl->hasTrivialDestructor() = %i\n",
+ // cxx_record_decl->hasTrivialDestructor());
+ return cxx_method_decl;
+}
+
+void ClangASTContext::AddMethodOverridesForCXXRecordType(
+ lldb::opaque_compiler_type_t type) {
+ if (auto *record = GetAsCXXRecordDecl(type))
+ for (auto *method : record->methods())
+ addOverridesForMethod(method);
+}
+
+#pragma mark C++ Base Classes
+
+std::unique_ptr<clang::CXXBaseSpecifier>
+ClangASTContext::CreateBaseClassSpecifier(lldb::opaque_compiler_type_t type,
+ AccessType access, bool is_virtual,
+ bool base_of_class) {
+ if (!type)
+ return nullptr;
+
+ return llvm::make_unique<clang::CXXBaseSpecifier>(
+ clang::SourceRange(), is_virtual, base_of_class,
+ ClangASTContext::ConvertAccessTypeToAccessSpecifier(access),
+ getASTContext()->getTrivialTypeSourceInfo(GetQualType(type)),
+ clang::SourceLocation());
+}
+
+bool ClangASTContext::TransferBaseClasses(
+ lldb::opaque_compiler_type_t type,
+ std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases) {
+ if (!type)
+ return false;
+ clang::CXXRecordDecl *cxx_record_decl = GetAsCXXRecordDecl(type);
+ if (!cxx_record_decl)
+ return false;
+ std::vector<clang::CXXBaseSpecifier *> raw_bases;
+ raw_bases.reserve(bases.size());
+
+ // Clang will make a copy of them, so it's ok that we pass pointers that we're
+ // about to destroy.
+ for (auto &b : bases)
+ raw_bases.push_back(b.get());
+ cxx_record_decl->setBases(raw_bases.data(), raw_bases.size());
+ return true;
+}
+
+bool ClangASTContext::SetObjCSuperClass(
+ const CompilerType &type, const CompilerType &superclass_clang_type) {
+ ClangASTContext *ast =
+ llvm::dyn_cast_or_null<ClangASTContext>(type.GetTypeSystem());
+ if (!ast)
+ return false;
+ clang::ASTContext *clang_ast = ast->getASTContext();
+
+ if (type && superclass_clang_type.IsValid() &&
+ superclass_clang_type.GetTypeSystem() == type.GetTypeSystem()) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ GetAsObjCInterfaceDecl(type);
+ clang::ObjCInterfaceDecl *super_interface_decl =
+ GetAsObjCInterfaceDecl(superclass_clang_type);
+ if (class_interface_decl && super_interface_decl) {
+ class_interface_decl->setSuperClass(clang_ast->getTrivialTypeSourceInfo(
+ clang_ast->getObjCInterfaceType(super_interface_decl)));
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ClangASTContext::AddObjCClassProperty(
+ const CompilerType &type, const char *property_name,
+ const CompilerType &property_clang_type, clang::ObjCIvarDecl *ivar_decl,
+ const char *property_setter_name, const char *property_getter_name,
+ uint32_t property_attributes, ClangASTMetadata *metadata) {
+ if (!type || !property_clang_type.IsValid() || property_name == nullptr ||
+ property_name[0] == '\0')
+ return false;
+ ClangASTContext *ast = llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (!ast)
+ return false;
+ clang::ASTContext *clang_ast = ast->getASTContext();
+
+ clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type);
+
+ if (class_interface_decl) {
+ CompilerType property_clang_type_to_access;
+
+ if (property_clang_type.IsValid())
+ property_clang_type_to_access = property_clang_type;
+ else if (ivar_decl)
+ property_clang_type_to_access =
+ CompilerType(clang_ast, ivar_decl->getType());
+
+ if (class_interface_decl && property_clang_type_to_access.IsValid()) {
+ clang::TypeSourceInfo *prop_type_source;
+ if (ivar_decl)
+ prop_type_source =
+ clang_ast->getTrivialTypeSourceInfo(ivar_decl->getType());
+ else
+ prop_type_source = clang_ast->getTrivialTypeSourceInfo(
+ ClangUtil::GetQualType(property_clang_type));
+
+ clang::ObjCPropertyDecl *property_decl = clang::ObjCPropertyDecl::Create(
+ *clang_ast, class_interface_decl,
+ clang::SourceLocation(), // Source Location
+ &clang_ast->Idents.get(property_name),
+ clang::SourceLocation(), // Source Location for AT
+ clang::SourceLocation(), // Source location for (
+ ivar_decl ? ivar_decl->getType()
+ : ClangUtil::GetQualType(property_clang_type),
+ prop_type_source);
+
+ if (property_decl) {
+ if (metadata)
+ ClangASTContext::SetMetadata(clang_ast, property_decl, *metadata);
+
+ class_interface_decl->addDecl(property_decl);
+
+ clang::Selector setter_sel, getter_sel;
+
+ if (property_setter_name != nullptr) {
+ std::string property_setter_no_colon(
+ property_setter_name, strlen(property_setter_name) - 1);
+ clang::IdentifierInfo *setter_ident =
+ &clang_ast->Idents.get(property_setter_no_colon);
+ setter_sel = clang_ast->Selectors.getSelector(1, &setter_ident);
+ } else if (!(property_attributes & DW_APPLE_PROPERTY_readonly)) {
+ std::string setter_sel_string("set");
+ setter_sel_string.push_back(::toupper(property_name[0]));
+ setter_sel_string.append(&property_name[1]);
+ clang::IdentifierInfo *setter_ident =
+ &clang_ast->Idents.get(setter_sel_string);
+ setter_sel = clang_ast->Selectors.getSelector(1, &setter_ident);
+ }
+ property_decl->setSetterName(setter_sel);
+ property_decl->setPropertyAttributes(
+ clang::ObjCPropertyDecl::OBJC_PR_setter);
+
+ if (property_getter_name != nullptr) {
+ clang::IdentifierInfo *getter_ident =
+ &clang_ast->Idents.get(property_getter_name);
+ getter_sel = clang_ast->Selectors.getSelector(0, &getter_ident);
+ } else {
+ clang::IdentifierInfo *getter_ident =
+ &clang_ast->Idents.get(property_name);
+ getter_sel = clang_ast->Selectors.getSelector(0, &getter_ident);
+ }
+ property_decl->setGetterName(getter_sel);
+ property_decl->setPropertyAttributes(
+ clang::ObjCPropertyDecl::OBJC_PR_getter);
+
+ if (ivar_decl)
+ property_decl->setPropertyIvarDecl(ivar_decl);
+
+ if (property_attributes & DW_APPLE_PROPERTY_readonly)
+ property_decl->setPropertyAttributes(
+ clang::ObjCPropertyDecl::OBJC_PR_readonly);
+ if (property_attributes & DW_APPLE_PROPERTY_readwrite)
+ property_decl->setPropertyAttributes(
+ clang::ObjCPropertyDecl::OBJC_PR_readwrite);
+ if (property_attributes & DW_APPLE_PROPERTY_assign)
+ property_decl->setPropertyAttributes(
+ clang::ObjCPropertyDecl::OBJC_PR_assign);
+ if (property_attributes & DW_APPLE_PROPERTY_retain)
+ property_decl->setPropertyAttributes(
+ clang::ObjCPropertyDecl::OBJC_PR_retain);
+ if (property_attributes & DW_APPLE_PROPERTY_copy)
+ property_decl->setPropertyAttributes(
+ clang::ObjCPropertyDecl::OBJC_PR_copy);
+ if (property_attributes & DW_APPLE_PROPERTY_nonatomic)
+ property_decl->setPropertyAttributes(
+ clang::ObjCPropertyDecl::OBJC_PR_nonatomic);
+ if (property_attributes & clang::ObjCPropertyDecl::OBJC_PR_nullability)
+ property_decl->setPropertyAttributes(
+ clang::ObjCPropertyDecl::OBJC_PR_nullability);
+ if (property_attributes &
+ clang::ObjCPropertyDecl::OBJC_PR_null_resettable)
+ property_decl->setPropertyAttributes(
+ clang::ObjCPropertyDecl::OBJC_PR_null_resettable);
+ if (property_attributes & clang::ObjCPropertyDecl::OBJC_PR_class)
+ property_decl->setPropertyAttributes(
+ clang::ObjCPropertyDecl::OBJC_PR_class);
+
+ const bool isInstance =
+ (property_attributes & clang::ObjCPropertyDecl::OBJC_PR_class) == 0;
+
+ if (!getter_sel.isNull() &&
+ !(isInstance
+ ? class_interface_decl->lookupInstanceMethod(getter_sel)
+ : class_interface_decl->lookupClassMethod(getter_sel))) {
+ const bool isVariadic = false;
+ const bool isSynthesized = false;
+ const bool isImplicitlyDeclared = true;
+ const bool isDefined = false;
+ const clang::ObjCMethodDecl::ImplementationControl impControl =
+ clang::ObjCMethodDecl::None;
+ const bool HasRelatedResultType = false;
+
+ clang::ObjCMethodDecl *getter = clang::ObjCMethodDecl::Create(
+ *clang_ast, clang::SourceLocation(), clang::SourceLocation(),
+ getter_sel, ClangUtil::GetQualType(property_clang_type_to_access),
+ nullptr, class_interface_decl, isInstance, isVariadic,
+ isSynthesized, isImplicitlyDeclared, isDefined, impControl,
+ HasRelatedResultType);
+
+ if (getter && metadata)
+ ClangASTContext::SetMetadata(clang_ast, getter, *metadata);
+
+ if (getter) {
+ getter->setMethodParams(*clang_ast,
+ llvm::ArrayRef<clang::ParmVarDecl *>(),
+ llvm::ArrayRef<clang::SourceLocation>());
+
+ class_interface_decl->addDecl(getter);
+ }
+ }
+
+ if (!setter_sel.isNull() &&
+ !(isInstance
+ ? class_interface_decl->lookupInstanceMethod(setter_sel)
+ : class_interface_decl->lookupClassMethod(setter_sel))) {
+ clang::QualType result_type = clang_ast->VoidTy;
+ const bool isVariadic = false;
+ const bool isSynthesized = false;
+ const bool isImplicitlyDeclared = true;
+ const bool isDefined = false;
+ const clang::ObjCMethodDecl::ImplementationControl impControl =
+ clang::ObjCMethodDecl::None;
+ const bool HasRelatedResultType = false;
+
+ clang::ObjCMethodDecl *setter = clang::ObjCMethodDecl::Create(
+ *clang_ast, clang::SourceLocation(), clang::SourceLocation(),
+ setter_sel, result_type, nullptr, class_interface_decl,
+ isInstance, isVariadic, isSynthesized, isImplicitlyDeclared,
+ isDefined, impControl, HasRelatedResultType);
+
+ if (setter && metadata)
+ ClangASTContext::SetMetadata(clang_ast, setter, *metadata);
+
+ llvm::SmallVector<clang::ParmVarDecl *, 1> params;
+
+ params.push_back(clang::ParmVarDecl::Create(
+ *clang_ast, setter, clang::SourceLocation(),
+ clang::SourceLocation(),
+ nullptr, // anonymous
+ ClangUtil::GetQualType(property_clang_type_to_access), nullptr,
+ clang::SC_Auto, nullptr));
+
+ if (setter) {
+ setter->setMethodParams(
+ *clang_ast, llvm::ArrayRef<clang::ParmVarDecl *>(params),
+ llvm::ArrayRef<clang::SourceLocation>());
+
+ class_interface_decl->addDecl(setter);
+ }
+ }
+
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool ClangASTContext::IsObjCClassTypeAndHasIVars(const CompilerType &type,
+ bool check_superclass) {
+ clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type);
+ if (class_interface_decl)
+ return ObjCDeclHasIVars(class_interface_decl, check_superclass);
+ return false;
+}
+
+clang::ObjCMethodDecl *ClangASTContext::AddMethodToObjCObjectType(
+ const CompilerType &type,
+ const char *name, // the full symbol name as seen in the symbol table
+ // (lldb::opaque_compiler_type_t type, "-[NString
+ // stringWithCString:]")
+ const CompilerType &method_clang_type, lldb::AccessType access,
+ bool is_artificial, bool is_variadic) {
+ if (!type || !method_clang_type.IsValid())
+ return nullptr;
+
+ clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type);
+
+ if (class_interface_decl == nullptr)
+ return nullptr;
+ ClangASTContext *lldb_ast =
+ llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (lldb_ast == nullptr)
+ return nullptr;
+ clang::ASTContext *ast = lldb_ast->getASTContext();
+
+ const char *selector_start = ::strchr(name, ' ');
+ if (selector_start == nullptr)
+ return nullptr;
+
+ selector_start++;
+ llvm::SmallVector<clang::IdentifierInfo *, 12> selector_idents;
+
+ size_t len = 0;
+ const char *start;
+ // printf ("name = '%s'\n", name);
+
+ unsigned num_selectors_with_args = 0;
+ for (start = selector_start; start && *start != '\0' && *start != ']';
+ start += len) {
+ len = ::strcspn(start, ":]");
+ bool has_arg = (start[len] == ':');
+ if (has_arg)
+ ++num_selectors_with_args;
+ selector_idents.push_back(&ast->Idents.get(llvm::StringRef(start, len)));
+ if (has_arg)
+ len += 1;
+ }
+
+ if (selector_idents.size() == 0)
+ return nullptr;
+
+ clang::Selector method_selector = ast->Selectors.getSelector(
+ num_selectors_with_args ? selector_idents.size() : 0,
+ selector_idents.data());
+
+ clang::QualType method_qual_type(ClangUtil::GetQualType(method_clang_type));
+
+ // Populate the method decl with parameter decls
+ const clang::Type *method_type(method_qual_type.getTypePtr());
+
+ if (method_type == nullptr)
+ return nullptr;
+
+ const clang::FunctionProtoType *method_function_prototype(
+ llvm::dyn_cast<clang::FunctionProtoType>(method_type));
+
+ if (!method_function_prototype)
+ return nullptr;
+
+ bool is_synthesized = false;
+ bool is_defined = false;
+ clang::ObjCMethodDecl::ImplementationControl imp_control =
+ clang::ObjCMethodDecl::None;
+
+ const unsigned num_args = method_function_prototype->getNumParams();
+
+ if (num_args != num_selectors_with_args)
+ return nullptr; // some debug information is corrupt. We are not going to
+ // deal with it.
+
+ clang::ObjCMethodDecl *objc_method_decl = clang::ObjCMethodDecl::Create(
+ *ast,
+ clang::SourceLocation(), // beginLoc,
+ clang::SourceLocation(), // endLoc,
+ method_selector, method_function_prototype->getReturnType(),
+ nullptr, // TypeSourceInfo *ResultTInfo,
+ ClangASTContext::GetASTContext(ast)->GetDeclContextForType(
+ ClangUtil::GetQualType(type)),
+ name[0] == '-', is_variadic, is_synthesized,
+ true, // is_implicitly_declared; we force this to true because we don't
+ // have source locations
+ is_defined, imp_control, false /*has_related_result_type*/);
+
+ if (objc_method_decl == nullptr)
+ return nullptr;
+
+ if (num_args > 0) {
+ llvm::SmallVector<clang::ParmVarDecl *, 12> params;
+
+ for (unsigned param_index = 0; param_index < num_args; ++param_index) {
+ params.push_back(clang::ParmVarDecl::Create(
+ *ast, objc_method_decl, clang::SourceLocation(),
+ clang::SourceLocation(),
+ nullptr, // anonymous
+ method_function_prototype->getParamType(param_index), nullptr,
+ clang::SC_Auto, nullptr));
+ }
+
+ objc_method_decl->setMethodParams(
+ *ast, llvm::ArrayRef<clang::ParmVarDecl *>(params),
+ llvm::ArrayRef<clang::SourceLocation>());
+ }
+
+ class_interface_decl->addDecl(objc_method_decl);
+
+#ifdef LLDB_CONFIGURATION_DEBUG
+ VerifyDecl(objc_method_decl);
+#endif
+
+ return objc_method_decl;
+}
+
+bool ClangASTContext::GetHasExternalStorage(const CompilerType &type) {
+ if (ClangUtil::IsClangType(type))
+ return false;
+
+ clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record: {
+ clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl)
+ return cxx_record_decl->hasExternalLexicalStorage() ||
+ cxx_record_decl->hasExternalVisibleStorage();
+ } break;
+
+ case clang::Type::Enum: {
+ clang::EnumDecl *enum_decl =
+ llvm::cast<clang::EnumType>(qual_type)->getDecl();
+ if (enum_decl)
+ return enum_decl->hasExternalLexicalStorage() ||
+ enum_decl->hasExternalVisibleStorage();
+ } break;
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface: {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr());
+ assert(objc_class_type);
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+
+ if (class_interface_decl)
+ return class_interface_decl->hasExternalLexicalStorage() ||
+ class_interface_decl->hasExternalVisibleStorage();
+ }
+ } break;
+
+ case clang::Type::Typedef:
+ return GetHasExternalStorage(CompilerType(
+ type.GetTypeSystem(), llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr()));
+
+ case clang::Type::Auto:
+ return GetHasExternalStorage(CompilerType(
+ type.GetTypeSystem(), llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr()));
+
+ case clang::Type::Elaborated:
+ return GetHasExternalStorage(CompilerType(
+ type.GetTypeSystem(), llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr()));
+
+ case clang::Type::Paren:
+ return GetHasExternalStorage(CompilerType(
+ type.GetTypeSystem(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr()));
+
+ default:
+ break;
+ }
+ return false;
+}
+
+bool ClangASTContext::SetHasExternalStorage(lldb::opaque_compiler_type_t type,
+ bool has_extern) {
+ if (!type)
+ return false;
+
+ clang::QualType qual_type(GetCanonicalQualType(type));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record: {
+ clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl) {
+ cxx_record_decl->setHasExternalLexicalStorage(has_extern);
+ cxx_record_decl->setHasExternalVisibleStorage(has_extern);
+ return true;
+ }
+ } break;
+
+ case clang::Type::Enum: {
+ clang::EnumDecl *enum_decl =
+ llvm::cast<clang::EnumType>(qual_type)->getDecl();
+ if (enum_decl) {
+ enum_decl->setHasExternalLexicalStorage(has_extern);
+ enum_decl->setHasExternalVisibleStorage(has_extern);
+ return true;
+ }
+ } break;
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface: {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr());
+ assert(objc_class_type);
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+
+ if (class_interface_decl) {
+ class_interface_decl->setHasExternalLexicalStorage(has_extern);
+ class_interface_decl->setHasExternalVisibleStorage(has_extern);
+ return true;
+ }
+ }
+ } break;
+
+ case clang::Type::Typedef:
+ return SetHasExternalStorage(llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr(),
+ has_extern);
+
+ case clang::Type::Auto:
+ return SetHasExternalStorage(llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr(),
+ has_extern);
+
+ case clang::Type::Elaborated:
+ return SetHasExternalStorage(llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr(),
+ has_extern);
+
+ case clang::Type::Paren:
+ return SetHasExternalStorage(
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr(),
+ has_extern);
+
+ default:
+ break;
+ }
+ return false;
+}
+
+#pragma mark TagDecl
+
+bool ClangASTContext::StartTagDeclarationDefinition(const CompilerType &type) {
+ clang::QualType qual_type(ClangUtil::GetQualType(type));
+ if (!qual_type.isNull()) {
+ const clang::TagType *tag_type = qual_type->getAs<clang::TagType>();
+ if (tag_type) {
+ clang::TagDecl *tag_decl = tag_type->getDecl();
+ if (tag_decl) {
+ tag_decl->startDefinition();
+ return true;
+ }
+ }
+
+ const clang::ObjCObjectType *object_type =
+ qual_type->getAs<clang::ObjCObjectType>();
+ if (object_type) {
+ clang::ObjCInterfaceDecl *interface_decl = object_type->getInterface();
+ if (interface_decl) {
+ interface_decl->startDefinition();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool ClangASTContext::CompleteTagDeclarationDefinition(
+ const CompilerType &type) {
+ clang::QualType qual_type(ClangUtil::GetQualType(type));
+ if (!qual_type.isNull()) {
+ // Make sure we use the same methodology as
+ // ClangASTContext::StartTagDeclarationDefinition() as to how we start/end
+ // the definition. Previously we were calling
+ const clang::TagType *tag_type = qual_type->getAs<clang::TagType>();
+ if (tag_type) {
+ clang::TagDecl *tag_decl = tag_type->getDecl();
+ if (tag_decl) {
+ clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast_or_null<clang::CXXRecordDecl>(tag_decl);
+
+ if (cxx_record_decl) {
+ if (!cxx_record_decl->isCompleteDefinition())
+ cxx_record_decl->completeDefinition();
+ cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true);
+ cxx_record_decl->setHasExternalLexicalStorage(false);
+ cxx_record_decl->setHasExternalVisibleStorage(false);
+ return true;
+ }
+ }
+ }
+
+ const clang::EnumType *enutype = qual_type->getAs<clang::EnumType>();
+
+ if (enutype) {
+ clang::EnumDecl *enum_decl = enutype->getDecl();
+
+ if (enum_decl) {
+ if (!enum_decl->isCompleteDefinition()) {
+ ClangASTContext *lldb_ast =
+ llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (lldb_ast == nullptr)
+ return false;
+ clang::ASTContext *ast = lldb_ast->getASTContext();
+
+ /// TODO This really needs to be fixed.
+
+ QualType integer_type(enum_decl->getIntegerType());
+ if (!integer_type.isNull()) {
+ unsigned NumPositiveBits = 1;
+ unsigned NumNegativeBits = 0;
+
+ clang::QualType promotion_qual_type;
+ // If the enum integer type is less than an integer in bit width,
+ // then we must promote it to an integer size.
+ if (ast->getTypeSize(enum_decl->getIntegerType()) <
+ ast->getTypeSize(ast->IntTy)) {
+ if (enum_decl->getIntegerType()->isSignedIntegerType())
+ promotion_qual_type = ast->IntTy;
+ else
+ promotion_qual_type = ast->UnsignedIntTy;
+ } else
+ promotion_qual_type = enum_decl->getIntegerType();
+
+ enum_decl->completeDefinition(enum_decl->getIntegerType(),
+ promotion_qual_type, NumPositiveBits,
+ NumNegativeBits);
+ }
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+clang::EnumConstantDecl *ClangASTContext::AddEnumerationValueToEnumerationType(
+ const CompilerType &enum_type, const Declaration &decl, const char *name,
+ const llvm::APSInt &value) {
+
+ if (!enum_type || ConstString(name).IsEmpty())
+ return nullptr;
+
+ lldbassert(enum_type.GetTypeSystem() == static_cast<TypeSystem *>(this));
+
+ lldb::opaque_compiler_type_t enum_opaque_compiler_type =
+ enum_type.GetOpaqueQualType();
+
+ if (!enum_opaque_compiler_type)
+ return nullptr;
+
+ clang::QualType enum_qual_type(
+ GetCanonicalQualType(enum_opaque_compiler_type));
+
+ const clang::Type *clang_type = enum_qual_type.getTypePtr();
+
+ if (!clang_type)
+ return nullptr;
+
+ const clang::EnumType *enutype = llvm::dyn_cast<clang::EnumType>(clang_type);
+
+ if (!enutype)
+ return nullptr;
+
+ clang::EnumConstantDecl *enumerator_decl = clang::EnumConstantDecl::Create(
+ *getASTContext(), enutype->getDecl(), clang::SourceLocation(),
+ name ? &getASTContext()->Idents.get(name) : nullptr, // Identifier
+ clang::QualType(enutype, 0), nullptr, value);
+
+ if (!enumerator_decl)
+ return nullptr;
+
+ enutype->getDecl()->addDecl(enumerator_decl);
+
+#ifdef LLDB_CONFIGURATION_DEBUG
+ VerifyDecl(enumerator_decl);
+#endif
+
+ return enumerator_decl;
+}
+
+clang::EnumConstantDecl *ClangASTContext::AddEnumerationValueToEnumerationType(
+ const CompilerType &enum_type, const Declaration &decl, const char *name,
+ int64_t enum_value, uint32_t enum_value_bit_size) {
+ CompilerType underlying_type =
+ GetEnumerationIntegerType(enum_type.GetOpaqueQualType());
+ bool is_signed = false;
+ underlying_type.IsIntegerType(is_signed);
+
+ llvm::APSInt value(enum_value_bit_size, is_signed);
+ value = enum_value;
+
+ return AddEnumerationValueToEnumerationType(enum_type, decl, name, value);
+}
+
+CompilerType
+ClangASTContext::GetEnumerationIntegerType(lldb::opaque_compiler_type_t type) {
+ clang::QualType enum_qual_type(GetCanonicalQualType(type));
+ const clang::Type *clang_type = enum_qual_type.getTypePtr();
+ if (clang_type) {
+ const clang::EnumType *enutype =
+ llvm::dyn_cast<clang::EnumType>(clang_type);
+ if (enutype) {
+ clang::EnumDecl *enum_decl = enutype->getDecl();
+ if (enum_decl)
+ return CompilerType(getASTContext(), enum_decl->getIntegerType());
+ }
+ }
+ return CompilerType();
+}
+
+CompilerType
+ClangASTContext::CreateMemberPointerType(const CompilerType &type,
+ const CompilerType &pointee_type) {
+ if (type && pointee_type.IsValid() &&
+ type.GetTypeSystem() == pointee_type.GetTypeSystem()) {
+ ClangASTContext *ast =
+ llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (!ast)
+ return CompilerType();
+ return CompilerType(ast->getASTContext(),
+ ast->getASTContext()->getMemberPointerType(
+ ClangUtil::GetQualType(pointee_type),
+ ClangUtil::GetQualType(type).getTypePtr()));
+ }
+ return CompilerType();
+}
+
+size_t
+ClangASTContext::ConvertStringToFloatValue(lldb::opaque_compiler_type_t type,
+ const char *s, uint8_t *dst,
+ size_t dst_size) {
+ if (type) {
+ clang::QualType qual_type(GetCanonicalQualType(type));
+ uint32_t count = 0;
+ bool is_complex = false;
+ if (IsFloatingPointType(type, count, is_complex)) {
+ // TODO: handle complex and vector types
+ if (count != 1)
+ return false;
+
+ llvm::StringRef s_sref(s);
+ llvm::APFloat ap_float(getASTContext()->getFloatTypeSemantics(qual_type),
+ s_sref);
+
+ const uint64_t bit_size = getASTContext()->getTypeSize(qual_type);
+ const uint64_t byte_size = bit_size / 8;
+ if (dst_size >= byte_size) {
+ Scalar scalar = ap_float.bitcastToAPInt().zextOrTrunc(
+ llvm::NextPowerOf2(byte_size) * 8);
+ lldb_private::Status get_data_error;
+ if (scalar.GetAsMemoryData(dst, byte_size,
+ lldb_private::endian::InlHostByteOrder(),
+ get_data_error))
+ return byte_size;
+ }
+ }
+ }
+ return 0;
+}
+
+// Dumping types
+#define DEPTH_INCREMENT 2
+
+#ifndef NDEBUG
+LLVM_DUMP_METHOD void
+ClangASTContext::dump(lldb::opaque_compiler_type_t type) const {
+ if (!type)
+ return;
+ clang::QualType qual_type(GetQualType(type));
+ qual_type.dump();
+}
+#endif
+
+void ClangASTContext::Dump(Stream &s) {
+ Decl *tu = Decl::castFromDeclContext(GetTranslationUnitDecl());
+ tu->dump(s.AsRawOstream());
+}
+
+void ClangASTContext::DumpValue(
+ lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, Stream *s,
+ lldb::Format format, const DataExtractor &data,
+ lldb::offset_t data_byte_offset, size_t data_byte_size,
+ uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, bool show_types,
+ bool show_summary, bool verbose, uint32_t depth) {
+ if (!type)
+ return;
+
+ clang::QualType qual_type(GetQualType(type));
+ switch (qual_type->getTypeClass()) {
+ case clang::Type::Record:
+ if (GetCompleteType(type)) {
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+ assert(record_decl);
+ uint32_t field_bit_offset = 0;
+ uint32_t field_byte_offset = 0;
+ const clang::ASTRecordLayout &record_layout =
+ getASTContext()->getASTRecordLayout(record_decl);
+ uint32_t child_idx = 0;
+
+ const clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
+ if (cxx_record_decl) {
+ // We might have base classes to print out first
+ clang::CXXRecordDecl::base_class_const_iterator base_class,
+ base_class_end;
+ for (base_class = cxx_record_decl->bases_begin(),
+ base_class_end = cxx_record_decl->bases_end();
+ base_class != base_class_end; ++base_class) {
+ const clang::CXXRecordDecl *base_class_decl =
+ llvm::cast<clang::CXXRecordDecl>(
+ base_class->getType()->getAs<clang::RecordType>()->getDecl());
+
+ // Skip empty base classes
+ if (!verbose && !ClangASTContext::RecordHasFields(base_class_decl))
+ continue;
+
+ if (base_class->isVirtual())
+ field_bit_offset =
+ record_layout.getVBaseClassOffset(base_class_decl)
+ .getQuantity() *
+ 8;
+ else
+ field_bit_offset = record_layout.getBaseClassOffset(base_class_decl)
+ .getQuantity() *
+ 8;
+ field_byte_offset = field_bit_offset / 8;
+ assert(field_bit_offset % 8 == 0);
+ if (child_idx == 0)
+ s->PutChar('{');
+ else
+ s->PutChar(',');
+
+ clang::QualType base_class_qual_type = base_class->getType();
+ std::string base_class_type_name(base_class_qual_type.getAsString());
+
+ // Indent and print the base class type name
+ s->Format("\n{0}{1}", llvm::fmt_repeat(" ", depth + DEPTH_INCREMENT),
+ base_class_type_name);
+
+ clang::TypeInfo base_class_type_info =
+ getASTContext()->getTypeInfo(base_class_qual_type);
+
+ // Dump the value of the member
+ CompilerType base_clang_type(getASTContext(), base_class_qual_type);
+ base_clang_type.DumpValue(
+ exe_ctx,
+ s, // Stream to dump to
+ base_clang_type
+ .GetFormat(), // The format with which to display the member
+ data, // Data buffer containing all bytes for this type
+ data_byte_offset + field_byte_offset, // Offset into "data" where
+ // to grab value from
+ base_class_type_info.Width / 8, // Size of this type in bytes
+ 0, // Bitfield bit size
+ 0, // Bitfield bit offset
+ show_types, // Boolean indicating if we should show the variable
+ // types
+ show_summary, // Boolean indicating if we should show a summary
+ // for the current type
+ verbose, // Verbose output?
+ depth + DEPTH_INCREMENT); // Scope depth for any types that have
+ // children
+
+ ++child_idx;
+ }
+ }
+ uint32_t field_idx = 0;
+ clang::RecordDecl::field_iterator field, field_end;
+ for (field = record_decl->field_begin(),
+ field_end = record_decl->field_end();
+ field != field_end; ++field, ++field_idx, ++child_idx) {
+ // Print the starting squiggly bracket (if this is the first member) or
+ // comma (for member 2 and beyond) for the struct/union/class member.
+ if (child_idx == 0)
+ s->PutChar('{');
+ else
+ s->PutChar(',');
+
+ // Indent
+ s->Printf("\n%*s", depth + DEPTH_INCREMENT, "");
+
+ clang::QualType field_type = field->getType();
+ // Print the member type if requested
+ // Figure out the type byte size (field_type_info.first) and alignment
+ // (field_type_info.second) from the AST context.
+ clang::TypeInfo field_type_info =
+ getASTContext()->getTypeInfo(field_type);
+ assert(field_idx < record_layout.getFieldCount());
+ // Figure out the field offset within the current struct/union/class
+ // type
+ field_bit_offset = record_layout.getFieldOffset(field_idx);
+ field_byte_offset = field_bit_offset / 8;
+ uint32_t field_bitfield_bit_size = 0;
+ uint32_t field_bitfield_bit_offset = 0;
+ if (ClangASTContext::FieldIsBitfield(getASTContext(), *field,
+ field_bitfield_bit_size))
+ field_bitfield_bit_offset = field_bit_offset % 8;
+
+ if (show_types) {
+ std::string field_type_name(field_type.getAsString());
+ if (field_bitfield_bit_size > 0)
+ s->Printf("(%s:%u) ", field_type_name.c_str(),
+ field_bitfield_bit_size);
+ else
+ s->Printf("(%s) ", field_type_name.c_str());
+ }
+ // Print the member name and equal sign
+ s->Printf("%s = ", field->getNameAsString().c_str());
+
+ // Dump the value of the member
+ CompilerType field_clang_type(getASTContext(), field_type);
+ field_clang_type.DumpValue(
+ exe_ctx,
+ s, // Stream to dump to
+ field_clang_type
+ .GetFormat(), // The format with which to display the member
+ data, // Data buffer containing all bytes for this type
+ data_byte_offset + field_byte_offset, // Offset into "data" where to
+ // grab value from
+ field_type_info.Width / 8, // Size of this type in bytes
+ field_bitfield_bit_size, // Bitfield bit size
+ field_bitfield_bit_offset, // Bitfield bit offset
+ show_types, // Boolean indicating if we should show the variable
+ // types
+ show_summary, // Boolean indicating if we should show a summary for
+ // the current type
+ verbose, // Verbose output?
+ depth + DEPTH_INCREMENT); // Scope depth for any types that have
+ // children
+ }
+
+ // Indent the trailing squiggly bracket
+ if (child_idx > 0)
+ s->Printf("\n%*s}", depth, "");
+ }
+ return;
+
+ case clang::Type::Enum:
+ if (GetCompleteType(type)) {
+ const clang::EnumType *enutype =
+ llvm::cast<clang::EnumType>(qual_type.getTypePtr());
+ const clang::EnumDecl *enum_decl = enutype->getDecl();
+ assert(enum_decl);
+ clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos;
+ lldb::offset_t offset = data_byte_offset;
+ const int64_t enum_value = data.GetMaxU64Bitfield(
+ &offset, data_byte_size, bitfield_bit_size, bitfield_bit_offset);
+ for (enum_pos = enum_decl->enumerator_begin(),
+ enum_end_pos = enum_decl->enumerator_end();
+ enum_pos != enum_end_pos; ++enum_pos) {
+ if (enum_pos->getInitVal() == enum_value) {
+ s->Printf("%s", enum_pos->getNameAsString().c_str());
+ return;
+ }
+ }
+ // If we have gotten here we didn't get find the enumerator in the enum
+ // decl, so just print the integer.
+ s->Printf("%" PRIi64, enum_value);
+ }
+ return;
+
+ case clang::Type::ConstantArray: {
+ const clang::ConstantArrayType *array =
+ llvm::cast<clang::ConstantArrayType>(qual_type.getTypePtr());
+ bool is_array_of_characters = false;
+ clang::QualType element_qual_type = array->getElementType();
+
+ const clang::Type *canonical_type =
+ element_qual_type->getCanonicalTypeInternal().getTypePtr();
+ if (canonical_type)
+ is_array_of_characters = canonical_type->isCharType();
+
+ const uint64_t element_count = array->getSize().getLimitedValue();
+
+ clang::TypeInfo field_type_info =
+ getASTContext()->getTypeInfo(element_qual_type);
+
+ uint32_t element_idx = 0;
+ uint32_t element_offset = 0;
+ uint64_t element_byte_size = field_type_info.Width / 8;
+ uint32_t element_stride = element_byte_size;
+
+ if (is_array_of_characters) {
+ s->PutChar('"');
+ DumpDataExtractor(data, s, data_byte_offset, lldb::eFormatChar,
+ element_byte_size, element_count, UINT32_MAX,
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('"');
+ return;
+ } else {
+ CompilerType element_clang_type(getASTContext(), element_qual_type);
+ lldb::Format element_format = element_clang_type.GetFormat();
+
+ for (element_idx = 0; element_idx < element_count; ++element_idx) {
+ // Print the starting squiggly bracket (if this is the first member) or
+ // comman (for member 2 and beyong) for the struct/union/class member.
+ if (element_idx == 0)
+ s->PutChar('{');
+ else
+ s->PutChar(',');
+
+ // Indent and print the index
+ s->Printf("\n%*s[%u] ", depth + DEPTH_INCREMENT, "", element_idx);
+
+ // Figure out the field offset within the current struct/union/class
+ // type
+ element_offset = element_idx * element_stride;
+
+ // Dump the value of the member
+ element_clang_type.DumpValue(
+ exe_ctx,
+ s, // Stream to dump to
+ element_format, // The format with which to display the element
+ data, // Data buffer containing all bytes for this type
+ data_byte_offset +
+ element_offset, // Offset into "data" where to grab value from
+ element_byte_size, // Size of this type in bytes
+ 0, // Bitfield bit size
+ 0, // Bitfield bit offset
+ show_types, // Boolean indicating if we should show the variable
+ // types
+ show_summary, // Boolean indicating if we should show a summary for
+ // the current type
+ verbose, // Verbose output?
+ depth + DEPTH_INCREMENT); // Scope depth for any types that have
+ // children
+ }
+
+ // Indent the trailing squiggly bracket
+ if (element_idx > 0)
+ s->Printf("\n%*s}", depth, "");
+ }
+ }
+ return;
+
+ case clang::Type::Typedef: {
+ clang::QualType typedef_qual_type =
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType();
+
+ CompilerType typedef_clang_type(getASTContext(), typedef_qual_type);
+ lldb::Format typedef_format = typedef_clang_type.GetFormat();
+ clang::TypeInfo typedef_type_info =
+ getASTContext()->getTypeInfo(typedef_qual_type);
+ uint64_t typedef_byte_size = typedef_type_info.Width / 8;
+
+ return typedef_clang_type.DumpValue(
+ exe_ctx,
+ s, // Stream to dump to
+ typedef_format, // The format with which to display the element
+ data, // Data buffer containing all bytes for this type
+ data_byte_offset, // Offset into "data" where to grab value from
+ typedef_byte_size, // Size of this type in bytes
+ bitfield_bit_size, // Bitfield bit size
+ bitfield_bit_offset, // Bitfield bit offset
+ show_types, // Boolean indicating if we should show the variable types
+ show_summary, // Boolean indicating if we should show a summary for the
+ // current type
+ verbose, // Verbose output?
+ depth); // Scope depth for any types that have children
+ } break;
+
+ case clang::Type::Auto: {
+ clang::QualType elaborated_qual_type =
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType();
+ CompilerType elaborated_clang_type(getASTContext(), elaborated_qual_type);
+ lldb::Format elaborated_format = elaborated_clang_type.GetFormat();
+ clang::TypeInfo elaborated_type_info =
+ getASTContext()->getTypeInfo(elaborated_qual_type);
+ uint64_t elaborated_byte_size = elaborated_type_info.Width / 8;
+
+ return elaborated_clang_type.DumpValue(
+ exe_ctx,
+ s, // Stream to dump to
+ elaborated_format, // The format with which to display the element
+ data, // Data buffer containing all bytes for this type
+ data_byte_offset, // Offset into "data" where to grab value from
+ elaborated_byte_size, // Size of this type in bytes
+ bitfield_bit_size, // Bitfield bit size
+ bitfield_bit_offset, // Bitfield bit offset
+ show_types, // Boolean indicating if we should show the variable types
+ show_summary, // Boolean indicating if we should show a summary for the
+ // current type
+ verbose, // Verbose output?
+ depth); // Scope depth for any types that have children
+ } break;
+
+ case clang::Type::Elaborated: {
+ clang::QualType elaborated_qual_type =
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType();
+ CompilerType elaborated_clang_type(getASTContext(), elaborated_qual_type);
+ lldb::Format elaborated_format = elaborated_clang_type.GetFormat();
+ clang::TypeInfo elaborated_type_info =
+ getASTContext()->getTypeInfo(elaborated_qual_type);
+ uint64_t elaborated_byte_size = elaborated_type_info.Width / 8;
+
+ return elaborated_clang_type.DumpValue(
+ exe_ctx,
+ s, // Stream to dump to
+ elaborated_format, // The format with which to display the element
+ data, // Data buffer containing all bytes for this type
+ data_byte_offset, // Offset into "data" where to grab value from
+ elaborated_byte_size, // Size of this type in bytes
+ bitfield_bit_size, // Bitfield bit size
+ bitfield_bit_offset, // Bitfield bit offset
+ show_types, // Boolean indicating if we should show the variable types
+ show_summary, // Boolean indicating if we should show a summary for the
+ // current type
+ verbose, // Verbose output?
+ depth); // Scope depth for any types that have children
+ } break;
+
+ case clang::Type::Paren: {
+ clang::QualType desugar_qual_type =
+ llvm::cast<clang::ParenType>(qual_type)->desugar();
+ CompilerType desugar_clang_type(getASTContext(), desugar_qual_type);
+
+ lldb::Format desugar_format = desugar_clang_type.GetFormat();
+ clang::TypeInfo desugar_type_info =
+ getASTContext()->getTypeInfo(desugar_qual_type);
+ uint64_t desugar_byte_size = desugar_type_info.Width / 8;
+
+ return desugar_clang_type.DumpValue(
+ exe_ctx,
+ s, // Stream to dump to
+ desugar_format, // The format with which to display the element
+ data, // Data buffer containing all bytes for this type
+ data_byte_offset, // Offset into "data" where to grab value from
+ desugar_byte_size, // Size of this type in bytes
+ bitfield_bit_size, // Bitfield bit size
+ bitfield_bit_offset, // Bitfield bit offset
+ show_types, // Boolean indicating if we should show the variable types
+ show_summary, // Boolean indicating if we should show a summary for the
+ // current type
+ verbose, // Verbose output?
+ depth); // Scope depth for any types that have children
+ } break;
+
+ default:
+ // We are down to a scalar type that we just need to display.
+ DumpDataExtractor(data, s, data_byte_offset, format, data_byte_size, 1,
+ UINT32_MAX, LLDB_INVALID_ADDRESS, bitfield_bit_size,
+ bitfield_bit_offset);
+
+ if (show_summary)
+ DumpSummary(type, exe_ctx, s, data, data_byte_offset, data_byte_size);
+ break;
+ }
+}
+
+bool ClangASTContext::DumpTypeValue(
+ lldb::opaque_compiler_type_t type, Stream *s, lldb::Format format,
+ const DataExtractor &data, lldb::offset_t byte_offset, size_t byte_size,
+ uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset,
+ ExecutionContextScope *exe_scope) {
+ if (!type)
+ return false;
+ if (IsAggregateType(type)) {
+ return false;
+ } else {
+ clang::QualType qual_type(GetQualType(type));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Typedef: {
+ clang::QualType typedef_qual_type =
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType();
+ CompilerType typedef_clang_type(getASTContext(), typedef_qual_type);
+ if (format == eFormatDefault)
+ format = typedef_clang_type.GetFormat();
+ clang::TypeInfo typedef_type_info =
+ getASTContext()->getTypeInfo(typedef_qual_type);
+ uint64_t typedef_byte_size = typedef_type_info.Width / 8;
+
+ return typedef_clang_type.DumpTypeValue(
+ s,
+ format, // The format with which to display the element
+ data, // Data buffer containing all bytes for this type
+ byte_offset, // Offset into "data" where to grab value from
+ typedef_byte_size, // Size of this type in bytes
+ bitfield_bit_size, // Size in bits of a bitfield value, if zero don't
+ // treat as a bitfield
+ bitfield_bit_offset, // Offset in bits of a bitfield value if
+ // bitfield_bit_size != 0
+ exe_scope);
+ } break;
+
+ case clang::Type::Enum:
+ // If our format is enum or default, show the enumeration value as its
+ // enumeration string value, else just display it as requested.
+ if ((format == eFormatEnum || format == eFormatDefault) &&
+ GetCompleteType(type)) {
+ const clang::EnumType *enutype =
+ llvm::cast<clang::EnumType>(qual_type.getTypePtr());
+ const clang::EnumDecl *enum_decl = enutype->getDecl();
+ assert(enum_decl);
+ clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos;
+ const bool is_signed = qual_type->isSignedIntegerOrEnumerationType();
+ lldb::offset_t offset = byte_offset;
+ if (is_signed) {
+ const int64_t enum_svalue = data.GetMaxS64Bitfield(
+ &offset, byte_size, bitfield_bit_size, bitfield_bit_offset);
+ for (enum_pos = enum_decl->enumerator_begin(),
+ enum_end_pos = enum_decl->enumerator_end();
+ enum_pos != enum_end_pos; ++enum_pos) {
+ if (enum_pos->getInitVal().getSExtValue() == enum_svalue) {
+ s->PutCString(enum_pos->getNameAsString());
+ return true;
+ }
+ }
+ // If we have gotten here we didn't get find the enumerator in the
+ // enum decl, so just print the integer.
+ s->Printf("%" PRIi64, enum_svalue);
+ } else {
+ const uint64_t enum_uvalue = data.GetMaxU64Bitfield(
+ &offset, byte_size, bitfield_bit_size, bitfield_bit_offset);
+ for (enum_pos = enum_decl->enumerator_begin(),
+ enum_end_pos = enum_decl->enumerator_end();
+ enum_pos != enum_end_pos; ++enum_pos) {
+ if (enum_pos->getInitVal().getZExtValue() == enum_uvalue) {
+ s->PutCString(enum_pos->getNameAsString());
+ return true;
+ }
+ }
+ // If we have gotten here we didn't get find the enumerator in the
+ // enum decl, so just print the integer.
+ s->Printf("%" PRIu64, enum_uvalue);
+ }
+ return true;
+ }
+ // format was not enum, just fall through and dump the value as
+ // requested....
+ LLVM_FALLTHROUGH;
+
+ default:
+ // We are down to a scalar type that we just need to display.
+ {
+ uint32_t item_count = 1;
+ // A few formats, we might need to modify our size and count for
+ // depending
+ // on how we are trying to display the value...
+ switch (format) {
+ default:
+ case eFormatBoolean:
+ case eFormatBinary:
+ case eFormatComplex:
+ case eFormatCString: // NULL terminated C strings
+ case eFormatDecimal:
+ case eFormatEnum:
+ case eFormatHex:
+ case eFormatHexUppercase:
+ case eFormatFloat:
+ case eFormatOctal:
+ case eFormatOSType:
+ case eFormatUnsigned:
+ case eFormatPointer:
+ case eFormatVectorOfChar:
+ case eFormatVectorOfSInt8:
+ case eFormatVectorOfUInt8:
+ case eFormatVectorOfSInt16:
+ case eFormatVectorOfUInt16:
+ case eFormatVectorOfSInt32:
+ case eFormatVectorOfUInt32:
+ case eFormatVectorOfSInt64:
+ case eFormatVectorOfUInt64:
+ case eFormatVectorOfFloat32:
+ case eFormatVectorOfFloat64:
+ case eFormatVectorOfUInt128:
+ break;
+
+ case eFormatChar:
+ case eFormatCharPrintable:
+ case eFormatCharArray:
+ case eFormatBytes:
+ case eFormatBytesWithASCII:
+ item_count = byte_size;
+ byte_size = 1;
+ break;
+
+ case eFormatUnicode16:
+ item_count = byte_size / 2;
+ byte_size = 2;
+ break;
+
+ case eFormatUnicode32:
+ item_count = byte_size / 4;
+ byte_size = 4;
+ break;
+ }
+ return DumpDataExtractor(data, s, byte_offset, format, byte_size,
+ item_count, UINT32_MAX, LLDB_INVALID_ADDRESS,
+ bitfield_bit_size, bitfield_bit_offset,
+ exe_scope);
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+void ClangASTContext::DumpSummary(lldb::opaque_compiler_type_t type,
+ ExecutionContext *exe_ctx, Stream *s,
+ const lldb_private::DataExtractor &data,
+ lldb::offset_t data_byte_offset,
+ size_t data_byte_size) {
+ uint32_t length = 0;
+ if (IsCStringType(type, length)) {
+ if (exe_ctx) {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process) {
+ lldb::offset_t offset = data_byte_offset;
+ lldb::addr_t pointer_address = data.GetMaxU64(&offset, data_byte_size);
+ std::vector<uint8_t> buf;
+ if (length > 0)
+ buf.resize(length);
+ else
+ buf.resize(256);
+
+ DataExtractor cstr_data(&buf.front(), buf.size(),
+ process->GetByteOrder(), 4);
+ buf.back() = '\0';
+ size_t bytes_read;
+ size_t total_cstr_len = 0;
+ Status error;
+ while ((bytes_read = process->ReadMemory(pointer_address, &buf.front(),
+ buf.size(), error)) > 0) {
+ const size_t len = strlen((const char *)&buf.front());
+ if (len == 0)
+ break;
+ if (total_cstr_len == 0)
+ s->PutCString(" \"");
+ DumpDataExtractor(cstr_data, s, 0, lldb::eFormatChar, 1, len,
+ UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0);
+ total_cstr_len += len;
+ if (len < buf.size())
+ break;
+ pointer_address += total_cstr_len;
+ }
+ if (total_cstr_len > 0)
+ s->PutChar('"');
+ }
+ }
+ }
+}
+
+void ClangASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type) {
+ StreamFile s(stdout, false);
+ DumpTypeDescription(type, &s);
+ ClangASTMetadata *metadata =
+ ClangASTContext::GetMetadata(getASTContext(), type);
+ if (metadata) {
+ metadata->Dump(&s);
+ }
+}
+
+void ClangASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type,
+ Stream *s) {
+ if (type) {
+ clang::QualType qual_type(GetQualType(type));
+
+ llvm::SmallVector<char, 1024> buf;
+ llvm::raw_svector_ostream llvm_ostrm(buf);
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface: {
+ GetCompleteType(type);
+
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr());
+ assert(objc_class_type);
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+ if (class_interface_decl) {
+ clang::PrintingPolicy policy = getASTContext()->getPrintingPolicy();
+ class_interface_decl->print(llvm_ostrm, policy, s->GetIndentLevel());
+ }
+ }
+ } break;
+
+ case clang::Type::Typedef: {
+ const clang::TypedefType *typedef_type =
+ qual_type->getAs<clang::TypedefType>();
+ if (typedef_type) {
+ const clang::TypedefNameDecl *typedef_decl = typedef_type->getDecl();
+ std::string clang_typedef_name(
+ typedef_decl->getQualifiedNameAsString());
+ if (!clang_typedef_name.empty()) {
+ s->PutCString("typedef ");
+ s->PutCString(clang_typedef_name);
+ }
+ }
+ } break;
+
+ case clang::Type::Auto:
+ CompilerType(getASTContext(),
+ llvm::cast<clang::AutoType>(qual_type)->getDeducedType())
+ .DumpTypeDescription(s);
+ return;
+
+ case clang::Type::Elaborated:
+ CompilerType(getASTContext(),
+ llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType())
+ .DumpTypeDescription(s);
+ return;
+
+ case clang::Type::Paren:
+ CompilerType(getASTContext(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar())
+ .DumpTypeDescription(s);
+ return;
+
+ case clang::Type::Record: {
+ GetCompleteType(type);
+
+ const clang::RecordType *record_type =
+ llvm::cast<clang::RecordType>(qual_type.getTypePtr());
+ const clang::RecordDecl *record_decl = record_type->getDecl();
+ const clang::CXXRecordDecl *cxx_record_decl =
+ llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
+
+ if (cxx_record_decl)
+ cxx_record_decl->print(llvm_ostrm, getASTContext()->getPrintingPolicy(),
+ s->GetIndentLevel());
+ else
+ record_decl->print(llvm_ostrm, getASTContext()->getPrintingPolicy(),
+ s->GetIndentLevel());
+ } break;
+
+ default: {
+ const clang::TagType *tag_type =
+ llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr());
+ if (tag_type) {
+ clang::TagDecl *tag_decl = tag_type->getDecl();
+ if (tag_decl)
+ tag_decl->print(llvm_ostrm, 0);
+ } else {
+ std::string clang_type_name(qual_type.getAsString());
+ if (!clang_type_name.empty())
+ s->PutCString(clang_type_name);
+ }
+ }
+ }
+
+ if (buf.size() > 0) {
+ s->Write(buf.data(), buf.size());
+ }
+ }
+}
+
+void ClangASTContext::DumpTypeName(const CompilerType &type) {
+ if (ClangUtil::IsClangType(type)) {
+ clang::QualType qual_type(
+ ClangUtil::GetCanonicalQualType(ClangUtil::RemoveFastQualifiers(type)));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record: {
+ const clang::CXXRecordDecl *cxx_record_decl =
+ qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl)
+ printf("class %s", cxx_record_decl->getName().str().c_str());
+ } break;
+
+ case clang::Type::Enum: {
+ clang::EnumDecl *enum_decl =
+ llvm::cast<clang::EnumType>(qual_type)->getDecl();
+ if (enum_decl) {
+ printf("enum %s", enum_decl->getName().str().c_str());
+ }
+ } break;
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface: {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type);
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+ // We currently can't complete objective C types through the newly
+ // added ASTContext because it only supports TagDecl objects right
+ // now...
+ if (class_interface_decl)
+ printf("@class %s", class_interface_decl->getName().str().c_str());
+ }
+ } break;
+
+ case clang::Type::Typedef:
+ printf("typedef %s", llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getName()
+ .str()
+ .c_str());
+ break;
+
+ case clang::Type::Auto:
+ printf("auto ");
+ return DumpTypeName(CompilerType(type.GetTypeSystem(),
+ llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr()));
+
+ case clang::Type::Elaborated:
+ printf("elaborated ");
+ return DumpTypeName(CompilerType(
+ type.GetTypeSystem(), llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr()));
+
+ case clang::Type::Paren:
+ printf("paren ");
+ return DumpTypeName(CompilerType(
+ type.GetTypeSystem(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr()));
+
+ default:
+ printf("ClangASTContext::DumpTypeName() type_class = %u", type_class);
+ break;
+ }
+ }
+}
+
+clang::ClassTemplateDecl *ClangASTContext::ParseClassTemplateDecl(
+ clang::DeclContext *decl_ctx, lldb::AccessType access_type,
+ const char *parent_name, int tag_decl_kind,
+ const ClangASTContext::TemplateParameterInfos &template_param_infos) {
+ if (template_param_infos.IsValid()) {
+ std::string template_basename(parent_name);
+ template_basename.erase(template_basename.find('<'));
+
+ return CreateClassTemplateDecl(decl_ctx, access_type,
+ template_basename.c_str(), tag_decl_kind,
+ template_param_infos);
+ }
+ return nullptr;
+}
+
+void ClangASTContext::CompleteTagDecl(void *baton, clang::TagDecl *decl) {
+ ClangASTContext *ast = (ClangASTContext *)baton;
+ SymbolFile *sym_file = ast->GetSymbolFile();
+ if (sym_file) {
+ CompilerType clang_type = GetTypeForDecl(decl);
+ if (clang_type)
+ sym_file->CompleteType(clang_type);
+ }
+}
+
+void ClangASTContext::CompleteObjCInterfaceDecl(
+ void *baton, clang::ObjCInterfaceDecl *decl) {
+ ClangASTContext *ast = (ClangASTContext *)baton;
+ SymbolFile *sym_file = ast->GetSymbolFile();
+ if (sym_file) {
+ CompilerType clang_type = GetTypeForDecl(decl);
+ if (clang_type)
+ sym_file->CompleteType(clang_type);
+ }
+}
+
+DWARFASTParser *ClangASTContext::GetDWARFParser() {
+ if (!m_dwarf_ast_parser_up)
+ m_dwarf_ast_parser_up.reset(new DWARFASTParserClang(*this));
+ return m_dwarf_ast_parser_up.get();
+}
+
+#ifdef LLDB_ENABLE_ALL
+PDBASTParser *ClangASTContext::GetPDBParser() {
+ if (!m_pdb_ast_parser_up)
+ m_pdb_ast_parser_up.reset(new PDBASTParser(*this));
+ return m_pdb_ast_parser_up.get();
+}
+#endif // LLDB_ENABLE_ALL
+
+bool ClangASTContext::LayoutRecordType(
+ void *baton, const clang::RecordDecl *record_decl, uint64_t &bit_size,
+ uint64_t &alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &base_offsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &vbase_offsets) {
+ ClangASTContext *ast = (ClangASTContext *)baton;
+ lldb_private::ClangASTImporter *importer = nullptr;
+ if (ast->m_dwarf_ast_parser_up)
+ importer = &ast->m_dwarf_ast_parser_up->GetClangASTImporter();
+#ifdef LLDB_ENABLE_ALL
+ if (!importer && ast->m_pdb_ast_parser_up)
+ importer = &ast->m_pdb_ast_parser_up->GetClangASTImporter();
+#endif // LLDB_ENABLE_ALL
+ if (!importer)
+ return false;
+
+ return importer->LayoutRecordType(record_decl, bit_size, alignment,
+ field_offsets, base_offsets, vbase_offsets);
+}
+
+// CompilerDecl override functions
+
+ConstString ClangASTContext::DeclGetName(void *opaque_decl) {
+ if (opaque_decl) {
+ clang::NamedDecl *nd =
+ llvm::dyn_cast<NamedDecl>((clang::Decl *)opaque_decl);
+ if (nd != nullptr)
+ return ConstString(nd->getDeclName().getAsString());
+ }
+ return ConstString();
+}
+
+ConstString ClangASTContext::DeclGetMangledName(void *opaque_decl) {
+ if (opaque_decl) {
+ clang::NamedDecl *nd =
+ llvm::dyn_cast<clang::NamedDecl>((clang::Decl *)opaque_decl);
+ if (nd != nullptr && !llvm::isa<clang::ObjCMethodDecl>(nd)) {
+ clang::MangleContext *mc = getMangleContext();
+ if (mc && mc->shouldMangleCXXName(nd)) {
+ llvm::SmallVector<char, 1024> buf;
+ llvm::raw_svector_ostream llvm_ostrm(buf);
+ if (llvm::isa<clang::CXXConstructorDecl>(nd)) {
+ mc->mangleCXXCtor(llvm::dyn_cast<clang::CXXConstructorDecl>(nd),
+ Ctor_Complete, llvm_ostrm);
+ } else if (llvm::isa<clang::CXXDestructorDecl>(nd)) {
+ mc->mangleCXXDtor(llvm::dyn_cast<clang::CXXDestructorDecl>(nd),
+ Dtor_Complete, llvm_ostrm);
+ } else {
+ mc->mangleName(nd, llvm_ostrm);
+ }
+ if (buf.size() > 0)
+ return ConstString(buf.data(), buf.size());
+ }
+ }
+ }
+ return ConstString();
+}
+
+CompilerDeclContext ClangASTContext::DeclGetDeclContext(void *opaque_decl) {
+ if (opaque_decl)
+ return CompilerDeclContext(this,
+ ((clang::Decl *)opaque_decl)->getDeclContext());
+ else
+ return CompilerDeclContext();
+}
+
+CompilerType ClangASTContext::DeclGetFunctionReturnType(void *opaque_decl) {
+ if (clang::FunctionDecl *func_decl =
+ llvm::dyn_cast<clang::FunctionDecl>((clang::Decl *)opaque_decl))
+ return CompilerType(this, func_decl->getReturnType().getAsOpaquePtr());
+ if (clang::ObjCMethodDecl *objc_method =
+ llvm::dyn_cast<clang::ObjCMethodDecl>((clang::Decl *)opaque_decl))
+ return CompilerType(this, objc_method->getReturnType().getAsOpaquePtr());
+ else
+ return CompilerType();
+}
+
+size_t ClangASTContext::DeclGetFunctionNumArguments(void *opaque_decl) {
+ if (clang::FunctionDecl *func_decl =
+ llvm::dyn_cast<clang::FunctionDecl>((clang::Decl *)opaque_decl))
+ return func_decl->param_size();
+ if (clang::ObjCMethodDecl *objc_method =
+ llvm::dyn_cast<clang::ObjCMethodDecl>((clang::Decl *)opaque_decl))
+ return objc_method->param_size();
+ else
+ return 0;
+}
+
+CompilerType ClangASTContext::DeclGetFunctionArgumentType(void *opaque_decl,
+ size_t idx) {
+ if (clang::FunctionDecl *func_decl =
+ llvm::dyn_cast<clang::FunctionDecl>((clang::Decl *)opaque_decl)) {
+ if (idx < func_decl->param_size()) {
+ ParmVarDecl *var_decl = func_decl->getParamDecl(idx);
+ if (var_decl)
+ return CompilerType(this, var_decl->getOriginalType().getAsOpaquePtr());
+ }
+ } else if (clang::ObjCMethodDecl *objc_method =
+ llvm::dyn_cast<clang::ObjCMethodDecl>(
+ (clang::Decl *)opaque_decl)) {
+ if (idx < objc_method->param_size())
+ return CompilerType(
+ this,
+ objc_method->parameters()[idx]->getOriginalType().getAsOpaquePtr());
+ }
+ return CompilerType();
+}
+
+// CompilerDeclContext functions
+
+std::vector<CompilerDecl> ClangASTContext::DeclContextFindDeclByName(
+ void *opaque_decl_ctx, ConstString name, const bool ignore_using_decls) {
+ std::vector<CompilerDecl> found_decls;
+ if (opaque_decl_ctx) {
+ DeclContext *root_decl_ctx = (DeclContext *)opaque_decl_ctx;
+ std::set<DeclContext *> searched;
+ std::multimap<DeclContext *, DeclContext *> search_queue;
+ SymbolFile *symbol_file = GetSymbolFile();
+
+ for (clang::DeclContext *decl_context = root_decl_ctx;
+ decl_context != nullptr && found_decls.empty();
+ decl_context = decl_context->getParent()) {
+ search_queue.insert(std::make_pair(decl_context, decl_context));
+
+ for (auto it = search_queue.find(decl_context); it != search_queue.end();
+ it++) {
+ if (!searched.insert(it->second).second)
+ continue;
+ symbol_file->ParseDeclsForContext(
+ CompilerDeclContext(this, it->second));
+
+ for (clang::Decl *child : it->second->decls()) {
+ if (clang::UsingDirectiveDecl *ud =
+ llvm::dyn_cast<clang::UsingDirectiveDecl>(child)) {
+ if (ignore_using_decls)
+ continue;
+ clang::DeclContext *from = ud->getCommonAncestor();
+ if (searched.find(ud->getNominatedNamespace()) == searched.end())
+ search_queue.insert(
+ std::make_pair(from, ud->getNominatedNamespace()));
+ } else if (clang::UsingDecl *ud =
+ llvm::dyn_cast<clang::UsingDecl>(child)) {
+ if (ignore_using_decls)
+ continue;
+ for (clang::UsingShadowDecl *usd : ud->shadows()) {
+ clang::Decl *target = usd->getTargetDecl();
+ if (clang::NamedDecl *nd =
+ llvm::dyn_cast<clang::NamedDecl>(target)) {
+ IdentifierInfo *ii = nd->getIdentifier();
+ if (ii != nullptr &&
+ ii->getName().equals(name.AsCString(nullptr)))
+ found_decls.push_back(CompilerDecl(this, nd));
+ }
+ }
+ } else if (clang::NamedDecl *nd =
+ llvm::dyn_cast<clang::NamedDecl>(child)) {
+ IdentifierInfo *ii = nd->getIdentifier();
+ if (ii != nullptr && ii->getName().equals(name.AsCString(nullptr)))
+ found_decls.push_back(CompilerDecl(this, nd));
+ }
+ }
+ }
+ }
+ }
+ return found_decls;
+}
+
+// Look for child_decl_ctx's lookup scope in frame_decl_ctx and its parents,
+// and return the number of levels it took to find it, or
+// LLDB_INVALID_DECL_LEVEL if not found. If the decl was imported via a using
+// declaration, its name and/or type, if set, will be used to check that the
+// decl found in the scope is a match.
+//
+// The optional name is required by languages (like C++) to handle using
+// declarations like:
+//
+// void poo();
+// namespace ns {
+// void foo();
+// void goo();
+// }
+// void bar() {
+// using ns::foo;
+// // CountDeclLevels returns 0 for 'foo', 1 for 'poo', and
+// // LLDB_INVALID_DECL_LEVEL for 'goo'.
+// }
+//
+// The optional type is useful in the case that there's a specific overload
+// that we're looking for that might otherwise be shadowed, like:
+//
+// void foo(int);
+// namespace ns {
+// void foo();
+// }
+// void bar() {
+// using ns::foo;
+// // CountDeclLevels returns 0 for { 'foo', void() },
+// // 1 for { 'foo', void(int) }, and
+// // LLDB_INVALID_DECL_LEVEL for { 'foo', void(int, int) }.
+// }
+//
+// NOTE: Because file statics are at the TranslationUnit along with globals, a
+// function at file scope will return the same level as a function at global
+// scope. Ideally we'd like to treat the file scope as an additional scope just
+// below the global scope. More work needs to be done to recognise that, if
+// the decl we're trying to look up is static, we should compare its source
+// file with that of the current scope and return a lower number for it.
+uint32_t ClangASTContext::CountDeclLevels(clang::DeclContext *frame_decl_ctx,
+ clang::DeclContext *child_decl_ctx,
+ ConstString *child_name,
+ CompilerType *child_type) {
+ if (frame_decl_ctx) {
+ std::set<DeclContext *> searched;
+ std::multimap<DeclContext *, DeclContext *> search_queue;
+ SymbolFile *symbol_file = GetSymbolFile();
+
+ // Get the lookup scope for the decl we're trying to find.
+ clang::DeclContext *parent_decl_ctx = child_decl_ctx->getParent();
+
+ // Look for it in our scope's decl context and its parents.
+ uint32_t level = 0;
+ for (clang::DeclContext *decl_ctx = frame_decl_ctx; decl_ctx != nullptr;
+ decl_ctx = decl_ctx->getParent()) {
+ if (!decl_ctx->isLookupContext())
+ continue;
+ if (decl_ctx == parent_decl_ctx)
+ // Found it!
+ return level;
+ search_queue.insert(std::make_pair(decl_ctx, decl_ctx));
+ for (auto it = search_queue.find(decl_ctx); it != search_queue.end();
+ it++) {
+ if (searched.find(it->second) != searched.end())
+ continue;
+
+ // Currently DWARF has one shared translation unit for all Decls at top
+ // level, so this would erroneously find using statements anywhere. So
+ // don't look at the top-level translation unit.
+ // TODO fix this and add a testcase that depends on it.
+
+ if (llvm::isa<clang::TranslationUnitDecl>(it->second))
+ continue;
+
+ searched.insert(it->second);
+ symbol_file->ParseDeclsForContext(
+ CompilerDeclContext(this, it->second));
+
+ for (clang::Decl *child : it->second->decls()) {
+ if (clang::UsingDirectiveDecl *ud =
+ llvm::dyn_cast<clang::UsingDirectiveDecl>(child)) {
+ clang::DeclContext *ns = ud->getNominatedNamespace();
+ if (ns == parent_decl_ctx)
+ // Found it!
+ return level;
+ clang::DeclContext *from = ud->getCommonAncestor();
+ if (searched.find(ns) == searched.end())
+ search_queue.insert(std::make_pair(from, ns));
+ } else if (child_name) {
+ if (clang::UsingDecl *ud =
+ llvm::dyn_cast<clang::UsingDecl>(child)) {
+ for (clang::UsingShadowDecl *usd : ud->shadows()) {
+ clang::Decl *target = usd->getTargetDecl();
+ clang::NamedDecl *nd = llvm::dyn_cast<clang::NamedDecl>(target);
+ if (!nd)
+ continue;
+ // Check names.
+ IdentifierInfo *ii = nd->getIdentifier();
+ if (ii == nullptr ||
+ !ii->getName().equals(child_name->AsCString(nullptr)))
+ continue;
+ // Check types, if one was provided.
+ if (child_type) {
+ CompilerType clang_type = ClangASTContext::GetTypeForDecl(nd);
+ if (!AreTypesSame(clang_type, *child_type,
+ /*ignore_qualifiers=*/true))
+ continue;
+ }
+ // Found it!
+ return level;
+ }
+ }
+ }
+ }
+ }
+ ++level;
+ }
+ }
+ return LLDB_INVALID_DECL_LEVEL;
+}
+
+bool ClangASTContext::DeclContextIsStructUnionOrClass(void *opaque_decl_ctx) {
+ if (opaque_decl_ctx)
+ return ((clang::DeclContext *)opaque_decl_ctx)->isRecord();
+ else
+ return false;
+}
+
+ConstString ClangASTContext::DeclContextGetName(void *opaque_decl_ctx) {
+ if (opaque_decl_ctx) {
+ clang::NamedDecl *named_decl =
+ llvm::dyn_cast<clang::NamedDecl>((clang::DeclContext *)opaque_decl_ctx);
+ if (named_decl)
+ return ConstString(named_decl->getName());
+ }
+ return ConstString();
+}
+
+ConstString
+ClangASTContext::DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) {
+ if (opaque_decl_ctx) {
+ clang::NamedDecl *named_decl =
+ llvm::dyn_cast<clang::NamedDecl>((clang::DeclContext *)opaque_decl_ctx);
+ if (named_decl)
+ return ConstString(
+ llvm::StringRef(named_decl->getQualifiedNameAsString()));
+ }
+ return ConstString();
+}
+
+bool ClangASTContext::DeclContextIsClassMethod(
+ void *opaque_decl_ctx, lldb::LanguageType *language_ptr,
+ bool *is_instance_method_ptr, ConstString *language_object_name_ptr) {
+ if (opaque_decl_ctx) {
+ clang::DeclContext *decl_ctx = (clang::DeclContext *)opaque_decl_ctx;
+ if (ObjCMethodDecl *objc_method =
+ llvm::dyn_cast<clang::ObjCMethodDecl>(decl_ctx)) {
+ if (is_instance_method_ptr)
+ *is_instance_method_ptr = objc_method->isInstanceMethod();
+ if (language_ptr)
+ *language_ptr = eLanguageTypeObjC;
+ if (language_object_name_ptr)
+ language_object_name_ptr->SetCString("self");
+ return true;
+ } else if (CXXMethodDecl *cxx_method =
+ llvm::dyn_cast<clang::CXXMethodDecl>(decl_ctx)) {
+ if (is_instance_method_ptr)
+ *is_instance_method_ptr = cxx_method->isInstance();
+ if (language_ptr)
+ *language_ptr = eLanguageTypeC_plus_plus;
+ if (language_object_name_ptr)
+ language_object_name_ptr->SetCString("this");
+ return true;
+ } else if (clang::FunctionDecl *function_decl =
+ llvm::dyn_cast<clang::FunctionDecl>(decl_ctx)) {
+ ClangASTMetadata *metadata =
+ GetMetadata(&decl_ctx->getParentASTContext(), function_decl);
+ if (metadata && metadata->HasObjectPtr()) {
+ if (is_instance_method_ptr)
+ *is_instance_method_ptr = true;
+ if (language_ptr)
+ *language_ptr = eLanguageTypeObjC;
+ if (language_object_name_ptr)
+ language_object_name_ptr->SetCString(metadata->GetObjectPtrName());
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool ClangASTContext::DeclContextIsContainedInLookup(
+ void *opaque_decl_ctx, void *other_opaque_decl_ctx) {
+ auto *decl_ctx = (clang::DeclContext *)opaque_decl_ctx;
+ auto *other = (clang::DeclContext *)other_opaque_decl_ctx;
+
+ do {
+ // A decl context always includes its own contents in its lookup.
+ if (decl_ctx == other)
+ return true;
+
+ // If we have an inline namespace, then the lookup of the parent context
+ // also includes the inline namespace contents.
+ } while (other->isInlineNamespace() && (other = other->getParent()));
+
+ return false;
+}
+
+clang::DeclContext *
+ClangASTContext::DeclContextGetAsDeclContext(const CompilerDeclContext &dc) {
+ if (dc.IsClang())
+ return (clang::DeclContext *)dc.GetOpaqueDeclContext();
+ return nullptr;
+}
+
+ObjCMethodDecl *
+ClangASTContext::DeclContextGetAsObjCMethodDecl(const CompilerDeclContext &dc) {
+ if (dc.IsClang())
+ return llvm::dyn_cast<clang::ObjCMethodDecl>(
+ (clang::DeclContext *)dc.GetOpaqueDeclContext());
+ return nullptr;
+}
+
+CXXMethodDecl *
+ClangASTContext::DeclContextGetAsCXXMethodDecl(const CompilerDeclContext &dc) {
+ if (dc.IsClang())
+ return llvm::dyn_cast<clang::CXXMethodDecl>(
+ (clang::DeclContext *)dc.GetOpaqueDeclContext());
+ return nullptr;
+}
+
+clang::FunctionDecl *
+ClangASTContext::DeclContextGetAsFunctionDecl(const CompilerDeclContext &dc) {
+ if (dc.IsClang())
+ return llvm::dyn_cast<clang::FunctionDecl>(
+ (clang::DeclContext *)dc.GetOpaqueDeclContext());
+ return nullptr;
+}
+
+clang::NamespaceDecl *
+ClangASTContext::DeclContextGetAsNamespaceDecl(const CompilerDeclContext &dc) {
+ if (dc.IsClang())
+ return llvm::dyn_cast<clang::NamespaceDecl>(
+ (clang::DeclContext *)dc.GetOpaqueDeclContext());
+ return nullptr;
+}
+
+ClangASTMetadata *
+ClangASTContext::DeclContextGetMetaData(const CompilerDeclContext &dc,
+ const void *object) {
+ clang::ASTContext *ast = DeclContextGetClangASTContext(dc);
+ if (ast)
+ return ClangASTContext::GetMetadata(ast, object);
+ return nullptr;
+}
+
+clang::ASTContext *
+ClangASTContext::DeclContextGetClangASTContext(const CompilerDeclContext &dc) {
+ ClangASTContext *ast =
+ llvm::dyn_cast_or_null<ClangASTContext>(dc.GetTypeSystem());
+ if (ast)
+ return ast->getASTContext();
+ return nullptr;
+}
+
+ClangASTContextForExpressions::ClangASTContextForExpressions(Target &target)
+ : ClangASTContext(target.GetArchitecture().GetTriple().getTriple().c_str()),
+ m_target_wp(target.shared_from_this()),
+ m_persistent_variables(new ClangPersistentVariables) {}
+
+UserExpression *ClangASTContextForExpressions::GetUserExpression(
+ llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language,
+ Expression::ResultType desired_type,
+ const EvaluateExpressionOptions &options,
+ ValueObject *ctx_obj) {
+ TargetSP target_sp = m_target_wp.lock();
+ if (!target_sp)
+ return nullptr;
+
+ return new ClangUserExpression(*target_sp.get(), expr, prefix, language,
+ desired_type, options, ctx_obj);
+}
+
+FunctionCaller *ClangASTContextForExpressions::GetFunctionCaller(
+ const CompilerType &return_type, const Address &function_address,
+ const ValueList &arg_value_list, const char *name) {
+ TargetSP target_sp = m_target_wp.lock();
+ if (!target_sp)
+ return nullptr;
+
+ Process *process = target_sp->GetProcessSP().get();
+ if (!process)
+ return nullptr;
+
+ return new ClangFunctionCaller(*process, return_type, function_address,
+ arg_value_list, name);
+}
+
+UtilityFunction *
+ClangASTContextForExpressions::GetUtilityFunction(const char *text,
+ const char *name) {
+ TargetSP target_sp = m_target_wp.lock();
+ if (!target_sp)
+ return nullptr;
+
+ return new ClangUtilityFunction(*target_sp.get(), text, name);
+}
+
+PersistentExpressionState *
+ClangASTContextForExpressions::GetPersistentExpressionState() {
+ return m_persistent_variables.get();
+}
+
+clang::ExternalASTMerger &
+ClangASTContextForExpressions::GetMergerUnchecked() {
+ lldbassert(m_scratch_ast_source_up != nullptr);
+ return m_scratch_ast_source_up->GetMergerUnchecked();
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/ClangASTImporter.cpp b/contrib/llvm-project/lldb/source/Symbol/ClangASTImporter.cpp
new file mode 100644
index 000000000000..32d0c47693b0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/ClangASTImporter.cpp
@@ -0,0 +1,1205 @@
+//===-- ClangASTImporter.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <memory>
+
+using namespace lldb_private;
+using namespace clang;
+
+ClangASTMetrics::Counters ClangASTMetrics::global_counters = {0, 0, 0, 0, 0, 0};
+ClangASTMetrics::Counters ClangASTMetrics::local_counters = {0, 0, 0, 0, 0, 0};
+
+void ClangASTMetrics::DumpCounters(Log *log,
+ ClangASTMetrics::Counters &counters) {
+ log->Printf(" Number of visible Decl queries by name : %" PRIu64,
+ counters.m_visible_query_count);
+ log->Printf(" Number of lexical Decl queries : %" PRIu64,
+ counters.m_lexical_query_count);
+ log->Printf(" Number of imports initiated by LLDB : %" PRIu64,
+ counters.m_lldb_import_count);
+ log->Printf(" Number of imports conducted by Clang : %" PRIu64,
+ counters.m_clang_import_count);
+ log->Printf(" Number of Decls completed : %" PRIu64,
+ counters.m_decls_completed_count);
+ log->Printf(" Number of records laid out : %" PRIu64,
+ counters.m_record_layout_count);
+}
+
+void ClangASTMetrics::DumpCounters(Log *log) {
+ if (!log)
+ return;
+
+ log->Printf("== ClangASTMetrics output ==");
+ log->Printf("-- Global metrics --");
+ DumpCounters(log, global_counters);
+ log->Printf("-- Local metrics --");
+ DumpCounters(log, local_counters);
+}
+
+clang::QualType ClangASTImporter::CopyType(clang::ASTContext *dst_ast,
+ clang::ASTContext *src_ast,
+ clang::QualType type) {
+ ImporterDelegateSP delegate_sp(GetDelegate(dst_ast, src_ast));
+
+ ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp, dst_ast);
+
+ if (!delegate_sp)
+ return QualType();
+
+ llvm::Expected<QualType> ret_or_error = delegate_sp->Import(type);
+ if (!ret_or_error) {
+ Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ LLDB_LOG_ERROR(log, ret_or_error.takeError(),
+ "Couldn't import type: {0}");
+ return QualType();
+ }
+ return *ret_or_error;
+}
+
+lldb::opaque_compiler_type_t
+ClangASTImporter::CopyType(clang::ASTContext *dst_ast,
+ clang::ASTContext *src_ast,
+ lldb::opaque_compiler_type_t type) {
+ return CopyType(dst_ast, src_ast, QualType::getFromOpaquePtr(type))
+ .getAsOpaquePtr();
+}
+
+CompilerType ClangASTImporter::CopyType(ClangASTContext &dst_ast,
+ const CompilerType &src_type) {
+ clang::ASTContext *dst_clang_ast = dst_ast.getASTContext();
+ if (dst_clang_ast) {
+ ClangASTContext *src_ast =
+ llvm::dyn_cast_or_null<ClangASTContext>(src_type.GetTypeSystem());
+ if (src_ast) {
+ clang::ASTContext *src_clang_ast = src_ast->getASTContext();
+ if (src_clang_ast) {
+ lldb::opaque_compiler_type_t dst_clang_type = CopyType(
+ dst_clang_ast, src_clang_ast, src_type.GetOpaqueQualType());
+
+ if (dst_clang_type)
+ return CompilerType(&dst_ast, dst_clang_type);
+ }
+ }
+ }
+ return CompilerType();
+}
+
+clang::Decl *ClangASTImporter::CopyDecl(clang::ASTContext *dst_ast,
+ clang::ASTContext *src_ast,
+ clang::Decl *decl) {
+ ImporterDelegateSP delegate_sp;
+
+ delegate_sp = GetDelegate(dst_ast, src_ast);
+
+ ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp, dst_ast);
+
+ if (!delegate_sp)
+ return nullptr;
+
+ llvm::Expected<clang::Decl *> result = delegate_sp->Import(decl);
+ if (!result) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+ LLDB_LOG_ERROR(log, result.takeError(), "Couldn't import decl: {0}");
+ if (log) {
+ lldb::user_id_t user_id = LLDB_INVALID_UID;
+ ClangASTMetadata *metadata = GetDeclMetadata(decl);
+ if (metadata)
+ user_id = metadata->GetUserID();
+
+ if (NamedDecl *named_decl = dyn_cast<NamedDecl>(decl))
+ log->Printf(" [ClangASTImporter] WARNING: Failed to import a %s "
+ "'%s', metadata 0x%" PRIx64,
+ decl->getDeclKindName(),
+ named_decl->getNameAsString().c_str(), user_id);
+ else
+ log->Printf(" [ClangASTImporter] WARNING: Failed to import a %s, "
+ "metadata 0x%" PRIx64,
+ decl->getDeclKindName(), user_id);
+ }
+ return nullptr;
+ }
+
+ return *result;
+}
+
+class DeclContextOverride {
+private:
+ struct Backup {
+ clang::DeclContext *decl_context;
+ clang::DeclContext *lexical_decl_context;
+ };
+
+ std::map<clang::Decl *, Backup> m_backups;
+
+ void OverrideOne(clang::Decl *decl) {
+ if (m_backups.find(decl) != m_backups.end()) {
+ return;
+ }
+
+ m_backups[decl] = {decl->getDeclContext(), decl->getLexicalDeclContext()};
+
+ decl->setDeclContext(decl->getASTContext().getTranslationUnitDecl());
+ decl->setLexicalDeclContext(decl->getASTContext().getTranslationUnitDecl());
+ }
+
+ bool ChainPassesThrough(
+ clang::Decl *decl, clang::DeclContext *base,
+ clang::DeclContext *(clang::Decl::*contextFromDecl)(),
+ clang::DeclContext *(clang::DeclContext::*contextFromContext)()) {
+ for (DeclContext *decl_ctx = (decl->*contextFromDecl)(); decl_ctx;
+ decl_ctx = (decl_ctx->*contextFromContext)()) {
+ if (decl_ctx == base) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ clang::Decl *GetEscapedChild(clang::Decl *decl,
+ clang::DeclContext *base = nullptr) {
+ if (base) {
+ // decl's DeclContext chains must pass through base.
+
+ if (!ChainPassesThrough(decl, base, &clang::Decl::getDeclContext,
+ &clang::DeclContext::getParent) ||
+ !ChainPassesThrough(decl, base, &clang::Decl::getLexicalDeclContext,
+ &clang::DeclContext::getLexicalParent)) {
+ return decl;
+ }
+ } else {
+ base = clang::dyn_cast<clang::DeclContext>(decl);
+
+ if (!base) {
+ return nullptr;
+ }
+ }
+
+ if (clang::DeclContext *context =
+ clang::dyn_cast<clang::DeclContext>(decl)) {
+ for (clang::Decl *decl : context->decls()) {
+ if (clang::Decl *escaped_child = GetEscapedChild(decl)) {
+ return escaped_child;
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ void Override(clang::Decl *decl) {
+ if (clang::Decl *escaped_child = GetEscapedChild(decl)) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf(" [ClangASTImporter] DeclContextOverride couldn't "
+ "override (%sDecl*)%p - its child (%sDecl*)%p escapes",
+ decl->getDeclKindName(), static_cast<void *>(decl),
+ escaped_child->getDeclKindName(),
+ static_cast<void *>(escaped_child));
+ lldbassert(0 && "Couldn't override!");
+ }
+
+ OverrideOne(decl);
+ }
+
+public:
+ DeclContextOverride() {}
+
+ void OverrideAllDeclsFromContainingFunction(clang::Decl *decl) {
+ for (DeclContext *decl_context = decl->getLexicalDeclContext();
+ decl_context; decl_context = decl_context->getLexicalParent()) {
+ DeclContext *redecl_context = decl_context->getRedeclContext();
+
+ if (llvm::isa<FunctionDecl>(redecl_context) &&
+ llvm::isa<TranslationUnitDecl>(redecl_context->getLexicalParent())) {
+ for (clang::Decl *child_decl : decl_context->decls()) {
+ Override(child_decl);
+ }
+ }
+ }
+ }
+
+ ~DeclContextOverride() {
+ for (const std::pair<clang::Decl *, Backup> &backup : m_backups) {
+ backup.first->setDeclContext(backup.second.decl_context);
+ backup.first->setLexicalDeclContext(backup.second.lexical_decl_context);
+ }
+ }
+};
+
+lldb::opaque_compiler_type_t
+ClangASTImporter::DeportType(clang::ASTContext *dst_ctx,
+ clang::ASTContext *src_ctx,
+ lldb::opaque_compiler_type_t type) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf(" [ClangASTImporter] DeportType called on (%sType*)0x%llx "
+ "from (ASTContext*)%p to (ASTContext*)%p",
+ QualType::getFromOpaquePtr(type)->getTypeClassName(),
+ (unsigned long long)type, static_cast<void *>(src_ctx),
+ static_cast<void *>(dst_ctx));
+
+ ImporterDelegateSP delegate_sp(GetDelegate(dst_ctx, src_ctx));
+
+ if (!delegate_sp)
+ return nullptr;
+
+ std::set<NamedDecl *> decls_to_deport;
+ std::set<NamedDecl *> decls_already_deported;
+
+ DeclContextOverride decl_context_override;
+
+ if (const clang::TagType *tag_type =
+ clang::QualType::getFromOpaquePtr(type)->getAs<TagType>()) {
+ decl_context_override.OverrideAllDeclsFromContainingFunction(
+ tag_type->getDecl());
+ }
+
+ delegate_sp->InitDeportWorkQueues(&decls_to_deport, &decls_already_deported);
+
+ lldb::opaque_compiler_type_t result = CopyType(dst_ctx, src_ctx, type);
+
+ delegate_sp->ExecuteDeportWorkQueues();
+
+ if (!result)
+ return nullptr;
+
+ return result;
+}
+
+clang::Decl *ClangASTImporter::DeportDecl(clang::ASTContext *dst_ctx,
+ clang::ASTContext *src_ctx,
+ clang::Decl *decl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf(" [ClangASTImporter] DeportDecl called on (%sDecl*)%p from "
+ "(ASTContext*)%p to (ASTContext*)%p",
+ decl->getDeclKindName(), static_cast<void *>(decl),
+ static_cast<void *>(src_ctx), static_cast<void *>(dst_ctx));
+
+ ImporterDelegateSP delegate_sp(GetDelegate(dst_ctx, src_ctx));
+
+ if (!delegate_sp)
+ return nullptr;
+
+ std::set<NamedDecl *> decls_to_deport;
+ std::set<NamedDecl *> decls_already_deported;
+
+ DeclContextOverride decl_context_override;
+
+ decl_context_override.OverrideAllDeclsFromContainingFunction(decl);
+
+ delegate_sp->InitDeportWorkQueues(&decls_to_deport, &decls_already_deported);
+
+ clang::Decl *result = CopyDecl(dst_ctx, src_ctx, decl);
+
+ delegate_sp->ExecuteDeportWorkQueues();
+
+ if (!result)
+ return nullptr;
+
+ if (log)
+ log->Printf(
+ " [ClangASTImporter] DeportDecl deported (%sDecl*)%p to (%sDecl*)%p",
+ decl->getDeclKindName(), static_cast<void *>(decl),
+ result->getDeclKindName(), static_cast<void *>(result));
+
+ return result;
+}
+
+bool ClangASTImporter::CanImport(const CompilerType &type) {
+ if (!ClangUtil::IsClangType(type))
+ return false;
+
+ // TODO: remove external completion BOOL
+ // CompleteAndFetchChildren should get the Decl out and check for the
+
+ clang::QualType qual_type(
+ ClangUtil::GetCanonicalQualType(ClangUtil::RemoveFastQualifiers(type)));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record: {
+ const clang::CXXRecordDecl *cxx_record_decl =
+ qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl) {
+ if (ResolveDeclOrigin(cxx_record_decl, nullptr, nullptr))
+ return true;
+ }
+ } break;
+
+ case clang::Type::Enum: {
+ clang::EnumDecl *enum_decl =
+ llvm::cast<clang::EnumType>(qual_type)->getDecl();
+ if (enum_decl) {
+ if (ResolveDeclOrigin(enum_decl, nullptr, nullptr))
+ return true;
+ }
+ } break;
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface: {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type);
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+ // We currently can't complete objective C types through the newly added
+ // ASTContext because it only supports TagDecl objects right now...
+ if (class_interface_decl) {
+ if (ResolveDeclOrigin(class_interface_decl, nullptr, nullptr))
+ return true;
+ }
+ }
+ } break;
+
+ case clang::Type::Typedef:
+ return CanImport(CompilerType(type.GetTypeSystem(),
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr()));
+
+ case clang::Type::Auto:
+ return CanImport(CompilerType(type.GetTypeSystem(),
+ llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr()));
+
+ case clang::Type::Elaborated:
+ return CanImport(CompilerType(type.GetTypeSystem(),
+ llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr()));
+
+ case clang::Type::Paren:
+ return CanImport(CompilerType(
+ type.GetTypeSystem(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr()));
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool ClangASTImporter::Import(const CompilerType &type) {
+ if (!ClangUtil::IsClangType(type))
+ return false;
+ // TODO: remove external completion BOOL
+ // CompleteAndFetchChildren should get the Decl out and check for the
+
+ clang::QualType qual_type(
+ ClangUtil::GetCanonicalQualType(ClangUtil::RemoveFastQualifiers(type)));
+
+ const clang::Type::TypeClass type_class = qual_type->getTypeClass();
+ switch (type_class) {
+ case clang::Type::Record: {
+ const clang::CXXRecordDecl *cxx_record_decl =
+ qual_type->getAsCXXRecordDecl();
+ if (cxx_record_decl) {
+ if (ResolveDeclOrigin(cxx_record_decl, nullptr, nullptr))
+ return CompleteAndFetchChildren(qual_type);
+ }
+ } break;
+
+ case clang::Type::Enum: {
+ clang::EnumDecl *enum_decl =
+ llvm::cast<clang::EnumType>(qual_type)->getDecl();
+ if (enum_decl) {
+ if (ResolveDeclOrigin(enum_decl, nullptr, nullptr))
+ return CompleteAndFetchChildren(qual_type);
+ }
+ } break;
+
+ case clang::Type::ObjCObject:
+ case clang::Type::ObjCInterface: {
+ const clang::ObjCObjectType *objc_class_type =
+ llvm::dyn_cast<clang::ObjCObjectType>(qual_type);
+ if (objc_class_type) {
+ clang::ObjCInterfaceDecl *class_interface_decl =
+ objc_class_type->getInterface();
+ // We currently can't complete objective C types through the newly added
+ // ASTContext because it only supports TagDecl objects right now...
+ if (class_interface_decl) {
+ if (ResolveDeclOrigin(class_interface_decl, nullptr, nullptr))
+ return CompleteAndFetchChildren(qual_type);
+ }
+ }
+ } break;
+
+ case clang::Type::Typedef:
+ return Import(CompilerType(type.GetTypeSystem(),
+ llvm::cast<clang::TypedefType>(qual_type)
+ ->getDecl()
+ ->getUnderlyingType()
+ .getAsOpaquePtr()));
+
+ case clang::Type::Auto:
+ return Import(CompilerType(type.GetTypeSystem(),
+ llvm::cast<clang::AutoType>(qual_type)
+ ->getDeducedType()
+ .getAsOpaquePtr()));
+
+ case clang::Type::Elaborated:
+ return Import(CompilerType(type.GetTypeSystem(),
+ llvm::cast<clang::ElaboratedType>(qual_type)
+ ->getNamedType()
+ .getAsOpaquePtr()));
+
+ case clang::Type::Paren:
+ return Import(CompilerType(
+ type.GetTypeSystem(),
+ llvm::cast<clang::ParenType>(qual_type)->desugar().getAsOpaquePtr()));
+
+ default:
+ break;
+ }
+ return false;
+}
+
+bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) {
+ if (!CanImport(compiler_type))
+ return false;
+
+ if (Import(compiler_type)) {
+ ClangASTContext::CompleteTagDeclarationDefinition(compiler_type);
+ return true;
+ }
+
+ ClangASTContext::SetHasExternalStorage(compiler_type.GetOpaqueQualType(),
+ false);
+ return false;
+}
+
+bool ClangASTImporter::LayoutRecordType(
+ const clang::RecordDecl *record_decl, uint64_t &bit_size,
+ uint64_t &alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &base_offsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &vbase_offsets) {
+ RecordDeclToLayoutMap::iterator pos =
+ m_record_decl_to_layout_map.find(record_decl);
+ bool success = false;
+ base_offsets.clear();
+ vbase_offsets.clear();
+ if (pos != m_record_decl_to_layout_map.end()) {
+ bit_size = pos->second.bit_size;
+ alignment = pos->second.alignment;
+ field_offsets.swap(pos->second.field_offsets);
+ base_offsets.swap(pos->second.base_offsets);
+ vbase_offsets.swap(pos->second.vbase_offsets);
+ m_record_decl_to_layout_map.erase(pos);
+ success = true;
+ } else {
+ bit_size = 0;
+ alignment = 0;
+ field_offsets.clear();
+ }
+ return success;
+}
+
+void ClangASTImporter::InsertRecordDecl(clang::RecordDecl *decl,
+ const LayoutInfo &layout) {
+ m_record_decl_to_layout_map.insert(std::make_pair(decl, layout));
+}
+
+void ClangASTImporter::CompleteDecl(clang::Decl *decl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf(" [ClangASTImporter] CompleteDecl called on (%sDecl*)%p",
+ decl->getDeclKindName(), static_cast<void *>(decl));
+
+ if (ObjCInterfaceDecl *interface_decl = dyn_cast<ObjCInterfaceDecl>(decl)) {
+ if (!interface_decl->getDefinition()) {
+ interface_decl->startDefinition();
+ CompleteObjCInterfaceDecl(interface_decl);
+ }
+ } else if (ObjCProtocolDecl *protocol_decl =
+ dyn_cast<ObjCProtocolDecl>(decl)) {
+ if (!protocol_decl->getDefinition())
+ protocol_decl->startDefinition();
+ } else if (TagDecl *tag_decl = dyn_cast<TagDecl>(decl)) {
+ if (!tag_decl->getDefinition() && !tag_decl->isBeingDefined()) {
+ tag_decl->startDefinition();
+ CompleteTagDecl(tag_decl);
+ tag_decl->setCompleteDefinition(true);
+ }
+ } else {
+ assert(0 && "CompleteDecl called on a Decl that can't be completed");
+ }
+}
+
+bool ClangASTImporter::CompleteTagDecl(clang::TagDecl *decl) {
+ ClangASTMetrics::RegisterDeclCompletion();
+
+ DeclOrigin decl_origin = GetDeclOrigin(decl);
+
+ if (!decl_origin.Valid())
+ return false;
+
+ if (!ClangASTContext::GetCompleteDecl(decl_origin.ctx, decl_origin.decl))
+ return false;
+
+ ImporterDelegateSP delegate_sp(
+ GetDelegate(&decl->getASTContext(), decl_origin.ctx));
+
+ ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp,
+ &decl->getASTContext());
+ if (delegate_sp)
+ delegate_sp->ImportDefinitionTo(decl, decl_origin.decl);
+
+ return true;
+}
+
+bool ClangASTImporter::CompleteTagDeclWithOrigin(clang::TagDecl *decl,
+ clang::TagDecl *origin_decl) {
+ ClangASTMetrics::RegisterDeclCompletion();
+
+ clang::ASTContext *origin_ast_ctx = &origin_decl->getASTContext();
+
+ if (!ClangASTContext::GetCompleteDecl(origin_ast_ctx, origin_decl))
+ return false;
+
+ ImporterDelegateSP delegate_sp(
+ GetDelegate(&decl->getASTContext(), origin_ast_ctx));
+
+ if (delegate_sp)
+ delegate_sp->ImportDefinitionTo(decl, origin_decl);
+
+ ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext());
+
+ OriginMap &origins = context_md->m_origins;
+
+ origins[decl] = DeclOrigin(origin_ast_ctx, origin_decl);
+
+ return true;
+}
+
+bool ClangASTImporter::CompleteObjCInterfaceDecl(
+ clang::ObjCInterfaceDecl *interface_decl) {
+ ClangASTMetrics::RegisterDeclCompletion();
+
+ DeclOrigin decl_origin = GetDeclOrigin(interface_decl);
+
+ if (!decl_origin.Valid())
+ return false;
+
+ if (!ClangASTContext::GetCompleteDecl(decl_origin.ctx, decl_origin.decl))
+ return false;
+
+ ImporterDelegateSP delegate_sp(
+ GetDelegate(&interface_decl->getASTContext(), decl_origin.ctx));
+
+ if (delegate_sp)
+ delegate_sp->ImportDefinitionTo(interface_decl, decl_origin.decl);
+
+ if (ObjCInterfaceDecl *super_class = interface_decl->getSuperClass())
+ RequireCompleteType(clang::QualType(super_class->getTypeForDecl(), 0));
+
+ return true;
+}
+
+bool ClangASTImporter::CompleteAndFetchChildren(clang::QualType type) {
+ if (!RequireCompleteType(type))
+ return false;
+
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+
+ if (const TagType *tag_type = type->getAs<TagType>()) {
+ TagDecl *tag_decl = tag_type->getDecl();
+
+ DeclOrigin decl_origin = GetDeclOrigin(tag_decl);
+
+ if (!decl_origin.Valid())
+ return false;
+
+ ImporterDelegateSP delegate_sp(
+ GetDelegate(&tag_decl->getASTContext(), decl_origin.ctx));
+
+ ASTImporterDelegate::CxxModuleScope std_scope(*delegate_sp,
+ &tag_decl->getASTContext());
+
+ TagDecl *origin_tag_decl = llvm::dyn_cast<TagDecl>(decl_origin.decl);
+
+ for (Decl *origin_child_decl : origin_tag_decl->decls()) {
+ llvm::Expected<Decl *> imported_or_err =
+ delegate_sp->Import(origin_child_decl);
+ if (!imported_or_err) {
+ LLDB_LOG_ERROR(log, imported_or_err.takeError(),
+ "Couldn't import decl: {0}");
+ return false;
+ }
+ }
+
+ if (RecordDecl *record_decl = dyn_cast<RecordDecl>(origin_tag_decl)) {
+ record_decl->setHasLoadedFieldsFromExternalStorage(true);
+ }
+
+ return true;
+ }
+
+ if (const ObjCObjectType *objc_object_type = type->getAs<ObjCObjectType>()) {
+ if (ObjCInterfaceDecl *objc_interface_decl =
+ objc_object_type->getInterface()) {
+ DeclOrigin decl_origin = GetDeclOrigin(objc_interface_decl);
+
+ if (!decl_origin.Valid())
+ return false;
+
+ ImporterDelegateSP delegate_sp(
+ GetDelegate(&objc_interface_decl->getASTContext(), decl_origin.ctx));
+
+ ObjCInterfaceDecl *origin_interface_decl =
+ llvm::dyn_cast<ObjCInterfaceDecl>(decl_origin.decl);
+
+ for (Decl *origin_child_decl : origin_interface_decl->decls()) {
+ llvm::Expected<Decl *> imported_or_err =
+ delegate_sp->Import(origin_child_decl);
+ if (!imported_or_err) {
+ LLDB_LOG_ERROR(log, imported_or_err.takeError(),
+ "Couldn't import decl: {0}");
+ return false;
+ }
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ClangASTImporter::RequireCompleteType(clang::QualType type) {
+ if (type.isNull())
+ return false;
+
+ if (const TagType *tag_type = type->getAs<TagType>()) {
+ TagDecl *tag_decl = tag_type->getDecl();
+
+ if (tag_decl->getDefinition() || tag_decl->isBeingDefined())
+ return true;
+
+ return CompleteTagDecl(tag_decl);
+ }
+ if (const ObjCObjectType *objc_object_type = type->getAs<ObjCObjectType>()) {
+ if (ObjCInterfaceDecl *objc_interface_decl =
+ objc_object_type->getInterface())
+ return CompleteObjCInterfaceDecl(objc_interface_decl);
+ else
+ return false;
+ }
+ if (const ArrayType *array_type = type->getAsArrayTypeUnsafe()) {
+ return RequireCompleteType(array_type->getElementType());
+ }
+ if (const AtomicType *atomic_type = type->getAs<AtomicType>()) {
+ return RequireCompleteType(atomic_type->getPointeeType());
+ }
+
+ return true;
+}
+
+ClangASTMetadata *ClangASTImporter::GetDeclMetadata(const clang::Decl *decl) {
+ DeclOrigin decl_origin = GetDeclOrigin(decl);
+
+ if (decl_origin.Valid())
+ return ClangASTContext::GetMetadata(decl_origin.ctx, decl_origin.decl);
+ else
+ return ClangASTContext::GetMetadata(&decl->getASTContext(), decl);
+}
+
+ClangASTImporter::DeclOrigin
+ClangASTImporter::GetDeclOrigin(const clang::Decl *decl) {
+ ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext());
+
+ OriginMap &origins = context_md->m_origins;
+
+ OriginMap::iterator iter = origins.find(decl);
+
+ if (iter != origins.end())
+ return iter->second;
+ else
+ return DeclOrigin();
+}
+
+void ClangASTImporter::SetDeclOrigin(const clang::Decl *decl,
+ clang::Decl *original_decl) {
+ ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext());
+
+ OriginMap &origins = context_md->m_origins;
+
+ OriginMap::iterator iter = origins.find(decl);
+
+ if (iter != origins.end()) {
+ iter->second.decl = original_decl;
+ iter->second.ctx = &original_decl->getASTContext();
+ } else {
+ origins[decl] = DeclOrigin(&original_decl->getASTContext(), original_decl);
+ }
+}
+
+void ClangASTImporter::RegisterNamespaceMap(const clang::NamespaceDecl *decl,
+ NamespaceMapSP &namespace_map) {
+ ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext());
+
+ context_md->m_namespace_maps[decl] = namespace_map;
+}
+
+ClangASTImporter::NamespaceMapSP
+ClangASTImporter::GetNamespaceMap(const clang::NamespaceDecl *decl) {
+ ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext());
+
+ NamespaceMetaMap &namespace_maps = context_md->m_namespace_maps;
+
+ NamespaceMetaMap::iterator iter = namespace_maps.find(decl);
+
+ if (iter != namespace_maps.end())
+ return iter->second;
+ else
+ return NamespaceMapSP();
+}
+
+void ClangASTImporter::BuildNamespaceMap(const clang::NamespaceDecl *decl) {
+ assert(decl);
+ ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext());
+
+ const DeclContext *parent_context = decl->getDeclContext();
+ const NamespaceDecl *parent_namespace =
+ dyn_cast<NamespaceDecl>(parent_context);
+ NamespaceMapSP parent_map;
+
+ if (parent_namespace)
+ parent_map = GetNamespaceMap(parent_namespace);
+
+ NamespaceMapSP new_map;
+
+ new_map = std::make_shared<NamespaceMap>();
+
+ if (context_md->m_map_completer) {
+ std::string namespace_string = decl->getDeclName().getAsString();
+
+ context_md->m_map_completer->CompleteNamespaceMap(
+ new_map, ConstString(namespace_string.c_str()), parent_map);
+ }
+
+ context_md->m_namespace_maps[decl] = new_map;
+}
+
+void ClangASTImporter::ForgetDestination(clang::ASTContext *dst_ast) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf(" [ClangASTImporter] Forgetting destination (ASTContext*)%p",
+ static_cast<void *>(dst_ast));
+
+ m_metadata_map.erase(dst_ast);
+}
+
+void ClangASTImporter::ForgetSource(clang::ASTContext *dst_ast,
+ clang::ASTContext *src_ast) {
+ ASTContextMetadataSP md = MaybeGetContextMetadata(dst_ast);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf(" [ClangASTImporter] Forgetting source->dest "
+ "(ASTContext*)%p->(ASTContext*)%p",
+ static_cast<void *>(src_ast), static_cast<void *>(dst_ast));
+
+ if (!md)
+ return;
+
+ md->m_delegates.erase(src_ast);
+
+ for (OriginMap::iterator iter = md->m_origins.begin();
+ iter != md->m_origins.end();) {
+ if (iter->second.ctx == src_ast)
+ md->m_origins.erase(iter++);
+ else
+ ++iter;
+ }
+}
+
+ClangASTImporter::MapCompleter::~MapCompleter() { return; }
+
+llvm::Expected<Decl *>
+ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) {
+ if (m_std_handler) {
+ llvm::Optional<Decl *> D = m_std_handler->Import(From);
+ if (D) {
+ // Make sure we don't use this decl later to map it back to it's original
+ // decl. The decl the CxxModuleHandler created has nothing to do with
+ // the one from debug info, and linking those two would just cause the
+ // ASTImporter to try 'updating' the module decl with the minimal one from
+ // the debug info.
+ m_decls_to_ignore.insert(*D);
+ return *D;
+ }
+ }
+
+ return ASTImporter::ImportImpl(From);
+}
+
+void ClangASTImporter::ASTImporterDelegate::InitDeportWorkQueues(
+ std::set<clang::NamedDecl *> *decls_to_deport,
+ std::set<clang::NamedDecl *> *decls_already_deported) {
+ assert(!m_decls_to_deport);
+ assert(!m_decls_already_deported);
+
+ m_decls_to_deport = decls_to_deport;
+ m_decls_already_deported = decls_already_deported;
+}
+
+void ClangASTImporter::ASTImporterDelegate::ExecuteDeportWorkQueues() {
+ assert(m_decls_to_deport);
+ assert(m_decls_already_deported);
+
+ ASTContextMetadataSP to_context_md =
+ m_master.GetContextMetadata(&getToContext());
+
+ while (!m_decls_to_deport->empty()) {
+ NamedDecl *decl = *m_decls_to_deport->begin();
+
+ m_decls_already_deported->insert(decl);
+ m_decls_to_deport->erase(decl);
+
+ DeclOrigin &origin = to_context_md->m_origins[decl];
+ UNUSED_IF_ASSERT_DISABLED(origin);
+
+ assert(origin.ctx ==
+ m_source_ctx); // otherwise we should never have added this
+ // because it doesn't need to be deported
+
+ Decl *original_decl = to_context_md->m_origins[decl].decl;
+
+ ClangASTContext::GetCompleteDecl(m_source_ctx, original_decl);
+
+ if (TagDecl *tag_decl = dyn_cast<TagDecl>(decl)) {
+ if (TagDecl *original_tag_decl = dyn_cast<TagDecl>(original_decl)) {
+ if (original_tag_decl->isCompleteDefinition()) {
+ ImportDefinitionTo(tag_decl, original_tag_decl);
+ tag_decl->setCompleteDefinition(true);
+ }
+ }
+
+ tag_decl->setHasExternalLexicalStorage(false);
+ tag_decl->setHasExternalVisibleStorage(false);
+ } else if (ObjCContainerDecl *container_decl =
+ dyn_cast<ObjCContainerDecl>(decl)) {
+ container_decl->setHasExternalLexicalStorage(false);
+ container_decl->setHasExternalVisibleStorage(false);
+ }
+
+ to_context_md->m_origins.erase(decl);
+ }
+
+ m_decls_to_deport = nullptr;
+ m_decls_already_deported = nullptr;
+}
+
+void ClangASTImporter::ASTImporterDelegate::ImportDefinitionTo(
+ clang::Decl *to, clang::Decl *from) {
+ ASTImporter::Imported(from, to);
+
+ /*
+ if (to_objc_interface)
+ to_objc_interface->startDefinition();
+
+ CXXRecordDecl *to_cxx_record = dyn_cast<CXXRecordDecl>(to);
+
+ if (to_cxx_record)
+ to_cxx_record->startDefinition();
+ */
+
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+
+ if (llvm::Error err = ImportDefinition(from)) {
+ LLDB_LOG_ERROR(log, std::move(err),
+ "[ClangASTImporter] Error during importing definition: {0}");
+ return;
+ }
+
+ if (clang::TagDecl *to_tag = dyn_cast<clang::TagDecl>(to)) {
+ if (clang::TagDecl *from_tag = dyn_cast<clang::TagDecl>(from)) {
+ to_tag->setCompleteDefinition(from_tag->isCompleteDefinition());
+
+ if (Log *log_ast =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_AST)) {
+ std::string name_string;
+ if (NamedDecl *from_named_decl = dyn_cast<clang::NamedDecl>(from)) {
+ llvm::raw_string_ostream name_stream(name_string);
+ from_named_decl->printName(name_stream);
+ name_stream.flush();
+ }
+ LLDB_LOG(log_ast, "==== [ClangASTImporter][TUDecl: {0}] Imported "
+ "({1}Decl*){2}, named {3} (from "
+ "(Decl*){4})",
+ static_cast<void *>(to->getTranslationUnitDecl()),
+ from->getDeclKindName(), static_cast<void *>(to), name_string,
+ static_cast<void *>(from));
+
+ // Log the AST of the TU.
+ std::string ast_string;
+ llvm::raw_string_ostream ast_stream(ast_string);
+ to->getTranslationUnitDecl()->dump(ast_stream);
+ LLDB_LOG(log_ast, "{0}", ast_string);
+ }
+ }
+ }
+
+ // If we're dealing with an Objective-C class, ensure that the inheritance
+ // has been set up correctly. The ASTImporter may not do this correctly if
+ // the class was originally sourced from symbols.
+
+ if (ObjCInterfaceDecl *to_objc_interface = dyn_cast<ObjCInterfaceDecl>(to)) {
+ do {
+ ObjCInterfaceDecl *to_superclass = to_objc_interface->getSuperClass();
+
+ if (to_superclass)
+ break; // we're not going to override it if it's set
+
+ ObjCInterfaceDecl *from_objc_interface =
+ dyn_cast<ObjCInterfaceDecl>(from);
+
+ if (!from_objc_interface)
+ break;
+
+ ObjCInterfaceDecl *from_superclass = from_objc_interface->getSuperClass();
+
+ if (!from_superclass)
+ break;
+
+ llvm::Expected<Decl *> imported_from_superclass_decl =
+ Import(from_superclass);
+
+ if (!imported_from_superclass_decl) {
+ LLDB_LOG_ERROR(log, imported_from_superclass_decl.takeError(),
+ "Couldn't import decl: {0}");
+ break;
+ }
+
+ ObjCInterfaceDecl *imported_from_superclass =
+ dyn_cast<ObjCInterfaceDecl>(*imported_from_superclass_decl);
+
+ if (!imported_from_superclass)
+ break;
+
+ if (!to_objc_interface->hasDefinition())
+ to_objc_interface->startDefinition();
+
+ to_objc_interface->setSuperClass(m_source_ctx->getTrivialTypeSourceInfo(
+ m_source_ctx->getObjCInterfaceType(imported_from_superclass)));
+ } while (false);
+ }
+}
+
+void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from,
+ clang::Decl *to) {
+ ClangASTMetrics::RegisterClangImport();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ // Some decls shouldn't be tracked here because they were not created by
+ // copying 'from' to 'to'. Just exit early for those.
+ if (m_decls_to_ignore.find(to) != m_decls_to_ignore.end())
+ return clang::ASTImporter::Imported(from, to);
+
+ lldb::user_id_t user_id = LLDB_INVALID_UID;
+ ClangASTMetadata *metadata = m_master.GetDeclMetadata(from);
+ if (metadata)
+ user_id = metadata->GetUserID();
+
+ if (log) {
+ if (NamedDecl *from_named_decl = dyn_cast<clang::NamedDecl>(from)) {
+ std::string name_string;
+ llvm::raw_string_ostream name_stream(name_string);
+ from_named_decl->printName(name_stream);
+ name_stream.flush();
+
+ log->Printf(" [ClangASTImporter] Imported (%sDecl*)%p, named %s (from "
+ "(Decl*)%p), metadata 0x%" PRIx64,
+ from->getDeclKindName(), static_cast<void *>(to),
+ name_string.c_str(), static_cast<void *>(from), user_id);
+ } else {
+ log->Printf(" [ClangASTImporter] Imported (%sDecl*)%p (from "
+ "(Decl*)%p), metadata 0x%" PRIx64,
+ from->getDeclKindName(), static_cast<void *>(to),
+ static_cast<void *>(from), user_id);
+ }
+ }
+
+ ASTContextMetadataSP to_context_md =
+ m_master.GetContextMetadata(&to->getASTContext());
+ ASTContextMetadataSP from_context_md =
+ m_master.MaybeGetContextMetadata(m_source_ctx);
+
+ if (from_context_md) {
+ OriginMap &origins = from_context_md->m_origins;
+
+ OriginMap::iterator origin_iter = origins.find(from);
+
+ if (origin_iter != origins.end()) {
+ if (to_context_md->m_origins.find(to) == to_context_md->m_origins.end() ||
+ user_id != LLDB_INVALID_UID) {
+ if (origin_iter->second.ctx != &to->getASTContext())
+ to_context_md->m_origins[to] = origin_iter->second;
+ }
+
+ ImporterDelegateSP direct_completer =
+ m_master.GetDelegate(&to->getASTContext(), origin_iter->second.ctx);
+
+ if (direct_completer.get() != this)
+ direct_completer->ASTImporter::Imported(origin_iter->second.decl, to);
+
+ if (log)
+ log->Printf(" [ClangASTImporter] Propagated origin "
+ "(Decl*)%p/(ASTContext*)%p from (ASTContext*)%p to "
+ "(ASTContext*)%p",
+ static_cast<void *>(origin_iter->second.decl),
+ static_cast<void *>(origin_iter->second.ctx),
+ static_cast<void *>(&from->getASTContext()),
+ static_cast<void *>(&to->getASTContext()));
+ } else {
+ if (m_decls_to_deport && m_decls_already_deported) {
+ if (isa<TagDecl>(to) || isa<ObjCInterfaceDecl>(to)) {
+ RecordDecl *from_record_decl = dyn_cast<RecordDecl>(from);
+ if (from_record_decl == nullptr ||
+ !from_record_decl->isInjectedClassName()) {
+ NamedDecl *to_named_decl = dyn_cast<NamedDecl>(to);
+
+ if (!m_decls_already_deported->count(to_named_decl))
+ m_decls_to_deport->insert(to_named_decl);
+ }
+ }
+ }
+
+ if (to_context_md->m_origins.find(to) == to_context_md->m_origins.end() ||
+ user_id != LLDB_INVALID_UID) {
+ to_context_md->m_origins[to] = DeclOrigin(m_source_ctx, from);
+ }
+
+ if (log)
+ log->Printf(" [ClangASTImporter] Decl has no origin information in "
+ "(ASTContext*)%p",
+ static_cast<void *>(&from->getASTContext()));
+ }
+
+ if (clang::NamespaceDecl *to_namespace =
+ dyn_cast<clang::NamespaceDecl>(to)) {
+ clang::NamespaceDecl *from_namespace =
+ dyn_cast<clang::NamespaceDecl>(from);
+
+ NamespaceMetaMap &namespace_maps = from_context_md->m_namespace_maps;
+
+ NamespaceMetaMap::iterator namespace_map_iter =
+ namespace_maps.find(from_namespace);
+
+ if (namespace_map_iter != namespace_maps.end())
+ to_context_md->m_namespace_maps[to_namespace] =
+ namespace_map_iter->second;
+ }
+ } else {
+ to_context_md->m_origins[to] = DeclOrigin(m_source_ctx, from);
+
+ if (log)
+ log->Printf(" [ClangASTImporter] Sourced origin "
+ "(Decl*)%p/(ASTContext*)%p into (ASTContext*)%p",
+ static_cast<void *>(from), static_cast<void *>(m_source_ctx),
+ static_cast<void *>(&to->getASTContext()));
+ }
+
+ if (TagDecl *from_tag_decl = dyn_cast<TagDecl>(from)) {
+ TagDecl *to_tag_decl = dyn_cast<TagDecl>(to);
+
+ to_tag_decl->setHasExternalLexicalStorage();
+ to_tag_decl->getPrimaryContext()->setMustBuildLookupTable();
+
+ if (log)
+ log->Printf(
+ " [ClangASTImporter] To is a TagDecl - attributes %s%s [%s->%s]",
+ (to_tag_decl->hasExternalLexicalStorage() ? " Lexical" : ""),
+ (to_tag_decl->hasExternalVisibleStorage() ? " Visible" : ""),
+ (from_tag_decl->isCompleteDefinition() ? "complete" : "incomplete"),
+ (to_tag_decl->isCompleteDefinition() ? "complete" : "incomplete"));
+ }
+
+ if (isa<NamespaceDecl>(from)) {
+ NamespaceDecl *to_namespace_decl = dyn_cast<NamespaceDecl>(to);
+
+ m_master.BuildNamespaceMap(to_namespace_decl);
+
+ to_namespace_decl->setHasExternalVisibleStorage();
+ }
+
+ if (isa<ObjCContainerDecl>(from)) {
+ ObjCContainerDecl *to_container_decl = dyn_cast<ObjCContainerDecl>(to);
+
+ to_container_decl->setHasExternalLexicalStorage();
+ to_container_decl->setHasExternalVisibleStorage();
+
+ /*to_interface_decl->setExternallyCompleted();*/
+
+ if (log) {
+ if (ObjCInterfaceDecl *to_interface_decl =
+ llvm::dyn_cast<ObjCInterfaceDecl>(to_container_decl)) {
+ log->Printf(
+ " [ClangASTImporter] To is an ObjCInterfaceDecl - attributes "
+ "%s%s%s",
+ (to_interface_decl->hasExternalLexicalStorage() ? " Lexical" : ""),
+ (to_interface_decl->hasExternalVisibleStorage() ? " Visible" : ""),
+ (to_interface_decl->hasDefinition() ? " HasDefinition" : ""));
+ } else {
+ log->Printf(
+ " [ClangASTImporter] To is an %sDecl - attributes %s%s",
+ ((Decl *)to_container_decl)->getDeclKindName(),
+ (to_container_decl->hasExternalLexicalStorage() ? " Lexical" : ""),
+ (to_container_decl->hasExternalVisibleStorage() ? " Visible" : ""));
+ }
+ }
+ }
+}
+
+clang::Decl *
+ClangASTImporter::ASTImporterDelegate::GetOriginalDecl(clang::Decl *To) {
+ ASTContextMetadataSP to_context_md =
+ m_master.GetContextMetadata(&To->getASTContext());
+
+ if (!to_context_md)
+ return nullptr;
+
+ OriginMap::iterator iter = to_context_md->m_origins.find(To);
+
+ if (iter == to_context_md->m_origins.end())
+ return nullptr;
+
+ return const_cast<clang::Decl *>(iter->second.decl);
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/ClangExternalASTSourceCallbacks.cpp b/contrib/llvm-project/lldb/source/Symbol/ClangExternalASTSourceCallbacks.cpp
new file mode 100644
index 000000000000..c35fc585bfa5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/ClangExternalASTSourceCallbacks.cpp
@@ -0,0 +1,99 @@
+//===-- ClangExternalASTSourceCallbacks.cpp ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/ClangExternalASTSourceCallbacks.h"
+
+
+// Clang headers like to use NDEBUG inside of them to enable/disable debug
+// related features using "#ifndef NDEBUG" preprocessor blocks to do one thing
+// or another. This is bad because it means that if clang was built in release
+// mode, it assumes that you are building in release mode which is not always
+// the case. You can end up with functions that are defined as empty in header
+// files when NDEBUG is not defined, and this can cause link errors with the
+// clang .a files that you have since you might be missing functions in the .a
+// file. So we have to define NDEBUG when including clang headers to avoid any
+// mismatches. This is covered by rdar://problem/8691220
+
+#if !defined(NDEBUG) && !defined(LLVM_NDEBUG_OFF)
+#define LLDB_DEFINED_NDEBUG_FOR_CLANG
+#define NDEBUG
+// Need to include assert.h so it is as clang would expect it to be (disabled)
+#include <assert.h>
+#endif
+
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclarationName.h"
+
+#ifdef LLDB_DEFINED_NDEBUG_FOR_CLANG
+#undef NDEBUG
+#undef LLDB_DEFINED_NDEBUG_FOR_CLANG
+// Need to re-include assert.h so it is as _we_ would expect it to be (enabled)
+#include <assert.h>
+#endif
+
+#include "lldb/Utility/Log.h"
+#include "clang/AST/Decl.h"
+
+using namespace clang;
+using namespace lldb_private;
+
+bool ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName(
+ const clang::DeclContext *decl_ctx,
+ clang::DeclarationName clang_decl_name) {
+ if (m_callback_find_by_name) {
+ llvm::SmallVector<clang::NamedDecl *, 3> results;
+
+ m_callback_find_by_name(m_callback_baton, decl_ctx, clang_decl_name,
+ &results);
+
+ SetExternalVisibleDeclsForName(decl_ctx, clang_decl_name, results);
+
+ return (results.size() != 0);
+ }
+
+ std::string decl_name(clang_decl_name.getAsString());
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+}
+
+void ClangExternalASTSourceCallbacks::CompleteType(TagDecl *tag_decl) {
+ if (m_callback_tag_decl)
+ m_callback_tag_decl(m_callback_baton, tag_decl);
+}
+
+void ClangExternalASTSourceCallbacks::CompleteType(
+ ObjCInterfaceDecl *objc_decl) {
+ if (m_callback_objc_decl)
+ m_callback_objc_decl(m_callback_baton, objc_decl);
+}
+
+bool ClangExternalASTSourceCallbacks::layoutRecordType(
+ const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> &BaseOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &VirtualBaseOffsets) {
+ if (m_callback_layout_record_type)
+ return m_callback_layout_record_type(m_callback_baton, Record, Size,
+ Alignment, FieldOffsets, BaseOffsets,
+ VirtualBaseOffsets);
+
+ return false;
+}
+
+void ClangExternalASTSourceCallbacks::FindExternalLexicalDecls(
+ const clang::DeclContext *decl_ctx,
+ llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
+ llvm::SmallVectorImpl<clang::Decl *> &decls) {
+ if (m_callback_tag_decl && decl_ctx) {
+ clang::TagDecl *tag_decl = llvm::dyn_cast<clang::TagDecl>(
+ const_cast<clang::DeclContext *>(decl_ctx));
+ if (tag_decl)
+ CompleteType(tag_decl);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/ClangExternalASTSourceCommon.cpp b/contrib/llvm-project/lldb/source/Symbol/ClangExternalASTSourceCommon.cpp
new file mode 100644
index 000000000000..3dcf9051d0a4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/ClangExternalASTSourceCommon.cpp
@@ -0,0 +1,99 @@
+//===-- ClangExternalASTSourceCommon.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Utility/Stream.h"
+
+#include <mutex>
+
+using namespace lldb_private;
+
+uint64_t g_TotalSizeOfMetadata = 0;
+
+typedef llvm::DenseMap<clang::ExternalASTSource *,
+ ClangExternalASTSourceCommon *>
+ ASTSourceMap;
+
+static ASTSourceMap &GetSourceMap(std::unique_lock<std::mutex> &guard) {
+ // Intentionally leaked to avoid problems with global destructors.
+ static ASTSourceMap *s_source_map = new ASTSourceMap;
+ static std::mutex s_mutex;
+ std::unique_lock<std::mutex> locked_guard(s_mutex);
+ guard.swap(locked_guard);
+ return *s_source_map;
+}
+
+ClangExternalASTSourceCommon *
+ClangExternalASTSourceCommon::Lookup(clang::ExternalASTSource *source) {
+ std::unique_lock<std::mutex> guard;
+ ASTSourceMap &source_map = GetSourceMap(guard);
+
+ ASTSourceMap::iterator iter = source_map.find(source);
+
+ if (iter != source_map.end()) {
+ return iter->second;
+ } else {
+ return nullptr;
+ }
+}
+
+ClangExternalASTSourceCommon::ClangExternalASTSourceCommon()
+ : clang::ExternalASTSource() {
+ g_TotalSizeOfMetadata += m_metadata.size();
+ std::unique_lock<std::mutex> guard;
+ GetSourceMap(guard)[this] = this;
+}
+
+ClangExternalASTSourceCommon::~ClangExternalASTSourceCommon() {
+ std::unique_lock<std::mutex> guard;
+ GetSourceMap(guard).erase(this);
+ g_TotalSizeOfMetadata -= m_metadata.size();
+}
+
+ClangASTMetadata *
+ClangExternalASTSourceCommon::GetMetadata(const void *object) {
+ if (HasMetadata(object))
+ return &m_metadata[object];
+ else
+ return nullptr;
+}
+
+void ClangExternalASTSourceCommon::SetMetadata(const void *object,
+ ClangASTMetadata &metadata) {
+ uint64_t orig_size = m_metadata.size();
+ m_metadata[object] = metadata;
+ uint64_t new_size = m_metadata.size();
+ g_TotalSizeOfMetadata += (new_size - orig_size);
+}
+
+bool ClangExternalASTSourceCommon::HasMetadata(const void *object) {
+ return m_metadata.find(object) != m_metadata.end();
+}
+
+void ClangASTMetadata::Dump(Stream *s) {
+ lldb::user_id_t uid = GetUserID();
+
+ if (uid != LLDB_INVALID_UID) {
+ s->Printf("uid=0x%" PRIx64, uid);
+ }
+
+ uint64_t isa_ptr = GetISAPtr();
+ if (isa_ptr != 0) {
+ s->Printf("isa_ptr=0x%" PRIx64, isa_ptr);
+ }
+
+ const char *obj_ptr_name = GetObjectPtrName();
+ if (obj_ptr_name) {
+ s->Printf("obj_ptr_name=\"%s\" ", obj_ptr_name);
+ }
+
+ if (m_is_dynamic_cxx) {
+ s->Printf("is_dynamic_cxx=%i ", m_is_dynamic_cxx);
+ }
+ s->EOL();
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/ClangUtil.cpp b/contrib/llvm-project/lldb/source/Symbol/ClangUtil.cpp
new file mode 100644
index 000000000000..86be895fadcb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/ClangUtil.cpp
@@ -0,0 +1,57 @@
+//===-- ClangUtil.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// A collection of helper methods and data structures for manipulating clang
+// types and decls.
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/ClangASTContext.h"
+
+using namespace clang;
+using namespace lldb_private;
+
+bool ClangUtil::IsClangType(const CompilerType &ct) {
+ if (llvm::dyn_cast_or_null<ClangASTContext>(ct.GetTypeSystem()) == nullptr)
+ return false;
+
+ if (!ct.GetOpaqueQualType())
+ return false;
+
+ return true;
+}
+
+QualType ClangUtil::GetQualType(const CompilerType &ct) {
+ // Make sure we have a clang type before making a clang::QualType
+ if (!IsClangType(ct))
+ return QualType();
+
+ return QualType::getFromOpaquePtr(ct.GetOpaqueQualType());
+}
+
+QualType ClangUtil::GetCanonicalQualType(const CompilerType &ct) {
+ if (!IsClangType(ct))
+ return QualType();
+
+ return GetQualType(ct).getCanonicalType();
+}
+
+CompilerType ClangUtil::RemoveFastQualifiers(const CompilerType &ct) {
+ if (!IsClangType(ct))
+ return ct;
+
+ QualType qual_type(GetQualType(ct));
+ qual_type.removeLocalFastQualifiers();
+ return CompilerType(ct.GetTypeSystem(), qual_type.getAsOpaquePtr());
+}
+
+clang::TagDecl *ClangUtil::GetAsTagDecl(const CompilerType &type) {
+ clang::QualType qual_type = ClangUtil::GetCanonicalQualType(type);
+ if (qual_type.isNull())
+ return nullptr;
+
+ return qual_type->getAsTagDecl();
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/CompactUnwindInfo.cpp b/contrib/llvm-project/lldb/source/Symbol/CompactUnwindInfo.cpp
new file mode 100644
index 000000000000..3a2a4d3a09e6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/CompactUnwindInfo.cpp
@@ -0,0 +1,1606 @@
+//===-- CompactUnwindInfo.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/CompactUnwindInfo.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Support/MathExtras.h"
+
+#include <algorithm>
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace lldb_private {
+
+// Constants from <mach-o/compact_unwind_encoding.h>
+
+FLAGS_ANONYMOUS_ENUM(){
+ UNWIND_IS_NOT_FUNCTION_START = 0x80000000, UNWIND_HAS_LSDA = 0x40000000,
+ UNWIND_PERSONALITY_MASK = 0x30000000,
+};
+
+FLAGS_ANONYMOUS_ENUM(){
+ UNWIND_X86_MODE_MASK = 0x0F000000,
+ UNWIND_X86_MODE_EBP_FRAME = 0x01000000,
+ UNWIND_X86_MODE_STACK_IMMD = 0x02000000,
+ UNWIND_X86_MODE_STACK_IND = 0x03000000,
+ UNWIND_X86_MODE_DWARF = 0x04000000,
+
+ UNWIND_X86_EBP_FRAME_REGISTERS = 0x00007FFF,
+ UNWIND_X86_EBP_FRAME_OFFSET = 0x00FF0000,
+
+ UNWIND_X86_FRAMELESS_STACK_SIZE = 0x00FF0000,
+ UNWIND_X86_FRAMELESS_STACK_ADJUST = 0x0000E000,
+ UNWIND_X86_FRAMELESS_STACK_REG_COUNT = 0x00001C00,
+ UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF,
+
+ UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF,
+};
+
+enum {
+ UNWIND_X86_REG_NONE = 0,
+ UNWIND_X86_REG_EBX = 1,
+ UNWIND_X86_REG_ECX = 2,
+ UNWIND_X86_REG_EDX = 3,
+ UNWIND_X86_REG_EDI = 4,
+ UNWIND_X86_REG_ESI = 5,
+ UNWIND_X86_REG_EBP = 6,
+};
+
+FLAGS_ANONYMOUS_ENUM(){
+ UNWIND_X86_64_MODE_MASK = 0x0F000000,
+ UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000,
+ UNWIND_X86_64_MODE_STACK_IMMD = 0x02000000,
+ UNWIND_X86_64_MODE_STACK_IND = 0x03000000,
+ UNWIND_X86_64_MODE_DWARF = 0x04000000,
+
+ UNWIND_X86_64_RBP_FRAME_REGISTERS = 0x00007FFF,
+ UNWIND_X86_64_RBP_FRAME_OFFSET = 0x00FF0000,
+
+ UNWIND_X86_64_FRAMELESS_STACK_SIZE = 0x00FF0000,
+ UNWIND_X86_64_FRAMELESS_STACK_ADJUST = 0x0000E000,
+ UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT = 0x00001C00,
+ UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF,
+
+ UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
+};
+
+enum {
+ UNWIND_X86_64_REG_NONE = 0,
+ UNWIND_X86_64_REG_RBX = 1,
+ UNWIND_X86_64_REG_R12 = 2,
+ UNWIND_X86_64_REG_R13 = 3,
+ UNWIND_X86_64_REG_R14 = 4,
+ UNWIND_X86_64_REG_R15 = 5,
+ UNWIND_X86_64_REG_RBP = 6,
+};
+
+FLAGS_ANONYMOUS_ENUM(){
+ UNWIND_ARM64_MODE_MASK = 0x0F000000,
+ UNWIND_ARM64_MODE_FRAMELESS = 0x02000000,
+ UNWIND_ARM64_MODE_DWARF = 0x03000000,
+ UNWIND_ARM64_MODE_FRAME = 0x04000000,
+
+ UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001,
+ UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002,
+ UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004,
+ UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008,
+ UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010,
+ UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100,
+ UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200,
+ UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400,
+ UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800,
+
+ UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
+ UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
+};
+
+FLAGS_ANONYMOUS_ENUM(){
+ UNWIND_ARM_MODE_MASK = 0x0F000000,
+ UNWIND_ARM_MODE_FRAME = 0x01000000,
+ UNWIND_ARM_MODE_FRAME_D = 0x02000000,
+ UNWIND_ARM_MODE_DWARF = 0x04000000,
+
+ UNWIND_ARM_FRAME_STACK_ADJUST_MASK = 0x00C00000,
+
+ UNWIND_ARM_FRAME_FIRST_PUSH_R4 = 0x00000001,
+ UNWIND_ARM_FRAME_FIRST_PUSH_R5 = 0x00000002,
+ UNWIND_ARM_FRAME_FIRST_PUSH_R6 = 0x00000004,
+
+ UNWIND_ARM_FRAME_SECOND_PUSH_R8 = 0x00000008,
+ UNWIND_ARM_FRAME_SECOND_PUSH_R9 = 0x00000010,
+ UNWIND_ARM_FRAME_SECOND_PUSH_R10 = 0x00000020,
+ UNWIND_ARM_FRAME_SECOND_PUSH_R11 = 0x00000040,
+ UNWIND_ARM_FRAME_SECOND_PUSH_R12 = 0x00000080,
+
+ UNWIND_ARM_FRAME_D_REG_COUNT_MASK = 0x00000700,
+
+ UNWIND_ARM_DWARF_SECTION_OFFSET = 0x00FFFFFF,
+};
+}
+
+#ifndef UNWIND_SECOND_LEVEL_REGULAR
+#define UNWIND_SECOND_LEVEL_REGULAR 2
+#endif
+
+#ifndef UNWIND_SECOND_LEVEL_COMPRESSED
+#define UNWIND_SECOND_LEVEL_COMPRESSED 3
+#endif
+
+#ifndef UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET
+#define UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) (entry & 0x00FFFFFF)
+#endif
+
+#ifndef UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX
+#define UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry) \
+ ((entry >> 24) & 0xFF)
+#endif
+
+#define EXTRACT_BITS(value, mask) \
+ ((value >> \
+ llvm::countTrailingZeros(static_cast<uint32_t>(mask), llvm::ZB_Width)) & \
+ (((1 << llvm::countPopulation(static_cast<uint32_t>(mask)))) - 1))
+
+// constructor
+
+CompactUnwindInfo::CompactUnwindInfo(ObjectFile &objfile, SectionSP &section_sp)
+ : m_objfile(objfile), m_section_sp(section_sp),
+ m_section_contents_if_encrypted(), m_mutex(), m_indexes(),
+ m_indexes_computed(eLazyBoolCalculate), m_unwindinfo_data(),
+ m_unwindinfo_data_computed(false), m_unwind_header() {}
+
+// destructor
+
+CompactUnwindInfo::~CompactUnwindInfo() {}
+
+bool CompactUnwindInfo::GetUnwindPlan(Target &target, Address addr,
+ UnwindPlan &unwind_plan) {
+ if (!IsValid(target.GetProcessSP())) {
+ return false;
+ }
+ FunctionInfo function_info;
+ if (GetCompactUnwindInfoForFunction(target, addr, function_info)) {
+ // shortcut return for functions that have no compact unwind
+ if (function_info.encoding == 0)
+ return false;
+
+ if (ArchSpec arch = m_objfile.GetArchitecture()) {
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+ addr.Dump(
+ &strm, nullptr,
+ Address::DumpStyle::DumpStyleResolvedDescriptionNoFunctionArguments,
+ Address::DumpStyle::DumpStyleFileAddress,
+ arch.GetAddressByteSize());
+ log->Printf("Got compact unwind encoding 0x%x for function %s",
+ function_info.encoding, strm.GetData());
+ }
+
+ if (function_info.valid_range_offset_start != 0 &&
+ function_info.valid_range_offset_end != 0) {
+ SectionList *sl = m_objfile.GetSectionList();
+ if (sl) {
+ addr_t func_range_start_file_addr =
+ function_info.valid_range_offset_start +
+ m_objfile.GetBaseAddress().GetFileAddress();
+ AddressRange func_range(func_range_start_file_addr,
+ function_info.valid_range_offset_end -
+ function_info.valid_range_offset_start,
+ sl);
+ unwind_plan.SetPlanValidAddressRange(func_range);
+ }
+ }
+
+ if (arch.GetTriple().getArch() == llvm::Triple::x86_64) {
+ return CreateUnwindPlan_x86_64(target, function_info, unwind_plan,
+ addr);
+ }
+ if (arch.GetTriple().getArch() == llvm::Triple::aarch64) {
+ return CreateUnwindPlan_arm64(target, function_info, unwind_plan, addr);
+ }
+ if (arch.GetTriple().getArch() == llvm::Triple::x86) {
+ return CreateUnwindPlan_i386(target, function_info, unwind_plan, addr);
+ }
+ if (arch.GetTriple().getArch() == llvm::Triple::arm ||
+ arch.GetTriple().getArch() == llvm::Triple::thumb) {
+ return CreateUnwindPlan_armv7(target, function_info, unwind_plan, addr);
+ }
+ }
+ }
+ return false;
+}
+
+bool CompactUnwindInfo::IsValid(const ProcessSP &process_sp) {
+ if (m_section_sp.get() == nullptr)
+ return false;
+
+ if (m_indexes_computed == eLazyBoolYes && m_unwindinfo_data_computed)
+ return true;
+
+ ScanIndex(process_sp);
+
+ return m_indexes_computed == eLazyBoolYes && m_unwindinfo_data_computed;
+}
+
+void CompactUnwindInfo::ScanIndex(const ProcessSP &process_sp) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ if (m_indexes_computed == eLazyBoolYes && m_unwindinfo_data_computed)
+ return;
+
+ // We can't read the index for some reason.
+ if (m_indexes_computed == eLazyBoolNo) {
+ return;
+ }
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log)
+ m_objfile.GetModule()->LogMessage(
+ log, "Reading compact unwind first-level indexes");
+
+ if (!m_unwindinfo_data_computed) {
+ if (m_section_sp->IsEncrypted()) {
+ // Can't get section contents of a protected/encrypted section until we
+ // have a live process and can read them out of memory.
+ if (process_sp.get() == nullptr)
+ return;
+ m_section_contents_if_encrypted =
+ std::make_shared<DataBufferHeap>(m_section_sp->GetByteSize(), 0);
+ Status error;
+ if (process_sp->ReadMemory(
+ m_section_sp->GetLoadBaseAddress(&process_sp->GetTarget()),
+ m_section_contents_if_encrypted->GetBytes(),
+ m_section_sp->GetByteSize(),
+ error) == m_section_sp->GetByteSize() &&
+ error.Success()) {
+ m_unwindinfo_data.SetAddressByteSize(
+ process_sp->GetTarget().GetArchitecture().GetAddressByteSize());
+ m_unwindinfo_data.SetByteOrder(
+ process_sp->GetTarget().GetArchitecture().GetByteOrder());
+ m_unwindinfo_data.SetData(m_section_contents_if_encrypted, 0);
+ }
+ } else {
+ m_objfile.ReadSectionData(m_section_sp.get(), m_unwindinfo_data);
+ }
+ if (m_unwindinfo_data.GetByteSize() != m_section_sp->GetByteSize())
+ return;
+ m_unwindinfo_data_computed = true;
+ }
+
+ if (m_unwindinfo_data.GetByteSize() > 0) {
+ offset_t offset = 0;
+
+ // struct unwind_info_section_header
+ // {
+ // uint32_t version; // UNWIND_SECTION_VERSION
+ // uint32_t commonEncodingsArraySectionOffset;
+ // uint32_t commonEncodingsArrayCount;
+ // uint32_t personalityArraySectionOffset;
+ // uint32_t personalityArrayCount;
+ // uint32_t indexSectionOffset;
+ // uint32_t indexCount;
+
+ m_unwind_header.version = m_unwindinfo_data.GetU32(&offset);
+ m_unwind_header.common_encodings_array_offset =
+ m_unwindinfo_data.GetU32(&offset);
+ m_unwind_header.common_encodings_array_count =
+ m_unwindinfo_data.GetU32(&offset);
+ m_unwind_header.personality_array_offset =
+ m_unwindinfo_data.GetU32(&offset);
+ m_unwind_header.personality_array_count = m_unwindinfo_data.GetU32(&offset);
+ uint32_t indexSectionOffset = m_unwindinfo_data.GetU32(&offset);
+
+ uint32_t indexCount = m_unwindinfo_data.GetU32(&offset);
+
+ if (m_unwind_header.common_encodings_array_offset >
+ m_unwindinfo_data.GetByteSize() ||
+ m_unwind_header.personality_array_offset >
+ m_unwindinfo_data.GetByteSize() ||
+ indexSectionOffset > m_unwindinfo_data.GetByteSize() ||
+ offset > m_unwindinfo_data.GetByteSize()) {
+ Host::SystemLog(Host::eSystemLogError, "error: Invalid offset "
+ "encountered in compact unwind "
+ "info, skipping\n");
+ // don't trust anything from this compact_unwind section if it looks
+ // blatantly invalid data in the header.
+ m_indexes_computed = eLazyBoolNo;
+ return;
+ }
+
+ // Parse the basic information from the indexes We wait to scan the second
+ // level page info until it's needed
+
+ // struct unwind_info_section_header_index_entry {
+ // uint32_t functionOffset;
+ // uint32_t secondLevelPagesSectionOffset;
+ // uint32_t lsdaIndexArraySectionOffset;
+ // };
+
+ bool clear_address_zeroth_bit = false;
+ if (ArchSpec arch = m_objfile.GetArchitecture()) {
+ if (arch.GetTriple().getArch() == llvm::Triple::arm ||
+ arch.GetTriple().getArch() == llvm::Triple::thumb)
+ clear_address_zeroth_bit = true;
+ }
+
+ offset = indexSectionOffset;
+ for (uint32_t idx = 0; idx < indexCount; idx++) {
+ uint32_t function_offset =
+ m_unwindinfo_data.GetU32(&offset); // functionOffset
+ uint32_t second_level_offset =
+ m_unwindinfo_data.GetU32(&offset); // secondLevelPagesSectionOffset
+ uint32_t lsda_offset =
+ m_unwindinfo_data.GetU32(&offset); // lsdaIndexArraySectionOffset
+
+ if (second_level_offset > m_section_sp->GetByteSize() ||
+ lsda_offset > m_section_sp->GetByteSize()) {
+ m_indexes_computed = eLazyBoolNo;
+ }
+
+ if (clear_address_zeroth_bit)
+ function_offset &= ~1ull;
+
+ UnwindIndex this_index;
+ this_index.function_offset = function_offset;
+ this_index.second_level = second_level_offset;
+ this_index.lsda_array_start = lsda_offset;
+
+ if (m_indexes.size() > 0) {
+ m_indexes[m_indexes.size() - 1].lsda_array_end = lsda_offset;
+ }
+
+ if (second_level_offset == 0) {
+ this_index.sentinal_entry = true;
+ }
+
+ m_indexes.push_back(this_index);
+ }
+ m_indexes_computed = eLazyBoolYes;
+ } else {
+ m_indexes_computed = eLazyBoolNo;
+ }
+}
+
+uint32_t CompactUnwindInfo::GetLSDAForFunctionOffset(uint32_t lsda_offset,
+ uint32_t lsda_count,
+ uint32_t function_offset) {
+ // struct unwind_info_section_header_lsda_index_entry {
+ // uint32_t functionOffset;
+ // uint32_t lsdaOffset;
+ // };
+
+ offset_t first_entry = lsda_offset;
+ uint32_t low = 0;
+ uint32_t high = lsda_count;
+ while (low < high) {
+ uint32_t mid = (low + high) / 2;
+ offset_t offset = first_entry + (mid * 8);
+ uint32_t mid_func_offset =
+ m_unwindinfo_data.GetU32(&offset); // functionOffset
+ uint32_t mid_lsda_offset = m_unwindinfo_data.GetU32(&offset); // lsdaOffset
+ if (mid_func_offset == function_offset) {
+ return mid_lsda_offset;
+ }
+ if (mid_func_offset < function_offset) {
+ low = mid + 1;
+ } else {
+ high = mid;
+ }
+ }
+ return 0;
+}
+
+lldb::offset_t CompactUnwindInfo::BinarySearchRegularSecondPage(
+ uint32_t entry_page_offset, uint32_t entry_count, uint32_t function_offset,
+ uint32_t *entry_func_start_offset, uint32_t *entry_func_end_offset) {
+ // typedef uint32_t compact_unwind_encoding_t;
+ // struct unwind_info_regular_second_level_entry {
+ // uint32_t functionOffset;
+ // compact_unwind_encoding_t encoding;
+
+ offset_t first_entry = entry_page_offset;
+
+ uint32_t low = 0;
+ uint32_t high = entry_count;
+ uint32_t last = high - 1;
+ while (low < high) {
+ uint32_t mid = (low + high) / 2;
+ offset_t offset = first_entry + (mid * 8);
+ uint32_t mid_func_offset =
+ m_unwindinfo_data.GetU32(&offset); // functionOffset
+ uint32_t next_func_offset = 0;
+ if (mid < last) {
+ offset = first_entry + ((mid + 1) * 8);
+ next_func_offset = m_unwindinfo_data.GetU32(&offset); // functionOffset
+ }
+ if (mid_func_offset <= function_offset) {
+ if (mid == last || (next_func_offset > function_offset)) {
+ if (entry_func_start_offset)
+ *entry_func_start_offset = mid_func_offset;
+ if (mid != last && entry_func_end_offset)
+ *entry_func_end_offset = next_func_offset;
+ return first_entry + (mid * 8);
+ } else {
+ low = mid + 1;
+ }
+ } else {
+ high = mid;
+ }
+ }
+ return LLDB_INVALID_OFFSET;
+}
+
+uint32_t CompactUnwindInfo::BinarySearchCompressedSecondPage(
+ uint32_t entry_page_offset, uint32_t entry_count,
+ uint32_t function_offset_to_find, uint32_t function_offset_base,
+ uint32_t *entry_func_start_offset, uint32_t *entry_func_end_offset) {
+ offset_t first_entry = entry_page_offset;
+
+ uint32_t low = 0;
+ uint32_t high = entry_count;
+ uint32_t last = high - 1;
+ while (low < high) {
+ uint32_t mid = (low + high) / 2;
+ offset_t offset = first_entry + (mid * 4);
+ uint32_t entry = m_unwindinfo_data.GetU32(&offset); // entry
+ uint32_t mid_func_offset = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry);
+ mid_func_offset += function_offset_base;
+ uint32_t next_func_offset = 0;
+ if (mid < last) {
+ offset = first_entry + ((mid + 1) * 4);
+ uint32_t next_entry = m_unwindinfo_data.GetU32(&offset); // entry
+ next_func_offset = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(next_entry);
+ next_func_offset += function_offset_base;
+ }
+ if (mid_func_offset <= function_offset_to_find) {
+ if (mid == last || (next_func_offset > function_offset_to_find)) {
+ if (entry_func_start_offset)
+ *entry_func_start_offset = mid_func_offset;
+ if (mid != last && entry_func_end_offset)
+ *entry_func_end_offset = next_func_offset;
+ return UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry);
+ } else {
+ low = mid + 1;
+ }
+ } else {
+ high = mid;
+ }
+ }
+
+ return UINT32_MAX;
+}
+
+bool CompactUnwindInfo::GetCompactUnwindInfoForFunction(
+ Target &target, Address address, FunctionInfo &unwind_info) {
+ unwind_info.encoding = 0;
+ unwind_info.lsda_address.Clear();
+ unwind_info.personality_ptr_address.Clear();
+
+ if (!IsValid(target.GetProcessSP()))
+ return false;
+
+ addr_t text_section_file_address = LLDB_INVALID_ADDRESS;
+ SectionList *sl = m_objfile.GetSectionList();
+ if (sl) {
+ SectionSP text_sect = sl->FindSectionByType(eSectionTypeCode, true);
+ if (text_sect.get()) {
+ text_section_file_address = text_sect->GetFileAddress();
+ }
+ }
+ if (text_section_file_address == LLDB_INVALID_ADDRESS)
+ return false;
+
+ addr_t function_offset =
+ address.GetFileAddress() - m_objfile.GetBaseAddress().GetFileAddress();
+
+ UnwindIndex key;
+ key.function_offset = function_offset;
+
+ std::vector<UnwindIndex>::const_iterator it;
+ it = std::lower_bound(m_indexes.begin(), m_indexes.end(), key);
+ if (it == m_indexes.end()) {
+ return false;
+ }
+
+ if (it->function_offset != key.function_offset) {
+ if (it != m_indexes.begin())
+ --it;
+ }
+
+ if (it->sentinal_entry) {
+ return false;
+ }
+
+ auto next_it = it + 1;
+ if (next_it != m_indexes.end()) {
+ // initialize the function offset end range to be the start of the next
+ // index offset. If we find an entry which is at the end of the index
+ // table, this will establish the range end.
+ unwind_info.valid_range_offset_end = next_it->function_offset;
+ }
+
+ offset_t second_page_offset = it->second_level;
+ offset_t lsda_array_start = it->lsda_array_start;
+ offset_t lsda_array_count = (it->lsda_array_end - it->lsda_array_start) / 8;
+
+ offset_t offset = second_page_offset;
+ uint32_t kind = m_unwindinfo_data.GetU32(
+ &offset); // UNWIND_SECOND_LEVEL_REGULAR or UNWIND_SECOND_LEVEL_COMPRESSED
+
+ if (kind == UNWIND_SECOND_LEVEL_REGULAR) {
+ // struct unwind_info_regular_second_level_page_header {
+ // uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR
+ // uint16_t entryPageOffset;
+ // uint16_t entryCount;
+
+ // typedef uint32_t compact_unwind_encoding_t;
+ // struct unwind_info_regular_second_level_entry {
+ // uint32_t functionOffset;
+ // compact_unwind_encoding_t encoding;
+
+ uint16_t entry_page_offset =
+ m_unwindinfo_data.GetU16(&offset); // entryPageOffset
+ uint16_t entry_count = m_unwindinfo_data.GetU16(&offset); // entryCount
+
+ offset_t entry_offset = BinarySearchRegularSecondPage(
+ second_page_offset + entry_page_offset, entry_count, function_offset,
+ &unwind_info.valid_range_offset_start,
+ &unwind_info.valid_range_offset_end);
+ if (entry_offset == LLDB_INVALID_OFFSET) {
+ return false;
+ }
+ entry_offset += 4; // skip over functionOffset
+ unwind_info.encoding = m_unwindinfo_data.GetU32(&entry_offset); // encoding
+ if (unwind_info.encoding & UNWIND_HAS_LSDA) {
+ SectionList *sl = m_objfile.GetSectionList();
+ if (sl) {
+ uint32_t lsda_offset = GetLSDAForFunctionOffset(
+ lsda_array_start, lsda_array_count, function_offset);
+ addr_t objfile_base_address =
+ m_objfile.GetBaseAddress().GetFileAddress();
+ unwind_info.lsda_address.ResolveAddressUsingFileSections(
+ objfile_base_address + lsda_offset, sl);
+ }
+ }
+ if (unwind_info.encoding & UNWIND_PERSONALITY_MASK) {
+ uint32_t personality_index =
+ EXTRACT_BITS(unwind_info.encoding, UNWIND_PERSONALITY_MASK);
+
+ if (personality_index > 0) {
+ personality_index--;
+ if (personality_index < m_unwind_header.personality_array_count) {
+ offset_t offset = m_unwind_header.personality_array_offset;
+ offset += 4 * personality_index;
+ SectionList *sl = m_objfile.GetSectionList();
+ if (sl) {
+ uint32_t personality_offset = m_unwindinfo_data.GetU32(&offset);
+ addr_t objfile_base_address =
+ m_objfile.GetBaseAddress().GetFileAddress();
+ unwind_info.personality_ptr_address.ResolveAddressUsingFileSections(
+ objfile_base_address + personality_offset, sl);
+ }
+ }
+ }
+ }
+ return true;
+ } else if (kind == UNWIND_SECOND_LEVEL_COMPRESSED) {
+ // struct unwind_info_compressed_second_level_page_header {
+ // uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED
+ // uint16_t entryPageOffset; // offset from this 2nd lvl page
+ // idx to array of entries
+ // // (an entry has a function
+ // offset and index into the
+ // encodings)
+ // // NB function offset from the
+ // entry in the compressed page
+ // // must be added to the index's
+ // functionOffset value.
+ // uint16_t entryCount;
+ // uint16_t encodingsPageOffset; // offset from this 2nd lvl page
+ // idx to array of encodings
+ // uint16_t encodingsCount;
+
+ uint16_t entry_page_offset =
+ m_unwindinfo_data.GetU16(&offset); // entryPageOffset
+ uint16_t entry_count = m_unwindinfo_data.GetU16(&offset); // entryCount
+ uint16_t encodings_page_offset =
+ m_unwindinfo_data.GetU16(&offset); // encodingsPageOffset
+ uint16_t encodings_count =
+ m_unwindinfo_data.GetU16(&offset); // encodingsCount
+
+ uint32_t encoding_index = BinarySearchCompressedSecondPage(
+ second_page_offset + entry_page_offset, entry_count, function_offset,
+ it->function_offset, &unwind_info.valid_range_offset_start,
+ &unwind_info.valid_range_offset_end);
+ if (encoding_index == UINT32_MAX ||
+ encoding_index >=
+ encodings_count + m_unwind_header.common_encodings_array_count) {
+ return false;
+ }
+ uint32_t encoding = 0;
+ if (encoding_index < m_unwind_header.common_encodings_array_count) {
+ offset = m_unwind_header.common_encodings_array_offset +
+ (encoding_index * sizeof(uint32_t));
+ encoding = m_unwindinfo_data.GetU32(
+ &offset); // encoding entry from the commonEncodingsArray
+ } else {
+ uint32_t page_specific_entry_index =
+ encoding_index - m_unwind_header.common_encodings_array_count;
+ offset = second_page_offset + encodings_page_offset +
+ (page_specific_entry_index * sizeof(uint32_t));
+ encoding = m_unwindinfo_data.GetU32(
+ &offset); // encoding entry from the page-specific encoding array
+ }
+ if (encoding == 0)
+ return false;
+
+ unwind_info.encoding = encoding;
+ if (unwind_info.encoding & UNWIND_HAS_LSDA) {
+ SectionList *sl = m_objfile.GetSectionList();
+ if (sl) {
+ uint32_t lsda_offset = GetLSDAForFunctionOffset(
+ lsda_array_start, lsda_array_count, function_offset);
+ addr_t objfile_base_address =
+ m_objfile.GetBaseAddress().GetFileAddress();
+ unwind_info.lsda_address.ResolveAddressUsingFileSections(
+ objfile_base_address + lsda_offset, sl);
+ }
+ }
+ if (unwind_info.encoding & UNWIND_PERSONALITY_MASK) {
+ uint32_t personality_index =
+ EXTRACT_BITS(unwind_info.encoding, UNWIND_PERSONALITY_MASK);
+
+ if (personality_index > 0) {
+ personality_index--;
+ if (personality_index < m_unwind_header.personality_array_count) {
+ offset_t offset = m_unwind_header.personality_array_offset;
+ offset += 4 * personality_index;
+ SectionList *sl = m_objfile.GetSectionList();
+ if (sl) {
+ uint32_t personality_offset = m_unwindinfo_data.GetU32(&offset);
+ addr_t objfile_base_address =
+ m_objfile.GetBaseAddress().GetFileAddress();
+ unwind_info.personality_ptr_address.ResolveAddressUsingFileSections(
+ objfile_base_address + personality_offset, sl);
+ }
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+enum x86_64_eh_regnum {
+ rax = 0,
+ rdx = 1,
+ rcx = 2,
+ rbx = 3,
+ rsi = 4,
+ rdi = 5,
+ rbp = 6,
+ rsp = 7,
+ r8 = 8,
+ r9 = 9,
+ r10 = 10,
+ r11 = 11,
+ r12 = 12,
+ r13 = 13,
+ r14 = 14,
+ r15 = 15,
+ rip = 16 // this is officially the Return Address register number, but close
+ // enough
+};
+
+// Convert the compact_unwind_info.h register numbering scheme to
+// eRegisterKindEHFrame (eh_frame) register numbering scheme.
+uint32_t translate_to_eh_frame_regnum_x86_64(uint32_t unwind_regno) {
+ switch (unwind_regno) {
+ case UNWIND_X86_64_REG_RBX:
+ return x86_64_eh_regnum::rbx;
+ case UNWIND_X86_64_REG_R12:
+ return x86_64_eh_regnum::r12;
+ case UNWIND_X86_64_REG_R13:
+ return x86_64_eh_regnum::r13;
+ case UNWIND_X86_64_REG_R14:
+ return x86_64_eh_regnum::r14;
+ case UNWIND_X86_64_REG_R15:
+ return x86_64_eh_regnum::r15;
+ case UNWIND_X86_64_REG_RBP:
+ return x86_64_eh_regnum::rbp;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+}
+
+bool CompactUnwindInfo::CreateUnwindPlan_x86_64(Target &target,
+ FunctionInfo &function_info,
+ UnwindPlan &unwind_plan,
+ Address pc_or_function_start) {
+ unwind_plan.SetSourceName("compact unwind info");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolYes);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ unwind_plan.SetRegisterKind(eRegisterKindEHFrame);
+
+ unwind_plan.SetLSDAAddress(function_info.lsda_address);
+ unwind_plan.SetPersonalityFunctionPtr(function_info.personality_ptr_address);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ const int wordsize = 8;
+ int mode = function_info.encoding & UNWIND_X86_64_MODE_MASK;
+ switch (mode) {
+ case UNWIND_X86_64_MODE_RBP_FRAME: {
+ row->GetCFAValue().SetIsRegisterPlusOffset(
+ translate_to_eh_frame_regnum_x86_64(UNWIND_X86_64_REG_RBP),
+ 2 * wordsize);
+ row->SetOffset(0);
+ row->SetRegisterLocationToAtCFAPlusOffset(x86_64_eh_regnum::rbp,
+ wordsize * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(x86_64_eh_regnum::rip,
+ wordsize * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(x86_64_eh_regnum::rsp, 0, true);
+
+ uint32_t saved_registers_offset =
+ EXTRACT_BITS(function_info.encoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
+
+ uint32_t saved_registers_locations =
+ EXTRACT_BITS(function_info.encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
+
+ saved_registers_offset += 2;
+
+ for (int i = 0; i < 5; i++) {
+ uint32_t regnum = saved_registers_locations & 0x7;
+ switch (regnum) {
+ case UNWIND_X86_64_REG_NONE:
+ break;
+ case UNWIND_X86_64_REG_RBX:
+ case UNWIND_X86_64_REG_R12:
+ case UNWIND_X86_64_REG_R13:
+ case UNWIND_X86_64_REG_R14:
+ case UNWIND_X86_64_REG_R15:
+ row->SetRegisterLocationToAtCFAPlusOffset(
+ translate_to_eh_frame_regnum_x86_64(regnum),
+ wordsize * -saved_registers_offset, true);
+ break;
+ }
+ saved_registers_offset--;
+ saved_registers_locations >>= 3;
+ }
+ unwind_plan.AppendRow(row);
+ return true;
+ } break;
+
+ case UNWIND_X86_64_MODE_STACK_IND: {
+ // The clang in Xcode 6 is emitting incorrect compact unwind encodings for
+ // this style of unwind. It was fixed in llvm r217020. The clang in Xcode
+ // 7 has this fixed.
+ return false;
+ } break;
+
+ case UNWIND_X86_64_MODE_STACK_IMMD: {
+ uint32_t stack_size = EXTRACT_BITS(function_info.encoding,
+ UNWIND_X86_64_FRAMELESS_STACK_SIZE);
+ uint32_t register_count = EXTRACT_BITS(
+ function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
+ uint32_t permutation = EXTRACT_BITS(
+ function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
+
+ if (mode == UNWIND_X86_64_MODE_STACK_IND &&
+ function_info.valid_range_offset_start != 0) {
+ uint32_t stack_adjust = EXTRACT_BITS(
+ function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
+
+ // offset into the function instructions; 0 == beginning of first
+ // instruction
+ uint32_t offset_to_subl_insn = EXTRACT_BITS(
+ function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
+
+ SectionList *sl = m_objfile.GetSectionList();
+ if (sl) {
+ ProcessSP process_sp = target.GetProcessSP();
+ if (process_sp) {
+ Address subl_payload_addr(function_info.valid_range_offset_start, sl);
+ subl_payload_addr.Slide(offset_to_subl_insn);
+ Status error;
+ uint64_t large_stack_size = process_sp->ReadUnsignedIntegerFromMemory(
+ subl_payload_addr.GetLoadAddress(&target), 4, 0, error);
+ if (large_stack_size != 0 && error.Success()) {
+ // Got the large stack frame size correctly - use it
+ stack_size = large_stack_size + (stack_adjust * wordsize);
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ int32_t offset = mode == UNWIND_X86_64_MODE_STACK_IND
+ ? stack_size
+ : stack_size * wordsize;
+ row->GetCFAValue().SetIsRegisterPlusOffset(x86_64_eh_regnum::rsp, offset);
+
+ row->SetOffset(0);
+ row->SetRegisterLocationToAtCFAPlusOffset(x86_64_eh_regnum::rip,
+ wordsize * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(x86_64_eh_regnum::rsp, 0, true);
+
+ if (register_count > 0) {
+
+ // We need to include (up to) 6 registers in 10 bits. That would be 18
+ // bits if we just used 3 bits per reg to indicate the order they're
+ // saved on the stack.
+ //
+ // This is done with Lehmer code permutation, e.g. see
+ // http://stackoverflow.com/questions/1506078/fast-permutation-number-
+ // permutation-mapping-algorithms
+ int permunreg[6] = {0, 0, 0, 0, 0, 0};
+
+ // This decodes the variable-base number in the 10 bits and gives us the
+ // Lehmer code sequence which can then be decoded.
+
+ switch (register_count) {
+ case 6:
+ permunreg[0] = permutation / 120; // 120 == 5!
+ permutation -= (permunreg[0] * 120);
+ permunreg[1] = permutation / 24; // 24 == 4!
+ permutation -= (permunreg[1] * 24);
+ permunreg[2] = permutation / 6; // 6 == 3!
+ permutation -= (permunreg[2] * 6);
+ permunreg[3] = permutation / 2; // 2 == 2!
+ permutation -= (permunreg[3] * 2);
+ permunreg[4] = permutation; // 1 == 1!
+ permunreg[5] = 0;
+ break;
+ case 5:
+ permunreg[0] = permutation / 120;
+ permutation -= (permunreg[0] * 120);
+ permunreg[1] = permutation / 24;
+ permutation -= (permunreg[1] * 24);
+ permunreg[2] = permutation / 6;
+ permutation -= (permunreg[2] * 6);
+ permunreg[3] = permutation / 2;
+ permutation -= (permunreg[3] * 2);
+ permunreg[4] = permutation;
+ break;
+ case 4:
+ permunreg[0] = permutation / 60;
+ permutation -= (permunreg[0] * 60);
+ permunreg[1] = permutation / 12;
+ permutation -= (permunreg[1] * 12);
+ permunreg[2] = permutation / 3;
+ permutation -= (permunreg[2] * 3);
+ permunreg[3] = permutation;
+ break;
+ case 3:
+ permunreg[0] = permutation / 20;
+ permutation -= (permunreg[0] * 20);
+ permunreg[1] = permutation / 4;
+ permutation -= (permunreg[1] * 4);
+ permunreg[2] = permutation;
+ break;
+ case 2:
+ permunreg[0] = permutation / 5;
+ permutation -= (permunreg[0] * 5);
+ permunreg[1] = permutation;
+ break;
+ case 1:
+ permunreg[0] = permutation;
+ break;
+ }
+
+ // Decode the Lehmer code for this permutation of the registers v.
+ // http://en.wikipedia.org/wiki/Lehmer_code
+
+ int registers[6] = {UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE,
+ UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE,
+ UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE};
+ bool used[7] = {false, false, false, false, false, false, false};
+ for (uint32_t i = 0; i < register_count; i++) {
+ int renum = 0;
+ for (int j = 1; j < 7; j++) {
+ if (!used[j]) {
+ if (renum == permunreg[i]) {
+ registers[i] = j;
+ used[j] = true;
+ break;
+ }
+ renum++;
+ }
+ }
+ }
+
+ uint32_t saved_registers_offset = 1;
+ saved_registers_offset++;
+
+ for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) {
+ switch (registers[i]) {
+ case UNWIND_X86_64_REG_NONE:
+ break;
+ case UNWIND_X86_64_REG_RBX:
+ case UNWIND_X86_64_REG_R12:
+ case UNWIND_X86_64_REG_R13:
+ case UNWIND_X86_64_REG_R14:
+ case UNWIND_X86_64_REG_R15:
+ case UNWIND_X86_64_REG_RBP:
+ row->SetRegisterLocationToAtCFAPlusOffset(
+ translate_to_eh_frame_regnum_x86_64(registers[i]),
+ wordsize * -saved_registers_offset, true);
+ saved_registers_offset++;
+ break;
+ }
+ }
+ }
+ unwind_plan.AppendRow(row);
+ return true;
+ } break;
+
+ case UNWIND_X86_64_MODE_DWARF: {
+ return false;
+ } break;
+
+ case 0: {
+ return false;
+ } break;
+ }
+ return false;
+}
+
+enum i386_eh_regnum {
+ eax = 0,
+ ecx = 1,
+ edx = 2,
+ ebx = 3,
+ ebp = 4,
+ esp = 5,
+ esi = 6,
+ edi = 7,
+ eip = 8 // this is officially the Return Address register number, but close
+ // enough
+};
+
+// Convert the compact_unwind_info.h register numbering scheme to
+// eRegisterKindEHFrame (eh_frame) register numbering scheme.
+uint32_t translate_to_eh_frame_regnum_i386(uint32_t unwind_regno) {
+ switch (unwind_regno) {
+ case UNWIND_X86_REG_EBX:
+ return i386_eh_regnum::ebx;
+ case UNWIND_X86_REG_ECX:
+ return i386_eh_regnum::ecx;
+ case UNWIND_X86_REG_EDX:
+ return i386_eh_regnum::edx;
+ case UNWIND_X86_REG_EDI:
+ return i386_eh_regnum::edi;
+ case UNWIND_X86_REG_ESI:
+ return i386_eh_regnum::esi;
+ case UNWIND_X86_REG_EBP:
+ return i386_eh_regnum::ebp;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+}
+
+bool CompactUnwindInfo::CreateUnwindPlan_i386(Target &target,
+ FunctionInfo &function_info,
+ UnwindPlan &unwind_plan,
+ Address pc_or_function_start) {
+ unwind_plan.SetSourceName("compact unwind info");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolYes);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ unwind_plan.SetRegisterKind(eRegisterKindEHFrame);
+
+ unwind_plan.SetLSDAAddress(function_info.lsda_address);
+ unwind_plan.SetPersonalityFunctionPtr(function_info.personality_ptr_address);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ const int wordsize = 4;
+ int mode = function_info.encoding & UNWIND_X86_MODE_MASK;
+ switch (mode) {
+ case UNWIND_X86_MODE_EBP_FRAME: {
+ row->GetCFAValue().SetIsRegisterPlusOffset(
+ translate_to_eh_frame_regnum_i386(UNWIND_X86_REG_EBP), 2 * wordsize);
+ row->SetOffset(0);
+ row->SetRegisterLocationToAtCFAPlusOffset(i386_eh_regnum::ebp,
+ wordsize * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(i386_eh_regnum::eip,
+ wordsize * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(i386_eh_regnum::esp, 0, true);
+
+ uint32_t saved_registers_offset =
+ EXTRACT_BITS(function_info.encoding, UNWIND_X86_EBP_FRAME_OFFSET);
+
+ uint32_t saved_registers_locations =
+ EXTRACT_BITS(function_info.encoding, UNWIND_X86_EBP_FRAME_REGISTERS);
+
+ saved_registers_offset += 2;
+
+ for (int i = 0; i < 5; i++) {
+ uint32_t regnum = saved_registers_locations & 0x7;
+ switch (regnum) {
+ case UNWIND_X86_REG_NONE:
+ break;
+ case UNWIND_X86_REG_EBX:
+ case UNWIND_X86_REG_ECX:
+ case UNWIND_X86_REG_EDX:
+ case UNWIND_X86_REG_EDI:
+ case UNWIND_X86_REG_ESI:
+ row->SetRegisterLocationToAtCFAPlusOffset(
+ translate_to_eh_frame_regnum_i386(regnum),
+ wordsize * -saved_registers_offset, true);
+ break;
+ }
+ saved_registers_offset--;
+ saved_registers_locations >>= 3;
+ }
+ unwind_plan.AppendRow(row);
+ return true;
+ } break;
+
+ case UNWIND_X86_MODE_STACK_IND:
+ case UNWIND_X86_MODE_STACK_IMMD: {
+ uint32_t stack_size =
+ EXTRACT_BITS(function_info.encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
+ uint32_t register_count = EXTRACT_BITS(
+ function_info.encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
+ uint32_t permutation = EXTRACT_BITS(
+ function_info.encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
+
+ if (mode == UNWIND_X86_MODE_STACK_IND &&
+ function_info.valid_range_offset_start != 0) {
+ uint32_t stack_adjust = EXTRACT_BITS(function_info.encoding,
+ UNWIND_X86_FRAMELESS_STACK_ADJUST);
+
+ // offset into the function instructions; 0 == beginning of first
+ // instruction
+ uint32_t offset_to_subl_insn =
+ EXTRACT_BITS(function_info.encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
+
+ SectionList *sl = m_objfile.GetSectionList();
+ if (sl) {
+ ProcessSP process_sp = target.GetProcessSP();
+ if (process_sp) {
+ Address subl_payload_addr(function_info.valid_range_offset_start, sl);
+ subl_payload_addr.Slide(offset_to_subl_insn);
+ Status error;
+ uint64_t large_stack_size = process_sp->ReadUnsignedIntegerFromMemory(
+ subl_payload_addr.GetLoadAddress(&target), 4, 0, error);
+ if (large_stack_size != 0 && error.Success()) {
+ // Got the large stack frame size correctly - use it
+ stack_size = large_stack_size + (stack_adjust * wordsize);
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ int32_t offset =
+ mode == UNWIND_X86_MODE_STACK_IND ? stack_size : stack_size * wordsize;
+ row->GetCFAValue().SetIsRegisterPlusOffset(i386_eh_regnum::esp, offset);
+ row->SetOffset(0);
+ row->SetRegisterLocationToAtCFAPlusOffset(i386_eh_regnum::eip,
+ wordsize * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(i386_eh_regnum::esp, 0, true);
+
+ if (register_count > 0) {
+
+ // We need to include (up to) 6 registers in 10 bits. That would be 18
+ // bits if we just used 3 bits per reg to indicate the order they're
+ // saved on the stack.
+ //
+ // This is done with Lehmer code permutation, e.g. see
+ // http://stackoverflow.com/questions/1506078/fast-permutation-number-
+ // permutation-mapping-algorithms
+ int permunreg[6] = {0, 0, 0, 0, 0, 0};
+
+ // This decodes the variable-base number in the 10 bits and gives us the
+ // Lehmer code sequence which can then be decoded.
+
+ switch (register_count) {
+ case 6:
+ permunreg[0] = permutation / 120; // 120 == 5!
+ permutation -= (permunreg[0] * 120);
+ permunreg[1] = permutation / 24; // 24 == 4!
+ permutation -= (permunreg[1] * 24);
+ permunreg[2] = permutation / 6; // 6 == 3!
+ permutation -= (permunreg[2] * 6);
+ permunreg[3] = permutation / 2; // 2 == 2!
+ permutation -= (permunreg[3] * 2);
+ permunreg[4] = permutation; // 1 == 1!
+ permunreg[5] = 0;
+ break;
+ case 5:
+ permunreg[0] = permutation / 120;
+ permutation -= (permunreg[0] * 120);
+ permunreg[1] = permutation / 24;
+ permutation -= (permunreg[1] * 24);
+ permunreg[2] = permutation / 6;
+ permutation -= (permunreg[2] * 6);
+ permunreg[3] = permutation / 2;
+ permutation -= (permunreg[3] * 2);
+ permunreg[4] = permutation;
+ break;
+ case 4:
+ permunreg[0] = permutation / 60;
+ permutation -= (permunreg[0] * 60);
+ permunreg[1] = permutation / 12;
+ permutation -= (permunreg[1] * 12);
+ permunreg[2] = permutation / 3;
+ permutation -= (permunreg[2] * 3);
+ permunreg[3] = permutation;
+ break;
+ case 3:
+ permunreg[0] = permutation / 20;
+ permutation -= (permunreg[0] * 20);
+ permunreg[1] = permutation / 4;
+ permutation -= (permunreg[1] * 4);
+ permunreg[2] = permutation;
+ break;
+ case 2:
+ permunreg[0] = permutation / 5;
+ permutation -= (permunreg[0] * 5);
+ permunreg[1] = permutation;
+ break;
+ case 1:
+ permunreg[0] = permutation;
+ break;
+ }
+
+ // Decode the Lehmer code for this permutation of the registers v.
+ // http://en.wikipedia.org/wiki/Lehmer_code
+
+ int registers[6] = {UNWIND_X86_REG_NONE, UNWIND_X86_REG_NONE,
+ UNWIND_X86_REG_NONE, UNWIND_X86_REG_NONE,
+ UNWIND_X86_REG_NONE, UNWIND_X86_REG_NONE};
+ bool used[7] = {false, false, false, false, false, false, false};
+ for (uint32_t i = 0; i < register_count; i++) {
+ int renum = 0;
+ for (int j = 1; j < 7; j++) {
+ if (!used[j]) {
+ if (renum == permunreg[i]) {
+ registers[i] = j;
+ used[j] = true;
+ break;
+ }
+ renum++;
+ }
+ }
+ }
+
+ uint32_t saved_registers_offset = 1;
+ saved_registers_offset++;
+
+ for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) {
+ switch (registers[i]) {
+ case UNWIND_X86_REG_NONE:
+ break;
+ case UNWIND_X86_REG_EBX:
+ case UNWIND_X86_REG_ECX:
+ case UNWIND_X86_REG_EDX:
+ case UNWIND_X86_REG_EDI:
+ case UNWIND_X86_REG_ESI:
+ case UNWIND_X86_REG_EBP:
+ row->SetRegisterLocationToAtCFAPlusOffset(
+ translate_to_eh_frame_regnum_i386(registers[i]),
+ wordsize * -saved_registers_offset, true);
+ saved_registers_offset++;
+ break;
+ }
+ }
+ }
+
+ unwind_plan.AppendRow(row);
+ return true;
+ } break;
+
+ case UNWIND_X86_MODE_DWARF: {
+ return false;
+ } break;
+ }
+ return false;
+}
+
+// DWARF register numbers from "DWARF for the ARM 64-bit Architecture (AArch64)"
+// doc by ARM
+
+enum arm64_eh_regnum {
+ x19 = 19,
+ x20 = 20,
+ x21 = 21,
+ x22 = 22,
+ x23 = 23,
+ x24 = 24,
+ x25 = 25,
+ x26 = 26,
+ x27 = 27,
+ x28 = 28,
+
+ fp = 29,
+ ra = 30,
+ sp = 31,
+ pc = 32,
+
+ // Compact unwind encodes d8-d15 but we don't have eh_frame / dwarf reg #'s
+ // for the 64-bit fp regs. Normally in DWARF it's context sensitive - so it
+ // knows it is fetching a 32- or 64-bit quantity from reg v8 to indicate s0
+ // or d0 - but the unwinder is operating at a lower level and we'd try to
+ // fetch 128 bits if we were told that v8 were stored on the stack...
+ v8 = 72,
+ v9 = 73,
+ v10 = 74,
+ v11 = 75,
+ v12 = 76,
+ v13 = 77,
+ v14 = 78,
+ v15 = 79,
+};
+
+enum arm_eh_regnum {
+ arm_r0 = 0,
+ arm_r1 = 1,
+ arm_r2 = 2,
+ arm_r3 = 3,
+ arm_r4 = 4,
+ arm_r5 = 5,
+ arm_r6 = 6,
+ arm_r7 = 7,
+ arm_r8 = 8,
+ arm_r9 = 9,
+ arm_r10 = 10,
+ arm_r11 = 11,
+ arm_r12 = 12,
+
+ arm_sp = 13,
+ arm_lr = 14,
+ arm_pc = 15,
+
+ arm_d0 = 256,
+ arm_d1 = 257,
+ arm_d2 = 258,
+ arm_d3 = 259,
+ arm_d4 = 260,
+ arm_d5 = 261,
+ arm_d6 = 262,
+ arm_d7 = 263,
+ arm_d8 = 264,
+ arm_d9 = 265,
+ arm_d10 = 266,
+ arm_d11 = 267,
+ arm_d12 = 268,
+ arm_d13 = 269,
+ arm_d14 = 270,
+};
+
+bool CompactUnwindInfo::CreateUnwindPlan_arm64(Target &target,
+ FunctionInfo &function_info,
+ UnwindPlan &unwind_plan,
+ Address pc_or_function_start) {
+ unwind_plan.SetSourceName("compact unwind info");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolYes);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ unwind_plan.SetRegisterKind(eRegisterKindEHFrame);
+
+ unwind_plan.SetLSDAAddress(function_info.lsda_address);
+ unwind_plan.SetPersonalityFunctionPtr(function_info.personality_ptr_address);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ const int wordsize = 8;
+ int mode = function_info.encoding & UNWIND_ARM64_MODE_MASK;
+
+ if (mode == UNWIND_ARM64_MODE_DWARF)
+ return false;
+
+ if (mode == UNWIND_ARM64_MODE_FRAMELESS) {
+ row->SetOffset(0);
+
+ uint32_t stack_size =
+ (EXTRACT_BITS(function_info.encoding,
+ UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)) *
+ 16;
+
+ // Our previous Call Frame Address is the stack pointer plus the stack size
+ row->GetCFAValue().SetIsRegisterPlusOffset(arm64_eh_regnum::sp, stack_size);
+
+ // Our previous PC is in the LR
+ row->SetRegisterLocationToRegister(arm64_eh_regnum::pc, arm64_eh_regnum::ra,
+ true);
+
+ unwind_plan.AppendRow(row);
+ return true;
+ }
+
+ // Should not be possible
+ if (mode != UNWIND_ARM64_MODE_FRAME)
+ return false;
+
+ // mode == UNWIND_ARM64_MODE_FRAME
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(arm64_eh_regnum::fp, 2 * wordsize);
+ row->SetOffset(0);
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::fp, wordsize * -2,
+ true);
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::pc, wordsize * -1,
+ true);
+ row->SetRegisterLocationToIsCFAPlusOffset(arm64_eh_regnum::sp, 0, true);
+
+ int reg_pairs_saved_count = 1;
+
+ uint32_t saved_register_bits = function_info.encoding & 0xfff;
+
+ if (saved_register_bits & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::x19, cfa_offset,
+ true);
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::x20, cfa_offset,
+ true);
+ reg_pairs_saved_count++;
+ }
+
+ if (saved_register_bits & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::x21, cfa_offset,
+ true);
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::x22, cfa_offset,
+ true);
+ reg_pairs_saved_count++;
+ }
+
+ if (saved_register_bits & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::x23, cfa_offset,
+ true);
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::x24, cfa_offset,
+ true);
+ reg_pairs_saved_count++;
+ }
+
+ if (saved_register_bits & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::x25, cfa_offset,
+ true);
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::x26, cfa_offset,
+ true);
+ reg_pairs_saved_count++;
+ }
+
+ if (saved_register_bits & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::x27, cfa_offset,
+ true);
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm64_eh_regnum::x28, cfa_offset,
+ true);
+ reg_pairs_saved_count++;
+ }
+
+ // If we use the v8-v15 regnums here, the unwinder will try to grab 128 bits
+ // off the stack;
+ // not sure if we have a good way to represent the 64-bitness of these saves.
+
+ if (saved_register_bits & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
+ reg_pairs_saved_count++;
+ }
+ if (saved_register_bits & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
+ reg_pairs_saved_count++;
+ }
+ if (saved_register_bits & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
+ reg_pairs_saved_count++;
+ }
+ if (saved_register_bits & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
+ reg_pairs_saved_count++;
+ }
+
+ unwind_plan.AppendRow(row);
+ return true;
+}
+
+bool CompactUnwindInfo::CreateUnwindPlan_armv7(Target &target,
+ FunctionInfo &function_info,
+ UnwindPlan &unwind_plan,
+ Address pc_or_function_start) {
+ unwind_plan.SetSourceName("compact unwind info");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolYes);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ unwind_plan.SetRegisterKind(eRegisterKindEHFrame);
+
+ unwind_plan.SetLSDAAddress(function_info.lsda_address);
+ unwind_plan.SetPersonalityFunctionPtr(function_info.personality_ptr_address);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ const int wordsize = 4;
+ int mode = function_info.encoding & UNWIND_ARM_MODE_MASK;
+
+ if (mode == UNWIND_ARM_MODE_DWARF)
+ return false;
+
+ uint32_t stack_adjust = (EXTRACT_BITS(function_info.encoding,
+ UNWIND_ARM_FRAME_STACK_ADJUST_MASK)) *
+ wordsize;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(arm_r7,
+ (2 * wordsize) + stack_adjust);
+ row->SetOffset(0);
+ row->SetRegisterLocationToAtCFAPlusOffset(
+ arm_r7, (wordsize * -2) - stack_adjust, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(
+ arm_pc, (wordsize * -1) - stack_adjust, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(arm_sp, 0, true);
+
+ int cfa_offset = -stack_adjust - (2 * wordsize);
+
+ uint32_t saved_register_bits = function_info.encoding & 0xff;
+
+ if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R6) {
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_r6, cfa_offset, true);
+ }
+
+ if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R5) {
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_r5, cfa_offset, true);
+ }
+
+ if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R4) {
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_r4, cfa_offset, true);
+ }
+
+ if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R12) {
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_r12, cfa_offset, true);
+ }
+
+ if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R11) {
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_r11, cfa_offset, true);
+ }
+
+ if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R10) {
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_r10, cfa_offset, true);
+ }
+
+ if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R9) {
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_r9, cfa_offset, true);
+ }
+
+ if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R8) {
+ cfa_offset -= wordsize;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_r8, cfa_offset, true);
+ }
+
+ if (mode == UNWIND_ARM_MODE_FRAME_D) {
+ uint32_t d_reg_bits =
+ EXTRACT_BITS(function_info.encoding, UNWIND_ARM_FRAME_D_REG_COUNT_MASK);
+ switch (d_reg_bits) {
+ case 0:
+ // vpush {d8}
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d8, cfa_offset, true);
+ break;
+ case 1:
+ // vpush {d10}
+ // vpush {d8}
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d10, cfa_offset, true);
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d8, cfa_offset, true);
+ break;
+ case 2:
+ // vpush {d12}
+ // vpush {d10}
+ // vpush {d8}
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d12, cfa_offset, true);
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d10, cfa_offset, true);
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d8, cfa_offset, true);
+ break;
+ case 3:
+ // vpush {d14}
+ // vpush {d12}
+ // vpush {d10}
+ // vpush {d8}
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d14, cfa_offset, true);
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d12, cfa_offset, true);
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d10, cfa_offset, true);
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d8, cfa_offset, true);
+ break;
+ case 4:
+ // vpush {d14}
+ // vpush {d12}
+ // sp = (sp - 24) & (-16);
+ // vst {d8, d9, d10}
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d14, cfa_offset, true);
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d12, cfa_offset, true);
+
+ // FIXME we don't have a way to represent reg saves at an specific
+ // alignment short of
+ // coming up with some DWARF location description.
+
+ break;
+ case 5:
+ // vpush {d14}
+ // sp = (sp - 40) & (-16);
+ // vst {d8, d9, d10, d11}
+ // vst {d12}
+
+ cfa_offset -= 8;
+ row->SetRegisterLocationToAtCFAPlusOffset(arm_d14, cfa_offset, true);
+
+ // FIXME we don't have a way to represent reg saves at an specific
+ // alignment short of
+ // coming up with some DWARF location description.
+
+ break;
+ case 6:
+ // sp = (sp - 56) & (-16);
+ // vst {d8, d9, d10, d11}
+ // vst {d12, d13, d14}
+
+ // FIXME we don't have a way to represent reg saves at an specific
+ // alignment short of
+ // coming up with some DWARF location description.
+
+ break;
+ case 7:
+ // sp = (sp - 64) & (-16);
+ // vst {d8, d9, d10, d11}
+ // vst {d12, d13, d14, d15}
+
+ // FIXME we don't have a way to represent reg saves at an specific
+ // alignment short of
+ // coming up with some DWARF location description.
+
+ break;
+ }
+ }
+
+ unwind_plan.AppendRow(row);
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/CompileUnit.cpp b/contrib/llvm-project/lldb/source/Symbol/CompileUnit.cpp
new file mode 100644
index 000000000000..5fb9b6b9f729
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/CompileUnit.cpp
@@ -0,0 +1,413 @@
+//===-- CompileUnit.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Language.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data,
+ const char *pathname, const lldb::user_id_t cu_sym_id,
+ lldb::LanguageType language,
+ lldb_private::LazyBool is_optimized)
+ : ModuleChild(module_sp), FileSpec(pathname), UserID(cu_sym_id),
+ m_user_data(user_data), m_language(language), m_flags(0),
+ m_support_files(), m_line_table_up(), m_variables(),
+ m_is_optimized(is_optimized) {
+ if (language != eLanguageTypeUnknown)
+ m_flags.Set(flagsParsedLanguage);
+ assert(module_sp);
+}
+
+CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data,
+ const FileSpec &fspec, const lldb::user_id_t cu_sym_id,
+ lldb::LanguageType language,
+ lldb_private::LazyBool is_optimized)
+ : ModuleChild(module_sp), FileSpec(fspec), UserID(cu_sym_id),
+ m_user_data(user_data), m_language(language), m_flags(0),
+ m_support_files(), m_line_table_up(), m_variables(),
+ m_is_optimized(is_optimized) {
+ if (language != eLanguageTypeUnknown)
+ m_flags.Set(flagsParsedLanguage);
+ assert(module_sp);
+}
+
+CompileUnit::~CompileUnit() {}
+
+void CompileUnit::CalculateSymbolContext(SymbolContext *sc) {
+ sc->comp_unit = this;
+ GetModule()->CalculateSymbolContext(sc);
+}
+
+ModuleSP CompileUnit::CalculateSymbolContextModule() { return GetModule(); }
+
+CompileUnit *CompileUnit::CalculateSymbolContextCompileUnit() { return this; }
+
+void CompileUnit::DumpSymbolContext(Stream *s) {
+ GetModule()->DumpSymbolContext(s);
+ s->Printf(", CompileUnit{0x%8.8" PRIx64 "}", GetID());
+}
+
+void CompileUnit::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) const {
+ const char *language = Language::GetNameForLanguageType(m_language);
+ *s << "id = " << (const UserID &)*this << ", file = \""
+ << (const FileSpec &)*this << "\", language = \"" << language << '"';
+}
+
+void CompileUnit::ForeachFunction(
+ llvm::function_ref<bool(const FunctionSP &)> lambda) const {
+ std::vector<lldb::FunctionSP> sorted_functions;
+ sorted_functions.reserve(m_functions_by_uid.size());
+ for (auto &p : m_functions_by_uid)
+ sorted_functions.push_back(p.second);
+ llvm::sort(sorted_functions.begin(), sorted_functions.end(),
+ [](const lldb::FunctionSP &a, const lldb::FunctionSP &b) {
+ return a->GetID() < b->GetID();
+ });
+
+ for (auto &f : sorted_functions)
+ if (lambda(f))
+ return;
+}
+
+// Dump the current contents of this object. No functions that cause on demand
+// parsing of functions, globals, statics are called, so this is a good
+// function to call to get an idea of the current contents of the CompileUnit
+// object.
+void CompileUnit::Dump(Stream *s, bool show_context) const {
+ const char *language = Language::GetNameForLanguageType(m_language);
+
+ s->Printf("%p: ", static_cast<const void *>(this));
+ s->Indent();
+ *s << "CompileUnit" << static_cast<const UserID &>(*this) << ", language = \""
+ << language << "\", file = '" << static_cast<const FileSpec &>(*this)
+ << "'\n";
+
+ // m_types.Dump(s);
+
+ if (m_variables.get()) {
+ s->IndentMore();
+ m_variables->Dump(s, show_context);
+ s->IndentLess();
+ }
+
+ if (!m_functions_by_uid.empty()) {
+ s->IndentMore();
+ ForeachFunction([&s, show_context](const FunctionSP &f) {
+ f->Dump(s, show_context);
+ return false;
+ });
+
+ s->IndentLess();
+ s->EOL();
+ }
+}
+
+// Add a function to this compile unit
+void CompileUnit::AddFunction(FunctionSP &funcSP) {
+ m_functions_by_uid[funcSP->GetID()] = funcSP;
+}
+
+// Find functions using the Mangled::Tokens token list. This function currently
+// implements an interactive approach designed to find all instances of certain
+// functions. It isn't designed to the quickest way to lookup functions as it
+// will need to iterate through all functions and see if they match, though it
+// does provide a powerful and context sensitive way to search for all
+// functions with a certain name, all functions in a namespace, or all
+// functions of a template type. See Mangled::Tokens::Parse() comments for more
+// information.
+//
+// The function prototype will need to change to return a list of results. It
+// was originally used to help debug the Mangled class and the
+// Mangled::Tokens::MatchesQuery() function and it currently will print out a
+// list of matching results for the functions that are currently in this
+// compile unit.
+//
+// A FindFunctions method should be called prior to this that takes
+// a regular function name (const char * or ConstString as a parameter) before
+// resorting to this slower but more complete function. The other FindFunctions
+// method should be able to take advantage of any accelerator tables available
+// in the debug information (which is parsed by the SymbolFile parser plug-ins
+// and registered with each Module).
+// void
+// CompileUnit::FindFunctions(const Mangled::Tokens& tokens)
+//{
+// if (!m_functions.empty())
+// {
+// Stream s(stdout);
+// std::vector<FunctionSP>::const_iterator pos;
+// std::vector<FunctionSP>::const_iterator end = m_functions.end();
+// for (pos = m_functions.begin(); pos != end; ++pos)
+// {
+// const ConstString& demangled = (*pos)->Mangled().Demangled();
+// if (demangled)
+// {
+// const Mangled::Tokens& func_tokens =
+// (*pos)->Mangled().GetTokens();
+// if (func_tokens.MatchesQuery (tokens))
+// s << "demangled MATCH found: " << demangled << "\n";
+// }
+// }
+// }
+//}
+
+FunctionSP CompileUnit::FindFunctionByUID(lldb::user_id_t func_uid) {
+ auto it = m_functions_by_uid.find(func_uid);
+ if (it == m_functions_by_uid.end())
+ return FunctionSP();
+ return it->second;
+}
+
+lldb::LanguageType CompileUnit::GetLanguage() {
+ if (m_language == eLanguageTypeUnknown) {
+ if (m_flags.IsClear(flagsParsedLanguage)) {
+ m_flags.Set(flagsParsedLanguage);
+ SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
+ if (symbol_vendor) {
+ m_language = symbol_vendor->ParseLanguage(*this);
+ }
+ }
+ }
+ return m_language;
+}
+
+LineTable *CompileUnit::GetLineTable() {
+ if (m_line_table_up == nullptr) {
+ if (m_flags.IsClear(flagsParsedLineTable)) {
+ m_flags.Set(flagsParsedLineTable);
+ SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
+ if (symbol_vendor)
+ symbol_vendor->ParseLineTable(*this);
+ }
+ }
+ return m_line_table_up.get();
+}
+
+void CompileUnit::SetLineTable(LineTable *line_table) {
+ if (line_table == nullptr)
+ m_flags.Clear(flagsParsedLineTable);
+ else
+ m_flags.Set(flagsParsedLineTable);
+ m_line_table_up.reset(line_table);
+}
+
+DebugMacros *CompileUnit::GetDebugMacros() {
+ if (m_debug_macros_sp.get() == nullptr) {
+ if (m_flags.IsClear(flagsParsedDebugMacros)) {
+ m_flags.Set(flagsParsedDebugMacros);
+ SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
+ if (symbol_vendor) {
+ symbol_vendor->ParseDebugMacros(*this);
+ }
+ }
+ }
+
+ return m_debug_macros_sp.get();
+}
+
+void CompileUnit::SetDebugMacros(const DebugMacrosSP &debug_macros_sp) {
+ if (debug_macros_sp.get() == nullptr)
+ m_flags.Clear(flagsParsedDebugMacros);
+ else
+ m_flags.Set(flagsParsedDebugMacros);
+ m_debug_macros_sp = debug_macros_sp;
+}
+
+VariableListSP CompileUnit::GetVariableList(bool can_create) {
+ if (m_variables.get() == nullptr && can_create) {
+ SymbolContext sc;
+ CalculateSymbolContext(&sc);
+ assert(sc.module_sp);
+ sc.module_sp->GetSymbolVendor()->ParseVariablesForContext(sc);
+ }
+
+ return m_variables;
+}
+
+uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line,
+ const FileSpec *file_spec_ptr, bool exact,
+ LineEntry *line_entry_ptr) {
+ uint32_t file_idx = 0;
+
+ if (file_spec_ptr) {
+ file_idx = GetSupportFiles().FindFileIndex(1, *file_spec_ptr, true);
+ if (file_idx == UINT32_MAX)
+ return UINT32_MAX;
+ } else {
+ // All the line table entries actually point to the version of the Compile
+ // Unit that is in the support files (the one at 0 was artificially added.)
+ // So prefer the one further on in the support files if it exists...
+ const FileSpecList &support_files = GetSupportFiles();
+ const bool full = true;
+ file_idx = support_files.FindFileIndex(
+ 1, support_files.GetFileSpecAtIndex(0), full);
+ if (file_idx == UINT32_MAX)
+ file_idx = 0;
+ }
+ LineTable *line_table = GetLineTable();
+ if (line_table)
+ return line_table->FindLineEntryIndexByFileIndex(start_idx, file_idx, line,
+ exact, line_entry_ptr);
+ return UINT32_MAX;
+}
+
+uint32_t CompileUnit::ResolveSymbolContext(const FileSpec &file_spec,
+ uint32_t line, bool check_inlines,
+ bool exact,
+ SymbolContextItem resolve_scope,
+ SymbolContextList &sc_list) {
+ // First find all of the file indexes that match our "file_spec". If
+ // "file_spec" has an empty directory, then only compare the basenames when
+ // finding file indexes
+ std::vector<uint32_t> file_indexes;
+ const bool full_match = (bool)file_spec.GetDirectory();
+ bool file_spec_matches_cu_file_spec =
+ FileSpec::Equal(file_spec, *this, full_match);
+
+ // If we are not looking for inlined functions and our file spec doesn't
+ // match then we are done...
+ if (!file_spec_matches_cu_file_spec && !check_inlines)
+ return 0;
+
+ uint32_t file_idx =
+ GetSupportFiles().FindFileIndex(1, file_spec, true);
+ while (file_idx != UINT32_MAX) {
+ file_indexes.push_back(file_idx);
+ file_idx = GetSupportFiles().FindFileIndex(file_idx + 1, file_spec, true);
+ }
+
+ const size_t num_file_indexes = file_indexes.size();
+ if (num_file_indexes == 0)
+ return 0;
+
+ const uint32_t prev_size = sc_list.GetSize();
+
+ SymbolContext sc(GetModule());
+ sc.comp_unit = this;
+
+ if (line != 0) {
+ LineTable *line_table = sc.comp_unit->GetLineTable();
+
+ if (line_table != nullptr) {
+ uint32_t found_line;
+ uint32_t line_idx;
+
+ if (num_file_indexes == 1) {
+ // We only have a single support file that matches, so use the line
+ // table function that searches for a line entries that match a single
+ // support file index
+ LineEntry line_entry;
+ line_idx = line_table->FindLineEntryIndexByFileIndex(
+ 0, file_indexes.front(), line, exact, &line_entry);
+
+ // If "exact == true", then "found_line" will be the same as "line". If
+ // "exact == false", the "found_line" will be the closest line entry
+ // with a line number greater than "line" and we will use this for our
+ // subsequent line exact matches below.
+ found_line = line_entry.line;
+
+ while (line_idx != UINT32_MAX) {
+ // If they only asked for the line entry, then we're done, we can
+ // just copy that over. But if they wanted more than just the line
+ // number, fill it in.
+ if (resolve_scope == eSymbolContextLineEntry) {
+ sc.line_entry = line_entry;
+ } else {
+ line_entry.range.GetBaseAddress().CalculateSymbolContext(
+ &sc, resolve_scope);
+ }
+
+ sc_list.Append(sc);
+ line_idx = line_table->FindLineEntryIndexByFileIndex(
+ line_idx + 1, file_indexes.front(), found_line, true,
+ &line_entry);
+ }
+ } else {
+ // We found multiple support files that match "file_spec" so use the
+ // line table function that searches for a line entries that match a
+ // multiple support file indexes.
+ LineEntry line_entry;
+ line_idx = line_table->FindLineEntryIndexByFileIndex(
+ 0, file_indexes, line, exact, &line_entry);
+
+ // If "exact == true", then "found_line" will be the same as "line". If
+ // "exact == false", the "found_line" will be the closest line entry
+ // with a line number greater than "line" and we will use this for our
+ // subsequent line exact matches below.
+ found_line = line_entry.line;
+
+ while (line_idx != UINT32_MAX) {
+ if (resolve_scope == eSymbolContextLineEntry) {
+ sc.line_entry = line_entry;
+ } else {
+ line_entry.range.GetBaseAddress().CalculateSymbolContext(
+ &sc, resolve_scope);
+ }
+
+ sc_list.Append(sc);
+ line_idx = line_table->FindLineEntryIndexByFileIndex(
+ line_idx + 1, file_indexes, found_line, true, &line_entry);
+ }
+ }
+ }
+ } else if (file_spec_matches_cu_file_spec && !check_inlines) {
+ // only append the context if we aren't looking for inline call sites by
+ // file and line and if the file spec matches that of the compile unit
+ sc_list.Append(sc);
+ }
+ return sc_list.GetSize() - prev_size;
+}
+
+bool CompileUnit::GetIsOptimized() {
+ if (m_is_optimized == eLazyBoolCalculate) {
+ m_is_optimized = eLazyBoolNo;
+ if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
+ if (symbol_vendor->ParseIsOptimized(*this))
+ m_is_optimized = eLazyBoolYes;
+ }
+ }
+ return m_is_optimized;
+}
+
+void CompileUnit::SetVariableList(VariableListSP &variables) {
+ m_variables = variables;
+}
+
+const std::vector<SourceModule> &CompileUnit::GetImportedModules() {
+ if (m_imported_modules.empty() &&
+ m_flags.IsClear(flagsParsedImportedModules)) {
+ m_flags.Set(flagsParsedImportedModules);
+ if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
+ SymbolContext sc;
+ CalculateSymbolContext(&sc);
+ symbol_vendor->ParseImportedModules(sc, m_imported_modules);
+ }
+ }
+ return m_imported_modules;
+}
+
+const FileSpecList &CompileUnit::GetSupportFiles() {
+ if (m_support_files.GetSize() == 0) {
+ if (m_flags.IsClear(flagsParsedSupportFiles)) {
+ m_flags.Set(flagsParsedSupportFiles);
+ SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
+ if (symbol_vendor) {
+ symbol_vendor->ParseSupportFiles(*this, m_support_files);
+ }
+ }
+ }
+ return m_support_files;
+}
+
+void *CompileUnit::GetUserData() const { return m_user_data; }
diff --git a/contrib/llvm-project/lldb/source/Symbol/CompilerDecl.cpp b/contrib/llvm-project/lldb/source/Symbol/CompilerDecl.cpp
new file mode 100644
index 000000000000..2c64113a2bbe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/CompilerDecl.cpp
@@ -0,0 +1,53 @@
+//===-- CompilerDecl.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/CompilerDecl.h"
+#include "lldb/Symbol/CompilerDeclContext.h"
+#include "lldb/Symbol/TypeSystem.h"
+
+using namespace lldb_private;
+
+bool CompilerDecl::IsClang() const {
+ return IsValid() && m_type_system->getKind() == TypeSystem::eKindClang;
+}
+
+ConstString CompilerDecl::GetName() const {
+ return m_type_system->DeclGetName(m_opaque_decl);
+}
+
+ConstString CompilerDecl::GetMangledName() const {
+ return m_type_system->DeclGetMangledName(m_opaque_decl);
+}
+
+CompilerDeclContext CompilerDecl::GetDeclContext() const {
+ return m_type_system->DeclGetDeclContext(m_opaque_decl);
+}
+
+CompilerType CompilerDecl::GetFunctionReturnType() const {
+ return m_type_system->DeclGetFunctionReturnType(m_opaque_decl);
+}
+
+size_t CompilerDecl::GetNumFunctionArguments() const {
+ return m_type_system->DeclGetFunctionNumArguments(m_opaque_decl);
+}
+
+CompilerType CompilerDecl::GetFunctionArgumentType(size_t arg_idx) const {
+ return m_type_system->DeclGetFunctionArgumentType(m_opaque_decl, arg_idx);
+}
+
+bool lldb_private::operator==(const lldb_private::CompilerDecl &lhs,
+ const lldb_private::CompilerDecl &rhs) {
+ return lhs.GetTypeSystem() == rhs.GetTypeSystem() &&
+ lhs.GetOpaqueDecl() == rhs.GetOpaqueDecl();
+}
+
+bool lldb_private::operator!=(const lldb_private::CompilerDecl &lhs,
+ const lldb_private::CompilerDecl &rhs) {
+ return lhs.GetTypeSystem() != rhs.GetTypeSystem() ||
+ lhs.GetOpaqueDecl() != rhs.GetOpaqueDecl();
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/CompilerDeclContext.cpp b/contrib/llvm-project/lldb/source/Symbol/CompilerDeclContext.cpp
new file mode 100644
index 000000000000..a6f046c4eb22
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/CompilerDeclContext.cpp
@@ -0,0 +1,85 @@
+//===-- CompilerDeclContext.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/CompilerDeclContext.h"
+#include "lldb/Symbol/CompilerDecl.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include <vector>
+
+using namespace lldb_private;
+
+std::vector<CompilerDecl>
+CompilerDeclContext::FindDeclByName(ConstString name,
+ const bool ignore_using_decls) {
+ if (IsValid())
+ return m_type_system->DeclContextFindDeclByName(m_opaque_decl_ctx, name,
+ ignore_using_decls);
+ else
+ return std::vector<CompilerDecl>();
+}
+
+bool CompilerDeclContext::IsClang() const {
+ return IsValid() && m_type_system->getKind() == TypeSystem::eKindClang;
+}
+
+ConstString CompilerDeclContext::GetName() const {
+ if (IsValid())
+ return m_type_system->DeclContextGetName(m_opaque_decl_ctx);
+ else
+ return ConstString();
+}
+
+ConstString CompilerDeclContext::GetScopeQualifiedName() const {
+ if (IsValid())
+ return m_type_system->DeclContextGetScopeQualifiedName(m_opaque_decl_ctx);
+ else
+ return ConstString();
+}
+
+bool CompilerDeclContext::IsStructUnionOrClass() const {
+ if (IsValid())
+ return m_type_system->DeclContextIsStructUnionOrClass(m_opaque_decl_ctx);
+ else
+ return false;
+}
+
+bool CompilerDeclContext::IsClassMethod(lldb::LanguageType *language_ptr,
+ bool *is_instance_method_ptr,
+ ConstString *language_object_name_ptr) {
+ if (IsValid())
+ return m_type_system->DeclContextIsClassMethod(
+ m_opaque_decl_ctx, language_ptr, is_instance_method_ptr,
+ language_object_name_ptr);
+ else
+ return false;
+}
+
+bool CompilerDeclContext::IsContainedInLookup(CompilerDeclContext other) const {
+ if (!IsValid())
+ return false;
+
+ // If the other context is just the current context, we don't need to go
+ // over the type system to know that the lookup is identical.
+ if (this == &other)
+ return true;
+
+ return m_type_system->DeclContextIsContainedInLookup(m_opaque_decl_ctx,
+ other.m_opaque_decl_ctx);
+}
+
+bool lldb_private::operator==(const lldb_private::CompilerDeclContext &lhs,
+ const lldb_private::CompilerDeclContext &rhs) {
+ return lhs.GetTypeSystem() == rhs.GetTypeSystem() &&
+ lhs.GetOpaqueDeclContext() == rhs.GetOpaqueDeclContext();
+}
+
+bool lldb_private::operator!=(const lldb_private::CompilerDeclContext &lhs,
+ const lldb_private::CompilerDeclContext &rhs) {
+ return lhs.GetTypeSystem() != rhs.GetTypeSystem() ||
+ lhs.GetOpaqueDeclContext() != rhs.GetOpaqueDeclContext();
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/CompilerType.cpp b/contrib/llvm-project/lldb/source/Symbol/CompilerType.cpp
new file mode 100644
index 000000000000..bb9a1a642e42
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/CompilerType.cpp
@@ -0,0 +1,1075 @@
+//===-- CompilerType.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/CompilerType.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <iterator>
+#include <mutex>
+
+using namespace lldb;
+using namespace lldb_private;
+
+CompilerType::CompilerType(TypeSystem *type_system,
+ lldb::opaque_compiler_type_t type)
+ : m_type(type), m_type_system(type_system) {}
+
+CompilerType::CompilerType(clang::ASTContext *ast, clang::QualType qual_type)
+ : m_type(qual_type.getAsOpaquePtr()),
+ m_type_system(ClangASTContext::GetASTContext(ast)) {
+ if (m_type)
+ assert(m_type_system != nullptr);
+}
+
+CompilerType::~CompilerType() {}
+
+// Tests
+
+bool CompilerType::IsAggregateType() const {
+ if (IsValid())
+ return m_type_system->IsAggregateType(m_type);
+ return false;
+}
+
+bool CompilerType::IsAnonymousType() const {
+ if (IsValid())
+ return m_type_system->IsAnonymousType(m_type);
+ return false;
+}
+
+bool CompilerType::IsArrayType(CompilerType *element_type_ptr, uint64_t *size,
+ bool *is_incomplete) const {
+ if (IsValid())
+ return m_type_system->IsArrayType(m_type, element_type_ptr, size,
+ is_incomplete);
+
+ if (element_type_ptr)
+ element_type_ptr->Clear();
+ if (size)
+ *size = 0;
+ if (is_incomplete)
+ *is_incomplete = false;
+ return false;
+}
+
+bool CompilerType::IsVectorType(CompilerType *element_type,
+ uint64_t *size) const {
+ if (IsValid())
+ return m_type_system->IsVectorType(m_type, element_type, size);
+ return false;
+}
+
+bool CompilerType::IsRuntimeGeneratedType() const {
+ if (IsValid())
+ return m_type_system->IsRuntimeGeneratedType(m_type);
+ return false;
+}
+
+bool CompilerType::IsCharType() const {
+ if (IsValid())
+ return m_type_system->IsCharType(m_type);
+ return false;
+}
+
+bool CompilerType::IsCompleteType() const {
+ if (IsValid())
+ return m_type_system->IsCompleteType(m_type);
+ return false;
+}
+
+bool CompilerType::IsConst() const {
+ if (IsValid())
+ return m_type_system->IsConst(m_type);
+ return false;
+}
+
+bool CompilerType::IsCStringType(uint32_t &length) const {
+ if (IsValid())
+ return m_type_system->IsCStringType(m_type, length);
+ return false;
+}
+
+bool CompilerType::IsFunctionType(bool *is_variadic_ptr) const {
+ if (IsValid())
+ return m_type_system->IsFunctionType(m_type, is_variadic_ptr);
+ return false;
+}
+
+// Used to detect "Homogeneous Floating-point Aggregates"
+uint32_t
+CompilerType::IsHomogeneousAggregate(CompilerType *base_type_ptr) const {
+ if (IsValid())
+ return m_type_system->IsHomogeneousAggregate(m_type, base_type_ptr);
+ return 0;
+}
+
+size_t CompilerType::GetNumberOfFunctionArguments() const {
+ if (IsValid())
+ return m_type_system->GetNumberOfFunctionArguments(m_type);
+ return 0;
+}
+
+CompilerType
+CompilerType::GetFunctionArgumentAtIndex(const size_t index) const {
+ if (IsValid())
+ return m_type_system->GetFunctionArgumentAtIndex(m_type, index);
+ return CompilerType();
+}
+
+bool CompilerType::IsFunctionPointerType() const {
+ if (IsValid())
+ return m_type_system->IsFunctionPointerType(m_type);
+ return false;
+}
+
+bool CompilerType::IsBlockPointerType(
+ CompilerType *function_pointer_type_ptr) const {
+ if (IsValid())
+ return m_type_system->IsBlockPointerType(m_type, function_pointer_type_ptr);
+ return false;
+}
+
+bool CompilerType::IsIntegerType(bool &is_signed) const {
+ if (IsValid())
+ return m_type_system->IsIntegerType(m_type, is_signed);
+ return false;
+}
+
+bool CompilerType::IsEnumerationType(bool &is_signed) const {
+ if (IsValid())
+ return m_type_system->IsEnumerationType(m_type, is_signed);
+ return false;
+}
+
+bool CompilerType::IsIntegerOrEnumerationType(bool &is_signed) const {
+ return IsIntegerType(is_signed) || IsEnumerationType(is_signed);
+}
+
+bool CompilerType::IsPointerType(CompilerType *pointee_type) const {
+ if (IsValid()) {
+ return m_type_system->IsPointerType(m_type, pointee_type);
+ }
+ if (pointee_type)
+ pointee_type->Clear();
+ return false;
+}
+
+bool CompilerType::IsPointerOrReferenceType(CompilerType *pointee_type) const {
+ if (IsValid()) {
+ return m_type_system->IsPointerOrReferenceType(m_type, pointee_type);
+ }
+ if (pointee_type)
+ pointee_type->Clear();
+ return false;
+}
+
+bool CompilerType::IsReferenceType(CompilerType *pointee_type,
+ bool *is_rvalue) const {
+ if (IsValid()) {
+ return m_type_system->IsReferenceType(m_type, pointee_type, is_rvalue);
+ }
+ if (pointee_type)
+ pointee_type->Clear();
+ return false;
+}
+
+bool CompilerType::ShouldTreatScalarValueAsAddress() const {
+ if (IsValid())
+ return m_type_system->ShouldTreatScalarValueAsAddress(m_type);
+ return false;
+}
+
+bool CompilerType::IsFloatingPointType(uint32_t &count,
+ bool &is_complex) const {
+ if (IsValid()) {
+ return m_type_system->IsFloatingPointType(m_type, count, is_complex);
+ }
+ count = 0;
+ is_complex = false;
+ return false;
+}
+
+bool CompilerType::IsDefined() const {
+ if (IsValid())
+ return m_type_system->IsDefined(m_type);
+ return true;
+}
+
+bool CompilerType::IsPolymorphicClass() const {
+ if (IsValid()) {
+ return m_type_system->IsPolymorphicClass(m_type);
+ }
+ return false;
+}
+
+bool CompilerType::IsPossibleDynamicType(CompilerType *dynamic_pointee_type,
+ bool check_cplusplus,
+ bool check_objc) const {
+ if (IsValid())
+ return m_type_system->IsPossibleDynamicType(m_type, dynamic_pointee_type,
+ check_cplusplus, check_objc);
+ return false;
+}
+
+bool CompilerType::IsScalarType() const {
+ if (!IsValid())
+ return false;
+
+ return m_type_system->IsScalarType(m_type);
+}
+
+bool CompilerType::IsTypedefType() const {
+ if (!IsValid())
+ return false;
+ return m_type_system->IsTypedefType(m_type);
+}
+
+bool CompilerType::IsVoidType() const {
+ if (!IsValid())
+ return false;
+ return m_type_system->IsVoidType(m_type);
+}
+
+bool CompilerType::IsPointerToScalarType() const {
+ if (!IsValid())
+ return false;
+
+ return IsPointerType() && GetPointeeType().IsScalarType();
+}
+
+bool CompilerType::IsArrayOfScalarType() const {
+ CompilerType element_type;
+ if (IsArrayType(&element_type, nullptr, nullptr))
+ return element_type.IsScalarType();
+ return false;
+}
+
+bool CompilerType::IsBeingDefined() const {
+ if (!IsValid())
+ return false;
+ return m_type_system->IsBeingDefined(m_type);
+}
+
+// Type Completion
+
+bool CompilerType::GetCompleteType() const {
+ if (!IsValid())
+ return false;
+ return m_type_system->GetCompleteType(m_type);
+}
+
+// AST related queries
+size_t CompilerType::GetPointerByteSize() const {
+ if (m_type_system)
+ return m_type_system->GetPointerByteSize();
+ return 0;
+}
+
+ConstString CompilerType::GetConstQualifiedTypeName() const {
+ return GetConstTypeName();
+}
+
+ConstString CompilerType::GetConstTypeName() const {
+ if (IsValid()) {
+ ConstString type_name(GetTypeName());
+ if (type_name)
+ return type_name;
+ }
+ return ConstString("<invalid>");
+}
+
+ConstString CompilerType::GetTypeName() const {
+ if (IsValid()) {
+ return m_type_system->GetTypeName(m_type);
+ }
+ return ConstString("<invalid>");
+}
+
+ConstString CompilerType::GetDisplayTypeName() const { return GetTypeName(); }
+
+uint32_t CompilerType::GetTypeInfo(
+ CompilerType *pointee_or_element_compiler_type) const {
+ if (!IsValid())
+ return 0;
+
+ return m_type_system->GetTypeInfo(m_type, pointee_or_element_compiler_type);
+}
+
+lldb::LanguageType CompilerType::GetMinimumLanguage() {
+ if (!IsValid())
+ return lldb::eLanguageTypeC;
+
+ return m_type_system->GetMinimumLanguage(m_type);
+}
+
+lldb::TypeClass CompilerType::GetTypeClass() const {
+ if (!IsValid())
+ return lldb::eTypeClassInvalid;
+
+ return m_type_system->GetTypeClass(m_type);
+}
+
+void CompilerType::SetCompilerType(TypeSystem *type_system,
+ lldb::opaque_compiler_type_t type) {
+ m_type_system = type_system;
+ m_type = type;
+}
+
+void CompilerType::SetCompilerType(clang::ASTContext *ast,
+ clang::QualType qual_type) {
+ m_type_system = ClangASTContext::GetASTContext(ast);
+ m_type = qual_type.getAsOpaquePtr();
+}
+
+unsigned CompilerType::GetTypeQualifiers() const {
+ if (IsValid())
+ return m_type_system->GetTypeQualifiers(m_type);
+ return 0;
+}
+
+// Creating related types
+
+CompilerType CompilerType::GetArrayElementType(uint64_t *stride) const {
+ if (IsValid()) {
+ return m_type_system->GetArrayElementType(m_type, stride);
+ }
+ return CompilerType();
+}
+
+CompilerType CompilerType::GetArrayType(uint64_t size) const {
+ if (IsValid()) {
+ return m_type_system->GetArrayType(m_type, size);
+ }
+ return CompilerType();
+}
+
+CompilerType CompilerType::GetCanonicalType() const {
+ if (IsValid())
+ return m_type_system->GetCanonicalType(m_type);
+ return CompilerType();
+}
+
+CompilerType CompilerType::GetFullyUnqualifiedType() const {
+ if (IsValid())
+ return m_type_system->GetFullyUnqualifiedType(m_type);
+ return CompilerType();
+}
+
+int CompilerType::GetFunctionArgumentCount() const {
+ if (IsValid()) {
+ return m_type_system->GetFunctionArgumentCount(m_type);
+ }
+ return -1;
+}
+
+CompilerType CompilerType::GetFunctionArgumentTypeAtIndex(size_t idx) const {
+ if (IsValid()) {
+ return m_type_system->GetFunctionArgumentTypeAtIndex(m_type, idx);
+ }
+ return CompilerType();
+}
+
+CompilerType CompilerType::GetFunctionReturnType() const {
+ if (IsValid()) {
+ return m_type_system->GetFunctionReturnType(m_type);
+ }
+ return CompilerType();
+}
+
+size_t CompilerType::GetNumMemberFunctions() const {
+ if (IsValid()) {
+ return m_type_system->GetNumMemberFunctions(m_type);
+ }
+ return 0;
+}
+
+TypeMemberFunctionImpl CompilerType::GetMemberFunctionAtIndex(size_t idx) {
+ if (IsValid()) {
+ return m_type_system->GetMemberFunctionAtIndex(m_type, idx);
+ }
+ return TypeMemberFunctionImpl();
+}
+
+CompilerType CompilerType::GetNonReferenceType() const {
+ if (IsValid())
+ return m_type_system->GetNonReferenceType(m_type);
+ return CompilerType();
+}
+
+CompilerType CompilerType::GetPointeeType() const {
+ if (IsValid()) {
+ return m_type_system->GetPointeeType(m_type);
+ }
+ return CompilerType();
+}
+
+CompilerType CompilerType::GetPointerType() const {
+ if (IsValid()) {
+ return m_type_system->GetPointerType(m_type);
+ }
+ return CompilerType();
+}
+
+CompilerType CompilerType::GetLValueReferenceType() const {
+ if (IsValid())
+ return m_type_system->GetLValueReferenceType(m_type);
+ else
+ return CompilerType();
+}
+
+CompilerType CompilerType::GetRValueReferenceType() const {
+ if (IsValid())
+ return m_type_system->GetRValueReferenceType(m_type);
+ else
+ return CompilerType();
+}
+
+CompilerType CompilerType::AddConstModifier() const {
+ if (IsValid())
+ return m_type_system->AddConstModifier(m_type);
+ else
+ return CompilerType();
+}
+
+CompilerType CompilerType::AddVolatileModifier() const {
+ if (IsValid())
+ return m_type_system->AddVolatileModifier(m_type);
+ else
+ return CompilerType();
+}
+
+CompilerType CompilerType::AddRestrictModifier() const {
+ if (IsValid())
+ return m_type_system->AddRestrictModifier(m_type);
+ else
+ return CompilerType();
+}
+
+CompilerType
+CompilerType::CreateTypedef(const char *name,
+ const CompilerDeclContext &decl_ctx) const {
+ if (IsValid())
+ return m_type_system->CreateTypedef(m_type, name, decl_ctx);
+ else
+ return CompilerType();
+}
+
+CompilerType CompilerType::GetTypedefedType() const {
+ if (IsValid())
+ return m_type_system->GetTypedefedType(m_type);
+ else
+ return CompilerType();
+}
+
+// Create related types using the current type's AST
+
+CompilerType
+CompilerType::GetBasicTypeFromAST(lldb::BasicType basic_type) const {
+ if (IsValid())
+ return m_type_system->GetBasicTypeFromAST(basic_type);
+ return CompilerType();
+}
+// Exploring the type
+
+llvm::Optional<uint64_t>
+CompilerType::GetBitSize(ExecutionContextScope *exe_scope) const {
+ if (IsValid())
+ return m_type_system->GetBitSize(m_type, exe_scope);
+ return {};
+}
+
+llvm::Optional<uint64_t>
+CompilerType::GetByteSize(ExecutionContextScope *exe_scope) const {
+ if (llvm::Optional<uint64_t> bit_size = GetBitSize(exe_scope))
+ return (*bit_size + 7) / 8;
+ return {};
+}
+
+size_t CompilerType::GetTypeBitAlign() const {
+ if (IsValid())
+ return m_type_system->GetTypeBitAlign(m_type);
+ return 0;
+}
+
+lldb::Encoding CompilerType::GetEncoding(uint64_t &count) const {
+ if (!IsValid())
+ return lldb::eEncodingInvalid;
+
+ return m_type_system->GetEncoding(m_type, count);
+}
+
+lldb::Format CompilerType::GetFormat() const {
+ if (!IsValid())
+ return lldb::eFormatDefault;
+
+ return m_type_system->GetFormat(m_type);
+}
+
+uint32_t CompilerType::GetNumChildren(bool omit_empty_base_classes,
+ const ExecutionContext *exe_ctx) const {
+ if (!IsValid())
+ return 0;
+ return m_type_system->GetNumChildren(m_type, omit_empty_base_classes,
+ exe_ctx);
+}
+
+lldb::BasicType CompilerType::GetBasicTypeEnumeration() const {
+ if (IsValid())
+ return m_type_system->GetBasicTypeEnumeration(m_type);
+ return eBasicTypeInvalid;
+}
+
+void CompilerType::ForEachEnumerator(
+ std::function<bool(const CompilerType &integer_type,
+ ConstString name,
+ const llvm::APSInt &value)> const &callback) const {
+ if (IsValid())
+ return m_type_system->ForEachEnumerator(m_type, callback);
+}
+
+uint32_t CompilerType::GetNumFields() const {
+ if (!IsValid())
+ return 0;
+ return m_type_system->GetNumFields(m_type);
+}
+
+CompilerType CompilerType::GetFieldAtIndex(size_t idx, std::string &name,
+ uint64_t *bit_offset_ptr,
+ uint32_t *bitfield_bit_size_ptr,
+ bool *is_bitfield_ptr) const {
+ if (!IsValid())
+ return CompilerType();
+ return m_type_system->GetFieldAtIndex(m_type, idx, name, bit_offset_ptr,
+ bitfield_bit_size_ptr, is_bitfield_ptr);
+}
+
+uint32_t CompilerType::GetNumDirectBaseClasses() const {
+ if (IsValid())
+ return m_type_system->GetNumDirectBaseClasses(m_type);
+ return 0;
+}
+
+uint32_t CompilerType::GetNumVirtualBaseClasses() const {
+ if (IsValid())
+ return m_type_system->GetNumVirtualBaseClasses(m_type);
+ return 0;
+}
+
+CompilerType
+CompilerType::GetDirectBaseClassAtIndex(size_t idx,
+ uint32_t *bit_offset_ptr) const {
+ if (IsValid())
+ return m_type_system->GetDirectBaseClassAtIndex(m_type, idx,
+ bit_offset_ptr);
+ return CompilerType();
+}
+
+CompilerType
+CompilerType::GetVirtualBaseClassAtIndex(size_t idx,
+ uint32_t *bit_offset_ptr) const {
+ if (IsValid())
+ return m_type_system->GetVirtualBaseClassAtIndex(m_type, idx,
+ bit_offset_ptr);
+ return CompilerType();
+}
+
+uint32_t CompilerType::GetIndexOfFieldWithName(
+ const char *name, CompilerType *field_compiler_type_ptr,
+ uint64_t *bit_offset_ptr, uint32_t *bitfield_bit_size_ptr,
+ bool *is_bitfield_ptr) const {
+ unsigned count = GetNumFields();
+ std::string field_name;
+ for (unsigned index = 0; index < count; index++) {
+ CompilerType field_compiler_type(
+ GetFieldAtIndex(index, field_name, bit_offset_ptr,
+ bitfield_bit_size_ptr, is_bitfield_ptr));
+ if (strcmp(field_name.c_str(), name) == 0) {
+ if (field_compiler_type_ptr)
+ *field_compiler_type_ptr = field_compiler_type;
+ return index;
+ }
+ }
+ return UINT32_MAX;
+}
+
+CompilerType CompilerType::GetChildCompilerTypeAtIndex(
+ ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers,
+ bool omit_empty_base_classes, bool ignore_array_bounds,
+ std::string &child_name, uint32_t &child_byte_size,
+ int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size,
+ uint32_t &child_bitfield_bit_offset, bool &child_is_base_class,
+ bool &child_is_deref_of_parent, ValueObject *valobj,
+ uint64_t &language_flags) const {
+ if (!IsValid())
+ return CompilerType();
+ return m_type_system->GetChildCompilerTypeAtIndex(
+ m_type, exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, valobj, language_flags);
+}
+
+// Look for a child member (doesn't include base classes, but it does include
+// their members) in the type hierarchy. Returns an index path into
+// "clang_type" on how to reach the appropriate member.
+//
+// class A
+// {
+// public:
+// int m_a;
+// int m_b;
+// };
+//
+// class B
+// {
+// };
+//
+// class C :
+// public B,
+// public A
+// {
+// };
+//
+// If we have a clang type that describes "class C", and we wanted to looked
+// "m_b" in it:
+//
+// With omit_empty_base_classes == false we would get an integer array back
+// with: { 1, 1 } The first index 1 is the child index for "class A" within
+// class C The second index 1 is the child index for "m_b" within class A
+//
+// With omit_empty_base_classes == true we would get an integer array back
+// with: { 0, 1 } The first index 0 is the child index for "class A" within
+// class C (since class B doesn't have any members it doesn't count) The second
+// index 1 is the child index for "m_b" within class A
+
+size_t CompilerType::GetIndexOfChildMemberWithName(
+ const char *name, bool omit_empty_base_classes,
+ std::vector<uint32_t> &child_indexes) const {
+ if (IsValid() && name && name[0]) {
+ return m_type_system->GetIndexOfChildMemberWithName(
+ m_type, name, omit_empty_base_classes, child_indexes);
+ }
+ return 0;
+}
+
+size_t CompilerType::GetNumTemplateArguments() const {
+ if (IsValid()) {
+ return m_type_system->GetNumTemplateArguments(m_type);
+ }
+ return 0;
+}
+
+TemplateArgumentKind CompilerType::GetTemplateArgumentKind(size_t idx) const {
+ if (IsValid())
+ return m_type_system->GetTemplateArgumentKind(m_type, idx);
+ return eTemplateArgumentKindNull;
+}
+
+CompilerType CompilerType::GetTypeTemplateArgument(size_t idx) const {
+ if (IsValid()) {
+ return m_type_system->GetTypeTemplateArgument(m_type, idx);
+ }
+ return CompilerType();
+}
+
+llvm::Optional<CompilerType::IntegralTemplateArgument>
+CompilerType::GetIntegralTemplateArgument(size_t idx) const {
+ if (IsValid())
+ return m_type_system->GetIntegralTemplateArgument(m_type, idx);
+ return llvm::None;
+}
+
+CompilerType CompilerType::GetTypeForFormatters() const {
+ if (IsValid())
+ return m_type_system->GetTypeForFormatters(m_type);
+ return CompilerType();
+}
+
+LazyBool CompilerType::ShouldPrintAsOneLiner(ValueObject *valobj) const {
+ if (IsValid())
+ return m_type_system->ShouldPrintAsOneLiner(m_type, valobj);
+ return eLazyBoolCalculate;
+}
+
+bool CompilerType::IsMeaninglessWithoutDynamicResolution() const {
+ if (IsValid())
+ return m_type_system->IsMeaninglessWithoutDynamicResolution(m_type);
+ return false;
+}
+
+// Get the index of the child of "clang_type" whose name matches. This function
+// doesn't descend into the children, but only looks one level deep and name
+// matches can include base class names.
+
+uint32_t
+CompilerType::GetIndexOfChildWithName(const char *name,
+ bool omit_empty_base_classes) const {
+ if (IsValid() && name && name[0]) {
+ return m_type_system->GetIndexOfChildWithName(m_type, name,
+ omit_empty_base_classes);
+ }
+ return UINT32_MAX;
+}
+
+size_t CompilerType::ConvertStringToFloatValue(const char *s, uint8_t *dst,
+ size_t dst_size) const {
+ if (IsValid())
+ return m_type_system->ConvertStringToFloatValue(m_type, s, dst, dst_size);
+ return 0;
+}
+
+// Dumping types
+#define DEPTH_INCREMENT 2
+
+void CompilerType::DumpValue(ExecutionContext *exe_ctx, Stream *s,
+ lldb::Format format, const DataExtractor &data,
+ lldb::offset_t data_byte_offset,
+ size_t data_byte_size, uint32_t bitfield_bit_size,
+ uint32_t bitfield_bit_offset, bool show_types,
+ bool show_summary, bool verbose, uint32_t depth) {
+ if (!IsValid())
+ return;
+ m_type_system->DumpValue(m_type, exe_ctx, s, format, data, data_byte_offset,
+ data_byte_size, bitfield_bit_size,
+ bitfield_bit_offset, show_types, show_summary,
+ verbose, depth);
+}
+
+bool CompilerType::DumpTypeValue(Stream *s, lldb::Format format,
+ const DataExtractor &data,
+ lldb::offset_t byte_offset, size_t byte_size,
+ uint32_t bitfield_bit_size,
+ uint32_t bitfield_bit_offset,
+ ExecutionContextScope *exe_scope) {
+ if (!IsValid())
+ return false;
+ return m_type_system->DumpTypeValue(m_type, s, format, data, byte_offset,
+ byte_size, bitfield_bit_size,
+ bitfield_bit_offset, exe_scope);
+}
+
+void CompilerType::DumpSummary(ExecutionContext *exe_ctx, Stream *s,
+ const DataExtractor &data,
+ lldb::offset_t data_byte_offset,
+ size_t data_byte_size) {
+ if (IsValid())
+ m_type_system->DumpSummary(m_type, exe_ctx, s, data, data_byte_offset,
+ data_byte_size);
+}
+
+void CompilerType::DumpTypeDescription() const {
+ if (IsValid())
+ m_type_system->DumpTypeDescription(m_type);
+}
+
+void CompilerType::DumpTypeDescription(Stream *s) const {
+ if (IsValid()) {
+ m_type_system->DumpTypeDescription(m_type, s);
+ }
+}
+
+#ifndef NDEBUG
+LLVM_DUMP_METHOD void CompilerType::dump() const {
+ if (IsValid())
+ m_type_system->dump(m_type);
+ else
+ llvm::errs() << "<invalid>\n";
+}
+#endif
+
+bool CompilerType::GetValueAsScalar(const lldb_private::DataExtractor &data,
+ lldb::offset_t data_byte_offset,
+ size_t data_byte_size,
+ Scalar &value) const {
+ if (!IsValid())
+ return false;
+
+ if (IsAggregateType()) {
+ return false; // Aggregate types don't have scalar values
+ } else {
+ uint64_t count = 0;
+ lldb::Encoding encoding = GetEncoding(count);
+
+ if (encoding == lldb::eEncodingInvalid || count != 1)
+ return false;
+
+ llvm::Optional<uint64_t> byte_size = GetByteSize(nullptr);
+ if (!byte_size)
+ return false;
+ lldb::offset_t offset = data_byte_offset;
+ switch (encoding) {
+ case lldb::eEncodingInvalid:
+ break;
+ case lldb::eEncodingVector:
+ break;
+ case lldb::eEncodingUint:
+ if (*byte_size <= sizeof(unsigned long long)) {
+ uint64_t uval64 = data.GetMaxU64(&offset, *byte_size);
+ if (*byte_size <= sizeof(unsigned int)) {
+ value = (unsigned int)uval64;
+ return true;
+ } else if (*byte_size <= sizeof(unsigned long)) {
+ value = (unsigned long)uval64;
+ return true;
+ } else if (*byte_size <= sizeof(unsigned long long)) {
+ value = (unsigned long long)uval64;
+ return true;
+ } else
+ value.Clear();
+ }
+ break;
+
+ case lldb::eEncodingSint:
+ if (*byte_size <= sizeof(long long)) {
+ int64_t sval64 = data.GetMaxS64(&offset, *byte_size);
+ if (*byte_size <= sizeof(int)) {
+ value = (int)sval64;
+ return true;
+ } else if (*byte_size <= sizeof(long)) {
+ value = (long)sval64;
+ return true;
+ } else if (*byte_size <= sizeof(long long)) {
+ value = (long long)sval64;
+ return true;
+ } else
+ value.Clear();
+ }
+ break;
+
+ case lldb::eEncodingIEEE754:
+ if (*byte_size <= sizeof(long double)) {
+ uint32_t u32;
+ uint64_t u64;
+ if (*byte_size == sizeof(float)) {
+ if (sizeof(float) == sizeof(uint32_t)) {
+ u32 = data.GetU32(&offset);
+ value = *((float *)&u32);
+ return true;
+ } else if (sizeof(float) == sizeof(uint64_t)) {
+ u64 = data.GetU64(&offset);
+ value = *((float *)&u64);
+ return true;
+ }
+ } else if (*byte_size == sizeof(double)) {
+ if (sizeof(double) == sizeof(uint32_t)) {
+ u32 = data.GetU32(&offset);
+ value = *((double *)&u32);
+ return true;
+ } else if (sizeof(double) == sizeof(uint64_t)) {
+ u64 = data.GetU64(&offset);
+ value = *((double *)&u64);
+ return true;
+ }
+ } else if (*byte_size == sizeof(long double)) {
+ if (sizeof(long double) == sizeof(uint32_t)) {
+ u32 = data.GetU32(&offset);
+ value = *((long double *)&u32);
+ return true;
+ } else if (sizeof(long double) == sizeof(uint64_t)) {
+ u64 = data.GetU64(&offset);
+ value = *((long double *)&u64);
+ return true;
+ }
+ }
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+bool CompilerType::SetValueFromScalar(const Scalar &value, Stream &strm) {
+ if (!IsValid())
+ return false;
+
+ // Aggregate types don't have scalar values
+ if (!IsAggregateType()) {
+ strm.GetFlags().Set(Stream::eBinary);
+ uint64_t count = 0;
+ lldb::Encoding encoding = GetEncoding(count);
+
+ if (encoding == lldb::eEncodingInvalid || count != 1)
+ return false;
+
+ llvm::Optional<uint64_t> bit_width = GetBitSize(nullptr);
+ if (!bit_width)
+ return false;
+
+ // This function doesn't currently handle non-byte aligned assignments
+ if ((*bit_width % 8) != 0)
+ return false;
+
+ const uint64_t byte_size = (*bit_width + 7) / 8;
+ switch (encoding) {
+ case lldb::eEncodingInvalid:
+ break;
+ case lldb::eEncodingVector:
+ break;
+ case lldb::eEncodingUint:
+ switch (byte_size) {
+ case 1:
+ strm.PutHex8(value.UInt());
+ return true;
+ case 2:
+ strm.PutHex16(value.UInt());
+ return true;
+ case 4:
+ strm.PutHex32(value.UInt());
+ return true;
+ case 8:
+ strm.PutHex64(value.ULongLong());
+ return true;
+ default:
+ break;
+ }
+ break;
+
+ case lldb::eEncodingSint:
+ switch (byte_size) {
+ case 1:
+ strm.PutHex8(value.SInt());
+ return true;
+ case 2:
+ strm.PutHex16(value.SInt());
+ return true;
+ case 4:
+ strm.PutHex32(value.SInt());
+ return true;
+ case 8:
+ strm.PutHex64(value.SLongLong());
+ return true;
+ default:
+ break;
+ }
+ break;
+
+ case lldb::eEncodingIEEE754:
+ if (byte_size <= sizeof(long double)) {
+ if (byte_size == sizeof(float)) {
+ strm.PutFloat(value.Float());
+ return true;
+ } else if (byte_size == sizeof(double)) {
+ strm.PutDouble(value.Double());
+ return true;
+ } else if (byte_size == sizeof(long double)) {
+ strm.PutDouble(value.LongDouble());
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+bool CompilerType::ReadFromMemory(lldb_private::ExecutionContext *exe_ctx,
+ lldb::addr_t addr, AddressType address_type,
+ lldb_private::DataExtractor &data) {
+ if (!IsValid())
+ return false;
+
+ // Can't convert a file address to anything valid without more context (which
+ // Module it came from)
+ if (address_type == eAddressTypeFile)
+ return false;
+
+ if (!GetCompleteType())
+ return false;
+
+ auto byte_size =
+ GetByteSize(exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr);
+ if (!byte_size)
+ return false;
+
+ if (data.GetByteSize() < *byte_size) {
+ lldb::DataBufferSP data_sp(new DataBufferHeap(*byte_size, '\0'));
+ data.SetData(data_sp);
+ }
+
+ uint8_t *dst = const_cast<uint8_t *>(data.PeekData(0, *byte_size));
+ if (dst != nullptr) {
+ if (address_type == eAddressTypeHost) {
+ if (addr == 0)
+ return false;
+ // The address is an address in this process, so just copy it
+ memcpy(dst, reinterpret_cast<uint8_t *>(addr), *byte_size);
+ return true;
+ } else {
+ Process *process = nullptr;
+ if (exe_ctx)
+ process = exe_ctx->GetProcessPtr();
+ if (process) {
+ Status error;
+ return process->ReadMemory(addr, dst, *byte_size, error) == *byte_size;
+ }
+ }
+ }
+ return false;
+}
+
+bool CompilerType::WriteToMemory(lldb_private::ExecutionContext *exe_ctx,
+ lldb::addr_t addr, AddressType address_type,
+ StreamString &new_value) {
+ if (!IsValid())
+ return false;
+
+ // Can't convert a file address to anything valid without more context (which
+ // Module it came from)
+ if (address_type == eAddressTypeFile)
+ return false;
+
+ if (!GetCompleteType())
+ return false;
+
+ auto byte_size =
+ GetByteSize(exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr);
+ if (!byte_size)
+ return false;
+
+ if (*byte_size > 0) {
+ if (address_type == eAddressTypeHost) {
+ // The address is an address in this process, so just copy it
+ memcpy((void *)addr, new_value.GetData(), *byte_size);
+ return true;
+ } else {
+ Process *process = nullptr;
+ if (exe_ctx)
+ process = exe_ctx->GetProcessPtr();
+ if (process) {
+ Status error;
+ return process->WriteMemory(addr, new_value.GetData(), *byte_size,
+ error) == *byte_size;
+ }
+ }
+ }
+ return false;
+}
+
+bool lldb_private::operator==(const lldb_private::CompilerType &lhs,
+ const lldb_private::CompilerType &rhs) {
+ return lhs.GetTypeSystem() == rhs.GetTypeSystem() &&
+ lhs.GetOpaqueQualType() == rhs.GetOpaqueQualType();
+}
+
+bool lldb_private::operator!=(const lldb_private::CompilerType &lhs,
+ const lldb_private::CompilerType &rhs) {
+ return !(lhs == rhs);
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/CxxModuleHandler.cpp b/contrib/llvm-project/lldb/source/Symbol/CxxModuleHandler.cpp
new file mode 100644
index 000000000000..68a2aab80bd6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/CxxModuleHandler.cpp
@@ -0,0 +1,283 @@
+//===-- CxxModuleHandler.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/CxxModuleHandler.h"
+
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Utility/Log.h"
+#include "clang/Sema/Lookup.h"
+#include "llvm/Support/Error.h"
+
+using namespace lldb_private;
+using namespace clang;
+
+CxxModuleHandler::CxxModuleHandler(ASTImporter &importer, ASTContext *target)
+ : m_importer(&importer),
+ m_sema(ClangASTContext::GetASTContext(target)->getSema()) {
+
+ std::initializer_list<const char *> supported_names = {
+ // containers
+ "deque",
+ "forward_list",
+ "list",
+ "queue",
+ "stack",
+ "vector",
+ // pointers
+ "shared_ptr",
+ "unique_ptr",
+ "weak_ptr",
+ // utility
+ "allocator",
+ };
+ m_supported_templates.insert(supported_names.begin(), supported_names.end());
+}
+
+/// Builds a list of scopes that point into the given context.
+///
+/// \param sema The sema that will be using the scopes.
+/// \param ctxt The context that the scope should look into.
+/// \param result A list of scopes. The scopes need to be freed by the caller
+/// (except the TUScope which is owned by the sema).
+static void makeScopes(Sema &sema, DeclContext *ctxt,
+ std::vector<Scope *> &result) {
+ // FIXME: The result should be a list of unique_ptrs, but the TUScope makes
+ // this currently impossible as it's owned by the Sema.
+
+ if (auto parent = ctxt->getParent()) {
+ makeScopes(sema, parent, result);
+
+ Scope *scope =
+ new Scope(result.back(), Scope::DeclScope, sema.getDiagnostics());
+ scope->setEntity(ctxt);
+ result.push_back(scope);
+ } else
+ result.push_back(sema.TUScope);
+}
+
+/// Uses the Sema to look up the given name in the given DeclContext.
+static std::unique_ptr<LookupResult>
+emulateLookupInCtxt(Sema &sema, llvm::StringRef name, DeclContext *ctxt) {
+ IdentifierInfo &ident = sema.getASTContext().Idents.get(name);
+
+ std::unique_ptr<LookupResult> lookup_result;
+ lookup_result.reset(new LookupResult(sema, DeclarationName(&ident),
+ SourceLocation(),
+ Sema::LookupOrdinaryName));
+
+ // Usually during parsing we already encountered the scopes we would use. But
+ // here don't have these scopes so we have to emulate the behavior of the
+ // Sema during parsing.
+ std::vector<Scope *> scopes;
+ makeScopes(sema, ctxt, scopes);
+
+ // Now actually perform the lookup with the sema.
+ sema.LookupName(*lookup_result, scopes.back());
+
+ // Delete all the allocated scopes beside the translation unit scope (which
+ // has depth 0).
+ for (Scope *s : scopes)
+ if (s->getDepth() != 0)
+ delete s;
+
+ return lookup_result;
+}
+
+/// Error class for handling problems when finding a certain DeclContext.
+struct MissingDeclContext : public llvm::ErrorInfo<MissingDeclContext> {
+
+ static char ID;
+
+ MissingDeclContext(DeclContext *context, std::string error)
+ : m_context(context), m_error(error) {}
+
+ DeclContext *m_context;
+ std::string m_error;
+
+ void log(llvm::raw_ostream &OS) const override {
+ OS << llvm::formatv("error when reconstructing context of kind {0}:{1}",
+ m_context->getDeclKindName(), m_error);
+ }
+
+ std::error_code convertToErrorCode() const override {
+ return llvm::inconvertibleErrorCode();
+ }
+};
+
+char MissingDeclContext::ID = 0;
+
+/// Given a foreign decl context, this function finds the equivalent local
+/// decl context in the ASTContext of the given Sema. Potentially deserializes
+/// decls from the 'std' module if necessary.
+static llvm::Expected<DeclContext *>
+getEqualLocalDeclContext(Sema &sema, DeclContext *foreign_ctxt) {
+
+ // Inline namespaces don't matter for lookups, so let's skip them.
+ while (foreign_ctxt && foreign_ctxt->isInlineNamespace())
+ foreign_ctxt = foreign_ctxt->getParent();
+
+ // If the foreign context is the TU, we just return the local TU.
+ if (foreign_ctxt->isTranslationUnit())
+ return sema.getASTContext().getTranslationUnitDecl();
+
+ // Recursively find/build the parent DeclContext.
+ llvm::Expected<DeclContext *> parent =
+ getEqualLocalDeclContext(sema, foreign_ctxt->getParent());
+ if (!parent)
+ return parent;
+
+ // We currently only support building namespaces.
+ if (foreign_ctxt->isNamespace()) {
+ NamedDecl *ns = llvm::dyn_cast<NamedDecl>(foreign_ctxt);
+ llvm::StringRef ns_name = ns->getName();
+
+ auto lookup_result = emulateLookupInCtxt(sema, ns_name, *parent);
+ for (NamedDecl *named_decl : *lookup_result) {
+ if (DeclContext *DC = llvm::dyn_cast<DeclContext>(named_decl))
+ return DC->getPrimaryContext();
+ }
+ return llvm::make_error<MissingDeclContext>(
+ foreign_ctxt,
+ "Couldn't find namespace " + ns->getQualifiedNameAsString());
+ }
+
+ return llvm::make_error<MissingDeclContext>(foreign_ctxt, "Unknown context ");
+}
+
+/// Returns true iff tryInstantiateStdTemplate supports instantiating a template
+/// with the given template arguments.
+static bool templateArgsAreSupported(ArrayRef<TemplateArgument> a) {
+ for (const TemplateArgument &arg : a) {
+ switch (arg.getKind()) {
+ case TemplateArgument::Type:
+ case TemplateArgument::Integral:
+ break;
+ default:
+ // TemplateArgument kind hasn't been handled yet.
+ return false;
+ }
+ }
+ return true;
+}
+
+/// Constructor function for Clang declarations. Ensures that the created
+/// declaration is registered with the ASTImporter.
+template <typename T, typename... Args>
+T *createDecl(ASTImporter &importer, Decl *from_d, Args &&... args) {
+ T *to_d = T::Create(std::forward<Args>(args)...);
+ importer.RegisterImportedDecl(from_d, to_d);
+ return to_d;
+}
+
+llvm::Optional<Decl *> CxxModuleHandler::tryInstantiateStdTemplate(Decl *d) {
+ // If we don't have a template to instiantiate, then there is nothing to do.
+ auto td = dyn_cast<ClassTemplateSpecializationDecl>(d);
+ if (!td)
+ return {};
+
+ // We only care about templates in the std namespace.
+ if (!td->getDeclContext()->isStdNamespace())
+ return {};
+
+ // We have a whitelist of supported template names.
+ if (m_supported_templates.find(td->getName()) == m_supported_templates.end())
+ return {};
+
+ // Early check if we even support instantiating this template. We do this
+ // before we import anything into the target AST.
+ auto &foreign_args = td->getTemplateInstantiationArgs();
+ if (!templateArgsAreSupported(foreign_args.asArray()))
+ return {};
+
+ // Find the local DeclContext that corresponds to the DeclContext of our
+ // decl we want to import.
+ auto to_context = getEqualLocalDeclContext(*m_sema, td->getDeclContext());
+ if (!to_context)
+ return {};
+
+ // Look up the template in our local context.
+ std::unique_ptr<LookupResult> lookup =
+ emulateLookupInCtxt(*m_sema, td->getName(), *to_context);
+
+ ClassTemplateDecl *new_class_template = nullptr;
+ for (auto LD : *lookup) {
+ if ((new_class_template = dyn_cast<ClassTemplateDecl>(LD)))
+ break;
+ }
+ if (!new_class_template)
+ return {};
+
+ // Import the foreign template arguments.
+ llvm::SmallVector<TemplateArgument, 4> imported_args;
+
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+
+ // If this logic is changed, also update templateArgsAreSupported.
+ for (const TemplateArgument &arg : foreign_args.asArray()) {
+ switch (arg.getKind()) {
+ case TemplateArgument::Type: {
+ llvm::Expected<QualType> type = m_importer->Import(arg.getAsType());
+ if (!type) {
+ LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
+ return {};
+ }
+ imported_args.push_back(TemplateArgument(*type));
+ break;
+ }
+ case TemplateArgument::Integral: {
+ llvm::APSInt integral = arg.getAsIntegral();
+ llvm::Expected<QualType> type =
+ m_importer->Import(arg.getIntegralType());
+ if (!type) {
+ LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
+ return {};
+ }
+ imported_args.push_back(
+ TemplateArgument(d->getASTContext(), integral, *type));
+ break;
+ }
+ default:
+ assert(false && "templateArgsAreSupported not updated?");
+ }
+ }
+
+ // Find the class template specialization declaration that
+ // corresponds to these arguments.
+ void *InsertPos = nullptr;
+ ClassTemplateSpecializationDecl *result =
+ new_class_template->findSpecialization(imported_args, InsertPos);
+
+ if (result) {
+ // We found an existing specialization in the module that fits our arguments
+ // so we can treat it as the result and register it with the ASTImporter.
+ m_importer->RegisterImportedDecl(d, result);
+ return result;
+ }
+
+ // Instantiate the template.
+ result = createDecl<ClassTemplateSpecializationDecl>(
+ *m_importer, d, m_sema->getASTContext(),
+ new_class_template->getTemplatedDecl()->getTagKind(),
+ new_class_template->getDeclContext(),
+ new_class_template->getTemplatedDecl()->getLocation(),
+ new_class_template->getLocation(), new_class_template, imported_args,
+ nullptr);
+
+ new_class_template->AddSpecialization(result, InsertPos);
+ if (new_class_template->isOutOfLine())
+ result->setLexicalDeclContext(
+ new_class_template->getLexicalDeclContext());
+ return result;
+}
+
+llvm::Optional<Decl *> CxxModuleHandler::Import(Decl *d) {
+ if (!isValid())
+ return {};
+
+ return tryInstantiateStdTemplate(d);
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/DWARFCallFrameInfo.cpp b/contrib/llvm-project/lldb/source/Symbol/DWARFCallFrameInfo.cpp
new file mode 100644
index 000000000000..0ab9fa4b7bbd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/DWARFCallFrameInfo.cpp
@@ -0,0 +1,1020 @@
+//===-- DWARFCallFrameInfo.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/DWARFCallFrameInfo.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Timer.h"
+#include <list>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// GetDwarfEHPtr
+//
+// Used for calls when the value type is specified by a DWARF EH Frame pointer
+// encoding.
+static uint64_t
+GetGNUEHPointer(const DataExtractor &DE, offset_t *offset_ptr,
+ uint32_t eh_ptr_enc, addr_t pc_rel_addr, addr_t text_addr,
+ addr_t data_addr) //, BSDRelocs *data_relocs) const
+{
+ if (eh_ptr_enc == DW_EH_PE_omit)
+ return ULLONG_MAX; // Value isn't in the buffer...
+
+ uint64_t baseAddress = 0;
+ uint64_t addressValue = 0;
+ const uint32_t addr_size = DE.GetAddressByteSize();
+ assert(addr_size == 4 || addr_size == 8);
+
+ bool signExtendValue = false;
+ // Decode the base part or adjust our offset
+ switch (eh_ptr_enc & 0x70) {
+ case DW_EH_PE_pcrel:
+ signExtendValue = true;
+ baseAddress = *offset_ptr;
+ if (pc_rel_addr != LLDB_INVALID_ADDRESS)
+ baseAddress += pc_rel_addr;
+ // else
+ // Log::GlobalWarning ("PC relative pointer encoding found with
+ // invalid pc relative address.");
+ break;
+
+ case DW_EH_PE_textrel:
+ signExtendValue = true;
+ if (text_addr != LLDB_INVALID_ADDRESS)
+ baseAddress = text_addr;
+ // else
+ // Log::GlobalWarning ("text relative pointer encoding being
+ // decoded with invalid text section address, setting base address
+ // to zero.");
+ break;
+
+ case DW_EH_PE_datarel:
+ signExtendValue = true;
+ if (data_addr != LLDB_INVALID_ADDRESS)
+ baseAddress = data_addr;
+ // else
+ // Log::GlobalWarning ("data relative pointer encoding being
+ // decoded with invalid data section address, setting base address
+ // to zero.");
+ break;
+
+ case DW_EH_PE_funcrel:
+ signExtendValue = true;
+ break;
+
+ case DW_EH_PE_aligned: {
+ // SetPointerSize should be called prior to extracting these so the pointer
+ // size is cached
+ assert(addr_size != 0);
+ if (addr_size) {
+ // Align to a address size boundary first
+ uint32_t alignOffset = *offset_ptr % addr_size;
+ if (alignOffset)
+ offset_ptr += addr_size - alignOffset;
+ }
+ } break;
+
+ default:
+ break;
+ }
+
+ // Decode the value part
+ switch (eh_ptr_enc & DW_EH_PE_MASK_ENCODING) {
+ case DW_EH_PE_absptr: {
+ addressValue = DE.GetAddress(offset_ptr);
+ // if (data_relocs)
+ // addressValue = data_relocs->Relocate(*offset_ptr -
+ // addr_size, *this, addressValue);
+ } break;
+ case DW_EH_PE_uleb128:
+ addressValue = DE.GetULEB128(offset_ptr);
+ break;
+ case DW_EH_PE_udata2:
+ addressValue = DE.GetU16(offset_ptr);
+ break;
+ case DW_EH_PE_udata4:
+ addressValue = DE.GetU32(offset_ptr);
+ break;
+ case DW_EH_PE_udata8:
+ addressValue = DE.GetU64(offset_ptr);
+ break;
+ case DW_EH_PE_sleb128:
+ addressValue = DE.GetSLEB128(offset_ptr);
+ break;
+ case DW_EH_PE_sdata2:
+ addressValue = (int16_t)DE.GetU16(offset_ptr);
+ break;
+ case DW_EH_PE_sdata4:
+ addressValue = (int32_t)DE.GetU32(offset_ptr);
+ break;
+ case DW_EH_PE_sdata8:
+ addressValue = (int64_t)DE.GetU64(offset_ptr);
+ break;
+ default:
+ // Unhandled encoding type
+ assert(eh_ptr_enc);
+ break;
+ }
+
+ // Since we promote everything to 64 bit, we may need to sign extend
+ if (signExtendValue && addr_size < sizeof(baseAddress)) {
+ uint64_t sign_bit = 1ull << ((addr_size * 8ull) - 1ull);
+ if (sign_bit & addressValue) {
+ uint64_t mask = ~sign_bit + 1;
+ addressValue |= mask;
+ }
+ }
+ return baseAddress + addressValue;
+}
+
+DWARFCallFrameInfo::DWARFCallFrameInfo(ObjectFile &objfile,
+ SectionSP &section_sp, Type type)
+ : m_objfile(objfile), m_section_sp(section_sp), m_type(type) {}
+
+bool DWARFCallFrameInfo::GetUnwindPlan(const Address &addr,
+ UnwindPlan &unwind_plan) {
+ return GetUnwindPlan(AddressRange(addr, 1), unwind_plan);
+}
+
+bool DWARFCallFrameInfo::GetUnwindPlan(const AddressRange &range,
+ UnwindPlan &unwind_plan) {
+ FDEEntryMap::Entry fde_entry;
+ Address addr = range.GetBaseAddress();
+
+ // Make sure that the Address we're searching for is the same object file as
+ // this DWARFCallFrameInfo, we only store File offsets in m_fde_index.
+ ModuleSP module_sp = addr.GetModule();
+ if (module_sp.get() == nullptr || module_sp->GetObjectFile() == nullptr ||
+ module_sp->GetObjectFile() != &m_objfile)
+ return false;
+
+ if (llvm::Optional<FDEEntryMap::Entry> entry = GetFirstFDEEntryInRange(range))
+ return FDEToUnwindPlan(entry->data, addr, unwind_plan);
+ return false;
+}
+
+bool DWARFCallFrameInfo::GetAddressRange(Address addr, AddressRange &range) {
+
+ // Make sure that the Address we're searching for is the same object file as
+ // this DWARFCallFrameInfo, we only store File offsets in m_fde_index.
+ ModuleSP module_sp = addr.GetModule();
+ if (module_sp.get() == nullptr || module_sp->GetObjectFile() == nullptr ||
+ module_sp->GetObjectFile() != &m_objfile)
+ return false;
+
+ if (m_section_sp.get() == nullptr || m_section_sp->IsEncrypted())
+ return false;
+ GetFDEIndex();
+ FDEEntryMap::Entry *fde_entry =
+ m_fde_index.FindEntryThatContains(addr.GetFileAddress());
+ if (!fde_entry)
+ return false;
+
+ range = AddressRange(fde_entry->base, fde_entry->size,
+ m_objfile.GetSectionList());
+ return true;
+}
+
+llvm::Optional<DWARFCallFrameInfo::FDEEntryMap::Entry>
+DWARFCallFrameInfo::GetFirstFDEEntryInRange(const AddressRange &range) {
+ if (!m_section_sp || m_section_sp->IsEncrypted())
+ return llvm::None;
+
+ GetFDEIndex();
+
+ addr_t start_file_addr = range.GetBaseAddress().GetFileAddress();
+ const FDEEntryMap::Entry *fde =
+ m_fde_index.FindEntryThatContainsOrFollows(start_file_addr);
+ if (fde && fde->DoesIntersect(
+ FDEEntryMap::Range(start_file_addr, range.GetByteSize())))
+ return *fde;
+
+ return llvm::None;
+}
+
+void DWARFCallFrameInfo::GetFunctionAddressAndSizeVector(
+ FunctionAddressAndSizeVector &function_info) {
+ GetFDEIndex();
+ const size_t count = m_fde_index.GetSize();
+ function_info.Clear();
+ if (count > 0)
+ function_info.Reserve(count);
+ for (size_t i = 0; i < count; ++i) {
+ const FDEEntryMap::Entry *func_offset_data_entry =
+ m_fde_index.GetEntryAtIndex(i);
+ if (func_offset_data_entry) {
+ FunctionAddressAndSizeVector::Entry function_offset_entry(
+ func_offset_data_entry->base, func_offset_data_entry->size);
+ function_info.Append(function_offset_entry);
+ }
+ }
+}
+
+const DWARFCallFrameInfo::CIE *
+DWARFCallFrameInfo::GetCIE(dw_offset_t cie_offset) {
+ cie_map_t::iterator pos = m_cie_map.find(cie_offset);
+
+ if (pos != m_cie_map.end()) {
+ // Parse and cache the CIE
+ if (pos->second == nullptr)
+ pos->second = ParseCIE(cie_offset);
+
+ return pos->second.get();
+ }
+ return nullptr;
+}
+
+DWARFCallFrameInfo::CIESP
+DWARFCallFrameInfo::ParseCIE(const dw_offset_t cie_offset) {
+ CIESP cie_sp(new CIE(cie_offset));
+ lldb::offset_t offset = cie_offset;
+ if (!m_cfi_data_initialized)
+ GetCFIData();
+ uint32_t length = m_cfi_data.GetU32(&offset);
+ dw_offset_t cie_id, end_offset;
+ bool is_64bit = (length == UINT32_MAX);
+ if (is_64bit) {
+ length = m_cfi_data.GetU64(&offset);
+ cie_id = m_cfi_data.GetU64(&offset);
+ end_offset = cie_offset + length + 12;
+ } else {
+ cie_id = m_cfi_data.GetU32(&offset);
+ end_offset = cie_offset + length + 4;
+ }
+ if (length > 0 && ((m_type == DWARF && cie_id == UINT32_MAX) ||
+ (m_type == EH && cie_id == 0ul))) {
+ size_t i;
+ // cie.offset = cie_offset;
+ // cie.length = length;
+ // cie.cieID = cieID;
+ cie_sp->ptr_encoding = DW_EH_PE_absptr; // default
+ cie_sp->version = m_cfi_data.GetU8(&offset);
+ if (cie_sp->version > CFI_VERSION4) {
+ Host::SystemLog(Host::eSystemLogError,
+ "CIE parse error: CFI version %d is not supported\n",
+ cie_sp->version);
+ return nullptr;
+ }
+
+ for (i = 0; i < CFI_AUG_MAX_SIZE; ++i) {
+ cie_sp->augmentation[i] = m_cfi_data.GetU8(&offset);
+ if (cie_sp->augmentation[i] == '\0') {
+ // Zero out remaining bytes in augmentation string
+ for (size_t j = i + 1; j < CFI_AUG_MAX_SIZE; ++j)
+ cie_sp->augmentation[j] = '\0';
+
+ break;
+ }
+ }
+
+ if (i == CFI_AUG_MAX_SIZE &&
+ cie_sp->augmentation[CFI_AUG_MAX_SIZE - 1] != '\0') {
+ Host::SystemLog(Host::eSystemLogError,
+ "CIE parse error: CIE augmentation string was too large "
+ "for the fixed sized buffer of %d bytes.\n",
+ CFI_AUG_MAX_SIZE);
+ return nullptr;
+ }
+
+ // m_cfi_data uses address size from target architecture of the process may
+ // ignore these fields?
+ if (m_type == DWARF && cie_sp->version >= CFI_VERSION4) {
+ cie_sp->address_size = m_cfi_data.GetU8(&offset);
+ cie_sp->segment_size = m_cfi_data.GetU8(&offset);
+ }
+
+ cie_sp->code_align = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ cie_sp->data_align = (int32_t)m_cfi_data.GetSLEB128(&offset);
+
+ cie_sp->return_addr_reg_num =
+ m_type == DWARF && cie_sp->version >= CFI_VERSION3
+ ? static_cast<uint32_t>(m_cfi_data.GetULEB128(&offset))
+ : m_cfi_data.GetU8(&offset);
+
+ if (cie_sp->augmentation[0]) {
+ // Get the length of the eh_frame augmentation data which starts with a
+ // ULEB128 length in bytes
+ const size_t aug_data_len = (size_t)m_cfi_data.GetULEB128(&offset);
+ const size_t aug_data_end = offset + aug_data_len;
+ const size_t aug_str_len = strlen(cie_sp->augmentation);
+ // A 'z' may be present as the first character of the string.
+ // If present, the Augmentation Data field shall be present. The contents
+ // of the Augmentation Data shall be interpreted according to other
+ // characters in the Augmentation String.
+ if (cie_sp->augmentation[0] == 'z') {
+ // Extract the Augmentation Data
+ size_t aug_str_idx = 0;
+ for (aug_str_idx = 1; aug_str_idx < aug_str_len; aug_str_idx++) {
+ char aug = cie_sp->augmentation[aug_str_idx];
+ switch (aug) {
+ case 'L':
+ // Indicates the presence of one argument in the Augmentation Data
+ // of the CIE, and a corresponding argument in the Augmentation
+ // Data of the FDE. The argument in the Augmentation Data of the
+ // CIE is 1-byte and represents the pointer encoding used for the
+ // argument in the Augmentation Data of the FDE, which is the
+ // address of a language-specific data area (LSDA). The size of the
+ // LSDA pointer is specified by the pointer encoding used.
+ cie_sp->lsda_addr_encoding = m_cfi_data.GetU8(&offset);
+ break;
+
+ case 'P':
+ // Indicates the presence of two arguments in the Augmentation Data
+ // of the CIE. The first argument is 1-byte and represents the
+ // pointer encoding used for the second argument, which is the
+ // address of a personality routine handler. The size of the
+ // personality routine pointer is specified by the pointer encoding
+ // used.
+ //
+ // The address of the personality function will be stored at this
+ // location. Pre-execution, it will be all zero's so don't read it
+ // until we're trying to do an unwind & the reloc has been
+ // resolved.
+ {
+ uint8_t arg_ptr_encoding = m_cfi_data.GetU8(&offset);
+ const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress();
+ cie_sp->personality_loc = GetGNUEHPointer(
+ m_cfi_data, &offset, arg_ptr_encoding, pc_rel_addr,
+ LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS);
+ }
+ break;
+
+ case 'R':
+ // A 'R' may be present at any position after the
+ // first character of the string. The Augmentation Data shall
+ // include a 1 byte argument that represents the pointer encoding
+ // for the address pointers used in the FDE. Example: 0x1B ==
+ // DW_EH_PE_pcrel | DW_EH_PE_sdata4
+ cie_sp->ptr_encoding = m_cfi_data.GetU8(&offset);
+ break;
+ }
+ }
+ } else if (strcmp(cie_sp->augmentation, "eh") == 0) {
+ // If the Augmentation string has the value "eh", then the EH Data
+ // field shall be present
+ }
+
+ // Set the offset to be the end of the augmentation data just in case we
+ // didn't understand any of the data.
+ offset = (uint32_t)aug_data_end;
+ }
+
+ if (end_offset > offset) {
+ cie_sp->inst_offset = offset;
+ cie_sp->inst_length = end_offset - offset;
+ }
+ while (offset < end_offset) {
+ uint8_t inst = m_cfi_data.GetU8(&offset);
+ uint8_t primary_opcode = inst & 0xC0;
+ uint8_t extended_opcode = inst & 0x3F;
+
+ if (!HandleCommonDwarfOpcode(primary_opcode, extended_opcode,
+ cie_sp->data_align, offset,
+ cie_sp->initial_row))
+ break; // Stop if we hit an unrecognized opcode
+ }
+ }
+
+ return cie_sp;
+}
+
+void DWARFCallFrameInfo::GetCFIData() {
+ if (!m_cfi_data_initialized) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log)
+ m_objfile.GetModule()->LogMessage(log, "Reading EH frame info");
+ m_objfile.ReadSectionData(m_section_sp.get(), m_cfi_data);
+ m_cfi_data_initialized = true;
+ }
+}
+// Scan through the eh_frame or debug_frame section looking for FDEs and noting
+// the start/end addresses of the functions and a pointer back to the
+// function's FDE for later expansion. Internalize CIEs as we come across them.
+
+void DWARFCallFrameInfo::GetFDEIndex() {
+ if (m_section_sp.get() == nullptr || m_section_sp->IsEncrypted())
+ return;
+
+ if (m_fde_index_initialized)
+ return;
+
+ std::lock_guard<std::mutex> guard(m_fde_index_mutex);
+
+ if (m_fde_index_initialized) // if two threads hit the locker
+ return;
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s - %s", LLVM_PRETTY_FUNCTION,
+ m_objfile.GetFileSpec().GetFilename().AsCString(""));
+
+ bool clear_address_zeroth_bit = false;
+ if (ArchSpec arch = m_objfile.GetArchitecture()) {
+ if (arch.GetTriple().getArch() == llvm::Triple::arm ||
+ arch.GetTriple().getArch() == llvm::Triple::thumb)
+ clear_address_zeroth_bit = true;
+ }
+
+ lldb::offset_t offset = 0;
+ if (!m_cfi_data_initialized)
+ GetCFIData();
+ while (m_cfi_data.ValidOffsetForDataOfSize(offset, 8)) {
+ const dw_offset_t current_entry = offset;
+ dw_offset_t cie_id, next_entry, cie_offset;
+ uint32_t len = m_cfi_data.GetU32(&offset);
+ bool is_64bit = (len == UINT32_MAX);
+ if (is_64bit) {
+ len = m_cfi_data.GetU64(&offset);
+ cie_id = m_cfi_data.GetU64(&offset);
+ next_entry = current_entry + len + 12;
+ cie_offset = current_entry + 12 - cie_id;
+ } else {
+ cie_id = m_cfi_data.GetU32(&offset);
+ next_entry = current_entry + len + 4;
+ cie_offset = current_entry + 4 - cie_id;
+ }
+
+ if (next_entry > m_cfi_data.GetByteSize() + 1) {
+ Host::SystemLog(Host::eSystemLogError, "error: Invalid fde/cie next "
+ "entry offset of 0x%x found in "
+ "cie/fde at 0x%x\n",
+ next_entry, current_entry);
+ // Don't trust anything in this eh_frame section if we find blatantly
+ // invalid data.
+ m_fde_index.Clear();
+ m_fde_index_initialized = true;
+ return;
+ }
+
+ // An FDE entry contains CIE_pointer in debug_frame in same place as cie_id
+ // in eh_frame. CIE_pointer is an offset into the .debug_frame section. So,
+ // variable cie_offset should be equal to cie_id for debug_frame.
+ // FDE entries with cie_id == 0 shouldn't be ignored for it.
+ if ((cie_id == 0 && m_type == EH) || cie_id == UINT32_MAX || len == 0) {
+ auto cie_sp = ParseCIE(current_entry);
+ if (!cie_sp) {
+ // Cannot parse, the reason is already logged
+ m_fde_index.Clear();
+ m_fde_index_initialized = true;
+ return;
+ }
+
+ m_cie_map[current_entry] = std::move(cie_sp);
+ offset = next_entry;
+ continue;
+ }
+
+ if (m_type == DWARF)
+ cie_offset = cie_id;
+
+ if (cie_offset > m_cfi_data.GetByteSize()) {
+ Host::SystemLog(Host::eSystemLogError,
+ "error: Invalid cie offset of 0x%x "
+ "found in cie/fde at 0x%x\n",
+ cie_offset, current_entry);
+ // Don't trust anything in this eh_frame section if we find blatantly
+ // invalid data.
+ m_fde_index.Clear();
+ m_fde_index_initialized = true;
+ return;
+ }
+
+ const CIE *cie = GetCIE(cie_offset);
+ if (cie) {
+ const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress();
+ const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS;
+ const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS;
+
+ lldb::addr_t addr =
+ GetGNUEHPointer(m_cfi_data, &offset, cie->ptr_encoding, pc_rel_addr,
+ text_addr, data_addr);
+ if (clear_address_zeroth_bit)
+ addr &= ~1ull;
+
+ lldb::addr_t length = GetGNUEHPointer(
+ m_cfi_data, &offset, cie->ptr_encoding & DW_EH_PE_MASK_ENCODING,
+ pc_rel_addr, text_addr, data_addr);
+ FDEEntryMap::Entry fde(addr, length, current_entry);
+ m_fde_index.Append(fde);
+ } else {
+ Host::SystemLog(Host::eSystemLogError, "error: unable to find CIE at "
+ "0x%8.8x for cie_id = 0x%8.8x for "
+ "entry at 0x%8.8x.\n",
+ cie_offset, cie_id, current_entry);
+ }
+ offset = next_entry;
+ }
+ m_fde_index.Sort();
+ m_fde_index_initialized = true;
+}
+
+bool DWARFCallFrameInfo::FDEToUnwindPlan(dw_offset_t dwarf_offset,
+ Address startaddr,
+ UnwindPlan &unwind_plan) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND);
+ lldb::offset_t offset = dwarf_offset;
+ lldb::offset_t current_entry = offset;
+
+ if (m_section_sp.get() == nullptr || m_section_sp->IsEncrypted())
+ return false;
+
+ if (!m_cfi_data_initialized)
+ GetCFIData();
+
+ uint32_t length = m_cfi_data.GetU32(&offset);
+ dw_offset_t cie_offset;
+ bool is_64bit = (length == UINT32_MAX);
+ if (is_64bit) {
+ length = m_cfi_data.GetU64(&offset);
+ cie_offset = m_cfi_data.GetU64(&offset);
+ } else {
+ cie_offset = m_cfi_data.GetU32(&offset);
+ }
+
+ // FDE entries with zeroth cie_offset may occur for debug_frame.
+ assert(!(m_type == EH && 0 == cie_offset) && cie_offset != UINT32_MAX);
+
+ // Translate the CIE_id from the eh_frame format, which is relative to the
+ // FDE offset, into a __eh_frame section offset
+ if (m_type == EH) {
+ unwind_plan.SetSourceName("eh_frame CFI");
+ cie_offset = current_entry + (is_64bit ? 12 : 4) - cie_offset;
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ } else {
+ unwind_plan.SetSourceName("DWARF CFI");
+ // In theory the debug_frame info should be valid at all call sites
+ // ("asynchronous unwind info" as it is sometimes called) but in practice
+ // gcc et al all emit call frame info for the prologue and call sites, but
+ // not for the epilogue or all the other locations during the function
+ // reliably.
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ }
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolYes);
+
+ const CIE *cie = GetCIE(cie_offset);
+ assert(cie != nullptr);
+
+ const dw_offset_t end_offset = current_entry + length + (is_64bit ? 12 : 4);
+
+ const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress();
+ const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS;
+ const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t range_base =
+ GetGNUEHPointer(m_cfi_data, &offset, cie->ptr_encoding, pc_rel_addr,
+ text_addr, data_addr);
+ lldb::addr_t range_len = GetGNUEHPointer(
+ m_cfi_data, &offset, cie->ptr_encoding & DW_EH_PE_MASK_ENCODING,
+ pc_rel_addr, text_addr, data_addr);
+ AddressRange range(range_base, m_objfile.GetAddressByteSize(),
+ m_objfile.GetSectionList());
+ range.SetByteSize(range_len);
+
+ addr_t lsda_data_file_address = LLDB_INVALID_ADDRESS;
+
+ if (cie->augmentation[0] == 'z') {
+ uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ if (aug_data_len != 0 && cie->lsda_addr_encoding != DW_EH_PE_omit) {
+ offset_t saved_offset = offset;
+ lsda_data_file_address =
+ GetGNUEHPointer(m_cfi_data, &offset, cie->lsda_addr_encoding,
+ pc_rel_addr, text_addr, data_addr);
+ if (offset - saved_offset != aug_data_len) {
+ // There is more in the augmentation region than we know how to process;
+ // don't read anything.
+ lsda_data_file_address = LLDB_INVALID_ADDRESS;
+ }
+ offset = saved_offset;
+ }
+ offset += aug_data_len;
+ }
+ Address lsda_data;
+ Address personality_function_ptr;
+
+ if (lsda_data_file_address != LLDB_INVALID_ADDRESS &&
+ cie->personality_loc != LLDB_INVALID_ADDRESS) {
+ m_objfile.GetModule()->ResolveFileAddress(lsda_data_file_address,
+ lsda_data);
+ m_objfile.GetModule()->ResolveFileAddress(cie->personality_loc,
+ personality_function_ptr);
+ }
+
+ if (lsda_data.IsValid() && personality_function_ptr.IsValid()) {
+ unwind_plan.SetLSDAAddress(lsda_data);
+ unwind_plan.SetPersonalityFunctionPtr(personality_function_ptr);
+ }
+
+ uint32_t code_align = cie->code_align;
+ int32_t data_align = cie->data_align;
+
+ unwind_plan.SetPlanValidAddressRange(range);
+ UnwindPlan::Row *cie_initial_row = new UnwindPlan::Row;
+ *cie_initial_row = cie->initial_row;
+ UnwindPlan::RowSP row(cie_initial_row);
+
+ unwind_plan.SetRegisterKind(GetRegisterKind());
+ unwind_plan.SetReturnAddressRegister(cie->return_addr_reg_num);
+
+ std::vector<UnwindPlan::RowSP> stack;
+
+ UnwindPlan::Row::RegisterLocation reg_location;
+ while (m_cfi_data.ValidOffset(offset) && offset < end_offset) {
+ uint8_t inst = m_cfi_data.GetU8(&offset);
+ uint8_t primary_opcode = inst & 0xC0;
+ uint8_t extended_opcode = inst & 0x3F;
+
+ if (!HandleCommonDwarfOpcode(primary_opcode, extended_opcode, data_align,
+ offset, *row)) {
+ if (primary_opcode) {
+ switch (primary_opcode) {
+ case DW_CFA_advance_loc: // (Row Creation Instruction)
+ { // 0x40 - high 2 bits are 0x1, lower 6 bits are delta
+ // takes a single argument that represents a constant delta. The
+ // required action is to create a new table row with a location value
+ // that is computed by taking the current entry's location value and
+ // adding (delta * code_align). All other values in the new row are
+ // initially identical to the current row.
+ unwind_plan.AppendRow(row);
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+ row->SlideOffset(extended_opcode * code_align);
+ break;
+ }
+
+ case DW_CFA_restore: { // 0xC0 - high 2 bits are 0x3, lower 6 bits are
+ // register
+ // takes a single argument that represents a register number. The
+ // required action is to change the rule for the indicated register
+ // to the rule assigned it by the initial_instructions in the CIE.
+ uint32_t reg_num = extended_opcode;
+ // We only keep enough register locations around to unwind what is in
+ // our thread, and these are organized by the register index in that
+ // state, so we need to convert our eh_frame register number from the
+ // EH frame info, to a register index
+
+ if (unwind_plan.IsValidRowIndex(0) &&
+ unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num,
+ reg_location))
+ row->SetRegisterInfo(reg_num, reg_location);
+ break;
+ }
+ }
+ } else {
+ switch (extended_opcode) {
+ case DW_CFA_set_loc: // 0x1 (Row Creation Instruction)
+ {
+ // DW_CFA_set_loc takes a single argument that represents an address.
+ // The required action is to create a new table row using the
+ // specified address as the location. All other values in the new row
+ // are initially identical to the current row. The new location value
+ // should always be greater than the current one.
+ unwind_plan.AppendRow(row);
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+ row->SetOffset(m_cfi_data.GetPointer(&offset) -
+ startaddr.GetFileAddress());
+ break;
+ }
+
+ case DW_CFA_advance_loc1: // 0x2 (Row Creation Instruction)
+ {
+ // takes a single uword argument that represents a constant delta.
+ // This instruction is identical to DW_CFA_advance_loc except for the
+ // encoding and size of the delta argument.
+ unwind_plan.AppendRow(row);
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+ row->SlideOffset(m_cfi_data.GetU8(&offset) * code_align);
+ break;
+ }
+
+ case DW_CFA_advance_loc2: // 0x3 (Row Creation Instruction)
+ {
+ // takes a single uword argument that represents a constant delta.
+ // This instruction is identical to DW_CFA_advance_loc except for the
+ // encoding and size of the delta argument.
+ unwind_plan.AppendRow(row);
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+ row->SlideOffset(m_cfi_data.GetU16(&offset) * code_align);
+ break;
+ }
+
+ case DW_CFA_advance_loc4: // 0x4 (Row Creation Instruction)
+ {
+ // takes a single uword argument that represents a constant delta.
+ // This instruction is identical to DW_CFA_advance_loc except for the
+ // encoding and size of the delta argument.
+ unwind_plan.AppendRow(row);
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+ row->SlideOffset(m_cfi_data.GetU32(&offset) * code_align);
+ break;
+ }
+
+ case DW_CFA_restore_extended: // 0x6
+ {
+ // takes a single unsigned LEB128 argument that represents a register
+ // number. This instruction is identical to DW_CFA_restore except for
+ // the encoding and size of the register argument.
+ uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ if (unwind_plan.IsValidRowIndex(0) &&
+ unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num,
+ reg_location))
+ row->SetRegisterInfo(reg_num, reg_location);
+ break;
+ }
+
+ case DW_CFA_remember_state: // 0xA
+ {
+ // These instructions define a stack of information. Encountering the
+ // DW_CFA_remember_state instruction means to save the rules for
+ // every register on the current row on the stack. Encountering the
+ // DW_CFA_restore_state instruction means to pop the set of rules off
+ // the stack and place them in the current row. (This operation is
+ // useful for compilers that move epilogue code into the body of a
+ // function.)
+ stack.push_back(row);
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+ break;
+ }
+
+ case DW_CFA_restore_state: // 0xB
+ {
+ // These instructions define a stack of information. Encountering the
+ // DW_CFA_remember_state instruction means to save the rules for
+ // every register on the current row on the stack. Encountering the
+ // DW_CFA_restore_state instruction means to pop the set of rules off
+ // the stack and place them in the current row. (This operation is
+ // useful for compilers that move epilogue code into the body of a
+ // function.)
+ if (stack.empty()) {
+ if (log)
+ log->Printf("DWARFCallFrameInfo::%s(dwarf_offset: %" PRIx32
+ ", startaddr: %" PRIx64
+ " encountered DW_CFA_restore_state but state stack "
+ "is empty. Corrupt unwind info?",
+ __FUNCTION__, dwarf_offset,
+ startaddr.GetFileAddress());
+ break;
+ }
+ lldb::addr_t offset = row->GetOffset();
+ row = stack.back();
+ stack.pop_back();
+ row->SetOffset(offset);
+ break;
+ }
+
+ case DW_CFA_GNU_args_size: // 0x2e
+ {
+ // The DW_CFA_GNU_args_size instruction takes an unsigned LEB128
+ // operand representing an argument size. This instruction specifies
+ // the total of the size of the arguments which have been pushed onto
+ // the stack.
+
+ // TODO: Figure out how we should handle this.
+ m_cfi_data.GetULEB128(&offset);
+ break;
+ }
+
+ case DW_CFA_val_offset: // 0x14
+ case DW_CFA_val_offset_sf: // 0x15
+ default:
+ break;
+ }
+ }
+ }
+ }
+ unwind_plan.AppendRow(row);
+
+ return true;
+}
+
+bool DWARFCallFrameInfo::HandleCommonDwarfOpcode(uint8_t primary_opcode,
+ uint8_t extended_opcode,
+ int32_t data_align,
+ lldb::offset_t &offset,
+ UnwindPlan::Row &row) {
+ UnwindPlan::Row::RegisterLocation reg_location;
+
+ if (primary_opcode) {
+ switch (primary_opcode) {
+ case DW_CFA_offset: { // 0x80 - high 2 bits are 0x2, lower 6 bits are
+ // register
+ // takes two arguments: an unsigned LEB128 constant representing a
+ // factored offset and a register number. The required action is to
+ // change the rule for the register indicated by the register number to
+ // be an offset(N) rule with a value of (N = factored offset *
+ // data_align).
+ uint8_t reg_num = extended_opcode;
+ int32_t op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align;
+ reg_location.SetAtCFAPlusOffset(op_offset);
+ row.SetRegisterInfo(reg_num, reg_location);
+ return true;
+ }
+ }
+ } else {
+ switch (extended_opcode) {
+ case DW_CFA_nop: // 0x0
+ return true;
+
+ case DW_CFA_offset_extended: // 0x5
+ {
+ // takes two unsigned LEB128 arguments representing a register number and
+ // a factored offset. This instruction is identical to DW_CFA_offset
+ // except for the encoding and size of the register argument.
+ uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ int32_t op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align;
+ UnwindPlan::Row::RegisterLocation reg_location;
+ reg_location.SetAtCFAPlusOffset(op_offset);
+ row.SetRegisterInfo(reg_num, reg_location);
+ return true;
+ }
+
+ case DW_CFA_undefined: // 0x7
+ {
+ // takes a single unsigned LEB128 argument that represents a register
+ // number. The required action is to set the rule for the specified
+ // register to undefined.
+ uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ UnwindPlan::Row::RegisterLocation reg_location;
+ reg_location.SetUndefined();
+ row.SetRegisterInfo(reg_num, reg_location);
+ return true;
+ }
+
+ case DW_CFA_same_value: // 0x8
+ {
+ // takes a single unsigned LEB128 argument that represents a register
+ // number. The required action is to set the rule for the specified
+ // register to same value.
+ uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ UnwindPlan::Row::RegisterLocation reg_location;
+ reg_location.SetSame();
+ row.SetRegisterInfo(reg_num, reg_location);
+ return true;
+ }
+
+ case DW_CFA_register: // 0x9
+ {
+ // takes two unsigned LEB128 arguments representing register numbers. The
+ // required action is to set the rule for the first register to be the
+ // second register.
+ uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ uint32_t other_reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ UnwindPlan::Row::RegisterLocation reg_location;
+ reg_location.SetInRegister(other_reg_num);
+ row.SetRegisterInfo(reg_num, reg_location);
+ return true;
+ }
+
+ case DW_CFA_def_cfa: // 0xC (CFA Definition Instruction)
+ {
+ // Takes two unsigned LEB128 operands representing a register number and
+ // a (non-factored) offset. The required action is to define the current
+ // CFA rule to use the provided register and offset.
+ uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ int32_t op_offset = (int32_t)m_cfi_data.GetULEB128(&offset);
+ row.GetCFAValue().SetIsRegisterPlusOffset(reg_num, op_offset);
+ return true;
+ }
+
+ case DW_CFA_def_cfa_register: // 0xD (CFA Definition Instruction)
+ {
+ // takes a single unsigned LEB128 argument representing a register
+ // number. The required action is to define the current CFA rule to use
+ // the provided register (but to keep the old offset).
+ uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ row.GetCFAValue().SetIsRegisterPlusOffset(reg_num,
+ row.GetCFAValue().GetOffset());
+ return true;
+ }
+
+ case DW_CFA_def_cfa_offset: // 0xE (CFA Definition Instruction)
+ {
+ // Takes a single unsigned LEB128 operand representing a (non-factored)
+ // offset. The required action is to define the current CFA rule to use
+ // the provided offset (but to keep the old register).
+ int32_t op_offset = (int32_t)m_cfi_data.GetULEB128(&offset);
+ row.GetCFAValue().SetIsRegisterPlusOffset(
+ row.GetCFAValue().GetRegisterNumber(), op_offset);
+ return true;
+ }
+
+ case DW_CFA_def_cfa_expression: // 0xF (CFA Definition Instruction)
+ {
+ size_t block_len = (size_t)m_cfi_data.GetULEB128(&offset);
+ const uint8_t *block_data =
+ static_cast<const uint8_t *>(m_cfi_data.GetData(&offset, block_len));
+ row.GetCFAValue().SetIsDWARFExpression(block_data, block_len);
+ return true;
+ }
+
+ case DW_CFA_expression: // 0x10
+ {
+ // Takes two operands: an unsigned LEB128 value representing a register
+ // number, and a DW_FORM_block value representing a DWARF expression. The
+ // required action is to change the rule for the register indicated by
+ // the register number to be an expression(E) rule where E is the DWARF
+ // expression. That is, the DWARF expression computes the address. The
+ // value of the CFA is pushed on the DWARF evaluation stack prior to
+ // execution of the DWARF expression.
+ uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ const uint8_t *block_data =
+ static_cast<const uint8_t *>(m_cfi_data.GetData(&offset, block_len));
+ UnwindPlan::Row::RegisterLocation reg_location;
+ reg_location.SetAtDWARFExpression(block_data, block_len);
+ row.SetRegisterInfo(reg_num, reg_location);
+ return true;
+ }
+
+ case DW_CFA_offset_extended_sf: // 0x11
+ {
+ // takes two operands: an unsigned LEB128 value representing a register
+ // number and a signed LEB128 factored offset. This instruction is
+ // identical to DW_CFA_offset_extended except that the second operand is
+ // signed and factored.
+ uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ int32_t op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align;
+ UnwindPlan::Row::RegisterLocation reg_location;
+ reg_location.SetAtCFAPlusOffset(op_offset);
+ row.SetRegisterInfo(reg_num, reg_location);
+ return true;
+ }
+
+ case DW_CFA_def_cfa_sf: // 0x12 (CFA Definition Instruction)
+ {
+ // Takes two operands: an unsigned LEB128 value representing a register
+ // number and a signed LEB128 factored offset. This instruction is
+ // identical to DW_CFA_def_cfa except that the second operand is signed
+ // and factored.
+ uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ int32_t op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align;
+ row.GetCFAValue().SetIsRegisterPlusOffset(reg_num, op_offset);
+ return true;
+ }
+
+ case DW_CFA_def_cfa_offset_sf: // 0x13 (CFA Definition Instruction)
+ {
+ // takes a signed LEB128 operand representing a factored offset. This
+ // instruction is identical to DW_CFA_def_cfa_offset except that the
+ // operand is signed and factored.
+ int32_t op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align;
+ uint32_t cfa_regnum = row.GetCFAValue().GetRegisterNumber();
+ row.GetCFAValue().SetIsRegisterPlusOffset(cfa_regnum, op_offset);
+ return true;
+ }
+
+ case DW_CFA_val_expression: // 0x16
+ {
+ // takes two operands: an unsigned LEB128 value representing a register
+ // number, and a DW_FORM_block value representing a DWARF expression. The
+ // required action is to change the rule for the register indicated by
+ // the register number to be a val_expression(E) rule where E is the
+ // DWARF expression. That is, the DWARF expression computes the value of
+ // the given register. The value of the CFA is pushed on the DWARF
+ // evaluation stack prior to execution of the DWARF expression.
+ uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset);
+ const uint8_t *block_data =
+ (const uint8_t *)m_cfi_data.GetData(&offset, block_len);
+ reg_location.SetIsDWARFExpression(block_data, block_len);
+ row.SetRegisterInfo(reg_num, reg_location);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void DWARFCallFrameInfo::ForEachFDEEntries(
+ const std::function<bool(lldb::addr_t, uint32_t, dw_offset_t)> &callback) {
+ GetFDEIndex();
+
+ for (size_t i = 0, c = m_fde_index.GetSize(); i < c; ++i) {
+ const FDEEntryMap::Entry &entry = m_fde_index.GetEntryRef(i);
+ if (!callback(entry.base, entry.size, entry.data))
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/DebugMacros.cpp b/contrib/llvm-project/lldb/source/Symbol/DebugMacros.cpp
new file mode 100644
index 000000000000..d119a78868a4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/DebugMacros.cpp
@@ -0,0 +1,53 @@
+//===-- DebugMacros.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/DebugMacros.h"
+
+#include "lldb/Symbol/CompileUnit.h"
+
+using namespace lldb_private;
+
+DebugMacroEntry::DebugMacroEntry(EntryType type, uint32_t line,
+ uint32_t debug_line_file_idx, const char *str)
+ : m_type(type), m_line(line), m_debug_line_file_idx(debug_line_file_idx),
+ m_str(str) {}
+
+DebugMacroEntry::DebugMacroEntry(EntryType type,
+ const DebugMacrosSP &debug_macros_sp)
+ : m_type(type), m_line(0), m_debug_line_file_idx(0),
+ m_debug_macros_sp(debug_macros_sp) {}
+
+const FileSpec &DebugMacroEntry::GetFileSpec(CompileUnit *comp_unit) const {
+ return comp_unit->GetSupportFiles().GetFileSpecAtIndex(m_debug_line_file_idx);
+}
+
+DebugMacroEntry DebugMacroEntry::CreateDefineEntry(uint32_t line,
+ const char *str) {
+ return DebugMacroEntry(DebugMacroEntry::DEFINE, line, 0, str);
+}
+
+DebugMacroEntry DebugMacroEntry::CreateUndefEntry(uint32_t line,
+ const char *str) {
+ return DebugMacroEntry(DebugMacroEntry::UNDEF, line, 0, str);
+}
+
+DebugMacroEntry
+DebugMacroEntry::CreateStartFileEntry(uint32_t line,
+ uint32_t debug_line_file_idx) {
+ return DebugMacroEntry(DebugMacroEntry::START_FILE, line, debug_line_file_idx,
+ nullptr);
+}
+
+DebugMacroEntry DebugMacroEntry::CreateEndFileEntry() {
+ return DebugMacroEntry(DebugMacroEntry::END_FILE, 0, 0, nullptr);
+}
+
+DebugMacroEntry
+DebugMacroEntry::CreateIndirectEntry(const DebugMacrosSP &debug_macros_sp) {
+ return DebugMacroEntry(DebugMacroEntry::INDIRECT, debug_macros_sp);
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/DeclVendor.cpp b/contrib/llvm-project/lldb/source/Symbol/DeclVendor.cpp
new file mode 100644
index 000000000000..0a912a2fd214
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/DeclVendor.cpp
@@ -0,0 +1,29 @@
+//===-- DeclVendor.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/DeclVendor.h"
+
+#include "lldb/Symbol/ClangASTContext.h"
+
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+std::vector<CompilerType> DeclVendor::FindTypes(ConstString name,
+ uint32_t max_matches) {
+ // FIXME: This depends on clang, but should be able to support any
+ // TypeSystem.
+ std::vector<CompilerType> ret;
+ std::vector<clang::NamedDecl *> decls;
+ if (FindDecls(name, /*append*/ true, max_matches, decls))
+ for (auto *decl : decls)
+ if (auto type = ClangASTContext::GetTypeForDecl(decl))
+ ret.push_back(type);
+ return ret;
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/Declaration.cpp b/contrib/llvm-project/lldb/source/Symbol/Declaration.cpp
new file mode 100644
index 000000000000..d78ba967d280
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/Declaration.cpp
@@ -0,0 +1,101 @@
+//===-- Declaration.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/Declaration.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+
+void Declaration::Dump(Stream *s, bool show_fullpaths) const {
+ if (m_file) {
+ *s << ", decl = ";
+ if (show_fullpaths)
+ *s << m_file;
+ else
+ *s << m_file.GetFilename();
+ if (m_line > 0)
+ s->Printf(":%u", m_line);
+#ifdef LLDB_ENABLE_DECLARATION_COLUMNS
+ if (m_column > 0)
+ s->Printf(":%u", m_column);
+#endif
+ } else {
+ if (m_line > 0) {
+ s->Printf(", line = %u", m_line);
+#ifdef LLDB_ENABLE_DECLARATION_COLUMNS
+ if (m_column > 0)
+ s->Printf(":%u", m_column);
+#endif
+ }
+#ifdef LLDB_ENABLE_DECLARATION_COLUMNS
+ else if (m_column > 0)
+ s->Printf(", column = %u", m_column);
+#endif
+ }
+}
+
+bool Declaration::DumpStopContext(Stream *s, bool show_fullpaths) const {
+ if (m_file) {
+ if (show_fullpaths)
+ *s << m_file;
+ else
+ m_file.GetFilename().Dump(s);
+
+ if (m_line > 0)
+ s->Printf(":%u", m_line);
+#ifdef LLDB_ENABLE_DECLARATION_COLUMNS
+ if (m_column > 0)
+ s->Printf(":%u", m_column);
+#endif
+ return true;
+ } else if (m_line > 0) {
+ s->Printf(" line %u", m_line);
+#ifdef LLDB_ENABLE_DECLARATION_COLUMNS
+ if (m_column > 0)
+ s->Printf(":%u", m_column);
+#endif
+ return true;
+ }
+ return false;
+}
+
+size_t Declaration::MemorySize() const { return sizeof(Declaration); }
+
+int Declaration::Compare(const Declaration &a, const Declaration &b) {
+ int result = FileSpec::Compare(a.m_file, b.m_file, true);
+ if (result)
+ return result;
+ if (a.m_line < b.m_line)
+ return -1;
+ else if (a.m_line > b.m_line)
+ return 1;
+#ifdef LLDB_ENABLE_DECLARATION_COLUMNS
+ if (a.m_column < b.m_column)
+ return -1;
+ else if (a.m_column > b.m_column)
+ return 1;
+#endif
+ return 0;
+}
+
+bool Declaration::FileAndLineEqual(const Declaration &declaration) const {
+ int file_compare = FileSpec::Compare(this->m_file, declaration.m_file, true);
+ return file_compare == 0 && this->m_line == declaration.m_line;
+}
+
+bool lldb_private::operator==(const Declaration &lhs, const Declaration &rhs) {
+#ifdef LLDB_ENABLE_DECLARATION_COLUMNS
+ if (lhs.GetColumn() == rhs.GetColumn())
+ if (lhs.GetLine() == rhs.GetLine())
+ return lhs.GetFile() == rhs.GetFile();
+#else
+ if (lhs.GetLine() == rhs.GetLine())
+ return FileSpec::Equal(lhs.GetFile(), rhs.GetFile(), true);
+#endif
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/FuncUnwinders.cpp b/contrib/llvm-project/lldb/source/Symbol/FuncUnwinders.cpp
new file mode 100644
index 000000000000..09cb9b00aaf3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/FuncUnwinders.cpp
@@ -0,0 +1,495 @@
+//===-- FuncUnwinders.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Symbol/ArmUnwindInfo.h"
+#include "lldb/Symbol/CompactUnwindInfo.h"
+#include "lldb/Symbol/DWARFCallFrameInfo.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Symbol/UnwindTable.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/RegisterNumber.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/UnwindAssembly.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+/// constructor
+
+FuncUnwinders::FuncUnwinders(UnwindTable &unwind_table, AddressRange range)
+ : m_unwind_table(unwind_table), m_range(range), m_mutex(),
+ m_unwind_plan_assembly_sp(), m_unwind_plan_eh_frame_sp(),
+ m_unwind_plan_eh_frame_augmented_sp(), m_unwind_plan_compact_unwind(),
+ m_unwind_plan_arm_unwind_sp(), m_unwind_plan_fast_sp(),
+ m_unwind_plan_arch_default_sp(),
+ m_unwind_plan_arch_default_at_func_entry_sp(),
+ m_tried_unwind_plan_assembly(false), m_tried_unwind_plan_eh_frame(false),
+ m_tried_unwind_plan_debug_frame(false),
+ m_tried_unwind_plan_eh_frame_augmented(false),
+ m_tried_unwind_plan_debug_frame_augmented(false),
+ m_tried_unwind_plan_compact_unwind(false),
+ m_tried_unwind_plan_arm_unwind(false),
+ m_tried_unwind_plan_symbol_file(false), m_tried_unwind_fast(false),
+ m_tried_unwind_arch_default(false),
+ m_tried_unwind_arch_default_at_func_entry(false),
+ m_first_non_prologue_insn() {}
+
+/// destructor
+
+FuncUnwinders::~FuncUnwinders() {}
+
+UnwindPlanSP FuncUnwinders::GetUnwindPlanAtCallSite(Target &target,
+ Thread &thread) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ if (UnwindPlanSP plan_sp = GetSymbolFileUnwindPlan(thread))
+ return plan_sp;
+ if (UnwindPlanSP plan_sp = GetDebugFrameUnwindPlan(target))
+ return plan_sp;
+ if (UnwindPlanSP plan_sp = GetEHFrameUnwindPlan(target))
+ return plan_sp;
+ if (UnwindPlanSP plan_sp = GetCompactUnwindUnwindPlan(target))
+ return plan_sp;
+ if (UnwindPlanSP plan_sp = GetArmUnwindUnwindPlan(target))
+ return plan_sp;
+
+ return nullptr;
+}
+
+UnwindPlanSP FuncUnwinders::GetCompactUnwindUnwindPlan(Target &target) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_compact_unwind.size() > 0)
+ return m_unwind_plan_compact_unwind[0]; // FIXME support multiple compact
+ // unwind plans for one func
+ if (m_tried_unwind_plan_compact_unwind)
+ return UnwindPlanSP();
+
+ m_tried_unwind_plan_compact_unwind = true;
+ if (m_range.GetBaseAddress().IsValid()) {
+ Address current_pc(m_range.GetBaseAddress());
+ CompactUnwindInfo *compact_unwind = m_unwind_table.GetCompactUnwindInfo();
+ if (compact_unwind) {
+ UnwindPlanSP unwind_plan_sp(new UnwindPlan(lldb::eRegisterKindGeneric));
+ if (compact_unwind->GetUnwindPlan(target, current_pc, *unwind_plan_sp)) {
+ m_unwind_plan_compact_unwind.push_back(unwind_plan_sp);
+ return m_unwind_plan_compact_unwind[0]; // FIXME support multiple
+ // compact unwind plans for one
+ // func
+ }
+ }
+ }
+ return UnwindPlanSP();
+}
+
+UnwindPlanSP FuncUnwinders::GetEHFrameUnwindPlan(Target &target) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_eh_frame_sp.get() || m_tried_unwind_plan_eh_frame)
+ return m_unwind_plan_eh_frame_sp;
+
+ m_tried_unwind_plan_eh_frame = true;
+ if (m_range.GetBaseAddress().IsValid()) {
+ DWARFCallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo();
+ if (eh_frame) {
+ m_unwind_plan_eh_frame_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (!eh_frame->GetUnwindPlan(m_range, *m_unwind_plan_eh_frame_sp))
+ m_unwind_plan_eh_frame_sp.reset();
+ }
+ }
+ return m_unwind_plan_eh_frame_sp;
+}
+
+UnwindPlanSP FuncUnwinders::GetDebugFrameUnwindPlan(Target &target) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_debug_frame_sp || m_tried_unwind_plan_debug_frame)
+ return m_unwind_plan_debug_frame_sp;
+
+ m_tried_unwind_plan_debug_frame = true;
+ if (m_range.GetBaseAddress().IsValid()) {
+ DWARFCallFrameInfo *debug_frame = m_unwind_table.GetDebugFrameInfo();
+ if (debug_frame) {
+ m_unwind_plan_debug_frame_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (!debug_frame->GetUnwindPlan(m_range, *m_unwind_plan_debug_frame_sp))
+ m_unwind_plan_debug_frame_sp.reset();
+ }
+ }
+ return m_unwind_plan_debug_frame_sp;
+}
+
+UnwindPlanSP FuncUnwinders::GetArmUnwindUnwindPlan(Target &target) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_arm_unwind_sp.get() || m_tried_unwind_plan_arm_unwind)
+ return m_unwind_plan_arm_unwind_sp;
+
+ m_tried_unwind_plan_arm_unwind = true;
+ if (m_range.GetBaseAddress().IsValid()) {
+ Address current_pc(m_range.GetBaseAddress());
+ ArmUnwindInfo *arm_unwind_info = m_unwind_table.GetArmUnwindInfo();
+ if (arm_unwind_info) {
+ m_unwind_plan_arm_unwind_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (!arm_unwind_info->GetUnwindPlan(target, current_pc,
+ *m_unwind_plan_arm_unwind_sp))
+ m_unwind_plan_arm_unwind_sp.reset();
+ }
+ }
+ return m_unwind_plan_arm_unwind_sp;
+}
+
+namespace {
+class RegisterContextToInfo: public SymbolFile::RegisterInfoResolver {
+public:
+ RegisterContextToInfo(RegisterContext &ctx) : m_ctx(ctx) {}
+
+ const RegisterInfo *ResolveName(llvm::StringRef name) const {
+ return m_ctx.GetRegisterInfoByName(name);
+ }
+ const RegisterInfo *ResolveNumber(lldb::RegisterKind kind,
+ uint32_t number) const {
+ return m_ctx.GetRegisterInfo(kind, number);
+ }
+
+private:
+ RegisterContext &m_ctx;
+};
+} // namespace
+
+UnwindPlanSP FuncUnwinders::GetSymbolFileUnwindPlan(Thread &thread) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_symbol_file_sp.get() || m_tried_unwind_plan_symbol_file)
+ return m_unwind_plan_symbol_file_sp;
+
+ m_tried_unwind_plan_symbol_file = true;
+ if (SymbolFile *symfile = m_unwind_table.GetSymbolFile()) {
+ m_unwind_plan_symbol_file_sp = symfile->GetUnwindPlan(
+ m_range.GetBaseAddress(),
+ RegisterContextToInfo(*thread.GetRegisterContext()));
+ }
+ return m_unwind_plan_symbol_file_sp;
+}
+
+UnwindPlanSP FuncUnwinders::GetEHFrameAugmentedUnwindPlan(Target &target,
+ Thread &thread) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_eh_frame_augmented_sp.get() ||
+ m_tried_unwind_plan_eh_frame_augmented)
+ return m_unwind_plan_eh_frame_augmented_sp;
+
+ // Only supported on x86 architectures where we get eh_frame from the
+ // compiler that describes the prologue instructions perfectly, and sometimes
+ // the epilogue instructions too.
+ if (target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_32_i386 &&
+ target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64 &&
+ target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64h) {
+ m_tried_unwind_plan_eh_frame_augmented = true;
+ return m_unwind_plan_eh_frame_augmented_sp;
+ }
+
+ m_tried_unwind_plan_eh_frame_augmented = true;
+
+ UnwindPlanSP eh_frame_plan = GetEHFrameUnwindPlan(target);
+ if (!eh_frame_plan)
+ return m_unwind_plan_eh_frame_augmented_sp;
+
+ m_unwind_plan_eh_frame_augmented_sp =
+ std::make_shared<UnwindPlan>(*eh_frame_plan);
+
+ // Augment the eh_frame instructions with epilogue descriptions if necessary
+ // so the UnwindPlan can be used at any instruction in the function.
+
+ UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
+ if (assembly_profiler_sp) {
+ if (!assembly_profiler_sp->AugmentUnwindPlanFromCallSite(
+ m_range, thread, *m_unwind_plan_eh_frame_augmented_sp)) {
+ m_unwind_plan_eh_frame_augmented_sp.reset();
+ }
+ } else {
+ m_unwind_plan_eh_frame_augmented_sp.reset();
+ }
+ return m_unwind_plan_eh_frame_augmented_sp;
+}
+
+UnwindPlanSP FuncUnwinders::GetDebugFrameAugmentedUnwindPlan(Target &target,
+ Thread &thread) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_debug_frame_augmented_sp.get() ||
+ m_tried_unwind_plan_debug_frame_augmented)
+ return m_unwind_plan_debug_frame_augmented_sp;
+
+ // Only supported on x86 architectures where we get debug_frame from the
+ // compiler that describes the prologue instructions perfectly, and sometimes
+ // the epilogue instructions too.
+ if (target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_32_i386 &&
+ target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64 &&
+ target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64h) {
+ m_tried_unwind_plan_debug_frame_augmented = true;
+ return m_unwind_plan_debug_frame_augmented_sp;
+ }
+
+ m_tried_unwind_plan_debug_frame_augmented = true;
+
+ UnwindPlanSP debug_frame_plan = GetDebugFrameUnwindPlan(target);
+ if (!debug_frame_plan)
+ return m_unwind_plan_debug_frame_augmented_sp;
+
+ m_unwind_plan_debug_frame_augmented_sp =
+ std::make_shared<UnwindPlan>(*debug_frame_plan);
+
+ // Augment the debug_frame instructions with epilogue descriptions if
+ // necessary so the UnwindPlan can be used at any instruction in the
+ // function.
+
+ UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
+ if (assembly_profiler_sp) {
+ if (!assembly_profiler_sp->AugmentUnwindPlanFromCallSite(
+ m_range, thread, *m_unwind_plan_debug_frame_augmented_sp)) {
+ m_unwind_plan_debug_frame_augmented_sp.reset();
+ }
+ } else
+ m_unwind_plan_debug_frame_augmented_sp.reset();
+ return m_unwind_plan_debug_frame_augmented_sp;
+}
+
+UnwindPlanSP FuncUnwinders::GetAssemblyUnwindPlan(Target &target,
+ Thread &thread) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_assembly_sp.get() || m_tried_unwind_plan_assembly ||
+ !m_unwind_table.GetAllowAssemblyEmulationUnwindPlans()) {
+ return m_unwind_plan_assembly_sp;
+ }
+
+ m_tried_unwind_plan_assembly = true;
+
+ UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
+ if (assembly_profiler_sp) {
+ m_unwind_plan_assembly_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (!assembly_profiler_sp->GetNonCallSiteUnwindPlanFromAssembly(
+ m_range, thread, *m_unwind_plan_assembly_sp)) {
+ m_unwind_plan_assembly_sp.reset();
+ }
+ }
+ return m_unwind_plan_assembly_sp;
+}
+
+// This method compares the pc unwind rule in the first row of two UnwindPlans.
+// If they have the same way of getting the pc value (e.g. "CFA - 8" + "CFA is
+// sp"), then it will return LazyBoolTrue.
+LazyBool FuncUnwinders::CompareUnwindPlansForIdenticalInitialPCLocation(
+ Thread &thread, const UnwindPlanSP &a, const UnwindPlanSP &b) {
+ LazyBool plans_are_identical = eLazyBoolCalculate;
+
+ RegisterNumber pc_reg(thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ uint32_t pc_reg_lldb_regnum = pc_reg.GetAsKind(eRegisterKindLLDB);
+
+ if (a.get() && b.get()) {
+ UnwindPlan::RowSP a_first_row = a->GetRowAtIndex(0);
+ UnwindPlan::RowSP b_first_row = b->GetRowAtIndex(0);
+
+ if (a_first_row.get() && b_first_row.get()) {
+ UnwindPlan::Row::RegisterLocation a_pc_regloc;
+ UnwindPlan::Row::RegisterLocation b_pc_regloc;
+
+ a_first_row->GetRegisterInfo(pc_reg_lldb_regnum, a_pc_regloc);
+ b_first_row->GetRegisterInfo(pc_reg_lldb_regnum, b_pc_regloc);
+
+ plans_are_identical = eLazyBoolYes;
+
+ if (a_first_row->GetCFAValue() != b_first_row->GetCFAValue()) {
+ plans_are_identical = eLazyBoolNo;
+ }
+ if (a_pc_regloc != b_pc_regloc) {
+ plans_are_identical = eLazyBoolNo;
+ }
+ }
+ }
+ return plans_are_identical;
+}
+
+UnwindPlanSP FuncUnwinders::GetUnwindPlanAtNonCallSite(Target &target,
+ Thread &thread) {
+ UnwindPlanSP eh_frame_sp = GetEHFrameUnwindPlan(target);
+ if (!eh_frame_sp)
+ eh_frame_sp = GetDebugFrameUnwindPlan(target);
+ UnwindPlanSP arch_default_at_entry_sp =
+ GetUnwindPlanArchitectureDefaultAtFunctionEntry(thread);
+ UnwindPlanSP arch_default_sp = GetUnwindPlanArchitectureDefault(thread);
+ UnwindPlanSP assembly_sp = GetAssemblyUnwindPlan(target, thread);
+
+ // This point of this code is to detect when a function is using a non-
+ // standard ABI, and the eh_frame correctly describes that alternate ABI.
+ // This is addressing a specific situation on x86_64 linux systems where one
+ // function in a library pushes a value on the stack and jumps to another
+ // function. So using an assembly instruction based unwind will not work
+ // when you're in the second function - the stack has been modified in a non-
+ // ABI way. But we have eh_frame that correctly describes how to unwind from
+ // this location. So we're looking to see if the initial pc register save
+ // location from the eh_frame is different from the assembly unwind, the arch
+ // default unwind, and the arch default at initial function entry.
+ //
+ // We may have eh_frame that describes the entire function -- or we may have
+ // eh_frame that only describes the unwind after the prologue has executed --
+ // so we need to check both the arch default (once the prologue has executed)
+ // and the arch default at initial function entry. And we may be running on
+ // a target where we have only some of the assembly/arch default unwind plans
+ // available.
+
+ if (CompareUnwindPlansForIdenticalInitialPCLocation(
+ thread, eh_frame_sp, arch_default_at_entry_sp) == eLazyBoolNo &&
+ CompareUnwindPlansForIdenticalInitialPCLocation(
+ thread, eh_frame_sp, arch_default_sp) == eLazyBoolNo &&
+ CompareUnwindPlansForIdenticalInitialPCLocation(
+ thread, assembly_sp, arch_default_sp) == eLazyBoolNo) {
+ return eh_frame_sp;
+ }
+
+ if (UnwindPlanSP plan_sp = GetSymbolFileUnwindPlan(thread))
+ return plan_sp;
+ if (UnwindPlanSP plan_sp = GetDebugFrameAugmentedUnwindPlan(target, thread))
+ return plan_sp;
+ if (UnwindPlanSP plan_sp = GetEHFrameAugmentedUnwindPlan(target, thread))
+ return plan_sp;
+
+ return assembly_sp;
+}
+
+UnwindPlanSP FuncUnwinders::GetUnwindPlanFastUnwind(Target &target,
+ Thread &thread) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_fast_sp.get() || m_tried_unwind_fast)
+ return m_unwind_plan_fast_sp;
+
+ m_tried_unwind_fast = true;
+
+ UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
+ if (assembly_profiler_sp) {
+ m_unwind_plan_fast_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (!assembly_profiler_sp->GetFastUnwindPlan(m_range, thread,
+ *m_unwind_plan_fast_sp)) {
+ m_unwind_plan_fast_sp.reset();
+ }
+ }
+ return m_unwind_plan_fast_sp;
+}
+
+UnwindPlanSP FuncUnwinders::GetUnwindPlanArchitectureDefault(Thread &thread) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_arch_default_sp.get() || m_tried_unwind_arch_default)
+ return m_unwind_plan_arch_default_sp;
+
+ m_tried_unwind_arch_default = true;
+
+ Address current_pc;
+ ProcessSP process_sp(thread.CalculateProcess());
+ if (process_sp) {
+ ABI *abi = process_sp->GetABI().get();
+ if (abi) {
+ m_unwind_plan_arch_default_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (!abi->CreateDefaultUnwindPlan(*m_unwind_plan_arch_default_sp)) {
+ m_unwind_plan_arch_default_sp.reset();
+ }
+ }
+ }
+
+ return m_unwind_plan_arch_default_sp;
+}
+
+UnwindPlanSP
+FuncUnwinders::GetUnwindPlanArchitectureDefaultAtFunctionEntry(Thread &thread) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_arch_default_at_func_entry_sp.get() ||
+ m_tried_unwind_arch_default_at_func_entry)
+ return m_unwind_plan_arch_default_at_func_entry_sp;
+
+ m_tried_unwind_arch_default_at_func_entry = true;
+
+ Address current_pc;
+ ProcessSP process_sp(thread.CalculateProcess());
+ if (process_sp) {
+ ABI *abi = process_sp->GetABI().get();
+ if (abi) {
+ m_unwind_plan_arch_default_at_func_entry_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (!abi->CreateFunctionEntryUnwindPlan(
+ *m_unwind_plan_arch_default_at_func_entry_sp)) {
+ m_unwind_plan_arch_default_at_func_entry_sp.reset();
+ }
+ }
+ }
+
+ return m_unwind_plan_arch_default_at_func_entry_sp;
+}
+
+Address &FuncUnwinders::GetFirstNonPrologueInsn(Target &target) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_first_non_prologue_insn.IsValid())
+ return m_first_non_prologue_insn;
+
+ ExecutionContext exe_ctx(target.shared_from_this(), false);
+ UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
+ if (assembly_profiler_sp)
+ assembly_profiler_sp->FirstNonPrologueInsn(m_range, exe_ctx,
+ m_first_non_prologue_insn);
+ return m_first_non_prologue_insn;
+}
+
+const Address &FuncUnwinders::GetFunctionStartAddress() const {
+ return m_range.GetBaseAddress();
+}
+
+lldb::UnwindAssemblySP
+FuncUnwinders::GetUnwindAssemblyProfiler(Target &target) {
+ UnwindAssemblySP assembly_profiler_sp;
+ if (ArchSpec arch = m_unwind_table.GetArchitecture()) {
+ arch.MergeFrom(target.GetArchitecture());
+ assembly_profiler_sp = UnwindAssembly::FindPlugin(arch);
+ }
+ return assembly_profiler_sp;
+}
+
+Address FuncUnwinders::GetLSDAAddress(Target &target) {
+ Address lsda_addr;
+
+ UnwindPlanSP unwind_plan_sp = GetEHFrameUnwindPlan(target);
+ if (unwind_plan_sp.get() == nullptr) {
+ unwind_plan_sp = GetCompactUnwindUnwindPlan(target);
+ }
+ if (unwind_plan_sp.get() && unwind_plan_sp->GetLSDAAddress().IsValid()) {
+ lsda_addr = unwind_plan_sp->GetLSDAAddress();
+ }
+ return lsda_addr;
+}
+
+Address FuncUnwinders::GetPersonalityRoutinePtrAddress(Target &target) {
+ Address personality_addr;
+
+ UnwindPlanSP unwind_plan_sp = GetEHFrameUnwindPlan(target);
+ if (unwind_plan_sp.get() == nullptr) {
+ unwind_plan_sp = GetCompactUnwindUnwindPlan(target);
+ }
+ if (unwind_plan_sp.get() &&
+ unwind_plan_sp->GetPersonalityFunctionPtr().IsValid()) {
+ personality_addr = unwind_plan_sp->GetPersonalityFunctionPtr();
+ }
+
+ return personality_addr;
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/Function.cpp b/contrib/llvm-project/lldb/source/Symbol/Function.cpp
new file mode 100644
index 000000000000..951392c1f1bf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/Function.cpp
@@ -0,0 +1,614 @@
+//===-- Function.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/Function.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/Log.h"
+#include "llvm/Support/Casting.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Basic function information is contained in the FunctionInfo class. It is
+// designed to contain the name, linkage name, and declaration location.
+FunctionInfo::FunctionInfo(const char *name, const Declaration *decl_ptr)
+ : m_name(name), m_declaration(decl_ptr) {}
+
+FunctionInfo::FunctionInfo(ConstString name, const Declaration *decl_ptr)
+ : m_name(name), m_declaration(decl_ptr) {}
+
+FunctionInfo::~FunctionInfo() {}
+
+void FunctionInfo::Dump(Stream *s, bool show_fullpaths) const {
+ if (m_name)
+ *s << ", name = \"" << m_name << "\"";
+ m_declaration.Dump(s, show_fullpaths);
+}
+
+int FunctionInfo::Compare(const FunctionInfo &a, const FunctionInfo &b) {
+ int result = ConstString::Compare(a.GetName(), b.GetName());
+ if (result)
+ return result;
+
+ return Declaration::Compare(a.m_declaration, b.m_declaration);
+}
+
+Declaration &FunctionInfo::GetDeclaration() { return m_declaration; }
+
+const Declaration &FunctionInfo::GetDeclaration() const {
+ return m_declaration;
+}
+
+ConstString FunctionInfo::GetName() const { return m_name; }
+
+size_t FunctionInfo::MemorySize() const {
+ return m_name.MemorySize() + m_declaration.MemorySize();
+}
+
+InlineFunctionInfo::InlineFunctionInfo(const char *name, const char *mangled,
+ const Declaration *decl_ptr,
+ const Declaration *call_decl_ptr)
+ : FunctionInfo(name, decl_ptr), m_mangled(ConstString(mangled), true),
+ m_call_decl(call_decl_ptr) {}
+
+InlineFunctionInfo::InlineFunctionInfo(ConstString name,
+ const Mangled &mangled,
+ const Declaration *decl_ptr,
+ const Declaration *call_decl_ptr)
+ : FunctionInfo(name, decl_ptr), m_mangled(mangled),
+ m_call_decl(call_decl_ptr) {}
+
+InlineFunctionInfo::~InlineFunctionInfo() {}
+
+int InlineFunctionInfo::Compare(const InlineFunctionInfo &a,
+ const InlineFunctionInfo &b) {
+
+ int result = FunctionInfo::Compare(a, b);
+ if (result)
+ return result;
+ // only compare the mangled names if both have them
+ return Mangled::Compare(a.m_mangled, a.m_mangled);
+}
+
+void InlineFunctionInfo::Dump(Stream *s, bool show_fullpaths) const {
+ FunctionInfo::Dump(s, show_fullpaths);
+ if (m_mangled)
+ m_mangled.Dump(s);
+}
+
+void InlineFunctionInfo::DumpStopContext(Stream *s,
+ LanguageType language) const {
+ // s->Indent("[inlined] ");
+ s->Indent();
+ if (m_mangled)
+ s->PutCString(m_mangled.GetName(language).AsCString());
+ else
+ s->PutCString(m_name.AsCString());
+}
+
+ConstString InlineFunctionInfo::GetName(LanguageType language) const {
+ if (m_mangled)
+ return m_mangled.GetName(language);
+ return m_name;
+}
+
+ConstString InlineFunctionInfo::GetDisplayName(LanguageType language) const {
+ if (m_mangled)
+ return m_mangled.GetDisplayDemangledName(language);
+ return m_name;
+}
+
+Declaration &InlineFunctionInfo::GetCallSite() { return m_call_decl; }
+
+const Declaration &InlineFunctionInfo::GetCallSite() const {
+ return m_call_decl;
+}
+
+Mangled &InlineFunctionInfo::GetMangled() { return m_mangled; }
+
+const Mangled &InlineFunctionInfo::GetMangled() const { return m_mangled; }
+
+size_t InlineFunctionInfo::MemorySize() const {
+ return FunctionInfo::MemorySize() + m_mangled.MemorySize();
+}
+
+//
+CallEdge::CallEdge(const char *symbol_name, lldb::addr_t return_pc)
+ : return_pc(return_pc), resolved(false) {
+ lazy_callee.symbol_name = symbol_name;
+}
+
+void CallEdge::ParseSymbolFileAndResolve(ModuleList &images) {
+ if (resolved)
+ return;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ LLDB_LOG(log, "CallEdge: Lazily parsing the call graph for {0}",
+ lazy_callee.symbol_name);
+
+ auto resolve_lazy_callee = [&]() -> Function * {
+ ConstString callee_name{lazy_callee.symbol_name};
+ SymbolContextList sc_list;
+ size_t num_matches =
+ images.FindFunctionSymbols(callee_name, eFunctionNameTypeAuto, sc_list);
+ if (num_matches == 0 || !sc_list[0].symbol) {
+ LLDB_LOG(log, "CallEdge: Found no symbols for {0}, cannot resolve it",
+ callee_name);
+ return nullptr;
+ }
+ Address callee_addr = sc_list[0].symbol->GetAddress();
+ if (!callee_addr.IsValid()) {
+ LLDB_LOG(log, "CallEdge: Invalid symbol address");
+ return nullptr;
+ }
+ Function *f = callee_addr.CalculateSymbolContextFunction();
+ if (!f) {
+ LLDB_LOG(log, "CallEdge: Could not find complete function");
+ return nullptr;
+ }
+ return f;
+ };
+ lazy_callee.def = resolve_lazy_callee();
+ resolved = true;
+}
+
+Function *CallEdge::GetCallee(ModuleList &images) {
+ ParseSymbolFileAndResolve(images);
+ return lazy_callee.def;
+}
+
+lldb::addr_t CallEdge::GetReturnPCAddress(Function &caller,
+ Target &target) const {
+ const Address &base = caller.GetAddressRange().GetBaseAddress();
+ return base.GetLoadAddress(&target) + return_pc;
+}
+
+//
+Function::Function(CompileUnit *comp_unit, lldb::user_id_t func_uid,
+ lldb::user_id_t type_uid, const Mangled &mangled, Type *type,
+ const AddressRange &range)
+ : UserID(func_uid), m_comp_unit(comp_unit), m_type_uid(type_uid),
+ m_type(type), m_mangled(mangled), m_block(func_uid), m_range(range),
+ m_frame_base(), m_flags(), m_prologue_byte_size(0) {
+ m_block.SetParentScope(this);
+ assert(comp_unit != nullptr);
+}
+
+Function::~Function() {}
+
+void Function::GetStartLineSourceInfo(FileSpec &source_file,
+ uint32_t &line_no) {
+ line_no = 0;
+ source_file.Clear();
+
+ if (m_comp_unit == nullptr)
+ return;
+
+ // Initialize m_type if it hasn't been initialized already
+ GetType();
+
+ if (m_type != nullptr && m_type->GetDeclaration().GetLine() != 0) {
+ source_file = m_type->GetDeclaration().GetFile();
+ line_no = m_type->GetDeclaration().GetLine();
+ } else {
+ LineTable *line_table = m_comp_unit->GetLineTable();
+ if (line_table == nullptr)
+ return;
+
+ LineEntry line_entry;
+ if (line_table->FindLineEntryByAddress(GetAddressRange().GetBaseAddress(),
+ line_entry, nullptr)) {
+ line_no = line_entry.line;
+ source_file = line_entry.file;
+ }
+ }
+}
+
+void Function::GetEndLineSourceInfo(FileSpec &source_file, uint32_t &line_no) {
+ line_no = 0;
+ source_file.Clear();
+
+ // The -1 is kind of cheesy, but I want to get the last line entry for the
+ // given function, not the first entry of the next.
+ Address scratch_addr(GetAddressRange().GetBaseAddress());
+ scratch_addr.SetOffset(scratch_addr.GetOffset() +
+ GetAddressRange().GetByteSize() - 1);
+
+ LineTable *line_table = m_comp_unit->GetLineTable();
+ if (line_table == nullptr)
+ return;
+
+ LineEntry line_entry;
+ if (line_table->FindLineEntryByAddress(scratch_addr, line_entry, nullptr)) {
+ line_no = line_entry.line;
+ source_file = line_entry.file;
+ }
+}
+
+llvm::MutableArrayRef<CallEdge> Function::GetCallEdges() {
+ if (m_call_edges_resolved)
+ return m_call_edges;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ LLDB_LOG(log, "GetCallEdges: Attempting to parse call site info for {0}",
+ GetDisplayName());
+
+ m_call_edges_resolved = true;
+
+ // Find the SymbolFile which provided this function's definition.
+ Block &block = GetBlock(/*can_create*/true);
+ SymbolFile *sym_file = block.GetSymbolFile();
+ if (!sym_file)
+ return llvm::None;
+
+ // Lazily read call site information from the SymbolFile.
+ m_call_edges = sym_file->ParseCallEdgesInFunction(GetID());
+
+ // Sort the call edges to speed up return_pc lookups.
+ llvm::sort(m_call_edges.begin(), m_call_edges.end(),
+ [](const CallEdge &LHS, const CallEdge &RHS) {
+ return LHS.GetUnresolvedReturnPCAddress() <
+ RHS.GetUnresolvedReturnPCAddress();
+ });
+
+ return m_call_edges;
+}
+
+llvm::MutableArrayRef<CallEdge> Function::GetTailCallingEdges() {
+ // Call edges are sorted by return PC, and tail calling edges have invalid
+ // return PCs. Find them at the end of the list.
+ return GetCallEdges().drop_until([](const CallEdge &edge) {
+ return edge.GetUnresolvedReturnPCAddress() == LLDB_INVALID_ADDRESS;
+ });
+}
+
+Block &Function::GetBlock(bool can_create) {
+ if (!m_block.BlockInfoHasBeenParsed() && can_create) {
+ ModuleSP module_sp = CalculateSymbolContextModule();
+ if (module_sp) {
+ module_sp->GetSymbolVendor()->ParseBlocksRecursive(*this);
+ } else {
+ Host::SystemLog(Host::eSystemLogError,
+ "error: unable to find module "
+ "shared pointer for function '%s' "
+ "in %s\n",
+ GetName().GetCString(), m_comp_unit->GetPath().c_str());
+ }
+ m_block.SetBlockInfoHasBeenParsed(true, true);
+ }
+ return m_block;
+}
+
+CompileUnit *Function::GetCompileUnit() { return m_comp_unit; }
+
+const CompileUnit *Function::GetCompileUnit() const { return m_comp_unit; }
+
+void Function::GetDescription(Stream *s, lldb::DescriptionLevel level,
+ Target *target) {
+ ConstString name = GetName();
+ ConstString mangled = m_mangled.GetMangledName();
+
+ *s << "id = " << (const UserID &)*this;
+ if (name)
+ *s << ", name = \"" << name.GetCString() << '"';
+ if (mangled)
+ *s << ", mangled = \"" << mangled.GetCString() << '"';
+ *s << ", range = ";
+ Address::DumpStyle fallback_style;
+ if (level == eDescriptionLevelVerbose)
+ fallback_style = Address::DumpStyleModuleWithFileAddress;
+ else
+ fallback_style = Address::DumpStyleFileAddress;
+ GetAddressRange().Dump(s, target, Address::DumpStyleLoadAddress,
+ fallback_style);
+}
+
+void Function::Dump(Stream *s, bool show_context) const {
+ s->Printf("%p: ", static_cast<const void *>(this));
+ s->Indent();
+ *s << "Function" << static_cast<const UserID &>(*this);
+
+ m_mangled.Dump(s);
+
+ if (m_type)
+ s->Printf(", type = %p", static_cast<void *>(m_type));
+ else if (m_type_uid != LLDB_INVALID_UID)
+ s->Printf(", type_uid = 0x%8.8" PRIx64, m_type_uid);
+
+ s->EOL();
+ // Dump the root object
+ if (m_block.BlockInfoHasBeenParsed())
+ m_block.Dump(s, m_range.GetBaseAddress().GetFileAddress(), INT_MAX,
+ show_context);
+}
+
+void Function::CalculateSymbolContext(SymbolContext *sc) {
+ sc->function = this;
+ m_comp_unit->CalculateSymbolContext(sc);
+}
+
+ModuleSP Function::CalculateSymbolContextModule() {
+ SectionSP section_sp(m_range.GetBaseAddress().GetSection());
+ if (section_sp)
+ return section_sp->GetModule();
+
+ return this->GetCompileUnit()->GetModule();
+}
+
+CompileUnit *Function::CalculateSymbolContextCompileUnit() {
+ return this->GetCompileUnit();
+}
+
+Function *Function::CalculateSymbolContextFunction() { return this; }
+
+lldb::DisassemblerSP Function::GetInstructions(const ExecutionContext &exe_ctx,
+ const char *flavor,
+ bool prefer_file_cache) {
+ ModuleSP module_sp(GetAddressRange().GetBaseAddress().GetModule());
+ if (module_sp) {
+ const bool prefer_file_cache = false;
+ return Disassembler::DisassembleRange(module_sp->GetArchitecture(), nullptr,
+ flavor, exe_ctx, GetAddressRange(),
+ prefer_file_cache);
+ }
+ return lldb::DisassemblerSP();
+}
+
+bool Function::GetDisassembly(const ExecutionContext &exe_ctx,
+ const char *flavor, bool prefer_file_cache,
+ Stream &strm) {
+ lldb::DisassemblerSP disassembler_sp =
+ GetInstructions(exe_ctx, flavor, prefer_file_cache);
+ if (disassembler_sp) {
+ const bool show_address = true;
+ const bool show_bytes = false;
+ disassembler_sp->GetInstructionList().Dump(&strm, show_address, show_bytes,
+ &exe_ctx);
+ return true;
+ }
+ return false;
+}
+
+// Symbol *
+// Function::CalculateSymbolContextSymbol ()
+//{
+// return // TODO: find the symbol for the function???
+//}
+
+void Function::DumpSymbolContext(Stream *s) {
+ m_comp_unit->DumpSymbolContext(s);
+ s->Printf(", Function{0x%8.8" PRIx64 "}", GetID());
+}
+
+size_t Function::MemorySize() const {
+ size_t mem_size = sizeof(Function) + m_block.MemorySize();
+ return mem_size;
+}
+
+bool Function::GetIsOptimized() {
+ bool result = false;
+
+ // Currently optimization is only indicted by the vendor extension
+ // DW_AT_APPLE_optimized which is set on a compile unit level.
+ if (m_comp_unit) {
+ result = m_comp_unit->GetIsOptimized();
+ }
+ return result;
+}
+
+bool Function::IsTopLevelFunction() {
+ bool result = false;
+
+ if (Language *language = Language::FindPlugin(GetLanguage()))
+ result = language->IsTopLevelFunction(*this);
+
+ return result;
+}
+
+ConstString Function::GetDisplayName() const {
+ return m_mangled.GetDisplayDemangledName(GetLanguage());
+}
+
+CompilerDeclContext Function::GetDeclContext() {
+ ModuleSP module_sp = CalculateSymbolContextModule();
+
+ if (module_sp) {
+ SymbolVendor *sym_vendor = module_sp->GetSymbolVendor();
+
+ if (sym_vendor) {
+ SymbolFile *sym_file = sym_vendor->GetSymbolFile();
+
+ if (sym_file)
+ return sym_file->GetDeclContextForUID(GetID());
+ }
+ }
+ return CompilerDeclContext();
+}
+
+Type *Function::GetType() {
+ if (m_type == nullptr) {
+ SymbolContext sc;
+
+ CalculateSymbolContext(&sc);
+
+ if (!sc.module_sp)
+ return nullptr;
+
+ SymbolVendor *sym_vendor = sc.module_sp->GetSymbolVendor();
+
+ if (sym_vendor == nullptr)
+ return nullptr;
+
+ SymbolFile *sym_file = sym_vendor->GetSymbolFile();
+
+ if (sym_file == nullptr)
+ return nullptr;
+
+ m_type = sym_file->ResolveTypeUID(m_type_uid);
+ }
+ return m_type;
+}
+
+const Type *Function::GetType() const { return m_type; }
+
+CompilerType Function::GetCompilerType() {
+ Type *function_type = GetType();
+ if (function_type)
+ return function_type->GetFullCompilerType();
+ return CompilerType();
+}
+
+uint32_t Function::GetPrologueByteSize() {
+ if (m_prologue_byte_size == 0 &&
+ m_flags.IsClear(flagsCalculatedPrologueSize)) {
+ m_flags.Set(flagsCalculatedPrologueSize);
+ LineTable *line_table = m_comp_unit->GetLineTable();
+ uint32_t prologue_end_line_idx = 0;
+
+ if (line_table) {
+ LineEntry first_line_entry;
+ uint32_t first_line_entry_idx = UINT32_MAX;
+ if (line_table->FindLineEntryByAddress(GetAddressRange().GetBaseAddress(),
+ first_line_entry,
+ &first_line_entry_idx)) {
+ // Make sure the first line entry isn't already the end of the prologue
+ addr_t prologue_end_file_addr = LLDB_INVALID_ADDRESS;
+ addr_t line_zero_end_file_addr = LLDB_INVALID_ADDRESS;
+
+ if (first_line_entry.is_prologue_end) {
+ prologue_end_file_addr =
+ first_line_entry.range.GetBaseAddress().GetFileAddress();
+ prologue_end_line_idx = first_line_entry_idx;
+ } else {
+ // Check the first few instructions and look for one that has
+ // is_prologue_end set to true.
+ const uint32_t last_line_entry_idx = first_line_entry_idx + 6;
+ for (uint32_t idx = first_line_entry_idx + 1;
+ idx < last_line_entry_idx; ++idx) {
+ LineEntry line_entry;
+ if (line_table->GetLineEntryAtIndex(idx, line_entry)) {
+ if (line_entry.is_prologue_end) {
+ prologue_end_file_addr =
+ line_entry.range.GetBaseAddress().GetFileAddress();
+ prologue_end_line_idx = idx;
+ break;
+ }
+ }
+ }
+ }
+
+ // If we didn't find the end of the prologue in the line tables, then
+ // just use the end address of the first line table entry
+ if (prologue_end_file_addr == LLDB_INVALID_ADDRESS) {
+ // Check the first few instructions and look for one that has a line
+ // number that's different than the first entry.
+ uint32_t last_line_entry_idx = first_line_entry_idx + 6;
+ for (uint32_t idx = first_line_entry_idx + 1;
+ idx < last_line_entry_idx; ++idx) {
+ LineEntry line_entry;
+ if (line_table->GetLineEntryAtIndex(idx, line_entry)) {
+ if (line_entry.line != first_line_entry.line) {
+ prologue_end_file_addr =
+ line_entry.range.GetBaseAddress().GetFileAddress();
+ prologue_end_line_idx = idx;
+ break;
+ }
+ }
+ }
+
+ if (prologue_end_file_addr == LLDB_INVALID_ADDRESS) {
+ prologue_end_file_addr =
+ first_line_entry.range.GetBaseAddress().GetFileAddress() +
+ first_line_entry.range.GetByteSize();
+ prologue_end_line_idx = first_line_entry_idx;
+ }
+ }
+
+ const addr_t func_start_file_addr =
+ m_range.GetBaseAddress().GetFileAddress();
+ const addr_t func_end_file_addr =
+ func_start_file_addr + m_range.GetByteSize();
+
+ // Now calculate the offset to pass the subsequent line 0 entries.
+ uint32_t first_non_zero_line = prologue_end_line_idx;
+ while (true) {
+ LineEntry line_entry;
+ if (line_table->GetLineEntryAtIndex(first_non_zero_line,
+ line_entry)) {
+ if (line_entry.line != 0)
+ break;
+ }
+ if (line_entry.range.GetBaseAddress().GetFileAddress() >=
+ func_end_file_addr)
+ break;
+
+ first_non_zero_line++;
+ }
+
+ if (first_non_zero_line > prologue_end_line_idx) {
+ LineEntry first_non_zero_entry;
+ if (line_table->GetLineEntryAtIndex(first_non_zero_line,
+ first_non_zero_entry)) {
+ line_zero_end_file_addr =
+ first_non_zero_entry.range.GetBaseAddress().GetFileAddress();
+ }
+ }
+
+ // Verify that this prologue end file address in the function's address
+ // range just to be sure
+ if (func_start_file_addr < prologue_end_file_addr &&
+ prologue_end_file_addr < func_end_file_addr) {
+ m_prologue_byte_size = prologue_end_file_addr - func_start_file_addr;
+ }
+
+ if (prologue_end_file_addr < line_zero_end_file_addr &&
+ line_zero_end_file_addr < func_end_file_addr) {
+ m_prologue_byte_size +=
+ line_zero_end_file_addr - prologue_end_file_addr;
+ }
+ }
+ }
+ }
+
+ return m_prologue_byte_size;
+}
+
+lldb::LanguageType Function::GetLanguage() const {
+ lldb::LanguageType lang = m_mangled.GuessLanguage();
+ if (lang != lldb::eLanguageTypeUnknown)
+ return lang;
+
+ if (m_comp_unit)
+ return m_comp_unit->GetLanguage();
+
+ return lldb::eLanguageTypeUnknown;
+}
+
+ConstString Function::GetName() const {
+ LanguageType language = lldb::eLanguageTypeUnknown;
+ if (m_comp_unit)
+ language = m_comp_unit->GetLanguage();
+ return m_mangled.GetName(language);
+}
+
+ConstString Function::GetNameNoArguments() const {
+ LanguageType language = lldb::eLanguageTypeUnknown;
+ if (m_comp_unit)
+ language = m_comp_unit->GetLanguage();
+ return m_mangled.GetName(language, Mangled::ePreferDemangledWithoutArguments);
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/LineEntry.cpp b/contrib/llvm-project/lldb/source/Symbol/LineEntry.cpp
new file mode 100644
index 000000000000..959a3274ec92
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/LineEntry.cpp
@@ -0,0 +1,261 @@
+//===-- LineEntry.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb_private;
+
+LineEntry::LineEntry()
+ : range(), file(), line(LLDB_INVALID_LINE_NUMBER), column(0),
+ is_start_of_statement(0), is_start_of_basic_block(0), is_prologue_end(0),
+ is_epilogue_begin(0), is_terminal_entry(0) {}
+
+LineEntry::LineEntry(const lldb::SectionSP &section_sp,
+ lldb::addr_t section_offset, lldb::addr_t byte_size,
+ const FileSpec &_file, uint32_t _line, uint16_t _column,
+ bool _is_start_of_statement, bool _is_start_of_basic_block,
+ bool _is_prologue_end, bool _is_epilogue_begin,
+ bool _is_terminal_entry)
+ : range(section_sp, section_offset, byte_size), file(_file),
+ original_file(_file), line(_line), column(_column),
+ is_start_of_statement(_is_start_of_statement),
+ is_start_of_basic_block(_is_start_of_basic_block),
+ is_prologue_end(_is_prologue_end), is_epilogue_begin(_is_epilogue_begin),
+ is_terminal_entry(_is_terminal_entry) {}
+
+void LineEntry::Clear() {
+ range.Clear();
+ file.Clear();
+ original_file.Clear();
+ line = LLDB_INVALID_LINE_NUMBER;
+ column = 0;
+ is_start_of_statement = 0;
+ is_start_of_basic_block = 0;
+ is_prologue_end = 0;
+ is_epilogue_begin = 0;
+ is_terminal_entry = 0;
+}
+
+bool LineEntry::IsValid() const {
+ return range.GetBaseAddress().IsValid() && line != LLDB_INVALID_LINE_NUMBER;
+}
+
+bool LineEntry::DumpStopContext(Stream *s, bool show_fullpaths) const {
+ if (file) {
+ if (show_fullpaths)
+ file.Dump(s);
+ else
+ file.GetFilename().Dump(s);
+
+ if (line)
+ s->PutChar(':');
+ }
+ if (line) {
+ s->Printf("%u", line);
+ if (column) {
+ s->PutChar(':');
+ s->Printf("%u", column);
+ }
+ }
+ return file || line;
+}
+
+bool LineEntry::Dump(Stream *s, Target *target, bool show_file,
+ Address::DumpStyle style,
+ Address::DumpStyle fallback_style, bool show_range) const {
+ if (show_range) {
+ // Show address range
+ if (!range.Dump(s, target, style, fallback_style))
+ return false;
+ } else {
+ // Show address only
+ if (!range.GetBaseAddress().Dump(s, target, style, fallback_style))
+ return false;
+ }
+ if (show_file)
+ *s << ", file = " << file;
+ if (line)
+ s->Printf(", line = %u", line);
+ if (column)
+ s->Printf(", column = %u", column);
+ if (is_start_of_statement)
+ *s << ", is_start_of_statement = TRUE";
+
+ if (is_start_of_basic_block)
+ *s << ", is_start_of_basic_block = TRUE";
+
+ if (is_prologue_end)
+ *s << ", is_prologue_end = TRUE";
+
+ if (is_epilogue_begin)
+ *s << ", is_epilogue_begin = TRUE";
+
+ if (is_terminal_entry)
+ *s << ", is_terminal_entry = TRUE";
+ return true;
+}
+
+bool LineEntry::GetDescription(Stream *s, lldb::DescriptionLevel level,
+ CompileUnit *cu, Target *target,
+ bool show_address_only) const {
+
+ if (level == lldb::eDescriptionLevelBrief ||
+ level == lldb::eDescriptionLevelFull) {
+ if (show_address_only) {
+ range.GetBaseAddress().Dump(s, target, Address::DumpStyleLoadAddress,
+ Address::DumpStyleFileAddress);
+ } else {
+ range.Dump(s, target, Address::DumpStyleLoadAddress,
+ Address::DumpStyleFileAddress);
+ }
+
+ *s << ": " << file;
+
+ if (line) {
+ s->Printf(":%u", line);
+ if (column)
+ s->Printf(":%u", column);
+ }
+
+ if (level == lldb::eDescriptionLevelFull) {
+ if (is_start_of_statement)
+ *s << ", is_start_of_statement = TRUE";
+
+ if (is_start_of_basic_block)
+ *s << ", is_start_of_basic_block = TRUE";
+
+ if (is_prologue_end)
+ *s << ", is_prologue_end = TRUE";
+
+ if (is_epilogue_begin)
+ *s << ", is_epilogue_begin = TRUE";
+
+ if (is_terminal_entry)
+ *s << ", is_terminal_entry = TRUE";
+ } else {
+ if (is_terminal_entry)
+ s->EOL();
+ }
+ } else {
+ return Dump(s, target, true, Address::DumpStyleLoadAddress,
+ Address::DumpStyleModuleWithFileAddress, true);
+ }
+ return true;
+}
+
+bool lldb_private::operator<(const LineEntry &a, const LineEntry &b) {
+ return LineEntry::Compare(a, b) < 0;
+}
+
+int LineEntry::Compare(const LineEntry &a, const LineEntry &b) {
+ int result = Address::CompareFileAddress(a.range.GetBaseAddress(),
+ b.range.GetBaseAddress());
+ if (result != 0)
+ return result;
+
+ const lldb::addr_t a_byte_size = a.range.GetByteSize();
+ const lldb::addr_t b_byte_size = b.range.GetByteSize();
+
+ if (a_byte_size < b_byte_size)
+ return -1;
+ if (a_byte_size > b_byte_size)
+ return +1;
+
+ // Check for an end sequence entry mismatch after we have determined that the
+ // address values are equal. If one of the items is an end sequence, we don't
+ // care about the line, file, or column info.
+ if (a.is_terminal_entry > b.is_terminal_entry)
+ return -1;
+ if (a.is_terminal_entry < b.is_terminal_entry)
+ return +1;
+
+ if (a.line < b.line)
+ return -1;
+ if (a.line > b.line)
+ return +1;
+
+ if (a.column < b.column)
+ return -1;
+ if (a.column > b.column)
+ return +1;
+
+ return FileSpec::Compare(a.file, b.file, true);
+}
+
+AddressRange LineEntry::GetSameLineContiguousAddressRange(
+ bool include_inlined_functions) const {
+ // Add each LineEntry's range to complete_line_range until we find a
+ // different file / line number.
+ AddressRange complete_line_range = range;
+ auto symbol_context_scope = lldb::eSymbolContextLineEntry;
+ Declaration start_call_site(original_file, line);
+ if (include_inlined_functions)
+ symbol_context_scope |= lldb::eSymbolContextBlock;
+
+ while (true) {
+ SymbolContext next_line_sc;
+ Address range_end(complete_line_range.GetBaseAddress());
+ range_end.Slide(complete_line_range.GetByteSize());
+ range_end.CalculateSymbolContext(&next_line_sc, symbol_context_scope);
+
+ if (!next_line_sc.line_entry.IsValid() ||
+ next_line_sc.line_entry.range.GetByteSize() == 0)
+ break;
+
+ if (original_file == next_line_sc.line_entry.original_file &&
+ (next_line_sc.line_entry.line == 0 ||
+ line == next_line_sc.line_entry.line)) {
+ // Include any line 0 entries - they indicate that this is compiler-
+ // generated code that does not correspond to user source code.
+ // next_line_sc is the same file & line as this LineEntry, so extend
+ // our AddressRange by its size and continue to see if there are more
+ // LineEntries that we can combine. However, if there was nothing to
+ // extend we're done.
+ if (!complete_line_range.Extend(next_line_sc.line_entry.range))
+ break;
+ continue;
+ }
+
+ if (include_inlined_functions && next_line_sc.block &&
+ next_line_sc.block->GetContainingInlinedBlock() != nullptr) {
+ // The next_line_sc might be in a different file if it's an inlined
+ // function. If this is the case then we still want to expand our line
+ // range to include them if the inlined function is at the same call site
+ // as this line entry. The current block could represent a nested inline
+ // function call so we need to need to check up the block tree to see if
+ // we find one.
+ auto inlined_parent_block =
+ next_line_sc.block->GetContainingInlinedBlockWithCallSite(
+ start_call_site);
+ if (!inlined_parent_block)
+ // We didn't find any parent inlined block with a call site at this line
+ // entry so this inlined function is probably at another line.
+ break;
+ // Extend our AddressRange by the size of the inlined block, but if there
+ // was nothing to add then we're done.
+ if (!complete_line_range.Extend(next_line_sc.line_entry.range))
+ break;
+ continue;
+ }
+
+ break;
+ }
+ return complete_line_range;
+}
+
+void LineEntry::ApplyFileMappings(lldb::TargetSP target_sp) {
+ if (target_sp) {
+ // Apply any file remappings to our file
+ FileSpec new_file_spec;
+ if (target_sp->GetSourcePathMap().FindFile(original_file, new_file_spec))
+ file = new_file_spec;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/LineTable.cpp b/contrib/llvm-project/lldb/source/Symbol/LineTable.cpp
new file mode 100644
index 000000000000..8d4d72c9a2a2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/LineTable.cpp
@@ -0,0 +1,533 @@
+//===-- LineTable.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Utility/Stream.h"
+#include <algorithm>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// LineTable constructor
+LineTable::LineTable(CompileUnit *comp_unit)
+ : m_comp_unit(comp_unit), m_entries() {}
+
+// Destructor
+LineTable::~LineTable() {}
+
+void LineTable::InsertLineEntry(lldb::addr_t file_addr, uint32_t line,
+ uint16_t column, uint16_t file_idx,
+ bool is_start_of_statement,
+ bool is_start_of_basic_block,
+ bool is_prologue_end, bool is_epilogue_begin,
+ bool is_terminal_entry) {
+ Entry entry(file_addr, line, column, file_idx, is_start_of_statement,
+ is_start_of_basic_block, is_prologue_end, is_epilogue_begin,
+ is_terminal_entry);
+
+ entry_collection::iterator begin_pos = m_entries.begin();
+ entry_collection::iterator end_pos = m_entries.end();
+ LineTable::Entry::LessThanBinaryPredicate less_than_bp(this);
+ entry_collection::iterator pos =
+ upper_bound(begin_pos, end_pos, entry, less_than_bp);
+
+ // Stream s(stdout);
+ // s << "\n\nBefore:\n";
+ // Dump (&s, Address::DumpStyleFileAddress);
+ m_entries.insert(pos, entry);
+ // s << "After:\n";
+ // Dump (&s, Address::DumpStyleFileAddress);
+}
+
+LineSequence::LineSequence() {}
+
+void LineTable::LineSequenceImpl::Clear() { m_entries.clear(); }
+
+LineSequence *LineTable::CreateLineSequenceContainer() {
+ return new LineTable::LineSequenceImpl();
+}
+
+void LineTable::AppendLineEntryToSequence(
+ LineSequence *sequence, lldb::addr_t file_addr, uint32_t line,
+ uint16_t column, uint16_t file_idx, bool is_start_of_statement,
+ bool is_start_of_basic_block, bool is_prologue_end, bool is_epilogue_begin,
+ bool is_terminal_entry) {
+ assert(sequence != nullptr);
+ LineSequenceImpl *seq = reinterpret_cast<LineSequenceImpl *>(sequence);
+ Entry entry(file_addr, line, column, file_idx, is_start_of_statement,
+ is_start_of_basic_block, is_prologue_end, is_epilogue_begin,
+ is_terminal_entry);
+ entry_collection &entries = seq->m_entries;
+ // Replace the last entry if the address is the same, otherwise append it. If
+ // we have multiple line entries at the same address, this indicates illegal
+ // DWARF so this "fixes" the line table to be correct. If not fixed this can
+ // cause a line entry's address that when resolved back to a symbol context,
+ // could resolve to a different line entry. We really want a
+ // 1 to 1 mapping
+ // here to avoid these kinds of inconsistencies. We will need tor revisit
+ // this if the DWARF line tables are updated to allow multiple entries at the
+ // same address legally.
+ if (!entries.empty() && entries.back().file_addr == file_addr) {
+ // GCC don't use the is_prologue_end flag to mark the first instruction
+ // after the prologue.
+ // Instead of it it is issuing a line table entry for the first instruction
+ // of the prologue and one for the first instruction after the prologue. If
+ // the size of the prologue is 0 instruction then the 2 line entry will
+ // have the same file address. Removing it will remove our ability to
+ // properly detect the location of the end of prologe so we set the
+ // prologue_end flag to preserve this information (setting the prologue_end
+ // flag for an entry what is after the prologue end don't have any effect)
+ entry.is_prologue_end = entry.file_idx == entries.back().file_idx;
+ entries.back() = entry;
+ } else
+ entries.push_back(entry);
+}
+
+void LineTable::InsertSequence(LineSequence *sequence) {
+ assert(sequence != nullptr);
+ LineSequenceImpl *seq = reinterpret_cast<LineSequenceImpl *>(sequence);
+ if (seq->m_entries.empty())
+ return;
+ Entry &entry = seq->m_entries.front();
+
+ // If the first entry address in this sequence is greater than or equal to
+ // the address of the last item in our entry collection, just append.
+ if (m_entries.empty() ||
+ !Entry::EntryAddressLessThan(entry, m_entries.back())) {
+ m_entries.insert(m_entries.end(), seq->m_entries.begin(),
+ seq->m_entries.end());
+ return;
+ }
+
+ // Otherwise, find where this belongs in the collection
+ entry_collection::iterator begin_pos = m_entries.begin();
+ entry_collection::iterator end_pos = m_entries.end();
+ LineTable::Entry::LessThanBinaryPredicate less_than_bp(this);
+ entry_collection::iterator pos =
+ upper_bound(begin_pos, end_pos, entry, less_than_bp);
+
+ // We should never insert a sequence in the middle of another sequence
+ if (pos != begin_pos) {
+ while (pos < end_pos && !((pos - 1)->is_terminal_entry))
+ pos++;
+ }
+
+#ifndef NDEBUG
+ // If we aren't inserting at the beginning, the previous entry should
+ // terminate a sequence.
+ if (pos != begin_pos) {
+ entry_collection::iterator prev_pos = pos - 1;
+ assert(prev_pos->is_terminal_entry);
+ }
+#endif
+ m_entries.insert(pos, seq->m_entries.begin(), seq->m_entries.end());
+}
+
+LineTable::Entry::LessThanBinaryPredicate::LessThanBinaryPredicate(
+ LineTable *line_table)
+ : m_line_table(line_table) {}
+
+bool LineTable::Entry::LessThanBinaryPredicate::
+operator()(const LineTable::Entry &a, const LineTable::Entry &b) const {
+#define LT_COMPARE(a, b) \
+ if (a != b) \
+ return a < b
+ LT_COMPARE(a.file_addr, b.file_addr);
+ // b and a reversed on purpose below.
+ LT_COMPARE(b.is_terminal_entry, a.is_terminal_entry);
+ LT_COMPARE(a.line, b.line);
+ LT_COMPARE(a.column, b.column);
+ LT_COMPARE(a.is_start_of_statement, b.is_start_of_statement);
+ LT_COMPARE(a.is_start_of_basic_block, b.is_start_of_basic_block);
+ // b and a reversed on purpose below.
+ LT_COMPARE(b.is_prologue_end, a.is_prologue_end);
+ LT_COMPARE(a.is_epilogue_begin, b.is_epilogue_begin);
+ LT_COMPARE(a.file_idx, b.file_idx);
+ return false;
+#undef LT_COMPARE
+}
+
+uint32_t LineTable::GetSize() const { return m_entries.size(); }
+
+bool LineTable::GetLineEntryAtIndex(uint32_t idx, LineEntry &line_entry) {
+ if (idx < m_entries.size()) {
+ ConvertEntryAtIndexToLineEntry(idx, line_entry);
+ return true;
+ }
+ line_entry.Clear();
+ return false;
+}
+
+bool LineTable::FindLineEntryByAddress(const Address &so_addr,
+ LineEntry &line_entry,
+ uint32_t *index_ptr) {
+ if (index_ptr != nullptr)
+ *index_ptr = UINT32_MAX;
+
+ bool success = false;
+
+ if (so_addr.GetModule().get() == m_comp_unit->GetModule().get()) {
+ Entry search_entry;
+ search_entry.file_addr = so_addr.GetFileAddress();
+ if (search_entry.file_addr != LLDB_INVALID_ADDRESS) {
+ entry_collection::const_iterator begin_pos = m_entries.begin();
+ entry_collection::const_iterator end_pos = m_entries.end();
+ entry_collection::const_iterator pos = lower_bound(
+ begin_pos, end_pos, search_entry, Entry::EntryAddressLessThan);
+ if (pos != end_pos) {
+ if (pos != begin_pos) {
+ if (pos->file_addr != search_entry.file_addr)
+ --pos;
+ else if (pos->file_addr == search_entry.file_addr) {
+ // If this is a termination entry, it shouldn't match since entries
+ // with the "is_terminal_entry" member set to true are termination
+ // entries that define the range for the previous entry.
+ if (pos->is_terminal_entry) {
+ // The matching entry is a terminal entry, so we skip ahead to
+ // the next entry to see if there is another entry following this
+ // one whose section/offset matches.
+ ++pos;
+ if (pos != end_pos) {
+ if (pos->file_addr != search_entry.file_addr)
+ pos = end_pos;
+ }
+ }
+
+ if (pos != end_pos) {
+ // While in the same section/offset backup to find the first line
+ // entry that matches the address in case there are multiple
+ while (pos != begin_pos) {
+ entry_collection::const_iterator prev_pos = pos - 1;
+ if (prev_pos->file_addr == search_entry.file_addr &&
+ prev_pos->is_terminal_entry == false)
+ --pos;
+ else
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ // There might be code in the containing objfile before the first
+ // line table entry. Make sure that does not get considered part of
+ // the first line table entry.
+ if (pos->file_addr > so_addr.GetFileAddress())
+ return false;
+ }
+
+ // Make sure we have a valid match and that the match isn't a
+ // terminating entry for a previous line...
+ if (pos != end_pos && pos->is_terminal_entry == false) {
+ uint32_t match_idx = std::distance(begin_pos, pos);
+ success = ConvertEntryAtIndexToLineEntry(match_idx, line_entry);
+ if (index_ptr != nullptr && success)
+ *index_ptr = match_idx;
+ }
+ }
+ }
+ }
+ return success;
+}
+
+bool LineTable::ConvertEntryAtIndexToLineEntry(uint32_t idx,
+ LineEntry &line_entry) {
+ if (idx < m_entries.size()) {
+ const Entry &entry = m_entries[idx];
+ ModuleSP module_sp(m_comp_unit->GetModule());
+ if (module_sp &&
+ module_sp->ResolveFileAddress(entry.file_addr,
+ line_entry.range.GetBaseAddress())) {
+ if (!entry.is_terminal_entry && idx + 1 < m_entries.size())
+ line_entry.range.SetByteSize(m_entries[idx + 1].file_addr -
+ entry.file_addr);
+ else
+ line_entry.range.SetByteSize(0);
+
+ line_entry.file =
+ m_comp_unit->GetSupportFiles().GetFileSpecAtIndex(entry.file_idx);
+ line_entry.original_file =
+ m_comp_unit->GetSupportFiles().GetFileSpecAtIndex(entry.file_idx);
+ line_entry.line = entry.line;
+ line_entry.column = entry.column;
+ line_entry.is_start_of_statement = entry.is_start_of_statement;
+ line_entry.is_start_of_basic_block = entry.is_start_of_basic_block;
+ line_entry.is_prologue_end = entry.is_prologue_end;
+ line_entry.is_epilogue_begin = entry.is_epilogue_begin;
+ line_entry.is_terminal_entry = entry.is_terminal_entry;
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32_t LineTable::FindLineEntryIndexByFileIndex(
+ uint32_t start_idx, const std::vector<uint32_t> &file_indexes,
+ uint32_t line, bool exact, LineEntry *line_entry_ptr) {
+
+ const size_t count = m_entries.size();
+ std::vector<uint32_t>::const_iterator begin_pos = file_indexes.begin();
+ std::vector<uint32_t>::const_iterator end_pos = file_indexes.end();
+ size_t best_match = UINT32_MAX;
+
+ for (size_t idx = start_idx; idx < count; ++idx) {
+ // Skip line table rows that terminate the previous row (is_terminal_entry
+ // is non-zero)
+ if (m_entries[idx].is_terminal_entry)
+ continue;
+
+ if (find(begin_pos, end_pos, m_entries[idx].file_idx) == end_pos)
+ continue;
+
+ // Exact match always wins. Otherwise try to find the closest line > the
+ // desired line.
+ // FIXME: Maybe want to find the line closest before and the line closest
+ // after and
+ // if they're not in the same function, don't return a match.
+
+ if (m_entries[idx].line < line) {
+ continue;
+ } else if (m_entries[idx].line == line) {
+ if (line_entry_ptr)
+ ConvertEntryAtIndexToLineEntry(idx, *line_entry_ptr);
+ return idx;
+ } else if (!exact) {
+ if (best_match == UINT32_MAX)
+ best_match = idx;
+ else if (m_entries[idx].line < m_entries[best_match].line)
+ best_match = idx;
+ }
+ }
+
+ if (best_match != UINT32_MAX) {
+ if (line_entry_ptr)
+ ConvertEntryAtIndexToLineEntry(best_match, *line_entry_ptr);
+ return best_match;
+ }
+ return UINT32_MAX;
+}
+
+uint32_t LineTable::FindLineEntryIndexByFileIndex(uint32_t start_idx,
+ uint32_t file_idx,
+ uint32_t line, bool exact,
+ LineEntry *line_entry_ptr) {
+ const size_t count = m_entries.size();
+ size_t best_match = UINT32_MAX;
+
+ for (size_t idx = start_idx; idx < count; ++idx) {
+ // Skip line table rows that terminate the previous row (is_terminal_entry
+ // is non-zero)
+ if (m_entries[idx].is_terminal_entry)
+ continue;
+
+ if (m_entries[idx].file_idx != file_idx)
+ continue;
+
+ // Exact match always wins. Otherwise try to find the closest line > the
+ // desired line.
+ // FIXME: Maybe want to find the line closest before and the line closest
+ // after and
+ // if they're not in the same function, don't return a match.
+
+ if (m_entries[idx].line < line) {
+ continue;
+ } else if (m_entries[idx].line == line) {
+ if (line_entry_ptr)
+ ConvertEntryAtIndexToLineEntry(idx, *line_entry_ptr);
+ return idx;
+ } else if (!exact) {
+ if (best_match == UINT32_MAX)
+ best_match = idx;
+ else if (m_entries[idx].line < m_entries[best_match].line)
+ best_match = idx;
+ }
+ }
+
+ if (best_match != UINT32_MAX) {
+ if (line_entry_ptr)
+ ConvertEntryAtIndexToLineEntry(best_match, *line_entry_ptr);
+ return best_match;
+ }
+ return UINT32_MAX;
+}
+
+size_t LineTable::FineLineEntriesForFileIndex(uint32_t file_idx, bool append,
+ SymbolContextList &sc_list) {
+
+ if (!append)
+ sc_list.Clear();
+
+ size_t num_added = 0;
+ const size_t count = m_entries.size();
+ if (count > 0) {
+ SymbolContext sc(m_comp_unit);
+
+ for (size_t idx = 0; idx < count; ++idx) {
+ // Skip line table rows that terminate the previous row
+ // (is_terminal_entry is non-zero)
+ if (m_entries[idx].is_terminal_entry)
+ continue;
+
+ if (m_entries[idx].file_idx == file_idx) {
+ if (ConvertEntryAtIndexToLineEntry(idx, sc.line_entry)) {
+ ++num_added;
+ sc_list.Append(sc);
+ }
+ }
+ }
+ }
+ return num_added;
+}
+
+void LineTable::Dump(Stream *s, Target *target, Address::DumpStyle style,
+ Address::DumpStyle fallback_style, bool show_line_ranges) {
+ const size_t count = m_entries.size();
+ LineEntry line_entry;
+ FileSpec prev_file;
+ for (size_t idx = 0; idx < count; ++idx) {
+ ConvertEntryAtIndexToLineEntry(idx, line_entry);
+ line_entry.Dump(s, target, prev_file != line_entry.original_file, style,
+ fallback_style, show_line_ranges);
+ s->EOL();
+ prev_file = line_entry.original_file;
+ }
+}
+
+void LineTable::GetDescription(Stream *s, Target *target,
+ DescriptionLevel level) {
+ const size_t count = m_entries.size();
+ LineEntry line_entry;
+ for (size_t idx = 0; idx < count; ++idx) {
+ ConvertEntryAtIndexToLineEntry(idx, line_entry);
+ line_entry.GetDescription(s, level, m_comp_unit, target, true);
+ s->EOL();
+ }
+}
+
+size_t LineTable::GetContiguousFileAddressRanges(FileAddressRanges &file_ranges,
+ bool append) {
+ if (!append)
+ file_ranges.Clear();
+ const size_t initial_count = file_ranges.GetSize();
+
+ const size_t count = m_entries.size();
+ LineEntry line_entry;
+ FileAddressRanges::Entry range(LLDB_INVALID_ADDRESS, 0);
+ for (size_t idx = 0; idx < count; ++idx) {
+ const Entry &entry = m_entries[idx];
+
+ if (entry.is_terminal_entry) {
+ if (range.GetRangeBase() != LLDB_INVALID_ADDRESS) {
+ range.SetRangeEnd(entry.file_addr);
+ file_ranges.Append(range);
+ range.Clear(LLDB_INVALID_ADDRESS);
+ }
+ } else if (range.GetRangeBase() == LLDB_INVALID_ADDRESS) {
+ range.SetRangeBase(entry.file_addr);
+ }
+ }
+ return file_ranges.GetSize() - initial_count;
+}
+
+LineTable *LineTable::LinkLineTable(const FileRangeMap &file_range_map) {
+ std::unique_ptr<LineTable> line_table_up(new LineTable(m_comp_unit));
+ LineSequenceImpl sequence;
+ const size_t count = m_entries.size();
+ LineEntry line_entry;
+ const FileRangeMap::Entry *file_range_entry = nullptr;
+ const FileRangeMap::Entry *prev_file_range_entry = nullptr;
+ lldb::addr_t prev_file_addr = LLDB_INVALID_ADDRESS;
+ bool prev_entry_was_linked = false;
+ bool range_changed = false;
+ for (size_t idx = 0; idx < count; ++idx) {
+ const Entry &entry = m_entries[idx];
+
+ const bool end_sequence = entry.is_terminal_entry;
+ const lldb::addr_t lookup_file_addr =
+ entry.file_addr - (end_sequence ? 1 : 0);
+ if (file_range_entry == nullptr ||
+ !file_range_entry->Contains(lookup_file_addr)) {
+ prev_file_range_entry = file_range_entry;
+ file_range_entry = file_range_map.FindEntryThatContains(lookup_file_addr);
+ range_changed = true;
+ }
+
+ lldb::addr_t prev_end_entry_linked_file_addr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t entry_linked_file_addr = LLDB_INVALID_ADDRESS;
+
+ bool terminate_previous_entry = false;
+ if (file_range_entry) {
+ entry_linked_file_addr = entry.file_addr -
+ file_range_entry->GetRangeBase() +
+ file_range_entry->data;
+ // Determine if we need to terminate the previous entry when the previous
+ // entry was not contiguous with this one after being linked.
+ if (range_changed && prev_file_range_entry) {
+ prev_end_entry_linked_file_addr =
+ std::min<lldb::addr_t>(entry.file_addr,
+ prev_file_range_entry->GetRangeEnd()) -
+ prev_file_range_entry->GetRangeBase() + prev_file_range_entry->data;
+ if (prev_end_entry_linked_file_addr != entry_linked_file_addr)
+ terminate_previous_entry = prev_entry_was_linked;
+ }
+ } else if (prev_entry_was_linked) {
+ // This entry doesn't have a remapping and it needs to be removed. Watch
+ // out in case we need to terminate a previous entry needs to be
+ // terminated now that one line entry in a sequence is not longer valid.
+ if (!sequence.m_entries.empty() &&
+ !sequence.m_entries.back().is_terminal_entry) {
+ terminate_previous_entry = true;
+ }
+ }
+
+ if (terminate_previous_entry && !sequence.m_entries.empty()) {
+ assert(prev_file_addr != LLDB_INVALID_ADDRESS);
+ UNUSED_IF_ASSERT_DISABLED(prev_file_addr);
+ sequence.m_entries.push_back(sequence.m_entries.back());
+ if (prev_end_entry_linked_file_addr == LLDB_INVALID_ADDRESS)
+ prev_end_entry_linked_file_addr =
+ std::min<lldb::addr_t>(entry.file_addr,
+ prev_file_range_entry->GetRangeEnd()) -
+ prev_file_range_entry->GetRangeBase() + prev_file_range_entry->data;
+ sequence.m_entries.back().file_addr = prev_end_entry_linked_file_addr;
+ sequence.m_entries.back().is_terminal_entry = true;
+
+ // Append the sequence since we just terminated the previous one
+ line_table_up->InsertSequence(&sequence);
+ sequence.Clear();
+ }
+
+ // Now link the current entry
+ if (file_range_entry) {
+ // This entry has an address remapping and it needs to have its address
+ // relinked
+ sequence.m_entries.push_back(entry);
+ sequence.m_entries.back().file_addr = entry_linked_file_addr;
+ }
+
+ // If we have items in the sequence and the last entry is a terminal entry,
+ // insert this sequence into our new line table.
+ if (!sequence.m_entries.empty() &&
+ sequence.m_entries.back().is_terminal_entry) {
+ line_table_up->InsertSequence(&sequence);
+ sequence.Clear();
+ prev_entry_was_linked = false;
+ } else {
+ prev_entry_was_linked = file_range_entry != nullptr;
+ }
+ prev_file_addr = entry.file_addr;
+ range_changed = false;
+ }
+ if (line_table_up->m_entries.empty())
+ return nullptr;
+ return line_table_up.release();
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/LocateSymbolFile.cpp b/contrib/llvm-project/lldb/source/Symbol/LocateSymbolFile.cpp
new file mode 100644
index 000000000000..bfdb6e705f4a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/LocateSymbolFile.cpp
@@ -0,0 +1,390 @@
+//===-- LocateSymbolFile.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/LocateSymbolFile.h"
+
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+#include "lldb/Utility/UUID.h"
+
+#include "llvm/Support/FileSystem.h"
+
+// From MacOSX system header "mach/machine.h"
+typedef int cpu_type_t;
+typedef int cpu_subtype_t;
+
+using namespace lldb;
+using namespace lldb_private;
+
+#if defined(__APPLE__)
+
+// Forward declaration of method defined in source/Host/macosx/Symbols.cpp
+int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
+ ModuleSpec &return_module_spec);
+
+#else
+
+int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
+ ModuleSpec &return_module_spec) {
+ // Cannot find MacOSX files using debug symbols on non MacOSX.
+ return 0;
+}
+
+#endif
+
+static bool FileAtPathContainsArchAndUUID(const FileSpec &file_fspec,
+ const ArchSpec *arch,
+ const lldb_private::UUID *uuid) {
+ ModuleSpecList module_specs;
+ if (ObjectFile::GetModuleSpecifications(file_fspec, 0, 0, module_specs)) {
+ ModuleSpec spec;
+ for (size_t i = 0; i < module_specs.GetSize(); ++i) {
+ bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
+ UNUSED_IF_ASSERT_DISABLED(got_spec);
+ assert(got_spec);
+ if ((uuid == nullptr || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
+ (arch == nullptr ||
+ (spec.GetArchitecturePtr() &&
+ spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Given a binary exec_fspec, and a ModuleSpec with an architecture/uuid,
+// return true if there is a matching dSYM bundle next to the exec_fspec,
+// and return that value in dsym_fspec.
+// If there is a .dSYM.yaa compressed archive next to the exec_fspec,
+// call through Symbols::DownloadObjectAndSymbolFile to download the
+// expanded/uncompressed dSYM and return that filepath in dsym_fspec.
+
+static bool LookForDsymNextToExecutablePath(const ModuleSpec &mod_spec,
+ const FileSpec &exec_fspec,
+ FileSpec &dsym_fspec) {
+ ConstString filename = exec_fspec.GetFilename();
+ FileSpec dsym_directory = exec_fspec;
+ dsym_directory.RemoveLastPathComponent();
+
+ std::string dsym_filename = filename.AsCString();
+ dsym_filename += ".dSYM";
+ dsym_directory.AppendPathComponent(dsym_filename);
+ dsym_directory.AppendPathComponent("Contents");
+ dsym_directory.AppendPathComponent("Resources");
+ dsym_directory.AppendPathComponent("DWARF");
+
+ if (FileSystem::Instance().Exists(dsym_directory)) {
+
+ // See if the binary name exists in the dSYM DWARF
+ // subdir.
+ dsym_fspec = dsym_directory;
+ dsym_fspec.AppendPathComponent(filename.AsCString());
+ if (FileSystem::Instance().Exists(dsym_fspec) &&
+ FileAtPathContainsArchAndUUID(dsym_fspec, mod_spec.GetArchitecturePtr(),
+ mod_spec.GetUUIDPtr())) {
+ return true;
+ }
+
+ // See if we have "../CF.framework" - so we'll look for
+ // CF.framework.dSYM/Contents/Resources/DWARF/CF
+ // We need to drop the last suffix after '.' to match
+ // 'CF' in the DWARF subdir.
+ std::string binary_name(filename.AsCString());
+ auto last_dot = binary_name.find_last_of('.');
+ if (last_dot != std::string::npos) {
+ binary_name.erase(last_dot);
+ dsym_fspec = dsym_directory;
+ dsym_fspec.AppendPathComponent(binary_name);
+ if (FileSystem::Instance().Exists(dsym_fspec) &&
+ FileAtPathContainsArchAndUUID(dsym_fspec,
+ mod_spec.GetArchitecturePtr(),
+ mod_spec.GetUUIDPtr())) {
+ return true;
+ }
+ }
+ }
+
+ // See if we have a .dSYM.yaa next to this executable path.
+ FileSpec dsym_yaa_fspec = exec_fspec;
+ dsym_yaa_fspec.RemoveLastPathComponent();
+ std::string dsym_yaa_filename = filename.AsCString();
+ dsym_yaa_filename += ".dSYM.yaa";
+ dsym_yaa_fspec.AppendPathComponent(dsym_yaa_filename);
+
+ if (FileSystem::Instance().Exists(dsym_yaa_fspec)) {
+ ModuleSpec mutable_mod_spec = mod_spec;
+ if (Symbols::DownloadObjectAndSymbolFile(mutable_mod_spec, true) &&
+ FileSystem::Instance().Exists(mutable_mod_spec.GetSymbolFileSpec())) {
+ dsym_fspec = mutable_mod_spec.GetSymbolFileSpec();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Given a ModuleSpec with a FileSpec and optionally uuid/architecture
+// filled in, look for a .dSYM bundle next to that binary. Returns true
+// if a .dSYM bundle is found, and that path is returned in the dsym_fspec
+// FileSpec.
+//
+// This routine looks a few directory layers above the given exec_path -
+// exec_path might be /System/Library/Frameworks/CF.framework/CF and the
+// dSYM might be /System/Library/Frameworks/CF.framework.dSYM.
+//
+// If there is a .dSYM.yaa compressed archive found next to the binary,
+// we'll call DownloadObjectAndSymbolFile to expand it into a plain .dSYM
+
+static bool LocateDSYMInVincinityOfExecutable(const ModuleSpec &module_spec,
+ FileSpec &dsym_fspec) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ const FileSpec &exec_fspec = module_spec.GetFileSpec();
+ if (exec_fspec) {
+ if (::LookForDsymNextToExecutablePath(module_spec, exec_fspec,
+ dsym_fspec)) {
+ if (log) {
+ log->Printf("dSYM with matching UUID & arch found at %s",
+ dsym_fspec.GetPath().c_str());
+ }
+ return true;
+ } else {
+ FileSpec parent_dirs = exec_fspec;
+
+ // Remove the binary name from the FileSpec
+ parent_dirs.RemoveLastPathComponent();
+
+ // Add a ".dSYM" name to each directory component of the path,
+ // stripping off components. e.g. we may have a binary like
+ // /S/L/F/Foundation.framework/Versions/A/Foundation and
+ // /S/L/F/Foundation.framework.dSYM
+ //
+ // so we'll need to start with
+ // /S/L/F/Foundation.framework/Versions/A, add the .dSYM part to the
+ // "A", and if that doesn't exist, strip off the "A" and try it again
+ // with "Versions", etc., until we find a dSYM bundle or we've
+ // stripped off enough path components that there's no need to
+ // continue.
+
+ for (int i = 0; i < 4; i++) {
+ // Does this part of the path have a "." character - could it be a
+ // bundle's top level directory?
+ const char *fn = parent_dirs.GetFilename().AsCString();
+ if (fn == nullptr)
+ break;
+ if (::strchr(fn, '.') != nullptr) {
+ if (::LookForDsymNextToExecutablePath(module_spec, parent_dirs,
+ dsym_fspec)) {
+ if (log) {
+ log->Printf("dSYM with matching UUID & arch found at %s",
+ dsym_fspec.GetPath().c_str());
+ }
+ return true;
+ }
+ }
+ parent_dirs.RemoveLastPathComponent();
+ }
+ }
+ }
+ dsym_fspec.Clear();
+ return false;
+}
+
+static FileSpec LocateExecutableSymbolFileDsym(const ModuleSpec &module_spec) {
+ const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
+ const ArchSpec *arch = module_spec.GetArchitecturePtr();
+ const UUID *uuid = module_spec.GetUUIDPtr();
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat,
+ "LocateExecutableSymbolFileDsym (file = %s, arch = %s, uuid = %p)",
+ exec_fspec ? exec_fspec->GetFilename().AsCString("<NULL>") : "<NULL>",
+ arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid);
+
+ FileSpec symbol_fspec;
+ ModuleSpec dsym_module_spec;
+ // First try and find the dSYM in the same directory as the executable or in
+ // an appropriate parent directory
+ if (!LocateDSYMInVincinityOfExecutable(module_spec, symbol_fspec)) {
+ // We failed to easily find the dSYM above, so use DebugSymbols
+ LocateMacOSXFilesUsingDebugSymbols(module_spec, dsym_module_spec);
+ } else {
+ dsym_module_spec.GetSymbolFileSpec() = symbol_fspec;
+ }
+ return dsym_module_spec.GetSymbolFileSpec();
+}
+
+ModuleSpec Symbols::LocateExecutableObjectFile(const ModuleSpec &module_spec) {
+ ModuleSpec result;
+ const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
+ const ArchSpec *arch = module_spec.GetArchitecturePtr();
+ const UUID *uuid = module_spec.GetUUIDPtr();
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat, "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)",
+ exec_fspec ? exec_fspec->GetFilename().AsCString("<NULL>") : "<NULL>",
+ arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid);
+
+ ModuleSpecList module_specs;
+ ModuleSpec matched_module_spec;
+ if (exec_fspec &&
+ ObjectFile::GetModuleSpecifications(*exec_fspec, 0, 0, module_specs) &&
+ module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) {
+ result.GetFileSpec() = exec_fspec;
+ } else {
+ LocateMacOSXFilesUsingDebugSymbols(module_spec, result);
+ }
+ return result;
+}
+
+// Keep "symbols.enable-external-lookup" description in sync with this function.
+
+FileSpec
+Symbols::LocateExecutableSymbolFile(const ModuleSpec &module_spec,
+ const FileSpecList &default_search_paths) {
+ FileSpec symbol_file_spec = module_spec.GetSymbolFileSpec();
+ if (symbol_file_spec.IsAbsolute() &&
+ FileSystem::Instance().Exists(symbol_file_spec))
+ return symbol_file_spec;
+
+ const char *symbol_filename = symbol_file_spec.GetFilename().AsCString();
+ if (symbol_filename && symbol_filename[0]) {
+ FileSpecList debug_file_search_paths = default_search_paths;
+
+ // Add module directory.
+ FileSpec module_file_spec = module_spec.GetFileSpec();
+ // We keep the unresolved pathname if it fails.
+ FileSystem::Instance().ResolveSymbolicLink(module_file_spec,
+ module_file_spec);
+
+ ConstString file_dir = module_file_spec.GetDirectory();
+ {
+ FileSpec file_spec(file_dir.AsCString("."));
+ FileSystem::Instance().Resolve(file_spec);
+ debug_file_search_paths.AppendIfUnique(file_spec);
+ }
+
+ if (ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
+
+ // Add current working directory.
+ {
+ FileSpec file_spec(".");
+ FileSystem::Instance().Resolve(file_spec);
+ debug_file_search_paths.AppendIfUnique(file_spec);
+ }
+
+#ifndef _WIN32
+#if defined(__NetBSD__)
+ // Add /usr/libdata/debug directory.
+ {
+ FileSpec file_spec("/usr/libdata/debug");
+ FileSystem::Instance().Resolve(file_spec);
+ debug_file_search_paths.AppendIfUnique(file_spec);
+ }
+#else
+ // Add /usr/lib/debug directory.
+ {
+ FileSpec file_spec("/usr/lib/debug");
+ FileSystem::Instance().Resolve(file_spec);
+ debug_file_search_paths.AppendIfUnique(file_spec);
+ }
+#endif
+#endif // _WIN32
+ }
+
+ std::string uuid_str;
+ const UUID &module_uuid = module_spec.GetUUID();
+ if (module_uuid.IsValid()) {
+ // Some debug files are stored in the .build-id directory like this:
+ // /usr/lib/debug/.build-id/ff/e7fe727889ad82bb153de2ad065b2189693315.debug
+ uuid_str = module_uuid.GetAsString("");
+ std::transform(uuid_str.begin(), uuid_str.end(), uuid_str.begin(),
+ ::tolower);
+ uuid_str.insert(2, 1, '/');
+ uuid_str = uuid_str + ".debug";
+ }
+
+ size_t num_directories = debug_file_search_paths.GetSize();
+ for (size_t idx = 0; idx < num_directories; ++idx) {
+ FileSpec dirspec = debug_file_search_paths.GetFileSpecAtIndex(idx);
+ FileSystem::Instance().Resolve(dirspec);
+ if (!FileSystem::Instance().IsDirectory(dirspec))
+ continue;
+
+ std::vector<std::string> files;
+ std::string dirname = dirspec.GetPath();
+
+ files.push_back(dirname + "/" + symbol_filename);
+ files.push_back(dirname + "/.debug/" + symbol_filename);
+ files.push_back(dirname + "/.build-id/" + uuid_str);
+
+ // Some debug files may stored in the module directory like this:
+ // /usr/lib/debug/usr/lib/library.so.debug
+ if (!file_dir.IsEmpty())
+ files.push_back(dirname + file_dir.AsCString() + "/" + symbol_filename);
+
+ const uint32_t num_files = files.size();
+ for (size_t idx_file = 0; idx_file < num_files; ++idx_file) {
+ const std::string &filename = files[idx_file];
+ FileSpec file_spec(filename);
+ FileSystem::Instance().Resolve(file_spec);
+
+ if (llvm::sys::fs::equivalent(file_spec.GetPath(),
+ module_file_spec.GetPath()))
+ continue;
+
+ if (FileSystem::Instance().Exists(file_spec)) {
+ lldb_private::ModuleSpecList specs;
+ const size_t num_specs =
+ ObjectFile::GetModuleSpecifications(file_spec, 0, 0, specs);
+ assert(num_specs <= 1 &&
+ "Symbol Vendor supports only a single architecture");
+ if (num_specs == 1) {
+ ModuleSpec mspec;
+ if (specs.GetModuleSpecAtIndex(0, mspec)) {
+ // Skip the uuids check if module_uuid is invalid. For example,
+ // this happens for *.dwp files since at the moment llvm-dwp
+ // doesn't output build ids, nor does binutils dwp.
+ if (!module_uuid.IsValid() || module_uuid == mspec.GetUUID())
+ return file_spec;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return LocateExecutableSymbolFileDsym(module_spec);
+}
+
+#if !defined(__APPLE__)
+
+FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &symfile_bundle,
+ const lldb_private::UUID *uuid,
+ const ArchSpec *arch) {
+ // FIXME
+ return FileSpec();
+}
+
+bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
+ bool force_lookup) {
+ // Fill in the module_spec.GetFileSpec() for the object file and/or the
+ // module_spec.GetSymbolFileSpec() for the debug symbols file.
+ return false;
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Symbol/LocateSymbolFileMacOSX.cpp b/contrib/llvm-project/lldb/source/Symbol/LocateSymbolFileMacOSX.cpp
new file mode 100644
index 000000000000..4e16382d53e7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/LocateSymbolFileMacOSX.cpp
@@ -0,0 +1,657 @@
+//===-- LocateSymbolFileMacOSX.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/LocateSymbolFile.h"
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <pwd.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "Host/macosx/cfcpp/CFCBundle.h"
+#include "Host/macosx/cfcpp/CFCData.h"
+#include "Host/macosx/cfcpp/CFCReleaser.h"
+#include "Host/macosx/cfcpp/CFCString.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/CleanUp.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+#include "lldb/Utility/UUID.h"
+#include "mach/machine.h"
+
+#include "llvm/Support/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)(CFUUIDRef uuid, CFURLRef exec_url) = nullptr;
+static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) = nullptr;
+
+int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
+ ModuleSpec &return_module_spec) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
+ if (log)
+ log->Printf("Spotlight lookup for .dSYM bundles is disabled.");
+ return 0;
+ }
+
+ return_module_spec = module_spec;
+ return_module_spec.GetFileSpec().Clear();
+ return_module_spec.GetSymbolFileSpec().Clear();
+
+ int items_found = 0;
+
+ if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
+ g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
+ void *handle = dlopen ("/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols", RTLD_LAZY | RTLD_LOCAL);
+ if (handle) {
+ g_dlsym_DBGCopyFullDSYMURLForUUID = (CFURLRef (*)(CFUUIDRef, CFURLRef)) dlsym (handle, "DBGCopyFullDSYMURLForUUID");
+ g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef (*)(CFURLRef)) dlsym (handle, "DBGCopyDSYMPropertyLists");
+ }
+ }
+
+ if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
+ g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
+ return items_found;
+ }
+
+ const UUID *uuid = module_spec.GetUUIDPtr();
+ const ArchSpec *arch = module_spec.GetArchitecturePtr();
+
+ if (uuid && uuid->IsValid()) {
+ // Try and locate the dSYM file using DebugSymbols first
+ llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes();
+ if (module_uuid.size() == 16) {
+ CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes(
+ NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3],
+ module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7],
+ module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11],
+ module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15]));
+
+ if (module_uuid_ref.get()) {
+ CFCReleaser<CFURLRef> exec_url;
+ const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
+ if (exec_fspec) {
+ char exec_cf_path[PATH_MAX];
+ if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
+ exec_url.reset(::CFURLCreateFromFileSystemRepresentation(
+ NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path),
+ FALSE));
+ }
+
+ CFCReleaser<CFURLRef> dsym_url(
+ g_dlsym_DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get()));
+ char path[PATH_MAX];
+
+ if (dsym_url.get()) {
+ if (::CFURLGetFileSystemRepresentation(
+ dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
+ if (log) {
+ log->Printf("DebugSymbols framework returned dSYM path of %s for "
+ "UUID %s -- looking for the dSYM",
+ path, uuid->GetAsString().c_str());
+ }
+ FileSpec dsym_filespec(path);
+ if (path[0] == '~')
+ FileSystem::Instance().Resolve(dsym_filespec);
+
+ if (FileSystem::Instance().IsDirectory(dsym_filespec)) {
+ dsym_filespec =
+ Symbols::FindSymbolFileInBundle(dsym_filespec, uuid, arch);
+ ++items_found;
+ } else {
+ ++items_found;
+ }
+ return_module_spec.GetSymbolFileSpec() = dsym_filespec;
+ }
+
+ bool success = false;
+ if (log) {
+ if (::CFURLGetFileSystemRepresentation(
+ dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
+ log->Printf("DebugSymbols framework returned dSYM path of %s for "
+ "UUID %s -- looking for an exec file",
+ path, uuid->GetAsString().c_str());
+ }
+ }
+
+ CFCReleaser<CFDictionaryRef> dict(
+ g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get()));
+ CFDictionaryRef uuid_dict = NULL;
+ if (dict.get()) {
+ CFCString uuid_cfstr(uuid->GetAsString().c_str());
+ uuid_dict = static_cast<CFDictionaryRef>(
+ ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get()));
+ }
+ if (uuid_dict) {
+ CFStringRef exec_cf_path =
+ static_cast<CFStringRef>(::CFDictionaryGetValue(
+ uuid_dict, CFSTR("DBGSymbolRichExecutable")));
+ if (exec_cf_path && ::CFStringGetFileSystemRepresentation(
+ exec_cf_path, path, sizeof(path))) {
+ if (log) {
+ log->Printf("plist bundle has exec path of %s for UUID %s",
+ path, uuid->GetAsString().c_str());
+ }
+ ++items_found;
+ FileSpec exec_filespec(path);
+ if (path[0] == '~')
+ FileSystem::Instance().Resolve(exec_filespec);
+ if (FileSystem::Instance().Exists(exec_filespec)) {
+ success = true;
+ return_module_spec.GetFileSpec() = exec_filespec;
+ }
+ }
+ }
+
+ if (!success) {
+ // No dictionary, check near the dSYM bundle for an executable that
+ // matches...
+ if (::CFURLGetFileSystemRepresentation(
+ dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
+ char *dsym_extension_pos = ::strstr(path, ".dSYM");
+ if (dsym_extension_pos) {
+ *dsym_extension_pos = '\0';
+ if (log) {
+ log->Printf("Looking for executable binary next to dSYM "
+ "bundle with name with name %s",
+ path);
+ }
+ FileSpec file_spec(path);
+ FileSystem::Instance().Resolve(file_spec);
+ ModuleSpecList module_specs;
+ ModuleSpec matched_module_spec;
+ using namespace llvm::sys::fs;
+ switch (get_file_type(file_spec.GetPath())) {
+
+ case file_type::directory_file: // Bundle directory?
+ {
+ CFCBundle bundle(path);
+ CFCReleaser<CFURLRef> bundle_exe_url(
+ bundle.CopyExecutableURL());
+ if (bundle_exe_url.get()) {
+ if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(),
+ true, (UInt8 *)path,
+ sizeof(path) - 1)) {
+ FileSpec bundle_exe_file_spec(path);
+ FileSystem::Instance().Resolve(bundle_exe_file_spec);
+ if (ObjectFile::GetModuleSpecifications(
+ bundle_exe_file_spec, 0, 0, module_specs) &&
+ module_specs.FindMatchingModuleSpec(
+ module_spec, matched_module_spec))
+
+ {
+ ++items_found;
+ return_module_spec.GetFileSpec() = bundle_exe_file_spec;
+ if (log) {
+ log->Printf("Executable binary %s next to dSYM is "
+ "compatible; using",
+ path);
+ }
+ }
+ }
+ }
+ } break;
+
+ case file_type::fifo_file: // Forget pipes
+ case file_type::socket_file: // We can't process socket files
+ case file_type::file_not_found: // File doesn't exist...
+ case file_type::status_error:
+ break;
+
+ case file_type::type_unknown:
+ case file_type::regular_file:
+ case file_type::symlink_file:
+ case file_type::block_file:
+ case file_type::character_file:
+ if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0,
+ module_specs) &&
+ module_specs.FindMatchingModuleSpec(module_spec,
+ matched_module_spec))
+
+ {
+ ++items_found;
+ return_module_spec.GetFileSpec() = file_spec;
+ if (log) {
+ log->Printf("Executable binary %s next to dSYM is "
+ "compatible; using",
+ path);
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return items_found;
+}
+
+FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &dsym_bundle_fspec,
+ const lldb_private::UUID *uuid,
+ const ArchSpec *arch) {
+ char path[PATH_MAX];
+ if (dsym_bundle_fspec.GetPath(path, sizeof(path)) == 0)
+ return {};
+
+ ::strncat(path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1);
+
+ DIR *dirp = opendir(path);
+ if (!dirp)
+ return {};
+
+ // Make sure we close the directory before exiting this scope.
+ CleanUp cleanup_dir(closedir, dirp);
+
+ FileSpec dsym_fspec;
+ dsym_fspec.GetDirectory().SetCString(path);
+ struct dirent *dp;
+ while ((dp = readdir(dirp)) != NULL) {
+ // Only search directories
+ if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) {
+ if (dp->d_namlen == 1 && dp->d_name[0] == '.')
+ continue;
+
+ if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
+ continue;
+ }
+
+ if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) {
+ dsym_fspec.GetFilename().SetCString(dp->d_name);
+ ModuleSpecList module_specs;
+ if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) {
+ ModuleSpec spec;
+ for (size_t i = 0; i < module_specs.GetSize(); ++i) {
+ bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
+ UNUSED_IF_ASSERT_DISABLED(got_spec);
+ assert(got_spec);
+ if ((uuid == NULL ||
+ (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
+ (arch == NULL ||
+ (spec.GetArchitecturePtr() &&
+ spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
+ return dsym_fspec;
+ }
+ }
+ }
+ }
+ }
+
+ return {};
+}
+
+static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,
+ ModuleSpec &module_spec) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ bool success = false;
+ if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) {
+ std::string str;
+ CFStringRef cf_str;
+ CFDictionaryRef cf_dict;
+
+ cf_str = (CFStringRef)CFDictionaryGetValue(
+ (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable"));
+ if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
+ if (CFCString::FileSystemRepresentation(cf_str, str)) {
+ module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native);
+ FileSystem::Instance().Resolve(module_spec.GetFileSpec());
+ if (log) {
+ log->Printf(
+ "From dsymForUUID plist: Symbol rich executable is at '%s'",
+ str.c_str());
+ }
+ }
+ }
+
+ cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
+ CFSTR("DBGDSYMPath"));
+ if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
+ if (CFCString::FileSystemRepresentation(cf_str, str)) {
+ module_spec.GetSymbolFileSpec().SetFile(str.c_str(),
+ FileSpec::Style::native);
+ FileSystem::Instance().Resolve(module_spec.GetFileSpec());
+ success = true;
+ if (log) {
+ log->Printf("From dsymForUUID plist: dSYM is at '%s'", str.c_str());
+ }
+ }
+ }
+
+ cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
+ CFSTR("DBGArchitecture"));
+ if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
+ if (CFCString::FileSystemRepresentation(cf_str, str))
+ module_spec.GetArchitecture().SetTriple(str.c_str());
+ }
+
+ std::string DBGBuildSourcePath;
+ std::string DBGSourcePath;
+
+ // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping.
+ // If DBGVersion 2, strip last two components of path remappings from
+ // entries to fix an issue with a specific set of
+ // DBGSourcePathRemapping entries that lldb worked
+ // with.
+ // If DBGVersion 3, trust & use the source path remappings as-is.
+ //
+ cf_dict = (CFDictionaryRef)CFDictionaryGetValue(
+ (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping"));
+ if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {
+ // If we see DBGVersion with a value of 2 or higher, this is a new style
+ // DBGSourcePathRemapping dictionary
+ bool new_style_source_remapping_dictionary = false;
+ bool do_truncate_remapping_names = false;
+ std::string original_DBGSourcePath_value = DBGSourcePath;
+ cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
+ CFSTR("DBGVersion"));
+ if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
+ std::string version;
+ CFCString::FileSystemRepresentation(cf_str, version);
+ if (!version.empty() && isdigit(version[0])) {
+ int version_number = atoi(version.c_str());
+ if (version_number > 1) {
+ new_style_source_remapping_dictionary = true;
+ }
+ if (version_number == 2) {
+ do_truncate_remapping_names = true;
+ }
+ }
+ }
+
+ CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);
+ if (kv_pair_count > 0) {
+ CFStringRef *keys =
+ (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
+ CFStringRef *values =
+ (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
+ if (keys != nullptr && values != nullptr) {
+ CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict,
+ (const void **)keys,
+ (const void **)values);
+ }
+ for (CFIndex i = 0; i < kv_pair_count; i++) {
+ DBGBuildSourcePath.clear();
+ DBGSourcePath.clear();
+ if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) {
+ CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath);
+ }
+ if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
+ CFCString::FileSystemRepresentation(values[i], DBGSourcePath);
+ }
+ if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
+ // In the "old style" DBGSourcePathRemapping dictionary, the
+ // DBGSourcePath values (the "values" half of key-value path pairs)
+ // were wrong. Ignore them and use the universal DBGSourcePath
+ // string from earlier.
+ if (new_style_source_remapping_dictionary &&
+ !original_DBGSourcePath_value.empty()) {
+ DBGSourcePath = original_DBGSourcePath_value;
+ }
+ if (DBGSourcePath[0] == '~') {
+ FileSpec resolved_source_path(DBGSourcePath.c_str());
+ FileSystem::Instance().Resolve(resolved_source_path);
+ DBGSourcePath = resolved_source_path.GetPath();
+ }
+ // With version 2 of DBGSourcePathRemapping, we can chop off the
+ // last two filename parts from the source remapping and get a more
+ // general source remapping that still works. Add this as another
+ // option in addition to the full source path remap.
+ module_spec.GetSourceMappingList().Append(
+ ConstString(DBGBuildSourcePath.c_str()),
+ ConstString(DBGSourcePath.c_str()), true);
+ if (do_truncate_remapping_names) {
+ FileSpec build_path(DBGBuildSourcePath.c_str());
+ FileSpec source_path(DBGSourcePath.c_str());
+ build_path.RemoveLastPathComponent();
+ build_path.RemoveLastPathComponent();
+ source_path.RemoveLastPathComponent();
+ source_path.RemoveLastPathComponent();
+ module_spec.GetSourceMappingList().Append(
+ ConstString(build_path.GetPath().c_str()),
+ ConstString(source_path.GetPath().c_str()), true);
+ }
+ }
+ }
+ if (keys)
+ free(keys);
+ if (values)
+ free(values);
+ }
+ }
+
+ // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the
+ // source remappings list.
+
+ cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
+ CFSTR("DBGBuildSourcePath"));
+ if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
+ CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
+ }
+
+ cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
+ CFSTR("DBGSourcePath"));
+ if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
+ CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
+ }
+
+ if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
+ if (DBGSourcePath[0] == '~') {
+ FileSpec resolved_source_path(DBGSourcePath.c_str());
+ FileSystem::Instance().Resolve(resolved_source_path);
+ DBGSourcePath = resolved_source_path.GetPath();
+ }
+ module_spec.GetSourceMappingList().Append(
+ ConstString(DBGBuildSourcePath.c_str()),
+ ConstString(DBGSourcePath.c_str()), true);
+ }
+ }
+ return success;
+}
+
+bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
+ bool force_lookup) {
+ bool success = false;
+ const UUID *uuid_ptr = module_spec.GetUUIDPtr();
+ const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
+
+ // It's expensive to check for the DBGShellCommands defaults setting, only do
+ // it once per lldb run and cache the result.
+ static bool g_have_checked_for_dbgshell_command = false;
+ static const char *g_dbgshell_command = NULL;
+ if (!g_have_checked_for_dbgshell_command) {
+ g_have_checked_for_dbgshell_command = true;
+ CFTypeRef defaults_setting = CFPreferencesCopyAppValue(
+ CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols"));
+ if (defaults_setting &&
+ CFGetTypeID(defaults_setting) == CFStringGetTypeID()) {
+ char cstr_buf[PATH_MAX];
+ if (CFStringGetCString((CFStringRef)defaults_setting, cstr_buf,
+ sizeof(cstr_buf), kCFStringEncodingUTF8)) {
+ g_dbgshell_command =
+ strdup(cstr_buf); // this malloc'ed memory will never be freed
+ }
+ }
+ if (defaults_setting) {
+ CFRelease(defaults_setting);
+ }
+ }
+
+ // When g_dbgshell_command is NULL, the user has not enabled the use of an
+ // external program to find the symbols, don't run it for them.
+ if (!force_lookup && g_dbgshell_command == NULL) {
+ return false;
+ }
+
+ if (uuid_ptr ||
+ (file_spec_ptr && FileSystem::Instance().Exists(*file_spec_ptr))) {
+ static bool g_located_dsym_for_uuid_exe = false;
+ static bool g_dsym_for_uuid_exe_exists = false;
+ static char g_dsym_for_uuid_exe_path[PATH_MAX];
+ if (!g_located_dsym_for_uuid_exe) {
+ g_located_dsym_for_uuid_exe = true;
+ const char *dsym_for_uuid_exe_path_cstr =
+ getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE");
+ FileSpec dsym_for_uuid_exe_spec;
+ if (dsym_for_uuid_exe_path_cstr) {
+ dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr,
+ FileSpec::Style::native);
+ FileSystem::Instance().Resolve(dsym_for_uuid_exe_spec);
+ g_dsym_for_uuid_exe_exists =
+ FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
+ }
+
+ if (!g_dsym_for_uuid_exe_exists) {
+ dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID",
+ FileSpec::Style::native);
+ g_dsym_for_uuid_exe_exists =
+ FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
+ if (!g_dsym_for_uuid_exe_exists) {
+ long bufsize;
+ if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1) {
+ char buffer[bufsize];
+ struct passwd pwd;
+ struct passwd *tilde_rc = NULL;
+ // we are a library so we need to use the reentrant version of
+ // getpwnam()
+ if (getpwnam_r("rc", &pwd, buffer, bufsize, &tilde_rc) == 0 &&
+ tilde_rc && tilde_rc->pw_dir) {
+ std::string dsymforuuid_path(tilde_rc->pw_dir);
+ dsymforuuid_path += "/bin/dsymForUUID";
+ dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(),
+ FileSpec::Style::native);
+ g_dsym_for_uuid_exe_exists =
+ FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
+ }
+ }
+ }
+ }
+ if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL) {
+ dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command,
+ FileSpec::Style::native);
+ FileSystem::Instance().Resolve(dsym_for_uuid_exe_spec);
+ g_dsym_for_uuid_exe_exists =
+ FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
+ }
+
+ if (g_dsym_for_uuid_exe_exists)
+ dsym_for_uuid_exe_spec.GetPath(g_dsym_for_uuid_exe_path,
+ sizeof(g_dsym_for_uuid_exe_path));
+ }
+ if (g_dsym_for_uuid_exe_exists) {
+ std::string uuid_str;
+ char file_path[PATH_MAX];
+ file_path[0] = '\0';
+
+ if (uuid_ptr)
+ uuid_str = uuid_ptr->GetAsString();
+
+ if (file_spec_ptr)
+ file_spec_ptr->GetPath(file_path, sizeof(file_path));
+
+ StreamString command;
+ if (!uuid_str.empty())
+ command.Printf("%s --ignoreNegativeCache --copyExecutable %s",
+ g_dsym_for_uuid_exe_path, uuid_str.c_str());
+ else if (file_path[0] != '\0')
+ command.Printf("%s --ignoreNegativeCache --copyExecutable %s",
+ g_dsym_for_uuid_exe_path, file_path);
+
+ if (!command.GetString().empty()) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ int exit_status = -1;
+ int signo = -1;
+ std::string command_output;
+ if (log) {
+ if (!uuid_str.empty())
+ log->Printf("Calling %s with UUID %s to find dSYM",
+ g_dsym_for_uuid_exe_path, uuid_str.c_str());
+ else if (file_path[0] != '\0')
+ log->Printf("Calling %s with file %s to find dSYM",
+ g_dsym_for_uuid_exe_path, file_path);
+ }
+ Status error = Host::RunShellCommand(
+ command.GetData(),
+ NULL, // current working directory
+ &exit_status, // Exit status
+ &signo, // Signal int *
+ &command_output, // Command output
+ std::chrono::seconds(
+ 120), // Large timeout to allow for long dsym download times
+ false); // Don't run in a shell (we don't need shell expansion)
+ if (error.Success() && exit_status == 0 && !command_output.empty()) {
+ CFCData data(CFDataCreateWithBytesNoCopy(
+ NULL, (const UInt8 *)command_output.data(), command_output.size(),
+ kCFAllocatorNull));
+
+ CFCReleaser<CFDictionaryRef> plist(
+ (CFDictionaryRef)::CFPropertyListCreateFromXMLData(
+ NULL, data.get(), kCFPropertyListImmutable, NULL));
+
+ if (plist.get() &&
+ CFGetTypeID(plist.get()) == CFDictionaryGetTypeID()) {
+ if (!uuid_str.empty()) {
+ CFCString uuid_cfstr(uuid_str.c_str());
+ CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue(
+ plist.get(), uuid_cfstr.get());
+ success =
+ GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec);
+ } else {
+ const CFIndex num_values = ::CFDictionaryGetCount(plist.get());
+ if (num_values > 0) {
+ std::vector<CFStringRef> keys(num_values, NULL);
+ std::vector<CFDictionaryRef> values(num_values, NULL);
+ ::CFDictionaryGetKeysAndValues(plist.get(), NULL,
+ (const void **)&values[0]);
+ if (num_values == 1) {
+ return GetModuleSpecInfoFromUUIDDictionary(values[0],
+ module_spec);
+ } else {
+ for (CFIndex i = 0; i < num_values; ++i) {
+ ModuleSpec curr_module_spec;
+ if (GetModuleSpecInfoFromUUIDDictionary(values[i],
+ curr_module_spec)) {
+ if (module_spec.GetArchitecture().IsCompatibleMatch(
+ curr_module_spec.GetArchitecture())) {
+ module_spec = curr_module_spec;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if (log) {
+ if (!uuid_str.empty())
+ log->Printf("Called %s on %s, no matches",
+ g_dsym_for_uuid_exe_path, uuid_str.c_str());
+ else if (file_path[0] != '\0')
+ log->Printf("Called %s on %s, no matches",
+ g_dsym_for_uuid_exe_path, file_path);
+ }
+ }
+ }
+ }
+ }
+ return success;
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/ObjectFile.cpp b/contrib/llvm-project/lldb/source/Symbol/ObjectFile.cpp
new file mode 100644
index 000000000000..172d2b3f01e3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/ObjectFile.cpp
@@ -0,0 +1,744 @@
+//===-- ObjectFile.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectContainer.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Timer.h"
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ObjectFileSP
+ObjectFile::FindPlugin(const lldb::ModuleSP &module_sp, const FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t file_size,
+ DataBufferSP &data_sp, lldb::offset_t &data_offset) {
+ ObjectFileSP object_file_sp;
+
+ if (module_sp) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat,
+ "ObjectFile::FindPlugin (module = %s, file = %p, file_offset = "
+ "0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")",
+ module_sp->GetFileSpec().GetPath().c_str(),
+ static_cast<const void *>(file), static_cast<uint64_t>(file_offset),
+ static_cast<uint64_t>(file_size));
+ if (file) {
+ FileSpec archive_file;
+ ObjectContainerCreateInstance create_object_container_callback;
+
+ const bool file_exists = FileSystem::Instance().Exists(*file);
+ if (!data_sp) {
+ // We have an object name which most likely means we have a .o file in
+ // a static archive (.a file). Try and see if we have a cached archive
+ // first without reading any data first
+ if (file_exists && module_sp->GetObjectName()) {
+ for (uint32_t idx = 0;
+ (create_object_container_callback =
+ PluginManager::GetObjectContainerCreateCallbackAtIndex(
+ idx)) != nullptr;
+ ++idx) {
+ std::unique_ptr<ObjectContainer> object_container_up(
+ create_object_container_callback(module_sp, data_sp,
+ data_offset, file, file_offset,
+ file_size));
+
+ if (object_container_up)
+ object_file_sp = object_container_up->GetObjectFile(file);
+
+ if (object_file_sp.get())
+ return object_file_sp;
+ }
+ }
+ // Ok, we didn't find any containers that have a named object, now lets
+ // read the first 512 bytes from the file so the object file and object
+ // container plug-ins can use these bytes to see if they can parse this
+ // file.
+ if (file_size > 0) {
+ data_sp = FileSystem::Instance().CreateDataBuffer(file->GetPath(),
+ 512, file_offset);
+ data_offset = 0;
+ }
+ }
+
+ if (!data_sp || data_sp->GetByteSize() == 0) {
+ // Check for archive file with format "/path/to/archive.a(object.o)"
+ char path_with_object[PATH_MAX * 2];
+ module_sp->GetFileSpec().GetPath(path_with_object,
+ sizeof(path_with_object));
+
+ ConstString archive_object;
+ const bool must_exist = true;
+ if (ObjectFile::SplitArchivePathWithObject(
+ path_with_object, archive_file, archive_object, must_exist)) {
+ file_size = FileSystem::Instance().GetByteSize(archive_file);
+ if (file_size > 0) {
+ file = &archive_file;
+ module_sp->SetFileSpecAndObjectName(archive_file, archive_object);
+ // Check if this is a object container by iterating through all
+ // object container plugin instances and then trying to get an
+ // object file from the container plugins since we had a name.
+ // Also, don't read
+ // ANY data in case there is data cached in the container plug-ins
+ // (like BSD archives caching the contained objects within an
+ // file).
+ for (uint32_t idx = 0;
+ (create_object_container_callback =
+ PluginManager::GetObjectContainerCreateCallbackAtIndex(
+ idx)) != nullptr;
+ ++idx) {
+ std::unique_ptr<ObjectContainer> object_container_up(
+ create_object_container_callback(module_sp, data_sp,
+ data_offset, file,
+ file_offset, file_size));
+
+ if (object_container_up)
+ object_file_sp = object_container_up->GetObjectFile(file);
+
+ if (object_file_sp.get())
+ return object_file_sp;
+ }
+ // We failed to find any cached object files in the container plug-
+ // ins, so lets read the first 512 bytes and try again below...
+ data_sp = FileSystem::Instance().CreateDataBuffer(
+ archive_file.GetPath(), 512, file_offset);
+ }
+ }
+ }
+
+ if (data_sp && data_sp->GetByteSize() > 0) {
+ // Check if this is a normal object file by iterating through all
+ // object file plugin instances.
+ ObjectFileCreateInstance create_object_file_callback;
+ for (uint32_t idx = 0;
+ (create_object_file_callback =
+ PluginManager::GetObjectFileCreateCallbackAtIndex(idx)) !=
+ nullptr;
+ ++idx) {
+ object_file_sp.reset(create_object_file_callback(
+ module_sp, data_sp, data_offset, file, file_offset, file_size));
+ if (object_file_sp.get())
+ return object_file_sp;
+ }
+
+ // Check if this is a object container by iterating through all object
+ // container plugin instances and then trying to get an object file
+ // from the container.
+ for (uint32_t idx = 0;
+ (create_object_container_callback =
+ PluginManager::GetObjectContainerCreateCallbackAtIndex(
+ idx)) != nullptr;
+ ++idx) {
+ std::unique_ptr<ObjectContainer> object_container_up(
+ create_object_container_callback(module_sp, data_sp, data_offset,
+ file, file_offset, file_size));
+
+ if (object_container_up)
+ object_file_sp = object_container_up->GetObjectFile(file);
+
+ if (object_file_sp.get())
+ return object_file_sp;
+ }
+ }
+ }
+ }
+ // We didn't find it, so clear our shared pointer in case it contains
+ // anything and return an empty shared pointer
+ object_file_sp.reset();
+ return object_file_sp;
+}
+
+ObjectFileSP ObjectFile::FindPlugin(const lldb::ModuleSP &module_sp,
+ const ProcessSP &process_sp,
+ lldb::addr_t header_addr,
+ DataBufferSP &data_sp) {
+ ObjectFileSP object_file_sp;
+
+ if (module_sp) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "ObjectFile::FindPlugin (module = "
+ "%s, process = %p, header_addr = "
+ "0x%" PRIx64 ")",
+ module_sp->GetFileSpec().GetPath().c_str(),
+ static_cast<void *>(process_sp.get()), header_addr);
+ uint32_t idx;
+
+ // Check if this is a normal object file by iterating through all object
+ // file plugin instances.
+ ObjectFileCreateMemoryInstance create_callback;
+ for (idx = 0;
+ (create_callback =
+ PluginManager::GetObjectFileCreateMemoryCallbackAtIndex(idx)) !=
+ nullptr;
+ ++idx) {
+ object_file_sp.reset(
+ create_callback(module_sp, data_sp, process_sp, header_addr));
+ if (object_file_sp.get())
+ return object_file_sp;
+ }
+ }
+
+ // We didn't find it, so clear our shared pointer in case it contains
+ // anything and return an empty shared pointer
+ object_file_sp.reset();
+ return object_file_sp;
+}
+
+size_t ObjectFile::GetModuleSpecifications(const FileSpec &file,
+ lldb::offset_t file_offset,
+ lldb::offset_t file_size,
+ ModuleSpecList &specs) {
+ DataBufferSP data_sp =
+ FileSystem::Instance().CreateDataBuffer(file.GetPath(), 512, file_offset);
+ if (data_sp) {
+ if (file_size == 0) {
+ const lldb::offset_t actual_file_size =
+ FileSystem::Instance().GetByteSize(file);
+ if (actual_file_size > file_offset)
+ file_size = actual_file_size - file_offset;
+ }
+ return ObjectFile::GetModuleSpecifications(file, // file spec
+ data_sp, // data bytes
+ 0, // data offset
+ file_offset, // file offset
+ file_size, // file length
+ specs);
+ }
+ return 0;
+}
+
+size_t ObjectFile::GetModuleSpecifications(
+ const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, lldb::offset_t file_offset,
+ lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) {
+ const size_t initial_count = specs.GetSize();
+ ObjectFileGetModuleSpecifications callback;
+ uint32_t i;
+ // Try the ObjectFile plug-ins
+ for (i = 0;
+ (callback =
+ PluginManager::GetObjectFileGetModuleSpecificationsCallbackAtIndex(
+ i)) != nullptr;
+ ++i) {
+ if (callback(file, data_sp, data_offset, file_offset, file_size, specs) > 0)
+ return specs.GetSize() - initial_count;
+ }
+
+ // Try the ObjectContainer plug-ins
+ for (i = 0;
+ (callback = PluginManager::
+ GetObjectContainerGetModuleSpecificationsCallbackAtIndex(i)) !=
+ nullptr;
+ ++i) {
+ if (callback(file, data_sp, data_offset, file_offset, file_size, specs) > 0)
+ return specs.GetSize() - initial_count;
+ }
+ return 0;
+}
+
+ObjectFile::ObjectFile(const lldb::ModuleSP &module_sp,
+ const FileSpec *file_spec_ptr,
+ lldb::offset_t file_offset, lldb::offset_t length,
+ const lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset)
+ : ModuleChild(module_sp),
+ m_file(), // This file could be different from the original module's file
+ m_type(eTypeInvalid), m_strata(eStrataInvalid),
+ m_file_offset(file_offset), m_length(length), m_data(), m_process_wp(),
+ m_memory_addr(LLDB_INVALID_ADDRESS), m_sections_up(), m_symtab_up(),
+ m_synthetic_symbol_idx(0) {
+ if (file_spec_ptr)
+ m_file = *file_spec_ptr;
+ if (data_sp)
+ m_data.SetData(data_sp, data_offset, length);
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p ObjectFile::ObjectFile() module = %p (%s), file = %s, "
+ "file_offset = 0x%8.8" PRIx64 ", size = %" PRIu64,
+ static_cast<void *>(this), static_cast<void *>(module_sp.get()),
+ module_sp->GetSpecificationDescription().c_str(),
+ m_file ? m_file.GetPath().c_str() : "<NULL>", m_file_offset,
+ m_length);
+}
+
+ObjectFile::ObjectFile(const lldb::ModuleSP &module_sp,
+ const ProcessSP &process_sp, lldb::addr_t header_addr,
+ DataBufferSP &header_data_sp)
+ : ModuleChild(module_sp), m_file(), m_type(eTypeInvalid),
+ m_strata(eStrataInvalid), m_file_offset(0), m_length(0), m_data(),
+ m_process_wp(process_sp), m_memory_addr(header_addr), m_sections_up(),
+ m_symtab_up(), m_synthetic_symbol_idx(0) {
+ if (header_data_sp)
+ m_data.SetData(header_data_sp, 0, header_data_sp->GetByteSize());
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p ObjectFile::ObjectFile() module = %p (%s), process = %p, "
+ "header_addr = 0x%" PRIx64,
+ static_cast<void *>(this), static_cast<void *>(module_sp.get()),
+ module_sp->GetSpecificationDescription().c_str(),
+ static_cast<void *>(process_sp.get()), m_memory_addr);
+}
+
+ObjectFile::~ObjectFile() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p ObjectFile::~ObjectFile ()\n", static_cast<void *>(this));
+}
+
+bool ObjectFile::SetModulesArchitecture(const ArchSpec &new_arch) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp)
+ return module_sp->SetArchitecture(new_arch);
+ return false;
+}
+
+AddressClass ObjectFile::GetAddressClass(addr_t file_addr) {
+ Symtab *symtab = GetSymtab();
+ if (symtab) {
+ Symbol *symbol = symtab->FindSymbolContainingFileAddress(file_addr);
+ if (symbol) {
+ if (symbol->ValueIsAddress()) {
+ const SectionSP section_sp(symbol->GetAddressRef().GetSection());
+ if (section_sp) {
+ const SectionType section_type = section_sp->GetType();
+ switch (section_type) {
+ case eSectionTypeInvalid:
+ return AddressClass::eUnknown;
+ case eSectionTypeCode:
+ return AddressClass::eCode;
+ case eSectionTypeContainer:
+ return AddressClass::eUnknown;
+ case eSectionTypeData:
+ case eSectionTypeDataCString:
+ case eSectionTypeDataCStringPointers:
+ case eSectionTypeDataSymbolAddress:
+ case eSectionTypeData4:
+ case eSectionTypeData8:
+ case eSectionTypeData16:
+ case eSectionTypeDataPointers:
+ case eSectionTypeZeroFill:
+ case eSectionTypeDataObjCMessageRefs:
+ case eSectionTypeDataObjCCFStrings:
+ case eSectionTypeGoSymtab:
+ return AddressClass::eData;
+ case eSectionTypeDebug:
+ case eSectionTypeDWARFDebugAbbrev:
+ case eSectionTypeDWARFDebugAbbrevDwo:
+ case eSectionTypeDWARFDebugAddr:
+ case eSectionTypeDWARFDebugAranges:
+ case eSectionTypeDWARFDebugCuIndex:
+ case eSectionTypeDWARFDebugFrame:
+ case eSectionTypeDWARFDebugInfo:
+ case eSectionTypeDWARFDebugInfoDwo:
+ case eSectionTypeDWARFDebugLine:
+ case eSectionTypeDWARFDebugLineStr:
+ case eSectionTypeDWARFDebugLoc:
+ case eSectionTypeDWARFDebugLocLists:
+ case eSectionTypeDWARFDebugMacInfo:
+ case eSectionTypeDWARFDebugMacro:
+ case eSectionTypeDWARFDebugNames:
+ case eSectionTypeDWARFDebugPubNames:
+ case eSectionTypeDWARFDebugPubTypes:
+ case eSectionTypeDWARFDebugRanges:
+ case eSectionTypeDWARFDebugRngLists:
+ case eSectionTypeDWARFDebugStr:
+ case eSectionTypeDWARFDebugStrDwo:
+ case eSectionTypeDWARFDebugStrOffsets:
+ case eSectionTypeDWARFDebugStrOffsetsDwo:
+ case eSectionTypeDWARFDebugTypes:
+ case eSectionTypeDWARFDebugTypesDwo:
+ case eSectionTypeDWARFAppleNames:
+ case eSectionTypeDWARFAppleTypes:
+ case eSectionTypeDWARFAppleNamespaces:
+ case eSectionTypeDWARFAppleObjC:
+ case eSectionTypeDWARFGNUDebugAltLink:
+ return AddressClass::eDebug;
+ case eSectionTypeEHFrame:
+ case eSectionTypeARMexidx:
+ case eSectionTypeARMextab:
+ case eSectionTypeCompactUnwind:
+ return AddressClass::eRuntime;
+ case eSectionTypeELFSymbolTable:
+ case eSectionTypeELFDynamicSymbols:
+ case eSectionTypeELFRelocationEntries:
+ case eSectionTypeELFDynamicLinkInfo:
+ case eSectionTypeOther:
+ return AddressClass::eUnknown;
+ case eSectionTypeAbsoluteAddress:
+ // In case of absolute sections decide the address class based on
+ // the symbol type because the section type isn't specify if it is
+ // a code or a data section.
+ break;
+ }
+ }
+ }
+
+ const SymbolType symbol_type = symbol->GetType();
+ switch (symbol_type) {
+ case eSymbolTypeAny:
+ return AddressClass::eUnknown;
+ case eSymbolTypeAbsolute:
+ return AddressClass::eUnknown;
+ case eSymbolTypeCode:
+ return AddressClass::eCode;
+ case eSymbolTypeTrampoline:
+ return AddressClass::eCode;
+ case eSymbolTypeResolver:
+ return AddressClass::eCode;
+ case eSymbolTypeData:
+ return AddressClass::eData;
+ case eSymbolTypeRuntime:
+ return AddressClass::eRuntime;
+ case eSymbolTypeException:
+ return AddressClass::eRuntime;
+ case eSymbolTypeSourceFile:
+ return AddressClass::eDebug;
+ case eSymbolTypeHeaderFile:
+ return AddressClass::eDebug;
+ case eSymbolTypeObjectFile:
+ return AddressClass::eDebug;
+ case eSymbolTypeCommonBlock:
+ return AddressClass::eDebug;
+ case eSymbolTypeBlock:
+ return AddressClass::eDebug;
+ case eSymbolTypeLocal:
+ return AddressClass::eData;
+ case eSymbolTypeParam:
+ return AddressClass::eData;
+ case eSymbolTypeVariable:
+ return AddressClass::eData;
+ case eSymbolTypeVariableType:
+ return AddressClass::eDebug;
+ case eSymbolTypeLineEntry:
+ return AddressClass::eDebug;
+ case eSymbolTypeLineHeader:
+ return AddressClass::eDebug;
+ case eSymbolTypeScopeBegin:
+ return AddressClass::eDebug;
+ case eSymbolTypeScopeEnd:
+ return AddressClass::eDebug;
+ case eSymbolTypeAdditional:
+ return AddressClass::eUnknown;
+ case eSymbolTypeCompiler:
+ return AddressClass::eDebug;
+ case eSymbolTypeInstrumentation:
+ return AddressClass::eDebug;
+ case eSymbolTypeUndefined:
+ return AddressClass::eUnknown;
+ case eSymbolTypeObjCClass:
+ return AddressClass::eRuntime;
+ case eSymbolTypeObjCMetaClass:
+ return AddressClass::eRuntime;
+ case eSymbolTypeObjCIVar:
+ return AddressClass::eRuntime;
+ case eSymbolTypeReExported:
+ return AddressClass::eRuntime;
+ }
+ }
+ }
+ return AddressClass::eUnknown;
+}
+
+DataBufferSP ObjectFile::ReadMemory(const ProcessSP &process_sp,
+ lldb::addr_t addr, size_t byte_size) {
+ DataBufferSP data_sp;
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> data_up(new DataBufferHeap(byte_size, 0));
+ Status error;
+ const size_t bytes_read = process_sp->ReadMemory(
+ addr, data_up->GetBytes(), data_up->GetByteSize(), error);
+ if (bytes_read == byte_size)
+ data_sp.reset(data_up.release());
+ }
+ return data_sp;
+}
+
+size_t ObjectFile::GetData(lldb::offset_t offset, size_t length,
+ DataExtractor &data) const {
+ // The entire file has already been mmap'ed into m_data, so just copy from
+ // there as the back mmap buffer will be shared with shared pointers.
+ return data.SetData(m_data, offset, length);
+}
+
+size_t ObjectFile::CopyData(lldb::offset_t offset, size_t length,
+ void *dst) const {
+ // The entire file has already been mmap'ed into m_data, so just copy from
+ // there Note that the data remains in target byte order.
+ return m_data.CopyData(offset, length, dst);
+}
+
+size_t ObjectFile::ReadSectionData(Section *section,
+ lldb::offset_t section_offset, void *dst,
+ size_t dst_len) {
+ assert(section);
+ section_offset *= section->GetTargetByteSize();
+
+ // If some other objectfile owns this data, pass this to them.
+ if (section->GetObjectFile() != this)
+ return section->GetObjectFile()->ReadSectionData(section, section_offset,
+ dst, dst_len);
+
+ if (IsInMemory()) {
+ ProcessSP process_sp(m_process_wp.lock());
+ if (process_sp) {
+ Status error;
+ const addr_t base_load_addr =
+ section->GetLoadBaseAddress(&process_sp->GetTarget());
+ if (base_load_addr != LLDB_INVALID_ADDRESS)
+ return process_sp->ReadMemory(base_load_addr + section_offset, dst,
+ dst_len, error);
+ }
+ } else {
+ if (!section->IsRelocated())
+ RelocateSection(section);
+
+ const lldb::offset_t section_file_size = section->GetFileSize();
+ if (section_offset < section_file_size) {
+ const size_t section_bytes_left = section_file_size - section_offset;
+ size_t section_dst_len = dst_len;
+ if (section_dst_len > section_bytes_left)
+ section_dst_len = section_bytes_left;
+ return CopyData(section->GetFileOffset() + section_offset,
+ section_dst_len, dst);
+ } else {
+ if (section->GetType() == eSectionTypeZeroFill) {
+ const uint64_t section_size = section->GetByteSize();
+ const uint64_t section_bytes_left = section_size - section_offset;
+ uint64_t section_dst_len = dst_len;
+ if (section_dst_len > section_bytes_left)
+ section_dst_len = section_bytes_left;
+ memset(dst, 0, section_dst_len);
+ return section_dst_len;
+ }
+ }
+ }
+ return 0;
+}
+
+// Get the section data the file on disk
+size_t ObjectFile::ReadSectionData(Section *section,
+ DataExtractor &section_data) {
+ // If some other objectfile owns this data, pass this to them.
+ if (section->GetObjectFile() != this)
+ return section->GetObjectFile()->ReadSectionData(section, section_data);
+
+ if (IsInMemory()) {
+ ProcessSP process_sp(m_process_wp.lock());
+ if (process_sp) {
+ const addr_t base_load_addr =
+ section->GetLoadBaseAddress(&process_sp->GetTarget());
+ if (base_load_addr != LLDB_INVALID_ADDRESS) {
+ DataBufferSP data_sp(
+ ReadMemory(process_sp, base_load_addr, section->GetByteSize()));
+ if (data_sp) {
+ section_data.SetData(data_sp, 0, data_sp->GetByteSize());
+ section_data.SetByteOrder(process_sp->GetByteOrder());
+ section_data.SetAddressByteSize(process_sp->GetAddressByteSize());
+ return section_data.GetByteSize();
+ }
+ }
+ }
+ return GetData(section->GetFileOffset(), section->GetFileSize(),
+ section_data);
+ } else {
+ // The object file now contains a full mmap'ed copy of the object file
+ // data, so just use this
+ if (!section->IsRelocated())
+ RelocateSection(section);
+
+ return GetData(section->GetFileOffset(), section->GetFileSize(),
+ section_data);
+ }
+}
+
+bool ObjectFile::SplitArchivePathWithObject(const char *path_with_object,
+ FileSpec &archive_file,
+ ConstString &archive_object,
+ bool must_exist) {
+ RegularExpression g_object_regex(llvm::StringRef("(.*)\\(([^\\)]+)\\)$"));
+ RegularExpression::Match regex_match(2);
+ if (g_object_regex.Execute(llvm::StringRef::withNullAsEmpty(path_with_object),
+ &regex_match)) {
+ std::string path;
+ std::string obj;
+ if (regex_match.GetMatchAtIndex(path_with_object, 1, path) &&
+ regex_match.GetMatchAtIndex(path_with_object, 2, obj)) {
+ archive_file.SetFile(path, FileSpec::Style::native);
+ archive_object.SetCString(obj.c_str());
+ return !(must_exist && !FileSystem::Instance().Exists(archive_file));
+ }
+ }
+ return false;
+}
+
+void ObjectFile::ClearSymtab() {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p ObjectFile::ClearSymtab () symtab = %p",
+ static_cast<void *>(this),
+ static_cast<void *>(m_symtab_up.get()));
+ m_symtab_up.reset();
+ }
+}
+
+SectionList *ObjectFile::GetSectionList(bool update_module_section_list) {
+ if (m_sections_up == nullptr) {
+ if (update_module_section_list) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ CreateSections(*module_sp->GetUnifiedSectionList());
+ }
+ } else {
+ SectionList unified_section_list;
+ CreateSections(unified_section_list);
+ }
+ }
+ return m_sections_up.get();
+}
+
+lldb::SymbolType
+ObjectFile::GetSymbolTypeFromName(llvm::StringRef name,
+ lldb::SymbolType symbol_type_hint) {
+ if (!name.empty()) {
+ if (name.startswith("_OBJC_")) {
+ // ObjC
+ if (name.startswith("_OBJC_CLASS_$_"))
+ return lldb::eSymbolTypeObjCClass;
+ if (name.startswith("_OBJC_METACLASS_$_"))
+ return lldb::eSymbolTypeObjCMetaClass;
+ if (name.startswith("_OBJC_IVAR_$_"))
+ return lldb::eSymbolTypeObjCIVar;
+ } else if (name.startswith(".objc_class_name_")) {
+ // ObjC v1
+ return lldb::eSymbolTypeObjCClass;
+ }
+ }
+ return symbol_type_hint;
+}
+
+ConstString ObjectFile::GetNextSyntheticSymbolName() {
+ StreamString ss;
+ ConstString file_name = GetModule()->GetFileSpec().GetFilename();
+ ss.Printf("___lldb_unnamed_symbol%u$$%s", ++m_synthetic_symbol_idx,
+ file_name.GetCString());
+ return ConstString(ss.GetString());
+}
+
+std::vector<ObjectFile::LoadableData>
+ObjectFile::GetLoadableData(Target &target) {
+ std::vector<LoadableData> loadables;
+ SectionList *section_list = GetSectionList();
+ if (!section_list)
+ return loadables;
+ // Create a list of loadable data from loadable sections
+ size_t section_count = section_list->GetNumSections(0);
+ for (size_t i = 0; i < section_count; ++i) {
+ LoadableData loadable;
+ SectionSP section_sp = section_list->GetSectionAtIndex(i);
+ loadable.Dest =
+ target.GetSectionLoadList().GetSectionLoadAddress(section_sp);
+ if (loadable.Dest == LLDB_INVALID_ADDRESS)
+ continue;
+ // We can skip sections like bss
+ if (section_sp->GetFileSize() == 0)
+ continue;
+ DataExtractor section_data;
+ section_sp->GetSectionData(section_data);
+ loadable.Contents = llvm::ArrayRef<uint8_t>(section_data.GetDataStart(),
+ section_data.GetByteSize());
+ loadables.push_back(loadable);
+ }
+ return loadables;
+}
+
+void ObjectFile::RelocateSection(lldb_private::Section *section)
+{
+}
+
+DataBufferSP ObjectFile::MapFileData(const FileSpec &file, uint64_t Size,
+ uint64_t Offset) {
+ return FileSystem::Instance().CreateDataBuffer(file.GetPath(), Size, Offset);
+}
+
+void llvm::format_provider<ObjectFile::Type>::format(
+ const ObjectFile::Type &type, raw_ostream &OS, StringRef Style) {
+ switch (type) {
+ case ObjectFile::eTypeInvalid:
+ OS << "invalid";
+ break;
+ case ObjectFile::eTypeCoreFile:
+ OS << "core file";
+ break;
+ case ObjectFile::eTypeExecutable:
+ OS << "executable";
+ break;
+ case ObjectFile::eTypeDebugInfo:
+ OS << "debug info";
+ break;
+ case ObjectFile::eTypeDynamicLinker:
+ OS << "dynamic linker";
+ break;
+ case ObjectFile::eTypeObjectFile:
+ OS << "object file";
+ break;
+ case ObjectFile::eTypeSharedLibrary:
+ OS << "shared library";
+ break;
+ case ObjectFile::eTypeStubLibrary:
+ OS << "stub library";
+ break;
+ case ObjectFile::eTypeJIT:
+ OS << "jit";
+ break;
+ case ObjectFile::eTypeUnknown:
+ OS << "unknown";
+ break;
+ }
+}
+
+void llvm::format_provider<ObjectFile::Strata>::format(
+ const ObjectFile::Strata &strata, raw_ostream &OS, StringRef Style) {
+ switch (strata) {
+ case ObjectFile::eStrataInvalid:
+ OS << "invalid";
+ break;
+ case ObjectFile::eStrataUnknown:
+ OS << "unknown";
+ break;
+ case ObjectFile::eStrataUser:
+ OS << "user";
+ break;
+ case ObjectFile::eStrataKernel:
+ OS << "kernel";
+ break;
+ case ObjectFile::eStrataRawImage:
+ OS << "raw image";
+ break;
+ case ObjectFile::eStrataJIT:
+ OS << "jit";
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/PostfixExpression.cpp b/contrib/llvm-project/lldb/source/Symbol/PostfixExpression.cpp
new file mode 100644
index 000000000000..148653561a4e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/PostfixExpression.cpp
@@ -0,0 +1,227 @@
+//===-- PostfixExpression.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements support for postfix expressions found in several symbol
+// file formats, and their conversion to DWARF.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/PostfixExpression.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Utility/Stream.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace lldb_private;
+using namespace lldb_private::postfix;
+
+static llvm::Optional<BinaryOpNode::OpType>
+GetBinaryOpType(llvm::StringRef token) {
+ if (token.size() != 1)
+ return llvm::None;
+ switch (token[0]) {
+ case '@':
+ return BinaryOpNode::Align;
+ case '-':
+ return BinaryOpNode::Minus;
+ case '+':
+ return BinaryOpNode::Plus;
+ }
+ return llvm::None;
+}
+
+static llvm::Optional<UnaryOpNode::OpType>
+GetUnaryOpType(llvm::StringRef token) {
+ if (token == "^")
+ return UnaryOpNode::Deref;
+ return llvm::None;
+}
+
+Node *postfix::Parse(llvm::StringRef expr, llvm::BumpPtrAllocator &alloc) {
+ llvm::SmallVector<Node *, 4> stack;
+
+ llvm::StringRef token;
+ while (std::tie(token, expr) = getToken(expr), !token.empty()) {
+ if (auto op_type = GetBinaryOpType(token)) {
+ // token is binary operator
+ if (stack.size() < 2)
+ return nullptr;
+
+ Node *right = stack.pop_back_val();
+ Node *left = stack.pop_back_val();
+ stack.push_back(MakeNode<BinaryOpNode>(alloc, *op_type, *left, *right));
+ continue;
+ }
+
+ if (auto op_type = GetUnaryOpType(token)) {
+ // token is unary operator
+ if (stack.empty())
+ return nullptr;
+
+ Node *operand = stack.pop_back_val();
+ stack.push_back(MakeNode<UnaryOpNode>(alloc, *op_type, *operand));
+ continue;
+ }
+
+ int64_t value;
+ if (to_integer(token, value, 10)) {
+ // token is integer literal
+ stack.push_back(MakeNode<IntegerNode>(alloc, value));
+ continue;
+ }
+
+ stack.push_back(MakeNode<SymbolNode>(alloc, token));
+ }
+
+ if (stack.size() != 1)
+ return nullptr;
+
+ return stack.back();
+}
+
+namespace {
+class SymbolResolver : public Visitor<bool> {
+public:
+ SymbolResolver(llvm::function_ref<Node *(SymbolNode &symbol)> replacer)
+ : m_replacer(replacer) {}
+
+ using Visitor<bool>::Dispatch;
+
+private:
+ bool Visit(BinaryOpNode &binary, Node *&) override {
+ return Dispatch(binary.Left()) && Dispatch(binary.Right());
+ }
+
+ bool Visit(InitialValueNode &, Node *&) override { return true; }
+ bool Visit(IntegerNode &, Node *&) override { return true; }
+ bool Visit(RegisterNode &, Node *&) override { return true; }
+
+ bool Visit(SymbolNode &symbol, Node *&ref) override {
+ if (Node *replacement = m_replacer(symbol)) {
+ ref = replacement;
+ if (replacement != &symbol)
+ return Dispatch(ref);
+ return true;
+ }
+ return false;
+ }
+
+ bool Visit(UnaryOpNode &unary, Node *&) override {
+ return Dispatch(unary.Operand());
+ }
+
+ llvm::function_ref<Node *(SymbolNode &symbol)> m_replacer;
+};
+
+class DWARFCodegen : public Visitor<> {
+public:
+ DWARFCodegen(Stream &stream) : m_out_stream(stream) {}
+
+ using Visitor<>::Dispatch;
+
+private:
+ void Visit(BinaryOpNode &binary, Node *&) override;
+
+ void Visit(InitialValueNode &val, Node *&) override;
+
+ void Visit(IntegerNode &integer, Node *&) override {
+ m_out_stream.PutHex8(DW_OP_consts);
+ m_out_stream.PutSLEB128(integer.GetValue());
+ ++m_stack_depth;
+ }
+
+ void Visit(RegisterNode &reg, Node *&) override;
+
+ void Visit(SymbolNode &symbol, Node *&) override {
+ llvm_unreachable("Symbols should have been resolved by now!");
+ }
+
+ void Visit(UnaryOpNode &unary, Node *&) override;
+
+ Stream &m_out_stream;
+
+ /// The number keeping track of the evaluation stack depth at any given
+ /// moment. Used for implementing InitialValueNodes. We start with
+ /// m_stack_depth = 1, assuming that the initial value is already on the
+ /// stack. This initial value will be the value of all InitialValueNodes. If
+ /// the expression does not contain InitialValueNodes, then m_stack_depth is
+ /// not used, and the generated expression will run correctly even without an
+ /// initial value.
+ size_t m_stack_depth = 1;
+};
+} // namespace
+
+void DWARFCodegen::Visit(BinaryOpNode &binary, Node *&) {
+ Dispatch(binary.Left());
+ Dispatch(binary.Right());
+
+ switch (binary.GetOpType()) {
+ case BinaryOpNode::Plus:
+ m_out_stream.PutHex8(DW_OP_plus);
+ // NOTE: can be optimized by using DW_OP_plus_uconst opcpode
+ // if right child node is constant value
+ break;
+ case BinaryOpNode::Minus:
+ m_out_stream.PutHex8(DW_OP_minus);
+ break;
+ case BinaryOpNode::Align:
+ // emit align operator a @ b as
+ // a & ~(b - 1)
+ // NOTE: implicitly assuming that b is power of 2
+ m_out_stream.PutHex8(DW_OP_lit1);
+ m_out_stream.PutHex8(DW_OP_minus);
+ m_out_stream.PutHex8(DW_OP_not);
+
+ m_out_stream.PutHex8(DW_OP_and);
+ break;
+ }
+ --m_stack_depth; // Two pops, one push.
+}
+
+void DWARFCodegen::Visit(InitialValueNode &, Node *&) {
+ // We never go below the initial stack, so we can pick the initial value from
+ // the bottom of the stack at any moment.
+ assert(m_stack_depth >= 1);
+ m_out_stream.PutHex8(DW_OP_pick);
+ m_out_stream.PutHex8(m_stack_depth - 1);
+ ++m_stack_depth;
+}
+
+void DWARFCodegen::Visit(RegisterNode &reg, Node *&) {
+ uint32_t reg_num = reg.GetRegNum();
+ assert(reg_num != LLDB_INVALID_REGNUM);
+
+ if (reg_num > 31) {
+ m_out_stream.PutHex8(DW_OP_bregx);
+ m_out_stream.PutULEB128(reg_num);
+ } else
+ m_out_stream.PutHex8(DW_OP_breg0 + reg_num);
+
+ m_out_stream.PutSLEB128(0);
+ ++m_stack_depth;
+}
+
+void DWARFCodegen::Visit(UnaryOpNode &unary, Node *&) {
+ Dispatch(unary.Operand());
+
+ switch (unary.GetOpType()) {
+ case UnaryOpNode::Deref:
+ m_out_stream.PutHex8(DW_OP_deref);
+ break;
+ }
+ // Stack depth unchanged.
+}
+
+bool postfix::ResolveSymbols(
+ Node *&node, llvm::function_ref<Node *(SymbolNode &)> replacer) {
+ return SymbolResolver(replacer).Dispatch(node);
+}
+
+void postfix::ToDWARF(Node &node, Stream &stream) {
+ Node *ptr = &node;
+ DWARFCodegen(stream).Dispatch(ptr);
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/Symbol.cpp b/contrib/llvm-project/lldb/source/Symbol/Symbol.cpp
new file mode 100644
index 000000000000..589f69244a48
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/Symbol.cpp
@@ -0,0 +1,570 @@
+//===-- Symbol.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/Symbol.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+Symbol::Symbol()
+ : SymbolContextScope(), m_uid(UINT32_MAX), m_type_data(0),
+ m_type_data_resolved(false), m_is_synthetic(false), m_is_debug(false),
+ m_is_external(false), m_size_is_sibling(false),
+ m_size_is_synthesized(false), m_size_is_valid(false),
+ m_demangled_is_synthesized(false), m_contains_linker_annotations(false),
+ m_is_weak(false), m_type(eSymbolTypeInvalid), m_mangled(), m_addr_range(),
+ m_flags() {}
+
+Symbol::Symbol(uint32_t symID, const char *name, bool name_is_mangled,
+ SymbolType type, bool external, bool is_debug,
+ bool is_trampoline, bool is_artificial,
+ const lldb::SectionSP &section_sp, addr_t offset, addr_t size,
+ bool size_is_valid, bool contains_linker_annotations,
+ uint32_t flags)
+ : SymbolContextScope(), m_uid(symID), m_type_data(0),
+ m_type_data_resolved(false), m_is_synthetic(is_artificial),
+ m_is_debug(is_debug), m_is_external(external), m_size_is_sibling(false),
+ m_size_is_synthesized(false), m_size_is_valid(size_is_valid || size > 0),
+ m_demangled_is_synthesized(false),
+ m_contains_linker_annotations(contains_linker_annotations),
+ m_is_weak(false), m_type(type),
+ m_mangled(ConstString(name), name_is_mangled),
+ m_addr_range(section_sp, offset, size), m_flags(flags) {}
+
+Symbol::Symbol(uint32_t symID, const Mangled &mangled, SymbolType type,
+ bool external, bool is_debug, bool is_trampoline,
+ bool is_artificial, const AddressRange &range,
+ bool size_is_valid, bool contains_linker_annotations,
+ uint32_t flags)
+ : SymbolContextScope(), m_uid(symID), m_type_data(0),
+ m_type_data_resolved(false), m_is_synthetic(is_artificial),
+ m_is_debug(is_debug), m_is_external(external), m_size_is_sibling(false),
+ m_size_is_synthesized(false),
+ m_size_is_valid(size_is_valid || range.GetByteSize() > 0),
+ m_demangled_is_synthesized(false),
+ m_contains_linker_annotations(contains_linker_annotations),
+ m_is_weak(false), m_type(type), m_mangled(mangled), m_addr_range(range),
+ m_flags(flags) {}
+
+Symbol::Symbol(const Symbol &rhs)
+ : SymbolContextScope(rhs), m_uid(rhs.m_uid), m_type_data(rhs.m_type_data),
+ m_type_data_resolved(rhs.m_type_data_resolved),
+ m_is_synthetic(rhs.m_is_synthetic), m_is_debug(rhs.m_is_debug),
+ m_is_external(rhs.m_is_external),
+ m_size_is_sibling(rhs.m_size_is_sibling), m_size_is_synthesized(false),
+ m_size_is_valid(rhs.m_size_is_valid),
+ m_demangled_is_synthesized(rhs.m_demangled_is_synthesized),
+ m_contains_linker_annotations(rhs.m_contains_linker_annotations),
+ m_is_weak(rhs.m_is_weak), m_type(rhs.m_type), m_mangled(rhs.m_mangled),
+ m_addr_range(rhs.m_addr_range), m_flags(rhs.m_flags) {}
+
+const Symbol &Symbol::operator=(const Symbol &rhs) {
+ if (this != &rhs) {
+ SymbolContextScope::operator=(rhs);
+ m_uid = rhs.m_uid;
+ m_type_data = rhs.m_type_data;
+ m_type_data_resolved = rhs.m_type_data_resolved;
+ m_is_synthetic = rhs.m_is_synthetic;
+ m_is_debug = rhs.m_is_debug;
+ m_is_external = rhs.m_is_external;
+ m_size_is_sibling = rhs.m_size_is_sibling;
+ m_size_is_synthesized = rhs.m_size_is_sibling;
+ m_size_is_valid = rhs.m_size_is_valid;
+ m_demangled_is_synthesized = rhs.m_demangled_is_synthesized;
+ m_contains_linker_annotations = rhs.m_contains_linker_annotations;
+ m_is_weak = rhs.m_is_weak;
+ m_type = rhs.m_type;
+ m_mangled = rhs.m_mangled;
+ m_addr_range = rhs.m_addr_range;
+ m_flags = rhs.m_flags;
+ }
+ return *this;
+}
+
+void Symbol::Clear() {
+ m_uid = UINT32_MAX;
+ m_mangled.Clear();
+ m_type_data = 0;
+ m_type_data_resolved = false;
+ m_is_synthetic = false;
+ m_is_debug = false;
+ m_is_external = false;
+ m_size_is_sibling = false;
+ m_size_is_synthesized = false;
+ m_size_is_valid = false;
+ m_demangled_is_synthesized = false;
+ m_contains_linker_annotations = false;
+ m_is_weak = false;
+ m_type = eSymbolTypeInvalid;
+ m_flags = 0;
+ m_addr_range.Clear();
+}
+
+bool Symbol::ValueIsAddress() const {
+ return m_addr_range.GetBaseAddress().GetSection().get() != nullptr;
+}
+
+ConstString Symbol::GetDisplayName() const {
+ return m_mangled.GetDisplayDemangledName(GetLanguage());
+}
+
+ConstString Symbol::GetReExportedSymbolName() const {
+ if (m_type == eSymbolTypeReExported) {
+ // For eSymbolTypeReExported, the "const char *" from a ConstString is used
+ // as the offset in the address range base address. We can then make this
+ // back into a string that is the re-exported name.
+ intptr_t str_ptr = m_addr_range.GetBaseAddress().GetOffset();
+ if (str_ptr != 0)
+ return ConstString((const char *)str_ptr);
+ else
+ return GetName();
+ }
+ return ConstString();
+}
+
+FileSpec Symbol::GetReExportedSymbolSharedLibrary() const {
+ if (m_type == eSymbolTypeReExported) {
+ // For eSymbolTypeReExported, the "const char *" from a ConstString is used
+ // as the offset in the address range base address. We can then make this
+ // back into a string that is the re-exported name.
+ intptr_t str_ptr = m_addr_range.GetByteSize();
+ if (str_ptr != 0)
+ return FileSpec((const char *)str_ptr);
+ }
+ return FileSpec();
+}
+
+void Symbol::SetReExportedSymbolName(ConstString name) {
+ SetType(eSymbolTypeReExported);
+ // For eSymbolTypeReExported, the "const char *" from a ConstString is used
+ // as the offset in the address range base address.
+ m_addr_range.GetBaseAddress().SetOffset((uintptr_t)name.GetCString());
+}
+
+bool Symbol::SetReExportedSymbolSharedLibrary(const FileSpec &fspec) {
+ if (m_type == eSymbolTypeReExported) {
+ // For eSymbolTypeReExported, the "const char *" from a ConstString is used
+ // as the offset in the address range base address.
+ m_addr_range.SetByteSize(
+ (uintptr_t)ConstString(fspec.GetPath().c_str()).GetCString());
+ return true;
+ }
+ return false;
+}
+
+uint32_t Symbol::GetSiblingIndex() const {
+ return m_size_is_sibling ? m_addr_range.GetByteSize() : UINT32_MAX;
+}
+
+bool Symbol::IsTrampoline() const { return m_type == eSymbolTypeTrampoline; }
+
+bool Symbol::IsIndirect() const { return m_type == eSymbolTypeResolver; }
+
+void Symbol::GetDescription(Stream *s, lldb::DescriptionLevel level,
+ Target *target) const {
+ s->Printf("id = {0x%8.8x}", m_uid);
+
+ if (m_addr_range.GetBaseAddress().GetSection()) {
+ if (ValueIsAddress()) {
+ const lldb::addr_t byte_size = GetByteSize();
+ if (byte_size > 0) {
+ s->PutCString(", range = ");
+ m_addr_range.Dump(s, target, Address::DumpStyleLoadAddress,
+ Address::DumpStyleFileAddress);
+ } else {
+ s->PutCString(", address = ");
+ m_addr_range.GetBaseAddress().Dump(s, target,
+ Address::DumpStyleLoadAddress,
+ Address::DumpStyleFileAddress);
+ }
+ } else
+ s->Printf(", value = 0x%16.16" PRIx64,
+ m_addr_range.GetBaseAddress().GetOffset());
+ } else {
+ if (m_size_is_sibling)
+ s->Printf(", sibling = %5" PRIu64,
+ m_addr_range.GetBaseAddress().GetOffset());
+ else
+ s->Printf(", value = 0x%16.16" PRIx64,
+ m_addr_range.GetBaseAddress().GetOffset());
+ }
+ ConstString demangled = m_mangled.GetDemangledName(GetLanguage());
+ if (demangled)
+ s->Printf(", name=\"%s\"", demangled.AsCString());
+ if (m_mangled.GetMangledName())
+ s->Printf(", mangled=\"%s\"", m_mangled.GetMangledName().AsCString());
+}
+
+void Symbol::Dump(Stream *s, Target *target, uint32_t index) const {
+ s->Printf("[%5u] %6u %c%c%c %-15s ", index, GetID(), m_is_debug ? 'D' : ' ',
+ m_is_synthetic ? 'S' : ' ', m_is_external ? 'X' : ' ',
+ GetTypeAsString());
+
+ // Make sure the size of the symbol is up to date before dumping
+ GetByteSize();
+
+ ConstString name = m_mangled.GetName(GetLanguage());
+ if (ValueIsAddress()) {
+ if (!m_addr_range.GetBaseAddress().Dump(s, nullptr,
+ Address::DumpStyleFileAddress))
+ s->Printf("%*s", 18, "");
+
+ s->PutChar(' ');
+
+ if (!m_addr_range.GetBaseAddress().Dump(s, target,
+ Address::DumpStyleLoadAddress))
+ s->Printf("%*s", 18, "");
+
+ const char *format = m_size_is_sibling ? " Sibling -> [%5llu] 0x%8.8x %s\n"
+ : " 0x%16.16" PRIx64 " 0x%8.8x %s\n";
+ s->Printf(format, GetByteSize(), m_flags, name.AsCString(""));
+ } else if (m_type == eSymbolTypeReExported) {
+ s->Printf(
+ " 0x%8.8x %s",
+ m_flags, name.AsCString(""));
+
+ ConstString reexport_name = GetReExportedSymbolName();
+ intptr_t shlib = m_addr_range.GetByteSize();
+ if (shlib)
+ s->Printf(" -> %s`%s\n", (const char *)shlib, reexport_name.GetCString());
+ else
+ s->Printf(" -> %s\n", reexport_name.GetCString());
+ } else {
+ const char *format =
+ m_size_is_sibling
+ ? "0x%16.16" PRIx64
+ " Sibling -> [%5llu] 0x%8.8x %s\n"
+ : "0x%16.16" PRIx64 " 0x%16.16" PRIx64
+ " 0x%8.8x %s\n";
+ s->Printf(format, m_addr_range.GetBaseAddress().GetOffset(), GetByteSize(),
+ m_flags, name.AsCString(""));
+ }
+}
+
+uint32_t Symbol::GetPrologueByteSize() {
+ if (m_type == eSymbolTypeCode || m_type == eSymbolTypeResolver) {
+ if (!m_type_data_resolved) {
+ m_type_data_resolved = true;
+
+ const Address &base_address = m_addr_range.GetBaseAddress();
+ Function *function = base_address.CalculateSymbolContextFunction();
+ if (function) {
+ // Functions have line entries which can also potentially have end of
+ // prologue information. So if this symbol points to a function, use
+ // the prologue information from there.
+ m_type_data = function->GetPrologueByteSize();
+ } else {
+ ModuleSP module_sp(base_address.GetModule());
+ SymbolContext sc;
+ if (module_sp) {
+ uint32_t resolved_flags = module_sp->ResolveSymbolContextForAddress(
+ base_address, eSymbolContextLineEntry, sc);
+ if (resolved_flags & eSymbolContextLineEntry) {
+ // Default to the end of the first line entry.
+ m_type_data = sc.line_entry.range.GetByteSize();
+
+ // Set address for next line.
+ Address addr(base_address);
+ addr.Slide(m_type_data);
+
+ // Check the first few instructions and look for one that has a
+ // line number that is different than the first entry. This is also
+ // done in Function::GetPrologueByteSize().
+ uint16_t total_offset = m_type_data;
+ for (int idx = 0; idx < 6; ++idx) {
+ SymbolContext sc_temp;
+ resolved_flags = module_sp->ResolveSymbolContextForAddress(
+ addr, eSymbolContextLineEntry, sc_temp);
+ // Make sure we got line number information...
+ if (!(resolved_flags & eSymbolContextLineEntry))
+ break;
+
+ // If this line number is different than our first one, use it
+ // and we're done.
+ if (sc_temp.line_entry.line != sc.line_entry.line) {
+ m_type_data = total_offset;
+ break;
+ }
+
+ // Slide addr up to the next line address.
+ addr.Slide(sc_temp.line_entry.range.GetByteSize());
+ total_offset += sc_temp.line_entry.range.GetByteSize();
+ // If we've gone too far, bail out.
+ if (total_offset >= m_addr_range.GetByteSize())
+ break;
+ }
+
+ // Sanity check - this may be a function in the middle of code that
+ // has debug information, but not for this symbol. So the line
+ // entries surrounding us won't lie inside our function. In that
+ // case, the line entry will be bigger than we are, so we do that
+ // quick check and if that is true, we just return 0.
+ if (m_type_data >= m_addr_range.GetByteSize())
+ m_type_data = 0;
+ } else {
+ // TODO: expose something in Process to figure out the
+ // size of a function prologue.
+ m_type_data = 0;
+ }
+ }
+ }
+ }
+ return m_type_data;
+ }
+ return 0;
+}
+
+bool Symbol::Compare(ConstString name, SymbolType type) const {
+ if (type == eSymbolTypeAny || m_type == type)
+ return m_mangled.GetMangledName() == name ||
+ m_mangled.GetDemangledName(GetLanguage()) == name;
+ return false;
+}
+
+#define ENUM_TO_CSTRING(x) \
+ case eSymbolType##x: \
+ return #x;
+
+const char *Symbol::GetTypeAsString() const {
+ switch (m_type) {
+ ENUM_TO_CSTRING(Invalid);
+ ENUM_TO_CSTRING(Absolute);
+ ENUM_TO_CSTRING(Code);
+ ENUM_TO_CSTRING(Resolver);
+ ENUM_TO_CSTRING(Data);
+ ENUM_TO_CSTRING(Trampoline);
+ ENUM_TO_CSTRING(Runtime);
+ ENUM_TO_CSTRING(Exception);
+ ENUM_TO_CSTRING(SourceFile);
+ ENUM_TO_CSTRING(HeaderFile);
+ ENUM_TO_CSTRING(ObjectFile);
+ ENUM_TO_CSTRING(CommonBlock);
+ ENUM_TO_CSTRING(Block);
+ ENUM_TO_CSTRING(Local);
+ ENUM_TO_CSTRING(Param);
+ ENUM_TO_CSTRING(Variable);
+ ENUM_TO_CSTRING(VariableType);
+ ENUM_TO_CSTRING(LineEntry);
+ ENUM_TO_CSTRING(LineHeader);
+ ENUM_TO_CSTRING(ScopeBegin);
+ ENUM_TO_CSTRING(ScopeEnd);
+ ENUM_TO_CSTRING(Additional);
+ ENUM_TO_CSTRING(Compiler);
+ ENUM_TO_CSTRING(Instrumentation);
+ ENUM_TO_CSTRING(Undefined);
+ ENUM_TO_CSTRING(ObjCClass);
+ ENUM_TO_CSTRING(ObjCMetaClass);
+ ENUM_TO_CSTRING(ObjCIVar);
+ ENUM_TO_CSTRING(ReExported);
+ default:
+ break;
+ }
+ return "<unknown SymbolType>";
+}
+
+void Symbol::CalculateSymbolContext(SymbolContext *sc) {
+ // Symbols can reconstruct the symbol and the module in the symbol context
+ sc->symbol = this;
+ if (ValueIsAddress())
+ sc->module_sp = GetAddressRef().GetModule();
+ else
+ sc->module_sp.reset();
+}
+
+ModuleSP Symbol::CalculateSymbolContextModule() {
+ if (ValueIsAddress())
+ return GetAddressRef().GetModule();
+ return ModuleSP();
+}
+
+Symbol *Symbol::CalculateSymbolContextSymbol() { return this; }
+
+void Symbol::DumpSymbolContext(Stream *s) {
+ bool dumped_module = false;
+ if (ValueIsAddress()) {
+ ModuleSP module_sp(GetAddressRef().GetModule());
+ if (module_sp) {
+ dumped_module = true;
+ module_sp->DumpSymbolContext(s);
+ }
+ }
+ if (dumped_module)
+ s->PutCString(", ");
+
+ s->Printf("Symbol{0x%8.8x}", GetID());
+}
+
+lldb::addr_t Symbol::GetByteSize() const { return m_addr_range.GetByteSize(); }
+
+Symbol *Symbol::ResolveReExportedSymbolInModuleSpec(
+ Target &target, ConstString &reexport_name, ModuleSpec &module_spec,
+ ModuleList &seen_modules) const {
+ ModuleSP module_sp;
+ if (module_spec.GetFileSpec()) {
+ // Try searching for the module file spec first using the full path
+ module_sp = target.GetImages().FindFirstModule(module_spec);
+ if (!module_sp) {
+ // Next try and find the module by basename in case environment variables
+ // or other runtime trickery causes shared libraries to be loaded from
+ // alternate paths
+ module_spec.GetFileSpec().GetDirectory().Clear();
+ module_sp = target.GetImages().FindFirstModule(module_spec);
+ }
+ }
+
+ if (module_sp) {
+ // There should not be cycles in the reexport list, but we don't want to
+ // crash if there are so make sure we haven't seen this before:
+ if (!seen_modules.AppendIfNeeded(module_sp))
+ return nullptr;
+
+ lldb_private::SymbolContextList sc_list;
+ module_sp->FindSymbolsWithNameAndType(reexport_name, eSymbolTypeAny,
+ sc_list);
+ const size_t num_scs = sc_list.GetSize();
+ if (num_scs > 0) {
+ for (size_t i = 0; i < num_scs; ++i) {
+ lldb_private::SymbolContext sc;
+ if (sc_list.GetContextAtIndex(i, sc)) {
+ if (sc.symbol->IsExternal())
+ return sc.symbol;
+ }
+ }
+ }
+ // If we didn't find the symbol in this module, it may be because this
+ // module re-exports some whole other library. We have to search those as
+ // well:
+ seen_modules.Append(module_sp);
+
+ FileSpecList reexported_libraries =
+ module_sp->GetObjectFile()->GetReExportedLibraries();
+ size_t num_reexported_libraries = reexported_libraries.GetSize();
+ for (size_t idx = 0; idx < num_reexported_libraries; idx++) {
+ ModuleSpec reexported_module_spec;
+ reexported_module_spec.GetFileSpec() =
+ reexported_libraries.GetFileSpecAtIndex(idx);
+ Symbol *result_symbol = ResolveReExportedSymbolInModuleSpec(
+ target, reexport_name, reexported_module_spec, seen_modules);
+ if (result_symbol)
+ return result_symbol;
+ }
+ }
+ return nullptr;
+}
+
+Symbol *Symbol::ResolveReExportedSymbol(Target &target) const {
+ ConstString reexport_name(GetReExportedSymbolName());
+ if (reexport_name) {
+ ModuleSpec module_spec;
+ ModuleList seen_modules;
+ module_spec.GetFileSpec() = GetReExportedSymbolSharedLibrary();
+ if (module_spec.GetFileSpec()) {
+ return ResolveReExportedSymbolInModuleSpec(target, reexport_name,
+ module_spec, seen_modules);
+ }
+ }
+ return nullptr;
+}
+
+lldb::addr_t Symbol::GetFileAddress() const {
+ if (ValueIsAddress())
+ return GetAddressRef().GetFileAddress();
+ else
+ return LLDB_INVALID_ADDRESS;
+}
+
+lldb::addr_t Symbol::GetLoadAddress(Target *target) const {
+ if (ValueIsAddress())
+ return GetAddressRef().GetLoadAddress(target);
+ else
+ return LLDB_INVALID_ADDRESS;
+}
+
+ConstString Symbol::GetName() const { return m_mangled.GetName(GetLanguage()); }
+
+ConstString Symbol::GetNameNoArguments() const {
+ return m_mangled.GetName(GetLanguage(),
+ Mangled::ePreferDemangledWithoutArguments);
+}
+
+lldb::addr_t Symbol::ResolveCallableAddress(Target &target) const {
+ if (GetType() == lldb::eSymbolTypeUndefined)
+ return LLDB_INVALID_ADDRESS;
+
+ Address func_so_addr;
+
+ bool is_indirect = IsIndirect();
+ if (GetType() == eSymbolTypeReExported) {
+ Symbol *reexported_symbol = ResolveReExportedSymbol(target);
+ if (reexported_symbol) {
+ func_so_addr = reexported_symbol->GetAddress();
+ is_indirect = reexported_symbol->IsIndirect();
+ }
+ } else {
+ func_so_addr = GetAddress();
+ is_indirect = IsIndirect();
+ }
+
+ if (func_so_addr.IsValid()) {
+ if (!target.GetProcessSP() && is_indirect) {
+ // can't resolve indirect symbols without calling a function...
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ lldb::addr_t load_addr =
+ func_so_addr.GetCallableLoadAddress(&target, is_indirect);
+
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ return load_addr;
+ }
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+lldb::DisassemblerSP Symbol::GetInstructions(const ExecutionContext &exe_ctx,
+ const char *flavor,
+ bool prefer_file_cache) {
+ ModuleSP module_sp(m_addr_range.GetBaseAddress().GetModule());
+ if (module_sp) {
+ const bool prefer_file_cache = false;
+ return Disassembler::DisassembleRange(module_sp->GetArchitecture(), nullptr,
+ flavor, exe_ctx, m_addr_range,
+ prefer_file_cache);
+ }
+ return lldb::DisassemblerSP();
+}
+
+bool Symbol::GetDisassembly(const ExecutionContext &exe_ctx, const char *flavor,
+ bool prefer_file_cache, Stream &strm) {
+ lldb::DisassemblerSP disassembler_sp =
+ GetInstructions(exe_ctx, flavor, prefer_file_cache);
+ if (disassembler_sp) {
+ const bool show_address = true;
+ const bool show_bytes = false;
+ disassembler_sp->GetInstructionList().Dump(&strm, show_address, show_bytes,
+ &exe_ctx);
+ return true;
+ }
+ return false;
+}
+
+bool Symbol::ContainsFileAddress(lldb::addr_t file_addr) const {
+ return m_addr_range.ContainsFileAddress(file_addr);
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/SymbolContext.cpp b/contrib/llvm-project/lldb/source/Symbol/SymbolContext.cpp
new file mode 100644
index 000000000000..a0b35cf3d0b9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/SymbolContext.cpp
@@ -0,0 +1,1336 @@
+//===-- SymbolContext.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/SymbolContext.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SymbolContext::SymbolContext()
+ : target_sp(), module_sp(), comp_unit(nullptr), function(nullptr),
+ block(nullptr), line_entry(), symbol(nullptr), variable(nullptr) {}
+
+SymbolContext::SymbolContext(const ModuleSP &m, CompileUnit *cu, Function *f,
+ Block *b, LineEntry *le, Symbol *s)
+ : target_sp(), module_sp(m), comp_unit(cu), function(f), block(b),
+ line_entry(), symbol(s), variable(nullptr) {
+ if (le)
+ line_entry = *le;
+}
+
+SymbolContext::SymbolContext(const TargetSP &t, const ModuleSP &m,
+ CompileUnit *cu, Function *f, Block *b,
+ LineEntry *le, Symbol *s)
+ : target_sp(t), module_sp(m), comp_unit(cu), function(f), block(b),
+ line_entry(), symbol(s), variable(nullptr) {
+ if (le)
+ line_entry = *le;
+}
+
+SymbolContext::SymbolContext(SymbolContextScope *sc_scope)
+ : target_sp(), module_sp(), comp_unit(nullptr), function(nullptr),
+ block(nullptr), line_entry(), symbol(nullptr), variable(nullptr) {
+ sc_scope->CalculateSymbolContext(this);
+}
+
+SymbolContext::~SymbolContext() {}
+
+const SymbolContext &SymbolContext::operator=(const SymbolContext &rhs) {
+ if (this != &rhs) {
+ target_sp = rhs.target_sp;
+ module_sp = rhs.module_sp;
+ comp_unit = rhs.comp_unit;
+ function = rhs.function;
+ block = rhs.block;
+ line_entry = rhs.line_entry;
+ symbol = rhs.symbol;
+ variable = rhs.variable;
+ }
+ return *this;
+}
+
+void SymbolContext::Clear(bool clear_target) {
+ if (clear_target)
+ target_sp.reset();
+ module_sp.reset();
+ comp_unit = nullptr;
+ function = nullptr;
+ block = nullptr;
+ line_entry.Clear();
+ symbol = nullptr;
+ variable = nullptr;
+}
+
+bool SymbolContext::DumpStopContext(Stream *s, ExecutionContextScope *exe_scope,
+ const Address &addr, bool show_fullpaths,
+ bool show_module, bool show_inlined_frames,
+ bool show_function_arguments,
+ bool show_function_name) const {
+ bool dumped_something = false;
+ if (show_module && module_sp) {
+ if (show_fullpaths)
+ *s << module_sp->GetFileSpec();
+ else
+ *s << module_sp->GetFileSpec().GetFilename();
+ s->PutChar('`');
+ dumped_something = true;
+ }
+
+ if (function != nullptr) {
+ SymbolContext inline_parent_sc;
+ Address inline_parent_addr;
+ if (!show_function_name) {
+ s->Printf("<");
+ dumped_something = true;
+ } else {
+ ConstString name;
+ if (!show_function_arguments)
+ name = function->GetNameNoArguments();
+ if (!name)
+ name = function->GetName();
+ if (name)
+ name.Dump(s);
+ }
+
+ if (addr.IsValid()) {
+ const addr_t function_offset =
+ addr.GetOffset() -
+ function->GetAddressRange().GetBaseAddress().GetOffset();
+ if (!show_function_name) {
+ // Print +offset even if offset is 0
+ dumped_something = true;
+ s->Printf("+%" PRIu64 ">", function_offset);
+ } else if (function_offset) {
+ dumped_something = true;
+ s->Printf(" + %" PRIu64, function_offset);
+ }
+ }
+
+ if (GetParentOfInlinedScope(addr, inline_parent_sc, inline_parent_addr)) {
+ dumped_something = true;
+ Block *inlined_block = block->GetContainingInlinedBlock();
+ const InlineFunctionInfo *inlined_block_info =
+ inlined_block->GetInlinedFunctionInfo();
+ s->Printf(
+ " [inlined] %s",
+ inlined_block_info->GetName(function->GetLanguage()).GetCString());
+
+ lldb_private::AddressRange block_range;
+ if (inlined_block->GetRangeContainingAddress(addr, block_range)) {
+ const addr_t inlined_function_offset =
+ addr.GetOffset() - block_range.GetBaseAddress().GetOffset();
+ if (inlined_function_offset) {
+ s->Printf(" + %" PRIu64, inlined_function_offset);
+ }
+ }
+ const Declaration &call_site = inlined_block_info->GetCallSite();
+ if (call_site.IsValid()) {
+ s->PutCString(" at ");
+ call_site.DumpStopContext(s, show_fullpaths);
+ }
+ if (show_inlined_frames) {
+ s->EOL();
+ s->Indent();
+ const bool show_function_name = true;
+ return inline_parent_sc.DumpStopContext(
+ s, exe_scope, inline_parent_addr, show_fullpaths, show_module,
+ show_inlined_frames, show_function_arguments, show_function_name);
+ }
+ } else {
+ if (line_entry.IsValid()) {
+ dumped_something = true;
+ s->PutCString(" at ");
+ if (line_entry.DumpStopContext(s, show_fullpaths))
+ dumped_something = true;
+ }
+ }
+ } else if (symbol != nullptr) {
+ if (!show_function_name) {
+ s->Printf("<");
+ dumped_something = true;
+ } else if (symbol->GetName()) {
+ dumped_something = true;
+ if (symbol->GetType() == eSymbolTypeTrampoline)
+ s->PutCString("symbol stub for: ");
+ symbol->GetName().Dump(s);
+ }
+
+ if (addr.IsValid() && symbol->ValueIsAddress()) {
+ const addr_t symbol_offset =
+ addr.GetOffset() - symbol->GetAddressRef().GetOffset();
+ if (!show_function_name) {
+ // Print +offset even if offset is 0
+ dumped_something = true;
+ s->Printf("+%" PRIu64 ">", symbol_offset);
+ } else if (symbol_offset) {
+ dumped_something = true;
+ s->Printf(" + %" PRIu64, symbol_offset);
+ }
+ }
+ } else if (addr.IsValid()) {
+ addr.Dump(s, exe_scope, Address::DumpStyleModuleWithFileAddress);
+ dumped_something = true;
+ }
+ return dumped_something;
+}
+
+void SymbolContext::GetDescription(Stream *s, lldb::DescriptionLevel level,
+ Target *target) const {
+ if (module_sp) {
+ s->Indent(" Module: file = \"");
+ module_sp->GetFileSpec().Dump(s);
+ *s << '"';
+ if (module_sp->GetArchitecture().IsValid())
+ s->Printf(", arch = \"%s\"",
+ module_sp->GetArchitecture().GetArchitectureName());
+ s->EOL();
+ }
+
+ if (comp_unit != nullptr) {
+ s->Indent("CompileUnit: ");
+ comp_unit->GetDescription(s, level);
+ s->EOL();
+ }
+
+ if (function != nullptr) {
+ s->Indent(" Function: ");
+ function->GetDescription(s, level, target);
+ s->EOL();
+
+ Type *func_type = function->GetType();
+ if (func_type) {
+ s->Indent(" FuncType: ");
+ func_type->GetDescription(s, level, false);
+ s->EOL();
+ }
+ }
+
+ if (block != nullptr) {
+ std::vector<Block *> blocks;
+ blocks.push_back(block);
+ Block *parent_block = block->GetParent();
+
+ while (parent_block) {
+ blocks.push_back(parent_block);
+ parent_block = parent_block->GetParent();
+ }
+ std::vector<Block *>::reverse_iterator pos;
+ std::vector<Block *>::reverse_iterator begin = blocks.rbegin();
+ std::vector<Block *>::reverse_iterator end = blocks.rend();
+ for (pos = begin; pos != end; ++pos) {
+ if (pos == begin)
+ s->Indent(" Blocks: ");
+ else
+ s->Indent(" ");
+ (*pos)->GetDescription(s, function, level, target);
+ s->EOL();
+ }
+ }
+
+ if (line_entry.IsValid()) {
+ s->Indent(" LineEntry: ");
+ line_entry.GetDescription(s, level, comp_unit, target, false);
+ s->EOL();
+ }
+
+ if (symbol != nullptr) {
+ s->Indent(" Symbol: ");
+ symbol->GetDescription(s, level, target);
+ s->EOL();
+ }
+
+ if (variable != nullptr) {
+ s->Indent(" Variable: ");
+
+ s->Printf("id = {0x%8.8" PRIx64 "}, ", variable->GetID());
+
+ switch (variable->GetScope()) {
+ case eValueTypeVariableGlobal:
+ s->PutCString("kind = global, ");
+ break;
+
+ case eValueTypeVariableStatic:
+ s->PutCString("kind = static, ");
+ break;
+
+ case eValueTypeVariableArgument:
+ s->PutCString("kind = argument, ");
+ break;
+
+ case eValueTypeVariableLocal:
+ s->PutCString("kind = local, ");
+ break;
+
+ case eValueTypeVariableThreadLocal:
+ s->PutCString("kind = thread local, ");
+ break;
+
+ default:
+ break;
+ }
+
+ s->Printf("name = \"%s\"\n", variable->GetName().GetCString());
+ }
+}
+
+uint32_t SymbolContext::GetResolvedMask() const {
+ uint32_t resolved_mask = 0;
+ if (target_sp)
+ resolved_mask |= eSymbolContextTarget;
+ if (module_sp)
+ resolved_mask |= eSymbolContextModule;
+ if (comp_unit)
+ resolved_mask |= eSymbolContextCompUnit;
+ if (function)
+ resolved_mask |= eSymbolContextFunction;
+ if (block)
+ resolved_mask |= eSymbolContextBlock;
+ if (line_entry.IsValid())
+ resolved_mask |= eSymbolContextLineEntry;
+ if (symbol)
+ resolved_mask |= eSymbolContextSymbol;
+ if (variable)
+ resolved_mask |= eSymbolContextVariable;
+ return resolved_mask;
+}
+
+void SymbolContext::Dump(Stream *s, Target *target) const {
+ *s << this << ": ";
+ s->Indent();
+ s->PutCString("SymbolContext");
+ s->IndentMore();
+ s->EOL();
+ s->IndentMore();
+ s->Indent();
+ *s << "Module = " << module_sp.get() << ' ';
+ if (module_sp)
+ module_sp->GetFileSpec().Dump(s);
+ s->EOL();
+ s->Indent();
+ *s << "CompileUnit = " << comp_unit;
+ if (comp_unit != nullptr)
+ *s << " {0x" << comp_unit->GetID() << "} "
+ << *(static_cast<FileSpec *>(comp_unit));
+ s->EOL();
+ s->Indent();
+ *s << "Function = " << function;
+ if (function != nullptr) {
+ *s << " {0x" << function->GetID() << "} " << function->GetType()->GetName()
+ << ", address-range = ";
+ function->GetAddressRange().Dump(s, target, Address::DumpStyleLoadAddress,
+ Address::DumpStyleModuleWithFileAddress);
+ s->EOL();
+ s->Indent();
+ Type *func_type = function->GetType();
+ if (func_type) {
+ *s << " Type = ";
+ func_type->Dump(s, false);
+ }
+ }
+ s->EOL();
+ s->Indent();
+ *s << "Block = " << block;
+ if (block != nullptr)
+ *s << " {0x" << block->GetID() << '}';
+ // Dump the block and pass it a negative depth to we print all the parent
+ // blocks if (block != NULL)
+ // block->Dump(s, function->GetFileAddress(), INT_MIN);
+ s->EOL();
+ s->Indent();
+ *s << "LineEntry = ";
+ line_entry.Dump(s, target, true, Address::DumpStyleLoadAddress,
+ Address::DumpStyleModuleWithFileAddress, true);
+ s->EOL();
+ s->Indent();
+ *s << "Symbol = " << symbol;
+ if (symbol != nullptr && symbol->GetMangled())
+ *s << ' ' << symbol->GetName().AsCString();
+ s->EOL();
+ *s << "Variable = " << variable;
+ if (variable != nullptr) {
+ *s << " {0x" << variable->GetID() << "} " << variable->GetType()->GetName();
+ s->EOL();
+ }
+ s->IndentLess();
+ s->IndentLess();
+}
+
+bool lldb_private::operator==(const SymbolContext &lhs,
+ const SymbolContext &rhs) {
+ return lhs.function == rhs.function && lhs.symbol == rhs.symbol &&
+ lhs.module_sp.get() == rhs.module_sp.get() &&
+ lhs.comp_unit == rhs.comp_unit &&
+ lhs.target_sp.get() == rhs.target_sp.get() &&
+ LineEntry::Compare(lhs.line_entry, rhs.line_entry) == 0 &&
+ lhs.variable == rhs.variable;
+}
+
+bool lldb_private::operator!=(const SymbolContext &lhs,
+ const SymbolContext &rhs) {
+ return !(lhs == rhs);
+}
+
+bool SymbolContext::GetAddressRange(uint32_t scope, uint32_t range_idx,
+ bool use_inline_block_range,
+ AddressRange &range) const {
+ if ((scope & eSymbolContextLineEntry) && line_entry.IsValid()) {
+ range = line_entry.range;
+ return true;
+ }
+
+ if ((scope & eSymbolContextBlock) && (block != nullptr)) {
+ if (use_inline_block_range) {
+ Block *inline_block = block->GetContainingInlinedBlock();
+ if (inline_block)
+ return inline_block->GetRangeAtIndex(range_idx, range);
+ } else {
+ return block->GetRangeAtIndex(range_idx, range);
+ }
+ }
+
+ if ((scope & eSymbolContextFunction) && (function != nullptr)) {
+ if (range_idx == 0) {
+ range = function->GetAddressRange();
+ return true;
+ }
+ }
+
+ if ((scope & eSymbolContextSymbol) && (symbol != nullptr)) {
+ if (range_idx == 0) {
+ if (symbol->ValueIsAddress()) {
+ range.GetBaseAddress() = symbol->GetAddressRef();
+ range.SetByteSize(symbol->GetByteSize());
+ return true;
+ }
+ }
+ }
+ range.Clear();
+ return false;
+}
+
+LanguageType SymbolContext::GetLanguage() const {
+ LanguageType lang;
+ if (function && (lang = function->GetLanguage()) != eLanguageTypeUnknown) {
+ return lang;
+ } else if (variable &&
+ (lang = variable->GetLanguage()) != eLanguageTypeUnknown) {
+ return lang;
+ } else if (symbol && (lang = symbol->GetLanguage()) != eLanguageTypeUnknown) {
+ return lang;
+ } else if (comp_unit &&
+ (lang = comp_unit->GetLanguage()) != eLanguageTypeUnknown) {
+ return lang;
+ } else if (symbol) {
+ // If all else fails, try to guess the language from the name.
+ return symbol->GetMangled().GuessLanguage();
+ }
+ return eLanguageTypeUnknown;
+}
+
+bool SymbolContext::GetParentOfInlinedScope(const Address &curr_frame_pc,
+ SymbolContext &next_frame_sc,
+ Address &next_frame_pc) const {
+ next_frame_sc.Clear(false);
+ next_frame_pc.Clear();
+
+ if (block) {
+ // const addr_t curr_frame_file_addr = curr_frame_pc.GetFileAddress();
+
+ // In order to get the parent of an inlined function we first need to see
+ // if we are in an inlined block as "this->block" could be an inlined
+ // block, or a parent of "block" could be. So lets check if this block or
+ // one of this blocks parents is an inlined function.
+ Block *curr_inlined_block = block->GetContainingInlinedBlock();
+ if (curr_inlined_block) {
+ // "this->block" is contained in an inline function block, so to get the
+ // scope above the inlined block, we get the parent of the inlined block
+ // itself
+ Block *next_frame_block = curr_inlined_block->GetParent();
+ // Now calculate the symbol context of the containing block
+ next_frame_block->CalculateSymbolContext(&next_frame_sc);
+
+ // If we get here we weren't able to find the return line entry using the
+ // nesting of the blocks and the line table. So just use the call site
+ // info from our inlined block.
+
+ AddressRange range;
+ if (curr_inlined_block->GetRangeContainingAddress(curr_frame_pc, range)) {
+ // To see there this new frame block it, we need to look at the call
+ // site information from
+ const InlineFunctionInfo *curr_inlined_block_inlined_info =
+ curr_inlined_block->GetInlinedFunctionInfo();
+ next_frame_pc = range.GetBaseAddress();
+ next_frame_sc.line_entry.range.GetBaseAddress() = next_frame_pc;
+ next_frame_sc.line_entry.file =
+ curr_inlined_block_inlined_info->GetCallSite().GetFile();
+ next_frame_sc.line_entry.original_file =
+ curr_inlined_block_inlined_info->GetCallSite().GetFile();
+ next_frame_sc.line_entry.line =
+ curr_inlined_block_inlined_info->GetCallSite().GetLine();
+ next_frame_sc.line_entry.column =
+ curr_inlined_block_inlined_info->GetCallSite().GetColumn();
+ return true;
+ } else {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS));
+
+ if (log) {
+ log->Printf(
+ "warning: inlined block 0x%8.8" PRIx64
+ " doesn't have a range that contains file address 0x%" PRIx64,
+ curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress());
+ }
+#ifdef LLDB_CONFIGURATION_DEBUG
+ else {
+ ObjectFile *objfile = nullptr;
+ if (module_sp) {
+ SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor();
+ if (symbol_vendor) {
+ SymbolFile *symbol_file = symbol_vendor->GetSymbolFile();
+ if (symbol_file)
+ objfile = symbol_file->GetObjectFile();
+ }
+ }
+ if (objfile) {
+ Host::SystemLog(
+ Host::eSystemLogWarning,
+ "warning: inlined block 0x%8.8" PRIx64
+ " doesn't have a range that contains file address 0x%" PRIx64
+ " in %s\n",
+ curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress(),
+ objfile->GetFileSpec().GetPath().c_str());
+ } else {
+ Host::SystemLog(
+ Host::eSystemLogWarning,
+ "warning: inlined block 0x%8.8" PRIx64
+ " doesn't have a range that contains file address 0x%" PRIx64
+ "\n",
+ curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress());
+ }
+ }
+#endif
+ }
+ }
+ }
+
+ return false;
+}
+
+Block *SymbolContext::GetFunctionBlock() {
+ if (function) {
+ if (block) {
+ // If this symbol context has a block, check to see if this block is
+ // itself, or is contained within a block with inlined function
+ // information. If so, then the inlined block is the block that defines
+ // the function.
+ Block *inlined_block = block->GetContainingInlinedBlock();
+ if (inlined_block)
+ return inlined_block;
+
+ // The block in this symbol context is not inside an inlined block, so
+ // the block that defines the function is the function's top level block,
+ // which is returned below.
+ }
+
+ // There is no block information in this symbol context, so we must assume
+ // that the block that is desired is the top level block of the function
+ // itself.
+ return &function->GetBlock(true);
+ }
+ return nullptr;
+}
+
+bool SymbolContext::GetFunctionMethodInfo(lldb::LanguageType &language,
+ bool &is_instance_method,
+ ConstString &language_object_name)
+
+{
+ Block *function_block = GetFunctionBlock();
+ if (function_block) {
+ CompilerDeclContext decl_ctx = function_block->GetDeclContext();
+ if (decl_ctx)
+ return decl_ctx.IsClassMethod(&language, &is_instance_method,
+ &language_object_name);
+ }
+ return false;
+}
+
+void SymbolContext::SortTypeList(TypeMap &type_map, TypeList &type_list) const {
+ Block *curr_block = block;
+ bool isInlinedblock = false;
+ if (curr_block != nullptr &&
+ curr_block->GetContainingInlinedBlock() != nullptr)
+ isInlinedblock = true;
+
+ // Find all types that match the current block if we have one and put them
+ // first in the list. Keep iterating up through all blocks.
+ while (curr_block != nullptr && !isInlinedblock) {
+ type_map.ForEach(
+ [curr_block, &type_list](const lldb::TypeSP &type_sp) -> bool {
+ SymbolContextScope *scs = type_sp->GetSymbolContextScope();
+ if (scs && curr_block == scs->CalculateSymbolContextBlock())
+ type_list.Insert(type_sp);
+ return true; // Keep iterating
+ });
+
+ // Remove any entries that are now in "type_list" from "type_map" since we
+ // can't remove from type_map while iterating
+ type_list.ForEach([&type_map](const lldb::TypeSP &type_sp) -> bool {
+ type_map.Remove(type_sp);
+ return true; // Keep iterating
+ });
+ curr_block = curr_block->GetParent();
+ }
+ // Find all types that match the current function, if we have onem, and put
+ // them next in the list.
+ if (function != nullptr && !type_map.Empty()) {
+ const size_t old_type_list_size = type_list.GetSize();
+ type_map.ForEach([this, &type_list](const lldb::TypeSP &type_sp) -> bool {
+ SymbolContextScope *scs = type_sp->GetSymbolContextScope();
+ if (scs && function == scs->CalculateSymbolContextFunction())
+ type_list.Insert(type_sp);
+ return true; // Keep iterating
+ });
+
+ // Remove any entries that are now in "type_list" from "type_map" since we
+ // can't remove from type_map while iterating
+ const size_t new_type_list_size = type_list.GetSize();
+ if (new_type_list_size > old_type_list_size) {
+ for (size_t i = old_type_list_size; i < new_type_list_size; ++i)
+ type_map.Remove(type_list.GetTypeAtIndex(i));
+ }
+ }
+ // Find all types that match the current compile unit, if we have one, and
+ // put them next in the list.
+ if (comp_unit != nullptr && !type_map.Empty()) {
+ const size_t old_type_list_size = type_list.GetSize();
+
+ type_map.ForEach([this, &type_list](const lldb::TypeSP &type_sp) -> bool {
+ SymbolContextScope *scs = type_sp->GetSymbolContextScope();
+ if (scs && comp_unit == scs->CalculateSymbolContextCompileUnit())
+ type_list.Insert(type_sp);
+ return true; // Keep iterating
+ });
+
+ // Remove any entries that are now in "type_list" from "type_map" since we
+ // can't remove from type_map while iterating
+ const size_t new_type_list_size = type_list.GetSize();
+ if (new_type_list_size > old_type_list_size) {
+ for (size_t i = old_type_list_size; i < new_type_list_size; ++i)
+ type_map.Remove(type_list.GetTypeAtIndex(i));
+ }
+ }
+ // Find all types that match the current module, if we have one, and put them
+ // next in the list.
+ if (module_sp && !type_map.Empty()) {
+ const size_t old_type_list_size = type_list.GetSize();
+ type_map.ForEach([this, &type_list](const lldb::TypeSP &type_sp) -> bool {
+ SymbolContextScope *scs = type_sp->GetSymbolContextScope();
+ if (scs && module_sp == scs->CalculateSymbolContextModule())
+ type_list.Insert(type_sp);
+ return true; // Keep iterating
+ });
+ // Remove any entries that are now in "type_list" from "type_map" since we
+ // can't remove from type_map while iterating
+ const size_t new_type_list_size = type_list.GetSize();
+ if (new_type_list_size > old_type_list_size) {
+ for (size_t i = old_type_list_size; i < new_type_list_size; ++i)
+ type_map.Remove(type_list.GetTypeAtIndex(i));
+ }
+ }
+ // Any types that are left get copied into the list an any order.
+ if (!type_map.Empty()) {
+ type_map.ForEach([&type_list](const lldb::TypeSP &type_sp) -> bool {
+ type_list.Insert(type_sp);
+ return true; // Keep iterating
+ });
+ }
+}
+
+ConstString
+SymbolContext::GetFunctionName(Mangled::NamePreference preference) const {
+ if (function) {
+ if (block) {
+ Block *inlined_block = block->GetContainingInlinedBlock();
+
+ if (inlined_block) {
+ const InlineFunctionInfo *inline_info =
+ inlined_block->GetInlinedFunctionInfo();
+ if (inline_info)
+ return inline_info->GetName(function->GetLanguage());
+ }
+ }
+ return function->GetMangled().GetName(function->GetLanguage(), preference);
+ } else if (symbol && symbol->ValueIsAddress()) {
+ return symbol->GetMangled().GetName(symbol->GetLanguage(), preference);
+ } else {
+ // No function, return an empty string.
+ return ConstString();
+ }
+}
+
+LineEntry SymbolContext::GetFunctionStartLineEntry() const {
+ LineEntry line_entry;
+ Address start_addr;
+ if (block) {
+ Block *inlined_block = block->GetContainingInlinedBlock();
+ if (inlined_block) {
+ if (inlined_block->GetStartAddress(start_addr)) {
+ if (start_addr.CalculateSymbolContextLineEntry(line_entry))
+ return line_entry;
+ }
+ return LineEntry();
+ }
+ }
+
+ if (function) {
+ if (function->GetAddressRange()
+ .GetBaseAddress()
+ .CalculateSymbolContextLineEntry(line_entry))
+ return line_entry;
+ }
+ return LineEntry();
+}
+
+bool SymbolContext::GetAddressRangeFromHereToEndLine(uint32_t end_line,
+ AddressRange &range,
+ Status &error) {
+ if (!line_entry.IsValid()) {
+ error.SetErrorString("Symbol context has no line table.");
+ return false;
+ }
+
+ range = line_entry.range;
+ if (line_entry.line > end_line) {
+ error.SetErrorStringWithFormat(
+ "end line option %d must be after the current line: %d", end_line,
+ line_entry.line);
+ return false;
+ }
+
+ uint32_t line_index = 0;
+ bool found = false;
+ while (true) {
+ LineEntry this_line;
+ line_index = comp_unit->FindLineEntry(line_index, line_entry.line, nullptr,
+ false, &this_line);
+ if (line_index == UINT32_MAX)
+ break;
+ if (LineEntry::Compare(this_line, line_entry) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ LineEntry end_entry;
+ if (!found) {
+ // Can't find the index of the SymbolContext's line entry in the
+ // SymbolContext's CompUnit.
+ error.SetErrorString(
+ "Can't find the current line entry in the CompUnit - can't process "
+ "the end-line option");
+ return false;
+ }
+
+ line_index = comp_unit->FindLineEntry(line_index, end_line, nullptr, false,
+ &end_entry);
+ if (line_index == UINT32_MAX) {
+ error.SetErrorStringWithFormat(
+ "could not find a line table entry corresponding "
+ "to end line number %d",
+ end_line);
+ return false;
+ }
+
+ Block *func_block = GetFunctionBlock();
+ if (func_block &&
+ func_block->GetRangeIndexContainingAddress(
+ end_entry.range.GetBaseAddress()) == UINT32_MAX) {
+ error.SetErrorStringWithFormat(
+ "end line number %d is not contained within the current function.",
+ end_line);
+ return false;
+ }
+
+ lldb::addr_t range_size = end_entry.range.GetBaseAddress().GetFileAddress() -
+ range.GetBaseAddress().GetFileAddress();
+ range.SetByteSize(range_size);
+ return true;
+}
+
+const Symbol *
+SymbolContext::FindBestGlobalDataSymbol(ConstString name, Status &error) {
+ error.Clear();
+
+ if (!target_sp) {
+ return nullptr;
+ }
+
+ Target &target = *target_sp;
+ Module *module = module_sp.get();
+
+ auto ProcessMatches = [this, &name, &target, module]
+ (SymbolContextList &sc_list, Status &error) -> const Symbol* {
+ llvm::SmallVector<const Symbol *, 1> external_symbols;
+ llvm::SmallVector<const Symbol *, 1> internal_symbols;
+ const uint32_t matches = sc_list.GetSize();
+ for (uint32_t i = 0; i < matches; ++i) {
+ SymbolContext sym_ctx;
+ sc_list.GetContextAtIndex(i, sym_ctx);
+ if (sym_ctx.symbol) {
+ const Symbol *symbol = sym_ctx.symbol;
+ const Address sym_address = symbol->GetAddress();
+
+ if (sym_address.IsValid()) {
+ switch (symbol->GetType()) {
+ case eSymbolTypeData:
+ case eSymbolTypeRuntime:
+ case eSymbolTypeAbsolute:
+ case eSymbolTypeObjCClass:
+ case eSymbolTypeObjCMetaClass:
+ case eSymbolTypeObjCIVar:
+ if (symbol->GetDemangledNameIsSynthesized()) {
+ // If the demangled name was synthesized, then don't use it for
+ // expressions. Only let the symbol match if the mangled named
+ // matches for these symbols.
+ if (symbol->GetMangled().GetMangledName() != name)
+ break;
+ }
+ if (symbol->IsExternal()) {
+ external_symbols.push_back(symbol);
+ } else {
+ internal_symbols.push_back(symbol);
+ }
+ break;
+ case eSymbolTypeReExported: {
+ ConstString reexport_name = symbol->GetReExportedSymbolName();
+ if (reexport_name) {
+ ModuleSP reexport_module_sp;
+ ModuleSpec reexport_module_spec;
+ reexport_module_spec.GetPlatformFileSpec() =
+ symbol->GetReExportedSymbolSharedLibrary();
+ if (reexport_module_spec.GetPlatformFileSpec()) {
+ reexport_module_sp =
+ target.GetImages().FindFirstModule(reexport_module_spec);
+ if (!reexport_module_sp) {
+ reexport_module_spec.GetPlatformFileSpec()
+ .GetDirectory()
+ .Clear();
+ reexport_module_sp =
+ target.GetImages().FindFirstModule(reexport_module_spec);
+ }
+ }
+ // Don't allow us to try and resolve a re-exported symbol if it
+ // is the same as the current symbol
+ if (name == symbol->GetReExportedSymbolName() &&
+ module == reexport_module_sp.get())
+ return nullptr;
+
+ return FindBestGlobalDataSymbol(
+ symbol->GetReExportedSymbolName(), error);
+ }
+ } break;
+
+ case eSymbolTypeCode: // We already lookup functions elsewhere
+ case eSymbolTypeVariable:
+ case eSymbolTypeLocal:
+ case eSymbolTypeParam:
+ case eSymbolTypeTrampoline:
+ case eSymbolTypeInvalid:
+ case eSymbolTypeException:
+ case eSymbolTypeSourceFile:
+ case eSymbolTypeHeaderFile:
+ case eSymbolTypeObjectFile:
+ case eSymbolTypeCommonBlock:
+ case eSymbolTypeBlock:
+ case eSymbolTypeVariableType:
+ case eSymbolTypeLineEntry:
+ case eSymbolTypeLineHeader:
+ case eSymbolTypeScopeBegin:
+ case eSymbolTypeScopeEnd:
+ case eSymbolTypeAdditional:
+ case eSymbolTypeCompiler:
+ case eSymbolTypeInstrumentation:
+ case eSymbolTypeUndefined:
+ case eSymbolTypeResolver:
+ break;
+ }
+ }
+ }
+ }
+
+ if (external_symbols.size() > 1) {
+ StreamString ss;
+ ss.Printf("Multiple external symbols found for '%s'\n", name.AsCString());
+ for (const Symbol *symbol : external_symbols) {
+ symbol->GetDescription(&ss, eDescriptionLevelFull, &target);
+ }
+ ss.PutChar('\n');
+ error.SetErrorString(ss.GetData());
+ return nullptr;
+ } else if (external_symbols.size()) {
+ return external_symbols[0];
+ } else if (internal_symbols.size() > 1) {
+ StreamString ss;
+ ss.Printf("Multiple internal symbols found for '%s'\n", name.AsCString());
+ for (const Symbol *symbol : internal_symbols) {
+ symbol->GetDescription(&ss, eDescriptionLevelVerbose, &target);
+ ss.PutChar('\n');
+ }
+ error.SetErrorString(ss.GetData());
+ return nullptr;
+ } else if (internal_symbols.size()) {
+ return internal_symbols[0];
+ } else {
+ return nullptr;
+ }
+ };
+
+ if (module) {
+ SymbolContextList sc_list;
+ module->FindSymbolsWithNameAndType(name, eSymbolTypeAny, sc_list);
+ const Symbol *const module_symbol = ProcessMatches(sc_list, error);
+
+ if (!error.Success()) {
+ return nullptr;
+ } else if (module_symbol) {
+ return module_symbol;
+ }
+ }
+
+ {
+ SymbolContextList sc_list;
+ target.GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeAny,
+ sc_list);
+ const Symbol *const target_symbol = ProcessMatches(sc_list, error);
+
+ if (!error.Success()) {
+ return nullptr;
+ } else if (target_symbol) {
+ return target_symbol;
+ }
+ }
+
+ return nullptr; // no error; we just didn't find anything
+}
+
+
+//
+// SymbolContextSpecifier
+//
+
+SymbolContextSpecifier::SymbolContextSpecifier(const TargetSP &target_sp)
+ : m_target_sp(target_sp), m_module_spec(), m_module_sp(), m_file_spec_up(),
+ m_start_line(0), m_end_line(0), m_function_spec(), m_class_name(),
+ m_address_range_up(), m_type(eNothingSpecified) {}
+
+SymbolContextSpecifier::~SymbolContextSpecifier() {}
+
+bool SymbolContextSpecifier::AddLineSpecification(uint32_t line_no,
+ SpecificationType type) {
+ bool return_value = true;
+ switch (type) {
+ case eNothingSpecified:
+ Clear();
+ break;
+ case eLineStartSpecified:
+ m_start_line = line_no;
+ m_type |= eLineStartSpecified;
+ break;
+ case eLineEndSpecified:
+ m_end_line = line_no;
+ m_type |= eLineEndSpecified;
+ break;
+ default:
+ return_value = false;
+ break;
+ }
+ return return_value;
+}
+
+bool SymbolContextSpecifier::AddSpecification(const char *spec_string,
+ SpecificationType type) {
+ bool return_value = true;
+ switch (type) {
+ case eNothingSpecified:
+ Clear();
+ break;
+ case eModuleSpecified: {
+ // See if we can find the Module, if so stick it in the SymbolContext.
+ FileSpec module_file_spec(spec_string);
+ ModuleSpec module_spec(module_file_spec);
+ lldb::ModuleSP module_sp(
+ m_target_sp->GetImages().FindFirstModule(module_spec));
+ m_type |= eModuleSpecified;
+ if (module_sp)
+ m_module_sp = module_sp;
+ else
+ m_module_spec.assign(spec_string);
+ } break;
+ case eFileSpecified:
+ // CompUnits can't necessarily be resolved here, since an inlined function
+ // might show up in a number of CompUnits. Instead we just convert to a
+ // FileSpec and store it away.
+ m_file_spec_up.reset(new FileSpec(spec_string));
+ m_type |= eFileSpecified;
+ break;
+ case eLineStartSpecified:
+ m_start_line = StringConvert::ToSInt32(spec_string, 0, 0, &return_value);
+ if (return_value)
+ m_type |= eLineStartSpecified;
+ break;
+ case eLineEndSpecified:
+ m_end_line = StringConvert::ToSInt32(spec_string, 0, 0, &return_value);
+ if (return_value)
+ m_type |= eLineEndSpecified;
+ break;
+ case eFunctionSpecified:
+ m_function_spec.assign(spec_string);
+ m_type |= eFunctionSpecified;
+ break;
+ case eClassOrNamespaceSpecified:
+ Clear();
+ m_class_name.assign(spec_string);
+ m_type = eClassOrNamespaceSpecified;
+ break;
+ case eAddressRangeSpecified:
+ // Not specified yet...
+ break;
+ }
+
+ return return_value;
+}
+
+void SymbolContextSpecifier::Clear() {
+ m_module_spec.clear();
+ m_file_spec_up.reset();
+ m_function_spec.clear();
+ m_class_name.clear();
+ m_start_line = 0;
+ m_end_line = 0;
+ m_address_range_up.reset();
+
+ m_type = eNothingSpecified;
+}
+
+bool SymbolContextSpecifier::SymbolContextMatches(SymbolContext &sc) {
+ if (m_type == eNothingSpecified)
+ return true;
+
+ if (m_target_sp.get() != sc.target_sp.get())
+ return false;
+
+ if (m_type & eModuleSpecified) {
+ if (sc.module_sp) {
+ if (m_module_sp.get() != nullptr) {
+ if (m_module_sp.get() != sc.module_sp.get())
+ return false;
+ } else {
+ FileSpec module_file_spec(m_module_spec);
+ if (!FileSpec::Equal(module_file_spec, sc.module_sp->GetFileSpec(),
+ false))
+ return false;
+ }
+ }
+ }
+ if (m_type & eFileSpecified) {
+ if (m_file_spec_up) {
+ // If we don't have a block or a comp_unit, then we aren't going to match
+ // a source file.
+ if (sc.block == nullptr && sc.comp_unit == nullptr)
+ return false;
+
+ // Check if the block is present, and if so is it inlined:
+ bool was_inlined = false;
+ if (sc.block != nullptr) {
+ const InlineFunctionInfo *inline_info =
+ sc.block->GetInlinedFunctionInfo();
+ if (inline_info != nullptr) {
+ was_inlined = true;
+ if (!FileSpec::Equal(inline_info->GetDeclaration().GetFile(),
+ *(m_file_spec_up.get()), false))
+ return false;
+ }
+ }
+
+ // Next check the comp unit, but only if the SymbolContext was not
+ // inlined.
+ if (!was_inlined && sc.comp_unit != nullptr) {
+ if (!FileSpec::Equal(*(sc.comp_unit), *(m_file_spec_up.get()), false))
+ return false;
+ }
+ }
+ }
+ if (m_type & eLineStartSpecified || m_type & eLineEndSpecified) {
+ if (sc.line_entry.line < m_start_line || sc.line_entry.line > m_end_line)
+ return false;
+ }
+
+ if (m_type & eFunctionSpecified) {
+ // First check the current block, and if it is inlined, get the inlined
+ // function name:
+ bool was_inlined = false;
+ ConstString func_name(m_function_spec.c_str());
+
+ if (sc.block != nullptr) {
+ const InlineFunctionInfo *inline_info =
+ sc.block->GetInlinedFunctionInfo();
+ if (inline_info != nullptr) {
+ was_inlined = true;
+ const Mangled &name = inline_info->GetMangled();
+ if (!name.NameMatches(func_name, sc.function->GetLanguage()))
+ return false;
+ }
+ }
+ // If it wasn't inlined, check the name in the function or symbol:
+ if (!was_inlined) {
+ if (sc.function != nullptr) {
+ if (!sc.function->GetMangled().NameMatches(func_name,
+ sc.function->GetLanguage()))
+ return false;
+ } else if (sc.symbol != nullptr) {
+ if (!sc.symbol->GetMangled().NameMatches(func_name,
+ sc.symbol->GetLanguage()))
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool SymbolContextSpecifier::AddressMatches(lldb::addr_t addr) {
+ if (m_type & eAddressRangeSpecified) {
+
+ } else {
+ Address match_address(addr, nullptr);
+ SymbolContext sc;
+ m_target_sp->GetImages().ResolveSymbolContextForAddress(
+ match_address, eSymbolContextEverything, sc);
+ return SymbolContextMatches(sc);
+ }
+ return true;
+}
+
+void SymbolContextSpecifier::GetDescription(
+ Stream *s, lldb::DescriptionLevel level) const {
+ char path_str[PATH_MAX + 1];
+
+ if (m_type == eNothingSpecified) {
+ s->Printf("Nothing specified.\n");
+ }
+
+ if (m_type == eModuleSpecified) {
+ s->Indent();
+ if (m_module_sp) {
+ m_module_sp->GetFileSpec().GetPath(path_str, PATH_MAX);
+ s->Printf("Module: %s\n", path_str);
+ } else
+ s->Printf("Module: %s\n", m_module_spec.c_str());
+ }
+
+ if (m_type == eFileSpecified && m_file_spec_up != nullptr) {
+ m_file_spec_up->GetPath(path_str, PATH_MAX);
+ s->Indent();
+ s->Printf("File: %s", path_str);
+ if (m_type == eLineStartSpecified) {
+ s->Printf(" from line %" PRIu64 "", (uint64_t)m_start_line);
+ if (m_type == eLineEndSpecified)
+ s->Printf("to line %" PRIu64 "", (uint64_t)m_end_line);
+ else
+ s->Printf("to end");
+ } else if (m_type == eLineEndSpecified) {
+ s->Printf(" from start to line %" PRIu64 "", (uint64_t)m_end_line);
+ }
+ s->Printf(".\n");
+ }
+
+ if (m_type == eLineStartSpecified) {
+ s->Indent();
+ s->Printf("From line %" PRIu64 "", (uint64_t)m_start_line);
+ if (m_type == eLineEndSpecified)
+ s->Printf("to line %" PRIu64 "", (uint64_t)m_end_line);
+ else
+ s->Printf("to end");
+ s->Printf(".\n");
+ } else if (m_type == eLineEndSpecified) {
+ s->Printf("From start to line %" PRIu64 ".\n", (uint64_t)m_end_line);
+ }
+
+ if (m_type == eFunctionSpecified) {
+ s->Indent();
+ s->Printf("Function: %s.\n", m_function_spec.c_str());
+ }
+
+ if (m_type == eClassOrNamespaceSpecified) {
+ s->Indent();
+ s->Printf("Class name: %s.\n", m_class_name.c_str());
+ }
+
+ if (m_type == eAddressRangeSpecified && m_address_range_up != nullptr) {
+ s->Indent();
+ s->PutCString("Address range: ");
+ m_address_range_up->Dump(s, m_target_sp.get(),
+ Address::DumpStyleLoadAddress,
+ Address::DumpStyleFileAddress);
+ s->PutCString("\n");
+ }
+}
+
+//
+// SymbolContextList
+//
+
+SymbolContextList::SymbolContextList() : m_symbol_contexts() {}
+
+SymbolContextList::~SymbolContextList() {}
+
+void SymbolContextList::Append(const SymbolContext &sc) {
+ m_symbol_contexts.push_back(sc);
+}
+
+void SymbolContextList::Append(const SymbolContextList &sc_list) {
+ collection::const_iterator pos, end = sc_list.m_symbol_contexts.end();
+ for (pos = sc_list.m_symbol_contexts.begin(); pos != end; ++pos)
+ m_symbol_contexts.push_back(*pos);
+}
+
+uint32_t SymbolContextList::AppendIfUnique(const SymbolContextList &sc_list,
+ bool merge_symbol_into_function) {
+ uint32_t unique_sc_add_count = 0;
+ collection::const_iterator pos, end = sc_list.m_symbol_contexts.end();
+ for (pos = sc_list.m_symbol_contexts.begin(); pos != end; ++pos) {
+ if (AppendIfUnique(*pos, merge_symbol_into_function))
+ ++unique_sc_add_count;
+ }
+ return unique_sc_add_count;
+}
+
+bool SymbolContextList::AppendIfUnique(const SymbolContext &sc,
+ bool merge_symbol_into_function) {
+ collection::iterator pos, end = m_symbol_contexts.end();
+ for (pos = m_symbol_contexts.begin(); pos != end; ++pos) {
+ if (*pos == sc)
+ return false;
+ }
+ if (merge_symbol_into_function && sc.symbol != nullptr &&
+ sc.comp_unit == nullptr && sc.function == nullptr &&
+ sc.block == nullptr && !sc.line_entry.IsValid()) {
+ if (sc.symbol->ValueIsAddress()) {
+ for (pos = m_symbol_contexts.begin(); pos != end; ++pos) {
+ // Don't merge symbols into inlined function symbol contexts
+ if (pos->block && pos->block->GetContainingInlinedBlock())
+ continue;
+
+ if (pos->function) {
+ if (pos->function->GetAddressRange().GetBaseAddress() ==
+ sc.symbol->GetAddressRef()) {
+ // Do we already have a function with this symbol?
+ if (pos->symbol == sc.symbol)
+ return false;
+ if (pos->symbol == nullptr) {
+ pos->symbol = sc.symbol;
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ m_symbol_contexts.push_back(sc);
+ return true;
+}
+
+void SymbolContextList::Clear() { m_symbol_contexts.clear(); }
+
+void SymbolContextList::Dump(Stream *s, Target *target) const {
+
+ *s << this << ": ";
+ s->Indent();
+ s->PutCString("SymbolContextList");
+ s->EOL();
+ s->IndentMore();
+
+ collection::const_iterator pos, end = m_symbol_contexts.end();
+ for (pos = m_symbol_contexts.begin(); pos != end; ++pos) {
+ // pos->Dump(s, target);
+ pos->GetDescription(s, eDescriptionLevelVerbose, target);
+ }
+ s->IndentLess();
+}
+
+bool SymbolContextList::GetContextAtIndex(size_t idx, SymbolContext &sc) const {
+ if (idx < m_symbol_contexts.size()) {
+ sc = m_symbol_contexts[idx];
+ return true;
+ }
+ return false;
+}
+
+bool SymbolContextList::RemoveContextAtIndex(size_t idx) {
+ if (idx < m_symbol_contexts.size()) {
+ m_symbol_contexts.erase(m_symbol_contexts.begin() + idx);
+ return true;
+ }
+ return false;
+}
+
+uint32_t SymbolContextList::GetSize() const { return m_symbol_contexts.size(); }
+
+uint32_t SymbolContextList::NumLineEntriesWithLine(uint32_t line) const {
+ uint32_t match_count = 0;
+ const size_t size = m_symbol_contexts.size();
+ for (size_t idx = 0; idx < size; ++idx) {
+ if (m_symbol_contexts[idx].line_entry.line == line)
+ ++match_count;
+ }
+ return match_count;
+}
+
+void SymbolContextList::GetDescription(Stream *s, lldb::DescriptionLevel level,
+ Target *target) const {
+ const size_t size = m_symbol_contexts.size();
+ for (size_t idx = 0; idx < size; ++idx)
+ m_symbol_contexts[idx].GetDescription(s, level, target);
+}
+
+bool lldb_private::operator==(const SymbolContextList &lhs,
+ const SymbolContextList &rhs) {
+ const uint32_t size = lhs.GetSize();
+ if (size != rhs.GetSize())
+ return false;
+
+ SymbolContext lhs_sc;
+ SymbolContext rhs_sc;
+ for (uint32_t i = 0; i < size; ++i) {
+ lhs.GetContextAtIndex(i, lhs_sc);
+ rhs.GetContextAtIndex(i, rhs_sc);
+ if (lhs_sc != rhs_sc)
+ return false;
+ }
+ return true;
+}
+
+bool lldb_private::operator!=(const SymbolContextList &lhs,
+ const SymbolContextList &rhs) {
+ return !(lhs == rhs);
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/SymbolFile.cpp b/contrib/llvm-project/lldb/source/Symbol/SymbolFile.cpp
new file mode 100644
index 000000000000..77ab2223ec07
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/SymbolFile.cpp
@@ -0,0 +1,172 @@
+//===-- SymbolFile.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/SymbolFile.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-private.h"
+
+#include <future>
+
+using namespace lldb_private;
+
+void SymbolFile::PreloadSymbols() {
+ // No-op for most implementations.
+}
+
+std::recursive_mutex &SymbolFile::GetModuleMutex() const {
+ return GetObjectFile()->GetModule()->GetMutex();
+}
+
+SymbolFile *SymbolFile::FindPlugin(ObjectFile *obj_file) {
+ std::unique_ptr<SymbolFile> best_symfile_up;
+ if (obj_file != nullptr) {
+
+ // We need to test the abilities of this section list. So create what it
+ // would be with this new obj_file.
+ lldb::ModuleSP module_sp(obj_file->GetModule());
+ if (module_sp) {
+ // Default to the main module section list.
+ ObjectFile *module_obj_file = module_sp->GetObjectFile();
+ if (module_obj_file != obj_file) {
+ // Make sure the main object file's sections are created
+ module_obj_file->GetSectionList();
+ obj_file->CreateSections(*module_sp->GetUnifiedSectionList());
+ }
+ }
+
+ // TODO: Load any plug-ins in the appropriate plug-in search paths and
+ // iterate over all of them to find the best one for the job.
+
+ uint32_t best_symfile_abilities = 0;
+
+ SymbolFileCreateInstance create_callback;
+ for (uint32_t idx = 0;
+ (create_callback = PluginManager::GetSymbolFileCreateCallbackAtIndex(
+ idx)) != nullptr;
+ ++idx) {
+ std::unique_ptr<SymbolFile> curr_symfile_up(create_callback(obj_file));
+
+ if (curr_symfile_up) {
+ const uint32_t sym_file_abilities = curr_symfile_up->GetAbilities();
+ if (sym_file_abilities > best_symfile_abilities) {
+ best_symfile_abilities = sym_file_abilities;
+ best_symfile_up.reset(curr_symfile_up.release());
+ // If any symbol file parser has all of the abilities, then we should
+ // just stop looking.
+ if ((kAllAbilities & sym_file_abilities) == kAllAbilities)
+ break;
+ }
+ }
+ }
+ if (best_symfile_up) {
+ // Let the winning symbol file parser initialize itself more completely
+ // now that it has been chosen
+ best_symfile_up->InitializeObject();
+ }
+ }
+ return best_symfile_up.release();
+}
+
+TypeList *SymbolFile::GetTypeList() {
+ if (m_obj_file)
+ return m_obj_file->GetModule()->GetTypeList();
+ return nullptr;
+}
+
+TypeSystem *SymbolFile::GetTypeSystemForLanguage(lldb::LanguageType language) {
+ TypeSystem *type_system =
+ m_obj_file->GetModule()->GetTypeSystemForLanguage(language);
+ if (type_system)
+ type_system->SetSymbolFile(this);
+ return type_system;
+}
+
+uint32_t SymbolFile::ResolveSymbolContext(const FileSpec &file_spec,
+ uint32_t line, bool check_inlines,
+ lldb::SymbolContextItem resolve_scope,
+ SymbolContextList &sc_list) {
+ return 0;
+}
+
+uint32_t
+SymbolFile::FindGlobalVariables(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, VariableList &variables) {
+ return 0;
+}
+
+uint32_t SymbolFile::FindGlobalVariables(const RegularExpression &regex,
+ uint32_t max_matches,
+ VariableList &variables) {
+ return 0;
+}
+
+uint32_t SymbolFile::FindFunctions(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ lldb::FunctionNameType name_type_mask,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ if (!append)
+ sc_list.Clear();
+ return 0;
+}
+
+uint32_t SymbolFile::FindFunctions(const RegularExpression &regex,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ if (!append)
+ sc_list.Clear();
+ return 0;
+}
+
+void SymbolFile::GetMangledNamesForFunction(
+ const std::string &scope_qualified_name,
+ std::vector<ConstString> &mangled_names) {
+ return;
+}
+
+uint32_t SymbolFile::FindTypes(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ TypeMap &types) {
+ if (!append)
+ types.Clear();
+ return 0;
+}
+
+size_t SymbolFile::FindTypes(const std::vector<CompilerContext> &context,
+ bool append, TypeMap &types) {
+ if (!append)
+ types.Clear();
+ return 0;
+}
+
+void SymbolFile::AssertModuleLock() {
+ // The code below is too expensive to leave enabled in release builds. It's
+ // enabled in debug builds or when the correct macro is set.
+#if defined(LLDB_CONFIGURATION_DEBUG)
+ // We assert that we have to module lock by trying to acquire the lock from a
+ // different thread. Note that we must abort if the result is true to
+ // guarantee correctness.
+ assert(std::async(std::launch::async,
+ [this] { return this->GetModuleMutex().try_lock(); })
+ .get() == false &&
+ "Module is not locked");
+#endif
+}
+
+SymbolFile::RegisterInfoResolver::~RegisterInfoResolver() = default;
diff --git a/contrib/llvm-project/lldb/source/Symbol/SymbolVendor.cpp b/contrib/llvm-project/lldb/source/Symbol/SymbolVendor.cpp
new file mode 100644
index 000000000000..b9f3a5fe3926
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/SymbolVendor.cpp
@@ -0,0 +1,489 @@
+//===-- SymbolVendor.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/SymbolVendor.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// FindPlugin
+//
+// Platforms can register a callback to use when creating symbol vendors to
+// allow for complex debug information file setups, and to also allow for
+// finding separate debug information files.
+SymbolVendor *SymbolVendor::FindPlugin(const lldb::ModuleSP &module_sp,
+ lldb_private::Stream *feedback_strm) {
+ std::unique_ptr<SymbolVendor> instance_up;
+ SymbolVendorCreateInstance create_callback;
+
+ for (size_t idx = 0;
+ (create_callback = PluginManager::GetSymbolVendorCreateCallbackAtIndex(
+ idx)) != nullptr;
+ ++idx) {
+ instance_up.reset(create_callback(module_sp, feedback_strm));
+
+ if (instance_up) {
+ return instance_up.release();
+ }
+ }
+ // The default implementation just tries to create debug information using
+ // the file representation for the module.
+ ObjectFileSP sym_objfile_sp;
+ FileSpec sym_spec = module_sp->GetSymbolFileFileSpec();
+ if (sym_spec && sym_spec != module_sp->GetObjectFile()->GetFileSpec()) {
+ DataBufferSP data_sp;
+ offset_t data_offset = 0;
+ sym_objfile_sp = ObjectFile::FindPlugin(
+ module_sp, &sym_spec, 0, FileSystem::Instance().GetByteSize(sym_spec),
+ data_sp, data_offset);
+ }
+ if (!sym_objfile_sp)
+ sym_objfile_sp = module_sp->GetObjectFile()->shared_from_this();
+ instance_up.reset(new SymbolVendor(module_sp));
+ instance_up->AddSymbolFileRepresentation(sym_objfile_sp);
+ return instance_up.release();
+}
+
+// SymbolVendor constructor
+SymbolVendor::SymbolVendor(const lldb::ModuleSP &module_sp)
+ : ModuleChild(module_sp), m_type_list(), m_compile_units(), m_sym_file_up(),
+ m_symtab() {}
+
+// Destructor
+SymbolVendor::~SymbolVendor() {}
+
+// Add a representation given an object file.
+void SymbolVendor::AddSymbolFileRepresentation(const ObjectFileSP &objfile_sp) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (objfile_sp) {
+ m_objfile_sp = objfile_sp;
+ m_sym_file_up.reset(SymbolFile::FindPlugin(objfile_sp.get()));
+ }
+ }
+}
+
+bool SymbolVendor::SetCompileUnitAtIndex(size_t idx, const CompUnitSP &cu_sp) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ const size_t num_compile_units = GetNumCompileUnits();
+ if (idx < num_compile_units) {
+ // Fire off an assertion if this compile unit already exists for now. The
+ // partial parsing should take care of only setting the compile unit
+ // once, so if this assertion fails, we need to make sure that we don't
+ // have a race condition, or have a second parse of the same compile
+ // unit.
+ assert(m_compile_units[idx].get() == nullptr);
+ m_compile_units[idx] = cu_sp;
+ return true;
+ } else {
+ // This should NOT happen, and if it does, we want to crash and know
+ // about it
+ assert(idx < num_compile_units);
+ }
+ }
+ return false;
+}
+
+size_t SymbolVendor::GetNumCompileUnits() {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_compile_units.empty()) {
+ if (m_sym_file_up) {
+ // Resize our array of compile unit shared pointers -- which will each
+ // remain NULL until someone asks for the actual compile unit
+ // information. When this happens, the symbol file will be asked to
+ // parse this compile unit information.
+ m_compile_units.resize(m_sym_file_up->GetNumCompileUnits());
+ }
+ }
+ }
+ return m_compile_units.size();
+}
+
+lldb::LanguageType SymbolVendor::ParseLanguage(CompileUnit &comp_unit) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ParseLanguage(comp_unit);
+ }
+ return eLanguageTypeUnknown;
+}
+
+size_t SymbolVendor::ParseFunctions(CompileUnit &comp_unit) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ParseFunctions(comp_unit);
+ }
+ return 0;
+}
+
+bool SymbolVendor::ParseLineTable(CompileUnit &comp_unit) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ParseLineTable(comp_unit);
+ }
+ return false;
+}
+
+bool SymbolVendor::ParseDebugMacros(CompileUnit &comp_unit) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ParseDebugMacros(comp_unit);
+ }
+ return false;
+}
+bool SymbolVendor::ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ParseSupportFiles(comp_unit, support_files);
+ }
+ return false;
+}
+
+bool SymbolVendor::ParseIsOptimized(CompileUnit &comp_unit) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ParseIsOptimized(comp_unit);
+ }
+ return false;
+}
+
+bool SymbolVendor::ParseImportedModules(
+ const SymbolContext &sc, std::vector<SourceModule> &imported_modules) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ParseImportedModules(sc, imported_modules);
+ }
+ return false;
+}
+
+size_t SymbolVendor::ParseBlocksRecursive(Function &func) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ParseBlocksRecursive(func);
+ }
+ return 0;
+}
+
+size_t SymbolVendor::ParseTypes(CompileUnit &comp_unit) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ParseTypes(comp_unit);
+ }
+ return 0;
+}
+
+size_t SymbolVendor::ParseVariablesForContext(const SymbolContext &sc) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ParseVariablesForContext(sc);
+ }
+ return 0;
+}
+
+Type *SymbolVendor::ResolveTypeUID(lldb::user_id_t type_uid) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ResolveTypeUID(type_uid);
+ }
+ return nullptr;
+}
+
+uint32_t SymbolVendor::ResolveSymbolContext(const Address &so_addr,
+ SymbolContextItem resolve_scope,
+ SymbolContext &sc) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ResolveSymbolContext(so_addr, resolve_scope, sc);
+ }
+ return 0;
+}
+
+uint32_t SymbolVendor::ResolveSymbolContext(const FileSpec &file_spec,
+ uint32_t line, bool check_inlines,
+ SymbolContextItem resolve_scope,
+ SymbolContextList &sc_list) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->ResolveSymbolContext(file_spec, line, check_inlines,
+ resolve_scope, sc_list);
+ }
+ return 0;
+}
+
+size_t
+SymbolVendor::FindGlobalVariables(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ size_t max_matches, VariableList &variables) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->FindGlobalVariables(name, parent_decl_ctx,
+ max_matches, variables);
+ }
+ return 0;
+}
+
+size_t SymbolVendor::FindGlobalVariables(const RegularExpression &regex,
+ size_t max_matches,
+ VariableList &variables) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->FindGlobalVariables(regex, max_matches, variables);
+ }
+ return 0;
+}
+
+size_t SymbolVendor::FindFunctions(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->FindFunctions(name, parent_decl_ctx, name_type_mask,
+ include_inlines, append, sc_list);
+ }
+ return 0;
+}
+
+size_t SymbolVendor::FindFunctions(const RegularExpression &regex,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->FindFunctions(regex, include_inlines, append,
+ sc_list);
+ }
+ return 0;
+}
+
+size_t SymbolVendor::FindTypes(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ bool append, size_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ TypeMap &types) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->FindTypes(name, parent_decl_ctx, append,
+ max_matches, searched_symbol_files,
+ types);
+ }
+ if (!append)
+ types.Clear();
+ return 0;
+}
+
+size_t SymbolVendor::FindTypes(const std::vector<CompilerContext> &context,
+ bool append, TypeMap &types) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->FindTypes(context, append, types);
+ }
+ if (!append)
+ types.Clear();
+ return 0;
+}
+
+size_t SymbolVendor::GetTypes(SymbolContextScope *sc_scope, TypeClass type_mask,
+ lldb_private::TypeList &type_list) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ return m_sym_file_up->GetTypes(sc_scope, type_mask, type_list);
+ }
+ return 0;
+}
+
+CompilerDeclContext
+SymbolVendor::FindNamespace(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx) {
+ CompilerDeclContext namespace_decl_ctx;
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_sym_file_up)
+ namespace_decl_ctx = m_sym_file_up->FindNamespace(name, parent_decl_ctx);
+ }
+ return namespace_decl_ctx;
+}
+
+void SymbolVendor::Dump(Stream *s) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+
+ bool show_context = false;
+
+ s->Printf("%p: ", static_cast<void *>(this));
+ s->Indent();
+ s->PutCString("SymbolVendor");
+ if (m_sym_file_up) {
+ *s << " " << m_sym_file_up->GetPluginName();
+ ObjectFile *objfile = m_sym_file_up->GetObjectFile();
+ if (objfile) {
+ const FileSpec &objfile_file_spec = objfile->GetFileSpec();
+ if (objfile_file_spec) {
+ s->PutCString(" (");
+ objfile_file_spec.Dump(s);
+ s->PutChar(')');
+ }
+ }
+ }
+ s->EOL();
+ if (m_sym_file_up)
+ m_sym_file_up->Dump(*s);
+ s->IndentMore();
+ m_type_list.Dump(s, show_context);
+
+ CompileUnitConstIter cu_pos, cu_end;
+ cu_end = m_compile_units.end();
+ for (cu_pos = m_compile_units.begin(); cu_pos != cu_end; ++cu_pos) {
+ // We currently only dump the compile units that have been parsed
+ if (*cu_pos)
+ (*cu_pos)->Dump(s, show_context);
+ }
+
+ if (Symtab *symtab = GetSymtab())
+ symtab->Dump(s, nullptr, eSortOrderNone);
+
+ s->IndentLess();
+ }
+}
+
+CompUnitSP SymbolVendor::GetCompileUnitAtIndex(size_t idx) {
+ CompUnitSP cu_sp;
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ const size_t num_compile_units = GetNumCompileUnits();
+ if (idx < num_compile_units) {
+ cu_sp = m_compile_units[idx];
+ if (cu_sp.get() == nullptr) {
+ m_compile_units[idx] = m_sym_file_up->ParseCompileUnitAtIndex(idx);
+ cu_sp = m_compile_units[idx];
+ }
+ }
+ }
+ return cu_sp;
+}
+
+FileSpec SymbolVendor::GetMainFileSpec() const {
+ if (m_sym_file_up) {
+ const ObjectFile *symfile_objfile = m_sym_file_up->GetObjectFile();
+ if (symfile_objfile)
+ return symfile_objfile->GetFileSpec();
+ }
+
+ return FileSpec();
+}
+
+Symtab *SymbolVendor::GetSymtab() {
+ ModuleSP module_sp(GetModule());
+ if (!module_sp)
+ return nullptr;
+
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+
+ if (m_symtab)
+ return m_symtab;
+
+ ObjectFile *objfile = module_sp->GetObjectFile();
+ if (!objfile)
+ return nullptr;
+
+ m_symtab = objfile->GetSymtab();
+ if (m_symtab && m_sym_file_up)
+ m_sym_file_up->AddSymbols(*m_symtab);
+
+ return m_symtab;
+}
+
+void SymbolVendor::ClearSymtab() {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ ObjectFile *objfile = module_sp->GetObjectFile();
+ if (objfile) {
+ // Clear symbol table from unified section list.
+ objfile->ClearSymtab();
+ }
+ }
+}
+
+void SymbolVendor::SectionFileAddressesChanged() {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ ObjectFile *module_objfile = module_sp->GetObjectFile();
+ if (m_sym_file_up) {
+ ObjectFile *symfile_objfile = m_sym_file_up->GetObjectFile();
+ if (symfile_objfile != module_objfile)
+ symfile_objfile->SectionFileAddressesChanged();
+ }
+ Symtab *symtab = GetSymtab();
+ if (symtab) {
+ symtab->SectionFileAddressesChanged();
+ }
+ }
+}
+
+// PluginInterface protocol
+lldb_private::ConstString SymbolVendor::GetPluginName() {
+ static ConstString g_name("vendor-default");
+ return g_name;
+}
+
+uint32_t SymbolVendor::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Symbol/Symtab.cpp b/contrib/llvm-project/lldb/source/Symbol/Symtab.cpp
new file mode 100644
index 000000000000..29c390e83878
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/Symtab.cpp
@@ -0,0 +1,1129 @@
+//===-- Symtab.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <map>
+#include <set>
+
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/RichManglingContext.h"
+#include "lldb/Core/STLUtils.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+
+#include "llvm/ADT/StringRef.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+Symtab::Symtab(ObjectFile *objfile)
+ : m_objfile(objfile), m_symbols(), m_file_addr_to_index(),
+ m_name_to_index(), m_mutex(), m_file_addr_to_index_computed(false),
+ m_name_indexes_computed(false) {}
+
+Symtab::~Symtab() {}
+
+void Symtab::Reserve(size_t count) {
+ // Clients should grab the mutex from this symbol table and lock it manually
+ // when calling this function to avoid performance issues.
+ m_symbols.reserve(count);
+}
+
+Symbol *Symtab::Resize(size_t count) {
+ // Clients should grab the mutex from this symbol table and lock it manually
+ // when calling this function to avoid performance issues.
+ m_symbols.resize(count);
+ return m_symbols.empty() ? nullptr : &m_symbols[0];
+}
+
+uint32_t Symtab::AddSymbol(const Symbol &symbol) {
+ // Clients should grab the mutex from this symbol table and lock it manually
+ // when calling this function to avoid performance issues.
+ uint32_t symbol_idx = m_symbols.size();
+ m_name_to_index.Clear();
+ m_file_addr_to_index.Clear();
+ m_symbols.push_back(symbol);
+ m_file_addr_to_index_computed = false;
+ m_name_indexes_computed = false;
+ return symbol_idx;
+}
+
+size_t Symtab::GetNumSymbols() const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return m_symbols.size();
+}
+
+void Symtab::SectionFileAddressesChanged() {
+ m_name_to_index.Clear();
+ m_file_addr_to_index_computed = false;
+}
+
+void Symtab::Dump(Stream *s, Target *target, SortOrder sort_order) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this);
+ s->Indent();
+ const FileSpec &file_spec = m_objfile->GetFileSpec();
+ const char *object_name = nullptr;
+ if (m_objfile->GetModule())
+ object_name = m_objfile->GetModule()->GetObjectName().GetCString();
+
+ if (file_spec)
+ s->Printf("Symtab, file = %s%s%s%s, num_symbols = %" PRIu64,
+ file_spec.GetPath().c_str(), object_name ? "(" : "",
+ object_name ? object_name : "", object_name ? ")" : "",
+ (uint64_t)m_symbols.size());
+ else
+ s->Printf("Symtab, num_symbols = %" PRIu64 "", (uint64_t)m_symbols.size());
+
+ if (!m_symbols.empty()) {
+ switch (sort_order) {
+ case eSortOrderNone: {
+ s->PutCString(":\n");
+ DumpSymbolHeader(s);
+ const_iterator begin = m_symbols.begin();
+ const_iterator end = m_symbols.end();
+ for (const_iterator pos = m_symbols.begin(); pos != end; ++pos) {
+ s->Indent();
+ pos->Dump(s, target, std::distance(begin, pos));
+ }
+ } break;
+
+ case eSortOrderByName: {
+ // Although we maintain a lookup by exact name map, the table isn't
+ // sorted by name. So we must make the ordered symbol list up ourselves.
+ s->PutCString(" (sorted by name):\n");
+ DumpSymbolHeader(s);
+ typedef std::multimap<const char *, const Symbol *,
+ CStringCompareFunctionObject>
+ CStringToSymbol;
+ CStringToSymbol name_map;
+ for (const_iterator pos = m_symbols.begin(), end = m_symbols.end();
+ pos != end; ++pos) {
+ const char *name = pos->GetName().AsCString();
+ if (name && name[0])
+ name_map.insert(std::make_pair(name, &(*pos)));
+ }
+
+ for (CStringToSymbol::const_iterator pos = name_map.begin(),
+ end = name_map.end();
+ pos != end; ++pos) {
+ s->Indent();
+ pos->second->Dump(s, target, pos->second - &m_symbols[0]);
+ }
+ } break;
+
+ case eSortOrderByAddress:
+ s->PutCString(" (sorted by address):\n");
+ DumpSymbolHeader(s);
+ if (!m_file_addr_to_index_computed)
+ InitAddressIndexes();
+ const size_t num_entries = m_file_addr_to_index.GetSize();
+ for (size_t i = 0; i < num_entries; ++i) {
+ s->Indent();
+ const uint32_t symbol_idx = m_file_addr_to_index.GetEntryRef(i).data;
+ m_symbols[symbol_idx].Dump(s, target, symbol_idx);
+ }
+ break;
+ }
+ } else {
+ s->PutCString("\n");
+ }
+}
+
+void Symtab::Dump(Stream *s, Target *target,
+ std::vector<uint32_t> &indexes) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ const size_t num_symbols = GetNumSymbols();
+ // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this);
+ s->Indent();
+ s->Printf("Symtab %" PRIu64 " symbol indexes (%" PRIu64 " symbols total):\n",
+ (uint64_t)indexes.size(), (uint64_t)m_symbols.size());
+ s->IndentMore();
+
+ if (!indexes.empty()) {
+ std::vector<uint32_t>::const_iterator pos;
+ std::vector<uint32_t>::const_iterator end = indexes.end();
+ DumpSymbolHeader(s);
+ for (pos = indexes.begin(); pos != end; ++pos) {
+ size_t idx = *pos;
+ if (idx < num_symbols) {
+ s->Indent();
+ m_symbols[idx].Dump(s, target, idx);
+ }
+ }
+ }
+ s->IndentLess();
+}
+
+void Symtab::DumpSymbolHeader(Stream *s) {
+ s->Indent(" Debug symbol\n");
+ s->Indent(" |Synthetic symbol\n");
+ s->Indent(" ||Externally Visible\n");
+ s->Indent(" |||\n");
+ s->Indent("Index UserID DSX Type File Address/Value Load "
+ "Address Size Flags Name\n");
+ s->Indent("------- ------ --- --------------- ------------------ "
+ "------------------ ------------------ ---------- "
+ "----------------------------------\n");
+}
+
+static int CompareSymbolID(const void *key, const void *p) {
+ const user_id_t match_uid = *(const user_id_t *)key;
+ const user_id_t symbol_uid = ((const Symbol *)p)->GetID();
+ if (match_uid < symbol_uid)
+ return -1;
+ if (match_uid > symbol_uid)
+ return 1;
+ return 0;
+}
+
+Symbol *Symtab::FindSymbolByID(lldb::user_id_t symbol_uid) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ Symbol *symbol =
+ (Symbol *)::bsearch(&symbol_uid, &m_symbols[0], m_symbols.size(),
+ sizeof(m_symbols[0]), CompareSymbolID);
+ return symbol;
+}
+
+Symbol *Symtab::SymbolAtIndex(size_t idx) {
+ // Clients should grab the mutex from this symbol table and lock it manually
+ // when calling this function to avoid performance issues.
+ if (idx < m_symbols.size())
+ return &m_symbols[idx];
+ return nullptr;
+}
+
+const Symbol *Symtab::SymbolAtIndex(size_t idx) const {
+ // Clients should grab the mutex from this symbol table and lock it manually
+ // when calling this function to avoid performance issues.
+ if (idx < m_symbols.size())
+ return &m_symbols[idx];
+ return nullptr;
+}
+
+static bool lldb_skip_name(llvm::StringRef mangled,
+ Mangled::ManglingScheme scheme) {
+ switch (scheme) {
+ case Mangled::eManglingSchemeItanium: {
+ if (mangled.size() < 3 || !mangled.startswith("_Z"))
+ return true;
+
+ // Avoid the following types of symbols in the index.
+ switch (mangled[2]) {
+ case 'G': // guard variables
+ case 'T': // virtual tables, VTT structures, typeinfo structures + names
+ case 'Z': // named local entities (if we eventually handle
+ // eSymbolTypeData, we will want this back)
+ return true;
+
+ default:
+ break;
+ }
+
+ // Include this name in the index.
+ return false;
+ }
+
+ // No filters for this scheme yet. Include all names in indexing.
+ case Mangled::eManglingSchemeMSVC:
+ return false;
+
+ // Don't try and demangle things we can't categorize.
+ case Mangled::eManglingSchemeNone:
+ return true;
+ }
+ llvm_unreachable("unknown scheme!");
+}
+
+void Symtab::InitNameIndexes() {
+ // Protected function, no need to lock mutex...
+ if (!m_name_indexes_computed) {
+ m_name_indexes_computed = true;
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s", LLVM_PRETTY_FUNCTION);
+ // Create the name index vector to be able to quickly search by name
+ const size_t num_symbols = m_symbols.size();
+ m_name_to_index.Reserve(num_symbols);
+
+ // The "const char *" in "class_contexts" and backlog::value_type::second
+ // must come from a ConstString::GetCString()
+ std::set<const char *> class_contexts;
+ std::vector<std::pair<NameToIndexMap::Entry, const char *>> backlog;
+ backlog.reserve(num_symbols / 2);
+
+ // Instantiation of the demangler is expensive, so better use a single one
+ // for all entries during batch processing.
+ RichManglingContext rmc;
+ for (uint32_t value = 0; value < num_symbols; ++value) {
+ Symbol *symbol = &m_symbols[value];
+
+ // Don't let trampolines get into the lookup by name map If we ever need
+ // the trampoline symbols to be searchable by name we can remove this and
+ // then possibly add a new bool to any of the Symtab functions that
+ // lookup symbols by name to indicate if they want trampolines.
+ if (symbol->IsTrampoline())
+ continue;
+
+ // If the symbol's name string matched a Mangled::ManglingScheme, it is
+ // stored in the mangled field.
+ Mangled &mangled = symbol->GetMangled();
+ if (ConstString name = mangled.GetMangledName()) {
+ m_name_to_index.Append(name, value);
+
+ if (symbol->ContainsLinkerAnnotations()) {
+ // If the symbol has linker annotations, also add the version without
+ // the annotations.
+ ConstString stripped = ConstString(
+ m_objfile->StripLinkerSymbolAnnotations(name.GetStringRef()));
+ m_name_to_index.Append(stripped, value);
+ }
+
+ const SymbolType type = symbol->GetType();
+ if (type == eSymbolTypeCode || type == eSymbolTypeResolver) {
+ if (mangled.DemangleWithRichManglingInfo(rmc, lldb_skip_name))
+ RegisterMangledNameEntry(value, class_contexts, backlog, rmc);
+ }
+ }
+
+ // Symbol name strings that didn't match a Mangled::ManglingScheme, are
+ // stored in the demangled field.
+ if (ConstString name = mangled.GetDemangledName(symbol->GetLanguage())) {
+ m_name_to_index.Append(name, value);
+
+ if (symbol->ContainsLinkerAnnotations()) {
+ // If the symbol has linker annotations, also add the version without
+ // the annotations.
+ name = ConstString(
+ m_objfile->StripLinkerSymbolAnnotations(name.GetStringRef()));
+ m_name_to_index.Append(name, value);
+ }
+
+ // If the demangled name turns out to be an ObjC name, and is a category
+ // name, add the version without categories to the index too.
+ ObjCLanguage::MethodName objc_method(name.GetStringRef(), true);
+ if (objc_method.IsValid(true)) {
+ m_selector_to_index.Append(objc_method.GetSelector(), value);
+
+ if (ConstString objc_method_no_category =
+ objc_method.GetFullNameWithoutCategory(true))
+ m_name_to_index.Append(objc_method_no_category, value);
+ }
+ }
+ }
+
+ for (const auto &record : backlog) {
+ RegisterBacklogEntry(record.first, record.second, class_contexts);
+ }
+
+ m_name_to_index.Sort();
+ m_name_to_index.SizeToFit();
+ m_selector_to_index.Sort();
+ m_selector_to_index.SizeToFit();
+ m_basename_to_index.Sort();
+ m_basename_to_index.SizeToFit();
+ m_method_to_index.Sort();
+ m_method_to_index.SizeToFit();
+ }
+}
+
+void Symtab::RegisterMangledNameEntry(
+ uint32_t value, std::set<const char *> &class_contexts,
+ std::vector<std::pair<NameToIndexMap::Entry, const char *>> &backlog,
+ RichManglingContext &rmc) {
+ // Only register functions that have a base name.
+ rmc.ParseFunctionBaseName();
+ llvm::StringRef base_name = rmc.GetBufferRef();
+ if (base_name.empty())
+ return;
+
+ // The base name will be our entry's name.
+ NameToIndexMap::Entry entry(ConstString(base_name), value);
+
+ rmc.ParseFunctionDeclContextName();
+ llvm::StringRef decl_context = rmc.GetBufferRef();
+
+ // Register functions with no context.
+ if (decl_context.empty()) {
+ // This has to be a basename
+ m_basename_to_index.Append(entry);
+ // If there is no context (no namespaces or class scopes that come before
+ // the function name) then this also could be a fullname.
+ m_name_to_index.Append(entry);
+ return;
+ }
+
+ // Make sure we have a pool-string pointer and see if we already know the
+ // context name.
+ const char *decl_context_ccstr = ConstString(decl_context).GetCString();
+ auto it = class_contexts.find(decl_context_ccstr);
+
+ // Register constructors and destructors. They are methods and create
+ // declaration contexts.
+ if (rmc.IsCtorOrDtor()) {
+ m_method_to_index.Append(entry);
+ if (it == class_contexts.end())
+ class_contexts.insert(it, decl_context_ccstr);
+ return;
+ }
+
+ // Register regular methods with a known declaration context.
+ if (it != class_contexts.end()) {
+ m_method_to_index.Append(entry);
+ return;
+ }
+
+ // Regular methods in unknown declaration contexts are put to the backlog. We
+ // will revisit them once we processed all remaining symbols.
+ backlog.push_back(std::make_pair(entry, decl_context_ccstr));
+}
+
+void Symtab::RegisterBacklogEntry(
+ const NameToIndexMap::Entry &entry, const char *decl_context,
+ const std::set<const char *> &class_contexts) {
+ auto it = class_contexts.find(decl_context);
+ if (it != class_contexts.end()) {
+ m_method_to_index.Append(entry);
+ } else {
+ // If we got here, we have something that had a context (was inside
+ // a namespace or class) yet we don't know the entry
+ m_method_to_index.Append(entry);
+ m_basename_to_index.Append(entry);
+ }
+}
+
+void Symtab::PreloadSymbols() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ InitNameIndexes();
+}
+
+void Symtab::AppendSymbolNamesToMap(const IndexCollection &indexes,
+ bool add_demangled, bool add_mangled,
+ NameToIndexMap &name_to_index_map) const {
+ if (add_demangled || add_mangled) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s", LLVM_PRETTY_FUNCTION);
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ // Create the name index vector to be able to quickly search by name
+ const size_t num_indexes = indexes.size();
+ for (size_t i = 0; i < num_indexes; ++i) {
+ uint32_t value = indexes[i];
+ assert(i < m_symbols.size());
+ const Symbol *symbol = &m_symbols[value];
+
+ const Mangled &mangled = symbol->GetMangled();
+ if (add_demangled) {
+ if (ConstString name = mangled.GetDemangledName(symbol->GetLanguage()))
+ name_to_index_map.Append(name, value);
+ }
+
+ if (add_mangled) {
+ if (ConstString name = mangled.GetMangledName())
+ name_to_index_map.Append(name, value);
+ }
+ }
+ }
+}
+
+uint32_t Symtab::AppendSymbolIndexesWithType(SymbolType symbol_type,
+ std::vector<uint32_t> &indexes,
+ uint32_t start_idx,
+ uint32_t end_index) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ uint32_t prev_size = indexes.size();
+
+ const uint32_t count = std::min<uint32_t>(m_symbols.size(), end_index);
+
+ for (uint32_t i = start_idx; i < count; ++i) {
+ if (symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type)
+ indexes.push_back(i);
+ }
+
+ return indexes.size() - prev_size;
+}
+
+uint32_t Symtab::AppendSymbolIndexesWithTypeAndFlagsValue(
+ SymbolType symbol_type, uint32_t flags_value,
+ std::vector<uint32_t> &indexes, uint32_t start_idx,
+ uint32_t end_index) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ uint32_t prev_size = indexes.size();
+
+ const uint32_t count = std::min<uint32_t>(m_symbols.size(), end_index);
+
+ for (uint32_t i = start_idx; i < count; ++i) {
+ if ((symbol_type == eSymbolTypeAny ||
+ m_symbols[i].GetType() == symbol_type) &&
+ m_symbols[i].GetFlags() == flags_value)
+ indexes.push_back(i);
+ }
+
+ return indexes.size() - prev_size;
+}
+
+uint32_t Symtab::AppendSymbolIndexesWithType(SymbolType symbol_type,
+ Debug symbol_debug_type,
+ Visibility symbol_visibility,
+ std::vector<uint32_t> &indexes,
+ uint32_t start_idx,
+ uint32_t end_index) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ uint32_t prev_size = indexes.size();
+
+ const uint32_t count = std::min<uint32_t>(m_symbols.size(), end_index);
+
+ for (uint32_t i = start_idx; i < count; ++i) {
+ if (symbol_type == eSymbolTypeAny ||
+ m_symbols[i].GetType() == symbol_type) {
+ if (CheckSymbolAtIndex(i, symbol_debug_type, symbol_visibility))
+ indexes.push_back(i);
+ }
+ }
+
+ return indexes.size() - prev_size;
+}
+
+uint32_t Symtab::GetIndexForSymbol(const Symbol *symbol) const {
+ if (!m_symbols.empty()) {
+ const Symbol *first_symbol = &m_symbols[0];
+ if (symbol >= first_symbol && symbol < first_symbol + m_symbols.size())
+ return symbol - first_symbol;
+ }
+ return UINT32_MAX;
+}
+
+struct SymbolSortInfo {
+ const bool sort_by_load_addr;
+ const Symbol *symbols;
+};
+
+namespace {
+struct SymbolIndexComparator {
+ const std::vector<Symbol> &symbols;
+ std::vector<lldb::addr_t> &addr_cache;
+
+ // Getting from the symbol to the Address to the File Address involves some
+ // work. Since there are potentially many symbols here, and we're using this
+ // for sorting so we're going to be computing the address many times, cache
+ // that in addr_cache. The array passed in has to be the same size as the
+ // symbols array passed into the member variable symbols, and should be
+ // initialized with LLDB_INVALID_ADDRESS.
+ // NOTE: You have to make addr_cache externally and pass it in because
+ // std::stable_sort
+ // makes copies of the comparator it is initially passed in, and you end up
+ // spending huge amounts of time copying this array...
+
+ SymbolIndexComparator(const std::vector<Symbol> &s,
+ std::vector<lldb::addr_t> &a)
+ : symbols(s), addr_cache(a) {
+ assert(symbols.size() == addr_cache.size());
+ }
+ bool operator()(uint32_t index_a, uint32_t index_b) {
+ addr_t value_a = addr_cache[index_a];
+ if (value_a == LLDB_INVALID_ADDRESS) {
+ value_a = symbols[index_a].GetAddressRef().GetFileAddress();
+ addr_cache[index_a] = value_a;
+ }
+
+ addr_t value_b = addr_cache[index_b];
+ if (value_b == LLDB_INVALID_ADDRESS) {
+ value_b = symbols[index_b].GetAddressRef().GetFileAddress();
+ addr_cache[index_b] = value_b;
+ }
+
+ if (value_a == value_b) {
+ // The if the values are equal, use the original symbol user ID
+ lldb::user_id_t uid_a = symbols[index_a].GetID();
+ lldb::user_id_t uid_b = symbols[index_b].GetID();
+ if (uid_a < uid_b)
+ return true;
+ if (uid_a > uid_b)
+ return false;
+ return false;
+ } else if (value_a < value_b)
+ return true;
+
+ return false;
+ }
+};
+}
+
+void Symtab::SortSymbolIndexesByValue(std::vector<uint32_t> &indexes,
+ bool remove_duplicates) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+ // No need to sort if we have zero or one items...
+ if (indexes.size() <= 1)
+ return;
+
+ // Sort the indexes in place using std::stable_sort.
+ // NOTE: The use of std::stable_sort instead of llvm::sort here is strictly
+ // for performance, not correctness. The indexes vector tends to be "close"
+ // to sorted, which the stable sort handles better.
+
+ std::vector<lldb::addr_t> addr_cache(m_symbols.size(), LLDB_INVALID_ADDRESS);
+
+ SymbolIndexComparator comparator(m_symbols, addr_cache);
+ std::stable_sort(indexes.begin(), indexes.end(), comparator);
+
+ // Remove any duplicates if requested
+ if (remove_duplicates) {
+ auto last = std::unique(indexes.begin(), indexes.end());
+ indexes.erase(last, indexes.end());
+ }
+}
+
+uint32_t Symtab::AppendSymbolIndexesWithName(ConstString symbol_name,
+ std::vector<uint32_t> &indexes) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s", LLVM_PRETTY_FUNCTION);
+ if (symbol_name) {
+ if (!m_name_indexes_computed)
+ InitNameIndexes();
+
+ return m_name_to_index.GetValues(symbol_name, indexes);
+ }
+ return 0;
+}
+
+uint32_t Symtab::AppendSymbolIndexesWithName(ConstString symbol_name,
+ Debug symbol_debug_type,
+ Visibility symbol_visibility,
+ std::vector<uint32_t> &indexes) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s", LLVM_PRETTY_FUNCTION);
+ if (symbol_name) {
+ const size_t old_size = indexes.size();
+ if (!m_name_indexes_computed)
+ InitNameIndexes();
+
+ std::vector<uint32_t> all_name_indexes;
+ const size_t name_match_count =
+ m_name_to_index.GetValues(symbol_name, all_name_indexes);
+ for (size_t i = 0; i < name_match_count; ++i) {
+ if (CheckSymbolAtIndex(all_name_indexes[i], symbol_debug_type,
+ symbol_visibility))
+ indexes.push_back(all_name_indexes[i]);
+ }
+ return indexes.size() - old_size;
+ }
+ return 0;
+}
+
+uint32_t
+Symtab::AppendSymbolIndexesWithNameAndType(ConstString symbol_name,
+ SymbolType symbol_type,
+ std::vector<uint32_t> &indexes) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ if (AppendSymbolIndexesWithName(symbol_name, indexes) > 0) {
+ std::vector<uint32_t>::iterator pos = indexes.begin();
+ while (pos != indexes.end()) {
+ if (symbol_type == eSymbolTypeAny ||
+ m_symbols[*pos].GetType() == symbol_type)
+ ++pos;
+ else
+ pos = indexes.erase(pos);
+ }
+ }
+ return indexes.size();
+}
+
+uint32_t Symtab::AppendSymbolIndexesWithNameAndType(
+ ConstString symbol_name, SymbolType symbol_type,
+ Debug symbol_debug_type, Visibility symbol_visibility,
+ std::vector<uint32_t> &indexes) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ if (AppendSymbolIndexesWithName(symbol_name, symbol_debug_type,
+ symbol_visibility, indexes) > 0) {
+ std::vector<uint32_t>::iterator pos = indexes.begin();
+ while (pos != indexes.end()) {
+ if (symbol_type == eSymbolTypeAny ||
+ m_symbols[*pos].GetType() == symbol_type)
+ ++pos;
+ else
+ pos = indexes.erase(pos);
+ }
+ }
+ return indexes.size();
+}
+
+uint32_t Symtab::AppendSymbolIndexesMatchingRegExAndType(
+ const RegularExpression &regexp, SymbolType symbol_type,
+ std::vector<uint32_t> &indexes) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ uint32_t prev_size = indexes.size();
+ uint32_t sym_end = m_symbols.size();
+
+ for (uint32_t i = 0; i < sym_end; i++) {
+ if (symbol_type == eSymbolTypeAny ||
+ m_symbols[i].GetType() == symbol_type) {
+ const char *name = m_symbols[i].GetName().AsCString();
+ if (name) {
+ if (regexp.Execute(name))
+ indexes.push_back(i);
+ }
+ }
+ }
+ return indexes.size() - prev_size;
+}
+
+uint32_t Symtab::AppendSymbolIndexesMatchingRegExAndType(
+ const RegularExpression &regexp, SymbolType symbol_type,
+ Debug symbol_debug_type, Visibility symbol_visibility,
+ std::vector<uint32_t> &indexes) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ uint32_t prev_size = indexes.size();
+ uint32_t sym_end = m_symbols.size();
+
+ for (uint32_t i = 0; i < sym_end; i++) {
+ if (symbol_type == eSymbolTypeAny ||
+ m_symbols[i].GetType() == symbol_type) {
+ if (!CheckSymbolAtIndex(i, symbol_debug_type, symbol_visibility))
+ continue;
+
+ const char *name = m_symbols[i].GetName().AsCString();
+ if (name) {
+ if (regexp.Execute(name))
+ indexes.push_back(i);
+ }
+ }
+ }
+ return indexes.size() - prev_size;
+}
+
+Symbol *Symtab::FindSymbolWithType(SymbolType symbol_type,
+ Debug symbol_debug_type,
+ Visibility symbol_visibility,
+ uint32_t &start_idx) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ const size_t count = m_symbols.size();
+ for (size_t idx = start_idx; idx < count; ++idx) {
+ if (symbol_type == eSymbolTypeAny ||
+ m_symbols[idx].GetType() == symbol_type) {
+ if (CheckSymbolAtIndex(idx, symbol_debug_type, symbol_visibility)) {
+ start_idx = idx;
+ return &m_symbols[idx];
+ }
+ }
+ }
+ return nullptr;
+}
+
+size_t
+Symtab::FindAllSymbolsWithNameAndType(ConstString name,
+ SymbolType symbol_type,
+ std::vector<uint32_t> &symbol_indexes) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s", LLVM_PRETTY_FUNCTION);
+ // Initialize all of the lookup by name indexes before converting NAME to a
+ // uniqued string NAME_STR below.
+ if (!m_name_indexes_computed)
+ InitNameIndexes();
+
+ if (name) {
+ // The string table did have a string that matched, but we need to check
+ // the symbols and match the symbol_type if any was given.
+ AppendSymbolIndexesWithNameAndType(name, symbol_type, symbol_indexes);
+ }
+ return symbol_indexes.size();
+}
+
+size_t Symtab::FindAllSymbolsWithNameAndType(
+ ConstString name, SymbolType symbol_type, Debug symbol_debug_type,
+ Visibility symbol_visibility, std::vector<uint32_t> &symbol_indexes) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s", LLVM_PRETTY_FUNCTION);
+ // Initialize all of the lookup by name indexes before converting NAME to a
+ // uniqued string NAME_STR below.
+ if (!m_name_indexes_computed)
+ InitNameIndexes();
+
+ if (name) {
+ // The string table did have a string that matched, but we need to check
+ // the symbols and match the symbol_type if any was given.
+ AppendSymbolIndexesWithNameAndType(name, symbol_type, symbol_debug_type,
+ symbol_visibility, symbol_indexes);
+ }
+ return symbol_indexes.size();
+}
+
+size_t Symtab::FindAllSymbolsMatchingRexExAndType(
+ const RegularExpression &regex, SymbolType symbol_type,
+ Debug symbol_debug_type, Visibility symbol_visibility,
+ std::vector<uint32_t> &symbol_indexes) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ AppendSymbolIndexesMatchingRegExAndType(regex, symbol_type, symbol_debug_type,
+ symbol_visibility, symbol_indexes);
+ return symbol_indexes.size();
+}
+
+Symbol *Symtab::FindFirstSymbolWithNameAndType(ConstString name,
+ SymbolType symbol_type,
+ Debug symbol_debug_type,
+ Visibility symbol_visibility) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s", LLVM_PRETTY_FUNCTION);
+ if (!m_name_indexes_computed)
+ InitNameIndexes();
+
+ if (name) {
+ std::vector<uint32_t> matching_indexes;
+ // The string table did have a string that matched, but we need to check
+ // the symbols and match the symbol_type if any was given.
+ if (AppendSymbolIndexesWithNameAndType(name, symbol_type, symbol_debug_type,
+ symbol_visibility,
+ matching_indexes)) {
+ std::vector<uint32_t>::const_iterator pos, end = matching_indexes.end();
+ for (pos = matching_indexes.begin(); pos != end; ++pos) {
+ Symbol *symbol = SymbolAtIndex(*pos);
+
+ if (symbol->Compare(name, symbol_type))
+ return symbol;
+ }
+ }
+ }
+ return nullptr;
+}
+
+typedef struct {
+ const Symtab *symtab;
+ const addr_t file_addr;
+ Symbol *match_symbol;
+ const uint32_t *match_index_ptr;
+ addr_t match_offset;
+} SymbolSearchInfo;
+
+// Add all the section file start address & size to the RangeVector, recusively
+// adding any children sections.
+static void AddSectionsToRangeMap(SectionList *sectlist,
+ RangeVector<addr_t, addr_t> &section_ranges) {
+ const int num_sections = sectlist->GetNumSections(0);
+ for (int i = 0; i < num_sections; i++) {
+ SectionSP sect_sp = sectlist->GetSectionAtIndex(i);
+ if (sect_sp) {
+ SectionList &child_sectlist = sect_sp->GetChildren();
+
+ // If this section has children, add the children to the RangeVector.
+ // Else add this section to the RangeVector.
+ if (child_sectlist.GetNumSections(0) > 0) {
+ AddSectionsToRangeMap(&child_sectlist, section_ranges);
+ } else {
+ size_t size = sect_sp->GetByteSize();
+ if (size > 0) {
+ addr_t base_addr = sect_sp->GetFileAddress();
+ RangeVector<addr_t, addr_t>::Entry entry;
+ entry.SetRangeBase(base_addr);
+ entry.SetByteSize(size);
+ section_ranges.Append(entry);
+ }
+ }
+ }
+ }
+}
+
+void Symtab::InitAddressIndexes() {
+ // Protected function, no need to lock mutex...
+ if (!m_file_addr_to_index_computed && !m_symbols.empty()) {
+ m_file_addr_to_index_computed = true;
+
+ FileRangeToIndexMap::Entry entry;
+ const_iterator begin = m_symbols.begin();
+ const_iterator end = m_symbols.end();
+ for (const_iterator pos = m_symbols.begin(); pos != end; ++pos) {
+ if (pos->ValueIsAddress()) {
+ entry.SetRangeBase(pos->GetAddressRef().GetFileAddress());
+ entry.SetByteSize(pos->GetByteSize());
+ entry.data = std::distance(begin, pos);
+ m_file_addr_to_index.Append(entry);
+ }
+ }
+ const size_t num_entries = m_file_addr_to_index.GetSize();
+ if (num_entries > 0) {
+ m_file_addr_to_index.Sort();
+
+ // Create a RangeVector with the start & size of all the sections for
+ // this objfile. We'll need to check this for any FileRangeToIndexMap
+ // entries with an uninitialized size, which could potentially be a large
+ // number so reconstituting the weak pointer is busywork when it is
+ // invariant information.
+ SectionList *sectlist = m_objfile->GetSectionList();
+ RangeVector<addr_t, addr_t> section_ranges;
+ if (sectlist) {
+ AddSectionsToRangeMap(sectlist, section_ranges);
+ section_ranges.Sort();
+ }
+
+ // Iterate through the FileRangeToIndexMap and fill in the size for any
+ // entries that didn't already have a size from the Symbol (e.g. if we
+ // have a plain linker symbol with an address only, instead of debug info
+ // where we get an address and a size and a type, etc.)
+ for (size_t i = 0; i < num_entries; i++) {
+ FileRangeToIndexMap::Entry *entry =
+ m_file_addr_to_index.GetMutableEntryAtIndex(i);
+ if (entry->GetByteSize() > 0)
+ continue;
+ addr_t curr_base_addr = entry->GetRangeBase();
+ // Symbols with non-zero size will show after zero-sized symbols on the
+ // same address. So do not set size of a non-last zero-sized symbol.
+ if (i == num_entries - 1 ||
+ m_file_addr_to_index.GetMutableEntryAtIndex(i + 1)
+ ->GetRangeBase() != curr_base_addr) {
+ const RangeVector<addr_t, addr_t>::Entry *containing_section =
+ section_ranges.FindEntryThatContains(curr_base_addr);
+
+ // Use the end of the section as the default max size of the symbol
+ addr_t sym_size = 0;
+ if (containing_section) {
+ sym_size =
+ containing_section->GetByteSize() -
+ (entry->GetRangeBase() - containing_section->GetRangeBase());
+ }
+
+ for (size_t j = i; j < num_entries; j++) {
+ FileRangeToIndexMap::Entry *next_entry =
+ m_file_addr_to_index.GetMutableEntryAtIndex(j);
+ addr_t next_base_addr = next_entry->GetRangeBase();
+ if (next_base_addr > curr_base_addr) {
+ addr_t size_to_next_symbol = next_base_addr - curr_base_addr;
+
+ // Take the difference between this symbol and the next one as
+ // its size, if it is less than the size of the section.
+ if (sym_size == 0 || size_to_next_symbol < sym_size) {
+ sym_size = size_to_next_symbol;
+ }
+ break;
+ }
+ }
+
+ if (sym_size > 0) {
+ entry->SetByteSize(sym_size);
+ Symbol &symbol = m_symbols[entry->data];
+ symbol.SetByteSize(sym_size);
+ symbol.SetSizeIsSynthesized(true);
+ }
+ }
+ }
+
+ // Sort again in case the range size changes the ordering
+ m_file_addr_to_index.Sort();
+ }
+ }
+}
+
+void Symtab::CalculateSymbolSizes() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ // Size computation happens inside InitAddressIndexes.
+ InitAddressIndexes();
+}
+
+Symbol *Symtab::FindSymbolAtFileAddress(addr_t file_addr) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_file_addr_to_index_computed)
+ InitAddressIndexes();
+
+ const FileRangeToIndexMap::Entry *entry =
+ m_file_addr_to_index.FindEntryStartsAt(file_addr);
+ if (entry) {
+ Symbol *symbol = SymbolAtIndex(entry->data);
+ if (symbol->GetFileAddress() == file_addr)
+ return symbol;
+ }
+ return nullptr;
+}
+
+Symbol *Symtab::FindSymbolContainingFileAddress(addr_t file_addr) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ if (!m_file_addr_to_index_computed)
+ InitAddressIndexes();
+
+ const FileRangeToIndexMap::Entry *entry =
+ m_file_addr_to_index.FindEntryThatContains(file_addr);
+ if (entry) {
+ Symbol *symbol = SymbolAtIndex(entry->data);
+ if (symbol->ContainsFileAddress(file_addr))
+ return symbol;
+ }
+ return nullptr;
+}
+
+void Symtab::ForEachSymbolContainingFileAddress(
+ addr_t file_addr, std::function<bool(Symbol *)> const &callback) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ if (!m_file_addr_to_index_computed)
+ InitAddressIndexes();
+
+ std::vector<uint32_t> all_addr_indexes;
+
+ // Get all symbols with file_addr
+ const size_t addr_match_count =
+ m_file_addr_to_index.FindEntryIndexesThatContain(file_addr,
+ all_addr_indexes);
+
+ for (size_t i = 0; i < addr_match_count; ++i) {
+ Symbol *symbol = SymbolAtIndex(all_addr_indexes[i]);
+ if (symbol->ContainsFileAddress(file_addr)) {
+ if (!callback(symbol))
+ break;
+ }
+ }
+}
+
+void Symtab::SymbolIndicesToSymbolContextList(
+ std::vector<uint32_t> &symbol_indexes, SymbolContextList &sc_list) {
+ // No need to protect this call using m_mutex all other method calls are
+ // already thread safe.
+
+ const bool merge_symbol_into_function = true;
+ size_t num_indices = symbol_indexes.size();
+ if (num_indices > 0) {
+ SymbolContext sc;
+ sc.module_sp = m_objfile->GetModule();
+ for (size_t i = 0; i < num_indices; i++) {
+ sc.symbol = SymbolAtIndex(symbol_indexes[i]);
+ if (sc.symbol)
+ sc_list.AppendIfUnique(sc, merge_symbol_into_function);
+ }
+ }
+}
+
+size_t Symtab::FindFunctionSymbols(ConstString name,
+ uint32_t name_type_mask,
+ SymbolContextList &sc_list) {
+ size_t count = 0;
+ std::vector<uint32_t> symbol_indexes;
+
+ // eFunctionNameTypeAuto should be pre-resolved by a call to
+ // Module::LookupInfo::LookupInfo()
+ assert((name_type_mask & eFunctionNameTypeAuto) == 0);
+
+ if (name_type_mask & (eFunctionNameTypeBase | eFunctionNameTypeFull)) {
+ std::vector<uint32_t> temp_symbol_indexes;
+ FindAllSymbolsWithNameAndType(name, eSymbolTypeAny, temp_symbol_indexes);
+
+ unsigned temp_symbol_indexes_size = temp_symbol_indexes.size();
+ if (temp_symbol_indexes_size > 0) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (unsigned i = 0; i < temp_symbol_indexes_size; i++) {
+ SymbolContext sym_ctx;
+ sym_ctx.symbol = SymbolAtIndex(temp_symbol_indexes[i]);
+ if (sym_ctx.symbol) {
+ switch (sym_ctx.symbol->GetType()) {
+ case eSymbolTypeCode:
+ case eSymbolTypeResolver:
+ case eSymbolTypeReExported:
+ symbol_indexes.push_back(temp_symbol_indexes[i]);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (name_type_mask & eFunctionNameTypeBase) {
+ // From mangled names we can't tell what is a basename and what is a method
+ // name, so we just treat them the same
+ if (!m_name_indexes_computed)
+ InitNameIndexes();
+
+ if (!m_basename_to_index.IsEmpty()) {
+ const UniqueCStringMap<uint32_t>::Entry *match;
+ for (match = m_basename_to_index.FindFirstValueForName(name);
+ match != nullptr;
+ match = m_basename_to_index.FindNextValueForName(match)) {
+ symbol_indexes.push_back(match->value);
+ }
+ }
+ }
+
+ if (name_type_mask & eFunctionNameTypeMethod) {
+ if (!m_name_indexes_computed)
+ InitNameIndexes();
+
+ if (!m_method_to_index.IsEmpty()) {
+ const UniqueCStringMap<uint32_t>::Entry *match;
+ for (match = m_method_to_index.FindFirstValueForName(name);
+ match != nullptr;
+ match = m_method_to_index.FindNextValueForName(match)) {
+ symbol_indexes.push_back(match->value);
+ }
+ }
+ }
+
+ if (name_type_mask & eFunctionNameTypeSelector) {
+ if (!m_name_indexes_computed)
+ InitNameIndexes();
+
+ if (!m_selector_to_index.IsEmpty()) {
+ const UniqueCStringMap<uint32_t>::Entry *match;
+ for (match = m_selector_to_index.FindFirstValueForName(name);
+ match != nullptr;
+ match = m_selector_to_index.FindNextValueForName(match)) {
+ symbol_indexes.push_back(match->value);
+ }
+ }
+ }
+
+ if (!symbol_indexes.empty()) {
+ llvm::sort(symbol_indexes.begin(), symbol_indexes.end());
+ symbol_indexes.erase(
+ std::unique(symbol_indexes.begin(), symbol_indexes.end()),
+ symbol_indexes.end());
+ count = symbol_indexes.size();
+ SymbolIndicesToSymbolContextList(symbol_indexes, sc_list);
+ }
+
+ return count;
+}
+
+const Symbol *Symtab::GetParent(Symbol *child_symbol) const {
+ uint32_t child_idx = GetIndexForSymbol(child_symbol);
+ if (child_idx != UINT32_MAX && child_idx > 0) {
+ for (uint32_t idx = child_idx - 1; idx != UINT32_MAX; --idx) {
+ const Symbol *symbol = SymbolAtIndex(idx);
+ const uint32_t sibling_idx = symbol->GetSiblingIndex();
+ if (sibling_idx != UINT32_MAX && sibling_idx > child_idx)
+ return symbol;
+ }
+ }
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/Type.cpp b/contrib/llvm-project/lldb/source/Symbol/Type.cpp
new file mode 100644
index 000000000000..4ee8330ce288
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/Type.cpp
@@ -0,0 +1,1066 @@
+//===-- Type.cpp ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdio.h>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContextScope.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/TypeSystem.h"
+
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void CompilerContext::Dump() const {
+ switch (type) {
+ case CompilerContextKind::Invalid:
+ printf("Invalid");
+ break;
+ case CompilerContextKind::TranslationUnit:
+ printf("TranslationUnit");
+ break;
+ case CompilerContextKind::Module:
+ printf("Module");
+ break;
+ case CompilerContextKind::Namespace:
+ printf("Namespace");
+ break;
+ case CompilerContextKind::Class:
+ printf("Class");
+ break;
+ case CompilerContextKind::Structure:
+ printf("Structure");
+ break;
+ case CompilerContextKind::Union:
+ printf("Union");
+ break;
+ case CompilerContextKind::Function:
+ printf("Function");
+ break;
+ case CompilerContextKind::Variable:
+ printf("Variable");
+ break;
+ case CompilerContextKind::Enumeration:
+ printf("Enumeration");
+ break;
+ case CompilerContextKind::Typedef:
+ printf("Typedef");
+ break;
+ }
+ printf("(\"%s\")\n", name.GetCString());
+}
+
+class TypeAppendVisitor {
+public:
+ TypeAppendVisitor(TypeListImpl &type_list) : m_type_list(type_list) {}
+
+ bool operator()(const lldb::TypeSP &type) {
+ m_type_list.Append(TypeImplSP(new TypeImpl(type)));
+ return true;
+ }
+
+private:
+ TypeListImpl &m_type_list;
+};
+
+void TypeListImpl::Append(const lldb_private::TypeList &type_list) {
+ TypeAppendVisitor cb(*this);
+ type_list.ForEach(cb);
+}
+
+SymbolFileType::SymbolFileType(SymbolFile &symbol_file,
+ const lldb::TypeSP &type_sp)
+ : UserID(type_sp ? type_sp->GetID() : LLDB_INVALID_UID),
+ m_symbol_file(symbol_file), m_type_sp(type_sp) {}
+
+Type *SymbolFileType::GetType() {
+ if (!m_type_sp) {
+ Type *resolved_type = m_symbol_file.ResolveTypeUID(GetID());
+ if (resolved_type)
+ m_type_sp = resolved_type->shared_from_this();
+ }
+ return m_type_sp.get();
+}
+
+Type::Type(lldb::user_id_t uid, SymbolFile *symbol_file,
+ ConstString name, llvm::Optional<uint64_t> byte_size,
+ SymbolContextScope *context, user_id_t encoding_uid,
+ EncodingDataType encoding_uid_type, const Declaration &decl,
+ const CompilerType &compiler_type,
+ ResolveState compiler_type_resolve_state)
+ : std::enable_shared_from_this<Type>(), UserID(uid), m_name(name),
+ m_symbol_file(symbol_file), m_context(context), m_encoding_type(nullptr),
+ m_encoding_uid(encoding_uid), m_encoding_uid_type(encoding_uid_type),
+ m_decl(decl), m_compiler_type(compiler_type) {
+ if (byte_size) {
+ m_byte_size = *byte_size;
+ m_byte_size_has_value = true;
+ } else {
+ m_byte_size = 0;
+ m_byte_size_has_value = false;
+ }
+ m_flags.compiler_type_resolve_state =
+ (compiler_type ? compiler_type_resolve_state : eResolveStateUnresolved);
+ m_flags.is_complete_objc_class = false;
+}
+
+Type::Type()
+ : std::enable_shared_from_this<Type>(), UserID(0), m_name("<INVALID TYPE>"),
+ m_symbol_file(nullptr), m_context(nullptr), m_encoding_type(nullptr),
+ m_encoding_uid(LLDB_INVALID_UID), m_encoding_uid_type(eEncodingInvalid),
+ m_byte_size(0), m_byte_size_has_value(false), m_decl(),
+ m_compiler_type() {
+ m_flags.compiler_type_resolve_state = eResolveStateUnresolved;
+ m_flags.is_complete_objc_class = false;
+}
+
+void Type::GetDescription(Stream *s, lldb::DescriptionLevel level,
+ bool show_name) {
+ *s << "id = " << (const UserID &)*this;
+
+ // Call the name accessor to make sure we resolve the type name
+ if (show_name) {
+ ConstString type_name = GetName();
+ if (type_name) {
+ *s << ", name = \"" << type_name << '"';
+ ConstString qualified_type_name(GetQualifiedName());
+ if (qualified_type_name != type_name) {
+ *s << ", qualified = \"" << qualified_type_name << '"';
+ }
+ }
+ }
+
+ // Call the get byte size accesor so we resolve our byte size
+ if (GetByteSize())
+ s->Printf(", byte-size = %" PRIu64, m_byte_size);
+ bool show_fullpaths = (level == lldb::eDescriptionLevelVerbose);
+ m_decl.Dump(s, show_fullpaths);
+
+ if (m_compiler_type.IsValid()) {
+ *s << ", compiler_type = \"";
+ GetForwardCompilerType().DumpTypeDescription(s);
+ *s << '"';
+ } else if (m_encoding_uid != LLDB_INVALID_UID) {
+ s->Printf(", type_uid = 0x%8.8" PRIx64, m_encoding_uid);
+ switch (m_encoding_uid_type) {
+ case eEncodingInvalid:
+ break;
+ case eEncodingIsUID:
+ s->PutCString(" (unresolved type)");
+ break;
+ case eEncodingIsConstUID:
+ s->PutCString(" (unresolved const type)");
+ break;
+ case eEncodingIsRestrictUID:
+ s->PutCString(" (unresolved restrict type)");
+ break;
+ case eEncodingIsVolatileUID:
+ s->PutCString(" (unresolved volatile type)");
+ break;
+ case eEncodingIsTypedefUID:
+ s->PutCString(" (unresolved typedef)");
+ break;
+ case eEncodingIsPointerUID:
+ s->PutCString(" (unresolved pointer)");
+ break;
+ case eEncodingIsLValueReferenceUID:
+ s->PutCString(" (unresolved L value reference)");
+ break;
+ case eEncodingIsRValueReferenceUID:
+ s->PutCString(" (unresolved R value reference)");
+ break;
+ case eEncodingIsSyntheticUID:
+ s->PutCString(" (synthetic type)");
+ break;
+ }
+ }
+}
+
+void Type::Dump(Stream *s, bool show_context) {
+ s->Printf("%p: ", static_cast<void *>(this));
+ s->Indent();
+ *s << "Type" << static_cast<const UserID &>(*this) << ' ';
+ if (m_name)
+ *s << ", name = \"" << m_name << "\"";
+
+ if (m_byte_size_has_value)
+ s->Printf(", size = %" PRIu64, m_byte_size);
+
+ if (show_context && m_context != nullptr) {
+ s->PutCString(", context = ( ");
+ m_context->DumpSymbolContext(s);
+ s->PutCString(" )");
+ }
+
+ bool show_fullpaths = false;
+ m_decl.Dump(s, show_fullpaths);
+
+ if (m_compiler_type.IsValid()) {
+ *s << ", compiler_type = " << m_compiler_type.GetOpaqueQualType() << ' ';
+ GetForwardCompilerType().DumpTypeDescription(s);
+ } else if (m_encoding_uid != LLDB_INVALID_UID) {
+ *s << ", type_data = " << (uint64_t)m_encoding_uid;
+ switch (m_encoding_uid_type) {
+ case eEncodingInvalid:
+ break;
+ case eEncodingIsUID:
+ s->PutCString(" (unresolved type)");
+ break;
+ case eEncodingIsConstUID:
+ s->PutCString(" (unresolved const type)");
+ break;
+ case eEncodingIsRestrictUID:
+ s->PutCString(" (unresolved restrict type)");
+ break;
+ case eEncodingIsVolatileUID:
+ s->PutCString(" (unresolved volatile type)");
+ break;
+ case eEncodingIsTypedefUID:
+ s->PutCString(" (unresolved typedef)");
+ break;
+ case eEncodingIsPointerUID:
+ s->PutCString(" (unresolved pointer)");
+ break;
+ case eEncodingIsLValueReferenceUID:
+ s->PutCString(" (unresolved L value reference)");
+ break;
+ case eEncodingIsRValueReferenceUID:
+ s->PutCString(" (unresolved R value reference)");
+ break;
+ case eEncodingIsSyntheticUID:
+ s->PutCString(" (synthetic type)");
+ break;
+ }
+ }
+
+ //
+ // if (m_access)
+ // s->Printf(", access = %u", m_access);
+ s->EOL();
+}
+
+ConstString Type::GetName() {
+ if (!m_name)
+ m_name = GetForwardCompilerType().GetConstTypeName();
+ return m_name;
+}
+
+void Type::DumpTypeName(Stream *s) { GetName().Dump(s, "<invalid-type-name>"); }
+
+void Type::DumpValue(ExecutionContext *exe_ctx, Stream *s,
+ const DataExtractor &data, uint32_t data_byte_offset,
+ bool show_types, bool show_summary, bool verbose,
+ lldb::Format format) {
+ if (ResolveClangType(eResolveStateForward)) {
+ if (show_types) {
+ s->PutChar('(');
+ if (verbose)
+ s->Printf("Type{0x%8.8" PRIx64 "} ", GetID());
+ DumpTypeName(s);
+ s->PutCString(") ");
+ }
+
+ GetForwardCompilerType().DumpValue(
+ exe_ctx, s, format == lldb::eFormatDefault ? GetFormat() : format, data,
+ data_byte_offset, GetByteSize().getValueOr(0),
+ 0, // Bitfield bit size
+ 0, // Bitfield bit offset
+ show_types, show_summary, verbose, 0);
+ }
+}
+
+Type *Type::GetEncodingType() {
+ if (m_encoding_type == nullptr && m_encoding_uid != LLDB_INVALID_UID)
+ m_encoding_type = m_symbol_file->ResolveTypeUID(m_encoding_uid);
+ return m_encoding_type;
+}
+
+llvm::Optional<uint64_t> Type::GetByteSize() {
+ if (m_byte_size_has_value)
+ return m_byte_size;
+
+ switch (m_encoding_uid_type) {
+ case eEncodingInvalid:
+ case eEncodingIsSyntheticUID:
+ break;
+ case eEncodingIsUID:
+ case eEncodingIsConstUID:
+ case eEncodingIsRestrictUID:
+ case eEncodingIsVolatileUID:
+ case eEncodingIsTypedefUID: {
+ Type *encoding_type = GetEncodingType();
+ if (encoding_type)
+ if (llvm::Optional<uint64_t> size = encoding_type->GetByteSize()) {
+ m_byte_size = *size;
+ m_byte_size_has_value = true;
+ return m_byte_size;
+ }
+
+ if (llvm::Optional<uint64_t> size =
+ GetLayoutCompilerType().GetByteSize(nullptr)) {
+ m_byte_size = *size;
+ m_byte_size_has_value = true;
+ return m_byte_size;
+ }
+ } break;
+
+ // If we are a pointer or reference, then this is just a pointer size;
+ case eEncodingIsPointerUID:
+ case eEncodingIsLValueReferenceUID:
+ case eEncodingIsRValueReferenceUID: {
+ if (ArchSpec arch = m_symbol_file->GetObjectFile()->GetArchitecture()) {
+ m_byte_size = arch.GetAddressByteSize();
+ m_byte_size_has_value = true;
+ }
+ } break;
+ }
+ return {};
+}
+
+uint32_t Type::GetNumChildren(bool omit_empty_base_classes) {
+ return GetForwardCompilerType().GetNumChildren(omit_empty_base_classes, nullptr);
+}
+
+bool Type::IsAggregateType() {
+ return GetForwardCompilerType().IsAggregateType();
+}
+
+lldb::TypeSP Type::GetTypedefType() {
+ lldb::TypeSP type_sp;
+ if (IsTypedef()) {
+ Type *typedef_type = m_symbol_file->ResolveTypeUID(m_encoding_uid);
+ if (typedef_type)
+ type_sp = typedef_type->shared_from_this();
+ }
+ return type_sp;
+}
+
+lldb::Format Type::GetFormat() { return GetForwardCompilerType().GetFormat(); }
+
+lldb::Encoding Type::GetEncoding(uint64_t &count) {
+ // Make sure we resolve our type if it already hasn't been.
+ return GetForwardCompilerType().GetEncoding(count);
+}
+
+bool Type::DumpValueInMemory(ExecutionContext *exe_ctx, Stream *s,
+ lldb::addr_t address, AddressType address_type,
+ bool show_types, bool show_summary, bool verbose) {
+ if (address != LLDB_INVALID_ADDRESS) {
+ DataExtractor data;
+ Target *target = nullptr;
+ if (exe_ctx)
+ target = exe_ctx->GetTargetPtr();
+ if (target)
+ data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ if (ReadFromMemory(exe_ctx, address, address_type, data)) {
+ DumpValue(exe_ctx, s, data, 0, show_types, show_summary, verbose);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Type::ReadFromMemory(ExecutionContext *exe_ctx, lldb::addr_t addr,
+ AddressType address_type, DataExtractor &data) {
+ if (address_type == eAddressTypeFile) {
+ // Can't convert a file address to anything valid without more context
+ // (which Module it came from)
+ return false;
+ }
+
+ const uint64_t byte_size = GetByteSize().getValueOr(0);
+ if (data.GetByteSize() < byte_size) {
+ lldb::DataBufferSP data_sp(new DataBufferHeap(byte_size, '\0'));
+ data.SetData(data_sp);
+ }
+
+ uint8_t *dst = const_cast<uint8_t *>(data.PeekData(0, byte_size));
+ if (dst != nullptr) {
+ if (address_type == eAddressTypeHost) {
+ // The address is an address in this process, so just copy it
+ if (addr == 0)
+ return false;
+ memcpy(dst, reinterpret_cast<uint8_t *>(addr), byte_size);
+ return true;
+ } else {
+ if (exe_ctx) {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process) {
+ Status error;
+ return exe_ctx->GetProcessPtr()->ReadMemory(addr, dst, byte_size,
+ error) == byte_size;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool Type::WriteToMemory(ExecutionContext *exe_ctx, lldb::addr_t addr,
+ AddressType address_type, DataExtractor &data) {
+ return false;
+}
+
+TypeList *Type::GetTypeList() { return GetSymbolFile()->GetTypeList(); }
+
+const Declaration &Type::GetDeclaration() const { return m_decl; }
+
+bool Type::ResolveClangType(ResolveState compiler_type_resolve_state) {
+ // TODO: This needs to consider the correct type system to use.
+ Type *encoding_type = nullptr;
+ if (!m_compiler_type.IsValid()) {
+ encoding_type = GetEncodingType();
+ if (encoding_type) {
+ switch (m_encoding_uid_type) {
+ case eEncodingIsUID: {
+ CompilerType encoding_compiler_type =
+ encoding_type->GetForwardCompilerType();
+ if (encoding_compiler_type.IsValid()) {
+ m_compiler_type = encoding_compiler_type;
+ m_flags.compiler_type_resolve_state =
+ encoding_type->m_flags.compiler_type_resolve_state;
+ }
+ } break;
+
+ case eEncodingIsConstUID:
+ m_compiler_type =
+ encoding_type->GetForwardCompilerType().AddConstModifier();
+ break;
+
+ case eEncodingIsRestrictUID:
+ m_compiler_type =
+ encoding_type->GetForwardCompilerType().AddRestrictModifier();
+ break;
+
+ case eEncodingIsVolatileUID:
+ m_compiler_type =
+ encoding_type->GetForwardCompilerType().AddVolatileModifier();
+ break;
+
+ case eEncodingIsTypedefUID:
+ m_compiler_type = encoding_type->GetForwardCompilerType().CreateTypedef(
+ m_name.AsCString("__lldb_invalid_typedef_name"),
+ GetSymbolFile()->GetDeclContextContainingUID(GetID()));
+ m_name.Clear();
+ break;
+
+ case eEncodingIsPointerUID:
+ m_compiler_type =
+ encoding_type->GetForwardCompilerType().GetPointerType();
+ break;
+
+ case eEncodingIsLValueReferenceUID:
+ m_compiler_type =
+ encoding_type->GetForwardCompilerType().GetLValueReferenceType();
+ break;
+
+ case eEncodingIsRValueReferenceUID:
+ m_compiler_type =
+ encoding_type->GetForwardCompilerType().GetRValueReferenceType();
+ break;
+
+ default:
+ llvm_unreachable("Unhandled encoding_data_type.");
+ }
+ } else {
+ // We have no encoding type, return void?
+ TypeSystem *type_system =
+ m_symbol_file->GetTypeSystemForLanguage(eLanguageTypeC);
+ CompilerType void_compiler_type =
+ type_system->GetBasicTypeFromAST(eBasicTypeVoid);
+ switch (m_encoding_uid_type) {
+ case eEncodingIsUID:
+ m_compiler_type = void_compiler_type;
+ break;
+
+ case eEncodingIsConstUID:
+ m_compiler_type = void_compiler_type.AddConstModifier();
+ break;
+
+ case eEncodingIsRestrictUID:
+ m_compiler_type = void_compiler_type.AddRestrictModifier();
+ break;
+
+ case eEncodingIsVolatileUID:
+ m_compiler_type = void_compiler_type.AddVolatileModifier();
+ break;
+
+ case eEncodingIsTypedefUID:
+ m_compiler_type = void_compiler_type.CreateTypedef(
+ m_name.AsCString("__lldb_invalid_typedef_name"),
+ GetSymbolFile()->GetDeclContextContainingUID(GetID()));
+ break;
+
+ case eEncodingIsPointerUID:
+ m_compiler_type = void_compiler_type.GetPointerType();
+ break;
+
+ case eEncodingIsLValueReferenceUID:
+ m_compiler_type = void_compiler_type.GetLValueReferenceType();
+ break;
+
+ case eEncodingIsRValueReferenceUID:
+ m_compiler_type = void_compiler_type.GetRValueReferenceType();
+ break;
+
+ default:
+ llvm_unreachable("Unhandled encoding_data_type.");
+ }
+ }
+
+ // When we have a EncodingUID, our "m_flags.compiler_type_resolve_state" is
+ // set to eResolveStateUnresolved so we need to update it to say that we
+ // now have a forward declaration since that is what we created above.
+ if (m_compiler_type.IsValid())
+ m_flags.compiler_type_resolve_state = eResolveStateForward;
+ }
+
+ // Check if we have a forward reference to a class/struct/union/enum?
+ if (compiler_type_resolve_state == eResolveStateLayout ||
+ compiler_type_resolve_state == eResolveStateFull) {
+ // Check if we have a forward reference to a class/struct/union/enum?
+ if (m_compiler_type.IsValid() &&
+ m_flags.compiler_type_resolve_state < compiler_type_resolve_state) {
+ m_flags.compiler_type_resolve_state = eResolveStateFull;
+ if (!m_compiler_type.IsDefined()) {
+ // We have a forward declaration, we need to resolve it to a complete
+ // definition.
+ m_symbol_file->CompleteType(m_compiler_type);
+ }
+ }
+ }
+
+ // If we have an encoding type, then we need to make sure it is resolved
+ // appropriately.
+ if (m_encoding_uid != LLDB_INVALID_UID) {
+ if (encoding_type == nullptr)
+ encoding_type = GetEncodingType();
+ if (encoding_type) {
+ ResolveState encoding_compiler_type_resolve_state =
+ compiler_type_resolve_state;
+
+ if (compiler_type_resolve_state == eResolveStateLayout) {
+ switch (m_encoding_uid_type) {
+ case eEncodingIsPointerUID:
+ case eEncodingIsLValueReferenceUID:
+ case eEncodingIsRValueReferenceUID:
+ encoding_compiler_type_resolve_state = eResolveStateForward;
+ break;
+ default:
+ break;
+ }
+ }
+ encoding_type->ResolveClangType(encoding_compiler_type_resolve_state);
+ }
+ }
+ return m_compiler_type.IsValid();
+}
+uint32_t Type::GetEncodingMask() {
+ uint32_t encoding_mask = 1u << m_encoding_uid_type;
+ Type *encoding_type = GetEncodingType();
+ assert(encoding_type != this);
+ if (encoding_type)
+ encoding_mask |= encoding_type->GetEncodingMask();
+ return encoding_mask;
+}
+
+CompilerType Type::GetFullCompilerType() {
+ ResolveClangType(eResolveStateFull);
+ return m_compiler_type;
+}
+
+CompilerType Type::GetLayoutCompilerType() {
+ ResolveClangType(eResolveStateLayout);
+ return m_compiler_type;
+}
+
+CompilerType Type::GetForwardCompilerType() {
+ ResolveClangType(eResolveStateForward);
+ return m_compiler_type;
+}
+
+int Type::Compare(const Type &a, const Type &b) {
+ // Just compare the UID values for now...
+ lldb::user_id_t a_uid = a.GetID();
+ lldb::user_id_t b_uid = b.GetID();
+ if (a_uid < b_uid)
+ return -1;
+ if (a_uid > b_uid)
+ return 1;
+ return 0;
+}
+
+ConstString Type::GetQualifiedName() {
+ return GetForwardCompilerType().GetConstTypeName();
+}
+
+bool Type::GetTypeScopeAndBasename(const llvm::StringRef& name,
+ llvm::StringRef &scope,
+ llvm::StringRef &basename,
+ TypeClass &type_class) {
+ type_class = eTypeClassAny;
+
+ if (name.empty())
+ return false;
+
+ basename = name;
+ if (basename.consume_front("struct "))
+ type_class = eTypeClassStruct;
+ else if (basename.consume_front("class "))
+ type_class = eTypeClassClass;
+ else if (basename.consume_front("union "))
+ type_class = eTypeClassUnion;
+ else if (basename.consume_front("enum "))
+ type_class = eTypeClassEnumeration;
+ else if (basename.consume_front("typedef "))
+ type_class = eTypeClassTypedef;
+
+ size_t namespace_separator = basename.find("::");
+ if (namespace_separator == llvm::StringRef::npos)
+ return false;
+
+ size_t template_begin = basename.find('<');
+ while (namespace_separator != llvm::StringRef::npos) {
+ if (template_begin != llvm::StringRef::npos &&
+ namespace_separator > template_begin) {
+ size_t template_depth = 1;
+ llvm::StringRef template_arg =
+ basename.drop_front(template_begin + 1);
+ while (template_depth > 0 && !template_arg.empty()) {
+ if (template_arg.front() == '<')
+ template_depth++;
+ else if (template_arg.front() == '>')
+ template_depth--;
+ template_arg = template_arg.drop_front(1);
+ }
+ if (template_depth != 0)
+ return false; // We have an invalid type name. Bail out.
+ if (template_arg.empty())
+ break; // The template ends at the end of the full name.
+ basename = template_arg;
+ } else {
+ basename = basename.drop_front(namespace_separator + 2);
+ }
+ template_begin = basename.find('<');
+ namespace_separator = basename.find("::");
+ }
+ if (basename.size() < name.size()) {
+ scope = name.take_front(name.size() - basename.size());
+ return true;
+ }
+ return false;
+}
+
+ModuleSP Type::GetModule() {
+ if (m_symbol_file)
+ return m_symbol_file->GetObjectFile()->GetModule();
+ return ModuleSP();
+}
+
+TypeAndOrName::TypeAndOrName(TypeSP &in_type_sp) {
+ if (in_type_sp) {
+ m_compiler_type = in_type_sp->GetForwardCompilerType();
+ m_type_name = in_type_sp->GetName();
+ }
+}
+
+TypeAndOrName::TypeAndOrName(const char *in_type_str)
+ : m_type_name(in_type_str) {}
+
+TypeAndOrName::TypeAndOrName(ConstString &in_type_const_string)
+ : m_type_name(in_type_const_string) {}
+
+bool TypeAndOrName::operator==(const TypeAndOrName &other) const {
+ if (m_compiler_type != other.m_compiler_type)
+ return false;
+ if (m_type_name != other.m_type_name)
+ return false;
+ return true;
+}
+
+bool TypeAndOrName::operator!=(const TypeAndOrName &other) const {
+ return !(*this == other);
+}
+
+ConstString TypeAndOrName::GetName() const {
+ if (m_type_name)
+ return m_type_name;
+ if (m_compiler_type)
+ return m_compiler_type.GetTypeName();
+ return ConstString("<invalid>");
+}
+
+void TypeAndOrName::SetName(ConstString type_name) {
+ m_type_name = type_name;
+}
+
+void TypeAndOrName::SetName(const char *type_name_cstr) {
+ m_type_name.SetCString(type_name_cstr);
+}
+
+void TypeAndOrName::SetTypeSP(lldb::TypeSP type_sp) {
+ if (type_sp) {
+ m_compiler_type = type_sp->GetForwardCompilerType();
+ m_type_name = type_sp->GetName();
+ } else
+ Clear();
+}
+
+void TypeAndOrName::SetCompilerType(CompilerType compiler_type) {
+ m_compiler_type = compiler_type;
+ if (m_compiler_type)
+ m_type_name = m_compiler_type.GetTypeName();
+}
+
+bool TypeAndOrName::IsEmpty() const {
+ return !((bool)m_type_name || (bool)m_compiler_type);
+}
+
+void TypeAndOrName::Clear() {
+ m_type_name.Clear();
+ m_compiler_type.Clear();
+}
+
+bool TypeAndOrName::HasName() const { return (bool)m_type_name; }
+
+bool TypeAndOrName::HasCompilerType() const {
+ return m_compiler_type.IsValid();
+}
+
+TypeImpl::TypeImpl(const lldb::TypeSP &type_sp)
+ : m_module_wp(), m_static_type(), m_dynamic_type() {
+ SetType(type_sp);
+}
+
+TypeImpl::TypeImpl(const CompilerType &compiler_type)
+ : m_module_wp(), m_static_type(), m_dynamic_type() {
+ SetType(compiler_type);
+}
+
+TypeImpl::TypeImpl(const lldb::TypeSP &type_sp, const CompilerType &dynamic)
+ : m_module_wp(), m_static_type(), m_dynamic_type(dynamic) {
+ SetType(type_sp, dynamic);
+}
+
+TypeImpl::TypeImpl(const CompilerType &static_type,
+ const CompilerType &dynamic_type)
+ : m_module_wp(), m_static_type(), m_dynamic_type() {
+ SetType(static_type, dynamic_type);
+}
+
+void TypeImpl::SetType(const lldb::TypeSP &type_sp) {
+ if (type_sp) {
+ m_static_type = type_sp->GetForwardCompilerType();
+ m_module_wp = type_sp->GetModule();
+ } else {
+ m_static_type.Clear();
+ m_module_wp = lldb::ModuleWP();
+ }
+}
+
+void TypeImpl::SetType(const CompilerType &compiler_type) {
+ m_module_wp = lldb::ModuleWP();
+ m_static_type = compiler_type;
+}
+
+void TypeImpl::SetType(const lldb::TypeSP &type_sp,
+ const CompilerType &dynamic) {
+ SetType(type_sp);
+ m_dynamic_type = dynamic;
+}
+
+void TypeImpl::SetType(const CompilerType &compiler_type,
+ const CompilerType &dynamic) {
+ m_module_wp = lldb::ModuleWP();
+ m_static_type = compiler_type;
+ m_dynamic_type = dynamic;
+}
+
+bool TypeImpl::CheckModule(lldb::ModuleSP &module_sp) const {
+ // Check if we have a module for this type. If we do and the shared pointer
+ // is can be successfully initialized with m_module_wp, return true. Else
+ // return false if we didn't have a module, or if we had a module and it has
+ // been deleted. Any functions doing anything with a TypeSP in this TypeImpl
+ // class should call this function and only do anything with the ivars if
+ // this function returns true. If we have a module, the "module_sp" will be
+ // filled in with a strong reference to the module so that the module will at
+ // least stay around long enough for the type query to succeed.
+ module_sp = m_module_wp.lock();
+ if (!module_sp) {
+ lldb::ModuleWP empty_module_wp;
+ // If either call to "std::weak_ptr::owner_before(...) value returns true,
+ // this indicates that m_module_wp once contained (possibly still does) a
+ // reference to a valid shared pointer. This helps us know if we had a
+ // valid reference to a section which is now invalid because the module it
+ // was in was deleted
+ if (empty_module_wp.owner_before(m_module_wp) ||
+ m_module_wp.owner_before(empty_module_wp)) {
+ // m_module_wp had a valid reference to a module, but all strong
+ // references have been released and the module has been deleted
+ return false;
+ }
+ }
+ // We either successfully locked the module, or didn't have one to begin with
+ return true;
+}
+
+bool TypeImpl::operator==(const TypeImpl &rhs) const {
+ return m_static_type == rhs.m_static_type &&
+ m_dynamic_type == rhs.m_dynamic_type;
+}
+
+bool TypeImpl::operator!=(const TypeImpl &rhs) const {
+ return !(*this == rhs);
+}
+
+bool TypeImpl::IsValid() const {
+ // just a name is not valid
+ ModuleSP module_sp;
+ if (CheckModule(module_sp))
+ return m_static_type.IsValid() || m_dynamic_type.IsValid();
+ return false;
+}
+
+TypeImpl::operator bool() const { return IsValid(); }
+
+void TypeImpl::Clear() {
+ m_module_wp = lldb::ModuleWP();
+ m_static_type.Clear();
+ m_dynamic_type.Clear();
+}
+
+ConstString TypeImpl::GetName() const {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (m_dynamic_type)
+ return m_dynamic_type.GetTypeName();
+ return m_static_type.GetTypeName();
+ }
+ return ConstString();
+}
+
+ConstString TypeImpl::GetDisplayTypeName() const {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (m_dynamic_type)
+ return m_dynamic_type.GetDisplayTypeName();
+ return m_static_type.GetDisplayTypeName();
+ }
+ return ConstString();
+}
+
+TypeImpl TypeImpl::GetPointerType() const {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (m_dynamic_type.IsValid()) {
+ return TypeImpl(m_static_type.GetPointerType(),
+ m_dynamic_type.GetPointerType());
+ }
+ return TypeImpl(m_static_type.GetPointerType());
+ }
+ return TypeImpl();
+}
+
+TypeImpl TypeImpl::GetPointeeType() const {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (m_dynamic_type.IsValid()) {
+ return TypeImpl(m_static_type.GetPointeeType(),
+ m_dynamic_type.GetPointeeType());
+ }
+ return TypeImpl(m_static_type.GetPointeeType());
+ }
+ return TypeImpl();
+}
+
+TypeImpl TypeImpl::GetReferenceType() const {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (m_dynamic_type.IsValid()) {
+ return TypeImpl(m_static_type.GetLValueReferenceType(),
+ m_dynamic_type.GetLValueReferenceType());
+ }
+ return TypeImpl(m_static_type.GetLValueReferenceType());
+ }
+ return TypeImpl();
+}
+
+TypeImpl TypeImpl::GetTypedefedType() const {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (m_dynamic_type.IsValid()) {
+ return TypeImpl(m_static_type.GetTypedefedType(),
+ m_dynamic_type.GetTypedefedType());
+ }
+ return TypeImpl(m_static_type.GetTypedefedType());
+ }
+ return TypeImpl();
+}
+
+TypeImpl TypeImpl::GetDereferencedType() const {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (m_dynamic_type.IsValid()) {
+ return TypeImpl(m_static_type.GetNonReferenceType(),
+ m_dynamic_type.GetNonReferenceType());
+ }
+ return TypeImpl(m_static_type.GetNonReferenceType());
+ }
+ return TypeImpl();
+}
+
+TypeImpl TypeImpl::GetUnqualifiedType() const {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (m_dynamic_type.IsValid()) {
+ return TypeImpl(m_static_type.GetFullyUnqualifiedType(),
+ m_dynamic_type.GetFullyUnqualifiedType());
+ }
+ return TypeImpl(m_static_type.GetFullyUnqualifiedType());
+ }
+ return TypeImpl();
+}
+
+TypeImpl TypeImpl::GetCanonicalType() const {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (m_dynamic_type.IsValid()) {
+ return TypeImpl(m_static_type.GetCanonicalType(),
+ m_dynamic_type.GetCanonicalType());
+ }
+ return TypeImpl(m_static_type.GetCanonicalType());
+ }
+ return TypeImpl();
+}
+
+CompilerType TypeImpl::GetCompilerType(bool prefer_dynamic) {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (prefer_dynamic) {
+ if (m_dynamic_type.IsValid())
+ return m_dynamic_type;
+ }
+ return m_static_type;
+ }
+ return CompilerType();
+}
+
+TypeSystem *TypeImpl::GetTypeSystem(bool prefer_dynamic) {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (prefer_dynamic) {
+ if (m_dynamic_type.IsValid())
+ return m_dynamic_type.GetTypeSystem();
+ }
+ return m_static_type.GetTypeSystem();
+ }
+ return nullptr;
+}
+
+bool TypeImpl::GetDescription(lldb_private::Stream &strm,
+ lldb::DescriptionLevel description_level) {
+ ModuleSP module_sp;
+ if (CheckModule(module_sp)) {
+ if (m_dynamic_type.IsValid()) {
+ strm.Printf("Dynamic:\n");
+ m_dynamic_type.DumpTypeDescription(&strm);
+ strm.Printf("\nStatic:\n");
+ }
+ m_static_type.DumpTypeDescription(&strm);
+ } else {
+ strm.PutCString("Invalid TypeImpl module for type has been deleted\n");
+ }
+ return true;
+}
+
+bool TypeMemberFunctionImpl::IsValid() {
+ return m_type.IsValid() && m_kind != lldb::eMemberFunctionKindUnknown;
+}
+
+ConstString TypeMemberFunctionImpl::GetName() const { return m_name; }
+
+ConstString TypeMemberFunctionImpl::GetMangledName() const {
+ return m_decl.GetMangledName();
+}
+
+CompilerType TypeMemberFunctionImpl::GetType() const { return m_type; }
+
+lldb::MemberFunctionKind TypeMemberFunctionImpl::GetKind() const {
+ return m_kind;
+}
+
+bool TypeMemberFunctionImpl::GetDescription(Stream &stream) {
+ switch (m_kind) {
+ case lldb::eMemberFunctionKindUnknown:
+ return false;
+ case lldb::eMemberFunctionKindConstructor:
+ stream.Printf("constructor for %s",
+ m_type.GetTypeName().AsCString("<unknown>"));
+ break;
+ case lldb::eMemberFunctionKindDestructor:
+ stream.Printf("destructor for %s",
+ m_type.GetTypeName().AsCString("<unknown>"));
+ break;
+ case lldb::eMemberFunctionKindInstanceMethod:
+ stream.Printf("instance method %s of type %s", m_name.AsCString(),
+ m_decl.GetDeclContext().GetName().AsCString());
+ break;
+ case lldb::eMemberFunctionKindStaticMethod:
+ stream.Printf("static method %s of type %s", m_name.AsCString(),
+ m_decl.GetDeclContext().GetName().AsCString());
+ break;
+ }
+ return true;
+}
+
+CompilerType TypeMemberFunctionImpl::GetReturnType() const {
+ if (m_type)
+ return m_type.GetFunctionReturnType();
+ return m_decl.GetFunctionReturnType();
+}
+
+size_t TypeMemberFunctionImpl::GetNumArguments() const {
+ if (m_type)
+ return m_type.GetNumberOfFunctionArguments();
+ else
+ return m_decl.GetNumFunctionArguments();
+}
+
+CompilerType TypeMemberFunctionImpl::GetArgumentAtIndex(size_t idx) const {
+ if (m_type)
+ return m_type.GetFunctionArgumentAtIndex(idx);
+ else
+ return m_decl.GetFunctionArgumentType(idx);
+}
+
+TypeEnumMemberImpl::TypeEnumMemberImpl(const lldb::TypeImplSP &integer_type_sp,
+ ConstString name,
+ const llvm::APSInt &value)
+ : m_integer_type_sp(integer_type_sp), m_name(name), m_value(value),
+ m_valid((bool)name && (bool)integer_type_sp)
+
+{}
diff --git a/contrib/llvm-project/lldb/source/Symbol/TypeList.cpp b/contrib/llvm-project/lldb/source/Symbol/TypeList.cpp
new file mode 100644
index 000000000000..1813e8ecca9a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/TypeList.cpp
@@ -0,0 +1,207 @@
+//===-- TypeList.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <vector>
+
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+TypeList::TypeList() : m_types() {}
+
+// Destructor
+TypeList::~TypeList() {}
+
+void TypeList::Insert(const TypeSP &type_sp) {
+ // Just push each type on the back for now. We will worry about uniquing
+ // later
+ if (type_sp)
+ m_types.push_back(type_sp);
+}
+
+// Find a base type by its unique ID.
+// TypeSP
+// TypeList::FindType(lldb::user_id_t uid)
+//{
+// iterator pos = m_types.find(uid);
+// if (pos != m_types.end())
+// return pos->second;
+// return TypeSP();
+//}
+
+// Find a type by name.
+// TypeList
+// TypeList::FindTypes (ConstString name)
+//{
+// // Do we ever need to make a lookup by name map? Here we are doing
+// // a linear search which isn't going to be fast.
+// TypeList types(m_ast.getTargetInfo()->getTriple().getTriple().c_str());
+// iterator pos, end;
+// for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos)
+// if (pos->second->GetName() == name)
+// types.Insert (pos->second);
+// return types;
+//}
+
+void TypeList::Clear() { m_types.clear(); }
+
+uint32_t TypeList::GetSize() const { return m_types.size(); }
+
+// GetTypeAtIndex isn't used a lot for large type lists, currently only for
+// type lists that are returned for "image dump -t TYPENAME" commands and other
+// simple symbol queries that grab the first result...
+
+TypeSP TypeList::GetTypeAtIndex(uint32_t idx) {
+ iterator pos, end;
+ uint32_t i = idx;
+ assert(i < GetSize() && "Accessing past the end of a TypeList");
+ for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) {
+ if (i == 0)
+ return *pos;
+ --i;
+ }
+ return TypeSP();
+}
+
+void TypeList::ForEach(
+ std::function<bool(const lldb::TypeSP &type_sp)> const &callback) const {
+ for (auto pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) {
+ if (!callback(*pos))
+ break;
+ }
+}
+
+void TypeList::ForEach(
+ std::function<bool(lldb::TypeSP &type_sp)> const &callback) {
+ for (auto pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) {
+ if (!callback(*pos))
+ break;
+ }
+}
+
+void TypeList::Dump(Stream *s, bool show_context) {
+ for (iterator pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) {
+ pos->get()->Dump(s, show_context);
+ }
+}
+
+void TypeList::RemoveMismatchedTypes(const char *qualified_typename,
+ bool exact_match) {
+ llvm::StringRef type_scope;
+ llvm::StringRef type_basename;
+ TypeClass type_class = eTypeClassAny;
+ if (!Type::GetTypeScopeAndBasename(qualified_typename, type_scope,
+ type_basename, type_class)) {
+ type_basename = qualified_typename;
+ type_scope = "";
+ }
+ return RemoveMismatchedTypes(type_scope, type_basename, type_class,
+ exact_match);
+}
+
+void TypeList::RemoveMismatchedTypes(const std::string &type_scope,
+ const std::string &type_basename,
+ TypeClass type_class, bool exact_match) {
+ // Our "collection" type currently is a std::map which doesn't have any good
+ // way to iterate and remove items from the map so we currently just make a
+ // new list and add all of the matching types to it, and then swap it into
+ // m_types at the end
+ collection matching_types;
+
+ iterator pos, end = m_types.end();
+
+ for (pos = m_types.begin(); pos != end; ++pos) {
+ Type *the_type = pos->get();
+ bool keep_match = false;
+ TypeClass match_type_class = eTypeClassAny;
+
+ if (type_class != eTypeClassAny) {
+ match_type_class = the_type->GetForwardCompilerType().GetTypeClass();
+ if ((match_type_class & type_class) == 0)
+ continue;
+ }
+
+ ConstString match_type_name_const_str(the_type->GetQualifiedName());
+ if (match_type_name_const_str) {
+ const char *match_type_name = match_type_name_const_str.GetCString();
+ llvm::StringRef match_type_scope;
+ llvm::StringRef match_type_basename;
+ if (Type::GetTypeScopeAndBasename(match_type_name, match_type_scope,
+ match_type_basename,
+ match_type_class)) {
+ if (match_type_basename == type_basename) {
+ const size_t type_scope_size = type_scope.size();
+ const size_t match_type_scope_size = match_type_scope.size();
+ if (exact_match || (type_scope_size == match_type_scope_size)) {
+ keep_match = match_type_scope == type_scope;
+ } else {
+ if (match_type_scope_size > type_scope_size) {
+ const size_t type_scope_pos = match_type_scope.rfind(type_scope);
+ if (type_scope_pos == match_type_scope_size - type_scope_size) {
+ if (type_scope_pos >= 2) {
+ // Our match scope ends with the type scope we were looking
+ // for, but we need to make sure what comes before the
+ // matching type scope is a namespace boundary in case we are
+ // trying to match: type_basename = "d" type_scope = "b::c::"
+ // We want to match:
+ // match_type_scope "a::b::c::"
+ // But not:
+ // match_type_scope "a::bb::c::"
+ // So below we make sure what comes before "b::c::" in
+ // match_type_scope is "::", or the namespace boundary
+ if (match_type_scope[type_scope_pos - 1] == ':' &&
+ match_type_scope[type_scope_pos - 2] == ':') {
+ keep_match = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // The type we are currently looking at doesn't exists in a namespace
+ // or class, so it only matches if there is no type scope...
+ keep_match = type_scope.empty() && type_basename == match_type_name;
+ }
+ }
+
+ if (keep_match) {
+ matching_types.push_back(*pos);
+ }
+ }
+ m_types.swap(matching_types);
+}
+
+void TypeList::RemoveMismatchedTypes(TypeClass type_class) {
+ if (type_class == eTypeClassAny)
+ return;
+
+ // Our "collection" type currently is a std::map which doesn't have any good
+ // way to iterate and remove items from the map so we currently just make a
+ // new list and add all of the matching types to it, and then swap it into
+ // m_types at the end
+ collection matching_types;
+
+ iterator pos, end = m_types.end();
+
+ for (pos = m_types.begin(); pos != end; ++pos) {
+ Type *the_type = pos->get();
+ TypeClass match_type_class =
+ the_type->GetForwardCompilerType().GetTypeClass();
+ if (match_type_class & type_class)
+ matching_types.push_back(*pos);
+ }
+ m_types.swap(matching_types);
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/TypeMap.cpp b/contrib/llvm-project/lldb/source/Symbol/TypeMap.cpp
new file mode 100644
index 000000000000..bc6e272449f0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/TypeMap.cpp
@@ -0,0 +1,250 @@
+//===-- TypeMap.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <vector>
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+
+#include "clang/Basic/Builtins.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
+
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeMap.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace clang;
+
+TypeMap::TypeMap() : m_types() {}
+
+// Destructor
+TypeMap::~TypeMap() {}
+
+void TypeMap::Insert(const TypeSP &type_sp) {
+ // Just push each type on the back for now. We will worry about uniquing
+ // later
+ if (type_sp)
+ m_types.insert(std::make_pair(type_sp->GetID(), type_sp));
+}
+
+bool TypeMap::InsertUnique(const TypeSP &type_sp) {
+ if (type_sp) {
+ user_id_t type_uid = type_sp->GetID();
+ iterator pos, end = m_types.end();
+
+ for (pos = m_types.find(type_uid);
+ pos != end && pos->second->GetID() == type_uid; ++pos) {
+ if (pos->second.get() == type_sp.get())
+ return false;
+ }
+ Insert(type_sp);
+ }
+ return true;
+}
+
+// Find a base type by its unique ID.
+// TypeSP
+// TypeMap::FindType(lldb::user_id_t uid)
+//{
+// iterator pos = m_types.find(uid);
+// if (pos != m_types.end())
+// return pos->second;
+// return TypeSP();
+//}
+
+// Find a type by name.
+// TypeMap
+// TypeMap::FindTypes (ConstString name)
+//{
+// // Do we ever need to make a lookup by name map? Here we are doing
+// // a linear search which isn't going to be fast.
+// TypeMap types(m_ast.getTargetInfo()->getTriple().getTriple().c_str());
+// iterator pos, end;
+// for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos)
+// if (pos->second->GetName() == name)
+// types.Insert (pos->second);
+// return types;
+//}
+
+void TypeMap::Clear() { m_types.clear(); }
+
+uint32_t TypeMap::GetSize() const { return m_types.size(); }
+
+bool TypeMap::Empty() const { return m_types.empty(); }
+
+// GetTypeAtIndex isn't used a lot for large type lists, currently only for
+// type lists that are returned for "image dump -t TYPENAME" commands and other
+// simple symbol queries that grab the first result...
+
+TypeSP TypeMap::GetTypeAtIndex(uint32_t idx) {
+ iterator pos, end;
+ uint32_t i = idx;
+ for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) {
+ if (i == 0)
+ return pos->second;
+ --i;
+ }
+ return TypeSP();
+}
+
+void TypeMap::ForEach(
+ std::function<bool(const lldb::TypeSP &type_sp)> const &callback) const {
+ for (auto pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) {
+ if (!callback(pos->second))
+ break;
+ }
+}
+
+void TypeMap::ForEach(
+ std::function<bool(lldb::TypeSP &type_sp)> const &callback) {
+ for (auto pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) {
+ if (!callback(pos->second))
+ break;
+ }
+}
+
+bool TypeMap::Remove(const lldb::TypeSP &type_sp) {
+ if (type_sp) {
+ lldb::user_id_t uid = type_sp->GetID();
+ for (iterator pos = m_types.find(uid), end = m_types.end();
+ pos != end && pos->first == uid; ++pos) {
+ if (pos->second == type_sp) {
+ m_types.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void TypeMap::Dump(Stream *s, bool show_context) {
+ for (iterator pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) {
+ pos->second->Dump(s, show_context);
+ }
+}
+
+void TypeMap::RemoveMismatchedTypes(const char *qualified_typename,
+ bool exact_match) {
+ llvm::StringRef type_scope;
+ llvm::StringRef type_basename;
+ TypeClass type_class = eTypeClassAny;
+ if (!Type::GetTypeScopeAndBasename(qualified_typename, type_scope,
+ type_basename, type_class)) {
+ type_basename = qualified_typename;
+ type_scope = "";
+ }
+ return RemoveMismatchedTypes(type_scope, type_basename, type_class,
+ exact_match);
+}
+
+void TypeMap::RemoveMismatchedTypes(const std::string &type_scope,
+ const std::string &type_basename,
+ TypeClass type_class, bool exact_match) {
+ // Our "collection" type currently is a std::map which doesn't have any good
+ // way to iterate and remove items from the map so we currently just make a
+ // new list and add all of the matching types to it, and then swap it into
+ // m_types at the end
+ collection matching_types;
+
+ iterator pos, end = m_types.end();
+
+ for (pos = m_types.begin(); pos != end; ++pos) {
+ Type *the_type = pos->second.get();
+ bool keep_match = false;
+ TypeClass match_type_class = eTypeClassAny;
+
+ if (type_class != eTypeClassAny) {
+ match_type_class = the_type->GetForwardCompilerType().GetTypeClass();
+ if ((match_type_class & type_class) == 0)
+ continue;
+ }
+
+ ConstString match_type_name_const_str(the_type->GetQualifiedName());
+ if (match_type_name_const_str) {
+ const char *match_type_name = match_type_name_const_str.GetCString();
+ llvm::StringRef match_type_scope;
+ llvm::StringRef match_type_basename;
+ if (Type::GetTypeScopeAndBasename(match_type_name, match_type_scope,
+ match_type_basename,
+ match_type_class)) {
+ if (match_type_basename == type_basename) {
+ const size_t type_scope_size = type_scope.size();
+ const size_t match_type_scope_size = match_type_scope.size();
+ if (exact_match || (type_scope_size == match_type_scope_size)) {
+ keep_match = match_type_scope == type_scope;
+ } else {
+ if (match_type_scope_size > type_scope_size) {
+ const size_t type_scope_pos = match_type_scope.rfind(type_scope);
+ if (type_scope_pos == match_type_scope_size - type_scope_size) {
+ if (type_scope_pos >= 2) {
+ // Our match scope ends with the type scope we were looking
+ // for, but we need to make sure what comes before the
+ // matching type scope is a namespace boundary in case we are
+ // trying to match: type_basename = "d" type_scope = "b::c::"
+ // We want to match:
+ // match_type_scope "a::b::c::"
+ // But not:
+ // match_type_scope "a::bb::c::"
+ // So below we make sure what comes before "b::c::" in
+ // match_type_scope is "::", or the namespace boundary
+ if (match_type_scope[type_scope_pos - 1] == ':' &&
+ match_type_scope[type_scope_pos - 2] == ':') {
+ keep_match = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // The type we are currently looking at doesn't exists in a namespace
+ // or class, so it only matches if there is no type scope...
+ keep_match = type_scope.empty() && type_basename == match_type_name;
+ }
+ }
+
+ if (keep_match) {
+ matching_types.insert(*pos);
+ }
+ }
+ m_types.swap(matching_types);
+}
+
+void TypeMap::RemoveMismatchedTypes(TypeClass type_class) {
+ if (type_class == eTypeClassAny)
+ return;
+
+ // Our "collection" type currently is a std::map which doesn't have any good
+ // way to iterate and remove items from the map so we currently just make a
+ // new list and add all of the matching types to it, and then swap it into
+ // m_types at the end
+ collection matching_types;
+
+ iterator pos, end = m_types.end();
+
+ for (pos = m_types.begin(); pos != end; ++pos) {
+ Type *the_type = pos->second.get();
+ TypeClass match_type_class =
+ the_type->GetForwardCompilerType().GetTypeClass();
+ if (match_type_class & type_class)
+ matching_types.insert(*pos);
+ }
+ m_types.swap(matching_types);
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/TypeSystem.cpp b/contrib/llvm-project/lldb/source/Symbol/TypeSystem.cpp
new file mode 100644
index 000000000000..fb9c8e71acb3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/TypeSystem.cpp
@@ -0,0 +1,262 @@
+//===-- TypeSystem.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+//
+// TypeSystem.cpp
+// lldb
+//
+// Created by Ryan Brown on 3/29/15.
+//
+//
+
+#include "lldb/Symbol/TypeSystem.h"
+
+#include <set>
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/CompilerType.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+TypeSystem::TypeSystem(LLVMCastKind kind) : m_kind(kind), m_sym_file(nullptr) {}
+
+TypeSystem::~TypeSystem() {}
+
+static lldb::TypeSystemSP CreateInstanceHelper(lldb::LanguageType language,
+ Module *module, Target *target) {
+ uint32_t i = 0;
+ TypeSystemCreateInstance create_callback;
+ while ((create_callback = PluginManager::GetTypeSystemCreateCallbackAtIndex(
+ i++)) != nullptr) {
+ lldb::TypeSystemSP type_system_sp =
+ create_callback(language, module, target);
+ if (type_system_sp)
+ return type_system_sp;
+ }
+
+ return lldb::TypeSystemSP();
+}
+
+lldb::TypeSystemSP TypeSystem::CreateInstance(lldb::LanguageType language,
+ Module *module) {
+ return CreateInstanceHelper(language, module, nullptr);
+}
+
+lldb::TypeSystemSP TypeSystem::CreateInstance(lldb::LanguageType language,
+ Target *target) {
+ return CreateInstanceHelper(language, nullptr, target);
+}
+
+bool TypeSystem::IsAnonymousType(lldb::opaque_compiler_type_t type) {
+ return false;
+}
+
+CompilerType TypeSystem::GetArrayType(lldb::opaque_compiler_type_t type,
+ uint64_t size) {
+ return CompilerType();
+}
+
+CompilerType
+TypeSystem::GetLValueReferenceType(lldb::opaque_compiler_type_t type) {
+ return CompilerType();
+}
+
+CompilerType
+TypeSystem::GetRValueReferenceType(lldb::opaque_compiler_type_t type) {
+ return CompilerType();
+}
+
+CompilerType TypeSystem::AddConstModifier(lldb::opaque_compiler_type_t type) {
+ return CompilerType();
+}
+
+CompilerType
+TypeSystem::AddVolatileModifier(lldb::opaque_compiler_type_t type) {
+ return CompilerType();
+}
+
+CompilerType
+TypeSystem::AddRestrictModifier(lldb::opaque_compiler_type_t type) {
+ return CompilerType();
+}
+
+CompilerType TypeSystem::CreateTypedef(lldb::opaque_compiler_type_t type,
+ const char *name,
+ const CompilerDeclContext &decl_ctx) {
+ return CompilerType();
+}
+
+CompilerType TypeSystem::GetBuiltinTypeByName(ConstString name) {
+ return CompilerType();
+}
+
+CompilerType TypeSystem::GetTypeForFormatters(void *type) {
+ return CompilerType(this, type);
+}
+
+size_t TypeSystem::GetNumTemplateArguments(lldb::opaque_compiler_type_t type) {
+ return 0;
+}
+
+TemplateArgumentKind
+TypeSystem::GetTemplateArgumentKind(opaque_compiler_type_t type, size_t idx) {
+ return eTemplateArgumentKindNull;
+}
+
+CompilerType TypeSystem::GetTypeTemplateArgument(opaque_compiler_type_t type,
+ size_t idx) {
+ return CompilerType();
+}
+
+llvm::Optional<CompilerType::IntegralTemplateArgument>
+TypeSystem::GetIntegralTemplateArgument(opaque_compiler_type_t type,
+ size_t idx) {
+ return llvm::None;
+}
+
+LazyBool TypeSystem::ShouldPrintAsOneLiner(void *type, ValueObject *valobj) {
+ return eLazyBoolCalculate;
+}
+
+bool TypeSystem::IsMeaninglessWithoutDynamicResolution(void *type) {
+ return false;
+}
+
+ConstString TypeSystem::DeclGetMangledName(void *opaque_decl) {
+ return ConstString();
+}
+
+CompilerDeclContext TypeSystem::DeclGetDeclContext(void *opaque_decl) {
+ return CompilerDeclContext();
+}
+
+CompilerType TypeSystem::DeclGetFunctionReturnType(void *opaque_decl) {
+ return CompilerType();
+}
+
+size_t TypeSystem::DeclGetFunctionNumArguments(void *opaque_decl) { return 0; }
+
+CompilerType TypeSystem::DeclGetFunctionArgumentType(void *opaque_decl,
+ size_t arg_idx) {
+ return CompilerType();
+}
+
+std::vector<CompilerDecl>
+TypeSystem::DeclContextFindDeclByName(void *opaque_decl_ctx, ConstString name,
+ bool ignore_imported_decls) {
+ return std::vector<CompilerDecl>();
+}
+
+#pragma mark TypeSystemMap
+
+TypeSystemMap::TypeSystemMap()
+ : m_mutex(), m_map(), m_clear_in_progress(false) {}
+
+TypeSystemMap::~TypeSystemMap() {}
+
+void TypeSystemMap::Clear() {
+ collection map;
+ {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ map = m_map;
+ m_clear_in_progress = true;
+ }
+ std::set<TypeSystem *> visited;
+ for (auto pair : map) {
+ TypeSystem *type_system = pair.second.get();
+ if (type_system && !visited.count(type_system)) {
+ visited.insert(type_system);
+ type_system->Finalize();
+ }
+ }
+ map.clear();
+ {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ m_map.clear();
+ m_clear_in_progress = false;
+ }
+}
+
+void TypeSystemMap::ForEach(std::function<bool(TypeSystem *)> const &callback) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ // Use a std::set so we only call the callback once for each unique
+ // TypeSystem instance
+ std::set<TypeSystem *> visited;
+ for (auto pair : m_map) {
+ TypeSystem *type_system = pair.second.get();
+ if (type_system && !visited.count(type_system)) {
+ visited.insert(type_system);
+ if (!callback(type_system))
+ break;
+ }
+ }
+}
+
+TypeSystem *TypeSystemMap::GetTypeSystemForLanguage(lldb::LanguageType language,
+ Module *module,
+ bool can_create) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ collection::iterator pos = m_map.find(language);
+ if (pos != m_map.end())
+ return pos->second.get();
+
+ for (const auto &pair : m_map) {
+ if (pair.second && pair.second->SupportsLanguage(language)) {
+ // Add a new mapping for "language" to point to an already existing
+ // TypeSystem that supports this language
+ AddToMap(language, pair.second);
+ return pair.second.get();
+ }
+ }
+
+ if (!can_create)
+ return nullptr;
+
+ // Cache even if we get a shared pointer that contains null type system back
+ lldb::TypeSystemSP type_system_sp =
+ TypeSystem::CreateInstance(language, module);
+ AddToMap(language, type_system_sp);
+ return type_system_sp.get();
+}
+
+TypeSystem *TypeSystemMap::GetTypeSystemForLanguage(lldb::LanguageType language,
+ Target *target,
+ bool can_create) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ collection::iterator pos = m_map.find(language);
+ if (pos != m_map.end())
+ return pos->second.get();
+
+ for (const auto &pair : m_map) {
+ if (pair.second && pair.second->SupportsLanguage(language)) {
+ // Add a new mapping for "language" to point to an already existing
+ // TypeSystem that supports this language
+
+ AddToMap(language, pair.second);
+ return pair.second.get();
+ }
+ }
+
+ if (!can_create)
+ return nullptr;
+
+ // Cache even if we get a shared pointer that contains null type system back
+ lldb::TypeSystemSP type_system_sp;
+ if (!m_clear_in_progress)
+ type_system_sp = TypeSystem::CreateInstance(language, target);
+
+ AddToMap(language, type_system_sp);
+ return type_system_sp.get();
+}
+
+void TypeSystemMap::AddToMap(lldb::LanguageType language,
+ lldb::TypeSystemSP const &type_system_sp) {
+ if (!m_clear_in_progress)
+ m_map[language] = type_system_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/UnwindPlan.cpp b/contrib/llvm-project/lldb/source/Symbol/UnwindPlan.cpp
new file mode 100644
index 000000000000..774f9cb587ee
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/UnwindPlan.cpp
@@ -0,0 +1,564 @@
+//===-- UnwindPlan.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/UnwindPlan.h"
+
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool UnwindPlan::Row::RegisterLocation::
+operator==(const UnwindPlan::Row::RegisterLocation &rhs) const {
+ if (m_type == rhs.m_type) {
+ switch (m_type) {
+ case unspecified:
+ case undefined:
+ case same:
+ return true;
+
+ case atCFAPlusOffset:
+ case isCFAPlusOffset:
+ case atAFAPlusOffset:
+ case isAFAPlusOffset:
+ return m_location.offset == rhs.m_location.offset;
+
+ case inOtherRegister:
+ return m_location.reg_num == rhs.m_location.reg_num;
+
+ case atDWARFExpression:
+ case isDWARFExpression:
+ if (m_location.expr.length == rhs.m_location.expr.length)
+ return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes,
+ m_location.expr.length);
+ break;
+ }
+ }
+ return false;
+}
+
+// This function doesn't copy the dwarf expression bytes; they must remain in
+// allocated memory for the lifespan of this UnwindPlan object.
+void UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression(
+ const uint8_t *opcodes, uint32_t len) {
+ m_type = atDWARFExpression;
+ m_location.expr.opcodes = opcodes;
+ m_location.expr.length = len;
+}
+
+// This function doesn't copy the dwarf expression bytes; they must remain in
+// allocated memory for the lifespan of this UnwindPlan object.
+void UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression(
+ const uint8_t *opcodes, uint32_t len) {
+ m_type = isDWARFExpression;
+ m_location.expr.opcodes = opcodes;
+ m_location.expr.length = len;
+}
+
+static llvm::Optional<std::pair<lldb::ByteOrder, uint32_t>>
+GetByteOrderAndAddrSize(Thread *thread) {
+ if (!thread)
+ return llvm::None;
+ ProcessSP process_sp = thread->GetProcess();
+ if (!process_sp)
+ return llvm::None;
+ ArchSpec arch = process_sp->GetTarget().GetArchitecture();
+ return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());
+}
+
+static void DumpDWARFExpr(Stream &s, llvm::ArrayRef<uint8_t> expr, Thread *thread) {
+ if (auto order_and_width = GetByteOrderAndAddrSize(thread)) {
+ DataExtractor extractor(expr.data(), expr.size(), order_and_width->first,
+ order_and_width->second);
+ if (!DWARFExpression::PrintDWARFExpression(s, extractor,
+ order_and_width->second,
+ /*dwarf_ref_size*/ 4,
+ /*location_expression*/ false))
+ s.PutCString("invalid-dwarf-expr");
+ } else
+ s.PutCString("dwarf-expr");
+}
+
+void UnwindPlan::Row::RegisterLocation::Dump(Stream &s,
+ const UnwindPlan *unwind_plan,
+ const UnwindPlan::Row *row,
+ Thread *thread,
+ bool verbose) const {
+ switch (m_type) {
+ case unspecified:
+ if (verbose)
+ s.PutCString("=<unspec>");
+ else
+ s.PutCString("=!");
+ break;
+ case undefined:
+ if (verbose)
+ s.PutCString("=<undef>");
+ else
+ s.PutCString("=?");
+ break;
+ case same:
+ s.PutCString("= <same>");
+ break;
+
+ case atCFAPlusOffset:
+ case isCFAPlusOffset: {
+ s.PutChar('=');
+ if (m_type == atCFAPlusOffset)
+ s.PutChar('[');
+ s.Printf("CFA%+d", m_location.offset);
+ if (m_type == atCFAPlusOffset)
+ s.PutChar(']');
+ } break;
+
+ case atAFAPlusOffset:
+ case isAFAPlusOffset: {
+ s.PutChar('=');
+ if (m_type == atAFAPlusOffset)
+ s.PutChar('[');
+ s.Printf("AFA%+d", m_location.offset);
+ if (m_type == atAFAPlusOffset)
+ s.PutChar(']');
+ } break;
+
+ case inOtherRegister: {
+ const RegisterInfo *other_reg_info = nullptr;
+ if (unwind_plan)
+ other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num);
+ if (other_reg_info)
+ s.Printf("=%s", other_reg_info->name);
+ else
+ s.Printf("=reg(%u)", m_location.reg_num);
+ } break;
+
+ case atDWARFExpression:
+ case isDWARFExpression: {
+ s.PutChar('=');
+ if (m_type == atDWARFExpression)
+ s.PutChar('[');
+ DumpDWARFExpr(
+ s, llvm::makeArrayRef(m_location.expr.opcodes, m_location.expr.length),
+ thread);
+ if (m_type == atDWARFExpression)
+ s.PutChar(']');
+ } break;
+ }
+}
+
+static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan,
+ Thread *thread, uint32_t reg_num) {
+ const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num);
+ if (reg_info)
+ s.PutCString(reg_info->name);
+ else
+ s.Printf("reg(%u)", reg_num);
+}
+
+bool UnwindPlan::Row::FAValue::
+operator==(const UnwindPlan::Row::FAValue &rhs) const {
+ if (m_type == rhs.m_type) {
+ switch (m_type) {
+ case unspecified:
+ return true;
+
+ case isRegisterPlusOffset:
+ return m_value.reg.offset == rhs.m_value.reg.offset;
+
+ case isRegisterDereferenced:
+ return m_value.reg.reg_num == rhs.m_value.reg.reg_num;
+
+ case isDWARFExpression:
+ if (m_value.expr.length == rhs.m_value.expr.length)
+ return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes,
+ m_value.expr.length);
+ break;
+ }
+ }
+ return false;
+}
+
+void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan,
+ Thread *thread) const {
+ switch (m_type) {
+ case isRegisterPlusOffset:
+ DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
+ s.Printf("%+3d", m_value.reg.offset);
+ break;
+ case isRegisterDereferenced:
+ s.PutChar('[');
+ DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
+ s.PutChar(']');
+ break;
+ case isDWARFExpression:
+ DumpDWARFExpr(s,
+ llvm::makeArrayRef(m_value.expr.opcodes, m_value.expr.length),
+ thread);
+ break;
+ default:
+ s.PutCString("unspecified");
+ break;
+ }
+}
+
+void UnwindPlan::Row::Clear() {
+ m_cfa_value.SetUnspecified();
+ m_afa_value.SetUnspecified();
+ m_offset = 0;
+ m_register_locations.clear();
+}
+
+void UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan,
+ Thread *thread, addr_t base_addr) const {
+ if (base_addr != LLDB_INVALID_ADDRESS)
+ s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset());
+ else
+ s.Printf("%4" PRId64 ": CFA=", GetOffset());
+
+ m_cfa_value.Dump(s, unwind_plan, thread);
+
+ if (!m_afa_value.IsUnspecified()) {
+ s.Printf(" AFA=");
+ m_afa_value.Dump(s, unwind_plan, thread);
+ }
+
+ s.Printf(" => ");
+ for (collection::const_iterator idx = m_register_locations.begin();
+ idx != m_register_locations.end(); ++idx) {
+ DumpRegisterName(s, unwind_plan, thread, idx->first);
+ const bool verbose = false;
+ idx->second.Dump(s, unwind_plan, this, thread, verbose);
+ s.PutChar(' ');
+ }
+ s.EOL();
+}
+
+UnwindPlan::Row::Row()
+ : m_offset(0), m_cfa_value(), m_afa_value(), m_register_locations() {}
+
+bool UnwindPlan::Row::GetRegisterInfo(
+ uint32_t reg_num,
+ UnwindPlan::Row::RegisterLocation &register_location) const {
+ collection::const_iterator pos = m_register_locations.find(reg_num);
+ if (pos != m_register_locations.end()) {
+ register_location = pos->second;
+ return true;
+ }
+ return false;
+}
+
+void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) {
+ collection::const_iterator pos = m_register_locations.find(reg_num);
+ if (pos != m_register_locations.end()) {
+ m_register_locations.erase(pos);
+ }
+}
+
+void UnwindPlan::Row::SetRegisterInfo(
+ uint32_t reg_num,
+ const UnwindPlan::Row::RegisterLocation register_location) {
+ m_register_locations[reg_num] = register_location;
+}
+
+bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,
+ int32_t offset,
+ bool can_replace) {
+ if (!can_replace &&
+ m_register_locations.find(reg_num) != m_register_locations.end())
+ return false;
+ RegisterLocation reg_loc;
+ reg_loc.SetAtCFAPlusOffset(offset);
+ m_register_locations[reg_num] = reg_loc;
+ return true;
+}
+
+bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,
+ int32_t offset,
+ bool can_replace) {
+ if (!can_replace &&
+ m_register_locations.find(reg_num) != m_register_locations.end())
+ return false;
+ RegisterLocation reg_loc;
+ reg_loc.SetIsCFAPlusOffset(offset);
+ m_register_locations[reg_num] = reg_loc;
+ return true;
+}
+
+bool UnwindPlan::Row::SetRegisterLocationToUndefined(
+ uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) {
+ collection::iterator pos = m_register_locations.find(reg_num);
+ collection::iterator end = m_register_locations.end();
+
+ if (pos != end) {
+ if (!can_replace)
+ return false;
+ if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())
+ return false;
+ }
+ RegisterLocation reg_loc;
+ reg_loc.SetUndefined();
+ m_register_locations[reg_num] = reg_loc;
+ return true;
+}
+
+bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num,
+ bool can_replace) {
+ if (!can_replace &&
+ m_register_locations.find(reg_num) != m_register_locations.end())
+ return false;
+ RegisterLocation reg_loc;
+ reg_loc.SetUnspecified();
+ m_register_locations[reg_num] = reg_loc;
+ return true;
+}
+
+bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num,
+ uint32_t other_reg_num,
+ bool can_replace) {
+ if (!can_replace &&
+ m_register_locations.find(reg_num) != m_register_locations.end())
+ return false;
+ RegisterLocation reg_loc;
+ reg_loc.SetInRegister(other_reg_num);
+ m_register_locations[reg_num] = reg_loc;
+ return true;
+}
+
+bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,
+ bool must_replace) {
+ if (must_replace &&
+ m_register_locations.find(reg_num) == m_register_locations.end())
+ return false;
+ RegisterLocation reg_loc;
+ reg_loc.SetSame();
+ m_register_locations[reg_num] = reg_loc;
+ return true;
+}
+
+bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const {
+ return m_offset == rhs.m_offset &&
+ m_cfa_value == rhs.m_cfa_value &&
+ m_afa_value == rhs.m_afa_value &&
+ m_register_locations == rhs.m_register_locations;
+}
+
+void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) {
+ if (m_row_list.empty() ||
+ m_row_list.back()->GetOffset() != row_sp->GetOffset())
+ m_row_list.push_back(row_sp);
+ else
+ m_row_list.back() = row_sp;
+}
+
+void UnwindPlan::InsertRow(const UnwindPlan::RowSP &row_sp,
+ bool replace_existing) {
+ collection::iterator it = m_row_list.begin();
+ while (it != m_row_list.end()) {
+ RowSP row = *it;
+ if (row->GetOffset() >= row_sp->GetOffset())
+ break;
+ it++;
+ }
+ if (it == m_row_list.end() || (*it)->GetOffset() != row_sp->GetOffset())
+ m_row_list.insert(it, row_sp);
+ else if (replace_existing)
+ *it = row_sp;
+}
+
+UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const {
+ RowSP row;
+ if (!m_row_list.empty()) {
+ if (offset == -1)
+ row = m_row_list.back();
+ else {
+ collection::const_iterator pos, end = m_row_list.end();
+ for (pos = m_row_list.begin(); pos != end; ++pos) {
+ if ((*pos)->GetOffset() <= static_cast<lldb::offset_t>(offset))
+ row = *pos;
+ else
+ break;
+ }
+ }
+ }
+ return row;
+}
+
+bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
+ return idx < m_row_list.size();
+}
+
+const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const {
+ if (idx < m_row_list.size())
+ return m_row_list[idx];
+ else {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log)
+ log->Printf("error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index "
+ "(number rows is %u)",
+ idx, (uint32_t)m_row_list.size());
+ return UnwindPlan::RowSP();
+ }
+}
+
+const UnwindPlan::RowSP UnwindPlan::GetLastRow() const {
+ if (m_row_list.empty()) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log)
+ log->Printf("UnwindPlan::GetLastRow() when rows are empty");
+ return UnwindPlan::RowSP();
+ }
+ return m_row_list.back();
+}
+
+int UnwindPlan::GetRowCount() const { return m_row_list.size(); }
+
+void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) {
+ if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
+ m_plan_valid_address_range = range;
+}
+
+bool UnwindPlan::PlanValidAtAddress(Address addr) {
+ // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
+ if (GetRowCount() == 0) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log) {
+ StreamString s;
+ if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
+ log->Printf("UnwindPlan is invalid -- no unwind rows for UnwindPlan "
+ "'%s' at address %s",
+ m_source_name.GetCString(), s.GetData());
+ } else {
+ log->Printf(
+ "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
+ m_source_name.GetCString());
+ }
+ }
+ return false;
+ }
+
+ // If the 0th Row of unwind instructions is missing, or if it doesn't provide
+ // a register to use to find the Canonical Frame Address, this is not a valid
+ // UnwindPlan.
+ if (GetRowAtIndex(0).get() == nullptr ||
+ GetRowAtIndex(0)->GetCFAValue().GetValueType() ==
+ Row::FAValue::unspecified) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log) {
+ StreamString s;
+ if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
+ log->Printf("UnwindPlan is invalid -- no CFA register defined in row 0 "
+ "for UnwindPlan '%s' at address %s",
+ m_source_name.GetCString(), s.GetData());
+ } else {
+ log->Printf("UnwindPlan is invalid -- no CFA register defined in row 0 "
+ "for UnwindPlan '%s'",
+ m_source_name.GetCString());
+ }
+ }
+ return false;
+ }
+
+ if (!m_plan_valid_address_range.GetBaseAddress().IsValid() ||
+ m_plan_valid_address_range.GetByteSize() == 0)
+ return true;
+
+ if (!addr.IsValid())
+ return true;
+
+ if (m_plan_valid_address_range.ContainsFileAddress(addr))
+ return true;
+
+ return false;
+}
+
+void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {
+ if (!m_source_name.IsEmpty()) {
+ s.Printf("This UnwindPlan originally sourced from %s\n",
+ m_source_name.GetCString());
+ }
+ if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) {
+ TargetSP target_sp(thread->CalculateTarget());
+ addr_t lsda_load_addr = m_lsda_address.GetLoadAddress(target_sp.get());
+ addr_t personality_func_load_addr =
+ m_personality_func_addr.GetLoadAddress(target_sp.get());
+
+ if (lsda_load_addr != LLDB_INVALID_ADDRESS &&
+ personality_func_load_addr != LLDB_INVALID_ADDRESS) {
+ s.Printf("LSDA address 0x%" PRIx64
+ ", personality routine is at address 0x%" PRIx64 "\n",
+ lsda_load_addr, personality_func_load_addr);
+ }
+ }
+ s.Printf("This UnwindPlan is sourced from the compiler: ");
+ switch (m_plan_is_sourced_from_compiler) {
+ case eLazyBoolYes:
+ s.Printf("yes.\n");
+ break;
+ case eLazyBoolNo:
+ s.Printf("no.\n");
+ break;
+ case eLazyBoolCalculate:
+ s.Printf("not specified.\n");
+ break;
+ }
+ s.Printf("This UnwindPlan is valid at all instruction locations: ");
+ switch (m_plan_is_valid_at_all_instruction_locations) {
+ case eLazyBoolYes:
+ s.Printf("yes.\n");
+ break;
+ case eLazyBoolNo:
+ s.Printf("no.\n");
+ break;
+ case eLazyBoolCalculate:
+ s.Printf("not specified.\n");
+ break;
+ }
+ if (m_plan_valid_address_range.GetBaseAddress().IsValid() &&
+ m_plan_valid_address_range.GetByteSize() > 0) {
+ s.PutCString("Address range of this UnwindPlan: ");
+ TargetSP target_sp(thread->CalculateTarget());
+ m_plan_valid_address_range.Dump(&s, target_sp.get(),
+ Address::DumpStyleSectionNameOffset);
+ s.EOL();
+ }
+ collection::const_iterator pos, begin = m_row_list.begin(),
+ end = m_row_list.end();
+ for (pos = begin; pos != end; ++pos) {
+ s.Printf("row[%u]: ", (uint32_t)std::distance(begin, pos));
+ (*pos)->Dump(s, this, thread, base_addr);
+ }
+}
+
+void UnwindPlan::SetSourceName(const char *source) {
+ m_source_name = ConstString(source);
+}
+
+ConstString UnwindPlan::GetSourceName() const { return m_source_name; }
+
+const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,
+ uint32_t unwind_reg) const {
+ if (thread) {
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+ if (reg_ctx) {
+ uint32_t reg;
+ if (m_register_kind == eRegisterKindLLDB)
+ reg = unwind_reg;
+ else
+ reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,
+ unwind_reg);
+ if (reg != LLDB_INVALID_REGNUM)
+ return reg_ctx->GetRegisterInfoAtIndex(reg);
+ }
+ }
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/UnwindTable.cpp b/contrib/llvm-project/lldb/source/Symbol/UnwindTable.cpp
new file mode 100644
index 000000000000..a8f451dc4643
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/UnwindTable.cpp
@@ -0,0 +1,197 @@
+//===-- UnwindTable.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/UnwindTable.h"
+
+#include <stdio.h>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ArmUnwindInfo.h"
+#include "lldb/Symbol/CompactUnwindInfo.h"
+#include "lldb/Symbol/DWARFCallFrameInfo.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+
+// There is one UnwindTable object per ObjectFile. It contains a list of Unwind
+// objects -- one per function, populated lazily -- for the ObjectFile. Each
+// Unwind object has multiple UnwindPlans for different scenarios.
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindTable::UnwindTable(Module &module)
+ : m_module(module), m_unwinds(), m_initialized(false), m_mutex(),
+ m_eh_frame_up(), m_compact_unwind_up(), m_arm_unwind_up() {}
+
+// We can't do some of this initialization when the ObjectFile is running its
+// ctor; delay doing it until needed for something.
+
+void UnwindTable::Initialize() {
+ if (m_initialized)
+ return;
+
+ std::lock_guard<std::mutex> guard(m_mutex);
+
+ if (m_initialized) // check again once we've acquired the lock
+ return;
+ m_initialized = true;
+ ObjectFile *object_file = m_module.GetObjectFile();
+ if (!object_file)
+ return;
+
+ SectionList *sl = m_module.GetSectionList();
+ if (!sl)
+ return;
+
+ SectionSP sect = sl->FindSectionByType(eSectionTypeEHFrame, true);
+ if (sect.get()) {
+ m_eh_frame_up.reset(
+ new DWARFCallFrameInfo(*object_file, sect, DWARFCallFrameInfo::EH));
+ }
+
+ sect = sl->FindSectionByType(eSectionTypeDWARFDebugFrame, true);
+ if (sect) {
+ m_debug_frame_up.reset(
+ new DWARFCallFrameInfo(*object_file, sect, DWARFCallFrameInfo::DWARF));
+ }
+
+ sect = sl->FindSectionByType(eSectionTypeCompactUnwind, true);
+ if (sect) {
+ m_compact_unwind_up.reset(new CompactUnwindInfo(*object_file, sect));
+ }
+
+ sect = sl->FindSectionByType(eSectionTypeARMexidx, true);
+ if (sect) {
+ SectionSP sect_extab = sl->FindSectionByType(eSectionTypeARMextab, true);
+ if (sect_extab.get()) {
+ m_arm_unwind_up.reset(new ArmUnwindInfo(*object_file, sect, sect_extab));
+ }
+ }
+}
+
+UnwindTable::~UnwindTable() {}
+
+llvm::Optional<AddressRange> UnwindTable::GetAddressRange(const Address &addr,
+ SymbolContext &sc) {
+ AddressRange range;
+
+ // First check the symbol context
+ if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
+ false, range) &&
+ range.GetBaseAddress().IsValid())
+ return range;
+
+ // Does the eh_frame unwind info has a function bounds for this addr?
+ if (m_eh_frame_up && m_eh_frame_up->GetAddressRange(addr, range))
+ return range;
+
+ // Try debug_frame as well
+ if (m_debug_frame_up && m_debug_frame_up->GetAddressRange(addr, range))
+ return range;
+
+ return llvm::None;
+}
+
+FuncUnwindersSP
+UnwindTable::GetFuncUnwindersContainingAddress(const Address &addr,
+ SymbolContext &sc) {
+ Initialize();
+
+ std::lock_guard<std::mutex> guard(m_mutex);
+
+ // There is an UnwindTable per object file, so we can safely use file handles
+ addr_t file_addr = addr.GetFileAddress();
+ iterator end = m_unwinds.end();
+ iterator insert_pos = end;
+ if (!m_unwinds.empty()) {
+ insert_pos = m_unwinds.lower_bound(file_addr);
+ iterator pos = insert_pos;
+ if ((pos == m_unwinds.end()) ||
+ (pos != m_unwinds.begin() &&
+ pos->second->GetFunctionStartAddress() != addr))
+ --pos;
+
+ if (pos->second->ContainsAddress(addr))
+ return pos->second;
+ }
+
+ auto range_or = GetAddressRange(addr, sc);
+ if (!range_or)
+ return nullptr;
+
+ FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, *range_or));
+ m_unwinds.insert(insert_pos,
+ std::make_pair(range_or->GetBaseAddress().GetFileAddress(),
+ func_unwinder_sp));
+ return func_unwinder_sp;
+}
+
+// Ignore any existing FuncUnwinders for this function, create a new one and
+// don't add it to the UnwindTable. This is intended for use by target modules
+// show-unwind where we want to create new UnwindPlans, not re-use existing
+// ones.
+FuncUnwindersSP
+UnwindTable::GetUncachedFuncUnwindersContainingAddress(const Address &addr,
+ SymbolContext &sc) {
+ Initialize();
+
+ auto range_or = GetAddressRange(addr, sc);
+ if (!range_or)
+ return nullptr;
+
+ return std::make_shared<FuncUnwinders>(*this, *range_or);
+}
+
+void UnwindTable::Dump(Stream &s) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ s.Format("UnwindTable for '{0}':\n", m_module.GetFileSpec());
+ const_iterator begin = m_unwinds.begin();
+ const_iterator end = m_unwinds.end();
+ for (const_iterator pos = begin; pos != end; ++pos) {
+ s.Printf("[%u] 0x%16.16" PRIx64 "\n", (unsigned)std::distance(begin, pos),
+ pos->first);
+ }
+ s.EOL();
+}
+
+DWARFCallFrameInfo *UnwindTable::GetEHFrameInfo() {
+ Initialize();
+ return m_eh_frame_up.get();
+}
+
+DWARFCallFrameInfo *UnwindTable::GetDebugFrameInfo() {
+ Initialize();
+ return m_debug_frame_up.get();
+}
+
+CompactUnwindInfo *UnwindTable::GetCompactUnwindInfo() {
+ Initialize();
+ return m_compact_unwind_up.get();
+}
+
+ArmUnwindInfo *UnwindTable::GetArmUnwindInfo() {
+ Initialize();
+ return m_arm_unwind_up.get();
+}
+
+SymbolFile *UnwindTable::GetSymbolFile() {
+ if (SymbolVendor *vendor = m_module.GetSymbolVendor())
+ return vendor->GetSymbolFile();
+ return nullptr;
+}
+
+ArchSpec UnwindTable::GetArchitecture() { return m_module.GetArchitecture(); }
+
+bool UnwindTable::GetAllowAssemblyEmulationUnwindPlans() {
+ if (ObjectFile *object_file = m_module.GetObjectFile())
+ return object_file->AllowAssemblyEmulationUnwindPlans();
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/Variable.cpp b/contrib/llvm-project/lldb/source/Symbol/Variable.cpp
new file mode 100644
index 000000000000..29a7a5191f61
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/Variable.cpp
@@ -0,0 +1,768 @@
+//===-- Variable.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/Variable.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/CompilerDecl.h"
+#include "lldb/Symbol/CompilerDeclContext.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/Twine.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Variable constructor
+Variable::Variable(
+ lldb::user_id_t uid, const char *name,
+ const char *mangled, // The mangled or fully qualified name of the variable.
+ const lldb::SymbolFileTypeSP &symfile_type_sp, ValueType scope,
+ SymbolContextScope *context, const RangeList &scope_range,
+ Declaration *decl_ptr, const DWARFExpression &location, bool external,
+ bool artificial, bool static_member)
+ : UserID(uid), m_name(name), m_mangled(ConstString(mangled)),
+ m_symfile_type_sp(symfile_type_sp), m_scope(scope),
+ m_owner_scope(context), m_scope_range(scope_range),
+ m_declaration(decl_ptr), m_location(location), m_external(external),
+ m_artificial(artificial), m_loc_is_const_data(false),
+ m_static_member(static_member) {}
+
+// Destructor
+Variable::~Variable() {}
+
+lldb::LanguageType Variable::GetLanguage() const {
+ SymbolContext variable_sc;
+ m_owner_scope->CalculateSymbolContext(&variable_sc);
+ if (variable_sc.comp_unit)
+ return variable_sc.comp_unit->GetLanguage();
+ return lldb::eLanguageTypeUnknown;
+}
+
+ConstString Variable::GetName() const {
+ ConstString name = m_mangled.GetName(GetLanguage());
+ if (name)
+ return name;
+ return m_name;
+}
+
+ConstString Variable::GetUnqualifiedName() const { return m_name; }
+
+bool Variable::NameMatches(ConstString name) const {
+ if (m_name == name)
+ return true;
+ SymbolContext variable_sc;
+ m_owner_scope->CalculateSymbolContext(&variable_sc);
+
+ LanguageType language = eLanguageTypeUnknown;
+ if (variable_sc.comp_unit)
+ language = variable_sc.comp_unit->GetLanguage();
+ return m_mangled.NameMatches(name, language);
+}
+bool Variable::NameMatches(const RegularExpression &regex) const {
+ if (regex.Execute(m_name.AsCString()))
+ return true;
+ if (m_mangled)
+ return m_mangled.NameMatches(regex, GetLanguage());
+ return false;
+}
+
+Type *Variable::GetType() {
+ if (m_symfile_type_sp)
+ return m_symfile_type_sp->GetType();
+ return nullptr;
+}
+
+void Variable::Dump(Stream *s, bool show_context) const {
+ s->Printf("%p: ", static_cast<const void *>(this));
+ s->Indent();
+ *s << "Variable" << (const UserID &)*this;
+
+ if (m_name)
+ *s << ", name = \"" << m_name << "\"";
+
+ if (m_symfile_type_sp) {
+ Type *type = m_symfile_type_sp->GetType();
+ if (type) {
+ *s << ", type = {" << type->GetID() << "} " << (void *)type << " (";
+ type->DumpTypeName(s);
+ s->PutChar(')');
+ }
+ }
+
+ if (m_scope != eValueTypeInvalid) {
+ s->PutCString(", scope = ");
+ switch (m_scope) {
+ case eValueTypeVariableGlobal:
+ s->PutCString(m_external ? "global" : "static");
+ break;
+ case eValueTypeVariableArgument:
+ s->PutCString("parameter");
+ break;
+ case eValueTypeVariableLocal:
+ s->PutCString("local");
+ break;
+ case eValueTypeVariableThreadLocal:
+ s->PutCString("thread local");
+ break;
+ default:
+ *s << "??? (" << m_scope << ')';
+ }
+ }
+
+ if (show_context && m_owner_scope != nullptr) {
+ s->PutCString(", context = ( ");
+ m_owner_scope->DumpSymbolContext(s);
+ s->PutCString(" )");
+ }
+
+ bool show_fullpaths = false;
+ m_declaration.Dump(s, show_fullpaths);
+
+ if (m_location.IsValid()) {
+ s->PutCString(", location = ");
+ lldb::addr_t loclist_base_addr = LLDB_INVALID_ADDRESS;
+ if (m_location.IsLocationList()) {
+ SymbolContext variable_sc;
+ m_owner_scope->CalculateSymbolContext(&variable_sc);
+ if (variable_sc.function)
+ loclist_base_addr = variable_sc.function->GetAddressRange()
+ .GetBaseAddress()
+ .GetFileAddress();
+ }
+ ABISP abi;
+ if (m_owner_scope) {
+ ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());
+ if (module_sp)
+ abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());
+ }
+ m_location.GetDescription(s, lldb::eDescriptionLevelBrief,
+ loclist_base_addr, abi.get());
+ }
+
+ if (m_external)
+ s->PutCString(", external");
+
+ if (m_artificial)
+ s->PutCString(", artificial");
+
+ s->EOL();
+}
+
+bool Variable::DumpDeclaration(Stream *s, bool show_fullpaths,
+ bool show_module) {
+ bool dumped_declaration_info = false;
+ if (m_owner_scope) {
+ SymbolContext sc;
+ m_owner_scope->CalculateSymbolContext(&sc);
+ sc.block = nullptr;
+ sc.line_entry.Clear();
+ bool show_inlined_frames = false;
+ const bool show_function_arguments = true;
+ const bool show_function_name = true;
+
+ dumped_declaration_info = sc.DumpStopContext(
+ s, nullptr, Address(), show_fullpaths, show_module, show_inlined_frames,
+ show_function_arguments, show_function_name);
+
+ if (sc.function)
+ s->PutChar(':');
+ }
+ if (m_declaration.DumpStopContext(s, false))
+ dumped_declaration_info = true;
+ return dumped_declaration_info;
+}
+
+size_t Variable::MemorySize() const { return sizeof(Variable); }
+
+CompilerDeclContext Variable::GetDeclContext() {
+ Type *type = GetType();
+ if (type)
+ return type->GetSymbolFile()->GetDeclContextContainingUID(GetID());
+ return CompilerDeclContext();
+}
+
+CompilerDecl Variable::GetDecl() {
+ Type *type = GetType();
+ return type ? type->GetSymbolFile()->GetDeclForUID(GetID()) : CompilerDecl();
+}
+
+void Variable::CalculateSymbolContext(SymbolContext *sc) {
+ if (m_owner_scope) {
+ m_owner_scope->CalculateSymbolContext(sc);
+ sc->variable = this;
+ } else
+ sc->Clear(false);
+}
+
+bool Variable::LocationIsValidForFrame(StackFrame *frame) {
+ // Is the variable is described by a single location?
+ if (!m_location.IsLocationList()) {
+ // Yes it is, the location is valid.
+ return true;
+ }
+
+ if (frame) {
+ Function *function =
+ frame->GetSymbolContext(eSymbolContextFunction).function;
+ if (function) {
+ TargetSP target_sp(frame->CalculateTarget());
+
+ addr_t loclist_base_load_addr =
+ function->GetAddressRange().GetBaseAddress().GetLoadAddress(
+ target_sp.get());
+ if (loclist_base_load_addr == LLDB_INVALID_ADDRESS)
+ return false;
+ // It is a location list. We just need to tell if the location list
+ // contains the current address when converted to a load address
+ return m_location.LocationListContainsAddress(
+ loclist_base_load_addr,
+ frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get()));
+ }
+ }
+ return false;
+}
+
+bool Variable::LocationIsValidForAddress(const Address &address) {
+ // Be sure to resolve the address to section offset prior to calling this
+ // function.
+ if (address.IsSectionOffset()) {
+ SymbolContext sc;
+ CalculateSymbolContext(&sc);
+ if (sc.module_sp == address.GetModule()) {
+ // Is the variable is described by a single location?
+ if (!m_location.IsLocationList()) {
+ // Yes it is, the location is valid.
+ return true;
+ }
+
+ if (sc.function) {
+ addr_t loclist_base_file_addr =
+ sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
+ if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
+ return false;
+ // It is a location list. We just need to tell if the location list
+ // contains the current address when converted to a load address
+ return m_location.LocationListContainsAddress(loclist_base_file_addr,
+ address.GetFileAddress());
+ }
+ }
+ }
+ return false;
+}
+
+bool Variable::IsInScope(StackFrame *frame) {
+ switch (m_scope) {
+ case eValueTypeRegister:
+ case eValueTypeRegisterSet:
+ return frame != nullptr;
+
+ case eValueTypeConstResult:
+ case eValueTypeVariableGlobal:
+ case eValueTypeVariableStatic:
+ case eValueTypeVariableThreadLocal:
+ return true;
+
+ case eValueTypeVariableArgument:
+ case eValueTypeVariableLocal:
+ if (frame) {
+ // We don't have a location list, we just need to see if the block that
+ // this variable was defined in is currently
+ Block *deepest_frame_block =
+ frame->GetSymbolContext(eSymbolContextBlock).block;
+ if (deepest_frame_block) {
+ SymbolContext variable_sc;
+ CalculateSymbolContext(&variable_sc);
+
+ // Check for static or global variable defined at the compile unit
+ // level that wasn't defined in a block
+ if (variable_sc.block == nullptr)
+ return true;
+
+ // Check if the variable is valid in the current block
+ if (variable_sc.block != deepest_frame_block &&
+ !variable_sc.block->Contains(deepest_frame_block))
+ return false;
+
+ // If no scope range is specified then it means that the scope is the
+ // same as the scope of the enclosing lexical block.
+ if (m_scope_range.IsEmpty())
+ return true;
+
+ addr_t file_address = frame->GetFrameCodeAddress().GetFileAddress();
+ return m_scope_range.FindEntryThatContains(file_address) != nullptr;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+Status Variable::GetValuesForVariableExpressionPath(
+ llvm::StringRef variable_expr_path, ExecutionContextScope *scope,
+ GetVariableCallback callback, void *baton, VariableList &variable_list,
+ ValueObjectList &valobj_list) {
+ Status error;
+ if (!callback || variable_expr_path.empty()) {
+ error.SetErrorString("unknown error");
+ return error;
+ }
+
+ switch (variable_expr_path.front()) {
+ case '*':
+ error = Variable::GetValuesForVariableExpressionPath(
+ variable_expr_path.drop_front(), scope, callback, baton, variable_list,
+ valobj_list);
+ if (error.Fail()) {
+ error.SetErrorString("unknown error");
+ return error;
+ }
+ for (uint32_t i = 0; i < valobj_list.GetSize();) {
+ Status tmp_error;
+ ValueObjectSP valobj_sp(
+ valobj_list.GetValueObjectAtIndex(i)->Dereference(tmp_error));
+ if (tmp_error.Fail()) {
+ variable_list.RemoveVariableAtIndex(i);
+ valobj_list.RemoveValueObjectAtIndex(i);
+ } else {
+ valobj_list.SetValueObjectAtIndex(i, valobj_sp);
+ ++i;
+ }
+ }
+ return error;
+ case '&': {
+ error = Variable::GetValuesForVariableExpressionPath(
+ variable_expr_path.drop_front(), scope, callback, baton, variable_list,
+ valobj_list);
+ if (error.Success()) {
+ for (uint32_t i = 0; i < valobj_list.GetSize();) {
+ Status tmp_error;
+ ValueObjectSP valobj_sp(
+ valobj_list.GetValueObjectAtIndex(i)->AddressOf(tmp_error));
+ if (tmp_error.Fail()) {
+ variable_list.RemoveVariableAtIndex(i);
+ valobj_list.RemoveValueObjectAtIndex(i);
+ } else {
+ valobj_list.SetValueObjectAtIndex(i, valobj_sp);
+ ++i;
+ }
+ }
+ } else {
+ error.SetErrorString("unknown error");
+ }
+ return error;
+ } break;
+
+ default: {
+ static RegularExpression g_regex(
+ llvm::StringRef("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)"));
+ RegularExpression::Match regex_match(1);
+ std::string variable_name;
+ variable_list.Clear();
+ if (!g_regex.Execute(variable_expr_path, &regex_match)) {
+ error.SetErrorStringWithFormat(
+ "unable to extract a variable name from '%s'",
+ variable_expr_path.str().c_str());
+ return error;
+ }
+ if (!regex_match.GetMatchAtIndex(variable_expr_path, 1, variable_name)) {
+ error.SetErrorStringWithFormat(
+ "unable to extract a variable name from '%s'",
+ variable_expr_path.str().c_str());
+ return error;
+ }
+ if (!callback(baton, variable_name.c_str(), variable_list)) {
+ error.SetErrorString("unknown error");
+ return error;
+ }
+ uint32_t i = 0;
+ while (i < variable_list.GetSize()) {
+ VariableSP var_sp(variable_list.GetVariableAtIndex(i));
+ ValueObjectSP valobj_sp;
+ if (!var_sp) {
+ variable_list.RemoveVariableAtIndex(i);
+ continue;
+ }
+ ValueObjectSP variable_valobj_sp(
+ ValueObjectVariable::Create(scope, var_sp));
+ if (!variable_valobj_sp) {
+ variable_list.RemoveVariableAtIndex(i);
+ continue;
+ }
+
+ llvm::StringRef variable_sub_expr_path =
+ variable_expr_path.drop_front(variable_name.size());
+ if (!variable_sub_expr_path.empty()) {
+ valobj_sp = variable_valobj_sp->GetValueForExpressionPath(
+ variable_sub_expr_path);
+ if (!valobj_sp) {
+ error.SetErrorStringWithFormat(
+ "invalid expression path '%s' for variable '%s'",
+ variable_sub_expr_path.str().c_str(),
+ var_sp->GetName().GetCString());
+ variable_list.RemoveVariableAtIndex(i);
+ continue;
+ }
+ } else {
+ // Just the name of a variable with no extras
+ valobj_sp = variable_valobj_sp;
+ }
+
+ valobj_list.Append(valobj_sp);
+ ++i;
+ }
+
+ if (variable_list.GetSize() > 0) {
+ error.Clear();
+ return error;
+ }
+ } break;
+ }
+ error.SetErrorString("unknown error");
+ return error;
+}
+
+bool Variable::DumpLocationForAddress(Stream *s, const Address &address) {
+ // Be sure to resolve the address to section offset prior to calling this
+ // function.
+ if (address.IsSectionOffset()) {
+ SymbolContext sc;
+ CalculateSymbolContext(&sc);
+ if (sc.module_sp == address.GetModule()) {
+ ABISP abi;
+ if (m_owner_scope) {
+ ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());
+ if (module_sp)
+ abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());
+ }
+
+ const addr_t file_addr = address.GetFileAddress();
+ if (sc.function) {
+ if (sc.function->GetAddressRange().ContainsFileAddress(address)) {
+ addr_t loclist_base_file_addr =
+ sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
+ if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
+ return false;
+ return m_location.DumpLocationForAddress(s, eDescriptionLevelBrief,
+ loclist_base_file_addr,
+ file_addr, abi.get());
+ }
+ }
+ return m_location.DumpLocationForAddress(s, eDescriptionLevelBrief,
+ LLDB_INVALID_ADDRESS, file_addr,
+ abi.get());
+ }
+ }
+ return false;
+}
+
+static void PrivateAutoComplete(
+ StackFrame *frame, llvm::StringRef partial_path,
+ const llvm::Twine
+ &prefix_path, // Anything that has been resolved already will be in here
+ const CompilerType &compiler_type,
+ StringList &matches, bool &word_complete);
+
+static void PrivateAutoCompleteMembers(
+ StackFrame *frame, const std::string &partial_member_name,
+ llvm::StringRef partial_path,
+ const llvm::Twine
+ &prefix_path, // Anything that has been resolved already will be in here
+ const CompilerType &compiler_type,
+ StringList &matches, bool &word_complete);
+
+static void PrivateAutoCompleteMembers(
+ StackFrame *frame, const std::string &partial_member_name,
+ llvm::StringRef partial_path,
+ const llvm::Twine
+ &prefix_path, // Anything that has been resolved already will be in here
+ const CompilerType &compiler_type,
+ StringList &matches, bool &word_complete) {
+
+ // We are in a type parsing child members
+ const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses();
+
+ if (num_bases > 0) {
+ for (uint32_t i = 0; i < num_bases; ++i) {
+ CompilerType base_class_type =
+ compiler_type.GetDirectBaseClassAtIndex(i, nullptr);
+
+ PrivateAutoCompleteMembers(
+ frame, partial_member_name, partial_path, prefix_path,
+ base_class_type.GetCanonicalType(), matches, word_complete);
+ }
+ }
+
+ const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses();
+
+ if (num_vbases > 0) {
+ for (uint32_t i = 0; i < num_vbases; ++i) {
+ CompilerType vbase_class_type =
+ compiler_type.GetVirtualBaseClassAtIndex(i, nullptr);
+
+ PrivateAutoCompleteMembers(
+ frame, partial_member_name, partial_path, prefix_path,
+ vbase_class_type.GetCanonicalType(), matches, word_complete);
+ }
+ }
+
+ // We are in a type parsing child members
+ const uint32_t num_fields = compiler_type.GetNumFields();
+
+ if (num_fields > 0) {
+ for (uint32_t i = 0; i < num_fields; ++i) {
+ std::string member_name;
+
+ CompilerType member_compiler_type = compiler_type.GetFieldAtIndex(
+ i, member_name, nullptr, nullptr, nullptr);
+
+ if (partial_member_name.empty() ||
+ member_name.find(partial_member_name) == 0) {
+ if (member_name == partial_member_name) {
+ PrivateAutoComplete(
+ frame, partial_path,
+ prefix_path + member_name, // Anything that has been resolved
+ // already will be in here
+ member_compiler_type.GetCanonicalType(), matches, word_complete);
+ } else {
+ matches.AppendString((prefix_path + member_name).str());
+ }
+ }
+ }
+ }
+}
+
+static void PrivateAutoComplete(
+ StackFrame *frame, llvm::StringRef partial_path,
+ const llvm::Twine
+ &prefix_path, // Anything that has been resolved already will be in here
+ const CompilerType &compiler_type,
+ StringList &matches, bool &word_complete) {
+ // printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path =
+ // '%s'\n", prefix_path.c_str(), partial_path.c_str());
+ std::string remaining_partial_path;
+
+ const lldb::TypeClass type_class = compiler_type.GetTypeClass();
+ if (partial_path.empty()) {
+ if (compiler_type.IsValid()) {
+ switch (type_class) {
+ default:
+ case eTypeClassArray:
+ case eTypeClassBlockPointer:
+ case eTypeClassBuiltin:
+ case eTypeClassComplexFloat:
+ case eTypeClassComplexInteger:
+ case eTypeClassEnumeration:
+ case eTypeClassFunction:
+ case eTypeClassMemberPointer:
+ case eTypeClassReference:
+ case eTypeClassTypedef:
+ case eTypeClassVector: {
+ matches.AppendString(prefix_path.str());
+ word_complete = matches.GetSize() == 1;
+ } break;
+
+ case eTypeClassClass:
+ case eTypeClassStruct:
+ case eTypeClassUnion:
+ if (prefix_path.str().back() != '.')
+ matches.AppendString((prefix_path + ".").str());
+ break;
+
+ case eTypeClassObjCObject:
+ case eTypeClassObjCInterface:
+ break;
+ case eTypeClassObjCObjectPointer:
+ case eTypeClassPointer: {
+ bool omit_empty_base_classes = true;
+ if (compiler_type.GetNumChildren(omit_empty_base_classes, nullptr) > 0)
+ matches.AppendString((prefix_path + "->").str());
+ else {
+ matches.AppendString(prefix_path.str());
+ word_complete = true;
+ }
+ } break;
+ }
+ } else {
+ if (frame) {
+ const bool get_file_globals = true;
+
+ VariableList *variable_list = frame->GetVariableList(get_file_globals);
+
+ if (variable_list) {
+ const size_t num_variables = variable_list->GetSize();
+ for (size_t i = 0; i < num_variables; ++i) {
+ Variable *variable = variable_list->GetVariableAtIndex(i).get();
+ matches.AppendString(variable->GetName().AsCString());
+ }
+ }
+ }
+ }
+ } else {
+ const char ch = partial_path[0];
+ switch (ch) {
+ case '*':
+ if (prefix_path.str().empty()) {
+ PrivateAutoComplete(frame, partial_path.substr(1), "*", compiler_type,
+ matches, word_complete);
+ }
+ break;
+
+ case '&':
+ if (prefix_path.isTriviallyEmpty()) {
+ PrivateAutoComplete(frame, partial_path.substr(1), std::string("&"),
+ compiler_type, matches, word_complete);
+ }
+ break;
+
+ case '-':
+ if (partial_path.size() > 1 && partial_path[1] == '>' &&
+ !prefix_path.str().empty()) {
+ switch (type_class) {
+ case lldb::eTypeClassPointer: {
+ CompilerType pointee_type(compiler_type.GetPointeeType());
+ if (partial_path.size() > 2 && partial_path[2]) {
+ // If there is more after the "->", then search deeper
+ PrivateAutoComplete(
+ frame, partial_path.substr(2), prefix_path + "->",
+ pointee_type.GetCanonicalType(), matches, word_complete);
+ } else {
+ // Nothing after the "->", so list all members
+ PrivateAutoCompleteMembers(
+ frame, std::string(), std::string(), prefix_path + "->",
+ pointee_type.GetCanonicalType(), matches, word_complete);
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case '.':
+ if (compiler_type.IsValid()) {
+ switch (type_class) {
+ case lldb::eTypeClassUnion:
+ case lldb::eTypeClassStruct:
+ case lldb::eTypeClassClass:
+ if (partial_path.size() > 1 && partial_path[1]) {
+ // If there is more after the ".", then search deeper
+ PrivateAutoComplete(frame, partial_path.substr(1),
+ prefix_path + ".", compiler_type, matches,
+ word_complete);
+
+ } else {
+ // Nothing after the ".", so list all members
+ PrivateAutoCompleteMembers(frame, std::string(), partial_path,
+ prefix_path + ".", compiler_type,
+ matches, word_complete);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ if (isalpha(ch) || ch == '_' || ch == '$') {
+ const size_t partial_path_len = partial_path.size();
+ size_t pos = 1;
+ while (pos < partial_path_len) {
+ const char curr_ch = partial_path[pos];
+ if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') {
+ ++pos;
+ continue;
+ }
+ break;
+ }
+
+ std::string token(partial_path, 0, pos);
+ remaining_partial_path = partial_path.substr(pos);
+
+ if (compiler_type.IsValid()) {
+ PrivateAutoCompleteMembers(frame, token, remaining_partial_path,
+ prefix_path, compiler_type, matches,
+ word_complete);
+ } else if (frame) {
+ // We haven't found our variable yet
+ const bool get_file_globals = true;
+
+ VariableList *variable_list =
+ frame->GetVariableList(get_file_globals);
+
+ if (!variable_list)
+ break;
+
+ const size_t num_variables = variable_list->GetSize();
+ for (size_t i = 0; i < num_variables; ++i) {
+ Variable *variable = variable_list->GetVariableAtIndex(i).get();
+
+ if (!variable)
+ continue;
+
+ const char *variable_name = variable->GetName().AsCString();
+ if (strstr(variable_name, token.c_str()) == variable_name) {
+ if (strcmp(variable_name, token.c_str()) == 0) {
+ Type *variable_type = variable->GetType();
+ if (variable_type) {
+ CompilerType variable_compiler_type(
+ variable_type->GetForwardCompilerType());
+ PrivateAutoComplete(
+ frame, remaining_partial_path,
+ prefix_path + token, // Anything that has been resolved
+ // already will be in here
+ variable_compiler_type.GetCanonicalType(), matches,
+ word_complete);
+ } else {
+ matches.AppendString((prefix_path + variable_name).str());
+ }
+ } else if (remaining_partial_path.empty()) {
+ matches.AppendString((prefix_path + variable_name).str());
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+size_t Variable::AutoComplete(const ExecutionContext &exe_ctx,
+ CompletionRequest &request) {
+ CompilerType compiler_type;
+
+ bool word_complete = false;
+ StringList matches;
+ PrivateAutoComplete(exe_ctx.GetFramePtr(), request.GetCursorArgumentPrefix(),
+ "", compiler_type, matches, word_complete);
+ request.SetWordComplete(word_complete);
+ request.AddCompletions(matches);
+
+ return request.GetNumberOfMatches();
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/VariableList.cpp b/contrib/llvm-project/lldb/source/Symbol/VariableList.cpp
new file mode 100644
index 000000000000..51312472e265
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/VariableList.cpp
@@ -0,0 +1,176 @@
+//===-- VariableList.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/VariableList.h"
+
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Utility/RegularExpression.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// VariableList constructor
+VariableList::VariableList() : m_variables() {}
+
+// Destructor
+VariableList::~VariableList() {}
+
+void VariableList::AddVariable(const VariableSP &var_sp) {
+ m_variables.push_back(var_sp);
+}
+
+bool VariableList::AddVariableIfUnique(const lldb::VariableSP &var_sp) {
+ if (FindVariableIndex(var_sp) == UINT32_MAX) {
+ m_variables.push_back(var_sp);
+ return true;
+ }
+ return false;
+}
+
+void VariableList::AddVariables(VariableList *variable_list) {
+ if (variable_list) {
+ std::copy(variable_list->m_variables.begin(), // source begin
+ variable_list->m_variables.end(), // source end
+ back_inserter(m_variables)); // destination
+ }
+}
+
+void VariableList::Clear() { m_variables.clear(); }
+
+VariableSP VariableList::GetVariableAtIndex(size_t idx) const {
+ VariableSP var_sp;
+ if (idx < m_variables.size())
+ var_sp = m_variables[idx];
+ return var_sp;
+}
+
+VariableSP VariableList::RemoveVariableAtIndex(size_t idx) {
+ VariableSP var_sp;
+ if (idx < m_variables.size()) {
+ var_sp = m_variables[idx];
+ m_variables.erase(m_variables.begin() + idx);
+ }
+ return var_sp;
+}
+
+uint32_t VariableList::FindVariableIndex(const VariableSP &var_sp) {
+ iterator pos, end = m_variables.end();
+ for (pos = m_variables.begin(); pos != end; ++pos) {
+ if (pos->get() == var_sp.get())
+ return std::distance(m_variables.begin(), pos);
+ }
+ return UINT32_MAX;
+}
+
+VariableSP VariableList::FindVariable(ConstString name,
+ bool include_static_members) {
+ VariableSP var_sp;
+ iterator pos, end = m_variables.end();
+ for (pos = m_variables.begin(); pos != end; ++pos) {
+ if ((*pos)->NameMatches(name)) {
+ if (include_static_members || !(*pos)->IsStaticMember()) {
+ var_sp = (*pos);
+ break;
+ }
+ }
+ }
+ return var_sp;
+}
+
+VariableSP VariableList::FindVariable(ConstString name,
+ lldb::ValueType value_type,
+ bool include_static_members) {
+ VariableSP var_sp;
+ iterator pos, end = m_variables.end();
+ for (pos = m_variables.begin(); pos != end; ++pos) {
+ if ((*pos)->NameMatches(name) && (*pos)->GetScope() == value_type) {
+ if (include_static_members || !(*pos)->IsStaticMember()) {
+ var_sp = (*pos);
+ break;
+ }
+ }
+ }
+ return var_sp;
+}
+
+size_t VariableList::AppendVariablesIfUnique(VariableList &var_list) {
+ const size_t initial_size = var_list.GetSize();
+ iterator pos, end = m_variables.end();
+ for (pos = m_variables.begin(); pos != end; ++pos)
+ var_list.AddVariableIfUnique(*pos);
+ return var_list.GetSize() - initial_size;
+}
+
+size_t VariableList::AppendVariablesIfUnique(const RegularExpression &regex,
+ VariableList &var_list,
+ size_t &total_matches) {
+ const size_t initial_size = var_list.GetSize();
+ iterator pos, end = m_variables.end();
+ for (pos = m_variables.begin(); pos != end; ++pos) {
+ if ((*pos)->NameMatches(regex)) {
+ // Note the total matches found
+ total_matches++;
+ // Only add this variable if it isn't already in the "var_list"
+ var_list.AddVariableIfUnique(*pos);
+ }
+ }
+ // Return the number of new unique variables added to "var_list"
+ return var_list.GetSize() - initial_size;
+}
+
+size_t VariableList::AppendVariablesWithScope(lldb::ValueType type,
+ VariableList &var_list,
+ bool if_unique) {
+ const size_t initial_size = var_list.GetSize();
+ iterator pos, end = m_variables.end();
+ for (pos = m_variables.begin(); pos != end; ++pos) {
+ if ((*pos)->GetScope() == type) {
+ if (if_unique)
+ var_list.AddVariableIfUnique(*pos);
+ else
+ var_list.AddVariable(*pos);
+ }
+ }
+ // Return the number of new unique variables added to "var_list"
+ return var_list.GetSize() - initial_size;
+}
+
+uint32_t VariableList::FindIndexForVariable(Variable *variable) {
+ VariableSP var_sp;
+ iterator pos;
+ const iterator begin = m_variables.begin();
+ const iterator end = m_variables.end();
+ for (pos = m_variables.begin(); pos != end; ++pos) {
+ if ((*pos).get() == variable)
+ return std::distance(begin, pos);
+ }
+ return UINT32_MAX;
+}
+
+size_t VariableList::MemorySize() const {
+ size_t mem_size = sizeof(VariableList);
+ const_iterator pos, end = m_variables.end();
+ for (pos = m_variables.begin(); pos != end; ++pos)
+ mem_size += (*pos)->MemorySize();
+ return mem_size;
+}
+
+size_t VariableList::GetSize() const { return m_variables.size(); }
+
+void VariableList::Dump(Stream *s, bool show_context) const {
+ // s.Printf("%.*p: ", (int)sizeof(void*) * 2, this);
+ // s.Indent();
+ // s << "VariableList\n";
+
+ const_iterator pos, end = m_variables.end();
+ for (pos = m_variables.begin(); pos != end; ++pos) {
+ (*pos)->Dump(s, show_context);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Symbol/VerifyDecl.cpp b/contrib/llvm-project/lldb/source/Symbol/VerifyDecl.cpp
new file mode 100644
index 000000000000..1873d3a5d03c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/VerifyDecl.cpp
@@ -0,0 +1,15 @@
+//===-- VerifyDecl.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/VerifyDecl.h"
+#include "clang/AST/DeclBase.h"
+
+void lldb_private::VerifyDecl(clang::Decl *decl) {
+ assert(decl && "VerifyDecl called with nullptr?");
+ decl->getAccess();
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ABI.cpp b/contrib/llvm-project/lldb/source/Target/ABI.cpp
new file mode 100644
index 000000000000..28cd9aec665c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ABI.cpp
@@ -0,0 +1,212 @@
+//===-- ABI.cpp -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ABISP
+ABI::FindPlugin(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ ABISP abi_sp;
+ ABICreateInstance create_callback;
+
+ for (uint32_t idx = 0;
+ (create_callback = PluginManager::GetABICreateCallbackAtIndex(idx)) !=
+ nullptr;
+ ++idx) {
+ abi_sp = create_callback(process_sp, arch);
+
+ if (abi_sp)
+ return abi_sp;
+ }
+ abi_sp.reset();
+ return abi_sp;
+}
+
+ABI::~ABI() = default;
+
+bool ABI::GetRegisterInfoByName(ConstString name, RegisterInfo &info) {
+ uint32_t count = 0;
+ const RegisterInfo *register_info_array = GetRegisterInfoArray(count);
+ if (register_info_array) {
+ const char *unique_name_cstr = name.GetCString();
+ uint32_t i;
+ for (i = 0; i < count; ++i) {
+ if (register_info_array[i].name == unique_name_cstr) {
+ info = register_info_array[i];
+ return true;
+ }
+ }
+ for (i = 0; i < count; ++i) {
+ if (register_info_array[i].alt_name == unique_name_cstr) {
+ info = register_info_array[i];
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool ABI::GetRegisterInfoByKind(RegisterKind reg_kind, uint32_t reg_num,
+ RegisterInfo &info) {
+ if (reg_kind < eRegisterKindEHFrame || reg_kind >= kNumRegisterKinds)
+ return false;
+
+ uint32_t count = 0;
+ const RegisterInfo *register_info_array = GetRegisterInfoArray(count);
+ if (register_info_array) {
+ for (uint32_t i = 0; i < count; ++i) {
+ if (register_info_array[i].kinds[reg_kind] == reg_num) {
+ info = register_info_array[i];
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ValueObjectSP ABI::GetReturnValueObject(Thread &thread, CompilerType &ast_type,
+ bool persistent) const {
+ if (!ast_type.IsValid())
+ return ValueObjectSP();
+
+ ValueObjectSP return_valobj_sp;
+
+ return_valobj_sp = GetReturnValueObjectImpl(thread, ast_type);
+ if (!return_valobj_sp)
+ return return_valobj_sp;
+
+ // Now turn this into a persistent variable.
+ // FIXME: This code is duplicated from Target::EvaluateExpression, and it is
+ // used in similar form in a couple
+ // of other places. Figure out the correct Create function to do all this
+ // work.
+
+ if (persistent) {
+ Target &target = *thread.CalculateTarget();
+ PersistentExpressionState *persistent_expression_state =
+ target.GetPersistentExpressionStateForLanguage(
+ ast_type.GetMinimumLanguage());
+
+ if (!persistent_expression_state)
+ return ValueObjectSP();
+
+ auto prefix = persistent_expression_state->GetPersistentVariablePrefix();
+ ConstString persistent_variable_name =
+ persistent_expression_state->GetNextPersistentVariableName(target,
+ prefix);
+
+ lldb::ValueObjectSP const_valobj_sp;
+
+ // Check in case our value is already a constant value
+ if (return_valobj_sp->GetIsConstant()) {
+ const_valobj_sp = return_valobj_sp;
+ const_valobj_sp->SetName(persistent_variable_name);
+ } else
+ const_valobj_sp =
+ return_valobj_sp->CreateConstantValue(persistent_variable_name);
+
+ lldb::ValueObjectSP live_valobj_sp = return_valobj_sp;
+
+ return_valobj_sp = const_valobj_sp;
+
+ ExpressionVariableSP expr_variable_sp(
+ persistent_expression_state->CreatePersistentVariable(
+ return_valobj_sp));
+
+ assert(expr_variable_sp);
+
+ // Set flags and live data as appropriate
+
+ const Value &result_value = live_valobj_sp->GetValue();
+
+ switch (result_value.GetValueType()) {
+ case Value::eValueTypeHostAddress:
+ case Value::eValueTypeFileAddress:
+ // we don't do anything with these for now
+ break;
+ case Value::eValueTypeScalar:
+ case Value::eValueTypeVector:
+ expr_variable_sp->m_flags |=
+ ExpressionVariable::EVIsFreezeDried;
+ expr_variable_sp->m_flags |=
+ ExpressionVariable::EVIsLLDBAllocated;
+ expr_variable_sp->m_flags |=
+ ExpressionVariable::EVNeedsAllocation;
+ break;
+ case Value::eValueTypeLoadAddress:
+ expr_variable_sp->m_live_sp = live_valobj_sp;
+ expr_variable_sp->m_flags |=
+ ExpressionVariable::EVIsProgramReference;
+ break;
+ }
+
+ return_valobj_sp = expr_variable_sp->GetValueObject();
+ }
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABI::GetReturnValueObject(Thread &thread, llvm::Type &ast_type,
+ bool persistent) const {
+ ValueObjectSP return_valobj_sp;
+ return_valobj_sp = GetReturnValueObjectImpl(thread, ast_type);
+ return return_valobj_sp;
+}
+
+// specialized to work with llvm IR types
+//
+// for now we will specify a default implementation so that we don't need to
+// modify other ABIs
+lldb::ValueObjectSP ABI::GetReturnValueObjectImpl(Thread &thread,
+ llvm::Type &ir_type) const {
+ ValueObjectSP return_valobj_sp;
+
+ /* this is a dummy and will only be called if an ABI does not override this */
+
+ return return_valobj_sp;
+}
+
+bool ABI::PrepareTrivialCall(Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress, llvm::Type &returntype,
+ llvm::ArrayRef<ABI::CallArgument> args) const {
+ // dummy prepare trivial call
+ llvm_unreachable("Should never get here!");
+}
+
+bool ABI::GetFallbackRegisterLocation(
+ const RegisterInfo *reg_info,
+ UnwindPlan::Row::RegisterLocation &unwind_regloc) {
+ // Did the UnwindPlan fail to give us the caller's stack pointer? The stack
+ // pointer is defined to be the same as THIS frame's CFA, so return the CFA
+ // value as the caller's stack pointer. This is true on x86-32/x86-64 at
+ // least.
+ if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_SP) {
+ unwind_regloc.SetIsCFAPlusOffset(0);
+ return true;
+ }
+
+ // If a volatile register is being requested, we don't want to forward the
+ // next frame's register contents up the stack -- the register is not
+ // retrievable at this frame.
+ if (RegisterIsVolatile(reg_info)) {
+ unwind_regloc.SetUndefined();
+ return true;
+ }
+
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ExecutionContext.cpp b/contrib/llvm-project/lldb/source/Target/ExecutionContext.cpp
new file mode 100644
index 000000000000..77327372f88b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ExecutionContext.cpp
@@ -0,0 +1,618 @@
+//===-- ExecutionContext.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb_private;
+
+ExecutionContext::ExecutionContext()
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {}
+
+ExecutionContext::ExecutionContext(const ExecutionContext &rhs)
+ : m_target_sp(rhs.m_target_sp), m_process_sp(rhs.m_process_sp),
+ m_thread_sp(rhs.m_thread_sp), m_frame_sp(rhs.m_frame_sp) {}
+
+ExecutionContext::ExecutionContext(const lldb::TargetSP &target_sp,
+ bool get_process)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ if (target_sp)
+ SetContext(target_sp, get_process);
+}
+
+ExecutionContext::ExecutionContext(const lldb::ProcessSP &process_sp)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ if (process_sp)
+ SetContext(process_sp);
+}
+
+ExecutionContext::ExecutionContext(const lldb::ThreadSP &thread_sp)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ if (thread_sp)
+ SetContext(thread_sp);
+}
+
+ExecutionContext::ExecutionContext(const lldb::StackFrameSP &frame_sp)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ if (frame_sp)
+ SetContext(frame_sp);
+}
+
+ExecutionContext::ExecutionContext(const lldb::TargetWP &target_wp,
+ bool get_process)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ lldb::TargetSP target_sp(target_wp.lock());
+ if (target_sp)
+ SetContext(target_sp, get_process);
+}
+
+ExecutionContext::ExecutionContext(const lldb::ProcessWP &process_wp)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ lldb::ProcessSP process_sp(process_wp.lock());
+ if (process_sp)
+ SetContext(process_sp);
+}
+
+ExecutionContext::ExecutionContext(const lldb::ThreadWP &thread_wp)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ lldb::ThreadSP thread_sp(thread_wp.lock());
+ if (thread_sp)
+ SetContext(thread_sp);
+}
+
+ExecutionContext::ExecutionContext(const lldb::StackFrameWP &frame_wp)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ lldb::StackFrameSP frame_sp(frame_wp.lock());
+ if (frame_sp)
+ SetContext(frame_sp);
+}
+
+ExecutionContext::ExecutionContext(Target *t,
+ bool fill_current_process_thread_frame)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ if (t) {
+ m_target_sp = t->shared_from_this();
+ if (fill_current_process_thread_frame) {
+ m_process_sp = t->GetProcessSP();
+ if (m_process_sp) {
+ m_thread_sp = m_process_sp->GetThreadList().GetSelectedThread();
+ if (m_thread_sp)
+ m_frame_sp = m_thread_sp->GetSelectedFrame();
+ }
+ }
+ }
+}
+
+ExecutionContext::ExecutionContext(Process *process, Thread *thread,
+ StackFrame *frame)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ if (process) {
+ m_process_sp = process->shared_from_this();
+ m_target_sp = process->GetTarget().shared_from_this();
+ }
+ if (thread)
+ m_thread_sp = thread->shared_from_this();
+ if (frame)
+ m_frame_sp = frame->shared_from_this();
+}
+
+ExecutionContext::ExecutionContext(const ExecutionContextRef &exe_ctx_ref)
+ : m_target_sp(exe_ctx_ref.GetTargetSP()),
+ m_process_sp(exe_ctx_ref.GetProcessSP()),
+ m_thread_sp(exe_ctx_ref.GetThreadSP()),
+ m_frame_sp(exe_ctx_ref.GetFrameSP()) {}
+
+ExecutionContext::ExecutionContext(const ExecutionContextRef *exe_ctx_ref_ptr,
+ bool thread_and_frame_only_if_stopped)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ if (exe_ctx_ref_ptr) {
+ m_target_sp = exe_ctx_ref_ptr->GetTargetSP();
+ m_process_sp = exe_ctx_ref_ptr->GetProcessSP();
+ if (!thread_and_frame_only_if_stopped ||
+ (m_process_sp && StateIsStoppedState(m_process_sp->GetState(), true))) {
+ m_thread_sp = exe_ctx_ref_ptr->GetThreadSP();
+ m_frame_sp = exe_ctx_ref_ptr->GetFrameSP();
+ }
+ }
+}
+
+ExecutionContext::ExecutionContext(const ExecutionContextRef *exe_ctx_ref_ptr,
+ std::unique_lock<std::recursive_mutex> &lock)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ if (exe_ctx_ref_ptr) {
+ m_target_sp = exe_ctx_ref_ptr->GetTargetSP();
+ if (m_target_sp) {
+ lock = std::unique_lock<std::recursive_mutex>(m_target_sp->GetAPIMutex());
+
+ m_process_sp = exe_ctx_ref_ptr->GetProcessSP();
+ m_thread_sp = exe_ctx_ref_ptr->GetThreadSP();
+ m_frame_sp = exe_ctx_ref_ptr->GetFrameSP();
+ }
+ }
+}
+
+ExecutionContext::ExecutionContext(const ExecutionContextRef &exe_ctx_ref,
+ std::unique_lock<std::recursive_mutex> &lock)
+ : m_target_sp(exe_ctx_ref.GetTargetSP()), m_process_sp(), m_thread_sp(),
+ m_frame_sp() {
+ if (m_target_sp) {
+ lock = std::unique_lock<std::recursive_mutex>(m_target_sp->GetAPIMutex());
+
+ m_process_sp = exe_ctx_ref.GetProcessSP();
+ m_thread_sp = exe_ctx_ref.GetThreadSP();
+ m_frame_sp = exe_ctx_ref.GetFrameSP();
+ }
+}
+
+ExecutionContext::ExecutionContext(ExecutionContextScope *exe_scope_ptr)
+ : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
+ if (exe_scope_ptr)
+ exe_scope_ptr->CalculateExecutionContext(*this);
+}
+
+ExecutionContext::ExecutionContext(ExecutionContextScope &exe_scope_ref) {
+ exe_scope_ref.CalculateExecutionContext(*this);
+}
+
+void ExecutionContext::Clear() {
+ m_target_sp.reset();
+ m_process_sp.reset();
+ m_thread_sp.reset();
+ m_frame_sp.reset();
+}
+
+ExecutionContext::~ExecutionContext() = default;
+
+uint32_t ExecutionContext::GetAddressByteSize() const {
+ if (m_target_sp && m_target_sp->GetArchitecture().IsValid())
+ return m_target_sp->GetArchitecture().GetAddressByteSize();
+ if (m_process_sp)
+ return m_process_sp->GetAddressByteSize();
+ return sizeof(void *);
+}
+
+lldb::ByteOrder ExecutionContext::GetByteOrder() const {
+ if (m_target_sp && m_target_sp->GetArchitecture().IsValid())
+ m_target_sp->GetArchitecture().GetByteOrder();
+ if (m_process_sp)
+ m_process_sp->GetByteOrder();
+ return endian::InlHostByteOrder();
+}
+
+RegisterContext *ExecutionContext::GetRegisterContext() const {
+ if (m_frame_sp)
+ return m_frame_sp->GetRegisterContext().get();
+ else if (m_thread_sp)
+ return m_thread_sp->GetRegisterContext().get();
+ return nullptr;
+}
+
+Target *ExecutionContext::GetTargetPtr() const {
+ if (m_target_sp)
+ return m_target_sp.get();
+ if (m_process_sp)
+ return &m_process_sp->GetTarget();
+ return nullptr;
+}
+
+Process *ExecutionContext::GetProcessPtr() const {
+ if (m_process_sp)
+ return m_process_sp.get();
+ if (m_target_sp)
+ return m_target_sp->GetProcessSP().get();
+ return nullptr;
+}
+
+ExecutionContextScope *ExecutionContext::GetBestExecutionContextScope() const {
+ if (m_frame_sp)
+ return m_frame_sp.get();
+ if (m_thread_sp)
+ return m_thread_sp.get();
+ if (m_process_sp)
+ return m_process_sp.get();
+ return m_target_sp.get();
+}
+
+Target &ExecutionContext::GetTargetRef() const {
+ assert(m_target_sp);
+ return *m_target_sp;
+}
+
+Process &ExecutionContext::GetProcessRef() const {
+ assert(m_process_sp);
+ return *m_process_sp;
+}
+
+Thread &ExecutionContext::GetThreadRef() const {
+ assert(m_thread_sp);
+ return *m_thread_sp;
+}
+
+StackFrame &ExecutionContext::GetFrameRef() const {
+ assert(m_frame_sp);
+ return *m_frame_sp;
+}
+
+void ExecutionContext::SetTargetSP(const lldb::TargetSP &target_sp) {
+ m_target_sp = target_sp;
+}
+
+void ExecutionContext::SetProcessSP(const lldb::ProcessSP &process_sp) {
+ m_process_sp = process_sp;
+}
+
+void ExecutionContext::SetThreadSP(const lldb::ThreadSP &thread_sp) {
+ m_thread_sp = thread_sp;
+}
+
+void ExecutionContext::SetFrameSP(const lldb::StackFrameSP &frame_sp) {
+ m_frame_sp = frame_sp;
+}
+
+void ExecutionContext::SetTargetPtr(Target *target) {
+ if (target)
+ m_target_sp = target->shared_from_this();
+ else
+ m_target_sp.reset();
+}
+
+void ExecutionContext::SetProcessPtr(Process *process) {
+ if (process)
+ m_process_sp = process->shared_from_this();
+ else
+ m_process_sp.reset();
+}
+
+void ExecutionContext::SetThreadPtr(Thread *thread) {
+ if (thread)
+ m_thread_sp = thread->shared_from_this();
+ else
+ m_thread_sp.reset();
+}
+
+void ExecutionContext::SetFramePtr(StackFrame *frame) {
+ if (frame)
+ m_frame_sp = frame->shared_from_this();
+ else
+ m_frame_sp.reset();
+}
+
+void ExecutionContext::SetContext(const lldb::TargetSP &target_sp,
+ bool get_process) {
+ m_target_sp = target_sp;
+ if (get_process && target_sp)
+ m_process_sp = target_sp->GetProcessSP();
+ else
+ m_process_sp.reset();
+ m_thread_sp.reset();
+ m_frame_sp.reset();
+}
+
+void ExecutionContext::SetContext(const lldb::ProcessSP &process_sp) {
+ m_process_sp = process_sp;
+ if (process_sp)
+ m_target_sp = process_sp->GetTarget().shared_from_this();
+ else
+ m_target_sp.reset();
+ m_thread_sp.reset();
+ m_frame_sp.reset();
+}
+
+void ExecutionContext::SetContext(const lldb::ThreadSP &thread_sp) {
+ m_frame_sp.reset();
+ m_thread_sp = thread_sp;
+ if (thread_sp) {
+ m_process_sp = thread_sp->GetProcess();
+ if (m_process_sp)
+ m_target_sp = m_process_sp->GetTarget().shared_from_this();
+ else
+ m_target_sp.reset();
+ } else {
+ m_target_sp.reset();
+ m_process_sp.reset();
+ }
+}
+
+void ExecutionContext::SetContext(const lldb::StackFrameSP &frame_sp) {
+ m_frame_sp = frame_sp;
+ if (frame_sp) {
+ m_thread_sp = frame_sp->CalculateThread();
+ if (m_thread_sp) {
+ m_process_sp = m_thread_sp->GetProcess();
+ if (m_process_sp)
+ m_target_sp = m_process_sp->GetTarget().shared_from_this();
+ else
+ m_target_sp.reset();
+ } else {
+ m_target_sp.reset();
+ m_process_sp.reset();
+ }
+ } else {
+ m_target_sp.reset();
+ m_process_sp.reset();
+ m_thread_sp.reset();
+ }
+}
+
+ExecutionContext &ExecutionContext::operator=(const ExecutionContext &rhs) {
+ if (this != &rhs) {
+ m_target_sp = rhs.m_target_sp;
+ m_process_sp = rhs.m_process_sp;
+ m_thread_sp = rhs.m_thread_sp;
+ m_frame_sp = rhs.m_frame_sp;
+ }
+ return *this;
+}
+
+bool ExecutionContext::operator==(const ExecutionContext &rhs) const {
+ // Check that the frame shared pointers match, or both are valid and their
+ // stack IDs match since sometimes we get new objects that represent the same
+ // frame within a thread.
+ if ((m_frame_sp == rhs.m_frame_sp) ||
+ (m_frame_sp && rhs.m_frame_sp &&
+ m_frame_sp->GetStackID() == rhs.m_frame_sp->GetStackID())) {
+ // Check that the thread shared pointers match, or both are valid and their
+ // thread IDs match since sometimes we get new objects that represent the
+ // same thread within a process.
+ if ((m_thread_sp == rhs.m_thread_sp) ||
+ (m_thread_sp && rhs.m_thread_sp &&
+ m_thread_sp->GetID() == rhs.m_thread_sp->GetID())) {
+ // Processes and targets don't change much
+ return m_process_sp == rhs.m_process_sp && m_target_sp == rhs.m_target_sp;
+ }
+ }
+ return false;
+}
+
+bool ExecutionContext::operator!=(const ExecutionContext &rhs) const {
+ return !(*this == rhs);
+}
+
+bool ExecutionContext::HasTargetScope() const {
+ return ((bool)m_target_sp && m_target_sp->IsValid());
+}
+
+bool ExecutionContext::HasProcessScope() const {
+ return (HasTargetScope() && ((bool)m_process_sp && m_process_sp->IsValid()));
+}
+
+bool ExecutionContext::HasThreadScope() const {
+ return (HasProcessScope() && ((bool)m_thread_sp && m_thread_sp->IsValid()));
+}
+
+bool ExecutionContext::HasFrameScope() const {
+ return HasThreadScope() && m_frame_sp;
+}
+
+ExecutionContextRef::ExecutionContextRef()
+ : m_target_wp(), m_process_wp(), m_thread_wp(),
+ m_tid(LLDB_INVALID_THREAD_ID), m_stack_id() {}
+
+ExecutionContextRef::ExecutionContextRef(const ExecutionContext *exe_ctx)
+ : m_target_wp(), m_process_wp(), m_thread_wp(),
+ m_tid(LLDB_INVALID_THREAD_ID), m_stack_id() {
+ if (exe_ctx)
+ *this = *exe_ctx;
+}
+
+ExecutionContextRef::ExecutionContextRef(const ExecutionContext &exe_ctx)
+ : m_target_wp(), m_process_wp(), m_thread_wp(),
+ m_tid(LLDB_INVALID_THREAD_ID), m_stack_id() {
+ *this = exe_ctx;
+}
+
+ExecutionContextRef::ExecutionContextRef(Target *target, bool adopt_selected)
+ : m_target_wp(), m_process_wp(), m_thread_wp(),
+ m_tid(LLDB_INVALID_THREAD_ID), m_stack_id() {
+ SetTargetPtr(target, adopt_selected);
+}
+
+ExecutionContextRef::ExecutionContextRef(const ExecutionContextRef &rhs)
+ : m_target_wp(rhs.m_target_wp), m_process_wp(rhs.m_process_wp),
+ m_thread_wp(rhs.m_thread_wp), m_tid(rhs.m_tid),
+ m_stack_id(rhs.m_stack_id) {}
+
+ExecutionContextRef &ExecutionContextRef::
+operator=(const ExecutionContextRef &rhs) {
+ if (this != &rhs) {
+ m_target_wp = rhs.m_target_wp;
+ m_process_wp = rhs.m_process_wp;
+ m_thread_wp = rhs.m_thread_wp;
+ m_tid = rhs.m_tid;
+ m_stack_id = rhs.m_stack_id;
+ }
+ return *this;
+}
+
+ExecutionContextRef &ExecutionContextRef::
+operator=(const ExecutionContext &exe_ctx) {
+ m_target_wp = exe_ctx.GetTargetSP();
+ m_process_wp = exe_ctx.GetProcessSP();
+ lldb::ThreadSP thread_sp(exe_ctx.GetThreadSP());
+ m_thread_wp = thread_sp;
+ if (thread_sp)
+ m_tid = thread_sp->GetID();
+ else
+ m_tid = LLDB_INVALID_THREAD_ID;
+ lldb::StackFrameSP frame_sp(exe_ctx.GetFrameSP());
+ if (frame_sp)
+ m_stack_id = frame_sp->GetStackID();
+ else
+ m_stack_id.Clear();
+ return *this;
+}
+
+void ExecutionContextRef::Clear() {
+ m_target_wp.reset();
+ m_process_wp.reset();
+ ClearThread();
+ ClearFrame();
+}
+
+ExecutionContextRef::~ExecutionContextRef() = default;
+
+void ExecutionContextRef::SetTargetSP(const lldb::TargetSP &target_sp) {
+ m_target_wp = target_sp;
+}
+
+void ExecutionContextRef::SetProcessSP(const lldb::ProcessSP &process_sp) {
+ if (process_sp) {
+ m_process_wp = process_sp;
+ SetTargetSP(process_sp->GetTarget().shared_from_this());
+ } else {
+ m_process_wp.reset();
+ m_target_wp.reset();
+ }
+}
+
+void ExecutionContextRef::SetThreadSP(const lldb::ThreadSP &thread_sp) {
+ if (thread_sp) {
+ m_thread_wp = thread_sp;
+ m_tid = thread_sp->GetID();
+ SetProcessSP(thread_sp->GetProcess());
+ } else {
+ ClearThread();
+ m_process_wp.reset();
+ m_target_wp.reset();
+ }
+}
+
+void ExecutionContextRef::SetFrameSP(const lldb::StackFrameSP &frame_sp) {
+ if (frame_sp) {
+ m_stack_id = frame_sp->GetStackID();
+ SetThreadSP(frame_sp->GetThread());
+ } else {
+ ClearFrame();
+ ClearThread();
+ m_process_wp.reset();
+ m_target_wp.reset();
+ }
+}
+
+void ExecutionContextRef::SetTargetPtr(Target *target, bool adopt_selected) {
+ Clear();
+ if (target) {
+ lldb::TargetSP target_sp(target->shared_from_this());
+ if (target_sp) {
+ m_target_wp = target_sp;
+ if (adopt_selected) {
+ lldb::ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp) {
+ m_process_wp = process_sp;
+ if (process_sp) {
+ // Only fill in the thread and frame if our process is stopped
+ // Don't just check the state, since we might be in the middle of
+ // resuming.
+ Process::StopLocker stop_locker;
+
+ if (stop_locker.TryLock(&process_sp->GetRunLock()) &&
+ StateIsStoppedState(process_sp->GetState(), true)) {
+ lldb::ThreadSP thread_sp(
+ process_sp->GetThreadList().GetSelectedThread());
+ if (!thread_sp)
+ thread_sp = process_sp->GetThreadList().GetThreadAtIndex(0);
+
+ if (thread_sp) {
+ SetThreadSP(thread_sp);
+ lldb::StackFrameSP frame_sp(thread_sp->GetSelectedFrame());
+ if (!frame_sp)
+ frame_sp = thread_sp->GetStackFrameAtIndex(0);
+ if (frame_sp)
+ SetFrameSP(frame_sp);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void ExecutionContextRef::SetProcessPtr(Process *process) {
+ if (process) {
+ SetProcessSP(process->shared_from_this());
+ } else {
+ m_process_wp.reset();
+ m_target_wp.reset();
+ }
+}
+
+void ExecutionContextRef::SetThreadPtr(Thread *thread) {
+ if (thread) {
+ SetThreadSP(thread->shared_from_this());
+ } else {
+ ClearThread();
+ m_process_wp.reset();
+ m_target_wp.reset();
+ }
+}
+
+void ExecutionContextRef::SetFramePtr(StackFrame *frame) {
+ if (frame)
+ SetFrameSP(frame->shared_from_this());
+ else
+ Clear();
+}
+
+lldb::TargetSP ExecutionContextRef::GetTargetSP() const {
+ lldb::TargetSP target_sp(m_target_wp.lock());
+ if (target_sp && !target_sp->IsValid())
+ target_sp.reset();
+ return target_sp;
+}
+
+lldb::ProcessSP ExecutionContextRef::GetProcessSP() const {
+ lldb::ProcessSP process_sp(m_process_wp.lock());
+ if (process_sp && !process_sp->IsValid())
+ process_sp.reset();
+ return process_sp;
+}
+
+lldb::ThreadSP ExecutionContextRef::GetThreadSP() const {
+ lldb::ThreadSP thread_sp(m_thread_wp.lock());
+
+ if (m_tid != LLDB_INVALID_THREAD_ID) {
+ // We check if the thread has been destroyed in cases where clients might
+ // still have shared pointer to a thread, but the thread is not valid
+ // anymore (not part of the process)
+ if (!thread_sp || !thread_sp->IsValid()) {
+ lldb::ProcessSP process_sp(GetProcessSP());
+ if (process_sp && process_sp->IsValid()) {
+ thread_sp = process_sp->GetThreadList().FindThreadByID(m_tid);
+ m_thread_wp = thread_sp;
+ }
+ }
+ }
+
+ // Check that we aren't about to return an invalid thread sp. We might
+ // return a nullptr thread_sp, but don't return an invalid one.
+
+ if (thread_sp && !thread_sp->IsValid())
+ thread_sp.reset();
+
+ return thread_sp;
+}
+
+lldb::StackFrameSP ExecutionContextRef::GetFrameSP() const {
+ if (m_stack_id.IsValid()) {
+ lldb::ThreadSP thread_sp(GetThreadSP());
+ if (thread_sp)
+ return thread_sp->GetFrameWithStackID(m_stack_id);
+ }
+ return lldb::StackFrameSP();
+}
+
+ExecutionContext
+ExecutionContextRef::Lock(bool thread_and_frame_only_if_stopped) const {
+ return ExecutionContext(this, thread_and_frame_only_if_stopped);
+}
diff --git a/contrib/llvm-project/lldb/source/Target/InstrumentationRuntime.cpp b/contrib/llvm-project/lldb/source/Target/InstrumentationRuntime.cpp
new file mode 100644
index 000000000000..4c2eb064fbb0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/InstrumentationRuntime.cpp
@@ -0,0 +1,75 @@
+//===-- InstrumentationRuntime.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#include "lldb/Target/InstrumentationRuntime.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void InstrumentationRuntime::ModulesDidLoad(
+ lldb_private::ModuleList &module_list, lldb_private::Process *process,
+ InstrumentationRuntimeCollection &runtimes) {
+ InstrumentationRuntimeCreateInstance create_callback = nullptr;
+ InstrumentationRuntimeGetType get_type_callback;
+ for (uint32_t idx = 0;; ++idx) {
+ create_callback =
+ PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex(idx);
+ if (create_callback == nullptr)
+ break;
+ get_type_callback =
+ PluginManager::GetInstrumentationRuntimeGetTypeCallbackAtIndex(idx);
+ InstrumentationRuntimeType type = get_type_callback();
+
+ InstrumentationRuntimeCollection::iterator pos;
+ pos = runtimes.find(type);
+ if (pos == runtimes.end()) {
+ runtimes[type] = create_callback(process->shared_from_this());
+ }
+ }
+}
+
+void InstrumentationRuntime::ModulesDidLoad(
+ lldb_private::ModuleList &module_list) {
+ if (IsActive())
+ return;
+
+ if (GetRuntimeModuleSP()) {
+ Activate();
+ return;
+ }
+
+ module_list.ForEach([this](const lldb::ModuleSP module_sp) -> bool {
+ const FileSpec &file_spec = module_sp->GetFileSpec();
+ if (!file_spec)
+ return true; // Keep iterating.
+
+ const RegularExpression &runtime_regex = GetPatternForRuntimeLibrary();
+ if (runtime_regex.Execute(file_spec.GetFilename().GetCString()) ||
+ module_sp->IsExecutable()) {
+ if (CheckIfRuntimeIsValid(module_sp)) {
+ SetRuntimeModuleSP(module_sp);
+ Activate();
+ return false; // Stop iterating, we're done.
+ }
+ }
+
+ return true;
+ });
+}
+
+lldb::ThreadCollectionSP
+InstrumentationRuntime::GetBacktracesFromExtendedStopInfo(
+ StructuredData::ObjectSP info) {
+ return ThreadCollectionSP(new ThreadCollection());
+}
diff --git a/contrib/llvm-project/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp b/contrib/llvm-project/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp
new file mode 100644
index 000000000000..f2767ffbff0d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp
@@ -0,0 +1,36 @@
+//===-- InstrumentationRuntimeStopInfo.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
+
+#include "lldb/Target/InstrumentationRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+InstrumentationRuntimeStopInfo::InstrumentationRuntimeStopInfo(
+ Thread &thread, std::string description,
+ StructuredData::ObjectSP additional_data)
+ : StopInfo(thread, 0) {
+ m_extended_info = additional_data;
+ m_description = description;
+}
+
+const char *InstrumentationRuntimeStopInfo::GetDescription() {
+ return m_description.c_str();
+}
+
+StopInfoSP
+InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
+ Thread &thread, std::string description,
+ StructuredData::ObjectSP additionalData) {
+ return StopInfoSP(
+ new InstrumentationRuntimeStopInfo(thread, description, additionalData));
+}
diff --git a/contrib/llvm-project/lldb/source/Target/JITLoader.cpp b/contrib/llvm-project/lldb/source/Target/JITLoader.cpp
new file mode 100644
index 000000000000..e7c13bddcb36
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/JITLoader.cpp
@@ -0,0 +1,32 @@
+//===-- JITLoader.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/JITLoader.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/JITLoaderList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void JITLoader::LoadPlugins(Process *process, JITLoaderList &list) {
+ JITLoaderCreateInstance create_callback = nullptr;
+ for (uint32_t idx = 0;
+ (create_callback =
+ PluginManager::GetJITLoaderCreateCallbackAtIndex(idx)) != nullptr;
+ ++idx) {
+ JITLoaderSP instance_sp(create_callback(process, false));
+ if (instance_sp)
+ list.Append(std::move(instance_sp));
+ }
+}
+
+JITLoader::JITLoader(Process *process) : m_process(process) {}
+
+JITLoader::~JITLoader() = default;
diff --git a/contrib/llvm-project/lldb/source/Target/JITLoaderList.cpp b/contrib/llvm-project/lldb/source/Target/JITLoaderList.cpp
new file mode 100644
index 000000000000..4d1f06d0843e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/JITLoaderList.cpp
@@ -0,0 +1,55 @@
+//===-- JITLoaderList.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/JITLoader.h"
+#include "lldb/Target/JITLoaderList.h"
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+JITLoaderList::JITLoaderList() : m_jit_loaders_vec(), m_jit_loaders_mutex() {}
+
+JITLoaderList::~JITLoaderList() {}
+
+void JITLoaderList::Append(const JITLoaderSP &jit_loader_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex);
+ m_jit_loaders_vec.push_back(jit_loader_sp);
+}
+
+void JITLoaderList::Remove(const JITLoaderSP &jit_loader_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex);
+ m_jit_loaders_vec.erase(std::remove(m_jit_loaders_vec.begin(),
+ m_jit_loaders_vec.end(), jit_loader_sp),
+ m_jit_loaders_vec.end());
+}
+
+size_t JITLoaderList::GetSize() const { return m_jit_loaders_vec.size(); }
+
+JITLoaderSP JITLoaderList::GetLoaderAtIndex(size_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex);
+ return m_jit_loaders_vec[idx];
+}
+
+void JITLoaderList::DidLaunch() {
+ std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex);
+ for (auto const &jit_loader : m_jit_loaders_vec)
+ jit_loader->DidLaunch();
+}
+
+void JITLoaderList::DidAttach() {
+ std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex);
+ for (auto const &jit_loader : m_jit_loaders_vec)
+ jit_loader->DidAttach();
+}
+
+void JITLoaderList::ModulesDidLoad(ModuleList &module_list) {
+ std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex);
+ for (auto const &jit_loader : m_jit_loaders_vec)
+ jit_loader->ModulesDidLoad(module_list);
+}
diff --git a/contrib/llvm-project/lldb/source/Target/Language.cpp b/contrib/llvm-project/lldb/source/Target/Language.cpp
new file mode 100644
index 000000000000..3c3ef2841d44
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/Language.cpp
@@ -0,0 +1,477 @@
+//===-- Language.cpp -------------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <functional>
+#include <map>
+#include <mutex>
+
+#include "lldb/Target/Language.h"
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/Support/Threading.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+typedef std::unique_ptr<Language> LanguageUP;
+typedef std::map<lldb::LanguageType, LanguageUP> LanguagesMap;
+
+static LanguagesMap &GetLanguagesMap() {
+ static LanguagesMap *g_map = nullptr;
+ static llvm::once_flag g_initialize;
+
+ llvm::call_once(g_initialize, [] {
+ g_map = new LanguagesMap(); // NOTE: INTENTIONAL LEAK due to global
+ // destructor chain
+ });
+
+ return *g_map;
+}
+static std::mutex &GetLanguagesMutex() {
+ static std::mutex *g_mutex = nullptr;
+ static llvm::once_flag g_initialize;
+
+ llvm::call_once(g_initialize, [] {
+ g_mutex = new std::mutex(); // NOTE: INTENTIONAL LEAK due to global
+ // destructor chain
+ });
+
+ return *g_mutex;
+}
+
+Language *Language::FindPlugin(lldb::LanguageType language) {
+ std::lock_guard<std::mutex> guard(GetLanguagesMutex());
+ LanguagesMap &map(GetLanguagesMap());
+ auto iter = map.find(language), end = map.end();
+ if (iter != end)
+ return iter->second.get();
+
+ Language *language_ptr = nullptr;
+ LanguageCreateInstance create_callback;
+
+ for (uint32_t idx = 0;
+ (create_callback =
+ PluginManager::GetLanguageCreateCallbackAtIndex(idx)) != nullptr;
+ ++idx) {
+ language_ptr = create_callback(language);
+
+ if (language_ptr) {
+ map[language] = std::unique_ptr<Language>(language_ptr);
+ return language_ptr;
+ }
+ }
+
+ return nullptr;
+}
+
+Language *Language::FindPlugin(llvm::StringRef file_path) {
+ Language *result = nullptr;
+ ForEach([&result, file_path](Language *language) {
+ if (language->IsSourceFile(file_path)) {
+ result = language;
+ return false;
+ }
+ return true;
+ });
+ return result;
+}
+
+Language *Language::FindPlugin(LanguageType language,
+ llvm::StringRef file_path) {
+ Language *result = FindPlugin(language);
+ // Finding a language by file path is slower, we so we use this as the
+ // fallback.
+ if (!result)
+ result = FindPlugin(file_path);
+ return result;
+}
+
+void Language::ForEach(std::function<bool(Language *)> callback) {
+ // If we want to iterate over all languages, we first have to complete the
+ // LanguagesMap.
+ static llvm::once_flag g_initialize;
+ llvm::call_once(g_initialize, [] {
+ for (unsigned lang = eLanguageTypeUnknown; lang < eNumLanguageTypes;
+ ++lang) {
+ FindPlugin(static_cast<lldb::LanguageType>(lang));
+ }
+ });
+
+ std::lock_guard<std::mutex> guard(GetLanguagesMutex());
+ LanguagesMap &map(GetLanguagesMap());
+ for (const auto &entry : map) {
+ if (!callback(entry.second.get()))
+ break;
+ }
+}
+
+bool Language::IsTopLevelFunction(Function &function) { return false; }
+
+lldb::TypeCategoryImplSP Language::GetFormatters() { return nullptr; }
+
+HardcodedFormatters::HardcodedFormatFinder Language::GetHardcodedFormats() {
+ return {};
+}
+
+HardcodedFormatters::HardcodedSummaryFinder Language::GetHardcodedSummaries() {
+ return {};
+}
+
+HardcodedFormatters::HardcodedSyntheticFinder
+Language::GetHardcodedSynthetics() {
+ return {};
+}
+
+HardcodedFormatters::HardcodedValidatorFinder
+Language::GetHardcodedValidators() {
+ return {};
+}
+
+std::vector<ConstString>
+Language::GetPossibleFormattersMatches(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ return {};
+}
+
+lldb_private::formatters::StringPrinter::EscapingHelper
+Language::GetStringPrinterEscapingHelper(
+ lldb_private::formatters::StringPrinter::GetPrintableElementType
+ elem_type) {
+ return StringPrinter::GetDefaultEscapingHelper(elem_type);
+}
+
+struct language_name_pair {
+ const char *name;
+ LanguageType type;
+};
+
+struct language_name_pair language_names[] = {
+ // To allow GetNameForLanguageType to be a simple array lookup, the first
+ // part of this array must follow enum LanguageType exactly.
+ {"unknown", eLanguageTypeUnknown},
+ {"c89", eLanguageTypeC89},
+ {"c", eLanguageTypeC},
+ {"ada83", eLanguageTypeAda83},
+ {"c++", eLanguageTypeC_plus_plus},
+ {"cobol74", eLanguageTypeCobol74},
+ {"cobol85", eLanguageTypeCobol85},
+ {"fortran77", eLanguageTypeFortran77},
+ {"fortran90", eLanguageTypeFortran90},
+ {"pascal83", eLanguageTypePascal83},
+ {"modula2", eLanguageTypeModula2},
+ {"java", eLanguageTypeJava},
+ {"c99", eLanguageTypeC99},
+ {"ada95", eLanguageTypeAda95},
+ {"fortran95", eLanguageTypeFortran95},
+ {"pli", eLanguageTypePLI},
+ {"objective-c", eLanguageTypeObjC},
+ {"objective-c++", eLanguageTypeObjC_plus_plus},
+ {"upc", eLanguageTypeUPC},
+ {"d", eLanguageTypeD},
+ {"python", eLanguageTypePython},
+ {"opencl", eLanguageTypeOpenCL},
+ {"go", eLanguageTypeGo},
+ {"modula3", eLanguageTypeModula3},
+ {"haskell", eLanguageTypeHaskell},
+ {"c++03", eLanguageTypeC_plus_plus_03},
+ {"c++11", eLanguageTypeC_plus_plus_11},
+ {"ocaml", eLanguageTypeOCaml},
+ {"rust", eLanguageTypeRust},
+ {"c11", eLanguageTypeC11},
+ {"swift", eLanguageTypeSwift},
+ {"julia", eLanguageTypeJulia},
+ {"dylan", eLanguageTypeDylan},
+ {"c++14", eLanguageTypeC_plus_plus_14},
+ {"fortran03", eLanguageTypeFortran03},
+ {"fortran08", eLanguageTypeFortran08},
+ // Vendor Extensions
+ {"mipsassem", eLanguageTypeMipsAssembler},
+ {"renderscript", eLanguageTypeExtRenderScript},
+ // Now synonyms, in arbitrary order
+ {"objc", eLanguageTypeObjC},
+ {"objc++", eLanguageTypeObjC_plus_plus},
+ {"pascal", eLanguageTypePascal83}};
+
+static uint32_t num_languages =
+ sizeof(language_names) / sizeof(struct language_name_pair);
+
+LanguageType Language::GetLanguageTypeFromString(llvm::StringRef string) {
+ for (const auto &L : language_names) {
+ if (string.equals_lower(L.name))
+ return static_cast<LanguageType>(L.type);
+ }
+
+ return eLanguageTypeUnknown;
+}
+
+const char *Language::GetNameForLanguageType(LanguageType language) {
+ if (language < num_languages)
+ return language_names[language].name;
+ else
+ return language_names[eLanguageTypeUnknown].name;
+}
+
+void Language::PrintAllLanguages(Stream &s, const char *prefix,
+ const char *suffix) {
+ for (uint32_t i = 1; i < num_languages; i++) {
+ s.Printf("%s%s%s", prefix, language_names[i].name, suffix);
+ }
+}
+
+void Language::ForAllLanguages(
+ std::function<bool(lldb::LanguageType)> callback) {
+ for (uint32_t i = 1; i < num_languages; i++) {
+ if (!callback(language_names[i].type))
+ break;
+ }
+}
+
+bool Language::LanguageIsCPlusPlus(LanguageType language) {
+ switch (language) {
+ case eLanguageTypeC_plus_plus:
+ case eLanguageTypeC_plus_plus_03:
+ case eLanguageTypeC_plus_plus_11:
+ case eLanguageTypeC_plus_plus_14:
+ case eLanguageTypeObjC_plus_plus:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Language::LanguageIsObjC(LanguageType language) {
+ switch (language) {
+ case eLanguageTypeObjC:
+ case eLanguageTypeObjC_plus_plus:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Language::LanguageIsC(LanguageType language) {
+ switch (language) {
+ case eLanguageTypeC:
+ case eLanguageTypeC89:
+ case eLanguageTypeC99:
+ case eLanguageTypeC11:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Language::LanguageIsCFamily(LanguageType language) {
+ switch (language) {
+ case eLanguageTypeC:
+ case eLanguageTypeC89:
+ case eLanguageTypeC99:
+ case eLanguageTypeC11:
+ case eLanguageTypeC_plus_plus:
+ case eLanguageTypeC_plus_plus_03:
+ case eLanguageTypeC_plus_plus_11:
+ case eLanguageTypeC_plus_plus_14:
+ case eLanguageTypeObjC_plus_plus:
+ case eLanguageTypeObjC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Language::LanguageIsPascal(LanguageType language) {
+ switch (language) {
+ case eLanguageTypePascal83:
+ return true;
+ default:
+ return false;
+ }
+}
+
+LanguageType Language::GetPrimaryLanguage(LanguageType language) {
+ switch (language) {
+ case eLanguageTypeC_plus_plus:
+ case eLanguageTypeC_plus_plus_03:
+ case eLanguageTypeC_plus_plus_11:
+ case eLanguageTypeC_plus_plus_14:
+ return eLanguageTypeC_plus_plus;
+ case eLanguageTypeC:
+ case eLanguageTypeC89:
+ case eLanguageTypeC99:
+ case eLanguageTypeC11:
+ return eLanguageTypeC;
+ case eLanguageTypeObjC:
+ case eLanguageTypeObjC_plus_plus:
+ return eLanguageTypeObjC;
+ case eLanguageTypePascal83:
+ case eLanguageTypeCobol74:
+ case eLanguageTypeCobol85:
+ case eLanguageTypeFortran77:
+ case eLanguageTypeFortran90:
+ case eLanguageTypeFortran95:
+ case eLanguageTypeFortran03:
+ case eLanguageTypeFortran08:
+ case eLanguageTypeAda83:
+ case eLanguageTypeAda95:
+ case eLanguageTypeModula2:
+ case eLanguageTypeJava:
+ case eLanguageTypePLI:
+ case eLanguageTypeUPC:
+ case eLanguageTypeD:
+ case eLanguageTypePython:
+ case eLanguageTypeOpenCL:
+ case eLanguageTypeGo:
+ case eLanguageTypeModula3:
+ case eLanguageTypeHaskell:
+ case eLanguageTypeOCaml:
+ case eLanguageTypeRust:
+ case eLanguageTypeSwift:
+ case eLanguageTypeJulia:
+ case eLanguageTypeDylan:
+ case eLanguageTypeMipsAssembler:
+ case eLanguageTypeExtRenderScript:
+ case eLanguageTypeUnknown:
+ default:
+ return language;
+ }
+}
+
+std::set<lldb::LanguageType> Language::GetSupportedLanguages() {
+ std::set<lldb::LanguageType> supported_languages;
+ ForEach([&](Language *lang) {
+ supported_languages.emplace(lang->GetLanguageType());
+ return true;
+ });
+ return supported_languages;
+}
+
+void Language::GetLanguagesSupportingTypeSystems(
+ std::set<lldb::LanguageType> &languages,
+ std::set<lldb::LanguageType> &languages_for_expressions) {
+ uint32_t idx = 0;
+
+ while (TypeSystemEnumerateSupportedLanguages enumerate = PluginManager::
+ GetTypeSystemEnumerateSupportedLanguagesCallbackAtIndex(idx++)) {
+ (*enumerate)(languages, languages_for_expressions);
+ }
+}
+
+void Language::GetLanguagesSupportingREPLs(
+ std::set<lldb::LanguageType> &languages) {
+ uint32_t idx = 0;
+
+ while (REPLEnumerateSupportedLanguages enumerate =
+ PluginManager::GetREPLEnumerateSupportedLanguagesCallbackAtIndex(
+ idx++)) {
+ (*enumerate)(languages);
+ }
+}
+
+std::unique_ptr<Language::TypeScavenger> Language::GetTypeScavenger() {
+ return nullptr;
+}
+
+const char *Language::GetLanguageSpecificTypeLookupHelp() { return nullptr; }
+
+size_t Language::TypeScavenger::Find(ExecutionContextScope *exe_scope,
+ const char *key, ResultSet &results,
+ bool append) {
+ if (!exe_scope || !exe_scope->CalculateTarget().get())
+ return false;
+
+ if (!key || !key[0])
+ return false;
+
+ if (!append)
+ results.clear();
+
+ size_t old_size = results.size();
+
+ if (this->Find_Impl(exe_scope, key, results))
+ return results.size() - old_size;
+ return 0;
+}
+
+bool Language::ImageListTypeScavenger::Find_Impl(
+ ExecutionContextScope *exe_scope, const char *key, ResultSet &results) {
+ bool result = false;
+
+ Target *target = exe_scope->CalculateTarget().get();
+ if (target) {
+ const auto &images(target->GetImages());
+ ConstString cs_key(key);
+ llvm::DenseSet<SymbolFile *> searched_sym_files;
+ TypeList matches;
+ images.FindTypes(nullptr, cs_key, false, UINT32_MAX, searched_sym_files,
+ matches);
+ for (const auto &match : matches.Types()) {
+ if (match) {
+ CompilerType compiler_type(match->GetFullCompilerType());
+ compiler_type = AdjustForInclusion(compiler_type);
+ if (!compiler_type)
+ continue;
+ std::unique_ptr<Language::TypeScavenger::Result> scavengeresult(
+ new Result(compiler_type));
+ results.insert(std::move(scavengeresult));
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+bool Language::GetFormatterPrefixSuffix(ValueObject &valobj,
+ ConstString type_hint,
+ std::string &prefix,
+ std::string &suffix) {
+ return false;
+}
+
+DumpValueObjectOptions::DeclPrintingHelper Language::GetDeclPrintingHelper() {
+ return nullptr;
+}
+
+LazyBool Language::IsLogicalTrue(ValueObject &valobj, Status &error) {
+ return eLazyBoolCalculate;
+}
+
+bool Language::IsNilReference(ValueObject &valobj) { return false; }
+
+bool Language::IsUninitializedReference(ValueObject &valobj) { return false; }
+
+bool Language::GetFunctionDisplayName(const SymbolContext *sc,
+ const ExecutionContext *exe_ctx,
+ FunctionNameRepresentation representation,
+ Stream &s) {
+ return false;
+}
+
+void Language::GetExceptionResolverDescription(bool catch_on, bool throw_on,
+ Stream &s) {
+ GetDefaultExceptionResolverDescription(catch_on, throw_on, s);
+}
+
+void Language::GetDefaultExceptionResolverDescription(bool catch_on,
+ bool throw_on,
+ Stream &s) {
+ s.Printf("Exception breakpoint (catch: %s throw: %s)",
+ catch_on ? "on" : "off", throw_on ? "on" : "off");
+}
+// Constructor
+Language::Language() {}
+
+// Destructor
+Language::~Language() {}
diff --git a/contrib/llvm-project/lldb/source/Target/LanguageRuntime.cpp b/contrib/llvm-project/lldb/source/Target/LanguageRuntime.cpp
new file mode 100644
index 000000000000..dd4415810613
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/LanguageRuntime.cpp
@@ -0,0 +1,296 @@
+//===-- LanguageRuntime.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/SearchFilter.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+char LanguageRuntime::ID = 0;
+
+ExceptionSearchFilter::ExceptionSearchFilter(const lldb::TargetSP &target_sp,
+ lldb::LanguageType language,
+ bool update_module_list)
+ : SearchFilter(target_sp, FilterTy::Exception), m_language(language),
+ m_language_runtime(nullptr), m_filter_sp() {
+ if (update_module_list)
+ UpdateModuleListIfNeeded();
+}
+
+bool ExceptionSearchFilter::ModulePasses(const lldb::ModuleSP &module_sp) {
+ UpdateModuleListIfNeeded();
+ if (m_filter_sp)
+ return m_filter_sp->ModulePasses(module_sp);
+ return false;
+}
+
+bool ExceptionSearchFilter::ModulePasses(const FileSpec &spec) {
+ UpdateModuleListIfNeeded();
+ if (m_filter_sp)
+ return m_filter_sp->ModulePasses(spec);
+ return false;
+}
+
+void ExceptionSearchFilter::Search(Searcher &searcher) {
+ UpdateModuleListIfNeeded();
+ if (m_filter_sp)
+ m_filter_sp->Search(searcher);
+}
+
+void ExceptionSearchFilter::GetDescription(Stream *s) {
+ UpdateModuleListIfNeeded();
+ if (m_filter_sp)
+ m_filter_sp->GetDescription(s);
+}
+
+void ExceptionSearchFilter::UpdateModuleListIfNeeded() {
+ ProcessSP process_sp(m_target_sp->GetProcessSP());
+ if (process_sp) {
+ bool refreash_filter = !m_filter_sp;
+ if (m_language_runtime == nullptr) {
+ m_language_runtime = process_sp->GetLanguageRuntime(m_language);
+ refreash_filter = true;
+ } else {
+ LanguageRuntime *language_runtime =
+ process_sp->GetLanguageRuntime(m_language);
+ if (m_language_runtime != language_runtime) {
+ m_language_runtime = language_runtime;
+ refreash_filter = true;
+ }
+ }
+
+ if (refreash_filter && m_language_runtime) {
+ m_filter_sp = m_language_runtime->CreateExceptionSearchFilter();
+ }
+ } else {
+ m_filter_sp.reset();
+ m_language_runtime = nullptr;
+ }
+}
+
+SearchFilterSP
+ExceptionSearchFilter::DoCopyForBreakpoint(Breakpoint &breakpoint) {
+ return SearchFilterSP(
+ new ExceptionSearchFilter(TargetSP(), m_language, false));
+}
+
+SearchFilter *ExceptionSearchFilter::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &data_dict,
+ Status &error) {
+ SearchFilter *result = nullptr;
+ return result;
+}
+
+StructuredData::ObjectSP ExceptionSearchFilter::SerializeToStructuredData() {
+ StructuredData::ObjectSP result_sp;
+
+ return result_sp;
+}
+
+// The Target is the one that knows how to create breakpoints, so this function
+// is meant to be used either by the target or internally in
+// Set/ClearExceptionBreakpoints.
+class ExceptionBreakpointResolver : public BreakpointResolver {
+public:
+ ExceptionBreakpointResolver(lldb::LanguageType language, bool catch_bp,
+ bool throw_bp)
+ : BreakpointResolver(nullptr, BreakpointResolver::ExceptionResolver),
+ m_language(language), m_language_runtime(nullptr), m_catch_bp(catch_bp),
+ m_throw_bp(throw_bp) {}
+
+ ~ExceptionBreakpointResolver() override = default;
+
+ Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) override {
+
+ if (SetActualResolver())
+ return m_actual_resolver_sp->SearchCallback(filter, context, addr,
+ containing);
+ else
+ return eCallbackReturnStop;
+ }
+
+ lldb::SearchDepth GetDepth() override {
+ if (SetActualResolver())
+ return m_actual_resolver_sp->GetDepth();
+ else
+ return lldb::eSearchDepthTarget;
+ }
+
+ void GetDescription(Stream *s) override {
+ Language *language_plugin = Language::FindPlugin(m_language);
+ if (language_plugin)
+ language_plugin->GetExceptionResolverDescription(m_catch_bp, m_throw_bp,
+ *s);
+ else
+ Language::GetDefaultExceptionResolverDescription(m_catch_bp, m_throw_bp,
+ *s);
+
+ SetActualResolver();
+ if (m_actual_resolver_sp) {
+ s->Printf(" using: ");
+ m_actual_resolver_sp->GetDescription(s);
+ } else
+ s->Printf(" the correct runtime exception handler will be determined "
+ "when you run");
+ }
+
+ void Dump(Stream *s) const override {}
+
+ /// Methods for support type inquiry through isa, cast, and dyn_cast:
+ static inline bool classof(const BreakpointResolverName *) { return true; }
+ static inline bool classof(const BreakpointResolver *V) {
+ return V->getResolverID() == BreakpointResolver::ExceptionResolver;
+ }
+
+protected:
+ BreakpointResolverSP CopyForBreakpoint(Breakpoint &breakpoint) override {
+ return BreakpointResolverSP(
+ new ExceptionBreakpointResolver(m_language, m_catch_bp, m_throw_bp));
+ }
+
+ bool SetActualResolver() {
+ ProcessSP process_sp;
+ if (m_breakpoint) {
+ process_sp = m_breakpoint->GetTarget().GetProcessSP();
+ if (process_sp) {
+ bool refreash_resolver = !m_actual_resolver_sp;
+ if (m_language_runtime == nullptr) {
+ m_language_runtime = process_sp->GetLanguageRuntime(m_language);
+ refreash_resolver = true;
+ } else {
+ LanguageRuntime *language_runtime =
+ process_sp->GetLanguageRuntime(m_language);
+ if (m_language_runtime != language_runtime) {
+ m_language_runtime = language_runtime;
+ refreash_resolver = true;
+ }
+ }
+
+ if (refreash_resolver && m_language_runtime) {
+ m_actual_resolver_sp = m_language_runtime->CreateExceptionResolver(
+ m_breakpoint, m_catch_bp, m_throw_bp);
+ }
+ } else {
+ m_actual_resolver_sp.reset();
+ m_language_runtime = nullptr;
+ }
+ } else {
+ m_actual_resolver_sp.reset();
+ m_language_runtime = nullptr;
+ }
+ return (bool)m_actual_resolver_sp;
+ }
+
+ lldb::BreakpointResolverSP m_actual_resolver_sp;
+ lldb::LanguageType m_language;
+ LanguageRuntime *m_language_runtime;
+ bool m_catch_bp;
+ bool m_throw_bp;
+};
+
+LanguageRuntime *LanguageRuntime::FindPlugin(Process *process,
+ lldb::LanguageType language) {
+ std::unique_ptr<LanguageRuntime> language_runtime_up;
+ LanguageRuntimeCreateInstance create_callback;
+
+ for (uint32_t idx = 0;
+ (create_callback =
+ PluginManager::GetLanguageRuntimeCreateCallbackAtIndex(idx)) !=
+ nullptr;
+ ++idx) {
+ language_runtime_up.reset(create_callback(process, language));
+
+ if (language_runtime_up)
+ return language_runtime_up.release();
+ }
+
+ return nullptr;
+}
+
+LanguageRuntime::LanguageRuntime(Process *process) : m_process(process) {}
+
+LanguageRuntime::~LanguageRuntime() = default;
+
+BreakpointPreconditionSP
+LanguageRuntime::GetExceptionPrecondition(LanguageType language,
+ bool throw_bp) {
+ LanguageRuntimeCreateInstance create_callback;
+ for (uint32_t idx = 0;
+ (create_callback =
+ PluginManager::GetLanguageRuntimeCreateCallbackAtIndex(idx)) !=
+ nullptr;
+ idx++) {
+ if (auto precondition_callback =
+ PluginManager::GetLanguageRuntimeGetExceptionPreconditionAtIndex(
+ idx)) {
+ if (BreakpointPreconditionSP precond =
+ precondition_callback(language, throw_bp))
+ return precond;
+ }
+ }
+ return BreakpointPreconditionSP();
+}
+
+BreakpointSP LanguageRuntime::CreateExceptionBreakpoint(
+ Target &target, lldb::LanguageType language, bool catch_bp, bool throw_bp,
+ bool is_internal) {
+ BreakpointResolverSP resolver_sp(
+ new ExceptionBreakpointResolver(language, catch_bp, throw_bp));
+ SearchFilterSP filter_sp(
+ new ExceptionSearchFilter(target.shared_from_this(), language));
+ bool hardware = false;
+ bool resolve_indirect_functions = false;
+ BreakpointSP exc_breakpt_sp(
+ target.CreateBreakpoint(filter_sp, resolver_sp, is_internal, hardware,
+ resolve_indirect_functions));
+ if (exc_breakpt_sp) {
+ if (auto precond = GetExceptionPrecondition(language, throw_bp))
+ exc_breakpt_sp->SetPrecondition(precond);
+
+ if (is_internal)
+ exc_breakpt_sp->SetBreakpointKind("exception");
+ }
+
+ return exc_breakpt_sp;
+}
+
+void LanguageRuntime::InitializeCommands(CommandObject *parent) {
+ if (!parent)
+ return;
+
+ if (!parent->IsMultiwordObject())
+ return;
+
+ LanguageRuntimeCreateInstance create_callback;
+
+ for (uint32_t idx = 0;
+ (create_callback =
+ PluginManager::GetLanguageRuntimeCreateCallbackAtIndex(idx)) !=
+ nullptr;
+ ++idx) {
+ if (LanguageRuntimeGetCommandObject command_callback =
+ PluginManager::GetLanguageRuntimeGetCommandObjectAtIndex(idx)) {
+ CommandObjectSP command =
+ command_callback(parent->GetCommandInterpreter());
+ if (command) {
+ // the CommandObject vended by a Language plugin cannot be created once
+ // and cached because we may create multiple debuggers and need one
+ // instance of the command each - the implementing function is meant to
+ // create a new instance of the command each time it is invoked.
+ parent->LoadSubCommand(command->GetCommandName().str().c_str(), command);
+ }
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Target/Memory.cpp b/contrib/llvm-project/lldb/source/Target/Memory.cpp
new file mode 100644
index 000000000000..31a378069cec
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/Memory.cpp
@@ -0,0 +1,414 @@
+//===-- Memory.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/Memory.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/Utility/State.h"
+
+#include <cinttypes>
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// MemoryCache constructor
+MemoryCache::MemoryCache(Process &process)
+ : m_mutex(), m_L1_cache(), m_L2_cache(), m_invalid_ranges(),
+ m_process(process),
+ m_L2_cache_line_byte_size(process.GetMemoryCacheLineSize()) {}
+
+// Destructor
+MemoryCache::~MemoryCache() {}
+
+void MemoryCache::Clear(bool clear_invalid_ranges) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_L1_cache.clear();
+ m_L2_cache.clear();
+ if (clear_invalid_ranges)
+ m_invalid_ranges.Clear();
+ m_L2_cache_line_byte_size = m_process.GetMemoryCacheLineSize();
+}
+
+void MemoryCache::AddL1CacheData(lldb::addr_t addr, const void *src,
+ size_t src_len) {
+ AddL1CacheData(
+ addr, DataBufferSP(new DataBufferHeap(DataBufferHeap(src, src_len))));
+}
+
+void MemoryCache::AddL1CacheData(lldb::addr_t addr,
+ const DataBufferSP &data_buffer_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_L1_cache[addr] = data_buffer_sp;
+}
+
+void MemoryCache::Flush(addr_t addr, size_t size) {
+ if (size == 0)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ // Erase any blocks from the L1 cache that intersect with the flush range
+ if (!m_L1_cache.empty()) {
+ AddrRange flush_range(addr, size);
+ BlockMap::iterator pos = m_L1_cache.upper_bound(addr);
+ if (pos != m_L1_cache.begin()) {
+ --pos;
+ }
+ while (pos != m_L1_cache.end()) {
+ AddrRange chunk_range(pos->first, pos->second->GetByteSize());
+ if (!chunk_range.DoesIntersect(flush_range))
+ break;
+ pos = m_L1_cache.erase(pos);
+ }
+ }
+
+ if (!m_L2_cache.empty()) {
+ const uint32_t cache_line_byte_size = m_L2_cache_line_byte_size;
+ const addr_t end_addr = (addr + size - 1);
+ const addr_t first_cache_line_addr = addr - (addr % cache_line_byte_size);
+ const addr_t last_cache_line_addr =
+ end_addr - (end_addr % cache_line_byte_size);
+ // Watch for overflow where size will cause us to go off the end of the
+ // 64 bit address space
+ uint32_t num_cache_lines;
+ if (last_cache_line_addr >= first_cache_line_addr)
+ num_cache_lines = ((last_cache_line_addr - first_cache_line_addr) /
+ cache_line_byte_size) +
+ 1;
+ else
+ num_cache_lines =
+ (UINT64_MAX - first_cache_line_addr + 1) / cache_line_byte_size;
+
+ uint32_t cache_idx = 0;
+ for (addr_t curr_addr = first_cache_line_addr; cache_idx < num_cache_lines;
+ curr_addr += cache_line_byte_size, ++cache_idx) {
+ BlockMap::iterator pos = m_L2_cache.find(curr_addr);
+ if (pos != m_L2_cache.end())
+ m_L2_cache.erase(pos);
+ }
+ }
+}
+
+void MemoryCache::AddInvalidRange(lldb::addr_t base_addr,
+ lldb::addr_t byte_size) {
+ if (byte_size > 0) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ InvalidRanges::Entry range(base_addr, byte_size);
+ m_invalid_ranges.Append(range);
+ m_invalid_ranges.Sort();
+ }
+}
+
+bool MemoryCache::RemoveInvalidRange(lldb::addr_t base_addr,
+ lldb::addr_t byte_size) {
+ if (byte_size > 0) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ const uint32_t idx = m_invalid_ranges.FindEntryIndexThatContains(base_addr);
+ if (idx != UINT32_MAX) {
+ const InvalidRanges::Entry *entry = m_invalid_ranges.GetEntryAtIndex(idx);
+ if (entry->GetRangeBase() == base_addr &&
+ entry->GetByteSize() == byte_size)
+ return m_invalid_ranges.RemoveEntrtAtIndex(idx);
+ }
+ }
+ return false;
+}
+
+size_t MemoryCache::Read(addr_t addr, void *dst, size_t dst_len,
+ Status &error) {
+ size_t bytes_left = dst_len;
+
+ // Check the L1 cache for a range that contain the entire memory read. If we
+ // find a range in the L1 cache that does, we use it. Else we fall back to
+ // reading memory in m_L2_cache_line_byte_size byte sized chunks. The L1
+ // cache contains chunks of memory that are not required to be
+ // m_L2_cache_line_byte_size bytes in size, so we don't try anything tricky
+ // when reading from them (no partial reads from the L1 cache).
+
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_L1_cache.empty()) {
+ AddrRange read_range(addr, dst_len);
+ BlockMap::iterator pos = m_L1_cache.upper_bound(addr);
+ if (pos != m_L1_cache.begin()) {
+ --pos;
+ }
+ AddrRange chunk_range(pos->first, pos->second->GetByteSize());
+ if (chunk_range.Contains(read_range)) {
+ memcpy(dst, pos->second->GetBytes() + (addr - chunk_range.GetRangeBase()),
+ dst_len);
+ return dst_len;
+ }
+ }
+
+ // If this memory read request is larger than the cache line size, then we
+ // (1) try to read as much of it at once as possible, and (2) don't add the
+ // data to the memory cache. We don't want to split a big read up into more
+ // separate reads than necessary, and with a large memory read request, it is
+ // unlikely that the caller function will ask for the next
+ // 4 bytes after the large memory read - so there's little benefit to saving
+ // it in the cache.
+ if (dst && dst_len > m_L2_cache_line_byte_size) {
+ size_t bytes_read =
+ m_process.ReadMemoryFromInferior(addr, dst, dst_len, error);
+ // Add this non block sized range to the L1 cache if we actually read
+ // anything
+ if (bytes_read > 0)
+ AddL1CacheData(addr, dst, bytes_read);
+ return bytes_read;
+ }
+
+ if (dst && bytes_left > 0) {
+ const uint32_t cache_line_byte_size = m_L2_cache_line_byte_size;
+ uint8_t *dst_buf = (uint8_t *)dst;
+ addr_t curr_addr = addr - (addr % cache_line_byte_size);
+ addr_t cache_offset = addr - curr_addr;
+
+ while (bytes_left > 0) {
+ if (m_invalid_ranges.FindEntryThatContains(curr_addr)) {
+ error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64,
+ curr_addr);
+ return dst_len - bytes_left;
+ }
+
+ BlockMap::const_iterator pos = m_L2_cache.find(curr_addr);
+ BlockMap::const_iterator end = m_L2_cache.end();
+
+ if (pos != end) {
+ size_t curr_read_size = cache_line_byte_size - cache_offset;
+ if (curr_read_size > bytes_left)
+ curr_read_size = bytes_left;
+
+ memcpy(dst_buf + dst_len - bytes_left,
+ pos->second->GetBytes() + cache_offset, curr_read_size);
+
+ bytes_left -= curr_read_size;
+ curr_addr += curr_read_size + cache_offset;
+ cache_offset = 0;
+
+ if (bytes_left > 0) {
+ // Get sequential cache page hits
+ for (++pos; (pos != end) && (bytes_left > 0); ++pos) {
+ assert((curr_addr % cache_line_byte_size) == 0);
+
+ if (pos->first != curr_addr)
+ break;
+
+ curr_read_size = pos->second->GetByteSize();
+ if (curr_read_size > bytes_left)
+ curr_read_size = bytes_left;
+
+ memcpy(dst_buf + dst_len - bytes_left, pos->second->GetBytes(),
+ curr_read_size);
+
+ bytes_left -= curr_read_size;
+ curr_addr += curr_read_size;
+
+ // We have a cache page that succeeded to read some bytes but not
+ // an entire page. If this happens, we must cap off how much data
+ // we are able to read...
+ if (pos->second->GetByteSize() != cache_line_byte_size)
+ return dst_len - bytes_left;
+ }
+ }
+ }
+
+ // We need to read from the process
+
+ if (bytes_left > 0) {
+ assert((curr_addr % cache_line_byte_size) == 0);
+ std::unique_ptr<DataBufferHeap> data_buffer_heap_up(
+ new DataBufferHeap(cache_line_byte_size, 0));
+ size_t process_bytes_read = m_process.ReadMemoryFromInferior(
+ curr_addr, data_buffer_heap_up->GetBytes(),
+ data_buffer_heap_up->GetByteSize(), error);
+ if (process_bytes_read == 0)
+ return dst_len - bytes_left;
+
+ if (process_bytes_read != cache_line_byte_size)
+ data_buffer_heap_up->SetByteSize(process_bytes_read);
+ m_L2_cache[curr_addr] = DataBufferSP(data_buffer_heap_up.release());
+ // We have read data and put it into the cache, continue through the
+ // loop again to get the data out of the cache...
+ }
+ }
+ }
+
+ return dst_len - bytes_left;
+}
+
+AllocatedBlock::AllocatedBlock(lldb::addr_t addr, uint32_t byte_size,
+ uint32_t permissions, uint32_t chunk_size)
+ : m_range(addr, byte_size), m_permissions(permissions),
+ m_chunk_size(chunk_size)
+{
+ // The entire address range is free to start with.
+ m_free_blocks.Append(m_range);
+ assert(byte_size > chunk_size);
+}
+
+AllocatedBlock::~AllocatedBlock() {}
+
+lldb::addr_t AllocatedBlock::ReserveBlock(uint32_t size) {
+ // We must return something valid for zero bytes.
+ if (size == 0)
+ size = 1;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ const size_t free_count = m_free_blocks.GetSize();
+ for (size_t i=0; i<free_count; ++i)
+ {
+ auto &free_block = m_free_blocks.GetEntryRef(i);
+ const lldb::addr_t range_size = free_block.GetByteSize();
+ if (range_size >= size)
+ {
+ // We found a free block that is big enough for our data. Figure out how
+ // many chunks we will need and calculate the resulting block size we
+ // will reserve.
+ addr_t addr = free_block.GetRangeBase();
+ size_t num_chunks = CalculateChunksNeededForSize(size);
+ lldb::addr_t block_size = num_chunks * m_chunk_size;
+ lldb::addr_t bytes_left = range_size - block_size;
+ if (bytes_left == 0)
+ {
+ // The newly allocated block will take all of the bytes in this
+ // available block, so we can just add it to the allocated ranges and
+ // remove the range from the free ranges.
+ m_reserved_blocks.Insert(free_block, false);
+ m_free_blocks.RemoveEntryAtIndex(i);
+ }
+ else
+ {
+ // Make the new allocated range and add it to the allocated ranges.
+ Range<lldb::addr_t, uint32_t> reserved_block(free_block);
+ reserved_block.SetByteSize(block_size);
+ // Insert the reserved range and don't combine it with other blocks in
+ // the reserved blocks list.
+ m_reserved_blocks.Insert(reserved_block, false);
+ // Adjust the free range in place since we won't change the sorted
+ // ordering of the m_free_blocks list.
+ free_block.SetRangeBase(reserved_block.GetRangeEnd());
+ free_block.SetByteSize(bytes_left);
+ }
+ LLDB_LOGV(log, "({0}) (size = {1} ({1:x})) => {2:x}", this, size, addr);
+ return addr;
+ }
+ }
+
+ LLDB_LOGV(log, "({0}) (size = {1} ({1:x})) => {2:x}", this, size,
+ LLDB_INVALID_ADDRESS);
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool AllocatedBlock::FreeBlock(addr_t addr) {
+ bool success = false;
+ auto entry_idx = m_reserved_blocks.FindEntryIndexThatContains(addr);
+ if (entry_idx != UINT32_MAX)
+ {
+ m_free_blocks.Insert(m_reserved_blocks.GetEntryRef(entry_idx), true);
+ m_reserved_blocks.RemoveEntryAtIndex(entry_idx);
+ success = true;
+ }
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ LLDB_LOGV(log, "({0}) (addr = {1:x}) => {2}", this, addr, success);
+ return success;
+}
+
+AllocatedMemoryCache::AllocatedMemoryCache(Process &process)
+ : m_process(process), m_mutex(), m_memory_map() {}
+
+AllocatedMemoryCache::~AllocatedMemoryCache() {}
+
+void AllocatedMemoryCache::Clear() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_process.IsAlive()) {
+ PermissionsToBlockMap::iterator pos, end = m_memory_map.end();
+ for (pos = m_memory_map.begin(); pos != end; ++pos)
+ m_process.DoDeallocateMemory(pos->second->GetBaseAddress());
+ }
+ m_memory_map.clear();
+}
+
+AllocatedMemoryCache::AllocatedBlockSP
+AllocatedMemoryCache::AllocatePage(uint32_t byte_size, uint32_t permissions,
+ uint32_t chunk_size, Status &error) {
+ AllocatedBlockSP block_sp;
+ const size_t page_size = 4096;
+ const size_t num_pages = (byte_size + page_size - 1) / page_size;
+ const size_t page_byte_size = num_pages * page_size;
+
+ addr_t addr = m_process.DoAllocateMemory(page_byte_size, permissions, error);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log) {
+ log->Printf("Process::DoAllocateMemory (byte_size = 0x%8.8" PRIx32
+ ", permissions = %s) => 0x%16.16" PRIx64,
+ (uint32_t)page_byte_size, GetPermissionsAsCString(permissions),
+ (uint64_t)addr);
+ }
+
+ if (addr != LLDB_INVALID_ADDRESS) {
+ block_sp = std::make_shared<AllocatedBlock>(addr, page_byte_size,
+ permissions, chunk_size);
+ m_memory_map.insert(std::make_pair(permissions, block_sp));
+ }
+ return block_sp;
+}
+
+lldb::addr_t AllocatedMemoryCache::AllocateMemory(size_t byte_size,
+ uint32_t permissions,
+ Status &error) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ addr_t addr = LLDB_INVALID_ADDRESS;
+ std::pair<PermissionsToBlockMap::iterator, PermissionsToBlockMap::iterator>
+ range = m_memory_map.equal_range(permissions);
+
+ for (PermissionsToBlockMap::iterator pos = range.first; pos != range.second;
+ ++pos) {
+ addr = (*pos).second->ReserveBlock(byte_size);
+ if (addr != LLDB_INVALID_ADDRESS)
+ break;
+ }
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ AllocatedBlockSP block_sp(AllocatePage(byte_size, permissions, 16, error));
+
+ if (block_sp)
+ addr = block_sp->ReserveBlock(byte_size);
+ }
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf(
+ "AllocatedMemoryCache::AllocateMemory (byte_size = 0x%8.8" PRIx32
+ ", permissions = %s) => 0x%16.16" PRIx64,
+ (uint32_t)byte_size, GetPermissionsAsCString(permissions),
+ (uint64_t)addr);
+ return addr;
+}
+
+bool AllocatedMemoryCache::DeallocateMemory(lldb::addr_t addr) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ PermissionsToBlockMap::iterator pos, end = m_memory_map.end();
+ bool success = false;
+ for (pos = m_memory_map.begin(); pos != end; ++pos) {
+ if (pos->second->Contains(addr)) {
+ success = pos->second->FreeBlock(addr);
+ break;
+ }
+ }
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("AllocatedMemoryCache::DeallocateMemory (addr = 0x%16.16" PRIx64
+ ") => %i",
+ (uint64_t)addr, success);
+ return success;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/MemoryHistory.cpp b/contrib/llvm-project/lldb/source/Target/MemoryHistory.cpp
new file mode 100644
index 000000000000..37148e1e72e9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/MemoryHistory.cpp
@@ -0,0 +1,28 @@
+//===-- MemoryHistory.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/MemoryHistory.h"
+#include "lldb/Core/PluginManager.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+lldb::MemoryHistorySP MemoryHistory::FindPlugin(const ProcessSP process) {
+ MemoryHistoryCreateInstance create_callback = nullptr;
+
+ for (uint32_t idx = 0;
+ (create_callback = PluginManager::GetMemoryHistoryCreateCallbackAtIndex(
+ idx)) != nullptr;
+ ++idx) {
+ MemoryHistorySP memory_history_sp(create_callback(process));
+ if (memory_history_sp)
+ return memory_history_sp;
+ }
+
+ return MemoryHistorySP();
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ModuleCache.cpp b/contrib/llvm-project/lldb/source/Target/ModuleCache.cpp
new file mode 100644
index 000000000000..444c9806f98b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ModuleCache.cpp
@@ -0,0 +1,332 @@
+//===--------------------- ModuleCache.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ModuleCache.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/LockFile.h"
+#include "lldb/Utility/Log.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
+
+#include <assert.h>
+
+#include <cstdio>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+const char *kModulesSubdir = ".cache";
+const char *kLockDirName = ".lock";
+const char *kTempFileName = ".temp";
+const char *kTempSymFileName = ".symtemp";
+const char *kSymFileExtension = ".sym";
+const char *kFSIllegalChars = "\\/:*?\"<>|";
+
+std::string GetEscapedHostname(const char *hostname) {
+ if (hostname == nullptr)
+ hostname = "unknown";
+ std::string result(hostname);
+ size_t size = result.size();
+ for (size_t i = 0; i < size; ++i) {
+ if ((result[i] >= 1 && result[i] <= 31) ||
+ strchr(kFSIllegalChars, result[i]) != nullptr)
+ result[i] = '_';
+ }
+ return result;
+}
+
+class ModuleLock {
+private:
+ File m_file;
+ std::unique_ptr<lldb_private::LockFile> m_lock;
+ FileSpec m_file_spec;
+
+public:
+ ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, Status &error);
+ void Delete();
+};
+
+static FileSpec JoinPath(const FileSpec &path1, const char *path2) {
+ FileSpec result_spec(path1);
+ result_spec.AppendPathComponent(path2);
+ return result_spec;
+}
+
+static Status MakeDirectory(const FileSpec &dir_path) {
+ namespace fs = llvm::sys::fs;
+
+ return fs::create_directories(dir_path.GetPath(), true, fs::perms::owner_all);
+}
+
+FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) {
+ const auto modules_dir_spec = JoinPath(root_dir_spec, kModulesSubdir);
+ return JoinPath(modules_dir_spec, uuid.GetAsString().c_str());
+}
+
+FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) {
+ return FileSpec(module_file_spec.GetPath() + kSymFileExtension);
+}
+
+void DeleteExistingModule(const FileSpec &root_dir_spec,
+ const FileSpec &sysroot_module_path_spec) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
+ UUID module_uuid;
+ {
+ auto module_sp =
+ std::make_shared<Module>(ModuleSpec(sysroot_module_path_spec));
+ module_uuid = module_sp->GetUUID();
+ }
+
+ if (!module_uuid.IsValid())
+ return;
+
+ Status error;
+ ModuleLock lock(root_dir_spec, module_uuid, error);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("Failed to lock module %s: %s",
+ module_uuid.GetAsString().c_str(), error.AsCString());
+ }
+
+ namespace fs = llvm::sys::fs;
+ fs::file_status st;
+ if (status(sysroot_module_path_spec.GetPath(), st))
+ return;
+
+ if (st.getLinkCount() > 2) // module is referred by other hosts.
+ return;
+
+ const auto module_spec_dir = GetModuleDirectory(root_dir_spec, module_uuid);
+ llvm::sys::fs::remove_directories(module_spec_dir.GetPath());
+ lock.Delete();
+}
+
+void DecrementRefExistingModule(const FileSpec &root_dir_spec,
+ const FileSpec &sysroot_module_path_spec) {
+ // Remove $platform/.cache/$uuid folder if nobody else references it.
+ DeleteExistingModule(root_dir_spec, sysroot_module_path_spec);
+
+ // Remove sysroot link.
+ llvm::sys::fs::remove(sysroot_module_path_spec.GetPath());
+
+ FileSpec symfile_spec = GetSymbolFileSpec(sysroot_module_path_spec);
+ llvm::sys::fs::remove(symfile_spec.GetPath());
+}
+
+Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec,
+ const char *hostname,
+ const FileSpec &platform_module_spec,
+ const FileSpec &local_module_spec,
+ bool delete_existing) {
+ const auto sysroot_module_path_spec =
+ JoinPath(JoinPath(root_dir_spec, hostname),
+ platform_module_spec.GetPath().c_str());
+ if (FileSystem::Instance().Exists(sysroot_module_path_spec)) {
+ if (!delete_existing)
+ return Status();
+
+ DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec);
+ }
+
+ const auto error = MakeDirectory(
+ FileSpec(sysroot_module_path_spec.GetDirectory().AsCString()));
+ if (error.Fail())
+ return error;
+
+ return llvm::sys::fs::create_hard_link(local_module_spec.GetPath(),
+ sysroot_module_path_spec.GetPath());
+}
+
+} // namespace
+
+ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid,
+ Status &error) {
+ const auto lock_dir_spec = JoinPath(root_dir_spec, kLockDirName);
+ error = MakeDirectory(lock_dir_spec);
+ if (error.Fail())
+ return;
+
+ m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str());
+ FileSystem::Instance().Open(m_file, m_file_spec,
+ File::eOpenOptionWrite |
+ File::eOpenOptionCanCreate |
+ File::eOpenOptionCloseOnExec);
+ if (!m_file) {
+ error.SetErrorToErrno();
+ return;
+ }
+
+ m_lock.reset(new lldb_private::LockFile(m_file.GetDescriptor()));
+ error = m_lock->WriteLock(0, 1);
+ if (error.Fail())
+ error.SetErrorStringWithFormat("Failed to lock file: %s",
+ error.AsCString());
+}
+
+void ModuleLock::Delete() {
+ if (!m_file)
+ return;
+
+ m_file.Close();
+ llvm::sys::fs::remove(m_file_spec.GetPath());
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+Status ModuleCache::Put(const FileSpec &root_dir_spec, const char *hostname,
+ const ModuleSpec &module_spec, const FileSpec &tmp_file,
+ const FileSpec &target_file) {
+ const auto module_spec_dir =
+ GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
+ const auto module_file_path =
+ JoinPath(module_spec_dir, target_file.GetFilename().AsCString());
+
+ const auto tmp_file_path = tmp_file.GetPath();
+ const auto err_code =
+ llvm::sys::fs::rename(tmp_file_path, module_file_path.GetPath());
+ if (err_code)
+ return Status("Failed to rename file %s to %s: %s", tmp_file_path.c_str(),
+ module_file_path.GetPath().c_str(),
+ err_code.message().c_str());
+
+ const auto error = CreateHostSysRootModuleLink(
+ root_dir_spec, hostname, target_file, module_file_path, true);
+ if (error.Fail())
+ return Status("Failed to create link to %s: %s",
+ module_file_path.GetPath().c_str(), error.AsCString());
+ return Status();
+}
+
+Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname,
+ const ModuleSpec &module_spec,
+ ModuleSP &cached_module_sp, bool *did_create_ptr) {
+ const auto find_it =
+ m_loaded_modules.find(module_spec.GetUUID().GetAsString());
+ if (find_it != m_loaded_modules.end()) {
+ cached_module_sp = (*find_it).second.lock();
+ if (cached_module_sp)
+ return Status();
+ m_loaded_modules.erase(find_it);
+ }
+
+ const auto module_spec_dir =
+ GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
+ const auto module_file_path = JoinPath(
+ module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString());
+
+ if (!FileSystem::Instance().Exists(module_file_path))
+ return Status("Module %s not found", module_file_path.GetPath().c_str());
+ if (FileSystem::Instance().GetByteSize(module_file_path) !=
+ module_spec.GetObjectSize())
+ return Status("Module %s has invalid file size",
+ module_file_path.GetPath().c_str());
+
+ // We may have already cached module but downloaded from an another host - in
+ // this case let's create a link to it.
+ auto error = CreateHostSysRootModuleLink(root_dir_spec, hostname,
+ module_spec.GetFileSpec(),
+ module_file_path, false);
+ if (error.Fail())
+ return Status("Failed to create link to %s: %s",
+ module_file_path.GetPath().c_str(), error.AsCString());
+
+ auto cached_module_spec(module_spec);
+ cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5
+ // content hash instead of real UUID.
+ cached_module_spec.GetFileSpec() = module_file_path;
+ cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec();
+
+ error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp,
+ nullptr, nullptr, did_create_ptr, false);
+ if (error.Fail())
+ return error;
+
+ FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
+ if (FileSystem::Instance().Exists(symfile_spec))
+ cached_module_sp->SetSymbolFileFileSpec(symfile_spec);
+
+ m_loaded_modules.insert(
+ std::make_pair(module_spec.GetUUID().GetAsString(), cached_module_sp));
+
+ return Status();
+}
+
+Status ModuleCache::GetAndPut(const FileSpec &root_dir_spec,
+ const char *hostname,
+ const ModuleSpec &module_spec,
+ const ModuleDownloader &module_downloader,
+ const SymfileDownloader &symfile_downloader,
+ lldb::ModuleSP &cached_module_sp,
+ bool *did_create_ptr) {
+ const auto module_spec_dir =
+ GetModuleDirectory(root_dir_spec, module_spec.GetUUID());
+ auto error = MakeDirectory(module_spec_dir);
+ if (error.Fail())
+ return error;
+
+ ModuleLock lock(root_dir_spec, module_spec.GetUUID(), error);
+ if (error.Fail())
+ return Status("Failed to lock module %s: %s",
+ module_spec.GetUUID().GetAsString().c_str(),
+ error.AsCString());
+
+ const auto escaped_hostname(GetEscapedHostname(hostname));
+ // Check local cache for a module.
+ error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,
+ cached_module_sp, did_create_ptr);
+ if (error.Success())
+ return error;
+
+ const auto tmp_download_file_spec = JoinPath(module_spec_dir, kTempFileName);
+ error = module_downloader(module_spec, tmp_download_file_spec);
+ llvm::FileRemover tmp_file_remover(tmp_download_file_spec.GetPath());
+ if (error.Fail())
+ return Status("Failed to download module: %s", error.AsCString());
+
+ // Put downloaded file into local module cache.
+ error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,
+ tmp_download_file_spec, module_spec.GetFileSpec());
+ if (error.Fail())
+ return Status("Failed to put module into cache: %s", error.AsCString());
+
+ tmp_file_remover.releaseFile();
+ error = Get(root_dir_spec, escaped_hostname.c_str(), module_spec,
+ cached_module_sp, did_create_ptr);
+ if (error.Fail())
+ return error;
+
+ // Fetching a symbol file for the module
+ const auto tmp_download_sym_file_spec =
+ JoinPath(module_spec_dir, kTempSymFileName);
+ error = symfile_downloader(cached_module_sp, tmp_download_sym_file_spec);
+ llvm::FileRemover tmp_symfile_remover(tmp_download_sym_file_spec.GetPath());
+ if (error.Fail())
+ // Failed to download a symfile but fetching the module was successful. The
+ // module might contain the necessary symbols and the debugging is also
+ // possible without a symfile.
+ return Status();
+
+ error = Put(root_dir_spec, escaped_hostname.c_str(), module_spec,
+ tmp_download_sym_file_spec,
+ GetSymbolFileSpec(module_spec.GetFileSpec()));
+ if (error.Fail())
+ return Status("Failed to put symbol file into cache: %s",
+ error.AsCString());
+
+ tmp_symfile_remover.releaseFile();
+
+ FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec());
+ cached_module_sp->SetSymbolFileFileSpec(symfile_spec);
+ return Status();
+}
diff --git a/contrib/llvm-project/lldb/source/Target/OperatingSystem.cpp b/contrib/llvm-project/lldb/source/Target/OperatingSystem.cpp
new file mode 100644
index 000000000000..c78c197db5be
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/OperatingSystem.cpp
@@ -0,0 +1,54 @@
+//===-- OperatingSystem.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/OperatingSystem.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+OperatingSystem *OperatingSystem::FindPlugin(Process *process,
+ const char *plugin_name) {
+ OperatingSystemCreateInstance create_callback = nullptr;
+ if (plugin_name) {
+ ConstString const_plugin_name(plugin_name);
+ create_callback =
+ PluginManager::GetOperatingSystemCreateCallbackForPluginName(
+ const_plugin_name);
+ if (create_callback) {
+ std::unique_ptr<OperatingSystem> instance_up(
+ create_callback(process, true));
+ if (instance_up)
+ return instance_up.release();
+ }
+ } else {
+ for (uint32_t idx = 0;
+ (create_callback =
+ PluginManager::GetOperatingSystemCreateCallbackAtIndex(idx)) !=
+ nullptr;
+ ++idx) {
+ std::unique_ptr<OperatingSystem> instance_up(
+ create_callback(process, false));
+ if (instance_up)
+ return instance_up.release();
+ }
+ }
+ return nullptr;
+}
+
+OperatingSystem::OperatingSystem(Process *process) : m_process(process) {}
+
+OperatingSystem::~OperatingSystem() = default;
+
+bool OperatingSystem::IsOperatingSystemPluginThread(
+ const lldb::ThreadSP &thread_sp) {
+ if (thread_sp)
+ return thread_sp->IsOperatingSystemPluginThread();
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/PathMappingList.cpp b/contrib/llvm-project/lldb/source/Target/PathMappingList.cpp
new file mode 100644
index 000000000000..58c2c43c9368
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/PathMappingList.cpp
@@ -0,0 +1,317 @@
+//===-- PathMappingList.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <climits>
+#include <cstring>
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Target/PathMappingList.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-private-enumerations.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+ // We must normalize our path pairs that we store because if we don't then
+ // things won't always work. We found a case where if we did:
+ // (lldb) settings set target.source-map . /tmp
+ // We would store a path pairs of "." and "/tmp" as raw strings. If the debug
+ // info contains "./foo/bar.c", the path will get normalized to "foo/bar.c".
+ // When PathMappingList::RemapPath() is called, it expects the path to start
+ // with the raw path pair, which doesn't work anymore because the paths have
+ // been normalized when the debug info was loaded. So we need to store
+ // nomalized path pairs to ensure things match up.
+ ConstString NormalizePath(ConstString path) {
+ // If we use "path" to construct a FileSpec, it will normalize the path for
+ // us. We then grab the string and turn it back into a ConstString.
+ return ConstString(FileSpec(path.GetStringRef()).GetPath());
+ }
+}
+// PathMappingList constructor
+PathMappingList::PathMappingList()
+ : m_pairs(), m_callback(nullptr), m_callback_baton(nullptr), m_mod_id(0) {}
+
+PathMappingList::PathMappingList(ChangedCallback callback, void *callback_baton)
+ : m_pairs(), m_callback(callback), m_callback_baton(callback_baton),
+ m_mod_id(0) {}
+
+PathMappingList::PathMappingList(const PathMappingList &rhs)
+ : m_pairs(rhs.m_pairs), m_callback(nullptr), m_callback_baton(nullptr),
+ m_mod_id(0) {}
+
+const PathMappingList &PathMappingList::operator=(const PathMappingList &rhs) {
+ if (this != &rhs) {
+ m_pairs = rhs.m_pairs;
+ m_callback = nullptr;
+ m_callback_baton = nullptr;
+ m_mod_id = rhs.m_mod_id;
+ }
+ return *this;
+}
+
+PathMappingList::~PathMappingList() = default;
+
+void PathMappingList::Append(ConstString path,
+ ConstString replacement, bool notify) {
+ ++m_mod_id;
+ m_pairs.emplace_back(pair(NormalizePath(path), NormalizePath(replacement)));
+ if (notify && m_callback)
+ m_callback(*this, m_callback_baton);
+}
+
+void PathMappingList::Append(const PathMappingList &rhs, bool notify) {
+ ++m_mod_id;
+ if (!rhs.m_pairs.empty()) {
+ const_iterator pos, end = rhs.m_pairs.end();
+ for (pos = rhs.m_pairs.begin(); pos != end; ++pos)
+ m_pairs.push_back(*pos);
+ if (notify && m_callback)
+ m_callback(*this, m_callback_baton);
+ }
+}
+
+void PathMappingList::Insert(ConstString path,
+ ConstString replacement, uint32_t index,
+ bool notify) {
+ ++m_mod_id;
+ iterator insert_iter;
+ if (index >= m_pairs.size())
+ insert_iter = m_pairs.end();
+ else
+ insert_iter = m_pairs.begin() + index;
+ m_pairs.emplace(insert_iter, pair(NormalizePath(path),
+ NormalizePath(replacement)));
+ if (notify && m_callback)
+ m_callback(*this, m_callback_baton);
+}
+
+bool PathMappingList::Replace(ConstString path,
+ ConstString replacement, uint32_t index,
+ bool notify) {
+ if (index >= m_pairs.size())
+ return false;
+ ++m_mod_id;
+ m_pairs[index] = pair(NormalizePath(path), NormalizePath(replacement));
+ if (notify && m_callback)
+ m_callback(*this, m_callback_baton);
+ return true;
+}
+
+bool PathMappingList::Remove(size_t index, bool notify) {
+ if (index >= m_pairs.size())
+ return false;
+
+ ++m_mod_id;
+ iterator iter = m_pairs.begin() + index;
+ m_pairs.erase(iter);
+ if (notify && m_callback)
+ m_callback(*this, m_callback_baton);
+ return true;
+}
+
+// For clients which do not need the pair index dumped, pass a pair_index >= 0
+// to only dump the indicated pair.
+void PathMappingList::Dump(Stream *s, int pair_index) {
+ unsigned int numPairs = m_pairs.size();
+
+ if (pair_index < 0) {
+ unsigned int index;
+ for (index = 0; index < numPairs; ++index)
+ s->Printf("[%d] \"%s\" -> \"%s\"\n", index,
+ m_pairs[index].first.GetCString(),
+ m_pairs[index].second.GetCString());
+ } else {
+ if (static_cast<unsigned int>(pair_index) < numPairs)
+ s->Printf("%s -> %s", m_pairs[pair_index].first.GetCString(),
+ m_pairs[pair_index].second.GetCString());
+ }
+}
+
+void PathMappingList::Clear(bool notify) {
+ if (!m_pairs.empty())
+ ++m_mod_id;
+ m_pairs.clear();
+ if (notify && m_callback)
+ m_callback(*this, m_callback_baton);
+}
+
+bool PathMappingList::RemapPath(ConstString path,
+ ConstString &new_path) const {
+ std::string remapped;
+ if (RemapPath(path.GetStringRef(), remapped)) {
+ new_path.SetString(remapped);
+ return true;
+ }
+ return false;
+}
+
+bool PathMappingList::RemapPath(llvm::StringRef path,
+ std::string &new_path) const {
+ if (m_pairs.empty() || path.empty())
+ return false;
+ LazyBool path_is_relative = eLazyBoolCalculate;
+ for (const auto &it : m_pairs) {
+ auto prefix = it.first.GetStringRef();
+ if (!path.consume_front(prefix)) {
+ // Relative paths won't have a leading "./" in them unless "." is the
+ // only thing in the relative path so we need to work around "."
+ // carefully.
+ if (prefix != ".")
+ continue;
+ // We need to figure out if the "path" argument is relative. If it is,
+ // then we should remap, else skip this entry.
+ if (path_is_relative == eLazyBoolCalculate) {
+ path_is_relative =
+ FileSpec(path).IsRelative() ? eLazyBoolYes : eLazyBoolNo;
+ }
+ if (!path_is_relative)
+ continue;
+ }
+ FileSpec remapped(it.second.GetStringRef());
+ remapped.AppendPathComponent(path);
+ new_path = remapped.GetPath();
+ return true;
+ }
+ return false;
+}
+
+bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) const {
+ std::string path = file.GetPath();
+ llvm::StringRef path_ref(path);
+ for (const auto &it : m_pairs) {
+ if (!path_ref.consume_front(it.second.GetStringRef()))
+ continue;
+ fixed.SetFile(it.first.GetStringRef(), FileSpec::Style::native);
+ fixed.AppendPathComponent(path_ref);
+ return true;
+ }
+ return false;
+}
+
+bool PathMappingList::FindFile(const FileSpec &orig_spec,
+ FileSpec &new_spec) const {
+ if (m_pairs.empty())
+ return false;
+
+ std::string orig_path = orig_spec.GetPath();
+
+ if (orig_path.empty())
+ return false;
+
+ bool orig_is_relative = orig_spec.IsRelative();
+
+ for (auto entry : m_pairs) {
+ llvm::StringRef orig_ref(orig_path);
+ llvm::StringRef prefix_ref = entry.first.GetStringRef();
+ if (orig_ref.size() < prefix_ref.size())
+ continue;
+ // We consider a relative prefix or one of just "." to
+ // mean "only apply to relative paths".
+ bool prefix_is_relative = false;
+
+ if (prefix_ref == ".") {
+ prefix_is_relative = true;
+ // Remove the "." since it will have been removed from the
+ // FileSpec paths already.
+ prefix_ref = prefix_ref.drop_front();
+ } else {
+ FileSpec prefix_spec(prefix_ref, FileSpec::Style::native);
+ prefix_is_relative = prefix_spec.IsRelative();
+ }
+ if (prefix_is_relative != orig_is_relative)
+ continue;
+
+ if (orig_ref.consume_front(prefix_ref)) {
+ new_spec.SetFile(entry.second.GetCString(), FileSpec::Style::native);
+ new_spec.AppendPathComponent(orig_ref);
+ if (FileSystem::Instance().Exists(new_spec))
+ return true;
+ }
+ }
+
+ new_spec.Clear();
+ return false;
+}
+
+bool PathMappingList::Replace(ConstString path,
+ ConstString new_path, bool notify) {
+ uint32_t idx = FindIndexForPath(path);
+ if (idx < m_pairs.size()) {
+ ++m_mod_id;
+ m_pairs[idx].second = new_path;
+ if (notify && m_callback)
+ m_callback(*this, m_callback_baton);
+ return true;
+ }
+ return false;
+}
+
+bool PathMappingList::Remove(ConstString path, bool notify) {
+ iterator pos = FindIteratorForPath(path);
+ if (pos != m_pairs.end()) {
+ ++m_mod_id;
+ m_pairs.erase(pos);
+ if (notify && m_callback)
+ m_callback(*this, m_callback_baton);
+ return true;
+ }
+ return false;
+}
+
+PathMappingList::const_iterator
+PathMappingList::FindIteratorForPath(ConstString path) const {
+ const_iterator pos;
+ const_iterator begin = m_pairs.begin();
+ const_iterator end = m_pairs.end();
+
+ for (pos = begin; pos != end; ++pos) {
+ if (pos->first == path)
+ break;
+ }
+ return pos;
+}
+
+PathMappingList::iterator
+PathMappingList::FindIteratorForPath(ConstString path) {
+ iterator pos;
+ iterator begin = m_pairs.begin();
+ iterator end = m_pairs.end();
+
+ for (pos = begin; pos != end; ++pos) {
+ if (pos->first == path)
+ break;
+ }
+ return pos;
+}
+
+bool PathMappingList::GetPathsAtIndex(uint32_t idx, ConstString &path,
+ ConstString &new_path) const {
+ if (idx < m_pairs.size()) {
+ path = m_pairs[idx].first;
+ new_path = m_pairs[idx].second;
+ return true;
+ }
+ return false;
+}
+
+uint32_t PathMappingList::FindIndexForPath(ConstString orig_path) const {
+ const ConstString path = NormalizePath(orig_path);
+ const_iterator pos;
+ const_iterator begin = m_pairs.begin();
+ const_iterator end = m_pairs.end();
+
+ for (pos = begin; pos != end; ++pos) {
+ if (pos->first == path)
+ return std::distance(begin, pos);
+ }
+ return UINT32_MAX;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/Platform.cpp b/contrib/llvm-project/lldb/source/Target/Platform.cpp
new file mode 100644
index 000000000000..710f82eaefa9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/Platform.cpp
@@ -0,0 +1,1924 @@
+//===-- Platform.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+#include <csignal>
+#include <fstream>
+#include <memory>
+#include <vector>
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+#include "lldb/Breakpoint/BreakpointIDList.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/ModuleCache.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StructuredData.h"
+
+#include "llvm/Support/FileSystem.h"
+
+// Define these constants from POSIX mman.h rather than include the file so
+// that they will be correct even when compiled on Linux.
+#define MAP_PRIVATE 2
+#define MAP_ANON 0x1000
+
+using namespace lldb;
+using namespace lldb_private;
+
+static uint32_t g_initialize_count = 0;
+
+// Use a singleton function for g_local_platform_sp to avoid init constructors
+// since LLDB is often part of a shared library
+static PlatformSP &GetHostPlatformSP() {
+ static PlatformSP g_platform_sp;
+ return g_platform_sp;
+}
+
+const char *Platform::GetHostPlatformName() { return "host"; }
+
+namespace {
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"use-module-cache", OptionValue::eTypeBoolean, true, true, nullptr,
+ {}, "Use module cache."},
+ {"module-cache-directory", OptionValue::eTypeFileSpec, true, 0, nullptr,
+ {}, "Root directory for cached modules."}};
+
+enum { ePropertyUseModuleCache, ePropertyModuleCacheDirectory };
+
+} // namespace
+
+ConstString PlatformProperties::GetSettingName() {
+ static ConstString g_setting_name("platform");
+ return g_setting_name;
+}
+
+PlatformProperties::PlatformProperties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_properties);
+
+ auto module_cache_dir = GetModuleCacheDirectory();
+ if (module_cache_dir)
+ return;
+
+ llvm::SmallString<64> user_home_dir;
+ if (!llvm::sys::path::home_directory(user_home_dir))
+ return;
+
+ module_cache_dir = FileSpec(user_home_dir.c_str());
+ module_cache_dir.AppendPathComponent(".lldb");
+ module_cache_dir.AppendPathComponent("module_cache");
+ SetModuleCacheDirectory(module_cache_dir);
+}
+
+bool PlatformProperties::GetUseModuleCache() const {
+ const auto idx = ePropertyUseModuleCache;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool PlatformProperties::SetUseModuleCache(bool use_module_cache) {
+ return m_collection_sp->SetPropertyAtIndexAsBoolean(
+ nullptr, ePropertyUseModuleCache, use_module_cache);
+}
+
+FileSpec PlatformProperties::GetModuleCacheDirectory() const {
+ return m_collection_sp->GetPropertyAtIndexAsFileSpec(
+ nullptr, ePropertyModuleCacheDirectory);
+}
+
+bool PlatformProperties::SetModuleCacheDirectory(const FileSpec &dir_spec) {
+ return m_collection_sp->SetPropertyAtIndexAsFileSpec(
+ nullptr, ePropertyModuleCacheDirectory, dir_spec);
+}
+
+/// Get the native host platform plug-in.
+///
+/// There should only be one of these for each host that LLDB runs
+/// upon that should be statically compiled in and registered using
+/// preprocessor macros or other similar build mechanisms.
+///
+/// This platform will be used as the default platform when launching
+/// or attaching to processes unless another platform is specified.
+PlatformSP Platform::GetHostPlatform() { return GetHostPlatformSP(); }
+
+static std::vector<PlatformSP> &GetPlatformList() {
+ static std::vector<PlatformSP> g_platform_list;
+ return g_platform_list;
+}
+
+static std::recursive_mutex &GetPlatformListMutex() {
+ static std::recursive_mutex g_mutex;
+ return g_mutex;
+}
+
+void Platform::Initialize() { g_initialize_count++; }
+
+void Platform::Terminate() {
+ if (g_initialize_count > 0) {
+ if (--g_initialize_count == 0) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
+ GetPlatformList().clear();
+ }
+ }
+}
+
+const PlatformPropertiesSP &Platform::GetGlobalPlatformProperties() {
+ static const auto g_settings_sp(std::make_shared<PlatformProperties>());
+ return g_settings_sp;
+}
+
+void Platform::SetHostPlatform(const lldb::PlatformSP &platform_sp) {
+ // The native platform should use its static void Platform::Initialize()
+ // function to register itself as the native platform.
+ GetHostPlatformSP() = platform_sp;
+
+ if (platform_sp) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
+ GetPlatformList().push_back(platform_sp);
+ }
+}
+
+Status Platform::GetFileWithUUID(const FileSpec &platform_file,
+ const UUID *uuid_ptr, FileSpec &local_file) {
+ // Default to the local case
+ local_file = platform_file;
+ return Status();
+}
+
+FileSpecList
+Platform::LocateExecutableScriptingResources(Target *target, Module &module,
+ Stream *feedback_stream) {
+ return FileSpecList();
+}
+
+// PlatformSP
+// Platform::FindPlugin (Process *process, ConstString plugin_name)
+//{
+// PlatformCreateInstance create_callback = nullptr;
+// if (plugin_name)
+// {
+// create_callback =
+// PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name);
+// if (create_callback)
+// {
+// ArchSpec arch;
+// if (process)
+// {
+// arch = process->GetTarget().GetArchitecture();
+// }
+// PlatformSP platform_sp(create_callback(process, &arch));
+// if (platform_sp)
+// return platform_sp;
+// }
+// }
+// else
+// {
+// for (uint32_t idx = 0; (create_callback =
+// PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != nullptr;
+// ++idx)
+// {
+// PlatformSP platform_sp(create_callback(process, nullptr));
+// if (platform_sp)
+// return platform_sp;
+// }
+// }
+// return PlatformSP();
+//}
+
+Status Platform::GetSharedModule(const ModuleSpec &module_spec,
+ Process *process, ModuleSP &module_sp,
+ const FileSpecList *module_search_paths_ptr,
+ ModuleSP *old_module_sp_ptr,
+ bool *did_create_ptr) {
+ if (IsHost())
+ return ModuleList::GetSharedModule(
+ module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr,
+ did_create_ptr, false);
+
+ // Module resolver lambda.
+ auto resolver = [&](const ModuleSpec &spec) {
+ Status error(eErrorTypeGeneric);
+ ModuleSpec resolved_spec;
+ // Check if we have sysroot set.
+ if (m_sdk_sysroot) {
+ // Prepend sysroot to module spec.
+ resolved_spec = spec;
+ resolved_spec.GetFileSpec().PrependPathComponent(
+ m_sdk_sysroot.GetStringRef());
+ // Try to get shared module with resolved spec.
+ error = ModuleList::GetSharedModule(
+ resolved_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr,
+ did_create_ptr, false);
+ }
+ // If we don't have sysroot or it didn't work then
+ // try original module spec.
+ if (!error.Success()) {
+ resolved_spec = spec;
+ error = ModuleList::GetSharedModule(
+ resolved_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr,
+ did_create_ptr, false);
+ }
+ if (error.Success() && module_sp)
+ module_sp->SetPlatformFileSpec(resolved_spec.GetFileSpec());
+ return error;
+ };
+
+ return GetRemoteSharedModule(module_spec, process, module_sp, resolver,
+ did_create_ptr);
+}
+
+bool Platform::GetModuleSpec(const FileSpec &module_file_spec,
+ const ArchSpec &arch, ModuleSpec &module_spec) {
+ ModuleSpecList module_specs;
+ if (ObjectFile::GetModuleSpecifications(module_file_spec, 0, 0,
+ module_specs) == 0)
+ return false;
+
+ ModuleSpec matched_module_spec;
+ return module_specs.FindMatchingModuleSpec(ModuleSpec(module_file_spec, arch),
+ module_spec);
+}
+
+PlatformSP Platform::Find(ConstString name) {
+ if (name) {
+ static ConstString g_host_platform_name("host");
+ if (name == g_host_platform_name)
+ return GetHostPlatform();
+
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
+ for (const auto &platform_sp : GetPlatformList()) {
+ if (platform_sp->GetName() == name)
+ return platform_sp;
+ }
+ }
+ return PlatformSP();
+}
+
+PlatformSP Platform::Create(ConstString name, Status &error) {
+ PlatformCreateInstance create_callback = nullptr;
+ lldb::PlatformSP platform_sp;
+ if (name) {
+ static ConstString g_host_platform_name("host");
+ if (name == g_host_platform_name)
+ return GetHostPlatform();
+
+ create_callback =
+ PluginManager::GetPlatformCreateCallbackForPluginName(name);
+ if (create_callback)
+ platform_sp = create_callback(true, nullptr);
+ else
+ error.SetErrorStringWithFormat(
+ "unable to find a plug-in for the platform named \"%s\"",
+ name.GetCString());
+ } else
+ error.SetErrorString("invalid platform name");
+
+ if (platform_sp) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
+ GetPlatformList().push_back(platform_sp);
+ }
+
+ return platform_sp;
+}
+
+PlatformSP Platform::Create(const ArchSpec &arch, ArchSpec *platform_arch_ptr,
+ Status &error) {
+ lldb::PlatformSP platform_sp;
+ if (arch.IsValid()) {
+ // Scope for locker
+ {
+ // First try exact arch matches across all platforms already created
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
+ for (const auto &platform_sp : GetPlatformList()) {
+ if (platform_sp->IsCompatibleArchitecture(arch, true,
+ platform_arch_ptr))
+ return platform_sp;
+ }
+
+ // Next try compatible arch matches across all platforms already created
+ for (const auto &platform_sp : GetPlatformList()) {
+ if (platform_sp->IsCompatibleArchitecture(arch, false,
+ platform_arch_ptr))
+ return platform_sp;
+ }
+ }
+
+ PlatformCreateInstance create_callback;
+ // First try exact arch matches across all platform plug-ins
+ uint32_t idx;
+ for (idx = 0; (create_callback =
+ PluginManager::GetPlatformCreateCallbackAtIndex(idx));
+ ++idx) {
+ if (create_callback) {
+ platform_sp = create_callback(false, &arch);
+ if (platform_sp &&
+ platform_sp->IsCompatibleArchitecture(arch, true,
+ platform_arch_ptr)) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
+ GetPlatformList().push_back(platform_sp);
+ return platform_sp;
+ }
+ }
+ }
+ // Next try compatible arch matches across all platform plug-ins
+ for (idx = 0; (create_callback =
+ PluginManager::GetPlatformCreateCallbackAtIndex(idx));
+ ++idx) {
+ if (create_callback) {
+ platform_sp = create_callback(false, &arch);
+ if (platform_sp &&
+ platform_sp->IsCompatibleArchitecture(arch, false,
+ platform_arch_ptr)) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
+ GetPlatformList().push_back(platform_sp);
+ return platform_sp;
+ }
+ }
+ }
+ } else
+ error.SetErrorString("invalid platform name");
+ if (platform_arch_ptr)
+ platform_arch_ptr->Clear();
+ platform_sp.reset();
+ return platform_sp;
+}
+
+ArchSpec Platform::GetAugmentedArchSpec(Platform *platform, llvm::StringRef triple) {
+ if (platform)
+ return platform->GetAugmentedArchSpec(triple);
+ return HostInfo::GetAugmentedArchSpec(triple);
+}
+
+/// Default Constructor
+Platform::Platform(bool is_host)
+ : m_is_host(is_host), m_os_version_set_while_connected(false),
+ m_system_arch_set_while_connected(false), m_sdk_sysroot(), m_sdk_build(),
+ m_working_dir(), m_remote_url(), m_name(), m_system_arch(), m_mutex(),
+ m_max_uid_name_len(0), m_max_gid_name_len(0), m_supports_rsync(false),
+ m_rsync_opts(), m_rsync_prefix(), m_supports_ssh(false), m_ssh_opts(),
+ m_ignores_remote_hostname(false), m_trap_handlers(),
+ m_calculated_trap_handlers(false),
+ m_module_cache(llvm::make_unique<ModuleCache>()) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p Platform::Platform()", static_cast<void *>(this));
+}
+
+/// Destructor.
+///
+/// The destructor is virtual since this class is designed to be
+/// inherited from by the plug-in instance.
+Platform::~Platform() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p Platform::~Platform()", static_cast<void *>(this));
+}
+
+void Platform::GetStatus(Stream &strm) {
+ std::string s;
+ strm.Printf(" Platform: %s\n", GetPluginName().GetCString());
+
+ ArchSpec arch(GetSystemArchitecture());
+ if (arch.IsValid()) {
+ if (!arch.GetTriple().str().empty()) {
+ strm.Printf(" Triple: ");
+ arch.DumpTriple(strm);
+ strm.EOL();
+ }
+ }
+
+ llvm::VersionTuple os_version = GetOSVersion();
+ if (!os_version.empty()) {
+ strm.Format("OS Version: {0}", os_version.getAsString());
+
+ if (GetOSBuildString(s))
+ strm.Printf(" (%s)", s.c_str());
+
+ strm.EOL();
+ }
+
+ if (GetOSKernelDescription(s))
+ strm.Printf(" Kernel: %s\n", s.c_str());
+
+ if (IsHost()) {
+ strm.Printf(" Hostname: %s\n", GetHostname());
+ } else {
+ const bool is_connected = IsConnected();
+ if (is_connected)
+ strm.Printf(" Hostname: %s\n", GetHostname());
+ strm.Printf(" Connected: %s\n", is_connected ? "yes" : "no");
+ }
+
+ if (GetWorkingDirectory()) {
+ strm.Printf("WorkingDir: %s\n", GetWorkingDirectory().GetCString());
+ }
+ if (!IsConnected())
+ return;
+
+ std::string specific_info(GetPlatformSpecificConnectionInformation());
+
+ if (!specific_info.empty())
+ strm.Printf("Platform-specific connection: %s\n", specific_info.c_str());
+}
+
+llvm::VersionTuple Platform::GetOSVersion(Process *process) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+
+ if (IsHost()) {
+ if (m_os_version.empty()) {
+ // We have a local host platform
+ m_os_version = HostInfo::GetOSVersion();
+ m_os_version_set_while_connected = !m_os_version.empty();
+ }
+ } else {
+ // We have a remote platform. We can only fetch the remote
+ // OS version if we are connected, and we don't want to do it
+ // more than once.
+
+ const bool is_connected = IsConnected();
+
+ bool fetch = false;
+ if (!m_os_version.empty()) {
+ // We have valid OS version info, check to make sure it wasn't manually
+ // set prior to connecting. If it was manually set prior to connecting,
+ // then lets fetch the actual OS version info if we are now connected.
+ if (is_connected && !m_os_version_set_while_connected)
+ fetch = true;
+ } else {
+ // We don't have valid OS version info, fetch it if we are connected
+ fetch = is_connected;
+ }
+
+ if (fetch)
+ m_os_version_set_while_connected = GetRemoteOSVersion();
+ }
+
+ if (!m_os_version.empty())
+ return m_os_version;
+ if (process) {
+ // Check with the process in case it can answer the question if a process
+ // was provided
+ return process->GetHostOSVersion();
+ }
+ return llvm::VersionTuple();
+}
+
+bool Platform::GetOSBuildString(std::string &s) {
+ s.clear();
+
+ if (IsHost())
+#if !defined(__linux__)
+ return HostInfo::GetOSBuildString(s);
+#else
+ return false;
+#endif
+ else
+ return GetRemoteOSBuildString(s);
+}
+
+bool Platform::GetOSKernelDescription(std::string &s) {
+ if (IsHost())
+#if !defined(__linux__)
+ return HostInfo::GetOSKernelDescription(s);
+#else
+ return false;
+#endif
+ else
+ return GetRemoteOSKernelDescription(s);
+}
+
+void Platform::AddClangModuleCompilationOptions(
+ Target *target, std::vector<std::string> &options) {
+ std::vector<std::string> default_compilation_options = {
+ "-x", "c++", "-Xclang", "-nostdsysteminc", "-Xclang", "-nostdsysteminc"};
+
+ options.insert(options.end(), default_compilation_options.begin(),
+ default_compilation_options.end());
+}
+
+FileSpec Platform::GetWorkingDirectory() {
+ if (IsHost()) {
+ llvm::SmallString<64> cwd;
+ if (llvm::sys::fs::current_path(cwd))
+ return {};
+ else {
+ FileSpec file_spec(cwd);
+ FileSystem::Instance().Resolve(file_spec);
+ return file_spec;
+ }
+ } else {
+ if (!m_working_dir)
+ m_working_dir = GetRemoteWorkingDirectory();
+ return m_working_dir;
+ }
+}
+
+struct RecurseCopyBaton {
+ const FileSpec &dst;
+ Platform *platform_ptr;
+ Status error;
+};
+
+static FileSystem::EnumerateDirectoryResult
+RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft,
+ llvm::StringRef path) {
+ RecurseCopyBaton *rc_baton = (RecurseCopyBaton *)baton;
+ FileSpec src(path);
+ namespace fs = llvm::sys::fs;
+ switch (ft) {
+ case fs::file_type::fifo_file:
+ case fs::file_type::socket_file:
+ // we have no way to copy pipes and sockets - ignore them and continue
+ return FileSystem::eEnumerateDirectoryResultNext;
+ break;
+
+ case fs::file_type::directory_file: {
+ // make the new directory and get in there
+ FileSpec dst_dir = rc_baton->dst;
+ if (!dst_dir.GetFilename())
+ dst_dir.GetFilename() = src.GetLastPathComponent();
+ Status error = rc_baton->platform_ptr->MakeDirectory(
+ dst_dir, lldb::eFilePermissionsDirectoryDefault);
+ if (error.Fail()) {
+ rc_baton->error.SetErrorStringWithFormat(
+ "unable to setup directory %s on remote end", dst_dir.GetCString());
+ return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
+ }
+
+ // now recurse
+ std::string src_dir_path(src.GetPath());
+
+ // Make a filespec that only fills in the directory of a FileSpec so when
+ // we enumerate we can quickly fill in the filename for dst copies
+ FileSpec recurse_dst;
+ recurse_dst.GetDirectory().SetCString(dst_dir.GetPath().c_str());
+ RecurseCopyBaton rc_baton2 = {recurse_dst, rc_baton->platform_ptr,
+ Status()};
+ FileSystem::Instance().EnumerateDirectory(src_dir_path, true, true, true,
+ RecurseCopy_Callback, &rc_baton2);
+ if (rc_baton2.error.Fail()) {
+ rc_baton->error.SetErrorString(rc_baton2.error.AsCString());
+ return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
+ }
+ return FileSystem::eEnumerateDirectoryResultNext;
+ } break;
+
+ case fs::file_type::symlink_file: {
+ // copy the file and keep going
+ FileSpec dst_file = rc_baton->dst;
+ if (!dst_file.GetFilename())
+ dst_file.GetFilename() = src.GetFilename();
+
+ FileSpec src_resolved;
+
+ rc_baton->error = FileSystem::Instance().Readlink(src, src_resolved);
+
+ if (rc_baton->error.Fail())
+ return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
+
+ rc_baton->error =
+ rc_baton->platform_ptr->CreateSymlink(dst_file, src_resolved);
+
+ if (rc_baton->error.Fail())
+ return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
+
+ return FileSystem::eEnumerateDirectoryResultNext;
+ } break;
+
+ case fs::file_type::regular_file: {
+ // copy the file and keep going
+ FileSpec dst_file = rc_baton->dst;
+ if (!dst_file.GetFilename())
+ dst_file.GetFilename() = src.GetFilename();
+ Status err = rc_baton->platform_ptr->PutFile(src, dst_file);
+ if (err.Fail()) {
+ rc_baton->error.SetErrorString(err.AsCString());
+ return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
+ }
+ return FileSystem::eEnumerateDirectoryResultNext;
+ } break;
+
+ default:
+ rc_baton->error.SetErrorStringWithFormat(
+ "invalid file detected during copy: %s", src.GetPath().c_str());
+ return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
+ break;
+ }
+ llvm_unreachable("Unhandled file_type!");
+}
+
+Status Platform::Install(const FileSpec &src, const FileSpec &dst) {
+ Status error;
+
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("Platform::Install (src='%s', dst='%s')", src.GetPath().c_str(),
+ dst.GetPath().c_str());
+ FileSpec fixed_dst(dst);
+
+ if (!fixed_dst.GetFilename())
+ fixed_dst.GetFilename() = src.GetFilename();
+
+ FileSpec working_dir = GetWorkingDirectory();
+
+ if (dst) {
+ if (dst.GetDirectory()) {
+ const char first_dst_dir_char = dst.GetDirectory().GetCString()[0];
+ if (first_dst_dir_char == '/' || first_dst_dir_char == '\\') {
+ fixed_dst.GetDirectory() = dst.GetDirectory();
+ }
+ // If the fixed destination file doesn't have a directory yet, then we
+ // must have a relative path. We will resolve this relative path against
+ // the platform's working directory
+ if (!fixed_dst.GetDirectory()) {
+ FileSpec relative_spec;
+ std::string path;
+ if (working_dir) {
+ relative_spec = working_dir;
+ relative_spec.AppendPathComponent(dst.GetPath());
+ fixed_dst.GetDirectory() = relative_spec.GetDirectory();
+ } else {
+ error.SetErrorStringWithFormat(
+ "platform working directory must be valid for relative path '%s'",
+ dst.GetPath().c_str());
+ return error;
+ }
+ }
+ } else {
+ if (working_dir) {
+ fixed_dst.GetDirectory().SetCString(working_dir.GetCString());
+ } else {
+ error.SetErrorStringWithFormat(
+ "platform working directory must be valid for relative path '%s'",
+ dst.GetPath().c_str());
+ return error;
+ }
+ }
+ } else {
+ if (working_dir) {
+ fixed_dst.GetDirectory().SetCString(working_dir.GetCString());
+ } else {
+ error.SetErrorStringWithFormat("platform working directory must be valid "
+ "when destination directory is empty");
+ return error;
+ }
+ }
+
+ if (log)
+ log->Printf("Platform::Install (src='%s', dst='%s') fixed_dst='%s'",
+ src.GetPath().c_str(), dst.GetPath().c_str(),
+ fixed_dst.GetPath().c_str());
+
+ if (GetSupportsRSync()) {
+ error = PutFile(src, dst);
+ } else {
+ namespace fs = llvm::sys::fs;
+ switch (fs::get_file_type(src.GetPath(), false)) {
+ case fs::file_type::directory_file: {
+ llvm::sys::fs::remove(fixed_dst.GetPath());
+ uint32_t permissions = FileSystem::Instance().GetPermissions(src);
+ if (permissions == 0)
+ permissions = eFilePermissionsDirectoryDefault;
+ error = MakeDirectory(fixed_dst, permissions);
+ if (error.Success()) {
+ // Make a filespec that only fills in the directory of a FileSpec so
+ // when we enumerate we can quickly fill in the filename for dst copies
+ FileSpec recurse_dst;
+ recurse_dst.GetDirectory().SetCString(fixed_dst.GetCString());
+ std::string src_dir_path(src.GetPath());
+ RecurseCopyBaton baton = {recurse_dst, this, Status()};
+ FileSystem::Instance().EnumerateDirectory(
+ src_dir_path, true, true, true, RecurseCopy_Callback, &baton);
+ return baton.error;
+ }
+ } break;
+
+ case fs::file_type::regular_file:
+ llvm::sys::fs::remove(fixed_dst.GetPath());
+ error = PutFile(src, fixed_dst);
+ break;
+
+ case fs::file_type::symlink_file: {
+ llvm::sys::fs::remove(fixed_dst.GetPath());
+ FileSpec src_resolved;
+ error = FileSystem::Instance().Readlink(src, src_resolved);
+ if (error.Success())
+ error = CreateSymlink(dst, src_resolved);
+ } break;
+ case fs::file_type::fifo_file:
+ error.SetErrorString("platform install doesn't handle pipes");
+ break;
+ case fs::file_type::socket_file:
+ error.SetErrorString("platform install doesn't handle sockets");
+ break;
+ default:
+ error.SetErrorString(
+ "platform install doesn't handle non file or directory items");
+ break;
+ }
+ }
+ return error;
+}
+
+bool Platform::SetWorkingDirectory(const FileSpec &file_spec) {
+ if (IsHost()) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ LLDB_LOG(log, "{0}", file_spec);
+ if (std::error_code ec = llvm::sys::fs::set_current_path(file_spec.GetPath())) {
+ LLDB_LOG(log, "error: {0}", ec.message());
+ return false;
+ }
+ return true;
+ } else {
+ m_working_dir.Clear();
+ return SetRemoteWorkingDirectory(file_spec);
+ }
+}
+
+Status Platform::MakeDirectory(const FileSpec &file_spec,
+ uint32_t permissions) {
+ if (IsHost())
+ return llvm::sys::fs::create_directory(file_spec.GetPath(), permissions);
+ else {
+ Status error;
+ error.SetErrorStringWithFormat("remote platform %s doesn't support %s",
+ GetPluginName().GetCString(),
+ LLVM_PRETTY_FUNCTION);
+ return error;
+ }
+}
+
+Status Platform::GetFilePermissions(const FileSpec &file_spec,
+ uint32_t &file_permissions) {
+ if (IsHost()) {
+ auto Value = llvm::sys::fs::getPermissions(file_spec.GetPath());
+ if (Value)
+ file_permissions = Value.get();
+ return Status(Value.getError());
+ } else {
+ Status error;
+ error.SetErrorStringWithFormat("remote platform %s doesn't support %s",
+ GetPluginName().GetCString(),
+ LLVM_PRETTY_FUNCTION);
+ return error;
+ }
+}
+
+Status Platform::SetFilePermissions(const FileSpec &file_spec,
+ uint32_t file_permissions) {
+ if (IsHost()) {
+ auto Perms = static_cast<llvm::sys::fs::perms>(file_permissions);
+ return llvm::sys::fs::setPermissions(file_spec.GetPath(), Perms);
+ } else {
+ Status error;
+ error.SetErrorStringWithFormat("remote platform %s doesn't support %s",
+ GetPluginName().GetCString(),
+ LLVM_PRETTY_FUNCTION);
+ return error;
+ }
+}
+
+ConstString Platform::GetName() { return GetPluginName(); }
+
+const char *Platform::GetHostname() {
+ if (IsHost())
+ return "127.0.0.1";
+
+ if (m_name.empty())
+ return nullptr;
+ return m_name.c_str();
+}
+
+ConstString Platform::GetFullNameForDylib(ConstString basename) {
+ return basename;
+}
+
+bool Platform::SetRemoteWorkingDirectory(const FileSpec &working_dir) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("Platform::SetRemoteWorkingDirectory('%s')",
+ working_dir.GetCString());
+ m_working_dir = working_dir;
+ return true;
+}
+
+bool Platform::SetOSVersion(llvm::VersionTuple version) {
+ if (IsHost()) {
+ // We don't need anyone setting the OS version for the host platform, we
+ // should be able to figure it out by calling HostInfo::GetOSVersion(...).
+ return false;
+ } else {
+ // We have a remote platform, allow setting the target OS version if we
+ // aren't connected, since if we are connected, we should be able to
+ // request the remote OS version from the connected platform.
+ if (IsConnected())
+ return false;
+ else {
+ // We aren't connected and we might want to set the OS version ahead of
+ // time before we connect so we can peruse files and use a local SDK or
+ // PDK cache of support files to disassemble or do other things.
+ m_os_version = version;
+ return true;
+ }
+ }
+ return false;
+}
+
+Status
+Platform::ResolveExecutable(const ModuleSpec &module_spec,
+ lldb::ModuleSP &exe_module_sp,
+ const FileSpecList *module_search_paths_ptr) {
+ Status error;
+ if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
+ if (module_spec.GetArchitecture().IsValid()) {
+ error = ModuleList::GetSharedModule(module_spec, exe_module_sp,
+ module_search_paths_ptr, nullptr,
+ nullptr);
+ } else {
+ // No valid architecture was specified, ask the platform for the
+ // architectures that we should be using (in the correct order) and see
+ // if we can find a match that way
+ ModuleSpec arch_module_spec(module_spec);
+ for (uint32_t idx = 0; GetSupportedArchitectureAtIndex(
+ idx, arch_module_spec.GetArchitecture());
+ ++idx) {
+ error = ModuleList::GetSharedModule(arch_module_spec, exe_module_sp,
+ module_search_paths_ptr, nullptr,
+ nullptr);
+ // Did we find an executable using one of the
+ if (error.Success() && exe_module_sp)
+ break;
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat("'%s' does not exist",
+ module_spec.GetFileSpec().GetPath().c_str());
+ }
+ return error;
+}
+
+Status Platform::ResolveSymbolFile(Target &target, const ModuleSpec &sym_spec,
+ FileSpec &sym_file) {
+ Status error;
+ if (FileSystem::Instance().Exists(sym_spec.GetSymbolFileSpec()))
+ sym_file = sym_spec.GetSymbolFileSpec();
+ else
+ error.SetErrorString("unable to resolve symbol file");
+ return error;
+}
+
+bool Platform::ResolveRemotePath(const FileSpec &platform_path,
+ FileSpec &resolved_platform_path) {
+ resolved_platform_path = platform_path;
+ FileSystem::Instance().Resolve(resolved_platform_path);
+ return true;
+}
+
+const ArchSpec &Platform::GetSystemArchitecture() {
+ if (IsHost()) {
+ if (!m_system_arch.IsValid()) {
+ // We have a local host platform
+ m_system_arch = HostInfo::GetArchitecture();
+ m_system_arch_set_while_connected = m_system_arch.IsValid();
+ }
+ } else {
+ // We have a remote platform. We can only fetch the remote system
+ // architecture if we are connected, and we don't want to do it more than
+ // once.
+
+ const bool is_connected = IsConnected();
+
+ bool fetch = false;
+ if (m_system_arch.IsValid()) {
+ // We have valid OS version info, check to make sure it wasn't manually
+ // set prior to connecting. If it was manually set prior to connecting,
+ // then lets fetch the actual OS version info if we are now connected.
+ if (is_connected && !m_system_arch_set_while_connected)
+ fetch = true;
+ } else {
+ // We don't have valid OS version info, fetch it if we are connected
+ fetch = is_connected;
+ }
+
+ if (fetch) {
+ m_system_arch = GetRemoteSystemArchitecture();
+ m_system_arch_set_while_connected = m_system_arch.IsValid();
+ }
+ }
+ return m_system_arch;
+}
+
+ArchSpec Platform::GetAugmentedArchSpec(llvm::StringRef triple) {
+ if (triple.empty())
+ return ArchSpec();
+ llvm::Triple normalized_triple(llvm::Triple::normalize(triple));
+ if (!ArchSpec::ContainsOnlyArch(normalized_triple))
+ return ArchSpec(triple);
+
+ if (auto kind = HostInfo::ParseArchitectureKind(triple))
+ return HostInfo::GetArchitecture(*kind);
+
+ ArchSpec compatible_arch;
+ ArchSpec raw_arch(triple);
+ if (!IsCompatibleArchitecture(raw_arch, false, &compatible_arch))
+ return raw_arch;
+
+ if (!compatible_arch.IsValid())
+ return ArchSpec(normalized_triple);
+
+ const llvm::Triple &compatible_triple = compatible_arch.GetTriple();
+ if (normalized_triple.getVendorName().empty())
+ normalized_triple.setVendor(compatible_triple.getVendor());
+ if (normalized_triple.getOSName().empty())
+ normalized_triple.setOS(compatible_triple.getOS());
+ if (normalized_triple.getEnvironmentName().empty())
+ normalized_triple.setEnvironment(compatible_triple.getEnvironment());
+ return ArchSpec(normalized_triple);
+}
+
+Status Platform::ConnectRemote(Args &args) {
+ Status error;
+ if (IsHost())
+ error.SetErrorStringWithFormat("The currently selected platform (%s) is "
+ "the host platform and is always connected.",
+ GetPluginName().GetCString());
+ else
+ error.SetErrorStringWithFormat(
+ "Platform::ConnectRemote() is not supported by %s",
+ GetPluginName().GetCString());
+ return error;
+}
+
+Status Platform::DisconnectRemote() {
+ Status error;
+ if (IsHost())
+ error.SetErrorStringWithFormat("The currently selected platform (%s) is "
+ "the host platform and is always connected.",
+ GetPluginName().GetCString());
+ else
+ error.SetErrorStringWithFormat(
+ "Platform::DisconnectRemote() is not supported by %s",
+ GetPluginName().GetCString());
+ return error;
+}
+
+bool Platform::GetProcessInfo(lldb::pid_t pid,
+ ProcessInstanceInfo &process_info) {
+ // Take care of the host case so that each subclass can just call this
+ // function to get the host functionality.
+ if (IsHost())
+ return Host::GetProcessInfo(pid, process_info);
+ return false;
+}
+
+uint32_t Platform::FindProcesses(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ // Take care of the host case so that each subclass can just call this
+ // function to get the host functionality.
+ uint32_t match_count = 0;
+ if (IsHost())
+ match_count = Host::FindProcesses(match_info, process_infos);
+ return match_count;
+}
+
+Status Platform::LaunchProcess(ProcessLaunchInfo &launch_info) {
+ Status error;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ if (log)
+ log->Printf("Platform::%s()", __FUNCTION__);
+
+ // Take care of the host case so that each subclass can just call this
+ // function to get the host functionality.
+ if (IsHost()) {
+ if (::getenv("LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY"))
+ launch_info.GetFlags().Set(eLaunchFlagLaunchInTTY);
+
+ if (launch_info.GetFlags().Test(eLaunchFlagLaunchInShell)) {
+ const bool is_localhost = true;
+ const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug);
+ const bool first_arg_is_full_shell_command = false;
+ uint32_t num_resumes = GetResumeCountForLaunchInfo(launch_info);
+ if (log) {
+ const FileSpec &shell = launch_info.GetShell();
+ std::string shell_str = (shell) ? shell.GetPath() : "<null>";
+ log->Printf(
+ "Platform::%s GetResumeCountForLaunchInfo() returned %" PRIu32
+ ", shell is '%s'",
+ __FUNCTION__, num_resumes, shell_str.c_str());
+ }
+
+ if (!launch_info.ConvertArgumentsForLaunchingInShell(
+ error, is_localhost, will_debug, first_arg_is_full_shell_command,
+ num_resumes))
+ return error;
+ } else if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) {
+ error = ShellExpandArguments(launch_info);
+ if (error.Fail()) {
+ error.SetErrorStringWithFormat("shell expansion failed (reason: %s). "
+ "consider launching with 'process "
+ "launch'.",
+ error.AsCString("unknown"));
+ return error;
+ }
+ }
+
+ if (log)
+ log->Printf("Platform::%s final launch_info resume count: %" PRIu32,
+ __FUNCTION__, launch_info.GetResumeCount());
+
+ error = Host::LaunchProcess(launch_info);
+ } else
+ error.SetErrorString(
+ "base lldb_private::Platform class can't launch remote processes");
+ return error;
+}
+
+Status Platform::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
+ if (IsHost())
+ return Host::ShellExpandArguments(launch_info);
+ return Status("base lldb_private::Platform class can't expand arguments");
+}
+
+Status Platform::KillProcess(const lldb::pid_t pid) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ if (log)
+ log->Printf("Platform::%s, pid %" PRIu64, __FUNCTION__, pid);
+
+ // Try to find a process plugin to handle this Kill request. If we can't,
+ // fall back to the default OS implementation.
+ size_t num_debuggers = Debugger::GetNumDebuggers();
+ for (size_t didx = 0; didx < num_debuggers; ++didx) {
+ DebuggerSP debugger = Debugger::GetDebuggerAtIndex(didx);
+ lldb_private::TargetList &targets = debugger->GetTargetList();
+ for (int tidx = 0; tidx < targets.GetNumTargets(); ++tidx) {
+ ProcessSP process = targets.GetTargetAtIndex(tidx)->GetProcessSP();
+ if (process->GetID() == pid)
+ return process->Destroy(true);
+ }
+ }
+
+ if (!IsHost()) {
+ return Status(
+ "base lldb_private::Platform class can't kill remote processes unless "
+ "they are controlled by a process plugin");
+ }
+ Host::Kill(pid, SIGTERM);
+ return Status();
+}
+
+lldb::ProcessSP
+Platform::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger,
+ Target *target, // Can be nullptr, if nullptr create a
+ // new target, else use existing one
+ Status &error) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ if (log)
+ log->Printf("Platform::%s entered (target %p)", __FUNCTION__,
+ static_cast<void *>(target));
+
+ ProcessSP process_sp;
+ // Make sure we stop at the entry point
+ launch_info.GetFlags().Set(eLaunchFlagDebug);
+ // We always launch the process we are going to debug in a separate process
+ // group, since then we can handle ^C interrupts ourselves w/o having to
+ // worry about the target getting them as well.
+ launch_info.SetLaunchInSeparateProcessGroup(true);
+
+ // Allow any StructuredData process-bound plugins to adjust the launch info
+ // if needed
+ size_t i = 0;
+ bool iteration_complete = false;
+ // Note iteration can't simply go until a nullptr callback is returned, as it
+ // is valid for a plugin to not supply a filter.
+ auto get_filter_func = PluginManager::GetStructuredDataFilterCallbackAtIndex;
+ for (auto filter_callback = get_filter_func(i, iteration_complete);
+ !iteration_complete;
+ filter_callback = get_filter_func(++i, iteration_complete)) {
+ if (filter_callback) {
+ // Give this ProcessLaunchInfo filter a chance to adjust the launch info.
+ error = (*filter_callback)(launch_info, target);
+ if (!error.Success()) {
+ if (log)
+ log->Printf("Platform::%s() StructuredDataPlugin launch "
+ "filter failed.",
+ __FUNCTION__);
+ return process_sp;
+ }
+ }
+ }
+
+ error = LaunchProcess(launch_info);
+ if (error.Success()) {
+ if (log)
+ log->Printf("Platform::%s LaunchProcess() call succeeded (pid=%" PRIu64
+ ")",
+ __FUNCTION__, launch_info.GetProcessID());
+ if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) {
+ ProcessAttachInfo attach_info(launch_info);
+ process_sp = Attach(attach_info, debugger, target, error);
+ if (process_sp) {
+ if (log)
+ log->Printf("Platform::%s Attach() succeeded, Process plugin: %s",
+ __FUNCTION__, process_sp->GetPluginName().AsCString());
+ launch_info.SetHijackListener(attach_info.GetHijackListener());
+
+ // Since we attached to the process, it will think it needs to detach
+ // if the process object just goes away without an explicit call to
+ // Process::Kill() or Process::Detach(), so let it know to kill the
+ // process if this happens.
+ process_sp->SetShouldDetach(false);
+
+ // If we didn't have any file actions, the pseudo terminal might have
+ // been used where the slave side was given as the file to open for
+ // stdin/out/err after we have already opened the master so we can
+ // read/write stdin/out/err.
+ int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
+ if (pty_fd != PseudoTerminal::invalid_fd) {
+ process_sp->SetSTDIOFileDescriptor(pty_fd);
+ }
+ } else {
+ if (log)
+ log->Printf("Platform::%s Attach() failed: %s", __FUNCTION__,
+ error.AsCString());
+ }
+ } else {
+ if (log)
+ log->Printf("Platform::%s LaunchProcess() returned launch_info with "
+ "invalid process id",
+ __FUNCTION__);
+ }
+ } else {
+ if (log)
+ log->Printf("Platform::%s LaunchProcess() failed: %s", __FUNCTION__,
+ error.AsCString());
+ }
+
+ return process_sp;
+}
+
+lldb::PlatformSP
+Platform::GetPlatformForArchitecture(const ArchSpec &arch,
+ ArchSpec *platform_arch_ptr) {
+ lldb::PlatformSP platform_sp;
+ Status error;
+ if (arch.IsValid())
+ platform_sp = Platform::Create(arch, platform_arch_ptr, error);
+ return platform_sp;
+}
+
+/// Lets a platform answer if it is compatible with a given
+/// architecture and the target triple contained within.
+bool Platform::IsCompatibleArchitecture(const ArchSpec &arch,
+ bool exact_arch_match,
+ ArchSpec *compatible_arch_ptr) {
+ // If the architecture is invalid, we must answer true...
+ if (arch.IsValid()) {
+ ArchSpec platform_arch;
+ // Try for an exact architecture match first.
+ if (exact_arch_match) {
+ for (uint32_t arch_idx = 0;
+ GetSupportedArchitectureAtIndex(arch_idx, platform_arch);
+ ++arch_idx) {
+ if (arch.IsExactMatch(platform_arch)) {
+ if (compatible_arch_ptr)
+ *compatible_arch_ptr = platform_arch;
+ return true;
+ }
+ }
+ } else {
+ for (uint32_t arch_idx = 0;
+ GetSupportedArchitectureAtIndex(arch_idx, platform_arch);
+ ++arch_idx) {
+ if (arch.IsCompatibleMatch(platform_arch)) {
+ if (compatible_arch_ptr)
+ *compatible_arch_ptr = platform_arch;
+ return true;
+ }
+ }
+ }
+ }
+ if (compatible_arch_ptr)
+ compatible_arch_ptr->Clear();
+ return false;
+}
+
+Status Platform::PutFile(const FileSpec &source, const FileSpec &destination,
+ uint32_t uid, uint32_t gid) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ if (log)
+ log->Printf("[PutFile] Using block by block transfer....\n");
+
+ uint32_t source_open_options =
+ File::eOpenOptionRead | File::eOpenOptionCloseOnExec;
+ namespace fs = llvm::sys::fs;
+ if (fs::is_symlink_file(source.GetPath()))
+ source_open_options |= File::eOpenOptionDontFollowSymlinks;
+
+ File source_file;
+ Status error = FileSystem::Instance().Open(
+ source_file, source, source_open_options, lldb::eFilePermissionsUserRW);
+ uint32_t permissions = source_file.GetPermissions(error);
+ if (permissions == 0)
+ permissions = lldb::eFilePermissionsFileDefault;
+
+ if (!source_file.IsValid())
+ return Status("PutFile: unable to open source file");
+ lldb::user_id_t dest_file = OpenFile(
+ destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite |
+ File::eOpenOptionTruncate | File::eOpenOptionCloseOnExec,
+ permissions, error);
+ if (log)
+ log->Printf("dest_file = %" PRIu64 "\n", dest_file);
+
+ if (error.Fail())
+ return error;
+ if (dest_file == UINT64_MAX)
+ return Status("unable to open target file");
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024 * 16, 0));
+ uint64_t offset = 0;
+ for (;;) {
+ size_t bytes_read = buffer_sp->GetByteSize();
+ error = source_file.Read(buffer_sp->GetBytes(), bytes_read);
+ if (error.Fail() || bytes_read == 0)
+ break;
+
+ const uint64_t bytes_written =
+ WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error);
+ if (error.Fail())
+ break;
+
+ offset += bytes_written;
+ if (bytes_written != bytes_read) {
+ // We didn't write the correct number of bytes, so adjust the file
+ // position in the source file we are reading from...
+ source_file.SeekFromStart(offset);
+ }
+ }
+ CloseFile(dest_file, error);
+
+ if (uid == UINT32_MAX && gid == UINT32_MAX)
+ return error;
+
+ // TODO: ChownFile?
+
+ return error;
+}
+
+Status Platform::GetFile(const FileSpec &source, const FileSpec &destination) {
+ Status error("unimplemented");
+ return error;
+}
+
+Status
+Platform::CreateSymlink(const FileSpec &src, // The name of the link is in src
+ const FileSpec &dst) // The symlink points to dst
+{
+ Status error("unimplemented");
+ return error;
+}
+
+bool Platform::GetFileExists(const lldb_private::FileSpec &file_spec) {
+ return false;
+}
+
+Status Platform::Unlink(const FileSpec &path) {
+ Status error("unimplemented");
+ return error;
+}
+
+MmapArgList Platform::GetMmapArgumentList(const ArchSpec &arch, addr_t addr,
+ addr_t length, unsigned prot,
+ unsigned flags, addr_t fd,
+ addr_t offset) {
+ uint64_t flags_platform = 0;
+ if (flags & eMmapFlagsPrivate)
+ flags_platform |= MAP_PRIVATE;
+ if (flags & eMmapFlagsAnon)
+ flags_platform |= MAP_ANON;
+
+ MmapArgList args({addr, length, prot, flags_platform, fd, offset});
+ return args;
+}
+
+lldb_private::Status Platform::RunShellCommand(
+ const char *command, // Shouldn't be nullptr
+ const FileSpec &
+ working_dir, // Pass empty FileSpec to use the current working directory
+ int *status_ptr, // Pass nullptr if you don't want the process exit status
+ int *signo_ptr, // Pass nullptr if you don't want the signal that caused the
+ // process to exit
+ std::string
+ *command_output, // Pass nullptr if you don't want the command output
+ const Timeout<std::micro> &timeout) {
+ if (IsHost())
+ return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr,
+ command_output, timeout);
+ else
+ return Status("unimplemented");
+}
+
+bool Platform::CalculateMD5(const FileSpec &file_spec, uint64_t &low,
+ uint64_t &high) {
+ if (!IsHost())
+ return false;
+ auto Result = llvm::sys::fs::md5_contents(file_spec.GetPath());
+ if (!Result)
+ return false;
+ std::tie(high, low) = Result->words();
+ return true;
+}
+
+void Platform::SetLocalCacheDirectory(const char *local) {
+ m_local_cache_directory.assign(local);
+}
+
+const char *Platform::GetLocalCacheDirectory() {
+ return m_local_cache_directory.c_str();
+}
+
+static constexpr OptionDefinition g_rsync_option_table[] = {
+ {LLDB_OPT_SET_ALL, false, "rsync", 'r', OptionParser::eNoArgument, nullptr,
+ {}, 0, eArgTypeNone, "Enable rsync."},
+ {LLDB_OPT_SET_ALL, false, "rsync-opts", 'R',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommandName,
+ "Platform-specific options required for rsync to work."},
+ {LLDB_OPT_SET_ALL, false, "rsync-prefix", 'P',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommandName,
+ "Platform-specific rsync prefix put before the remote path."},
+ {LLDB_OPT_SET_ALL, false, "ignore-remote-hostname", 'i',
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
+ "Do not automatically fill in the remote hostname when composing the "
+ "rsync command."},
+};
+
+static constexpr OptionDefinition g_ssh_option_table[] = {
+ {LLDB_OPT_SET_ALL, false, "ssh", 's', OptionParser::eNoArgument, nullptr,
+ {}, 0, eArgTypeNone, "Enable SSH."},
+ {LLDB_OPT_SET_ALL, false, "ssh-opts", 'S', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeCommandName,
+ "Platform-specific options required for SSH to work."},
+};
+
+static constexpr OptionDefinition g_caching_option_table[] = {
+ {LLDB_OPT_SET_ALL, false, "local-cache-dir", 'c',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePath,
+ "Path in which to store local copies of files."},
+};
+
+llvm::ArrayRef<OptionDefinition> OptionGroupPlatformRSync::GetDefinitions() {
+ return llvm::makeArrayRef(g_rsync_option_table);
+}
+
+void OptionGroupPlatformRSync::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_rsync = false;
+ m_rsync_opts.clear();
+ m_rsync_prefix.clear();
+ m_ignores_remote_hostname = false;
+}
+
+lldb_private::Status
+OptionGroupPlatformRSync::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ char short_option = (char)GetDefinitions()[option_idx].short_option;
+ switch (short_option) {
+ case 'r':
+ m_rsync = true;
+ break;
+
+ case 'R':
+ m_rsync_opts.assign(option_arg);
+ break;
+
+ case 'P':
+ m_rsync_prefix.assign(option_arg);
+ break;
+
+ case 'i':
+ m_ignores_remote_hostname = true;
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+
+ return error;
+}
+
+lldb::BreakpointSP
+Platform::SetThreadCreationBreakpoint(lldb_private::Target &target) {
+ return lldb::BreakpointSP();
+}
+
+llvm::ArrayRef<OptionDefinition> OptionGroupPlatformSSH::GetDefinitions() {
+ return llvm::makeArrayRef(g_ssh_option_table);
+}
+
+void OptionGroupPlatformSSH::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_ssh = false;
+ m_ssh_opts.clear();
+}
+
+lldb_private::Status
+OptionGroupPlatformSSH::SetOptionValue(uint32_t option_idx,
+ llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ char short_option = (char)GetDefinitions()[option_idx].short_option;
+ switch (short_option) {
+ case 's':
+ m_ssh = true;
+ break;
+
+ case 'S':
+ m_ssh_opts.assign(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+
+ return error;
+}
+
+llvm::ArrayRef<OptionDefinition> OptionGroupPlatformCaching::GetDefinitions() {
+ return llvm::makeArrayRef(g_caching_option_table);
+}
+
+void OptionGroupPlatformCaching::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ m_cache_dir.clear();
+}
+
+lldb_private::Status OptionGroupPlatformCaching::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ char short_option = (char)GetDefinitions()[option_idx].short_option;
+ switch (short_option) {
+ case 'c':
+ m_cache_dir.assign(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+
+ return error;
+}
+
+Environment Platform::GetEnvironment() { return Environment(); }
+
+const std::vector<ConstString> &Platform::GetTrapHandlerSymbolNames() {
+ if (!m_calculated_trap_handlers) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ if (!m_calculated_trap_handlers) {
+ CalculateTrapHandlerSymbolNames();
+ m_calculated_trap_handlers = true;
+ }
+ }
+ return m_trap_handlers;
+}
+
+Status Platform::GetCachedExecutable(
+ ModuleSpec &module_spec, lldb::ModuleSP &module_sp,
+ const FileSpecList *module_search_paths_ptr, Platform &remote_platform) {
+ const auto platform_spec = module_spec.GetFileSpec();
+ const auto error = LoadCachedExecutable(
+ module_spec, module_sp, module_search_paths_ptr, remote_platform);
+ if (error.Success()) {
+ module_spec.GetFileSpec() = module_sp->GetFileSpec();
+ module_spec.GetPlatformFileSpec() = platform_spec;
+ }
+
+ return error;
+}
+
+Status Platform::LoadCachedExecutable(
+ const ModuleSpec &module_spec, lldb::ModuleSP &module_sp,
+ const FileSpecList *module_search_paths_ptr, Platform &remote_platform) {
+ return GetRemoteSharedModule(module_spec, nullptr, module_sp,
+ [&](const ModuleSpec &spec) {
+ return remote_platform.ResolveExecutable(
+ spec, module_sp, module_search_paths_ptr);
+ },
+ nullptr);
+}
+
+Status Platform::GetRemoteSharedModule(const ModuleSpec &module_spec,
+ Process *process,
+ lldb::ModuleSP &module_sp,
+ const ModuleResolver &module_resolver,
+ bool *did_create_ptr) {
+ // Get module information from a target.
+ ModuleSpec resolved_module_spec;
+ bool got_module_spec = false;
+ if (process) {
+ // Try to get module information from the process
+ if (process->GetModuleSpec(module_spec.GetFileSpec(),
+ module_spec.GetArchitecture(),
+ resolved_module_spec)) {
+ if (!module_spec.GetUUID().IsValid() ||
+ module_spec.GetUUID() == resolved_module_spec.GetUUID()) {
+ got_module_spec = true;
+ }
+ }
+ }
+
+ if (!module_spec.GetArchitecture().IsValid()) {
+ Status error;
+ // No valid architecture was specified, ask the platform for the
+ // architectures that we should be using (in the correct order) and see if
+ // we can find a match that way
+ ModuleSpec arch_module_spec(module_spec);
+ for (uint32_t idx = 0; GetSupportedArchitectureAtIndex(
+ idx, arch_module_spec.GetArchitecture());
+ ++idx) {
+ error = ModuleList::GetSharedModule(arch_module_spec, module_sp, nullptr,
+ nullptr, nullptr);
+ // Did we find an executable using one of the
+ if (error.Success() && module_sp)
+ break;
+ }
+ if (module_sp)
+ got_module_spec = true;
+ }
+
+ if (!got_module_spec) {
+ // Get module information from a target.
+ if (!GetModuleSpec(module_spec.GetFileSpec(), module_spec.GetArchitecture(),
+ resolved_module_spec)) {
+ if (!module_spec.GetUUID().IsValid() ||
+ module_spec.GetUUID() == resolved_module_spec.GetUUID()) {
+ return module_resolver(module_spec);
+ }
+ }
+ }
+
+ // If we are looking for a specific UUID, make sure resolved_module_spec has
+ // the same one before we search.
+ if (module_spec.GetUUID().IsValid()) {
+ resolved_module_spec.GetUUID() = module_spec.GetUUID();
+ }
+
+ // Trying to find a module by UUID on local file system.
+ const auto error = module_resolver(resolved_module_spec);
+ if (error.Fail()) {
+ if (GetCachedSharedModule(resolved_module_spec, module_sp, did_create_ptr))
+ return Status();
+ }
+
+ return error;
+}
+
+bool Platform::GetCachedSharedModule(const ModuleSpec &module_spec,
+ lldb::ModuleSP &module_sp,
+ bool *did_create_ptr) {
+ if (IsHost() || !GetGlobalPlatformProperties()->GetUseModuleCache() ||
+ !GetGlobalPlatformProperties()->GetModuleCacheDirectory())
+ return false;
+
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+
+ // Check local cache for a module.
+ auto error = m_module_cache->GetAndPut(
+ GetModuleCacheRoot(), GetCacheHostname(), module_spec,
+ [this](const ModuleSpec &module_spec,
+ const FileSpec &tmp_download_file_spec) {
+ return DownloadModuleSlice(
+ module_spec.GetFileSpec(), module_spec.GetObjectOffset(),
+ module_spec.GetObjectSize(), tmp_download_file_spec);
+
+ },
+ [this](const ModuleSP &module_sp,
+ const FileSpec &tmp_download_file_spec) {
+ return DownloadSymbolFile(module_sp, tmp_download_file_spec);
+ },
+ module_sp, did_create_ptr);
+ if (error.Success())
+ return true;
+
+ if (log)
+ log->Printf("Platform::%s - module %s not found in local cache: %s",
+ __FUNCTION__, module_spec.GetUUID().GetAsString().c_str(),
+ error.AsCString());
+ return false;
+}
+
+Status Platform::DownloadModuleSlice(const FileSpec &src_file_spec,
+ const uint64_t src_offset,
+ const uint64_t src_size,
+ const FileSpec &dst_file_spec) {
+ Status error;
+
+ std::error_code EC;
+ llvm::raw_fd_ostream dst(dst_file_spec.GetPath(), EC, llvm::sys::fs::F_None);
+ if (EC) {
+ error.SetErrorStringWithFormat("unable to open destination file: %s",
+ dst_file_spec.GetPath().c_str());
+ return error;
+ }
+
+ auto src_fd = OpenFile(src_file_spec, File::eOpenOptionRead,
+ lldb::eFilePermissionsFileDefault, error);
+
+ if (error.Fail()) {
+ error.SetErrorStringWithFormat("unable to open source file: %s",
+ error.AsCString());
+ return error;
+ }
+
+ std::vector<char> buffer(1024);
+ auto offset = src_offset;
+ uint64_t total_bytes_read = 0;
+ while (total_bytes_read < src_size) {
+ const auto to_read = std::min(static_cast<uint64_t>(buffer.size()),
+ src_size - total_bytes_read);
+ const uint64_t n_read =
+ ReadFile(src_fd, offset, &buffer[0], to_read, error);
+ if (error.Fail())
+ break;
+ if (n_read == 0) {
+ error.SetErrorString("read 0 bytes");
+ break;
+ }
+ offset += n_read;
+ total_bytes_read += n_read;
+ dst.write(&buffer[0], n_read);
+ }
+
+ Status close_error;
+ CloseFile(src_fd, close_error); // Ignoring close error.
+
+ return error;
+}
+
+Status Platform::DownloadSymbolFile(const lldb::ModuleSP &module_sp,
+ const FileSpec &dst_file_spec) {
+ return Status(
+ "Symbol file downloading not supported by the default platform.");
+}
+
+FileSpec Platform::GetModuleCacheRoot() {
+ auto dir_spec = GetGlobalPlatformProperties()->GetModuleCacheDirectory();
+ dir_spec.AppendPathComponent(GetName().AsCString());
+ return dir_spec;
+}
+
+const char *Platform::GetCacheHostname() { return GetHostname(); }
+
+const UnixSignalsSP &Platform::GetRemoteUnixSignals() {
+ static const auto s_default_unix_signals_sp = std::make_shared<UnixSignals>();
+ return s_default_unix_signals_sp;
+}
+
+UnixSignalsSP Platform::GetUnixSignals() {
+ if (IsHost())
+ return UnixSignals::CreateForHost();
+ return GetRemoteUnixSignals();
+}
+
+uint32_t Platform::LoadImage(lldb_private::Process *process,
+ const lldb_private::FileSpec &local_file,
+ const lldb_private::FileSpec &remote_file,
+ lldb_private::Status &error) {
+ if (local_file && remote_file) {
+ // Both local and remote file was specified. Install the local file to the
+ // given location.
+ if (IsRemote() || local_file != remote_file) {
+ error = Install(local_file, remote_file);
+ if (error.Fail())
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+ return DoLoadImage(process, remote_file, nullptr, error);
+ }
+
+ if (local_file) {
+ // Only local file was specified. Install it to the current working
+ // directory.
+ FileSpec target_file = GetWorkingDirectory();
+ target_file.AppendPathComponent(local_file.GetFilename().AsCString());
+ if (IsRemote() || local_file != target_file) {
+ error = Install(local_file, target_file);
+ if (error.Fail())
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+ return DoLoadImage(process, target_file, nullptr, error);
+ }
+
+ if (remote_file) {
+ // Only remote file was specified so we don't have to do any copying
+ return DoLoadImage(process, remote_file, nullptr, error);
+ }
+
+ error.SetErrorString("Neither local nor remote file was specified");
+ return LLDB_INVALID_IMAGE_TOKEN;
+}
+
+uint32_t Platform::DoLoadImage(lldb_private::Process *process,
+ const lldb_private::FileSpec &remote_file,
+ const std::vector<std::string> *paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_image) {
+ error.SetErrorString("LoadImage is not supported on the current platform");
+ return LLDB_INVALID_IMAGE_TOKEN;
+}
+
+uint32_t Platform::LoadImageUsingPaths(lldb_private::Process *process,
+ const lldb_private::FileSpec &remote_filename,
+ const std::vector<std::string> &paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_path)
+{
+ FileSpec file_to_use;
+ if (remote_filename.IsAbsolute())
+ file_to_use = FileSpec(remote_filename.GetFilename().GetStringRef(),
+
+ remote_filename.GetPathStyle());
+ else
+ file_to_use = remote_filename;
+
+ return DoLoadImage(process, file_to_use, &paths, error, loaded_path);
+}
+
+Status Platform::UnloadImage(lldb_private::Process *process,
+ uint32_t image_token) {
+ return Status("UnloadImage is not supported on the current platform");
+}
+
+lldb::ProcessSP Platform::ConnectProcess(llvm::StringRef connect_url,
+ llvm::StringRef plugin_name,
+ lldb_private::Debugger &debugger,
+ lldb_private::Target *target,
+ lldb_private::Status &error) {
+ error.Clear();
+
+ if (!target) {
+ ArchSpec arch;
+ if (target && target->GetArchitecture().IsValid())
+ arch = target->GetArchitecture();
+ else
+ arch = Target::GetDefaultArchitecture();
+
+ const char *triple = "";
+ if (arch.IsValid())
+ triple = arch.GetTriple().getTriple().c_str();
+
+ TargetSP new_target_sp;
+ error = debugger.GetTargetList().CreateTarget(
+ debugger, "", triple, eLoadDependentsNo, nullptr, new_target_sp);
+ target = new_target_sp.get();
+ }
+
+ if (!target || error.Fail())
+ return nullptr;
+
+ debugger.GetTargetList().SetSelectedTarget(target);
+
+ lldb::ProcessSP process_sp =
+ target->CreateProcess(debugger.GetListener(), plugin_name, nullptr);
+ if (!process_sp)
+ return nullptr;
+
+ error =
+ process_sp->ConnectRemote(debugger.GetOutputFile().get(), connect_url);
+ if (error.Fail())
+ return nullptr;
+
+ return process_sp;
+}
+
+size_t Platform::ConnectToWaitingProcesses(lldb_private::Debugger &debugger,
+ lldb_private::Status &error) {
+ error.Clear();
+ return 0;
+}
+
+size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target,
+ BreakpointSite *bp_site) {
+ ArchSpec arch = target.GetArchitecture();
+ const uint8_t *trap_opcode = nullptr;
+ size_t trap_opcode_size = 0;
+
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64: {
+ static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xd4};
+ trap_opcode = g_aarch64_opcode;
+ trap_opcode_size = sizeof(g_aarch64_opcode);
+ } break;
+
+ // TODO: support big-endian arm and thumb trap codes.
+ case llvm::Triple::arm: {
+ // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the
+ // linux kernel does otherwise.
+ static const uint8_t g_arm_breakpoint_opcode[] = {0xf0, 0x01, 0xf0, 0xe7};
+ static const uint8_t g_thumb_breakpoint_opcode[] = {0x01, 0xde};
+
+ lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0));
+ AddressClass addr_class = AddressClass::eUnknown;
+
+ if (bp_loc_sp) {
+ addr_class = bp_loc_sp->GetAddress().GetAddressClass();
+ if (addr_class == AddressClass::eUnknown &&
+ (bp_loc_sp->GetAddress().GetFileAddress() & 1))
+ addr_class = AddressClass::eCodeAlternateISA;
+ }
+
+ if (addr_class == AddressClass::eCodeAlternateISA) {
+ trap_opcode = g_thumb_breakpoint_opcode;
+ trap_opcode_size = sizeof(g_thumb_breakpoint_opcode);
+ } else {
+ trap_opcode = g_arm_breakpoint_opcode;
+ trap_opcode_size = sizeof(g_arm_breakpoint_opcode);
+ }
+ } break;
+
+ case llvm::Triple::mips:
+ case llvm::Triple::mips64: {
+ static const uint8_t g_hex_opcode[] = {0x00, 0x00, 0x00, 0x0d};
+ trap_opcode = g_hex_opcode;
+ trap_opcode_size = sizeof(g_hex_opcode);
+ } break;
+
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64el: {
+ static const uint8_t g_hex_opcode[] = {0x0d, 0x00, 0x00, 0x00};
+ trap_opcode = g_hex_opcode;
+ trap_opcode_size = sizeof(g_hex_opcode);
+ } break;
+
+ case llvm::Triple::systemz: {
+ static const uint8_t g_hex_opcode[] = {0x00, 0x01};
+ trap_opcode = g_hex_opcode;
+ trap_opcode_size = sizeof(g_hex_opcode);
+ } break;
+
+ case llvm::Triple::hexagon: {
+ static const uint8_t g_hex_opcode[] = {0x0c, 0xdb, 0x00, 0x54};
+ trap_opcode = g_hex_opcode;
+ trap_opcode_size = sizeof(g_hex_opcode);
+ } break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64: {
+ static const uint8_t g_ppc_opcode[] = {0x7f, 0xe0, 0x00, 0x08};
+ trap_opcode = g_ppc_opcode;
+ trap_opcode_size = sizeof(g_ppc_opcode);
+ } break;
+
+ case llvm::Triple::ppc64le: {
+ static const uint8_t g_ppc64le_opcode[] = {0x08, 0x00, 0xe0, 0x7f}; // trap
+ trap_opcode = g_ppc64le_opcode;
+ trap_opcode_size = sizeof(g_ppc64le_opcode);
+ } break;
+
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64: {
+ static const uint8_t g_i386_opcode[] = {0xCC};
+ trap_opcode = g_i386_opcode;
+ trap_opcode_size = sizeof(g_i386_opcode);
+ } break;
+
+ default:
+ llvm_unreachable(
+ "Unhandled architecture in Platform::GetSoftwareBreakpointTrapOpcode");
+ }
+
+ assert(bp_site);
+ if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size))
+ return trap_opcode_size;
+
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/Process.cpp b/contrib/llvm-project/lldb/source/Target/Process.cpp
new file mode 100644
index 000000000000..6c634dba00c7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/Process.cpp
@@ -0,0 +1,6048 @@
+//===-- Process.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/Threading.h"
+
+#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/DynamicCheckerFunctions.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Host/Terminal.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/InstrumentationRuntime.h"
+#include "lldb/Target/JITLoader.h"
+#include "lldb/Target/JITLoaderList.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/MemoryHistory.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/OperatingSystem.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/StructuredDataPlugin.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanBase.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/NameMatches.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/SelectHelper.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace std::chrono;
+
+// Comment out line below to disable memory caching, overriding the process
+// setting target.process.disable-memory-cache
+#define ENABLE_MEMORY_CACHING
+
+#ifdef ENABLE_MEMORY_CACHING
+#define DISABLE_MEM_CACHE_DEFAULT false
+#else
+#define DISABLE_MEM_CACHE_DEFAULT true
+#endif
+
+class ProcessOptionValueProperties : public OptionValueProperties {
+public:
+ ProcessOptionValueProperties(ConstString name)
+ : OptionValueProperties(name) {}
+
+ // This constructor is used when creating ProcessOptionValueProperties when
+ // it is part of a new lldb_private::Process instance. It will copy all
+ // current global property values as needed
+ ProcessOptionValueProperties(ProcessProperties *global_properties)
+ : OptionValueProperties(*global_properties->GetValueProperties()) {}
+
+ const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx,
+ bool will_modify,
+ uint32_t idx) const override {
+ // When getting the value for a key from the process options, we will
+ // always try and grab the setting from the current process if there is
+ // one. Else we just use the one from this instance.
+ if (exe_ctx) {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process) {
+ ProcessOptionValueProperties *instance_properties =
+ static_cast<ProcessOptionValueProperties *>(
+ process->GetValueProperties().get());
+ if (this != instance_properties)
+ return instance_properties->ProtectedGetPropertyAtIndex(idx);
+ }
+ }
+ return ProtectedGetPropertyAtIndex(idx);
+ }
+};
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"disable-memory-cache", OptionValue::eTypeBoolean, false,
+ DISABLE_MEM_CACHE_DEFAULT, nullptr, {},
+ "Disable reading and caching of memory in fixed-size units."},
+ {"extra-startup-command", OptionValue::eTypeArray, false,
+ OptionValue::eTypeString, nullptr, {},
+ "A list containing extra commands understood by the particular process "
+ "plugin used. "
+ "For instance, to turn on debugserver logging set this to "
+ "\"QSetLogging:bitmask=LOG_DEFAULT;\""},
+ {"ignore-breakpoints-in-expressions", OptionValue::eTypeBoolean, true, true,
+ nullptr, {},
+ "If true, breakpoints will be ignored during expression evaluation."},
+ {"unwind-on-error-in-expressions", OptionValue::eTypeBoolean, true, true,
+ nullptr, {}, "If true, errors in expression evaluation will unwind "
+ "the stack back to the state before the call."},
+ {"python-os-plugin-path", OptionValue::eTypeFileSpec, false, true, nullptr,
+ {}, "A path to a python OS plug-in module file that contains a "
+ "OperatingSystemPlugIn class."},
+ {"stop-on-sharedlibrary-events", OptionValue::eTypeBoolean, true, false,
+ nullptr, {},
+ "If true, stop when a shared library is loaded or unloaded."},
+ {"detach-keeps-stopped", OptionValue::eTypeBoolean, true, false, nullptr,
+ {}, "If true, detach will attempt to keep the process stopped."},
+ {"memory-cache-line-size", OptionValue::eTypeUInt64, false, 512, nullptr,
+ {}, "The memory cache line size"},
+ {"optimization-warnings", OptionValue::eTypeBoolean, false, true, nullptr,
+ {}, "If true, warn when stopped in code that is optimized where "
+ "stepping and variable availability may not behave as expected."},
+ {"stop-on-exec", OptionValue::eTypeBoolean, true, true,
+ nullptr, {},
+ "If true, stop when a shared library is loaded or unloaded."},
+ {"utility-expression-timeout", OptionValue::eTypeUInt64, false, 15,
+ nullptr, {},
+ "The time in seconds to wait for LLDB-internal utility expressions."}
+};
+
+enum {
+ ePropertyDisableMemCache,
+ ePropertyExtraStartCommand,
+ ePropertyIgnoreBreakpointsInExpressions,
+ ePropertyUnwindOnErrorInExpressions,
+ ePropertyPythonOSPluginPath,
+ ePropertyStopOnSharedLibraryEvents,
+ ePropertyDetachKeepsStopped,
+ ePropertyMemCacheLineSize,
+ ePropertyWarningOptimization,
+ ePropertyStopOnExec,
+ ePropertyUtilityExpressionTimeout,
+};
+
+ProcessProperties::ProcessProperties(lldb_private::Process *process)
+ : Properties(),
+ m_process(process) // Can be nullptr for global ProcessProperties
+{
+ if (process == nullptr) {
+ // Global process properties, set them up one time
+ m_collection_sp =
+ std::make_shared<ProcessOptionValueProperties>(ConstString("process"));
+ m_collection_sp->Initialize(g_properties);
+ m_collection_sp->AppendProperty(
+ ConstString("thread"), ConstString("Settings specific to threads."),
+ true, Thread::GetGlobalProperties()->GetValueProperties());
+ } else {
+ m_collection_sp = std::make_shared<ProcessOptionValueProperties>(
+ Process::GetGlobalProperties().get());
+ m_collection_sp->SetValueChangedCallback(
+ ePropertyPythonOSPluginPath,
+ ProcessProperties::OptionValueChangedCallback, this);
+ }
+}
+
+ProcessProperties::~ProcessProperties() = default;
+
+void ProcessProperties::OptionValueChangedCallback(void *baton,
+ OptionValue *option_value) {
+ ProcessProperties *properties = (ProcessProperties *)baton;
+ if (properties->m_process)
+ properties->m_process->LoadOperatingSystemPlugin(true);
+}
+
+bool ProcessProperties::GetDisableMemoryCache() const {
+ const uint32_t idx = ePropertyDisableMemCache;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+uint64_t ProcessProperties::GetMemoryCacheLineSize() const {
+ const uint32_t idx = ePropertyMemCacheLineSize;
+ return m_collection_sp->GetPropertyAtIndexAsUInt64(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+Args ProcessProperties::GetExtraStartupCommands() const {
+ Args args;
+ const uint32_t idx = ePropertyExtraStartCommand;
+ m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args);
+ return args;
+}
+
+void ProcessProperties::SetExtraStartupCommands(const Args &args) {
+ const uint32_t idx = ePropertyExtraStartCommand;
+ m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args);
+}
+
+FileSpec ProcessProperties::GetPythonOSPluginPath() const {
+ const uint32_t idx = ePropertyPythonOSPluginPath;
+ return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx);
+}
+
+void ProcessProperties::SetPythonOSPluginPath(const FileSpec &file) {
+ const uint32_t idx = ePropertyPythonOSPluginPath;
+ m_collection_sp->SetPropertyAtIndexAsFileSpec(nullptr, idx, file);
+}
+
+bool ProcessProperties::GetIgnoreBreakpointsInExpressions() const {
+ const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void ProcessProperties::SetIgnoreBreakpointsInExpressions(bool ignore) {
+ const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, ignore);
+}
+
+bool ProcessProperties::GetUnwindOnErrorInExpressions() const {
+ const uint32_t idx = ePropertyUnwindOnErrorInExpressions;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void ProcessProperties::SetUnwindOnErrorInExpressions(bool ignore) {
+ const uint32_t idx = ePropertyUnwindOnErrorInExpressions;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, ignore);
+}
+
+bool ProcessProperties::GetStopOnSharedLibraryEvents() const {
+ const uint32_t idx = ePropertyStopOnSharedLibraryEvents;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void ProcessProperties::SetStopOnSharedLibraryEvents(bool stop) {
+ const uint32_t idx = ePropertyStopOnSharedLibraryEvents;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, stop);
+}
+
+bool ProcessProperties::GetDetachKeepsStopped() const {
+ const uint32_t idx = ePropertyDetachKeepsStopped;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void ProcessProperties::SetDetachKeepsStopped(bool stop) {
+ const uint32_t idx = ePropertyDetachKeepsStopped;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, stop);
+}
+
+bool ProcessProperties::GetWarningsOptimization() const {
+ const uint32_t idx = ePropertyWarningOptimization;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool ProcessProperties::GetStopOnExec() const {
+ const uint32_t idx = ePropertyStopOnExec;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+std::chrono::seconds ProcessProperties::GetUtilityExpressionTimeout() const {
+ const uint32_t idx = ePropertyUtilityExpressionTimeout;
+ uint64_t value = m_collection_sp->GetPropertyAtIndexAsUInt64(
+ nullptr, idx, g_properties[idx].default_uint_value);
+ return std::chrono::seconds(value);
+}
+
+Status ProcessLaunchCommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 's': // Stop at program entry point
+ launch_info.GetFlags().Set(eLaunchFlagStopAtEntry);
+ break;
+
+ case 'i': // STDIN for read only
+ {
+ FileAction action;
+ if (action.Open(STDIN_FILENO, FileSpec(option_arg), true, false))
+ launch_info.AppendFileAction(action);
+ break;
+ }
+
+ case 'o': // Open STDOUT for write only
+ {
+ FileAction action;
+ if (action.Open(STDOUT_FILENO, FileSpec(option_arg), false, true))
+ launch_info.AppendFileAction(action);
+ break;
+ }
+
+ case 'e': // STDERR for write only
+ {
+ FileAction action;
+ if (action.Open(STDERR_FILENO, FileSpec(option_arg), false, true))
+ launch_info.AppendFileAction(action);
+ break;
+ }
+
+ case 'p': // Process plug-in name
+ launch_info.SetProcessPluginName(option_arg);
+ break;
+
+ case 'n': // Disable STDIO
+ {
+ FileAction action;
+ const FileSpec dev_null(FileSystem::DEV_NULL);
+ if (action.Open(STDIN_FILENO, dev_null, true, false))
+ launch_info.AppendFileAction(action);
+ if (action.Open(STDOUT_FILENO, dev_null, false, true))
+ launch_info.AppendFileAction(action);
+ if (action.Open(STDERR_FILENO, dev_null, false, true))
+ launch_info.AppendFileAction(action);
+ break;
+ }
+
+ case 'w':
+ launch_info.SetWorkingDirectory(FileSpec(option_arg));
+ break;
+
+ case 't': // Open process in new terminal window
+ launch_info.GetFlags().Set(eLaunchFlagLaunchInTTY);
+ break;
+
+ case 'a': {
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ PlatformSP platform_sp =
+ target_sp ? target_sp->GetPlatform() : PlatformSP();
+ launch_info.GetArchitecture() =
+ Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg);
+ } break;
+
+ case 'A': // Disable ASLR.
+ {
+ bool success;
+ const bool disable_aslr_arg =
+ OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ disable_aslr = disable_aslr_arg ? eLazyBoolYes : eLazyBoolNo;
+ else
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for disable-aslr option: '%s'",
+ option_arg.empty() ? "<null>" : option_arg.str().c_str());
+ break;
+ }
+
+ case 'X': // shell expand args.
+ {
+ bool success;
+ const bool expand_args =
+ OptionArgParser::ToBoolean(option_arg, true, &success);
+ if (success)
+ launch_info.SetShellExpandArguments(expand_args);
+ else
+ error.SetErrorStringWithFormat(
+ "Invalid boolean value for shell-expand-args option: '%s'",
+ option_arg.empty() ? "<null>" : option_arg.str().c_str());
+ break;
+ }
+
+ case 'c':
+ if (!option_arg.empty())
+ launch_info.SetShell(FileSpec(option_arg));
+ else
+ launch_info.SetShell(HostInfo::GetDefaultShell());
+ break;
+
+ case 'v':
+ launch_info.GetEnvironment().insert(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option character '%c'",
+ short_option);
+ break;
+ }
+ return error;
+}
+
+static constexpr OptionDefinition g_process_launch_options[] = {
+ {LLDB_OPT_SET_ALL, false, "stop-at-entry", 's', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Stop at the entry point of the program when launching a process."},
+ {LLDB_OPT_SET_ALL, false, "disable-aslr", 'A',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
+ "Set whether to disable address space layout randomization when launching "
+ "a process."},
+ {LLDB_OPT_SET_ALL, false, "plugin", 'p', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypePlugin,
+ "Name of the process plugin you want to use."},
+ {LLDB_OPT_SET_ALL, false, "working-dir", 'w',
+ OptionParser::eRequiredArgument, nullptr, {}, 0,
+ eArgTypeDirectoryName,
+ "Set the current working directory to <path> when running the inferior."},
+ {LLDB_OPT_SET_ALL, false, "arch", 'a', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeArchitecture,
+ "Set the architecture for the process to launch when ambiguous."},
+ {LLDB_OPT_SET_ALL, false, "environment", 'v',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone,
+ "Specify an environment variable name/value string (--environment "
+ "NAME=VALUE). Can be specified multiple times for subsequent environment "
+ "entries."},
+ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "shell", 'c',
+ OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeFilename,
+ "Run the process in a shell (not supported on all platforms)."},
+
+ {LLDB_OPT_SET_1, false, "stdin", 'i', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeFilename,
+ "Redirect stdin for the process to <filename>."},
+ {LLDB_OPT_SET_1, false, "stdout", 'o', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeFilename,
+ "Redirect stdout for the process to <filename>."},
+ {LLDB_OPT_SET_1, false, "stderr", 'e', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeFilename,
+ "Redirect stderr for the process to <filename>."},
+
+ {LLDB_OPT_SET_2, false, "tty", 't', OptionParser::eNoArgument, nullptr,
+ {}, 0, eArgTypeNone,
+ "Start the process in a terminal (not supported on all platforms)."},
+
+ {LLDB_OPT_SET_3, false, "no-stdio", 'n', OptionParser::eNoArgument, nullptr,
+ {}, 0, eArgTypeNone,
+ "Do not set up for terminal I/O to go to running process."},
+ {LLDB_OPT_SET_4, false, "shell-expand-args", 'X',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
+ "Set whether to shell expand arguments to the process when launching."},
+};
+
+llvm::ArrayRef<OptionDefinition> ProcessLaunchCommandOptions::GetDefinitions() {
+ return llvm::makeArrayRef(g_process_launch_options);
+}
+
+ProcessSP Process::FindPlugin(lldb::TargetSP target_sp,
+ llvm::StringRef plugin_name,
+ ListenerSP listener_sp,
+ const FileSpec *crash_file_path) {
+ static uint32_t g_process_unique_id = 0;
+
+ ProcessSP process_sp;
+ ProcessCreateInstance create_callback = nullptr;
+ if (!plugin_name.empty()) {
+ ConstString const_plugin_name(plugin_name);
+ create_callback =
+ PluginManager::GetProcessCreateCallbackForPluginName(const_plugin_name);
+ if (create_callback) {
+ process_sp = create_callback(target_sp, listener_sp, crash_file_path);
+ if (process_sp) {
+ if (process_sp->CanDebug(target_sp, true)) {
+ process_sp->m_process_unique_id = ++g_process_unique_id;
+ } else
+ process_sp.reset();
+ }
+ }
+ } else {
+ for (uint32_t idx = 0;
+ (create_callback =
+ PluginManager::GetProcessCreateCallbackAtIndex(idx)) != nullptr;
+ ++idx) {
+ process_sp = create_callback(target_sp, listener_sp, crash_file_path);
+ if (process_sp) {
+ if (process_sp->CanDebug(target_sp, false)) {
+ process_sp->m_process_unique_id = ++g_process_unique_id;
+ break;
+ } else
+ process_sp.reset();
+ }
+ }
+ }
+ return process_sp;
+}
+
+ConstString &Process::GetStaticBroadcasterClass() {
+ static ConstString class_name("lldb.process");
+ return class_name;
+}
+
+Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp)
+ : Process(target_sp, listener_sp,
+ UnixSignals::Create(HostInfo::GetArchitecture())) {
+ // This constructor just delegates to the full Process constructor,
+ // defaulting to using the Host's UnixSignals.
+}
+
+Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp,
+ const UnixSignalsSP &unix_signals_sp)
+ : ProcessProperties(this), UserID(LLDB_INVALID_PROCESS_ID),
+ Broadcaster((target_sp->GetDebugger().GetBroadcasterManager()),
+ Process::GetStaticBroadcasterClass().AsCString()),
+ m_target_wp(target_sp), m_public_state(eStateUnloaded),
+ m_private_state(eStateUnloaded),
+ m_private_state_broadcaster(nullptr,
+ "lldb.process.internal_state_broadcaster"),
+ m_private_state_control_broadcaster(
+ nullptr, "lldb.process.internal_state_control_broadcaster"),
+ m_private_state_listener_sp(
+ Listener::MakeListener("lldb.process.internal_state_listener")),
+ m_mod_id(), m_process_unique_id(0), m_thread_index_id(0),
+ m_thread_id_to_index_id_map(), m_exit_status(-1), m_exit_string(),
+ m_exit_status_mutex(), m_thread_mutex(), m_thread_list_real(this),
+ m_thread_list(this), m_extended_thread_list(this),
+ m_extended_thread_stop_id(0), m_queue_list(this), m_queue_list_stop_id(0),
+ m_notifications(), m_image_tokens(), m_listener_sp(listener_sp),
+ m_breakpoint_site_list(), m_dynamic_checkers_up(),
+ m_unix_signals_sp(unix_signals_sp), m_abi_sp(), m_process_input_reader(),
+ m_stdio_communication("process.stdio"), m_stdio_communication_mutex(),
+ m_stdin_forward(false), m_stdout_data(), m_stderr_data(),
+ m_profile_data_comm_mutex(), m_profile_data(), m_iohandler_sync(0),
+ m_memory_cache(*this), m_allocated_memory_cache(*this),
+ m_should_detach(false), m_next_event_action_up(), m_public_run_lock(),
+ m_private_run_lock(), m_finalizing(false), m_finalize_called(false),
+ m_clear_thread_plans_on_stop(false), m_force_next_event_delivery(false),
+ m_last_broadcast_state(eStateInvalid), m_destroy_in_process(false),
+ m_can_interpret_function_calls(false), m_warnings_issued(),
+ m_run_thread_plan_lock(), m_can_jit(eCanJITDontKnow) {
+ CheckInWithManager();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p Process::Process()", static_cast<void *>(this));
+
+ if (!m_unix_signals_sp)
+ m_unix_signals_sp = std::make_shared<UnixSignals>();
+
+ SetEventName(eBroadcastBitStateChanged, "state-changed");
+ SetEventName(eBroadcastBitInterrupt, "interrupt");
+ SetEventName(eBroadcastBitSTDOUT, "stdout-available");
+ SetEventName(eBroadcastBitSTDERR, "stderr-available");
+ SetEventName(eBroadcastBitProfileData, "profile-data-available");
+ SetEventName(eBroadcastBitStructuredData, "structured-data-available");
+
+ m_private_state_control_broadcaster.SetEventName(
+ eBroadcastInternalStateControlStop, "control-stop");
+ m_private_state_control_broadcaster.SetEventName(
+ eBroadcastInternalStateControlPause, "control-pause");
+ m_private_state_control_broadcaster.SetEventName(
+ eBroadcastInternalStateControlResume, "control-resume");
+
+ m_listener_sp->StartListeningForEvents(
+ this, eBroadcastBitStateChanged | eBroadcastBitInterrupt |
+ eBroadcastBitSTDOUT | eBroadcastBitSTDERR |
+ eBroadcastBitProfileData | eBroadcastBitStructuredData);
+
+ m_private_state_listener_sp->StartListeningForEvents(
+ &m_private_state_broadcaster,
+ eBroadcastBitStateChanged | eBroadcastBitInterrupt);
+
+ m_private_state_listener_sp->StartListeningForEvents(
+ &m_private_state_control_broadcaster,
+ eBroadcastInternalStateControlStop | eBroadcastInternalStateControlPause |
+ eBroadcastInternalStateControlResume);
+ // We need something valid here, even if just the default UnixSignalsSP.
+ assert(m_unix_signals_sp && "null m_unix_signals_sp after initialization");
+
+ // Allow the platform to override the default cache line size
+ OptionValueSP value_sp =
+ m_collection_sp
+ ->GetPropertyAtIndex(nullptr, true, ePropertyMemCacheLineSize)
+ ->GetValue();
+ uint32_t platform_cache_line_size =
+ target_sp->GetPlatform()->GetDefaultMemoryCacheLineSize();
+ if (!value_sp->OptionWasSet() && platform_cache_line_size != 0)
+ value_sp->SetUInt64Value(platform_cache_line_size);
+}
+
+Process::~Process() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p Process::~Process()", static_cast<void *>(this));
+ StopPrivateStateThread();
+
+ // ThreadList::Clear() will try to acquire this process's mutex, so
+ // explicitly clear the thread list here to ensure that the mutex is not
+ // destroyed before the thread list.
+ m_thread_list.Clear();
+}
+
+const ProcessPropertiesSP &Process::GetGlobalProperties() {
+ // NOTE: intentional leak so we don't crash if global destructor chain gets
+ // called as other threads still use the result of this function
+ static ProcessPropertiesSP *g_settings_sp_ptr =
+ new ProcessPropertiesSP(new ProcessProperties(nullptr));
+ return *g_settings_sp_ptr;
+}
+
+void Process::Finalize() {
+ m_finalizing = true;
+
+ // Destroy this process if needed
+ switch (GetPrivateState()) {
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateStopped:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateCrashed:
+ case eStateSuspended:
+ Destroy(false);
+ break;
+
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateDetached:
+ case eStateExited:
+ break;
+ }
+
+ // Clear our broadcaster before we proceed with destroying
+ Broadcaster::Clear();
+
+ // Do any cleanup needed prior to being destructed... Subclasses that
+ // override this method should call this superclass method as well.
+
+ // We need to destroy the loader before the derived Process class gets
+ // destroyed since it is very likely that undoing the loader will require
+ // access to the real process.
+ m_dynamic_checkers_up.reset();
+ m_abi_sp.reset();
+ m_os_up.reset();
+ m_system_runtime_up.reset();
+ m_dyld_up.reset();
+ m_jit_loaders_up.reset();
+ m_thread_list_real.Destroy();
+ m_thread_list.Destroy();
+ m_extended_thread_list.Destroy();
+ m_queue_list.Clear();
+ m_queue_list_stop_id = 0;
+ std::vector<Notifications> empty_notifications;
+ m_notifications.swap(empty_notifications);
+ m_image_tokens.clear();
+ m_memory_cache.Clear();
+ m_allocated_memory_cache.Clear();
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex);
+ m_language_runtimes.clear();
+ }
+ m_instrumentation_runtimes.clear();
+ m_next_event_action_up.reset();
+ // Clear the last natural stop ID since it has a strong reference to this
+ // process
+ m_mod_id.SetStopEventForLastNaturalStopID(EventSP());
+ //#ifdef LLDB_CONFIGURATION_DEBUG
+ // StreamFile s(stdout, false);
+ // EventSP event_sp;
+ // while (m_private_state_listener_sp->GetNextEvent(event_sp))
+ // {
+ // event_sp->Dump (&s);
+ // s.EOL();
+ // }
+ //#endif
+ // We have to be very careful here as the m_private_state_listener might
+ // contain events that have ProcessSP values in them which can keep this
+ // process around forever. These events need to be cleared out.
+ m_private_state_listener_sp->Clear();
+ m_public_run_lock.TrySetRunning(); // This will do nothing if already locked
+ m_public_run_lock.SetStopped();
+ m_private_run_lock.TrySetRunning(); // This will do nothing if already locked
+ m_private_run_lock.SetStopped();
+ m_structured_data_plugin_map.clear();
+ m_finalize_called = true;
+}
+
+void Process::RegisterNotificationCallbacks(const Notifications &callbacks) {
+ m_notifications.push_back(callbacks);
+ if (callbacks.initialize != nullptr)
+ callbacks.initialize(callbacks.baton, this);
+}
+
+bool Process::UnregisterNotificationCallbacks(const Notifications &callbacks) {
+ std::vector<Notifications>::iterator pos, end = m_notifications.end();
+ for (pos = m_notifications.begin(); pos != end; ++pos) {
+ if (pos->baton == callbacks.baton &&
+ pos->initialize == callbacks.initialize &&
+ pos->process_state_changed == callbacks.process_state_changed) {
+ m_notifications.erase(pos);
+ return true;
+ }
+ }
+ return false;
+}
+
+void Process::SynchronouslyNotifyStateChanged(StateType state) {
+ std::vector<Notifications>::iterator notification_pos,
+ notification_end = m_notifications.end();
+ for (notification_pos = m_notifications.begin();
+ notification_pos != notification_end; ++notification_pos) {
+ if (notification_pos->process_state_changed)
+ notification_pos->process_state_changed(notification_pos->baton, this,
+ state);
+ }
+}
+
+// FIXME: We need to do some work on events before the general Listener sees
+// them.
+// For instance if we are continuing from a breakpoint, we need to ensure that
+// we do the little "insert real insn, step & stop" trick. But we can't do
+// that when the event is delivered by the broadcaster - since that is done on
+// the thread that is waiting for new events, so if we needed more than one
+// event for our handling, we would stall. So instead we do it when we fetch
+// the event off of the queue.
+//
+
+StateType Process::GetNextEvent(EventSP &event_sp) {
+ StateType state = eStateInvalid;
+
+ if (m_listener_sp->GetEventForBroadcaster(this, event_sp,
+ std::chrono::seconds(0)) &&
+ event_sp)
+ state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+
+ return state;
+}
+
+void Process::SyncIOHandler(uint32_t iohandler_id,
+ const Timeout<std::micro> &timeout) {
+ // don't sync (potentially context switch) in case where there is no process
+ // IO
+ if (!m_process_input_reader)
+ return;
+
+ auto Result = m_iohandler_sync.WaitForValueNotEqualTo(iohandler_id, timeout);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (Result) {
+ LLDB_LOG(
+ log,
+ "waited from m_iohandler_sync to change from {0}. New value is {1}.",
+ iohandler_id, *Result);
+ } else {
+ LLDB_LOG(log, "timed out waiting for m_iohandler_sync to change from {0}.",
+ iohandler_id);
+ }
+}
+
+StateType Process::WaitForProcessToStop(const Timeout<std::micro> &timeout,
+ EventSP *event_sp_ptr, bool wait_always,
+ ListenerSP hijack_listener_sp,
+ Stream *stream, bool use_run_lock) {
+ // We can't just wait for a "stopped" event, because the stopped event may
+ // have restarted the target. We have to actually check each event, and in
+ // the case of a stopped event check the restarted flag on the event.
+ if (event_sp_ptr)
+ event_sp_ptr->reset();
+ StateType state = GetState();
+ // If we are exited or detached, we won't ever get back to any other valid
+ // state...
+ if (state == eStateDetached || state == eStateExited)
+ return state;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ LLDB_LOG(log, "timeout = {0}", timeout);
+
+ if (!wait_always && StateIsStoppedState(state, true) &&
+ StateIsStoppedState(GetPrivateState(), true)) {
+ if (log)
+ log->Printf("Process::%s returning without waiting for events; process "
+ "private and public states are already 'stopped'.",
+ __FUNCTION__);
+ // We need to toggle the run lock as this won't get done in
+ // SetPublicState() if the process is hijacked.
+ if (hijack_listener_sp && use_run_lock)
+ m_public_run_lock.SetStopped();
+ return state;
+ }
+
+ while (state != eStateInvalid) {
+ EventSP event_sp;
+ state = GetStateChangedEvents(event_sp, timeout, hijack_listener_sp);
+ if (event_sp_ptr && event_sp)
+ *event_sp_ptr = event_sp;
+
+ bool pop_process_io_handler = (hijack_listener_sp.get() != nullptr);
+ Process::HandleProcessStateChangedEvent(event_sp, stream,
+ pop_process_io_handler);
+
+ switch (state) {
+ case eStateCrashed:
+ case eStateDetached:
+ case eStateExited:
+ case eStateUnloaded:
+ // We need to toggle the run lock as this won't get done in
+ // SetPublicState() if the process is hijacked.
+ if (hijack_listener_sp && use_run_lock)
+ m_public_run_lock.SetStopped();
+ return state;
+ case eStateStopped:
+ if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get()))
+ continue;
+ else {
+ // We need to toggle the run lock as this won't get done in
+ // SetPublicState() if the process is hijacked.
+ if (hijack_listener_sp && use_run_lock)
+ m_public_run_lock.SetStopped();
+ return state;
+ }
+ default:
+ continue;
+ }
+ }
+ return state;
+}
+
+bool Process::HandleProcessStateChangedEvent(const EventSP &event_sp,
+ Stream *stream,
+ bool &pop_process_io_handler) {
+ const bool handle_pop = pop_process_io_handler;
+
+ pop_process_io_handler = false;
+ ProcessSP process_sp =
+ Process::ProcessEventData::GetProcessFromEvent(event_sp.get());
+
+ if (!process_sp)
+ return false;
+
+ StateType event_state =
+ Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+ if (event_state == eStateInvalid)
+ return false;
+
+ switch (event_state) {
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateStepping:
+ case eStateDetached:
+ if (stream)
+ stream->Printf("Process %" PRIu64 " %s\n", process_sp->GetID(),
+ StateAsCString(event_state));
+ if (event_state == eStateDetached)
+ pop_process_io_handler = true;
+ break;
+
+ case eStateConnected:
+ case eStateRunning:
+ // Don't be chatty when we run...
+ break;
+
+ case eStateExited:
+ if (stream)
+ process_sp->GetStatus(*stream);
+ pop_process_io_handler = true;
+ break;
+
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ // Make sure the program hasn't been auto-restarted:
+ if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) {
+ if (stream) {
+ size_t num_reasons =
+ Process::ProcessEventData::GetNumRestartedReasons(event_sp.get());
+ if (num_reasons > 0) {
+ // FIXME: Do we want to report this, or would that just be annoyingly
+ // chatty?
+ if (num_reasons == 1) {
+ const char *reason =
+ Process::ProcessEventData::GetRestartedReasonAtIndex(
+ event_sp.get(), 0);
+ stream->Printf("Process %" PRIu64 " stopped and restarted: %s\n",
+ process_sp->GetID(),
+ reason ? reason : "<UNKNOWN REASON>");
+ } else {
+ stream->Printf("Process %" PRIu64
+ " stopped and restarted, reasons:\n",
+ process_sp->GetID());
+
+ for (size_t i = 0; i < num_reasons; i++) {
+ const char *reason =
+ Process::ProcessEventData::GetRestartedReasonAtIndex(
+ event_sp.get(), i);
+ stream->Printf("\t%s\n", reason ? reason : "<UNKNOWN REASON>");
+ }
+ }
+ }
+ }
+ } else {
+ StopInfoSP curr_thread_stop_info_sp;
+ // Lock the thread list so it doesn't change on us, this is the scope for
+ // the locker:
+ {
+ ThreadList &thread_list = process_sp->GetThreadList();
+ std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
+
+ ThreadSP curr_thread(thread_list.GetSelectedThread());
+ ThreadSP thread;
+ StopReason curr_thread_stop_reason = eStopReasonInvalid;
+ if (curr_thread) {
+ curr_thread_stop_reason = curr_thread->GetStopReason();
+ curr_thread_stop_info_sp = curr_thread->GetStopInfo();
+ }
+ if (!curr_thread || !curr_thread->IsValid() ||
+ curr_thread_stop_reason == eStopReasonInvalid ||
+ curr_thread_stop_reason == eStopReasonNone) {
+ // Prefer a thread that has just completed its plan over another
+ // thread as current thread.
+ ThreadSP plan_thread;
+ ThreadSP other_thread;
+
+ const size_t num_threads = thread_list.GetSize();
+ size_t i;
+ for (i = 0; i < num_threads; ++i) {
+ thread = thread_list.GetThreadAtIndex(i);
+ StopReason thread_stop_reason = thread->GetStopReason();
+ switch (thread_stop_reason) {
+ case eStopReasonInvalid:
+ case eStopReasonNone:
+ break;
+
+ case eStopReasonSignal: {
+ // Don't select a signal thread if we weren't going to stop at
+ // that signal. We have to have had another reason for stopping
+ // here, and the user doesn't want to see this thread.
+ uint64_t signo = thread->GetStopInfo()->GetValue();
+ if (process_sp->GetUnixSignals()->GetShouldStop(signo)) {
+ if (!other_thread)
+ other_thread = thread;
+ }
+ break;
+ }
+ case eStopReasonTrace:
+ case eStopReasonBreakpoint:
+ case eStopReasonWatchpoint:
+ case eStopReasonException:
+ case eStopReasonExec:
+ case eStopReasonThreadExiting:
+ case eStopReasonInstrumentation:
+ if (!other_thread)
+ other_thread = thread;
+ break;
+ case eStopReasonPlanComplete:
+ if (!plan_thread)
+ plan_thread = thread;
+ break;
+ }
+ }
+ if (plan_thread)
+ thread_list.SetSelectedThreadByID(plan_thread->GetID());
+ else if (other_thread)
+ thread_list.SetSelectedThreadByID(other_thread->GetID());
+ else {
+ if (curr_thread && curr_thread->IsValid())
+ thread = curr_thread;
+ else
+ thread = thread_list.GetThreadAtIndex(0);
+
+ if (thread)
+ thread_list.SetSelectedThreadByID(thread->GetID());
+ }
+ }
+ }
+ // Drop the ThreadList mutex by here, since GetThreadStatus below might
+ // have to run code, e.g. for Data formatters, and if we hold the
+ // ThreadList mutex, then the process is going to have a hard time
+ // restarting the process.
+ if (stream) {
+ Debugger &debugger = process_sp->GetTarget().GetDebugger();
+ if (debugger.GetTargetList().GetSelectedTarget().get() ==
+ &process_sp->GetTarget()) {
+ const bool only_threads_with_stop_reason = true;
+ const uint32_t start_frame = 0;
+ const uint32_t num_frames = 1;
+ const uint32_t num_frames_with_source = 1;
+ const bool stop_format = true;
+ process_sp->GetStatus(*stream);
+ process_sp->GetThreadStatus(*stream, only_threads_with_stop_reason,
+ start_frame, num_frames,
+ num_frames_with_source,
+ stop_format);
+ if (curr_thread_stop_info_sp) {
+ lldb::addr_t crashing_address;
+ ValueObjectSP valobj_sp = StopInfo::GetCrashingDereference(
+ curr_thread_stop_info_sp, &crashing_address);
+ if (valobj_sp) {
+ const bool qualify_cxx_base_classes = false;
+
+ const ValueObject::GetExpressionPathFormat format =
+ ValueObject::GetExpressionPathFormat::
+ eGetExpressionPathFormatHonorPointers;
+ stream->PutCString("Likely cause: ");
+ valobj_sp->GetExpressionPath(*stream, qualify_cxx_base_classes,
+ format);
+ stream->Printf(" accessed 0x%" PRIx64 "\n", crashing_address);
+ }
+ }
+ } else {
+ uint32_t target_idx = debugger.GetTargetList().GetIndexOfTarget(
+ process_sp->GetTarget().shared_from_this());
+ if (target_idx != UINT32_MAX)
+ stream->Printf("Target %d: (", target_idx);
+ else
+ stream->Printf("Target <unknown index>: (");
+ process_sp->GetTarget().Dump(stream, eDescriptionLevelBrief);
+ stream->Printf(") stopped.\n");
+ }
+ }
+
+ // Pop the process IO handler
+ pop_process_io_handler = true;
+ }
+ break;
+ }
+
+ if (handle_pop && pop_process_io_handler)
+ process_sp->PopProcessIOHandler();
+
+ return true;
+}
+
+bool Process::HijackProcessEvents(ListenerSP listener_sp) {
+ if (listener_sp) {
+ return HijackBroadcaster(listener_sp, eBroadcastBitStateChanged |
+ eBroadcastBitInterrupt);
+ } else
+ return false;
+}
+
+void Process::RestoreProcessEvents() { RestoreBroadcaster(); }
+
+StateType Process::GetStateChangedEvents(EventSP &event_sp,
+ const Timeout<std::micro> &timeout,
+ ListenerSP hijack_listener_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout);
+
+ ListenerSP listener_sp = hijack_listener_sp;
+ if (!listener_sp)
+ listener_sp = m_listener_sp;
+
+ StateType state = eStateInvalid;
+ if (listener_sp->GetEventForBroadcasterWithType(
+ this, eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp,
+ timeout)) {
+ if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged)
+ state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+ else
+ LLDB_LOG(log, "got no event or was interrupted.");
+ }
+
+ LLDB_LOG(log, "timeout = {0}, event_sp) => {1}", timeout, state);
+ return state;
+}
+
+Event *Process::PeekAtStateChangedEvents() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf("Process::%s...", __FUNCTION__);
+
+ Event *event_ptr;
+ event_ptr = m_listener_sp->PeekAtNextEventForBroadcasterWithType(
+ this, eBroadcastBitStateChanged);
+ if (log) {
+ if (event_ptr) {
+ log->Printf(
+ "Process::%s (event_ptr) => %s", __FUNCTION__,
+ StateAsCString(ProcessEventData::GetStateFromEvent(event_ptr)));
+ } else {
+ log->Printf("Process::%s no events found", __FUNCTION__);
+ }
+ }
+ return event_ptr;
+}
+
+StateType
+Process::GetStateChangedEventsPrivate(EventSP &event_sp,
+ const Timeout<std::micro> &timeout) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout);
+
+ StateType state = eStateInvalid;
+ if (m_private_state_listener_sp->GetEventForBroadcasterWithType(
+ &m_private_state_broadcaster,
+ eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp,
+ timeout))
+ if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged)
+ state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+
+ LLDB_LOG(log, "timeout = {0}, event_sp) => {1}", timeout,
+ state == eStateInvalid ? "TIMEOUT" : StateAsCString(state));
+ return state;
+}
+
+bool Process::GetEventsPrivate(EventSP &event_sp,
+ const Timeout<std::micro> &timeout,
+ bool control_only) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout);
+
+ if (control_only)
+ return m_private_state_listener_sp->GetEventForBroadcaster(
+ &m_private_state_control_broadcaster, event_sp, timeout);
+ else
+ return m_private_state_listener_sp->GetEvent(event_sp, timeout);
+}
+
+bool Process::IsRunning() const {
+ return StateIsRunningState(m_public_state.GetValue());
+}
+
+int Process::GetExitStatus() {
+ std::lock_guard<std::mutex> guard(m_exit_status_mutex);
+
+ if (m_public_state.GetValue() == eStateExited)
+ return m_exit_status;
+ return -1;
+}
+
+const char *Process::GetExitDescription() {
+ std::lock_guard<std::mutex> guard(m_exit_status_mutex);
+
+ if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty())
+ return m_exit_string.c_str();
+ return nullptr;
+}
+
+bool Process::SetExitStatus(int status, const char *cstr) {
+ // Use a mutex to protect setting the exit status.
+ std::lock_guard<std::mutex> guard(m_exit_status_mutex);
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE |
+ LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf(
+ "Process::SetExitStatus (status=%i (0x%8.8x), description=%s%s%s)",
+ status, status, cstr ? "\"" : "", cstr ? cstr : "NULL",
+ cstr ? "\"" : "");
+
+ // We were already in the exited state
+ if (m_private_state.GetValue() == eStateExited) {
+ if (log)
+ log->Printf("Process::SetExitStatus () ignoring exit status because "
+ "state was already set to eStateExited");
+ return false;
+ }
+
+ m_exit_status = status;
+ if (cstr)
+ m_exit_string = cstr;
+ else
+ m_exit_string.clear();
+
+ // Clear the last natural stop ID since it has a strong reference to this
+ // process
+ m_mod_id.SetStopEventForLastNaturalStopID(EventSP());
+
+ SetPrivateState(eStateExited);
+
+ // Allow subclasses to do some cleanup
+ DidExit();
+
+ return true;
+}
+
+bool Process::IsAlive() {
+ switch (m_private_state.GetValue()) {
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateStopped:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateCrashed:
+ case eStateSuspended:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// This static callback can be used to watch for local child processes on the
+// current host. The child process exits, the process will be found in the
+// global target list (we want to be completely sure that the
+// lldb_private::Process doesn't go away before we can deliver the signal.
+bool Process::SetProcessExitStatus(
+ lldb::pid_t pid, bool exited,
+ int signo, // Zero for no signal
+ int exit_status // Exit value of process if signal is zero
+ ) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::SetProcessExitStatus (pid=%" PRIu64
+ ", exited=%i, signal=%i, exit_status=%i)\n",
+ pid, exited, signo, exit_status);
+
+ if (exited) {
+ TargetSP target_sp(Debugger::FindTargetWithProcessID(pid));
+ if (target_sp) {
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp) {
+ const char *signal_cstr = nullptr;
+ if (signo)
+ signal_cstr = process_sp->GetUnixSignals()->GetSignalAsCString(signo);
+
+ process_sp->SetExitStatus(exit_status, signal_cstr);
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+void Process::UpdateThreadListIfNeeded() {
+ const uint32_t stop_id = GetStopID();
+ if (m_thread_list.GetSize(false) == 0 ||
+ stop_id != m_thread_list.GetStopID()) {
+ const StateType state = GetPrivateState();
+ if (StateIsStoppedState(state, true)) {
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ // m_thread_list does have its own mutex, but we need to hold onto the
+ // mutex between the call to UpdateThreadList(...) and the
+ // os->UpdateThreadList(...) so it doesn't change on us
+ ThreadList &old_thread_list = m_thread_list;
+ ThreadList real_thread_list(this);
+ ThreadList new_thread_list(this);
+ // Always update the thread list with the protocol specific thread list,
+ // but only update if "true" is returned
+ if (UpdateThreadList(m_thread_list_real, real_thread_list)) {
+ // Don't call into the OperatingSystem to update the thread list if we
+ // are shutting down, since that may call back into the SBAPI's,
+ // requiring the API lock which is already held by whoever is shutting
+ // us down, causing a deadlock.
+ OperatingSystem *os = GetOperatingSystem();
+ if (os && !m_destroy_in_process) {
+ // Clear any old backing threads where memory threads might have been
+ // backed by actual threads from the lldb_private::Process subclass
+ size_t num_old_threads = old_thread_list.GetSize(false);
+ for (size_t i = 0; i < num_old_threads; ++i)
+ old_thread_list.GetThreadAtIndex(i, false)->ClearBackingThread();
+
+ // Turn off dynamic types to ensure we don't run any expressions.
+ // Objective-C can run an expression to determine if a SBValue is a
+ // dynamic type or not and we need to avoid this. OperatingSystem
+ // plug-ins can't run expressions that require running code...
+
+ Target &target = GetTarget();
+ const lldb::DynamicValueType saved_prefer_dynamic =
+ target.GetPreferDynamicValue();
+ if (saved_prefer_dynamic != lldb::eNoDynamicValues)
+ target.SetPreferDynamicValue(lldb::eNoDynamicValues);
+
+ // Now let the OperatingSystem plug-in update the thread list
+
+ os->UpdateThreadList(
+ old_thread_list, // Old list full of threads created by OS plug-in
+ real_thread_list, // The actual thread list full of threads
+ // created by each lldb_private::Process
+ // subclass
+ new_thread_list); // The new thread list that we will show to the
+ // user that gets filled in
+
+ if (saved_prefer_dynamic != lldb::eNoDynamicValues)
+ target.SetPreferDynamicValue(saved_prefer_dynamic);
+ } else {
+ // No OS plug-in, the new thread list is the same as the real thread
+ // list
+ new_thread_list = real_thread_list;
+ }
+
+ m_thread_list_real.Update(real_thread_list);
+ m_thread_list.Update(new_thread_list);
+ m_thread_list.SetStopID(stop_id);
+
+ if (GetLastNaturalStopID() != m_extended_thread_stop_id) {
+ // Clear any extended threads that we may have accumulated previously
+ m_extended_thread_list.Clear();
+ m_extended_thread_stop_id = GetLastNaturalStopID();
+
+ m_queue_list.Clear();
+ m_queue_list_stop_id = GetLastNaturalStopID();
+ }
+ }
+ }
+ }
+}
+
+void Process::UpdateQueueListIfNeeded() {
+ if (m_system_runtime_up) {
+ if (m_queue_list.GetSize() == 0 ||
+ m_queue_list_stop_id != GetLastNaturalStopID()) {
+ const StateType state = GetPrivateState();
+ if (StateIsStoppedState(state, true)) {
+ m_system_runtime_up->PopulateQueueList(m_queue_list);
+ m_queue_list_stop_id = GetLastNaturalStopID();
+ }
+ }
+ }
+}
+
+ThreadSP Process::CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context) {
+ OperatingSystem *os = GetOperatingSystem();
+ if (os)
+ return os->CreateThread(tid, context);
+ return ThreadSP();
+}
+
+uint32_t Process::GetNextThreadIndexID(uint64_t thread_id) {
+ return AssignIndexIDToThread(thread_id);
+}
+
+bool Process::HasAssignedIndexIDToThread(uint64_t thread_id) {
+ return (m_thread_id_to_index_id_map.find(thread_id) !=
+ m_thread_id_to_index_id_map.end());
+}
+
+uint32_t Process::AssignIndexIDToThread(uint64_t thread_id) {
+ uint32_t result = 0;
+ std::map<uint64_t, uint32_t>::iterator iterator =
+ m_thread_id_to_index_id_map.find(thread_id);
+ if (iterator == m_thread_id_to_index_id_map.end()) {
+ result = ++m_thread_index_id;
+ m_thread_id_to_index_id_map[thread_id] = result;
+ } else {
+ result = iterator->second;
+ }
+
+ return result;
+}
+
+StateType Process::GetState() {
+ return m_public_state.GetValue();
+}
+
+void Process::SetPublicState(StateType new_state, bool restarted) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE |
+ LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::SetPublicState (state = %s, restarted = %i)",
+ StateAsCString(new_state), restarted);
+ const StateType old_state = m_public_state.GetValue();
+ m_public_state.SetValue(new_state);
+
+ // On the transition from Run to Stopped, we unlock the writer end of the run
+ // lock. The lock gets locked in Resume, which is the public API to tell the
+ // program to run.
+ if (!StateChangedIsExternallyHijacked()) {
+ if (new_state == eStateDetached) {
+ if (log)
+ log->Printf(
+ "Process::SetPublicState (%s) -- unlocking run lock for detach",
+ StateAsCString(new_state));
+ m_public_run_lock.SetStopped();
+ } else {
+ const bool old_state_is_stopped = StateIsStoppedState(old_state, false);
+ const bool new_state_is_stopped = StateIsStoppedState(new_state, false);
+ if ((old_state_is_stopped != new_state_is_stopped)) {
+ if (new_state_is_stopped && !restarted) {
+ if (log)
+ log->Printf("Process::SetPublicState (%s) -- unlocking run lock",
+ StateAsCString(new_state));
+ m_public_run_lock.SetStopped();
+ }
+ }
+ }
+ }
+}
+
+Status Process::Resume() {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE |
+ LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::Resume -- locking run lock");
+ if (!m_public_run_lock.TrySetRunning()) {
+ Status error("Resume request failed - process still running.");
+ if (log)
+ log->Printf("Process::Resume: -- TrySetRunning failed, not resuming.");
+ return error;
+ }
+ Status error = PrivateResume();
+ if (!error.Success()) {
+ // Undo running state change
+ m_public_run_lock.SetStopped();
+ }
+ return error;
+}
+
+static const char *g_resume_sync_name = "lldb.Process.ResumeSynchronous.hijack";
+
+Status Process::ResumeSynchronous(Stream *stream) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE |
+ LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::ResumeSynchronous -- locking run lock");
+ if (!m_public_run_lock.TrySetRunning()) {
+ Status error("Resume request failed - process still running.");
+ if (log)
+ log->Printf("Process::Resume: -- TrySetRunning failed, not resuming.");
+ return error;
+ }
+
+ ListenerSP listener_sp(
+ Listener::MakeListener(g_resume_sync_name));
+ HijackProcessEvents(listener_sp);
+
+ Status error = PrivateResume();
+ if (error.Success()) {
+ StateType state =
+ WaitForProcessToStop(llvm::None, nullptr, true, listener_sp, stream);
+ const bool must_be_alive =
+ false; // eStateExited is ok, so this must be false
+ if (!StateIsStoppedState(state, must_be_alive))
+ error.SetErrorStringWithFormat(
+ "process not in stopped state after synchronous resume: %s",
+ StateAsCString(state));
+ } else {
+ // Undo running state change
+ m_public_run_lock.SetStopped();
+ }
+
+ // Undo the hijacking of process events...
+ RestoreProcessEvents();
+
+ return error;
+}
+
+bool Process::StateChangedIsExternallyHijacked() {
+ if (IsHijackedForEvent(eBroadcastBitStateChanged)) {
+ const char *hijacking_name = GetHijackingListenerName();
+ if (hijacking_name &&
+ strcmp(hijacking_name, g_resume_sync_name))
+ return true;
+ }
+ return false;
+}
+
+bool Process::StateChangedIsHijackedForSynchronousResume() {
+ if (IsHijackedForEvent(eBroadcastBitStateChanged)) {
+ const char *hijacking_name = GetHijackingListenerName();
+ if (hijacking_name &&
+ strcmp(hijacking_name, g_resume_sync_name) == 0)
+ return true;
+ }
+ return false;
+}
+
+StateType Process::GetPrivateState() { return m_private_state.GetValue(); }
+
+void Process::SetPrivateState(StateType new_state) {
+ if (m_finalize_called)
+ return;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE |
+ LIBLLDB_LOG_PROCESS));
+ bool state_changed = false;
+
+ if (log)
+ log->Printf("Process::SetPrivateState (%s)", StateAsCString(new_state));
+
+ std::lock_guard<std::recursive_mutex> thread_guard(m_thread_list.GetMutex());
+ std::lock_guard<std::recursive_mutex> guard(m_private_state.GetMutex());
+
+ const StateType old_state = m_private_state.GetValueNoLock();
+ state_changed = old_state != new_state;
+
+ const bool old_state_is_stopped = StateIsStoppedState(old_state, false);
+ const bool new_state_is_stopped = StateIsStoppedState(new_state, false);
+ if (old_state_is_stopped != new_state_is_stopped) {
+ if (new_state_is_stopped)
+ m_private_run_lock.SetStopped();
+ else
+ m_private_run_lock.SetRunning();
+ }
+
+ if (state_changed) {
+ m_private_state.SetValueNoLock(new_state);
+ EventSP event_sp(
+ new Event(eBroadcastBitStateChanged,
+ new ProcessEventData(shared_from_this(), new_state)));
+ if (StateIsStoppedState(new_state, false)) {
+ // Note, this currently assumes that all threads in the list stop when
+ // the process stops. In the future we will want to support a debugging
+ // model where some threads continue to run while others are stopped.
+ // When that happens we will either need a way for the thread list to
+ // identify which threads are stopping or create a special thread list
+ // containing only threads which actually stopped.
+ //
+ // The process plugin is responsible for managing the actual behavior of
+ // the threads and should have stopped any threads that are going to stop
+ // before we get here.
+ m_thread_list.DidStop();
+
+ m_mod_id.BumpStopID();
+ if (!m_mod_id.IsLastResumeForUserExpression())
+ m_mod_id.SetStopEventForLastNaturalStopID(event_sp);
+ m_memory_cache.Clear();
+ if (log)
+ log->Printf("Process::SetPrivateState (%s) stop_id = %u",
+ StateAsCString(new_state), m_mod_id.GetStopID());
+ }
+
+ // Use our target to get a shared pointer to ourselves...
+ if (m_finalize_called && !PrivateStateThreadIsValid())
+ BroadcastEvent(event_sp);
+ else
+ m_private_state_broadcaster.BroadcastEvent(event_sp);
+ } else {
+ if (log)
+ log->Printf(
+ "Process::SetPrivateState (%s) state didn't change. Ignoring...",
+ StateAsCString(new_state));
+ }
+}
+
+void Process::SetRunningUserExpression(bool on) {
+ m_mod_id.SetRunningUserExpression(on);
+}
+
+void Process::SetRunningUtilityFunction(bool on) {
+ m_mod_id.SetRunningUtilityFunction(on);
+}
+
+addr_t Process::GetImageInfoAddress() { return LLDB_INVALID_ADDRESS; }
+
+const lldb::ABISP &Process::GetABI() {
+ if (!m_abi_sp)
+ m_abi_sp = ABI::FindPlugin(shared_from_this(), GetTarget().GetArchitecture());
+ return m_abi_sp;
+}
+
+std::vector<LanguageRuntime *>
+Process::GetLanguageRuntimes(bool retry_if_null) {
+ std::vector<LanguageRuntime *> language_runtimes;
+
+ if (m_finalizing)
+ return language_runtimes;
+
+ std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex);
+ // Before we pass off a copy of the language runtimes, we must make sure that
+ // our collection is properly populated. It's possible that some of the
+ // language runtimes were not loaded yet, either because nobody requested it
+ // yet or the proper condition for loading wasn't yet met (e.g. libc++.so
+ // hadn't been loaded).
+ for (const lldb::LanguageType lang_type : Language::GetSupportedLanguages()) {
+ if (LanguageRuntime *runtime = GetLanguageRuntime(lang_type, retry_if_null))
+ language_runtimes.emplace_back(runtime);
+ }
+
+ return language_runtimes;
+}
+
+LanguageRuntime *Process::GetLanguageRuntime(lldb::LanguageType language,
+ bool retry_if_null) {
+ if (m_finalizing)
+ return nullptr;
+
+ LanguageRuntime *runtime = nullptr;
+
+ std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex);
+ LanguageRuntimeCollection::iterator pos;
+ pos = m_language_runtimes.find(language);
+ if (pos == m_language_runtimes.end() || (retry_if_null && !pos->second)) {
+ lldb::LanguageRuntimeSP runtime_sp(
+ LanguageRuntime::FindPlugin(this, language));
+
+ m_language_runtimes[language] = runtime_sp;
+ runtime = runtime_sp.get();
+ } else
+ runtime = pos->second.get();
+
+ if (runtime)
+ // It's possible that a language runtime can support multiple LanguageTypes,
+ // for example, CPPLanguageRuntime will support eLanguageTypeC_plus_plus,
+ // eLanguageTypeC_plus_plus_03, etc. Because of this, we should get the
+ // primary language type and make sure that our runtime supports it.
+ assert(runtime->GetLanguageType() == Language::GetPrimaryLanguage(language));
+
+ return runtime;
+}
+
+bool Process::IsPossibleDynamicValue(ValueObject &in_value) {
+ if (m_finalizing)
+ return false;
+
+ if (in_value.IsDynamic())
+ return false;
+ LanguageType known_type = in_value.GetObjectRuntimeLanguage();
+
+ if (known_type != eLanguageTypeUnknown && known_type != eLanguageTypeC) {
+ LanguageRuntime *runtime = GetLanguageRuntime(known_type);
+ return runtime ? runtime->CouldHaveDynamicValue(in_value) : false;
+ }
+
+ for (LanguageRuntime *runtime : GetLanguageRuntimes()) {
+ if (runtime->CouldHaveDynamicValue(in_value))
+ return true;
+ }
+
+ return false;
+}
+
+void Process::SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers) {
+ m_dynamic_checkers_up.reset(dynamic_checkers);
+}
+
+BreakpointSiteList &Process::GetBreakpointSiteList() {
+ return m_breakpoint_site_list;
+}
+
+const BreakpointSiteList &Process::GetBreakpointSiteList() const {
+ return m_breakpoint_site_list;
+}
+
+void Process::DisableAllBreakpointSites() {
+ m_breakpoint_site_list.ForEach([this](BreakpointSite *bp_site) -> void {
+ // bp_site->SetEnabled(true);
+ DisableBreakpointSite(bp_site);
+ });
+}
+
+Status Process::ClearBreakpointSiteByID(lldb::user_id_t break_id) {
+ Status error(DisableBreakpointSiteByID(break_id));
+
+ if (error.Success())
+ m_breakpoint_site_list.Remove(break_id);
+
+ return error;
+}
+
+Status Process::DisableBreakpointSiteByID(lldb::user_id_t break_id) {
+ Status error;
+ BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id);
+ if (bp_site_sp) {
+ if (bp_site_sp->IsEnabled())
+ error = DisableBreakpointSite(bp_site_sp.get());
+ } else {
+ error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64,
+ break_id);
+ }
+
+ return error;
+}
+
+Status Process::EnableBreakpointSiteByID(lldb::user_id_t break_id) {
+ Status error;
+ BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id);
+ if (bp_site_sp) {
+ if (!bp_site_sp->IsEnabled())
+ error = EnableBreakpointSite(bp_site_sp.get());
+ } else {
+ error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64,
+ break_id);
+ }
+ return error;
+}
+
+lldb::break_id_t
+Process::CreateBreakpointSite(const BreakpointLocationSP &owner,
+ bool use_hardware) {
+ addr_t load_addr = LLDB_INVALID_ADDRESS;
+
+ bool show_error = true;
+ switch (GetState()) {
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateDetached:
+ case eStateExited:
+ show_error = false;
+ break;
+
+ case eStateStopped:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateCrashed:
+ case eStateSuspended:
+ show_error = IsAlive();
+ break;
+ }
+
+ // Reset the IsIndirect flag here, in case the location changes from pointing
+ // to a indirect symbol to a regular symbol.
+ owner->SetIsIndirect(false);
+
+ if (owner->ShouldResolveIndirectFunctions()) {
+ Symbol *symbol = owner->GetAddress().CalculateSymbolContextSymbol();
+ if (symbol && symbol->IsIndirect()) {
+ Status error;
+ Address symbol_address = symbol->GetAddress();
+ load_addr = ResolveIndirectFunction(&symbol_address, error);
+ if (!error.Success() && show_error) {
+ GetTarget().GetDebugger().GetErrorFile()->Printf(
+ "warning: failed to resolve indirect function at 0x%" PRIx64
+ " for breakpoint %i.%i: %s\n",
+ symbol->GetLoadAddress(&GetTarget()),
+ owner->GetBreakpoint().GetID(), owner->GetID(),
+ error.AsCString() ? error.AsCString() : "unknown error");
+ return LLDB_INVALID_BREAK_ID;
+ }
+ Address resolved_address(load_addr);
+ load_addr = resolved_address.GetOpcodeLoadAddress(&GetTarget());
+ owner->SetIsIndirect(true);
+ } else
+ load_addr = owner->GetAddress().GetOpcodeLoadAddress(&GetTarget());
+ } else
+ load_addr = owner->GetAddress().GetOpcodeLoadAddress(&GetTarget());
+
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ BreakpointSiteSP bp_site_sp;
+
+ // Look up this breakpoint site. If it exists, then add this new owner,
+ // otherwise create a new breakpoint site and add it.
+
+ bp_site_sp = m_breakpoint_site_list.FindByAddress(load_addr);
+
+ if (bp_site_sp) {
+ bp_site_sp->AddOwner(owner);
+ owner->SetBreakpointSite(bp_site_sp);
+ return bp_site_sp->GetID();
+ } else {
+ bp_site_sp.reset(new BreakpointSite(&m_breakpoint_site_list, owner,
+ load_addr, use_hardware));
+ if (bp_site_sp) {
+ Status error = EnableBreakpointSite(bp_site_sp.get());
+ if (error.Success()) {
+ owner->SetBreakpointSite(bp_site_sp);
+ return m_breakpoint_site_list.Add(bp_site_sp);
+ } else {
+ if (show_error || use_hardware) {
+ // Report error for setting breakpoint...
+ GetTarget().GetDebugger().GetErrorFile()->Printf(
+ "warning: failed to set breakpoint site at 0x%" PRIx64
+ " for breakpoint %i.%i: %s\n",
+ load_addr, owner->GetBreakpoint().GetID(), owner->GetID(),
+ error.AsCString() ? error.AsCString() : "unknown error");
+ }
+ }
+ }
+ }
+ }
+ // We failed to enable the breakpoint
+ return LLDB_INVALID_BREAK_ID;
+}
+
+void Process::RemoveOwnerFromBreakpointSite(lldb::user_id_t owner_id,
+ lldb::user_id_t owner_loc_id,
+ BreakpointSiteSP &bp_site_sp) {
+ uint32_t num_owners = bp_site_sp->RemoveOwner(owner_id, owner_loc_id);
+ if (num_owners == 0) {
+ // Don't try to disable the site if we don't have a live process anymore.
+ if (IsAlive())
+ DisableBreakpointSite(bp_site_sp.get());
+ m_breakpoint_site_list.RemoveByAddress(bp_site_sp->GetLoadAddress());
+ }
+}
+
+size_t Process::RemoveBreakpointOpcodesFromBuffer(addr_t bp_addr, size_t size,
+ uint8_t *buf) const {
+ size_t bytes_removed = 0;
+ BreakpointSiteList bp_sites_in_range;
+
+ if (m_breakpoint_site_list.FindInRange(bp_addr, bp_addr + size,
+ bp_sites_in_range)) {
+ bp_sites_in_range.ForEach([bp_addr, size,
+ buf](BreakpointSite *bp_site) -> void {
+ if (bp_site->GetType() == BreakpointSite::eSoftware) {
+ addr_t intersect_addr;
+ size_t intersect_size;
+ size_t opcode_offset;
+ if (bp_site->IntersectsRange(bp_addr, size, &intersect_addr,
+ &intersect_size, &opcode_offset)) {
+ assert(bp_addr <= intersect_addr && intersect_addr < bp_addr + size);
+ assert(bp_addr < intersect_addr + intersect_size &&
+ intersect_addr + intersect_size <= bp_addr + size);
+ assert(opcode_offset + intersect_size <= bp_site->GetByteSize());
+ size_t buf_offset = intersect_addr - bp_addr;
+ ::memcpy(buf + buf_offset,
+ bp_site->GetSavedOpcodeBytes() + opcode_offset,
+ intersect_size);
+ }
+ }
+ });
+ }
+ return bytes_removed;
+}
+
+size_t Process::GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site) {
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ if (platform_sp)
+ return platform_sp->GetSoftwareBreakpointTrapOpcode(GetTarget(), bp_site);
+ return 0;
+}
+
+Status Process::EnableSoftwareBreakpoint(BreakpointSite *bp_site) {
+ Status error;
+ assert(bp_site != nullptr);
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ const addr_t bp_addr = bp_site->GetLoadAddress();
+ if (log)
+ log->Printf(
+ "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64,
+ bp_site->GetID(), (uint64_t)bp_addr);
+ if (bp_site->IsEnabled()) {
+ if (log)
+ log->Printf(
+ "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
+ " -- already enabled",
+ bp_site->GetID(), (uint64_t)bp_addr);
+ return error;
+ }
+
+ if (bp_addr == LLDB_INVALID_ADDRESS) {
+ error.SetErrorString("BreakpointSite contains an invalid load address.");
+ return error;
+ }
+ // Ask the lldb::Process subclass to fill in the correct software breakpoint
+ // trap for the breakpoint site
+ const size_t bp_opcode_size = GetSoftwareBreakpointTrapOpcode(bp_site);
+
+ if (bp_opcode_size == 0) {
+ error.SetErrorStringWithFormat("Process::GetSoftwareBreakpointTrapOpcode() "
+ "returned zero, unable to get breakpoint "
+ "trap for address 0x%" PRIx64,
+ bp_addr);
+ } else {
+ const uint8_t *const bp_opcode_bytes = bp_site->GetTrapOpcodeBytes();
+
+ if (bp_opcode_bytes == nullptr) {
+ error.SetErrorString(
+ "BreakpointSite doesn't contain a valid breakpoint trap opcode.");
+ return error;
+ }
+
+ // Save the original opcode by reading it
+ if (DoReadMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), bp_opcode_size,
+ error) == bp_opcode_size) {
+ // Write a software breakpoint in place of the original opcode
+ if (DoWriteMemory(bp_addr, bp_opcode_bytes, bp_opcode_size, error) ==
+ bp_opcode_size) {
+ uint8_t verify_bp_opcode_bytes[64];
+ if (DoReadMemory(bp_addr, verify_bp_opcode_bytes, bp_opcode_size,
+ error) == bp_opcode_size) {
+ if (::memcmp(bp_opcode_bytes, verify_bp_opcode_bytes,
+ bp_opcode_size) == 0) {
+ bp_site->SetEnabled(true);
+ bp_site->SetType(BreakpointSite::eSoftware);
+ if (log)
+ log->Printf("Process::EnableSoftwareBreakpoint (site_id = %d) "
+ "addr = 0x%" PRIx64 " -- SUCCESS",
+ bp_site->GetID(), (uint64_t)bp_addr);
+ } else
+ error.SetErrorString(
+ "failed to verify the breakpoint trap in memory.");
+ } else
+ error.SetErrorString(
+ "Unable to read memory to verify breakpoint trap.");
+ } else
+ error.SetErrorString("Unable to write breakpoint trap to memory.");
+ } else
+ error.SetErrorString("Unable to read memory at breakpoint address.");
+ }
+ if (log && error.Fail())
+ log->Printf(
+ "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
+ " -- FAILED: %s",
+ bp_site->GetID(), (uint64_t)bp_addr, error.AsCString());
+ return error;
+}
+
+Status Process::DisableSoftwareBreakpoint(BreakpointSite *bp_site) {
+ Status error;
+ assert(bp_site != nullptr);
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ addr_t bp_addr = bp_site->GetLoadAddress();
+ lldb::user_id_t breakID = bp_site->GetID();
+ if (log)
+ log->Printf("Process::DisableSoftwareBreakpoint (breakID = %" PRIu64
+ ") addr = 0x%" PRIx64,
+ breakID, (uint64_t)bp_addr);
+
+ if (bp_site->IsHardware()) {
+ error.SetErrorString("Breakpoint site is a hardware breakpoint.");
+ } else if (bp_site->IsEnabled()) {
+ const size_t break_op_size = bp_site->GetByteSize();
+ const uint8_t *const break_op = bp_site->GetTrapOpcodeBytes();
+ if (break_op_size > 0) {
+ // Clear a software breakpoint instruction
+ uint8_t curr_break_op[8];
+ assert(break_op_size <= sizeof(curr_break_op));
+ bool break_op_found = false;
+
+ // Read the breakpoint opcode
+ if (DoReadMemory(bp_addr, curr_break_op, break_op_size, error) ==
+ break_op_size) {
+ bool verify = false;
+ // Make sure the breakpoint opcode exists at this address
+ if (::memcmp(curr_break_op, break_op, break_op_size) == 0) {
+ break_op_found = true;
+ // We found a valid breakpoint opcode at this address, now restore
+ // the saved opcode.
+ if (DoWriteMemory(bp_addr, bp_site->GetSavedOpcodeBytes(),
+ break_op_size, error) == break_op_size) {
+ verify = true;
+ } else
+ error.SetErrorString(
+ "Memory write failed when restoring original opcode.");
+ } else {
+ error.SetErrorString(
+ "Original breakpoint trap is no longer in memory.");
+ // Set verify to true and so we can check if the original opcode has
+ // already been restored
+ verify = true;
+ }
+
+ if (verify) {
+ uint8_t verify_opcode[8];
+ assert(break_op_size < sizeof(verify_opcode));
+ // Verify that our original opcode made it back to the inferior
+ if (DoReadMemory(bp_addr, verify_opcode, break_op_size, error) ==
+ break_op_size) {
+ // compare the memory we just read with the original opcode
+ if (::memcmp(bp_site->GetSavedOpcodeBytes(), verify_opcode,
+ break_op_size) == 0) {
+ // SUCCESS
+ bp_site->SetEnabled(false);
+ if (log)
+ log->Printf("Process::DisableSoftwareBreakpoint (site_id = %d) "
+ "addr = 0x%" PRIx64 " -- SUCCESS",
+ bp_site->GetID(), (uint64_t)bp_addr);
+ return error;
+ } else {
+ if (break_op_found)
+ error.SetErrorString("Failed to restore original opcode.");
+ }
+ } else
+ error.SetErrorString("Failed to read memory to verify that "
+ "breakpoint trap was restored.");
+ }
+ } else
+ error.SetErrorString(
+ "Unable to read memory that should contain the breakpoint trap.");
+ }
+ } else {
+ if (log)
+ log->Printf(
+ "Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
+ " -- already disabled",
+ bp_site->GetID(), (uint64_t)bp_addr);
+ return error;
+ }
+
+ if (log)
+ log->Printf(
+ "Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
+ " -- FAILED: %s",
+ bp_site->GetID(), (uint64_t)bp_addr, error.AsCString());
+ return error;
+}
+
+// Uncomment to verify memory caching works after making changes to caching
+// code
+//#define VERIFY_MEMORY_READS
+
+size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) {
+ error.Clear();
+ if (!GetDisableMemoryCache()) {
+#if defined(VERIFY_MEMORY_READS)
+ // Memory caching is enabled, with debug verification
+
+ if (buf && size) {
+ // Uncomment the line below to make sure memory caching is working.
+ // I ran this through the test suite and got no assertions, so I am
+ // pretty confident this is working well. If any changes are made to
+ // memory caching, uncomment the line below and test your changes!
+
+ // Verify all memory reads by using the cache first, then redundantly
+ // reading the same memory from the inferior and comparing to make sure
+ // everything is exactly the same.
+ std::string verify_buf(size, '\0');
+ assert(verify_buf.size() == size);
+ const size_t cache_bytes_read =
+ m_memory_cache.Read(this, addr, buf, size, error);
+ Status verify_error;
+ const size_t verify_bytes_read =
+ ReadMemoryFromInferior(addr, const_cast<char *>(verify_buf.data()),
+ verify_buf.size(), verify_error);
+ assert(cache_bytes_read == verify_bytes_read);
+ assert(memcmp(buf, verify_buf.data(), verify_buf.size()) == 0);
+ assert(verify_error.Success() == error.Success());
+ return cache_bytes_read;
+ }
+ return 0;
+#else // !defined(VERIFY_MEMORY_READS)
+ // Memory caching is enabled, without debug verification
+
+ return m_memory_cache.Read(addr, buf, size, error);
+#endif // defined (VERIFY_MEMORY_READS)
+ } else {
+ // Memory caching is disabled
+
+ return ReadMemoryFromInferior(addr, buf, size, error);
+ }
+}
+
+size_t Process::ReadCStringFromMemory(addr_t addr, std::string &out_str,
+ Status &error) {
+ char buf[256];
+ out_str.clear();
+ addr_t curr_addr = addr;
+ while (true) {
+ size_t length = ReadCStringFromMemory(curr_addr, buf, sizeof(buf), error);
+ if (length == 0)
+ break;
+ out_str.append(buf, length);
+ // If we got "length - 1" bytes, we didn't get the whole C string, we need
+ // to read some more characters
+ if (length == sizeof(buf) - 1)
+ curr_addr += length;
+ else
+ break;
+ }
+ return out_str.size();
+}
+
+size_t Process::ReadStringFromMemory(addr_t addr, char *dst, size_t max_bytes,
+ Status &error, size_t type_width) {
+ size_t total_bytes_read = 0;
+ if (dst && max_bytes && type_width && max_bytes >= type_width) {
+ // Ensure a null terminator independent of the number of bytes that is
+ // read.
+ memset(dst, 0, max_bytes);
+ size_t bytes_left = max_bytes - type_width;
+
+ const char terminator[4] = {'\0', '\0', '\0', '\0'};
+ assert(sizeof(terminator) >= type_width && "Attempting to validate a "
+ "string with more than 4 bytes "
+ "per character!");
+
+ addr_t curr_addr = addr;
+ const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize();
+ char *curr_dst = dst;
+
+ error.Clear();
+ while (bytes_left > 0 && error.Success()) {
+ addr_t cache_line_bytes_left =
+ cache_line_size - (curr_addr % cache_line_size);
+ addr_t bytes_to_read =
+ std::min<addr_t>(bytes_left, cache_line_bytes_left);
+ size_t bytes_read = ReadMemory(curr_addr, curr_dst, bytes_to_read, error);
+
+ if (bytes_read == 0)
+ break;
+
+ // Search for a null terminator of correct size and alignment in
+ // bytes_read
+ size_t aligned_start = total_bytes_read - total_bytes_read % type_width;
+ for (size_t i = aligned_start;
+ i + type_width <= total_bytes_read + bytes_read; i += type_width)
+ if (::memcmp(&dst[i], terminator, type_width) == 0) {
+ error.Clear();
+ return i;
+ }
+
+ total_bytes_read += bytes_read;
+ curr_dst += bytes_read;
+ curr_addr += bytes_read;
+ bytes_left -= bytes_read;
+ }
+ } else {
+ if (max_bytes)
+ error.SetErrorString("invalid arguments");
+ }
+ return total_bytes_read;
+}
+
+// Deprecated in favor of ReadStringFromMemory which has wchar support and
+// correct code to find null terminators.
+size_t Process::ReadCStringFromMemory(addr_t addr, char *dst,
+ size_t dst_max_len,
+ Status &result_error) {
+ size_t total_cstr_len = 0;
+ if (dst && dst_max_len) {
+ result_error.Clear();
+ // NULL out everything just to be safe
+ memset(dst, 0, dst_max_len);
+ Status error;
+ addr_t curr_addr = addr;
+ const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize();
+ size_t bytes_left = dst_max_len - 1;
+ char *curr_dst = dst;
+
+ while (bytes_left > 0) {
+ addr_t cache_line_bytes_left =
+ cache_line_size - (curr_addr % cache_line_size);
+ addr_t bytes_to_read =
+ std::min<addr_t>(bytes_left, cache_line_bytes_left);
+ size_t bytes_read = ReadMemory(curr_addr, curr_dst, bytes_to_read, error);
+
+ if (bytes_read == 0) {
+ result_error = error;
+ dst[total_cstr_len] = '\0';
+ break;
+ }
+ const size_t len = strlen(curr_dst);
+
+ total_cstr_len += len;
+
+ if (len < bytes_to_read)
+ break;
+
+ curr_dst += bytes_read;
+ curr_addr += bytes_read;
+ bytes_left -= bytes_read;
+ }
+ } else {
+ if (dst == nullptr)
+ result_error.SetErrorString("invalid arguments");
+ else
+ result_error.Clear();
+ }
+ return total_cstr_len;
+}
+
+size_t Process::ReadMemoryFromInferior(addr_t addr, void *buf, size_t size,
+ Status &error) {
+ if (buf == nullptr || size == 0)
+ return 0;
+
+ size_t bytes_read = 0;
+ uint8_t *bytes = (uint8_t *)buf;
+
+ while (bytes_read < size) {
+ const size_t curr_size = size - bytes_read;
+ const size_t curr_bytes_read =
+ DoReadMemory(addr + bytes_read, bytes + bytes_read, curr_size, error);
+ bytes_read += curr_bytes_read;
+ if (curr_bytes_read == curr_size || curr_bytes_read == 0)
+ break;
+ }
+
+ // Replace any software breakpoint opcodes that fall into this range back
+ // into "buf" before we return
+ if (bytes_read > 0)
+ RemoveBreakpointOpcodesFromBuffer(addr, bytes_read, (uint8_t *)buf);
+ return bytes_read;
+}
+
+uint64_t Process::ReadUnsignedIntegerFromMemory(lldb::addr_t vm_addr,
+ size_t integer_byte_size,
+ uint64_t fail_value,
+ Status &error) {
+ Scalar scalar;
+ if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, false, scalar,
+ error))
+ return scalar.ULongLong(fail_value);
+ return fail_value;
+}
+
+int64_t Process::ReadSignedIntegerFromMemory(lldb::addr_t vm_addr,
+ size_t integer_byte_size,
+ int64_t fail_value,
+ Status &error) {
+ Scalar scalar;
+ if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, true, scalar,
+ error))
+ return scalar.SLongLong(fail_value);
+ return fail_value;
+}
+
+addr_t Process::ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error) {
+ Scalar scalar;
+ if (ReadScalarIntegerFromMemory(vm_addr, GetAddressByteSize(), false, scalar,
+ error))
+ return scalar.ULongLong(LLDB_INVALID_ADDRESS);
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool Process::WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value,
+ Status &error) {
+ Scalar scalar;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ if (addr_byte_size <= 4)
+ scalar = (uint32_t)ptr_value;
+ else
+ scalar = ptr_value;
+ return WriteScalarToMemory(vm_addr, scalar, addr_byte_size, error) ==
+ addr_byte_size;
+}
+
+size_t Process::WriteMemoryPrivate(addr_t addr, const void *buf, size_t size,
+ Status &error) {
+ size_t bytes_written = 0;
+ const uint8_t *bytes = (const uint8_t *)buf;
+
+ while (bytes_written < size) {
+ const size_t curr_size = size - bytes_written;
+ const size_t curr_bytes_written = DoWriteMemory(
+ addr + bytes_written, bytes + bytes_written, curr_size, error);
+ bytes_written += curr_bytes_written;
+ if (curr_bytes_written == curr_size || curr_bytes_written == 0)
+ break;
+ }
+ return bytes_written;
+}
+
+size_t Process::WriteMemory(addr_t addr, const void *buf, size_t size,
+ Status &error) {
+#if defined(ENABLE_MEMORY_CACHING)
+ m_memory_cache.Flush(addr, size);
+#endif
+
+ if (buf == nullptr || size == 0)
+ return 0;
+
+ m_mod_id.BumpMemoryID();
+
+ // We need to write any data that would go where any current software traps
+ // (enabled software breakpoints) any software traps (breakpoints) that we
+ // may have placed in our tasks memory.
+
+ BreakpointSiteList bp_sites_in_range;
+ if (!m_breakpoint_site_list.FindInRange(addr, addr + size, bp_sites_in_range))
+ return WriteMemoryPrivate(addr, buf, size, error);
+
+ // No breakpoint sites overlap
+ if (bp_sites_in_range.IsEmpty())
+ return WriteMemoryPrivate(addr, buf, size, error);
+
+ const uint8_t *ubuf = (const uint8_t *)buf;
+ uint64_t bytes_written = 0;
+
+ bp_sites_in_range.ForEach([this, addr, size, &bytes_written, &ubuf,
+ &error](BreakpointSite *bp) -> void {
+ if (error.Fail())
+ return;
+
+ addr_t intersect_addr;
+ size_t intersect_size;
+ size_t opcode_offset;
+ const bool intersects = bp->IntersectsRange(
+ addr, size, &intersect_addr, &intersect_size, &opcode_offset);
+ UNUSED_IF_ASSERT_DISABLED(intersects);
+ assert(intersects);
+ assert(addr <= intersect_addr && intersect_addr < addr + size);
+ assert(addr < intersect_addr + intersect_size &&
+ intersect_addr + intersect_size <= addr + size);
+ assert(opcode_offset + intersect_size <= bp->GetByteSize());
+
+ // Check for bytes before this breakpoint
+ const addr_t curr_addr = addr + bytes_written;
+ if (intersect_addr > curr_addr) {
+ // There are some bytes before this breakpoint that we need to just
+ // write to memory
+ size_t curr_size = intersect_addr - curr_addr;
+ size_t curr_bytes_written =
+ WriteMemoryPrivate(curr_addr, ubuf + bytes_written, curr_size, error);
+ bytes_written += curr_bytes_written;
+ if (curr_bytes_written != curr_size) {
+ // We weren't able to write all of the requested bytes, we are
+ // done looping and will return the number of bytes that we have
+ // written so far.
+ if (error.Success())
+ error.SetErrorToGenericError();
+ }
+ }
+ // Now write any bytes that would cover up any software breakpoints
+ // directly into the breakpoint opcode buffer
+ ::memcpy(bp->GetSavedOpcodeBytes() + opcode_offset, ubuf + bytes_written,
+ intersect_size);
+ bytes_written += intersect_size;
+ });
+
+ // Write any remaining bytes after the last breakpoint if we have any left
+ if (bytes_written < size)
+ bytes_written +=
+ WriteMemoryPrivate(addr + bytes_written, ubuf + bytes_written,
+ size - bytes_written, error);
+
+ return bytes_written;
+}
+
+size_t Process::WriteScalarToMemory(addr_t addr, const Scalar &scalar,
+ size_t byte_size, Status &error) {
+ if (byte_size == UINT32_MAX)
+ byte_size = scalar.GetByteSize();
+ if (byte_size > 0) {
+ uint8_t buf[32];
+ const size_t mem_size =
+ scalar.GetAsMemoryData(buf, byte_size, GetByteOrder(), error);
+ if (mem_size > 0)
+ return WriteMemory(addr, buf, mem_size, error);
+ else
+ error.SetErrorString("failed to get scalar as memory data");
+ } else {
+ error.SetErrorString("invalid scalar value");
+ }
+ return 0;
+}
+
+size_t Process::ReadScalarIntegerFromMemory(addr_t addr, uint32_t byte_size,
+ bool is_signed, Scalar &scalar,
+ Status &error) {
+ uint64_t uval = 0;
+ if (byte_size == 0) {
+ error.SetErrorString("byte size is zero");
+ } else if (byte_size & (byte_size - 1)) {
+ error.SetErrorStringWithFormat("byte size %u is not a power of 2",
+ byte_size);
+ } else if (byte_size <= sizeof(uval)) {
+ const size_t bytes_read = ReadMemory(addr, &uval, byte_size, error);
+ if (bytes_read == byte_size) {
+ DataExtractor data(&uval, sizeof(uval), GetByteOrder(),
+ GetAddressByteSize());
+ lldb::offset_t offset = 0;
+ if (byte_size <= 4)
+ scalar = data.GetMaxU32(&offset, byte_size);
+ else
+ scalar = data.GetMaxU64(&offset, byte_size);
+ if (is_signed)
+ scalar.SignExtend(byte_size * 8);
+ return bytes_read;
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "byte size of %u is too large for integer scalar type", byte_size);
+ }
+ return 0;
+}
+
+Status Process::WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) {
+ Status error;
+ for (const auto &Entry : entries) {
+ WriteMemory(Entry.Dest, Entry.Contents.data(), Entry.Contents.size(),
+ error);
+ if (!error.Success())
+ break;
+ }
+ return error;
+}
+
+#define USE_ALLOCATE_MEMORY_CACHE 1
+addr_t Process::AllocateMemory(size_t size, uint32_t permissions,
+ Status &error) {
+ if (GetPrivateState() != eStateStopped) {
+ error.SetErrorToGenericError();
+ return LLDB_INVALID_ADDRESS;
+ }
+
+#if defined(USE_ALLOCATE_MEMORY_CACHE)
+ return m_allocated_memory_cache.AllocateMemory(size, permissions, error);
+#else
+ addr_t allocated_addr = DoAllocateMemory(size, permissions, error);
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::AllocateMemory(size=%" PRIu64
+ ", permissions=%s) => 0x%16.16" PRIx64
+ " (m_stop_id = %u m_memory_id = %u)",
+ (uint64_t)size, GetPermissionsAsCString(permissions),
+ (uint64_t)allocated_addr, m_mod_id.GetStopID(),
+ m_mod_id.GetMemoryID());
+ return allocated_addr;
+#endif
+}
+
+addr_t Process::CallocateMemory(size_t size, uint32_t permissions,
+ Status &error) {
+ addr_t return_addr = AllocateMemory(size, permissions, error);
+ if (error.Success()) {
+ std::string buffer(size, 0);
+ WriteMemory(return_addr, buffer.c_str(), size, error);
+ }
+ return return_addr;
+}
+
+bool Process::CanJIT() {
+ if (m_can_jit == eCanJITDontKnow) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ Status err;
+
+ uint64_t allocated_memory = AllocateMemory(
+ 8, ePermissionsReadable | ePermissionsWritable | ePermissionsExecutable,
+ err);
+
+ if (err.Success()) {
+ m_can_jit = eCanJITYes;
+ if (log)
+ log->Printf("Process::%s pid %" PRIu64
+ " allocation test passed, CanJIT () is true",
+ __FUNCTION__, GetID());
+ } else {
+ m_can_jit = eCanJITNo;
+ if (log)
+ log->Printf("Process::%s pid %" PRIu64
+ " allocation test failed, CanJIT () is false: %s",
+ __FUNCTION__, GetID(), err.AsCString());
+ }
+
+ DeallocateMemory(allocated_memory);
+ }
+
+ return m_can_jit == eCanJITYes;
+}
+
+void Process::SetCanJIT(bool can_jit) {
+ m_can_jit = (can_jit ? eCanJITYes : eCanJITNo);
+}
+
+void Process::SetCanRunCode(bool can_run_code) {
+ SetCanJIT(can_run_code);
+ m_can_interpret_function_calls = can_run_code;
+}
+
+Status Process::DeallocateMemory(addr_t ptr) {
+ Status error;
+#if defined(USE_ALLOCATE_MEMORY_CACHE)
+ if (!m_allocated_memory_cache.DeallocateMemory(ptr)) {
+ error.SetErrorStringWithFormat(
+ "deallocation of memory at 0x%" PRIx64 " failed.", (uint64_t)ptr);
+ }
+#else
+ error = DoDeallocateMemory(ptr);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::DeallocateMemory(addr=0x%16.16" PRIx64
+ ") => err = %s (m_stop_id = %u, m_memory_id = %u)",
+ ptr, error.AsCString("SUCCESS"), m_mod_id.GetStopID(),
+ m_mod_id.GetMemoryID());
+#endif
+ return error;
+}
+
+ModuleSP Process::ReadModuleFromMemory(const FileSpec &file_spec,
+ lldb::addr_t header_addr,
+ size_t size_to_read) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ if (log) {
+ log->Printf("Process::ReadModuleFromMemory reading %s binary from memory",
+ file_spec.GetPath().c_str());
+ }
+ ModuleSP module_sp(new Module(file_spec, ArchSpec()));
+ if (module_sp) {
+ Status error;
+ ObjectFile *objfile = module_sp->GetMemoryObjectFile(
+ shared_from_this(), header_addr, error, size_to_read);
+ if (objfile)
+ return module_sp;
+ }
+ return ModuleSP();
+}
+
+bool Process::GetLoadAddressPermissions(lldb::addr_t load_addr,
+ uint32_t &permissions) {
+ MemoryRegionInfo range_info;
+ permissions = 0;
+ Status error(GetMemoryRegionInfo(load_addr, range_info));
+ if (!error.Success())
+ return false;
+ if (range_info.GetReadable() == MemoryRegionInfo::eDontKnow ||
+ range_info.GetWritable() == MemoryRegionInfo::eDontKnow ||
+ range_info.GetExecutable() == MemoryRegionInfo::eDontKnow) {
+ return false;
+ }
+
+ if (range_info.GetReadable() == MemoryRegionInfo::eYes)
+ permissions |= lldb::ePermissionsReadable;
+
+ if (range_info.GetWritable() == MemoryRegionInfo::eYes)
+ permissions |= lldb::ePermissionsWritable;
+
+ if (range_info.GetExecutable() == MemoryRegionInfo::eYes)
+ permissions |= lldb::ePermissionsExecutable;
+
+ return true;
+}
+
+Status Process::EnableWatchpoint(Watchpoint *watchpoint, bool notify) {
+ Status error;
+ error.SetErrorString("watchpoints are not supported");
+ return error;
+}
+
+Status Process::DisableWatchpoint(Watchpoint *watchpoint, bool notify) {
+ Status error;
+ error.SetErrorString("watchpoints are not supported");
+ return error;
+}
+
+StateType
+Process::WaitForProcessStopPrivate(EventSP &event_sp,
+ const Timeout<std::micro> &timeout) {
+ StateType state;
+
+ while (true) {
+ event_sp.reset();
+ state = GetStateChangedEventsPrivate(event_sp, timeout);
+
+ if (StateIsStoppedState(state, false))
+ break;
+
+ // If state is invalid, then we timed out
+ if (state == eStateInvalid)
+ break;
+
+ if (event_sp)
+ HandlePrivateEvent(event_sp);
+ }
+ return state;
+}
+
+void Process::LoadOperatingSystemPlugin(bool flush) {
+ if (flush)
+ m_thread_list.Clear();
+ m_os_up.reset(OperatingSystem::FindPlugin(this, nullptr));
+ if (flush)
+ Flush();
+}
+
+Status Process::Launch(ProcessLaunchInfo &launch_info) {
+ Status error;
+ m_abi_sp.reset();
+ m_dyld_up.reset();
+ m_jit_loaders_up.reset();
+ m_system_runtime_up.reset();
+ m_os_up.reset();
+ m_process_input_reader.reset();
+
+ Module *exe_module = GetTarget().GetExecutableModulePointer();
+ if (!exe_module) {
+ error.SetErrorString("executable module does not exist");
+ return error;
+ }
+
+ char local_exec_file_path[PATH_MAX];
+ char platform_exec_file_path[PATH_MAX];
+ exe_module->GetFileSpec().GetPath(local_exec_file_path,
+ sizeof(local_exec_file_path));
+ exe_module->GetPlatformFileSpec().GetPath(platform_exec_file_path,
+ sizeof(platform_exec_file_path));
+ if (FileSystem::Instance().Exists(exe_module->GetFileSpec())) {
+ // Install anything that might need to be installed prior to launching.
+ // For host systems, this will do nothing, but if we are connected to a
+ // remote platform it will install any needed binaries
+ error = GetTarget().Install(&launch_info);
+ if (error.Fail())
+ return error;
+
+ if (PrivateStateThreadIsValid())
+ PausePrivateStateThread();
+
+ error = WillLaunch(exe_module);
+ if (error.Success()) {
+ const bool restarted = false;
+ SetPublicState(eStateLaunching, restarted);
+ m_should_detach = false;
+
+ if (m_public_run_lock.TrySetRunning()) {
+ // Now launch using these arguments.
+ error = DoLaunch(exe_module, launch_info);
+ } else {
+ // This shouldn't happen
+ error.SetErrorString("failed to acquire process run lock");
+ }
+
+ if (error.Fail()) {
+ if (GetID() != LLDB_INVALID_PROCESS_ID) {
+ SetID(LLDB_INVALID_PROCESS_ID);
+ const char *error_string = error.AsCString();
+ if (error_string == nullptr)
+ error_string = "launch failed";
+ SetExitStatus(-1, error_string);
+ }
+ } else {
+ EventSP event_sp;
+
+ // Now wait for the process to launch and return control to us, and then
+ // call DidLaunch:
+ StateType state = WaitForProcessStopPrivate(event_sp, seconds(10));
+
+ if (state == eStateInvalid || !event_sp) {
+ // We were able to launch the process, but we failed to catch the
+ // initial stop.
+ error.SetErrorString("failed to catch stop after launch");
+ SetExitStatus(0, "failed to catch stop after launch");
+ Destroy(false);
+ } else if (state == eStateStopped || state == eStateCrashed) {
+ DidLaunch();
+
+ DynamicLoader *dyld = GetDynamicLoader();
+ if (dyld)
+ dyld->DidLaunch();
+
+ GetJITLoaders().DidLaunch();
+
+ SystemRuntime *system_runtime = GetSystemRuntime();
+ if (system_runtime)
+ system_runtime->DidLaunch();
+
+ if (!m_os_up)
+ LoadOperatingSystemPlugin(false);
+
+ // We successfully launched the process and stopped, now it the
+ // right time to set up signal filters before resuming.
+ UpdateAutomaticSignalFiltering();
+
+ // Note, the stop event was consumed above, but not handled. This
+ // was done to give DidLaunch a chance to run. The target is either
+ // stopped or crashed. Directly set the state. This is done to
+ // prevent a stop message with a bunch of spurious output on thread
+ // status, as well as not pop a ProcessIOHandler.
+ SetPublicState(state, false);
+
+ if (PrivateStateThreadIsValid())
+ ResumePrivateStateThread();
+ else
+ StartPrivateStateThread();
+
+ // Target was stopped at entry as was intended. Need to notify the
+ // listeners about it.
+ if (state == eStateStopped &&
+ launch_info.GetFlags().Test(eLaunchFlagStopAtEntry))
+ HandlePrivateEvent(event_sp);
+ } else if (state == eStateExited) {
+ // We exited while trying to launch somehow. Don't call DidLaunch
+ // as that's not likely to work, and return an invalid pid.
+ HandlePrivateEvent(event_sp);
+ }
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat("file doesn't exist: '%s'",
+ local_exec_file_path);
+ }
+
+ return error;
+}
+
+Status Process::LoadCore() {
+ Status error = DoLoadCore();
+ if (error.Success()) {
+ ListenerSP listener_sp(
+ Listener::MakeListener("lldb.process.load_core_listener"));
+ HijackProcessEvents(listener_sp);
+
+ if (PrivateStateThreadIsValid())
+ ResumePrivateStateThread();
+ else
+ StartPrivateStateThread();
+
+ DynamicLoader *dyld = GetDynamicLoader();
+ if (dyld)
+ dyld->DidAttach();
+
+ GetJITLoaders().DidAttach();
+
+ SystemRuntime *system_runtime = GetSystemRuntime();
+ if (system_runtime)
+ system_runtime->DidAttach();
+
+ if (!m_os_up)
+ LoadOperatingSystemPlugin(false);
+
+ // We successfully loaded a core file, now pretend we stopped so we can
+ // show all of the threads in the core file and explore the crashed state.
+ SetPrivateState(eStateStopped);
+
+ // Wait for a stopped event since we just posted one above...
+ lldb::EventSP event_sp;
+ StateType state =
+ WaitForProcessToStop(llvm::None, &event_sp, true, listener_sp);
+
+ if (!StateIsStoppedState(state, false)) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::Halt() failed to stop, state is: %s",
+ StateAsCString(state));
+ error.SetErrorString(
+ "Did not get stopped event after loading the core file.");
+ }
+ RestoreProcessEvents();
+ }
+ return error;
+}
+
+DynamicLoader *Process::GetDynamicLoader() {
+ if (!m_dyld_up)
+ m_dyld_up.reset(DynamicLoader::FindPlugin(this, nullptr));
+ return m_dyld_up.get();
+}
+
+DataExtractor Process::GetAuxvData() { return DataExtractor(); }
+
+JITLoaderList &Process::GetJITLoaders() {
+ if (!m_jit_loaders_up) {
+ m_jit_loaders_up.reset(new JITLoaderList());
+ JITLoader::LoadPlugins(this, *m_jit_loaders_up);
+ }
+ return *m_jit_loaders_up;
+}
+
+SystemRuntime *Process::GetSystemRuntime() {
+ if (!m_system_runtime_up)
+ m_system_runtime_up.reset(SystemRuntime::FindPlugin(this));
+ return m_system_runtime_up.get();
+}
+
+Process::AttachCompletionHandler::AttachCompletionHandler(Process *process,
+ uint32_t exec_count)
+ : NextEventAction(process), m_exec_count(exec_count) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf(
+ "Process::AttachCompletionHandler::%s process=%p, exec_count=%" PRIu32,
+ __FUNCTION__, static_cast<void *>(process), exec_count);
+}
+
+Process::NextEventAction::EventActionResult
+Process::AttachCompletionHandler::PerformAction(lldb::EventSP &event_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ StateType state = ProcessEventData::GetStateFromEvent(event_sp.get());
+ if (log)
+ log->Printf(
+ "Process::AttachCompletionHandler::%s called with state %s (%d)",
+ __FUNCTION__, StateAsCString(state), static_cast<int>(state));
+
+ switch (state) {
+ case eStateAttaching:
+ return eEventActionSuccess;
+
+ case eStateRunning:
+ case eStateConnected:
+ return eEventActionRetry;
+
+ case eStateStopped:
+ case eStateCrashed:
+ // During attach, prior to sending the eStateStopped event,
+ // lldb_private::Process subclasses must set the new process ID.
+ assert(m_process->GetID() != LLDB_INVALID_PROCESS_ID);
+ // We don't want these events to be reported, so go set the
+ // ShouldReportStop here:
+ m_process->GetThreadList().SetShouldReportStop(eVoteNo);
+
+ if (m_exec_count > 0) {
+ --m_exec_count;
+
+ if (log)
+ log->Printf("Process::AttachCompletionHandler::%s state %s: reduced "
+ "remaining exec count to %" PRIu32 ", requesting resume",
+ __FUNCTION__, StateAsCString(state), m_exec_count);
+
+ RequestResume();
+ return eEventActionRetry;
+ } else {
+ if (log)
+ log->Printf("Process::AttachCompletionHandler::%s state %s: no more "
+ "execs expected to start, continuing with attach",
+ __FUNCTION__, StateAsCString(state));
+
+ m_process->CompleteAttach();
+ return eEventActionSuccess;
+ }
+ break;
+
+ default:
+ case eStateExited:
+ case eStateInvalid:
+ break;
+ }
+
+ m_exit_string.assign("No valid Process");
+ return eEventActionExit;
+}
+
+Process::NextEventAction::EventActionResult
+Process::AttachCompletionHandler::HandleBeingInterrupted() {
+ return eEventActionSuccess;
+}
+
+const char *Process::AttachCompletionHandler::GetExitString() {
+ return m_exit_string.c_str();
+}
+
+ListenerSP ProcessAttachInfo::GetListenerForProcess(Debugger &debugger) {
+ if (m_listener_sp)
+ return m_listener_sp;
+ else
+ return debugger.GetListener();
+}
+
+Status Process::Attach(ProcessAttachInfo &attach_info) {
+ m_abi_sp.reset();
+ m_process_input_reader.reset();
+ m_dyld_up.reset();
+ m_jit_loaders_up.reset();
+ m_system_runtime_up.reset();
+ m_os_up.reset();
+
+ lldb::pid_t attach_pid = attach_info.GetProcessID();
+ Status error;
+ if (attach_pid == LLDB_INVALID_PROCESS_ID) {
+ char process_name[PATH_MAX];
+
+ if (attach_info.GetExecutableFile().GetPath(process_name,
+ sizeof(process_name))) {
+ const bool wait_for_launch = attach_info.GetWaitForLaunch();
+
+ if (wait_for_launch) {
+ error = WillAttachToProcessWithName(process_name, wait_for_launch);
+ if (error.Success()) {
+ if (m_public_run_lock.TrySetRunning()) {
+ m_should_detach = true;
+ const bool restarted = false;
+ SetPublicState(eStateAttaching, restarted);
+ // Now attach using these arguments.
+ error = DoAttachToProcessWithName(process_name, attach_info);
+ } else {
+ // This shouldn't happen
+ error.SetErrorString("failed to acquire process run lock");
+ }
+
+ if (error.Fail()) {
+ if (GetID() != LLDB_INVALID_PROCESS_ID) {
+ SetID(LLDB_INVALID_PROCESS_ID);
+ if (error.AsCString() == nullptr)
+ error.SetErrorString("attach failed");
+
+ SetExitStatus(-1, error.AsCString());
+ }
+ } else {
+ SetNextEventAction(new Process::AttachCompletionHandler(
+ this, attach_info.GetResumeCount()));
+ StartPrivateStateThread();
+ }
+ return error;
+ }
+ } else {
+ ProcessInstanceInfoList process_infos;
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+
+ if (platform_sp) {
+ ProcessInstanceInfoMatch match_info;
+ match_info.GetProcessInfo() = attach_info;
+ match_info.SetNameMatchType(NameMatch::Equals);
+ platform_sp->FindProcesses(match_info, process_infos);
+ const uint32_t num_matches = process_infos.GetSize();
+ if (num_matches == 1) {
+ attach_pid = process_infos.GetProcessIDAtIndex(0);
+ // Fall through and attach using the above process ID
+ } else {
+ match_info.GetProcessInfo().GetExecutableFile().GetPath(
+ process_name, sizeof(process_name));
+ if (num_matches > 1) {
+ StreamString s;
+ ProcessInstanceInfo::DumpTableHeader(s, true, false);
+ for (size_t i = 0; i < num_matches; i++) {
+ process_infos.GetProcessInfoAtIndex(i).DumpAsTableRow(
+ s, platform_sp->GetUserIDResolver(), true, false);
+ }
+ error.SetErrorStringWithFormat(
+ "more than one process named %s:\n%s", process_name,
+ s.GetData());
+ } else
+ error.SetErrorStringWithFormat(
+ "could not find a process named %s", process_name);
+ }
+ } else {
+ error.SetErrorString(
+ "invalid platform, can't find processes by name");
+ return error;
+ }
+ }
+ } else {
+ error.SetErrorString("invalid process name");
+ }
+ }
+
+ if (attach_pid != LLDB_INVALID_PROCESS_ID) {
+ error = WillAttachToProcessWithID(attach_pid);
+ if (error.Success()) {
+
+ if (m_public_run_lock.TrySetRunning()) {
+ // Now attach using these arguments.
+ m_should_detach = true;
+ const bool restarted = false;
+ SetPublicState(eStateAttaching, restarted);
+ error = DoAttachToProcessWithID(attach_pid, attach_info);
+ } else {
+ // This shouldn't happen
+ error.SetErrorString("failed to acquire process run lock");
+ }
+
+ if (error.Success()) {
+ SetNextEventAction(new Process::AttachCompletionHandler(
+ this, attach_info.GetResumeCount()));
+ StartPrivateStateThread();
+ } else {
+ if (GetID() != LLDB_INVALID_PROCESS_ID)
+ SetID(LLDB_INVALID_PROCESS_ID);
+
+ const char *error_string = error.AsCString();
+ if (error_string == nullptr)
+ error_string = "attach failed";
+
+ SetExitStatus(-1, error_string);
+ }
+ }
+ }
+ return error;
+}
+
+void Process::CompleteAttach() {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS |
+ LIBLLDB_LOG_TARGET));
+ if (log)
+ log->Printf("Process::%s()", __FUNCTION__);
+
+ // Let the process subclass figure out at much as it can about the process
+ // before we go looking for a dynamic loader plug-in.
+ ArchSpec process_arch;
+ DidAttach(process_arch);
+
+ if (process_arch.IsValid()) {
+ GetTarget().SetArchitecture(process_arch);
+ if (log) {
+ const char *triple_str = process_arch.GetTriple().getTriple().c_str();
+ log->Printf("Process::%s replacing process architecture with DidAttach() "
+ "architecture: %s",
+ __FUNCTION__, triple_str ? triple_str : "<null>");
+ }
+ }
+
+ // We just attached. If we have a platform, ask it for the process
+ // architecture, and if it isn't the same as the one we've already set,
+ // switch architectures.
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ assert(platform_sp);
+ if (platform_sp) {
+ const ArchSpec &target_arch = GetTarget().GetArchitecture();
+ if (target_arch.IsValid() &&
+ !platform_sp->IsCompatibleArchitecture(target_arch, false, nullptr)) {
+ ArchSpec platform_arch;
+ platform_sp =
+ platform_sp->GetPlatformForArchitecture(target_arch, &platform_arch);
+ if (platform_sp) {
+ GetTarget().SetPlatform(platform_sp);
+ GetTarget().SetArchitecture(platform_arch);
+ if (log)
+ log->Printf("Process::%s switching platform to %s and architecture "
+ "to %s based on info from attach",
+ __FUNCTION__, platform_sp->GetName().AsCString(""),
+ platform_arch.GetTriple().getTriple().c_str());
+ }
+ } else if (!process_arch.IsValid()) {
+ ProcessInstanceInfo process_info;
+ GetProcessInfo(process_info);
+ const ArchSpec &process_arch = process_info.GetArchitecture();
+ if (process_arch.IsValid() &&
+ !GetTarget().GetArchitecture().IsExactMatch(process_arch)) {
+ GetTarget().SetArchitecture(process_arch);
+ if (log)
+ log->Printf("Process::%s switching architecture to %s based on info "
+ "the platform retrieved for pid %" PRIu64,
+ __FUNCTION__,
+ process_arch.GetTriple().getTriple().c_str(), GetID());
+ }
+ }
+ }
+
+ // We have completed the attach, now it is time to find the dynamic loader
+ // plug-in
+ DynamicLoader *dyld = GetDynamicLoader();
+ if (dyld) {
+ dyld->DidAttach();
+ if (log) {
+ ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
+ log->Printf("Process::%s after DynamicLoader::DidAttach(), target "
+ "executable is %s (using %s plugin)",
+ __FUNCTION__,
+ exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()
+ : "<none>",
+ dyld->GetPluginName().AsCString("<unnamed>"));
+ }
+ }
+
+ GetJITLoaders().DidAttach();
+
+ SystemRuntime *system_runtime = GetSystemRuntime();
+ if (system_runtime) {
+ system_runtime->DidAttach();
+ if (log) {
+ ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
+ log->Printf("Process::%s after SystemRuntime::DidAttach(), target "
+ "executable is %s (using %s plugin)",
+ __FUNCTION__,
+ exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()
+ : "<none>",
+ system_runtime->GetPluginName().AsCString("<unnamed>"));
+ }
+ }
+
+ if (!m_os_up) {
+ LoadOperatingSystemPlugin(false);
+ if (m_os_up) {
+ // Somebody might have gotten threads before now, but we need to force the
+ // update after we've loaded the OperatingSystem plugin or it won't get a
+ // chance to process the threads.
+ m_thread_list.Clear();
+ UpdateThreadListIfNeeded();
+ }
+ }
+ // Figure out which one is the executable, and set that in our target:
+ const ModuleList &target_modules = GetTarget().GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ size_t num_modules = target_modules.GetSize();
+ ModuleSP new_executable_module_sp;
+
+ for (size_t i = 0; i < num_modules; i++) {
+ ModuleSP module_sp(target_modules.GetModuleAtIndexUnlocked(i));
+ if (module_sp && module_sp->IsExecutable()) {
+ if (GetTarget().GetExecutableModulePointer() != module_sp.get())
+ new_executable_module_sp = module_sp;
+ break;
+ }
+ }
+ if (new_executable_module_sp) {
+ GetTarget().SetExecutableModule(new_executable_module_sp,
+ eLoadDependentsNo);
+ if (log) {
+ ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
+ log->Printf(
+ "Process::%s after looping through modules, target executable is %s",
+ __FUNCTION__,
+ exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()
+ : "<none>");
+ }
+ }
+}
+
+Status Process::ConnectRemote(Stream *strm, llvm::StringRef remote_url) {
+ m_abi_sp.reset();
+ m_process_input_reader.reset();
+
+ // Find the process and its architecture. Make sure it matches the
+ // architecture of the current Target, and if not adjust it.
+
+ Status error(DoConnectRemote(strm, remote_url));
+ if (error.Success()) {
+ if (GetID() != LLDB_INVALID_PROCESS_ID) {
+ EventSP event_sp;
+ StateType state = WaitForProcessStopPrivate(event_sp, llvm::None);
+
+ if (state == eStateStopped || state == eStateCrashed) {
+ // If we attached and actually have a process on the other end, then
+ // this ended up being the equivalent of an attach.
+ CompleteAttach();
+
+ // This delays passing the stopped event to listeners till
+ // CompleteAttach gets a chance to complete...
+ HandlePrivateEvent(event_sp);
+ }
+ }
+
+ if (PrivateStateThreadIsValid())
+ ResumePrivateStateThread();
+ else
+ StartPrivateStateThread();
+ }
+ return error;
+}
+
+Status Process::PrivateResume() {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS |
+ LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Process::PrivateResume() m_stop_id = %u, public state: %s "
+ "private state: %s",
+ m_mod_id.GetStopID(), StateAsCString(m_public_state.GetValue()),
+ StateAsCString(m_private_state.GetValue()));
+
+ // If signals handing status changed we might want to update our signal
+ // filters before resuming.
+ UpdateAutomaticSignalFiltering();
+
+ Status error(WillResume());
+ // Tell the process it is about to resume before the thread list
+ if (error.Success()) {
+ // Now let the thread list know we are about to resume so it can let all of
+ // our threads know that they are about to be resumed. Threads will each be
+ // called with Thread::WillResume(StateType) where StateType contains the
+ // state that they are supposed to have when the process is resumed
+ // (suspended/running/stepping). Threads should also check their resume
+ // signal in lldb::Thread::GetResumeSignal() to see if they are supposed to
+ // start back up with a signal.
+ if (m_thread_list.WillResume()) {
+ // Last thing, do the PreResumeActions.
+ if (!RunPreResumeActions()) {
+ error.SetErrorStringWithFormat(
+ "Process::PrivateResume PreResumeActions failed, not resuming.");
+ } else {
+ m_mod_id.BumpResumeID();
+ error = DoResume();
+ if (error.Success()) {
+ DidResume();
+ m_thread_list.DidResume();
+ if (log)
+ log->Printf("Process thinks the process has resumed.");
+ } else {
+ if (log)
+ log->Printf(
+ "Process::PrivateResume() DoResume failed.");
+ return error;
+ }
+ }
+ } else {
+ // Somebody wanted to run without running (e.g. we were faking a step
+ // from one frame of a set of inlined frames that share the same PC to
+ // another.) So generate a continue & a stopped event, and let the world
+ // handle them.
+ if (log)
+ log->Printf(
+ "Process::PrivateResume() asked to simulate a start & stop.");
+
+ SetPrivateState(eStateRunning);
+ SetPrivateState(eStateStopped);
+ }
+ } else if (log)
+ log->Printf("Process::PrivateResume() got an error \"%s\".",
+ error.AsCString("<unknown error>"));
+ return error;
+}
+
+Status Process::Halt(bool clear_thread_plans, bool use_run_lock) {
+ if (!StateIsRunningState(m_public_state.GetValue()))
+ return Status("Process is not running.");
+
+ // Don't clear the m_clear_thread_plans_on_stop, only set it to true if in
+ // case it was already set and some thread plan logic calls halt on its own.
+ m_clear_thread_plans_on_stop |= clear_thread_plans;
+
+ ListenerSP halt_listener_sp(
+ Listener::MakeListener("lldb.process.halt_listener"));
+ HijackProcessEvents(halt_listener_sp);
+
+ EventSP event_sp;
+
+ SendAsyncInterrupt();
+
+ if (m_public_state.GetValue() == eStateAttaching) {
+ // Don't hijack and eat the eStateExited as the code that was doing the
+ // attach will be waiting for this event...
+ RestoreProcessEvents();
+ SetExitStatus(SIGKILL, "Cancelled async attach.");
+ Destroy(false);
+ return Status();
+ }
+
+ // Wait for 10 second for the process to stop.
+ StateType state = WaitForProcessToStop(
+ seconds(10), &event_sp, true, halt_listener_sp, nullptr, use_run_lock);
+ RestoreProcessEvents();
+
+ if (state == eStateInvalid || !event_sp) {
+ // We timed out and didn't get a stop event...
+ return Status("Halt timed out. State = %s", StateAsCString(GetState()));
+ }
+
+ BroadcastEvent(event_sp);
+
+ return Status();
+}
+
+Status Process::StopForDestroyOrDetach(lldb::EventSP &exit_event_sp) {
+ Status error;
+
+ // Check both the public & private states here. If we're hung evaluating an
+ // expression, for instance, then the public state will be stopped, but we
+ // still need to interrupt.
+ if (m_public_state.GetValue() == eStateRunning ||
+ m_private_state.GetValue() == eStateRunning) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::%s() About to stop.", __FUNCTION__);
+
+ ListenerSP listener_sp(
+ Listener::MakeListener("lldb.Process.StopForDestroyOrDetach.hijack"));
+ HijackProcessEvents(listener_sp);
+
+ SendAsyncInterrupt();
+
+ // Consume the interrupt event.
+ StateType state =
+ WaitForProcessToStop(seconds(10), &exit_event_sp, true, listener_sp);
+
+ RestoreProcessEvents();
+
+ // If the process exited while we were waiting for it to stop, put the
+ // exited event into the shared pointer passed in and return. Our caller
+ // doesn't need to do anything else, since they don't have a process
+ // anymore...
+
+ if (state == eStateExited || m_private_state.GetValue() == eStateExited) {
+ if (log)
+ log->Printf("Process::%s() Process exited while waiting to stop.",
+ __FUNCTION__);
+ return error;
+ } else
+ exit_event_sp.reset(); // It is ok to consume any non-exit stop events
+
+ if (state != eStateStopped) {
+ if (log)
+ log->Printf("Process::%s() failed to stop, state is: %s", __FUNCTION__,
+ StateAsCString(state));
+ // If we really couldn't stop the process then we should just error out
+ // here, but if the lower levels just bobbled sending the event and we
+ // really are stopped, then continue on.
+ StateType private_state = m_private_state.GetValue();
+ if (private_state != eStateStopped) {
+ return Status(
+ "Attempt to stop the target in order to detach timed out. "
+ "State = %s",
+ StateAsCString(GetState()));
+ }
+ }
+ }
+ return error;
+}
+
+Status Process::Detach(bool keep_stopped) {
+ EventSP exit_event_sp;
+ Status error;
+ m_destroy_in_process = true;
+
+ error = WillDetach();
+
+ if (error.Success()) {
+ if (DetachRequiresHalt()) {
+ error = StopForDestroyOrDetach(exit_event_sp);
+ if (!error.Success()) {
+ m_destroy_in_process = false;
+ return error;
+ } else if (exit_event_sp) {
+ // We shouldn't need to do anything else here. There's no process left
+ // to detach from...
+ StopPrivateStateThread();
+ m_destroy_in_process = false;
+ return error;
+ }
+ }
+
+ m_thread_list.DiscardThreadPlans();
+ DisableAllBreakpointSites();
+
+ error = DoDetach(keep_stopped);
+ if (error.Success()) {
+ DidDetach();
+ StopPrivateStateThread();
+ } else {
+ return error;
+ }
+ }
+ m_destroy_in_process = false;
+
+ // If we exited when we were waiting for a process to stop, then forward the
+ // event here so we don't lose the event
+ if (exit_event_sp) {
+ // Directly broadcast our exited event because we shut down our private
+ // state thread above
+ BroadcastEvent(exit_event_sp);
+ }
+
+ // If we have been interrupted (to kill us) in the middle of running, we may
+ // not end up propagating the last events through the event system, in which
+ // case we might strand the write lock. Unlock it here so when we do to tear
+ // down the process we don't get an error destroying the lock.
+
+ m_public_run_lock.SetStopped();
+ return error;
+}
+
+Status Process::Destroy(bool force_kill) {
+
+ // Tell ourselves we are in the process of destroying the process, so that we
+ // don't do any unnecessary work that might hinder the destruction. Remember
+ // to set this back to false when we are done. That way if the attempt
+ // failed and the process stays around for some reason it won't be in a
+ // confused state.
+
+ if (force_kill)
+ m_should_detach = false;
+
+ if (GetShouldDetach()) {
+ // FIXME: This will have to be a process setting:
+ bool keep_stopped = false;
+ Detach(keep_stopped);
+ }
+
+ m_destroy_in_process = true;
+
+ Status error(WillDestroy());
+ if (error.Success()) {
+ EventSP exit_event_sp;
+ if (DestroyRequiresHalt()) {
+ error = StopForDestroyOrDetach(exit_event_sp);
+ }
+
+ if (m_public_state.GetValue() != eStateRunning) {
+ // Ditch all thread plans, and remove all our breakpoints: in case we
+ // have to restart the target to kill it, we don't want it hitting a
+ // breakpoint... Only do this if we've stopped, however, since if we
+ // didn't manage to halt it above, then we're not going to have much luck
+ // doing this now.
+ m_thread_list.DiscardThreadPlans();
+ DisableAllBreakpointSites();
+ }
+
+ error = DoDestroy();
+ if (error.Success()) {
+ DidDestroy();
+ StopPrivateStateThread();
+ }
+ m_stdio_communication.Disconnect();
+ m_stdio_communication.StopReadThread();
+ m_stdin_forward = false;
+
+ if (m_process_input_reader) {
+ m_process_input_reader->SetIsDone(true);
+ m_process_input_reader->Cancel();
+ m_process_input_reader.reset();
+ }
+
+ // If we exited when we were waiting for a process to stop, then forward
+ // the event here so we don't lose the event
+ if (exit_event_sp) {
+ // Directly broadcast our exited event because we shut down our private
+ // state thread above
+ BroadcastEvent(exit_event_sp);
+ }
+
+ // If we have been interrupted (to kill us) in the middle of running, we
+ // may not end up propagating the last events through the event system, in
+ // which case we might strand the write lock. Unlock it here so when we do
+ // to tear down the process we don't get an error destroying the lock.
+ m_public_run_lock.SetStopped();
+ }
+
+ m_destroy_in_process = false;
+
+ return error;
+}
+
+Status Process::Signal(int signal) {
+ Status error(WillSignal());
+ if (error.Success()) {
+ error = DoSignal(signal);
+ if (error.Success())
+ DidSignal();
+ }
+ return error;
+}
+
+void Process::SetUnixSignals(UnixSignalsSP &&signals_sp) {
+ assert(signals_sp && "null signals_sp");
+ m_unix_signals_sp = signals_sp;
+}
+
+const lldb::UnixSignalsSP &Process::GetUnixSignals() {
+ assert(m_unix_signals_sp && "null m_unix_signals_sp");
+ return m_unix_signals_sp;
+}
+
+lldb::ByteOrder Process::GetByteOrder() const {
+ return GetTarget().GetArchitecture().GetByteOrder();
+}
+
+uint32_t Process::GetAddressByteSize() const {
+ return GetTarget().GetArchitecture().GetAddressByteSize();
+}
+
+bool Process::ShouldBroadcastEvent(Event *event_ptr) {
+ const StateType state =
+ Process::ProcessEventData::GetStateFromEvent(event_ptr);
+ bool return_value = true;
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EVENTS |
+ LIBLLDB_LOG_PROCESS));
+
+ switch (state) {
+ case eStateDetached:
+ case eStateExited:
+ case eStateUnloaded:
+ m_stdio_communication.SynchronizeWithReadThread();
+ m_stdio_communication.Disconnect();
+ m_stdio_communication.StopReadThread();
+ m_stdin_forward = false;
+
+ LLVM_FALLTHROUGH;
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ // These events indicate changes in the state of the debugging session,
+ // always report them.
+ return_value = true;
+ break;
+ case eStateInvalid:
+ // We stopped for no apparent reason, don't report it.
+ return_value = false;
+ break;
+ case eStateRunning:
+ case eStateStepping:
+ // If we've started the target running, we handle the cases where we are
+ // already running and where there is a transition from stopped to running
+ // differently. running -> running: Automatically suppress extra running
+ // events stopped -> running: Report except when there is one or more no
+ // votes
+ // and no yes votes.
+ SynchronouslyNotifyStateChanged(state);
+ if (m_force_next_event_delivery)
+ return_value = true;
+ else {
+ switch (m_last_broadcast_state) {
+ case eStateRunning:
+ case eStateStepping:
+ // We always suppress multiple runnings with no PUBLIC stop in between.
+ return_value = false;
+ break;
+ default:
+ // TODO: make this work correctly. For now always report
+ // run if we aren't running so we don't miss any running events. If I
+ // run the lldb/test/thread/a.out file and break at main.cpp:58, run
+ // and hit the breakpoints on multiple threads, then somehow during the
+ // stepping over of all breakpoints no run gets reported.
+
+ // This is a transition from stop to run.
+ switch (m_thread_list.ShouldReportRun(event_ptr)) {
+ case eVoteYes:
+ case eVoteNoOpinion:
+ return_value = true;
+ break;
+ case eVoteNo:
+ return_value = false;
+ break;
+ }
+ break;
+ }
+ }
+ break;
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ // We've stopped. First see if we're going to restart the target. If we
+ // are going to stop, then we always broadcast the event. If we aren't
+ // going to stop, let the thread plans decide if we're going to report this
+ // event. If no thread has an opinion, we don't report it.
+
+ m_stdio_communication.SynchronizeWithReadThread();
+ RefreshStateAfterStop();
+ if (ProcessEventData::GetInterruptedFromEvent(event_ptr)) {
+ if (log)
+ log->Printf("Process::ShouldBroadcastEvent (%p) stopped due to an "
+ "interrupt, state: %s",
+ static_cast<void *>(event_ptr), StateAsCString(state));
+ // Even though we know we are going to stop, we should let the threads
+ // have a look at the stop, so they can properly set their state.
+ m_thread_list.ShouldStop(event_ptr);
+ return_value = true;
+ } else {
+ bool was_restarted = ProcessEventData::GetRestartedFromEvent(event_ptr);
+ bool should_resume = false;
+
+ // It makes no sense to ask "ShouldStop" if we've already been
+ // restarted... Asking the thread list is also not likely to go well,
+ // since we are running again. So in that case just report the event.
+
+ if (!was_restarted)
+ should_resume = !m_thread_list.ShouldStop(event_ptr);
+
+ if (was_restarted || should_resume || m_resume_requested) {
+ Vote stop_vote = m_thread_list.ShouldReportStop(event_ptr);
+ if (log)
+ log->Printf("Process::ShouldBroadcastEvent: should_resume: %i state: "
+ "%s was_restarted: %i stop_vote: %d.",
+ should_resume, StateAsCString(state), was_restarted,
+ stop_vote);
+
+ switch (stop_vote) {
+ case eVoteYes:
+ return_value = true;
+ break;
+ case eVoteNoOpinion:
+ case eVoteNo:
+ return_value = false;
+ break;
+ }
+
+ if (!was_restarted) {
+ if (log)
+ log->Printf("Process::ShouldBroadcastEvent (%p) Restarting process "
+ "from state: %s",
+ static_cast<void *>(event_ptr), StateAsCString(state));
+ ProcessEventData::SetRestartedInEvent(event_ptr, true);
+ PrivateResume();
+ }
+ } else {
+ return_value = true;
+ SynchronouslyNotifyStateChanged(state);
+ }
+ }
+ break;
+ }
+
+ // Forcing the next event delivery is a one shot deal. So reset it here.
+ m_force_next_event_delivery = false;
+
+ // We do some coalescing of events (for instance two consecutive running
+ // events get coalesced.) But we only coalesce against events we actually
+ // broadcast. So we use m_last_broadcast_state to track that. NB - you
+ // can't use "m_public_state.GetValue()" for that purpose, as was originally
+ // done, because the PublicState reflects the last event pulled off the
+ // queue, and there may be several events stacked up on the queue unserviced.
+ // So the PublicState may not reflect the last broadcasted event yet.
+ // m_last_broadcast_state gets updated here.
+
+ if (return_value)
+ m_last_broadcast_state = state;
+
+ if (log)
+ log->Printf("Process::ShouldBroadcastEvent (%p) => new state: %s, last "
+ "broadcast state: %s - %s",
+ static_cast<void *>(event_ptr), StateAsCString(state),
+ StateAsCString(m_last_broadcast_state),
+ return_value ? "YES" : "NO");
+ return return_value;
+}
+
+bool Process::StartPrivateStateThread(bool is_secondary_thread) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
+
+ bool already_running = PrivateStateThreadIsValid();
+ if (log)
+ log->Printf("Process::%s()%s ", __FUNCTION__,
+ already_running ? " already running"
+ : " starting private state thread");
+
+ if (!is_secondary_thread && already_running)
+ return true;
+
+ // Create a thread that watches our internal state and controls which events
+ // make it to clients (into the DCProcess event queue).
+ char thread_name[1024];
+ uint32_t max_len = llvm::get_max_thread_name_length();
+ if (max_len > 0 && max_len <= 30) {
+ // On platforms with abbreviated thread name lengths, choose thread names
+ // that fit within the limit.
+ if (already_running)
+ snprintf(thread_name, sizeof(thread_name), "intern-state-OV");
+ else
+ snprintf(thread_name, sizeof(thread_name), "intern-state");
+ } else {
+ if (already_running)
+ snprintf(thread_name, sizeof(thread_name),
+ "<lldb.process.internal-state-override(pid=%" PRIu64 ")>",
+ GetID());
+ else
+ snprintf(thread_name, sizeof(thread_name),
+ "<lldb.process.internal-state(pid=%" PRIu64 ")>", GetID());
+ }
+
+ // Create the private state thread, and start it running.
+ PrivateStateThreadArgs *args_ptr =
+ new PrivateStateThreadArgs(this, is_secondary_thread);
+ llvm::Expected<HostThread> private_state_thread =
+ ThreadLauncher::LaunchThread(thread_name, Process::PrivateStateThread,
+ (void *)args_ptr, 8 * 1024 * 1024);
+ if (!private_state_thread) {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(private_state_thread.takeError()));
+ return false;
+ }
+
+ assert(private_state_thread->IsJoinable());
+ m_private_state_thread = *private_state_thread;
+ ResumePrivateStateThread();
+ return true;
+}
+
+void Process::PausePrivateStateThread() {
+ ControlPrivateStateThread(eBroadcastInternalStateControlPause);
+}
+
+void Process::ResumePrivateStateThread() {
+ ControlPrivateStateThread(eBroadcastInternalStateControlResume);
+}
+
+void Process::StopPrivateStateThread() {
+ if (m_private_state_thread.IsJoinable())
+ ControlPrivateStateThread(eBroadcastInternalStateControlStop);
+ else {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf(
+ "Went to stop the private state thread, but it was already invalid.");
+ }
+}
+
+void Process::ControlPrivateStateThread(uint32_t signal) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ assert(signal == eBroadcastInternalStateControlStop ||
+ signal == eBroadcastInternalStateControlPause ||
+ signal == eBroadcastInternalStateControlResume);
+
+ if (log)
+ log->Printf("Process::%s (signal = %d)", __FUNCTION__, signal);
+
+ // Signal the private state thread
+ if (m_private_state_thread.IsJoinable()) {
+ // Broadcast the event.
+ // It is important to do this outside of the if below, because it's
+ // possible that the thread state is invalid but that the thread is waiting
+ // on a control event instead of simply being on its way out (this should
+ // not happen, but it apparently can).
+ if (log)
+ log->Printf("Sending control event of type: %d.", signal);
+ std::shared_ptr<EventDataReceipt> event_receipt_sp(new EventDataReceipt());
+ m_private_state_control_broadcaster.BroadcastEvent(signal,
+ event_receipt_sp);
+
+ // Wait for the event receipt or for the private state thread to exit
+ bool receipt_received = false;
+ if (PrivateStateThreadIsValid()) {
+ while (!receipt_received) {
+ // Check for a receipt for n seconds and then check if the private
+ // state thread is still around.
+ receipt_received =
+ event_receipt_sp->WaitForEventReceived(GetUtilityExpressionTimeout());
+ if (!receipt_received) {
+ // Check if the private state thread is still around. If it isn't
+ // then we are done waiting
+ if (!PrivateStateThreadIsValid())
+ break; // Private state thread exited or is exiting, we are done
+ }
+ }
+ }
+
+ if (signal == eBroadcastInternalStateControlStop) {
+ thread_result_t result = {};
+ m_private_state_thread.Join(&result);
+ m_private_state_thread.Reset();
+ }
+ } else {
+ if (log)
+ log->Printf(
+ "Private state thread already dead, no need to signal it to stop.");
+ }
+}
+
+void Process::SendAsyncInterrupt() {
+ if (PrivateStateThreadIsValid())
+ m_private_state_broadcaster.BroadcastEvent(Process::eBroadcastBitInterrupt,
+ nullptr);
+ else
+ BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr);
+}
+
+void Process::HandlePrivateEvent(EventSP &event_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ m_resume_requested = false;
+
+ const StateType new_state =
+ Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+
+ // First check to see if anybody wants a shot at this event:
+ if (m_next_event_action_up) {
+ NextEventAction::EventActionResult action_result =
+ m_next_event_action_up->PerformAction(event_sp);
+ if (log)
+ log->Printf("Ran next event action, result was %d.", action_result);
+
+ switch (action_result) {
+ case NextEventAction::eEventActionSuccess:
+ SetNextEventAction(nullptr);
+ break;
+
+ case NextEventAction::eEventActionRetry:
+ break;
+
+ case NextEventAction::eEventActionExit:
+ // Handle Exiting Here. If we already got an exited event, we should
+ // just propagate it. Otherwise, swallow this event, and set our state
+ // to exit so the next event will kill us.
+ if (new_state != eStateExited) {
+ // FIXME: should cons up an exited event, and discard this one.
+ SetExitStatus(0, m_next_event_action_up->GetExitString());
+ SetNextEventAction(nullptr);
+ return;
+ }
+ SetNextEventAction(nullptr);
+ break;
+ }
+ }
+
+ // See if we should broadcast this state to external clients?
+ const bool should_broadcast = ShouldBroadcastEvent(event_sp.get());
+
+ if (should_broadcast) {
+ const bool is_hijacked = IsHijackedForEvent(eBroadcastBitStateChanged);
+ if (log) {
+ log->Printf("Process::%s (pid = %" PRIu64
+ ") broadcasting new state %s (old state %s) to %s",
+ __FUNCTION__, GetID(), StateAsCString(new_state),
+ StateAsCString(GetState()),
+ is_hijacked ? "hijacked" : "public");
+ }
+ Process::ProcessEventData::SetUpdateStateOnRemoval(event_sp.get());
+ if (StateIsRunningState(new_state)) {
+ // Only push the input handler if we aren't fowarding events, as this
+ // means the curses GUI is in use... Or don't push it if we are launching
+ // since it will come up stopped.
+ if (!GetTarget().GetDebugger().IsForwardingEvents() &&
+ new_state != eStateLaunching && new_state != eStateAttaching) {
+ PushProcessIOHandler();
+ m_iohandler_sync.SetValue(m_iohandler_sync.GetValue() + 1,
+ eBroadcastAlways);
+ if (log)
+ log->Printf("Process::%s updated m_iohandler_sync to %d",
+ __FUNCTION__, m_iohandler_sync.GetValue());
+ }
+ } else if (StateIsStoppedState(new_state, false)) {
+ if (!Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) {
+ // If the lldb_private::Debugger is handling the events, we don't want
+ // to pop the process IOHandler here, we want to do it when we receive
+ // the stopped event so we can carefully control when the process
+ // IOHandler is popped because when we stop we want to display some
+ // text stating how and why we stopped, then maybe some
+ // process/thread/frame info, and then we want the "(lldb) " prompt to
+ // show up. If we pop the process IOHandler here, then we will cause
+ // the command interpreter to become the top IOHandler after the
+ // process pops off and it will update its prompt right away... See the
+ // Debugger.cpp file where it calls the function as
+ // "process_sp->PopProcessIOHandler()" to see where I am talking about.
+ // Otherwise we end up getting overlapping "(lldb) " prompts and
+ // garbled output.
+ //
+ // If we aren't handling the events in the debugger (which is indicated
+ // by "m_target.GetDebugger().IsHandlingEvents()" returning false) or
+ // we are hijacked, then we always pop the process IO handler manually.
+ // Hijacking happens when the internal process state thread is running
+ // thread plans, or when commands want to run in synchronous mode and
+ // they call "process->WaitForProcessToStop()". An example of something
+ // that will hijack the events is a simple expression:
+ //
+ // (lldb) expr (int)puts("hello")
+ //
+ // This will cause the internal process state thread to resume and halt
+ // the process (and _it_ will hijack the eBroadcastBitStateChanged
+ // events) and we do need the IO handler to be pushed and popped
+ // correctly.
+
+ if (is_hijacked || !GetTarget().GetDebugger().IsHandlingEvents())
+ PopProcessIOHandler();
+ }
+ }
+
+ BroadcastEvent(event_sp);
+ } else {
+ if (log) {
+ log->Printf(
+ "Process::%s (pid = %" PRIu64
+ ") suppressing state %s (old state %s): should_broadcast == false",
+ __FUNCTION__, GetID(), StateAsCString(new_state),
+ StateAsCString(GetState()));
+ }
+ }
+}
+
+Status Process::HaltPrivate() {
+ EventSP event_sp;
+ Status error(WillHalt());
+ if (error.Fail())
+ return error;
+
+ // Ask the process subclass to actually halt our process
+ bool caused_stop;
+ error = DoHalt(caused_stop);
+
+ DidHalt();
+ return error;
+}
+
+thread_result_t Process::PrivateStateThread(void *arg) {
+ std::unique_ptr<PrivateStateThreadArgs> args_up(
+ static_cast<PrivateStateThreadArgs *>(arg));
+ thread_result_t result =
+ args_up->process->RunPrivateStateThread(args_up->is_secondary_thread);
+ return result;
+}
+
+thread_result_t Process::RunPrivateStateThread(bool is_secondary_thread) {
+ bool control_only = true;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::%s (arg = %p, pid = %" PRIu64 ") thread starting...",
+ __FUNCTION__, static_cast<void *>(this), GetID());
+
+ bool exit_now = false;
+ bool interrupt_requested = false;
+ while (!exit_now) {
+ EventSP event_sp;
+ GetEventsPrivate(event_sp, llvm::None, control_only);
+ if (event_sp->BroadcasterIs(&m_private_state_control_broadcaster)) {
+ if (log)
+ log->Printf("Process::%s (arg = %p, pid = %" PRIu64
+ ") got a control event: %d",
+ __FUNCTION__, static_cast<void *>(this), GetID(),
+ event_sp->GetType());
+
+ switch (event_sp->GetType()) {
+ case eBroadcastInternalStateControlStop:
+ exit_now = true;
+ break; // doing any internal state management below
+
+ case eBroadcastInternalStateControlPause:
+ control_only = true;
+ break;
+
+ case eBroadcastInternalStateControlResume:
+ control_only = false;
+ break;
+ }
+
+ continue;
+ } else if (event_sp->GetType() == eBroadcastBitInterrupt) {
+ if (m_public_state.GetValue() == eStateAttaching) {
+ if (log)
+ log->Printf("Process::%s (arg = %p, pid = %" PRIu64
+ ") woke up with an interrupt while attaching - "
+ "forwarding interrupt.",
+ __FUNCTION__, static_cast<void *>(this), GetID());
+ BroadcastEvent(eBroadcastBitInterrupt, nullptr);
+ } else if (StateIsRunningState(m_last_broadcast_state)) {
+ if (log)
+ log->Printf("Process::%s (arg = %p, pid = %" PRIu64
+ ") woke up with an interrupt - Halting.",
+ __FUNCTION__, static_cast<void *>(this), GetID());
+ Status error = HaltPrivate();
+ if (error.Fail() && log)
+ log->Printf("Process::%s (arg = %p, pid = %" PRIu64
+ ") failed to halt the process: %s",
+ __FUNCTION__, static_cast<void *>(this), GetID(),
+ error.AsCString());
+ // Halt should generate a stopped event. Make a note of the fact that
+ // we were doing the interrupt, so we can set the interrupted flag
+ // after we receive the event. We deliberately set this to true even if
+ // HaltPrivate failed, so that we can interrupt on the next natural
+ // stop.
+ interrupt_requested = true;
+ } else {
+ // This can happen when someone (e.g. Process::Halt) sees that we are
+ // running and sends an interrupt request, but the process actually
+ // stops before we receive it. In that case, we can just ignore the
+ // request. We use m_last_broadcast_state, because the Stopped event
+ // may not have been popped of the event queue yet, which is when the
+ // public state gets updated.
+ if (log)
+ log->Printf(
+ "Process::%s ignoring interrupt as we have already stopped.",
+ __FUNCTION__);
+ }
+ continue;
+ }
+
+ const StateType internal_state =
+ Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+
+ if (internal_state != eStateInvalid) {
+ if (m_clear_thread_plans_on_stop &&
+ StateIsStoppedState(internal_state, true)) {
+ m_clear_thread_plans_on_stop = false;
+ m_thread_list.DiscardThreadPlans();
+ }
+
+ if (interrupt_requested) {
+ if (StateIsStoppedState(internal_state, true)) {
+ // We requested the interrupt, so mark this as such in the stop event
+ // so clients can tell an interrupted process from a natural stop
+ ProcessEventData::SetInterruptedInEvent(event_sp.get(), true);
+ interrupt_requested = false;
+ } else if (log) {
+ log->Printf("Process::%s interrupt_requested, but a non-stopped "
+ "state '%s' received.",
+ __FUNCTION__, StateAsCString(internal_state));
+ }
+ }
+
+ HandlePrivateEvent(event_sp);
+ }
+
+ if (internal_state == eStateInvalid || internal_state == eStateExited ||
+ internal_state == eStateDetached) {
+ if (log)
+ log->Printf("Process::%s (arg = %p, pid = %" PRIu64
+ ") about to exit with internal state %s...",
+ __FUNCTION__, static_cast<void *>(this), GetID(),
+ StateAsCString(internal_state));
+
+ break;
+ }
+ }
+
+ // Verify log is still enabled before attempting to write to it...
+ if (log)
+ log->Printf("Process::%s (arg = %p, pid = %" PRIu64 ") thread exiting...",
+ __FUNCTION__, static_cast<void *>(this), GetID());
+
+ // If we are a secondary thread, then the primary thread we are working for
+ // will have already acquired the public_run_lock, and isn't done with what
+ // it was doing yet, so don't try to change it on the way out.
+ if (!is_secondary_thread)
+ m_public_run_lock.SetStopped();
+ return {};
+}
+
+// Process Event Data
+
+Process::ProcessEventData::ProcessEventData()
+ : EventData(), m_process_wp(), m_state(eStateInvalid), m_restarted(false),
+ m_update_state(0), m_interrupted(false) {}
+
+Process::ProcessEventData::ProcessEventData(const ProcessSP &process_sp,
+ StateType state)
+ : EventData(), m_process_wp(), m_state(state), m_restarted(false),
+ m_update_state(0), m_interrupted(false) {
+ if (process_sp)
+ m_process_wp = process_sp;
+}
+
+Process::ProcessEventData::~ProcessEventData() = default;
+
+ConstString Process::ProcessEventData::GetFlavorString() {
+ static ConstString g_flavor("Process::ProcessEventData");
+ return g_flavor;
+}
+
+ConstString Process::ProcessEventData::GetFlavor() const {
+ return ProcessEventData::GetFlavorString();
+}
+
+void Process::ProcessEventData::DoOnRemoval(Event *event_ptr) {
+ ProcessSP process_sp(m_process_wp.lock());
+
+ if (!process_sp)
+ return;
+
+ // This function gets called twice for each event, once when the event gets
+ // pulled off of the private process event queue, and then any number of
+ // times, first when it gets pulled off of the public event queue, then other
+ // times when we're pretending that this is where we stopped at the end of
+ // expression evaluation. m_update_state is used to distinguish these three
+ // cases; it is 0 when we're just pulling it off for private handling, and >
+ // 1 for expression evaluation, and we don't want to do the breakpoint
+ // command handling then.
+ if (m_update_state != 1)
+ return;
+
+ process_sp->SetPublicState(
+ m_state, Process::ProcessEventData::GetRestartedFromEvent(event_ptr));
+
+ if (m_state == eStateStopped && !m_restarted) {
+ // Let process subclasses know we are about to do a public stop and do
+ // anything they might need to in order to speed up register and memory
+ // accesses.
+ process_sp->WillPublicStop();
+ }
+
+ // If this is a halt event, even if the halt stopped with some reason other
+ // than a plain interrupt (e.g. we had already stopped for a breakpoint when
+ // the halt request came through) don't do the StopInfo actions, as they may
+ // end up restarting the process.
+ if (m_interrupted)
+ return;
+
+ // If we're stopped and haven't restarted, then do the StopInfo actions here:
+ if (m_state == eStateStopped && !m_restarted) {
+ ThreadList &curr_thread_list = process_sp->GetThreadList();
+ uint32_t num_threads = curr_thread_list.GetSize();
+ uint32_t idx;
+
+ // The actions might change one of the thread's stop_info's opinions about
+ // whether we should stop the process, so we need to query that as we go.
+
+ // One other complication here, is that we try to catch any case where the
+ // target has run (except for expressions) and immediately exit, but if we
+ // get that wrong (which is possible) then the thread list might have
+ // changed, and that would cause our iteration here to crash. We could
+ // make a copy of the thread list, but we'd really like to also know if it
+ // has changed at all, so we make up a vector of the thread ID's and check
+ // what we get back against this list & bag out if anything differs.
+ std::vector<uint32_t> thread_index_array(num_threads);
+ for (idx = 0; idx < num_threads; ++idx)
+ thread_index_array[idx] =
+ curr_thread_list.GetThreadAtIndex(idx)->GetIndexID();
+
+ // Use this to track whether we should continue from here. We will only
+ // continue the target running if no thread says we should stop. Of course
+ // if some thread's PerformAction actually sets the target running, then it
+ // doesn't matter what the other threads say...
+
+ bool still_should_stop = false;
+
+ // Sometimes - for instance if we have a bug in the stub we are talking to,
+ // we stop but no thread has a valid stop reason. In that case we should
+ // just stop, because we have no way of telling what the right thing to do
+ // is, and it's better to let the user decide than continue behind their
+ // backs.
+
+ bool does_anybody_have_an_opinion = false;
+
+ for (idx = 0; idx < num_threads; ++idx) {
+ curr_thread_list = process_sp->GetThreadList();
+ if (curr_thread_list.GetSize() != num_threads) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP |
+ LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf(
+ "Number of threads changed from %u to %u while processing event.",
+ num_threads, curr_thread_list.GetSize());
+ break;
+ }
+
+ lldb::ThreadSP thread_sp = curr_thread_list.GetThreadAtIndex(idx);
+
+ if (thread_sp->GetIndexID() != thread_index_array[idx]) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP |
+ LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("The thread at position %u changed from %u to %u while "
+ "processing event.",
+ idx, thread_index_array[idx], thread_sp->GetIndexID());
+ break;
+ }
+
+ StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
+ if (stop_info_sp && stop_info_sp->IsValid()) {
+ does_anybody_have_an_opinion = true;
+ bool this_thread_wants_to_stop;
+ if (stop_info_sp->GetOverrideShouldStop()) {
+ this_thread_wants_to_stop =
+ stop_info_sp->GetOverriddenShouldStopValue();
+ } else {
+ stop_info_sp->PerformAction(event_ptr);
+ // The stop action might restart the target. If it does, then we
+ // want to mark that in the event so that whoever is receiving it
+ // will know to wait for the running event and reflect that state
+ // appropriately. We also need to stop processing actions, since they
+ // aren't expecting the target to be running.
+
+ // FIXME: we might have run.
+ if (stop_info_sp->HasTargetRunSinceMe()) {
+ SetRestarted(true);
+ break;
+ }
+
+ this_thread_wants_to_stop = stop_info_sp->ShouldStop(event_ptr);
+ }
+
+ if (!still_should_stop)
+ still_should_stop = this_thread_wants_to_stop;
+ }
+ }
+
+ if (!GetRestarted()) {
+ if (!still_should_stop && does_anybody_have_an_opinion) {
+ // We've been asked to continue, so do that here.
+ SetRestarted(true);
+ // Use the public resume method here, since this is just extending a
+ // public resume.
+ process_sp->PrivateResume();
+ } else {
+ bool hijacked =
+ process_sp->IsHijackedForEvent(eBroadcastBitStateChanged) &&
+ !process_sp->StateChangedIsHijackedForSynchronousResume();
+
+ if (!hijacked) {
+ // If we didn't restart, run the Stop Hooks here.
+ // Don't do that if state changed events aren't hooked up to the
+ // public (or SyncResume) broadcasters. StopHooks are just for
+ // real public stops. They might also restart the target,
+ // so watch for that.
+ process_sp->GetTarget().RunStopHooks();
+ if (process_sp->GetPrivateState() == eStateRunning)
+ SetRestarted(true);
+ }
+ }
+ }
+}
+}
+
+void Process::ProcessEventData::Dump(Stream *s) const {
+ ProcessSP process_sp(m_process_wp.lock());
+
+ if (process_sp)
+ s->Printf(" process = %p (pid = %" PRIu64 "), ",
+ static_cast<void *>(process_sp.get()), process_sp->GetID());
+ else
+ s->PutCString(" process = NULL, ");
+
+ s->Printf("state = %s", StateAsCString(GetState()));
+}
+
+const Process::ProcessEventData *
+Process::ProcessEventData::GetEventDataFromEvent(const Event *event_ptr) {
+ if (event_ptr) {
+ const EventData *event_data = event_ptr->GetData();
+ if (event_data &&
+ event_data->GetFlavor() == ProcessEventData::GetFlavorString())
+ return static_cast<const ProcessEventData *>(event_ptr->GetData());
+ }
+ return nullptr;
+}
+
+ProcessSP
+Process::ProcessEventData::GetProcessFromEvent(const Event *event_ptr) {
+ ProcessSP process_sp;
+ const ProcessEventData *data = GetEventDataFromEvent(event_ptr);
+ if (data)
+ process_sp = data->GetProcessSP();
+ return process_sp;
+}
+
+StateType Process::ProcessEventData::GetStateFromEvent(const Event *event_ptr) {
+ const ProcessEventData *data = GetEventDataFromEvent(event_ptr);
+ if (data == nullptr)
+ return eStateInvalid;
+ else
+ return data->GetState();
+}
+
+bool Process::ProcessEventData::GetRestartedFromEvent(const Event *event_ptr) {
+ const ProcessEventData *data = GetEventDataFromEvent(event_ptr);
+ if (data == nullptr)
+ return false;
+ else
+ return data->GetRestarted();
+}
+
+void Process::ProcessEventData::SetRestartedInEvent(Event *event_ptr,
+ bool new_value) {
+ ProcessEventData *data =
+ const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
+ if (data != nullptr)
+ data->SetRestarted(new_value);
+}
+
+size_t
+Process::ProcessEventData::GetNumRestartedReasons(const Event *event_ptr) {
+ ProcessEventData *data =
+ const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
+ if (data != nullptr)
+ return data->GetNumRestartedReasons();
+ else
+ return 0;
+}
+
+const char *
+Process::ProcessEventData::GetRestartedReasonAtIndex(const Event *event_ptr,
+ size_t idx) {
+ ProcessEventData *data =
+ const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
+ if (data != nullptr)
+ return data->GetRestartedReasonAtIndex(idx);
+ else
+ return nullptr;
+}
+
+void Process::ProcessEventData::AddRestartedReason(Event *event_ptr,
+ const char *reason) {
+ ProcessEventData *data =
+ const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
+ if (data != nullptr)
+ data->AddRestartedReason(reason);
+}
+
+bool Process::ProcessEventData::GetInterruptedFromEvent(
+ const Event *event_ptr) {
+ const ProcessEventData *data = GetEventDataFromEvent(event_ptr);
+ if (data == nullptr)
+ return false;
+ else
+ return data->GetInterrupted();
+}
+
+void Process::ProcessEventData::SetInterruptedInEvent(Event *event_ptr,
+ bool new_value) {
+ ProcessEventData *data =
+ const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
+ if (data != nullptr)
+ data->SetInterrupted(new_value);
+}
+
+bool Process::ProcessEventData::SetUpdateStateOnRemoval(Event *event_ptr) {
+ ProcessEventData *data =
+ const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
+ if (data) {
+ data->SetUpdateStateOnRemoval();
+ return true;
+ }
+ return false;
+}
+
+lldb::TargetSP Process::CalculateTarget() { return m_target_wp.lock(); }
+
+void Process::CalculateExecutionContext(ExecutionContext &exe_ctx) {
+ exe_ctx.SetTargetPtr(&GetTarget());
+ exe_ctx.SetProcessPtr(this);
+ exe_ctx.SetThreadPtr(nullptr);
+ exe_ctx.SetFramePtr(nullptr);
+}
+
+// uint32_t
+// Process::ListProcessesMatchingName (const char *name, StringList &matches,
+// std::vector<lldb::pid_t> &pids)
+//{
+// return 0;
+//}
+//
+// ArchSpec
+// Process::GetArchSpecForExistingProcess (lldb::pid_t pid)
+//{
+// return Host::GetArchSpecForExistingProcess (pid);
+//}
+//
+// ArchSpec
+// Process::GetArchSpecForExistingProcess (const char *process_name)
+//{
+// return Host::GetArchSpecForExistingProcess (process_name);
+//}
+
+void Process::AppendSTDOUT(const char *s, size_t len) {
+ std::lock_guard<std::recursive_mutex> guard(m_stdio_communication_mutex);
+ m_stdout_data.append(s, len);
+ BroadcastEventIfUnique(eBroadcastBitSTDOUT,
+ new ProcessEventData(shared_from_this(), GetState()));
+}
+
+void Process::AppendSTDERR(const char *s, size_t len) {
+ std::lock_guard<std::recursive_mutex> guard(m_stdio_communication_mutex);
+ m_stderr_data.append(s, len);
+ BroadcastEventIfUnique(eBroadcastBitSTDERR,
+ new ProcessEventData(shared_from_this(), GetState()));
+}
+
+void Process::BroadcastAsyncProfileData(const std::string &one_profile_data) {
+ std::lock_guard<std::recursive_mutex> guard(m_profile_data_comm_mutex);
+ m_profile_data.push_back(one_profile_data);
+ BroadcastEventIfUnique(eBroadcastBitProfileData,
+ new ProcessEventData(shared_from_this(), GetState()));
+}
+
+void Process::BroadcastStructuredData(const StructuredData::ObjectSP &object_sp,
+ const StructuredDataPluginSP &plugin_sp) {
+ BroadcastEvent(
+ eBroadcastBitStructuredData,
+ new EventDataStructuredData(shared_from_this(), object_sp, plugin_sp));
+}
+
+StructuredDataPluginSP
+Process::GetStructuredDataPlugin(ConstString type_name) const {
+ auto find_it = m_structured_data_plugin_map.find(type_name);
+ if (find_it != m_structured_data_plugin_map.end())
+ return find_it->second;
+ else
+ return StructuredDataPluginSP();
+}
+
+size_t Process::GetAsyncProfileData(char *buf, size_t buf_size, Status &error) {
+ std::lock_guard<std::recursive_mutex> guard(m_profile_data_comm_mutex);
+ if (m_profile_data.empty())
+ return 0;
+
+ std::string &one_profile_data = m_profile_data.front();
+ size_t bytes_available = one_profile_data.size();
+ if (bytes_available > 0) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::GetProfileData (buf = %p, size = %" PRIu64 ")",
+ static_cast<void *>(buf), static_cast<uint64_t>(buf_size));
+ if (bytes_available > buf_size) {
+ memcpy(buf, one_profile_data.c_str(), buf_size);
+ one_profile_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ } else {
+ memcpy(buf, one_profile_data.c_str(), bytes_available);
+ m_profile_data.erase(m_profile_data.begin());
+ }
+ }
+ return bytes_available;
+}
+
+// Process STDIO
+
+size_t Process::GetSTDOUT(char *buf, size_t buf_size, Status &error) {
+ std::lock_guard<std::recursive_mutex> guard(m_stdio_communication_mutex);
+ size_t bytes_available = m_stdout_data.size();
+ if (bytes_available > 0) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::GetSTDOUT (buf = %p, size = %" PRIu64 ")",
+ static_cast<void *>(buf), static_cast<uint64_t>(buf_size));
+ if (bytes_available > buf_size) {
+ memcpy(buf, m_stdout_data.c_str(), buf_size);
+ m_stdout_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ } else {
+ memcpy(buf, m_stdout_data.c_str(), bytes_available);
+ m_stdout_data.clear();
+ }
+ }
+ return bytes_available;
+}
+
+size_t Process::GetSTDERR(char *buf, size_t buf_size, Status &error) {
+ std::lock_guard<std::recursive_mutex> gaurd(m_stdio_communication_mutex);
+ size_t bytes_available = m_stderr_data.size();
+ if (bytes_available > 0) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::GetSTDERR (buf = %p, size = %" PRIu64 ")",
+ static_cast<void *>(buf), static_cast<uint64_t>(buf_size));
+ if (bytes_available > buf_size) {
+ memcpy(buf, m_stderr_data.c_str(), buf_size);
+ m_stderr_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ } else {
+ memcpy(buf, m_stderr_data.c_str(), bytes_available);
+ m_stderr_data.clear();
+ }
+ }
+ return bytes_available;
+}
+
+void Process::STDIOReadThreadBytesReceived(void *baton, const void *src,
+ size_t src_len) {
+ Process *process = (Process *)baton;
+ process->AppendSTDOUT(static_cast<const char *>(src), src_len);
+}
+
+class IOHandlerProcessSTDIO : public IOHandler {
+public:
+ IOHandlerProcessSTDIO(Process *process, int write_fd)
+ : IOHandler(process->GetTarget().GetDebugger(),
+ IOHandler::Type::ProcessIO),
+ m_process(process), m_write_file(write_fd, false) {
+ m_pipe.CreateNew(false);
+ m_read_file.SetDescriptor(GetInputFD(), false);
+ }
+
+ ~IOHandlerProcessSTDIO() override = default;
+
+ // Each IOHandler gets to run until it is done. It should read data from the
+ // "in" and place output into "out" and "err and return when done.
+ void Run() override {
+ if (!m_read_file.IsValid() || !m_write_file.IsValid() ||
+ !m_pipe.CanRead() || !m_pipe.CanWrite()) {
+ SetIsDone(true);
+ return;
+ }
+
+ SetIsDone(false);
+ const int read_fd = m_read_file.GetDescriptor();
+ TerminalState terminal_state;
+ terminal_state.Save(read_fd, false);
+ Terminal terminal(read_fd);
+ terminal.SetCanonical(false);
+ terminal.SetEcho(false);
+// FD_ZERO, FD_SET are not supported on windows
+#ifndef _WIN32
+ const int pipe_read_fd = m_pipe.GetReadFileDescriptor();
+ m_is_running = true;
+ while (!GetIsDone()) {
+ SelectHelper select_helper;
+ select_helper.FDSetRead(read_fd);
+ select_helper.FDSetRead(pipe_read_fd);
+ Status error = select_helper.Select();
+
+ if (error.Fail()) {
+ SetIsDone(true);
+ } else {
+ char ch = 0;
+ size_t n;
+ if (select_helper.FDIsSetRead(read_fd)) {
+ n = 1;
+ if (m_read_file.Read(&ch, n).Success() && n == 1) {
+ if (m_write_file.Write(&ch, n).Fail() || n != 1)
+ SetIsDone(true);
+ } else
+ SetIsDone(true);
+ }
+ if (select_helper.FDIsSetRead(pipe_read_fd)) {
+ size_t bytes_read;
+ // Consume the interrupt byte
+ Status error = m_pipe.Read(&ch, 1, bytes_read);
+ if (error.Success()) {
+ switch (ch) {
+ case 'q':
+ SetIsDone(true);
+ break;
+ case 'i':
+ if (StateIsRunningState(m_process->GetState()))
+ m_process->SendAsyncInterrupt();
+ break;
+ }
+ }
+ }
+ }
+ }
+ m_is_running = false;
+#endif
+ terminal_state.Restore();
+ }
+
+ void Cancel() override {
+ SetIsDone(true);
+ // Only write to our pipe to cancel if we are in
+ // IOHandlerProcessSTDIO::Run(). We can end up with a python command that
+ // is being run from the command interpreter:
+ //
+ // (lldb) step_process_thousands_of_times
+ //
+ // In this case the command interpreter will be in the middle of handling
+ // the command and if the process pushes and pops the IOHandler thousands
+ // of times, we can end up writing to m_pipe without ever consuming the
+ // bytes from the pipe in IOHandlerProcessSTDIO::Run() and end up
+ // deadlocking when the pipe gets fed up and blocks until data is consumed.
+ if (m_is_running) {
+ char ch = 'q'; // Send 'q' for quit
+ size_t bytes_written = 0;
+ m_pipe.Write(&ch, 1, bytes_written);
+ }
+ }
+
+ bool Interrupt() override {
+ // Do only things that are safe to do in an interrupt context (like in a
+ // SIGINT handler), like write 1 byte to a file descriptor. This will
+ // interrupt the IOHandlerProcessSTDIO::Run() and we can look at the byte
+ // that was written to the pipe and then call
+ // m_process->SendAsyncInterrupt() from a much safer location in code.
+ if (m_active) {
+ char ch = 'i'; // Send 'i' for interrupt
+ size_t bytes_written = 0;
+ Status result = m_pipe.Write(&ch, 1, bytes_written);
+ return result.Success();
+ } else {
+ // This IOHandler might be pushed on the stack, but not being run
+ // currently so do the right thing if we aren't actively watching for
+ // STDIN by sending the interrupt to the process. Otherwise the write to
+ // the pipe above would do nothing. This can happen when the command
+ // interpreter is running and gets a "expression ...". It will be on the
+ // IOHandler thread and sending the input is complete to the delegate
+ // which will cause the expression to run, which will push the process IO
+ // handler, but not run it.
+
+ if (StateIsRunningState(m_process->GetState())) {
+ m_process->SendAsyncInterrupt();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void GotEOF() override {}
+
+protected:
+ Process *m_process;
+ File m_read_file; // Read from this file (usually actual STDIN for LLDB
+ File m_write_file; // Write to this file (usually the master pty for getting
+ // io to debuggee)
+ Pipe m_pipe;
+ std::atomic<bool> m_is_running{false};
+};
+
+void Process::SetSTDIOFileDescriptor(int fd) {
+ // First set up the Read Thread for reading/handling process I/O
+
+ std::unique_ptr<ConnectionFileDescriptor> conn_up(
+ new ConnectionFileDescriptor(fd, true));
+
+ if (conn_up) {
+ m_stdio_communication.SetConnection(conn_up.release());
+ if (m_stdio_communication.IsConnected()) {
+ m_stdio_communication.SetReadThreadBytesReceivedCallback(
+ STDIOReadThreadBytesReceived, this);
+ m_stdio_communication.StartReadThread();
+
+ // Now read thread is set up, set up input reader.
+
+ if (!m_process_input_reader)
+ m_process_input_reader =
+ std::make_shared<IOHandlerProcessSTDIO>(this, fd);
+ }
+ }
+}
+
+bool Process::ProcessIOHandlerIsActive() {
+ IOHandlerSP io_handler_sp(m_process_input_reader);
+ if (io_handler_sp)
+ return GetTarget().GetDebugger().IsTopIOHandler(io_handler_sp);
+ return false;
+}
+bool Process::PushProcessIOHandler() {
+ IOHandlerSP io_handler_sp(m_process_input_reader);
+ if (io_handler_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::%s pushing IO handler", __FUNCTION__);
+
+ io_handler_sp->SetIsDone(false);
+ // If we evaluate an utility function, then we don't cancel the current
+ // IOHandler. Our IOHandler is non-interactive and shouldn't disturb the
+ // existing IOHandler that potentially provides the user interface (e.g.
+ // the IOHandler for Editline).
+ bool cancel_top_handler = !m_mod_id.IsRunningUtilityFunction();
+ GetTarget().GetDebugger().PushIOHandler(io_handler_sp, cancel_top_handler);
+ return true;
+ }
+ return false;
+}
+
+bool Process::PopProcessIOHandler() {
+ IOHandlerSP io_handler_sp(m_process_input_reader);
+ if (io_handler_sp)
+ return GetTarget().GetDebugger().PopIOHandler(io_handler_sp);
+ return false;
+}
+
+// The process needs to know about installed plug-ins
+void Process::SettingsInitialize() { Thread::SettingsInitialize(); }
+
+void Process::SettingsTerminate() { Thread::SettingsTerminate(); }
+
+namespace {
+// RestorePlanState is used to record the "is private", "is master" and "okay
+// to discard" fields of the plan we are running, and reset it on Clean or on
+// destruction. It will only reset the state once, so you can call Clean and
+// then monkey with the state and it won't get reset on you again.
+
+class RestorePlanState {
+public:
+ RestorePlanState(lldb::ThreadPlanSP thread_plan_sp)
+ : m_thread_plan_sp(thread_plan_sp), m_already_reset(false) {
+ if (m_thread_plan_sp) {
+ m_private = m_thread_plan_sp->GetPrivate();
+ m_is_master = m_thread_plan_sp->IsMasterPlan();
+ m_okay_to_discard = m_thread_plan_sp->OkayToDiscard();
+ }
+ }
+
+ ~RestorePlanState() { Clean(); }
+
+ void Clean() {
+ if (!m_already_reset && m_thread_plan_sp) {
+ m_already_reset = true;
+ m_thread_plan_sp->SetPrivate(m_private);
+ m_thread_plan_sp->SetIsMasterPlan(m_is_master);
+ m_thread_plan_sp->SetOkayToDiscard(m_okay_to_discard);
+ }
+ }
+
+private:
+ lldb::ThreadPlanSP m_thread_plan_sp;
+ bool m_already_reset;
+ bool m_private;
+ bool m_is_master;
+ bool m_okay_to_discard;
+};
+} // anonymous namespace
+
+static microseconds
+GetOneThreadExpressionTimeout(const EvaluateExpressionOptions &options) {
+ const milliseconds default_one_thread_timeout(250);
+
+ // If the overall wait is forever, then we don't need to worry about it.
+ if (!options.GetTimeout()) {
+ return options.GetOneThreadTimeout() ? *options.GetOneThreadTimeout()
+ : default_one_thread_timeout;
+ }
+
+ // If the one thread timeout is set, use it.
+ if (options.GetOneThreadTimeout())
+ return *options.GetOneThreadTimeout();
+
+ // Otherwise use half the total timeout, bounded by the
+ // default_one_thread_timeout.
+ return std::min<microseconds>(default_one_thread_timeout,
+ *options.GetTimeout() / 2);
+}
+
+static Timeout<std::micro>
+GetExpressionTimeout(const EvaluateExpressionOptions &options,
+ bool before_first_timeout) {
+ // If we are going to run all threads the whole time, or if we are only going
+ // to run one thread, we can just return the overall timeout.
+ if (!options.GetStopOthers() || !options.GetTryAllThreads())
+ return options.GetTimeout();
+
+ if (before_first_timeout)
+ return GetOneThreadExpressionTimeout(options);
+
+ if (!options.GetTimeout())
+ return llvm::None;
+ else
+ return *options.GetTimeout() - GetOneThreadExpressionTimeout(options);
+}
+
+static llvm::Optional<ExpressionResults>
+HandleStoppedEvent(Thread &thread, const ThreadPlanSP &thread_plan_sp,
+ RestorePlanState &restorer, const EventSP &event_sp,
+ EventSP &event_to_broadcast_sp,
+ const EvaluateExpressionOptions &options, bool handle_interrupts) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS);
+
+ ThreadPlanSP plan = thread.GetCompletedPlan();
+ if (plan == thread_plan_sp && plan->PlanSucceeded()) {
+ LLDB_LOG(log, "execution completed successfully");
+
+ // Restore the plan state so it will get reported as intended when we are
+ // done.
+ restorer.Clean();
+ return eExpressionCompleted;
+ }
+
+ StopInfoSP stop_info_sp = thread.GetStopInfo();
+ if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint &&
+ stop_info_sp->ShouldNotify(event_sp.get())) {
+ LLDB_LOG(log, "stopped for breakpoint: {0}.", stop_info_sp->GetDescription());
+ if (!options.DoesIgnoreBreakpoints()) {
+ // Restore the plan state and then force Private to false. We are going
+ // to stop because of this plan so we need it to become a public plan or
+ // it won't report correctly when we continue to its termination later
+ // on.
+ restorer.Clean();
+ thread_plan_sp->SetPrivate(false);
+ event_to_broadcast_sp = event_sp;
+ }
+ return eExpressionHitBreakpoint;
+ }
+
+ if (!handle_interrupts &&
+ Process::ProcessEventData::GetInterruptedFromEvent(event_sp.get()))
+ return llvm::None;
+
+ LLDB_LOG(log, "thread plan did not successfully complete");
+ if (!options.DoesUnwindOnError())
+ event_to_broadcast_sp = event_sp;
+ return eExpressionInterrupted;
+}
+
+ExpressionResults
+Process::RunThreadPlan(ExecutionContext &exe_ctx,
+ lldb::ThreadPlanSP &thread_plan_sp,
+ const EvaluateExpressionOptions &options,
+ DiagnosticManager &diagnostic_manager) {
+ ExpressionResults return_value = eExpressionSetupError;
+
+ std::lock_guard<std::mutex> run_thread_plan_locker(m_run_thread_plan_lock);
+
+ if (!thread_plan_sp) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "RunThreadPlan called with empty thread plan.");
+ return eExpressionSetupError;
+ }
+
+ if (!thread_plan_sp->ValidatePlan(nullptr)) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "RunThreadPlan called with an invalid thread plan.");
+ return eExpressionSetupError;
+ }
+
+ if (exe_ctx.GetProcessPtr() != this) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "RunThreadPlan called on wrong process.");
+ return eExpressionSetupError;
+ }
+
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (thread == nullptr) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "RunThreadPlan called with invalid thread.");
+ return eExpressionSetupError;
+ }
+
+ // We need to change some of the thread plan attributes for the thread plan
+ // runner. This will restore them when we are done:
+
+ RestorePlanState thread_plan_restorer(thread_plan_sp);
+
+ // We rely on the thread plan we are running returning "PlanCompleted" if
+ // when it successfully completes. For that to be true the plan can't be
+ // private - since private plans suppress themselves in the GetCompletedPlan
+ // call.
+
+ thread_plan_sp->SetPrivate(false);
+
+ // The plans run with RunThreadPlan also need to be terminal master plans or
+ // when they are done we will end up asking the plan above us whether we
+ // should stop, which may give the wrong answer.
+
+ thread_plan_sp->SetIsMasterPlan(true);
+ thread_plan_sp->SetOkayToDiscard(false);
+
+ // If we are running some utility expression for LLDB, we now have to mark
+ // this in the ProcesModID of this process. This RAII takes care of marking
+ // and reverting the mark it once we are done running the expression.
+ UtilityFunctionScope util_scope(options.IsForUtilityExpr() ? this : nullptr);
+
+ if (m_private_state.GetValue() != eStateStopped) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "RunThreadPlan called while the private state was not stopped.");
+ return eExpressionSetupError;
+ }
+
+ // Save the thread & frame from the exe_ctx for restoration after we run
+ const uint32_t thread_idx_id = thread->GetIndexID();
+ StackFrameSP selected_frame_sp = thread->GetSelectedFrame();
+ if (!selected_frame_sp) {
+ thread->SetSelectedFrame(nullptr);
+ selected_frame_sp = thread->GetSelectedFrame();
+ if (!selected_frame_sp) {
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityError,
+ "RunThreadPlan called without a selected frame on thread %d",
+ thread_idx_id);
+ return eExpressionSetupError;
+ }
+ }
+
+ // Make sure the timeout values make sense. The one thread timeout needs to
+ // be smaller than the overall timeout.
+ if (options.GetOneThreadTimeout() && options.GetTimeout() &&
+ *options.GetTimeout() < *options.GetOneThreadTimeout()) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "RunThreadPlan called with one thread "
+ "timeout greater than total timeout");
+ return eExpressionSetupError;
+ }
+
+ StackID ctx_frame_id = selected_frame_sp->GetStackID();
+
+ // N.B. Running the target may unset the currently selected thread and frame.
+ // We don't want to do that either, so we should arrange to reset them as
+ // well.
+
+ lldb::ThreadSP selected_thread_sp = GetThreadList().GetSelectedThread();
+
+ uint32_t selected_tid;
+ StackID selected_stack_id;
+ if (selected_thread_sp) {
+ selected_tid = selected_thread_sp->GetIndexID();
+ selected_stack_id = selected_thread_sp->GetSelectedFrame()->GetStackID();
+ } else {
+ selected_tid = LLDB_INVALID_THREAD_ID;
+ }
+
+ HostThread backup_private_state_thread;
+ lldb::StateType old_state = eStateInvalid;
+ lldb::ThreadPlanSP stopper_base_plan_sp;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP |
+ LIBLLDB_LOG_PROCESS));
+ if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) {
+ // Yikes, we are running on the private state thread! So we can't wait for
+ // public events on this thread, since we are the thread that is generating
+ // public events. The simplest thing to do is to spin up a temporary thread
+ // to handle private state thread events while we are fielding public
+ // events here.
+ if (log)
+ log->Printf("Running thread plan on private state thread, spinning up "
+ "another state thread to handle the events.");
+
+ backup_private_state_thread = m_private_state_thread;
+
+ // One other bit of business: we want to run just this thread plan and
+ // anything it pushes, and then stop, returning control here. But in the
+ // normal course of things, the plan above us on the stack would be given a
+ // shot at the stop event before deciding to stop, and we don't want that.
+ // So we insert a "stopper" base plan on the stack before the plan we want
+ // to run. Since base plans always stop and return control to the user,
+ // that will do just what we want.
+ stopper_base_plan_sp.reset(new ThreadPlanBase(*thread));
+ thread->QueueThreadPlan(stopper_base_plan_sp, false);
+ // Have to make sure our public state is stopped, since otherwise the
+ // reporting logic below doesn't work correctly.
+ old_state = m_public_state.GetValue();
+ m_public_state.SetValueNoLock(eStateStopped);
+
+ // Now spin up the private state thread:
+ StartPrivateStateThread(true);
+ }
+
+ thread->QueueThreadPlan(
+ thread_plan_sp, false); // This used to pass "true" does that make sense?
+
+ if (options.GetDebug()) {
+ // In this case, we aren't actually going to run, we just want to stop
+ // right away. Flush this thread so we will refetch the stacks and show the
+ // correct backtrace.
+ // FIXME: To make this prettier we should invent some stop reason for this,
+ // but that
+ // is only cosmetic, and this functionality is only of use to lldb
+ // developers who can live with not pretty...
+ thread->Flush();
+ return eExpressionStoppedForDebug;
+ }
+
+ ListenerSP listener_sp(
+ Listener::MakeListener("lldb.process.listener.run-thread-plan"));
+
+ lldb::EventSP event_to_broadcast_sp;
+
+ {
+ // This process event hijacker Hijacks the Public events and its destructor
+ // makes sure that the process events get restored on exit to the function.
+ //
+ // If the event needs to propagate beyond the hijacker (e.g., the process
+ // exits during execution), then the event is put into
+ // event_to_broadcast_sp for rebroadcasting.
+
+ ProcessEventHijacker run_thread_plan_hijacker(*this, listener_sp);
+
+ if (log) {
+ StreamString s;
+ thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ log->Printf("Process::RunThreadPlan(): Resuming thread %u - 0x%4.4" PRIx64
+ " to run thread plan \"%s\".",
+ thread->GetIndexID(), thread->GetID(), s.GetData());
+ }
+
+ bool got_event;
+ lldb::EventSP event_sp;
+ lldb::StateType stop_state = lldb::eStateInvalid;
+
+ bool before_first_timeout = true; // This is set to false the first time
+ // that we have to halt the target.
+ bool do_resume = true;
+ bool handle_running_event = true;
+
+ // This is just for accounting:
+ uint32_t num_resumes = 0;
+
+ // If we are going to run all threads the whole time, or if we are only
+ // going to run one thread, then we don't need the first timeout. So we
+ // pretend we are after the first timeout already.
+ if (!options.GetStopOthers() || !options.GetTryAllThreads())
+ before_first_timeout = false;
+
+ if (log)
+ log->Printf("Stop others: %u, try all: %u, before_first: %u.\n",
+ options.GetStopOthers(), options.GetTryAllThreads(),
+ before_first_timeout);
+
+ // This isn't going to work if there are unfetched events on the queue. Are
+ // there cases where we might want to run the remaining events here, and
+ // then try to call the function? That's probably being too tricky for our
+ // own good.
+
+ Event *other_events = listener_sp->PeekAtNextEvent();
+ if (other_events != nullptr) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "RunThreadPlan called with pending events on the queue.");
+ return eExpressionSetupError;
+ }
+
+ // We also need to make sure that the next event is delivered. We might be
+ // calling a function as part of a thread plan, in which case the last
+ // delivered event could be the running event, and we don't want event
+ // coalescing to cause us to lose OUR running event...
+ ForceNextEventDelivery();
+
+// This while loop must exit out the bottom, there's cleanup that we need to do
+// when we are done. So don't call return anywhere within it.
+
+#ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT
+ // It's pretty much impossible to write test cases for things like: One
+ // thread timeout expires, I go to halt, but the process already stopped on
+ // the function call stop breakpoint. Turning on this define will make us
+ // not fetch the first event till after the halt. So if you run a quick
+ // function, it will have completed, and the completion event will be
+ // waiting, when you interrupt for halt. The expression evaluation should
+ // still succeed.
+ bool miss_first_event = true;
+#endif
+ while (true) {
+ // We usually want to resume the process if we get to the top of the
+ // loop. The only exception is if we get two running events with no
+ // intervening stop, which can happen, we will just wait for then next
+ // stop event.
+ if (log)
+ log->Printf("Top of while loop: do_resume: %i handle_running_event: %i "
+ "before_first_timeout: %i.",
+ do_resume, handle_running_event, before_first_timeout);
+
+ if (do_resume || handle_running_event) {
+ // Do the initial resume and wait for the running event before going
+ // further.
+
+ if (do_resume) {
+ num_resumes++;
+ Status resume_error = PrivateResume();
+ if (!resume_error.Success()) {
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityError,
+ "couldn't resume inferior the %d time: \"%s\".", num_resumes,
+ resume_error.AsCString());
+ return_value = eExpressionSetupError;
+ break;
+ }
+ }
+
+ got_event =
+ listener_sp->GetEvent(event_sp, GetUtilityExpressionTimeout());
+ if (!got_event) {
+ if (log)
+ log->Printf("Process::RunThreadPlan(): didn't get any event after "
+ "resume %" PRIu32 ", exiting.",
+ num_resumes);
+
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "didn't get any event after resume %" PRIu32
+ ", exiting.",
+ num_resumes);
+ return_value = eExpressionSetupError;
+ break;
+ }
+
+ stop_state =
+ Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+
+ if (stop_state != eStateRunning) {
+ bool restarted = false;
+
+ if (stop_state == eStateStopped) {
+ restarted = Process::ProcessEventData::GetRestartedFromEvent(
+ event_sp.get());
+ if (log)
+ log->Printf(
+ "Process::RunThreadPlan(): didn't get running event after "
+ "resume %d, got %s instead (restarted: %i, do_resume: %i, "
+ "handle_running_event: %i).",
+ num_resumes, StateAsCString(stop_state), restarted, do_resume,
+ handle_running_event);
+ }
+
+ if (restarted) {
+ // This is probably an overabundance of caution, I don't think I
+ // should ever get a stopped & restarted event here. But if I do,
+ // the best thing is to Halt and then get out of here.
+ const bool clear_thread_plans = false;
+ const bool use_run_lock = false;
+ Halt(clear_thread_plans, use_run_lock);
+ }
+
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityError,
+ "didn't get running event after initial resume, got %s instead.",
+ StateAsCString(stop_state));
+ return_value = eExpressionSetupError;
+ break;
+ }
+
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): resuming succeeded.");
+ // We need to call the function synchronously, so spin waiting for it
+ // to return. If we get interrupted while executing, we're going to
+ // lose our context, and won't be able to gather the result at this
+ // point. We set the timeout AFTER the resume, since the resume takes
+ // some time and we don't want to charge that to the timeout.
+ } else {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): waiting for next event.");
+ }
+
+ do_resume = true;
+ handle_running_event = true;
+
+ // Now wait for the process to stop again:
+ event_sp.reset();
+
+ Timeout<std::micro> timeout =
+ GetExpressionTimeout(options, before_first_timeout);
+ if (log) {
+ if (timeout) {
+ auto now = system_clock::now();
+ log->Printf("Process::RunThreadPlan(): about to wait - now is %s - "
+ "endpoint is %s",
+ llvm::to_string(now).c_str(),
+ llvm::to_string(now + *timeout).c_str());
+ } else {
+ log->Printf("Process::RunThreadPlan(): about to wait forever.");
+ }
+ }
+
+#ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT
+ // See comment above...
+ if (miss_first_event) {
+ usleep(1000);
+ miss_first_event = false;
+ got_event = false;
+ } else
+#endif
+ got_event = listener_sp->GetEvent(event_sp, timeout);
+
+ if (got_event) {
+ if (event_sp) {
+ bool keep_going = false;
+ if (event_sp->GetType() == eBroadcastBitInterrupt) {
+ const bool clear_thread_plans = false;
+ const bool use_run_lock = false;
+ Halt(clear_thread_plans, use_run_lock);
+ return_value = eExpressionInterrupted;
+ diagnostic_manager.PutString(eDiagnosticSeverityRemark,
+ "execution halted by user interrupt.");
+ if (log)
+ log->Printf("Process::RunThreadPlan(): Got interrupted by "
+ "eBroadcastBitInterrupted, exiting.");
+ break;
+ } else {
+ stop_state =
+ Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+ if (log)
+ log->Printf(
+ "Process::RunThreadPlan(): in while loop, got event: %s.",
+ StateAsCString(stop_state));
+
+ switch (stop_state) {
+ case lldb::eStateStopped: {
+ // We stopped, figure out what we are going to do now.
+ ThreadSP thread_sp =
+ GetThreadList().FindThreadByIndexID(thread_idx_id);
+ if (!thread_sp) {
+ // Ooh, our thread has vanished. Unlikely that this was
+ // successful execution...
+ if (log)
+ log->Printf("Process::RunThreadPlan(): execution completed "
+ "but our thread (index-id=%u) has vanished.",
+ thread_idx_id);
+ return_value = eExpressionInterrupted;
+ } else if (Process::ProcessEventData::GetRestartedFromEvent(
+ event_sp.get())) {
+ // If we were restarted, we just need to go back up to fetch
+ // another event.
+ if (log) {
+ log->Printf("Process::RunThreadPlan(): Got a stop and "
+ "restart, so we'll continue waiting.");
+ }
+ keep_going = true;
+ do_resume = false;
+ handle_running_event = true;
+ } else {
+ const bool handle_interrupts = true;
+ return_value = *HandleStoppedEvent(
+ *thread, thread_plan_sp, thread_plan_restorer, event_sp,
+ event_to_broadcast_sp, options, handle_interrupts);
+ }
+ } break;
+
+ case lldb::eStateRunning:
+ // This shouldn't really happen, but sometimes we do get two
+ // running events without an intervening stop, and in that case
+ // we should just go back to waiting for the stop.
+ do_resume = false;
+ keep_going = true;
+ handle_running_event = false;
+ break;
+
+ default:
+ if (log)
+ log->Printf("Process::RunThreadPlan(): execution stopped with "
+ "unexpected state: %s.",
+ StateAsCString(stop_state));
+
+ if (stop_state == eStateExited)
+ event_to_broadcast_sp = event_sp;
+
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "execution stopped with unexpected state.");
+ return_value = eExpressionInterrupted;
+ break;
+ }
+ }
+
+ if (keep_going)
+ continue;
+ else
+ break;
+ } else {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): got_event was true, but "
+ "the event pointer was null. How odd...");
+ return_value = eExpressionInterrupted;
+ break;
+ }
+ } else {
+ // If we didn't get an event that means we've timed out... We will
+ // interrupt the process here. Depending on what we were asked to do
+ // we will either exit, or try with all threads running for the same
+ // timeout.
+
+ if (log) {
+ if (options.GetTryAllThreads()) {
+ if (before_first_timeout) {
+ LLDB_LOG(log,
+ "Running function with one thread timeout timed out.");
+ } else
+ LLDB_LOG(log, "Restarting function with all threads enabled and "
+ "timeout: {0} timed out, abandoning execution.",
+ timeout);
+ } else
+ LLDB_LOG(log, "Running function with timeout: {0} timed out, "
+ "abandoning execution.",
+ timeout);
+ }
+
+ // It is possible that between the time we issued the Halt, and we get
+ // around to calling Halt the target could have stopped. That's fine,
+ // Halt will figure that out and send the appropriate Stopped event.
+ // BUT it is also possible that we stopped & restarted (e.g. hit a
+ // signal with "stop" set to false.) In
+ // that case, we'll get the stopped & restarted event, and we should go
+ // back to waiting for the Halt's stopped event. That's what this
+ // while loop does.
+
+ bool back_to_top = true;
+ uint32_t try_halt_again = 0;
+ bool do_halt = true;
+ const uint32_t num_retries = 5;
+ while (try_halt_again < num_retries) {
+ Status halt_error;
+ if (do_halt) {
+ if (log)
+ log->Printf("Process::RunThreadPlan(): Running Halt.");
+ const bool clear_thread_plans = false;
+ const bool use_run_lock = false;
+ Halt(clear_thread_plans, use_run_lock);
+ }
+ if (halt_error.Success()) {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): Halt succeeded.");
+
+ got_event =
+ listener_sp->GetEvent(event_sp, GetUtilityExpressionTimeout());
+
+ if (got_event) {
+ stop_state =
+ Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+ if (log) {
+ log->Printf("Process::RunThreadPlan(): Stopped with event: %s",
+ StateAsCString(stop_state));
+ if (stop_state == lldb::eStateStopped &&
+ Process::ProcessEventData::GetInterruptedFromEvent(
+ event_sp.get()))
+ log->PutCString(" Event was the Halt interruption event.");
+ }
+
+ if (stop_state == lldb::eStateStopped) {
+ if (Process::ProcessEventData::GetRestartedFromEvent(
+ event_sp.get())) {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): Went to halt "
+ "but got a restarted event, there must be "
+ "an un-restarted stopped event so try "
+ "again... "
+ "Exiting wait loop.");
+ try_halt_again++;
+ do_halt = false;
+ continue;
+ }
+
+ // Between the time we initiated the Halt and the time we
+ // delivered it, the process could have already finished its
+ // job. Check that here:
+ const bool handle_interrupts = false;
+ if (auto result = HandleStoppedEvent(
+ *thread, thread_plan_sp, thread_plan_restorer, event_sp,
+ event_to_broadcast_sp, options, handle_interrupts)) {
+ return_value = *result;
+ back_to_top = false;
+ break;
+ }
+
+ if (!options.GetTryAllThreads()) {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): try_all_threads "
+ "was false, we stopped so now we're "
+ "quitting.");
+ return_value = eExpressionInterrupted;
+ back_to_top = false;
+ break;
+ }
+
+ if (before_first_timeout) {
+ // Set all the other threads to run, and return to the top of
+ // the loop, which will continue;
+ before_first_timeout = false;
+ thread_plan_sp->SetStopOthers(false);
+ if (log)
+ log->PutCString(
+ "Process::RunThreadPlan(): about to resume.");
+
+ back_to_top = true;
+ break;
+ } else {
+ // Running all threads failed, so return Interrupted.
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): running all "
+ "threads timed out.");
+ return_value = eExpressionInterrupted;
+ back_to_top = false;
+ break;
+ }
+ }
+ } else {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): halt said it "
+ "succeeded, but I got no event. "
+ "I'm getting out of here passing Interrupted.");
+ return_value = eExpressionInterrupted;
+ back_to_top = false;
+ break;
+ }
+ } else {
+ try_halt_again++;
+ continue;
+ }
+ }
+
+ if (!back_to_top || try_halt_again > num_retries)
+ break;
+ else
+ continue;
+ }
+ } // END WAIT LOOP
+
+ // If we had to start up a temporary private state thread to run this
+ // thread plan, shut it down now.
+ if (backup_private_state_thread.IsJoinable()) {
+ StopPrivateStateThread();
+ Status error;
+ m_private_state_thread = backup_private_state_thread;
+ if (stopper_base_plan_sp) {
+ thread->DiscardThreadPlansUpToPlan(stopper_base_plan_sp);
+ }
+ if (old_state != eStateInvalid)
+ m_public_state.SetValueNoLock(old_state);
+ }
+
+ if (return_value != eExpressionCompleted && log) {
+ // Print a backtrace into the log so we can figure out where we are:
+ StreamString s;
+ s.PutCString("Thread state after unsuccessful completion: \n");
+ thread->GetStackFrameStatus(s, 0, UINT32_MAX, true, UINT32_MAX);
+ log->PutString(s.GetString());
+ }
+ // Restore the thread state if we are going to discard the plan execution.
+ // There are three cases where this could happen: 1) The execution
+ // successfully completed 2) We hit a breakpoint, and ignore_breakpoints
+ // was true 3) We got some other error, and discard_on_error was true
+ bool should_unwind = (return_value == eExpressionInterrupted &&
+ options.DoesUnwindOnError()) ||
+ (return_value == eExpressionHitBreakpoint &&
+ options.DoesIgnoreBreakpoints());
+
+ if (return_value == eExpressionCompleted || should_unwind) {
+ thread_plan_sp->RestoreThreadState();
+ }
+
+ // Now do some processing on the results of the run:
+ if (return_value == eExpressionInterrupted ||
+ return_value == eExpressionHitBreakpoint) {
+ if (log) {
+ StreamString s;
+ if (event_sp)
+ event_sp->Dump(&s);
+ else {
+ log->PutCString("Process::RunThreadPlan(): Stop event that "
+ "interrupted us is NULL.");
+ }
+
+ StreamString ts;
+
+ const char *event_explanation = nullptr;
+
+ do {
+ if (!event_sp) {
+ event_explanation = "<no event>";
+ break;
+ } else if (event_sp->GetType() == eBroadcastBitInterrupt) {
+ event_explanation = "<user interrupt>";
+ break;
+ } else {
+ const Process::ProcessEventData *event_data =
+ Process::ProcessEventData::GetEventDataFromEvent(
+ event_sp.get());
+
+ if (!event_data) {
+ event_explanation = "<no event data>";
+ break;
+ }
+
+ Process *process = event_data->GetProcessSP().get();
+
+ if (!process) {
+ event_explanation = "<no process>";
+ break;
+ }
+
+ ThreadList &thread_list = process->GetThreadList();
+
+ uint32_t num_threads = thread_list.GetSize();
+ uint32_t thread_index;
+
+ ts.Printf("<%u threads> ", num_threads);
+
+ for (thread_index = 0; thread_index < num_threads; ++thread_index) {
+ Thread *thread = thread_list.GetThreadAtIndex(thread_index).get();
+
+ if (!thread) {
+ ts.Printf("<?> ");
+ continue;
+ }
+
+ ts.Printf("<0x%4.4" PRIx64 " ", thread->GetID());
+ RegisterContext *register_context =
+ thread->GetRegisterContext().get();
+
+ if (register_context)
+ ts.Printf("[ip 0x%" PRIx64 "] ", register_context->GetPC());
+ else
+ ts.Printf("[ip unknown] ");
+
+ // Show the private stop info here, the public stop info will be
+ // from the last natural stop.
+ lldb::StopInfoSP stop_info_sp = thread->GetPrivateStopInfo();
+ if (stop_info_sp) {
+ const char *stop_desc = stop_info_sp->GetDescription();
+ if (stop_desc)
+ ts.PutCString(stop_desc);
+ }
+ ts.Printf(">");
+ }
+
+ event_explanation = ts.GetData();
+ }
+ } while (false);
+
+ if (event_explanation)
+ log->Printf("Process::RunThreadPlan(): execution interrupted: %s %s",
+ s.GetData(), event_explanation);
+ else
+ log->Printf("Process::RunThreadPlan(): execution interrupted: %s",
+ s.GetData());
+ }
+
+ if (should_unwind) {
+ if (log)
+ log->Printf("Process::RunThreadPlan: ExecutionInterrupted - "
+ "discarding thread plans up to %p.",
+ static_cast<void *>(thread_plan_sp.get()));
+ thread->DiscardThreadPlansUpToPlan(thread_plan_sp);
+ } else {
+ if (log)
+ log->Printf("Process::RunThreadPlan: ExecutionInterrupted - for "
+ "plan: %p not discarding.",
+ static_cast<void *>(thread_plan_sp.get()));
+ }
+ } else if (return_value == eExpressionSetupError) {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): execution set up error.");
+
+ if (options.DoesUnwindOnError()) {
+ thread->DiscardThreadPlansUpToPlan(thread_plan_sp);
+ }
+ } else {
+ if (thread->IsThreadPlanDone(thread_plan_sp.get())) {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): thread plan is done");
+ return_value = eExpressionCompleted;
+ } else if (thread->WasThreadPlanDiscarded(thread_plan_sp.get())) {
+ if (log)
+ log->PutCString(
+ "Process::RunThreadPlan(): thread plan was discarded");
+ return_value = eExpressionDiscarded;
+ } else {
+ if (log)
+ log->PutCString(
+ "Process::RunThreadPlan(): thread plan stopped in mid course");
+ if (options.DoesUnwindOnError() && thread_plan_sp) {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): discarding thread plan "
+ "'cause unwind_on_error is set.");
+ thread->DiscardThreadPlansUpToPlan(thread_plan_sp);
+ }
+ }
+ }
+
+ // Thread we ran the function in may have gone away because we ran the
+ // target Check that it's still there, and if it is put it back in the
+ // context. Also restore the frame in the context if it is still present.
+ thread = GetThreadList().FindThreadByIndexID(thread_idx_id, true).get();
+ if (thread) {
+ exe_ctx.SetFrameSP(thread->GetFrameWithStackID(ctx_frame_id));
+ }
+
+ // Also restore the current process'es selected frame & thread, since this
+ // function calling may be done behind the user's back.
+
+ if (selected_tid != LLDB_INVALID_THREAD_ID) {
+ if (GetThreadList().SetSelectedThreadByIndexID(selected_tid) &&
+ selected_stack_id.IsValid()) {
+ // We were able to restore the selected thread, now restore the frame:
+ std::lock_guard<std::recursive_mutex> guard(GetThreadList().GetMutex());
+ StackFrameSP old_frame_sp =
+ GetThreadList().GetSelectedThread()->GetFrameWithStackID(
+ selected_stack_id);
+ if (old_frame_sp)
+ GetThreadList().GetSelectedThread()->SetSelectedFrame(
+ old_frame_sp.get());
+ }
+ }
+ }
+
+ // If the process exited during the run of the thread plan, notify everyone.
+
+ if (event_to_broadcast_sp) {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): rebroadcasting event.");
+ BroadcastEvent(event_to_broadcast_sp);
+ }
+
+ return return_value;
+}
+
+const char *Process::ExecutionResultAsCString(ExpressionResults result) {
+ const char *result_name;
+
+ switch (result) {
+ case eExpressionCompleted:
+ result_name = "eExpressionCompleted";
+ break;
+ case eExpressionDiscarded:
+ result_name = "eExpressionDiscarded";
+ break;
+ case eExpressionInterrupted:
+ result_name = "eExpressionInterrupted";
+ break;
+ case eExpressionHitBreakpoint:
+ result_name = "eExpressionHitBreakpoint";
+ break;
+ case eExpressionSetupError:
+ result_name = "eExpressionSetupError";
+ break;
+ case eExpressionParseError:
+ result_name = "eExpressionParseError";
+ break;
+ case eExpressionResultUnavailable:
+ result_name = "eExpressionResultUnavailable";
+ break;
+ case eExpressionTimedOut:
+ result_name = "eExpressionTimedOut";
+ break;
+ case eExpressionStoppedForDebug:
+ result_name = "eExpressionStoppedForDebug";
+ break;
+ }
+ return result_name;
+}
+
+void Process::GetStatus(Stream &strm) {
+ const StateType state = GetState();
+ if (StateIsStoppedState(state, false)) {
+ if (state == eStateExited) {
+ int exit_status = GetExitStatus();
+ const char *exit_description = GetExitDescription();
+ strm.Printf("Process %" PRIu64 " exited with status = %i (0x%8.8x) %s\n",
+ GetID(), exit_status, exit_status,
+ exit_description ? exit_description : "");
+ } else {
+ if (state == eStateConnected)
+ strm.Printf("Connected to remote target.\n");
+ else
+ strm.Printf("Process %" PRIu64 " %s\n", GetID(), StateAsCString(state));
+ }
+ } else {
+ strm.Printf("Process %" PRIu64 " is running.\n", GetID());
+ }
+}
+
+size_t Process::GetThreadStatus(Stream &strm,
+ bool only_threads_with_stop_reason,
+ uint32_t start_frame, uint32_t num_frames,
+ uint32_t num_frames_with_source,
+ bool stop_format) {
+ size_t num_thread_infos_dumped = 0;
+
+ // You can't hold the thread list lock while calling Thread::GetStatus. That
+ // very well might run code (e.g. if we need it to get return values or
+ // arguments.) For that to work the process has to be able to acquire it.
+ // So instead copy the thread ID's, and look them up one by one:
+
+ uint32_t num_threads;
+ std::vector<lldb::tid_t> thread_id_array;
+ // Scope for thread list locker;
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetThreadList().GetMutex());
+ ThreadList &curr_thread_list = GetThreadList();
+ num_threads = curr_thread_list.GetSize();
+ uint32_t idx;
+ thread_id_array.resize(num_threads);
+ for (idx = 0; idx < num_threads; ++idx)
+ thread_id_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetID();
+ }
+
+ for (uint32_t i = 0; i < num_threads; i++) {
+ ThreadSP thread_sp(GetThreadList().FindThreadByID(thread_id_array[i]));
+ if (thread_sp) {
+ if (only_threads_with_stop_reason) {
+ StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
+ if (!stop_info_sp || !stop_info_sp->IsValid())
+ continue;
+ }
+ thread_sp->GetStatus(strm, start_frame, num_frames,
+ num_frames_with_source,
+ stop_format);
+ ++num_thread_infos_dumped;
+ } else {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::GetThreadStatus - thread 0x" PRIu64
+ " vanished while running Thread::GetStatus.");
+ }
+ }
+ return num_thread_infos_dumped;
+}
+
+void Process::AddInvalidMemoryRegion(const LoadRange &region) {
+ m_memory_cache.AddInvalidRange(region.GetRangeBase(), region.GetByteSize());
+}
+
+bool Process::RemoveInvalidMemoryRange(const LoadRange &region) {
+ return m_memory_cache.RemoveInvalidRange(region.GetRangeBase(),
+ region.GetByteSize());
+}
+
+void Process::AddPreResumeAction(PreResumeActionCallback callback,
+ void *baton) {
+ m_pre_resume_actions.push_back(PreResumeCallbackAndBaton(callback, baton));
+}
+
+bool Process::RunPreResumeActions() {
+ bool result = true;
+ while (!m_pre_resume_actions.empty()) {
+ struct PreResumeCallbackAndBaton action = m_pre_resume_actions.back();
+ m_pre_resume_actions.pop_back();
+ bool this_result = action.callback(action.baton);
+ if (result)
+ result = this_result;
+ }
+ return result;
+}
+
+void Process::ClearPreResumeActions() { m_pre_resume_actions.clear(); }
+
+void Process::ClearPreResumeAction(PreResumeActionCallback callback, void *baton)
+{
+ PreResumeCallbackAndBaton element(callback, baton);
+ auto found_iter = std::find(m_pre_resume_actions.begin(), m_pre_resume_actions.end(), element);
+ if (found_iter != m_pre_resume_actions.end())
+ {
+ m_pre_resume_actions.erase(found_iter);
+ }
+}
+
+ProcessRunLock &Process::GetRunLock() {
+ if (m_private_state_thread.EqualsThread(Host::GetCurrentThread()))
+ return m_private_run_lock;
+ else
+ return m_public_run_lock;
+}
+
+void Process::Flush() {
+ m_thread_list.Flush();
+ m_extended_thread_list.Flush();
+ m_extended_thread_stop_id = 0;
+ m_queue_list.Clear();
+ m_queue_list_stop_id = 0;
+}
+
+void Process::DidExec() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::%s()", __FUNCTION__);
+
+ Target &target = GetTarget();
+ target.CleanupProcess();
+ target.ClearModules(false);
+ m_dynamic_checkers_up.reset();
+ m_abi_sp.reset();
+ m_system_runtime_up.reset();
+ m_os_up.reset();
+ m_dyld_up.reset();
+ m_jit_loaders_up.reset();
+ m_image_tokens.clear();
+ m_allocated_memory_cache.Clear();
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex);
+ m_language_runtimes.clear();
+ }
+ m_instrumentation_runtimes.clear();
+ m_thread_list.DiscardThreadPlans();
+ m_memory_cache.Clear(true);
+ DoDidExec();
+ CompleteAttach();
+ // Flush the process (threads and all stack frames) after running
+ // CompleteAttach() in case the dynamic loader loaded things in new
+ // locations.
+ Flush();
+
+ // After we figure out what was loaded/unloaded in CompleteAttach, we need to
+ // let the target know so it can do any cleanup it needs to.
+ target.DidExec();
+}
+
+addr_t Process::ResolveIndirectFunction(const Address *address, Status &error) {
+ if (address == nullptr) {
+ error.SetErrorString("Invalid address argument");
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ addr_t function_addr = LLDB_INVALID_ADDRESS;
+
+ addr_t addr = address->GetLoadAddress(&GetTarget());
+ std::map<addr_t, addr_t>::const_iterator iter =
+ m_resolved_indirect_addresses.find(addr);
+ if (iter != m_resolved_indirect_addresses.end()) {
+ function_addr = (*iter).second;
+ } else {
+ if (!InferiorCall(this, address, function_addr)) {
+ Symbol *symbol = address->CalculateSymbolContextSymbol();
+ error.SetErrorStringWithFormat(
+ "Unable to call resolver for indirect function %s",
+ symbol ? symbol->GetName().AsCString() : "<UNKNOWN>");
+ function_addr = LLDB_INVALID_ADDRESS;
+ } else {
+ m_resolved_indirect_addresses.insert(
+ std::pair<addr_t, addr_t>(addr, function_addr));
+ }
+ }
+ return function_addr;
+}
+
+void Process::ModulesDidLoad(ModuleList &module_list) {
+ SystemRuntime *sys_runtime = GetSystemRuntime();
+ if (sys_runtime) {
+ sys_runtime->ModulesDidLoad(module_list);
+ }
+
+ GetJITLoaders().ModulesDidLoad(module_list);
+
+ // Give runtimes a chance to be created.
+ InstrumentationRuntime::ModulesDidLoad(module_list, this,
+ m_instrumentation_runtimes);
+
+ // Tell runtimes about new modules.
+ for (auto pos = m_instrumentation_runtimes.begin();
+ pos != m_instrumentation_runtimes.end(); ++pos) {
+ InstrumentationRuntimeSP runtime = pos->second;
+ runtime->ModulesDidLoad(module_list);
+ }
+
+ // Let any language runtimes we have already created know about the modules
+ // that loaded.
+
+ // Iterate over a copy of this language runtime list in case the language
+ // runtime ModulesDidLoad somehow causes the language runtime to be
+ // unloaded.
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex);
+ LanguageRuntimeCollection language_runtimes(m_language_runtimes);
+ for (const auto &pair : language_runtimes) {
+ // We must check language_runtime_sp to make sure it is not nullptr as we
+ // might cache the fact that we didn't have a language runtime for a
+ // language.
+ LanguageRuntimeSP language_runtime_sp = pair.second;
+ if (language_runtime_sp)
+ language_runtime_sp->ModulesDidLoad(module_list);
+ }
+ }
+
+ // If we don't have an operating system plug-in, try to load one since
+ // loading shared libraries might cause a new one to try and load
+ if (!m_os_up)
+ LoadOperatingSystemPlugin(false);
+
+ // Give structured-data plugins a chance to see the modified modules.
+ for (auto pair : m_structured_data_plugin_map) {
+ if (pair.second)
+ pair.second->ModulesDidLoad(*this, module_list);
+ }
+}
+
+void Process::PrintWarning(uint64_t warning_type, const void *repeat_key,
+ const char *fmt, ...) {
+ bool print_warning = true;
+
+ StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream();
+ if (!stream_sp)
+ return;
+ if (warning_type == eWarningsOptimization && !GetWarningsOptimization()) {
+ return;
+ }
+
+ if (repeat_key != nullptr) {
+ WarningsCollection::iterator it = m_warnings_issued.find(warning_type);
+ if (it == m_warnings_issued.end()) {
+ m_warnings_issued[warning_type] = WarningsPointerSet();
+ m_warnings_issued[warning_type].insert(repeat_key);
+ } else {
+ if (it->second.find(repeat_key) != it->second.end()) {
+ print_warning = false;
+ } else {
+ it->second.insert(repeat_key);
+ }
+ }
+ }
+
+ if (print_warning) {
+ va_list args;
+ va_start(args, fmt);
+ stream_sp->PrintfVarArg(fmt, args);
+ va_end(args);
+ }
+}
+
+void Process::PrintWarningOptimization(const SymbolContext &sc) {
+ if (GetWarningsOptimization() && sc.module_sp &&
+ !sc.module_sp->GetFileSpec().GetFilename().IsEmpty() && sc.function &&
+ sc.function->GetIsOptimized()) {
+ PrintWarning(Process::Warnings::eWarningsOptimization, sc.module_sp.get(),
+ "%s was compiled with optimization - stepping may behave "
+ "oddly; variables may not be available.\n",
+ sc.module_sp->GetFileSpec().GetFilename().GetCString());
+ }
+}
+
+bool Process::GetProcessInfo(ProcessInstanceInfo &info) {
+ info.Clear();
+
+ PlatformSP platform_sp = GetTarget().GetPlatform();
+ if (!platform_sp)
+ return false;
+
+ return platform_sp->GetProcessInfo(GetID(), info);
+}
+
+ThreadCollectionSP Process::GetHistoryThreads(lldb::addr_t addr) {
+ ThreadCollectionSP threads;
+
+ const MemoryHistorySP &memory_history =
+ MemoryHistory::FindPlugin(shared_from_this());
+
+ if (!memory_history) {
+ return threads;
+ }
+
+ threads = std::make_shared<ThreadCollection>(
+ memory_history->GetHistoryThreads(addr));
+
+ return threads;
+}
+
+InstrumentationRuntimeSP
+Process::GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type) {
+ InstrumentationRuntimeCollection::iterator pos;
+ pos = m_instrumentation_runtimes.find(type);
+ if (pos == m_instrumentation_runtimes.end()) {
+ return InstrumentationRuntimeSP();
+ } else
+ return (*pos).second;
+}
+
+bool Process::GetModuleSpec(const FileSpec &module_file_spec,
+ const ArchSpec &arch, ModuleSpec &module_spec) {
+ module_spec.Clear();
+ return false;
+}
+
+size_t Process::AddImageToken(lldb::addr_t image_ptr) {
+ m_image_tokens.push_back(image_ptr);
+ return m_image_tokens.size() - 1;
+}
+
+lldb::addr_t Process::GetImagePtrFromToken(size_t token) const {
+ if (token < m_image_tokens.size())
+ return m_image_tokens[token];
+ return LLDB_INVALID_IMAGE_TOKEN;
+}
+
+void Process::ResetImageToken(size_t token) {
+ if (token < m_image_tokens.size())
+ m_image_tokens[token] = LLDB_INVALID_IMAGE_TOKEN;
+}
+
+Address
+Process::AdvanceAddressToNextBranchInstruction(Address default_stop_addr,
+ AddressRange range_bounds) {
+ Target &target = GetTarget();
+ DisassemblerSP disassembler_sp;
+ InstructionList *insn_list = nullptr;
+
+ Address retval = default_stop_addr;
+
+ if (!target.GetUseFastStepping())
+ return retval;
+ if (!default_stop_addr.IsValid())
+ return retval;
+
+ ExecutionContext exe_ctx(this);
+ const char *plugin_name = nullptr;
+ const char *flavor = nullptr;
+ const bool prefer_file_cache = true;
+ disassembler_sp = Disassembler::DisassembleRange(
+ target.GetArchitecture(), plugin_name, flavor, exe_ctx, range_bounds,
+ prefer_file_cache);
+ if (disassembler_sp)
+ insn_list = &disassembler_sp->GetInstructionList();
+
+ if (insn_list == nullptr) {
+ return retval;
+ }
+
+ size_t insn_offset =
+ insn_list->GetIndexOfInstructionAtAddress(default_stop_addr);
+ if (insn_offset == UINT32_MAX) {
+ return retval;
+ }
+
+ uint32_t branch_index =
+ insn_list->GetIndexOfNextBranchInstruction(insn_offset, target,
+ false /* ignore_calls*/);
+ if (branch_index == UINT32_MAX) {
+ return retval;
+ }
+
+ if (branch_index > insn_offset) {
+ Address next_branch_insn_address =
+ insn_list->GetInstructionAtIndex(branch_index)->GetAddress();
+ if (next_branch_insn_address.IsValid() &&
+ range_bounds.ContainsFileAddress(next_branch_insn_address)) {
+ retval = next_branch_insn_address;
+ }
+ }
+
+ return retval;
+}
+
+Status
+Process::GetMemoryRegions(lldb_private::MemoryRegionInfos &region_list) {
+
+ Status error;
+
+ lldb::addr_t range_end = 0;
+
+ region_list.clear();
+ do {
+ lldb_private::MemoryRegionInfo region_info;
+ error = GetMemoryRegionInfo(range_end, region_info);
+ // GetMemoryRegionInfo should only return an error if it is unimplemented.
+ if (error.Fail()) {
+ region_list.clear();
+ break;
+ }
+
+ range_end = region_info.GetRange().GetRangeEnd();
+ if (region_info.GetMapped() == MemoryRegionInfo::eYes) {
+ region_list.push_back(std::move(region_info));
+ }
+ } while (range_end != LLDB_INVALID_ADDRESS);
+
+ return error;
+}
+
+Status
+Process::ConfigureStructuredData(ConstString type_name,
+ const StructuredData::ObjectSP &config_sp) {
+ // If you get this, the Process-derived class needs to implement a method to
+ // enable an already-reported asynchronous structured data feature. See
+ // ProcessGDBRemote for an example implementation over gdb-remote.
+ return Status("unimplemented");
+}
+
+void Process::MapSupportedStructuredDataPlugins(
+ const StructuredData::Array &supported_type_names) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Bail out early if there are no type names to map.
+ if (supported_type_names.GetSize() == 0) {
+ if (log)
+ log->Printf("Process::%s(): no structured data types supported",
+ __FUNCTION__);
+ return;
+ }
+
+ // Convert StructuredData type names to ConstString instances.
+ std::set<ConstString> const_type_names;
+
+ if (log)
+ log->Printf("Process::%s(): the process supports the following async "
+ "structured data types:",
+ __FUNCTION__);
+
+ supported_type_names.ForEach(
+ [&const_type_names, &log](StructuredData::Object *object) {
+ if (!object) {
+ // Invalid - shouldn't be null objects in the array.
+ return false;
+ }
+
+ auto type_name = object->GetAsString();
+ if (!type_name) {
+ // Invalid format - all type names should be strings.
+ return false;
+ }
+
+ const_type_names.insert(ConstString(type_name->GetValue()));
+ LLDB_LOG(log, "- {0}", type_name->GetValue());
+ return true;
+ });
+
+ // For each StructuredDataPlugin, if the plugin handles any of the types in
+ // the supported_type_names, map that type name to that plugin. Stop when
+ // we've consumed all the type names.
+ // FIXME: should we return an error if there are type names nobody
+ // supports?
+ for (uint32_t plugin_index = 0; !const_type_names.empty(); plugin_index++) {
+ auto create_instance =
+ PluginManager::GetStructuredDataPluginCreateCallbackAtIndex(
+ plugin_index);
+ if (!create_instance)
+ break;
+
+ // Create the plugin.
+ StructuredDataPluginSP plugin_sp = (*create_instance)(*this);
+ if (!plugin_sp) {
+ // This plugin doesn't think it can work with the process. Move on to the
+ // next.
+ continue;
+ }
+
+ // For any of the remaining type names, map any that this plugin supports.
+ std::vector<ConstString> names_to_remove;
+ for (auto &type_name : const_type_names) {
+ if (plugin_sp->SupportsStructuredDataType(type_name)) {
+ m_structured_data_plugin_map.insert(
+ std::make_pair(type_name, plugin_sp));
+ names_to_remove.push_back(type_name);
+ if (log)
+ log->Printf("Process::%s(): using plugin %s for type name "
+ "%s",
+ __FUNCTION__, plugin_sp->GetPluginName().GetCString(),
+ type_name.GetCString());
+ }
+ }
+
+ // Remove the type names that were consumed by this plugin.
+ for (auto &type_name : names_to_remove)
+ const_type_names.erase(type_name);
+ }
+}
+
+bool Process::RouteAsyncStructuredData(
+ const StructuredData::ObjectSP object_sp) {
+ // Nothing to do if there's no data.
+ if (!object_sp)
+ return false;
+
+ // The contract is this must be a dictionary, so we can look up the routing
+ // key via the top-level 'type' string value within the dictionary.
+ StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary();
+ if (!dictionary)
+ return false;
+
+ // Grab the async structured type name (i.e. the feature/plugin name).
+ ConstString type_name;
+ if (!dictionary->GetValueForKeyAsString("type", type_name))
+ return false;
+
+ // Check if there's a plugin registered for this type name.
+ auto find_it = m_structured_data_plugin_map.find(type_name);
+ if (find_it == m_structured_data_plugin_map.end()) {
+ // We don't have a mapping for this structured data type.
+ return false;
+ }
+
+ // Route the structured data to the plugin.
+ find_it->second->HandleArrivalOfStructuredData(*this, type_name, object_sp);
+ return true;
+}
+
+Status Process::UpdateAutomaticSignalFiltering() {
+ // Default implementation does nothign.
+ // No automatic signal filtering to speak of.
+ return Status();
+}
+
+UtilityFunction *Process::GetLoadImageUtilityFunction(
+ Platform *platform,
+ llvm::function_ref<std::unique_ptr<UtilityFunction>()> factory) {
+ if (platform != GetTarget().GetPlatform().get())
+ return nullptr;
+ std::call_once(m_dlopen_utility_func_flag_once,
+ [&] { m_dlopen_utility_func_up = factory(); });
+ return m_dlopen_utility_func_up.get();
+}
diff --git a/contrib/llvm-project/lldb/source/Target/Queue.cpp b/contrib/llvm-project/lldb/source/Target/Queue.cpp
new file mode 100644
index 000000000000..fc2a93dbe899
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/Queue.cpp
@@ -0,0 +1,89 @@
+//===-- Queue.cpp -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/Queue.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/QueueList.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+Queue::Queue(ProcessSP process_sp, lldb::queue_id_t queue_id,
+ const char *queue_name)
+ : m_process_wp(), m_queue_id(queue_id), m_queue_name(),
+ m_running_work_items_count(0), m_pending_work_items_count(0),
+ m_pending_items(), m_dispatch_queue_t_addr(LLDB_INVALID_ADDRESS),
+ m_kind(eQueueKindUnknown) {
+ if (queue_name)
+ m_queue_name = queue_name;
+
+ m_process_wp = process_sp;
+}
+
+Queue::~Queue() = default;
+
+queue_id_t Queue::GetID() { return m_queue_id; }
+
+const char *Queue::GetName() {
+ return (m_queue_name.empty() ? nullptr : m_queue_name.c_str());
+}
+
+uint32_t Queue::GetIndexID() { return m_queue_id; }
+
+std::vector<lldb::ThreadSP> Queue::GetThreads() {
+ std::vector<ThreadSP> result;
+ ProcessSP process_sp = m_process_wp.lock();
+ if (process_sp) {
+ for (ThreadSP thread_sp : process_sp->Threads()) {
+ if (thread_sp->GetQueueID() == m_queue_id) {
+ result.push_back(thread_sp);
+ }
+ }
+ }
+ return result;
+}
+
+void Queue::SetNumRunningWorkItems(uint32_t count) {
+ m_running_work_items_count = count;
+}
+
+uint32_t Queue::GetNumRunningWorkItems() const {
+ return m_running_work_items_count;
+}
+
+void Queue::SetNumPendingWorkItems(uint32_t count) {
+ m_pending_work_items_count = count;
+}
+
+uint32_t Queue::GetNumPendingWorkItems() const {
+ return m_pending_work_items_count;
+}
+
+void Queue::SetLibdispatchQueueAddress(addr_t dispatch_queue_t_addr) {
+ m_dispatch_queue_t_addr = dispatch_queue_t_addr;
+}
+
+addr_t Queue::GetLibdispatchQueueAddress() const {
+ return m_dispatch_queue_t_addr;
+}
+
+const std::vector<lldb::QueueItemSP> &Queue::GetPendingItems() {
+ if (m_pending_items.empty()) {
+ ProcessSP process_sp = m_process_wp.lock();
+ if (process_sp && process_sp->GetSystemRuntime()) {
+ process_sp->GetSystemRuntime()->PopulatePendingItemsForQueue(this);
+ }
+ }
+ return m_pending_items;
+}
+
+lldb::QueueKind Queue::GetKind() { return m_kind; }
+
+void Queue::SetKind(lldb::QueueKind kind) { m_kind = kind; }
diff --git a/contrib/llvm-project/lldb/source/Target/QueueItem.cpp b/contrib/llvm-project/lldb/source/Target/QueueItem.cpp
new file mode 100644
index 000000000000..47ff9e028fcb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/QueueItem.cpp
@@ -0,0 +1,106 @@
+//===-- QueueItem.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/Queue.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/QueueItem.h"
+#include "lldb/Target/SystemRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+QueueItem::QueueItem(QueueSP queue_sp, ProcessSP process_sp,
+ lldb::addr_t item_ref, lldb_private::Address address)
+ : m_queue_wp(), m_process_wp(), m_item_ref(item_ref), m_address(address),
+ m_have_fetched_entire_item(false), m_kind(eQueueItemKindUnknown),
+ m_item_that_enqueued_this_ref(LLDB_INVALID_ADDRESS),
+ m_enqueueing_thread_id(LLDB_INVALID_THREAD_ID),
+ m_enqueueing_queue_id(LLDB_INVALID_QUEUE_ID),
+ m_target_queue_id(LLDB_INVALID_QUEUE_ID), m_stop_id(0), m_backtrace(),
+ m_thread_label(), m_queue_label(), m_target_queue_label() {
+ m_queue_wp = queue_sp;
+ m_process_wp = process_sp;
+}
+
+QueueItem::~QueueItem() {}
+
+QueueItemKind QueueItem::GetKind() {
+ FetchEntireItem();
+ return m_kind;
+}
+
+void QueueItem::SetKind(QueueItemKind item_kind) { m_kind = item_kind; }
+
+Address &QueueItem::GetAddress() { return m_address; }
+
+void QueueItem::SetAddress(Address addr) { m_address = addr; }
+
+ThreadSP QueueItem::GetExtendedBacktraceThread(ConstString type) {
+ FetchEntireItem();
+ ThreadSP return_thread;
+ QueueSP queue_sp = m_queue_wp.lock();
+ if (queue_sp) {
+ ProcessSP process_sp = queue_sp->GetProcess();
+ if (process_sp && process_sp->GetSystemRuntime()) {
+ return_thread =
+ process_sp->GetSystemRuntime()->GetExtendedBacktraceForQueueItem(
+ this->shared_from_this(), type);
+ }
+ }
+ return return_thread;
+}
+
+lldb::addr_t QueueItem::GetItemThatEnqueuedThis() {
+ FetchEntireItem();
+ return m_item_that_enqueued_this_ref;
+}
+
+lldb::tid_t QueueItem::GetEnqueueingThreadID() {
+ FetchEntireItem();
+ return m_enqueueing_thread_id;
+}
+
+lldb::queue_id_t QueueItem::GetEnqueueingQueueID() {
+ FetchEntireItem();
+ return m_enqueueing_queue_id;
+}
+
+uint32_t QueueItem::GetStopID() {
+ FetchEntireItem();
+ return m_stop_id;
+}
+
+std::vector<lldb::addr_t> &QueueItem::GetEnqueueingBacktrace() {
+ FetchEntireItem();
+ return m_backtrace;
+}
+
+std::string QueueItem::GetThreadLabel() {
+ FetchEntireItem();
+ return m_thread_label;
+}
+
+std::string QueueItem::GetQueueLabel() {
+ FetchEntireItem();
+ return m_queue_label;
+}
+
+ProcessSP QueueItem::GetProcessSP() { return m_process_wp.lock(); }
+
+void QueueItem::FetchEntireItem() {
+ if (m_have_fetched_entire_item)
+ return;
+ ProcessSP process_sp = m_process_wp.lock();
+ if (process_sp) {
+ SystemRuntime *runtime = process_sp->GetSystemRuntime();
+ if (runtime) {
+ runtime->CompleteQueueItem(this, m_item_ref);
+ m_have_fetched_entire_item = true;
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Target/QueueList.cpp b/contrib/llvm-project/lldb/source/Target/QueueList.cpp
new file mode 100644
index 000000000000..796825135187
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/QueueList.cpp
@@ -0,0 +1,69 @@
+//===-- QueueList.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/Queue.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/QueueList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+QueueList::QueueList(Process *process)
+ : m_process(process), m_stop_id(0), m_queues(), m_mutex() {}
+
+QueueList::~QueueList() { Clear(); }
+
+uint32_t QueueList::GetSize() {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ return m_queues.size();
+}
+
+lldb::QueueSP QueueList::GetQueueAtIndex(uint32_t idx) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ if (idx < m_queues.size()) {
+ return m_queues[idx];
+ } else {
+ return QueueSP();
+ }
+}
+
+void QueueList::Clear() {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ m_queues.clear();
+}
+
+void QueueList::AddQueue(QueueSP queue_sp) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ if (queue_sp.get()) {
+ m_queues.push_back(queue_sp);
+ }
+}
+
+lldb::QueueSP QueueList::FindQueueByID(lldb::queue_id_t qid) {
+ QueueSP ret;
+ for (QueueSP queue_sp : Queues()) {
+ if (queue_sp->GetID() == qid) {
+ ret = queue_sp;
+ break;
+ }
+ }
+ return ret;
+}
+
+lldb::QueueSP QueueList::FindQueueByIndexID(uint32_t index_id) {
+ QueueSP ret;
+ for (QueueSP queue_sp : Queues()) {
+ if (queue_sp->GetIndexID() == index_id) {
+ ret = queue_sp;
+ break;
+ }
+ }
+ return ret;
+}
+
+std::mutex &QueueList::GetMutex() { return m_mutex; }
diff --git a/contrib/llvm-project/lldb/source/Target/RegisterContext.cpp b/contrib/llvm-project/lldb/source/Target/RegisterContext.cpp
new file mode 100644
index 000000000000..c960260f30c8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/RegisterContext.cpp
@@ -0,0 +1,449 @@
+//===-- RegisterContext.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContext::RegisterContext(Thread &thread, uint32_t concrete_frame_idx)
+ : m_thread(thread), m_concrete_frame_idx(concrete_frame_idx),
+ m_stop_id(thread.GetProcess()->GetStopID()) {}
+
+RegisterContext::~RegisterContext() = default;
+
+void RegisterContext::InvalidateIfNeeded(bool force) {
+ ProcessSP process_sp(m_thread.GetProcess());
+ bool invalidate = force;
+ uint32_t process_stop_id = UINT32_MAX;
+
+ if (process_sp)
+ process_stop_id = process_sp->GetStopID();
+ else
+ invalidate = true;
+
+ if (!invalidate)
+ invalidate = process_stop_id != GetStopID();
+
+ if (invalidate) {
+ InvalidateAllRegisters();
+ SetStopID(process_stop_id);
+ }
+}
+
+const RegisterInfo *
+RegisterContext::GetRegisterInfoByName(llvm::StringRef reg_name,
+ uint32_t start_idx) {
+ if (reg_name.empty())
+ return nullptr;
+
+ const uint32_t num_registers = GetRegisterCount();
+ for (uint32_t reg = start_idx; reg < num_registers; ++reg) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+
+ if (reg_name.equals_lower(reg_info->name) ||
+ reg_name.equals_lower(reg_info->alt_name))
+ return reg_info;
+ }
+ return nullptr;
+}
+
+uint32_t
+RegisterContext::UpdateDynamicRegisterSize(const lldb_private::ArchSpec &arch,
+ RegisterInfo *reg_info) {
+ ExecutionContext exe_ctx(CalculateThread());
+
+ // In MIPS, the floating point registers size is depends on FR bit of SR
+ // register. if SR.FR == 1 then all floating point registers are 64 bits.
+ // else they are all 32 bits.
+
+ int expr_result;
+ uint32_t addr_size = arch.GetAddressByteSize();
+ const uint8_t *dwarf_opcode_ptr = reg_info->dynamic_size_dwarf_expr_bytes;
+ const size_t dwarf_opcode_len = reg_info->dynamic_size_dwarf_len;
+
+ DataExtractor dwarf_data(dwarf_opcode_ptr, dwarf_opcode_len,
+ arch.GetByteOrder(), addr_size);
+ ModuleSP opcode_ctx;
+ DWARFExpression dwarf_expr(opcode_ctx, dwarf_data, nullptr, 0,
+ dwarf_opcode_len);
+ Value result;
+ Status error;
+ const lldb::offset_t offset = 0;
+ if (dwarf_expr.Evaluate(&exe_ctx, this, opcode_ctx, dwarf_data, nullptr,
+ offset, dwarf_opcode_len, eRegisterKindDWARF, nullptr,
+ nullptr, result, &error)) {
+ expr_result = result.GetScalar().SInt(-1);
+ switch (expr_result) {
+ case 0:
+ return 4;
+ case 1:
+ return 8;
+ default:
+ return reg_info->byte_size;
+ }
+ } else {
+ printf("Error executing DwarfExpression::Evaluate %s\n", error.AsCString());
+ return reg_info->byte_size;
+ }
+}
+
+const RegisterInfo *RegisterContext::GetRegisterInfo(lldb::RegisterKind kind,
+ uint32_t num) {
+ const uint32_t reg_num = ConvertRegisterKindToRegisterNumber(kind, num);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return nullptr;
+ return GetRegisterInfoAtIndex(reg_num);
+}
+
+const char *RegisterContext::GetRegisterName(uint32_t reg) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info)
+ return reg_info->name;
+ return nullptr;
+}
+
+uint64_t RegisterContext::GetPC(uint64_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+ uint64_t pc = ReadRegisterAsUnsigned(reg, fail_value);
+
+ if (pc != fail_value) {
+ TargetSP target_sp = m_thread.CalculateTarget();
+ if (target_sp) {
+ Target *target = target_sp.get();
+ if (target)
+ pc = target->GetOpcodeLoadAddress(pc, AddressClass::eCode);
+ }
+ }
+
+ return pc;
+}
+
+bool RegisterContext::SetPC(uint64_t pc) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+ bool success = WriteRegisterFromUnsigned(reg, pc);
+ if (success) {
+ StackFrameSP frame_sp(
+ m_thread.GetFrameWithConcreteFrameIndex(m_concrete_frame_idx));
+ if (frame_sp)
+ frame_sp->ChangePC(pc);
+ else
+ m_thread.ClearStackFrames();
+ }
+ return success;
+}
+
+bool RegisterContext::SetPC(Address addr) {
+ TargetSP target_sp = m_thread.CalculateTarget();
+ Target *target = target_sp.get();
+
+ lldb::addr_t callAddr = addr.GetCallableLoadAddress(target);
+ if (callAddr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ return SetPC(callAddr);
+}
+
+uint64_t RegisterContext::GetSP(uint64_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+bool RegisterContext::SetSP(uint64_t sp) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP);
+ return WriteRegisterFromUnsigned(reg, sp);
+}
+
+uint64_t RegisterContext::GetFP(uint64_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FP);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+bool RegisterContext::SetFP(uint64_t fp) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FP);
+ return WriteRegisterFromUnsigned(reg, fp);
+}
+
+uint64_t RegisterContext::GetReturnAddress(uint64_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+uint64_t RegisterContext::GetFlags(uint64_t fail_value) {
+ uint32_t reg = ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS);
+ return ReadRegisterAsUnsigned(reg, fail_value);
+}
+
+uint64_t RegisterContext::ReadRegisterAsUnsigned(uint32_t reg,
+ uint64_t fail_value) {
+ if (reg != LLDB_INVALID_REGNUM)
+ return ReadRegisterAsUnsigned(GetRegisterInfoAtIndex(reg), fail_value);
+ return fail_value;
+}
+
+uint64_t RegisterContext::ReadRegisterAsUnsigned(const RegisterInfo *reg_info,
+ uint64_t fail_value) {
+ if (reg_info) {
+ RegisterValue value;
+ if (ReadRegister(reg_info, value))
+ return value.GetAsUInt64();
+ }
+ return fail_value;
+}
+
+bool RegisterContext::WriteRegisterFromUnsigned(uint32_t reg, uint64_t uval) {
+ if (reg == LLDB_INVALID_REGNUM)
+ return false;
+ return WriteRegisterFromUnsigned(GetRegisterInfoAtIndex(reg), uval);
+}
+
+bool RegisterContext::WriteRegisterFromUnsigned(const RegisterInfo *reg_info,
+ uint64_t uval) {
+ if (reg_info) {
+ RegisterValue value;
+ if (value.SetUInt(uval, reg_info->byte_size))
+ return WriteRegister(reg_info, value);
+ }
+ return false;
+}
+
+bool RegisterContext::CopyFromRegisterContext(lldb::RegisterContextSP context) {
+ uint32_t num_register_sets = context->GetRegisterSetCount();
+ // We don't know that two threads have the same register context, so require
+ // the threads to be the same.
+ if (context->GetThreadID() != GetThreadID())
+ return false;
+
+ if (num_register_sets != GetRegisterSetCount())
+ return false;
+
+ RegisterContextSP frame_zero_context = m_thread.GetRegisterContext();
+
+ for (uint32_t set_idx = 0; set_idx < num_register_sets; ++set_idx) {
+ const RegisterSet *const reg_set = GetRegisterSet(set_idx);
+
+ const uint32_t num_registers = reg_set->num_registers;
+ for (uint32_t reg_idx = 0; reg_idx < num_registers; ++reg_idx) {
+ const uint32_t reg = reg_set->registers[reg_idx];
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (!reg_info || reg_info->value_regs)
+ continue;
+ RegisterValue reg_value;
+
+ // If we can reconstruct the register from the frame we are copying from,
+ // then do so, otherwise use the value from frame 0.
+ if (context->ReadRegister(reg_info, reg_value)) {
+ WriteRegister(reg_info, reg_value);
+ } else if (frame_zero_context->ReadRegister(reg_info, reg_value)) {
+ WriteRegister(reg_info, reg_value);
+ }
+ }
+ }
+ return true;
+}
+
+lldb::tid_t RegisterContext::GetThreadID() const { return m_thread.GetID(); }
+
+uint32_t RegisterContext::NumSupportedHardwareBreakpoints() { return 0; }
+
+uint32_t RegisterContext::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContext::ClearHardwareBreakpoint(uint32_t hw_idx) { return false; }
+
+uint32_t RegisterContext::NumSupportedHardwareWatchpoints() { return 0; }
+
+uint32_t RegisterContext::SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
+ bool read, bool write) {
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContext::ClearHardwareWatchpoint(uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContext::HardwareSingleStep(bool enable) { return false; }
+
+Status RegisterContext::ReadRegisterValueFromMemory(
+ const RegisterInfo *reg_info, lldb::addr_t src_addr, uint32_t src_len,
+ RegisterValue &reg_value) {
+ Status error;
+ if (reg_info == nullptr) {
+ error.SetErrorString("invalid register info argument.");
+ return error;
+ }
+
+ // Moving from addr into a register
+ //
+ // Case 1: src_len == dst_len
+ //
+ // |AABBCCDD| Address contents
+ // |AABBCCDD| Register contents
+ //
+ // Case 2: src_len > dst_len
+ //
+ // Status! (The register should always be big enough to hold the data)
+ //
+ // Case 3: src_len < dst_len
+ //
+ // |AABB| Address contents
+ // |AABB0000| Register contents [on little-endian hardware]
+ // |0000AABB| Register contents [on big-endian hardware]
+ if (src_len > RegisterValue::kMaxRegisterByteSize) {
+ error.SetErrorString("register too small to receive memory data");
+ return error;
+ }
+
+ const uint32_t dst_len = reg_info->byte_size;
+
+ if (src_len > dst_len) {
+ error.SetErrorStringWithFormat(
+ "%u bytes is too big to store in register %s (%u bytes)", src_len,
+ reg_info->name, dst_len);
+ return error;
+ }
+
+ ProcessSP process_sp(m_thread.GetProcess());
+ if (process_sp) {
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Read the memory
+ const uint32_t bytes_read =
+ process_sp->ReadMemory(src_addr, src, src_len, error);
+
+ // Make sure the memory read succeeded...
+ if (bytes_read != src_len) {
+ if (error.Success()) {
+ // This might happen if we read _some_ bytes but not all
+ error.SetErrorStringWithFormat("read %u of %u bytes", bytes_read,
+ src_len);
+ }
+ return error;
+ }
+
+ // We now have a memory buffer that contains the part or all of the
+ // register value. Set the register value using this memory data.
+ // TODO: we might need to add a parameter to this function in case the byte
+ // order of the memory data doesn't match the process. For now we are
+ // assuming they are the same.
+ reg_value.SetFromMemoryData(reg_info, src, src_len,
+ process_sp->GetByteOrder(), error);
+ } else
+ error.SetErrorString("invalid process");
+
+ return error;
+}
+
+Status RegisterContext::WriteRegisterValueToMemory(
+ const RegisterInfo *reg_info, lldb::addr_t dst_addr, uint32_t dst_len,
+ const RegisterValue &reg_value) {
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ Status error;
+
+ ProcessSP process_sp(m_thread.GetProcess());
+ if (process_sp) {
+
+ // TODO: we might need to add a parameter to this function in case the byte
+ // order of the memory data doesn't match the process. For now we are
+ // assuming they are the same.
+
+ const uint32_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, dst, dst_len, process_sp->GetByteOrder(), error);
+
+ if (error.Success()) {
+ if (bytes_copied == 0) {
+ error.SetErrorString("byte copy failed.");
+ } else {
+ const uint32_t bytes_written =
+ process_sp->WriteMemory(dst_addr, dst, bytes_copied, error);
+ if (bytes_written != bytes_copied) {
+ if (error.Success()) {
+ // This might happen if we read _some_ bytes but not all
+ error.SetErrorStringWithFormat("only wrote %u of %u bytes",
+ bytes_written, bytes_copied);
+ }
+ }
+ }
+ }
+ } else
+ error.SetErrorString("invalid process");
+
+ return error;
+}
+
+bool RegisterContext::ReadAllRegisterValues(
+ lldb_private::RegisterCheckpoint &reg_checkpoint) {
+ return ReadAllRegisterValues(reg_checkpoint.GetData());
+}
+
+bool RegisterContext::WriteAllRegisterValues(
+ const lldb_private::RegisterCheckpoint &reg_checkpoint) {
+ return WriteAllRegisterValues(reg_checkpoint.GetData());
+}
+
+TargetSP RegisterContext::CalculateTarget() {
+ return m_thread.CalculateTarget();
+}
+
+ProcessSP RegisterContext::CalculateProcess() {
+ return m_thread.CalculateProcess();
+}
+
+ThreadSP RegisterContext::CalculateThread() {
+ return m_thread.shared_from_this();
+}
+
+StackFrameSP RegisterContext::CalculateStackFrame() {
+ // Register contexts might belong to many frames if we have inlined functions
+ // inside a frame since all inlined functions share the same registers, so we
+ // can't definitively say which frame we come from...
+ return StackFrameSP();
+}
+
+void RegisterContext::CalculateExecutionContext(ExecutionContext &exe_ctx) {
+ m_thread.CalculateExecutionContext(exe_ctx);
+}
+
+bool RegisterContext::ConvertBetweenRegisterKinds(lldb::RegisterKind source_rk,
+ uint32_t source_regnum,
+ lldb::RegisterKind target_rk,
+ uint32_t &target_regnum) {
+ const uint32_t num_registers = GetRegisterCount();
+ for (uint32_t reg = 0; reg < num_registers; ++reg) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+
+ if (reg_info->kinds[source_rk] == source_regnum) {
+ target_regnum = reg_info->kinds[target_rk];
+ return (target_regnum != LLDB_INVALID_REGNUM);
+ }
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/RegisterNumber.cpp b/contrib/llvm-project/lldb/source/Target/RegisterNumber.cpp
new file mode 100644
index 000000000000..63b58d3582fd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/RegisterNumber.cpp
@@ -0,0 +1,107 @@
+//===--------------------- RegisterNumber.cpp -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/RegisterNumber.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb_private;
+
+RegisterNumber::RegisterNumber(lldb_private::Thread &thread,
+ lldb::RegisterKind kind, uint32_t num)
+ : m_reg_ctx_sp(thread.GetRegisterContext()), m_regnum(num), m_kind(kind),
+ m_kind_regnum_map(), m_name("") {
+ if (m_reg_ctx_sp.get()) {
+ const lldb_private::RegisterInfo *reginfo =
+ m_reg_ctx_sp->GetRegisterInfoAtIndex(
+ GetAsKind(lldb::eRegisterKindLLDB));
+ if (reginfo && reginfo->name) {
+ m_name = reginfo->name;
+ }
+ }
+}
+
+RegisterNumber::RegisterNumber()
+ : m_reg_ctx_sp(), m_regnum(LLDB_INVALID_REGNUM),
+ m_kind(lldb::kNumRegisterKinds), m_kind_regnum_map(), m_name(nullptr) {}
+
+void RegisterNumber::init(lldb_private::Thread &thread, lldb::RegisterKind kind,
+ uint32_t num) {
+ m_reg_ctx_sp = thread.GetRegisterContext();
+ m_regnum = num;
+ m_kind = kind;
+ if (m_reg_ctx_sp.get()) {
+ const lldb_private::RegisterInfo *reginfo =
+ m_reg_ctx_sp->GetRegisterInfoAtIndex(
+ GetAsKind(lldb::eRegisterKindLLDB));
+ if (reginfo && reginfo->name) {
+ m_name = reginfo->name;
+ }
+ }
+}
+
+const RegisterNumber &RegisterNumber::operator=(const RegisterNumber &rhs) {
+ m_reg_ctx_sp = rhs.m_reg_ctx_sp;
+ m_regnum = rhs.m_regnum;
+ m_kind = rhs.m_kind;
+ for (auto it : rhs.m_kind_regnum_map)
+ m_kind_regnum_map[it.first] = it.second;
+ m_name = rhs.m_name;
+ return *this;
+}
+
+bool RegisterNumber::operator==(RegisterNumber &rhs) {
+ if (IsValid() != rhs.IsValid())
+ return false;
+
+ if (m_kind == rhs.m_kind) {
+ return m_regnum == rhs.m_regnum;
+ }
+
+ uint32_t rhs_regnum = rhs.GetAsKind(m_kind);
+ if (rhs_regnum != LLDB_INVALID_REGNUM) {
+ return m_regnum == rhs_regnum;
+ }
+ uint32_t lhs_regnum = GetAsKind(rhs.m_kind);
+ { return lhs_regnum == rhs.m_regnum; }
+ return false;
+}
+
+bool RegisterNumber::operator!=(RegisterNumber &rhs) { return !(*this == rhs); }
+
+bool RegisterNumber::IsValid() const {
+ return m_reg_ctx_sp.get() && m_kind != lldb::kNumRegisterKinds &&
+ m_regnum != LLDB_INVALID_REGNUM;
+}
+
+uint32_t RegisterNumber::GetAsKind(lldb::RegisterKind kind) {
+ if (m_regnum == LLDB_INVALID_REGNUM)
+ return LLDB_INVALID_REGNUM;
+
+ if (kind == m_kind)
+ return m_regnum;
+
+ Collection::iterator iter = m_kind_regnum_map.find(kind);
+ if (iter != m_kind_regnum_map.end()) {
+ return iter->second;
+ }
+ uint32_t output_regnum = LLDB_INVALID_REGNUM;
+ if (m_reg_ctx_sp &&
+ m_reg_ctx_sp->ConvertBetweenRegisterKinds(m_kind, m_regnum, kind,
+ output_regnum) &&
+ output_regnum != LLDB_INVALID_REGNUM) {
+ m_kind_regnum_map[kind] = output_regnum;
+ }
+ return output_regnum;
+}
+
+uint32_t RegisterNumber::GetRegisterNumber() const { return m_regnum; }
+
+lldb::RegisterKind RegisterNumber::GetRegisterKind() const { return m_kind; }
+
+const char *RegisterNumber::GetName() { return m_name; }
diff --git a/contrib/llvm-project/lldb/source/Target/RemoteAwarePlatform.cpp b/contrib/llvm-project/lldb/source/Target/RemoteAwarePlatform.cpp
new file mode 100644
index 000000000000..1704e1533e9c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/RemoteAwarePlatform.cpp
@@ -0,0 +1,284 @@
+//===-- RemoteAwarePlatform.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/RemoteAwarePlatform.h"
+#include "lldb/Host/FileCache.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+
+using namespace lldb_private;
+
+bool RemoteAwarePlatform::GetModuleSpec(const FileSpec &module_file_spec,
+ const ArchSpec &arch,
+ ModuleSpec &module_spec) {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetModuleSpec(module_file_spec, arch,
+ module_spec);
+
+ return Platform::GetModuleSpec(module_file_spec, arch, module_spec);
+}
+
+Status RemoteAwarePlatform::RunShellCommand(
+ const char *command, const FileSpec &working_dir, int *status_ptr,
+ int *signo_ptr, std::string *command_output,
+ const Timeout<std::micro> &timeout) {
+ if (IsHost())
+ return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr,
+ command_output, timeout);
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->RunShellCommand(
+ command, working_dir, status_ptr, signo_ptr, command_output, timeout);
+ return Status("unable to run a remote command without a platform");
+}
+
+Status RemoteAwarePlatform::MakeDirectory(const FileSpec &file_spec,
+ uint32_t file_permissions) {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->MakeDirectory(file_spec, file_permissions);
+ return Platform::MakeDirectory(file_spec, file_permissions);
+}
+
+Status RemoteAwarePlatform::GetFilePermissions(const FileSpec &file_spec,
+ uint32_t &file_permissions) {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetFilePermissions(file_spec,
+ file_permissions);
+ return Platform::GetFilePermissions(file_spec, file_permissions);
+}
+
+Status RemoteAwarePlatform::SetFilePermissions(const FileSpec &file_spec,
+ uint32_t file_permissions) {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->SetFilePermissions(file_spec,
+ file_permissions);
+ return Platform::SetFilePermissions(file_spec, file_permissions);
+}
+
+lldb::user_id_t RemoteAwarePlatform::OpenFile(const FileSpec &file_spec,
+ uint32_t flags, uint32_t mode,
+ Status &error) {
+ if (IsHost())
+ return FileCache::GetInstance().OpenFile(file_spec, flags, mode, error);
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error);
+ return Platform::OpenFile(file_spec, flags, mode, error);
+}
+
+bool RemoteAwarePlatform::CloseFile(lldb::user_id_t fd, Status &error) {
+ if (IsHost())
+ return FileCache::GetInstance().CloseFile(fd, error);
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->CloseFile(fd, error);
+ return Platform::CloseFile(fd, error);
+}
+
+uint64_t RemoteAwarePlatform::ReadFile(lldb::user_id_t fd, uint64_t offset,
+ void *dst, uint64_t dst_len,
+ Status &error) {
+ if (IsHost())
+ return FileCache::GetInstance().ReadFile(fd, offset, dst, dst_len, error);
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->ReadFile(fd, offset, dst, dst_len, error);
+ return Platform::ReadFile(fd, offset, dst, dst_len, error);
+}
+
+uint64_t RemoteAwarePlatform::WriteFile(lldb::user_id_t fd, uint64_t offset,
+ const void *src, uint64_t src_len,
+ Status &error) {
+ if (IsHost())
+ return FileCache::GetInstance().WriteFile(fd, offset, src, src_len, error);
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->WriteFile(fd, offset, src, src_len, error);
+ return Platform::WriteFile(fd, offset, src, src_len, error);
+}
+
+lldb::user_id_t RemoteAwarePlatform::GetFileSize(const FileSpec &file_spec) {
+ if (IsHost()) {
+ uint64_t Size;
+ if (llvm::sys::fs::file_size(file_spec.GetPath(), Size))
+ return 0;
+ return Size;
+ }
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetFileSize(file_spec);
+ return Platform::GetFileSize(file_spec);
+}
+
+Status RemoteAwarePlatform::CreateSymlink(const FileSpec &src,
+ const FileSpec &dst) {
+ if (IsHost())
+ return FileSystem::Instance().Symlink(src, dst);
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->CreateSymlink(src, dst);
+ return Platform::CreateSymlink(src, dst);
+}
+
+bool RemoteAwarePlatform::GetFileExists(const FileSpec &file_spec) {
+ if (IsHost())
+ return FileSystem::Instance().Exists(file_spec);
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetFileExists(file_spec);
+ return Platform::GetFileExists(file_spec);
+}
+
+Status RemoteAwarePlatform::Unlink(const FileSpec &file_spec) {
+ if (IsHost())
+ return llvm::sys::fs::remove(file_spec.GetPath());
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->Unlink(file_spec);
+ return Platform::Unlink(file_spec);
+}
+
+bool RemoteAwarePlatform::CalculateMD5(const FileSpec &file_spec, uint64_t &low,
+ uint64_t &high) {
+ if (IsHost())
+ return Platform::CalculateMD5(file_spec, low, high);
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->CalculateMD5(file_spec, low, high);
+ return false;
+}
+
+FileSpec RemoteAwarePlatform::GetRemoteWorkingDirectory() {
+ if (IsRemote() && m_remote_platform_sp)
+ return m_remote_platform_sp->GetRemoteWorkingDirectory();
+ return Platform::GetRemoteWorkingDirectory();
+}
+
+bool RemoteAwarePlatform::SetRemoteWorkingDirectory(
+ const FileSpec &working_dir) {
+ if (IsRemote() && m_remote_platform_sp)
+ return m_remote_platform_sp->SetRemoteWorkingDirectory(working_dir);
+ return Platform::SetRemoteWorkingDirectory(working_dir);
+}
+
+Status RemoteAwarePlatform::GetFileWithUUID(const FileSpec &platform_file,
+ const UUID *uuid_ptr,
+ FileSpec &local_file) {
+ if (IsRemote() && m_remote_platform_sp)
+ return m_remote_platform_sp->GetFileWithUUID(platform_file, uuid_ptr,
+ local_file);
+
+ // Default to the local case
+ local_file = platform_file;
+ return Status();
+}
+
+bool RemoteAwarePlatform::GetRemoteOSVersion() {
+ if (m_remote_platform_sp) {
+ m_os_version = m_remote_platform_sp->GetOSVersion();
+ return !m_os_version.empty();
+ }
+ return false;
+}
+
+bool RemoteAwarePlatform::GetRemoteOSBuildString(std::string &s) {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetRemoteOSBuildString(s);
+ s.clear();
+ return false;
+}
+
+bool RemoteAwarePlatform::GetRemoteOSKernelDescription(std::string &s) {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetRemoteOSKernelDescription(s);
+ s.clear();
+ return false;
+}
+
+ArchSpec RemoteAwarePlatform::GetRemoteSystemArchitecture() {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetRemoteSystemArchitecture();
+ return ArchSpec();
+}
+
+const char *RemoteAwarePlatform::GetHostname() {
+ if (IsHost())
+ return Platform::GetHostname();
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetHostname();
+ return nullptr;
+}
+
+UserIDResolver &RemoteAwarePlatform::GetUserIDResolver() {
+ if (IsHost())
+ return HostInfo::GetUserIDResolver();
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetUserIDResolver();
+ return UserIDResolver::GetNoopResolver();
+}
+
+Environment RemoteAwarePlatform::GetEnvironment() {
+ if (IsRemote()) {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetEnvironment();
+ return Environment();
+ }
+ return Host::GetEnvironment();
+}
+
+bool RemoteAwarePlatform::IsConnected() const {
+ if (IsHost())
+ return true;
+ else if (m_remote_platform_sp)
+ return m_remote_platform_sp->IsConnected();
+ return false;
+}
+
+bool RemoteAwarePlatform::GetProcessInfo(lldb::pid_t pid,
+ ProcessInstanceInfo &process_info) {
+ if (IsHost())
+ return Platform::GetProcessInfo(pid, process_info);
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetProcessInfo(pid, process_info);
+ return false;
+}
+
+uint32_t
+RemoteAwarePlatform::FindProcesses(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ if (IsHost())
+ return Platform::FindProcesses(match_info, process_infos);
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->FindProcesses(match_info, process_infos);
+ return 0;
+}
+
+lldb::ProcessSP RemoteAwarePlatform::ConnectProcess(llvm::StringRef connect_url,
+ llvm::StringRef plugin_name,
+ Debugger &debugger,
+ Target *target,
+ Status &error) {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->ConnectProcess(connect_url, plugin_name,
+ debugger, target, error);
+ return Platform::ConnectProcess(connect_url, plugin_name, debugger, target,
+ error);
+}
+
+Status RemoteAwarePlatform::LaunchProcess(ProcessLaunchInfo &launch_info) {
+ Status error;
+
+ if (IsHost()) {
+ error = Platform::LaunchProcess(launch_info);
+ } else {
+ if (m_remote_platform_sp)
+ error = m_remote_platform_sp->LaunchProcess(launch_info);
+ else
+ error.SetErrorString("the platform is not currently connected");
+ }
+ return error;
+}
+
+Status RemoteAwarePlatform::KillProcess(const lldb::pid_t pid) {
+ if (IsHost())
+ return Platform::KillProcess(pid);
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->KillProcess(pid);
+ return Status("the platform is not currently connected");
+}
diff --git a/contrib/llvm-project/lldb/source/Target/SectionLoadHistory.cpp b/contrib/llvm-project/lldb/source/Target/SectionLoadHistory.cpp
new file mode 100644
index 000000000000..ec16b58b4451
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/SectionLoadHistory.cpp
@@ -0,0 +1,164 @@
+//===-- SectionLoadHistory.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/SectionLoadHistory.h"
+
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool SectionLoadHistory::IsEmpty() const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return m_stop_id_to_section_load_list.empty();
+}
+
+void SectionLoadHistory::Clear() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_stop_id_to_section_load_list.clear();
+}
+
+uint32_t SectionLoadHistory::GetLastStopID() const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_stop_id_to_section_load_list.empty())
+ return 0;
+ else
+ return m_stop_id_to_section_load_list.rbegin()->first;
+}
+
+SectionLoadList *
+SectionLoadHistory::GetSectionLoadListForStopID(uint32_t stop_id,
+ bool read_only) {
+ if (!m_stop_id_to_section_load_list.empty()) {
+ if (read_only) {
+ // The section load list is for reading data only so we don't need to
+ // create a new SectionLoadList for the current stop ID, just return the
+ // section load list for the stop ID that is equal to or less than the
+ // current stop ID
+ if (stop_id == eStopIDNow) {
+ // If we are asking for the latest and greatest value, it is always at
+ // the end of our list because that will be the highest stop ID.
+ StopIDToSectionLoadList::reverse_iterator rpos =
+ m_stop_id_to_section_load_list.rbegin();
+ return rpos->second.get();
+ } else {
+ StopIDToSectionLoadList::iterator pos =
+ m_stop_id_to_section_load_list.lower_bound(stop_id);
+ if (pos != m_stop_id_to_section_load_list.end() &&
+ pos->first == stop_id)
+ return pos->second.get();
+ else if (pos != m_stop_id_to_section_load_list.begin()) {
+ --pos;
+ return pos->second.get();
+ }
+ }
+ } else {
+ // You can only use "eStopIDNow" when reading from the section load
+ // history
+ assert(stop_id != eStopIDNow);
+
+ // We are updating the section load list (not read only), so if the stop
+ // ID passed in isn't the same as the last stop ID in our collection,
+ // then create a new node using the current stop ID
+ StopIDToSectionLoadList::iterator pos =
+ m_stop_id_to_section_load_list.lower_bound(stop_id);
+ if (pos != m_stop_id_to_section_load_list.end() &&
+ pos->first == stop_id) {
+ // We already have an entry for this value
+ return pos->second.get();
+ }
+
+ // We must make a new section load list that is based on the last valid
+ // section load list, so here we copy the last section load list and add
+ // a new node for the current stop ID.
+ StopIDToSectionLoadList::reverse_iterator rpos =
+ m_stop_id_to_section_load_list.rbegin();
+ SectionLoadListSP section_load_list_sp(
+ new SectionLoadList(*rpos->second));
+ m_stop_id_to_section_load_list[stop_id] = section_load_list_sp;
+ return section_load_list_sp.get();
+ }
+ }
+ SectionLoadListSP section_load_list_sp(new SectionLoadList());
+ if (stop_id == eStopIDNow)
+ stop_id = 0;
+ m_stop_id_to_section_load_list[stop_id] = section_load_list_sp;
+ return section_load_list_sp.get();
+}
+
+SectionLoadList &SectionLoadHistory::GetCurrentSectionLoadList() {
+ const bool read_only = true;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ SectionLoadList *section_load_list =
+ GetSectionLoadListForStopID(eStopIDNow, read_only);
+ assert(section_load_list != nullptr);
+ return *section_load_list;
+}
+
+addr_t
+SectionLoadHistory::GetSectionLoadAddress(uint32_t stop_id,
+ const lldb::SectionSP &section_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ const bool read_only = true;
+ SectionLoadList *section_load_list =
+ GetSectionLoadListForStopID(stop_id, read_only);
+ return section_load_list->GetSectionLoadAddress(section_sp);
+}
+
+bool SectionLoadHistory::ResolveLoadAddress(uint32_t stop_id, addr_t load_addr,
+ Address &so_addr) {
+ // First find the top level section that this load address exists in
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ const bool read_only = true;
+ SectionLoadList *section_load_list =
+ GetSectionLoadListForStopID(stop_id, read_only);
+ return section_load_list->ResolveLoadAddress(load_addr, so_addr);
+}
+
+bool SectionLoadHistory::SetSectionLoadAddress(
+ uint32_t stop_id, const lldb::SectionSP &section_sp, addr_t load_addr,
+ bool warn_multiple) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ const bool read_only = false;
+ SectionLoadList *section_load_list =
+ GetSectionLoadListForStopID(stop_id, read_only);
+ return section_load_list->SetSectionLoadAddress(section_sp, load_addr,
+ warn_multiple);
+}
+
+size_t
+SectionLoadHistory::SetSectionUnloaded(uint32_t stop_id,
+ const lldb::SectionSP &section_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ const bool read_only = false;
+ SectionLoadList *section_load_list =
+ GetSectionLoadListForStopID(stop_id, read_only);
+ return section_load_list->SetSectionUnloaded(section_sp);
+}
+
+bool SectionLoadHistory::SetSectionUnloaded(uint32_t stop_id,
+ const lldb::SectionSP &section_sp,
+ addr_t load_addr) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ const bool read_only = false;
+ SectionLoadList *section_load_list =
+ GetSectionLoadListForStopID(stop_id, read_only);
+ return section_load_list->SetSectionUnloaded(section_sp, load_addr);
+}
+
+void SectionLoadHistory::Dump(Stream &s, Target *target) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ StopIDToSectionLoadList::iterator pos,
+ end = m_stop_id_to_section_load_list.end();
+ for (pos = m_stop_id_to_section_load_list.begin(); pos != end; ++pos) {
+ s.Printf("StopID = %u:\n", pos->first);
+ pos->second->Dump(s, target);
+ s.EOL();
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Target/SectionLoadList.cpp b/contrib/llvm-project/lldb/source/Target/SectionLoadList.cpp
new file mode 100644
index 000000000000..598f49ca13de
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/SectionLoadList.cpp
@@ -0,0 +1,256 @@
+//===-- SectionLoadList.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/SectionLoadList.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SectionLoadList::SectionLoadList(const SectionLoadList &rhs)
+ : m_addr_to_sect(), m_sect_to_addr(), m_mutex() {
+ std::lock_guard<std::recursive_mutex> guard(rhs.m_mutex);
+ m_addr_to_sect = rhs.m_addr_to_sect;
+ m_sect_to_addr = rhs.m_sect_to_addr;
+}
+
+void SectionLoadList::operator=(const SectionLoadList &rhs) {
+ std::lock(m_mutex, rhs.m_mutex);
+ std::lock_guard<std::recursive_mutex> lhs_guard(m_mutex, std::adopt_lock);
+ std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_mutex, std::adopt_lock);
+ m_addr_to_sect = rhs.m_addr_to_sect;
+ m_sect_to_addr = rhs.m_sect_to_addr;
+}
+
+bool SectionLoadList::IsEmpty() const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return m_addr_to_sect.empty();
+}
+
+void SectionLoadList::Clear() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_addr_to_sect.clear();
+ m_sect_to_addr.clear();
+}
+
+addr_t
+SectionLoadList::GetSectionLoadAddress(const lldb::SectionSP &section) const {
+ // TODO: add support for the same section having multiple load addresses
+ addr_t section_load_addr = LLDB_INVALID_ADDRESS;
+ if (section) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ sect_to_addr_collection::const_iterator pos =
+ m_sect_to_addr.find(section.get());
+
+ if (pos != m_sect_to_addr.end())
+ section_load_addr = pos->second;
+ }
+ return section_load_addr;
+}
+
+bool SectionLoadList::SetSectionLoadAddress(const lldb::SectionSP &section,
+ addr_t load_addr,
+ bool warn_multiple) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ ModuleSP module_sp(section->GetModule());
+
+ if (module_sp) {
+ LLDB_LOGV(log, "(section = {0} ({1}.{2}), load_addr = {3:x}) module = {4}",
+ section.get(), module_sp->GetFileSpec(), section->GetName(),
+ load_addr, module_sp.get());
+
+ if (section->GetByteSize() == 0)
+ return false; // No change
+
+ // Fill in the section -> load_addr map
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ sect_to_addr_collection::iterator sta_pos =
+ m_sect_to_addr.find(section.get());
+ if (sta_pos != m_sect_to_addr.end()) {
+ if (load_addr == sta_pos->second)
+ return false; // No change...
+ else
+ sta_pos->second = load_addr;
+ } else
+ m_sect_to_addr[section.get()] = load_addr;
+
+ // Fill in the load_addr -> section map
+ addr_to_sect_collection::iterator ats_pos = m_addr_to_sect.find(load_addr);
+ if (ats_pos != m_addr_to_sect.end()) {
+ // Some sections are ok to overlap, and for others we should warn. When
+ // we have multiple load addresses that correspond to a section, we will
+ // always attribute the section to the be last section that claims it
+ // exists at that address. Sometimes it is ok for more that one section
+ // to be loaded at a specific load address, and other times it isn't. The
+ // "warn_multiple" parameter tells us if we should warn in this case or
+ // not. The DynamicLoader plug-in subclasses should know which sections
+ // should warn and which shouldn't (darwin shared cache modules all
+ // shared the same "__LINKEDIT" sections, so the dynamic loader can pass
+ // false for "warn_multiple").
+ if (warn_multiple && section != ats_pos->second) {
+ ModuleSP module_sp(section->GetModule());
+ if (module_sp) {
+ ModuleSP curr_module_sp(ats_pos->second->GetModule());
+ if (curr_module_sp) {
+ module_sp->ReportWarning(
+ "address 0x%16.16" PRIx64
+ " maps to more than one section: %s.%s and %s.%s",
+ load_addr, module_sp->GetFileSpec().GetFilename().GetCString(),
+ section->GetName().GetCString(),
+ curr_module_sp->GetFileSpec().GetFilename().GetCString(),
+ ats_pos->second->GetName().GetCString());
+ }
+ }
+ }
+ ats_pos->second = section;
+ } else
+ m_addr_to_sect[load_addr] = section;
+ return true; // Changed
+
+ } else {
+ if (log) {
+ log->Printf(
+ "SectionLoadList::%s (section = %p (%s), load_addr = 0x%16.16" PRIx64
+ ") error: module has been deleted",
+ __FUNCTION__, static_cast<void *>(section.get()),
+ section->GetName().AsCString(), load_addr);
+ }
+ }
+ return false;
+}
+
+size_t SectionLoadList::SetSectionUnloaded(const lldb::SectionSP &section_sp) {
+ size_t unload_count = 0;
+
+ if (section_sp) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ if (log && log->GetVerbose()) {
+ ModuleSP module_sp = section_sp->GetModule();
+ std::string module_name("<Unknown>");
+ if (module_sp) {
+ const FileSpec &module_file_spec(
+ section_sp->GetModule()->GetFileSpec());
+ module_name = module_file_spec.GetPath();
+ }
+ log->Printf("SectionLoadList::%s (section = %p (%s.%s))", __FUNCTION__,
+ static_cast<void *>(section_sp.get()), module_name.c_str(),
+ section_sp->GetName().AsCString());
+ }
+
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ sect_to_addr_collection::iterator sta_pos =
+ m_sect_to_addr.find(section_sp.get());
+ if (sta_pos != m_sect_to_addr.end()) {
+ ++unload_count;
+ addr_t load_addr = sta_pos->second;
+ m_sect_to_addr.erase(sta_pos);
+
+ addr_to_sect_collection::iterator ats_pos =
+ m_addr_to_sect.find(load_addr);
+ if (ats_pos != m_addr_to_sect.end())
+ m_addr_to_sect.erase(ats_pos);
+ }
+ }
+ return unload_count;
+}
+
+bool SectionLoadList::SetSectionUnloaded(const lldb::SectionSP &section_sp,
+ addr_t load_addr) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ if (log && log->GetVerbose()) {
+ ModuleSP module_sp = section_sp->GetModule();
+ std::string module_name("<Unknown>");
+ if (module_sp) {
+ const FileSpec &module_file_spec(section_sp->GetModule()->GetFileSpec());
+ module_name = module_file_spec.GetPath();
+ }
+ log->Printf(
+ "SectionLoadList::%s (section = %p (%s.%s), load_addr = 0x%16.16" PRIx64
+ ")",
+ __FUNCTION__, static_cast<void *>(section_sp.get()),
+ module_name.c_str(), section_sp->GetName().AsCString(), load_addr);
+ }
+ bool erased = false;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ sect_to_addr_collection::iterator sta_pos =
+ m_sect_to_addr.find(section_sp.get());
+ if (sta_pos != m_sect_to_addr.end()) {
+ erased = true;
+ m_sect_to_addr.erase(sta_pos);
+ }
+
+ addr_to_sect_collection::iterator ats_pos = m_addr_to_sect.find(load_addr);
+ if (ats_pos != m_addr_to_sect.end()) {
+ erased = true;
+ m_addr_to_sect.erase(ats_pos);
+ }
+
+ return erased;
+}
+
+bool SectionLoadList::ResolveLoadAddress(addr_t load_addr, Address &so_addr,
+ bool allow_section_end) const {
+ // First find the top level section that this load address exists in
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_addr_to_sect.empty()) {
+ addr_to_sect_collection::const_iterator pos =
+ m_addr_to_sect.lower_bound(load_addr);
+ if (pos != m_addr_to_sect.end()) {
+ if (load_addr != pos->first && pos != m_addr_to_sect.begin())
+ --pos;
+ const addr_t pos_load_addr = pos->first;
+ if (load_addr >= pos_load_addr) {
+ addr_t offset = load_addr - pos_load_addr;
+ if (offset < pos->second->GetByteSize() + (allow_section_end ? 1 : 0)) {
+ // We have found the top level section, now we need to find the
+ // deepest child section.
+ return pos->second->ResolveContainedAddress(offset, so_addr,
+ allow_section_end);
+ }
+ }
+ } else {
+ // There are no entries that have an address that is >= load_addr, so we
+ // need to check the last entry on our collection.
+ addr_to_sect_collection::const_reverse_iterator rpos =
+ m_addr_to_sect.rbegin();
+ if (load_addr >= rpos->first) {
+ addr_t offset = load_addr - rpos->first;
+ if (offset <
+ rpos->second->GetByteSize() + (allow_section_end ? 1 : 0)) {
+ // We have found the top level section, now we need to find the
+ // deepest child section.
+ return rpos->second->ResolveContainedAddress(offset, so_addr,
+ allow_section_end);
+ }
+ }
+ }
+ }
+ so_addr.Clear();
+ return false;
+}
+
+void SectionLoadList::Dump(Stream &s, Target *target) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ addr_to_sect_collection::const_iterator pos, end;
+ for (pos = m_addr_to_sect.begin(), end = m_addr_to_sect.end(); pos != end;
+ ++pos) {
+ s.Printf("addr = 0x%16.16" PRIx64 ", section = %p: ", pos->first,
+ static_cast<void *>(pos->second.get()));
+ pos->second->Dump(&s, target, 0);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Target/StackFrame.cpp b/contrib/llvm-project/lldb/source/Target/StackFrame.cpp
new file mode 100644
index 000000000000..f8b22d96e16f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/StackFrame.cpp
@@ -0,0 +1,1964 @@
+//===-- StackFrame.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/FormatEntity.h"
+#include "lldb/Core/Mangled.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContextScope.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrameRecognizer.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "lldb/lldb-enumerations.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// The first bits in the flags are reserved for the SymbolContext::Scope bits
+// so we know if we have tried to look up information in our internal symbol
+// context (m_sc) already.
+#define RESOLVED_FRAME_CODE_ADDR (uint32_t(eSymbolContextEverything + 1))
+#define RESOLVED_FRAME_ID_SYMBOL_SCOPE (RESOLVED_FRAME_CODE_ADDR << 1)
+#define GOT_FRAME_BASE (RESOLVED_FRAME_ID_SYMBOL_SCOPE << 1)
+#define RESOLVED_VARIABLES (GOT_FRAME_BASE << 1)
+#define RESOLVED_GLOBAL_VARIABLES (RESOLVED_VARIABLES << 1)
+
+StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
+ user_id_t unwind_frame_index, addr_t cfa,
+ bool cfa_is_valid, addr_t pc, StackFrame::Kind kind,
+ const SymbolContext *sc_ptr)
+ : m_thread_wp(thread_sp), m_frame_index(frame_idx),
+ m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(),
+ m_id(pc, cfa, nullptr), m_frame_code_addr(pc), m_sc(), m_flags(),
+ m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid),
+ m_stack_frame_kind(kind), m_variable_list_sp(),
+ m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(),
+ m_mutex() {
+ // If we don't have a CFA value, use the frame index for our StackID so that
+ // recursive functions properly aren't confused with one another on a history
+ // stack.
+ if (IsHistorical() && !m_cfa_is_valid) {
+ m_id.SetCFA(m_frame_index);
+ }
+
+ if (sc_ptr != nullptr) {
+ m_sc = *sc_ptr;
+ m_flags.Set(m_sc.GetResolvedMask());
+ }
+}
+
+StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
+ user_id_t unwind_frame_index,
+ const RegisterContextSP &reg_context_sp, addr_t cfa,
+ addr_t pc, const SymbolContext *sc_ptr)
+ : m_thread_wp(thread_sp), m_frame_index(frame_idx),
+ m_concrete_frame_index(unwind_frame_index),
+ m_reg_context_sp(reg_context_sp), m_id(pc, cfa, nullptr),
+ m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(),
+ m_frame_base_error(), m_cfa_is_valid(true),
+ m_stack_frame_kind(StackFrame::Kind::Regular), m_variable_list_sp(),
+ m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(),
+ m_mutex() {
+ if (sc_ptr != nullptr) {
+ m_sc = *sc_ptr;
+ m_flags.Set(m_sc.GetResolvedMask());
+ }
+
+ if (reg_context_sp && !m_sc.target_sp) {
+ m_sc.target_sp = reg_context_sp->CalculateTarget();
+ if (m_sc.target_sp)
+ m_flags.Set(eSymbolContextTarget);
+ }
+}
+
+StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
+ user_id_t unwind_frame_index,
+ const RegisterContextSP &reg_context_sp, addr_t cfa,
+ const Address &pc_addr, const SymbolContext *sc_ptr)
+ : m_thread_wp(thread_sp), m_frame_index(frame_idx),
+ m_concrete_frame_index(unwind_frame_index),
+ m_reg_context_sp(reg_context_sp),
+ m_id(pc_addr.GetLoadAddress(thread_sp->CalculateTarget().get()), cfa,
+ nullptr),
+ m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(),
+ m_frame_base_error(), m_cfa_is_valid(true),
+ m_stack_frame_kind(StackFrame::Kind::Regular), m_variable_list_sp(),
+ m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(),
+ m_mutex() {
+ if (sc_ptr != nullptr) {
+ m_sc = *sc_ptr;
+ m_flags.Set(m_sc.GetResolvedMask());
+ }
+
+ if (!m_sc.target_sp && reg_context_sp) {
+ m_sc.target_sp = reg_context_sp->CalculateTarget();
+ if (m_sc.target_sp)
+ m_flags.Set(eSymbolContextTarget);
+ }
+
+ ModuleSP pc_module_sp(pc_addr.GetModule());
+ if (!m_sc.module_sp || m_sc.module_sp != pc_module_sp) {
+ if (pc_module_sp) {
+ m_sc.module_sp = pc_module_sp;
+ m_flags.Set(eSymbolContextModule);
+ } else {
+ m_sc.module_sp.reset();
+ }
+ }
+}
+
+StackFrame::~StackFrame() = default;
+
+StackID &StackFrame::GetStackID() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ // Make sure we have resolved the StackID object's symbol context scope if we
+ // already haven't looked it up.
+
+ if (m_flags.IsClear(RESOLVED_FRAME_ID_SYMBOL_SCOPE)) {
+ if (m_id.GetSymbolContextScope()) {
+ // We already have a symbol context scope, we just don't have our flag
+ // bit set.
+ m_flags.Set(RESOLVED_FRAME_ID_SYMBOL_SCOPE);
+ } else {
+ // Calculate the frame block and use this for the stack ID symbol context
+ // scope if we have one.
+ SymbolContextScope *scope = GetFrameBlock();
+ if (scope == nullptr) {
+ // We don't have a block, so use the symbol
+ if (m_flags.IsClear(eSymbolContextSymbol))
+ GetSymbolContext(eSymbolContextSymbol);
+
+ // It is ok if m_sc.symbol is nullptr here
+ scope = m_sc.symbol;
+ }
+ // Set the symbol context scope (the accessor will set the
+ // RESOLVED_FRAME_ID_SYMBOL_SCOPE bit in m_flags).
+ SetSymbolContextScope(scope);
+ }
+ }
+ return m_id;
+}
+
+uint32_t StackFrame::GetFrameIndex() const {
+ ThreadSP thread_sp = GetThread();
+ if (thread_sp)
+ return thread_sp->GetStackFrameList()->GetVisibleStackFrameIndex(
+ m_frame_index);
+ else
+ return m_frame_index;
+}
+
+void StackFrame::SetSymbolContextScope(SymbolContextScope *symbol_scope) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_flags.Set(RESOLVED_FRAME_ID_SYMBOL_SCOPE);
+ m_id.SetSymbolContextScope(symbol_scope);
+}
+
+const Address &StackFrame::GetFrameCodeAddress() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR) &&
+ !m_frame_code_addr.IsSectionOffset()) {
+ m_flags.Set(RESOLVED_FRAME_CODE_ADDR);
+
+ // Resolve the PC into a temporary address because if ResolveLoadAddress
+ // fails to resolve the address, it will clear the address object...
+ ThreadSP thread_sp(GetThread());
+ if (thread_sp) {
+ TargetSP target_sp(thread_sp->CalculateTarget());
+ if (target_sp) {
+ const bool allow_section_end = true;
+ if (m_frame_code_addr.SetOpcodeLoadAddress(
+ m_frame_code_addr.GetOffset(), target_sp.get(),
+ AddressClass::eCode, allow_section_end)) {
+ ModuleSP module_sp(m_frame_code_addr.GetModule());
+ if (module_sp) {
+ m_sc.module_sp = module_sp;
+ m_flags.Set(eSymbolContextModule);
+ }
+ }
+ }
+ }
+ }
+ return m_frame_code_addr;
+}
+
+bool StackFrame::ChangePC(addr_t pc) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ // We can't change the pc value of a history stack frame - it is immutable.
+ if (IsHistorical())
+ return false;
+ m_frame_code_addr.SetRawAddress(pc);
+ m_sc.Clear(false);
+ m_flags.Reset(0);
+ ThreadSP thread_sp(GetThread());
+ if (thread_sp)
+ thread_sp->ClearStackFrames();
+ return true;
+}
+
+const char *StackFrame::Disassemble() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_disassembly.Empty()) {
+ ExecutionContext exe_ctx(shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ const char *plugin_name = nullptr;
+ const char *flavor = nullptr;
+ Disassembler::Disassemble(target->GetDebugger(),
+ target->GetArchitecture(), plugin_name, flavor,
+ exe_ctx, 0, false, 0, 0, m_disassembly);
+ }
+ if (m_disassembly.Empty())
+ return nullptr;
+ }
+
+ return m_disassembly.GetData();
+}
+
+Block *StackFrame::GetFrameBlock() {
+ if (m_sc.block == nullptr && m_flags.IsClear(eSymbolContextBlock))
+ GetSymbolContext(eSymbolContextBlock);
+
+ if (m_sc.block) {
+ Block *inline_block = m_sc.block->GetContainingInlinedBlock();
+ if (inline_block) {
+ // Use the block with the inlined function info as the frame block we
+ // want this frame to have only the variables for the inlined function
+ // and its non-inlined block child blocks.
+ return inline_block;
+ } else {
+ // This block is not contained within any inlined function blocks with so
+ // we want to use the top most function block.
+ return &m_sc.function->GetBlock(false);
+ }
+ }
+ return nullptr;
+}
+
+// Get the symbol context if we already haven't done so by resolving the
+// PC address as much as possible. This way when we pass around a
+// StackFrame object, everyone will have as much information as possible and no
+// one will ever have to look things up manually.
+const SymbolContext &
+StackFrame::GetSymbolContext(SymbolContextItem resolve_scope) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ // Copy our internal symbol context into "sc".
+ if ((m_flags.Get() & resolve_scope) != resolve_scope) {
+ uint32_t resolved = 0;
+
+ // If the target was requested add that:
+ if (!m_sc.target_sp) {
+ m_sc.target_sp = CalculateTarget();
+ if (m_sc.target_sp)
+ resolved |= eSymbolContextTarget;
+ }
+
+ // Resolve our PC to section offset if we haven't already done so and if we
+ // don't have a module. The resolved address section will contain the
+ // module to which it belongs
+ if (!m_sc.module_sp && m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR))
+ GetFrameCodeAddress();
+
+ // If this is not frame zero, then we need to subtract 1 from the PC value
+ // when doing address lookups since the PC will be on the instruction
+ // following the function call instruction...
+
+ Address lookup_addr(GetFrameCodeAddress());
+ if (m_frame_index > 0 && lookup_addr.IsValid()) {
+ addr_t offset = lookup_addr.GetOffset();
+ if (offset > 0) {
+ lookup_addr.SetOffset(offset - 1);
+
+ } else {
+ // lookup_addr is the start of a section. We need do the math on the
+ // actual load address and re-compute the section. We're working with
+ // a 'noreturn' function at the end of a section.
+ ThreadSP thread_sp(GetThread());
+ if (thread_sp) {
+ TargetSP target_sp(thread_sp->CalculateTarget());
+ if (target_sp) {
+ addr_t addr_minus_one =
+ lookup_addr.GetLoadAddress(target_sp.get()) - 1;
+ lookup_addr.SetLoadAddress(addr_minus_one, target_sp.get());
+ } else {
+ lookup_addr.SetOffset(offset - 1);
+ }
+ }
+ }
+ }
+
+ if (m_sc.module_sp) {
+ // We have something in our stack frame symbol context, lets check if we
+ // haven't already tried to lookup one of those things. If we haven't
+ // then we will do the query.
+
+ SymbolContextItem actual_resolve_scope = SymbolContextItem(0);
+
+ if (resolve_scope & eSymbolContextCompUnit) {
+ if (m_flags.IsClear(eSymbolContextCompUnit)) {
+ if (m_sc.comp_unit)
+ resolved |= eSymbolContextCompUnit;
+ else
+ actual_resolve_scope |= eSymbolContextCompUnit;
+ }
+ }
+
+ if (resolve_scope & eSymbolContextFunction) {
+ if (m_flags.IsClear(eSymbolContextFunction)) {
+ if (m_sc.function)
+ resolved |= eSymbolContextFunction;
+ else
+ actual_resolve_scope |= eSymbolContextFunction;
+ }
+ }
+
+ if (resolve_scope & eSymbolContextBlock) {
+ if (m_flags.IsClear(eSymbolContextBlock)) {
+ if (m_sc.block)
+ resolved |= eSymbolContextBlock;
+ else
+ actual_resolve_scope |= eSymbolContextBlock;
+ }
+ }
+
+ if (resolve_scope & eSymbolContextSymbol) {
+ if (m_flags.IsClear(eSymbolContextSymbol)) {
+ if (m_sc.symbol)
+ resolved |= eSymbolContextSymbol;
+ else
+ actual_resolve_scope |= eSymbolContextSymbol;
+ }
+ }
+
+ if (resolve_scope & eSymbolContextLineEntry) {
+ if (m_flags.IsClear(eSymbolContextLineEntry)) {
+ if (m_sc.line_entry.IsValid())
+ resolved |= eSymbolContextLineEntry;
+ else
+ actual_resolve_scope |= eSymbolContextLineEntry;
+ }
+ }
+
+ if (actual_resolve_scope) {
+ // We might be resolving less information than what is already in our
+ // current symbol context so resolve into a temporary symbol context
+ // "sc" so we don't clear out data we have already found in "m_sc"
+ SymbolContext sc;
+ // Set flags that indicate what we have tried to resolve
+ resolved |= m_sc.module_sp->ResolveSymbolContextForAddress(
+ lookup_addr, actual_resolve_scope, sc);
+ // Only replace what we didn't already have as we may have information
+ // for an inlined function scope that won't match what a standard
+ // lookup by address would match
+ if ((resolved & eSymbolContextCompUnit) && m_sc.comp_unit == nullptr)
+ m_sc.comp_unit = sc.comp_unit;
+ if ((resolved & eSymbolContextFunction) && m_sc.function == nullptr)
+ m_sc.function = sc.function;
+ if ((resolved & eSymbolContextBlock) && m_sc.block == nullptr)
+ m_sc.block = sc.block;
+ if ((resolved & eSymbolContextSymbol) && m_sc.symbol == nullptr)
+ m_sc.symbol = sc.symbol;
+ if ((resolved & eSymbolContextLineEntry) &&
+ !m_sc.line_entry.IsValid()) {
+ m_sc.line_entry = sc.line_entry;
+ m_sc.line_entry.ApplyFileMappings(m_sc.target_sp);
+ }
+ }
+ } else {
+ // If we don't have a module, then we can't have the compile unit,
+ // function, block, line entry or symbol, so we can safely call
+ // ResolveSymbolContextForAddress with our symbol context member m_sc.
+ if (m_sc.target_sp) {
+ resolved |= m_sc.target_sp->GetImages().ResolveSymbolContextForAddress(
+ lookup_addr, resolve_scope, m_sc);
+ }
+ }
+
+ // Update our internal flags so we remember what we have tried to locate so
+ // we don't have to keep trying when more calls to this function are made.
+ // We might have dug up more information that was requested (for example if
+ // we were asked to only get the block, we will have gotten the compile
+ // unit, and function) so set any additional bits that we resolved
+ m_flags.Set(resolve_scope | resolved);
+ }
+
+ // Return the symbol context with everything that was possible to resolve
+ // resolved.
+ return m_sc;
+}
+
+VariableList *StackFrame::GetVariableList(bool get_file_globals) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_flags.IsClear(RESOLVED_VARIABLES)) {
+ m_flags.Set(RESOLVED_VARIABLES);
+
+ Block *frame_block = GetFrameBlock();
+
+ if (frame_block) {
+ const bool get_child_variables = true;
+ const bool can_create = true;
+ const bool stop_if_child_block_is_inlined_function = true;
+ m_variable_list_sp = std::make_shared<VariableList>();
+ frame_block->AppendBlockVariables(can_create, get_child_variables,
+ stop_if_child_block_is_inlined_function,
+ [](Variable *v) { return true; },
+ m_variable_list_sp.get());
+ }
+ }
+
+ if (m_flags.IsClear(RESOLVED_GLOBAL_VARIABLES) && get_file_globals) {
+ m_flags.Set(RESOLVED_GLOBAL_VARIABLES);
+
+ if (m_flags.IsClear(eSymbolContextCompUnit))
+ GetSymbolContext(eSymbolContextCompUnit);
+
+ if (m_sc.comp_unit) {
+ VariableListSP global_variable_list_sp(
+ m_sc.comp_unit->GetVariableList(true));
+ if (m_variable_list_sp)
+ m_variable_list_sp->AddVariables(global_variable_list_sp.get());
+ else
+ m_variable_list_sp = global_variable_list_sp;
+ }
+ }
+
+ return m_variable_list_sp.get();
+}
+
+VariableListSP
+StackFrame::GetInScopeVariableList(bool get_file_globals,
+ bool must_have_valid_location) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ // We can't fetch variable information for a history stack frame.
+ if (IsHistorical())
+ return VariableListSP();
+
+ VariableListSP var_list_sp(new VariableList);
+ GetSymbolContext(eSymbolContextCompUnit | eSymbolContextBlock);
+
+ if (m_sc.block) {
+ const bool can_create = true;
+ const bool get_parent_variables = true;
+ const bool stop_if_block_is_inlined_function = true;
+ m_sc.block->AppendVariables(
+ can_create, get_parent_variables, stop_if_block_is_inlined_function,
+ [this, must_have_valid_location](Variable *v) {
+ return v->IsInScope(this) && (!must_have_valid_location ||
+ v->LocationIsValidForFrame(this));
+ },
+ var_list_sp.get());
+ }
+
+ if (m_sc.comp_unit && get_file_globals) {
+ VariableListSP global_variable_list_sp(
+ m_sc.comp_unit->GetVariableList(true));
+ if (global_variable_list_sp)
+ var_list_sp->AddVariables(global_variable_list_sp.get());
+ }
+
+ return var_list_sp;
+}
+
+ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
+ llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options,
+ VariableSP &var_sp, Status &error) {
+ llvm::StringRef original_var_expr = var_expr;
+ // We can't fetch variable information for a history stack frame.
+ if (IsHistorical())
+ return ValueObjectSP();
+
+ if (var_expr.empty()) {
+ error.SetErrorStringWithFormat("invalid variable path '%s'",
+ var_expr.str().c_str());
+ return ValueObjectSP();
+ }
+
+ const bool check_ptr_vs_member =
+ (options & eExpressionPathOptionCheckPtrVsMember) != 0;
+ const bool no_fragile_ivar =
+ (options & eExpressionPathOptionsNoFragileObjcIvar) != 0;
+ const bool no_synth_child =
+ (options & eExpressionPathOptionsNoSyntheticChildren) != 0;
+ // const bool no_synth_array = (options &
+ // eExpressionPathOptionsNoSyntheticArrayRange) != 0;
+ error.Clear();
+ bool deref = false;
+ bool address_of = false;
+ ValueObjectSP valobj_sp;
+ const bool get_file_globals = true;
+ // When looking up a variable for an expression, we need only consider the
+ // variables that are in scope.
+ VariableListSP var_list_sp(GetInScopeVariableList(get_file_globals));
+ VariableList *variable_list = var_list_sp.get();
+
+ if (!variable_list)
+ return ValueObjectSP();
+
+ // If first character is a '*', then show pointer contents
+ std::string var_expr_storage;
+ if (var_expr[0] == '*') {
+ deref = true;
+ var_expr = var_expr.drop_front(); // Skip the '*'
+ } else if (var_expr[0] == '&') {
+ address_of = true;
+ var_expr = var_expr.drop_front(); // Skip the '&'
+ }
+
+ size_t separator_idx = var_expr.find_first_of(".-[=+~|&^%#@!/?,<>{}");
+ StreamString var_expr_path_strm;
+
+ ConstString name_const_string(var_expr.substr(0, separator_idx));
+
+ var_sp = variable_list->FindVariable(name_const_string, false);
+
+ bool synthetically_added_instance_object = false;
+
+ if (var_sp) {
+ var_expr = var_expr.drop_front(name_const_string.GetLength());
+ }
+
+ if (!var_sp && (options & eExpressionPathOptionsAllowDirectIVarAccess)) {
+ // Check for direct ivars access which helps us with implicit access to
+ // ivars with the "this->" or "self->"
+ GetSymbolContext(eSymbolContextFunction | eSymbolContextBlock);
+ lldb::LanguageType method_language = eLanguageTypeUnknown;
+ bool is_instance_method = false;
+ ConstString method_object_name;
+ if (m_sc.GetFunctionMethodInfo(method_language, is_instance_method,
+ method_object_name)) {
+ if (is_instance_method && method_object_name) {
+ var_sp = variable_list->FindVariable(method_object_name);
+ if (var_sp) {
+ separator_idx = 0;
+ var_expr_storage = "->";
+ var_expr_storage += var_expr;
+ var_expr = var_expr_storage;
+ synthetically_added_instance_object = true;
+ }
+ }
+ }
+ }
+
+ if (!var_sp && (options & eExpressionPathOptionsInspectAnonymousUnions)) {
+ // Check if any anonymous unions are there which contain a variable with
+ // the name we need
+ for (size_t i = 0; i < variable_list->GetSize(); i++) {
+ VariableSP variable_sp = variable_list->GetVariableAtIndex(i);
+ if (!variable_sp)
+ continue;
+ if (!variable_sp->GetName().IsEmpty())
+ continue;
+
+ Type *var_type = variable_sp->GetType();
+ if (!var_type)
+ continue;
+
+ if (!var_type->GetForwardCompilerType().IsAnonymousType())
+ continue;
+ valobj_sp = GetValueObjectForFrameVariable(variable_sp, use_dynamic);
+ if (!valobj_sp)
+ return valobj_sp;
+ valobj_sp = valobj_sp->GetChildMemberWithName(name_const_string, true);
+ if (valobj_sp)
+ break;
+ }
+ }
+
+ if (var_sp && !valobj_sp) {
+ valobj_sp = GetValueObjectForFrameVariable(var_sp, use_dynamic);
+ if (!valobj_sp)
+ return valobj_sp;
+ }
+ if (!valobj_sp) {
+ error.SetErrorStringWithFormat("no variable named '%s' found in this frame",
+ name_const_string.GetCString());
+ return ValueObjectSP();
+ }
+
+ // We are dumping at least one child
+ while (separator_idx != std::string::npos) {
+ // Calculate the next separator index ahead of time
+ ValueObjectSP child_valobj_sp;
+ const char separator_type = var_expr[0];
+ bool expr_is_ptr = false;
+ switch (separator_type) {
+ case '-':
+ expr_is_ptr = true;
+ if (var_expr.size() >= 2 && var_expr[1] != '>')
+ return ValueObjectSP();
+
+ if (no_fragile_ivar) {
+ // Make sure we aren't trying to deref an objective
+ // C ivar if this is not allowed
+ const uint32_t pointer_type_flags =
+ valobj_sp->GetCompilerType().GetTypeInfo(nullptr);
+ if ((pointer_type_flags & eTypeIsObjC) &&
+ (pointer_type_flags & eTypeIsPointer)) {
+ // This was an objective C object pointer and it was requested we
+ // skip any fragile ivars so return nothing here
+ return ValueObjectSP();
+ }
+ }
+
+ // If we have a non pointer type with a sythetic value then lets check if
+ // we have an sythetic dereference specified.
+ if (!valobj_sp->IsPointerType() && valobj_sp->HasSyntheticValue()) {
+ Status deref_error;
+ if (valobj_sp->GetCompilerType().IsReferenceType()) {
+ valobj_sp = valobj_sp->GetSyntheticValue()->Dereference(deref_error);
+ if (error.Fail()) {
+ error.SetErrorStringWithFormatv(
+ "Failed to dereference reference type: %s", deref_error);
+ return ValueObjectSP();
+ }
+ }
+
+ valobj_sp = valobj_sp->Dereference(deref_error);
+ if (error.Fail()) {
+ error.SetErrorStringWithFormatv(
+ "Failed to dereference sythetic value: {0}", deref_error);
+ return ValueObjectSP();
+ }
+ // Some synthetic plug-ins fail to set the error in Dereference
+ if (!valobj_sp) {
+ error.SetErrorString("Failed to dereference sythetic value");
+ return ValueObjectSP();
+ }
+ expr_is_ptr = false;
+ }
+
+ var_expr = var_expr.drop_front(); // Remove the '-'
+ LLVM_FALLTHROUGH;
+ case '.': {
+ var_expr = var_expr.drop_front(); // Remove the '.' or '>'
+ separator_idx = var_expr.find_first_of(".-[");
+ ConstString child_name(var_expr.substr(0, var_expr.find_first_of(".-[")));
+
+ if (check_ptr_vs_member) {
+ // We either have a pointer type and need to verify valobj_sp is a
+ // pointer, or we have a member of a class/union/struct being accessed
+ // with the . syntax and need to verify we don't have a pointer.
+ const bool actual_is_ptr = valobj_sp->IsPointerType();
+
+ if (actual_is_ptr != expr_is_ptr) {
+ // Incorrect use of "." with a pointer, or "->" with a
+ // class/union/struct instance or reference.
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ if (actual_is_ptr)
+ error.SetErrorStringWithFormat(
+ "\"%s\" is a pointer and . was used to attempt to access "
+ "\"%s\". Did you mean \"%s->%s\"?",
+ var_expr_path_strm.GetData(), child_name.GetCString(),
+ var_expr_path_strm.GetData(), var_expr.str().c_str());
+ else
+ error.SetErrorStringWithFormat(
+ "\"%s\" is not a pointer and -> was used to attempt to "
+ "access \"%s\". Did you mean \"%s.%s\"?",
+ var_expr_path_strm.GetData(), child_name.GetCString(),
+ var_expr_path_strm.GetData(), var_expr.str().c_str());
+ return ValueObjectSP();
+ }
+ }
+ child_valobj_sp = valobj_sp->GetChildMemberWithName(child_name, true);
+ if (!child_valobj_sp) {
+ if (!no_synth_child) {
+ child_valobj_sp = valobj_sp->GetSyntheticValue();
+ if (child_valobj_sp)
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+
+ if (no_synth_child || !child_valobj_sp) {
+ // No child member with name "child_name"
+ if (synthetically_added_instance_object) {
+ // We added a "this->" or "self->" to the beginning of the
+ // expression and this is the first pointer ivar access, so just
+ // return the normal error
+ error.SetErrorStringWithFormat(
+ "no variable or instance variable named '%s' found in "
+ "this frame",
+ name_const_string.GetCString());
+ } else {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ if (child_name) {
+ error.SetErrorStringWithFormat(
+ "\"%s\" is not a member of \"(%s) %s\"",
+ child_name.GetCString(),
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ } else {
+ error.SetErrorStringWithFormat(
+ "incomplete expression path after \"%s\" in \"%s\"",
+ var_expr_path_strm.GetData(),
+ original_var_expr.str().c_str());
+ }
+ }
+ return ValueObjectSP();
+ }
+ }
+ synthetically_added_instance_object = false;
+ // Remove the child name from the path
+ var_expr = var_expr.drop_front(child_name.GetLength());
+ if (use_dynamic != eNoDynamicValues) {
+ ValueObjectSP dynamic_value_sp(
+ child_valobj_sp->GetDynamicValue(use_dynamic));
+ if (dynamic_value_sp)
+ child_valobj_sp = dynamic_value_sp;
+ }
+ } break;
+
+ case '[': {
+ // Array member access, or treating pointer as an array Need at least two
+ // brackets and a number
+ if (var_expr.size() <= 2) {
+ error.SetErrorStringWithFormat(
+ "invalid square bracket encountered after \"%s\" in \"%s\"",
+ var_expr_path_strm.GetData(), var_expr.str().c_str());
+ return ValueObjectSP();
+ }
+
+ // Drop the open brace.
+ var_expr = var_expr.drop_front();
+ long child_index = 0;
+
+ // If there's no closing brace, this is an invalid expression.
+ size_t end_pos = var_expr.find_first_of(']');
+ if (end_pos == llvm::StringRef::npos) {
+ error.SetErrorStringWithFormat(
+ "missing closing square bracket in expression \"%s\"",
+ var_expr_path_strm.GetData());
+ return ValueObjectSP();
+ }
+ llvm::StringRef index_expr = var_expr.take_front(end_pos);
+ llvm::StringRef original_index_expr = index_expr;
+ // Drop all of "[index_expr]"
+ var_expr = var_expr.drop_front(end_pos + 1);
+
+ if (index_expr.consumeInteger(0, child_index)) {
+ // If there was no integer anywhere in the index expression, this is
+ // erroneous expression.
+ error.SetErrorStringWithFormat("invalid index expression \"%s\"",
+ index_expr.str().c_str());
+ return ValueObjectSP();
+ }
+
+ if (index_expr.empty()) {
+ // The entire index expression was a single integer.
+
+ if (valobj_sp->GetCompilerType().IsPointerToScalarType() && deref) {
+ // what we have is *ptr[low]. the most similar C++ syntax is to deref
+ // ptr and extract bit low out of it. reading array item low would be
+ // done by saying ptr[low], without a deref * sign
+ Status error;
+ ValueObjectSP temp(valobj_sp->Dereference(error));
+ if (error.Fail()) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "could not dereference \"(%s) %s\"",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ return ValueObjectSP();
+ }
+ valobj_sp = temp;
+ deref = false;
+ } else if (valobj_sp->GetCompilerType().IsArrayOfScalarType() &&
+ deref) {
+ // what we have is *arr[low]. the most similar C++ syntax is to get
+ // arr[0] (an operation that is equivalent to deref-ing arr) and
+ // extract bit low out of it. reading array item low would be done by
+ // saying arr[low], without a deref * sign
+ Status error;
+ ValueObjectSP temp(valobj_sp->GetChildAtIndex(0, true));
+ if (error.Fail()) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "could not get item 0 for \"(%s) %s\"",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ return ValueObjectSP();
+ }
+ valobj_sp = temp;
+ deref = false;
+ }
+
+ bool is_incomplete_array = false;
+ if (valobj_sp->IsPointerType()) {
+ bool is_objc_pointer = true;
+
+ if (valobj_sp->GetCompilerType().GetMinimumLanguage() !=
+ eLanguageTypeObjC)
+ is_objc_pointer = false;
+ else if (!valobj_sp->GetCompilerType().IsPointerType())
+ is_objc_pointer = false;
+
+ if (no_synth_child && is_objc_pointer) {
+ error.SetErrorStringWithFormat(
+ "\"(%s) %s\" is an Objective-C pointer, and cannot be "
+ "subscripted",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+
+ return ValueObjectSP();
+ } else if (is_objc_pointer) {
+ // dereferencing ObjC variables is not valid.. so let's try and
+ // recur to synthetic children
+ ValueObjectSP synthetic = valobj_sp->GetSyntheticValue();
+ if (!synthetic /* no synthetic */
+ || synthetic == valobj_sp) /* synthetic is the same as
+ the original object */
+ {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "\"(%s) %s\" is not an array type",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ } else if (
+ static_cast<uint32_t>(child_index) >=
+ synthetic
+ ->GetNumChildren() /* synthetic does not have that many values */) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "array index %ld is not valid for \"(%s) %s\"", child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ } else {
+ child_valobj_sp = synthetic->GetChildAtIndex(child_index, true);
+ if (!child_valobj_sp) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "array index %ld is not valid for \"(%s) %s\"", child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ }
+ }
+ } else {
+ child_valobj_sp =
+ valobj_sp->GetSyntheticArrayMember(child_index, true);
+ if (!child_valobj_sp) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "failed to use pointer as array for index %ld for "
+ "\"(%s) %s\"",
+ child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ }
+ }
+ } else if (valobj_sp->GetCompilerType().IsArrayType(
+ nullptr, nullptr, &is_incomplete_array)) {
+ // Pass false to dynamic_value here so we can tell the difference
+ // between no dynamic value and no member of this type...
+ child_valobj_sp = valobj_sp->GetChildAtIndex(child_index, true);
+ if (!child_valobj_sp && (is_incomplete_array || !no_synth_child))
+ child_valobj_sp =
+ valobj_sp->GetSyntheticArrayMember(child_index, true);
+
+ if (!child_valobj_sp) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "array index %ld is not valid for \"(%s) %s\"", child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ }
+ } else if (valobj_sp->GetCompilerType().IsScalarType()) {
+ // this is a bitfield asking to display just one bit
+ child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild(
+ child_index, child_index, true);
+ if (!child_valobj_sp) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "bitfield range %ld-%ld is not valid for \"(%s) %s\"",
+ child_index, child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ }
+ } else {
+ ValueObjectSP synthetic = valobj_sp->GetSyntheticValue();
+ if (no_synth_child /* synthetic is forbidden */ ||
+ !synthetic /* no synthetic */
+ || synthetic == valobj_sp) /* synthetic is the same as the
+ original object */
+ {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "\"(%s) %s\" is not an array type",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ } else if (
+ static_cast<uint32_t>(child_index) >=
+ synthetic
+ ->GetNumChildren() /* synthetic does not have that many values */) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "array index %ld is not valid for \"(%s) %s\"", child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ } else {
+ child_valobj_sp = synthetic->GetChildAtIndex(child_index, true);
+ if (!child_valobj_sp) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "array index %ld is not valid for \"(%s) %s\"", child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ }
+ }
+ }
+
+ if (!child_valobj_sp) {
+ // Invalid array index...
+ return ValueObjectSP();
+ }
+
+ separator_idx = var_expr.find_first_of(".-[");
+ if (use_dynamic != eNoDynamicValues) {
+ ValueObjectSP dynamic_value_sp(
+ child_valobj_sp->GetDynamicValue(use_dynamic));
+ if (dynamic_value_sp)
+ child_valobj_sp = dynamic_value_sp;
+ }
+ // Break out early from the switch since we were able to find the child
+ // member
+ break;
+ }
+
+ // this is most probably a BitField, let's take a look
+ if (index_expr.front() != '-') {
+ error.SetErrorStringWithFormat("invalid range expression \"'%s'\"",
+ original_index_expr.str().c_str());
+ return ValueObjectSP();
+ }
+
+ index_expr = index_expr.drop_front();
+ long final_index = 0;
+ if (index_expr.getAsInteger(0, final_index)) {
+ error.SetErrorStringWithFormat("invalid range expression \"'%s'\"",
+ original_index_expr.str().c_str());
+ return ValueObjectSP();
+ }
+
+ // if the format given is [high-low], swap range
+ if (child_index > final_index) {
+ long temp = child_index;
+ child_index = final_index;
+ final_index = temp;
+ }
+
+ if (valobj_sp->GetCompilerType().IsPointerToScalarType() && deref) {
+ // what we have is *ptr[low-high]. the most similar C++ syntax is to
+ // deref ptr and extract bits low thru high out of it. reading array
+ // items low thru high would be done by saying ptr[low-high], without a
+ // deref * sign
+ Status error;
+ ValueObjectSP temp(valobj_sp->Dereference(error));
+ if (error.Fail()) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "could not dereference \"(%s) %s\"",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ return ValueObjectSP();
+ }
+ valobj_sp = temp;
+ deref = false;
+ } else if (valobj_sp->GetCompilerType().IsArrayOfScalarType() && deref) {
+ // what we have is *arr[low-high]. the most similar C++ syntax is to
+ // get arr[0] (an operation that is equivalent to deref-ing arr) and
+ // extract bits low thru high out of it. reading array items low thru
+ // high would be done by saying arr[low-high], without a deref * sign
+ Status error;
+ ValueObjectSP temp(valobj_sp->GetChildAtIndex(0, true));
+ if (error.Fail()) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "could not get item 0 for \"(%s) %s\"",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ return ValueObjectSP();
+ }
+ valobj_sp = temp;
+ deref = false;
+ }
+
+ child_valobj_sp =
+ valobj_sp->GetSyntheticBitFieldChild(child_index, final_index, true);
+ if (!child_valobj_sp) {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "bitfield range %ld-%ld is not valid for \"(%s) %s\"", child_index,
+ final_index, valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ }
+
+ if (!child_valobj_sp) {
+ // Invalid bitfield range...
+ return ValueObjectSP();
+ }
+
+ separator_idx = var_expr.find_first_of(".-[");
+ if (use_dynamic != eNoDynamicValues) {
+ ValueObjectSP dynamic_value_sp(
+ child_valobj_sp->GetDynamicValue(use_dynamic));
+ if (dynamic_value_sp)
+ child_valobj_sp = dynamic_value_sp;
+ }
+ // Break out early from the switch since we were able to find the child
+ // member
+ break;
+ }
+ default:
+ // Failure...
+ {
+ valobj_sp->GetExpressionPath(var_expr_path_strm, false);
+ error.SetErrorStringWithFormat(
+ "unexpected char '%c' encountered after \"%s\" in \"%s\"",
+ separator_type, var_expr_path_strm.GetData(),
+ var_expr.str().c_str());
+
+ return ValueObjectSP();
+ }
+ }
+
+ if (child_valobj_sp)
+ valobj_sp = child_valobj_sp;
+
+ if (var_expr.empty())
+ break;
+ }
+ if (valobj_sp) {
+ if (deref) {
+ ValueObjectSP deref_valobj_sp(valobj_sp->Dereference(error));
+ valobj_sp = deref_valobj_sp;
+ } else if (address_of) {
+ ValueObjectSP address_of_valobj_sp(valobj_sp->AddressOf(error));
+ valobj_sp = address_of_valobj_sp;
+ }
+ }
+ return valobj_sp;
+}
+
+bool StackFrame::GetFrameBaseValue(Scalar &frame_base, Status *error_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_cfa_is_valid) {
+ m_frame_base_error.SetErrorString(
+ "No frame base available for this historical stack frame.");
+ return false;
+ }
+
+ if (m_flags.IsClear(GOT_FRAME_BASE)) {
+ if (m_sc.function) {
+ m_frame_base.Clear();
+ m_frame_base_error.Clear();
+
+ m_flags.Set(GOT_FRAME_BASE);
+ ExecutionContext exe_ctx(shared_from_this());
+ Value expr_value;
+ addr_t loclist_base_addr = LLDB_INVALID_ADDRESS;
+ if (m_sc.function->GetFrameBaseExpression().IsLocationList())
+ loclist_base_addr =
+ m_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(
+ exe_ctx.GetTargetPtr());
+
+ if (!m_sc.function->GetFrameBaseExpression().Evaluate(
+ &exe_ctx, nullptr, loclist_base_addr, nullptr, nullptr,
+ expr_value, &m_frame_base_error)) {
+ // We should really have an error if evaluate returns, but in case we
+ // don't, lets set the error to something at least.
+ if (m_frame_base_error.Success())
+ m_frame_base_error.SetErrorString(
+ "Evaluation of the frame base expression failed.");
+ } else {
+ m_frame_base = expr_value.ResolveValue(&exe_ctx);
+ }
+ } else {
+ m_frame_base_error.SetErrorString("No function in symbol context.");
+ }
+ }
+
+ if (m_frame_base_error.Success())
+ frame_base = m_frame_base;
+
+ if (error_ptr)
+ *error_ptr = m_frame_base_error;
+ return m_frame_base_error.Success();
+}
+
+DWARFExpression *StackFrame::GetFrameBaseExpression(Status *error_ptr) {
+ if (!m_sc.function) {
+ if (error_ptr) {
+ error_ptr->SetErrorString("No function in symbol context.");
+ }
+ return nullptr;
+ }
+
+ return &m_sc.function->GetFrameBaseExpression();
+}
+
+RegisterContextSP StackFrame::GetRegisterContext() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_reg_context_sp) {
+ ThreadSP thread_sp(GetThread());
+ if (thread_sp)
+ m_reg_context_sp = thread_sp->CreateRegisterContextForFrame(this);
+ }
+ return m_reg_context_sp;
+}
+
+bool StackFrame::HasDebugInformation() {
+ GetSymbolContext(eSymbolContextLineEntry);
+ return m_sc.line_entry.IsValid();
+}
+
+ValueObjectSP
+StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp,
+ DynamicValueType use_dynamic) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ ValueObjectSP valobj_sp;
+ if (IsHistorical()) {
+ return valobj_sp;
+ }
+ VariableList *var_list = GetVariableList(true);
+ if (var_list) {
+ // Make sure the variable is a frame variable
+ const uint32_t var_idx = var_list->FindIndexForVariable(variable_sp.get());
+ const uint32_t num_variables = var_list->GetSize();
+ if (var_idx < num_variables) {
+ valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex(var_idx);
+ if (!valobj_sp) {
+ if (m_variable_list_value_objects.GetSize() < num_variables)
+ m_variable_list_value_objects.Resize(num_variables);
+ valobj_sp = ValueObjectVariable::Create(this, variable_sp);
+ m_variable_list_value_objects.SetValueObjectAtIndex(var_idx, valobj_sp);
+ }
+ }
+ }
+ if (use_dynamic != eNoDynamicValues && valobj_sp) {
+ ValueObjectSP dynamic_sp = valobj_sp->GetDynamicValue(use_dynamic);
+ if (dynamic_sp)
+ return dynamic_sp;
+ }
+ return valobj_sp;
+}
+
+ValueObjectSP StackFrame::TrackGlobalVariable(const VariableSP &variable_sp,
+ DynamicValueType use_dynamic) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (IsHistorical())
+ return ValueObjectSP();
+
+ // Check to make sure we aren't already tracking this variable?
+ ValueObjectSP valobj_sp(
+ GetValueObjectForFrameVariable(variable_sp, use_dynamic));
+ if (!valobj_sp) {
+ // We aren't already tracking this global
+ VariableList *var_list = GetVariableList(true);
+ // If this frame has no variables, create a new list
+ if (var_list == nullptr)
+ m_variable_list_sp = std::make_shared<VariableList>();
+
+ // Add the global/static variable to this frame
+ m_variable_list_sp->AddVariable(variable_sp);
+
+ // Now make a value object for it so we can track its changes
+ valobj_sp = GetValueObjectForFrameVariable(variable_sp, use_dynamic);
+ }
+ return valobj_sp;
+}
+
+bool StackFrame::IsInlined() {
+ if (m_sc.block == nullptr)
+ GetSymbolContext(eSymbolContextBlock);
+ if (m_sc.block)
+ return m_sc.block->GetContainingInlinedBlock() != nullptr;
+ return false;
+}
+
+bool StackFrame::IsHistorical() const {
+ return m_stack_frame_kind == StackFrame::Kind::History;
+}
+
+bool StackFrame::IsArtificial() const {
+ return m_stack_frame_kind == StackFrame::Kind::Artificial;
+}
+
+lldb::LanguageType StackFrame::GetLanguage() {
+ CompileUnit *cu = GetSymbolContext(eSymbolContextCompUnit).comp_unit;
+ if (cu)
+ return cu->GetLanguage();
+ return lldb::eLanguageTypeUnknown;
+}
+
+lldb::LanguageType StackFrame::GuessLanguage() {
+ LanguageType lang_type = GetLanguage();
+
+ if (lang_type == eLanguageTypeUnknown) {
+ SymbolContext sc = GetSymbolContext(eSymbolContextFunction
+ | eSymbolContextSymbol);
+ if (sc.function) {
+ lang_type = sc.function->GetMangled().GuessLanguage();
+ }
+ else if (sc.symbol)
+ {
+ lang_type = sc.symbol->GetMangled().GuessLanguage();
+ }
+ }
+
+ return lang_type;
+}
+
+namespace {
+std::pair<const Instruction::Operand *, int64_t>
+GetBaseExplainingValue(const Instruction::Operand &operand,
+ RegisterContext &register_context, lldb::addr_t value) {
+ switch (operand.m_type) {
+ case Instruction::Operand::Type::Dereference:
+ case Instruction::Operand::Type::Immediate:
+ case Instruction::Operand::Type::Invalid:
+ case Instruction::Operand::Type::Product:
+ // These are not currently interesting
+ return std::make_pair(nullptr, 0);
+ case Instruction::Operand::Type::Sum: {
+ const Instruction::Operand *immediate_child = nullptr;
+ const Instruction::Operand *variable_child = nullptr;
+ if (operand.m_children[0].m_type == Instruction::Operand::Type::Immediate) {
+ immediate_child = &operand.m_children[0];
+ variable_child = &operand.m_children[1];
+ } else if (operand.m_children[1].m_type ==
+ Instruction::Operand::Type::Immediate) {
+ immediate_child = &operand.m_children[1];
+ variable_child = &operand.m_children[0];
+ }
+ if (!immediate_child) {
+ return std::make_pair(nullptr, 0);
+ }
+ lldb::addr_t adjusted_value = value;
+ if (immediate_child->m_negative) {
+ adjusted_value += immediate_child->m_immediate;
+ } else {
+ adjusted_value -= immediate_child->m_immediate;
+ }
+ std::pair<const Instruction::Operand *, int64_t> base_and_offset =
+ GetBaseExplainingValue(*variable_child, register_context,
+ adjusted_value);
+ if (!base_and_offset.first) {
+ return std::make_pair(nullptr, 0);
+ }
+ if (immediate_child->m_negative) {
+ base_and_offset.second -= immediate_child->m_immediate;
+ } else {
+ base_and_offset.second += immediate_child->m_immediate;
+ }
+ return base_and_offset;
+ }
+ case Instruction::Operand::Type::Register: {
+ const RegisterInfo *info =
+ register_context.GetRegisterInfoByName(operand.m_register.AsCString());
+ if (!info) {
+ return std::make_pair(nullptr, 0);
+ }
+ RegisterValue reg_value;
+ if (!register_context.ReadRegister(info, reg_value)) {
+ return std::make_pair(nullptr, 0);
+ }
+ if (reg_value.GetAsUInt64() == value) {
+ return std::make_pair(&operand, 0);
+ } else {
+ return std::make_pair(nullptr, 0);
+ }
+ }
+ }
+ return std::make_pair(nullptr, 0);
+}
+
+std::pair<const Instruction::Operand *, int64_t>
+GetBaseExplainingDereference(const Instruction::Operand &operand,
+ RegisterContext &register_context,
+ lldb::addr_t addr) {
+ if (operand.m_type == Instruction::Operand::Type::Dereference) {
+ return GetBaseExplainingValue(operand.m_children[0], register_context,
+ addr);
+ }
+ return std::make_pair(nullptr, 0);
+}
+}
+
+lldb::ValueObjectSP StackFrame::GuessValueForAddress(lldb::addr_t addr) {
+ TargetSP target_sp = CalculateTarget();
+
+ const ArchSpec &target_arch = target_sp->GetArchitecture();
+
+ AddressRange pc_range;
+ pc_range.GetBaseAddress() = GetFrameCodeAddress();
+ pc_range.SetByteSize(target_arch.GetMaximumOpcodeByteSize());
+
+ ExecutionContext exe_ctx(shared_from_this());
+
+ const char *plugin_name = nullptr;
+ const char *flavor = nullptr;
+ const bool prefer_file_cache = false;
+
+ DisassemblerSP disassembler_sp = Disassembler::DisassembleRange(
+ target_arch, plugin_name, flavor, exe_ctx, pc_range, prefer_file_cache);
+
+ if (!disassembler_sp || !disassembler_sp->GetInstructionList().GetSize()) {
+ return ValueObjectSP();
+ }
+
+ InstructionSP instruction_sp =
+ disassembler_sp->GetInstructionList().GetInstructionAtIndex(0);
+
+ llvm::SmallVector<Instruction::Operand, 3> operands;
+
+ if (!instruction_sp->ParseOperands(operands)) {
+ return ValueObjectSP();
+ }
+
+ RegisterContextSP register_context_sp = GetRegisterContext();
+
+ if (!register_context_sp) {
+ return ValueObjectSP();
+ }
+
+ for (const Instruction::Operand &operand : operands) {
+ std::pair<const Instruction::Operand *, int64_t> base_and_offset =
+ GetBaseExplainingDereference(operand, *register_context_sp, addr);
+
+ if (!base_and_offset.first) {
+ continue;
+ }
+
+ switch (base_and_offset.first->m_type) {
+ case Instruction::Operand::Type::Immediate: {
+ lldb_private::Address addr;
+ if (target_sp->ResolveLoadAddress(base_and_offset.first->m_immediate +
+ base_and_offset.second,
+ addr)) {
+ TypeSystem *c_type_system =
+ target_sp->GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC);
+ if (!c_type_system) {
+ return ValueObjectSP();
+ } else {
+ CompilerType void_ptr_type =
+ c_type_system
+ ->GetBasicTypeFromAST(lldb::BasicType::eBasicTypeChar)
+ .GetPointerType();
+ return ValueObjectMemory::Create(this, "", addr, void_ptr_type);
+ }
+ } else {
+ return ValueObjectSP();
+ }
+ break;
+ }
+ case Instruction::Operand::Type::Register: {
+ return GuessValueForRegisterAndOffset(base_and_offset.first->m_register,
+ base_and_offset.second);
+ }
+ default:
+ return ValueObjectSP();
+ }
+ }
+
+ return ValueObjectSP();
+}
+
+namespace {
+ValueObjectSP GetValueForOffset(StackFrame &frame, ValueObjectSP &parent,
+ int64_t offset) {
+ if (offset < 0 || uint64_t(offset) >= parent->GetByteSize()) {
+ return ValueObjectSP();
+ }
+
+ if (parent->IsPointerOrReferenceType()) {
+ return parent;
+ }
+
+ for (int ci = 0, ce = parent->GetNumChildren(); ci != ce; ++ci) {
+ const bool can_create = true;
+ ValueObjectSP child_sp = parent->GetChildAtIndex(ci, can_create);
+
+ if (!child_sp) {
+ return ValueObjectSP();
+ }
+
+ int64_t child_offset = child_sp->GetByteOffset();
+ int64_t child_size = child_sp->GetByteSize();
+
+ if (offset >= child_offset && offset < (child_offset + child_size)) {
+ return GetValueForOffset(frame, child_sp, offset - child_offset);
+ }
+ }
+
+ if (offset == 0) {
+ return parent;
+ } else {
+ return ValueObjectSP();
+ }
+}
+
+ValueObjectSP GetValueForDereferincingOffset(StackFrame &frame,
+ ValueObjectSP &base,
+ int64_t offset) {
+ // base is a pointer to something
+ // offset is the thing to add to the pointer We return the most sensible
+ // ValueObject for the result of *(base+offset)
+
+ if (!base->IsPointerOrReferenceType()) {
+ return ValueObjectSP();
+ }
+
+ Status error;
+ ValueObjectSP pointee = base->Dereference(error);
+
+ if (!pointee) {
+ return ValueObjectSP();
+ }
+
+ if (offset >= 0 && uint64_t(offset) >= pointee->GetByteSize()) {
+ int64_t index = offset / pointee->GetByteSize();
+ offset = offset % pointee->GetByteSize();
+ const bool can_create = true;
+ pointee = base->GetSyntheticArrayMember(index, can_create);
+ }
+
+ if (!pointee || error.Fail()) {
+ return ValueObjectSP();
+ }
+
+ return GetValueForOffset(frame, pointee, offset);
+}
+
+/// Attempt to reconstruct the ValueObject for the address contained in a
+/// given register plus an offset.
+///
+/// \params [in] frame
+/// The current stack frame.
+///
+/// \params [in] reg
+/// The register.
+///
+/// \params [in] offset
+/// The offset from the register.
+///
+/// \param [in] disassembler
+/// A disassembler containing instructions valid up to the current PC.
+///
+/// \param [in] variables
+/// The variable list from the current frame,
+///
+/// \param [in] pc
+/// The program counter for the instruction considered the 'user'.
+///
+/// \return
+/// A string describing the base for the ExpressionPath. This could be a
+/// variable, a register value, an argument, or a function return value.
+/// The ValueObject if found. If valid, it has a valid ExpressionPath.
+lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg,
+ int64_t offset, Disassembler &disassembler,
+ VariableList &variables, const Address &pc) {
+ // Example of operation for Intel:
+ //
+ // +14: movq -0x8(%rbp), %rdi
+ // +18: movq 0x8(%rdi), %rdi
+ // +22: addl 0x4(%rdi), %eax
+ //
+ // f, a pointer to a struct, is known to be at -0x8(%rbp).
+ //
+ // DoGuessValueAt(frame, rdi, 4, dis, vars, 0x22) finds the instruction at
+ // +18 that assigns to rdi, and calls itself recursively for that dereference
+ // DoGuessValueAt(frame, rdi, 8, dis, vars, 0x18) finds the instruction at
+ // +14 that assigns to rdi, and calls itself recursively for that
+ // derefernece
+ // DoGuessValueAt(frame, rbp, -8, dis, vars, 0x14) finds "f" in the
+ // variable list.
+ // Returns a ValueObject for f. (That's what was stored at rbp-8 at +14)
+ // Returns a ValueObject for *(f+8) or f->b (That's what was stored at rdi+8
+ // at +18)
+ // Returns a ValueObject for *(f->b+4) or f->b->a (That's what was stored at
+ // rdi+4 at +22)
+
+ // First, check the variable list to see if anything is at the specified
+ // location.
+
+ using namespace OperandMatchers;
+
+ const RegisterInfo *reg_info =
+ frame.GetRegisterContext()->GetRegisterInfoByName(reg.AsCString());
+ if (!reg_info) {
+ return ValueObjectSP();
+ }
+
+ Instruction::Operand op =
+ offset ? Instruction::Operand::BuildDereference(
+ Instruction::Operand::BuildSum(
+ Instruction::Operand::BuildRegister(reg),
+ Instruction::Operand::BuildImmediate(offset)))
+ : Instruction::Operand::BuildDereference(
+ Instruction::Operand::BuildRegister(reg));
+
+ for (size_t vi = 0, ve = variables.GetSize(); vi != ve; ++vi) {
+ VariableSP var_sp = variables.GetVariableAtIndex(vi);
+ if (var_sp->LocationExpression().MatchesOperand(frame, op)) {
+ return frame.GetValueObjectForFrameVariable(var_sp, eNoDynamicValues);
+ }
+ }
+
+ const uint32_t current_inst =
+ disassembler.GetInstructionList().GetIndexOfInstructionAtAddress(pc);
+ if (current_inst == UINT32_MAX) {
+ return ValueObjectSP();
+ }
+
+ for (uint32_t ii = current_inst - 1; ii != (uint32_t)-1; --ii) {
+ // This is not an exact algorithm, and it sacrifices accuracy for
+ // generality. Recognizing "mov" and "ld" instructions –– and which
+ // are their source and destination operands -- is something the
+ // disassembler should do for us.
+ InstructionSP instruction_sp =
+ disassembler.GetInstructionList().GetInstructionAtIndex(ii);
+
+ if (instruction_sp->IsCall()) {
+ ABISP abi_sp = frame.CalculateProcess()->GetABI();
+ if (!abi_sp) {
+ continue;
+ }
+
+ const char *return_register_name;
+ if (!abi_sp->GetPointerReturnRegister(return_register_name)) {
+ continue;
+ }
+
+ const RegisterInfo *return_register_info =
+ frame.GetRegisterContext()->GetRegisterInfoByName(
+ return_register_name);
+ if (!return_register_info) {
+ continue;
+ }
+
+ int64_t offset = 0;
+
+ if (!MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference),
+ MatchRegOp(*return_register_info))(op) &&
+ !MatchUnaryOp(
+ MatchOpType(Instruction::Operand::Type::Dereference),
+ MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum),
+ MatchRegOp(*return_register_info),
+ FetchImmOp(offset)))(op)) {
+ continue;
+ }
+
+ llvm::SmallVector<Instruction::Operand, 1> operands;
+ if (!instruction_sp->ParseOperands(operands) || operands.size() != 1) {
+ continue;
+ }
+
+ switch (operands[0].m_type) {
+ default:
+ break;
+ case Instruction::Operand::Type::Immediate: {
+ SymbolContext sc;
+ Address load_address;
+ if (!frame.CalculateTarget()->ResolveLoadAddress(
+ operands[0].m_immediate, load_address)) {
+ break;
+ }
+ frame.CalculateTarget()->GetImages().ResolveSymbolContextForAddress(
+ load_address, eSymbolContextFunction, sc);
+ if (!sc.function) {
+ break;
+ }
+ CompilerType function_type = sc.function->GetCompilerType();
+ if (!function_type.IsFunctionType()) {
+ break;
+ }
+ CompilerType return_type = function_type.GetFunctionReturnType();
+ RegisterValue return_value;
+ if (!frame.GetRegisterContext()->ReadRegister(return_register_info,
+ return_value)) {
+ break;
+ }
+ std::string name_str(
+ sc.function->GetName().AsCString("<unknown function>"));
+ name_str.append("()");
+ Address return_value_address(return_value.GetAsUInt64());
+ ValueObjectSP return_value_sp = ValueObjectMemory::Create(
+ &frame, name_str, return_value_address, return_type);
+ return GetValueForDereferincingOffset(frame, return_value_sp, offset);
+ }
+ }
+
+ continue;
+ }
+
+ llvm::SmallVector<Instruction::Operand, 2> operands;
+ if (!instruction_sp->ParseOperands(operands) || operands.size() != 2) {
+ continue;
+ }
+
+ Instruction::Operand *origin_operand = nullptr;
+ auto clobbered_reg_matcher = [reg_info](const Instruction::Operand &op) {
+ return MatchRegOp(*reg_info)(op) && op.m_clobbered;
+ };
+
+ if (clobbered_reg_matcher(operands[0])) {
+ origin_operand = &operands[1];
+ }
+ else if (clobbered_reg_matcher(operands[1])) {
+ origin_operand = &operands[0];
+ }
+ else {
+ continue;
+ }
+
+ // We have an origin operand. Can we track its value down?
+ ValueObjectSP source_path;
+ ConstString origin_register;
+ int64_t origin_offset = 0;
+
+ if (FetchRegOp(origin_register)(*origin_operand)) {
+ source_path = DoGuessValueAt(frame, origin_register, 0, disassembler,
+ variables, instruction_sp->GetAddress());
+ } else if (MatchUnaryOp(
+ MatchOpType(Instruction::Operand::Type::Dereference),
+ FetchRegOp(origin_register))(*origin_operand) ||
+ MatchUnaryOp(
+ MatchOpType(Instruction::Operand::Type::Dereference),
+ MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum),
+ FetchRegOp(origin_register),
+ FetchImmOp(origin_offset)))(*origin_operand)) {
+ source_path =
+ DoGuessValueAt(frame, origin_register, origin_offset, disassembler,
+ variables, instruction_sp->GetAddress());
+ if (!source_path) {
+ continue;
+ }
+ source_path =
+ GetValueForDereferincingOffset(frame, source_path, offset);
+ }
+
+ if (source_path) {
+ return source_path;
+ }
+ }
+
+ return ValueObjectSP();
+}
+}
+
+lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg,
+ int64_t offset) {
+ TargetSP target_sp = CalculateTarget();
+
+ const ArchSpec &target_arch = target_sp->GetArchitecture();
+
+ Block *frame_block = GetFrameBlock();
+
+ if (!frame_block) {
+ return ValueObjectSP();
+ }
+
+ Function *function = frame_block->CalculateSymbolContextFunction();
+ if (!function) {
+ return ValueObjectSP();
+ }
+
+ AddressRange pc_range = function->GetAddressRange();
+
+ if (GetFrameCodeAddress().GetFileAddress() <
+ pc_range.GetBaseAddress().GetFileAddress() ||
+ GetFrameCodeAddress().GetFileAddress() -
+ pc_range.GetBaseAddress().GetFileAddress() >=
+ pc_range.GetByteSize()) {
+ return ValueObjectSP();
+ }
+
+ ExecutionContext exe_ctx(shared_from_this());
+
+ const char *plugin_name = nullptr;
+ const char *flavor = nullptr;
+ const bool prefer_file_cache = false;
+ DisassemblerSP disassembler_sp = Disassembler::DisassembleRange(
+ target_arch, plugin_name, flavor, exe_ctx, pc_range, prefer_file_cache);
+
+ if (!disassembler_sp || !disassembler_sp->GetInstructionList().GetSize()) {
+ return ValueObjectSP();
+ }
+
+ const bool get_file_globals = false;
+ VariableList *variables = GetVariableList(get_file_globals);
+
+ if (!variables) {
+ return ValueObjectSP();
+ }
+
+ return DoGuessValueAt(*this, reg, offset, *disassembler_sp, *variables,
+ GetFrameCodeAddress());
+}
+
+lldb::ValueObjectSP StackFrame::FindVariable(ConstString name) {
+ ValueObjectSP value_sp;
+
+ if (!name)
+ return value_sp;
+
+ TargetSP target_sp = CalculateTarget();
+ ProcessSP process_sp = CalculateProcess();
+
+ if (!target_sp && !process_sp)
+ return value_sp;
+
+ VariableList variable_list;
+ VariableSP var_sp;
+ SymbolContext sc(GetSymbolContext(eSymbolContextBlock));
+
+ if (sc.block) {
+ const bool can_create = true;
+ const bool get_parent_variables = true;
+ const bool stop_if_block_is_inlined_function = true;
+
+ if (sc.block->AppendVariables(
+ can_create, get_parent_variables, stop_if_block_is_inlined_function,
+ [this](Variable *v) { return v->IsInScope(this); },
+ &variable_list)) {
+ var_sp = variable_list.FindVariable(name);
+ }
+
+ if (var_sp)
+ value_sp = GetValueObjectForFrameVariable(var_sp, eNoDynamicValues);
+ }
+
+ return value_sp;
+}
+
+TargetSP StackFrame::CalculateTarget() {
+ TargetSP target_sp;
+ ThreadSP thread_sp(GetThread());
+ if (thread_sp) {
+ ProcessSP process_sp(thread_sp->CalculateProcess());
+ if (process_sp)
+ target_sp = process_sp->CalculateTarget();
+ }
+ return target_sp;
+}
+
+ProcessSP StackFrame::CalculateProcess() {
+ ProcessSP process_sp;
+ ThreadSP thread_sp(GetThread());
+ if (thread_sp)
+ process_sp = thread_sp->CalculateProcess();
+ return process_sp;
+}
+
+ThreadSP StackFrame::CalculateThread() { return GetThread(); }
+
+StackFrameSP StackFrame::CalculateStackFrame() { return shared_from_this(); }
+
+void StackFrame::CalculateExecutionContext(ExecutionContext &exe_ctx) {
+ exe_ctx.SetContext(shared_from_this());
+}
+
+void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
+ const char *frame_marker) {
+ if (strm == nullptr)
+ return;
+
+ GetSymbolContext(eSymbolContextEverything);
+ ExecutionContext exe_ctx(shared_from_this());
+ StreamString s;
+
+ if (frame_marker)
+ s.PutCString(frame_marker);
+
+ const FormatEntity::Entry *frame_format = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ if (show_unique) {
+ frame_format = target->GetDebugger().GetFrameFormatUnique();
+ } else {
+ frame_format = target->GetDebugger().GetFrameFormat();
+ }
+ }
+ if (frame_format && FormatEntity::Format(*frame_format, s, &m_sc, &exe_ctx,
+ nullptr, nullptr, false, false)) {
+ strm->PutCString(s.GetString());
+ } else {
+ Dump(strm, true, false);
+ strm->EOL();
+ }
+}
+
+void StackFrame::Dump(Stream *strm, bool show_frame_index,
+ bool show_fullpaths) {
+ if (strm == nullptr)
+ return;
+
+ if (show_frame_index)
+ strm->Printf("frame #%u: ", m_frame_index);
+ ExecutionContext exe_ctx(shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ strm->Printf("0x%0*" PRIx64 " ",
+ target ? (target->GetArchitecture().GetAddressByteSize() * 2)
+ : 16,
+ GetFrameCodeAddress().GetLoadAddress(target));
+ GetSymbolContext(eSymbolContextEverything);
+ const bool show_module = true;
+ const bool show_inline = true;
+ const bool show_function_arguments = true;
+ const bool show_function_name = true;
+ m_sc.DumpStopContext(strm, exe_ctx.GetBestExecutionContextScope(),
+ GetFrameCodeAddress(), show_fullpaths, show_module,
+ show_inline, show_function_arguments,
+ show_function_name);
+}
+
+void StackFrame::UpdateCurrentFrameFromPreviousFrame(StackFrame &prev_frame) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ assert(GetStackID() ==
+ prev_frame.GetStackID()); // TODO: remove this after some testing
+ m_variable_list_sp = prev_frame.m_variable_list_sp;
+ m_variable_list_value_objects.Swap(prev_frame.m_variable_list_value_objects);
+ if (!m_disassembly.GetString().empty()) {
+ m_disassembly.Clear();
+ m_disassembly.PutCString(prev_frame.m_disassembly.GetString());
+ }
+}
+
+void StackFrame::UpdatePreviousFrameFromCurrentFrame(StackFrame &curr_frame) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ assert(GetStackID() ==
+ curr_frame.GetStackID()); // TODO: remove this after some testing
+ m_id.SetPC(curr_frame.m_id.GetPC()); // Update the Stack ID PC value
+ assert(GetThread() == curr_frame.GetThread());
+ m_frame_index = curr_frame.m_frame_index;
+ m_concrete_frame_index = curr_frame.m_concrete_frame_index;
+ m_reg_context_sp = curr_frame.m_reg_context_sp;
+ m_frame_code_addr = curr_frame.m_frame_code_addr;
+ assert(!m_sc.target_sp || !curr_frame.m_sc.target_sp ||
+ m_sc.target_sp.get() == curr_frame.m_sc.target_sp.get());
+ assert(!m_sc.module_sp || !curr_frame.m_sc.module_sp ||
+ m_sc.module_sp.get() == curr_frame.m_sc.module_sp.get());
+ assert(m_sc.comp_unit == nullptr || curr_frame.m_sc.comp_unit == nullptr ||
+ m_sc.comp_unit == curr_frame.m_sc.comp_unit);
+ assert(m_sc.function == nullptr || curr_frame.m_sc.function == nullptr ||
+ m_sc.function == curr_frame.m_sc.function);
+ m_sc = curr_frame.m_sc;
+ m_flags.Clear(GOT_FRAME_BASE | eSymbolContextEverything);
+ m_flags.Set(m_sc.GetResolvedMask());
+ m_frame_base.Clear();
+ m_frame_base_error.Clear();
+}
+
+bool StackFrame::HasCachedData() const {
+ if (m_variable_list_sp)
+ return true;
+ if (m_variable_list_value_objects.GetSize() > 0)
+ return true;
+ if (!m_disassembly.GetString().empty())
+ return true;
+ return false;
+}
+
+bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
+ bool show_unique, const char *frame_marker) {
+ if (show_frame_info) {
+ strm.Indent();
+ DumpUsingSettingsFormat(&strm, show_unique, frame_marker);
+ }
+
+ if (show_source) {
+ ExecutionContext exe_ctx(shared_from_this());
+ bool have_source = false, have_debuginfo = false;
+ Debugger::StopDisassemblyType disasm_display =
+ Debugger::eStopDisassemblyTypeNever;
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ Debugger &debugger = target->GetDebugger();
+ const uint32_t source_lines_before =
+ debugger.GetStopSourceLineCount(true);
+ const uint32_t source_lines_after =
+ debugger.GetStopSourceLineCount(false);
+ disasm_display = debugger.GetStopDisassemblyDisplay();
+
+ GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry);
+ if (m_sc.comp_unit && m_sc.line_entry.IsValid()) {
+ have_debuginfo = true;
+ if (source_lines_before > 0 || source_lines_after > 0) {
+ size_t num_lines =
+ target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
+ m_sc.line_entry.file, m_sc.line_entry.line,
+ m_sc.line_entry.column, source_lines_before,
+ source_lines_after, "->", &strm);
+ if (num_lines != 0)
+ have_source = true;
+ // TODO: Give here a one time warning if source file is missing.
+ }
+ }
+ switch (disasm_display) {
+ case Debugger::eStopDisassemblyTypeNever:
+ break;
+
+ case Debugger::eStopDisassemblyTypeNoDebugInfo:
+ if (have_debuginfo)
+ break;
+ LLVM_FALLTHROUGH;
+
+ case Debugger::eStopDisassemblyTypeNoSource:
+ if (have_source)
+ break;
+ LLVM_FALLTHROUGH;
+
+ case Debugger::eStopDisassemblyTypeAlways:
+ if (target) {
+ const uint32_t disasm_lines = debugger.GetDisassemblyLineCount();
+ if (disasm_lines > 0) {
+ const ArchSpec &target_arch = target->GetArchitecture();
+ AddressRange pc_range;
+ pc_range.GetBaseAddress() = GetFrameCodeAddress();
+ pc_range.SetByteSize(disasm_lines *
+ target_arch.GetMaximumOpcodeByteSize());
+ const char *plugin_name = nullptr;
+ const char *flavor = nullptr;
+ const bool mixed_source_and_assembly = false;
+ Disassembler::Disassemble(
+ target->GetDebugger(), target_arch, plugin_name, flavor,
+ exe_ctx, pc_range, disasm_lines, mixed_source_and_assembly, 0,
+ Disassembler::eOptionMarkPCAddress, strm);
+ }
+ }
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+RecognizedStackFrameSP StackFrame::GetRecognizedFrame() {
+ if (!m_recognized_frame_sp) {
+ m_recognized_frame_sp =
+ StackFrameRecognizerManager::RecognizeFrame(CalculateStackFrame());
+ }
+ return m_recognized_frame_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/StackFrameList.cpp b/contrib/llvm-project/lldb/source/Target/StackFrameList.cpp
new file mode 100644
index 000000000000..5492dda46402
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/StackFrameList.cpp
@@ -0,0 +1,986 @@
+//===-- StackFrameList.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/StackFrameList.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Utility/Log.h"
+#include "llvm/ADT/SmallPtrSet.h"
+
+#include <memory>
+
+//#define DEBUG_STACK_FRAMES 1
+
+using namespace lldb;
+using namespace lldb_private;
+
+// StackFrameList constructor
+StackFrameList::StackFrameList(Thread &thread,
+ const lldb::StackFrameListSP &prev_frames_sp,
+ bool show_inline_frames)
+ : m_thread(thread), m_prev_frames_sp(prev_frames_sp), m_mutex(), m_frames(),
+ m_selected_frame_idx(0), m_concrete_frames_fetched(0),
+ m_current_inlined_depth(UINT32_MAX),
+ m_current_inlined_pc(LLDB_INVALID_ADDRESS),
+ m_show_inlined_frames(show_inline_frames) {
+ if (prev_frames_sp) {
+ m_current_inlined_depth = prev_frames_sp->m_current_inlined_depth;
+ m_current_inlined_pc = prev_frames_sp->m_current_inlined_pc;
+ }
+}
+
+StackFrameList::~StackFrameList() {
+ // Call clear since this takes a lock and clears the stack frame list in case
+ // another thread is currently using this stack frame list
+ Clear();
+}
+
+void StackFrameList::CalculateCurrentInlinedDepth() {
+ uint32_t cur_inlined_depth = GetCurrentInlinedDepth();
+ if (cur_inlined_depth == UINT32_MAX) {
+ ResetCurrentInlinedDepth();
+ }
+}
+
+uint32_t StackFrameList::GetCurrentInlinedDepth() {
+ if (m_show_inlined_frames && m_current_inlined_pc != LLDB_INVALID_ADDRESS) {
+ lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC();
+ if (cur_pc != m_current_inlined_pc) {
+ m_current_inlined_pc = LLDB_INVALID_ADDRESS;
+ m_current_inlined_depth = UINT32_MAX;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf(
+ "GetCurrentInlinedDepth: invalidating current inlined depth.\n");
+ }
+ return m_current_inlined_depth;
+ } else {
+ return UINT32_MAX;
+ }
+}
+
+void StackFrameList::ResetCurrentInlinedDepth() {
+ if (!m_show_inlined_frames)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ GetFramesUpTo(0);
+ if (m_frames.empty())
+ return;
+ if (!m_frames[0]->IsInlined()) {
+ m_current_inlined_depth = UINT32_MAX;
+ m_current_inlined_pc = LLDB_INVALID_ADDRESS;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf(
+ "ResetCurrentInlinedDepth: Invalidating current inlined depth.\n");
+ return;
+ }
+
+ // We only need to do something special about inlined blocks when we are
+ // at the beginning of an inlined function:
+ // FIXME: We probably also have to do something special if the PC is at
+ // the END of an inlined function, which coincides with the end of either
+ // its containing function or another inlined function.
+
+ Block *block_ptr = m_frames[0]->GetFrameBlock();
+ if (!block_ptr)
+ return;
+
+ Address pc_as_address;
+ lldb::addr_t curr_pc = m_thread.GetRegisterContext()->GetPC();
+ pc_as_address.SetLoadAddress(curr_pc, &(m_thread.GetProcess()->GetTarget()));
+ AddressRange containing_range;
+ if (!block_ptr->GetRangeContainingAddress(pc_as_address, containing_range) ||
+ pc_as_address != containing_range.GetBaseAddress())
+ return;
+
+ // If we got here because of a breakpoint hit, then set the inlined depth
+ // depending on where the breakpoint was set. If we got here because of a
+ // crash, then set the inlined depth to the deepest most block. Otherwise,
+ // we stopped here naturally as the result of a step, so set ourselves in the
+ // containing frame of the whole set of nested inlines, so the user can then
+ // "virtually" step into the frames one by one, or next over the whole mess.
+ // Note: We don't have to handle being somewhere in the middle of the stack
+ // here, since ResetCurrentInlinedDepth doesn't get called if there is a
+ // valid inlined depth set.
+ StopInfoSP stop_info_sp = m_thread.GetStopInfo();
+ if (!stop_info_sp)
+ return;
+ switch (stop_info_sp->GetStopReason()) {
+ case eStopReasonWatchpoint:
+ case eStopReasonException:
+ case eStopReasonExec:
+ case eStopReasonSignal:
+ // In all these cases we want to stop in the deepest frame.
+ m_current_inlined_pc = curr_pc;
+ m_current_inlined_depth = 0;
+ break;
+ case eStopReasonBreakpoint: {
+ // FIXME: Figure out what this break point is doing, and set the inline
+ // depth appropriately. Be careful to take into account breakpoints that
+ // implement step over prologue, since that should do the default
+ // calculation. For now, if the breakpoints corresponding to this hit are
+ // all internal, I set the stop location to the top of the inlined stack,
+ // since that will make things like stepping over prologues work right.
+ // But if there are any non-internal breakpoints I do to the bottom of the
+ // stack, since that was the old behavior.
+ uint32_t bp_site_id = stop_info_sp->GetValue();
+ BreakpointSiteSP bp_site_sp(
+ m_thread.GetProcess()->GetBreakpointSiteList().FindByID(bp_site_id));
+ bool all_internal = true;
+ if (bp_site_sp) {
+ uint32_t num_owners = bp_site_sp->GetNumberOfOwners();
+ for (uint32_t i = 0; i < num_owners; i++) {
+ Breakpoint &bp_ref = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint();
+ if (!bp_ref.IsInternal()) {
+ all_internal = false;
+ }
+ }
+ }
+ if (!all_internal) {
+ m_current_inlined_pc = curr_pc;
+ m_current_inlined_depth = 0;
+ break;
+ }
+ }
+ LLVM_FALLTHROUGH;
+ default: {
+ // Otherwise, we should set ourselves at the container of the inlining, so
+ // that the user can descend into them. So first we check whether we have
+ // more than one inlined block sharing this PC:
+ int num_inlined_functions = 0;
+
+ for (Block *container_ptr = block_ptr->GetInlinedParent();
+ container_ptr != nullptr;
+ container_ptr = container_ptr->GetInlinedParent()) {
+ if (!container_ptr->GetRangeContainingAddress(pc_as_address,
+ containing_range))
+ break;
+ if (pc_as_address != containing_range.GetBaseAddress())
+ break;
+
+ num_inlined_functions++;
+ }
+ m_current_inlined_pc = curr_pc;
+ m_current_inlined_depth = num_inlined_functions + 1;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf("ResetCurrentInlinedDepth: setting inlined "
+ "depth: %d 0x%" PRIx64 ".\n",
+ m_current_inlined_depth, curr_pc);
+
+ break;
+ }
+ }
+}
+
+bool StackFrameList::DecrementCurrentInlinedDepth() {
+ if (m_show_inlined_frames) {
+ uint32_t current_inlined_depth = GetCurrentInlinedDepth();
+ if (current_inlined_depth != UINT32_MAX) {
+ if (current_inlined_depth > 0) {
+ m_current_inlined_depth--;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void StackFrameList::SetCurrentInlinedDepth(uint32_t new_depth) {
+ m_current_inlined_depth = new_depth;
+ if (new_depth == UINT32_MAX)
+ m_current_inlined_pc = LLDB_INVALID_ADDRESS;
+ else
+ m_current_inlined_pc = m_thread.GetRegisterContext()->GetPC();
+}
+
+void StackFrameList::GetOnlyConcreteFramesUpTo(uint32_t end_idx,
+ Unwind *unwinder) {
+ assert(m_thread.IsValid() && "Expected valid thread");
+ assert(m_frames.size() <= end_idx && "Expected there to be frames to fill");
+
+ if (end_idx < m_concrete_frames_fetched)
+ return;
+
+ if (!unwinder)
+ return;
+
+ uint32_t num_frames = unwinder->GetFramesUpTo(end_idx);
+ if (num_frames <= end_idx + 1) {
+ // Done unwinding.
+ m_concrete_frames_fetched = UINT32_MAX;
+ }
+
+ // Don't create the frames eagerly. Defer this work to GetFrameAtIndex,
+ // which can lazily query the unwinder to create frames.
+ m_frames.resize(num_frames);
+}
+
+/// Find the unique path through the call graph from \p begin (with return PC
+/// \p return_pc) to \p end. On success this path is stored into \p path, and
+/// on failure \p path is unchanged.
+static void FindInterveningFrames(Function &begin, Function &end,
+ Target &target, addr_t return_pc,
+ std::vector<Function *> &path,
+ ModuleList &images, Log *log) {
+ LLDB_LOG(log, "Finding frames between {0} and {1}, retn-pc={2:x}",
+ begin.GetDisplayName(), end.GetDisplayName(), return_pc);
+
+ // Find a non-tail calling edge with the correct return PC.
+ auto first_level_edges = begin.GetCallEdges();
+ if (log)
+ for (const CallEdge &edge : first_level_edges)
+ LLDB_LOG(log, "FindInterveningFrames: found call with retn-PC = {0:x}",
+ edge.GetReturnPCAddress(begin, target));
+ auto first_edge_it = std::lower_bound(
+ first_level_edges.begin(), first_level_edges.end(), return_pc,
+ [&](const CallEdge &edge, addr_t target_pc) {
+ return edge.GetReturnPCAddress(begin, target) < target_pc;
+ });
+ if (first_edge_it == first_level_edges.end() ||
+ first_edge_it->GetReturnPCAddress(begin, target) != return_pc) {
+ LLDB_LOG(log, "No call edge outgoing from {0} with retn-PC == {1:x}",
+ begin.GetDisplayName(), return_pc);
+ return;
+ }
+ CallEdge &first_edge = const_cast<CallEdge &>(*first_edge_it);
+
+ // The first callee may not be resolved, or there may be nothing to fill in.
+ Function *first_callee = first_edge.GetCallee(images);
+ if (!first_callee) {
+ LLDB_LOG(log, "Could not resolve callee");
+ return;
+ }
+ if (first_callee == &end) {
+ LLDB_LOG(log, "Not searching further, first callee is {0} (retn-PC: {1:x})",
+ end.GetDisplayName(), return_pc);
+ return;
+ }
+
+ // Run DFS on the tail-calling edges out of the first callee to find \p end.
+ // Fully explore the set of functions reachable from the first edge via tail
+ // calls in order to detect ambiguous executions.
+ struct DFS {
+ std::vector<Function *> active_path = {};
+ std::vector<Function *> solution_path = {};
+ llvm::SmallPtrSet<Function *, 2> visited_nodes = {};
+ bool ambiguous = false;
+ Function *end;
+ ModuleList &images;
+
+ DFS(Function *end, ModuleList &images) : end(end), images(images) {}
+
+ void search(Function *first_callee, std::vector<Function *> &path) {
+ dfs(first_callee);
+ if (!ambiguous)
+ path = std::move(solution_path);
+ }
+
+ void dfs(Function *callee) {
+ // Found a path to the target function.
+ if (callee == end) {
+ if (solution_path.empty())
+ solution_path = active_path;
+ else
+ ambiguous = true;
+ return;
+ }
+
+ // Terminate the search if tail recursion is found, or more generally if
+ // there's more than one way to reach a target. This errs on the side of
+ // caution: it conservatively stops searching when some solutions are
+ // still possible to save time in the average case.
+ if (!visited_nodes.insert(callee).second) {
+ ambiguous = true;
+ return;
+ }
+
+ // Search the calls made from this callee.
+ active_path.push_back(callee);
+ for (CallEdge &edge : callee->GetTailCallingEdges()) {
+ Function *next_callee = edge.GetCallee(images);
+ if (!next_callee)
+ continue;
+
+ dfs(next_callee);
+ if (ambiguous)
+ return;
+ }
+ active_path.pop_back();
+ }
+ };
+
+ DFS(&end, images).search(first_callee, path);
+}
+
+/// Given that \p next_frame will be appended to the frame list, synthesize
+/// tail call frames between the current end of the list and \p next_frame.
+/// If any frames are added, adjust the frame index of \p next_frame.
+///
+/// --------------
+/// | ... | <- Completed frames.
+/// --------------
+/// | prev_frame |
+/// --------------
+/// | ... | <- Artificial frames inserted here.
+/// --------------
+/// | next_frame |
+/// --------------
+/// | ... | <- Not-yet-visited frames.
+/// --------------
+void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) {
+ TargetSP target_sp = next_frame.CalculateTarget();
+ if (!target_sp)
+ return;
+
+ lldb::RegisterContextSP next_reg_ctx_sp = next_frame.GetRegisterContext();
+ if (!next_reg_ctx_sp)
+ return;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ assert(!m_frames.empty() && "Cannot synthesize frames in an empty stack");
+ StackFrame &prev_frame = *m_frames.back().get();
+
+ // Find the functions prev_frame and next_frame are stopped in. The function
+ // objects are needed to search the lazy call graph for intervening frames.
+ Function *prev_func =
+ prev_frame.GetSymbolContext(eSymbolContextFunction).function;
+ if (!prev_func) {
+ LLDB_LOG(log, "SynthesizeTailCallFrames: can't find previous function");
+ return;
+ }
+ Function *next_func =
+ next_frame.GetSymbolContext(eSymbolContextFunction).function;
+ if (!next_func) {
+ LLDB_LOG(log, "SynthesizeTailCallFrames: can't find next function");
+ return;
+ }
+
+ // Try to find the unique sequence of (tail) calls which led from next_frame
+ // to prev_frame.
+ std::vector<Function *> path;
+ addr_t return_pc = next_reg_ctx_sp->GetPC();
+ Target &target = *target_sp.get();
+ ModuleList &images = next_frame.CalculateTarget()->GetImages();
+ FindInterveningFrames(*next_func, *prev_func, target, return_pc, path, images,
+ log);
+
+ // Push synthetic tail call frames.
+ for (Function *callee : llvm::reverse(path)) {
+ uint32_t frame_idx = m_frames.size();
+ uint32_t concrete_frame_idx = next_frame.GetConcreteFrameIndex();
+ addr_t cfa = LLDB_INVALID_ADDRESS;
+ bool cfa_is_valid = false;
+ addr_t pc =
+ callee->GetAddressRange().GetBaseAddress().GetLoadAddress(&target);
+ SymbolContext sc;
+ callee->CalculateSymbolContext(&sc);
+ auto synth_frame = std::make_shared<StackFrame>(
+ m_thread.shared_from_this(), frame_idx, concrete_frame_idx, cfa,
+ cfa_is_valid, pc, StackFrame::Kind::Artificial, &sc);
+ m_frames.push_back(synth_frame);
+ LLDB_LOG(log, "Pushed frame {0}", callee->GetDisplayName());
+ }
+
+ // If any frames were created, adjust next_frame's index.
+ if (!path.empty())
+ next_frame.SetFrameIndex(m_frames.size());
+}
+
+void StackFrameList::GetFramesUpTo(uint32_t end_idx) {
+ // Do not fetch frames for an invalid thread.
+ if (!m_thread.IsValid())
+ return;
+
+ // We've already gotten more frames than asked for, or we've already finished
+ // unwinding, return.
+ if (m_frames.size() > end_idx || GetAllFramesFetched())
+ return;
+
+ Unwind *unwinder = m_thread.GetUnwinder();
+
+ if (!m_show_inlined_frames) {
+ GetOnlyConcreteFramesUpTo(end_idx, unwinder);
+ return;
+ }
+
+#if defined(DEBUG_STACK_FRAMES)
+ StreamFile s(stdout, false);
+#endif
+ // If we are hiding some frames from the outside world, we need to add
+ // those onto the total count of frames to fetch. However, we don't need
+ // to do that if end_idx is 0 since in that case we always get the first
+ // concrete frame and all the inlined frames below it... And of course, if
+ // end_idx is UINT32_MAX that means get all, so just do that...
+
+ uint32_t inlined_depth = 0;
+ if (end_idx > 0 && end_idx != UINT32_MAX) {
+ inlined_depth = GetCurrentInlinedDepth();
+ if (inlined_depth != UINT32_MAX) {
+ if (end_idx > 0)
+ end_idx += inlined_depth;
+ }
+ }
+
+ StackFrameSP unwind_frame_sp;
+ do {
+ uint32_t idx = m_concrete_frames_fetched++;
+ lldb::addr_t pc = LLDB_INVALID_ADDRESS;
+ lldb::addr_t cfa = LLDB_INVALID_ADDRESS;
+ if (idx == 0) {
+ // We might have already created frame zero, only create it if we need
+ // to.
+ if (m_frames.empty()) {
+ RegisterContextSP reg_ctx_sp(m_thread.GetRegisterContext());
+
+ if (reg_ctx_sp) {
+ const bool success =
+ unwinder && unwinder->GetFrameInfoAtIndex(idx, cfa, pc);
+ // There shouldn't be any way not to get the frame info for frame
+ // 0. But if the unwinder can't make one, lets make one by hand
+ // with the SP as the CFA and see if that gets any further.
+ if (!success) {
+ cfa = reg_ctx_sp->GetSP();
+ pc = reg_ctx_sp->GetPC();
+ }
+
+ unwind_frame_sp = std::make_shared<StackFrame>(
+ m_thread.shared_from_this(), m_frames.size(), idx, reg_ctx_sp,
+ cfa, pc, nullptr);
+ m_frames.push_back(unwind_frame_sp);
+ }
+ } else {
+ unwind_frame_sp = m_frames.front();
+ cfa = unwind_frame_sp->m_id.GetCallFrameAddress();
+ }
+ } else {
+ const bool success =
+ unwinder && unwinder->GetFrameInfoAtIndex(idx, cfa, pc);
+ if (!success) {
+ // We've gotten to the end of the stack.
+ SetAllFramesFetched();
+ break;
+ }
+ const bool cfa_is_valid = true;
+ unwind_frame_sp = std::make_shared<StackFrame>(
+ m_thread.shared_from_this(), m_frames.size(), idx, cfa, cfa_is_valid,
+ pc, StackFrame::Kind::Regular, nullptr);
+
+ // Create synthetic tail call frames between the previous frame and the
+ // newly-found frame. The new frame's index may change after this call,
+ // although its concrete index will stay the same.
+ SynthesizeTailCallFrames(*unwind_frame_sp.get());
+
+ m_frames.push_back(unwind_frame_sp);
+ }
+
+ assert(unwind_frame_sp);
+ SymbolContext unwind_sc = unwind_frame_sp->GetSymbolContext(
+ eSymbolContextBlock | eSymbolContextFunction);
+ Block *unwind_block = unwind_sc.block;
+ if (unwind_block) {
+ Address curr_frame_address(unwind_frame_sp->GetFrameCodeAddress());
+ TargetSP target_sp = m_thread.CalculateTarget();
+ // Be sure to adjust the frame address to match the address that was
+ // used to lookup the symbol context above. If we are in the first
+ // concrete frame, then we lookup using the current address, else we
+ // decrement the address by one to get the correct location.
+ if (idx > 0) {
+ if (curr_frame_address.GetOffset() == 0) {
+ // If curr_frame_address points to the first address in a section
+ // then after adjustment it will point to an other section. In that
+ // case resolve the address again to the correct section plus
+ // offset form.
+ addr_t load_addr = curr_frame_address.GetOpcodeLoadAddress(
+ target_sp.get(), AddressClass::eCode);
+ curr_frame_address.SetOpcodeLoadAddress(
+ load_addr - 1, target_sp.get(), AddressClass::eCode);
+ } else {
+ curr_frame_address.Slide(-1);
+ }
+ }
+
+ SymbolContext next_frame_sc;
+ Address next_frame_address;
+
+ while (unwind_sc.GetParentOfInlinedScope(
+ curr_frame_address, next_frame_sc, next_frame_address)) {
+ next_frame_sc.line_entry.ApplyFileMappings(target_sp);
+ StackFrameSP frame_sp(
+ new StackFrame(m_thread.shared_from_this(), m_frames.size(), idx,
+ unwind_frame_sp->GetRegisterContextSP(), cfa,
+ next_frame_address, &next_frame_sc));
+
+ m_frames.push_back(frame_sp);
+ unwind_sc = next_frame_sc;
+ curr_frame_address = next_frame_address;
+ }
+ }
+ } while (m_frames.size() - 1 < end_idx);
+
+ // Don't try to merge till you've calculated all the frames in this stack.
+ if (GetAllFramesFetched() && m_prev_frames_sp) {
+ StackFrameList *prev_frames = m_prev_frames_sp.get();
+ StackFrameList *curr_frames = this;
+
+#if defined(DEBUG_STACK_FRAMES)
+ s.PutCString("\nprev_frames:\n");
+ prev_frames->Dump(&s);
+ s.PutCString("\ncurr_frames:\n");
+ curr_frames->Dump(&s);
+ s.EOL();
+#endif
+ size_t curr_frame_num, prev_frame_num;
+
+ for (curr_frame_num = curr_frames->m_frames.size(),
+ prev_frame_num = prev_frames->m_frames.size();
+ curr_frame_num > 0 && prev_frame_num > 0;
+ --curr_frame_num, --prev_frame_num) {
+ const size_t curr_frame_idx = curr_frame_num - 1;
+ const size_t prev_frame_idx = prev_frame_num - 1;
+ StackFrameSP curr_frame_sp(curr_frames->m_frames[curr_frame_idx]);
+ StackFrameSP prev_frame_sp(prev_frames->m_frames[prev_frame_idx]);
+
+#if defined(DEBUG_STACK_FRAMES)
+ s.Printf("\n\nCurr frame #%u ", curr_frame_idx);
+ if (curr_frame_sp)
+ curr_frame_sp->Dump(&s, true, false);
+ else
+ s.PutCString("NULL");
+ s.Printf("\nPrev frame #%u ", prev_frame_idx);
+ if (prev_frame_sp)
+ prev_frame_sp->Dump(&s, true, false);
+ else
+ s.PutCString("NULL");
+#endif
+
+ StackFrame *curr_frame = curr_frame_sp.get();
+ StackFrame *prev_frame = prev_frame_sp.get();
+
+ if (curr_frame == nullptr || prev_frame == nullptr)
+ break;
+
+ // Check the stack ID to make sure they are equal.
+ if (curr_frame->GetStackID() != prev_frame->GetStackID())
+ break;
+
+ prev_frame->UpdatePreviousFrameFromCurrentFrame(*curr_frame);
+ // Now copy the fixed up previous frame into the current frames so the
+ // pointer doesn't change.
+ m_frames[curr_frame_idx] = prev_frame_sp;
+
+#if defined(DEBUG_STACK_FRAMES)
+ s.Printf("\n Copying previous frame to current frame");
+#endif
+ }
+ // We are done with the old stack frame list, we can release it now.
+ m_prev_frames_sp.reset();
+ }
+
+#if defined(DEBUG_STACK_FRAMES)
+ s.PutCString("\n\nNew frames:\n");
+ Dump(&s);
+ s.EOL();
+#endif
+}
+
+uint32_t StackFrameList::GetNumFrames(bool can_create) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ if (can_create)
+ GetFramesUpTo(UINT32_MAX);
+
+ return GetVisibleStackFrameIndex(m_frames.size());
+}
+
+void StackFrameList::Dump(Stream *s) {
+ if (s == nullptr)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ const_iterator pos, begin = m_frames.begin(), end = m_frames.end();
+ for (pos = begin; pos != end; ++pos) {
+ StackFrame *frame = (*pos).get();
+ s->Printf("%p: ", static_cast<void *>(frame));
+ if (frame) {
+ frame->GetStackID().Dump(s);
+ frame->DumpUsingSettingsFormat(s);
+ } else
+ s->Printf("frame #%u", (uint32_t)std::distance(begin, pos));
+ s->EOL();
+ }
+ s->EOL();
+}
+
+StackFrameSP StackFrameList::GetFrameAtIndex(uint32_t idx) {
+ StackFrameSP frame_sp;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ uint32_t original_idx = idx;
+
+ uint32_t inlined_depth = GetCurrentInlinedDepth();
+ if (inlined_depth != UINT32_MAX)
+ idx += inlined_depth;
+
+ if (idx < m_frames.size())
+ frame_sp = m_frames[idx];
+
+ if (frame_sp)
+ return frame_sp;
+
+ // GetFramesUpTo will fill m_frames with as many frames as you asked for, if
+ // there are that many. If there weren't then you asked for too many frames.
+ GetFramesUpTo(idx);
+ if (idx < m_frames.size()) {
+ if (m_show_inlined_frames) {
+ // When inline frames are enabled we actually create all the frames in
+ // GetFramesUpTo.
+ frame_sp = m_frames[idx];
+ } else {
+ Unwind *unwinder = m_thread.GetUnwinder();
+ if (unwinder) {
+ addr_t pc, cfa;
+ if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc)) {
+ const bool cfa_is_valid = true;
+ frame_sp = std::make_shared<StackFrame>(
+ m_thread.shared_from_this(), idx, idx, cfa, cfa_is_valid, pc,
+ StackFrame::Kind::Regular, nullptr);
+
+ Function *function =
+ frame_sp->GetSymbolContext(eSymbolContextFunction).function;
+ if (function) {
+ // When we aren't showing inline functions we always use the top
+ // most function block as the scope.
+ frame_sp->SetSymbolContextScope(&function->GetBlock(false));
+ } else {
+ // Set the symbol scope from the symbol regardless if it is nullptr
+ // or valid.
+ frame_sp->SetSymbolContextScope(
+ frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol);
+ }
+ SetFrameAtIndex(idx, frame_sp);
+ }
+ }
+ }
+ } else if (original_idx == 0) {
+ // There should ALWAYS be a frame at index 0. If something went wrong with
+ // the CurrentInlinedDepth such that there weren't as many frames as we
+ // thought taking that into account, then reset the current inlined depth
+ // and return the real zeroth frame.
+ if (m_frames.empty()) {
+ // Why do we have a thread with zero frames, that should not ever
+ // happen...
+ assert(!m_thread.IsValid() && "A valid thread has no frames.");
+ } else {
+ ResetCurrentInlinedDepth();
+ frame_sp = m_frames[original_idx];
+ }
+ }
+
+ return frame_sp;
+}
+
+StackFrameSP
+StackFrameList::GetFrameWithConcreteFrameIndex(uint32_t unwind_idx) {
+ // First try assuming the unwind index is the same as the frame index. The
+ // unwind index is always greater than or equal to the frame index, so it is
+ // a good place to start. If we have inlined frames we might have 5 concrete
+ // frames (frame unwind indexes go from 0-4), but we might have 15 frames
+ // after we make all the inlined frames. Most of the time the unwind frame
+ // index (or the concrete frame index) is the same as the frame index.
+ uint32_t frame_idx = unwind_idx;
+ StackFrameSP frame_sp(GetFrameAtIndex(frame_idx));
+ while (frame_sp) {
+ if (frame_sp->GetFrameIndex() == unwind_idx)
+ break;
+ frame_sp = GetFrameAtIndex(++frame_idx);
+ }
+ return frame_sp;
+}
+
+static bool CompareStackID(const StackFrameSP &stack_sp,
+ const StackID &stack_id) {
+ return stack_sp->GetStackID() < stack_id;
+}
+
+StackFrameSP StackFrameList::GetFrameWithStackID(const StackID &stack_id) {
+ StackFrameSP frame_sp;
+
+ if (stack_id.IsValid()) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ uint32_t frame_idx = 0;
+ // Do a binary search in case the stack frame is already in our cache
+ collection::const_iterator begin = m_frames.begin();
+ collection::const_iterator end = m_frames.end();
+ if (begin != end) {
+ collection::const_iterator pos =
+ std::lower_bound(begin, end, stack_id, CompareStackID);
+ if (pos != end) {
+ if ((*pos)->GetStackID() == stack_id)
+ return *pos;
+ }
+ }
+ do {
+ frame_sp = GetFrameAtIndex(frame_idx);
+ if (frame_sp && frame_sp->GetStackID() == stack_id)
+ break;
+ frame_idx++;
+ } while (frame_sp);
+ }
+ return frame_sp;
+}
+
+bool StackFrameList::SetFrameAtIndex(uint32_t idx, StackFrameSP &frame_sp) {
+ if (idx >= m_frames.size())
+ m_frames.resize(idx + 1);
+ // Make sure allocation succeeded by checking bounds again
+ if (idx < m_frames.size()) {
+ m_frames[idx] = frame_sp;
+ return true;
+ }
+ return false; // resize failed, out of memory?
+}
+
+uint32_t StackFrameList::GetSelectedFrameIndex() const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return m_selected_frame_idx;
+}
+
+uint32_t StackFrameList::SetSelectedFrame(lldb_private::StackFrame *frame) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ const_iterator pos;
+ const_iterator begin = m_frames.begin();
+ const_iterator end = m_frames.end();
+ m_selected_frame_idx = 0;
+ for (pos = begin; pos != end; ++pos) {
+ if (pos->get() == frame) {
+ m_selected_frame_idx = std::distance(begin, pos);
+ uint32_t inlined_depth = GetCurrentInlinedDepth();
+ if (inlined_depth != UINT32_MAX)
+ m_selected_frame_idx -= inlined_depth;
+ break;
+ }
+ }
+ SetDefaultFileAndLineToSelectedFrame();
+ return m_selected_frame_idx;
+}
+
+bool StackFrameList::SetSelectedFrameByIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ StackFrameSP frame_sp(GetFrameAtIndex(idx));
+ if (frame_sp) {
+ SetSelectedFrame(frame_sp.get());
+ return true;
+ } else
+ return false;
+}
+
+void StackFrameList::SetDefaultFileAndLineToSelectedFrame() {
+ if (m_thread.GetID() ==
+ m_thread.GetProcess()->GetThreadList().GetSelectedThread()->GetID()) {
+ StackFrameSP frame_sp(GetFrameAtIndex(GetSelectedFrameIndex()));
+ if (frame_sp) {
+ SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextLineEntry);
+ if (sc.line_entry.file)
+ m_thread.CalculateTarget()->GetSourceManager().SetDefaultFileAndLine(
+ sc.line_entry.file, sc.line_entry.line);
+ }
+ }
+}
+
+// The thread has been run, reset the number stack frames to zero so we can
+// determine how many frames we have lazily.
+void StackFrameList::Clear() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_frames.clear();
+ m_concrete_frames_fetched = 0;
+}
+
+void StackFrameList::Merge(std::unique_ptr<StackFrameList> &curr_up,
+ lldb::StackFrameListSP &prev_sp) {
+ std::unique_lock<std::recursive_mutex> current_lock, previous_lock;
+ if (curr_up)
+ current_lock = std::unique_lock<std::recursive_mutex>(curr_up->m_mutex);
+ if (prev_sp)
+ previous_lock = std::unique_lock<std::recursive_mutex>(prev_sp->m_mutex);
+
+#if defined(DEBUG_STACK_FRAMES)
+ StreamFile s(stdout, false);
+ s.PutCString("\n\nStackFrameList::Merge():\nPrev:\n");
+ if (prev_sp)
+ prev_sp->Dump(&s);
+ else
+ s.PutCString("NULL");
+ s.PutCString("\nCurr:\n");
+ if (curr_up)
+ curr_up->Dump(&s);
+ else
+ s.PutCString("NULL");
+ s.EOL();
+#endif
+
+ if (!curr_up || curr_up->GetNumFrames(false) == 0) {
+#if defined(DEBUG_STACK_FRAMES)
+ s.PutCString("No current frames, leave previous frames alone...\n");
+#endif
+ curr_up.release();
+ return;
+ }
+
+ if (!prev_sp || prev_sp->GetNumFrames(false) == 0) {
+#if defined(DEBUG_STACK_FRAMES)
+ s.PutCString("No previous frames, so use current frames...\n");
+#endif
+ // We either don't have any previous frames, or since we have more than one
+ // current frames it means we have all the frames and can safely replace
+ // our previous frames.
+ prev_sp.reset(curr_up.release());
+ return;
+ }
+
+ const uint32_t num_curr_frames = curr_up->GetNumFrames(false);
+
+ if (num_curr_frames > 1) {
+#if defined(DEBUG_STACK_FRAMES)
+ s.PutCString(
+ "We have more than one current frame, so use current frames...\n");
+#endif
+ // We have more than one current frames it means we have all the frames and
+ // can safely replace our previous frames.
+ prev_sp.reset(curr_up.release());
+
+#if defined(DEBUG_STACK_FRAMES)
+ s.PutCString("\nMerged:\n");
+ prev_sp->Dump(&s);
+#endif
+ return;
+ }
+
+ StackFrameSP prev_frame_zero_sp(prev_sp->GetFrameAtIndex(0));
+ StackFrameSP curr_frame_zero_sp(curr_up->GetFrameAtIndex(0));
+ StackID curr_stack_id(curr_frame_zero_sp->GetStackID());
+ StackID prev_stack_id(prev_frame_zero_sp->GetStackID());
+
+#if defined(DEBUG_STACK_FRAMES)
+ const uint32_t num_prev_frames = prev_sp->GetNumFrames(false);
+ s.Printf("\n%u previous frames with one current frame\n", num_prev_frames);
+#endif
+
+ // We have only a single current frame
+ // Our previous stack frames only had a single frame as well...
+ if (curr_stack_id == prev_stack_id) {
+#if defined(DEBUG_STACK_FRAMES)
+ s.Printf("\nPrevious frame #0 is same as current frame #0, merge the "
+ "cached data\n");
+#endif
+
+ curr_frame_zero_sp->UpdateCurrentFrameFromPreviousFrame(
+ *prev_frame_zero_sp);
+ // prev_frame_zero_sp->UpdatePreviousFrameFromCurrentFrame
+ // (*curr_frame_zero_sp);
+ // prev_sp->SetFrameAtIndex (0, prev_frame_zero_sp);
+ } else if (curr_stack_id < prev_stack_id) {
+#if defined(DEBUG_STACK_FRAMES)
+ s.Printf("\nCurrent frame #0 has a stack ID that is less than the previous "
+ "frame #0, insert current frame zero in front of previous\n");
+#endif
+ prev_sp->m_frames.insert(prev_sp->m_frames.begin(), curr_frame_zero_sp);
+ }
+
+ curr_up.release();
+
+#if defined(DEBUG_STACK_FRAMES)
+ s.PutCString("\nMerged:\n");
+ prev_sp->Dump(&s);
+#endif
+}
+
+lldb::StackFrameSP
+StackFrameList::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) {
+ const_iterator pos;
+ const_iterator begin = m_frames.begin();
+ const_iterator end = m_frames.end();
+ lldb::StackFrameSP ret_sp;
+
+ for (pos = begin; pos != end; ++pos) {
+ if (pos->get() == stack_frame_ptr) {
+ ret_sp = (*pos);
+ break;
+ }
+ }
+ return ret_sp;
+}
+
+size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
+ uint32_t num_frames, bool show_frame_info,
+ uint32_t num_frames_with_source,
+ bool show_unique,
+ const char *selected_frame_marker) {
+ size_t num_frames_displayed = 0;
+
+ if (num_frames == 0)
+ return 0;
+
+ StackFrameSP frame_sp;
+ uint32_t frame_idx = 0;
+ uint32_t last_frame;
+
+ // Don't let the last frame wrap around...
+ if (num_frames == UINT32_MAX)
+ last_frame = UINT32_MAX;
+ else
+ last_frame = first_frame + num_frames;
+
+ StackFrameSP selected_frame_sp = m_thread.GetSelectedFrame();
+ const char *unselected_marker = nullptr;
+ std::string buffer;
+ if (selected_frame_marker) {
+ size_t len = strlen(selected_frame_marker);
+ buffer.insert(buffer.begin(), len, ' ');
+ unselected_marker = buffer.c_str();
+ }
+ const char *marker = nullptr;
+
+ for (frame_idx = first_frame; frame_idx < last_frame; ++frame_idx) {
+ frame_sp = GetFrameAtIndex(frame_idx);
+ if (!frame_sp)
+ break;
+
+ if (selected_frame_marker != nullptr) {
+ if (frame_sp == selected_frame_sp)
+ marker = selected_frame_marker;
+ else
+ marker = unselected_marker;
+ }
+
+ if (!frame_sp->GetStatus(strm, show_frame_info,
+ num_frames_with_source > (first_frame - frame_idx),
+ show_unique, marker))
+ break;
+ ++num_frames_displayed;
+ }
+
+ strm.IndentLess();
+ return num_frames_displayed;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/StackFrameRecognizer.cpp b/contrib/llvm-project/lldb/source/Target/StackFrameRecognizer.cpp
new file mode 100644
index 000000000000..567d694bf093
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/StackFrameRecognizer.cpp
@@ -0,0 +1,192 @@
+//===-- StackFrameRecognizer.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <vector>
+#include "lldb/Core/Module.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StackFrameRecognizer.h"
+#include "lldb/Utility/RegularExpression.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class ScriptedRecognizedStackFrame : public RecognizedStackFrame {
+public:
+ ScriptedRecognizedStackFrame(ValueObjectListSP args) {
+ m_arguments = args;
+ }
+};
+
+ScriptedStackFrameRecognizer::ScriptedStackFrameRecognizer(
+ ScriptInterpreter *interpreter, const char *pclass)
+ : m_interpreter(interpreter), m_python_class(pclass) {
+ m_python_object_sp =
+ m_interpreter->CreateFrameRecognizer(m_python_class.c_str());
+}
+
+RecognizedStackFrameSP
+ScriptedStackFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame) {
+ if (!m_python_object_sp || !m_interpreter)
+ return RecognizedStackFrameSP();
+
+ ValueObjectListSP args =
+ m_interpreter->GetRecognizedArguments(m_python_object_sp, frame);
+ auto args_synthesized = ValueObjectListSP(new ValueObjectList());
+ for (const auto o : args->GetObjects()) {
+ args_synthesized->Append(ValueObjectRecognizerSynthesizedValue::Create(
+ *o, eValueTypeVariableArgument));
+ }
+
+ return RecognizedStackFrameSP(
+ new ScriptedRecognizedStackFrame(args_synthesized));
+}
+
+class StackFrameRecognizerManagerImpl {
+public:
+ void AddRecognizer(StackFrameRecognizerSP recognizer,
+ ConstString module, ConstString symbol,
+ bool first_instruction_only) {
+ m_recognizers.push_front({(uint32_t)m_recognizers.size(), false, recognizer, false, module, RegularExpressionSP(),
+ symbol, RegularExpressionSP(),
+ first_instruction_only});
+ }
+
+ void AddRecognizer(StackFrameRecognizerSP recognizer,
+ RegularExpressionSP module, RegularExpressionSP symbol,
+ bool first_instruction_only) {
+ m_recognizers.push_front({(uint32_t)m_recognizers.size(), false, recognizer, true, ConstString(), module,
+ ConstString(), symbol, first_instruction_only});
+ }
+
+ void ForEach(
+ std::function<void(uint32_t recognized_id, std::string recognizer_name, std::string module,
+ std::string symbol, bool regexp)> const &callback) {
+ for (auto entry : m_recognizers) {
+ if (entry.is_regexp) {
+ callback(entry.recognizer_id, entry.recognizer->GetName(), entry.module_regexp->GetText(),
+ entry.symbol_regexp->GetText(), true);
+ } else {
+ callback(entry.recognizer_id, entry.recognizer->GetName(), entry.module.GetCString(),
+ entry.symbol.GetCString(), false);
+ }
+ }
+ }
+
+ bool RemoveRecognizerWithID(uint32_t recognizer_id) {
+ if (recognizer_id >= m_recognizers.size()) return false;
+ if (m_recognizers[recognizer_id].deleted) return false;
+ m_recognizers[recognizer_id].deleted = true;
+ return true;
+ }
+
+ void RemoveAllRecognizers() {
+ m_recognizers.clear();
+ }
+
+ StackFrameRecognizerSP GetRecognizerForFrame(StackFrameSP frame) {
+ const SymbolContext &symctx =
+ frame->GetSymbolContext(eSymbolContextModule | eSymbolContextFunction);
+ ConstString function_name = symctx.GetFunctionName();
+ ModuleSP module_sp = symctx.module_sp;
+ if (!module_sp) return StackFrameRecognizerSP();
+ ConstString module_name = module_sp->GetFileSpec().GetFilename();
+ Symbol *symbol = symctx.symbol;
+ if (!symbol) return StackFrameRecognizerSP();
+ Address start_addr = symbol->GetAddress();
+ Address current_addr = frame->GetFrameCodeAddress();
+
+ for (auto entry : m_recognizers) {
+ if (entry.deleted) continue;
+ if (entry.module)
+ if (entry.module != module_name) continue;
+
+ if (entry.module_regexp)
+ if (!entry.module_regexp->Execute(module_name.GetStringRef())) continue;
+
+ if (entry.symbol)
+ if (entry.symbol != function_name) continue;
+
+ if (entry.symbol_regexp)
+ if (!entry.symbol_regexp->Execute(function_name.GetStringRef()))
+ continue;
+
+ if (entry.first_instruction_only)
+ if (start_addr != current_addr) continue;
+
+ return entry.recognizer;
+ }
+ return StackFrameRecognizerSP();
+ }
+
+ RecognizedStackFrameSP RecognizeFrame(StackFrameSP frame) {
+ auto recognizer = GetRecognizerForFrame(frame);
+ if (!recognizer) return RecognizedStackFrameSP();
+ return recognizer->RecognizeFrame(frame);
+ }
+
+ private:
+ struct RegisteredEntry {
+ uint32_t recognizer_id;
+ bool deleted;
+ StackFrameRecognizerSP recognizer;
+ bool is_regexp;
+ ConstString module;
+ RegularExpressionSP module_regexp;
+ ConstString symbol;
+ RegularExpressionSP symbol_regexp;
+ bool first_instruction_only;
+ };
+
+ std::deque<RegisteredEntry> m_recognizers;
+};
+
+StackFrameRecognizerManagerImpl &GetStackFrameRecognizerManagerImpl() {
+ static StackFrameRecognizerManagerImpl instance =
+ StackFrameRecognizerManagerImpl();
+ return instance;
+}
+
+void StackFrameRecognizerManager::AddRecognizer(
+ StackFrameRecognizerSP recognizer, ConstString module,
+ ConstString symbol, bool first_instruction_only) {
+ GetStackFrameRecognizerManagerImpl().AddRecognizer(recognizer, module, symbol,
+ first_instruction_only);
+}
+
+void StackFrameRecognizerManager::AddRecognizer(
+ StackFrameRecognizerSP recognizer, RegularExpressionSP module,
+ RegularExpressionSP symbol, bool first_instruction_only) {
+ GetStackFrameRecognizerManagerImpl().AddRecognizer(recognizer, module, symbol,
+ first_instruction_only);
+}
+
+void StackFrameRecognizerManager::ForEach(
+ std::function<void(uint32_t recognized_id, std::string recognizer_name, std::string module,
+ std::string symbol, bool regexp)> const &callback) {
+ GetStackFrameRecognizerManagerImpl().ForEach(callback);
+}
+
+void StackFrameRecognizerManager::RemoveAllRecognizers() {
+ GetStackFrameRecognizerManagerImpl().RemoveAllRecognizers();
+}
+
+bool StackFrameRecognizerManager::RemoveRecognizerWithID(uint32_t recognizer_id) {
+ return GetStackFrameRecognizerManagerImpl().RemoveRecognizerWithID(recognizer_id);
+}
+
+RecognizedStackFrameSP StackFrameRecognizerManager::RecognizeFrame(
+ StackFrameSP frame) {
+ return GetStackFrameRecognizerManagerImpl().RecognizeFrame(frame);
+}
+
+StackFrameRecognizerSP StackFrameRecognizerManager::GetRecognizerForFrame(
+ lldb::StackFrameSP frame) {
+ return GetStackFrameRecognizerManagerImpl().GetRecognizerForFrame(frame);
+}
diff --git a/contrib/llvm-project/lldb/source/Target/StackID.cpp b/contrib/llvm-project/lldb/source/Target/StackID.cpp
new file mode 100644
index 000000000000..a8f6b787f4b4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/StackID.cpp
@@ -0,0 +1,97 @@
+//===-- StackID.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/StackID.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+
+void StackID::Dump(Stream *s) {
+ s->Printf("StackID (pc = 0x%16.16" PRIx64 ", cfa = 0x%16.16" PRIx64
+ ", symbol_scope = %p",
+ m_pc, m_cfa, static_cast<void *>(m_symbol_scope));
+ if (m_symbol_scope) {
+ SymbolContext sc;
+
+ m_symbol_scope->CalculateSymbolContext(&sc);
+ if (sc.block)
+ s->Printf(" (Block {0x%8.8" PRIx64 "})", sc.block->GetID());
+ else if (sc.symbol)
+ s->Printf(" (Symbol{0x%8.8x})", sc.symbol->GetID());
+ }
+ s->PutCString(") ");
+}
+
+bool lldb_private::operator==(const StackID &lhs, const StackID &rhs) {
+ if (lhs.GetCallFrameAddress() != rhs.GetCallFrameAddress())
+ return false;
+
+ SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope();
+ SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope();
+
+ // Only compare the PC values if both symbol context scopes are nullptr
+ if (lhs_scope == nullptr && rhs_scope == nullptr)
+ return lhs.GetPC() == rhs.GetPC();
+
+ return lhs_scope == rhs_scope;
+}
+
+bool lldb_private::operator!=(const StackID &lhs, const StackID &rhs) {
+ if (lhs.GetCallFrameAddress() != rhs.GetCallFrameAddress())
+ return true;
+
+ SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope();
+ SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope();
+
+ if (lhs_scope == nullptr && rhs_scope == nullptr)
+ return lhs.GetPC() != rhs.GetPC();
+
+ return lhs_scope != rhs_scope;
+}
+
+bool lldb_private::operator<(const StackID &lhs, const StackID &rhs) {
+ const lldb::addr_t lhs_cfa = lhs.GetCallFrameAddress();
+ const lldb::addr_t rhs_cfa = rhs.GetCallFrameAddress();
+
+ // FIXME: We are assuming that the stacks grow downward in memory. That's not
+ // necessary, but true on
+ // all the machines we care about at present. If this changes, we'll have to
+ // deal with that. The ABI is the agent who knows this ordering, but the
+ // StackID has no access to the ABI. The most straightforward way to handle
+ // this is to add a "m_grows_downward" bool to the StackID, and set it in the
+ // constructor. But I'm not going to waste a bool per StackID on this till we
+ // need it.
+
+ if (lhs_cfa != rhs_cfa)
+ return lhs_cfa < rhs_cfa;
+
+ SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope();
+ SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope();
+
+ if (lhs_scope != nullptr && rhs_scope != nullptr) {
+ // Same exact scope, lhs is not less than (younger than rhs)
+ if (lhs_scope == rhs_scope)
+ return false;
+
+ SymbolContext lhs_sc;
+ SymbolContext rhs_sc;
+ lhs_scope->CalculateSymbolContext(&lhs_sc);
+ rhs_scope->CalculateSymbolContext(&rhs_sc);
+
+ // Items with the same function can only be compared
+ if (lhs_sc.function == rhs_sc.function && lhs_sc.function != nullptr &&
+ lhs_sc.block != nullptr && rhs_sc.function != nullptr &&
+ rhs_sc.block != nullptr) {
+ return rhs_sc.block->Contains(lhs_sc.block);
+ }
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/StopInfo.cpp b/contrib/llvm-project/lldb/source/Target/StopInfo.cpp
new file mode 100644
index 000000000000..6db0c2b037e9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/StopInfo.cpp
@@ -0,0 +1,1205 @@
+//===-- StopInfo.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <string>
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+StopInfo::StopInfo(Thread &thread, uint64_t value)
+ : m_thread_wp(thread.shared_from_this()),
+ m_stop_id(thread.GetProcess()->GetStopID()),
+ m_resume_id(thread.GetProcess()->GetResumeID()), m_value(value),
+ m_description(), m_override_should_notify(eLazyBoolCalculate),
+ m_override_should_stop(eLazyBoolCalculate), m_extended_info() {}
+
+bool StopInfo::IsValid() const {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp)
+ return thread_sp->GetProcess()->GetStopID() == m_stop_id;
+ return false;
+}
+
+void StopInfo::MakeStopInfoValid() {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ m_stop_id = thread_sp->GetProcess()->GetStopID();
+ m_resume_id = thread_sp->GetProcess()->GetResumeID();
+ }
+}
+
+bool StopInfo::HasTargetRunSinceMe() {
+ ThreadSP thread_sp(m_thread_wp.lock());
+
+ if (thread_sp) {
+ lldb::StateType ret_type = thread_sp->GetProcess()->GetPrivateState();
+ if (ret_type == eStateRunning) {
+ return true;
+ } else if (ret_type == eStateStopped) {
+ // This is a little tricky. We want to count "run and stopped again
+ // before you could ask this question as a "TRUE" answer to
+ // HasTargetRunSinceMe. But we don't want to include any running of the
+ // target done for expressions. So we track both resumes, and resumes
+ // caused by expressions, and check if there are any resumes
+ // NOT caused
+ // by expressions.
+
+ uint32_t curr_resume_id = thread_sp->GetProcess()->GetResumeID();
+ uint32_t last_user_expression_id =
+ thread_sp->GetProcess()->GetLastUserExpressionResumeID();
+ if (curr_resume_id == m_resume_id) {
+ return false;
+ } else if (curr_resume_id > last_user_expression_id) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// StopInfoBreakpoint
+
+namespace lldb_private {
+class StopInfoBreakpoint : public StopInfo {
+public:
+ StopInfoBreakpoint(Thread &thread, break_id_t break_id)
+ : StopInfo(thread, break_id), m_should_stop(false),
+ m_should_stop_is_valid(false), m_should_perform_action(true),
+ m_address(LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID),
+ m_was_one_shot(false) {
+ StoreBPInfo();
+ }
+
+ StopInfoBreakpoint(Thread &thread, break_id_t break_id, bool should_stop)
+ : StopInfo(thread, break_id), m_should_stop(should_stop),
+ m_should_stop_is_valid(true), m_should_perform_action(true),
+ m_address(LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID),
+ m_was_one_shot(false) {
+ StoreBPInfo();
+ }
+
+ ~StopInfoBreakpoint() override = default;
+
+ void StoreBPInfo() {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ BreakpointSiteSP bp_site_sp(
+ thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
+ if (bp_site_sp) {
+ if (bp_site_sp->GetNumberOfOwners() == 1) {
+ BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(0);
+ if (bp_loc_sp) {
+ m_break_id = bp_loc_sp->GetBreakpoint().GetID();
+ m_was_one_shot = bp_loc_sp->GetBreakpoint().IsOneShot();
+ }
+ }
+ m_address = bp_site_sp->GetLoadAddress();
+ }
+ }
+ }
+
+ bool IsValidForOperatingSystemThread(Thread &thread) override {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ BreakpointSiteSP bp_site_sp(
+ process_sp->GetBreakpointSiteList().FindByID(m_value));
+ if (bp_site_sp)
+ return bp_site_sp->ValidForThisThread(&thread);
+ }
+ return false;
+ }
+
+ StopReason GetStopReason() const override { return eStopReasonBreakpoint; }
+
+ bool ShouldStopSynchronous(Event *event_ptr) override {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ if (!m_should_stop_is_valid) {
+ // Only check once if we should stop at a breakpoint
+ BreakpointSiteSP bp_site_sp(
+ thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
+ if (bp_site_sp) {
+ 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);
+ } else {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf(
+ "Process::%s could not find breakpoint site id: %" PRId64 "...",
+ __FUNCTION__, m_value);
+
+ m_should_stop = true;
+ }
+ m_should_stop_is_valid = true;
+ }
+ return m_should_stop;
+ }
+ return false;
+ }
+
+ bool DoShouldNotify(Event *event_ptr) override {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ BreakpointSiteSP bp_site_sp(
+ thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
+ if (bp_site_sp) {
+ bool all_internal = true;
+
+ for (uint32_t i = 0; i < bp_site_sp->GetNumberOfOwners(); i++) {
+ if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal()) {
+ all_internal = false;
+ break;
+ }
+ }
+ return !all_internal;
+ }
+ }
+ return true;
+ }
+
+ const char *GetDescription() override {
+ if (m_description.empty()) {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ BreakpointSiteSP bp_site_sp(
+ thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
+ if (bp_site_sp) {
+ StreamString strm;
+ // If we have just hit an internal breakpoint, and it has a kind
+ // description, print that instead of the full breakpoint printing:
+ if (bp_site_sp->IsInternal()) {
+ size_t num_owners = bp_site_sp->GetNumberOfOwners();
+ for (size_t idx = 0; idx < num_owners; idx++) {
+ const char *kind = bp_site_sp->GetOwnerAtIndex(idx)
+ ->GetBreakpoint()
+ .GetBreakpointKind();
+ if (kind != nullptr) {
+ m_description.assign(kind);
+ return kind;
+ }
+ }
+ }
+
+ strm.Printf("breakpoint ");
+ bp_site_sp->GetDescription(&strm, eDescriptionLevelBrief);
+ m_description = strm.GetString();
+ } else {
+ StreamString strm;
+ if (m_break_id != LLDB_INVALID_BREAK_ID) {
+ BreakpointSP break_sp =
+ thread_sp->GetProcess()->GetTarget().GetBreakpointByID(
+ m_break_id);
+ if (break_sp) {
+ if (break_sp->IsInternal()) {
+ const char *kind = break_sp->GetBreakpointKind();
+ if (kind)
+ strm.Printf("internal %s breakpoint(%d).", kind, m_break_id);
+ else
+ strm.Printf("internal breakpoint(%d).", m_break_id);
+ } else {
+ strm.Printf("breakpoint %d.", m_break_id);
+ }
+ } else {
+ if (m_was_one_shot)
+ strm.Printf("one-shot breakpoint %d", m_break_id);
+ else
+ strm.Printf("breakpoint %d which has been deleted.",
+ m_break_id);
+ }
+ } else if (m_address == LLDB_INVALID_ADDRESS)
+ strm.Printf("breakpoint site %" PRIi64
+ " which has been deleted - unknown address",
+ m_value);
+ else
+ strm.Printf("breakpoint site %" PRIi64
+ " which has been deleted - was at 0x%" PRIx64,
+ m_value, m_address);
+
+ m_description = strm.GetString();
+ }
+ }
+ }
+ return m_description.c_str();
+ }
+
+protected:
+ bool ShouldStop(Event *event_ptr) override {
+ // This just reports the work done by PerformAction or the synchronous
+ // stop. It should only ever get called after they have had a chance to
+ // run.
+ assert(m_should_stop_is_valid);
+ return m_should_stop;
+ }
+
+ void PerformAction(Event *event_ptr) override {
+ if (!m_should_perform_action)
+ return;
+ m_should_perform_action = false;
+ bool internal_breakpoint = true;
+
+ ThreadSP thread_sp(m_thread_wp.lock());
+
+ if (thread_sp) {
+ Log *log = lldb_private::GetLogIfAnyCategoriesSet(
+ LIBLLDB_LOG_BREAKPOINTS | LIBLLDB_LOG_STEP);
+
+ if (!thread_sp->IsValid()) {
+ // This shouldn't ever happen, but just in case, don't do more harm.
+ if (log) {
+ log->Printf("PerformAction got called with an invalid thread.");
+ }
+ m_should_stop = true;
+ m_should_stop_is_valid = true;
+ return;
+ }
+
+ BreakpointSiteSP bp_site_sp(
+ thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
+ std::unordered_set<break_id_t> precondition_breakpoints;
+
+ if (bp_site_sp) {
+ // Let's copy the owners list out of the site and store them in a 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_owners = bp_site_sp->CopyOwnersList(site_locations);
+
+ if (num_owners == 0) {
+ m_should_stop = true;
+ } else {
+ // We go through each location, and test first its precondition -
+ // this overrides everything. Note, we only do this once per
+ // breakpoint - not once per location... Then check the condition.
+ // If the condition says to stop, then we run the callback for that
+ // location. If that callback says to stop as well, then we set
+ // m_should_stop to true; we are going to stop. But we still want to
+ // give all the breakpoints whose conditions say we are going to stop
+ // a chance to run their callbacks. Of course if any callback
+ // restarts the target by putting "continue" in the callback, then
+ // we're going to restart, without running the rest of the callbacks.
+ // And in this case we will end up not stopping even if another
+ // location said we should stop. But that's better than not running
+ // all the callbacks.
+
+ m_should_stop = false;
+
+ // We don't select threads as we go through them testing breakpoint
+ // conditions and running commands. So we need to set the thread for
+ // expression evaluation here:
+ ThreadList::ExpressionExecutionThreadPusher thread_pusher(thread_sp);
+
+ ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process->GetModIDRef().IsLastResumeForUserExpression()) {
+ // If we are in the middle of evaluating an expression, don't run
+ // asynchronous breakpoint commands or expressions. That could
+ // lead to infinite recursion if the command or condition re-calls
+ // the function with this breakpoint.
+ // TODO: We can keep a list of the breakpoints we've seen while
+ // running expressions in the nested
+ // PerformAction calls that can arise when the action runs a
+ // function that hits another breakpoint, and only stop running
+ // commands when we see the same breakpoint hit a second time.
+
+ m_should_stop_is_valid = true;
+
+ // It is possible that the user has a breakpoint at the same site
+ // as the completed plan had (e.g. user has a breakpoint
+ // on a module entry point, and `ThreadPlanCallFunction` ends
+ // also there). We can't find an internal breakpoint in the loop
+ // later because it was already removed on the plan completion.
+ // So check if the plan was completed, and stop if so.
+ if (thread_sp->CompletedPlanOverridesBreakpoint()) {
+ m_should_stop = true;
+ thread_sp->ResetStopInfo();
+ return;
+ }
+
+ if (log)
+ log->Printf("StopInfoBreakpoint::PerformAction - Hit a "
+ "breakpoint while running an expression,"
+ " not running commands to avoid recursion.");
+ bool ignoring_breakpoints =
+ process->GetIgnoreBreakpointsInExpressions();
+ if (ignoring_breakpoints) {
+ m_should_stop = false;
+ // Internal breakpoints will always stop.
+ for (size_t j = 0; j < num_owners; j++) {
+ lldb::BreakpointLocationSP bp_loc_sp =
+ bp_site_sp->GetOwnerAtIndex(j);
+ if (bp_loc_sp->GetBreakpoint().IsInternal()) {
+ m_should_stop = true;
+ break;
+ }
+ }
+ } else {
+ m_should_stop = true;
+ }
+ if (log)
+ log->Printf("StopInfoBreakpoint::PerformAction - in expression, "
+ "continuing: %s.",
+ m_should_stop ? "true" : "false");
+ process->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf(
+ "Warning: hit breakpoint while running function, skipping "
+ "commands and conditions to prevent recursion.\n");
+ return;
+ }
+
+ StoppointCallbackContext context(event_ptr, exe_ctx, false);
+
+ // For safety's sake let's also grab an extra reference to the
+ // breakpoint owners of the locations we're going to examine, since
+ // the locations are going to have to get back to their breakpoints,
+ // and the locations don't keep their owners alive. I'm just
+ // sticking the BreakpointSP's in a vector since I'm only using it to
+ // locally increment their retain counts.
+
+ std::vector<lldb::BreakpointSP> location_owners;
+
+ for (size_t j = 0; j < num_owners; j++) {
+ BreakpointLocationSP loc(site_locations.GetByIndex(j));
+ location_owners.push_back(loc->GetBreakpoint().shared_from_this());
+ }
+
+ for (size_t j = 0; j < num_owners; j++) {
+ lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j);
+ StreamString loc_desc;
+ if (log) {
+ bp_loc_sp->GetDescription(&loc_desc, eDescriptionLevelBrief);
+ }
+ // If another action disabled this breakpoint or its location, then
+ // don't run the actions.
+ if (!bp_loc_sp->IsEnabled() ||
+ !bp_loc_sp->GetBreakpoint().IsEnabled())
+ continue;
+
+ // The breakpoint site may have many locations associated with it,
+ // not all of them valid for this thread. Skip the ones that
+ // aren't:
+ if (!bp_loc_sp->ValidForThisThread(thread_sp.get())) {
+ if (log) {
+ log->Printf("Breakpoint %s hit on thread 0x%llx but it was not "
+ "for this thread, continuing.",
+ loc_desc.GetData(), static_cast<unsigned long long>(
+ thread_sp->GetID()));
+ }
+ continue;
+ }
+
+ internal_breakpoint = bp_loc_sp->GetBreakpoint().IsInternal();
+
+ // First run the precondition, but since the precondition is per
+ // breakpoint, only run it once per breakpoint.
+ std::pair<std::unordered_set<break_id_t>::iterator, bool> result =
+ precondition_breakpoints.insert(
+ bp_loc_sp->GetBreakpoint().GetID());
+ if (!result.second)
+ continue;
+
+ bool precondition_result =
+ bp_loc_sp->GetBreakpoint().EvaluatePrecondition(context);
+ if (!precondition_result)
+ continue;
+
+ // Next run the condition for the breakpoint. If that says we
+ // should stop, then we'll run the callback for the breakpoint. If
+ // the callback says we shouldn't stop that will win.
+
+ if (bp_loc_sp->GetConditionText() != nullptr) {
+ Status condition_error;
+ bool condition_says_stop =
+ bp_loc_sp->ConditionSaysStop(exe_ctx, condition_error);
+
+ if (!condition_error.Success()) {
+ Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
+ StreamSP error_sp = debugger.GetAsyncErrorStream();
+ error_sp->Printf("Stopped due to an error evaluating condition "
+ "of breakpoint ");
+ bp_loc_sp->GetDescription(error_sp.get(),
+ eDescriptionLevelBrief);
+ error_sp->Printf(": \"%s\"", bp_loc_sp->GetConditionText());
+ error_sp->EOL();
+ const char *err_str =
+ condition_error.AsCString("<Unknown Error>");
+ if (log)
+ log->Printf("Error evaluating condition: \"%s\"\n", err_str);
+
+ error_sp->PutCString(err_str);
+ error_sp->EOL();
+ error_sp->Flush();
+ } else {
+ if (log) {
+ log->Printf("Condition evaluated for breakpoint %s on thread "
+ "0x%llx conditon_says_stop: %i.",
+ loc_desc.GetData(),
+ static_cast<unsigned long long>(
+ thread_sp->GetID()),
+ condition_says_stop);
+ }
+ if (!condition_says_stop) {
+ // We don't want to increment the hit count of breakpoints if
+ // the condition fails. We've already bumped it by the time
+ // we get here, so undo the bump:
+ bp_loc_sp->UndoBumpHitCount();
+ continue;
+ }
+ }
+ }
+
+ // Check the auto-continue bit on the location, do this before the
+ // callback since it may change this, but that would be for the
+ // NEXT hit. Note, you might think you could check auto-continue
+ // before the condition, and not evaluate the condition if it says
+ // to continue. But failing the condition means the breakpoint was
+ // effectively NOT HIT. So these two states are different.
+ bool auto_continue_says_stop = true;
+ if (bp_loc_sp->IsAutoContinue())
+ {
+ if (log)
+ log->Printf("Continuing breakpoint %s as AutoContinue was set.",
+ loc_desc.GetData());
+ // We want this stop reported, so you will know we auto-continued
+ // but only for external breakpoints:
+ if (!internal_breakpoint)
+ thread_sp->SetShouldReportStop(eVoteYes);
+ auto_continue_says_stop = false;
+ }
+
+ bool callback_says_stop = true;
+
+ // FIXME: For now the callbacks have to run in async mode - the
+ // first time we restart we need
+ // to get out of there. So set it here.
+ // When we figure out how to nest breakpoint hits then this will
+ // change.
+
+ Debugger &debugger = thread_sp->CalculateTarget()->GetDebugger();
+ bool old_async = debugger.GetAsyncExecution();
+ debugger.SetAsyncExecution(true);
+
+ callback_says_stop = bp_loc_sp->InvokeCallback(&context);
+
+ debugger.SetAsyncExecution(old_async);
+
+ if (callback_says_stop && auto_continue_says_stop)
+ m_should_stop = true;
+
+ // If we are going to stop for this breakpoint, then remove the
+ // breakpoint.
+ if (callback_says_stop && bp_loc_sp &&
+ bp_loc_sp->GetBreakpoint().IsOneShot()) {
+ thread_sp->GetProcess()->GetTarget().RemoveBreakpointByID(
+ bp_loc_sp->GetBreakpoint().GetID());
+ }
+ // Also make sure that the callback hasn't continued the target. If
+ // it did, when we'll set m_should_start to false and get out of
+ // here.
+ if (HasTargetRunSinceMe()) {
+ m_should_stop = false;
+ break;
+ }
+ }
+ }
+ // We've figured out what this stop wants to do, so mark it as valid so
+ // we don't compute it again.
+ m_should_stop_is_valid = true;
+ } else {
+ m_should_stop = true;
+ m_should_stop_is_valid = true;
+ Log *log_process(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (log_process)
+ log_process->Printf(
+ "Process::%s could not find breakpoint site id: %" PRId64 "...",
+ __FUNCTION__, m_value);
+ }
+
+ if ((!m_should_stop || internal_breakpoint) &&
+ thread_sp->CompletedPlanOverridesBreakpoint()) {
+
+ // Override should_stop decision when we have completed step plan
+ // additionally to the breakpoint
+ m_should_stop = true;
+
+ // Here we clean the preset stop info so the next GetStopInfo call will
+ // find the appropriate stop info, which should be the stop info
+ // related to the completed plan
+ thread_sp->ResetStopInfo();
+ }
+
+ if (log)
+ log->Printf("Process::%s returning from action with m_should_stop: %d.",
+ __FUNCTION__, m_should_stop);
+ }
+ }
+
+private:
+ bool m_should_stop;
+ bool m_should_stop_is_valid;
+ bool m_should_perform_action; // Since we are trying to preserve the "state"
+ // of the system even if we run functions
+ // etc. behind the users backs, we need to make sure we only REALLY perform
+ // the action once.
+ lldb::addr_t m_address; // We use this to capture the breakpoint site address
+ // when we create the StopInfo,
+ // in case somebody deletes it between the time the StopInfo is made and the
+ // description is asked for.
+ lldb::break_id_t m_break_id;
+ bool m_was_one_shot;
+};
+
+// StopInfoWatchpoint
+
+class StopInfoWatchpoint : public StopInfo {
+public:
+ // Make sure watchpoint is properly disabled and subsequently enabled while
+ // performing watchpoint actions.
+ class WatchpointSentry {
+ public:
+ WatchpointSentry(ProcessSP p_sp, WatchpointSP w_sp) : process_sp(p_sp),
+ watchpoint_sp(w_sp) {
+ if (process_sp && watchpoint_sp) {
+ const bool notify = false;
+ watchpoint_sp->TurnOnEphemeralMode();
+ process_sp->DisableWatchpoint(watchpoint_sp.get(), notify);
+ process_sp->AddPreResumeAction(SentryPreResumeAction, this);
+ }
+ }
+
+ void DoReenable() {
+ if (process_sp && watchpoint_sp) {
+ bool was_disabled = watchpoint_sp->IsDisabledDuringEphemeralMode();
+ watchpoint_sp->TurnOffEphemeralMode();
+ const bool notify = false;
+ if (was_disabled) {
+ process_sp->DisableWatchpoint(watchpoint_sp.get(), notify);
+ } else {
+ process_sp->EnableWatchpoint(watchpoint_sp.get(), notify);
+ }
+ }
+ }
+
+ ~WatchpointSentry() {
+ DoReenable();
+ if (process_sp)
+ process_sp->ClearPreResumeAction(SentryPreResumeAction, this);
+ }
+
+ static bool SentryPreResumeAction(void *sentry_void) {
+ WatchpointSentry *sentry = (WatchpointSentry *) sentry_void;
+ sentry->DoReenable();
+ return true;
+ }
+
+ private:
+ ProcessSP process_sp;
+ WatchpointSP watchpoint_sp;
+ };
+
+ StopInfoWatchpoint(Thread &thread, break_id_t watch_id,
+ lldb::addr_t watch_hit_addr)
+ : StopInfo(thread, watch_id), m_should_stop(false),
+ m_should_stop_is_valid(false), m_watch_hit_addr(watch_hit_addr) {}
+
+ ~StopInfoWatchpoint() override = default;
+
+ StopReason GetStopReason() const override { return eStopReasonWatchpoint; }
+
+ const char *GetDescription() override {
+ if (m_description.empty()) {
+ StreamString strm;
+ strm.Printf("watchpoint %" PRIi64, m_value);
+ m_description = strm.GetString();
+ }
+ return m_description.c_str();
+ }
+
+protected:
+ bool ShouldStopSynchronous(Event *event_ptr) override {
+ // ShouldStop() method is idempotent and should not affect hit count. See
+ // Process::RunPrivateStateThread()->Process()->HandlePrivateEvent()
+ // -->Process()::ShouldBroadcastEvent()->ThreadList::ShouldStop()->
+ // Thread::ShouldStop()->ThreadPlanBase::ShouldStop()->
+ // StopInfoWatchpoint::ShouldStop() and
+ // Event::DoOnRemoval()->Process::ProcessEventData::DoOnRemoval()->
+ // StopInfoWatchpoint::PerformAction().
+ if (m_should_stop_is_valid)
+ return m_should_stop;
+
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ WatchpointSP wp_sp(
+ thread_sp->CalculateTarget()->GetWatchpointList().FindByID(
+ GetValue()));
+ if (wp_sp) {
+ // Check if we should stop at a watchpoint.
+ ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
+ StoppointCallbackContext context(event_ptr, exe_ctx, true);
+ m_should_stop = wp_sp->ShouldStop(&context);
+ } else {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf(
+ "Process::%s could not find watchpoint location id: %" PRId64
+ "...",
+ __FUNCTION__, GetValue());
+
+ m_should_stop = true;
+ }
+ }
+ m_should_stop_is_valid = true;
+ return m_should_stop;
+ }
+
+ bool ShouldStop(Event *event_ptr) override {
+ // This just reports the work done by PerformAction or the synchronous
+ // stop. It should only ever get called after they have had a chance to
+ // run.
+ assert(m_should_stop_is_valid);
+ return m_should_stop;
+ }
+
+ void PerformAction(Event *event_ptr) override {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS);
+ // We're going to calculate if we should stop or not in some way during the
+ // course of this code. Also by default we're going to stop, so set that
+ // here.
+ m_should_stop = true;
+
+
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+
+ WatchpointSP wp_sp(
+ thread_sp->CalculateTarget()->GetWatchpointList().FindByID(
+ GetValue()));
+ if (wp_sp) {
+ ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
+ ProcessSP process_sp = exe_ctx.GetProcessSP();
+
+ {
+ // check if this process is running on an architecture where
+ // watchpoints trigger before the associated instruction runs. if so,
+ // disable the WP, single-step and then re-enable the watchpoint
+ if (process_sp) {
+ uint32_t num;
+ bool wp_triggers_after;
+
+ if (process_sp->GetWatchpointSupportInfo(num, wp_triggers_after)
+ .Success()) {
+ if (!wp_triggers_after) {
+ // We need to preserve the watch_index before watchpoint is
+ // disable. Since Watchpoint::SetEnabled will clear the watch
+ // index. This will fix TestWatchpointIter failure
+ Watchpoint *wp = wp_sp.get();
+ uint32_t watch_index = wp->GetHardwareIndex();
+ process_sp->DisableWatchpoint(wp, false);
+ StopInfoSP stored_stop_info_sp = thread_sp->GetStopInfo();
+ assert(stored_stop_info_sp.get() == this);
+
+ Status new_plan_status;
+ ThreadPlanSP new_plan_sp(
+ thread_sp->QueueThreadPlanForStepSingleInstruction(
+ false, // step-over
+ false, // abort_other_plans
+ true, // stop_other_threads
+ new_plan_status));
+ if (new_plan_sp && new_plan_status.Success()) {
+ new_plan_sp->SetIsMasterPlan(true);
+ new_plan_sp->SetOkayToDiscard(false);
+ new_plan_sp->SetPrivate(true);
+ }
+ process_sp->GetThreadList().SetSelectedThreadByID(
+ thread_sp->GetID());
+ process_sp->ResumeSynchronous(nullptr);
+ process_sp->GetThreadList().SetSelectedThreadByID(
+ thread_sp->GetID());
+ thread_sp->SetStopInfo(stored_stop_info_sp);
+ process_sp->EnableWatchpoint(wp, false);
+ wp->SetHardwareIndex(watch_index);
+ }
+ }
+ }
+ }
+
+ // This sentry object makes sure the current watchpoint is disabled
+ // while performing watchpoint actions, and it is then enabled after we
+ // are finished.
+ WatchpointSentry sentry(process_sp, wp_sp);
+
+ /*
+ * MIPS: Last 3bits of the watchpoint address are masked by the kernel.
+ * For example:
+ * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is
+ * set at 'm', then
+ * watch exception is generated even when 'n' is read/written. To handle
+ * this case,
+ * server emulates the instruction at PC and finds the base address of
+ * the load/store
+ * instruction and appends it in the description of the stop-info
+ * packet. If watchpoint
+ * is not set on this address by user then this do not stop.
+ */
+ if (m_watch_hit_addr != LLDB_INVALID_ADDRESS) {
+ WatchpointSP wp_hit_sp =
+ thread_sp->CalculateTarget()->GetWatchpointList().FindByAddress(
+ m_watch_hit_addr);
+ if (!wp_hit_sp) {
+ m_should_stop = false;
+ wp_sp->IncrementFalseAlarmsAndReviseHitCount();
+ }
+ }
+
+ // TODO: This condition should be checked in the synchronous part of the
+ // watchpoint code
+ // (Watchpoint::ShouldStop), so that we avoid pulling an event even if
+ // the watchpoint fails the ignore count condition. It is moved here
+ // temporarily, because for archs with
+ // watchpoint_exceptions_received=before, the code in the previous
+ // lines takes care of moving the inferior to next PC. We have to check
+ // the ignore count condition after this is done, otherwise we will hit
+ // same watchpoint multiple times until we pass ignore condition, but
+ // we won't actually be ignoring them.
+ if (wp_sp->GetHitCount() <= wp_sp->GetIgnoreCount())
+ m_should_stop = false;
+
+ Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
+
+ if (m_should_stop && wp_sp->GetConditionText() != nullptr) {
+ // We need to make sure the user sees any parse errors in their
+ // condition, so we'll hook the constructor errors up to the
+ // debugger's Async I/O.
+ ExpressionResults result_code;
+ EvaluateExpressionOptions expr_options;
+ expr_options.SetUnwindOnError(true);
+ expr_options.SetIgnoreBreakpoints(true);
+ ValueObjectSP result_value_sp;
+ Status error;
+ result_code = UserExpression::Evaluate(
+ exe_ctx, expr_options, wp_sp->GetConditionText(),
+ llvm::StringRef(), result_value_sp, error);
+
+ if (result_code == eExpressionCompleted) {
+ if (result_value_sp) {
+ Scalar scalar_value;
+ if (result_value_sp->ResolveValue(scalar_value)) {
+ if (scalar_value.ULongLong(1) == 0) {
+ // We have been vetoed. This takes precedence over querying
+ // the watchpoint whether it should stop (aka ignore count
+ // and friends). See also StopInfoWatchpoint::ShouldStop()
+ // as well as Process::ProcessEventData::DoOnRemoval().
+ m_should_stop = false;
+ } else
+ m_should_stop = true;
+ if (log)
+ log->Printf(
+ "Condition successfully evaluated, result is %s.\n",
+ m_should_stop ? "true" : "false");
+ } else {
+ m_should_stop = true;
+ if (log)
+ log->Printf(
+ "Failed to get an integer result from the expression.");
+ }
+ }
+ } else {
+ StreamSP error_sp = debugger.GetAsyncErrorStream();
+ error_sp->Printf(
+ "Stopped due to an error evaluating condition of watchpoint ");
+ wp_sp->GetDescription(error_sp.get(), eDescriptionLevelBrief);
+ error_sp->Printf(": \"%s\"", wp_sp->GetConditionText());
+ error_sp->EOL();
+ const char *err_str = error.AsCString("<Unknown Error>");
+ if (log)
+ log->Printf("Error evaluating condition: \"%s\"\n", err_str);
+
+ error_sp->PutCString(err_str);
+ error_sp->EOL();
+ error_sp->Flush();
+ // If the condition fails to be parsed or run, we should stop.
+ m_should_stop = true;
+ }
+ }
+
+ // If the condition says to stop, we run the callback to further decide
+ // whether to stop.
+ if (m_should_stop) {
+ // FIXME: For now the callbacks have to run in async mode - the
+ // first time we restart we need
+ // to get out of there. So set it here.
+ // When we figure out how to nest watchpoint hits then this will
+ // change.
+
+ bool old_async = debugger.GetAsyncExecution();
+ debugger.SetAsyncExecution(true);
+
+ StoppointCallbackContext context(event_ptr, exe_ctx, false);
+ bool stop_requested = wp_sp->InvokeCallback(&context);
+
+ debugger.SetAsyncExecution(old_async);
+
+ // Also make sure that the callback hasn't continued the target. If
+ // it did, when we'll set m_should_stop to false and get out of here.
+ if (HasTargetRunSinceMe())
+ m_should_stop = false;
+
+ if (m_should_stop && !stop_requested) {
+ // We have been vetoed by the callback mechanism.
+ m_should_stop = false;
+ }
+ }
+ // Finally, if we are going to stop, print out the new & old values:
+ if (m_should_stop) {
+ wp_sp->CaptureWatchedValue(exe_ctx);
+
+ Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
+ StreamSP output_sp = debugger.GetAsyncOutputStream();
+ wp_sp->DumpSnapshots(output_sp.get());
+ output_sp->EOL();
+ output_sp->Flush();
+ }
+
+ } else {
+ Log *log_process(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (log_process)
+ log_process->Printf(
+ "Process::%s could not find watchpoint id: %" PRId64 "...",
+ __FUNCTION__, m_value);
+ }
+ if (log)
+ log->Printf("Process::%s returning from action with m_should_stop: %d.",
+ __FUNCTION__, m_should_stop);
+
+ m_should_stop_is_valid = true;
+ }
+ }
+
+private:
+ bool m_should_stop;
+ bool m_should_stop_is_valid;
+ lldb::addr_t m_watch_hit_addr;
+};
+
+// StopInfoUnixSignal
+
+class StopInfoUnixSignal : public StopInfo {
+public:
+ StopInfoUnixSignal(Thread &thread, int signo, const char *description)
+ : StopInfo(thread, signo) {
+ SetDescription(description);
+ }
+
+ ~StopInfoUnixSignal() override = default;
+
+ StopReason GetStopReason() const override { return eStopReasonSignal; }
+
+ bool ShouldStopSynchronous(Event *event_ptr) override {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp)
+ return thread_sp->GetProcess()->GetUnixSignals()->GetShouldStop(m_value);
+ return false;
+ }
+
+ bool ShouldStop(Event *event_ptr) override {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp)
+ return thread_sp->GetProcess()->GetUnixSignals()->GetShouldStop(m_value);
+ return false;
+ }
+
+ // If should stop returns false, check if we should notify of this event
+ bool DoShouldNotify(Event *event_ptr) override {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ bool should_notify =
+ thread_sp->GetProcess()->GetUnixSignals()->GetShouldNotify(m_value);
+ if (should_notify) {
+ StreamString strm;
+ strm.Printf(
+ "thread %d received signal: %s", thread_sp->GetIndexID(),
+ thread_sp->GetProcess()->GetUnixSignals()->GetSignalAsCString(
+ m_value));
+ Process::ProcessEventData::AddRestartedReason(event_ptr,
+ strm.GetData());
+ }
+ return should_notify;
+ }
+ return true;
+ }
+
+ void WillResume(lldb::StateType resume_state) override {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ if (!thread_sp->GetProcess()->GetUnixSignals()->GetShouldSuppress(
+ m_value))
+ thread_sp->SetResumeSignal(m_value);
+ }
+ }
+
+ const char *GetDescription() override {
+ if (m_description.empty()) {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ StreamString strm;
+ const char *signal_name =
+ thread_sp->GetProcess()->GetUnixSignals()->GetSignalAsCString(
+ m_value);
+ if (signal_name)
+ strm.Printf("signal %s", signal_name);
+ else
+ strm.Printf("signal %" PRIi64, m_value);
+ m_description = strm.GetString();
+ }
+ }
+ return m_description.c_str();
+ }
+};
+
+// StopInfoTrace
+
+class StopInfoTrace : public StopInfo {
+public:
+ StopInfoTrace(Thread &thread) : StopInfo(thread, LLDB_INVALID_UID) {}
+
+ ~StopInfoTrace() override = default;
+
+ StopReason GetStopReason() const override { return eStopReasonTrace; }
+
+ const char *GetDescription() override {
+ if (m_description.empty())
+ return "trace";
+ else
+ return m_description.c_str();
+ }
+};
+
+// StopInfoException
+
+class StopInfoException : public StopInfo {
+public:
+ StopInfoException(Thread &thread, const char *description)
+ : StopInfo(thread, LLDB_INVALID_UID) {
+ if (description)
+ SetDescription(description);
+ }
+
+ ~StopInfoException() override = default;
+
+ StopReason GetStopReason() const override { return eStopReasonException; }
+
+ const char *GetDescription() override {
+ if (m_description.empty())
+ return "exception";
+ else
+ return m_description.c_str();
+ }
+};
+
+// StopInfoThreadPlan
+
+class StopInfoThreadPlan : public StopInfo {
+public:
+ StopInfoThreadPlan(ThreadPlanSP &plan_sp, ValueObjectSP &return_valobj_sp,
+ ExpressionVariableSP &expression_variable_sp)
+ : StopInfo(plan_sp->GetThread(), LLDB_INVALID_UID), m_plan_sp(plan_sp),
+ m_return_valobj_sp(return_valobj_sp),
+ m_expression_variable_sp(expression_variable_sp) {}
+
+ ~StopInfoThreadPlan() override = default;
+
+ StopReason GetStopReason() const override { return eStopReasonPlanComplete; }
+
+ const char *GetDescription() override {
+ if (m_description.empty()) {
+ StreamString strm;
+ m_plan_sp->GetDescription(&strm, eDescriptionLevelBrief);
+ m_description = strm.GetString();
+ }
+ return m_description.c_str();
+ }
+
+ ValueObjectSP GetReturnValueObject() { return m_return_valobj_sp; }
+
+ ExpressionVariableSP GetExpressionVariable() {
+ return m_expression_variable_sp;
+ }
+
+protected:
+ bool ShouldStop(Event *event_ptr) override {
+ if (m_plan_sp)
+ return m_plan_sp->ShouldStop(event_ptr);
+ else
+ return StopInfo::ShouldStop(event_ptr);
+ }
+
+private:
+ ThreadPlanSP m_plan_sp;
+ ValueObjectSP m_return_valobj_sp;
+ ExpressionVariableSP m_expression_variable_sp;
+};
+
+// StopInfoExec
+
+class StopInfoExec : public StopInfo {
+public:
+ StopInfoExec(Thread &thread)
+ : StopInfo(thread, LLDB_INVALID_UID), m_performed_action(false) {}
+
+ ~StopInfoExec() override = default;
+
+ bool ShouldStop(Event *event_ptr) override {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp)
+ return thread_sp->GetProcess()->GetStopOnExec();
+ return false;
+ }
+
+ StopReason GetStopReason() const override { return eStopReasonExec; }
+
+ const char *GetDescription() override { return "exec"; }
+
+protected:
+ void PerformAction(Event *event_ptr) override {
+ // Only perform the action once
+ if (m_performed_action)
+ return;
+ m_performed_action = true;
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp)
+ thread_sp->GetProcess()->DidExec();
+ }
+
+ bool m_performed_action;
+};
+
+} // namespace lldb_private
+
+StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread,
+ break_id_t break_id) {
+ return StopInfoSP(new StopInfoBreakpoint(thread, break_id));
+}
+
+StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread,
+ break_id_t break_id,
+ bool should_stop) {
+ return StopInfoSP(new StopInfoBreakpoint(thread, break_id, should_stop));
+}
+
+StopInfoSP
+StopInfo::CreateStopReasonWithWatchpointID(Thread &thread, break_id_t watch_id,
+ lldb::addr_t watch_hit_addr) {
+ return StopInfoSP(new StopInfoWatchpoint(thread, watch_id, watch_hit_addr));
+}
+
+StopInfoSP StopInfo::CreateStopReasonWithSignal(Thread &thread, int signo,
+ const char *description) {
+ return StopInfoSP(new StopInfoUnixSignal(thread, signo, description));
+}
+
+StopInfoSP StopInfo::CreateStopReasonToTrace(Thread &thread) {
+ return StopInfoSP(new StopInfoTrace(thread));
+}
+
+StopInfoSP StopInfo::CreateStopReasonWithPlan(
+ ThreadPlanSP &plan_sp, ValueObjectSP return_valobj_sp,
+ ExpressionVariableSP expression_variable_sp) {
+ return StopInfoSP(new StopInfoThreadPlan(plan_sp, return_valobj_sp,
+ expression_variable_sp));
+}
+
+StopInfoSP StopInfo::CreateStopReasonWithException(Thread &thread,
+ const char *description) {
+ return StopInfoSP(new StopInfoException(thread, description));
+}
+
+StopInfoSP StopInfo::CreateStopReasonWithExec(Thread &thread) {
+ return StopInfoSP(new StopInfoExec(thread));
+}
+
+ValueObjectSP StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp) {
+ if (stop_info_sp &&
+ stop_info_sp->GetStopReason() == eStopReasonPlanComplete) {
+ StopInfoThreadPlan *plan_stop_info =
+ static_cast<StopInfoThreadPlan *>(stop_info_sp.get());
+ return plan_stop_info->GetReturnValueObject();
+ } else
+ return ValueObjectSP();
+}
+
+ExpressionVariableSP StopInfo::GetExpressionVariable(StopInfoSP &stop_info_sp) {
+ if (stop_info_sp &&
+ stop_info_sp->GetStopReason() == eStopReasonPlanComplete) {
+ StopInfoThreadPlan *plan_stop_info =
+ static_cast<StopInfoThreadPlan *>(stop_info_sp.get());
+ return plan_stop_info->GetExpressionVariable();
+ } else
+ return ExpressionVariableSP();
+}
+
+lldb::ValueObjectSP
+StopInfo::GetCrashingDereference(StopInfoSP &stop_info_sp,
+ lldb::addr_t *crashing_address) {
+ if (!stop_info_sp) {
+ return ValueObjectSP();
+ }
+
+ const char *description = stop_info_sp->GetDescription();
+ if (!description) {
+ return ValueObjectSP();
+ }
+
+ ThreadSP thread_sp = stop_info_sp->GetThread();
+ if (!thread_sp) {
+ return ValueObjectSP();
+ }
+
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+
+ if (!frame_sp) {
+ return ValueObjectSP();
+ }
+
+ const char address_string[] = "address=";
+
+ const char *address_loc = strstr(description, address_string);
+ if (!address_loc) {
+ return ValueObjectSP();
+ }
+
+ address_loc += (sizeof(address_string) - 1);
+
+ uint64_t address = strtoull(address_loc, nullptr, 0);
+ if (crashing_address) {
+ *crashing_address = address;
+ }
+
+ return frame_sp->GuessValueForAddress(address);
+}
diff --git a/contrib/llvm-project/lldb/source/Target/StructuredDataPlugin.cpp b/contrib/llvm-project/lldb/source/Target/StructuredDataPlugin.cpp
new file mode 100644
index 000000000000..a22902d99f7c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/StructuredDataPlugin.cpp
@@ -0,0 +1,67 @@
+//===-- StructuredDataPlugin.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/StructuredDataPlugin.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+class CommandStructuredData : public CommandObjectMultiword {
+public:
+ CommandStructuredData(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "structured-data",
+ "Parent for per-plugin structured data commands",
+ "plugin structured-data <plugin>") {}
+
+ ~CommandStructuredData() override {}
+};
+}
+
+StructuredDataPlugin::StructuredDataPlugin(const ProcessWP &process_wp)
+ : PluginInterface(), m_process_wp(process_wp) {}
+
+StructuredDataPlugin::~StructuredDataPlugin() {}
+
+bool StructuredDataPlugin::GetEnabled(ConstString type_name) const {
+ // By default, plugins are always enabled. Plugin authors should override
+ // this if there is an enabled/disabled state for their plugin.
+ return true;
+}
+
+ProcessSP StructuredDataPlugin::GetProcess() const {
+ return m_process_wp.lock();
+}
+
+void StructuredDataPlugin::InitializeBasePluginForDebugger(Debugger &debugger) {
+ // Create our mutliword command anchor if it doesn't already exist.
+ auto &interpreter = debugger.GetCommandInterpreter();
+ if (!interpreter.GetCommandObject("plugin structured-data")) {
+ // Find the parent command.
+ auto parent_command =
+ debugger.GetCommandInterpreter().GetCommandObject("plugin");
+ if (!parent_command)
+ return;
+
+ // Create the structured-data ommand object.
+ auto command_name = "structured-data";
+ auto command_sp = CommandObjectSP(new CommandStructuredData(interpreter));
+
+ // Hook it up under the top-level plugin command.
+ parent_command->LoadSubCommand(command_name, command_sp);
+ }
+}
+
+void StructuredDataPlugin::ModulesDidLoad(Process &process,
+ ModuleList &module_list) {
+ // Default implementation does nothing.
+}
diff --git a/contrib/llvm-project/lldb/source/Target/SystemRuntime.cpp b/contrib/llvm-project/lldb/source/Target/SystemRuntime.cpp
new file mode 100644
index 000000000000..286bea09f854
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/SystemRuntime.cpp
@@ -0,0 +1,51 @@
+//===-- SystemRuntime.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Process.h"
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SystemRuntime *SystemRuntime::FindPlugin(Process *process) {
+ SystemRuntimeCreateInstance create_callback = nullptr;
+ for (uint32_t idx = 0;
+ (create_callback = PluginManager::GetSystemRuntimeCreateCallbackAtIndex(
+ idx)) != nullptr;
+ ++idx) {
+ std::unique_ptr<SystemRuntime> instance_up(create_callback(process));
+ if (instance_up)
+ return instance_up.release();
+ }
+ return nullptr;
+}
+
+// SystemRuntime constructor
+SystemRuntime::SystemRuntime(Process *process)
+ : m_process(process), m_types() {}
+
+SystemRuntime::~SystemRuntime() = default;
+
+void SystemRuntime::DidAttach() {}
+
+void SystemRuntime::DidLaunch() {}
+
+void SystemRuntime::Detach() {}
+
+void SystemRuntime::ModulesDidLoad(ModuleList &module_list) {}
+
+const std::vector<ConstString> &SystemRuntime::GetExtendedBacktraceTypes() {
+ return m_types;
+}
+
+ThreadSP SystemRuntime::GetExtendedBacktraceThread(ThreadSP thread,
+ ConstString type) {
+ return ThreadSP();
+}
diff --git a/contrib/llvm-project/lldb/source/Target/Target.cpp b/contrib/llvm-project/lldb/source/Target/Target.cpp
new file mode 100644
index 000000000000..4941cb585c55
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/Target.cpp
@@ -0,0 +1,4261 @@
+//===-- Target.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/Target.h"
+#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
+#include "lldb/Breakpoint/BreakpointIDList.h"
+#include "lldb/Breakpoint/BreakpointPrecondition.h"
+#include "lldb/Breakpoint/BreakpointResolver.h"
+#include "lldb/Breakpoint/BreakpointResolverAddress.h"
+#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
+#include "lldb/Breakpoint/BreakpointResolverFileRegex.h"
+#include "lldb/Breakpoint/BreakpointResolverName.h"
+#include "lldb/Breakpoint/BreakpointResolverScripted.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/SearchFilter.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/REPL.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionGroupWatchpoint.h"
+#include "lldb/Interpreter/OptionValues.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+#include "llvm/ADT/ScopeExit.h"
+
+#include <memory>
+#include <mutex>
+
+using namespace lldb;
+using namespace lldb_private;
+
+constexpr std::chrono::milliseconds EvaluateExpressionOptions::default_timeout;
+
+Target::Arch::Arch(const ArchSpec &spec)
+ : m_spec(spec),
+ m_plugin_up(PluginManager::CreateArchitectureInstance(spec)) {}
+
+const Target::Arch& Target::Arch::operator=(const ArchSpec &spec) {
+ m_spec = spec;
+ m_plugin_up = PluginManager::CreateArchitectureInstance(spec);
+ return *this;
+}
+
+ConstString &Target::GetStaticBroadcasterClass() {
+ static ConstString class_name("lldb.target");
+ return class_name;
+}
+
+Target::Target(Debugger &debugger, const ArchSpec &target_arch,
+ const lldb::PlatformSP &platform_sp, bool is_dummy_target)
+ : TargetProperties(this),
+ Broadcaster(debugger.GetBroadcasterManager(),
+ Target::GetStaticBroadcasterClass().AsCString()),
+ ExecutionContextScope(), m_debugger(debugger), m_platform_sp(platform_sp),
+ m_mutex(), m_arch(target_arch), m_images(this), m_section_load_history(),
+ m_breakpoint_list(false), m_internal_breakpoint_list(true),
+ m_watchpoint_list(), m_process_sp(), m_search_filter_sp(),
+ m_image_search_paths(ImageSearchPathsChanged, this), m_ast_importer_sp(),
+ m_source_manager_up(), m_stop_hooks(), m_stop_hook_next_id(0),
+ m_valid(true), m_suppress_stop_hooks(false),
+ m_is_dummy_target(is_dummy_target),
+ m_stats_storage(static_cast<int>(StatisticKind::StatisticMax))
+
+{
+ SetEventName(eBroadcastBitBreakpointChanged, "breakpoint-changed");
+ SetEventName(eBroadcastBitModulesLoaded, "modules-loaded");
+ SetEventName(eBroadcastBitModulesUnloaded, "modules-unloaded");
+ SetEventName(eBroadcastBitWatchpointChanged, "watchpoint-changed");
+ SetEventName(eBroadcastBitSymbolsLoaded, "symbols-loaded");
+
+ CheckInWithManager();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p Target::Target()", static_cast<void *>(this));
+ if (target_arch.IsValid()) {
+ LogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET,
+ "Target::Target created with architecture %s (%s)",
+ target_arch.GetArchitectureName(),
+ target_arch.GetTriple().getTriple().c_str());
+ }
+}
+
+Target::~Target() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p Target::~Target()", static_cast<void *>(this));
+ DeleteCurrentProcess();
+}
+
+void Target::PrimeFromDummyTarget(Target *target) {
+ if (!target)
+ return;
+
+ m_stop_hooks = target->m_stop_hooks;
+
+ for (BreakpointSP breakpoint_sp : target->m_breakpoint_list.Breakpoints()) {
+ if (breakpoint_sp->IsInternal())
+ continue;
+
+ BreakpointSP new_bp(new Breakpoint(*this, *breakpoint_sp.get()));
+ AddBreakpoint(new_bp, false);
+ }
+
+ for (auto bp_name_entry : target->m_breakpoint_names)
+ {
+
+ BreakpointName *new_bp_name = new BreakpointName(*bp_name_entry.second);
+ AddBreakpointName(new_bp_name);
+ }
+}
+
+void Target::Dump(Stream *s, lldb::DescriptionLevel description_level) {
+ // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this);
+ if (description_level != lldb::eDescriptionLevelBrief) {
+ s->Indent();
+ s->PutCString("Target\n");
+ s->IndentMore();
+ m_images.Dump(s);
+ m_breakpoint_list.Dump(s);
+ m_internal_breakpoint_list.Dump(s);
+ s->IndentLess();
+ } else {
+ Module *exe_module = GetExecutableModulePointer();
+ if (exe_module)
+ s->PutCString(exe_module->GetFileSpec().GetFilename().GetCString());
+ else
+ s->PutCString("No executable module.");
+ }
+}
+
+void Target::CleanupProcess() {
+ // Do any cleanup of the target we need to do between process instances.
+ // NB It is better to do this before destroying the process in case the
+ // clean up needs some help from the process.
+ m_breakpoint_list.ClearAllBreakpointSites();
+ m_internal_breakpoint_list.ClearAllBreakpointSites();
+ // Disable watchpoints just on the debugger side.
+ std::unique_lock<std::recursive_mutex> lock;
+ this->GetWatchpointList().GetListMutex(lock);
+ DisableAllWatchpoints(false);
+ ClearAllWatchpointHitCounts();
+ ClearAllWatchpointHistoricValues();
+}
+
+void Target::DeleteCurrentProcess() {
+ if (m_process_sp) {
+ m_section_load_history.Clear();
+ if (m_process_sp->IsAlive())
+ m_process_sp->Destroy(false);
+
+ m_process_sp->Finalize();
+
+ CleanupProcess();
+
+ m_process_sp.reset();
+ }
+}
+
+const lldb::ProcessSP &Target::CreateProcess(ListenerSP listener_sp,
+ llvm::StringRef plugin_name,
+ const FileSpec *crash_file) {
+ if (!listener_sp)
+ listener_sp = GetDebugger().GetListener();
+ DeleteCurrentProcess();
+ m_process_sp = Process::FindPlugin(shared_from_this(), plugin_name,
+ listener_sp, crash_file);
+ return m_process_sp;
+}
+
+const lldb::ProcessSP &Target::GetProcessSP() const { return m_process_sp; }
+
+lldb::REPLSP Target::GetREPL(Status &err, lldb::LanguageType language,
+ const char *repl_options, bool can_create) {
+ if (language == eLanguageTypeUnknown) {
+ std::set<LanguageType> repl_languages;
+
+ Language::GetLanguagesSupportingREPLs(repl_languages);
+
+ if (repl_languages.size() == 1) {
+ language = *repl_languages.begin();
+ } else if (repl_languages.size() == 0) {
+ err.SetErrorStringWithFormat(
+ "LLDB isn't configured with REPL support for any languages.");
+ return REPLSP();
+ } else {
+ err.SetErrorStringWithFormat(
+ "Multiple possible REPL languages. Please specify a language.");
+ return REPLSP();
+ }
+ }
+
+ REPLMap::iterator pos = m_repl_map.find(language);
+
+ if (pos != m_repl_map.end()) {
+ return pos->second;
+ }
+
+ if (!can_create) {
+ err.SetErrorStringWithFormat(
+ "Couldn't find an existing REPL for %s, and can't create a new one",
+ Language::GetNameForLanguageType(language));
+ return lldb::REPLSP();
+ }
+
+ Debugger *const debugger = nullptr;
+ lldb::REPLSP ret = REPL::Create(err, language, debugger, this, repl_options);
+
+ if (ret) {
+ m_repl_map[language] = ret;
+ return m_repl_map[language];
+ }
+
+ if (err.Success()) {
+ err.SetErrorStringWithFormat("Couldn't create a REPL for %s",
+ Language::GetNameForLanguageType(language));
+ }
+
+ return lldb::REPLSP();
+}
+
+void Target::SetREPL(lldb::LanguageType language, lldb::REPLSP repl_sp) {
+ lldbassert(!m_repl_map.count(language));
+
+ m_repl_map[language] = repl_sp;
+}
+
+void Target::Destroy() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_valid = false;
+ DeleteCurrentProcess();
+ m_platform_sp.reset();
+ m_arch = ArchSpec();
+ ClearModules(true);
+ m_section_load_history.Clear();
+ const bool notify = false;
+ m_breakpoint_list.RemoveAll(notify);
+ m_internal_breakpoint_list.RemoveAll(notify);
+ m_last_created_breakpoint.reset();
+ m_last_created_watchpoint.reset();
+ m_search_filter_sp.reset();
+ m_image_search_paths.Clear(notify);
+ m_stop_hooks.clear();
+ m_stop_hook_next_id = 0;
+ m_suppress_stop_hooks = false;
+}
+
+BreakpointList &Target::GetBreakpointList(bool internal) {
+ if (internal)
+ return m_internal_breakpoint_list;
+ else
+ return m_breakpoint_list;
+}
+
+const BreakpointList &Target::GetBreakpointList(bool internal) const {
+ if (internal)
+ return m_internal_breakpoint_list;
+ else
+ return m_breakpoint_list;
+}
+
+BreakpointSP Target::GetBreakpointByID(break_id_t break_id) {
+ BreakpointSP bp_sp;
+
+ if (LLDB_BREAK_ID_IS_INTERNAL(break_id))
+ bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id);
+ else
+ bp_sp = m_breakpoint_list.FindBreakpointByID(break_id);
+
+ return bp_sp;
+}
+
+BreakpointSP Target::CreateSourceRegexBreakpoint(
+ const FileSpecList *containingModules,
+ const FileSpecList *source_file_spec_list,
+ const std::unordered_set<std::string> &function_names,
+ RegularExpression &source_regex, bool internal, bool hardware,
+ LazyBool move_to_nearest_code) {
+ SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList(
+ containingModules, source_file_spec_list));
+ if (move_to_nearest_code == eLazyBoolCalculate)
+ move_to_nearest_code = GetMoveToNearestCode() ? eLazyBoolYes : eLazyBoolNo;
+ BreakpointResolverSP resolver_sp(new BreakpointResolverFileRegex(
+ nullptr, source_regex, function_names,
+ !static_cast<bool>(move_to_nearest_code)));
+
+ return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true);
+}
+
+BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules,
+ const FileSpec &file, uint32_t line_no,
+ uint32_t column, lldb::addr_t offset,
+ LazyBool check_inlines,
+ LazyBool skip_prologue, bool internal,
+ bool hardware,
+ LazyBool move_to_nearest_code) {
+ FileSpec remapped_file;
+ if (!GetSourcePathMap().ReverseRemapPath(file, remapped_file))
+ remapped_file = file;
+
+ if (check_inlines == eLazyBoolCalculate) {
+ const InlineStrategy inline_strategy = GetInlineStrategy();
+ switch (inline_strategy) {
+ case eInlineBreakpointsNever:
+ check_inlines = eLazyBoolNo;
+ break;
+
+ case eInlineBreakpointsHeaders:
+ if (remapped_file.IsSourceImplementationFile())
+ check_inlines = eLazyBoolNo;
+ else
+ check_inlines = eLazyBoolYes;
+ break;
+
+ case eInlineBreakpointsAlways:
+ check_inlines = eLazyBoolYes;
+ break;
+ }
+ }
+ SearchFilterSP filter_sp;
+ if (check_inlines == eLazyBoolNo) {
+ // Not checking for inlines, we are looking only for matching compile units
+ FileSpecList compile_unit_list;
+ compile_unit_list.Append(remapped_file);
+ filter_sp = GetSearchFilterForModuleAndCUList(containingModules,
+ &compile_unit_list);
+ } else {
+ filter_sp = GetSearchFilterForModuleList(containingModules);
+ }
+ if (skip_prologue == eLazyBoolCalculate)
+ skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo;
+ if (move_to_nearest_code == eLazyBoolCalculate)
+ move_to_nearest_code = GetMoveToNearestCode() ? eLazyBoolYes : eLazyBoolNo;
+
+ BreakpointResolverSP resolver_sp(new BreakpointResolverFileLine(
+ nullptr, remapped_file, line_no, column, offset, check_inlines,
+ skip_prologue, !static_cast<bool>(move_to_nearest_code)));
+ return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true);
+}
+
+BreakpointSP Target::CreateBreakpoint(lldb::addr_t addr, bool internal,
+ bool hardware) {
+ Address so_addr;
+
+ // Check for any reason we want to move this breakpoint to other address.
+ addr = GetBreakableLoadAddress(addr);
+
+ // Attempt to resolve our load address if possible, though it is ok if it
+ // doesn't resolve to section/offset.
+
+ // Try and resolve as a load address if possible
+ GetSectionLoadList().ResolveLoadAddress(addr, so_addr);
+ if (!so_addr.IsValid()) {
+ // The address didn't resolve, so just set this as an absolute address
+ so_addr.SetOffset(addr);
+ }
+ BreakpointSP bp_sp(CreateBreakpoint(so_addr, internal, hardware));
+ return bp_sp;
+}
+
+BreakpointSP Target::CreateBreakpoint(const Address &addr, bool internal,
+ bool hardware) {
+ SearchFilterSP filter_sp(
+ new SearchFilterForUnconstrainedSearches(shared_from_this()));
+ BreakpointResolverSP resolver_sp(
+ new BreakpointResolverAddress(nullptr, addr));
+ return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, false);
+}
+
+lldb::BreakpointSP
+Target::CreateAddressInModuleBreakpoint(lldb::addr_t file_addr, bool internal,
+ const FileSpec *file_spec,
+ bool request_hardware) {
+ SearchFilterSP filter_sp(
+ new SearchFilterForUnconstrainedSearches(shared_from_this()));
+ BreakpointResolverSP resolver_sp(
+ new BreakpointResolverAddress(nullptr, file_addr, file_spec));
+ return CreateBreakpoint(filter_sp, resolver_sp, internal, request_hardware,
+ false);
+}
+
+BreakpointSP Target::CreateBreakpoint(
+ const FileSpecList *containingModules,
+ const FileSpecList *containingSourceFiles, const char *func_name,
+ FunctionNameType func_name_type_mask, LanguageType language,
+ lldb::addr_t offset, LazyBool skip_prologue, bool internal, bool hardware) {
+ BreakpointSP bp_sp;
+ if (func_name) {
+ SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList(
+ containingModules, containingSourceFiles));
+
+ if (skip_prologue == eLazyBoolCalculate)
+ skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo;
+ if (language == lldb::eLanguageTypeUnknown)
+ language = GetLanguage();
+
+ BreakpointResolverSP resolver_sp(new BreakpointResolverName(
+ nullptr, func_name, func_name_type_mask, language, Breakpoint::Exact,
+ offset, skip_prologue));
+ bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true);
+ }
+ return bp_sp;
+}
+
+lldb::BreakpointSP
+Target::CreateBreakpoint(const FileSpecList *containingModules,
+ const FileSpecList *containingSourceFiles,
+ const std::vector<std::string> &func_names,
+ FunctionNameType func_name_type_mask,
+ LanguageType language, lldb::addr_t offset,
+ LazyBool skip_prologue, bool internal, bool hardware) {
+ BreakpointSP bp_sp;
+ size_t num_names = func_names.size();
+ if (num_names > 0) {
+ SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList(
+ containingModules, containingSourceFiles));
+
+ if (skip_prologue == eLazyBoolCalculate)
+ skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo;
+ if (language == lldb::eLanguageTypeUnknown)
+ language = GetLanguage();
+
+ BreakpointResolverSP resolver_sp(
+ new BreakpointResolverName(nullptr, func_names, func_name_type_mask,
+ language, offset, skip_prologue));
+ bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true);
+ }
+ return bp_sp;
+}
+
+BreakpointSP
+Target::CreateBreakpoint(const FileSpecList *containingModules,
+ const FileSpecList *containingSourceFiles,
+ const char *func_names[], size_t num_names,
+ FunctionNameType func_name_type_mask,
+ LanguageType language, lldb::addr_t offset,
+ LazyBool skip_prologue, bool internal, bool hardware) {
+ BreakpointSP bp_sp;
+ if (num_names > 0) {
+ SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList(
+ containingModules, containingSourceFiles));
+
+ if (skip_prologue == eLazyBoolCalculate) {
+ if (offset == 0)
+ skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo;
+ else
+ skip_prologue = eLazyBoolNo;
+ }
+ if (language == lldb::eLanguageTypeUnknown)
+ language = GetLanguage();
+
+ BreakpointResolverSP resolver_sp(new BreakpointResolverName(
+ nullptr, func_names, num_names, func_name_type_mask, language, offset,
+ skip_prologue));
+ resolver_sp->SetOffset(offset);
+ bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true);
+ }
+ return bp_sp;
+}
+
+SearchFilterSP
+Target::GetSearchFilterForModule(const FileSpec *containingModule) {
+ SearchFilterSP filter_sp;
+ if (containingModule != nullptr) {
+ // TODO: We should look into sharing module based search filters
+ // across many breakpoints like we do for the simple target based one
+ filter_sp = std::make_shared<SearchFilterByModule>(shared_from_this(),
+ *containingModule);
+ } else {
+ if (!m_search_filter_sp)
+ m_search_filter_sp =
+ std::make_shared<SearchFilterForUnconstrainedSearches>(
+ shared_from_this());
+ filter_sp = m_search_filter_sp;
+ }
+ return filter_sp;
+}
+
+SearchFilterSP
+Target::GetSearchFilterForModuleList(const FileSpecList *containingModules) {
+ SearchFilterSP filter_sp;
+ if (containingModules && containingModules->GetSize() != 0) {
+ // TODO: We should look into sharing module based search filters
+ // across many breakpoints like we do for the simple target based one
+ filter_sp = std::make_shared<SearchFilterByModuleList>(shared_from_this(),
+ *containingModules);
+ } else {
+ if (!m_search_filter_sp)
+ m_search_filter_sp =
+ std::make_shared<SearchFilterForUnconstrainedSearches>(
+ shared_from_this());
+ filter_sp = m_search_filter_sp;
+ }
+ return filter_sp;
+}
+
+SearchFilterSP Target::GetSearchFilterForModuleAndCUList(
+ const FileSpecList *containingModules,
+ const FileSpecList *containingSourceFiles) {
+ if (containingSourceFiles == nullptr || containingSourceFiles->GetSize() == 0)
+ return GetSearchFilterForModuleList(containingModules);
+
+ SearchFilterSP filter_sp;
+ if (containingModules == nullptr) {
+ // We could make a special "CU List only SearchFilter". Better yet was if
+ // these could be composable, but that will take a little reworking.
+
+ filter_sp = std::make_shared<SearchFilterByModuleListAndCU>(
+ shared_from_this(), FileSpecList(), *containingSourceFiles);
+ } else {
+ filter_sp = std::make_shared<SearchFilterByModuleListAndCU>(
+ shared_from_this(), *containingModules, *containingSourceFiles);
+ }
+ return filter_sp;
+}
+
+BreakpointSP Target::CreateFuncRegexBreakpoint(
+ const FileSpecList *containingModules,
+ const FileSpecList *containingSourceFiles, RegularExpression &func_regex,
+ lldb::LanguageType requested_language, LazyBool skip_prologue,
+ bool internal, bool hardware) {
+ SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList(
+ containingModules, containingSourceFiles));
+ bool skip = (skip_prologue == eLazyBoolCalculate)
+ ? GetSkipPrologue()
+ : static_cast<bool>(skip_prologue);
+ BreakpointResolverSP resolver_sp(new BreakpointResolverName(
+ nullptr, func_regex, requested_language, 0, skip));
+
+ return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true);
+}
+
+lldb::BreakpointSP
+Target::CreateExceptionBreakpoint(enum lldb::LanguageType language,
+ bool catch_bp, bool throw_bp, bool internal,
+ Args *additional_args, Status *error) {
+ BreakpointSP exc_bkpt_sp = LanguageRuntime::CreateExceptionBreakpoint(
+ *this, language, catch_bp, throw_bp, internal);
+ if (exc_bkpt_sp && additional_args) {
+ BreakpointPreconditionSP precondition_sp = exc_bkpt_sp->GetPrecondition();
+ if (precondition_sp && additional_args) {
+ if (error)
+ *error = precondition_sp->ConfigurePrecondition(*additional_args);
+ else
+ precondition_sp->ConfigurePrecondition(*additional_args);
+ }
+ }
+ return exc_bkpt_sp;
+}
+
+lldb::BreakpointSP
+Target::CreateScriptedBreakpoint(const llvm::StringRef class_name,
+ const FileSpecList *containingModules,
+ const FileSpecList *containingSourceFiles,
+ bool internal,
+ bool request_hardware,
+ StructuredData::ObjectSP extra_args_sp,
+ Status *creation_error)
+{
+ SearchFilterSP filter_sp;
+
+ lldb::SearchDepth depth = lldb::eSearchDepthTarget;
+ bool has_files = containingSourceFiles && containingSourceFiles->GetSize() > 0;
+ bool has_modules = containingModules && containingModules->GetSize() > 0;
+
+ if (has_files && has_modules) {
+ filter_sp = GetSearchFilterForModuleAndCUList(
+ containingModules, containingSourceFiles);
+ } else if (has_files) {
+ filter_sp = GetSearchFilterForModuleAndCUList(
+ nullptr, containingSourceFiles);
+ } else if (has_modules) {
+ filter_sp = GetSearchFilterForModuleList(containingModules);
+ } else {
+ filter_sp = std::make_shared<SearchFilterForUnconstrainedSearches>(
+ shared_from_this());
+ }
+
+ StructuredDataImpl *extra_args_impl = new StructuredDataImpl();
+ if (extra_args_sp)
+ extra_args_impl->SetObjectSP(extra_args_sp);
+
+ BreakpointResolverSP resolver_sp(new BreakpointResolverScripted(
+ nullptr, class_name, depth, extra_args_impl,
+ *GetDebugger().GetScriptInterpreter()));
+ return CreateBreakpoint(filter_sp, resolver_sp, internal, false, true);
+
+}
+
+
+BreakpointSP Target::CreateBreakpoint(SearchFilterSP &filter_sp,
+ BreakpointResolverSP &resolver_sp,
+ bool internal, bool request_hardware,
+ bool resolve_indirect_symbols) {
+ BreakpointSP bp_sp;
+ if (filter_sp && resolver_sp) {
+ const bool hardware = request_hardware || GetRequireHardwareBreakpoints();
+ bp_sp.reset(new Breakpoint(*this, filter_sp, resolver_sp, hardware,
+ resolve_indirect_symbols));
+ resolver_sp->SetBreakpoint(bp_sp.get());
+ AddBreakpoint(bp_sp, internal);
+ }
+ return bp_sp;
+}
+
+void Target::AddBreakpoint(lldb::BreakpointSP bp_sp, bool internal) {
+ if (!bp_sp)
+ return;
+ if (internal)
+ m_internal_breakpoint_list.Add(bp_sp, false);
+ else
+ m_breakpoint_list.Add(bp_sp, true);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log) {
+ StreamString s;
+ bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ log->Printf("Target::%s (internal = %s) => break_id = %s\n", __FUNCTION__,
+ bp_sp->IsInternal() ? "yes" : "no", s.GetData());
+ }
+
+ bp_sp->ResolveBreakpoint();
+
+ if (!internal) {
+ m_last_created_breakpoint = bp_sp;
+ }
+}
+
+void Target::AddNameToBreakpoint(BreakpointID &id,
+ const char *name,
+ Status &error)
+ {
+ BreakpointSP bp_sp
+ = m_breakpoint_list.FindBreakpointByID(id.GetBreakpointID());
+ if (!bp_sp)
+ {
+ StreamString s;
+ id.GetDescription(&s, eDescriptionLevelBrief);
+ error.SetErrorStringWithFormat("Could not find breakpoint %s",
+ s.GetData());
+ return;
+ }
+ AddNameToBreakpoint(bp_sp, name, error);
+ }
+
+void Target::AddNameToBreakpoint(BreakpointSP &bp_sp,
+ const char *name,
+ Status &error)
+ {
+ if (!bp_sp)
+ return;
+
+ BreakpointName *bp_name = FindBreakpointName(ConstString(name), true, error);
+ if (!bp_name)
+ return;
+
+ bp_name->ConfigureBreakpoint(bp_sp);
+ bp_sp->AddName(name);
+ }
+
+void Target::AddBreakpointName(BreakpointName *bp_name) {
+ m_breakpoint_names.insert(std::make_pair(bp_name->GetName(), bp_name));
+}
+
+BreakpointName *Target::FindBreakpointName(ConstString name,
+ bool can_create,
+ Status &error)
+{
+ BreakpointID::StringIsBreakpointName(name.GetStringRef(), error);
+ if (!error.Success())
+ return nullptr;
+
+ BreakpointNameList::iterator iter = m_breakpoint_names.find(name);
+ if (iter == m_breakpoint_names.end()) {
+ if (!can_create)
+ {
+ error.SetErrorStringWithFormat("Breakpoint name \"%s\" doesn't exist and "
+ "can_create is false.", name.AsCString());
+ return nullptr;
+ }
+
+ iter = m_breakpoint_names.insert(std::make_pair(name,
+ new BreakpointName(name)))
+ .first;
+ }
+ return (iter->second);
+}
+
+void
+Target::DeleteBreakpointName(ConstString name)
+{
+ BreakpointNameList::iterator iter = m_breakpoint_names.find(name);
+
+ if (iter != m_breakpoint_names.end()) {
+ const char *name_cstr = name.AsCString();
+ m_breakpoint_names.erase(iter);
+ for (auto bp_sp : m_breakpoint_list.Breakpoints())
+ bp_sp->RemoveName(name_cstr);
+ }
+}
+
+void Target::RemoveNameFromBreakpoint(lldb::BreakpointSP &bp_sp,
+ ConstString name)
+{
+ bp_sp->RemoveName(name.AsCString());
+}
+
+void Target::ConfigureBreakpointName(BreakpointName &bp_name,
+ const BreakpointOptions &new_options,
+ const BreakpointName::Permissions &new_permissions)
+{
+ bp_name.GetOptions().CopyOverSetOptions(new_options);
+ bp_name.GetPermissions().MergeInto(new_permissions);
+ ApplyNameToBreakpoints(bp_name);
+}
+
+void Target::ApplyNameToBreakpoints(BreakpointName &bp_name) {
+ BreakpointList bkpts_with_name(false);
+ m_breakpoint_list.FindBreakpointsByName(bp_name.GetName().AsCString(),
+ bkpts_with_name);
+
+ for (auto bp_sp : bkpts_with_name.Breakpoints())
+ bp_name.ConfigureBreakpoint(bp_sp);
+}
+
+void Target::GetBreakpointNames(std::vector<std::string> &names)
+{
+ names.clear();
+ for (auto bp_name : m_breakpoint_names) {
+ names.push_back(bp_name.first.AsCString());
+ }
+ llvm::sort(names.begin(), names.end());
+}
+
+bool Target::ProcessIsValid() {
+ return (m_process_sp && m_process_sp->IsAlive());
+}
+
+static bool CheckIfWatchpointsSupported(Target *target, Status &error) {
+ uint32_t num_supported_hardware_watchpoints;
+ Status rc = target->GetProcessSP()->GetWatchpointSupportInfo(
+ num_supported_hardware_watchpoints);
+
+ // If unable to determine the # of watchpoints available,
+ // assume they are supported.
+ if (rc.Fail())
+ return true;
+
+ if (num_supported_hardware_watchpoints == 0) {
+ error.SetErrorStringWithFormat(
+ "Target supports (%u) hardware watchpoint slots.\n",
+ num_supported_hardware_watchpoints);
+ return false;
+ }
+ return true;
+}
+
+// See also Watchpoint::SetWatchpointType(uint32_t type) and the
+// OptionGroupWatchpoint::WatchType enum type.
+WatchpointSP Target::CreateWatchpoint(lldb::addr_t addr, size_t size,
+ const CompilerType *type, uint32_t kind,
+ Status &error) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s (addr = 0x%8.8" PRIx64 " size = %" PRIu64
+ " type = %u)\n",
+ __FUNCTION__, addr, (uint64_t)size, kind);
+
+ WatchpointSP wp_sp;
+ if (!ProcessIsValid()) {
+ error.SetErrorString("process is not alive");
+ return wp_sp;
+ }
+
+ if (addr == LLDB_INVALID_ADDRESS || size == 0) {
+ if (size == 0)
+ error.SetErrorString("cannot set a watchpoint with watch_size of 0");
+ else
+ error.SetErrorStringWithFormat("invalid watch address: %" PRIu64, addr);
+ return wp_sp;
+ }
+
+ if (!LLDB_WATCH_TYPE_IS_VALID(kind)) {
+ error.SetErrorStringWithFormat("invalid watchpoint type: %d", kind);
+ }
+
+ if (!CheckIfWatchpointsSupported(this, error))
+ return wp_sp;
+
+ // Currently we only support one watchpoint per address, with total number of
+ // watchpoints limited by the hardware which the inferior is running on.
+
+ // Grab the list mutex while doing operations.
+ const bool notify = false; // Don't notify about all the state changes we do
+ // on creating the watchpoint.
+ std::unique_lock<std::recursive_mutex> lock;
+ this->GetWatchpointList().GetListMutex(lock);
+ WatchpointSP matched_sp = m_watchpoint_list.FindByAddress(addr);
+ if (matched_sp) {
+ size_t old_size = matched_sp->GetByteSize();
+ uint32_t old_type =
+ (matched_sp->WatchpointRead() ? LLDB_WATCH_TYPE_READ : 0) |
+ (matched_sp->WatchpointWrite() ? LLDB_WATCH_TYPE_WRITE : 0);
+ // Return the existing watchpoint if both size and type match.
+ if (size == old_size && kind == old_type) {
+ wp_sp = matched_sp;
+ wp_sp->SetEnabled(false, notify);
+ } else {
+ // Nil the matched watchpoint; we will be creating a new one.
+ m_process_sp->DisableWatchpoint(matched_sp.get(), notify);
+ m_watchpoint_list.Remove(matched_sp->GetID(), true);
+ }
+ }
+
+ if (!wp_sp) {
+ wp_sp = std::make_shared<Watchpoint>(*this, addr, size, type);
+ wp_sp->SetWatchpointType(kind, notify);
+ m_watchpoint_list.Add(wp_sp, true);
+ }
+
+ error = m_process_sp->EnableWatchpoint(wp_sp.get(), notify);
+ if (log)
+ log->Printf("Target::%s (creation of watchpoint %s with id = %u)\n",
+ __FUNCTION__, error.Success() ? "succeeded" : "failed",
+ wp_sp->GetID());
+
+ if (error.Fail()) {
+ // Enabling the watchpoint on the device side failed. Remove the said
+ // watchpoint from the list maintained by the target instance.
+ m_watchpoint_list.Remove(wp_sp->GetID(), true);
+ // See if we could provide more helpful error message.
+ if (!OptionGroupWatchpoint::IsWatchSizeSupported(size))
+ error.SetErrorStringWithFormat(
+ "watch size of %" PRIu64 " is not supported", (uint64_t)size);
+
+ wp_sp.reset();
+ } else
+ m_last_created_watchpoint = wp_sp;
+ return wp_sp;
+}
+
+void Target::RemoveAllowedBreakpoints ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Target::%s \n", __FUNCTION__);
+
+ m_breakpoint_list.RemoveAllowed(true);
+
+ m_last_created_breakpoint.reset();
+}
+
+void Target::RemoveAllBreakpoints(bool internal_also) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Target::%s (internal_also = %s)\n", __FUNCTION__,
+ internal_also ? "yes" : "no");
+
+ m_breakpoint_list.RemoveAll(true);
+ if (internal_also)
+ m_internal_breakpoint_list.RemoveAll(false);
+
+ m_last_created_breakpoint.reset();
+}
+
+void Target::DisableAllBreakpoints(bool internal_also) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Target::%s (internal_also = %s)\n", __FUNCTION__,
+ internal_also ? "yes" : "no");
+
+ m_breakpoint_list.SetEnabledAll(false);
+ if (internal_also)
+ m_internal_breakpoint_list.SetEnabledAll(false);
+}
+
+void Target::DisableAllowedBreakpoints() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Target::%s", __FUNCTION__);
+
+ m_breakpoint_list.SetEnabledAllowed(false);
+}
+
+void Target::EnableAllBreakpoints(bool internal_also) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Target::%s (internal_also = %s)\n", __FUNCTION__,
+ internal_also ? "yes" : "no");
+
+ m_breakpoint_list.SetEnabledAll(true);
+ if (internal_also)
+ m_internal_breakpoint_list.SetEnabledAll(true);
+}
+
+void Target::EnableAllowedBreakpoints() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Target::%s", __FUNCTION__);
+
+ m_breakpoint_list.SetEnabledAllowed(true);
+}
+
+bool Target::RemoveBreakpointByID(break_id_t break_id) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__,
+ break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no");
+
+ if (DisableBreakpointByID(break_id)) {
+ if (LLDB_BREAK_ID_IS_INTERNAL(break_id))
+ m_internal_breakpoint_list.Remove(break_id, false);
+ else {
+ if (m_last_created_breakpoint) {
+ if (m_last_created_breakpoint->GetID() == break_id)
+ m_last_created_breakpoint.reset();
+ }
+ m_breakpoint_list.Remove(break_id, true);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool Target::DisableBreakpointByID(break_id_t break_id) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__,
+ break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no");
+
+ BreakpointSP bp_sp;
+
+ if (LLDB_BREAK_ID_IS_INTERNAL(break_id))
+ bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id);
+ else
+ bp_sp = m_breakpoint_list.FindBreakpointByID(break_id);
+ if (bp_sp) {
+ bp_sp->SetEnabled(false);
+ return true;
+ }
+ return false;
+}
+
+bool Target::EnableBreakpointByID(break_id_t break_id) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__,
+ break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no");
+
+ BreakpointSP bp_sp;
+
+ if (LLDB_BREAK_ID_IS_INTERNAL(break_id))
+ bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id);
+ else
+ bp_sp = m_breakpoint_list.FindBreakpointByID(break_id);
+
+ if (bp_sp) {
+ bp_sp->SetEnabled(true);
+ return true;
+ }
+ return false;
+}
+
+Status Target::SerializeBreakpointsToFile(const FileSpec &file,
+ const BreakpointIDList &bp_ids,
+ bool append) {
+ Status error;
+
+ if (!file) {
+ error.SetErrorString("Invalid FileSpec.");
+ return error;
+ }
+
+ std::string path(file.GetPath());
+ StructuredData::ObjectSP input_data_sp;
+
+ StructuredData::ArraySP break_store_sp;
+ StructuredData::Array *break_store_ptr = nullptr;
+
+ if (append) {
+ input_data_sp = StructuredData::ParseJSONFromFile(file, error);
+ if (error.Success()) {
+ break_store_ptr = input_data_sp->GetAsArray();
+ if (!break_store_ptr) {
+ error.SetErrorStringWithFormat(
+ "Tried to append to invalid input file %s", path.c_str());
+ return error;
+ }
+ }
+ }
+
+ if (!break_store_ptr) {
+ break_store_sp = std::make_shared<StructuredData::Array>();
+ break_store_ptr = break_store_sp.get();
+ }
+
+ StreamFile out_file(path.c_str(),
+ File::OpenOptions::eOpenOptionTruncate |
+ File::OpenOptions::eOpenOptionWrite |
+ File::OpenOptions::eOpenOptionCanCreate |
+ File::OpenOptions::eOpenOptionCloseOnExec,
+ lldb::eFilePermissionsFileDefault);
+ if (!out_file.GetFile().IsValid()) {
+ error.SetErrorStringWithFormat("Unable to open output file: %s.",
+ path.c_str());
+ return error;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock;
+ GetBreakpointList().GetListMutex(lock);
+
+ if (bp_ids.GetSize() == 0) {
+ const BreakpointList &breakpoints = GetBreakpointList();
+
+ size_t num_breakpoints = breakpoints.GetSize();
+ for (size_t i = 0; i < num_breakpoints; i++) {
+ Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
+ StructuredData::ObjectSP bkpt_save_sp = bp->SerializeToStructuredData();
+ // If a breakpoint can't serialize it, just ignore it for now:
+ if (bkpt_save_sp)
+ break_store_ptr->AddItem(bkpt_save_sp);
+ }
+ } else {
+
+ std::unordered_set<lldb::break_id_t> processed_bkpts;
+ const size_t count = bp_ids.GetSize();
+ for (size_t i = 0; i < count; ++i) {
+ BreakpointID cur_bp_id = bp_ids.GetBreakpointIDAtIndex(i);
+ lldb::break_id_t bp_id = cur_bp_id.GetBreakpointID();
+
+ if (bp_id != LLDB_INVALID_BREAK_ID) {
+ // Only do each breakpoint once:
+ std::pair<std::unordered_set<lldb::break_id_t>::iterator, bool>
+ insert_result = processed_bkpts.insert(bp_id);
+ if (!insert_result.second)
+ continue;
+
+ Breakpoint *bp = GetBreakpointByID(bp_id).get();
+ StructuredData::ObjectSP bkpt_save_sp = bp->SerializeToStructuredData();
+ // If the user explicitly asked to serialize a breakpoint, and we
+ // can't, then raise an error:
+ if (!bkpt_save_sp) {
+ error.SetErrorStringWithFormat("Unable to serialize breakpoint %d",
+ bp_id);
+ return error;
+ }
+ break_store_ptr->AddItem(bkpt_save_sp);
+ }
+ }
+ }
+
+ break_store_ptr->Dump(out_file, false);
+ out_file.PutChar('\n');
+ return error;
+}
+
+Status Target::CreateBreakpointsFromFile(const FileSpec &file,
+ BreakpointIDList &new_bps) {
+ std::vector<std::string> no_names;
+ return CreateBreakpointsFromFile(file, no_names, new_bps);
+}
+
+Status Target::CreateBreakpointsFromFile(const FileSpec &file,
+ std::vector<std::string> &names,
+ BreakpointIDList &new_bps) {
+ std::unique_lock<std::recursive_mutex> lock;
+ GetBreakpointList().GetListMutex(lock);
+
+ Status error;
+ StructuredData::ObjectSP input_data_sp =
+ StructuredData::ParseJSONFromFile(file, error);
+ if (!error.Success()) {
+ return error;
+ } else if (!input_data_sp || !input_data_sp->IsValid()) {
+ error.SetErrorStringWithFormat("Invalid JSON from input file: %s.",
+ file.GetPath().c_str());
+ return error;
+ }
+
+ StructuredData::Array *bkpt_array = input_data_sp->GetAsArray();
+ if (!bkpt_array) {
+ error.SetErrorStringWithFormat(
+ "Invalid breakpoint data from input file: %s.", file.GetPath().c_str());
+ return error;
+ }
+
+ size_t num_bkpts = bkpt_array->GetSize();
+ size_t num_names = names.size();
+
+ for (size_t i = 0; i < num_bkpts; i++) {
+ StructuredData::ObjectSP bkpt_object_sp = bkpt_array->GetItemAtIndex(i);
+ // Peel off the breakpoint key, and feed the rest to the Breakpoint:
+ StructuredData::Dictionary *bkpt_dict = bkpt_object_sp->GetAsDictionary();
+ if (!bkpt_dict) {
+ error.SetErrorStringWithFormat(
+ "Invalid breakpoint data for element %zu from input file: %s.", i,
+ file.GetPath().c_str());
+ return error;
+ }
+ StructuredData::ObjectSP bkpt_data_sp =
+ bkpt_dict->GetValueForKey(Breakpoint::GetSerializationKey());
+ if (num_names &&
+ !Breakpoint::SerializedBreakpointMatchesNames(bkpt_data_sp, names))
+ continue;
+
+ BreakpointSP bkpt_sp =
+ Breakpoint::CreateFromStructuredData(*this, bkpt_data_sp, error);
+ if (!error.Success()) {
+ error.SetErrorStringWithFormat(
+ "Error restoring breakpoint %zu from %s: %s.", i,
+ file.GetPath().c_str(), error.AsCString());
+ return error;
+ }
+ new_bps.AddBreakpointID(BreakpointID(bkpt_sp->GetID()));
+ }
+ return error;
+}
+
+// The flag 'end_to_end', default to true, signifies that the operation is
+// performed end to end, for both the debugger and the debuggee.
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list for end
+// to end operations.
+bool Target::RemoveAllWatchpoints(bool end_to_end) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s\n", __FUNCTION__);
+
+ if (!end_to_end) {
+ m_watchpoint_list.RemoveAll(true);
+ return true;
+ }
+
+ // Otherwise, it's an end to end operation.
+
+ if (!ProcessIsValid())
+ return false;
+
+ size_t num_watchpoints = m_watchpoint_list.GetSize();
+ for (size_t i = 0; i < num_watchpoints; ++i) {
+ WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i);
+ if (!wp_sp)
+ return false;
+
+ Status rc = m_process_sp->DisableWatchpoint(wp_sp.get());
+ if (rc.Fail())
+ return false;
+ }
+ m_watchpoint_list.RemoveAll(true);
+ m_last_created_watchpoint.reset();
+ return true; // Success!
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list for end
+// to end operations.
+bool Target::DisableAllWatchpoints(bool end_to_end) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s\n", __FUNCTION__);
+
+ if (!end_to_end) {
+ m_watchpoint_list.SetEnabledAll(false);
+ return true;
+ }
+
+ // Otherwise, it's an end to end operation.
+
+ if (!ProcessIsValid())
+ return false;
+
+ size_t num_watchpoints = m_watchpoint_list.GetSize();
+ for (size_t i = 0; i < num_watchpoints; ++i) {
+ WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i);
+ if (!wp_sp)
+ return false;
+
+ Status rc = m_process_sp->DisableWatchpoint(wp_sp.get());
+ if (rc.Fail())
+ return false;
+ }
+ return true; // Success!
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list for end
+// to end operations.
+bool Target::EnableAllWatchpoints(bool end_to_end) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s\n", __FUNCTION__);
+
+ if (!end_to_end) {
+ m_watchpoint_list.SetEnabledAll(true);
+ return true;
+ }
+
+ // Otherwise, it's an end to end operation.
+
+ if (!ProcessIsValid())
+ return false;
+
+ size_t num_watchpoints = m_watchpoint_list.GetSize();
+ for (size_t i = 0; i < num_watchpoints; ++i) {
+ WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i);
+ if (!wp_sp)
+ return false;
+
+ Status rc = m_process_sp->EnableWatchpoint(wp_sp.get());
+ if (rc.Fail())
+ return false;
+ }
+ return true; // Success!
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list.
+bool Target::ClearAllWatchpointHitCounts() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s\n", __FUNCTION__);
+
+ size_t num_watchpoints = m_watchpoint_list.GetSize();
+ for (size_t i = 0; i < num_watchpoints; ++i) {
+ WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i);
+ if (!wp_sp)
+ return false;
+
+ wp_sp->ResetHitCount();
+ }
+ return true; // Success!
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list.
+bool Target::ClearAllWatchpointHistoricValues() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s\n", __FUNCTION__);
+
+ size_t num_watchpoints = m_watchpoint_list.GetSize();
+ for (size_t i = 0; i < num_watchpoints; ++i) {
+ WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i);
+ if (!wp_sp)
+ return false;
+
+ wp_sp->ResetHistoricValues();
+ }
+ return true; // Success!
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list during
+// these operations.
+bool Target::IgnoreAllWatchpoints(uint32_t ignore_count) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s\n", __FUNCTION__);
+
+ if (!ProcessIsValid())
+ return false;
+
+ size_t num_watchpoints = m_watchpoint_list.GetSize();
+ for (size_t i = 0; i < num_watchpoints; ++i) {
+ WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i);
+ if (!wp_sp)
+ return false;
+
+ wp_sp->SetIgnoreCount(ignore_count);
+ }
+ return true; // Success!
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list.
+bool Target::DisableWatchpointByID(lldb::watch_id_t watch_id) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id);
+
+ if (!ProcessIsValid())
+ return false;
+
+ WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id);
+ if (wp_sp) {
+ Status rc = m_process_sp->DisableWatchpoint(wp_sp.get());
+ if (rc.Success())
+ return true;
+
+ // Else, fallthrough.
+ }
+ return false;
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list.
+bool Target::EnableWatchpointByID(lldb::watch_id_t watch_id) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id);
+
+ if (!ProcessIsValid())
+ return false;
+
+ WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id);
+ if (wp_sp) {
+ Status rc = m_process_sp->EnableWatchpoint(wp_sp.get());
+ if (rc.Success())
+ return true;
+
+ // Else, fallthrough.
+ }
+ return false;
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list.
+bool Target::RemoveWatchpointByID(lldb::watch_id_t watch_id) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id);
+
+ WatchpointSP watch_to_remove_sp = m_watchpoint_list.FindByID(watch_id);
+ if (watch_to_remove_sp == m_last_created_watchpoint)
+ m_last_created_watchpoint.reset();
+
+ if (DisableWatchpointByID(watch_id)) {
+ m_watchpoint_list.Remove(watch_id, true);
+ return true;
+ }
+ return false;
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list.
+bool Target::IgnoreWatchpointByID(lldb::watch_id_t watch_id,
+ uint32_t ignore_count) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id);
+
+ if (!ProcessIsValid())
+ return false;
+
+ WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id);
+ if (wp_sp) {
+ wp_sp->SetIgnoreCount(ignore_count);
+ return true;
+ }
+ return false;
+}
+
+ModuleSP Target::GetExecutableModule() {
+ // search for the first executable in the module list
+ for (size_t i = 0; i < m_images.GetSize(); ++i) {
+ ModuleSP module_sp = m_images.GetModuleAtIndex(i);
+ lldb_private::ObjectFile *obj = module_sp->GetObjectFile();
+ if (obj == nullptr)
+ continue;
+ if (obj->GetType() == ObjectFile::Type::eTypeExecutable)
+ return module_sp;
+ }
+ // as fall back return the first module loaded
+ return m_images.GetModuleAtIndex(0);
+}
+
+Module *Target::GetExecutableModulePointer() {
+ return GetExecutableModule().get();
+}
+
+static void LoadScriptingResourceForModule(const ModuleSP &module_sp,
+ Target *target) {
+ Status error;
+ StreamString feedback_stream;
+ if (module_sp &&
+ !module_sp->LoadScriptingResourceInTarget(target, error,
+ &feedback_stream)) {
+ if (error.AsCString())
+ target->GetDebugger().GetErrorFile()->Printf(
+ "unable to load scripting data for module %s - error reported was "
+ "%s\n",
+ module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(),
+ error.AsCString());
+ }
+ if (feedback_stream.GetSize())
+ target->GetDebugger().GetErrorFile()->Printf("%s\n",
+ feedback_stream.GetData());
+}
+
+void Target::ClearModules(bool delete_locations) {
+ ModulesDidUnload(m_images, delete_locations);
+ m_section_load_history.Clear();
+ m_images.Clear();
+ m_scratch_type_system_map.Clear();
+ m_ast_importer_sp.reset();
+}
+
+void Target::DidExec() {
+ // When a process exec's we need to know about it so we can do some cleanup.
+ m_breakpoint_list.RemoveInvalidLocations(m_arch.GetSpec());
+ m_internal_breakpoint_list.RemoveInvalidLocations(m_arch.GetSpec());
+}
+
+void Target::SetExecutableModule(ModuleSP &executable_sp,
+ LoadDependentFiles load_dependent_files) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET));
+ ClearModules(false);
+
+ if (executable_sp) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "Target::SetExecutableModule (executable = '%s')",
+ executable_sp->GetFileSpec().GetPath().c_str());
+
+ const bool notify = true;
+ m_images.Append(executable_sp, notify); // The first image is our executable file
+
+ // If we haven't set an architecture yet, reset our architecture based on
+ // what we found in the executable module.
+ if (!m_arch.GetSpec().IsValid()) {
+ m_arch = executable_sp->GetArchitecture();
+ LLDB_LOG(log,
+ "setting architecture to {0} ({1}) based on executable file",
+ m_arch.GetSpec().GetArchitectureName(),
+ m_arch.GetSpec().GetTriple().getTriple());
+ }
+
+ FileSpecList dependent_files;
+ ObjectFile *executable_objfile = executable_sp->GetObjectFile();
+ bool load_dependents = true;
+ switch (load_dependent_files) {
+ case eLoadDependentsDefault:
+ load_dependents = executable_sp->IsExecutable();
+ break;
+ case eLoadDependentsYes:
+ load_dependents = true;
+ break;
+ case eLoadDependentsNo:
+ load_dependents = false;
+ break;
+ }
+
+ if (executable_objfile && load_dependents) {
+ ModuleList added_modules;
+ executable_objfile->GetDependentModules(dependent_files);
+ for (uint32_t i = 0; i < dependent_files.GetSize(); i++) {
+ FileSpec dependent_file_spec(
+ dependent_files.GetFileSpecPointerAtIndex(i));
+ FileSpec platform_dependent_file_spec;
+ if (m_platform_sp)
+ m_platform_sp->GetFileWithUUID(dependent_file_spec, nullptr,
+ platform_dependent_file_spec);
+ else
+ platform_dependent_file_spec = dependent_file_spec;
+
+ ModuleSpec module_spec(platform_dependent_file_spec, m_arch.GetSpec());
+ ModuleSP image_module_sp(GetOrCreateModule(module_spec,
+ false /* notify */));
+ if (image_module_sp) {
+ added_modules.AppendIfNeeded (image_module_sp, false);
+ ObjectFile *objfile = image_module_sp->GetObjectFile();
+ if (objfile)
+ objfile->GetDependentModules(dependent_files);
+ }
+ }
+ ModulesDidLoad(added_modules);
+ }
+ }
+}
+
+bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET));
+ bool missing_local_arch = !m_arch.GetSpec().IsValid();
+ bool replace_local_arch = true;
+ bool compatible_local_arch = false;
+ ArchSpec other(arch_spec);
+
+ // Changing the architecture might mean that the currently selected platform
+ // isn't compatible. Set the platform correctly if we are asked to do so,
+ // otherwise assume the user will set the platform manually.
+ if (set_platform) {
+ if (other.IsValid()) {
+ auto platform_sp = GetPlatform();
+ if (!platform_sp ||
+ !platform_sp->IsCompatibleArchitecture(other, false, nullptr)) {
+ ArchSpec platform_arch;
+ auto arch_platform_sp =
+ Platform::GetPlatformForArchitecture(other, &platform_arch);
+ if (arch_platform_sp) {
+ SetPlatform(arch_platform_sp);
+ if (platform_arch.IsValid())
+ other = platform_arch;
+ }
+ }
+ }
+ }
+
+ if (!missing_local_arch) {
+ if (m_arch.GetSpec().IsCompatibleMatch(arch_spec)) {
+ other.MergeFrom(m_arch.GetSpec());
+
+ if (m_arch.GetSpec().IsCompatibleMatch(other)) {
+ compatible_local_arch = true;
+ bool arch_changed, vendor_changed, os_changed, os_ver_changed,
+ env_changed;
+
+ m_arch.GetSpec().PiecewiseTripleCompare(other, arch_changed, vendor_changed,
+ os_changed, os_ver_changed, env_changed);
+
+ if (!arch_changed && !vendor_changed && !os_changed && !env_changed)
+ replace_local_arch = false;
+ }
+ }
+ }
+
+ if (compatible_local_arch || missing_local_arch) {
+ // If we haven't got a valid arch spec, or the architectures are compatible
+ // update the architecture, unless the one we already have is more
+ // specified
+ if (replace_local_arch)
+ m_arch = other;
+ LLDB_LOG(log, "set architecture to {0} ({1})",
+ m_arch.GetSpec().GetArchitectureName(),
+ m_arch.GetSpec().GetTriple().getTriple());
+ return true;
+ }
+
+ // If we have an executable file, try to reset the executable to the desired
+ // architecture
+ if (log)
+ log->Printf("Target::SetArchitecture changing architecture to %s (%s)",
+ arch_spec.GetArchitectureName(),
+ arch_spec.GetTriple().getTriple().c_str());
+ m_arch = other;
+ ModuleSP executable_sp = GetExecutableModule();
+
+ ClearModules(true);
+ // Need to do something about unsetting breakpoints.
+
+ if (executable_sp) {
+ if (log)
+ log->Printf("Target::SetArchitecture Trying to select executable file "
+ "architecture %s (%s)",
+ arch_spec.GetArchitectureName(),
+ arch_spec.GetTriple().getTriple().c_str());
+ ModuleSpec module_spec(executable_sp->GetFileSpec(), other);
+ FileSpecList search_paths = GetExecutableSearchPaths();
+ Status error = ModuleList::GetSharedModule(module_spec, executable_sp,
+ &search_paths,
+ nullptr, nullptr);
+
+ if (!error.Fail() && executable_sp) {
+ SetExecutableModule(executable_sp, eLoadDependentsYes);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Target::MergeArchitecture(const ArchSpec &arch_spec) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET));
+ if (arch_spec.IsValid()) {
+ if (m_arch.GetSpec().IsCompatibleMatch(arch_spec)) {
+ // The current target arch is compatible with "arch_spec", see if we can
+ // improve our current architecture using bits from "arch_spec"
+
+ if (log)
+ log->Printf("Target::MergeArchitecture target has arch %s, merging with "
+ "arch %s",
+ m_arch.GetSpec().GetTriple().getTriple().c_str(),
+ arch_spec.GetTriple().getTriple().c_str());
+
+ // Merge bits from arch_spec into "merged_arch" and set our architecture
+ ArchSpec merged_arch(m_arch.GetSpec());
+ merged_arch.MergeFrom(arch_spec);
+ return SetArchitecture(merged_arch);
+ } else {
+ // The new architecture is different, we just need to replace it
+ return SetArchitecture(arch_spec);
+ }
+ }
+ return false;
+}
+
+void Target::NotifyWillClearList(const ModuleList &module_list) {}
+
+void Target::NotifyModuleAdded(const ModuleList &module_list,
+ const ModuleSP &module_sp) {
+ // A module is being added to this target for the first time
+ if (m_valid) {
+ ModuleList my_module_list;
+ my_module_list.Append(module_sp);
+ ModulesDidLoad(my_module_list);
+ }
+}
+
+void Target::NotifyModuleRemoved(const ModuleList &module_list,
+ const ModuleSP &module_sp) {
+ // A module is being removed from this target.
+ if (m_valid) {
+ ModuleList my_module_list;
+ my_module_list.Append(module_sp);
+ ModulesDidUnload(my_module_list, false);
+ }
+}
+
+void Target::NotifyModuleUpdated(const ModuleList &module_list,
+ const ModuleSP &old_module_sp,
+ const ModuleSP &new_module_sp) {
+ // A module is replacing an already added module
+ if (m_valid) {
+ m_breakpoint_list.UpdateBreakpointsWhenModuleIsReplaced(old_module_sp,
+ new_module_sp);
+ m_internal_breakpoint_list.UpdateBreakpointsWhenModuleIsReplaced(
+ old_module_sp, new_module_sp);
+ }
+}
+
+void Target::NotifyModulesRemoved(lldb_private::ModuleList &module_list) {
+ ModulesDidUnload (module_list, false);
+}
+
+
+void Target::ModulesDidLoad(ModuleList &module_list) {
+ const size_t num_images = module_list.GetSize();
+ if (m_valid && num_images) {
+ for (size_t idx = 0; idx < num_images; ++idx) {
+ ModuleSP module_sp(module_list.GetModuleAtIndex(idx));
+ LoadScriptingResourceForModule(module_sp, this);
+ }
+ m_breakpoint_list.UpdateBreakpoints(module_list, true, false);
+ m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false);
+ if (m_process_sp) {
+ m_process_sp->ModulesDidLoad(module_list);
+ }
+ BroadcastEvent(eBroadcastBitModulesLoaded,
+ new TargetEventData(this->shared_from_this(), module_list));
+ }
+}
+
+void Target::SymbolsDidLoad(ModuleList &module_list) {
+ if (m_valid && module_list.GetSize()) {
+ if (m_process_sp) {
+ for (LanguageRuntime *runtime : m_process_sp->GetLanguageRuntimes()) {
+ runtime->SymbolsDidLoad(module_list);
+ }
+ }
+
+ m_breakpoint_list.UpdateBreakpoints(module_list, true, false);
+ m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false);
+ BroadcastEvent(eBroadcastBitSymbolsLoaded,
+ new TargetEventData(this->shared_from_this(), module_list));
+ }
+}
+
+void Target::ModulesDidUnload(ModuleList &module_list, bool delete_locations) {
+ if (m_valid && module_list.GetSize()) {
+ UnloadModuleSections(module_list);
+ m_breakpoint_list.UpdateBreakpoints(module_list, false, delete_locations);
+ m_internal_breakpoint_list.UpdateBreakpoints(module_list, false,
+ delete_locations);
+ BroadcastEvent(eBroadcastBitModulesUnloaded,
+ new TargetEventData(this->shared_from_this(), module_list));
+ }
+}
+
+bool Target::ModuleIsExcludedForUnconstrainedSearches(
+ const FileSpec &module_file_spec) {
+ if (GetBreakpointsConsultPlatformAvoidList()) {
+ ModuleList matchingModules;
+ ModuleSpec module_spec(module_file_spec);
+ size_t num_modules = GetImages().FindModules(module_spec, matchingModules);
+
+ // If there is more than one module for this file spec, only return true if
+ // ALL the modules are on the
+ // black list.
+ if (num_modules > 0) {
+ for (size_t i = 0; i < num_modules; i++) {
+ if (!ModuleIsExcludedForUnconstrainedSearches(
+ matchingModules.GetModuleAtIndex(i)))
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Target::ModuleIsExcludedForUnconstrainedSearches(
+ const lldb::ModuleSP &module_sp) {
+ if (GetBreakpointsConsultPlatformAvoidList()) {
+ if (m_platform_sp)
+ return m_platform_sp->ModuleIsExcludedForUnconstrainedSearches(*this,
+ module_sp);
+ }
+ return false;
+}
+
+size_t Target::ReadMemoryFromFileCache(const Address &addr, void *dst,
+ size_t dst_len, Status &error) {
+ SectionSP section_sp(addr.GetSection());
+ if (section_sp) {
+ // If the contents of this section are encrypted, the on-disk file is
+ // unusable. Read only from live memory.
+ if (section_sp->IsEncrypted()) {
+ error.SetErrorString("section is encrypted");
+ return 0;
+ }
+ ModuleSP module_sp(section_sp->GetModule());
+ if (module_sp) {
+ ObjectFile *objfile = section_sp->GetModule()->GetObjectFile();
+ if (objfile) {
+ size_t bytes_read = objfile->ReadSectionData(
+ section_sp.get(), addr.GetOffset(), dst, dst_len);
+ if (bytes_read > 0)
+ return bytes_read;
+ else
+ error.SetErrorStringWithFormat("error reading data from section %s",
+ section_sp->GetName().GetCString());
+ } else
+ error.SetErrorString("address isn't from a object file");
+ } else
+ error.SetErrorString("address isn't in a module");
+ } else
+ error.SetErrorString("address doesn't contain a section that points to a "
+ "section in a object file");
+
+ return 0;
+}
+
+size_t Target::ReadMemory(const Address &addr, bool prefer_file_cache,
+ void *dst, size_t dst_len, Status &error,
+ lldb::addr_t *load_addr_ptr) {
+ error.Clear();
+
+ // if we end up reading this from process memory, we will fill this with the
+ // actual load address
+ if (load_addr_ptr)
+ *load_addr_ptr = LLDB_INVALID_ADDRESS;
+
+ size_t bytes_read = 0;
+
+ addr_t load_addr = LLDB_INVALID_ADDRESS;
+ addr_t file_addr = LLDB_INVALID_ADDRESS;
+ Address resolved_addr;
+ if (!addr.IsSectionOffset()) {
+ SectionLoadList &section_load_list = GetSectionLoadList();
+ if (section_load_list.IsEmpty()) {
+ // No sections are loaded, so we must assume we are not running yet and
+ // anything we are given is a file address.
+ file_addr = addr.GetOffset(); // "addr" doesn't have a section, so its
+ // offset is the file address
+ m_images.ResolveFileAddress(file_addr, resolved_addr);
+ } else {
+ // We have at least one section loaded. This can be because we have
+ // manually loaded some sections with "target modules load ..." or
+ // because we have have a live process that has sections loaded through
+ // the dynamic loader
+ load_addr = addr.GetOffset(); // "addr" doesn't have a section, so its
+ // offset is the load address
+ section_load_list.ResolveLoadAddress(load_addr, resolved_addr);
+ }
+ }
+ if (!resolved_addr.IsValid())
+ resolved_addr = addr;
+
+ if (prefer_file_cache) {
+ bytes_read = ReadMemoryFromFileCache(resolved_addr, dst, dst_len, error);
+ if (bytes_read > 0)
+ return bytes_read;
+ }
+
+ if (ProcessIsValid()) {
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ load_addr = resolved_addr.GetLoadAddress(this);
+
+ if (load_addr == LLDB_INVALID_ADDRESS) {
+ ModuleSP addr_module_sp(resolved_addr.GetModule());
+ if (addr_module_sp && addr_module_sp->GetFileSpec())
+ error.SetErrorStringWithFormatv(
+ "{0:F}[{1:x+}] can't be resolved, {0:F} is not currently loaded",
+ addr_module_sp->GetFileSpec(), resolved_addr.GetFileAddress());
+ else
+ error.SetErrorStringWithFormat("0x%" PRIx64 " can't be resolved",
+ resolved_addr.GetFileAddress());
+ } else {
+ bytes_read = m_process_sp->ReadMemory(load_addr, dst, dst_len, error);
+ if (bytes_read != dst_len) {
+ if (error.Success()) {
+ if (bytes_read == 0)
+ error.SetErrorStringWithFormat(
+ "read memory from 0x%" PRIx64 " failed", load_addr);
+ else
+ error.SetErrorStringWithFormat(
+ "only %" PRIu64 " of %" PRIu64
+ " bytes were read from memory at 0x%" PRIx64,
+ (uint64_t)bytes_read, (uint64_t)dst_len, load_addr);
+ }
+ }
+ if (bytes_read) {
+ if (load_addr_ptr)
+ *load_addr_ptr = load_addr;
+ return bytes_read;
+ }
+ // If the address is not section offset we have an address that doesn't
+ // resolve to any address in any currently loaded shared libraries and we
+ // failed to read memory so there isn't anything more we can do. If it is
+ // section offset, we might be able to read cached memory from the object
+ // file.
+ if (!resolved_addr.IsSectionOffset())
+ return 0;
+ }
+ }
+
+ if (!prefer_file_cache && resolved_addr.IsSectionOffset()) {
+ // If we didn't already try and read from the object file cache, then try
+ // it after failing to read from the process.
+ return ReadMemoryFromFileCache(resolved_addr, dst, dst_len, error);
+ }
+ return 0;
+}
+
+size_t Target::ReadCStringFromMemory(const Address &addr, std::string &out_str,
+ Status &error) {
+ char buf[256];
+ out_str.clear();
+ addr_t curr_addr = addr.GetLoadAddress(this);
+ Address address(addr);
+ while (true) {
+ size_t length = ReadCStringFromMemory(address, buf, sizeof(buf), error);
+ if (length == 0)
+ break;
+ out_str.append(buf, length);
+ // If we got "length - 1" bytes, we didn't get the whole C string, we need
+ // to read some more characters
+ if (length == sizeof(buf) - 1)
+ curr_addr += length;
+ else
+ break;
+ address = Address(curr_addr);
+ }
+ return out_str.size();
+}
+
+size_t Target::ReadCStringFromMemory(const Address &addr, char *dst,
+ size_t dst_max_len, Status &result_error) {
+ size_t total_cstr_len = 0;
+ if (dst && dst_max_len) {
+ result_error.Clear();
+ // NULL out everything just to be safe
+ memset(dst, 0, dst_max_len);
+ Status error;
+ addr_t curr_addr = addr.GetLoadAddress(this);
+ Address address(addr);
+
+ // We could call m_process_sp->GetMemoryCacheLineSize() but I don't think
+ // this really needs to be tied to the memory cache subsystem's cache line
+ // size, so leave this as a fixed constant.
+ const size_t cache_line_size = 512;
+
+ size_t bytes_left = dst_max_len - 1;
+ char *curr_dst = dst;
+
+ while (bytes_left > 0) {
+ addr_t cache_line_bytes_left =
+ cache_line_size - (curr_addr % cache_line_size);
+ addr_t bytes_to_read =
+ std::min<addr_t>(bytes_left, cache_line_bytes_left);
+ size_t bytes_read =
+ ReadMemory(address, false, curr_dst, bytes_to_read, error);
+
+ if (bytes_read == 0) {
+ result_error = error;
+ dst[total_cstr_len] = '\0';
+ break;
+ }
+ const size_t len = strlen(curr_dst);
+
+ total_cstr_len += len;
+
+ if (len < bytes_to_read)
+ break;
+
+ curr_dst += bytes_read;
+ curr_addr += bytes_read;
+ bytes_left -= bytes_read;
+ address = Address(curr_addr);
+ }
+ } else {
+ if (dst == nullptr)
+ result_error.SetErrorString("invalid arguments");
+ else
+ result_error.Clear();
+ }
+ return total_cstr_len;
+}
+
+size_t Target::ReadScalarIntegerFromMemory(const Address &addr,
+ bool prefer_file_cache,
+ uint32_t byte_size, bool is_signed,
+ Scalar &scalar, Status &error) {
+ uint64_t uval;
+
+ if (byte_size <= sizeof(uval)) {
+ size_t bytes_read =
+ ReadMemory(addr, prefer_file_cache, &uval, byte_size, error);
+ if (bytes_read == byte_size) {
+ DataExtractor data(&uval, sizeof(uval), m_arch.GetSpec().GetByteOrder(),
+ m_arch.GetSpec().GetAddressByteSize());
+ lldb::offset_t offset = 0;
+ if (byte_size <= 4)
+ scalar = data.GetMaxU32(&offset, byte_size);
+ else
+ scalar = data.GetMaxU64(&offset, byte_size);
+
+ if (is_signed)
+ scalar.SignExtend(byte_size * 8);
+ return bytes_read;
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "byte size of %u is too large for integer scalar type", byte_size);
+ }
+ return 0;
+}
+
+uint64_t Target::ReadUnsignedIntegerFromMemory(const Address &addr,
+ bool prefer_file_cache,
+ size_t integer_byte_size,
+ uint64_t fail_value,
+ Status &error) {
+ Scalar scalar;
+ if (ReadScalarIntegerFromMemory(addr, prefer_file_cache, integer_byte_size,
+ false, scalar, error))
+ return scalar.ULongLong(fail_value);
+ return fail_value;
+}
+
+bool Target::ReadPointerFromMemory(const Address &addr, bool prefer_file_cache,
+ Status &error, Address &pointer_addr) {
+ Scalar scalar;
+ if (ReadScalarIntegerFromMemory(addr, prefer_file_cache,
+ m_arch.GetSpec().GetAddressByteSize(), false, scalar,
+ error)) {
+ addr_t pointer_vm_addr = scalar.ULongLong(LLDB_INVALID_ADDRESS);
+ if (pointer_vm_addr != LLDB_INVALID_ADDRESS) {
+ SectionLoadList &section_load_list = GetSectionLoadList();
+ if (section_load_list.IsEmpty()) {
+ // No sections are loaded, so we must assume we are not running yet and
+ // anything we are given is a file address.
+ m_images.ResolveFileAddress(pointer_vm_addr, pointer_addr);
+ } else {
+ // We have at least one section loaded. This can be because we have
+ // manually loaded some sections with "target modules load ..." or
+ // because we have have a live process that has sections loaded through
+ // the dynamic loader
+ section_load_list.ResolveLoadAddress(pointer_vm_addr, pointer_addr);
+ }
+ // We weren't able to resolve the pointer value, so just return an
+ // address with no section
+ if (!pointer_addr.IsValid())
+ pointer_addr.SetOffset(pointer_vm_addr);
+ return true;
+ }
+ }
+ return false;
+}
+
+ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify,
+ Status *error_ptr) {
+ ModuleSP module_sp;
+
+ Status error;
+
+ // First see if we already have this module in our module list. If we do,
+ // then we're done, we don't need to consult the shared modules list. But
+ // only do this if we are passed a UUID.
+
+ if (module_spec.GetUUID().IsValid())
+ module_sp = m_images.FindFirstModule(module_spec);
+
+ if (!module_sp) {
+ ModuleSP old_module_sp; // This will get filled in if we have a new version
+ // of the library
+ bool did_create_module = false;
+ FileSpecList search_paths = GetExecutableSearchPaths();
+ // If there are image search path entries, try to use them first to acquire
+ // a suitable image.
+ if (m_image_search_paths.GetSize()) {
+ ModuleSpec transformed_spec(module_spec);
+ if (m_image_search_paths.RemapPath(
+ module_spec.GetFileSpec().GetDirectory(),
+ transformed_spec.GetFileSpec().GetDirectory())) {
+ transformed_spec.GetFileSpec().GetFilename() =
+ module_spec.GetFileSpec().GetFilename();
+ error = ModuleList::GetSharedModule(transformed_spec, module_sp,
+ &search_paths,
+ &old_module_sp, &did_create_module);
+ }
+ }
+
+ if (!module_sp) {
+ // If we have a UUID, we can check our global shared module list in case
+ // we already have it. If we don't have a valid UUID, then we can't since
+ // the path in "module_spec" will be a platform path, and we will need to
+ // let the platform find that file. For example, we could be asking for
+ // "/usr/lib/dyld" and if we do not have a UUID, we don't want to pick
+ // the local copy of "/usr/lib/dyld" since our platform could be a remote
+ // platform that has its own "/usr/lib/dyld" in an SDK or in a local file
+ // cache.
+ if (module_spec.GetUUID().IsValid()) {
+ // We have a UUID, it is OK to check the global module list...
+ error = ModuleList::GetSharedModule(module_spec, module_sp,
+ &search_paths,
+ &old_module_sp, &did_create_module);
+ }
+
+ if (!module_sp) {
+ // The platform is responsible for finding and caching an appropriate
+ // module in the shared module cache.
+ if (m_platform_sp) {
+ error = m_platform_sp->GetSharedModule(
+ module_spec, m_process_sp.get(), module_sp,
+ &search_paths, &old_module_sp, &did_create_module);
+ } else {
+ error.SetErrorString("no platform is currently set");
+ }
+ }
+ }
+
+ // We found a module that wasn't in our target list. Let's make sure that
+ // there wasn't an equivalent module in the list already, and if there was,
+ // let's remove it.
+ if (module_sp) {
+ ObjectFile *objfile = module_sp->GetObjectFile();
+ if (objfile) {
+ switch (objfile->GetType()) {
+ case ObjectFile::eTypeCoreFile: /// A core file that has a checkpoint of
+ /// a program's execution state
+ case ObjectFile::eTypeExecutable: /// A normal executable
+ case ObjectFile::eTypeDynamicLinker: /// The platform's dynamic linker
+ /// executable
+ case ObjectFile::eTypeObjectFile: /// An intermediate object file
+ case ObjectFile::eTypeSharedLibrary: /// A shared library that can be
+ /// used during execution
+ break;
+ case ObjectFile::eTypeDebugInfo: /// An object file that contains only
+ /// debug information
+ if (error_ptr)
+ error_ptr->SetErrorString("debug info files aren't valid target "
+ "modules, please specify an executable");
+ return ModuleSP();
+ case ObjectFile::eTypeStubLibrary: /// A library that can be linked
+ /// against but not used for
+ /// execution
+ if (error_ptr)
+ error_ptr->SetErrorString("stub libraries aren't valid target "
+ "modules, please specify an executable");
+ return ModuleSP();
+ default:
+ if (error_ptr)
+ error_ptr->SetErrorString(
+ "unsupported file type, please specify an executable");
+ return ModuleSP();
+ }
+ // GetSharedModule is not guaranteed to find the old shared module, for
+ // instance in the common case where you pass in the UUID, it is only
+ // going to find the one module matching the UUID. In fact, it has no
+ // good way to know what the "old module" relevant to this target is,
+ // since there might be many copies of a module with this file spec in
+ // various running debug sessions, but only one of them will belong to
+ // this target. So let's remove the UUID from the module list, and look
+ // in the target's module list. Only do this if there is SOMETHING else
+ // in the module spec...
+ if (!old_module_sp) {
+ if (module_spec.GetUUID().IsValid() &&
+ !module_spec.GetFileSpec().GetFilename().IsEmpty() &&
+ !module_spec.GetFileSpec().GetDirectory().IsEmpty()) {
+ ModuleSpec module_spec_copy(module_spec.GetFileSpec());
+ module_spec_copy.GetUUID().Clear();
+
+ ModuleList found_modules;
+ size_t num_found =
+ m_images.FindModules(module_spec_copy, found_modules);
+ if (num_found == 1) {
+ old_module_sp = found_modules.GetModuleAtIndex(0);
+ }
+ }
+ }
+
+ // Preload symbols outside of any lock, so hopefully we can do this for
+ // each library in parallel.
+ if (GetPreloadSymbols())
+ module_sp->PreloadSymbols();
+
+ if (old_module_sp &&
+ m_images.GetIndexForModule(old_module_sp.get()) !=
+ LLDB_INVALID_INDEX32) {
+ m_images.ReplaceModule(old_module_sp, module_sp);
+ Module *old_module_ptr = old_module_sp.get();
+ old_module_sp.reset();
+ ModuleList::RemoveSharedModuleIfOrphaned(old_module_ptr);
+ } else {
+ m_images.Append(module_sp, notify);
+ }
+ } else
+ module_sp.reset();
+ }
+ }
+ if (error_ptr)
+ *error_ptr = error;
+ return module_sp;
+}
+
+TargetSP Target::CalculateTarget() { return shared_from_this(); }
+
+ProcessSP Target::CalculateProcess() { return m_process_sp; }
+
+ThreadSP Target::CalculateThread() { return ThreadSP(); }
+
+StackFrameSP Target::CalculateStackFrame() { return StackFrameSP(); }
+
+void Target::CalculateExecutionContext(ExecutionContext &exe_ctx) {
+ exe_ctx.Clear();
+ exe_ctx.SetTargetPtr(this);
+}
+
+PathMappingList &Target::GetImageSearchPathList() {
+ return m_image_search_paths;
+}
+
+void Target::ImageSearchPathsChanged(const PathMappingList &path_list,
+ void *baton) {
+ Target *target = (Target *)baton;
+ ModuleSP exe_module_sp(target->GetExecutableModule());
+ if (exe_module_sp)
+ target->SetExecutableModule(exe_module_sp, eLoadDependentsYes);
+}
+
+TypeSystem *Target::GetScratchTypeSystemForLanguage(Status *error,
+ lldb::LanguageType language,
+ bool create_on_demand) {
+ if (!m_valid)
+ return nullptr;
+
+ if (error) {
+ error->Clear();
+ }
+
+ if (language == eLanguageTypeMipsAssembler // GNU AS and LLVM use it for all
+ // assembly code
+ || language == eLanguageTypeUnknown) {
+ std::set<lldb::LanguageType> languages_for_types;
+ std::set<lldb::LanguageType> languages_for_expressions;
+
+ Language::GetLanguagesSupportingTypeSystems(languages_for_types,
+ languages_for_expressions);
+
+ if (languages_for_expressions.count(eLanguageTypeC)) {
+ language = eLanguageTypeC; // LLDB's default. Override by setting the
+ // target language.
+ } else {
+ if (languages_for_expressions.empty()) {
+ return nullptr;
+ } else {
+ language = *languages_for_expressions.begin();
+ }
+ }
+ }
+
+ return m_scratch_type_system_map.GetTypeSystemForLanguage(language, this,
+ create_on_demand);
+}
+
+PersistentExpressionState *
+Target::GetPersistentExpressionStateForLanguage(lldb::LanguageType language) {
+ TypeSystem *type_system =
+ GetScratchTypeSystemForLanguage(nullptr, language, true);
+
+ if (type_system) {
+ return type_system->GetPersistentExpressionState();
+ } else {
+ return nullptr;
+ }
+}
+
+UserExpression *Target::GetUserExpressionForLanguage(
+ llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language,
+ Expression::ResultType desired_type,
+ const EvaluateExpressionOptions &options,
+ ValueObject *ctx_obj, Status &error) {
+ Status type_system_error;
+
+ TypeSystem *type_system =
+ GetScratchTypeSystemForLanguage(&type_system_error, language);
+ UserExpression *user_expr = nullptr;
+
+ if (!type_system) {
+ error.SetErrorStringWithFormat(
+ "Could not find type system for language %s: %s",
+ Language::GetNameForLanguageType(language),
+ type_system_error.AsCString());
+ return nullptr;
+ }
+
+ user_expr = type_system->GetUserExpression(expr, prefix, language,
+ desired_type, options, ctx_obj);
+ if (!user_expr)
+ error.SetErrorStringWithFormat(
+ "Could not create an expression for language %s",
+ Language::GetNameForLanguageType(language));
+
+ return user_expr;
+}
+
+FunctionCaller *Target::GetFunctionCallerForLanguage(
+ lldb::LanguageType language, const CompilerType &return_type,
+ const Address &function_address, const ValueList &arg_value_list,
+ const char *name, Status &error) {
+ Status type_system_error;
+ TypeSystem *type_system =
+ GetScratchTypeSystemForLanguage(&type_system_error, language);
+ FunctionCaller *persistent_fn = nullptr;
+
+ if (!type_system) {
+ error.SetErrorStringWithFormat(
+ "Could not find type system for language %s: %s",
+ Language::GetNameForLanguageType(language),
+ type_system_error.AsCString());
+ return persistent_fn;
+ }
+
+ persistent_fn = type_system->GetFunctionCaller(return_type, function_address,
+ arg_value_list, name);
+ if (!persistent_fn)
+ error.SetErrorStringWithFormat(
+ "Could not create an expression for language %s",
+ Language::GetNameForLanguageType(language));
+
+ return persistent_fn;
+}
+
+UtilityFunction *
+Target::GetUtilityFunctionForLanguage(const char *text,
+ lldb::LanguageType language,
+ const char *name, Status &error) {
+ Status type_system_error;
+ TypeSystem *type_system =
+ GetScratchTypeSystemForLanguage(&type_system_error, language);
+ UtilityFunction *utility_fn = nullptr;
+
+ if (!type_system) {
+ error.SetErrorStringWithFormat(
+ "Could not find type system for language %s: %s",
+ Language::GetNameForLanguageType(language),
+ type_system_error.AsCString());
+ return utility_fn;
+ }
+
+ utility_fn = type_system->GetUtilityFunction(text, name);
+ if (!utility_fn)
+ error.SetErrorStringWithFormat(
+ "Could not create an expression for language %s",
+ Language::GetNameForLanguageType(language));
+
+ return utility_fn;
+}
+
+ClangASTContext *Target::GetScratchClangASTContext(bool create_on_demand) {
+ if (m_valid) {
+ if (TypeSystem *type_system = GetScratchTypeSystemForLanguage(
+ nullptr, eLanguageTypeC, create_on_demand))
+ return llvm::dyn_cast<ClangASTContext>(type_system);
+ }
+ return nullptr;
+}
+
+ClangASTImporterSP Target::GetClangASTImporter() {
+ if (m_valid) {
+ if (!m_ast_importer_sp) {
+ m_ast_importer_sp = std::make_shared<ClangASTImporter>();
+ }
+ return m_ast_importer_sp;
+ }
+ return ClangASTImporterSP();
+}
+
+void Target::SettingsInitialize() { Process::SettingsInitialize(); }
+
+void Target::SettingsTerminate() { Process::SettingsTerminate(); }
+
+FileSpecList Target::GetDefaultExecutableSearchPaths() {
+ TargetPropertiesSP properties_sp(Target::GetGlobalProperties());
+ if (properties_sp)
+ return properties_sp->GetExecutableSearchPaths();
+ return FileSpecList();
+}
+
+FileSpecList Target::GetDefaultDebugFileSearchPaths() {
+ TargetPropertiesSP properties_sp(Target::GetGlobalProperties());
+ if (properties_sp)
+ return properties_sp->GetDebugFileSearchPaths();
+ return FileSpecList();
+}
+
+ArchSpec Target::GetDefaultArchitecture() {
+ TargetPropertiesSP properties_sp(Target::GetGlobalProperties());
+ if (properties_sp)
+ return properties_sp->GetDefaultArchitecture();
+ return ArchSpec();
+}
+
+void Target::SetDefaultArchitecture(const ArchSpec &arch) {
+ TargetPropertiesSP properties_sp(Target::GetGlobalProperties());
+ if (properties_sp) {
+ LogIfAnyCategoriesSet(
+ LIBLLDB_LOG_TARGET, "Target::SetDefaultArchitecture setting target's "
+ "default architecture to %s (%s)",
+ arch.GetArchitectureName(), arch.GetTriple().getTriple().c_str());
+ return properties_sp->SetDefaultArchitecture(arch);
+ }
+}
+
+Target *Target::GetTargetFromContexts(const ExecutionContext *exe_ctx_ptr,
+ const SymbolContext *sc_ptr) {
+ // The target can either exist in the "process" of ExecutionContext, or in
+ // the "target_sp" member of SymbolContext. This accessor helper function
+ // will get the target from one of these locations.
+
+ Target *target = nullptr;
+ if (sc_ptr != nullptr)
+ target = sc_ptr->target_sp.get();
+ if (target == nullptr && exe_ctx_ptr)
+ target = exe_ctx_ptr->GetTargetPtr();
+ return target;
+}
+
+ExpressionResults Target::EvaluateExpression(
+ llvm::StringRef expr, ExecutionContextScope *exe_scope,
+ lldb::ValueObjectSP &result_valobj_sp,
+ const EvaluateExpressionOptions &options, std::string *fixed_expression,
+ ValueObject *ctx_obj) {
+ result_valobj_sp.reset();
+
+ ExpressionResults execution_results = eExpressionSetupError;
+
+ if (expr.empty())
+ return execution_results;
+
+ // We shouldn't run stop hooks in expressions.
+ bool old_suppress_value = m_suppress_stop_hooks;
+ m_suppress_stop_hooks = true;
+ auto on_exit = llvm::make_scope_exit([this, old_suppress_value]() {
+ m_suppress_stop_hooks = old_suppress_value; });
+
+ ExecutionContext exe_ctx;
+
+ if (exe_scope) {
+ exe_scope->CalculateExecutionContext(exe_ctx);
+ } else if (m_process_sp) {
+ m_process_sp->CalculateExecutionContext(exe_ctx);
+ } else {
+ CalculateExecutionContext(exe_ctx);
+ }
+
+ // Make sure we aren't just trying to see the value of a persistent variable
+ // (something like "$0")
+ lldb::ExpressionVariableSP persistent_var_sp;
+ // Only check for persistent variables the expression starts with a '$'
+ if (expr[0] == '$')
+ persistent_var_sp = GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC)
+ ->GetPersistentExpressionState()
+ ->GetVariable(expr);
+
+ if (persistent_var_sp) {
+ result_valobj_sp = persistent_var_sp->GetValueObject();
+ execution_results = eExpressionCompleted;
+ } else {
+ llvm::StringRef prefix = GetExpressionPrefixContents();
+ Status error;
+ execution_results =
+ UserExpression::Evaluate(exe_ctx, options, expr, prefix,
+ result_valobj_sp, error, fixed_expression,
+ nullptr, // Module
+ ctx_obj);
+ }
+
+ return execution_results;
+}
+
+lldb::ExpressionVariableSP
+Target::GetPersistentVariable(ConstString name) {
+ lldb::ExpressionVariableSP variable_sp;
+ m_scratch_type_system_map.ForEach(
+ [name, &variable_sp](TypeSystem *type_system) -> bool {
+ if (PersistentExpressionState *persistent_state =
+ type_system->GetPersistentExpressionState()) {
+ variable_sp = persistent_state->GetVariable(name);
+
+ if (variable_sp)
+ return false; // Stop iterating the ForEach
+ }
+ return true; // Keep iterating the ForEach
+ });
+ return variable_sp;
+}
+
+lldb::addr_t Target::GetPersistentSymbol(ConstString name) {
+ lldb::addr_t address = LLDB_INVALID_ADDRESS;
+
+ m_scratch_type_system_map.ForEach(
+ [name, &address](TypeSystem *type_system) -> bool {
+ if (PersistentExpressionState *persistent_state =
+ type_system->GetPersistentExpressionState()) {
+ address = persistent_state->LookupSymbol(name);
+ if (address != LLDB_INVALID_ADDRESS)
+ return false; // Stop iterating the ForEach
+ }
+ return true; // Keep iterating the ForEach
+ });
+ return address;
+}
+
+lldb::addr_t Target::GetCallableLoadAddress(lldb::addr_t load_addr,
+ AddressClass addr_class) const {
+ auto arch_plugin = GetArchitecturePlugin();
+ return arch_plugin ?
+ arch_plugin->GetCallableLoadAddress(load_addr, addr_class) : load_addr;
+}
+
+lldb::addr_t Target::GetOpcodeLoadAddress(lldb::addr_t load_addr,
+ AddressClass addr_class) const {
+ auto arch_plugin = GetArchitecturePlugin();
+ return arch_plugin ?
+ arch_plugin->GetOpcodeLoadAddress(load_addr, addr_class) : load_addr;
+}
+
+lldb::addr_t Target::GetBreakableLoadAddress(lldb::addr_t addr) {
+ auto arch_plugin = GetArchitecturePlugin();
+ return arch_plugin ?
+ arch_plugin->GetBreakableLoadAddress(addr, *this) : addr;
+}
+
+SourceManager &Target::GetSourceManager() {
+ if (!m_source_manager_up)
+ m_source_manager_up.reset(new SourceManager(shared_from_this()));
+ return *m_source_manager_up;
+}
+
+ClangModulesDeclVendor *Target::GetClangModulesDeclVendor() {
+ static std::mutex s_clang_modules_decl_vendor_mutex; // If this is contended
+ // we can make it
+ // per-target
+
+ {
+ std::lock_guard<std::mutex> guard(s_clang_modules_decl_vendor_mutex);
+
+ if (!m_clang_modules_decl_vendor_up) {
+ m_clang_modules_decl_vendor_up.reset(
+ ClangModulesDeclVendor::Create(*this));
+ }
+ }
+
+ return m_clang_modules_decl_vendor_up.get();
+}
+
+Target::StopHookSP Target::CreateStopHook() {
+ lldb::user_id_t new_uid = ++m_stop_hook_next_id;
+ Target::StopHookSP stop_hook_sp(new StopHook(shared_from_this(), new_uid));
+ m_stop_hooks[new_uid] = stop_hook_sp;
+ return stop_hook_sp;
+}
+
+bool Target::RemoveStopHookByID(lldb::user_id_t user_id) {
+ size_t num_removed = m_stop_hooks.erase(user_id);
+ return (num_removed != 0);
+}
+
+void Target::RemoveAllStopHooks() { m_stop_hooks.clear(); }
+
+Target::StopHookSP Target::GetStopHookByID(lldb::user_id_t user_id) {
+ StopHookSP found_hook;
+
+ StopHookCollection::iterator specified_hook_iter;
+ specified_hook_iter = m_stop_hooks.find(user_id);
+ if (specified_hook_iter != m_stop_hooks.end())
+ found_hook = (*specified_hook_iter).second;
+ return found_hook;
+}
+
+bool Target::SetStopHookActiveStateByID(lldb::user_id_t user_id,
+ bool active_state) {
+ StopHookCollection::iterator specified_hook_iter;
+ specified_hook_iter = m_stop_hooks.find(user_id);
+ if (specified_hook_iter == m_stop_hooks.end())
+ return false;
+
+ (*specified_hook_iter).second->SetIsActive(active_state);
+ return true;
+}
+
+void Target::SetAllStopHooksActiveState(bool active_state) {
+ StopHookCollection::iterator pos, end = m_stop_hooks.end();
+ for (pos = m_stop_hooks.begin(); pos != end; pos++) {
+ (*pos).second->SetIsActive(active_state);
+ }
+}
+
+void Target::RunStopHooks() {
+ if (m_suppress_stop_hooks)
+ return;
+
+ if (!m_process_sp)
+ return;
+
+ // Somebody might have restarted the process:
+ if (m_process_sp->GetState() != eStateStopped)
+ return;
+
+ // <rdar://problem/12027563> make sure we check that we are not stopped
+ // because of us running a user expression since in that case we do not want
+ // to run the stop-hooks
+ if (m_process_sp->GetModIDRef().IsLastResumeForUserExpression())
+ return;
+
+ if (m_stop_hooks.empty())
+ return;
+
+ StopHookCollection::iterator pos, end = m_stop_hooks.end();
+
+ // If there aren't any active stop hooks, don't bother either.
+ // Also see if any of the active hooks want to auto-continue.
+ bool any_active_hooks = false;
+ bool auto_continue = false;
+ for (auto hook : m_stop_hooks) {
+ if (hook.second->IsActive()) {
+ any_active_hooks = true;
+ auto_continue |= hook.second->GetAutoContinue();
+ }
+ }
+ if (!any_active_hooks)
+ return;
+
+ CommandReturnObject result;
+
+ std::vector<ExecutionContext> exc_ctx_with_reasons;
+ std::vector<SymbolContext> sym_ctx_with_reasons;
+
+ ThreadList &cur_threadlist = m_process_sp->GetThreadList();
+ size_t num_threads = cur_threadlist.GetSize();
+ for (size_t i = 0; i < num_threads; i++) {
+ lldb::ThreadSP cur_thread_sp = cur_threadlist.GetThreadAtIndex(i);
+ if (cur_thread_sp->ThreadStoppedForAReason()) {
+ lldb::StackFrameSP cur_frame_sp = cur_thread_sp->GetStackFrameAtIndex(0);
+ exc_ctx_with_reasons.push_back(ExecutionContext(
+ m_process_sp.get(), cur_thread_sp.get(), cur_frame_sp.get()));
+ sym_ctx_with_reasons.push_back(
+ cur_frame_sp->GetSymbolContext(eSymbolContextEverything));
+ }
+ }
+
+ // If no threads stopped for a reason, don't run the stop-hooks.
+ size_t num_exe_ctx = exc_ctx_with_reasons.size();
+ if (num_exe_ctx == 0)
+ return;
+
+ result.SetImmediateOutputStream(m_debugger.GetAsyncOutputStream());
+ result.SetImmediateErrorStream(m_debugger.GetAsyncErrorStream());
+
+ bool keep_going = true;
+ bool hooks_ran = false;
+ bool print_hook_header = (m_stop_hooks.size() != 1);
+ bool print_thread_header = (num_exe_ctx != 1);
+ bool did_restart = false;
+
+ for (pos = m_stop_hooks.begin(); keep_going && pos != end; pos++) {
+ // result.Clear();
+ StopHookSP cur_hook_sp = (*pos).second;
+ if (!cur_hook_sp->IsActive())
+ continue;
+
+ bool any_thread_matched = false;
+ for (size_t i = 0; keep_going && i < num_exe_ctx; i++) {
+ if ((cur_hook_sp->GetSpecifier() == nullptr ||
+ cur_hook_sp->GetSpecifier()->SymbolContextMatches(
+ sym_ctx_with_reasons[i])) &&
+ (cur_hook_sp->GetThreadSpecifier() == nullptr ||
+ cur_hook_sp->GetThreadSpecifier()->ThreadPassesBasicTests(
+ exc_ctx_with_reasons[i].GetThreadRef()))) {
+ if (!hooks_ran) {
+ hooks_ran = true;
+ }
+ if (print_hook_header && !any_thread_matched) {
+ const char *cmd =
+ (cur_hook_sp->GetCommands().GetSize() == 1
+ ? cur_hook_sp->GetCommands().GetStringAtIndex(0)
+ : nullptr);
+ if (cmd)
+ result.AppendMessageWithFormat("\n- Hook %" PRIu64 " (%s)\n",
+ cur_hook_sp->GetID(), cmd);
+ else
+ result.AppendMessageWithFormat("\n- Hook %" PRIu64 "\n",
+ cur_hook_sp->GetID());
+ any_thread_matched = true;
+ }
+
+ if (print_thread_header)
+ result.AppendMessageWithFormat(
+ "-- Thread %d\n",
+ exc_ctx_with_reasons[i].GetThreadPtr()->GetIndexID());
+
+ CommandInterpreterRunOptions options;
+ options.SetStopOnContinue(true);
+ options.SetStopOnError(true);
+ options.SetEchoCommands(false);
+ options.SetPrintResults(true);
+ options.SetPrintErrors(true);
+ options.SetAddToHistory(false);
+
+ // Force Async:
+ bool old_async = GetDebugger().GetAsyncExecution();
+ GetDebugger().SetAsyncExecution(true);
+ GetDebugger().GetCommandInterpreter().HandleCommands(
+ cur_hook_sp->GetCommands(), &exc_ctx_with_reasons[i], options,
+ result);
+ GetDebugger().SetAsyncExecution(old_async);
+ // If the command started the target going again, we should bag out of
+ // running the stop hooks.
+ if ((result.GetStatus() == eReturnStatusSuccessContinuingNoResult) ||
+ (result.GetStatus() == eReturnStatusSuccessContinuingResult)) {
+ // But only complain if there were more stop hooks to do:
+ StopHookCollection::iterator tmp = pos;
+ if (++tmp != end)
+ result.AppendMessageWithFormat("\nAborting stop hooks, hook %" PRIu64
+ " set the program running.\n"
+ " Consider using '-G true' to make "
+ "stop hooks auto-continue.\n",
+ cur_hook_sp->GetID());
+ keep_going = false;
+ did_restart = true;
+ }
+ }
+ }
+ }
+ // Finally, if auto-continue was requested, do it now:
+ if (!did_restart && auto_continue)
+ m_process_sp->PrivateResume();
+
+ result.GetImmediateOutputStream()->Flush();
+ result.GetImmediateErrorStream()->Flush();
+}
+
+const TargetPropertiesSP &Target::GetGlobalProperties() {
+ // NOTE: intentional leak so we don't crash if global destructor chain gets
+ // called as other threads still use the result of this function
+ static TargetPropertiesSP *g_settings_sp_ptr =
+ new TargetPropertiesSP(new TargetProperties(nullptr));
+ return *g_settings_sp_ptr;
+}
+
+Status Target::Install(ProcessLaunchInfo *launch_info) {
+ Status error;
+ PlatformSP platform_sp(GetPlatform());
+ if (platform_sp) {
+ if (platform_sp->IsRemote()) {
+ if (platform_sp->IsConnected()) {
+ // Install all files that have an install path, and always install the
+ // main executable when connected to a remote platform
+ const ModuleList &modules = GetImages();
+ const size_t num_images = modules.GetSize();
+ for (size_t idx = 0; idx < num_images; ++idx) {
+ ModuleSP module_sp(modules.GetModuleAtIndex(idx));
+ if (module_sp) {
+ const bool is_main_executable = module_sp == GetExecutableModule();
+ FileSpec local_file(module_sp->GetFileSpec());
+ if (local_file) {
+ FileSpec remote_file(module_sp->GetRemoteInstallFileSpec());
+ if (!remote_file) {
+ if (is_main_executable) // TODO: add setting for always
+ // installing main executable???
+ {
+ // Always install the main executable
+ remote_file = platform_sp->GetRemoteWorkingDirectory();
+ remote_file.AppendPathComponent(
+ module_sp->GetFileSpec().GetFilename().GetCString());
+ }
+ }
+ if (remote_file) {
+ error = platform_sp->Install(local_file, remote_file);
+ if (error.Success()) {
+ module_sp->SetPlatformFileSpec(remote_file);
+ if (is_main_executable) {
+ platform_sp->SetFilePermissions(remote_file, 0700);
+ if (launch_info)
+ launch_info->SetExecutableFile(remote_file, false);
+ }
+ } else
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return error;
+}
+
+bool Target::ResolveLoadAddress(addr_t load_addr, Address &so_addr,
+ uint32_t stop_id) {
+ return m_section_load_history.ResolveLoadAddress(stop_id, load_addr, so_addr);
+}
+
+bool Target::ResolveFileAddress(lldb::addr_t file_addr,
+ Address &resolved_addr) {
+ return m_images.ResolveFileAddress(file_addr, resolved_addr);
+}
+
+bool Target::SetSectionLoadAddress(const SectionSP &section_sp,
+ addr_t new_section_load_addr,
+ bool warn_multiple) {
+ const addr_t old_section_load_addr =
+ m_section_load_history.GetSectionLoadAddress(
+ SectionLoadHistory::eStopIDNow, section_sp);
+ if (old_section_load_addr != new_section_load_addr) {
+ uint32_t stop_id = 0;
+ ProcessSP process_sp(GetProcessSP());
+ if (process_sp)
+ stop_id = process_sp->GetStopID();
+ else
+ stop_id = m_section_load_history.GetLastStopID();
+ if (m_section_load_history.SetSectionLoadAddress(
+ stop_id, section_sp, new_section_load_addr, warn_multiple))
+ return true; // Return true if the section load address was changed...
+ }
+ return false; // Return false to indicate nothing changed
+}
+
+size_t Target::UnloadModuleSections(const ModuleList &module_list) {
+ size_t section_unload_count = 0;
+ size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; ++i) {
+ section_unload_count +=
+ UnloadModuleSections(module_list.GetModuleAtIndex(i));
+ }
+ return section_unload_count;
+}
+
+size_t Target::UnloadModuleSections(const lldb::ModuleSP &module_sp) {
+ uint32_t stop_id = 0;
+ ProcessSP process_sp(GetProcessSP());
+ if (process_sp)
+ stop_id = process_sp->GetStopID();
+ else
+ stop_id = m_section_load_history.GetLastStopID();
+ SectionList *sections = module_sp->GetSectionList();
+ size_t section_unload_count = 0;
+ if (sections) {
+ const uint32_t num_sections = sections->GetNumSections(0);
+ for (uint32_t i = 0; i < num_sections; ++i) {
+ section_unload_count += m_section_load_history.SetSectionUnloaded(
+ stop_id, sections->GetSectionAtIndex(i));
+ }
+ }
+ return section_unload_count;
+}
+
+bool Target::SetSectionUnloaded(const lldb::SectionSP &section_sp) {
+ uint32_t stop_id = 0;
+ ProcessSP process_sp(GetProcessSP());
+ if (process_sp)
+ stop_id = process_sp->GetStopID();
+ else
+ stop_id = m_section_load_history.GetLastStopID();
+ return m_section_load_history.SetSectionUnloaded(stop_id, section_sp);
+}
+
+bool Target::SetSectionUnloaded(const lldb::SectionSP &section_sp,
+ addr_t load_addr) {
+ uint32_t stop_id = 0;
+ ProcessSP process_sp(GetProcessSP());
+ if (process_sp)
+ stop_id = process_sp->GetStopID();
+ else
+ stop_id = m_section_load_history.GetLastStopID();
+ return m_section_load_history.SetSectionUnloaded(stop_id, section_sp,
+ load_addr);
+}
+
+void Target::ClearAllLoadedSections() { m_section_load_history.Clear(); }
+
+Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) {
+ Status error;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET));
+
+ if (log)
+ log->Printf("Target::%s() called for %s", __FUNCTION__,
+ launch_info.GetExecutableFile().GetPath().c_str());
+
+ StateType state = eStateInvalid;
+
+ // Scope to temporarily get the process state in case someone has manually
+ // remotely connected already to a process and we can skip the platform
+ // launching.
+ {
+ ProcessSP process_sp(GetProcessSP());
+
+ if (process_sp) {
+ state = process_sp->GetState();
+ if (log)
+ log->Printf(
+ "Target::%s the process exists, and its current state is %s",
+ __FUNCTION__, StateAsCString(state));
+ } else {
+ if (log)
+ log->Printf("Target::%s the process instance doesn't currently exist.",
+ __FUNCTION__);
+ }
+ }
+
+ launch_info.GetFlags().Set(eLaunchFlagDebug);
+
+ // Get the value of synchronous execution here. If you wait till after you
+ // have started to run, then you could have hit a breakpoint, whose command
+ // might switch the value, and then you'll pick up that incorrect value.
+ Debugger &debugger = GetDebugger();
+ const bool synchronous_execution =
+ debugger.GetCommandInterpreter().GetSynchronous();
+
+ PlatformSP platform_sp(GetPlatform());
+
+ FinalizeFileActions(launch_info);
+
+ if (state == eStateConnected) {
+ if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) {
+ error.SetErrorString(
+ "can't launch in tty when launching through a remote connection");
+ return error;
+ }
+ }
+
+ if (!launch_info.GetArchitecture().IsValid())
+ launch_info.GetArchitecture() = GetArchitecture();
+
+ // If we're not already connected to the process, and if we have a platform
+ // that can launch a process for debugging, go ahead and do that here.
+ if (state != eStateConnected && platform_sp &&
+ platform_sp->CanDebugProcess()) {
+ if (log)
+ log->Printf("Target::%s asking the platform to debug the process",
+ __FUNCTION__);
+
+ // If there was a previous process, delete it before we make the new one.
+ // One subtle point, we delete the process before we release the reference
+ // to m_process_sp. That way even if we are the last owner, the process
+ // will get Finalized before it gets destroyed.
+ DeleteCurrentProcess();
+
+ m_process_sp =
+ GetPlatform()->DebugProcess(launch_info, debugger, this, error);
+
+ } else {
+ if (log)
+ log->Printf("Target::%s the platform doesn't know how to debug a "
+ "process, getting a process plugin to do this for us.",
+ __FUNCTION__);
+
+ if (state == eStateConnected) {
+ assert(m_process_sp);
+ } else {
+ // Use a Process plugin to construct the process.
+ const char *plugin_name = launch_info.GetProcessPluginName();
+ CreateProcess(launch_info.GetListener(), plugin_name, nullptr);
+ }
+
+ // Since we didn't have a platform launch the process, launch it here.
+ if (m_process_sp)
+ error = m_process_sp->Launch(launch_info);
+ }
+
+ if (!m_process_sp) {
+ if (error.Success())
+ error.SetErrorString("failed to launch or debug process");
+ return error;
+ }
+
+ if (error.Success()) {
+ if (synchronous_execution ||
+ !launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) {
+ ListenerSP hijack_listener_sp(launch_info.GetHijackListener());
+ if (!hijack_listener_sp) {
+ hijack_listener_sp =
+ Listener::MakeListener("lldb.Target.Launch.hijack");
+ launch_info.SetHijackListener(hijack_listener_sp);
+ m_process_sp->HijackProcessEvents(hijack_listener_sp);
+ }
+
+ StateType state = m_process_sp->WaitForProcessToStop(
+ llvm::None, nullptr, false, hijack_listener_sp, nullptr);
+
+ if (state == eStateStopped) {
+ if (!launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) {
+ if (synchronous_execution) {
+ // Now we have handled the stop-from-attach, and we are just switching
+ // to a synchronous resume. So we should switch to the SyncResume
+ // hijacker.
+ m_process_sp->RestoreProcessEvents();
+ m_process_sp->ResumeSynchronous(stream);
+ } else {
+ m_process_sp->RestoreProcessEvents();
+ error = m_process_sp->PrivateResume();
+ }
+ if (!error.Success()) {
+ Status error2;
+ error2.SetErrorStringWithFormat(
+ "process resume at entry point failed: %s", error.AsCString());
+ error = error2;
+ }
+ }
+ } else if (state == eStateExited) {
+ bool with_shell = !!launch_info.GetShell();
+ const int exit_status = m_process_sp->GetExitStatus();
+ const char *exit_desc = m_process_sp->GetExitDescription();
+#define LAUNCH_SHELL_MESSAGE \
+ "\n'r' and 'run' are aliases that default to launching through a " \
+ "shell.\nTry launching without going through a shell by using 'process " \
+ "launch'."
+ if (exit_desc && exit_desc[0]) {
+ if (with_shell)
+ error.SetErrorStringWithFormat(
+ "process exited with status %i (%s)" LAUNCH_SHELL_MESSAGE,
+ exit_status, exit_desc);
+ else
+ error.SetErrorStringWithFormat("process exited with status %i (%s)",
+ exit_status, exit_desc);
+ } else {
+ if (with_shell)
+ error.SetErrorStringWithFormat(
+ "process exited with status %i" LAUNCH_SHELL_MESSAGE,
+ exit_status);
+ else
+ error.SetErrorStringWithFormat("process exited with status %i",
+ exit_status);
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "initial process state wasn't stopped: %s", StateAsCString(state));
+ }
+ }
+ m_process_sp->RestoreProcessEvents();
+ } else {
+ Status error2;
+ error2.SetErrorStringWithFormat("process launch failed: %s",
+ error.AsCString());
+ error = error2;
+ }
+ return error;
+}
+
+Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) {
+ auto state = eStateInvalid;
+ auto process_sp = GetProcessSP();
+ if (process_sp) {
+ state = process_sp->GetState();
+ if (process_sp->IsAlive() && state != eStateConnected) {
+ if (state == eStateAttaching)
+ return Status("process attach is in progress");
+ return Status("a process is already being debugged");
+ }
+ }
+
+ const ModuleSP old_exec_module_sp = GetExecutableModule();
+
+ // If no process info was specified, then use the target executable name as
+ // the process to attach to by default
+ if (!attach_info.ProcessInfoSpecified()) {
+ if (old_exec_module_sp)
+ attach_info.GetExecutableFile().GetFilename() =
+ old_exec_module_sp->GetPlatformFileSpec().GetFilename();
+
+ if (!attach_info.ProcessInfoSpecified()) {
+ return Status("no process specified, create a target with a file, or "
+ "specify the --pid or --name");
+ }
+ }
+
+ const auto platform_sp =
+ GetDebugger().GetPlatformList().GetSelectedPlatform();
+ ListenerSP hijack_listener_sp;
+ const bool async = attach_info.GetAsync();
+ if (!async) {
+ hijack_listener_sp =
+ Listener::MakeListener("lldb.Target.Attach.attach.hijack");
+ attach_info.SetHijackListener(hijack_listener_sp);
+ }
+
+ Status error;
+ if (state != eStateConnected && platform_sp != nullptr &&
+ platform_sp->CanDebugProcess()) {
+ SetPlatform(platform_sp);
+ process_sp = platform_sp->Attach(attach_info, GetDebugger(), this, error);
+ } else {
+ if (state != eStateConnected) {
+ const char *plugin_name = attach_info.GetProcessPluginName();
+ process_sp =
+ CreateProcess(attach_info.GetListenerForProcess(GetDebugger()),
+ plugin_name, nullptr);
+ if (process_sp == nullptr) {
+ error.SetErrorStringWithFormat(
+ "failed to create process using plugin %s",
+ (plugin_name) ? plugin_name : "null");
+ return error;
+ }
+ }
+ if (hijack_listener_sp)
+ process_sp->HijackProcessEvents(hijack_listener_sp);
+ error = process_sp->Attach(attach_info);
+ }
+
+ if (error.Success() && process_sp) {
+ if (async) {
+ process_sp->RestoreProcessEvents();
+ } else {
+ state = process_sp->WaitForProcessToStop(
+ llvm::None, nullptr, false, attach_info.GetHijackListener(), stream);
+ process_sp->RestoreProcessEvents();
+
+ if (state != eStateStopped) {
+ const char *exit_desc = process_sp->GetExitDescription();
+ if (exit_desc)
+ error.SetErrorStringWithFormat("%s", exit_desc);
+ else
+ error.SetErrorString(
+ "process did not stop (no such process or permission problem?)");
+ process_sp->Destroy(false);
+ }
+ }
+ }
+ return error;
+}
+
+void Target::FinalizeFileActions(ProcessLaunchInfo &info) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Finalize the file actions, and if none were given, default to opening up a
+ // pseudo terminal
+ PlatformSP platform_sp = GetPlatform();
+ const bool default_to_use_pty =
+ m_platform_sp ? m_platform_sp->IsHost() : false;
+ LLDB_LOG(
+ log,
+ "have platform={0}, platform_sp->IsHost()={1}, default_to_use_pty={2}",
+ bool(platform_sp),
+ platform_sp ? (platform_sp->IsHost() ? "true" : "false") : "n/a",
+ default_to_use_pty);
+
+ // If nothing for stdin or stdout or stderr was specified, then check the
+ // process for any default settings that were set with "settings set"
+ if (info.GetFileActionForFD(STDIN_FILENO) == nullptr ||
+ info.GetFileActionForFD(STDOUT_FILENO) == nullptr ||
+ info.GetFileActionForFD(STDERR_FILENO) == nullptr) {
+ LLDB_LOG(log, "at least one of stdin/stdout/stderr was not set, evaluating "
+ "default handling");
+
+ if (info.GetFlags().Test(eLaunchFlagLaunchInTTY)) {
+ // Do nothing, if we are launching in a remote terminal no file actions
+ // should be done at all.
+ return;
+ }
+
+ if (info.GetFlags().Test(eLaunchFlagDisableSTDIO)) {
+ LLDB_LOG(log, "eLaunchFlagDisableSTDIO set, adding suppression action "
+ "for stdin, stdout and stderr");
+ info.AppendSuppressFileAction(STDIN_FILENO, true, false);
+ info.AppendSuppressFileAction(STDOUT_FILENO, false, true);
+ info.AppendSuppressFileAction(STDERR_FILENO, false, true);
+ } else {
+ // Check for any values that might have gotten set with any of: (lldb)
+ // settings set target.input-path (lldb) settings set target.output-path
+ // (lldb) settings set target.error-path
+ FileSpec in_file_spec;
+ FileSpec out_file_spec;
+ FileSpec err_file_spec;
+ // Only override with the target settings if we don't already have an
+ // action for in, out or error
+ if (info.GetFileActionForFD(STDIN_FILENO) == nullptr)
+ in_file_spec = GetStandardInputPath();
+ if (info.GetFileActionForFD(STDOUT_FILENO) == nullptr)
+ out_file_spec = GetStandardOutputPath();
+ if (info.GetFileActionForFD(STDERR_FILENO) == nullptr)
+ err_file_spec = GetStandardErrorPath();
+
+ LLDB_LOG(log, "target stdin='{0}', target stdout='{1}', stderr='{1}'",
+ in_file_spec, out_file_spec, err_file_spec);
+
+ if (in_file_spec) {
+ info.AppendOpenFileAction(STDIN_FILENO, in_file_spec, true, false);
+ LLDB_LOG(log, "appended stdin open file action for {0}", in_file_spec);
+ }
+
+ if (out_file_spec) {
+ info.AppendOpenFileAction(STDOUT_FILENO, out_file_spec, false, true);
+ LLDB_LOG(log, "appended stdout open file action for {0}",
+ out_file_spec);
+ }
+
+ if (err_file_spec) {
+ info.AppendOpenFileAction(STDERR_FILENO, err_file_spec, false, true);
+ LLDB_LOG(log, "appended stderr open file action for {0}",
+ err_file_spec);
+ }
+
+ if (default_to_use_pty &&
+ (!in_file_spec || !out_file_spec || !err_file_spec)) {
+ llvm::Error Err = info.SetUpPtyRedirection();
+ LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}");
+ }
+ }
+ }
+}
+
+// Target::StopHook
+Target::StopHook::StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid)
+ : UserID(uid), m_target_sp(target_sp), m_commands(), m_specifier_sp(),
+ m_thread_spec_up() {}
+
+Target::StopHook::StopHook(const StopHook &rhs)
+ : UserID(rhs.GetID()), m_target_sp(rhs.m_target_sp),
+ m_commands(rhs.m_commands), m_specifier_sp(rhs.m_specifier_sp),
+ m_thread_spec_up(), m_active(rhs.m_active),
+ m_auto_continue(rhs.m_auto_continue) {
+ if (rhs.m_thread_spec_up)
+ m_thread_spec_up.reset(new ThreadSpec(*rhs.m_thread_spec_up));
+}
+
+Target::StopHook::~StopHook() = default;
+
+void Target::StopHook::SetSpecifier(SymbolContextSpecifier *specifier) {
+ m_specifier_sp.reset(specifier);
+}
+
+void Target::StopHook::SetThreadSpecifier(ThreadSpec *specifier) {
+ m_thread_spec_up.reset(specifier);
+}
+
+void Target::StopHook::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) const {
+ int indent_level = s->GetIndentLevel();
+
+ s->SetIndentLevel(indent_level + 2);
+
+ s->Printf("Hook: %" PRIu64 "\n", GetID());
+ if (m_active)
+ s->Indent("State: enabled\n");
+ else
+ s->Indent("State: disabled\n");
+
+ if (m_auto_continue)
+ s->Indent("AutoContinue on\n");
+
+ if (m_specifier_sp) {
+ s->Indent();
+ s->PutCString("Specifier:\n");
+ s->SetIndentLevel(indent_level + 4);
+ m_specifier_sp->GetDescription(s, level);
+ s->SetIndentLevel(indent_level + 2);
+ }
+
+ if (m_thread_spec_up) {
+ StreamString tmp;
+ s->Indent("Thread:\n");
+ m_thread_spec_up->GetDescription(&tmp, level);
+ s->SetIndentLevel(indent_level + 4);
+ s->Indent(tmp.GetString());
+ s->PutCString("\n");
+ s->SetIndentLevel(indent_level + 2);
+ }
+
+ s->Indent("Commands: \n");
+ s->SetIndentLevel(indent_level + 4);
+ uint32_t num_commands = m_commands.GetSize();
+ for (uint32_t i = 0; i < num_commands; i++) {
+ s->Indent(m_commands.GetStringAtIndex(i));
+ s->PutCString("\n");
+ }
+ s->SetIndentLevel(indent_level);
+}
+
+// class TargetProperties
+
+// clang-format off
+static constexpr OptionEnumValueElement g_dynamic_value_types[] = {
+ {eNoDynamicValues, "no-dynamic-values",
+ "Don't calculate the dynamic type of values"},
+ {eDynamicCanRunTarget, "run-target", "Calculate the dynamic type of values "
+ "even if you have to run the target."},
+ {eDynamicDontRunTarget, "no-run-target",
+ "Calculate the dynamic type of values, but don't run the target."} };
+
+OptionEnumValues lldb_private::GetDynamicValueTypes() {
+ return OptionEnumValues(g_dynamic_value_types);
+}
+
+static constexpr OptionEnumValueElement g_inline_breakpoint_enums[] = {
+ {eInlineBreakpointsNever, "never", "Never look for inline breakpoint "
+ "locations (fastest). This setting "
+ "should only be used if you know that "
+ "no inlining occurs in your programs."},
+ {eInlineBreakpointsHeaders, "headers",
+ "Only check for inline breakpoint locations when setting breakpoints in "
+ "header files, but not when setting breakpoint in implementation source "
+ "files (default)."},
+ {eInlineBreakpointsAlways, "always",
+ "Always look for inline breakpoint locations when setting file and line "
+ "breakpoints (slower but most accurate)."} };
+
+enum x86DisassemblyFlavor {
+ eX86DisFlavorDefault,
+ eX86DisFlavorIntel,
+ eX86DisFlavorATT
+};
+
+static constexpr OptionEnumValueElement g_x86_dis_flavor_value_types[] = {
+ {eX86DisFlavorDefault, "default", "Disassembler default (currently att)."},
+ {eX86DisFlavorIntel, "intel", "Intel disassembler flavor."},
+ {eX86DisFlavorATT, "att", "AT&T disassembler flavor."} };
+
+static constexpr OptionEnumValueElement g_hex_immediate_style_values[] = {
+ {Disassembler::eHexStyleC, "c", "C-style (0xffff)."},
+ {Disassembler::eHexStyleAsm, "asm", "Asm-style (0ffffh)."} };
+
+static constexpr OptionEnumValueElement g_load_script_from_sym_file_values[] = {
+ {eLoadScriptFromSymFileTrue, "true",
+ "Load debug scripts inside symbol files"},
+ {eLoadScriptFromSymFileFalse, "false",
+ "Do not load debug scripts inside symbol files."},
+ {eLoadScriptFromSymFileWarn, "warn",
+ "Warn about debug scripts inside symbol files but do not load them."} };
+
+static constexpr
+OptionEnumValueElement g_load_current_working_dir_lldbinit_values[] = {
+ {eLoadCWDlldbinitTrue, "true",
+ "Load .lldbinit files from current directory"},
+ {eLoadCWDlldbinitFalse, "false",
+ "Do not load .lldbinit files from current directory"},
+ {eLoadCWDlldbinitWarn, "warn",
+ "Warn about loading .lldbinit files from current directory"} };
+
+static constexpr OptionEnumValueElement g_memory_module_load_level_values[] = {
+ {eMemoryModuleLoadLevelMinimal, "minimal",
+ "Load minimal information when loading modules from memory. Currently "
+ "this setting loads sections only."},
+ {eMemoryModuleLoadLevelPartial, "partial",
+ "Load partial information when loading modules from memory. Currently "
+ "this setting loads sections and function bounds."},
+ {eMemoryModuleLoadLevelComplete, "complete",
+ "Load complete information when loading modules from memory. Currently "
+ "this setting loads sections and all symbols."} };
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"default-arch", OptionValue::eTypeArch, true, 0, nullptr, {},
+ "Default architecture to choose, when there's a choice."},
+ {"move-to-nearest-code", OptionValue::eTypeBoolean, false, true, nullptr,
+ {}, "Move breakpoints to nearest code."},
+ {"language", OptionValue::eTypeLanguage, false, eLanguageTypeUnknown,
+ nullptr, {},
+ "The language to use when interpreting expressions entered in commands."},
+ {"expr-prefix", OptionValue::eTypeFileSpec, false, 0, nullptr, {},
+ "Path to a file containing expressions to be prepended to all "
+ "expressions."},
+ {"prefer-dynamic-value", OptionValue::eTypeEnum, false,
+ eDynamicDontRunTarget, nullptr, OptionEnumValues(g_dynamic_value_types),
+ "Should printed values be shown as their dynamic value."},
+ {"enable-synthetic-value", OptionValue::eTypeBoolean, false, true, nullptr,
+ {}, "Should synthetic values be used by default whenever available."},
+ {"skip-prologue", OptionValue::eTypeBoolean, false, true, nullptr, {},
+ "Skip function prologues when setting breakpoints by name."},
+ {"source-map", OptionValue::eTypePathMap, false, 0, nullptr, {},
+ "Source path remappings are used to track the change of location between "
+ "a source file when built, and "
+ "where it exists on the current system. It consists of an array of "
+ "duples, the first element of each duple is "
+ "some part (starting at the root) of the path to the file when it was "
+ "built, "
+ "and the second is where the remainder of the original build hierarchy is "
+ "rooted on the local system. "
+ "Each element of the array is checked in order and the first one that "
+ "results in a match wins."},
+ {"exec-search-paths", OptionValue::eTypeFileSpecList, false, 0, nullptr,
+ {}, "Executable search paths to use when locating executable files "
+ "whose paths don't match the local file system."},
+ {"debug-file-search-paths", OptionValue::eTypeFileSpecList, false, 0,
+ nullptr, {},
+ "List of directories to be searched when locating debug symbol files. "
+ "See also symbols.enable-external-lookup."},
+ {"clang-module-search-paths", OptionValue::eTypeFileSpecList, false, 0,
+ nullptr, {},
+ "List of directories to be searched when locating modules for Clang."},
+ {"auto-import-clang-modules", OptionValue::eTypeBoolean, false, true,
+ nullptr, {},
+ "Automatically load Clang modules referred to by the program."},
+ {"import-std-module", OptionValue::eTypeBoolean, false, false,
+ nullptr, {},
+ "Import the C++ std module to improve debugging STL containers."},
+ {"auto-apply-fixits", OptionValue::eTypeBoolean, false, true, nullptr,
+ {}, "Automatically apply fix-it hints to expressions."},
+ {"notify-about-fixits", OptionValue::eTypeBoolean, false, true, nullptr,
+ {}, "Print the fixed expression text."},
+ {"save-jit-objects", OptionValue::eTypeBoolean, false, false, nullptr,
+ {}, "Save intermediate object files generated by the LLVM JIT"},
+ {"max-children-count", OptionValue::eTypeSInt64, false, 256, nullptr,
+ {}, "Maximum number of children to expand in any level of depth."},
+ {"max-string-summary-length", OptionValue::eTypeSInt64, false, 1024,
+ nullptr, {},
+ "Maximum number of characters to show when using %s in summary strings."},
+ {"max-memory-read-size", OptionValue::eTypeSInt64, false, 1024, nullptr,
+ {}, "Maximum number of bytes that 'memory read' will fetch before "
+ "--force must be specified."},
+ {"breakpoints-use-platform-avoid-list", OptionValue::eTypeBoolean, false,
+ true, nullptr, {}, "Consult the platform module avoid list when "
+ "setting non-module specific breakpoints."},
+ {"arg0", OptionValue::eTypeString, false, 0, nullptr, {},
+ "The first argument passed to the program in the argument array which can "
+ "be different from the executable itself."},
+ {"run-args", OptionValue::eTypeArgs, false, 0, nullptr, {},
+ "A list containing all the arguments to be passed to the executable when "
+ "it is run. Note that this does NOT include the argv[0] which is in "
+ "target.arg0."},
+ {"env-vars", OptionValue::eTypeDictionary, false, OptionValue::eTypeString,
+ nullptr, {}, "A list of all the environment variables to be passed "
+ "to the executable's environment, and their values."},
+ {"inherit-env", OptionValue::eTypeBoolean, false, true, nullptr, {},
+ "Inherit the environment from the process that is running LLDB."},
+ {"input-path", OptionValue::eTypeFileSpec, false, 0, nullptr, {},
+ "The file/path to be used by the executable program for reading its "
+ "standard input."},
+ {"output-path", OptionValue::eTypeFileSpec, false, 0, nullptr, {},
+ "The file/path to be used by the executable program for writing its "
+ "standard output."},
+ {"error-path", OptionValue::eTypeFileSpec, false, 0, nullptr, {},
+ "The file/path to be used by the executable program for writing its "
+ "standard error."},
+ {"detach-on-error", OptionValue::eTypeBoolean, false, true, nullptr,
+ {}, "debugserver will detach (rather than killing) a process if it "
+ "loses connection with lldb."},
+ {"preload-symbols", OptionValue::eTypeBoolean, false, true, nullptr, {},
+ "Enable loading of symbol tables before they are needed."},
+ {"disable-aslr", OptionValue::eTypeBoolean, false, true, nullptr, {},
+ "Disable Address Space Layout Randomization (ASLR)"},
+ {"disable-stdio", OptionValue::eTypeBoolean, false, false, nullptr, {},
+ "Disable stdin/stdout for process (e.g. for a GUI application)"},
+ {"inline-breakpoint-strategy", OptionValue::eTypeEnum, false,
+ eInlineBreakpointsAlways, nullptr,
+ OptionEnumValues(g_inline_breakpoint_enums),
+ "The strategy to use when settings breakpoints by file and line. "
+ "Breakpoint locations can end up being inlined by the compiler, so that a "
+ "compile unit 'a.c' might contain an inlined function from another source "
+ "file. "
+ "Usually this is limited to breakpoint locations from inlined functions "
+ "from header or other include files, or more accurately "
+ "non-implementation source files. "
+ "Sometimes code might #include implementation files and cause inlined "
+ "breakpoint locations in inlined implementation files. "
+ "Always checking for inlined breakpoint locations can be expensive "
+ "(memory and time), so if you have a project with many headers "
+ "and find that setting breakpoints is slow, then you can change this "
+ "setting to headers. "
+ "This setting allows you to control exactly which strategy is used when "
+ "setting "
+ "file and line breakpoints."},
+ // FIXME: This is the wrong way to do per-architecture settings, but we
+ // don't have a general per architecture settings system in place yet.
+ {"x86-disassembly-flavor", OptionValue::eTypeEnum, false,
+ eX86DisFlavorDefault, nullptr,
+ OptionEnumValues(g_x86_dis_flavor_value_types),
+ "The default disassembly flavor to use for x86 or x86-64 targets."},
+ {"use-hex-immediates", OptionValue::eTypeBoolean, false, true, nullptr,
+ {}, "Show immediates in disassembly as hexadecimal."},
+ {"hex-immediate-style", OptionValue::eTypeEnum, false,
+ Disassembler::eHexStyleC, nullptr,
+ OptionEnumValues(g_hex_immediate_style_values),
+ "Which style to use for printing hexadecimal disassembly values."},
+ {"use-fast-stepping", OptionValue::eTypeBoolean, false, true, nullptr,
+ {}, "Use a fast stepping algorithm based on running from branch to "
+ "branch rather than instruction single-stepping."},
+ {"load-script-from-symbol-file", OptionValue::eTypeEnum, false,
+ eLoadScriptFromSymFileWarn, nullptr,
+ OptionEnumValues(g_load_script_from_sym_file_values),
+ "Allow LLDB to load scripting resources embedded in symbol files when "
+ "available."},
+ {"load-cwd-lldbinit", OptionValue::eTypeEnum, false, eLoadCWDlldbinitWarn,
+ nullptr, OptionEnumValues(g_load_current_working_dir_lldbinit_values),
+ "Allow LLDB to .lldbinit files from the current directory automatically."},
+ {"memory-module-load-level", OptionValue::eTypeEnum, false,
+ eMemoryModuleLoadLevelComplete, nullptr,
+ OptionEnumValues(g_memory_module_load_level_values),
+ "Loading modules from memory can be slow as reading the symbol tables and "
+ "other data can take a long time depending on your connection to the "
+ "debug target. "
+ "This setting helps users control how much information gets loaded when "
+ "loading modules from memory."
+ "'complete' is the default value for this setting which will load all "
+ "sections and symbols by reading them from memory (slowest, most "
+ "accurate). "
+ "'partial' will load sections and attempt to find function bounds without "
+ "downloading the symbol table (faster, still accurate, missing symbol "
+ "names). "
+ "'minimal' is the fastest setting and will load section data with no "
+ "symbols, but should rarely be used as stack frames in these memory "
+ "regions will be inaccurate and not provide any context (fastest). "},
+ {"display-expression-in-crashlogs", OptionValue::eTypeBoolean, false, false,
+ nullptr, {}, "Expressions that crash will show up in crash logs if "
+ "the host system supports executable specific crash log "
+ "strings and this setting is set to true."},
+ {"trap-handler-names", OptionValue::eTypeArray, true,
+ OptionValue::eTypeString, nullptr, {},
+ "A list of trap handler function names, e.g. a common Unix user process "
+ "one is _sigtramp."},
+ {"display-runtime-support-values", OptionValue::eTypeBoolean, false, false,
+ nullptr, {}, "If true, LLDB will show variables that are meant to "
+ "support the operation of a language's runtime support."},
+ {"display-recognized-arguments", OptionValue::eTypeBoolean, false, false,
+ nullptr, {}, "Show recognized arguments in variable listings by default."},
+ {"non-stop-mode", OptionValue::eTypeBoolean, false, 0, nullptr, {},
+ "Disable lock-step debugging, instead control threads independently."},
+ {"require-hardware-breakpoint", OptionValue::eTypeBoolean, false, 0,
+ nullptr, {}, "Require all breakpoints to be hardware breakpoints."}};
+// clang-format on
+
+enum {
+ ePropertyDefaultArch,
+ ePropertyMoveToNearestCode,
+ ePropertyLanguage,
+ ePropertyExprPrefix,
+ ePropertyPreferDynamic,
+ ePropertyEnableSynthetic,
+ ePropertySkipPrologue,
+ ePropertySourceMap,
+ ePropertyExecutableSearchPaths,
+ ePropertyDebugFileSearchPaths,
+ ePropertyClangModuleSearchPaths,
+ ePropertyAutoImportClangModules,
+ ePropertyImportStdModule,
+ ePropertyAutoApplyFixIts,
+ ePropertyNotifyAboutFixIts,
+ ePropertySaveObjects,
+ ePropertyMaxChildrenCount,
+ ePropertyMaxSummaryLength,
+ ePropertyMaxMemReadSize,
+ ePropertyBreakpointUseAvoidList,
+ ePropertyArg0,
+ ePropertyRunArgs,
+ ePropertyEnvVars,
+ ePropertyInheritEnv,
+ ePropertyInputPath,
+ ePropertyOutputPath,
+ ePropertyErrorPath,
+ ePropertyDetachOnError,
+ ePropertyPreloadSymbols,
+ ePropertyDisableASLR,
+ ePropertyDisableSTDIO,
+ ePropertyInlineStrategy,
+ ePropertyDisassemblyFlavor,
+ ePropertyUseHexImmediates,
+ ePropertyHexImmediateStyle,
+ ePropertyUseFastStepping,
+ ePropertyLoadScriptFromSymbolFile,
+ ePropertyLoadCWDlldbinitFile,
+ ePropertyMemoryModuleLoadLevel,
+ ePropertyDisplayExpressionsInCrashlogs,
+ ePropertyTrapHandlerNames,
+ ePropertyDisplayRuntimeSupportValues,
+ ePropertyDisplayRecognizedArguments,
+ ePropertyNonStopModeEnabled,
+ ePropertyRequireHardwareBreakpoints,
+ ePropertyExperimental,
+};
+
+class TargetOptionValueProperties : public OptionValueProperties {
+public:
+ TargetOptionValueProperties(ConstString name)
+ : OptionValueProperties(name), m_target(nullptr), m_got_host_env(false) {}
+
+ // This constructor is used when creating TargetOptionValueProperties when it
+ // is part of a new lldb_private::Target instance. It will copy all current
+ // global property values as needed
+ TargetOptionValueProperties(Target *target,
+ const TargetPropertiesSP &target_properties_sp)
+ : OptionValueProperties(*target_properties_sp->GetValueProperties()),
+ m_target(target), m_got_host_env(false) {}
+
+ const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx,
+ bool will_modify,
+ uint32_t idx) const override {
+ // When getting the value for a key from the target options, we will always
+ // try and grab the setting from the current target if there is one. Else
+ // we just use the one from this instance.
+ if (idx == ePropertyEnvVars)
+ GetHostEnvironmentIfNeeded();
+
+ if (exe_ctx) {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target) {
+ TargetOptionValueProperties *target_properties =
+ static_cast<TargetOptionValueProperties *>(
+ target->GetValueProperties().get());
+ if (this != target_properties)
+ return target_properties->ProtectedGetPropertyAtIndex(idx);
+ }
+ }
+ return ProtectedGetPropertyAtIndex(idx);
+ }
+
+ lldb::TargetSP GetTargetSP() { return m_target->shared_from_this(); }
+
+protected:
+ void GetHostEnvironmentIfNeeded() const {
+ if (!m_got_host_env) {
+ if (m_target) {
+ m_got_host_env = true;
+ const uint32_t idx = ePropertyInheritEnv;
+ if (GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0)) {
+ PlatformSP platform_sp(m_target->GetPlatform());
+ if (platform_sp) {
+ Environment env = platform_sp->GetEnvironment();
+ OptionValueDictionary *env_dict =
+ GetPropertyAtIndexAsOptionValueDictionary(nullptr,
+ ePropertyEnvVars);
+ if (env_dict) {
+ const bool can_replace = false;
+ for (const auto &KV : env) {
+ // Don't allow existing keys to be replaced with ones we get
+ // from the platform environment
+ env_dict->SetValueForKey(
+ ConstString(KV.first()),
+ OptionValueSP(new OptionValueString(KV.second.c_str())),
+ can_replace);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ Target *m_target;
+ mutable bool m_got_host_env;
+};
+
+// TargetProperties
+static constexpr PropertyDefinition g_experimental_properties[]{
+ {"inject-local-vars", OptionValue::eTypeBoolean, true, true, nullptr,
+ {},
+ "If true, inject local variables explicitly into the expression text. "
+ "This will fix symbol resolution when there are name collisions between "
+ "ivars and local variables. "
+ "But it can make expressions run much more slowly."},
+ {"use-modern-type-lookup", OptionValue::eTypeBoolean, true, false, nullptr,
+ {}, "If true, use Clang's modern type lookup infrastructure."}};
+
+enum { ePropertyInjectLocalVars = 0, ePropertyUseModernTypeLookup };
+
+class TargetExperimentalOptionValueProperties : public OptionValueProperties {
+public:
+ TargetExperimentalOptionValueProperties()
+ : OptionValueProperties(
+ ConstString(Properties::GetExperimentalSettingsName())) {}
+};
+
+TargetExperimentalProperties::TargetExperimentalProperties()
+ : Properties(OptionValuePropertiesSP(
+ new TargetExperimentalOptionValueProperties())) {
+ m_collection_sp->Initialize(g_experimental_properties);
+}
+
+// TargetProperties
+TargetProperties::TargetProperties(Target *target)
+ : Properties(), m_launch_info() {
+ if (target) {
+ m_collection_sp = std::make_shared<TargetOptionValueProperties>(
+ target, Target::GetGlobalProperties());
+
+ // Set callbacks to update launch_info whenever "settins set" updated any
+ // of these properties
+ m_collection_sp->SetValueChangedCallback(
+ ePropertyArg0, TargetProperties::Arg0ValueChangedCallback, this);
+ m_collection_sp->SetValueChangedCallback(
+ ePropertyRunArgs, TargetProperties::RunArgsValueChangedCallback, this);
+ m_collection_sp->SetValueChangedCallback(
+ ePropertyEnvVars, TargetProperties::EnvVarsValueChangedCallback, this);
+ m_collection_sp->SetValueChangedCallback(
+ ePropertyInputPath, TargetProperties::InputPathValueChangedCallback,
+ this);
+ m_collection_sp->SetValueChangedCallback(
+ ePropertyOutputPath, TargetProperties::OutputPathValueChangedCallback,
+ this);
+ m_collection_sp->SetValueChangedCallback(
+ ePropertyErrorPath, TargetProperties::ErrorPathValueChangedCallback,
+ this);
+ m_collection_sp->SetValueChangedCallback(
+ ePropertyDetachOnError,
+ TargetProperties::DetachOnErrorValueChangedCallback, this);
+ m_collection_sp->SetValueChangedCallback(
+ ePropertyDisableASLR, TargetProperties::DisableASLRValueChangedCallback,
+ this);
+ m_collection_sp->SetValueChangedCallback(
+ ePropertyDisableSTDIO,
+ TargetProperties::DisableSTDIOValueChangedCallback, this);
+
+ m_experimental_properties_up.reset(new TargetExperimentalProperties());
+ m_collection_sp->AppendProperty(
+ ConstString(Properties::GetExperimentalSettingsName()),
+ ConstString("Experimental settings - setting these won't produce "
+ "errors if the setting is not present."),
+ true, m_experimental_properties_up->GetValueProperties());
+
+ // Update m_launch_info once it was created
+ Arg0ValueChangedCallback(this, nullptr);
+ RunArgsValueChangedCallback(this, nullptr);
+ // EnvVarsValueChangedCallback(this, nullptr); // FIXME: cause segfault in
+ // Target::GetPlatform()
+ InputPathValueChangedCallback(this, nullptr);
+ OutputPathValueChangedCallback(this, nullptr);
+ ErrorPathValueChangedCallback(this, nullptr);
+ DetachOnErrorValueChangedCallback(this, nullptr);
+ DisableASLRValueChangedCallback(this, nullptr);
+ DisableSTDIOValueChangedCallback(this, nullptr);
+ } else {
+ m_collection_sp =
+ std::make_shared<TargetOptionValueProperties>(ConstString("target"));
+ m_collection_sp->Initialize(g_properties);
+ m_experimental_properties_up.reset(new TargetExperimentalProperties());
+ m_collection_sp->AppendProperty(
+ ConstString(Properties::GetExperimentalSettingsName()),
+ ConstString("Experimental settings - setting these won't produce "
+ "errors if the setting is not present."),
+ true, m_experimental_properties_up->GetValueProperties());
+ m_collection_sp->AppendProperty(
+ ConstString("process"), ConstString("Settings specific to processes."),
+ true, Process::GetGlobalProperties()->GetValueProperties());
+ }
+}
+
+TargetProperties::~TargetProperties() = default;
+
+bool TargetProperties::GetInjectLocalVariables(
+ ExecutionContext *exe_ctx) const {
+ const Property *exp_property = m_collection_sp->GetPropertyAtIndex(
+ exe_ctx, false, ePropertyExperimental);
+ OptionValueProperties *exp_values =
+ exp_property->GetValue()->GetAsProperties();
+ if (exp_values)
+ return exp_values->GetPropertyAtIndexAsBoolean(
+ exe_ctx, ePropertyInjectLocalVars, true);
+ else
+ return true;
+}
+
+void TargetProperties::SetInjectLocalVariables(ExecutionContext *exe_ctx,
+ bool b) {
+ const Property *exp_property =
+ m_collection_sp->GetPropertyAtIndex(exe_ctx, true, ePropertyExperimental);
+ OptionValueProperties *exp_values =
+ exp_property->GetValue()->GetAsProperties();
+ if (exp_values)
+ exp_values->SetPropertyAtIndexAsBoolean(exe_ctx, ePropertyInjectLocalVars,
+ true);
+}
+
+bool TargetProperties::GetUseModernTypeLookup() const {
+ const Property *exp_property = m_collection_sp->GetPropertyAtIndex(
+ nullptr, false, ePropertyExperimental);
+ OptionValueProperties *exp_values =
+ exp_property->GetValue()->GetAsProperties();
+ if (exp_values)
+ return exp_values->GetPropertyAtIndexAsBoolean(
+ nullptr, ePropertyUseModernTypeLookup, true);
+ else
+ return true;
+}
+
+ArchSpec TargetProperties::GetDefaultArchitecture() const {
+ OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch(
+ nullptr, ePropertyDefaultArch);
+ if (value)
+ return value->GetCurrentValue();
+ return ArchSpec();
+}
+
+void TargetProperties::SetDefaultArchitecture(const ArchSpec &arch) {
+ OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch(
+ nullptr, ePropertyDefaultArch);
+ if (value)
+ return value->SetCurrentValue(arch, true);
+}
+
+bool TargetProperties::GetMoveToNearestCode() const {
+ const uint32_t idx = ePropertyMoveToNearestCode;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+lldb::DynamicValueType TargetProperties::GetPreferDynamicValue() const {
+ const uint32_t idx = ePropertyPreferDynamic;
+ return (lldb::DynamicValueType)
+ m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+bool TargetProperties::SetPreferDynamicValue(lldb::DynamicValueType d) {
+ const uint32_t idx = ePropertyPreferDynamic;
+ return m_collection_sp->SetPropertyAtIndexAsEnumeration(nullptr, idx, d);
+}
+
+bool TargetProperties::GetPreloadSymbols() const {
+ const uint32_t idx = ePropertyPreloadSymbols;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void TargetProperties::SetPreloadSymbols(bool b) {
+ const uint32_t idx = ePropertyPreloadSymbols;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+bool TargetProperties::GetDisableASLR() const {
+ const uint32_t idx = ePropertyDisableASLR;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void TargetProperties::SetDisableASLR(bool b) {
+ const uint32_t idx = ePropertyDisableASLR;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+bool TargetProperties::GetDetachOnError() const {
+ const uint32_t idx = ePropertyDetachOnError;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void TargetProperties::SetDetachOnError(bool b) {
+ const uint32_t idx = ePropertyDetachOnError;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+bool TargetProperties::GetDisableSTDIO() const {
+ const uint32_t idx = ePropertyDisableSTDIO;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void TargetProperties::SetDisableSTDIO(bool b) {
+ const uint32_t idx = ePropertyDisableSTDIO;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+const char *TargetProperties::GetDisassemblyFlavor() const {
+ const uint32_t idx = ePropertyDisassemblyFlavor;
+ const char *return_value;
+
+ x86DisassemblyFlavor flavor_value =
+ (x86DisassemblyFlavor)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_properties[idx].default_uint_value);
+ return_value = g_x86_dis_flavor_value_types[flavor_value].string_value;
+ return return_value;
+}
+
+InlineStrategy TargetProperties::GetInlineStrategy() const {
+ const uint32_t idx = ePropertyInlineStrategy;
+ return (InlineStrategy)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+llvm::StringRef TargetProperties::GetArg0() const {
+ const uint32_t idx = ePropertyArg0;
+ return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, llvm::StringRef());
+}
+
+void TargetProperties::SetArg0(llvm::StringRef arg) {
+ const uint32_t idx = ePropertyArg0;
+ m_collection_sp->SetPropertyAtIndexAsString(
+ nullptr, idx, arg);
+ m_launch_info.SetArg0(arg);
+}
+
+bool TargetProperties::GetRunArguments(Args &args) const {
+ const uint32_t idx = ePropertyRunArgs;
+ return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args);
+}
+
+void TargetProperties::SetRunArguments(const Args &args) {
+ const uint32_t idx = ePropertyRunArgs;
+ m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args);
+ m_launch_info.GetArguments() = args;
+}
+
+Environment TargetProperties::GetEnvironment() const {
+ // TODO: Get rid of the Args intermediate step
+ Args env;
+ const uint32_t idx = ePropertyEnvVars;
+ m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, env);
+ return Environment(env);
+}
+
+void TargetProperties::SetEnvironment(Environment env) {
+ // TODO: Get rid of the Args intermediate step
+ const uint32_t idx = ePropertyEnvVars;
+ m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, Args(env));
+ m_launch_info.GetEnvironment() = std::move(env);
+}
+
+bool TargetProperties::GetSkipPrologue() const {
+ const uint32_t idx = ePropertySkipPrologue;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+PathMappingList &TargetProperties::GetSourcePathMap() const {
+ const uint32_t idx = ePropertySourceMap;
+ OptionValuePathMappings *option_value =
+ m_collection_sp->GetPropertyAtIndexAsOptionValuePathMappings(nullptr,
+ false, idx);
+ assert(option_value);
+ return option_value->GetCurrentValue();
+}
+
+void TargetProperties::AppendExecutableSearchPaths(const FileSpec& dir) {
+ const uint32_t idx = ePropertyExecutableSearchPaths;
+ OptionValueFileSpecList *option_value =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr,
+ false, idx);
+ assert(option_value);
+ option_value->AppendCurrentValue(dir);
+}
+
+FileSpecList TargetProperties::GetExecutableSearchPaths() {
+ const uint32_t idx = ePropertyExecutableSearchPaths;
+ const OptionValueFileSpecList *option_value =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr,
+ false, idx);
+ assert(option_value);
+ return option_value->GetCurrentValue();
+}
+
+FileSpecList TargetProperties::GetDebugFileSearchPaths() {
+ const uint32_t idx = ePropertyDebugFileSearchPaths;
+ const OptionValueFileSpecList *option_value =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr,
+ false, idx);
+ assert(option_value);
+ return option_value->GetCurrentValue();
+}
+
+FileSpecList TargetProperties::GetClangModuleSearchPaths() {
+ const uint32_t idx = ePropertyClangModuleSearchPaths;
+ const OptionValueFileSpecList *option_value =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr,
+ false, idx);
+ assert(option_value);
+ return option_value->GetCurrentValue();
+}
+
+bool TargetProperties::GetEnableAutoImportClangModules() const {
+ const uint32_t idx = ePropertyAutoImportClangModules;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool TargetProperties::GetEnableImportStdModule() const {
+ const uint32_t idx = ePropertyImportStdModule;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool TargetProperties::GetEnableAutoApplyFixIts() const {
+ const uint32_t idx = ePropertyAutoApplyFixIts;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool TargetProperties::GetEnableNotifyAboutFixIts() const {
+ const uint32_t idx = ePropertyNotifyAboutFixIts;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool TargetProperties::GetEnableSaveObjects() const {
+ const uint32_t idx = ePropertySaveObjects;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool TargetProperties::GetEnableSyntheticValue() const {
+ const uint32_t idx = ePropertyEnableSynthetic;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+uint32_t TargetProperties::GetMaximumNumberOfChildrenToDisplay() const {
+ const uint32_t idx = ePropertyMaxChildrenCount;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+uint32_t TargetProperties::GetMaximumSizeOfStringSummary() const {
+ const uint32_t idx = ePropertyMaxSummaryLength;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+uint32_t TargetProperties::GetMaximumMemReadSize() const {
+ const uint32_t idx = ePropertyMaxMemReadSize;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+FileSpec TargetProperties::GetStandardInputPath() const {
+ const uint32_t idx = ePropertyInputPath;
+ return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx);
+}
+
+void TargetProperties::SetStandardInputPath(llvm::StringRef path) {
+ const uint32_t idx = ePropertyInputPath;
+ m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path);
+}
+
+FileSpec TargetProperties::GetStandardOutputPath() const {
+ const uint32_t idx = ePropertyOutputPath;
+ return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx);
+}
+
+void TargetProperties::SetStandardOutputPath(llvm::StringRef path) {
+ const uint32_t idx = ePropertyOutputPath;
+ m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path);
+}
+
+FileSpec TargetProperties::GetStandardErrorPath() const {
+ const uint32_t idx = ePropertyErrorPath;
+ return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx);
+}
+
+void TargetProperties::SetStandardErrorPath(llvm::StringRef path) {
+ const uint32_t idx = ePropertyErrorPath;
+ m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path);
+}
+
+LanguageType TargetProperties::GetLanguage() const {
+ OptionValueLanguage *value =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueLanguage(
+ nullptr, ePropertyLanguage);
+ if (value)
+ return value->GetCurrentValue();
+ return LanguageType();
+}
+
+llvm::StringRef TargetProperties::GetExpressionPrefixContents() {
+ const uint32_t idx = ePropertyExprPrefix;
+ OptionValueFileSpec *file =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpec(nullptr, false,
+ idx);
+ if (file) {
+ DataBufferSP data_sp(file->GetFileContents());
+ if (data_sp)
+ return llvm::StringRef(
+ reinterpret_cast<const char *>(data_sp->GetBytes()),
+ data_sp->GetByteSize());
+ }
+ return "";
+}
+
+bool TargetProperties::GetBreakpointsConsultPlatformAvoidList() {
+ const uint32_t idx = ePropertyBreakpointUseAvoidList;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool TargetProperties::GetUseHexImmediates() const {
+ const uint32_t idx = ePropertyUseHexImmediates;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool TargetProperties::GetUseFastStepping() const {
+ const uint32_t idx = ePropertyUseFastStepping;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool TargetProperties::GetDisplayExpressionsInCrashlogs() const {
+ const uint32_t idx = ePropertyDisplayExpressionsInCrashlogs;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+LoadScriptFromSymFile TargetProperties::GetLoadScriptFromSymbolFile() const {
+ const uint32_t idx = ePropertyLoadScriptFromSymbolFile;
+ return (LoadScriptFromSymFile)
+ m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+LoadCWDlldbinitFile TargetProperties::GetLoadCWDlldbinitFile() const {
+ const uint32_t idx = ePropertyLoadCWDlldbinitFile;
+ return (LoadCWDlldbinitFile)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+Disassembler::HexImmediateStyle TargetProperties::GetHexImmediateStyle() const {
+ const uint32_t idx = ePropertyHexImmediateStyle;
+ return (Disassembler::HexImmediateStyle)
+ m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+MemoryModuleLoadLevel TargetProperties::GetMemoryModuleLoadLevel() const {
+ const uint32_t idx = ePropertyMemoryModuleLoadLevel;
+ return (MemoryModuleLoadLevel)
+ m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+bool TargetProperties::GetUserSpecifiedTrapHandlerNames(Args &args) const {
+ const uint32_t idx = ePropertyTrapHandlerNames;
+ return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args);
+}
+
+void TargetProperties::SetUserSpecifiedTrapHandlerNames(const Args &args) {
+ const uint32_t idx = ePropertyTrapHandlerNames;
+ m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args);
+}
+
+bool TargetProperties::GetDisplayRuntimeSupportValues() const {
+ const uint32_t idx = ePropertyDisplayRuntimeSupportValues;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false);
+}
+
+void TargetProperties::SetDisplayRuntimeSupportValues(bool b) {
+ const uint32_t idx = ePropertyDisplayRuntimeSupportValues;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+bool TargetProperties::GetDisplayRecognizedArguments() const {
+ const uint32_t idx = ePropertyDisplayRecognizedArguments;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false);
+}
+
+void TargetProperties::SetDisplayRecognizedArguments(bool b) {
+ const uint32_t idx = ePropertyDisplayRecognizedArguments;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+bool TargetProperties::GetNonStopModeEnabled() const {
+ const uint32_t idx = ePropertyNonStopModeEnabled;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false);
+}
+
+void TargetProperties::SetNonStopModeEnabled(bool b) {
+ const uint32_t idx = ePropertyNonStopModeEnabled;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+const ProcessLaunchInfo &TargetProperties::GetProcessLaunchInfo() {
+ m_launch_info.SetArg0(GetArg0()); // FIXME: Arg0 callback doesn't work
+ return m_launch_info;
+}
+
+void TargetProperties::SetProcessLaunchInfo(
+ const ProcessLaunchInfo &launch_info) {
+ m_launch_info = launch_info;
+ SetArg0(launch_info.GetArg0());
+ SetRunArguments(launch_info.GetArguments());
+ SetEnvironment(launch_info.GetEnvironment());
+ const FileAction *input_file_action =
+ launch_info.GetFileActionForFD(STDIN_FILENO);
+ if (input_file_action) {
+ SetStandardInputPath(input_file_action->GetPath());
+ }
+ const FileAction *output_file_action =
+ launch_info.GetFileActionForFD(STDOUT_FILENO);
+ if (output_file_action) {
+ SetStandardOutputPath(output_file_action->GetPath());
+ }
+ const FileAction *error_file_action =
+ launch_info.GetFileActionForFD(STDERR_FILENO);
+ if (error_file_action) {
+ SetStandardErrorPath(error_file_action->GetPath());
+ }
+ SetDetachOnError(launch_info.GetFlags().Test(lldb::eLaunchFlagDetachOnError));
+ SetDisableASLR(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableASLR));
+ SetDisableSTDIO(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableSTDIO));
+}
+
+bool TargetProperties::GetRequireHardwareBreakpoints() const {
+ const uint32_t idx = ePropertyRequireHardwareBreakpoints;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void TargetProperties::SetRequireHardwareBreakpoints(bool b) {
+ const uint32_t idx = ePropertyRequireHardwareBreakpoints;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+void TargetProperties::Arg0ValueChangedCallback(void *target_property_ptr,
+ OptionValue *) {
+ TargetProperties *this_ =
+ reinterpret_cast<TargetProperties *>(target_property_ptr);
+ this_->m_launch_info.SetArg0(this_->GetArg0());
+}
+
+void TargetProperties::RunArgsValueChangedCallback(void *target_property_ptr,
+ OptionValue *) {
+ TargetProperties *this_ =
+ reinterpret_cast<TargetProperties *>(target_property_ptr);
+ Args args;
+ if (this_->GetRunArguments(args))
+ this_->m_launch_info.GetArguments() = args;
+}
+
+void TargetProperties::EnvVarsValueChangedCallback(void *target_property_ptr,
+ OptionValue *) {
+ TargetProperties *this_ =
+ reinterpret_cast<TargetProperties *>(target_property_ptr);
+ this_->m_launch_info.GetEnvironment() = this_->GetEnvironment();
+}
+
+void TargetProperties::InputPathValueChangedCallback(void *target_property_ptr,
+ OptionValue *) {
+ TargetProperties *this_ =
+ reinterpret_cast<TargetProperties *>(target_property_ptr);
+ this_->m_launch_info.AppendOpenFileAction(
+ STDIN_FILENO, this_->GetStandardInputPath(), true, false);
+}
+
+void TargetProperties::OutputPathValueChangedCallback(void *target_property_ptr,
+ OptionValue *) {
+ TargetProperties *this_ =
+ reinterpret_cast<TargetProperties *>(target_property_ptr);
+ this_->m_launch_info.AppendOpenFileAction(
+ STDOUT_FILENO, this_->GetStandardOutputPath(), false, true);
+}
+
+void TargetProperties::ErrorPathValueChangedCallback(void *target_property_ptr,
+ OptionValue *) {
+ TargetProperties *this_ =
+ reinterpret_cast<TargetProperties *>(target_property_ptr);
+ this_->m_launch_info.AppendOpenFileAction(
+ STDERR_FILENO, this_->GetStandardErrorPath(), false, true);
+}
+
+void TargetProperties::DetachOnErrorValueChangedCallback(
+ void *target_property_ptr, OptionValue *) {
+ TargetProperties *this_ =
+ reinterpret_cast<TargetProperties *>(target_property_ptr);
+ if (this_->GetDetachOnError())
+ this_->m_launch_info.GetFlags().Set(lldb::eLaunchFlagDetachOnError);
+ else
+ this_->m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDetachOnError);
+}
+
+void TargetProperties::DisableASLRValueChangedCallback(
+ void *target_property_ptr, OptionValue *) {
+ TargetProperties *this_ =
+ reinterpret_cast<TargetProperties *>(target_property_ptr);
+ if (this_->GetDisableASLR())
+ this_->m_launch_info.GetFlags().Set(lldb::eLaunchFlagDisableASLR);
+ else
+ this_->m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableASLR);
+}
+
+void TargetProperties::DisableSTDIOValueChangedCallback(
+ void *target_property_ptr, OptionValue *) {
+ TargetProperties *this_ =
+ reinterpret_cast<TargetProperties *>(target_property_ptr);
+ if (this_->GetDisableSTDIO())
+ this_->m_launch_info.GetFlags().Set(lldb::eLaunchFlagDisableSTDIO);
+ else
+ this_->m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableSTDIO);
+}
+
+// Target::TargetEventData
+
+Target::TargetEventData::TargetEventData(const lldb::TargetSP &target_sp)
+ : EventData(), m_target_sp(target_sp), m_module_list() {}
+
+Target::TargetEventData::TargetEventData(const lldb::TargetSP &target_sp,
+ const ModuleList &module_list)
+ : EventData(), m_target_sp(target_sp), m_module_list(module_list) {}
+
+Target::TargetEventData::~TargetEventData() = default;
+
+ConstString Target::TargetEventData::GetFlavorString() {
+ static ConstString g_flavor("Target::TargetEventData");
+ return g_flavor;
+}
+
+void Target::TargetEventData::Dump(Stream *s) const {
+ for (size_t i = 0; i < m_module_list.GetSize(); ++i) {
+ if (i != 0)
+ *s << ", ";
+ m_module_list.GetModuleAtIndex(i)->GetDescription(
+ s, lldb::eDescriptionLevelBrief);
+ }
+}
+
+const Target::TargetEventData *
+Target::TargetEventData::GetEventDataFromEvent(const Event *event_ptr) {
+ if (event_ptr) {
+ const EventData *event_data = event_ptr->GetData();
+ if (event_data &&
+ event_data->GetFlavor() == TargetEventData::GetFlavorString())
+ return static_cast<const TargetEventData *>(event_ptr->GetData());
+ }
+ return nullptr;
+}
+
+TargetSP Target::TargetEventData::GetTargetFromEvent(const Event *event_ptr) {
+ TargetSP target_sp;
+ const TargetEventData *event_data = GetEventDataFromEvent(event_ptr);
+ if (event_data)
+ target_sp = event_data->m_target_sp;
+ return target_sp;
+}
+
+ModuleList
+Target::TargetEventData::GetModuleListFromEvent(const Event *event_ptr) {
+ ModuleList module_list;
+ const TargetEventData *event_data = GetEventDataFromEvent(event_ptr);
+ if (event_data)
+ module_list = event_data->m_module_list;
+ return module_list;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/TargetList.cpp b/contrib/llvm-project/lldb/source/Target/TargetList.cpp
new file mode 100644
index 000000000000..7c7a36e97bbf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/TargetList.cpp
@@ -0,0 +1,620 @@
+//===-- TargetList.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/TargetList.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionGroupPlatform.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/Broadcaster.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/TildeExpressionResolver.h"
+#include "lldb/Utility/Timer.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ConstString &TargetList::GetStaticBroadcasterClass() {
+ static ConstString class_name("lldb.targetList");
+ return class_name;
+}
+
+// TargetList constructor
+TargetList::TargetList(Debugger &debugger)
+ : Broadcaster(debugger.GetBroadcasterManager(),
+ TargetList::GetStaticBroadcasterClass().AsCString()),
+ m_target_list(), m_target_list_mutex(), m_selected_target_idx(0) {
+ CheckInWithManager();
+}
+
+// Destructor
+TargetList::~TargetList() {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ m_target_list.clear();
+}
+
+Status TargetList::CreateTarget(Debugger &debugger,
+ llvm::StringRef user_exe_path,
+ llvm::StringRef triple_str,
+ LoadDependentFiles load_dependent_files,
+ const OptionGroupPlatform *platform_options,
+ TargetSP &target_sp) {
+ return CreateTargetInternal(debugger, user_exe_path, triple_str,
+ load_dependent_files, platform_options, target_sp,
+ false);
+}
+
+Status TargetList::CreateTarget(Debugger &debugger,
+ llvm::StringRef user_exe_path,
+ const ArchSpec &specified_arch,
+ LoadDependentFiles load_dependent_files,
+ PlatformSP &platform_sp, TargetSP &target_sp) {
+ return CreateTargetInternal(debugger, user_exe_path, specified_arch,
+ load_dependent_files, platform_sp, target_sp,
+ false);
+}
+
+Status TargetList::CreateTargetInternal(
+ Debugger &debugger, llvm::StringRef user_exe_path,
+ llvm::StringRef triple_str, LoadDependentFiles load_dependent_files,
+ const OptionGroupPlatform *platform_options, TargetSP &target_sp,
+ bool is_dummy_target) {
+ Status error;
+ PlatformSP platform_sp;
+
+ // This is purposely left empty unless it is specified by triple_cstr. If not
+ // initialized via triple_cstr, then the currently selected platform will set
+ // the architecture correctly.
+ const ArchSpec arch(triple_str);
+ if (!triple_str.empty()) {
+ if (!arch.IsValid()) {
+ error.SetErrorStringWithFormat("invalid triple '%s'",
+ triple_str.str().c_str());
+ return error;
+ }
+ }
+
+ ArchSpec platform_arch(arch);
+
+ bool prefer_platform_arch = false;
+
+ CommandInterpreter &interpreter = debugger.GetCommandInterpreter();
+
+ // let's see if there is already an existing platform before we go creating
+ // another...
+ platform_sp = debugger.GetPlatformList().GetSelectedPlatform();
+
+ if (platform_options && platform_options->PlatformWasSpecified()) {
+ // Create a new platform if it doesn't match the selected platform
+ if (!platform_options->PlatformMatches(platform_sp)) {
+ const bool select_platform = true;
+ platform_sp = platform_options->CreatePlatformWithOptions(
+ interpreter, arch, select_platform, error, platform_arch);
+ if (!platform_sp)
+ return error;
+ }
+ }
+
+ if (!user_exe_path.empty()) {
+ ModuleSpecList module_specs;
+ ModuleSpec module_spec;
+ module_spec.GetFileSpec().SetFile(user_exe_path, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(module_spec.GetFileSpec());
+
+ // Resolve the executable in case we are given a path to a application
+ // bundle like a .app bundle on MacOSX
+ Host::ResolveExecutableInBundle(module_spec.GetFileSpec());
+
+ lldb::offset_t file_offset = 0;
+ lldb::offset_t file_size = 0;
+ const size_t num_specs = ObjectFile::GetModuleSpecifications(
+ module_spec.GetFileSpec(), file_offset, file_size, module_specs);
+ if (num_specs > 0) {
+ ModuleSpec matching_module_spec;
+
+ if (num_specs == 1) {
+ if (module_specs.GetModuleSpecAtIndex(0, matching_module_spec)) {
+ if (platform_arch.IsValid()) {
+ if (platform_arch.IsCompatibleMatch(
+ matching_module_spec.GetArchitecture())) {
+ // If the OS or vendor weren't specified, then adopt the module's
+ // architecture so that the platform matching can be more
+ // accurate
+ if (!platform_arch.TripleOSWasSpecified() ||
+ !platform_arch.TripleVendorWasSpecified()) {
+ prefer_platform_arch = true;
+ platform_arch = matching_module_spec.GetArchitecture();
+ }
+ } else {
+ StreamString platform_arch_strm;
+ StreamString module_arch_strm;
+
+ platform_arch.DumpTriple(platform_arch_strm);
+ matching_module_spec.GetArchitecture().DumpTriple(
+ module_arch_strm);
+ error.SetErrorStringWithFormat(
+ "the specified architecture '%s' is not compatible with '%s' "
+ "in '%s'",
+ platform_arch_strm.GetData(), module_arch_strm.GetData(),
+ module_spec.GetFileSpec().GetPath().c_str());
+ return error;
+ }
+ } else {
+ // Only one arch and none was specified
+ prefer_platform_arch = true;
+ platform_arch = matching_module_spec.GetArchitecture();
+ }
+ }
+ } else {
+ if (arch.IsValid()) {
+ module_spec.GetArchitecture() = arch;
+ if (module_specs.FindMatchingModuleSpec(module_spec,
+ matching_module_spec)) {
+ prefer_platform_arch = true;
+ platform_arch = matching_module_spec.GetArchitecture();
+ }
+ } else {
+ // No architecture specified, check if there is only one platform for
+ // all of the architectures.
+
+ typedef std::vector<PlatformSP> PlatformList;
+ PlatformList platforms;
+ PlatformSP host_platform_sp = Platform::GetHostPlatform();
+ for (size_t i = 0; i < num_specs; ++i) {
+ ModuleSpec module_spec;
+ if (module_specs.GetModuleSpecAtIndex(i, module_spec)) {
+ // See if there was a selected platform and check that first
+ // since the user may have specified it.
+ if (platform_sp) {
+ if (platform_sp->IsCompatibleArchitecture(
+ module_spec.GetArchitecture(), false, nullptr)) {
+ platforms.push_back(platform_sp);
+ continue;
+ }
+ }
+
+ // Next check the host platform it if wasn't already checked
+ // above
+ if (host_platform_sp &&
+ (!platform_sp ||
+ host_platform_sp->GetName() != platform_sp->GetName())) {
+ if (host_platform_sp->IsCompatibleArchitecture(
+ module_spec.GetArchitecture(), false, nullptr)) {
+ platforms.push_back(host_platform_sp);
+ continue;
+ }
+ }
+
+ // Just find a platform that matches the architecture in the
+ // executable file
+ PlatformSP fallback_platform_sp(
+ Platform::GetPlatformForArchitecture(
+ module_spec.GetArchitecture(), nullptr));
+ if (fallback_platform_sp) {
+ platforms.push_back(fallback_platform_sp);
+ }
+ }
+ }
+
+ Platform *platform_ptr = nullptr;
+ bool more_than_one_platforms = false;
+ for (const auto &the_platform_sp : platforms) {
+ if (platform_ptr) {
+ if (platform_ptr->GetName() != the_platform_sp->GetName()) {
+ more_than_one_platforms = true;
+ platform_ptr = nullptr;
+ break;
+ }
+ } else {
+ platform_ptr = the_platform_sp.get();
+ }
+ }
+
+ if (platform_ptr) {
+ // All platforms for all modules in the executable match, so we can
+ // select this platform
+ platform_sp = platforms.front();
+ } else if (!more_than_one_platforms) {
+ // No platforms claim to support this file
+ error.SetErrorString("No matching platforms found for this file, "
+ "specify one with the --platform option");
+ return error;
+ } else {
+ // More than one platform claims to support this file, so the
+ // --platform option must be specified
+ StreamString error_strm;
+ std::set<Platform *> platform_set;
+ error_strm.Printf(
+ "more than one platform supports this executable (");
+ for (const auto &the_platform_sp : platforms) {
+ if (platform_set.find(the_platform_sp.get()) ==
+ platform_set.end()) {
+ if (!platform_set.empty())
+ error_strm.PutCString(", ");
+ error_strm.PutCString(the_platform_sp->GetName().GetCString());
+ platform_set.insert(the_platform_sp.get());
+ }
+ }
+ error_strm.Printf(
+ "), use the --platform option to specify a platform");
+ error.SetErrorString(error_strm.GetString());
+ return error;
+ }
+ }
+ }
+ }
+ }
+
+ // If we have a valid architecture, make sure the current platform is
+ // compatible with that architecture
+ if (!prefer_platform_arch && arch.IsValid()) {
+ if (!platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch)) {
+ platform_sp = Platform::GetPlatformForArchitecture(arch, &platform_arch);
+ if (!is_dummy_target && platform_sp)
+ debugger.GetPlatformList().SetSelectedPlatform(platform_sp);
+ }
+ } else if (platform_arch.IsValid()) {
+ // if "arch" isn't valid, yet "platform_arch" is, it means we have an
+ // executable file with a single architecture which should be used
+ ArchSpec fixed_platform_arch;
+ if (!platform_sp->IsCompatibleArchitecture(platform_arch, false,
+ &fixed_platform_arch)) {
+ platform_sp = Platform::GetPlatformForArchitecture(platform_arch,
+ &fixed_platform_arch);
+ if (!is_dummy_target && platform_sp)
+ debugger.GetPlatformList().SetSelectedPlatform(platform_sp);
+ }
+ }
+
+ if (!platform_arch.IsValid())
+ platform_arch = arch;
+
+ error = TargetList::CreateTargetInternal(
+ debugger, user_exe_path, platform_arch, load_dependent_files, platform_sp,
+ target_sp, is_dummy_target);
+ return error;
+}
+
+lldb::TargetSP TargetList::GetDummyTarget(lldb_private::Debugger &debugger) {
+ // FIXME: Maybe the dummy target should be per-Debugger
+ if (!m_dummy_target_sp || !m_dummy_target_sp->IsValid()) {
+ ArchSpec arch(Target::GetDefaultArchitecture());
+ if (!arch.IsValid())
+ arch = HostInfo::GetArchitecture();
+ Status err = CreateDummyTarget(
+ debugger, arch.GetTriple().getTriple().c_str(), m_dummy_target_sp);
+ }
+
+ return m_dummy_target_sp;
+}
+
+Status TargetList::CreateDummyTarget(Debugger &debugger,
+ llvm::StringRef specified_arch_name,
+ lldb::TargetSP &target_sp) {
+ PlatformSP host_platform_sp(Platform::GetHostPlatform());
+ return CreateTargetInternal(
+ debugger, (const char *)nullptr, specified_arch_name, eLoadDependentsNo,
+ (const OptionGroupPlatform *)nullptr, target_sp, true);
+}
+
+Status TargetList::CreateTargetInternal(Debugger &debugger,
+ llvm::StringRef user_exe_path,
+ const ArchSpec &specified_arch,
+ LoadDependentFiles load_dependent_files,
+ lldb::PlatformSP &platform_sp,
+ lldb::TargetSP &target_sp,
+ bool is_dummy_target) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat, "TargetList::CreateTarget (file = '%s', arch = '%s')",
+ user_exe_path.str().c_str(), specified_arch.GetArchitectureName());
+ Status error;
+
+ ArchSpec arch(specified_arch);
+
+ if (arch.IsValid()) {
+ if (!platform_sp ||
+ !platform_sp->IsCompatibleArchitecture(arch, false, nullptr))
+ platform_sp = Platform::GetPlatformForArchitecture(specified_arch, &arch);
+ }
+
+ if (!platform_sp)
+ platform_sp = debugger.GetPlatformList().GetSelectedPlatform();
+
+ if (!arch.IsValid())
+ arch = specified_arch;
+
+ FileSpec file(user_exe_path);
+ if (!FileSystem::Instance().Exists(file) && user_exe_path.startswith("~")) {
+ // we want to expand the tilde but we don't want to resolve any symbolic
+ // links so we can't use the FileSpec constructor's resolve flag
+ llvm::SmallString<64> unglobbed_path;
+ StandardTildeExpressionResolver Resolver;
+ Resolver.ResolveFullPath(user_exe_path, unglobbed_path);
+
+ if (unglobbed_path.empty())
+ file = FileSpec(user_exe_path);
+ else
+ file = FileSpec(unglobbed_path.c_str());
+ }
+
+ bool user_exe_path_is_bundle = false;
+ char resolved_bundle_exe_path[PATH_MAX];
+ resolved_bundle_exe_path[0] = '\0';
+ if (file) {
+ if (FileSystem::Instance().IsDirectory(file))
+ user_exe_path_is_bundle = true;
+
+ if (file.IsRelative() && !user_exe_path.empty()) {
+ llvm::SmallString<64> cwd;
+ if (! llvm::sys::fs::current_path(cwd)) {
+ FileSpec cwd_file(cwd.c_str());
+ cwd_file.AppendPathComponent(file);
+ if (FileSystem::Instance().Exists(cwd_file))
+ file = cwd_file;
+ }
+ }
+
+ ModuleSP exe_module_sp;
+ if (platform_sp) {
+ FileSpecList executable_search_paths(
+ Target::GetDefaultExecutableSearchPaths());
+ ModuleSpec module_spec(file, arch);
+ error = platform_sp->ResolveExecutable(module_spec, exe_module_sp,
+ executable_search_paths.GetSize()
+ ? &executable_search_paths
+ : nullptr);
+ }
+
+ if (error.Success() && exe_module_sp) {
+ if (exe_module_sp->GetObjectFile() == nullptr) {
+ if (arch.IsValid()) {
+ error.SetErrorStringWithFormat(
+ "\"%s\" doesn't contain architecture %s", file.GetPath().c_str(),
+ arch.GetArchitectureName());
+ } else {
+ error.SetErrorStringWithFormat("unsupported file type \"%s\"",
+ file.GetPath().c_str());
+ }
+ return error;
+ }
+ target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target));
+ target_sp->SetExecutableModule(exe_module_sp, load_dependent_files);
+ if (user_exe_path_is_bundle)
+ exe_module_sp->GetFileSpec().GetPath(resolved_bundle_exe_path,
+ sizeof(resolved_bundle_exe_path));
+ }
+ } else {
+ // No file was specified, just create an empty target with any arch if a
+ // valid arch was specified
+ target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target));
+ }
+
+ if (target_sp) {
+ // Set argv0 with what the user typed, unless the user specified a
+ // directory. If the user specified a directory, then it is probably a
+ // bundle that was resolved and we need to use the resolved bundle path
+ if (!user_exe_path.empty()) {
+ // Use exactly what the user typed as the first argument when we exec or
+ // posix_spawn
+ if (user_exe_path_is_bundle && resolved_bundle_exe_path[0]) {
+ target_sp->SetArg0(resolved_bundle_exe_path);
+ } else {
+ // Use resolved path
+ target_sp->SetArg0(file.GetPath().c_str());
+ }
+ }
+ if (file.GetDirectory()) {
+ FileSpec file_dir;
+ file_dir.GetDirectory() = file.GetDirectory();
+ target_sp->AppendExecutableSearchPaths(file_dir);
+ }
+
+ // Don't put the dummy target in the target list, it's held separately.
+ if (!is_dummy_target) {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ m_selected_target_idx = m_target_list.size();
+ m_target_list.push_back(target_sp);
+ // Now prime this from the dummy target:
+ target_sp->PrimeFromDummyTarget(debugger.GetDummyTarget());
+ } else {
+ m_dummy_target_sp = target_sp;
+ }
+ }
+
+ return error;
+}
+
+bool TargetList::DeleteTarget(TargetSP &target_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ collection::iterator pos, end = m_target_list.end();
+
+ for (pos = m_target_list.begin(); pos != end; ++pos) {
+ if (pos->get() == target_sp.get()) {
+ m_target_list.erase(pos);
+ return true;
+ }
+ }
+ return false;
+}
+
+TargetSP TargetList::FindTargetWithExecutableAndArchitecture(
+ const FileSpec &exe_file_spec, const ArchSpec *exe_arch_ptr) const {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ TargetSP target_sp;
+ bool full_match = (bool)exe_file_spec.GetDirectory();
+
+ collection::const_iterator pos, end = m_target_list.end();
+ for (pos = m_target_list.begin(); pos != end; ++pos) {
+ Module *exe_module = (*pos)->GetExecutableModulePointer();
+
+ if (exe_module) {
+ if (FileSpec::Equal(exe_file_spec, exe_module->GetFileSpec(),
+ full_match)) {
+ if (exe_arch_ptr) {
+ if (!exe_arch_ptr->IsCompatibleMatch(exe_module->GetArchitecture()))
+ continue;
+ }
+ target_sp = *pos;
+ break;
+ }
+ }
+ }
+ return target_sp;
+}
+
+TargetSP TargetList::FindTargetWithProcessID(lldb::pid_t pid) const {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ TargetSP target_sp;
+ collection::const_iterator pos, end = m_target_list.end();
+ for (pos = m_target_list.begin(); pos != end; ++pos) {
+ Process *process = (*pos)->GetProcessSP().get();
+ if (process && process->GetID() == pid) {
+ target_sp = *pos;
+ break;
+ }
+ }
+ return target_sp;
+}
+
+TargetSP TargetList::FindTargetWithProcess(Process *process) const {
+ TargetSP target_sp;
+ if (process) {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ collection::const_iterator pos, end = m_target_list.end();
+ for (pos = m_target_list.begin(); pos != end; ++pos) {
+ if (process == (*pos)->GetProcessSP().get()) {
+ target_sp = *pos;
+ break;
+ }
+ }
+ }
+ return target_sp;
+}
+
+TargetSP TargetList::GetTargetSP(Target *target) const {
+ TargetSP target_sp;
+ if (target) {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ collection::const_iterator pos, end = m_target_list.end();
+ for (pos = m_target_list.begin(); pos != end; ++pos) {
+ if (target == (*pos).get()) {
+ target_sp = *pos;
+ break;
+ }
+ }
+ }
+ return target_sp;
+}
+
+uint32_t TargetList::SendAsyncInterrupt(lldb::pid_t pid) {
+ uint32_t num_async_interrupts_sent = 0;
+
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ TargetSP target_sp(FindTargetWithProcessID(pid));
+ if (target_sp) {
+ Process *process = target_sp->GetProcessSP().get();
+ if (process) {
+ process->SendAsyncInterrupt();
+ ++num_async_interrupts_sent;
+ }
+ }
+ } else {
+ // We don't have a valid pid to broadcast to, so broadcast to the target
+ // list's async broadcaster...
+ BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr);
+ }
+
+ return num_async_interrupts_sent;
+}
+
+uint32_t TargetList::SignalIfRunning(lldb::pid_t pid, int signo) {
+ uint32_t num_signals_sent = 0;
+ Process *process = nullptr;
+ if (pid == LLDB_INVALID_PROCESS_ID) {
+ // Signal all processes with signal
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ collection::iterator pos, end = m_target_list.end();
+ for (pos = m_target_list.begin(); pos != end; ++pos) {
+ process = (*pos)->GetProcessSP().get();
+ if (process) {
+ if (process->IsAlive()) {
+ ++num_signals_sent;
+ process->Signal(signo);
+ }
+ }
+ }
+ } else {
+ // Signal a specific process with signal
+ TargetSP target_sp(FindTargetWithProcessID(pid));
+ if (target_sp) {
+ process = target_sp->GetProcessSP().get();
+ if (process) {
+ if (process->IsAlive()) {
+ ++num_signals_sent;
+ process->Signal(signo);
+ }
+ }
+ }
+ }
+ return num_signals_sent;
+}
+
+int TargetList::GetNumTargets() const {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ return m_target_list.size();
+}
+
+lldb::TargetSP TargetList::GetTargetAtIndex(uint32_t idx) const {
+ TargetSP target_sp;
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ if (idx < m_target_list.size())
+ target_sp = m_target_list[idx];
+ return target_sp;
+}
+
+uint32_t TargetList::GetIndexOfTarget(lldb::TargetSP target_sp) const {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ size_t num_targets = m_target_list.size();
+ for (size_t idx = 0; idx < num_targets; idx++) {
+ if (target_sp == m_target_list[idx])
+ return idx;
+ }
+ return UINT32_MAX;
+}
+
+uint32_t TargetList::SetSelectedTarget(Target *target) {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ collection::const_iterator pos, begin = m_target_list.begin(),
+ end = m_target_list.end();
+ for (pos = begin; pos != end; ++pos) {
+ if (pos->get() == target) {
+ m_selected_target_idx = std::distance(begin, pos);
+ return m_selected_target_idx;
+ }
+ }
+ m_selected_target_idx = 0;
+ return m_selected_target_idx;
+}
+
+lldb::TargetSP TargetList::GetSelectedTarget() {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ if (m_selected_target_idx >= m_target_list.size())
+ m_selected_target_idx = 0;
+ return GetTargetAtIndex(m_selected_target_idx);
+}
diff --git a/contrib/llvm-project/lldb/source/Target/Thread.cpp b/contrib/llvm-project/lldb/source/Target/Thread.cpp
new file mode 100644
index 000000000000..7a6b49e55252
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/Thread.cpp
@@ -0,0 +1,2235 @@
+//===-- Thread.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/Thread.h"
+#include "Plugins/Process/Utility/UnwindLLDB.h"
+#include "Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FormatEntity.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Interpreter/OptionValueFileSpecList.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrameRecognizer.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanBase.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Target/ThreadPlanPython.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Target/ThreadPlanStepInRange.h"
+#include "lldb/Target/ThreadPlanStepInstruction.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Target/ThreadPlanStepOverBreakpoint.h"
+#include "lldb/Target/ThreadPlanStepOverRange.h"
+#include "lldb/Target/ThreadPlanStepThrough.h"
+#include "lldb/Target/ThreadPlanStepUntil.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-enumerations.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+const ThreadPropertiesSP &Thread::GetGlobalProperties() {
+ // NOTE: intentional leak so we don't crash if global destructor chain gets
+ // called as other threads still use the result of this function
+ static ThreadPropertiesSP *g_settings_sp_ptr =
+ new ThreadPropertiesSP(new ThreadProperties(true));
+ return *g_settings_sp_ptr;
+}
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"step-in-avoid-nodebug", OptionValue::eTypeBoolean, true, true, nullptr,
+ {},
+ "If true, step-in will not stop in functions with no debug information."},
+ {"step-out-avoid-nodebug", OptionValue::eTypeBoolean, true, false, nullptr,
+ {}, "If true, when step-in/step-out/step-over leave the current frame, "
+ "they will continue to step out till they come to a function with "
+ "debug information. Passing a frame argument to step-out will "
+ "override this option."},
+ {"step-avoid-regexp", OptionValue::eTypeRegex, true, 0, "^std::", {},
+ "A regular expression defining functions step-in won't stop in."},
+ {"step-avoid-libraries", OptionValue::eTypeFileSpecList, true, 0, nullptr,
+ {}, "A list of libraries that source stepping won't stop in."},
+ {"trace-thread", OptionValue::eTypeBoolean, false, false, nullptr, {},
+ "If true, this thread will single-step and log execution."},
+ {"max-backtrace-depth", OptionValue::eTypeUInt64, false, 300000, nullptr,
+ {}, "Maximum number of frames to backtrace."}};
+
+enum {
+ ePropertyStepInAvoidsNoDebug,
+ ePropertyStepOutAvoidsNoDebug,
+ ePropertyStepAvoidRegex,
+ ePropertyStepAvoidLibraries,
+ ePropertyEnableThreadTrace,
+ ePropertyMaxBacktraceDepth
+};
+
+class ThreadOptionValueProperties : public OptionValueProperties {
+public:
+ ThreadOptionValueProperties(ConstString name)
+ : OptionValueProperties(name) {}
+
+ // This constructor is used when creating ThreadOptionValueProperties when it
+ // is part of a new lldb_private::Thread instance. It will copy all current
+ // global property values as needed
+ ThreadOptionValueProperties(ThreadProperties *global_properties)
+ : OptionValueProperties(*global_properties->GetValueProperties()) {}
+
+ const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx,
+ bool will_modify,
+ uint32_t idx) const override {
+ // When getting the value for a key from the thread options, we will always
+ // try and grab the setting from the current thread if there is one. Else
+ // we just use the one from this instance.
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ ThreadOptionValueProperties *instance_properties =
+ static_cast<ThreadOptionValueProperties *>(
+ thread->GetValueProperties().get());
+ if (this != instance_properties)
+ return instance_properties->ProtectedGetPropertyAtIndex(idx);
+ }
+ }
+ return ProtectedGetPropertyAtIndex(idx);
+ }
+};
+
+ThreadProperties::ThreadProperties(bool is_global) : Properties() {
+ if (is_global) {
+ m_collection_sp =
+ std::make_shared<ThreadOptionValueProperties>(ConstString("thread"));
+ m_collection_sp->Initialize(g_properties);
+ } else
+ m_collection_sp = std::make_shared<ThreadOptionValueProperties>(
+ Thread::GetGlobalProperties().get());
+}
+
+ThreadProperties::~ThreadProperties() = default;
+
+const RegularExpression *ThreadProperties::GetSymbolsToAvoidRegexp() {
+ const uint32_t idx = ePropertyStepAvoidRegex;
+ return m_collection_sp->GetPropertyAtIndexAsOptionValueRegex(nullptr, idx);
+}
+
+FileSpecList ThreadProperties::GetLibrariesToAvoid() const {
+ const uint32_t idx = ePropertyStepAvoidLibraries;
+ const OptionValueFileSpecList *option_value =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr,
+ false, idx);
+ assert(option_value);
+ return option_value->GetCurrentValue();
+}
+
+bool ThreadProperties::GetTraceEnabledState() const {
+ const uint32_t idx = ePropertyEnableThreadTrace;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool ThreadProperties::GetStepInAvoidsNoDebug() const {
+ const uint32_t idx = ePropertyStepInAvoidsNoDebug;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool ThreadProperties::GetStepOutAvoidsNoDebug() const {
+ const uint32_t idx = ePropertyStepOutAvoidsNoDebug;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+uint64_t ThreadProperties::GetMaxBacktraceDepth() const {
+ const uint32_t idx = ePropertyMaxBacktraceDepth;
+ return m_collection_sp->GetPropertyAtIndexAsUInt64(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+}
+
+// Thread Event Data
+
+ConstString Thread::ThreadEventData::GetFlavorString() {
+ static ConstString g_flavor("Thread::ThreadEventData");
+ return g_flavor;
+}
+
+Thread::ThreadEventData::ThreadEventData(const lldb::ThreadSP thread_sp)
+ : m_thread_sp(thread_sp), m_stack_id() {}
+
+Thread::ThreadEventData::ThreadEventData(const lldb::ThreadSP thread_sp,
+ const StackID &stack_id)
+ : m_thread_sp(thread_sp), m_stack_id(stack_id) {}
+
+Thread::ThreadEventData::ThreadEventData() : m_thread_sp(), m_stack_id() {}
+
+Thread::ThreadEventData::~ThreadEventData() = default;
+
+void Thread::ThreadEventData::Dump(Stream *s) const {}
+
+const Thread::ThreadEventData *
+Thread::ThreadEventData::GetEventDataFromEvent(const Event *event_ptr) {
+ if (event_ptr) {
+ const EventData *event_data = event_ptr->GetData();
+ if (event_data &&
+ event_data->GetFlavor() == ThreadEventData::GetFlavorString())
+ return static_cast<const ThreadEventData *>(event_ptr->GetData());
+ }
+ return nullptr;
+}
+
+ThreadSP Thread::ThreadEventData::GetThreadFromEvent(const Event *event_ptr) {
+ ThreadSP thread_sp;
+ const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr);
+ if (event_data)
+ thread_sp = event_data->GetThread();
+ return thread_sp;
+}
+
+StackID Thread::ThreadEventData::GetStackIDFromEvent(const Event *event_ptr) {
+ StackID stack_id;
+ const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr);
+ if (event_data)
+ stack_id = event_data->GetStackID();
+ return stack_id;
+}
+
+StackFrameSP
+Thread::ThreadEventData::GetStackFrameFromEvent(const Event *event_ptr) {
+ const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr);
+ StackFrameSP frame_sp;
+ if (event_data) {
+ ThreadSP thread_sp = event_data->GetThread();
+ if (thread_sp) {
+ frame_sp = thread_sp->GetStackFrameList()->GetFrameWithStackID(
+ event_data->GetStackID());
+ }
+ }
+ return frame_sp;
+}
+
+// Thread class
+
+ConstString &Thread::GetStaticBroadcasterClass() {
+ static ConstString class_name("lldb.thread");
+ return class_name;
+}
+
+Thread::Thread(Process &process, lldb::tid_t tid, bool use_invalid_index_id)
+ : ThreadProperties(false), UserID(tid),
+ Broadcaster(process.GetTarget().GetDebugger().GetBroadcasterManager(),
+ Thread::GetStaticBroadcasterClass().AsCString()),
+ m_process_wp(process.shared_from_this()), m_stop_info_sp(),
+ m_stop_info_stop_id(0), m_stop_info_override_stop_id(0),
+ m_index_id(use_invalid_index_id ? LLDB_INVALID_INDEX32
+ : process.GetNextThreadIndexID(tid)),
+ m_reg_context_sp(), m_state(eStateUnloaded), m_state_mutex(),
+ m_plan_stack(), m_completed_plan_stack(), m_frame_mutex(),
+ m_curr_frames_sp(), m_prev_frames_sp(),
+ m_resume_signal(LLDB_INVALID_SIGNAL_NUMBER),
+ m_resume_state(eStateRunning), m_temporary_resume_state(eStateRunning),
+ m_unwinder_up(), m_destroy_called(false),
+ m_override_should_notify(eLazyBoolCalculate),
+ m_extended_info_fetched(false), m_extended_info() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p Thread::Thread(tid = 0x%4.4" PRIx64 ")",
+ static_cast<void *>(this), GetID());
+
+ CheckInWithManager();
+
+ QueueFundamentalPlan(true);
+}
+
+Thread::~Thread() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p Thread::~Thread(tid = 0x%4.4" PRIx64 ")",
+ static_cast<void *>(this), GetID());
+ /// If you hit this assert, it means your derived class forgot to call
+ /// DoDestroy in its destructor.
+ assert(m_destroy_called);
+}
+
+void Thread::DestroyThread() {
+ // Tell any plans on the plan stacks that the thread is being destroyed since
+ // any plans that have a thread go away in the middle of might need to do
+ // cleanup, or in some cases NOT do cleanup...
+ for (auto plan : m_plan_stack)
+ plan->ThreadDestroyed();
+
+ for (auto plan : m_discarded_plan_stack)
+ plan->ThreadDestroyed();
+
+ for (auto plan : m_completed_plan_stack)
+ plan->ThreadDestroyed();
+
+ m_destroy_called = true;
+ m_plan_stack.clear();
+ m_discarded_plan_stack.clear();
+ m_completed_plan_stack.clear();
+
+ // Push a ThreadPlanNull on the plan stack. That way we can continue
+ // assuming that the plan stack is never empty, but if somebody errantly asks
+ // questions of a destroyed thread without checking first whether it is
+ // destroyed, they won't crash.
+ ThreadPlanSP null_plan_sp(new ThreadPlanNull(*this));
+ m_plan_stack.push_back(null_plan_sp);
+
+ m_stop_info_sp.reset();
+ m_reg_context_sp.reset();
+ m_unwinder_up.reset();
+ std::lock_guard<std::recursive_mutex> guard(m_frame_mutex);
+ m_curr_frames_sp.reset();
+ m_prev_frames_sp.reset();
+}
+
+void Thread::BroadcastSelectedFrameChange(StackID &new_frame_id) {
+ if (EventTypeHasListeners(eBroadcastBitSelectedFrameChanged))
+ BroadcastEvent(eBroadcastBitSelectedFrameChanged,
+ new ThreadEventData(this->shared_from_this(), new_frame_id));
+}
+
+lldb::StackFrameSP Thread::GetSelectedFrame() {
+ StackFrameListSP stack_frame_list_sp(GetStackFrameList());
+ StackFrameSP frame_sp = stack_frame_list_sp->GetFrameAtIndex(
+ stack_frame_list_sp->GetSelectedFrameIndex());
+ FunctionOptimizationWarning(frame_sp.get());
+ return frame_sp;
+}
+
+uint32_t Thread::SetSelectedFrame(lldb_private::StackFrame *frame,
+ bool broadcast) {
+ uint32_t ret_value = GetStackFrameList()->SetSelectedFrame(frame);
+ if (broadcast)
+ BroadcastSelectedFrameChange(frame->GetStackID());
+ FunctionOptimizationWarning(frame);
+ return ret_value;
+}
+
+bool Thread::SetSelectedFrameByIndex(uint32_t frame_idx, bool broadcast) {
+ StackFrameSP frame_sp(GetStackFrameList()->GetFrameAtIndex(frame_idx));
+ if (frame_sp) {
+ GetStackFrameList()->SetSelectedFrame(frame_sp.get());
+ if (broadcast)
+ BroadcastSelectedFrameChange(frame_sp->GetStackID());
+ FunctionOptimizationWarning(frame_sp.get());
+ return true;
+ } else
+ return false;
+}
+
+bool Thread::SetSelectedFrameByIndexNoisily(uint32_t frame_idx,
+ Stream &output_stream) {
+ const bool broadcast = true;
+ bool success = SetSelectedFrameByIndex(frame_idx, broadcast);
+ if (success) {
+ StackFrameSP frame_sp = GetSelectedFrame();
+ if (frame_sp) {
+ bool already_shown = false;
+ SymbolContext frame_sc(
+ frame_sp->GetSymbolContext(eSymbolContextLineEntry));
+ if (GetProcess()->GetTarget().GetDebugger().GetUseExternalEditor() &&
+ frame_sc.line_entry.file && frame_sc.line_entry.line != 0) {
+ already_shown = Host::OpenFileInExternalEditor(
+ frame_sc.line_entry.file, frame_sc.line_entry.line);
+ }
+
+ bool show_frame_info = true;
+ bool show_source = !already_shown;
+ FunctionOptimizationWarning(frame_sp.get());
+ return frame_sp->GetStatus(output_stream, show_frame_info, show_source);
+ }
+ return false;
+ } else
+ return false;
+}
+
+void Thread::FunctionOptimizationWarning(StackFrame *frame) {
+ if (frame && frame->HasDebugInformation() &&
+ GetProcess()->GetWarningsOptimization()) {
+ SymbolContext sc =
+ frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextModule);
+ GetProcess()->PrintWarningOptimization(sc);
+ }
+}
+
+lldb::StopInfoSP Thread::GetStopInfo() {
+ if (m_destroy_called)
+ return m_stop_info_sp;
+
+ ThreadPlanSP completed_plan_sp(GetCompletedPlan());
+ ProcessSP process_sp(GetProcess());
+ const uint32_t stop_id = process_sp ? process_sp->GetStopID() : UINT32_MAX;
+
+ // Here we select the stop info according to priorirty: - m_stop_info_sp (if
+ // not trace) - preset value - completed plan stop info - new value with plan
+ // from completed plan stack - m_stop_info_sp (trace stop reason is OK now) -
+ // ask GetPrivateStopInfo to set stop info
+
+ bool have_valid_stop_info = m_stop_info_sp &&
+ m_stop_info_sp ->IsValid() &&
+ m_stop_info_stop_id == stop_id;
+ bool have_valid_completed_plan = completed_plan_sp && completed_plan_sp->PlanSucceeded();
+ bool plan_failed = completed_plan_sp && !completed_plan_sp->PlanSucceeded();
+ bool plan_overrides_trace =
+ have_valid_stop_info && have_valid_completed_plan
+ && (m_stop_info_sp->GetStopReason() == eStopReasonTrace);
+
+ if (have_valid_stop_info && !plan_overrides_trace && !plan_failed) {
+ return m_stop_info_sp;
+ } else if (completed_plan_sp) {
+ return StopInfo::CreateStopReasonWithPlan(
+ completed_plan_sp, GetReturnValueObject(), GetExpressionVariable());
+ } else {
+ GetPrivateStopInfo();
+ return m_stop_info_sp;
+ }
+}
+
+lldb::StopInfoSP Thread::GetPrivateStopInfo() {
+ if (m_destroy_called)
+ return m_stop_info_sp;
+
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ const uint32_t process_stop_id = process_sp->GetStopID();
+ if (m_stop_info_stop_id != process_stop_id) {
+ if (m_stop_info_sp) {
+ if (m_stop_info_sp->IsValid() || IsStillAtLastBreakpointHit() ||
+ GetCurrentPlan()->IsVirtualStep())
+ SetStopInfo(m_stop_info_sp);
+ else
+ m_stop_info_sp.reset();
+ }
+
+ if (!m_stop_info_sp) {
+ if (!CalculateStopInfo())
+ SetStopInfo(StopInfoSP());
+ }
+ }
+
+ // The stop info can be manually set by calling Thread::SetStopInfo() prior
+ // to this function ever getting called, so we can't rely on
+ // "m_stop_info_stop_id != process_stop_id" as the condition for the if
+ // statement below, we must also check the stop info to see if we need to
+ // override it. See the header documentation in
+ // Process::GetStopInfoOverrideCallback() for more information on the stop
+ // info override callback.
+ if (m_stop_info_override_stop_id != process_stop_id) {
+ m_stop_info_override_stop_id = process_stop_id;
+ if (m_stop_info_sp) {
+ if (const Architecture *arch =
+ process_sp->GetTarget().GetArchitecturePlugin())
+ arch->OverrideStopInfo(*this);
+ }
+ }
+ }
+ return m_stop_info_sp;
+}
+
+lldb::StopReason Thread::GetStopReason() {
+ lldb::StopInfoSP stop_info_sp(GetStopInfo());
+ if (stop_info_sp)
+ return stop_info_sp->GetStopReason();
+ return eStopReasonNone;
+}
+
+bool Thread::StopInfoIsUpToDate() const {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp)
+ return m_stop_info_stop_id == process_sp->GetStopID();
+ else
+ return true; // Process is no longer around so stop info is always up to
+ // date...
+}
+
+void Thread::ResetStopInfo() {
+ if (m_stop_info_sp) {
+ m_stop_info_sp.reset();
+ }
+}
+
+void Thread::SetStopInfo(const lldb::StopInfoSP &stop_info_sp) {
+ m_stop_info_sp = stop_info_sp;
+ if (m_stop_info_sp) {
+ m_stop_info_sp->MakeStopInfoValid();
+ // If we are overriding the ShouldReportStop, do that here:
+ if (m_override_should_notify != eLazyBoolCalculate)
+ m_stop_info_sp->OverrideShouldNotify(m_override_should_notify ==
+ eLazyBoolYes);
+ }
+
+ ProcessSP process_sp(GetProcess());
+ if (process_sp)
+ m_stop_info_stop_id = process_sp->GetStopID();
+ else
+ m_stop_info_stop_id = UINT32_MAX;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("%p: tid = 0x%" PRIx64 ": stop info = %s (stop_id = %u)",
+ static_cast<void *>(this), GetID(),
+ stop_info_sp ? stop_info_sp->GetDescription() : "<NULL>",
+ m_stop_info_stop_id);
+}
+
+void Thread::SetShouldReportStop(Vote vote) {
+ if (vote == eVoteNoOpinion)
+ return;
+ else {
+ m_override_should_notify = (vote == eVoteYes ? eLazyBoolYes : eLazyBoolNo);
+ if (m_stop_info_sp)
+ m_stop_info_sp->OverrideShouldNotify(m_override_should_notify ==
+ eLazyBoolYes);
+ }
+}
+
+void Thread::SetStopInfoToNothing() {
+ // Note, we can't just NULL out the private reason, or the native thread
+ // implementation will try to go calculate it again. For now, just set it to
+ // a Unix Signal with an invalid signal number.
+ SetStopInfo(
+ StopInfo::CreateStopReasonWithSignal(*this, LLDB_INVALID_SIGNAL_NUMBER));
+}
+
+bool Thread::ThreadStoppedForAReason(void) {
+ return (bool)GetPrivateStopInfo();
+}
+
+bool Thread::CheckpointThreadState(ThreadStateCheckpoint &saved_state) {
+ saved_state.register_backup_sp.reset();
+ lldb::StackFrameSP frame_sp(GetStackFrameAtIndex(0));
+ if (frame_sp) {
+ lldb::RegisterCheckpointSP reg_checkpoint_sp(
+ new RegisterCheckpoint(RegisterCheckpoint::Reason::eExpression));
+ if (reg_checkpoint_sp) {
+ lldb::RegisterContextSP reg_ctx_sp(frame_sp->GetRegisterContext());
+ if (reg_ctx_sp && reg_ctx_sp->ReadAllRegisterValues(*reg_checkpoint_sp))
+ saved_state.register_backup_sp = reg_checkpoint_sp;
+ }
+ }
+ if (!saved_state.register_backup_sp)
+ return false;
+
+ saved_state.stop_info_sp = GetStopInfo();
+ ProcessSP process_sp(GetProcess());
+ if (process_sp)
+ saved_state.orig_stop_id = process_sp->GetStopID();
+ saved_state.current_inlined_depth = GetCurrentInlinedDepth();
+ saved_state.m_completed_plan_stack = m_completed_plan_stack;
+
+ return true;
+}
+
+bool Thread::RestoreRegisterStateFromCheckpoint(
+ ThreadStateCheckpoint &saved_state) {
+ if (saved_state.register_backup_sp) {
+ lldb::StackFrameSP frame_sp(GetStackFrameAtIndex(0));
+ if (frame_sp) {
+ lldb::RegisterContextSP reg_ctx_sp(frame_sp->GetRegisterContext());
+ if (reg_ctx_sp) {
+ bool ret =
+ reg_ctx_sp->WriteAllRegisterValues(*saved_state.register_backup_sp);
+
+ // Clear out all stack frames as our world just changed.
+ ClearStackFrames();
+ reg_ctx_sp->InvalidateIfNeeded(true);
+ if (m_unwinder_up)
+ m_unwinder_up->Clear();
+ return ret;
+ }
+ }
+ }
+ return false;
+}
+
+bool Thread::RestoreThreadStateFromCheckpoint(
+ ThreadStateCheckpoint &saved_state) {
+ if (saved_state.stop_info_sp)
+ saved_state.stop_info_sp->MakeStopInfoValid();
+ SetStopInfo(saved_state.stop_info_sp);
+ GetStackFrameList()->SetCurrentInlinedDepth(
+ saved_state.current_inlined_depth);
+ m_completed_plan_stack = saved_state.m_completed_plan_stack;
+ return true;
+}
+
+StateType Thread::GetState() const {
+ // If any other threads access this we will need a mutex for it
+ std::lock_guard<std::recursive_mutex> guard(m_state_mutex);
+ return m_state;
+}
+
+void Thread::SetState(StateType state) {
+ std::lock_guard<std::recursive_mutex> guard(m_state_mutex);
+ m_state = state;
+}
+
+void Thread::WillStop() {
+ ThreadPlan *current_plan = GetCurrentPlan();
+
+ // FIXME: I may decide to disallow threads with no plans. In which
+ // case this should go to an assert.
+
+ if (!current_plan)
+ return;
+
+ current_plan->WillStop();
+}
+
+void Thread::SetupForResume() {
+ if (GetResumeState() != eStateSuspended) {
+ // If we're at a breakpoint push the step-over breakpoint plan. Do this
+ // before telling the current plan it will resume, since we might change
+ // what the current plan is.
+
+ lldb::RegisterContextSP reg_ctx_sp(GetRegisterContext());
+ if (reg_ctx_sp) {
+ const addr_t thread_pc = reg_ctx_sp->GetPC();
+ BreakpointSiteSP bp_site_sp =
+ GetProcess()->GetBreakpointSiteList().FindByAddress(thread_pc);
+ if (bp_site_sp) {
+ // Note, don't assume there's a ThreadPlanStepOverBreakpoint, the
+ // target may not require anything special to step over a breakpoint.
+
+ ThreadPlan *cur_plan = GetCurrentPlan();
+
+ bool push_step_over_bp_plan = false;
+ if (cur_plan->GetKind() == ThreadPlan::eKindStepOverBreakpoint) {
+ ThreadPlanStepOverBreakpoint *bp_plan =
+ (ThreadPlanStepOverBreakpoint *)cur_plan;
+ if (bp_plan->GetBreakpointLoadAddress() != thread_pc)
+ push_step_over_bp_plan = true;
+ } else
+ push_step_over_bp_plan = true;
+
+ if (push_step_over_bp_plan) {
+ ThreadPlanSP step_bp_plan_sp(new ThreadPlanStepOverBreakpoint(*this));
+ if (step_bp_plan_sp) {
+ step_bp_plan_sp->SetPrivate(true);
+
+ if (GetCurrentPlan()->RunState() != eStateStepping) {
+ ThreadPlanStepOverBreakpoint *step_bp_plan =
+ static_cast<ThreadPlanStepOverBreakpoint *>(
+ step_bp_plan_sp.get());
+ step_bp_plan->SetAutoContinue(true);
+ }
+ QueueThreadPlan(step_bp_plan_sp, false);
+ }
+ }
+ }
+ }
+ }
+}
+
+bool Thread::ShouldResume(StateType resume_state) {
+ // At this point clear the completed plan stack.
+ m_completed_plan_stack.clear();
+ m_discarded_plan_stack.clear();
+ m_override_should_notify = eLazyBoolCalculate;
+
+ StateType prev_resume_state = GetTemporaryResumeState();
+
+ SetTemporaryResumeState(resume_state);
+
+ lldb::ThreadSP backing_thread_sp(GetBackingThread());
+ if (backing_thread_sp)
+ backing_thread_sp->SetTemporaryResumeState(resume_state);
+
+ // Make sure m_stop_info_sp is valid. Don't do this for threads we suspended
+ // in the previous run.
+ if (prev_resume_state != eStateSuspended)
+ GetPrivateStopInfo();
+
+ // This is a little dubious, but we are trying to limit how often we actually
+ // fetch stop info from the target, 'cause that slows down single stepping.
+ // So assume that if we got to the point where we're about to resume, and we
+ // haven't yet had to fetch the stop reason, then it doesn't need to know
+ // about the fact that we are resuming...
+ const uint32_t process_stop_id = GetProcess()->GetStopID();
+ if (m_stop_info_stop_id == process_stop_id &&
+ (m_stop_info_sp && m_stop_info_sp->IsValid())) {
+ StopInfo *stop_info = GetPrivateStopInfo().get();
+ if (stop_info)
+ stop_info->WillResume(resume_state);
+ }
+
+ // Tell all the plans that we are about to resume in case they need to clear
+ // any state. We distinguish between the plan on the top of the stack and the
+ // lower plans in case a plan needs to do any special business before it
+ // runs.
+
+ bool need_to_resume = false;
+ ThreadPlan *plan_ptr = GetCurrentPlan();
+ if (plan_ptr) {
+ need_to_resume = plan_ptr->WillResume(resume_state, true);
+
+ while ((plan_ptr = GetPreviousPlan(plan_ptr)) != nullptr) {
+ plan_ptr->WillResume(resume_state, false);
+ }
+
+ // If the WillResume for the plan says we are faking a resume, then it will
+ // have set an appropriate stop info. In that case, don't reset it here.
+
+ if (need_to_resume && resume_state != eStateSuspended) {
+ m_stop_info_sp.reset();
+ }
+ }
+
+ if (need_to_resume) {
+ ClearStackFrames();
+ // Let Thread subclasses do any special work they need to prior to resuming
+ WillResume(resume_state);
+ }
+
+ return need_to_resume;
+}
+
+void Thread::DidResume() { SetResumeSignal(LLDB_INVALID_SIGNAL_NUMBER); }
+
+void Thread::DidStop() { SetState(eStateStopped); }
+
+bool Thread::ShouldStop(Event *event_ptr) {
+ ThreadPlan *current_plan = GetCurrentPlan();
+
+ bool should_stop = true;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (GetResumeState() == eStateSuspended) {
+ if (log)
+ log->Printf("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64
+ ", should_stop = 0 (ignore since thread was suspended)",
+ __FUNCTION__, GetID(), GetProtocolID());
+ return false;
+ }
+
+ if (GetTemporaryResumeState() == eStateSuspended) {
+ if (log)
+ log->Printf("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64
+ ", should_stop = 0 (ignore since thread was suspended)",
+ __FUNCTION__, GetID(), GetProtocolID());
+ return false;
+ }
+
+ // Based on the current thread plan and process stop info, check if this
+ // thread caused the process to stop. NOTE: this must take place before the
+ // plan is moved from the current plan stack to the completed plan stack.
+ if (!ThreadStoppedForAReason()) {
+ if (log)
+ log->Printf("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64
+ ", pc = 0x%16.16" PRIx64
+ ", should_stop = 0 (ignore since no stop reason)",
+ __FUNCTION__, GetID(), GetProtocolID(),
+ GetRegisterContext() ? GetRegisterContext()->GetPC()
+ : LLDB_INVALID_ADDRESS);
+ return false;
+ }
+
+ if (log) {
+ log->Printf("Thread::%s(%p) for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64
+ ", pc = 0x%16.16" PRIx64,
+ __FUNCTION__, static_cast<void *>(this), GetID(),
+ GetProtocolID(),
+ GetRegisterContext() ? GetRegisterContext()->GetPC()
+ : LLDB_INVALID_ADDRESS);
+ log->Printf("^^^^^^^^ Thread::ShouldStop Begin ^^^^^^^^");
+ StreamString s;
+ s.IndentMore();
+ DumpThreadPlans(&s);
+ log->Printf("Plan stack initial state:\n%s", s.GetData());
+ }
+
+ // The top most plan always gets to do the trace log...
+ current_plan->DoTraceLog();
+
+ // First query the stop info's ShouldStopSynchronous. This handles
+ // "synchronous" stop reasons, for example the breakpoint command on internal
+ // breakpoints. If a synchronous stop reason says we should not stop, then
+ // we don't have to do any more work on this stop.
+ StopInfoSP private_stop_info(GetPrivateStopInfo());
+ if (private_stop_info &&
+ !private_stop_info->ShouldStopSynchronous(event_ptr)) {
+ if (log)
+ log->Printf("StopInfo::ShouldStop async callback says we should not "
+ "stop, returning ShouldStop of false.");
+ return false;
+ }
+
+ // If we've already been restarted, don't query the plans since the state
+ // they would examine is not current.
+ if (Process::ProcessEventData::GetRestartedFromEvent(event_ptr))
+ return false;
+
+ // Before the plans see the state of the world, calculate the current inlined
+ // depth.
+ GetStackFrameList()->CalculateCurrentInlinedDepth();
+
+ // If the base plan doesn't understand why we stopped, then we have to find a
+ // plan that does. If that plan is still working, then we don't need to do
+ // any more work. If the plan that explains the stop is done, then we should
+ // pop all the plans below it, and pop it, and then let the plans above it
+ // decide whether they still need to do more work.
+
+ bool done_processing_current_plan = false;
+
+ if (!current_plan->PlanExplainsStop(event_ptr)) {
+ if (current_plan->TracerExplainsStop()) {
+ done_processing_current_plan = true;
+ should_stop = false;
+ } else {
+ // If the current plan doesn't explain the stop, then find one that does
+ // and let it handle the situation.
+ ThreadPlan *plan_ptr = current_plan;
+ while ((plan_ptr = GetPreviousPlan(plan_ptr)) != nullptr) {
+ if (plan_ptr->PlanExplainsStop(event_ptr)) {
+ should_stop = plan_ptr->ShouldStop(event_ptr);
+
+ // plan_ptr explains the stop, next check whether plan_ptr is done,
+ // if so, then we should take it and all the plans below it off the
+ // stack.
+
+ if (plan_ptr->MischiefManaged()) {
+ // We're going to pop the plans up to and including the plan that
+ // explains the stop.
+ ThreadPlan *prev_plan_ptr = GetPreviousPlan(plan_ptr);
+
+ do {
+ if (should_stop)
+ current_plan->WillStop();
+ PopPlan();
+ } while ((current_plan = GetCurrentPlan()) != prev_plan_ptr);
+ // Now, if the responsible plan was not "Okay to discard" then
+ // we're done, otherwise we forward this to the next plan in the
+ // stack below.
+ done_processing_current_plan =
+ (plan_ptr->IsMasterPlan() && !plan_ptr->OkayToDiscard());
+ } else
+ done_processing_current_plan = true;
+
+ break;
+ }
+ }
+ }
+ }
+
+ if (!done_processing_current_plan) {
+ bool over_ride_stop = current_plan->ShouldAutoContinue(event_ptr);
+
+ if (log)
+ log->Printf("Plan %s explains stop, auto-continue %i.",
+ current_plan->GetName(), over_ride_stop);
+
+ // We're starting from the base plan, so just let it decide;
+ if (PlanIsBasePlan(current_plan)) {
+ should_stop = current_plan->ShouldStop(event_ptr);
+ if (log)
+ log->Printf("Base plan says should stop: %i.", should_stop);
+ } else {
+ // Otherwise, don't let the base plan override what the other plans say
+ // to do, since presumably if there were other plans they would know what
+ // to do...
+ while (true) {
+ if (PlanIsBasePlan(current_plan))
+ break;
+
+ should_stop = current_plan->ShouldStop(event_ptr);
+ if (log)
+ log->Printf("Plan %s should stop: %d.", current_plan->GetName(),
+ should_stop);
+ if (current_plan->MischiefManaged()) {
+ if (should_stop)
+ current_plan->WillStop();
+
+ // If a Master Plan wants to stop, and wants to stick on the stack,
+ // we let it. Otherwise, see if the plan's parent wants to stop.
+
+ if (should_stop && current_plan->IsMasterPlan() &&
+ !current_plan->OkayToDiscard()) {
+ PopPlan();
+ break;
+ } else {
+ PopPlan();
+
+ current_plan = GetCurrentPlan();
+ if (current_plan == nullptr) {
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (over_ride_stop)
+ should_stop = false;
+ }
+
+ // One other potential problem is that we set up a master plan, then stop in
+ // before it is complete - for instance by hitting a breakpoint during a
+ // step-over - then do some step/finish/etc operations that wind up past the
+ // end point condition of the initial plan. We don't want to strand the
+ // original plan on the stack, This code clears stale plans off the stack.
+
+ if (should_stop) {
+ ThreadPlan *plan_ptr = GetCurrentPlan();
+
+ // Discard the stale plans and all plans below them in the stack, plus move
+ // the completed plans to the completed plan stack
+ while (!PlanIsBasePlan(plan_ptr)) {
+ bool stale = plan_ptr->IsPlanStale();
+ ThreadPlan *examined_plan = plan_ptr;
+ plan_ptr = GetPreviousPlan(examined_plan);
+
+ if (stale) {
+ if (log)
+ log->Printf(
+ "Plan %s being discarded in cleanup, it says it is already done.",
+ examined_plan->GetName());
+ while (GetCurrentPlan() != examined_plan) {
+ DiscardPlan();
+ }
+ if (examined_plan->IsPlanComplete()) {
+ // plan is complete but does not explain the stop (example: step to a
+ // line with breakpoint), let us move the plan to
+ // completed_plan_stack anyway
+ PopPlan();
+ } else
+ DiscardPlan();
+ }
+ }
+ }
+
+ if (log) {
+ StreamString s;
+ s.IndentMore();
+ DumpThreadPlans(&s);
+ log->Printf("Plan stack final state:\n%s", s.GetData());
+ log->Printf("vvvvvvvv Thread::ShouldStop End (returning %i) vvvvvvvv",
+ should_stop);
+ }
+ return should_stop;
+}
+
+Vote Thread::ShouldReportStop(Event *event_ptr) {
+ StateType thread_state = GetResumeState();
+ StateType temp_thread_state = GetTemporaryResumeState();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (thread_state == eStateSuspended || thread_state == eStateInvalid) {
+ if (log)
+ log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64
+ ": returning vote %i (state was suspended or invalid)",
+ GetID(), eVoteNoOpinion);
+ return eVoteNoOpinion;
+ }
+
+ if (temp_thread_state == eStateSuspended ||
+ temp_thread_state == eStateInvalid) {
+ if (log)
+ log->Printf(
+ "Thread::ShouldReportStop() tid = 0x%4.4" PRIx64
+ ": returning vote %i (temporary state was suspended or invalid)",
+ GetID(), eVoteNoOpinion);
+ return eVoteNoOpinion;
+ }
+
+ if (!ThreadStoppedForAReason()) {
+ if (log)
+ log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64
+ ": returning vote %i (thread didn't stop for a reason.)",
+ GetID(), eVoteNoOpinion);
+ return eVoteNoOpinion;
+ }
+
+ if (m_completed_plan_stack.size() > 0) {
+ // Don't use GetCompletedPlan here, since that suppresses private plans.
+ if (log)
+ log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64
+ ": returning vote for complete stack's back plan",
+ GetID());
+ return m_completed_plan_stack.back()->ShouldReportStop(event_ptr);
+ } else {
+ Vote thread_vote = eVoteNoOpinion;
+ ThreadPlan *plan_ptr = GetCurrentPlan();
+ while (true) {
+ if (plan_ptr->PlanExplainsStop(event_ptr)) {
+ thread_vote = plan_ptr->ShouldReportStop(event_ptr);
+ break;
+ }
+ if (PlanIsBasePlan(plan_ptr))
+ break;
+ else
+ plan_ptr = GetPreviousPlan(plan_ptr);
+ }
+ if (log)
+ log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64
+ ": returning vote %i for current plan",
+ GetID(), thread_vote);
+
+ return thread_vote;
+ }
+}
+
+Vote Thread::ShouldReportRun(Event *event_ptr) {
+ StateType thread_state = GetResumeState();
+
+ if (thread_state == eStateSuspended || thread_state == eStateInvalid) {
+ return eVoteNoOpinion;
+ }
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (m_completed_plan_stack.size() > 0) {
+ // Don't use GetCompletedPlan here, since that suppresses private plans.
+ if (log)
+ log->Printf("Current Plan for thread %d(%p) (0x%4.4" PRIx64
+ ", %s): %s being asked whether we should report run.",
+ GetIndexID(), static_cast<void *>(this), GetID(),
+ StateAsCString(GetTemporaryResumeState()),
+ m_completed_plan_stack.back()->GetName());
+
+ return m_completed_plan_stack.back()->ShouldReportRun(event_ptr);
+ } else {
+ if (log)
+ log->Printf("Current Plan for thread %d(%p) (0x%4.4" PRIx64
+ ", %s): %s being asked whether we should report run.",
+ GetIndexID(), static_cast<void *>(this), GetID(),
+ StateAsCString(GetTemporaryResumeState()),
+ GetCurrentPlan()->GetName());
+
+ return GetCurrentPlan()->ShouldReportRun(event_ptr);
+ }
+}
+
+bool Thread::MatchesSpec(const ThreadSpec *spec) {
+ return (spec == nullptr) ? true : spec->ThreadPassesBasicTests(*this);
+}
+
+void Thread::PushPlan(ThreadPlanSP &thread_plan_sp) {
+ if (thread_plan_sp) {
+ // If the thread plan doesn't already have a tracer, give it its parent's
+ // tracer:
+ if (!thread_plan_sp->GetThreadPlanTracer()) {
+ assert(!m_plan_stack.empty());
+ thread_plan_sp->SetThreadPlanTracer(
+ m_plan_stack.back()->GetThreadPlanTracer());
+ }
+ m_plan_stack.push_back(thread_plan_sp);
+
+ thread_plan_sp->DidPush();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log) {
+ StreamString s;
+ thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull);
+ log->Printf("Thread::PushPlan(0x%p): \"%s\", tid = 0x%4.4" PRIx64 ".",
+ static_cast<void *>(this), s.GetData(),
+ thread_plan_sp->GetThread().GetID());
+ }
+ }
+}
+
+void Thread::PopPlan() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (m_plan_stack.size() <= 1)
+ return;
+ else {
+ ThreadPlanSP &plan = m_plan_stack.back();
+ if (log) {
+ log->Printf("Popping plan: \"%s\", tid = 0x%4.4" PRIx64 ".",
+ plan->GetName(), plan->GetThread().GetID());
+ }
+ m_completed_plan_stack.push_back(plan);
+ plan->WillPop();
+ m_plan_stack.pop_back();
+ }
+}
+
+void Thread::DiscardPlan() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (m_plan_stack.size() > 1) {
+ ThreadPlanSP &plan = m_plan_stack.back();
+ if (log)
+ log->Printf("Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".",
+ plan->GetName(), plan->GetThread().GetID());
+
+ m_discarded_plan_stack.push_back(plan);
+ plan->WillPop();
+ m_plan_stack.pop_back();
+ }
+}
+
+ThreadPlan *Thread::GetCurrentPlan() {
+ // There will always be at least the base plan. If somebody is mucking with
+ // a thread with an empty plan stack, we should assert right away.
+ return m_plan_stack.empty() ? nullptr : m_plan_stack.back().get();
+}
+
+ThreadPlanSP Thread::GetCompletedPlan() {
+ ThreadPlanSP empty_plan_sp;
+ if (!m_completed_plan_stack.empty()) {
+ for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
+ ThreadPlanSP completed_plan_sp;
+ completed_plan_sp = m_completed_plan_stack[i];
+ if (!completed_plan_sp->GetPrivate())
+ return completed_plan_sp;
+ }
+ }
+ return empty_plan_sp;
+}
+
+ValueObjectSP Thread::GetReturnValueObject() {
+ if (!m_completed_plan_stack.empty()) {
+ for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
+ ValueObjectSP return_valobj_sp;
+ return_valobj_sp = m_completed_plan_stack[i]->GetReturnValueObject();
+ if (return_valobj_sp)
+ return return_valobj_sp;
+ }
+ }
+ return ValueObjectSP();
+}
+
+ExpressionVariableSP Thread::GetExpressionVariable() {
+ if (!m_completed_plan_stack.empty()) {
+ for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
+ ExpressionVariableSP expression_variable_sp;
+ expression_variable_sp =
+ m_completed_plan_stack[i]->GetExpressionVariable();
+ if (expression_variable_sp)
+ return expression_variable_sp;
+ }
+ }
+ return ExpressionVariableSP();
+}
+
+bool Thread::IsThreadPlanDone(ThreadPlan *plan) {
+ if (!m_completed_plan_stack.empty()) {
+ for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) {
+ if (m_completed_plan_stack[i].get() == plan)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Thread::WasThreadPlanDiscarded(ThreadPlan *plan) {
+ if (!m_discarded_plan_stack.empty()) {
+ for (int i = m_discarded_plan_stack.size() - 1; i >= 0; i--) {
+ if (m_discarded_plan_stack[i].get() == plan)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Thread::CompletedPlanOverridesBreakpoint() {
+ return (!m_completed_plan_stack.empty()) ;
+}
+
+ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) {
+ if (current_plan == nullptr)
+ return nullptr;
+
+ int stack_size = m_completed_plan_stack.size();
+ for (int i = stack_size - 1; i > 0; i--) {
+ if (current_plan == m_completed_plan_stack[i].get())
+ return m_completed_plan_stack[i - 1].get();
+ }
+
+ if (stack_size > 0 && m_completed_plan_stack[0].get() == current_plan) {
+ return GetCurrentPlan();
+ }
+
+ stack_size = m_plan_stack.size();
+ for (int i = stack_size - 1; i > 0; i--) {
+ if (current_plan == m_plan_stack[i].get())
+ return m_plan_stack[i - 1].get();
+ }
+ return nullptr;
+}
+
+Status Thread::QueueThreadPlan(ThreadPlanSP &thread_plan_sp,
+ bool abort_other_plans) {
+ Status status;
+ StreamString s;
+ if (!thread_plan_sp->ValidatePlan(&s)) {
+ DiscardThreadPlansUpToPlan(thread_plan_sp);
+ thread_plan_sp.reset();
+ status.SetErrorString(s.GetString());
+ return status;
+ }
+
+ if (abort_other_plans)
+ DiscardThreadPlans(true);
+
+ PushPlan(thread_plan_sp);
+
+ // This seems a little funny, but I don't want to have to split up the
+ // constructor and the DidPush in the scripted plan, that seems annoying.
+ // That means the constructor has to be in DidPush. So I have to validate the
+ // plan AFTER pushing it, and then take it off again...
+ if (!thread_plan_sp->ValidatePlan(&s)) {
+ DiscardThreadPlansUpToPlan(thread_plan_sp);
+ thread_plan_sp.reset();
+ status.SetErrorString(s.GetString());
+ return status;
+ }
+
+ return status;
+}
+
+void Thread::EnableTracer(bool value, bool single_stepping) {
+ int stack_size = m_plan_stack.size();
+ for (int i = 0; i < stack_size; i++) {
+ if (m_plan_stack[i]->GetThreadPlanTracer()) {
+ m_plan_stack[i]->GetThreadPlanTracer()->EnableTracing(value);
+ m_plan_stack[i]->GetThreadPlanTracer()->EnableSingleStep(single_stepping);
+ }
+ }
+}
+
+void Thread::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) {
+ int stack_size = m_plan_stack.size();
+ for (int i = 0; i < stack_size; i++)
+ m_plan_stack[i]->SetThreadPlanTracer(tracer_sp);
+}
+
+bool Thread::DiscardUserThreadPlansUpToIndex(uint32_t thread_index) {
+ // Count the user thread plans from the back end to get the number of the one
+ // we want to discard:
+
+ uint32_t idx = 0;
+ ThreadPlan *up_to_plan_ptr = nullptr;
+
+ for (ThreadPlanSP plan_sp : m_plan_stack) {
+ if (plan_sp->GetPrivate())
+ continue;
+ if (idx == thread_index) {
+ up_to_plan_ptr = plan_sp.get();
+ break;
+ } else
+ idx++;
+ }
+
+ if (up_to_plan_ptr == nullptr)
+ return false;
+
+ DiscardThreadPlansUpToPlan(up_to_plan_ptr);
+ return true;
+}
+
+void Thread::DiscardThreadPlansUpToPlan(lldb::ThreadPlanSP &up_to_plan_sp) {
+ DiscardThreadPlansUpToPlan(up_to_plan_sp.get());
+}
+
+void Thread::DiscardThreadPlansUpToPlan(ThreadPlan *up_to_plan_ptr) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Discarding thread plans for thread tid = 0x%4.4" PRIx64
+ ", up to %p",
+ GetID(), static_cast<void *>(up_to_plan_ptr));
+
+ int stack_size = m_plan_stack.size();
+
+ // If the input plan is nullptr, discard all plans. Otherwise make sure this
+ // plan is in the stack, and if so discard up to and including it.
+
+ if (up_to_plan_ptr == nullptr) {
+ for (int i = stack_size - 1; i > 0; i--)
+ DiscardPlan();
+ } else {
+ bool found_it = false;
+ for (int i = stack_size - 1; i > 0; i--) {
+ if (m_plan_stack[i].get() == up_to_plan_ptr)
+ found_it = true;
+ }
+ if (found_it) {
+ bool last_one = false;
+ for (int i = stack_size - 1; i > 0 && !last_one; i--) {
+ if (GetCurrentPlan() == up_to_plan_ptr)
+ last_one = true;
+ DiscardPlan();
+ }
+ }
+ }
+}
+
+void Thread::DiscardThreadPlans(bool force) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log) {
+ log->Printf("Discarding thread plans for thread (tid = 0x%4.4" PRIx64
+ ", force %d)",
+ GetID(), force);
+ }
+
+ if (force) {
+ int stack_size = m_plan_stack.size();
+ for (int i = stack_size - 1; i > 0; i--) {
+ DiscardPlan();
+ }
+ return;
+ }
+
+ while (true) {
+ int master_plan_idx;
+ bool discard = true;
+
+ // Find the first master plan, see if it wants discarding, and if yes
+ // discard up to it.
+ for (master_plan_idx = m_plan_stack.size() - 1; master_plan_idx >= 0;
+ master_plan_idx--) {
+ if (m_plan_stack[master_plan_idx]->IsMasterPlan()) {
+ discard = m_plan_stack[master_plan_idx]->OkayToDiscard();
+ break;
+ }
+ }
+
+ if (discard) {
+ // First pop all the dependent plans:
+ for (int i = m_plan_stack.size() - 1; i > master_plan_idx; i--) {
+ // FIXME: Do we need a finalize here, or is the rule that
+ // "PrepareForStop"
+ // for the plan leaves it in a state that it is safe to pop the plan
+ // with no more notice?
+ DiscardPlan();
+ }
+
+ // Now discard the master plan itself.
+ // The bottom-most plan never gets discarded. "OkayToDiscard" for it
+ // means discard it's dependent plans, but not it...
+ if (master_plan_idx > 0) {
+ DiscardPlan();
+ }
+ } else {
+ // If the master plan doesn't want to get discarded, then we're done.
+ break;
+ }
+ }
+}
+
+bool Thread::PlanIsBasePlan(ThreadPlan *plan_ptr) {
+ if (plan_ptr->IsBasePlan())
+ return true;
+ else if (m_plan_stack.size() == 0)
+ return false;
+ else
+ return m_plan_stack[0].get() == plan_ptr;
+}
+
+Status Thread::UnwindInnermostExpression() {
+ Status error;
+ int stack_size = m_plan_stack.size();
+
+ // If the input plan is nullptr, discard all plans. Otherwise make sure this
+ // plan is in the stack, and if so discard up to and including it.
+
+ for (int i = stack_size - 1; i > 0; i--) {
+ if (m_plan_stack[i]->GetKind() == ThreadPlan::eKindCallFunction) {
+ DiscardThreadPlansUpToPlan(m_plan_stack[i].get());
+ return error;
+ }
+ }
+ error.SetErrorString("No expressions currently active on this thread");
+ return error;
+}
+
+ThreadPlanSP Thread::QueueFundamentalPlan(bool abort_other_plans) {
+ ThreadPlanSP thread_plan_sp(new ThreadPlanBase(*this));
+ QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP Thread::QueueThreadPlanForStepSingleInstruction(
+ bool step_over, bool abort_other_plans, bool stop_other_threads,
+ Status &status) {
+ ThreadPlanSP thread_plan_sp(new ThreadPlanStepInstruction(
+ *this, step_over, stop_other_threads, eVoteNoOpinion, eVoteNoOpinion));
+ status = QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP Thread::QueueThreadPlanForStepOverRange(
+ bool abort_other_plans, const AddressRange &range,
+ const SymbolContext &addr_context, lldb::RunMode stop_other_threads,
+ Status &status, LazyBool step_out_avoids_code_withoug_debug_info) {
+ ThreadPlanSP thread_plan_sp;
+ thread_plan_sp = std::make_shared<ThreadPlanStepOverRange>(
+ *this, range, addr_context, stop_other_threads,
+ step_out_avoids_code_withoug_debug_info);
+
+ status = QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+// Call the QueueThreadPlanForStepOverRange method which takes an address
+// range.
+ThreadPlanSP Thread::QueueThreadPlanForStepOverRange(
+ bool abort_other_plans, const LineEntry &line_entry,
+ const SymbolContext &addr_context, lldb::RunMode stop_other_threads,
+ Status &status, LazyBool step_out_avoids_code_withoug_debug_info) {
+ const bool include_inlined_functions = true;
+ auto address_range =
+ line_entry.GetSameLineContiguousAddressRange(include_inlined_functions);
+ return QueueThreadPlanForStepOverRange(
+ abort_other_plans, address_range, addr_context, stop_other_threads,
+ status, step_out_avoids_code_withoug_debug_info);
+}
+
+ThreadPlanSP Thread::QueueThreadPlanForStepInRange(
+ bool abort_other_plans, const AddressRange &range,
+ const SymbolContext &addr_context, const char *step_in_target,
+ lldb::RunMode stop_other_threads, Status &status,
+ LazyBool step_in_avoids_code_without_debug_info,
+ LazyBool step_out_avoids_code_without_debug_info) {
+ ThreadPlanSP thread_plan_sp(
+ new ThreadPlanStepInRange(*this, range, addr_context, stop_other_threads,
+ step_in_avoids_code_without_debug_info,
+ step_out_avoids_code_without_debug_info));
+ ThreadPlanStepInRange *plan =
+ static_cast<ThreadPlanStepInRange *>(thread_plan_sp.get());
+
+ if (step_in_target)
+ plan->SetStepInTarget(step_in_target);
+
+ status = QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+// Call the QueueThreadPlanForStepInRange method which takes an address range.
+ThreadPlanSP Thread::QueueThreadPlanForStepInRange(
+ bool abort_other_plans, const LineEntry &line_entry,
+ const SymbolContext &addr_context, const char *step_in_target,
+ lldb::RunMode stop_other_threads, Status &status,
+ LazyBool step_in_avoids_code_without_debug_info,
+ LazyBool step_out_avoids_code_without_debug_info) {
+ const bool include_inlined_functions = false;
+ return QueueThreadPlanForStepInRange(
+ abort_other_plans,
+ line_entry.GetSameLineContiguousAddressRange(include_inlined_functions),
+ addr_context, step_in_target, stop_other_threads, status,
+ step_in_avoids_code_without_debug_info,
+ step_out_avoids_code_without_debug_info);
+}
+
+ThreadPlanSP Thread::QueueThreadPlanForStepOut(
+ bool abort_other_plans, SymbolContext *addr_context, bool first_insn,
+ bool stop_other_threads, Vote stop_vote, Vote run_vote, uint32_t frame_idx,
+ Status &status, LazyBool step_out_avoids_code_without_debug_info) {
+ ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut(
+ *this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote,
+ frame_idx, step_out_avoids_code_without_debug_info));
+
+ status = QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP Thread::QueueThreadPlanForStepOutNoShouldStop(
+ bool abort_other_plans, SymbolContext *addr_context, bool first_insn,
+ bool stop_other_threads, Vote stop_vote, Vote run_vote, uint32_t frame_idx,
+ Status &status, bool continue_to_next_branch) {
+ const bool calculate_return_value =
+ false; // No need to calculate the return value here.
+ ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut(
+ *this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote,
+ frame_idx, eLazyBoolNo, continue_to_next_branch, calculate_return_value));
+
+ ThreadPlanStepOut *new_plan =
+ static_cast<ThreadPlanStepOut *>(thread_plan_sp.get());
+ new_plan->ClearShouldStopHereCallbacks();
+
+ status = QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP Thread::QueueThreadPlanForStepThrough(StackID &return_stack_id,
+ bool abort_other_plans,
+ bool stop_other_threads,
+ Status &status) {
+ ThreadPlanSP thread_plan_sp(
+ new ThreadPlanStepThrough(*this, return_stack_id, stop_other_threads));
+ if (!thread_plan_sp || !thread_plan_sp->ValidatePlan(nullptr))
+ return ThreadPlanSP();
+
+ status = QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP Thread::QueueThreadPlanForRunToAddress(bool abort_other_plans,
+ Address &target_addr,
+ bool stop_other_threads,
+ Status &status) {
+ ThreadPlanSP thread_plan_sp(
+ new ThreadPlanRunToAddress(*this, target_addr, stop_other_threads));
+
+ status = QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP Thread::QueueThreadPlanForStepUntil(
+ bool abort_other_plans, lldb::addr_t *address_list, size_t num_addresses,
+ bool stop_other_threads, uint32_t frame_idx, Status &status) {
+ ThreadPlanSP thread_plan_sp(new ThreadPlanStepUntil(
+ *this, address_list, num_addresses, stop_other_threads, frame_idx));
+
+ status = QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+lldb::ThreadPlanSP Thread::QueueThreadPlanForStepScripted(
+ bool abort_other_plans, const char *class_name, bool stop_other_threads,
+ Status &status) {
+ ThreadPlanSP thread_plan_sp(new ThreadPlanPython(*this, class_name));
+
+ status = QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+uint32_t Thread::GetIndexID() const { return m_index_id; }
+
+static void PrintPlanElement(Stream *s, const ThreadPlanSP &plan,
+ lldb::DescriptionLevel desc_level,
+ int32_t elem_idx) {
+ s->IndentMore();
+ s->Indent();
+ s->Printf("Element %d: ", elem_idx);
+ plan->GetDescription(s, desc_level);
+ s->EOL();
+ s->IndentLess();
+}
+
+static void PrintPlanStack(Stream *s,
+ const std::vector<lldb::ThreadPlanSP> &plan_stack,
+ lldb::DescriptionLevel desc_level,
+ bool include_internal) {
+ int32_t print_idx = 0;
+ for (ThreadPlanSP plan_sp : plan_stack) {
+ if (include_internal || !plan_sp->GetPrivate()) {
+ PrintPlanElement(s, plan_sp, desc_level, print_idx++);
+ }
+ }
+}
+
+void Thread::DumpThreadPlans(Stream *s, lldb::DescriptionLevel desc_level,
+ bool include_internal,
+ bool ignore_boring_threads) const {
+ uint32_t stack_size;
+
+ if (ignore_boring_threads) {
+ uint32_t stack_size = m_plan_stack.size();
+ uint32_t completed_stack_size = m_completed_plan_stack.size();
+ uint32_t discarded_stack_size = m_discarded_plan_stack.size();
+ if (stack_size == 1 && completed_stack_size == 0 &&
+ discarded_stack_size == 0) {
+ s->Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", GetIndexID(), GetID());
+ s->IndentMore();
+ s->Indent();
+ s->Printf("No active thread plans\n");
+ s->IndentLess();
+ return;
+ }
+ }
+
+ s->Indent();
+ s->Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", GetIndexID(), GetID());
+ s->IndentMore();
+ s->Indent();
+ s->Printf("Active plan stack:\n");
+ PrintPlanStack(s, m_plan_stack, desc_level, include_internal);
+
+ stack_size = m_completed_plan_stack.size();
+ if (stack_size > 0) {
+ s->Indent();
+ s->Printf("Completed Plan Stack:\n");
+ PrintPlanStack(s, m_completed_plan_stack, desc_level, include_internal);
+ }
+
+ stack_size = m_discarded_plan_stack.size();
+ if (stack_size > 0) {
+ s->Indent();
+ s->Printf("Discarded Plan Stack:\n");
+ PrintPlanStack(s, m_discarded_plan_stack, desc_level, include_internal);
+ }
+
+ s->IndentLess();
+}
+
+TargetSP Thread::CalculateTarget() {
+ TargetSP target_sp;
+ ProcessSP process_sp(GetProcess());
+ if (process_sp)
+ target_sp = process_sp->CalculateTarget();
+ return target_sp;
+}
+
+ProcessSP Thread::CalculateProcess() { return GetProcess(); }
+
+ThreadSP Thread::CalculateThread() { return shared_from_this(); }
+
+StackFrameSP Thread::CalculateStackFrame() { return StackFrameSP(); }
+
+void Thread::CalculateExecutionContext(ExecutionContext &exe_ctx) {
+ exe_ctx.SetContext(shared_from_this());
+}
+
+StackFrameListSP Thread::GetStackFrameList() {
+ std::lock_guard<std::recursive_mutex> guard(m_frame_mutex);
+
+ if (!m_curr_frames_sp)
+ m_curr_frames_sp =
+ std::make_shared<StackFrameList>(*this, m_prev_frames_sp, true);
+
+ return m_curr_frames_sp;
+}
+
+void Thread::ClearStackFrames() {
+ std::lock_guard<std::recursive_mutex> guard(m_frame_mutex);
+
+ Unwind *unwinder = GetUnwinder();
+ if (unwinder)
+ unwinder->Clear();
+
+ // Only store away the old "reference" StackFrameList if we got all its
+ // frames:
+ // FIXME: At some point we can try to splice in the frames we have fetched
+ // into
+ // the new frame as we make it, but let's not try that now.
+ if (m_curr_frames_sp && m_curr_frames_sp->GetAllFramesFetched())
+ m_prev_frames_sp.swap(m_curr_frames_sp);
+ m_curr_frames_sp.reset();
+
+ m_extended_info.reset();
+ m_extended_info_fetched = false;
+}
+
+lldb::StackFrameSP Thread::GetFrameWithConcreteFrameIndex(uint32_t unwind_idx) {
+ return GetStackFrameList()->GetFrameWithConcreteFrameIndex(unwind_idx);
+}
+
+Status Thread::ReturnFromFrameWithIndex(uint32_t frame_idx,
+ lldb::ValueObjectSP return_value_sp,
+ bool broadcast) {
+ StackFrameSP frame_sp = GetStackFrameAtIndex(frame_idx);
+ Status return_error;
+
+ if (!frame_sp) {
+ return_error.SetErrorStringWithFormat(
+ "Could not find frame with index %d in thread 0x%" PRIx64 ".",
+ frame_idx, GetID());
+ }
+
+ return ReturnFromFrame(frame_sp, return_value_sp, broadcast);
+}
+
+Status Thread::ReturnFromFrame(lldb::StackFrameSP frame_sp,
+ lldb::ValueObjectSP return_value_sp,
+ bool broadcast) {
+ Status return_error;
+
+ if (!frame_sp) {
+ return_error.SetErrorString("Can't return to a null frame.");
+ return return_error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+ uint32_t older_frame_idx = frame_sp->GetFrameIndex() + 1;
+ StackFrameSP older_frame_sp = thread->GetStackFrameAtIndex(older_frame_idx);
+ if (!older_frame_sp) {
+ return_error.SetErrorString("No older frame to return to.");
+ return return_error;
+ }
+
+ if (return_value_sp) {
+ lldb::ABISP abi = thread->GetProcess()->GetABI();
+ if (!abi) {
+ return_error.SetErrorString("Could not find ABI to set return value.");
+ return return_error;
+ }
+ SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextFunction);
+
+ // FIXME: ValueObject::Cast doesn't currently work correctly, at least not
+ // for scalars.
+ // Turn that back on when that works.
+ if (/* DISABLES CODE */ (false) && sc.function != nullptr) {
+ Type *function_type = sc.function->GetType();
+ if (function_type) {
+ CompilerType return_type =
+ sc.function->GetCompilerType().GetFunctionReturnType();
+ if (return_type) {
+ StreamString s;
+ return_type.DumpTypeDescription(&s);
+ ValueObjectSP cast_value_sp = return_value_sp->Cast(return_type);
+ if (cast_value_sp) {
+ cast_value_sp->SetFormat(eFormatHex);
+ return_value_sp = cast_value_sp;
+ }
+ }
+ }
+ }
+
+ return_error = abi->SetReturnValueObject(older_frame_sp, return_value_sp);
+ if (!return_error.Success())
+ return return_error;
+ }
+
+ // Now write the return registers for the chosen frame: Note, we can't use
+ // ReadAllRegisterValues->WriteAllRegisterValues, since the read & write cook
+ // their data
+
+ StackFrameSP youngest_frame_sp = thread->GetStackFrameAtIndex(0);
+ if (youngest_frame_sp) {
+ lldb::RegisterContextSP reg_ctx_sp(youngest_frame_sp->GetRegisterContext());
+ if (reg_ctx_sp) {
+ bool copy_success = reg_ctx_sp->CopyFromRegisterContext(
+ older_frame_sp->GetRegisterContext());
+ if (copy_success) {
+ thread->DiscardThreadPlans(true);
+ thread->ClearStackFrames();
+ if (broadcast && EventTypeHasListeners(eBroadcastBitStackChanged))
+ BroadcastEvent(eBroadcastBitStackChanged,
+ new ThreadEventData(this->shared_from_this()));
+ } else {
+ return_error.SetErrorString("Could not reset register values.");
+ }
+ } else {
+ return_error.SetErrorString("Frame has no register context.");
+ }
+ } else {
+ return_error.SetErrorString("Returned past top frame.");
+ }
+ return return_error;
+}
+
+static void DumpAddressList(Stream &s, const std::vector<Address> &list,
+ ExecutionContextScope *exe_scope) {
+ for (size_t n = 0; n < list.size(); n++) {
+ s << "\t";
+ list[n].Dump(&s, exe_scope, Address::DumpStyleResolvedDescription,
+ Address::DumpStyleSectionNameOffset);
+ s << "\n";
+ }
+}
+
+Status Thread::JumpToLine(const FileSpec &file, uint32_t line,
+ bool can_leave_function, std::string *warnings) {
+ ExecutionContext exe_ctx(GetStackFrameAtIndex(0));
+ Target *target = exe_ctx.GetTargetPtr();
+ TargetSP target_sp = exe_ctx.GetTargetSP();
+ RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ const SymbolContext &sc = frame->GetSymbolContext(eSymbolContextFunction);
+
+ // Find candidate locations.
+ std::vector<Address> candidates, within_function, outside_function;
+ target->GetImages().FindAddressesForLine(target_sp, file, line, sc.function,
+ within_function, outside_function);
+
+ // If possible, we try and stay within the current function. Within a
+ // function, we accept multiple locations (optimized code may do this,
+ // there's no solution here so we do the best we can). However if we're
+ // trying to leave the function, we don't know how to pick the right
+ // location, so if there's more than one then we bail.
+ if (!within_function.empty())
+ candidates = within_function;
+ else if (outside_function.size() == 1 && can_leave_function)
+ candidates = outside_function;
+
+ // Check if we got anything.
+ if (candidates.empty()) {
+ if (outside_function.empty()) {
+ return Status("Cannot locate an address for %s:%i.",
+ file.GetFilename().AsCString(), line);
+ } else if (outside_function.size() == 1) {
+ return Status("%s:%i is outside the current function.",
+ file.GetFilename().AsCString(), line);
+ } else {
+ StreamString sstr;
+ DumpAddressList(sstr, outside_function, target);
+ return Status("%s:%i has multiple candidate locations:\n%s",
+ file.GetFilename().AsCString(), line, sstr.GetData());
+ }
+ }
+
+ // Accept the first location, warn about any others.
+ Address dest = candidates[0];
+ if (warnings && candidates.size() > 1) {
+ StreamString sstr;
+ sstr.Printf("%s:%i appears multiple times in this function, selecting the "
+ "first location:\n",
+ file.GetFilename().AsCString(), line);
+ DumpAddressList(sstr, candidates, target);
+ *warnings = sstr.GetString();
+ }
+
+ if (!reg_ctx->SetPC(dest))
+ return Status("Cannot change PC to target address.");
+
+ return Status();
+}
+
+void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
+ bool stop_format) {
+ ExecutionContext exe_ctx(shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process == nullptr)
+ return;
+
+ StackFrameSP frame_sp;
+ SymbolContext frame_sc;
+ if (frame_idx != LLDB_INVALID_FRAME_ID) {
+ frame_sp = GetStackFrameAtIndex(frame_idx);
+ if (frame_sp) {
+ exe_ctx.SetFrameSP(frame_sp);
+ frame_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
+ }
+ }
+
+ const FormatEntity::Entry *thread_format;
+ if (stop_format)
+ thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadStopFormat();
+ else
+ thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadFormat();
+
+ assert(thread_format);
+
+ FormatEntity::Format(*thread_format, strm, frame_sp ? &frame_sc : nullptr,
+ &exe_ctx, nullptr, nullptr, false, false);
+}
+
+void Thread::SettingsInitialize() {}
+
+void Thread::SettingsTerminate() {}
+
+lldb::addr_t Thread::GetThreadPointer() { return LLDB_INVALID_ADDRESS; }
+
+addr_t Thread::GetThreadLocalData(const ModuleSP module,
+ lldb::addr_t tls_file_addr) {
+ // The default implementation is to ask the dynamic loader for it. This can
+ // be overridden for specific platforms.
+ DynamicLoader *loader = GetProcess()->GetDynamicLoader();
+ if (loader)
+ return loader->GetThreadLocalData(module, shared_from_this(),
+ tls_file_addr);
+ else
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool Thread::SafeToCallFunctions() {
+ Process *process = GetProcess().get();
+ if (process) {
+ SystemRuntime *runtime = process->GetSystemRuntime();
+ if (runtime) {
+ return runtime->SafeToCallFunctionsOnThisThread(shared_from_this());
+ }
+ }
+ return true;
+}
+
+lldb::StackFrameSP
+Thread::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) {
+ return GetStackFrameList()->GetStackFrameSPForStackFramePtr(stack_frame_ptr);
+}
+
+const char *Thread::StopReasonAsCString(lldb::StopReason reason) {
+ switch (reason) {
+ case eStopReasonInvalid:
+ return "invalid";
+ case eStopReasonNone:
+ return "none";
+ case eStopReasonTrace:
+ return "trace";
+ case eStopReasonBreakpoint:
+ return "breakpoint";
+ case eStopReasonWatchpoint:
+ return "watchpoint";
+ case eStopReasonSignal:
+ return "signal";
+ case eStopReasonException:
+ return "exception";
+ case eStopReasonExec:
+ return "exec";
+ case eStopReasonPlanComplete:
+ return "plan complete";
+ case eStopReasonThreadExiting:
+ return "thread exiting";
+ case eStopReasonInstrumentation:
+ return "instrumentation break";
+ }
+
+ static char unknown_state_string[64];
+ snprintf(unknown_state_string, sizeof(unknown_state_string),
+ "StopReason = %i", reason);
+ return unknown_state_string;
+}
+
+const char *Thread::RunModeAsCString(lldb::RunMode mode) {
+ switch (mode) {
+ case eOnlyThisThread:
+ return "only this thread";
+ case eAllThreads:
+ return "all threads";
+ case eOnlyDuringStepping:
+ return "only during stepping";
+ }
+
+ static char unknown_state_string[64];
+ snprintf(unknown_state_string, sizeof(unknown_state_string), "RunMode = %i",
+ mode);
+ return unknown_state_string;
+}
+
+size_t Thread::GetStatus(Stream &strm, uint32_t start_frame,
+ uint32_t num_frames, uint32_t num_frames_with_source,
+ bool stop_format, bool only_stacks) {
+
+ if (!only_stacks) {
+ ExecutionContext exe_ctx(shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ strm.Indent();
+ bool is_selected = false;
+ if (process) {
+ if (process->GetThreadList().GetSelectedThread().get() == this)
+ is_selected = true;
+ }
+ strm.Printf("%c ", is_selected ? '*' : ' ');
+ if (target && target->GetDebugger().GetUseExternalEditor()) {
+ StackFrameSP frame_sp = GetStackFrameAtIndex(start_frame);
+ if (frame_sp) {
+ SymbolContext frame_sc(
+ frame_sp->GetSymbolContext(eSymbolContextLineEntry));
+ if (frame_sc.line_entry.line != 0 && frame_sc.line_entry.file) {
+ Host::OpenFileInExternalEditor(frame_sc.line_entry.file,
+ frame_sc.line_entry.line);
+ }
+ }
+ }
+
+ DumpUsingSettingsFormat(strm, start_frame, stop_format);
+ }
+
+ size_t num_frames_shown = 0;
+ if (num_frames > 0) {
+ strm.IndentMore();
+
+ const bool show_frame_info = true;
+ const bool show_frame_unique = only_stacks;
+ const char *selected_frame_marker = nullptr;
+ if (num_frames == 1 || only_stacks ||
+ (GetID() != GetProcess()->GetThreadList().GetSelectedThread()->GetID()))
+ strm.IndentMore();
+ else
+ selected_frame_marker = "* ";
+
+ num_frames_shown = GetStackFrameList()->GetStatus(
+ strm, start_frame, num_frames, show_frame_info, num_frames_with_source,
+ show_frame_unique, selected_frame_marker);
+ if (num_frames == 1)
+ strm.IndentLess();
+ strm.IndentLess();
+ }
+ return num_frames_shown;
+}
+
+bool Thread::GetDescription(Stream &strm, lldb::DescriptionLevel level,
+ bool print_json_thread, bool print_json_stopinfo) {
+ const bool stop_format = false;
+ DumpUsingSettingsFormat(strm, 0, stop_format);
+ strm.Printf("\n");
+
+ StructuredData::ObjectSP thread_info = GetExtendedInfo();
+
+ if (print_json_thread || print_json_stopinfo) {
+ if (thread_info && print_json_thread) {
+ thread_info->Dump(strm);
+ strm.Printf("\n");
+ }
+
+ if (print_json_stopinfo && m_stop_info_sp) {
+ StructuredData::ObjectSP stop_info = m_stop_info_sp->GetExtendedInfo();
+ if (stop_info) {
+ stop_info->Dump(strm);
+ strm.Printf("\n");
+ }
+ }
+
+ return true;
+ }
+
+ if (thread_info) {
+ StructuredData::ObjectSP activity =
+ thread_info->GetObjectForDotSeparatedPath("activity");
+ StructuredData::ObjectSP breadcrumb =
+ thread_info->GetObjectForDotSeparatedPath("breadcrumb");
+ StructuredData::ObjectSP messages =
+ thread_info->GetObjectForDotSeparatedPath("trace_messages");
+
+ bool printed_activity = false;
+ if (activity && activity->GetType() == eStructuredDataTypeDictionary) {
+ StructuredData::Dictionary *activity_dict = activity->GetAsDictionary();
+ StructuredData::ObjectSP id = activity_dict->GetValueForKey("id");
+ StructuredData::ObjectSP name = activity_dict->GetValueForKey("name");
+ if (name && name->GetType() == eStructuredDataTypeString && id &&
+ id->GetType() == eStructuredDataTypeInteger) {
+ strm.Format(" Activity '{0}', {1:x}\n",
+ name->GetAsString()->GetValue(),
+ id->GetAsInteger()->GetValue());
+ }
+ printed_activity = true;
+ }
+ bool printed_breadcrumb = false;
+ if (breadcrumb && breadcrumb->GetType() == eStructuredDataTypeDictionary) {
+ if (printed_activity)
+ strm.Printf("\n");
+ StructuredData::Dictionary *breadcrumb_dict =
+ breadcrumb->GetAsDictionary();
+ StructuredData::ObjectSP breadcrumb_text =
+ breadcrumb_dict->GetValueForKey("name");
+ if (breadcrumb_text &&
+ breadcrumb_text->GetType() == eStructuredDataTypeString) {
+ strm.Format(" Current Breadcrumb: {0}\n",
+ breadcrumb_text->GetAsString()->GetValue());
+ }
+ printed_breadcrumb = true;
+ }
+ if (messages && messages->GetType() == eStructuredDataTypeArray) {
+ if (printed_breadcrumb)
+ strm.Printf("\n");
+ StructuredData::Array *messages_array = messages->GetAsArray();
+ const size_t msg_count = messages_array->GetSize();
+ if (msg_count > 0) {
+ strm.Printf(" %zu trace messages:\n", msg_count);
+ for (size_t i = 0; i < msg_count; i++) {
+ StructuredData::ObjectSP message = messages_array->GetItemAtIndex(i);
+ if (message && message->GetType() == eStructuredDataTypeDictionary) {
+ StructuredData::Dictionary *message_dict =
+ message->GetAsDictionary();
+ StructuredData::ObjectSP message_text =
+ message_dict->GetValueForKey("message");
+ if (message_text &&
+ message_text->GetType() == eStructuredDataTypeString) {
+ strm.Format(" {0}\n", message_text->GetAsString()->GetValue());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+size_t Thread::GetStackFrameStatus(Stream &strm, uint32_t first_frame,
+ uint32_t num_frames, bool show_frame_info,
+ uint32_t num_frames_with_source) {
+ return GetStackFrameList()->GetStatus(
+ strm, first_frame, num_frames, show_frame_info, num_frames_with_source);
+}
+
+Unwind *Thread::GetUnwinder() {
+ if (!m_unwinder_up) {
+ const ArchSpec target_arch(CalculateTarget()->GetArchitecture());
+ const llvm::Triple::ArchType machine = target_arch.GetMachine();
+ switch (machine) {
+ case llvm::Triple::x86_64:
+ case llvm::Triple::x86:
+ case llvm::Triple::arm:
+ case llvm::Triple::aarch64:
+ case llvm::Triple::thumb:
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ case llvm::Triple::ppc64le:
+ case llvm::Triple::systemz:
+ case llvm::Triple::hexagon:
+ m_unwinder_up.reset(new UnwindLLDB(*this));
+ break;
+
+ default:
+ if (target_arch.GetTriple().getVendor() == llvm::Triple::Apple)
+ m_unwinder_up.reset(new UnwindMacOSXFrameBackchain(*this));
+ break;
+ }
+ }
+ return m_unwinder_up.get();
+}
+
+void Thread::Flush() {
+ ClearStackFrames();
+ m_reg_context_sp.reset();
+}
+
+bool Thread::IsStillAtLastBreakpointHit() {
+ // If we are currently stopped at a breakpoint, always return that stopinfo
+ // and don't reset it. This allows threads to maintain their breakpoint
+ // stopinfo, such as when thread-stepping in multithreaded programs.
+ if (m_stop_info_sp) {
+ StopReason stop_reason = m_stop_info_sp->GetStopReason();
+ if (stop_reason == lldb::eStopReasonBreakpoint) {
+ uint64_t value = m_stop_info_sp->GetValue();
+ lldb::RegisterContextSP reg_ctx_sp(GetRegisterContext());
+ if (reg_ctx_sp) {
+ lldb::addr_t pc = reg_ctx_sp->GetPC();
+ BreakpointSiteSP bp_site_sp =
+ GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
+ if (bp_site_sp && static_cast<break_id_t>(value) == bp_site_sp->GetID())
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+Status Thread::StepIn(bool source_step,
+ LazyBool step_in_avoids_code_without_debug_info,
+ LazyBool step_out_avoids_code_without_debug_info)
+
+{
+ Status error;
+ Process *process = GetProcess().get();
+ if (StateIsStoppedState(process->GetState(), true)) {
+ StackFrameSP frame_sp = GetStackFrameAtIndex(0);
+ ThreadPlanSP new_plan_sp;
+ const lldb::RunMode run_mode = eOnlyThisThread;
+ const bool abort_other_plans = false;
+
+ if (source_step && frame_sp && frame_sp->HasDebugInformation()) {
+ SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything));
+ new_plan_sp = QueueThreadPlanForStepInRange(
+ abort_other_plans, sc.line_entry, sc, nullptr, run_mode, error,
+ step_in_avoids_code_without_debug_info,
+ step_out_avoids_code_without_debug_info);
+ } else {
+ new_plan_sp = QueueThreadPlanForStepSingleInstruction(
+ false, abort_other_plans, run_mode, error);
+ }
+
+ new_plan_sp->SetIsMasterPlan(true);
+ new_plan_sp->SetOkayToDiscard(false);
+
+ // Why do we need to set the current thread by ID here???
+ process->GetThreadList().SetSelectedThreadByID(GetID());
+ error = process->Resume();
+ } else {
+ error.SetErrorString("process not stopped");
+ }
+ return error;
+}
+
+Status Thread::StepOver(bool source_step,
+ LazyBool step_out_avoids_code_without_debug_info) {
+ Status error;
+ Process *process = GetProcess().get();
+ if (StateIsStoppedState(process->GetState(), true)) {
+ StackFrameSP frame_sp = GetStackFrameAtIndex(0);
+ ThreadPlanSP new_plan_sp;
+
+ const lldb::RunMode run_mode = eOnlyThisThread;
+ const bool abort_other_plans = false;
+
+ if (source_step && frame_sp && frame_sp->HasDebugInformation()) {
+ SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything));
+ new_plan_sp = QueueThreadPlanForStepOverRange(
+ abort_other_plans, sc.line_entry, sc, run_mode, error,
+ step_out_avoids_code_without_debug_info);
+ } else {
+ new_plan_sp = QueueThreadPlanForStepSingleInstruction(
+ true, abort_other_plans, run_mode, error);
+ }
+
+ new_plan_sp->SetIsMasterPlan(true);
+ new_plan_sp->SetOkayToDiscard(false);
+
+ // Why do we need to set the current thread by ID here???
+ process->GetThreadList().SetSelectedThreadByID(GetID());
+ error = process->Resume();
+ } else {
+ error.SetErrorString("process not stopped");
+ }
+ return error;
+}
+
+Status Thread::StepOut() {
+ Status error;
+ Process *process = GetProcess().get();
+ if (StateIsStoppedState(process->GetState(), true)) {
+ const bool first_instruction = false;
+ const bool stop_other_threads = false;
+ const bool abort_other_plans = false;
+
+ ThreadPlanSP new_plan_sp(QueueThreadPlanForStepOut(
+ abort_other_plans, nullptr, first_instruction, stop_other_threads,
+ eVoteYes, eVoteNoOpinion, 0, error));
+
+ new_plan_sp->SetIsMasterPlan(true);
+ new_plan_sp->SetOkayToDiscard(false);
+
+ // Why do we need to set the current thread by ID here???
+ process->GetThreadList().SetSelectedThreadByID(GetID());
+ error = process->Resume();
+ } else {
+ error.SetErrorString("process not stopped");
+ }
+ return error;
+}
+
+ValueObjectSP Thread::GetCurrentException() {
+ if (auto frame_sp = GetStackFrameAtIndex(0))
+ if (auto recognized_frame = frame_sp->GetRecognizedFrame())
+ if (auto e = recognized_frame->GetExceptionObject())
+ return e;
+
+ // NOTE: Even though this behavior is generalized, only ObjC is actually
+ // supported at the moment.
+ for (LanguageRuntime *runtime : GetProcess()->GetLanguageRuntimes()) {
+ if (auto e = runtime->GetExceptionObjectForThread(shared_from_this()))
+ return e;
+ }
+
+ return ValueObjectSP();
+}
+
+ThreadSP Thread::GetCurrentExceptionBacktrace() {
+ ValueObjectSP exception = GetCurrentException();
+ if (!exception)
+ return ThreadSP();
+
+ // NOTE: Even though this behavior is generalized, only ObjC is actually
+ // supported at the moment.
+ for (LanguageRuntime *runtime : GetProcess()->GetLanguageRuntimes()) {
+ if (auto bt = runtime->GetBacktraceThreadFromException(exception))
+ return bt;
+ }
+
+ return ThreadSP();
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadCollection.cpp b/contrib/llvm-project/lldb/source/Target/ThreadCollection.cpp
new file mode 100644
index 000000000000..cf3c1e242999
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadCollection.cpp
@@ -0,0 +1,65 @@
+//===-- ThreadCollection.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include <stdlib.h>
+
+#include <algorithm>
+#include <mutex>
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadCollection.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ThreadCollection::ThreadCollection() : m_threads(), m_mutex() {}
+
+ThreadCollection::ThreadCollection(collection threads)
+ : m_threads(threads), m_mutex() {}
+
+void ThreadCollection::AddThread(const ThreadSP &thread_sp) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ m_threads.push_back(thread_sp);
+}
+
+void ThreadCollection::AddThreadSortedByIndexID(const ThreadSP &thread_sp) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ // Make sure we always keep the threads sorted by thread index ID
+ const uint32_t thread_index_id = thread_sp->GetIndexID();
+ if (m_threads.empty() || m_threads.back()->GetIndexID() < thread_index_id)
+ m_threads.push_back(thread_sp);
+ else {
+ m_threads.insert(
+ std::upper_bound(m_threads.begin(), m_threads.end(), thread_sp,
+ [](const ThreadSP &lhs, const ThreadSP &rhs) -> bool {
+ return lhs->GetIndexID() < rhs->GetIndexID();
+ }),
+ thread_sp);
+ }
+}
+
+void ThreadCollection::InsertThread(const lldb::ThreadSP &thread_sp,
+ uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ if (idx < m_threads.size())
+ m_threads.insert(m_threads.begin() + idx, thread_sp);
+ else
+ m_threads.push_back(thread_sp);
+}
+
+uint32_t ThreadCollection::GetSize() {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ return m_threads.size();
+}
+
+ThreadSP ThreadCollection::GetThreadAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ ThreadSP thread_sp;
+ if (idx < m_threads.size())
+ thread_sp = m_threads[idx];
+ return thread_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadList.cpp b/contrib/llvm-project/lldb/source/Target/ThreadList.cpp
new file mode 100644
index 000000000000..afdfda3b0ae1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadList.cpp
@@ -0,0 +1,755 @@
+//===-- ThreadList.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ThreadList::ThreadList(Process *process)
+ : ThreadCollection(), m_process(process), m_stop_id(0),
+ m_selected_tid(LLDB_INVALID_THREAD_ID) {}
+
+ThreadList::ThreadList(const ThreadList &rhs)
+ : ThreadCollection(), m_process(rhs.m_process), m_stop_id(rhs.m_stop_id),
+ m_selected_tid() {
+ // Use the assignment operator since it uses the mutex
+ *this = rhs;
+}
+
+const ThreadList &ThreadList::operator=(const ThreadList &rhs) {
+ if (this != &rhs) {
+ // Lock both mutexes to make sure neither side changes anyone on us while
+ // the assignment occurs
+ std::lock(GetMutex(), rhs.GetMutex());
+ std::lock_guard<std::recursive_mutex> guard(GetMutex(), std::adopt_lock);
+ std::lock_guard<std::recursive_mutex> rhs_guard(rhs.GetMutex(),
+ std::adopt_lock);
+
+ m_process = rhs.m_process;
+ m_stop_id = rhs.m_stop_id;
+ m_threads = rhs.m_threads;
+ m_selected_tid = rhs.m_selected_tid;
+ }
+ return *this;
+}
+
+ThreadList::~ThreadList() {
+ // Clear the thread list. Clear will take the mutex lock which will ensure
+ // that if anyone is using the list they won't get it removed while using it.
+ Clear();
+}
+
+lldb::ThreadSP ThreadList::GetExpressionExecutionThread() {
+ if (m_expression_tid_stack.empty())
+ return GetSelectedThread();
+ ThreadSP expr_thread_sp = FindThreadByID(m_expression_tid_stack.back());
+ if (expr_thread_sp)
+ return expr_thread_sp;
+ else
+ return GetSelectedThread();
+}
+
+void ThreadList::PushExpressionExecutionThread(lldb::tid_t tid) {
+ m_expression_tid_stack.push_back(tid);
+}
+
+void ThreadList::PopExpressionExecutionThread(lldb::tid_t tid) {
+ assert(m_expression_tid_stack.back() == tid);
+ m_expression_tid_stack.pop_back();
+}
+
+uint32_t ThreadList::GetStopID() const { return m_stop_id; }
+
+void ThreadList::SetStopID(uint32_t stop_id) { m_stop_id = stop_id; }
+
+uint32_t ThreadList::GetSize(bool can_update) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+ return m_threads.size();
+}
+
+ThreadSP ThreadList::GetThreadAtIndex(uint32_t idx, bool can_update) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ if (idx < m_threads.size())
+ thread_sp = m_threads[idx];
+ return thread_sp;
+}
+
+ThreadSP ThreadList::FindThreadByID(lldb::tid_t tid, bool can_update) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx]->GetID() == tid) {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+ThreadSP ThreadList::FindThreadByProtocolID(lldb::tid_t tid, bool can_update) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx]->GetProtocolID() == tid) {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+ThreadSP ThreadList::RemoveThreadByID(lldb::tid_t tid, bool can_update) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx]->GetID() == tid) {
+ thread_sp = m_threads[idx];
+ m_threads.erase(m_threads.begin() + idx);
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+ThreadSP ThreadList::RemoveThreadByProtocolID(lldb::tid_t tid,
+ bool can_update) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx]->GetProtocolID() == tid) {
+ thread_sp = m_threads[idx];
+ m_threads.erase(m_threads.begin() + idx);
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+ThreadSP ThreadList::GetThreadSPForThreadPtr(Thread *thread_ptr) {
+ ThreadSP thread_sp;
+ if (thread_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx].get() == thread_ptr) {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ }
+ return thread_sp;
+}
+
+ThreadSP ThreadList::GetBackingThread(const ThreadSP &real_thread) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ ThreadSP thread_sp;
+ const uint32_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx]->GetBackingThread() == real_thread) {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+ThreadSP ThreadList::FindThreadByIndexID(uint32_t index_id, bool can_update) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ const uint32_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx]->GetIndexID() == index_id) {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+bool ThreadList::ShouldStop(Event *event_ptr) {
+ // Running events should never stop, obviously...
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ // The ShouldStop method of the threads can do a whole lot of work, figuring
+ // out whether the thread plan conditions are met. So we don't want to keep
+ // the ThreadList locked the whole time we are doing this.
+ // FIXME: It is possible that running code could cause new threads
+ // to be created. If that happens, we will miss asking them whether they
+ // should stop. This is not a big deal since we haven't had a chance to hang
+ // any interesting operations on those threads yet.
+
+ collection threads_copy;
+ {
+ // Scope for locker
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ m_process->UpdateThreadListIfNeeded();
+ for (lldb::ThreadSP thread_sp : m_threads) {
+ // This is an optimization... If we didn't let a thread run in between
+ // the previous stop and this one, we shouldn't have to consult it for
+ // ShouldStop. So just leave it off the list we are going to inspect. On
+ // Linux, if a thread-specific conditional breakpoint was hit, it won't
+ // necessarily be the thread that hit the breakpoint itself that
+ // evaluates the conditional expression, so the thread that hit the
+ // breakpoint could still be asked to stop, even though it hasn't been
+ // allowed to run since the previous stop.
+ if (thread_sp->GetTemporaryResumeState() != eStateSuspended ||
+ thread_sp->IsStillAtLastBreakpointHit())
+ threads_copy.push_back(thread_sp);
+ }
+
+ // It is possible the threads we were allowing to run all exited and then
+ // maybe the user interrupted or something, then fall back on looking at
+ // all threads:
+
+ if (threads_copy.size() == 0)
+ threads_copy = m_threads;
+ }
+
+ collection::iterator pos, end = threads_copy.end();
+
+ if (log) {
+ log->PutCString("");
+ log->Printf("ThreadList::%s: %" PRIu64 " threads, %" PRIu64
+ " unsuspended threads",
+ __FUNCTION__, (uint64_t)m_threads.size(),
+ (uint64_t)threads_copy.size());
+ }
+
+ bool did_anybody_stop_for_a_reason = false;
+
+ // If the event is an Interrupt event, then we're going to stop no matter
+ // what. Otherwise, presume we won't stop.
+ bool should_stop = false;
+ if (Process::ProcessEventData::GetInterruptedFromEvent(event_ptr)) {
+ if (log)
+ log->Printf(
+ "ThreadList::%s handling interrupt event, should stop set to true",
+ __FUNCTION__);
+
+ should_stop = true;
+ }
+
+ // Now we run through all the threads and get their stop info's. We want to
+ // make sure to do this first before we start running the ShouldStop, because
+ // one thread's ShouldStop could destroy information (like deleting a thread
+ // specific breakpoint another thread had stopped at) which could lead us to
+ // compute the StopInfo incorrectly. We don't need to use it here, we just
+ // want to make sure it gets computed.
+
+ for (pos = threads_copy.begin(); pos != end; ++pos) {
+ ThreadSP thread_sp(*pos);
+ thread_sp->GetStopInfo();
+ }
+
+ for (pos = threads_copy.begin(); pos != end; ++pos) {
+ ThreadSP thread_sp(*pos);
+
+ // We should never get a stop for which no thread had a stop reason, but
+ // sometimes we do see this - for instance when we first connect to a
+ // remote stub. In that case we should stop, since we can't figure out the
+ // right thing to do and stopping gives the user control over what to do in
+ // this instance.
+ //
+ // Note, this causes a problem when you have a thread specific breakpoint,
+ // and a bunch of threads hit the breakpoint, but not the thread which we
+ // are waiting for. All the threads that are not "supposed" to hit the
+ // breakpoint are marked as having no stop reason, which is right, they
+ // should not show a stop reason. But that triggers this code and causes
+ // us to stop seemingly for no reason.
+ //
+ // Since the only way we ever saw this error was on first attach, I'm only
+ // going to trigger set did_anybody_stop_for_a_reason to true unless this
+ // is the first stop.
+ //
+ // If this becomes a problem, we'll have to have another StopReason like
+ // "StopInfoHidden" which will look invalid everywhere but at this check.
+
+ if (thread_sp->GetProcess()->GetStopID() > 1)
+ did_anybody_stop_for_a_reason = true;
+ else
+ did_anybody_stop_for_a_reason |= thread_sp->ThreadStoppedForAReason();
+
+ const bool thread_should_stop = thread_sp->ShouldStop(event_ptr);
+ if (thread_should_stop)
+ should_stop |= true;
+ }
+
+ if (!should_stop && !did_anybody_stop_for_a_reason) {
+ should_stop = true;
+ if (log)
+ log->Printf("ThreadList::%s we stopped but no threads had a stop reason, "
+ "overriding should_stop and stopping.",
+ __FUNCTION__);
+ }
+
+ if (log)
+ log->Printf("ThreadList::%s overall should_stop = %i", __FUNCTION__,
+ should_stop);
+
+ if (should_stop) {
+ for (pos = threads_copy.begin(); pos != end; ++pos) {
+ ThreadSP thread_sp(*pos);
+ thread_sp->WillStop();
+ }
+ }
+
+ return should_stop;
+}
+
+Vote ThreadList::ShouldReportStop(Event *event_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ Vote result = eVoteNoOpinion;
+ m_process->UpdateThreadListIfNeeded();
+ collection::iterator pos, end = m_threads.end();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (log)
+ log->Printf("ThreadList::%s %" PRIu64 " threads", __FUNCTION__,
+ (uint64_t)m_threads.size());
+
+ // Run through the threads and ask whether we should report this event. For
+ // stopping, a YES vote wins over everything. A NO vote wins over NO
+ // opinion.
+ for (pos = m_threads.begin(); pos != end; ++pos) {
+ ThreadSP thread_sp(*pos);
+ const Vote vote = thread_sp->ShouldReportStop(event_ptr);
+ switch (vote) {
+ case eVoteNoOpinion:
+ continue;
+
+ case eVoteYes:
+ result = eVoteYes;
+ break;
+
+ case eVoteNo:
+ if (result == eVoteNoOpinion) {
+ result = eVoteNo;
+ } else {
+ LLDB_LOG(log,
+ "Thread {0:x} voted {1}, but lost out because result was {2}",
+ thread_sp->GetID(), vote, result);
+ }
+ break;
+ }
+ }
+ LLDB_LOG(log, "Returning {0}", result);
+ return result;
+}
+
+void ThreadList::SetShouldReportStop(Vote vote) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ m_process->UpdateThreadListIfNeeded();
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos) {
+ ThreadSP thread_sp(*pos);
+ thread_sp->SetShouldReportStop(vote);
+ }
+}
+
+Vote ThreadList::ShouldReportRun(Event *event_ptr) {
+
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ Vote result = eVoteNoOpinion;
+ m_process->UpdateThreadListIfNeeded();
+ collection::iterator pos, end = m_threads.end();
+
+ // Run through the threads and ask whether we should report this event. The
+ // rule is NO vote wins over everything, a YES vote wins over no opinion.
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ for (pos = m_threads.begin(); pos != end; ++pos) {
+ if ((*pos)->GetResumeState() != eStateSuspended) {
+ switch ((*pos)->ShouldReportRun(event_ptr)) {
+ case eVoteNoOpinion:
+ continue;
+ case eVoteYes:
+ if (result == eVoteNoOpinion)
+ result = eVoteYes;
+ break;
+ case eVoteNo:
+ if (log)
+ log->Printf("ThreadList::ShouldReportRun() thread %d (0x%4.4" PRIx64
+ ") says don't report.",
+ (*pos)->GetIndexID(), (*pos)->GetID());
+ result = eVoteNo;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+void ThreadList::Clear() {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ m_stop_id = 0;
+ m_threads.clear();
+ m_selected_tid = LLDB_INVALID_THREAD_ID;
+}
+
+void ThreadList::Destroy() {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ const uint32_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ m_threads[idx]->DestroyThread();
+ }
+}
+
+void ThreadList::RefreshStateAfterStop() {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ m_process->UpdateThreadListIfNeeded();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf("Turning off notification of new threads while single stepping "
+ "a thread.");
+
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ (*pos)->RefreshStateAfterStop();
+}
+
+void ThreadList::DiscardThreadPlans() {
+ // You don't need to update the thread list here, because only threads that
+ // you currently know about have any thread plans.
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ (*pos)->DiscardThreadPlans(true);
+}
+
+bool ThreadList::WillResume() {
+ // Run through the threads and perform their momentary actions. But we only
+ // do this for threads that are running, user suspended threads stay where
+ // they are.
+
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ m_process->UpdateThreadListIfNeeded();
+
+ collection::iterator pos, end = m_threads.end();
+
+ // See if any thread wants to run stopping others. If it does, then we won't
+ // setup the other threads for resume, since they aren't going to get a
+ // chance to run. This is necessary because the SetupForResume might add
+ // "StopOthers" plans which would then get to be part of the who-gets-to-run
+ // negotiation, but they're coming in after the fact, and the threads that
+ // are already set up should take priority.
+
+ bool wants_solo_run = false;
+
+ for (pos = m_threads.begin(); pos != end; ++pos) {
+ lldbassert((*pos)->GetCurrentPlan() &&
+ "thread should not have null thread plan");
+ if ((*pos)->GetResumeState() != eStateSuspended &&
+ (*pos)->GetCurrentPlan()->StopOthers()) {
+ if ((*pos)->IsOperatingSystemPluginThread() &&
+ !(*pos)->GetBackingThread())
+ continue;
+ wants_solo_run = true;
+ break;
+ }
+ }
+
+ if (wants_solo_run) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf("Turning on notification of new threads while single "
+ "stepping a thread.");
+ m_process->StartNoticingNewThreads();
+ } else {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf("Turning off notification of new threads while single "
+ "stepping a thread.");
+ m_process->StopNoticingNewThreads();
+ }
+
+ // Give all the threads that are likely to run a last chance to set up their
+ // state before we negotiate who is actually going to get a chance to run...
+ // Don't set to resume suspended threads, and if any thread wanted to stop
+ // others, only call setup on the threads that request StopOthers...
+
+ for (pos = m_threads.begin(); pos != end; ++pos) {
+ if ((*pos)->GetResumeState() != eStateSuspended &&
+ (!wants_solo_run || (*pos)->GetCurrentPlan()->StopOthers())) {
+ if ((*pos)->IsOperatingSystemPluginThread() &&
+ !(*pos)->GetBackingThread())
+ continue;
+ (*pos)->SetupForResume();
+ }
+ }
+
+ // Now go through the threads and see if any thread wants to run just itself.
+ // if so then pick one and run it.
+
+ ThreadList run_me_only_list(m_process);
+
+ run_me_only_list.SetStopID(m_process->GetStopID());
+
+ bool run_only_current_thread = false;
+
+ for (pos = m_threads.begin(); pos != end; ++pos) {
+ ThreadSP thread_sp(*pos);
+ if (thread_sp->GetResumeState() != eStateSuspended &&
+ thread_sp->GetCurrentPlan()->StopOthers()) {
+ if ((*pos)->IsOperatingSystemPluginThread() &&
+ !(*pos)->GetBackingThread())
+ continue;
+
+ // You can't say "stop others" and also want yourself to be suspended.
+ assert(thread_sp->GetCurrentPlan()->RunState() != eStateSuspended);
+
+ if (thread_sp == GetSelectedThread()) {
+ // If the currently selected thread wants to run on its own, always let
+ // it.
+ run_only_current_thread = true;
+ run_me_only_list.Clear();
+ run_me_only_list.AddThread(thread_sp);
+ break;
+ }
+
+ run_me_only_list.AddThread(thread_sp);
+ }
+ }
+
+ bool need_to_resume = true;
+
+ if (run_me_only_list.GetSize(false) == 0) {
+ // Everybody runs as they wish:
+ for (pos = m_threads.begin(); pos != end; ++pos) {
+ ThreadSP thread_sp(*pos);
+ StateType run_state;
+ if (thread_sp->GetResumeState() != eStateSuspended)
+ run_state = thread_sp->GetCurrentPlan()->RunState();
+ else
+ run_state = eStateSuspended;
+ if (!thread_sp->ShouldResume(run_state))
+ need_to_resume = false;
+ }
+ } else {
+ ThreadSP thread_to_run;
+
+ if (run_only_current_thread) {
+ thread_to_run = GetSelectedThread();
+ } else if (run_me_only_list.GetSize(false) == 1) {
+ thread_to_run = run_me_only_list.GetThreadAtIndex(0);
+ } else {
+ int random_thread =
+ (int)((run_me_only_list.GetSize(false) * (double)rand()) /
+ (RAND_MAX + 1.0));
+ thread_to_run = run_me_only_list.GetThreadAtIndex(random_thread);
+ }
+
+ for (pos = m_threads.begin(); pos != end; ++pos) {
+ ThreadSP thread_sp(*pos);
+ if (thread_sp == thread_to_run) {
+ if (!thread_sp->ShouldResume(thread_sp->GetCurrentPlan()->RunState()))
+ need_to_resume = false;
+ } else
+ thread_sp->ShouldResume(eStateSuspended);
+ }
+ }
+
+ return need_to_resume;
+}
+
+void ThreadList::DidResume() {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos) {
+ // Don't clear out threads that aren't going to get a chance to run, rather
+ // leave their state for the next time around.
+ ThreadSP thread_sp(*pos);
+ if (thread_sp->GetResumeState() != eStateSuspended)
+ thread_sp->DidResume();
+ }
+}
+
+void ThreadList::DidStop() {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos) {
+ // Notify threads that the process just stopped. Note, this currently
+ // assumes that all threads in the list stop when the process stops. In
+ // the future we will want to support a debugging model where some threads
+ // continue to run while others are stopped. We either need to handle that
+ // somehow here or create a special thread list containing only threads
+ // which will stop in the code that calls this method (currently
+ // Process::SetPrivateState).
+ ThreadSP thread_sp(*pos);
+ if (StateIsRunningState(thread_sp->GetState()))
+ thread_sp->DidStop();
+ }
+}
+
+ThreadSP ThreadList::GetSelectedThread() {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ ThreadSP thread_sp = FindThreadByID(m_selected_tid);
+ if (!thread_sp.get()) {
+ if (m_threads.size() == 0)
+ return thread_sp;
+ m_selected_tid = m_threads[0]->GetID();
+ thread_sp = m_threads[0];
+ }
+ return thread_sp;
+}
+
+bool ThreadList::SetSelectedThreadByID(lldb::tid_t tid, bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ ThreadSP selected_thread_sp(FindThreadByID(tid));
+ if (selected_thread_sp) {
+ m_selected_tid = tid;
+ selected_thread_sp->SetDefaultFileAndLineToSelectedFrame();
+ } else
+ m_selected_tid = LLDB_INVALID_THREAD_ID;
+
+ if (notify)
+ NotifySelectedThreadChanged(m_selected_tid);
+
+ return m_selected_tid != LLDB_INVALID_THREAD_ID;
+}
+
+bool ThreadList::SetSelectedThreadByIndexID(uint32_t index_id, bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ ThreadSP selected_thread_sp(FindThreadByIndexID(index_id));
+ if (selected_thread_sp.get()) {
+ m_selected_tid = selected_thread_sp->GetID();
+ selected_thread_sp->SetDefaultFileAndLineToSelectedFrame();
+ } else
+ m_selected_tid = LLDB_INVALID_THREAD_ID;
+
+ if (notify)
+ NotifySelectedThreadChanged(m_selected_tid);
+
+ return m_selected_tid != LLDB_INVALID_THREAD_ID;
+}
+
+void ThreadList::NotifySelectedThreadChanged(lldb::tid_t tid) {
+ ThreadSP selected_thread_sp(FindThreadByID(tid));
+ if (selected_thread_sp->EventTypeHasListeners(
+ Thread::eBroadcastBitThreadSelected))
+ selected_thread_sp->BroadcastEvent(
+ Thread::eBroadcastBitThreadSelected,
+ new Thread::ThreadEventData(selected_thread_sp));
+}
+
+void ThreadList::Update(ThreadList &rhs) {
+ if (this != &rhs) {
+ // Lock both mutexes to make sure neither side changes anyone on us while
+ // the assignment occurs
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+
+ m_process = rhs.m_process;
+ m_stop_id = rhs.m_stop_id;
+ m_threads.swap(rhs.m_threads);
+ m_selected_tid = rhs.m_selected_tid;
+
+ // Now we look for threads that we are done with and make sure to clear
+ // them up as much as possible so anyone with a shared pointer will still
+ // have a reference, but the thread won't be of much use. Using
+ // std::weak_ptr for all backward references (such as a thread to a
+ // process) will eventually solve this issue for us, but for now, we need
+ // to work around the issue
+ collection::iterator rhs_pos, rhs_end = rhs.m_threads.end();
+ for (rhs_pos = rhs.m_threads.begin(); rhs_pos != rhs_end; ++rhs_pos) {
+ const lldb::tid_t tid = (*rhs_pos)->GetID();
+ bool thread_is_alive = false;
+ const uint32_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ ThreadSP backing_thread = m_threads[idx]->GetBackingThread();
+ if (m_threads[idx]->GetID() == tid ||
+ (backing_thread && backing_thread->GetID() == tid)) {
+ thread_is_alive = true;
+ break;
+ }
+ }
+ if (!thread_is_alive)
+ (*rhs_pos)->DestroyThread();
+ }
+ }
+}
+
+void ThreadList::Flush() {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ (*pos)->Flush();
+}
+
+std::recursive_mutex &ThreadList::GetMutex() const {
+ return m_process->m_thread_mutex;
+}
+
+ThreadList::ExpressionExecutionThreadPusher::ExpressionExecutionThreadPusher(
+ lldb::ThreadSP thread_sp)
+ : m_thread_list(nullptr), m_tid(LLDB_INVALID_THREAD_ID) {
+ if (thread_sp) {
+ m_tid = thread_sp->GetID();
+ m_thread_list = &thread_sp->GetProcess()->GetThreadList();
+ m_thread_list->PushExpressionExecutionThread(m_tid);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlan.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlan.cpp
new file mode 100644
index 000000000000..1d8cc18db4d6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlan.cpp
@@ -0,0 +1,274 @@
+//===-- ThreadPlan.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlan constructor
+ThreadPlan::ThreadPlan(ThreadPlanKind kind, const char *name, Thread &thread,
+ Vote stop_vote, Vote run_vote)
+ : m_thread(thread), m_stop_vote(stop_vote), m_run_vote(run_vote),
+ m_takes_iteration_count(false), m_could_not_resolve_hw_bp(false),
+ m_kind(kind), m_name(name), m_plan_complete_mutex(),
+ m_cached_plan_explains_stop(eLazyBoolCalculate), m_plan_complete(false),
+ m_plan_private(false), m_okay_to_discard(true), m_is_master_plan(false),
+ m_plan_succeeded(true) {
+ SetID(GetNextID());
+}
+
+// Destructor
+ThreadPlan::~ThreadPlan() = default;
+
+bool ThreadPlan::PlanExplainsStop(Event *event_ptr) {
+ if (m_cached_plan_explains_stop == eLazyBoolCalculate) {
+ bool actual_value = DoPlanExplainsStop(event_ptr);
+ m_cached_plan_explains_stop = actual_value ? eLazyBoolYes : eLazyBoolNo;
+ return actual_value;
+ } else {
+ return m_cached_plan_explains_stop == eLazyBoolYes;
+ }
+}
+
+bool ThreadPlan::IsPlanComplete() {
+ std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex);
+ return m_plan_complete;
+}
+
+void ThreadPlan::SetPlanComplete(bool success) {
+ std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex);
+ m_plan_complete = true;
+ m_plan_succeeded = success;
+}
+
+bool ThreadPlan::MischiefManaged() {
+ std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex);
+ // Mark the plan is complete, but don't override the success flag.
+ m_plan_complete = true;
+ return true;
+}
+
+Vote ThreadPlan::ShouldReportStop(Event *event_ptr) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (m_stop_vote == eVoteNoOpinion) {
+ ThreadPlan *prev_plan = GetPreviousPlan();
+ if (prev_plan) {
+ Vote prev_vote = prev_plan->ShouldReportStop(event_ptr);
+ LLDB_LOG(log, "returning previous thread plan vote: {0}", prev_vote);
+ return prev_vote;
+ }
+ }
+ LLDB_LOG(log, "Returning vote: {0}", m_stop_vote);
+ return m_stop_vote;
+}
+
+Vote ThreadPlan::ShouldReportRun(Event *event_ptr) {
+ if (m_run_vote == eVoteNoOpinion) {
+ ThreadPlan *prev_plan = GetPreviousPlan();
+ if (prev_plan)
+ return prev_plan->ShouldReportRun(event_ptr);
+ }
+ return m_run_vote;
+}
+
+bool ThreadPlan::StopOthers() {
+ ThreadPlan *prev_plan;
+ prev_plan = GetPreviousPlan();
+ return (prev_plan == nullptr) ? false : prev_plan->StopOthers();
+}
+
+void ThreadPlan::SetStopOthers(bool new_value) {
+ // SetStopOthers doesn't work up the hierarchy. You have to set the explicit
+ // ThreadPlan you want to affect.
+}
+
+bool ThreadPlan::WillResume(StateType resume_state, bool current_plan) {
+ m_cached_plan_explains_stop = eLazyBoolCalculate;
+
+ if (current_plan) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (log) {
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+ assert(reg_ctx);
+ addr_t pc = reg_ctx->GetPC();
+ addr_t sp = reg_ctx->GetSP();
+ addr_t fp = reg_ctx->GetFP();
+ log->Printf(
+ "%s Thread #%u (0x%p): tid = 0x%4.4" PRIx64 ", pc = 0x%8.8" PRIx64
+ ", sp = 0x%8.8" PRIx64 ", fp = 0x%8.8" PRIx64 ", "
+ "plan = '%s', state = %s, stop others = %d",
+ __FUNCTION__, m_thread.GetIndexID(), static_cast<void *>(&m_thread),
+ m_thread.GetID(), static_cast<uint64_t>(pc),
+ static_cast<uint64_t>(sp), static_cast<uint64_t>(fp), m_name.c_str(),
+ StateAsCString(resume_state), StopOthers());
+ }
+ }
+ return DoWillResume(resume_state, current_plan);
+}
+
+lldb::user_id_t ThreadPlan::GetNextID() {
+ static uint32_t g_nextPlanID = 0;
+ return ++g_nextPlanID;
+}
+
+void ThreadPlan::DidPush() {}
+
+void ThreadPlan::WillPop() {}
+
+bool ThreadPlan::OkayToDiscard() {
+ return IsMasterPlan() ? m_okay_to_discard : true;
+}
+
+lldb::StateType ThreadPlan::RunState() {
+ if (m_tracer_sp && m_tracer_sp->TracingEnabled() &&
+ m_tracer_sp->SingleStepEnabled())
+ return eStateStepping;
+ else
+ return GetPlanRunState();
+}
+
+bool ThreadPlan::IsUsuallyUnexplainedStopReason(lldb::StopReason reason) {
+ switch (reason) {
+ case eStopReasonWatchpoint:
+ case eStopReasonSignal:
+ case eStopReasonException:
+ case eStopReasonExec:
+ case eStopReasonThreadExiting:
+ case eStopReasonInstrumentation:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// ThreadPlanNull
+
+ThreadPlanNull::ThreadPlanNull(Thread &thread)
+ : ThreadPlan(ThreadPlan::eKindNull, "Null Thread Plan", thread,
+ eVoteNoOpinion, eVoteNoOpinion) {}
+
+ThreadPlanNull::~ThreadPlanNull() = default;
+
+void ThreadPlanNull::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ s->PutCString("Null thread plan - thread has been destroyed.");
+}
+
+bool ThreadPlanNull::ValidatePlan(Stream *error) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr,
+ "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return true;
+}
+
+bool ThreadPlanNull::ShouldStop(Event *event_ptr) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr,
+ "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return true;
+}
+
+bool ThreadPlanNull::WillStop() {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr,
+ "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return true;
+}
+
+bool ThreadPlanNull::DoPlanExplainsStop(Event *event_ptr) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr,
+ "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return true;
+}
+
+// The null plan is never done.
+bool ThreadPlanNull::MischiefManaged() {
+// The null plan is never done.
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr,
+ "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return false;
+}
+
+lldb::StateType ThreadPlanNull::GetPlanRunState() {
+// Not sure what to return here. This is a dead thread.
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr,
+ "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(), m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
+ ", ptid = 0x%" PRIx64 ")",
+ LLVM_PRETTY_FUNCTION, m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return eStateRunning;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanBase.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanBase.cpp
new file mode 100644
index 000000000000..9cd4bcb455be
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanBase.cpp
@@ -0,0 +1,197 @@
+//===-- ThreadPlanBase.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanBase.h"
+
+//
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointSite.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanBase: This one always stops, and never has anything particular to
+// do.
+// FIXME: The "signal handling" policies should probably go here.
+
+ThreadPlanBase::ThreadPlanBase(Thread &thread)
+ : ThreadPlan(ThreadPlan::eKindBase, "base plan", thread, eVoteYes,
+ eVoteNoOpinion) {
+// Set the tracer to a default tracer.
+// FIXME: need to add a thread settings variable to pix various tracers...
+#define THREAD_PLAN_USE_ASSEMBLY_TRACER 1
+
+#ifdef THREAD_PLAN_USE_ASSEMBLY_TRACER
+ ThreadPlanTracerSP new_tracer_sp(new ThreadPlanAssemblyTracer(m_thread));
+#else
+ ThreadPlanTracerSP new_tracer_sp(new ThreadPlanTracer(m_thread));
+#endif
+ new_tracer_sp->EnableTracing(m_thread.GetTraceEnabledState());
+ SetThreadPlanTracer(new_tracer_sp);
+ SetIsMasterPlan(true);
+}
+
+ThreadPlanBase::~ThreadPlanBase() {}
+
+void ThreadPlanBase::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ s->Printf("Base thread plan.");
+}
+
+bool ThreadPlanBase::ValidatePlan(Stream *error) { return true; }
+
+bool ThreadPlanBase::DoPlanExplainsStop(Event *event_ptr) {
+ // The base plan should defer to its tracer, since by default it always
+ // handles the stop.
+ return !TracerExplainsStop();
+}
+
+Vote ThreadPlanBase::ShouldReportStop(Event *event_ptr) {
+ StopInfoSP stop_info_sp = m_thread.GetStopInfo();
+ if (stop_info_sp) {
+ bool should_notify = stop_info_sp->ShouldNotify(event_ptr);
+ if (should_notify)
+ return eVoteYes;
+ else
+ return eVoteNoOpinion;
+ } else
+ return eVoteNoOpinion;
+}
+
+bool ThreadPlanBase::ShouldStop(Event *event_ptr) {
+ m_stop_vote = eVoteYes;
+ m_run_vote = eVoteYes;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ StopInfoSP stop_info_sp = GetPrivateStopInfo();
+ if (stop_info_sp) {
+ StopReason reason = stop_info_sp->GetStopReason();
+ switch (reason) {
+ case eStopReasonInvalid:
+ case eStopReasonNone:
+ // This
+ m_run_vote = eVoteNoOpinion;
+ m_stop_vote = eVoteNo;
+ return false;
+
+ case eStopReasonBreakpoint:
+ case eStopReasonWatchpoint:
+ if (stop_info_sp->ShouldStopSynchronous(event_ptr)) {
+ // If we are going to stop for a breakpoint, then unship the other
+ // plans at this point. Don't force the discard, however, so Master
+ // plans can stay in place if they want to.
+ if (log)
+ log->Printf(
+ "Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64
+ " (breakpoint hit.)",
+ m_thread.GetID());
+ m_thread.DiscardThreadPlans(false);
+ return true;
+ }
+ // If we aren't going to stop at this breakpoint, and it is internal,
+ // don't report this stop or the subsequent running event. Otherwise we
+ // will post the stopped & running, but the stopped event will get marked
+ // with "restarted" so the UI will know to wait and expect the consequent
+ // "running".
+ if (stop_info_sp->ShouldNotify(event_ptr)) {
+ m_stop_vote = eVoteYes;
+ m_run_vote = eVoteYes;
+ } else {
+ m_stop_vote = eVoteNo;
+ m_run_vote = eVoteNo;
+ }
+ return false;
+
+ // TODO: the break below was missing, was this intentional??? If so
+ // please mention it
+ break;
+
+ case eStopReasonException:
+ // If we crashed, discard thread plans and stop. Don't force the
+ // discard, however, since on rerun the target may clean up this
+ // exception and continue normally from there.
+ if (log)
+ log->Printf(
+ "Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64
+ " (exception: %s)",
+ m_thread.GetID(), stop_info_sp->GetDescription());
+ m_thread.DiscardThreadPlans(false);
+ return true;
+
+ case eStopReasonExec:
+ // If we crashed, discard thread plans and stop. Don't force the
+ // discard, however, since on rerun the target may clean up this
+ // exception and continue normally from there.
+ if (log)
+ log->Printf(
+ "Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64
+ " (exec.)",
+ m_thread.GetID());
+ m_thread.DiscardThreadPlans(false);
+ return true;
+
+ case eStopReasonThreadExiting:
+ case eStopReasonSignal:
+ if (stop_info_sp->ShouldStop(event_ptr)) {
+ if (log)
+ log->Printf(
+ "Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64
+ " (signal: %s)",
+ m_thread.GetID(), stop_info_sp->GetDescription());
+ m_thread.DiscardThreadPlans(false);
+ return true;
+ } else {
+ // We're not going to stop, but while we are here, let's figure out
+ // whether to report this.
+ if (stop_info_sp->ShouldNotify(event_ptr))
+ m_stop_vote = eVoteYes;
+ else
+ m_stop_vote = eVoteNo;
+ }
+ return false;
+
+ default:
+ return true;
+ }
+
+ } else {
+ m_run_vote = eVoteNoOpinion;
+ m_stop_vote = eVoteNo;
+ }
+
+ // If there's no explicit reason to stop, then we will continue.
+ return false;
+}
+
+bool ThreadPlanBase::StopOthers() { return false; }
+
+StateType ThreadPlanBase::GetPlanRunState() { return eStateRunning; }
+
+bool ThreadPlanBase::WillStop() { return true; }
+
+bool ThreadPlanBase::DoWillResume(lldb::StateType resume_state,
+ bool current_plan) {
+ // Reset these to the default values so we don't set them wrong, then not get
+ // asked for a while, then return the wrong answer.
+ m_run_vote = eVoteNoOpinion;
+ m_stop_vote = eVoteNo;
+ return true;
+}
+
+// The base plan is never done.
+bool ThreadPlanBase::MischiefManaged() {
+ // The base plan is never done.
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanCallFunction.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanCallFunction.cpp
new file mode 100644
index 000000000000..68d771c37946
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanCallFunction.cpp
@@ -0,0 +1,505 @@
+//===-- ThreadPlanCallFunction.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanCallFunction: Plan to call a single function
+bool ThreadPlanCallFunction::ConstructorSetup(
+ Thread &thread, ABI *&abi, lldb::addr_t &start_load_addr,
+ lldb::addr_t &function_load_addr) {
+ SetIsMasterPlan(true);
+ SetOkayToDiscard(false);
+ SetPrivate(true);
+
+ ProcessSP process_sp(thread.GetProcess());
+ if (!process_sp)
+ return false;
+
+ abi = process_sp->GetABI().get();
+
+ if (!abi)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP));
+
+ SetBreakpoints();
+
+ m_function_sp = thread.GetRegisterContext()->GetSP() - abi->GetRedZoneSize();
+ // If we can't read memory at the point of the process where we are planning
+ // to put our function, we're not going to get any further...
+ Status error;
+ process_sp->ReadUnsignedIntegerFromMemory(m_function_sp, 4, 0, error);
+ if (!error.Success()) {
+ m_constructor_errors.Printf(
+ "Trying to put the stack in unreadable memory at: 0x%" PRIx64 ".",
+ m_function_sp);
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): %s.", static_cast<void *>(this),
+ m_constructor_errors.GetData());
+ return false;
+ }
+
+ Module *exe_module = GetTarget().GetExecutableModulePointer();
+
+ if (exe_module == nullptr) {
+ m_constructor_errors.Printf(
+ "Can't execute code without an executable module.");
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): %s.", static_cast<void *>(this),
+ m_constructor_errors.GetData());
+ return false;
+ } else {
+ ObjectFile *objectFile = exe_module->GetObjectFile();
+ if (!objectFile) {
+ m_constructor_errors.Printf(
+ "Could not find object file for module \"%s\".",
+ exe_module->GetFileSpec().GetFilename().AsCString());
+
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): %s.",
+ static_cast<void *>(this), m_constructor_errors.GetData());
+ return false;
+ }
+
+ m_start_addr = objectFile->GetEntryPointAddress();
+ if (!m_start_addr.IsValid()) {
+ m_constructor_errors.Printf(
+ "Could not find entry point address for executable module \"%s\".",
+ exe_module->GetFileSpec().GetFilename().AsCString());
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): %s.",
+ static_cast<void *>(this), m_constructor_errors.GetData());
+ return false;
+ }
+ }
+
+ start_load_addr = m_start_addr.GetLoadAddress(&GetTarget());
+
+ // Checkpoint the thread state so we can restore it later.
+ if (log && log->GetVerbose())
+ ReportRegisterState("About to checkpoint thread before function call. "
+ "Original register state was:");
+
+ if (!thread.CheckpointThreadState(m_stored_thread_state)) {
+ m_constructor_errors.Printf("Setting up ThreadPlanCallFunction, failed to "
+ "checkpoint thread state.");
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): %s.", static_cast<void *>(this),
+ m_constructor_errors.GetData());
+ return false;
+ }
+ function_load_addr = m_function_addr.GetLoadAddress(&GetTarget());
+
+ return true;
+}
+
+ThreadPlanCallFunction::ThreadPlanCallFunction(
+ Thread &thread, const Address &function, const CompilerType &return_type,
+ llvm::ArrayRef<addr_t> args, const EvaluateExpressionOptions &options)
+ : ThreadPlan(ThreadPlan::eKindCallFunction, "Call function plan", thread,
+ eVoteNoOpinion, eVoteNoOpinion),
+ m_valid(false), m_stop_other_threads(options.GetStopOthers()),
+ m_unwind_on_error(options.DoesUnwindOnError()),
+ m_ignore_breakpoints(options.DoesIgnoreBreakpoints()),
+ m_debug_execution(options.GetDebug()),
+ m_trap_exceptions(options.GetTrapExceptions()), m_function_addr(function),
+ m_function_sp(0), m_takedown_done(false),
+ m_should_clear_objc_exception_bp(false),
+ m_should_clear_cxx_exception_bp(false),
+ m_stop_address(LLDB_INVALID_ADDRESS), m_return_type(return_type) {
+ lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS;
+ ABI *abi = nullptr;
+
+ if (!ConstructorSetup(thread, abi, start_load_addr, function_load_addr))
+ return;
+
+ if (!abi->PrepareTrivialCall(thread, m_function_sp, function_load_addr,
+ start_load_addr, args))
+ return;
+
+ ReportRegisterState("Function call was set up. Register state was:");
+
+ m_valid = true;
+}
+
+ThreadPlanCallFunction::ThreadPlanCallFunction(
+ Thread &thread, const Address &function,
+ const EvaluateExpressionOptions &options)
+ : ThreadPlan(ThreadPlan::eKindCallFunction, "Call function plan", thread,
+ eVoteNoOpinion, eVoteNoOpinion),
+ m_valid(false), m_stop_other_threads(options.GetStopOthers()),
+ m_unwind_on_error(options.DoesUnwindOnError()),
+ m_ignore_breakpoints(options.DoesIgnoreBreakpoints()),
+ m_debug_execution(options.GetDebug()),
+ m_trap_exceptions(options.GetTrapExceptions()), m_function_addr(function),
+ m_function_sp(0), m_takedown_done(false),
+ m_should_clear_objc_exception_bp(false),
+ m_should_clear_cxx_exception_bp(false),
+ m_stop_address(LLDB_INVALID_ADDRESS), m_return_type(CompilerType()) {}
+
+ThreadPlanCallFunction::~ThreadPlanCallFunction() {
+ DoTakedown(PlanSucceeded());
+}
+
+void ThreadPlanCallFunction::ReportRegisterState(const char *message) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+
+ log->PutCString(message);
+
+ RegisterValue reg_value;
+
+ for (uint32_t reg_idx = 0, num_registers = reg_ctx->GetRegisterCount();
+ reg_idx < num_registers; ++reg_idx) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx);
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ DumpRegisterValue(reg_value, &strm, reg_info, true, false,
+ eFormatDefault);
+ strm.EOL();
+ }
+ }
+ log->PutString(strm.GetString());
+ }
+}
+
+void ThreadPlanCallFunction::DoTakedown(bool success) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (!m_valid) {
+ // Don't call DoTakedown if we were never valid to begin with.
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): Log called on "
+ "ThreadPlanCallFunction that was never valid.",
+ static_cast<void *>(this));
+ return;
+ }
+
+ if (!m_takedown_done) {
+ if (success) {
+ SetReturnValue();
+ }
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): DoTakedown called for thread "
+ "0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n",
+ static_cast<void *>(this), m_thread.GetID(), m_valid,
+ IsPlanComplete());
+ m_takedown_done = true;
+ m_stop_address =
+ m_thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC();
+ m_real_stop_info_sp = GetPrivateStopInfo();
+ if (!m_thread.RestoreRegisterStateFromCheckpoint(m_stored_thread_state)) {
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): DoTakedown failed to restore "
+ "register state",
+ static_cast<void *>(this));
+ }
+ SetPlanComplete(success);
+ ClearBreakpoints();
+ if (log && log->GetVerbose())
+ ReportRegisterState("Restoring thread state after function call. "
+ "Restored register state:");
+ } else {
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): DoTakedown called as no-op for "
+ "thread 0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n",
+ static_cast<void *>(this), m_thread.GetID(), m_valid,
+ IsPlanComplete());
+ }
+}
+
+void ThreadPlanCallFunction::WillPop() { DoTakedown(PlanSucceeded()); }
+
+void ThreadPlanCallFunction::GetDescription(Stream *s, DescriptionLevel level) {
+ if (level == eDescriptionLevelBrief) {
+ s->Printf("Function call thread plan");
+ } else {
+ TargetSP target_sp(m_thread.CalculateTarget());
+ s->Printf("Thread plan to call 0x%" PRIx64,
+ m_function_addr.GetLoadAddress(target_sp.get()));
+ }
+}
+
+bool ThreadPlanCallFunction::ValidatePlan(Stream *error) {
+ if (!m_valid) {
+ if (error) {
+ if (m_constructor_errors.GetSize() > 0)
+ error->PutCString(m_constructor_errors.GetString());
+ else
+ error->PutCString("Unknown error");
+ }
+ return false;
+ }
+
+ return true;
+}
+
+Vote ThreadPlanCallFunction::ShouldReportStop(Event *event_ptr) {
+ if (m_takedown_done || IsPlanComplete())
+ return eVoteYes;
+ else
+ return ThreadPlan::ShouldReportStop(event_ptr);
+}
+
+bool ThreadPlanCallFunction::DoPlanExplainsStop(Event *event_ptr) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP |
+ LIBLLDB_LOG_PROCESS));
+ m_real_stop_info_sp = GetPrivateStopInfo();
+
+ // If our subplan knows why we stopped, even if it's done (which would
+ // forward the question to us) we answer yes.
+ if (m_subplan_sp && m_subplan_sp->PlanExplainsStop(event_ptr)) {
+ SetPlanComplete();
+ return true;
+ }
+
+ // Check if the breakpoint is one of ours.
+
+ StopReason stop_reason;
+ if (!m_real_stop_info_sp)
+ stop_reason = eStopReasonNone;
+ else
+ stop_reason = m_real_stop_info_sp->GetStopReason();
+ if (log)
+ log->Printf(
+ "ThreadPlanCallFunction::PlanExplainsStop: Got stop reason - %s.",
+ Thread::StopReasonAsCString(stop_reason));
+
+ if (stop_reason == eStopReasonBreakpoint && BreakpointsExplainStop())
+ return true;
+
+ // One more quirk here. If this event was from Halt interrupting the target,
+ // then we should not consider ourselves complete. Return true to
+ // acknowledge the stop.
+ if (Process::ProcessEventData::GetInterruptedFromEvent(event_ptr)) {
+ if (log)
+ log->Printf("ThreadPlanCallFunction::PlanExplainsStop: The event is an "
+ "Interrupt, returning true.");
+ return true;
+ }
+ // We control breakpoints separately from other "stop reasons." So first,
+ // check the case where we stopped for an internal breakpoint, in that case,
+ // continue on. If it is not an internal breakpoint, consult
+ // m_ignore_breakpoints.
+
+ if (stop_reason == eStopReasonBreakpoint) {
+ ProcessSP process_sp(m_thread.CalculateProcess());
+ uint64_t break_site_id = m_real_stop_info_sp->GetValue();
+ BreakpointSiteSP bp_site_sp;
+ if (process_sp)
+ bp_site_sp = process_sp->GetBreakpointSiteList().FindByID(break_site_id);
+ if (bp_site_sp) {
+ uint32_t num_owners = bp_site_sp->GetNumberOfOwners();
+ bool is_internal = true;
+ for (uint32_t i = 0; i < num_owners; i++) {
+ Breakpoint &bp = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint();
+ if (log)
+ log->Printf("ThreadPlanCallFunction::PlanExplainsStop: hit "
+ "breakpoint %d while calling function",
+ bp.GetID());
+
+ if (!bp.IsInternal()) {
+ is_internal = false;
+ break;
+ }
+ }
+ if (is_internal) {
+ if (log)
+ log->Printf("ThreadPlanCallFunction::PlanExplainsStop hit an "
+ "internal breakpoint, not stopping.");
+ return false;
+ }
+ }
+
+ if (m_ignore_breakpoints) {
+ if (log)
+ log->Printf("ThreadPlanCallFunction::PlanExplainsStop: we are ignoring "
+ "breakpoints, overriding breakpoint stop info ShouldStop, "
+ "returning true");
+ m_real_stop_info_sp->OverrideShouldStop(false);
+ return true;
+ } else {
+ if (log)
+ log->Printf("ThreadPlanCallFunction::PlanExplainsStop: we are not "
+ "ignoring breakpoints, overriding breakpoint stop info "
+ "ShouldStop, returning true");
+ m_real_stop_info_sp->OverrideShouldStop(true);
+ return false;
+ }
+ } else if (!m_unwind_on_error) {
+ // If we don't want to discard this plan, than any stop we don't understand
+ // should be propagated up the stack.
+ return false;
+ } else {
+ // If the subplan is running, any crashes are attributable to us. If we
+ // want to discard the plan, then we say we explain the stop but if we are
+ // going to be discarded, let whoever is above us explain the stop. But
+ // don't discard the plan if the stop would restart itself (for instance if
+ // it is a signal that is set not to stop. Check that here first. We just
+ // say we explain the stop but aren't done and everything will continue on
+ // from there.
+
+ if (m_real_stop_info_sp &&
+ m_real_stop_info_sp->ShouldStopSynchronous(event_ptr)) {
+ SetPlanComplete(false);
+ return m_subplan_sp ? m_unwind_on_error : false;
+ } else
+ return true;
+ }
+}
+
+bool ThreadPlanCallFunction::ShouldStop(Event *event_ptr) {
+ // We do some computation in DoPlanExplainsStop that may or may not set the
+ // plan as complete. We need to do that here to make sure our state is
+ // correct.
+ DoPlanExplainsStop(event_ptr);
+
+ if (IsPlanComplete()) {
+ ReportRegisterState("Function completed. Register state was:");
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool ThreadPlanCallFunction::StopOthers() { return m_stop_other_threads; }
+
+StateType ThreadPlanCallFunction::GetPlanRunState() { return eStateRunning; }
+
+void ThreadPlanCallFunction::DidPush() {
+ //#define SINGLE_STEP_EXPRESSIONS
+
+ // Now set the thread state to "no reason" so we don't run with whatever
+ // signal was outstanding... Wait till the plan is pushed so we aren't
+ // changing the stop info till we're about to run.
+
+ GetThread().SetStopInfoToNothing();
+
+#ifndef SINGLE_STEP_EXPRESSIONS
+ m_subplan_sp = std::make_shared<ThreadPlanRunToAddress>(
+ m_thread, m_start_addr, m_stop_other_threads);
+
+ m_thread.QueueThreadPlan(m_subplan_sp, false);
+ m_subplan_sp->SetPrivate(true);
+#endif
+}
+
+bool ThreadPlanCallFunction::WillStop() { return true; }
+
+bool ThreadPlanCallFunction::MischiefManaged() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (IsPlanComplete()) {
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): Completed call function plan.",
+ static_cast<void *>(this));
+
+ ThreadPlan::MischiefManaged();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void ThreadPlanCallFunction::SetBreakpoints() {
+ ProcessSP process_sp(m_thread.CalculateProcess());
+ if (m_trap_exceptions && process_sp) {
+ m_cxx_language_runtime =
+ process_sp->GetLanguageRuntime(eLanguageTypeC_plus_plus);
+ m_objc_language_runtime = process_sp->GetLanguageRuntime(eLanguageTypeObjC);
+
+ if (m_cxx_language_runtime) {
+ m_should_clear_cxx_exception_bp =
+ !m_cxx_language_runtime->ExceptionBreakpointsAreSet();
+ m_cxx_language_runtime->SetExceptionBreakpoints();
+ }
+ if (m_objc_language_runtime) {
+ m_should_clear_objc_exception_bp =
+ !m_objc_language_runtime->ExceptionBreakpointsAreSet();
+ m_objc_language_runtime->SetExceptionBreakpoints();
+ }
+ }
+}
+
+void ThreadPlanCallFunction::ClearBreakpoints() {
+ if (m_trap_exceptions) {
+ if (m_cxx_language_runtime && m_should_clear_cxx_exception_bp)
+ m_cxx_language_runtime->ClearExceptionBreakpoints();
+ if (m_objc_language_runtime && m_should_clear_objc_exception_bp)
+ m_objc_language_runtime->ClearExceptionBreakpoints();
+ }
+}
+
+bool ThreadPlanCallFunction::BreakpointsExplainStop() {
+ StopInfoSP stop_info_sp = GetPrivateStopInfo();
+
+ if (m_trap_exceptions) {
+ if ((m_cxx_language_runtime &&
+ m_cxx_language_runtime->ExceptionBreakpointsExplainStop(
+ stop_info_sp)) ||
+ (m_objc_language_runtime &&
+ m_objc_language_runtime->ExceptionBreakpointsExplainStop(
+ stop_info_sp))) {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("ThreadPlanCallFunction::BreakpointsExplainStop - Hit an "
+ "exception breakpoint, setting plan complete.");
+
+ SetPlanComplete(false);
+
+ // If the user has set the ObjC language breakpoint, it would normally
+ // get priority over our internal catcher breakpoint, but in this case we
+ // can't let that happen, so force the ShouldStop here.
+ stop_info_sp->OverrideShouldStop(true);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ThreadPlanCallFunction::SetStopOthers(bool new_value) {
+ m_subplan_sp->SetStopOthers(new_value);
+}
+
+bool ThreadPlanCallFunction::RestoreThreadState() {
+ return GetThread().RestoreThreadStateFromCheckpoint(m_stored_thread_state);
+}
+
+void ThreadPlanCallFunction::SetReturnValue() {
+ ProcessSP process_sp(m_thread.GetProcess());
+ const ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr;
+ if (abi && m_return_type.IsValid()) {
+ const bool persistent = false;
+ m_return_valobj_sp =
+ abi->GetReturnValueObject(m_thread, m_return_type, persistent);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanCallFunctionUsingABI.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanCallFunctionUsingABI.cpp
new file mode 100644
index 000000000000..3155e6f7965f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanCallFunctionUsingABI.cpp
@@ -0,0 +1,68 @@
+//===-- ThreadPlanCallFunctionUsingABI.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanCallFunctionUsingABI.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanCallFunctionUsingABI: Plan to call a single function using the ABI
+// instead of JIT
+ThreadPlanCallFunctionUsingABI::ThreadPlanCallFunctionUsingABI(
+ Thread &thread, const Address &function, llvm::Type &prototype,
+ llvm::Type &return_type, llvm::ArrayRef<ABI::CallArgument> args,
+ const EvaluateExpressionOptions &options)
+ : ThreadPlanCallFunction(thread, function, options),
+ m_return_type(return_type) {
+ lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS;
+ ABI *abi = nullptr;
+
+ if (!ConstructorSetup(thread, abi, start_load_addr, function_load_addr))
+ return;
+
+ if (!abi->PrepareTrivialCall(thread, m_function_sp, function_load_addr,
+ start_load_addr, prototype, args))
+ return;
+
+ ReportRegisterState("ABI Function call was set up. Register state was:");
+
+ m_valid = true;
+}
+
+ThreadPlanCallFunctionUsingABI::~ThreadPlanCallFunctionUsingABI() = default;
+
+void ThreadPlanCallFunctionUsingABI::GetDescription(Stream *s,
+ DescriptionLevel level) {
+ if (level == eDescriptionLevelBrief) {
+ s->Printf("Function call thread plan using ABI instead of JIT");
+ } else {
+ TargetSP target_sp(m_thread.CalculateTarget());
+ s->Printf("Thread plan to call 0x%" PRIx64 " using ABI instead of JIT",
+ m_function_addr.GetLoadAddress(target_sp.get()));
+ }
+}
+
+void ThreadPlanCallFunctionUsingABI::SetReturnValue() {
+ ProcessSP process_sp(m_thread.GetProcess());
+ const ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr;
+
+ // Ask the abi for the return value
+ if (abi) {
+ const bool persistent = false;
+ m_return_valobj_sp =
+ abi->GetReturnValueObject(m_thread, m_return_type, persistent);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanCallOnFunctionExit.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanCallOnFunctionExit.cpp
new file mode 100644
index 000000000000..3330adc0c2af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanCallOnFunctionExit.cpp
@@ -0,0 +1,97 @@
+//===-- ThreadPlanCallOnFunctionExit.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanCallOnFunctionExit.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ThreadPlanCallOnFunctionExit::ThreadPlanCallOnFunctionExit(
+ Thread &thread, const Callback &callback)
+ : ThreadPlan(ThreadPlanKind::eKindGeneric, "CallOnFunctionExit", thread,
+ eVoteNoOpinion, eVoteNoOpinion // TODO check with Jim on these
+ ),
+ m_callback(callback) {
+ // We are not a user-generated plan.
+ SetIsMasterPlan(false);
+}
+
+void ThreadPlanCallOnFunctionExit::DidPush() {
+ // We now want to queue the "step out" thread plan so it executes and
+ // completes.
+
+ // Set stop vote to eVoteNo.
+ Status status;
+ m_step_out_threadplan_sp = GetThread().QueueThreadPlanForStepOut(
+ false, // abort other plans
+ nullptr, // addr_context
+ true, // first instruction
+ true, // stop other threads
+ eVoteNo, // do not say "we're stopping"
+ eVoteNoOpinion, // don't care about run state broadcasting
+ 0, // frame_idx
+ status, // status
+ eLazyBoolCalculate // avoid code w/o debinfo
+ );
+}
+
+// ThreadPlan API
+
+void ThreadPlanCallOnFunctionExit::GetDescription(
+ Stream *s, lldb::DescriptionLevel level) {
+ if (!s)
+ return;
+ s->Printf("Running until completion of current function, then making "
+ "callback.");
+}
+
+bool ThreadPlanCallOnFunctionExit::ValidatePlan(Stream *error) {
+ // We'll say we're always good since I don't know what would make this
+ // invalid.
+ return true;
+}
+
+bool ThreadPlanCallOnFunctionExit::ShouldStop(Event *event_ptr) {
+ // If this is where we find out that an internal stop came in, then: Check if
+ // the step-out plan completed. If it did, then we want to run the callback
+ // here (our reason for living...)
+ if (m_step_out_threadplan_sp && m_step_out_threadplan_sp->IsPlanComplete()) {
+ m_callback();
+
+ // We no longer need the pointer to the step-out thread plan.
+ m_step_out_threadplan_sp.reset();
+
+ // Indicate that this plan is done and can be discarded.
+ SetPlanComplete();
+
+ // We're done now, but we want to return false so that we don't cause the
+ // thread to really stop.
+ }
+
+ return false;
+}
+
+bool ThreadPlanCallOnFunctionExit::WillStop() {
+ // The code looks like the return value is ignored via ThreadList::
+ // ShouldStop(). This is called when we really are going to stop. We don't
+ // care and don't need to do anything here.
+ return false;
+}
+
+bool ThreadPlanCallOnFunctionExit::DoPlanExplainsStop(Event *event_ptr) {
+ // We don't ever explain a stop. The only stop that is relevant to us
+ // directly is the step_out plan we added to do the heavy lifting of getting
+ // us past the current method.
+ return false;
+}
+
+lldb::StateType ThreadPlanCallOnFunctionExit::GetPlanRunState() {
+ // This value doesn't matter - we'll never be the top thread plan, so nobody
+ // will ask us this question.
+ return eStateRunning;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanCallUserExpression.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanCallUserExpression.cpp
new file mode 100644
index 000000000000..864808a4b5ea
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanCallUserExpression.cpp
@@ -0,0 +1,119 @@
+//===-- ThreadPlanCallUserExpression.cpp -------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanCallUserExpression.h"
+
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/DynamicCheckerFunctions.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanCallUserExpression: Plan to call a single function
+
+ThreadPlanCallUserExpression::ThreadPlanCallUserExpression(
+ Thread &thread, Address &function, llvm::ArrayRef<lldb::addr_t> args,
+ const EvaluateExpressionOptions &options,
+ lldb::UserExpressionSP &user_expression_sp)
+ : ThreadPlanCallFunction(thread, function, CompilerType(), args, options),
+ m_user_expression_sp(user_expression_sp) {
+ // User expressions are generally "User generated" so we should set them up
+ // to stop when done.
+ SetIsMasterPlan(true);
+ SetOkayToDiscard(false);
+}
+
+ThreadPlanCallUserExpression::~ThreadPlanCallUserExpression() {}
+
+void ThreadPlanCallUserExpression::GetDescription(
+ Stream *s, lldb::DescriptionLevel level) {
+ if (level == eDescriptionLevelBrief)
+ s->Printf("User Expression thread plan");
+ else
+ ThreadPlanCallFunction::GetDescription(s, level);
+}
+
+void ThreadPlanCallUserExpression::DidPush() {
+ ThreadPlanCallFunction::DidPush();
+ if (m_user_expression_sp)
+ m_user_expression_sp->WillStartExecuting();
+}
+
+void ThreadPlanCallUserExpression::WillPop() {
+ ThreadPlanCallFunction::WillPop();
+ if (m_user_expression_sp)
+ m_user_expression_sp.reset();
+}
+
+bool ThreadPlanCallUserExpression::MischiefManaged() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (IsPlanComplete()) {
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): Completed call function plan.",
+ static_cast<void *>(this));
+
+ if (m_manage_materialization && PlanSucceeded() && m_user_expression_sp) {
+ lldb::addr_t function_stack_top;
+ lldb::addr_t function_stack_bottom;
+ lldb::addr_t function_stack_pointer = GetFunctionStackPointer();
+
+ function_stack_bottom = function_stack_pointer - HostInfo::GetPageSize();
+ function_stack_top = function_stack_pointer;
+
+ DiagnosticManager diagnostics;
+
+ ExecutionContext exe_ctx(GetThread());
+
+ m_user_expression_sp->FinalizeJITExecution(
+ diagnostics, exe_ctx, m_result_var_sp, function_stack_bottom,
+ function_stack_top);
+ }
+
+ ThreadPlan::MischiefManaged();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+StopInfoSP ThreadPlanCallUserExpression::GetRealStopInfo() {
+ StopInfoSP stop_info_sp = ThreadPlanCallFunction::GetRealStopInfo();
+
+ if (stop_info_sp) {
+ lldb::addr_t addr = GetStopAddress();
+ DynamicCheckerFunctions *checkers =
+ m_thread.GetProcess()->GetDynamicCheckers();
+ StreamString s;
+
+ if (checkers && checkers->DoCheckersExplainStop(addr, s))
+ stop_info_sp->SetDescription(s.GetData());
+ }
+
+ return stop_info_sp;
+}
+
+void ThreadPlanCallUserExpression::DoTakedown(bool success) {
+ ThreadPlanCallFunction::DoTakedown(success);
+ m_user_expression_sp->DidFinishExecuting();
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanPython.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanPython.cpp
new file mode 100644
index 000000000000..8b30c4ea7cb1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanPython.cpp
@@ -0,0 +1,195 @@
+//===-- ThreadPlanPython.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlan.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanPython.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanPython
+
+ThreadPlanPython::ThreadPlanPython(Thread &thread, const char *class_name)
+ : ThreadPlan(ThreadPlan::eKindPython, "Python based Thread Plan", thread,
+ eVoteNoOpinion, eVoteNoOpinion),
+ m_class_name(class_name), m_did_push(false) {
+ SetIsMasterPlan(true);
+ SetOkayToDiscard(true);
+ SetPrivate(false);
+}
+
+ThreadPlanPython::~ThreadPlanPython() {
+ // FIXME, do I need to decrement the ref count on this implementation object
+ // to make it go away?
+}
+
+bool ThreadPlanPython::ValidatePlan(Stream *error) {
+ if (!m_did_push)
+ return true;
+
+ if (!m_implementation_sp) {
+ if (error)
+ error->Printf("Python thread plan does not have an implementation");
+ return false;
+ }
+
+ return true;
+}
+
+void ThreadPlanPython::DidPush() {
+ // We set up the script side in DidPush, so that it can push other plans in
+ // the constructor, and doesn't have to care about the details of DidPush.
+ m_did_push = true;
+ if (!m_class_name.empty()) {
+ ScriptInterpreter *script_interp = m_thread.GetProcess()
+ ->GetTarget()
+ .GetDebugger()
+ .GetScriptInterpreter();
+ if (script_interp) {
+ m_implementation_sp = script_interp->CreateScriptedThreadPlan(
+ m_class_name.c_str(), this->shared_from_this());
+ }
+ }
+}
+
+bool ThreadPlanPython::ShouldStop(Event *event_ptr) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
+ m_class_name.c_str());
+
+ bool should_stop = true;
+ if (m_implementation_sp) {
+ ScriptInterpreter *script_interp = m_thread.GetProcess()
+ ->GetTarget()
+ .GetDebugger()
+ .GetScriptInterpreter();
+ if (script_interp) {
+ bool script_error;
+ should_stop = script_interp->ScriptedThreadPlanShouldStop(
+ m_implementation_sp, event_ptr, script_error);
+ if (script_error)
+ SetPlanComplete(false);
+ }
+ }
+ return should_stop;
+}
+
+bool ThreadPlanPython::IsPlanStale() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
+ m_class_name.c_str());
+
+ bool is_stale = true;
+ if (m_implementation_sp) {
+ ScriptInterpreter *script_interp = m_thread.GetProcess()
+ ->GetTarget()
+ .GetDebugger()
+ .GetScriptInterpreter();
+ if (script_interp) {
+ bool script_error;
+ is_stale = script_interp->ScriptedThreadPlanIsStale(m_implementation_sp,
+ script_error);
+ if (script_error)
+ SetPlanComplete(false);
+ }
+ }
+ return is_stale;
+}
+
+bool ThreadPlanPython::DoPlanExplainsStop(Event *event_ptr) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
+ m_class_name.c_str());
+
+ bool explains_stop = true;
+ if (m_implementation_sp) {
+ ScriptInterpreter *script_interp = m_thread.GetProcess()
+ ->GetTarget()
+ .GetDebugger()
+ .GetScriptInterpreter();
+ if (script_interp) {
+ bool script_error;
+ explains_stop = script_interp->ScriptedThreadPlanExplainsStop(
+ m_implementation_sp, event_ptr, script_error);
+ if (script_error)
+ SetPlanComplete(false);
+ }
+ }
+ return explains_stop;
+}
+
+bool ThreadPlanPython::MischiefManaged() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
+ m_class_name.c_str());
+ bool mischief_managed = true;
+ if (m_implementation_sp) {
+ // I don't really need mischief_managed, since it's simpler to just call
+ // SetPlanComplete in should_stop.
+ mischief_managed = IsPlanComplete();
+ if (mischief_managed)
+ m_implementation_sp.reset();
+ }
+ return mischief_managed;
+}
+
+lldb::StateType ThreadPlanPython::GetPlanRunState() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
+ m_class_name.c_str());
+ lldb::StateType run_state = eStateRunning;
+ if (m_implementation_sp) {
+ ScriptInterpreter *script_interp = m_thread.GetProcess()
+ ->GetTarget()
+ .GetDebugger()
+ .GetScriptInterpreter();
+ if (script_interp) {
+ bool script_error;
+ run_state = script_interp->ScriptedThreadPlanGetRunState(
+ m_implementation_sp, script_error);
+ }
+ }
+ return run_state;
+}
+
+// The ones below are not currently exported to Python.
+
+bool ThreadPlanPython::StopOthers() {
+ // For now Python plans run all threads, but we should add some controls for
+ // this.
+ return false;
+}
+
+void ThreadPlanPython::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ s->Printf("Python thread plan implemented by class %s.",
+ m_class_name.c_str());
+}
+
+bool ThreadPlanPython::WillStop() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
+ m_class_name.c_str());
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanRunToAddress.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanRunToAddress.cpp
new file mode 100644
index 000000000000..a973bd112796
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanRunToAddress.cpp
@@ -0,0 +1,204 @@
+//===-- ThreadPlanRunToAddress.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanRunToAddress: Continue plan
+
+ThreadPlanRunToAddress::ThreadPlanRunToAddress(Thread &thread, Address &address,
+ bool stop_others)
+ : ThreadPlan(ThreadPlan::eKindRunToAddress, "Run to address plan", thread,
+ eVoteNoOpinion, eVoteNoOpinion),
+ m_stop_others(stop_others), m_addresses(), m_break_ids() {
+ m_addresses.push_back(
+ address.GetOpcodeLoadAddress(m_thread.CalculateTarget().get()));
+ SetInitialBreakpoints();
+}
+
+ThreadPlanRunToAddress::ThreadPlanRunToAddress(Thread &thread,
+ lldb::addr_t address,
+ bool stop_others)
+ : ThreadPlan(ThreadPlan::eKindRunToAddress, "Run to address plan", thread,
+ eVoteNoOpinion, eVoteNoOpinion),
+ m_stop_others(stop_others), m_addresses(), m_break_ids() {
+ m_addresses.push_back(
+ m_thread.CalculateTarget()->GetOpcodeLoadAddress(address));
+ SetInitialBreakpoints();
+}
+
+ThreadPlanRunToAddress::ThreadPlanRunToAddress(
+ Thread &thread, const std::vector<lldb::addr_t> &addresses,
+ bool stop_others)
+ : ThreadPlan(ThreadPlan::eKindRunToAddress, "Run to address plan", thread,
+ eVoteNoOpinion, eVoteNoOpinion),
+ m_stop_others(stop_others), m_addresses(addresses), m_break_ids() {
+ // Convert all addresses into opcode addresses to make sure we set
+ // breakpoints at the correct address.
+ Target &target = thread.GetProcess()->GetTarget();
+ std::vector<lldb::addr_t>::iterator pos, end = m_addresses.end();
+ for (pos = m_addresses.begin(); pos != end; ++pos)
+ *pos = target.GetOpcodeLoadAddress(*pos);
+
+ SetInitialBreakpoints();
+}
+
+void ThreadPlanRunToAddress::SetInitialBreakpoints() {
+ size_t num_addresses = m_addresses.size();
+ m_break_ids.resize(num_addresses);
+
+ for (size_t i = 0; i < num_addresses; i++) {
+ Breakpoint *breakpoint;
+ breakpoint = m_thread.CalculateTarget()
+ ->CreateBreakpoint(m_addresses[i], true, false)
+ .get();
+ if (breakpoint != nullptr) {
+ if (breakpoint->IsHardware() && !breakpoint->HasResolvedLocations())
+ m_could_not_resolve_hw_bp = true;
+ m_break_ids[i] = breakpoint->GetID();
+ breakpoint->SetThreadID(m_thread.GetID());
+ breakpoint->SetBreakpointKind("run-to-address");
+ }
+ }
+}
+
+ThreadPlanRunToAddress::~ThreadPlanRunToAddress() {
+ size_t num_break_ids = m_break_ids.size();
+ for (size_t i = 0; i < num_break_ids; i++) {
+ m_thread.CalculateTarget()->RemoveBreakpointByID(m_break_ids[i]);
+ }
+ m_could_not_resolve_hw_bp = false;
+}
+
+void ThreadPlanRunToAddress::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ size_t num_addresses = m_addresses.size();
+
+ if (level == lldb::eDescriptionLevelBrief) {
+ if (num_addresses == 0) {
+ s->Printf("run to address with no addresses given.");
+ return;
+ } else if (num_addresses == 1)
+ s->Printf("run to address: ");
+ else
+ s->Printf("run to addresses: ");
+
+ for (size_t i = 0; i < num_addresses; i++) {
+ s->Address(m_addresses[i], sizeof(addr_t));
+ s->Printf(" ");
+ }
+ } else {
+ if (num_addresses == 0) {
+ s->Printf("run to address with no addresses given.");
+ return;
+ } else if (num_addresses == 1)
+ s->Printf("Run to address: ");
+ else {
+ s->Printf("Run to addresses: ");
+ }
+
+ for (size_t i = 0; i < num_addresses; i++) {
+ if (num_addresses > 1) {
+ s->Printf("\n");
+ s->Indent();
+ }
+
+ s->Address(m_addresses[i], sizeof(addr_t));
+ s->Printf(" using breakpoint: %d - ", m_break_ids[i]);
+ Breakpoint *breakpoint =
+ m_thread.CalculateTarget()->GetBreakpointByID(m_break_ids[i]).get();
+ if (breakpoint)
+ breakpoint->Dump(s);
+ else
+ s->Printf("but the breakpoint has been deleted.");
+ }
+ }
+}
+
+bool ThreadPlanRunToAddress::ValidatePlan(Stream *error) {
+ if (m_could_not_resolve_hw_bp) {
+ if (error)
+ error->Printf("Could not set hardware breakpoint(s)");
+ return false;
+ }
+
+ // If we couldn't set the breakpoint for some reason, then this won't work.
+ bool all_bps_good = true;
+ size_t num_break_ids = m_break_ids.size();
+ for (size_t i = 0; i < num_break_ids; i++) {
+ if (m_break_ids[i] == LLDB_INVALID_BREAK_ID) {
+ all_bps_good = false;
+ if (error) {
+ error->Printf("Could not set breakpoint for address: ");
+ error->Address(m_addresses[i], sizeof(addr_t));
+ error->Printf("\n");
+ }
+ }
+ }
+ return all_bps_good;
+}
+
+bool ThreadPlanRunToAddress::DoPlanExplainsStop(Event *event_ptr) {
+ return AtOurAddress();
+}
+
+bool ThreadPlanRunToAddress::ShouldStop(Event *event_ptr) {
+ return AtOurAddress();
+}
+
+bool ThreadPlanRunToAddress::StopOthers() { return m_stop_others; }
+
+void ThreadPlanRunToAddress::SetStopOthers(bool new_value) {
+ m_stop_others = new_value;
+}
+
+StateType ThreadPlanRunToAddress::GetPlanRunState() { return eStateRunning; }
+
+bool ThreadPlanRunToAddress::WillStop() { return true; }
+
+bool ThreadPlanRunToAddress::MischiefManaged() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (AtOurAddress()) {
+ // Remove the breakpoint
+ size_t num_break_ids = m_break_ids.size();
+
+ for (size_t i = 0; i < num_break_ids; i++) {
+ if (m_break_ids[i] != LLDB_INVALID_BREAK_ID) {
+ m_thread.CalculateTarget()->RemoveBreakpointByID(m_break_ids[i]);
+ m_break_ids[i] = LLDB_INVALID_BREAK_ID;
+ }
+ }
+ if (log)
+ log->Printf("Completed run to address plan.");
+ ThreadPlan::MischiefManaged();
+ return true;
+ } else
+ return false;
+}
+
+bool ThreadPlanRunToAddress::AtOurAddress() {
+ lldb::addr_t current_address = m_thread.GetRegisterContext()->GetPC();
+ bool found_it = false;
+ size_t num_addresses = m_addresses.size();
+ for (size_t i = 0; i < num_addresses; i++) {
+ if (m_addresses[i] == current_address) {
+ found_it = true;
+ break;
+ }
+ }
+ return found_it;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanShouldStopHere.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanShouldStopHere.cpp
new file mode 100644
index 000000000000..a0b7072a1071
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanShouldStopHere.cpp
@@ -0,0 +1,163 @@
+//===-- ThreadPlanShouldStopHere.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanShouldStopHere.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanShouldStopHere constructor
+ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(ThreadPlan *owner)
+ : m_callbacks(), m_baton(nullptr), m_owner(owner),
+ m_flags(ThreadPlanShouldStopHere::eNone) {
+ m_callbacks.should_stop_here_callback =
+ ThreadPlanShouldStopHere::DefaultShouldStopHereCallback;
+ m_callbacks.step_from_here_callback =
+ ThreadPlanShouldStopHere::DefaultStepFromHereCallback;
+}
+
+ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(
+ ThreadPlan *owner, const ThreadPlanShouldStopHereCallbacks *callbacks,
+ void *baton)
+ : m_callbacks(), m_baton(), m_owner(owner),
+ m_flags(ThreadPlanShouldStopHere::eNone) {
+ SetShouldStopHereCallbacks(callbacks, baton);
+}
+
+ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere() = default;
+
+bool ThreadPlanShouldStopHere::InvokeShouldStopHereCallback(
+ FrameComparison operation, Status &status) {
+ bool should_stop_here = true;
+ if (m_callbacks.should_stop_here_callback) {
+ should_stop_here = m_callbacks.should_stop_here_callback(
+ m_owner, m_flags, operation, status, m_baton);
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log) {
+ lldb::addr_t current_addr =
+ m_owner->GetThread().GetRegisterContext()->GetPC(0);
+
+ log->Printf("ShouldStopHere callback returned %u from 0x%" PRIx64 ".",
+ should_stop_here, current_addr);
+ }
+ }
+
+ return should_stop_here;
+}
+
+bool ThreadPlanShouldStopHere::DefaultShouldStopHereCallback(
+ ThreadPlan *current_plan, Flags &flags, FrameComparison operation,
+ Status &status, void *baton) {
+ bool should_stop_here = true;
+ StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get();
+ if (!frame)
+ return true;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if ((operation == eFrameCompareOlder && flags.Test(eStepOutAvoidNoDebug)) ||
+ (operation == eFrameCompareYounger && flags.Test(eStepInAvoidNoDebug)) ||
+ (operation == eFrameCompareSameParent &&
+ flags.Test(eStepInAvoidNoDebug))) {
+ if (!frame->HasDebugInformation()) {
+ if (log)
+ log->Printf("Stepping out of frame with no debug info");
+
+ should_stop_here = false;
+ }
+ }
+
+ // Always avoid code with line number 0.
+ // FIXME: At present the ShouldStop and the StepFromHere calculate this
+ // independently. If this ever
+ // becomes expensive (this one isn't) we can try to have this set a state
+ // that the StepFromHere can use.
+ if (frame) {
+ SymbolContext sc;
+ sc = frame->GetSymbolContext(eSymbolContextLineEntry);
+ if (sc.line_entry.line == 0)
+ should_stop_here = false;
+ }
+
+ return should_stop_here;
+}
+
+ThreadPlanSP ThreadPlanShouldStopHere::DefaultStepFromHereCallback(
+ ThreadPlan *current_plan, Flags &flags, FrameComparison operation,
+ Status &status, void *baton) {
+ const bool stop_others = false;
+ const size_t frame_index = 0;
+ ThreadPlanSP return_plan_sp;
+ // If we are stepping through code at line number 0, then we need to step
+ // over this range. Otherwise we will step out.
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get();
+ if (!frame)
+ return return_plan_sp;
+ SymbolContext sc;
+ sc = frame->GetSymbolContext(eSymbolContextLineEntry | eSymbolContextSymbol);
+
+ if (sc.line_entry.line == 0) {
+ AddressRange range = sc.line_entry.range;
+
+ // If the whole function is marked line 0 just step out, that's easier &
+ // faster than continuing to step through it.
+ bool just_step_out = false;
+ if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ Address symbol_end = sc.symbol->GetAddress();
+ symbol_end.Slide(sc.symbol->GetByteSize() - 1);
+ if (range.ContainsFileAddress(sc.symbol->GetAddress()) &&
+ range.ContainsFileAddress(symbol_end)) {
+ if (log)
+ log->Printf("Stopped in a function with only line 0 lines, just "
+ "stepping out.");
+ just_step_out = true;
+ }
+ }
+ if (!just_step_out) {
+ if (log)
+ log->Printf("ThreadPlanShouldStopHere::DefaultStepFromHereCallback "
+ "Queueing StepInRange plan to step through line 0 code.");
+
+ return_plan_sp = current_plan->GetThread().QueueThreadPlanForStepInRange(
+ false, range, sc, nullptr, eOnlyDuringStepping, status,
+ eLazyBoolCalculate, eLazyBoolNo);
+ }
+ }
+
+ if (!return_plan_sp)
+ return_plan_sp =
+ current_plan->GetThread().QueueThreadPlanForStepOutNoShouldStop(
+ false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion,
+ frame_index, status, true);
+ return return_plan_sp;
+}
+
+ThreadPlanSP ThreadPlanShouldStopHere::QueueStepOutFromHerePlan(
+ lldb_private::Flags &flags, lldb::FrameComparison operation,
+ Status &status) {
+ ThreadPlanSP return_plan_sp;
+ if (m_callbacks.step_from_here_callback) {
+ return_plan_sp = m_callbacks.step_from_here_callback(
+ m_owner, flags, operation, status, m_baton);
+ }
+ return return_plan_sp;
+}
+
+lldb::ThreadPlanSP ThreadPlanShouldStopHere::CheckShouldStopHereAndQueueStepOut(
+ lldb::FrameComparison operation, Status &status) {
+ if (!InvokeShouldStopHereCallback(operation, status))
+ return QueueStepOutFromHerePlan(m_flags, operation, status);
+ else
+ return ThreadPlanSP();
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanStepInRange.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepInRange.cpp
new file mode 100644
index 000000000000..2065fa55fa6a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepInRange.cpp
@@ -0,0 +1,515 @@
+//===-- ThreadPlanStepInRange.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanStepInRange.h"
+#include "lldb/Core/Architecture.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Target/ThreadPlanStepThrough.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+uint32_t ThreadPlanStepInRange::s_default_flag_values =
+ ThreadPlanShouldStopHere::eStepInAvoidNoDebug;
+
+// ThreadPlanStepInRange: Step through a stack range, either stepping over or
+// into based on the value of \a type.
+
+ThreadPlanStepInRange::ThreadPlanStepInRange(
+ Thread &thread, const AddressRange &range,
+ const SymbolContext &addr_context, lldb::RunMode stop_others,
+ LazyBool step_in_avoids_code_without_debug_info,
+ LazyBool step_out_avoids_code_without_debug_info)
+ : ThreadPlanStepRange(ThreadPlan::eKindStepInRange,
+ "Step Range stepping in", thread, range, addr_context,
+ stop_others),
+ ThreadPlanShouldStopHere(this), m_step_past_prologue(true),
+ m_virtual_step(false) {
+ SetCallbacks();
+ SetFlagsToDefault();
+ SetupAvoidNoDebug(step_in_avoids_code_without_debug_info,
+ step_out_avoids_code_without_debug_info);
+}
+
+ThreadPlanStepInRange::ThreadPlanStepInRange(
+ Thread &thread, const AddressRange &range,
+ const SymbolContext &addr_context, const char *step_into_target,
+ lldb::RunMode stop_others, LazyBool step_in_avoids_code_without_debug_info,
+ LazyBool step_out_avoids_code_without_debug_info)
+ : ThreadPlanStepRange(ThreadPlan::eKindStepInRange,
+ "Step Range stepping in", thread, range, addr_context,
+ stop_others),
+ ThreadPlanShouldStopHere(this), m_step_past_prologue(true),
+ m_virtual_step(false), m_step_into_target(step_into_target) {
+ SetCallbacks();
+ SetFlagsToDefault();
+ SetupAvoidNoDebug(step_in_avoids_code_without_debug_info,
+ step_out_avoids_code_without_debug_info);
+}
+
+ThreadPlanStepInRange::~ThreadPlanStepInRange() = default;
+
+void ThreadPlanStepInRange::SetupAvoidNoDebug(
+ LazyBool step_in_avoids_code_without_debug_info,
+ LazyBool step_out_avoids_code_without_debug_info) {
+ bool avoid_nodebug = true;
+
+ switch (step_in_avoids_code_without_debug_info) {
+ case eLazyBoolYes:
+ avoid_nodebug = true;
+ break;
+ case eLazyBoolNo:
+ avoid_nodebug = false;
+ break;
+ case eLazyBoolCalculate:
+ avoid_nodebug = m_thread.GetStepInAvoidsNoDebug();
+ break;
+ }
+ if (avoid_nodebug)
+ GetFlags().Set(ThreadPlanShouldStopHere::eStepInAvoidNoDebug);
+ else
+ GetFlags().Clear(ThreadPlanShouldStopHere::eStepInAvoidNoDebug);
+
+ switch (step_out_avoids_code_without_debug_info) {
+ case eLazyBoolYes:
+ avoid_nodebug = true;
+ break;
+ case eLazyBoolNo:
+ avoid_nodebug = false;
+ break;
+ case eLazyBoolCalculate:
+ avoid_nodebug = m_thread.GetStepOutAvoidsNoDebug();
+ break;
+ }
+ if (avoid_nodebug)
+ GetFlags().Set(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);
+ else
+ GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);
+}
+
+void ThreadPlanStepInRange::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+
+ auto PrintFailureIfAny = [&]() {
+ if (m_status.Success())
+ return;
+ s->Printf(" failed (%s)", m_status.AsCString());
+ };
+
+ if (level == lldb::eDescriptionLevelBrief) {
+ s->Printf("step in");
+ PrintFailureIfAny();
+ return;
+ }
+
+ s->Printf("Stepping in");
+ bool printed_line_info = false;
+ if (m_addr_context.line_entry.IsValid()) {
+ s->Printf(" through line ");
+ m_addr_context.line_entry.DumpStopContext(s, false);
+ printed_line_info = true;
+ }
+
+ const char *step_into_target = m_step_into_target.AsCString();
+ if (step_into_target && step_into_target[0] != '\0')
+ s->Printf(" targeting %s", m_step_into_target.AsCString());
+
+ if (!printed_line_info || level == eDescriptionLevelVerbose) {
+ s->Printf(" using ranges:");
+ DumpRanges(s);
+ }
+
+ PrintFailureIfAny();
+
+ s->PutChar('.');
+}
+
+bool ThreadPlanStepInRange::ShouldStop(Event *event_ptr) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (log) {
+ StreamString s;
+ s.Address(
+ m_thread.GetRegisterContext()->GetPC(),
+ m_thread.CalculateTarget()->GetArchitecture().GetAddressByteSize());
+ log->Printf("ThreadPlanStepInRange reached %s.", s.GetData());
+ }
+
+ if (IsPlanComplete())
+ return true;
+
+ m_no_more_plans = false;
+ if (m_sub_plan_sp && m_sub_plan_sp->IsPlanComplete()) {
+ if (!m_sub_plan_sp->PlanSucceeded()) {
+ SetPlanComplete();
+ m_no_more_plans = true;
+ return true;
+ } else
+ m_sub_plan_sp.reset();
+ }
+
+ if (m_virtual_step) {
+ // If we've just completed a virtual step, all we need to do is check for a
+ // ShouldStopHere plan, and otherwise we're done.
+ // FIXME - This can be both a step in and a step out. Probably should
+ // record which in the m_virtual_step.
+ m_sub_plan_sp =
+ CheckShouldStopHereAndQueueStepOut(eFrameCompareYounger, m_status);
+ } else {
+ // Stepping through should be done running other threads in general, since
+ // we're setting a breakpoint and continuing. So only stop others if we
+ // are explicitly told to do so.
+
+ bool stop_others = (m_stop_others == lldb::eOnlyThisThread);
+
+ FrameComparison frame_order = CompareCurrentFrameToStartFrame();
+
+ if (frame_order == eFrameCompareOlder ||
+ frame_order == eFrameCompareSameParent) {
+ // If we're in an older frame then we should stop.
+ //
+ // A caveat to this is if we think the frame is older but we're actually
+ // in a trampoline.
+ // I'm going to make the assumption that you wouldn't RETURN to a
+ // trampoline. So if we are in a trampoline we think the frame is older
+ // because the trampoline confused the backtracer.
+ m_sub_plan_sp = m_thread.QueueThreadPlanForStepThrough(
+ m_stack_id, false, stop_others, m_status);
+ if (!m_sub_plan_sp) {
+ // Otherwise check the ShouldStopHere for step out:
+ m_sub_plan_sp =
+ CheckShouldStopHereAndQueueStepOut(frame_order, m_status);
+ if (log) {
+ if (m_sub_plan_sp)
+ log->Printf("ShouldStopHere found plan to step out of this frame.");
+ else
+ log->Printf("ShouldStopHere no plan to step out of this frame.");
+ }
+ } else if (log) {
+ log->Printf(
+ "Thought I stepped out, but in fact arrived at a trampoline.");
+ }
+ } else if (frame_order == eFrameCompareEqual && InSymbol()) {
+ // If we are not in a place we should step through, we're done. One
+ // tricky bit here is that some stubs don't push a frame, so we have to
+ // check both the case of a frame that is younger, or the same as this
+ // frame. However, if the frame is the same, and we are still in the
+ // symbol we started in, the we don't need to do this. This first check
+ // isn't strictly necessary, but it is more efficient.
+
+ // If we're still in the range, keep going, either by running to the next
+ // branch breakpoint, or by stepping.
+ if (InRange()) {
+ SetNextBranchBreakpoint();
+ return false;
+ }
+
+ SetPlanComplete();
+ m_no_more_plans = true;
+ return true;
+ }
+
+ // If we get to this point, we're not going to use a previously set "next
+ // branch" breakpoint, so delete it:
+ ClearNextBranchBreakpoint();
+
+ // We may have set the plan up above in the FrameIsOlder section:
+
+ if (!m_sub_plan_sp)
+ m_sub_plan_sp = m_thread.QueueThreadPlanForStepThrough(
+ m_stack_id, false, stop_others, m_status);
+
+ if (log) {
+ if (m_sub_plan_sp)
+ log->Printf("Found a step through plan: %s", m_sub_plan_sp->GetName());
+ else
+ log->Printf("No step through plan found.");
+ }
+
+ // If not, give the "should_stop" callback a chance to push a plan to get
+ // us out of here. But only do that if we actually have stepped in.
+ if (!m_sub_plan_sp && frame_order == eFrameCompareYounger)
+ m_sub_plan_sp = CheckShouldStopHereAndQueueStepOut(frame_order, m_status);
+
+ // If we've stepped in and we are going to stop here, check to see if we
+ // were asked to run past the prologue, and if so do that.
+
+ if (!m_sub_plan_sp && frame_order == eFrameCompareYounger &&
+ m_step_past_prologue) {
+ lldb::StackFrameSP curr_frame = m_thread.GetStackFrameAtIndex(0);
+ if (curr_frame) {
+ size_t bytes_to_skip = 0;
+ lldb::addr_t curr_addr = m_thread.GetRegisterContext()->GetPC();
+ Address func_start_address;
+
+ SymbolContext sc = curr_frame->GetSymbolContext(eSymbolContextFunction |
+ eSymbolContextSymbol);
+
+ if (sc.function) {
+ func_start_address = sc.function->GetAddressRange().GetBaseAddress();
+ if (curr_addr ==
+ func_start_address.GetLoadAddress(
+ m_thread.CalculateTarget().get()))
+ bytes_to_skip = sc.function->GetPrologueByteSize();
+ } else if (sc.symbol) {
+ func_start_address = sc.symbol->GetAddress();
+ if (curr_addr ==
+ func_start_address.GetLoadAddress(
+ m_thread.CalculateTarget().get()))
+ bytes_to_skip = sc.symbol->GetPrologueByteSize();
+ }
+
+ if (bytes_to_skip == 0 && sc.symbol) {
+ TargetSP target = m_thread.CalculateTarget();
+ const Architecture *arch = target->GetArchitecturePlugin();
+ if (arch) {
+ Address curr_sec_addr;
+ target->GetSectionLoadList().ResolveLoadAddress(curr_addr,
+ curr_sec_addr);
+ bytes_to_skip = arch->GetBytesToSkip(*sc.symbol, curr_sec_addr);
+ }
+ }
+
+ if (bytes_to_skip != 0) {
+ func_start_address.Slide(bytes_to_skip);
+ log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP);
+ if (log)
+ log->Printf("Pushing past prologue ");
+
+ m_sub_plan_sp = m_thread.QueueThreadPlanForRunToAddress(
+ false, func_start_address, true, m_status);
+ }
+ }
+ }
+ }
+
+ if (!m_sub_plan_sp) {
+ m_no_more_plans = true;
+ SetPlanComplete();
+ return true;
+ } else {
+ m_no_more_plans = false;
+ m_sub_plan_sp->SetPrivate(true);
+ return false;
+ }
+}
+
+void ThreadPlanStepInRange::SetAvoidRegexp(const char *name) {
+ auto name_ref = llvm::StringRef::withNullAsEmpty(name);
+ if (!m_avoid_regexp_up)
+ m_avoid_regexp_up.reset(new RegularExpression(name_ref));
+
+ m_avoid_regexp_up->Compile(name_ref);
+}
+
+void ThreadPlanStepInRange::SetDefaultFlagValue(uint32_t new_value) {
+ // TODO: Should we test this for sanity?
+ ThreadPlanStepInRange::s_default_flag_values = new_value;
+}
+
+bool ThreadPlanStepInRange::FrameMatchesAvoidCriteria() {
+ StackFrame *frame = GetThread().GetStackFrameAtIndex(0).get();
+
+ // Check the library list first, as that's cheapest:
+ bool libraries_say_avoid = false;
+
+ FileSpecList libraries_to_avoid(GetThread().GetLibrariesToAvoid());
+ size_t num_libraries = libraries_to_avoid.GetSize();
+ if (num_libraries > 0) {
+ SymbolContext sc(frame->GetSymbolContext(eSymbolContextModule));
+ FileSpec frame_library(sc.module_sp->GetFileSpec());
+
+ if (frame_library) {
+ for (size_t i = 0; i < num_libraries; i++) {
+ const FileSpec &file_spec(libraries_to_avoid.GetFileSpecAtIndex(i));
+ if (FileSpec::Equal(file_spec, frame_library, false)) {
+ libraries_say_avoid = true;
+ break;
+ }
+ }
+ }
+ }
+ if (libraries_say_avoid)
+ return true;
+
+ const RegularExpression *avoid_regexp_to_use = m_avoid_regexp_up.get();
+ if (avoid_regexp_to_use == nullptr)
+ avoid_regexp_to_use = GetThread().GetSymbolsToAvoidRegexp();
+
+ if (avoid_regexp_to_use != nullptr) {
+ SymbolContext sc = frame->GetSymbolContext(
+ eSymbolContextFunction | eSymbolContextBlock | eSymbolContextSymbol);
+ if (sc.symbol != nullptr) {
+ const char *frame_function_name =
+ sc.GetFunctionName(Mangled::ePreferDemangledWithoutArguments)
+ .GetCString();
+ if (frame_function_name) {
+ size_t num_matches = 0;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ num_matches = 1;
+
+ RegularExpression::Match regex_match(num_matches);
+
+ bool return_value =
+ avoid_regexp_to_use->Execute(frame_function_name, &regex_match);
+ if (return_value) {
+ if (log) {
+ std::string match;
+ regex_match.GetMatchAtIndex(frame_function_name, 0, match);
+ log->Printf("Stepping out of function \"%s\" because it matches "
+ "the avoid regexp \"%s\" - match substring: \"%s\".",
+ frame_function_name,
+ avoid_regexp_to_use->GetText().str().c_str(),
+ match.c_str());
+ }
+ }
+ return return_value;
+ }
+ }
+ }
+ return false;
+}
+
+bool ThreadPlanStepInRange::DefaultShouldStopHereCallback(
+ ThreadPlan *current_plan, Flags &flags, FrameComparison operation,
+ Status &status, void *baton) {
+ bool should_stop_here = true;
+ StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get();
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ // First see if the ThreadPlanShouldStopHere default implementation thinks we
+ // should get out of here:
+ should_stop_here = ThreadPlanShouldStopHere::DefaultShouldStopHereCallback(
+ current_plan, flags, operation, status, baton);
+ if (!should_stop_here)
+ return should_stop_here;
+
+ if (should_stop_here && current_plan->GetKind() == eKindStepInRange &&
+ operation == eFrameCompareYounger) {
+ ThreadPlanStepInRange *step_in_range_plan =
+ static_cast<ThreadPlanStepInRange *>(current_plan);
+ if (step_in_range_plan->m_step_into_target) {
+ SymbolContext sc = frame->GetSymbolContext(
+ eSymbolContextFunction | eSymbolContextBlock | eSymbolContextSymbol);
+ if (sc.symbol != nullptr) {
+ // First try an exact match, since that's cheap with ConstStrings.
+ // Then do a strstr compare.
+ if (step_in_range_plan->m_step_into_target == sc.GetFunctionName()) {
+ should_stop_here = true;
+ } else {
+ const char *target_name =
+ step_in_range_plan->m_step_into_target.AsCString();
+ const char *function_name = sc.GetFunctionName().AsCString();
+
+ if (function_name == nullptr)
+ should_stop_here = false;
+ else if (strstr(function_name, target_name) == nullptr)
+ should_stop_here = false;
+ }
+ if (log && !should_stop_here)
+ log->Printf("Stepping out of frame %s which did not match step into "
+ "target %s.",
+ sc.GetFunctionName().AsCString(),
+ step_in_range_plan->m_step_into_target.AsCString());
+ }
+ }
+
+ if (should_stop_here) {
+ ThreadPlanStepInRange *step_in_range_plan =
+ static_cast<ThreadPlanStepInRange *>(current_plan);
+ // Don't log the should_step_out here, it's easier to do it in
+ // FrameMatchesAvoidCriteria.
+ should_stop_here = !step_in_range_plan->FrameMatchesAvoidCriteria();
+ }
+ }
+
+ return should_stop_here;
+}
+
+bool ThreadPlanStepInRange::DoPlanExplainsStop(Event *event_ptr) {
+ // We always explain a stop. Either we've just done a single step, in which
+ // case we'll do our ordinary processing, or we stopped for some reason that
+ // isn't handled by our sub-plans, in which case we want to just stop right
+ // away. In general, we don't want to mark the plan as complete for
+ // unexplained stops. For instance, if you step in to some code with no debug
+ // info, so you step out and in the course of that hit a breakpoint, then you
+ // want to stop & show the user the breakpoint, but not unship the step in
+ // plan, since you still may want to complete that plan when you continue.
+ // This is particularly true when doing "step in to target function."
+ // stepping.
+ //
+ // The only variation is that if we are doing "step by running to next
+ // branch" in which case if we hit our branch breakpoint we don't set the
+ // plan to complete.
+
+ bool return_value = false;
+
+ if (m_virtual_step) {
+ return_value = true;
+ } else {
+ StopInfoSP stop_info_sp = GetPrivateStopInfo();
+ if (stop_info_sp) {
+ StopReason reason = stop_info_sp->GetStopReason();
+
+ if (reason == eStopReasonBreakpoint) {
+ if (NextRangeBreakpointExplainsStop(stop_info_sp)) {
+ return_value = true;
+ }
+ } else if (IsUsuallyUnexplainedStopReason(reason)) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->PutCString("ThreadPlanStepInRange got asked if it explains the "
+ "stop for some reason other than step.");
+ return_value = false;
+ } else {
+ return_value = true;
+ }
+ } else
+ return_value = true;
+ }
+
+ return return_value;
+}
+
+bool ThreadPlanStepInRange::DoWillResume(lldb::StateType resume_state,
+ bool current_plan) {
+ m_virtual_step = false;
+ if (resume_state == eStateStepping && current_plan) {
+ // See if we are about to step over a virtual inlined call.
+ bool step_without_resume = m_thread.DecrementCurrentInlinedDepth();
+ if (step_without_resume) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("ThreadPlanStepInRange::DoWillResume: returning false, "
+ "inline_depth: %d",
+ m_thread.GetCurrentInlinedDepth());
+ SetStopInfo(StopInfo::CreateStopReasonToTrace(m_thread));
+
+ // FIXME: Maybe it would be better to create a InlineStep stop reason, but
+ // then
+ // the whole rest of the world would have to handle that stop reason.
+ m_virtual_step = true;
+ }
+ return !step_without_resume;
+ }
+ return true;
+}
+
+bool ThreadPlanStepInRange::IsVirtualStep() { return m_virtual_step; }
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanStepInstruction.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepInstruction.cpp
new file mode 100644
index 000000000000..a11b623c8acf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepInstruction.cpp
@@ -0,0 +1,253 @@
+//===-- ThreadPlanStepInstruction.cpp ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanStepInstruction.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanStepInstruction: Step over the current instruction
+
+ThreadPlanStepInstruction::ThreadPlanStepInstruction(Thread &thread,
+ bool step_over,
+ bool stop_other_threads,
+ Vote stop_vote,
+ Vote run_vote)
+ : ThreadPlan(ThreadPlan::eKindStepInstruction,
+ "Step over single instruction", thread, stop_vote, run_vote),
+ m_instruction_addr(0), m_stop_other_threads(stop_other_threads),
+ m_step_over(step_over) {
+ m_takes_iteration_count = true;
+ SetUpState();
+}
+
+ThreadPlanStepInstruction::~ThreadPlanStepInstruction() = default;
+
+void ThreadPlanStepInstruction::SetUpState() {
+ m_instruction_addr = m_thread.GetRegisterContext()->GetPC(0);
+ StackFrameSP start_frame_sp(m_thread.GetStackFrameAtIndex(0));
+ m_stack_id = start_frame_sp->GetStackID();
+
+ m_start_has_symbol =
+ start_frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol != nullptr;
+
+ StackFrameSP parent_frame_sp = m_thread.GetStackFrameAtIndex(1);
+ if (parent_frame_sp)
+ m_parent_frame_id = parent_frame_sp->GetStackID();
+}
+
+void ThreadPlanStepInstruction::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ auto PrintFailureIfAny = [&]() {
+ if (m_status.Success())
+ return;
+ s->Printf(" failed (%s)", m_status.AsCString());
+ };
+
+ if (level == lldb::eDescriptionLevelBrief) {
+ if (m_step_over)
+ s->Printf("instruction step over");
+ else
+ s->Printf("instruction step into");
+
+ PrintFailureIfAny();
+ } else {
+ s->Printf("Stepping one instruction past ");
+ s->Address(m_instruction_addr, sizeof(addr_t));
+ if (!m_start_has_symbol)
+ s->Printf(" which has no symbol");
+
+ if (m_step_over)
+ s->Printf(" stepping over calls");
+ else
+ s->Printf(" stepping into calls");
+
+ PrintFailureIfAny();
+ }
+}
+
+bool ThreadPlanStepInstruction::ValidatePlan(Stream *error) {
+ // Since we read the instruction we're stepping over from the thread, this
+ // plan will always work.
+ return true;
+}
+
+bool ThreadPlanStepInstruction::DoPlanExplainsStop(Event *event_ptr) {
+ StopInfoSP stop_info_sp = GetPrivateStopInfo();
+ if (stop_info_sp) {
+ StopReason reason = stop_info_sp->GetStopReason();
+ return (reason == eStopReasonTrace || reason == eStopReasonNone);
+ }
+ return false;
+}
+
+bool ThreadPlanStepInstruction::IsPlanStale() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ StackID cur_frame_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+ if (cur_frame_id == m_stack_id) {
+ // Set plan Complete when we reach next instruction
+ uint64_t pc = m_thread.GetRegisterContext()->GetPC(0);
+ uint32_t max_opcode_size = m_thread.CalculateTarget()
+ ->GetArchitecture().GetMaximumOpcodeByteSize();
+ bool next_instruction_reached = (pc > m_instruction_addr) &&
+ (pc <= m_instruction_addr + max_opcode_size);
+ if (next_instruction_reached) {
+ SetPlanComplete();
+ }
+ return (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr);
+ } else if (cur_frame_id < m_stack_id) {
+ // If the current frame is younger than the start frame and we are stepping
+ // over, then we need to continue, but if we are doing just one step, we're
+ // done.
+ return !m_step_over;
+ } else {
+ if (log) {
+ log->Printf("ThreadPlanStepInstruction::IsPlanStale - Current frame is "
+ "older than start frame, plan is stale.");
+ }
+ return true;
+ }
+}
+
+bool ThreadPlanStepInstruction::ShouldStop(Event *event_ptr) {
+ if (m_step_over) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ StackFrameSP cur_frame_sp = m_thread.GetStackFrameAtIndex(0);
+ if (!cur_frame_sp) {
+ if (log)
+ log->Printf(
+ "ThreadPlanStepInstruction couldn't get the 0th frame, stopping.");
+ SetPlanComplete();
+ return true;
+ }
+
+ StackID cur_frame_zero_id = cur_frame_sp->GetStackID();
+
+ if (cur_frame_zero_id == m_stack_id || m_stack_id < cur_frame_zero_id) {
+ if (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr) {
+ if (--m_iteration_count <= 0) {
+ SetPlanComplete();
+ return true;
+ } else {
+ // We are still stepping, reset the start pc, and in case we've
+ // stepped out, reset the current stack id.
+ SetUpState();
+ return false;
+ }
+ } else
+ return false;
+ } else {
+ // We've stepped in, step back out again:
+ StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get();
+ if (return_frame) {
+ if (return_frame->GetStackID() != m_parent_frame_id ||
+ m_start_has_symbol) {
+ // next-instruction shouldn't step out of inlined functions. But we
+ // may have stepped into a real function that starts with an inlined
+ // function, and we do want to step out of that...
+
+ if (cur_frame_sp->IsInlined()) {
+ StackFrameSP parent_frame_sp =
+ m_thread.GetFrameWithStackID(m_stack_id);
+
+ if (parent_frame_sp &&
+ parent_frame_sp->GetConcreteFrameIndex() ==
+ cur_frame_sp->GetConcreteFrameIndex()) {
+ SetPlanComplete();
+ if (log) {
+ log->Printf("Frame we stepped into is inlined into the frame "
+ "we were stepping from, stopping.");
+ }
+ return true;
+ }
+ }
+
+ if (log) {
+ StreamString s;
+ s.PutCString("Stepped in to: ");
+ addr_t stop_addr =
+ m_thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC();
+ s.Address(stop_addr, m_thread.CalculateTarget()
+ ->GetArchitecture()
+ .GetAddressByteSize());
+ s.PutCString(" stepping out to: ");
+ addr_t return_addr = return_frame->GetRegisterContext()->GetPC();
+ s.Address(return_addr, m_thread.CalculateTarget()
+ ->GetArchitecture()
+ .GetAddressByteSize());
+ log->Printf("%s.", s.GetData());
+ }
+
+ // StepInstruction should probably have the tri-state RunMode, but
+ // for now it is safer to run others.
+ const bool stop_others = false;
+ m_thread.QueueThreadPlanForStepOutNoShouldStop(
+ false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, 0,
+ m_status);
+ return false;
+ } else {
+ if (log) {
+ log->PutCString(
+ "The stack id we are stepping in changed, but our parent frame "
+ "did not when stepping from code with no symbols. "
+ "We are probably just confused about where we are, stopping.");
+ }
+ SetPlanComplete();
+ return true;
+ }
+ } else {
+ if (log)
+ log->Printf("Could not find previous frame, stopping.");
+ SetPlanComplete();
+ return true;
+ }
+ }
+ } else {
+ lldb::addr_t pc_addr = m_thread.GetRegisterContext()->GetPC(0);
+ if (pc_addr != m_instruction_addr) {
+ if (--m_iteration_count <= 0) {
+ SetPlanComplete();
+ return true;
+ } else {
+ // We are still stepping, reset the start pc, and in case we've stepped
+ // in or out, reset the current stack id.
+ SetUpState();
+ return false;
+ }
+ } else
+ return false;
+ }
+}
+
+bool ThreadPlanStepInstruction::StopOthers() { return m_stop_other_threads; }
+
+StateType ThreadPlanStepInstruction::GetPlanRunState() {
+ return eStateStepping;
+}
+
+bool ThreadPlanStepInstruction::WillStop() { return true; }
+
+bool ThreadPlanStepInstruction::MischiefManaged() {
+ if (IsPlanComplete()) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Completed single instruction step plan.");
+ ThreadPlan::MischiefManaged();
+ return true;
+ } else {
+ return false;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp
new file mode 100644
index 000000000000..bf55c376513d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepOut.cpp
@@ -0,0 +1,510 @@
+//===-- ThreadPlanStepOut.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlanStepOverRange.h"
+#include "lldb/Target/ThreadPlanStepThrough.h"
+#include "lldb/Utility/Log.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+uint32_t ThreadPlanStepOut::s_default_flag_values = 0;
+
+// ThreadPlanStepOut: Step out of the current frame
+ThreadPlanStepOut::ThreadPlanStepOut(
+ Thread &thread, SymbolContext *context, bool first_insn, bool stop_others,
+ Vote stop_vote, Vote run_vote, uint32_t frame_idx,
+ LazyBool step_out_avoids_code_without_debug_info,
+ bool continue_to_next_branch, bool gather_return_value)
+ : ThreadPlan(ThreadPlan::eKindStepOut, "Step out", thread, stop_vote,
+ run_vote),
+ ThreadPlanShouldStopHere(this), m_step_from_insn(LLDB_INVALID_ADDRESS),
+ m_return_bp_id(LLDB_INVALID_BREAK_ID),
+ m_return_addr(LLDB_INVALID_ADDRESS), m_stop_others(stop_others),
+ m_immediate_step_from_function(nullptr),
+ m_calculate_return_value(gather_return_value) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ SetFlagsToDefault();
+ SetupAvoidNoDebug(step_out_avoids_code_without_debug_info);
+
+ m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0);
+
+ uint32_t return_frame_index = frame_idx + 1;
+ StackFrameSP return_frame_sp(
+ m_thread.GetStackFrameAtIndex(return_frame_index));
+ StackFrameSP immediate_return_from_sp(
+ m_thread.GetStackFrameAtIndex(frame_idx));
+
+ if (!return_frame_sp || !immediate_return_from_sp)
+ return; // we can't do anything here. ValidatePlan() will return false.
+
+ // While stepping out, behave as-if artificial frames are not present.
+ while (return_frame_sp->IsArtificial()) {
+ m_stepped_past_frames.push_back(return_frame_sp);
+
+ ++return_frame_index;
+ return_frame_sp = m_thread.GetStackFrameAtIndex(return_frame_index);
+
+ // We never expect to see an artificial frame without a regular ancestor.
+ // If this happens, log the issue and defensively refuse to step out.
+ if (!return_frame_sp) {
+ LLDB_LOG(log, "Can't step out of frame with artificial ancestors");
+ return;
+ }
+ }
+
+ m_step_out_to_id = return_frame_sp->GetStackID();
+ m_immediate_step_from_id = immediate_return_from_sp->GetStackID();
+
+ // If the frame directly below the one we are returning to is inlined, we
+ // have to be a little more careful. It is non-trivial to determine the real
+ // "return code address" for an inlined frame, so we have to work our way to
+ // that frame and then step out.
+ if (immediate_return_from_sp->IsInlined()) {
+ if (frame_idx > 0) {
+ // First queue a plan that gets us to this inlined frame, and when we get
+ // there we'll queue a second plan that walks us out of this frame.
+ m_step_out_to_inline_plan_sp = std::make_shared<ThreadPlanStepOut>(
+ m_thread, nullptr, false, stop_others, eVoteNoOpinion, eVoteNoOpinion,
+ frame_idx - 1, eLazyBoolNo, continue_to_next_branch);
+ static_cast<ThreadPlanStepOut *>(m_step_out_to_inline_plan_sp.get())
+ ->SetShouldStopHereCallbacks(nullptr, nullptr);
+ m_step_out_to_inline_plan_sp->SetPrivate(true);
+ } else {
+ // If we're already at the inlined frame we're stepping through, then
+ // just do that now.
+ QueueInlinedStepPlan(false);
+ }
+ } else {
+ // Find the return address and set a breakpoint there:
+ // FIXME - can we do this more securely if we know first_insn?
+
+ Address return_address(return_frame_sp->GetFrameCodeAddress());
+ if (continue_to_next_branch) {
+ SymbolContext return_address_sc;
+ AddressRange range;
+ Address return_address_decr_pc = return_address;
+ if (return_address_decr_pc.GetOffset() > 0)
+ return_address_decr_pc.Slide(-1);
+
+ return_address_decr_pc.CalculateSymbolContext(
+ &return_address_sc, lldb::eSymbolContextLineEntry);
+ if (return_address_sc.line_entry.IsValid()) {
+ const bool include_inlined_functions = false;
+ range = return_address_sc.line_entry.GetSameLineContiguousAddressRange(
+ include_inlined_functions);
+ if (range.GetByteSize() > 0) {
+ return_address =
+ m_thread.GetProcess()->AdvanceAddressToNextBranchInstruction(
+ return_address, range);
+ }
+ }
+ }
+ m_return_addr =
+ return_address.GetLoadAddress(&m_thread.GetProcess()->GetTarget());
+
+ if (m_return_addr == LLDB_INVALID_ADDRESS)
+ return;
+
+ Breakpoint *return_bp = m_thread.CalculateTarget()
+ ->CreateBreakpoint(m_return_addr, true, false)
+ .get();
+
+ if (return_bp != nullptr) {
+ if (return_bp->IsHardware() && !return_bp->HasResolvedLocations())
+ m_could_not_resolve_hw_bp = true;
+ return_bp->SetThreadID(m_thread.GetID());
+ m_return_bp_id = return_bp->GetID();
+ return_bp->SetBreakpointKind("step-out");
+ }
+
+ if (immediate_return_from_sp) {
+ const SymbolContext &sc =
+ immediate_return_from_sp->GetSymbolContext(eSymbolContextFunction);
+ if (sc.function) {
+ m_immediate_step_from_function = sc.function;
+ }
+ }
+ }
+}
+
+void ThreadPlanStepOut::SetupAvoidNoDebug(
+ LazyBool step_out_avoids_code_without_debug_info) {
+ bool avoid_nodebug = true;
+ switch (step_out_avoids_code_without_debug_info) {
+ case eLazyBoolYes:
+ avoid_nodebug = true;
+ break;
+ case eLazyBoolNo:
+ avoid_nodebug = false;
+ break;
+ case eLazyBoolCalculate:
+ avoid_nodebug = m_thread.GetStepOutAvoidsNoDebug();
+ break;
+ }
+ if (avoid_nodebug)
+ GetFlags().Set(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);
+ else
+ GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);
+}
+
+void ThreadPlanStepOut::DidPush() {
+ if (m_step_out_to_inline_plan_sp)
+ m_thread.QueueThreadPlan(m_step_out_to_inline_plan_sp, false);
+ else if (m_step_through_inline_plan_sp)
+ m_thread.QueueThreadPlan(m_step_through_inline_plan_sp, false);
+}
+
+ThreadPlanStepOut::~ThreadPlanStepOut() {
+ if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
+ m_thread.CalculateTarget()->RemoveBreakpointByID(m_return_bp_id);
+}
+
+void ThreadPlanStepOut::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ if (level == lldb::eDescriptionLevelBrief)
+ s->Printf("step out");
+ else {
+ if (m_step_out_to_inline_plan_sp)
+ s->Printf("Stepping out to inlined frame so we can walk through it.");
+ else if (m_step_through_inline_plan_sp)
+ s->Printf("Stepping out by stepping through inlined function.");
+ else {
+ s->Printf("Stepping out from ");
+ Address tmp_address;
+ if (tmp_address.SetLoadAddress(m_step_from_insn, &GetTarget())) {
+ tmp_address.Dump(s, &GetThread(), Address::DumpStyleResolvedDescription,
+ Address::DumpStyleLoadAddress);
+ } else {
+ s->Printf("address 0x%" PRIx64 "", (uint64_t)m_step_from_insn);
+ }
+
+ // FIXME: find some useful way to present the m_return_id, since there may
+ // be multiple copies of the
+ // same function on the stack.
+
+ s->Printf(" returning to frame at ");
+ if (tmp_address.SetLoadAddress(m_return_addr, &GetTarget())) {
+ tmp_address.Dump(s, &GetThread(), Address::DumpStyleResolvedDescription,
+ Address::DumpStyleLoadAddress);
+ } else {
+ s->Printf("address 0x%" PRIx64 "", (uint64_t)m_return_addr);
+ }
+
+ if (level == eDescriptionLevelVerbose)
+ s->Printf(" using breakpoint site %d", m_return_bp_id);
+ }
+ }
+
+ s->Printf("\n");
+ for (StackFrameSP frame_sp : m_stepped_past_frames) {
+ s->Printf("Stepped out past: ");
+ frame_sp->DumpUsingSettingsFormat(s);
+ }
+}
+
+bool ThreadPlanStepOut::ValidatePlan(Stream *error) {
+ if (m_step_out_to_inline_plan_sp)
+ return m_step_out_to_inline_plan_sp->ValidatePlan(error);
+
+ if (m_step_through_inline_plan_sp)
+ return m_step_through_inline_plan_sp->ValidatePlan(error);
+
+ if (m_could_not_resolve_hw_bp) {
+ if (error)
+ error->PutCString(
+ "Could not create hardware breakpoint for thread plan.");
+ return false;
+ }
+
+ if (m_return_bp_id == LLDB_INVALID_BREAK_ID) {
+ if (error)
+ error->PutCString("Could not create return address breakpoint.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ThreadPlanStepOut::DoPlanExplainsStop(Event *event_ptr) {
+ // If the step out plan is done, then we just need to step through the
+ // inlined frame.
+ if (m_step_out_to_inline_plan_sp) {
+ return m_step_out_to_inline_plan_sp->MischiefManaged();
+ } else if (m_step_through_inline_plan_sp) {
+ if (m_step_through_inline_plan_sp->MischiefManaged()) {
+ CalculateReturnValue();
+ SetPlanComplete();
+ return true;
+ } else
+ return false;
+ } else if (m_step_out_further_plan_sp) {
+ return m_step_out_further_plan_sp->MischiefManaged();
+ }
+
+ // We don't explain signals or breakpoints (breakpoints that handle stepping
+ // in or out will be handled by a child plan.
+
+ StopInfoSP stop_info_sp = GetPrivateStopInfo();
+ if (stop_info_sp) {
+ StopReason reason = stop_info_sp->GetStopReason();
+ if (reason == eStopReasonBreakpoint) {
+ // If this is OUR breakpoint, we're fine, otherwise we don't know why
+ // this happened...
+ BreakpointSiteSP site_sp(
+ m_thread.GetProcess()->GetBreakpointSiteList().FindByID(
+ stop_info_sp->GetValue()));
+ if (site_sp && site_sp->IsBreakpointAtThisSite(m_return_bp_id)) {
+ bool done;
+
+ StackID frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+
+ if (m_step_out_to_id == frame_zero_id)
+ done = true;
+ else if (m_step_out_to_id < frame_zero_id) {
+ // Either we stepped past the breakpoint, or the stack ID calculation
+ // was incorrect and we should probably stop.
+ done = true;
+ } else {
+ done = (m_immediate_step_from_id < frame_zero_id);
+ }
+
+ if (done) {
+ if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) {
+ CalculateReturnValue();
+ SetPlanComplete();
+ }
+ }
+
+ // If there was only one owner, then we're done. But if we also hit
+ // some user breakpoint on our way out, we should mark ourselves as
+ // done, but also not claim to explain the stop, since it is more
+ // important to report the user breakpoint than the step out
+ // completion.
+
+ if (site_sp->GetNumberOfOwners() == 1)
+ return true;
+ }
+ return false;
+ } else if (IsUsuallyUnexplainedStopReason(reason))
+ return false;
+ else
+ return true;
+ }
+ return true;
+}
+
+bool ThreadPlanStepOut::ShouldStop(Event *event_ptr) {
+ if (IsPlanComplete())
+ return true;
+
+ bool done = false;
+ if (m_step_out_to_inline_plan_sp) {
+ if (m_step_out_to_inline_plan_sp->MischiefManaged()) {
+ // Now step through the inlined stack we are in:
+ if (QueueInlinedStepPlan(true)) {
+ // If we can't queue a plan to do this, then just call ourselves done.
+ m_step_out_to_inline_plan_sp.reset();
+ SetPlanComplete(false);
+ return true;
+ } else
+ done = true;
+ } else
+ return m_step_out_to_inline_plan_sp->ShouldStop(event_ptr);
+ } else if (m_step_through_inline_plan_sp) {
+ if (m_step_through_inline_plan_sp->MischiefManaged())
+ done = true;
+ 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())
+ m_step_out_further_plan_sp.reset();
+ else
+ return m_step_out_further_plan_sp->ShouldStop(event_ptr);
+ }
+
+ if (!done) {
+ StackID frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+ done = !(frame_zero_id < m_step_out_to_id);
+ }
+
+ // The normal step out computations think we are done, so all we need to do
+ // is consult the ShouldStopHere, and we are done.
+
+ if (done) {
+ if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) {
+ CalculateReturnValue();
+ SetPlanComplete();
+ } else {
+ m_step_out_further_plan_sp =
+ QueueStepOutFromHerePlan(m_flags, eFrameCompareOlder, m_status);
+ done = false;
+ }
+ }
+
+ return done;
+}
+
+bool ThreadPlanStepOut::StopOthers() { return m_stop_others; }
+
+StateType ThreadPlanStepOut::GetPlanRunState() { return eStateRunning; }
+
+bool ThreadPlanStepOut::DoWillResume(StateType resume_state,
+ bool current_plan) {
+ if (m_step_out_to_inline_plan_sp || m_step_through_inline_plan_sp)
+ return true;
+
+ if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
+ return false;
+
+ if (current_plan) {
+ Breakpoint *return_bp =
+ m_thread.CalculateTarget()->GetBreakpointByID(m_return_bp_id).get();
+ if (return_bp != nullptr)
+ return_bp->SetEnabled(true);
+ }
+ return true;
+}
+
+bool ThreadPlanStepOut::WillStop() {
+ if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {
+ Breakpoint *return_bp =
+ m_thread.CalculateTarget()->GetBreakpointByID(m_return_bp_id).get();
+ if (return_bp != nullptr)
+ return_bp->SetEnabled(false);
+ }
+
+ return true;
+}
+
+bool ThreadPlanStepOut::MischiefManaged() {
+ if (IsPlanComplete()) {
+ // Did I reach my breakpoint? If so I'm done.
+ //
+ // I also check the stack depth, since if we've blown past the breakpoint
+ // for some
+ // reason and we're now stopping for some other reason altogether, then
+ // we're done with this step out operation.
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Completed step out plan.");
+ if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {
+ m_thread.CalculateTarget()->RemoveBreakpointByID(m_return_bp_id);
+ m_return_bp_id = LLDB_INVALID_BREAK_ID;
+ }
+
+ ThreadPlan::MischiefManaged();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool ThreadPlanStepOut::QueueInlinedStepPlan(bool queue_now) {
+ // Now figure out the range of this inlined block, and set up a "step through
+ // range" plan for that. If we've been provided with a context, then use the
+ // block in that context.
+ StackFrameSP immediate_return_from_sp(m_thread.GetStackFrameAtIndex(0));
+ if (!immediate_return_from_sp)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log) {
+ StreamString s;
+ immediate_return_from_sp->Dump(&s, true, false);
+ log->Printf("Queuing inlined frame to step past: %s.", s.GetData());
+ }
+
+ Block *from_block = immediate_return_from_sp->GetFrameBlock();
+ if (from_block) {
+ Block *inlined_block = from_block->GetContainingInlinedBlock();
+ if (inlined_block) {
+ size_t num_ranges = inlined_block->GetNumRanges();
+ AddressRange inline_range;
+ if (inlined_block->GetRangeAtIndex(0, inline_range)) {
+ SymbolContext inlined_sc;
+ inlined_block->CalculateSymbolContext(&inlined_sc);
+ inlined_sc.target_sp = GetTarget().shared_from_this();
+ RunMode run_mode =
+ m_stop_others ? lldb::eOnlyThisThread : lldb::eAllThreads;
+ const LazyBool avoid_no_debug = eLazyBoolNo;
+
+ m_step_through_inline_plan_sp =
+ std::make_shared<ThreadPlanStepOverRange>(
+ m_thread, inline_range, inlined_sc, run_mode, avoid_no_debug);
+ ThreadPlanStepOverRange *step_through_inline_plan_ptr =
+ static_cast<ThreadPlanStepOverRange *>(
+ m_step_through_inline_plan_sp.get());
+ m_step_through_inline_plan_sp->SetPrivate(true);
+
+ step_through_inline_plan_ptr->SetOkayToDiscard(true);
+ StreamString errors;
+ if (!step_through_inline_plan_ptr->ValidatePlan(&errors)) {
+ // FIXME: Log this failure.
+ delete step_through_inline_plan_ptr;
+ return false;
+ }
+
+ for (size_t i = 1; i < num_ranges; i++) {
+ if (inlined_block->GetRangeAtIndex(i, inline_range))
+ step_through_inline_plan_ptr->AddRange(inline_range);
+ }
+
+ if (queue_now)
+ m_thread.QueueThreadPlan(m_step_through_inline_plan_sp, false);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void ThreadPlanStepOut::CalculateReturnValue() {
+ if (m_return_valobj_sp)
+ return;
+
+ if (!m_calculate_return_value)
+ return;
+
+ if (m_immediate_step_from_function != nullptr) {
+ CompilerType return_compiler_type =
+ m_immediate_step_from_function->GetCompilerType()
+ .GetFunctionReturnType();
+ if (return_compiler_type) {
+ lldb::ABISP abi_sp = m_thread.GetProcess()->GetABI();
+ if (abi_sp)
+ m_return_valobj_sp =
+ abi_sp->GetReturnValueObject(m_thread, return_compiler_type);
+ }
+ }
+}
+
+bool ThreadPlanStepOut::IsPlanStale() {
+ // If we are still lower on the stack than the frame we are returning to,
+ // then there's something for us to do. Otherwise, we're stale.
+
+ StackID frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+ return !(frame_zero_id < m_step_out_to_id);
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp
new file mode 100644
index 000000000000..4770b57ab7f2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp
@@ -0,0 +1,187 @@
+//===-- ThreadPlanStepOverBreakpoint.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanStepOverBreakpoint.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanStepOverBreakpoint: Single steps over a breakpoint bp_site_sp at
+// the pc.
+
+ThreadPlanStepOverBreakpoint::ThreadPlanStepOverBreakpoint(Thread &thread)
+ : ThreadPlan(
+ ThreadPlan::eKindStepOverBreakpoint, "Step over breakpoint trap",
+ thread, eVoteNo,
+ eVoteNoOpinion), // We need to report the run since this happens
+ // first in the thread plan stack when stepping over
+ // a breakpoint
+ m_breakpoint_addr(LLDB_INVALID_ADDRESS),
+ m_auto_continue(false), m_reenabled_breakpoint_site(false)
+
+{
+ m_breakpoint_addr = m_thread.GetRegisterContext()->GetPC();
+ m_breakpoint_site_id =
+ m_thread.GetProcess()->GetBreakpointSiteList().FindIDByAddress(
+ m_breakpoint_addr);
+}
+
+ThreadPlanStepOverBreakpoint::~ThreadPlanStepOverBreakpoint() {}
+
+void ThreadPlanStepOverBreakpoint::GetDescription(
+ Stream *s, lldb::DescriptionLevel level) {
+ s->Printf("Single stepping past breakpoint site %" PRIu64 " at 0x%" PRIx64,
+ m_breakpoint_site_id, (uint64_t)m_breakpoint_addr);
+}
+
+bool ThreadPlanStepOverBreakpoint::ValidatePlan(Stream *error) { return true; }
+
+bool ThreadPlanStepOverBreakpoint::DoPlanExplainsStop(Event *event_ptr) {
+ StopInfoSP stop_info_sp = GetPrivateStopInfo();
+ if (stop_info_sp) {
+ // It's a little surprising that we stop here for a breakpoint hit.
+ // However, when you single step ONTO a breakpoint we still want to call
+ // that a breakpoint hit, and trigger the actions, etc. Otherwise you
+ // would see the
+ // PC at the breakpoint without having triggered the actions, then you'd
+ // continue, the PC wouldn't change,
+ // and you'd see the breakpoint hit, which would be odd. So the lower
+ // levels fake "step onto breakpoint address" and return that as a
+ // breakpoint. So our trace step COULD appear as a breakpoint hit if the
+ // next instruction also contained a breakpoint.
+ StopReason reason = stop_info_sp->GetStopReason();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (log)
+ log->Printf("Step over breakpoint stopped for reason: %s.",
+ Thread::StopReasonAsCString(reason));
+
+ switch (reason) {
+ case eStopReasonTrace:
+ case eStopReasonNone:
+ return true;
+ case eStopReasonBreakpoint:
+ {
+ // It's a little surprising that we stop here for a breakpoint hit.
+ // However, when you single step ONTO a breakpoint we still want to call
+ // that a breakpoint hit, and trigger the actions, etc. Otherwise you
+ // would see the PC at the breakpoint without having triggered the
+ // actions, then you'd continue, the PC wouldn't change, and you'd see
+ // the breakpoint hit, which would be odd. So the lower levels fake
+ // "step onto breakpoint address" and return that as a breakpoint hit.
+ // So our trace step COULD appear as a breakpoint hit if the next
+ // instruction also contained a breakpoint. We don't want to handle
+ // that, since we really don't know what to do with breakpoint hits.
+ // But make sure we don't set ourselves to auto-continue or we'll wrench
+ // control away from the plans that can deal with this.
+ // Be careful, however, as we may have "seen a breakpoint under the PC
+ // because we stopped without changing the PC, in which case we do want
+ // to re-claim this stop so we'll try again.
+ lldb::addr_t pc_addr = m_thread.GetRegisterContext()->GetPC();
+
+ if (pc_addr == m_breakpoint_addr) {
+ if (log)
+ log->Printf("Got breakpoint stop reason but pc: 0x%" PRIx64
+ "hasn't changed.", pc_addr);
+ return true;
+ }
+
+ SetAutoContinue(false);
+ return false;
+ }
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+bool ThreadPlanStepOverBreakpoint::ShouldStop(Event *event_ptr) {
+ return !ShouldAutoContinue(event_ptr);
+}
+
+bool ThreadPlanStepOverBreakpoint::StopOthers() { return true; }
+
+StateType ThreadPlanStepOverBreakpoint::GetPlanRunState() {
+ return eStateStepping;
+}
+
+bool ThreadPlanStepOverBreakpoint::DoWillResume(StateType resume_state,
+ bool current_plan) {
+ if (current_plan) {
+ BreakpointSiteSP bp_site_sp(
+ m_thread.GetProcess()->GetBreakpointSiteList().FindByAddress(
+ m_breakpoint_addr));
+ if (bp_site_sp && bp_site_sp->IsEnabled()) {
+ m_thread.GetProcess()->DisableBreakpointSite(bp_site_sp.get());
+ m_reenabled_breakpoint_site = false;
+ }
+ }
+ return true;
+}
+
+bool ThreadPlanStepOverBreakpoint::WillStop() {
+ ReenableBreakpointSite();
+ return true;
+}
+
+void ThreadPlanStepOverBreakpoint::WillPop() {
+ ReenableBreakpointSite();
+}
+
+bool ThreadPlanStepOverBreakpoint::MischiefManaged() {
+ lldb::addr_t pc_addr = m_thread.GetRegisterContext()->GetPC();
+
+ if (pc_addr == m_breakpoint_addr) {
+ // If we are still at the PC of our breakpoint, then for some reason we
+ // didn't get a chance to run.
+ return false;
+ } else {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Completed step over breakpoint plan.");
+ // Otherwise, re-enable the breakpoint we were stepping over, and we're
+ // done.
+ ReenableBreakpointSite();
+ ThreadPlan::MischiefManaged();
+ return true;
+ }
+}
+
+void ThreadPlanStepOverBreakpoint::ReenableBreakpointSite() {
+ if (!m_reenabled_breakpoint_site) {
+ m_reenabled_breakpoint_site = true;
+ BreakpointSiteSP bp_site_sp(
+ m_thread.GetProcess()->GetBreakpointSiteList().FindByAddress(
+ m_breakpoint_addr));
+ if (bp_site_sp) {
+ m_thread.GetProcess()->EnableBreakpointSite(bp_site_sp.get());
+ }
+ }
+}
+void ThreadPlanStepOverBreakpoint::ThreadDestroyed() {
+ ReenableBreakpointSite();
+}
+
+void ThreadPlanStepOverBreakpoint::SetAutoContinue(bool do_it) {
+ m_auto_continue = do_it;
+}
+
+bool ThreadPlanStepOverBreakpoint::ShouldAutoContinue(Event *event_ptr) {
+ return m_auto_continue;
+}
+
+bool ThreadPlanStepOverBreakpoint::IsPlanStale() {
+ return m_thread.GetRegisterContext()->GetPC() != m_breakpoint_addr;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanStepOverRange.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepOverRange.cpp
new file mode 100644
index 000000000000..3aaeac9f5b21
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepOverRange.cpp
@@ -0,0 +1,414 @@
+//===-- ThreadPlanStepOverRange.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanStepOverRange.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Target/ThreadPlanStepThrough.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+uint32_t ThreadPlanStepOverRange::s_default_flag_values = 0;
+
+// ThreadPlanStepOverRange: Step through a stack range, either stepping over or
+// into based on the value of \a type.
+
+ThreadPlanStepOverRange::ThreadPlanStepOverRange(
+ Thread &thread, const AddressRange &range,
+ const SymbolContext &addr_context, lldb::RunMode stop_others,
+ LazyBool step_out_avoids_code_without_debug_info)
+ : ThreadPlanStepRange(ThreadPlan::eKindStepOverRange,
+ "Step range stepping over", thread, range,
+ addr_context, stop_others),
+ ThreadPlanShouldStopHere(this), m_first_resume(true) {
+ SetFlagsToDefault();
+ SetupAvoidNoDebug(step_out_avoids_code_without_debug_info);
+}
+
+ThreadPlanStepOverRange::~ThreadPlanStepOverRange() = default;
+
+void ThreadPlanStepOverRange::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ auto PrintFailureIfAny = [&]() {
+ if (m_status.Success())
+ return;
+ s->Printf(" failed (%s)", m_status.AsCString());
+ };
+
+ if (level == lldb::eDescriptionLevelBrief) {
+ s->Printf("step over");
+ PrintFailureIfAny();
+ return;
+ }
+
+ s->Printf("Stepping over");
+ bool printed_line_info = false;
+ if (m_addr_context.line_entry.IsValid()) {
+ s->Printf(" line ");
+ m_addr_context.line_entry.DumpStopContext(s, false);
+ printed_line_info = true;
+ }
+
+ if (!printed_line_info || level == eDescriptionLevelVerbose) {
+ s->Printf(" using ranges: ");
+ DumpRanges(s);
+ }
+
+ PrintFailureIfAny();
+
+ s->PutChar('.');
+}
+
+void ThreadPlanStepOverRange::SetupAvoidNoDebug(
+ LazyBool step_out_avoids_code_without_debug_info) {
+ bool avoid_nodebug = true;
+ switch (step_out_avoids_code_without_debug_info) {
+ case eLazyBoolYes:
+ avoid_nodebug = true;
+ break;
+ case eLazyBoolNo:
+ avoid_nodebug = false;
+ break;
+ case eLazyBoolCalculate:
+ avoid_nodebug = m_thread.GetStepOutAvoidsNoDebug();
+ break;
+ }
+ if (avoid_nodebug)
+ GetFlags().Set(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);
+ else
+ GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);
+ // Step Over plans should always avoid no-debug on step in. Seems like you
+ // shouldn't have to say this, but a tail call looks more like a step in that
+ // a step out, so we want to catch this case.
+ GetFlags().Set(ThreadPlanShouldStopHere::eStepInAvoidNoDebug);
+}
+
+bool ThreadPlanStepOverRange::IsEquivalentContext(
+ const SymbolContext &context) {
+ // Match as much as is specified in the m_addr_context: This is a fairly
+ // loose sanity check. Note, sometimes the target doesn't get filled in so I
+ // left out the target check. And sometimes the module comes in as the .o
+ // file from the inlined range, so I left that out too...
+ if (m_addr_context.comp_unit) {
+ if (m_addr_context.comp_unit != context.comp_unit)
+ return false;
+ if (m_addr_context.function) {
+ if (m_addr_context.function != context.function)
+ return false;
+ // It is okay to return to a different block of a straight function, we
+ // only have to be more careful if returning from one inlined block to
+ // another.
+ if (m_addr_context.block->GetInlinedFunctionInfo() == nullptr &&
+ context.block->GetInlinedFunctionInfo() == nullptr)
+ return true;
+ return m_addr_context.block == context.block;
+ }
+ }
+ // Fall back to symbol if we have no decision from comp_unit/function/block.
+ return m_addr_context.symbol && m_addr_context.symbol == context.symbol;
+}
+
+bool ThreadPlanStepOverRange::ShouldStop(Event *event_ptr) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (log) {
+ StreamString s;
+ s.Address(
+ m_thread.GetRegisterContext()->GetPC(),
+ m_thread.CalculateTarget()->GetArchitecture().GetAddressByteSize());
+ log->Printf("ThreadPlanStepOverRange reached %s.", s.GetData());
+ }
+
+ // If we're out of the range but in the same frame or in our caller's frame
+ // then we should stop. When stepping out we only stop others if we are
+ // forcing running one thread.
+ bool stop_others = (m_stop_others == lldb::eOnlyThisThread);
+ ThreadPlanSP new_plan_sp;
+ FrameComparison frame_order = CompareCurrentFrameToStartFrame();
+
+ if (frame_order == eFrameCompareOlder) {
+ // If we're in an older frame then we should stop.
+ //
+ // A caveat to this is if we think the frame is older but we're actually in
+ // a trampoline.
+ // I'm going to make the assumption that you wouldn't RETURN to a
+ // trampoline. So if we are in a trampoline we think the frame is older
+ // because the trampoline confused the backtracer. As below, we step
+ // through first, and then try to figure out how to get back out again.
+
+ new_plan_sp = m_thread.QueueThreadPlanForStepThrough(m_stack_id, false,
+ stop_others, m_status);
+
+ if (new_plan_sp && log)
+ log->Printf(
+ "Thought I stepped out, but in fact arrived at a trampoline.");
+ } else if (frame_order == eFrameCompareYounger) {
+ // Make sure we really are in a new frame. Do that by unwinding and seeing
+ // if the start function really is our start function...
+ for (uint32_t i = 1;; ++i) {
+ StackFrameSP older_frame_sp = m_thread.GetStackFrameAtIndex(i);
+ if (!older_frame_sp) {
+ // We can't unwind the next frame we should just get out of here &
+ // stop...
+ break;
+ }
+
+ const SymbolContext &older_context =
+ older_frame_sp->GetSymbolContext(eSymbolContextEverything);
+ if (IsEquivalentContext(older_context)) {
+ new_plan_sp = m_thread.QueueThreadPlanForStepOutNoShouldStop(
+ false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, 0,
+ m_status, true);
+ break;
+ } else {
+ new_plan_sp = m_thread.QueueThreadPlanForStepThrough(
+ m_stack_id, false, stop_others, m_status);
+ // If we found a way through, then we should stop recursing.
+ if (new_plan_sp)
+ break;
+ }
+ }
+ } else {
+ // If we're still in the range, keep going.
+ if (InRange()) {
+ SetNextBranchBreakpoint();
+ return false;
+ }
+
+ if (!InSymbol()) {
+ // This one is a little tricky. Sometimes we may be in a stub or
+ // something similar, in which case we need to get out of there. But if
+ // we are in a stub then it's likely going to be hard to get out from
+ // here. It is probably easiest to step into the stub, and then it will
+ // be straight-forward to step out.
+ new_plan_sp = m_thread.QueueThreadPlanForStepThrough(
+ m_stack_id, false, stop_others, m_status);
+ } else {
+ // The current clang (at least through 424) doesn't always get the
+ // address range for the DW_TAG_inlined_subroutines right, so that when
+ // you leave the inlined range the line table says you are still in the
+ // source file of the inlining function. This is bad, because now you
+ // are missing the stack frame for the function containing the inlining,
+ // and if you sensibly do "finish" to get out of this function you will
+ // instead exit the containing function. To work around this, we check
+ // whether we are still in the source file we started in, and if not
+ // assume it is an error, and push a plan to get us out of this line and
+ // back to the containing file.
+
+ if (m_addr_context.line_entry.IsValid()) {
+ SymbolContext sc;
+ StackFrameSP frame_sp = m_thread.GetStackFrameAtIndex(0);
+ sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
+ if (sc.line_entry.IsValid()) {
+ if (sc.line_entry.original_file !=
+ m_addr_context.line_entry.original_file &&
+ sc.comp_unit == m_addr_context.comp_unit &&
+ sc.function == m_addr_context.function) {
+ // Okay, find the next occurrence of this file in the line table:
+ LineTable *line_table = m_addr_context.comp_unit->GetLineTable();
+ if (line_table) {
+ Address cur_address = frame_sp->GetFrameCodeAddress();
+ uint32_t entry_idx;
+ LineEntry line_entry;
+ if (line_table->FindLineEntryByAddress(cur_address, line_entry,
+ &entry_idx)) {
+ LineEntry next_line_entry;
+ bool step_past_remaining_inline = false;
+ if (entry_idx > 0) {
+ // We require the previous line entry and the current line
+ // entry come from the same file. The other requirement is
+ // that the previous line table entry be part of an inlined
+ // block, we don't want to step past cases where people have
+ // inlined some code fragment by using #include <source-
+ // fragment.c> directly.
+ LineEntry prev_line_entry;
+ if (line_table->GetLineEntryAtIndex(entry_idx - 1,
+ prev_line_entry) &&
+ prev_line_entry.original_file ==
+ line_entry.original_file) {
+ SymbolContext prev_sc;
+ Address prev_address =
+ prev_line_entry.range.GetBaseAddress();
+ prev_address.CalculateSymbolContext(&prev_sc);
+ if (prev_sc.block) {
+ Block *inlined_block =
+ prev_sc.block->GetContainingInlinedBlock();
+ if (inlined_block) {
+ AddressRange inline_range;
+ inlined_block->GetRangeContainingAddress(prev_address,
+ inline_range);
+ if (!inline_range.ContainsFileAddress(cur_address)) {
+
+ step_past_remaining_inline = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (step_past_remaining_inline) {
+ uint32_t look_ahead_step = 1;
+ while (line_table->GetLineEntryAtIndex(
+ entry_idx + look_ahead_step, next_line_entry)) {
+ // Make sure we haven't wandered out of the function we
+ // started from...
+ Address next_line_address =
+ next_line_entry.range.GetBaseAddress();
+ Function *next_line_function =
+ next_line_address.CalculateSymbolContextFunction();
+ if (next_line_function != m_addr_context.function)
+ break;
+
+ if (next_line_entry.original_file ==
+ m_addr_context.line_entry.original_file) {
+ const bool abort_other_plans = false;
+ const RunMode stop_other_threads = RunMode::eAllThreads;
+ lldb::addr_t cur_pc = m_thread.GetStackFrameAtIndex(0)
+ ->GetRegisterContext()
+ ->GetPC();
+ AddressRange step_range(
+ cur_pc,
+ next_line_address.GetLoadAddress(&GetTarget()) -
+ cur_pc);
+
+ new_plan_sp = m_thread.QueueThreadPlanForStepOverRange(
+ abort_other_plans, step_range, sc, stop_other_threads,
+ m_status);
+ break;
+ }
+ look_ahead_step++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // If we get to this point, we're not going to use a previously set "next
+ // branch" breakpoint, so delete it:
+ ClearNextBranchBreakpoint();
+
+ // If we haven't figured out something to do yet, then ask the ShouldStopHere
+ // callback:
+ if (!new_plan_sp) {
+ new_plan_sp = CheckShouldStopHereAndQueueStepOut(frame_order, m_status);
+ }
+
+ if (!new_plan_sp)
+ m_no_more_plans = true;
+ else {
+ // Any new plan will be an implementation plan, so mark it private:
+ new_plan_sp->SetPrivate(true);
+ m_no_more_plans = false;
+ }
+
+ if (!new_plan_sp) {
+ // For efficiencies sake, we know we're done here so we don't have to do
+ // this calculation again in MischiefManaged.
+ SetPlanComplete(m_status.Success());
+ return true;
+ } else
+ return false;
+}
+
+bool ThreadPlanStepOverRange::DoPlanExplainsStop(Event *event_ptr) {
+ // For crashes, breakpoint hits, signals, etc, let the base plan (or some
+ // plan above us) handle the stop. That way the user can see the stop, step
+ // around, and then when they are done, continue and have their step
+ // complete. The exception is if we've hit our "run to next branch"
+ // breakpoint. Note, unlike the step in range plan, we don't mark ourselves
+ // complete if we hit an unexplained breakpoint/crash.
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ StopInfoSP stop_info_sp = GetPrivateStopInfo();
+ bool return_value;
+
+ if (stop_info_sp) {
+ StopReason reason = stop_info_sp->GetStopReason();
+
+ if (reason == eStopReasonTrace) {
+ return_value = true;
+ } else if (reason == eStopReasonBreakpoint) {
+ return_value = NextRangeBreakpointExplainsStop(stop_info_sp);
+ } else {
+ if (log)
+ log->PutCString("ThreadPlanStepInRange got asked if it explains the "
+ "stop for some reason other than step.");
+ return_value = false;
+ }
+ } else
+ return_value = true;
+
+ return return_value;
+}
+
+bool ThreadPlanStepOverRange::DoWillResume(lldb::StateType resume_state,
+ bool current_plan) {
+ if (resume_state != eStateSuspended && m_first_resume) {
+ m_first_resume = false;
+ if (resume_state == eStateStepping && current_plan) {
+ // See if we are about to step over an inlined call in the middle of the
+ // inlined stack, if so figure out its extents and reset our range to
+ // step over that.
+ bool in_inlined_stack = m_thread.DecrementCurrentInlinedDepth();
+ if (in_inlined_stack) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("ThreadPlanStepInRange::DoWillResume: adjusting range to "
+ "the frame at inlined depth %d.",
+ m_thread.GetCurrentInlinedDepth());
+ StackFrameSP stack_sp = m_thread.GetStackFrameAtIndex(0);
+ if (stack_sp) {
+ Block *frame_block = stack_sp->GetFrameBlock();
+ lldb::addr_t curr_pc = m_thread.GetRegisterContext()->GetPC();
+ AddressRange my_range;
+ if (frame_block->GetRangeContainingLoadAddress(
+ curr_pc, m_thread.GetProcess()->GetTarget(), my_range)) {
+ m_address_ranges.clear();
+ m_address_ranges.push_back(my_range);
+ if (log) {
+ StreamString s;
+ const InlineFunctionInfo *inline_info =
+ frame_block->GetInlinedFunctionInfo();
+ const char *name;
+ if (inline_info)
+ name =
+ inline_info
+ ->GetName(frame_block->CalculateSymbolContextFunction()
+ ->GetLanguage())
+ .AsCString();
+ else
+ name = "<unknown-notinlined>";
+
+ s.Printf(
+ "Stepping over inlined function \"%s\" in inlined stack: ",
+ name);
+ DumpRanges(&s);
+ log->PutString(s.GetString());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanStepRange.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepRange.cpp
new file mode 100644
index 000000000000..49c72dbf0911
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepRange.cpp
@@ -0,0 +1,487 @@
+//===-- ThreadPlanStepRange.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanStepRange.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointSite.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanStepRange: Step through a stack range, either stepping over or
+// into based on the value of \a type.
+
+ThreadPlanStepRange::ThreadPlanStepRange(ThreadPlanKind kind, const char *name,
+ Thread &thread,
+ const AddressRange &range,
+ const SymbolContext &addr_context,
+ lldb::RunMode stop_others,
+ bool given_ranges_only)
+ : ThreadPlan(kind, name, thread, eVoteNoOpinion, eVoteNoOpinion),
+ m_addr_context(addr_context), m_address_ranges(),
+ m_stop_others(stop_others), m_stack_id(), m_parent_stack_id(),
+ m_no_more_plans(false), m_first_run_event(true), m_use_fast_step(false),
+ m_given_ranges_only(given_ranges_only) {
+ m_use_fast_step = GetTarget().GetUseFastStepping();
+ AddRange(range);
+ m_stack_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+ StackFrameSP parent_stack = m_thread.GetStackFrameAtIndex(1);
+ if (parent_stack)
+ m_parent_stack_id = parent_stack->GetStackID();
+}
+
+ThreadPlanStepRange::~ThreadPlanStepRange() { ClearNextBranchBreakpoint(); }
+
+void ThreadPlanStepRange::DidPush() {
+ // See if we can find a "next range" breakpoint:
+ SetNextBranchBreakpoint();
+}
+
+bool ThreadPlanStepRange::ValidatePlan(Stream *error) {
+ if (m_could_not_resolve_hw_bp) {
+ if (error)
+ error->PutCString(
+ "Could not create hardware breakpoint for thread plan.");
+ return false;
+ }
+ return true;
+}
+
+Vote ThreadPlanStepRange::ShouldReportStop(Event *event_ptr) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ const Vote vote = IsPlanComplete() ? eVoteYes : eVoteNo;
+ if (log)
+ log->Printf("ThreadPlanStepRange::ShouldReportStop() returning vote %i\n",
+ vote);
+ return vote;
+}
+
+void ThreadPlanStepRange::AddRange(const AddressRange &new_range) {
+ // For now I'm just adding the ranges. At some point we may want to condense
+ // the ranges if they overlap, though I don't think it is likely to be very
+ // important.
+ m_address_ranges.push_back(new_range);
+
+ // Fill the slot for this address range with an empty DisassemblerSP in the
+ // instruction ranges. I want the indices to match, but I don't want to do
+ // the work to disassemble this range if I don't step into it.
+ m_instruction_ranges.push_back(DisassemblerSP());
+}
+
+void ThreadPlanStepRange::DumpRanges(Stream *s) {
+ size_t num_ranges = m_address_ranges.size();
+ if (num_ranges == 1) {
+ m_address_ranges[0].Dump(s, m_thread.CalculateTarget().get(),
+ Address::DumpStyleLoadAddress);
+ } else {
+ for (size_t i = 0; i < num_ranges; i++) {
+ s->Printf(" %" PRIu64 ": ", uint64_t(i));
+ m_address_ranges[i].Dump(s, m_thread.CalculateTarget().get(),
+ Address::DumpStyleLoadAddress);
+ }
+ }
+}
+
+bool ThreadPlanStepRange::InRange() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ bool ret_value = false;
+
+ lldb::addr_t pc_load_addr = m_thread.GetRegisterContext()->GetPC();
+
+ size_t num_ranges = m_address_ranges.size();
+ for (size_t i = 0; i < num_ranges; i++) {
+ ret_value = m_address_ranges[i].ContainsLoadAddress(
+ pc_load_addr, m_thread.CalculateTarget().get());
+ if (ret_value)
+ break;
+ }
+
+ if (!ret_value && !m_given_ranges_only) {
+ // See if we've just stepped to another part of the same line number...
+ StackFrame *frame = m_thread.GetStackFrameAtIndex(0).get();
+
+ SymbolContext new_context(
+ frame->GetSymbolContext(eSymbolContextEverything));
+ if (m_addr_context.line_entry.IsValid() &&
+ new_context.line_entry.IsValid()) {
+ if (m_addr_context.line_entry.original_file ==
+ new_context.line_entry.original_file) {
+ if (m_addr_context.line_entry.line == new_context.line_entry.line) {
+ m_addr_context = new_context;
+ const bool include_inlined_functions =
+ GetKind() == eKindStepOverRange;
+ AddRange(m_addr_context.line_entry.GetSameLineContiguousAddressRange(
+ include_inlined_functions));
+ ret_value = true;
+ if (log) {
+ StreamString s;
+ m_addr_context.line_entry.Dump(&s, m_thread.CalculateTarget().get(),
+ true, Address::DumpStyleLoadAddress,
+ Address::DumpStyleLoadAddress, true);
+
+ log->Printf(
+ "Step range plan stepped to another range of same line: %s",
+ s.GetData());
+ }
+ } else if (new_context.line_entry.line == 0) {
+ new_context.line_entry.line = m_addr_context.line_entry.line;
+ m_addr_context = new_context;
+ const bool include_inlined_functions =
+ GetKind() == eKindStepOverRange;
+ AddRange(m_addr_context.line_entry.GetSameLineContiguousAddressRange(
+ include_inlined_functions));
+ ret_value = true;
+ if (log) {
+ StreamString s;
+ m_addr_context.line_entry.Dump(&s, m_thread.CalculateTarget().get(),
+ true, Address::DumpStyleLoadAddress,
+ Address::DumpStyleLoadAddress, true);
+
+ log->Printf("Step range plan stepped to a range at linenumber 0 "
+ "stepping through that range: %s",
+ s.GetData());
+ }
+ } else if (new_context.line_entry.range.GetBaseAddress().GetLoadAddress(
+ m_thread.CalculateTarget().get()) != pc_load_addr) {
+ // Another thing that sometimes happens here is that we step out of
+ // one line into the MIDDLE of another line. So far I mostly see
+ // this due to bugs in the debug information. But we probably don't
+ // want to be in the middle of a line range, so in that case reset
+ // the stepping range to the line we've stepped into the middle of
+ // and continue.
+ m_addr_context = new_context;
+ m_address_ranges.clear();
+ AddRange(m_addr_context.line_entry.range);
+ ret_value = true;
+ if (log) {
+ StreamString s;
+ m_addr_context.line_entry.Dump(&s, m_thread.CalculateTarget().get(),
+ true, Address::DumpStyleLoadAddress,
+ Address::DumpStyleLoadAddress, true);
+
+ log->Printf("Step range plan stepped to the middle of new "
+ "line(%d): %s, continuing to clear this line.",
+ new_context.line_entry.line, s.GetData());
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret_value && log)
+ log->Printf("Step range plan out of range to 0x%" PRIx64, pc_load_addr);
+
+ return ret_value;
+}
+
+bool ThreadPlanStepRange::InSymbol() {
+ lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC();
+ if (m_addr_context.function != nullptr) {
+ return m_addr_context.function->GetAddressRange().ContainsLoadAddress(
+ cur_pc, m_thread.CalculateTarget().get());
+ } else if (m_addr_context.symbol && m_addr_context.symbol->ValueIsAddress()) {
+ AddressRange range(m_addr_context.symbol->GetAddressRef(),
+ m_addr_context.symbol->GetByteSize());
+ return range.ContainsLoadAddress(cur_pc, m_thread.CalculateTarget().get());
+ }
+ return false;
+}
+
+// FIXME: This should also handle inlining if we aren't going to do inlining in
+// the
+// main stack.
+//
+// Ideally we should remember the whole stack frame list, and then compare that
+// to the current list.
+
+lldb::FrameComparison ThreadPlanStepRange::CompareCurrentFrameToStartFrame() {
+ FrameComparison frame_order;
+
+ StackID cur_frame_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+
+ if (cur_frame_id == m_stack_id) {
+ frame_order = eFrameCompareEqual;
+ } else if (cur_frame_id < m_stack_id) {
+ frame_order = eFrameCompareYounger;
+ } else {
+ StackFrameSP cur_parent_frame = m_thread.GetStackFrameAtIndex(1);
+ StackID cur_parent_id;
+ if (cur_parent_frame)
+ cur_parent_id = cur_parent_frame->GetStackID();
+ if (m_parent_stack_id.IsValid() && cur_parent_id.IsValid() &&
+ m_parent_stack_id == cur_parent_id)
+ frame_order = eFrameCompareSameParent;
+ else
+ frame_order = eFrameCompareOlder;
+ }
+ return frame_order;
+}
+
+bool ThreadPlanStepRange::StopOthers() {
+ return (m_stop_others == lldb::eOnlyThisThread ||
+ m_stop_others == lldb::eOnlyDuringStepping);
+}
+
+InstructionList *ThreadPlanStepRange::GetInstructionsForAddress(
+ lldb::addr_t addr, size_t &range_index, size_t &insn_offset) {
+ size_t num_ranges = m_address_ranges.size();
+ for (size_t i = 0; i < num_ranges; i++) {
+ if (m_address_ranges[i].ContainsLoadAddress(addr, &GetTarget())) {
+ // Some joker added a zero size range to the stepping range...
+ if (m_address_ranges[i].GetByteSize() == 0)
+ return nullptr;
+
+ if (!m_instruction_ranges[i]) {
+ // Disassemble the address range given:
+ ExecutionContext exe_ctx(m_thread.GetProcess());
+ const char *plugin_name = nullptr;
+ const char *flavor = nullptr;
+ const bool prefer_file_cache = true;
+ m_instruction_ranges[i] = Disassembler::DisassembleRange(
+ GetTarget().GetArchitecture(), plugin_name, flavor, exe_ctx,
+ m_address_ranges[i], prefer_file_cache);
+ }
+ if (!m_instruction_ranges[i])
+ return nullptr;
+ else {
+ // Find where we are in the instruction list as well. If we aren't at
+ // an instruction, return nullptr. In this case, we're probably lost,
+ // and shouldn't try to do anything fancy.
+
+ insn_offset =
+ m_instruction_ranges[i]
+ ->GetInstructionList()
+ .GetIndexOfInstructionAtLoadAddress(addr, GetTarget());
+ if (insn_offset == UINT32_MAX)
+ return nullptr;
+ else {
+ range_index = i;
+ return &m_instruction_ranges[i]->GetInstructionList();
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+void ThreadPlanStepRange::ClearNextBranchBreakpoint() {
+ if (m_next_branch_bp_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Removing next branch breakpoint: %d.",
+ m_next_branch_bp_sp->GetID());
+ GetTarget().RemoveBreakpointByID(m_next_branch_bp_sp->GetID());
+ m_next_branch_bp_sp.reset();
+ m_could_not_resolve_hw_bp = false;
+ }
+}
+
+bool ThreadPlanStepRange::SetNextBranchBreakpoint() {
+ if (m_next_branch_bp_sp)
+ return true;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ // Stepping through ranges using breakpoints doesn't work yet, but with this
+ // off we fall back to instruction single stepping.
+ if (!m_use_fast_step)
+ return false;
+
+ lldb::addr_t cur_addr = GetThread().GetRegisterContext()->GetPC();
+ // Find the current address in our address ranges, and fetch the disassembly
+ // if we haven't already:
+ size_t pc_index;
+ size_t range_index;
+ InstructionList *instructions =
+ GetInstructionsForAddress(cur_addr, range_index, pc_index);
+ if (instructions == nullptr)
+ return false;
+ else {
+ Target &target = GetThread().GetProcess()->GetTarget();
+ const bool ignore_calls = GetKind() == eKindStepOverRange;
+ uint32_t branch_index =
+ instructions->GetIndexOfNextBranchInstruction(pc_index, target,
+ ignore_calls);
+
+ Address run_to_address;
+
+ // If we didn't find a branch, run to the end of the range.
+ if (branch_index == UINT32_MAX) {
+ uint32_t last_index = instructions->GetSize() - 1;
+ if (last_index - pc_index > 1) {
+ InstructionSP last_inst =
+ instructions->GetInstructionAtIndex(last_index);
+ size_t last_inst_size = last_inst->GetOpcode().GetByteSize();
+ run_to_address = last_inst->GetAddress();
+ run_to_address.Slide(last_inst_size);
+ }
+ } else if (branch_index - pc_index > 1) {
+ run_to_address =
+ instructions->GetInstructionAtIndex(branch_index)->GetAddress();
+ }
+
+ if (run_to_address.IsValid()) {
+ const bool is_internal = true;
+ m_next_branch_bp_sp =
+ GetTarget().CreateBreakpoint(run_to_address, is_internal, false);
+ if (m_next_branch_bp_sp) {
+
+ if (m_next_branch_bp_sp->IsHardware() &&
+ !m_next_branch_bp_sp->HasResolvedLocations())
+ m_could_not_resolve_hw_bp = true;
+
+ if (log) {
+ lldb::break_id_t bp_site_id = LLDB_INVALID_BREAK_ID;
+ BreakpointLocationSP bp_loc =
+ m_next_branch_bp_sp->GetLocationAtIndex(0);
+ if (bp_loc) {
+ BreakpointSiteSP bp_site = bp_loc->GetBreakpointSite();
+ if (bp_site) {
+ bp_site_id = bp_site->GetID();
+ }
+ }
+ log->Printf("ThreadPlanStepRange::SetNextBranchBreakpoint - Setting "
+ "breakpoint %d (site %d) to run to address 0x%" PRIx64,
+ m_next_branch_bp_sp->GetID(), bp_site_id,
+ run_to_address.GetLoadAddress(
+ &m_thread.GetProcess()->GetTarget()));
+ }
+
+ m_next_branch_bp_sp->SetThreadID(m_thread.GetID());
+ m_next_branch_bp_sp->SetBreakpointKind("next-branch-location");
+
+ return true;
+ } else
+ return false;
+ }
+ }
+ return false;
+}
+
+bool ThreadPlanStepRange::NextRangeBreakpointExplainsStop(
+ lldb::StopInfoSP stop_info_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (!m_next_branch_bp_sp)
+ return false;
+
+ break_id_t bp_site_id = stop_info_sp->GetValue();
+ BreakpointSiteSP bp_site_sp =
+ m_thread.GetProcess()->GetBreakpointSiteList().FindByID(bp_site_id);
+ if (!bp_site_sp)
+ return false;
+ else if (!bp_site_sp->IsBreakpointAtThisSite(m_next_branch_bp_sp->GetID()))
+ return false;
+ else {
+ // If we've hit the next branch breakpoint, then clear it.
+ size_t num_owners = bp_site_sp->GetNumberOfOwners();
+ bool explains_stop = true;
+ // If all the owners are internal, then we are probably just stepping over
+ // this range from multiple threads, or multiple frames, so we want to
+ // continue. If one is not internal, then we should not explain the stop,
+ // and let the user breakpoint handle the stop.
+ for (size_t i = 0; i < num_owners; i++) {
+ if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal()) {
+ explains_stop = false;
+ break;
+ }
+ }
+ if (log)
+ log->Printf("ThreadPlanStepRange::NextRangeBreakpointExplainsStop - Hit "
+ "next range breakpoint which has %" PRIu64
+ " owners - explains stop: %u.",
+ (uint64_t)num_owners, explains_stop);
+ ClearNextBranchBreakpoint();
+ return explains_stop;
+ }
+}
+
+bool ThreadPlanStepRange::WillStop() { return true; }
+
+StateType ThreadPlanStepRange::GetPlanRunState() {
+ if (m_next_branch_bp_sp)
+ return eStateRunning;
+ else
+ return eStateStepping;
+}
+
+bool ThreadPlanStepRange::MischiefManaged() {
+ // If we have pushed some plans between ShouldStop & MischiefManaged, then
+ // we're not done...
+ // I do this check first because we might have stepped somewhere that will
+ // fool InRange into
+ // thinking it needs to step past the end of that line. This happens, for
+ // instance, when stepping over inlined code that is in the middle of the
+ // current line.
+
+ if (!m_no_more_plans)
+ return false;
+
+ bool done = true;
+ if (!IsPlanComplete()) {
+ if (InRange()) {
+ done = false;
+ } else {
+ FrameComparison frame_order = CompareCurrentFrameToStartFrame();
+ done = (frame_order != eFrameCompareOlder) ? m_no_more_plans : true;
+ }
+ }
+
+ if (done) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Completed step through range plan.");
+ ClearNextBranchBreakpoint();
+ ThreadPlan::MischiefManaged();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool ThreadPlanStepRange::IsPlanStale() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ FrameComparison frame_order = CompareCurrentFrameToStartFrame();
+
+ if (frame_order == eFrameCompareOlder) {
+ if (log) {
+ log->Printf("ThreadPlanStepRange::IsPlanStale returning true, we've "
+ "stepped out.");
+ }
+ return true;
+ } else if (frame_order == eFrameCompareEqual && InSymbol()) {
+ // If we are not in a place we should step through, we've gotten stale. One
+ // tricky bit here is that some stubs don't push a frame, so we should.
+ // check that we are in the same symbol.
+ if (!InRange()) {
+ // Set plan Complete when we reach next instruction just after the range
+ lldb::addr_t addr = m_thread.GetRegisterContext()->GetPC() - 1;
+ size_t num_ranges = m_address_ranges.size();
+ for (size_t i = 0; i < num_ranges; i++) {
+ bool in_range = m_address_ranges[i].ContainsLoadAddress(
+ addr, m_thread.CalculateTarget().get());
+ if (in_range) {
+ SetPlanComplete();
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanStepThrough.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepThrough.cpp
new file mode 100644
index 000000000000..e46eba00184e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepThrough.cpp
@@ -0,0 +1,266 @@
+//===-- ThreadPlanStepThrough.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanStepThrough.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanStepThrough: If the current instruction is a trampoline, step
+// through it If it is the beginning of the prologue of a function, step
+// through that as well.
+// FIXME: At present only handles DYLD trampolines.
+
+ThreadPlanStepThrough::ThreadPlanStepThrough(Thread &thread,
+ StackID &m_stack_id,
+ bool stop_others)
+ : ThreadPlan(ThreadPlan::eKindStepThrough,
+ "Step through trampolines and prologues", thread,
+ eVoteNoOpinion, eVoteNoOpinion),
+ m_start_address(0), m_backstop_bkpt_id(LLDB_INVALID_BREAK_ID),
+ m_backstop_addr(LLDB_INVALID_ADDRESS), m_return_stack_id(m_stack_id),
+ m_stop_others(stop_others) {
+ LookForPlanToStepThroughFromCurrentPC();
+
+ // If we don't get a valid step through plan, don't bother to set up a
+ // backstop.
+ if (m_sub_plan_sp) {
+ m_start_address = GetThread().GetRegisterContext()->GetPC(0);
+
+ // We are going to return back to the concrete frame 1, we might pass by
+ // some inlined code that we're in the middle of by doing this, but it's
+ // easier than trying to figure out where the inlined code might return to.
+
+ StackFrameSP return_frame_sp = m_thread.GetFrameWithStackID(m_stack_id);
+
+ if (return_frame_sp) {
+ m_backstop_addr = return_frame_sp->GetFrameCodeAddress().GetLoadAddress(
+ m_thread.CalculateTarget().get());
+ Breakpoint *return_bp =
+ m_thread.GetProcess()
+ ->GetTarget()
+ .CreateBreakpoint(m_backstop_addr, true, false)
+ .get();
+
+ if (return_bp != nullptr) {
+ if (return_bp->IsHardware() && !return_bp->HasResolvedLocations())
+ m_could_not_resolve_hw_bp = true;
+ return_bp->SetThreadID(m_thread.GetID());
+ m_backstop_bkpt_id = return_bp->GetID();
+ return_bp->SetBreakpointKind("step-through-backstop");
+ }
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log) {
+ log->Printf("Setting backstop breakpoint %d at address: 0x%" PRIx64,
+ m_backstop_bkpt_id, m_backstop_addr);
+ }
+ }
+ }
+}
+
+ThreadPlanStepThrough::~ThreadPlanStepThrough() { ClearBackstopBreakpoint(); }
+
+void ThreadPlanStepThrough::DidPush() {
+ if (m_sub_plan_sp)
+ PushPlan(m_sub_plan_sp);
+}
+
+void ThreadPlanStepThrough::LookForPlanToStepThroughFromCurrentPC() {
+ DynamicLoader *loader = m_thread.GetProcess()->GetDynamicLoader();
+ if (loader)
+ m_sub_plan_sp =
+ loader->GetStepThroughTrampolinePlan(m_thread, m_stop_others);
+
+ // If the DynamicLoader was unable to provide us with a ThreadPlan, then we
+ // try the LanguageRuntimes.
+ if (!m_sub_plan_sp) {
+ for (LanguageRuntime *runtime :
+ m_thread.GetProcess()->GetLanguageRuntimes()) {
+ m_sub_plan_sp =
+ runtime->GetStepThroughTrampolinePlan(m_thread, m_stop_others);
+
+ if (m_sub_plan_sp)
+ break;
+ }
+ }
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log) {
+ lldb::addr_t current_address = GetThread().GetRegisterContext()->GetPC(0);
+ if (m_sub_plan_sp) {
+ StreamString s;
+ m_sub_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull);
+ log->Printf("Found step through plan from 0x%" PRIx64 ": %s",
+ current_address, s.GetData());
+ } else {
+ log->Printf("Couldn't find step through plan from address 0x%" PRIx64 ".",
+ current_address);
+ }
+ }
+}
+
+void ThreadPlanStepThrough::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ if (level == lldb::eDescriptionLevelBrief)
+ s->Printf("Step through");
+ else {
+ s->PutCString("Stepping through trampoline code from: ");
+ s->Address(m_start_address, sizeof(addr_t));
+ if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID) {
+ s->Printf(" with backstop breakpoint ID: %d at address: ",
+ m_backstop_bkpt_id);
+ s->Address(m_backstop_addr, sizeof(addr_t));
+ } else
+ s->PutCString(" unable to set a backstop breakpoint.");
+ }
+}
+
+bool ThreadPlanStepThrough::ValidatePlan(Stream *error) {
+ if (m_could_not_resolve_hw_bp) {
+ if (error)
+ error->PutCString(
+ "Could not create hardware breakpoint for thread plan.");
+ return false;
+ }
+
+ if (m_backstop_bkpt_id == LLDB_INVALID_BREAK_ID) {
+ if (error)
+ error->PutCString("Could not create backstop breakpoint.");
+ return false;
+ }
+
+ if (!m_sub_plan_sp.get()) {
+ if (error)
+ error->PutCString("Does not have a subplan.");
+ return false;
+ }
+
+ return true;
+}
+
+bool ThreadPlanStepThrough::DoPlanExplainsStop(Event *event_ptr) {
+ // If we have a sub-plan, it will have been asked first if we explain the
+ // stop, and we won't get asked. The only time we would be the one directly
+ // asked this question is if we hit our backstop breakpoint.
+
+ return HitOurBackstopBreakpoint();
+}
+
+bool ThreadPlanStepThrough::ShouldStop(Event *event_ptr) {
+ // If we've already marked ourselves done, then we're done...
+ if (IsPlanComplete())
+ return true;
+
+ // First, did we hit the backstop breakpoint?
+ if (HitOurBackstopBreakpoint()) {
+ SetPlanComplete(true);
+ return true;
+ }
+
+ // If we don't have a sub-plan, then we're also done (can't see how we would
+ // ever get here without a plan, but just in case.
+
+ if (!m_sub_plan_sp) {
+ SetPlanComplete();
+ return true;
+ }
+
+ // If the current sub plan is not done, we don't want to stop. Actually, we
+ // probably won't ever get here in this state, since we generally won't get
+ // asked any questions if out current sub-plan is not done...
+ if (!m_sub_plan_sp->IsPlanComplete())
+ return false;
+
+ // If our current sub plan failed, then let's just run to our backstop. If
+ // we can't do that then just stop.
+ if (!m_sub_plan_sp->PlanSucceeded()) {
+ if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID) {
+ m_sub_plan_sp.reset();
+ return false;
+ } else {
+ SetPlanComplete(false);
+ return true;
+ }
+ }
+
+ // Next see if there is a specific step through plan at our current pc (these
+ // might chain, for instance stepping through a dylib trampoline to the objc
+ // dispatch function...)
+ LookForPlanToStepThroughFromCurrentPC();
+ if (m_sub_plan_sp) {
+ PushPlan(m_sub_plan_sp);
+ return false;
+ } else {
+ SetPlanComplete();
+ return true;
+ }
+}
+
+bool ThreadPlanStepThrough::StopOthers() { return m_stop_others; }
+
+StateType ThreadPlanStepThrough::GetPlanRunState() { return eStateRunning; }
+
+bool ThreadPlanStepThrough::DoWillResume(StateType resume_state,
+ bool current_plan) {
+ return true;
+}
+
+bool ThreadPlanStepThrough::WillStop() { return true; }
+
+void ThreadPlanStepThrough::ClearBackstopBreakpoint() {
+ if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID) {
+ m_thread.GetProcess()->GetTarget().RemoveBreakpointByID(m_backstop_bkpt_id);
+ m_backstop_bkpt_id = LLDB_INVALID_BREAK_ID;
+ m_could_not_resolve_hw_bp = false;
+ }
+}
+
+bool ThreadPlanStepThrough::MischiefManaged() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ if (!IsPlanComplete()) {
+ return false;
+ } else {
+ if (log)
+ log->Printf("Completed step through step plan.");
+
+ ClearBackstopBreakpoint();
+ ThreadPlan::MischiefManaged();
+ return true;
+ }
+}
+
+bool ThreadPlanStepThrough::HitOurBackstopBreakpoint() {
+ StopInfoSP stop_info_sp(m_thread.GetStopInfo());
+ if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint) {
+ break_id_t stop_value = (break_id_t)stop_info_sp->GetValue();
+ BreakpointSiteSP cur_site_sp =
+ m_thread.GetProcess()->GetBreakpointSiteList().FindByID(stop_value);
+ if (cur_site_sp &&
+ cur_site_sp->IsBreakpointAtThisSite(m_backstop_bkpt_id)) {
+ StackID cur_frame_zero_id =
+ m_thread.GetStackFrameAtIndex(0)->GetStackID();
+
+ if (cur_frame_zero_id == m_return_stack_id) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->PutCString("ThreadPlanStepThrough hit backstop breakpoint.");
+ return true;
+ }
+ }
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanStepUntil.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepUntil.cpp
new file mode 100644
index 000000000000..d4109c3d38da
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanStepUntil.cpp
@@ -0,0 +1,339 @@
+//===-- ThreadPlanStepUntil.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanStepUntil.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Symbol/SymbolContextScope.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanStepUntil: Run until we reach a given line number or step out of
+// the current frame
+
+ThreadPlanStepUntil::ThreadPlanStepUntil(Thread &thread,
+ lldb::addr_t *address_list,
+ size_t num_addresses, bool stop_others,
+ uint32_t frame_idx)
+ : ThreadPlan(ThreadPlan::eKindStepUntil, "Step until", thread,
+ eVoteNoOpinion, eVoteNoOpinion),
+ m_step_from_insn(LLDB_INVALID_ADDRESS),
+ m_return_bp_id(LLDB_INVALID_BREAK_ID),
+ m_return_addr(LLDB_INVALID_ADDRESS), m_stepped_out(false),
+ m_should_stop(false), m_ran_analyze(false), m_explains_stop(false),
+ m_until_points(), m_stop_others(stop_others) {
+ // Stash away our "until" addresses:
+ TargetSP target_sp(m_thread.CalculateTarget());
+
+ StackFrameSP frame_sp(m_thread.GetStackFrameAtIndex(frame_idx));
+ if (frame_sp) {
+ m_step_from_insn = frame_sp->GetStackID().GetPC();
+ lldb::user_id_t thread_id = m_thread.GetID();
+
+ // Find the return address and set a breakpoint there:
+ // FIXME - can we do this more securely if we know first_insn?
+
+ StackFrameSP return_frame_sp(m_thread.GetStackFrameAtIndex(frame_idx + 1));
+ if (return_frame_sp) {
+ // TODO: add inline functionality
+ m_return_addr = return_frame_sp->GetStackID().GetPC();
+ Breakpoint *return_bp =
+ target_sp->CreateBreakpoint(m_return_addr, true, false).get();
+
+ if (return_bp != nullptr) {
+ if (return_bp->IsHardware() && !return_bp->HasResolvedLocations())
+ m_could_not_resolve_hw_bp = true;
+ return_bp->SetThreadID(thread_id);
+ m_return_bp_id = return_bp->GetID();
+ return_bp->SetBreakpointKind("until-return-backstop");
+ }
+ }
+
+ m_stack_id = frame_sp->GetStackID();
+
+ // Now set breakpoints on all our return addresses:
+ for (size_t i = 0; i < num_addresses; i++) {
+ Breakpoint *until_bp =
+ target_sp->CreateBreakpoint(address_list[i], true, false).get();
+ if (until_bp != nullptr) {
+ until_bp->SetThreadID(thread_id);
+ m_until_points[address_list[i]] = until_bp->GetID();
+ until_bp->SetBreakpointKind("until-target");
+ } else {
+ m_until_points[address_list[i]] = LLDB_INVALID_BREAK_ID;
+ }
+ }
+ }
+}
+
+ThreadPlanStepUntil::~ThreadPlanStepUntil() { Clear(); }
+
+void ThreadPlanStepUntil::Clear() {
+ TargetSP target_sp(m_thread.CalculateTarget());
+ if (target_sp) {
+ if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {
+ target_sp->RemoveBreakpointByID(m_return_bp_id);
+ m_return_bp_id = LLDB_INVALID_BREAK_ID;
+ }
+
+ until_collection::iterator pos, end = m_until_points.end();
+ for (pos = m_until_points.begin(); pos != end; pos++) {
+ target_sp->RemoveBreakpointByID((*pos).second);
+ }
+ }
+ m_until_points.clear();
+ m_could_not_resolve_hw_bp = false;
+}
+
+void ThreadPlanStepUntil::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ if (level == lldb::eDescriptionLevelBrief) {
+ s->Printf("step until");
+ if (m_stepped_out)
+ s->Printf(" - stepped out");
+ } else {
+ if (m_until_points.size() == 1)
+ s->Printf("Stepping from address 0x%" PRIx64 " until we reach 0x%" PRIx64
+ " using breakpoint %d",
+ (uint64_t)m_step_from_insn,
+ (uint64_t)(*m_until_points.begin()).first,
+ (*m_until_points.begin()).second);
+ else {
+ until_collection::iterator pos, end = m_until_points.end();
+ s->Printf("Stepping from address 0x%" PRIx64 " until we reach one of:",
+ (uint64_t)m_step_from_insn);
+ for (pos = m_until_points.begin(); pos != end; pos++) {
+ s->Printf("\n\t0x%" PRIx64 " (bp: %d)", (uint64_t)(*pos).first,
+ (*pos).second);
+ }
+ }
+ s->Printf(" stepped out address is 0x%" PRIx64 ".",
+ (uint64_t)m_return_addr);
+ }
+}
+
+bool ThreadPlanStepUntil::ValidatePlan(Stream *error) {
+ if (m_could_not_resolve_hw_bp) {
+ if (error)
+ error->PutCString(
+ "Could not create hardware breakpoint for thread plan.");
+ return false;
+ } else if (m_return_bp_id == LLDB_INVALID_BREAK_ID) {
+ if (error)
+ error->PutCString("Could not create return breakpoint.");
+ return false;
+ } else {
+ until_collection::iterator pos, end = m_until_points.end();
+ for (pos = m_until_points.begin(); pos != end; pos++) {
+ if (!LLDB_BREAK_ID_IS_VALID((*pos).second))
+ return false;
+ }
+ return true;
+ }
+}
+
+void ThreadPlanStepUntil::AnalyzeStop() {
+ if (m_ran_analyze)
+ return;
+
+ StopInfoSP stop_info_sp = GetPrivateStopInfo();
+ m_should_stop = true;
+ m_explains_stop = false;
+
+ if (stop_info_sp) {
+ StopReason reason = stop_info_sp->GetStopReason();
+
+ if (reason == eStopReasonBreakpoint) {
+ // If this is OUR breakpoint, we're fine, otherwise we don't know why
+ // this happened...
+ BreakpointSiteSP this_site =
+ m_thread.GetProcess()->GetBreakpointSiteList().FindByID(
+ stop_info_sp->GetValue());
+ if (!this_site) {
+ m_explains_stop = false;
+ return;
+ }
+
+ if (this_site->IsBreakpointAtThisSite(m_return_bp_id)) {
+ // If we are at our "step out" breakpoint, and the stack depth has
+ // shrunk, then this is indeed our stop. If the stack depth has grown,
+ // then we've hit our step out breakpoint recursively. If we are the
+ // only breakpoint at that location, then we do explain the stop, and
+ // we'll just continue. If there was another breakpoint here, then we
+ // don't explain the stop, but we won't mark ourselves Completed,
+ // because maybe that breakpoint will continue, and then we'll finish
+ // the "until".
+ bool done;
+ StackID cur_frame_zero_id;
+
+ done = (m_stack_id < cur_frame_zero_id);
+
+ if (done) {
+ m_stepped_out = true;
+ SetPlanComplete();
+ } else
+ m_should_stop = false;
+
+ if (this_site->GetNumberOfOwners() == 1)
+ m_explains_stop = true;
+ else
+ m_explains_stop = false;
+ return;
+ } else {
+ // Check if we've hit one of our "until" breakpoints.
+ until_collection::iterator pos, end = m_until_points.end();
+ for (pos = m_until_points.begin(); pos != end; pos++) {
+ if (this_site->IsBreakpointAtThisSite((*pos).second)) {
+ // If we're at the right stack depth, then we're done.
+
+ bool done;
+ StackID frame_zero_id =
+ m_thread.GetStackFrameAtIndex(0)->GetStackID();
+
+ if (frame_zero_id == m_stack_id)
+ done = true;
+ else if (frame_zero_id < m_stack_id)
+ done = false;
+ else {
+ StackFrameSP older_frame_sp = m_thread.GetStackFrameAtIndex(1);
+
+ // But if we can't even unwind one frame we should just get out
+ // of here & stop...
+ if (older_frame_sp) {
+ const SymbolContext &older_context =
+ older_frame_sp->GetSymbolContext(eSymbolContextEverything);
+ SymbolContext stack_context;
+ m_stack_id.GetSymbolContextScope()->CalculateSymbolContext(
+ &stack_context);
+
+ done = (older_context == stack_context);
+ } else
+ done = false;
+ }
+
+ if (done)
+ SetPlanComplete();
+ else
+ m_should_stop = false;
+
+ // Otherwise we've hit this breakpoint recursively. If we're the
+ // only breakpoint here, then we do explain the stop, and we'll
+ // continue. If not then we should let higher plans handle this
+ // stop.
+ if (this_site->GetNumberOfOwners() == 1)
+ m_explains_stop = true;
+ else {
+ m_should_stop = true;
+ m_explains_stop = false;
+ }
+ return;
+ }
+ }
+ }
+ // If we get here we haven't hit any of our breakpoints, so let the
+ // higher plans take care of the stop.
+ m_explains_stop = false;
+ return;
+ } else if (IsUsuallyUnexplainedStopReason(reason)) {
+ m_explains_stop = false;
+ } else {
+ m_explains_stop = true;
+ }
+ }
+}
+
+bool ThreadPlanStepUntil::DoPlanExplainsStop(Event *event_ptr) {
+ // We don't explain signals or breakpoints (breakpoints that handle stepping
+ // in or out will be handled by a child plan.
+ AnalyzeStop();
+ return m_explains_stop;
+}
+
+bool ThreadPlanStepUntil::ShouldStop(Event *event_ptr) {
+ // If we've told our self in ExplainsStop that we plan to continue, then do
+ // so here. Otherwise, as long as this thread has stopped for a reason, we
+ // will stop.
+
+ StopInfoSP stop_info_sp = GetPrivateStopInfo();
+ if (!stop_info_sp || stop_info_sp->GetStopReason() == eStopReasonNone)
+ return false;
+
+ AnalyzeStop();
+ return m_should_stop;
+}
+
+bool ThreadPlanStepUntil::StopOthers() { return m_stop_others; }
+
+StateType ThreadPlanStepUntil::GetPlanRunState() { return eStateRunning; }
+
+bool ThreadPlanStepUntil::DoWillResume(StateType resume_state,
+ bool current_plan) {
+ if (current_plan) {
+ TargetSP target_sp(m_thread.CalculateTarget());
+ if (target_sp) {
+ Breakpoint *return_bp =
+ target_sp->GetBreakpointByID(m_return_bp_id).get();
+ if (return_bp != nullptr)
+ return_bp->SetEnabled(true);
+
+ until_collection::iterator pos, end = m_until_points.end();
+ for (pos = m_until_points.begin(); pos != end; pos++) {
+ Breakpoint *until_bp =
+ target_sp->GetBreakpointByID((*pos).second).get();
+ if (until_bp != nullptr)
+ until_bp->SetEnabled(true);
+ }
+ }
+ }
+
+ m_should_stop = true;
+ m_ran_analyze = false;
+ m_explains_stop = false;
+ return true;
+}
+
+bool ThreadPlanStepUntil::WillStop() {
+ TargetSP target_sp(m_thread.CalculateTarget());
+ if (target_sp) {
+ Breakpoint *return_bp = target_sp->GetBreakpointByID(m_return_bp_id).get();
+ if (return_bp != nullptr)
+ return_bp->SetEnabled(false);
+
+ until_collection::iterator pos, end = m_until_points.end();
+ for (pos = m_until_points.begin(); pos != end; pos++) {
+ Breakpoint *until_bp = target_sp->GetBreakpointByID((*pos).second).get();
+ if (until_bp != nullptr)
+ until_bp->SetEnabled(false);
+ }
+ }
+ return true;
+}
+
+bool ThreadPlanStepUntil::MischiefManaged() {
+ // I'm letting "PlanExplainsStop" do all the work, and just reporting that
+ // here.
+ bool done = false;
+ if (IsPlanComplete()) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Completed step until plan.");
+
+ Clear();
+ done = true;
+ }
+ if (done)
+ ThreadPlan::MischiefManaged();
+
+ return done;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadPlanTracer.cpp b/contrib/llvm-project/lldb/source/Target/ThreadPlanTracer.cpp
new file mode 100644
index 000000000000..4e79b6b1e59d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadPlanTracer.cpp
@@ -0,0 +1,225 @@
+//===-- ThreadPlanTracer.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstring>
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark ThreadPlanTracer
+
+ThreadPlanTracer::ThreadPlanTracer(Thread &thread, lldb::StreamSP &stream_sp)
+ : m_thread(thread), m_single_step(true), m_enabled(false),
+ m_stream_sp(stream_sp) {}
+
+ThreadPlanTracer::ThreadPlanTracer(Thread &thread)
+ : m_thread(thread), m_single_step(true), m_enabled(false), m_stream_sp() {}
+
+Stream *ThreadPlanTracer::GetLogStream() {
+ if (m_stream_sp)
+ return m_stream_sp.get();
+ else {
+ TargetSP target_sp(m_thread.CalculateTarget());
+ if (target_sp)
+ return target_sp->GetDebugger().GetOutputFile().get();
+ }
+ return nullptr;
+}
+
+void ThreadPlanTracer::Log() {
+ SymbolContext sc;
+ bool show_frame_index = false;
+ bool show_fullpaths = false;
+
+ Stream *stream = GetLogStream();
+ if (stream) {
+ m_thread.GetStackFrameAtIndex(0)->Dump(stream, show_frame_index,
+ show_fullpaths);
+ stream->Printf("\n");
+ stream->Flush();
+ }
+}
+
+bool ThreadPlanTracer::TracerExplainsStop() {
+ if (m_enabled && m_single_step) {
+ lldb::StopInfoSP stop_info = m_thread.GetStopInfo();
+ return (stop_info->GetStopReason() == eStopReasonTrace);
+ } else
+ return false;
+}
+
+#pragma mark ThreadPlanAssemblyTracer
+
+ThreadPlanAssemblyTracer::ThreadPlanAssemblyTracer(Thread &thread,
+ lldb::StreamSP &stream_sp)
+ : ThreadPlanTracer(thread, stream_sp), m_disassembler_sp(), m_intptr_type(),
+ m_register_values() {}
+
+ThreadPlanAssemblyTracer::ThreadPlanAssemblyTracer(Thread &thread)
+ : ThreadPlanTracer(thread), m_disassembler_sp(), m_intptr_type(),
+ m_register_values() {}
+
+Disassembler *ThreadPlanAssemblyTracer::GetDisassembler() {
+ if (!m_disassembler_sp)
+ m_disassembler_sp = Disassembler::FindPlugin(
+ m_thread.GetProcess()->GetTarget().GetArchitecture(), nullptr, nullptr);
+ return m_disassembler_sp.get();
+}
+
+TypeFromUser ThreadPlanAssemblyTracer::GetIntPointerType() {
+ if (!m_intptr_type.IsValid()) {
+ TargetSP target_sp(m_thread.CalculateTarget());
+ if (target_sp) {
+ TypeSystem *type_system =
+ target_sp->GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC);
+ if (type_system)
+ m_intptr_type =
+ TypeFromUser(type_system->GetBuiltinTypeForEncodingAndBitSize(
+ eEncodingUint,
+ target_sp->GetArchitecture().GetAddressByteSize() * 8));
+ }
+ }
+ return m_intptr_type;
+}
+
+ThreadPlanAssemblyTracer::~ThreadPlanAssemblyTracer() = default;
+
+void ThreadPlanAssemblyTracer::TracingStarted() {
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+
+ if (m_register_values.empty())
+ m_register_values.resize(reg_ctx->GetRegisterCount());
+}
+
+void ThreadPlanAssemblyTracer::TracingEnded() { m_register_values.clear(); }
+
+void ThreadPlanAssemblyTracer::Log() {
+ Stream *stream = GetLogStream();
+
+ if (!stream)
+ return;
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+
+ lldb::addr_t pc = reg_ctx->GetPC();
+ ProcessSP process_sp(m_thread.GetProcess());
+ Address pc_addr;
+ bool addr_valid = false;
+ uint8_t buffer[16] = {0}; // Must be big enough for any single instruction
+ addr_valid = process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(
+ pc, pc_addr);
+
+ pc_addr.Dump(stream, &m_thread, Address::DumpStyleResolvedDescription,
+ Address::DumpStyleModuleWithFileAddress);
+ stream->PutCString(" ");
+
+ Disassembler *disassembler = GetDisassembler();
+ if (disassembler) {
+ Status err;
+ process_sp->ReadMemory(pc, buffer, sizeof(buffer), err);
+
+ if (err.Success()) {
+ DataExtractor extractor(buffer, sizeof(buffer),
+ process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+
+ bool data_from_file = false;
+ if (addr_valid)
+ disassembler->DecodeInstructions(pc_addr, extractor, 0, 1, false,
+ data_from_file);
+ else
+ disassembler->DecodeInstructions(Address(pc), extractor, 0, 1, false,
+ data_from_file);
+
+ InstructionList &instruction_list = disassembler->GetInstructionList();
+ const uint32_t max_opcode_byte_size =
+ instruction_list.GetMaxOpcocdeByteSize();
+
+ if (instruction_list.GetSize()) {
+ const bool show_bytes = true;
+ const bool show_address = true;
+ Instruction *instruction =
+ instruction_list.GetInstructionAtIndex(0).get();
+ const FormatEntity::Entry *disassemble_format =
+ m_thread.GetProcess()
+ ->GetTarget()
+ .GetDebugger()
+ .GetDisassemblyFormat();
+ instruction->Dump(stream, max_opcode_byte_size, show_address,
+ show_bytes, nullptr, nullptr, nullptr,
+ disassemble_format, 0);
+ }
+ }
+ }
+
+ const ABI *abi = process_sp->GetABI().get();
+ TypeFromUser intptr_type = GetIntPointerType();
+
+ if (abi && intptr_type.IsValid()) {
+ ValueList value_list;
+ const int num_args = 1;
+
+ for (int arg_index = 0; arg_index < num_args; ++arg_index) {
+ Value value;
+ value.SetValueType(Value::eValueTypeScalar);
+ value.SetCompilerType(intptr_type);
+ value_list.PushValue(value);
+ }
+
+ if (abi->GetArgumentValues(m_thread, value_list)) {
+ for (int arg_index = 0; arg_index < num_args; ++arg_index) {
+ stream->Printf(
+ "\n\targ[%d]=%llx", arg_index,
+ value_list.GetValueAtIndex(arg_index)->GetScalar().ULongLong());
+
+ if (arg_index + 1 < num_args)
+ stream->PutCString(", ");
+ }
+ }
+ }
+
+ RegisterValue reg_value;
+ for (uint32_t reg_num = 0, num_registers = reg_ctx->GetRegisterCount();
+ reg_num < num_registers; ++reg_num) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num);
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ assert(reg_num < m_register_values.size());
+ if (m_register_values[reg_num].GetType() == RegisterValue::eTypeInvalid ||
+ reg_value != m_register_values[reg_num]) {
+ if (reg_value.GetType() != RegisterValue::eTypeInvalid) {
+ stream->PutCString("\n\t");
+ DumpRegisterValue(reg_value, stream, reg_info, true, false,
+ eFormatDefault);
+ }
+ }
+ m_register_values[reg_num] = reg_value;
+ }
+ }
+ stream->EOL();
+ stream->Flush();
+}
diff --git a/contrib/llvm-project/lldb/source/Target/ThreadSpec.cpp b/contrib/llvm-project/lldb/source/Target/ThreadSpec.cpp
new file mode 100644
index 000000000000..1a733cb551e5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/ThreadSpec.cpp
@@ -0,0 +1,157 @@
+//===-- ThreadSpec.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/StructuredData.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *ThreadSpec::g_option_names[static_cast<uint32_t>(
+ ThreadSpec::OptionNames::LastOptionName)]{"Index", "ID", "Name",
+ "QueueName"};
+
+ThreadSpec::ThreadSpec()
+ : m_index(UINT32_MAX), m_tid(LLDB_INVALID_THREAD_ID), m_name(),
+ m_queue_name() {}
+
+std::unique_ptr<ThreadSpec> ThreadSpec::CreateFromStructuredData(
+ const StructuredData::Dictionary &spec_dict, Status &error) {
+ uint32_t index = UINT32_MAX;
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ llvm::StringRef name;
+ llvm::StringRef queue_name;
+
+ std::unique_ptr<ThreadSpec> thread_spec_up(new ThreadSpec());
+ bool success = spec_dict.GetValueForKeyAsInteger(
+ GetKey(OptionNames::ThreadIndex), index);
+ if (success)
+ thread_spec_up->SetIndex(index);
+
+ success =
+ spec_dict.GetValueForKeyAsInteger(GetKey(OptionNames::ThreadID), tid);
+ if (success)
+ thread_spec_up->SetTID(tid);
+
+ success =
+ spec_dict.GetValueForKeyAsString(GetKey(OptionNames::ThreadName), name);
+ if (success)
+ thread_spec_up->SetName(name);
+
+ success = spec_dict.GetValueForKeyAsString(GetKey(OptionNames::ThreadName),
+ queue_name);
+ if (success)
+ thread_spec_up->SetQueueName(queue_name);
+
+ return thread_spec_up;
+}
+
+StructuredData::ObjectSP ThreadSpec::SerializeToStructuredData() {
+ StructuredData::DictionarySP data_dict_sp(new StructuredData::Dictionary());
+
+ if (m_index != UINT32_MAX)
+ data_dict_sp->AddIntegerItem(GetKey(OptionNames::ThreadIndex), m_index);
+ if (m_tid != LLDB_INVALID_THREAD_ID)
+ data_dict_sp->AddIntegerItem(GetKey(OptionNames::ThreadID), m_tid);
+ if (!m_name.empty())
+ data_dict_sp->AddStringItem(GetKey(OptionNames::ThreadName), m_name);
+ if (!m_queue_name.empty())
+ data_dict_sp->AddStringItem(GetKey(OptionNames::QueueName), m_queue_name);
+
+ return data_dict_sp;
+}
+
+const char *ThreadSpec::GetName() const {
+ return m_name.empty() ? nullptr : m_name.c_str();
+}
+
+const char *ThreadSpec::GetQueueName() const {
+ return m_queue_name.empty() ? nullptr : m_queue_name.c_str();
+}
+
+bool ThreadSpec::TIDMatches(Thread &thread) const {
+ if (m_tid == LLDB_INVALID_THREAD_ID)
+ return true;
+
+ lldb::tid_t thread_id = thread.GetID();
+ return TIDMatches(thread_id);
+}
+
+bool ThreadSpec::IndexMatches(Thread &thread) const {
+ if (m_index == UINT32_MAX)
+ return true;
+ uint32_t index = thread.GetIndexID();
+ return IndexMatches(index);
+}
+
+bool ThreadSpec::NameMatches(Thread &thread) const {
+ if (m_name.empty())
+ return true;
+
+ const char *name = thread.GetName();
+ return NameMatches(name);
+}
+
+bool ThreadSpec::QueueNameMatches(Thread &thread) const {
+ if (m_queue_name.empty())
+ return true;
+
+ const char *queue_name = thread.GetQueueName();
+ return QueueNameMatches(queue_name);
+}
+
+bool ThreadSpec::ThreadPassesBasicTests(Thread &thread) const {
+ if (!HasSpecification())
+ return true;
+
+ if (!TIDMatches(thread))
+ return false;
+
+ if (!IndexMatches(thread))
+ return false;
+
+ if (!NameMatches(thread))
+ return false;
+
+ if (!QueueNameMatches(thread))
+ return false;
+
+ return true;
+}
+
+bool ThreadSpec::HasSpecification() const {
+ return (m_index != UINT32_MAX || m_tid != LLDB_INVALID_THREAD_ID ||
+ !m_name.empty() || !m_queue_name.empty());
+}
+
+void ThreadSpec::GetDescription(Stream *s, lldb::DescriptionLevel level) const {
+ if (!HasSpecification()) {
+ if (level == eDescriptionLevelBrief) {
+ s->PutCString("thread spec: no ");
+ }
+ } else {
+ if (level == eDescriptionLevelBrief) {
+ s->PutCString("thread spec: yes ");
+ } else {
+ if (GetTID() != LLDB_INVALID_THREAD_ID)
+ s->Printf("tid: 0x%" PRIx64 " ", GetTID());
+
+ if (GetIndex() != UINT32_MAX)
+ s->Printf("index: %d ", GetIndex());
+
+ const char *name = GetName();
+ if (name)
+ s->Printf("thread name: \"%s\" ", name);
+
+ const char *queue_name = GetQueueName();
+ if (queue_name)
+ s->Printf("queue name: \"%s\" ", queue_name);
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Target/UnixSignals.cpp b/contrib/llvm-project/lldb/source/Target/UnixSignals.cpp
new file mode 100644
index 000000000000..33090a72df54
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/UnixSignals.cpp
@@ -0,0 +1,324 @@
+//===-- UnixSignals.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/UnixSignals.h"
+#include "Plugins/Process/Utility/FreeBSDSignals.h"
+#include "Plugins/Process/Utility/LinuxSignals.h"
+#include "Plugins/Process/Utility/MipsLinuxSignals.h"
+#include "Plugins/Process/Utility/NetBSDSignals.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Utility/ArchSpec.h"
+
+using namespace lldb_private;
+
+UnixSignals::Signal::Signal(const char *name, bool default_suppress,
+ bool default_stop, bool default_notify,
+ const char *description, const char *alias)
+ : m_name(name), m_alias(alias), m_description(),
+ m_suppress(default_suppress), m_stop(default_stop),
+ m_notify(default_notify) {
+ if (description)
+ m_description.assign(description);
+}
+
+lldb::UnixSignalsSP UnixSignals::Create(const ArchSpec &arch) {
+ const auto &triple = arch.GetTriple();
+ switch (triple.getOS()) {
+ case llvm::Triple::Linux: {
+ switch (triple.getArch()) {
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ return std::make_shared<MipsLinuxSignals>();
+ default:
+ return std::make_shared<LinuxSignals>();
+ }
+ }
+ case llvm::Triple::FreeBSD:
+ case llvm::Triple::OpenBSD:
+ return std::make_shared<FreeBSDSignals>();
+ case llvm::Triple::NetBSD:
+ return std::make_shared<NetBSDSignals>();
+ default:
+ return std::make_shared<UnixSignals>();
+ }
+}
+
+lldb::UnixSignalsSP UnixSignals::CreateForHost() {
+ static lldb::UnixSignalsSP s_unix_signals_sp =
+ Create(HostInfo::GetArchitecture());
+ return s_unix_signals_sp;
+}
+
+// UnixSignals constructor
+UnixSignals::UnixSignals() { Reset(); }
+
+UnixSignals::UnixSignals(const UnixSignals &rhs) : m_signals(rhs.m_signals) {}
+
+UnixSignals::~UnixSignals() = default;
+
+void UnixSignals::Reset() {
+ // This builds one standard set of Unix Signals. If yours aren't quite in
+ // this order, you can either subclass this class, and use Add & Remove to
+ // change them
+ // or you can subclass and build them afresh in your constructor;
+ //
+ // Note: the signals below are the Darwin signals. Do not change these!
+ m_signals.clear();
+ // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION
+ // ====== ============ ======== ====== ======
+ // ===================================================
+ AddSignal(1, "SIGHUP", false, true, true, "hangup");
+ AddSignal(2, "SIGINT", true, true, true, "interrupt");
+ AddSignal(3, "SIGQUIT", false, true, true, "quit");
+ AddSignal(4, "SIGILL", false, true, true, "illegal instruction");
+ AddSignal(5, "SIGTRAP", true, true, true,
+ "trace trap (not reset when caught)");
+ AddSignal(6, "SIGABRT", false, true, true, "abort()");
+ AddSignal(7, "SIGEMT", false, true, true, "pollable event");
+ AddSignal(8, "SIGFPE", false, true, true, "floating point exception");
+ AddSignal(9, "SIGKILL", false, true, true, "kill");
+ AddSignal(10, "SIGBUS", false, true, true, "bus error");
+ AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation");
+ AddSignal(12, "SIGSYS", false, true, true, "bad argument to system call");
+ AddSignal(13, "SIGPIPE", false, false, false,
+ "write on a pipe with no one to read it");
+ AddSignal(14, "SIGALRM", false, false, false, "alarm clock");
+ AddSignal(15, "SIGTERM", false, true, true,
+ "software termination signal from kill");
+ AddSignal(16, "SIGURG", false, false, false,
+ "urgent condition on IO channel");
+ AddSignal(17, "SIGSTOP", true, true, true,
+ "sendable stop signal not from tty");
+ AddSignal(18, "SIGTSTP", false, true, true, "stop signal from tty");
+ AddSignal(19, "SIGCONT", false, true, true, "continue a stopped process");
+ AddSignal(20, "SIGCHLD", false, false, false,
+ "to parent on child stop or exit");
+ AddSignal(21, "SIGTTIN", false, true, true,
+ "to readers process group upon background tty read");
+ AddSignal(22, "SIGTTOU", false, true, true,
+ "to readers process group upon background tty write");
+ AddSignal(23, "SIGIO", false, false, false, "input/output possible signal");
+ AddSignal(24, "SIGXCPU", false, true, true, "exceeded CPU time limit");
+ AddSignal(25, "SIGXFSZ", false, true, true, "exceeded file size limit");
+ AddSignal(26, "SIGVTALRM", false, false, false, "virtual time alarm");
+ AddSignal(27, "SIGPROF", false, false, false, "profiling time alarm");
+ AddSignal(28, "SIGWINCH", false, false, false, "window size changes");
+ AddSignal(29, "SIGINFO", false, true, true, "information request");
+ AddSignal(30, "SIGUSR1", false, true, true, "user defined signal 1");
+ AddSignal(31, "SIGUSR2", false, true, true, "user defined signal 2");
+}
+
+void UnixSignals::AddSignal(int signo, const char *name, bool default_suppress,
+ bool default_stop, bool default_notify,
+ const char *description, const char *alias) {
+ Signal new_signal(name, default_suppress, default_stop, default_notify,
+ description, alias);
+ m_signals.insert(std::make_pair(signo, new_signal));
+ ++m_version;
+}
+
+void UnixSignals::RemoveSignal(int signo) {
+ collection::iterator pos = m_signals.find(signo);
+ if (pos != m_signals.end())
+ m_signals.erase(pos);
+ ++m_version;
+}
+
+const char *UnixSignals::GetSignalAsCString(int signo) const {
+ collection::const_iterator pos = m_signals.find(signo);
+ if (pos == m_signals.end())
+ return nullptr;
+ else
+ return pos->second.m_name.GetCString();
+}
+
+bool UnixSignals::SignalIsValid(int32_t signo) const {
+ return m_signals.find(signo) != m_signals.end();
+}
+
+ConstString UnixSignals::GetShortName(ConstString name) const {
+ if (name) {
+ const char *signame = name.AsCString();
+ return ConstString(signame + 3); // Remove "SIG" from name
+ }
+ return name;
+}
+
+int32_t UnixSignals::GetSignalNumberFromName(const char *name) const {
+ ConstString const_name(name);
+
+ collection::const_iterator pos, end = m_signals.end();
+ for (pos = m_signals.begin(); pos != end; pos++) {
+ if ((const_name == pos->second.m_name) ||
+ (const_name == pos->second.m_alias) ||
+ (const_name == GetShortName(pos->second.m_name)) ||
+ (const_name == GetShortName(pos->second.m_alias)))
+ return pos->first;
+ }
+
+ const int32_t signo =
+ StringConvert::ToSInt32(name, LLDB_INVALID_SIGNAL_NUMBER, 0);
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ return signo;
+ return LLDB_INVALID_SIGNAL_NUMBER;
+}
+
+int32_t UnixSignals::GetFirstSignalNumber() const {
+ if (m_signals.empty())
+ return LLDB_INVALID_SIGNAL_NUMBER;
+
+ return (*m_signals.begin()).first;
+}
+
+int32_t UnixSignals::GetNextSignalNumber(int32_t current_signal) const {
+ collection::const_iterator pos = m_signals.find(current_signal);
+ collection::const_iterator end = m_signals.end();
+ if (pos == end)
+ return LLDB_INVALID_SIGNAL_NUMBER;
+ else {
+ pos++;
+ if (pos == end)
+ return LLDB_INVALID_SIGNAL_NUMBER;
+ else
+ return pos->first;
+ }
+}
+
+const char *UnixSignals::GetSignalInfo(int32_t signo, bool &should_suppress,
+ bool &should_stop,
+ bool &should_notify) const {
+ collection::const_iterator pos = m_signals.find(signo);
+ if (pos == m_signals.end())
+ return nullptr;
+ else {
+ const Signal &signal = pos->second;
+ should_suppress = signal.m_suppress;
+ should_stop = signal.m_stop;
+ should_notify = signal.m_notify;
+ return signal.m_name.AsCString("");
+ }
+}
+
+bool UnixSignals::GetShouldSuppress(int signo) const {
+ collection::const_iterator pos = m_signals.find(signo);
+ if (pos != m_signals.end())
+ return pos->second.m_suppress;
+ return false;
+}
+
+bool UnixSignals::SetShouldSuppress(int signo, bool value) {
+ collection::iterator pos = m_signals.find(signo);
+ if (pos != m_signals.end()) {
+ pos->second.m_suppress = value;
+ ++m_version;
+ return true;
+ }
+ return false;
+}
+
+bool UnixSignals::SetShouldSuppress(const char *signal_name, bool value) {
+ const int32_t signo = GetSignalNumberFromName(signal_name);
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ return SetShouldSuppress(signo, value);
+ return false;
+}
+
+bool UnixSignals::GetShouldStop(int signo) const {
+ collection::const_iterator pos = m_signals.find(signo);
+ if (pos != m_signals.end())
+ return pos->second.m_stop;
+ return false;
+}
+
+bool UnixSignals::SetShouldStop(int signo, bool value) {
+ collection::iterator pos = m_signals.find(signo);
+ if (pos != m_signals.end()) {
+ pos->second.m_stop = value;
+ ++m_version;
+ return true;
+ }
+ return false;
+}
+
+bool UnixSignals::SetShouldStop(const char *signal_name, bool value) {
+ const int32_t signo = GetSignalNumberFromName(signal_name);
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ return SetShouldStop(signo, value);
+ return false;
+}
+
+bool UnixSignals::GetShouldNotify(int signo) const {
+ collection::const_iterator pos = m_signals.find(signo);
+ if (pos != m_signals.end())
+ return pos->second.m_notify;
+ return false;
+}
+
+bool UnixSignals::SetShouldNotify(int signo, bool value) {
+ collection::iterator pos = m_signals.find(signo);
+ if (pos != m_signals.end()) {
+ pos->second.m_notify = value;
+ ++m_version;
+ return true;
+ }
+ return false;
+}
+
+bool UnixSignals::SetShouldNotify(const char *signal_name, bool value) {
+ const int32_t signo = GetSignalNumberFromName(signal_name);
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ return SetShouldNotify(signo, value);
+ return false;
+}
+
+int32_t UnixSignals::GetNumSignals() const { return m_signals.size(); }
+
+int32_t UnixSignals::GetSignalAtIndex(int32_t index) const {
+ if (index < 0 || m_signals.size() <= static_cast<size_t>(index))
+ return LLDB_INVALID_SIGNAL_NUMBER;
+ auto it = m_signals.begin();
+ std::advance(it, index);
+ return it->first;
+}
+
+uint64_t UnixSignals::GetVersion() const { return m_version; }
+
+std::vector<int32_t>
+UnixSignals::GetFilteredSignals(llvm::Optional<bool> should_suppress,
+ llvm::Optional<bool> should_stop,
+ llvm::Optional<bool> should_notify) {
+ std::vector<int32_t> result;
+ for (int32_t signo = GetFirstSignalNumber();
+ signo != LLDB_INVALID_SIGNAL_NUMBER;
+ signo = GetNextSignalNumber(signo)) {
+
+ bool signal_suppress = false;
+ bool signal_stop = false;
+ bool signal_notify = false;
+ GetSignalInfo(signo, signal_suppress, signal_stop, signal_notify);
+
+ // If any of filtering conditions are not met, we move on to the next
+ // signal.
+ if (should_suppress.hasValue() &&
+ signal_suppress != should_suppress.getValue())
+ continue;
+
+ if (should_stop.hasValue() && signal_stop != should_stop.getValue())
+ continue;
+
+ if (should_notify.hasValue() && signal_notify != should_notify.getValue())
+ continue;
+
+ result.push_back(signo);
+ }
+
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Target/UnwindAssembly.cpp b/contrib/llvm-project/lldb/source/Target/UnwindAssembly.cpp
new file mode 100644
index 000000000000..d3d8068687c0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Target/UnwindAssembly.cpp
@@ -0,0 +1,33 @@
+//===-- UnwindAssembly.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/UnwindAssembly.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindAssemblySP UnwindAssembly::FindPlugin(const ArchSpec &arch) {
+ UnwindAssemblyCreateInstance create_callback;
+
+ for (uint32_t idx = 0;
+ (create_callback = PluginManager::GetUnwindAssemblyCreateCallbackAtIndex(
+ idx)) != nullptr;
+ ++idx) {
+ UnwindAssemblySP assembly_profiler_up(create_callback(arch));
+ if (assembly_profiler_up)
+ return assembly_profiler_up;
+ }
+ return nullptr;
+}
+
+UnwindAssembly::UnwindAssembly(const ArchSpec &arch) : m_arch(arch) {}
+
+UnwindAssembly::~UnwindAssembly() = default;
diff --git a/contrib/llvm-project/lldb/source/Utility/ARM64_DWARF_Registers.h b/contrib/llvm-project/lldb/source/Utility/ARM64_DWARF_Registers.h
new file mode 100644
index 000000000000..64f69d643565
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/ARM64_DWARF_Registers.h
@@ -0,0 +1,95 @@
+//===-- ARM64_DWARF_Registers.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_ARM64_DWARF_Registers_h_
+#define utility_ARM64_DWARF_Registers_h_
+
+#include "lldb/lldb-private.h"
+
+namespace arm64_dwarf {
+
+enum {
+ x0 = 0,
+ x1,
+ x2,
+ x3,
+ x4,
+ x5,
+ x6,
+ x7,
+ x8,
+ x9,
+ x10,
+ x11,
+ x12,
+ x13,
+ x14,
+ x15,
+ x16,
+ x17,
+ x18,
+ x19,
+ x20,
+ x21,
+ x22,
+ x23,
+ x24,
+ x25,
+ x26,
+ x27,
+ x28,
+ x29 = 29,
+ fp = x29,
+ x30 = 30,
+ lr = x30,
+ x31 = 31,
+ sp = x31,
+ pc = 32,
+ cpsr = 33,
+ // 34-63 reserved
+
+ // V0-V31 (128 bit vector registers)
+ v0 = 64,
+ v1,
+ v2,
+ v3,
+ v4,
+ v5,
+ v6,
+ v7,
+ v8,
+ v9,
+ v10,
+ v11,
+ v12,
+ v13,
+ v14,
+ v15,
+ v16,
+ v17,
+ v18,
+ v19,
+ v20,
+ v21,
+ v22,
+ v23,
+ v24,
+ v25,
+ v26,
+ v27,
+ v28,
+ v29,
+ v30,
+ v31
+
+ // 96-127 reserved
+};
+
+} // namespace arm64_dwarf
+
+#endif // utility_ARM64_DWARF_Registers_h_
diff --git a/contrib/llvm-project/lldb/source/Utility/ARM64_ehframe_Registers.h b/contrib/llvm-project/lldb/source/Utility/ARM64_ehframe_Registers.h
new file mode 100644
index 000000000000..9b5cd931bf28
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/ARM64_ehframe_Registers.h
@@ -0,0 +1,91 @@
+//===-- ARM64_ehframe_Registers.h -------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_ARM64_ehframe_Registers_h_
+#define utility_ARM64_ehframe_Registers_h_
+
+// The register numbers used in the eh_frame unwind information.
+// Should be the same as DWARF register numbers.
+
+namespace arm64_ehframe {
+
+enum {
+ x0 = 0,
+ x1,
+ x2,
+ x3,
+ x4,
+ x5,
+ x6,
+ x7,
+ x8,
+ x9,
+ x10,
+ x11,
+ x12,
+ x13,
+ x14,
+ x15,
+ x16,
+ x17,
+ x18,
+ x19,
+ x20,
+ x21,
+ x22,
+ x23,
+ x24,
+ x25,
+ x26,
+ x27,
+ x28,
+ fp, // aka x29
+ lr, // aka x30
+ sp, // aka x31 aka wzr
+ pc, // value is 32
+ cpsr
+};
+
+enum {
+ v0 = 64,
+ v1,
+ v2,
+ v3,
+ v4,
+ v5,
+ v6,
+ v7,
+ v8,
+ v9,
+ v10,
+ v11,
+ v12,
+ v13,
+ v14,
+ v15,
+ v16,
+ v17,
+ v18,
+ v19,
+ v20,
+ v21,
+ v22,
+ v23,
+ v24,
+ v25,
+ v26,
+ v27,
+ v28,
+ v29,
+ v30,
+ v31 // 95
+};
+}
+
+#endif // utility_ARM64_ehframe_Registers_h_
diff --git a/contrib/llvm-project/lldb/source/Utility/ARM_DWARF_Registers.h b/contrib/llvm-project/lldb/source/Utility/ARM_DWARF_Registers.h
new file mode 100644
index 000000000000..e33210dfbfbd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/ARM_DWARF_Registers.h
@@ -0,0 +1,207 @@
+//===-- ARM_DWARF_Registers.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_ARM_DWARF_Registers_h_
+#define utility_ARM_DWARF_Registers_h_
+
+#include "lldb/lldb-private.h"
+
+enum {
+ dwarf_r0 = 0,
+ dwarf_r1,
+ dwarf_r2,
+ dwarf_r3,
+ dwarf_r4,
+ dwarf_r5,
+ dwarf_r6,
+ dwarf_r7,
+ dwarf_r8,
+ dwarf_r9,
+ dwarf_r10,
+ dwarf_r11,
+ dwarf_r12,
+ dwarf_sp,
+ dwarf_lr,
+ dwarf_pc,
+ dwarf_cpsr,
+
+ dwarf_s0 = 64,
+ dwarf_s1,
+ dwarf_s2,
+ dwarf_s3,
+ dwarf_s4,
+ dwarf_s5,
+ dwarf_s6,
+ dwarf_s7,
+ dwarf_s8,
+ dwarf_s9,
+ dwarf_s10,
+ dwarf_s11,
+ dwarf_s12,
+ dwarf_s13,
+ dwarf_s14,
+ dwarf_s15,
+ dwarf_s16,
+ dwarf_s17,
+ dwarf_s18,
+ dwarf_s19,
+ dwarf_s20,
+ dwarf_s21,
+ dwarf_s22,
+ dwarf_s23,
+ dwarf_s24,
+ dwarf_s25,
+ dwarf_s26,
+ dwarf_s27,
+ dwarf_s28,
+ dwarf_s29,
+ dwarf_s30,
+ dwarf_s31,
+
+ // FPA Registers 0-7
+ dwarf_f0 = 96,
+ dwarf_f1,
+ dwarf_f2,
+ dwarf_f3,
+ dwarf_f4,
+ dwarf_f5,
+ dwarf_f6,
+ dwarf_f7,
+
+ // Intel wireless MMX general purpose registers 0 - 7
+ dwarf_wCGR0 = 104,
+ dwarf_wCGR1,
+ dwarf_wCGR2,
+ dwarf_wCGR3,
+ dwarf_wCGR4,
+ dwarf_wCGR5,
+ dwarf_wCGR6,
+ dwarf_wCGR7,
+
+ // XScale accumulator register 0 - 7 (they do overlap with wCGR0 - wCGR7)
+ dwarf_ACC0 = 104,
+ dwarf_ACC1,
+ dwarf_ACC2,
+ dwarf_ACC3,
+ dwarf_ACC4,
+ dwarf_ACC5,
+ dwarf_ACC6,
+ dwarf_ACC7,
+
+ // Intel wireless MMX data registers 0 - 15
+ dwarf_wR0 = 112,
+ dwarf_wR1,
+ dwarf_wR2,
+ dwarf_wR3,
+ dwarf_wR4,
+ dwarf_wR5,
+ dwarf_wR6,
+ dwarf_wR7,
+ dwarf_wR8,
+ dwarf_wR9,
+ dwarf_wR10,
+ dwarf_wR11,
+ dwarf_wR12,
+ dwarf_wR13,
+ dwarf_wR14,
+ dwarf_wR15,
+
+ dwarf_spsr = 128,
+ dwarf_spsr_fiq,
+ dwarf_spsr_irq,
+ dwarf_spsr_abt,
+ dwarf_spsr_und,
+ dwarf_spsr_svc,
+
+ dwarf_r8_usr = 144,
+ dwarf_r9_usr,
+ dwarf_r10_usr,
+ dwarf_r11_usr,
+ dwarf_r12_usr,
+ dwarf_r13_usr,
+ dwarf_r14_usr,
+ dwarf_r8_fiq,
+ dwarf_r9_fiq,
+ dwarf_r10_fiq,
+ dwarf_r11_fiq,
+ dwarf_r12_fiq,
+ dwarf_r13_fiq,
+ dwarf_r14_fiq,
+ dwarf_r13_irq,
+ dwarf_r14_irq,
+ dwarf_r13_abt,
+ dwarf_r14_abt,
+ dwarf_r13_und,
+ dwarf_r14_und,
+ dwarf_r13_svc,
+ dwarf_r14_svc,
+
+ // Intel wireless MMX control register in co-processor 0 - 7
+ dwarf_wC0 = 192,
+ dwarf_wC1,
+ dwarf_wC2,
+ dwarf_wC3,
+ dwarf_wC4,
+ dwarf_wC5,
+ dwarf_wC6,
+ dwarf_wC7,
+
+ // VFP-v3/Neon
+ dwarf_d0 = 256,
+ dwarf_d1,
+ dwarf_d2,
+ dwarf_d3,
+ dwarf_d4,
+ dwarf_d5,
+ dwarf_d6,
+ dwarf_d7,
+ dwarf_d8,
+ dwarf_d9,
+ dwarf_d10,
+ dwarf_d11,
+ dwarf_d12,
+ dwarf_d13,
+ dwarf_d14,
+ dwarf_d15,
+ dwarf_d16,
+ dwarf_d17,
+ dwarf_d18,
+ dwarf_d19,
+ dwarf_d20,
+ dwarf_d21,
+ dwarf_d22,
+ dwarf_d23,
+ dwarf_d24,
+ dwarf_d25,
+ dwarf_d26,
+ dwarf_d27,
+ dwarf_d28,
+ dwarf_d29,
+ dwarf_d30,
+ dwarf_d31,
+
+ // Neon quadword registers
+ dwarf_q0 = 288,
+ dwarf_q1,
+ dwarf_q2,
+ dwarf_q3,
+ dwarf_q4,
+ dwarf_q5,
+ dwarf_q6,
+ dwarf_q7,
+ dwarf_q8,
+ dwarf_q9,
+ dwarf_q10,
+ dwarf_q11,
+ dwarf_q12,
+ dwarf_q13,
+ dwarf_q14,
+ dwarf_q15
+};
+
+#endif // utility_ARM_DWARF_Registers_h_
diff --git a/contrib/llvm-project/lldb/source/Utility/ARM_ehframe_Registers.h b/contrib/llvm-project/lldb/source/Utility/ARM_ehframe_Registers.h
new file mode 100644
index 000000000000..1816b1d97497
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/ARM_ehframe_Registers.h
@@ -0,0 +1,36 @@
+//===-- ARM_ehframe_Registers.h -------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_ARM_ehframe_Registers_h_
+#define utility_ARM_ehframe_Registers_h_
+
+// The register numbers used in the eh_frame unwind information.
+// Should be the same as DWARF register numbers.
+
+enum {
+ ehframe_r0 = 0,
+ ehframe_r1,
+ ehframe_r2,
+ ehframe_r3,
+ ehframe_r4,
+ ehframe_r5,
+ ehframe_r6,
+ ehframe_r7,
+ ehframe_r8,
+ ehframe_r9,
+ ehframe_r10,
+ ehframe_r11,
+ ehframe_r12,
+ ehframe_sp,
+ ehframe_lr,
+ ehframe_pc,
+ ehframe_cpsr
+};
+
+#endif // utility_ARM_ehframe_Registers_h_
diff --git a/contrib/llvm-project/lldb/source/Utility/ArchSpec.cpp b/contrib/llvm-project/lldb/source/Utility/ArchSpec.cpp
new file mode 100644
index 000000000000..81b87fff88d4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/ArchSpec.cpp
@@ -0,0 +1,1459 @@
+//===-- ArchSpec.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/ArchSpec.h"
+
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/NameMatches.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+#include "lldb/lldb-defines.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Host.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2,
+ bool try_inverse, bool enforce_exact_match);
+
+namespace lldb_private {
+
+struct CoreDefinition {
+ ByteOrder default_byte_order;
+ uint32_t addr_byte_size;
+ uint32_t min_opcode_byte_size;
+ uint32_t max_opcode_byte_size;
+ llvm::Triple::ArchType machine;
+ ArchSpec::Core core;
+ const char *const name;
+};
+
+} // namespace lldb_private
+
+// This core information can be looked using the ArchSpec::Core as the index
+static const CoreDefinition g_core_definitions[] = {
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_generic,
+ "arm"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv4,
+ "armv4"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv4t,
+ "armv4t"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5,
+ "armv5"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5e,
+ "armv5e"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5t,
+ "armv5t"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv6,
+ "armv6"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv6m,
+ "armv6m"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7,
+ "armv7"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7f,
+ "armv7f"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7s,
+ "armv7s"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7k,
+ "armv7k"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7m,
+ "armv7m"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7em,
+ "armv7em"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_xscale,
+ "xscale"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumb,
+ "thumb"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv4t,
+ "thumbv4t"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv5,
+ "thumbv5"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv5e,
+ "thumbv5e"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv6,
+ "thumbv6"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv6m,
+ "thumbv6m"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7,
+ "thumbv7"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7f,
+ "thumbv7f"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7s,
+ "thumbv7s"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7k,
+ "thumbv7k"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7m,
+ "thumbv7m"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7em,
+ "thumbv7em"},
+ {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64,
+ ArchSpec::eCore_arm_arm64, "arm64"},
+ {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64,
+ ArchSpec::eCore_arm_armv8, "armv8"},
+ {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64,
+ ArchSpec::eCore_arm_aarch64, "aarch64"},
+
+ // mips32, mips32r2, mips32r3, mips32r5, mips32r6
+ {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32,
+ "mips"},
+ {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r2,
+ "mipsr2"},
+ {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r3,
+ "mipsr3"},
+ {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r5,
+ "mipsr5"},
+ {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r6,
+ "mipsr6"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32el,
+ "mipsel"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel,
+ ArchSpec::eCore_mips32r2el, "mipsr2el"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel,
+ ArchSpec::eCore_mips32r3el, "mipsr3el"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel,
+ ArchSpec::eCore_mips32r5el, "mipsr5el"},
+ {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel,
+ ArchSpec::eCore_mips32r6el, "mipsr6el"},
+
+ // mips64, mips64r2, mips64r3, mips64r5, mips64r6
+ {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64,
+ "mips64"},
+ {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r2,
+ "mips64r2"},
+ {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r3,
+ "mips64r3"},
+ {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r5,
+ "mips64r5"},
+ {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r6,
+ "mips64r6"},
+ {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el,
+ ArchSpec::eCore_mips64el, "mips64el"},
+ {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el,
+ ArchSpec::eCore_mips64r2el, "mips64r2el"},
+ {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el,
+ ArchSpec::eCore_mips64r3el, "mips64r3el"},
+ {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el,
+ ArchSpec::eCore_mips64r5el, "mips64r5el"},
+ {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el,
+ ArchSpec::eCore_mips64r6el, "mips64r6el"},
+
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_generic,
+ "powerpc"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc601,
+ "ppc601"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc602,
+ "ppc602"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603,
+ "ppc603"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603e,
+ "ppc603e"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603ev,
+ "ppc603ev"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc604,
+ "ppc604"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc604e,
+ "ppc604e"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc620,
+ "ppc620"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc750,
+ "ppc750"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc7400,
+ "ppc7400"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc7450,
+ "ppc7450"},
+ {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc970,
+ "ppc970"},
+
+ {eByteOrderLittle, 8, 4, 4, llvm::Triple::ppc64le,
+ ArchSpec::eCore_ppc64le_generic, "powerpc64le"},
+ {eByteOrderBig, 8, 4, 4, llvm::Triple::ppc64, ArchSpec::eCore_ppc64_generic,
+ "powerpc64"},
+ {eByteOrderBig, 8, 4, 4, llvm::Triple::ppc64,
+ ArchSpec::eCore_ppc64_ppc970_64, "ppc970-64"},
+
+ {eByteOrderBig, 8, 2, 6, llvm::Triple::systemz,
+ ArchSpec::eCore_s390x_generic, "s390x"},
+
+ {eByteOrderLittle, 4, 4, 4, llvm::Triple::sparc,
+ ArchSpec::eCore_sparc_generic, "sparc"},
+ {eByteOrderLittle, 8, 4, 4, llvm::Triple::sparcv9,
+ ArchSpec::eCore_sparc9_generic, "sparcv9"},
+
+ {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i386,
+ "i386"},
+ {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i486,
+ "i486"},
+ {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86,
+ ArchSpec::eCore_x86_32_i486sx, "i486sx"},
+ {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i686,
+ "i686"},
+
+ {eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64,
+ ArchSpec::eCore_x86_64_x86_64, "x86_64"},
+ {eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64,
+ ArchSpec::eCore_x86_64_x86_64h, "x86_64h"},
+ {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon,
+ ArchSpec::eCore_hexagon_generic, "hexagon"},
+ {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon,
+ ArchSpec::eCore_hexagon_hexagonv4, "hexagonv4"},
+ {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon,
+ ArchSpec::eCore_hexagon_hexagonv5, "hexagonv5"},
+
+ {eByteOrderLittle, 4, 4, 4, llvm::Triple::UnknownArch,
+ ArchSpec::eCore_uknownMach32, "unknown-mach-32"},
+ {eByteOrderLittle, 8, 4, 4, llvm::Triple::UnknownArch,
+ ArchSpec::eCore_uknownMach64, "unknown-mach-64"},
+};
+
+// Ensure that we have an entry in the g_core_definitions for each core. If you
+// comment out an entry above, you will need to comment out the corresponding
+// ArchSpec::Core enumeration.
+static_assert(sizeof(g_core_definitions) / sizeof(CoreDefinition) ==
+ ArchSpec::kNumCores,
+ "make sure we have one core definition for each core");
+
+struct ArchDefinitionEntry {
+ ArchSpec::Core core;
+ uint32_t cpu;
+ uint32_t sub;
+ uint32_t cpu_mask;
+ uint32_t sub_mask;
+};
+
+struct ArchDefinition {
+ ArchitectureType type;
+ size_t num_entries;
+ const ArchDefinitionEntry *entries;
+ const char *name;
+};
+
+void ArchSpec::ListSupportedArchNames(StringList &list) {
+ for (uint32_t i = 0; i < llvm::array_lengthof(g_core_definitions); ++i)
+ list.AppendString(g_core_definitions[i].name);
+}
+
+size_t ArchSpec::AutoComplete(CompletionRequest &request) {
+ if (!request.GetCursorArgumentPrefix().empty()) {
+ for (uint32_t i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) {
+ if (NameMatches(g_core_definitions[i].name, NameMatch::StartsWith,
+ request.GetCursorArgumentPrefix()))
+ request.AddCompletion(g_core_definitions[i].name);
+ }
+ } else {
+ StringList matches;
+ ListSupportedArchNames(matches);
+ request.AddCompletions(matches);
+ }
+ return request.GetNumberOfMatches();
+}
+
+#define CPU_ANY (UINT32_MAX)
+
+//===----------------------------------------------------------------------===//
+// A table that gets searched linearly for matches. This table is used to
+// convert cpu type and subtypes to architecture names, and to convert
+// architecture names to cpu types and subtypes. The ordering is important and
+// allows the precedence to be set when the table is built.
+#define SUBTYPE_MASK 0x00FFFFFFu
+
+static const ArchDefinitionEntry g_macho_arch_entries[] = {
+ {ArchSpec::eCore_arm_generic, llvm::MachO::CPU_TYPE_ARM, CPU_ANY,
+ UINT32_MAX, UINT32_MAX},
+ {ArchSpec::eCore_arm_generic, llvm::MachO::CPU_TYPE_ARM, 0, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv4, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv4t, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv6, llvm::MachO::CPU_TYPE_ARM, 6, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv6m, llvm::MachO::CPU_TYPE_ARM, 14, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv5, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv5e, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv5t, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_xscale, llvm::MachO::CPU_TYPE_ARM, 8, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv7, llvm::MachO::CPU_TYPE_ARM, 9, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv7f, llvm::MachO::CPU_TYPE_ARM, 10, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv7s, llvm::MachO::CPU_TYPE_ARM, 11, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv7k, llvm::MachO::CPU_TYPE_ARM, 12, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv7m, llvm::MachO::CPU_TYPE_ARM, 15, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_armv7em, llvm::MachO::CPU_TYPE_ARM, 16, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 1, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 0, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 13, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, CPU_ANY,
+ UINT32_MAX, SUBTYPE_MASK},
+ {ArchSpec::eCore_thumb, llvm::MachO::CPU_TYPE_ARM, 0, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_thumbv4t, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_thumbv5, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_thumbv5e, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_thumbv6, llvm::MachO::CPU_TYPE_ARM, 6, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_thumbv6m, llvm::MachO::CPU_TYPE_ARM, 14, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_thumbv7, llvm::MachO::CPU_TYPE_ARM, 9, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_thumbv7f, llvm::MachO::CPU_TYPE_ARM, 10, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_thumbv7s, llvm::MachO::CPU_TYPE_ARM, 11, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_thumbv7k, llvm::MachO::CPU_TYPE_ARM, 12, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_thumbv7m, llvm::MachO::CPU_TYPE_ARM, 15, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_thumbv7em, llvm::MachO::CPU_TYPE_ARM, 16, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_generic, llvm::MachO::CPU_TYPE_POWERPC, CPU_ANY,
+ UINT32_MAX, UINT32_MAX},
+ {ArchSpec::eCore_ppc_generic, llvm::MachO::CPU_TYPE_POWERPC, 0, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc601, llvm::MachO::CPU_TYPE_POWERPC, 1, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc602, llvm::MachO::CPU_TYPE_POWERPC, 2, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc603, llvm::MachO::CPU_TYPE_POWERPC, 3, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc603e, llvm::MachO::CPU_TYPE_POWERPC, 4, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc603ev, llvm::MachO::CPU_TYPE_POWERPC, 5, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc604, llvm::MachO::CPU_TYPE_POWERPC, 6, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc604e, llvm::MachO::CPU_TYPE_POWERPC, 7, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc620, llvm::MachO::CPU_TYPE_POWERPC, 8, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc750, llvm::MachO::CPU_TYPE_POWERPC, 9, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc7400, llvm::MachO::CPU_TYPE_POWERPC, 10, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc7450, llvm::MachO::CPU_TYPE_POWERPC, 11, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc_ppc970, llvm::MachO::CPU_TYPE_POWERPC, 100, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc64_generic, llvm::MachO::CPU_TYPE_POWERPC64, 0,
+ UINT32_MAX, SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc64le_generic, llvm::MachO::CPU_TYPE_POWERPC64, CPU_ANY,
+ UINT32_MAX, SUBTYPE_MASK},
+ {ArchSpec::eCore_ppc64_ppc970_64, llvm::MachO::CPU_TYPE_POWERPC64, 100,
+ UINT32_MAX, SUBTYPE_MASK},
+ {ArchSpec::eCore_x86_32_i386, llvm::MachO::CPU_TYPE_I386, 3, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_x86_32_i486, llvm::MachO::CPU_TYPE_I386, 4, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_x86_32_i486sx, llvm::MachO::CPU_TYPE_I386, 0x84,
+ UINT32_MAX, SUBTYPE_MASK},
+ {ArchSpec::eCore_x86_32_i386, llvm::MachO::CPU_TYPE_I386, CPU_ANY,
+ UINT32_MAX, UINT32_MAX},
+ {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, 3, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, 4, UINT32_MAX,
+ SUBTYPE_MASK},
+ {ArchSpec::eCore_x86_64_x86_64h, llvm::MachO::CPU_TYPE_X86_64, 8,
+ UINT32_MAX, SUBTYPE_MASK},
+ {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, CPU_ANY,
+ UINT32_MAX, UINT32_MAX},
+ // Catch any unknown mach architectures so we can always use the object and
+ // symbol mach-o files
+ {ArchSpec::eCore_uknownMach32, 0, 0, 0xFF000000u, 0x00000000u},
+ {ArchSpec::eCore_uknownMach64, llvm::MachO::CPU_ARCH_ABI64, 0, 0xFF000000u,
+ 0x00000000u}};
+
+static const ArchDefinition g_macho_arch_def = {
+ eArchTypeMachO, llvm::array_lengthof(g_macho_arch_entries),
+ g_macho_arch_entries, "mach-o"};
+
+//===----------------------------------------------------------------------===//
+// A table that gets searched linearly for matches. This table is used to
+// convert cpu type and subtypes to architecture names, and to convert
+// architecture names to cpu types and subtypes. The ordering is important and
+// allows the precedence to be set when the table is built.
+static const ArchDefinitionEntry g_elf_arch_entries[] = {
+ {ArchSpec::eCore_sparc_generic, llvm::ELF::EM_SPARC, LLDB_INVALID_CPUTYPE,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // Sparc
+ {ArchSpec::eCore_x86_32_i386, llvm::ELF::EM_386, LLDB_INVALID_CPUTYPE,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80386
+ {ArchSpec::eCore_x86_32_i486, llvm::ELF::EM_IAMCU, LLDB_INVALID_CPUTYPE,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel MCU // FIXME: is this correct?
+ {ArchSpec::eCore_ppc_generic, llvm::ELF::EM_PPC, LLDB_INVALID_CPUTYPE,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC
+ {ArchSpec::eCore_ppc64le_generic, llvm::ELF::EM_PPC64, LLDB_INVALID_CPUTYPE,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64le
+ {ArchSpec::eCore_ppc64_generic, llvm::ELF::EM_PPC64, LLDB_INVALID_CPUTYPE,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64
+ {ArchSpec::eCore_arm_generic, llvm::ELF::EM_ARM, LLDB_INVALID_CPUTYPE,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM
+ {ArchSpec::eCore_arm_aarch64, llvm::ELF::EM_AARCH64, LLDB_INVALID_CPUTYPE,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM64
+ {ArchSpec::eCore_s390x_generic, llvm::ELF::EM_S390, LLDB_INVALID_CPUTYPE,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // SystemZ
+ {ArchSpec::eCore_sparc9_generic, llvm::ELF::EM_SPARCV9,
+ LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // SPARC V9
+ {ArchSpec::eCore_x86_64_x86_64, llvm::ELF::EM_X86_64, LLDB_INVALID_CPUTYPE,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // AMD64
+ {ArchSpec::eCore_mips32, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32
+ {ArchSpec::eCore_mips32r2, llvm::ELF::EM_MIPS,
+ ArchSpec::eMIPSSubType_mips32r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2
+ {ArchSpec::eCore_mips32r6, llvm::ELF::EM_MIPS,
+ ArchSpec::eMIPSSubType_mips32r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6
+ {ArchSpec::eCore_mips32el, llvm::ELF::EM_MIPS,
+ ArchSpec::eMIPSSubType_mips32el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32el
+ {ArchSpec::eCore_mips32r2el, llvm::ELF::EM_MIPS,
+ ArchSpec::eMIPSSubType_mips32r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2el
+ {ArchSpec::eCore_mips32r6el, llvm::ELF::EM_MIPS,
+ ArchSpec::eMIPSSubType_mips32r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6el
+ {ArchSpec::eCore_mips64, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64,
+ 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64
+ {ArchSpec::eCore_mips64r2, llvm::ELF::EM_MIPS,
+ ArchSpec::eMIPSSubType_mips64r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2
+ {ArchSpec::eCore_mips64r6, llvm::ELF::EM_MIPS,
+ ArchSpec::eMIPSSubType_mips64r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6
+ {ArchSpec::eCore_mips64el, llvm::ELF::EM_MIPS,
+ ArchSpec::eMIPSSubType_mips64el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64el
+ {ArchSpec::eCore_mips64r2el, llvm::ELF::EM_MIPS,
+ ArchSpec::eMIPSSubType_mips64r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2el
+ {ArchSpec::eCore_mips64r6el, llvm::ELF::EM_MIPS,
+ ArchSpec::eMIPSSubType_mips64r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6el
+ {ArchSpec::eCore_hexagon_generic, llvm::ELF::EM_HEXAGON,
+ LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // HEXAGON
+};
+
+static const ArchDefinition g_elf_arch_def = {
+ eArchTypeELF,
+ llvm::array_lengthof(g_elf_arch_entries),
+ g_elf_arch_entries,
+ "elf",
+};
+
+static const ArchDefinitionEntry g_coff_arch_entries[] = {
+ {ArchSpec::eCore_x86_32_i386, llvm::COFF::IMAGE_FILE_MACHINE_I386,
+ LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80x86
+ {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPC,
+ LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC
+ {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP,
+ LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC (with FPU)
+ {ArchSpec::eCore_arm_generic, llvm::COFF::IMAGE_FILE_MACHINE_ARM,
+ LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM
+ {ArchSpec::eCore_arm_armv7, llvm::COFF::IMAGE_FILE_MACHINE_ARMNT,
+ LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7
+ {ArchSpec::eCore_thumb, llvm::COFF::IMAGE_FILE_MACHINE_THUMB,
+ LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7
+ {ArchSpec::eCore_x86_64_x86_64, llvm::COFF::IMAGE_FILE_MACHINE_AMD64,
+ LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu} // AMD64
+};
+
+static const ArchDefinition g_coff_arch_def = {
+ eArchTypeCOFF,
+ llvm::array_lengthof(g_coff_arch_entries),
+ g_coff_arch_entries,
+ "pe-coff",
+};
+
+//===----------------------------------------------------------------------===//
+// Table of all ArchDefinitions
+static const ArchDefinition *g_arch_definitions[] = {
+ &g_macho_arch_def, &g_elf_arch_def, &g_coff_arch_def};
+
+static const size_t k_num_arch_definitions =
+ llvm::array_lengthof(g_arch_definitions);
+
+//===----------------------------------------------------------------------===//
+// Static helper functions.
+
+// Get the architecture definition for a given object type.
+static const ArchDefinition *FindArchDefinition(ArchitectureType arch_type) {
+ for (unsigned int i = 0; i < k_num_arch_definitions; ++i) {
+ const ArchDefinition *def = g_arch_definitions[i];
+ if (def->type == arch_type)
+ return def;
+ }
+ return nullptr;
+}
+
+// Get an architecture definition by name.
+static const CoreDefinition *FindCoreDefinition(llvm::StringRef name) {
+ for (unsigned int i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) {
+ if (name.equals_lower(g_core_definitions[i].name))
+ return &g_core_definitions[i];
+ }
+ return nullptr;
+}
+
+static inline const CoreDefinition *FindCoreDefinition(ArchSpec::Core core) {
+ if (core < llvm::array_lengthof(g_core_definitions))
+ return &g_core_definitions[core];
+ return nullptr;
+}
+
+// Get a definition entry by cpu type and subtype.
+static const ArchDefinitionEntry *
+FindArchDefinitionEntry(const ArchDefinition *def, uint32_t cpu, uint32_t sub) {
+ if (def == nullptr)
+ return nullptr;
+
+ const ArchDefinitionEntry *entries = def->entries;
+ for (size_t i = 0; i < def->num_entries; ++i) {
+ if (entries[i].cpu == (cpu & entries[i].cpu_mask))
+ if (entries[i].sub == (sub & entries[i].sub_mask))
+ return &entries[i];
+ }
+ return nullptr;
+}
+
+static const ArchDefinitionEntry *
+FindArchDefinitionEntry(const ArchDefinition *def, ArchSpec::Core core) {
+ if (def == nullptr)
+ return nullptr;
+
+ const ArchDefinitionEntry *entries = def->entries;
+ for (size_t i = 0; i < def->num_entries; ++i) {
+ if (entries[i].core == core)
+ return &entries[i];
+ }
+ return nullptr;
+}
+
+//===----------------------------------------------------------------------===//
+// Constructors and destructors.
+
+ArchSpec::ArchSpec() {}
+
+ArchSpec::ArchSpec(const char *triple_cstr) {
+ if (triple_cstr)
+ SetTriple(triple_cstr);
+}
+
+ArchSpec::ArchSpec(llvm::StringRef triple_str) { SetTriple(triple_str); }
+
+ArchSpec::ArchSpec(const llvm::Triple &triple) { SetTriple(triple); }
+
+ArchSpec::ArchSpec(ArchitectureType arch_type, uint32_t cpu, uint32_t subtype) {
+ SetArchitecture(arch_type, cpu, subtype);
+}
+
+ArchSpec::~ArchSpec() = default;
+
+//===----------------------------------------------------------------------===//
+// Assignment and initialization.
+
+const ArchSpec &ArchSpec::operator=(const ArchSpec &rhs) {
+ if (this != &rhs) {
+ m_triple = rhs.m_triple;
+ m_core = rhs.m_core;
+ m_byte_order = rhs.m_byte_order;
+ m_distribution_id = rhs.m_distribution_id;
+ m_flags = rhs.m_flags;
+ }
+ return *this;
+}
+
+void ArchSpec::Clear() {
+ m_triple = llvm::Triple();
+ m_core = kCore_invalid;
+ m_byte_order = eByteOrderInvalid;
+ m_distribution_id.Clear();
+ m_flags = 0;
+}
+
+//===----------------------------------------------------------------------===//
+// Predicates.
+
+const char *ArchSpec::GetArchitectureName() const {
+ const CoreDefinition *core_def = FindCoreDefinition(m_core);
+ if (core_def)
+ return core_def->name;
+ return "unknown";
+}
+
+bool ArchSpec::IsMIPS() const { return GetTriple().isMIPS(); }
+
+std::string ArchSpec::GetTargetABI() const {
+
+ std::string abi;
+
+ if (IsMIPS()) {
+ switch (GetFlags() & ArchSpec::eMIPSABI_mask) {
+ case ArchSpec::eMIPSABI_N64:
+ abi = "n64";
+ return abi;
+ case ArchSpec::eMIPSABI_N32:
+ abi = "n32";
+ return abi;
+ case ArchSpec::eMIPSABI_O32:
+ abi = "o32";
+ return abi;
+ default:
+ return abi;
+ }
+ }
+ return abi;
+}
+
+void ArchSpec::SetFlags(std::string elf_abi) {
+
+ uint32_t flag = GetFlags();
+ if (IsMIPS()) {
+ if (elf_abi == "n64")
+ flag |= ArchSpec::eMIPSABI_N64;
+ else if (elf_abi == "n32")
+ flag |= ArchSpec::eMIPSABI_N32;
+ else if (elf_abi == "o32")
+ flag |= ArchSpec::eMIPSABI_O32;
+ }
+ SetFlags(flag);
+}
+
+std::string ArchSpec::GetClangTargetCPU() const {
+ std::string cpu;
+
+ if (IsMIPS()) {
+ switch (m_core) {
+ case ArchSpec::eCore_mips32:
+ case ArchSpec::eCore_mips32el:
+ cpu = "mips32";
+ break;
+ case ArchSpec::eCore_mips32r2:
+ case ArchSpec::eCore_mips32r2el:
+ cpu = "mips32r2";
+ break;
+ case ArchSpec::eCore_mips32r3:
+ case ArchSpec::eCore_mips32r3el:
+ cpu = "mips32r3";
+ break;
+ case ArchSpec::eCore_mips32r5:
+ case ArchSpec::eCore_mips32r5el:
+ cpu = "mips32r5";
+ break;
+ case ArchSpec::eCore_mips32r6:
+ case ArchSpec::eCore_mips32r6el:
+ cpu = "mips32r6";
+ break;
+ case ArchSpec::eCore_mips64:
+ case ArchSpec::eCore_mips64el:
+ cpu = "mips64";
+ break;
+ case ArchSpec::eCore_mips64r2:
+ case ArchSpec::eCore_mips64r2el:
+ cpu = "mips64r2";
+ break;
+ case ArchSpec::eCore_mips64r3:
+ case ArchSpec::eCore_mips64r3el:
+ cpu = "mips64r3";
+ break;
+ case ArchSpec::eCore_mips64r5:
+ case ArchSpec::eCore_mips64r5el:
+ cpu = "mips64r5";
+ break;
+ case ArchSpec::eCore_mips64r6:
+ case ArchSpec::eCore_mips64r6el:
+ cpu = "mips64r6";
+ break;
+ default:
+ break;
+ }
+ }
+ return cpu;
+}
+
+uint32_t ArchSpec::GetMachOCPUType() const {
+ const CoreDefinition *core_def = FindCoreDefinition(m_core);
+ if (core_def) {
+ const ArchDefinitionEntry *arch_def =
+ FindArchDefinitionEntry(&g_macho_arch_def, core_def->core);
+ if (arch_def) {
+ return arch_def->cpu;
+ }
+ }
+ return LLDB_INVALID_CPUTYPE;
+}
+
+uint32_t ArchSpec::GetMachOCPUSubType() const {
+ const CoreDefinition *core_def = FindCoreDefinition(m_core);
+ if (core_def) {
+ const ArchDefinitionEntry *arch_def =
+ FindArchDefinitionEntry(&g_macho_arch_def, core_def->core);
+ if (arch_def) {
+ return arch_def->sub;
+ }
+ }
+ return LLDB_INVALID_CPUTYPE;
+}
+
+uint32_t ArchSpec::GetDataByteSize() const {
+ return 1;
+}
+
+uint32_t ArchSpec::GetCodeByteSize() const {
+ return 1;
+}
+
+llvm::Triple::ArchType ArchSpec::GetMachine() const {
+ const CoreDefinition *core_def = FindCoreDefinition(m_core);
+ if (core_def)
+ return core_def->machine;
+
+ return llvm::Triple::UnknownArch;
+}
+
+ConstString ArchSpec::GetDistributionId() const {
+ return m_distribution_id;
+}
+
+void ArchSpec::SetDistributionId(const char *distribution_id) {
+ m_distribution_id.SetCString(distribution_id);
+}
+
+uint32_t ArchSpec::GetAddressByteSize() const {
+ const CoreDefinition *core_def = FindCoreDefinition(m_core);
+ if (core_def) {
+ if (core_def->machine == llvm::Triple::mips64 ||
+ core_def->machine == llvm::Triple::mips64el) {
+ // For N32/O32 applications Address size is 4 bytes.
+ if (m_flags & (eMIPSABI_N32 | eMIPSABI_O32))
+ return 4;
+ }
+ return core_def->addr_byte_size;
+ }
+ return 0;
+}
+
+ByteOrder ArchSpec::GetDefaultEndian() const {
+ const CoreDefinition *core_def = FindCoreDefinition(m_core);
+ if (core_def)
+ return core_def->default_byte_order;
+ return eByteOrderInvalid;
+}
+
+bool ArchSpec::CharIsSignedByDefault() const {
+ switch (m_triple.getArch()) {
+ default:
+ return true;
+
+ case llvm::Triple::aarch64:
+ case llvm::Triple::aarch64_be:
+ case llvm::Triple::arm:
+ case llvm::Triple::armeb:
+ case llvm::Triple::thumb:
+ case llvm::Triple::thumbeb:
+ return m_triple.isOSDarwin() || m_triple.isOSWindows();
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ return m_triple.isOSDarwin();
+
+ case llvm::Triple::ppc64le:
+ case llvm::Triple::systemz:
+ case llvm::Triple::xcore:
+ case llvm::Triple::arc:
+ return false;
+ }
+}
+
+lldb::ByteOrder ArchSpec::GetByteOrder() const {
+ if (m_byte_order == eByteOrderInvalid)
+ return GetDefaultEndian();
+ return m_byte_order;
+}
+
+//===----------------------------------------------------------------------===//
+// Mutators.
+
+bool ArchSpec::SetTriple(const llvm::Triple &triple) {
+ m_triple = triple;
+ UpdateCore();
+ return IsValid();
+}
+
+bool lldb_private::ParseMachCPUDashSubtypeTriple(llvm::StringRef triple_str,
+ ArchSpec &arch) {
+ // Accept "12-10" or "12.10" as cpu type/subtype
+ if (triple_str.empty())
+ return false;
+
+ size_t pos = triple_str.find_first_of("-.");
+ if (pos == llvm::StringRef::npos)
+ return false;
+
+ llvm::StringRef cpu_str = triple_str.substr(0, pos);
+ llvm::StringRef remainder = triple_str.substr(pos + 1);
+ if (cpu_str.empty() || remainder.empty())
+ return false;
+
+ llvm::StringRef sub_str;
+ llvm::StringRef vendor;
+ llvm::StringRef os;
+ std::tie(sub_str, remainder) = remainder.split('-');
+ std::tie(vendor, os) = remainder.split('-');
+
+ uint32_t cpu = 0;
+ uint32_t sub = 0;
+ if (cpu_str.getAsInteger(10, cpu) || sub_str.getAsInteger(10, sub))
+ return false;
+
+ if (!arch.SetArchitecture(eArchTypeMachO, cpu, sub))
+ return false;
+ if (!vendor.empty() && !os.empty()) {
+ arch.GetTriple().setVendorName(vendor);
+ arch.GetTriple().setOSName(os);
+ }
+
+ return true;
+}
+
+bool ArchSpec::SetTriple(llvm::StringRef triple) {
+ if (triple.empty()) {
+ Clear();
+ return false;
+ }
+
+ if (ParseMachCPUDashSubtypeTriple(triple, *this))
+ return true;
+
+ SetTriple(llvm::Triple(llvm::Triple::normalize(triple)));
+ return IsValid();
+}
+
+bool ArchSpec::ContainsOnlyArch(const llvm::Triple &normalized_triple) {
+ return !normalized_triple.getArchName().empty() &&
+ normalized_triple.getOSName().empty() &&
+ normalized_triple.getVendorName().empty() &&
+ normalized_triple.getEnvironmentName().empty();
+}
+
+void ArchSpec::MergeFrom(const ArchSpec &other) {
+ if (!TripleVendorWasSpecified() && other.TripleVendorWasSpecified())
+ GetTriple().setVendor(other.GetTriple().getVendor());
+ if (!TripleOSWasSpecified() && other.TripleOSWasSpecified())
+ GetTriple().setOS(other.GetTriple().getOS());
+ if (GetTriple().getArch() == llvm::Triple::UnknownArch) {
+ GetTriple().setArch(other.GetTriple().getArch());
+
+ // MachO unknown64 isn't really invalid as the debugger can still obtain
+ // information from the binary, e.g. line tables. As such, we don't update
+ // the core here.
+ if (other.GetCore() != eCore_uknownMach64)
+ UpdateCore();
+ }
+ if (!TripleEnvironmentWasSpecified() &&
+ other.TripleEnvironmentWasSpecified()) {
+ GetTriple().setEnvironment(other.GetTriple().getEnvironment());
+ }
+ // If this and other are both arm ArchSpecs and this ArchSpec is a generic
+ // "some kind of arm" spec but the other ArchSpec is a specific arm core,
+ // adopt the specific arm core.
+ if (GetTriple().getArch() == llvm::Triple::arm &&
+ other.GetTriple().getArch() == llvm::Triple::arm &&
+ IsCompatibleMatch(other) && GetCore() == ArchSpec::eCore_arm_generic &&
+ other.GetCore() != ArchSpec::eCore_arm_generic) {
+ m_core = other.GetCore();
+ CoreUpdated(true);
+ }
+ if (GetFlags() == 0) {
+ SetFlags(other.GetFlags());
+ }
+}
+
+bool ArchSpec::SetArchitecture(ArchitectureType arch_type, uint32_t cpu,
+ uint32_t sub, uint32_t os) {
+ m_core = kCore_invalid;
+ bool update_triple = true;
+ const ArchDefinition *arch_def = FindArchDefinition(arch_type);
+ if (arch_def) {
+ const ArchDefinitionEntry *arch_def_entry =
+ FindArchDefinitionEntry(arch_def, cpu, sub);
+ if (arch_def_entry) {
+ const CoreDefinition *core_def = FindCoreDefinition(arch_def_entry->core);
+ if (core_def) {
+ m_core = core_def->core;
+ update_triple = false;
+ // Always use the architecture name because it might be more
+ // descriptive than the architecture enum ("armv7" ->
+ // llvm::Triple::arm).
+ m_triple.setArchName(llvm::StringRef(core_def->name));
+ if (arch_type == eArchTypeMachO) {
+ m_triple.setVendor(llvm::Triple::Apple);
+
+ // Don't set the OS. It could be simulator, macosx, ios, watchos,
+ // tvos, bridgeos. We could get close with the cpu type - but we
+ // can't get it right all of the time. Better to leave this unset
+ // so other sections of code will set it when they have more
+ // information. NB: don't call m_triple.setOS
+ // (llvm::Triple::UnknownOS). That sets the OSName to "unknown" and
+ // the ArchSpec::TripleVendorWasSpecified() method says that any
+ // OSName setting means it was specified.
+ } else if (arch_type == eArchTypeELF) {
+ switch (os) {
+ case llvm::ELF::ELFOSABI_AIX:
+ m_triple.setOS(llvm::Triple::OSType::AIX);
+ break;
+ case llvm::ELF::ELFOSABI_FREEBSD:
+ m_triple.setOS(llvm::Triple::OSType::FreeBSD);
+ break;
+ case llvm::ELF::ELFOSABI_GNU:
+ m_triple.setOS(llvm::Triple::OSType::Linux);
+ break;
+ case llvm::ELF::ELFOSABI_NETBSD:
+ m_triple.setOS(llvm::Triple::OSType::NetBSD);
+ break;
+ case llvm::ELF::ELFOSABI_OPENBSD:
+ m_triple.setOS(llvm::Triple::OSType::OpenBSD);
+ break;
+ case llvm::ELF::ELFOSABI_SOLARIS:
+ m_triple.setOS(llvm::Triple::OSType::Solaris);
+ break;
+ }
+ } else if (arch_type == eArchTypeCOFF && os == llvm::Triple::Win32) {
+ m_triple.setVendor(llvm::Triple::PC);
+ m_triple.setOS(llvm::Triple::Win32);
+ } else {
+ m_triple.setVendor(llvm::Triple::UnknownVendor);
+ m_triple.setOS(llvm::Triple::UnknownOS);
+ }
+ // Fall back onto setting the machine type if the arch by name
+ // failed...
+ if (m_triple.getArch() == llvm::Triple::UnknownArch)
+ m_triple.setArch(core_def->machine);
+ }
+ } else {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET | LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_PLATFORM));
+ if (log)
+ log->Printf("Unable to find a core definition for cpu 0x%" PRIx32 " sub %" PRId32, cpu, sub);
+ }
+ }
+ CoreUpdated(update_triple);
+ return IsValid();
+}
+
+uint32_t ArchSpec::GetMinimumOpcodeByteSize() const {
+ const CoreDefinition *core_def = FindCoreDefinition(m_core);
+ if (core_def)
+ return core_def->min_opcode_byte_size;
+ return 0;
+}
+
+uint32_t ArchSpec::GetMaximumOpcodeByteSize() const {
+ const CoreDefinition *core_def = FindCoreDefinition(m_core);
+ if (core_def)
+ return core_def->max_opcode_byte_size;
+ return 0;
+}
+
+bool ArchSpec::IsExactMatch(const ArchSpec &rhs) const {
+ return IsEqualTo(rhs, true);
+}
+
+bool ArchSpec::IsCompatibleMatch(const ArchSpec &rhs) const {
+ return IsEqualTo(rhs, false);
+}
+
+static bool IsCompatibleEnvironment(llvm::Triple::EnvironmentType lhs,
+ llvm::Triple::EnvironmentType rhs) {
+ if (lhs == rhs)
+ return true;
+
+ // If any of the environment is unknown then they are compatible
+ if (lhs == llvm::Triple::UnknownEnvironment ||
+ rhs == llvm::Triple::UnknownEnvironment)
+ return true;
+
+ // If one of the environment is Android and the other one is EABI then they
+ // are considered to be compatible. This is required as a workaround for
+ // shared libraries compiled for Android without the NOTE section indicating
+ // that they are using the Android ABI.
+ if ((lhs == llvm::Triple::Android && rhs == llvm::Triple::EABI) ||
+ (rhs == llvm::Triple::Android && lhs == llvm::Triple::EABI) ||
+ (lhs == llvm::Triple::GNUEABI && rhs == llvm::Triple::EABI) ||
+ (rhs == llvm::Triple::GNUEABI && lhs == llvm::Triple::EABI) ||
+ (lhs == llvm::Triple::GNUEABIHF && rhs == llvm::Triple::EABIHF) ||
+ (rhs == llvm::Triple::GNUEABIHF && lhs == llvm::Triple::EABIHF))
+ return true;
+
+ return false;
+}
+
+bool ArchSpec::IsEqualTo(const ArchSpec &rhs, bool exact_match) const {
+ // explicitly ignoring m_distribution_id in this method.
+
+ if (GetByteOrder() != rhs.GetByteOrder())
+ return false;
+
+ const ArchSpec::Core lhs_core = GetCore();
+ const ArchSpec::Core rhs_core = rhs.GetCore();
+
+ const bool core_match = cores_match(lhs_core, rhs_core, true, exact_match);
+
+ if (core_match) {
+ const llvm::Triple &lhs_triple = GetTriple();
+ const llvm::Triple &rhs_triple = rhs.GetTriple();
+
+ const llvm::Triple::VendorType lhs_triple_vendor = lhs_triple.getVendor();
+ const llvm::Triple::VendorType rhs_triple_vendor = rhs_triple.getVendor();
+ if (lhs_triple_vendor != rhs_triple_vendor) {
+ const bool rhs_vendor_specified = rhs.TripleVendorWasSpecified();
+ const bool lhs_vendor_specified = TripleVendorWasSpecified();
+ // Both architectures had the vendor specified, so if they aren't equal
+ // then we return false
+ if (rhs_vendor_specified && lhs_vendor_specified)
+ return false;
+
+ // Only fail if both vendor types are not unknown
+ if (lhs_triple_vendor != llvm::Triple::UnknownVendor &&
+ rhs_triple_vendor != llvm::Triple::UnknownVendor)
+ return false;
+ }
+
+ const llvm::Triple::OSType lhs_triple_os = lhs_triple.getOS();
+ const llvm::Triple::OSType rhs_triple_os = rhs_triple.getOS();
+ if (lhs_triple_os != rhs_triple_os) {
+ const bool rhs_os_specified = rhs.TripleOSWasSpecified();
+ const bool lhs_os_specified = TripleOSWasSpecified();
+ // Both architectures had the OS specified, so if they aren't equal then
+ // we return false
+ if (rhs_os_specified && lhs_os_specified)
+ return false;
+
+ // Only fail if both os types are not unknown
+ if (lhs_triple_os != llvm::Triple::UnknownOS &&
+ rhs_triple_os != llvm::Triple::UnknownOS)
+ return false;
+ }
+
+ const llvm::Triple::EnvironmentType lhs_triple_env =
+ lhs_triple.getEnvironment();
+ const llvm::Triple::EnvironmentType rhs_triple_env =
+ rhs_triple.getEnvironment();
+
+ return IsCompatibleEnvironment(lhs_triple_env, rhs_triple_env);
+ }
+ return false;
+}
+
+void ArchSpec::UpdateCore() {
+ llvm::StringRef arch_name(m_triple.getArchName());
+ const CoreDefinition *core_def = FindCoreDefinition(arch_name);
+ if (core_def) {
+ m_core = core_def->core;
+ // Set the byte order to the default byte order for an architecture. This
+ // can be modified if needed for cases when cores handle both big and
+ // little endian
+ m_byte_order = core_def->default_byte_order;
+ } else {
+ Clear();
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Helper methods.
+
+void ArchSpec::CoreUpdated(bool update_triple) {
+ const CoreDefinition *core_def = FindCoreDefinition(m_core);
+ if (core_def) {
+ if (update_triple)
+ m_triple = llvm::Triple(core_def->name, "unknown", "unknown");
+ m_byte_order = core_def->default_byte_order;
+ } else {
+ if (update_triple)
+ m_triple = llvm::Triple();
+ m_byte_order = eByteOrderInvalid;
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Operators.
+
+static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2,
+ bool try_inverse, bool enforce_exact_match) {
+ if (core1 == core2)
+ return true;
+
+ switch (core1) {
+ case ArchSpec::kCore_any:
+ return true;
+
+ case ArchSpec::eCore_arm_generic:
+ if (enforce_exact_match)
+ break;
+ LLVM_FALLTHROUGH;
+ case ArchSpec::kCore_arm_any:
+ if (core2 >= ArchSpec::kCore_arm_first && core2 <= ArchSpec::kCore_arm_last)
+ return true;
+ if (core2 >= ArchSpec::kCore_thumb_first &&
+ core2 <= ArchSpec::kCore_thumb_last)
+ return true;
+ if (core2 == ArchSpec::kCore_arm_any)
+ return true;
+ break;
+
+ case ArchSpec::kCore_x86_32_any:
+ if ((core2 >= ArchSpec::kCore_x86_32_first &&
+ core2 <= ArchSpec::kCore_x86_32_last) ||
+ (core2 == ArchSpec::kCore_x86_32_any))
+ return true;
+ break;
+
+ case ArchSpec::kCore_x86_64_any:
+ if ((core2 >= ArchSpec::kCore_x86_64_first &&
+ core2 <= ArchSpec::kCore_x86_64_last) ||
+ (core2 == ArchSpec::kCore_x86_64_any))
+ return true;
+ break;
+
+ case ArchSpec::kCore_ppc_any:
+ if ((core2 >= ArchSpec::kCore_ppc_first &&
+ core2 <= ArchSpec::kCore_ppc_last) ||
+ (core2 == ArchSpec::kCore_ppc_any))
+ return true;
+ break;
+
+ case ArchSpec::kCore_ppc64_any:
+ if ((core2 >= ArchSpec::kCore_ppc64_first &&
+ core2 <= ArchSpec::kCore_ppc64_last) ||
+ (core2 == ArchSpec::kCore_ppc64_any))
+ return true;
+ break;
+
+ case ArchSpec::eCore_arm_armv6m:
+ if (!enforce_exact_match) {
+ if (core2 == ArchSpec::eCore_arm_generic)
+ return true;
+ try_inverse = false;
+ if (core2 == ArchSpec::eCore_arm_armv7)
+ return true;
+ if (core2 == ArchSpec::eCore_arm_armv6m)
+ return true;
+ }
+ break;
+
+ case ArchSpec::kCore_hexagon_any:
+ if ((core2 >= ArchSpec::kCore_hexagon_first &&
+ core2 <= ArchSpec::kCore_hexagon_last) ||
+ (core2 == ArchSpec::kCore_hexagon_any))
+ return true;
+ break;
+
+ // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization
+ // Cortex-M0 - ARMv6-M - armv6m Cortex-M3 - ARMv7-M - armv7m Cortex-M4 -
+ // ARMv7E-M - armv7em
+ case ArchSpec::eCore_arm_armv7em:
+ if (!enforce_exact_match) {
+ if (core2 == ArchSpec::eCore_arm_generic)
+ return true;
+ if (core2 == ArchSpec::eCore_arm_armv7m)
+ return true;
+ if (core2 == ArchSpec::eCore_arm_armv6m)
+ return true;
+ if (core2 == ArchSpec::eCore_arm_armv7)
+ return true;
+ try_inverse = true;
+ }
+ break;
+
+ // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization
+ // Cortex-M0 - ARMv6-M - armv6m Cortex-M3 - ARMv7-M - armv7m Cortex-M4 -
+ // ARMv7E-M - armv7em
+ case ArchSpec::eCore_arm_armv7m:
+ if (!enforce_exact_match) {
+ if (core2 == ArchSpec::eCore_arm_generic)
+ return true;
+ if (core2 == ArchSpec::eCore_arm_armv6m)
+ return true;
+ if (core2 == ArchSpec::eCore_arm_armv7)
+ return true;
+ if (core2 == ArchSpec::eCore_arm_armv7em)
+ return true;
+ try_inverse = true;
+ }
+ break;
+
+ case ArchSpec::eCore_arm_armv7f:
+ case ArchSpec::eCore_arm_armv7k:
+ case ArchSpec::eCore_arm_armv7s:
+ if (!enforce_exact_match) {
+ if (core2 == ArchSpec::eCore_arm_generic)
+ return true;
+ if (core2 == ArchSpec::eCore_arm_armv7)
+ return true;
+ try_inverse = false;
+ }
+ break;
+
+ case ArchSpec::eCore_x86_64_x86_64h:
+ if (!enforce_exact_match) {
+ try_inverse = false;
+ if (core2 == ArchSpec::eCore_x86_64_x86_64)
+ return true;
+ }
+ break;
+
+ case ArchSpec::eCore_arm_armv8:
+ if (!enforce_exact_match) {
+ if (core2 == ArchSpec::eCore_arm_arm64)
+ return true;
+ if (core2 == ArchSpec::eCore_arm_aarch64)
+ return true;
+ try_inverse = false;
+ }
+ break;
+
+ case ArchSpec::eCore_arm_aarch64:
+ if (!enforce_exact_match) {
+ if (core2 == ArchSpec::eCore_arm_arm64)
+ return true;
+ if (core2 == ArchSpec::eCore_arm_armv8)
+ return true;
+ try_inverse = false;
+ }
+ break;
+
+ case ArchSpec::eCore_arm_arm64:
+ if (!enforce_exact_match) {
+ if (core2 == ArchSpec::eCore_arm_aarch64)
+ return true;
+ if (core2 == ArchSpec::eCore_arm_armv8)
+ return true;
+ try_inverse = false;
+ }
+ break;
+
+ case ArchSpec::eCore_mips32:
+ if (!enforce_exact_match) {
+ if (core2 >= ArchSpec::kCore_mips32_first &&
+ core2 <= ArchSpec::kCore_mips32_last)
+ return true;
+ try_inverse = false;
+ }
+ break;
+
+ case ArchSpec::eCore_mips32el:
+ if (!enforce_exact_match) {
+ if (core2 >= ArchSpec::kCore_mips32el_first &&
+ core2 <= ArchSpec::kCore_mips32el_last)
+ return true;
+ try_inverse = true;
+ }
+ break;
+
+ case ArchSpec::eCore_mips64:
+ if (!enforce_exact_match) {
+ if (core2 >= ArchSpec::kCore_mips32_first &&
+ core2 <= ArchSpec::kCore_mips32_last)
+ return true;
+ if (core2 >= ArchSpec::kCore_mips64_first &&
+ core2 <= ArchSpec::kCore_mips64_last)
+ return true;
+ try_inverse = false;
+ }
+ break;
+
+ case ArchSpec::eCore_mips64el:
+ if (!enforce_exact_match) {
+ if (core2 >= ArchSpec::kCore_mips32el_first &&
+ core2 <= ArchSpec::kCore_mips32el_last)
+ return true;
+ if (core2 >= ArchSpec::kCore_mips64el_first &&
+ core2 <= ArchSpec::kCore_mips64el_last)
+ return true;
+ try_inverse = false;
+ }
+ break;
+
+ case ArchSpec::eCore_mips64r2:
+ case ArchSpec::eCore_mips64r3:
+ case ArchSpec::eCore_mips64r5:
+ if (!enforce_exact_match) {
+ if (core2 >= ArchSpec::kCore_mips32_first && core2 <= (core1 - 10))
+ return true;
+ if (core2 >= ArchSpec::kCore_mips64_first && core2 <= (core1 - 1))
+ return true;
+ try_inverse = false;
+ }
+ break;
+
+ case ArchSpec::eCore_mips64r2el:
+ case ArchSpec::eCore_mips64r3el:
+ case ArchSpec::eCore_mips64r5el:
+ if (!enforce_exact_match) {
+ if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= (core1 - 10))
+ return true;
+ if (core2 >= ArchSpec::kCore_mips64el_first && core2 <= (core1 - 1))
+ return true;
+ try_inverse = false;
+ }
+ break;
+
+ case ArchSpec::eCore_mips32r2:
+ case ArchSpec::eCore_mips32r3:
+ case ArchSpec::eCore_mips32r5:
+ if (!enforce_exact_match) {
+ if (core2 >= ArchSpec::kCore_mips32_first && core2 <= core1)
+ return true;
+ }
+ break;
+
+ case ArchSpec::eCore_mips32r2el:
+ case ArchSpec::eCore_mips32r3el:
+ case ArchSpec::eCore_mips32r5el:
+ if (!enforce_exact_match) {
+ if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= core1)
+ return true;
+ }
+ break;
+
+ case ArchSpec::eCore_mips32r6:
+ if (!enforce_exact_match) {
+ if (core2 == ArchSpec::eCore_mips32 || core2 == ArchSpec::eCore_mips32r6)
+ return true;
+ }
+ break;
+
+ case ArchSpec::eCore_mips32r6el:
+ if (!enforce_exact_match) {
+ if (core2 == ArchSpec::eCore_mips32el ||
+ core2 == ArchSpec::eCore_mips32r6el)
+ return true;
+ }
+ break;
+
+ case ArchSpec::eCore_mips64r6:
+ if (!enforce_exact_match) {
+ if (core2 == ArchSpec::eCore_mips32 || core2 == ArchSpec::eCore_mips32r6)
+ return true;
+ if (core2 == ArchSpec::eCore_mips64 || core2 == ArchSpec::eCore_mips64r6)
+ return true;
+ }
+ break;
+
+ case ArchSpec::eCore_mips64r6el:
+ if (!enforce_exact_match) {
+ if (core2 == ArchSpec::eCore_mips32el ||
+ core2 == ArchSpec::eCore_mips32r6el)
+ return true;
+ if (core2 == ArchSpec::eCore_mips64el ||
+ core2 == ArchSpec::eCore_mips64r6el)
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (try_inverse)
+ return cores_match(core2, core1, false, enforce_exact_match);
+ return false;
+}
+
+bool lldb_private::operator<(const ArchSpec &lhs, const ArchSpec &rhs) {
+ const ArchSpec::Core lhs_core = lhs.GetCore();
+ const ArchSpec::Core rhs_core = rhs.GetCore();
+ return lhs_core < rhs_core;
+}
+
+
+bool lldb_private::operator==(const ArchSpec &lhs, const ArchSpec &rhs) {
+ return lhs.GetCore() == rhs.GetCore();
+}
+
+bool ArchSpec::IsFullySpecifiedTriple() const {
+ const auto &user_specified_triple = GetTriple();
+
+ bool user_triple_fully_specified = false;
+
+ if ((user_specified_triple.getOS() != llvm::Triple::UnknownOS) ||
+ TripleOSWasSpecified()) {
+ if ((user_specified_triple.getVendor() != llvm::Triple::UnknownVendor) ||
+ TripleVendorWasSpecified()) {
+ const unsigned unspecified = 0;
+ if (user_specified_triple.getOSMajorVersion() != unspecified) {
+ user_triple_fully_specified = true;
+ }
+ }
+ }
+
+ return user_triple_fully_specified;
+}
+
+void ArchSpec::PiecewiseTripleCompare(
+ const ArchSpec &other, bool &arch_different, bool &vendor_different,
+ bool &os_different, bool &os_version_different, bool &env_different) const {
+ const llvm::Triple &me(GetTriple());
+ const llvm::Triple &them(other.GetTriple());
+
+ arch_different = (me.getArch() != them.getArch());
+
+ vendor_different = (me.getVendor() != them.getVendor());
+
+ os_different = (me.getOS() != them.getOS());
+
+ os_version_different = (me.getOSMajorVersion() != them.getOSMajorVersion());
+
+ env_different = (me.getEnvironment() != them.getEnvironment());
+}
+
+bool ArchSpec::IsAlwaysThumbInstructions() const {
+ std::string Status;
+ if (GetTriple().getArch() == llvm::Triple::arm ||
+ GetTriple().getArch() == llvm::Triple::thumb) {
+ // v. https://en.wikipedia.org/wiki/ARM_Cortex-M
+ //
+ // Cortex-M0 through Cortex-M7 are ARM processor cores which can only
+ // execute thumb instructions. We map the cores to arch names like this:
+ //
+ // Cortex-M0, Cortex-M0+, Cortex-M1: armv6m Cortex-M3: armv7m Cortex-M4,
+ // Cortex-M7: armv7em
+
+ if (GetCore() == ArchSpec::Core::eCore_arm_armv7m ||
+ GetCore() == ArchSpec::Core::eCore_arm_armv7em ||
+ GetCore() == ArchSpec::Core::eCore_arm_armv6m ||
+ GetCore() == ArchSpec::Core::eCore_thumbv7m ||
+ GetCore() == ArchSpec::Core::eCore_thumbv7em ||
+ GetCore() == ArchSpec::Core::eCore_thumbv6m) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void ArchSpec::DumpTriple(Stream &s) const {
+ const llvm::Triple &triple = GetTriple();
+ llvm::StringRef arch_str = triple.getArchName();
+ llvm::StringRef vendor_str = triple.getVendorName();
+ llvm::StringRef os_str = triple.getOSName();
+ llvm::StringRef environ_str = triple.getEnvironmentName();
+
+ s.Printf("%s-%s-%s", arch_str.empty() ? "*" : arch_str.str().c_str(),
+ vendor_str.empty() ? "*" : vendor_str.str().c_str(),
+ os_str.empty() ? "*" : os_str.str().c_str());
+
+ if (!environ_str.empty())
+ s.Printf("-%s", environ_str.str().c_str());
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Args.cpp b/contrib/llvm-project/lldb/source/Utility/Args.cpp
new file mode 100644
index 000000000000..77b0d43254a1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Args.cpp
@@ -0,0 +1,723 @@
+//===-- Args.cpp ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+#include "llvm/ADT/StringSwitch.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// A helper function for argument parsing.
+// Parses the initial part of the first argument using normal double quote
+// rules: backslash escapes the double quote and itself. The parsed string is
+// appended to the second argument. The function returns the unparsed portion
+// of the string, starting at the closing quote.
+static llvm::StringRef ParseDoubleQuotes(llvm::StringRef quoted,
+ std::string &result) {
+ // Inside double quotes, '\' and '"' are special.
+ static const char *k_escapable_characters = "\"\\";
+ while (true) {
+ // Skip over over regular characters and append them.
+ size_t regular = quoted.find_first_of(k_escapable_characters);
+ result += quoted.substr(0, regular);
+ quoted = quoted.substr(regular);
+
+ // If we have reached the end of string or the closing quote, we're done.
+ if (quoted.empty() || quoted.front() == '"')
+ break;
+
+ // We have found a backslash.
+ quoted = quoted.drop_front();
+
+ if (quoted.empty()) {
+ // A lone backslash at the end of string, let's just append it.
+ result += '\\';
+ break;
+ }
+
+ // If the character after the backslash is not a whitelisted escapable
+ // character, we leave the character sequence untouched.
+ if (strchr(k_escapable_characters, quoted.front()) == nullptr)
+ result += '\\';
+
+ result += quoted.front();
+ quoted = quoted.drop_front();
+ }
+
+ return quoted;
+}
+
+static size_t ArgvToArgc(const char **argv) {
+ if (!argv)
+ return 0;
+ size_t count = 0;
+ while (*argv++)
+ ++count;
+ return count;
+}
+
+// Trims all whitespace that can separate command line arguments from the left
+// side of the string.
+static llvm::StringRef ltrimForArgs(llvm::StringRef str) {
+ static const char *k_space_separators = " \t";
+ return str.ltrim(k_space_separators);
+}
+
+// A helper function for SetCommandString. Parses a single argument from the
+// command string, processing quotes and backslashes in a shell-like manner.
+// The function returns a tuple consisting of the parsed argument, the quote
+// char used, and the unparsed portion of the string starting at the first
+// unqouted, unescaped whitespace character.
+static std::tuple<std::string, char, llvm::StringRef>
+ParseSingleArgument(llvm::StringRef command) {
+ // Argument can be split into multiple discontiguous pieces, for example:
+ // "Hello ""World"
+ // this would result in a single argument "Hello World" (without the quotes)
+ // since the quotes would be removed and there is not space between the
+ // strings.
+ std::string arg;
+
+ // Since we can have multiple quotes that form a single command in a command
+ // like: "Hello "world'!' (which will make a single argument "Hello world!")
+ // we remember the first quote character we encounter and use that for the
+ // quote character.
+ char first_quote_char = '\0';
+
+ bool arg_complete = false;
+ do {
+ // Skip over over regular characters and append them.
+ size_t regular = command.find_first_of(" \t\r\"'`\\");
+ arg += command.substr(0, regular);
+ command = command.substr(regular);
+
+ if (command.empty())
+ break;
+
+ char special = command.front();
+ command = command.drop_front();
+ switch (special) {
+ case '\\':
+ if (command.empty()) {
+ arg += '\\';
+ break;
+ }
+
+ // If the character after the backslash is not a whitelisted escapable
+ // character, we leave the character sequence untouched.
+ if (strchr(" \t\\'\"`", command.front()) == nullptr)
+ arg += '\\';
+
+ arg += command.front();
+ command = command.drop_front();
+
+ break;
+
+ case ' ':
+ case '\t':
+ case '\r':
+ // We are not inside any quotes, we just found a space after an argument.
+ // We are done.
+ arg_complete = true;
+ break;
+
+ case '"':
+ case '\'':
+ case '`':
+ // We found the start of a quote scope.
+ if (first_quote_char == '\0')
+ first_quote_char = special;
+
+ if (special == '"')
+ command = ParseDoubleQuotes(command, arg);
+ else {
+ // For single quotes, we simply skip ahead to the matching quote
+ // character (or the end of the string).
+ size_t quoted = command.find(special);
+ arg += command.substr(0, quoted);
+ command = command.substr(quoted);
+ }
+
+ // If we found a closing quote, skip it.
+ if (!command.empty())
+ command = command.drop_front();
+
+ break;
+ }
+ } while (!arg_complete);
+
+ return std::make_tuple(arg, first_quote_char, command);
+}
+
+Args::ArgEntry::ArgEntry(llvm::StringRef str, char quote) : quote(quote) {
+ size_t size = str.size();
+ ptr.reset(new char[size + 1]);
+
+ ::memcpy(data(), str.data() ? str.data() : "", size);
+ ptr[size] = 0;
+ ref = llvm::StringRef(c_str(), size);
+}
+
+// Args constructor
+Args::Args(llvm::StringRef command) { SetCommandString(command); }
+
+Args::Args(const Args &rhs) { *this = rhs; }
+
+Args::Args(const StringList &list) : Args() {
+ for (size_t i = 0; i < list.GetSize(); ++i)
+ AppendArgument(list[i]);
+}
+
+Args &Args::operator=(const Args &rhs) {
+ Clear();
+
+ m_argv.clear();
+ m_entries.clear();
+ for (auto &entry : rhs.m_entries) {
+ m_entries.emplace_back(entry.ref, entry.quote);
+ m_argv.push_back(m_entries.back().data());
+ }
+ m_argv.push_back(nullptr);
+ return *this;
+}
+
+// Destructor
+Args::~Args() {}
+
+void Args::Dump(Stream &s, const char *label_name) const {
+ if (!label_name)
+ return;
+
+ int i = 0;
+ for (auto &entry : m_entries) {
+ s.Indent();
+ s.Format("{0}[{1}]=\"{2}\"\n", label_name, i++, entry.ref);
+ }
+ s.Format("{0}[{1}]=NULL\n", label_name, i);
+ s.EOL();
+}
+
+bool Args::GetCommandString(std::string &command) const {
+ command.clear();
+
+ for (size_t i = 0; i < m_entries.size(); ++i) {
+ if (i > 0)
+ command += ' ';
+ command += m_entries[i].ref;
+ }
+
+ return !m_entries.empty();
+}
+
+bool Args::GetQuotedCommandString(std::string &command) const {
+ command.clear();
+
+ for (size_t i = 0; i < m_entries.size(); ++i) {
+ if (i > 0)
+ command += ' ';
+
+ if (m_entries[i].quote) {
+ command += m_entries[i].quote;
+ command += m_entries[i].ref;
+ command += m_entries[i].quote;
+ } else {
+ command += m_entries[i].ref;
+ }
+ }
+
+ return !m_entries.empty();
+}
+
+void Args::SetCommandString(llvm::StringRef command) {
+ Clear();
+ m_argv.clear();
+
+ command = ltrimForArgs(command);
+ std::string arg;
+ char quote;
+ while (!command.empty()) {
+ std::tie(arg, quote, command) = ParseSingleArgument(command);
+ m_entries.emplace_back(arg, quote);
+ m_argv.push_back(m_entries.back().data());
+ command = ltrimForArgs(command);
+ }
+ m_argv.push_back(nullptr);
+}
+
+size_t Args::GetArgumentCount() const { return m_entries.size(); }
+
+const char *Args::GetArgumentAtIndex(size_t idx) const {
+ if (idx < m_argv.size())
+ return m_argv[idx];
+ return nullptr;
+}
+
+char Args::GetArgumentQuoteCharAtIndex(size_t idx) const {
+ if (idx < m_entries.size())
+ return m_entries[idx].quote;
+ return '\0';
+}
+
+char **Args::GetArgumentVector() {
+ assert(!m_argv.empty());
+ // TODO: functions like execve and posix_spawnp exhibit undefined behavior
+ // when argv or envp is null. So the code below is actually wrong. However,
+ // other code in LLDB depends on it being null. The code has been acting
+ // this way for some time, so it makes sense to leave it this way until
+ // someone has the time to come along and fix it.
+ return (m_argv.size() > 1) ? m_argv.data() : nullptr;
+}
+
+const char **Args::GetConstArgumentVector() const {
+ assert(!m_argv.empty());
+ return (m_argv.size() > 1) ? const_cast<const char **>(m_argv.data())
+ : nullptr;
+}
+
+void Args::Shift() {
+ // Don't pop the last NULL terminator from the argv array
+ if (m_entries.empty())
+ return;
+ m_argv.erase(m_argv.begin());
+ m_entries.erase(m_entries.begin());
+}
+
+void Args::Unshift(llvm::StringRef arg_str, char quote_char) {
+ InsertArgumentAtIndex(0, arg_str, quote_char);
+}
+
+void Args::AppendArguments(const Args &rhs) {
+ assert(m_argv.size() == m_entries.size() + 1);
+ assert(m_argv.back() == nullptr);
+ m_argv.pop_back();
+ for (auto &entry : rhs.m_entries) {
+ m_entries.emplace_back(entry.ref, entry.quote);
+ m_argv.push_back(m_entries.back().data());
+ }
+ m_argv.push_back(nullptr);
+}
+
+void Args::AppendArguments(const char **argv) {
+ size_t argc = ArgvToArgc(argv);
+
+ assert(m_argv.size() == m_entries.size() + 1);
+ assert(m_argv.back() == nullptr);
+ m_argv.pop_back();
+ for (auto arg : llvm::makeArrayRef(argv, argc)) {
+ m_entries.emplace_back(arg, '\0');
+ m_argv.push_back(m_entries.back().data());
+ }
+
+ m_argv.push_back(nullptr);
+}
+
+void Args::AppendArgument(llvm::StringRef arg_str, char quote_char) {
+ InsertArgumentAtIndex(GetArgumentCount(), arg_str, quote_char);
+}
+
+void Args::InsertArgumentAtIndex(size_t idx, llvm::StringRef arg_str,
+ char quote_char) {
+ assert(m_argv.size() == m_entries.size() + 1);
+ assert(m_argv.back() == nullptr);
+
+ if (idx > m_entries.size())
+ return;
+ m_entries.emplace(m_entries.begin() + idx, arg_str, quote_char);
+ m_argv.insert(m_argv.begin() + idx, m_entries[idx].data());
+}
+
+void Args::ReplaceArgumentAtIndex(size_t idx, llvm::StringRef arg_str,
+ char quote_char) {
+ assert(m_argv.size() == m_entries.size() + 1);
+ assert(m_argv.back() == nullptr);
+
+ if (idx >= m_entries.size())
+ return;
+
+ if (arg_str.size() > m_entries[idx].ref.size()) {
+ m_entries[idx] = ArgEntry(arg_str, quote_char);
+ m_argv[idx] = m_entries[idx].data();
+ } else {
+ const char *src_data = arg_str.data() ? arg_str.data() : "";
+ ::memcpy(m_entries[idx].data(), src_data, arg_str.size());
+ m_entries[idx].ptr[arg_str.size()] = 0;
+ m_entries[idx].ref = m_entries[idx].ref.take_front(arg_str.size());
+ }
+}
+
+void Args::DeleteArgumentAtIndex(size_t idx) {
+ if (idx >= m_entries.size())
+ return;
+
+ m_argv.erase(m_argv.begin() + idx);
+ m_entries.erase(m_entries.begin() + idx);
+}
+
+void Args::SetArguments(size_t argc, const char **argv) {
+ Clear();
+
+ auto args = llvm::makeArrayRef(argv, argc);
+ m_entries.resize(argc);
+ m_argv.resize(argc + 1);
+ for (size_t i = 0; i < args.size(); ++i) {
+ char quote =
+ ((args[i][0] == '\'') || (args[i][0] == '"') || (args[i][0] == '`'))
+ ? args[i][0]
+ : '\0';
+
+ m_entries[i] = ArgEntry(args[i], quote);
+ m_argv[i] = m_entries[i].data();
+ }
+}
+
+void Args::SetArguments(const char **argv) {
+ SetArguments(ArgvToArgc(argv), argv);
+}
+
+void Args::Clear() {
+ m_entries.clear();
+ m_argv.clear();
+ m_argv.push_back(nullptr);
+}
+
+const char *Args::StripSpaces(std::string &s, bool leading, bool trailing,
+ bool return_null_if_empty) {
+ static const char *k_white_space = " \t\v";
+ if (!s.empty()) {
+ if (leading) {
+ size_t pos = s.find_first_not_of(k_white_space);
+ if (pos == std::string::npos)
+ s.clear();
+ else if (pos > 0)
+ s.erase(0, pos);
+ }
+
+ if (trailing) {
+ size_t rpos = s.find_last_not_of(k_white_space);
+ if (rpos != std::string::npos && rpos + 1 < s.size())
+ s.erase(rpos + 1);
+ }
+ }
+ if (return_null_if_empty && s.empty())
+ return nullptr;
+ return s.c_str();
+}
+
+const char *Args::GetShellSafeArgument(const FileSpec &shell,
+ const char *unsafe_arg,
+ std::string &safe_arg) {
+ struct ShellDescriptor {
+ ConstString m_basename;
+ const char *m_escapables;
+ };
+
+ static ShellDescriptor g_Shells[] = {{ConstString("bash"), " '\"<>()&"},
+ {ConstString("tcsh"), " '\"<>()&$"},
+ {ConstString("sh"), " '\"<>()&"}};
+
+ // safe minimal set
+ const char *escapables = " '\"";
+
+ if (auto basename = shell.GetFilename()) {
+ for (const auto &Shell : g_Shells) {
+ if (Shell.m_basename == basename) {
+ escapables = Shell.m_escapables;
+ break;
+ }
+ }
+ }
+
+ safe_arg.assign(unsafe_arg);
+ size_t prev_pos = 0;
+ while (prev_pos < safe_arg.size()) {
+ // Escape spaces and quotes
+ size_t pos = safe_arg.find_first_of(escapables, prev_pos);
+ if (pos != std::string::npos) {
+ safe_arg.insert(pos, 1, '\\');
+ prev_pos = pos + 2;
+ } else
+ break;
+ }
+ return safe_arg.c_str();
+}
+
+lldb::Encoding Args::StringToEncoding(llvm::StringRef s,
+ lldb::Encoding fail_value) {
+ return llvm::StringSwitch<lldb::Encoding>(s)
+ .Case("uint", eEncodingUint)
+ .Case("sint", eEncodingSint)
+ .Case("ieee754", eEncodingIEEE754)
+ .Case("vector", eEncodingVector)
+ .Default(fail_value);
+}
+
+uint32_t Args::StringToGenericRegister(llvm::StringRef s) {
+ if (s.empty())
+ return LLDB_INVALID_REGNUM;
+ uint32_t result = llvm::StringSwitch<uint32_t>(s)
+ .Case("pc", LLDB_REGNUM_GENERIC_PC)
+ .Case("sp", LLDB_REGNUM_GENERIC_SP)
+ .Case("fp", LLDB_REGNUM_GENERIC_FP)
+ .Cases("ra", "lr", LLDB_REGNUM_GENERIC_RA)
+ .Case("flags", LLDB_REGNUM_GENERIC_FLAGS)
+ .Case("arg1", LLDB_REGNUM_GENERIC_ARG1)
+ .Case("arg2", LLDB_REGNUM_GENERIC_ARG2)
+ .Case("arg3", LLDB_REGNUM_GENERIC_ARG3)
+ .Case("arg4", LLDB_REGNUM_GENERIC_ARG4)
+ .Case("arg5", LLDB_REGNUM_GENERIC_ARG5)
+ .Case("arg6", LLDB_REGNUM_GENERIC_ARG6)
+ .Case("arg7", LLDB_REGNUM_GENERIC_ARG7)
+ .Case("arg8", LLDB_REGNUM_GENERIC_ARG8)
+ .Default(LLDB_INVALID_REGNUM);
+ return result;
+}
+
+void Args::EncodeEscapeSequences(const char *src, std::string &dst) {
+ dst.clear();
+ if (src) {
+ for (const char *p = src; *p != '\0'; ++p) {
+ size_t non_special_chars = ::strcspn(p, "\\");
+ if (non_special_chars > 0) {
+ dst.append(p, non_special_chars);
+ p += non_special_chars;
+ if (*p == '\0')
+ break;
+ }
+
+ if (*p == '\\') {
+ ++p; // skip the slash
+ switch (*p) {
+ case 'a':
+ dst.append(1, '\a');
+ break;
+ case 'b':
+ dst.append(1, '\b');
+ break;
+ case 'f':
+ dst.append(1, '\f');
+ break;
+ case 'n':
+ dst.append(1, '\n');
+ break;
+ case 'r':
+ dst.append(1, '\r');
+ break;
+ case 't':
+ dst.append(1, '\t');
+ break;
+ case 'v':
+ dst.append(1, '\v');
+ break;
+ case '\\':
+ dst.append(1, '\\');
+ break;
+ case '\'':
+ dst.append(1, '\'');
+ break;
+ case '"':
+ dst.append(1, '"');
+ break;
+ case '0':
+ // 1 to 3 octal chars
+ {
+ // Make a string that can hold onto the initial zero char, up to 3
+ // octal digits, and a terminating NULL.
+ char oct_str[5] = {'\0', '\0', '\0', '\0', '\0'};
+
+ int i;
+ for (i = 0; (p[i] >= '0' && p[i] <= '7') && i < 4; ++i)
+ oct_str[i] = p[i];
+
+ // We don't want to consume the last octal character since the main
+ // for loop will do this for us, so we advance p by one less than i
+ // (even if i is zero)
+ p += i - 1;
+ unsigned long octal_value = ::strtoul(oct_str, nullptr, 8);
+ if (octal_value <= UINT8_MAX) {
+ dst.append(1, static_cast<char>(octal_value));
+ }
+ }
+ break;
+
+ case 'x':
+ // hex number in the format
+ if (isxdigit(p[1])) {
+ ++p; // Skip the 'x'
+
+ // Make a string that can hold onto two hex chars plus a
+ // NULL terminator
+ char hex_str[3] = {*p, '\0', '\0'};
+ if (isxdigit(p[1])) {
+ ++p; // Skip the first of the two hex chars
+ hex_str[1] = *p;
+ }
+
+ unsigned long hex_value = strtoul(hex_str, nullptr, 16);
+ if (hex_value <= UINT8_MAX)
+ dst.append(1, static_cast<char>(hex_value));
+ } else {
+ dst.append(1, 'x');
+ }
+ break;
+
+ default:
+ // Just desensitize any other character by just printing what came
+ // after the '\'
+ dst.append(1, *p);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void Args::ExpandEscapedCharacters(const char *src, std::string &dst) {
+ dst.clear();
+ if (src) {
+ for (const char *p = src; *p != '\0'; ++p) {
+ if (isprint(*p))
+ dst.append(1, *p);
+ else {
+ switch (*p) {
+ case '\a':
+ dst.append("\\a");
+ break;
+ case '\b':
+ dst.append("\\b");
+ break;
+ case '\f':
+ dst.append("\\f");
+ break;
+ case '\n':
+ dst.append("\\n");
+ break;
+ case '\r':
+ dst.append("\\r");
+ break;
+ case '\t':
+ dst.append("\\t");
+ break;
+ case '\v':
+ dst.append("\\v");
+ break;
+ case '\'':
+ dst.append("\\'");
+ break;
+ case '"':
+ dst.append("\\\"");
+ break;
+ case '\\':
+ dst.append("\\\\");
+ break;
+ default: {
+ // Just encode as octal
+ dst.append("\\0");
+ char octal_str[32];
+ snprintf(octal_str, sizeof(octal_str), "%o", *p);
+ dst.append(octal_str);
+ } break;
+ }
+ }
+ }
+ }
+}
+
+std::string Args::EscapeLLDBCommandArgument(const std::string &arg,
+ char quote_char) {
+ const char *chars_to_escape = nullptr;
+ switch (quote_char) {
+ case '\0':
+ chars_to_escape = " \t\\'\"`";
+ break;
+ case '"':
+ chars_to_escape = "$\"`\\";
+ break;
+ case '`':
+ case '\'':
+ return arg;
+ default:
+ assert(false && "Unhandled quote character");
+ return arg;
+ }
+
+ std::string res;
+ res.reserve(arg.size());
+ for (char c : arg) {
+ if (::strchr(chars_to_escape, c))
+ res.push_back('\\');
+ res.push_back(c);
+ }
+ return res;
+}
+
+OptionsWithRaw::OptionsWithRaw(llvm::StringRef arg_string) {
+ SetFromString(arg_string);
+}
+
+void OptionsWithRaw::SetFromString(llvm::StringRef arg_string) {
+ const llvm::StringRef original_args = arg_string;
+
+ arg_string = ltrimForArgs(arg_string);
+ std::string arg;
+ char quote;
+
+ // If the string doesn't start with a dash, we just have no options and just
+ // a raw part.
+ if (!arg_string.startswith("-")) {
+ m_suffix = original_args;
+ return;
+ }
+
+ bool found_suffix = false;
+
+ while (!arg_string.empty()) {
+ // The length of the prefix before parsing.
+ std::size_t prev_prefix_length = original_args.size() - arg_string.size();
+
+ // Parse the next argument from the remaining string.
+ std::tie(arg, quote, arg_string) = ParseSingleArgument(arg_string);
+
+ // If we get an unquoted '--' argument, then we reached the suffix part
+ // of the command.
+ Args::ArgEntry entry(arg, quote);
+ if (!entry.IsQuoted() && arg == "--") {
+ // The remaining line is the raw suffix, and the line we parsed so far
+ // needs to be interpreted as arguments.
+ m_has_args = true;
+ m_suffix = arg_string;
+ found_suffix = true;
+
+ // The length of the prefix after parsing.
+ std::size_t prefix_length = original_args.size() - arg_string.size();
+
+ // Take the string we know contains all the arguments and actually parse
+ // it as proper arguments.
+ llvm::StringRef prefix = original_args.take_front(prev_prefix_length);
+ m_args = Args(prefix);
+ m_arg_string = prefix;
+
+ // We also record the part of the string that contains the arguments plus
+ // the delimiter.
+ m_arg_string_with_delimiter = original_args.take_front(prefix_length);
+
+ // As the rest of the string became the raw suffix, we are done here.
+ break;
+ }
+
+ arg_string = ltrimForArgs(arg_string);
+ }
+
+ // If we didn't find a suffix delimiter, the whole string is the raw suffix.
+ if (!found_suffix) {
+ found_suffix = true;
+ m_suffix = original_args;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Baton.cpp b/contrib/llvm-project/lldb/source/Utility/Baton.cpp
new file mode 100644
index 000000000000..84e295e24686
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Baton.cpp
@@ -0,0 +1,12 @@
+//===-- Baton.cpp -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Baton.h"
+
+void lldb_private::UntypedBaton::GetDescription(
+ Stream *s, lldb::DescriptionLevel level) const {}
diff --git a/contrib/llvm-project/lldb/source/Utility/Broadcaster.cpp b/contrib/llvm-project/lldb/source/Utility/Broadcaster.cpp
new file mode 100644
index 000000000000..597888cfa0e2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Broadcaster.cpp
@@ -0,0 +1,468 @@
+//===-- Broadcaster.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Broadcaster.h"
+
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/Listener.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <algorithm>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include <assert.h>
+#include <stddef.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+Broadcaster::Broadcaster(BroadcasterManagerSP manager_sp, const char *name)
+ : m_broadcaster_sp(std::make_shared<BroadcasterImpl>(*this)),
+ m_manager_sp(std::move(manager_sp)), m_broadcaster_name(name) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ LLDB_LOG(log, "{0} Broadcaster::Broadcaster(\"{1}\")",
+ static_cast<void *>(this), GetBroadcasterName().AsCString());
+}
+
+Broadcaster::BroadcasterImpl::BroadcasterImpl(Broadcaster &broadcaster)
+ : m_broadcaster(broadcaster), m_listeners(), m_listeners_mutex(),
+ m_hijacking_listeners(), m_hijacking_masks() {}
+
+Broadcaster::~Broadcaster() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ LLDB_LOG(log, "{0} Broadcaster::~Broadcaster(\"{1}\")",
+ static_cast<void *>(this), GetBroadcasterName().AsCString());
+
+ Clear();
+}
+
+void Broadcaster::CheckInWithManager() {
+ if (m_manager_sp) {
+ m_manager_sp->SignUpListenersForBroadcaster(*this);
+ }
+}
+
+llvm::SmallVector<std::pair<ListenerSP, uint32_t &>, 4>
+Broadcaster::BroadcasterImpl::GetListeners() {
+ llvm::SmallVector<std::pair<ListenerSP, uint32_t &>, 4> listeners;
+ listeners.reserve(m_listeners.size());
+
+ for (auto it = m_listeners.begin(); it != m_listeners.end();) {
+ lldb::ListenerSP curr_listener_sp(it->first.lock());
+ if (curr_listener_sp && it->second) {
+ listeners.emplace_back(std::move(curr_listener_sp), it->second);
+ ++it;
+ } else
+ it = m_listeners.erase(it);
+ }
+
+ return listeners;
+}
+
+void Broadcaster::BroadcasterImpl::Clear() {
+ std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
+
+ // Make sure the listener forgets about this broadcaster. We do this in the
+ // broadcaster in case the broadcaster object initiates the removal.
+ for (auto &pair : GetListeners())
+ pair.first->BroadcasterWillDestruct(&m_broadcaster);
+
+ m_listeners.clear();
+}
+
+Broadcaster *Broadcaster::BroadcasterImpl::GetBroadcaster() {
+ return &m_broadcaster;
+}
+
+bool Broadcaster::BroadcasterImpl::GetEventNames(
+ Stream &s, uint32_t event_mask, bool prefix_with_broadcaster_name) const {
+ uint32_t num_names_added = 0;
+ if (event_mask && !m_event_names.empty()) {
+ event_names_map::const_iterator end = m_event_names.end();
+ for (uint32_t bit = 1u, mask = event_mask; mask != 0 && bit != 0;
+ bit <<= 1, mask >>= 1) {
+ if (mask & 1) {
+ event_names_map::const_iterator pos = m_event_names.find(bit);
+ if (pos != end) {
+ if (num_names_added > 0)
+ s.PutCString(", ");
+
+ if (prefix_with_broadcaster_name) {
+ s.PutCString(GetBroadcasterName());
+ s.PutChar('.');
+ }
+ s.PutCString(pos->second);
+ ++num_names_added;
+ }
+ }
+ }
+ }
+ return num_names_added > 0;
+}
+
+void Broadcaster::AddInitialEventsToListener(
+ const lldb::ListenerSP &listener_sp, uint32_t requested_events) {}
+
+uint32_t
+Broadcaster::BroadcasterImpl::AddListener(const lldb::ListenerSP &listener_sp,
+ uint32_t event_mask) {
+ if (!listener_sp)
+ return 0;
+
+ std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
+
+ // See if we already have this listener, and if so, update its mask
+
+ bool handled = false;
+
+ for (auto &pair : GetListeners()) {
+ if (pair.first == listener_sp) {
+ handled = true;
+ pair.second |= event_mask;
+ m_broadcaster.AddInitialEventsToListener(listener_sp, event_mask);
+ break;
+ }
+ }
+
+ if (!handled) {
+ // Grant a new listener the available event bits
+ m_listeners.push_back(
+ std::make_pair(lldb::ListenerWP(listener_sp), event_mask));
+
+ // Individual broadcasters decide whether they have outstanding data when a
+ // listener attaches, and insert it into the listener with this method.
+ m_broadcaster.AddInitialEventsToListener(listener_sp, event_mask);
+ }
+
+ // Return the event bits that were granted to the listener
+ return event_mask;
+}
+
+bool Broadcaster::BroadcasterImpl::EventTypeHasListeners(uint32_t event_type) {
+ std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
+
+ if (!m_hijacking_listeners.empty() && event_type & m_hijacking_masks.back())
+ return true;
+
+ for (auto &pair : GetListeners()) {
+ if (pair.second & event_type)
+ return true;
+ }
+ return false;
+}
+
+bool Broadcaster::BroadcasterImpl::RemoveListener(
+ lldb_private::Listener *listener, uint32_t event_mask) {
+ if (!listener)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
+ for (auto &pair : GetListeners()) {
+ if (pair.first.get() == listener) {
+ pair.second &= ~event_mask;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Broadcaster::BroadcasterImpl::RemoveListener(
+ const lldb::ListenerSP &listener_sp, uint32_t event_mask) {
+ return RemoveListener(listener_sp.get(), event_mask);
+}
+
+void Broadcaster::BroadcasterImpl::BroadcastEvent(EventSP &event_sp) {
+ return PrivateBroadcastEvent(event_sp, false);
+}
+
+void Broadcaster::BroadcasterImpl::BroadcastEventIfUnique(EventSP &event_sp) {
+ return PrivateBroadcastEvent(event_sp, true);
+}
+
+void Broadcaster::BroadcasterImpl::PrivateBroadcastEvent(EventSP &event_sp,
+ bool unique) {
+ // Can't add a nullptr event...
+ if (!event_sp)
+ return;
+
+ // Update the broadcaster on this event
+ event_sp->SetBroadcaster(&m_broadcaster);
+
+ const uint32_t event_type = event_sp->GetType();
+
+ std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
+
+ ListenerSP hijacking_listener_sp;
+
+ if (!m_hijacking_listeners.empty()) {
+ assert(!m_hijacking_masks.empty());
+ hijacking_listener_sp = m_hijacking_listeners.back();
+ if ((event_type & m_hijacking_masks.back()) == 0)
+ hijacking_listener_sp.reset();
+ }
+
+ if (Log *log = lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EVENTS)) {
+ StreamString event_description;
+ event_sp->Dump(&event_description);
+ log->Printf("%p Broadcaster(\"%s\")::BroadcastEvent (event_sp = {%s}, "
+ "unique =%i) hijack = %p",
+ static_cast<void *>(this), GetBroadcasterName(),
+ event_description.GetData(), unique,
+ static_cast<void *>(hijacking_listener_sp.get()));
+ }
+
+ if (hijacking_listener_sp) {
+ if (unique && hijacking_listener_sp->PeekAtNextEventForBroadcasterWithType(
+ &m_broadcaster, event_type))
+ return;
+ hijacking_listener_sp->AddEvent(event_sp);
+ } else {
+ for (auto &pair : GetListeners()) {
+ if (!(pair.second & event_type))
+ continue;
+ if (unique && pair.first->PeekAtNextEventForBroadcasterWithType(
+ &m_broadcaster, event_type))
+ continue;
+
+ pair.first->AddEvent(event_sp);
+ }
+ }
+}
+
+void Broadcaster::BroadcasterImpl::BroadcastEvent(uint32_t event_type,
+ EventData *event_data) {
+ auto event_sp = std::make_shared<Event>(event_type, event_data);
+ PrivateBroadcastEvent(event_sp, false);
+}
+
+void Broadcaster::BroadcasterImpl::BroadcastEvent(
+ uint32_t event_type, const lldb::EventDataSP &event_data_sp) {
+ auto event_sp = std::make_shared<Event>(event_type, event_data_sp);
+ PrivateBroadcastEvent(event_sp, false);
+}
+
+void Broadcaster::BroadcasterImpl::BroadcastEventIfUnique(
+ uint32_t event_type, EventData *event_data) {
+ auto event_sp = std::make_shared<Event>(event_type, event_data);
+ PrivateBroadcastEvent(event_sp, true);
+}
+
+bool Broadcaster::BroadcasterImpl::HijackBroadcaster(
+ const lldb::ListenerSP &listener_sp, uint32_t event_mask) {
+ std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EVENTS));
+ LLDB_LOG(
+ log,
+ "{0} Broadcaster(\"{1}\")::HijackBroadcaster (listener(\"{2}\")={3})",
+ static_cast<void *>(this), GetBroadcasterName(),
+ listener_sp->m_name.c_str(), static_cast<void *>(listener_sp.get()));
+ m_hijacking_listeners.push_back(listener_sp);
+ m_hijacking_masks.push_back(event_mask);
+ return true;
+}
+
+bool Broadcaster::BroadcasterImpl::IsHijackedForEvent(uint32_t event_mask) {
+ std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
+
+ if (!m_hijacking_listeners.empty())
+ return (event_mask & m_hijacking_masks.back()) != 0;
+ return false;
+}
+
+const char *Broadcaster::BroadcasterImpl::GetHijackingListenerName() {
+ if (m_hijacking_listeners.size()) {
+ return m_hijacking_listeners.back()->GetName();
+ }
+ return nullptr;
+}
+
+void Broadcaster::BroadcasterImpl::RestoreBroadcaster() {
+ std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
+
+ if (!m_hijacking_listeners.empty()) {
+ ListenerSP listener_sp = m_hijacking_listeners.back();
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EVENTS));
+ LLDB_LOG(log,
+ "{0} Broadcaster(\"{1}\")::RestoreBroadcaster (about to pop "
+ "listener(\"{2}\")={3})",
+ static_cast<void *>(this), GetBroadcasterName(),
+ listener_sp->m_name.c_str(),
+ static_cast<void *>(listener_sp.get()));
+ m_hijacking_listeners.pop_back();
+ }
+ if (!m_hijacking_masks.empty())
+ m_hijacking_masks.pop_back();
+}
+
+ConstString &Broadcaster::GetBroadcasterClass() const {
+ static ConstString class_name("lldb.anonymous");
+ return class_name;
+}
+
+bool BroadcastEventSpec::operator<(const BroadcastEventSpec &rhs) const {
+ if (GetBroadcasterClass() == rhs.GetBroadcasterClass()) {
+ return GetEventBits() < rhs.GetEventBits();
+ }
+ return GetBroadcasterClass() < rhs.GetBroadcasterClass();
+}
+
+BroadcastEventSpec &BroadcastEventSpec::
+operator=(const BroadcastEventSpec &rhs) = default;
+
+BroadcasterManager::BroadcasterManager() : m_manager_mutex() {}
+
+lldb::BroadcasterManagerSP BroadcasterManager::MakeBroadcasterManager() {
+ return lldb::BroadcasterManagerSP(new BroadcasterManager());
+}
+
+uint32_t BroadcasterManager::RegisterListenerForEvents(
+ const lldb::ListenerSP &listener_sp, const BroadcastEventSpec &event_spec) {
+ std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
+
+ collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end();
+ uint32_t available_bits = event_spec.GetEventBits();
+
+ while (iter != end_iter &&
+ (iter = find_if(iter, end_iter,
+ BroadcasterClassMatches(
+ event_spec.GetBroadcasterClass()))) != end_iter) {
+ available_bits &= ~((*iter).first.GetEventBits());
+ iter++;
+ }
+
+ if (available_bits != 0) {
+ m_event_map.insert(event_listener_key(
+ BroadcastEventSpec(event_spec.GetBroadcasterClass(), available_bits),
+ listener_sp));
+ m_listeners.insert(listener_sp);
+ }
+
+ return available_bits;
+}
+
+bool BroadcasterManager::UnregisterListenerForEvents(
+ const lldb::ListenerSP &listener_sp, const BroadcastEventSpec &event_spec) {
+ std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
+ bool removed_some = false;
+
+ if (m_listeners.erase(listener_sp) == 0)
+ return false;
+
+ ListenerMatchesAndSharedBits predicate(event_spec, listener_sp);
+ std::vector<BroadcastEventSpec> to_be_readded;
+ uint32_t event_bits_to_remove = event_spec.GetEventBits();
+
+ // Go through the map and delete the exact matches, and build a list of
+ // matches that weren't exact to re-add:
+ while (true) {
+ collection::iterator iter, end_iter = m_event_map.end();
+ iter = find_if(m_event_map.begin(), end_iter, predicate);
+ if (iter == end_iter) {
+ break;
+ }
+ uint32_t iter_event_bits = (*iter).first.GetEventBits();
+ removed_some = true;
+
+ if (event_bits_to_remove != iter_event_bits) {
+ uint32_t new_event_bits = iter_event_bits & ~event_bits_to_remove;
+ to_be_readded.push_back(
+ BroadcastEventSpec(event_spec.GetBroadcasterClass(), new_event_bits));
+ }
+ m_event_map.erase(iter);
+ }
+
+ // Okay now add back the bits that weren't completely removed:
+ for (size_t i = 0; i < to_be_readded.size(); i++) {
+ m_event_map.insert(event_listener_key(to_be_readded[i], listener_sp));
+ }
+
+ return removed_some;
+}
+
+ListenerSP BroadcasterManager::GetListenerForEventSpec(
+ const BroadcastEventSpec &event_spec) const {
+ std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
+
+ collection::const_iterator iter, end_iter = m_event_map.end();
+ iter = find_if(m_event_map.begin(), end_iter,
+ BroadcastEventSpecMatches(event_spec));
+ if (iter != end_iter)
+ return (*iter).second;
+
+ return nullptr;
+}
+
+void BroadcasterManager::RemoveListener(Listener *listener) {
+ std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
+ ListenerMatchesPointer predicate(listener);
+ listener_collection::iterator iter = m_listeners.begin(),
+ end_iter = m_listeners.end();
+
+ std::find_if(iter, end_iter, predicate);
+ if (iter != end_iter)
+ m_listeners.erase(iter);
+
+ while (true) {
+ collection::iterator iter, end_iter = m_event_map.end();
+ iter = find_if(m_event_map.begin(), end_iter, predicate);
+ if (iter == end_iter)
+ break;
+
+ m_event_map.erase(iter);
+ }
+}
+
+void BroadcasterManager::RemoveListener(const lldb::ListenerSP &listener_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
+ ListenerMatches predicate(listener_sp);
+
+ if (m_listeners.erase(listener_sp) == 0)
+ return;
+
+ while (true) {
+ collection::iterator iter, end_iter = m_event_map.end();
+ iter = find_if(m_event_map.begin(), end_iter, predicate);
+ if (iter == end_iter)
+ break;
+
+ m_event_map.erase(iter);
+ }
+}
+
+void BroadcasterManager::SignUpListenersForBroadcaster(
+ Broadcaster &broadcaster) {
+ std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
+
+ collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end();
+
+ while (iter != end_iter &&
+ (iter = find_if(iter, end_iter,
+ BroadcasterClassMatches(
+ broadcaster.GetBroadcasterClass()))) != end_iter) {
+ (*iter).second->StartListeningForEvents(&broadcaster,
+ (*iter).first.GetEventBits());
+ iter++;
+ }
+}
+
+void BroadcasterManager::Clear() {
+ std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
+ listener_collection::iterator end_iter = m_listeners.end();
+
+ for (listener_collection::iterator iter = m_listeners.begin();
+ iter != end_iter; iter++)
+ (*iter)->BroadcasterManagerWillDestruct(this->shared_from_this());
+ m_listeners.clear();
+ m_event_map.clear();
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/CompletionRequest.cpp b/contrib/llvm-project/lldb/source/Utility/CompletionRequest.cpp
new file mode 100644
index 000000000000..c62ec4f56ffa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/CompletionRequest.cpp
@@ -0,0 +1,92 @@
+//===-- CompletionRequest.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/CompletionRequest.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+CompletionRequest::CompletionRequest(llvm::StringRef command_line,
+ unsigned raw_cursor_pos,
+ int match_start_point,
+ int max_return_elements,
+ CompletionResult &result)
+ : m_command(command_line), m_raw_cursor_pos(raw_cursor_pos),
+ m_match_start_point(match_start_point),
+ m_max_return_elements(max_return_elements), m_result(result) {
+
+ // We parse the argument up to the cursor, so the last argument in
+ // parsed_line is the one containing the cursor, and the cursor is after the
+ // last character.
+ m_parsed_line = Args(command_line);
+ m_partial_parsed_line = Args(command_line.substr(0, raw_cursor_pos));
+
+ m_cursor_index = m_partial_parsed_line.GetArgumentCount() - 1;
+
+ if (m_cursor_index == -1)
+ m_cursor_char_position = 0;
+ else
+ m_cursor_char_position =
+ strlen(m_partial_parsed_line.GetArgumentAtIndex(m_cursor_index));
+
+ const char *cursor = command_line.data() + raw_cursor_pos;
+ if (raw_cursor_pos > 0 && cursor[-1] == ' ') {
+ // We are just after a space. If we are in an argument, then we will
+ // continue parsing, but if we are between arguments, then we have to
+ // complete whatever the next element would be. We can distinguish the two
+ // cases because if we are in an argument (e.g. because the space is
+ // protected by a quote) then the space will also be in the parsed
+ // argument...
+
+ const char *current_elem =
+ m_partial_parsed_line.GetArgumentAtIndex(m_cursor_index);
+ if (m_cursor_char_position == 0 ||
+ current_elem[m_cursor_char_position - 1] != ' ') {
+ m_parsed_line.InsertArgumentAtIndex(m_cursor_index + 1, llvm::StringRef(),
+ '\0');
+ m_cursor_index++;
+ m_cursor_char_position = 0;
+ }
+ }
+}
+
+std::string CompletionResult::Completion::GetUniqueKey() const {
+
+ // We build a unique key for this pair of completion:description. We
+ // prefix the key with the length of the completion string. This prevents
+ // that we could get any collisions from completions pairs such as these:
+ // "foo:", "bar" would be "foo:bar", but will now be: "4foo:bar"
+ // "foo", ":bar" would be "foo:bar", but will now be: "3foo:bar"
+
+ std::string result;
+ result.append(std::to_string(m_completion.size()));
+ result.append(m_completion);
+ result.append(m_descripton);
+ return result;
+}
+
+void CompletionResult::AddResult(llvm::StringRef completion,
+ llvm::StringRef description) {
+ Completion r(completion, description);
+
+ // Add the completion if we haven't seen the same value before.
+ if (m_added_values.insert(r.GetUniqueKey()).second)
+ m_results.push_back(r);
+}
+
+void CompletionResult::GetMatches(StringList &matches) const {
+ matches.Clear();
+ for (const Completion &completion : m_results)
+ matches.AppendString(completion.m_completion);
+}
+
+void CompletionResult::GetDescriptions(StringList &descriptions) const {
+ descriptions.Clear();
+ for (const Completion &completion : m_results)
+ descriptions.AppendString(completion.m_descripton);
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Connection.cpp b/contrib/llvm-project/lldb/source/Utility/Connection.cpp
new file mode 100644
index 000000000000..483a0c941be4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Connection.cpp
@@ -0,0 +1,13 @@
+//===-- Connection.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Connection.h"
+
+using namespace lldb_private;
+
+Connection::~Connection() = default;
diff --git a/contrib/llvm-project/lldb/source/Utility/ConstString.cpp b/contrib/llvm-project/lldb/source/Utility/ConstString.cpp
new file mode 100644
index 000000000000..46b7ab259383
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/ConstString.cpp
@@ -0,0 +1,329 @@
+//===-- ConstString.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/ConstString.h"
+
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/iterator.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/DJB.h"
+#include "llvm/Support/FormatProviders.h"
+#include "llvm/Support/RWMutex.h"
+#include "llvm/Support/Threading.h"
+
+#include <algorithm>
+#include <array>
+#include <utility>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+
+using namespace lldb_private;
+
+class Pool {
+public:
+ typedef const char *StringPoolValueType;
+ typedef llvm::StringMap<StringPoolValueType, llvm::BumpPtrAllocator>
+ StringPool;
+ typedef llvm::StringMapEntry<StringPoolValueType> StringPoolEntryType;
+
+ static StringPoolEntryType &
+ GetStringMapEntryFromKeyData(const char *keyData) {
+ return StringPoolEntryType::GetStringMapEntryFromKeyData(keyData);
+ }
+
+ static size_t GetConstCStringLength(const char *ccstr) {
+ if (ccstr != nullptr) {
+ // Since the entry is read only, and we derive the entry entirely from
+ // the pointer, we don't need the lock.
+ const StringPoolEntryType &entry = GetStringMapEntryFromKeyData(ccstr);
+ return entry.getKey().size();
+ }
+ return 0;
+ }
+
+ StringPoolValueType GetMangledCounterpart(const char *ccstr) const {
+ if (ccstr != nullptr) {
+ const uint8_t h = hash(llvm::StringRef(ccstr));
+ llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].m_mutex);
+ return GetStringMapEntryFromKeyData(ccstr).getValue();
+ }
+ return nullptr;
+ }
+
+ bool SetMangledCounterparts(const char *key_ccstr, const char *value_ccstr) {
+ if (key_ccstr != nullptr && value_ccstr != nullptr) {
+ {
+ const uint8_t h = hash(llvm::StringRef(key_ccstr));
+ llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
+ GetStringMapEntryFromKeyData(key_ccstr).setValue(value_ccstr);
+ }
+ {
+ const uint8_t h = hash(llvm::StringRef(value_ccstr));
+ llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
+ GetStringMapEntryFromKeyData(value_ccstr).setValue(key_ccstr);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ const char *GetConstCString(const char *cstr) {
+ if (cstr != nullptr)
+ return GetConstCStringWithLength(cstr, strlen(cstr));
+ return nullptr;
+ }
+
+ const char *GetConstCStringWithLength(const char *cstr, size_t cstr_len) {
+ if (cstr != nullptr)
+ return GetConstCStringWithStringRef(llvm::StringRef(cstr, cstr_len));
+ return nullptr;
+ }
+
+ const char *GetConstCStringWithStringRef(const llvm::StringRef &string_ref) {
+ if (string_ref.data()) {
+ const uint8_t h = hash(string_ref);
+
+ {
+ llvm::sys::SmartScopedReader<false> rlock(m_string_pools[h].m_mutex);
+ auto it = m_string_pools[h].m_string_map.find(string_ref);
+ if (it != m_string_pools[h].m_string_map.end())
+ return it->getKeyData();
+ }
+
+ llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
+ StringPoolEntryType &entry =
+ *m_string_pools[h]
+ .m_string_map.insert(std::make_pair(string_ref, nullptr))
+ .first;
+ return entry.getKeyData();
+ }
+ return nullptr;
+ }
+
+ const char *
+ GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled,
+ const char *mangled_ccstr) {
+ const char *demangled_ccstr = nullptr;
+
+ {
+ const uint8_t h = hash(demangled);
+ llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
+
+ // Make or update string pool entry with the mangled counterpart
+ StringPool &map = m_string_pools[h].m_string_map;
+ StringPoolEntryType &entry = *map.try_emplace(demangled).first;
+
+ entry.second = mangled_ccstr;
+
+ // Extract the const version of the demangled_cstr
+ demangled_ccstr = entry.getKeyData();
+ }
+
+ {
+ // Now assign the demangled const string as the counterpart of the
+ // mangled const string...
+ const uint8_t h = hash(llvm::StringRef(mangled_ccstr));
+ llvm::sys::SmartScopedWriter<false> wlock(m_string_pools[h].m_mutex);
+ GetStringMapEntryFromKeyData(mangled_ccstr).setValue(demangled_ccstr);
+ }
+
+ // Return the constant demangled C string
+ return demangled_ccstr;
+ }
+
+ const char *GetConstTrimmedCStringWithLength(const char *cstr,
+ size_t cstr_len) {
+ if (cstr != nullptr) {
+ const size_t trimmed_len = strnlen(cstr, cstr_len);
+ return GetConstCStringWithLength(cstr, trimmed_len);
+ }
+ return nullptr;
+ }
+
+ // Return the size in bytes that this object and any items in its collection
+ // of uniqued strings + data count values takes in memory.
+ size_t MemorySize() const {
+ size_t mem_size = sizeof(Pool);
+ for (const auto &pool : m_string_pools) {
+ llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);
+ for (const auto &entry : pool.m_string_map)
+ mem_size += sizeof(StringPoolEntryType) + entry.getKey().size();
+ }
+ return mem_size;
+ }
+
+protected:
+ uint8_t hash(const llvm::StringRef &s) const {
+ uint32_t h = llvm::djbHash(s);
+ return ((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h) & 0xff;
+ }
+
+ struct PoolEntry {
+ mutable llvm::sys::SmartRWMutex<false> m_mutex;
+ StringPool m_string_map;
+ };
+
+ std::array<PoolEntry, 256> m_string_pools;
+};
+
+// Frameworks and dylibs aren't supposed to have global C++ initializers so we
+// hide the string pool in a static function so that it will get initialized on
+// the first call to this static function.
+//
+// Note, for now we make the string pool a pointer to the pool, because we
+// can't guarantee that some objects won't get destroyed after the global
+// destructor chain is run, and trying to make sure no destructors touch
+// ConstStrings is difficult. So we leak the pool instead.
+static Pool &StringPool() {
+ static llvm::once_flag g_pool_initialization_flag;
+ static Pool *g_string_pool = nullptr;
+
+ llvm::call_once(g_pool_initialization_flag,
+ []() { g_string_pool = new Pool(); });
+
+ return *g_string_pool;
+}
+
+ConstString::ConstString(const char *cstr)
+ : m_string(StringPool().GetConstCString(cstr)) {}
+
+ConstString::ConstString(const char *cstr, size_t cstr_len)
+ : m_string(StringPool().GetConstCStringWithLength(cstr, cstr_len)) {}
+
+ConstString::ConstString(const llvm::StringRef &s)
+ : m_string(StringPool().GetConstCStringWithStringRef(s)) {}
+
+bool ConstString::operator<(ConstString rhs) const {
+ if (m_string == rhs.m_string)
+ return false;
+
+ llvm::StringRef lhs_string_ref(GetStringRef());
+ llvm::StringRef rhs_string_ref(rhs.GetStringRef());
+
+ // If both have valid C strings, then return the comparison
+ if (lhs_string_ref.data() && rhs_string_ref.data())
+ return lhs_string_ref < rhs_string_ref;
+
+ // Else one of them was nullptr, so if LHS is nullptr then it is less than
+ return lhs_string_ref.data() == nullptr;
+}
+
+Stream &lldb_private::operator<<(Stream &s, ConstString str) {
+ const char *cstr = str.GetCString();
+ if (cstr != nullptr)
+ s << cstr;
+
+ return s;
+}
+
+size_t ConstString::GetLength() const {
+ return Pool::GetConstCStringLength(m_string);
+}
+
+bool ConstString::Equals(ConstString lhs, ConstString rhs,
+ const bool case_sensitive) {
+ if (lhs.m_string == rhs.m_string)
+ return true;
+
+ // Since the pointers weren't equal, and identical ConstStrings always have
+ // identical pointers, the result must be false for case sensitive equality
+ // test.
+ if (case_sensitive)
+ return false;
+
+ // perform case insensitive equality test
+ llvm::StringRef lhs_string_ref(lhs.GetStringRef());
+ llvm::StringRef rhs_string_ref(rhs.GetStringRef());
+ return lhs_string_ref.equals_lower(rhs_string_ref);
+}
+
+int ConstString::Compare(ConstString lhs, ConstString rhs,
+ const bool case_sensitive) {
+ // If the iterators are the same, this is the same string
+ const char *lhs_cstr = lhs.m_string;
+ const char *rhs_cstr = rhs.m_string;
+ if (lhs_cstr == rhs_cstr)
+ return 0;
+ if (lhs_cstr && rhs_cstr) {
+ llvm::StringRef lhs_string_ref(lhs.GetStringRef());
+ llvm::StringRef rhs_string_ref(rhs.GetStringRef());
+
+ if (case_sensitive) {
+ return lhs_string_ref.compare(rhs_string_ref);
+ } else {
+ return lhs_string_ref.compare_lower(rhs_string_ref);
+ }
+ }
+
+ if (lhs_cstr)
+ return +1; // LHS isn't nullptr but RHS is
+ else
+ return -1; // LHS is nullptr but RHS isn't
+}
+
+void ConstString::Dump(Stream *s, const char *fail_value) const {
+ if (s != nullptr) {
+ const char *cstr = AsCString(fail_value);
+ if (cstr != nullptr)
+ s->PutCString(cstr);
+ }
+}
+
+void ConstString::DumpDebug(Stream *s) const {
+ const char *cstr = GetCString();
+ size_t cstr_len = GetLength();
+ // Only print the parens if we have a non-nullptr string
+ const char *parens = cstr ? "\"" : "";
+ s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64,
+ static_cast<int>(sizeof(void *) * 2),
+ static_cast<const void *>(this), parens, cstr, parens,
+ static_cast<uint64_t>(cstr_len));
+}
+
+void ConstString::SetCString(const char *cstr) {
+ m_string = StringPool().GetConstCString(cstr);
+}
+
+void ConstString::SetString(const llvm::StringRef &s) {
+ m_string = StringPool().GetConstCStringWithLength(s.data(), s.size());
+}
+
+void ConstString::SetStringWithMangledCounterpart(llvm::StringRef demangled,
+ ConstString mangled) {
+ m_string = StringPool().GetConstCStringAndSetMangledCounterPart(
+ demangled, mangled.m_string);
+}
+
+bool ConstString::GetMangledCounterpart(ConstString &counterpart) const {
+ counterpart.m_string = StringPool().GetMangledCounterpart(m_string);
+ return (bool)counterpart;
+}
+
+void ConstString::SetCStringWithLength(const char *cstr, size_t cstr_len) {
+ m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len);
+}
+
+void ConstString::SetTrimmedCStringWithLength(const char *cstr,
+ size_t cstr_len) {
+ m_string = StringPool().GetConstTrimmedCStringWithLength(cstr, cstr_len);
+}
+
+size_t ConstString::StaticMemorySize() {
+ // Get the size of the static string pool
+ return StringPool().MemorySize();
+}
+
+void llvm::format_provider<ConstString>::format(const ConstString &CS,
+ llvm::raw_ostream &OS,
+ llvm::StringRef Options) {
+ format_provider<StringRef>::format(CS.AsCString(), OS, Options);
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/DataBufferHeap.cpp b/contrib/llvm-project/lldb/source/Utility/DataBufferHeap.cpp
new file mode 100644
index 000000000000..5bff7775f138
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/DataBufferHeap.cpp
@@ -0,0 +1,70 @@
+//===-- DataBufferHeap.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/DataBufferHeap.h"
+
+
+using namespace lldb_private;
+
+// Default constructor
+DataBufferHeap::DataBufferHeap() : m_data() {}
+
+// Initialize this class with "n" characters and fill the buffer with "ch".
+DataBufferHeap::DataBufferHeap(lldb::offset_t n, uint8_t ch) : m_data() {
+ if (n < m_data.max_size())
+ m_data.assign(n, ch);
+}
+
+// Initialize this class with a copy of the "n" bytes from the "bytes" buffer.
+DataBufferHeap::DataBufferHeap(const void *src, lldb::offset_t src_len)
+ : m_data() {
+ CopyData(src, src_len);
+}
+
+// Virtual destructor since this class inherits from a pure virtual base class.
+DataBufferHeap::~DataBufferHeap() = default;
+
+// Return a pointer to the bytes owned by this object, or nullptr if the object
+// contains no bytes.
+uint8_t *DataBufferHeap::GetBytes() {
+ return (m_data.empty() ? nullptr : m_data.data());
+}
+
+// Return a const pointer to the bytes owned by this object, or nullptr if the
+// object contains no bytes.
+const uint8_t *DataBufferHeap::GetBytes() const {
+ return (m_data.empty() ? nullptr : m_data.data());
+}
+
+// Return the number of bytes this object currently contains.
+uint64_t DataBufferHeap::GetByteSize() const { return m_data.size(); }
+
+// Sets the number of bytes that this object should be able to contain. This
+// can be used prior to copying data into the buffer.
+uint64_t DataBufferHeap::SetByteSize(uint64_t new_size) {
+ m_data.resize(new_size);
+ return m_data.size();
+}
+
+void DataBufferHeap::CopyData(const void *src, uint64_t src_len) {
+ const uint8_t *src_u8 = static_cast<const uint8_t *>(src);
+ if (src && src_len > 0)
+ m_data.assign(src_u8, src_u8 + src_len);
+ else
+ m_data.clear();
+}
+
+void DataBufferHeap::AppendData(const void *src, uint64_t src_len) {
+ m_data.insert(m_data.end(), static_cast<const uint8_t *>(src),
+ static_cast<const uint8_t *>(src) + src_len);
+}
+
+void DataBufferHeap::Clear() {
+ buffer_t empty;
+ m_data.swap(empty);
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/DataBufferLLVM.cpp b/contrib/llvm-project/lldb/source/Utility/DataBufferLLVM.cpp
new file mode 100644
index 000000000000..4227e9b39960
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/DataBufferLLVM.cpp
@@ -0,0 +1,39 @@
+//===--- DataBufferLLVM.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/DataBufferLLVM.h"
+
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include <assert.h>
+#include <type_traits>
+
+using namespace lldb_private;
+
+DataBufferLLVM::DataBufferLLVM(
+ std::unique_ptr<llvm::WritableMemoryBuffer> MemBuffer)
+ : Buffer(std::move(MemBuffer)) {
+ assert(Buffer != nullptr &&
+ "Cannot construct a DataBufferLLVM with a null buffer");
+}
+
+DataBufferLLVM::~DataBufferLLVM() {}
+
+uint8_t *DataBufferLLVM::GetBytes() {
+ return reinterpret_cast<uint8_t *>(Buffer->getBufferStart());
+}
+
+const uint8_t *DataBufferLLVM::GetBytes() const {
+ return reinterpret_cast<const uint8_t *>(Buffer->getBufferStart());
+}
+
+lldb::offset_t DataBufferLLVM::GetByteSize() const {
+ return Buffer->getBufferSize();
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/DataEncoder.cpp b/contrib/llvm-project/lldb/source/Utility/DataEncoder.cpp
new file mode 100644
index 000000000000..13c505e34e82
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/DataEncoder.cpp
@@ -0,0 +1,235 @@
+//===-- DataEncoder.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/DataEncoder.h"
+
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/Endian.h"
+
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MathExtras.h"
+
+#include <cassert>
+#include <cstddef>
+
+#include <string.h>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm::support::endian;
+
+// Default constructor.
+DataEncoder::DataEncoder()
+ : m_start(nullptr), m_end(nullptr),
+ m_byte_order(endian::InlHostByteOrder()), m_addr_size(sizeof(void *)),
+ m_data_sp() {}
+
+// This constructor allows us to use data that is owned by someone else. The
+// data must stay around as long as this object is valid.
+DataEncoder::DataEncoder(void *data, uint32_t length, ByteOrder endian,
+ uint8_t addr_size)
+ : m_start(static_cast<uint8_t *>(data)),
+ m_end(static_cast<uint8_t *>(data) + length), m_byte_order(endian),
+ m_addr_size(addr_size), m_data_sp() {}
+
+// Make a shared pointer reference to the shared data in "data_sp" and set the
+// endian swapping setting to "swap", and the address size to "addr_size". The
+// shared data reference will ensure the data lives as long as any DataEncoder
+// objects exist that have a reference to this data.
+DataEncoder::DataEncoder(const DataBufferSP &data_sp, ByteOrder endian,
+ uint8_t addr_size)
+ : m_start(nullptr), m_end(nullptr), m_byte_order(endian),
+ m_addr_size(addr_size), m_data_sp() {
+ SetData(data_sp);
+}
+
+DataEncoder::~DataEncoder() = default;
+
+// Clears the object contents back to a default invalid state, and release any
+// references to shared data that this object may contain.
+void DataEncoder::Clear() {
+ m_start = nullptr;
+ m_end = nullptr;
+ m_byte_order = endian::InlHostByteOrder();
+ m_addr_size = sizeof(void *);
+ m_data_sp.reset();
+}
+
+// If this object contains shared data, this function returns the offset into
+// that shared data. Else zero is returned.
+size_t DataEncoder::GetSharedDataOffset() const {
+ if (m_start != nullptr) {
+ const DataBuffer *data = m_data_sp.get();
+ if (data != nullptr) {
+ const uint8_t *data_bytes = data->GetBytes();
+ if (data_bytes != nullptr) {
+ assert(m_start >= data_bytes);
+ return m_start - data_bytes;
+ }
+ }
+ }
+ return 0;
+}
+
+// Set the data with which this object will extract from to data starting at
+// BYTES and set the length of the data to LENGTH bytes long. The data is
+// externally owned must be around at least as long as this object points to
+// the data. No copy of the data is made, this object just refers to this data
+// and can extract from it. If this object refers to any shared data upon
+// entry, the reference to that data will be released. Is SWAP is set to true,
+// any data extracted will be endian swapped.
+uint32_t DataEncoder::SetData(void *bytes, uint32_t length, ByteOrder endian) {
+ m_byte_order = endian;
+ m_data_sp.reset();
+ if (bytes == nullptr || length == 0) {
+ m_start = nullptr;
+ m_end = nullptr;
+ } else {
+ m_start = static_cast<uint8_t *>(bytes);
+ m_end = m_start + length;
+ }
+ return GetByteSize();
+}
+
+// Assign the data for this object to be a subrange of the shared data in
+// "data_sp" starting "data_offset" bytes into "data_sp" and ending
+// "data_length" bytes later. If "data_offset" is not a valid offset into
+// "data_sp", then this object will contain no bytes. If "data_offset" is
+// within "data_sp" yet "data_length" is too large, the length will be capped
+// at the number of bytes remaining in "data_sp". A ref counted pointer to the
+// data in "data_sp" will be made in this object IF the number of bytes this
+// object refers to in greater than zero (if at least one byte was available
+// starting at "data_offset") to ensure the data stays around as long as it is
+// needed. The address size and endian swap settings will remain unchanged from
+// their current settings.
+uint32_t DataEncoder::SetData(const DataBufferSP &data_sp, uint32_t data_offset,
+ uint32_t data_length) {
+ m_start = m_end = nullptr;
+
+ if (data_length > 0) {
+ m_data_sp = data_sp;
+ if (data_sp) {
+ const size_t data_size = data_sp->GetByteSize();
+ if (data_offset < data_size) {
+ m_start = data_sp->GetBytes() + data_offset;
+ const size_t bytes_left = data_size - data_offset;
+ // Cap the length of we asked for too many
+ if (data_length <= bytes_left)
+ m_end = m_start + data_length; // We got all the bytes we wanted
+ else
+ m_end = m_start + bytes_left; // Not all the bytes requested were
+ // available in the shared data
+ }
+ }
+ }
+
+ uint32_t new_size = GetByteSize();
+
+ // Don't hold a shared pointer to the data buffer if we don't share any valid
+ // bytes in the shared buffer.
+ if (new_size == 0)
+ m_data_sp.reset();
+
+ return new_size;
+}
+
+// Extract a single unsigned char from the binary data and update the offset
+// pointed to by "offset_ptr".
+//
+// RETURNS the byte that was extracted, or zero on failure.
+uint32_t DataEncoder::PutU8(uint32_t offset, uint8_t value) {
+ if (ValidOffset(offset)) {
+ m_start[offset] = value;
+ return offset + 1;
+ }
+ return UINT32_MAX;
+}
+
+uint32_t DataEncoder::PutU16(uint32_t offset, uint16_t value) {
+ if (ValidOffsetForDataOfSize(offset, sizeof(value))) {
+ if (m_byte_order != endian::InlHostByteOrder())
+ write16be(m_start + offset, value);
+ else
+ write16le(m_start + offset, value);
+
+ return offset + sizeof(value);
+ }
+ return UINT32_MAX;
+}
+
+uint32_t DataEncoder::PutU32(uint32_t offset, uint32_t value) {
+ if (ValidOffsetForDataOfSize(offset, sizeof(value))) {
+ if (m_byte_order != endian::InlHostByteOrder())
+ write32be(m_start + offset, value);
+ else
+ write32le(m_start + offset, value);
+
+ return offset + sizeof(value);
+ }
+ return UINT32_MAX;
+}
+
+uint32_t DataEncoder::PutU64(uint32_t offset, uint64_t value) {
+ if (ValidOffsetForDataOfSize(offset, sizeof(value))) {
+ if (m_byte_order != endian::InlHostByteOrder())
+ write64be(m_start + offset, value);
+ else
+ write64le(m_start + offset, value);
+
+ return offset + sizeof(value);
+ }
+ return UINT32_MAX;
+}
+
+// Extract a single integer value from the data and update the offset pointed
+// to by "offset_ptr". The size of the extracted integer is specified by the
+// "byte_size" argument. "byte_size" should have a value >= 1 and <= 8 since
+// the return value is only 64 bits wide. Any "byte_size" values less than 1 or
+// greater than 8 will result in nothing being extracted, and zero being
+// returned.
+//
+// RETURNS the integer value that was extracted, or zero on failure.
+uint32_t DataEncoder::PutMaxU64(uint32_t offset, uint32_t byte_size,
+ uint64_t value) {
+ switch (byte_size) {
+ case 1:
+ return PutU8(offset, value);
+ case 2:
+ return PutU16(offset, value);
+ case 4:
+ return PutU32(offset, value);
+ case 8:
+ return PutU64(offset, value);
+ default:
+ llvm_unreachable("GetMax64 unhandled case!");
+ }
+ return UINT32_MAX;
+}
+
+uint32_t DataEncoder::PutData(uint32_t offset, const void *src,
+ uint32_t src_len) {
+ if (src == nullptr || src_len == 0)
+ return offset;
+
+ if (ValidOffsetForDataOfSize(offset, src_len)) {
+ memcpy(m_start + offset, src, src_len);
+ return offset + src_len;
+ }
+ return UINT32_MAX;
+}
+
+uint32_t DataEncoder::PutAddress(uint32_t offset, lldb::addr_t addr) {
+ return PutMaxU64(offset, GetAddressByteSize(), addr);
+}
+
+uint32_t DataEncoder::PutCString(uint32_t offset, const char *cstr) {
+ if (cstr != nullptr)
+ return PutData(offset, cstr, strlen(cstr) + 1);
+ return UINT32_MAX;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/DataExtractor.cpp b/contrib/llvm-project/lldb/source/Utility/DataExtractor.cpp
new file mode 100644
index 000000000000..79a1f75d737c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/DataExtractor.cpp
@@ -0,0 +1,1121 @@
+//===-- DataExtractor.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/DataExtractor.h"
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-types.h"
+
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/UUID.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/MD5.h"
+#include "llvm/Support/MathExtras.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cstdint>
+#include <string>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <string.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static inline uint16_t ReadInt16(const unsigned char *ptr, offset_t offset) {
+ uint16_t value;
+ memcpy(&value, ptr + offset, 2);
+ return value;
+}
+
+static inline uint32_t ReadInt32(const unsigned char *ptr,
+ offset_t offset = 0) {
+ uint32_t value;
+ memcpy(&value, ptr + offset, 4);
+ return value;
+}
+
+static inline uint64_t ReadInt64(const unsigned char *ptr,
+ offset_t offset = 0) {
+ uint64_t value;
+ memcpy(&value, ptr + offset, 8);
+ return value;
+}
+
+static inline uint16_t ReadInt16(const void *ptr) {
+ uint16_t value;
+ memcpy(&value, ptr, 2);
+ return value;
+}
+
+static inline uint16_t ReadSwapInt16(const unsigned char *ptr,
+ offset_t offset) {
+ uint16_t value;
+ memcpy(&value, ptr + offset, 2);
+ return llvm::ByteSwap_16(value);
+}
+
+static inline uint32_t ReadSwapInt32(const unsigned char *ptr,
+ offset_t offset) {
+ uint32_t value;
+ memcpy(&value, ptr + offset, 4);
+ return llvm::ByteSwap_32(value);
+}
+
+static inline uint64_t ReadSwapInt64(const unsigned char *ptr,
+ offset_t offset) {
+ uint64_t value;
+ memcpy(&value, ptr + offset, 8);
+ return llvm::ByteSwap_64(value);
+}
+
+static inline uint16_t ReadSwapInt16(const void *ptr) {
+ uint16_t value;
+ memcpy(&value, ptr, 2);
+ return llvm::ByteSwap_16(value);
+}
+
+static inline uint32_t ReadSwapInt32(const void *ptr) {
+ uint32_t value;
+ memcpy(&value, ptr, 4);
+ return llvm::ByteSwap_32(value);
+}
+
+static inline uint64_t ReadSwapInt64(const void *ptr) {
+ uint64_t value;
+ memcpy(&value, ptr, 8);
+ return llvm::ByteSwap_64(value);
+}
+
+static inline uint64_t ReadMaxInt64(const uint8_t *data, size_t byte_size,
+ ByteOrder byte_order) {
+ uint64_t res = 0;
+ if (byte_order == eByteOrderBig)
+ for (size_t i = 0; i < byte_size; ++i)
+ res = (res << 8) | data[i];
+ else {
+ assert(byte_order == eByteOrderLittle);
+ for (size_t i = 0; i < byte_size; ++i)
+ res = (res << 8) | data[byte_size - 1 - i];
+ }
+ return res;
+}
+
+DataExtractor::DataExtractor()
+ : m_start(nullptr), m_end(nullptr),
+ m_byte_order(endian::InlHostByteOrder()), m_addr_size(sizeof(void *)),
+ m_data_sp(), m_target_byte_size(1) {}
+
+// This constructor allows us to use data that is owned by someone else. The
+// data must stay around as long as this object is valid.
+DataExtractor::DataExtractor(const void *data, offset_t length,
+ ByteOrder endian, uint32_t addr_size,
+ uint32_t target_byte_size /*=1*/)
+ : m_start(const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(data))),
+ m_end(const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(data)) +
+ length),
+ m_byte_order(endian), m_addr_size(addr_size), m_data_sp(),
+ m_target_byte_size(target_byte_size) {
+ assert(addr_size == 4 || addr_size == 8);
+}
+
+// Make a shared pointer reference to the shared data in "data_sp" and set the
+// endian swapping setting to "swap", and the address size to "addr_size". The
+// shared data reference will ensure the data lives as long as any
+// DataExtractor objects exist that have a reference to this data.
+DataExtractor::DataExtractor(const DataBufferSP &data_sp, ByteOrder endian,
+ uint32_t addr_size,
+ uint32_t target_byte_size /*=1*/)
+ : m_start(nullptr), m_end(nullptr), m_byte_order(endian),
+ m_addr_size(addr_size), m_data_sp(),
+ m_target_byte_size(target_byte_size) {
+ assert(addr_size == 4 || addr_size == 8);
+ SetData(data_sp);
+}
+
+// Initialize this object with a subset of the data bytes in "data". If "data"
+// contains shared data, then a reference to this shared data will added and
+// the shared data will stay around as long as any object contains a reference
+// to that data. The endian swap and address size settings are copied from
+// "data".
+DataExtractor::DataExtractor(const DataExtractor &data, offset_t offset,
+ offset_t length, uint32_t target_byte_size /*=1*/)
+ : m_start(nullptr), m_end(nullptr), m_byte_order(data.m_byte_order),
+ m_addr_size(data.m_addr_size), m_data_sp(),
+ m_target_byte_size(target_byte_size) {
+ assert(m_addr_size == 4 || m_addr_size == 8);
+ if (data.ValidOffset(offset)) {
+ offset_t bytes_available = data.GetByteSize() - offset;
+ if (length > bytes_available)
+ length = bytes_available;
+ SetData(data, offset, length);
+ }
+}
+
+DataExtractor::DataExtractor(const DataExtractor &rhs)
+ : m_start(rhs.m_start), m_end(rhs.m_end), m_byte_order(rhs.m_byte_order),
+ m_addr_size(rhs.m_addr_size), m_data_sp(rhs.m_data_sp),
+ m_target_byte_size(rhs.m_target_byte_size) {
+ assert(m_addr_size == 4 || m_addr_size == 8);
+}
+
+// Assignment operator
+const DataExtractor &DataExtractor::operator=(const DataExtractor &rhs) {
+ if (this != &rhs) {
+ m_start = rhs.m_start;
+ m_end = rhs.m_end;
+ m_byte_order = rhs.m_byte_order;
+ m_addr_size = rhs.m_addr_size;
+ m_data_sp = rhs.m_data_sp;
+ }
+ return *this;
+}
+
+DataExtractor::~DataExtractor() = default;
+
+// Clears the object contents back to a default invalid state, and release any
+// references to shared data that this object may contain.
+void DataExtractor::Clear() {
+ m_start = nullptr;
+ m_end = nullptr;
+ m_byte_order = endian::InlHostByteOrder();
+ m_addr_size = sizeof(void *);
+ m_data_sp.reset();
+}
+
+// If this object contains shared data, this function returns the offset into
+// that shared data. Else zero is returned.
+size_t DataExtractor::GetSharedDataOffset() const {
+ if (m_start != nullptr) {
+ const DataBuffer *data = m_data_sp.get();
+ if (data != nullptr) {
+ const uint8_t *data_bytes = data->GetBytes();
+ if (data_bytes != nullptr) {
+ assert(m_start >= data_bytes);
+ return m_start - data_bytes;
+ }
+ }
+ }
+ return 0;
+}
+
+// Set the data with which this object will extract from to data starting at
+// BYTES and set the length of the data to LENGTH bytes long. The data is
+// externally owned must be around at least as long as this object points to
+// the data. No copy of the data is made, this object just refers to this data
+// and can extract from it. If this object refers to any shared data upon
+// entry, the reference to that data will be released. Is SWAP is set to true,
+// any data extracted will be endian swapped.
+lldb::offset_t DataExtractor::SetData(const void *bytes, offset_t length,
+ ByteOrder endian) {
+ m_byte_order = endian;
+ m_data_sp.reset();
+ if (bytes == nullptr || length == 0) {
+ m_start = nullptr;
+ m_end = nullptr;
+ } else {
+ m_start = const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(bytes));
+ m_end = m_start + length;
+ }
+ return GetByteSize();
+}
+
+// Assign the data for this object to be a subrange in "data" starting
+// "data_offset" bytes into "data" and ending "data_length" bytes later. If
+// "data_offset" is not a valid offset into "data", then this object will
+// contain no bytes. If "data_offset" is within "data" yet "data_length" is too
+// large, the length will be capped at the number of bytes remaining in "data".
+// If "data" contains a shared pointer to other data, then a ref counted
+// pointer to that data will be made in this object. If "data" doesn't contain
+// a shared pointer to data, then the bytes referred to in "data" will need to
+// exist at least as long as this object refers to those bytes. The address
+// size and endian swap settings are copied from the current values in "data".
+lldb::offset_t DataExtractor::SetData(const DataExtractor &data,
+ offset_t data_offset,
+ offset_t data_length) {
+ m_addr_size = data.m_addr_size;
+ assert(m_addr_size == 4 || m_addr_size == 8);
+ // If "data" contains shared pointer to data, then we can use that
+ if (data.m_data_sp) {
+ m_byte_order = data.m_byte_order;
+ return SetData(data.m_data_sp, data.GetSharedDataOffset() + data_offset,
+ data_length);
+ }
+
+ // We have a DataExtractor object that just has a pointer to bytes
+ if (data.ValidOffset(data_offset)) {
+ if (data_length > data.GetByteSize() - data_offset)
+ data_length = data.GetByteSize() - data_offset;
+ return SetData(data.GetDataStart() + data_offset, data_length,
+ data.GetByteOrder());
+ }
+ return 0;
+}
+
+// Assign the data for this object to be a subrange of the shared data in
+// "data_sp" starting "data_offset" bytes into "data_sp" and ending
+// "data_length" bytes later. If "data_offset" is not a valid offset into
+// "data_sp", then this object will contain no bytes. If "data_offset" is
+// within "data_sp" yet "data_length" is too large, the length will be capped
+// at the number of bytes remaining in "data_sp". A ref counted pointer to the
+// data in "data_sp" will be made in this object IF the number of bytes this
+// object refers to in greater than zero (if at least one byte was available
+// starting at "data_offset") to ensure the data stays around as long as it is
+// needed. The address size and endian swap settings will remain unchanged from
+// their current settings.
+lldb::offset_t DataExtractor::SetData(const DataBufferSP &data_sp,
+ offset_t data_offset,
+ offset_t data_length) {
+ m_start = m_end = nullptr;
+
+ if (data_length > 0) {
+ m_data_sp = data_sp;
+ if (data_sp) {
+ const size_t data_size = data_sp->GetByteSize();
+ if (data_offset < data_size) {
+ m_start = data_sp->GetBytes() + data_offset;
+ const size_t bytes_left = data_size - data_offset;
+ // Cap the length of we asked for too many
+ if (data_length <= bytes_left)
+ m_end = m_start + data_length; // We got all the bytes we wanted
+ else
+ m_end = m_start + bytes_left; // Not all the bytes requested were
+ // available in the shared data
+ }
+ }
+ }
+
+ size_t new_size = GetByteSize();
+
+ // Don't hold a shared pointer to the data buffer if we don't share any valid
+ // bytes in the shared buffer.
+ if (new_size == 0)
+ m_data_sp.reset();
+
+ return new_size;
+}
+
+// Extract a single unsigned char from the binary data and update the offset
+// pointed to by "offset_ptr".
+//
+// RETURNS the byte that was extracted, or zero on failure.
+uint8_t DataExtractor::GetU8(offset_t *offset_ptr) const {
+ const uint8_t *data = static_cast<const uint8_t *>(GetData(offset_ptr, 1));
+ if (data)
+ return *data;
+ return 0;
+}
+
+// Extract "count" unsigned chars from the binary data and update the offset
+// pointed to by "offset_ptr". The extracted data is copied into "dst".
+//
+// RETURNS the non-nullptr buffer pointer upon successful extraction of
+// all the requested bytes, or nullptr when the data is not available in the
+// buffer due to being out of bounds, or insufficient data.
+void *DataExtractor::GetU8(offset_t *offset_ptr, void *dst,
+ uint32_t count) const {
+ const uint8_t *data =
+ static_cast<const uint8_t *>(GetData(offset_ptr, count));
+ if (data) {
+ // Copy the data into the buffer
+ memcpy(dst, data, count);
+ // Return a non-nullptr pointer to the converted data as an indicator of
+ // success
+ return dst;
+ }
+ return nullptr;
+}
+
+// Extract a single uint16_t from the data and update the offset pointed to by
+// "offset_ptr".
+//
+// RETURNS the uint16_t that was extracted, or zero on failure.
+uint16_t DataExtractor::GetU16(offset_t *offset_ptr) const {
+ uint16_t val = 0;
+ const uint8_t *data =
+ static_cast<const uint8_t *>(GetData(offset_ptr, sizeof(val)));
+ if (data) {
+ if (m_byte_order != endian::InlHostByteOrder())
+ val = ReadSwapInt16(data);
+ else
+ val = ReadInt16(data);
+ }
+ return val;
+}
+
+uint16_t DataExtractor::GetU16_unchecked(offset_t *offset_ptr) const {
+ uint16_t val;
+ if (m_byte_order == endian::InlHostByteOrder())
+ val = ReadInt16(m_start, *offset_ptr);
+ else
+ val = ReadSwapInt16(m_start, *offset_ptr);
+ *offset_ptr += sizeof(val);
+ return val;
+}
+
+uint32_t DataExtractor::GetU32_unchecked(offset_t *offset_ptr) const {
+ uint32_t val;
+ if (m_byte_order == endian::InlHostByteOrder())
+ val = ReadInt32(m_start, *offset_ptr);
+ else
+ val = ReadSwapInt32(m_start, *offset_ptr);
+ *offset_ptr += sizeof(val);
+ return val;
+}
+
+uint64_t DataExtractor::GetU64_unchecked(offset_t *offset_ptr) const {
+ uint64_t val;
+ if (m_byte_order == endian::InlHostByteOrder())
+ val = ReadInt64(m_start, *offset_ptr);
+ else
+ val = ReadSwapInt64(m_start, *offset_ptr);
+ *offset_ptr += sizeof(val);
+ return val;
+}
+
+// Extract "count" uint16_t values from the binary data and update the offset
+// pointed to by "offset_ptr". The extracted data is copied into "dst".
+//
+// RETURNS the non-nullptr buffer pointer upon successful extraction of
+// all the requested bytes, or nullptr when the data is not available in the
+// buffer due to being out of bounds, or insufficient data.
+void *DataExtractor::GetU16(offset_t *offset_ptr, void *void_dst,
+ uint32_t count) const {
+ const size_t src_size = sizeof(uint16_t) * count;
+ const uint16_t *src =
+ static_cast<const uint16_t *>(GetData(offset_ptr, src_size));
+ if (src) {
+ if (m_byte_order != endian::InlHostByteOrder()) {
+ uint16_t *dst_pos = static_cast<uint16_t *>(void_dst);
+ uint16_t *dst_end = dst_pos + count;
+ const uint16_t *src_pos = src;
+ while (dst_pos < dst_end) {
+ *dst_pos = ReadSwapInt16(src_pos);
+ ++dst_pos;
+ ++src_pos;
+ }
+ } else {
+ memcpy(void_dst, src, src_size);
+ }
+ // Return a non-nullptr pointer to the converted data as an indicator of
+ // success
+ return void_dst;
+ }
+ return nullptr;
+}
+
+// Extract a single uint32_t from the data and update the offset pointed to by
+// "offset_ptr".
+//
+// RETURNS the uint32_t that was extracted, or zero on failure.
+uint32_t DataExtractor::GetU32(offset_t *offset_ptr) const {
+ uint32_t val = 0;
+ const uint8_t *data =
+ static_cast<const uint8_t *>(GetData(offset_ptr, sizeof(val)));
+ if (data) {
+ if (m_byte_order != endian::InlHostByteOrder()) {
+ val = ReadSwapInt32(data);
+ } else {
+ memcpy(&val, data, 4);
+ }
+ }
+ return val;
+}
+
+// Extract "count" uint32_t values from the binary data and update the offset
+// pointed to by "offset_ptr". The extracted data is copied into "dst".
+//
+// RETURNS the non-nullptr buffer pointer upon successful extraction of
+// all the requested bytes, or nullptr when the data is not available in the
+// buffer due to being out of bounds, or insufficient data.
+void *DataExtractor::GetU32(offset_t *offset_ptr, void *void_dst,
+ uint32_t count) const {
+ const size_t src_size = sizeof(uint32_t) * count;
+ const uint32_t *src =
+ static_cast<const uint32_t *>(GetData(offset_ptr, src_size));
+ if (src) {
+ if (m_byte_order != endian::InlHostByteOrder()) {
+ uint32_t *dst_pos = static_cast<uint32_t *>(void_dst);
+ uint32_t *dst_end = dst_pos + count;
+ const uint32_t *src_pos = src;
+ while (dst_pos < dst_end) {
+ *dst_pos = ReadSwapInt32(src_pos);
+ ++dst_pos;
+ ++src_pos;
+ }
+ } else {
+ memcpy(void_dst, src, src_size);
+ }
+ // Return a non-nullptr pointer to the converted data as an indicator of
+ // success
+ return void_dst;
+ }
+ return nullptr;
+}
+
+// Extract a single uint64_t from the data and update the offset pointed to by
+// "offset_ptr".
+//
+// RETURNS the uint64_t that was extracted, or zero on failure.
+uint64_t DataExtractor::GetU64(offset_t *offset_ptr) const {
+ uint64_t val = 0;
+ const uint8_t *data =
+ static_cast<const uint8_t *>(GetData(offset_ptr, sizeof(val)));
+ if (data) {
+ if (m_byte_order != endian::InlHostByteOrder()) {
+ val = ReadSwapInt64(data);
+ } else {
+ memcpy(&val, data, 8);
+ }
+ }
+ return val;
+}
+
+// GetU64
+//
+// Get multiple consecutive 64 bit values. Return true if the entire read
+// succeeds and increment the offset pointed to by offset_ptr, else return
+// false and leave the offset pointed to by offset_ptr unchanged.
+void *DataExtractor::GetU64(offset_t *offset_ptr, void *void_dst,
+ uint32_t count) const {
+ const size_t src_size = sizeof(uint64_t) * count;
+ const uint64_t *src =
+ static_cast<const uint64_t *>(GetData(offset_ptr, src_size));
+ if (src) {
+ if (m_byte_order != endian::InlHostByteOrder()) {
+ uint64_t *dst_pos = static_cast<uint64_t *>(void_dst);
+ uint64_t *dst_end = dst_pos + count;
+ const uint64_t *src_pos = src;
+ while (dst_pos < dst_end) {
+ *dst_pos = ReadSwapInt64(src_pos);
+ ++dst_pos;
+ ++src_pos;
+ }
+ } else {
+ memcpy(void_dst, src, src_size);
+ }
+ // Return a non-nullptr pointer to the converted data as an indicator of
+ // success
+ return void_dst;
+ }
+ return nullptr;
+}
+
+uint32_t DataExtractor::GetMaxU32(offset_t *offset_ptr,
+ size_t byte_size) const {
+ lldbassert(byte_size > 0 && byte_size <= 4 && "GetMaxU32 invalid byte_size!");
+ return GetMaxU64(offset_ptr, byte_size);
+}
+
+uint64_t DataExtractor::GetMaxU64(offset_t *offset_ptr,
+ size_t byte_size) const {
+ lldbassert(byte_size > 0 && byte_size <= 8 && "GetMaxU64 invalid byte_size!");
+ switch (byte_size) {
+ case 1:
+ return GetU8(offset_ptr);
+ case 2:
+ return GetU16(offset_ptr);
+ case 4:
+ return GetU32(offset_ptr);
+ case 8:
+ return GetU64(offset_ptr);
+ default: {
+ // General case.
+ const uint8_t *data =
+ static_cast<const uint8_t *>(GetData(offset_ptr, byte_size));
+ if (data == nullptr)
+ return 0;
+ return ReadMaxInt64(data, byte_size, m_byte_order);
+ }
+ }
+ return 0;
+}
+
+uint64_t DataExtractor::GetMaxU64_unchecked(offset_t *offset_ptr,
+ size_t byte_size) const {
+ switch (byte_size) {
+ case 1:
+ return GetU8_unchecked(offset_ptr);
+ case 2:
+ return GetU16_unchecked(offset_ptr);
+ case 4:
+ return GetU32_unchecked(offset_ptr);
+ case 8:
+ return GetU64_unchecked(offset_ptr);
+ default: {
+ uint64_t res = ReadMaxInt64(&m_start[*offset_ptr], byte_size, m_byte_order);
+ *offset_ptr += byte_size;
+ return res;
+ }
+ }
+ return 0;
+}
+
+int64_t DataExtractor::GetMaxS64(offset_t *offset_ptr, size_t byte_size) const {
+ uint64_t u64 = GetMaxU64(offset_ptr, byte_size);
+ return llvm::SignExtend64(u64, 8 * byte_size);
+}
+
+uint64_t DataExtractor::GetMaxU64Bitfield(offset_t *offset_ptr, size_t size,
+ uint32_t bitfield_bit_size,
+ uint32_t bitfield_bit_offset) const {
+ uint64_t uval64 = GetMaxU64(offset_ptr, size);
+ if (bitfield_bit_size > 0) {
+ int32_t lsbcount = bitfield_bit_offset;
+ if (m_byte_order == eByteOrderBig)
+ lsbcount = size * 8 - bitfield_bit_offset - bitfield_bit_size;
+ if (lsbcount > 0)
+ uval64 >>= lsbcount;
+ uint64_t bitfield_mask = ((1ul << bitfield_bit_size) - 1);
+ if (!bitfield_mask && bitfield_bit_offset == 0 && bitfield_bit_size == 64)
+ return uval64;
+ uval64 &= bitfield_mask;
+ }
+ return uval64;
+}
+
+int64_t DataExtractor::GetMaxS64Bitfield(offset_t *offset_ptr, size_t size,
+ uint32_t bitfield_bit_size,
+ uint32_t bitfield_bit_offset) const {
+ int64_t sval64 = GetMaxS64(offset_ptr, size);
+ if (bitfield_bit_size > 0) {
+ int32_t lsbcount = bitfield_bit_offset;
+ if (m_byte_order == eByteOrderBig)
+ lsbcount = size * 8 - bitfield_bit_offset - bitfield_bit_size;
+ if (lsbcount > 0)
+ sval64 >>= lsbcount;
+ uint64_t bitfield_mask =
+ ((static_cast<uint64_t>(1)) << bitfield_bit_size) - 1;
+ sval64 &= bitfield_mask;
+ // sign extend if needed
+ if (sval64 & ((static_cast<uint64_t>(1)) << (bitfield_bit_size - 1)))
+ sval64 |= ~bitfield_mask;
+ }
+ return sval64;
+}
+
+float DataExtractor::GetFloat(offset_t *offset_ptr) const {
+ typedef float float_type;
+ float_type val = 0.0;
+ const size_t src_size = sizeof(float_type);
+ const float_type *src =
+ static_cast<const float_type *>(GetData(offset_ptr, src_size));
+ if (src) {
+ if (m_byte_order != endian::InlHostByteOrder()) {
+ const uint8_t *src_data = reinterpret_cast<const uint8_t *>(src);
+ uint8_t *dst_data = reinterpret_cast<uint8_t *>(&val);
+ for (size_t i = 0; i < sizeof(float_type); ++i)
+ dst_data[sizeof(float_type) - 1 - i] = src_data[i];
+ } else {
+ val = *src;
+ }
+ }
+ return val;
+}
+
+double DataExtractor::GetDouble(offset_t *offset_ptr) const {
+ typedef double float_type;
+ float_type val = 0.0;
+ const size_t src_size = sizeof(float_type);
+ const float_type *src =
+ static_cast<const float_type *>(GetData(offset_ptr, src_size));
+ if (src) {
+ if (m_byte_order != endian::InlHostByteOrder()) {
+ const uint8_t *src_data = reinterpret_cast<const uint8_t *>(src);
+ uint8_t *dst_data = reinterpret_cast<uint8_t *>(&val);
+ for (size_t i = 0; i < sizeof(float_type); ++i)
+ dst_data[sizeof(float_type) - 1 - i] = src_data[i];
+ } else {
+ val = *src;
+ }
+ }
+ return val;
+}
+
+long double DataExtractor::GetLongDouble(offset_t *offset_ptr) const {
+ long double val = 0.0;
+#if defined(__i386__) || defined(__amd64__) || defined(__x86_64__) || \
+ defined(_M_IX86) || defined(_M_IA64) || defined(_M_X64)
+ *offset_ptr += CopyByteOrderedData(*offset_ptr, 10, &val, sizeof(val),
+ endian::InlHostByteOrder());
+#else
+ *offset_ptr += CopyByteOrderedData(*offset_ptr, sizeof(val), &val,
+ sizeof(val), endian::InlHostByteOrder());
+#endif
+ return val;
+}
+
+// Extract a single address from the data and update the offset pointed to by
+// "offset_ptr". The size of the extracted address comes from the
+// "this->m_addr_size" member variable and should be set correctly prior to
+// extracting any address values.
+//
+// RETURNS the address that was extracted, or zero on failure.
+uint64_t DataExtractor::GetAddress(offset_t *offset_ptr) const {
+ assert(m_addr_size == 4 || m_addr_size == 8);
+ return GetMaxU64(offset_ptr, m_addr_size);
+}
+
+uint64_t DataExtractor::GetAddress_unchecked(offset_t *offset_ptr) const {
+ assert(m_addr_size == 4 || m_addr_size == 8);
+ return GetMaxU64_unchecked(offset_ptr, m_addr_size);
+}
+
+// Extract a single pointer from the data and update the offset pointed to by
+// "offset_ptr". The size of the extracted pointer comes from the
+// "this->m_addr_size" member variable and should be set correctly prior to
+// extracting any pointer values.
+//
+// RETURNS the pointer that was extracted, or zero on failure.
+uint64_t DataExtractor::GetPointer(offset_t *offset_ptr) const {
+ assert(m_addr_size == 4 || m_addr_size == 8);
+ return GetMaxU64(offset_ptr, m_addr_size);
+}
+
+size_t DataExtractor::ExtractBytes(offset_t offset, offset_t length,
+ ByteOrder dst_byte_order, void *dst) const {
+ const uint8_t *src = PeekData(offset, length);
+ if (src) {
+ if (dst_byte_order != GetByteOrder()) {
+ // Validate that only a word- or register-sized dst is byte swapped
+ assert(length == 1 || length == 2 || length == 4 || length == 8 ||
+ length == 10 || length == 16 || length == 32);
+
+ for (uint32_t i = 0; i < length; ++i)
+ (static_cast<uint8_t *>(dst))[i] = src[length - i - 1];
+ } else
+ ::memcpy(dst, src, length);
+ return length;
+ }
+ return 0;
+}
+
+// Extract data as it exists in target memory
+lldb::offset_t DataExtractor::CopyData(offset_t offset, offset_t length,
+ void *dst) const {
+ const uint8_t *src = PeekData(offset, length);
+ if (src) {
+ ::memcpy(dst, src, length);
+ return length;
+ }
+ return 0;
+}
+
+// Extract data and swap if needed when doing the copy
+lldb::offset_t
+DataExtractor::CopyByteOrderedData(offset_t src_offset, offset_t src_len,
+ void *dst_void_ptr, offset_t dst_len,
+ ByteOrder dst_byte_order) const {
+ // Validate the source info
+ if (!ValidOffsetForDataOfSize(src_offset, src_len))
+ assert(ValidOffsetForDataOfSize(src_offset, src_len));
+ assert(src_len > 0);
+ assert(m_byte_order == eByteOrderBig || m_byte_order == eByteOrderLittle);
+
+ // Validate the destination info
+ assert(dst_void_ptr != nullptr);
+ assert(dst_len > 0);
+ assert(dst_byte_order == eByteOrderBig || dst_byte_order == eByteOrderLittle);
+
+ // Validate that only a word- or register-sized dst is byte swapped
+ assert(dst_byte_order == m_byte_order || dst_len == 1 || dst_len == 2 ||
+ dst_len == 4 || dst_len == 8 || dst_len == 10 || dst_len == 16 ||
+ dst_len == 32);
+
+ // Must have valid byte orders set in this object and for destination
+ if (!(dst_byte_order == eByteOrderBig ||
+ dst_byte_order == eByteOrderLittle) ||
+ !(m_byte_order == eByteOrderBig || m_byte_order == eByteOrderLittle))
+ return 0;
+
+ uint8_t *dst = static_cast<uint8_t *>(dst_void_ptr);
+ const uint8_t *src = PeekData(src_offset, src_len);
+ if (src) {
+ if (dst_len >= src_len) {
+ // We are copying the entire value from src into dst. Calculate how many,
+ // if any, zeroes we need for the most significant bytes if "dst_len" is
+ // greater than "src_len"...
+ const size_t num_zeroes = dst_len - src_len;
+ if (dst_byte_order == eByteOrderBig) {
+ // Big endian, so we lead with zeroes...
+ if (num_zeroes > 0)
+ ::memset(dst, 0, num_zeroes);
+ // Then either copy or swap the rest
+ if (m_byte_order == eByteOrderBig) {
+ ::memcpy(dst + num_zeroes, src, src_len);
+ } else {
+ for (uint32_t i = 0; i < src_len; ++i)
+ dst[i + num_zeroes] = src[src_len - 1 - i];
+ }
+ } else {
+ // Little endian destination, so we lead the value bytes
+ if (m_byte_order == eByteOrderBig) {
+ for (uint32_t i = 0; i < src_len; ++i)
+ dst[i] = src[src_len - 1 - i];
+ } else {
+ ::memcpy(dst, src, src_len);
+ }
+ // And zero the rest...
+ if (num_zeroes > 0)
+ ::memset(dst + src_len, 0, num_zeroes);
+ }
+ return src_len;
+ } else {
+ // We are only copying some of the value from src into dst..
+
+ if (dst_byte_order == eByteOrderBig) {
+ // Big endian dst
+ if (m_byte_order == eByteOrderBig) {
+ // Big endian dst, with big endian src
+ ::memcpy(dst, src + (src_len - dst_len), dst_len);
+ } else {
+ // Big endian dst, with little endian src
+ for (uint32_t i = 0; i < dst_len; ++i)
+ dst[i] = src[dst_len - 1 - i];
+ }
+ } else {
+ // Little endian dst
+ if (m_byte_order == eByteOrderBig) {
+ // Little endian dst, with big endian src
+ for (uint32_t i = 0; i < dst_len; ++i)
+ dst[i] = src[src_len - 1 - i];
+ } else {
+ // Little endian dst, with big endian src
+ ::memcpy(dst, src, dst_len);
+ }
+ }
+ return dst_len;
+ }
+ }
+ return 0;
+}
+
+// Extracts a variable length NULL terminated C string from the data at the
+// offset pointed to by "offset_ptr". The "offset_ptr" will be updated with
+// the offset of the byte that follows the NULL terminator byte.
+//
+// If the offset pointed to by "offset_ptr" is out of bounds, or if "length" is
+// non-zero and there aren't enough available bytes, nullptr will be returned
+// and "offset_ptr" will not be updated.
+const char *DataExtractor::GetCStr(offset_t *offset_ptr) const {
+ const char *cstr = reinterpret_cast<const char *>(PeekData(*offset_ptr, 1));
+ if (cstr) {
+ const char *cstr_end = cstr;
+ const char *end = reinterpret_cast<const char *>(m_end);
+ while (cstr_end < end && *cstr_end)
+ ++cstr_end;
+
+ // Now we are either at the end of the data or we point to the
+ // NULL C string terminator with cstr_end...
+ if (*cstr_end == '\0') {
+ // Advance the offset with one extra byte for the NULL terminator
+ *offset_ptr += (cstr_end - cstr + 1);
+ return cstr;
+ }
+
+ // We reached the end of the data without finding a NULL C string
+ // terminator. Fall through and return nullptr otherwise anyone that would
+ // have used the result as a C string can wander into unknown memory...
+ }
+ return nullptr;
+}
+
+// Extracts a NULL terminated C string from the fixed length field of length
+// "len" at the offset pointed to by "offset_ptr". The "offset_ptr" will be
+// updated with the offset of the byte that follows the fixed length field.
+//
+// If the offset pointed to by "offset_ptr" is out of bounds, or if the offset
+// plus the length of the field is out of bounds, or if the field does not
+// contain a NULL terminator byte, nullptr will be returned and "offset_ptr"
+// will not be updated.
+const char *DataExtractor::GetCStr(offset_t *offset_ptr, offset_t len) const {
+ const char *cstr = reinterpret_cast<const char *>(PeekData(*offset_ptr, len));
+ if (cstr != nullptr) {
+ if (memchr(cstr, '\0', len) == nullptr) {
+ return nullptr;
+ }
+ *offset_ptr += len;
+ return cstr;
+ }
+ return nullptr;
+}
+
+// Peeks at a string in the contained data. No verification is done to make
+// sure the entire string lies within the bounds of this object's data, only
+// "offset" is verified to be a valid offset.
+//
+// Returns a valid C string pointer if "offset" is a valid offset in this
+// object's data, else nullptr is returned.
+const char *DataExtractor::PeekCStr(offset_t offset) const {
+ return reinterpret_cast<const char *>(PeekData(offset, 1));
+}
+
+// Extracts an unsigned LEB128 number from this object's data starting at the
+// offset pointed to by "offset_ptr". The offset pointed to by "offset_ptr"
+// will be updated with the offset of the byte following the last extracted
+// byte.
+//
+// Returned the extracted integer value.
+uint64_t DataExtractor::GetULEB128(offset_t *offset_ptr) const {
+ const uint8_t *src = PeekData(*offset_ptr, 1);
+ if (src == nullptr)
+ return 0;
+
+ const uint8_t *end = m_end;
+
+ if (src < end) {
+ uint64_t result = *src++;
+ if (result >= 0x80) {
+ result &= 0x7f;
+ int shift = 7;
+ while (src < end) {
+ uint8_t byte = *src++;
+ result |= static_cast<uint64_t>(byte & 0x7f) << shift;
+ if ((byte & 0x80) == 0)
+ break;
+ shift += 7;
+ }
+ }
+ *offset_ptr = src - m_start;
+ return result;
+ }
+
+ return 0;
+}
+
+// Extracts an signed LEB128 number from this object's data starting at the
+// offset pointed to by "offset_ptr". The offset pointed to by "offset_ptr"
+// will be updated with the offset of the byte following the last extracted
+// byte.
+//
+// Returned the extracted integer value.
+int64_t DataExtractor::GetSLEB128(offset_t *offset_ptr) const {
+ const uint8_t *src = PeekData(*offset_ptr, 1);
+ if (src == nullptr)
+ return 0;
+
+ const uint8_t *end = m_end;
+
+ if (src < end) {
+ int64_t result = 0;
+ int shift = 0;
+ int size = sizeof(int64_t) * 8;
+
+ uint8_t byte = 0;
+ int bytecount = 0;
+
+ while (src < end) {
+ bytecount++;
+ byte = *src++;
+ result |= static_cast<int64_t>(byte & 0x7f) << shift;
+ shift += 7;
+ if ((byte & 0x80) == 0)
+ break;
+ }
+
+ // Sign bit of byte is 2nd high order bit (0x40)
+ if (shift < size && (byte & 0x40))
+ result |= -(1 << shift);
+
+ *offset_ptr += bytecount;
+ return result;
+ }
+ return 0;
+}
+
+// Skips a ULEB128 number (signed or unsigned) from this object's data starting
+// at the offset pointed to by "offset_ptr". The offset pointed to by
+// "offset_ptr" will be updated with the offset of the byte following the last
+// extracted byte.
+//
+// Returns the number of bytes consumed during the extraction.
+uint32_t DataExtractor::Skip_LEB128(offset_t *offset_ptr) const {
+ uint32_t bytes_consumed = 0;
+ const uint8_t *src = PeekData(*offset_ptr, 1);
+ if (src == nullptr)
+ return 0;
+
+ const uint8_t *end = m_end;
+
+ if (src < end) {
+ const uint8_t *src_pos = src;
+ while ((src_pos < end) && (*src_pos++ & 0x80))
+ ++bytes_consumed;
+ *offset_ptr += src_pos - src;
+ }
+ return bytes_consumed;
+}
+
+// Dumps bytes from this object's data to the stream "s" starting
+// "start_offset" bytes into this data, and ending with the byte before
+// "end_offset". "base_addr" will be added to the offset into the dumped data
+// when showing the offset into the data in the output information.
+// "num_per_line" objects of type "type" will be dumped with the option to
+// override the format for each object with "type_format". "type_format" is a
+// printf style formatting string. If "type_format" is nullptr, then an
+// appropriate format string will be used for the supplied "type". If the
+// stream "s" is nullptr, then the output will be send to Log().
+lldb::offset_t DataExtractor::PutToLog(Log *log, offset_t start_offset,
+ offset_t length, uint64_t base_addr,
+ uint32_t num_per_line,
+ DataExtractor::Type type,
+ const char *format) const {
+ if (log == nullptr)
+ return start_offset;
+
+ offset_t offset;
+ offset_t end_offset;
+ uint32_t count;
+ StreamString sstr;
+ for (offset = start_offset, end_offset = offset + length, count = 0;
+ ValidOffset(offset) && offset < end_offset; ++count) {
+ if ((count % num_per_line) == 0) {
+ // Print out any previous string
+ if (sstr.GetSize() > 0) {
+ log->PutString(sstr.GetString());
+ sstr.Clear();
+ }
+ // Reset string offset and fill the current line string with address:
+ if (base_addr != LLDB_INVALID_ADDRESS)
+ sstr.Printf("0x%8.8" PRIx64 ":",
+ static_cast<uint64_t>(base_addr + (offset - start_offset)));
+ }
+
+ switch (type) {
+ case TypeUInt8:
+ sstr.Printf(format ? format : " %2.2x", GetU8(&offset));
+ break;
+ case TypeChar: {
+ char ch = GetU8(&offset);
+ sstr.Printf(format ? format : " %c", isprint(ch) ? ch : ' ');
+ } break;
+ case TypeUInt16:
+ sstr.Printf(format ? format : " %4.4x", GetU16(&offset));
+ break;
+ case TypeUInt32:
+ sstr.Printf(format ? format : " %8.8x", GetU32(&offset));
+ break;
+ case TypeUInt64:
+ sstr.Printf(format ? format : " %16.16" PRIx64, GetU64(&offset));
+ break;
+ case TypePointer:
+ sstr.Printf(format ? format : " 0x%" PRIx64, GetAddress(&offset));
+ break;
+ case TypeULEB128:
+ sstr.Printf(format ? format : " 0x%" PRIx64, GetULEB128(&offset));
+ break;
+ case TypeSLEB128:
+ sstr.Printf(format ? format : " %" PRId64, GetSLEB128(&offset));
+ break;
+ }
+ }
+
+ if (!sstr.Empty())
+ log->PutString(sstr.GetString());
+
+ return offset; // Return the offset at which we ended up
+}
+
+size_t DataExtractor::Copy(DataExtractor &dest_data) const {
+ if (m_data_sp) {
+ // we can pass along the SP to the data
+ dest_data.SetData(m_data_sp);
+ } else {
+ const uint8_t *base_ptr = m_start;
+ size_t data_size = GetByteSize();
+ dest_data.SetData(DataBufferSP(new DataBufferHeap(base_ptr, data_size)));
+ }
+ return GetByteSize();
+}
+
+bool DataExtractor::Append(DataExtractor &rhs) {
+ if (rhs.GetByteOrder() != GetByteOrder())
+ return false;
+
+ if (rhs.GetByteSize() == 0)
+ return true;
+
+ if (GetByteSize() == 0)
+ return (rhs.Copy(*this) > 0);
+
+ size_t bytes = GetByteSize() + rhs.GetByteSize();
+
+ DataBufferHeap *buffer_heap_ptr = nullptr;
+ DataBufferSP buffer_sp(buffer_heap_ptr = new DataBufferHeap(bytes, 0));
+
+ if (!buffer_sp || buffer_heap_ptr == nullptr)
+ return false;
+
+ uint8_t *bytes_ptr = buffer_heap_ptr->GetBytes();
+
+ memcpy(bytes_ptr, GetDataStart(), GetByteSize());
+ memcpy(bytes_ptr + GetByteSize(), rhs.GetDataStart(), rhs.GetByteSize());
+
+ SetData(buffer_sp);
+
+ return true;
+}
+
+bool DataExtractor::Append(void *buf, offset_t length) {
+ if (buf == nullptr)
+ return false;
+
+ if (length == 0)
+ return true;
+
+ size_t bytes = GetByteSize() + length;
+
+ DataBufferHeap *buffer_heap_ptr = nullptr;
+ DataBufferSP buffer_sp(buffer_heap_ptr = new DataBufferHeap(bytes, 0));
+
+ if (!buffer_sp || buffer_heap_ptr == nullptr)
+ return false;
+
+ uint8_t *bytes_ptr = buffer_heap_ptr->GetBytes();
+
+ if (GetByteSize() > 0)
+ memcpy(bytes_ptr, GetDataStart(), GetByteSize());
+
+ memcpy(bytes_ptr + GetByteSize(), buf, length);
+
+ SetData(buffer_sp);
+
+ return true;
+}
+
+void DataExtractor::Checksum(llvm::SmallVectorImpl<uint8_t> &dest,
+ uint64_t max_data) {
+ if (max_data == 0)
+ max_data = GetByteSize();
+ else
+ max_data = std::min(max_data, GetByteSize());
+
+ llvm::MD5 md5;
+
+ const llvm::ArrayRef<uint8_t> data(GetDataStart(), max_data);
+ md5.update(data);
+
+ llvm::MD5::MD5Result result;
+ md5.final(result);
+
+ dest.clear();
+ dest.append(result.Bytes.begin(), result.Bytes.end());
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Environment.cpp b/contrib/llvm-project/lldb/source/Utility/Environment.cpp
new file mode 100644
index 000000000000..140533600712
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Environment.cpp
@@ -0,0 +1,49 @@
+//===-- Environment.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Environment.h"
+
+using namespace lldb_private;
+
+char *Environment::Envp::make_entry(llvm::StringRef Key,
+ llvm::StringRef Value) {
+ const size_t size = Key.size() + 1 /*=*/ + Value.size() + 1 /*\0*/;
+ char *Result = reinterpret_cast<char *>(
+ Allocator.Allocate(sizeof(char) * size, alignof(char)));
+ char *Next = Result;
+
+ Next = std::copy(Key.begin(), Key.end(), Next);
+ *Next++ = '=';
+ Next = std::copy(Value.begin(), Value.end(), Next);
+ *Next++ = '\0';
+
+ return Result;
+}
+
+Environment::Envp::Envp(const Environment &Env) {
+ Data = reinterpret_cast<char **>(
+ Allocator.Allocate(sizeof(char *) * (Env.size() + 1), alignof(char *)));
+ char **Next = Data;
+ for (const auto &KV : Env)
+ *Next++ = make_entry(KV.first(), KV.second);
+ *Next++ = nullptr;
+}
+
+Environment::Environment(const char *const *Env) {
+ if (!Env)
+ return;
+ while (*Env)
+ insert(*Env++);
+}
+
+void Environment::insert(const_iterator first, const_iterator last) {
+ while (first != last) {
+ try_emplace(first->first(), first->second);
+ ++first;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Event.cpp b/contrib/llvm-project/lldb/source/Utility/Event.cpp
new file mode 100644
index 000000000000..579d0dac86ed
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Event.cpp
@@ -0,0 +1,288 @@
+//===-- Event.cpp -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Event.h"
+
+#include "lldb/Utility/Broadcaster.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-enumerations.h"
+
+#include <algorithm>
+
+#include <ctype.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark -
+#pragma mark Event
+
+// Event functions
+
+Event::Event(Broadcaster *broadcaster, uint32_t event_type, EventData *data)
+ : m_broadcaster_wp(broadcaster->GetBroadcasterImpl()), m_type(event_type),
+ m_data_sp(data) {}
+
+Event::Event(Broadcaster *broadcaster, uint32_t event_type,
+ const EventDataSP &event_data_sp)
+ : m_broadcaster_wp(broadcaster->GetBroadcasterImpl()), m_type(event_type),
+ m_data_sp(event_data_sp) {}
+
+Event::Event(uint32_t event_type, EventData *data)
+ : m_broadcaster_wp(), m_type(event_type), m_data_sp(data) {}
+
+Event::Event(uint32_t event_type, const EventDataSP &event_data_sp)
+ : m_broadcaster_wp(), m_type(event_type), m_data_sp(event_data_sp) {}
+
+Event::~Event() = default;
+
+void Event::Dump(Stream *s) const {
+ Broadcaster *broadcaster;
+ Broadcaster::BroadcasterImplSP broadcaster_impl_sp(m_broadcaster_wp.lock());
+ if (broadcaster_impl_sp)
+ broadcaster = broadcaster_impl_sp->GetBroadcaster();
+ else
+ broadcaster = nullptr;
+
+ if (broadcaster) {
+ StreamString event_name;
+ if (broadcaster->GetEventNames(event_name, m_type, false))
+ s->Printf("%p Event: broadcaster = %p (%s), type = 0x%8.8x (%s), data = ",
+ static_cast<const void *>(this),
+ static_cast<void *>(broadcaster),
+ broadcaster->GetBroadcasterName().GetCString(), m_type,
+ event_name.GetData());
+ else
+ s->Printf("%p Event: broadcaster = %p (%s), type = 0x%8.8x, data = ",
+ static_cast<const void *>(this),
+ static_cast<void *>(broadcaster),
+ broadcaster->GetBroadcasterName().GetCString(), m_type);
+ } else
+ s->Printf("%p Event: broadcaster = NULL, type = 0x%8.8x, data = ",
+ static_cast<const void *>(this), m_type);
+
+ if (m_data_sp) {
+ s->PutChar('{');
+ m_data_sp->Dump(s);
+ s->PutChar('}');
+ } else
+ s->Printf("<NULL>");
+}
+
+void Event::DoOnRemoval() {
+ if (m_data_sp)
+ m_data_sp->DoOnRemoval(this);
+}
+
+#pragma mark -
+#pragma mark EventData
+
+// EventData functions
+
+EventData::EventData() = default;
+
+EventData::~EventData() = default;
+
+void EventData::Dump(Stream *s) const { s->PutCString("Generic Event Data"); }
+
+#pragma mark -
+#pragma mark EventDataBytes
+
+// EventDataBytes functions
+
+EventDataBytes::EventDataBytes() : m_bytes() {}
+
+EventDataBytes::EventDataBytes(const char *cstr) : m_bytes() {
+ SetBytesFromCString(cstr);
+}
+
+EventDataBytes::EventDataBytes(llvm::StringRef str) : m_bytes() {
+ SetBytes(str.data(), str.size());
+}
+
+EventDataBytes::EventDataBytes(const void *src, size_t src_len) : m_bytes() {
+ SetBytes(src, src_len);
+}
+
+EventDataBytes::~EventDataBytes() = default;
+
+ConstString EventDataBytes::GetFlavorString() {
+ static ConstString g_flavor("EventDataBytes");
+ return g_flavor;
+}
+
+ConstString EventDataBytes::GetFlavor() const {
+ return EventDataBytes::GetFlavorString();
+}
+
+void EventDataBytes::Dump(Stream *s) const {
+ size_t num_printable_chars =
+ std::count_if(m_bytes.begin(), m_bytes.end(), isprint);
+ if (num_printable_chars == m_bytes.size())
+ s->Format("\"{0}\"", m_bytes);
+ else
+ s->Format("{0:$[ ]@[x-2]}", llvm::make_range(
+ reinterpret_cast<const uint8_t *>(m_bytes.data()),
+ reinterpret_cast<const uint8_t *>(m_bytes.data() +
+ m_bytes.size())));
+}
+
+const void *EventDataBytes::GetBytes() const {
+ return (m_bytes.empty() ? nullptr : m_bytes.data());
+}
+
+size_t EventDataBytes::GetByteSize() const { return m_bytes.size(); }
+
+void EventDataBytes::SetBytes(const void *src, size_t src_len) {
+ if (src != nullptr && src_len > 0)
+ m_bytes.assign(static_cast<const char *>(src), src_len);
+ else
+ m_bytes.clear();
+}
+
+void EventDataBytes::SetBytesFromCString(const char *cstr) {
+ if (cstr != nullptr && cstr[0])
+ m_bytes.assign(cstr);
+ else
+ m_bytes.clear();
+}
+
+const void *EventDataBytes::GetBytesFromEvent(const Event *event_ptr) {
+ const EventDataBytes *e = GetEventDataFromEvent(event_ptr);
+ if (e != nullptr)
+ return e->GetBytes();
+ return nullptr;
+}
+
+size_t EventDataBytes::GetByteSizeFromEvent(const Event *event_ptr) {
+ const EventDataBytes *e = GetEventDataFromEvent(event_ptr);
+ if (e != nullptr)
+ return e->GetByteSize();
+ return 0;
+}
+
+const EventDataBytes *
+EventDataBytes::GetEventDataFromEvent(const Event *event_ptr) {
+ if (event_ptr != nullptr) {
+ const EventData *event_data = event_ptr->GetData();
+ if (event_data &&
+ event_data->GetFlavor() == EventDataBytes::GetFlavorString())
+ return static_cast<const EventDataBytes *>(event_data);
+ }
+ return nullptr;
+}
+
+void EventDataBytes::SwapBytes(std::string &new_bytes) {
+ m_bytes.swap(new_bytes);
+}
+
+#pragma mark -
+#pragma mark EventStructuredData
+
+// EventDataStructuredData definitions
+
+EventDataStructuredData::EventDataStructuredData()
+ : EventData(), m_process_sp(), m_object_sp(), m_plugin_sp() {}
+
+EventDataStructuredData::EventDataStructuredData(
+ const ProcessSP &process_sp, const StructuredData::ObjectSP &object_sp,
+ const lldb::StructuredDataPluginSP &plugin_sp)
+ : EventData(), m_process_sp(process_sp), m_object_sp(object_sp),
+ m_plugin_sp(plugin_sp) {}
+
+EventDataStructuredData::~EventDataStructuredData() {}
+
+// EventDataStructuredData member functions
+
+ConstString EventDataStructuredData::GetFlavor() const {
+ return EventDataStructuredData::GetFlavorString();
+}
+
+void EventDataStructuredData::Dump(Stream *s) const {
+ if (!s)
+ return;
+
+ if (m_object_sp)
+ m_object_sp->Dump(*s);
+}
+
+const ProcessSP &EventDataStructuredData::GetProcess() const {
+ return m_process_sp;
+}
+
+const StructuredData::ObjectSP &EventDataStructuredData::GetObject() const {
+ return m_object_sp;
+}
+
+const lldb::StructuredDataPluginSP &
+EventDataStructuredData::GetStructuredDataPlugin() const {
+ return m_plugin_sp;
+}
+
+void EventDataStructuredData::SetProcess(const ProcessSP &process_sp) {
+ m_process_sp = process_sp;
+}
+
+void EventDataStructuredData::SetObject(
+ const StructuredData::ObjectSP &object_sp) {
+ m_object_sp = object_sp;
+}
+
+void EventDataStructuredData::SetStructuredDataPlugin(
+ const lldb::StructuredDataPluginSP &plugin_sp) {
+ m_plugin_sp = plugin_sp;
+}
+
+// EventDataStructuredData static functions
+
+const EventDataStructuredData *
+EventDataStructuredData::GetEventDataFromEvent(const Event *event_ptr) {
+ if (event_ptr == nullptr)
+ return nullptr;
+
+ const EventData *event_data = event_ptr->GetData();
+ if (!event_data ||
+ event_data->GetFlavor() != EventDataStructuredData::GetFlavorString())
+ return nullptr;
+
+ return static_cast<const EventDataStructuredData *>(event_data);
+}
+
+ProcessSP EventDataStructuredData::GetProcessFromEvent(const Event *event_ptr) {
+ auto event_data = EventDataStructuredData::GetEventDataFromEvent(event_ptr);
+ if (event_data)
+ return event_data->GetProcess();
+ else
+ return ProcessSP();
+}
+
+StructuredData::ObjectSP
+EventDataStructuredData::GetObjectFromEvent(const Event *event_ptr) {
+ auto event_data = EventDataStructuredData::GetEventDataFromEvent(event_ptr);
+ if (event_data)
+ return event_data->GetObject();
+ else
+ return StructuredData::ObjectSP();
+}
+
+lldb::StructuredDataPluginSP
+EventDataStructuredData::GetPluginFromEvent(const Event *event_ptr) {
+ auto event_data = EventDataStructuredData::GetEventDataFromEvent(event_ptr);
+ if (event_data)
+ return event_data->GetStructuredDataPlugin();
+ else
+ return StructuredDataPluginSP();
+}
+
+ConstString EventDataStructuredData::GetFlavorString() {
+ static ConstString s_flavor("EventDataStructuredData");
+ return s_flavor;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/FileCollector.cpp b/contrib/llvm-project/lldb/source/Utility/FileCollector.cpp
new file mode 100644
index 000000000000..ed9359192205
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/FileCollector.cpp
@@ -0,0 +1,182 @@
+//===-- FileCollector.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/FileCollector.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+
+using namespace lldb_private;
+using namespace llvm;
+
+static bool IsCaseSensitivePath(StringRef path) {
+ SmallString<256> tmp_dest = path, upper_dest, real_dest;
+
+ // Remove component traversals, links, etc.
+ if (!sys::fs::real_path(path, tmp_dest))
+ return true; // Current default value in vfs.yaml
+ path = tmp_dest;
+
+ // Change path to all upper case and ask for its real path, if the latter
+ // exists and is equal to path, it's not case sensitive. Default to case
+ // sensitive in the absence of real_path, since this is the YAMLVFSWriter
+ // default.
+ upper_dest = path.upper();
+ if (sys::fs::real_path(upper_dest, real_dest) && path.equals(real_dest))
+ return false;
+ return true;
+}
+
+FileCollector::FileCollector(const FileSpec &root, const FileSpec &overlay_root)
+ : m_root(root), m_overlay_root(overlay_root) {
+ sys::fs::create_directories(m_root.GetPath(), true);
+}
+
+bool FileCollector::GetRealPath(StringRef src_path,
+ SmallVectorImpl<char> &result) {
+ SmallString<256> real_path;
+ StringRef FileName = sys::path::filename(src_path);
+ std::string directory = sys::path::parent_path(src_path).str();
+ auto dir_with_symlink = m_symlink_map.find(directory);
+
+ // Use real_path to fix any symbolic link component present in a path.
+ // Computing the real path is expensive, cache the search through the
+ // parent path directory.
+ if (dir_with_symlink == m_symlink_map.end()) {
+ auto ec = sys::fs::real_path(directory, real_path);
+ if (ec)
+ return false;
+ m_symlink_map[directory] = real_path.str();
+ } else {
+ real_path = dir_with_symlink->second;
+ }
+
+ sys::path::append(real_path, FileName);
+ result.swap(real_path);
+ return true;
+}
+
+void FileCollector::AddFile(const Twine &file) {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ std::string file_str = file.str();
+ if (MarkAsSeen(file_str))
+ AddFileImpl(file_str);
+}
+
+void FileCollector::AddFileImpl(StringRef src_path) {
+ std::string root = m_root.GetPath();
+
+ // We need an absolute src path to append to the root.
+ SmallString<256> absolute_src = src_path;
+ sys::fs::make_absolute(absolute_src);
+
+ // Canonicalize src to a native path to avoid mixed separator styles.
+ sys::path::native(absolute_src);
+
+ // Remove redundant leading "./" pieces and consecutive separators.
+ absolute_src = sys::path::remove_leading_dotslash(absolute_src);
+
+ // Canonicalize the source path by removing "..", "." components.
+ SmallString<256> virtual_path = absolute_src;
+ sys::path::remove_dots(virtual_path, /*remove_dot_dot=*/true);
+
+ // If a ".." component is present after a symlink component, remove_dots may
+ // lead to the wrong real destination path. Let the source be canonicalized
+ // like that but make sure we always use the real path for the destination.
+ SmallString<256> copy_from;
+ if (!GetRealPath(absolute_src, copy_from))
+ copy_from = virtual_path;
+
+ SmallString<256> dst_path = StringRef(root);
+ sys::path::append(dst_path, sys::path::relative_path(copy_from));
+
+ // Always map a canonical src path to its real path into the YAML, by doing
+ // this we map different virtual src paths to the same entry in the VFS
+ // overlay, which is a way to emulate symlink inside the VFS; this is also
+ // needed for correctness, not doing that can lead to module redefinition
+ // errors.
+ AddFileToMapping(virtual_path, dst_path);
+}
+
+/// Set the access and modification time for the given file from the given
+/// status object.
+static std::error_code
+CopyAccessAndModificationTime(StringRef filename,
+ const sys::fs::file_status &stat) {
+ int fd;
+
+ if (auto ec =
+ sys::fs::openFileForWrite(filename, fd, sys::fs::CD_OpenExisting))
+ return ec;
+
+ if (auto ec = sys::fs::setLastAccessAndModificationTime(
+ fd, stat.getLastAccessedTime(), stat.getLastModificationTime()))
+ return ec;
+
+ if (auto ec = sys::Process::SafelyCloseFileDescriptor(fd))
+ return ec;
+
+ return {};
+}
+
+std::error_code FileCollector::CopyFiles(bool stop_on_error) {
+ for (auto &entry : m_vfs_writer.getMappings()) {
+ // Create directory tree.
+ if (std::error_code ec =
+ sys::fs::create_directories(sys::path::parent_path(entry.RPath),
+ /*IgnoreExisting=*/true)) {
+ if (stop_on_error)
+ return ec;
+ }
+
+ // Copy file over.
+ if (std::error_code ec = sys::fs::copy_file(entry.VPath, entry.RPath)) {
+ if (stop_on_error)
+ return ec;
+ }
+
+ // Copy over permissions.
+ if (auto perms = sys::fs::getPermissions(entry.VPath)) {
+ if (std::error_code ec = sys::fs::setPermissions(entry.RPath, *perms)) {
+ if (stop_on_error)
+ return ec;
+ }
+ }
+
+ // Copy over modification time.
+ sys::fs::file_status stat;
+ if (std::error_code ec = sys::fs::status(entry.VPath, stat)) {
+ if (stop_on_error)
+ return ec;
+ continue;
+ }
+ CopyAccessAndModificationTime(entry.RPath, stat);
+ }
+ return {};
+}
+
+std::error_code FileCollector::WriteMapping(const FileSpec &mapping_file) {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ std::string root = m_overlay_root.GetPath();
+
+ m_vfs_writer.setOverlayDir(root);
+ m_vfs_writer.setCaseSensitivity(IsCaseSensitivePath(root));
+ m_vfs_writer.setUseExternalNames(false);
+
+ std::error_code ec;
+ raw_fd_ostream os(mapping_file.GetPath(), ec, sys::fs::F_Text);
+ if (ec)
+ return ec;
+
+ m_vfs_writer.write(os);
+
+ return {};
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/FileSpec.cpp b/contrib/llvm-project/lldb/source/Utility/FileSpec.cpp
new file mode 100644
index 000000000000..35d22404b948
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/FileSpec.cpp
@@ -0,0 +1,562 @@
+//===-- FileSpec.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+#include <system_error>
+#include <vector>
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+static constexpr FileSpec::Style GetNativeStyle() {
+#if defined(_WIN32)
+ return FileSpec::Style::windows;
+#else
+ return FileSpec::Style::posix;
+#endif
+}
+
+bool PathStyleIsPosix(FileSpec::Style style) {
+ return (style == FileSpec::Style::posix ||
+ (style == FileSpec::Style::native &&
+ GetNativeStyle() == FileSpec::Style::posix));
+}
+
+const char *GetPathSeparators(FileSpec::Style style) {
+ return llvm::sys::path::get_separator(style).data();
+}
+
+char GetPreferredPathSeparator(FileSpec::Style style) {
+ return GetPathSeparators(style)[0];
+}
+
+void Denormalize(llvm::SmallVectorImpl<char> &path, FileSpec::Style style) {
+ if (PathStyleIsPosix(style))
+ return;
+
+ std::replace(path.begin(), path.end(), '/', '\\');
+}
+
+} // end anonymous namespace
+
+FileSpec::FileSpec() : m_style(GetNativeStyle()) {}
+
+// Default constructor that can take an optional full path to a file on disk.
+FileSpec::FileSpec(llvm::StringRef path, Style style) : m_style(style) {
+ SetFile(path, style);
+}
+
+FileSpec::FileSpec(llvm::StringRef path, const llvm::Triple &Triple)
+ : FileSpec{path, Triple.isOSWindows() ? Style::windows : Style::posix} {}
+
+// Copy constructor
+FileSpec::FileSpec(const FileSpec *rhs) : m_directory(), m_filename() {
+ if (rhs)
+ *this = *rhs;
+}
+
+// Virtual destructor in case anyone inherits from this class.
+FileSpec::~FileSpec() {}
+
+namespace {
+/// Safely get a character at the specified index.
+///
+/// \param[in] path
+/// A full, partial, or relative path to a file.
+///
+/// \param[in] i
+/// An index into path which may or may not be valid.
+///
+/// \return
+/// The character at index \a i if the index is valid, or 0 if
+/// the index is not valid.
+inline char safeCharAtIndex(const llvm::StringRef &path, size_t i) {
+ if (i < path.size())
+ return path[i];
+ return 0;
+}
+
+/// Check if a path needs to be normalized.
+///
+/// Check if a path needs to be normalized. We currently consider a
+/// path to need normalization if any of the following are true
+/// - path contains "/./"
+/// - path contains "/../"
+/// - path contains "//"
+/// - path ends with "/"
+/// Paths that start with "./" or with "../" are not considered to
+/// need normalization since we aren't trying to resolve the path,
+/// we are just trying to remove redundant things from the path.
+///
+/// \param[in] path
+/// A full, partial, or relative path to a file.
+///
+/// \return
+/// Returns \b true if the path needs to be normalized.
+bool needsNormalization(const llvm::StringRef &path) {
+ if (path.empty())
+ return false;
+ // We strip off leading "." values so these paths need to be normalized
+ if (path[0] == '.')
+ return true;
+ for (auto i = path.find_first_of("\\/"); i != llvm::StringRef::npos;
+ i = path.find_first_of("\\/", i + 1)) {
+ const auto next = safeCharAtIndex(path, i+1);
+ switch (next) {
+ case 0:
+ // path separator char at the end of the string which should be
+ // stripped unless it is the one and only character
+ return i > 0;
+ case '/':
+ case '\\':
+ // two path separator chars in the middle of a path needs to be
+ // normalized
+ if (i > 0)
+ return true;
+ ++i;
+ break;
+
+ case '.': {
+ const auto next_next = safeCharAtIndex(path, i+2);
+ switch (next_next) {
+ default: break;
+ case 0: return true; // ends with "/."
+ case '/':
+ case '\\':
+ return true; // contains "/./"
+ case '.': {
+ const auto next_next_next = safeCharAtIndex(path, i+3);
+ switch (next_next_next) {
+ default: break;
+ case 0: return true; // ends with "/.."
+ case '/':
+ case '\\':
+ return true; // contains "/../"
+ }
+ break;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+
+}
+// Assignment operator.
+const FileSpec &FileSpec::operator=(const FileSpec &rhs) {
+ if (this != &rhs) {
+ m_directory = rhs.m_directory;
+ m_filename = rhs.m_filename;
+ m_is_resolved = rhs.m_is_resolved;
+ m_style = rhs.m_style;
+ }
+ return *this;
+}
+
+void FileSpec::SetFile(llvm::StringRef pathname) { SetFile(pathname, m_style); }
+
+// Update the contents of this object with a new path. The path will be split
+// up into a directory and filename and stored as uniqued string values for
+// quick comparison and efficient memory usage.
+void FileSpec::SetFile(llvm::StringRef pathname, Style style) {
+ m_filename.Clear();
+ m_directory.Clear();
+ m_is_resolved = false;
+ m_style = (style == Style::native) ? GetNativeStyle() : style;
+
+ if (pathname.empty())
+ return;
+
+ llvm::SmallString<128> resolved(pathname);
+
+ // Normalize the path by removing ".", ".." and other redundant components.
+ if (needsNormalization(resolved))
+ llvm::sys::path::remove_dots(resolved, true, m_style);
+
+ // Normalize back slashes to forward slashes
+ if (m_style == Style::windows)
+ std::replace(resolved.begin(), resolved.end(), '\\', '/');
+
+ if (resolved.empty()) {
+ // If we have no path after normalization set the path to the current
+ // directory. This matches what python does and also a few other path
+ // utilities.
+ m_filename.SetString(".");
+ return;
+ }
+
+ // Split path into filename and directory. We rely on the underlying char
+ // pointer to be nullptr when the components are empty.
+ llvm::StringRef filename = llvm::sys::path::filename(resolved, m_style);
+ if(!filename.empty())
+ m_filename.SetString(filename);
+
+ llvm::StringRef directory = llvm::sys::path::parent_path(resolved, m_style);
+ if(!directory.empty())
+ m_directory.SetString(directory);
+}
+
+void FileSpec::SetFile(llvm::StringRef path, const llvm::Triple &Triple) {
+ return SetFile(path, Triple.isOSWindows() ? Style::windows : Style::posix);
+}
+
+// Convert to pointer operator. This allows code to check any FileSpec objects
+// to see if they contain anything valid using code such as:
+//
+// if (file_spec)
+// {}
+FileSpec::operator bool() const { return m_filename || m_directory; }
+
+// Logical NOT operator. This allows code to check any FileSpec objects to see
+// if they are invalid using code such as:
+//
+// if (!file_spec)
+// {}
+bool FileSpec::operator!() const { return !m_directory && !m_filename; }
+
+bool FileSpec::DirectoryEquals(const FileSpec &rhs) const {
+ const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
+ return ConstString::Equals(m_directory, rhs.m_directory, case_sensitive);
+}
+
+bool FileSpec::FileEquals(const FileSpec &rhs) const {
+ const bool case_sensitive = IsCaseSensitive() || rhs.IsCaseSensitive();
+ return ConstString::Equals(m_filename, rhs.m_filename, case_sensitive);
+}
+
+// Equal to operator
+bool FileSpec::operator==(const FileSpec &rhs) const {
+ return FileEquals(rhs) && DirectoryEquals(rhs);
+}
+
+// Not equal to operator
+bool FileSpec::operator!=(const FileSpec &rhs) const { return !(*this == rhs); }
+
+// Less than operator
+bool FileSpec::operator<(const FileSpec &rhs) const {
+ return FileSpec::Compare(*this, rhs, true) < 0;
+}
+
+// Dump a FileSpec object to a stream
+Stream &lldb_private::operator<<(Stream &s, const FileSpec &f) {
+ f.Dump(&s);
+ return s;
+}
+
+// Clear this object by releasing both the directory and filename string values
+// and making them both the empty string.
+void FileSpec::Clear() {
+ m_directory.Clear();
+ m_filename.Clear();
+}
+
+// Compare two FileSpec objects. If "full" is true, then both the directory and
+// the filename must match. If "full" is false, then the directory names for
+// "a" and "b" are only compared if they are both non-empty. This allows a
+// FileSpec object to only contain a filename and it can match FileSpec objects
+// that have matching filenames with different paths.
+//
+// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" and "1" if
+// "a" is greater than "b".
+int FileSpec::Compare(const FileSpec &a, const FileSpec &b, bool full) {
+ int result = 0;
+
+ // case sensitivity of compare
+ const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive();
+
+ // If full is true, then we must compare both the directory and filename.
+
+ // If full is false, then if either directory is empty, then we match on the
+ // basename only, and if both directories have valid values, we still do a
+ // full compare. This allows for matching when we just have a filename in one
+ // of the FileSpec objects.
+
+ if (full || (a.m_directory && b.m_directory)) {
+ result = ConstString::Compare(a.m_directory, b.m_directory, case_sensitive);
+ if (result)
+ return result;
+ }
+ return ConstString::Compare(a.m_filename, b.m_filename, case_sensitive);
+}
+
+bool FileSpec::Equal(const FileSpec &a, const FileSpec &b, bool full) {
+ // case sensitivity of equality test
+ const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive();
+
+ const bool filenames_equal = ConstString::Equals(a.m_filename,
+ b.m_filename,
+ case_sensitive);
+
+ if (!filenames_equal)
+ return false;
+
+ if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty()))
+ return filenames_equal;
+
+ return a == b;
+}
+
+llvm::Optional<FileSpec::Style> FileSpec::GuessPathStyle(llvm::StringRef absolute_path) {
+ if (absolute_path.startswith("/"))
+ return Style::posix;
+ if (absolute_path.startswith(R"(\\)"))
+ return Style::windows;
+ if (absolute_path.size() > 3 && llvm::isAlpha(absolute_path[0]) &&
+ absolute_path.substr(1, 2) == R"(:\)")
+ return Style::windows;
+ return llvm::None;
+}
+
+// Dump the object to the supplied stream. If the object contains a valid
+// directory name, it will be displayed followed by a directory delimiter, and
+// the filename.
+void FileSpec::Dump(Stream *s) const {
+ if (s) {
+ std::string path{GetPath(true)};
+ s->PutCString(path);
+ char path_separator = GetPreferredPathSeparator(m_style);
+ if (!m_filename && !path.empty() && path.back() != path_separator)
+ s->PutChar(path_separator);
+ }
+}
+
+FileSpec::Style FileSpec::GetPathStyle() const { return m_style; }
+
+// Directory string get accessor.
+ConstString &FileSpec::GetDirectory() { return m_directory; }
+
+// Directory string const get accessor.
+ConstString FileSpec::GetDirectory() const { return m_directory; }
+
+// Filename string get accessor.
+ConstString &FileSpec::GetFilename() { return m_filename; }
+
+// Filename string const get accessor.
+ConstString FileSpec::GetFilename() const { return m_filename; }
+
+// Extract the directory and path into a fixed buffer. This is needed as the
+// directory and path are stored in separate string values.
+size_t FileSpec::GetPath(char *path, size_t path_max_len,
+ bool denormalize) const {
+ if (!path)
+ return 0;
+
+ std::string result = GetPath(denormalize);
+ ::snprintf(path, path_max_len, "%s", result.c_str());
+ return std::min(path_max_len - 1, result.length());
+}
+
+std::string FileSpec::GetPath(bool denormalize) const {
+ llvm::SmallString<64> result;
+ GetPath(result, denormalize);
+ return std::string(result.begin(), result.end());
+}
+
+const char *FileSpec::GetCString(bool denormalize) const {
+ return ConstString{GetPath(denormalize)}.AsCString(nullptr);
+}
+
+void FileSpec::GetPath(llvm::SmallVectorImpl<char> &path,
+ bool denormalize) const {
+ path.append(m_directory.GetStringRef().begin(),
+ m_directory.GetStringRef().end());
+ // Since the path was normalized and all paths use '/' when stored in these
+ // objects, we don't need to look for the actual syntax specific path
+ // separator, we just look for and insert '/'.
+ if (m_directory && m_filename && m_directory.GetStringRef().back() != '/' &&
+ m_filename.GetStringRef().back() != '/')
+ path.insert(path.end(), '/');
+ path.append(m_filename.GetStringRef().begin(),
+ m_filename.GetStringRef().end());
+ if (denormalize && !path.empty())
+ Denormalize(path, m_style);
+}
+
+ConstString FileSpec::GetFileNameExtension() const {
+ return ConstString(
+ llvm::sys::path::extension(m_filename.GetStringRef(), m_style));
+}
+
+ConstString FileSpec::GetFileNameStrippingExtension() const {
+ return ConstString(llvm::sys::path::stem(m_filename.GetStringRef(), m_style));
+}
+
+// Return the size in bytes that this object takes in memory. This returns the
+// size in bytes of this object, not any shared string values it may refer to.
+size_t FileSpec::MemorySize() const {
+ return m_filename.MemorySize() + m_directory.MemorySize();
+}
+
+FileSpec
+FileSpec::CopyByAppendingPathComponent(llvm::StringRef component) const {
+ FileSpec ret = *this;
+ ret.AppendPathComponent(component);
+ return ret;
+}
+
+FileSpec FileSpec::CopyByRemovingLastPathComponent() const {
+ llvm::SmallString<64> current_path;
+ GetPath(current_path, false);
+ if (llvm::sys::path::has_parent_path(current_path, m_style))
+ return FileSpec(llvm::sys::path::parent_path(current_path, m_style),
+ m_style);
+ return *this;
+}
+
+ConstString FileSpec::GetLastPathComponent() const {
+ llvm::SmallString<64> current_path;
+ GetPath(current_path, false);
+ return ConstString(llvm::sys::path::filename(current_path, m_style));
+}
+
+void FileSpec::PrependPathComponent(llvm::StringRef component) {
+ llvm::SmallString<64> new_path(component);
+ llvm::SmallString<64> current_path;
+ GetPath(current_path, false);
+ llvm::sys::path::append(new_path,
+ llvm::sys::path::begin(current_path, m_style),
+ llvm::sys::path::end(current_path), m_style);
+ SetFile(new_path, m_style);
+}
+
+void FileSpec::PrependPathComponent(const FileSpec &new_path) {
+ return PrependPathComponent(new_path.GetPath(false));
+}
+
+void FileSpec::AppendPathComponent(llvm::StringRef component) {
+ llvm::SmallString<64> current_path;
+ GetPath(current_path, false);
+ llvm::sys::path::append(current_path, m_style, component);
+ SetFile(current_path, m_style);
+}
+
+void FileSpec::AppendPathComponent(const FileSpec &new_path) {
+ return AppendPathComponent(new_path.GetPath(false));
+}
+
+bool FileSpec::RemoveLastPathComponent() {
+ llvm::SmallString<64> current_path;
+ GetPath(current_path, false);
+ if (llvm::sys::path::has_parent_path(current_path, m_style)) {
+ SetFile(llvm::sys::path::parent_path(current_path, m_style));
+ return true;
+ }
+ return false;
+}
+/// Returns true if the filespec represents an implementation source
+/// file (files with a ".c", ".cpp", ".m", ".mm" (many more)
+/// extension).
+///
+/// \return
+/// \b true if the filespec represents an implementation source
+/// file, \b false otherwise.
+bool FileSpec::IsSourceImplementationFile() const {
+ ConstString extension(GetFileNameExtension());
+ if (!extension)
+ return false;
+
+ static RegularExpression g_source_file_regex(llvm::StringRef(
+ "^.([cC]|[mM]|[mM][mM]|[cC][pP][pP]|[cC]\\+\\+|[cC][xX][xX]|[cC][cC]|["
+ "cC][pP]|[sS]|[aA][sS][mM]|[fF]|[fF]77|[fF]90|[fF]95|[fF]03|[fF][oO]["
+ "rR]|[fF][tT][nN]|[fF][pP][pP]|[aA][dD][aA]|[aA][dD][bB]|[aA][dD][sS])"
+ "$"));
+ return g_source_file_regex.Execute(extension.GetStringRef());
+}
+
+bool FileSpec::IsRelative() const {
+ return !IsAbsolute();
+}
+
+bool FileSpec::IsAbsolute() const {
+ llvm::SmallString<64> current_path;
+ GetPath(current_path, false);
+
+ // Early return if the path is empty.
+ if (current_path.empty())
+ return false;
+
+ // We consider paths starting with ~ to be absolute.
+ if (current_path[0] == '~')
+ return true;
+
+ return llvm::sys::path::is_absolute(current_path, m_style);
+}
+
+void FileSpec::MakeAbsolute(const FileSpec &dir) {
+ if (IsRelative())
+ PrependPathComponent(dir);
+}
+
+void llvm::format_provider<FileSpec>::format(const FileSpec &F,
+ raw_ostream &Stream,
+ StringRef Style) {
+ assert(
+ (Style.empty() || Style.equals_lower("F") || Style.equals_lower("D")) &&
+ "Invalid FileSpec style!");
+
+ StringRef dir = F.GetDirectory().GetStringRef();
+ StringRef file = F.GetFilename().GetStringRef();
+
+ if (dir.empty() && file.empty()) {
+ Stream << "(empty)";
+ return;
+ }
+
+ if (Style.equals_lower("F")) {
+ Stream << (file.empty() ? "(empty)" : file);
+ return;
+ }
+
+ // Style is either D or empty, either way we need to print the directory.
+ if (!dir.empty()) {
+ // Directory is stored in normalized form, which might be different than
+ // preferred form. In order to handle this, we need to cut off the
+ // filename, then denormalize, then write the entire denorm'ed directory.
+ llvm::SmallString<64> denormalized_dir = dir;
+ Denormalize(denormalized_dir, F.GetPathStyle());
+ Stream << denormalized_dir;
+ Stream << GetPreferredPathSeparator(F.GetPathStyle());
+ }
+
+ if (Style.equals_lower("D")) {
+ // We only want to print the directory, so now just exit.
+ if (dir.empty())
+ Stream << "(empty)";
+ return;
+ }
+
+ if (!file.empty())
+ Stream << file;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/IOObject.cpp b/contrib/llvm-project/lldb/source/Utility/IOObject.cpp
new file mode 100644
index 000000000000..5e3ccddb6a30
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/IOObject.cpp
@@ -0,0 +1,14 @@
+//===-- IOObject.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/IOObject.h"
+
+using namespace lldb_private;
+
+const IOObject::WaitableHandle IOObject::kInvalidHandleValue = -1;
+IOObject::~IOObject() = default;
diff --git a/contrib/llvm-project/lldb/source/Utility/JSON.cpp b/contrib/llvm-project/lldb/source/Utility/JSON.cpp
new file mode 100644
index 000000000000..2c3f6229eda1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/JSON.cpp
@@ -0,0 +1,550 @@
+//===--------------------- JSON.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/JSON.h"
+
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stddef.h>
+#include <utility>
+
+using namespace lldb_private;
+
+std::string JSONString::json_string_quote_metachars(const std::string &s) {
+ if (s.find_first_of("\\\n\"") == std::string::npos)
+ return 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 == '\n') {
+ output.push_back('\\');
+ if (ch == '\n') ch = 'n';
+ }
+ output.push_back(ch);
+ }
+ return output;
+}
+
+JSONString::JSONString() : JSONValue(JSONValue::Kind::String), m_data() {}
+
+JSONString::JSONString(const char *s)
+ : JSONValue(JSONValue::Kind::String), m_data(s ? s : "") {}
+
+JSONString::JSONString(const std::string &s)
+ : JSONValue(JSONValue::Kind::String), m_data(s) {}
+
+void JSONString::Write(Stream &s) {
+ s.Printf("\"%s\"", json_string_quote_metachars(m_data).c_str());
+}
+
+uint64_t JSONNumber::GetAsUnsigned() const {
+ switch (m_data_type) {
+ case DataType::Unsigned:
+ return m_data.m_unsigned;
+ case DataType::Signed:
+ return static_cast<uint64_t>(m_data.m_signed);
+ case DataType::Double:
+ return static_cast<uint64_t>(m_data.m_double);
+ }
+ llvm_unreachable("Unhandled data type");
+}
+
+int64_t JSONNumber::GetAsSigned() const {
+ switch (m_data_type) {
+ case DataType::Unsigned:
+ return static_cast<int64_t>(m_data.m_unsigned);
+ case DataType::Signed:
+ return m_data.m_signed;
+ case DataType::Double:
+ return static_cast<int64_t>(m_data.m_double);
+ }
+ llvm_unreachable("Unhandled data type");
+}
+
+double JSONNumber::GetAsDouble() const {
+ switch (m_data_type) {
+ case DataType::Unsigned:
+ return static_cast<double>(m_data.m_unsigned);
+ case DataType::Signed:
+ return static_cast<double>(m_data.m_signed);
+ case DataType::Double:
+ return m_data.m_double;
+ }
+ llvm_unreachable("Unhandled data type");
+}
+
+void JSONNumber::Write(Stream &s) {
+ switch (m_data_type) {
+ case DataType::Unsigned:
+ s.Printf("%" PRIu64, m_data.m_unsigned);
+ break;
+ case DataType::Signed:
+ s.Printf("%" PRId64, m_data.m_signed);
+ break;
+ case DataType::Double:
+ s.Printf("%g", m_data.m_double);
+ break;
+ }
+}
+
+JSONTrue::JSONTrue() : JSONValue(JSONValue::Kind::True) {}
+
+void JSONTrue::Write(Stream &s) { s.Printf("true"); }
+
+JSONFalse::JSONFalse() : JSONValue(JSONValue::Kind::False) {}
+
+void JSONFalse::Write(Stream &s) { s.Printf("false"); }
+
+JSONNull::JSONNull() : JSONValue(JSONValue::Kind::Null) {}
+
+void JSONNull::Write(Stream &s) { s.Printf("null"); }
+
+JSONObject::JSONObject() : JSONValue(JSONValue::Kind::Object) {}
+
+void JSONObject::Write(Stream &s) {
+ bool first = true;
+ s.PutChar('{');
+ auto iter = m_elements.begin(), end = m_elements.end();
+ for (; iter != end; iter++) {
+ if (first)
+ first = false;
+ else
+ s.PutChar(',');
+ JSONString key(iter->first);
+ JSONValue::SP value(iter->second);
+ key.Write(s);
+ s.PutChar(':');
+ value->Write(s);
+ }
+ s.PutChar('}');
+}
+
+bool JSONObject::SetObject(const std::string &key, JSONValue::SP value) {
+ if (key.empty() || nullptr == value.get())
+ return false;
+ m_elements[key] = value;
+ return true;
+}
+
+JSONValue::SP JSONObject::GetObject(const std::string &key) {
+ auto iter = m_elements.find(key), end = m_elements.end();
+ if (iter == end)
+ return JSONValue::SP();
+ return iter->second;
+}
+
+JSONArray::JSONArray() : JSONValue(JSONValue::Kind::Array) {}
+
+void JSONArray::Write(Stream &s) {
+ bool first = true;
+ s.PutChar('[');
+ auto iter = m_elements.begin(), end = m_elements.end();
+ for (; iter != end; iter++) {
+ if (first)
+ first = false;
+ else
+ s.PutChar(',');
+ (*iter)->Write(s);
+ }
+ s.PutChar(']');
+}
+
+bool JSONArray::SetObject(Index i, JSONValue::SP value) {
+ if (value.get() == nullptr)
+ return false;
+ if (i < m_elements.size()) {
+ m_elements[i] = value;
+ return true;
+ }
+ if (i == m_elements.size()) {
+ m_elements.push_back(value);
+ return true;
+ }
+ return false;
+}
+
+bool JSONArray::AppendObject(JSONValue::SP value) {
+ if (value.get() == nullptr)
+ return false;
+ m_elements.push_back(value);
+ return true;
+}
+
+JSONValue::SP JSONArray::GetObject(Index i) {
+ if (i < m_elements.size())
+ return m_elements[i];
+ return JSONValue::SP();
+}
+
+JSONArray::Size JSONArray::GetNumElements() { return m_elements.size(); }
+
+JSONParser::JSONParser(llvm::StringRef data) : StringExtractor(data) {}
+
+JSONParser::Token JSONParser::GetToken(std::string &value) {
+ StreamString error;
+
+ value.clear();
+ SkipSpaces();
+ const uint64_t start_index = m_index;
+ const char ch = GetChar();
+ switch (ch) {
+ case '{':
+ return Token::ObjectStart;
+ case '}':
+ return Token::ObjectEnd;
+ case '[':
+ return Token::ArrayStart;
+ case ']':
+ return Token::ArrayEnd;
+ case ',':
+ return Token::Comma;
+ case ':':
+ return Token::Colon;
+ case '\0':
+ return Token::EndOfFile;
+ case 't':
+ if (GetChar() == 'r')
+ if (GetChar() == 'u')
+ if (GetChar() == 'e')
+ return Token::True;
+ break;
+
+ case 'f':
+ if (GetChar() == 'a')
+ if (GetChar() == 'l')
+ if (GetChar() == 's')
+ if (GetChar() == 'e')
+ return Token::False;
+ break;
+
+ case 'n':
+ if (GetChar() == 'u')
+ if (GetChar() == 'l')
+ if (GetChar() == 'l')
+ return Token::Null;
+ break;
+
+ case '"': {
+ while (true) {
+ bool was_escaped = false;
+ int escaped_ch = GetEscapedChar(was_escaped);
+ if (escaped_ch == -1) {
+ error.Printf(
+ "error: an error occurred getting a character from offset %" PRIu64,
+ start_index);
+ value = std::move(error.GetString());
+ return Token::Status;
+
+ } else {
+ const bool is_end_quote = escaped_ch == '"';
+ const bool is_null = escaped_ch == 0;
+ if (was_escaped || (!is_end_quote && !is_null)) {
+ if (CHAR_MIN <= escaped_ch && escaped_ch <= CHAR_MAX) {
+ value.append(1, static_cast<char>(escaped_ch));
+ } else {
+ error.Printf("error: wide character support is needed for unicode "
+ "character 0x%4.4x at offset %" PRIu64,
+ escaped_ch, start_index);
+ value = std::move(error.GetString());
+ return Token::Status;
+ }
+ } else if (is_end_quote) {
+ return Token::String;
+ } else if (is_null) {
+ value = "error: missing end quote for string";
+ return Token::Status;
+ }
+ }
+ }
+ } break;
+
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ bool done = false;
+ bool got_decimal_point = false;
+ uint64_t exp_index = 0;
+ bool got_int_digits = (ch >= '0') && (ch <= '9');
+ bool got_frac_digits = false;
+ bool got_exp_digits = false;
+ while (!done) {
+ const char next_ch = PeekChar();
+ switch (next_ch) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (exp_index != 0) {
+ got_exp_digits = true;
+ } else if (got_decimal_point) {
+ got_frac_digits = true;
+ } else {
+ got_int_digits = true;
+ }
+ ++m_index; // Skip this character
+ break;
+
+ case '.':
+ if (got_decimal_point) {
+ error.Printf("error: extra decimal point found at offset %" PRIu64,
+ start_index);
+ value = std::move(error.GetString());
+ return Token::Status;
+ } else {
+ got_decimal_point = true;
+ ++m_index; // Skip this character
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ if (exp_index != 0) {
+ error.Printf(
+ "error: extra exponent character found at offset %" PRIu64,
+ start_index);
+ value = std::move(error.GetString());
+ return Token::Status;
+ } else {
+ exp_index = m_index;
+ ++m_index; // Skip this character
+ }
+ break;
+
+ case '+':
+ case '-':
+ // The '+' and '-' can only come after an exponent character...
+ if (exp_index == m_index - 1) {
+ ++m_index; // Skip the exponent sign character
+ } else {
+ error.Printf("error: unexpected %c character at offset %" PRIu64,
+ next_ch, start_index);
+ value = std::move(error.GetString());
+ return Token::Status;
+ }
+ break;
+
+ default:
+ done = true;
+ break;
+ }
+ }
+
+ if (m_index > start_index) {
+ value = m_packet.substr(start_index, m_index - start_index);
+ if (got_decimal_point) {
+ if (exp_index != 0) {
+ // We have an exponent, make sure we got exponent digits
+ if (got_exp_digits) {
+ return Token::Float;
+ } else {
+ error.Printf("error: got exponent character but no exponent digits "
+ "at offset in float value \"%s\"",
+ value.c_str());
+ value = std::move(error.GetString());
+ return Token::Status;
+ }
+ } else {
+ // No exponent, but we need at least one decimal after the decimal
+ // point
+ if (got_frac_digits) {
+ return Token::Float;
+ } else {
+ error.Printf("error: no digits after decimal point \"%s\"",
+ value.c_str());
+ value = std::move(error.GetString());
+ return Token::Status;
+ }
+ }
+ } else {
+ // No decimal point
+ if (got_int_digits) {
+ // We need at least some integer digits to make an integer
+ return Token::Integer;
+ } else {
+ error.Printf("error: no digits negate sign \"%s\"", value.c_str());
+ value = std::move(error.GetString());
+ return Token::Status;
+ }
+ }
+ } else {
+ error.Printf("error: invalid number found at offset %" PRIu64,
+ start_index);
+ value = std::move(error.GetString());
+ return Token::Status;
+ }
+ } break;
+ default:
+ break;
+ }
+ error.Printf("error: failed to parse token at offset %" PRIu64
+ " (around character '%c')",
+ start_index, ch);
+ value = std::move(error.GetString());
+ return Token::Status;
+}
+
+int JSONParser::GetEscapedChar(bool &was_escaped) {
+ was_escaped = false;
+ const char ch = GetChar();
+ if (ch == '\\') {
+ was_escaped = true;
+ const char ch2 = GetChar();
+ switch (ch2) {
+ case '"':
+ case '\\':
+ case '/':
+ default:
+ break;
+
+ case 'b':
+ return '\b';
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'u': {
+ const int hi_byte = DecodeHexU8();
+ const int lo_byte = DecodeHexU8();
+ if (hi_byte >= 0 && lo_byte >= 0)
+ return hi_byte << 8 | lo_byte;
+ return -1;
+ } break;
+ }
+ return ch2;
+ }
+ return ch;
+}
+
+JSONValue::SP JSONParser::ParseJSONObject() {
+ // The "JSONParser::Token::ObjectStart" token should have already been
+ // consumed by the time this function is called
+ std::unique_ptr<JSONObject> dict_up(new JSONObject());
+
+ std::string value;
+ std::string key;
+ while (true) {
+ JSONParser::Token token = GetToken(value);
+
+ if (token == JSONParser::Token::String) {
+ key.swap(value);
+ token = GetToken(value);
+ if (token == JSONParser::Token::Colon) {
+ JSONValue::SP value_sp = ParseJSONValue();
+ if (value_sp)
+ dict_up->SetObject(key, value_sp);
+ else
+ break;
+ }
+ } else if (token == JSONParser::Token::ObjectEnd) {
+ return JSONValue::SP(dict_up.release());
+ } else if (token == JSONParser::Token::Comma) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ return JSONValue::SP();
+}
+
+JSONValue::SP JSONParser::ParseJSONArray() {
+ // The "JSONParser::Token::ObjectStart" token should have already been
+ // consumed by the time this function is called
+ std::unique_ptr<JSONArray> array_up(new JSONArray());
+
+ std::string value;
+ std::string key;
+ while (true) {
+ JSONValue::SP value_sp = ParseJSONValue();
+ if (value_sp)
+ array_up->AppendObject(value_sp);
+ else
+ break;
+
+ JSONParser::Token token = GetToken(value);
+ if (token == JSONParser::Token::Comma) {
+ continue;
+ } else if (token == JSONParser::Token::ArrayEnd) {
+ return JSONValue::SP(array_up.release());
+ } else {
+ break;
+ }
+ }
+ return JSONValue::SP();
+}
+
+JSONValue::SP JSONParser::ParseJSONValue() {
+ std::string value;
+ const JSONParser::Token token = GetToken(value);
+ switch (token) {
+ case JSONParser::Token::ObjectStart:
+ return ParseJSONObject();
+
+ case JSONParser::Token::ArrayStart:
+ return ParseJSONArray();
+
+ case JSONParser::Token::Integer: {
+ if (value.front() == '-') {
+ int64_t sval = 0;
+ if (!llvm::StringRef(value).getAsInteger(0, sval))
+ return JSONValue::SP(new JSONNumber(sval));
+ } else {
+ uint64_t uval = 0;
+ if (!llvm::StringRef(value).getAsInteger(0, uval))
+ return JSONValue::SP(new JSONNumber(uval));
+ }
+ } break;
+
+ case JSONParser::Token::Float: {
+ double D;
+ if (!llvm::StringRef(value).getAsDouble(D))
+ return JSONValue::SP(new JSONNumber(D));
+ } break;
+
+ case JSONParser::Token::String:
+ return JSONValue::SP(new JSONString(value));
+
+ case JSONParser::Token::True:
+ return JSONValue::SP(new JSONTrue());
+
+ case JSONParser::Token::False:
+ return JSONValue::SP(new JSONFalse());
+
+ case JSONParser::Token::Null:
+ return JSONValue::SP(new JSONNull());
+
+ default:
+ break;
+ }
+ return JSONValue::SP();
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/LLDBAssert.cpp b/contrib/llvm-project/lldb/source/Utility/LLDBAssert.cpp
new file mode 100644
index 000000000000..75530d806310
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/LLDBAssert.cpp
@@ -0,0 +1,36 @@
+//===--------------------- LLDBAssert.cpp ------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/LLDBAssert.h"
+
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace lldb_private;
+
+void lldb_private::lldb_assert(bool expression, const char *expr_text,
+ const char *func, const char *file,
+ unsigned int line) {
+ if (LLVM_LIKELY(expression))
+ return;
+
+ // In a Debug configuration lldb_assert() behaves like assert(0).
+ llvm_unreachable("lldb_assert failed");
+
+ // In a Release configuration it will print a warning and encourage the user
+ // to file a bug report, similar to LLVM’s crash handler, and then return
+ // execution.
+ errs() << format("Assertion failed: (%s), function %s, file %s, line %u\n",
+ expr_text, func, file, line);
+ errs() << "backtrace leading to the failure:\n";
+ llvm::sys::PrintStackTrace(errs());
+ errs() << "please file a bug report against lldb reporting this failure "
+ "log, and as many details as possible\n";
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Listener.cpp b/contrib/llvm-project/lldb/source/Utility/Listener.cpp
new file mode 100644
index 000000000000..50c56406c2ca
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Listener.cpp
@@ -0,0 +1,467 @@
+//===-- Listener.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Listener.h"
+
+#include "lldb/Utility/Broadcaster.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+
+#include "llvm/ADT/Optional.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+class BroadcasterManagerWPMatcher {
+public:
+ BroadcasterManagerWPMatcher(BroadcasterManagerSP manager_sp)
+ : m_manager_sp(std::move(manager_sp)) {}
+ bool operator()(const BroadcasterManagerWP &input_wp) const {
+ BroadcasterManagerSP input_sp = input_wp.lock();
+ return (input_sp && input_sp == m_manager_sp);
+ }
+
+ BroadcasterManagerSP m_manager_sp;
+};
+} // anonymous namespace
+
+Listener::Listener(const char *name)
+ : m_name(name), m_broadcasters(), m_broadcasters_mutex(), m_events(),
+ m_events_mutex() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log != nullptr)
+ log->Printf("%p Listener::Listener('%s')", static_cast<void *>(this),
+ m_name.c_str());
+}
+
+Listener::~Listener() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+
+ Clear();
+
+ if (log)
+ log->Printf("%p Listener::%s('%s')", static_cast<void *>(this),
+ __FUNCTION__, m_name.c_str());
+}
+
+void Listener::Clear() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ std::lock_guard<std::recursive_mutex> broadcasters_guard(
+ m_broadcasters_mutex);
+ broadcaster_collection::iterator pos, end = m_broadcasters.end();
+ for (pos = m_broadcasters.begin(); pos != end; ++pos) {
+ Broadcaster::BroadcasterImplSP broadcaster_sp(pos->first.lock());
+ if (broadcaster_sp)
+ broadcaster_sp->RemoveListener(this, pos->second.event_mask);
+ }
+ m_broadcasters.clear();
+
+ std::lock_guard<std::mutex> events_guard(m_events_mutex);
+ m_events.clear();
+ size_t num_managers = m_broadcaster_managers.size();
+
+ for (size_t i = 0; i < num_managers; i++) {
+ BroadcasterManagerSP manager_sp(m_broadcaster_managers[i].lock());
+ if (manager_sp)
+ manager_sp->RemoveListener(this);
+ }
+
+ if (log)
+ log->Printf("%p Listener::%s('%s')", static_cast<void *>(this),
+ __FUNCTION__, m_name.c_str());
+}
+
+uint32_t Listener::StartListeningForEvents(Broadcaster *broadcaster,
+ uint32_t event_mask) {
+ if (broadcaster) {
+ // Scope for "locker"
+ // Tell the broadcaster to add this object as a listener
+ {
+ std::lock_guard<std::recursive_mutex> broadcasters_guard(
+ m_broadcasters_mutex);
+ Broadcaster::BroadcasterImplWP impl_wp(broadcaster->GetBroadcasterImpl());
+ m_broadcasters.insert(
+ std::make_pair(impl_wp, BroadcasterInfo(event_mask)));
+ }
+
+ uint32_t acquired_mask =
+ broadcaster->AddListener(this->shared_from_this(), event_mask);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
+ if (log != nullptr)
+ log->Printf("%p Listener::StartListeningForEvents (broadcaster = %p, "
+ "mask = 0x%8.8x) acquired_mask = 0x%8.8x for %s",
+ static_cast<void *>(this), static_cast<void *>(broadcaster),
+ event_mask, acquired_mask, m_name.c_str());
+
+ return acquired_mask;
+ }
+ return 0;
+}
+
+uint32_t Listener::StartListeningForEvents(Broadcaster *broadcaster,
+ uint32_t event_mask,
+ HandleBroadcastCallback callback,
+ void *callback_user_data) {
+ if (broadcaster) {
+ // Scope for "locker"
+ // Tell the broadcaster to add this object as a listener
+ {
+ std::lock_guard<std::recursive_mutex> broadcasters_guard(
+ m_broadcasters_mutex);
+ Broadcaster::BroadcasterImplWP impl_wp(broadcaster->GetBroadcasterImpl());
+ m_broadcasters.insert(std::make_pair(
+ impl_wp, BroadcasterInfo(event_mask, callback, callback_user_data)));
+ }
+
+ uint32_t acquired_mask =
+ broadcaster->AddListener(this->shared_from_this(), event_mask);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
+ if (log != nullptr) {
+ void **pointer = reinterpret_cast<void **>(&callback);
+ log->Printf("%p Listener::StartListeningForEvents (broadcaster = %p, "
+ "mask = 0x%8.8x, callback = %p, user_data = %p) "
+ "acquired_mask = 0x%8.8x for %s",
+ static_cast<void *>(this), static_cast<void *>(broadcaster),
+ event_mask, *pointer, static_cast<void *>(callback_user_data),
+ acquired_mask, m_name.c_str());
+ }
+
+ return acquired_mask;
+ }
+ return 0;
+}
+
+bool Listener::StopListeningForEvents(Broadcaster *broadcaster,
+ uint32_t event_mask) {
+ if (broadcaster) {
+ // Scope for "locker"
+ {
+ std::lock_guard<std::recursive_mutex> broadcasters_guard(
+ m_broadcasters_mutex);
+ m_broadcasters.erase(broadcaster->GetBroadcasterImpl());
+ }
+ // Remove the broadcaster from our set of broadcasters
+ return broadcaster->RemoveListener(this->shared_from_this(), event_mask);
+ }
+
+ return false;
+}
+
+// Called when a Broadcaster is in its destructor. We need to remove all
+// knowledge of this broadcaster and any events that it may have queued up
+void Listener::BroadcasterWillDestruct(Broadcaster *broadcaster) {
+ // Scope for "broadcasters_locker"
+ {
+ std::lock_guard<std::recursive_mutex> broadcasters_guard(
+ m_broadcasters_mutex);
+ m_broadcasters.erase(broadcaster->GetBroadcasterImpl());
+ }
+
+ // Scope for "event_locker"
+ {
+ std::lock_guard<std::mutex> events_guard(m_events_mutex);
+ // Remove all events for this broadcaster object.
+ event_collection::iterator pos = m_events.begin();
+ while (pos != m_events.end()) {
+ if ((*pos)->GetBroadcaster() == broadcaster)
+ pos = m_events.erase(pos);
+ else
+ ++pos;
+ }
+ }
+}
+
+void Listener::BroadcasterManagerWillDestruct(BroadcasterManagerSP manager_sp) {
+ // Just need to remove this broadcast manager from the list of managers:
+ broadcaster_manager_collection::iterator iter,
+ end_iter = m_broadcaster_managers.end();
+ BroadcasterManagerWP manager_wp;
+
+ BroadcasterManagerWPMatcher matcher(std::move(manager_sp));
+ iter = std::find_if<broadcaster_manager_collection::iterator,
+ BroadcasterManagerWPMatcher>(
+ m_broadcaster_managers.begin(), end_iter, matcher);
+ if (iter != end_iter)
+ m_broadcaster_managers.erase(iter);
+}
+
+void Listener::AddEvent(EventSP &event_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
+ if (log != nullptr)
+ log->Printf("%p Listener('%s')::AddEvent (event_sp = {%p})",
+ static_cast<void *>(this), m_name.c_str(),
+ static_cast<void *>(event_sp.get()));
+
+ std::lock_guard<std::mutex> guard(m_events_mutex);
+ m_events.push_back(event_sp);
+ m_events_condition.notify_all();
+}
+
+class EventBroadcasterMatches {
+public:
+ EventBroadcasterMatches(Broadcaster *broadcaster)
+ : m_broadcaster(broadcaster) {}
+
+ bool operator()(const EventSP &event_sp) const {
+ return event_sp->BroadcasterIs(m_broadcaster);
+ }
+
+private:
+ Broadcaster *m_broadcaster;
+};
+
+class EventMatcher {
+public:
+ EventMatcher(Broadcaster *broadcaster, const ConstString *broadcaster_names,
+ uint32_t num_broadcaster_names, uint32_t event_type_mask)
+ : m_broadcaster(broadcaster), m_broadcaster_names(broadcaster_names),
+ m_num_broadcaster_names(num_broadcaster_names),
+ m_event_type_mask(event_type_mask) {}
+
+ bool operator()(const EventSP &event_sp) const {
+ if (m_broadcaster && !event_sp->BroadcasterIs(m_broadcaster))
+ return false;
+
+ if (m_broadcaster_names) {
+ bool found_source = false;
+ ConstString event_broadcaster_name =
+ event_sp->GetBroadcaster()->GetBroadcasterName();
+ for (uint32_t i = 0; i < m_num_broadcaster_names; ++i) {
+ if (m_broadcaster_names[i] == event_broadcaster_name) {
+ found_source = true;
+ break;
+ }
+ }
+ if (!found_source)
+ return false;
+ }
+
+ return m_event_type_mask == 0 || m_event_type_mask & event_sp->GetType();
+ }
+
+private:
+ Broadcaster *m_broadcaster;
+ const ConstString *m_broadcaster_names;
+ const uint32_t m_num_broadcaster_names;
+ const uint32_t m_event_type_mask;
+};
+
+bool Listener::FindNextEventInternal(
+ std::unique_lock<std::mutex> &lock,
+ Broadcaster *broadcaster, // nullptr for any broadcaster
+ const ConstString *broadcaster_names, // nullptr for any event
+ uint32_t num_broadcaster_names, uint32_t event_type_mask, EventSP &event_sp,
+ bool remove) {
+ // NOTE: callers of this function must lock m_events_mutex using a
+ // Mutex::Locker
+ // and pass the locker as the first argument. m_events_mutex is no longer
+ // recursive.
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
+
+ if (m_events.empty())
+ return false;
+
+ Listener::event_collection::iterator pos = m_events.end();
+
+ if (broadcaster == nullptr && broadcaster_names == nullptr &&
+ event_type_mask == 0) {
+ pos = m_events.begin();
+ } else {
+ pos = std::find_if(m_events.begin(), m_events.end(),
+ EventMatcher(broadcaster, broadcaster_names,
+ num_broadcaster_names, event_type_mask));
+ }
+
+ if (pos != m_events.end()) {
+ event_sp = *pos;
+
+ if (log != nullptr)
+ log->Printf("%p '%s' Listener::FindNextEventInternal(broadcaster=%p, "
+ "broadcaster_names=%p[%u], event_type_mask=0x%8.8x, "
+ "remove=%i) event %p",
+ static_cast<void *>(this), GetName(),
+ static_cast<void *>(broadcaster),
+ static_cast<const void *>(broadcaster_names),
+ num_broadcaster_names, event_type_mask, remove,
+ static_cast<void *>(event_sp.get()));
+
+ if (remove) {
+ m_events.erase(pos);
+ // Unlock the event queue here. We've removed this event and are about
+ // to return it so it should be okay to get the next event off the queue
+ // here - and it might be useful to do that in the "DoOnRemoval".
+ lock.unlock();
+ event_sp->DoOnRemoval();
+ }
+ return true;
+ }
+
+ event_sp.reset();
+ return false;
+}
+
+Event *Listener::PeekAtNextEvent() {
+ std::unique_lock<std::mutex> guard(m_events_mutex);
+ EventSP event_sp;
+ if (FindNextEventInternal(guard, nullptr, nullptr, 0, 0, event_sp, false))
+ return event_sp.get();
+ return nullptr;
+}
+
+Event *Listener::PeekAtNextEventForBroadcaster(Broadcaster *broadcaster) {
+ std::unique_lock<std::mutex> guard(m_events_mutex);
+ EventSP event_sp;
+ if (FindNextEventInternal(guard, broadcaster, nullptr, 0, 0, event_sp, false))
+ return event_sp.get();
+ return nullptr;
+}
+
+Event *
+Listener::PeekAtNextEventForBroadcasterWithType(Broadcaster *broadcaster,
+ uint32_t event_type_mask) {
+ std::unique_lock<std::mutex> guard(m_events_mutex);
+ EventSP event_sp;
+ if (FindNextEventInternal(guard, broadcaster, nullptr, 0, event_type_mask,
+ event_sp, false))
+ return event_sp.get();
+ return nullptr;
+}
+
+bool Listener::GetEventInternal(
+ const Timeout<std::micro> &timeout,
+ Broadcaster *broadcaster, // nullptr for any broadcaster
+ const ConstString *broadcaster_names, // nullptr for any event
+ uint32_t num_broadcaster_names, uint32_t event_type_mask,
+ EventSP &event_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
+ LLDB_LOG(log, "this = {0}, timeout = {1} for {2}", this, timeout, m_name);
+
+ std::unique_lock<std::mutex> lock(m_events_mutex);
+
+ while (true) {
+ if (FindNextEventInternal(lock, broadcaster, broadcaster_names,
+ num_broadcaster_names, event_type_mask, event_sp,
+ true)) {
+ return true;
+ } else {
+ std::cv_status result = std::cv_status::no_timeout;
+ if (!timeout)
+ m_events_condition.wait(lock);
+ else
+ result = m_events_condition.wait_for(lock, *timeout);
+
+ if (result == std::cv_status::timeout) {
+ log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS);
+ if (log)
+ log->Printf("%p Listener::GetEventInternal() timed out for %s",
+ static_cast<void *>(this), m_name.c_str());
+ return false;
+ } else if (result != std::cv_status::no_timeout) {
+ log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS);
+ if (log)
+ log->Printf("%p Listener::GetEventInternal() unknown error for %s",
+ static_cast<void *>(this), m_name.c_str());
+ return false;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool Listener::GetEventForBroadcasterWithType(
+ Broadcaster *broadcaster, uint32_t event_type_mask, EventSP &event_sp,
+ const Timeout<std::micro> &timeout) {
+ return GetEventInternal(timeout, broadcaster, nullptr, 0, event_type_mask,
+ event_sp);
+}
+
+bool Listener::GetEventForBroadcaster(Broadcaster *broadcaster,
+ EventSP &event_sp,
+ const Timeout<std::micro> &timeout) {
+ return GetEventInternal(timeout, broadcaster, nullptr, 0, 0, event_sp);
+}
+
+bool Listener::GetEvent(EventSP &event_sp, const Timeout<std::micro> &timeout) {
+ return GetEventInternal(timeout, nullptr, nullptr, 0, 0, event_sp);
+}
+
+size_t Listener::HandleBroadcastEvent(EventSP &event_sp) {
+ size_t num_handled = 0;
+ std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex);
+ Broadcaster *broadcaster = event_sp->GetBroadcaster();
+ if (!broadcaster)
+ return 0;
+ broadcaster_collection::iterator pos;
+ broadcaster_collection::iterator end = m_broadcasters.end();
+ Broadcaster::BroadcasterImplSP broadcaster_impl_sp(
+ broadcaster->GetBroadcasterImpl());
+ for (pos = m_broadcasters.find(broadcaster_impl_sp);
+ pos != end && pos->first.lock() == broadcaster_impl_sp; ++pos) {
+ BroadcasterInfo info = pos->second;
+ if (event_sp->GetType() & info.event_mask) {
+ if (info.callback != nullptr) {
+ info.callback(event_sp, info.callback_user_data);
+ ++num_handled;
+ }
+ }
+ }
+ return num_handled;
+}
+
+uint32_t
+Listener::StartListeningForEventSpec(const BroadcasterManagerSP &manager_sp,
+ const BroadcastEventSpec &event_spec) {
+ if (!manager_sp)
+ return 0;
+
+ // The BroadcasterManager mutex must be locked before m_broadcasters_mutex to
+ // avoid violating the lock hierarchy (manager before broadcasters).
+ std::lock_guard<std::recursive_mutex> manager_guard(
+ manager_sp->m_manager_mutex);
+ std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex);
+
+ uint32_t bits_acquired = manager_sp->RegisterListenerForEvents(
+ this->shared_from_this(), event_spec);
+ if (bits_acquired) {
+ broadcaster_manager_collection::iterator iter,
+ end_iter = m_broadcaster_managers.end();
+ BroadcasterManagerWP manager_wp(manager_sp);
+ BroadcasterManagerWPMatcher matcher(manager_sp);
+ iter = std::find_if<broadcaster_manager_collection::iterator,
+ BroadcasterManagerWPMatcher>(
+ m_broadcaster_managers.begin(), end_iter, matcher);
+ if (iter == end_iter)
+ m_broadcaster_managers.push_back(manager_wp);
+ }
+
+ return bits_acquired;
+}
+
+bool Listener::StopListeningForEventSpec(const BroadcasterManagerSP &manager_sp,
+ const BroadcastEventSpec &event_spec) {
+ if (!manager_sp)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex);
+ return manager_sp->UnregisterListenerForEvents(this->shared_from_this(),
+ event_spec);
+}
+
+ListenerSP Listener::MakeListener(const char *name) {
+ return ListenerSP(new Listener(name));
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Log.cpp b/contrib/llvm-project/lldb/source/Utility/Log.cpp
new file mode 100644
index 000000000000..217b0d2ba97b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Log.cpp
@@ -0,0 +1,330 @@
+//===-- Log.cpp -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/VASPrintf.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/ADT/iterator.h"
+
+#include "llvm/Support/Chrono.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <chrono>
+#include <cstdarg>
+#include <mutex>
+#include <utility>
+
+#include <assert.h>
+#if defined(_WIN32)
+#include <process.h>
+#else
+#include <unistd.h>
+#include <pthread.h>
+#endif
+
+using namespace lldb_private;
+
+llvm::ManagedStatic<Log::ChannelMap> Log::g_channel_map;
+
+void Log::ListCategories(llvm::raw_ostream &stream, const ChannelMap::value_type &entry) {
+ stream << llvm::formatv("Logging categories for '{0}':\n", entry.first());
+ stream << " all - all available logging categories\n";
+ stream << " default - default set of logging categories\n";
+ for (const auto &category : entry.second.m_channel.categories)
+ stream << llvm::formatv(" {0} - {1}\n", category.name,
+ category.description);
+}
+
+uint32_t Log::GetFlags(llvm::raw_ostream &stream, const ChannelMap::value_type &entry,
+ llvm::ArrayRef<const char *> categories) {
+ bool list_categories = false;
+ uint32_t flags = 0;
+ for (const char *category : categories) {
+ if (llvm::StringRef("all").equals_lower(category)) {
+ flags |= UINT32_MAX;
+ continue;
+ }
+ if (llvm::StringRef("default").equals_lower(category)) {
+ flags |= entry.second.m_channel.default_flags;
+ continue;
+ }
+ auto cat = llvm::find_if(
+ entry.second.m_channel.categories,
+ [&](const Log::Category &c) { return c.name.equals_lower(category); });
+ if (cat != entry.second.m_channel.categories.end()) {
+ flags |= cat->flag;
+ continue;
+ }
+ stream << llvm::formatv("error: unrecognized log category '{0}'\n",
+ category);
+ list_categories = true;
+ }
+ if (list_categories)
+ ListCategories(stream, entry);
+ return flags;
+}
+
+void Log::Enable(const std::shared_ptr<llvm::raw_ostream> &stream_sp,
+ uint32_t options, uint32_t flags) {
+ llvm::sys::ScopedWriter lock(m_mutex);
+
+ uint32_t mask = m_mask.fetch_or(flags, std::memory_order_relaxed);
+ if (mask | flags) {
+ m_options.store(options, std::memory_order_relaxed);
+ m_stream_sp = stream_sp;
+ m_channel.log_ptr.store(this, std::memory_order_relaxed);
+ }
+}
+
+void Log::Disable(uint32_t flags) {
+ llvm::sys::ScopedWriter lock(m_mutex);
+
+ uint32_t mask = m_mask.fetch_and(~flags, std::memory_order_relaxed);
+ if (!(mask & ~flags)) {
+ m_stream_sp.reset();
+ m_channel.log_ptr.store(nullptr, std::memory_order_relaxed);
+ }
+}
+
+const Flags Log::GetOptions() const {
+ return m_options.load(std::memory_order_relaxed);
+}
+
+const Flags Log::GetMask() const {
+ return m_mask.load(std::memory_order_relaxed);
+}
+
+void Log::PutCString(const char *cstr) { Printf("%s", cstr); }
+void Log::PutString(llvm::StringRef str) { PutCString(str.str().c_str()); }
+
+// Simple variable argument logging with flags.
+void Log::Printf(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ VAPrintf(format, args);
+ va_end(args);
+}
+
+// All logging eventually boils down to this function call. If we have a
+// callback registered, then we call the logging callback. If we have a valid
+// file handle, we also log to the file.
+void Log::VAPrintf(const char *format, va_list args) {
+ llvm::SmallString<64> FinalMessage;
+ llvm::raw_svector_ostream Stream(FinalMessage);
+ WriteHeader(Stream, "", "");
+
+ llvm::SmallString<64> Content;
+ lldb_private::VASprintf(Content, format, args);
+
+ Stream << Content << "\n";
+
+ WriteMessage(FinalMessage.str());
+}
+
+// Printing of errors that are not fatal.
+void Log::Error(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ VAError(format, args);
+ va_end(args);
+}
+
+void Log::VAError(const char *format, va_list args) {
+ llvm::SmallString<64> Content;
+ VASprintf(Content, format, args);
+
+ Printf("error: %s", Content.c_str());
+}
+
+// Printing of warnings that are not fatal only if verbose mode is enabled.
+void Log::Verbose(const char *format, ...) {
+ if (!GetVerbose())
+ return;
+
+ va_list args;
+ va_start(args, format);
+ VAPrintf(format, args);
+ va_end(args);
+}
+
+// Printing of warnings that are not fatal.
+void Log::Warning(const char *format, ...) {
+ llvm::SmallString<64> Content;
+ va_list args;
+ va_start(args, format);
+ VASprintf(Content, format, args);
+ va_end(args);
+
+ Printf("warning: %s", Content.c_str());
+}
+
+void Log::Initialize() {
+#ifdef LLVM_ON_UNIX
+ pthread_atfork(nullptr, nullptr, &Log::DisableLoggingChild);
+#endif
+ InitializeLldbChannel();
+}
+
+void Log::Register(llvm::StringRef name, Channel &channel) {
+ auto iter = g_channel_map->try_emplace(name, channel);
+ assert(iter.second == true);
+ (void)iter;
+}
+
+void Log::Unregister(llvm::StringRef name) {
+ auto iter = g_channel_map->find(name);
+ assert(iter != g_channel_map->end());
+ iter->second.Disable(UINT32_MAX);
+ g_channel_map->erase(iter);
+}
+
+bool Log::EnableLogChannel(
+ const std::shared_ptr<llvm::raw_ostream> &log_stream_sp,
+ uint32_t log_options, llvm::StringRef channel,
+ llvm::ArrayRef<const char *> categories, llvm::raw_ostream &error_stream) {
+ auto iter = g_channel_map->find(channel);
+ if (iter == g_channel_map->end()) {
+ error_stream << llvm::formatv("Invalid log channel '{0}'.\n", channel);
+ return false;
+ }
+ uint32_t flags = categories.empty()
+ ? iter->second.m_channel.default_flags
+ : GetFlags(error_stream, *iter, categories);
+ iter->second.Enable(log_stream_sp, log_options, flags);
+ return true;
+}
+
+bool Log::DisableLogChannel(llvm::StringRef channel,
+ llvm::ArrayRef<const char *> categories,
+ llvm::raw_ostream &error_stream) {
+ auto iter = g_channel_map->find(channel);
+ if (iter == g_channel_map->end()) {
+ error_stream << llvm::formatv("Invalid log channel '{0}'.\n", channel);
+ return false;
+ }
+ uint32_t flags = categories.empty()
+ ? UINT32_MAX
+ : GetFlags(error_stream, *iter, categories);
+ iter->second.Disable(flags);
+ return true;
+}
+
+bool Log::ListChannelCategories(llvm::StringRef channel,
+ llvm::raw_ostream &stream) {
+ auto ch = g_channel_map->find(channel);
+ if (ch == g_channel_map->end()) {
+ stream << llvm::formatv("Invalid log channel '{0}'.\n", channel);
+ return false;
+ }
+ ListCategories(stream, *ch);
+ return true;
+}
+
+void Log::DisableAllLogChannels() {
+ for (auto &entry : *g_channel_map)
+ entry.second.Disable(UINT32_MAX);
+}
+
+void Log::ListAllLogChannels(llvm::raw_ostream &stream) {
+ if (g_channel_map->empty()) {
+ stream << "No logging channels are currently registered.\n";
+ return;
+ }
+
+ for (const auto &channel : *g_channel_map)
+ ListCategories(stream, channel);
+}
+
+bool Log::GetVerbose() const {
+ return m_options.load(std::memory_order_relaxed) & LLDB_LOG_OPTION_VERBOSE;
+}
+
+void Log::WriteHeader(llvm::raw_ostream &OS, llvm::StringRef file,
+ llvm::StringRef function) {
+ Flags options = GetOptions();
+ static uint32_t g_sequence_id = 0;
+ // Add a sequence ID if requested
+ if (options.Test(LLDB_LOG_OPTION_PREPEND_SEQUENCE))
+ OS << ++g_sequence_id << " ";
+
+ // Timestamp if requested
+ if (options.Test(LLDB_LOG_OPTION_PREPEND_TIMESTAMP)) {
+ auto now = std::chrono::duration<double>(
+ std::chrono::system_clock::now().time_since_epoch());
+ OS << llvm::formatv("{0:f9} ", now.count());
+ }
+
+ // Add the process and thread if requested
+ if (options.Test(LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD))
+ OS << llvm::formatv("[{0,0+4}/{1,0+4}] ", getpid(),
+ llvm::get_threadid());
+
+ // Add the thread name if requested
+ if (options.Test(LLDB_LOG_OPTION_PREPEND_THREAD_NAME)) {
+ llvm::SmallString<32> thread_name;
+ llvm::get_thread_name(thread_name);
+
+ llvm::SmallString<12> format_str;
+ llvm::raw_svector_ostream format_os(format_str);
+ format_os << "{0,-" << llvm::alignTo<16>(thread_name.size()) << "} ";
+ OS << llvm::formatv(format_str.c_str(), thread_name);
+ }
+
+ if (options.Test(LLDB_LOG_OPTION_BACKTRACE))
+ llvm::sys::PrintStackTrace(OS);
+
+ if (options.Test(LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION) &&
+ (!file.empty() || !function.empty())) {
+ file = llvm::sys::path::filename(file).take_front(40);
+ function = function.take_front(40);
+ OS << llvm::formatv("{0,-60:60} ", (file + ":" + function).str());
+ }
+}
+
+void Log::WriteMessage(const std::string &message) {
+ // Make a copy of our stream shared pointer in case someone disables our log
+ // while we are logging and releases the stream
+ auto stream_sp = GetStream();
+ if (!stream_sp)
+ return;
+
+ Flags options = GetOptions();
+ if (options.Test(LLDB_LOG_OPTION_THREADSAFE)) {
+ static std::recursive_mutex g_LogThreadedMutex;
+ std::lock_guard<std::recursive_mutex> guard(g_LogThreadedMutex);
+ *stream_sp << message;
+ stream_sp->flush();
+ } else {
+ *stream_sp << message;
+ stream_sp->flush();
+ }
+}
+
+void Log::Format(llvm::StringRef file, llvm::StringRef function,
+ const llvm::formatv_object_base &payload) {
+ std::string message_string;
+ llvm::raw_string_ostream message(message_string);
+ WriteHeader(message, file, function);
+ message << payload << "\n";
+ WriteMessage(message.str());
+}
+
+void Log::DisableLoggingChild() {
+ // Disable logging by clearing out the atomic variable after forking -- if we
+ // forked while another thread held the channel mutex, we would deadlock when
+ // trying to write to the log.
+ for (auto &c: *g_channel_map)
+ c.second.m_channel.log_ptr.store(nullptr, std::memory_order_relaxed);
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Logging.cpp b/contrib/llvm-project/lldb/source/Utility/Logging.cpp
new file mode 100644
index 000000000000..c0856e5d9267
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Logging.cpp
@@ -0,0 +1,74 @@
+//===-- Logging.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/ADT/ArrayRef.h"
+
+#include <stdarg.h>
+
+using namespace lldb_private;
+
+static constexpr Log::Category g_categories[] = {
+ {{"api"}, {"log API calls and return values"}, LIBLLDB_LOG_API},
+ {{"ast"}, {"log AST"}, LIBLLDB_LOG_AST},
+ {{"break"}, {"log breakpoints"}, LIBLLDB_LOG_BREAKPOINTS},
+ {{"commands"}, {"log command argument parsing"}, LIBLLDB_LOG_COMMANDS},
+ {{"comm"}, {"log communication activities"}, LIBLLDB_LOG_COMMUNICATION},
+ {{"conn"}, {"log connection details"}, LIBLLDB_LOG_CONNECTION},
+ {{"demangle"}, {"log mangled names to catch demangler crashes"}, LIBLLDB_LOG_DEMANGLE},
+ {{"dyld"}, {"log shared library related activities"}, LIBLLDB_LOG_DYNAMIC_LOADER},
+ {{"event"}, {"log broadcaster, listener and event queue activities"}, LIBLLDB_LOG_EVENTS},
+ {{"expr"}, {"log expressions"}, LIBLLDB_LOG_EXPRESSIONS},
+ {{"formatters"}, {"log data formatters related activities"}, LIBLLDB_LOG_DATAFORMATTERS},
+ {{"host"}, {"log host activities"}, LIBLLDB_LOG_HOST},
+ {{"jit"}, {"log JIT events in the target"}, LIBLLDB_LOG_JIT_LOADER},
+ {{"language"}, {"log language runtime events"}, LIBLLDB_LOG_LANGUAGE},
+ {{"mmap"}, {"log mmap related activities"}, LIBLLDB_LOG_MMAP},
+ {{"module"}, {"log module activities such as when modules are created, destroyed, replaced, and more"}, LIBLLDB_LOG_MODULES},
+ {{"object"}, {"log object construction/destruction for important objects"}, LIBLLDB_LOG_OBJECT},
+ {{"os"}, {"log OperatingSystem plugin related activities"}, LIBLLDB_LOG_OS},
+ {{"platform"}, {"log platform events and activities"}, LIBLLDB_LOG_PLATFORM},
+ {{"process"}, {"log process events and activities"}, LIBLLDB_LOG_PROCESS},
+ {{"script"}, {"log events about the script interpreter"}, LIBLLDB_LOG_SCRIPT},
+ {{"state"}, {"log private and public process state changes"}, LIBLLDB_LOG_STATE},
+ {{"step"}, {"log step related activities"}, LIBLLDB_LOG_STEP},
+ {{"symbol"}, {"log symbol related issues and warnings"}, LIBLLDB_LOG_SYMBOLS},
+ {{"system-runtime"}, {"log system runtime events"}, LIBLLDB_LOG_SYSTEM_RUNTIME},
+ {{"target"}, {"log target events and activities"}, LIBLLDB_LOG_TARGET},
+ {{"temp"}, {"log internal temporary debug messages"}, LIBLLDB_LOG_TEMPORARY},
+ {{"thread"}, {"log thread events and activities"}, LIBLLDB_LOG_THREAD},
+ {{"types"}, {"log type system related activities"}, LIBLLDB_LOG_TYPES},
+ {{"unwind"}, {"log stack unwind activities"}, LIBLLDB_LOG_UNWIND},
+ {{"watch"}, {"log watchpoint related activities"}, LIBLLDB_LOG_WATCHPOINTS},
+};
+
+static Log::Channel g_log_channel(g_categories, LIBLLDB_LOG_DEFAULT);
+
+void lldb_private::InitializeLldbChannel() {
+ Log::Register("lldb", g_log_channel);
+}
+
+Log *lldb_private::GetLogIfAllCategoriesSet(uint32_t mask) {
+ return g_log_channel.GetLogIfAll(mask);
+}
+
+Log *lldb_private::GetLogIfAnyCategoriesSet(uint32_t mask) {
+ return g_log_channel.GetLogIfAny(mask);
+}
+
+
+void lldb_private::LogIfAnyCategoriesSet(uint32_t mask, const char *format, ...) {
+ if (Log *log = GetLogIfAnyCategoriesSet(mask)) {
+ va_list args;
+ va_start(args, format);
+ log->VAPrintf(format, args);
+ va_end(args);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/NameMatches.cpp b/contrib/llvm-project/lldb/source/Utility/NameMatches.cpp
new file mode 100644
index 000000000000..5c9579ea7332
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/NameMatches.cpp
@@ -0,0 +1,34 @@
+//===-- NameMatches.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "lldb/Utility/NameMatches.h"
+#include "lldb/Utility/RegularExpression.h"
+
+#include "llvm/ADT/StringRef.h"
+
+using namespace lldb_private;
+
+bool lldb_private::NameMatches(llvm::StringRef name, NameMatch match_type,
+ llvm::StringRef match) {
+ switch (match_type) {
+ case NameMatch::Ignore:
+ return true;
+ case NameMatch::Equals:
+ return name == match;
+ case NameMatch::Contains:
+ return name.contains(match);
+ case NameMatch::StartsWith:
+ return name.startswith(match);
+ case NameMatch::EndsWith:
+ return name.endswith(match);
+ case NameMatch::RegularExpression: {
+ RegularExpression regex(match);
+ return regex.Execute(name);
+ }
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/PPC64LE_DWARF_Registers.h b/contrib/llvm-project/lldb/source/Utility/PPC64LE_DWARF_Registers.h
new file mode 100644
index 000000000000..548c1fda8600
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/PPC64LE_DWARF_Registers.h
@@ -0,0 +1,193 @@
+//===-- PPC64LE_DWARF_Registers.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_PPC64LE_DWARF_Registers_h_
+#define utility_PPC64LE_DWARF_Registers_h_
+
+#include "lldb/lldb-private.h"
+
+namespace ppc64le_dwarf {
+
+enum {
+ dwarf_r0_ppc64le = 0,
+ dwarf_r1_ppc64le,
+ dwarf_r2_ppc64le,
+ dwarf_r3_ppc64le,
+ dwarf_r4_ppc64le,
+ dwarf_r5_ppc64le,
+ dwarf_r6_ppc64le,
+ dwarf_r7_ppc64le,
+ dwarf_r8_ppc64le,
+ dwarf_r9_ppc64le,
+ dwarf_r10_ppc64le,
+ dwarf_r11_ppc64le,
+ dwarf_r12_ppc64le,
+ dwarf_r13_ppc64le,
+ dwarf_r14_ppc64le,
+ dwarf_r15_ppc64le,
+ dwarf_r16_ppc64le,
+ dwarf_r17_ppc64le,
+ dwarf_r18_ppc64le,
+ dwarf_r19_ppc64le,
+ dwarf_r20_ppc64le,
+ dwarf_r21_ppc64le,
+ dwarf_r22_ppc64le,
+ dwarf_r23_ppc64le,
+ dwarf_r24_ppc64le,
+ dwarf_r25_ppc64le,
+ dwarf_r26_ppc64le,
+ dwarf_r27_ppc64le,
+ dwarf_r28_ppc64le,
+ dwarf_r29_ppc64le,
+ dwarf_r30_ppc64le,
+ dwarf_r31_ppc64le,
+ dwarf_f0_ppc64le,
+ dwarf_f1_ppc64le,
+ dwarf_f2_ppc64le,
+ dwarf_f3_ppc64le,
+ dwarf_f4_ppc64le,
+ dwarf_f5_ppc64le,
+ dwarf_f6_ppc64le,
+ dwarf_f7_ppc64le,
+ dwarf_f8_ppc64le,
+ dwarf_f9_ppc64le,
+ dwarf_f10_ppc64le,
+ dwarf_f11_ppc64le,
+ dwarf_f12_ppc64le,
+ dwarf_f13_ppc64le,
+ dwarf_f14_ppc64le,
+ dwarf_f15_ppc64le,
+ dwarf_f16_ppc64le,
+ dwarf_f17_ppc64le,
+ dwarf_f18_ppc64le,
+ dwarf_f19_ppc64le,
+ dwarf_f20_ppc64le,
+ dwarf_f21_ppc64le,
+ dwarf_f22_ppc64le,
+ dwarf_f23_ppc64le,
+ dwarf_f24_ppc64le,
+ dwarf_f25_ppc64le,
+ dwarf_f26_ppc64le,
+ dwarf_f27_ppc64le,
+ dwarf_f28_ppc64le,
+ dwarf_f29_ppc64le,
+ dwarf_f30_ppc64le,
+ dwarf_f31_ppc64le,
+ dwarf_lr_ppc64le = 65,
+ dwarf_ctr_ppc64le,
+ dwarf_cr_ppc64le = 68,
+ dwarf_xer_ppc64le = 76,
+ dwarf_vr0_ppc64le,
+ dwarf_vr1_ppc64le,
+ dwarf_vr2_ppc64le,
+ dwarf_vr3_ppc64le,
+ dwarf_vr4_ppc64le,
+ dwarf_vr5_ppc64le,
+ dwarf_vr6_ppc64le,
+ dwarf_vr7_ppc64le,
+ dwarf_vr8_ppc64le,
+ dwarf_vr9_ppc64le,
+ dwarf_vr10_ppc64le,
+ dwarf_vr11_ppc64le,
+ dwarf_vr12_ppc64le,
+ dwarf_vr13_ppc64le,
+ dwarf_vr14_ppc64le,
+ dwarf_vr15_ppc64le,
+ dwarf_vr16_ppc64le,
+ dwarf_vr17_ppc64le,
+ dwarf_vr18_ppc64le,
+ dwarf_vr19_ppc64le,
+ dwarf_vr20_ppc64le,
+ dwarf_vr21_ppc64le,
+ dwarf_vr22_ppc64le,
+ dwarf_vr23_ppc64le,
+ dwarf_vr24_ppc64le,
+ dwarf_vr25_ppc64le,
+ dwarf_vr26_ppc64le,
+ dwarf_vr27_ppc64le,
+ dwarf_vr28_ppc64le,
+ dwarf_vr29_ppc64le,
+ dwarf_vr30_ppc64le,
+ dwarf_vr31_ppc64le,
+ dwarf_vscr_ppc64le = 110,
+ dwarf_vrsave_ppc64le = 117,
+ dwarf_pc_ppc64le,
+ dwarf_softe_ppc64le,
+ dwarf_trap_ppc64le,
+ dwarf_origr3_ppc64le,
+ dwarf_fpscr_ppc64le,
+ dwarf_msr_ppc64le,
+ dwarf_vs0_ppc64le,
+ dwarf_vs1_ppc64le,
+ dwarf_vs2_ppc64le,
+ dwarf_vs3_ppc64le,
+ dwarf_vs4_ppc64le,
+ dwarf_vs5_ppc64le,
+ dwarf_vs6_ppc64le,
+ dwarf_vs7_ppc64le,
+ dwarf_vs8_ppc64le,
+ dwarf_vs9_ppc64le,
+ dwarf_vs10_ppc64le,
+ dwarf_vs11_ppc64le,
+ dwarf_vs12_ppc64le,
+ dwarf_vs13_ppc64le,
+ dwarf_vs14_ppc64le,
+ dwarf_vs15_ppc64le,
+ dwarf_vs16_ppc64le,
+ dwarf_vs17_ppc64le,
+ dwarf_vs18_ppc64le,
+ dwarf_vs19_ppc64le,
+ dwarf_vs20_ppc64le,
+ dwarf_vs21_ppc64le,
+ dwarf_vs22_ppc64le,
+ dwarf_vs23_ppc64le,
+ dwarf_vs24_ppc64le,
+ dwarf_vs25_ppc64le,
+ dwarf_vs26_ppc64le,
+ dwarf_vs27_ppc64le,
+ dwarf_vs28_ppc64le,
+ dwarf_vs29_ppc64le,
+ dwarf_vs30_ppc64le,
+ dwarf_vs31_ppc64le,
+ dwarf_vs32_ppc64le,
+ dwarf_vs33_ppc64le,
+ dwarf_vs34_ppc64le,
+ dwarf_vs35_ppc64le,
+ dwarf_vs36_ppc64le,
+ dwarf_vs37_ppc64le,
+ dwarf_vs38_ppc64le,
+ dwarf_vs39_ppc64le,
+ dwarf_vs40_ppc64le,
+ dwarf_vs41_ppc64le,
+ dwarf_vs42_ppc64le,
+ dwarf_vs43_ppc64le,
+ dwarf_vs44_ppc64le,
+ dwarf_vs45_ppc64le,
+ dwarf_vs46_ppc64le,
+ dwarf_vs47_ppc64le,
+ dwarf_vs48_ppc64le,
+ dwarf_vs49_ppc64le,
+ dwarf_vs50_ppc64le,
+ dwarf_vs51_ppc64le,
+ dwarf_vs52_ppc64le,
+ dwarf_vs53_ppc64le,
+ dwarf_vs54_ppc64le,
+ dwarf_vs55_ppc64le,
+ dwarf_vs56_ppc64le,
+ dwarf_vs57_ppc64le,
+ dwarf_vs58_ppc64le,
+ dwarf_vs59_ppc64le,
+ dwarf_vs60_ppc64le,
+ dwarf_vs61_ppc64le,
+ dwarf_vs62_ppc64le,
+ dwarf_vs63_ppc64le,
+};
+
+} // namespace ppc64le_dwarf
+
+#endif // utility_PPC64LE_DWARF_Registers_h_
diff --git a/contrib/llvm-project/lldb/source/Utility/PPC64LE_ehframe_Registers.h b/contrib/llvm-project/lldb/source/Utility/PPC64LE_ehframe_Registers.h
new file mode 100644
index 000000000000..77cb3e5924e7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/PPC64LE_ehframe_Registers.h
@@ -0,0 +1,193 @@
+//===-- PPC64LE_ehframe_Registers.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_PPC64LE_ehframe_Registers_h_
+#define utility_PPC64LE_ehframe_Registers_h_
+
+// The register numbers used in the eh_frame unwind information.
+// Should be the same as DWARF register numbers.
+
+namespace ppc64le_ehframe {
+
+enum {
+ r0 = 0,
+ r1,
+ r2,
+ r3,
+ r4,
+ r5,
+ r6,
+ r7,
+ r8,
+ r9,
+ r10,
+ r11,
+ r12,
+ r13,
+ r14,
+ r15,
+ r16,
+ r17,
+ r18,
+ r19,
+ r20,
+ r21,
+ r22,
+ r23,
+ r24,
+ r25,
+ r26,
+ r27,
+ r28,
+ r29,
+ r30,
+ r31,
+ f0,
+ f1,
+ f2,
+ f3,
+ f4,
+ f5,
+ f6,
+ f7,
+ f8,
+ f9,
+ f10,
+ f11,
+ f12,
+ f13,
+ f14,
+ f15,
+ f16,
+ f17,
+ f18,
+ f19,
+ f20,
+ f21,
+ f22,
+ f23,
+ f24,
+ f25,
+ f26,
+ f27,
+ f28,
+ f29,
+ f30,
+ f31,
+ lr = 65,
+ ctr,
+ cr = 68,
+ xer = 76,
+ vr0,
+ vr1,
+ vr2,
+ vr3,
+ vr4,
+ vr5,
+ vr6,
+ vr7,
+ vr8,
+ vr9,
+ vr10,
+ vr11,
+ vr12,
+ vr13,
+ vr14,
+ vr15,
+ vr16,
+ vr17,
+ vr18,
+ vr19,
+ vr20,
+ vr21,
+ vr22,
+ vr23,
+ vr24,
+ vr25,
+ vr26,
+ vr27,
+ vr28,
+ vr29,
+ vr30,
+ vr31,
+ vscr = 110,
+ vrsave = 117,
+ pc,
+ softe,
+ trap,
+ origr3,
+ fpscr,
+ msr,
+ vs0,
+ vs1,
+ vs2,
+ vs3,
+ vs4,
+ vs5,
+ vs6,
+ vs7,
+ vs8,
+ vs9,
+ vs10,
+ vs11,
+ vs12,
+ vs13,
+ vs14,
+ vs15,
+ vs16,
+ vs17,
+ vs18,
+ vs19,
+ vs20,
+ vs21,
+ vs22,
+ vs23,
+ vs24,
+ vs25,
+ vs26,
+ vs27,
+ vs28,
+ vs29,
+ vs30,
+ vs31,
+ vs32,
+ vs33,
+ vs34,
+ vs35,
+ vs36,
+ vs37,
+ vs38,
+ vs39,
+ vs40,
+ vs41,
+ vs42,
+ vs43,
+ vs44,
+ vs45,
+ vs46,
+ vs47,
+ vs48,
+ vs49,
+ vs50,
+ vs51,
+ vs52,
+ vs53,
+ vs54,
+ vs55,
+ vs56,
+ vs57,
+ vs58,
+ vs59,
+ vs60,
+ vs61,
+ vs62,
+ vs63,
+};
+}
+
+#endif // utility_PPC64LE_ehframe_Registers_h_
diff --git a/contrib/llvm-project/lldb/source/Utility/PPC64_DWARF_Registers.h b/contrib/llvm-project/lldb/source/Utility/PPC64_DWARF_Registers.h
new file mode 100644
index 000000000000..6ba5b6ac3727
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/PPC64_DWARF_Registers.h
@@ -0,0 +1,126 @@
+//===-- PPC64_DWARF_Registers.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_PPC64_DWARF_Registers_h_
+#define utility_PPC64_DWARF_Registers_h_
+
+#include "lldb/lldb-private.h"
+
+namespace ppc64_dwarf {
+
+enum {
+ dwarf_r0_ppc64 = 0,
+ dwarf_r1_ppc64,
+ dwarf_r2_ppc64,
+ dwarf_r3_ppc64,
+ dwarf_r4_ppc64,
+ dwarf_r5_ppc64,
+ dwarf_r6_ppc64,
+ dwarf_r7_ppc64,
+ dwarf_r8_ppc64,
+ dwarf_r9_ppc64,
+ dwarf_r10_ppc64,
+ dwarf_r11_ppc64,
+ dwarf_r12_ppc64,
+ dwarf_r13_ppc64,
+ dwarf_r14_ppc64,
+ dwarf_r15_ppc64,
+ dwarf_r16_ppc64,
+ dwarf_r17_ppc64,
+ dwarf_r18_ppc64,
+ dwarf_r19_ppc64,
+ dwarf_r20_ppc64,
+ dwarf_r21_ppc64,
+ dwarf_r22_ppc64,
+ dwarf_r23_ppc64,
+ dwarf_r24_ppc64,
+ dwarf_r25_ppc64,
+ dwarf_r26_ppc64,
+ dwarf_r27_ppc64,
+ dwarf_r28_ppc64,
+ dwarf_r29_ppc64,
+ dwarf_r30_ppc64,
+ dwarf_r31_ppc64,
+ dwarf_f0_ppc64,
+ dwarf_f1_ppc64,
+ dwarf_f2_ppc64,
+ dwarf_f3_ppc64,
+ dwarf_f4_ppc64,
+ dwarf_f5_ppc64,
+ dwarf_f6_ppc64,
+ dwarf_f7_ppc64,
+ dwarf_f8_ppc64,
+ dwarf_f9_ppc64,
+ dwarf_f10_ppc64,
+ dwarf_f11_ppc64,
+ dwarf_f12_ppc64,
+ dwarf_f13_ppc64,
+ dwarf_f14_ppc64,
+ dwarf_f15_ppc64,
+ dwarf_f16_ppc64,
+ dwarf_f17_ppc64,
+ dwarf_f18_ppc64,
+ dwarf_f19_ppc64,
+ dwarf_f20_ppc64,
+ dwarf_f21_ppc64,
+ dwarf_f22_ppc64,
+ dwarf_f23_ppc64,
+ dwarf_f24_ppc64,
+ dwarf_f25_ppc64,
+ dwarf_f26_ppc64,
+ dwarf_f27_ppc64,
+ dwarf_f28_ppc64,
+ dwarf_f29_ppc64,
+ dwarf_f30_ppc64,
+ dwarf_f31_ppc64,
+ dwarf_cr_ppc64 = 64,
+ dwarf_fpscr_ppc64,
+ dwarf_msr_ppc64,
+ dwarf_xer_ppc64 = 100,
+ dwarf_lr_ppc64 = 108,
+ dwarf_ctr_ppc64,
+ dwarf_vscr_ppc64,
+ dwarf_vrsave_ppc64 = 356,
+ dwarf_pc_ppc64,
+ dwarf_vr0_ppc64 = 1124,
+ dwarf_vr1_ppc64,
+ dwarf_vr2_ppc64,
+ dwarf_vr3_ppc64,
+ dwarf_vr4_ppc64,
+ dwarf_vr5_ppc64,
+ dwarf_vr6_ppc64,
+ dwarf_vr7_ppc64,
+ dwarf_vr8_ppc64,
+ dwarf_vr9_ppc64,
+ dwarf_vr10_ppc64,
+ dwarf_vr11_ppc64,
+ dwarf_vr12_ppc64,
+ dwarf_vr13_ppc64,
+ dwarf_vr14_ppc64,
+ dwarf_vr15_ppc64,
+ dwarf_vr16_ppc64,
+ dwarf_vr17_ppc64,
+ dwarf_vr18_ppc64,
+ dwarf_vr19_ppc64,
+ dwarf_vr20_ppc64,
+ dwarf_vr21_ppc64,
+ dwarf_vr22_ppc64,
+ dwarf_vr23_ppc64,
+ dwarf_vr24_ppc64,
+ dwarf_vr25_ppc64,
+ dwarf_vr26_ppc64,
+ dwarf_vr27_ppc64,
+ dwarf_vr28_ppc64,
+ dwarf_vr29_ppc64,
+ dwarf_vr30_ppc64,
+ dwarf_vr31_ppc64,
+};
+
+} // namespace ppc64_dwarf
+
+#endif // utility_PPC64_DWARF_Registers_h_
diff --git a/contrib/llvm-project/lldb/source/Utility/ProcessInfo.cpp b/contrib/llvm-project/lldb/source/Utility/ProcessInfo.cpp
new file mode 100644
index 000000000000..b67ae8779000
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/ProcessInfo.cpp
@@ -0,0 +1,310 @@
+//===-- ProcessInfo.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/ProcessInfo.h"
+
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/UserIDResolver.h"
+#include "llvm/ADT/SmallString.h"
+
+#include <climits>
+
+using namespace lldb;
+using namespace lldb_private;
+
+ProcessInfo::ProcessInfo()
+ : m_executable(), m_arguments(), m_environment(), m_uid(UINT32_MAX),
+ m_gid(UINT32_MAX), m_arch(), m_pid(LLDB_INVALID_PROCESS_ID) {}
+
+ProcessInfo::ProcessInfo(const char *name, const ArchSpec &arch,
+ lldb::pid_t pid)
+ : m_executable(name), m_arguments(), m_environment(), m_uid(UINT32_MAX),
+ m_gid(UINT32_MAX), m_arch(arch), m_pid(pid) {}
+
+void ProcessInfo::Clear() {
+ m_executable.Clear();
+ m_arguments.Clear();
+ m_environment.clear();
+ m_uid = UINT32_MAX;
+ m_gid = UINT32_MAX;
+ m_arch.Clear();
+ m_pid = LLDB_INVALID_PROCESS_ID;
+}
+
+const char *ProcessInfo::GetName() const {
+ return m_executable.GetFilename().GetCString();
+}
+
+size_t ProcessInfo::GetNameLength() const {
+ return m_executable.GetFilename().GetLength();
+}
+
+void ProcessInfo::Dump(Stream &s, Platform *platform) const {
+ s << "Executable: " << GetName() << "\n";
+ s << "Triple: ";
+ m_arch.DumpTriple(s);
+ s << "\n";
+
+ s << "Arguments:\n";
+ m_arguments.Dump(s);
+
+ s.Format("Environment:\n{0}", m_environment);
+}
+
+void ProcessInfo::SetExecutableFile(const FileSpec &exe_file,
+ bool add_exe_file_as_first_arg) {
+ if (exe_file) {
+ m_executable = exe_file;
+ if (add_exe_file_as_first_arg) {
+ llvm::SmallString<128> filename;
+ exe_file.GetPath(filename);
+ if (!filename.empty())
+ m_arguments.InsertArgumentAtIndex(0, filename);
+ }
+ } else {
+ m_executable.Clear();
+ }
+}
+
+llvm::StringRef ProcessInfo::GetArg0() const { return m_arg0; }
+
+void ProcessInfo::SetArg0(llvm::StringRef arg) { m_arg0 = arg; }
+
+void ProcessInfo::SetArguments(char const **argv,
+ bool first_arg_is_executable) {
+ m_arguments.SetArguments(argv);
+
+ // Is the first argument the executable?
+ if (first_arg_is_executable) {
+ const char *first_arg = m_arguments.GetArgumentAtIndex(0);
+ if (first_arg) {
+ // Yes the first argument is an executable, set it as the executable in
+ // the launch options. Don't resolve the file path as the path could be a
+ // remote platform path
+ m_executable.SetFile(first_arg, FileSpec::Style::native);
+ }
+ }
+}
+
+void ProcessInfo::SetArguments(const Args &args, bool first_arg_is_executable) {
+ // Copy all arguments
+ m_arguments = args;
+
+ // Is the first argument the executable?
+ if (first_arg_is_executable) {
+ const char *first_arg = m_arguments.GetArgumentAtIndex(0);
+ if (first_arg) {
+ // Yes the first argument is an executable, set it as the executable in
+ // the launch options. Don't resolve the file path as the path could be a
+ // remote platform path
+ m_executable.SetFile(first_arg, FileSpec::Style::native);
+ }
+ }
+}
+
+void ProcessInstanceInfo::Dump(Stream &s, UserIDResolver &resolver) const {
+ if (m_pid != LLDB_INVALID_PROCESS_ID)
+ s.Printf(" pid = %" PRIu64 "\n", m_pid);
+
+ if (m_parent_pid != LLDB_INVALID_PROCESS_ID)
+ s.Printf(" parent = %" PRIu64 "\n", m_parent_pid);
+
+ if (m_executable) {
+ s.Printf(" name = %s\n", m_executable.GetFilename().GetCString());
+ s.PutCString(" file = ");
+ m_executable.Dump(&s);
+ s.EOL();
+ }
+ const uint32_t argc = m_arguments.GetArgumentCount();
+ if (argc > 0) {
+ for (uint32_t i = 0; i < argc; i++) {
+ const char *arg = m_arguments.GetArgumentAtIndex(i);
+ if (i < 10)
+ s.Printf(" arg[%u] = %s\n", i, arg);
+ else
+ s.Printf("arg[%u] = %s\n", i, arg);
+ }
+ }
+
+ s.Format("{0}", m_environment);
+
+ if (m_arch.IsValid()) {
+ s.Printf(" arch = ");
+ m_arch.DumpTriple(s);
+ s.EOL();
+ }
+
+ if (UserIDIsValid()) {
+ s.Format(" uid = {0,-5} ({1})\n", GetUserID(),
+ resolver.GetUserName(GetUserID()).getValueOr(""));
+ }
+ if (GroupIDIsValid()) {
+ s.Format(" gid = {0,-5} ({1})\n", GetGroupID(),
+ resolver.GetGroupName(GetGroupID()).getValueOr(""));
+ }
+ if (EffectiveUserIDIsValid()) {
+ s.Format(" euid = {0,-5} ({1})\n", GetEffectiveUserID(),
+ resolver.GetUserName(GetEffectiveUserID()).getValueOr(""));
+ }
+ if (EffectiveGroupIDIsValid()) {
+ s.Format(" egid = {0,-5} ({1})\n", GetEffectiveGroupID(),
+ resolver.GetGroupName(GetEffectiveGroupID()).getValueOr(""));
+ }
+}
+
+void ProcessInstanceInfo::DumpTableHeader(Stream &s, bool show_args,
+ bool verbose) {
+ const char *label;
+ if (show_args || verbose)
+ label = "ARGUMENTS";
+ else
+ label = "NAME";
+
+ if (verbose) {
+ s.Printf("PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE "
+ " %s\n",
+ label);
+ s.PutCString("====== ====== ========== ========== ========== ========== "
+ "======================== ============================\n");
+ } else {
+ s.Printf("PID PARENT USER TRIPLE %s\n", label);
+ s.PutCString("====== ====== ========== ======================== "
+ "============================\n");
+ }
+}
+
+void ProcessInstanceInfo::DumpAsTableRow(Stream &s, UserIDResolver &resolver,
+ bool show_args, bool verbose) const {
+ if (m_pid != LLDB_INVALID_PROCESS_ID) {
+ s.Printf("%-6" PRIu64 " %-6" PRIu64 " ", m_pid, m_parent_pid);
+
+ StreamString arch_strm;
+ if (m_arch.IsValid())
+ m_arch.DumpTriple(arch_strm);
+
+ auto print = [&](UserIDResolver::id_t id,
+ llvm::Optional<llvm::StringRef> (UserIDResolver::*get)(
+ UserIDResolver::id_t id)) {
+ if (auto name = (resolver.*get)(id))
+ s.Format("{0,-10} ", *name);
+ else
+ s.Format("{0,-10} ", id);
+ };
+ if (verbose) {
+ print(m_uid, &UserIDResolver::GetUserName);
+ print(m_gid, &UserIDResolver::GetGroupName);
+ print(m_euid, &UserIDResolver::GetUserName);
+ print(m_egid, &UserIDResolver::GetGroupName);
+
+ s.Printf("%-24s ", arch_strm.GetData());
+ } else {
+ print(m_euid, &UserIDResolver::GetUserName);
+ s.Printf(" %-24s ", arch_strm.GetData());
+ }
+
+ if (verbose || show_args) {
+ const uint32_t argc = m_arguments.GetArgumentCount();
+ if (argc > 0) {
+ for (uint32_t i = 0; i < argc; i++) {
+ if (i > 0)
+ s.PutChar(' ');
+ s.PutCString(m_arguments.GetArgumentAtIndex(i));
+ }
+ }
+ } else {
+ s.PutCString(GetName());
+ }
+
+ s.EOL();
+ }
+}
+
+bool ProcessInstanceInfoMatch::NameMatches(const char *process_name) const {
+ if (m_name_match_type == NameMatch::Ignore || process_name == nullptr)
+ return true;
+ const char *match_name = m_match_info.GetName();
+ if (!match_name)
+ return true;
+
+ return lldb_private::NameMatches(process_name, m_name_match_type, match_name);
+}
+
+bool ProcessInstanceInfoMatch::Matches(
+ const ProcessInstanceInfo &proc_info) const {
+ if (!NameMatches(proc_info.GetName()))
+ return false;
+
+ if (m_match_info.ProcessIDIsValid() &&
+ m_match_info.GetProcessID() != proc_info.GetProcessID())
+ return false;
+
+ if (m_match_info.ParentProcessIDIsValid() &&
+ m_match_info.GetParentProcessID() != proc_info.GetParentProcessID())
+ return false;
+
+ if (m_match_info.UserIDIsValid() &&
+ m_match_info.GetUserID() != proc_info.GetUserID())
+ return false;
+
+ if (m_match_info.GroupIDIsValid() &&
+ m_match_info.GetGroupID() != proc_info.GetGroupID())
+ return false;
+
+ if (m_match_info.EffectiveUserIDIsValid() &&
+ m_match_info.GetEffectiveUserID() != proc_info.GetEffectiveUserID())
+ return false;
+
+ if (m_match_info.EffectiveGroupIDIsValid() &&
+ m_match_info.GetEffectiveGroupID() != proc_info.GetEffectiveGroupID())
+ return false;
+
+ if (m_match_info.GetArchitecture().IsValid() &&
+ !m_match_info.GetArchitecture().IsCompatibleMatch(
+ proc_info.GetArchitecture()))
+ return false;
+ return true;
+}
+
+bool ProcessInstanceInfoMatch::MatchAllProcesses() const {
+ if (m_name_match_type != NameMatch::Ignore)
+ return false;
+
+ if (m_match_info.ProcessIDIsValid())
+ return false;
+
+ if (m_match_info.ParentProcessIDIsValid())
+ return false;
+
+ if (m_match_info.UserIDIsValid())
+ return false;
+
+ if (m_match_info.GroupIDIsValid())
+ return false;
+
+ if (m_match_info.EffectiveUserIDIsValid())
+ return false;
+
+ if (m_match_info.EffectiveGroupIDIsValid())
+ return false;
+
+ if (m_match_info.GetArchitecture().IsValid())
+ return false;
+
+ if (m_match_all_users)
+ return false;
+
+ return true;
+}
+
+void ProcessInstanceInfoMatch::Clear() {
+ m_match_info.Clear();
+ m_name_match_type = NameMatch::Ignore;
+ m_match_all_users = false;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/RegisterValue.cpp b/contrib/llvm-project/lldb/source/Utility/RegisterValue.cpp
new file mode 100644
index 000000000000..a01c35a2818e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/RegisterValue.cpp
@@ -0,0 +1,878 @@
+//===-- RegisterValue.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/RegisterValue.h"
+
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-private-types.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <cstdint>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool RegisterValue::GetData(DataExtractor &data) const {
+ return data.SetData(GetBytes(), GetByteSize(), GetByteOrder()) > 0;
+}
+
+uint32_t RegisterValue::GetAsMemoryData(const RegisterInfo *reg_info, void *dst,
+ uint32_t dst_len,
+ lldb::ByteOrder dst_byte_order,
+ Status &error) const {
+ if (reg_info == nullptr) {
+ error.SetErrorString("invalid register info argument.");
+ return 0;
+ }
+
+ // ReadRegister should have already been called on this object prior to
+ // calling this.
+ if (GetType() == eTypeInvalid) {
+ // No value has been read into this object...
+ error.SetErrorStringWithFormat(
+ "invalid register value type for register %s", reg_info->name);
+ return 0;
+ }
+
+ if (dst_len > kMaxRegisterByteSize) {
+ error.SetErrorString("destination is too big");
+ return 0;
+ }
+
+ const uint32_t src_len = reg_info->byte_size;
+
+ // Extract the register data into a data extractor
+ DataExtractor reg_data;
+ if (!GetData(reg_data)) {
+ error.SetErrorString("invalid register value to copy into");
+ return 0;
+ }
+
+ // Prepare a memory buffer that contains some or all of the register value
+ const uint32_t bytes_copied =
+ reg_data.CopyByteOrderedData(0, // src offset
+ src_len, // src length
+ dst, // dst buffer
+ dst_len, // dst length
+ dst_byte_order); // dst byte order
+ if (bytes_copied == 0)
+ error.SetErrorStringWithFormat(
+ "failed to copy data for register write of %s", reg_info->name);
+
+ return bytes_copied;
+}
+
+uint32_t RegisterValue::SetFromMemoryData(const RegisterInfo *reg_info,
+ const void *src, uint32_t src_len,
+ lldb::ByteOrder src_byte_order,
+ Status &error) {
+ if (reg_info == nullptr) {
+ error.SetErrorString("invalid register info argument.");
+ return 0;
+ }
+
+ // Moving from addr into a register
+ //
+ // Case 1: src_len == dst_len
+ //
+ // |AABBCCDD| Address contents
+ // |AABBCCDD| Register contents
+ //
+ // Case 2: src_len > dst_len
+ //
+ // Status! (The register should always be big enough to hold the data)
+ //
+ // Case 3: src_len < dst_len
+ //
+ // |AABB| Address contents
+ // |AABB0000| Register contents [on little-endian hardware]
+ // |0000AABB| Register contents [on big-endian hardware]
+ if (src_len > kMaxRegisterByteSize) {
+ error.SetErrorStringWithFormat(
+ "register buffer is too small to receive %u bytes of data.", src_len);
+ return 0;
+ }
+
+ const uint32_t dst_len = reg_info->byte_size;
+
+ if (src_len > dst_len) {
+ error.SetErrorStringWithFormat(
+ "%u bytes is too big to store in register %s (%u bytes)", src_len,
+ reg_info->name, dst_len);
+ return 0;
+ }
+
+ // Use a data extractor to correctly copy and pad the bytes read into the
+ // register value
+ DataExtractor src_data(src, src_len, src_byte_order, 4);
+
+ error = SetValueFromData(reg_info, src_data, 0, true);
+ if (error.Fail())
+ return 0;
+
+ // If SetValueFromData succeeded, we must have copied all of src_len
+ return src_len;
+}
+
+bool RegisterValue::GetScalarValue(Scalar &scalar) const {
+ switch (m_type) {
+ case eTypeInvalid:
+ break;
+ case eTypeBytes: {
+ switch (buffer.length) {
+ default:
+ break;
+ case 1:
+ scalar = *(const uint8_t *)buffer.bytes;
+ return true;
+ case 2:
+ scalar = *reinterpret_cast<const uint16_t *>(buffer.bytes);
+ return true;
+ case 4:
+ scalar = *reinterpret_cast<const uint32_t *>(buffer.bytes);
+ return true;
+ case 8:
+ scalar = *reinterpret_cast<const uint64_t *>(buffer.bytes);
+ return true;
+ case 16:
+ case 32:
+ case 64:
+ if (buffer.length % sizeof(uint64_t) == 0) {
+ const auto length_in_bits = buffer.length * 8;
+ const auto length_in_uint64 = buffer.length / sizeof(uint64_t);
+ scalar =
+ llvm::APInt(length_in_bits,
+ llvm::ArrayRef<uint64_t>(
+ reinterpret_cast<const uint64_t *>(buffer.bytes),
+ length_in_uint64));
+ return true;
+ }
+ break;
+ }
+ } break;
+ case eTypeUInt8:
+ case eTypeUInt16:
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ scalar = m_scalar;
+ return true;
+ }
+ return false;
+}
+
+void RegisterValue::Clear() { m_type = eTypeInvalid; }
+
+RegisterValue::Type RegisterValue::SetType(const RegisterInfo *reg_info) {
+ // To change the type, we simply copy the data in again, using the new format
+ RegisterValue copy;
+ DataExtractor copy_data;
+ if (copy.CopyValue(*this) && copy.GetData(copy_data))
+ SetValueFromData(reg_info, copy_data, 0, true);
+
+ return m_type;
+}
+
+Status RegisterValue::SetValueFromData(const RegisterInfo *reg_info,
+ DataExtractor &src,
+ lldb::offset_t src_offset,
+ bool partial_data_ok) {
+ Status error;
+
+ if (src.GetByteSize() == 0) {
+ error.SetErrorString("empty data.");
+ return error;
+ }
+
+ if (reg_info->byte_size == 0) {
+ error.SetErrorString("invalid register info.");
+ return error;
+ }
+
+ uint32_t src_len = src.GetByteSize() - src_offset;
+
+ if (!partial_data_ok && (src_len < reg_info->byte_size)) {
+ error.SetErrorString("not enough data.");
+ return error;
+ }
+
+ // Cap the data length if there is more than enough bytes for this register
+ // value
+ if (src_len > reg_info->byte_size)
+ src_len = reg_info->byte_size;
+
+ // Zero out the value in case we get partial data...
+ memset(buffer.bytes, 0, sizeof(buffer.bytes));
+
+ type128 int128;
+
+ m_type = eTypeInvalid;
+ switch (reg_info->encoding) {
+ case eEncodingInvalid:
+ break;
+ case eEncodingUint:
+ case eEncodingSint:
+ if (reg_info->byte_size == 1)
+ SetUInt8(src.GetMaxU32(&src_offset, src_len));
+ else if (reg_info->byte_size <= 2)
+ SetUInt16(src.GetMaxU32(&src_offset, src_len));
+ else if (reg_info->byte_size <= 4)
+ SetUInt32(src.GetMaxU32(&src_offset, src_len));
+ else if (reg_info->byte_size <= 8)
+ SetUInt64(src.GetMaxU64(&src_offset, src_len));
+ else if (reg_info->byte_size <= 16) {
+ uint64_t data1 = src.GetU64(&src_offset);
+ uint64_t data2 = src.GetU64(&src_offset);
+ if (src.GetByteSize() == eByteOrderBig) {
+ int128.x[0] = data1;
+ int128.x[1] = data2;
+ } else {
+ int128.x[0] = data2;
+ int128.x[1] = data1;
+ }
+ SetUInt128(llvm::APInt(128, 2, int128.x));
+ }
+ break;
+ case eEncodingIEEE754:
+ if (reg_info->byte_size == sizeof(float))
+ SetFloat(src.GetFloat(&src_offset));
+ else if (reg_info->byte_size == sizeof(double))
+ SetDouble(src.GetDouble(&src_offset));
+ else if (reg_info->byte_size == sizeof(long double))
+ SetLongDouble(src.GetLongDouble(&src_offset));
+ break;
+ case eEncodingVector: {
+ m_type = eTypeBytes;
+ buffer.length = reg_info->byte_size;
+ buffer.byte_order = src.GetByteOrder();
+ assert(buffer.length <= kMaxRegisterByteSize);
+ if (buffer.length > kMaxRegisterByteSize)
+ buffer.length = kMaxRegisterByteSize;
+ if (src.CopyByteOrderedData(
+ src_offset, // offset within "src" to start extracting data
+ src_len, // src length
+ buffer.bytes, // dst buffer
+ buffer.length, // dst length
+ buffer.byte_order) == 0) // dst byte order
+ {
+ error.SetErrorStringWithFormat(
+ "failed to copy data for register write of %s", reg_info->name);
+ return error;
+ }
+ }
+ }
+
+ if (m_type == eTypeInvalid)
+ error.SetErrorStringWithFormat(
+ "invalid register value type for register %s", reg_info->name);
+ return error;
+}
+
+// Helper function for RegisterValue::SetValueFromString()
+static bool ParseVectorEncoding(const RegisterInfo *reg_info,
+ llvm::StringRef vector_str,
+ const uint32_t byte_size,
+ RegisterValue *reg_value) {
+ // Example: vector_str = "{0x2c 0x4b 0x2a 0x3e 0xd0 0x4f 0x2a 0x3e 0xac 0x4a
+ // 0x2a 0x3e 0x84 0x4f 0x2a 0x3e}".
+ vector_str = vector_str.trim();
+ vector_str.consume_front("{");
+ vector_str.consume_back("}");
+ vector_str = vector_str.trim();
+
+ char Sep = ' ';
+
+ // The first split should give us:
+ // ('0x2c', '0x4b 0x2a 0x3e 0xd0 0x4f 0x2a 0x3e 0xac 0x4a 0x2a 0x3e 0x84 0x4f
+ // 0x2a 0x3e').
+ llvm::StringRef car;
+ llvm::StringRef cdr = vector_str;
+ std::tie(car, cdr) = vector_str.split(Sep);
+ std::vector<uint8_t> bytes;
+ unsigned byte = 0;
+
+ // Using radix auto-sensing by passing 0 as the radix. Keep on processing the
+ // vector elements as long as the parsing succeeds and the vector size is <
+ // byte_size.
+ while (!car.getAsInteger(0, byte) && bytes.size() < byte_size) {
+ bytes.push_back(byte);
+ std::tie(car, cdr) = cdr.split(Sep);
+ }
+
+ // Check for vector of exact byte_size elements.
+ if (bytes.size() != byte_size)
+ return false;
+
+ reg_value->SetBytes(&(bytes.front()), byte_size, eByteOrderLittle);
+ return true;
+}
+
+Status RegisterValue::SetValueFromString(const RegisterInfo *reg_info,
+ llvm::StringRef value_str) {
+ Status error;
+ if (reg_info == nullptr) {
+ error.SetErrorString("Invalid register info argument.");
+ return error;
+ }
+
+ m_type = eTypeInvalid;
+ if (value_str.empty()) {
+ error.SetErrorString("Invalid c-string value string.");
+ return error;
+ }
+ const uint32_t byte_size = reg_info->byte_size;
+
+ uint64_t uval64;
+ int64_t ival64;
+ float flt_val;
+ double dbl_val;
+ long double ldbl_val;
+ switch (reg_info->encoding) {
+ case eEncodingInvalid:
+ error.SetErrorString("Invalid encoding.");
+ break;
+
+ case eEncodingUint:
+ if (byte_size > sizeof(uint64_t)) {
+ error.SetErrorStringWithFormat(
+ "unsupported unsigned integer byte size: %u", byte_size);
+ break;
+ }
+ if (value_str.getAsInteger(0, uval64)) {
+ error.SetErrorStringWithFormat(
+ "'%s' is not a valid unsigned integer string value",
+ value_str.str().c_str());
+ break;
+ }
+
+ if (!Args::UInt64ValueIsValidForByteSize(uval64, byte_size)) {
+ error.SetErrorStringWithFormat(
+ "value 0x%" PRIx64
+ " is too large to fit in a %u byte unsigned integer value",
+ uval64, byte_size);
+ break;
+ }
+
+ if (!SetUInt(uval64, reg_info->byte_size)) {
+ error.SetErrorStringWithFormat(
+ "unsupported unsigned integer byte size: %u", byte_size);
+ break;
+ }
+ break;
+
+ case eEncodingSint:
+ if (byte_size > sizeof(long long)) {
+ error.SetErrorStringWithFormat("unsupported signed integer byte size: %u",
+ byte_size);
+ break;
+ }
+
+ if (value_str.getAsInteger(0, ival64)) {
+ error.SetErrorStringWithFormat(
+ "'%s' is not a valid signed integer string value",
+ value_str.str().c_str());
+ break;
+ }
+
+ if (!Args::SInt64ValueIsValidForByteSize(ival64, byte_size)) {
+ error.SetErrorStringWithFormat(
+ "value 0x%" PRIx64
+ " is too large to fit in a %u byte signed integer value",
+ ival64, byte_size);
+ break;
+ }
+
+ if (!SetUInt(ival64, reg_info->byte_size)) {
+ error.SetErrorStringWithFormat("unsupported signed integer byte size: %u",
+ byte_size);
+ break;
+ }
+ break;
+
+ case eEncodingIEEE754: {
+ std::string value_string = value_str;
+ if (byte_size == sizeof(float)) {
+ if (::sscanf(value_string.c_str(), "%f", &flt_val) != 1) {
+ error.SetErrorStringWithFormat("'%s' is not a valid float string value",
+ value_string.c_str());
+ break;
+ }
+ m_scalar = flt_val;
+ m_type = eTypeFloat;
+ } else if (byte_size == sizeof(double)) {
+ if (::sscanf(value_string.c_str(), "%lf", &dbl_val) != 1) {
+ error.SetErrorStringWithFormat("'%s' is not a valid float string value",
+ value_string.c_str());
+ break;
+ }
+ m_scalar = dbl_val;
+ m_type = eTypeDouble;
+ } else if (byte_size == sizeof(long double)) {
+ if (::sscanf(value_string.c_str(), "%Lf", &ldbl_val) != 1) {
+ error.SetErrorStringWithFormat("'%s' is not a valid float string value",
+ value_string.c_str());
+ break;
+ }
+ m_scalar = ldbl_val;
+ m_type = eTypeLongDouble;
+ } else {
+ error.SetErrorStringWithFormat("unsupported float byte size: %u",
+ byte_size);
+ return error;
+ }
+ break;
+ }
+ case eEncodingVector:
+ if (!ParseVectorEncoding(reg_info, value_str, byte_size, this))
+ error.SetErrorString("unrecognized vector encoding string value.");
+ break;
+ }
+
+ return error;
+}
+
+bool RegisterValue::SignExtend(uint32_t sign_bitpos) {
+ switch (m_type) {
+ case eTypeInvalid:
+ break;
+
+ case eTypeUInt8:
+ case eTypeUInt16:
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ return m_scalar.SignExtend(sign_bitpos);
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ case eTypeBytes:
+ break;
+ }
+ return false;
+}
+
+bool RegisterValue::CopyValue(const RegisterValue &rhs) {
+ if (this == &rhs)
+ return rhs.m_type != eTypeInvalid;
+
+ m_type = rhs.m_type;
+ switch (m_type) {
+ case eTypeInvalid:
+ return false;
+ case eTypeUInt8:
+ case eTypeUInt16:
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ m_scalar = rhs.m_scalar;
+ break;
+ case eTypeBytes:
+ assert(rhs.buffer.length <= kMaxRegisterByteSize);
+ ::memcpy(buffer.bytes, rhs.buffer.bytes, kMaxRegisterByteSize);
+ buffer.length = rhs.buffer.length;
+ buffer.byte_order = rhs.buffer.byte_order;
+ break;
+ }
+ return true;
+}
+
+uint16_t RegisterValue::GetAsUInt16(uint16_t fail_value,
+ bool *success_ptr) const {
+ if (success_ptr)
+ *success_ptr = true;
+
+ switch (m_type) {
+ default:
+ break;
+ case eTypeUInt8:
+ case eTypeUInt16:
+ return m_scalar.UShort(fail_value);
+ case eTypeBytes: {
+ switch (buffer.length) {
+ default:
+ break;
+ case 1:
+ case 2:
+ return *reinterpret_cast<const uint16_t *>(buffer.bytes);
+ }
+ } break;
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+uint32_t RegisterValue::GetAsUInt32(uint32_t fail_value,
+ bool *success_ptr) const {
+ if (success_ptr)
+ *success_ptr = true;
+ switch (m_type) {
+ default:
+ break;
+ case eTypeUInt8:
+ case eTypeUInt16:
+ case eTypeUInt32:
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ return m_scalar.UInt(fail_value);
+ case eTypeBytes: {
+ switch (buffer.length) {
+ default:
+ break;
+ case 1:
+ case 2:
+ case 4:
+ return *reinterpret_cast<const uint32_t *>(buffer.bytes);
+ }
+ } break;
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+uint64_t RegisterValue::GetAsUInt64(uint64_t fail_value,
+ bool *success_ptr) const {
+ if (success_ptr)
+ *success_ptr = true;
+ switch (m_type) {
+ default:
+ break;
+ case eTypeUInt8:
+ case eTypeUInt16:
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ return m_scalar.ULongLong(fail_value);
+ case eTypeBytes: {
+ switch (buffer.length) {
+ default:
+ break;
+ case 1:
+ return *(const uint8_t *)buffer.bytes;
+ case 2:
+ return *reinterpret_cast<const uint16_t *>(buffer.bytes);
+ case 4:
+ return *reinterpret_cast<const uint32_t *>(buffer.bytes);
+ case 8:
+ return *reinterpret_cast<const uint64_t *>(buffer.bytes);
+ }
+ } break;
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+llvm::APInt RegisterValue::GetAsUInt128(const llvm::APInt &fail_value,
+ bool *success_ptr) const {
+ if (success_ptr)
+ *success_ptr = true;
+ switch (m_type) {
+ default:
+ break;
+ case eTypeUInt8:
+ case eTypeUInt16:
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ return m_scalar.UInt128(fail_value);
+ case eTypeBytes: {
+ switch (buffer.length) {
+ default:
+ break;
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ return llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128,
+ (reinterpret_cast<const type128 *>(buffer.bytes))->x);
+ }
+ } break;
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+float RegisterValue::GetAsFloat(float fail_value, bool *success_ptr) const {
+ if (success_ptr)
+ *success_ptr = true;
+ switch (m_type) {
+ default:
+ break;
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ return m_scalar.Float(fail_value);
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+double RegisterValue::GetAsDouble(double fail_value, bool *success_ptr) const {
+ if (success_ptr)
+ *success_ptr = true;
+ switch (m_type) {
+ default:
+ break;
+
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ return m_scalar.Double(fail_value);
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+long double RegisterValue::GetAsLongDouble(long double fail_value,
+ bool *success_ptr) const {
+ if (success_ptr)
+ *success_ptr = true;
+ switch (m_type) {
+ default:
+ break;
+
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ return m_scalar.LongDouble();
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+const void *RegisterValue::GetBytes() const {
+ switch (m_type) {
+ case eTypeInvalid:
+ break;
+ case eTypeUInt8:
+ case eTypeUInt16:
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ return m_scalar.GetBytes();
+ case eTypeBytes:
+ return buffer.bytes;
+ }
+ return nullptr;
+}
+
+uint32_t RegisterValue::GetByteSize() const {
+ switch (m_type) {
+ case eTypeInvalid:
+ break;
+ case eTypeUInt8:
+ return 1;
+ case eTypeUInt16:
+ return 2;
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ return m_scalar.GetByteSize();
+ case eTypeBytes:
+ return buffer.length;
+ }
+ return 0;
+}
+
+bool RegisterValue::SetUInt(uint64_t uint, uint32_t byte_size) {
+ if (byte_size == 0) {
+ SetUInt64(uint);
+ } else if (byte_size == 1) {
+ SetUInt8(uint);
+ } else if (byte_size <= 2) {
+ SetUInt16(uint);
+ } else if (byte_size <= 4) {
+ SetUInt32(uint);
+ } else if (byte_size <= 8) {
+ SetUInt64(uint);
+ } else if (byte_size <= 16) {
+ SetUInt128(llvm::APInt(128, uint));
+ } else
+ return false;
+ return true;
+}
+
+void RegisterValue::SetBytes(const void *bytes, size_t length,
+ lldb::ByteOrder byte_order) {
+ // If this assertion fires off we need to increase the size of buffer.bytes,
+ // or make it something that is allocated on the heap. Since the data buffer
+ // is in a union, we can't make it a collection class like SmallVector...
+ if (bytes && length > 0) {
+ assert(length <= sizeof(buffer.bytes) &&
+ "Storing too many bytes in a RegisterValue.");
+ m_type = eTypeBytes;
+ buffer.length = length;
+ memcpy(buffer.bytes, bytes, length);
+ buffer.byte_order = byte_order;
+ } else {
+ m_type = eTypeInvalid;
+ buffer.length = 0;
+ }
+}
+
+bool RegisterValue::operator==(const RegisterValue &rhs) const {
+ if (m_type == rhs.m_type) {
+ switch (m_type) {
+ case eTypeInvalid:
+ return true;
+ case eTypeUInt8:
+ case eTypeUInt16:
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ return m_scalar == rhs.m_scalar;
+ case eTypeBytes:
+ if (buffer.length != rhs.buffer.length)
+ return false;
+ else {
+ uint8_t length = buffer.length;
+ if (length > kMaxRegisterByteSize)
+ length = kMaxRegisterByteSize;
+ return memcmp(buffer.bytes, rhs.buffer.bytes, length) == 0;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+bool RegisterValue::operator!=(const RegisterValue &rhs) const {
+ return !(*this == rhs);
+}
+
+bool RegisterValue::ClearBit(uint32_t bit) {
+ switch (m_type) {
+ case eTypeInvalid:
+ break;
+
+ case eTypeUInt8:
+ case eTypeUInt16:
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ if (bit < (GetByteSize() * 8)) {
+ return m_scalar.ClearBit(bit);
+ }
+ break;
+
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ break;
+
+ case eTypeBytes:
+ if (buffer.byte_order == eByteOrderBig ||
+ buffer.byte_order == eByteOrderLittle) {
+ uint32_t byte_idx;
+ if (buffer.byte_order == eByteOrderBig)
+ byte_idx = buffer.length - (bit / 8) - 1;
+ else
+ byte_idx = bit / 8;
+
+ const uint32_t byte_bit = bit % 8;
+ if (byte_idx < buffer.length) {
+ buffer.bytes[byte_idx] &= ~(1u << byte_bit);
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+}
+
+bool RegisterValue::SetBit(uint32_t bit) {
+ switch (m_type) {
+ case eTypeInvalid:
+ break;
+
+ case eTypeUInt8:
+ case eTypeUInt16:
+ case eTypeUInt32:
+ case eTypeUInt64:
+ case eTypeUInt128:
+ if (bit < (GetByteSize() * 8)) {
+ return m_scalar.SetBit(bit);
+ }
+ break;
+
+ case eTypeFloat:
+ case eTypeDouble:
+ case eTypeLongDouble:
+ break;
+
+ case eTypeBytes:
+ if (buffer.byte_order == eByteOrderBig ||
+ buffer.byte_order == eByteOrderLittle) {
+ uint32_t byte_idx;
+ if (buffer.byte_order == eByteOrderBig)
+ byte_idx = buffer.length - (bit / 8) - 1;
+ else
+ byte_idx = bit / 8;
+
+ const uint32_t byte_bit = bit % 8;
+ if (byte_idx < buffer.length) {
+ buffer.bytes[byte_idx] |= (1u << byte_bit);
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/RegularExpression.cpp b/contrib/llvm-project/lldb/source/Utility/RegularExpression.cpp
new file mode 100644
index 000000000000..0192e8b8a01a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/RegularExpression.cpp
@@ -0,0 +1,175 @@
+//===-- RegularExpression.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/RegularExpression.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include <string>
+
+// Enable enhanced mode if it is available. This allows for things like \d for
+// digit, \s for space, and many more, but it isn't available everywhere.
+#if defined(REG_ENHANCED)
+#define DEFAULT_COMPILE_FLAGS (REG_ENHANCED | REG_EXTENDED)
+#else
+#define DEFAULT_COMPILE_FLAGS (REG_EXTENDED)
+#endif
+
+using namespace lldb_private;
+
+RegularExpression::RegularExpression() : m_re(), m_comp_err(1), m_preg() {
+ memset(&m_preg, 0, sizeof(m_preg));
+}
+
+// Constructor that compiles "re" using "flags" and stores the resulting
+// compiled regular expression into this object.
+RegularExpression::RegularExpression(llvm::StringRef str)
+ : RegularExpression() {
+ Compile(str);
+}
+
+RegularExpression::RegularExpression(const RegularExpression &rhs)
+ : RegularExpression() {
+ Compile(rhs.GetText());
+}
+
+const RegularExpression &RegularExpression::
+operator=(const RegularExpression &rhs) {
+ if (&rhs != this)
+ Compile(rhs.GetText());
+ return *this;
+}
+
+// Destructor
+//
+// Any previously compiled regular expression contained in this object will be
+// freed.
+RegularExpression::~RegularExpression() { Free(); }
+
+// Compile a regular expression using the supplied regular expression text and
+// flags. The compiled regular expression lives in this object so that it can
+// be readily used for regular expression matches. Execute() can be called
+// after the regular expression is compiled. Any previously compiled regular
+// expression contained in this object will be freed.
+//
+// RETURNS
+// True if the regular expression compiles successfully, false
+// otherwise.
+bool RegularExpression::Compile(llvm::StringRef str) {
+ Free();
+
+ // regcomp() on darwin does not recognize "" as a valid regular expression,
+ // so we substitute it with an equivalent non-empty one.
+ m_re = str.empty() ? "()" : str;
+ m_comp_err = ::regcomp(&m_preg, m_re.c_str(), DEFAULT_COMPILE_FLAGS);
+ return m_comp_err == 0;
+}
+
+// Execute a regular expression match using the compiled regular expression
+// that is already in this object against the match string "s". If any parens
+// are used for regular expression matches "match_count" should indicate the
+// number of regmatch_t values that are present in "match_ptr". The regular
+// expression will be executed using the "execute_flags".
+bool RegularExpression::Execute(llvm::StringRef str, Match *match) const {
+ int err = 1;
+ if (m_comp_err == 0) {
+ // Argument to regexec must be null-terminated.
+ std::string reg_str = str;
+ if (match) {
+ err = ::regexec(&m_preg, reg_str.c_str(), match->GetSize(),
+ match->GetData(), 0);
+ } else {
+ err = ::regexec(&m_preg, reg_str.c_str(), 0, nullptr, 0);
+ }
+ }
+
+ if (err != 0) {
+ // The regular expression didn't compile, so clear the matches
+ if (match)
+ match->Clear();
+ return false;
+ }
+ return true;
+}
+
+bool RegularExpression::Match::GetMatchAtIndex(llvm::StringRef s, uint32_t idx,
+ std::string &match_str) const {
+ llvm::StringRef match_str_ref;
+ if (GetMatchAtIndex(s, idx, match_str_ref)) {
+ match_str = match_str_ref.str();
+ return true;
+ }
+ return false;
+}
+
+bool RegularExpression::Match::GetMatchAtIndex(
+ llvm::StringRef s, uint32_t idx, llvm::StringRef &match_str) const {
+ if (idx < m_matches.size()) {
+ if (m_matches[idx].rm_eo == -1 && m_matches[idx].rm_so == -1)
+ return false;
+
+ if (m_matches[idx].rm_eo == m_matches[idx].rm_so) {
+ // Matched the empty string...
+ match_str = llvm::StringRef();
+ return true;
+ } else if (m_matches[idx].rm_eo > m_matches[idx].rm_so) {
+ match_str = s.substr(m_matches[idx].rm_so,
+ m_matches[idx].rm_eo - m_matches[idx].rm_so);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool RegularExpression::Match::GetMatchSpanningIndices(
+ llvm::StringRef s, uint32_t idx1, uint32_t idx2,
+ llvm::StringRef &match_str) const {
+ if (idx1 < m_matches.size() && idx2 < m_matches.size()) {
+ if (m_matches[idx1].rm_so == m_matches[idx2].rm_eo) {
+ // Matched the empty string...
+ match_str = llvm::StringRef();
+ return true;
+ } else if (m_matches[idx1].rm_so < m_matches[idx2].rm_eo) {
+ match_str = s.substr(m_matches[idx1].rm_so,
+ m_matches[idx2].rm_eo - m_matches[idx1].rm_so);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Returns true if the regular expression compiled and is ready for execution.
+bool RegularExpression::IsValid() const { return m_comp_err == 0; }
+
+// Returns the text that was used to compile the current regular expression.
+llvm::StringRef RegularExpression::GetText() const { return m_re; }
+
+// Free any contained compiled regular expressions.
+void RegularExpression::Free() {
+ if (m_comp_err == 0) {
+ m_re.clear();
+ regfree(&m_preg);
+ // Set a compile error since we no longer have a valid regex
+ m_comp_err = 1;
+ }
+}
+
+size_t RegularExpression::GetErrorAsCString(char *err_str,
+ size_t err_str_max_len) const {
+ if (m_comp_err == 0) {
+ if (err_str && err_str_max_len)
+ *err_str = '\0';
+ return 0;
+ }
+
+ return ::regerror(m_comp_err, &m_preg, err_str, err_str_max_len);
+}
+
+bool RegularExpression::operator<(const RegularExpression &rhs) const {
+ return (m_re < rhs.m_re);
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Reproducer.cpp b/contrib/llvm-project/lldb/source/Utility/Reproducer.cpp
new file mode 100644
index 000000000000..479ed311d1de
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Reproducer.cpp
@@ -0,0 +1,285 @@
+//===-- Reproducer.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Utility/LLDBAssert.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace lldb_private;
+using namespace lldb_private::repro;
+using namespace llvm;
+using namespace llvm::yaml;
+
+Reproducer &Reproducer::Instance() { return *InstanceImpl(); }
+
+llvm::Error Reproducer::Initialize(ReproducerMode mode,
+ llvm::Optional<FileSpec> root) {
+ lldbassert(!InstanceImpl() && "Already initialized.");
+ InstanceImpl().emplace();
+
+ switch (mode) {
+ case ReproducerMode::Capture: {
+ if (!root) {
+ SmallString<128> repro_dir;
+ auto ec = sys::fs::createUniqueDirectory("reproducer", repro_dir);
+ if (ec)
+ return make_error<StringError>(
+ "unable to create unique reproducer directory", ec);
+ root.emplace(repro_dir);
+ } else {
+ auto ec = sys::fs::create_directory(root->GetPath());
+ if (ec)
+ return make_error<StringError>("unable to create reproducer directory",
+ ec);
+ }
+ return Instance().SetCapture(root);
+ } break;
+ case ReproducerMode::Replay:
+ return Instance().SetReplay(root);
+ case ReproducerMode::Off:
+ break;
+ };
+
+ return Error::success();
+}
+
+bool Reproducer::Initialized() { return InstanceImpl().operator bool(); }
+
+void Reproducer::Terminate() {
+ lldbassert(InstanceImpl() && "Already terminated.");
+ InstanceImpl().reset();
+}
+
+Optional<Reproducer> &Reproducer::InstanceImpl() {
+ static Optional<Reproducer> g_reproducer;
+ return g_reproducer;
+}
+
+const Generator *Reproducer::GetGenerator() const {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ if (m_generator)
+ return &(*m_generator);
+ return nullptr;
+}
+
+const Loader *Reproducer::GetLoader() const {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ if (m_loader)
+ return &(*m_loader);
+ return nullptr;
+}
+
+Generator *Reproducer::GetGenerator() {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ if (m_generator)
+ return &(*m_generator);
+ return nullptr;
+}
+
+Loader *Reproducer::GetLoader() {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ if (m_loader)
+ return &(*m_loader);
+ return nullptr;
+}
+
+llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+
+ if (root && m_loader)
+ return make_error<StringError>(
+ "cannot generate a reproducer when replay one",
+ inconvertibleErrorCode());
+
+ if (!root) {
+ m_generator.reset();
+ return Error::success();
+ }
+
+ m_generator.emplace(*root);
+ return Error::success();
+}
+
+llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+
+ if (root && m_generator)
+ return make_error<StringError>(
+ "cannot replay a reproducer when generating one",
+ inconvertibleErrorCode());
+
+ if (!root) {
+ m_loader.reset();
+ return Error::success();
+ }
+
+ m_loader.emplace(*root);
+ if (auto e = m_loader->LoadIndex())
+ return e;
+
+ return Error::success();
+}
+
+FileSpec Reproducer::GetReproducerPath() const {
+ if (auto g = GetGenerator())
+ return g->GetRoot();
+ if (auto l = GetLoader())
+ return l->GetRoot();
+ return {};
+}
+
+Generator::Generator(const FileSpec &root) : m_root(root), m_done(false) {}
+
+Generator::~Generator() {}
+
+ProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) {
+ std::lock_guard<std::mutex> lock(m_providers_mutex);
+ std::pair<const void *, std::unique_ptr<ProviderBase>> key_value(
+ provider->DynamicClassID(), std::move(provider));
+ auto e = m_providers.insert(std::move(key_value));
+ return e.first->getSecond().get();
+}
+
+void Generator::Keep() {
+ assert(!m_done);
+ m_done = true;
+
+ for (auto &provider : m_providers)
+ provider.second->Keep();
+
+ AddProvidersToIndex();
+}
+
+void Generator::Discard() {
+ assert(!m_done);
+ m_done = true;
+
+ for (auto &provider : m_providers)
+ provider.second->Discard();
+
+ llvm::sys::fs::remove_directories(m_root.GetPath());
+}
+
+const FileSpec &Generator::GetRoot() const { return m_root; }
+
+void Generator::AddProvidersToIndex() {
+ FileSpec index = m_root;
+ index.AppendPathComponent("index.yaml");
+
+ std::error_code EC;
+ auto strm = llvm::make_unique<raw_fd_ostream>(index.GetPath(), EC,
+ sys::fs::OpenFlags::F_None);
+ yaml::Output yout(*strm);
+
+ std::vector<std::string> files;
+ files.reserve(m_providers.size());
+ for (auto &provider : m_providers) {
+ files.emplace_back(provider.second->GetFile());
+ }
+
+ yout << files;
+}
+
+Loader::Loader(const FileSpec &root) : m_root(root), m_loaded(false) {}
+
+llvm::Error Loader::LoadIndex() {
+ if (m_loaded)
+ return llvm::Error::success();
+
+ FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml");
+
+ auto error_or_file = MemoryBuffer::getFile(index.GetPath());
+ if (auto err = error_or_file.getError())
+ return make_error<StringError>("unable to load reproducer index", err);
+
+ yaml::Input yin((*error_or_file)->getBuffer());
+ yin >> m_files;
+ if (auto err = yin.error())
+ return make_error<StringError>("unable to read reproducer index", err);
+
+ // Sort files to speed up search.
+ llvm::sort(m_files);
+
+ // Remember that we've loaded the index.
+ m_loaded = true;
+
+ return llvm::Error::success();
+}
+
+bool Loader::HasFile(StringRef file) {
+ assert(m_loaded);
+ auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str());
+ return (it != m_files.end()) && (*it == file);
+}
+
+llvm::Expected<std::unique_ptr<DataRecorder>>
+DataRecorder::Create(const FileSpec &filename) {
+ std::error_code ec;
+ auto recorder = llvm::make_unique<DataRecorder>(std::move(filename), ec);
+ if (ec)
+ return llvm::errorCodeToError(ec);
+ return std::move(recorder);
+}
+
+DataRecorder *CommandProvider::GetNewDataRecorder() {
+ std::size_t i = m_data_recorders.size() + 1;
+ std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") +
+ llvm::Twine(i) + llvm::Twine(".txt"))
+ .str();
+ auto recorder_or_error =
+ DataRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename));
+ if (!recorder_or_error) {
+ llvm::consumeError(recorder_or_error.takeError());
+ return nullptr;
+ }
+
+ m_data_recorders.push_back(std::move(*recorder_or_error));
+ return m_data_recorders.back().get();
+}
+
+void CommandProvider::Keep() {
+ std::vector<std::string> files;
+ for (auto &recorder : m_data_recorders) {
+ recorder->Stop();
+ files.push_back(recorder->GetFilename().GetPath());
+ }
+
+ FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
+ std::error_code ec;
+ llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::F_Text);
+ if (ec)
+ return;
+ yaml::Output yout(os);
+ yout << files;
+}
+
+void CommandProvider::Discard() { m_data_recorders.clear(); }
+
+void VersionProvider::Keep() {
+ FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
+ std::error_code ec;
+ llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::F_Text);
+ if (ec)
+ return;
+ os << m_version << "\n";
+}
+
+void ProviderBase::anchor() {}
+char ProviderBase::ID = 0;
+char CommandProvider::ID = 0;
+char FileProvider::ID = 0;
+char VersionProvider::ID = 0;
+const char *CommandProvider::Info::file = "command-interpreter.yaml";
+const char *CommandProvider::Info::name = "command-interpreter";
+const char *FileProvider::Info::file = "files.yaml";
+const char *FileProvider::Info::name = "files";
+const char *VersionProvider::Info::file = "version.txt";
+const char *VersionProvider::Info::name = "version";
diff --git a/contrib/llvm-project/lldb/source/Utility/ReproducerInstrumentation.cpp b/contrib/llvm-project/lldb/source/Utility/ReproducerInstrumentation.cpp
new file mode 100644
index 000000000000..473786ef4d3e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/ReproducerInstrumentation.cpp
@@ -0,0 +1,122 @@
+//===-- ReproducerInstrumentation.cpp ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/ReproducerInstrumentation.h"
+#include "lldb/Utility/Reproducer.h"
+
+using namespace lldb_private;
+using namespace lldb_private::repro;
+
+void *IndexToObject::GetObjectForIndexImpl(unsigned idx) {
+ return m_mapping.lookup(idx);
+}
+
+void IndexToObject::AddObjectForIndexImpl(unsigned idx, void *object) {
+ assert(idx != 0 && "Cannot add object for sentinel");
+ m_mapping[idx] = object;
+}
+
+template <> char *Deserializer::Deserialize<char *>() {
+ return const_cast<char *>(Deserialize<const char *>());
+}
+
+template <> const char *Deserializer::Deserialize<const char *>() {
+ auto pos = m_buffer.find('\0');
+ if (pos == llvm::StringRef::npos)
+ return nullptr;
+ const char *str = m_buffer.data();
+ m_buffer = m_buffer.drop_front(pos + 1);
+ return str;
+}
+
+bool Registry::Replay(const FileSpec &file) {
+ auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
+ if (auto err = error_or_file.getError())
+ return false;
+
+ return Replay((*error_or_file)->getBuffer());
+}
+
+bool Registry::Replay(llvm::StringRef buffer) {
+#ifndef LLDB_REPRO_INSTR_TRACE
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API);
+#endif
+
+ Deserializer deserializer(buffer);
+ while (deserializer.HasData(1)) {
+ unsigned id = deserializer.Deserialize<unsigned>();
+
+#ifndef LLDB_REPRO_INSTR_TRACE
+ LLDB_LOG(log, "Replaying {0}: {1}", id, GetSignature(id));
+#else
+ llvm::errs() << "Replaying " << id << ": " << GetSignature(id) << "\n";
+#endif
+
+ GetReplayer(id)->operator()(deserializer);
+ }
+
+ return true;
+}
+
+void Registry::DoRegister(uintptr_t RunID, std::unique_ptr<Replayer> replayer,
+ SignatureStr signature) {
+ const unsigned id = m_replayers.size() + 1;
+ assert(m_replayers.find(RunID) == m_replayers.end());
+ m_replayers[RunID] = std::make_pair(std::move(replayer), id);
+ m_ids[id] =
+ std::make_pair(m_replayers[RunID].first.get(), std::move(signature));
+}
+
+unsigned Registry::GetID(uintptr_t addr) {
+ unsigned id = m_replayers[addr].second;
+ assert(id != 0 && "Forgot to add function to registry?");
+ return id;
+}
+
+std::string Registry::GetSignature(unsigned id) {
+ assert(m_ids.count(id) != 0 && "ID not in registry");
+ return m_ids[id].second.ToString();
+}
+
+Replayer *Registry::GetReplayer(unsigned id) {
+ assert(m_ids.count(id) != 0 && "ID not in registry");
+ return m_ids[id].first;
+}
+
+std::string Registry::SignatureStr::ToString() const {
+ return (result + (result.empty() ? "" : " ") + scope + "::" + name + args)
+ .str();
+}
+
+unsigned ObjectToIndex::GetIndexForObjectImpl(const void *object) {
+ unsigned index = m_mapping.size() + 1;
+ auto it = m_mapping.find(object);
+ if (it == m_mapping.end())
+ m_mapping[object] = index;
+ return m_mapping[object];
+}
+
+Recorder::Recorder(llvm::StringRef pretty_func, std::string &&pretty_args)
+ : m_serializer(nullptr), m_pretty_func(pretty_func),
+ m_pretty_args(pretty_args), m_local_boundary(false),
+ m_result_recorded(true) {
+ if (!g_global_boundary) {
+ g_global_boundary = true;
+ m_local_boundary = true;
+
+ LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "{0} ({1})",
+ m_pretty_func, m_pretty_args);
+ }
+}
+
+Recorder::~Recorder() {
+ assert(m_result_recorded && "Did you forget LLDB_RECORD_RESULT?");
+ UpdateBoundary();
+}
+
+bool lldb_private::repro::Recorder::g_global_boundary;
diff --git a/contrib/llvm-project/lldb/source/Utility/Scalar.cpp b/contrib/llvm-project/lldb/source/Utility/Scalar.cpp
new file mode 100644
index 000000000000..23d50b9eaba5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Scalar.cpp
@@ -0,0 +1,2863 @@
+//===-- Scalar.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Scalar.h"
+
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-types.h"
+
+#include "llvm/ADT/SmallString.h"
+
+#include <cinttypes>
+#include <cstdio>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Promote to max type currently follows the ANSI C rule for type promotion in
+// expressions.
+static Scalar::Type PromoteToMaxType(
+ const Scalar &lhs, // The const left hand side object
+ const Scalar &rhs, // The const right hand side object
+ Scalar &temp_value, // A modifiable temp value than can be used to hold
+ // either the promoted lhs or rhs object
+ const Scalar *&promoted_lhs_ptr, // Pointer to the resulting possibly
+ // promoted value of lhs (at most one of
+ // lhs/rhs will get promoted)
+ const Scalar *&promoted_rhs_ptr // Pointer to the resulting possibly
+ // promoted value of rhs (at most one of
+ // lhs/rhs will get promoted)
+) {
+ Scalar result;
+ // Initialize the promoted values for both the right and left hand side
+ // values to be the objects themselves. If no promotion is needed (both right
+ // and left have the same type), then the temp_value will not get used.
+ promoted_lhs_ptr = &lhs;
+ promoted_rhs_ptr = &rhs;
+ // Extract the types of both the right and left hand side values
+ Scalar::Type lhs_type = lhs.GetType();
+ Scalar::Type rhs_type = rhs.GetType();
+
+ if (lhs_type > rhs_type) {
+ // Right hand side need to be promoted
+ temp_value = rhs; // Copy right hand side into the temp value
+ if (temp_value.Promote(lhs_type)) // Promote it
+ promoted_rhs_ptr =
+ &temp_value; // Update the pointer for the promoted right hand side
+ } else if (lhs_type < rhs_type) {
+ // Left hand side need to be promoted
+ temp_value = lhs; // Copy left hand side value into the temp value
+ if (temp_value.Promote(rhs_type)) // Promote it
+ promoted_lhs_ptr =
+ &temp_value; // Update the pointer for the promoted left hand side
+ }
+
+ // Make sure our type promotion worked as expected
+ if (promoted_lhs_ptr->GetType() == promoted_rhs_ptr->GetType())
+ return promoted_lhs_ptr->GetType(); // Return the resulting max type
+
+ // Return the void type (zero) if we fail to promote either of the values.
+ return Scalar::e_void;
+}
+
+Scalar::Scalar() : m_type(e_void), m_float(static_cast<float>(0)) {}
+
+bool Scalar::GetData(DataExtractor &data, size_t limit_byte_size) const {
+ size_t byte_size = GetByteSize();
+ if (byte_size > 0) {
+ const uint8_t *bytes = reinterpret_cast<const uint8_t *>(GetBytes());
+
+ if (limit_byte_size < byte_size) {
+ if (endian::InlHostByteOrder() == eByteOrderLittle) {
+ // On little endian systems if we want fewer bytes from the current
+ // type we just specify fewer bytes since the LSByte is first...
+ byte_size = limit_byte_size;
+ } else if (endian::InlHostByteOrder() == eByteOrderBig) {
+ // On big endian systems if we want fewer bytes from the current type
+ // have to advance our initial byte pointer and trim down the number of
+ // bytes since the MSByte is first
+ bytes += byte_size - limit_byte_size;
+ byte_size = limit_byte_size;
+ }
+ }
+
+ data.SetData(bytes, byte_size, endian::InlHostByteOrder());
+ return true;
+ }
+ data.Clear();
+ return false;
+}
+
+const void *Scalar::GetBytes() const {
+ const uint64_t *apint_words;
+ const uint8_t *bytes;
+ static float_t flt_val;
+ static double_t dbl_val;
+ static uint64_t swapped_words[8];
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ bytes = reinterpret_cast<const uint8_t *>(m_integer.getRawData());
+ // getRawData always returns a pointer to an uint64_t. If we have a
+ // smaller type, we need to update the pointer on big-endian systems.
+ if (endian::InlHostByteOrder() == eByteOrderBig) {
+ size_t byte_size = m_integer.getBitWidth() / 8;
+ if (byte_size < 8)
+ bytes += 8 - byte_size;
+ }
+ return bytes;
+ // getRawData always returns a pointer to an array of uint64_t values,
+ // where the least-significant word always comes first. On big-endian
+ // systems we need to swap the words.
+ case e_sint128:
+ case e_uint128:
+ apint_words = m_integer.getRawData();
+ if (endian::InlHostByteOrder() == eByteOrderBig) {
+ swapped_words[0] = apint_words[1];
+ swapped_words[1] = apint_words[0];
+ apint_words = swapped_words;
+ }
+ return reinterpret_cast<const void *>(apint_words);
+ case e_sint256:
+ case e_uint256:
+ apint_words = m_integer.getRawData();
+ if (endian::InlHostByteOrder() == eByteOrderBig) {
+ swapped_words[0] = apint_words[3];
+ swapped_words[1] = apint_words[2];
+ swapped_words[2] = apint_words[1];
+ swapped_words[3] = apint_words[0];
+ apint_words = swapped_words;
+ }
+ return reinterpret_cast<const void *>(apint_words);
+ case e_sint512:
+ case e_uint512:
+ apint_words = m_integer.getRawData();
+ if (endian::InlHostByteOrder() == eByteOrderBig) {
+ swapped_words[0] = apint_words[7];
+ swapped_words[1] = apint_words[6];
+ swapped_words[2] = apint_words[5];
+ swapped_words[3] = apint_words[4];
+ swapped_words[4] = apint_words[3];
+ swapped_words[5] = apint_words[2];
+ swapped_words[6] = apint_words[1];
+ swapped_words[7] = apint_words[0];
+ apint_words = swapped_words;
+ }
+ return reinterpret_cast<const void *>(apint_words);
+ case e_float:
+ flt_val = m_float.convertToFloat();
+ return reinterpret_cast<const void *>(&flt_val);
+ case e_double:
+ dbl_val = m_float.convertToDouble();
+ return reinterpret_cast<const void *>(&dbl_val);
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ apint_words = ldbl_val.getRawData();
+ // getRawData always returns a pointer to an array of two uint64_t values,
+ // where the least-significant word always comes first. On big-endian
+ // systems we need to swap the two words.
+ if (endian::InlHostByteOrder() == eByteOrderBig) {
+ swapped_words[0] = apint_words[1];
+ swapped_words[1] = apint_words[0];
+ apint_words = swapped_words;
+ }
+ return reinterpret_cast<const void *>(apint_words);
+ }
+ return nullptr;
+}
+
+size_t Scalar::GetByteSize() const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return (m_integer.getBitWidth() / 8);
+ case e_float:
+ return sizeof(float_t);
+ case e_double:
+ return sizeof(double_t);
+ case e_long_double:
+ return sizeof(long_double_t);
+ }
+ return 0;
+}
+
+bool Scalar::IsZero() const {
+ llvm::APInt zero_int = llvm::APInt::getNullValue(m_integer.getBitWidth() / 8);
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_uint512:
+ case e_sint512:
+ return llvm::APInt::isSameValue(zero_int, m_integer);
+ case e_float:
+ case e_double:
+ case e_long_double:
+ return m_float.isZero();
+ }
+ return false;
+}
+
+void Scalar::GetValue(Stream *s, bool show_type) const {
+ if (show_type)
+ s->Printf("(%s) ", GetTypeAsCString());
+
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_slong:
+ case e_slonglong:
+ case e_sint128:
+ case e_sint256:
+ case e_sint512:
+ s->PutCString(m_integer.toString(10, true));
+ break;
+ case e_uint:
+ case e_ulong:
+ case e_ulonglong:
+ case e_uint128:
+ case e_uint256:
+ case e_uint512:
+ s->PutCString(m_integer.toString(10, false));
+ break;
+ case e_float:
+ case e_double:
+ case e_long_double:
+ llvm::SmallString<24> string;
+ m_float.toString(string);
+ s->Printf("%s", string.c_str());
+ break;
+ }
+}
+
+const char *Scalar::GetTypeAsCString() const {
+ switch (m_type) {
+ case e_void:
+ return "void";
+ case e_sint:
+ return "int";
+ case e_uint:
+ return "unsigned int";
+ case e_slong:
+ return "long";
+ case e_ulong:
+ return "unsigned long";
+ case e_slonglong:
+ return "long long";
+ case e_ulonglong:
+ return "unsigned long long";
+ case e_sint128:
+ return "int128_t";
+ case e_uint128:
+ return "unsigned int128_t";
+ case e_sint256:
+ return "int256_t";
+ case e_uint256:
+ return "unsigned int256_t";
+ case e_sint512:
+ return "int512_t";
+ case e_uint512:
+ return "unsigned int512_t";
+ case e_float:
+ return "float";
+ case e_double:
+ return "double";
+ case e_long_double:
+ return "long double";
+ }
+ return "<invalid Scalar type>";
+}
+
+Scalar &Scalar::operator=(const Scalar &rhs) {
+ if (this != &rhs) {
+ m_type = rhs.m_type;
+ m_integer = llvm::APInt(rhs.m_integer);
+ m_float = rhs.m_float;
+ }
+ return *this;
+}
+
+Scalar &Scalar::operator=(const int v) {
+ m_type = e_sint;
+ m_integer = llvm::APInt(sizeof(int) * 8, v, true);
+ return *this;
+}
+
+Scalar &Scalar::operator=(unsigned int v) {
+ m_type = e_uint;
+ m_integer = llvm::APInt(sizeof(int) * 8, v);
+ return *this;
+}
+
+Scalar &Scalar::operator=(long v) {
+ m_type = e_slong;
+ m_integer = llvm::APInt(sizeof(long) * 8, v, true);
+ return *this;
+}
+
+Scalar &Scalar::operator=(unsigned long v) {
+ m_type = e_ulong;
+ m_integer = llvm::APInt(sizeof(long) * 8, v);
+ return *this;
+}
+
+Scalar &Scalar::operator=(long long v) {
+ m_type = e_slonglong;
+ m_integer = llvm::APInt(sizeof(long) * 8, v, true);
+ return *this;
+}
+
+Scalar &Scalar::operator=(unsigned long long v) {
+ m_type = e_ulonglong;
+ m_integer = llvm::APInt(sizeof(long long) * 8, v);
+ return *this;
+}
+
+Scalar &Scalar::operator=(float v) {
+ m_type = e_float;
+ m_float = llvm::APFloat(v);
+ return *this;
+}
+
+Scalar &Scalar::operator=(double v) {
+ m_type = e_double;
+ m_float = llvm::APFloat(v);
+ return *this;
+}
+
+Scalar &Scalar::operator=(long double v) {
+ m_type = e_long_double;
+ if (m_ieee_quad)
+ m_float = llvm::APFloat(llvm::APFloat::IEEEquad(),
+ llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128,
+ (reinterpret_cast<type128 *>(&v))->x));
+ else
+ m_float = llvm::APFloat(llvm::APFloat::x87DoubleExtended(),
+ llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128,
+ (reinterpret_cast<type128 *>(&v))->x));
+ return *this;
+}
+
+Scalar &Scalar::operator=(llvm::APInt rhs) {
+ m_integer = llvm::APInt(rhs);
+ switch (m_integer.getBitWidth()) {
+ case 8:
+ case 16:
+ case 32:
+ if (m_integer.isSignedIntN(sizeof(sint_t) * 8))
+ m_type = e_sint;
+ else
+ m_type = e_uint;
+ break;
+ case 64:
+ if (m_integer.isSignedIntN(sizeof(slonglong_t) * 8))
+ m_type = e_slonglong;
+ else
+ m_type = e_ulonglong;
+ break;
+ case 128:
+ if (m_integer.isSignedIntN(BITWIDTH_INT128))
+ m_type = e_sint128;
+ else
+ m_type = e_uint128;
+ break;
+ case 256:
+ if (m_integer.isSignedIntN(BITWIDTH_INT256))
+ m_type = e_sint256;
+ else
+ m_type = e_uint256;
+ break;
+ case 512:
+ if (m_integer.isSignedIntN(BITWIDTH_INT512))
+ m_type = e_sint512;
+ else
+ m_type = e_uint512;
+ break;
+ }
+ return *this;
+}
+
+Scalar::~Scalar() = default;
+
+bool Scalar::Promote(Scalar::Type type) {
+ bool success = false;
+ switch (m_type) {
+ case e_void:
+ break;
+
+ case e_sint:
+ switch (type) {
+ case e_void:
+ break;
+ case e_sint:
+ success = true;
+ break;
+ case e_uint:
+ m_integer = m_integer.sextOrTrunc(sizeof(uint_t) * 8);
+ success = true;
+ break;
+
+ case e_slong:
+ m_integer = m_integer.sextOrTrunc(sizeof(slong_t) * 8);
+ success = true;
+ break;
+
+ case e_ulong:
+ m_integer = m_integer.sextOrTrunc(sizeof(ulong_t) * 8);
+ success = true;
+ break;
+
+ case e_slonglong:
+ m_integer = m_integer.sextOrTrunc(sizeof(slonglong_t) * 8);
+ success = true;
+ break;
+
+ case e_ulonglong:
+ m_integer = m_integer.sextOrTrunc(sizeof(ulonglong_t) * 8);
+ success = true;
+ break;
+
+ case e_sint128:
+ case e_uint128:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT128);
+ success = true;
+ break;
+
+ case e_sint256:
+ case e_uint256:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256);
+ success = true;
+ break;
+
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT512);
+ success = true;
+ break;
+
+ case e_float:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEsingle());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_double:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEdouble());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_long_double:
+ m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+ }
+ break;
+
+ case e_uint:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ break;
+ case e_uint:
+ success = true;
+ break;
+ case e_slong:
+ m_integer = m_integer.zextOrTrunc(sizeof(slong_t) * 8);
+ success = true;
+ break;
+
+ case e_ulong:
+ m_integer = m_integer.zextOrTrunc(sizeof(ulong_t) * 8);
+ success = true;
+ break;
+
+ case e_slonglong:
+ m_integer = m_integer.zextOrTrunc(sizeof(slonglong_t) * 8);
+ success = true;
+ break;
+
+ case e_ulonglong:
+ m_integer = m_integer.zextOrTrunc(sizeof(ulonglong_t) * 8);
+ success = true;
+ break;
+
+ case e_sint128:
+ case e_uint128:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT128);
+ success = true;
+ break;
+
+ case e_sint256:
+ case e_uint256:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT256);
+ success = true;
+ break;
+
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512);
+ success = true;
+ break;
+
+ case e_float:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEsingle());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_double:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEdouble());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_long_double:
+ m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+ }
+ break;
+
+ case e_slong:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ case e_uint:
+ break;
+ case e_slong:
+ success = true;
+ break;
+ case e_ulong:
+ m_integer = m_integer.sextOrTrunc(sizeof(ulong_t) * 8);
+ success = true;
+ break;
+
+ case e_slonglong:
+ m_integer = m_integer.sextOrTrunc(sizeof(slonglong_t) * 8);
+ success = true;
+ break;
+
+ case e_ulonglong:
+ m_integer = m_integer.sextOrTrunc(sizeof(ulonglong_t) * 8);
+ success = true;
+ break;
+
+ case e_sint128:
+ case e_uint128:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT128);
+ success = true;
+ break;
+
+ case e_sint256:
+ case e_uint256:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256);
+ success = true;
+ break;
+
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT512);
+ success = true;
+ break;
+
+ case e_float:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEsingle());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_double:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEdouble());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_long_double:
+ m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+ }
+ break;
+
+ case e_ulong:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ break;
+ case e_ulong:
+ success = true;
+ break;
+ case e_slonglong:
+ m_integer = m_integer.zextOrTrunc(sizeof(slonglong_t) * 8);
+ success = true;
+ break;
+
+ case e_ulonglong:
+ m_integer = m_integer.zextOrTrunc(sizeof(ulonglong_t) * 8);
+ success = true;
+ break;
+
+ case e_sint128:
+ case e_uint128:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT128);
+ success = true;
+ break;
+
+ case e_sint256:
+ case e_uint256:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT256);
+ success = true;
+ break;
+
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512);
+ success = true;
+ break;
+
+ case e_float:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEsingle());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_double:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEdouble());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_long_double:
+ m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+ }
+ break;
+
+ case e_slonglong:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ break;
+ case e_slonglong:
+ success = true;
+ break;
+ case e_ulonglong:
+ m_integer = m_integer.sextOrTrunc(sizeof(ulonglong_t) * 8);
+ success = true;
+ break;
+
+ case e_sint128:
+ case e_uint128:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT128);
+ success = true;
+ break;
+
+ case e_sint256:
+ case e_uint256:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256);
+ success = true;
+ break;
+
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT512);
+ success = true;
+ break;
+
+ case e_float:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEsingle());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_double:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEdouble());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_long_double:
+ m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+ }
+ break;
+
+ case e_ulonglong:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ break;
+ case e_ulonglong:
+ success = true;
+ break;
+ case e_sint128:
+ case e_uint128:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT128);
+ success = true;
+ break;
+
+ case e_sint256:
+ case e_uint256:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT256);
+ success = true;
+ break;
+
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512);
+ success = true;
+ break;
+
+ case e_float:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEsingle());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_double:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEdouble());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_long_double:
+ m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+ }
+ break;
+
+ case e_sint128:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ break;
+ case e_sint128:
+ success = true;
+ break;
+ case e_uint128:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT128);
+ success = true;
+ break;
+
+ case e_sint256:
+ case e_uint256:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256);
+ success = true;
+ break;
+
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT512);
+ success = true;
+ break;
+
+ case e_float:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEsingle());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_double:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEdouble());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_long_double:
+ m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+ }
+ break;
+
+ case e_uint128:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ break;
+ case e_uint128:
+ success = true;
+ break;
+ case e_sint256:
+ case e_uint256:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT256);
+ success = true;
+ break;
+
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512);
+ success = true;
+ break;
+
+ case e_float:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEsingle());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_double:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEdouble());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_long_double:
+ m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+ }
+ break;
+
+ case e_sint256:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ break;
+ case e_sint256:
+ success = true;
+ break;
+ case e_uint256:
+ m_integer = m_integer.sextOrTrunc(BITWIDTH_INT256);
+ success = true;
+ break;
+
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512);
+ success = true;
+ break;
+
+ case e_float:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEsingle());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_double:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEdouble());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_long_double:
+ m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended());
+ m_float.convertFromAPInt(m_integer, true,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+ }
+ break;
+
+ case e_uint256:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ break;
+ case e_uint256:
+ success = true;
+ break;
+
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.zextOrTrunc(BITWIDTH_INT512);
+ success = true;
+ break;
+
+ case e_float:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEsingle());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_double:
+ m_float = llvm::APFloat(llvm::APFloat::IEEEdouble());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+
+ case e_long_double:
+ m_float = llvm::APFloat(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended());
+ m_float.convertFromAPInt(m_integer, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ success = true;
+ break;
+ }
+ break;
+
+ case e_sint512:
+ case e_uint512:
+ lldbassert(false && "unimplemented");
+ break;
+
+ case e_float:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_uint512:
+ case e_sint512:
+ break;
+ case e_float:
+ success = true;
+ break;
+ case e_double:
+ m_float = llvm::APFloat(static_cast<double_t>(m_float.convertToFloat()));
+ success = true;
+ break;
+
+ case e_long_double: {
+ bool ignore;
+ m_float.convert(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended(),
+ llvm::APFloat::rmNearestTiesToEven, &ignore);
+ success = true;
+ break;
+ }
+ }
+ break;
+
+ case e_double:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ case e_float:
+ break;
+ case e_double:
+ success = true;
+ break;
+ case e_long_double: {
+ bool ignore;
+ m_float.convert(m_ieee_quad ? llvm::APFloat::IEEEquad()
+ : llvm::APFloat::x87DoubleExtended(),
+ llvm::APFloat::rmNearestTiesToEven, &ignore);
+ success = true;
+ break;
+ }
+ }
+ break;
+
+ case e_long_double:
+ switch (type) {
+ case e_void:
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ case e_float:
+ case e_double:
+ break;
+ case e_long_double:
+ success = true;
+ break;
+ }
+ break;
+ }
+
+ if (success)
+ m_type = type;
+ return success;
+}
+
+const char *Scalar::GetValueTypeAsCString(Scalar::Type type) {
+ switch (type) {
+ case e_void:
+ return "void";
+ case e_sint:
+ return "int";
+ case e_uint:
+ return "unsigned int";
+ case e_slong:
+ return "long";
+ case e_ulong:
+ return "unsigned long";
+ case e_slonglong:
+ return "long long";
+ case e_ulonglong:
+ return "unsigned long long";
+ case e_float:
+ return "float";
+ case e_double:
+ return "double";
+ case e_long_double:
+ return "long double";
+ case e_sint128:
+ return "int128_t";
+ case e_uint128:
+ return "uint128_t";
+ case e_sint256:
+ return "int256_t";
+ case e_uint256:
+ return "uint256_t";
+ case e_sint512:
+ return "int512_t";
+ case e_uint512:
+ return "uint512_t";
+ }
+ return "???";
+}
+
+Scalar::Type
+Scalar::GetValueTypeForSignedIntegerWithByteSize(size_t byte_size) {
+ if (byte_size <= sizeof(sint_t))
+ return e_sint;
+ if (byte_size <= sizeof(slong_t))
+ return e_slong;
+ if (byte_size <= sizeof(slonglong_t))
+ return e_slonglong;
+ return e_void;
+}
+
+Scalar::Type
+Scalar::GetValueTypeForUnsignedIntegerWithByteSize(size_t byte_size) {
+ if (byte_size <= sizeof(uint_t))
+ return e_uint;
+ if (byte_size <= sizeof(ulong_t))
+ return e_ulong;
+ if (byte_size <= sizeof(ulonglong_t))
+ return e_ulonglong;
+ return e_void;
+}
+
+Scalar::Type Scalar::GetValueTypeForFloatWithByteSize(size_t byte_size) {
+ if (byte_size == sizeof(float_t))
+ return e_float;
+ if (byte_size == sizeof(double_t))
+ return e_double;
+ if (byte_size == sizeof(long_double_t))
+ return e_long_double;
+ return e_void;
+}
+
+bool Scalar::MakeSigned() {
+ bool success = false;
+
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ success = true;
+ break;
+ case e_uint:
+ m_type = e_sint;
+ success = true;
+ break;
+ case e_slong:
+ success = true;
+ break;
+ case e_ulong:
+ m_type = e_slong;
+ success = true;
+ break;
+ case e_slonglong:
+ success = true;
+ break;
+ case e_ulonglong:
+ m_type = e_slonglong;
+ success = true;
+ break;
+ case e_sint128:
+ success = true;
+ break;
+ case e_uint128:
+ m_type = e_sint128;
+ success = true;
+ break;
+ case e_sint256:
+ success = true;
+ break;
+ case e_uint256:
+ m_type = e_sint256;
+ success = true;
+ break;
+ case e_sint512:
+ success = true;
+ break;
+ case e_uint512:
+ m_type = e_sint512;
+ success = true;
+ break;
+ case e_float:
+ success = true;
+ break;
+ case e_double:
+ success = true;
+ break;
+ case e_long_double:
+ success = true;
+ break;
+ }
+
+ return success;
+}
+
+bool Scalar::MakeUnsigned() {
+ bool success = false;
+
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ m_type = e_uint;
+ success = true;
+ break;
+ case e_uint:
+ success = true;
+ break;
+ case e_slong:
+ m_type = e_ulong;
+ success = true;
+ break;
+ case e_ulong:
+ success = true;
+ break;
+ case e_slonglong:
+ m_type = e_ulonglong;
+ success = true;
+ break;
+ case e_ulonglong:
+ success = true;
+ break;
+ case e_sint128:
+ m_type = e_uint128;
+ success = true;
+ break;
+ case e_uint128:
+ success = true;
+ break;
+ case e_sint256:
+ m_type = e_uint256;
+ success = true;
+ break;
+ case e_uint256:
+ success = true;
+ break;
+ case e_sint512:
+ m_type = e_uint512;
+ success = true;
+ break;
+ case e_uint512:
+ success = true;
+ break;
+ case e_float:
+ success = true;
+ break;
+ case e_double:
+ success = true;
+ break;
+ case e_long_double:
+ success = true;
+ break;
+ }
+
+ return success;
+}
+
+signed char Scalar::SChar(char fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return static_cast<schar_t>(
+ (m_integer.sextOrTrunc(sizeof(schar_t) * 8)).getSExtValue());
+ case e_float:
+ return static_cast<schar_t>(m_float.convertToFloat());
+ case e_double:
+ return static_cast<schar_t>(m_float.convertToDouble());
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return static_cast<schar_t>(
+ (ldbl_val.sextOrTrunc(sizeof(schar_t) * 8)).getSExtValue());
+ }
+ return fail_value;
+}
+
+unsigned char Scalar::UChar(unsigned char fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return static_cast<uchar_t>(
+ (m_integer.zextOrTrunc(sizeof(uchar_t) * 8)).getZExtValue());
+ case e_float:
+ return static_cast<uchar_t>(m_float.convertToFloat());
+ case e_double:
+ return static_cast<uchar_t>(m_float.convertToDouble());
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return static_cast<uchar_t>(
+ (ldbl_val.zextOrTrunc(sizeof(uchar_t) * 8)).getZExtValue());
+ }
+ return fail_value;
+}
+
+short Scalar::SShort(short fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return static_cast<sshort_t>(
+ (m_integer.sextOrTrunc(sizeof(sshort_t) * 8)).getSExtValue());
+ case e_float:
+ return static_cast<sshort_t>(m_float.convertToFloat());
+ case e_double:
+ return static_cast<sshort_t>(m_float.convertToDouble());
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return static_cast<sshort_t>(
+ (ldbl_val.sextOrTrunc(sizeof(sshort_t) * 8)).getSExtValue());
+ }
+ return fail_value;
+}
+
+unsigned short Scalar::UShort(unsigned short fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return static_cast<ushort_t>(
+ (m_integer.zextOrTrunc(sizeof(ushort_t) * 8)).getZExtValue());
+ case e_float:
+ return static_cast<ushort_t>(m_float.convertToFloat());
+ case e_double:
+ return static_cast<ushort_t>(m_float.convertToDouble());
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return static_cast<ushort_t>(
+ (ldbl_val.zextOrTrunc(sizeof(ushort_t) * 8)).getZExtValue());
+ }
+ return fail_value;
+}
+
+int Scalar::SInt(int fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return static_cast<sint_t>(
+ (m_integer.sextOrTrunc(sizeof(sint_t) * 8)).getSExtValue());
+ case e_float:
+ return static_cast<sint_t>(m_float.convertToFloat());
+ case e_double:
+ return static_cast<sint_t>(m_float.convertToDouble());
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return static_cast<sint_t>(
+ (ldbl_val.sextOrTrunc(sizeof(sint_t) * 8)).getSExtValue());
+ }
+ return fail_value;
+}
+
+unsigned int Scalar::UInt(unsigned int fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return static_cast<uint_t>(
+ (m_integer.zextOrTrunc(sizeof(uint_t) * 8)).getZExtValue());
+ case e_float:
+ return static_cast<uint_t>(m_float.convertToFloat());
+ case e_double:
+ return static_cast<uint_t>(m_float.convertToDouble());
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return static_cast<uint_t>(
+ (ldbl_val.zextOrTrunc(sizeof(uint_t) * 8)).getZExtValue());
+ }
+ return fail_value;
+}
+
+long Scalar::SLong(long fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return static_cast<slong_t>(
+ (m_integer.sextOrTrunc(sizeof(slong_t) * 8)).getSExtValue());
+ case e_float:
+ return static_cast<slong_t>(m_float.convertToFloat());
+ case e_double:
+ return static_cast<slong_t>(m_float.convertToDouble());
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return static_cast<slong_t>(
+ (ldbl_val.sextOrTrunc(sizeof(slong_t) * 8)).getSExtValue());
+ }
+ return fail_value;
+}
+
+unsigned long Scalar::ULong(unsigned long fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return static_cast<ulong_t>(
+ (m_integer.zextOrTrunc(sizeof(ulong_t) * 8)).getZExtValue());
+ case e_float:
+ return static_cast<ulong_t>(m_float.convertToFloat());
+ case e_double:
+ return static_cast<ulong_t>(m_float.convertToDouble());
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return static_cast<ulong_t>(
+ (ldbl_val.zextOrTrunc(sizeof(ulong_t) * 8)).getZExtValue());
+ }
+ return fail_value;
+}
+
+long long Scalar::SLongLong(long long fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return static_cast<slonglong_t>(
+ (m_integer.sextOrTrunc(sizeof(slonglong_t) * 8)).getSExtValue());
+ case e_float:
+ return static_cast<slonglong_t>(m_float.convertToFloat());
+ case e_double:
+ return static_cast<slonglong_t>(m_float.convertToDouble());
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return static_cast<slonglong_t>(
+ (ldbl_val.sextOrTrunc(sizeof(slonglong_t) * 8)).getSExtValue());
+ }
+ return fail_value;
+}
+
+unsigned long long Scalar::ULongLong(unsigned long long fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return static_cast<ulonglong_t>(
+ (m_integer.zextOrTrunc(sizeof(ulonglong_t) * 8)).getZExtValue());
+ case e_float:
+ return static_cast<ulonglong_t>(m_float.convertToFloat());
+ case e_double: {
+ double d_val = m_float.convertToDouble();
+ llvm::APInt rounded_double =
+ llvm::APIntOps::RoundDoubleToAPInt(d_val, sizeof(ulonglong_t) * 8);
+ return static_cast<ulonglong_t>(
+ (rounded_double.zextOrTrunc(sizeof(ulonglong_t) * 8)).getZExtValue());
+ }
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return static_cast<ulonglong_t>(
+ (ldbl_val.zextOrTrunc(sizeof(ulonglong_t) * 8)).getZExtValue());
+ }
+ return fail_value;
+}
+
+llvm::APInt Scalar::SInt128(llvm::APInt &fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return m_integer;
+ case e_float:
+ case e_double:
+ case e_long_double:
+ return m_float.bitcastToAPInt();
+ }
+ return fail_value;
+}
+
+llvm::APInt Scalar::UInt128(const llvm::APInt &fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return m_integer;
+ case e_float:
+ case e_double:
+ case e_long_double:
+ return m_float.bitcastToAPInt();
+ }
+ return fail_value;
+}
+
+float Scalar::Float(float fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return llvm::APIntOps::RoundAPIntToFloat(m_integer);
+ case e_float:
+ return m_float.convertToFloat();
+ case e_double:
+ return static_cast<float_t>(m_float.convertToDouble());
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return ldbl_val.bitsToFloat();
+ }
+ return fail_value;
+}
+
+double Scalar::Double(double fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return llvm::APIntOps::RoundAPIntToDouble(m_integer);
+ case e_float:
+ return static_cast<double_t>(m_float.convertToFloat());
+ case e_double:
+ return m_float.convertToDouble();
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return ldbl_val.bitsToFloat();
+ }
+ return fail_value;
+}
+
+long double Scalar::LongDouble(long double fail_value) const {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ return static_cast<long_double_t>(
+ llvm::APIntOps::RoundAPIntToDouble(m_integer));
+ case e_float:
+ return static_cast<long_double_t>(m_float.convertToFloat());
+ case e_double:
+ return static_cast<long_double_t>(m_float.convertToDouble());
+ case e_long_double:
+ llvm::APInt ldbl_val = m_float.bitcastToAPInt();
+ return static_cast<long_double_t>(ldbl_val.bitsToDouble());
+ }
+ return fail_value;
+}
+
+Scalar &Scalar::operator+=(const Scalar &rhs) {
+ Scalar temp_value;
+ const Scalar *a;
+ const Scalar *b;
+ if ((m_type = PromoteToMaxType(*this, rhs, temp_value, a, b)) !=
+ Scalar::e_void) {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ m_integer = a->m_integer + b->m_integer;
+ break;
+
+ case e_float:
+ case e_double:
+ case e_long_double:
+ m_float = a->m_float + b->m_float;
+ break;
+ }
+ }
+ return *this;
+}
+
+Scalar &Scalar::operator<<=(const Scalar &rhs) {
+ switch (m_type) {
+ case e_void:
+ case e_float:
+ case e_double:
+ case e_long_double:
+ m_type = e_void;
+ break;
+
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ switch (rhs.m_type) {
+ case e_void:
+ case e_float:
+ case e_double:
+ case e_long_double:
+ m_type = e_void;
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer << rhs.m_integer;
+ break;
+ }
+ break;
+ }
+ return *this;
+}
+
+bool Scalar::ShiftRightLogical(const Scalar &rhs) {
+ switch (m_type) {
+ case e_void:
+ case e_float:
+ case e_double:
+ case e_long_double:
+ m_type = e_void;
+ break;
+
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ switch (rhs.m_type) {
+ case e_void:
+ case e_float:
+ case e_double:
+ case e_long_double:
+ m_type = e_void;
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.lshr(rhs.m_integer);
+ break;
+ }
+ break;
+ }
+ return m_type != e_void;
+}
+
+Scalar &Scalar::operator>>=(const Scalar &rhs) {
+ switch (m_type) {
+ case e_void:
+ case e_float:
+ case e_double:
+ case e_long_double:
+ m_type = e_void;
+ break;
+
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ switch (rhs.m_type) {
+ case e_void:
+ case e_float:
+ case e_double:
+ case e_long_double:
+ m_type = e_void;
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ m_integer = m_integer.ashr(rhs.m_integer);
+ break;
+ }
+ break;
+ }
+ return *this;
+}
+
+Scalar &Scalar::operator&=(const Scalar &rhs) {
+ switch (m_type) {
+ case e_void:
+ case e_float:
+ case e_double:
+ case e_long_double:
+ m_type = e_void;
+ break;
+
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ switch (rhs.m_type) {
+ case e_void:
+ case e_float:
+ case e_double:
+ case e_long_double:
+ m_type = e_void;
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ m_integer &= rhs.m_integer;
+ break;
+ }
+ break;
+ }
+ return *this;
+}
+
+bool Scalar::AbsoluteValue() {
+ switch (m_type) {
+ case e_void:
+ break;
+
+ case e_sint:
+ case e_slong:
+ case e_slonglong:
+ case e_sint128:
+ case e_sint256:
+ case e_sint512:
+ if (m_integer.isNegative())
+ m_integer = -m_integer;
+ return true;
+
+ case e_uint:
+ case e_ulong:
+ case e_ulonglong:
+ return true;
+ case e_uint128:
+ case e_uint256:
+ case e_uint512:
+ case e_float:
+ case e_double:
+ case e_long_double:
+ m_float.clearSign();
+ return true;
+ }
+ return false;
+}
+
+bool Scalar::UnaryNegate() {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ m_integer = -m_integer;
+ return true;
+ case e_float:
+ case e_double:
+ case e_long_double:
+ m_float.changeSign();
+ return true;
+ }
+ return false;
+}
+
+bool Scalar::OnesComplement() {
+ switch (m_type) {
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ m_integer = ~m_integer;
+ return true;
+
+ case e_void:
+ case e_float:
+ case e_double:
+ case e_long_double:
+ break;
+ }
+ return false;
+}
+
+const Scalar lldb_private::operator+(const Scalar &lhs, const Scalar &rhs) {
+ Scalar result;
+ Scalar temp_value;
+ const Scalar *a;
+ const Scalar *b;
+ if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) !=
+ Scalar::e_void) {
+ switch (result.m_type) {
+ case Scalar::e_void:
+ break;
+ case Scalar::e_sint:
+ case Scalar::e_uint:
+ case Scalar::e_slong:
+ case Scalar::e_ulong:
+ case Scalar::e_slonglong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_uint128:
+ case Scalar::e_sint256:
+ case Scalar::e_uint256:
+ case Scalar::e_sint512:
+ case Scalar::e_uint512:
+ result.m_integer = a->m_integer + b->m_integer;
+ break;
+ case Scalar::e_float:
+ case Scalar::e_double:
+ case Scalar::e_long_double:
+ result.m_float = a->m_float + b->m_float;
+ break;
+ }
+ }
+ return result;
+}
+
+const Scalar lldb_private::operator-(const Scalar &lhs, const Scalar &rhs) {
+ Scalar result;
+ Scalar temp_value;
+ const Scalar *a;
+ const Scalar *b;
+ if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) !=
+ Scalar::e_void) {
+ switch (result.m_type) {
+ case Scalar::e_void:
+ break;
+ case Scalar::e_sint:
+ case Scalar::e_uint:
+ case Scalar::e_slong:
+ case Scalar::e_ulong:
+ case Scalar::e_slonglong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_uint128:
+ case Scalar::e_sint256:
+ case Scalar::e_uint256:
+ case Scalar::e_sint512:
+ case Scalar::e_uint512:
+ result.m_integer = a->m_integer - b->m_integer;
+ break;
+ case Scalar::e_float:
+ case Scalar::e_double:
+ case Scalar::e_long_double:
+ result.m_float = a->m_float - b->m_float;
+ break;
+ }
+ }
+ return result;
+}
+
+const Scalar lldb_private::operator/(const Scalar &lhs, const Scalar &rhs) {
+ Scalar result;
+ Scalar temp_value;
+ const Scalar *a;
+ const Scalar *b;
+ if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) !=
+ Scalar::e_void) {
+ switch (result.m_type) {
+ case Scalar::e_void:
+ break;
+ case Scalar::e_sint:
+ case Scalar::e_slong:
+ case Scalar::e_slonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_sint256:
+ case Scalar::e_sint512:
+ if (b->m_integer != 0) {
+ result.m_integer = a->m_integer.sdiv(b->m_integer);
+ return result;
+ }
+ break;
+ case Scalar::e_uint:
+ case Scalar::e_ulong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_uint128:
+ case Scalar::e_uint256:
+ case Scalar::e_uint512:
+ if (b->m_integer != 0) {
+ result.m_integer = a->m_integer.udiv(b->m_integer);
+ return result;
+ }
+ break;
+ case Scalar::e_float:
+ case Scalar::e_double:
+ case Scalar::e_long_double:
+ if (!b->m_float.isZero()) {
+ result.m_float = a->m_float / b->m_float;
+ return result;
+ }
+ break;
+ }
+ }
+ // For division only, the only way it should make it here is if a promotion
+ // failed, or if we are trying to do a divide by zero.
+ result.m_type = Scalar::e_void;
+ return result;
+}
+
+const Scalar lldb_private::operator*(const Scalar &lhs, const Scalar &rhs) {
+ Scalar result;
+ Scalar temp_value;
+ const Scalar *a;
+ const Scalar *b;
+ if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) !=
+ Scalar::e_void) {
+ switch (result.m_type) {
+ case Scalar::e_void:
+ break;
+ case Scalar::e_sint:
+ case Scalar::e_uint:
+ case Scalar::e_slong:
+ case Scalar::e_ulong:
+ case Scalar::e_slonglong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_uint128:
+ case Scalar::e_sint256:
+ case Scalar::e_uint256:
+ case Scalar::e_sint512:
+ case Scalar::e_uint512:
+ result.m_integer = a->m_integer * b->m_integer;
+ break;
+ case Scalar::e_float:
+ case Scalar::e_double:
+ case Scalar::e_long_double:
+ result.m_float = a->m_float * b->m_float;
+ break;
+ }
+ }
+ return result;
+}
+
+const Scalar lldb_private::operator&(const Scalar &lhs, const Scalar &rhs) {
+ Scalar result;
+ Scalar temp_value;
+ const Scalar *a;
+ const Scalar *b;
+ if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) !=
+ Scalar::e_void) {
+ switch (result.m_type) {
+ case Scalar::e_sint:
+ case Scalar::e_uint:
+ case Scalar::e_slong:
+ case Scalar::e_ulong:
+ case Scalar::e_slonglong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_uint128:
+ case Scalar::e_sint256:
+ case Scalar::e_uint256:
+ case Scalar::e_sint512:
+ case Scalar::e_uint512:
+ result.m_integer = a->m_integer & b->m_integer;
+ break;
+ case Scalar::e_void:
+ case Scalar::e_float:
+ case Scalar::e_double:
+ case Scalar::e_long_double:
+ // No bitwise AND on floats, doubles of long doubles
+ result.m_type = Scalar::e_void;
+ break;
+ }
+ }
+ return result;
+}
+
+const Scalar lldb_private::operator|(const Scalar &lhs, const Scalar &rhs) {
+ Scalar result;
+ Scalar temp_value;
+ const Scalar *a;
+ const Scalar *b;
+ if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) !=
+ Scalar::e_void) {
+ switch (result.m_type) {
+ case Scalar::e_sint:
+ case Scalar::e_uint:
+ case Scalar::e_slong:
+ case Scalar::e_ulong:
+ case Scalar::e_slonglong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_uint128:
+ case Scalar::e_sint256:
+ case Scalar::e_uint256:
+ case Scalar::e_sint512:
+ case Scalar::e_uint512:
+ result.m_integer = a->m_integer | b->m_integer;
+ break;
+
+ case Scalar::e_void:
+ case Scalar::e_float:
+ case Scalar::e_double:
+ case Scalar::e_long_double:
+ // No bitwise AND on floats, doubles of long doubles
+ result.m_type = Scalar::e_void;
+ break;
+ }
+ }
+ return result;
+}
+
+const Scalar lldb_private::operator%(const Scalar &lhs, const Scalar &rhs) {
+ Scalar result;
+ Scalar temp_value;
+ const Scalar *a;
+ const Scalar *b;
+ if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) !=
+ Scalar::e_void) {
+ switch (result.m_type) {
+ default:
+ break;
+ case Scalar::e_void:
+ break;
+ case Scalar::e_sint:
+ case Scalar::e_slong:
+ case Scalar::e_slonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_sint256:
+ case Scalar::e_sint512:
+ if (b->m_integer != 0) {
+ result.m_integer = a->m_integer.srem(b->m_integer);
+ return result;
+ }
+ break;
+ case Scalar::e_uint:
+ case Scalar::e_ulong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_uint128:
+ case Scalar::e_uint256:
+ case Scalar::e_uint512:
+ if (b->m_integer != 0) {
+ result.m_integer = a->m_integer.urem(b->m_integer);
+ return result;
+ }
+ break;
+ }
+ }
+ result.m_type = Scalar::e_void;
+ return result;
+}
+
+const Scalar lldb_private::operator^(const Scalar &lhs, const Scalar &rhs) {
+ Scalar result;
+ Scalar temp_value;
+ const Scalar *a;
+ const Scalar *b;
+ if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) !=
+ Scalar::e_void) {
+ switch (result.m_type) {
+ case Scalar::e_sint:
+ case Scalar::e_uint:
+ case Scalar::e_slong:
+ case Scalar::e_ulong:
+ case Scalar::e_slonglong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_uint128:
+ case Scalar::e_sint256:
+ case Scalar::e_uint256:
+ case Scalar::e_sint512:
+ case Scalar::e_uint512:
+ result.m_integer = a->m_integer ^ b->m_integer;
+ break;
+
+ case Scalar::e_void:
+ case Scalar::e_float:
+ case Scalar::e_double:
+ case Scalar::e_long_double:
+ // No bitwise AND on floats, doubles of long doubles
+ result.m_type = Scalar::e_void;
+ break;
+ }
+ }
+ return result;
+}
+
+const Scalar lldb_private::operator<<(const Scalar &lhs, const Scalar &rhs) {
+ Scalar result = lhs;
+ result <<= rhs;
+ return result;
+}
+
+const Scalar lldb_private::operator>>(const Scalar &lhs, const Scalar &rhs) {
+ Scalar result = lhs;
+ result >>= rhs;
+ return result;
+}
+
+Status Scalar::SetValueFromCString(const char *value_str, Encoding encoding,
+ size_t byte_size) {
+ Status error;
+ if (value_str == nullptr || value_str[0] == '\0') {
+ error.SetErrorString("Invalid c-string value string.");
+ return error;
+ }
+ switch (encoding) {
+ case eEncodingInvalid:
+ error.SetErrorString("Invalid encoding.");
+ break;
+
+ case eEncodingUint:
+ if (byte_size <= sizeof(uint64_t)) {
+ uint64_t uval64;
+ if (!llvm::to_integer(value_str, uval64))
+ error.SetErrorStringWithFormat(
+ "'%s' is not a valid unsigned integer string value", value_str);
+ else if (!UIntValueIsValidForSize(uval64, byte_size))
+ error.SetErrorStringWithFormat(
+ "value 0x%" PRIx64 " is too large to fit in a %" PRIu64
+ " byte unsigned integer value",
+ uval64, static_cast<uint64_t>(byte_size));
+ else {
+ m_type = Scalar::GetValueTypeForUnsignedIntegerWithByteSize(byte_size);
+ switch (m_type) {
+ case e_uint:
+ m_integer = llvm::APInt(sizeof(uint_t) * 8, uval64, false);
+ break;
+ case e_ulong:
+ m_integer = llvm::APInt(sizeof(ulong_t) * 8, uval64, false);
+ break;
+ case e_ulonglong:
+ m_integer = llvm::APInt(sizeof(ulonglong_t) * 8, uval64, false);
+ break;
+ default:
+ error.SetErrorStringWithFormat(
+ "unsupported unsigned integer byte size: %" PRIu64 "",
+ static_cast<uint64_t>(byte_size));
+ break;
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "unsupported unsigned integer byte size: %" PRIu64 "",
+ static_cast<uint64_t>(byte_size));
+ return error;
+ }
+ break;
+
+ case eEncodingSint:
+ if (byte_size <= sizeof(int64_t)) {
+ int64_t sval64;
+ if (!llvm::to_integer(value_str, sval64))
+ error.SetErrorStringWithFormat(
+ "'%s' is not a valid signed integer string value", value_str);
+ else if (!SIntValueIsValidForSize(sval64, byte_size))
+ error.SetErrorStringWithFormat(
+ "value 0x%" PRIx64 " is too large to fit in a %" PRIu64
+ " byte signed integer value",
+ sval64, static_cast<uint64_t>(byte_size));
+ else {
+ m_type = Scalar::GetValueTypeForSignedIntegerWithByteSize(byte_size);
+ switch (m_type) {
+ case e_sint:
+ m_integer = llvm::APInt(sizeof(sint_t) * 8, sval64, true);
+ break;
+ case e_slong:
+ m_integer = llvm::APInt(sizeof(slong_t) * 8, sval64, true);
+ break;
+ case e_slonglong:
+ m_integer = llvm::APInt(sizeof(slonglong_t) * 8, sval64, true);
+ break;
+ default:
+ error.SetErrorStringWithFormat(
+ "unsupported signed integer byte size: %" PRIu64 "",
+ static_cast<uint64_t>(byte_size));
+ break;
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "unsupported signed integer byte size: %" PRIu64 "",
+ static_cast<uint64_t>(byte_size));
+ return error;
+ }
+ break;
+
+ case eEncodingIEEE754:
+ static float f_val;
+ static double d_val;
+ static long double l_val;
+ if (byte_size == sizeof(float)) {
+ if (::sscanf(value_str, "%f", &f_val) == 1) {
+ m_float = llvm::APFloat(f_val);
+ m_type = e_float;
+ } else
+ error.SetErrorStringWithFormat("'%s' is not a valid float string value",
+ value_str);
+ } else if (byte_size == sizeof(double)) {
+ if (::sscanf(value_str, "%lf", &d_val) == 1) {
+ m_float = llvm::APFloat(d_val);
+ m_type = e_double;
+ } else
+ error.SetErrorStringWithFormat("'%s' is not a valid float string value",
+ value_str);
+ } else if (byte_size == sizeof(long double)) {
+ if (::sscanf(value_str, "%Lf", &l_val) == 1) {
+ m_float = llvm::APFloat(
+ llvm::APFloat::x87DoubleExtended(),
+ llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128,
+ (reinterpret_cast<type128 *>(&l_val))->x));
+ m_type = e_long_double;
+ } else
+ error.SetErrorStringWithFormat("'%s' is not a valid float string value",
+ value_str);
+ } else {
+ error.SetErrorStringWithFormat("unsupported float byte size: %" PRIu64 "",
+ static_cast<uint64_t>(byte_size));
+ return error;
+ }
+ break;
+
+ case eEncodingVector:
+ error.SetErrorString("vector encoding unsupported.");
+ break;
+ }
+ if (error.Fail())
+ m_type = e_void;
+
+ return error;
+}
+
+Status Scalar::SetValueFromData(DataExtractor &data, lldb::Encoding encoding,
+ size_t byte_size) {
+ Status error;
+
+ type128 int128;
+ type256 int256;
+ switch (encoding) {
+ case lldb::eEncodingInvalid:
+ error.SetErrorString("invalid encoding");
+ break;
+ case lldb::eEncodingVector:
+ error.SetErrorString("vector encoding unsupported");
+ break;
+ case lldb::eEncodingUint: {
+ lldb::offset_t offset = 0;
+
+ switch (byte_size) {
+ case 1:
+ operator=(data.GetU8(&offset));
+ break;
+ case 2:
+ operator=(data.GetU16(&offset));
+ break;
+ case 4:
+ operator=(data.GetU32(&offset));
+ break;
+ case 8:
+ operator=(data.GetU64(&offset));
+ break;
+ case 16:
+ if (data.GetByteOrder() == eByteOrderBig) {
+ int128.x[1] = data.GetU64(&offset);
+ int128.x[0] = data.GetU64(&offset);
+ } else {
+ int128.x[0] = data.GetU64(&offset);
+ int128.x[1] = data.GetU64(&offset);
+ }
+ operator=(llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128, int128.x));
+ break;
+ case 32:
+ if (data.GetByteOrder() == eByteOrderBig) {
+ int256.x[3] = data.GetU64(&offset);
+ int256.x[2] = data.GetU64(&offset);
+ int256.x[1] = data.GetU64(&offset);
+ int256.x[0] = data.GetU64(&offset);
+ } else {
+ int256.x[0] = data.GetU64(&offset);
+ int256.x[1] = data.GetU64(&offset);
+ int256.x[2] = data.GetU64(&offset);
+ int256.x[3] = data.GetU64(&offset);
+ }
+ operator=(llvm::APInt(BITWIDTH_INT256, NUM_OF_WORDS_INT256, int256.x));
+ break;
+ default:
+ error.SetErrorStringWithFormat(
+ "unsupported unsigned integer byte size: %" PRIu64 "",
+ static_cast<uint64_t>(byte_size));
+ break;
+ }
+ } break;
+ case lldb::eEncodingSint: {
+ lldb::offset_t offset = 0;
+
+ switch (byte_size) {
+ case 1:
+ operator=(static_cast<int8_t>(data.GetU8(&offset)));
+ break;
+ case 2:
+ operator=(static_cast<int16_t>(data.GetU16(&offset)));
+ break;
+ case 4:
+ operator=(static_cast<int32_t>(data.GetU32(&offset)));
+ break;
+ case 8:
+ operator=(static_cast<int64_t>(data.GetU64(&offset)));
+ break;
+ case 16:
+ if (data.GetByteOrder() == eByteOrderBig) {
+ int128.x[1] = data.GetU64(&offset);
+ int128.x[0] = data.GetU64(&offset);
+ } else {
+ int128.x[0] = data.GetU64(&offset);
+ int128.x[1] = data.GetU64(&offset);
+ }
+ operator=(llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128, int128.x));
+ break;
+ case 32:
+ if (data.GetByteOrder() == eByteOrderBig) {
+ int256.x[3] = data.GetU64(&offset);
+ int256.x[2] = data.GetU64(&offset);
+ int256.x[1] = data.GetU64(&offset);
+ int256.x[0] = data.GetU64(&offset);
+ } else {
+ int256.x[0] = data.GetU64(&offset);
+ int256.x[1] = data.GetU64(&offset);
+ int256.x[2] = data.GetU64(&offset);
+ int256.x[3] = data.GetU64(&offset);
+ }
+ operator=(llvm::APInt(BITWIDTH_INT256, NUM_OF_WORDS_INT256, int256.x));
+ break;
+ default:
+ error.SetErrorStringWithFormat(
+ "unsupported signed integer byte size: %" PRIu64 "",
+ static_cast<uint64_t>(byte_size));
+ break;
+ }
+ } break;
+ case lldb::eEncodingIEEE754: {
+ lldb::offset_t offset = 0;
+
+ if (byte_size == sizeof(float))
+ operator=(data.GetFloat(&offset));
+ else if (byte_size == sizeof(double))
+ operator=(data.GetDouble(&offset));
+ else if (byte_size == sizeof(long double))
+ operator=(data.GetLongDouble(&offset));
+ else
+ error.SetErrorStringWithFormat("unsupported float byte size: %" PRIu64 "",
+ static_cast<uint64_t>(byte_size));
+ } break;
+ }
+
+ return error;
+}
+
+bool Scalar::SignExtend(uint32_t sign_bit_pos) {
+ const uint32_t max_bit_pos = GetByteSize() * 8;
+
+ if (sign_bit_pos < max_bit_pos) {
+ switch (m_type) {
+ case Scalar::e_void:
+ case Scalar::e_float:
+ case Scalar::e_double:
+ case Scalar::e_long_double:
+ return false;
+
+ case Scalar::e_sint:
+ case Scalar::e_uint:
+ case Scalar::e_slong:
+ case Scalar::e_ulong:
+ case Scalar::e_slonglong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_uint128:
+ case Scalar::e_sint256:
+ case Scalar::e_uint256:
+ case Scalar::e_sint512:
+ case Scalar::e_uint512:
+ if (max_bit_pos == sign_bit_pos)
+ return true;
+ else if (sign_bit_pos < (max_bit_pos - 1)) {
+ llvm::APInt sign_bit = llvm::APInt::getSignMask(sign_bit_pos + 1);
+ llvm::APInt bitwize_and = m_integer & sign_bit;
+ if (bitwize_and.getBoolValue()) {
+ const llvm::APInt mask =
+ ~(sign_bit) + llvm::APInt(m_integer.getBitWidth(), 1);
+ m_integer |= mask;
+ }
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+size_t Scalar::GetAsMemoryData(void *dst, size_t dst_len,
+ lldb::ByteOrder dst_byte_order,
+ Status &error) const {
+ // Get a data extractor that points to the native scalar data
+ DataExtractor data;
+ if (!GetData(data)) {
+ error.SetErrorString("invalid scalar value");
+ return 0;
+ }
+
+ const size_t src_len = data.GetByteSize();
+
+ // Prepare a memory buffer that contains some or all of the register value
+ const size_t bytes_copied =
+ data.CopyByteOrderedData(0, // src offset
+ src_len, // src length
+ dst, // dst buffer
+ dst_len, // dst length
+ dst_byte_order); // dst byte order
+ if (bytes_copied == 0)
+ error.SetErrorString("failed to copy data");
+
+ return bytes_copied;
+}
+
+bool Scalar::ExtractBitfield(uint32_t bit_size, uint32_t bit_offset) {
+ if (bit_size == 0)
+ return true;
+
+ switch (m_type) {
+ case Scalar::e_void:
+ case Scalar::e_float:
+ case Scalar::e_double:
+ case Scalar::e_long_double:
+ break;
+
+ case Scalar::e_sint:
+ case Scalar::e_slong:
+ case Scalar::e_slonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_sint256:
+ case Scalar::e_sint512:
+ m_integer = m_integer.ashr(bit_offset)
+ .sextOrTrunc(bit_size)
+ .sextOrSelf(8 * GetByteSize());
+ return true;
+
+ case Scalar::e_uint:
+ case Scalar::e_ulong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_uint128:
+ case Scalar::e_uint256:
+ case Scalar::e_uint512:
+ m_integer = m_integer.lshr(bit_offset)
+ .zextOrTrunc(bit_size)
+ .zextOrSelf(8 * GetByteSize());
+ return true;
+ }
+ return false;
+}
+
+bool lldb_private::operator==(const Scalar &lhs, const Scalar &rhs) {
+ // If either entry is void then we can just compare the types
+ if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void)
+ return lhs.m_type == rhs.m_type;
+
+ Scalar temp_value;
+ const Scalar *a;
+ const Scalar *b;
+ llvm::APFloat::cmpResult result;
+ switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) {
+ case Scalar::e_void:
+ break;
+ case Scalar::e_sint:
+ case Scalar::e_uint:
+ case Scalar::e_slong:
+ case Scalar::e_ulong:
+ case Scalar::e_slonglong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_uint128:
+ case Scalar::e_sint256:
+ case Scalar::e_uint256:
+ case Scalar::e_sint512:
+ case Scalar::e_uint512:
+ return a->m_integer == b->m_integer;
+ case Scalar::e_float:
+ case Scalar::e_double:
+ case Scalar::e_long_double:
+ result = a->m_float.compare(b->m_float);
+ if (result == llvm::APFloat::cmpEqual)
+ return true;
+ }
+ return false;
+}
+
+bool lldb_private::operator!=(const Scalar &lhs, const Scalar &rhs) {
+ return !(lhs == rhs);
+}
+
+bool lldb_private::operator<(const Scalar &lhs, const Scalar &rhs) {
+ if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void)
+ return false;
+
+ Scalar temp_value;
+ const Scalar *a;
+ const Scalar *b;
+ llvm::APFloat::cmpResult result;
+ switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) {
+ case Scalar::e_void:
+ break;
+ case Scalar::e_sint:
+ case Scalar::e_slong:
+ case Scalar::e_slonglong:
+ case Scalar::e_sint128:
+ case Scalar::e_sint256:
+ case Scalar::e_sint512:
+ case Scalar::e_uint512:
+ return a->m_integer.slt(b->m_integer);
+ case Scalar::e_uint:
+ case Scalar::e_ulong:
+ case Scalar::e_ulonglong:
+ case Scalar::e_uint128:
+ case Scalar::e_uint256:
+ return a->m_integer.ult(b->m_integer);
+ case Scalar::e_float:
+ case Scalar::e_double:
+ case Scalar::e_long_double:
+ result = a->m_float.compare(b->m_float);
+ if (result == llvm::APFloat::cmpLessThan)
+ return true;
+ }
+ return false;
+}
+
+bool lldb_private::operator<=(const Scalar &lhs, const Scalar &rhs) {
+ return !(rhs < lhs);
+}
+
+bool lldb_private::operator>(const Scalar &lhs, const Scalar &rhs) {
+ return rhs < lhs;
+}
+
+bool lldb_private::operator>=(const Scalar &lhs, const Scalar &rhs) {
+ return !(lhs < rhs);
+}
+
+bool Scalar::ClearBit(uint32_t bit) {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ m_integer.clearBit(bit);
+ return true;
+ case e_float:
+ case e_double:
+ case e_long_double:
+ break;
+ }
+ return false;
+}
+
+bool Scalar::SetBit(uint32_t bit) {
+ switch (m_type) {
+ case e_void:
+ break;
+ case e_sint:
+ case e_uint:
+ case e_slong:
+ case e_ulong:
+ case e_slonglong:
+ case e_ulonglong:
+ case e_sint128:
+ case e_uint128:
+ case e_sint256:
+ case e_uint256:
+ case e_sint512:
+ case e_uint512:
+ m_integer.setBit(bit);
+ return true;
+ case e_float:
+ case e_double:
+ case e_long_double:
+ break;
+ }
+ return false;
+}
+
+llvm::raw_ostream &lldb_private::operator<<(llvm::raw_ostream &os, const Scalar &scalar) {
+ StreamString s;
+ scalar.GetValue(&s, /*show_type*/ true);
+ return os << s.GetString();
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/SelectHelper.cpp b/contrib/llvm-project/lldb/source/Utility/SelectHelper.cpp
new file mode 100644
index 000000000000..ff21d99e400a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/SelectHelper.cpp
@@ -0,0 +1,254 @@
+//===-- SelectHelper.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+// Enable this special support for Apple builds where we can have unlimited
+// select bounds. We tried switching to poll() and kqueue and we were panicing
+// the kernel, so we have to stick with select for now.
+#define _DARWIN_UNLIMITED_SELECT
+#endif
+
+#include "lldb/Utility/SelectHelper.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-types.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
+
+#include <algorithm>
+#include <chrono>
+
+#include <errno.h>
+#if defined(_WIN32)
+// Define NOMINMAX to avoid macros that conflict with std::min and std::max
+#define NOMINMAX
+#include <winsock2.h>
+#else
+#include <sys/time.h>
+#include <sys/select.h>
+#endif
+
+
+SelectHelper::SelectHelper()
+ : m_fd_map(), m_end_time() // Infinite timeout unless
+ // SelectHelper::SetTimeout() gets called
+{}
+
+void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {
+ using namespace std::chrono;
+ m_end_time = steady_clock::time_point(steady_clock::now() + timeout);
+}
+
+void SelectHelper::FDSetRead(lldb::socket_t fd) {
+ m_fd_map[fd].read_set = true;
+}
+
+void SelectHelper::FDSetWrite(lldb::socket_t fd) {
+ m_fd_map[fd].write_set = true;
+}
+
+void SelectHelper::FDSetError(lldb::socket_t fd) {
+ m_fd_map[fd].error_set = true;
+}
+
+bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const {
+ auto pos = m_fd_map.find(fd);
+ if (pos != m_fd_map.end())
+ return pos->second.read_is_set;
+ else
+ return false;
+}
+
+bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const {
+ auto pos = m_fd_map.find(fd);
+ if (pos != m_fd_map.end())
+ return pos->second.write_is_set;
+ else
+ return false;
+}
+
+bool SelectHelper::FDIsSetError(lldb::socket_t fd) const {
+ auto pos = m_fd_map.find(fd);
+ if (pos != m_fd_map.end())
+ return pos->second.error_is_set;
+ else
+ return false;
+}
+
+static void updateMaxFd(llvm::Optional<lldb::socket_t> &vold,
+ lldb::socket_t vnew) {
+ if (!vold.hasValue())
+ vold = vnew;
+ else
+ vold = std::max(*vold, vnew);
+}
+
+lldb_private::Status SelectHelper::Select() {
+ lldb_private::Status error;
+#ifdef _MSC_VER
+ // On windows FD_SETSIZE limits the number of file descriptors, not their
+ // numeric value.
+ lldbassert(m_fd_map.size() <= FD_SETSIZE);
+ if (m_fd_map.size() > FD_SETSIZE)
+ return lldb_private::Status("Too many file descriptors for select()");
+#endif
+
+ llvm::Optional<lldb::socket_t> max_read_fd;
+ llvm::Optional<lldb::socket_t> max_write_fd;
+ llvm::Optional<lldb::socket_t> max_error_fd;
+ llvm::Optional<lldb::socket_t> max_fd;
+ for (auto &pair : m_fd_map) {
+ pair.second.PrepareForSelect();
+ const lldb::socket_t fd = pair.first;
+#if !defined(__APPLE__) && !defined(_MSC_VER)
+ lldbassert(fd < static_cast<int>(FD_SETSIZE));
+ if (fd >= static_cast<int>(FD_SETSIZE)) {
+ error.SetErrorStringWithFormat("%i is too large for select()", fd);
+ return error;
+ }
+#endif
+ if (pair.second.read_set)
+ updateMaxFd(max_read_fd, fd);
+ if (pair.second.write_set)
+ updateMaxFd(max_write_fd, fd);
+ if (pair.second.error_set)
+ updateMaxFd(max_error_fd, fd);
+ updateMaxFd(max_fd, fd);
+ }
+
+ if (!max_fd.hasValue()) {
+ error.SetErrorString("no valid file descriptors");
+ return error;
+ }
+
+ const unsigned nfds = static_cast<unsigned>(*max_fd) + 1;
+ fd_set *read_fdset_ptr = nullptr;
+ fd_set *write_fdset_ptr = nullptr;
+ fd_set *error_fdset_ptr = nullptr;
+// Initialize and zero out the fdsets
+#if defined(__APPLE__)
+ llvm::SmallVector<fd_set, 1> read_fdset;
+ llvm::SmallVector<fd_set, 1> write_fdset;
+ llvm::SmallVector<fd_set, 1> error_fdset;
+
+ if (max_read_fd.hasValue()) {
+ read_fdset.resize((nfds / FD_SETSIZE) + 1);
+ read_fdset_ptr = read_fdset.data();
+ }
+ if (max_write_fd.hasValue()) {
+ write_fdset.resize((nfds / FD_SETSIZE) + 1);
+ write_fdset_ptr = write_fdset.data();
+ }
+ if (max_error_fd.hasValue()) {
+ error_fdset.resize((nfds / FD_SETSIZE) + 1);
+ error_fdset_ptr = error_fdset.data();
+ }
+ for (auto &fd_set : read_fdset)
+ FD_ZERO(&fd_set);
+ for (auto &fd_set : write_fdset)
+ FD_ZERO(&fd_set);
+ for (auto &fd_set : error_fdset)
+ FD_ZERO(&fd_set);
+#else
+ fd_set read_fdset;
+ fd_set write_fdset;
+ fd_set error_fdset;
+
+ if (max_read_fd.hasValue()) {
+ FD_ZERO(&read_fdset);
+ read_fdset_ptr = &read_fdset;
+ }
+ if (max_write_fd.hasValue()) {
+ FD_ZERO(&write_fdset);
+ write_fdset_ptr = &write_fdset;
+ }
+ if (max_error_fd.hasValue()) {
+ FD_ZERO(&error_fdset);
+ error_fdset_ptr = &error_fdset;
+ }
+#endif
+ // Set the FD bits in the fdsets for read/write/error
+ for (auto &pair : m_fd_map) {
+ const lldb::socket_t fd = pair.first;
+
+ if (pair.second.read_set)
+ FD_SET(fd, read_fdset_ptr);
+
+ if (pair.second.write_set)
+ FD_SET(fd, write_fdset_ptr);
+
+ if (pair.second.error_set)
+ FD_SET(fd, error_fdset_ptr);
+ }
+
+ // Setup our timeout time value if needed
+ struct timeval *tv_ptr = nullptr;
+ struct timeval tv = {0, 0};
+
+ while (true) {
+ using namespace std::chrono;
+ // Setup out relative timeout based on the end time if we have one
+ if (m_end_time.hasValue()) {
+ tv_ptr = &tv;
+ const auto remaining_dur = duration_cast<microseconds>(
+ m_end_time.getValue() - steady_clock::now());
+ if (remaining_dur.count() > 0) {
+ // Wait for a specific amount of time
+ const auto dur_secs = duration_cast<seconds>(remaining_dur);
+ const auto dur_usecs = remaining_dur % seconds(1);
+ tv.tv_sec = dur_secs.count();
+ tv.tv_usec = dur_usecs.count();
+ } else {
+ // Just poll once with no timeout
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ }
+ }
+ const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,
+ error_fdset_ptr, tv_ptr);
+ if (num_set_fds < 0) {
+ // We got an error
+ error.SetErrorToErrno();
+ if (error.GetError() == EINTR) {
+ error.Clear();
+ continue; // Keep calling select if we get EINTR
+ } else
+ return error;
+ } else if (num_set_fds == 0) {
+ // Timeout
+ error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX);
+ error.SetErrorString("timed out");
+ return error;
+ } else {
+ // One or more descriptors were set, update the FDInfo::select_is_set
+ // mask so users can ask the SelectHelper class so clients can call one
+ // of:
+
+ for (auto &pair : m_fd_map) {
+ const int fd = pair.first;
+
+ if (pair.second.read_set) {
+ if (FD_ISSET(fd, read_fdset_ptr))
+ pair.second.read_is_set = true;
+ }
+ if (pair.second.write_set) {
+ if (FD_ISSET(fd, write_fdset_ptr))
+ pair.second.write_is_set = true;
+ }
+ if (pair.second.error_set) {
+ if (FD_ISSET(fd, error_fdset_ptr))
+ pair.second.error_is_set = true;
+ }
+ }
+ break;
+ }
+ }
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/SharingPtr.cpp b/contrib/llvm-project/lldb/source/Utility/SharingPtr.cpp
new file mode 100644
index 000000000000..45f2a773758b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/SharingPtr.cpp
@@ -0,0 +1,134 @@
+//===---------------------SharingPtr.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/SharingPtr.h"
+
+#if defined(ENABLE_SP_LOGGING)
+
+// If ENABLE_SP_LOGGING is defined, then log all shared pointer assignments and
+// allow them to be queried using a pointer by a call to:
+#include <assert.h>
+#include <execinfo.h>
+
+#include "llvm/ADT/STLExtras.h"
+
+#include <map>
+#include <mutex>
+#include <vector>
+
+class Backtrace {
+public:
+ Backtrace();
+
+ ~Backtrace();
+
+ void GetFrames();
+
+ void Dump() const;
+
+private:
+ void *m_sp_this;
+ std::vector<void *> m_frames;
+};
+
+Backtrace::Backtrace() : m_frames() {}
+
+Backtrace::~Backtrace() {}
+
+void Backtrace::GetFrames() {
+ void *frames[1024];
+ const int count = ::backtrace(frames, llvm::array_lengthof(frames));
+ if (count > 2)
+ m_frames.assign(frames + 2, frames + (count - 2));
+}
+
+void Backtrace::Dump() const {
+ if (!m_frames.empty())
+ ::backtrace_symbols_fd(m_frames.data(), m_frames.size(), STDOUT_FILENO);
+ write(STDOUT_FILENO, "\n\n", 2);
+}
+
+extern "C" void track_sp(void *sp_this, void *ptr, long use_count) {
+ typedef std::pair<void *, Backtrace> PtrBacktracePair;
+ typedef std::map<void *, PtrBacktracePair> PtrToBacktraceMap;
+ static std::mutex g_mutex;
+ std::lock_guard<std::mutex> guard(g_mutex);
+ static PtrToBacktraceMap g_map;
+
+ if (sp_this) {
+ printf("sp(%p) -> %p %lu\n", sp_this, ptr, use_count);
+
+ if (ptr) {
+ Backtrace bt;
+ bt.GetFrames();
+ g_map[sp_this] = std::make_pair(ptr, bt);
+ } else {
+ g_map.erase(sp_this);
+ }
+ } else {
+ if (ptr)
+ printf("Searching for shared pointers that are tracking %p: ", ptr);
+ else
+ printf("Dump all live shared pointres: ");
+
+ uint32_t matches = 0;
+ PtrToBacktraceMap::iterator pos, end = g_map.end();
+ for (pos = g_map.begin(); pos != end; ++pos) {
+ if (ptr == NULL || pos->second.first == ptr) {
+ ++matches;
+ printf("\nsp(%p): %p\n", pos->first, pos->second.first);
+ pos->second.second.Dump();
+ }
+ }
+ if (matches == 0) {
+ printf("none.\n");
+ }
+ }
+}
+// Put dump_sp_refs in the lldb namespace to it gets through our exports lists
+// filter in the LLDB.framework or lldb.so
+namespace lldb {
+
+void dump_sp_refs(void *ptr) {
+ // Use a specially crafted call to "track_sp" which will dump info on all
+ // live shared pointers that reference "ptr"
+ track_sp(NULL, ptr, 0);
+}
+}
+
+#endif
+
+namespace lldb_private {
+
+namespace imp {
+
+shared_count::~shared_count() {}
+
+void shared_count::add_shared() {
+#ifdef _MSC_VER
+ _InterlockedIncrement(&shared_owners_);
+#else
+ ++shared_owners_;
+#endif
+}
+
+void shared_count::release_shared() {
+#ifdef _MSC_VER
+ if (_InterlockedDecrement(&shared_owners_) == -1)
+#else
+ if (--shared_owners_ == -1)
+#endif
+ {
+ on_zero_shared();
+ delete this;
+ }
+}
+
+} // imp
+
+} // namespace lldb
diff --git a/contrib/llvm-project/lldb/source/Utility/State.cpp b/contrib/llvm-project/lldb/source/Utility/State.cpp
new file mode 100644
index 000000000000..51fe92bad77e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/State.cpp
@@ -0,0 +1,110 @@
+//===-- State.cpp -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *lldb_private::StateAsCString(StateType state) {
+ switch (state) {
+ case eStateInvalid:
+ return "invalid";
+ case eStateUnloaded:
+ return "unloaded";
+ case eStateConnected:
+ return "connected";
+ case eStateAttaching:
+ return "attaching";
+ case eStateLaunching:
+ return "launching";
+ case eStateStopped:
+ return "stopped";
+ case eStateRunning:
+ return "running";
+ case eStateStepping:
+ return "stepping";
+ case eStateCrashed:
+ return "crashed";
+ case eStateDetached:
+ return "detached";
+ case eStateExited:
+ return "exited";
+ case eStateSuspended:
+ return "suspended";
+ }
+ return "unknown";
+}
+
+const char *lldb_private::GetPermissionsAsCString(uint32_t permissions) {
+ switch (permissions) {
+ case 0:
+ return "---";
+ case ePermissionsWritable:
+ return "-w-";
+ case ePermissionsReadable:
+ return "r--";
+ case ePermissionsExecutable:
+ return "--x";
+ case ePermissionsReadable | ePermissionsWritable:
+ return "rw-";
+ case ePermissionsReadable | ePermissionsExecutable:
+ return "r-x";
+ case ePermissionsWritable | ePermissionsExecutable:
+ return "-wx";
+ case ePermissionsReadable | ePermissionsWritable | ePermissionsExecutable:
+ return "rwx";
+ default:
+ break;
+ }
+ return "???";
+}
+
+bool lldb_private::StateIsRunningState(StateType state) {
+ switch (state) {
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateRunning:
+ case eStateStepping:
+ return true;
+
+ case eStateConnected:
+ case eStateDetached:
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateExited:
+ case eStateSuspended:
+ break;
+ }
+ return false;
+}
+
+bool lldb_private::StateIsStoppedState(StateType state, bool must_exist) {
+ switch (state) {
+ case eStateInvalid:
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateDetached:
+ break;
+
+ case eStateUnloaded:
+ case eStateExited:
+ return !must_exist;
+
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ return true;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Status.cpp b/contrib/llvm-project/lldb/source/Utility/Status.cpp
new file mode 100644
index 000000000000..3d64fb810abf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Status.cpp
@@ -0,0 +1,296 @@
+//===-- Status.cpp -----------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Status.h"
+
+#include "lldb/Utility/VASPrintf.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FormatProviders.h"
+
+#include <cerrno>
+#include <cstdarg>
+#include <string>
+#include <system_error>
+
+#ifdef __APPLE__
+#include <mach/mach.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include <stdint.h>
+
+namespace llvm {
+class raw_ostream;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+Status::Status() : m_code(0), m_type(eErrorTypeInvalid), m_string() {}
+
+Status::Status(ValueType err, ErrorType type)
+ : m_code(err), m_type(type), m_string() {}
+
+Status::Status(std::error_code EC)
+ : m_code(EC.value()), m_type(ErrorType::eErrorTypeGeneric),
+ m_string(EC.message()) {}
+
+Status::Status(const char *format, ...)
+ : m_code(0), m_type(eErrorTypeInvalid), m_string() {
+ va_list args;
+ va_start(args, format);
+ SetErrorToGenericError();
+ SetErrorStringWithVarArg(format, args);
+ va_end(args);
+}
+
+const Status &Status::operator=(llvm::Error error) {
+ if (!error) {
+ Clear();
+ return *this;
+ }
+
+ // if the error happens to be a errno error, preserve the error code
+ error = llvm::handleErrors(
+ std::move(error), [&](std::unique_ptr<llvm::ECError> e) -> llvm::Error {
+ std::error_code ec = e->convertToErrorCode();
+ if (ec.category() == std::generic_category()) {
+ m_code = ec.value();
+ m_type = ErrorType::eErrorTypePOSIX;
+ return llvm::Error::success();
+ }
+ return llvm::Error(std::move(e));
+ });
+
+ // Otherwise, just preserve the message
+ if (error) {
+ SetErrorToGenericError();
+ SetErrorString(llvm::toString(std::move(error)));
+ }
+
+ return *this;
+}
+
+llvm::Error Status::ToError() const {
+ if (Success())
+ return llvm::Error::success();
+ if (m_type == ErrorType::eErrorTypePOSIX)
+ return llvm::errorCodeToError(
+ std::error_code(m_code, std::generic_category()));
+ return llvm::make_error<llvm::StringError>(AsCString(),
+ llvm::inconvertibleErrorCode());
+}
+
+// Assignment operator
+const Status &Status::operator=(const Status &rhs) {
+ if (this != &rhs) {
+ m_code = rhs.m_code;
+ m_type = rhs.m_type;
+ m_string = rhs.m_string;
+ }
+ return *this;
+}
+
+Status::~Status() = default;
+
+#ifdef _WIN32
+static std::string RetrieveWin32ErrorString(uint32_t error_code) {
+ char *buffer = nullptr;
+ std::string message;
+ // Retrieve win32 system error.
+ if (::FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR)&buffer, 0, NULL)) {
+ message.assign(buffer);
+ ::LocalFree(buffer);
+ }
+ return message;
+}
+#endif
+
+// Get the error value as a NULL C string. The error string will be fetched and
+// cached on demand. The cached error string value will remain until the error
+// value is changed or cleared.
+const char *Status::AsCString(const char *default_error_str) const {
+ if (Success())
+ return nullptr;
+
+ if (m_string.empty()) {
+ switch (m_type) {
+ case eErrorTypeMachKernel:
+#if defined(__APPLE__)
+ if (const char *s = ::mach_error_string(m_code))
+ m_string.assign(s);
+#endif
+ break;
+
+ case eErrorTypePOSIX:
+ m_string = llvm::sys::StrError(m_code);
+ break;
+
+ case eErrorTypeWin32:
+#if defined(_WIN32)
+ m_string = RetrieveWin32ErrorString(m_code);
+#endif
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (m_string.empty()) {
+ if (default_error_str)
+ m_string.assign(default_error_str);
+ else
+ return nullptr; // User wanted a nullptr string back...
+ }
+ return m_string.c_str();
+}
+
+// Clear the error and any cached error string that it might contain.
+void Status::Clear() {
+ m_code = 0;
+ m_type = eErrorTypeInvalid;
+ m_string.clear();
+}
+
+// Access the error value.
+Status::ValueType Status::GetError() const { return m_code; }
+
+// Access the error type.
+ErrorType Status::GetType() const { return m_type; }
+
+// Returns true if this object contains a value that describes an error or
+// otherwise non-success result.
+bool Status::Fail() const { return m_code != 0; }
+
+// Set accessor for the error value to "err" and the type to
+// "eErrorTypeMachKernel"
+void Status::SetMachError(uint32_t err) {
+ m_code = err;
+ m_type = eErrorTypeMachKernel;
+ m_string.clear();
+}
+
+void Status::SetExpressionError(lldb::ExpressionResults result,
+ const char *mssg) {
+ m_code = result;
+ m_type = eErrorTypeExpression;
+ m_string = mssg;
+}
+
+int Status::SetExpressionErrorWithFormat(lldb::ExpressionResults result,
+ const char *format, ...) {
+ int length = 0;
+
+ if (format != nullptr && format[0]) {
+ va_list args;
+ va_start(args, format);
+ length = SetErrorStringWithVarArg(format, args);
+ va_end(args);
+ } else {
+ m_string.clear();
+ }
+ m_code = result;
+ m_type = eErrorTypeExpression;
+ return length;
+}
+
+// Set accessor for the error value and type.
+void Status::SetError(ValueType err, ErrorType type) {
+ m_code = err;
+ m_type = type;
+ m_string.clear();
+}
+
+// Update the error value to be "errno" and update the type to be "POSIX".
+void Status::SetErrorToErrno() {
+ m_code = errno;
+ m_type = eErrorTypePOSIX;
+ m_string.clear();
+}
+
+// Update the error value to be LLDB_GENERIC_ERROR and update the type to be
+// "Generic".
+void Status::SetErrorToGenericError() {
+ m_code = LLDB_GENERIC_ERROR;
+ m_type = eErrorTypeGeneric;
+ m_string.clear();
+}
+
+// Set accessor for the error string value for a specific error. This allows
+// any string to be supplied as an error explanation. The error string value
+// will remain until the error value is cleared or a new error value/type is
+// assigned.
+void Status::SetErrorString(llvm::StringRef err_str) {
+ if (!err_str.empty()) {
+ // If we have an error string, we should always at least have an error set
+ // to a generic value.
+ if (Success())
+ SetErrorToGenericError();
+ }
+ m_string = err_str;
+}
+
+/// Set the current error string to a formatted error string.
+///
+/// \param format
+/// A printf style format string
+int Status::SetErrorStringWithFormat(const char *format, ...) {
+ if (format != nullptr && format[0]) {
+ va_list args;
+ va_start(args, format);
+ int length = SetErrorStringWithVarArg(format, args);
+ va_end(args);
+ return length;
+ } else {
+ m_string.clear();
+ }
+ return 0;
+}
+
+int Status::SetErrorStringWithVarArg(const char *format, va_list args) {
+ if (format != nullptr && format[0]) {
+ // If we have an error string, we should always at least have an error set
+ // to a generic value.
+ if (Success())
+ SetErrorToGenericError();
+
+ llvm::SmallString<1024> buf;
+ VASprintf(buf, format, args);
+ m_string = buf.str();
+ return buf.size();
+ } else {
+ m_string.clear();
+ }
+ return 0;
+}
+
+// Returns true if the error code in this object is considered a successful
+// return value.
+bool Status::Success() const { return m_code == 0; }
+
+bool Status::WasInterrupted() const {
+ return (m_type == eErrorTypePOSIX && m_code == EINTR);
+}
+
+void llvm::format_provider<lldb_private::Status>::format(
+ const lldb_private::Status &error, llvm::raw_ostream &OS,
+ llvm::StringRef Options) {
+ llvm::format_provider<llvm::StringRef>::format(error.AsCString(), OS,
+ Options);
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Stream.cpp b/contrib/llvm-project/lldb/source/Utility/Stream.cpp
new file mode 100644
index 000000000000..c48a12acd906
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Stream.cpp
@@ -0,0 +1,441 @@
+//===-- Stream.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Stream.h"
+
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/VASPrintf.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/LEB128.h"
+
+#include <string>
+
+#include <inttypes.h>
+#include <stddef.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+Stream::Stream(uint32_t flags, uint32_t addr_size, ByteOrder byte_order)
+ : m_flags(flags), m_addr_size(addr_size), m_byte_order(byte_order),
+ m_indent_level(0), m_forwarder(*this) {}
+
+Stream::Stream()
+ : m_flags(0), m_addr_size(4), m_byte_order(endian::InlHostByteOrder()),
+ m_indent_level(0), m_forwarder(*this) {}
+
+// Destructor
+Stream::~Stream() {}
+
+ByteOrder Stream::SetByteOrder(ByteOrder byte_order) {
+ ByteOrder old_byte_order = m_byte_order;
+ m_byte_order = byte_order;
+ return old_byte_order;
+}
+
+// Put an offset "uval" out to the stream using the printf format in "format".
+void Stream::Offset(uint32_t uval, const char *format) { Printf(format, uval); }
+
+// Put an SLEB128 "uval" out to the stream using the printf format in "format".
+size_t Stream::PutSLEB128(int64_t sval) {
+ if (m_flags.Test(eBinary))
+ return llvm::encodeSLEB128(sval, m_forwarder);
+ else
+ return Printf("0x%" PRIi64, sval);
+}
+
+// Put an ULEB128 "uval" out to the stream using the printf format in "format".
+size_t Stream::PutULEB128(uint64_t uval) {
+ if (m_flags.Test(eBinary))
+ return llvm::encodeULEB128(uval, m_forwarder);
+ else
+ return Printf("0x%" PRIx64, uval);
+}
+
+// Print a raw NULL terminated C string to the stream.
+size_t Stream::PutCString(llvm::StringRef str) {
+ size_t bytes_written = 0;
+ bytes_written = Write(str.data(), str.size());
+
+ // when in binary mode, emit the NULL terminator
+ if (m_flags.Test(eBinary))
+ bytes_written += PutChar('\0');
+ return bytes_written;
+}
+
+// Print a double quoted NULL terminated C string to the stream using the
+// printf format in "format".
+void Stream::QuotedCString(const char *cstr, const char *format) {
+ Printf(format, cstr);
+}
+
+// Put an address "addr" out to the stream with optional prefix and suffix
+// strings.
+void Stream::Address(uint64_t addr, uint32_t addr_size, const char *prefix,
+ const char *suffix) {
+ if (prefix == nullptr)
+ prefix = "";
+ if (suffix == nullptr)
+ suffix = "";
+ // int addr_width = m_addr_size << 1;
+ // Printf ("%s0x%0*" PRIx64 "%s", prefix, addr_width, addr, suffix);
+ Printf("%s0x%0*" PRIx64 "%s", prefix, addr_size * 2, addr, suffix);
+}
+
+// Put an address range out to the stream with optional prefix and suffix
+// strings.
+void Stream::AddressRange(uint64_t lo_addr, uint64_t hi_addr,
+ uint32_t addr_size, const char *prefix,
+ const char *suffix) {
+ if (prefix && prefix[0])
+ PutCString(prefix);
+ Address(lo_addr, addr_size, "[");
+ Address(hi_addr, addr_size, "-", ")");
+ if (suffix && suffix[0])
+ PutCString(suffix);
+}
+
+size_t Stream::PutChar(char ch) { return Write(&ch, 1); }
+
+// Print some formatted output to the stream.
+size_t Stream::Printf(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ size_t result = PrintfVarArg(format, args);
+ va_end(args);
+ return result;
+}
+
+// Print some formatted output to the stream.
+size_t Stream::PrintfVarArg(const char *format, va_list args) {
+ llvm::SmallString<1024> buf;
+ VASprintf(buf, format, args);
+
+ // Include the NULL termination byte for binary output
+ size_t length = buf.size();
+ if (m_flags.Test(eBinary))
+ ++length;
+ return Write(buf.c_str(), length);
+}
+
+// Print and End of Line character to the stream
+size_t Stream::EOL() { return PutChar('\n'); }
+
+// Indent the current line using the current indentation level and print an
+// optional string following the indentation spaces.
+size_t Stream::Indent(const char *s) {
+ return Printf("%*.*s%s", m_indent_level, m_indent_level, "", s ? s : "");
+}
+
+size_t Stream::Indent(llvm::StringRef str) {
+ return Printf("%*.*s%s", m_indent_level, m_indent_level, "",
+ str.str().c_str());
+}
+
+// Stream a character "ch" out to this stream.
+Stream &Stream::operator<<(char ch) {
+ PutChar(ch);
+ return *this;
+}
+
+// Stream the NULL terminated C string out to this stream.
+Stream &Stream::operator<<(const char *s) {
+ Printf("%s", s);
+ return *this;
+}
+
+Stream &Stream::operator<<(llvm::StringRef str) {
+ Write(str.data(), str.size());
+ return *this;
+}
+
+// Stream the pointer value out to this stream.
+Stream &Stream::operator<<(const void *p) {
+ Printf("0x%.*tx", static_cast<int>(sizeof(const void *)) * 2, (ptrdiff_t)p);
+ return *this;
+}
+
+// Stream a uint8_t "uval" out to this stream.
+Stream &Stream::operator<<(uint8_t uval) {
+ PutHex8(uval);
+ return *this;
+}
+
+// Stream a uint16_t "uval" out to this stream.
+Stream &Stream::operator<<(uint16_t uval) {
+ PutHex16(uval, m_byte_order);
+ return *this;
+}
+
+// Stream a uint32_t "uval" out to this stream.
+Stream &Stream::operator<<(uint32_t uval) {
+ PutHex32(uval, m_byte_order);
+ return *this;
+}
+
+// Stream a uint64_t "uval" out to this stream.
+Stream &Stream::operator<<(uint64_t uval) {
+ PutHex64(uval, m_byte_order);
+ return *this;
+}
+
+// Stream a int8_t "sval" out to this stream.
+Stream &Stream::operator<<(int8_t sval) {
+ Printf("%i", static_cast<int>(sval));
+ return *this;
+}
+
+// Stream a int16_t "sval" out to this stream.
+Stream &Stream::operator<<(int16_t sval) {
+ Printf("%i", static_cast<int>(sval));
+ return *this;
+}
+
+// Stream a int32_t "sval" out to this stream.
+Stream &Stream::operator<<(int32_t sval) {
+ Printf("%i", static_cast<int>(sval));
+ return *this;
+}
+
+// Stream a int64_t "sval" out to this stream.
+Stream &Stream::operator<<(int64_t sval) {
+ Printf("%" PRIi64, sval);
+ return *this;
+}
+
+// Get the current indentation level
+int Stream::GetIndentLevel() const { return m_indent_level; }
+
+// Set the current indentation level
+void Stream::SetIndentLevel(int indent_level) { m_indent_level = indent_level; }
+
+// Increment the current indentation level
+void Stream::IndentMore(int amount) { m_indent_level += amount; }
+
+// Decrement the current indentation level
+void Stream::IndentLess(int amount) {
+ if (m_indent_level >= amount)
+ m_indent_level -= amount;
+ else
+ m_indent_level = 0;
+}
+
+// Get the address size in bytes
+uint32_t Stream::GetAddressByteSize() const { return m_addr_size; }
+
+// Set the address size in bytes
+void Stream::SetAddressByteSize(uint32_t addr_size) { m_addr_size = addr_size; }
+
+// The flags get accessor
+Flags &Stream::GetFlags() { return m_flags; }
+
+// The flags const get accessor
+const Flags &Stream::GetFlags() const { return m_flags; }
+
+// The byte order get accessor
+
+lldb::ByteOrder Stream::GetByteOrder() const { return m_byte_order; }
+
+size_t Stream::PrintfAsRawHex8(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+
+ llvm::SmallString<1024> buf;
+ VASprintf(buf, format, args);
+
+ ByteDelta delta(*this);
+ for (char C : buf)
+ _PutHex8(C, false);
+
+ va_end(args);
+
+ return *delta;
+}
+
+size_t Stream::PutNHex8(size_t n, uint8_t uvalue) {
+ ByteDelta delta(*this);
+ for (size_t i = 0; i < n; ++i)
+ _PutHex8(uvalue, false);
+ return *delta;
+}
+
+void Stream::_PutHex8(uint8_t uvalue, bool add_prefix) {
+ if (m_flags.Test(eBinary)) {
+ Write(&uvalue, 1);
+ } else {
+ if (add_prefix)
+ PutCString("0x");
+
+ static char g_hex_to_ascii_hex_char[16] = {'0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'a', 'b',
+ 'c', 'd', 'e', 'f'};
+ char nibble_chars[2];
+ nibble_chars[0] = g_hex_to_ascii_hex_char[(uvalue >> 4) & 0xf];
+ nibble_chars[1] = g_hex_to_ascii_hex_char[(uvalue >> 0) & 0xf];
+ Write(nibble_chars, sizeof(nibble_chars));
+ }
+}
+
+size_t Stream::PutHex8(uint8_t uvalue) {
+ ByteDelta delta(*this);
+ _PutHex8(uvalue, false);
+ return *delta;
+}
+
+size_t Stream::PutHex16(uint16_t uvalue, ByteOrder byte_order) {
+ ByteDelta delta(*this);
+
+ if (byte_order == eByteOrderInvalid)
+ byte_order = m_byte_order;
+
+ if (byte_order == eByteOrderLittle) {
+ for (size_t byte = 0; byte < sizeof(uvalue); ++byte)
+ _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false);
+ } else {
+ for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte)
+ _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false);
+ }
+ return *delta;
+}
+
+size_t Stream::PutHex32(uint32_t uvalue, ByteOrder byte_order) {
+ ByteDelta delta(*this);
+
+ if (byte_order == eByteOrderInvalid)
+ byte_order = m_byte_order;
+
+ if (byte_order == eByteOrderLittle) {
+ for (size_t byte = 0; byte < sizeof(uvalue); ++byte)
+ _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false);
+ } else {
+ for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte)
+ _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false);
+ }
+ return *delta;
+}
+
+size_t Stream::PutHex64(uint64_t uvalue, ByteOrder byte_order) {
+ ByteDelta delta(*this);
+
+ if (byte_order == eByteOrderInvalid)
+ byte_order = m_byte_order;
+
+ if (byte_order == eByteOrderLittle) {
+ for (size_t byte = 0; byte < sizeof(uvalue); ++byte)
+ _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false);
+ } else {
+ for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte)
+ _PutHex8(static_cast<uint8_t>(uvalue >> (byte * 8)), false);
+ }
+ return *delta;
+}
+
+size_t Stream::PutMaxHex64(uint64_t uvalue, size_t byte_size,
+ lldb::ByteOrder byte_order) {
+ switch (byte_size) {
+ case 1:
+ return PutHex8(static_cast<uint8_t>(uvalue));
+ case 2:
+ return PutHex16(static_cast<uint16_t>(uvalue), byte_order);
+ case 4:
+ return PutHex32(static_cast<uint32_t>(uvalue), byte_order);
+ case 8:
+ return PutHex64(uvalue, byte_order);
+ }
+ return 0;
+}
+
+size_t Stream::PutPointer(void *ptr) {
+ return PutRawBytes(&ptr, sizeof(ptr), endian::InlHostByteOrder(),
+ endian::InlHostByteOrder());
+}
+
+size_t Stream::PutFloat(float f, ByteOrder byte_order) {
+ if (byte_order == eByteOrderInvalid)
+ byte_order = m_byte_order;
+
+ return PutRawBytes(&f, sizeof(f), endian::InlHostByteOrder(), byte_order);
+}
+
+size_t Stream::PutDouble(double d, ByteOrder byte_order) {
+ if (byte_order == eByteOrderInvalid)
+ byte_order = m_byte_order;
+
+ return PutRawBytes(&d, sizeof(d), endian::InlHostByteOrder(), byte_order);
+}
+
+size_t Stream::PutLongDouble(long double ld, ByteOrder byte_order) {
+ if (byte_order == eByteOrderInvalid)
+ byte_order = m_byte_order;
+
+ return PutRawBytes(&ld, sizeof(ld), endian::InlHostByteOrder(), byte_order);
+}
+
+size_t Stream::PutRawBytes(const void *s, size_t src_len,
+ ByteOrder src_byte_order, ByteOrder dst_byte_order) {
+ ByteDelta delta(*this);
+
+ if (src_byte_order == eByteOrderInvalid)
+ src_byte_order = m_byte_order;
+
+ if (dst_byte_order == eByteOrderInvalid)
+ dst_byte_order = m_byte_order;
+
+ const uint8_t *src = static_cast<const uint8_t *>(s);
+ bool binary_was_set = m_flags.Test(eBinary);
+ if (!binary_was_set)
+ m_flags.Set(eBinary);
+ if (src_byte_order == dst_byte_order) {
+ for (size_t i = 0; i < src_len; ++i)
+ _PutHex8(src[i], false);
+ } else {
+ for (size_t i = src_len - 1; i < src_len; --i)
+ _PutHex8(src[i], false);
+ }
+ if (!binary_was_set)
+ m_flags.Clear(eBinary);
+
+ return *delta;
+}
+
+size_t Stream::PutBytesAsRawHex8(const void *s, size_t src_len,
+ ByteOrder src_byte_order,
+ ByteOrder dst_byte_order) {
+ ByteDelta delta(*this);
+ if (src_byte_order == eByteOrderInvalid)
+ src_byte_order = m_byte_order;
+
+ if (dst_byte_order == eByteOrderInvalid)
+ dst_byte_order = m_byte_order;
+
+ const uint8_t *src = static_cast<const uint8_t *>(s);
+ bool binary_is_set = m_flags.Test(eBinary);
+ m_flags.Clear(eBinary);
+ if (src_byte_order == dst_byte_order) {
+ for (size_t i = 0; i < src_len; ++i)
+ _PutHex8(src[i], false);
+ } else {
+ for (size_t i = src_len - 1; i < src_len; --i)
+ _PutHex8(src[i], false);
+ }
+ if (binary_is_set)
+ m_flags.Set(eBinary);
+
+ return *delta;
+}
+
+size_t Stream::PutStringAsRawHex8(llvm::StringRef s) {
+ ByteDelta delta(*this);
+ bool binary_is_set = m_flags.Test(eBinary);
+ m_flags.Clear(eBinary);
+ for (char c : s)
+ _PutHex8(c, false);
+ if (binary_is_set)
+ m_flags.Set(eBinary);
+ return *delta;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/StreamCallback.cpp b/contrib/llvm-project/lldb/source/Utility/StreamCallback.cpp
new file mode 100644
index 000000000000..b3d3adea78bc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/StreamCallback.cpp
@@ -0,0 +1,22 @@
+//===-- StreamCallback.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/StreamCallback.h"
+
+#include <string>
+
+using namespace lldb_private;
+
+StreamCallback::StreamCallback(lldb::LogOutputCallback callback, void *baton)
+ : llvm::raw_ostream(true), m_callback(callback), m_baton(baton) {}
+
+void StreamCallback::write_impl(const char *Ptr, size_t Size) {
+ m_callback(std::string(Ptr, Size).c_str(), m_baton);
+}
+
+uint64_t StreamCallback::current_pos() const { return 0; }
diff --git a/contrib/llvm-project/lldb/source/Utility/StreamGDBRemote.cpp b/contrib/llvm-project/lldb/source/Utility/StreamGDBRemote.cpp
new file mode 100644
index 000000000000..c710bbe3eecb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/StreamGDBRemote.cpp
@@ -0,0 +1,45 @@
+//===-- StreamGDBRemote.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/StreamGDBRemote.h"
+
+#include "lldb/Utility/Flags.h"
+#include "lldb/Utility/Stream.h"
+
+#include <stdio.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+StreamGDBRemote::StreamGDBRemote() : StreamString() {}
+
+StreamGDBRemote::StreamGDBRemote(uint32_t flags, uint32_t addr_size,
+ ByteOrder byte_order)
+ : StreamString(flags, addr_size, byte_order) {}
+
+StreamGDBRemote::~StreamGDBRemote() {}
+
+int StreamGDBRemote::PutEscapedBytes(const void *s, size_t src_len) {
+ int bytes_written = 0;
+ const uint8_t *src = static_cast<const uint8_t *>(s);
+ bool binary_is_set = m_flags.Test(eBinary);
+ m_flags.Clear(eBinary);
+ while (src_len) {
+ uint8_t byte = *src;
+ src++;
+ src_len--;
+ if (byte == 0x23 || byte == 0x24 || byte == 0x7d || byte == 0x2a) {
+ bytes_written += PutChar(0x7d);
+ byte ^= 0x20;
+ }
+ bytes_written += PutChar(byte);
+ };
+ if (binary_is_set)
+ m_flags.Set(eBinary);
+ return bytes_written;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/StreamString.cpp b/contrib/llvm-project/lldb/source/Utility/StreamString.cpp
new file mode 100644
index 000000000000..bf9814d6c305
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/StreamString.cpp
@@ -0,0 +1,66 @@
+//===-- StreamString.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+StreamString::StreamString() : Stream(0, 4, eByteOrderBig) {}
+
+StreamString::StreamString(uint32_t flags, uint32_t addr_size,
+ ByteOrder byte_order)
+ : Stream(flags, addr_size, byte_order), m_packet() {}
+
+StreamString::~StreamString() {}
+
+void StreamString::Flush() {
+ // Nothing to do when flushing a buffer based stream...
+}
+
+size_t StreamString::WriteImpl(const void *s, size_t length) {
+ m_packet.append(reinterpret_cast<const char *>(s), length);
+ return length;
+}
+
+void StreamString::Clear() {
+ m_packet.clear();
+ m_bytes_written = 0;
+}
+
+bool StreamString::Empty() const { return GetSize() == 0; }
+
+size_t StreamString::GetSize() const { return m_packet.size(); }
+
+size_t StreamString::GetSizeOfLastLine() const {
+ const size_t length = m_packet.size();
+ size_t last_line_begin_pos = m_packet.find_last_of("\r\n");
+ if (last_line_begin_pos == std::string::npos) {
+ return length;
+ } else {
+ ++last_line_begin_pos;
+ return length - last_line_begin_pos;
+ }
+}
+
+llvm::StringRef StreamString::GetString() const { return m_packet; }
+
+void StreamString::FillLastLineToColumn(uint32_t column, char fill_char) {
+ const size_t length = m_packet.size();
+ size_t last_line_begin_pos = m_packet.find_last_of("\r\n");
+ if (last_line_begin_pos == std::string::npos) {
+ last_line_begin_pos = 0;
+ } else {
+ ++last_line_begin_pos;
+ }
+
+ const size_t line_columns = length - last_line_begin_pos;
+ if (column > line_columns) {
+ m_packet.append(column - line_columns, fill_char);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/StringExtractor.cpp b/contrib/llvm-project/lldb/source/Utility/StringExtractor.cpp
new file mode 100644
index 000000000000..502f468da3cc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/StringExtractor.cpp
@@ -0,0 +1,398 @@
+//===-- StringExtractor.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/StringExtractor.h"
+
+#include <tuple>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+static inline int xdigit_to_sint(char ch) {
+ if (ch >= 'a' && ch <= 'f')
+ return 10 + ch - 'a';
+ if (ch >= 'A' && ch <= 'F')
+ return 10 + ch - 'A';
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ return -1;
+}
+
+// StringExtractor constructor
+StringExtractor::StringExtractor() : m_packet(), m_index(0) {}
+
+StringExtractor::StringExtractor(llvm::StringRef packet_str)
+ : m_packet(), m_index(0) {
+ m_packet.assign(packet_str.begin(), packet_str.end());
+}
+
+StringExtractor::StringExtractor(const char *packet_cstr)
+ : m_packet(), m_index(0) {
+ if (packet_cstr)
+ m_packet.assign(packet_cstr);
+}
+
+// Destructor
+StringExtractor::~StringExtractor() {}
+
+char StringExtractor::GetChar(char fail_value) {
+ if (m_index < m_packet.size()) {
+ char ch = m_packet[m_index];
+ ++m_index;
+ return ch;
+ }
+ m_index = UINT64_MAX;
+ return fail_value;
+}
+
+// If a pair of valid hex digits exist at the head of the StringExtractor they
+// are decoded into an unsigned byte and returned by this function
+//
+// If there is not a pair of valid hex digits at the head of the
+// StringExtractor, it is left unchanged and -1 is returned
+int StringExtractor::DecodeHexU8() {
+ SkipSpaces();
+ if (GetBytesLeft() < 2) {
+ return -1;
+ }
+ const int hi_nibble = xdigit_to_sint(m_packet[m_index]);
+ const int lo_nibble = xdigit_to_sint(m_packet[m_index + 1]);
+ if (hi_nibble == -1 || lo_nibble == -1) {
+ return -1;
+ }
+ m_index += 2;
+ return static_cast<uint8_t>((hi_nibble << 4) + lo_nibble);
+}
+
+// Extract an unsigned character from two hex ASCII chars in the packet string,
+// or return fail_value on failure
+uint8_t StringExtractor::GetHexU8(uint8_t fail_value, bool set_eof_on_fail) {
+ // On success, fail_value will be overwritten with the next character in the
+ // stream
+ GetHexU8Ex(fail_value, set_eof_on_fail);
+ return fail_value;
+}
+
+bool StringExtractor::GetHexU8Ex(uint8_t &ch, bool set_eof_on_fail) {
+ int byte = DecodeHexU8();
+ if (byte == -1) {
+ if (set_eof_on_fail || m_index >= m_packet.size())
+ m_index = UINT64_MAX;
+ // ch should not be changed in case of failure
+ return false;
+ }
+ ch = static_cast<uint8_t>(byte);
+ return true;
+}
+
+uint32_t StringExtractor::GetU32(uint32_t fail_value, int base) {
+ if (m_index < m_packet.size()) {
+ char *end = nullptr;
+ const char *start = m_packet.c_str();
+ const char *cstr = start + m_index;
+ uint32_t result = static_cast<uint32_t>(::strtoul(cstr, &end, base));
+
+ if (end && end != cstr) {
+ m_index = end - start;
+ return result;
+ }
+ }
+ return fail_value;
+}
+
+int32_t StringExtractor::GetS32(int32_t fail_value, int base) {
+ if (m_index < m_packet.size()) {
+ char *end = nullptr;
+ const char *start = m_packet.c_str();
+ const char *cstr = start + m_index;
+ int32_t result = static_cast<int32_t>(::strtol(cstr, &end, base));
+
+ if (end && end != cstr) {
+ m_index = end - start;
+ return result;
+ }
+ }
+ return fail_value;
+}
+
+uint64_t StringExtractor::GetU64(uint64_t fail_value, int base) {
+ if (m_index < m_packet.size()) {
+ char *end = nullptr;
+ const char *start = m_packet.c_str();
+ const char *cstr = start + m_index;
+ uint64_t result = ::strtoull(cstr, &end, base);
+
+ if (end && end != cstr) {
+ m_index = end - start;
+ return result;
+ }
+ }
+ return fail_value;
+}
+
+int64_t StringExtractor::GetS64(int64_t fail_value, int base) {
+ if (m_index < m_packet.size()) {
+ char *end = nullptr;
+ const char *start = m_packet.c_str();
+ const char *cstr = start + m_index;
+ int64_t result = ::strtoll(cstr, &end, base);
+
+ if (end && end != cstr) {
+ m_index = end - start;
+ return result;
+ }
+ }
+ return fail_value;
+}
+
+uint32_t StringExtractor::GetHexMaxU32(bool little_endian,
+ uint32_t fail_value) {
+ uint32_t result = 0;
+ uint32_t nibble_count = 0;
+
+ SkipSpaces();
+ if (little_endian) {
+ uint32_t shift_amount = 0;
+ while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ // Make sure we don't exceed the size of a uint32_t...
+ if (nibble_count >= (sizeof(uint32_t) * 2)) {
+ m_index = UINT64_MAX;
+ return fail_value;
+ }
+
+ uint8_t nibble_lo;
+ uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]);
+ ++m_index;
+ if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ nibble_lo = xdigit_to_sint(m_packet[m_index]);
+ ++m_index;
+ result |= (static_cast<uint32_t>(nibble_hi) << (shift_amount + 4));
+ result |= (static_cast<uint32_t>(nibble_lo) << shift_amount);
+ nibble_count += 2;
+ shift_amount += 8;
+ } else {
+ result |= (static_cast<uint32_t>(nibble_hi) << shift_amount);
+ nibble_count += 1;
+ shift_amount += 4;
+ }
+ }
+ } else {
+ while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ // Make sure we don't exceed the size of a uint32_t...
+ if (nibble_count >= (sizeof(uint32_t) * 2)) {
+ m_index = UINT64_MAX;
+ return fail_value;
+ }
+
+ uint8_t nibble = xdigit_to_sint(m_packet[m_index]);
+ // Big Endian
+ result <<= 4;
+ result |= nibble;
+
+ ++m_index;
+ ++nibble_count;
+ }
+ }
+ return result;
+}
+
+uint64_t StringExtractor::GetHexMaxU64(bool little_endian,
+ uint64_t fail_value) {
+ uint64_t result = 0;
+ uint32_t nibble_count = 0;
+
+ SkipSpaces();
+ if (little_endian) {
+ uint32_t shift_amount = 0;
+ while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ // Make sure we don't exceed the size of a uint64_t...
+ if (nibble_count >= (sizeof(uint64_t) * 2)) {
+ m_index = UINT64_MAX;
+ return fail_value;
+ }
+
+ uint8_t nibble_lo;
+ uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]);
+ ++m_index;
+ if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ nibble_lo = xdigit_to_sint(m_packet[m_index]);
+ ++m_index;
+ result |= (static_cast<uint64_t>(nibble_hi) << (shift_amount + 4));
+ result |= (static_cast<uint64_t>(nibble_lo) << shift_amount);
+ nibble_count += 2;
+ shift_amount += 8;
+ } else {
+ result |= (static_cast<uint64_t>(nibble_hi) << shift_amount);
+ nibble_count += 1;
+ shift_amount += 4;
+ }
+ }
+ } else {
+ while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ // Make sure we don't exceed the size of a uint64_t...
+ if (nibble_count >= (sizeof(uint64_t) * 2)) {
+ m_index = UINT64_MAX;
+ return fail_value;
+ }
+
+ uint8_t nibble = xdigit_to_sint(m_packet[m_index]);
+ // Big Endian
+ result <<= 4;
+ result |= nibble;
+
+ ++m_index;
+ ++nibble_count;
+ }
+ }
+ return result;
+}
+
+bool StringExtractor::ConsumeFront(const llvm::StringRef &str) {
+ llvm::StringRef S = GetStringRef();
+ if (!S.startswith(str))
+ return false;
+ else
+ m_index += str.size();
+ return true;
+}
+
+size_t StringExtractor::GetHexBytes(llvm::MutableArrayRef<uint8_t> dest,
+ uint8_t fail_fill_value) {
+ size_t bytes_extracted = 0;
+ while (!dest.empty() && GetBytesLeft() > 0) {
+ dest[0] = GetHexU8(fail_fill_value);
+ if (!IsGood())
+ break;
+ ++bytes_extracted;
+ dest = dest.drop_front();
+ }
+
+ if (!dest.empty())
+ ::memset(dest.data(), fail_fill_value, dest.size());
+
+ return bytes_extracted;
+}
+
+// Decodes all valid hex encoded bytes at the head of the StringExtractor,
+// limited by dst_len.
+//
+// Returns the number of bytes successfully decoded
+size_t StringExtractor::GetHexBytesAvail(llvm::MutableArrayRef<uint8_t> dest) {
+ size_t bytes_extracted = 0;
+ while (!dest.empty()) {
+ int decode = DecodeHexU8();
+ if (decode == -1)
+ break;
+ dest[0] = static_cast<uint8_t>(decode);
+ dest = dest.drop_front();
+ ++bytes_extracted;
+ }
+ return bytes_extracted;
+}
+
+// Consume ASCII hex nibble character pairs until we have decoded byte_size
+// bytes of data.
+
+uint64_t StringExtractor::GetHexWithFixedSize(uint32_t byte_size,
+ bool little_endian,
+ uint64_t fail_value) {
+ if (byte_size <= 8 && GetBytesLeft() >= byte_size * 2) {
+ uint64_t result = 0;
+ uint32_t i;
+ if (little_endian) {
+ // Little Endian
+ uint32_t shift_amount;
+ for (i = 0, shift_amount = 0; i < byte_size && IsGood();
+ ++i, shift_amount += 8) {
+ result |= (static_cast<uint64_t>(GetHexU8()) << shift_amount);
+ }
+ } else {
+ // Big Endian
+ for (i = 0; i < byte_size && IsGood(); ++i) {
+ result <<= 8;
+ result |= GetHexU8();
+ }
+ }
+ }
+ m_index = UINT64_MAX;
+ return fail_value;
+}
+
+size_t StringExtractor::GetHexByteString(std::string &str) {
+ str.clear();
+ str.reserve(GetBytesLeft() / 2);
+ char ch;
+ while ((ch = GetHexU8()) != '\0')
+ str.append(1, ch);
+ return str.size();
+}
+
+size_t StringExtractor::GetHexByteStringFixedLength(std::string &str,
+ uint32_t nibble_length) {
+ str.clear();
+
+ uint32_t nibble_count = 0;
+ for (const char *pch = Peek();
+ (nibble_count < nibble_length) && (pch != nullptr);
+ str.append(1, GetHexU8(0, false)), pch = Peek(), nibble_count += 2) {
+ }
+
+ return str.size();
+}
+
+size_t StringExtractor::GetHexByteStringTerminatedBy(std::string &str,
+ char terminator) {
+ str.clear();
+ char ch;
+ while ((ch = GetHexU8(0, false)) != '\0')
+ str.append(1, ch);
+ if (Peek() && *Peek() == terminator)
+ return str.size();
+
+ str.clear();
+ return str.size();
+}
+
+bool StringExtractor::GetNameColonValue(llvm::StringRef &name,
+ llvm::StringRef &value) {
+ // Read something in the form of NNNN:VVVV; where NNNN is any character that
+ // is not a colon, followed by a ':' character, then a value (one or more ';'
+ // chars), followed by a ';'
+ if (m_index >= m_packet.size())
+ return fail();
+
+ llvm::StringRef view(m_packet);
+ if (view.empty())
+ return fail();
+
+ llvm::StringRef a, b, c, d;
+ view = view.substr(m_index);
+ std::tie(a, b) = view.split(':');
+ if (a.empty() || b.empty())
+ return fail();
+ std::tie(c, d) = b.split(';');
+ if (b == c && d.empty())
+ return fail();
+
+ name = a;
+ value = c;
+ if (d.empty())
+ m_index = m_packet.size();
+ else {
+ size_t bytes_consumed = d.data() - view.data();
+ m_index += bytes_consumed;
+ }
+ return true;
+}
+
+void StringExtractor::SkipSpaces() {
+ const size_t n = m_packet.size();
+ while (m_index < n && isspace(m_packet[m_index]))
+ ++m_index;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp b/contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp
new file mode 100644
index 000000000000..a011e9246d15
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -0,0 +1,604 @@
+//===-- StringExtractorGDBRemote.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+#include <ctype.h>
+#include <string.h>
+
+StringExtractorGDBRemote::ResponseType
+StringExtractorGDBRemote::GetResponseType() const {
+ if (m_packet.empty())
+ return eUnsupported;
+
+ switch (m_packet[0]) {
+ case 'E':
+ if (isxdigit(m_packet[1]) && isxdigit(m_packet[2])) {
+ if (m_packet.size() == 3)
+ return eError;
+ llvm::StringRef packet_ref(m_packet);
+ if (packet_ref[3] == ';') {
+ auto err_string = packet_ref.substr(4);
+ for (auto e : err_string)
+ if (!isxdigit(e))
+ return eResponse;
+ return eError;
+ }
+ }
+ break;
+
+ case 'O':
+ if (m_packet.size() == 2 && m_packet[1] == 'K')
+ return eOK;
+ break;
+
+ case '+':
+ if (m_packet.size() == 1)
+ return eAck;
+ break;
+
+ case '-':
+ if (m_packet.size() == 1)
+ return eNack;
+ break;
+ }
+ return eResponse;
+}
+
+StringExtractorGDBRemote::ServerPacketType
+StringExtractorGDBRemote::GetServerPacketType() const {
+#define PACKET_MATCHES(s) \
+ ((packet_size == (sizeof(s) - 1)) && (strcmp((packet_cstr), (s)) == 0))
+#define PACKET_STARTS_WITH(s) \
+ ((packet_size >= (sizeof(s) - 1)) && \
+ ::strncmp(packet_cstr, s, (sizeof(s) - 1)) == 0)
+
+ // Empty is not a supported packet...
+ if (m_packet.empty())
+ return eServerPacketType_invalid;
+
+ const size_t packet_size = m_packet.size();
+ const char *packet_cstr = m_packet.c_str();
+ switch (m_packet[0]) {
+
+ case '%':
+ return eServerPacketType_notify;
+
+ case '\x03':
+ if (packet_size == 1)
+ return eServerPacketType_interrupt;
+ break;
+
+ case '-':
+ if (packet_size == 1)
+ return eServerPacketType_nack;
+ break;
+
+ case '+':
+ if (packet_size == 1)
+ return eServerPacketType_ack;
+ break;
+
+ case 'A':
+ return eServerPacketType_A;
+
+ case 'Q':
+
+ switch (packet_cstr[1]) {
+ case 'E':
+ if (PACKET_STARTS_WITH("QEnvironment:"))
+ return eServerPacketType_QEnvironment;
+ if (PACKET_STARTS_WITH("QEnvironmentHexEncoded:"))
+ return eServerPacketType_QEnvironmentHexEncoded;
+ if (PACKET_STARTS_WITH("QEnableErrorStrings"))
+ return eServerPacketType_QEnableErrorStrings;
+ break;
+
+ case 'P':
+ if (PACKET_STARTS_WITH("QPassSignals:"))
+ return eServerPacketType_QPassSignals;
+ break;
+
+ case 'S':
+ if (PACKET_MATCHES("QStartNoAckMode"))
+ return eServerPacketType_QStartNoAckMode;
+ if (PACKET_STARTS_WITH("QSaveRegisterState"))
+ return eServerPacketType_QSaveRegisterState;
+ if (PACKET_STARTS_WITH("QSetDisableASLR:"))
+ return eServerPacketType_QSetDisableASLR;
+ if (PACKET_STARTS_WITH("QSetDetachOnError:"))
+ return eServerPacketType_QSetDetachOnError;
+ if (PACKET_STARTS_WITH("QSetSTDIN:"))
+ return eServerPacketType_QSetSTDIN;
+ if (PACKET_STARTS_WITH("QSetSTDOUT:"))
+ return eServerPacketType_QSetSTDOUT;
+ if (PACKET_STARTS_WITH("QSetSTDERR:"))
+ return eServerPacketType_QSetSTDERR;
+ if (PACKET_STARTS_WITH("QSetWorkingDir:"))
+ return eServerPacketType_QSetWorkingDir;
+ if (PACKET_STARTS_WITH("QSetLogging:"))
+ return eServerPacketType_QSetLogging;
+ if (PACKET_STARTS_WITH("QSetMaxPacketSize:"))
+ return eServerPacketType_QSetMaxPacketSize;
+ if (PACKET_STARTS_WITH("QSetMaxPayloadSize:"))
+ return eServerPacketType_QSetMaxPayloadSize;
+ if (PACKET_STARTS_WITH("QSetEnableAsyncProfiling;"))
+ return eServerPacketType_QSetEnableAsyncProfiling;
+ if (PACKET_STARTS_WITH("QSyncThreadState:"))
+ return eServerPacketType_QSyncThreadState;
+ break;
+
+ case 'L':
+ if (PACKET_STARTS_WITH("QLaunchArch:"))
+ return eServerPacketType_QLaunchArch;
+ if (PACKET_MATCHES("QListThreadsInStopReply"))
+ return eServerPacketType_QListThreadsInStopReply;
+ break;
+
+ case 'R':
+ if (PACKET_STARTS_WITH("QRestoreRegisterState:"))
+ return eServerPacketType_QRestoreRegisterState;
+ break;
+
+ case 'T':
+ if (PACKET_MATCHES("QThreadSuffixSupported"))
+ return eServerPacketType_QThreadSuffixSupported;
+ break;
+ }
+ break;
+
+ case 'q':
+ switch (packet_cstr[1]) {
+ case 's':
+ if (PACKET_MATCHES("qsProcessInfo"))
+ return eServerPacketType_qsProcessInfo;
+ if (PACKET_MATCHES("qsThreadInfo"))
+ return eServerPacketType_qsThreadInfo;
+ break;
+
+ case 'f':
+ if (PACKET_STARTS_WITH("qfProcessInfo"))
+ return eServerPacketType_qfProcessInfo;
+ if (PACKET_STARTS_WITH("qfThreadInfo"))
+ return eServerPacketType_qfThreadInfo;
+ break;
+
+ case 'C':
+ if (packet_size == 2)
+ return eServerPacketType_qC;
+ break;
+
+ case 'E':
+ if (PACKET_STARTS_WITH("qEcho:"))
+ return eServerPacketType_qEcho;
+ break;
+
+ case 'F':
+ if (PACKET_STARTS_WITH("qFileLoadAddress:"))
+ return eServerPacketType_qFileLoadAddress;
+ break;
+
+ case 'G':
+ if (PACKET_STARTS_WITH("qGroupName:"))
+ return eServerPacketType_qGroupName;
+ if (PACKET_MATCHES("qGetWorkingDir"))
+ return eServerPacketType_qGetWorkingDir;
+ if (PACKET_MATCHES("qGetPid"))
+ return eServerPacketType_qGetPid;
+ if (PACKET_STARTS_WITH("qGetProfileData;"))
+ return eServerPacketType_qGetProfileData;
+ if (PACKET_MATCHES("qGDBServerVersion"))
+ return eServerPacketType_qGDBServerVersion;
+ break;
+
+ case 'H':
+ if (PACKET_MATCHES("qHostInfo"))
+ return eServerPacketType_qHostInfo;
+ break;
+
+ case 'K':
+ if (PACKET_STARTS_WITH("qKillSpawnedProcess"))
+ return eServerPacketType_qKillSpawnedProcess;
+ break;
+
+ case 'L':
+ if (PACKET_STARTS_WITH("qLaunchGDBServer"))
+ return eServerPacketType_qLaunchGDBServer;
+ if (PACKET_MATCHES("qLaunchSuccess"))
+ return eServerPacketType_qLaunchSuccess;
+ break;
+
+ case 'M':
+ if (PACKET_STARTS_WITH("qMemoryRegionInfo:"))
+ return eServerPacketType_qMemoryRegionInfo;
+ if (PACKET_MATCHES("qMemoryRegionInfo"))
+ return eServerPacketType_qMemoryRegionInfoSupported;
+ if (PACKET_STARTS_WITH("qModuleInfo:"))
+ return eServerPacketType_qModuleInfo;
+ break;
+
+ case 'P':
+ if (PACKET_STARTS_WITH("qProcessInfoPID:"))
+ return eServerPacketType_qProcessInfoPID;
+ if (PACKET_STARTS_WITH("qPlatform_shell:"))
+ return eServerPacketType_qPlatform_shell;
+ if (PACKET_STARTS_WITH("qPlatform_mkdir:"))
+ return eServerPacketType_qPlatform_mkdir;
+ if (PACKET_STARTS_WITH("qPlatform_chmod:"))
+ return eServerPacketType_qPlatform_chmod;
+ if (PACKET_MATCHES("qProcessInfo"))
+ return eServerPacketType_qProcessInfo;
+ break;
+
+ case 'Q':
+ if (PACKET_MATCHES("qQueryGDBServer"))
+ return eServerPacketType_qQueryGDBServer;
+ break;
+
+ case 'R':
+ if (PACKET_STARTS_WITH("qRcmd,"))
+ return eServerPacketType_qRcmd;
+ if (PACKET_STARTS_WITH("qRegisterInfo"))
+ return eServerPacketType_qRegisterInfo;
+ break;
+
+ case 'S':
+ if (PACKET_STARTS_WITH("qSpeedTest:"))
+ return eServerPacketType_qSpeedTest;
+ if (PACKET_MATCHES("qShlibInfoAddr"))
+ return eServerPacketType_qShlibInfoAddr;
+ if (PACKET_MATCHES("qStepPacketSupported"))
+ return eServerPacketType_qStepPacketSupported;
+ if (PACKET_STARTS_WITH("qSupported"))
+ return eServerPacketType_qSupported;
+ if (PACKET_MATCHES("qSyncThreadStateSupported"))
+ return eServerPacketType_qSyncThreadStateSupported;
+ break;
+
+ case 'T':
+ if (PACKET_STARTS_WITH("qThreadExtraInfo,"))
+ return eServerPacketType_qThreadExtraInfo;
+ if (PACKET_STARTS_WITH("qThreadStopInfo"))
+ return eServerPacketType_qThreadStopInfo;
+ break;
+
+ case 'U':
+ if (PACKET_STARTS_WITH("qUserName:"))
+ return eServerPacketType_qUserName;
+ break;
+
+ case 'V':
+ if (PACKET_MATCHES("qVAttachOrWaitSupported"))
+ return eServerPacketType_qVAttachOrWaitSupported;
+ break;
+
+ case 'W':
+ if (PACKET_STARTS_WITH("qWatchpointSupportInfo:"))
+ return eServerPacketType_qWatchpointSupportInfo;
+ if (PACKET_MATCHES("qWatchpointSupportInfo"))
+ return eServerPacketType_qWatchpointSupportInfoSupported;
+ break;
+
+ case 'X':
+ if (PACKET_STARTS_WITH("qXfer:"))
+ return eServerPacketType_qXfer;
+ break;
+ }
+ break;
+
+ case 'j':
+ if (PACKET_STARTS_WITH("jModulesInfo:"))
+ return eServerPacketType_jModulesInfo;
+ if (PACKET_MATCHES("jSignalsInfo"))
+ return eServerPacketType_jSignalsInfo;
+ if (PACKET_MATCHES("jThreadsInfo"))
+ return eServerPacketType_jThreadsInfo;
+ if (PACKET_STARTS_WITH("jTraceBufferRead:"))
+ return eServerPacketType_jTraceBufferRead;
+ if (PACKET_STARTS_WITH("jTraceConfigRead:"))
+ return eServerPacketType_jTraceConfigRead;
+ if (PACKET_STARTS_WITH("jTraceMetaRead:"))
+ return eServerPacketType_jTraceMetaRead;
+ if (PACKET_STARTS_WITH("jTraceStart:"))
+ return eServerPacketType_jTraceStart;
+ if (PACKET_STARTS_WITH("jTraceStop:"))
+ return eServerPacketType_jTraceStop;
+ break;
+
+ case 'v':
+ if (PACKET_STARTS_WITH("vFile:")) {
+ if (PACKET_STARTS_WITH("vFile:open:"))
+ return eServerPacketType_vFile_open;
+ else if (PACKET_STARTS_WITH("vFile:close:"))
+ return eServerPacketType_vFile_close;
+ else if (PACKET_STARTS_WITH("vFile:pread"))
+ return eServerPacketType_vFile_pread;
+ else if (PACKET_STARTS_WITH("vFile:pwrite"))
+ return eServerPacketType_vFile_pwrite;
+ else if (PACKET_STARTS_WITH("vFile:size"))
+ return eServerPacketType_vFile_size;
+ else if (PACKET_STARTS_WITH("vFile:exists"))
+ return eServerPacketType_vFile_exists;
+ else if (PACKET_STARTS_WITH("vFile:stat"))
+ return eServerPacketType_vFile_stat;
+ else if (PACKET_STARTS_WITH("vFile:mode"))
+ return eServerPacketType_vFile_mode;
+ else if (PACKET_STARTS_WITH("vFile:MD5"))
+ return eServerPacketType_vFile_md5;
+ else if (PACKET_STARTS_WITH("vFile:symlink"))
+ return eServerPacketType_vFile_symlink;
+ else if (PACKET_STARTS_WITH("vFile:unlink"))
+ return eServerPacketType_vFile_unlink;
+
+ } else {
+ if (PACKET_STARTS_WITH("vAttach;"))
+ return eServerPacketType_vAttach;
+ if (PACKET_STARTS_WITH("vAttachWait;"))
+ return eServerPacketType_vAttachWait;
+ if (PACKET_STARTS_WITH("vAttachOrWait;"))
+ return eServerPacketType_vAttachOrWait;
+ if (PACKET_STARTS_WITH("vAttachName;"))
+ return eServerPacketType_vAttachName;
+ if (PACKET_STARTS_WITH("vCont;"))
+ return eServerPacketType_vCont;
+ if (PACKET_MATCHES("vCont?"))
+ return eServerPacketType_vCont_actions;
+ }
+ break;
+ case '_':
+ switch (packet_cstr[1]) {
+ case 'M':
+ return eServerPacketType__M;
+
+ case 'm':
+ return eServerPacketType__m;
+ }
+ break;
+
+ case '?':
+ if (packet_size == 1)
+ return eServerPacketType_stop_reason;
+ break;
+
+ case 'c':
+ return eServerPacketType_c;
+
+ case 'C':
+ return eServerPacketType_C;
+
+ case 'D':
+ if (packet_size == 1)
+ return eServerPacketType_D;
+ break;
+
+ case 'g':
+ return eServerPacketType_g;
+
+ case 'G':
+ return eServerPacketType_G;
+
+ case 'H':
+ return eServerPacketType_H;
+
+ case 'I':
+ return eServerPacketType_I;
+
+ case 'k':
+ if (packet_size == 1)
+ return eServerPacketType_k;
+ break;
+
+ case 'm':
+ return eServerPacketType_m;
+
+ case 'M':
+ return eServerPacketType_M;
+
+ case 'p':
+ return eServerPacketType_p;
+
+ case 'P':
+ return eServerPacketType_P;
+
+ case 's':
+ if (packet_size == 1)
+ return eServerPacketType_s;
+ break;
+
+ case 'S':
+ return eServerPacketType_S;
+
+ case 'x':
+ return eServerPacketType_x;
+
+ case 'X':
+ return eServerPacketType_X;
+
+ case 'T':
+ return eServerPacketType_T;
+
+ case 'z':
+ if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4')
+ return eServerPacketType_z;
+ break;
+
+ case 'Z':
+ if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4')
+ return eServerPacketType_Z;
+ break;
+ }
+ return eServerPacketType_unimplemented;
+}
+
+bool StringExtractorGDBRemote::IsOKResponse() const {
+ return GetResponseType() == eOK;
+}
+
+bool StringExtractorGDBRemote::IsUnsupportedResponse() const {
+ return GetResponseType() == eUnsupported;
+}
+
+bool StringExtractorGDBRemote::IsNormalResponse() const {
+ return GetResponseType() == eResponse;
+}
+
+bool StringExtractorGDBRemote::IsErrorResponse() const {
+ return GetResponseType() == eError && isxdigit(m_packet[1]) &&
+ isxdigit(m_packet[2]);
+}
+
+uint8_t StringExtractorGDBRemote::GetError() {
+ if (GetResponseType() == eError) {
+ SetFilePos(1);
+ return GetHexU8(255);
+ }
+ return 0;
+}
+
+lldb_private::Status StringExtractorGDBRemote::GetStatus() {
+ lldb_private::Status error;
+ if (GetResponseType() == eError) {
+ SetFilePos(1);
+ uint8_t errc = GetHexU8(255);
+ error.SetError(errc, lldb::eErrorTypeGeneric);
+
+ error.SetErrorStringWithFormat("Error %u", errc);
+ std::string error_messg;
+ if (GetChar() == ';') {
+ GetHexByteString(error_messg);
+ error.SetErrorString(error_messg);
+ }
+ }
+ return error;
+}
+
+size_t StringExtractorGDBRemote::GetEscapedBinaryData(std::string &str) {
+ // Just get the data bytes in the string as
+ // GDBRemoteCommunication::CheckForPacket() already removes any 0x7d escaped
+ // characters. If any 0x7d characters are left in the packet, then they are
+ // supposed to be there...
+ str.clear();
+ const size_t bytes_left = GetBytesLeft();
+ if (bytes_left > 0) {
+ str.assign(m_packet, m_index, bytes_left);
+ m_index += bytes_left;
+ }
+ return str.size();
+}
+
+static bool
+OKErrorNotSupportedResponseValidator(void *,
+ const StringExtractorGDBRemote &response) {
+ switch (response.GetResponseType()) {
+ case StringExtractorGDBRemote::eOK:
+ case StringExtractorGDBRemote::eError:
+ case StringExtractorGDBRemote::eUnsupported:
+ return true;
+
+ case StringExtractorGDBRemote::eAck:
+ case StringExtractorGDBRemote::eNack:
+ case StringExtractorGDBRemote::eResponse:
+ break;
+ }
+ return false;
+}
+
+static bool JSONResponseValidator(void *,
+ const StringExtractorGDBRemote &response) {
+ switch (response.GetResponseType()) {
+ case StringExtractorGDBRemote::eUnsupported:
+ case StringExtractorGDBRemote::eError:
+ return true; // Accept unsupported or EXX as valid responses
+
+ case StringExtractorGDBRemote::eOK:
+ case StringExtractorGDBRemote::eAck:
+ case StringExtractorGDBRemote::eNack:
+ break;
+
+ case StringExtractorGDBRemote::eResponse:
+ // JSON that is returned in from JSON query packets is currently always
+ // either a dictionary which starts with a '{', or an array which starts
+ // with a '['. This is a quick validator to just make sure the response
+ // could be valid JSON without having to validate all of the
+ // JSON content.
+ switch (response.GetStringRef()[0]) {
+ case '{':
+ return true;
+ case '[':
+ return true;
+ default:
+ break;
+ }
+ break;
+ }
+ return false;
+}
+
+static bool
+ASCIIHexBytesResponseValidator(void *,
+ const StringExtractorGDBRemote &response) {
+ switch (response.GetResponseType()) {
+ case StringExtractorGDBRemote::eUnsupported:
+ case StringExtractorGDBRemote::eError:
+ return true; // Accept unsupported or EXX as valid responses
+
+ case StringExtractorGDBRemote::eOK:
+ case StringExtractorGDBRemote::eAck:
+ case StringExtractorGDBRemote::eNack:
+ break;
+
+ case StringExtractorGDBRemote::eResponse: {
+ uint32_t valid_count = 0;
+ for (const char ch : response.GetStringRef()) {
+ if (!isxdigit(ch)) {
+ return false;
+ }
+ if (++valid_count >= 16)
+ break; // Don't validate all the characters in case the packet is very
+ // large
+ }
+ return true;
+ } break;
+ }
+ return false;
+}
+
+void StringExtractorGDBRemote::CopyResponseValidator(
+ const StringExtractorGDBRemote &rhs) {
+ m_validator = rhs.m_validator;
+ m_validator_baton = rhs.m_validator_baton;
+}
+
+void StringExtractorGDBRemote::SetResponseValidator(
+ ResponseValidatorCallback callback, void *baton) {
+ m_validator = callback;
+ m_validator_baton = baton;
+}
+
+void StringExtractorGDBRemote::SetResponseValidatorToOKErrorNotSupported() {
+ m_validator = OKErrorNotSupportedResponseValidator;
+ m_validator_baton = nullptr;
+}
+
+void StringExtractorGDBRemote::SetResponseValidatorToASCIIHexBytes() {
+ m_validator = ASCIIHexBytesResponseValidator;
+ m_validator_baton = nullptr;
+}
+
+void StringExtractorGDBRemote::SetResponseValidatorToJSON() {
+ m_validator = JSONResponseValidator;
+ m_validator_baton = nullptr;
+}
+
+bool StringExtractorGDBRemote::ValidateResponse() const {
+ // If we have a validator callback, try to validate the callback
+ if (m_validator)
+ return m_validator(m_validator_baton, *this);
+ else
+ return true; // No validator, so response is valid
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/StringLexer.cpp b/contrib/llvm-project/lldb/source/Utility/StringLexer.cpp
new file mode 100644
index 000000000000..958a9580db7a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/StringLexer.cpp
@@ -0,0 +1,84 @@
+//===--------------------- StringLexer.cpp -----------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/StringLexer.h"
+
+#include <algorithm>
+#include <assert.h>
+
+using namespace lldb_utility;
+
+StringLexer::StringLexer(std::string s) : m_data(s), m_position(0) {}
+
+StringLexer::Character StringLexer::Peek() { return m_data[m_position]; }
+
+bool StringLexer::NextIf(Character c) {
+ auto val = Peek();
+ if (val == c) {
+ Next();
+ return true;
+ }
+ return false;
+}
+
+std::pair<bool, StringLexer::Character>
+StringLexer::NextIf(std::initializer_list<Character> cs) {
+ auto val = Peek();
+ for (auto c : cs) {
+ if (val == c) {
+ Next();
+ return {true, c};
+ }
+ }
+ return {false, 0};
+}
+
+bool StringLexer::AdvanceIf(const std::string &token) {
+ auto pos = m_position;
+ bool matches = true;
+ for (auto c : token) {
+ if (!NextIf(c)) {
+ matches = false;
+ break;
+ }
+ }
+ if (!matches) {
+ m_position = pos;
+ return false;
+ }
+ return true;
+}
+
+StringLexer::Character StringLexer::Next() {
+ auto val = Peek();
+ Consume();
+ return val;
+}
+
+bool StringLexer::HasAtLeast(Size s) {
+ return (m_data.size() - m_position) >= s;
+}
+
+void StringLexer::PutBack(Size s) {
+ assert(m_position >= s);
+ m_position -= s;
+}
+
+std::string StringLexer::GetUnlexed() {
+ return std::string(m_data, m_position);
+}
+
+void StringLexer::Consume() { m_position++; }
+
+StringLexer &StringLexer::operator=(const StringLexer &rhs) {
+ if (this != &rhs) {
+ m_data = rhs.m_data;
+ m_position = rhs.m_position;
+ }
+ return *this;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/StringList.cpp b/contrib/llvm-project/lldb/source/Utility/StringList.cpp
new file mode 100644
index 000000000000..fb0d9be8797d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/StringList.cpp
@@ -0,0 +1,267 @@
+//===-- StringList.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/StringList.h"
+
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/ADT/ArrayRef.h"
+
+#include <algorithm>
+#include <stdint.h>
+#include <string.h>
+
+using namespace lldb_private;
+
+StringList::StringList() : m_strings() {}
+
+StringList::StringList(const char *str) : m_strings() {
+ if (str)
+ m_strings.push_back(str);
+}
+
+StringList::StringList(const char **strv, int strc) : m_strings() {
+ for (int i = 0; i < strc; ++i) {
+ if (strv[i])
+ m_strings.push_back(strv[i]);
+ }
+}
+
+StringList::~StringList() {}
+
+void StringList::AppendString(const char *str) {
+ if (str)
+ m_strings.push_back(str);
+}
+
+void StringList::AppendString(const std::string &s) { m_strings.push_back(s); }
+
+void StringList::AppendString(std::string &&s) { m_strings.push_back(s); }
+
+void StringList::AppendString(const char *str, size_t str_len) {
+ if (str)
+ m_strings.push_back(std::string(str, str_len));
+}
+
+void StringList::AppendString(llvm::StringRef str) {
+ m_strings.push_back(str.str());
+}
+
+void StringList::AppendList(const char **strv, int strc) {
+ for (int i = 0; i < strc; ++i) {
+ if (strv[i])
+ m_strings.push_back(strv[i]);
+ }
+}
+
+void StringList::AppendList(StringList strings) {
+ size_t len = strings.GetSize();
+
+ for (size_t i = 0; i < len; ++i)
+ m_strings.push_back(strings.GetStringAtIndex(i));
+}
+
+size_t StringList::GetSize() const { return m_strings.size(); }
+
+size_t StringList::GetMaxStringLength() const {
+ size_t max_length = 0;
+ for (const auto &s : m_strings) {
+ const size_t len = s.size();
+ if (max_length < len)
+ max_length = len;
+ }
+ return max_length;
+}
+
+const char *StringList::GetStringAtIndex(size_t idx) const {
+ if (idx < m_strings.size())
+ return m_strings[idx].c_str();
+ return nullptr;
+}
+
+void StringList::Join(const char *separator, Stream &strm) {
+ size_t size = GetSize();
+
+ if (size == 0)
+ return;
+
+ for (uint32_t i = 0; i < size; ++i) {
+ if (i > 0)
+ strm.PutCString(separator);
+ strm.PutCString(GetStringAtIndex(i));
+ }
+}
+
+void StringList::Clear() { m_strings.clear(); }
+
+void StringList::LongestCommonPrefix(std::string &common_prefix) {
+ common_prefix.clear();
+ if (m_strings.empty())
+ return;
+
+ auto args = llvm::makeArrayRef(m_strings);
+ llvm::StringRef prefix = args.front();
+ for (auto arg : args.drop_front()) {
+ size_t count = 0;
+ for (count = 0; count < std::min(prefix.size(), arg.size()); ++count) {
+ if (prefix[count] != arg[count])
+ break;
+ }
+ prefix = prefix.take_front(count);
+ }
+ common_prefix = prefix;
+}
+
+void StringList::InsertStringAtIndex(size_t idx, const char *str) {
+ if (str) {
+ if (idx < m_strings.size())
+ m_strings.insert(m_strings.begin() + idx, str);
+ else
+ m_strings.push_back(str);
+ }
+}
+
+void StringList::InsertStringAtIndex(size_t idx, const std::string &str) {
+ if (idx < m_strings.size())
+ m_strings.insert(m_strings.begin() + idx, str);
+ else
+ m_strings.push_back(str);
+}
+
+void StringList::InsertStringAtIndex(size_t idx, std::string &&str) {
+ if (idx < m_strings.size())
+ m_strings.insert(m_strings.begin() + idx, str);
+ else
+ m_strings.push_back(str);
+}
+
+void StringList::DeleteStringAtIndex(size_t idx) {
+ if (idx < m_strings.size())
+ m_strings.erase(m_strings.begin() + idx);
+}
+
+size_t StringList::SplitIntoLines(const std::string &lines) {
+ return SplitIntoLines(lines.c_str(), lines.size());
+}
+
+size_t StringList::SplitIntoLines(const char *lines, size_t len) {
+ const size_t orig_size = m_strings.size();
+
+ if (len == 0)
+ return 0;
+
+ const char *k_newline_chars = "\r\n";
+ const char *p = lines;
+ const char *end = lines + len;
+ while (p < end) {
+ size_t count = strcspn(p, k_newline_chars);
+ if (count == 0) {
+ if (p[count] == '\r' || p[count] == '\n')
+ m_strings.push_back(std::string());
+ else
+ break;
+ } else {
+ if (p + count > end)
+ count = end - p;
+ m_strings.push_back(std::string(p, count));
+ }
+ if (p[count] == '\r' && p[count + 1] == '\n')
+ count++; // Skip an extra newline char for the DOS newline
+ count++; // Skip the newline character
+ p += count;
+ }
+ return m_strings.size() - orig_size;
+}
+
+void StringList::RemoveBlankLines() {
+ if (GetSize() == 0)
+ return;
+
+ size_t idx = 0;
+ while (idx < m_strings.size()) {
+ if (m_strings[idx].empty())
+ DeleteStringAtIndex(idx);
+ else
+ idx++;
+ }
+}
+
+std::string StringList::CopyList(const char *item_preamble,
+ const char *items_sep) const {
+ StreamString strm;
+ for (size_t i = 0; i < GetSize(); i++) {
+ if (i && items_sep && items_sep[0])
+ strm << items_sep;
+ if (item_preamble)
+ strm << item_preamble;
+ strm << GetStringAtIndex(i);
+ }
+ return strm.GetString();
+}
+
+StringList &StringList::operator<<(const char *str) {
+ AppendString(str);
+ return *this;
+}
+
+StringList &StringList::operator<<(const std::string &str) {
+ AppendString(str);
+ return *this;
+}
+
+StringList &StringList::operator<<(StringList strings) {
+ AppendList(strings);
+ return *this;
+}
+
+StringList &StringList::operator=(const std::vector<std::string> &rhs) {
+ m_strings.assign(rhs.begin(), rhs.end());
+
+ return *this;
+}
+
+size_t StringList::AutoComplete(llvm::StringRef s, StringList &matches,
+ size_t &exact_idx) const {
+ matches.Clear();
+ exact_idx = SIZE_MAX;
+ if (s.empty()) {
+ // No string, so it matches everything
+ matches = *this;
+ return matches.GetSize();
+ }
+
+ const size_t s_len = s.size();
+ const size_t num_strings = m_strings.size();
+
+ for (size_t i = 0; i < num_strings; ++i) {
+ if (m_strings[i].find(s) == 0) {
+ if (exact_idx == SIZE_MAX && m_strings[i].size() == s_len)
+ exact_idx = matches.GetSize();
+ matches.AppendString(m_strings[i]);
+ }
+ }
+ return matches.GetSize();
+}
+
+void StringList::LogDump(Log *log, const char *name) {
+ if (!log)
+ return;
+
+ StreamString strm;
+ if (name)
+ strm.Printf("Begin %s:\n", name);
+ for (const auto &s : m_strings) {
+ strm.Indent();
+ strm.Printf("%s\n", s.c_str());
+ }
+ if (name)
+ strm.Printf("End %s.\n", name);
+
+ LLDB_LOGV(log, "{0}", strm.GetData());
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/StructuredData.cpp b/contrib/llvm-project/lldb/source/Utility/StructuredData.cpp
new file mode 100644
index 000000000000..0e203f9739d1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/StructuredData.cpp
@@ -0,0 +1,278 @@
+//===---------------------StructuredData.cpp ---------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/JSON.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <cerrno>
+#include <cstdlib>
+#include <inttypes.h>
+#include <limits>
+
+using namespace lldb_private;
+
+// Functions that use a JSONParser to parse JSON into StructuredData
+static StructuredData::ObjectSP ParseJSONValue(JSONParser &json_parser);
+static StructuredData::ObjectSP ParseJSONObject(JSONParser &json_parser);
+static StructuredData::ObjectSP ParseJSONArray(JSONParser &json_parser);
+
+StructuredData::ObjectSP
+StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Status &error) {
+ StructuredData::ObjectSP return_sp;
+
+ auto buffer_or_error = llvm::MemoryBuffer::getFile(input_spec.GetPath());
+ if (!buffer_or_error) {
+ error.SetErrorStringWithFormatv("could not open input file: {0} - {1}.",
+ input_spec.GetPath(),
+ buffer_or_error.getError().message());
+ return return_sp;
+ }
+
+ JSONParser json_parser(buffer_or_error.get()->getBuffer());
+ return_sp = ParseJSONValue(json_parser);
+ return return_sp;
+}
+
+static StructuredData::ObjectSP ParseJSONObject(JSONParser &json_parser) {
+ // The "JSONParser::Token::ObjectStart" token should have already been
+ // consumed by the time this function is called
+ auto dict_up = llvm::make_unique<StructuredData::Dictionary>();
+
+ std::string value;
+ std::string key;
+ while (true) {
+ JSONParser::Token token = json_parser.GetToken(value);
+
+ if (token == JSONParser::Token::String) {
+ key.swap(value);
+ token = json_parser.GetToken(value);
+ if (token == JSONParser::Token::Colon) {
+ StructuredData::ObjectSP value_sp = ParseJSONValue(json_parser);
+ if (value_sp)
+ dict_up->AddItem(key, value_sp);
+ else
+ break;
+ }
+ } else if (token == JSONParser::Token::ObjectEnd) {
+ return StructuredData::ObjectSP(dict_up.release());
+ } else if (token == JSONParser::Token::Comma) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ return StructuredData::ObjectSP();
+}
+
+static StructuredData::ObjectSP ParseJSONArray(JSONParser &json_parser) {
+ // The "JSONParser::Token::ObjectStart" token should have already been
+ // consumed by the time this function is called
+ auto array_up = llvm::make_unique<StructuredData::Array>();
+
+ std::string value;
+ std::string key;
+ while (true) {
+ StructuredData::ObjectSP value_sp = ParseJSONValue(json_parser);
+ if (value_sp)
+ array_up->AddItem(value_sp);
+ else
+ break;
+
+ JSONParser::Token token = json_parser.GetToken(value);
+ if (token == JSONParser::Token::Comma) {
+ continue;
+ } else if (token == JSONParser::Token::ArrayEnd) {
+ return StructuredData::ObjectSP(array_up.release());
+ } else {
+ break;
+ }
+ }
+ return StructuredData::ObjectSP();
+}
+
+static StructuredData::ObjectSP ParseJSONValue(JSONParser &json_parser) {
+ std::string value;
+ const JSONParser::Token token = json_parser.GetToken(value);
+ switch (token) {
+ case JSONParser::Token::ObjectStart:
+ return ParseJSONObject(json_parser);
+
+ case JSONParser::Token::ArrayStart:
+ return ParseJSONArray(json_parser);
+
+ case JSONParser::Token::Integer: {
+ uint64_t uval;
+ if (llvm::to_integer(value, uval, 0))
+ return std::make_shared<StructuredData::Integer>(uval);
+ } break;
+
+ case JSONParser::Token::Float: {
+ double val;
+ if (llvm::to_float(value, val))
+ return std::make_shared<StructuredData::Float>(val);
+ } break;
+
+ case JSONParser::Token::String:
+ return std::make_shared<StructuredData::String>(value);
+
+ case JSONParser::Token::True:
+ case JSONParser::Token::False:
+ return std::make_shared<StructuredData::Boolean>(token ==
+ JSONParser::Token::True);
+
+ case JSONParser::Token::Null:
+ return std::make_shared<StructuredData::Null>();
+
+ default:
+ break;
+ }
+ return StructuredData::ObjectSP();
+}
+
+StructuredData::ObjectSP StructuredData::ParseJSON(std::string json_text) {
+ JSONParser json_parser(json_text);
+ StructuredData::ObjectSP object_sp = ParseJSONValue(json_parser);
+ return object_sp;
+}
+
+StructuredData::ObjectSP
+StructuredData::Object::GetObjectForDotSeparatedPath(llvm::StringRef path) {
+ if (this->GetType() == lldb::eStructuredDataTypeDictionary) {
+ std::pair<llvm::StringRef, llvm::StringRef> match = path.split('.');
+ std::string key = match.first.str();
+ ObjectSP value = this->GetAsDictionary()->GetValueForKey(key);
+ if (value.get()) {
+ // Do we have additional words to descend? If not, return the value
+ // we're at right now.
+ if (match.second.empty()) {
+ return value;
+ } else {
+ return value->GetObjectForDotSeparatedPath(match.second);
+ }
+ }
+ return ObjectSP();
+ }
+
+ if (this->GetType() == lldb::eStructuredDataTypeArray) {
+ std::pair<llvm::StringRef, llvm::StringRef> match = path.split('[');
+ if (match.second.empty()) {
+ return this->shared_from_this();
+ }
+ errno = 0;
+ uint64_t val = strtoul(match.second.str().c_str(), nullptr, 10);
+ if (errno == 0) {
+ return this->GetAsArray()->GetItemAtIndex(val);
+ }
+ return ObjectSP();
+ }
+
+ return this->shared_from_this();
+}
+
+void StructuredData::Object::DumpToStdout(bool pretty_print) const {
+ StreamString stream;
+ Dump(stream, pretty_print);
+ llvm::outs() << stream.GetString();
+}
+
+void StructuredData::Array::Dump(Stream &s, bool pretty_print) const {
+ bool first = true;
+ s << "[";
+ if (pretty_print) {
+ s << "\n";
+ s.IndentMore();
+ }
+ for (const auto &item_sp : m_items) {
+ if (first) {
+ first = false;
+ } else {
+ s << ",";
+ if (pretty_print)
+ s << "\n";
+ }
+
+ if (pretty_print)
+ s.Indent();
+ item_sp->Dump(s, pretty_print);
+ }
+ if (pretty_print) {
+ s.IndentLess();
+ s.EOL();
+ s.Indent();
+ }
+ s << "]";
+}
+
+void StructuredData::Integer::Dump(Stream &s, bool pretty_print) const {
+ s.Printf("%" PRIu64, m_value);
+}
+
+void StructuredData::Float::Dump(Stream &s, bool pretty_print) const {
+ s.Printf("%lg", m_value);
+}
+
+void StructuredData::Boolean::Dump(Stream &s, bool pretty_print) const {
+ if (m_value)
+ s.PutCString("true");
+ else
+ s.PutCString("false");
+}
+
+void StructuredData::String::Dump(Stream &s, bool pretty_print) const {
+ std::string quoted;
+ const size_t strsize = m_value.size();
+ for (size_t i = 0; i < strsize; ++i) {
+ char ch = m_value[i];
+ if (ch == '"' || ch == '\\')
+ quoted.push_back('\\');
+ quoted.push_back(ch);
+ }
+ s.Printf("\"%s\"", quoted.c_str());
+}
+
+void StructuredData::Dictionary::Dump(Stream &s, bool pretty_print) const {
+ bool first = true;
+ s << "{";
+ if (pretty_print) {
+ s << "\n";
+ s.IndentMore();
+ }
+ for (const auto &pair : m_dict) {
+ if (first)
+ first = false;
+ else {
+ s << ",";
+ if (pretty_print)
+ s << "\n";
+ }
+ if (pretty_print)
+ s.Indent();
+ s << "\"" << pair.first.AsCString() << "\" : ";
+ pair.second->Dump(s, pretty_print);
+ }
+ if (pretty_print) {
+ s.IndentLess();
+ s.EOL();
+ s.Indent();
+ }
+ s << "}";
+}
+
+void StructuredData::Null::Dump(Stream &s, bool pretty_print) const {
+ s << "null";
+}
+
+void StructuredData::Generic::Dump(Stream &s, bool pretty_print) const {
+ s << "0x" << m_object;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/TildeExpressionResolver.cpp b/contrib/llvm-project/lldb/source/Utility/TildeExpressionResolver.cpp
new file mode 100644
index 000000000000..b58f45728ce7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/TildeExpressionResolver.cpp
@@ -0,0 +1,93 @@
+//===--------------------- TildeExpressionResolver.cpp ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/TildeExpressionResolver.h"
+
+#include <assert.h>
+#include <system_error>
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+#if !defined(_WIN32)
+#include <pwd.h>
+#endif
+
+using namespace lldb_private;
+using namespace llvm;
+
+namespace fs = llvm::sys::fs;
+namespace path = llvm::sys::path;
+
+TildeExpressionResolver::~TildeExpressionResolver() {}
+
+bool StandardTildeExpressionResolver::ResolveExact(
+ StringRef Expr, SmallVectorImpl<char> &Output) {
+ // We expect the tilde expression to be ONLY the expression itself, and
+ // contain no separators.
+ assert(!llvm::any_of(Expr, [](char c) { return path::is_separator(c); }));
+ assert(Expr.empty() || Expr[0] == '~');
+
+ return !fs::real_path(Expr, Output, true);
+}
+
+bool StandardTildeExpressionResolver::ResolvePartial(StringRef Expr,
+ StringSet<> &Output) {
+ // We expect the tilde expression to be ONLY the expression itself, and
+ // contain no separators.
+ assert(!llvm::any_of(Expr, [](char c) { return path::is_separator(c); }));
+ assert(Expr.empty() || Expr[0] == '~');
+
+ Output.clear();
+#if defined(_WIN32) || defined(__ANDROID__)
+ return false;
+#else
+ if (Expr.empty())
+ return false;
+
+ SmallString<32> Buffer("~");
+ setpwent();
+ struct passwd *user_entry;
+ Expr = Expr.drop_front();
+
+ while ((user_entry = getpwent()) != nullptr) {
+ StringRef ThisName(user_entry->pw_name);
+ if (!ThisName.startswith(Expr))
+ continue;
+
+ Buffer.resize(1);
+ Buffer.append(ThisName);
+ Buffer.append(path::get_separator());
+ Output.insert(Buffer);
+ }
+
+ return true;
+#endif
+}
+
+bool TildeExpressionResolver::ResolveFullPath(
+ StringRef Expr, llvm::SmallVectorImpl<char> &Output) {
+ Output.clear();
+ if (!Expr.startswith("~")) {
+ Output.append(Expr.begin(), Expr.end());
+ return false;
+ }
+
+ namespace path = llvm::sys::path;
+ StringRef Left =
+ Expr.take_until([](char c) { return path::is_separator(c); });
+
+ if (!ResolveExact(Left, Output))
+ return false;
+
+ Output.append(Expr.begin() + Left.size(), Expr.end());
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/Timer.cpp b/contrib/llvm-project/lldb/source/Utility/Timer.cpp
new file mode 100644
index 000000000000..6b46d8ba7364
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/Timer.cpp
@@ -0,0 +1,152 @@
+//===-- Timer.cpp -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "lldb/Utility/Timer.h"
+#include "lldb/Utility/Stream.h"
+
+#include <algorithm>
+#include <map>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+using namespace lldb_private;
+
+#define TIMER_INDENT_AMOUNT 2
+
+namespace {
+typedef std::vector<Timer *> TimerStack;
+static std::atomic<Timer::Category *> g_categories;
+} // end of anonymous namespace
+
+std::atomic<bool> Timer::g_quiet(true);
+std::atomic<unsigned> Timer::g_display_depth(0);
+static std::mutex &GetFileMutex() {
+ static std::mutex *g_file_mutex_ptr = new std::mutex();
+ return *g_file_mutex_ptr;
+}
+
+static TimerStack &GetTimerStackForCurrentThread() {
+ static thread_local TimerStack g_stack;
+ return g_stack;
+}
+
+Timer::Category::Category(const char *cat) : m_name(cat) {
+ m_nanos.store(0, std::memory_order_release);
+ m_nanos_total.store(0, std::memory_order_release);
+ m_count.store(0, std::memory_order_release);
+ Category *expected = g_categories;
+ do {
+ m_next = expected;
+ } while (!g_categories.compare_exchange_weak(expected, this));
+}
+
+void Timer::SetQuiet(bool value) { g_quiet = value; }
+
+Timer::Timer(Timer::Category &category, const char *format, ...)
+ : m_category(category), m_total_start(std::chrono::steady_clock::now()) {
+ TimerStack &stack = GetTimerStackForCurrentThread();
+
+ stack.push_back(this);
+ if (g_quiet && stack.size() <= g_display_depth) {
+ std::lock_guard<std::mutex> lock(GetFileMutex());
+
+ // Indent
+ ::fprintf(stdout, "%*s", int(stack.size() - 1) * TIMER_INDENT_AMOUNT, "");
+ // Print formatted string
+ va_list args;
+ va_start(args, format);
+ ::vfprintf(stdout, format, args);
+ va_end(args);
+
+ // Newline
+ ::fprintf(stdout, "\n");
+ }
+}
+
+Timer::~Timer() {
+ using namespace std::chrono;
+
+ auto stop_time = steady_clock::now();
+ auto total_dur = stop_time - m_total_start;
+ auto timer_dur = total_dur - m_child_duration;
+
+ TimerStack &stack = GetTimerStackForCurrentThread();
+ if (g_quiet && stack.size() <= g_display_depth) {
+ std::lock_guard<std::mutex> lock(GetFileMutex());
+ ::fprintf(stdout, "%*s%.9f sec (%.9f sec)\n",
+ int(stack.size() - 1) * TIMER_INDENT_AMOUNT, "",
+ duration<double>(total_dur).count(),
+ duration<double>(timer_dur).count());
+ }
+
+ assert(stack.back() == this);
+ stack.pop_back();
+ if (!stack.empty())
+ stack.back()->ChildDuration(total_dur);
+
+ // Keep total results for each category so we can dump results.
+ m_category.m_nanos += std::chrono::nanoseconds(timer_dur).count();
+ m_category.m_nanos_total += std::chrono::nanoseconds(total_dur).count();
+ m_category.m_count++;
+}
+
+void Timer::SetDisplayDepth(uint32_t depth) { g_display_depth = depth; }
+
+/* binary function predicate:
+ * - returns whether a person is less than another person
+ */
+namespace {
+struct Stats {
+ const char *name;
+ uint64_t nanos;
+ uint64_t nanos_total;
+ uint64_t count;
+};
+} // namespace
+
+static bool CategoryMapIteratorSortCriterion(const Stats &lhs,
+ const Stats &rhs) {
+ return lhs.nanos > rhs.nanos;
+}
+
+void Timer::ResetCategoryTimes() {
+ for (Category *i = g_categories; i; i = i->m_next) {
+ i->m_nanos.store(0, std::memory_order_release);
+ i->m_nanos_total.store(0, std::memory_order_release);
+ i->m_count.store(0, std::memory_order_release);
+ }
+}
+
+void Timer::DumpCategoryTimes(Stream *s) {
+ std::vector<Stats> sorted;
+ for (Category *i = g_categories; i; i = i->m_next) {
+ uint64_t nanos = i->m_nanos.load(std::memory_order_acquire);
+ if (nanos) {
+ uint64_t nanos_total = i->m_nanos_total.load(std::memory_order_acquire);
+ uint64_t count = i->m_count.load(std::memory_order_acquire);
+ Stats stats{i->m_name, nanos, nanos_total, count};
+ sorted.push_back(stats);
+ }
+ }
+ if (sorted.empty())
+ return; // Later code will break without any elements.
+
+ // Sort by time
+ llvm::sort(sorted.begin(), sorted.end(), CategoryMapIteratorSortCriterion);
+
+ for (const auto &stats : sorted)
+ s->Printf("%.9f sec (total: %.3fs; child: %.3fs; count: %" PRIu64
+ ") for %s\n",
+ stats.nanos / 1000000000., stats.nanos_total / 1000000000.,
+ (stats.nanos_total - stats.nanos) / 1000000000., stats.count,
+ stats.name);
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/UUID.cpp b/contrib/llvm-project/lldb/source/Utility/UUID.cpp
new file mode 100644
index 000000000000..2a73f9a482ff
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/UUID.cpp
@@ -0,0 +1,123 @@
+//===-- UUID.cpp ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/UUID.h"
+
+#include "lldb/Utility/Stream.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Format.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+using namespace lldb_private;
+
+// Whether to put a separator after count uuid bytes.
+// For the first 16 bytes we follow the traditional UUID format. After that, we
+// simply put a dash after every 6 bytes.
+static inline bool separate(size_t count) {
+ if (count >= 10)
+ return (count - 10) % 6 == 0;
+
+ switch (count) {
+ case 4:
+ case 6:
+ case 8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+std::string UUID::GetAsString(llvm::StringRef separator) const {
+ std::string result;
+ llvm::raw_string_ostream os(result);
+
+ for (auto B : llvm::enumerate(GetBytes())) {
+ if (separate(B.index()))
+ os << separator;
+
+ os << llvm::format_hex_no_prefix(B.value(), 2, true);
+ }
+ os.flush();
+
+ return result;
+}
+
+void UUID::Dump(Stream *s) const { s->PutCString(GetAsString()); }
+
+static inline int xdigit_to_int(char ch) {
+ ch = tolower(ch);
+ if (ch >= 'a' && ch <= 'f')
+ return 10 + ch - 'a';
+ return ch - '0';
+}
+
+llvm::StringRef
+UUID::DecodeUUIDBytesFromString(llvm::StringRef p,
+ llvm::SmallVectorImpl<uint8_t> &uuid_bytes,
+ uint32_t num_uuid_bytes) {
+ uuid_bytes.clear();
+ while (!p.empty()) {
+ if (isxdigit(p[0]) && isxdigit(p[1])) {
+ int hi_nibble = xdigit_to_int(p[0]);
+ int lo_nibble = xdigit_to_int(p[1]);
+ // Translate the two hex nibble characters into a byte
+ uuid_bytes.push_back((hi_nibble << 4) + lo_nibble);
+
+ // Skip both hex digits
+ p = p.drop_front(2);
+
+ // Increment the byte that we are decoding within the UUID value and
+ // break out if we are done
+ if (uuid_bytes.size() == num_uuid_bytes)
+ break;
+ } else if (p.front() == '-') {
+ // Skip dashes
+ p = p.drop_front();
+ } else {
+ // UUID values can only consist of hex characters and '-' chars
+ break;
+ }
+ }
+ return p;
+}
+
+size_t UUID::SetFromStringRef(llvm::StringRef str, uint32_t num_uuid_bytes) {
+ llvm::StringRef p = str;
+
+ // Skip leading whitespace characters
+ p = p.ltrim();
+
+ llvm::SmallVector<uint8_t, 20> bytes;
+ llvm::StringRef rest =
+ UUID::DecodeUUIDBytesFromString(p, bytes, num_uuid_bytes);
+
+ // If we successfully decoded a UUID, return the amount of characters that
+ // were consumed
+ if (bytes.size() == num_uuid_bytes) {
+ *this = fromData(bytes);
+ return str.size() - rest.size();
+ }
+
+ // Else return zero to indicate we were not able to parse a UUID value
+ return 0;
+}
+
+size_t UUID::SetFromOptionalStringRef(llvm::StringRef str,
+ uint32_t num_uuid_bytes) {
+ size_t num_chars_consumed = SetFromStringRef(str, num_uuid_bytes);
+ if (num_chars_consumed) {
+ if (llvm::all_of(m_bytes, [](uint8_t b) { return b == 0; }))
+ Clear();
+ }
+
+ return num_chars_consumed;
+}
+
diff --git a/contrib/llvm-project/lldb/source/Utility/UriParser.cpp b/contrib/llvm-project/lldb/source/Utility/UriParser.cpp
new file mode 100644
index 000000000000..b446958f2f47
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/UriParser.cpp
@@ -0,0 +1,70 @@
+//===-- UriParser.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/UriParser.h"
+
+#include <string>
+
+#include <stdint.h>
+#include <tuple>
+
+using namespace lldb_private;
+
+// UriParser::Parse
+bool UriParser::Parse(llvm::StringRef uri, llvm::StringRef &scheme,
+ llvm::StringRef &hostname, int &port,
+ llvm::StringRef &path) {
+ llvm::StringRef tmp_scheme, tmp_hostname, tmp_path;
+
+ const llvm::StringRef kSchemeSep("://");
+ auto pos = uri.find(kSchemeSep);
+ if (pos == std::string::npos)
+ return false;
+
+ // Extract path.
+ tmp_scheme = uri.substr(0, pos);
+ auto host_pos = pos + kSchemeSep.size();
+ auto path_pos = uri.find('/', host_pos);
+ if (path_pos != std::string::npos)
+ tmp_path = uri.substr(path_pos);
+ else
+ tmp_path = "/";
+
+ auto host_port = uri.substr(
+ host_pos,
+ ((path_pos != std::string::npos) ? path_pos : uri.size()) - host_pos);
+
+ // Extract hostname
+ if (!host_port.empty() && host_port[0] == '[') {
+ // hostname is enclosed with square brackets.
+ pos = host_port.find(']');
+ if (pos == std::string::npos)
+ return false;
+
+ tmp_hostname = host_port.substr(1, pos - 1);
+ host_port = host_port.drop_front(pos + 1);
+ if (!host_port.empty() && !host_port.consume_front(":"))
+ return false;
+ } else {
+ std::tie(tmp_hostname, host_port) = host_port.split(':');
+ }
+
+ // Extract port
+ if (!host_port.empty()) {
+ uint16_t port_value = 0;
+ if (host_port.getAsInteger(0, port_value))
+ return false;
+ port = port_value;
+ } else
+ port = -1;
+
+ scheme = tmp_scheme;
+ hostname = tmp_hostname;
+ path = tmp_path;
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/UserID.cpp b/contrib/llvm-project/lldb/source/Utility/UserID.cpp
new file mode 100644
index 000000000000..b76a1cd84f82
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/UserID.cpp
@@ -0,0 +1,20 @@
+//===-- UserID.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/UserID.h"
+#include "lldb/Utility/Stream.h"
+
+#include <inttypes.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+Stream &lldb_private::operator<<(Stream &strm, const UserID &uid) {
+ strm.Printf("{0x%8.8" PRIx64 "}", uid.GetID());
+ return strm;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/UserIDResolver.cpp b/contrib/llvm-project/lldb/source/Utility/UserIDResolver.cpp
new file mode 100644
index 000000000000..8aac6f948cd2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/UserIDResolver.cpp
@@ -0,0 +1,44 @@
+//===-- UserIDResolver.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/UserIDResolver.h"
+#include "llvm/Support/ManagedStatic.h"
+
+using namespace lldb_private;
+
+UserIDResolver::~UserIDResolver() = default;
+
+llvm::Optional<llvm::StringRef> UserIDResolver::Get(
+ id_t id, Map &cache,
+ llvm::Optional<std::string> (UserIDResolver::*do_get)(id_t)) {
+
+ std::lock_guard<std::mutex> guard(m_mutex);
+ auto iter_bool = cache.try_emplace(id, llvm::None);
+ if (iter_bool.second)
+ iter_bool.first->second = (this->*do_get)(id);
+ if (iter_bool.first->second)
+ return llvm::StringRef(*iter_bool.first->second);
+ return llvm::None;
+}
+
+namespace {
+class NoopResolver : public UserIDResolver {
+protected:
+ llvm::Optional<std::string> DoGetUserName(id_t uid) override {
+ return llvm::None;
+ }
+
+ llvm::Optional<std::string> DoGetGroupName(id_t gid) override {
+ return llvm::None;
+ }
+};
+} // namespace
+
+static llvm::ManagedStatic<NoopResolver> g_noop_resolver;
+
+UserIDResolver &UserIDResolver::GetNoopResolver() { return *g_noop_resolver; }
diff --git a/contrib/llvm-project/lldb/source/Utility/UuidCompatibility.h b/contrib/llvm-project/lldb/source/Utility/UuidCompatibility.h
new file mode 100644
index 000000000000..e992c0c79a1f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/UuidCompatibility.h
@@ -0,0 +1,17 @@
+//===-- UuidCompatibility.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Include this header if your system does not have a definition of uuid_t
+
+#ifndef utility_UUID_COMPATIBILITY_H
+#define utility_UUID_COMPATIBILITY_H
+
+// uuid_t is guaranteed to always be a 16-byte array
+typedef unsigned char uuid_t[16];
+
+#endif // utility_UUID_COMPATIBILITY_H
diff --git a/contrib/llvm-project/lldb/source/Utility/VASprintf.cpp b/contrib/llvm-project/lldb/source/Utility/VASprintf.cpp
new file mode 100644
index 000000000000..2ee0f6676fa7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/VASprintf.cpp
@@ -0,0 +1,55 @@
+//===-- VASprintf.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/VASPrintf.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+bool lldb_private::VASprintf(llvm::SmallVectorImpl<char> &buf, const char *fmt,
+ va_list args) {
+ llvm::SmallString<16> error("<Encoding error>");
+ bool result = true;
+
+ // Copy in case our first call to vsnprintf doesn't fit into our buffer
+ va_list copy_args;
+ va_copy(copy_args, args);
+
+ buf.resize(buf.capacity());
+ // Write up to `capacity` bytes, ignoring the current size.
+ int length = ::vsnprintf(buf.data(), buf.size(), fmt, args);
+ if (length < 0) {
+ buf = error;
+ result = false;
+ goto finish;
+ }
+
+ if (size_t(length) >= buf.size()) {
+ // The error formatted string didn't fit into our buffer, resize it to the
+ // exact needed size, and retry
+ buf.resize(length + 1);
+ length = ::vsnprintf(buf.data(), buf.size(), fmt, copy_args);
+ if (length < 0) {
+ buf = error;
+ result = false;
+ goto finish;
+ }
+ assert(size_t(length) < buf.size());
+ }
+ buf.resize(length);
+
+finish:
+ va_end(args);
+ va_end(copy_args);
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Utility/VMRange.cpp b/contrib/llvm-project/lldb/source/Utility/VMRange.cpp
new file mode 100644
index 000000000000..f3f4ae7efc3c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Utility/VMRange.cpp
@@ -0,0 +1,70 @@
+//===-- VMRange.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/VMRange.h"
+
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-types.h"
+
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+#include <stddef.h>
+#include <stdint.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool VMRange::ContainsValue(const VMRange::collection &coll,
+ lldb::addr_t value) {
+ return llvm::find_if(coll, [&](const VMRange &r) {
+ return r.Contains(value);
+ }) != coll.end();
+}
+
+bool VMRange::ContainsRange(const VMRange::collection &coll,
+ const VMRange &range) {
+ return llvm::find_if(coll, [&](const VMRange &r) {
+ return r.Contains(range);
+ }) != coll.end();
+}
+
+void VMRange::Dump(Stream *s, lldb::addr_t offset, uint32_t addr_width) const {
+ s->AddressRange(offset + GetBaseAddress(), offset + GetEndAddress(),
+ addr_width);
+}
+
+bool lldb_private::operator==(const VMRange &lhs, const VMRange &rhs) {
+ return lhs.GetBaseAddress() == rhs.GetBaseAddress() &&
+ lhs.GetEndAddress() == rhs.GetEndAddress();
+}
+
+bool lldb_private::operator!=(const VMRange &lhs, const VMRange &rhs) {
+ return !(lhs == rhs);
+}
+
+bool lldb_private::operator<(const VMRange &lhs, const VMRange &rhs) {
+ if (lhs.GetBaseAddress() < rhs.GetBaseAddress())
+ return true;
+ else if (lhs.GetBaseAddress() > rhs.GetBaseAddress())
+ return false;
+ return lhs.GetEndAddress() < rhs.GetEndAddress();
+}
+
+bool lldb_private::operator<=(const VMRange &lhs, const VMRange &rhs) {
+ return !(lhs > rhs);
+}
+
+bool lldb_private::operator>(const VMRange &lhs, const VMRange &rhs) {
+ return rhs < lhs;
+}
+
+bool lldb_private::operator>=(const VMRange &lhs, const VMRange &rhs) {
+ return !(lhs < rhs);
+}
diff --git a/contrib/llvm-project/lldb/source/lldb.cpp b/contrib/llvm-project/lldb/source/lldb.cpp
new file mode 100644
index 000000000000..c96f5d9ee7e1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/lldb.cpp
@@ -0,0 +1,72 @@
+//===-- lldb.cpp ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-private.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#include "clang/Basic/Version.h"
+
+#ifdef HAVE_VCS_VERSION_INC
+#include "VCSVersion.inc"
+#endif
+
+static const char *GetLLDBRevision() {
+#ifdef LLDB_REVISION
+ return LLDB_REVISION;
+#else
+ return NULL;
+#endif
+}
+
+static const char *GetLLDBRepository() {
+#ifdef LLDB_REPOSITORY
+ return LLDB_REPOSITORY;
+#else
+ return NULL;
+#endif
+}
+
+#define QUOTE(str) #str
+#define EXPAND_AND_QUOTE(str) QUOTE(str)
+
+const char *lldb_private::GetVersion() {
+ // On platforms other than Darwin, report a version number in the same style
+ // as the clang tool.
+ static std::string g_version_str;
+ if (g_version_str.empty()) {
+ g_version_str += "lldb version ";
+ g_version_str += CLANG_VERSION_STRING;
+
+ const char *lldb_repo = GetLLDBRepository();
+ const char *lldb_rev = GetLLDBRevision();
+ if (lldb_repo || lldb_rev) {
+ g_version_str += " (";
+ if (lldb_repo)
+ g_version_str += lldb_repo;
+ if (lldb_rev) {
+ g_version_str += " revision ";
+ g_version_str += lldb_rev;
+ }
+ g_version_str += ")";
+ }
+
+ std::string clang_rev(clang::getClangRevision());
+ if (clang_rev.length() > 0) {
+ g_version_str += "\n clang revision ";
+ g_version_str += clang_rev;
+ }
+ std::string llvm_rev(clang::getLLVMRevision());
+ if (llvm_rev.length() > 0) {
+ g_version_str += "\n llvm revision ";
+ g_version_str += llvm_rev;
+ }
+ }
+ return g_version_str.c_str();
+}